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/of_address.h>
  28#include <linux/of_mdio.h>
  29#include <linux/platform_device.h>
  30#include <linux/delay.h>
  31
  32#define MVMDIO_SMI_DATA_SHIFT              0
  33#define MVMDIO_SMI_PHY_ADDR_SHIFT          16
  34#define MVMDIO_SMI_PHY_REG_SHIFT           21
  35#define MVMDIO_SMI_READ_OPERATION          BIT(26)
  36#define MVMDIO_SMI_WRITE_OPERATION         0
  37#define MVMDIO_SMI_READ_VALID              BIT(27)
  38#define MVMDIO_SMI_BUSY                    BIT(28)
  39
  40struct orion_mdio_dev {
  41        struct mutex lock;
  42        void __iomem *smireg;
  43};
  44
  45/* Wait for the SMI unit to be ready for another operation
  46 */
  47static int orion_mdio_wait_ready(struct mii_bus *bus)
  48{
  49        struct orion_mdio_dev *dev = bus->priv;
  50        int count;
  51        u32 val;
  52
  53        count = 0;
  54        while (1) {
  55                val = readl(dev->smireg);
  56                if (!(val & MVMDIO_SMI_BUSY))
  57                        break;
  58
  59                if (count > 100) {
  60                        dev_err(bus->parent, "Timeout: SMI busy for too long\n");
  61                        return -ETIMEDOUT;
  62                }
  63
  64                udelay(10);
  65                count++;
  66        }
  67
  68        return 0;
  69}
  70
  71static int orion_mdio_read(struct mii_bus *bus, int mii_id,
  72                           int regnum)
  73{
  74        struct orion_mdio_dev *dev = bus->priv;
  75        int count;
  76        u32 val;
  77        int ret;
  78
  79        mutex_lock(&dev->lock);
  80
  81        ret = orion_mdio_wait_ready(bus);
  82        if (ret < 0) {
  83                mutex_unlock(&dev->lock);
  84                return ret;
  85        }
  86
  87        writel(((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) |
  88                (regnum << MVMDIO_SMI_PHY_REG_SHIFT)  |
  89                MVMDIO_SMI_READ_OPERATION),
  90               dev->smireg);
  91
  92        /* Wait for the value to become available */
  93        count = 0;
  94        while (1) {
  95                val = readl(dev->smireg);
  96                if (val & MVMDIO_SMI_READ_VALID)
  97                        break;
  98
  99                if (count > 100) {
 100                        dev_err(bus->parent, "Timeout when reading PHY\n");
 101                        mutex_unlock(&dev->lock);
 102                        return -ETIMEDOUT;
 103                }
 104
 105                udelay(10);
 106                count++;
 107        }
 108
 109        mutex_unlock(&dev->lock);
 110
 111        return val & 0xFFFF;
 112}
 113
 114static int orion_mdio_write(struct mii_bus *bus, int mii_id,
 115                            int regnum, u16 value)
 116{
 117        struct orion_mdio_dev *dev = bus->priv;
 118        int ret;
 119
 120        mutex_lock(&dev->lock);
 121
 122        ret = orion_mdio_wait_ready(bus);
 123        if (ret < 0) {
 124                mutex_unlock(&dev->lock);
 125                return ret;
 126        }
 127
 128        writel(((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) |
 129                (regnum << MVMDIO_SMI_PHY_REG_SHIFT)  |
 130                MVMDIO_SMI_WRITE_OPERATION            |
 131                (value << MVMDIO_SMI_DATA_SHIFT)),
 132               dev->smireg);
 133
 134        mutex_unlock(&dev->lock);
 135
 136        return 0;
 137}
 138
 139static int orion_mdio_reset(struct mii_bus *bus)
 140{
 141        return 0;
 142}
 143
 144static int orion_mdio_probe(struct platform_device *pdev)
 145{
 146        struct device_node *np = pdev->dev.of_node;
 147        struct mii_bus *bus;
 148        struct orion_mdio_dev *dev;
 149        int i, ret;
 150
 151        bus = mdiobus_alloc_size(sizeof(struct orion_mdio_dev));
 152        if (!bus) {
 153                dev_err(&pdev->dev, "Cannot allocate MDIO bus\n");
 154                return -ENOMEM;
 155        }
 156
 157        bus->name = "orion_mdio_bus";
 158        bus->read = orion_mdio_read;
 159        bus->write = orion_mdio_write;
 160        bus->reset = orion_mdio_reset;
 161        snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii",
 162                 dev_name(&pdev->dev));
 163        bus->parent = &pdev->dev;
 164
 165        bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
 166        if (!bus->irq) {
 167                mdiobus_free(bus);
 168                return -ENOMEM;
 169        }
 170
 171        for (i = 0; i < PHY_MAX_ADDR; i++)
 172                bus->irq[i] = PHY_POLL;
 173
 174        dev = bus->priv;
 175        dev->smireg = of_iomap(pdev->dev.of_node, 0);
 176        if (!dev->smireg) {
 177                dev_err(&pdev->dev, "No SMI register address given in DT\n");
 178                kfree(bus->irq);
 179                mdiobus_free(bus);
 180                return -ENODEV;
 181        }
 182
 183        mutex_init(&dev->lock);
 184
 185        ret = of_mdiobus_register(bus, np);
 186        if (ret < 0) {
 187                dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
 188                iounmap(dev->smireg);
 189                kfree(bus->irq);
 190                mdiobus_free(bus);
 191                return ret;
 192        }
 193
 194        platform_set_drvdata(pdev, bus);
 195
 196        return 0;
 197}
 198
 199static int orion_mdio_remove(struct platform_device *pdev)
 200{
 201        struct mii_bus *bus = platform_get_drvdata(pdev);
 202        mdiobus_unregister(bus);
 203        kfree(bus->irq);
 204        mdiobus_free(bus);
 205        return 0;
 206}
 207
 208static const struct of_device_id orion_mdio_match[] = {
 209        { .compatible = "marvell,orion-mdio" },
 210        { }
 211};
 212MODULE_DEVICE_TABLE(of, orion_mdio_match);
 213
 214static struct platform_driver orion_mdio_driver = {
 215        .probe = orion_mdio_probe,
 216        .remove = orion_mdio_remove,
 217        .driver = {
 218                .name = "orion-mdio",
 219                .of_match_table = orion_mdio_match,
 220        },
 221};
 222
 223module_platform_driver(orion_mdio_driver);
 224
 225MODULE_DESCRIPTION("Marvell MDIO interface driver");
 226MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
 227MODULE_LICENSE("GPL");
 228
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.