220 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2015 Broadcom Corporation4 */5 6#include <linux/delay.h>7#include <linux/io.h>8#include <linux/kernel.h>9#include <linux/module.h>10#include <linux/of.h>11#include <linux/of_platform.h>12#include <linux/of_mdio.h>13#include <linux/phy.h>14#include <linux/platform_device.h>15#include <linux/sched.h>16 17#define IPROC_GPHY_MDCDIV 0x1a18 19#define MII_CTRL_OFFSET 0x00020 21#define MII_CTRL_DIV_SHIFT 022#define MII_CTRL_PRE_SHIFT 723#define MII_CTRL_BUSY_SHIFT 824 25#define MII_DATA_OFFSET 0x00426#define MII_DATA_MASK 0xffff27#define MII_DATA_TA_SHIFT 1628#define MII_DATA_TA_VAL 229#define MII_DATA_RA_SHIFT 1830#define MII_DATA_PA_SHIFT 2331#define MII_DATA_OP_SHIFT 2832#define MII_DATA_OP_WRITE 133#define MII_DATA_OP_READ 234#define MII_DATA_SB_SHIFT 3035 36struct iproc_mdio_priv {37 struct mii_bus *mii_bus;38 void __iomem *base;39};40 41static inline int iproc_mdio_wait_for_idle(void __iomem *base)42{43 u32 val;44 unsigned int timeout = 1000; /* loop for 1s */45 46 do {47 val = readl(base + MII_CTRL_OFFSET);48 if ((val & BIT(MII_CTRL_BUSY_SHIFT)) == 0)49 return 0;50 51 usleep_range(1000, 2000);52 } while (timeout--);53 54 return -ETIMEDOUT;55}56 57static inline void iproc_mdio_config_clk(void __iomem *base)58{59 u32 val;60 61 val = (IPROC_GPHY_MDCDIV << MII_CTRL_DIV_SHIFT) |62 BIT(MII_CTRL_PRE_SHIFT);63 writel(val, base + MII_CTRL_OFFSET);64}65 66static int iproc_mdio_read(struct mii_bus *bus, int phy_id, int reg)67{68 struct iproc_mdio_priv *priv = bus->priv;69 u32 cmd;70 int rc;71 72 rc = iproc_mdio_wait_for_idle(priv->base);73 if (rc)74 return rc;75 76 /* Prepare the read operation */77 cmd = (MII_DATA_TA_VAL << MII_DATA_TA_SHIFT) |78 (reg << MII_DATA_RA_SHIFT) |79 (phy_id << MII_DATA_PA_SHIFT) |80 BIT(MII_DATA_SB_SHIFT) |81 (MII_DATA_OP_READ << MII_DATA_OP_SHIFT);82 83 writel(cmd, priv->base + MII_DATA_OFFSET);84 85 rc = iproc_mdio_wait_for_idle(priv->base);86 if (rc)87 return rc;88 89 cmd = readl(priv->base + MII_DATA_OFFSET) & MII_DATA_MASK;90 91 return cmd;92}93 94static int iproc_mdio_write(struct mii_bus *bus, int phy_id,95 int reg, u16 val)96{97 struct iproc_mdio_priv *priv = bus->priv;98 u32 cmd;99 int rc;100 101 rc = iproc_mdio_wait_for_idle(priv->base);102 if (rc)103 return rc;104 105 /* Prepare the write operation */106 cmd = (MII_DATA_TA_VAL << MII_DATA_TA_SHIFT) |107 (reg << MII_DATA_RA_SHIFT) |108 (phy_id << MII_DATA_PA_SHIFT) |109 BIT(MII_DATA_SB_SHIFT) |110 (MII_DATA_OP_WRITE << MII_DATA_OP_SHIFT) |111 ((u32)(val) & MII_DATA_MASK);112 113 writel(cmd, priv->base + MII_DATA_OFFSET);114 115 rc = iproc_mdio_wait_for_idle(priv->base);116 if (rc)117 return rc;118 119 return 0;120}121 122static int iproc_mdio_probe(struct platform_device *pdev)123{124 struct iproc_mdio_priv *priv;125 struct mii_bus *bus;126 int rc;127 128 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);129 if (!priv)130 return -ENOMEM;131 132 priv->base = devm_platform_ioremap_resource(pdev, 0);133 if (IS_ERR(priv->base)) {134 dev_err(&pdev->dev, "failed to ioremap register\n");135 return PTR_ERR(priv->base);136 }137 138 priv->mii_bus = mdiobus_alloc();139 if (!priv->mii_bus) {140 dev_err(&pdev->dev, "MDIO bus alloc failed\n");141 return -ENOMEM;142 }143 144 bus = priv->mii_bus;145 bus->priv = priv;146 bus->name = "iProc MDIO bus";147 snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", pdev->name, pdev->id);148 bus->parent = &pdev->dev;149 bus->read = iproc_mdio_read;150 bus->write = iproc_mdio_write;151 152 iproc_mdio_config_clk(priv->base);153 154 rc = of_mdiobus_register(bus, pdev->dev.of_node);155 if (rc) {156 dev_err(&pdev->dev, "MDIO bus registration failed\n");157 goto err_iproc_mdio;158 }159 160 platform_set_drvdata(pdev, priv);161 162 dev_info(&pdev->dev, "Broadcom iProc MDIO bus registered\n");163 164 return 0;165 166err_iproc_mdio:167 mdiobus_free(bus);168 return rc;169}170 171static void iproc_mdio_remove(struct platform_device *pdev)172{173 struct iproc_mdio_priv *priv = platform_get_drvdata(pdev);174 175 mdiobus_unregister(priv->mii_bus);176 mdiobus_free(priv->mii_bus);177}178 179#ifdef CONFIG_PM_SLEEP180static int iproc_mdio_resume(struct device *dev)181{182 struct platform_device *pdev = to_platform_device(dev);183 struct iproc_mdio_priv *priv = platform_get_drvdata(pdev);184 185 /* restore the mii clock configuration */186 iproc_mdio_config_clk(priv->base);187 188 return 0;189}190 191static const struct dev_pm_ops iproc_mdio_pm_ops = {192 .resume = iproc_mdio_resume193};194#endif /* CONFIG_PM_SLEEP */195 196static const struct of_device_id iproc_mdio_of_match[] = {197 { .compatible = "brcm,iproc-mdio", },198 { /* sentinel */ },199};200MODULE_DEVICE_TABLE(of, iproc_mdio_of_match);201 202static struct platform_driver iproc_mdio_driver = {203 .driver = {204 .name = "iproc-mdio",205 .of_match_table = iproc_mdio_of_match,206#ifdef CONFIG_PM_SLEEP207 .pm = &iproc_mdio_pm_ops,208#endif209 },210 .probe = iproc_mdio_probe,211 .remove_new = iproc_mdio_remove,212};213 214module_platform_driver(iproc_mdio_driver);215 216MODULE_AUTHOR("Broadcom Corporation");217MODULE_DESCRIPTION("Broadcom iProc MDIO bus controller");218MODULE_LICENSE("GPL v2");219MODULE_ALIAS("platform:iproc-mdio");220