Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright 2016 Broadcom
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     /* Disable external mdio master access */
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         /* use rate adjust regs to derive the mdio's operating
0069          * frequency from the specified core clock
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 /* start_miim_ops- Program and start MDIO transaction over mdio bus.
0090  * @base: Base address
0091  * @phyid: phyid of the selected bus.
0092  * @reg: register offset to be read/written.
0093  * @val :0 if read op else value to be written in @reg;
0094  * @op: Operation that need to be carried out.
0095  *      MDIO_CTRL_READ_OP: Read transaction.
0096  *      MDIO_CTRL_WRITE_OP: Write transaction.
0097  *
0098  * Return value: Successful Read operation returns read reg values and write
0099  *      operation returns 0. Failure operation returns negative error code.
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     /* Write val at reg offset */
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     /* select bus and its properties */
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         /* For backward compatibility in case the
0195          * base address is specified with an offset.
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");