From 61f24c6885d6d509c485b71bdde75b499f5d19b6 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Tue, 20 May 2025 13:30:43 -0700 Subject: [PATCH 01/16] selftests: ncdevmem: make chunking optional Add new -z argument to specify max IOV size. By default, use single large IOV. Signed-off-by: Stanislav Fomichev Reviewed-by: Mina Almasry Signed-off-by: David S. Miller --- .../selftests/drivers/net/hw/ncdevmem.c | 49 +++++++++++-------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/tools/testing/selftests/drivers/net/hw/ncdevmem.c b/tools/testing/selftests/drivers/net/hw/ncdevmem.c index ca723722a810..fc7ba7d71502 100644 --- a/tools/testing/selftests/drivers/net/hw/ncdevmem.c +++ b/tools/testing/selftests/drivers/net/hw/ncdevmem.c @@ -82,6 +82,9 @@ #define MSG_SOCK_DEVMEM 0x2000000 #endif +#define MAX_IOV 1024 + +static size_t max_chunk; static char *server_ip; static char *client_ip; static char *port; @@ -834,10 +837,10 @@ static int do_client(struct memory_buffer *mem) struct sockaddr_in6 server_sin; struct sockaddr_in6 client_sin; struct ynl_sock *ys = NULL; + struct iovec iov[MAX_IOV]; struct msghdr msg = {}; ssize_t line_size = 0; struct cmsghdr *cmsg; - struct iovec iov[2]; char *line = NULL; unsigned long mid; size_t len = 0; @@ -893,27 +896,29 @@ static int do_client(struct memory_buffer *mem) if (line_size < 0) break; - mid = (line_size / 2) + 1; - - iov[0].iov_base = (void *)1; - iov[0].iov_len = mid; - iov[1].iov_base = (void *)(mid + 2); - iov[1].iov_len = line_size - mid; + if (max_chunk) { + msg.msg_iovlen = + (line_size + max_chunk - 1) / max_chunk; + if (msg.msg_iovlen > MAX_IOV) + error(1, 0, + "can't partition %zd bytes into maximum of %d chunks", + line_size, MAX_IOV); - provider->memcpy_to_device(mem, (size_t)iov[0].iov_base, line, - iov[0].iov_len); - provider->memcpy_to_device(mem, (size_t)iov[1].iov_base, - line + iov[0].iov_len, - iov[1].iov_len); + for (int i = 0; i < msg.msg_iovlen; i++) { + iov[i].iov_base = (void *)(i * max_chunk); + iov[i].iov_len = max_chunk; + } - fprintf(stderr, - "read line_size=%ld iov[0].iov_base=%lu, iov[0].iov_len=%lu, iov[1].iov_base=%lu, iov[1].iov_len=%lu\n", - line_size, (unsigned long)iov[0].iov_base, - iov[0].iov_len, (unsigned long)iov[1].iov_base, - iov[1].iov_len); + iov[msg.msg_iovlen - 1].iov_len = + line_size - (msg.msg_iovlen - 1) * max_chunk; + } else { + iov[0].iov_base = 0; + iov[0].iov_len = line_size; + msg.msg_iovlen = 1; + } msg.msg_iov = iov; - msg.msg_iovlen = 2; + provider->memcpy_to_device(mem, 0, line, line_size); msg.msg_control = ctrl_data; msg.msg_controllen = sizeof(ctrl_data); @@ -934,7 +939,8 @@ static int do_client(struct memory_buffer *mem) fprintf(stderr, "sendmsg_ret=%d\n", ret); if (ret != line_size) - error(1, errno, "Did not send all bytes"); + error(1, errno, "Did not send all bytes %d vs %zd", ret, + line_size); wait_compl(socket_fd); } @@ -956,7 +962,7 @@ int main(int argc, char *argv[]) int is_server = 0, opt; int ret; - while ((opt = getopt(argc, argv, "ls:c:p:v:q:t:f:")) != -1) { + while ((opt = getopt(argc, argv, "ls:c:p:v:q:t:f:z:")) != -1) { switch (opt) { case 'l': is_server = 1; @@ -982,6 +988,9 @@ int main(int argc, char *argv[]) case 'f': ifname = optarg; break; + case 'z': + max_chunk = atoi(optarg); + break; case '?': fprintf(stderr, "unknown option: %c\n", optopt); break; -- 2.51.0 From 8ceeef23a3a7b077caa98be713c09a4cdfbd018f Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Tue, 20 May 2025 13:30:44 -0700 Subject: [PATCH 02/16] selftests: ncdevmem: add tx test with multiple IOVs Use prime 3 for length to make offset slowly drift away. Signed-off-by: Stanislav Fomichev Acked-by: Mina Almasry Signed-off-by: David S. Miller --- .../testing/selftests/drivers/net/hw/devmem.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/drivers/net/hw/devmem.py b/tools/testing/selftests/drivers/net/hw/devmem.py index 7fc686cf47a2..d7f6a76eb2b7 100755 --- a/tools/testing/selftests/drivers/net/hw/devmem.py +++ b/tools/testing/selftests/drivers/net/hw/devmem.py @@ -49,12 +49,27 @@ def check_tx(cfg) -> None: ksft_eq(socat.stdout.strip(), "hello\nworld") +@ksft_disruptive +def check_tx_chunks(cfg) -> None: + cfg.require_ipver("6") + require_devmem(cfg) + + port = rand_port() + listen_cmd = f"socat -U - TCP6-LISTEN:{port}" + + with bkg(listen_cmd, exit_wait=True) as socat: + wait_port_listen(port) + cmd(f"echo -e \"hello\\nworld\"| {cfg.bin_remote} -f {cfg.ifname} -s {cfg.addr_v['6']} -p {port} -z 3", host=cfg.remote, shell=True) + + ksft_eq(socat.stdout.strip(), "hello\nworld") + + def main() -> None: with NetDrvEpEnv(__file__) as cfg: cfg.bin_local = path.abspath(path.dirname(__file__) + "/ncdevmem") cfg.bin_remote = cfg.remote.deploy(cfg.bin_local) - ksft_run([check_rx, check_tx], + ksft_run([check_rx, check_tx, check_tx_chunks], args=(cfg, )) ksft_exit() -- 2.51.0 From e45b7196df60a4aef86c3998611c91fcc93d21f3 Mon Sep 17 00:00:00 2001 From: Qiu Yutan Date: Wed, 21 May 2025 10:14:08 +0800 Subject: [PATCH 03/16] net: neigh: use kfree_skb_reason() in neigh_resolve_output() and neigh_connected_output() Replace kfree_skb() used in neigh_resolve_output() and neigh_connected_output() with kfree_skb_reason(). Following new skb drop reason is added: /* failed to fill the device hard header */ SKB_DROP_REASON_NEIGH_HH_FILLFAIL Signed-off-by: Qiu Yutan Signed-off-by: Jiang Kun Reviewed-by: Kuniyuki Iwashima Reviewed-by: Xu Xin Signed-off-by: David S. Miller --- include/net/dropreason-core.h | 3 +++ net/core/neighbour.c | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/include/net/dropreason-core.h b/include/net/dropreason-core.h index bea77934a235..bcf9d7467e1a 100644 --- a/include/net/dropreason-core.h +++ b/include/net/dropreason-core.h @@ -62,6 +62,7 @@ FN(NEIGH_FAILED) \ FN(NEIGH_QUEUEFULL) \ FN(NEIGH_DEAD) \ + FN(NEIGH_HH_FILLFAIL) \ FN(TC_EGRESS) \ FN(SECURITY_HOOK) \ FN(QDISC_DROP) \ @@ -348,6 +349,8 @@ enum skb_drop_reason { SKB_DROP_REASON_NEIGH_QUEUEFULL, /** @SKB_DROP_REASON_NEIGH_DEAD: neigh entry is dead */ SKB_DROP_REASON_NEIGH_DEAD, + /** @SKB_DROP_REASON_NEIGH_HH_FILLFAIL: failed to fill the device hard header */ + SKB_DROP_REASON_NEIGH_HH_FILLFAIL, /** @SKB_DROP_REASON_TC_EGRESS: dropped in TC egress HOOK */ SKB_DROP_REASON_TC_EGRESS, /** @SKB_DROP_REASON_SECURITY_HOOK: dropped due to security HOOK */ diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 254067b719da..a6e2c91ec3e7 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1517,7 +1517,7 @@ out: return rc; out_kfree_skb: rc = -EINVAL; - kfree_skb(skb); + kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_HH_FILLFAIL); goto out; } EXPORT_SYMBOL(neigh_resolve_output); @@ -1541,7 +1541,7 @@ int neigh_connected_output(struct neighbour *neigh, struct sk_buff *skb) err = dev_queue_xmit(skb); else { err = -EINVAL; - kfree_skb(skb); + kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_HH_FILLFAIL); } return err; } -- 2.51.0 From 405b0d610745fb5e84fc2961d9b960abb9f3d107 Mon Sep 17 00:00:00 2001 From: Nikita Zhandarovich Date: Tue, 20 May 2025 14:32:39 +0300 Subject: [PATCH 04/16] net: usb: aqc111: fix error handling of usbnet read calls Syzkaller, courtesy of syzbot, identified an error (see report [1]) in aqc111 driver, caused by incomplete sanitation of usb read calls' results. This problem is quite similar to the one fixed in commit 920a9fa27e78 ("net: asix: add proper error handling of usb read errors"). For instance, usbnet_read_cmd() may read fewer than 'size' bytes, even if the caller expected the full amount, and aqc111_read_cmd() will not check its result properly. As [1] shows, this may lead to MAC address in aqc111_bind() being only partly initialized, triggering KMSAN warnings. Fix the issue by verifying that the number of bytes read is as expected and not less. [1] Partial syzbot report: BUG: KMSAN: uninit-value in is_valid_ether_addr include/linux/etherdevice.h:208 [inline] BUG: KMSAN: uninit-value in usbnet_probe+0x2e57/0x4390 drivers/net/usb/usbnet.c:1830 is_valid_ether_addr include/linux/etherdevice.h:208 [inline] usbnet_probe+0x2e57/0x4390 drivers/net/usb/usbnet.c:1830 usb_probe_interface+0xd01/0x1310 drivers/usb/core/driver.c:396 call_driver_probe drivers/base/dd.c:-1 [inline] really_probe+0x4d1/0xd90 drivers/base/dd.c:658 __driver_probe_device+0x268/0x380 drivers/base/dd.c:800 ... Uninit was stored to memory at: dev_addr_mod+0xb0/0x550 net/core/dev_addr_lists.c:582 __dev_addr_set include/linux/netdevice.h:4874 [inline] eth_hw_addr_set include/linux/etherdevice.h:325 [inline] aqc111_bind+0x35f/0x1150 drivers/net/usb/aqc111.c:717 usbnet_probe+0xbe6/0x4390 drivers/net/usb/usbnet.c:1772 usb_probe_interface+0xd01/0x1310 drivers/usb/core/driver.c:396 ... Uninit was stored to memory at: ether_addr_copy include/linux/etherdevice.h:305 [inline] aqc111_read_perm_mac drivers/net/usb/aqc111.c:663 [inline] aqc111_bind+0x794/0x1150 drivers/net/usb/aqc111.c:713 usbnet_probe+0xbe6/0x4390 drivers/net/usb/usbnet.c:1772 usb_probe_interface+0xd01/0x1310 drivers/usb/core/driver.c:396 call_driver_probe drivers/base/dd.c:-1 [inline] ... Local variable buf.i created at: aqc111_read_perm_mac drivers/net/usb/aqc111.c:656 [inline] aqc111_bind+0x221/0x1150 drivers/net/usb/aqc111.c:713 usbnet_probe+0xbe6/0x4390 drivers/net/usb/usbnet.c:1772 Reported-by: syzbot+3b6b9ff7b80430020c7b@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=3b6b9ff7b80430020c7b Tested-by: syzbot+3b6b9ff7b80430020c7b@syzkaller.appspotmail.com Fixes: df2d59a2ab6c ("net: usb: aqc111: Add support for getting and setting of MAC address") Signed-off-by: Nikita Zhandarovich Link: https://patch.msgid.link/20250520113240.2369438-1-n.zhandarovich@fintech.ru Signed-off-by: Paolo Abeni --- drivers/net/usb/aqc111.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/net/usb/aqc111.c b/drivers/net/usb/aqc111.c index ff5be2cbf17b..453a2cf82753 100644 --- a/drivers/net/usb/aqc111.c +++ b/drivers/net/usb/aqc111.c @@ -30,10 +30,13 @@ static int aqc111_read_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value, ret = usbnet_read_cmd_nopm(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, index, data, size); - if (unlikely(ret < 0)) + if (unlikely(ret < size)) { + ret = ret < 0 ? ret : -ENODATA; + netdev_warn(dev->net, "Failed to read(0x%x) reg index 0x%04x: %d\n", cmd, index, ret); + } return ret; } @@ -46,10 +49,13 @@ static int aqc111_read_cmd(struct usbnet *dev, u8 cmd, u16 value, ret = usbnet_read_cmd(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, index, data, size); - if (unlikely(ret < 0)) + if (unlikely(ret < size)) { + ret = ret < 0 ? ret : -ENODATA; + netdev_warn(dev->net, "Failed to read(0x%x) reg index 0x%04x: %d\n", cmd, index, ret); + } return ret; } -- 2.51.0 From e8c35bfce4c131c8a0777d25d9adfcbb0db8314b Mon Sep 17 00:00:00 2001 From: Tristram Ha Date: Tue, 20 May 2025 16:07:20 -0700 Subject: [PATCH 05/16] net: dsa: microchip: Add SGMII port support to KSZ9477 switch The KSZ9477 switch driver uses the XPCS driver to operate its SGMII port. However there are some hardware bugs in the KSZ9477 SGMII module so workarounds are needed. There was a proposal to update the XPCS driver to accommodate KSZ9477, but the new code is not generic enough to be used by other vendors. It is better to do all these workarounds inside the KSZ9477 driver instead of modifying the XPCS driver. There are 3 hardware issues. The first is the MII_ADVERTISE register needs to be write once after reset for the correct code word to be sent. The XPCS driver disables auto-negotiation first before configuring the SGMII/1000BASE-X mode and then enables it back. The KSZ9477 driver then writes the MII_ADVERTISE register before enabling auto-negotiation. In 1000BASE-X mode the MII_ADVERTISE register will be set, so KSZ9477 driver does not need to write it. The second issue is the MII_BMCR register needs to set the exact speed and duplex mode when running in SGMII mode. During link polling the KSZ9477 will check the speed and duplex mode are different from previous ones and update the MII_BMCR register accordingly. The last issue is 1000BASE-X mode does not work with auto-negotiation on. The cause is the local port hardware does not know the link is up and so network traffic is not forwarded. The workaround is to write 2 additional bits when 1000BASE-X mode is configured. Note the SGMII interrupt in the port cannot be masked. As that interrupt is not handled in the KSZ9477 driver the SGMII interrupt bit will not be set even when the XPCS driver sets it. Signed-off-by: Tristram Ha Reviewed-by: Maxime Chevallier Tested-by: Maxime Chevallier Link: https://patch.msgid.link/20250520230720.23425-1-Tristram.Ha@microchip.com Signed-off-by: Paolo Abeni --- drivers/net/dsa/microchip/Kconfig | 1 + drivers/net/dsa/microchip/ksz9477.c | 194 ++++++++++++++++++++++++- drivers/net/dsa/microchip/ksz9477.h | 4 +- drivers/net/dsa/microchip/ksz_common.c | 37 ++++- drivers/net/dsa/microchip/ksz_common.h | 23 ++- 5 files changed, 252 insertions(+), 7 deletions(-) diff --git a/drivers/net/dsa/microchip/Kconfig b/drivers/net/dsa/microchip/Kconfig index 12a86585a77f..c71d3fd5dfeb 100644 --- a/drivers/net/dsa/microchip/Kconfig +++ b/drivers/net/dsa/microchip/Kconfig @@ -6,6 +6,7 @@ menuconfig NET_DSA_MICROCHIP_KSZ_COMMON select NET_DSA_TAG_NONE select NET_IEEE8021Q_HELPERS select DCB + select PCS_XPCS help This driver adds support for Microchip KSZ8, KSZ9 and LAN937X series switch chips, being KSZ8863/8873, diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c index 29fe79ea74cd..d747ea1c41a7 100644 --- a/drivers/net/dsa/microchip/ksz9477.c +++ b/drivers/net/dsa/microchip/ksz9477.c @@ -2,7 +2,7 @@ /* * Microchip KSZ9477 switch driver main logic * - * Copyright (C) 2017-2024 Microchip Technology Inc. + * Copyright (C) 2017-2025 Microchip Technology Inc. */ #include @@ -161,6 +161,190 @@ static int ksz9477_wait_alu_sta_ready(struct ksz_device *dev) 10, 1000); } +static void port_sgmii_s(struct ksz_device *dev, uint port, u16 devid, u16 reg) +{ + u32 data; + + data = (devid & MII_MMD_CTRL_DEVAD_MASK) << 16; + data |= reg; + ksz_pwrite32(dev, port, REG_PORT_SGMII_ADDR__4, data); +} + +static void port_sgmii_r(struct ksz_device *dev, uint port, u16 devid, u16 reg, + u16 *buf) +{ + port_sgmii_s(dev, port, devid, reg); + ksz_pread16(dev, port, REG_PORT_SGMII_DATA__4 + 2, buf); +} + +static void port_sgmii_w(struct ksz_device *dev, uint port, u16 devid, u16 reg, + u16 buf) +{ + port_sgmii_s(dev, port, devid, reg); + ksz_pwrite32(dev, port, REG_PORT_SGMII_DATA__4, buf); +} + +static int ksz9477_pcs_read(struct mii_bus *bus, int phy, int mmd, int reg) +{ + struct ksz_device *dev = bus->priv; + int port = ksz_get_sgmii_port(dev); + u16 val; + + port_sgmii_r(dev, port, mmd, reg, &val); + + /* Simulate a value to activate special code in the XPCS driver if + * supported. + */ + if (mmd == MDIO_MMD_PMAPMD) { + if (reg == MDIO_DEVID1) + val = 0x9477; + else if (reg == MDIO_DEVID2) + val = 0x22 << 10; + } else if (mmd == MDIO_MMD_VEND2) { + struct ksz_port *p = &dev->ports[port]; + + /* Need to update MII_BMCR register with the exact speed and + * duplex mode when running in SGMII mode and this register is + * used to detect connected speed in that mode. + */ + if (reg == MMD_SR_MII_AUTO_NEG_STATUS) { + int duplex, speed; + + if (val & SR_MII_STAT_LINK_UP) { + speed = (val >> SR_MII_STAT_S) & SR_MII_STAT_M; + if (speed == SR_MII_STAT_1000_MBPS) + speed = SPEED_1000; + else if (speed == SR_MII_STAT_100_MBPS) + speed = SPEED_100; + else + speed = SPEED_10; + + if (val & SR_MII_STAT_FULL_DUPLEX) + duplex = DUPLEX_FULL; + else + duplex = DUPLEX_HALF; + + if (!p->phydev.link || + p->phydev.speed != speed || + p->phydev.duplex != duplex) { + u16 ctrl; + + p->phydev.link = 1; + p->phydev.speed = speed; + p->phydev.duplex = duplex; + port_sgmii_r(dev, port, mmd, MII_BMCR, + &ctrl); + ctrl &= BMCR_ANENABLE; + ctrl |= mii_bmcr_encode_fixed(speed, + duplex); + port_sgmii_w(dev, port, mmd, MII_BMCR, + ctrl); + } + } else { + p->phydev.link = 0; + } + } else if (reg == MII_BMSR) { + p->phydev.link = (val & BMSR_LSTATUS); + } + } + + return val; +} + +static int ksz9477_pcs_write(struct mii_bus *bus, int phy, int mmd, int reg, + u16 val) +{ + struct ksz_device *dev = bus->priv; + int port = ksz_get_sgmii_port(dev); + + if (mmd == MDIO_MMD_VEND2) { + struct ksz_port *p = &dev->ports[port]; + + if (reg == MMD_SR_MII_AUTO_NEG_CTRL) { + u16 sgmii_mode = SR_MII_PCS_SGMII << SR_MII_PCS_MODE_S; + + /* Need these bits for 1000BASE-X mode to work with + * AN on. + */ + if (!(val & sgmii_mode)) + val |= SR_MII_SGMII_LINK_UP | + SR_MII_TX_CFG_PHY_MASTER; + + /* SGMII interrupt in the port cannot be masked, so + * make sure interrupt is not enabled as it is not + * handled. + */ + val &= ~SR_MII_AUTO_NEG_COMPLETE_INTR; + } else if (reg == MII_BMCR) { + /* The MII_ADVERTISE register needs to write once + * before doing auto-negotiation for the correct + * config_word to be sent out after reset. + */ + if ((val & BMCR_ANENABLE) && !p->sgmii_adv_write) { + u16 adv; + + /* The SGMII port cannot disable flow control + * so it is better to just advertise symmetric + * pause. + */ + port_sgmii_r(dev, port, mmd, MII_ADVERTISE, + &adv); + adv |= ADVERTISE_1000XPAUSE; + adv &= ~ADVERTISE_1000XPSE_ASYM; + port_sgmii_w(dev, port, mmd, MII_ADVERTISE, + adv); + p->sgmii_adv_write = 1; + } else if (val & BMCR_RESET) { + p->sgmii_adv_write = 0; + } + } else if (reg == MII_ADVERTISE) { + /* XPCS driver writes to this register so there is no + * need to update it for the errata. + */ + p->sgmii_adv_write = 1; + } + } + port_sgmii_w(dev, port, mmd, reg, val); + + return 0; +} + +int ksz9477_pcs_create(struct ksz_device *dev) +{ + /* This chip has a SGMII port. */ + if (ksz_has_sgmii_port(dev)) { + int port = ksz_get_sgmii_port(dev); + struct ksz_port *p = &dev->ports[port]; + struct phylink_pcs *pcs; + struct mii_bus *bus; + int ret; + + bus = devm_mdiobus_alloc(dev->dev); + if (!bus) + return -ENOMEM; + + bus->name = "ksz_pcs_mdio_bus"; + snprintf(bus->id, MII_BUS_ID_SIZE, "%s-pcs", + dev_name(dev->dev)); + bus->read_c45 = &ksz9477_pcs_read; + bus->write_c45 = &ksz9477_pcs_write; + bus->parent = dev->dev; + bus->phy_mask = ~0; + bus->priv = dev; + + ret = devm_mdiobus_register(dev->dev, bus); + if (ret) + return ret; + + pcs = xpcs_create_pcs_mdiodev(bus, 0); + if (IS_ERR(pcs)) + return PTR_ERR(pcs); + p->pcs = pcs; + } + + return 0; +} + int ksz9477_reset_switch(struct ksz_device *dev) { u8 data8; @@ -978,6 +1162,14 @@ void ksz9477_get_caps(struct ksz_device *dev, int port, if (dev->info->gbit_capable[port]) config->mac_capabilities |= MAC_1000FD; + + if (ksz_is_sgmii_port(dev, port)) { + struct ksz_port *p = &dev->ports[port]; + + phy_interface_or(config->supported_interfaces, + config->supported_interfaces, + p->pcs->supported_interfaces); + } } int ksz9477_set_ageing_time(struct ksz_device *dev, unsigned int msecs) diff --git a/drivers/net/dsa/microchip/ksz9477.h b/drivers/net/dsa/microchip/ksz9477.h index d2166b0d881e..0d1a6dfda23e 100644 --- a/drivers/net/dsa/microchip/ksz9477.h +++ b/drivers/net/dsa/microchip/ksz9477.h @@ -2,7 +2,7 @@ /* * Microchip KSZ9477 series Header file * - * Copyright (C) 2017-2022 Microchip Technology Inc. + * Copyright (C) 2017-2025 Microchip Technology Inc. */ #ifndef __KSZ9477_H @@ -97,4 +97,6 @@ void ksz9477_acl_match_process_l2(struct ksz_device *dev, int port, u16 ethtype, u8 *src_mac, u8 *dst_mac, unsigned long cookie, u32 prio); +int ksz9477_pcs_create(struct ksz_device *dev); + #endif diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index f492fa9f6dd4..f424b3d10781 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -2,7 +2,7 @@ /* * Microchip switch driver main logic * - * Copyright (C) 2017-2024 Microchip Technology Inc. + * Copyright (C) 2017-2025 Microchip Technology Inc. */ #include @@ -408,12 +408,29 @@ static void ksz9477_phylink_mac_link_up(struct phylink_config *config, int speed, int duplex, bool tx_pause, bool rx_pause); +static struct phylink_pcs * +ksz_phylink_mac_select_pcs(struct phylink_config *config, + phy_interface_t interface) +{ + struct dsa_port *dp = dsa_phylink_to_port(config); + struct ksz_device *dev = dp->ds->priv; + struct ksz_port *p = &dev->ports[dp->index]; + + if (ksz_is_sgmii_port(dev, dp->index) && + (interface == PHY_INTERFACE_MODE_SGMII || + interface == PHY_INTERFACE_MODE_1000BASEX)) + return p->pcs; + + return NULL; +} + static const struct phylink_mac_ops ksz9477_phylink_mac_ops = { .mac_config = ksz_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, .mac_enable_tx_lpi = ksz_phylink_mac_enable_tx_lpi, + .mac_select_pcs = ksz_phylink_mac_select_pcs, }; static const struct ksz_dev_ops ksz9477_dev_ops = { @@ -451,6 +468,7 @@ static const struct ksz_dev_ops ksz9477_dev_ops = { .reset = ksz9477_reset_switch, .init = ksz9477_switch_init, .exit = ksz9477_switch_exit, + .pcs_create = ksz9477_pcs_create, }; static const struct phylink_mac_ops lan937x_phylink_mac_ops = { @@ -1093,8 +1111,7 @@ static const struct regmap_range ksz9477_valid_regs[] = { regmap_reg_range(0x701b, 0x701b), regmap_reg_range(0x701f, 0x7020), regmap_reg_range(0x7030, 0x7030), - regmap_reg_range(0x7200, 0x7203), - regmap_reg_range(0x7206, 0x7207), + regmap_reg_range(0x7200, 0x7207), regmap_reg_range(0x7300, 0x7301), regmap_reg_range(0x7400, 0x7401), regmap_reg_range(0x7403, 0x7403), @@ -1610,6 +1627,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { true, false, false}, .gbit_capable = {true, true, true, true, true, true, true}, .ptp_capable = true, + .sgmii_port = 7, .wr_table = &ksz9477_register_set, .rd_table = &ksz9477_register_set, }, @@ -2002,6 +2020,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .internal_phy = {true, true, true, true, true, false, false}, .gbit_capable = {true, true, true, true, true, true, true}, + .sgmii_port = 7, .wr_table = &ksz9477_register_set, .rd_table = &ksz9477_register_set, }, @@ -2137,7 +2156,7 @@ void ksz_r_mib_stats64(struct ksz_device *dev, int port) spin_unlock(&mib->stats64_lock); - if (dev->info->phy_errata_9477) { + 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"); @@ -2845,6 +2864,12 @@ static int ksz_setup(struct dsa_switch *ds) if (ret) return ret; + if (ksz_has_sgmii_port(dev) && dev->dev_ops->pcs_create) { + ret = dev->dev_ops->pcs_create(dev); + if (ret) + return ret; + } + /* set broadcast storm protection 10% rate */ regmap_update_bits(ksz_regmap_16(dev), regs[S_BROADCAST_CTRL], BROADCAST_STORM_RATE, @@ -3692,6 +3717,10 @@ static void ksz_phylink_mac_config(struct phylink_config *config, 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; diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index a034017568cd..a08417df2ca4 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* Microchip switch driver common header * - * Copyright (C) 2017-2024 Microchip Technology Inc. + * Copyright (C) 2017-2025 Microchip Technology Inc. */ #ifndef __KSZ_COMMON_H @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -93,6 +94,7 @@ struct ksz_chip_data { bool internal_phy[KSZ_MAX_NUM_PORTS]; bool gbit_capable[KSZ_MAX_NUM_PORTS]; bool ptp_capable; + u8 sgmii_port; const struct regmap_access_table *wr_table; const struct regmap_access_table *rd_table; }; @@ -132,6 +134,7 @@ struct ksz_port { u32 force:1; u32 read:1; /* read MIB counters in background */ u32 freeze:1; /* MIB counter freeze is enabled */ + u32 sgmii_adv_write:1; struct ksz_port_mib mib; phy_interface_t interface; @@ -141,6 +144,7 @@ struct ksz_port { void *acl_priv; struct ksz_irq pirq; u8 num; + struct phylink_pcs *pcs; #if IS_ENABLED(CONFIG_NET_DSA_MICROCHIP_KSZ_PTP) struct kernel_hwtstamp_config tstamp_config; bool hwts_tx_en; @@ -440,6 +444,8 @@ struct ksz_dev_ops { int (*reset)(struct ksz_device *dev); int (*init)(struct ksz_device *dev); void (*exit)(struct ksz_device *dev); + + int (*pcs_create)(struct ksz_device *dev); }; struct ksz_device *ksz_switch_alloc(struct device *base, void *priv); @@ -731,6 +737,21 @@ static inline bool is_lan937x_tx_phy(struct ksz_device *dev, int port) dev->chip_id == LAN9372_CHIP_ID) && port == KSZ_PORT_4; } +static inline int ksz_get_sgmii_port(struct ksz_device *dev) +{ + return dev->info->sgmii_port - 1; +} + +static inline bool ksz_has_sgmii_port(struct ksz_device *dev) +{ + return dev->info->sgmii_port > 0; +} + +static inline bool ksz_is_sgmii_port(struct ksz_device *dev, int port) +{ + return dev->info->sgmii_port == port + 1; +} + /* STP State Defines */ #define PORT_TX_ENABLE BIT(2) #define PORT_RX_ENABLE BIT(1) -- 2.51.0 From 893e4656d46248458a832736144f8477c416b284 Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Wed, 21 May 2025 14:43:54 +0800 Subject: [PATCH 06/16] net: txgbe: Remove specified SP type Since AML devices are going to reuse some definitions, remove the "SP" qualifier from these definitions. Signed-off-by: Jiawen Wu Reviewed-by: Simon Horman Link: https://patch.msgid.link/8EF712EC14B8FF70+20250521064402.22348-2-jiawenwu@trustnetic.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/wangxun/libwx/wx_type.h | 12 ++++----- drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c | 4 +-- .../net/ethernet/wangxun/txgbe/txgbe_main.c | 26 +++++++++---------- .../net/ethernet/wangxun/txgbe/txgbe_phy.c | 8 +++--- .../net/ethernet/wangxun/txgbe/txgbe_type.h | 14 +++++----- 5 files changed, 32 insertions(+), 32 deletions(-) diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h index 8cfed5d4ebf7..5f024f5ac3a6 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h @@ -840,11 +840,11 @@ enum wx_mac_type { wx_mac_aml, }; -enum sp_media_type { - sp_media_unknown = 0, - sp_media_fiber, - sp_media_copper, - sp_media_backplane +enum wx_media_type { + wx_media_unknown = 0, + wx_media_fiber, + wx_media_copper, + wx_media_backplane }; enum em_mac_type { @@ -1211,7 +1211,7 @@ struct wx { struct wx_mbx_info mbx; struct wx_mac_info mac; enum em_mac_type mac_type; - enum sp_media_type media_type; + enum wx_media_type media_type; struct wx_eeprom_info eeprom; struct wx_addr_filter_info addr_ctrl; struct wx_fc_info fc; diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c index a054b259d435..e551ae0e2069 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c @@ -188,7 +188,7 @@ int txgbe_reset_hw(struct wx *wx) if (status != 0) return status; - if (wx->media_type != sp_media_copper) { + if (wx->media_type != wx_media_copper) { u32 val; val = WX_MIS_RST_LAN_RST(wx->bus.func); @@ -218,7 +218,7 @@ int txgbe_reset_hw(struct wx *wx) * clear the multicast table. Also reset num_rar_entries to 128, * since we modify this value when programming the SAN MAC address. */ - wx->mac.num_rar_entries = TXGBE_SP_RAR_ENTRIES; + wx->mac.num_rar_entries = TXGBE_RAR_ENTRIES; wx_init_rx_addrs(wx); pci_set_master(wx->pdev); diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index f57d84628e07..ea0b1cb721c8 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -251,25 +251,25 @@ static void txgbe_init_type_code(struct wx *wx) switch (device_type) { case TXGBE_ID_SFP: - wx->media_type = sp_media_fiber; + wx->media_type = wx_media_fiber; break; case TXGBE_ID_XAUI: case TXGBE_ID_SGMII: - wx->media_type = sp_media_copper; + wx->media_type = wx_media_copper; break; case TXGBE_ID_KR_KX_KX4: case TXGBE_ID_MAC_XAUI: case TXGBE_ID_MAC_SGMII: - wx->media_type = sp_media_backplane; + wx->media_type = wx_media_backplane; break; case TXGBE_ID_SFI_XAUI: if (wx->bus.func == 0) - wx->media_type = sp_media_fiber; + wx->media_type = wx_media_fiber; else - wx->media_type = sp_media_copper; + wx->media_type = wx_media_copper; break; default: - wx->media_type = sp_media_unknown; + wx->media_type = wx_media_unknown; break; } } @@ -283,13 +283,13 @@ static int txgbe_sw_init(struct wx *wx) u16 msix_count = 0; int err; - wx->mac.num_rar_entries = TXGBE_SP_RAR_ENTRIES; - wx->mac.max_tx_queues = TXGBE_SP_MAX_TX_QUEUES; - wx->mac.max_rx_queues = TXGBE_SP_MAX_RX_QUEUES; - wx->mac.mcft_size = TXGBE_SP_MC_TBL_SIZE; - wx->mac.vft_size = TXGBE_SP_VFT_TBL_SIZE; - wx->mac.rx_pb_size = TXGBE_SP_RX_PB_SIZE; - wx->mac.tx_pb_size = TXGBE_SP_TDB_PB_SZ; + wx->mac.num_rar_entries = TXGBE_RAR_ENTRIES; + wx->mac.max_tx_queues = TXGBE_MAX_TXQ; + wx->mac.max_rx_queues = TXGBE_MAX_RXQ; + wx->mac.mcft_size = TXGBE_MC_TBL_SIZE; + wx->mac.vft_size = TXGBE_VFT_TBL_SIZE; + wx->mac.rx_pb_size = TXGBE_RX_PB_SIZE; + wx->mac.tx_pb_size = TXGBE_TDB_PB_SZ; /* PCI config space info */ err = wx_sw_init(wx); diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c index 1863cfd27ee7..b5ae7c25ac99 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c @@ -165,7 +165,7 @@ static struct phylink_pcs *txgbe_phylink_mac_select(struct phylink_config *confi struct wx *wx = phylink_to_wx(config); struct txgbe *txgbe = wx->priv; - if (wx->media_type != sp_media_copper) + if (wx->media_type != wx_media_copper) return txgbe->pcs; return NULL; @@ -278,7 +278,7 @@ static int txgbe_phylink_init(struct txgbe *txgbe) config->mac_capabilities = MAC_10000FD | MAC_1000FD | MAC_100FD | MAC_SYM_PAUSE | MAC_ASYM_PAUSE; - if (wx->media_type == sp_media_copper) { + if (wx->media_type == wx_media_copper) { phy_mode = PHY_INTERFACE_MODE_XAUI; __set_bit(PHY_INTERFACE_MODE_XAUI, config->supported_interfaces); } else { @@ -576,7 +576,7 @@ int txgbe_init_phy(struct txgbe *txgbe) if (wx->mac.type == wx_mac_aml) return 0; - if (txgbe->wx->media_type == sp_media_copper) + if (txgbe->wx->media_type == wx_media_copper) return txgbe_ext_phy_init(txgbe); ret = txgbe_swnodes_register(txgbe); @@ -643,7 +643,7 @@ void txgbe_remove_phy(struct txgbe *txgbe) if (txgbe->wx->mac.type == wx_mac_aml) return; - if (txgbe->wx->media_type == sp_media_copper) { + if (txgbe->wx->media_type == wx_media_copper) { phylink_disconnect_phy(txgbe->wx->phylink); phylink_destroy(txgbe->wx->phylink); return; diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h index 261a83308568..8376248fecda 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h @@ -173,13 +173,13 @@ #define TXGBE_MAX_RX_QUEUES (TXGBE_MAX_FDIR_INDICES + 1) #define TXGBE_MAX_TX_QUEUES (TXGBE_MAX_FDIR_INDICES + 1) -#define TXGBE_SP_MAX_TX_QUEUES 128 -#define TXGBE_SP_MAX_RX_QUEUES 128 -#define TXGBE_SP_RAR_ENTRIES 128 -#define TXGBE_SP_MC_TBL_SIZE 128 -#define TXGBE_SP_VFT_TBL_SIZE 128 -#define TXGBE_SP_RX_PB_SIZE 512 -#define TXGBE_SP_TDB_PB_SZ (160 * 1024) /* 160KB Packet Buffer */ +#define TXGBE_MAX_TXQ 128 +#define TXGBE_MAX_RXQ 128 +#define TXGBE_RAR_ENTRIES 128 +#define TXGBE_MC_TBL_SIZE 128 +#define TXGBE_VFT_TBL_SIZE 128 +#define TXGBE_RX_PB_SIZE 512 +#define TXGBE_TDB_PB_SZ (160 * 1024) /* 160KB Packet Buffer */ #define TXGBE_MAX_VFS_DRV_LIMIT 63 -- 2.51.0 From 78cafb1bb7e9b3c27d36b0c8670494ab2b8dc1da Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Wed, 21 May 2025 14:43:55 +0800 Subject: [PATCH 07/16] net: wangxun: Use specific flag bit to simplify the code Most of the different code that requires MAC type in the common library is due to NGBE only supports a few queues and pools, unlike TXGBE, which supports 128 queues and 64 pools. This difference accounts for most of the hardware configuration differences in the driver code. So add a flag bit "WX_FLAG_MULTI_64_FUNC" for them to clean-up the driver code. Signed-off-by: Jiawen Wu Reviewed-by: Simon Horman Link: https://patch.msgid.link/C731132E124D75E5+20250521064402.22348-3-jiawenwu@trustnetic.com Signed-off-by: Paolo Abeni --- .../net/ethernet/wangxun/libwx/wx_ethtool.c | 9 ++--- drivers/net/ethernet/wangxun/libwx/wx_hw.c | 35 ++++--------------- drivers/net/ethernet/wangxun/libwx/wx_lib.c | 10 +++--- drivers/net/ethernet/wangxun/libwx/wx_sriov.c | 8 ++--- drivers/net/ethernet/wangxun/libwx/wx_type.h | 1 + .../net/ethernet/wangxun/txgbe/txgbe_main.c | 1 + 6 files changed, 20 insertions(+), 44 deletions(-) diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c index 43019ec9329c..d58d7a8735bc 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c @@ -413,15 +413,10 @@ static unsigned int wx_max_channels(struct wx *wx) max_combined = 1; } else { /* support up to max allowed queues with RSS */ - switch (wx->mac.type) { - case wx_mac_sp: - case wx_mac_aml: + if (test_bit(WX_FLAG_MULTI_64_FUNC, wx->flags)) max_combined = 63; - break; - default: + else max_combined = 8; - break; - } } return max_combined; diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c index 143cc1088eea..1c5c14ac61bc 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c @@ -113,15 +113,10 @@ static void wx_intr_disable(struct wx *wx, u64 qmask) if (mask) wr32(wx, WX_PX_IMS(0), mask); - switch (wx->mac.type) { - case wx_mac_sp: - case wx_mac_aml: + if (test_bit(WX_FLAG_MULTI_64_FUNC, wx->flags)) { mask = (qmask >> 32); if (mask) wr32(wx, WX_PX_IMS(1), mask); - break; - default: - break; } } @@ -133,15 +128,10 @@ void wx_intr_enable(struct wx *wx, u64 qmask) if (mask) wr32(wx, WX_PX_IMC(0), mask); - switch (wx->mac.type) { - case wx_mac_sp: - case wx_mac_aml: + if (test_bit(WX_FLAG_MULTI_64_FUNC, wx->flags)) { mask = (qmask >> 32); if (mask) wr32(wx, WX_PX_IMC(1), mask); - break; - default: - break; } } EXPORT_SYMBOL(wx_intr_enable); @@ -774,14 +764,8 @@ static int wx_set_rar(struct wx *wx, u32 index, u8 *addr, u64 pools, /* setup VMDq pool mapping */ wr32(wx, WX_PSR_MAC_SWC_VM_L, pools & 0xFFFFFFFF); - switch (wx->mac.type) { - case wx_mac_sp: - case wx_mac_aml: + if (test_bit(WX_FLAG_MULTI_64_FUNC, wx->flags)) wr32(wx, WX_PSR_MAC_SWC_VM_H, pools >> 32); - break; - default: - break; - } /* HW expects these in little endian so we reverse the byte * order from network order (big endian) to little endian @@ -919,14 +903,9 @@ void wx_init_rx_addrs(struct wx *wx) wx_set_rar(wx, 0, wx->mac.addr, 0, WX_PSR_MAC_SWC_AD_H_AV); - switch (wx->mac.type) { - case wx_mac_sp: - case wx_mac_aml: + if (test_bit(WX_FLAG_MULTI_64_FUNC, wx->flags)) { /* clear VMDq pool/queue selection for RAR 0 */ wx_clear_vmdq(wx, 0, WX_CLEAR_VMDQ_ALL); - break; - default: - break; } } @@ -1512,7 +1491,7 @@ static void wx_configure_virtualization(struct wx *wx) wr32m(wx, WX_PSR_VM_L2CTL(pool), WX_PSR_VM_L2CTL_AUPE, WX_PSR_VM_L2CTL_AUPE); - if (wx->mac.type == wx_mac_em) { + if (!test_bit(WX_FLAG_MULTI_64_FUNC, wx->flags)) { vf_shift = BIT(VMDQ_P(0)); /* Enable only the PF pools for Tx/Rx */ wr32(wx, WX_RDM_VF_RE(0), vf_shift); @@ -1543,7 +1522,7 @@ static void wx_configure_port(struct wx *wx) { u32 value, i; - if (wx->mac.type == wx_mac_em) { + if (!test_bit(WX_FLAG_MULTI_64_FUNC, wx->flags)) { value = (wx->num_vfs == 0) ? WX_CFG_PORT_CTL_NUM_VT_NONE : WX_CFG_PORT_CTL_NUM_VT_8; @@ -2074,7 +2053,7 @@ static void wx_setup_psrtype(struct wx *wx) WX_RDB_PL_CFG_TUN_OUTL2HDR | WX_RDB_PL_CFG_TUN_TUNHDR; - if (wx->mac.type == wx_mac_em) { + if (!test_bit(WX_FLAG_MULTI_64_FUNC, wx->flags)) { for_each_set_bit(pool, &wx->fwd_bitmask, 8) wr32(wx, WX_RDB_PL_CFG(VMDQ_P(pool)), psrtype); } else { diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c index 2a808afeb414..eab16c57b039 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c @@ -1633,7 +1633,7 @@ static bool wx_set_vmdq_queues(struct wx *wx) /* Add starting offset to total pool count */ vmdq_i += wx->ring_feature[RING_F_VMDQ].offset; - if (wx->mac.type == wx_mac_sp) { + if (test_bit(WX_FLAG_MULTI_64_FUNC, wx->flags)) { /* double check we are limited to maximum pools */ vmdq_i = min_t(u16, 64, vmdq_i); @@ -1693,7 +1693,7 @@ static void wx_set_rss_queues(struct wx *wx) /* set mask for 16 queue limit of RSS */ f = &wx->ring_feature[RING_F_RSS]; - if (wx->mac.type == wx_mac_sp) + if (test_bit(WX_FLAG_MULTI_64_FUNC, wx->flags)) f->mask = WX_RSS_64Q_MASK; else f->mask = WX_RSS_8Q_MASK; @@ -1853,7 +1853,7 @@ static bool wx_cache_ring_vmdq(struct wx *wx) if (!test_bit(WX_FLAG_VMDQ_ENABLED, wx->flags)) return false; - if (wx->mac.type == wx_mac_sp) { + if (test_bit(WX_FLAG_MULTI_64_FUNC, wx->flags)) { /* start at VMDq register offset for SR-IOV enabled setups */ reg_idx = vmdq->offset * __ALIGN_MASK(1, ~vmdq->mask); for (i = 0; i < wx->num_rx_queues; i++, reg_idx++) { @@ -2354,10 +2354,10 @@ void wx_configure_vectors(struct wx *wx) if (pdev->msix_enabled) { /* Populate MSIX to EITR Select */ - if (wx->mac.type == wx_mac_sp) { + if (test_bit(WX_FLAG_MULTI_64_FUNC, wx->flags)) { if (wx->num_vfs >= 32) eitrsel = BIT(wx->num_vfs % 32) - 1; - } else if (wx->mac.type == wx_mac_em) { + } else { for (i = 0; i < wx->num_vfs; i++) eitrsel |= BIT(i); } diff --git a/drivers/net/ethernet/wangxun/libwx/wx_sriov.c b/drivers/net/ethernet/wangxun/libwx/wx_sriov.c index 195f64baedab..e8656d9d733b 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_sriov.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_sriov.c @@ -106,7 +106,7 @@ static int __wx_enable_sriov(struct wx *wx, u8 num_vfs) wx->vfinfo[i].xcast_mode = WXVF_XCAST_MODE_NONE; } - if (wx->mac.type == wx_mac_em) { + if (!test_bit(WX_FLAG_MULTI_64_FUNC, wx->flags)) { value = WX_CFG_PORT_CTL_NUM_VT_8; } else { if (num_vfs < 32) @@ -599,10 +599,10 @@ static int wx_set_vf_vlan_msg(struct wx *wx, u32 *msgbuf, u16 vf) if (VMDQ_P(0) < 32) { bits = rd32(wx, WX_PSR_VLAN_SWC_VM_L); bits &= ~BIT(VMDQ_P(0)); - if (wx->mac.type != wx_mac_em) + if (test_bit(WX_FLAG_MULTI_64_FUNC, wx->flags)) bits |= rd32(wx, WX_PSR_VLAN_SWC_VM_H); } else { - if (wx->mac.type != wx_mac_em) + if (test_bit(WX_FLAG_MULTI_64_FUNC, wx->flags)) bits = rd32(wx, WX_PSR_VLAN_SWC_VM_H); bits &= ~BIT(VMDQ_P(0) % 32); bits |= rd32(wx, WX_PSR_VLAN_SWC_VM_L); @@ -848,7 +848,7 @@ void wx_disable_vf_rx_tx(struct wx *wx) { wr32(wx, WX_TDM_VFTE_CLR(0), U32_MAX); wr32(wx, WX_RDM_VFRE_CLR(0), U32_MAX); - if (wx->mac.type != wx_mac_em) { + if (test_bit(WX_FLAG_MULTI_64_FUNC, wx->flags)) { wr32(wx, WX_TDM_VFTE_CLR(1), U32_MAX); wr32(wx, WX_RDM_VFRE_CLR(1), U32_MAX); } diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h index 5f024f5ac3a6..6563d30e60c5 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h @@ -1184,6 +1184,7 @@ struct vf_macvlans { }; enum wx_pf_flags { + WX_FLAG_MULTI_64_FUNC, WX_FLAG_SWFW_RING, WX_FLAG_VMDQ_ENABLED, WX_FLAG_VLAN_PROMISC, diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index ea0b1cb721c8..0c81d8fc2f7d 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -318,6 +318,7 @@ static int txgbe_sw_init(struct wx *wx) wx->configure_fdir = txgbe_configure_fdir; set_bit(WX_FLAG_RSC_CAPABLE, wx->flags); + set_bit(WX_FLAG_MULTI_64_FUNC, wx->flags); /* enable itr by default in dynamic mode */ wx->rx_itr_setting = 1; -- 2.51.0 From 39709fe4bacda6748759522738e33464d6704dc0 Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Wed, 21 May 2025 14:43:56 +0800 Subject: [PATCH 08/16] net: txgbe: Distinguish between 40G and 25G devices For the following patches to support PHYLINK for AML 25G devices, separate MAC type wx_mac_aml40 to maintain the driver of 40G devices. Because 40G devices will complete support later, not now. And this patch makes the 25G devices use some PHYLINK interfaces, but it is not yet create PHYLINK and cannot be used on its own. It is just preparation for the next patches. Signed-off-by: Jiawen Wu Reviewed-by: Simon Horman Link: https://patch.msgid.link/592B1A6920867D0C+20250521064402.22348-4-jiawenwu@trustnetic.com Signed-off-by: Paolo Abeni --- .../net/ethernet/wangxun/libwx/wx_ethtool.c | 12 ++++--- drivers/net/ethernet/wangxun/libwx/wx_hw.c | 1 + drivers/net/ethernet/wangxun/libwx/wx_lib.c | 2 ++ drivers/net/ethernet/wangxun/libwx/wx_type.h | 1 + .../net/ethernet/wangxun/txgbe/txgbe_irq.c | 4 +-- .../net/ethernet/wangxun/txgbe/txgbe_main.c | 32 ++++++++++++++----- .../net/ethernet/wangxun/txgbe/txgbe_phy.c | 32 +++++++++++++------ .../net/ethernet/wangxun/txgbe/txgbe_type.h | 3 +- 8 files changed, 61 insertions(+), 26 deletions(-) diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c index d58d7a8735bc..86c0159e8a2d 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c @@ -219,7 +219,7 @@ int wx_nway_reset(struct net_device *netdev) { struct wx *wx = netdev_priv(netdev); - if (wx->mac.type == wx_mac_aml) + if (wx->mac.type == wx_mac_aml40) return -EOPNOTSUPP; return phylink_ethtool_nway_reset(wx->phylink); @@ -231,7 +231,7 @@ int wx_get_link_ksettings(struct net_device *netdev, { struct wx *wx = netdev_priv(netdev); - if (wx->mac.type == wx_mac_aml) + if (wx->mac.type == wx_mac_aml40) return -EOPNOTSUPP; return phylink_ethtool_ksettings_get(wx->phylink, cmd); @@ -243,7 +243,7 @@ int wx_set_link_ksettings(struct net_device *netdev, { struct wx *wx = netdev_priv(netdev); - if (wx->mac.type == wx_mac_aml) + if (wx->mac.type == wx_mac_aml40) return -EOPNOTSUPP; return phylink_ethtool_ksettings_set(wx->phylink, cmd); @@ -255,7 +255,7 @@ void wx_get_pauseparam(struct net_device *netdev, { struct wx *wx = netdev_priv(netdev); - if (wx->mac.type == wx_mac_aml) + if (wx->mac.type == wx_mac_aml40) return; phylink_ethtool_get_pauseparam(wx->phylink, pause); @@ -267,7 +267,7 @@ int wx_set_pauseparam(struct net_device *netdev, { struct wx *wx = netdev_priv(netdev); - if (wx->mac.type == wx_mac_aml) + if (wx->mac.type == wx_mac_aml40) return -EOPNOTSUPP; return phylink_ethtool_set_pauseparam(wx->phylink, pause); @@ -345,6 +345,7 @@ int wx_set_coalesce(struct net_device *netdev, max_eitr = WX_SP_MAX_EITR; break; case wx_mac_aml: + case wx_mac_aml40: max_eitr = WX_AML_MAX_EITR; break; default: @@ -375,6 +376,7 @@ int wx_set_coalesce(struct net_device *netdev, switch (wx->mac.type) { case wx_mac_sp: case wx_mac_aml: + case wx_mac_aml40: tx_itr_param = WX_12K_ITR; break; default: diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c index 1c5c14ac61bc..7a3467b41524 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c @@ -695,6 +695,7 @@ void wx_init_eeprom_params(struct wx *wx) switch (wx->mac.type) { case wx_mac_sp: case wx_mac_aml: + case wx_mac_aml40: if (wx_read_ee_hostif(wx, WX_SW_REGION_PTR, &data)) { wx_err(wx, "NVM Read Error\n"); return; diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c index eab16c57b039..68e7cfe2f7ea 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c @@ -1959,6 +1959,7 @@ static int wx_alloc_q_vector(struct wx *wx, switch (wx->mac.type) { case wx_mac_sp: case wx_mac_aml: + case wx_mac_aml40: default_itr = WX_12K_ITR; break; default: @@ -2327,6 +2328,7 @@ void wx_write_eitr(struct wx_q_vector *q_vector) itr_reg = q_vector->itr & WX_SP_MAX_EITR; break; case wx_mac_aml: + case wx_mac_aml40: itr_reg = (q_vector->itr >> 3) & WX_AML_MAX_EITR; break; default: diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h index 6563d30e60c5..b4275ba622de 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h @@ -838,6 +838,7 @@ enum wx_mac_type { wx_mac_sp, wx_mac_em, wx_mac_aml, + wx_mac_aml40, }; enum wx_media_type { diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c index 19878f02d956..f53a5d00a41b 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c @@ -176,7 +176,7 @@ static void txgbe_del_irq_domain(struct txgbe *txgbe) void txgbe_free_misc_irq(struct txgbe *txgbe) { - if (txgbe->wx->mac.type == wx_mac_aml) + if (txgbe->wx->mac.type == wx_mac_aml40) return; free_irq(txgbe->link_irq, txgbe); @@ -190,7 +190,7 @@ int txgbe_setup_misc_irq(struct txgbe *txgbe) struct wx *wx = txgbe->wx; int hwirq, err; - if (wx->mac.type == wx_mac_aml) + if (wx->mac.type == wx_mac_aml40) goto skip_sp_irq; txgbe->misc.nirqs = TXGBE_IRQ_MAX; diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index 0c81d8fc2f7d..ca3dbc448646 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -91,6 +91,7 @@ static int txgbe_enumerate_functions(struct wx *wx) static void txgbe_up_complete(struct wx *wx) { struct net_device *netdev = wx->netdev; + u32 reg; wx_control_hw(wx, true); wx_configure_vectors(wx); @@ -99,17 +100,21 @@ static void txgbe_up_complete(struct wx *wx) smp_mb__before_atomic(); wx_napi_enable_all(wx); - if (wx->mac.type == wx_mac_aml) { - u32 reg; - + switch (wx->mac.type) { + case wx_mac_aml40: reg = rd32(wx, TXGBE_AML_MAC_TX_CFG); reg &= ~TXGBE_AML_MAC_TX_CFG_SPEED_MASK; - reg |= TXGBE_AML_MAC_TX_CFG_SPEED_25G; + reg |= TXGBE_AML_MAC_TX_CFG_SPEED_40G; wr32(wx, WX_MAC_TX_CFG, reg); txgbe_enable_sec_tx_path(wx); netif_carrier_on(wx->netdev); - } else { + break; + case wx_mac_aml: + case wx_mac_sp: phylink_start(wx->phylink); + break; + default: + break; } /* clear any pending interrupts, may auto mask */ @@ -207,10 +212,18 @@ void txgbe_down(struct wx *wx) { txgbe_disable_device(wx); txgbe_reset(wx); - if (wx->mac.type == wx_mac_aml) + + switch (wx->mac.type) { + case wx_mac_aml40: netif_carrier_off(wx->netdev); - else + break; + case wx_mac_aml: + case wx_mac_sp: phylink_stop(wx->phylink); + break; + default: + break; + } wx_clean_all_tx_rings(wx); wx_clean_all_rx_rings(wx); @@ -240,9 +253,11 @@ static void txgbe_init_type_code(struct wx *wx) case TXGBE_DEV_ID_AML5110: case TXGBE_DEV_ID_AML5025: case TXGBE_DEV_ID_AML5125: + wx->mac.type = wx_mac_aml; + break; case TXGBE_DEV_ID_AML5040: case TXGBE_DEV_ID_AML5140: - wx->mac.type = wx_mac_aml; + wx->mac.type = wx_mac_aml40; break; default: wx->mac.type = wx_mac_unknown; @@ -341,6 +356,7 @@ static int txgbe_sw_init(struct wx *wx) case wx_mac_sp: break; case wx_mac_aml: + case wx_mac_aml40: set_bit(WX_FLAG_SWFW_RING, wx->flags); wx->swfw_index = 0; break; diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c index b5ae7c25ac99..ece378fa2620 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c @@ -573,11 +573,17 @@ int txgbe_init_phy(struct txgbe *txgbe) struct wx *wx = txgbe->wx; int ret; - if (wx->mac.type == wx_mac_aml) + switch (wx->mac.type) { + case wx_mac_aml40: + case wx_mac_aml: return 0; - - if (txgbe->wx->media_type == wx_media_copper) - return txgbe_ext_phy_init(txgbe); + case wx_mac_sp: + if (wx->media_type == wx_media_copper) + return txgbe_ext_phy_init(txgbe); + break; + default: + break; + } ret = txgbe_swnodes_register(txgbe); if (ret) { @@ -640,13 +646,19 @@ err_unregister_swnode: void txgbe_remove_phy(struct txgbe *txgbe) { - if (txgbe->wx->mac.type == wx_mac_aml) - return; - - if (txgbe->wx->media_type == wx_media_copper) { - phylink_disconnect_phy(txgbe->wx->phylink); - phylink_destroy(txgbe->wx->phylink); + switch (txgbe->wx->mac.type) { + case wx_mac_aml40: + case wx_mac_aml: return; + case wx_mac_sp: + if (txgbe->wx->media_type == wx_media_copper) { + phylink_disconnect_phy(txgbe->wx->phylink); + phylink_destroy(txgbe->wx->phylink); + return; + } + break; + default: + break; } platform_device_unregister(txgbe->sfp_dev); diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h index 8376248fecda..3b4e4361462a 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h @@ -152,7 +152,8 @@ #define TXGBE_PX_PF_BME 0x4B8 #define TXGBE_AML_MAC_TX_CFG 0x11000 #define TXGBE_AML_MAC_TX_CFG_SPEED_MASK GENMASK(30, 27) -#define TXGBE_AML_MAC_TX_CFG_SPEED_25G BIT(28) +#define TXGBE_AML_MAC_TX_CFG_SPEED_40G FIELD_PREP(GENMASK(30, 27), 0) +#define TXGBE_AML_MAC_TX_CFG_SPEED_25G FIELD_PREP(GENMASK(30, 27), 2) #define TXGBE_RDM_RSC_CTL 0x1200C #define TXGBE_RDM_RSC_CTL_FREE_CTL BIT(7) -- 2.51.0 From 6f8b4c01a8cd989a08a2eb6aab87e899ee450f93 Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Wed, 21 May 2025 14:43:57 +0800 Subject: [PATCH 09/16] net: txgbe: Implement PHYLINK for AML 25G/10G devices There is a new PHY attached to AML 25G/10G NIC, which is different from SP 10G/1G NIC. But the PHY configuration is handed over to firmware, and also I2C is controlled by firmware. So the different PHYLINK fixed-link mode is added for these devices. Signed-off-by: Jiawen Wu Reviewed-by: Simon Horman Link: https://patch.msgid.link/987B973A5929CD48+20250521064402.22348-5-jiawenwu@trustnetic.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/wangxun/libwx/wx_hw.c | 12 +- drivers/net/ethernet/wangxun/libwx/wx_hw.h | 1 + drivers/net/ethernet/wangxun/txgbe/Makefile | 3 +- .../net/ethernet/wangxun/txgbe/txgbe_aml.c | 154 ++++++++++++++++++ .../net/ethernet/wangxun/txgbe/txgbe_aml.h | 9 + .../net/ethernet/wangxun/txgbe/txgbe_phy.c | 11 +- .../net/ethernet/wangxun/txgbe/txgbe_type.h | 11 ++ 7 files changed, 195 insertions(+), 6 deletions(-) create mode 100644 drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c create mode 100644 drivers/net/ethernet/wangxun/txgbe/txgbe_aml.h diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c index 7a3467b41524..c6744a363e97 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c @@ -2252,10 +2252,8 @@ int wx_stop_adapter(struct wx *wx) } EXPORT_SYMBOL(wx_stop_adapter); -void wx_reset_misc(struct wx *wx) +void wx_reset_mac(struct wx *wx) { - int i; - /* receive packets that size > 2048 */ wr32m(wx, WX_MAC_RX_CFG, WX_MAC_RX_CFG_JE, WX_MAC_RX_CFG_JE); @@ -2267,6 +2265,14 @@ void wx_reset_misc(struct wx *wx) WX_MAC_RX_FLOW_CTRL_RFE, WX_MAC_RX_FLOW_CTRL_RFE); wr32(wx, WX_MAC_PKT_FLT, WX_MAC_PKT_FLT_PR); +} +EXPORT_SYMBOL(wx_reset_mac); + +void wx_reset_misc(struct wx *wx) +{ + int i; + + wx_reset_mac(wx); wr32m(wx, WX_MIS_RST_ST, WX_MIS_RST_ST_RST_INIT, 0x1E00); diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.h b/drivers/net/ethernet/wangxun/libwx/wx_hw.h index 91c1d6135045..26a56cba60b9 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.h @@ -42,6 +42,7 @@ void wx_configure(struct wx *wx); void wx_start_hw(struct wx *wx); int wx_disable_pcie_master(struct wx *wx); int wx_stop_adapter(struct wx *wx); +void wx_reset_mac(struct wx *wx); void wx_reset_misc(struct wx *wx); int wx_get_pcie_msix_counts(struct wx *wx, u16 *msix_count, u16 max_msix_count); int wx_sw_init(struct wx *wx); diff --git a/drivers/net/ethernet/wangxun/txgbe/Makefile b/drivers/net/ethernet/wangxun/txgbe/Makefile index f74576fe7062..c757fa95e58e 100644 --- a/drivers/net/ethernet/wangxun/txgbe/Makefile +++ b/drivers/net/ethernet/wangxun/txgbe/Makefile @@ -11,4 +11,5 @@ txgbe-objs := txgbe_main.o \ txgbe_phy.o \ txgbe_irq.o \ txgbe_fdir.o \ - txgbe_ethtool.o + txgbe_ethtool.o \ + txgbe_aml.o diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c new file mode 100644 index 000000000000..49eb93987a9d --- /dev/null +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2015 - 2025 Beijing WangXun Technology Co., Ltd. */ + +#include +#include +#include +#include + +#include "../libwx/wx_type.h" +#include "../libwx/wx_lib.h" +#include "../libwx/wx_hw.h" +#include "txgbe_type.h" +#include "txgbe_aml.h" +#include "txgbe_hw.h" + +static void txgbe_get_phy_link(struct wx *wx, int *speed) +{ + u32 status; + + status = rd32(wx, TXGBE_CFG_PORT_ST); + if (!(status & TXGBE_CFG_PORT_ST_LINK_UP)) + *speed = SPEED_UNKNOWN; + else if (status & TXGBE_CFG_PORT_ST_LINK_AML_25G) + *speed = SPEED_25000; + else if (status & TXGBE_CFG_PORT_ST_LINK_AML_10G) + *speed = SPEED_10000; + else + *speed = SPEED_UNKNOWN; +} + +static void txgbe_get_link_state(struct phylink_config *config, + struct phylink_link_state *state) +{ + struct wx *wx = phylink_to_wx(config); + int speed; + + txgbe_get_phy_link(wx, &speed); + state->link = speed != SPEED_UNKNOWN; + state->speed = speed; + state->duplex = state->link ? DUPLEX_FULL : DUPLEX_UNKNOWN; +} + +static void txgbe_reconfig_mac(struct wx *wx) +{ + u32 wdg, fc; + + wdg = rd32(wx, WX_MAC_WDG_TIMEOUT); + fc = rd32(wx, WX_MAC_RX_FLOW_CTRL); + + wr32(wx, WX_MIS_RST, TXGBE_MIS_RST_MAC_RST(wx->bus.func)); + /* wait for MAC reset complete */ + usleep_range(1000, 1500); + + wr32m(wx, TXGBE_MAC_MISC_CTL, TXGBE_MAC_MISC_CTL_LINK_STS_MOD, + TXGBE_MAC_MISC_CTL_LINK_BOTH); + wx_reset_mac(wx); + + wr32(wx, WX_MAC_WDG_TIMEOUT, wdg); + wr32(wx, WX_MAC_RX_FLOW_CTRL, fc); +} + +static void txgbe_mac_link_up_aml(struct phylink_config *config, + struct phy_device *phy, + unsigned int mode, + phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause) +{ + struct wx *wx = phylink_to_wx(config); + u32 txcfg; + + wx_fc_enable(wx, tx_pause, rx_pause); + + txgbe_reconfig_mac(wx); + + txcfg = rd32(wx, TXGBE_AML_MAC_TX_CFG); + txcfg &= ~TXGBE_AML_MAC_TX_CFG_SPEED_MASK; + + switch (speed) { + case SPEED_25000: + txcfg |= TXGBE_AML_MAC_TX_CFG_SPEED_25G; + break; + case SPEED_10000: + txcfg |= TXGBE_AML_MAC_TX_CFG_SPEED_10G; + break; + default: + break; + } + + wr32m(wx, WX_MAC_RX_CFG, WX_MAC_RX_CFG_RE, WX_MAC_RX_CFG_RE); + wr32(wx, TXGBE_AML_MAC_TX_CFG, txcfg | TXGBE_AML_MAC_TX_CFG_TE); + + wx->speed = speed; +} + +static void txgbe_mac_link_down_aml(struct phylink_config *config, + unsigned int mode, + phy_interface_t interface) +{ + struct wx *wx = phylink_to_wx(config); + + wr32m(wx, TXGBE_AML_MAC_TX_CFG, TXGBE_AML_MAC_TX_CFG_TE, 0); + wr32m(wx, WX_MAC_RX_CFG, WX_MAC_RX_CFG_RE, 0); + + wx->speed = SPEED_UNKNOWN; +} + +static void txgbe_mac_config_aml(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state) +{ +} + +static const struct phylink_mac_ops txgbe_mac_ops_aml = { + .mac_config = txgbe_mac_config_aml, + .mac_link_down = txgbe_mac_link_down_aml, + .mac_link_up = txgbe_mac_link_up_aml, +}; + +int txgbe_phylink_init_aml(struct txgbe *txgbe) +{ + struct phylink_link_state state; + struct phylink_config *config; + struct wx *wx = txgbe->wx; + phy_interface_t phy_mode; + struct phylink *phylink; + int err; + + config = &wx->phylink_config; + config->dev = &wx->netdev->dev; + config->type = PHYLINK_NETDEV; + config->mac_capabilities = MAC_25000FD | MAC_10000FD | + MAC_SYM_PAUSE | MAC_ASYM_PAUSE; + config->get_fixed_state = txgbe_get_link_state; + + phy_mode = PHY_INTERFACE_MODE_25GBASER; + __set_bit(PHY_INTERFACE_MODE_25GBASER, config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_10GBASER, config->supported_interfaces); + + phylink = phylink_create(config, NULL, phy_mode, &txgbe_mac_ops_aml); + if (IS_ERR(phylink)) + return PTR_ERR(phylink); + + state.speed = SPEED_25000; + state.duplex = DUPLEX_FULL; + err = phylink_set_fixed_link(phylink, &state); + if (err) { + wx_err(wx, "Failed to set fixed link\n"); + return err; + } + + wx->phylink = phylink; + + return 0; +} diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.h new file mode 100644 index 000000000000..aa3b7848e03d --- /dev/null +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2015 - 2025 Beijing WangXun Technology Co., Ltd. */ + +#ifndef _TXGBE_AML_H_ +#define _TXGBE_AML_H_ + +int txgbe_phylink_init_aml(struct txgbe *txgbe); + +#endif /* _TXGBE_AML_H_ */ diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c index ece378fa2620..03f1b9bc604d 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c @@ -20,6 +20,7 @@ #include "../libwx/wx_mbx.h" #include "../libwx/wx_hw.h" #include "txgbe_type.h" +#include "txgbe_aml.h" #include "txgbe_phy.h" #include "txgbe_hw.h" @@ -318,7 +319,10 @@ irqreturn_t txgbe_link_irq_handler(int irq, void *data) status = rd32(wx, TXGBE_CFG_PORT_ST); up = !!(status & TXGBE_CFG_PORT_ST_LINK_UP); - phylink_pcs_change(txgbe->pcs, up); + if (txgbe->pcs) + phylink_pcs_change(txgbe->pcs, up); + else + phylink_mac_change(wx->phylink, up); return IRQ_HANDLED; } @@ -575,8 +579,9 @@ int txgbe_init_phy(struct txgbe *txgbe) switch (wx->mac.type) { case wx_mac_aml40: - case wx_mac_aml: return 0; + case wx_mac_aml: + return txgbe_phylink_init_aml(txgbe); case wx_mac_sp: if (wx->media_type == wx_media_copper) return txgbe_ext_phy_init(txgbe); @@ -648,7 +653,9 @@ void txgbe_remove_phy(struct txgbe *txgbe) { switch (txgbe->wx->mac.type) { case wx_mac_aml40: + return; case wx_mac_aml: + phylink_destroy(txgbe->wx->phylink); return; case wx_mac_sp: if (txgbe->wx->media_type == wx_media_copper) { diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h index 3b4e4361462a..ca4da2696eed 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h @@ -50,6 +50,8 @@ /**************** SP Registers ****************************/ /* chip control Registers */ +#define TXGBE_MIS_RST 0x1000C +#define TXGBE_MIS_RST_MAC_RST(_i) BIT(20 - (_i) * 3) #define TXGBE_MIS_PRB_CTL 0x10010 #define TXGBE_MIS_PRB_CTL_LAN_UP(_i) BIT(1 - (_i)) /* FMGR Registers */ @@ -62,6 +64,11 @@ #define TXGBE_TS_CTL 0x10300 #define TXGBE_TS_CTL_EVAL_MD BIT(31) +/* MAC Misc Registers */ +#define TXGBE_MAC_MISC_CTL 0x11F00 +#define TXGBE_MAC_MISC_CTL_LINK_STS_MOD BIT(0) +#define TXGBE_MAC_MISC_CTL_LINK_PCS FIELD_PREP(BIT(0), 0) +#define TXGBE_MAC_MISC_CTL_LINK_BOTH FIELD_PREP(BIT(0), 1) /* GPIO register bit */ #define TXGBE_GPIOBIT_0 BIT(0) /* I:tx fault */ #define TXGBE_GPIOBIT_1 BIT(1) /* O:tx disabled */ @@ -88,6 +95,8 @@ /* Port cfg registers */ #define TXGBE_CFG_PORT_ST 0x14404 #define TXGBE_CFG_PORT_ST_LINK_UP BIT(0) +#define TXGBE_CFG_PORT_ST_LINK_AML_25G BIT(3) +#define TXGBE_CFG_PORT_ST_LINK_AML_10G BIT(4) #define TXGBE_CFG_VXLAN 0x14410 #define TXGBE_CFG_VXLAN_GPE 0x14414 #define TXGBE_CFG_GENEVE 0x14418 @@ -151,9 +160,11 @@ /*************************** Amber Lite Registers ****************************/ #define TXGBE_PX_PF_BME 0x4B8 #define TXGBE_AML_MAC_TX_CFG 0x11000 +#define TXGBE_AML_MAC_TX_CFG_TE BIT(0) #define TXGBE_AML_MAC_TX_CFG_SPEED_MASK GENMASK(30, 27) #define TXGBE_AML_MAC_TX_CFG_SPEED_40G FIELD_PREP(GENMASK(30, 27), 0) #define TXGBE_AML_MAC_TX_CFG_SPEED_25G FIELD_PREP(GENMASK(30, 27), 2) +#define TXGBE_AML_MAC_TX_CFG_SPEED_10G FIELD_PREP(GENMASK(30, 27), 8) #define TXGBE_RDM_RSC_CTL 0x1200C #define TXGBE_RDM_RSC_CTL_FREE_CTL BIT(7) -- 2.51.0 From 343929799ace121cd9b795c526b2ad7aefe54458 Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Wed, 21 May 2025 14:43:58 +0800 Subject: [PATCH 10/16] net: txgbe: Support to handle GPIO IRQs for AML devices The driver needs to handle GPIO interrupts to identify SFP module and configure PHY by sending mailbox messages to firmware. Since the SFP module needs to wait for ready to get information when it is inserted, workqueue is added to handle delayed tasks. And each SW-FW interaction takes time to wait, so they are processed in the workqueue instead of IRQ handler function. Signed-off-by: Jiawen Wu Reviewed-by: Simon Horman Link: https://patch.msgid.link/399624AF221E8E28+20250521064402.22348-6-jiawenwu@trustnetic.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/wangxun/libwx/wx_lib.c | 31 +++ drivers/net/ethernet/wangxun/libwx/wx_lib.h | 3 + drivers/net/ethernet/wangxun/libwx/wx_type.h | 8 + .../net/ethernet/wangxun/txgbe/txgbe_aml.c | 204 ++++++++++++++++++ .../net/ethernet/wangxun/txgbe/txgbe_aml.h | 5 + .../net/ethernet/wangxun/txgbe/txgbe_irq.c | 35 ++- .../net/ethernet/wangxun/txgbe/txgbe_main.c | 74 ++++++- .../net/ethernet/wangxun/txgbe/txgbe_type.h | 75 +++++++ 8 files changed, 433 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c index 68e7cfe2f7ea..5c747509d56b 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -3095,5 +3096,35 @@ void wx_set_ring(struct wx *wx, u32 new_tx_count, } EXPORT_SYMBOL(wx_set_ring); +void wx_service_event_schedule(struct wx *wx) +{ + if (!test_and_set_bit(WX_STATE_SERVICE_SCHED, wx->state)) + queue_work(system_power_efficient_wq, &wx->service_task); +} +EXPORT_SYMBOL(wx_service_event_schedule); + +void wx_service_event_complete(struct wx *wx) +{ + if (WARN_ON(!test_bit(WX_STATE_SERVICE_SCHED, wx->state))) + return; + + /* flush memory to make sure state is correct before next watchdog */ + smp_mb__before_atomic(); + clear_bit(WX_STATE_SERVICE_SCHED, wx->state); +} +EXPORT_SYMBOL(wx_service_event_complete); + +void wx_service_timer(struct timer_list *t) +{ + struct wx *wx = from_timer(wx, t, service_timer); + unsigned long next_event_offset = HZ * 2; + + /* Reset the timer */ + mod_timer(&wx->service_timer, next_event_offset + jiffies); + + wx_service_event_schedule(wx); +} +EXPORT_SYMBOL(wx_service_timer); + MODULE_DESCRIPTION("Common library for Wangxun(R) Ethernet drivers."); MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.h b/drivers/net/ethernet/wangxun/libwx/wx_lib.h index 49f8bde36ddb..aed6ea8cf0d6 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_lib.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.h @@ -38,5 +38,8 @@ netdev_features_t wx_features_check(struct sk_buff *skb, netdev_features_t features); void wx_set_ring(struct wx *wx, u32 new_tx_count, u32 new_rx_count, struct wx_ring *temp_ring); +void wx_service_event_schedule(struct wx *wx); +void wx_service_event_complete(struct wx *wx); +void wx_service_timer(struct timer_list *t); #endif /* _WX_LIB_H_ */ diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h index b4275ba622de..7730c9fc3e02 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h @@ -1154,6 +1154,7 @@ enum wx_state { WX_STATE_SWFW_BUSY, WX_STATE_PTP_RUNNING, WX_STATE_PTP_TX_IN_PROGRESS, + WX_STATE_SERVICE_SCHED, WX_STATE_NBITS /* must be last */ }; @@ -1197,6 +1198,8 @@ enum wx_pf_flags { WX_FLAG_RX_HWTSTAMP_ENABLED, WX_FLAG_RX_HWTSTAMP_IN_REGISTER, WX_FLAG_PTP_PPS_ENABLED, + WX_FLAG_NEED_LINK_CONFIG, + WX_FLAG_NEED_SFP_RESET, WX_PF_FLAGS_NBITS /* must be last */ }; @@ -1235,6 +1238,8 @@ struct wx { /* PHY stuff */ bool notify_down; + int adv_speed; + int adv_duplex; unsigned int link; int speed; int duplex; @@ -1332,6 +1337,9 @@ struct wx { struct ptp_clock_info ptp_caps; struct kernel_hwtstamp_config tstamp_config; struct sk_buff *ptp_tx_skb; + + struct timer_list service_timer; + struct work_struct service_task; }; #define WX_INTR_ALL (~0ULL) diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c index 49eb93987a9d..af12ebb89c71 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c @@ -13,6 +13,96 @@ #include "txgbe_aml.h" #include "txgbe_hw.h" +void txgbe_gpio_init_aml(struct wx *wx) +{ + u32 status; + + wr32(wx, WX_GPIO_INTTYPE_LEVEL, TXGBE_GPIOBIT_2 | TXGBE_GPIOBIT_3); + wr32(wx, WX_GPIO_INTEN, TXGBE_GPIOBIT_2 | TXGBE_GPIOBIT_3); + + status = rd32(wx, WX_GPIO_INTSTATUS); + for (int i = 0; i < 6; i++) { + if (status & BIT(i)) + wr32(wx, WX_GPIO_EOI, BIT(i)); + } +} + +irqreturn_t txgbe_gpio_irq_handler_aml(int irq, void *data) +{ + struct txgbe *txgbe = data; + struct wx *wx = txgbe->wx; + u32 status; + + wr32(wx, WX_GPIO_INTMASK, 0xFF); + status = rd32(wx, WX_GPIO_INTSTATUS); + if (status & TXGBE_GPIOBIT_2) { + set_bit(WX_FLAG_NEED_SFP_RESET, wx->flags); + wr32(wx, WX_GPIO_EOI, TXGBE_GPIOBIT_2); + wx_service_event_schedule(wx); + } + if (status & TXGBE_GPIOBIT_3) { + set_bit(WX_FLAG_NEED_LINK_CONFIG, wx->flags); + wx_service_event_schedule(wx); + wr32(wx, WX_GPIO_EOI, TXGBE_GPIOBIT_3); + } + + wr32(wx, WX_GPIO_INTMASK, 0); + return IRQ_HANDLED; +} + +static int txgbe_identify_sfp_hostif(struct wx *wx, struct txgbe_hic_i2c_read *buffer) +{ + buffer->hdr.cmd = FW_READ_SFP_INFO_CMD; + buffer->hdr.buf_len = sizeof(struct txgbe_hic_i2c_read) - + sizeof(struct wx_hic_hdr); + buffer->hdr.cmd_or_resp.cmd_resv = FW_CEM_CMD_RESERVED; + + return wx_host_interface_command(wx, (u32 *)buffer, + sizeof(struct txgbe_hic_i2c_read), + WX_HI_COMMAND_TIMEOUT, true); +} + +static int txgbe_set_phy_link_hostif(struct wx *wx, int speed, int autoneg, int duplex) +{ + struct txgbe_hic_ephy_setlink buffer; + + buffer.hdr.cmd = FW_PHY_SET_LINK_CMD; + buffer.hdr.buf_len = sizeof(struct txgbe_hic_ephy_setlink) - + sizeof(struct wx_hic_hdr); + buffer.hdr.cmd_or_resp.cmd_resv = FW_CEM_CMD_RESERVED; + + switch (speed) { + case SPEED_25000: + buffer.speed = TXGBE_LINK_SPEED_25GB_FULL; + break; + case SPEED_10000: + buffer.speed = TXGBE_LINK_SPEED_10GB_FULL; + break; + } + + buffer.fec_mode = TXGBE_PHY_FEC_AUTO; + buffer.autoneg = autoneg; + buffer.duplex = duplex; + + return wx_host_interface_command(wx, (u32 *)&buffer, sizeof(buffer), + WX_HI_COMMAND_TIMEOUT, true); +} + +static void txgbe_get_link_capabilities(struct wx *wx) +{ + struct txgbe *txgbe = wx->priv; + + if (test_bit(PHY_INTERFACE_MODE_25GBASER, txgbe->sfp_interfaces)) + wx->adv_speed = SPEED_25000; + else if (test_bit(PHY_INTERFACE_MODE_10GBASER, txgbe->sfp_interfaces)) + wx->adv_speed = SPEED_10000; + else + wx->adv_speed = SPEED_UNKNOWN; + + wx->adv_duplex = wx->adv_speed == SPEED_UNKNOWN ? + DUPLEX_HALF : DUPLEX_FULL; +} + static void txgbe_get_phy_link(struct wx *wx, int *speed) { u32 status; @@ -28,6 +118,120 @@ static void txgbe_get_phy_link(struct wx *wx, int *speed) *speed = SPEED_UNKNOWN; } +int txgbe_set_phy_link(struct wx *wx) +{ + int speed, err; + u32 gpio; + + /* Check RX signal */ + gpio = rd32(wx, WX_GPIO_EXT); + if (gpio & TXGBE_GPIOBIT_3) + return -ENODEV; + + txgbe_get_link_capabilities(wx); + if (wx->adv_speed == SPEED_UNKNOWN) + return -ENODEV; + + txgbe_get_phy_link(wx, &speed); + if (speed == wx->adv_speed) + return 0; + + err = txgbe_set_phy_link_hostif(wx, wx->adv_speed, 0, wx->adv_duplex); + if (err) { + wx_err(wx, "Failed to setup link\n"); + return err; + } + + return 0; +} + +static int txgbe_sfp_to_linkmodes(struct wx *wx, struct txgbe_sfp_id *id) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(modes) = { 0, }; + DECLARE_PHY_INTERFACE_MASK(interfaces); + struct txgbe *txgbe = wx->priv; + + if (id->com_25g_code & (TXGBE_SFF_25GBASESR_CAPABLE | + TXGBE_SFF_25GBASEER_CAPABLE | + TXGBE_SFF_25GBASELR_CAPABLE)) { + phylink_set(modes, 25000baseSR_Full); + __set_bit(PHY_INTERFACE_MODE_25GBASER, interfaces); + } + if (id->com_10g_code & TXGBE_SFF_10GBASESR_CAPABLE) { + phylink_set(modes, 10000baseSR_Full); + __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces); + } + if (id->com_10g_code & TXGBE_SFF_10GBASELR_CAPABLE) { + phylink_set(modes, 10000baseLR_Full); + __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces); + } + + if (phy_interface_empty(interfaces)) { + wx_err(wx, "unsupported SFP module\n"); + return -EINVAL; + } + + phylink_set(modes, Pause); + phylink_set(modes, Asym_Pause); + phylink_set(modes, FIBRE); + txgbe->link_port = PORT_FIBRE; + + if (!linkmode_equal(txgbe->sfp_support, modes)) { + linkmode_copy(txgbe->sfp_support, modes); + phy_interface_and(txgbe->sfp_interfaces, + wx->phylink_config.supported_interfaces, + interfaces); + linkmode_copy(txgbe->advertising, modes); + + set_bit(WX_FLAG_NEED_LINK_CONFIG, wx->flags); + } + + return 0; +} + +int txgbe_identify_sfp(struct wx *wx) +{ + struct txgbe_hic_i2c_read buffer; + struct txgbe_sfp_id *id; + int err = 0; + u32 gpio; + + gpio = rd32(wx, WX_GPIO_EXT); + if (gpio & TXGBE_GPIOBIT_2) + return -ENODEV; + + err = txgbe_identify_sfp_hostif(wx, &buffer); + if (err) { + wx_err(wx, "Failed to identify SFP module\n"); + return err; + } + + id = &buffer.id; + if (id->identifier != TXGBE_SFF_IDENTIFIER_SFP) { + wx_err(wx, "Invalid SFP module\n"); + return -ENODEV; + } + + err = txgbe_sfp_to_linkmodes(wx, id); + if (err) + return err; + + if (gpio & TXGBE_GPIOBIT_3) + set_bit(WX_FLAG_NEED_LINK_CONFIG, wx->flags); + + return 0; +} + +void txgbe_setup_link(struct wx *wx) +{ + struct txgbe *txgbe = wx->priv; + + phy_interface_zero(txgbe->sfp_interfaces); + linkmode_zero(txgbe->sfp_support); + + txgbe_identify_sfp(wx); +} + static void txgbe_get_link_state(struct phylink_config *config, struct phylink_link_state *state) { diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.h index aa3b7848e03d..2376a021ba8d 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.h @@ -4,6 +4,11 @@ #ifndef _TXGBE_AML_H_ #define _TXGBE_AML_H_ +void txgbe_gpio_init_aml(struct wx *wx); +irqreturn_t txgbe_gpio_irq_handler_aml(int irq, void *data); +int txgbe_set_phy_link(struct wx *wx); +int txgbe_identify_sfp(struct wx *wx); +void txgbe_setup_link(struct wx *wx); int txgbe_phylink_init_aml(struct txgbe *txgbe); #endif /* _TXGBE_AML_H_ */ diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c index f53a5d00a41b..05fe8fd43b80 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c @@ -11,6 +11,7 @@ #include "txgbe_type.h" #include "txgbe_phy.h" #include "txgbe_irq.h" +#include "txgbe_aml.h" /** * txgbe_irq_enable - Enable default interrupt generation settings @@ -19,7 +20,14 @@ **/ void txgbe_irq_enable(struct wx *wx, bool queues) { - wr32(wx, WX_PX_MISC_IEN, TXGBE_PX_MISC_IEN_MASK); + u32 misc_ien = TXGBE_PX_MISC_IEN_MASK; + + if (wx->mac.type == wx_mac_aml) { + misc_ien |= TXGBE_PX_MISC_GPIO; + txgbe_gpio_init_aml(wx); + } + + wr32(wx, WX_PX_MISC_IEN, misc_ien); /* unmask interrupt */ wx_intr_enable(wx, TXGBE_INTR_MISC); @@ -81,6 +89,14 @@ static int txgbe_request_link_irq(struct txgbe *txgbe) IRQF_ONESHOT, "txgbe-link-irq", txgbe); } +static int txgbe_request_gpio_irq(struct txgbe *txgbe) +{ + txgbe->gpio_irq = irq_find_mapping(txgbe->misc.domain, TXGBE_IRQ_GPIO); + return request_threaded_irq(txgbe->gpio_irq, NULL, + txgbe_gpio_irq_handler_aml, + IRQF_ONESHOT, "txgbe-gpio-irq", txgbe); +} + static const struct irq_chip txgbe_irq_chip = { .name = "txgbe-misc-irq", }; @@ -157,6 +173,11 @@ static irqreturn_t txgbe_misc_irq_thread_fn(int irq, void *data) handle_nested_irq(sub_irq); nhandled++; } + if (eicr & TXGBE_PX_MISC_GPIO) { + sub_irq = irq_find_mapping(txgbe->misc.domain, TXGBE_IRQ_GPIO); + handle_nested_irq(sub_irq); + nhandled++; + } wx_intr_enable(wx, TXGBE_INTR_MISC); return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE); @@ -179,6 +200,9 @@ void txgbe_free_misc_irq(struct txgbe *txgbe) if (txgbe->wx->mac.type == wx_mac_aml40) return; + if (txgbe->wx->mac.type == wx_mac_aml) + free_irq(txgbe->gpio_irq, txgbe); + free_irq(txgbe->link_irq, txgbe); free_irq(txgbe->misc.irq, txgbe); txgbe_del_irq_domain(txgbe); @@ -222,11 +246,20 @@ int txgbe_setup_misc_irq(struct txgbe *txgbe) if (err) goto free_msic_irq; + if (wx->mac.type == wx_mac_sp) + goto skip_sp_irq; + + err = txgbe_request_gpio_irq(txgbe); + if (err) + goto free_link_irq; + skip_sp_irq: wx->misc_irq_domain = true; return 0; +free_link_irq: + free_irq(txgbe->link_irq, txgbe); free_msic_irq: free_irq(txgbe->misc.irq, txgbe); del_misc_irq: diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index ca3dbc448646..6f3b67def51a 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -21,6 +21,7 @@ #include "txgbe_type.h" #include "txgbe_hw.h" #include "txgbe_phy.h" +#include "txgbe_aml.h" #include "txgbe_irq.h" #include "txgbe_fdir.h" #include "txgbe_ethtool.h" @@ -88,6 +89,58 @@ static int txgbe_enumerate_functions(struct wx *wx) return physfns; } +static void txgbe_sfp_detection_subtask(struct wx *wx) +{ + int err; + + if (!test_bit(WX_FLAG_NEED_SFP_RESET, wx->flags)) + return; + + /* wait for SFP module ready */ + msleep(200); + + err = txgbe_identify_sfp(wx); + if (err) + return; + + clear_bit(WX_FLAG_NEED_SFP_RESET, wx->flags); +} + +static void txgbe_link_config_subtask(struct wx *wx) +{ + int err; + + if (!test_bit(WX_FLAG_NEED_LINK_CONFIG, wx->flags)) + return; + + err = txgbe_set_phy_link(wx); + if (err) + return; + + clear_bit(WX_FLAG_NEED_LINK_CONFIG, wx->flags); +} + +/** + * txgbe_service_task - manages and runs subtasks + * @work: pointer to work_struct containing our data + **/ +static void txgbe_service_task(struct work_struct *work) +{ + struct wx *wx = container_of(work, struct wx, service_task); + + txgbe_sfp_detection_subtask(wx); + txgbe_link_config_subtask(wx); + + wx_service_event_complete(wx); +} + +static void txgbe_init_service(struct wx *wx) +{ + timer_setup(&wx->service_timer, wx_service_timer, 0); + INIT_WORK(&wx->service_task, txgbe_service_task); + clear_bit(WX_STATE_SERVICE_SCHED, wx->state); +} + static void txgbe_up_complete(struct wx *wx) { struct net_device *netdev = wx->netdev; @@ -110,6 +163,11 @@ static void txgbe_up_complete(struct wx *wx) netif_carrier_on(wx->netdev); break; case wx_mac_aml: + /* Enable TX laser */ + wr32m(wx, WX_GPIO_DR, TXGBE_GPIOBIT_1, 0); + txgbe_setup_link(wx); + phylink_start(wx->phylink); + break; case wx_mac_sp: phylink_start(wx->phylink); break; @@ -125,6 +183,7 @@ static void txgbe_up_complete(struct wx *wx) /* enable transmits */ netif_tx_start_all_queues(netdev); + mod_timer(&wx->service_timer, jiffies); /* Set PF Reset Done bit so PF/VF Mail Ops can work */ wr32m(wx, WX_CFG_PORT_CTL, WX_CFG_PORT_CTL_PFRSTD, @@ -173,6 +232,8 @@ static void txgbe_disable_device(struct wx *wx) wx_irq_disable(wx); wx_napi_disable_all(wx); + timer_delete_sync(&wx->service_timer); + if (wx->bus.func < 2) wr32m(wx, TXGBE_MIS_PRB_CTL, TXGBE_MIS_PRB_CTL_LAN_UP(wx->bus.func), 0); else @@ -218,6 +279,10 @@ void txgbe_down(struct wx *wx) netif_carrier_off(wx->netdev); break; case wx_mac_aml: + phylink_stop(wx->phylink); + /* Disable TX laser */ + wr32m(wx, WX_GPIO_DR, TXGBE_GPIOBIT_1, TXGBE_GPIOBIT_1); + break; case wx_mac_sp: phylink_stop(wx->phylink); break; @@ -752,9 +817,11 @@ static int txgbe_probe(struct pci_dev *pdev, eth_hw_addr_set(netdev, wx->mac.perm_addr); wx_mac_set_default_filter(wx, wx->mac.perm_addr); + txgbe_init_service(wx); + err = wx_init_interrupt_scheme(wx); if (err) - goto err_free_mac_table; + goto err_cancel_service; /* Save off EEPROM version number and Option Rom version which * together make a unique identify for the eeprom @@ -847,6 +914,9 @@ err_free_misc_irq: err_release_hw: wx_clear_interrupt_scheme(wx); wx_control_hw(wx, false); +err_cancel_service: + timer_delete_sync(&wx->service_timer); + cancel_work_sync(&wx->service_task); err_free_mac_table: kfree(wx->rss_key); kfree(wx->mac_table); @@ -873,6 +943,8 @@ static void txgbe_remove(struct pci_dev *pdev) struct txgbe *txgbe = wx->priv; struct net_device *netdev; + cancel_work_sync(&wx->service_task); + netdev = wx->netdev; wx_disable_sriov(wx); unregister_netdev(netdev); diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h index ca4da2696eed..98bd25254c80 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h @@ -6,6 +6,8 @@ #include #include +#include +#include "../libwx/wx_type.h" /* Device IDs */ #define TXGBE_DEV_ID_SP1000 0x1001 @@ -311,6 +313,72 @@ void txgbe_up(struct wx *wx); int txgbe_setup_tc(struct net_device *dev, u8 tc); void txgbe_do_reset(struct net_device *netdev); +#define TXGBE_LINK_SPEED_10GB_FULL 4 +#define TXGBE_LINK_SPEED_25GB_FULL 0x10 + +#define TXGBE_SFF_IDENTIFIER_SFP 0x3 +#define TXGBE_SFF_DA_PASSIVE_CABLE 0x4 +#define TXGBE_SFF_DA_ACTIVE_CABLE 0x8 +#define TXGBE_SFF_DA_SPEC_ACTIVE_LIMIT 0x4 +#define TXGBE_SFF_FCPI4_LIMITING 0x3 +#define TXGBE_SFF_10GBASESR_CAPABLE 0x10 +#define TXGBE_SFF_10GBASELR_CAPABLE 0x20 +#define TXGBE_SFF_25GBASESR_CAPABLE 0x2 +#define TXGBE_SFF_25GBASELR_CAPABLE 0x3 +#define TXGBE_SFF_25GBASEER_CAPABLE 0x4 +#define TXGBE_SFF_25GBASECR_91FEC 0xB +#define TXGBE_SFF_25GBASECR_74FEC 0xC +#define TXGBE_SFF_25GBASECR_NOFEC 0xD + +#define TXGBE_PHY_FEC_RS BIT(0) +#define TXGBE_PHY_FEC_BASER BIT(1) +#define TXGBE_PHY_FEC_OFF BIT(2) +#define TXGBE_PHY_FEC_AUTO (TXGBE_PHY_FEC_OFF | \ + TXGBE_PHY_FEC_BASER |\ + TXGBE_PHY_FEC_RS) + +#define FW_PHY_GET_LINK_CMD 0xC0 +#define FW_PHY_SET_LINK_CMD 0xC1 +#define FW_READ_SFP_INFO_CMD 0xC5 + +struct txgbe_sfp_id { + u8 identifier; /* A0H 0x00 */ + u8 com_1g_code; /* A0H 0x06 */ + u8 com_10g_code; /* A0H 0x03 */ + u8 com_25g_code; /* A0H 0x24 */ + u8 cable_spec; /* A0H 0x3C */ + u8 cable_tech; /* A0H 0x08 */ + u8 vendor_oui0; /* A0H 0x25 */ + u8 vendor_oui1; /* A0H 0x26 */ + u8 vendor_oui2; /* A0H 0x27 */ + u8 reserved[3]; +}; + +struct txgbe_hic_i2c_read { + struct wx_hic_hdr hdr; + struct txgbe_sfp_id id; +}; + +struct txgbe_hic_ephy_setlink { + struct wx_hic_hdr hdr; + u8 speed; + u8 duplex; + u8 autoneg; + u8 fec_mode; + u8 resv[4]; +}; + +struct txgbe_hic_ephy_getlink { + struct wx_hic_hdr hdr; + u8 speed; + u8 duplex; + u8 autoneg; + u8 flow_ctl; + u8 power; + u8 fec_mode; + u8 resv[6]; +}; + #define NODE_PROP(_NAME, _PROP) \ (const struct software_node) { \ .name = _NAME, \ @@ -348,6 +416,7 @@ struct txgbe_nodes { enum txgbe_misc_irqs { TXGBE_IRQ_LINK = 0, + TXGBE_IRQ_GPIO, TXGBE_IRQ_MAX }; @@ -369,6 +438,7 @@ struct txgbe { struct clk *clk; struct gpio_chip *gpio; unsigned int link_irq; + unsigned int gpio_irq; u32 eicr; /* flow director */ @@ -376,6 +446,11 @@ struct txgbe { union txgbe_atr_input fdir_mask; int fdir_filter_count; spinlock_t fdir_perfect_lock; /* spinlock for FDIR */ + + DECLARE_PHY_INTERFACE_MASK(sfp_interfaces); + __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); + __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising); + u8 link_port; }; #endif /* _TXGBE_TYPE_H_ */ -- 2.51.0 From c0f2e5113e2f073e88ea18e8912a9ee2558a0d6f Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Wed, 21 May 2025 14:43:59 +0800 Subject: [PATCH 11/16] net: txgbe: Correct the currect link settings For AML 25G/10G devices, some of the information returned from phylink_ethtool_ksettings_get() is not correct, since there is a fixed-link mode. So add additional corrections. Signed-off-by: Jiawen Wu Reviewed-by: Simon Horman Link: https://patch.msgid.link/C94BF867617C544D+20250521064402.22348-7-jiawenwu@trustnetic.com Signed-off-by: Paolo Abeni --- .../net/ethernet/wangxun/libwx/wx_ethtool.c | 3 --- .../ethernet/wangxun/txgbe/txgbe_ethtool.c | 27 ++++++++++++++++++- .../ethernet/wangxun/txgbe/txgbe_ethtool.h | 2 ++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c index 86c0159e8a2d..c12a4cb951f6 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c @@ -231,9 +231,6 @@ int wx_get_link_ksettings(struct net_device *netdev, { struct wx *wx = netdev_priv(netdev); - if (wx->mac.type == wx_mac_aml40) - return -EOPNOTSUPP; - return phylink_ethtool_ksettings_get(wx->phylink, cmd); } EXPORT_SYMBOL(wx_get_link_ksettings); diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c index 78999d484f18..fa770961df5f 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c @@ -12,6 +12,31 @@ #include "txgbe_fdir.h" #include "txgbe_ethtool.h" +int txgbe_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) +{ + struct wx *wx = netdev_priv(netdev); + struct txgbe *txgbe = wx->priv; + int err; + + if (wx->mac.type == wx_mac_aml40) + return -EOPNOTSUPP; + + err = wx_get_link_ksettings(netdev, cmd); + if (err) + return err; + + if (wx->mac.type == wx_mac_sp) + return 0; + + cmd->base.port = txgbe->link_port; + cmd->base.autoneg = AUTONEG_DISABLE; + linkmode_copy(cmd->link_modes.supported, txgbe->sfp_support); + linkmode_copy(cmd->link_modes.advertising, txgbe->advertising); + + return 0; +} + static int txgbe_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring, struct kernel_ethtool_ringparam *kernel_ring, @@ -510,7 +535,7 @@ static const struct ethtool_ops txgbe_ethtool_ops = { .get_drvinfo = wx_get_drvinfo, .nway_reset = wx_nway_reset, .get_link = ethtool_op_get_link, - .get_link_ksettings = wx_get_link_ksettings, + .get_link_ksettings = txgbe_get_link_ksettings, .set_link_ksettings = wx_set_link_ksettings, .get_sset_count = wx_get_sset_count, .get_strings = wx_get_strings, diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.h index ace1b3571012..66dbc8ec1bb6 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.h @@ -4,6 +4,8 @@ #ifndef _TXGBE_ETHTOOL_H_ #define _TXGBE_ETHTOOL_H_ +int txgbe_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd); void txgbe_set_ethtool_ops(struct net_device *netdev); #endif /* _TXGBE_ETHTOOL_H_ */ -- 2.51.0 From d84a3ff9aae815c51c40b9b61e1bf26b7256eebf Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Wed, 21 May 2025 14:44:00 +0800 Subject: [PATCH 12/16] net: txgbe: Restrict the use of mismatched FW versions The new added mailbox commands require a new released firmware version. Otherwise, a lot of logs "Unknown FW command" would be printed. And the devices may not work properly. So add the test command in the probe function. Signed-off-by: Jiawen Wu Reviewed-by: Simon Horman Link: https://patch.msgid.link/18283F17BE0FA335+20250521064402.22348-8-jiawenwu@trustnetic.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c | 16 ++++++++++++++++ drivers/net/ethernet/wangxun/txgbe/txgbe_aml.h | 1 + drivers/net/ethernet/wangxun/txgbe/txgbe_main.c | 7 +++++++ 3 files changed, 24 insertions(+) diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c index af12ebb89c71..83b383021790 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c @@ -50,6 +50,22 @@ irqreturn_t txgbe_gpio_irq_handler_aml(int irq, void *data) return IRQ_HANDLED; } +int txgbe_test_hostif(struct wx *wx) +{ + struct txgbe_hic_ephy_getlink buffer; + + if (wx->mac.type != wx_mac_aml) + return 0; + + buffer.hdr.cmd = FW_PHY_GET_LINK_CMD; + buffer.hdr.buf_len = sizeof(struct txgbe_hic_ephy_getlink) - + sizeof(struct wx_hic_hdr); + buffer.hdr.cmd_or_resp.cmd_resv = FW_CEM_CMD_RESERVED; + + return wx_host_interface_command(wx, (u32 *)&buffer, sizeof(buffer), + WX_HI_COMMAND_TIMEOUT, true); +} + static int txgbe_identify_sfp_hostif(struct wx *wx, struct txgbe_hic_i2c_read *buffer) { buffer->hdr.cmd = FW_READ_SFP_INFO_CMD; diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.h index 2376a021ba8d..25d4971ca0d9 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.h @@ -6,6 +6,7 @@ void txgbe_gpio_init_aml(struct wx *wx); irqreturn_t txgbe_gpio_irq_handler_aml(int irq, void *data); +int txgbe_test_hostif(struct wx *wx); int txgbe_set_phy_link(struct wx *wx); int txgbe_identify_sfp(struct wx *wx); void txgbe_setup_link(struct wx *wx); diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index 6f3b67def51a..f3d2778b8e35 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -864,6 +864,13 @@ static int txgbe_probe(struct pci_dev *pdev, if (etrack_id < 0x20010) dev_warn(&pdev->dev, "Please upgrade the firmware to 0x20010 or above.\n"); + err = txgbe_test_hostif(wx); + if (err != 0) { + dev_err(&pdev->dev, "Mismatched Firmware version\n"); + err = -EIO; + goto err_release_hw; + } + txgbe = devm_kzalloc(&pdev->dev, sizeof(*txgbe), GFP_KERNEL); if (!txgbe) { err = -ENOMEM; -- 2.51.0 From 182af02690fbb3ba1015cba1e5e758d2ecab94f0 Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Wed, 21 May 2025 14:44:01 +0800 Subject: [PATCH 13/16] net: txgbe: Implement PTP for AML devices Support PTP clock and 1PPS output signal for AML devices. Signed-off-by: Jiawen Wu Link: https://patch.msgid.link/F2F6E5E8899D2C20+20250521064402.22348-9-jiawenwu@trustnetic.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/wangxun/libwx/wx_ptp.c | 30 ++++++++++++++++--- .../net/ethernet/wangxun/txgbe/txgbe_aml.c | 6 ++++ .../net/ethernet/wangxun/txgbe/txgbe_irq.c | 5 ++++ .../net/ethernet/wangxun/txgbe/txgbe_type.h | 3 +- 4 files changed, 39 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ptp.c b/drivers/net/ethernet/wangxun/libwx/wx_ptp.c index 07c015ba338f..2c39b879f977 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_ptp.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_ptp.c @@ -15,12 +15,14 @@ #define WX_INCVAL_100 0xA00000 #define WX_INCVAL_10 0xC7F380 #define WX_INCVAL_EM 0x2000000 +#define WX_INCVAL_AML 0xA00000 #define WX_INCVAL_SHIFT_10GB 20 #define WX_INCVAL_SHIFT_1GB 18 #define WX_INCVAL_SHIFT_100 15 #define WX_INCVAL_SHIFT_10 12 #define WX_INCVAL_SHIFT_EM 22 +#define WX_INCVAL_SHIFT_AML 21 #define WX_OVERFLOW_PERIOD (HZ * 30) #define WX_PTP_TX_TIMEOUT (HZ) @@ -504,15 +506,27 @@ static long wx_ptp_create_clock(struct wx *wx) wx->ptp_caps.gettimex64 = wx_ptp_gettimex64; wx->ptp_caps.settime64 = wx_ptp_settime64; wx->ptp_caps.do_aux_work = wx_ptp_do_aux_work; - if (wx->mac.type == wx_mac_em) { - wx->ptp_caps.max_adj = 500000000; + switch (wx->mac.type) { + case wx_mac_aml: + case wx_mac_aml40: + wx->ptp_caps.max_adj = 250000000; wx->ptp_caps.n_per_out = 1; wx->ptp_setup_sdp = wx_ptp_setup_sdp; wx->ptp_caps.enable = wx_ptp_feature_enable; - } else { + break; + case wx_mac_sp: wx->ptp_caps.max_adj = 250000000; wx->ptp_caps.n_per_out = 0; wx->ptp_setup_sdp = NULL; + break; + case wx_mac_em: + wx->ptp_caps.max_adj = 500000000; + wx->ptp_caps.n_per_out = 1; + wx->ptp_setup_sdp = wx_ptp_setup_sdp; + wx->ptp_caps.enable = wx_ptp_feature_enable; + break; + default: + return -EOPNOTSUPP; } wx->ptp_clock = ptp_clock_register(&wx->ptp_caps, &wx->pdev->dev); @@ -647,10 +661,18 @@ static u64 wx_ptp_read(const struct cyclecounter *hw_cc) static void wx_ptp_link_speed_adjust(struct wx *wx, u32 *shift, u32 *incval) { - if (wx->mac.type == wx_mac_em) { + switch (wx->mac.type) { + case wx_mac_aml: + case wx_mac_aml40: + *shift = WX_INCVAL_SHIFT_AML; + *incval = WX_INCVAL_AML; + return; + case wx_mac_em: *shift = WX_INCVAL_SHIFT_EM; *incval = WX_INCVAL_EM; return; + default: + break; } switch (wx->speed) { diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c index 83b383021790..6bcf67bef576 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c @@ -8,6 +8,7 @@ #include "../libwx/wx_type.h" #include "../libwx/wx_lib.h" +#include "../libwx/wx_ptp.h" #include "../libwx/wx_hw.h" #include "txgbe_type.h" #include "txgbe_aml.h" @@ -311,6 +312,9 @@ static void txgbe_mac_link_up_aml(struct phylink_config *config, wr32(wx, TXGBE_AML_MAC_TX_CFG, txcfg | TXGBE_AML_MAC_TX_CFG_TE); wx->speed = speed; + wx->last_rx_ptp_check = jiffies; + if (test_bit(WX_STATE_PTP_RUNNING, wx->state)) + wx_ptp_reset_cyclecounter(wx); } static void txgbe_mac_link_down_aml(struct phylink_config *config, @@ -323,6 +327,8 @@ static void txgbe_mac_link_down_aml(struct phylink_config *config, wr32m(wx, WX_MAC_RX_CFG, WX_MAC_RX_CFG_RE, 0); wx->speed = SPEED_UNKNOWN; + if (test_bit(WX_STATE_PTP_RUNNING, wx->state)) + wx_ptp_reset_cyclecounter(wx); } static void txgbe_mac_config_aml(struct phylink_config *config, unsigned int mode, diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c index 05fe8fd43b80..dfc3a2cc27f6 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c @@ -6,6 +6,7 @@ #include "../libwx/wx_type.h" #include "../libwx/wx_lib.h" +#include "../libwx/wx_ptp.h" #include "../libwx/wx_hw.h" #include "../libwx/wx_sriov.h" #include "txgbe_type.h" @@ -178,6 +179,10 @@ static irqreturn_t txgbe_misc_irq_thread_fn(int irq, void *data) handle_nested_irq(sub_irq); nhandled++; } + if (unlikely(eicr & TXGBE_PX_MISC_IC_TIMESYNC)) { + wx_ptp_check_pps_event(wx); + nhandled++; + } wx_intr_enable(wx, TXGBE_INTR_MISC); return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE); diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h index 98bd25254c80..7a00e3343be6 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h @@ -82,6 +82,7 @@ /* Extended Interrupt Enable Set */ #define TXGBE_PX_MISC_ETH_LKDN BIT(8) #define TXGBE_PX_MISC_DEV_RST BIT(10) +#define TXGBE_PX_MISC_IC_TIMESYNC BIT(11) #define TXGBE_PX_MISC_ETH_EVENT BIT(17) #define TXGBE_PX_MISC_ETH_LK BIT(18) #define TXGBE_PX_MISC_ETH_AN BIT(19) @@ -92,7 +93,7 @@ (TXGBE_PX_MISC_ETH_LKDN | TXGBE_PX_MISC_DEV_RST | \ TXGBE_PX_MISC_ETH_EVENT | TXGBE_PX_MISC_ETH_LK | \ TXGBE_PX_MISC_ETH_AN | TXGBE_PX_MISC_INT_ERR | \ - TXGBE_PX_MISC_IC_VF_MBOX) + TXGBE_PX_MISC_IC_VF_MBOX | TXGBE_PX_MISC_IC_TIMESYNC) /* Port cfg registers */ #define TXGBE_CFG_PORT_ST 0x14404 -- 2.51.0 From cdae5bccab29a8e7810b682e886dfd8cd5a52ff3 Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Wed, 21 May 2025 14:44:02 +0800 Subject: [PATCH 14/16] net: txgbe: Implement SRIOV for AML devices Since .mac_link_up and .mac_link_down are changed for AML 25G/10G NICs, the SR-IOV related function should be invoked in these new functions, to bring VFs link up. Signed-off-by: Jiawen Wu Link: https://patch.msgid.link/BA8B302B7AAB6EA6+20250521064402.22348-10-jiawenwu@trustnetic.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c index 6bcf67bef576..7dbcf41750c1 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c @@ -10,6 +10,7 @@ #include "../libwx/wx_lib.h" #include "../libwx/wx_ptp.h" #include "../libwx/wx_hw.h" +#include "../libwx/wx_sriov.h" #include "txgbe_type.h" #include "txgbe_aml.h" #include "txgbe_hw.h" @@ -315,6 +316,8 @@ static void txgbe_mac_link_up_aml(struct phylink_config *config, wx->last_rx_ptp_check = jiffies; if (test_bit(WX_STATE_PTP_RUNNING, wx->state)) wx_ptp_reset_cyclecounter(wx); + /* ping all the active vfs to let them know we are going up */ + wx_ping_all_vfs_with_link_status(wx, true); } static void txgbe_mac_link_down_aml(struct phylink_config *config, @@ -329,6 +332,8 @@ static void txgbe_mac_link_down_aml(struct phylink_config *config, wx->speed = SPEED_UNKNOWN; if (test_bit(WX_STATE_PTP_RUNNING, wx->state)) wx_ptp_reset_cyclecounter(wx); + /* ping all the active vfs to let them know we are going down */ + wx_ping_all_vfs_with_link_status(wx, false); } static void txgbe_mac_config_aml(struct phylink_config *config, unsigned int mode, -- 2.51.0 From 2d13b45f8086a7142260cf19f4976b1e767fdcec Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Wed, 21 May 2025 09:16:36 +0200 Subject: [PATCH 15/16] dt-bindings: net: airoha: Add EN7581 memory-region property Introduce memory-region and memory-region-names properties for the ethernet node available on EN7581 SoC in order to reserve system memory for hw forwarding buffers queue used by the QDMA modules. Signed-off-by: Lorenzo Bianconi Acked-by: Conor Dooley Link: https://patch.msgid.link/20250521-airopha-desc-sram-v3-1-a6e9b085b4f0@kernel.org Signed-off-by: Paolo Abeni --- .../devicetree/bindings/net/airoha,en7581-eth.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml b/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml index 0fdd11265417..6d22131ac2f9 100644 --- a/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml +++ b/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml @@ -57,6 +57,16 @@ properties: - const: hsi-mac - const: xfp-mac + memory-region: + items: + - description: QDMA0 buffer memory + - description: QDMA1 buffer memory + + memory-region-names: + items: + - const: qdma0-buf + - const: qdma1-buf + "#address-cells": const: 1 @@ -140,6 +150,9 @@ examples: , ; + memory-region = <&qdma0_buf>, <&qdma1_buf>; + memory-region-names = "qdma0-buf", "qdma1-buf"; + airoha,npu = <&npu>; #address-cells = <1>; -- 2.51.0 From 09aa788f98da3e2f41ce158cc691d6d52e808bc9 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Wed, 21 May 2025 09:16:37 +0200 Subject: [PATCH 16/16] net: airoha: Do not store hfwd references in airoha_qdma struct Since hfwd descriptor and buffer queues are allocated via dmam_alloc_coherent() we do not need to store their references in airoha_qdma struct. This patch does not introduce any logical changes, just code clean-up. Signed-off-by: Lorenzo Bianconi Reviewed-by: Simon Horman Link: https://patch.msgid.link/20250521-airopha-desc-sram-v3-2-a6e9b085b4f0@kernel.org Signed-off-by: Paolo Abeni --- drivers/net/ethernet/airoha/airoha_eth.c | 8 ++------ drivers/net/ethernet/airoha/airoha_eth.h | 6 ------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index 0d627e511266..1ed43a5c629f 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -1077,17 +1077,13 @@ static int airoha_qdma_init_hfwd_queues(struct airoha_qdma *qdma) int size; size = HW_DSCP_NUM * sizeof(struct airoha_qdma_fwd_desc); - qdma->hfwd.desc = dmam_alloc_coherent(eth->dev, size, &dma_addr, - GFP_KERNEL); - if (!qdma->hfwd.desc) + if (!dmam_alloc_coherent(eth->dev, size, &dma_addr, GFP_KERNEL)) return -ENOMEM; airoha_qdma_wr(qdma, REG_FWD_DSCP_BASE, dma_addr); size = AIROHA_MAX_PACKET_SIZE * HW_DSCP_NUM; - qdma->hfwd.q = dmam_alloc_coherent(eth->dev, size, &dma_addr, - GFP_KERNEL); - if (!qdma->hfwd.q) + if (!dmam_alloc_coherent(eth->dev, size, &dma_addr, GFP_KERNEL)) return -ENOMEM; airoha_qdma_wr(qdma, REG_FWD_BUF_BASE, dma_addr); diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h index 531a3c49c156..3e03ae9a5d0d 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.h +++ b/drivers/net/ethernet/airoha/airoha_eth.h @@ -513,12 +513,6 @@ struct airoha_qdma { struct airoha_queue q_tx[AIROHA_NUM_TX_RING]; struct airoha_queue q_rx[AIROHA_NUM_RX_RING]; - - /* descriptor and packet buffers for qdma hw forward */ - struct { - void *desc; - void *q; - } hfwd; }; struct airoha_gdm_port { -- 2.51.0