2 * Simple memory-mapped device MDIO MUX driver
4 * Author: Timur Tabi <timur@freescale.com>
6 * Copyright 2012 Freescale Semiconductor, Inc.
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.
13 #include <linux/platform_device.h>
14 #include <linux/device.h>
15 #include <linux/of_address.h>
16 #include <linux/of_mdio.h>
17 #include <linux/module.h>
18 #include <linux/phy.h>
19 #include <linux/mdio-mux.h>
21 struct mdio_mux_mmioreg_state
{
29 * MDIO multiplexing switch function
31 * This function is called by the mdio-mux layer when it thinks the mdio bus
32 * multiplexer needs to switch.
34 * 'current_child' is the current value of the mux register (masked via
37 * 'desired_child' is the value of the 'reg' property of the target child MDIO
40 * The first time this function is called, current_child == -1.
42 * If current_child == desired_child, then the mux is already set to the
45 static int mdio_mux_mmioreg_switch_fn(int current_child
, int desired_child
,
48 struct mdio_mux_mmioreg_state
*s
= data
;
50 if (current_child
^ desired_child
) {
51 void __iomem
*p
= ioremap(s
->phys
, s
->iosize
);
56 case sizeof(uint8_t): {
60 y
= (x
& ~s
->mask
) | desired_child
;
62 iowrite8((x
& ~s
->mask
) | desired_child
, p
);
63 pr_debug("%s: %02x -> %02x\n", __func__
, x
, y
);
68 case sizeof(uint16_t): {
72 y
= (x
& ~s
->mask
) | desired_child
;
74 iowrite16((x
& ~s
->mask
) | desired_child
, p
);
75 pr_debug("%s: %04x -> %04x\n", __func__
, x
, y
);
80 case sizeof(uint32_t): {
84 y
= (x
& ~s
->mask
) | desired_child
;
86 iowrite32((x
& ~s
->mask
) | desired_child
, p
);
87 pr_debug("%s: %08x -> %08x\n", __func__
, x
, y
);
100 static int mdio_mux_mmioreg_probe(struct platform_device
*pdev
)
102 struct device_node
*np2
, *np
= pdev
->dev
.of_node
;
103 struct mdio_mux_mmioreg_state
*s
;
108 dev_dbg(&pdev
->dev
, "probing node %s\n", np
->full_name
);
110 s
= devm_kzalloc(&pdev
->dev
, sizeof(*s
), GFP_KERNEL
);
114 ret
= of_address_to_resource(np
, 0, &res
);
116 dev_err(&pdev
->dev
, "could not obtain memory map for node %s\n",
122 s
->iosize
= resource_size(&res
);
123 if (s
->iosize
!= sizeof(uint8_t) &&
124 s
->iosize
!= sizeof(uint16_t) &&
125 s
->iosize
!= sizeof(uint32_t)) {
126 dev_err(&pdev
->dev
, "only 8/16/32-bit registers are supported\n");
130 iprop
= of_get_property(np
, "mux-mask", &len
);
131 if (!iprop
|| len
!= sizeof(uint32_t)) {
132 dev_err(&pdev
->dev
, "missing or invalid mux-mask property\n");
135 if (be32_to_cpup(iprop
) >= BIT(s
->iosize
* 8)) {
136 dev_err(&pdev
->dev
, "only 8/16/32-bit registers are supported\n");
139 s
->mask
= be32_to_cpup(iprop
);
142 * Verify that the 'reg' property of each child MDIO bus does not
143 * set any bits outside of the 'mask'.
145 for_each_available_child_of_node(np
, np2
) {
146 iprop
= of_get_property(np2
, "reg", &len
);
147 if (!iprop
|| len
!= sizeof(uint32_t)) {
148 dev_err(&pdev
->dev
, "mdio-mux child node %s is "
149 "missing a 'reg' property\n", np2
->full_name
);
153 if (be32_to_cpup(iprop
) & ~s
->mask
) {
154 dev_err(&pdev
->dev
, "mdio-mux child node %s has "
155 "a 'reg' value with unmasked bits\n",
162 ret
= mdio_mux_init(&pdev
->dev
, mdio_mux_mmioreg_switch_fn
,
163 &s
->mux_handle
, s
, NULL
);
165 dev_err(&pdev
->dev
, "failed to register mdio-mux bus %s\n",
170 pdev
->dev
.platform_data
= s
;
175 static int mdio_mux_mmioreg_remove(struct platform_device
*pdev
)
177 struct mdio_mux_mmioreg_state
*s
= dev_get_platdata(&pdev
->dev
);
179 mdio_mux_uninit(s
->mux_handle
);
184 static const struct of_device_id mdio_mux_mmioreg_match
[] = {
186 .compatible
= "mdio-mux-mmioreg",
190 MODULE_DEVICE_TABLE(of
, mdio_mux_mmioreg_match
);
192 static struct platform_driver mdio_mux_mmioreg_driver
= {
194 .name
= "mdio-mux-mmioreg",
195 .of_match_table
= mdio_mux_mmioreg_match
,
197 .probe
= mdio_mux_mmioreg_probe
,
198 .remove
= mdio_mux_mmioreg_remove
,
201 module_platform_driver(mdio_mux_mmioreg_driver
);
203 MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
204 MODULE_DESCRIPTION("Memory-mapped device MDIO MUX driver");
205 MODULE_LICENSE("GPL v2");