0001
0002
0003
0004
0005 #include <linux/clk.h>
0006 #include <linux/delay.h>
0007 #include <linux/device.h>
0008 #include <linux/iopoll.h>
0009 #include <linux/mdio-mux.h>
0010 #include <linux/module.h>
0011 #include <linux/of_mdio.h>
0012 #include <linux/phy.h>
0013 #include <linux/platform_device.h>
0014
0015 #define MDIO_RATE_ADJ_EXT_OFFSET 0x000
0016 #define MDIO_RATE_ADJ_INT_OFFSET 0x004
0017 #define MDIO_RATE_ADJ_DIVIDENT_SHIFT 16
0018
0019 #define MDIO_SCAN_CTRL_OFFSET 0x008
0020 #define MDIO_SCAN_CTRL_OVRIDE_EXT_MSTR 28
0021
0022 #define MDIO_PARAM_OFFSET 0x23c
0023 #define MDIO_PARAM_MIIM_CYCLE 29
0024 #define MDIO_PARAM_INTERNAL_SEL 25
0025 #define MDIO_PARAM_BUS_ID 22
0026 #define MDIO_PARAM_C45_SEL 21
0027 #define MDIO_PARAM_PHY_ID 16
0028 #define MDIO_PARAM_PHY_DATA 0
0029
0030 #define MDIO_READ_OFFSET 0x240
0031 #define MDIO_READ_DATA_MASK 0xffff
0032 #define MDIO_ADDR_OFFSET 0x244
0033
0034 #define MDIO_CTRL_OFFSET 0x248
0035 #define MDIO_CTRL_WRITE_OP 0x1
0036 #define MDIO_CTRL_READ_OP 0x2
0037
0038 #define MDIO_STAT_OFFSET 0x24c
0039 #define MDIO_STAT_DONE 1
0040
0041 #define BUS_MAX_ADDR 32
0042 #define EXT_BUS_START_ADDR 16
0043
0044 #define MDIO_REG_ADDR_SPACE_SIZE 0x250
0045
0046 #define MDIO_OPERATING_FREQUENCY 11000000
0047 #define MDIO_RATE_ADJ_DIVIDENT 1
0048
0049 struct iproc_mdiomux_desc {
0050 void *mux_handle;
0051 void __iomem *base;
0052 struct device *dev;
0053 struct mii_bus *mii_bus;
0054 struct clk *core_clk;
0055 };
0056
0057 static void mdio_mux_iproc_config(struct iproc_mdiomux_desc *md)
0058 {
0059 u32 divisor;
0060 u32 val;
0061
0062
0063 val = readl(md->base + MDIO_SCAN_CTRL_OFFSET);
0064 val |= BIT(MDIO_SCAN_CTRL_OVRIDE_EXT_MSTR);
0065 writel(val, md->base + MDIO_SCAN_CTRL_OFFSET);
0066
0067 if (md->core_clk) {
0068
0069
0070
0071 divisor = clk_get_rate(md->core_clk) / MDIO_OPERATING_FREQUENCY;
0072 divisor = divisor / (MDIO_RATE_ADJ_DIVIDENT + 1);
0073 val = divisor;
0074 val |= MDIO_RATE_ADJ_DIVIDENT << MDIO_RATE_ADJ_DIVIDENT_SHIFT;
0075 writel(val, md->base + MDIO_RATE_ADJ_EXT_OFFSET);
0076 writel(val, md->base + MDIO_RATE_ADJ_INT_OFFSET);
0077 }
0078 }
0079
0080 static int iproc_mdio_wait_for_idle(void __iomem *base, bool result)
0081 {
0082 u32 val;
0083
0084 return readl_poll_timeout(base + MDIO_STAT_OFFSET, val,
0085 (val & MDIO_STAT_DONE) == result,
0086 2000, 1000000);
0087 }
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101 static int start_miim_ops(void __iomem *base,
0102 u16 phyid, u32 reg, u16 val, u32 op)
0103 {
0104 u32 param;
0105 int ret;
0106
0107 writel(0, base + MDIO_CTRL_OFFSET);
0108 ret = iproc_mdio_wait_for_idle(base, 0);
0109 if (ret)
0110 goto err;
0111
0112 param = readl(base + MDIO_PARAM_OFFSET);
0113 param |= phyid << MDIO_PARAM_PHY_ID;
0114 param |= val << MDIO_PARAM_PHY_DATA;
0115 if (reg & MII_ADDR_C45)
0116 param |= BIT(MDIO_PARAM_C45_SEL);
0117
0118 writel(param, base + MDIO_PARAM_OFFSET);
0119
0120 writel(reg, base + MDIO_ADDR_OFFSET);
0121
0122 writel(op, base + MDIO_CTRL_OFFSET);
0123
0124 ret = iproc_mdio_wait_for_idle(base, 1);
0125 if (ret)
0126 goto err;
0127
0128 if (op == MDIO_CTRL_READ_OP)
0129 ret = readl(base + MDIO_READ_OFFSET) & MDIO_READ_DATA_MASK;
0130 err:
0131 return ret;
0132 }
0133
0134 static int iproc_mdiomux_read(struct mii_bus *bus, int phyid, int reg)
0135 {
0136 struct iproc_mdiomux_desc *md = bus->priv;
0137 int ret;
0138
0139 ret = start_miim_ops(md->base, phyid, reg, 0, MDIO_CTRL_READ_OP);
0140 if (ret < 0)
0141 dev_err(&bus->dev, "mdiomux read operation failed!!!");
0142
0143 return ret;
0144 }
0145
0146 static int iproc_mdiomux_write(struct mii_bus *bus,
0147 int phyid, int reg, u16 val)
0148 {
0149 struct iproc_mdiomux_desc *md = bus->priv;
0150 int ret;
0151
0152
0153 ret = start_miim_ops(md->base, phyid, reg, val, MDIO_CTRL_WRITE_OP);
0154 if (ret < 0)
0155 dev_err(&bus->dev, "mdiomux write operation failed!!!");
0156
0157 return ret;
0158 }
0159
0160 static int mdio_mux_iproc_switch_fn(int current_child, int desired_child,
0161 void *data)
0162 {
0163 struct iproc_mdiomux_desc *md = data;
0164 u32 param, bus_id;
0165 bool bus_dir;
0166
0167
0168 bus_dir = (desired_child < EXT_BUS_START_ADDR);
0169 bus_id = bus_dir ? desired_child : (desired_child - EXT_BUS_START_ADDR);
0170
0171 param = (bus_dir ? 1 : 0) << MDIO_PARAM_INTERNAL_SEL;
0172 param |= (bus_id << MDIO_PARAM_BUS_ID);
0173
0174 writel(param, md->base + MDIO_PARAM_OFFSET);
0175 return 0;
0176 }
0177
0178 static int mdio_mux_iproc_probe(struct platform_device *pdev)
0179 {
0180 struct iproc_mdiomux_desc *md;
0181 struct mii_bus *bus;
0182 struct resource *res;
0183 int rc;
0184
0185 md = devm_kzalloc(&pdev->dev, sizeof(*md), GFP_KERNEL);
0186 if (!md)
0187 return -ENOMEM;
0188 md->dev = &pdev->dev;
0189
0190 md->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
0191 if (IS_ERR(md->base))
0192 return PTR_ERR(md->base);
0193 if (res->start & 0xfff) {
0194
0195
0196
0197 dev_info(&pdev->dev, "fix base address in dt-blob\n");
0198 res->start &= ~0xfff;
0199 res->end = res->start + MDIO_REG_ADDR_SPACE_SIZE - 1;
0200 }
0201
0202 md->mii_bus = devm_mdiobus_alloc(&pdev->dev);
0203 if (!md->mii_bus) {
0204 dev_err(&pdev->dev, "mdiomux bus alloc failed\n");
0205 return -ENOMEM;
0206 }
0207
0208 md->core_clk = devm_clk_get(&pdev->dev, NULL);
0209 if (md->core_clk == ERR_PTR(-ENOENT) ||
0210 md->core_clk == ERR_PTR(-EINVAL))
0211 md->core_clk = NULL;
0212 else if (IS_ERR(md->core_clk))
0213 return PTR_ERR(md->core_clk);
0214
0215 rc = clk_prepare_enable(md->core_clk);
0216 if (rc) {
0217 dev_err(&pdev->dev, "failed to enable core clk\n");
0218 return rc;
0219 }
0220
0221 bus = md->mii_bus;
0222 bus->priv = md;
0223 bus->name = "iProc MDIO mux bus";
0224 snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", pdev->name, pdev->id);
0225 bus->parent = &pdev->dev;
0226 bus->read = iproc_mdiomux_read;
0227 bus->write = iproc_mdiomux_write;
0228
0229 bus->phy_mask = ~0;
0230 bus->dev.of_node = pdev->dev.of_node;
0231 rc = mdiobus_register(bus);
0232 if (rc) {
0233 dev_err(&pdev->dev, "mdiomux registration failed\n");
0234 goto out_clk;
0235 }
0236
0237 platform_set_drvdata(pdev, md);
0238
0239 rc = mdio_mux_init(md->dev, md->dev->of_node, mdio_mux_iproc_switch_fn,
0240 &md->mux_handle, md, md->mii_bus);
0241 if (rc) {
0242 dev_info(md->dev, "mdiomux initialization failed\n");
0243 goto out_register;
0244 }
0245
0246 mdio_mux_iproc_config(md);
0247
0248 dev_info(md->dev, "iProc mdiomux registered\n");
0249 return 0;
0250
0251 out_register:
0252 mdiobus_unregister(bus);
0253 out_clk:
0254 clk_disable_unprepare(md->core_clk);
0255 return rc;
0256 }
0257
0258 static int mdio_mux_iproc_remove(struct platform_device *pdev)
0259 {
0260 struct iproc_mdiomux_desc *md = platform_get_drvdata(pdev);
0261
0262 mdio_mux_uninit(md->mux_handle);
0263 mdiobus_unregister(md->mii_bus);
0264 clk_disable_unprepare(md->core_clk);
0265
0266 return 0;
0267 }
0268
0269 #ifdef CONFIG_PM_SLEEP
0270 static int mdio_mux_iproc_suspend(struct device *dev)
0271 {
0272 struct iproc_mdiomux_desc *md = dev_get_drvdata(dev);
0273
0274 clk_disable_unprepare(md->core_clk);
0275
0276 return 0;
0277 }
0278
0279 static int mdio_mux_iproc_resume(struct device *dev)
0280 {
0281 struct iproc_mdiomux_desc *md = dev_get_drvdata(dev);
0282 int rc;
0283
0284 rc = clk_prepare_enable(md->core_clk);
0285 if (rc) {
0286 dev_err(md->dev, "failed to enable core clk\n");
0287 return rc;
0288 }
0289 mdio_mux_iproc_config(md);
0290
0291 return 0;
0292 }
0293 #endif
0294
0295 static SIMPLE_DEV_PM_OPS(mdio_mux_iproc_pm_ops,
0296 mdio_mux_iproc_suspend, mdio_mux_iproc_resume);
0297
0298 static const struct of_device_id mdio_mux_iproc_match[] = {
0299 {
0300 .compatible = "brcm,mdio-mux-iproc",
0301 },
0302 {},
0303 };
0304 MODULE_DEVICE_TABLE(of, mdio_mux_iproc_match);
0305
0306 static struct platform_driver mdiomux_iproc_driver = {
0307 .driver = {
0308 .name = "mdio-mux-iproc",
0309 .of_match_table = mdio_mux_iproc_match,
0310 .pm = &mdio_mux_iproc_pm_ops,
0311 },
0312 .probe = mdio_mux_iproc_probe,
0313 .remove = mdio_mux_iproc_remove,
0314 };
0315
0316 module_platform_driver(mdiomux_iproc_driver);
0317
0318 MODULE_DESCRIPTION("iProc MDIO Mux Bus Driver");
0319 MODULE_AUTHOR("Pramod Kumar <pramod.kumar@broadcom.com>");
0320 MODULE_LICENSE("GPL v2");