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@xxxxxxxxxx> Cc: Grant Likely <grant.likely@xxxxxxxxxxxx> Cc: "David S. Miller" <davem@xxxxxxxxxxxxx> --- .../devicetree/bindings/net/mdio-mux-gpio.txt | 127 +++++++++++++++++ drivers/net/phy/Kconfig | 9 ++ drivers/net/phy/Makefile | 1 + drivers/net/phy/mdio-mux-gpio.c | 143 ++++++++++++++++++++ 4 files changed, 280 insertions(+), 0 deletions(-) create mode 100644 Documentation/devicetree/bindings/net/mdio-mux-gpio.txt create mode 100644 drivers/net/phy/mdio-mux-gpio.c diff --git a/Documentation/devicetree/bindings/net/mdio-mux-gpio.txt b/Documentation/devicetree/bindings/net/mdio-mux-gpio.txt new file mode 100644 index 0000000..23406a4 --- /dev/null +++ b/Documentation/devicetree/bindings/net/mdio-mux-gpio.txt @@ -0,0 +1,127 @@ +Properties for an MDIO bus multiplexer/switch controlled by GPIO pins. + +This is a special case of a MDIO bus multiplexer. One or more GPIO +lines are used to control which child bus is connected. + +Required properties in addition to the generic multiplexer properties: + +- compatible : Should define the compatible device type for the + multiplexer. Currently "cavium,mdio-mux-sn74cbtlv3253" + is defined, others are possible. +- gpios : GPIO specifiers for each GPIO line. One or more must be specified. + + +Example : + + /* The parent MDIO bus. */ + smi1: mdio@1180000001900 { + compatible = "cavium,octeon-3860-mdio"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x11800 0x00001900 0x0 0x40>; + }; + + /* + An NXP sn74cbtlv3253 dual 1-of-4 switch controlled by a + pair of GPIO lines. Child busses 2 and 3 populated with 4 + PHYs each. + */ + mdio-mux { + compatible = "cavium,mdio-mux-sn74cbtlv3253", "cavium,mdio-mux"; + gpios = <&gpio1 3 0>, <&gpio1 4 0>; + parent-bus = <&smi1>; + + mdio@2 { + cell-index = <2>; + #address-cells = <1>; + #size-cells = <0>; + + phy11: ethernet-phy@1 { + reg = <1>; + compatible = "marvell,88e1149r"; + marvell,reg-init = <3 0x10 0 0x5777>, + <3 0x11 0 0x00aa>, + <3 0x12 0 0x4105>, + <3 0x13 0 0x0a60>; + interrupt-parent = <&gpio>; + interrupts = <10 8>; /* Pin 10, active low */ + }; + phy12: ethernet-phy@2 { + reg = <2>; + compatible = "marvell,88e1149r"; + marvell,reg-init = <3 0x10 0 0x5777>, + <3 0x11 0 0x00aa>, + <3 0x12 0 0x4105>, + <3 0x13 0 0x0a60>; + interrupt-parent = <&gpio>; + interrupts = <10 8>; /* Pin 10, active low */ + }; + phy13: ethernet-phy@3 { + reg = <3>; + compatible = "marvell,88e1149r"; + marvell,reg-init = <3 0x10 0 0x5777>, + <3 0x11 0 0x00aa>, + <3 0x12 0 0x4105>, + <3 0x13 0 0x0a60>; + interrupt-parent = <&gpio>; + interrupts = <10 8>; /* Pin 10, active low */ + }; + phy14: ethernet-phy@4 { + reg = <4>; + compatible = "marvell,88e1149r"; + marvell,reg-init = <3 0x10 0 0x5777>, + <3 0x11 0 0x00aa>, + <3 0x12 0 0x4105>, + <3 0x13 0 0x0a60>; + interrupt-parent = <&gpio>; + interrupts = <10 8>; /* Pin 10, active low */ + }; + }; + + mdio@3 { + cell-index = <3>; + #address-cells = <1>; + #size-cells = <0>; + + phy21: ethernet-phy@1 { + reg = <1>; + compatible = "marvell,88e1149r"; + marvell,reg-init = <3 0x10 0 0x5777>, + <3 0x11 0 0x00aa>, + <3 0x12 0 0x4105>, + <3 0x13 0 0x0a60>; + interrupt-parent = <&gpio>; + interrupts = <12 8>; /* Pin 12, active low */ + }; + phy22: ethernet-phy@2 { + reg = <2>; + compatible = "marvell,88e1149r"; + marvell,reg-init = <3 0x10 0 0x5777>, + <3 0x11 0 0x00aa>, + <3 0x12 0 0x4105>, + <3 0x13 0 0x0a60>; + interrupt-parent = <&gpio>; + interrupts = <12 8>; /* Pin 12, active low */ + }; + phy23: ethernet-phy@3 { + reg = <3>; + compatible = "marvell,88e1149r"; + marvell,reg-init = <3 0x10 0 0x5777>, + <3 0x11 0 0x00aa>, + <3 0x12 0 0x4105>, + <3 0x13 0 0x0a60>; + interrupt-parent = <&gpio>; + interrupts = <12 8>; /* Pin 12, active low */ + }; + phy24: ethernet-phy@4 { + reg = <4>; + compatible = "marvell,88e1149r"; + marvell,reg-init = <3 0x10 0 0x5777>, + <3 0x11 0 0x00aa>, + <3 0x12 0 0x4105>, + <3 0x13 0 0x0a60>; + interrupt-parent = <&gpio>; + interrupts = <12 8>; /* Pin 12, active low */ + }; + }; + }; diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 59848bc..59b3b17 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -127,6 +127,15 @@ 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 MDIO_BUS_MUX && GENERIC_GPIO + 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. + config MDIO_OCTEON tristate "Support for MDIO buses on Octeon SOCs" depends on CPU_CAVIUM_OCTEON diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 0c081d5..d1a1927 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -24,3 +24,4 @@ obj-$(CONFIG_STE10XP) += ste10Xp.o obj-$(CONFIG_MICREL_PHY) += micrel.o obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.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 0000000..3cdad35 --- /dev/null +++ b/drivers/net/phy/mdio-mux-gpio.c @@ -0,0 +1,143 @@ +/* + * 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 Cavium Networks + */ + +#include <linux/platform_device.h> +#include <linux/of_mdio.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/gfp.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; +}; + +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_named_gpio_flags(pdev->dev.of_node, + "gpios", n, &f); + s->gpio[n] = gpio; + if (gpio < 0) { + r = -ENODEV; + goto err; + } + + 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_probe(pdev, mdio_mux_gpio_switch_fn, s); + + if (r == 0) + 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) +{ + return 0; +} + +static struct of_device_id mdio_mux_gpio_match[] = { + { + .compatible = "cavium,mdio-mux-sn74cbtlv3253", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, mdio_mux_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), +}; + +static int __init mdio_mux_gpio_mod_init(void) +{ + return platform_driver_register(&mdio_mux_gpio_driver); +} +late_initcall(mdio_mux_gpio_mod_init); + +static void __exit mdio_mux_gpio_mod_exit(void) +{ + platform_driver_unregister(&mdio_mux_gpio_driver); +} +module_exit(mdio_mux_gpio_mod_exit); + +MODULE_DESCRIPTION(DRV_DESCRIPTION); +MODULE_VERSION(DRV_VERSION); +MODULE_AUTHOR("David Daney"); +MODULE_LICENSE("GPL"); -- 1.7.2.3