diff options
Diffstat (limited to 'drivers/net/mscc_eswitch/mscc_xfer.c')
-rw-r--r-- | drivers/net/mscc_eswitch/mscc_xfer.c | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/drivers/net/mscc_eswitch/mscc_xfer.c b/drivers/net/mscc_eswitch/mscc_xfer.c new file mode 100644 index 0000000000..f412901f1d --- /dev/null +++ b/drivers/net/mscc_eswitch/mscc_xfer.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2018 Microsemi Corporation + */ + +#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); +} |