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{
 221        struct device *dev = &ofdev->dev;
 222        struct device_node *np = ofdev->dev.of_node;
 223        struct mii_bus *new_bus;
 224        struct gpio_priv *priv;
 225        const unsigned int *prop;
 226        int err;
 227
 228        err = -ENOMEM;
 229        priv = kzalloc(sizeof(struct gpio_priv), GFP_KERNEL);
 230        if (!priv)
 231                goto out;
 232
 233        new_bus = mdiobus_alloc();
 234
 235        if (!new_bus)
 236                goto out_free_priv;
 237
 238        new_bus->name = "pasemi gpio mdio bus";
 239        new_bus->read = &gpio_mdio_read;
 240        new_bus->write = &gpio_mdio_write;
 241        new_bus->reset = &gpio_mdio_reset;
 242
 243        prop = of_get_property(np, "reg", NULL);
 244        snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", *prop);
 245        new_bus->priv = priv;
 246
 247        new_bus->irq = priv->mdio_irqs;
 248
 249        prop = of_get_property(np, "mdc-pin", NULL);
 250        priv->mdc_pin = *prop;
 251
 252        prop = of_get_property(np, "mdio-pin", NULL);
 253        priv->mdio_pin = *prop;
 254
 255        new_bus->parent = dev;
 256        dev_set_drvdata(dev, new_bus);
 257
 258        err = of_mdiobus_register(new_bus, np);
 259
 260        if (err != 0) {
 261                printk(KERN_ERR "%s: Cannot register as MDIO bus, err %d\n",
 262                                new_bus->name, err);
 263                goto out_free_irq;
 264        }
 265
 266        return 0;
 267
 268out_free_irq:
 269        kfree(new_bus);
 270out_free_priv:
 271        kfree(priv);
 272out:
 273        return err;
 274}
 275
 276
 277static int gpio_mdio_remove(struct platform_device *dev)
 278{
 279        struct mii_bus *bus = dev_get_drvdata(&dev->dev);
 280
 281        mdiobus_unregister(bus);
 282
 283        dev_set_drvdata(&dev->dev, NULL);
 284
 285        kfree(bus->priv);
 286        bus->priv = NULL;
 287        mdiobus_free(bus);
 288
 289        return 0;
 290}
 291
 292static struct of_device_id gpio_mdio_match[] =
 293{
 294        {
 295                .compatible      = "gpio-mdio",
 296        },
 297        {},
 298};
 299MODULE_DEVICE_TABLE(of, gpio_mdio_match);
 300
 301static struct platform_driver gpio_mdio_driver =
 302{
 303        .probe          = gpio_mdio_probe,
 304        .remove         = gpio_mdio_remove,
 305        .driver = {
 306                .name = "gpio-mdio-bitbang",
 307                .owner = THIS_MODULE,
 308                .of_match_table = gpio_mdio_match,
 309        },
 310};
 311
 312int gpio_mdio_init(void)
 313{
 314        struct device_node *np;
 315
 316        np = of_find_compatible_node(NULL, NULL, "1682m-gpio");
 317        if (!np)
 318                np = of_find_compatible_node(NULL, NULL,
 319                                             "pasemi,pwrficient-gpio");
 320        if (!np)
 321                return -ENODEV;
 322        gpio_regs = of_iomap(np, 0);
 323        of_node_put(np);
 324
 325        if (!gpio_regs)
 326                return -ENODEV;
 327
 328        return platform_driver_register(&gpio_mdio_driver);
 329}
 330module_init(gpio_mdio_init);
 331
 332void gpio_mdio_exit(void)
 333{
 334        platform_driver_unregister(&gpio_mdio_driver);
 335        if (gpio_regs)
 336                iounmap(gpio_regs);
 337}
 338module_exit(gpio_mdio_exit);
 339
 340MODULE_LICENSE("GPL");
 341MODULE_AUTHOR("Olof Johansson <olof@lixom.net>");
 342MODULE_DESCRIPTION("Driver for MDIO over GPIO on PA Semi PWRficient-based boards");
 343