linux/arch/powerpc/platforms/pasemi/gpio_mdio.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2006-2007 PA Semi, Inc
   3 *
   4 * Author: Olof Johansson, PA Semi
   5 *
   6 * Maintained by: Olof Johansson <olof@lixom.net>
   7 *
   8 * Based on drivers/net/fs_enet/mii-bitbang.c.
   9 *
  10 * This program is free software; you can redistribute it and/or modify
  11 * it under the terms of the GNU General Public License version 2 as
  12 * published by the Free Software Foundation.
  13 *
  14 * This program is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17 * GNU General Public License for more details.
  18 *
  19 * You should have received a copy of the GNU General Public License
  20 * along with this program; if not, write to the Free Software
  21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  22 */
  23
  24#include <linux/io.h>
  25#include <linux/module.h>
  26#include <linux/types.h>
  27#include <linux/slab.h>
  28#include <linux/sched.h>
  29#include <linux/errno.h>
  30#include <linux/ioport.h>
  31#include <linux/interrupt.h>
  32#include <linux/phy.h>
  33#include <linux/of_mdio.h>
  34#include <linux/of_platform.h>
  35
  36#define DELAY 1
  37
  38static void __iomem *gpio_regs;
  39
  40struct gpio_priv {
  41        int mdc_pin;
  42        int mdio_pin;
  43        int mdio_irqs[PHY_MAX_ADDR];
  44};
  45
  46#define MDC_PIN(bus)    (((struct gpio_priv *)bus->priv)->mdc_pin)
  47#define MDIO_PIN(bus)   (((struct gpio_priv *)bus->priv)->mdio_pin)
  48
  49static inline void mdio_lo(struct mii_bus *bus)
  50{
  51        out_le32(gpio_regs+0x10, 1 << MDIO_PIN(bus));
  52}
  53
  54static inline void mdio_hi(struct mii_bus *bus)
  55{
  56        out_le32(gpio_regs, 1 << MDIO_PIN(bus));
  57}
  58
  59static inline void mdc_lo(struct mii_bus *bus)
  60{
  61        out_le32(gpio_regs+0x10, 1 << MDC_PIN(bus));
  62}
  63
  64static inline void mdc_hi(struct mii_bus *bus)
  65{
  66        out_le32(gpio_regs, 1 << MDC_PIN(bus));
  67}
  68
  69static inline void mdio_active(struct mii_bus *bus)
  70{
  71        out_le32(gpio_regs+0x20, (1 << MDC_PIN(bus)) | (1 << MDIO_PIN(bus)));
  72}
  73
  74static inline void mdio_tristate(struct mii_bus *bus)
  75{
  76        out_le32(gpio_regs+0x30, (1 << MDIO_PIN(bus)));
  77}
  78
  79static inline int mdio_read(struct mii_bus *bus)
  80{
  81        return !!(in_le32(gpio_regs+0x40) & (1 << MDIO_PIN(bus)));
  82}
  83
  84static void clock_out(struct mii_bus *bus, int bit)
  85{
  86        if (bit)
  87                mdio_hi(bus);
  88        else
  89                mdio_lo(bus);
  90        udelay(DELAY);
  91        mdc_hi(bus);
  92        udelay(DELAY);
  93        mdc_lo(bus);
  94}
  95
  96/* Utility to send the preamble, address, and register (common to read and write). */
  97static void bitbang_pre(struct mii_bus *bus, int read, u8 addr, u8 reg)
  98{
  99        int i;
 100
 101        /* CFE uses a really long preamble (40 bits). We'll do the same. */
 102        mdio_active(bus);
 103        for (i = 0; i < 40; i++) {
 104                clock_out(bus, 1);
 105        }
 106
 107        /* send the start bit (01) and the read opcode (10) or write (10) */
 108        clock_out(bus, 0);
 109        clock_out(bus, 1);
 110
 111        clock_out(bus, read);
 112        clock_out(bus, !read);
 113
 114        /* send the PHY address */
 115        for (i = 0; i < 5; i++) {
 116                clock_out(bus, (addr & 0x10) != 0);
 117                addr <<= 1;
 118        }
 119
 120        /* send the register address */
 121        for (i = 0; i < 5; i++) {
 122                clock_out(bus, (reg & 0x10) != 0);
 123                reg <<= 1;
 124        }
 125}
 126
 127static int gpio_mdio_read(struct mii_bus *bus, int phy_id, int location)
 128{
 129        u16 rdreg;
 130        int ret, i;
 131        u8 addr = phy_id & 0xff;
 132        u8 reg = location & 0xff;
 133
 134        bitbang_pre(bus, 1, addr, reg);
 135
 136        /* tri-state our MDIO I/O pin so we can read */
 137        mdio_tristate(bus);
 138        udelay(DELAY);
 139        mdc_hi(bus);
 140        udelay(DELAY);
 141        mdc_lo(bus);
 142
 143        /* read 16 bits of register data, MSB first */
 144        rdreg = 0;
 145        for (i = 0; i < 16; i++) {
 146                mdc_lo(bus);
 147                udelay(DELAY);
 148                mdc_hi(bus);
 149                udelay(DELAY);
 150                mdc_lo(bus);
 151                udelay(DELAY);
 152                rdreg <<= 1;
 153                rdreg |= mdio_read(bus);
 154        }
 155
 156        mdc_hi(bus);
 157        udelay(DELAY);
 158        mdc_lo(bus);
 159        udelay(DELAY);
 160
 161        ret = rdreg;
 162
 163        return ret;
 164}
 165
 166static int gpio_mdio_write(struct mii_bus *bus, int phy_id, int location, u16 val)
 167{
 168        int i;
 169
 170        u8 addr = phy_id & 0xff;
 171        u8 reg = location & 0xff;
 172        u16 value = val & 0xffff;
 173
 174        bitbang_pre(bus, 0, addr, reg);
 175
 176        /* send the turnaround (10) */
 177        mdc_lo(bus);
 178        mdio_hi(bus);
 179        udelay(DELAY);
 180        mdc_hi(bus);
 181        udelay(DELAY);
 182        mdc_lo(bus);
 183        mdio_lo(bus);
 184        udelay(DELAY);
 185        mdc_hi(bus);
 186        udelay(DELAY);
 187
 188        /* write 16 bits of register data, MSB first */
 189        for (i = 0; i < 16; i++) {
 190                mdc_lo(bus);
 191                if (value & 0x8000)
 192                        mdio_hi(bus);
 193                else
 194                        mdio_lo(bus);
 195                udelay(DELAY);
 196                mdc_hi(bus);
 197                udelay(DELAY);
 198                value <<= 1;
 199        }
 200
 201        /*
 202         * Tri-state the MDIO line.
 203         */
 204        mdio_tristate(bus);
 205        mdc_lo(bus);
 206        udelay(DELAY);
 207        mdc_hi(bus);
 208        udelay(DELAY);
 209        return 0;
 210}
 211
 212static int gpio_mdio_reset(struct mii_bus *bus)
 213{
 214        /*nothing here - dunno how to reset it*/
 215        return 0;
 216}
 217
 218
 219static int __devinit gpio_mdio_probe(struct platform_device *ofdev,
 220                                     const struct of_device_id *match)
 221{
 222        struct device *dev = &ofdev->dev;
 223        struct device_node *np = ofdev->dev.of_node;
 224        struct mii_bus *new_bus;
 225        struct gpio_priv *priv;
 226        const unsigned int *prop;
 227        int err;
 228
 229        err = -ENOMEM;
 230        priv = kzalloc(sizeof(struct gpio_priv), GFP_KERNEL);
 231        if (!priv)
 232                goto out;
 233
 234        new_bus = mdiobus_alloc();
 235
 236        if (!new_bus)
 237                goto out_free_priv;
 238
 239        new_bus->name = "pasemi gpio mdio bus";
 240        new_bus->read = &gpio_mdio_read;
 241        new_bus->write = &gpio_mdio_write;
 242        new_bus->reset = &gpio_mdio_reset;
 243
 244        prop = of_get_property(np, "reg", NULL);
 245        snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", *prop);
 246        new_bus->priv = priv;
 247
 248        new_bus->irq = priv->mdio_irqs;
 249
 250        prop = of_get_property(np, "mdc-pin", NULL);
 251        priv->mdc_pin = *prop;
 252
 253        prop = of_get_property(np, "mdio-pin", NULL);
 254        priv->mdio_pin = *prop;
 255
 256        new_bus->parent = dev;
 257        dev_set_drvdata(dev, new_bus);
 258
 259        err = of_mdiobus_register(new_bus, np);
 260
 261        if (err != 0) {
 262                printk(KERN_ERR "%s: Cannot register as MDIO bus, err %d\n",
 263                                new_bus->name, err);
 264                goto out_free_irq;
 265        }
 266
 267        return 0;
 268
 269out_free_irq:
 270        kfree(new_bus);
 271out_free_priv:
 272        kfree(priv);
 273out:
 274        return err;
 275}
 276
 277
 278static int gpio_mdio_remove(struct platform_device *dev)
 279{
 280        struct mii_bus *bus = dev_get_drvdata(&dev->dev);
 281
 282        mdiobus_unregister(bus);
 283
 284        dev_set_drvdata(&dev->dev, NULL);
 285
 286        kfree(bus->priv);
 287        bus->priv = NULL;
 288        mdiobus_free(bus);
 289
 290        return 0;
 291}
 292
 293static struct of_device_id gpio_mdio_match[] =
 294{
 295        {
 296                .compatible      = "gpio-mdio",
 297        },
 298        {},
 299};
 300MODULE_DEVICE_TABLE(of, gpio_mdio_match);
 301
 302static struct of_platform_driver gpio_mdio_driver =
 303{
 304        .probe          = gpio_mdio_probe,
 305        .remove         = gpio_mdio_remove,
 306        .driver = {
 307                .name = "gpio-mdio-bitbang",
 308                .owner = THIS_MODULE,
 309                .of_match_table = gpio_mdio_match,
 310        },
 311};
 312
 313int gpio_mdio_init(void)
 314{
 315        struct device_node *np;
 316
 317        np = of_find_compatible_node(NULL, NULL, "1682m-gpio");
 318        if (!np)
 319                np = of_find_compatible_node(NULL, NULL,
 320                                             "pasemi,pwrficient-gpio");
 321        if (!np)
 322                return -ENODEV;
 323        gpio_regs = of_iomap(np, 0);
 324        of_node_put(np);
 325
 326        if (!gpio_regs)
 327                return -ENODEV;
 328
 329        return of_register_platform_driver(&gpio_mdio_driver);
 330}
 331module_init(gpio_mdio_init);
 332
 333void gpio_mdio_exit(void)
 334{
 335        of_unregister_platform_driver(&gpio_mdio_driver);
 336        if (gpio_regs)
 337                iounmap(gpio_regs);
 338}
 339module_exit(gpio_mdio_exit);
 340
 341MODULE_LICENSE("GPL");
 342MODULE_AUTHOR("Olof Johansson <olof@lixom.net>");
 343MODULE_DESCRIPTION("Driver for MDIO over GPIO on PA Semi PWRficient-based boards");
 344