Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) 2015 Broadcom Corporation
0004  */
0005 
0006 #include <linux/delay.h>
0007 #include <linux/io.h>
0008 #include <linux/kernel.h>
0009 #include <linux/module.h>
0010 #include <linux/of.h>
0011 #include <linux/of_platform.h>
0012 #include <linux/of_mdio.h>
0013 #include <linux/phy.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/sched.h>
0016 
0017 #define IPROC_GPHY_MDCDIV    0x1a
0018 
0019 #define MII_CTRL_OFFSET      0x000
0020 
0021 #define MII_CTRL_DIV_SHIFT   0
0022 #define MII_CTRL_PRE_SHIFT   7
0023 #define MII_CTRL_BUSY_SHIFT  8
0024 
0025 #define MII_DATA_OFFSET      0x004
0026 #define MII_DATA_MASK        0xffff
0027 #define MII_DATA_TA_SHIFT    16
0028 #define MII_DATA_TA_VAL      2
0029 #define MII_DATA_RA_SHIFT    18
0030 #define MII_DATA_PA_SHIFT    23
0031 #define MII_DATA_OP_SHIFT    28
0032 #define MII_DATA_OP_WRITE    1
0033 #define MII_DATA_OP_READ     2
0034 #define MII_DATA_SB_SHIFT    30
0035 
0036 struct iproc_mdio_priv {
0037     struct mii_bus *mii_bus;
0038     void __iomem *base;
0039 };
0040 
0041 static inline int iproc_mdio_wait_for_idle(void __iomem *base)
0042 {
0043     u32 val;
0044     unsigned int timeout = 1000; /* loop for 1s */
0045 
0046     do {
0047         val = readl(base + MII_CTRL_OFFSET);
0048         if ((val & BIT(MII_CTRL_BUSY_SHIFT)) == 0)
0049             return 0;
0050 
0051         usleep_range(1000, 2000);
0052     } while (timeout--);
0053 
0054     return -ETIMEDOUT;
0055 }
0056 
0057 static inline void iproc_mdio_config_clk(void __iomem *base)
0058 {
0059     u32 val;
0060 
0061     val = (IPROC_GPHY_MDCDIV << MII_CTRL_DIV_SHIFT) |
0062           BIT(MII_CTRL_PRE_SHIFT);
0063     writel(val, base + MII_CTRL_OFFSET);
0064 }
0065 
0066 static int iproc_mdio_read(struct mii_bus *bus, int phy_id, int reg)
0067 {
0068     struct iproc_mdio_priv *priv = bus->priv;
0069     u32 cmd;
0070     int rc;
0071 
0072     rc = iproc_mdio_wait_for_idle(priv->base);
0073     if (rc)
0074         return rc;
0075 
0076     /* Prepare the read operation */
0077     cmd = (MII_DATA_TA_VAL << MII_DATA_TA_SHIFT) |
0078         (reg << MII_DATA_RA_SHIFT) |
0079         (phy_id << MII_DATA_PA_SHIFT) |
0080         BIT(MII_DATA_SB_SHIFT) |
0081         (MII_DATA_OP_READ << MII_DATA_OP_SHIFT);
0082 
0083     writel(cmd, priv->base + MII_DATA_OFFSET);
0084 
0085     rc = iproc_mdio_wait_for_idle(priv->base);
0086     if (rc)
0087         return rc;
0088 
0089     cmd = readl(priv->base + MII_DATA_OFFSET) & MII_DATA_MASK;
0090 
0091     return cmd;
0092 }
0093 
0094 static int iproc_mdio_write(struct mii_bus *bus, int phy_id,
0095                 int reg, u16 val)
0096 {
0097     struct iproc_mdio_priv *priv = bus->priv;
0098     u32 cmd;
0099     int rc;
0100 
0101     rc = iproc_mdio_wait_for_idle(priv->base);
0102     if (rc)
0103         return rc;
0104 
0105     /* Prepare the write operation */
0106     cmd = (MII_DATA_TA_VAL << MII_DATA_TA_SHIFT) |
0107         (reg << MII_DATA_RA_SHIFT) |
0108         (phy_id << MII_DATA_PA_SHIFT) |
0109         BIT(MII_DATA_SB_SHIFT) |
0110         (MII_DATA_OP_WRITE << MII_DATA_OP_SHIFT) |
0111         ((u32)(val) & MII_DATA_MASK);
0112 
0113     writel(cmd, priv->base + MII_DATA_OFFSET);
0114 
0115     rc = iproc_mdio_wait_for_idle(priv->base);
0116     if (rc)
0117         return rc;
0118 
0119     return 0;
0120 }
0121 
0122 static int iproc_mdio_probe(struct platform_device *pdev)
0123 {
0124     struct iproc_mdio_priv *priv;
0125     struct mii_bus *bus;
0126     int rc;
0127 
0128     priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
0129     if (!priv)
0130         return -ENOMEM;
0131 
0132     priv->base = devm_platform_ioremap_resource(pdev, 0);
0133     if (IS_ERR(priv->base)) {
0134         dev_err(&pdev->dev, "failed to ioremap register\n");
0135         return PTR_ERR(priv->base);
0136     }
0137 
0138     priv->mii_bus = mdiobus_alloc();
0139     if (!priv->mii_bus) {
0140         dev_err(&pdev->dev, "MDIO bus alloc failed\n");
0141         return -ENOMEM;
0142     }
0143 
0144     bus = priv->mii_bus;
0145     bus->priv = priv;
0146     bus->name = "iProc MDIO bus";
0147     snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", pdev->name, pdev->id);
0148     bus->parent = &pdev->dev;
0149     bus->read = iproc_mdio_read;
0150     bus->write = iproc_mdio_write;
0151 
0152     iproc_mdio_config_clk(priv->base);
0153 
0154     rc = of_mdiobus_register(bus, pdev->dev.of_node);
0155     if (rc) {
0156         dev_err(&pdev->dev, "MDIO bus registration failed\n");
0157         goto err_iproc_mdio;
0158     }
0159 
0160     platform_set_drvdata(pdev, priv);
0161 
0162     dev_info(&pdev->dev, "Broadcom iProc MDIO bus registered\n");
0163 
0164     return 0;
0165 
0166 err_iproc_mdio:
0167     mdiobus_free(bus);
0168     return rc;
0169 }
0170 
0171 static int iproc_mdio_remove(struct platform_device *pdev)
0172 {
0173     struct iproc_mdio_priv *priv = platform_get_drvdata(pdev);
0174 
0175     mdiobus_unregister(priv->mii_bus);
0176     mdiobus_free(priv->mii_bus);
0177 
0178     return 0;
0179 }
0180 
0181 #ifdef CONFIG_PM_SLEEP
0182 static int iproc_mdio_resume(struct device *dev)
0183 {
0184     struct platform_device *pdev = to_platform_device(dev);
0185     struct iproc_mdio_priv *priv = platform_get_drvdata(pdev);
0186 
0187     /* restore the mii clock configuration */
0188     iproc_mdio_config_clk(priv->base);
0189 
0190     return 0;
0191 }
0192 
0193 static const struct dev_pm_ops iproc_mdio_pm_ops = {
0194     .resume = iproc_mdio_resume
0195 };
0196 #endif /* CONFIG_PM_SLEEP */
0197 
0198 static const struct of_device_id iproc_mdio_of_match[] = {
0199     { .compatible = "brcm,iproc-mdio", },
0200     { /* sentinel */ },
0201 };
0202 MODULE_DEVICE_TABLE(of, iproc_mdio_of_match);
0203 
0204 static struct platform_driver iproc_mdio_driver = {
0205     .driver = {
0206         .name = "iproc-mdio",
0207         .of_match_table = iproc_mdio_of_match,
0208 #ifdef CONFIG_PM_SLEEP
0209         .pm = &iproc_mdio_pm_ops,
0210 #endif
0211     },
0212     .probe = iproc_mdio_probe,
0213     .remove = iproc_mdio_remove,
0214 };
0215 
0216 module_platform_driver(iproc_mdio_driver);
0217 
0218 MODULE_AUTHOR("Broadcom Corporation");
0219 MODULE_DESCRIPTION("Broadcom iProc MDIO bus controller");
0220 MODULE_LICENSE("GPL v2");
0221 MODULE_ALIAS("platform:iproc-mdio");