Skip to content

Commit

Permalink
net: OpenFirmware GPIO based MDIO bitbang driver
Browse files Browse the repository at this point in the history
This patch adds an MDIO bitbang driver that uses the GPIO library and its
OF bindings to access the bus I/Os.

Signed-off-by: Laurent Pinchart <laurentp@cse-semaphore.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
  • Loading branch information
Laurent Pinchart authored and Jeff Garzik committed May 31, 2008
1 parent 62c7832 commit a5edecc
Show file tree
Hide file tree
Showing 4 changed files with 233 additions and 0 deletions.
21 changes: 21 additions & 0 deletions Documentation/powerpc/booting-without-of.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
===========================================================

Expand Down
6 changes: 6 additions & 0 deletions drivers/net/phy/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions drivers/net/phy/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
205 changes: 205 additions & 0 deletions drivers/net/phy/mdio-ofgpio.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
/*
* OpenFirmware GPIO based MDIO bitbang driver.
*
* Copyright (c) 2008 CSE Semaphore Belgium.
* by Laurent Pinchart <laurentp@cse-semaphore.com>
*
* Based on earlier work by
*
* Copyright (c) 2003 Intracom S.A.
* by Pantelis Antoniou <panto@intracom.gr>
*
* 2005 (c) MontaVista Software, Inc.
* Vitaly Bordug <vbordug@ru.mvista.com>
*
* 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 <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/mdio-bitbang.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>

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);

0 comments on commit a5edecc

Please sign in to comment.