[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20240415104352.4685-6-fujita.tomonori@gmail.com>
Date: Mon, 15 Apr 2024 19:43:52 +0900
From: FUJITA Tomonori <fujita.tomonori@...il.com>
To: netdev@...r.kernel.org
Cc: andrew@...n.ch
Subject: [PATCH net-next v1 5/5] net: tn40xx: add PHYLIB support
This patch adds supports for multiple PHY hardware with PHYLIB. The
adapters with TN40xx chips use multiple PHY hardware; AMCC QT2025, TI
TLK10232, Aqrate AQR105, and Marvell 88X3120, 88X3310, and MV88E2010.
For now, the PCI ID table of this driver enables adapters using only
QT2025 PHY. I've tested this driver and the QT2025 PHY driver with
Edimax EN-9320 10G adapter.
Signed-off-by: FUJITA Tomonori <fujita.tomonori@...il.com>
---
drivers/net/ethernet/tehuti/Kconfig | 1 +
drivers/net/ethernet/tehuti/Makefile | 2 +-
drivers/net/ethernet/tehuti/tn40.c | 32 ++++++
drivers/net/ethernet/tehuti/tn40.h | 5 +
drivers/net/ethernet/tehuti/tn40_mdio.c | 141 ++++++++++++++++++++++++
5 files changed, 180 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/ethernet/tehuti/tn40_mdio.c
diff --git a/drivers/net/ethernet/tehuti/Kconfig b/drivers/net/ethernet/tehuti/Kconfig
index 4198fd59e42e..71f22471f9a0 100644
--- a/drivers/net/ethernet/tehuti/Kconfig
+++ b/drivers/net/ethernet/tehuti/Kconfig
@@ -27,6 +27,7 @@ config TEHUTI_TN40
tristate "Tehuti Networks TN40xx 10G Ethernet adapters"
depends on PCI
select FW_LOADER
+ select AMCC_QT2025_PHY
help
This driver supports 10G Ethernet adapters using Tehuti Networks
TN40xx chips. Currently, adapters with Applied Micro Circuits
diff --git a/drivers/net/ethernet/tehuti/Makefile b/drivers/net/ethernet/tehuti/Makefile
index 1c468d99e476..7a0fe586a243 100644
--- a/drivers/net/ethernet/tehuti/Makefile
+++ b/drivers/net/ethernet/tehuti/Makefile
@@ -5,5 +5,5 @@
obj-$(CONFIG_TEHUTI) += tehuti.o
-tn40xx-y := tn40.o
+tn40xx-y := tn40.o tn40_mdio.o
obj-$(CONFIG_TEHUTI_TN40) += tn40xx.o
diff --git a/drivers/net/ethernet/tehuti/tn40.c b/drivers/net/ethernet/tehuti/tn40.c
index c8ed9b743753..2c50295f4e68 100644
--- a/drivers/net/ethernet/tehuti/tn40.c
+++ b/drivers/net/ethernet/tehuti/tn40.c
@@ -1285,18 +1285,26 @@ static void bdx_link_changed(struct bdx_priv *priv)
if (priv->link_loop_cnt++ > LINK_LOOP_MAX) {
/* MAC reset */
bdx_set_link_speed(priv, 0);
+ bdx_set_link_speed(priv, priv->phydev->speed);
priv->link_loop_cnt = 0;
}
write_reg(priv, 0x5150, 1000000);
return;
}
+
+ if (!netif_carrier_ok(priv->ndev)) {
+ netif_wake_queue(priv->ndev);
+ phy_print_status(priv->phydev);
+ }
priv->link = link;
+ netif_carrier_on(priv->ndev);
}
static inline void bdx_isr_extra(struct bdx_priv *priv, u32 isr)
{
if (isr & (IR_LNKCHG0 | IR_LNKCHG1 | IR_TMR0)) {
netdev_dbg(priv->ndev, "isr = 0x%x\n", isr);
+ phy_mac_interrupt(priv->phydev);
bdx_link_changed(priv);
}
}
@@ -1580,23 +1588,42 @@ static int bdx_close(struct net_device *ndev)
bdx_disable_interrupts(priv);
free_irq(priv->pdev->irq, priv->ndev);
+ phy_stop(priv->phydev);
+ phy_disconnect(priv->phydev);
bdx_sw_reset(priv);
destroy_rx_ring(priv);
destroy_tx_ring(priv);
return 0;
}
+static void phy_handler(struct net_device *_dev)
+{
+}
+
static int bdx_open(struct net_device *dev)
{
struct bdx_priv *priv = netdev_priv(dev);
int ret;
bdx_sw_reset(priv);
+
+ ret = phy_connect_direct(priv->ndev, priv->phydev, phy_handler, PHY_INTERFACE_MODE_XAUI);
+ if (ret) {
+ netdev_err(dev, "failed to connect to phy %d\n", ret);
+ return ret;
+ }
+ phy_attached_info(priv->phydev);
+ phy_start(priv->phydev);
+
ret = bdx_start(priv);
if (ret) {
netdev_err(dev, "failed to start %d\n", ret);
+ phy_stop(priv->phydev);
+ phy_disconnect(priv->phydev);
return ret;
}
+ napi_enable(&priv->napi);
+ netif_start_queue(priv->ndev);
return 0;
}
@@ -1872,6 +1899,11 @@ static int bdx_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
priv->stats_flag = ((read_reg(priv, FPGA_VER) & 0xFFF) != 308);
+ ret = bdx_mdiobus_init(priv);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to find PHY.\n");
+ goto err_free_irq;
+ }
priv->isr_mask =
IR_RX_FREE_0 | IR_LNKCHG0 | IR_PSE | IR_TMR0 | IR_RX_DESC_0 |
IR_TX_FREE_0 | IR_TMR1;
diff --git a/drivers/net/ethernet/tehuti/tn40.h b/drivers/net/ethernet/tehuti/tn40.h
index fb43ebb5911f..06ab9a2cb42d 100644
--- a/drivers/net/ethernet/tehuti/tn40.h
+++ b/drivers/net/ethernet/tehuti/tn40.h
@@ -197,6 +197,9 @@ struct bdx_priv {
char *b0_va; /* Virtual address of buffer */
struct bdx_rx_page_table rx_page_table;
+
+ struct mii_bus *mdio;
+ struct phy_device *phydev;
};
/* RX FREE descriptor - 64bit */
@@ -283,4 +286,6 @@ static inline void write_reg(struct bdx_priv *priv, u32 reg, u32 val)
writel(val, priv->regs + reg);
}
+int bdx_mdiobus_init(struct bdx_priv *priv);
+
#endif /* _TN40XX_H */
diff --git a/drivers/net/ethernet/tehuti/tn40_mdio.c b/drivers/net/ethernet/tehuti/tn40_mdio.c
new file mode 100644
index 000000000000..f7f83c77e8b2
--- /dev/null
+++ b/drivers/net/ethernet/tehuti/tn40_mdio.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) Tehuti Networks Ltd. */
+
+#include "tn40.h"
+
+static u32 bdx_mdio_get(struct bdx_priv *priv)
+{
+ void __iomem *regs = priv->regs;
+
+#define BDX_MAX_MDIO_BUSY_LOOPS 1024
+ int tries = 0;
+
+ while (++tries < BDX_MAX_MDIO_BUSY_LOOPS) {
+ u32 mdio_cmd_stat = readl(regs + REG_MDIO_CMD_STAT);
+
+ if (GET_MDIO_BUSY(mdio_cmd_stat) == 0)
+ return mdio_cmd_stat;
+ }
+ dev_err(&priv->pdev->dev, "MDIO busy!\n");
+ return 0xFFFFFFFF;
+}
+
+static u16 bdx_mdio_read(struct bdx_priv *priv, int device, int port, u16 addr)
+{
+ void __iomem *regs = priv->regs;
+ u32 tmp_reg, i;
+ /* wait until MDIO is not busy */
+ if (bdx_mdio_get(priv) == 0xFFFFFFFF)
+ return -1;
+
+ i = ((device & 0x1F) | ((port & 0x1F) << 5));
+ writel(i, regs + REG_MDIO_CMD);
+ writel((u32)addr, regs + REG_MDIO_ADDR);
+ tmp_reg = bdx_mdio_get(priv);
+ if (tmp_reg == 0xFFFFFFFF)
+ return -1;
+
+ writel(((1 << 15) | i), regs + REG_MDIO_CMD);
+ /* read CMD_STAT until not busy */
+ tmp_reg = bdx_mdio_get(priv);
+ if (tmp_reg == 0xFFFFFFFF)
+ return -1;
+
+ if (GET_MDIO_RD_ERR(tmp_reg)) {
+ dev_dbg(&priv->pdev->dev, "MDIO error after read command\n");
+ return -1;
+ }
+ tmp_reg = readl(regs + REG_MDIO_DATA);
+
+ return (tmp_reg & 0xFFFF);
+}
+
+static int bdx_mdio_write(struct bdx_priv *priv, int device, int port, u16 addr,
+ u16 data)
+{
+ void __iomem *regs = priv->regs;
+ u32 tmp_reg;
+
+ /* wait until MDIO is not busy */
+ if (bdx_mdio_get(priv) == 0xFFFFFFFF)
+ return -1;
+ writel(((device & 0x1F) | ((port & 0x1F) << 5)), regs + REG_MDIO_CMD);
+ writel((u32)addr, regs + REG_MDIO_ADDR);
+ if (bdx_mdio_get(priv) == 0xFFFFFFFF)
+ return -1;
+ writel((u32)data, regs + REG_MDIO_DATA);
+ /* read CMD_STAT until not busy */
+ tmp_reg = bdx_mdio_get(priv);
+ if (tmp_reg == 0xFFFFFFFF)
+ return -1;
+
+ if (GET_MDIO_RD_ERR(tmp_reg)) {
+ dev_err(&priv->pdev->dev, "MDIO error after write command\n");
+ return -1;
+ }
+ return 0;
+}
+
+static void bdx_mdio_set_speed(struct bdx_priv *priv, u32 speed)
+{
+ void __iomem *regs = priv->regs;
+ int mdio_cfg;
+
+ mdio_cfg = readl(regs + REG_MDIO_CMD_STAT);
+ if (speed == 1)
+ mdio_cfg = (0x7d << 7) | 0x08; /* 1MHz */
+ else
+ mdio_cfg = 0xA08; /* 6MHz */
+ mdio_cfg |= (1 << 6);
+ writel(mdio_cfg, regs + REG_MDIO_CMD_STAT);
+ msleep(100);
+}
+
+static int mdio_read_reg(struct mii_bus *mii_bus, int addr, int devnum, int regnum)
+{
+ return bdx_mdio_read(mii_bus->priv, devnum, addr, regnum);
+}
+
+static int mdio_write_reg(struct mii_bus *mii_bus, int addr, int devnum, int regnum, u16 val)
+{
+ return bdx_mdio_write(mii_bus->priv, devnum, addr, regnum, val);
+}
+
+int bdx_mdiobus_init(struct bdx_priv *priv)
+{
+ struct pci_dev *pdev = priv->pdev;
+ struct mii_bus *bus;
+ struct phy_device *phydev;
+ int ret;
+
+ bus = devm_mdiobus_alloc(&pdev->dev);
+ if (!bus)
+ return -ENOMEM;
+
+ bus->name = BDX_DRV_NAME;
+ bus->parent = &pdev->dev;
+ snprintf(bus->id, MII_BUS_ID_SIZE, "tn40xx-%x-%x",
+ pci_domain_nr(pdev->bus), pci_dev_id(pdev));
+ bus->priv = priv;
+
+ bus->read_c45 = mdio_read_reg;
+ bus->write_c45 = mdio_write_reg;
+
+ ret = devm_mdiobus_register(&pdev->dev, bus);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register mdiobus %d %u %u\n",
+ ret, bus->state, MDIOBUS_UNREGISTERED);
+ return ret;
+ }
+
+ phydev = phy_find_first(bus);
+ if (!phydev) {
+ dev_err(&pdev->dev, "failed to find phy\n");
+ return -1;
+ }
+ phydev->irq = PHY_MAC_INTERRUPT;
+ priv->mdio = bus;
+ priv->phydev = phydev;
+ bdx_mdio_set_speed(priv, MDIO_SPEED_6MHZ);
+ return 0;
+}
--
2.34.1
Powered by blists - more mailing lists