xref: /openbmc/linux/drivers/net/mdio/mdio-mux.c (revision 1bf34366)
1a9770eacSAndrew Lunn // SPDX-License-Identifier: GPL-2.0
2a9770eacSAndrew Lunn /*
3a9770eacSAndrew Lunn  * Copyright (C) 2011, 2012 Cavium, Inc.
4a9770eacSAndrew Lunn  */
5a9770eacSAndrew Lunn 
6a9770eacSAndrew Lunn #include <linux/device.h>
7*1bf34366SCalvin Johnson #include <linux/mdio-mux.h>
8a9770eacSAndrew Lunn #include <linux/module.h>
9*1bf34366SCalvin Johnson #include <linux/of_mdio.h>
10a9770eacSAndrew Lunn #include <linux/phy.h>
11*1bf34366SCalvin Johnson #include <linux/platform_device.h>
12a9770eacSAndrew Lunn 
13a9770eacSAndrew Lunn #define DRV_DESCRIPTION "MDIO bus multiplexer driver"
14a9770eacSAndrew Lunn 
15a9770eacSAndrew Lunn struct mdio_mux_child_bus;
16a9770eacSAndrew Lunn 
17a9770eacSAndrew Lunn struct mdio_mux_parent_bus {
18a9770eacSAndrew Lunn 	struct mii_bus *mii_bus;
19a9770eacSAndrew Lunn 	int current_child;
20a9770eacSAndrew Lunn 	int parent_id;
21a9770eacSAndrew Lunn 	void *switch_data;
22a9770eacSAndrew Lunn 	int (*switch_fn)(int current_child, int desired_child, void *data);
23a9770eacSAndrew Lunn 
24a9770eacSAndrew Lunn 	/* List of our children linked through their next fields. */
25a9770eacSAndrew Lunn 	struct mdio_mux_child_bus *children;
26a9770eacSAndrew Lunn };
27a9770eacSAndrew Lunn 
28a9770eacSAndrew Lunn struct mdio_mux_child_bus {
29a9770eacSAndrew Lunn 	struct mii_bus *mii_bus;
30a9770eacSAndrew Lunn 	struct mdio_mux_parent_bus *parent;
31a9770eacSAndrew Lunn 	struct mdio_mux_child_bus *next;
32a9770eacSAndrew Lunn 	int bus_number;
33a9770eacSAndrew Lunn };
34a9770eacSAndrew Lunn 
35a9770eacSAndrew Lunn /*
36a9770eacSAndrew Lunn  * The parent bus' lock is used to order access to the switch_fn.
37a9770eacSAndrew Lunn  */
38a9770eacSAndrew Lunn static int mdio_mux_read(struct mii_bus *bus, int phy_id, int regnum)
39a9770eacSAndrew Lunn {
40a9770eacSAndrew Lunn 	struct mdio_mux_child_bus *cb = bus->priv;
41a9770eacSAndrew Lunn 	struct mdio_mux_parent_bus *pb = cb->parent;
42a9770eacSAndrew Lunn 	int r;
43a9770eacSAndrew Lunn 
44a9770eacSAndrew Lunn 	mutex_lock_nested(&pb->mii_bus->mdio_lock, MDIO_MUTEX_MUX);
45a9770eacSAndrew Lunn 	r = pb->switch_fn(pb->current_child, cb->bus_number, pb->switch_data);
46a9770eacSAndrew Lunn 	if (r)
47a9770eacSAndrew Lunn 		goto out;
48a9770eacSAndrew Lunn 
49a9770eacSAndrew Lunn 	pb->current_child = cb->bus_number;
50a9770eacSAndrew Lunn 
51a9770eacSAndrew Lunn 	r = pb->mii_bus->read(pb->mii_bus, phy_id, regnum);
52a9770eacSAndrew Lunn out:
53a9770eacSAndrew Lunn 	mutex_unlock(&pb->mii_bus->mdio_lock);
54a9770eacSAndrew Lunn 
55a9770eacSAndrew Lunn 	return r;
56a9770eacSAndrew Lunn }
57a9770eacSAndrew Lunn 
58a9770eacSAndrew Lunn /*
59a9770eacSAndrew Lunn  * The parent bus' lock is used to order access to the switch_fn.
60a9770eacSAndrew Lunn  */
61a9770eacSAndrew Lunn static int mdio_mux_write(struct mii_bus *bus, int phy_id,
62a9770eacSAndrew Lunn 			  int regnum, u16 val)
63a9770eacSAndrew Lunn {
64a9770eacSAndrew Lunn 	struct mdio_mux_child_bus *cb = bus->priv;
65a9770eacSAndrew Lunn 	struct mdio_mux_parent_bus *pb = cb->parent;
66a9770eacSAndrew Lunn 
67a9770eacSAndrew Lunn 	int r;
68a9770eacSAndrew Lunn 
69a9770eacSAndrew Lunn 	mutex_lock_nested(&pb->mii_bus->mdio_lock, MDIO_MUTEX_MUX);
70a9770eacSAndrew Lunn 	r = pb->switch_fn(pb->current_child, cb->bus_number, pb->switch_data);
71a9770eacSAndrew Lunn 	if (r)
72a9770eacSAndrew Lunn 		goto out;
73a9770eacSAndrew Lunn 
74a9770eacSAndrew Lunn 	pb->current_child = cb->bus_number;
75a9770eacSAndrew Lunn 
76a9770eacSAndrew Lunn 	r = pb->mii_bus->write(pb->mii_bus, phy_id, regnum, val);
77a9770eacSAndrew Lunn out:
78a9770eacSAndrew Lunn 	mutex_unlock(&pb->mii_bus->mdio_lock);
79a9770eacSAndrew Lunn 
80a9770eacSAndrew Lunn 	return r;
81a9770eacSAndrew Lunn }
82a9770eacSAndrew Lunn 
83a9770eacSAndrew Lunn static int parent_count;
84a9770eacSAndrew Lunn 
85a9770eacSAndrew Lunn int mdio_mux_init(struct device *dev,
86a9770eacSAndrew Lunn 		  struct device_node *mux_node,
87a9770eacSAndrew Lunn 		  int (*switch_fn)(int cur, int desired, void *data),
88a9770eacSAndrew Lunn 		  void **mux_handle,
89a9770eacSAndrew Lunn 		  void *data,
90a9770eacSAndrew Lunn 		  struct mii_bus *mux_bus)
91a9770eacSAndrew Lunn {
92a9770eacSAndrew Lunn 	struct device_node *parent_bus_node;
93a9770eacSAndrew Lunn 	struct device_node *child_bus_node;
94a9770eacSAndrew Lunn 	int r, ret_val;
95a9770eacSAndrew Lunn 	struct mii_bus *parent_bus;
96a9770eacSAndrew Lunn 	struct mdio_mux_parent_bus *pb;
97a9770eacSAndrew Lunn 	struct mdio_mux_child_bus *cb;
98a9770eacSAndrew Lunn 
99a9770eacSAndrew Lunn 	if (!mux_node)
100a9770eacSAndrew Lunn 		return -ENODEV;
101a9770eacSAndrew Lunn 
102a9770eacSAndrew Lunn 	if (!mux_bus) {
103a9770eacSAndrew Lunn 		parent_bus_node = of_parse_phandle(mux_node,
104a9770eacSAndrew Lunn 						   "mdio-parent-bus", 0);
105a9770eacSAndrew Lunn 
106a9770eacSAndrew Lunn 		if (!parent_bus_node)
107a9770eacSAndrew Lunn 			return -ENODEV;
108a9770eacSAndrew Lunn 
109a9770eacSAndrew Lunn 		parent_bus = of_mdio_find_bus(parent_bus_node);
110a9770eacSAndrew Lunn 		if (!parent_bus) {
111a9770eacSAndrew Lunn 			ret_val = -EPROBE_DEFER;
112a9770eacSAndrew Lunn 			goto err_parent_bus;
113a9770eacSAndrew Lunn 		}
114a9770eacSAndrew Lunn 	} else {
115a9770eacSAndrew Lunn 		parent_bus_node = NULL;
116a9770eacSAndrew Lunn 		parent_bus = mux_bus;
117a9770eacSAndrew Lunn 		get_device(&parent_bus->dev);
118a9770eacSAndrew Lunn 	}
119a9770eacSAndrew Lunn 
120a9770eacSAndrew Lunn 	pb = devm_kzalloc(dev, sizeof(*pb), GFP_KERNEL);
121a9770eacSAndrew Lunn 	if (!pb) {
122a9770eacSAndrew Lunn 		ret_val = -ENOMEM;
123a9770eacSAndrew Lunn 		goto err_pb_kz;
124a9770eacSAndrew Lunn 	}
125a9770eacSAndrew Lunn 
126a9770eacSAndrew Lunn 	pb->switch_data = data;
127a9770eacSAndrew Lunn 	pb->switch_fn = switch_fn;
128a9770eacSAndrew Lunn 	pb->current_child = -1;
129a9770eacSAndrew Lunn 	pb->parent_id = parent_count++;
130a9770eacSAndrew Lunn 	pb->mii_bus = parent_bus;
131a9770eacSAndrew Lunn 
132a9770eacSAndrew Lunn 	ret_val = -ENODEV;
133a9770eacSAndrew Lunn 	for_each_available_child_of_node(mux_node, child_bus_node) {
134a9770eacSAndrew Lunn 		int v;
135a9770eacSAndrew Lunn 
136a9770eacSAndrew Lunn 		r = of_property_read_u32(child_bus_node, "reg", &v);
137a9770eacSAndrew Lunn 		if (r) {
138a9770eacSAndrew Lunn 			dev_err(dev,
139a9770eacSAndrew Lunn 				"Error: Failed to find reg for child %pOF\n",
140a9770eacSAndrew Lunn 				child_bus_node);
141a9770eacSAndrew Lunn 			continue;
142a9770eacSAndrew Lunn 		}
143a9770eacSAndrew Lunn 
144a9770eacSAndrew Lunn 		cb = devm_kzalloc(dev, sizeof(*cb), GFP_KERNEL);
145a9770eacSAndrew Lunn 		if (!cb) {
146a9770eacSAndrew Lunn 			ret_val = -ENOMEM;
147a9770eacSAndrew Lunn 			continue;
148a9770eacSAndrew Lunn 		}
149a9770eacSAndrew Lunn 		cb->bus_number = v;
150a9770eacSAndrew Lunn 		cb->parent = pb;
151a9770eacSAndrew Lunn 
152a9770eacSAndrew Lunn 		cb->mii_bus = mdiobus_alloc();
153a9770eacSAndrew Lunn 		if (!cb->mii_bus) {
154a9770eacSAndrew Lunn 			ret_val = -ENOMEM;
155a9770eacSAndrew Lunn 			devm_kfree(dev, cb);
156a9770eacSAndrew Lunn 			continue;
157a9770eacSAndrew Lunn 		}
158a9770eacSAndrew Lunn 		cb->mii_bus->priv = cb;
159a9770eacSAndrew Lunn 
160a9770eacSAndrew Lunn 		cb->mii_bus->name = "mdio_mux";
161a9770eacSAndrew Lunn 		snprintf(cb->mii_bus->id, MII_BUS_ID_SIZE, "%x.%x",
162a9770eacSAndrew Lunn 			 pb->parent_id, v);
163a9770eacSAndrew Lunn 		cb->mii_bus->parent = dev;
164a9770eacSAndrew Lunn 		cb->mii_bus->read = mdio_mux_read;
165a9770eacSAndrew Lunn 		cb->mii_bus->write = mdio_mux_write;
166a9770eacSAndrew Lunn 		r = of_mdiobus_register(cb->mii_bus, child_bus_node);
167a9770eacSAndrew Lunn 		if (r) {
168a9770eacSAndrew Lunn 			dev_err(dev,
169a9770eacSAndrew Lunn 				"Error: Failed to register MDIO bus for child %pOF\n",
170a9770eacSAndrew Lunn 				child_bus_node);
171a9770eacSAndrew Lunn 			mdiobus_free(cb->mii_bus);
172a9770eacSAndrew Lunn 			devm_kfree(dev, cb);
173a9770eacSAndrew Lunn 		} else {
174a9770eacSAndrew Lunn 			cb->next = pb->children;
175a9770eacSAndrew Lunn 			pb->children = cb;
176a9770eacSAndrew Lunn 		}
177a9770eacSAndrew Lunn 	}
178a9770eacSAndrew Lunn 	if (pb->children) {
179a9770eacSAndrew Lunn 		*mux_handle = pb;
180a9770eacSAndrew Lunn 		return 0;
181a9770eacSAndrew Lunn 	}
182a9770eacSAndrew Lunn 
183a9770eacSAndrew Lunn 	dev_err(dev, "Error: No acceptable child buses found\n");
184a9770eacSAndrew Lunn 	devm_kfree(dev, pb);
185a9770eacSAndrew Lunn err_pb_kz:
186a9770eacSAndrew Lunn 	put_device(&parent_bus->dev);
187a9770eacSAndrew Lunn err_parent_bus:
188a9770eacSAndrew Lunn 	of_node_put(parent_bus_node);
189a9770eacSAndrew Lunn 	return ret_val;
190a9770eacSAndrew Lunn }
191a9770eacSAndrew Lunn EXPORT_SYMBOL_GPL(mdio_mux_init);
192a9770eacSAndrew Lunn 
193a9770eacSAndrew Lunn void mdio_mux_uninit(void *mux_handle)
194a9770eacSAndrew Lunn {
195a9770eacSAndrew Lunn 	struct mdio_mux_parent_bus *pb = mux_handle;
196a9770eacSAndrew Lunn 	struct mdio_mux_child_bus *cb = pb->children;
197a9770eacSAndrew Lunn 
198a9770eacSAndrew Lunn 	while (cb) {
199a9770eacSAndrew Lunn 		mdiobus_unregister(cb->mii_bus);
200a9770eacSAndrew Lunn 		mdiobus_free(cb->mii_bus);
201a9770eacSAndrew Lunn 		cb = cb->next;
202a9770eacSAndrew Lunn 	}
203a9770eacSAndrew Lunn 
204a9770eacSAndrew Lunn 	put_device(&pb->mii_bus->dev);
205a9770eacSAndrew Lunn }
206a9770eacSAndrew Lunn EXPORT_SYMBOL_GPL(mdio_mux_uninit);
207a9770eacSAndrew Lunn 
208a9770eacSAndrew Lunn MODULE_DESCRIPTION(DRV_DESCRIPTION);
209a9770eacSAndrew Lunn MODULE_AUTHOR("David Daney");
210a9770eacSAndrew Lunn MODULE_LICENSE("GPL v2");
211