Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

David Daney and committed by
David S. Miller
416912a1 0ca2997d

+280
+127
Documentation/devicetree/bindings/net/mdio-mux-gpio.txt
··· 1 + Properties for an MDIO bus multiplexer/switch controlled by GPIO pins. 2 + 3 + This is a special case of a MDIO bus multiplexer. One or more GPIO 4 + lines are used to control which child bus is connected. 5 + 6 + Required properties in addition to the generic multiplexer properties: 7 + 8 + - compatible : mdio-mux-gpio. 9 + - gpios : GPIO specifiers for each GPIO line. One or more must be specified. 10 + 11 + 12 + Example : 13 + 14 + /* The parent MDIO bus. */ 15 + smi1: mdio@1180000001900 { 16 + compatible = "cavium,octeon-3860-mdio"; 17 + #address-cells = <1>; 18 + #size-cells = <0>; 19 + reg = <0x11800 0x00001900 0x0 0x40>; 20 + }; 21 + 22 + /* 23 + An NXP sn74cbtlv3253 dual 1-of-4 switch controlled by a 24 + pair of GPIO lines. Child busses 2 and 3 populated with 4 25 + PHYs each. 26 + */ 27 + mdio-mux { 28 + compatible = "mdio-mux-gpio"; 29 + gpios = <&gpio1 3 0>, <&gpio1 4 0>; 30 + mdio-parent-bus = <&smi1>; 31 + #address-cells = <1>; 32 + #size-cells = <0>; 33 + 34 + mdio@2 { 35 + reg = <2>; 36 + #address-cells = <1>; 37 + #size-cells = <0>; 38 + 39 + phy11: ethernet-phy@1 { 40 + reg = <1>; 41 + compatible = "marvell,88e1149r"; 42 + marvell,reg-init = <3 0x10 0 0x5777>, 43 + <3 0x11 0 0x00aa>, 44 + <3 0x12 0 0x4105>, 45 + <3 0x13 0 0x0a60>; 46 + interrupt-parent = <&gpio>; 47 + interrupts = <10 8>; /* Pin 10, active low */ 48 + }; 49 + phy12: ethernet-phy@2 { 50 + reg = <2>; 51 + compatible = "marvell,88e1149r"; 52 + marvell,reg-init = <3 0x10 0 0x5777>, 53 + <3 0x11 0 0x00aa>, 54 + <3 0x12 0 0x4105>, 55 + <3 0x13 0 0x0a60>; 56 + interrupt-parent = <&gpio>; 57 + interrupts = <10 8>; /* Pin 10, active low */ 58 + }; 59 + phy13: ethernet-phy@3 { 60 + reg = <3>; 61 + compatible = "marvell,88e1149r"; 62 + marvell,reg-init = <3 0x10 0 0x5777>, 63 + <3 0x11 0 0x00aa>, 64 + <3 0x12 0 0x4105>, 65 + <3 0x13 0 0x0a60>; 66 + interrupt-parent = <&gpio>; 67 + interrupts = <10 8>; /* Pin 10, active low */ 68 + }; 69 + phy14: ethernet-phy@4 { 70 + reg = <4>; 71 + compatible = "marvell,88e1149r"; 72 + marvell,reg-init = <3 0x10 0 0x5777>, 73 + <3 0x11 0 0x00aa>, 74 + <3 0x12 0 0x4105>, 75 + <3 0x13 0 0x0a60>; 76 + interrupt-parent = <&gpio>; 77 + interrupts = <10 8>; /* Pin 10, active low */ 78 + }; 79 + }; 80 + 81 + mdio@3 { 82 + reg = <3>; 83 + #address-cells = <1>; 84 + #size-cells = <0>; 85 + 86 + phy21: ethernet-phy@1 { 87 + reg = <1>; 88 + compatible = "marvell,88e1149r"; 89 + marvell,reg-init = <3 0x10 0 0x5777>, 90 + <3 0x11 0 0x00aa>, 91 + <3 0x12 0 0x4105>, 92 + <3 0x13 0 0x0a60>; 93 + interrupt-parent = <&gpio>; 94 + interrupts = <12 8>; /* Pin 12, active low */ 95 + }; 96 + phy22: ethernet-phy@2 { 97 + reg = <2>; 98 + compatible = "marvell,88e1149r"; 99 + marvell,reg-init = <3 0x10 0 0x5777>, 100 + <3 0x11 0 0x00aa>, 101 + <3 0x12 0 0x4105>, 102 + <3 0x13 0 0x0a60>; 103 + interrupt-parent = <&gpio>; 104 + interrupts = <12 8>; /* Pin 12, active low */ 105 + }; 106 + phy23: ethernet-phy@3 { 107 + reg = <3>; 108 + compatible = "marvell,88e1149r"; 109 + marvell,reg-init = <3 0x10 0 0x5777>, 110 + <3 0x11 0 0x00aa>, 111 + <3 0x12 0 0x4105>, 112 + <3 0x13 0 0x0a60>; 113 + interrupt-parent = <&gpio>; 114 + interrupts = <12 8>; /* Pin 12, active low */ 115 + }; 116 + phy24: ethernet-phy@4 { 117 + reg = <4>; 118 + compatible = "marvell,88e1149r"; 119 + marvell,reg-init = <3 0x10 0 0x5777>, 120 + <3 0x11 0 0x00aa>, 121 + <3 0x12 0 0x4105>, 122 + <3 0x13 0 0x0a60>; 123 + interrupt-parent = <&gpio>; 124 + interrupts = <12 8>; /* Pin 12, active low */ 125 + }; 126 + }; 127 + };
+10
drivers/net/phy/Kconfig
··· 144 144 to a parent bus. Switching between child busses is done by 145 145 device specific drivers. 146 146 147 + config MDIO_BUS_MUX_GPIO 148 + tristate "Support for GPIO controlled MDIO bus multiplexers" 149 + depends on OF_GPIO && OF_MDIO 150 + select MDIO_BUS_MUX 151 + help 152 + This module provides a driver for MDIO bus multiplexers that 153 + are controlled via GPIO lines. The multiplexer connects one of 154 + several child MDIO busses to a parent bus. Child bus 155 + selection is under the control of GPIO lines. 156 + 147 157 endif # PHYLIB 148 158 149 159 config MICREL_KS8995MA
+1
drivers/net/phy/Makefile
··· 26 26 obj-$(CONFIG_MICREL_KS8995MA) += spi_ks8995.o 27 27 obj-$(CONFIG_AMD_PHY) += amd.o 28 28 obj-$(CONFIG_MDIO_BUS_MUX) += mdio-mux.o 29 + obj-$(CONFIG_MDIO_BUS_MUX_GPIO) += mdio-mux-gpio.o
+142
drivers/net/phy/mdio-mux-gpio.c
··· 1 + /* 2 + * This file is subject to the terms and conditions of the GNU General Public 3 + * License. See the file "COPYING" in the main directory of this archive 4 + * for more details. 5 + * 6 + * Copyright (C) 2011, 2012 Cavium, Inc. 7 + */ 8 + 9 + #include <linux/platform_device.h> 10 + #include <linux/device.h> 11 + #include <linux/of_mdio.h> 12 + #include <linux/module.h> 13 + #include <linux/init.h> 14 + #include <linux/phy.h> 15 + #include <linux/mdio-mux.h> 16 + #include <linux/of_gpio.h> 17 + 18 + #define DRV_VERSION "1.0" 19 + #define DRV_DESCRIPTION "GPIO controlled MDIO bus multiplexer driver" 20 + 21 + #define MDIO_MUX_GPIO_MAX_BITS 8 22 + 23 + struct mdio_mux_gpio_state { 24 + int gpio[MDIO_MUX_GPIO_MAX_BITS]; 25 + unsigned int num_gpios; 26 + void *mux_handle; 27 + }; 28 + 29 + static int mdio_mux_gpio_switch_fn(int current_child, int desired_child, 30 + void *data) 31 + { 32 + int change; 33 + unsigned int n; 34 + struct mdio_mux_gpio_state *s = data; 35 + 36 + if (current_child == desired_child) 37 + return 0; 38 + 39 + change = current_child == -1 ? -1 : current_child ^ desired_child; 40 + 41 + for (n = 0; n < s->num_gpios; n++) { 42 + if (change & 1) 43 + gpio_set_value_cansleep(s->gpio[n], 44 + (desired_child & 1) != 0); 45 + change >>= 1; 46 + desired_child >>= 1; 47 + } 48 + 49 + return 0; 50 + } 51 + 52 + static int __devinit mdio_mux_gpio_probe(struct platform_device *pdev) 53 + { 54 + enum of_gpio_flags f; 55 + struct mdio_mux_gpio_state *s; 56 + unsigned int num_gpios; 57 + unsigned int n; 58 + int r; 59 + 60 + if (!pdev->dev.of_node) 61 + return -ENODEV; 62 + 63 + num_gpios = of_gpio_count(pdev->dev.of_node); 64 + if (num_gpios == 0 || num_gpios > MDIO_MUX_GPIO_MAX_BITS) 65 + return -ENODEV; 66 + 67 + s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL); 68 + if (!s) 69 + return -ENOMEM; 70 + 71 + s->num_gpios = num_gpios; 72 + 73 + for (n = 0; n < num_gpios; ) { 74 + int gpio = of_get_gpio_flags(pdev->dev.of_node, n, &f); 75 + if (gpio < 0) { 76 + r = (gpio == -ENODEV) ? -EPROBE_DEFER : gpio; 77 + goto err; 78 + } 79 + s->gpio[n] = gpio; 80 + 81 + n++; 82 + 83 + r = gpio_request(gpio, "mdio_mux_gpio"); 84 + if (r) 85 + goto err; 86 + 87 + r = gpio_direction_output(gpio, 0); 88 + if (r) 89 + goto err; 90 + } 91 + 92 + r = mdio_mux_init(&pdev->dev, 93 + mdio_mux_gpio_switch_fn, &s->mux_handle, s); 94 + 95 + if (r == 0) { 96 + pdev->dev.platform_data = s; 97 + return 0; 98 + } 99 + err: 100 + while (n) { 101 + n--; 102 + gpio_free(s->gpio[n]); 103 + } 104 + devm_kfree(&pdev->dev, s); 105 + return r; 106 + } 107 + 108 + static int __devexit mdio_mux_gpio_remove(struct platform_device *pdev) 109 + { 110 + struct mdio_mux_gpio_state *s = pdev->dev.platform_data; 111 + mdio_mux_uninit(s->mux_handle); 112 + return 0; 113 + } 114 + 115 + static struct of_device_id mdio_mux_gpio_match[] = { 116 + { 117 + .compatible = "mdio-mux-gpio", 118 + }, 119 + { 120 + /* Legacy compatible property. */ 121 + .compatible = "cavium,mdio-mux-sn74cbtlv3253", 122 + }, 123 + {}, 124 + }; 125 + MODULE_DEVICE_TABLE(of, mdio_mux_gpio_match); 126 + 127 + static struct platform_driver mdio_mux_gpio_driver = { 128 + .driver = { 129 + .name = "mdio-mux-gpio", 130 + .owner = THIS_MODULE, 131 + .of_match_table = mdio_mux_gpio_match, 132 + }, 133 + .probe = mdio_mux_gpio_probe, 134 + .remove = __devexit_p(mdio_mux_gpio_remove), 135 + }; 136 + 137 + module_platform_driver(mdio_mux_gpio_driver); 138 + 139 + MODULE_DESCRIPTION(DRV_DESCRIPTION); 140 + MODULE_VERSION(DRV_VERSION); 141 + MODULE_AUTHOR("David Daney"); 142 + MODULE_LICENSE("GPL");