1 // SPDX-License-Identifier: GPL-2.0
3 * Simple memory-mapped device MDIO MUX driver
5 * Author: Timur Tabi <timur@freescale.com>
7 * Copyright 2012 Freescale Semiconductor, Inc.
10 #include <linux/platform_device.h>
11 #include <linux/device.h>
12 #include <linux/of_address.h>
13 #include <linux/of_mdio.h>
14 #include <linux/module.h>
15 #include <linux/phy.h>
16 #include <linux/mdio-mux.h>
18 struct mdio_mux_mmioreg_state
{
26 * MDIO multiplexing switch function
28 * This function is called by the mdio-mux layer when it thinks the mdio bus
29 * multiplexer needs to switch.
31 * 'current_child' is the current value of the mux register (masked via
34 * 'desired_child' is the value of the 'reg' property of the target child MDIO
37 * The first time this function is called, current_child == -1.
39 * If current_child == desired_child, then the mux is already set to the
42 static int mdio_mux_mmioreg_switch_fn(int current_child
, int desired_child
,
45 struct mdio_mux_mmioreg_state
*s
= data
;
47 if (current_child
^ desired_child
) {
48 void __iomem
*p
= ioremap(s
->phys
, s
->iosize
);
53 case sizeof(uint8_t): {
57 y
= (x
& ~s
->mask
) | desired_child
;
59 iowrite8((x
& ~s
->mask
) | desired_child
, p
);
60 pr_debug("%s: %02x -> %02x\n", __func__
, x
, y
);
65 case sizeof(uint16_t): {
69 y
= (x
& ~s
->mask
) | desired_child
;
71 iowrite16((x
& ~s
->mask
) | desired_child
, p
);
72 pr_debug("%s: %04x -> %04x\n", __func__
, x
, y
);
77 case sizeof(uint32_t): {
81 y
= (x
& ~s
->mask
) | desired_child
;
83 iowrite32((x
& ~s
->mask
) | desired_child
, p
);
84 pr_debug("%s: %08x -> %08x\n", __func__
, x
, y
);
97 static int mdio_mux_mmioreg_probe(struct platform_device
*pdev
)
99 struct device_node
*np2
, *np
= pdev
->dev
.of_node
;
100 struct mdio_mux_mmioreg_state
*s
;
105 dev_dbg(&pdev
->dev
, "probing node %pOF\n", np
);
107 s
= devm_kzalloc(&pdev
->dev
, sizeof(*s
), GFP_KERNEL
);
111 ret
= of_address_to_resource(np
, 0, &res
);
113 dev_err(&pdev
->dev
, "could not obtain memory map for node %pOF\n",
119 s
->iosize
= resource_size(&res
);
120 if (s
->iosize
!= sizeof(uint8_t) &&
121 s
->iosize
!= sizeof(uint16_t) &&
122 s
->iosize
!= sizeof(uint32_t)) {
123 dev_err(&pdev
->dev
, "only 8/16/32-bit registers are supported\n");
127 iprop
= of_get_property(np
, "mux-mask", &len
);
128 if (!iprop
|| len
!= sizeof(uint32_t)) {
129 dev_err(&pdev
->dev
, "missing or invalid mux-mask property\n");
132 if (be32_to_cpup(iprop
) >= BIT(s
->iosize
* 8)) {
133 dev_err(&pdev
->dev
, "only 8/16/32-bit registers are supported\n");
136 s
->mask
= be32_to_cpup(iprop
);
139 * Verify that the 'reg' property of each child MDIO bus does not
140 * set any bits outside of the 'mask'.
142 for_each_available_child_of_node(np
, np2
) {
143 iprop
= of_get_property(np2
, "reg", &len
);
144 if (!iprop
|| len
!= sizeof(uint32_t)) {
145 dev_err(&pdev
->dev
, "mdio-mux child node %pOF is "
146 "missing a 'reg' property\n", np2
);
150 if (be32_to_cpup(iprop
) & ~s
->mask
) {
151 dev_err(&pdev
->dev
, "mdio-mux child node %pOF has "
152 "a 'reg' value with unmasked bits\n",
159 ret
= mdio_mux_init(&pdev
->dev
, pdev
->dev
.of_node
,
160 mdio_mux_mmioreg_switch_fn
,
161 &s
->mux_handle
, s
, NULL
);
163 if (ret
!= -EPROBE_DEFER
)
165 "failed to register mdio-mux bus %pOF\n", np
);
169 pdev
->dev
.platform_data
= s
;
174 static int mdio_mux_mmioreg_remove(struct platform_device
*pdev
)
176 struct mdio_mux_mmioreg_state
*s
= dev_get_platdata(&pdev
->dev
);
178 mdio_mux_uninit(s
->mux_handle
);
183 static const struct of_device_id mdio_mux_mmioreg_match
[] = {
185 .compatible
= "mdio-mux-mmioreg",
189 MODULE_DEVICE_TABLE(of
, mdio_mux_mmioreg_match
);
191 static struct platform_driver mdio_mux_mmioreg_driver
= {
193 .name
= "mdio-mux-mmioreg",
194 .of_match_table
= mdio_mux_mmioreg_match
,
196 .probe
= mdio_mux_mmioreg_probe
,
197 .remove
= mdio_mux_mmioreg_remove
,
200 module_platform_driver(mdio_mux_mmioreg_driver
);
202 MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
203 MODULE_DESCRIPTION("Memory-mapped device MDIO MUX driver");
204 MODULE_LICENSE("GPL v2");