diff --git a/Documentation/powerpc/booting-without-of.txt b/Documentation/powerpc/booting-without-of.txt index 1d2a772506cf..46a9dba11f2f 100644 --- a/Documentation/powerpc/booting-without-of.txt +++ b/Documentation/powerpc/booting-without-of.txt @@ -58,6 +58,7 @@ Table of Contents o) Xilinx IP cores p) Freescale Synchronous Serial Interface q) USB EHCI controllers + r) MDIO on GPIOs VII - Marvell Discovery mv64[345]6x System Controller chips 1) The /system-controller node @@ -2870,6 +2871,26 @@ platforms are moved over to use the flattened-device-tree model. reg = <0xe8000000 32>; }; + r) MDIO on GPIOs + + Currently defined compatibles: + - virtual,gpio-mdio + + MDC and MDIO lines connected to GPIO controllers are listed in the + gpios property as described in section VIII.1 in the following order: + + MDC, MDIO. + + Example: + + mdio { + compatible = "virtual,mdio-gpio"; + #address-cells = <1>; + #size-cells = <0>; + gpios = <&qe_pio_a 11 + &qe_pio_c 6>; + }; + VII - Marvell Discovery mv64[345]6x System Controller chips =========================================================== diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 284217c12839..d55932acd887 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -84,4 +84,10 @@ config MDIO_BITBANG If in doubt, say N. +config MDIO_OF_GPIO + tristate "Support for GPIO lib-based bitbanged MDIO buses" + depends on MDIO_BITBANG && OF_GPIO + ---help--- + Supports GPIO lib-based MDIO busses. + endif # PHYLIB diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 5997d6ef702b..eee329fa6f53 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -15,3 +15,4 @@ obj-$(CONFIG_ICPLUS_PHY) += icplus.o obj-$(CONFIG_REALTEK_PHY) += realtek.o obj-$(CONFIG_FIXED_PHY) += fixed.o obj-$(CONFIG_MDIO_BITBANG) += mdio-bitbang.o +obj-$(CONFIG_MDIO_OF_GPIO) += mdio-ofgpio.o diff --git a/drivers/net/phy/mdio-ofgpio.c b/drivers/net/phy/mdio-ofgpio.c new file mode 100644 index 000000000000..7edfc0c34835 --- /dev/null +++ b/drivers/net/phy/mdio-ofgpio.c @@ -0,0 +1,205 @@ +/* + * OpenFirmware GPIO based MDIO bitbang driver. + * + * Copyright (c) 2008 CSE Semaphore Belgium. + * by Laurent Pinchart + * + * Based on earlier work by + * + * Copyright (c) 2003 Intracom S.A. + * by Pantelis Antoniou + * + * 2005 (c) MontaVista Software, Inc. + * Vitaly Bordug + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include + +struct mdio_gpio_info { + struct mdiobb_ctrl ctrl; + int mdc, mdio; +}; + +static void mdio_dir(struct mdiobb_ctrl *ctrl, int dir) +{ + struct mdio_gpio_info *bitbang = + container_of(ctrl, struct mdio_gpio_info, ctrl); + + if (dir) + gpio_direction_output(bitbang->mdio, 1); + else + gpio_direction_input(bitbang->mdio); +} + +static int mdio_read(struct mdiobb_ctrl *ctrl) +{ + struct mdio_gpio_info *bitbang = + container_of(ctrl, struct mdio_gpio_info, ctrl); + + return gpio_get_value(bitbang->mdio); +} + +static void mdio(struct mdiobb_ctrl *ctrl, int what) +{ + struct mdio_gpio_info *bitbang = + container_of(ctrl, struct mdio_gpio_info, ctrl); + + gpio_set_value(bitbang->mdio, what); +} + +static void mdc(struct mdiobb_ctrl *ctrl, int what) +{ + struct mdio_gpio_info *bitbang = + container_of(ctrl, struct mdio_gpio_info, ctrl); + + gpio_set_value(bitbang->mdc, what); +} + +static struct mdiobb_ops mdio_gpio_ops = { + .owner = THIS_MODULE, + .set_mdc = mdc, + .set_mdio_dir = mdio_dir, + .set_mdio_data = mdio, + .get_mdio_data = mdio_read, +}; + +static int __devinit mdio_ofgpio_bitbang_init(struct mii_bus *bus, + struct device_node *np) +{ + struct mdio_gpio_info *bitbang = bus->priv; + + bitbang->mdc = of_get_gpio(np, 0); + bitbang->mdio = of_get_gpio(np, 1); + + if (bitbang->mdc < 0 || bitbang->mdio < 0) + return -ENODEV; + + snprintf(bus->id, MII_BUS_ID_SIZE, "%x", bitbang->mdc); + return 0; +} + +static void __devinit add_phy(struct mii_bus *bus, struct device_node *np) +{ + const u32 *data; + int len, id, irq; + + data = of_get_property(np, "reg", &len); + if (!data || len != 4) + return; + + id = *data; + bus->phy_mask &= ~(1 << id); + + irq = of_irq_to_resource(np, 0, NULL); + if (irq != NO_IRQ) + bus->irq[id] = irq; +} + +static int __devinit mdio_ofgpio_probe(struct of_device *ofdev, + const struct of_device_id *match) +{ + struct device_node *np = NULL; + struct mii_bus *new_bus; + struct mdio_gpio_info *bitbang; + int ret = -ENOMEM; + int i; + + bitbang = kzalloc(sizeof(struct mdio_gpio_info), GFP_KERNEL); + if (!bitbang) + goto out; + + bitbang->ctrl.ops = &mdio_gpio_ops; + + new_bus = alloc_mdio_bitbang(&bitbang->ctrl); + if (!new_bus) + goto out_free_priv; + + new_bus->name = "GPIO Bitbanged MII", + + ret = mdio_ofgpio_bitbang_init(new_bus, ofdev->node); + if (ret) + goto out_free_bus; + + new_bus->phy_mask = ~0; + new_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); + if (!new_bus->irq) + goto out_free_bus; + + for (i = 0; i < PHY_MAX_ADDR; i++) + new_bus->irq[i] = -1; + + while ((np = of_get_next_child(ofdev->node, np))) + if (!strcmp(np->type, "ethernet-phy")) + add_phy(new_bus, np); + + new_bus->dev = &ofdev->dev; + dev_set_drvdata(&ofdev->dev, new_bus); + + ret = mdiobus_register(new_bus); + if (ret) + goto out_free_irqs; + + return 0; + +out_free_irqs: + dev_set_drvdata(&ofdev->dev, NULL); + kfree(new_bus->irq); +out_free_bus: + kfree(new_bus); +out_free_priv: + free_mdio_bitbang(new_bus); +out: + return ret; +} + +static int mdio_ofgpio_remove(struct of_device *ofdev) +{ + struct mii_bus *bus = dev_get_drvdata(&ofdev->dev); + struct mdio_gpio_info *bitbang = bus->priv; + + mdiobus_unregister(bus); + free_mdio_bitbang(bus); + dev_set_drvdata(&ofdev->dev, NULL); + kfree(bus->irq); + kfree(bitbang); + kfree(bus); + + return 0; +} + +static struct of_device_id mdio_ofgpio_match[] = { + { + .compatible = "virtual,mdio-gpio", + }, + {}, +}; + +static struct of_platform_driver mdio_ofgpio_driver = { + .name = "mdio-gpio", + .match_table = mdio_ofgpio_match, + .probe = mdio_ofgpio_probe, + .remove = mdio_ofgpio_remove, +}; + +static int mdio_ofgpio_init(void) +{ + return of_register_platform_driver(&mdio_ofgpio_driver); +} + +static void mdio_ofgpio_exit(void) +{ + of_unregister_platform_driver(&mdio_ofgpio_driver); +} + +module_init(mdio_ofgpio_init); +module_exit(mdio_ofgpio_exit);