Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
0002 /* Copyright (c) 2015, The Linux Foundation. All rights reserved. */
0003 /* Copyright (c) 2020 Sartura Ltd. */
0004 
0005 #include <linux/delay.h>
0006 #include <linux/io.h>
0007 #include <linux/iopoll.h>
0008 #include <linux/kernel.h>
0009 #include <linux/module.h>
0010 #include <linux/of_address.h>
0011 #include <linux/of_mdio.h>
0012 #include <linux/phy.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/clk.h>
0015 
0016 #define MDIO_MODE_REG               0x40
0017 #define MDIO_ADDR_REG               0x44
0018 #define MDIO_DATA_WRITE_REG         0x48
0019 #define MDIO_DATA_READ_REG          0x4c
0020 #define MDIO_CMD_REG                0x50
0021 #define MDIO_CMD_ACCESS_BUSY        BIT(16)
0022 #define MDIO_CMD_ACCESS_START       BIT(8)
0023 #define MDIO_CMD_ACCESS_CODE_READ   0
0024 #define MDIO_CMD_ACCESS_CODE_WRITE  1
0025 #define MDIO_CMD_ACCESS_CODE_C45_ADDR   0
0026 #define MDIO_CMD_ACCESS_CODE_C45_WRITE  1
0027 #define MDIO_CMD_ACCESS_CODE_C45_READ   2
0028 
0029 /* 0 = Clause 22, 1 = Clause 45 */
0030 #define MDIO_MODE_C45               BIT(8)
0031 
0032 #define IPQ4019_MDIO_TIMEOUT    10000
0033 #define IPQ4019_MDIO_SLEEP      10
0034 
0035 /* MDIO clock source frequency is fixed to 100M */
0036 #define IPQ_MDIO_CLK_RATE   100000000
0037 
0038 #define IPQ_PHY_SET_DELAY_US    100000
0039 
0040 struct ipq4019_mdio_data {
0041     void __iomem    *membase;
0042     void __iomem *eth_ldo_rdy;
0043     struct clk *mdio_clk;
0044 };
0045 
0046 static int ipq4019_mdio_wait_busy(struct mii_bus *bus)
0047 {
0048     struct ipq4019_mdio_data *priv = bus->priv;
0049     unsigned int busy;
0050 
0051     return readl_poll_timeout(priv->membase + MDIO_CMD_REG, busy,
0052                   (busy & MDIO_CMD_ACCESS_BUSY) == 0,
0053                   IPQ4019_MDIO_SLEEP, IPQ4019_MDIO_TIMEOUT);
0054 }
0055 
0056 static int ipq4019_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
0057 {
0058     struct ipq4019_mdio_data *priv = bus->priv;
0059     unsigned int data;
0060     unsigned int cmd;
0061 
0062     if (ipq4019_mdio_wait_busy(bus))
0063         return -ETIMEDOUT;
0064 
0065     /* Clause 45 support */
0066     if (regnum & MII_ADDR_C45) {
0067         unsigned int mmd = (regnum >> 16) & 0x1F;
0068         unsigned int reg = regnum & 0xFFFF;
0069 
0070         /* Enter Clause 45 mode */
0071         data = readl(priv->membase + MDIO_MODE_REG);
0072 
0073         data |= MDIO_MODE_C45;
0074 
0075         writel(data, priv->membase + MDIO_MODE_REG);
0076 
0077         /* issue the phy address and mmd */
0078         writel((mii_id << 8) | mmd, priv->membase + MDIO_ADDR_REG);
0079 
0080         /* issue reg */
0081         writel(reg, priv->membase + MDIO_DATA_WRITE_REG);
0082 
0083         cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_C45_ADDR;
0084     } else {
0085         /* Enter Clause 22 mode */
0086         data = readl(priv->membase + MDIO_MODE_REG);
0087 
0088         data &= ~MDIO_MODE_C45;
0089 
0090         writel(data, priv->membase + MDIO_MODE_REG);
0091 
0092         /* issue the phy address and reg */
0093         writel((mii_id << 8) | regnum, priv->membase + MDIO_ADDR_REG);
0094 
0095         cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_READ;
0096     }
0097 
0098     /* issue read command */
0099     writel(cmd, priv->membase + MDIO_CMD_REG);
0100 
0101     /* Wait read complete */
0102     if (ipq4019_mdio_wait_busy(bus))
0103         return -ETIMEDOUT;
0104 
0105     if (regnum & MII_ADDR_C45) {
0106         cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_C45_READ;
0107 
0108         writel(cmd, priv->membase + MDIO_CMD_REG);
0109 
0110         if (ipq4019_mdio_wait_busy(bus))
0111             return -ETIMEDOUT;
0112     }
0113 
0114     /* Read and return data */
0115     return readl(priv->membase + MDIO_DATA_READ_REG);
0116 }
0117 
0118 static int ipq4019_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
0119                              u16 value)
0120 {
0121     struct ipq4019_mdio_data *priv = bus->priv;
0122     unsigned int data;
0123     unsigned int cmd;
0124 
0125     if (ipq4019_mdio_wait_busy(bus))
0126         return -ETIMEDOUT;
0127 
0128     /* Clause 45 support */
0129     if (regnum & MII_ADDR_C45) {
0130         unsigned int mmd = (regnum >> 16) & 0x1F;
0131         unsigned int reg = regnum & 0xFFFF;
0132 
0133         /* Enter Clause 45 mode */
0134         data = readl(priv->membase + MDIO_MODE_REG);
0135 
0136         data |= MDIO_MODE_C45;
0137 
0138         writel(data, priv->membase + MDIO_MODE_REG);
0139 
0140         /* issue the phy address and mmd */
0141         writel((mii_id << 8) | mmd, priv->membase + MDIO_ADDR_REG);
0142 
0143         /* issue reg */
0144         writel(reg, priv->membase + MDIO_DATA_WRITE_REG);
0145 
0146         cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_C45_ADDR;
0147 
0148         writel(cmd, priv->membase + MDIO_CMD_REG);
0149 
0150         if (ipq4019_mdio_wait_busy(bus))
0151             return -ETIMEDOUT;
0152     } else {
0153         /* Enter Clause 22 mode */
0154         data = readl(priv->membase + MDIO_MODE_REG);
0155 
0156         data &= ~MDIO_MODE_C45;
0157 
0158         writel(data, priv->membase + MDIO_MODE_REG);
0159 
0160         /* issue the phy address and reg */
0161         writel((mii_id << 8) | regnum, priv->membase + MDIO_ADDR_REG);
0162     }
0163 
0164     /* issue write data */
0165     writel(value, priv->membase + MDIO_DATA_WRITE_REG);
0166 
0167     /* issue write command */
0168     if (regnum & MII_ADDR_C45)
0169         cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_C45_WRITE;
0170     else
0171         cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_WRITE;
0172 
0173     writel(cmd, priv->membase + MDIO_CMD_REG);
0174 
0175     /* Wait write complete */
0176     if (ipq4019_mdio_wait_busy(bus))
0177         return -ETIMEDOUT;
0178 
0179     return 0;
0180 }
0181 
0182 static int ipq_mdio_reset(struct mii_bus *bus)
0183 {
0184     struct ipq4019_mdio_data *priv = bus->priv;
0185     u32 val;
0186     int ret;
0187 
0188     /* To indicate CMN_PLL that ethernet_ldo has been ready if platform resource 1
0189      * is specified in the device tree.
0190      */
0191     if (priv->eth_ldo_rdy) {
0192         val = readl(priv->eth_ldo_rdy);
0193         val |= BIT(0);
0194         writel(val, priv->eth_ldo_rdy);
0195         fsleep(IPQ_PHY_SET_DELAY_US);
0196     }
0197 
0198     /* Configure MDIO clock source frequency if clock is specified in the device tree */
0199     ret = clk_set_rate(priv->mdio_clk, IPQ_MDIO_CLK_RATE);
0200     if (ret)
0201         return ret;
0202 
0203     ret = clk_prepare_enable(priv->mdio_clk);
0204     if (ret == 0)
0205         mdelay(10);
0206 
0207     return ret;
0208 }
0209 
0210 static int ipq4019_mdio_probe(struct platform_device *pdev)
0211 {
0212     struct ipq4019_mdio_data *priv;
0213     struct mii_bus *bus;
0214     struct resource *res;
0215     int ret;
0216 
0217     bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*priv));
0218     if (!bus)
0219         return -ENOMEM;
0220 
0221     priv = bus->priv;
0222 
0223     priv->membase = devm_platform_ioremap_resource(pdev, 0);
0224     if (IS_ERR(priv->membase))
0225         return PTR_ERR(priv->membase);
0226 
0227     priv->mdio_clk = devm_clk_get_optional(&pdev->dev, "gcc_mdio_ahb_clk");
0228     if (IS_ERR(priv->mdio_clk))
0229         return PTR_ERR(priv->mdio_clk);
0230 
0231     /* The platform resource is provided on the chipset IPQ5018 */
0232     /* This resource is optional */
0233     res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
0234     if (res)
0235         priv->eth_ldo_rdy = devm_ioremap_resource(&pdev->dev, res);
0236 
0237     bus->name = "ipq4019_mdio";
0238     bus->read = ipq4019_mdio_read;
0239     bus->write = ipq4019_mdio_write;
0240     bus->reset = ipq_mdio_reset;
0241     bus->parent = &pdev->dev;
0242     snprintf(bus->id, MII_BUS_ID_SIZE, "%s%d", pdev->name, pdev->id);
0243 
0244     ret = of_mdiobus_register(bus, pdev->dev.of_node);
0245     if (ret) {
0246         dev_err(&pdev->dev, "Cannot register MDIO bus!\n");
0247         return ret;
0248     }
0249 
0250     platform_set_drvdata(pdev, bus);
0251 
0252     return 0;
0253 }
0254 
0255 static int ipq4019_mdio_remove(struct platform_device *pdev)
0256 {
0257     struct mii_bus *bus = platform_get_drvdata(pdev);
0258 
0259     mdiobus_unregister(bus);
0260 
0261     return 0;
0262 }
0263 
0264 static const struct of_device_id ipq4019_mdio_dt_ids[] = {
0265     { .compatible = "qcom,ipq4019-mdio" },
0266     { .compatible = "qcom,ipq5018-mdio" },
0267     { }
0268 };
0269 MODULE_DEVICE_TABLE(of, ipq4019_mdio_dt_ids);
0270 
0271 static struct platform_driver ipq4019_mdio_driver = {
0272     .probe = ipq4019_mdio_probe,
0273     .remove = ipq4019_mdio_remove,
0274     .driver = {
0275         .name = "ipq4019-mdio",
0276         .of_match_table = ipq4019_mdio_dt_ids,
0277     },
0278 };
0279 
0280 module_platform_driver(ipq4019_mdio_driver);
0281 
0282 MODULE_DESCRIPTION("ipq4019 MDIO interface driver");
0283 MODULE_AUTHOR("Qualcomm Atheros");
0284 MODULE_LICENSE("Dual BSD/GPL");