diff options
Diffstat (limited to 'drivers/net/phy/mdio-gpio.c')
| -rw-r--r-- | drivers/net/phy/mdio-gpio.c | 232 | 
1 files changed, 162 insertions, 70 deletions
diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c index 2ff97754e57..a439ebeb431 100644 --- a/drivers/net/phy/mdio-gpio.c +++ b/drivers/net/phy/mdio-gpio.c @@ -1,9 +1,12 @@  /* - * OpenFirmware GPIO based MDIO bitbang driver. + * GPIO based MDIO bitbang driver. + * Supports OpenFirmware.   *   * Copyright (c) 2008 CSE Semaphore Belgium.   *  by Laurent Pinchart <laurentp@cse-semaphore.com>   * + * Copyright (C) 2008, Paulius Zaleckas <paulius.zaleckas@teltonika.lt> + *   * Based on earlier work by   *   * Copyright (c) 2003 Intracom S.A. @@ -21,9 +24,14 @@  #include <linux/slab.h>  #include <linux/init.h>  #include <linux/interrupt.h> -#include <linux/mdio-bitbang.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <linux/mdio-gpio.h> + +#ifdef CONFIG_OF_GPIO  #include <linux/of_gpio.h>  #include <linux/of_platform.h> +#endif  struct mdio_gpio_info {  	struct mdiobb_ctrl ctrl; @@ -41,7 +49,7 @@ static void mdio_dir(struct mdiobb_ctrl *ctrl, int dir)  		gpio_direction_input(bitbang->mdio);  } -static int mdio_read(struct mdiobb_ctrl *ctrl) +static int mdio_get(struct mdiobb_ctrl *ctrl)  {  	struct mdio_gpio_info *bitbang =  		container_of(ctrl, struct mdio_gpio_info, ctrl); @@ -49,7 +57,7 @@ static int mdio_read(struct mdiobb_ctrl *ctrl)  	return gpio_get_value(bitbang->mdio);  } -static void mdio(struct mdiobb_ctrl *ctrl, int what) +static void mdio_set(struct mdiobb_ctrl *ctrl, int what)  {  	struct mdio_gpio_info *bitbang =  		container_of(ctrl, struct mdio_gpio_info, ctrl); @@ -57,7 +65,7 @@ static void mdio(struct mdiobb_ctrl *ctrl, int what)  	gpio_set_value(bitbang->mdio, what);  } -static void mdc(struct mdiobb_ctrl *ctrl, int what) +static void mdc_set(struct mdiobb_ctrl *ctrl, int what)  {  	struct mdio_gpio_info *bitbang =  		container_of(ctrl, struct mdio_gpio_info, ctrl); @@ -67,93 +75,69 @@ static void mdc(struct mdiobb_ctrl *ctrl, int what)  static struct mdiobb_ops mdio_gpio_ops = {  	.owner = THIS_MODULE, -	.set_mdc = mdc, +	.set_mdc = mdc_set,  	.set_mdio_dir = mdio_dir, -	.set_mdio_data = mdio, -	.get_mdio_data = mdio_read, +	.set_mdio_data = mdio_set, +	.get_mdio_data = mdio_get,  }; -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) +static int __devinit mdio_gpio_bus_init(struct device *dev, +					struct mdio_gpio_platform_data *pdata, +					int bus_id)  { -	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); +	bitbang = kzalloc(sizeof(*bitbang), GFP_KERNEL);  	if (!bitbang)  		goto out;  	bitbang->ctrl.ops = &mdio_gpio_ops; +	bitbang->mdc = pdata->mdc; +	bitbang->mdio = pdata->mdio;  	new_bus = alloc_mdio_bitbang(&bitbang->ctrl);  	if (!new_bus)  		goto out_free_bitbang; -	new_bus->name = "GPIO Bitbanged MII", +	new_bus->name = "GPIO Bitbanged MDIO", -	ret = mdio_ofgpio_bitbang_init(new_bus, ofdev->node); -	if (ret) -		goto out_free_bus; +	ret = -ENODEV; -	new_bus->phy_mask = ~0; -	new_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); -	if (!new_bus->irq) +	new_bus->phy_mask = pdata->phy_mask; +	new_bus->irq = pdata->irqs; +	new_bus->parent = dev; + +	if (new_bus->phy_mask == ~0)  		goto out_free_bus;  	for (i = 0; i < PHY_MAX_ADDR; i++) -		new_bus->irq[i] = -1; +		if (!new_bus->irq[i]) +			new_bus->irq[i] = PHY_POLL; -	while ((np = of_get_next_child(ofdev->node, np))) -		if (!strcmp(np->type, "ethernet-phy")) -			add_phy(new_bus, np); +	snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", bus_id); + +	if (gpio_request(bitbang->mdc, "mdc")) +		goto out_free_bus; + +	if (gpio_request(bitbang->mdio, "mdio")) +		goto out_free_mdc; -	new_bus->parent = &ofdev->dev; -	dev_set_drvdata(&ofdev->dev, new_bus); +	dev_set_drvdata(dev, new_bus);  	ret = mdiobus_register(new_bus);  	if (ret) -		goto out_free_irqs; +		goto out_free_all;  	return 0; -out_free_irqs: -	dev_set_drvdata(&ofdev->dev, NULL); -	kfree(new_bus->irq); +out_free_all: +	dev_set_drvdata(dev, NULL); +	gpio_free(bitbang->mdio); +out_free_mdc: +	gpio_free(bitbang->mdc);  out_free_bus:  	free_mdio_bitbang(new_bus);  out_free_bitbang: @@ -162,16 +146,86 @@ out:  	return ret;  } -static int mdio_ofgpio_remove(struct of_device *ofdev) +static void __devexit mdio_gpio_bus_destroy(struct device *dev)  { -	struct mii_bus *bus = dev_get_drvdata(&ofdev->dev); +	struct mii_bus *bus = dev_get_drvdata(dev);  	struct mdio_gpio_info *bitbang = bus->priv;  	mdiobus_unregister(bus); -	kfree(bus->irq);  	free_mdio_bitbang(bus); -	dev_set_drvdata(&ofdev->dev, NULL); +	dev_set_drvdata(dev, NULL); +	gpio_free(bitbang->mdc); +	gpio_free(bitbang->mdio);  	kfree(bitbang); +} + +static int __devinit mdio_gpio_probe(struct platform_device *pdev) +{ +	struct mdio_gpio_platform_data *pdata = pdev->dev.platform_data; + +	if (!pdata) +		return -ENODEV; + +	return mdio_gpio_bus_init(&pdev->dev, pdata, pdev->id); +} + +static int __devexit mdio_gpio_remove(struct platform_device *pdev) +{ +	mdio_gpio_bus_destroy(&pdev->dev); + +	return 0; +} + +#ifdef CONFIG_OF_GPIO +static void __devinit add_phy(struct mdio_gpio_platform_data *pdata, +			      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; +	pdata->phy_mask &= ~(1 << id); + +	irq = of_irq_to_resource(np, 0, NULL); +	if (irq) +		pdata->irqs[id] = irq; +} + +static int __devinit mdio_ofgpio_probe(struct of_device *ofdev, +                                        const struct of_device_id *match) +{ +	struct device_node *np = NULL; +	struct mdio_gpio_platform_data *pdata; + +	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); +	if (!pdata) +		return -ENOMEM; + +	pdata->mdc = of_get_gpio(ofdev->node, 0); +	pdata->mdio = of_get_gpio(ofdev->node, 1); + +	if (pdata->mdc < 0 || pdata->mdio < 0) +		goto out_free; + +	while ((np = of_get_next_child(ofdev->node, np))) +		if (!strcmp(np->type, "ethernet-phy")) +			add_phy(pdata, np); + +	return mdio_gpio_bus_init(&ofdev->dev, pdata, pdata->mdc); + +out_free: +	kfree(pdata); +	return -ENODEV; +} + +static int __devexit mdio_ofgpio_remove(struct of_device *ofdev) +{ +	mdio_gpio_bus_destroy(&ofdev->dev); +	kfree(ofdev->dev.platform_data);  	return 0;  } @@ -187,18 +241,56 @@ static struct of_platform_driver mdio_ofgpio_driver = {  	.name = "mdio-gpio",  	.match_table = mdio_ofgpio_match,  	.probe = mdio_ofgpio_probe, -	.remove = mdio_ofgpio_remove, +	.remove = __devexit_p(mdio_ofgpio_remove),  }; -static int mdio_ofgpio_init(void) +static inline int __init mdio_ofgpio_init(void)  {  	return of_register_platform_driver(&mdio_ofgpio_driver);  } -static void mdio_ofgpio_exit(void) +static inline void __exit mdio_ofgpio_exit(void)  {  	of_unregister_platform_driver(&mdio_ofgpio_driver);  } +#else +static inline int __init mdio_ofgpio_init(void) { return 0; } +static inline void __exit mdio_ofgpio_exit(void) { } +#endif /* CONFIG_OF_GPIO */ + +static struct platform_driver mdio_gpio_driver = { +	.probe = mdio_gpio_probe, +	.remove = __devexit_p(mdio_gpio_remove), +	.driver		= { +		.name	= "mdio-gpio", +		.owner	= THIS_MODULE, +	}, +}; + +static int __init mdio_gpio_init(void) +{ +	int ret; + +	ret = mdio_ofgpio_init(); +	if (ret) +		return ret; + +	ret = platform_driver_register(&mdio_gpio_driver); +	if (ret) +		mdio_ofgpio_exit(); + +	return ret; +} +module_init(mdio_gpio_init); + +static void __exit mdio_gpio_exit(void) +{ +	platform_driver_unregister(&mdio_gpio_driver); +	mdio_ofgpio_exit(); +} +module_exit(mdio_gpio_exit); -module_init(mdio_ofgpio_init); -module_exit(mdio_ofgpio_exit); +MODULE_ALIAS("platform:mdio-gpio"); +MODULE_AUTHOR("Laurent Pinchart, Paulius Zaleckas"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic driver for MDIO bus emulation using GPIO");  |