1*f76aef98SCharles Perry // SPDX-License-Identifier: GPL-2.0 2*f76aef98SCharles Perry /* Microchip PIC64-HPSC/HX MDIO controller driver 3*f76aef98SCharles Perry * 4*f76aef98SCharles Perry * Copyright (c) 2026 Microchip Technology Inc. and its subsidiaries. 5*f76aef98SCharles Perry */ 6*f76aef98SCharles Perry 7*f76aef98SCharles Perry #include <linux/bitops.h> 8*f76aef98SCharles Perry #include <linux/clk.h> 9*f76aef98SCharles Perry #include <linux/io.h> 10*f76aef98SCharles Perry #include <linux/iopoll.h> 11*f76aef98SCharles Perry #include <linux/kernel.h> 12*f76aef98SCharles Perry #include <linux/module.h> 13*f76aef98SCharles Perry #include <linux/of_mdio.h> 14*f76aef98SCharles Perry #include <linux/platform_device.h> 15*f76aef98SCharles Perry 16*f76aef98SCharles Perry #define MDIO_REG_PRESCALER 0x20 17*f76aef98SCharles Perry #define MDIO_CFG_PRESCALE_MASK GENMASK(7, 0) 18*f76aef98SCharles Perry 19*f76aef98SCharles Perry #define MDIO_REG_FRAME_CFG_1 0x24 20*f76aef98SCharles Perry #define MDIO_WDATA_MASK GENMASK(15, 0) 21*f76aef98SCharles Perry 22*f76aef98SCharles Perry #define MDIO_REG_FRAME_CFG_2 0x28 23*f76aef98SCharles Perry #define MDIO_TRIGGER_BIT BIT(31) 24*f76aef98SCharles Perry #define MDIO_REG_DEV_ADDR_MASK GENMASK(20, 16) 25*f76aef98SCharles Perry #define MDIO_PHY_PRT_ADDR_MASK GENMASK(8, 4) 26*f76aef98SCharles Perry #define MDIO_OPERATION_MASK GENMASK(3, 2) 27*f76aef98SCharles Perry #define MDIO_START_OF_FRAME_MASK GENMASK(1, 0) 28*f76aef98SCharles Perry 29*f76aef98SCharles Perry /* Possible value of MDIO_OPERATION_MASK */ 30*f76aef98SCharles Perry #define MDIO_OPERATION_WRITE BIT(0) 31*f76aef98SCharles Perry #define MDIO_OPERATION_READ BIT(1) 32*f76aef98SCharles Perry 33*f76aef98SCharles Perry #define MDIO_REG_FRAME_STATUS 0x2C 34*f76aef98SCharles Perry #define MDIO_READOK_BIT BIT(24) 35*f76aef98SCharles Perry #define MDIO_RDATA_MASK GENMASK(15, 0) 36*f76aef98SCharles Perry 37*f76aef98SCharles Perry struct pic64hpsc_mdio_dev { 38*f76aef98SCharles Perry void __iomem *regs; 39*f76aef98SCharles Perry }; 40*f76aef98SCharles Perry 41*f76aef98SCharles Perry static int pic64hpsc_mdio_wait_trigger(struct mii_bus *bus) 42*f76aef98SCharles Perry { 43*f76aef98SCharles Perry struct pic64hpsc_mdio_dev *priv = bus->priv; 44*f76aef98SCharles Perry u32 val; 45*f76aef98SCharles Perry int ret; 46*f76aef98SCharles Perry 47*f76aef98SCharles Perry /* The MDIO_TRIGGER bit returns 0 when a transaction has completed. */ 48*f76aef98SCharles Perry ret = readl_poll_timeout(priv->regs + MDIO_REG_FRAME_CFG_2, val, 49*f76aef98SCharles Perry !(val & MDIO_TRIGGER_BIT), 50, 10000); 50*f76aef98SCharles Perry 51*f76aef98SCharles Perry if (ret < 0) 52*f76aef98SCharles Perry dev_dbg(&bus->dev, "TRIGGER bit timeout: %x\n", val); 53*f76aef98SCharles Perry 54*f76aef98SCharles Perry return ret; 55*f76aef98SCharles Perry } 56*f76aef98SCharles Perry 57*f76aef98SCharles Perry static int pic64hpsc_mdio_c22_read(struct mii_bus *bus, int mii_id, int regnum) 58*f76aef98SCharles Perry { 59*f76aef98SCharles Perry struct pic64hpsc_mdio_dev *priv = bus->priv; 60*f76aef98SCharles Perry u32 val; 61*f76aef98SCharles Perry int ret; 62*f76aef98SCharles Perry 63*f76aef98SCharles Perry ret = pic64hpsc_mdio_wait_trigger(bus); 64*f76aef98SCharles Perry if (ret) 65*f76aef98SCharles Perry return ret; 66*f76aef98SCharles Perry 67*f76aef98SCharles Perry writel(MDIO_TRIGGER_BIT | FIELD_PREP(MDIO_REG_DEV_ADDR_MASK, regnum) | 68*f76aef98SCharles Perry FIELD_PREP(MDIO_PHY_PRT_ADDR_MASK, mii_id) | 69*f76aef98SCharles Perry FIELD_PREP(MDIO_OPERATION_MASK, MDIO_OPERATION_READ) | 70*f76aef98SCharles Perry FIELD_PREP(MDIO_START_OF_FRAME_MASK, 1), 71*f76aef98SCharles Perry priv->regs + MDIO_REG_FRAME_CFG_2); 72*f76aef98SCharles Perry 73*f76aef98SCharles Perry ret = pic64hpsc_mdio_wait_trigger(bus); 74*f76aef98SCharles Perry if (ret) 75*f76aef98SCharles Perry return ret; 76*f76aef98SCharles Perry 77*f76aef98SCharles Perry val = readl(priv->regs + MDIO_REG_FRAME_STATUS); 78*f76aef98SCharles Perry 79*f76aef98SCharles Perry /* The MDIO_READOK is a 1-bit value reflecting the inverse of the MDIO 80*f76aef98SCharles Perry * bus value captured during the 2nd TA cycle. A PHY/Port should drive 81*f76aef98SCharles Perry * the MDIO bus with a logic 0 on the 2nd TA cycle, however, the 82*f76aef98SCharles Perry * PHY/Port could optionally drive a logic 1, to communicate a read 83*f76aef98SCharles Perry * failure. This feature is optional, not defined by the 802.3 standard 84*f76aef98SCharles Perry * and not supported in standard external PHYs. 85*f76aef98SCharles Perry */ 86*f76aef98SCharles Perry if (!(bus->phy_ignore_ta_mask & 1 << mii_id) && 87*f76aef98SCharles Perry !FIELD_GET(MDIO_READOK_BIT, val)) { 88*f76aef98SCharles Perry dev_dbg(&bus->dev, "READOK bit cleared\n"); 89*f76aef98SCharles Perry return -EIO; 90*f76aef98SCharles Perry } 91*f76aef98SCharles Perry 92*f76aef98SCharles Perry return FIELD_GET(MDIO_RDATA_MASK, val); 93*f76aef98SCharles Perry } 94*f76aef98SCharles Perry 95*f76aef98SCharles Perry static int pic64hpsc_mdio_c22_write(struct mii_bus *bus, int mii_id, int regnum, 96*f76aef98SCharles Perry u16 value) 97*f76aef98SCharles Perry { 98*f76aef98SCharles Perry struct pic64hpsc_mdio_dev *priv = bus->priv; 99*f76aef98SCharles Perry int ret; 100*f76aef98SCharles Perry 101*f76aef98SCharles Perry ret = pic64hpsc_mdio_wait_trigger(bus); 102*f76aef98SCharles Perry if (ret < 0) 103*f76aef98SCharles Perry return ret; 104*f76aef98SCharles Perry 105*f76aef98SCharles Perry writel(FIELD_PREP(MDIO_WDATA_MASK, value), 106*f76aef98SCharles Perry priv->regs + MDIO_REG_FRAME_CFG_1); 107*f76aef98SCharles Perry 108*f76aef98SCharles Perry writel(MDIO_TRIGGER_BIT | FIELD_PREP(MDIO_REG_DEV_ADDR_MASK, regnum) | 109*f76aef98SCharles Perry FIELD_PREP(MDIO_PHY_PRT_ADDR_MASK, mii_id) | 110*f76aef98SCharles Perry FIELD_PREP(MDIO_OPERATION_MASK, MDIO_OPERATION_WRITE) | 111*f76aef98SCharles Perry FIELD_PREP(MDIO_START_OF_FRAME_MASK, 1), 112*f76aef98SCharles Perry priv->regs + MDIO_REG_FRAME_CFG_2); 113*f76aef98SCharles Perry 114*f76aef98SCharles Perry return 0; 115*f76aef98SCharles Perry } 116*f76aef98SCharles Perry 117*f76aef98SCharles Perry static int pic64hpsc_mdio_probe(struct platform_device *pdev) 118*f76aef98SCharles Perry { 119*f76aef98SCharles Perry struct device_node *np = pdev->dev.of_node; 120*f76aef98SCharles Perry struct device *dev = &pdev->dev; 121*f76aef98SCharles Perry struct pic64hpsc_mdio_dev *priv; 122*f76aef98SCharles Perry struct mii_bus *bus; 123*f76aef98SCharles Perry unsigned long rate; 124*f76aef98SCharles Perry struct clk *clk; 125*f76aef98SCharles Perry u32 bus_freq; 126*f76aef98SCharles Perry u32 div; 127*f76aef98SCharles Perry int ret; 128*f76aef98SCharles Perry 129*f76aef98SCharles Perry bus = devm_mdiobus_alloc_size(dev, sizeof(*priv)); 130*f76aef98SCharles Perry if (!bus) 131*f76aef98SCharles Perry return -ENOMEM; 132*f76aef98SCharles Perry 133*f76aef98SCharles Perry priv = bus->priv; 134*f76aef98SCharles Perry 135*f76aef98SCharles Perry priv->regs = devm_platform_ioremap_resource(pdev, 0); 136*f76aef98SCharles Perry if (IS_ERR(priv->regs)) 137*f76aef98SCharles Perry return PTR_ERR(priv->regs); 138*f76aef98SCharles Perry 139*f76aef98SCharles Perry bus->name = KBUILD_MODNAME; 140*f76aef98SCharles Perry bus->read = pic64hpsc_mdio_c22_read; 141*f76aef98SCharles Perry bus->write = pic64hpsc_mdio_c22_write; 142*f76aef98SCharles Perry snprintf(bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev)); 143*f76aef98SCharles Perry bus->parent = dev; 144*f76aef98SCharles Perry 145*f76aef98SCharles Perry clk = devm_clk_get_enabled(dev, NULL); 146*f76aef98SCharles Perry if (IS_ERR(clk)) 147*f76aef98SCharles Perry return PTR_ERR(clk); 148*f76aef98SCharles Perry 149*f76aef98SCharles Perry if (of_property_read_u32(np, "clock-frequency", &bus_freq)) 150*f76aef98SCharles Perry bus_freq = 2500000; 151*f76aef98SCharles Perry 152*f76aef98SCharles Perry rate = clk_get_rate(clk); 153*f76aef98SCharles Perry 154*f76aef98SCharles Perry div = DIV_ROUND_UP(rate, 2 * bus_freq) - 1; 155*f76aef98SCharles Perry if (div == 0 || div & ~MDIO_CFG_PRESCALE_MASK) { 156*f76aef98SCharles Perry dev_err(dev, "MDIO clock-frequency out of range\n"); 157*f76aef98SCharles Perry return -EINVAL; 158*f76aef98SCharles Perry } 159*f76aef98SCharles Perry 160*f76aef98SCharles Perry dev_dbg(dev, "rate=%lu bus_freq=%u real_bus_freq=%lu div=%u\n", rate, 161*f76aef98SCharles Perry bus_freq, rate / (2 * (1 + div)), div); 162*f76aef98SCharles Perry writel(div, priv->regs + MDIO_REG_PRESCALER); 163*f76aef98SCharles Perry 164*f76aef98SCharles Perry ret = devm_of_mdiobus_register(dev, bus, np); 165*f76aef98SCharles Perry if (ret) { 166*f76aef98SCharles Perry dev_err(dev, "Cannot register MDIO bus (%d)\n", ret); 167*f76aef98SCharles Perry return ret; 168*f76aef98SCharles Perry } 169*f76aef98SCharles Perry 170*f76aef98SCharles Perry return 0; 171*f76aef98SCharles Perry } 172*f76aef98SCharles Perry 173*f76aef98SCharles Perry static const struct of_device_id pic64hpsc_mdio_match[] = { 174*f76aef98SCharles Perry { .compatible = "microchip,pic64hpsc-mdio" }, 175*f76aef98SCharles Perry {} 176*f76aef98SCharles Perry }; 177*f76aef98SCharles Perry MODULE_DEVICE_TABLE(of, pic64hpsc_mdio_match); 178*f76aef98SCharles Perry 179*f76aef98SCharles Perry static struct platform_driver pic64hpsc_mdio_driver = { 180*f76aef98SCharles Perry .probe = pic64hpsc_mdio_probe, 181*f76aef98SCharles Perry .driver = { 182*f76aef98SCharles Perry .name = KBUILD_MODNAME, 183*f76aef98SCharles Perry .of_match_table = pic64hpsc_mdio_match, 184*f76aef98SCharles Perry }, 185*f76aef98SCharles Perry }; 186*f76aef98SCharles Perry module_platform_driver(pic64hpsc_mdio_driver); 187*f76aef98SCharles Perry 188*f76aef98SCharles Perry MODULE_AUTHOR("Charles Perry <charles.perry@microchip.com>"); 189*f76aef98SCharles Perry MODULE_DESCRIPTION("Microchip PIC64-HPSC/HX MDIO driver"); 190*f76aef98SCharles Perry MODULE_LICENSE("GPL"); 191