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