diff options
| author | Jakub Kicinski <kuba@kernel.org> | 2026-04-13 00:19:25 +0300 |
|---|---|---|
| committer | Jakub Kicinski <kuba@kernel.org> | 2026-04-13 00:19:54 +0300 |
| commit | cf5389811ae61fa8749a443d822e9a374080b251 (patch) | |
| tree | 4af9fe08ea1a9559bb920f26bac908e197dce5a9 | |
| parent | 600f01dc4bd0c736b3ffea9f7976136d8bf1b136 (diff) | |
| parent | 96aefe3afe0e122a1472967224ffe21c3d51b67b (diff) | |
| download | linux-cf5389811ae61fa8749a443d822e9a374080b251.tar.xz | |
Merge branch 'add-support-for-pic64-hpsc-hx-mdio-controller'
Charles Perry says:
====================
Add support for PIC64-HPSC/HX MDIO controller
This series adds a driver for the two MDIO controllers of PIC64-HPSC/HX.
The hardware supports C22 and C45 but only C22 is implemented for now.
This MDIO hardware is based on a Microsemi design supported in Linux by
mdio-mscc-miim.c. However, The register interface is completely different
with pic64hpsc, hence the need for a separate driver.
The documentation recommends an input clock of 156.25MHz and a prescaler of
39, which yields an MDIO clock of 1.95MHz.
This was tested on Microchip HB1301 evalkit which has a VSC8574 and a
VSC8541. I've tested with bus frequencies of 0.6, 1.95 and 2.5 MHz.
This series also adds a PHY write barrier when disabling PHY interrupts as
discussed in: https://lore.kernel.org/acvUqDgepCIScs8M@shell.armlinux.org.uk
====================
Link: https://patch.msgid.link/20260408131821.1145334-1-charles.perry@microchip.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
| -rw-r--r-- | Documentation/devicetree/bindings/net/microchip,pic64hpsc-mdio.yaml | 68 | ||||
| -rw-r--r-- | MAINTAINERS | 6 | ||||
| -rw-r--r-- | drivers/net/mdio/Kconfig | 7 | ||||
| -rw-r--r-- | drivers/net/mdio/Makefile | 1 | ||||
| -rw-r--r-- | drivers/net/mdio/mdio-pic64hpsc.c | 190 | ||||
| -rw-r--r-- | drivers/net/phy/phy.c | 25 |
6 files changed, 296 insertions, 1 deletions
diff --git a/Documentation/devicetree/bindings/net/microchip,pic64hpsc-mdio.yaml b/Documentation/devicetree/bindings/net/microchip,pic64hpsc-mdio.yaml new file mode 100644 index 000000000000..20f29b71566b --- /dev/null +++ b/Documentation/devicetree/bindings/net/microchip,pic64hpsc-mdio.yaml @@ -0,0 +1,68 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/microchip,pic64hpsc-mdio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip PIC64-HPSC/HX MDIO controller + +maintainers: + - Charles Perry <charles.perry@microchip.com> + +description: + This is the MDIO bus controller present in Microchip PIC64-HPSC/HX SoCs. It + supports C22 and C45 register access and is named "MDIO Initiator" in the + documentation. + +allOf: + - $ref: mdio.yaml# + +properties: + compatible: + oneOf: + - const: microchip,pic64hpsc-mdio + - items: + - const: microchip,pic64hx-mdio + - const: microchip,pic64hpsc-mdio + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-frequency: + default: 2500000 + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - clocks + - interrupts + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + bus { + #address-cells = <2>; + #size-cells = <2>; + + mdio@4000c21e000 { + compatible = "microchip,pic64hpsc-mdio"; + reg = <0x400 0x0c21e000 0x0 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&svc_clk>; + interrupt-parent = <&saplic0>; + interrupts = <168 IRQ_TYPE_LEVEL_HIGH>; + + ethernet-phy@0 { + reg = <0>; + }; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index d8947c78de04..65902b97f5df 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17392,6 +17392,12 @@ L: linux-serial@vger.kernel.org S: Maintained F: drivers/tty/serial/8250/8250_pci1xxxx.c +MICROCHIP PIC64-HPSC/HX DRIVERS +M: Charles Perry <charles.perry@microchip.com> +S: Supported +F: Documentation/devicetree/bindings/net/microchip,pic64hpsc-mdio.yaml +F: drivers/net/mdio/mdio-pic64hpsc.c + MICROCHIP POLARFIRE FPGA DRIVERS M: Conor Dooley <conor.dooley@microchip.com> L: linux-fpga@vger.kernel.org diff --git a/drivers/net/mdio/Kconfig b/drivers/net/mdio/Kconfig index 53e2c047d804..516b0d05e16e 100644 --- a/drivers/net/mdio/Kconfig +++ b/drivers/net/mdio/Kconfig @@ -145,6 +145,13 @@ config MDIO_OCTEON buses. It is required by the Octeon and ThunderX ethernet device drivers on some systems. +config MDIO_PIC64HPSC + tristate "PIC64-HPSC/HX MDIO interface support" + depends on HAS_IOMEM && OF_MDIO + help + This driver supports the MDIO interface found on the PIC64-HPSC/HX + SoCs. + config MDIO_IPQ4019 tristate "Qualcomm IPQ4019 MDIO interface support" depends on HAS_IOMEM && OF_MDIO diff --git a/drivers/net/mdio/Makefile b/drivers/net/mdio/Makefile index fbec636700e7..048586746026 100644 --- a/drivers/net/mdio/Makefile +++ b/drivers/net/mdio/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_MDIO_MOXART) += mdio-moxart.o obj-$(CONFIG_MDIO_MSCC_MIIM) += mdio-mscc-miim.o obj-$(CONFIG_MDIO_MVUSB) += mdio-mvusb.o obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o +obj-$(CONFIG_MDIO_PIC64HPSC) += mdio-pic64hpsc.o obj-$(CONFIG_MDIO_REALTEK_RTL9300) += mdio-realtek-rtl9300.o obj-$(CONFIG_MDIO_REGMAP) += mdio-regmap.o obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o diff --git a/drivers/net/mdio/mdio-pic64hpsc.c b/drivers/net/mdio/mdio-pic64hpsc.c new file mode 100644 index 000000000000..28be77ad6cf7 --- /dev/null +++ b/drivers/net/mdio/mdio-pic64hpsc.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Microchip PIC64-HPSC/HX MDIO controller driver + * + * Copyright (c) 2026 Microchip Technology Inc. and its subsidiaries. + */ + +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_mdio.h> +#include <linux/platform_device.h> + +#define MDIO_REG_PRESCALER 0x20 +#define MDIO_CFG_PRESCALE_MASK GENMASK(7, 0) + +#define MDIO_REG_FRAME_CFG_1 0x24 +#define MDIO_WDATA_MASK GENMASK(15, 0) + +#define MDIO_REG_FRAME_CFG_2 0x28 +#define MDIO_TRIGGER_BIT BIT(31) +#define MDIO_REG_DEV_ADDR_MASK GENMASK(20, 16) +#define MDIO_PHY_PRT_ADDR_MASK GENMASK(8, 4) +#define MDIO_OPERATION_MASK GENMASK(3, 2) +#define MDIO_START_OF_FRAME_MASK GENMASK(1, 0) + +/* Possible value of MDIO_OPERATION_MASK */ +#define MDIO_OPERATION_WRITE BIT(0) +#define MDIO_OPERATION_READ BIT(1) + +#define MDIO_REG_FRAME_STATUS 0x2C +#define MDIO_READOK_BIT BIT(24) +#define MDIO_RDATA_MASK GENMASK(15, 0) + +struct pic64hpsc_mdio_dev { + void __iomem *regs; +}; + +static int pic64hpsc_mdio_wait_trigger(struct mii_bus *bus) +{ + struct pic64hpsc_mdio_dev *priv = bus->priv; + u32 val; + int ret; + + /* The MDIO_TRIGGER bit returns 0 when a transaction has completed. */ + ret = readl_poll_timeout(priv->regs + MDIO_REG_FRAME_CFG_2, val, + !(val & MDIO_TRIGGER_BIT), 50, 10000); + + if (ret < 0) + dev_dbg(&bus->dev, "TRIGGER bit timeout: %x\n", val); + + return ret; +} + +static int pic64hpsc_mdio_c22_read(struct mii_bus *bus, int mii_id, int regnum) +{ + struct pic64hpsc_mdio_dev *priv = bus->priv; + u32 val; + int ret; + + ret = pic64hpsc_mdio_wait_trigger(bus); + if (ret) + return ret; + + writel(MDIO_TRIGGER_BIT | FIELD_PREP(MDIO_REG_DEV_ADDR_MASK, regnum) | + FIELD_PREP(MDIO_PHY_PRT_ADDR_MASK, mii_id) | + FIELD_PREP(MDIO_OPERATION_MASK, MDIO_OPERATION_READ) | + FIELD_PREP(MDIO_START_OF_FRAME_MASK, 1), + priv->regs + MDIO_REG_FRAME_CFG_2); + + ret = pic64hpsc_mdio_wait_trigger(bus); + if (ret) + return ret; + + val = readl(priv->regs + MDIO_REG_FRAME_STATUS); + + /* The MDIO_READOK is a 1-bit value reflecting the inverse of the MDIO + * bus value captured during the 2nd TA cycle. A PHY/Port should drive + * the MDIO bus with a logic 0 on the 2nd TA cycle, however, the + * PHY/Port could optionally drive a logic 1, to communicate a read + * failure. This feature is optional, not defined by the 802.3 standard + * and not supported in standard external PHYs. + */ + if (!(bus->phy_ignore_ta_mask & 1 << mii_id) && + !FIELD_GET(MDIO_READOK_BIT, val)) { + dev_dbg(&bus->dev, "READOK bit cleared\n"); + return -EIO; + } + + return FIELD_GET(MDIO_RDATA_MASK, val); +} + +static int pic64hpsc_mdio_c22_write(struct mii_bus *bus, int mii_id, int regnum, + u16 value) +{ + struct pic64hpsc_mdio_dev *priv = bus->priv; + int ret; + + ret = pic64hpsc_mdio_wait_trigger(bus); + if (ret < 0) + return ret; + + writel(FIELD_PREP(MDIO_WDATA_MASK, value), + priv->regs + MDIO_REG_FRAME_CFG_1); + + writel(MDIO_TRIGGER_BIT | FIELD_PREP(MDIO_REG_DEV_ADDR_MASK, regnum) | + FIELD_PREP(MDIO_PHY_PRT_ADDR_MASK, mii_id) | + FIELD_PREP(MDIO_OPERATION_MASK, MDIO_OPERATION_WRITE) | + FIELD_PREP(MDIO_START_OF_FRAME_MASK, 1), + priv->regs + MDIO_REG_FRAME_CFG_2); + + return 0; +} + +static int pic64hpsc_mdio_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct pic64hpsc_mdio_dev *priv; + struct mii_bus *bus; + unsigned long rate; + struct clk *clk; + u32 bus_freq; + u32 div; + int ret; + + bus = devm_mdiobus_alloc_size(dev, sizeof(*priv)); + if (!bus) + return -ENOMEM; + + priv = bus->priv; + + priv->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->regs)) + return PTR_ERR(priv->regs); + + bus->name = KBUILD_MODNAME; + bus->read = pic64hpsc_mdio_c22_read; + bus->write = pic64hpsc_mdio_c22_write; + snprintf(bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev)); + bus->parent = dev; + + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + if (of_property_read_u32(np, "clock-frequency", &bus_freq)) + bus_freq = 2500000; + + rate = clk_get_rate(clk); + + div = DIV_ROUND_UP(rate, 2 * bus_freq) - 1; + if (div == 0 || div & ~MDIO_CFG_PRESCALE_MASK) { + dev_err(dev, "MDIO clock-frequency out of range\n"); + return -EINVAL; + } + + dev_dbg(dev, "rate=%lu bus_freq=%u real_bus_freq=%lu div=%u\n", rate, + bus_freq, rate / (2 * (1 + div)), div); + writel(div, priv->regs + MDIO_REG_PRESCALER); + + ret = devm_of_mdiobus_register(dev, bus, np); + if (ret) { + dev_err(dev, "Cannot register MDIO bus (%d)\n", ret); + return ret; + } + + return 0; +} + +static const struct of_device_id pic64hpsc_mdio_match[] = { + { .compatible = "microchip,pic64hpsc-mdio" }, + {} +}; +MODULE_DEVICE_TABLE(of, pic64hpsc_mdio_match); + +static struct platform_driver pic64hpsc_mdio_driver = { + .probe = pic64hpsc_mdio_probe, + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = pic64hpsc_mdio_match, + }, +}; +module_platform_driver(pic64hpsc_mdio_driver); + +MODULE_AUTHOR("Charles Perry <charles.perry@microchip.com>"); +MODULE_DESCRIPTION("Microchip PIC64-HPSC/HX MDIO driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 1f1039084f51..fce9bc7be330 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -1369,13 +1369,36 @@ void phy_error(struct phy_device *phydev) EXPORT_SYMBOL(phy_error); /** + * phy_write_barrier - ensure the last write completed for this PHY device + * @phydev: target phy_device struct + * + * MDIO bus controllers are not required to wait for write transactions to + * complete before returning. Calling this function ensures that the previous + * write has completed. + */ +static void phy_write_barrier(struct phy_device *phydev) +{ + if (mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, MII_PHYSID1) == + -EOPNOTSUPP) + mdiobus_c45_read(phydev->mdio.bus, phydev->mdio.addr, + MDIO_MMD_PMAPMD, MII_PHYSID1); +} + +/** * phy_disable_interrupts - Disable the PHY interrupts from the PHY side * @phydev: target phy_device struct */ int phy_disable_interrupts(struct phy_device *phydev) { + int err; + /* Disable PHY interrupts */ - return phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED); + err = phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED); + if (err) + return err; + + phy_write_barrier(phydev); + return 0; } /** |
