summaryrefslogtreecommitdiffstats
path: root/drivers/net/mscc_eswitch/mscc_xfer.c
blob: 447b53782ddbb1cabe8dff3cd680800e49d92620 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
 * Copyright (c) 2018 Microsemi Corporation
 */

#include <log.h>
#include <linux/io.h>
#include "mscc_xfer.h"

#define QS_XTR_FLUSH_FLUSH		GENMASK(1, 0)
#define QS_INJ_CTRL_GAP_SIZE(x)		((x) << 21)
#define QS_INJ_CTRL_EOF			BIT(19)
#define QS_INJ_CTRL_SOF			BIT(18)
#define QS_INJ_CTRL_VLD_BYTES(x)	((x) << 16)

#define XTR_EOF_0     ntohl(0x80000000u)
#define XTR_EOF_1     ntohl(0x80000001u)
#define XTR_EOF_2     ntohl(0x80000002u)
#define XTR_EOF_3     ntohl(0x80000003u)
#define XTR_PRUNED    ntohl(0x80000004u)
#define XTR_ABORT     ntohl(0x80000005u)
#define XTR_ESCAPE    ntohl(0x80000006u)
#define XTR_NOT_READY ntohl(0x80000007u)

#define BUF_CELL_SZ		60
#define XTR_VALID_BYTES(x)	(4 - ((x) & 3))

int mscc_send(void __iomem *regs, const unsigned long *mscc_qs_offset,
	      u32 *ifh, size_t ifh_len, u32 *buff, size_t buff_len)
{
	int i, count = (buff_len + 3) / 4, last = buff_len % 4;

	writel(QS_INJ_CTRL_GAP_SIZE(1) | QS_INJ_CTRL_SOF,
	       regs + mscc_qs_offset[MSCC_QS_INJ_CTRL]);

	for (i = 0; i < ifh_len; i++)
		writel(ifh[i], regs + mscc_qs_offset[MSCC_QS_INJ_WR]);

	for (i = 0; i < count; i++)
		writel(buff[i], regs + mscc_qs_offset[MSCC_QS_INJ_WR]);

	/* Add padding */
	while (i < (BUF_CELL_SZ / 4)) {
		writel(0, regs + mscc_qs_offset[MSCC_QS_INJ_WR]);
		i++;
	}

	/* Indicate EOF and valid bytes in last word */
	writel(QS_INJ_CTRL_GAP_SIZE(1) |
	       QS_INJ_CTRL_VLD_BYTES(buff_len < BUF_CELL_SZ ? 0 : last) |
	       QS_INJ_CTRL_EOF, regs + mscc_qs_offset[MSCC_QS_INJ_CTRL]);

	/* Add dummy CRC */
	writel(0, regs + mscc_qs_offset[MSCC_QS_INJ_WR]);

	return 0;
}

int mscc_recv(void __iomem *regs, const unsigned long *mscc_qs_offset,
	      u32 *rxbuf, size_t ifh_len, bool byte_swap)
{
	u8 grp = 0; /* Recv everything on CPU group 0 */
	int i, byte_cnt = 0;
	bool eof_flag = false, pruned_flag = false, abort_flag = false;

	if (!(readl(regs + mscc_qs_offset[MSCC_QS_XTR_DATA_PRESENT]) &
	      BIT(grp)))
		return -EAGAIN;

	/* skip IFH */
	for (i = 0; i < ifh_len; i++)
		readl(regs + mscc_qs_offset[MSCC_QS_XTR_RD]);

	while (!eof_flag) {
		u32 val = readl(regs + mscc_qs_offset[MSCC_QS_XTR_RD]);
		u32 cmp = val;

		if (byte_swap)
			cmp = ntohl(val);

		switch (cmp) {
		case XTR_NOT_READY:
			debug("%d NOT_READY...?\n", byte_cnt);
			break;
		case XTR_ABORT:
			*rxbuf = readl(regs + mscc_qs_offset[MSCC_QS_XTR_RD]);
			abort_flag = true;
			eof_flag = true;
			debug("XTR_ABORT\n");
			break;
		case XTR_EOF_0:
		case XTR_EOF_1:
		case XTR_EOF_2:
		case XTR_EOF_3:
			byte_cnt += XTR_VALID_BYTES(val);
			*rxbuf = readl(regs + mscc_qs_offset[MSCC_QS_XTR_RD]);
			eof_flag = true;
			debug("EOF\n");
			break;
		case XTR_PRUNED:
			/* But get the last 4 bytes as well */
			eof_flag = true;
			pruned_flag = true;
			debug("PRUNED\n");
			/* fallthrough */
		case XTR_ESCAPE:
			*rxbuf = readl(regs + mscc_qs_offset[MSCC_QS_XTR_RD]);
			byte_cnt += 4;
			rxbuf++;
			debug("ESCAPED\n");
			break;
		default:
			*rxbuf = val;
			byte_cnt += 4;
			rxbuf++;
		}
	}

	if (abort_flag || pruned_flag || !eof_flag) {
		debug("Discarded frame: abort:%d pruned:%d eof:%d\n",
		      abort_flag, pruned_flag, eof_flag);
		return -EAGAIN;
	}

	return byte_cnt;
}

void mscc_flush(void __iomem *regs, const unsigned long *mscc_qs_offset)
{
	/* All Queues flush */
	setbits_le32(regs + mscc_qs_offset[MSCC_QS_XTR_FLUSH],
		     QS_XTR_FLUSH_FLUSH);

	/* Allow to drain */
	mdelay(1);

	/* All Queues normal */
	clrbits_le32(regs + mscc_qs_offset[MSCC_QS_XTR_FLUSH],
		     QS_XTR_FLUSH_FLUSH);
}