Path: blob/master/arch/powerpc/platforms/pasemi/gpio_mdio.c
10819 views
/*1* Copyright (C) 2006-2007 PA Semi, Inc2*3* Author: Olof Johansson, PA Semi4*5* Maintained by: Olof Johansson <[email protected]>6*7* Based on drivers/net/fs_enet/mii-bitbang.c.8*9* This program is free software; you can redistribute it and/or modify10* it under the terms of the GNU General Public License version 2 as11* published by the Free Software Foundation.12*13* This program is distributed in the hope that it will be useful,14* but WITHOUT ANY WARRANTY; without even the implied warranty of15* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the16* GNU General Public License for more details.17*18* You should have received a copy of the GNU General Public License19* along with this program; if not, write to the Free Software20* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA21*/2223#include <linux/io.h>24#include <linux/module.h>25#include <linux/types.h>26#include <linux/slab.h>27#include <linux/sched.h>28#include <linux/errno.h>29#include <linux/ioport.h>30#include <linux/interrupt.h>31#include <linux/phy.h>32#include <linux/of_mdio.h>33#include <linux/of_platform.h>3435#define DELAY 13637static void __iomem *gpio_regs;3839struct gpio_priv {40int mdc_pin;41int mdio_pin;42int mdio_irqs[PHY_MAX_ADDR];43};4445#define MDC_PIN(bus) (((struct gpio_priv *)bus->priv)->mdc_pin)46#define MDIO_PIN(bus) (((struct gpio_priv *)bus->priv)->mdio_pin)4748static inline void mdio_lo(struct mii_bus *bus)49{50out_le32(gpio_regs+0x10, 1 << MDIO_PIN(bus));51}5253static inline void mdio_hi(struct mii_bus *bus)54{55out_le32(gpio_regs, 1 << MDIO_PIN(bus));56}5758static inline void mdc_lo(struct mii_bus *bus)59{60out_le32(gpio_regs+0x10, 1 << MDC_PIN(bus));61}6263static inline void mdc_hi(struct mii_bus *bus)64{65out_le32(gpio_regs, 1 << MDC_PIN(bus));66}6768static inline void mdio_active(struct mii_bus *bus)69{70out_le32(gpio_regs+0x20, (1 << MDC_PIN(bus)) | (1 << MDIO_PIN(bus)));71}7273static inline void mdio_tristate(struct mii_bus *bus)74{75out_le32(gpio_regs+0x30, (1 << MDIO_PIN(bus)));76}7778static inline int mdio_read(struct mii_bus *bus)79{80return !!(in_le32(gpio_regs+0x40) & (1 << MDIO_PIN(bus)));81}8283static void clock_out(struct mii_bus *bus, int bit)84{85if (bit)86mdio_hi(bus);87else88mdio_lo(bus);89udelay(DELAY);90mdc_hi(bus);91udelay(DELAY);92mdc_lo(bus);93}9495/* Utility to send the preamble, address, and register (common to read and write). */96static void bitbang_pre(struct mii_bus *bus, int read, u8 addr, u8 reg)97{98int i;99100/* CFE uses a really long preamble (40 bits). We'll do the same. */101mdio_active(bus);102for (i = 0; i < 40; i++) {103clock_out(bus, 1);104}105106/* send the start bit (01) and the read opcode (10) or write (10) */107clock_out(bus, 0);108clock_out(bus, 1);109110clock_out(bus, read);111clock_out(bus, !read);112113/* send the PHY address */114for (i = 0; i < 5; i++) {115clock_out(bus, (addr & 0x10) != 0);116addr <<= 1;117}118119/* send the register address */120for (i = 0; i < 5; i++) {121clock_out(bus, (reg & 0x10) != 0);122reg <<= 1;123}124}125126static int gpio_mdio_read(struct mii_bus *bus, int phy_id, int location)127{128u16 rdreg;129int ret, i;130u8 addr = phy_id & 0xff;131u8 reg = location & 0xff;132133bitbang_pre(bus, 1, addr, reg);134135/* tri-state our MDIO I/O pin so we can read */136mdio_tristate(bus);137udelay(DELAY);138mdc_hi(bus);139udelay(DELAY);140mdc_lo(bus);141142/* read 16 bits of register data, MSB first */143rdreg = 0;144for (i = 0; i < 16; i++) {145mdc_lo(bus);146udelay(DELAY);147mdc_hi(bus);148udelay(DELAY);149mdc_lo(bus);150udelay(DELAY);151rdreg <<= 1;152rdreg |= mdio_read(bus);153}154155mdc_hi(bus);156udelay(DELAY);157mdc_lo(bus);158udelay(DELAY);159160ret = rdreg;161162return ret;163}164165static int gpio_mdio_write(struct mii_bus *bus, int phy_id, int location, u16 val)166{167int i;168169u8 addr = phy_id & 0xff;170u8 reg = location & 0xff;171u16 value = val & 0xffff;172173bitbang_pre(bus, 0, addr, reg);174175/* send the turnaround (10) */176mdc_lo(bus);177mdio_hi(bus);178udelay(DELAY);179mdc_hi(bus);180udelay(DELAY);181mdc_lo(bus);182mdio_lo(bus);183udelay(DELAY);184mdc_hi(bus);185udelay(DELAY);186187/* write 16 bits of register data, MSB first */188for (i = 0; i < 16; i++) {189mdc_lo(bus);190if (value & 0x8000)191mdio_hi(bus);192else193mdio_lo(bus);194udelay(DELAY);195mdc_hi(bus);196udelay(DELAY);197value <<= 1;198}199200/*201* Tri-state the MDIO line.202*/203mdio_tristate(bus);204mdc_lo(bus);205udelay(DELAY);206mdc_hi(bus);207udelay(DELAY);208return 0;209}210211static int gpio_mdio_reset(struct mii_bus *bus)212{213/*nothing here - dunno how to reset it*/214return 0;215}216217218static int __devinit gpio_mdio_probe(struct platform_device *ofdev)219{220struct device *dev = &ofdev->dev;221struct device_node *np = ofdev->dev.of_node;222struct mii_bus *new_bus;223struct gpio_priv *priv;224const unsigned int *prop;225int err;226227err = -ENOMEM;228priv = kzalloc(sizeof(struct gpio_priv), GFP_KERNEL);229if (!priv)230goto out;231232new_bus = mdiobus_alloc();233234if (!new_bus)235goto out_free_priv;236237new_bus->name = "pasemi gpio mdio bus";238new_bus->read = &gpio_mdio_read;239new_bus->write = &gpio_mdio_write;240new_bus->reset = &gpio_mdio_reset;241242prop = of_get_property(np, "reg", NULL);243snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", *prop);244new_bus->priv = priv;245246new_bus->irq = priv->mdio_irqs;247248prop = of_get_property(np, "mdc-pin", NULL);249priv->mdc_pin = *prop;250251prop = of_get_property(np, "mdio-pin", NULL);252priv->mdio_pin = *prop;253254new_bus->parent = dev;255dev_set_drvdata(dev, new_bus);256257err = of_mdiobus_register(new_bus, np);258259if (err != 0) {260printk(KERN_ERR "%s: Cannot register as MDIO bus, err %d\n",261new_bus->name, err);262goto out_free_irq;263}264265return 0;266267out_free_irq:268kfree(new_bus);269out_free_priv:270kfree(priv);271out:272return err;273}274275276static int gpio_mdio_remove(struct platform_device *dev)277{278struct mii_bus *bus = dev_get_drvdata(&dev->dev);279280mdiobus_unregister(bus);281282dev_set_drvdata(&dev->dev, NULL);283284kfree(bus->priv);285bus->priv = NULL;286mdiobus_free(bus);287288return 0;289}290291static struct of_device_id gpio_mdio_match[] =292{293{294.compatible = "gpio-mdio",295},296{},297};298MODULE_DEVICE_TABLE(of, gpio_mdio_match);299300static struct platform_driver gpio_mdio_driver =301{302.probe = gpio_mdio_probe,303.remove = gpio_mdio_remove,304.driver = {305.name = "gpio-mdio-bitbang",306.owner = THIS_MODULE,307.of_match_table = gpio_mdio_match,308},309};310311int gpio_mdio_init(void)312{313struct device_node *np;314315np = of_find_compatible_node(NULL, NULL, "1682m-gpio");316if (!np)317np = of_find_compatible_node(NULL, NULL,318"pasemi,pwrficient-gpio");319if (!np)320return -ENODEV;321gpio_regs = of_iomap(np, 0);322of_node_put(np);323324if (!gpio_regs)325return -ENODEV;326327return platform_driver_register(&gpio_mdio_driver);328}329module_init(gpio_mdio_init);330331void gpio_mdio_exit(void)332{333platform_driver_unregister(&gpio_mdio_driver);334if (gpio_regs)335iounmap(gpio_regs);336}337module_exit(gpio_mdio_exit);338339MODULE_LICENSE("GPL");340MODULE_AUTHOR("Olof Johansson <[email protected]>");341MODULE_DESCRIPTION("Driver for MDIO over GPIO on PA Semi PWRficient-based boards");342343344