Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

net: dsa: microchip: Add SGMII port support to KSZ9477 switch

The KSZ9477 switch driver uses the XPCS driver to operate its SGMII
port. However there are some hardware bugs in the KSZ9477 SGMII
module so workarounds are needed. There was a proposal to update the
XPCS driver to accommodate KSZ9477, but the new code is not generic
enough to be used by other vendors. It is better to do all these
workarounds inside the KSZ9477 driver instead of modifying the XPCS
driver.

There are 3 hardware issues. The first is the MII_ADVERTISE register
needs to be write once after reset for the correct code word to be
sent. The XPCS driver disables auto-negotiation first before
configuring the SGMII/1000BASE-X mode and then enables it back. The
KSZ9477 driver then writes the MII_ADVERTISE register before enabling
auto-negotiation. In 1000BASE-X mode the MII_ADVERTISE register will
be set, so KSZ9477 driver does not need to write it.

The second issue is the MII_BMCR register needs to set the exact speed
and duplex mode when running in SGMII mode. During link polling the
KSZ9477 will check the speed and duplex mode are different from
previous ones and update the MII_BMCR register accordingly.

The last issue is 1000BASE-X mode does not work with auto-negotiation
on. The cause is the local port hardware does not know the link is up
and so network traffic is not forwarded. The workaround is to write 2
additional bits when 1000BASE-X mode is configured.

Note the SGMII interrupt in the port cannot be masked. As that
interrupt is not handled in the KSZ9477 driver the SGMII interrupt bit
will not be set even when the XPCS driver sets it.

Signed-off-by: Tristram Ha <tristram.ha@microchip.com>
Reviewed-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Tested-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Link: https://patch.msgid.link/20250520230720.23425-1-Tristram.Ha@microchip.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Tristram Ha and committed by
Paolo Abeni
e8c35bfc 405b0d61

