Message ID | 20240512085611.79747-6-fujita.tomonori@gmail.com (mailing list archive) |
---|---|
State | Changes Requested |
Delegated to: | Netdev Maintainers |
Headers | show |
Series | add ethernet driver for Tehuti Networks TN40xx chips | expand |
> -----Original Message----- > From: FUJITA Tomonori <fujita.tomonori@gmail.com> > Sent: Sunday, May 12, 2024 2:26 PM > To: netdev@vger.kernel.org > Cc: andrew@lunn.ch; horms@kernel.org; kuba@kernel.org; jiri@resnulli.us; > pabeni@redhat.com; linux@armlinux.org.uk; hfdevel@gmx.net > Subject: [PATCH net-next v6 5/6] net: tn40xx: add mdio bus support > > This patch adds supports for mdio bus. A later path adds PHYLIB support on the > top of this. > > Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com> > --- > drivers/net/ethernet/tehuti/Makefile | 2 +- > drivers/net/ethernet/tehuti/tn40.c | 6 + > drivers/net/ethernet/tehuti/tn40.h | 3 + > drivers/net/ethernet/tehuti/tn40_mdio.c | 140 ++++++++++++++++++++++++ > 4 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 > drivers/net/ethernet/tehuti/tn40_mdio.c > > 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 4ddbdb21f1ed..38b2a1fe501a 100644 > --- a/drivers/net/ethernet/tehuti/tn40.c > +++ b/drivers/net/ethernet/tehuti/tn40.c > @@ -1764,6 +1764,12 @@ static int tn40_probe(struct pci_dev *pdev, const > struct pci_device_id *ent) > goto err_unset_drvdata; > } > > + ret = tn40_mdiobus_init(priv); > + if (ret) { > + dev_err(&pdev->dev, "failed to initialize mdio bus.\n"); > + goto err_free_irq; > + } > + > priv->stats_flag = > ((tn40_read_reg(priv, TN40_FPGA_VER) & 0xFFF) != 308); > > diff --git a/drivers/net/ethernet/tehuti/tn40.h b/drivers/net/ethernet/tehuti/tn40.h > index 6efa268ea0e2..e5e3610f9b8f 100644 > --- a/drivers/net/ethernet/tehuti/tn40.h > +++ b/drivers/net/ethernet/tehuti/tn40.h > @@ -158,6 +158,7 @@ struct tn40_priv { > char *b0_va; /* Virtual address of buffer */ > > struct tn40_rx_page_table rx_page_table; > + struct mii_bus *mdio; > }; > > /* RX FREE descriptor - 64bit */ > @@ -237,4 +238,6 @@ static inline void tn40_write_reg(struct tn40_priv *priv, > u32 reg, u32 val) > writel(val, priv->regs + reg); > } > > +int tn40_mdiobus_init(struct tn40_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..b0d559229ea3 > --- /dev/null > +++ b/drivers/net/ethernet/tehuti/tn40_mdio.c > @@ -0,0 +1,140 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* Copyright (c) Tehuti Networks Ltd. */ > + > +#include <linux/netdevice.h> > +#include <linux/pci.h> > +#include <linux/phylink.h> > + > +#include "tn40.h" > + > +#define TN40_MDIO_DEVAD_MASK GENMASK(4, 0) #define > TN40_MDIO_PRTAD_MASK > +GENMASK(9, 5) > +#define TN40_MDIO_CMD_VAL(device, port) \ > + (FIELD_PREP(TN40_MDIO_DEVAD_MASK, (device)) | \ > + (FIELD_PREP(TN40_MDIO_PRTAD_MASK, (port)))) #define > +TN40_MDIO_CMD_READ BIT(15) > + > +static void tn40_mdio_set_speed(struct tn40_priv *priv, u32 speed) { > + void __iomem *regs = priv->regs; > + int mdio_cfg; > + > + mdio_cfg = readl(regs + TN40_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 + TN40_REG_MDIO_CMD_STAT); > + msleep(100); > +} > + > +static u32 tn40_mdio_stat(struct tn40_priv *priv) { > + void __iomem *regs = priv->regs; > + > + return readl(regs + TN40_REG_MDIO_CMD_STAT); } > + > +static int tn40_mdio_get(struct tn40_priv *priv, u32 *val) { The argument "val" is not used inside this function. > + u32 stat; > + > + return readx_poll_timeout_atomic(tn40_mdio_stat, priv, stat, > + TN40_GET_MDIO_BUSY(stat) == 0, 10, > + 10000); > +} > + > +static int tn40_mdio_read(struct tn40_priv *priv, int port, int device, > + u16 regnum) > +{ > + void __iomem *regs = priv->regs; > + u32 tmp_reg, i; > + > + /* wait until MDIO is not busy */ > + if (tn40_mdio_get(priv, NULL)) > + return -EIO; > + > + i = TN40_MDIO_CMD_VAL(device, port); > + writel(i, regs + TN40_REG_MDIO_CMD); > + writel((u32)regnum, regs + TN40_REG_MDIO_ADDR); > + if (tn40_mdio_get(priv, NULL)) > + return -EIO; > + > + writel(TN40_MDIO_CMD_READ | i, regs + TN40_REG_MDIO_CMD); > + /* read CMD_STAT until not busy */ > + if (tn40_mdio_get(priv, NULL)) > + return -EIO; > + > + tmp_reg = readl(regs + TN40_REG_MDIO_DATA); > + return lower_16_bits(tmp_reg); > +} > + > +static int tn40_mdio_write(struct tn40_priv *priv, int port, int device, > + u16 regnum, u16 data) > +{ > + void __iomem *regs = priv->regs; > + u32 tmp_reg = 0; > + int ret; > + > + /* wait until MDIO is not busy */ > + if (tn40_mdio_get(priv, NULL)) > + return -EIO; > + writel(TN40_MDIO_CMD_VAL(device, port), regs + > TN40_REG_MDIO_CMD); > + writel((u32)regnum, regs + TN40_REG_MDIO_ADDR); > + if (tn40_mdio_get(priv, NULL)) > + return -EIO; > + writel((u32)data, regs + TN40_REG_MDIO_DATA); > + /* read CMD_STAT until not busy */ > + ret = tn40_mdio_get(priv, &tmp_reg); Is "tn40_mdio_get()" supposed to return any status in tmp_reg (which is missing?). Thanks, Naveen > + if (ret) > + return -EIO; > + > + if (TN40_GET_MDIO_RD_ERR(tmp_reg)) { > + dev_err(&priv->pdev->dev, "MDIO error after write command\n"); > + return -EIO; > + } > + return 0; > +} > + > +static int tn40_mdio_read_cb(struct mii_bus *mii_bus, int addr, int devnum, > + int regnum) > +{ > + return tn40_mdio_read(mii_bus->priv, addr, devnum, regnum); } > + > +static int tn40_mdio_write_cb(struct mii_bus *mii_bus, int addr, int devnum, > + int regnum, u16 val) > +{ > + return tn40_mdio_write(mii_bus->priv, addr, devnum, regnum, val); } > + > +int tn40_mdiobus_init(struct tn40_priv *priv) { > + struct pci_dev *pdev = priv->pdev; > + struct mii_bus *bus; > + int ret; > + > + bus = devm_mdiobus_alloc(&pdev->dev); > + if (!bus) > + return -ENOMEM; > + > + bus->name = TN40_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 = tn40_mdio_read_cb; > + bus->write_c45 = tn40_mdio_write_cb; > + > + 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; > + } > + tn40_mdio_set_speed(priv, TN40_MDIO_SPEED_6MHZ); > + priv->mdio = bus; > + return 0; > +} > -- > 2.34.1 >
Hi, On Mon, 13 May 2024 09:40:08 +0000 Naveen Mamindlapalli <naveenm@marvell.com> wrote: >> +static u32 tn40_mdio_stat(struct tn40_priv *priv) { >> + void __iomem *regs = priv->regs; >> + >> + return readl(regs + TN40_REG_MDIO_CMD_STAT); } >> + >> +static int tn40_mdio_get(struct tn40_priv *priv, u32 *val) { > > The argument "val" is not used inside this function. Oops, I'll fix in v7. >> +static int tn40_mdio_write(struct tn40_priv *priv, int port, int device, >> + u16 regnum, u16 data) >> +{ >> + void __iomem *regs = priv->regs; >> + u32 tmp_reg = 0; >> + int ret; >> + >> + /* wait until MDIO is not busy */ >> + if (tn40_mdio_get(priv, NULL)) >> + return -EIO; >> + writel(TN40_MDIO_CMD_VAL(device, port), regs + >> TN40_REG_MDIO_CMD); >> + writel((u32)regnum, regs + TN40_REG_MDIO_ADDR); >> + if (tn40_mdio_get(priv, NULL)) >> + return -EIO; >> + writel((u32)data, regs + TN40_REG_MDIO_DATA); >> + /* read CMD_STAT until not busy */ >> + ret = tn40_mdio_get(priv, &tmp_reg); > > Is "tn40_mdio_get()" supposed to return any status in tmp_reg (which is missing?). Indeed. Thanks a lot!
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 4ddbdb21f1ed..38b2a1fe501a 100644 --- a/drivers/net/ethernet/tehuti/tn40.c +++ b/drivers/net/ethernet/tehuti/tn40.c @@ -1764,6 +1764,12 @@ static int tn40_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_unset_drvdata; } + ret = tn40_mdiobus_init(priv); + if (ret) { + dev_err(&pdev->dev, "failed to initialize mdio bus.\n"); + goto err_free_irq; + } + priv->stats_flag = ((tn40_read_reg(priv, TN40_FPGA_VER) & 0xFFF) != 308); diff --git a/drivers/net/ethernet/tehuti/tn40.h b/drivers/net/ethernet/tehuti/tn40.h index 6efa268ea0e2..e5e3610f9b8f 100644 --- a/drivers/net/ethernet/tehuti/tn40.h +++ b/drivers/net/ethernet/tehuti/tn40.h @@ -158,6 +158,7 @@ struct tn40_priv { char *b0_va; /* Virtual address of buffer */ struct tn40_rx_page_table rx_page_table; + struct mii_bus *mdio; }; /* RX FREE descriptor - 64bit */ @@ -237,4 +238,6 @@ static inline void tn40_write_reg(struct tn40_priv *priv, u32 reg, u32 val) writel(val, priv->regs + reg); } +int tn40_mdiobus_init(struct tn40_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..b0d559229ea3 --- /dev/null +++ b/drivers/net/ethernet/tehuti/tn40_mdio.c @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (c) Tehuti Networks Ltd. */ + +#include <linux/netdevice.h> +#include <linux/pci.h> +#include <linux/phylink.h> + +#include "tn40.h" + +#define TN40_MDIO_DEVAD_MASK GENMASK(4, 0) +#define TN40_MDIO_PRTAD_MASK GENMASK(9, 5) +#define TN40_MDIO_CMD_VAL(device, port) \ + (FIELD_PREP(TN40_MDIO_DEVAD_MASK, (device)) | \ + (FIELD_PREP(TN40_MDIO_PRTAD_MASK, (port)))) +#define TN40_MDIO_CMD_READ BIT(15) + +static void tn40_mdio_set_speed(struct tn40_priv *priv, u32 speed) +{ + void __iomem *regs = priv->regs; + int mdio_cfg; + + mdio_cfg = readl(regs + TN40_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 + TN40_REG_MDIO_CMD_STAT); + msleep(100); +} + +static u32 tn40_mdio_stat(struct tn40_priv *priv) +{ + void __iomem *regs = priv->regs; + + return readl(regs + TN40_REG_MDIO_CMD_STAT); +} + +static int tn40_mdio_get(struct tn40_priv *priv, u32 *val) +{ + u32 stat; + + return readx_poll_timeout_atomic(tn40_mdio_stat, priv, stat, + TN40_GET_MDIO_BUSY(stat) == 0, 10, + 10000); +} + +static int tn40_mdio_read(struct tn40_priv *priv, int port, int device, + u16 regnum) +{ + void __iomem *regs = priv->regs; + u32 tmp_reg, i; + + /* wait until MDIO is not busy */ + if (tn40_mdio_get(priv, NULL)) + return -EIO; + + i = TN40_MDIO_CMD_VAL(device, port); + writel(i, regs + TN40_REG_MDIO_CMD); + writel((u32)regnum, regs + TN40_REG_MDIO_ADDR); + if (tn40_mdio_get(priv, NULL)) + return -EIO; + + writel(TN40_MDIO_CMD_READ | i, regs + TN40_REG_MDIO_CMD); + /* read CMD_STAT until not busy */ + if (tn40_mdio_get(priv, NULL)) + return -EIO; + + tmp_reg = readl(regs + TN40_REG_MDIO_DATA); + return lower_16_bits(tmp_reg); +} + +static int tn40_mdio_write(struct tn40_priv *priv, int port, int device, + u16 regnum, u16 data) +{ + void __iomem *regs = priv->regs; + u32 tmp_reg = 0; + int ret; + + /* wait until MDIO is not busy */ + if (tn40_mdio_get(priv, NULL)) + return -EIO; + writel(TN40_MDIO_CMD_VAL(device, port), regs + TN40_REG_MDIO_CMD); + writel((u32)regnum, regs + TN40_REG_MDIO_ADDR); + if (tn40_mdio_get(priv, NULL)) + return -EIO; + writel((u32)data, regs + TN40_REG_MDIO_DATA); + /* read CMD_STAT until not busy */ + ret = tn40_mdio_get(priv, &tmp_reg); + if (ret) + return -EIO; + + if (TN40_GET_MDIO_RD_ERR(tmp_reg)) { + dev_err(&priv->pdev->dev, "MDIO error after write command\n"); + return -EIO; + } + return 0; +} + +static int tn40_mdio_read_cb(struct mii_bus *mii_bus, int addr, int devnum, + int regnum) +{ + return tn40_mdio_read(mii_bus->priv, addr, devnum, regnum); +} + +static int tn40_mdio_write_cb(struct mii_bus *mii_bus, int addr, int devnum, + int regnum, u16 val) +{ + return tn40_mdio_write(mii_bus->priv, addr, devnum, regnum, val); +} + +int tn40_mdiobus_init(struct tn40_priv *priv) +{ + struct pci_dev *pdev = priv->pdev; + struct mii_bus *bus; + int ret; + + bus = devm_mdiobus_alloc(&pdev->dev); + if (!bus) + return -ENOMEM; + + bus->name = TN40_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 = tn40_mdio_read_cb; + bus->write_c45 = tn40_mdio_write_cb; + + 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; + } + tn40_mdio_set_speed(priv, TN40_MDIO_SPEED_6MHZ); + priv->mdio = bus; + return 0; +}
This patch adds supports for mdio bus. A later path adds PHYLIB support on the top of this. Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com> --- drivers/net/ethernet/tehuti/Makefile | 2 +- drivers/net/ethernet/tehuti/tn40.c | 6 + drivers/net/ethernet/tehuti/tn40.h | 3 + drivers/net/ethernet/tehuti/tn40_mdio.c | 140 ++++++++++++++++++++++++ 4 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/tehuti/tn40_mdio.c