aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/net/mdio-mux.txt136
-rw-r--r--drivers/net/phy/Kconfig9
-rw-r--r--drivers/net/phy/Makefile1
-rw-r--r--drivers/net/phy/mdio-mux.c192
-rw-r--r--include/linux/mdio-mux.h21
5 files changed, 359 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/net/mdio-mux.txt b/Documentation/devicetree/bindings/net/mdio-mux.txt
new file mode 100644
index 000000000000..f65606f8d632
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/mdio-mux.txt
@@ -0,0 +1,136 @@
1Common MDIO bus multiplexer/switch properties.
2
3An MDIO bus multiplexer/switch will have several child busses that are
4numbered uniquely in a device dependent manner. The nodes for an MDIO
5bus multiplexer/switch will have one child node for each child bus.
6
7Required properties:
8- mdio-parent-bus : phandle to the parent MDIO bus.
9- #address-cells = <1>;
10- #size-cells = <0>;
11
12Optional properties:
13- Other properties specific to the multiplexer/switch hardware.
14
15Required properties for child nodes:
16- #address-cells = <1>;
17- #size-cells = <0>;
18- reg : The sub-bus number.
19
20
21Example :
22
23 /* The parent MDIO bus. */
24 smi1: mdio@1180000001900 {
25 compatible = "cavium,octeon-3860-mdio";
26 #address-cells = <1>;
27 #size-cells = <0>;
28 reg = <0x11800 0x00001900 0x0 0x40>;
29 };
30
31 /*
32 An NXP sn74cbtlv3253 dual 1-of-4 switch controlled by a
33 pair of GPIO lines. Child busses 2 and 3 populated with 4
34 PHYs each.
35 */
36 mdio-mux {
37 compatible = "mdio-mux-gpio";
38 gpios = <&gpio1 3 0>, <&gpio1 4 0>;
39 mdio-parent-bus = <&smi1>;
40 #address-cells = <1>;
41 #size-cells = <0>;
42
43 mdio@2 {
44 reg = <2>;
45 #address-cells = <1>;
46 #size-cells = <0>;
47
48 phy11: ethernet-phy@1 {
49 reg = <1>;
50 compatible = "marvell,88e1149r";
51 marvell,reg-init = <3 0x10 0 0x5777>,
52 <3 0x11 0 0x00aa>,
53 <3 0x12 0 0x4105>,
54 <3 0x13 0 0x0a60>;
55 interrupt-parent = <&gpio>;
56 interrupts = <10 8>; /* Pin 10, active low */
57 };
58 phy12: ethernet-phy@2 {
59 reg = <2>;
60 compatible = "marvell,88e1149r";
61 marvell,reg-init = <3 0x10 0 0x5777>,
62 <3 0x11 0 0x00aa>,
63 <3 0x12 0 0x4105>,
64 <3 0x13 0 0x0a60>;
65 interrupt-parent = <&gpio>;
66 interrupts = <10 8>; /* Pin 10, active low */
67 };
68 phy13: ethernet-phy@3 {
69 reg = <3>;
70 compatible = "marvell,88e1149r";
71 marvell,reg-init = <3 0x10 0 0x5777>,
72 <3 0x11 0 0x00aa>,
73 <3 0x12 0 0x4105>,
74 <3 0x13 0 0x0a60>;
75 interrupt-parent = <&gpio>;
76 interrupts = <10 8>; /* Pin 10, active low */
77 };
78 phy14: ethernet-phy@4 {
79 reg = <4>;
80 compatible = "marvell,88e1149r";
81 marvell,reg-init = <3 0x10 0 0x5777>,
82 <3 0x11 0 0x00aa>,
83 <3 0x12 0 0x4105>,
84 <3 0x13 0 0x0a60>;
85 interrupt-parent = <&gpio>;
86 interrupts = <10 8>; /* Pin 10, active low */
87 };
88 };
89
90 mdio@3 {
91 reg = <3>;
92 #address-cells = <1>;
93 #size-cells = <0>;
94
95 phy21: ethernet-phy@1 {
96 reg = <1>;
97 compatible = "marvell,88e1149r";
98 marvell,reg-init = <3 0x10 0 0x5777>,
99 <3 0x11 0 0x00aa>,
100 <3 0x12 0 0x4105>,
101 <3 0x13 0 0x0a60>;
102 interrupt-parent = <&gpio>;
103 interrupts = <12 8>; /* Pin 12, active low */
104 };
105 phy22: ethernet-phy@2 {
106 reg = <2>;
107 compatible = "marvell,88e1149r";
108 marvell,reg-init = <3 0x10 0 0x5777>,
109 <3 0x11 0 0x00aa>,
110 <3 0x12 0 0x4105>,
111 <3 0x13 0 0x0a60>;
112 interrupt-parent = <&gpio>;
113 interrupts = <12 8>; /* Pin 12, active low */
114 };
115 phy23: ethernet-phy@3 {
116 reg = <3>;
117 compatible = "marvell,88e1149r";
118 marvell,reg-init = <3 0x10 0 0x5777>,
119 <3 0x11 0 0x00aa>,
120 <3 0x12 0 0x4105>,
121 <3 0x13 0 0x0a60>;
122 interrupt-parent = <&gpio>;
123 interrupts = <12 8>; /* Pin 12, active low */
124 };
125 phy24: ethernet-phy@4 {
126 reg = <4>;
127 compatible = "marvell,88e1149r";
128 marvell,reg-init = <3 0x10 0 0x5777>,
129 <3 0x11 0 0x00aa>,
130 <3 0x12 0 0x4105>,
131 <3 0x13 0 0x0a60>;
132 interrupt-parent = <&gpio>;
133 interrupts = <12 8>; /* Pin 12, active low */
134 };
135 };
136 };
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 0e01f4e5cd64..99c0674a5935 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -135,6 +135,15 @@ config MDIO_OCTEON
135 135
136 If in doubt, say Y. 136 If in doubt, say Y.
137 137
138config MDIO_BUS_MUX
139 tristate
140 depends on OF_MDIO
141 help
142 This module provides a driver framework for MDIO bus
143 multiplexers which connect one of several child MDIO busses
144 to a parent bus. Switching between child busses is done by
145 device specific drivers.
146
138endif # PHYLIB 147endif # PHYLIB
139 148
140config MICREL_KS8995MA 149config MICREL_KS8995MA
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index b7438b1b94b9..a6b50e7715d0 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -25,3 +25,4 @@ obj-$(CONFIG_MICREL_PHY) += micrel.o
25obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o 25obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o
26obj-$(CONFIG_MICREL_KS8995MA) += spi_ks8995.o 26obj-$(CONFIG_MICREL_KS8995MA) += spi_ks8995.o
27obj-$(CONFIG_AMD_PHY) += amd.o 27obj-$(CONFIG_AMD_PHY) += amd.o
28obj-$(CONFIG_MDIO_BUS_MUX) += mdio-mux.o
diff --git a/drivers/net/phy/mdio-mux.c b/drivers/net/phy/mdio-mux.c
new file mode 100644
index 000000000000..39ea0674dcde
--- /dev/null
+++ b/drivers/net/phy/mdio-mux.c
@@ -0,0 +1,192 @@
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/mdio-mux.h>
11#include <linux/of_mdio.h>
12#include <linux/device.h>
13#include <linux/module.h>
14#include <linux/phy.h>
15
16#define DRV_VERSION "1.0"
17#define DRV_DESCRIPTION "MDIO bus multiplexer driver"
18
19struct mdio_mux_child_bus;
20
21struct mdio_mux_parent_bus {
22 struct mii_bus *mii_bus;
23 int current_child;
24 int parent_id;
25 void *switch_data;
26 int (*switch_fn)(int current_child, int desired_child, void *data);
27
28 /* List of our children linked through their next fields. */
29 struct mdio_mux_child_bus *children;
30};
31
32struct mdio_mux_child_bus {
33 struct mii_bus *mii_bus;
34 struct mdio_mux_parent_bus *parent;
35 struct mdio_mux_child_bus *next;
36 int bus_number;
37 int phy_irq[PHY_MAX_ADDR];
38};
39
40/*
41 * The parent bus' lock is used to order access to the switch_fn.
42 */
43static int mdio_mux_read(struct mii_bus *bus, int phy_id, int regnum)
44{
45 struct mdio_mux_child_bus *cb = bus->priv;
46 struct mdio_mux_parent_bus *pb = cb->parent;
47 int r;
48
49 mutex_lock(&pb->mii_bus->mdio_lock);
50 r = pb->switch_fn(pb->current_child, cb->bus_number, pb->switch_data);
51 if (r)
52 goto out;
53
54 pb->current_child = cb->bus_number;
55
56 r = pb->mii_bus->read(pb->mii_bus, phy_id, regnum);
57out:
58 mutex_unlock(&pb->mii_bus->mdio_lock);
59
60 return r;
61}
62
63/*
64 * The parent bus' lock is used to order access to the switch_fn.
65 */
66static int mdio_mux_write(struct mii_bus *bus, int phy_id,
67 int regnum, u16 val)
68{
69 struct mdio_mux_child_bus *cb = bus->priv;
70 struct mdio_mux_parent_bus *pb = cb->parent;
71
72 int r;
73
74 mutex_lock(&pb->mii_bus->mdio_lock);
75 r = pb->switch_fn(pb->current_child, cb->bus_number, pb->switch_data);
76 if (r)
77 goto out;
78
79 pb->current_child = cb->bus_number;
80
81 r = pb->mii_bus->write(pb->mii_bus, phy_id, regnum, val);
82out:
83 mutex_unlock(&pb->mii_bus->mdio_lock);
84
85 return r;
86}
87
88static int parent_count;
89
90int mdio_mux_init(struct device *dev,
91 int (*switch_fn)(int cur, int desired, void *data),
92 void **mux_handle,
93 void *data)
94{
95 struct device_node *parent_bus_node;
96 struct device_node *child_bus_node;
97 int r, ret_val;
98 struct mii_bus *parent_bus;
99 struct mdio_mux_parent_bus *pb;
100 struct mdio_mux_child_bus *cb;
101
102 if (!dev->of_node)
103 return -ENODEV;
104
105 parent_bus_node = of_parse_phandle(dev->of_node, "mdio-parent-bus", 0);
106
107 if (!parent_bus_node)
108 return -ENODEV;
109
110 parent_bus = of_mdio_find_bus(parent_bus_node);
111 if (parent_bus == NULL) {
112 ret_val = -EPROBE_DEFER;
113 goto err_parent_bus;
114 }
115
116 pb = devm_kzalloc(dev, sizeof(*pb), GFP_KERNEL);
117 if (pb == NULL) {
118 ret_val = -ENOMEM;
119 goto err_parent_bus;
120 }
121
122 pb->switch_data = data;
123 pb->switch_fn = switch_fn;
124 pb->current_child = -1;
125 pb->parent_id = parent_count++;
126 pb->mii_bus = parent_bus;
127
128 ret_val = -ENODEV;
129 for_each_child_of_node(dev->of_node, child_bus_node) {
130 u32 v;
131
132 r = of_property_read_u32(child_bus_node, "reg", &v);
133 if (r)
134 continue;
135
136 cb = devm_kzalloc(dev, sizeof(*cb), GFP_KERNEL);
137 if (cb == NULL) {
138 dev_err(dev,
139 "Error: Failed to allocate memory for child\n");
140 ret_val = -ENOMEM;
141 break;
142 }
143 cb->bus_number = v;
144 cb->parent = pb;
145 cb->mii_bus = mdiobus_alloc();
146 cb->mii_bus->priv = cb;
147
148 cb->mii_bus->irq = cb->phy_irq;
149 cb->mii_bus->name = "mdio_mux";
150 snprintf(cb->mii_bus->id, MII_BUS_ID_SIZE, "%x.%x",
151 pb->parent_id, v);
152 cb->mii_bus->parent = dev;
153 cb->mii_bus->read = mdio_mux_read;
154 cb->mii_bus->write = mdio_mux_write;
155 r = of_mdiobus_register(cb->mii_bus, child_bus_node);
156 if (r) {
157 mdiobus_free(cb->mii_bus);
158 devm_kfree(dev, cb);
159 } else {
160 of_node_get(child_bus_node);
161 cb->next = pb->children;
162 pb->children = cb;
163 }
164 }
165 if (pb->children) {
166 *mux_handle = pb;
167 dev_info(dev, "Version " DRV_VERSION "\n");
168 return 0;
169 }
170err_parent_bus:
171 of_node_put(parent_bus_node);
172 return ret_val;
173}
174EXPORT_SYMBOL_GPL(mdio_mux_init);
175
176void mdio_mux_uninit(void *mux_handle)
177{
178 struct mdio_mux_parent_bus *pb = mux_handle;
179 struct mdio_mux_child_bus *cb = pb->children;
180
181 while (cb) {
182 mdiobus_unregister(cb->mii_bus);
183 mdiobus_free(cb->mii_bus);
184 cb = cb->next;
185 }
186}
187EXPORT_SYMBOL_GPL(mdio_mux_uninit);
188
189MODULE_DESCRIPTION(DRV_DESCRIPTION);
190MODULE_VERSION(DRV_VERSION);
191MODULE_AUTHOR("David Daney");
192MODULE_LICENSE("GPL");
diff --git a/include/linux/mdio-mux.h b/include/linux/mdio-mux.h
new file mode 100644
index 000000000000..a243dbba8659
--- /dev/null
+++ b/include/linux/mdio-mux.h
@@ -0,0 +1,21 @@
1/*
2 * MDIO bus multiplexer framwork.
3 *
4 * This file is subject to the terms and conditions of the GNU General Public
5 * License. See the file "COPYING" in the main directory of this archive
6 * for more details.
7 *
8 * Copyright (C) 2011, 2012 Cavium, Inc.
9 */
10#ifndef __LINUX_MDIO_MUX_H
11#define __LINUX_MDIO_MUX_H
12#include <linux/device.h>
13
14int mdio_mux_init(struct device *dev,
15 int (*switch_fn) (int cur, int desired, void *data),
16 void **mux_handle,
17 void *data);
18
19void mdio_mux_uninit(void *mux_handle);
20
21#endif /* __LINUX_MDIO_MUX_H */