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