+252 -7
+1
drivers/net/dsa/microchip/Kconfig
··· 6 6 select NET_DSA_TAG_NONE 7 7 select NET_IEEE8021Q_HELPERS 8 8 select DCB 9 + select PCS_XPCS 9 10 help 10 11 This driver adds support for Microchip KSZ8, KSZ9 and 11 12 LAN937X series switch chips, being KSZ8863/8873,
+193 -1
drivers/net/dsa/microchip/ksz9477.c
··· 2 2 /* 3 3 * Microchip KSZ9477 switch driver main logic 4 4 * 5 - * Copyright (C) 2017-2024 Microchip Technology Inc. 5 + * Copyright (C) 2017-2025 Microchip Technology Inc. 6 6 */ 7 7 8 8 #include <linux/kernel.h> ··· 159 159 REG_SW_ALU_STAT_CTRL__4, 160 160 val, !(val & ALU_STAT_START), 161 161 10, 1000); 162 + } 163 + 164 + static void port_sgmii_s(struct ksz_device *dev, uint port, u16 devid, u16 reg) 165 + { 166 + u32 data; 167 + 168 + data = (devid & MII_MMD_CTRL_DEVAD_MASK) << 16; 169 + data |= reg; 170 + ksz_pwrite32(dev, port, REG_PORT_SGMII_ADDR__4, data); 171 + } 172 + 173 + static void port_sgmii_r(struct ksz_device *dev, uint port, u16 devid, u16 reg, 174 + u16 *buf) 175 + { 176 + port_sgmii_s(dev, port, devid, reg); 177 + ksz_pread16(dev, port, REG_PORT_SGMII_DATA__4 + 2, buf); 178 + } 179 + 180 + static void port_sgmii_w(struct ksz_device *dev, uint port, u16 devid, u16 reg, 181 + u16 buf) 182 + { 183 + port_sgmii_s(dev, port, devid, reg); 184 + ksz_pwrite32(dev, port, REG_PORT_SGMII_DATA__4, buf); 185 + } 186 + 187 + static int ksz9477_pcs_read(struct mii_bus *bus, int phy, int mmd, int reg) 188 + { 189 + struct ksz_device *dev = bus->priv; 190 + int port = ksz_get_sgmii_port(dev); 191 + u16 val; 192 + 193 + port_sgmii_r(dev, port, mmd, reg, &val); 194 + 195 + /* Simulate a value to activate special code in the XPCS driver if 196 + * supported. 197 + */ 198 + if (mmd == MDIO_MMD_PMAPMD) { 199 + if (reg == MDIO_DEVID1) 200 + val = 0x9477; 201 + else if (reg == MDIO_DEVID2) 202 + val = 0x22 << 10; 203 + } else if (mmd == MDIO_MMD_VEND2) { 204 + struct ksz_port *p = &dev->ports[port]; 205 + 206 + /* Need to update MII_BMCR register with the exact speed and 207 + * duplex mode when running in SGMII mode and this register is 208 + * used to detect connected speed in that mode. 209 + */ 210 + if (reg == MMD_SR_MII_AUTO_NEG_STATUS) { 211 + int duplex, speed; 212 + 213 + if (val & SR_MII_STAT_LINK_UP) { 214 + speed = (val >> SR_MII_STAT_S) & SR_MII_STAT_M; 215 + if (speed == SR_MII_STAT_1000_MBPS) 216 + speed = SPEED_1000; 217 + else if (speed == SR_MII_STAT_100_MBPS) 218 + speed = SPEED_100; 219 + else 220 + speed = SPEED_10; 221 + 222 + if (val & SR_MII_STAT_FULL_DUPLEX) 223 + duplex = DUPLEX_FULL; 224 + else 225 + duplex = DUPLEX_HALF; 226 + 227 + if (!p->phydev.link || 228 + p->phydev.speed != speed || 229 + p->phydev.duplex != duplex) { 230 + u16 ctrl; 231 + 232 + p->phydev.link = 1; 233 + p->phydev.speed = speed; 234 + p->phydev.duplex = duplex; 235 + port_sgmii_r(dev, port, mmd, MII_BMCR, 236 + &ctrl); 237 + ctrl &= BMCR_ANENABLE; 238 + ctrl |= mii_bmcr_encode_fixed(speed, 239 + duplex); 240 + port_sgmii_w(dev, port, mmd, MII_BMCR, 241 + ctrl); 242 + } 243 + } else { 244 + p->phydev.link = 0; 245 + } 246 + } else if (reg == MII_BMSR) { 247 + p->phydev.link = (val & BMSR_LSTATUS); 248 + } 249 + } 250 + 251 + return val; 252 + } 253 + 254 + static int ksz9477_pcs_write(struct mii_bus *bus, int phy, int mmd, int reg, 255 + u16 val) 256 + { 257 + struct ksz_device *dev = bus->priv; 258 + int port = ksz_get_sgmii_port(dev); 259 + 260 + if (mmd == MDIO_MMD_VEND2) { 261 + struct ksz_port *p = &dev->ports[port]; 262 + 263 + if (reg == MMD_SR_MII_AUTO_NEG_CTRL) { 264 + u16 sgmii_mode = SR_MII_PCS_SGMII << SR_MII_PCS_MODE_S; 265 + 266 + /* Need these bits for 1000BASE-X mode to work with 267 + * AN on. 268 + */ 269 + if (!(val & sgmii_mode)) 270 + val |= SR_MII_SGMII_LINK_UP | 271 + SR_MII_TX_CFG_PHY_MASTER; 272 + 273 + /* SGMII interrupt in the port cannot be masked, so 274 + * make sure interrupt is not enabled as it is not 275 + * handled. 276 + */ 277 + val &= ~SR_MII_AUTO_NEG_COMPLETE_INTR; 278 + } else if (reg == MII_BMCR) { 279 + /* The MII_ADVERTISE register needs to write once 280 + * before doing auto-negotiation for the correct 281 + * config_word to be sent out after reset. 282 + */ 283 + if ((val & BMCR_ANENABLE) && !p->sgmii_adv_write) { 284 + u16 adv; 285 + 286 + /* The SGMII port cannot disable flow control 287 + * so it is better to just advertise symmetric 288 + * pause. 289 + */ 290 + port_sgmii_r(dev, port, mmd, MII_ADVERTISE, 291 + &adv); 292 + adv |= ADVERTISE_1000XPAUSE; 293 + adv &= ~ADVERTISE_1000XPSE_ASYM; 294 + port_sgmii_w(dev, port, mmd, MII_ADVERTISE, 295 + adv); 296 + p->sgmii_adv_write = 1; 297 + } else if (val & BMCR_RESET) { 298 + p->sgmii_adv_write = 0; 299 + } 300 + } else if (reg == MII_ADVERTISE) { 301 + /* XPCS driver writes to this register so there is no 302 + * need to update it for the errata. 303 + */ 304 + p->sgmii_adv_write = 1; 305 + } 306 + } 307 + port_sgmii_w(dev, port, mmd, reg, val); 308 + 309 + return 0; 310 + } 311 + 312 + int ksz9477_pcs_create(struct ksz_device *dev) 313 + { 314 + /* This chip has a SGMII port. */ 315 + if (ksz_has_sgmii_port(dev)) { 316 + int port = ksz_get_sgmii_port(dev); 317 + struct ksz_port *p = &dev->ports[port]; 318 + struct phylink_pcs *pcs; 319 + struct mii_bus *bus; 320 + int ret; 321 + 322 + bus = devm_mdiobus_alloc(dev->dev); 323 + if (!bus) 324 + return -ENOMEM; 325 + 326 + bus->name = "ksz_pcs_mdio_bus"; 327 + snprintf(bus->id, MII_BUS_ID_SIZE, "%s-pcs", 328 + dev_name(dev->dev)); 329 + bus->read_c45 = &ksz9477_pcs_read; 330 + bus->write_c45 = &ksz9477_pcs_write; 331 + bus->parent = dev->dev; 332 + bus->phy_mask = ~0; 333 + bus->priv = dev; 334 + 335 + ret = devm_mdiobus_register(dev->dev, bus); 336 + if (ret) 337 + return ret; 338 + 339 + pcs = xpcs_create_pcs_mdiodev(bus, 0); 340 + if (IS_ERR(pcs)) 341 + return PTR_ERR(pcs); 342 + p->pcs = pcs; 343 + } 344 + 345 + return 0; 162 346 } 163 347 164 348 int ksz9477_reset_switch(struct ksz_device *dev) ··· 1162 978 1163 979 if (dev->info->gbit_capable[port]) 1164 980 config->mac_capabilities |= MAC_1000FD; 981 + 982 + if (ksz_is_sgmii_port(dev, port)) { 983 + struct ksz_port *p = &dev->ports[port]; 984 + 985 + phy_interface_or(config->supported_interfaces, 986 + config->supported_interfaces, 987 + p->pcs->supported_interfaces); 988 + } 1165 989 } 1166 990 1167 991 int ksz9477_set_ageing_time(struct ksz_device *dev, unsigned int msecs)
+3 -1
drivers/net/dsa/microchip/ksz9477.h
··· 2 2 /* 3 3 * Microchip KSZ9477 series Header file 4 4 * 5 - * Copyright (C) 2017-2022 Microchip Technology Inc. 5 + * Copyright (C) 2017-2025 Microchip Technology Inc. 6 6 */ 7 7 8 8 #ifndef __KSZ9477_H ··· 96 96 void ksz9477_acl_match_process_l2(struct ksz_device *dev, int port, 97 97 u16 ethtype, u8 *src_mac, u8 *dst_mac, 98 98 unsigned long cookie, u32 prio); 99 + 100 + int ksz9477_pcs_create(struct ksz_device *dev); 99 101 100 102 #endif
+33 -4
drivers/net/dsa/microchip/ksz_common.c
··· 2 2 /* 3 3 * Microchip switch driver main logic 4 4 * 5 - * Copyright (C) 2017-2024 Microchip Technology Inc. 5 + * Copyright (C) 2017-2025 Microchip Technology Inc. 6 6 */ 7 7 8 8 #include <linux/delay.h> ··· 408 408 int speed, int duplex, bool tx_pause, 409 409 bool rx_pause); 410 410 411 + static struct phylink_pcs * 412 + ksz_phylink_mac_select_pcs(struct phylink_config *config, 413 + phy_interface_t interface) 414 + { 415 + struct dsa_port *dp = dsa_phylink_to_port(config); 416 + struct ksz_device *dev = dp->ds->priv; 417 + struct ksz_port *p = &dev->ports[dp->index]; 418 + 419 + if (ksz_is_sgmii_port(dev, dp->index) && 420 + (interface == PHY_INTERFACE_MODE_SGMII || 421 + interface == PHY_INTERFACE_MODE_1000BASEX)) 422 + return p->pcs; 423 + 424 + return NULL; 425 + } 426 + 411 427 static const struct phylink_mac_ops ksz9477_phylink_mac_ops = { 412 428 .mac_config = ksz_phylink_mac_config, 413 429 .mac_link_down = ksz_phylink_mac_link_down, 414 430 .mac_link_up = ksz9477_phylink_mac_link_up, 415 431 .mac_disable_tx_lpi = ksz_phylink_mac_disable_tx_lpi, 416 432 .mac_enable_tx_lpi = ksz_phylink_mac_enable_tx_lpi, 433 + .mac_select_pcs = ksz_phylink_mac_select_pcs, 417 434 }; 418 435 419 436 static const struct ksz_dev_ops ksz9477_dev_ops = { ··· 468 451 .reset = ksz9477_reset_switch, 469 452 .init = ksz9477_switch_init, 470 453 .exit = ksz9477_switch_exit, 454 + .pcs_create = ksz9477_pcs_create, 471 455 }; 472 456 473 457 static const struct phylink_mac_ops lan937x_phylink_mac_ops = { ··· 1111 1093 regmap_reg_range(0x701b, 0x701b), 1112 1094 regmap_reg_range(0x701f, 0x7020), 1113 1095 regmap_reg_range(0x7030, 0x7030), 1114 - regmap_reg_range(0x7200, 0x7203), 1115 - regmap_reg_range(0x7206, 0x7207), 1096 + regmap_reg_range(0x7200, 0x7207), 1116 1097 regmap_reg_range(0x7300, 0x7301), 1117 1098 regmap_reg_range(0x7400, 0x7401), 1118 1099 regmap_reg_range(0x7403, 0x7403), ··· 1627 1610 true, false, false}, 1628 1611 .gbit_capable = {true, true, true, true, true, true, true}, 1629 1612 .ptp_capable = true, 1613 + .sgmii_port = 7, 1630 1614 .wr_table = &ksz9477_register_set, 1631 1615 .rd_table = &ksz9477_register_set, 1632 1616 }, ··· 2020 2002 .internal_phy = {true, true, true, true, 2021 2003 true, false, false}, 2022 2004 .gbit_capable = {true, true, true, true, true, true, true}, 2005 + .sgmii_port = 7, 2023 2006 .wr_table = &ksz9477_register_set, 2024 2007 .rd_table = &ksz9477_register_set, 2025 2008 }, ··· 2156 2137 2157 2138 spin_unlock(&mib->stats64_lock); 2158 2139 2159 - if (dev->info->phy_errata_9477) { 2140 + if (dev->info->phy_errata_9477 && !ksz_is_sgmii_port(dev, port)) { 2160 2141 ret = ksz9477_errata_monitor(dev, port, raw->tx_late_col); 2161 2142 if (ret) 2162 2143 dev_err(dev->dev, "Failed to monitor transmission halt\n"); ··· 2863 2844 ret = ksz_parse_drive_strength(dev); 2864 2845 if (ret) 2865 2846 return ret; 2847 + 2848 + if (ksz_has_sgmii_port(dev) && dev->dev_ops->pcs_create) { 2849 + ret = dev->dev_ops->pcs_create(dev); 2850 + if (ret) 2851 + return ret; 2852 + } 2866 2853 2867 2854 /* set broadcast storm protection 10% rate */ 2868 2855 regmap_update_bits(ksz_regmap_16(dev), regs[S_BROADCAST_CTRL], ··· 3715 3690 3716 3691 /* Internal PHYs */ 3717 3692 if (dev->info->internal_phy[port]) 3693 + return; 3694 + 3695 + /* No need to configure XMII control register when using SGMII. */ 3696 + if (ksz_is_sgmii_port(dev, port)) 3718 3697 return; 3719 3698 3720 3699 if (phylink_autoneg_inband(mode)) {
+22 -1
drivers/net/dsa/microchip/ksz_common.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0 */ 2 2 /* Microchip switch driver common header 3 3 * 4 - * Copyright (C) 2017-2024 Microchip Technology Inc. 4 + * Copyright (C) 2017-2025 Microchip Technology Inc. 5 5 */ 6 6 7 7 #ifndef __KSZ_COMMON_H ··· 10 10 #include <linux/etherdevice.h> 11 11 #include <linux/kernel.h> 12 12 #include <linux/mutex.h> 13 + #include <linux/pcs/pcs-xpcs.h> 13 14 #include <linux/phy.h> 14 15 #include <linux/regmap.h> 15 16 #include <net/dsa.h> ··· 94 93 bool internal_phy[KSZ_MAX_NUM_PORTS]; 95 94 bool gbit_capable[KSZ_MAX_NUM_PORTS]; 96 95 bool ptp_capable; 96 + u8 sgmii_port; 97 97 const struct regmap_access_table *wr_table; 98 98 const struct regmap_access_table *rd_table; 99 99 }; ··· 134 132 u32 force:1; 135 133 u32 read:1; /* read MIB counters in background */ 136 134 u32 freeze:1; /* MIB counter freeze is enabled */ 135 + u32 sgmii_adv_write:1; 137 136 138 137 struct ksz_port_mib mib; 139 138 phy_interface_t interface; ··· 144 141 void *acl_priv; 145 142 struct ksz_irq pirq; 146 143 u8 num; 144 + struct phylink_pcs *pcs; 147 145 #if IS_ENABLED(CONFIG_NET_DSA_MICROCHIP_KSZ_PTP) 148 146 struct kernel_hwtstamp_config tstamp_config; 149 147 bool hwts_tx_en; ··· 444 440 int (*reset)(struct ksz_device *dev); 445 441 int (*init)(struct ksz_device *dev); 446 442 void (*exit)(struct ksz_device *dev); 443 + 444 + int (*pcs_create)(struct ksz_device *dev); 447 445 }; 448 446 449 447 struct ksz_device *ksz_switch_alloc(struct device *base, void *priv); ··· 735 729 { 736 730 return (dev->chip_id == LAN9371_CHIP_ID || 737 731 dev->chip_id == LAN9372_CHIP_ID) && port == KSZ_PORT_4; 732 + } 733 + 734 + static inline int ksz_get_sgmii_port(struct ksz_device *dev) 735 + { 736 + return dev->info->sgmii_port - 1; 737 + } 738 + 739 + static inline bool ksz_has_sgmii_port(struct ksz_device *dev) 740 + { 741 + return dev->info->sgmii_port > 0; 742 + } 743 + 744 + static inline bool ksz_is_sgmii_port(struct ksz_device *dev, int port) 745 + { 746 + return dev->info->sgmii_port == port + 1; 738 747 } 739 748 740 749 /* STP State Defines */