diff options
author | David Daney <david.daney@cavium.com> | 2012-05-02 19:16:39 +0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-05-08 06:58:09 +0400 |
commit | 416912a129349788372e6ac27ab86bd9b18c8cc0 (patch) | |
tree | 239766992fc6878f89cce3037f65f757fc880dae /drivers/net/phy | |
parent | 0ca2997d145268e6b4ef000692061849cdab8348 (diff) | |
download | linux-416912a129349788372e6ac27ab86bd9b18c8cc0.tar.xz |
netdev/of/phy: Add MDIO bus multiplexer driven by GPIO lines.
The GPIO pins select which sub bus is connected to the master.
Initially tested with an sn74cbtlv3253 switch device wired into the
MDIO bus.
Signed-off-by: David Daney <david.daney@cavium.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/phy')
-rw-r--r-- | drivers/net/phy/Kconfig | 10 | ||||
-rw-r--r-- | drivers/net/phy/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/phy/mdio-mux-gpio.c | 142 |
3 files changed, 153 insertions, 0 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 99c0674a5935..944cdfb80fe4 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -144,6 +144,16 @@ config MDIO_BUS_MUX to a parent bus. Switching between child busses is done by device specific drivers. +config MDIO_BUS_MUX_GPIO + tristate "Support for GPIO controlled MDIO bus multiplexers" + depends on OF_GPIO && OF_MDIO + select MDIO_BUS_MUX + help + This module provides a driver for MDIO bus multiplexers that + are controlled via GPIO lines. The multiplexer connects one of + several child MDIO busses to a parent bus. Child bus + selection is under the control of GPIO lines. + endif # PHYLIB config MICREL_KS8995MA diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index a6b50e7715d0..f51af688ef8b 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -26,3 +26,4 @@ obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o obj-$(CONFIG_MICREL_KS8995MA) += spi_ks8995.o obj-$(CONFIG_AMD_PHY) += amd.o obj-$(CONFIG_MDIO_BUS_MUX) += mdio-mux.o +obj-$(CONFIG_MDIO_BUS_MUX_GPIO) += mdio-mux-gpio.o diff --git a/drivers/net/phy/mdio-mux-gpio.c b/drivers/net/phy/mdio-mux-gpio.c new file mode 100644 index 000000000000..e0cc4ef33dee --- /dev/null +++ b/drivers/net/phy/mdio-mux-gpio.c @@ -0,0 +1,142 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2011, 2012 Cavium, Inc. + */ + +#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/of_mdio.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/phy.h> +#include <linux/mdio-mux.h> +#include <linux/of_gpio.h> + +#define DRV_VERSION "1.0" +#define DRV_DESCRIPTION "GPIO controlled MDIO bus multiplexer driver" + +#define MDIO_MUX_GPIO_MAX_BITS 8 + +struct mdio_mux_gpio_state { + int gpio[MDIO_MUX_GPIO_MAX_BITS]; + unsigned int num_gpios; + void *mux_handle; +}; + +static int mdio_mux_gpio_switch_fn(int current_child, int desired_child, + void *data) +{ + int change; + unsigned int n; + struct mdio_mux_gpio_state *s = data; + + if (current_child == desired_child) + return 0; + + change = current_child == -1 ? -1 : current_child ^ desired_child; + + for (n = 0; n < s->num_gpios; n++) { + if (change & 1) + gpio_set_value_cansleep(s->gpio[n], + (desired_child & 1) != 0); + change >>= 1; + desired_child >>= 1; + } + + return 0; +} + +static int __devinit mdio_mux_gpio_probe(struct platform_device *pdev) +{ + enum of_gpio_flags f; + struct mdio_mux_gpio_state *s; + unsigned int num_gpios; + unsigned int n; + int r; + + if (!pdev->dev.of_node) + return -ENODEV; + + num_gpios = of_gpio_count(pdev->dev.of_node); + if (num_gpios == 0 || num_gpios > MDIO_MUX_GPIO_MAX_BITS) + return -ENODEV; + + s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL); + if (!s) + return -ENOMEM; + + s->num_gpios = num_gpios; + + for (n = 0; n < num_gpios; ) { + int gpio = of_get_gpio_flags(pdev->dev.of_node, n, &f); + if (gpio < 0) { + r = (gpio == -ENODEV) ? -EPROBE_DEFER : gpio; + goto err; + } + s->gpio[n] = gpio; + + n++; + + r = gpio_request(gpio, "mdio_mux_gpio"); + if (r) + goto err; + + r = gpio_direction_output(gpio, 0); + if (r) + goto err; + } + + r = mdio_mux_init(&pdev->dev, + mdio_mux_gpio_switch_fn, &s->mux_handle, s); + + if (r == 0) { + pdev->dev.platform_data = s; + return 0; + } +err: + while (n) { + n--; + gpio_free(s->gpio[n]); + } + devm_kfree(&pdev->dev, s); + return r; +} + +static int __devexit mdio_mux_gpio_remove(struct platform_device *pdev) +{ + struct mdio_mux_gpio_state *s = pdev->dev.platform_data; + mdio_mux_uninit(s->mux_handle); + return 0; +} + +static struct of_device_id mdio_mux_gpio_match[] = { + { + .compatible = "mdio-mux-gpio", + }, + { + /* Legacy compatible property. */ + .compatible = "cavium,mdio-mux-sn74cbtlv3253", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, mdio_mux_gpio_match); + +static struct platform_driver mdio_mux_gpio_driver = { + .driver = { + .name = "mdio-mux-gpio", + .owner = THIS_MODULE, + .of_match_table = mdio_mux_gpio_match, + }, + .probe = mdio_mux_gpio_probe, + .remove = __devexit_p(mdio_mux_gpio_remove), +}; + +module_platform_driver(mdio_mux_gpio_driver); + +MODULE_DESCRIPTION(DRV_DESCRIPTION); +MODULE_VERSION(DRV_VERSION); +MODULE_AUTHOR("David Daney"); +MODULE_LICENSE("GPL"); |