diff options
Diffstat (limited to 'drivers/net/phy/mdio-gpio.c')
| -rw-r--r-- | drivers/net/phy/mdio-gpio.c | 233 |
1 files changed, 103 insertions, 130 deletions
diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c index 35897134a5d..5f1a2250018 100644 --- a/drivers/net/phy/mdio-gpio.c +++ b/drivers/net/phy/mdio-gpio.c @@ -22,30 +22,71 @@ #include <linux/module.h> #include <linux/slab.h> -#include <linux/init.h> #include <linux/interrupt.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_mdio.h> -#include <linux/of_platform.h> -#endif struct mdio_gpio_info { struct mdiobb_ctrl ctrl; - int mdc, mdio; + int mdc, mdio, mdo; + int mdc_active_low, mdio_active_low, mdo_active_low; }; +static void *mdio_gpio_of_get_data(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct mdio_gpio_platform_data *pdata; + enum of_gpio_flags flags; + int ret; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + ret = of_get_gpio_flags(np, 0, &flags); + if (ret < 0) + return NULL; + + pdata->mdc = ret; + pdata->mdc_active_low = flags & OF_GPIO_ACTIVE_LOW; + + ret = of_get_gpio_flags(np, 1, &flags); + if (ret < 0) + return NULL; + pdata->mdio = ret; + pdata->mdio_active_low = flags & OF_GPIO_ACTIVE_LOW; + + ret = of_get_gpio_flags(np, 2, &flags); + if (ret > 0) { + pdata->mdo = ret; + pdata->mdo_active_low = flags & OF_GPIO_ACTIVE_LOW; + } + + return pdata; +} + static void mdio_dir(struct mdiobb_ctrl *ctrl, int dir) { struct mdio_gpio_info *bitbang = container_of(ctrl, struct mdio_gpio_info, ctrl); + if (bitbang->mdo) { + /* Separate output pin. Always set its value to high + * when changing direction. If direction is input, + * assume the pin serves as pull-up. If direction is + * output, the default value is high. + */ + gpio_set_value(bitbang->mdo, 1 ^ bitbang->mdo_active_low); + return; + } + if (dir) - gpio_direction_output(bitbang->mdio, 1); + gpio_direction_output(bitbang->mdio, + 1 ^ bitbang->mdio_active_low); else gpio_direction_input(bitbang->mdio); } @@ -55,7 +96,7 @@ static int mdio_get(struct mdiobb_ctrl *ctrl) struct mdio_gpio_info *bitbang = container_of(ctrl, struct mdio_gpio_info, ctrl); - return gpio_get_value(bitbang->mdio); + return gpio_get_value(bitbang->mdio) ^ bitbang->mdio_active_low; } static void mdio_set(struct mdiobb_ctrl *ctrl, int what) @@ -63,7 +104,10 @@ static void mdio_set(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); + if (bitbang->mdo) + gpio_set_value(bitbang->mdo, what ^ bitbang->mdo_active_low); + else + gpio_set_value(bitbang->mdio, what ^ bitbang->mdio_active_low); } static void mdc_set(struct mdiobb_ctrl *ctrl, int what) @@ -71,7 +115,7 @@ static void mdc_set(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); + gpio_set_value(bitbang->mdc, what ^ bitbang->mdc_active_low); } static struct mdiobb_ops mdio_gpio_ops = { @@ -82,25 +126,30 @@ static struct mdiobb_ops mdio_gpio_ops = { .get_mdio_data = mdio_get, }; -static struct mii_bus * __devinit mdio_gpio_bus_init(struct device *dev, - struct mdio_gpio_platform_data *pdata, - int bus_id) +static struct mii_bus *mdio_gpio_bus_init(struct device *dev, + struct mdio_gpio_platform_data *pdata, + int bus_id) { struct mii_bus *new_bus; struct mdio_gpio_info *bitbang; int i; - bitbang = kzalloc(sizeof(*bitbang), GFP_KERNEL); + bitbang = devm_kzalloc(dev, sizeof(*bitbang), GFP_KERNEL); if (!bitbang) goto out; bitbang->ctrl.ops = &mdio_gpio_ops; + bitbang->ctrl.reset = pdata->reset; bitbang->mdc = pdata->mdc; + bitbang->mdc_active_low = pdata->mdc_active_low; bitbang->mdio = pdata->mdio; + bitbang->mdio_active_low = pdata->mdio_active_low; + bitbang->mdo = pdata->mdo; + bitbang->mdo_active_low = pdata->mdo_active_low; new_bus = alloc_mdio_bitbang(&bitbang->ctrl); if (!new_bus) - goto out_free_bitbang; + goto out; new_bus->name = "GPIO Bitbanged MDIO", @@ -115,13 +164,20 @@ static struct mii_bus * __devinit mdio_gpio_bus_init(struct device *dev, if (!new_bus->irq[i]) new_bus->irq[i] = PHY_POLL; - snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", bus_id); + snprintf(new_bus->id, MII_BUS_ID_SIZE, "gpio-%x", bus_id); - if (gpio_request(bitbang->mdc, "mdc")) + if (devm_gpio_request(dev, bitbang->mdc, "mdc")) goto out_free_bus; - if (gpio_request(bitbang->mdio, "mdio")) - goto out_free_mdc; + if (devm_gpio_request(dev, bitbang->mdio, "mdio")) + goto out_free_bus; + + if (bitbang->mdo) { + if (devm_gpio_request(dev, bitbang->mdo, "mdo")) + goto out_free_bus; + gpio_direction_output(bitbang->mdo, 1); + gpio_direction_input(bitbang->mdio); + } gpio_direction_output(bitbang->mdc, 0); @@ -129,12 +185,8 @@ static struct mii_bus * __devinit mdio_gpio_bus_init(struct device *dev, return new_bus; -out_free_mdc: - gpio_free(bitbang->mdc); out_free_bus: free_mdio_bitbang(new_bus); -out_free_bitbang: - kfree(bitbang); out: return NULL; } @@ -142,16 +194,11 @@ out: static void mdio_gpio_bus_deinit(struct device *dev) { struct mii_bus *bus = dev_get_drvdata(dev); - struct mdio_gpio_info *bitbang = bus->priv; - dev_set_drvdata(dev, NULL); - gpio_free(bitbang->mdio); - gpio_free(bitbang->mdc); free_mdio_bitbang(bus); - kfree(bitbang); } -static void __devexit mdio_gpio_bus_destroy(struct device *dev) +static void mdio_gpio_bus_destroy(struct device *dev) { struct mii_bus *bus = dev_get_drvdata(dev); @@ -159,139 +206,65 @@ static void __devexit mdio_gpio_bus_destroy(struct device *dev) mdio_gpio_bus_deinit(dev); } -static int __devinit mdio_gpio_probe(struct platform_device *pdev) +static int mdio_gpio_probe(struct platform_device *pdev) { - struct mdio_gpio_platform_data *pdata = pdev->dev.platform_data; + struct mdio_gpio_platform_data *pdata; struct mii_bus *new_bus; - int ret; + int ret, bus_id; + + if (pdev->dev.of_node) { + pdata = mdio_gpio_of_get_data(pdev); + bus_id = of_alias_get_id(pdev->dev.of_node, "mdio-gpio"); + if (bus_id < 0) { + dev_warn(&pdev->dev, "failed to get alias id\n"); + bus_id = 0; + } + } else { + pdata = dev_get_platdata(&pdev->dev); + bus_id = pdev->id; + } if (!pdata) return -ENODEV; - new_bus = mdio_gpio_bus_init(&pdev->dev, pdata, pdev->id); + new_bus = mdio_gpio_bus_init(&pdev->dev, pdata, bus_id); if (!new_bus) return -ENODEV; - ret = mdiobus_register(new_bus); + if (pdev->dev.of_node) + ret = of_mdiobus_register(new_bus, pdev->dev.of_node); + else + ret = mdiobus_register(new_bus); + if (ret) mdio_gpio_bus_deinit(&pdev->dev); return ret; } -static int __devexit mdio_gpio_remove(struct platform_device *pdev) +static int mdio_gpio_remove(struct platform_device *pdev) { mdio_gpio_bus_destroy(&pdev->dev); return 0; } -#ifdef CONFIG_OF_GPIO - -static int __devinit mdio_ofgpio_probe(struct of_device *ofdev, - const struct of_device_id *match) -{ - struct mdio_gpio_platform_data *pdata; - struct mii_bus *new_bus; - int ret; - - pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - ret = of_get_gpio(ofdev->node, 0); - if (ret < 0) - goto out_free; - pdata->mdc = ret; - - ret = of_get_gpio(ofdev->node, 1); - if (ret < 0) - goto out_free; - pdata->mdio = ret; - - new_bus = mdio_gpio_bus_init(&ofdev->dev, pdata, pdata->mdc); - if (!new_bus) - goto out_free; - - ret = of_mdiobus_register(new_bus, ofdev->node); - if (ret) - mdio_gpio_bus_deinit(&ofdev->dev); - - return ret; - -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; -} - -static struct of_device_id mdio_ofgpio_match[] = { - { - .compatible = "virtual,mdio-gpio", - }, - {}, -}; -MODULE_DEVICE_TABLE(of, mdio_ofgpio_match); - -static struct of_platform_driver mdio_ofgpio_driver = { - .name = "mdio-gpio", - .match_table = mdio_ofgpio_match, - .probe = mdio_ofgpio_probe, - .remove = __devexit_p(mdio_ofgpio_remove), +static struct of_device_id mdio_gpio_of_match[] = { + { .compatible = "virtual,mdio-gpio", }, + { /* sentinel */ } }; -static inline int __init mdio_ofgpio_init(void) -{ - return of_register_platform_driver(&mdio_ofgpio_driver); -} - -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), + .remove = mdio_gpio_remove, .driver = { .name = "mdio-gpio", .owner = THIS_MODULE, + .of_match_table = mdio_gpio_of_match, }, }; -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_platform_driver(mdio_gpio_driver); MODULE_ALIAS("platform:mdio-gpio"); MODULE_AUTHOR("Laurent Pinchart, Paulius Zaleckas"); |
