linux/drivers/net/ethernet/marvell/mvmdio.c
<<
>>
Prefs
   1/*
   2 * Driver for the MDIO interface of Marvell network interfaces.
   3 *
   4 * Since the MDIO interface of Marvell network interfaces is shared
   5 * between all network interfaces, having a single driver allows to
   6 * handle concurrent accesses properly (you may have four Ethernet
   7 * ports, but they in fact share the same SMI interface to access the
   8 * MDIO bus). Moreover, this MDIO interface code is similar between
   9 * the mv643xx_eth driver and the mvneta driver. For now, it is only
  10 * used by the mvneta driver, but it could later be used by the
  11 * mv643xx_eth driver as well.
  12 *
  13 * Copyright (C) 2012 Marvell
  14 *
  15 * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
  16 *
  17 * This file is licensed under the terms of the GNU General Public
  18 * License version 2. This program is licensed "as is" without any
  19 * warranty of any kind, whether express or implied.
  20 */
  21
  22#include <linux/init.h>
  23#include <linux/kernel.h>
  24#include <linux/module.h>
  25#include <linux/mutex.h>
  26#include <linux/phy.h>
  27#include <linux/interrupt.h>
  28#include <linux/platform_device.h>
  29#include <linux/delay.h>
  30#include <linux/io.h>
  31#include <linux/clk.h>
  32#include <linux/of_mdio.h>
  33#include <linux/sched.h>
  34#include <linux/wait.h>
  35
  36#define MVMDIO_SMI_DATA_SHIFT              0
  37#define MVMDIO_SMI_PHY_ADDR_SHIFT          16
  38#define MVMDIO_SMI_PHY_REG_SHIFT           21
  39#define MVMDIO_SMI_READ_OPERATION          BIT(26)
  40#define MVMDIO_SMI_WRITE_OPERATION         0
  41#define MVMDIO_SMI_READ_VALID              BIT(27)
  42#define MVMDIO_SMI_BUSY                    BIT(28)
  43#define MVMDIO_ERR_INT_CAUSE               0x007C
  44#define  MVMDIO_ERR_INT_SMI_DONE           0x00000010
  45#define MVMDIO_ERR_INT_MASK                0x0080
  46
  47struct orion_mdio_dev {
  48        struct mutex lock;
  49        void __iomem *regs;
  50        struct clk *clk;
  51        /*
  52         * If we have access to the error interrupt pin (which is
  53         * somewhat misnamed as it not only reflects internal errors
  54         * but also reflects SMI completion), use that to wait for
  55         * SMI access completion instead of polling the SMI busy bit.
  56         */
  57        int err_interrupt;
  58        wait_queue_head_t smi_busy_wait;
  59};
  60
  61static int orion_mdio_smi_is_done(struct orion_mdio_dev *dev)
  62{
  63        return !(readl(dev->regs) & MVMDIO_SMI_BUSY);
  64}
  65
  66/* Wait for the SMI unit to be ready for another operation
  67 */
  68static int orion_mdio_wait_ready(struct mii_bus *bus)
  69{
  70        struct orion_mdio_dev *dev = bus->priv;
  71        int count;
  72
  73        if (dev->err_interrupt <= 0) {
  74                count = 0;
  75                while (1) {
  76                        if (orion_mdio_smi_is_done(dev))
  77                                break;
  78
  79                        if (count > 100) {
  80                                dev_err(bus->parent,
  81                                        "Timeout: SMI busy for too long\n");
  82                                return -ETIMEDOUT;
  83                        }
  84
  85                        udelay(10);
  86                        count++;
  87                }
  88        } else {
  89                if (!orion_mdio_smi_is_done(dev)) {
  90                        wait_event_timeout(dev->smi_busy_wait,
  91                                orion_mdio_smi_is_done(dev),
  92                                msecs_to_jiffies(100));
  93                        if (!orion_mdio_smi_is_done(dev))
  94                                return -ETIMEDOUT;
  95                }
  96        }
  97
  98        return 0;
  99}
 100
 101static int orion_mdio_read(struct mii_bus *bus, int mii_id,
 102                           int regnum)
 103{
 104        struct orion_mdio_dev *dev = bus->priv;
 105        int count;
 106        u32 val;
 107        int ret;
 108
 109        mutex_lock(&dev->lock);
 110
 111        ret = orion_mdio_wait_ready(bus);
 112        if (ret < 0) {
 113                mutex_unlock(&dev->lock);
 114                return ret;
 115        }
 116
 117        writel(((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) |
 118                (regnum << MVMDIO_SMI_PHY_REG_SHIFT)  |
 119                MVMDIO_SMI_READ_OPERATION),
 120               dev->regs);
 121
 122        /* Wait for the value to become available */
 123        count = 0;
 124        while (1) {
 125                val = readl(dev->regs);
 126                if (val & MVMDIO_SMI_READ_VALID)
 127                        break;
 128
 129                if (count > 100) {
 130                        dev_err(bus->parent, "Timeout when reading PHY\n");
 131                        mutex_unlock(&dev->lock);
 132                        return -ETIMEDOUT;
 133                }
 134
 135                udelay(10);
 136                count++;
 137        }
 138
 139        mutex_unlock(&dev->lock);
 140
 141        return val & 0xFFFF;
 142}
 143
 144static int orion_mdio_write(struct mii_bus *bus, int mii_id,
 145                            int regnum, u16 value)
 146{
 147        struct orion_mdio_dev *dev = bus->priv;
 148        int ret;
 149
 150        mutex_lock(&dev->lock);
 151
 152        ret = orion_mdio_wait_ready(bus);
 153        if (ret < 0) {
 154                mutex_unlock(&dev->lock);
 155                return ret;
 156        }
 157
 158        writel(((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) |
 159                (regnum << MVMDIO_SMI_PHY_REG_SHIFT)  |
 160                MVMDIO_SMI_WRITE_OPERATION            |
 161                (value << MVMDIO_SMI_DATA_SHIFT)),
 162               dev->regs);
 163
 164        mutex_unlock(&dev->lock);
 165
 166        return 0;
 167}
 168
 169static int orion_mdio_reset(struct mii_bus *bus)
 170{
 171        return 0;
 172}
 173
 174static irqreturn_t orion_mdio_err_irq(int irq, void *dev_id)
 175{
 176        struct orion_mdio_dev *dev = dev_id;
 177
 178        if (readl(dev->regs + MVMDIO_ERR_INT_CAUSE) &
 179                        MVMDIO_ERR_INT_SMI_DONE) {
 180                writel(~MVMDIO_ERR_INT_SMI_DONE,
 181                                dev->regs + MVMDIO_ERR_INT_CAUSE);
 182                wake_up(&dev->smi_busy_wait);
 183                return IRQ_HANDLED;
 184        }
 185
 186        return IRQ_NONE;
 187}
 188
 189static int orion_mdio_probe(struct platform_device *pdev)
 190{
 191        struct resource *r;
 192        struct mii_bus *bus;
 193        struct orion_mdio_dev *dev;
 194        int i, ret;
 195
 196        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 197        if (!r) {
 198                dev_err(&pdev->dev, "No SMI register address given\n");
 199                return -ENODEV;
 200        }
 201
 202        bus = mdiobus_alloc_size(sizeof(struct orion_mdio_dev));
 203        if (!bus) {
 204                dev_err(&pdev->dev, "Cannot allocate MDIO bus\n");
 205                return -ENOMEM;
 206        }
 207
 208        bus->name = "orion_mdio_bus";
 209        bus->read = orion_mdio_read;
 210        bus->write = orion_mdio_write;
 211        bus->reset = orion_mdio_reset;
 212        snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii",
 213                 dev_name(&pdev->dev));
 214        bus->parent = &pdev->dev;
 215
 216        bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
 217        if (!bus->irq) {
 218                mdiobus_free(bus);
 219                return -ENOMEM;
 220        }
 221
 222        for (i = 0; i < PHY_MAX_ADDR; i++)
 223                bus->irq[i] = PHY_POLL;
 224
 225        dev = bus->priv;
 226        dev->regs = devm_ioremap(&pdev->dev, r->start, resource_size(r));
 227        if (!dev->regs) {
 228                dev_err(&pdev->dev, "Unable to remap SMI register\n");
 229                ret = -ENODEV;
 230                goto out_mdio;
 231        }
 232
 233        init_waitqueue_head(&dev->smi_busy_wait);
 234
 235        dev->clk = devm_clk_get(&pdev->dev, NULL);
 236        if (!IS_ERR(dev->clk))
 237                clk_prepare_enable(dev->clk);
 238
 239        dev->err_interrupt = platform_get_irq(pdev, 0);
 240        if (dev->err_interrupt != -ENXIO) {
 241                ret = devm_request_irq(&pdev->dev, dev->err_interrupt,
 242                                        orion_mdio_err_irq,
 243                                        IRQF_SHARED, pdev->name, dev);
 244                if (ret)
 245                        goto out_mdio;
 246
 247                writel(MVMDIO_ERR_INT_SMI_DONE,
 248                        dev->regs + MVMDIO_ERR_INT_MASK);
 249        }
 250
 251        mutex_init(&dev->lock);
 252
 253        if (pdev->dev.of_node)
 254                ret = of_mdiobus_register(bus, pdev->dev.of_node);
 255        else
 256                ret = mdiobus_register(bus);
 257        if (ret < 0) {
 258                dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
 259                goto out_mdio;
 260        }
 261
 262        platform_set_drvdata(pdev, bus);
 263
 264        return 0;
 265
 266out_mdio:
 267        if (!IS_ERR(dev->clk))
 268                clk_disable_unprepare(dev->clk);
 269        kfree(bus->irq);
 270        mdiobus_free(bus);
 271        return ret;
 272}
 273
 274static int orion_mdio_remove(struct platform_device *pdev)
 275{
 276        struct mii_bus *bus = platform_get_drvdata(pdev);
 277        struct orion_mdio_dev *dev = bus->priv;
 278
 279        writel(0, dev->regs + MVMDIO_ERR_INT_MASK);
 280        mdiobus_unregister(bus);
 281        kfree(bus->irq);
 282        mdiobus_free(bus);
 283        if (!IS_ERR(dev->clk))
 284                clk_disable_unprepare(dev->clk);
 285
 286        return 0;
 287}
 288
 289static const struct of_device_id orion_mdio_match[] = {
 290        { .compatible = "marvell,orion-mdio" },
 291        { }
 292};
 293MODULE_DEVICE_TABLE(of, orion_mdio_match);
 294
 295static struct platform_driver orion_mdio_driver = {
 296        .probe = orion_mdio_probe,
 297        .remove = orion_mdio_remove,
 298        .driver = {
 299                .name = "orion-mdio",
 300                .of_match_table = orion_mdio_match,
 301        },
 302};
 303
 304module_platform_driver(orion_mdio_driver);
 305
 306MODULE_DESCRIPTION("Marvell MDIO interface driver");
 307MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
 308MODULE_LICENSE("GPL");
 309MODULE_ALIAS("platform:orion-mdio");
 310