1// SPDX-License-Identifier: GPL-2.0
2/* ICSSG Ethernet driver
3 *
4 * Copyright (C) 2018-2024 Texas Instruments Incorporated - https://www.ti.com
5 */
6
7#include <phy.h>
8#include "icssg_prueth.h"
9#include "icssg_switch_map.h"
10#include "icss_mii_rt.h"
11#include <dm/device_compat.h>
12#include <linux/iopoll.h>
13
14/* TX IPG Values to be set for 100M and 1G link speeds.  These values are
15 * in ocp_clk cycles. So need change if ocp_clk is changed for a specific
16 * h/w design.
17 */
18
19/* SR2.0 IPG is in rgmii_clk (125MHz) clock cycles + 1 */
20#define MII_RT_TX_IPG_100M      0x17
21#define MII_RT_TX_IPG_1G        0xb
22
23#define	ICSSG_QUEUES_MAX		64
24#define	ICSSG_QUEUE_OFFSET		0xd00
25#define	ICSSG_QUEUE_PEEK_OFFSET		0xe00
26#define	ICSSG_QUEUE_CNT_OFFSET		0xe40
27#define	ICSSG_QUEUE_RESET_OFFSET	0xf40
28
29#define	ICSSG_NUM_TX_QUEUES	8
30
31#define	RECYCLE_Q_SLICE0	16
32#define	RECYCLE_Q_SLICE1	17
33
34#define	ICSSG_NUM_OTHER_QUEUES	5	/* port, host and special queues */
35
36#define	PORT_HI_Q_SLICE0	32
37#define	PORT_LO_Q_SLICE0	33
38#define	HOST_HI_Q_SLICE0	34
39#define	HOST_LO_Q_SLICE0	35
40#define	HOST_SPL_Q_SLICE0	40	/* Special Queue */
41
42#define	PORT_HI_Q_SLICE1	36
43#define	PORT_LO_Q_SLICE1	37
44#define	HOST_HI_Q_SLICE1	38
45#define	HOST_LO_Q_SLICE1	39
46#define	HOST_SPL_Q_SLICE1	41	/* Special Queue */
47
48#define MII_RXCFG_DEFAULT	(PRUSS_MII_RT_RXCFG_RX_ENABLE | \
49				 PRUSS_MII_RT_RXCFG_RX_DATA_RDY_MODE_DIS | \
50				 PRUSS_MII_RT_RXCFG_RX_L2_EN | \
51				 PRUSS_MII_RT_RXCFG_RX_L2_EOF_SCLR_DIS)
52
53#define MII_TXCFG_DEFAULT	(PRUSS_MII_RT_TXCFG_TX_ENABLE | \
54				 PRUSS_MII_RT_TXCFG_TX_AUTO_PREAMBLE | \
55				 PRUSS_MII_RT_TXCFG_TX_32_MODE_EN | \
56				 PRUSS_MII_RT_TXCFG_TX_IPG_WIRE_CLK_EN)
57
58#define ICSSG_CFG_DEFAULT	(ICSSG_CFG_TX_L1_EN | \
59				 ICSSG_CFG_TX_L2_EN | ICSSG_CFG_RX_L2_G_EN | \
60				 ICSSG_CFG_TX_PRU_EN | /* SR2.0 only */ \
61				 ICSSG_CFG_SGMII_MODE)
62
63#define FDB_GEN_CFG1		0x60
64#define SMEM_VLAN_OFFSET	8
65#define SMEM_VLAN_OFFSET_MASK	GENMASK(25, 8)
66
67#define FDB_GEN_CFG2		0x64
68#define FDB_VLAN_EN		BIT(6)
69#define FDB_HOST_EN		BIT(2)
70#define FDB_PRU1_EN		BIT(1)
71#define FDB_PRU0_EN		BIT(0)
72#define FDB_EN_ALL		(FDB_PRU0_EN | FDB_PRU1_EN | \
73				 FDB_HOST_EN | FDB_VLAN_EN)
74
75struct map {
76	int queue;
77	u32 pd_addr_start;
78	u32 flags;
79	bool special;
80};
81
82struct map hwq_map[2][ICSSG_NUM_OTHER_QUEUES] = {
83	{
84		{ PORT_HI_Q_SLICE0, PORT_DESC0_HI, 0x200000, 0 },
85		{ PORT_LO_Q_SLICE0, PORT_DESC0_LO, 0, 0 },
86		{ HOST_HI_Q_SLICE0, HOST_DESC0_HI, 0x200000, 0 },
87		{ HOST_LO_Q_SLICE0, HOST_DESC0_LO, 0, 0 },
88		{ HOST_SPL_Q_SLICE0, HOST_SPPD0, 0x400000, 1 },
89	},
90	{
91		{ PORT_HI_Q_SLICE1, PORT_DESC1_HI, 0xa00000, 0 },
92		{ PORT_LO_Q_SLICE1, PORT_DESC1_LO, 0x800000, 0 },
93		{ HOST_HI_Q_SLICE1, HOST_DESC1_HI, 0xa00000, 0 },
94		{ HOST_LO_Q_SLICE1, HOST_DESC1_LO, 0x800000, 0 },
95		{ HOST_SPL_Q_SLICE1, HOST_SPPD1, 0xc00000, 1 },
96	},
97};
98
99static void icssg_config_mii_init(struct prueth_priv *priv, int slice)
100{
101	struct prueth *prueth = priv->prueth;
102	struct regmap *mii_rt = prueth->mii_rt;
103	u32 txcfg_reg, pcnt_reg;
104	u32 txcfg;
105
106	txcfg_reg = (slice == ICSS_MII0) ? PRUSS_MII_RT_TXCFG0 :
107				       PRUSS_MII_RT_TXCFG1;
108	pcnt_reg = (slice == ICSS_MII0) ? PRUSS_MII_RT_RX_PCNT0 :
109				       PRUSS_MII_RT_RX_PCNT1;
110
111	txcfg = MII_TXCFG_DEFAULT;
112
113	if (prueth->phy_interface == PHY_INTERFACE_MODE_MII && slice == ICSS_MII0)
114		txcfg |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL;
115	else if (prueth->phy_interface != PHY_INTERFACE_MODE_MII && slice == ICSS_MII1)
116		txcfg |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL;
117
118	regmap_write(mii_rt, txcfg_reg, txcfg);
119	regmap_write(mii_rt, pcnt_reg, 0x1);
120}
121
122static void icssg_miig_queues_init(struct prueth_priv *priv, int slice)
123{
124	struct prueth *prueth = priv->prueth;
125	void __iomem *smem = (void __iomem *)prueth->shram.pa;
126	struct regmap *miig_rt = prueth->miig_rt;
127	int queue = 0, i, j;
128	u8 pd[ICSSG_SPECIAL_PD_SIZE];
129	u32 *pdword;
130
131	/* reset hwqueues */
132	if (slice)
133		queue = ICSSG_NUM_TX_QUEUES;
134
135	for (i = 0; i < ICSSG_NUM_TX_QUEUES; i++) {
136		regmap_write(miig_rt, ICSSG_QUEUE_RESET_OFFSET, queue);
137		queue++;
138	}
139
140	queue = slice ? RECYCLE_Q_SLICE1 : RECYCLE_Q_SLICE0;
141	regmap_write(miig_rt, ICSSG_QUEUE_RESET_OFFSET, queue);
142
143	for (i = 0; i < ICSSG_NUM_OTHER_QUEUES; i++) {
144		regmap_write(miig_rt, ICSSG_QUEUE_RESET_OFFSET,
145			     hwq_map[slice][i].queue);
146	}
147
148	/* initialize packet descriptors in SMEM */
149	/* push pakcet descriptors to hwqueues */
150
151	pdword = (u32 *)pd;
152	for (j = 0; j < ICSSG_NUM_OTHER_QUEUES; j++) {
153		struct map *mp;
154		int pd_size, num_pds;
155		u32 pdaddr;
156
157		mp = &hwq_map[slice][j];
158		if (mp->special) {
159			pd_size = ICSSG_SPECIAL_PD_SIZE;
160			num_pds = ICSSG_NUM_SPECIAL_PDS;
161		} else	{
162			pd_size = ICSSG_NORMAL_PD_SIZE;
163			num_pds = ICSSG_NUM_NORMAL_PDS;
164		}
165
166		for (i = 0; i < num_pds; i++) {
167			memset(pd, 0, pd_size);
168
169			pdword[0] &= cpu_to_le32(ICSSG_FLAG_MASK);
170			pdword[0] |= cpu_to_le32(mp->flags);
171			pdaddr = mp->pd_addr_start + i * pd_size;
172
173			memcpy_toio(smem + pdaddr, pd, pd_size);
174			queue = mp->queue;
175			regmap_write(miig_rt, ICSSG_QUEUE_OFFSET + 4 * queue,
176				     pdaddr);
177		}
178	}
179}
180
181void icssg_config_ipg(struct prueth_priv *priv, int speed, int mii)
182{
183	struct prueth *prueth = priv->prueth;
184
185	switch (speed) {
186	case SPEED_1000:
187		icssg_mii_update_ipg(prueth->mii_rt, mii, MII_RT_TX_IPG_1G);
188		break;
189	case SPEED_100:
190		icssg_mii_update_ipg(prueth->mii_rt, mii, MII_RT_TX_IPG_100M);
191		break;
192	default:
193		/* Other links speeds not supported */
194		pr_err("Unsupported link speed\n");
195		return;
196	}
197}
198
199static void emac_r30_cmd_init(struct prueth_priv *priv)
200{
201	struct prueth *prueth = priv->prueth;
202	struct icssg_r30_cmd *p;
203	int i;
204
205	p = (struct icssg_r30_cmd *)(prueth->dram[priv->port_id].pa + MGR_R30_CMD_OFFSET);
206
207	for (i = 0; i < 4; i++)
208		writel(EMAC_NONE, &p->cmd[i]);
209}
210
211static int emac_r30_is_done(struct prueth_priv *priv)
212{
213	struct prueth *prueth = priv->prueth;
214	const struct icssg_r30_cmd *p;
215	int i;
216	u32 cmd;
217
218	p = (const struct icssg_r30_cmd *)(prueth->dram[priv->port_id].pa + MGR_R30_CMD_OFFSET);
219
220	for (i = 0; i < 4; i++) {
221		cmd = readl(&p->cmd[i]);
222		if (cmd != EMAC_NONE)
223			return 0;
224	}
225
226	return 1;
227}
228
229static int prueth_emac_buffer_setup(struct prueth_priv *priv)
230{
231	struct prueth *prueth = priv->prueth;
232	struct icssg_buffer_pool_cfg *bpool_cfg;
233	struct icssg_rxq_ctx *rxq_ctx;
234	int slice = priv->port_id;
235	u32 addr;
236	int i;
237
238	/* Layout to have 64KB aligned buffer pool
239	 * |BPOOL0|BPOOL1|RX_CTX0|RX_CTX1|
240	 */
241
242	addr = lower_32_bits(prueth->sram_pa);
243	if (slice)
244		addr += PRUETH_NUM_BUF_POOLS * PRUETH_EMAC_BUF_POOL_SIZE;
245
246	if (addr % SZ_64K) {
247		dev_warn(prueth->dev, "buffer pool needs to be 64KB aligned\n");
248		return -EINVAL;
249	}
250
251	bpool_cfg = (struct icssg_buffer_pool_cfg *)(prueth->dram[priv->port_id].pa + BUFFER_POOL_0_ADDR_OFFSET);
252	/* workaround for f/w bug. bpool 0 needs to be initilalized */
253	bpool_cfg[0].addr = cpu_to_le32(addr);
254	bpool_cfg[0].len = 0;
255
256	for (i = PRUETH_EMAC_BUF_POOL_START;
257	     i < (PRUETH_EMAC_BUF_POOL_START + PRUETH_NUM_BUF_POOLS);
258	     i++) {
259		bpool_cfg[i].addr = cpu_to_le32(addr);
260		bpool_cfg[i].len = cpu_to_le32(PRUETH_EMAC_BUF_POOL_SIZE);
261		addr += PRUETH_EMAC_BUF_POOL_SIZE;
262	}
263
264	if (!slice)
265		addr += PRUETH_NUM_BUF_POOLS * PRUETH_EMAC_BUF_POOL_SIZE;
266	else
267		addr += PRUETH_EMAC_RX_CTX_BUF_SIZE * 2;
268
269	rxq_ctx = (struct icssg_rxq_ctx *)(prueth->dram[priv->port_id].pa + HOST_RX_Q_PRE_CONTEXT_OFFSET);
270
271	for (i = 0; i < 3; i++)
272		rxq_ctx->start[i] = cpu_to_le32(addr);
273
274	addr += PRUETH_EMAC_RX_CTX_BUF_SIZE;
275	rxq_ctx->end = cpu_to_le32(addr);
276
277	/* Express RX buffer queue */
278	rxq_ctx = (struct icssg_rxq_ctx *)(prueth->dram[priv->port_id].pa + HOST_RX_Q_EXP_CONTEXT_OFFSET);
279	for (i = 0; i < 3; i++)
280		rxq_ctx->start[i] = cpu_to_le32(addr);
281
282	addr += PRUETH_EMAC_RX_CTX_BUF_SIZE;
283	rxq_ctx->end = cpu_to_le32(addr);
284
285	return 0;
286}
287
288static void icssg_init_emac_mode(struct prueth *prueth)
289{
290	u8 mac[6] = { 0 };
291
292	regmap_update_bits(prueth->miig_rt, FDB_GEN_CFG1, SMEM_VLAN_OFFSET_MASK, 0);
293	regmap_write(prueth->miig_rt, FDB_GEN_CFG2, 0);
294	/* Clear host MAC address */
295	icssg_class_set_host_mac_addr(prueth->miig_rt, mac);
296}
297
298int icssg_config(struct prueth_priv *priv)
299{
300	struct prueth *prueth = priv->prueth;
301	void *config = (void *)(prueth->dram[priv->port_id].pa + ICSSG_CONFIG_OFFSET);
302	u8 *cfg_byte_ptr = config;
303	struct icssg_flow_cfg *flow_cfg;
304	u32 mask;
305	int ret;
306
307	int slice = priv->port_id;
308
309	icssg_init_emac_mode(prueth);
310
311	memset_io(config, 0, TAS_GATE_MASK_LIST0);
312	icssg_miig_queues_init(priv, slice);
313
314	prueth->speed = SPEED_1000;
315	prueth->duplex = DUPLEX_FULL;
316	if (!phy_interface_is_rgmii(priv->phydev)) {
317		prueth->speed = SPEED_100;
318		prueth->duplex = DUPLEX_FULL;
319	}
320
321	regmap_update_bits(prueth->miig_rt, ICSSG_CFG_OFFSET,
322			   ICSSG_CFG_DEFAULT, ICSSG_CFG_DEFAULT);
323	icssg_miig_set_interface_mode(prueth->miig_rt, ICSS_MII0, prueth->phy_interface);
324	icssg_miig_set_interface_mode(prueth->miig_rt, ICSS_MII1, prueth->phy_interface);
325	icssg_config_mii_init(priv, slice);
326
327	icssg_config_ipg(priv, SPEED_1000, slice);
328	icssg_update_rgmii_cfg(prueth->miig_rt, SPEED_1000, true, slice, priv);
329
330	/* set GPI mode */
331	pruss_cfg_gpimode(prueth->pruss, slice, PRUSS_GPI_MODE_MII);
332
333	/* enable XFR shift for PRU and RTU */
334	mask = PRUSS_SPP_XFER_SHIFT_EN | PRUSS_SPP_RTU_XFR_SHIFT_EN;
335	pruss_cfg_update(prueth->pruss, PRUSS_CFG_SPP, mask, mask);
336
337	flow_cfg = config + PSI_L_REGULAR_FLOW_ID_BASE_OFFSET;
338	flow_cfg->rx_base_flow = prueth->dma_rx.id;
339	flow_cfg->mgm_base_flow = 0;
340	*(cfg_byte_ptr + SPL_PKT_DEFAULT_PRIORITY) = 0;
341	*(cfg_byte_ptr + QUEUE_NUM_UNTAGGED) = 0x0;
342
343	ret = prueth_emac_buffer_setup(priv);
344
345	if (ret)
346		return ret;
347
348	emac_r30_cmd_init(priv);
349	return 0;
350}
351
352/* commands to program ICSSG R30 registers */
353static struct icssg_r30_cmd emac_r32_bitmask[] = {
354	{{0xffff0004, 0xffff0100, 0xffff0004, EMAC_NONE}},	/* EMAC_PORT_DISABLE */
355	{{0xfffb0040, 0xfeff0200, 0xfeff0200, EMAC_NONE}},	/* EMAC_PORT_BLOCK */
356	{{0xffbb0000, 0xfcff0000, 0xdcfb0000, EMAC_NONE}},	/* EMAC_PORT_FORWARD */
357	{{0xffbb0000, 0xfcff0000, 0xfcff2000, EMAC_NONE}},	/* EMAC_PORT_FORWARD_WO_LEARNING */
358	{{0xffff0001, EMAC_NONE,  EMAC_NONE, EMAC_NONE}},	/* ACCEPT ALL */
359	{{0xfffe0002, EMAC_NONE,  EMAC_NONE, EMAC_NONE}},	/* ACCEPT TAGGED */
360	{{0xfffc0000, EMAC_NONE,  EMAC_NONE, EMAC_NONE}},	/* ACCEPT UNTAGGED and PRIO */
361	{{EMAC_NONE,  0xffff0020, EMAC_NONE, EMAC_NONE}},	/* TAS Trigger List change */
362	{{EMAC_NONE,  0xdfff1000, EMAC_NONE, EMAC_NONE}},	/* TAS set state ENABLE*/
363	{{EMAC_NONE,  0xefff2000, EMAC_NONE, EMAC_NONE}},	/* TAS set state RESET*/
364	{{EMAC_NONE,  0xcfff0000, EMAC_NONE, EMAC_NONE}},	/* TAS set state DISABLE*/
365	{{EMAC_NONE,  EMAC_NONE,  0xffff0400, EMAC_NONE}},	/* UC flooding ENABLE*/
366	{{EMAC_NONE,  EMAC_NONE,  0xfbff0000, EMAC_NONE}},	/* UC flooding DISABLE*/
367	{{EMAC_NONE,  EMAC_NONE,  0xffff0800, EMAC_NONE}},	/* MC flooding ENABLE*/
368	{{EMAC_NONE,  EMAC_NONE,  0xf7ff0000, EMAC_NONE}},	/* MC flooding DISABLE*/
369	{{EMAC_NONE,  0xffff4000, EMAC_NONE, EMAC_NONE}},	/* Preemption on Tx ENABLE*/
370	{{EMAC_NONE,  0xbfff0000, EMAC_NONE, EMAC_NONE}}	/* Preemption on Tx DISABLE*/
371};
372
373int emac_set_port_state(struct prueth_priv *priv,
374			enum icssg_port_state_cmd cmd)
375{
376	struct prueth *prueth = priv->prueth;
377	struct icssg_r30_cmd *p;
378	int ret = -ETIMEDOUT;
379	int timeout = 10;
380	int i;
381
382	p = (struct icssg_r30_cmd *)(prueth->dram[priv->port_id].pa + MGR_R30_CMD_OFFSET);
383
384	if (cmd >= ICSSG_EMAC_PORT_MAX_COMMANDS) {
385		dev_err(prueth->dev, "invalid port command\n");
386		return -EINVAL;
387	}
388
389	for (i = 0; i < 4; i++)
390		writel(emac_r32_bitmask[cmd].cmd[i], &p->cmd[i]);
391
392	/* wait for done */
393	while (timeout) {
394		if (emac_r30_is_done(priv)) {
395			ret = 0;
396			break;
397		}
398
399		udelay(2000);
400		timeout--;
401	}
402
403	if (ret == -ETIMEDOUT)
404		dev_err(prueth->dev, "timeout waiting for command done\n");
405
406	return ret;
407}
408
409int icssg_send_fdb_msg(struct prueth_priv *priv, struct mgmt_cmd *cmd,
410		       struct mgmt_cmd_rsp *rsp)
411{
412	struct prueth *prueth = priv->prueth;
413	int slice = priv->port_id;
414	int ret, addr;
415
416	addr = icssg_queue_pop(prueth, slice == 0 ?
417			       ICSSG_CMD_POP_SLICE0 : ICSSG_CMD_POP_SLICE1);
418	if (addr < 0)
419		return addr;
420
421	/* First 4 bytes have FW owned buffer linking info which should
422	 * not be touched
423	 */
424	memcpy_toio((void __iomem *)prueth->shram.pa + addr + 4, cmd, sizeof(*cmd));
425	icssg_queue_push(prueth, slice == 0 ?
426			 ICSSG_CMD_PUSH_SLICE0 : ICSSG_CMD_PUSH_SLICE1, addr);
427	ret = read_poll_timeout(icssg_queue_pop, addr, addr >= 0,
428				2000, 20000000, prueth, slice == 0 ?
429				ICSSG_RSP_POP_SLICE0 : ICSSG_RSP_POP_SLICE1);
430
431	if (ret) {
432		dev_err(prueth->dev, "Timedout sending HWQ message\n");
433		return ret;
434	}
435
436	memcpy_fromio(rsp, (void __iomem *)prueth->shram.pa + addr, sizeof(*rsp));
437	/* Return buffer back for to pool */
438	icssg_queue_push(prueth, slice == 0 ?
439			 ICSSG_RSP_PUSH_SLICE0 : ICSSG_RSP_PUSH_SLICE1, addr);
440
441	return 0;
442}
443
444int emac_fdb_flow_id_updated(struct prueth_priv *priv)
445{
446	struct mgmt_cmd_rsp fdb_cmd_rsp = { 0 };
447	struct prueth *prueth = priv->prueth;
448	struct mgmt_cmd fdb_cmd = { 0 };
449	int slice = priv->port_id;
450	int ret = 0;
451
452	fdb_cmd.header = ICSSG_FW_MGMT_CMD_HEADER;
453	fdb_cmd.type   = ICSSG_FW_MGMT_FDB_CMD_TYPE_RX_FLOW;
454	fdb_cmd.seqnum = ++(prueth->icssg_hwcmdseq);
455	fdb_cmd.param  = 0;
456
457	fdb_cmd.param |= (slice << 4);
458	fdb_cmd.cmd_args[0] = 0;
459
460	ret = icssg_send_fdb_msg(priv, &fdb_cmd, &fdb_cmd_rsp);
461	if (ret)
462		return ret;
463
464	if (fdb_cmd.seqnum != fdb_cmd_rsp.seqnum) {
465		dev_err(prueth->dev, "seqnum doesn't match, cmd.seqnum %d != rsp.seqnum %d\n",
466			fdb_cmd.seqnum, fdb_cmd_rsp.seqnum);
467		return -EINVAL;
468	}
469
470	if (fdb_cmd_rsp.status == 1)
471		return 0;
472
473	return -EINVAL;
474}
475