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/device.h>
11 #include <linux/mdio-mux.h>
12 #include <linux/module.h>
13 #include <linux/of_address.h>
14 #include <linux/of_mdio.h>
15 #include <linux/phy.h>
16 #include <linux/platform_device.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
*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 return dev_err_probe(&pdev
->dev
, ret
,
114 "could not obtain memory map for node %pOF\n", np
);
117 s
->iosize
= resource_size(&res
);
118 if (s
->iosize
!= sizeof(uint8_t) &&
119 s
->iosize
!= sizeof(uint16_t) &&
120 s
->iosize
!= sizeof(uint32_t))
121 return dev_err_probe(&pdev
->dev
, -EINVAL
,
122 "only 8/16/32-bit registers are supported\n");
124 iprop
= of_get_property(np
, "mux-mask", &len
);
125 if (!iprop
|| len
!= sizeof(uint32_t))
126 return dev_err_probe(&pdev
->dev
, -ENODEV
,
127 "missing or invalid mux-mask property\n");
128 if (be32_to_cpup(iprop
) >= BIT(s
->iosize
* 8))
129 return dev_err_probe(&pdev
->dev
, -EINVAL
,
130 "only 8/16/32-bit registers are supported\n");
131 s
->mask
= be32_to_cpup(iprop
);
134 * Verify that the 'reg' property of each child MDIO bus does not
135 * set any bits outside of the 'mask'.
137 for_each_available_child_of_node_scoped(np
, np2
) {
140 if (of_property_read_reg(np2
, 0, ®
, NULL
))
141 return dev_err_probe(&pdev
->dev
, -ENODEV
,
142 "mdio-mux child node %pOF is missing a 'reg' property\n",
144 if ((u32
)reg
& ~s
->mask
)
145 return dev_err_probe(&pdev
->dev
, -ENODEV
,
146 "mdio-mux child node %pOF has a 'reg' value with unmasked bits\n",
150 ret
= mdio_mux_init(&pdev
->dev
, pdev
->dev
.of_node
,
151 mdio_mux_mmioreg_switch_fn
,
152 &s
->mux_handle
, s
, NULL
);
154 return dev_err_probe(&pdev
->dev
, ret
,
155 "failed to register mdio-mux bus %pOF\n", np
);
157 pdev
->dev
.platform_data
= s
;
162 static void mdio_mux_mmioreg_remove(struct platform_device
*pdev
)
164 struct mdio_mux_mmioreg_state
*s
= dev_get_platdata(&pdev
->dev
);
166 mdio_mux_uninit(s
->mux_handle
);
169 static const struct of_device_id mdio_mux_mmioreg_match
[] = {
171 .compatible
= "mdio-mux-mmioreg",
175 MODULE_DEVICE_TABLE(of
, mdio_mux_mmioreg_match
);
177 static struct platform_driver mdio_mux_mmioreg_driver
= {
179 .name
= "mdio-mux-mmioreg",
180 .of_match_table
= mdio_mux_mmioreg_match
,
182 .probe
= mdio_mux_mmioreg_probe
,
183 .remove
= mdio_mux_mmioreg_remove
,
186 module_platform_driver(mdio_mux_mmioreg_driver
);
188 MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
189 MODULE_DESCRIPTION("Memory-mapped device MDIO MUX driver");
190 MODULE_LICENSE("GPL v2");