This driver is the standalone variant of drivers/net/dsa/sja1105/sja1105_mdio.c. In terms of differences: - this one uses regmaps provided by the parent as a method to abstract away the sja1105_xfer_u32() calls for register access - the driver prefix has been changed from sja1105 to sja1110 (this MDIO controller is not present on the older SJA1105 family) - in the sja1105 driver, each memory word has 32 bits, so addresses as seen by regmap need to be multiplied by 4. This affects what sja1110_base_t1_encode_addr() returns, and is different compared to sja1105_base_t1_encode_addr(). Signed-off-by: Vladimir Oltean --- MAINTAINERS | 1 + drivers/net/mdio/Kconfig | 7 ++ drivers/net/mdio/Makefile | 1 + drivers/net/mdio/mdio-sja1110-cbt1.c | 173 +++++++++++++++++++++++++++ 4 files changed, 182 insertions(+) create mode 100644 drivers/net/mdio/mdio-sja1110-cbt1.c diff --git a/MAINTAINERS b/MAINTAINERS index 37f4278db851..c41b9d86c144 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18679,6 +18679,7 @@ M: Vladimir Oltean L: linux-kernel@vger.kernel.org S: Maintained F: drivers/net/dsa/sja1105 +F: drivers/net/mdio/mdio-sja1110-cbt1.c F: drivers/net/pcs/pcs-xpcs-nxp.c NXP TDA998X DRM DRIVER diff --git a/drivers/net/mdio/Kconfig b/drivers/net/mdio/Kconfig index 44380378911b..9819d1dc18de 100644 --- a/drivers/net/mdio/Kconfig +++ b/drivers/net/mdio/Kconfig @@ -136,6 +136,13 @@ config MDIO_MOXART This driver supports the MDIO interface found in the network interface units of the MOXA ART SoC +config MDIO_SJA1110_CBT1 + tristate "NXP SJA1110 100BASE-T1 MDIO bus" + help + This driver supports the MDIO controller embedded in the NXP SJA1110 + automotive Ethernet switches, which is used to access the internal + 100BASE-T1 PHYs over SPI. + config MDIO_OCTEON tristate "Octeon and some ThunderX SOCs MDIO buses" depends on (64BIT && OF_MDIO) || COMPILE_TEST diff --git a/drivers/net/mdio/Makefile b/drivers/net/mdio/Makefile index fbec636700e7..9abf20d1b030 100644 --- a/drivers/net/mdio/Makefile +++ b/drivers/net/mdio/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_MDIO_MVUSB) += mdio-mvusb.o obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o obj-$(CONFIG_MDIO_REALTEK_RTL9300) += mdio-realtek-rtl9300.o obj-$(CONFIG_MDIO_REGMAP) += mdio-regmap.o +obj-$(CONFIG_MDIO_SJA1110_CBT1) += mdio-sja1110-cbt1.o obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o obj-$(CONFIG_MDIO_THUNDER) += mdio-thunder.o obj-$(CONFIG_MDIO_XGENE) += mdio-xgene.o diff --git a/drivers/net/mdio/mdio-sja1110-cbt1.c b/drivers/net/mdio/mdio-sja1110-cbt1.c new file mode 100644 index 000000000000..a5f7830a6257 --- /dev/null +++ b/drivers/net/mdio/mdio-sja1110-cbt1.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright 2022 NXP + * + * NXP SJA1110 100BASE-T1 MDIO bus driver + */ +#include +#include +#include +#include +#include + +struct sja1110_base_t1_private { + struct regmap *regmap; + struct mii_bus *bus; + unsigned int base; +}; + +enum sja1110_mdio_opcode { + SJA1110_C45_ADDR = 0, + SJA1110_C22 = 1, + SJA1110_C45_DATA = 2, + SJA1110_C45_DATA_AUTOINC = 3, +}; + +static unsigned int sja1110_base_t1_encode_addr(unsigned int phy, + enum sja1110_mdio_opcode op, + unsigned int xad) +{ + return (phy << 9) | (op << 7) | (xad << 2); +} + +static int sja1110_base_t1_mdio_read_c22(struct mii_bus *bus, int phy, int reg) +{ + struct sja1110_base_t1_private *priv = bus->priv; + struct regmap *regmap = priv->regmap; + unsigned int addr, val; + int err; + + addr = sja1110_base_t1_encode_addr(phy, SJA1110_C22, reg & 0x1f); + + err = regmap_read(regmap, priv->base + addr, &val); + if (err) + return err; + + return val & 0xffff; +} + +static int sja1110_base_t1_mdio_read_c45(struct mii_bus *bus, int phy, + int mmd, int reg) +{ + struct sja1110_base_t1_private *priv = bus->priv; + struct regmap *regmap = priv->regmap; + unsigned int addr, val; + int err; + + addr = sja1110_base_t1_encode_addr(phy, SJA1110_C45_ADDR, mmd); + err = regmap_write(regmap, priv->base + addr, reg); + if (err) + return err; + + addr = sja1110_base_t1_encode_addr(phy, SJA1110_C45_DATA, mmd); + err = regmap_read(regmap, priv->base + addr, &val); + if (err) + return err; + + return val & 0xffff; +} + +static int sja1110_base_t1_mdio_write_c22(struct mii_bus *bus, int phy, int reg, + u16 val) +{ + struct sja1110_base_t1_private *priv = bus->priv; + struct regmap *regmap = priv->regmap; + unsigned int addr; + + addr = sja1110_base_t1_encode_addr(phy, SJA1110_C22, reg & 0x1f); + return regmap_write(regmap, priv->base + addr, val & 0xffff); +} + +static int sja1110_base_t1_mdio_write_c45(struct mii_bus *bus, int phy, + int mmd, int reg, u16 val) +{ + struct sja1110_base_t1_private *priv = bus->priv; + struct regmap *regmap = priv->regmap; + unsigned int addr; + int err; + + addr = sja1110_base_t1_encode_addr(phy, SJA1110_C45_ADDR, mmd); + err = regmap_write(regmap, priv->base + addr, reg); + if (err) + return err; + + addr = sja1110_base_t1_encode_addr(phy, SJA1110_C45_DATA, mmd); + return regmap_write(regmap, priv->base + addr, val & 0xffff); +} + +static int sja1110_base_t1_mdio_probe(struct platform_device *pdev) +{ + struct sja1110_base_t1_private *priv; + struct device *dev = &pdev->dev; + struct regmap *regmap; + struct resource *res; + struct mii_bus *bus; + int err; + + if (!dev->of_node || !dev->parent) + return -ENODEV; + + regmap = dev_get_regmap(dev->parent, NULL); + if (!regmap) + return -ENODEV; + + bus = mdiobus_alloc_size(sizeof(*priv)); + if (!bus) + return -ENOMEM; + + bus->name = "SJA1110 100base-T1 MDIO bus"; + snprintf(bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev)); + bus->read = sja1110_base_t1_mdio_read_c22; + bus->write = sja1110_base_t1_mdio_write_c22; + bus->read_c45 = sja1110_base_t1_mdio_read_c45; + bus->write_c45 = sja1110_base_t1_mdio_write_c45; + bus->parent = dev; + priv = bus->priv; + priv->regmap = regmap; + + res = platform_get_resource(pdev, IORESOURCE_REG, 0); + if (res) + priv->base = res->start; + + err = of_mdiobus_register(bus, dev->of_node); + if (err) + goto err_free_bus; + + priv->bus = bus; + platform_set_drvdata(pdev, priv); + + return 0; + +err_free_bus: + mdiobus_free(bus); + + return err; +} + +static void sja1110_base_t1_mdio_remove(struct platform_device *pdev) +{ + struct sja1110_base_t1_private *priv = platform_get_drvdata(pdev); + + mdiobus_unregister(priv->bus); + mdiobus_free(priv->bus); +} + +static const struct of_device_id sja1110_base_t1_mdio_match[] = { + { .compatible = "nxp,sja1110-base-t1-mdio", }, + {}, +}; +MODULE_DEVICE_TABLE(of, sja1110_base_t1_mdio_match); + +static struct platform_driver sja1110_base_t1_mdio_driver = { + .probe = sja1110_base_t1_mdio_probe, + .remove = sja1110_base_t1_mdio_remove, + .driver = { + .name = "sja1110-base-t1-mdio", + .of_match_table = sja1110_base_t1_mdio_match, + }, +}; + +module_platform_driver(sja1110_base_t1_mdio_driver); + +MODULE_DESCRIPTION("NXP SJA1110 100BASE-T1 MDIO bus driver"); +MODULE_AUTHOR("Vladimir Oltean "); +MODULE_LICENSE("GPL"); -- 2.34.1