diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c index 4ec2d5d2a1bc..138f2ab0774e 100644 --- a/drivers/net/dsa/microchip/ksz8.c +++ b/drivers/net/dsa/microchip/ksz8.c @@ -1562,7 +1562,7 @@ static int ksz8_port_vlan_filtering(struct dsa_switch *ds, int port, bool flag, { struct ksz_device *dev = ds->priv; - if (ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev)) + if (ksz_is_ksz88x3(dev)) return -ENOTSUPP; /* Discard packets with VID not enabled on the switch */ @@ -1599,7 +1599,7 @@ static int ksz8_port_vlan_add(struct dsa_switch *ds, int port, u16 data, new_pvid = 0; u8 fid, member, valid; - if (ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev)) + if (ksz_is_ksz88x3(dev)) return -ENOTSUPP; /* If a VLAN is added with untagged flag different from the @@ -1669,7 +1669,7 @@ static int ksz8_port_vlan_del(struct dsa_switch *ds, int port, u8 fid, member, valid; u16 data, pvid; - if (ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev)) + if (ksz_is_ksz88x3(dev)) return -ENOTSUPP; ksz_pread16(dev, port, REG_PORT_CTRL_VID, &pvid); @@ -2361,6 +2361,24 @@ static int ksz8463_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val return 0; } +static u32 ksz88xx_get_phy_flags(struct dsa_switch *ds, int port) +{ + struct ksz_device *dev = ds->priv; + + switch (dev->chip_id) { + case KSZ88X3_CHIP_ID: + /* Silicon Errata Sheet (DS80000830A): + * Port 1 does not work with LinkMD Cable-Testing. + * Port 1 does not respond to received PAUSE control frames. + */ + if (!port) + return MICREL_KSZ8_P1_ERRATA; + break; + } + + return 0; +} + static int ksz8_switch_init(struct ksz_device *dev) { dev->cpu_port = fls(dev->info->cpu_ports) - 1; @@ -2508,7 +2526,6 @@ const struct ksz_dev_ops ksz88xx_dev_ops = { const struct dsa_switch_ops ksz8463_switch_ops = { .get_tag_protocol = ksz8463_get_tag_protocol, .connect_tag_protocol = ksz8463_connect_tag_protocol, - .get_phy_flags = ksz_get_phy_flags, .setup = ksz8_setup, .teardown = ksz_teardown, .phy_read = ksz8463_phy_read16, @@ -2520,17 +2537,11 @@ const struct dsa_switch_ops ksz8463_switch_ops = { .get_sset_count = ksz_sset_count, .port_bridge_join = ksz_port_bridge_join, .port_bridge_leave = ksz_port_bridge_leave, - .port_hsr_join = ksz_hsr_join, - .port_hsr_leave = ksz_hsr_leave, .port_set_mac_address = ksz_port_set_mac_address, .port_stp_state_set = ksz_port_stp_state_set, - .port_teardown = ksz_port_teardown, .port_pre_bridge_flags = ksz_port_pre_bridge_flags, .port_bridge_flags = ksz_port_bridge_flags, .port_fast_age = ksz8_flush_dyn_mac_table, - .port_vlan_filtering = ksz8_port_vlan_filtering, - .port_vlan_add = ksz8_port_vlan_add, - .port_vlan_del = ksz8_port_vlan_del, .port_fdb_dump = ksz8_fdb_dump, .port_fdb_add = ksz8_fdb_add, .port_fdb_del = ksz8_fdb_del, @@ -2542,8 +2553,6 @@ const struct dsa_switch_ops ksz8463_switch_ops = { .get_pause_stats = ksz_get_pause_stats, .port_change_mtu = ksz8_change_mtu, .port_max_mtu = ksz_max_mtu, - .get_wol = ksz_get_wol, - .set_wol = ksz_set_wol, .suspend = ksz_suspend, .resume = ksz_resume, .get_ts_info = ksz_get_ts_info, @@ -2551,11 +2560,7 @@ const struct dsa_switch_ops ksz8463_switch_ops = { .port_hwtstamp_set = ksz_hwtstamp_set, .port_txtstamp = ksz_port_txtstamp, .port_rxtstamp = ksz_port_rxtstamp, - .cls_flower_add = ksz_cls_flower_add, - .cls_flower_del = ksz_cls_flower_del, .port_setup_tc = ksz_setup_tc, - .support_eee = ksz_support_eee, - .set_mac_eee = ksz_set_mac_eee, .port_get_default_prio = ksz_port_get_default_prio, .port_set_default_prio = ksz_port_set_default_prio, .port_get_dscp_prio = ksz_port_get_dscp_prio, @@ -2568,7 +2573,6 @@ const struct dsa_switch_ops ksz8463_switch_ops = { const struct dsa_switch_ops ksz87xx_switch_ops = { .get_tag_protocol = ksz87xx_get_tag_protocol, .connect_tag_protocol = ksz87xx_connect_tag_protocol, - .get_phy_flags = ksz_get_phy_flags, .setup = ksz8_setup, .teardown = ksz_teardown, .phy_read = ksz8_phy_read16, @@ -2580,11 +2584,8 @@ const struct dsa_switch_ops ksz87xx_switch_ops = { .get_sset_count = ksz_sset_count, .port_bridge_join = ksz_port_bridge_join, .port_bridge_leave = ksz_port_bridge_leave, - .port_hsr_join = ksz_hsr_join, - .port_hsr_leave = ksz_hsr_leave, .port_set_mac_address = ksz_port_set_mac_address, .port_stp_state_set = ksz_port_stp_state_set, - .port_teardown = ksz_port_teardown, .port_pre_bridge_flags = ksz_port_pre_bridge_flags, .port_bridge_flags = ksz_port_bridge_flags, .port_fast_age = ksz8_flush_dyn_mac_table, @@ -2602,8 +2603,6 @@ const struct dsa_switch_ops ksz87xx_switch_ops = { .get_pause_stats = ksz_get_pause_stats, .port_change_mtu = ksz8_change_mtu, .port_max_mtu = ksz_max_mtu, - .get_wol = ksz_get_wol, - .set_wol = ksz_set_wol, .suspend = ksz_suspend, .resume = ksz_resume, .get_ts_info = ksz_get_ts_info, @@ -2611,11 +2610,7 @@ const struct dsa_switch_ops ksz87xx_switch_ops = { .port_hwtstamp_set = ksz_hwtstamp_set, .port_txtstamp = ksz_port_txtstamp, .port_rxtstamp = ksz_port_rxtstamp, - .cls_flower_add = ksz_cls_flower_add, - .cls_flower_del = ksz_cls_flower_del, .port_setup_tc = ksz_setup_tc, - .support_eee = ksz_support_eee, - .set_mac_eee = ksz_set_mac_eee, .port_get_default_prio = ksz_port_get_default_prio, .port_set_default_prio = ksz_port_set_default_prio, .port_get_dscp_prio = ksz_port_get_dscp_prio, @@ -2628,7 +2623,7 @@ const struct dsa_switch_ops ksz87xx_switch_ops = { const struct dsa_switch_ops ksz88xx_switch_ops = { .get_tag_protocol = ksz88xx_get_tag_protocol, .connect_tag_protocol = ksz88xx_connect_tag_protocol, - .get_phy_flags = ksz_get_phy_flags, + .get_phy_flags = ksz88xx_get_phy_flags, .setup = ksz8_setup, .teardown = ksz_teardown, .phy_read = ksz8_phy_read16, @@ -2640,11 +2635,8 @@ const struct dsa_switch_ops ksz88xx_switch_ops = { .get_sset_count = ksz_sset_count, .port_bridge_join = ksz_port_bridge_join, .port_bridge_leave = ksz_port_bridge_leave, - .port_hsr_join = ksz_hsr_join, - .port_hsr_leave = ksz_hsr_leave, .port_set_mac_address = ksz_port_set_mac_address, .port_stp_state_set = ksz_port_stp_state_set, - .port_teardown = ksz_port_teardown, .port_pre_bridge_flags = ksz_port_pre_bridge_flags, .port_bridge_flags = ksz_port_bridge_flags, .port_fast_age = ksz8_flush_dyn_mac_table, @@ -2671,11 +2663,7 @@ const struct dsa_switch_ops ksz88xx_switch_ops = { .port_hwtstamp_set = ksz_hwtstamp_set, .port_txtstamp = ksz_port_txtstamp, .port_rxtstamp = ksz_port_rxtstamp, - .cls_flower_add = ksz_cls_flower_add, - .cls_flower_del = ksz_cls_flower_del, .port_setup_tc = ksz_setup_tc, - .support_eee = ksz_support_eee, - .set_mac_eee = ksz_set_mac_eee, .port_get_default_prio = ksz_port_get_default_prio, .port_set_default_prio = ksz_port_set_default_prio, .port_get_dscp_prio = ksz_port_get_dscp_prio, diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c index c1395322e54b..f3f0c98dfb5a 100644 --- a/drivers/net/dsa/microchip/ksz9477.c +++ b/drivers/net/dsa/microchip/ksz9477.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -1707,12 +1708,52 @@ static int ksz9477_tc_cbs_set_cinc(struct ksz_device *dev, int port, u32 val) */ #define KSZ9477_SUPPORTED_HSR_FEATURES (NETIF_F_HW_HSR_DUP | NETIF_F_HW_HSR_FWD) -void ksz9477_hsr_join(struct dsa_switch *ds, int port, struct net_device *hsr) +static int ksz9477_hsr_join(struct dsa_switch *ds, int port, + struct net_device *hsr, + struct netlink_ext_ack *extack) { struct ksz_device *dev = ds->priv; struct net_device *user; struct dsa_port *hsr_dp; u8 data, hsr_ports = 0; + enum hsr_version ver; + int ret; + + ret = hsr_get_version(hsr, &ver); + if (ret) + return ret; + + if (dev->chip_id != KSZ9477_CHIP_ID) { + NL_SET_ERR_MSG_MOD(extack, "Chip does not support HSR offload"); + return -EOPNOTSUPP; + } + + /* KSZ9477 can support HW offloading of only 1 HSR device */ + if (dev->hsr_dev && hsr != dev->hsr_dev) { + NL_SET_ERR_MSG_MOD(extack, + "Offload supported for a single HSR"); + return -EOPNOTSUPP; + } + + /* KSZ9477 only supports HSR v0 and v1 */ + if (!(ver == HSR_V0 || ver == HSR_V1)) { + NL_SET_ERR_MSG_MOD(extack, "Only HSR v0 and v1 supported"); + return -EOPNOTSUPP; + } + + /* KSZ9477 can only perform HSR offloading for up to two ports */ + if (hweight8(dev->hsr_ports) >= 2) { + NL_SET_ERR_MSG_MOD(extack, + "Cannot offload more than two ports - using software HSR"); + return -EOPNOTSUPP; + } + + /* Self MAC address filtering, to avoid frames traversing + * the HSR ring more than once. + */ + ret = ksz_switch_macaddr_get(ds, port, extack); + if (ret) + return ret; /* Program which port(s) shall support HSR */ ksz_rmw32(dev, REG_HSR_PORT_MAP__4, BIT(port), BIT(port)); @@ -1744,12 +1785,20 @@ void ksz9477_hsr_join(struct dsa_switch *ds, int port, struct net_device *hsr) /* Setup HW supported features for lan HSR ports */ user = dsa_to_port(ds, port)->user; user->features |= KSZ9477_SUPPORTED_HSR_FEATURES; + + dev->hsr_dev = hsr; + dev->hsr_ports |= BIT(port); + + return 0; } -void ksz9477_hsr_leave(struct dsa_switch *ds, int port, struct net_device *hsr) +static int ksz9477_hsr_leave(struct dsa_switch *ds, int port, + struct net_device *hsr) { struct ksz_device *dev = ds->priv; + WARN_ON(dev->chip_id != KSZ9477_CHIP_ID); + /* Clear port HSR support */ ksz_rmw32(dev, REG_HSR_PORT_MAP__4, BIT(port), 0); @@ -1758,6 +1807,14 @@ void ksz9477_hsr_leave(struct dsa_switch *ds, int port, struct net_device *hsr) /* Disable per port self-address filtering */ ksz_port_cfg(dev, port, REG_PORT_LUE_CTRL, PORT_SRC_ADDR_FILTER, false); + + dev->hsr_ports &= ~BIT(port); + if (!dev->hsr_ports) + dev->hsr_dev = NULL; + + ksz_switch_macaddr_put(ds); + + return 0; } static int ksz9477_switch_init(struct ksz_device *dev) @@ -1884,6 +1941,14 @@ static void ksz9477_duplex_flowctrl(struct ksz_device *dev, int port, int duplex ksz_prmw8(dev, port, regs[P_XMII_CTRL_0], mask, val); } +static void ksz9477_port_teardown(struct dsa_switch *ds, int port) +{ + struct ksz_device *dev = ds->priv; + + if (dsa_is_user_port(ds, port)) + ksz9477_port_acl_free(dev, port); +} + void ksz9477_phylink_mac_link_up(struct phylink_config *config, struct phy_device *phydev, unsigned int mode, @@ -1909,6 +1974,54 @@ void ksz9477_phylink_mac_link_up(struct phylink_config *config, ksz9477_duplex_flowctrl(dev, port, duplex, tx_pause, rx_pause); } +/** + * ksz9477_support_eee - Determine Energy Efficient Ethernet (EEE) support for a + * port + * @ds: Pointer to the DSA switch structure + * @port: Port number to check + * + * This function also documents devices where EEE was initially advertised but + * later withdrawn due to reliability issues, as described in official errata + * documents. These devices are explicitly listed to record known limitations, + * even if there is no technical necessity for runtime checks. + * + * Returns: true if the internal PHY on the given port supports fully + * operational EEE, false otherwise. + */ +static bool ksz9477_support_eee(struct dsa_switch *ds, int port) +{ + struct ksz_device *dev = ds->priv; + + if (!dev->info->internal_phy[port]) + return false; + + switch (dev->chip_id) { + case KSZ8563_CHIP_ID: + case KSZ9563_CHIP_ID: + case KSZ9893_CHIP_ID: + return true; + default: + /* KSZ8567R Errata DS80000752C Module 4 */ + /* KSZ9477S Errata DS80000754A Module 4 */ + /* KSZ9567S Errata DS80000756A Module 4 */ + /* KSZ9896C Errata DS80000757A Module 3 */ + /* KSZ9897R Errata DS80000758C Module 4 */ + /* Energy Efficient Ethernet (EEE) feature select must be + * manually disabled + * The EEE feature is enabled by default, but it is not fully + * operational. It must be manually disabled through register + * controls. If not disabled, the PHY ports can auto-negotiate + * to enable EEE, and this feature can cause link drops when + * linked to another device supporting EEE. + * + * The same item appears in the errata for all switches above. + */ + break; + } + + return false; +} + static struct phylink_pcs * ksz9477_phylink_mac_select_pcs(struct phylink_config *config, phy_interface_t interface) @@ -1952,7 +2065,6 @@ const struct ksz_dev_ops ksz9477_dev_ops = { const struct dsa_switch_ops ksz9477_switch_ops = { .get_tag_protocol = ksz9477_get_tag_protocol, .connect_tag_protocol = ksz9477_connect_tag_protocol, - .get_phy_flags = ksz_get_phy_flags, .setup = ksz9477_setup, .teardown = ksz_teardown, .phy_read = ksz9477_phy_read16, @@ -1965,11 +2077,11 @@ const struct dsa_switch_ops ksz9477_switch_ops = { .get_sset_count = ksz_sset_count, .port_bridge_join = ksz_port_bridge_join, .port_bridge_leave = ksz_port_bridge_leave, - .port_hsr_join = ksz_hsr_join, - .port_hsr_leave = ksz_hsr_leave, + .port_hsr_join = ksz9477_hsr_join, + .port_hsr_leave = ksz9477_hsr_leave, .port_set_mac_address = ksz_port_set_mac_address, .port_stp_state_set = ksz_port_stp_state_set, - .port_teardown = ksz_port_teardown, + .port_teardown = ksz9477_port_teardown, .port_pre_bridge_flags = ksz_port_pre_bridge_flags, .port_bridge_flags = ksz_port_bridge_flags, .port_fast_age = ksz9477_flush_dyn_mac_table, @@ -1996,10 +2108,10 @@ const struct dsa_switch_ops ksz9477_switch_ops = { .port_hwtstamp_set = ksz_hwtstamp_set, .port_txtstamp = ksz_port_txtstamp, .port_rxtstamp = ksz_port_rxtstamp, - .cls_flower_add = ksz_cls_flower_add, - .cls_flower_del = ksz_cls_flower_del, + .cls_flower_add = ksz9477_cls_flower_add, + .cls_flower_del = ksz9477_cls_flower_del, .port_setup_tc = ksz_setup_tc, - .support_eee = ksz_support_eee, + .support_eee = ksz9477_support_eee, .set_mac_eee = ksz_set_mac_eee, .port_get_default_prio = ksz_port_get_default_prio, .port_set_default_prio = ksz_port_set_default_prio, diff --git a/drivers/net/dsa/microchip/ksz9477.h b/drivers/net/dsa/microchip/ksz9477.h index 599db0a6ba2e..92a1d889224d 100644 --- a/drivers/net/dsa/microchip/ksz9477.h +++ b/drivers/net/dsa/microchip/ksz9477.h @@ -45,8 +45,6 @@ int ksz9477_mdb_del(struct dsa_switch *ds, int port, const struct switchdev_obj_port_mdb *mdb, struct dsa_db db); int ksz9477_enable_stp_addr(struct ksz_device *dev); void ksz9477_port_queue_split(struct ksz_device *dev, int port); -void ksz9477_hsr_join(struct dsa_switch *ds, int port, struct net_device *hsr); -void ksz9477_hsr_leave(struct dsa_switch *ds, int port, struct net_device *hsr); int ksz9477_port_acl_init(struct ksz_device *dev, int port); void ksz9477_port_acl_free(struct ksz_device *dev, int port); diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 57c8abfe0147..d1726778bb48 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -2257,7 +2256,7 @@ static void ksz_update_port_member(struct ksz_device *dev, int port) dev->dev_ops->cfg_port_member(dev, port, port_member | cpu_port); } -static int ksz_sw_mdio_read(struct mii_bus *bus, int addr, int regnum) +int ksz_sw_mdio_read(struct mii_bus *bus, int addr, int regnum) { struct ksz_device *dev = bus->priv; struct dsa_switch *ds = dev->ds; @@ -2265,8 +2264,7 @@ static int ksz_sw_mdio_read(struct mii_bus *bus, int addr, int regnum) return ds->ops->phy_read(ds, addr, regnum); } -static int ksz_sw_mdio_write(struct mii_bus *bus, int addr, int regnum, - u16 val) +int ksz_sw_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val) { struct ksz_device *dev = bus->priv; struct dsa_switch *ds = dev->ds; @@ -2287,7 +2285,7 @@ static int ksz_sw_mdio_write(struct mii_bus *bus, int addr, int regnum, * * Return: Value of the PHY register, or a negative error code on failure. */ -static int ksz_parent_mdio_read(struct mii_bus *bus, int addr, int regnum) +int ksz_parent_mdio_read(struct mii_bus *bus, int addr, int regnum) { struct ksz_device *dev = bus->priv; @@ -2307,8 +2305,7 @@ static int ksz_parent_mdio_read(struct mii_bus *bus, int addr, int regnum) * * Return: 0 on success, or a negative error code on failure. */ -static int ksz_parent_mdio_write(struct mii_bus *bus, int addr, int regnum, - u16 val) +int ksz_parent_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val) { struct ksz_device *dev = bus->priv; @@ -2416,8 +2413,8 @@ static void ksz_irq_phy_free(struct ksz_device *dev) * * Return: 0 on success, or a negative error code on failure. */ -static int ksz_parse_dt_phy_config(struct ksz_device *dev, struct mii_bus *bus, - struct device_node *mdio_np) +int ksz_parse_dt_phy_config(struct ksz_device *dev, struct mii_bus *bus, + struct device_node *mdio_np) { struct device_node *phy_node, *phy_parent_node; bool phys_are_valid = true; @@ -2520,20 +2517,8 @@ int ksz_mdio_register(struct ksz_device *dev) goto put_mdio_node; } - if (dev->dev_ops->mdio_bus_preinit) { - ret = dev->dev_ops->mdio_bus_preinit(dev, !!parent_bus); - if (ret) - goto put_mdio_node; - } - - if (dev->dev_ops->create_phy_addr_map) { - ret = dev->dev_ops->create_phy_addr_map(dev, !!parent_bus); - if (ret) - goto put_mdio_node; - } else { - for (i = 0; i < dev->info->port_cnt; i++) - dev->phy_addr_map[i] = i; - } + for (i = 0; i < dev->info->port_cnt; i++) + dev->phy_addr_map[i] = i; bus->priv = dev; if (parent_bus) { @@ -2838,24 +2823,6 @@ void ksz_init_mib_timer(struct ksz_device *dev) } } -u32 ksz_get_phy_flags(struct dsa_switch *ds, int port) -{ - struct ksz_device *dev = ds->priv; - - switch (dev->chip_id) { - case KSZ88X3_CHIP_ID: - /* Silicon Errata Sheet (DS80000830A): - * Port 1 does not work with LinkMD Cable-Testing. - * Port 1 does not respond to received PAUSE control frames. - */ - if (!port) - return MICREL_KSZ8_P1_ERRATA; - break; - } - - return 0; -} - void ksz_phylink_mac_link_down(struct phylink_config *config, unsigned int mode, phy_interface_t interface) @@ -2986,25 +2953,6 @@ void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) ksz_update_port_member(dev, port); } -void ksz_port_teardown(struct dsa_switch *ds, int port) -{ - struct ksz_device *dev = ds->priv; - - switch (dev->chip_id) { - case KSZ8563_CHIP_ID: - case KSZ8567_CHIP_ID: - case KSZ9477_CHIP_ID: - case KSZ9563_CHIP_ID: - case KSZ9567_CHIP_ID: - case KSZ9893_CHIP_ID: - case KSZ9896_CHIP_ID: - case KSZ9897_CHIP_ID: - case LAN9646_CHIP_ID: - if (dsa_is_user_port(ds, port)) - ksz9477_port_acl_free(dev, port); - } -} - int ksz_port_pre_bridge_flags(struct dsa_switch *ds, int port, struct switchdev_brport_flags flags, struct netlink_ext_ack *extack) @@ -3070,63 +3018,6 @@ int ksz_max_mtu(struct dsa_switch *ds, int port) return -EOPNOTSUPP; } -/** - * ksz_support_eee - Determine Energy Efficient Ethernet (EEE) support for a - * port - * @ds: Pointer to the DSA switch structure - * @port: Port number to check - * - * This function also documents devices where EEE was initially advertised but - * later withdrawn due to reliability issues, as described in official errata - * documents. These devices are explicitly listed to record known limitations, - * even if there is no technical necessity for runtime checks. - * - * Returns: true if the internal PHY on the given port supports fully - * operational EEE, false otherwise. - */ -bool ksz_support_eee(struct dsa_switch *ds, int port) -{ - struct ksz_device *dev = ds->priv; - - if (!dev->info->internal_phy[port]) - return false; - - switch (dev->chip_id) { - case KSZ8563_CHIP_ID: - case KSZ9563_CHIP_ID: - case KSZ9893_CHIP_ID: - return true; - case KSZ8567_CHIP_ID: - /* KSZ8567R Errata DS80000752C Module 4 */ - case KSZ8765_CHIP_ID: - case KSZ8794_CHIP_ID: - case KSZ8795_CHIP_ID: - /* KSZ879x/KSZ877x/KSZ876x Errata DS80000687C Module 2 */ - case KSZ9477_CHIP_ID: - /* KSZ9477S Errata DS80000754A Module 4 */ - case KSZ9567_CHIP_ID: - /* KSZ9567S Errata DS80000756A Module 4 */ - case KSZ9896_CHIP_ID: - /* KSZ9896C Errata DS80000757A Module 3 */ - case KSZ9897_CHIP_ID: - case LAN9646_CHIP_ID: - /* KSZ9897R Errata DS80000758C Module 4 */ - /* Energy Efficient Ethernet (EEE) feature select must be - * manually disabled - * The EEE feature is enabled by default, but it is not fully - * operational. It must be manually disabled through register - * controls. If not disabled, the PHY ports can auto-negotiate - * to enable EEE, and this feature can cause link drops when - * linked to another device supporting EEE. - * - * The same item appears in the errata for all switches above. - */ - break; - } - - return false; -} - int ksz_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_keee *e) { @@ -3145,8 +3036,7 @@ int ksz_set_mac_eee(struct dsa_switch *ds, int port, return 0; } -static void ksz_set_xmii(struct ksz_device *dev, int port, - phy_interface_t interface) +void ksz_set_xmii(struct ksz_device *dev, int port, phy_interface_t interface) { const u8 *bitval = dev->info->xmii_ctrl1; struct ksz_port *p = &dev->ports[port]; @@ -3229,6 +3119,29 @@ phy_interface_t ksz_get_xmii(struct ksz_device *dev, int port, bool gbit) return interface; } +bool ksz_phylink_need_config(struct phylink_config *config, + unsigned int mode) +{ + struct dsa_port *dp = dsa_phylink_to_port(config); + struct ksz_device *dev = dp->ds->priv; + int port = dp->index; + + /* Internal PHYs */ + if (dev->info->internal_phy[port]) + return false; + + /* No need to configure XMII control register when using SGMII. */ + if (ksz_is_sgmii_port(dev, port)) + return false; + + if (phylink_autoneg_inband(mode)) { + dev_err(dev->dev, "In-band AN not supported!\n"); + return false; + } + + return true; +} + void ksz_phylink_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state) @@ -3237,23 +3150,8 @@ void ksz_phylink_mac_config(struct phylink_config *config, struct ksz_device *dev = dp->ds->priv; int port = dp->index; - /* Internal PHYs */ - if (dev->info->internal_phy[port]) - return; - - /* No need to configure XMII control register when using SGMII. */ - if (ksz_is_sgmii_port(dev, port)) - return; - - if (phylink_autoneg_inband(mode)) { - dev_err(dev->dev, "In-band AN not supported!\n"); - return; - } - - ksz_set_xmii(dev, port, state->interface); - - if (dev->dev_ops->setup_rgmii_delay) - dev->dev_ops->setup_rgmii_delay(dev, port); + if (ksz_phylink_need_config(config, mode)) + ksz_set_xmii(dev, port, state->interface); } bool ksz_get_gbit(struct ksz_device *dev, int port) @@ -3373,48 +3271,6 @@ static int ksz_switch_detect(struct ksz_device *dev) return 0; } -int ksz_cls_flower_add(struct dsa_switch *ds, int port, - struct flow_cls_offload *cls, bool ingress) -{ - struct ksz_device *dev = ds->priv; - - switch (dev->chip_id) { - case KSZ8563_CHIP_ID: - case KSZ8567_CHIP_ID: - case KSZ9477_CHIP_ID: - case KSZ9563_CHIP_ID: - case KSZ9567_CHIP_ID: - case KSZ9893_CHIP_ID: - case KSZ9896_CHIP_ID: - case KSZ9897_CHIP_ID: - case LAN9646_CHIP_ID: - return ksz9477_cls_flower_add(ds, port, cls, ingress); - } - - return -EOPNOTSUPP; -} - -int ksz_cls_flower_del(struct dsa_switch *ds, int port, - struct flow_cls_offload *cls, bool ingress) -{ - struct ksz_device *dev = ds->priv; - - switch (dev->chip_id) { - case KSZ8563_CHIP_ID: - case KSZ8567_CHIP_ID: - case KSZ9477_CHIP_ID: - case KSZ9563_CHIP_ID: - case KSZ9567_CHIP_ID: - case KSZ9893_CHIP_ID: - case KSZ9896_CHIP_ID: - case KSZ9897_CHIP_ID: - case LAN9646_CHIP_ID: - return ksz9477_cls_flower_del(ds, port, cls, ingress); - } - - return -EOPNOTSUPP; -} - /* Bandwidth is calculated by idle slope/transmission speed. Then the Bandwidth * is converted to Hex-decimal using the successive multiplication method. On * every step, integer part is taken and decimal part is carry forwarded. @@ -3866,9 +3722,6 @@ void ksz_get_wol(struct dsa_switch *ds, int port, u8 pme_ctrl; int ret; - if (!is_ksz9477(dev) && !ksz_is_ksz87xx(dev)) - return; - if (!dev->wakeup_source) return; @@ -3919,9 +3772,6 @@ int ksz_set_wol(struct dsa_switch *ds, int port, if (wol->wolopts & ~(WAKE_PHY | WAKE_MAGIC)) return -EINVAL; - if (!is_ksz9477(dev) && !ksz_is_ksz87xx(dev)) - return -EOPNOTSUPP; - if (!dev->wakeup_source) return -EOPNOTSUPP; @@ -3981,12 +3831,13 @@ static void ksz_wol_pre_shutdown(struct ksz_device *dev) { const struct ksz_dev_ops *ops = dev->dev_ops; const u16 *regs = dev->info->regs; + struct dsa_switch *ds = dev->ds; u8 pme_pin_en = PME_ENABLE; bool wol_enabled = false; struct dsa_port *dp; int ret; - if (!is_ksz9477(dev) && !ksz_is_ksz87xx(dev)) + if (!ds->ops->set_wol) return; if (!dev->wakeup_source) @@ -4034,7 +3885,8 @@ int ksz_port_set_mac_address(struct dsa_switch *ds, int port, */ wol.wolopts = 0; - ksz_get_wol(ds, dp->index, &wol); + if (ds->ops->get_wol) + ds->ops->get_wol(ds, dp->index, &wol); if (wol.wolopts & WAKE_MAGIC) { dev_err(ds->dev, "Cannot change MAC address on port %d with active Wake on Magic Packet\n", @@ -4169,72 +4021,6 @@ void ksz_switch_macaddr_put(struct dsa_switch *ds) kfree(switch_macaddr); } -int ksz_hsr_join(struct dsa_switch *ds, int port, struct net_device *hsr, - struct netlink_ext_ack *extack) -{ - struct ksz_device *dev = ds->priv; - enum hsr_version ver; - int ret; - - ret = hsr_get_version(hsr, &ver); - if (ret) - return ret; - - if (dev->chip_id != KSZ9477_CHIP_ID) { - NL_SET_ERR_MSG_MOD(extack, "Chip does not support HSR offload"); - return -EOPNOTSUPP; - } - - /* KSZ9477 can support HW offloading of only 1 HSR device */ - if (dev->hsr_dev && hsr != dev->hsr_dev) { - NL_SET_ERR_MSG_MOD(extack, "Offload supported for a single HSR"); - return -EOPNOTSUPP; - } - - /* KSZ9477 only supports HSR v0 and v1 */ - if (!(ver == HSR_V0 || ver == HSR_V1)) { - NL_SET_ERR_MSG_MOD(extack, "Only HSR v0 and v1 supported"); - return -EOPNOTSUPP; - } - - /* KSZ9477 can only perform HSR offloading for up to two ports */ - if (hweight8(dev->hsr_ports) >= 2) { - NL_SET_ERR_MSG_MOD(extack, - "Cannot offload more than two ports - using software HSR"); - return -EOPNOTSUPP; - } - - /* Self MAC address filtering, to avoid frames traversing - * the HSR ring more than once. - */ - ret = ksz_switch_macaddr_get(ds, port, extack); - if (ret) - return ret; - - ksz9477_hsr_join(ds, port, hsr); - dev->hsr_dev = hsr; - dev->hsr_ports |= BIT(port); - - return 0; -} - -int ksz_hsr_leave(struct dsa_switch *ds, int port, - struct net_device *hsr) -{ - struct ksz_device *dev = ds->priv; - - WARN_ON(dev->chip_id != KSZ9477_CHIP_ID); - - ksz9477_hsr_leave(ds, port, hsr); - dev->hsr_ports &= ~BIT(port); - if (!dev->hsr_ports) - dev->hsr_dev = NULL; - - ksz_switch_macaddr_put(ds); - - return 0; -} - int ksz_suspend(struct dsa_switch *ds) { struct ksz_device *dev = ds->priv; diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index 45613b9aeb1e..b4a5673ba365 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -366,42 +366,6 @@ struct ksz_dev_ops { u32 (*get_port_addr)(int port, int offset); void (*cfg_port_member)(struct ksz_device *dev, int port, u8 member); - /** - * @mdio_bus_preinit: Function pointer to pre-initialize the MDIO bus - * for accessing PHYs. - * @dev: Pointer to device structure. - * @side_mdio: Boolean indicating if the PHYs are accessed over a side - * MDIO bus. - * - * This function pointer is used to configure the MDIO bus for PHY - * access before initiating regular PHY operations. It enables either - * SPI/I2C or side MDIO access modes by unlocking necessary registers - * and setting up access permissions for the selected mode. - * - * Return: - * - 0 on success. - * - Negative error code on failure. - */ - int (*mdio_bus_preinit)(struct ksz_device *dev, bool side_mdio); - - /** - * @create_phy_addr_map: Function pointer to create a port-to-PHY - * address map. - * @dev: Pointer to device structure. - * @side_mdio: Boolean indicating if the PHYs are accessed over a side - * MDIO bus. - * - * This function pointer is responsible for mapping switch ports to PHY - * addresses according to the configured access mode (SPI or side MDIO) - * and the device’s strap configuration. The mapping setup may vary - * depending on the chip variant and configuration. Ensures the correct - * address mapping for PHY communication. - * - * Return: - * - 0 on success. - * - Negative error code on failure (e.g., invalid configuration). - */ - int (*create_phy_addr_map)(struct ksz_device *dev, bool side_mdio); void (*r_mib_cnt)(struct ksz_device *dev, int port, u16 addr, u64 *cnt); void (*r_mib_pkt)(struct ksz_device *dev, int port, u16 addr, @@ -414,7 +378,6 @@ struct ksz_dev_ops { u8 data); void (*freeze_mib)(struct ksz_device *dev, int port, bool freeze); void (*port_init_cnt)(struct ksz_device *dev, int port); - void (*setup_rgmii_delay)(struct ksz_device *dev, int port); int (*tc_cbs_set_cinc)(struct ksz_device *dev, int port, u32 val); int (*init)(struct ksz_device *dev); }; @@ -428,7 +391,6 @@ int ksz_switch_suspend(struct device *dev); int ksz_switch_resume(struct device *dev); void ksz_teardown(struct dsa_switch *ds); -void ksz_port_teardown(struct dsa_switch *ds, int port); void ksz_init_mib_timer(struct ksz_device *dev); bool ksz_is_port_mac_global_usable(struct dsa_switch *ds, int port); @@ -444,8 +406,6 @@ void ksz_switch_macaddr_put(struct dsa_switch *ds); void ksz_switch_shutdown(struct ksz_device *dev); int ksz_handle_wake_reason(struct ksz_device *dev, int port); -u32 ksz_get_phy_flags(struct dsa_switch *ds, int port); - int ksz_sset_count(struct dsa_switch *ds, int port, int sset); void ksz_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *buf); @@ -474,6 +434,8 @@ void ksz_phylink_get_caps(struct dsa_switch *ds, int port, void ksz_phylink_mac_disable_tx_lpi(struct phylink_config *config); int ksz_phylink_mac_enable_tx_lpi(struct phylink_config *config, u32 timer, bool tx_clock_stop); +void ksz_set_xmii(struct ksz_device *dev, int port, phy_interface_t interface); +bool ksz_phylink_need_config(struct phylink_config *config, unsigned int mode); void ksz_phylink_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state); @@ -483,14 +445,9 @@ void ksz_phylink_mac_link_down(struct phylink_config *config, int ksz_max_mtu(struct dsa_switch *ds, int port); -bool ksz_support_eee(struct dsa_switch *ds, int port); int ksz_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_keee *e); -int ksz_cls_flower_add(struct dsa_switch *ds, int port, - struct flow_cls_offload *cls, bool ingress); -int ksz_cls_flower_del(struct dsa_switch *ds, int port, - struct flow_cls_offload *cls, bool ingress); int ksz_setup_tc(struct dsa_switch *ds, int port, enum tc_setup_type type, void *type_data); @@ -500,14 +457,16 @@ int ksz_set_wol(struct dsa_switch *ds, int port, struct ethtool_wolinfo *wol); int ksz_port_set_mac_address(struct dsa_switch *ds, int port, const unsigned char *addr); -int ksz_hsr_join(struct dsa_switch *ds, int port, struct net_device *hsr, - struct netlink_ext_ack *extack); -int ksz_hsr_leave(struct dsa_switch *ds, int port, - struct net_device *hsr); int ksz_suspend(struct dsa_switch *ds); int ksz_resume(struct dsa_switch *ds); +int ksz_parse_dt_phy_config(struct ksz_device *dev, struct mii_bus *bus, + struct device_node *mdio_np); +int ksz_sw_mdio_read(struct mii_bus *bus, int addr, int regnum); +int ksz_sw_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val); +int ksz_parent_mdio_read(struct mii_bus *bus, int addr, int regnum); +int ksz_parent_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val); int ksz_mdio_register(struct ksz_device *dev); int ksz_pirq_setup(struct ksz_device *dev, u8 p); int ksz_girq_setup(struct ksz_device *dev); diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c index 536153886f7b..8eb5337b0c10 100644 --- a/drivers/net/dsa/microchip/lan937x_main.c +++ b/drivers/net/dsa/microchip/lan937x_main.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -643,6 +644,20 @@ static void lan937x_setup_rgmii_delay(struct ksz_device *dev, int port) } } +static void lan937x_phylink_mac_config(struct phylink_config *config, + unsigned int mode, + const struct phylink_link_state *state) +{ + struct dsa_port *dp = dsa_phylink_to_port(config); + struct ksz_device *dev = dp->ds->priv; + int port = dp->index; + + if (ksz_phylink_need_config(config, mode)) { + ksz_set_xmii(dev, port, state->interface); + lan937x_setup_rgmii_delay(dev, port); + } +} + static int lan937x_tc_cbs_set_cinc(struct ksz_device *dev, int port, u32 val) { return ksz_pwrite32(dev, port, REG_PORT_MTI_CREDIT_INCREMENT, val); @@ -655,6 +670,101 @@ static int lan937x_switch_init(struct ksz_device *dev) return 0; } +/** + * lan937x_mdio_register - Register and configure the MDIO bus for the LAN937x. + * @dev: Pointer to the KSZ device structure. + * + * This function sets up and registers an MDIO bus for a LAN937x switch, + * allowing access to its internal PHYs. If the device supports side MDIO, + * the function will configure the external MDIO controller specified by the + * "mdio-parent-bus" device tree property to directly manage internal PHYs. + * Otherwise, SPI or I2C access is set up for PHY access. + * + * Return: 0 on success, or a negative error code on failure. + */ +static int lan937x_mdio_register(struct ksz_device *dev) +{ + struct device_node *parent_bus_node; + struct mii_bus *parent_bus = NULL; + struct dsa_switch *ds = dev->ds; + struct device_node *mdio_np; + struct mii_bus *bus; + int ret; + + mdio_np = of_get_child_by_name(dev->dev->of_node, "mdio"); + if (!mdio_np) + return 0; + + parent_bus_node = of_parse_phandle(mdio_np, "mdio-parent-bus", 0); + if (parent_bus_node && !dev->info->phy_side_mdio_supported) { + dev_err(dev->dev, "Side MDIO bus is not supported for this HW, ignoring 'mdio-parent-bus' property.\n"); + ret = -EINVAL; + + goto put_mdio_node; + } else if (parent_bus_node) { + parent_bus = of_mdio_find_bus(parent_bus_node); + if (!parent_bus) { + ret = -EPROBE_DEFER; + + goto put_mdio_node; + } + + dev->parent_mdio_bus = parent_bus; + } + + bus = devm_mdiobus_alloc(ds->dev); + if (!bus) { + ret = -ENOMEM; + goto put_mdio_node; + } + + ret = lan937x_mdio_bus_preinit(dev, !!parent_bus); + if (ret) + goto put_mdio_node; + + ret = lan937x_create_phy_addr_map(dev, !!parent_bus); + if (ret) + goto put_mdio_node; + + bus->priv = dev; + if (parent_bus) { + bus->read = ksz_parent_mdio_read; + bus->write = ksz_parent_mdio_write; + bus->name = "KSZ side MDIO"; + snprintf(bus->id, MII_BUS_ID_SIZE, "ksz-side-mdio-%d", + ds->index); + } else { + bus->read = ksz_sw_mdio_read; + bus->write = ksz_sw_mdio_write; + bus->name = "ksz user smi"; + if (ds->dst->index != 0) + snprintf(bus->id, MII_BUS_ID_SIZE, "SMI-%d-%d", + ds->dst->index, ds->index); + else + snprintf(bus->id, MII_BUS_ID_SIZE, "SMI-%d", ds->index); + } + + ret = ksz_parse_dt_phy_config(dev, bus, mdio_np); + if (ret) + goto put_mdio_node; + + ds->phys_mii_mask = bus->phy_mask; + bus->parent = ds->dev; + + ds->user_mii_bus = bus; + + ret = devm_of_mdiobus_register(ds->dev, bus, mdio_np); + if (ret) + dev_err(ds->dev, "unable to register MDIO bus %s\n", + bus->id); + +put_mdio_node: + of_node_put(mdio_np); + of_node_put(parent_bus_node); + + return ret; +} + static int lan937x_setup(struct dsa_switch *ds) { struct ksz_device *dev = ds->priv; @@ -768,7 +878,7 @@ static int lan937x_setup(struct dsa_switch *ds) goto port_release; } - ret = ksz_mdio_register(dev); + ret = lan937x_mdio_register(dev); if (ret < 0) { dev_err(dev->dev, "failed to register the mdio"); goto out_ptp_clock_unregister; @@ -821,7 +931,7 @@ static int lan937x_connect_tag_protocol(struct dsa_switch *ds, } const struct phylink_mac_ops lan937x_phylink_mac_ops = { - .mac_config = ksz_phylink_mac_config, + .mac_config = lan937x_phylink_mac_config, .mac_link_down = ksz_phylink_mac_link_down, .mac_link_up = ksz9477_phylink_mac_link_up, .mac_disable_tx_lpi = ksz_phylink_mac_disable_tx_lpi, @@ -831,14 +941,11 @@ const struct phylink_mac_ops lan937x_phylink_mac_ops = { const struct ksz_dev_ops lan937x_dev_ops = { .get_port_addr = ksz9477_get_port_addr, .cfg_port_member = ksz9477_cfg_port_member, - .mdio_bus_preinit = lan937x_mdio_bus_preinit, - .create_phy_addr_map = lan937x_create_phy_addr_map, .r_mib_cnt = ksz9477_r_mib_cnt, .r_mib_pkt = ksz9477_r_mib_pkt, .r_mib_stat64 = ksz_r_mib_stats64, .freeze_mib = ksz9477_freeze_mib, .port_init_cnt = ksz9477_port_init_cnt, - .setup_rgmii_delay = lan937x_setup_rgmii_delay, .tc_cbs_set_cinc = lan937x_tc_cbs_set_cinc, .init = lan937x_switch_init, }; @@ -846,7 +953,6 @@ const struct ksz_dev_ops lan937x_dev_ops = { const struct dsa_switch_ops lan937x_switch_ops = { .get_tag_protocol = lan937x_get_tag_protocol, .connect_tag_protocol = lan937x_connect_tag_protocol, - .get_phy_flags = ksz_get_phy_flags, .setup = lan937x_setup, .teardown = ksz_teardown, .phy_read = lan937x_phy_read16, @@ -859,11 +965,8 @@ const struct dsa_switch_ops lan937x_switch_ops = { .get_sset_count = ksz_sset_count, .port_bridge_join = ksz_port_bridge_join, .port_bridge_leave = ksz_port_bridge_leave, - .port_hsr_join = ksz_hsr_join, - .port_hsr_leave = ksz_hsr_leave, .port_set_mac_address = ksz_port_set_mac_address, .port_stp_state_set = ksz_port_stp_state_set, - .port_teardown = ksz_port_teardown, .port_pre_bridge_flags = ksz_port_pre_bridge_flags, .port_bridge_flags = ksz_port_bridge_flags, .port_fast_age = ksz9477_flush_dyn_mac_table, @@ -881,8 +984,6 @@ const struct dsa_switch_ops lan937x_switch_ops = { .get_pause_stats = ksz_get_pause_stats, .port_change_mtu = lan937x_change_mtu, .port_max_mtu = ksz_max_mtu, - .get_wol = ksz_get_wol, - .set_wol = ksz_set_wol, .suspend = ksz_suspend, .resume = ksz_resume, .get_ts_info = ksz_get_ts_info, @@ -890,11 +991,7 @@ const struct dsa_switch_ops lan937x_switch_ops = { .port_hwtstamp_set = ksz_hwtstamp_set, .port_txtstamp = ksz_port_txtstamp, .port_rxtstamp = ksz_port_rxtstamp, - .cls_flower_add = ksz_cls_flower_add, - .cls_flower_del = ksz_cls_flower_del, .port_setup_tc = ksz_setup_tc, - .support_eee = ksz_support_eee, - .set_mac_eee = ksz_set_mac_eee, .port_get_default_prio = ksz_port_get_default_prio, .port_set_default_prio = ksz_port_set_default_prio, .port_get_dscp_prio = ksz_port_get_dscp_prio,