blob: 5f132d0525c61491942244ad63b4bea3a8348124 [file] [log] [blame]
MD Danish Anwar89d709b2024-04-04 12:38:00 +05301// 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>
MD Danish Anwar6b8c29e2024-04-04 12:38:03 +053012#include <linux/iopoll.h>
MD Danish Anwar89d709b2024-04-04 12:38:00 +053013
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}
MD Danish Anwar6b8c29e2024-04-04 12:38:03 +0530408
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}