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        int mdio_irqs[PHY_MAX_ADDR];
  45};
  46
  47#define MDC_PIN(bus)    (((struct gpio_priv *)bus->priv)->mdc_pin)
  48#define MDIO_PIN(bus)   (((struct gpio_priv *)bus->priv)->mdio_pin)
  49
  50static inline void mdio_lo(struct mii_bus *bus)
  51{
  52        out_le32(gpio_regs+0x10, 1 << MDIO_PIN(bus));
  53}
  54
  55static inline void mdio_hi(struct mii_bus *bus)
  56{
  57        out_le32(gpio_regs, 1 << MDIO_PIN(bus));
  58}
  59
  60static inline void mdc_lo(struct mii_bus *bus)
  61{
  62        out_le32(gpio_regs+0x10, 1 << MDC_PIN(bus));
  63}
  64
  65static inline void mdc_hi(struct mii_bus *bus)
  66{
  67        out_le32(gpio_regs, 1 << MDC_PIN(bus));
  68}
  69
  70static inline void mdio_active(struct mii_bus *bus)
  71{
  72        out_le32(gpio_regs+0x20, (1 << MDC_PIN(bus)) | (1 << MDIO_PIN(bus)));
  73}
  74
  75static inline void mdio_tristate(struct mii_bus *bus)
  76{
  77        out_le32(gpio_regs+0x30, (1 << MDIO_PIN(bus)));
  78}
  79
  80static inline int mdio_read(struct mii_bus *bus)
  81{
  82        return !!(in_le32(gpio_regs+0x40) & (1 << MDIO_PIN(bus)));
  83}
  84
  85static void clock_out(struct mii_bus *bus, int bit)
  86{
  87        if (bit)
  88                mdio_hi(bus);
  89        else
  90                mdio_lo(bus);
  91        udelay(DELAY);
  92        mdc_hi(bus);
  93        udelay(DELAY);
  94        mdc_lo(bus);
  95}
  96
  97/* Utility to send the preamble, address, and register (common to read and write). */
  98static void bitbang_pre(struct mii_bus *bus, int read, u8 addr, u8 reg)
  99{
 100        int i;
 101
 102        /* CFE uses a really long preamble (40 bits). We'll do the same. */
 103        mdio_active(bus);
 104        for (i = 0; i < 40; i++) {
 105                clock_out(bus, 1);
 106        }
 107
 108        /* send the start bit (01) and the read opcode (10) or write (10) */
 109        clock_out(bus, 0);
 110        clock_out(bus, 1);
 111
 112        clock_out(bus, read);
 113        clock_out(bus, !read);
 114
 115        /* send the PHY address */
 116        for (i = 0; i < 5; i++) {
 117                clock_out(bus, (addr & 0x10) != 0);
 118                addr <<= 1;
 119        }
 120
 121        /* send the register address */
 122        for (i = 0; i < 5; i++) {
 123                clock_out(bus, (reg & 0x10) != 0);
 124                reg <<= 1;
 125        }
 126}
 127
 128static int gpio_mdio_read(struct mii_bus *bus, int phy_id, int location)
 129{
 130        u16 rdreg;
 131        int ret, i;
 132        u8 addr = phy_id & 0xff;
 133        u8 reg = location & 0xff;
 134
 135        bitbang_pre(bus, 1, addr, reg);
 136
 137        /* tri-state our MDIO I/O pin so we can read */
 138        mdio_tristate(bus);
 139        udelay(DELAY);
 140        mdc_hi(bus);
 141        udelay(DELAY);
 142        mdc_lo(bus);
 143
 144        /* read 16 bits of register data, MSB first */
 145        rdreg = 0;
 146        for (i = 0; i < 16; i++) {
 147                mdc_lo(bus);
 148                udelay(DELAY);
 149                mdc_hi(bus);
 150                udelay(DELAY);
 151                mdc_lo(bus);
 152                udelay(DELAY);
 153                rdreg <<= 1;
 154                rdreg |= mdio_read(bus);
 155        }
 156
 157        mdc_hi(bus);
 158        udelay(DELAY);
 159        mdc_lo(bus);
 160        udelay(DELAY);
 161
 162        ret = rdreg;
 163
 164        return ret;
 165}
 166
 167static int gpio_mdio_write(struct mii_bus *bus, int phy_id, int location, u16 val)
 168{
 169        int i;
 170
 171        u8 addr = phy_id & 0xff;
 172        u8 reg = location & 0xff;
 173        u16 value = val & 0xffff;
 174
 175        bitbang_pre(bus, 0, addr, reg);
 176
 177        /* send the turnaround (10) */
 178        mdc_lo(bus);
 179        mdio_hi(bus);
 180        udelay(DELAY);
 181        mdc_hi(bus);
 182        udelay(DELAY);
 183        mdc_lo(bus);
 184        mdio_lo(bus);
 185        udelay(DELAY);
 186        mdc_hi(bus);
 187        udelay(DELAY);
 188
 189        /* write 16 bits of register data, MSB first */
 190        for (i = 0; i < 16; i++) {
 191                mdc_lo(bus);
 192                if (value & 0x8000)
 193                        mdio_hi(bus);
 194                else
 195                        mdio_lo(bus);
 196                udelay(DELAY);
 197                mdc_hi(bus);
 198                udelay(DELAY);
 199                value <<= 1;
 200        }
 201
 202        /*
 203         * Tri-state the MDIO line.
 204         */
 205        mdio_tristate(bus);
 206        mdc_lo(bus);
 207        udelay(DELAY);
 208        mdc_hi(bus);
 209        udelay(DELAY);
 210        return 0;
 211}
 212
 213static int gpio_mdio_reset(struct mii_bus *bus)
 214{
 215        /*nothing here - dunno how to reset it*/
 216        return 0;
 217}
 218
 219
 220static int gpio_mdio_probe(struct platform_device *ofdev)
 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 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 platform_driver_register(&gpio_mdio_driver);
 330}
 331module_init(gpio_mdio_init);
 332
 333void gpio_mdio_exit(void)
 334{
 335        platform_driver_unregister(&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