linux/drivers/net/mdio/mdio-mux-bcm-iproc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright 2016 Broadcom
   4 */
   5#include <linux/clk.h>
   6#include <linux/delay.h>
   7#include <linux/device.h>
   8#include <linux/iopoll.h>
   9#include <linux/mdio-mux.h>
  10#include <linux/module.h>
  11#include <linux/of_mdio.h>
  12#include <linux/phy.h>
  13#include <linux/platform_device.h>
  14
  15#define MDIO_RATE_ADJ_EXT_OFFSET        0x000
  16#define MDIO_RATE_ADJ_INT_OFFSET        0x004
  17#define MDIO_RATE_ADJ_DIVIDENT_SHIFT    16
  18
  19#define MDIO_SCAN_CTRL_OFFSET           0x008
  20#define MDIO_SCAN_CTRL_OVRIDE_EXT_MSTR  28
  21
  22#define MDIO_PARAM_OFFSET               0x23c
  23#define MDIO_PARAM_MIIM_CYCLE           29
  24#define MDIO_PARAM_INTERNAL_SEL         25
  25#define MDIO_PARAM_BUS_ID               22
  26#define MDIO_PARAM_C45_SEL              21
  27#define MDIO_PARAM_PHY_ID               16
  28#define MDIO_PARAM_PHY_DATA             0
  29
  30#define MDIO_READ_OFFSET                0x240
  31#define MDIO_READ_DATA_MASK             0xffff
  32#define MDIO_ADDR_OFFSET                0x244
  33
  34#define MDIO_CTRL_OFFSET                0x248
  35#define MDIO_CTRL_WRITE_OP              0x1
  36#define MDIO_CTRL_READ_OP               0x2
  37
  38#define MDIO_STAT_OFFSET                0x24c
  39#define MDIO_STAT_DONE                  1
  40
  41#define BUS_MAX_ADDR                    32
  42#define EXT_BUS_START_ADDR              16
  43
  44#define MDIO_REG_ADDR_SPACE_SIZE        0x250
  45
  46#define MDIO_OPERATING_FREQUENCY        11000000
  47#define MDIO_RATE_ADJ_DIVIDENT          1
  48
  49struct iproc_mdiomux_desc {
  50        void *mux_handle;
  51        void __iomem *base;
  52        struct device *dev;
  53        struct mii_bus *mii_bus;
  54        struct clk *core_clk;
  55};
  56
  57static void mdio_mux_iproc_config(struct iproc_mdiomux_desc *md)
  58{
  59        u32 divisor;
  60        u32 val;
  61
  62        /* Disable external mdio master access */
  63        val = readl(md->base + MDIO_SCAN_CTRL_OFFSET);
  64        val |= BIT(MDIO_SCAN_CTRL_OVRIDE_EXT_MSTR);
  65        writel(val, md->base + MDIO_SCAN_CTRL_OFFSET);
  66
  67        if (md->core_clk) {
  68                /* use rate adjust regs to derive the mdio's operating
  69                 * frequency from the specified core clock
  70                 */
  71                divisor = clk_get_rate(md->core_clk) / MDIO_OPERATING_FREQUENCY;
  72                divisor = divisor / (MDIO_RATE_ADJ_DIVIDENT + 1);
  73                val = divisor;
  74                val |= MDIO_RATE_ADJ_DIVIDENT << MDIO_RATE_ADJ_DIVIDENT_SHIFT;
  75                writel(val, md->base + MDIO_RATE_ADJ_EXT_OFFSET);
  76                writel(val, md->base + MDIO_RATE_ADJ_INT_OFFSET);
  77        }
  78}
  79
  80static int iproc_mdio_wait_for_idle(void __iomem *base, bool result)
  81{
  82        u32 val;
  83
  84        return readl_poll_timeout(base + MDIO_STAT_OFFSET, val,
  85                                  (val & MDIO_STAT_DONE) == result,
  86                                  2000, 1000000);
  87}
  88
  89/* start_miim_ops- Program and start MDIO transaction over mdio bus.
  90 * @base: Base address
  91 * @phyid: phyid of the selected bus.
  92 * @reg: register offset to be read/written.
  93 * @val :0 if read op else value to be written in @reg;
  94 * @op: Operation that need to be carried out.
  95 *      MDIO_CTRL_READ_OP: Read transaction.
  96 *      MDIO_CTRL_WRITE_OP: Write transaction.
  97 *
  98 * Return value: Successful Read operation returns read reg values and write
  99 *      operation returns 0. Failure operation returns negative error code.
 100 */
 101static int start_miim_ops(void __iomem *base,
 102                          u16 phyid, u32 reg, u16 val, u32 op)
 103{
 104        u32 param;
 105        int ret;
 106
 107        writel(0, base + MDIO_CTRL_OFFSET);
 108        ret = iproc_mdio_wait_for_idle(base, 0);
 109        if (ret)
 110                goto err;
 111
 112        param = readl(base + MDIO_PARAM_OFFSET);
 113        param |= phyid << MDIO_PARAM_PHY_ID;
 114        param |= val << MDIO_PARAM_PHY_DATA;
 115        if (reg & MII_ADDR_C45)
 116                param |= BIT(MDIO_PARAM_C45_SEL);
 117
 118        writel(param, base + MDIO_PARAM_OFFSET);
 119
 120        writel(reg, base + MDIO_ADDR_OFFSET);
 121
 122        writel(op, base + MDIO_CTRL_OFFSET);
 123
 124        ret = iproc_mdio_wait_for_idle(base, 1);
 125        if (ret)
 126                goto err;
 127
 128        if (op == MDIO_CTRL_READ_OP)
 129                ret = readl(base + MDIO_READ_OFFSET) & MDIO_READ_DATA_MASK;
 130err:
 131        return ret;
 132}
 133
 134static int iproc_mdiomux_read(struct mii_bus *bus, int phyid, int reg)
 135{
 136        struct iproc_mdiomux_desc *md = bus->priv;
 137        int ret;
 138
 139        ret = start_miim_ops(md->base, phyid, reg, 0, MDIO_CTRL_READ_OP);
 140        if (ret < 0)
 141                dev_err(&bus->dev, "mdiomux read operation failed!!!");
 142
 143        return ret;
 144}
 145
 146static int iproc_mdiomux_write(struct mii_bus *bus,
 147                               int phyid, int reg, u16 val)
 148{
 149        struct iproc_mdiomux_desc *md = bus->priv;
 150        int ret;
 151
 152        /* Write val at reg offset */
 153        ret = start_miim_ops(md->base, phyid, reg, val, MDIO_CTRL_WRITE_OP);
 154        if (ret < 0)
 155                dev_err(&bus->dev, "mdiomux write operation failed!!!");
 156
 157        return ret;
 158}
 159
 160static int mdio_mux_iproc_switch_fn(int current_child, int desired_child,
 161                                    void *data)
 162{
 163        struct iproc_mdiomux_desc *md = data;
 164        u32 param, bus_id;
 165        bool bus_dir;
 166
 167        /* select bus and its properties */
 168        bus_dir = (desired_child < EXT_BUS_START_ADDR);
 169        bus_id = bus_dir ? desired_child : (desired_child - EXT_BUS_START_ADDR);
 170
 171        param = (bus_dir ? 1 : 0) << MDIO_PARAM_INTERNAL_SEL;
 172        param |= (bus_id << MDIO_PARAM_BUS_ID);
 173
 174        writel(param, md->base + MDIO_PARAM_OFFSET);
 175        return 0;
 176}
 177
 178static int mdio_mux_iproc_probe(struct platform_device *pdev)
 179{
 180        struct iproc_mdiomux_desc *md;
 181        struct mii_bus *bus;
 182        struct resource *res;
 183        int rc;
 184
 185        md = devm_kzalloc(&pdev->dev, sizeof(*md), GFP_KERNEL);
 186        if (!md)
 187                return -ENOMEM;
 188        md->dev = &pdev->dev;
 189
 190        md->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
 191        if (IS_ERR(md->base))
 192                return PTR_ERR(md->base);
 193        if (res->start & 0xfff) {
 194                /* For backward compatibility in case the
 195                 * base address is specified with an offset.
 196                 */
 197                dev_info(&pdev->dev, "fix base address in dt-blob\n");
 198                res->start &= ~0xfff;
 199                res->end = res->start + MDIO_REG_ADDR_SPACE_SIZE - 1;
 200        }
 201
 202        md->mii_bus = devm_mdiobus_alloc(&pdev->dev);
 203        if (!md->mii_bus) {
 204                dev_err(&pdev->dev, "mdiomux bus alloc failed\n");
 205                return -ENOMEM;
 206        }
 207
 208        md->core_clk = devm_clk_get(&pdev->dev, NULL);
 209        if (md->core_clk == ERR_PTR(-ENOENT) ||
 210            md->core_clk == ERR_PTR(-EINVAL))
 211                md->core_clk = NULL;
 212        else if (IS_ERR(md->core_clk))
 213                return PTR_ERR(md->core_clk);
 214
 215        rc = clk_prepare_enable(md->core_clk);
 216        if (rc) {
 217                dev_err(&pdev->dev, "failed to enable core clk\n");
 218                return rc;
 219        }
 220
 221        bus = md->mii_bus;
 222        bus->priv = md;
 223        bus->name = "iProc MDIO mux bus";
 224        snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", pdev->name, pdev->id);
 225        bus->parent = &pdev->dev;
 226        bus->read = iproc_mdiomux_read;
 227        bus->write = iproc_mdiomux_write;
 228
 229        bus->phy_mask = ~0;
 230        bus->dev.of_node = pdev->dev.of_node;
 231        rc = mdiobus_register(bus);
 232        if (rc) {
 233                dev_err(&pdev->dev, "mdiomux registration failed\n");
 234                goto out_clk;
 235        }
 236
 237        platform_set_drvdata(pdev, md);
 238
 239        rc = mdio_mux_init(md->dev, md->dev->of_node, mdio_mux_iproc_switch_fn,
 240                           &md->mux_handle, md, md->mii_bus);
 241        if (rc) {
 242                dev_info(md->dev, "mdiomux initialization failed\n");
 243                goto out_register;
 244        }
 245
 246        mdio_mux_iproc_config(md);
 247
 248        dev_info(md->dev, "iProc mdiomux registered\n");
 249        return 0;
 250
 251out_register:
 252        mdiobus_unregister(bus);
 253out_clk:
 254        clk_disable_unprepare(md->core_clk);
 255        return rc;
 256}
 257
 258static int mdio_mux_iproc_remove(struct platform_device *pdev)
 259{
 260        struct iproc_mdiomux_desc *md = platform_get_drvdata(pdev);
 261
 262        mdio_mux_uninit(md->mux_handle);
 263        mdiobus_unregister(md->mii_bus);
 264        clk_disable_unprepare(md->core_clk);
 265
 266        return 0;
 267}
 268
 269#ifdef CONFIG_PM_SLEEP
 270static int mdio_mux_iproc_suspend(struct device *dev)
 271{
 272        struct iproc_mdiomux_desc *md = dev_get_drvdata(dev);
 273
 274        clk_disable_unprepare(md->core_clk);
 275
 276        return 0;
 277}
 278
 279static int mdio_mux_iproc_resume(struct device *dev)
 280{
 281        struct iproc_mdiomux_desc *md = dev_get_drvdata(dev);
 282        int rc;
 283
 284        rc = clk_prepare_enable(md->core_clk);
 285        if (rc) {
 286                dev_err(md->dev, "failed to enable core clk\n");
 287                return rc;
 288        }
 289        mdio_mux_iproc_config(md);
 290
 291        return 0;
 292}
 293#endif
 294
 295static SIMPLE_DEV_PM_OPS(mdio_mux_iproc_pm_ops,
 296                         mdio_mux_iproc_suspend, mdio_mux_iproc_resume);
 297
 298static const struct of_device_id mdio_mux_iproc_match[] = {
 299        {
 300                .compatible = "brcm,mdio-mux-iproc",
 301        },
 302        {},
 303};
 304MODULE_DEVICE_TABLE(of, mdio_mux_iproc_match);
 305
 306static struct platform_driver mdiomux_iproc_driver = {
 307        .driver = {
 308                .name           = "mdio-mux-iproc",
 309                .of_match_table = mdio_mux_iproc_match,
 310                .pm             = &mdio_mux_iproc_pm_ops,
 311        },
 312        .probe          = mdio_mux_iproc_probe,
 313        .remove         = mdio_mux_iproc_remove,
 314};
 315
 316module_platform_driver(mdiomux_iproc_driver);
 317
 318MODULE_DESCRIPTION("iProc MDIO Mux Bus Driver");
 319MODULE_AUTHOR("Pramod Kumar <pramod.kumar@broadcom.com>");
 320MODULE_LICENSE("GPL v2");
 321