net: OpenFirmware GPIO based MDIO bitbang driver

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>
This commit is contained in:
Laurent Pinchart 2008-05-26 11:53:21 +02:00 committed by Jeff Garzik
parent 62c7832958
commit a5edeccb1a
4 changed files with 233 additions and 0 deletions

View file

@ -58,6 +58,7 @@ Table of Contents
o) Xilinx IP cores o) Xilinx IP cores
p) Freescale Synchronous Serial Interface p) Freescale Synchronous Serial Interface
q) USB EHCI controllers q) USB EHCI controllers
r) MDIO on GPIOs
VII - Marvell Discovery mv64[345]6x System Controller chips VII - Marvell Discovery mv64[345]6x System Controller chips
1) The /system-controller node 1) The /system-controller node
@ -2870,6 +2871,26 @@ platforms are moved over to use the flattened-device-tree model.
reg = <0xe8000000 32>; 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 VII - Marvell Discovery mv64[345]6x System Controller chips
=========================================================== ===========================================================

View file

@ -84,4 +84,10 @@ config MDIO_BITBANG
If in doubt, say N. 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 endif # PHYLIB

View file

@ -15,3 +15,4 @@ obj-$(CONFIG_ICPLUS_PHY) += icplus.o
obj-$(CONFIG_REALTEK_PHY) += realtek.o obj-$(CONFIG_REALTEK_PHY) += realtek.o
obj-$(CONFIG_FIXED_PHY) += fixed.o obj-$(CONFIG_FIXED_PHY) += fixed.o
obj-$(CONFIG_MDIO_BITBANG) += mdio-bitbang.o obj-$(CONFIG_MDIO_BITBANG) += mdio-bitbang.o
obj-$(CONFIG_MDIO_OF_GPIO) += mdio-ofgpio.o

View file

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