Alex Marginean | 4ea74fd | 2021-01-25 14:23:55 +0200 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause |
| 2 | /* |
| 3 | * Felix (VSC9959) Ethernet switch driver |
| 4 | * Copyright 2018-2021 NXP Semiconductors |
| 5 | */ |
| 6 | |
| 7 | /* |
| 8 | * This driver is used for the Ethernet switch integrated into NXP LS1028A. |
| 9 | * Felix switch is derived from Microsemi Ocelot but there are several NXP |
| 10 | * adaptations that makes the two U-Boot drivers largely incompatible. |
| 11 | * |
| 12 | * Felix on LS1028A has 4 front panel ports and two internal ports, connected |
| 13 | * to ENETC interfaces. We're using one of the ENETC interfaces to push traffic |
| 14 | * into the switch. Injection/extraction headers are used to identify |
| 15 | * egress/ingress ports in the switch for Tx/Rx. |
| 16 | */ |
| 17 | |
| 18 | #include <dm/device_compat.h> |
| 19 | #include <linux/delay.h> |
| 20 | #include <net/dsa.h> |
| 21 | #include <asm/io.h> |
| 22 | #include <miiphy.h> |
| 23 | #include <pci.h> |
| 24 | |
| 25 | /* defines especially around PCS are reused from enetc */ |
| 26 | #include "../fsl_enetc.h" |
| 27 | |
| 28 | #define PCI_DEVICE_ID_FELIX_ETHSW 0xEEF0 |
| 29 | |
| 30 | /* Felix has in fact 6 ports, but we don't use the last internal one */ |
| 31 | #define FELIX_PORT_COUNT 5 |
| 32 | /* Front panel port mask */ |
| 33 | #define FELIX_FP_PORT_MASK 0xf |
| 34 | |
| 35 | /* Register map for BAR4 */ |
| 36 | #define FELIX_SYS 0x010000 |
| 37 | #define FELIX_ES0 0x040000 |
| 38 | #define FELIX_IS1 0x050000 |
| 39 | #define FELIX_IS2 0x060000 |
| 40 | #define FELIX_GMII(port) (0x100000 + (port) * 0x10000) |
| 41 | #define FELIX_QSYS 0x200000 |
| 42 | |
| 43 | #define FELIX_SYS_SYSTEM (FELIX_SYS + 0x00000E00) |
| 44 | #define FELIX_SYS_SYSTEM_EN BIT(0) |
| 45 | #define FELIX_SYS_RAM_CTRL (FELIX_SYS + 0x00000F24) |
| 46 | #define FELIX_SYS_RAM_CTRL_INIT BIT(1) |
| 47 | #define FELIX_SYS_SYSTEM_PORT_MODE(a) (FELIX_SYS_SYSTEM + 0xC + (a) * 4) |
| 48 | #define FELIX_SYS_SYSTEM_PORT_MODE_CPU 0x0000001e |
| 49 | |
| 50 | #define FELIX_ES0_TCAM_CTRL (FELIX_ES0 + 0x000003C0) |
| 51 | #define FELIX_ES0_TCAM_CTRL_EN BIT(0) |
| 52 | #define FELIX_IS1_TCAM_CTRL (FELIX_IS1 + 0x000003C0) |
| 53 | #define FELIX_IS1_TCAM_CTRL_EN BIT(0) |
| 54 | #define FELIX_IS2_TCAM_CTRL (FELIX_IS2 + 0x000003C0) |
| 55 | #define FELIX_IS2_TCAM_CTRL_EN BIT(0) |
| 56 | |
| 57 | #define FELIX_GMII_CLOCK_CFG(port) (FELIX_GMII(port) + 0x00000000) |
| 58 | #define FELIX_GMII_CLOCK_CFG_LINK_1G 1 |
| 59 | #define FELIX_GMII_CLOCK_CFG_LINK_100M 2 |
| 60 | #define FELIX_GMII_CLOCK_CFG_LINK_10M 3 |
| 61 | #define FELIX_GMII_MAC_ENA_CFG(port) (FELIX_GMII(port) + 0x0000001C) |
| 62 | #define FELIX_GMII_MAX_ENA_CFG_TX BIT(0) |
| 63 | #define FELIX_GMII_MAX_ENA_CFG_RX BIT(4) |
| 64 | #define FELIX_GMII_MAC_IFG_CFG(port) (FELIX_GMII(port) + 0x0000001C + 0x14) |
| 65 | #define FELIX_GMII_MAC_IFG_CFG_DEF 0x515 |
| 66 | |
| 67 | #define FELIX_QSYS_SYSTEM (FELIX_QSYS + 0x0000F460) |
| 68 | #define FELIX_QSYS_SYSTEM_SW_PORT_MODE(a) \ |
| 69 | (FELIX_QSYS_SYSTEM + 0x20 + (a) * 4) |
| 70 | #define FELIX_QSYS_SYSTEM_SW_PORT_ENA BIT(14) |
| 71 | #define FELIX_QSYS_SYSTEM_SW_PORT_LOSSY BIT(9) |
| 72 | #define FELIX_QSYS_SYSTEM_SW_PORT_SCH(a) (((a) & 0x3800) << 11) |
| 73 | #define FELIX_QSYS_SYSTEM_EXT_CPU_CFG (FELIX_QSYS_SYSTEM + 0x80) |
| 74 | #define FELIX_QSYS_SYSTEM_EXT_CPU_PORT(a) (((a) & 0xf) << 8 | 0xff) |
| 75 | |
| 76 | /* internal MDIO in BAR0 */ |
| 77 | #define FELIX_PM_IMDIO_BASE 0x8030 |
| 78 | |
| 79 | /* Serdes block on LS1028A */ |
| 80 | #define FELIX_SERDES_BASE 0x1ea0000L |
| 81 | #define FELIX_SERDES_LNATECR0(lane) (FELIX_SERDES_BASE + 0x818 + \ |
| 82 | (lane) * 0x40) |
| 83 | #define FELIX_SERDES_LNATECR0_ADPT_EQ 0x00003000 |
| 84 | #define FELIX_SERDES_SGMIICR1(lane) (FELIX_SERDES_BASE + 0x1804 + \ |
| 85 | (lane) * 0x10) |
| 86 | #define FELIX_SERDES_SGMIICR1_SGPCS BIT(11) |
| 87 | #define FELIX_SERDES_SGMIICR1_MDEV(a) (((a) & 0x1f) << 27) |
| 88 | |
| 89 | #define FELIX_PCS_CTRL 0 |
| 90 | #define FELIX_PCS_CTRL_RST BIT(15) |
| 91 | |
| 92 | /* |
| 93 | * The long prefix format used here contains two dummy MAC addresses, a magic |
| 94 | * value in place of a VLAN tag followed by the extraction/injection header and |
| 95 | * the original L2 frame. Out of all this we only use the port ID. |
| 96 | */ |
| 97 | #define FELIX_DSA_TAG_LEN sizeof(struct felix_dsa_tag) |
| 98 | #define FELIX_DSA_TAG_MAGIC 0x0a008088 |
| 99 | #define FELIX_DSA_TAG_INJ_PORT 7 |
| 100 | #define FELIX_DSA_TAG_INJ_PORT_SET(a) (0x1 << ((a) & FELIX_FP_PORT_MASK)) |
| 101 | #define FELIX_DSA_TAG_EXT_PORT 10 |
| 102 | #define FELIX_DSA_TAG_EXT_PORT_GET(a) ((a) >> 3) |
| 103 | |
| 104 | struct felix_dsa_tag { |
| 105 | uchar d_mac[6]; |
| 106 | uchar s_mac[6]; |
| 107 | u32 magic; |
| 108 | uchar meta[16]; |
| 109 | }; |
| 110 | |
| 111 | struct felix_priv { |
| 112 | void *regs_base; |
| 113 | void *imdio_base; |
| 114 | struct mii_dev imdio; |
| 115 | }; |
| 116 | |
| 117 | /* MDIO wrappers, we're using these to drive internal MDIO to get to serdes */ |
| 118 | static int felix_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) |
| 119 | { |
| 120 | struct enetc_mdio_priv priv; |
| 121 | |
| 122 | priv.regs_base = bus->priv; |
| 123 | return enetc_mdio_read_priv(&priv, addr, devad, reg); |
| 124 | } |
| 125 | |
| 126 | static int felix_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, |
| 127 | u16 val) |
| 128 | { |
| 129 | struct enetc_mdio_priv priv; |
| 130 | |
| 131 | priv.regs_base = bus->priv; |
| 132 | return enetc_mdio_write_priv(&priv, addr, devad, reg, val); |
| 133 | } |
| 134 | |
| 135 | /* set up serdes for SGMII */ |
| 136 | static void felix_init_sgmii(struct mii_dev *imdio, int pidx, bool an) |
| 137 | { |
| 138 | u16 reg; |
| 139 | |
| 140 | /* set up PCS lane address */ |
| 141 | out_le32(FELIX_SERDES_SGMIICR1(pidx), FELIX_SERDES_SGMIICR1_SGPCS | |
| 142 | FELIX_SERDES_SGMIICR1_MDEV(pidx)); |
| 143 | |
| 144 | /* |
| 145 | * Set to SGMII mode, for 1Gbps enable AN, for 2.5Gbps set fixed speed. |
| 146 | * Although fixed speed is 1Gbps, we could be running at 2.5Gbps based |
| 147 | * on PLL configuration. Setting 1G for 2.5G here is counter intuitive |
| 148 | * but intentional. |
| 149 | */ |
| 150 | reg = ENETC_PCS_IF_MODE_SGMII; |
| 151 | reg |= an ? ENETC_PCS_IF_MODE_SGMII_AN : ENETC_PCS_IF_MODE_SPEED_1G; |
| 152 | felix_mdio_write(imdio, pidx, MDIO_DEVAD_NONE, |
| 153 | ENETC_PCS_IF_MODE, reg); |
| 154 | |
| 155 | /* Dev ability - SGMII */ |
| 156 | felix_mdio_write(imdio, pidx, MDIO_DEVAD_NONE, |
| 157 | ENETC_PCS_DEV_ABILITY, ENETC_PCS_DEV_ABILITY_SGMII); |
| 158 | |
| 159 | /* Adjust link timer for SGMII */ |
| 160 | felix_mdio_write(imdio, pidx, MDIO_DEVAD_NONE, |
| 161 | ENETC_PCS_LINK_TIMER1, ENETC_PCS_LINK_TIMER1_VAL); |
| 162 | felix_mdio_write(imdio, pidx, MDIO_DEVAD_NONE, |
| 163 | ENETC_PCS_LINK_TIMER2, ENETC_PCS_LINK_TIMER2_VAL); |
| 164 | |
| 165 | reg = ENETC_PCS_CR_DEF_VAL; |
| 166 | reg |= an ? ENETC_PCS_CR_RESET_AN : ENETC_PCS_CR_RST; |
| 167 | /* restart PCS AN */ |
| 168 | felix_mdio_write(imdio, pidx, MDIO_DEVAD_NONE, |
| 169 | ENETC_PCS_CR, reg); |
| 170 | } |
| 171 | |
| 172 | /* set up MAC and serdes for (Q)SXGMII */ |
| 173 | static int felix_init_sxgmii(struct mii_dev *imdio, int pidx) |
| 174 | { |
| 175 | int timeout = 1000; |
| 176 | |
| 177 | /* set up transit equalization control on serdes lane */ |
| 178 | out_le32(FELIX_SERDES_LNATECR0(1), FELIX_SERDES_LNATECR0_ADPT_EQ); |
| 179 | |
| 180 | /*reset lane */ |
| 181 | felix_mdio_write(imdio, pidx, MDIO_MMD_PCS, FELIX_PCS_CTRL, |
| 182 | FELIX_PCS_CTRL_RST); |
| 183 | while (felix_mdio_read(imdio, pidx, MDIO_MMD_PCS, |
| 184 | FELIX_PCS_CTRL) & FELIX_PCS_CTRL_RST && |
| 185 | --timeout) { |
| 186 | mdelay(10); |
| 187 | } |
| 188 | if (felix_mdio_read(imdio, pidx, MDIO_MMD_PCS, |
| 189 | FELIX_PCS_CTRL) & FELIX_PCS_CTRL_RST) |
| 190 | return -ETIME; |
| 191 | |
| 192 | /* Dev ability - SXGMII */ |
| 193 | felix_mdio_write(imdio, pidx, ENETC_PCS_DEVAD_REPL, |
| 194 | ENETC_PCS_DEV_ABILITY, ENETC_PCS_DEV_ABILITY_SXGMII); |
| 195 | |
| 196 | /* Restart PCS AN */ |
| 197 | felix_mdio_write(imdio, pidx, ENETC_PCS_DEVAD_REPL, ENETC_PCS_CR, |
| 198 | ENETC_PCS_CR_RST | ENETC_PCS_CR_RESET_AN); |
| 199 | felix_mdio_write(imdio, pidx, ENETC_PCS_DEVAD_REPL, |
| 200 | ENETC_PCS_REPL_LINK_TIMER_1, |
| 201 | ENETC_PCS_REPL_LINK_TIMER_1_DEF); |
| 202 | felix_mdio_write(imdio, pidx, ENETC_PCS_DEVAD_REPL, |
| 203 | ENETC_PCS_REPL_LINK_TIMER_2, |
| 204 | ENETC_PCS_REPL_LINK_TIMER_2_DEF); |
| 205 | |
| 206 | return 0; |
| 207 | } |
| 208 | |
| 209 | /* Apply protocol specific configuration to MAC, serdes as needed */ |
| 210 | static void felix_start_pcs(struct udevice *dev, int port, |
| 211 | struct phy_device *phy, struct mii_dev *imdio) |
| 212 | { |
| 213 | bool autoneg = true; |
| 214 | |
| 215 | if (phy->phy_id == PHY_FIXED_ID || |
| 216 | phy->interface == PHY_INTERFACE_MODE_SGMII_2500) |
| 217 | autoneg = false; |
| 218 | |
| 219 | switch (phy->interface) { |
| 220 | case PHY_INTERFACE_MODE_SGMII: |
| 221 | case PHY_INTERFACE_MODE_SGMII_2500: |
| 222 | case PHY_INTERFACE_MODE_QSGMII: |
| 223 | felix_init_sgmii(imdio, port, autoneg); |
| 224 | break; |
| 225 | case PHY_INTERFACE_MODE_XGMII: |
| 226 | case PHY_INTERFACE_MODE_XFI: |
| 227 | case PHY_INTERFACE_MODE_USXGMII: |
| 228 | if (felix_init_sxgmii(imdio, port)) |
| 229 | dev_err(dev, "PCS reset timeout on port %d\n", port); |
| 230 | break; |
| 231 | default: |
| 232 | break; |
| 233 | } |
| 234 | } |
| 235 | |
| 236 | void felix_init(struct udevice *dev) |
| 237 | { |
| 238 | struct dsa_pdata *pdata = dev_get_uclass_plat(dev); |
| 239 | struct felix_priv *priv = dev_get_priv(dev); |
| 240 | void *base = priv->regs_base; |
| 241 | int timeout = 100; |
| 242 | |
| 243 | /* Init core memories */ |
| 244 | out_le32(base + FELIX_SYS_RAM_CTRL, FELIX_SYS_RAM_CTRL_INIT); |
| 245 | while (in_le32(base + FELIX_SYS_RAM_CTRL) & FELIX_SYS_RAM_CTRL_INIT && |
| 246 | --timeout) |
| 247 | udelay(10); |
| 248 | if (in_le32(base + FELIX_SYS_RAM_CTRL) & FELIX_SYS_RAM_CTRL_INIT) |
| 249 | dev_err(dev, "Timeout waiting for switch memories\n"); |
| 250 | |
| 251 | /* Start switch core, set up ES0, IS1, IS2 */ |
| 252 | out_le32(base + FELIX_SYS_SYSTEM, FELIX_SYS_SYSTEM_EN); |
| 253 | out_le32(base + FELIX_ES0_TCAM_CTRL, FELIX_ES0_TCAM_CTRL_EN); |
| 254 | out_le32(base + FELIX_IS1_TCAM_CTRL, FELIX_IS1_TCAM_CTRL_EN); |
| 255 | out_le32(base + FELIX_IS2_TCAM_CTRL, FELIX_IS2_TCAM_CTRL_EN); |
| 256 | udelay(20); |
| 257 | |
| 258 | priv->imdio.read = felix_mdio_read; |
| 259 | priv->imdio.write = felix_mdio_write; |
| 260 | priv->imdio.priv = priv->imdio_base + FELIX_PM_IMDIO_BASE; |
| 261 | strncpy(priv->imdio.name, dev->name, MDIO_NAME_LEN); |
| 262 | |
| 263 | /* set up CPU port */ |
| 264 | out_le32(base + FELIX_QSYS_SYSTEM_EXT_CPU_CFG, |
| 265 | FELIX_QSYS_SYSTEM_EXT_CPU_PORT(pdata->cpu_port)); |
| 266 | out_le32(base + FELIX_SYS_SYSTEM_PORT_MODE(pdata->cpu_port), |
| 267 | FELIX_SYS_SYSTEM_PORT_MODE_CPU); |
| 268 | } |
| 269 | |
| 270 | /* |
| 271 | * Probe Felix: |
| 272 | * - enable the PCI function |
| 273 | * - map BAR 4 |
| 274 | * - init switch core and port registers |
| 275 | */ |
| 276 | static int felix_probe(struct udevice *dev) |
| 277 | { |
| 278 | struct felix_priv *priv = dev_get_priv(dev); |
| 279 | |
| 280 | if (ofnode_valid(dev_ofnode(dev)) && |
| 281 | !ofnode_is_available(dev_ofnode(dev))) { |
| 282 | dev_dbg(dev, "switch disabled\n"); |
| 283 | return -ENODEV; |
| 284 | } |
| 285 | |
| 286 | priv->imdio_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, 0); |
| 287 | if (!priv->imdio_base) { |
| 288 | dev_err(dev, "failed to map BAR0\n"); |
| 289 | return -EINVAL; |
| 290 | } |
| 291 | |
| 292 | priv->regs_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_4, 0); |
| 293 | if (!priv->regs_base) { |
| 294 | dev_err(dev, "failed to map BAR4\n"); |
| 295 | return -EINVAL; |
| 296 | } |
| 297 | |
| 298 | /* register internal MDIO for debug */ |
| 299 | if (!miiphy_get_dev_by_name(dev->name)) { |
| 300 | struct mii_dev *mii_bus; |
| 301 | |
| 302 | mii_bus = mdio_alloc(); |
| 303 | mii_bus->read = felix_mdio_read; |
| 304 | mii_bus->write = felix_mdio_write; |
| 305 | mii_bus->priv = priv->imdio_base + FELIX_PM_IMDIO_BASE; |
| 306 | strncpy(mii_bus->name, dev->name, MDIO_NAME_LEN); |
| 307 | mdio_register(mii_bus); |
| 308 | } |
| 309 | |
| 310 | dm_pci_clrset_config16(dev, PCI_COMMAND, 0, PCI_COMMAND_MEMORY); |
| 311 | |
| 312 | dsa_set_tagging(dev, FELIX_DSA_TAG_LEN, 0); |
| 313 | |
| 314 | /* set up registers */ |
| 315 | felix_init(dev); |
| 316 | |
| 317 | return 0; |
| 318 | } |
| 319 | |
| 320 | static int felix_port_enable(struct udevice *dev, int port, |
| 321 | struct phy_device *phy) |
| 322 | { |
| 323 | int supported = PHY_GBIT_FEATURES | SUPPORTED_2500baseX_Full; |
| 324 | struct felix_priv *priv = dev_get_priv(dev); |
| 325 | void *base = priv->regs_base; |
| 326 | |
| 327 | /* Set up MAC registers */ |
| 328 | out_le32(base + FELIX_GMII_CLOCK_CFG(port), |
| 329 | FELIX_GMII_CLOCK_CFG_LINK_1G); |
| 330 | |
| 331 | out_le32(base + FELIX_GMII_MAC_IFG_CFG(port), |
| 332 | FELIX_GMII_MAC_IFG_CFG_DEF); |
| 333 | |
| 334 | out_le32(base + FELIX_GMII_MAC_ENA_CFG(port), |
| 335 | FELIX_GMII_MAX_ENA_CFG_TX | FELIX_GMII_MAX_ENA_CFG_RX); |
| 336 | |
| 337 | out_le32(base + FELIX_QSYS_SYSTEM_SW_PORT_MODE(port), |
| 338 | FELIX_QSYS_SYSTEM_SW_PORT_ENA | |
| 339 | FELIX_QSYS_SYSTEM_SW_PORT_LOSSY | |
| 340 | FELIX_QSYS_SYSTEM_SW_PORT_SCH(1)); |
| 341 | |
| 342 | felix_start_pcs(dev, port, phy, &priv->imdio); |
| 343 | |
| 344 | phy->supported &= supported; |
| 345 | phy->advertising &= supported; |
| 346 | phy_config(phy); |
| 347 | |
| 348 | phy_startup(phy); |
| 349 | |
| 350 | return 0; |
| 351 | } |
| 352 | |
| 353 | static void felix_port_disable(struct udevice *dev, int pidx, |
| 354 | struct phy_device *phy) |
| 355 | { |
| 356 | struct felix_priv *priv = dev_get_priv(dev); |
| 357 | void *base = priv->regs_base; |
| 358 | |
| 359 | out_le32(base + FELIX_GMII_MAC_ENA_CFG(pidx), 0); |
| 360 | |
| 361 | out_le32(base + FELIX_QSYS_SYSTEM_SW_PORT_MODE(pidx), |
| 362 | FELIX_QSYS_SYSTEM_SW_PORT_LOSSY | |
| 363 | FELIX_QSYS_SYSTEM_SW_PORT_SCH(1)); |
| 364 | |
| 365 | /* |
| 366 | * we don't call phy_shutdown here to avoid waiting next time we use |
| 367 | * the port, but the downside is that remote side will think we're |
| 368 | * actively processing traffic although we are not. |
| 369 | */ |
| 370 | } |
| 371 | |
| 372 | static int felix_xmit(struct udevice *dev, int pidx, void *packet, int length) |
| 373 | { |
| 374 | struct felix_dsa_tag *tag = packet; |
| 375 | |
| 376 | tag->magic = FELIX_DSA_TAG_MAGIC; |
| 377 | tag->meta[FELIX_DSA_TAG_INJ_PORT] = FELIX_DSA_TAG_INJ_PORT_SET(pidx); |
| 378 | |
| 379 | return 0; |
| 380 | } |
| 381 | |
| 382 | static int felix_rcv(struct udevice *dev, int *pidx, void *packet, int length) |
| 383 | { |
| 384 | struct felix_dsa_tag *tag = packet; |
| 385 | |
| 386 | if (tag->magic != FELIX_DSA_TAG_MAGIC) |
| 387 | return -EINVAL; |
| 388 | |
| 389 | *pidx = FELIX_DSA_TAG_EXT_PORT_GET(tag->meta[FELIX_DSA_TAG_EXT_PORT]); |
| 390 | |
| 391 | return 0; |
| 392 | } |
| 393 | |
| 394 | static const struct dsa_ops felix_dsa_ops = { |
| 395 | .port_enable = felix_port_enable, |
| 396 | .port_disable = felix_port_disable, |
| 397 | .xmit = felix_xmit, |
| 398 | .rcv = felix_rcv, |
| 399 | }; |
| 400 | |
| 401 | U_BOOT_DRIVER(felix_ethsw) = { |
| 402 | .name = "felix-switch", |
| 403 | .id = UCLASS_DSA, |
| 404 | .probe = felix_probe, |
| 405 | .ops = &felix_dsa_ops, |
| 406 | .priv_auto = sizeof(struct felix_priv), |
| 407 | }; |
| 408 | |
| 409 | static struct pci_device_id felix_ethsw_ids[] = { |
| 410 | { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_FELIX_ETHSW) }, |
| 411 | {} |
| 412 | }; |
| 413 | |
| 414 | U_BOOT_PCI_DEVICE(felix_ethsw, felix_ethsw_ids); |