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