447b53782ddbb1cabe8dff3cd680800e49d92620
[oweals/u-boot.git] / drivers / net / mscc_eswitch / mscc_xfer.c
1 // SPDX-License-Identifier: (GPL-2.0+ OR MIT)
2 /*
3  * Copyright (c) 2018 Microsemi Corporation
4  */
5
6 #include <log.h>
7 #include <linux/io.h>
8 #include "mscc_xfer.h"
9
10 #define QS_XTR_FLUSH_FLUSH              GENMASK(1, 0)
11 #define QS_INJ_CTRL_GAP_SIZE(x)         ((x) << 21)
12 #define QS_INJ_CTRL_EOF                 BIT(19)
13 #define QS_INJ_CTRL_SOF                 BIT(18)
14 #define QS_INJ_CTRL_VLD_BYTES(x)        ((x) << 16)
15
16 #define XTR_EOF_0     ntohl(0x80000000u)
17 #define XTR_EOF_1     ntohl(0x80000001u)
18 #define XTR_EOF_2     ntohl(0x80000002u)
19 #define XTR_EOF_3     ntohl(0x80000003u)
20 #define XTR_PRUNED    ntohl(0x80000004u)
21 #define XTR_ABORT     ntohl(0x80000005u)
22 #define XTR_ESCAPE    ntohl(0x80000006u)
23 #define XTR_NOT_READY ntohl(0x80000007u)
24
25 #define BUF_CELL_SZ             60
26 #define XTR_VALID_BYTES(x)      (4 - ((x) & 3))
27
28 int mscc_send(void __iomem *regs, const unsigned long *mscc_qs_offset,
29               u32 *ifh, size_t ifh_len, u32 *buff, size_t buff_len)
30 {
31         int i, count = (buff_len + 3) / 4, last = buff_len % 4;
32
33         writel(QS_INJ_CTRL_GAP_SIZE(1) | QS_INJ_CTRL_SOF,
34                regs + mscc_qs_offset[MSCC_QS_INJ_CTRL]);
35
36         for (i = 0; i < ifh_len; i++)
37                 writel(ifh[i], regs + mscc_qs_offset[MSCC_QS_INJ_WR]);
38
39         for (i = 0; i < count; i++)
40                 writel(buff[i], regs + mscc_qs_offset[MSCC_QS_INJ_WR]);
41
42         /* Add padding */
43         while (i < (BUF_CELL_SZ / 4)) {
44                 writel(0, regs + mscc_qs_offset[MSCC_QS_INJ_WR]);
45                 i++;
46         }
47
48         /* Indicate EOF and valid bytes in last word */
49         writel(QS_INJ_CTRL_GAP_SIZE(1) |
50                QS_INJ_CTRL_VLD_BYTES(buff_len < BUF_CELL_SZ ? 0 : last) |
51                QS_INJ_CTRL_EOF, regs + mscc_qs_offset[MSCC_QS_INJ_CTRL]);
52
53         /* Add dummy CRC */
54         writel(0, regs + mscc_qs_offset[MSCC_QS_INJ_WR]);
55
56         return 0;
57 }
58
59 int mscc_recv(void __iomem *regs, const unsigned long *mscc_qs_offset,
60               u32 *rxbuf, size_t ifh_len, bool byte_swap)
61 {
62         u8 grp = 0; /* Recv everything on CPU group 0 */
63         int i, byte_cnt = 0;
64         bool eof_flag = false, pruned_flag = false, abort_flag = false;
65
66         if (!(readl(regs + mscc_qs_offset[MSCC_QS_XTR_DATA_PRESENT]) &
67               BIT(grp)))
68                 return -EAGAIN;
69
70         /* skip IFH */
71         for (i = 0; i < ifh_len; i++)
72                 readl(regs + mscc_qs_offset[MSCC_QS_XTR_RD]);
73
74         while (!eof_flag) {
75                 u32 val = readl(regs + mscc_qs_offset[MSCC_QS_XTR_RD]);
76                 u32 cmp = val;
77
78                 if (byte_swap)
79                         cmp = ntohl(val);
80
81                 switch (cmp) {
82                 case XTR_NOT_READY:
83                         debug("%d NOT_READY...?\n", byte_cnt);
84                         break;
85                 case XTR_ABORT:
86                         *rxbuf = readl(regs + mscc_qs_offset[MSCC_QS_XTR_RD]);
87                         abort_flag = true;
88                         eof_flag = true;
89                         debug("XTR_ABORT\n");
90                         break;
91                 case XTR_EOF_0:
92                 case XTR_EOF_1:
93                 case XTR_EOF_2:
94                 case XTR_EOF_3:
95                         byte_cnt += XTR_VALID_BYTES(val);
96                         *rxbuf = readl(regs + mscc_qs_offset[MSCC_QS_XTR_RD]);
97                         eof_flag = true;
98                         debug("EOF\n");
99                         break;
100                 case XTR_PRUNED:
101                         /* But get the last 4 bytes as well */
102                         eof_flag = true;
103                         pruned_flag = true;
104                         debug("PRUNED\n");
105                         /* fallthrough */
106                 case XTR_ESCAPE:
107                         *rxbuf = readl(regs + mscc_qs_offset[MSCC_QS_XTR_RD]);
108                         byte_cnt += 4;
109                         rxbuf++;
110                         debug("ESCAPED\n");
111                         break;
112                 default:
113                         *rxbuf = val;
114                         byte_cnt += 4;
115                         rxbuf++;
116                 }
117         }
118
119         if (abort_flag || pruned_flag || !eof_flag) {
120                 debug("Discarded frame: abort:%d pruned:%d eof:%d\n",
121                       abort_flag, pruned_flag, eof_flag);
122                 return -EAGAIN;
123         }
124
125         return byte_cnt;
126 }
127
128 void mscc_flush(void __iomem *regs, const unsigned long *mscc_qs_offset)
129 {
130         /* All Queues flush */
131         setbits_le32(regs + mscc_qs_offset[MSCC_QS_XTR_FLUSH],
132                      QS_XTR_FLUSH_FLUSH);
133
134         /* Allow to drain */
135         mdelay(1);
136
137         /* All Queues normal */
138         clrbits_le32(regs + mscc_qs_offset[MSCC_QS_XTR_FLUSH],
139                      QS_XTR_FLUSH_FLUSH);
140 }