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_address.h>
  34#include <linux/of_mdio.h>
  35#include <linux/of_platform.h>
  36
  37#define DELAY 1
  38
  39static void __iomem *gpio_regs;
  40
  41struct gpio_priv {
  42        int mdc_pin;
  43        int mdio_pin;
  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 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        prop = of_get_property(np, "mdc-pin", NULL);
 248        priv->mdc_pin = *prop;
 249
 250        prop = of_get_property(np, "mdio-pin", NULL);
 251        priv->mdio_pin = *prop;
 252
 253        new_bus->parent = dev;
 254        dev_set_drvdata(dev, new_bus);
 255
 256        err = of_mdiobus_register(new_bus, np);
 257
 258        if (err != 0) {
 259                printk(KERN_ERR "%s: Cannot register as MDIO bus, err %d\n",
 260                                new_bus->name, err);
 261                goto out_free_irq;
 262        }
 263
 264        return 0;
 265
 266out_free_irq:
 267        kfree(new_bus);
 268out_free_priv:
 269        kfree(priv);
 270out:
 271        return err;
 272}
 273
 274
 275static int gpio_mdio_remove(struct platform_device *dev)
 276{
 277        struct mii_bus *bus = dev_get_drvdata(&dev->dev);
 278
 279        mdiobus_unregister(bus);
 280
 281        dev_set_drvdata(&dev->dev, NULL);
 282
 283        kfree(bus->priv);
 284        bus->priv = NULL;
 285        mdiobus_free(bus);
 286
 287        return 0;
 288}
 289
 290static const struct of_device_id gpio_mdio_match[] =
 291{
 292        {
 293                .compatible      = "gpio-mdio",
 294        },
 295        {},
 296};
 297MODULE_DEVICE_TABLE(of, gpio_mdio_match);
 298
 299static struct platform_driver gpio_mdio_driver =
 300{
 301        .probe          = gpio_mdio_probe,
 302        .remove         = gpio_mdio_remove,
 303        .driver = {
 304                .name = "gpio-mdio-bitbang",
 305                .of_match_table = gpio_mdio_match,
 306        },
 307};
 308
 309static int gpio_mdio_init(void)
 310{
 311        struct device_node *np;
 312
 313        np = of_find_compatible_node(NULL, NULL, "1682m-gpio");
 314        if (!np)
 315                np = of_find_compatible_node(NULL, NULL,
 316                                             "pasemi,pwrficient-gpio");
 317        if (!np)
 318                return -ENODEV;
 319        gpio_regs = of_iomap(np, 0);
 320        of_node_put(np);
 321
 322        if (!gpio_regs)
 323                return -ENODEV;
 324
 325        return platform_driver_register(&gpio_mdio_driver);
 326}
 327module_init(gpio_mdio_init);
 328
 329static void gpio_mdio_exit(void)
 330{
 331        platform_driver_unregister(&gpio_mdio_driver);
 332        if (gpio_regs)
 333                iounmap(gpio_regs);
 334}
 335module_exit(gpio_mdio_exit);
 336
 337MODULE_LICENSE("GPL");
 338MODULE_AUTHOR("Olof Johansson <olof@lixom.net>");
 339MODULE_DESCRIPTION("Driver for MDIO over GPIO on PA Semi PWRficient-based boards");
 340