netdev/of/phy: Add MDIO bus multiplexer driven by GPIO lines.
authorDavid Daney <david.daney@cavium.com>
Wed, 2 May 2012 15:16:39 +0000 (15:16 +0000)
committerDavid S. Miller <davem@davemloft.net>
Tue, 8 May 2012 02:58:09 +0000 (22:58 -0400)
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>
Documentation/devicetree/bindings/net/mdio-mux-gpio.txt [new file with mode: 0644]
drivers/net/phy/Kconfig
drivers/net/phy/Makefile
drivers/net/phy/mdio-mux-gpio.c [new file with mode: 0644]

diff --git a/Documentation/devicetree/bindings/net/mdio-mux-gpio.txt b/Documentation/devicetree/bindings/net/mdio-mux-gpio.txt
new file mode 100644 (file)
index 0000000..7938411
--- /dev/null
@@ -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 : mdio-mux-gpio.
+- 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 = "mdio-mux-gpio";
+               gpios = <&gpio1 3 0>, <&gpio1 4 0>;
+               mdio-parent-bus = <&smi1>;
+               #address-cells = <1>;
+               #size-cells = <0>;
+
+               mdio@2 {
+                       reg = <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 {
+                       reg = <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 */
+                       };
+               };
+       };
index 99c0674..944cdfb 100644 (file)
@@ -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
index a6b50e7..f51af68 100644 (file)
@@ -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 (file)
index 0000000..e0cc4ef
--- /dev/null
@@ -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");