xref: /openbmc/linux/drivers/net/phy/mdio_bus.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1a2443fd1SAndrew Lunn // SPDX-License-Identifier: GPL-2.0+
202d320c3SSergei Shtylyov /* MDIO Bus interface
300db8189SAndy Fleming  *
400db8189SAndy Fleming  * Author: Andy Fleming
500db8189SAndy Fleming  *
600db8189SAndy Fleming  * Copyright (c) 2004 Freescale Semiconductor, Inc.
700db8189SAndy Fleming  */
88d242488SJoe Perches 
98d242488SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
108d242488SJoe Perches 
1100db8189SAndy Fleming #include <linux/delay.h>
123d1e4db2SAnton Vorontsov #include <linux/device.h>
1354e80dedSBartosz Golaszewski #include <linux/errno.h>
1454e80dedSBartosz Golaszewski #include <linux/etherdevice.h>
1554e80dedSBartosz Golaszewski #include <linux/ethtool.h>
1669226896SRoger Quadros #include <linux/gpio.h>
1769226896SRoger Quadros #include <linux/gpio/consumer.h>
1854e80dedSBartosz Golaszewski #include <linux/init.h>
1954e80dedSBartosz Golaszewski #include <linux/interrupt.h>
2054e80dedSBartosz Golaszewski #include <linux/io.h>
2154e80dedSBartosz Golaszewski #include <linux/kernel.h>
2234865933SAndrew Lunn #include <linux/micrel_phy.h>
2354e80dedSBartosz Golaszewski #include <linux/mii.h>
2400db8189SAndy Fleming #include <linux/mm.h>
2500db8189SAndy Fleming #include <linux/module.h>
2654e80dedSBartosz Golaszewski #include <linux/netdevice.h>
2754e80dedSBartosz Golaszewski #include <linux/of_device.h>
2854e80dedSBartosz Golaszewski #include <linux/of_gpio.h>
2954e80dedSBartosz Golaszewski #include <linux/of_mdio.h>
3000db8189SAndy Fleming #include <linux/phy.h>
3154e80dedSBartosz Golaszewski #include <linux/reset.h>
3254e80dedSBartosz Golaszewski #include <linux/skbuff.h>
3354e80dedSBartosz Golaszewski #include <linux/slab.h>
3454e80dedSBartosz Golaszewski #include <linux/spinlock.h>
3554e80dedSBartosz Golaszewski #include <linux/string.h>
3602d320c3SSergei Shtylyov #include <linux/uaccess.h>
3754e80dedSBartosz Golaszewski #include <linux/unistd.h>
3800db8189SAndy Fleming 
39e22e996bSUwe Kleine-König #define CREATE_TRACE_POINTS
40e22e996bSUwe Kleine-König #include <trace/events/mdio.h>
41e22e996bSUwe Kleine-König 
42648ea013SFlorian Fainelli #include "mdio-boardinfo.h"
43648ea013SFlorian Fainelli 
mdiobus_register_gpiod(struct mdio_device * mdiodev)44ee7e16b6SAndrew Lunn static int mdiobus_register_gpiod(struct mdio_device *mdiodev)
457f854420SAndrew Lunn {
46bafbdd52SSergei Shtylyov 	/* Deassert the optional reset signal */
4740ba6a12SDmitry Torokhov 	mdiodev->reset_gpio = gpiod_get_optional(&mdiodev->dev,
4840ba6a12SDmitry Torokhov 						 "reset", GPIOD_OUT_LOW);
49d0f0c55eSTang Bin 	if (IS_ERR(mdiodev->reset_gpio))
50d0f0c55eSTang Bin 		return PTR_ERR(mdiodev->reset_gpio);
51bafbdd52SSergei Shtylyov 
5240ba6a12SDmitry Torokhov 	if (mdiodev->reset_gpio)
5340ba6a12SDmitry Torokhov 		gpiod_set_consumer_name(mdiodev->reset_gpio, "PHY reset");
54bafbdd52SSergei Shtylyov 
5571dd6c0dSDavid Bauer 	return 0;
5671dd6c0dSDavid Bauer }
5771dd6c0dSDavid Bauer 
mdiobus_register_reset(struct mdio_device * mdiodev)5871dd6c0dSDavid Bauer static int mdiobus_register_reset(struct mdio_device *mdiodev)
5971dd6c0dSDavid Bauer {
6062140036SGeert Uytterhoeven 	struct reset_control *reset;
6171dd6c0dSDavid Bauer 
6262140036SGeert Uytterhoeven 	reset = reset_control_get_optional_exclusive(&mdiodev->dev, "phy");
6362140036SGeert Uytterhoeven 	if (IS_ERR(reset))
6471dd6c0dSDavid Bauer 		return PTR_ERR(reset);
6571dd6c0dSDavid Bauer 
6671dd6c0dSDavid Bauer 	mdiodev->reset_ctrl = reset;
67bafbdd52SSergei Shtylyov 
68ee7e16b6SAndrew Lunn 	return 0;
69ee7e16b6SAndrew Lunn }
70ee7e16b6SAndrew Lunn 
mdiobus_register_device(struct mdio_device * mdiodev)71ee7e16b6SAndrew Lunn int mdiobus_register_device(struct mdio_device *mdiodev)
72ee7e16b6SAndrew Lunn {
73ee7e16b6SAndrew Lunn 	int err;
74ee7e16b6SAndrew Lunn 
75ee7e16b6SAndrew Lunn 	if (mdiodev->bus->mdio_map[mdiodev->addr])
76ee7e16b6SAndrew Lunn 		return -EBUSY;
77ee7e16b6SAndrew Lunn 
78ee7e16b6SAndrew Lunn 	if (mdiodev->flags & MDIO_DEVICE_FLAG_PHY) {
79ee7e16b6SAndrew Lunn 		err = mdiobus_register_gpiod(mdiodev);
80ee7e16b6SAndrew Lunn 		if (err)
81ee7e16b6SAndrew Lunn 			return err;
8271dd6c0dSDavid Bauer 
8371dd6c0dSDavid Bauer 		err = mdiobus_register_reset(mdiodev);
8471dd6c0dSDavid Bauer 		if (err)
8571dd6c0dSDavid Bauer 			return err;
8671dd6c0dSDavid Bauer 
8771dd6c0dSDavid Bauer 		/* Assert the reset signal */
8871dd6c0dSDavid Bauer 		mdio_device_reset(mdiodev, 1);
89ee7e16b6SAndrew Lunn 	}
90ee7e16b6SAndrew Lunn 
917f854420SAndrew Lunn 	mdiodev->bus->mdio_map[mdiodev->addr] = mdiodev;
927f854420SAndrew Lunn 
937f854420SAndrew Lunn 	return 0;
947f854420SAndrew Lunn }
957f854420SAndrew Lunn EXPORT_SYMBOL(mdiobus_register_device);
967f854420SAndrew Lunn 
mdiobus_unregister_device(struct mdio_device * mdiodev)977f854420SAndrew Lunn int mdiobus_unregister_device(struct mdio_device *mdiodev)
987f854420SAndrew Lunn {
997f854420SAndrew Lunn 	if (mdiodev->bus->mdio_map[mdiodev->addr] != mdiodev)
1007f854420SAndrew Lunn 		return -EINVAL;
1017f854420SAndrew Lunn 
10232085f25SDavid Bauer 	reset_control_put(mdiodev->reset_ctrl);
10332085f25SDavid Bauer 
1047f854420SAndrew Lunn 	mdiodev->bus->mdio_map[mdiodev->addr] = NULL;
1057f854420SAndrew Lunn 
1067f854420SAndrew Lunn 	return 0;
1077f854420SAndrew Lunn }
1087f854420SAndrew Lunn EXPORT_SYMBOL(mdiobus_unregister_device);
1097f854420SAndrew Lunn 
mdiobus_find_device(struct mii_bus * bus,int addr)110*09bd2d7dSRussell King (Oracle) static struct mdio_device *mdiobus_find_device(struct mii_bus *bus, int addr)
1117f854420SAndrew Lunn {
1128a8b70b3SHeiner Kallweit 	bool addr_valid = addr >= 0 && addr < ARRAY_SIZE(bus->mdio_map);
113867dbe78SHeiner Kallweit 
1148a8b70b3SHeiner Kallweit 	if (WARN_ONCE(!addr_valid, "addr %d out of range\n", addr))
115867dbe78SHeiner Kallweit 		return NULL;
116867dbe78SHeiner Kallweit 
117*09bd2d7dSRussell King (Oracle) 	return bus->mdio_map[addr];
118*09bd2d7dSRussell King (Oracle) }
1197f854420SAndrew Lunn 
mdiobus_get_phy(struct mii_bus * bus,int addr)120*09bd2d7dSRussell King (Oracle) struct phy_device *mdiobus_get_phy(struct mii_bus *bus, int addr)
121*09bd2d7dSRussell King (Oracle) {
122*09bd2d7dSRussell King (Oracle) 	struct mdio_device *mdiodev;
123*09bd2d7dSRussell King (Oracle) 
124*09bd2d7dSRussell King (Oracle) 	mdiodev = mdiobus_find_device(bus, addr);
1257f854420SAndrew Lunn 	if (!mdiodev)
1267f854420SAndrew Lunn 		return NULL;
1277f854420SAndrew Lunn 
1287f854420SAndrew Lunn 	if (!(mdiodev->flags & MDIO_DEVICE_FLAG_PHY))
1297f854420SAndrew Lunn 		return NULL;
1307f854420SAndrew Lunn 
1317f854420SAndrew Lunn 	return container_of(mdiodev, struct phy_device, mdio);
1327f854420SAndrew Lunn }
1337f854420SAndrew Lunn EXPORT_SYMBOL(mdiobus_get_phy);
1347f854420SAndrew Lunn 
mdiobus_is_registered_device(struct mii_bus * bus,int addr)1357f854420SAndrew Lunn bool mdiobus_is_registered_device(struct mii_bus *bus, int addr)
1367f854420SAndrew Lunn {
137*09bd2d7dSRussell King (Oracle) 	return mdiobus_find_device(bus, addr) != NULL;
1387f854420SAndrew Lunn }
1397f854420SAndrew Lunn EXPORT_SYMBOL(mdiobus_is_registered_device);
1407f854420SAndrew Lunn 
141b3df0da8SRandy Dunlap /**
142eb8a54a7STimur Tabi  * mdiobus_alloc_size - allocate a mii_bus structure
143af58f1d6SRandy Dunlap  * @size: extra amount of memory to allocate for private storage.
144af58f1d6SRandy Dunlap  * If non-zero, then bus->priv is points to that memory.
145298cf9beSLennert Buytenhek  *
146298cf9beSLennert Buytenhek  * Description: called by a bus driver to allocate an mii_bus
147298cf9beSLennert Buytenhek  * structure to fill in.
148298cf9beSLennert Buytenhek  */
mdiobus_alloc_size(size_t size)149eb8a54a7STimur Tabi struct mii_bus *mdiobus_alloc_size(size_t size)
150298cf9beSLennert Buytenhek {
15146abc021SLennert Buytenhek 	struct mii_bus *bus;
152eb8a54a7STimur Tabi 	size_t aligned_size = ALIGN(sizeof(*bus), NETDEV_ALIGN);
153eb8a54a7STimur Tabi 	size_t alloc_size;
154e7f4dc35SAndrew Lunn 	int i;
15546abc021SLennert Buytenhek 
156eb8a54a7STimur Tabi 	/* If we alloc extra space, it should be aligned */
157eb8a54a7STimur Tabi 	if (size)
158eb8a54a7STimur Tabi 		alloc_size = aligned_size + size;
159eb8a54a7STimur Tabi 	else
160eb8a54a7STimur Tabi 		alloc_size = sizeof(*bus);
161eb8a54a7STimur Tabi 
162eb8a54a7STimur Tabi 	bus = kzalloc(alloc_size, GFP_KERNEL);
163db9107b4SDan Carpenter 	if (!bus)
164db9107b4SDan Carpenter 		return NULL;
165db9107b4SDan Carpenter 
16646abc021SLennert Buytenhek 	bus->state = MDIOBUS_ALLOCATED;
167eb8a54a7STimur Tabi 	if (size)
168eb8a54a7STimur Tabi 		bus->priv = (void *)bus + aligned_size;
16946abc021SLennert Buytenhek 
170080bb352SFlorian Fainelli 	/* Initialise the interrupts to polling and 64-bit seqcounts */
171080bb352SFlorian Fainelli 	for (i = 0; i < PHY_MAX_ADDR; i++) {
172e7f4dc35SAndrew Lunn 		bus->irq[i] = PHY_POLL;
173080bb352SFlorian Fainelli 		u64_stats_init(&bus->stats[i].syncp);
174080bb352SFlorian Fainelli 	}
175e7f4dc35SAndrew Lunn 
17646abc021SLennert Buytenhek 	return bus;
177298cf9beSLennert Buytenhek }
178eb8a54a7STimur Tabi EXPORT_SYMBOL(mdiobus_alloc_size);
179298cf9beSLennert Buytenhek 
180298cf9beSLennert Buytenhek /**
18146abc021SLennert Buytenhek  * mdiobus_release - mii_bus device release callback
18278c36b15SRandy Dunlap  * @d: the target struct device that contains the mii_bus
18346abc021SLennert Buytenhek  *
18446abc021SLennert Buytenhek  * Description: called when the last reference to an mii_bus is
18546abc021SLennert Buytenhek  * dropped, to free the underlying memory.
18646abc021SLennert Buytenhek  */
mdiobus_release(struct device * d)18746abc021SLennert Buytenhek static void mdiobus_release(struct device *d)
18846abc021SLennert Buytenhek {
18946abc021SLennert Buytenhek 	struct mii_bus *bus = to_mii_bus(d);
190775f2547SWenpeng Liang 
191867ae8a7SFlorian Fainelli 	WARN(bus->state != MDIOBUS_RELEASED &&
192161c8d2fSKrzysztof Halasa 	     /* for compatibility with error handling in drivers */
193867ae8a7SFlorian Fainelli 	     bus->state != MDIOBUS_ALLOCATED,
194867ae8a7SFlorian Fainelli 	     "%s: not in RELEASED or ALLOCATED state\n",
195867ae8a7SFlorian Fainelli 	     bus->id);
19646abc021SLennert Buytenhek 	kfree(bus);
19746abc021SLennert Buytenhek }
19846abc021SLennert Buytenhek 
199080bb352SFlorian Fainelli struct mdio_bus_stat_attr {
200080bb352SFlorian Fainelli 	int addr;
201080bb352SFlorian Fainelli 	unsigned int field_offset;
202080bb352SFlorian Fainelli };
203080bb352SFlorian Fainelli 
mdio_bus_get_stat(struct mdio_bus_stats * s,unsigned int offset)204080bb352SFlorian Fainelli static u64 mdio_bus_get_stat(struct mdio_bus_stats *s, unsigned int offset)
205080bb352SFlorian Fainelli {
206080bb352SFlorian Fainelli 	const char *p = (const char *)s + offset;
207080bb352SFlorian Fainelli 	unsigned int start;
208080bb352SFlorian Fainelli 	u64 val = 0;
209080bb352SFlorian Fainelli 
210080bb352SFlorian Fainelli 	do {
211080bb352SFlorian Fainelli 		start = u64_stats_fetch_begin(&s->syncp);
212080bb352SFlorian Fainelli 		val = u64_stats_read((const u64_stats_t *)p);
213080bb352SFlorian Fainelli 	} while (u64_stats_fetch_retry(&s->syncp, start));
214080bb352SFlorian Fainelli 
215080bb352SFlorian Fainelli 	return val;
216080bb352SFlorian Fainelli }
217080bb352SFlorian Fainelli 
mdio_bus_get_global_stat(struct mii_bus * bus,unsigned int offset)218080bb352SFlorian Fainelli static u64 mdio_bus_get_global_stat(struct mii_bus *bus, unsigned int offset)
219080bb352SFlorian Fainelli {
220080bb352SFlorian Fainelli 	unsigned int i;
221080bb352SFlorian Fainelli 	u64 val = 0;
222080bb352SFlorian Fainelli 
223080bb352SFlorian Fainelli 	for (i = 0; i < PHY_MAX_ADDR; i++)
224080bb352SFlorian Fainelli 		val += mdio_bus_get_stat(&bus->stats[i], offset);
225080bb352SFlorian Fainelli 
226080bb352SFlorian Fainelli 	return val;
227080bb352SFlorian Fainelli }
228080bb352SFlorian Fainelli 
mdio_bus_stat_field_show(struct device * dev,struct device_attribute * attr,char * buf)229080bb352SFlorian Fainelli static ssize_t mdio_bus_stat_field_show(struct device *dev,
230080bb352SFlorian Fainelli 					struct device_attribute *attr,
231080bb352SFlorian Fainelli 					char *buf)
232080bb352SFlorian Fainelli {
233080bb352SFlorian Fainelli 	struct mii_bus *bus = to_mii_bus(dev);
234080bb352SFlorian Fainelli 	struct mdio_bus_stat_attr *sattr;
235080bb352SFlorian Fainelli 	struct dev_ext_attribute *eattr;
236080bb352SFlorian Fainelli 	u64 val;
237080bb352SFlorian Fainelli 
238080bb352SFlorian Fainelli 	eattr = container_of(attr, struct dev_ext_attribute, attr);
239080bb352SFlorian Fainelli 	sattr = eattr->var;
240080bb352SFlorian Fainelli 
241080bb352SFlorian Fainelli 	if (sattr->addr < 0)
242080bb352SFlorian Fainelli 		val = mdio_bus_get_global_stat(bus, sattr->field_offset);
243080bb352SFlorian Fainelli 	else
244080bb352SFlorian Fainelli 		val = mdio_bus_get_stat(&bus->stats[sattr->addr],
245080bb352SFlorian Fainelli 					sattr->field_offset);
246080bb352SFlorian Fainelli 
247b5155dddSWang Yufen 	return sysfs_emit(buf, "%llu\n", val);
248080bb352SFlorian Fainelli }
249080bb352SFlorian Fainelli 
mdio_bus_device_stat_field_show(struct device * dev,struct device_attribute * attr,char * buf)250080bb352SFlorian Fainelli static ssize_t mdio_bus_device_stat_field_show(struct device *dev,
251080bb352SFlorian Fainelli 					       struct device_attribute *attr,
252080bb352SFlorian Fainelli 					       char *buf)
253080bb352SFlorian Fainelli {
254080bb352SFlorian Fainelli 	struct mdio_device *mdiodev = to_mdio_device(dev);
255080bb352SFlorian Fainelli 	struct mii_bus *bus = mdiodev->bus;
256080bb352SFlorian Fainelli 	struct mdio_bus_stat_attr *sattr;
257080bb352SFlorian Fainelli 	struct dev_ext_attribute *eattr;
258080bb352SFlorian Fainelli 	int addr = mdiodev->addr;
259080bb352SFlorian Fainelli 	u64 val;
260080bb352SFlorian Fainelli 
261080bb352SFlorian Fainelli 	eattr = container_of(attr, struct dev_ext_attribute, attr);
262080bb352SFlorian Fainelli 	sattr = eattr->var;
263080bb352SFlorian Fainelli 
264080bb352SFlorian Fainelli 	val = mdio_bus_get_stat(&bus->stats[addr], sattr->field_offset);
265080bb352SFlorian Fainelli 
266b5155dddSWang Yufen 	return sysfs_emit(buf, "%llu\n", val);
267080bb352SFlorian Fainelli }
268080bb352SFlorian Fainelli 
269080bb352SFlorian Fainelli #define MDIO_BUS_STATS_ATTR_DECL(field, file)				\
270080bb352SFlorian Fainelli static struct dev_ext_attribute dev_attr_mdio_bus_##field = {		\
271080bb352SFlorian Fainelli 	.attr = { .attr = { .name = file, .mode = 0444 },		\
272080bb352SFlorian Fainelli 		     .show = mdio_bus_stat_field_show,			\
273080bb352SFlorian Fainelli 	},								\
274080bb352SFlorian Fainelli 	.var = &((struct mdio_bus_stat_attr) {				\
275080bb352SFlorian Fainelli 		-1, offsetof(struct mdio_bus_stats, field)		\
276080bb352SFlorian Fainelli 	}),								\
277080bb352SFlorian Fainelli };									\
278080bb352SFlorian Fainelli static struct dev_ext_attribute dev_attr_mdio_bus_device_##field = {	\
279080bb352SFlorian Fainelli 	.attr = { .attr = { .name = file, .mode = 0444 },		\
280080bb352SFlorian Fainelli 		     .show = mdio_bus_device_stat_field_show,		\
281080bb352SFlorian Fainelli 	},								\
282080bb352SFlorian Fainelli 	.var = &((struct mdio_bus_stat_attr) {				\
283080bb352SFlorian Fainelli 		-1, offsetof(struct mdio_bus_stats, field)		\
284080bb352SFlorian Fainelli 	}),								\
285080bb352SFlorian Fainelli };
286080bb352SFlorian Fainelli 
287080bb352SFlorian Fainelli #define MDIO_BUS_STATS_ATTR(field)					\
288080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ATTR_DECL(field, __stringify(field))
289080bb352SFlorian Fainelli 
290080bb352SFlorian Fainelli MDIO_BUS_STATS_ATTR(transfers);
291080bb352SFlorian Fainelli MDIO_BUS_STATS_ATTR(errors);
292080bb352SFlorian Fainelli MDIO_BUS_STATS_ATTR(writes);
293080bb352SFlorian Fainelli MDIO_BUS_STATS_ATTR(reads);
294080bb352SFlorian Fainelli 
295080bb352SFlorian Fainelli #define MDIO_BUS_STATS_ADDR_ATTR_DECL(field, addr, file)		\
296080bb352SFlorian Fainelli static struct dev_ext_attribute dev_attr_mdio_bus_addr_##field##_##addr = { \
297080bb352SFlorian Fainelli 	.attr = { .attr = { .name = file, .mode = 0444 },		\
298080bb352SFlorian Fainelli 		     .show = mdio_bus_stat_field_show,			\
299080bb352SFlorian Fainelli 	},								\
300080bb352SFlorian Fainelli 	.var = &((struct mdio_bus_stat_attr) {				\
301080bb352SFlorian Fainelli 		addr, offsetof(struct mdio_bus_stats, field)		\
302080bb352SFlorian Fainelli 	}),								\
303080bb352SFlorian Fainelli }
304080bb352SFlorian Fainelli 
305080bb352SFlorian Fainelli #define MDIO_BUS_STATS_ADDR_ATTR(field, addr)				\
306080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_DECL(field, addr,			\
307080bb352SFlorian Fainelli 				 __stringify(field) "_" __stringify(addr))
308080bb352SFlorian Fainelli 
309080bb352SFlorian Fainelli #define MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(addr)			\
310080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR(transfers, addr);			\
311080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR(errors, addr);				\
312080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR(writes, addr);				\
313080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR(reads, addr)				\
314080bb352SFlorian Fainelli 
315080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(0);
316080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(1);
317080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(2);
318080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(3);
319080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(4);
320080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(5);
321080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(6);
322080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(7);
323080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(8);
324080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(9);
325080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(10);
326080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(11);
327080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(12);
328080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(13);
329080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(14);
330080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(15);
331080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(16);
332080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(17);
333080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(18);
334080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(19);
335080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(20);
336080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(21);
337080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(22);
338080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(23);
339080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(24);
340080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(25);
341080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(26);
342080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(27);
343080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(28);
344080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(29);
345080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(30);
346080bb352SFlorian Fainelli MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(31);
347080bb352SFlorian Fainelli 
348080bb352SFlorian Fainelli #define MDIO_BUS_STATS_ADDR_ATTR_GROUP(addr)				\
349080bb352SFlorian Fainelli 	&dev_attr_mdio_bus_addr_transfers_##addr.attr.attr,		\
350080bb352SFlorian Fainelli 	&dev_attr_mdio_bus_addr_errors_##addr.attr.attr,		\
351080bb352SFlorian Fainelli 	&dev_attr_mdio_bus_addr_writes_##addr.attr.attr,		\
352080bb352SFlorian Fainelli 	&dev_attr_mdio_bus_addr_reads_##addr.attr.attr			\
353080bb352SFlorian Fainelli 
354080bb352SFlorian Fainelli static struct attribute *mdio_bus_statistics_attrs[] = {
355080bb352SFlorian Fainelli 	&dev_attr_mdio_bus_transfers.attr.attr,
356080bb352SFlorian Fainelli 	&dev_attr_mdio_bus_errors.attr.attr,
357080bb352SFlorian Fainelli 	&dev_attr_mdio_bus_writes.attr.attr,
358080bb352SFlorian Fainelli 	&dev_attr_mdio_bus_reads.attr.attr,
359080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(0),
360080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(1),
361080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(2),
362080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(3),
363080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(4),
364080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(5),
365080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(6),
366080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(7),
367080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(8),
368080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(9),
369080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(10),
370080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(11),
371080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(12),
372080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(13),
373080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(14),
374080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(15),
375080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(16),
376080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(17),
377080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(18),
378080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(19),
379080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(20),
380080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(21),
381080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(22),
382080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(23),
383080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(24),
384080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(25),
385080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(26),
386080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(27),
387080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(28),
388080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(29),
389080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(30),
390080bb352SFlorian Fainelli 	MDIO_BUS_STATS_ADDR_ATTR_GROUP(31),
391080bb352SFlorian Fainelli 	NULL,
392080bb352SFlorian Fainelli };
393080bb352SFlorian Fainelli 
394080bb352SFlorian Fainelli static const struct attribute_group mdio_bus_statistics_group = {
395080bb352SFlorian Fainelli 	.name	= "statistics",
396080bb352SFlorian Fainelli 	.attrs	= mdio_bus_statistics_attrs,
397080bb352SFlorian Fainelli };
398080bb352SFlorian Fainelli 
399080bb352SFlorian Fainelli static const struct attribute_group *mdio_bus_groups[] = {
400080bb352SFlorian Fainelli 	&mdio_bus_statistics_group,
401080bb352SFlorian Fainelli 	NULL,
402080bb352SFlorian Fainelli };
403080bb352SFlorian Fainelli 
40446abc021SLennert Buytenhek static struct class mdio_bus_class = {
40546abc021SLennert Buytenhek 	.name		= "mdio_bus",
40646abc021SLennert Buytenhek 	.dev_release	= mdiobus_release,
407080bb352SFlorian Fainelli 	.dev_groups	= mdio_bus_groups,
40846abc021SLennert Buytenhek };
40946abc021SLennert Buytenhek 
410ce69e216SJeremy Linton /**
411ce69e216SJeremy Linton  * mdio_find_bus - Given the name of a mdiobus, find the mii_bus.
412ab741102SLothar Rubusch  * @mdio_name: The name of a mdiobus.
413ce69e216SJeremy Linton  *
414ce69e216SJeremy Linton  * Returns a reference to the mii_bus, or NULL if none found.  The
415ce69e216SJeremy Linton  * embedded struct device will have its reference count incremented,
416ce69e216SJeremy Linton  * and this must be put_deviced'ed once the bus is finished with.
417ce69e216SJeremy Linton  */
mdio_find_bus(const char * mdio_name)418ce69e216SJeremy Linton struct mii_bus *mdio_find_bus(const char *mdio_name)
419ce69e216SJeremy Linton {
420ce69e216SJeremy Linton 	struct device *d;
421ce69e216SJeremy Linton 
422ce69e216SJeremy Linton 	d = class_find_device_by_name(&mdio_bus_class, mdio_name);
423ce69e216SJeremy Linton 	return d ? to_mii_bus(d) : NULL;
424ce69e216SJeremy Linton }
425ce69e216SJeremy Linton EXPORT_SYMBOL(mdio_find_bus);
426ce69e216SJeremy Linton 
427b943fbb0SBjørn Mork #if IS_ENABLED(CONFIG_OF_MDIO)
42825106022SDavid Daney /**
42925106022SDavid Daney  * of_mdio_find_bus - Given an mii_bus node, find the mii_bus.
430f41ef2e7SRandy Dunlap  * @mdio_bus_np: Pointer to the mii_bus.
43125106022SDavid Daney  *
432a1364421SRussell King  * Returns a reference to the mii_bus, or NULL if none found.  The
433a1364421SRussell King  * embedded struct device will have its reference count incremented,
434a1364421SRussell King  * and this must be put once the bus is finished with.
43525106022SDavid Daney  *
43625106022SDavid Daney  * Because the association of a device_node and mii_bus is made via
43725106022SDavid Daney  * of_mdiobus_register(), the mii_bus cannot be found before it is
43825106022SDavid Daney  * registered with of_mdiobus_register().
43925106022SDavid Daney  *
44025106022SDavid Daney  */
of_mdio_find_bus(struct device_node * mdio_bus_np)44125106022SDavid Daney struct mii_bus *of_mdio_find_bus(struct device_node *mdio_bus_np)
44225106022SDavid Daney {
44325106022SDavid Daney 	struct device *d;
44425106022SDavid Daney 
44525106022SDavid Daney 	if (!mdio_bus_np)
44625106022SDavid Daney 		return NULL;
44725106022SDavid Daney 
448cfba5de9SSuzuki K Poulose 	d = class_find_device_by_of_node(&mdio_bus_class, mdio_bus_np);
44925106022SDavid Daney 	return d ? to_mii_bus(d) : NULL;
45025106022SDavid Daney }
45125106022SDavid Daney EXPORT_SYMBOL(of_mdio_find_bus);
452d9daa247SDaniel Mack 
453f03bc4aeSAndrew Lunn /* Walk the list of subnodes of a mdio bus and look for a node that
454f03bc4aeSAndrew Lunn  * matches the mdio device's address with its 'reg' property. If
455f03bc4aeSAndrew Lunn  * found, set the of_node pointer for the mdio device. This allows
456f03bc4aeSAndrew Lunn  * auto-probed phy devices to be supplied with information passed in
457f03bc4aeSAndrew Lunn  * via DT.
458d9daa247SDaniel Mack  */
of_mdiobus_link_mdiodev(struct mii_bus * bus,struct mdio_device * mdiodev)459f03bc4aeSAndrew Lunn static void of_mdiobus_link_mdiodev(struct mii_bus *bus,
460f03bc4aeSAndrew Lunn 				    struct mdio_device *mdiodev)
461d9daa247SDaniel Mack {
462f03bc4aeSAndrew Lunn 	struct device *dev = &mdiodev->dev;
463d9daa247SDaniel Mack 	struct device_node *child;
464d9daa247SDaniel Mack 
465e5a03bfdSAndrew Lunn 	if (dev->of_node || !bus->dev.of_node)
466d9daa247SDaniel Mack 		return;
467d9daa247SDaniel Mack 
468e5a03bfdSAndrew Lunn 	for_each_available_child_of_node(bus->dev.of_node, child) {
469d9daa247SDaniel Mack 		int addr;
470d9daa247SDaniel Mack 
471d0a65400SJon Mason 		addr = of_mdio_parse_addr(dev, child);
472d0a65400SJon Mason 		if (addr < 0)
473d9daa247SDaniel Mack 			continue;
474d9daa247SDaniel Mack 
475f03bc4aeSAndrew Lunn 		if (addr == mdiodev->addr) {
4767e33d84dSIoana Ciornei 			device_set_node(dev, of_fwnode_handle(child));
477d33dae51SRussell King (Oracle) 			/* The refcount on "child" is passed to the mdio
478d33dae51SRussell King (Oracle) 			 * device. Do _not_ use of_node_put(child) here.
479d33dae51SRussell King (Oracle) 			 */
480d9daa247SDaniel Mack 			return;
481d9daa247SDaniel Mack 		}
482d9daa247SDaniel Mack 	}
483d9daa247SDaniel Mack }
484d9daa247SDaniel Mack #else /* !IS_ENABLED(CONFIG_OF_MDIO) */
of_mdiobus_link_mdiodev(struct mii_bus * mdio,struct mdio_device * mdiodev)485f03bc4aeSAndrew Lunn static inline void of_mdiobus_link_mdiodev(struct mii_bus *mdio,
486f03bc4aeSAndrew Lunn 					   struct mdio_device *mdiodev)
487d9daa247SDaniel Mack {
488d9daa247SDaniel Mack }
48925106022SDavid Daney #endif
49025106022SDavid Daney 
49146abc021SLennert Buytenhek /**
49269280228SMauro Carvalho Chehab  * mdiobus_create_device - create a full MDIO device given
493d0281a56SFlorian Fainelli  * a mdio_board_info structure
494d0281a56SFlorian Fainelli  * @bus: MDIO bus to create the devices on
495d0281a56SFlorian Fainelli  * @bi: mdio_board_info structure describing the devices
496d0281a56SFlorian Fainelli  *
497d0281a56SFlorian Fainelli  * Returns 0 on success or < 0 on error.
498d0281a56SFlorian Fainelli  */
mdiobus_create_device(struct mii_bus * bus,struct mdio_board_info * bi)499d0281a56SFlorian Fainelli static int mdiobus_create_device(struct mii_bus *bus,
500d0281a56SFlorian Fainelli 				 struct mdio_board_info *bi)
501d0281a56SFlorian Fainelli {
502d0281a56SFlorian Fainelli 	struct mdio_device *mdiodev;
503d0281a56SFlorian Fainelli 	int ret = 0;
504d0281a56SFlorian Fainelli 
505d0281a56SFlorian Fainelli 	mdiodev = mdio_device_create(bus, bi->mdio_addr);
506d0281a56SFlorian Fainelli 	if (IS_ERR(mdiodev))
507d0281a56SFlorian Fainelli 		return -ENODEV;
508d0281a56SFlorian Fainelli 
509d0281a56SFlorian Fainelli 	strncpy(mdiodev->modalias, bi->modalias,
510d0281a56SFlorian Fainelli 		sizeof(mdiodev->modalias));
511d0281a56SFlorian Fainelli 	mdiodev->bus_match = mdio_device_bus_match;
512d0281a56SFlorian Fainelli 	mdiodev->dev.platform_data = (void *)bi->platform_data;
513d0281a56SFlorian Fainelli 
514d0281a56SFlorian Fainelli 	ret = mdio_device_register(mdiodev);
515d0281a56SFlorian Fainelli 	if (ret)
516d0281a56SFlorian Fainelli 		mdio_device_free(mdiodev);
517d0281a56SFlorian Fainelli 
518d0281a56SFlorian Fainelli 	return ret;
519d0281a56SFlorian Fainelli }
520d0281a56SFlorian Fainelli 
mdiobus_scan(struct mii_bus * bus,int addr,bool c45)521d41e1277SAndrew Lunn static struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr, bool c45)
52281d874e7SAndrew Lunn {
52381d874e7SAndrew Lunn 	struct phy_device *phydev = ERR_PTR(-ENODEV);
52481d874e7SAndrew Lunn 	int err;
52581d874e7SAndrew Lunn 
526d41e1277SAndrew Lunn 	phydev = get_phy_device(bus, addr, c45);
52781d874e7SAndrew Lunn 	if (IS_ERR(phydev))
52881d874e7SAndrew Lunn 		return phydev;
52981d874e7SAndrew Lunn 
53081d874e7SAndrew Lunn 	/* For DT, see if the auto-probed phy has a corresponding child
53181d874e7SAndrew Lunn 	 * in the bus node, and set the of_node pointer in this case.
53281d874e7SAndrew Lunn 	 */
53381d874e7SAndrew Lunn 	of_mdiobus_link_mdiodev(bus, &phydev->mdio);
53481d874e7SAndrew Lunn 
53581d874e7SAndrew Lunn 	err = phy_device_register(phydev);
53681d874e7SAndrew Lunn 	if (err) {
53781d874e7SAndrew Lunn 		phy_device_free(phydev);
53881d874e7SAndrew Lunn 		return ERR_PTR(-ENODEV);
53981d874e7SAndrew Lunn 	}
54081d874e7SAndrew Lunn 
54181d874e7SAndrew Lunn 	return phydev;
54281d874e7SAndrew Lunn }
543d41e1277SAndrew Lunn 
544d41e1277SAndrew Lunn /**
545d41e1277SAndrew Lunn  * mdiobus_scan_c22 - scan one address on a bus for C22 MDIO devices.
546d41e1277SAndrew Lunn  * @bus: mii_bus to scan
547d41e1277SAndrew Lunn  * @addr: address on bus to scan
548d41e1277SAndrew Lunn  *
549d41e1277SAndrew Lunn  * This function scans one address on the MDIO bus, looking for
550d41e1277SAndrew Lunn  * devices which can be identified using a vendor/product ID in
551d41e1277SAndrew Lunn  * registers 2 and 3. Not all MDIO devices have such registers, but
552d41e1277SAndrew Lunn  * PHY devices typically do. Hence this function assumes anything
553d41e1277SAndrew Lunn  * found is a PHY, or can be treated as a PHY. Other MDIO devices,
554d41e1277SAndrew Lunn  * such as switches, will probably not be found during the scan.
555d41e1277SAndrew Lunn  */
mdiobus_scan_c22(struct mii_bus * bus,int addr)556d41e1277SAndrew Lunn struct phy_device *mdiobus_scan_c22(struct mii_bus *bus, int addr)
557d41e1277SAndrew Lunn {
558d41e1277SAndrew Lunn 	return mdiobus_scan(bus, addr, false);
559d41e1277SAndrew Lunn }
560d41e1277SAndrew Lunn EXPORT_SYMBOL(mdiobus_scan_c22);
561d41e1277SAndrew Lunn 
562d41e1277SAndrew Lunn /**
563d41e1277SAndrew Lunn  * mdiobus_scan_c45 - scan one address on a bus for C45 MDIO devices.
564d41e1277SAndrew Lunn  * @bus: mii_bus to scan
565d41e1277SAndrew Lunn  * @addr: address on bus to scan
566d41e1277SAndrew Lunn  *
567d41e1277SAndrew Lunn  * This function scans one address on the MDIO bus, looking for
568d41e1277SAndrew Lunn  * devices which can be identified using a vendor/product ID in
569d41e1277SAndrew Lunn  * registers 2 and 3. Not all MDIO devices have such registers, but
570d41e1277SAndrew Lunn  * PHY devices typically do. Hence this function assumes anything
571d41e1277SAndrew Lunn  * found is a PHY, or can be treated as a PHY. Other MDIO devices,
572d41e1277SAndrew Lunn  * such as switches, will probably not be found during the scan.
573d41e1277SAndrew Lunn  */
mdiobus_scan_c45(struct mii_bus * bus,int addr)574d41e1277SAndrew Lunn static struct phy_device *mdiobus_scan_c45(struct mii_bus *bus, int addr)
575d41e1277SAndrew Lunn {
576d41e1277SAndrew Lunn 	return mdiobus_scan(bus, addr, true);
577d41e1277SAndrew Lunn }
578d41e1277SAndrew Lunn 
mdiobus_scan_bus_c22(struct mii_bus * bus)579d41e1277SAndrew Lunn static int mdiobus_scan_bus_c22(struct mii_bus *bus)
580d41e1277SAndrew Lunn {
581d41e1277SAndrew Lunn 	int i;
582d41e1277SAndrew Lunn 
583d41e1277SAndrew Lunn 	for (i = 0; i < PHY_MAX_ADDR; i++) {
584d41e1277SAndrew Lunn 		if ((bus->phy_mask & BIT(i)) == 0) {
585d41e1277SAndrew Lunn 			struct phy_device *phydev;
586d41e1277SAndrew Lunn 
587d41e1277SAndrew Lunn 			phydev = mdiobus_scan_c22(bus, i);
588d41e1277SAndrew Lunn 			if (IS_ERR(phydev) && (PTR_ERR(phydev) != -ENODEV))
589d41e1277SAndrew Lunn 				return PTR_ERR(phydev);
590d41e1277SAndrew Lunn 		}
591d41e1277SAndrew Lunn 	}
592d41e1277SAndrew Lunn 	return 0;
593d41e1277SAndrew Lunn }
594d41e1277SAndrew Lunn 
mdiobus_scan_bus_c45(struct mii_bus * bus)595d41e1277SAndrew Lunn static int mdiobus_scan_bus_c45(struct mii_bus *bus)
596d41e1277SAndrew Lunn {
597d41e1277SAndrew Lunn 	int i;
598d41e1277SAndrew Lunn 
599d41e1277SAndrew Lunn 	for (i = 0; i < PHY_MAX_ADDR; i++) {
600d41e1277SAndrew Lunn 		if ((bus->phy_mask & BIT(i)) == 0) {
601d41e1277SAndrew Lunn 			struct phy_device *phydev;
602d41e1277SAndrew Lunn 
603d41e1277SAndrew Lunn 			/* Don't scan C45 if we already have a C22 device */
604d41e1277SAndrew Lunn 			if (bus->mdio_map[i])
605d41e1277SAndrew Lunn 				continue;
606d41e1277SAndrew Lunn 
607d41e1277SAndrew Lunn 			phydev = mdiobus_scan_c45(bus, i);
608d41e1277SAndrew Lunn 			if (IS_ERR(phydev) && (PTR_ERR(phydev) != -ENODEV))
609d41e1277SAndrew Lunn 				return PTR_ERR(phydev);
610d41e1277SAndrew Lunn 		}
611d41e1277SAndrew Lunn 	}
612d41e1277SAndrew Lunn 	return 0;
613d41e1277SAndrew Lunn }
61481d874e7SAndrew Lunn 
61534865933SAndrew Lunn /* There are some C22 PHYs which do bad things when where is a C45
61634865933SAndrew Lunn  * transaction on the bus, like accepting a read themselves, and
61734865933SAndrew Lunn  * stomping over the true devices reply, to performing a write to
61834865933SAndrew Lunn  * themselves which was intended for another device. Now that C22
61934865933SAndrew Lunn  * devices have been found, see if any of them are bad for C45, and if we
62034865933SAndrew Lunn  * should skip the C45 scan.
62134865933SAndrew Lunn  */
mdiobus_prevent_c45_scan(struct mii_bus * bus)62234865933SAndrew Lunn static bool mdiobus_prevent_c45_scan(struct mii_bus *bus)
62334865933SAndrew Lunn {
62434865933SAndrew Lunn 	int i;
62534865933SAndrew Lunn 
62634865933SAndrew Lunn 	for (i = 0; i < PHY_MAX_ADDR; i++) {
62734865933SAndrew Lunn 		struct phy_device *phydev;
62834865933SAndrew Lunn 		u32 oui;
62934865933SAndrew Lunn 
63034865933SAndrew Lunn 		phydev = mdiobus_get_phy(bus, i);
63134865933SAndrew Lunn 		if (!phydev)
63234865933SAndrew Lunn 			continue;
63334865933SAndrew Lunn 		oui = phydev->phy_id >> 10;
63434865933SAndrew Lunn 
63534865933SAndrew Lunn 		if (oui == MICREL_OUI)
63634865933SAndrew Lunn 			return true;
63734865933SAndrew Lunn 	}
63834865933SAndrew Lunn 	return false;
63934865933SAndrew Lunn }
64034865933SAndrew Lunn 
64181d874e7SAndrew Lunn /**
64259f06978SRussell King  * __mdiobus_register - bring up all the PHYs on a given bus and attach them to bus
643b3df0da8SRandy Dunlap  * @bus: target mii_bus
64459f06978SRussell King  * @owner: module containing bus accessor functions
645e1393456SAndy Fleming  *
646b3df0da8SRandy Dunlap  * Description: Called by a bus driver to bring up all the PHYs
64759f06978SRussell King  *   on a given bus, and attach them to the bus. Drivers should use
64859f06978SRussell King  *   mdiobus_register() rather than __mdiobus_register() unless they
649f89df3f3SAndrew Lunn  *   need to pass a specific owner module. MDIO devices which are not
650fec76125SPeng Li  *   PHYs will not be brought up by this function. They are expected
651f89df3f3SAndrew Lunn  *   to be explicitly listed in DT and instantiated by of_mdiobus_register().
652b3df0da8SRandy Dunlap  *
653b3df0da8SRandy Dunlap  * Returns 0 on success or < 0 on error.
654e1393456SAndy Fleming  */
__mdiobus_register(struct mii_bus * bus,struct module * owner)6553e3aaf64SRussell King int __mdiobus_register(struct mii_bus *bus, struct module *owner)
656e1393456SAndy Fleming {
657711fdba3SAndrew Lunn 	struct mdio_device *mdiodev;
65869226896SRoger Quadros 	struct gpio_desc *gpiod;
65934865933SAndrew Lunn 	bool prevent_c45_scan;
66034865933SAndrew Lunn 	int i, err;
661e1393456SAndy Fleming 
662555d64c6SAndrew Lunn 	if (!bus || !bus->name)
663555d64c6SAndrew Lunn 		return -EINVAL;
664555d64c6SAndrew Lunn 
665555d64c6SAndrew Lunn 	/* An access method always needs both read and write operations */
666555d64c6SAndrew Lunn 	if (!!bus->read != !!bus->write || !!bus->read_c45 != !!bus->write_c45)
667555d64c6SAndrew Lunn 		return -EINVAL;
668555d64c6SAndrew Lunn 
669555d64c6SAndrew Lunn 	/* At least one method is mandatory */
670555d64c6SAndrew Lunn 	if (!bus->read && !bus->read_c45)
671e1393456SAndy Fleming 		return -EINVAL;
672e1393456SAndy Fleming 
67304f41c68SSaravana Kannan 	if (bus->parent && bus->parent->of_node)
67404f41c68SSaravana Kannan 		bus->parent->of_node->fwnode.flags |=
67504f41c68SSaravana Kannan 					FWNODE_FLAG_NEEDS_CHILD_BOUND_ON_ADD;
67604f41c68SSaravana Kannan 
677867ae8a7SFlorian Fainelli 	WARN(bus->state != MDIOBUS_ALLOCATED &&
678867ae8a7SFlorian Fainelli 	     bus->state != MDIOBUS_UNREGISTERED,
679867ae8a7SFlorian Fainelli 	     "%s: not in ALLOCATED or UNREGISTERED state\n", bus->id);
68046abc021SLennert Buytenhek 
6813e3aaf64SRussell King 	bus->owner = owner;
68246abc021SLennert Buytenhek 	bus->dev.parent = bus->parent;
68346abc021SLennert Buytenhek 	bus->dev.class = &mdio_bus_class;
68446abc021SLennert Buytenhek 	bus->dev.groups = NULL;
685036b6687SStephen Hemminger 	dev_set_name(&bus->dev, "%s", bus->id);
68646abc021SLennert Buytenhek 
687ca6e11c3SPavel Skripkin 	/* We need to set state to MDIOBUS_UNREGISTERED to correctly release
688ca6e11c3SPavel Skripkin 	 * the device in mdiobus_free()
689ca6e11c3SPavel Skripkin 	 *
690ca6e11c3SPavel Skripkin 	 * State will be updated later in this function in case of success
691ca6e11c3SPavel Skripkin 	 */
692ca6e11c3SPavel Skripkin 	bus->state = MDIOBUS_UNREGISTERED;
693ca6e11c3SPavel Skripkin 
69446abc021SLennert Buytenhek 	err = device_register(&bus->dev);
69546abc021SLennert Buytenhek 	if (err) {
6968d242488SJoe Perches 		pr_err("mii_bus %s failed to register\n", bus->id);
69746abc021SLennert Buytenhek 		return -EINVAL;
69846abc021SLennert Buytenhek 	}
69946abc021SLennert Buytenhek 
700d1e7fe4dSAdrian Bunk 	mutex_init(&bus->mdio_lock);
70163490847SMichael Walle 	mutex_init(&bus->shared_lock);
702d1e7fe4dSAdrian Bunk 
703e0183b97SMike Looijmans 	/* assert bus level PHY GPIO reset */
704e0183b97SMike Looijmans 	gpiod = devm_gpiod_get_optional(&bus->dev, "reset", GPIOD_OUT_HIGH);
70569226896SRoger Quadros 	if (IS_ERR(gpiod)) {
7060a12ad59SGrygorii Strashko 		err = dev_err_probe(&bus->dev, PTR_ERR(gpiod),
7070a12ad59SGrygorii Strashko 				    "mii_bus %s couldn't get reset GPIO\n",
70869226896SRoger Quadros 				    bus->id);
709e40e2a2eSThomas Petazzoni 		device_del(&bus->dev);
7100a12ad59SGrygorii Strashko 		return err;
711fe0e4052SSergei Shtylyov 	} else	if (gpiod) {
712d396e84cSSergei Shtylyov 		bus->reset_gpiod = gpiod;
7136259e0f5SBruno Thomsen 		fsleep(bus->reset_delay_us);
71469226896SRoger Quadros 		gpiod_set_value_cansleep(gpiod, 0);
715bb383129SBruno Thomsen 		if (bus->reset_post_delay_us > 0)
716bb383129SBruno Thomsen 			fsleep(bus->reset_post_delay_us);
71769226896SRoger Quadros 	}
71869226896SRoger Quadros 
719c290d1abSFlorian Fainelli 	if (bus->reset) {
720c290d1abSFlorian Fainelli 		err = bus->reset(bus);
721c290d1abSFlorian Fainelli 		if (err)
722c290d1abSFlorian Fainelli 			goto error_reset_gpiod;
723c290d1abSFlorian Fainelli 	}
724df0c8d91SFlorian Fainelli 
7251a136ca2SAndrew Lunn 	if (bus->read) {
726d41e1277SAndrew Lunn 		err = mdiobus_scan_bus_c22(bus);
727d41e1277SAndrew Lunn 		if (err)
728161c8d2fSKrzysztof Halasa 			goto error;
729161c8d2fSKrzysztof Halasa 	}
730d41e1277SAndrew Lunn 
73134865933SAndrew Lunn 	prevent_c45_scan = mdiobus_prevent_c45_scan(bus);
73234865933SAndrew Lunn 
7331a136ca2SAndrew Lunn 	if (!prevent_c45_scan && bus->read_c45) {
734d41e1277SAndrew Lunn 		err = mdiobus_scan_bus_c45(bus);
735d41e1277SAndrew Lunn 		if (err)
736d41e1277SAndrew Lunn 			goto error;
737e1393456SAndy Fleming 	}
738e1393456SAndy Fleming 
739d0281a56SFlorian Fainelli 	mdiobus_setup_mdiodev_from_board_info(bus, mdiobus_create_device);
740648ea013SFlorian Fainelli 
741e8e5752dSKrzysztof Halasa 	bus->state = MDIOBUS_REGISTERED;
7427590fc6fSFlorian Fainelli 	dev_dbg(&bus->dev, "probed\n");
743161c8d2fSKrzysztof Halasa 	return 0;
744e1393456SAndy Fleming 
745161c8d2fSKrzysztof Halasa error:
746d41e1277SAndrew Lunn 	for (i = 0; i < PHY_MAX_ADDR; i++) {
747711fdba3SAndrew Lunn 		mdiodev = bus->mdio_map[i];
748711fdba3SAndrew Lunn 		if (!mdiodev)
749711fdba3SAndrew Lunn 			continue;
750711fdba3SAndrew Lunn 
751711fdba3SAndrew Lunn 		mdiodev->device_remove(mdiodev);
752711fdba3SAndrew Lunn 		mdiodev->device_free(mdiodev);
753161c8d2fSKrzysztof Halasa 	}
754c290d1abSFlorian Fainelli error_reset_gpiod:
75569226896SRoger Quadros 	/* Put PHYs in RESET to save power */
756a010a2f6SFlorian Fainelli 	if (bus->reset_gpiod)
757d396e84cSSergei Shtylyov 		gpiod_set_value_cansleep(bus->reset_gpiod, 1);
75869226896SRoger Quadros 
759161c8d2fSKrzysztof Halasa 	device_del(&bus->dev);
760e1393456SAndy Fleming 	return err;
761e1393456SAndy Fleming }
7623e3aaf64SRussell King EXPORT_SYMBOL(__mdiobus_register);
763e1393456SAndy Fleming 
mdiobus_unregister(struct mii_bus * bus)764e1393456SAndy Fleming void mdiobus_unregister(struct mii_bus *bus)
765e1393456SAndy Fleming {
766a9049e0cSAndrew Lunn 	struct mdio_device *mdiodev;
767e1393456SAndy Fleming 	int i;
768e1393456SAndy Fleming 
7691dde47a6SDan Carpenter 	if (WARN_ON_ONCE(bus->state != MDIOBUS_REGISTERED))
7701dde47a6SDan Carpenter 		return;
77146abc021SLennert Buytenhek 	bus->state = MDIOBUS_UNREGISTERED;
77246abc021SLennert Buytenhek 
773e1393456SAndy Fleming 	for (i = 0; i < PHY_MAX_ADDR; i++) {
774a9049e0cSAndrew Lunn 		mdiodev = bus->mdio_map[i];
775a9049e0cSAndrew Lunn 		if (!mdiodev)
776a9049e0cSAndrew Lunn 			continue;
777a9049e0cSAndrew Lunn 
7786110ed2dSDavid Bauer 		if (mdiodev->reset_gpio)
7796110ed2dSDavid Bauer 			gpiod_put(mdiodev->reset_gpio);
780bafbdd52SSergei Shtylyov 
781711fdba3SAndrew Lunn 		mdiodev->device_remove(mdiodev);
782711fdba3SAndrew Lunn 		mdiodev->device_free(mdiodev);
783e1393456SAndy Fleming 	}
78469226896SRoger Quadros 
78569226896SRoger Quadros 	/* Put PHYs in RESET to save power */
786a010a2f6SFlorian Fainelli 	if (bus->reset_gpiod)
787d396e84cSSergei Shtylyov 		gpiod_set_value_cansleep(bus->reset_gpiod, 1);
78869226896SRoger Quadros 
789b6c6aedcSMark Salter 	device_del(&bus->dev);
790e1393456SAndy Fleming }
791e1393456SAndy Fleming EXPORT_SYMBOL(mdiobus_unregister);
792e1393456SAndy Fleming 
793298cf9beSLennert Buytenhek /**
794298cf9beSLennert Buytenhek  * mdiobus_free - free a struct mii_bus
795298cf9beSLennert Buytenhek  * @bus: mii_bus to free
796298cf9beSLennert Buytenhek  *
79746abc021SLennert Buytenhek  * This function releases the reference to the underlying device
79846abc021SLennert Buytenhek  * object in the mii_bus.  If this is the last reference, the mii_bus
79946abc021SLennert Buytenhek  * will be freed.
800298cf9beSLennert Buytenhek  */
mdiobus_free(struct mii_bus * bus)801298cf9beSLennert Buytenhek void mdiobus_free(struct mii_bus *bus)
802298cf9beSLennert Buytenhek {
80302d320c3SSergei Shtylyov 	/* For compatibility with error handling in drivers. */
80446abc021SLennert Buytenhek 	if (bus->state == MDIOBUS_ALLOCATED) {
805298cf9beSLennert Buytenhek 		kfree(bus);
80646abc021SLennert Buytenhek 		return;
80746abc021SLennert Buytenhek 	}
80846abc021SLennert Buytenhek 
809867ae8a7SFlorian Fainelli 	WARN(bus->state != MDIOBUS_UNREGISTERED,
810867ae8a7SFlorian Fainelli 	     "%s: not in UNREGISTERED state\n", bus->id);
81146abc021SLennert Buytenhek 	bus->state = MDIOBUS_RELEASED;
81246abc021SLennert Buytenhek 
81346abc021SLennert Buytenhek 	put_device(&bus->dev);
814298cf9beSLennert Buytenhek }
815298cf9beSLennert Buytenhek EXPORT_SYMBOL(mdiobus_free);
816298cf9beSLennert Buytenhek 
mdiobus_stats_acct(struct mdio_bus_stats * stats,bool op,int ret)817080bb352SFlorian Fainelli static void mdiobus_stats_acct(struct mdio_bus_stats *stats, bool op, int ret)
818080bb352SFlorian Fainelli {
819c7e261d8SAhmed S. Darwish 	preempt_disable();
820080bb352SFlorian Fainelli 	u64_stats_update_begin(&stats->syncp);
821080bb352SFlorian Fainelli 
822080bb352SFlorian Fainelli 	u64_stats_inc(&stats->transfers);
823080bb352SFlorian Fainelli 	if (ret < 0) {
824080bb352SFlorian Fainelli 		u64_stats_inc(&stats->errors);
825080bb352SFlorian Fainelli 		goto out;
826080bb352SFlorian Fainelli 	}
827080bb352SFlorian Fainelli 
828080bb352SFlorian Fainelli 	if (op)
829080bb352SFlorian Fainelli 		u64_stats_inc(&stats->reads);
830080bb352SFlorian Fainelli 	else
831080bb352SFlorian Fainelli 		u64_stats_inc(&stats->writes);
832080bb352SFlorian Fainelli out:
833080bb352SFlorian Fainelli 	u64_stats_update_end(&stats->syncp);
834c7e261d8SAhmed S. Darwish 	preempt_enable();
835080bb352SFlorian Fainelli }
836080bb352SFlorian Fainelli 
837b3df0da8SRandy Dunlap /**
83834dc08e4SRussell King  * __mdiobus_read - Unlocked version of the mdiobus_read function
83934dc08e4SRussell King  * @bus: the mii_bus struct
84034dc08e4SRussell King  * @addr: the phy address
84134dc08e4SRussell King  * @regnum: register number to read
84234dc08e4SRussell King  *
84334dc08e4SRussell King  * Read a MDIO bus register. Caller must hold the mdio bus lock.
84434dc08e4SRussell King  *
84534dc08e4SRussell King  * NOTE: MUST NOT be called from interrupt context.
84634dc08e4SRussell King  */
__mdiobus_read(struct mii_bus * bus,int addr,u32 regnum)84734dc08e4SRussell King int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum)
84834dc08e4SRussell King {
84934dc08e4SRussell King 	int retval;
85034dc08e4SRussell King 
851e6e918d4SHeiner Kallweit 	lockdep_assert_held_once(&bus->mdio_lock);
85234dc08e4SRussell King 
853b063b192SAndrew Lunn 	if (bus->read)
85434dc08e4SRussell King 		retval = bus->read(bus, addr, regnum);
855b063b192SAndrew Lunn 	else
856b063b192SAndrew Lunn 		retval = -EOPNOTSUPP;
85734dc08e4SRussell King 
85834dc08e4SRussell King 	trace_mdio_access(bus, 1, addr, regnum, retval, retval);
859080bb352SFlorian Fainelli 	mdiobus_stats_acct(&bus->stats[addr], true, retval);
86034dc08e4SRussell King 
86134dc08e4SRussell King 	return retval;
86234dc08e4SRussell King }
86334dc08e4SRussell King EXPORT_SYMBOL(__mdiobus_read);
86434dc08e4SRussell King 
86534dc08e4SRussell King /**
86634dc08e4SRussell King  * __mdiobus_write - Unlocked version of the mdiobus_write function
86734dc08e4SRussell King  * @bus: the mii_bus struct
86834dc08e4SRussell King  * @addr: the phy address
86934dc08e4SRussell King  * @regnum: register number to write
87034dc08e4SRussell King  * @val: value to write to @regnum
87134dc08e4SRussell King  *
87234dc08e4SRussell King  * Write a MDIO bus register. Caller must hold the mdio bus lock.
87334dc08e4SRussell King  *
87434dc08e4SRussell King  * NOTE: MUST NOT be called from interrupt context.
87534dc08e4SRussell King  */
__mdiobus_write(struct mii_bus * bus,int addr,u32 regnum,u16 val)87634dc08e4SRussell King int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val)
87734dc08e4SRussell King {
87834dc08e4SRussell King 	int err;
87934dc08e4SRussell King 
880e6e918d4SHeiner Kallweit 	lockdep_assert_held_once(&bus->mdio_lock);
88134dc08e4SRussell King 
882b063b192SAndrew Lunn 	if (bus->write)
88334dc08e4SRussell King 		err = bus->write(bus, addr, regnum, val);
884b063b192SAndrew Lunn 	else
885b063b192SAndrew Lunn 		err = -EOPNOTSUPP;
88634dc08e4SRussell King 
88734dc08e4SRussell King 	trace_mdio_access(bus, 0, addr, regnum, val, err);
888080bb352SFlorian Fainelli 	mdiobus_stats_acct(&bus->stats[addr], false, err);
88934dc08e4SRussell King 
89034dc08e4SRussell King 	return err;
89134dc08e4SRussell King }
89234dc08e4SRussell King EXPORT_SYMBOL(__mdiobus_write);
89334dc08e4SRussell King 
89434dc08e4SRussell King /**
8956cc7cf81SRussell King  * __mdiobus_modify_changed - Unlocked version of the mdiobus_modify function
8966cc7cf81SRussell King  * @bus: the mii_bus struct
8976cc7cf81SRussell King  * @addr: the phy address
8986cc7cf81SRussell King  * @regnum: register number to modify
8996cc7cf81SRussell King  * @mask: bit mask of bits to clear
9006cc7cf81SRussell King  * @set: bit mask of bits to set
9016cc7cf81SRussell King  *
9026cc7cf81SRussell King  * Read, modify, and if any change, write the register value back to the
9036cc7cf81SRussell King  * device. Any error returns a negative number.
9046cc7cf81SRussell King  *
9056cc7cf81SRussell King  * NOTE: MUST NOT be called from interrupt context.
9066cc7cf81SRussell King  */
__mdiobus_modify_changed(struct mii_bus * bus,int addr,u32 regnum,u16 mask,u16 set)9076cc7cf81SRussell King int __mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum,
9086cc7cf81SRussell King 			     u16 mask, u16 set)
9096cc7cf81SRussell King {
9106cc7cf81SRussell King 	int new, ret;
9116cc7cf81SRussell King 
9126cc7cf81SRussell King 	ret = __mdiobus_read(bus, addr, regnum);
9136cc7cf81SRussell King 	if (ret < 0)
9146cc7cf81SRussell King 		return ret;
9156cc7cf81SRussell King 
9166cc7cf81SRussell King 	new = (ret & ~mask) | set;
9176cc7cf81SRussell King 	if (new == ret)
9186cc7cf81SRussell King 		return 0;
9196cc7cf81SRussell King 
9206cc7cf81SRussell King 	ret = __mdiobus_write(bus, addr, regnum, new);
9216cc7cf81SRussell King 
9226cc7cf81SRussell King 	return ret < 0 ? ret : 1;
9236cc7cf81SRussell King }
9246cc7cf81SRussell King EXPORT_SYMBOL_GPL(__mdiobus_modify_changed);
9256cc7cf81SRussell King 
9266cc7cf81SRussell King /**
9274e4aafcdSAndrew Lunn  * __mdiobus_c45_read - Unlocked version of the mdiobus_c45_read function
9284e4aafcdSAndrew Lunn  * @bus: the mii_bus struct
9294e4aafcdSAndrew Lunn  * @addr: the phy address
9304e4aafcdSAndrew Lunn  * @devad: device address to read
9314e4aafcdSAndrew Lunn  * @regnum: register number to read
9324e4aafcdSAndrew Lunn  *
9334e4aafcdSAndrew Lunn  * Read a MDIO bus register. Caller must hold the mdio bus lock.
9344e4aafcdSAndrew Lunn  *
9354e4aafcdSAndrew Lunn  * NOTE: MUST NOT be called from interrupt context.
9364e4aafcdSAndrew Lunn  */
__mdiobus_c45_read(struct mii_bus * bus,int addr,int devad,u32 regnum)9374e4aafcdSAndrew Lunn int __mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum)
9384e4aafcdSAndrew Lunn {
9394e4aafcdSAndrew Lunn 	int retval;
9404e4aafcdSAndrew Lunn 
9414e4aafcdSAndrew Lunn 	lockdep_assert_held_once(&bus->mdio_lock);
9424e4aafcdSAndrew Lunn 
9434e4aafcdSAndrew Lunn 	if (bus->read_c45)
9444e4aafcdSAndrew Lunn 		retval = bus->read_c45(bus, addr, devad, regnum);
9454e4aafcdSAndrew Lunn 	else
946db1a63aeSAndrew Lunn 		retval = -EOPNOTSUPP;
9474e4aafcdSAndrew Lunn 
9484e4aafcdSAndrew Lunn 	trace_mdio_access(bus, 1, addr, regnum, retval, retval);
9494e4aafcdSAndrew Lunn 	mdiobus_stats_acct(&bus->stats[addr], true, retval);
9504e4aafcdSAndrew Lunn 
9514e4aafcdSAndrew Lunn 	return retval;
9524e4aafcdSAndrew Lunn }
9534e4aafcdSAndrew Lunn EXPORT_SYMBOL(__mdiobus_c45_read);
9544e4aafcdSAndrew Lunn 
9554e4aafcdSAndrew Lunn /**
9564e4aafcdSAndrew Lunn  * __mdiobus_c45_write - Unlocked version of the mdiobus_write function
9574e4aafcdSAndrew Lunn  * @bus: the mii_bus struct
9584e4aafcdSAndrew Lunn  * @addr: the phy address
9594e4aafcdSAndrew Lunn  * @devad: device address to read
9604e4aafcdSAndrew Lunn  * @regnum: register number to write
9614e4aafcdSAndrew Lunn  * @val: value to write to @regnum
9624e4aafcdSAndrew Lunn  *
9634e4aafcdSAndrew Lunn  * Write a MDIO bus register. Caller must hold the mdio bus lock.
9644e4aafcdSAndrew Lunn  *
9654e4aafcdSAndrew Lunn  * NOTE: MUST NOT be called from interrupt context.
9664e4aafcdSAndrew Lunn  */
__mdiobus_c45_write(struct mii_bus * bus,int addr,int devad,u32 regnum,u16 val)9674e4aafcdSAndrew Lunn int __mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum,
9684e4aafcdSAndrew Lunn 			u16 val)
9694e4aafcdSAndrew Lunn {
9704e4aafcdSAndrew Lunn 	int err;
9714e4aafcdSAndrew Lunn 
9724e4aafcdSAndrew Lunn 	lockdep_assert_held_once(&bus->mdio_lock);
9734e4aafcdSAndrew Lunn 
9744e4aafcdSAndrew Lunn 	if (bus->write_c45)
9754e4aafcdSAndrew Lunn 		err = bus->write_c45(bus, addr, devad, regnum, val);
9764e4aafcdSAndrew Lunn 	else
977db1a63aeSAndrew Lunn 		err = -EOPNOTSUPP;
9784e4aafcdSAndrew Lunn 
9794e4aafcdSAndrew Lunn 	trace_mdio_access(bus, 0, addr, regnum, val, err);
9804e4aafcdSAndrew Lunn 	mdiobus_stats_acct(&bus->stats[addr], false, err);
9814e4aafcdSAndrew Lunn 
9824e4aafcdSAndrew Lunn 	return err;
9834e4aafcdSAndrew Lunn }
9844e4aafcdSAndrew Lunn EXPORT_SYMBOL(__mdiobus_c45_write);
9854e4aafcdSAndrew Lunn 
9864e4aafcdSAndrew Lunn /**
9874e4aafcdSAndrew Lunn  * __mdiobus_c45_modify_changed - Unlocked version of the mdiobus_modify function
9884e4aafcdSAndrew Lunn  * @bus: the mii_bus struct
9894e4aafcdSAndrew Lunn  * @addr: the phy address
9904e4aafcdSAndrew Lunn  * @devad: device address to read
9914e4aafcdSAndrew Lunn  * @regnum: register number to modify
9924e4aafcdSAndrew Lunn  * @mask: bit mask of bits to clear
9934e4aafcdSAndrew Lunn  * @set: bit mask of bits to set
9944e4aafcdSAndrew Lunn  *
9954e4aafcdSAndrew Lunn  * Read, modify, and if any change, write the register value back to the
9964e4aafcdSAndrew Lunn  * device. Any error returns a negative number.
9974e4aafcdSAndrew Lunn  *
9984e4aafcdSAndrew Lunn  * NOTE: MUST NOT be called from interrupt context.
9994e4aafcdSAndrew Lunn  */
__mdiobus_c45_modify_changed(struct mii_bus * bus,int addr,int devad,u32 regnum,u16 mask,u16 set)10004e4aafcdSAndrew Lunn static int __mdiobus_c45_modify_changed(struct mii_bus *bus, int addr,
10014e4aafcdSAndrew Lunn 					int devad, u32 regnum, u16 mask,
10024e4aafcdSAndrew Lunn 					u16 set)
10034e4aafcdSAndrew Lunn {
10044e4aafcdSAndrew Lunn 	int new, ret;
10054e4aafcdSAndrew Lunn 
10064e4aafcdSAndrew Lunn 	ret = __mdiobus_c45_read(bus, addr, devad, regnum);
10074e4aafcdSAndrew Lunn 	if (ret < 0)
10084e4aafcdSAndrew Lunn 		return ret;
10094e4aafcdSAndrew Lunn 
10104e4aafcdSAndrew Lunn 	new = (ret & ~mask) | set;
10114e4aafcdSAndrew Lunn 	if (new == ret)
10124e4aafcdSAndrew Lunn 		return 0;
10134e4aafcdSAndrew Lunn 
10144e4aafcdSAndrew Lunn 	ret = __mdiobus_c45_write(bus, addr, devad, regnum, new);
10154e4aafcdSAndrew Lunn 
10164e4aafcdSAndrew Lunn 	return ret < 0 ? ret : 1;
10174e4aafcdSAndrew Lunn }
10184e4aafcdSAndrew Lunn 
10194e4aafcdSAndrew Lunn /**
102021dd19feSNeil Armstrong  * mdiobus_read_nested - Nested version of the mdiobus_read function
102121dd19feSNeil Armstrong  * @bus: the mii_bus struct
102221dd19feSNeil Armstrong  * @addr: the phy address
102321dd19feSNeil Armstrong  * @regnum: register number to read
102421dd19feSNeil Armstrong  *
102521dd19feSNeil Armstrong  * In case of nested MDIO bus access avoid lockdep false positives by
102621dd19feSNeil Armstrong  * using mutex_lock_nested().
102721dd19feSNeil Armstrong  *
102821dd19feSNeil Armstrong  * NOTE: MUST NOT be called from interrupt context,
102921dd19feSNeil Armstrong  * because the bus read/write functions may wait for an interrupt
103021dd19feSNeil Armstrong  * to conclude the operation.
103121dd19feSNeil Armstrong  */
mdiobus_read_nested(struct mii_bus * bus,int addr,u32 regnum)103221dd19feSNeil Armstrong int mdiobus_read_nested(struct mii_bus *bus, int addr, u32 regnum)
103321dd19feSNeil Armstrong {
103421dd19feSNeil Armstrong 	int retval;
103521dd19feSNeil Armstrong 
10369a6f2b01SAndrew Lunn 	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
103734dc08e4SRussell King 	retval = __mdiobus_read(bus, addr, regnum);
103821dd19feSNeil Armstrong 	mutex_unlock(&bus->mdio_lock);
103921dd19feSNeil Armstrong 
104021dd19feSNeil Armstrong 	return retval;
104121dd19feSNeil Armstrong }
104221dd19feSNeil Armstrong EXPORT_SYMBOL(mdiobus_read_nested);
104321dd19feSNeil Armstrong 
104421dd19feSNeil Armstrong /**
10452e888103SLennert Buytenhek  * mdiobus_read - Convenience function for reading a given MII mgmt register
10462e888103SLennert Buytenhek  * @bus: the mii_bus struct
10472e888103SLennert Buytenhek  * @addr: the phy address
10482e888103SLennert Buytenhek  * @regnum: register number to read
10492e888103SLennert Buytenhek  *
10502e888103SLennert Buytenhek  * NOTE: MUST NOT be called from interrupt context,
10512e888103SLennert Buytenhek  * because the bus read/write functions may wait for an interrupt
10522e888103SLennert Buytenhek  * to conclude the operation.
10532e888103SLennert Buytenhek  */
mdiobus_read(struct mii_bus * bus,int addr,u32 regnum)1054abf35df2SJason Gunthorpe int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum)
10552e888103SLennert Buytenhek {
10562e888103SLennert Buytenhek 	int retval;
10572e888103SLennert Buytenhek 
10582e888103SLennert Buytenhek 	mutex_lock(&bus->mdio_lock);
105934dc08e4SRussell King 	retval = __mdiobus_read(bus, addr, regnum);
10602e888103SLennert Buytenhek 	mutex_unlock(&bus->mdio_lock);
10612e888103SLennert Buytenhek 
10622e888103SLennert Buytenhek 	return retval;
10632e888103SLennert Buytenhek }
10642e888103SLennert Buytenhek EXPORT_SYMBOL(mdiobus_read);
10652e888103SLennert Buytenhek 
10662e888103SLennert Buytenhek /**
10674e4aafcdSAndrew Lunn  * mdiobus_c45_read - Convenience function for reading a given MII mgmt register
10684e4aafcdSAndrew Lunn  * @bus: the mii_bus struct
10694e4aafcdSAndrew Lunn  * @addr: the phy address
10704e4aafcdSAndrew Lunn  * @devad: device address to read
10714e4aafcdSAndrew Lunn  * @regnum: register number to read
10724e4aafcdSAndrew Lunn  *
10734e4aafcdSAndrew Lunn  * NOTE: MUST NOT be called from interrupt context,
10744e4aafcdSAndrew Lunn  * because the bus read/write functions may wait for an interrupt
10754e4aafcdSAndrew Lunn  * to conclude the operation.
10764e4aafcdSAndrew Lunn  */
mdiobus_c45_read(struct mii_bus * bus,int addr,int devad,u32 regnum)10774e4aafcdSAndrew Lunn int mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum)
10784e4aafcdSAndrew Lunn {
10794e4aafcdSAndrew Lunn 	int retval;
10804e4aafcdSAndrew Lunn 
10814e4aafcdSAndrew Lunn 	mutex_lock(&bus->mdio_lock);
10824e4aafcdSAndrew Lunn 	retval = __mdiobus_c45_read(bus, addr, devad, regnum);
10834e4aafcdSAndrew Lunn 	mutex_unlock(&bus->mdio_lock);
10844e4aafcdSAndrew Lunn 
10854e4aafcdSAndrew Lunn 	return retval;
10864e4aafcdSAndrew Lunn }
10874e4aafcdSAndrew Lunn EXPORT_SYMBOL(mdiobus_c45_read);
10884e4aafcdSAndrew Lunn 
10894e4aafcdSAndrew Lunn /**
10901d914d51SAndrew Lunn  * mdiobus_c45_read_nested - Nested version of the mdiobus_c45_read function
10911d914d51SAndrew Lunn  * @bus: the mii_bus struct
10921d914d51SAndrew Lunn  * @addr: the phy address
10931d914d51SAndrew Lunn  * @devad: device address to read
10941d914d51SAndrew Lunn  * @regnum: register number to read
10951d914d51SAndrew Lunn  *
10961d914d51SAndrew Lunn  * In case of nested MDIO bus access avoid lockdep false positives by
10971d914d51SAndrew Lunn  * using mutex_lock_nested().
10981d914d51SAndrew Lunn  *
10991d914d51SAndrew Lunn  * NOTE: MUST NOT be called from interrupt context,
11001d914d51SAndrew Lunn  * because the bus read/write functions may wait for an interrupt
11011d914d51SAndrew Lunn  * to conclude the operation.
11021d914d51SAndrew Lunn  */
mdiobus_c45_read_nested(struct mii_bus * bus,int addr,int devad,u32 regnum)11031d914d51SAndrew Lunn int mdiobus_c45_read_nested(struct mii_bus *bus, int addr, int devad,
11041d914d51SAndrew Lunn 			    u32 regnum)
11051d914d51SAndrew Lunn {
11061d914d51SAndrew Lunn 	int retval;
11071d914d51SAndrew Lunn 
11081d914d51SAndrew Lunn 	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
11091d914d51SAndrew Lunn 	retval = __mdiobus_c45_read(bus, addr, devad, regnum);
11101d914d51SAndrew Lunn 	mutex_unlock(&bus->mdio_lock);
11111d914d51SAndrew Lunn 
11121d914d51SAndrew Lunn 	return retval;
11131d914d51SAndrew Lunn }
11141d914d51SAndrew Lunn EXPORT_SYMBOL(mdiobus_c45_read_nested);
11151d914d51SAndrew Lunn 
11161d914d51SAndrew Lunn /**
111721dd19feSNeil Armstrong  * mdiobus_write_nested - Nested version of the mdiobus_write function
111821dd19feSNeil Armstrong  * @bus: the mii_bus struct
111921dd19feSNeil Armstrong  * @addr: the phy address
112021dd19feSNeil Armstrong  * @regnum: register number to write
112121dd19feSNeil Armstrong  * @val: value to write to @regnum
112221dd19feSNeil Armstrong  *
112321dd19feSNeil Armstrong  * In case of nested MDIO bus access avoid lockdep false positives by
112421dd19feSNeil Armstrong  * using mutex_lock_nested().
112521dd19feSNeil Armstrong  *
112621dd19feSNeil Armstrong  * NOTE: MUST NOT be called from interrupt context,
112721dd19feSNeil Armstrong  * because the bus read/write functions may wait for an interrupt
112821dd19feSNeil Armstrong  * to conclude the operation.
112921dd19feSNeil Armstrong  */
mdiobus_write_nested(struct mii_bus * bus,int addr,u32 regnum,u16 val)113021dd19feSNeil Armstrong int mdiobus_write_nested(struct mii_bus *bus, int addr, u32 regnum, u16 val)
113121dd19feSNeil Armstrong {
113221dd19feSNeil Armstrong 	int err;
113321dd19feSNeil Armstrong 
11349a6f2b01SAndrew Lunn 	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
113534dc08e4SRussell King 	err = __mdiobus_write(bus, addr, regnum, val);
113621dd19feSNeil Armstrong 	mutex_unlock(&bus->mdio_lock);
113721dd19feSNeil Armstrong 
113821dd19feSNeil Armstrong 	return err;
113921dd19feSNeil Armstrong }
114021dd19feSNeil Armstrong EXPORT_SYMBOL(mdiobus_write_nested);
114121dd19feSNeil Armstrong 
114221dd19feSNeil Armstrong /**
11432e888103SLennert Buytenhek  * mdiobus_write - Convenience function for writing a given MII mgmt register
11442e888103SLennert Buytenhek  * @bus: the mii_bus struct
11452e888103SLennert Buytenhek  * @addr: the phy address
11462e888103SLennert Buytenhek  * @regnum: register number to write
11472e888103SLennert Buytenhek  * @val: value to write to @regnum
11482e888103SLennert Buytenhek  *
11492e888103SLennert Buytenhek  * NOTE: MUST NOT be called from interrupt context,
11502e888103SLennert Buytenhek  * because the bus read/write functions may wait for an interrupt
11512e888103SLennert Buytenhek  * to conclude the operation.
11522e888103SLennert Buytenhek  */
mdiobus_write(struct mii_bus * bus,int addr,u32 regnum,u16 val)1153abf35df2SJason Gunthorpe int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val)
11542e888103SLennert Buytenhek {
11552e888103SLennert Buytenhek 	int err;
11562e888103SLennert Buytenhek 
11572e888103SLennert Buytenhek 	mutex_lock(&bus->mdio_lock);
115834dc08e4SRussell King 	err = __mdiobus_write(bus, addr, regnum, val);
11592e888103SLennert Buytenhek 	mutex_unlock(&bus->mdio_lock);
11602e888103SLennert Buytenhek 
11612e888103SLennert Buytenhek 	return err;
11622e888103SLennert Buytenhek }
11632e888103SLennert Buytenhek EXPORT_SYMBOL(mdiobus_write);
11642e888103SLennert Buytenhek 
11652e888103SLennert Buytenhek /**
11664e4aafcdSAndrew Lunn  * mdiobus_c45_write - Convenience function for writing a given MII mgmt register
11674e4aafcdSAndrew Lunn  * @bus: the mii_bus struct
11684e4aafcdSAndrew Lunn  * @addr: the phy address
11694e4aafcdSAndrew Lunn  * @devad: device address to read
11704e4aafcdSAndrew Lunn  * @regnum: register number to write
11714e4aafcdSAndrew Lunn  * @val: value to write to @regnum
11724e4aafcdSAndrew Lunn  *
11734e4aafcdSAndrew Lunn  * NOTE: MUST NOT be called from interrupt context,
11744e4aafcdSAndrew Lunn  * because the bus read/write functions may wait for an interrupt
11754e4aafcdSAndrew Lunn  * to conclude the operation.
11764e4aafcdSAndrew Lunn  */
mdiobus_c45_write(struct mii_bus * bus,int addr,int devad,u32 regnum,u16 val)11774e4aafcdSAndrew Lunn int mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum,
11784e4aafcdSAndrew Lunn 		      u16 val)
11794e4aafcdSAndrew Lunn {
11804e4aafcdSAndrew Lunn 	int err;
11814e4aafcdSAndrew Lunn 
11824e4aafcdSAndrew Lunn 	mutex_lock(&bus->mdio_lock);
11834e4aafcdSAndrew Lunn 	err = __mdiobus_c45_write(bus, addr, devad, regnum, val);
11844e4aafcdSAndrew Lunn 	mutex_unlock(&bus->mdio_lock);
11854e4aafcdSAndrew Lunn 
11864e4aafcdSAndrew Lunn 	return err;
11874e4aafcdSAndrew Lunn }
11884e4aafcdSAndrew Lunn EXPORT_SYMBOL(mdiobus_c45_write);
11894e4aafcdSAndrew Lunn 
11904e4aafcdSAndrew Lunn /**
11911d914d51SAndrew Lunn  * mdiobus_c45_write_nested - Nested version of the mdiobus_c45_write function
11921d914d51SAndrew Lunn  * @bus: the mii_bus struct
11931d914d51SAndrew Lunn  * @addr: the phy address
11941d914d51SAndrew Lunn  * @devad: device address to read
11951d914d51SAndrew Lunn  * @regnum: register number to write
11961d914d51SAndrew Lunn  * @val: value to write to @regnum
11971d914d51SAndrew Lunn  *
11981d914d51SAndrew Lunn  * In case of nested MDIO bus access avoid lockdep false positives by
11991d914d51SAndrew Lunn  * using mutex_lock_nested().
12001d914d51SAndrew Lunn  *
12011d914d51SAndrew Lunn  * NOTE: MUST NOT be called from interrupt context,
12021d914d51SAndrew Lunn  * because the bus read/write functions may wait for an interrupt
12031d914d51SAndrew Lunn  * to conclude the operation.
12041d914d51SAndrew Lunn  */
mdiobus_c45_write_nested(struct mii_bus * bus,int addr,int devad,u32 regnum,u16 val)12051d914d51SAndrew Lunn int mdiobus_c45_write_nested(struct mii_bus *bus, int addr, int devad,
12061d914d51SAndrew Lunn 			     u32 regnum, u16 val)
12071d914d51SAndrew Lunn {
12081d914d51SAndrew Lunn 	int err;
12091d914d51SAndrew Lunn 
12101d914d51SAndrew Lunn 	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
12111d914d51SAndrew Lunn 	err = __mdiobus_c45_write(bus, addr, devad, regnum, val);
12121d914d51SAndrew Lunn 	mutex_unlock(&bus->mdio_lock);
12131d914d51SAndrew Lunn 
12141d914d51SAndrew Lunn 	return err;
12151d914d51SAndrew Lunn }
12161d914d51SAndrew Lunn EXPORT_SYMBOL(mdiobus_c45_write_nested);
12171d914d51SAndrew Lunn 
1218e6a45700SRussell King (Oracle) /*
1219e6a45700SRussell King (Oracle)  * __mdiobus_modify - Convenience function for modifying a given mdio device
1220e6a45700SRussell King (Oracle)  *	register
1221e6a45700SRussell King (Oracle)  * @bus: the mii_bus struct
1222e6a45700SRussell King (Oracle)  * @addr: the phy address
1223e6a45700SRussell King (Oracle)  * @regnum: register number to write
1224e6a45700SRussell King (Oracle)  * @mask: bit mask of bits to clear
1225e6a45700SRussell King (Oracle)  * @set: bit mask of bits to set
1226e6a45700SRussell King (Oracle)  */
__mdiobus_modify(struct mii_bus * bus,int addr,u32 regnum,u16 mask,u16 set)1227e6a45700SRussell King (Oracle) int __mdiobus_modify(struct mii_bus *bus, int addr, u32 regnum, u16 mask,
1228e6a45700SRussell King (Oracle) 		     u16 set)
1229e6a45700SRussell King (Oracle) {
1230e6a45700SRussell King (Oracle) 	int err;
1231e6a45700SRussell King (Oracle) 
1232e6a45700SRussell King (Oracle) 	err = __mdiobus_modify_changed(bus, addr, regnum, mask, set);
1233e6a45700SRussell King (Oracle) 
1234e6a45700SRussell King (Oracle) 	return err < 0 ? err : 0;
1235e6a45700SRussell King (Oracle) }
1236e6a45700SRussell King (Oracle) EXPORT_SYMBOL_GPL(__mdiobus_modify);
1237e6a45700SRussell King (Oracle) 
12381d914d51SAndrew Lunn /**
12396cc7cf81SRussell King  * mdiobus_modify - Convenience function for modifying a given mdio device
12406cc7cf81SRussell King  *	register
12416cc7cf81SRussell King  * @bus: the mii_bus struct
12426cc7cf81SRussell King  * @addr: the phy address
12436cc7cf81SRussell King  * @regnum: register number to write
12446cc7cf81SRussell King  * @mask: bit mask of bits to clear
12456cc7cf81SRussell King  * @set: bit mask of bits to set
12466cc7cf81SRussell King  */
mdiobus_modify(struct mii_bus * bus,int addr,u32 regnum,u16 mask,u16 set)12476cc7cf81SRussell King int mdiobus_modify(struct mii_bus *bus, int addr, u32 regnum, u16 mask, u16 set)
12486cc7cf81SRussell King {
12496cc7cf81SRussell King 	int err;
12506cc7cf81SRussell King 
12516cc7cf81SRussell King 	mutex_lock(&bus->mdio_lock);
1252e6a45700SRussell King (Oracle) 	err = __mdiobus_modify(bus, addr, regnum, mask, set);
12536cc7cf81SRussell King 	mutex_unlock(&bus->mdio_lock);
12546cc7cf81SRussell King 
1255e6a45700SRussell King (Oracle) 	return err;
12566cc7cf81SRussell King }
12576cc7cf81SRussell King EXPORT_SYMBOL_GPL(mdiobus_modify);
12586cc7cf81SRussell King 
12596cc7cf81SRussell King /**
12604e4aafcdSAndrew Lunn  * mdiobus_c45_modify - Convenience function for modifying a given mdio device
12614e4aafcdSAndrew Lunn  *	register
12624e4aafcdSAndrew Lunn  * @bus: the mii_bus struct
12634e4aafcdSAndrew Lunn  * @addr: the phy address
12644e4aafcdSAndrew Lunn  * @devad: device address to read
12654e4aafcdSAndrew Lunn  * @regnum: register number to write
12664e4aafcdSAndrew Lunn  * @mask: bit mask of bits to clear
12674e4aafcdSAndrew Lunn  * @set: bit mask of bits to set
12684e4aafcdSAndrew Lunn  */
mdiobus_c45_modify(struct mii_bus * bus,int addr,int devad,u32 regnum,u16 mask,u16 set)12694e4aafcdSAndrew Lunn int mdiobus_c45_modify(struct mii_bus *bus, int addr, int devad, u32 regnum,
12704e4aafcdSAndrew Lunn 		       u16 mask, u16 set)
12714e4aafcdSAndrew Lunn {
12724e4aafcdSAndrew Lunn 	int err;
12734e4aafcdSAndrew Lunn 
12744e4aafcdSAndrew Lunn 	mutex_lock(&bus->mdio_lock);
12754e4aafcdSAndrew Lunn 	err = __mdiobus_c45_modify_changed(bus, addr, devad, regnum,
12764e4aafcdSAndrew Lunn 					   mask, set);
12774e4aafcdSAndrew Lunn 	mutex_unlock(&bus->mdio_lock);
12784e4aafcdSAndrew Lunn 
12794e4aafcdSAndrew Lunn 	return err < 0 ? err : 0;
12804e4aafcdSAndrew Lunn }
12814e4aafcdSAndrew Lunn EXPORT_SYMBOL_GPL(mdiobus_c45_modify);
12824e4aafcdSAndrew Lunn 
12834e4aafcdSAndrew Lunn /**
128479365f36SRussell King (Oracle)  * mdiobus_modify_changed - Convenience function for modifying a given mdio
128579365f36SRussell King (Oracle)  *	device register and returning if it changed
128679365f36SRussell King (Oracle)  * @bus: the mii_bus struct
128779365f36SRussell King (Oracle)  * @addr: the phy address
128879365f36SRussell King (Oracle)  * @regnum: register number to write
128979365f36SRussell King (Oracle)  * @mask: bit mask of bits to clear
129079365f36SRussell King (Oracle)  * @set: bit mask of bits to set
129179365f36SRussell King (Oracle)  */
mdiobus_modify_changed(struct mii_bus * bus,int addr,u32 regnum,u16 mask,u16 set)129279365f36SRussell King (Oracle) int mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum,
129379365f36SRussell King (Oracle) 			   u16 mask, u16 set)
129479365f36SRussell King (Oracle) {
129579365f36SRussell King (Oracle) 	int err;
129679365f36SRussell King (Oracle) 
129779365f36SRussell King (Oracle) 	mutex_lock(&bus->mdio_lock);
129879365f36SRussell King (Oracle) 	err = __mdiobus_modify_changed(bus, addr, regnum, mask, set);
129979365f36SRussell King (Oracle) 	mutex_unlock(&bus->mdio_lock);
130079365f36SRussell King (Oracle) 
130179365f36SRussell King (Oracle) 	return err;
130279365f36SRussell King (Oracle) }
130379365f36SRussell King (Oracle) EXPORT_SYMBOL_GPL(mdiobus_modify_changed);
130479365f36SRussell King (Oracle) 
130579365f36SRussell King (Oracle) /**
13064e4aafcdSAndrew Lunn  * mdiobus_c45_modify_changed - Convenience function for modifying a given mdio
13074e4aafcdSAndrew Lunn  *	device register and returning if it changed
13084e4aafcdSAndrew Lunn  * @bus: the mii_bus struct
13094e4aafcdSAndrew Lunn  * @addr: the phy address
13104e4aafcdSAndrew Lunn  * @devad: device address to read
13114e4aafcdSAndrew Lunn  * @regnum: register number to write
13124e4aafcdSAndrew Lunn  * @mask: bit mask of bits to clear
13134e4aafcdSAndrew Lunn  * @set: bit mask of bits to set
13144e4aafcdSAndrew Lunn  */
mdiobus_c45_modify_changed(struct mii_bus * bus,int addr,int devad,u32 regnum,u16 mask,u16 set)1315408c0900SJiawen Wu int mdiobus_c45_modify_changed(struct mii_bus *bus, int addr, int devad,
13164e4aafcdSAndrew Lunn 			       u32 regnum, u16 mask, u16 set)
13174e4aafcdSAndrew Lunn {
13184e4aafcdSAndrew Lunn 	int err;
13194e4aafcdSAndrew Lunn 
13204e4aafcdSAndrew Lunn 	mutex_lock(&bus->mdio_lock);
13214e4aafcdSAndrew Lunn 	err = __mdiobus_c45_modify_changed(bus, addr, devad, regnum, mask, set);
13224e4aafcdSAndrew Lunn 	mutex_unlock(&bus->mdio_lock);
13234e4aafcdSAndrew Lunn 
13244e4aafcdSAndrew Lunn 	return err;
13254e4aafcdSAndrew Lunn }
13264e4aafcdSAndrew Lunn EXPORT_SYMBOL_GPL(mdiobus_c45_modify_changed);
13274e4aafcdSAndrew Lunn 
13284e4aafcdSAndrew Lunn /**
1329e76a4957SAndrew Lunn  * mdio_bus_match - determine if given MDIO driver supports the given
1330e76a4957SAndrew Lunn  *		    MDIO device
1331e76a4957SAndrew Lunn  * @dev: target MDIO device
1332e76a4957SAndrew Lunn  * @drv: given MDIO driver
133300db8189SAndy Fleming  *
1334e76a4957SAndrew Lunn  * Description: Given a MDIO device, and a MDIO driver, return 1 if
1335e76a4957SAndrew Lunn  *   the driver supports the device.  Otherwise, return 0. This may
1336e76a4957SAndrew Lunn  *   require calling the devices own match function, since different classes
1337e76a4957SAndrew Lunn  *   of MDIO devices have different match criteria.
133800db8189SAndy Fleming  */
mdio_bus_match(struct device * dev,struct device_driver * drv)133900db8189SAndy Fleming static int mdio_bus_match(struct device *dev, struct device_driver *drv)
134000db8189SAndy Fleming {
134194114d90SRussell King (Oracle) 	struct mdio_driver *mdiodrv = to_mdio_driver(drv);
1342e76a4957SAndrew Lunn 	struct mdio_device *mdio = to_mdio_device(dev);
134300db8189SAndy Fleming 
134494114d90SRussell King (Oracle) 	/* Both the driver and device must type-match */
134594114d90SRussell King (Oracle) 	if (!(mdiodrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY) !=
134694114d90SRussell King (Oracle) 	    !(mdio->flags & MDIO_DEVICE_FLAG_PHY))
134794114d90SRussell King (Oracle) 		return 0;
134894114d90SRussell King (Oracle) 
1349a30e2c18SDavid Daney 	if (of_driver_match_device(dev, drv))
1350a30e2c18SDavid Daney 		return 1;
1351a30e2c18SDavid Daney 
1352e76a4957SAndrew Lunn 	if (mdio->bus_match)
1353e76a4957SAndrew Lunn 		return mdio->bus_match(dev, drv);
1354a30e2c18SDavid Daney 
1355e0536cd9SShaohui Xie 	return 0;
1356e0536cd9SShaohui Xie }
135700db8189SAndy Fleming 
mdio_uevent(const struct device * dev,struct kobj_uevent_env * env)13582a81ada3SGreg Kroah-Hartman static int mdio_uevent(const struct device *dev, struct kobj_uevent_env *env)
13591b8f8694SRussell King {
13601b8f8694SRussell King 	int rc;
13611b8f8694SRussell King 
13621b8f8694SRussell King 	/* Some devices have extra OF data and an OF-style MODALIAS */
13631b8f8694SRussell King 	rc = of_device_uevent_modalias(dev, env);
13641b8f8694SRussell King 	if (rc != -ENODEV)
13651b8f8694SRussell King 		return rc;
13661b8f8694SRussell King 
13671b8f8694SRussell King 	return 0;
13681b8f8694SRussell King }
13691b8f8694SRussell King 
1370080bb352SFlorian Fainelli static struct attribute *mdio_bus_device_statistics_attrs[] = {
1371080bb352SFlorian Fainelli 	&dev_attr_mdio_bus_device_transfers.attr.attr,
1372080bb352SFlorian Fainelli 	&dev_attr_mdio_bus_device_errors.attr.attr,
1373080bb352SFlorian Fainelli 	&dev_attr_mdio_bus_device_writes.attr.attr,
1374080bb352SFlorian Fainelli 	&dev_attr_mdio_bus_device_reads.attr.attr,
1375080bb352SFlorian Fainelli 	NULL,
1376080bb352SFlorian Fainelli };
1377080bb352SFlorian Fainelli 
1378080bb352SFlorian Fainelli static const struct attribute_group mdio_bus_device_statistics_group = {
1379080bb352SFlorian Fainelli 	.name	= "statistics",
1380080bb352SFlorian Fainelli 	.attrs	= mdio_bus_device_statistics_attrs,
1381080bb352SFlorian Fainelli };
1382080bb352SFlorian Fainelli 
1383080bb352SFlorian Fainelli static const struct attribute_group *mdio_bus_dev_groups[] = {
1384080bb352SFlorian Fainelli 	&mdio_bus_device_statistics_group,
1385080bb352SFlorian Fainelli 	NULL,
1386080bb352SFlorian Fainelli };
1387080bb352SFlorian Fainelli 
138800db8189SAndy Fleming struct bus_type mdio_bus_type = {
138900db8189SAndy Fleming 	.name		= "mdio_bus",
1390080bb352SFlorian Fainelli 	.dev_groups	= mdio_bus_dev_groups,
139100db8189SAndy Fleming 	.match		= mdio_bus_match,
13921b8f8694SRussell King 	.uevent		= mdio_uevent,
139300db8189SAndy Fleming };
139411b0bacdSVitaly Bordug EXPORT_SYMBOL(mdio_bus_type);
139500db8189SAndy Fleming 
mdio_bus_init(void)139667c4f3faSJeff Garzik int __init mdio_bus_init(void)
139700db8189SAndy Fleming {
139846abc021SLennert Buytenhek 	int ret;
139946abc021SLennert Buytenhek 
140046abc021SLennert Buytenhek 	ret = class_register(&mdio_bus_class);
140146abc021SLennert Buytenhek 	if (!ret) {
140246abc021SLennert Buytenhek 		ret = bus_register(&mdio_bus_type);
140346abc021SLennert Buytenhek 		if (ret)
140446abc021SLennert Buytenhek 			class_unregister(&mdio_bus_class);
140546abc021SLennert Buytenhek 	}
140646abc021SLennert Buytenhek 
140746abc021SLennert Buytenhek 	return ret;
140800db8189SAndy Fleming }
140900db8189SAndy Fleming 
141090eff909SFlorian Fainelli #if IS_ENABLED(CONFIG_PHYLIB)
mdio_bus_exit(void)1411dc85dec6SPeter Chubb void mdio_bus_exit(void)
1412e1393456SAndy Fleming {
141346abc021SLennert Buytenhek 	class_unregister(&mdio_bus_class);
1414e1393456SAndy Fleming 	bus_unregister(&mdio_bus_type);
1415e1393456SAndy Fleming }
141690eff909SFlorian Fainelli EXPORT_SYMBOL_GPL(mdio_bus_exit);
141790eff909SFlorian Fainelli #else
141890eff909SFlorian Fainelli module_init(mdio_bus_init);
141990eff909SFlorian Fainelli /* no module_exit, intentional */
142090eff909SFlorian Fainelli MODULE_LICENSE("GPL");
142190eff909SFlorian Fainelli MODULE_DESCRIPTION("MDIO bus/device layer");
142290eff909SFlorian Fainelli #endif
1423