aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTimur Tabi <timur@freescale.com>2012-08-24 05:10:53 -0400
committerDavid S. Miller <davem@davemloft.net>2012-08-30 12:54:48 -0400
commit6cc2ff82492d89f763e69ea6b8681926aceda610 (patch)
tree37cc43ef6eee4386bc68202f6681507e91bdd20a
parente92bdf4bf199f582dae8249a21e991db4bf66480 (diff)
netdev/phy: add MDIO bus multiplexer driven by a memory-mapped device
Add support for an MDIO bus multiplexer controlled by a simple memory-mapped device, like an FPGA. The device must be memory-mapped and contain only 8-bit registers (which keeps things simple). Tested on a Freescale P5020DS board which uses the "PIXIS" FPGA attached to the localbus. Signed-off-by: Timur Tabi <timur@freescale.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--Documentation/devicetree/bindings/net/mdio-mux-mmioreg.txt75
-rw-r--r--drivers/net/phy/Kconfig13
-rw-r--r--drivers/net/phy/Makefile1
-rw-r--r--drivers/net/phy/mdio-mux-mmioreg.c170
4 files changed, 259 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/net/mdio-mux-mmioreg.txt b/Documentation/devicetree/bindings/net/mdio-mux-mmioreg.txt
new file mode 100644
index 000000000000..8516929c7251
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/mdio-mux-mmioreg.txt
@@ -0,0 +1,75 @@
1Properties for an MDIO bus multiplexer controlled by a memory-mapped device
2
3This is a special case of a MDIO bus multiplexer. A memory-mapped device,
4like an FPGA, is used to control which child bus is connected. The mdio-mux
5node must be a child of the memory-mapped device. The driver currently only
6supports devices with eight-bit registers.
7
8Required properties in addition to the generic multiplexer properties:
9
10- compatible : string, must contain "mdio-mux-mmioreg"
11
12- reg : integer, contains the offset of the register that controls the bus
13 multiplexer. The size field in the 'reg' property is the size of
14 register, and must therefore be 1.
15
16- mux-mask : integer, contains an eight-bit mask that specifies which
17 bits in the register control the actual bus multiplexer. The
18 'reg' property of each child mdio-mux node must be constrained by
19 this mask.
20
21Example:
22
23The FPGA node defines a memory-mapped FPGA with a register space of 0x30 bytes.
24For the "EMI2" MDIO bus, register 9 (BRDCFG1) controls the mux on that bus.
25A bitmask of 0x6 means that bits 1 and 2 (bit 0 is lsb) are the bits on
26BRDCFG1 that control the actual mux.
27
28 /* The FPGA node */
29 fpga: board-control@3,0 {
30 #address-cells = <1>;
31 #size-cells = <1>;
32 compatible = "fsl,p5020ds-fpga", "fsl,fpga-ngpixis";
33 reg = <3 0 0x30>;
34 ranges = <0 3 0 0x30>;
35
36 mdio-mux-emi2 {
37 compatible = "mdio-mux-mmioreg", "mdio-mux";
38 mdio-parent-bus = <&xmdio0>;
39 #address-cells = <1>;
40 #size-cells = <0>;
41 reg = <9 1>; // BRDCFG1
42 mux-mask = <0x6>; // EMI2
43
44 emi2_slot1: mdio@0 { // Slot 1 XAUI (FM2)
45 reg = <0>;
46 #address-cells = <1>;
47 #size-cells = <0>;
48
49 phy_xgmii_slot1: ethernet-phy@0 {
50 compatible = "ethernet-phy-ieee802.3-c45";
51 reg = <4>;
52 };
53 };
54
55 emi2_slot2: mdio@2 { // Slot 2 XAUI (FM1)
56 reg = <2>;
57 #address-cells = <1>;
58 #size-cells = <0>;
59
60 phy_xgmii_slot2: ethernet-phy@4 {
61 compatible = "ethernet-phy-ieee802.3-c45";
62 reg = <0>;
63 };
64 };
65 };
66 };
67
68 /* The parent MDIO bus. */
69 xmdio0: mdio@f1000 {
70 #address-cells = <1>;
71 #size-cells = <0>;
72 compatible = "fsl,fman-xmdio";
73 reg = <0xf1000 0x1000>;
74 interrupts = <100 1 0 0>;
75 };
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 3090dc65a6f1..983bbf4d5ef6 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -159,6 +159,19 @@ config MDIO_BUS_MUX_GPIO
159 several child MDIO busses to a parent bus. Child bus 159 several child MDIO busses to a parent bus. Child bus
160 selection is under the control of GPIO lines. 160 selection is under the control of GPIO lines.
161 161
162config MDIO_BUS_MUX_MMIOREG
163 tristate "Support for MMIO device-controlled MDIO bus multiplexers"
164 depends on OF_MDIO
165 select MDIO_BUS_MUX
166 help
167 This module provides a driver for MDIO bus multiplexers that
168 are controlled via a simple memory-mapped device, like an FPGA.
169 The multiplexer connects one of several child MDIO busses to a
170 parent bus. Child bus selection is under the control of one of
171 the FPGA's registers.
172
173 Currently, only 8-bit registers are supported.
174
162endif # PHYLIB 175endif # PHYLIB
163 176
164config MICREL_KS8995MA 177config MICREL_KS8995MA
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 6d2dc6c94f2e..426674debae4 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -28,3 +28,4 @@ obj-$(CONFIG_MICREL_KS8995MA) += spi_ks8995.o
28obj-$(CONFIG_AMD_PHY) += amd.o 28obj-$(CONFIG_AMD_PHY) += amd.o
29obj-$(CONFIG_MDIO_BUS_MUX) += mdio-mux.o 29obj-$(CONFIG_MDIO_BUS_MUX) += mdio-mux.o
30obj-$(CONFIG_MDIO_BUS_MUX_GPIO) += mdio-mux-gpio.o 30obj-$(CONFIG_MDIO_BUS_MUX_GPIO) += mdio-mux-gpio.o
31obj-$(CONFIG_MDIO_BUS_MUX_MMIOREG) += mdio-mux-mmioreg.o
diff --git a/drivers/net/phy/mdio-mux-mmioreg.c b/drivers/net/phy/mdio-mux-mmioreg.c
new file mode 100644
index 000000000000..098239a98b19
--- /dev/null
+++ b/drivers/net/phy/mdio-mux-mmioreg.c
@@ -0,0 +1,170 @@
1/*
2 * Simple memory-mapped device MDIO MUX driver
3 *
4 * Author: Timur Tabi <timur@freescale.com>
5 *
6 * Copyright 2012 Freescale Semiconductor, Inc.
7 *
8 * This file is licensed under the terms of the GNU General Public License
9 * version 2. This program is licensed "as is" without any warranty of any
10 * kind, whether express or implied.
11 */
12
13#include <linux/platform_device.h>
14#include <linux/device.h>
15#include <linux/of_mdio.h>
16#include <linux/module.h>
17#include <linux/init.h>
18#include <linux/phy.h>
19#include <linux/mdio-mux.h>
20
21struct mdio_mux_mmioreg_state {
22 void *mux_handle;
23 phys_addr_t phys;
24 uint8_t mask;
25};
26
27/*
28 * MDIO multiplexing switch function
29 *
30 * This function is called by the mdio-mux layer when it thinks the mdio bus
31 * multiplexer needs to switch.
32 *
33 * 'current_child' is the current value of the mux register (masked via
34 * s->mask).
35 *
36 * 'desired_child' is the value of the 'reg' property of the target child MDIO
37 * node.
38 *
39 * The first time this function is called, current_child == -1.
40 *
41 * If current_child == desired_child, then the mux is already set to the
42 * correct bus.
43 */
44static int mdio_mux_mmioreg_switch_fn(int current_child, int desired_child,
45 void *data)
46{
47 struct mdio_mux_mmioreg_state *s = data;
48
49 if (current_child ^ desired_child) {
50 void *p = ioremap(s->phys, 1);
51 uint8_t x, y;
52
53 if (!p)
54 return -ENOMEM;
55
56 x = ioread8(p);
57 y = (x & ~s->mask) | desired_child;
58 if (x != y) {
59 iowrite8((x & ~s->mask) | desired_child, p);
60 pr_debug("%s: %02x -> %02x\n", __func__, x, y);
61 }
62
63 iounmap(p);
64 }
65
66 return 0;
67}
68
69static int __devinit mdio_mux_mmioreg_probe(struct platform_device *pdev)
70{
71 struct device_node *np2, *np = pdev->dev.of_node;
72 struct mdio_mux_mmioreg_state *s;
73 struct resource res;
74 const __be32 *iprop;
75 int len, ret;
76
77 dev_dbg(&pdev->dev, "probing node %s\n", np->full_name);
78
79 s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL);
80 if (!s)
81 return -ENOMEM;
82
83 ret = of_address_to_resource(np, 0, &res);
84 if (ret) {
85 dev_err(&pdev->dev, "could not obtain memory map for node %s\n",
86 np->full_name);
87 return ret;
88 }
89 s->phys = res.start;
90
91 if (resource_size(&res) != sizeof(uint8_t)) {
92 dev_err(&pdev->dev, "only 8-bit registers are supported\n");
93 return -EINVAL;
94 }
95
96 iprop = of_get_property(np, "mux-mask", &len);
97 if (!iprop || len != sizeof(uint32_t)) {
98 dev_err(&pdev->dev, "missing or invalid mux-mask property\n");
99 return -ENODEV;
100 }
101 if (be32_to_cpup(iprop) > 255) {
102 dev_err(&pdev->dev, "only 8-bit registers are supported\n");
103 return -EINVAL;
104 }
105 s->mask = be32_to_cpup(iprop);
106
107 /*
108 * Verify that the 'reg' property of each child MDIO bus does not
109 * set any bits outside of the 'mask'.
110 */
111 for_each_available_child_of_node(np, np2) {
112 iprop = of_get_property(np2, "reg", &len);
113 if (!iprop || len != sizeof(uint32_t)) {
114 dev_err(&pdev->dev, "mdio-mux child node %s is "
115 "missing a 'reg' property\n", np2->full_name);
116 return -ENODEV;
117 }
118 if (be32_to_cpup(iprop) & ~s->mask) {
119 dev_err(&pdev->dev, "mdio-mux child node %s has "
120 "a 'reg' value with unmasked bits\n",
121 np2->full_name);
122 return -ENODEV;
123 }
124 }
125
126 ret = mdio_mux_init(&pdev->dev, mdio_mux_mmioreg_switch_fn,
127 &s->mux_handle, s);
128 if (ret) {
129 dev_err(&pdev->dev, "failed to register mdio-mux bus %s\n",
130 np->full_name);
131 return ret;
132 }
133
134 pdev->dev.platform_data = s;
135
136 return 0;
137}
138
139static int __devexit mdio_mux_mmioreg_remove(struct platform_device *pdev)
140{
141 struct mdio_mux_mmioreg_state *s = dev_get_platdata(&pdev->dev);
142
143 mdio_mux_uninit(s->mux_handle);
144
145 return 0;
146}
147
148static struct of_device_id mdio_mux_mmioreg_match[] = {
149 {
150 .compatible = "mdio-mux-mmioreg",
151 },
152 {},
153};
154MODULE_DEVICE_TABLE(of, mdio_mux_mmioreg_match);
155
156static struct platform_driver mdio_mux_mmioreg_driver = {
157 .driver = {
158 .name = "mdio-mux-mmioreg",
159 .owner = THIS_MODULE,
160 .of_match_table = mdio_mux_mmioreg_match,
161 },
162 .probe = mdio_mux_mmioreg_probe,
163 .remove = __devexit_p(mdio_mux_mmioreg_remove),
164};
165
166module_platform_driver(mdio_mux_mmioreg_driver);
167
168MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
169MODULE_DESCRIPTION("Memory-mapped device MDIO MUX driver");
170MODULE_LICENSE("GPL v2");