19f35a734STimur Tabi /* 29f35a734STimur Tabi * QorIQ 10G MDIO Controller 39f35a734STimur Tabi * 49f35a734STimur Tabi * Copyright 2012 Freescale Semiconductor, Inc. 59f35a734STimur Tabi * 69f35a734STimur Tabi * Authors: Andy Fleming <afleming@freescale.com> 79f35a734STimur Tabi * Timur Tabi <timur@freescale.com> 89f35a734STimur Tabi * 99f35a734STimur Tabi * This file is licensed under the terms of the GNU General Public License 109f35a734STimur Tabi * version 2. This program is licensed "as is" without any warranty of any 119f35a734STimur Tabi * kind, whether express or implied. 129f35a734STimur Tabi */ 139f35a734STimur Tabi 149f35a734STimur Tabi #include <linux/kernel.h> 159f35a734STimur Tabi #include <linux/slab.h> 169f35a734STimur Tabi #include <linux/interrupt.h> 179f35a734STimur Tabi #include <linux/module.h> 189f35a734STimur Tabi #include <linux/phy.h> 199f35a734STimur Tabi #include <linux/mdio.h> 205af50730SRob Herring #include <linux/of_address.h> 219f35a734STimur Tabi #include <linux/of_platform.h> 229f35a734STimur Tabi #include <linux/of_mdio.h> 239f35a734STimur Tabi 249f35a734STimur Tabi /* Number of microseconds to wait for a register to respond */ 259f35a734STimur Tabi #define TIMEOUT 1000 269f35a734STimur Tabi 279f35a734STimur Tabi struct tgec_mdio_controller { 289f35a734STimur Tabi __be32 reserved[12]; 299f35a734STimur Tabi __be32 mdio_stat; /* MDIO configuration and status */ 309f35a734STimur Tabi __be32 mdio_ctl; /* MDIO control */ 319f35a734STimur Tabi __be32 mdio_data; /* MDIO data */ 329f35a734STimur Tabi __be32 mdio_addr; /* MDIO address */ 339f35a734STimur Tabi } __packed; 349f35a734STimur Tabi 351fcf77c8SAndy Fleming #define MDIO_STAT_ENC BIT(6) 369f35a734STimur Tabi #define MDIO_STAT_CLKDIV(x) (((x>>1) & 0xff) << 8) 3749ff2d3fSShaohui Xie #define MDIO_STAT_BSY BIT(0) 3849ff2d3fSShaohui Xie #define MDIO_STAT_RD_ER BIT(1) 399f35a734STimur Tabi #define MDIO_CTL_DEV_ADDR(x) (x & 0x1f) 409f35a734STimur Tabi #define MDIO_CTL_PORT_ADDR(x) ((x & 0x1f) << 5) 4149ff2d3fSShaohui Xie #define MDIO_CTL_PRE_DIS BIT(10) 4249ff2d3fSShaohui Xie #define MDIO_CTL_SCAN_EN BIT(11) 4349ff2d3fSShaohui Xie #define MDIO_CTL_POST_INC BIT(14) 4449ff2d3fSShaohui Xie #define MDIO_CTL_READ BIT(15) 459f35a734STimur Tabi 469f35a734STimur Tabi #define MDIO_DATA(x) (x & 0xffff) 4749ff2d3fSShaohui Xie #define MDIO_DATA_BSY BIT(31) 489f35a734STimur Tabi 4973ee5442SShaohui Xie struct mdio_fsl_priv { 5073ee5442SShaohui Xie struct tgec_mdio_controller __iomem *mdio_base; 5173ee5442SShaohui Xie bool is_little_endian; 521d3ca681SMadalin Bucur bool has_a011043; 5373ee5442SShaohui Xie }; 5473ee5442SShaohui Xie 5573ee5442SShaohui Xie static u32 xgmac_read32(void __iomem *regs, 5673ee5442SShaohui Xie bool is_little_endian) 5773ee5442SShaohui Xie { 5873ee5442SShaohui Xie if (is_little_endian) 5973ee5442SShaohui Xie return ioread32(regs); 6073ee5442SShaohui Xie else 6173ee5442SShaohui Xie return ioread32be(regs); 6273ee5442SShaohui Xie } 6373ee5442SShaohui Xie 6473ee5442SShaohui Xie static void xgmac_write32(u32 value, 6573ee5442SShaohui Xie void __iomem *regs, 6673ee5442SShaohui Xie bool is_little_endian) 6773ee5442SShaohui Xie { 6873ee5442SShaohui Xie if (is_little_endian) 6973ee5442SShaohui Xie iowrite32(value, regs); 7073ee5442SShaohui Xie else 7173ee5442SShaohui Xie iowrite32be(value, regs); 7273ee5442SShaohui Xie } 7373ee5442SShaohui Xie 749f35a734STimur Tabi /* 75c1543d37SMadalin Bucur * Wait until the MDIO bus is free 769f35a734STimur Tabi */ 779f35a734STimur Tabi static int xgmac_wait_until_free(struct device *dev, 7873ee5442SShaohui Xie struct tgec_mdio_controller __iomem *regs, 7973ee5442SShaohui Xie bool is_little_endian) 809f35a734STimur Tabi { 8122f6bba7SShaohui Xie unsigned int timeout; 829f35a734STimur Tabi 839f35a734STimur Tabi /* Wait till the bus is free */ 8422f6bba7SShaohui Xie timeout = TIMEOUT; 8573ee5442SShaohui Xie while ((xgmac_read32(®s->mdio_stat, is_little_endian) & 8673ee5442SShaohui Xie MDIO_STAT_BSY) && timeout) { 8722f6bba7SShaohui Xie cpu_relax(); 8822f6bba7SShaohui Xie timeout--; 8922f6bba7SShaohui Xie } 9022f6bba7SShaohui Xie 9122f6bba7SShaohui Xie if (!timeout) { 929f35a734STimur Tabi dev_err(dev, "timeout waiting for bus to be free\n"); 939f35a734STimur Tabi return -ETIMEDOUT; 949f35a734STimur Tabi } 959f35a734STimur Tabi 969f35a734STimur Tabi return 0; 979f35a734STimur Tabi } 989f35a734STimur Tabi 999f35a734STimur Tabi /* 1009f35a734STimur Tabi * Wait till the MDIO read or write operation is complete 1019f35a734STimur Tabi */ 1029f35a734STimur Tabi static int xgmac_wait_until_done(struct device *dev, 10373ee5442SShaohui Xie struct tgec_mdio_controller __iomem *regs, 10473ee5442SShaohui Xie bool is_little_endian) 1059f35a734STimur Tabi { 10622f6bba7SShaohui Xie unsigned int timeout; 1079f35a734STimur Tabi 1089f35a734STimur Tabi /* Wait till the MDIO write is complete */ 10922f6bba7SShaohui Xie timeout = TIMEOUT; 11073ee5442SShaohui Xie while ((xgmac_read32(®s->mdio_stat, is_little_endian) & 11173ee5442SShaohui Xie MDIO_STAT_BSY) && timeout) { 11222f6bba7SShaohui Xie cpu_relax(); 11322f6bba7SShaohui Xie timeout--; 11422f6bba7SShaohui Xie } 11522f6bba7SShaohui Xie 11622f6bba7SShaohui Xie if (!timeout) { 1179f35a734STimur Tabi dev_err(dev, "timeout waiting for operation to complete\n"); 1189f35a734STimur Tabi return -ETIMEDOUT; 1199f35a734STimur Tabi } 1209f35a734STimur Tabi 1219f35a734STimur Tabi return 0; 1229f35a734STimur Tabi } 1239f35a734STimur Tabi 1249f35a734STimur Tabi /* 1259f35a734STimur Tabi * Write value to the PHY for this device to the register at regnum,waiting 1269f35a734STimur Tabi * until the write is done before it returns. All PHY configuration has to be 1279f35a734STimur Tabi * done through the TSEC1 MIIM regs. 1289f35a734STimur Tabi */ 1299f35a734STimur Tabi static int xgmac_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value) 1309f35a734STimur Tabi { 13173ee5442SShaohui Xie struct mdio_fsl_priv *priv = (struct mdio_fsl_priv *)bus->priv; 13273ee5442SShaohui Xie struct tgec_mdio_controller __iomem *regs = priv->mdio_base; 1331fcf77c8SAndy Fleming uint16_t dev_addr; 1341fcf77c8SAndy Fleming u32 mdio_ctl, mdio_stat; 1359f35a734STimur Tabi int ret; 13673ee5442SShaohui Xie bool endian = priv->is_little_endian; 1379f35a734STimur Tabi 13873ee5442SShaohui Xie mdio_stat = xgmac_read32(®s->mdio_stat, endian); 1391fcf77c8SAndy Fleming if (regnum & MII_ADDR_C45) { 1401fcf77c8SAndy Fleming /* Clause 45 (ie 10G) */ 1411fcf77c8SAndy Fleming dev_addr = (regnum >> 16) & 0x1f; 1421fcf77c8SAndy Fleming mdio_stat |= MDIO_STAT_ENC; 1431fcf77c8SAndy Fleming } else { 1441fcf77c8SAndy Fleming /* Clause 22 (ie 1G) */ 1451fcf77c8SAndy Fleming dev_addr = regnum & 0x1f; 1461fcf77c8SAndy Fleming mdio_stat &= ~MDIO_STAT_ENC; 1471fcf77c8SAndy Fleming } 1481fcf77c8SAndy Fleming 14973ee5442SShaohui Xie xgmac_write32(mdio_stat, ®s->mdio_stat, endian); 1501fcf77c8SAndy Fleming 15173ee5442SShaohui Xie ret = xgmac_wait_until_free(&bus->dev, regs, endian); 1521fcf77c8SAndy Fleming if (ret) 1531fcf77c8SAndy Fleming return ret; 1541fcf77c8SAndy Fleming 1559f35a734STimur Tabi /* Set the port and dev addr */ 1561fcf77c8SAndy Fleming mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr); 15773ee5442SShaohui Xie xgmac_write32(mdio_ctl, ®s->mdio_ctl, endian); 1589f35a734STimur Tabi 1599f35a734STimur Tabi /* Set the register address */ 1601fcf77c8SAndy Fleming if (regnum & MII_ADDR_C45) { 16173ee5442SShaohui Xie xgmac_write32(regnum & 0xffff, ®s->mdio_addr, endian); 1629f35a734STimur Tabi 16373ee5442SShaohui Xie ret = xgmac_wait_until_free(&bus->dev, regs, endian); 1649f35a734STimur Tabi if (ret) 1659f35a734STimur Tabi return ret; 1661fcf77c8SAndy Fleming } 1679f35a734STimur Tabi 1689f35a734STimur Tabi /* Write the value to the register */ 16973ee5442SShaohui Xie xgmac_write32(MDIO_DATA(value), ®s->mdio_data, endian); 1709f35a734STimur Tabi 17173ee5442SShaohui Xie ret = xgmac_wait_until_done(&bus->dev, regs, endian); 1729f35a734STimur Tabi if (ret) 1739f35a734STimur Tabi return ret; 1749f35a734STimur Tabi 1759f35a734STimur Tabi return 0; 1769f35a734STimur Tabi } 1779f35a734STimur Tabi 1789f35a734STimur Tabi /* 1799f35a734STimur Tabi * Reads from register regnum in the PHY for device dev, returning the value. 1809f35a734STimur Tabi * Clears miimcom first. All PHY configuration has to be done through the 1819f35a734STimur Tabi * TSEC1 MIIM regs. 1829f35a734STimur Tabi */ 1839f35a734STimur Tabi static int xgmac_mdio_read(struct mii_bus *bus, int phy_id, int regnum) 1849f35a734STimur Tabi { 18573ee5442SShaohui Xie struct mdio_fsl_priv *priv = (struct mdio_fsl_priv *)bus->priv; 18673ee5442SShaohui Xie struct tgec_mdio_controller __iomem *regs = priv->mdio_base; 1871fcf77c8SAndy Fleming uint16_t dev_addr; 1881fcf77c8SAndy Fleming uint32_t mdio_stat; 1899f35a734STimur Tabi uint32_t mdio_ctl; 1909f35a734STimur Tabi uint16_t value; 1919f35a734STimur Tabi int ret; 19273ee5442SShaohui Xie bool endian = priv->is_little_endian; 1939f35a734STimur Tabi 19473ee5442SShaohui Xie mdio_stat = xgmac_read32(®s->mdio_stat, endian); 1951fcf77c8SAndy Fleming if (regnum & MII_ADDR_C45) { 1961fcf77c8SAndy Fleming dev_addr = (regnum >> 16) & 0x1f; 1971fcf77c8SAndy Fleming mdio_stat |= MDIO_STAT_ENC; 1981fcf77c8SAndy Fleming } else { 1991fcf77c8SAndy Fleming dev_addr = regnum & 0x1f; 200e54bfe9dSShaohui Xie mdio_stat &= ~MDIO_STAT_ENC; 2011fcf77c8SAndy Fleming } 2021fcf77c8SAndy Fleming 20373ee5442SShaohui Xie xgmac_write32(mdio_stat, ®s->mdio_stat, endian); 2041fcf77c8SAndy Fleming 20573ee5442SShaohui Xie ret = xgmac_wait_until_free(&bus->dev, regs, endian); 2061fcf77c8SAndy Fleming if (ret) 2071fcf77c8SAndy Fleming return ret; 2081fcf77c8SAndy Fleming 2099f35a734STimur Tabi /* Set the Port and Device Addrs */ 2109f35a734STimur Tabi mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr); 21173ee5442SShaohui Xie xgmac_write32(mdio_ctl, ®s->mdio_ctl, endian); 2129f35a734STimur Tabi 2139f35a734STimur Tabi /* Set the register address */ 2141fcf77c8SAndy Fleming if (regnum & MII_ADDR_C45) { 21573ee5442SShaohui Xie xgmac_write32(regnum & 0xffff, ®s->mdio_addr, endian); 2169f35a734STimur Tabi 21773ee5442SShaohui Xie ret = xgmac_wait_until_free(&bus->dev, regs, endian); 2189f35a734STimur Tabi if (ret) 2199f35a734STimur Tabi return ret; 2201fcf77c8SAndy Fleming } 2219f35a734STimur Tabi 2229f35a734STimur Tabi /* Initiate the read */ 22373ee5442SShaohui Xie xgmac_write32(mdio_ctl | MDIO_CTL_READ, ®s->mdio_ctl, endian); 2249f35a734STimur Tabi 22573ee5442SShaohui Xie ret = xgmac_wait_until_done(&bus->dev, regs, endian); 2269f35a734STimur Tabi if (ret) 2279f35a734STimur Tabi return ret; 2289f35a734STimur Tabi 2299f35a734STimur Tabi /* Return all Fs if nothing was there */ 2301d3ca681SMadalin Bucur if ((xgmac_read32(®s->mdio_stat, endian) & MDIO_STAT_RD_ER) && 2311d3ca681SMadalin Bucur !priv->has_a011043) { 23255fd3641SShruti Kanetkar dev_err(&bus->dev, 2339e6492ecSShruti Kanetkar "Error while reading PHY%d reg at %d.%hhu\n", 23455fd3641SShruti Kanetkar phy_id, dev_addr, regnum); 2359f35a734STimur Tabi return 0xffff; 2369f35a734STimur Tabi } 2379f35a734STimur Tabi 23873ee5442SShaohui Xie value = xgmac_read32(®s->mdio_data, endian) & 0xffff; 2399f35a734STimur Tabi dev_dbg(&bus->dev, "read %04x\n", value); 2409f35a734STimur Tabi 2419f35a734STimur Tabi return value; 2429f35a734STimur Tabi } 2439f35a734STimur Tabi 24433897cc8SBill Pemberton static int xgmac_mdio_probe(struct platform_device *pdev) 2459f35a734STimur Tabi { 2469f35a734STimur Tabi struct device_node *np = pdev->dev.of_node; 2479f35a734STimur Tabi struct mii_bus *bus; 248229f4bb4SCalvin Johnson struct resource *res; 24973ee5442SShaohui Xie struct mdio_fsl_priv *priv; 2509f35a734STimur Tabi int ret; 2519f35a734STimur Tabi 252229f4bb4SCalvin Johnson /* In DPAA-1, MDIO is one of the many FMan sub-devices. The FMan 253229f4bb4SCalvin Johnson * defines a register space that spans a large area, covering all the 254229f4bb4SCalvin Johnson * subdevice areas. Therefore, MDIO cannot claim exclusive access to 255229f4bb4SCalvin Johnson * this register area. 256229f4bb4SCalvin Johnson */ 257229f4bb4SCalvin Johnson res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 258229f4bb4SCalvin Johnson if (!res) { 2599f35a734STimur Tabi dev_err(&pdev->dev, "could not obtain address\n"); 260229f4bb4SCalvin Johnson return -EINVAL; 2619f35a734STimur Tabi } 2629f35a734STimur Tabi 26373ee5442SShaohui Xie bus = mdiobus_alloc_size(sizeof(struct mdio_fsl_priv)); 2649f35a734STimur Tabi if (!bus) 2659f35a734STimur Tabi return -ENOMEM; 2669f35a734STimur Tabi 2679f35a734STimur Tabi bus->name = "Freescale XGMAC MDIO Bus"; 2689f35a734STimur Tabi bus->read = xgmac_mdio_read; 2699f35a734STimur Tabi bus->write = xgmac_mdio_write; 2709f35a734STimur Tabi bus->parent = &pdev->dev; 271229f4bb4SCalvin Johnson snprintf(bus->id, MII_BUS_ID_SIZE, "%pa", &res->start); 2729f35a734STimur Tabi 2739f35a734STimur Tabi /* Set the PHY base address */ 27473ee5442SShaohui Xie priv = bus->priv; 275229f4bb4SCalvin Johnson priv->mdio_base = ioremap(res->start, resource_size(res)); 27673ee5442SShaohui Xie if (!priv->mdio_base) { 2779f35a734STimur Tabi ret = -ENOMEM; 2789f35a734STimur Tabi goto err_ioremap; 2799f35a734STimur Tabi } 2809f35a734STimur Tabi 281229f4bb4SCalvin Johnson priv->is_little_endian = device_property_read_bool(&pdev->dev, 28207bf2e11SJulia Lawall "little-endian"); 28373ee5442SShaohui Xie 284229f4bb4SCalvin Johnson priv->has_a011043 = device_property_read_bool(&pdev->dev, 2851d3ca681SMadalin Bucur "fsl,erratum-a011043"); 2861d3ca681SMadalin Bucur 2879f35a734STimur Tabi ret = of_mdiobus_register(bus, np); 2889f35a734STimur Tabi if (ret) { 2899f35a734STimur Tabi dev_err(&pdev->dev, "cannot register MDIO bus\n"); 2909f35a734STimur Tabi goto err_registration; 2919f35a734STimur Tabi } 2929f35a734STimur Tabi 2938513fbd8SJingoo Han platform_set_drvdata(pdev, bus); 2949f35a734STimur Tabi 2959f35a734STimur Tabi return 0; 2969f35a734STimur Tabi 2979f35a734STimur Tabi err_registration: 29873ee5442SShaohui Xie iounmap(priv->mdio_base); 2999f35a734STimur Tabi 3009f35a734STimur Tabi err_ioremap: 3019f35a734STimur Tabi mdiobus_free(bus); 3029f35a734STimur Tabi 3039f35a734STimur Tabi return ret; 3049f35a734STimur Tabi } 3059f35a734STimur Tabi 30633897cc8SBill Pemberton static int xgmac_mdio_remove(struct platform_device *pdev) 3079f35a734STimur Tabi { 3088513fbd8SJingoo Han struct mii_bus *bus = platform_get_drvdata(pdev); 3099f35a734STimur Tabi 3109f35a734STimur Tabi mdiobus_unregister(bus); 3119f35a734STimur Tabi iounmap(bus->priv); 3129f35a734STimur Tabi mdiobus_free(bus); 3139f35a734STimur Tabi 3149f35a734STimur Tabi return 0; 3159f35a734STimur Tabi } 3169f35a734STimur Tabi 31794e5a2a8SFabian Frederick static const struct of_device_id xgmac_mdio_match[] = { 3189f35a734STimur Tabi { 3199f35a734STimur Tabi .compatible = "fsl,fman-xmdio", 3209f35a734STimur Tabi }, 3211fcf77c8SAndy Fleming { 3221fcf77c8SAndy Fleming .compatible = "fsl,fman-memac-mdio", 3231fcf77c8SAndy Fleming }, 3249f35a734STimur Tabi {}, 3259f35a734STimur Tabi }; 3269f35a734STimur Tabi MODULE_DEVICE_TABLE(of, xgmac_mdio_match); 3279f35a734STimur Tabi 328229f4bb4SCalvin Johnson static const struct acpi_device_id xgmac_acpi_match[] = { 329229f4bb4SCalvin Johnson { "NXP0006" }, 330229f4bb4SCalvin Johnson { } 331229f4bb4SCalvin Johnson }; 332229f4bb4SCalvin Johnson MODULE_DEVICE_TABLE(acpi, xgmac_acpi_match); 333229f4bb4SCalvin Johnson 3349f35a734STimur Tabi static struct platform_driver xgmac_mdio_driver = { 3359f35a734STimur Tabi .driver = { 3369f35a734STimur Tabi .name = "fsl-fman_xmdio", 3379f35a734STimur Tabi .of_match_table = xgmac_mdio_match, 338229f4bb4SCalvin Johnson .acpi_match_table = xgmac_acpi_match, 3399f35a734STimur Tabi }, 3409f35a734STimur Tabi .probe = xgmac_mdio_probe, 3419f35a734STimur Tabi .remove = xgmac_mdio_remove, 3429f35a734STimur Tabi }; 3439f35a734STimur Tabi 3449f35a734STimur Tabi module_platform_driver(xgmac_mdio_driver); 3459f35a734STimur Tabi 3469f35a734STimur Tabi MODULE_DESCRIPTION("Freescale QorIQ 10G MDIO Controller"); 3479f35a734STimur Tabi MODULE_LICENSE("GPL v2"); 348