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