From 15604320877e09d84dd5f1d79ce138bf2263f2a1 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Thu, 2 Jul 2026 11:07:23 +0200 Subject: [PATCH 01/10] net: dsa: microchip: split ksz8_change_mtu() Even among the ksz8 family, there are big differences in the MTU change procedure between KSZ87xx and KSZ88xx (KSZ8463 is like KSZ88xx here). Since we have 3 separate dsa_switch_ops for what constitutes "KSZ8", we can split those procedures into separate functions. Signed-off-by: Vladimir Oltean Signed-off-by: Bastien Curutchet (Schneider Electric) Link: https://patch.msgid.link/20260702-clean-ksz-4th-v1-1-93441e695fa4@bootlin.com Signed-off-by: Paolo Abeni --- drivers/net/dsa/microchip/ksz8.c | 49 +++++++++++++------------------- 1 file changed, 19 insertions(+), 30 deletions(-) diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c index 586916570a84..c351179f6a60 100644 --- a/drivers/net/dsa/microchip/ksz8.c +++ b/drivers/net/dsa/microchip/ksz8.c @@ -158,10 +158,17 @@ static int ksz8_reset_switch(struct ksz_device *dev) return 0; } -static int ksz8863_change_mtu(struct ksz_device *dev, int frame_size) +static int ksz88xx_change_mtu(struct dsa_switch *ds, int port, int mtu) { + struct ksz_device *dev = ds->priv; + int frame_size; u8 ctrl2 = 0; + if (!dsa_is_cpu_port(dev->ds, port)) + return 0; + + frame_size = mtu + VLAN_ETH_HLEN + ETH_FCS_LEN; + if (frame_size <= KSZ8_LEGAL_PACKET_SIZE) ctrl2 |= KSZ8863_LEGAL_PACKET_ENABLE; else if (frame_size > KSZ8863_NORMAL_PACKET_SIZE) @@ -171,11 +178,18 @@ static int ksz8863_change_mtu(struct ksz_device *dev, int frame_size) KSZ8863_HUGE_PACKET_ENABLE, ctrl2); } -static int ksz8795_change_mtu(struct ksz_device *dev, int frame_size) +static int ksz87xx_change_mtu(struct dsa_switch *ds, int port, int mtu) { + struct ksz_device *dev = ds->priv; u8 ctrl1 = 0, ctrl2 = 0; + u16 frame_size; int ret; + if (!dsa_is_cpu_port(dev->ds, port)) + return 0; + + frame_size = mtu + VLAN_ETH_HLEN + ETH_FCS_LEN; + if (frame_size > KSZ8_LEGAL_PACKET_SIZE) ctrl2 |= SW_LEGAL_PACKET_DISABLE; if (frame_size > KSZ8863_NORMAL_PACKET_SIZE) @@ -188,31 +202,6 @@ static int ksz8795_change_mtu(struct ksz_device *dev, int frame_size) return ksz_rmw8(dev, REG_SW_CTRL_2, SW_LEGAL_PACKET_DISABLE, ctrl2); } -static int ksz8_change_mtu(struct dsa_switch *ds, int port, int mtu) -{ - struct ksz_device *dev = ds->priv; - u16 frame_size; - - if (!dsa_is_cpu_port(dev->ds, port)) - return 0; - - frame_size = mtu + VLAN_ETH_HLEN + ETH_FCS_LEN; - - switch (dev->chip_id) { - case KSZ8795_CHIP_ID: - case KSZ8794_CHIP_ID: - case KSZ8765_CHIP_ID: - return ksz8795_change_mtu(dev, frame_size); - case KSZ8463_CHIP_ID: - case KSZ88X3_CHIP_ID: - case KSZ8864_CHIP_ID: - case KSZ8895_CHIP_ID: - return ksz8863_change_mtu(dev, frame_size); - } - - return -EOPNOTSUPP; -} - static int ksz8_port_queue_split(struct ksz_device *dev, int port, int queues) { u8 mask_4q, mask_2q; @@ -2551,7 +2540,7 @@ const struct dsa_switch_ops ksz8463_switch_ops = { .port_mirror_del = ksz8_port_mirror_del, .get_stats64 = ksz_get_stats64, .get_pause_stats = ksz_get_pause_stats, - .port_change_mtu = ksz8_change_mtu, + .port_change_mtu = ksz88xx_change_mtu, .port_max_mtu = ksz_max_mtu, .suspend = ksz_suspend, .resume = ksz_resume, @@ -2601,7 +2590,7 @@ const struct dsa_switch_ops ksz87xx_switch_ops = { .port_mirror_del = ksz8_port_mirror_del, .get_stats64 = ksz_get_stats64, .get_pause_stats = ksz_get_pause_stats, - .port_change_mtu = ksz8_change_mtu, + .port_change_mtu = ksz87xx_change_mtu, .port_max_mtu = ksz_max_mtu, .suspend = ksz_suspend, .resume = ksz_resume, @@ -2652,7 +2641,7 @@ const struct dsa_switch_ops ksz88xx_switch_ops = { .port_mirror_del = ksz8_port_mirror_del, .get_stats64 = ksz_get_stats64, .get_pause_stats = ksz_get_pause_stats, - .port_change_mtu = ksz8_change_mtu, + .port_change_mtu = ksz88xx_change_mtu, .port_max_mtu = ksz_max_mtu, .get_wol = ksz_get_wol, .set_wol = ksz_set_wol, From 18a856b236800efb3cfc321e51580e00c4a6e497 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Thu, 2 Jul 2026 11:07:24 +0200 Subject: [PATCH 02/10] net: dsa: microchip: split port_max_mtu() implementation ksz_max_mtu() is a bit cluttered. It would be good for developers and reviewers if they didn't need to look at a common function for hardware they likely don't have, and which is vastly different, when they are interested in only a specific chip. Benefit from the fact that all families listed here have their own dsa_switch_ops, and provide separate implementations for the port_max_mtu() method. Signed-off-by: Vladimir Oltean Signed-off-by: Bastien Curutchet (Schneider Electric) Link: https://patch.msgid.link/20260702-clean-ksz-4th-v1-2-93441e695fa4@bootlin.com Signed-off-by: Paolo Abeni --- drivers/net/dsa/microchip/ksz8.c | 16 ++++++++--- drivers/net/dsa/microchip/ksz9477.c | 7 ++++- drivers/net/dsa/microchip/ksz9477.h | 1 + drivers/net/dsa/microchip/ksz_common.c | 34 ------------------------ drivers/net/dsa/microchip/ksz_common.h | 2 -- drivers/net/dsa/microchip/lan937x_main.c | 2 +- 6 files changed, 21 insertions(+), 41 deletions(-) diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c index c351179f6a60..2b4720c4c87e 100644 --- a/drivers/net/dsa/microchip/ksz8.c +++ b/drivers/net/dsa/microchip/ksz8.c @@ -202,6 +202,16 @@ static int ksz87xx_change_mtu(struct dsa_switch *ds, int port, int mtu) return ksz_rmw8(dev, REG_SW_CTRL_2, SW_LEGAL_PACKET_DISABLE, ctrl2); } +static int ksz87xx_max_mtu(struct dsa_switch *ds, int port) +{ + return KSZ8795_HUGE_PACKET_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN; +} + +static int ksz88xx_max_mtu(struct dsa_switch *ds, int port) +{ + return KSZ8863_HUGE_PACKET_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN; +} + static int ksz8_port_queue_split(struct ksz_device *dev, int port, int queues) { u8 mask_4q, mask_2q; @@ -2541,7 +2551,7 @@ const struct dsa_switch_ops ksz8463_switch_ops = { .get_stats64 = ksz_get_stats64, .get_pause_stats = ksz_get_pause_stats, .port_change_mtu = ksz88xx_change_mtu, - .port_max_mtu = ksz_max_mtu, + .port_max_mtu = ksz88xx_max_mtu, .suspend = ksz_suspend, .resume = ksz_resume, .get_ts_info = ksz_get_ts_info, @@ -2591,7 +2601,7 @@ const struct dsa_switch_ops ksz87xx_switch_ops = { .get_stats64 = ksz_get_stats64, .get_pause_stats = ksz_get_pause_stats, .port_change_mtu = ksz87xx_change_mtu, - .port_max_mtu = ksz_max_mtu, + .port_max_mtu = ksz87xx_max_mtu, .suspend = ksz_suspend, .resume = ksz_resume, .get_ts_info = ksz_get_ts_info, @@ -2642,7 +2652,7 @@ const struct dsa_switch_ops ksz88xx_switch_ops = { .get_stats64 = ksz_get_stats64, .get_pause_stats = ksz_get_pause_stats, .port_change_mtu = ksz88xx_change_mtu, - .port_max_mtu = ksz_max_mtu, + .port_max_mtu = ksz88xx_max_mtu, .get_wol = ksz_get_wol, .set_wol = ksz_set_wol, .suspend = ksz_suspend, diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c index f3f0c98dfb5a..0b08d2175f17 100644 --- a/drivers/net/dsa/microchip/ksz9477.c +++ b/drivers/net/dsa/microchip/ksz9477.c @@ -60,6 +60,11 @@ static int ksz9477_change_mtu(struct dsa_switch *ds, int port, int mtu) REG_SW_MTU_MASK, frame_size); } +int ksz9477_max_mtu(struct dsa_switch *ds, int port) +{ + return KSZ9477_MAX_FRAME_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN; +} + static int ksz9477_wait_vlan_ctrl_ready(struct ksz_device *dev) { unsigned int val; @@ -2098,7 +2103,7 @@ const struct dsa_switch_ops ksz9477_switch_ops = { .get_stats64 = ksz_get_stats64, .get_pause_stats = ksz_get_pause_stats, .port_change_mtu = ksz9477_change_mtu, - .port_max_mtu = ksz_max_mtu, + .port_max_mtu = ksz9477_max_mtu, .get_wol = ksz_get_wol, .set_wol = ksz_set_wol, .suspend = ksz_suspend, diff --git a/drivers/net/dsa/microchip/ksz9477.h b/drivers/net/dsa/microchip/ksz9477.h index 92a1d889224d..25b74d0af6c5 100644 --- a/drivers/net/dsa/microchip/ksz9477.h +++ b/drivers/net/dsa/microchip/ksz9477.h @@ -21,6 +21,7 @@ void ksz9477_freeze_mib(struct ksz_device *dev, int port, bool freeze); void ksz9477_port_init_cnt(struct ksz_device *dev, int port); int ksz9477_port_vlan_filtering(struct dsa_switch *ds, int port, bool flag, struct netlink_ext_ack *extack); +int ksz9477_max_mtu(struct dsa_switch *ds, int port); int ksz9477_port_vlan_add(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan, struct netlink_ext_ack *extack); diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index d1726778bb48..686bdbfe8b6a 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -2984,40 +2984,6 @@ int ksz_port_bridge_flags(struct dsa_switch *ds, int port, return 0; } -int ksz_max_mtu(struct dsa_switch *ds, int port) -{ - struct ksz_device *dev = ds->priv; - - switch (dev->chip_id) { - case KSZ8795_CHIP_ID: - case KSZ8794_CHIP_ID: - case KSZ8765_CHIP_ID: - return KSZ8795_HUGE_PACKET_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN; - case KSZ8463_CHIP_ID: - case KSZ88X3_CHIP_ID: - case KSZ8864_CHIP_ID: - case KSZ8895_CHIP_ID: - return KSZ8863_HUGE_PACKET_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN; - 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 LAN9370_CHIP_ID: - case LAN9371_CHIP_ID: - case LAN9372_CHIP_ID: - case LAN9373_CHIP_ID: - case LAN9374_CHIP_ID: - case LAN9646_CHIP_ID: - return KSZ9477_MAX_FRAME_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN; - } - - return -EOPNOTSUPP; -} - int ksz_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_keee *e) { diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index b4a5673ba365..f276f2452684 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -443,8 +443,6 @@ void ksz_phylink_mac_link_down(struct phylink_config *config, unsigned int mode, phy_interface_t interface); -int ksz_max_mtu(struct dsa_switch *ds, int port); - int ksz_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_keee *e); diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c index 8eb5337b0c10..f060fbc4c4f4 100644 --- a/drivers/net/dsa/microchip/lan937x_main.c +++ b/drivers/net/dsa/microchip/lan937x_main.c @@ -983,7 +983,7 @@ const struct dsa_switch_ops lan937x_switch_ops = { .get_stats64 = ksz_get_stats64, .get_pause_stats = ksz_get_pause_stats, .port_change_mtu = lan937x_change_mtu, - .port_max_mtu = ksz_max_mtu, + .port_max_mtu = ksz9477_max_mtu, .suspend = ksz_suspend, .resume = ksz_resume, .get_ts_info = ksz_get_ts_info, From caf5d68b51e61bfdd8aaa8d5aea6f5f24d76fca5 Mon Sep 17 00:00:00 2001 From: "Bastien Curutchet (Schneider Electric)" Date: Thu, 2 Jul 2026 11:07:25 +0200 Subject: [PATCH 03/10] net: dsa: microchip: make ksz_is_port_mac_global_usable() static ksz_is_port_mac_global_usable() is exposed in ksz_common.h while it's only used internally. Make ksz_is_port_mac_global_usable() static. Move its definition above its first call. Signed-off-by: Bastien Curutchet (Schneider Electric) Link: https://patch.msgid.link/20260702-clean-ksz-4th-v1-3-93441e695fa4@bootlin.com Signed-off-by: Paolo Abeni --- drivers/net/dsa/microchip/ksz_common.c | 56 +++++++++++++------------- drivers/net/dsa/microchip/ksz_common.h | 1 - 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 686bdbfe8b6a..be7738ae1f2a 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -3670,6 +3670,34 @@ int ksz_handle_wake_reason(struct ksz_device *dev, int port) pme_status); } +/** + * ksz_is_port_mac_global_usable - Check if the MAC address on a given port + * can be used as a global address. + * @ds: Pointer to the DSA switch structure. + * @port: The port number on which the MAC address is to be checked. + * + * This function examines the MAC address set on the specified port and + * determines if it can be used as a global address for the switch. + * + * Return: true if the port's MAC address can be used as a global address, false + * otherwise. + */ +static bool ksz_is_port_mac_global_usable(struct dsa_switch *ds, int port) +{ + struct net_device *user = dsa_to_port(ds, port)->user; + const unsigned char *addr = user->dev_addr; + struct ksz_switch_macaddr *switch_macaddr; + struct ksz_device *dev = ds->priv; + + ASSERT_RTNL(); + + switch_macaddr = dev->switch_macaddr; + if (switch_macaddr && !ether_addr_equal(switch_macaddr->addr, addr)) + return false; + + return true; +} + /** * ksz_get_wol - Get Wake-on-LAN settings for a specified port. * @ds: The dsa_switch structure. @@ -3863,34 +3891,6 @@ int ksz_port_set_mac_address(struct dsa_switch *ds, int port, return 0; } -/** - * ksz_is_port_mac_global_usable - Check if the MAC address on a given port - * can be used as a global address. - * @ds: Pointer to the DSA switch structure. - * @port: The port number on which the MAC address is to be checked. - * - * This function examines the MAC address set on the specified port and - * determines if it can be used as a global address for the switch. - * - * Return: true if the port's MAC address can be used as a global address, false - * otherwise. - */ -bool ksz_is_port_mac_global_usable(struct dsa_switch *ds, int port) -{ - struct net_device *user = dsa_to_port(ds, port)->user; - const unsigned char *addr = user->dev_addr; - struct ksz_switch_macaddr *switch_macaddr; - struct ksz_device *dev = ds->priv; - - ASSERT_RTNL(); - - switch_macaddr = dev->switch_macaddr; - if (switch_macaddr && !ether_addr_equal(switch_macaddr->addr, addr)) - return false; - - return true; -} - /** * ksz_switch_macaddr_get - Program the switch's MAC address register. * @ds: DSA switch instance. diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index f276f2452684..f367d6f96fa2 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -393,7 +393,6 @@ int ksz_switch_resume(struct device *dev); void ksz_teardown(struct dsa_switch *ds); void ksz_init_mib_timer(struct ksz_device *dev); -bool ksz_is_port_mac_global_usable(struct dsa_switch *ds, int port); void ksz_r_mib_stats64(struct ksz_device *dev, int port); void ksz88xx_r_mib_stats64(struct ksz_device *dev, int port); void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 state); From a7e806f11f37cc4d6a85cdbba568ed8d911be86f Mon Sep 17 00:00:00 2001 From: "Bastien Curutchet (Schneider Electric)" Date: Thu, 2 Jul 2026 11:07:26 +0200 Subject: [PATCH 04/10] net: dsa: microchip: move ksz88xx stats handling in ksz8.c ksz88xx_r_mib_stats64() is defined in ksz_common while it's clearly ksz88xx-specific. Move its definition in ksz8.c Signed-off-by: Bastien Curutchet (Schneider Electric) Link: https://patch.msgid.link/20260702-clean-ksz-4th-v1-4-93441e695fa4@bootlin.com Signed-off-by: Paolo Abeni --- drivers/net/dsa/microchip/ksz8.c | 86 ++++++++++++++++++++++++++ drivers/net/dsa/microchip/ksz_common.c | 86 -------------------------- drivers/net/dsa/microchip/ksz_common.h | 1 - 3 files changed, 86 insertions(+), 87 deletions(-) diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c index 2b4720c4c87e..ad7c7a6e1d31 100644 --- a/drivers/net/dsa/microchip/ksz8.c +++ b/drivers/net/dsa/microchip/ksz8.c @@ -36,6 +36,43 @@ #include "ksz8_reg.h" #include "ksz8.h" +struct ksz88xx_stats_raw { + u64 rx; + u64 rx_hi; + u64 rx_undersize; + u64 rx_fragments; + u64 rx_oversize; + u64 rx_jabbers; + u64 rx_symbol_err; + u64 rx_crc_err; + u64 rx_align_err; + u64 rx_mac_ctrl; + u64 rx_pause; + u64 rx_bcast; + u64 rx_mcast; + u64 rx_ucast; + u64 rx_64_or_less; + u64 rx_65_127; + u64 rx_128_255; + u64 rx_256_511; + u64 rx_512_1023; + u64 rx_1024_1522; + u64 tx; + u64 tx_hi; + u64 tx_late_col; + u64 tx_pause; + u64 tx_bcast; + u64 tx_mcast; + u64 tx_ucast; + u64 tx_deferred; + u64 tx_total_col; + u64 tx_exc_col; + u64 tx_single_col; + u64 tx_mult_col; + u64 rx_discards; + u64 tx_discards; +}; + static void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set) { ksz_rmw8(dev, addr, bits, set ? bits : 0); @@ -2052,6 +2089,55 @@ static int ksz8_enable_stp_addr(struct ksz_device *dev) return ksz8_w_sta_mac_table(dev, 0, &alu); } +static void ksz88xx_r_mib_stats64(struct ksz_device *dev, int port) +{ + struct ethtool_pause_stats *pstats; + struct rtnl_link_stats64 *stats; + struct ksz88xx_stats_raw *raw; + struct ksz_port_mib *mib; + + mib = &dev->ports[port].mib; + stats = &mib->stats64; + pstats = &mib->pause_stats; + raw = (struct ksz88xx_stats_raw *)mib->counters; + + spin_lock(&mib->stats64_lock); + + stats->rx_packets = raw->rx_bcast + raw->rx_mcast + raw->rx_ucast + + raw->rx_pause; + stats->tx_packets = raw->tx_bcast + raw->tx_mcast + raw->tx_ucast + + raw->tx_pause; + + /* HW counters are counting bytes + FCS which is not acceptable + * for rtnl_link_stats64 interface + */ + stats->rx_bytes = raw->rx + raw->rx_hi - stats->rx_packets * ETH_FCS_LEN; + stats->tx_bytes = raw->tx + raw->tx_hi - stats->tx_packets * ETH_FCS_LEN; + + stats->rx_length_errors = raw->rx_undersize + raw->rx_fragments + + raw->rx_oversize; + + stats->rx_crc_errors = raw->rx_crc_err; + stats->rx_frame_errors = raw->rx_align_err; + stats->rx_dropped = raw->rx_discards; + stats->rx_errors = stats->rx_length_errors + stats->rx_crc_errors + + stats->rx_frame_errors + stats->rx_dropped; + + stats->tx_window_errors = raw->tx_late_col; + stats->tx_fifo_errors = raw->tx_discards; + stats->tx_aborted_errors = raw->tx_exc_col; + stats->tx_errors = stats->tx_window_errors + stats->tx_fifo_errors + + stats->tx_aborted_errors; + + stats->multicast = raw->rx_mcast; + stats->collisions = raw->tx_total_col; + + pstats->tx_pause_frames = raw->tx_pause; + pstats->rx_pause_frames = raw->rx_pause; + + spin_unlock(&mib->stats64_lock); +} + static int ksz8_setup(struct dsa_switch *ds) { struct ksz_device *dev = ds->priv; diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index be7738ae1f2a..56dd4c27b182 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -76,43 +76,6 @@ struct ksz_stats_raw { u64 tx_discards; }; -struct ksz88xx_stats_raw { - u64 rx; - u64 rx_hi; - u64 rx_undersize; - u64 rx_fragments; - u64 rx_oversize; - u64 rx_jabbers; - u64 rx_symbol_err; - u64 rx_crc_err; - u64 rx_align_err; - u64 rx_mac_ctrl; - u64 rx_pause; - u64 rx_bcast; - u64 rx_mcast; - u64 rx_ucast; - u64 rx_64_or_less; - u64 rx_65_127; - u64 rx_128_255; - u64 rx_256_511; - u64 rx_512_1023; - u64 rx_1024_1522; - u64 tx; - u64 tx_hi; - u64 tx_late_col; - u64 tx_pause; - u64 tx_bcast; - u64 tx_mcast; - u64 tx_ucast; - u64 tx_deferred; - u64 tx_total_col; - u64 tx_exc_col; - u64 tx_single_col; - u64 tx_mult_col; - u64 rx_discards; - u64 tx_discards; -}; - static const struct ksz_mib_names ksz88xx_mib_names[] = { { 0x00, "rx" }, { 0x01, "rx_hi" }, @@ -2063,55 +2026,6 @@ void ksz_r_mib_stats64(struct ksz_device *dev, int port) } } -void ksz88xx_r_mib_stats64(struct ksz_device *dev, int port) -{ - struct ethtool_pause_stats *pstats; - struct rtnl_link_stats64 *stats; - struct ksz88xx_stats_raw *raw; - struct ksz_port_mib *mib; - - mib = &dev->ports[port].mib; - stats = &mib->stats64; - pstats = &mib->pause_stats; - raw = (struct ksz88xx_stats_raw *)mib->counters; - - spin_lock(&mib->stats64_lock); - - stats->rx_packets = raw->rx_bcast + raw->rx_mcast + raw->rx_ucast + - raw->rx_pause; - stats->tx_packets = raw->tx_bcast + raw->tx_mcast + raw->tx_ucast + - raw->tx_pause; - - /* HW counters are counting bytes + FCS which is not acceptable - * for rtnl_link_stats64 interface - */ - stats->rx_bytes = raw->rx + raw->rx_hi - stats->rx_packets * ETH_FCS_LEN; - stats->tx_bytes = raw->tx + raw->tx_hi - stats->tx_packets * ETH_FCS_LEN; - - stats->rx_length_errors = raw->rx_undersize + raw->rx_fragments + - raw->rx_oversize; - - stats->rx_crc_errors = raw->rx_crc_err; - stats->rx_frame_errors = raw->rx_align_err; - stats->rx_dropped = raw->rx_discards; - stats->rx_errors = stats->rx_length_errors + stats->rx_crc_errors + - stats->rx_frame_errors + stats->rx_dropped; - - stats->tx_window_errors = raw->tx_late_col; - stats->tx_fifo_errors = raw->tx_discards; - stats->tx_aborted_errors = raw->tx_exc_col; - stats->tx_errors = stats->tx_window_errors + stats->tx_fifo_errors + - stats->tx_aborted_errors; - - stats->multicast = raw->rx_mcast; - stats->collisions = raw->tx_total_col; - - pstats->tx_pause_frames = raw->tx_pause; - pstats->rx_pause_frames = raw->rx_pause; - - spin_unlock(&mib->stats64_lock); -} - void ksz_get_stats64(struct dsa_switch *ds, int port, struct rtnl_link_stats64 *s) { diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index f367d6f96fa2..347787ef1f00 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -394,7 +394,6 @@ void ksz_teardown(struct dsa_switch *ds); void ksz_init_mib_timer(struct ksz_device *dev); void ksz_r_mib_stats64(struct ksz_device *dev, int port); -void ksz88xx_r_mib_stats64(struct ksz_device *dev, int port); void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 state); bool ksz_get_gbit(struct ksz_device *dev, int port); phy_interface_t ksz_get_xmii(struct ksz_device *dev, int port, bool gbit); From 7a83196e901258764aa53b52ee4d90b4b1d905be Mon Sep 17 00:00:00 2001 From: "Bastien Curutchet (Schneider Electric)" Date: Thu, 2 Jul 2026 11:07:27 +0200 Subject: [PATCH 05/10] net: dsa: microchip: move ksz_get_gbit() and ksz_get_xmii() to ksz9477.c ksz_get_gbit() and ksz_get_xmii() are defined in the ksz_common while they are only used by the ksz9477 driver. Move their definition into ksz9477.c Signed-off-by: Bastien Curutchet (Schneider Electric) Link: https://patch.msgid.link/20260702-clean-ksz-4th-v1-5-93441e695fa4@bootlin.com Signed-off-by: Paolo Abeni --- drivers/net/dsa/microchip/ksz9477.c | 56 +++++++++++++++++++++++++- drivers/net/dsa/microchip/ksz_common.c | 51 ----------------------- drivers/net/dsa/microchip/ksz_common.h | 2 - 3 files changed, 54 insertions(+), 55 deletions(-) diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c index 0b08d2175f17..ad6748905e08 100644 --- a/drivers/net/dsa/microchip/ksz9477.c +++ b/drivers/net/dsa/microchip/ksz9477.c @@ -1181,6 +1181,58 @@ void ksz9477_port_mirror_del(struct dsa_switch *ds, int port, PORT_MIRROR_SNIFFER, false); } +static bool ksz9477_get_gbit(struct ksz_device *dev, int port) +{ + const u8 *bitval = dev->info->xmii_ctrl1; + const u16 *regs = dev->info->regs; + bool gbit = false; + u8 data8; + bool val; + + ksz_pread8(dev, port, regs[P_XMII_CTRL_1], &data8); + + val = FIELD_GET(P_GMII_1GBIT_M, data8); + + if (val == bitval[P_GMII_1GBIT]) + gbit = true; + + return gbit; +} + +static phy_interface_t ksz9477_get_xmii(struct ksz_device *dev, int port, + bool gbit) +{ + const u8 *bitval = dev->info->xmii_ctrl1; + const u16 *regs = dev->info->regs; + phy_interface_t interface; + u8 data8; + u8 val; + + ksz_pread8(dev, port, regs[P_XMII_CTRL_1], &data8); + + val = FIELD_GET(P_MII_SEL_M, data8); + + if (val == bitval[P_MII_SEL]) { + if (gbit) + interface = PHY_INTERFACE_MODE_GMII; + else + interface = PHY_INTERFACE_MODE_MII; + } else if (val == bitval[P_RMII_SEL]) { + interface = PHY_INTERFACE_MODE_RMII; + } else { + interface = PHY_INTERFACE_MODE_RGMII; + if (data8 & P_RGMII_ID_EG_ENABLE) + interface = PHY_INTERFACE_MODE_RGMII_TXID; + if (data8 & P_RGMII_ID_IG_ENABLE) { + interface = PHY_INTERFACE_MODE_RGMII_RXID; + if (data8 & P_RGMII_ID_EG_ENABLE) + interface = PHY_INTERFACE_MODE_RGMII_ID; + } + } + + return interface; +} + static phy_interface_t ksz9477_get_interface(struct ksz_device *dev, int port) { phy_interface_t interface; @@ -1189,9 +1241,9 @@ static phy_interface_t ksz9477_get_interface(struct ksz_device *dev, int port) if (dev->info->internal_phy[port]) return PHY_INTERFACE_MODE_NA; - gbit = ksz_get_gbit(dev, port); + gbit = ksz9477_get_gbit(dev, port); - interface = ksz_get_xmii(dev, port, gbit); + interface = ksz9477_get_xmii(dev, port, gbit); return interface; } diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 56dd4c27b182..92fcbd9605c7 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -2966,39 +2966,6 @@ void ksz_set_xmii(struct ksz_device *dev, int port, phy_interface_t interface) ksz_pwrite8(dev, port, regs[P_XMII_CTRL_1], data8); } -phy_interface_t ksz_get_xmii(struct ksz_device *dev, int port, bool gbit) -{ - const u8 *bitval = dev->info->xmii_ctrl1; - const u16 *regs = dev->info->regs; - phy_interface_t interface; - u8 data8; - u8 val; - - ksz_pread8(dev, port, regs[P_XMII_CTRL_1], &data8); - - val = FIELD_GET(P_MII_SEL_M, data8); - - if (val == bitval[P_MII_SEL]) { - if (gbit) - interface = PHY_INTERFACE_MODE_GMII; - else - interface = PHY_INTERFACE_MODE_MII; - } else if (val == bitval[P_RMII_SEL]) { - interface = PHY_INTERFACE_MODE_RMII; - } else { - interface = PHY_INTERFACE_MODE_RGMII; - if (data8 & P_RGMII_ID_EG_ENABLE) - interface = PHY_INTERFACE_MODE_RGMII_TXID; - if (data8 & P_RGMII_ID_IG_ENABLE) { - interface = PHY_INTERFACE_MODE_RGMII_RXID; - if (data8 & P_RGMII_ID_EG_ENABLE) - interface = PHY_INTERFACE_MODE_RGMII_ID; - } - } - - return interface; -} - bool ksz_phylink_need_config(struct phylink_config *config, unsigned int mode) { @@ -3034,24 +3001,6 @@ void ksz_phylink_mac_config(struct phylink_config *config, ksz_set_xmii(dev, port, state->interface); } -bool ksz_get_gbit(struct ksz_device *dev, int port) -{ - const u8 *bitval = dev->info->xmii_ctrl1; - const u16 *regs = dev->info->regs; - bool gbit = false; - u8 data8; - bool val; - - ksz_pread8(dev, port, regs[P_XMII_CTRL_1], &data8); - - val = FIELD_GET(P_GMII_1GBIT_M, data8); - - if (val == bitval[P_GMII_1GBIT]) - gbit = true; - - return gbit; -} - static int ksz_switch_detect(struct ksz_device *dev) { u8 id1, id2, id4; diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index 347787ef1f00..2c7716d8cf28 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -395,8 +395,6 @@ void ksz_teardown(struct dsa_switch *ds); void ksz_init_mib_timer(struct ksz_device *dev); void ksz_r_mib_stats64(struct ksz_device *dev, int port); void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 state); -bool ksz_get_gbit(struct ksz_device *dev, int port); -phy_interface_t ksz_get_xmii(struct ksz_device *dev, int port, bool gbit); extern const struct ksz_chip_data ksz_switch_chips[]; int ksz_switch_macaddr_get(struct dsa_switch *ds, int port, struct netlink_ext_ack *extack); From ab4b571d44fbc849de7a7f57c232ff074c323704 Mon Sep 17 00:00:00 2001 From: "Bastien Curutchet (Schneider Electric)" Date: Thu, 2 Jul 2026 11:07:28 +0200 Subject: [PATCH 06/10] net: dsa: microchip: move KSZ9477 errata handling to ksz9477.c The KSZ9477 PHY errata is handled from the common ksz_r_mib_stat64(). This errata clearly belongs to the KSZ9477 family so it should be handled from the ksz9477-specific portion of the driver. Create a ksz9477-specific r_mib_stat64() implementation that handles this errata. Remove the errata handling from the common ksz_r_mib_stat64(). Signed-off-by: Bastien Curutchet (Schneider Electric) Link: https://patch.msgid.link/20260702-clean-ksz-4th-v1-6-93441e695fa4@bootlin.com Signed-off-by: Paolo Abeni --- drivers/net/dsa/microchip/ksz9477.c | 24 ++++++++++++-- drivers/net/dsa/microchip/ksz9477.h | 2 -- drivers/net/dsa/microchip/ksz_common.c | 46 -------------------------- drivers/net/dsa/microchip/ksz_common.h | 39 ++++++++++++++++++++++ 4 files changed, 60 insertions(+), 51 deletions(-) diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c index ad6748905e08..a831eb884ce4 100644 --- a/drivers/net/dsa/microchip/ksz9477.c +++ b/drivers/net/dsa/microchip/ksz9477.c @@ -483,8 +483,8 @@ static int ksz9477_half_duplex_monitor(struct ksz_device *dev, int port, return ret; } -int ksz9477_errata_monitor(struct ksz_device *dev, int port, - u64 tx_late_col) +static int ksz9477_errata_monitor(struct ksz_device *dev, int port, + u64 tx_late_col) { u8 status; int ret; @@ -502,6 +502,24 @@ int ksz9477_errata_monitor(struct ksz_device *dev, int port, return ret; } +static void ksz9477_r_mib_stats64(struct ksz_device *dev, int port) +{ + struct ksz_stats_raw *raw; + struct ksz_port_mib *mib; + int ret; + + ksz_r_mib_stats64(dev, port); + + if (dev->info->phy_errata_9477 && !ksz_is_sgmii_port(dev, port)) { + mib = &dev->ports[port].mib; + raw = (struct ksz_stats_raw *)mib->counters; + + ret = ksz9477_errata_monitor(dev, port, raw->tx_late_col); + if (ret) + dev_err(dev->dev, "Failed to monitor transmission halt\n"); + } +}; + void ksz9477_port_init_cnt(struct ksz_device *dev, int port) { struct ksz_port_mib *mib = &dev->ports[port].mib; @@ -2109,7 +2127,7 @@ const struct ksz_dev_ops ksz9477_dev_ops = { .cfg_port_member = ksz9477_cfg_port_member, .r_mib_cnt = ksz9477_r_mib_cnt, .r_mib_pkt = ksz9477_r_mib_pkt, - .r_mib_stat64 = ksz_r_mib_stats64, + .r_mib_stat64 = ksz9477_r_mib_stats64, .freeze_mib = ksz9477_freeze_mib, .port_init_cnt = ksz9477_port_init_cnt, .pme_write8 = ksz_write8, diff --git a/drivers/net/dsa/microchip/ksz9477.h b/drivers/net/dsa/microchip/ksz9477.h index 25b74d0af6c5..962174a922a0 100644 --- a/drivers/net/dsa/microchip/ksz9477.h +++ b/drivers/net/dsa/microchip/ksz9477.h @@ -32,8 +32,6 @@ int ksz9477_port_mirror_add(struct dsa_switch *ds, int port, bool ingress, struct netlink_ext_ack *extack); void ksz9477_port_mirror_del(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror); -int ksz9477_errata_monitor(struct ksz_device *dev, int port, - u64 tx_late_col); int ksz9477_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb, void *data); int ksz9477_fdb_add(struct dsa_switch *ds, int port, diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 92fcbd9605c7..b54aea92b67d 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -37,45 +37,6 @@ #define MIB_COUNTER_NUM 0x20 -struct ksz_stats_raw { - u64 rx_hi; - u64 rx_undersize; - u64 rx_fragments; - u64 rx_oversize; - u64 rx_jabbers; - u64 rx_symbol_err; - u64 rx_crc_err; - u64 rx_align_err; - u64 rx_mac_ctrl; - u64 rx_pause; - u64 rx_bcast; - u64 rx_mcast; - u64 rx_ucast; - u64 rx_64_or_less; - u64 rx_65_127; - u64 rx_128_255; - u64 rx_256_511; - u64 rx_512_1023; - u64 rx_1024_1522; - u64 rx_1523_2000; - u64 rx_2001; - u64 tx_hi; - u64 tx_late_col; - u64 tx_pause; - u64 tx_bcast; - u64 tx_mcast; - u64 tx_ucast; - u64 tx_deferred; - u64 tx_total_col; - u64 tx_exc_col; - u64 tx_single_col; - u64 tx_mult_col; - u64 rx_total; - u64 tx_total; - u64 rx_discards; - u64 tx_discards; -}; - static const struct ksz_mib_names ksz88xx_mib_names[] = { { 0x00, "rx" }, { 0x01, "rx_hi" }, @@ -1976,7 +1937,6 @@ void ksz_r_mib_stats64(struct ksz_device *dev, int port) struct rtnl_link_stats64 *stats; struct ksz_stats_raw *raw; struct ksz_port_mib *mib; - int ret; mib = &dev->ports[port].mib; stats = &mib->stats64; @@ -2018,12 +1978,6 @@ void ksz_r_mib_stats64(struct ksz_device *dev, int port) pstats->rx_pause_frames = raw->rx_pause; spin_unlock(&mib->stats64_lock); - - if (dev->info->phy_errata_9477 && !ksz_is_sgmii_port(dev, port)) { - ret = ksz9477_errata_monitor(dev, port, raw->tx_late_col); - if (ret) - dev_err(dev->dev, "Failed to monitor transmission halt\n"); - } } void ksz_get_stats64(struct dsa_switch *ds, int port, diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index 2c7716d8cf28..245329b6e0e9 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -40,6 +40,45 @@ struct vlan_table { u32 table[3]; }; +struct ksz_stats_raw { + u64 rx_hi; + u64 rx_undersize; + u64 rx_fragments; + u64 rx_oversize; + u64 rx_jabbers; + u64 rx_symbol_err; + u64 rx_crc_err; + u64 rx_align_err; + u64 rx_mac_ctrl; + u64 rx_pause; + u64 rx_bcast; + u64 rx_mcast; + u64 rx_ucast; + u64 rx_64_or_less; + u64 rx_65_127; + u64 rx_128_255; + u64 rx_256_511; + u64 rx_512_1023; + u64 rx_1024_1522; + u64 rx_1523_2000; + u64 rx_2001; + u64 tx_hi; + u64 tx_late_col; + u64 tx_pause; + u64 tx_bcast; + u64 tx_mcast; + u64 tx_ucast; + u64 tx_deferred; + u64 tx_total_col; + u64 tx_exc_col; + u64 tx_single_col; + u64 tx_mult_col; + u64 rx_total; + u64 tx_total; + u64 rx_discards; + u64 tx_discards; +}; + struct ksz_port_mib { struct mutex cnt_mutex; /* structure access */ u8 cnt_ptr; From 610106257cd7cd1ba7dc014796f515a7bd3a480f Mon Sep 17 00:00:00 2001 From: "Bastien Curutchet (Schneider Electric)" Date: Thu, 2 Jul 2026 11:07:29 +0200 Subject: [PATCH 07/10] net: dsa: microchip: handle KSZ8-specific tc setup in ksz8.c The setup of QDISC_ETS isn't the same for the KSZ8 switches than for the other switches. It leads to is_ksz8() branches in the common code. Move the KSZ8-specific portions into ksz8.c by creating two setup_tc() functions, one dedicated to the KSZ87xx family that only handles the TC_SETUP_QDISC_CBS case, one for the rest of the KSZ8 that handles both TC_SETUP_QDISC_CBS and TC_SETUP_QDISC_ETS cases. It remains some is_kszXXXX() branches because inside the ksz88xx family, only the ksz88x3 switches support QDISC_ETS. Signed-off-by: Bastien Curutchet (Schneider Electric) Link: https://patch.msgid.link/20260702-clean-ksz-4th-v1-7-93441e695fa4@bootlin.com Signed-off-by: Paolo Abeni --- drivers/net/dsa/microchip/ksz8.c | 159 ++++++++++++++++++++++++- drivers/net/dsa/microchip/ksz_common.c | 124 ++----------------- drivers/net/dsa/microchip/ksz_common.h | 7 ++ 3 files changed, 171 insertions(+), 119 deletions(-) diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c index ad7c7a6e1d31..472cc62ea747 100644 --- a/drivers/net/dsa/microchip/ksz8.c +++ b/drivers/net/dsa/microchip/ksz8.c @@ -1782,6 +1782,159 @@ static void ksz8_port_mirror_del(struct dsa_switch *ds, int port, PORT_MIRROR_SNIFFER, false); } +static u8 ksz8463_tc_ctrl(int port, int queue) +{ + u8 reg; + + reg = 0xC8 + port * 4; + reg += ((3 - queue) / 2) * 2; + reg++; + reg -= (queue & 1); + return reg; +} + +/** + * ksz88x3_tc_ets_add - Configure ETS (Enhanced Transmission Selection) + * for a port on KSZ88x3 switch + * @dev: Pointer to the KSZ switch device structure + * @port: Port number to configure + * @p: Pointer to offload replace parameters describing ETS bands and mapping + * + * The KSZ88x3 supports two scheduling modes: Strict Priority and + * Weighted Fair Queuing (WFQ). Both modes have fixed behavior: + * - No configurable queue-to-priority mapping + * - No weight adjustment in WFQ mode + * + * This function configures the switch to use strict priority mode by + * clearing the WFQ enable bit for all queues associated with ETS bands. + * If strict priority is not explicitly requested, the switch will default + * to WFQ mode. + * + * Return: 0 on success, or a negative error code on failure + */ +static int ksz88x3_tc_ets_add(struct ksz_device *dev, int port, + struct tc_ets_qopt_offload_replace_params *p) +{ + int ret, band; + + /* Only strict priority mode is supported for now. + * WFQ is implicitly enabled when strict mode is disabled. + */ + for (band = 0; band < p->bands; band++) { + int queue = ksz_ets_band_to_queue(p, band); + u8 reg; + + /* Calculate TXQ Split Control register address for this + * port/queue + */ + reg = KSZ8873_TXQ_SPLIT_CTRL_REG(port, queue); + if (ksz_is_ksz8463(dev)) + reg = ksz8463_tc_ctrl(port, queue); + + /* Clear WFQ enable bit to select strict priority scheduling */ + ret = ksz_rmw8(dev, reg, KSZ8873_TXQ_WFQ_ENABLE, 0); + if (ret) + return ret; + } + + return 0; +} + +/** + * ksz88x3_tc_ets_del - Reset ETS (Enhanced Transmission Selection) config + * for a port on KSZ88x3 switch + * @dev: Pointer to the KSZ switch device structure + * @port: Port number to reset + * + * The KSZ88x3 supports only fixed scheduling modes: Strict Priority or + * Weighted Fair Queuing (WFQ), with no reconfiguration of weights or + * queue mapping. This function resets the port’s scheduling mode to + * the default, which is WFQ, by enabling the WFQ bit for all queues. + * + * Return: 0 on success, or a negative error code on failure + */ +static int ksz88x3_tc_ets_del(struct ksz_device *dev, int port) +{ + int ret, queue; + + /* Iterate over all transmit queues for this port */ + for (queue = 0; queue < dev->info->num_tx_queues; queue++) { + u8 reg; + + /* Calculate TXQ Split Control register address for this + * port/queue + */ + reg = KSZ8873_TXQ_SPLIT_CTRL_REG(port, queue); + if (ksz_is_ksz8463(dev)) + reg = ksz8463_tc_ctrl(port, queue); + + /* Set WFQ enable bit to revert back to default scheduling + * mode + */ + ret = ksz_rmw8(dev, reg, KSZ8873_TXQ_WFQ_ENABLE, + KSZ8873_TXQ_WFQ_ENABLE); + if (ret) + return ret; + } + + return 0; +} + +static int ksz8_tc_setup_qdisc_ets(struct dsa_switch *ds, int port, + struct tc_ets_qopt_offload *qopt) +{ + struct ksz_device *dev = ds->priv; + int ret; + + if (!(ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev))) + return -EOPNOTSUPP; + + if (qopt->parent != TC_H_ROOT) { + dev_err(dev->dev, "Parent should be \"root\"\n"); + return -EOPNOTSUPP; + } + + switch (qopt->command) { + case TC_ETS_REPLACE: + ret = ksz_tc_ets_validate(dev, port, &qopt->replace_params); + if (ret) + return ret; + + return ksz88x3_tc_ets_add(dev, port, &qopt->replace_params); + case TC_ETS_DESTROY: + return ksz88x3_tc_ets_del(dev, port); + case TC_ETS_STATS: + case TC_ETS_GRAFT: + return -EOPNOTSUPP; + } + + return -EOPNOTSUPP; +} + +static int ksz87xx_setup_tc(struct dsa_switch *ds, int port, + enum tc_setup_type type, void *type_data) +{ + switch (type) { + case TC_SETUP_QDISC_CBS: + return ksz_setup_tc_cbs(ds, port, type_data); + default: + return -EOPNOTSUPP; + } +} + +static int ksz8_setup_tc(struct dsa_switch *ds, int port, + enum tc_setup_type type, void *type_data) +{ + switch (type) { + case TC_SETUP_QDISC_CBS: + return ksz_setup_tc_cbs(ds, port, type_data); + case TC_SETUP_QDISC_ETS: + return ksz8_tc_setup_qdisc_ets(ds, port, type_data); + default: + return -EOPNOTSUPP; + } +} + static void ksz8795_cpu_interface_select(struct ksz_device *dev, int port) { struct ksz_port *p = &dev->ports[port]; @@ -2645,7 +2798,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, - .port_setup_tc = ksz_setup_tc, + .port_setup_tc = ksz8_setup_tc, .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, @@ -2695,7 +2848,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, - .port_setup_tc = ksz_setup_tc, + .port_setup_tc = ksz87xx_setup_tc, .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, @@ -2748,7 +2901,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, - .port_setup_tc = ksz_setup_tc, + .port_setup_tc = ksz8_setup_tc, .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/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index b54aea92b67d..2846041d97a5 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -3097,8 +3097,8 @@ static int ksz_setup_tc_mode(struct ksz_device *dev, int port, u8 scheduler, FIELD_PREP(MTI_SHAPING_M, shaper)); } -static int ksz_setup_tc_cbs(struct dsa_switch *ds, int port, - struct tc_cbs_qopt_offload *qopt) +int ksz_setup_tc_cbs(struct dsa_switch *ds, int port, + struct tc_cbs_qopt_offload *qopt) { struct ksz_device *dev = ds->priv; int ret; @@ -3163,8 +3163,8 @@ static int ksz_disable_egress_rate_limit(struct ksz_device *dev, int port) return 0; } -static int ksz_ets_band_to_queue(struct tc_ets_qopt_offload_replace_params *p, - int band) +int ksz_ets_band_to_queue(struct tc_ets_qopt_offload_replace_params *p, + int band) { /* Compared to queues, bands prioritize packets differently. In strict * priority mode, the lowest priority is assigned to Queue 0 while the @@ -3173,104 +3173,6 @@ static int ksz_ets_band_to_queue(struct tc_ets_qopt_offload_replace_params *p, return p->bands - 1 - band; } -static u8 ksz8463_tc_ctrl(int port, int queue) -{ - u8 reg; - - reg = 0xC8 + port * 4; - reg += ((3 - queue) / 2) * 2; - reg++; - reg -= (queue & 1); - return reg; -} - -/** - * ksz88x3_tc_ets_add - Configure ETS (Enhanced Transmission Selection) - * for a port on KSZ88x3 switch - * @dev: Pointer to the KSZ switch device structure - * @port: Port number to configure - * @p: Pointer to offload replace parameters describing ETS bands and mapping - * - * The KSZ88x3 supports two scheduling modes: Strict Priority and - * Weighted Fair Queuing (WFQ). Both modes have fixed behavior: - * - No configurable queue-to-priority mapping - * - No weight adjustment in WFQ mode - * - * This function configures the switch to use strict priority mode by - * clearing the WFQ enable bit for all queues associated with ETS bands. - * If strict priority is not explicitly requested, the switch will default - * to WFQ mode. - * - * Return: 0 on success, or a negative error code on failure - */ -static int ksz88x3_tc_ets_add(struct ksz_device *dev, int port, - struct tc_ets_qopt_offload_replace_params *p) -{ - int ret, band; - - /* Only strict priority mode is supported for now. - * WFQ is implicitly enabled when strict mode is disabled. - */ - for (band = 0; band < p->bands; band++) { - int queue = ksz_ets_band_to_queue(p, band); - u8 reg; - - /* Calculate TXQ Split Control register address for this - * port/queue - */ - reg = KSZ8873_TXQ_SPLIT_CTRL_REG(port, queue); - if (ksz_is_ksz8463(dev)) - reg = ksz8463_tc_ctrl(port, queue); - - /* Clear WFQ enable bit to select strict priority scheduling */ - ret = ksz_rmw8(dev, reg, KSZ8873_TXQ_WFQ_ENABLE, 0); - if (ret) - return ret; - } - - return 0; -} - -/** - * ksz88x3_tc_ets_del - Reset ETS (Enhanced Transmission Selection) config - * for a port on KSZ88x3 switch - * @dev: Pointer to the KSZ switch device structure - * @port: Port number to reset - * - * The KSZ88x3 supports only fixed scheduling modes: Strict Priority or - * Weighted Fair Queuing (WFQ), with no reconfiguration of weights or - * queue mapping. This function resets the port’s scheduling mode to - * the default, which is WFQ, by enabling the WFQ bit for all queues. - * - * Return: 0 on success, or a negative error code on failure - */ -static int ksz88x3_tc_ets_del(struct ksz_device *dev, int port) -{ - int ret, queue; - - /* Iterate over all transmit queues for this port */ - for (queue = 0; queue < dev->info->num_tx_queues; queue++) { - u8 reg; - - /* Calculate TXQ Split Control register address for this - * port/queue - */ - reg = KSZ8873_TXQ_SPLIT_CTRL_REG(port, queue); - if (ksz_is_ksz8463(dev)) - reg = ksz8463_tc_ctrl(port, queue); - - /* Set WFQ enable bit to revert back to default scheduling - * mode - */ - ret = ksz_rmw8(dev, reg, KSZ8873_TXQ_WFQ_ENABLE, - KSZ8873_TXQ_WFQ_ENABLE); - if (ret) - return ret; - } - - return 0; -} - static int ksz_queue_set_strict(struct ksz_device *dev, int port, int queue) { int ret; @@ -3363,8 +3265,8 @@ static int ksz_tc_ets_del(struct ksz_device *dev, int port) return ksz9477_set_default_prio_queue_mapping(dev, port); } -static int ksz_tc_ets_validate(struct ksz_device *dev, int port, - struct tc_ets_qopt_offload_replace_params *p) +int ksz_tc_ets_validate(struct ksz_device *dev, int port, + struct tc_ets_qopt_offload_replace_params *p) { int band; @@ -3405,9 +3307,6 @@ static int ksz_tc_setup_qdisc_ets(struct dsa_switch *ds, int port, struct ksz_device *dev = ds->priv; int ret; - if (is_ksz8(dev) && !(ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev))) - return -EOPNOTSUPP; - if (qopt->parent != TC_H_ROOT) { dev_err(dev->dev, "Parent should be \"root\"\n"); return -EOPNOTSUPP; @@ -3419,16 +3318,9 @@ static int ksz_tc_setup_qdisc_ets(struct dsa_switch *ds, int port, if (ret) return ret; - if (ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev)) - return ksz88x3_tc_ets_add(dev, port, - &qopt->replace_params); - else - return ksz_tc_ets_add(dev, port, &qopt->replace_params); + return ksz_tc_ets_add(dev, port, &qopt->replace_params); case TC_ETS_DESTROY: - if (ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev)) - return ksz88x3_tc_ets_del(dev, port); - else - return ksz_tc_ets_del(dev, port); + return ksz_tc_ets_del(dev, port); case TC_ETS_STATS: case TC_ETS_GRAFT: return -EOPNOTSUPP; diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index 245329b6e0e9..7aa4a69d06ef 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -481,6 +482,12 @@ void ksz_phylink_mac_link_down(struct phylink_config *config, int ksz_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_keee *e); +int ksz_ets_band_to_queue(struct tc_ets_qopt_offload_replace_params *p, + int band); +int ksz_setup_tc_cbs(struct dsa_switch *ds, int port, + struct tc_cbs_qopt_offload *qopt); +int ksz_tc_ets_validate(struct ksz_device *dev, int port, + struct tc_ets_qopt_offload_replace_params *p); int ksz_setup_tc(struct dsa_switch *ds, int port, enum tc_setup_type type, void *type_data); From e8de229a62ec3d004fb72246f77085f0d15c9068 Mon Sep 17 00:00:00 2001 From: "Bastien Curutchet (Schneider Electric)" Date: Thu, 2 Jul 2026 11:07:30 +0200 Subject: [PATCH 08/10] net: dsa: microchip: move ksz9477_set_default_prio_queue_mapping() to ksz9477.c ksz9477_set_default_prio_queue_mapping() dictates a KSZ9477-specific behavior but is defined in the common section of the code. Move its definition to the KSZ9477-specific area. Signed-off-by: Bastien Curutchet (Schneider Electric) Link: https://patch.msgid.link/20260702-clean-ksz-4th-v1-8-93441e695fa4@bootlin.com Signed-off-by: Paolo Abeni --- drivers/net/dsa/microchip/ksz9477.c | 23 +++++++++++++++++++++++ drivers/net/dsa/microchip/ksz9477.h | 1 + drivers/net/dsa/microchip/ksz_common.c | 22 ---------------------- drivers/net/dsa/microchip/ksz_common.h | 1 - 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c index a831eb884ce4..691b9b18c707 100644 --- a/drivers/net/dsa/microchip/ksz9477.c +++ b/drivers/net/dsa/microchip/ksz9477.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "ksz9477_reg.h" @@ -1410,6 +1411,28 @@ static void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port) ksz_pwrite8(dev, port, regs[REG_PORT_PME_CTRL], 0); } +int ksz9477_set_default_prio_queue_mapping(struct ksz_device *dev, int port) +{ + u32 queue_map = 0; + int ipm; + + for (ipm = 0; ipm < dev->info->num_ipms; ipm++) { + int queue; + + /* Traffic Type (TT) is corresponding to the Internal Priority + * Map (IPM) in the switch. Traffic Class (TC) is + * corresponding to the queue in the switch. + */ + queue = ieee8021q_tt_to_tc(ipm, dev->info->num_tx_queues); + if (queue < 0) + return queue; + + queue_map |= queue << (ipm * KSZ9477_PORT_TC_MAP_S); + } + + return ksz_pwrite32(dev, port, KSZ9477_PORT_MRI_TC_MAP__4, queue_map); +} + static int ksz9477_dsa_port_setup(struct dsa_switch *ds, int port) { struct ksz_device *dev = ds->priv; diff --git a/drivers/net/dsa/microchip/ksz9477.h b/drivers/net/dsa/microchip/ksz9477.h index 962174a922a0..a84c000000e6 100644 --- a/drivers/net/dsa/microchip/ksz9477.h +++ b/drivers/net/dsa/microchip/ksz9477.h @@ -44,6 +44,7 @@ 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); +int ksz9477_set_default_prio_queue_mapping(struct ksz_device *dev, int port); 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 2846041d97a5..4f6f5526c26b 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -2753,28 +2753,6 @@ void ksz_port_bridge_leave(struct dsa_switch *ds, int port, */ } -int ksz9477_set_default_prio_queue_mapping(struct ksz_device *dev, int port) -{ - u32 queue_map = 0; - int ipm; - - for (ipm = 0; ipm < dev->info->num_ipms; ipm++) { - int queue; - - /* Traffic Type (TT) is corresponding to the Internal Priority - * Map (IPM) in the switch. Traffic Class (TC) is - * corresponding to the queue in the switch. - */ - queue = ieee8021q_tt_to_tc(ipm, dev->info->num_tx_queues); - if (queue < 0) - return queue; - - queue_map |= queue << (ipm * KSZ9477_PORT_TC_MAP_S); - } - - return ksz_pwrite32(dev, port, KSZ9477_PORT_MRI_TC_MAP__4, queue_map); -} - void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) { 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 7aa4a69d06ef..029080838237 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -512,7 +512,6 @@ int ksz_pirq_setup(struct ksz_device *dev, u8 p); int ksz_girq_setup(struct ksz_device *dev); void ksz_irq_free(struct ksz_irq *kirq); int ksz_parse_drive_strength(struct ksz_device *dev); -int ksz9477_set_default_prio_queue_mapping(struct ksz_device *dev, int port); /* Common register access functions */ static inline struct regmap *ksz_regmap_8(struct ksz_device *dev) From da466bf62b01fdbbe4d2abeacec654a6e42b9e36 Mon Sep 17 00:00:00 2001 From: "Bastien Curutchet (Schneider Electric)" Date: Thu, 2 Jul 2026 11:07:31 +0200 Subject: [PATCH 09/10] net: dsa: microchip: rename ksz9477_drive_strength_write() ksz9477_drive_strength_write() isn't used for the KSZ9477-family only. It's also used for the KSZ87xx chip variants. This function name is misleading. Rename it ksz_drive_strength_write(). Signed-off-by: Bastien Curutchet (Schneider Electric) Link: https://patch.msgid.link/20260702-clean-ksz-4th-v1-9-93441e695fa4@bootlin.com Signed-off-by: Paolo Abeni --- drivers/net/dsa/microchip/ksz_common.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 4f6f5526c26b..15ed139564cb 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -3838,8 +3838,8 @@ static void ksz_drive_strength_error(struct ksz_device *dev, } /** - * ksz9477_drive_strength_write() - Set the drive strength for specific KSZ9477 - * chip variants. + * ksz_drive_strength_write() - Set the drive strength for specific KSZ9477 + * and the KSZ87xx chip variants. * @dev: ksz device * @props: Array of drive strength properties to be applied * @num_props: Number of properties in the array @@ -3850,9 +3850,9 @@ static void ksz_drive_strength_error(struct ksz_device *dev, * * Return: 0 on successful configuration, a negative error code on failure. */ -static int ksz9477_drive_strength_write(struct ksz_device *dev, - struct ksz_driver_strength_prop *props, - int num_props) +static int ksz_drive_strength_write(struct ksz_device *dev, + struct ksz_driver_strength_prop *props, + int num_props) { size_t array_size = ARRAY_SIZE(ksz9477_drive_strengths); int i, ret, reg; @@ -3998,8 +3998,8 @@ int ksz_parse_drive_strength(struct ksz_device *dev) case KSZ9896_CHIP_ID: case KSZ9897_CHIP_ID: case LAN9646_CHIP_ID: - return ksz9477_drive_strength_write(dev, of_props, - ARRAY_SIZE(of_props)); + return ksz_drive_strength_write(dev, of_props, + ARRAY_SIZE(of_props)); default: for (i = 0; i < ARRAY_SIZE(of_props); i++) { if (of_props[i].value == -1) From 90c676dd4bb61e63a5e27522d32799b5667021ad Mon Sep 17 00:00:00 2001 From: "Bastien Curutchet (Schneider Electric)" Date: Thu, 2 Jul 2026 11:07:32 +0200 Subject: [PATCH 10/10] net: dsa: microchip: move the drive strength config out of the common section The drive strength configuration is done during the setup of all switches through a common function that then has specific behavior depending on the switch identity. Split the common configuration in two functions: one is dedicated to the KSZ8 family, the other is dedicated to the KSZ9477 family. Remove the drive strength configuration from the lan937x_setup since the LAN937x family doesn't support it. Signed-off-by: Bastien Curutchet (Schneider Electric) Link: https://patch.msgid.link/20260702-clean-ksz-4th-v1-10-93441e695fa4@bootlin.com Signed-off-by: Paolo Abeni --- drivers/net/dsa/microchip/ksz8.c | 127 ++++++++++++++++- drivers/net/dsa/microchip/ksz9477.c | 54 ++++++- drivers/net/dsa/microchip/ksz_common.c | 171 ++--------------------- drivers/net/dsa/microchip/ksz_common.h | 32 ++++- drivers/net/dsa/microchip/lan937x_main.c | 4 - 5 files changed, 218 insertions(+), 170 deletions(-) diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c index 472cc62ea747..c4c769028a20 100644 --- a/drivers/net/dsa/microchip/ksz8.c +++ b/drivers/net/dsa/microchip/ksz8.c @@ -36,6 +36,15 @@ #include "ksz8_reg.h" #include "ksz8.h" +/* ksz88x3_drive_strengths - Drive strength mapping for KSZ8863, KSZ8873, .. + * variants. + * This values are documented in KSZ8873 and KSZ8863 datasheets. + */ +static const struct ksz_drive_strength ksz88x3_drive_strengths[] = { + { 0, 8000 }, + { KSZ8873_DRIVE_STRENGTH_16MA, 16000 }, +}; + struct ksz88xx_stats_raw { u64 rx; u64 rx_hi; @@ -2291,6 +2300,122 @@ static void ksz88xx_r_mib_stats64(struct ksz_device *dev, int port) spin_unlock(&mib->stats64_lock); } +/** + * ksz88x3_drive_strength_write() - Set the drive strength configuration for + * KSZ8863 compatible chip variants. + * @dev: ksz device + * @props: Array of drive strength properties to be set + * @num_props: Number of properties in the array + * + * This function applies the specified drive strength settings to KSZ88X3 chip + * variants (KSZ8873, KSZ8863). + * It ensures the configurations align with what the chip variant supports and + * warns or errors out on unsupported settings. + * + * Return: 0 on success, error code otherwise + */ +static int ksz88x3_drive_strength_write(struct ksz_device *dev, + struct ksz_driver_strength_prop *props, + int num_props) +{ + size_t array_size = ARRAY_SIZE(ksz88x3_drive_strengths); + int microamp; + int i, ret; + + for (i = 0; i < num_props; i++) { + if (props[i].value == -1 || i == KSZ_DRIVER_STRENGTH_IO) + continue; + + dev_warn(dev->dev, "%s is not supported by this chip variant\n", + props[i].name); + } + + microamp = props[KSZ_DRIVER_STRENGTH_IO].value; + ret = ksz_drive_strength_to_reg(ksz88x3_drive_strengths, array_size, + microamp); + if (ret < 0) { + ksz_drive_strength_error(dev, ksz88x3_drive_strengths, + array_size, microamp); + return ret; + } + + return ksz_rmw8(dev, KSZ8873_REG_GLOBAL_CTRL_12, + KSZ8873_DRIVE_STRENGTH_16MA, ret); +} + +/** + * ksz8_parse_drive_strength() - Extract and apply drive strength configurations + * from device tree properties. + * @dev: ksz device + * + * This function reads the specified drive strength properties from the + * device tree, validates against the supported chip variants, and sets + * them accordingly. An error should be critical here, as the drive strength + * settings are crucial for EMI compliance. + * + * Return: 0 on success, error code otherwise + */ +static int ksz8_parse_drive_strength(struct ksz_device *dev) +{ + struct ksz_driver_strength_prop of_props[] = { + [KSZ_DRIVER_STRENGTH_HI] = { + .name = "microchip,hi-drive-strength-microamp", + .offset = SW_HI_SPEED_DRIVE_STRENGTH_S, + .value = -1, + }, + [KSZ_DRIVER_STRENGTH_LO] = { + .name = "microchip,lo-drive-strength-microamp", + .offset = SW_LO_SPEED_DRIVE_STRENGTH_S, + .value = -1, + }, + [KSZ_DRIVER_STRENGTH_IO] = { + .name = "microchip,io-drive-strength-microamp", + .offset = 0, /* don't care */ + .value = -1, + }, + }; + struct device_node *np = dev->dev->of_node; + bool have_any_prop = false; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(of_props); i++) { + ret = of_property_read_u32(np, of_props[i].name, + &of_props[i].value); + if (ret && ret != -EINVAL) + dev_warn(dev->dev, "Failed to read %s\n", + of_props[i].name); + if (ret) + continue; + + have_any_prop = true; + } + + if (!have_any_prop) + return 0; + + switch (dev->chip_id) { + case KSZ88X3_CHIP_ID: + return ksz88x3_drive_strength_write(dev, of_props, + ARRAY_SIZE(of_props)); + case KSZ8795_CHIP_ID: + case KSZ8794_CHIP_ID: + case KSZ8765_CHIP_ID: + return ksz_drive_strength_write(dev, of_props, + ARRAY_SIZE(of_props)); + default: + /* KSZ8864, KSZ8895 */ + for (i = 0; i < ARRAY_SIZE(of_props); i++) { + if (of_props[i].value == -1) + continue; + + dev_warn(dev->dev, "%s is not supported by this chip variant\n", + of_props[i].name); + } + } + + return 0; +} + static int ksz8_setup(struct dsa_switch *ds) { struct ksz_device *dev = ds->priv; @@ -2313,7 +2438,7 @@ static int ksz8_setup(struct dsa_switch *ds) return ret; } - ret = ksz_parse_drive_strength(dev); + ret = ksz8_parse_drive_strength(dev); if (ret) return ret; diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c index 691b9b18c707..3ee995545c57 100644 --- a/drivers/net/dsa/microchip/ksz9477.c +++ b/drivers/net/dsa/microchip/ksz9477.c @@ -1615,6 +1615,58 @@ int ksz9477_enable_stp_addr(struct ksz_device *dev) return 0; } +/** + * ksz9477_parse_drive_strength() - Extract and apply drive strength + * configurations from device tree properties. + * @dev: ksz device + * + * This function reads the specified drive strength properties from the + * device tree, validates against the supported chip variants, and sets + * them accordingly. An error should be critical here, as the drive strength + * settings are crucial for EMI compliance. + * + * Return: 0 on success, error code otherwise + */ +static int ksz9477_parse_drive_strength(struct ksz_device *dev) +{ + struct ksz_driver_strength_prop of_props[] = { + [KSZ_DRIVER_STRENGTH_HI] = { + .name = "microchip,hi-drive-strength-microamp", + .offset = SW_HI_SPEED_DRIVE_STRENGTH_S, + .value = -1, + }, + [KSZ_DRIVER_STRENGTH_LO] = { + .name = "microchip,lo-drive-strength-microamp", + .offset = SW_LO_SPEED_DRIVE_STRENGTH_S, + .value = -1, + }, + [KSZ_DRIVER_STRENGTH_IO] = { + .name = "microchip,io-drive-strength-microamp", + .offset = 0, /* don't care */ + .value = -1, + }, + }; + struct device_node *np = dev->dev->of_node; + bool have_any_prop = false; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(of_props); i++) { + ret = of_property_read_u32(np, of_props[i].name, + &of_props[i].value); + if (ret && ret != -EINVAL) + dev_warn(dev->dev, "Failed to read %s\n", + of_props[i].name); + if (ret) + continue; + + have_any_prop = true; + } + + if (!have_any_prop) + return 0; + + return ksz_drive_strength_write(dev, of_props, ARRAY_SIZE(of_props)); +} static int ksz9477_setup(struct dsa_switch *ds) { struct ksz_device *dev = ds->priv; @@ -1637,7 +1689,7 @@ static int ksz9477_setup(struct dsa_switch *ds) return ret; } - ret = ksz_parse_drive_strength(dev); + ret = ksz9477_parse_drive_strength(dev); if (ret) return ret; diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 15ed139564cb..67ab6ddb9e53 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -113,28 +113,6 @@ static const struct ksz_mib_names ksz9477_mib_names[] = { { 0x83, "tx_discards" }, }; -struct ksz_driver_strength_prop { - const char *name; - int offset; - int value; -}; - -enum ksz_driver_strength_type { - KSZ_DRIVER_STRENGTH_HI, - KSZ_DRIVER_STRENGTH_LO, - KSZ_DRIVER_STRENGTH_IO, -}; - -/** - * struct ksz_drive_strength - drive strength mapping - * @reg_val: register value - * @microamp: microamp value - */ -struct ksz_drive_strength { - u32 reg_val; - u32 microamp; -}; - /* ksz9477_drive_strengths - Drive strength mapping for KSZ9477 variants * * This values are not documented in KSZ9477 variants but confirmed by @@ -170,15 +148,6 @@ static const struct ksz_drive_strength ksz9477_drive_strengths[] = { { SW_DRIVE_STRENGTH_28MA, 28000 }, }; -/* ksz88x3_drive_strengths - Drive strength mapping for KSZ8863, KSZ8873, .. - * variants. - * This values are documented in KSZ8873 and KSZ8863 datasheets. - */ -static const struct ksz_drive_strength ksz88x3_drive_strengths[] = { - { 0, 8000 }, - { KSZ8873_DRIVE_STRENGTH_16MA, 16000 }, -}; - /** * ksz_phylink_mac_disable_tx_lpi() - Callback to signal LPI support (Dummy) * @config: phylink config structure @@ -3785,8 +3754,8 @@ static void ksz_parse_rgmii_delay(struct ksz_device *dev, int port_num, * Returns: If found, the corresponding register value for that drive strength * is returned. Otherwise, -EINVAL is returned indicating an invalid value. */ -static int ksz_drive_strength_to_reg(const struct ksz_drive_strength *array, - size_t array_size, int microamp) +int ksz_drive_strength_to_reg(const struct ksz_drive_strength *array, + size_t array_size, int microamp) { int i; @@ -3809,9 +3778,9 @@ static int ksz_drive_strength_to_reg(const struct ksz_drive_strength *array, * is detected. It lists out all the supported drive strength values for * reference in the error message. */ -static void ksz_drive_strength_error(struct ksz_device *dev, - const struct ksz_drive_strength *array, - size_t array_size, int microamp) +void ksz_drive_strength_error(struct ksz_device *dev, + const struct ksz_drive_strength *array, + size_t array_size, int microamp) { char supported_values[100]; size_t remaining_size; @@ -3850,9 +3819,9 @@ static void ksz_drive_strength_error(struct ksz_device *dev, * * Return: 0 on successful configuration, a negative error code on failure. */ -static int ksz_drive_strength_write(struct ksz_device *dev, - struct ksz_driver_strength_prop *props, - int num_props) +int ksz_drive_strength_write(struct ksz_device *dev, + struct ksz_driver_strength_prop *props, + int num_props) { size_t array_size = ARRAY_SIZE(ksz9477_drive_strengths); int i, ret, reg; @@ -3889,130 +3858,6 @@ static int ksz_drive_strength_write(struct ksz_device *dev, return ksz_rmw8(dev, reg, mask, val); } -/** - * ksz88x3_drive_strength_write() - Set the drive strength configuration for - * KSZ8863 compatible chip variants. - * @dev: ksz device - * @props: Array of drive strength properties to be set - * @num_props: Number of properties in the array - * - * This function applies the specified drive strength settings to KSZ88X3 chip - * variants (KSZ8873, KSZ8863). - * It ensures the configurations align with what the chip variant supports and - * warns or errors out on unsupported settings. - * - * Return: 0 on success, error code otherwise - */ -static int ksz88x3_drive_strength_write(struct ksz_device *dev, - struct ksz_driver_strength_prop *props, - int num_props) -{ - size_t array_size = ARRAY_SIZE(ksz88x3_drive_strengths); - int microamp; - int i, ret; - - for (i = 0; i < num_props; i++) { - if (props[i].value == -1 || i == KSZ_DRIVER_STRENGTH_IO) - continue; - - dev_warn(dev->dev, "%s is not supported by this chip variant\n", - props[i].name); - } - - microamp = props[KSZ_DRIVER_STRENGTH_IO].value; - ret = ksz_drive_strength_to_reg(ksz88x3_drive_strengths, array_size, - microamp); - if (ret < 0) { - ksz_drive_strength_error(dev, ksz88x3_drive_strengths, - array_size, microamp); - return ret; - } - - return ksz_rmw8(dev, KSZ8873_REG_GLOBAL_CTRL_12, - KSZ8873_DRIVE_STRENGTH_16MA, ret); -} - -/** - * ksz_parse_drive_strength() - Extract and apply drive strength configurations - * from device tree properties. - * @dev: ksz device - * - * This function reads the specified drive strength properties from the - * device tree, validates against the supported chip variants, and sets - * them accordingly. An error should be critical here, as the drive strength - * settings are crucial for EMI compliance. - * - * Return: 0 on success, error code otherwise - */ -int ksz_parse_drive_strength(struct ksz_device *dev) -{ - struct ksz_driver_strength_prop of_props[] = { - [KSZ_DRIVER_STRENGTH_HI] = { - .name = "microchip,hi-drive-strength-microamp", - .offset = SW_HI_SPEED_DRIVE_STRENGTH_S, - .value = -1, - }, - [KSZ_DRIVER_STRENGTH_LO] = { - .name = "microchip,lo-drive-strength-microamp", - .offset = SW_LO_SPEED_DRIVE_STRENGTH_S, - .value = -1, - }, - [KSZ_DRIVER_STRENGTH_IO] = { - .name = "microchip,io-drive-strength-microamp", - .offset = 0, /* don't care */ - .value = -1, - }, - }; - struct device_node *np = dev->dev->of_node; - bool have_any_prop = false; - int i, ret; - - for (i = 0; i < ARRAY_SIZE(of_props); i++) { - ret = of_property_read_u32(np, of_props[i].name, - &of_props[i].value); - if (ret && ret != -EINVAL) - dev_warn(dev->dev, "Failed to read %s\n", - of_props[i].name); - if (ret) - continue; - - have_any_prop = true; - } - - if (!have_any_prop) - return 0; - - switch (dev->chip_id) { - case KSZ88X3_CHIP_ID: - return ksz88x3_drive_strength_write(dev, of_props, - ARRAY_SIZE(of_props)); - case KSZ8795_CHIP_ID: - case KSZ8794_CHIP_ID: - case KSZ8765_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 ksz_drive_strength_write(dev, of_props, - ARRAY_SIZE(of_props)); - default: - for (i = 0; i < ARRAY_SIZE(of_props); i++) { - if (of_props[i].value == -1) - continue; - - dev_warn(dev->dev, "%s is not supported by this chip variant\n", - of_props[i].name); - } - } - - return 0; -} - static int ksz8463_configure_straps_spi(struct ksz_device *dev) { struct pinctrl *pinctrl; diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index 029080838237..acaf70e6f393 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -511,7 +511,37 @@ 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); void ksz_irq_free(struct ksz_irq *kirq); -int ksz_parse_drive_strength(struct ksz_device *dev); + +struct ksz_driver_strength_prop { + const char *name; + int offset; + int value; +}; + +enum ksz_driver_strength_type { + KSZ_DRIVER_STRENGTH_HI, + KSZ_DRIVER_STRENGTH_LO, + KSZ_DRIVER_STRENGTH_IO, +}; + +/** + * struct ksz_drive_strength - drive strength mapping + * @reg_val: register value + * @microamp: microamp value + */ +struct ksz_drive_strength { + u32 reg_val; + u32 microamp; +}; + +void ksz_drive_strength_error(struct ksz_device *dev, + const struct ksz_drive_strength *array, + size_t array_size, int microamp); +int ksz_drive_strength_to_reg(const struct ksz_drive_strength *array, + size_t array_size, int microamp); +int ksz_drive_strength_write(struct ksz_device *dev, + struct ksz_driver_strength_prop *props, + int num_props); /* Common register access functions */ static inline struct regmap *ksz_regmap_8(struct ksz_device *dev) diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c index f060fbc4c4f4..86ce3a86705f 100644 --- a/drivers/net/dsa/microchip/lan937x_main.c +++ b/drivers/net/dsa/microchip/lan937x_main.c @@ -787,10 +787,6 @@ static int lan937x_setup(struct dsa_switch *ds) return ret; } - ret = ksz_parse_drive_strength(dev); - if (ret) - return ret; - /* set broadcast storm protection 10% rate */ storm_mask = BROADCAST_STORM_RATE; storm_rate = (BROADCAST_STORM_VALUE * BROADCAST_STORM_PROT_RATE) / 100;