.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0+ |
---|
1 | 2 | /* MDIO Bus interface |
---|
2 | 3 | * |
---|
3 | 4 | * Author: Andy Fleming |
---|
4 | 5 | * |
---|
5 | 6 | * Copyright (c) 2004 Freescale Semiconductor, Inc. |
---|
6 | | - * |
---|
7 | | - * This program is free software; you can redistribute it and/or modify it |
---|
8 | | - * under the terms of the GNU General Public License as published by the |
---|
9 | | - * Free Software Foundation; either version 2 of the License, or (at your |
---|
10 | | - * option) any later version. |
---|
11 | | - * |
---|
12 | 7 | */ |
---|
13 | 8 | |
---|
14 | 9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
---|
15 | 10 | |
---|
16 | | -#include <linux/kernel.h> |
---|
17 | | -#include <linux/string.h> |
---|
18 | | -#include <linux/errno.h> |
---|
19 | | -#include <linux/unistd.h> |
---|
20 | | -#include <linux/slab.h> |
---|
21 | | -#include <linux/interrupt.h> |
---|
22 | | -#include <linux/init.h> |
---|
23 | 11 | #include <linux/delay.h> |
---|
24 | 12 | #include <linux/device.h> |
---|
| 13 | +#include <linux/errno.h> |
---|
| 14 | +#include <linux/etherdevice.h> |
---|
| 15 | +#include <linux/ethtool.h> |
---|
25 | 16 | #include <linux/gpio.h> |
---|
26 | 17 | #include <linux/gpio/consumer.h> |
---|
27 | | -#include <linux/of_device.h> |
---|
28 | | -#include <linux/of_mdio.h> |
---|
29 | | -#include <linux/of_gpio.h> |
---|
30 | | -#include <linux/netdevice.h> |
---|
31 | | -#include <linux/etherdevice.h> |
---|
32 | | -#include <linux/skbuff.h> |
---|
33 | | -#include <linux/spinlock.h> |
---|
| 18 | +#include <linux/init.h> |
---|
| 19 | +#include <linux/interrupt.h> |
---|
| 20 | +#include <linux/io.h> |
---|
| 21 | +#include <linux/kernel.h> |
---|
| 22 | +#include <linux/mii.h> |
---|
34 | 23 | #include <linux/mm.h> |
---|
35 | 24 | #include <linux/module.h> |
---|
36 | | -#include <linux/mii.h> |
---|
37 | | -#include <linux/ethtool.h> |
---|
| 25 | +#include <linux/netdevice.h> |
---|
| 26 | +#include <linux/of_device.h> |
---|
| 27 | +#include <linux/of_gpio.h> |
---|
| 28 | +#include <linux/of_mdio.h> |
---|
38 | 29 | #include <linux/phy.h> |
---|
39 | | -#include <linux/io.h> |
---|
| 30 | +#include <linux/reset.h> |
---|
| 31 | +#include <linux/skbuff.h> |
---|
| 32 | +#include <linux/slab.h> |
---|
| 33 | +#include <linux/spinlock.h> |
---|
| 34 | +#include <linux/string.h> |
---|
40 | 35 | #include <linux/uaccess.h> |
---|
41 | | -#include <linux/gpio/consumer.h> |
---|
42 | | - |
---|
43 | | -#include <asm/irq.h> |
---|
| 36 | +#include <linux/unistd.h> |
---|
44 | 37 | |
---|
45 | 38 | #define CREATE_TRACE_POINTS |
---|
46 | 39 | #include <trace/events/mdio.h> |
---|
.. | .. |
---|
49 | 42 | |
---|
50 | 43 | static int mdiobus_register_gpiod(struct mdio_device *mdiodev) |
---|
51 | 44 | { |
---|
52 | | - struct gpio_desc *gpiod = NULL; |
---|
53 | | - |
---|
54 | 45 | /* Deassert the optional reset signal */ |
---|
55 | | - if (mdiodev->dev.of_node) |
---|
56 | | - gpiod = fwnode_get_named_gpiod(&mdiodev->dev.of_node->fwnode, |
---|
57 | | - "reset-gpios", 0, GPIOD_OUT_LOW, |
---|
58 | | - "PHY reset"); |
---|
59 | | - if (IS_ERR(gpiod)) { |
---|
60 | | - if (PTR_ERR(gpiod) == -ENOENT || PTR_ERR(gpiod) == -ENOSYS) |
---|
61 | | - gpiod = NULL; |
---|
62 | | - else |
---|
63 | | - return PTR_ERR(gpiod); |
---|
64 | | - } |
---|
| 46 | + mdiodev->reset_gpio = gpiod_get_optional(&mdiodev->dev, |
---|
| 47 | + "reset", GPIOD_OUT_LOW); |
---|
| 48 | + if (IS_ERR(mdiodev->reset_gpio)) |
---|
| 49 | + return PTR_ERR(mdiodev->reset_gpio); |
---|
65 | 50 | |
---|
66 | | - mdiodev->reset = gpiod; |
---|
| 51 | + if (mdiodev->reset_gpio) |
---|
| 52 | + gpiod_set_consumer_name(mdiodev->reset_gpio, "PHY reset"); |
---|
67 | 53 | |
---|
68 | | - /* Assert the reset signal again */ |
---|
69 | | - mdio_device_reset(mdiodev, 1); |
---|
| 54 | + return 0; |
---|
| 55 | +} |
---|
| 56 | + |
---|
| 57 | +static int mdiobus_register_reset(struct mdio_device *mdiodev) |
---|
| 58 | +{ |
---|
| 59 | + struct reset_control *reset; |
---|
| 60 | + |
---|
| 61 | + reset = reset_control_get_optional_exclusive(&mdiodev->dev, "phy"); |
---|
| 62 | + if (IS_ERR(reset)) |
---|
| 63 | + return PTR_ERR(reset); |
---|
| 64 | + |
---|
| 65 | + mdiodev->reset_ctrl = reset; |
---|
70 | 66 | |
---|
71 | 67 | return 0; |
---|
72 | 68 | } |
---|
.. | .. |
---|
82 | 78 | err = mdiobus_register_gpiod(mdiodev); |
---|
83 | 79 | if (err) |
---|
84 | 80 | return err; |
---|
| 81 | + |
---|
| 82 | + err = mdiobus_register_reset(mdiodev); |
---|
| 83 | + if (err) |
---|
| 84 | + return err; |
---|
| 85 | + |
---|
| 86 | + /* Assert the reset signal */ |
---|
| 87 | + mdio_device_reset(mdiodev, 1); |
---|
85 | 88 | } |
---|
86 | 89 | |
---|
87 | 90 | mdiodev->bus->mdio_map[mdiodev->addr] = mdiodev; |
---|
.. | .. |
---|
95 | 98 | if (mdiodev->bus->mdio_map[mdiodev->addr] != mdiodev) |
---|
96 | 99 | return -EINVAL; |
---|
97 | 100 | |
---|
| 101 | + reset_control_put(mdiodev->reset_ctrl); |
---|
| 102 | + |
---|
98 | 103 | mdiodev->bus->mdio_map[mdiodev->addr] = NULL; |
---|
99 | 104 | |
---|
100 | 105 | return 0; |
---|
.. | .. |
---|
103 | 108 | |
---|
104 | 109 | struct phy_device *mdiobus_get_phy(struct mii_bus *bus, int addr) |
---|
105 | 110 | { |
---|
106 | | - struct mdio_device *mdiodev = bus->mdio_map[addr]; |
---|
| 111 | + struct mdio_device *mdiodev; |
---|
| 112 | + |
---|
| 113 | + if (addr < 0 || addr >= ARRAY_SIZE(bus->mdio_map)) |
---|
| 114 | + return NULL; |
---|
| 115 | + |
---|
| 116 | + mdiodev = bus->mdio_map[addr]; |
---|
107 | 117 | |
---|
108 | 118 | if (!mdiodev) |
---|
109 | 119 | return NULL; |
---|
.. | .. |
---|
150 | 160 | if (size) |
---|
151 | 161 | bus->priv = (void *)bus + aligned_size; |
---|
152 | 162 | |
---|
153 | | - /* Initialise the interrupts to polling */ |
---|
154 | | - for (i = 0; i < PHY_MAX_ADDR; i++) |
---|
| 163 | + /* Initialise the interrupts to polling and 64-bit seqcounts */ |
---|
| 164 | + for (i = 0; i < PHY_MAX_ADDR; i++) { |
---|
155 | 165 | bus->irq[i] = PHY_POLL; |
---|
156 | | - |
---|
157 | | - return bus; |
---|
158 | | -} |
---|
159 | | -EXPORT_SYMBOL(mdiobus_alloc_size); |
---|
160 | | - |
---|
161 | | -static void _devm_mdiobus_free(struct device *dev, void *res) |
---|
162 | | -{ |
---|
163 | | - mdiobus_free(*(struct mii_bus **)res); |
---|
164 | | -} |
---|
165 | | - |
---|
166 | | -static int devm_mdiobus_match(struct device *dev, void *res, void *data) |
---|
167 | | -{ |
---|
168 | | - struct mii_bus **r = res; |
---|
169 | | - |
---|
170 | | - if (WARN_ON(!r || !*r)) |
---|
171 | | - return 0; |
---|
172 | | - |
---|
173 | | - return *r == data; |
---|
174 | | -} |
---|
175 | | - |
---|
176 | | -/** |
---|
177 | | - * devm_mdiobus_alloc_size - Resource-managed mdiobus_alloc_size() |
---|
178 | | - * @dev: Device to allocate mii_bus for |
---|
179 | | - * @sizeof_priv: Space to allocate for private structure. |
---|
180 | | - * |
---|
181 | | - * Managed mdiobus_alloc_size. mii_bus allocated with this function is |
---|
182 | | - * automatically freed on driver detach. |
---|
183 | | - * |
---|
184 | | - * If an mii_bus allocated with this function needs to be freed separately, |
---|
185 | | - * devm_mdiobus_free() must be used. |
---|
186 | | - * |
---|
187 | | - * RETURNS: |
---|
188 | | - * Pointer to allocated mii_bus on success, NULL on failure. |
---|
189 | | - */ |
---|
190 | | -struct mii_bus *devm_mdiobus_alloc_size(struct device *dev, int sizeof_priv) |
---|
191 | | -{ |
---|
192 | | - struct mii_bus **ptr, *bus; |
---|
193 | | - |
---|
194 | | - ptr = devres_alloc(_devm_mdiobus_free, sizeof(*ptr), GFP_KERNEL); |
---|
195 | | - if (!ptr) |
---|
196 | | - return NULL; |
---|
197 | | - |
---|
198 | | - /* use raw alloc_dr for kmalloc caller tracing */ |
---|
199 | | - bus = mdiobus_alloc_size(sizeof_priv); |
---|
200 | | - if (bus) { |
---|
201 | | - *ptr = bus; |
---|
202 | | - devres_add(dev, ptr); |
---|
203 | | - } else { |
---|
204 | | - devres_free(ptr); |
---|
| 166 | + u64_stats_init(&bus->stats[i].syncp); |
---|
205 | 167 | } |
---|
206 | 168 | |
---|
207 | 169 | return bus; |
---|
208 | 170 | } |
---|
209 | | -EXPORT_SYMBOL_GPL(devm_mdiobus_alloc_size); |
---|
210 | | - |
---|
211 | | -/** |
---|
212 | | - * devm_mdiobus_free - Resource-managed mdiobus_free() |
---|
213 | | - * @dev: Device this mii_bus belongs to |
---|
214 | | - * @bus: the mii_bus associated with the device |
---|
215 | | - * |
---|
216 | | - * Free mii_bus allocated with devm_mdiobus_alloc_size(). |
---|
217 | | - */ |
---|
218 | | -void devm_mdiobus_free(struct device *dev, struct mii_bus *bus) |
---|
219 | | -{ |
---|
220 | | - int rc; |
---|
221 | | - |
---|
222 | | - rc = devres_release(dev, _devm_mdiobus_free, |
---|
223 | | - devm_mdiobus_match, bus); |
---|
224 | | - WARN_ON(rc); |
---|
225 | | -} |
---|
226 | | -EXPORT_SYMBOL_GPL(devm_mdiobus_free); |
---|
| 171 | +EXPORT_SYMBOL(mdiobus_alloc_size); |
---|
227 | 172 | |
---|
228 | 173 | /** |
---|
229 | 174 | * mdiobus_release - mii_bus device release callback |
---|
.. | .. |
---|
241 | 186 | kfree(bus); |
---|
242 | 187 | } |
---|
243 | 188 | |
---|
| 189 | +struct mdio_bus_stat_attr { |
---|
| 190 | + int addr; |
---|
| 191 | + unsigned int field_offset; |
---|
| 192 | +}; |
---|
| 193 | + |
---|
| 194 | +static u64 mdio_bus_get_stat(struct mdio_bus_stats *s, unsigned int offset) |
---|
| 195 | +{ |
---|
| 196 | + const char *p = (const char *)s + offset; |
---|
| 197 | + unsigned int start; |
---|
| 198 | + u64 val = 0; |
---|
| 199 | + |
---|
| 200 | + do { |
---|
| 201 | + start = u64_stats_fetch_begin(&s->syncp); |
---|
| 202 | + val = u64_stats_read((const u64_stats_t *)p); |
---|
| 203 | + } while (u64_stats_fetch_retry(&s->syncp, start)); |
---|
| 204 | + |
---|
| 205 | + return val; |
---|
| 206 | +} |
---|
| 207 | + |
---|
| 208 | +static u64 mdio_bus_get_global_stat(struct mii_bus *bus, unsigned int offset) |
---|
| 209 | +{ |
---|
| 210 | + unsigned int i; |
---|
| 211 | + u64 val = 0; |
---|
| 212 | + |
---|
| 213 | + for (i = 0; i < PHY_MAX_ADDR; i++) |
---|
| 214 | + val += mdio_bus_get_stat(&bus->stats[i], offset); |
---|
| 215 | + |
---|
| 216 | + return val; |
---|
| 217 | +} |
---|
| 218 | + |
---|
| 219 | +static ssize_t mdio_bus_stat_field_show(struct device *dev, |
---|
| 220 | + struct device_attribute *attr, |
---|
| 221 | + char *buf) |
---|
| 222 | +{ |
---|
| 223 | + struct mii_bus *bus = to_mii_bus(dev); |
---|
| 224 | + struct mdio_bus_stat_attr *sattr; |
---|
| 225 | + struct dev_ext_attribute *eattr; |
---|
| 226 | + u64 val; |
---|
| 227 | + |
---|
| 228 | + eattr = container_of(attr, struct dev_ext_attribute, attr); |
---|
| 229 | + sattr = eattr->var; |
---|
| 230 | + |
---|
| 231 | + if (sattr->addr < 0) |
---|
| 232 | + val = mdio_bus_get_global_stat(bus, sattr->field_offset); |
---|
| 233 | + else |
---|
| 234 | + val = mdio_bus_get_stat(&bus->stats[sattr->addr], |
---|
| 235 | + sattr->field_offset); |
---|
| 236 | + |
---|
| 237 | + return sprintf(buf, "%llu\n", val); |
---|
| 238 | +} |
---|
| 239 | + |
---|
| 240 | +static ssize_t mdio_bus_device_stat_field_show(struct device *dev, |
---|
| 241 | + struct device_attribute *attr, |
---|
| 242 | + char *buf) |
---|
| 243 | +{ |
---|
| 244 | + struct mdio_device *mdiodev = to_mdio_device(dev); |
---|
| 245 | + struct mii_bus *bus = mdiodev->bus; |
---|
| 246 | + struct mdio_bus_stat_attr *sattr; |
---|
| 247 | + struct dev_ext_attribute *eattr; |
---|
| 248 | + int addr = mdiodev->addr; |
---|
| 249 | + u64 val; |
---|
| 250 | + |
---|
| 251 | + eattr = container_of(attr, struct dev_ext_attribute, attr); |
---|
| 252 | + sattr = eattr->var; |
---|
| 253 | + |
---|
| 254 | + val = mdio_bus_get_stat(&bus->stats[addr], sattr->field_offset); |
---|
| 255 | + |
---|
| 256 | + return sprintf(buf, "%llu\n", val); |
---|
| 257 | +} |
---|
| 258 | + |
---|
| 259 | +#define MDIO_BUS_STATS_ATTR_DECL(field, file) \ |
---|
| 260 | +static struct dev_ext_attribute dev_attr_mdio_bus_##field = { \ |
---|
| 261 | + .attr = { .attr = { .name = file, .mode = 0444 }, \ |
---|
| 262 | + .show = mdio_bus_stat_field_show, \ |
---|
| 263 | + }, \ |
---|
| 264 | + .var = &((struct mdio_bus_stat_attr) { \ |
---|
| 265 | + -1, offsetof(struct mdio_bus_stats, field) \ |
---|
| 266 | + }), \ |
---|
| 267 | +}; \ |
---|
| 268 | +static struct dev_ext_attribute dev_attr_mdio_bus_device_##field = { \ |
---|
| 269 | + .attr = { .attr = { .name = file, .mode = 0444 }, \ |
---|
| 270 | + .show = mdio_bus_device_stat_field_show, \ |
---|
| 271 | + }, \ |
---|
| 272 | + .var = &((struct mdio_bus_stat_attr) { \ |
---|
| 273 | + -1, offsetof(struct mdio_bus_stats, field) \ |
---|
| 274 | + }), \ |
---|
| 275 | +}; |
---|
| 276 | + |
---|
| 277 | +#define MDIO_BUS_STATS_ATTR(field) \ |
---|
| 278 | + MDIO_BUS_STATS_ATTR_DECL(field, __stringify(field)) |
---|
| 279 | + |
---|
| 280 | +MDIO_BUS_STATS_ATTR(transfers); |
---|
| 281 | +MDIO_BUS_STATS_ATTR(errors); |
---|
| 282 | +MDIO_BUS_STATS_ATTR(writes); |
---|
| 283 | +MDIO_BUS_STATS_ATTR(reads); |
---|
| 284 | + |
---|
| 285 | +#define MDIO_BUS_STATS_ADDR_ATTR_DECL(field, addr, file) \ |
---|
| 286 | +static struct dev_ext_attribute dev_attr_mdio_bus_addr_##field##_##addr = { \ |
---|
| 287 | + .attr = { .attr = { .name = file, .mode = 0444 }, \ |
---|
| 288 | + .show = mdio_bus_stat_field_show, \ |
---|
| 289 | + }, \ |
---|
| 290 | + .var = &((struct mdio_bus_stat_attr) { \ |
---|
| 291 | + addr, offsetof(struct mdio_bus_stats, field) \ |
---|
| 292 | + }), \ |
---|
| 293 | +} |
---|
| 294 | + |
---|
| 295 | +#define MDIO_BUS_STATS_ADDR_ATTR(field, addr) \ |
---|
| 296 | + MDIO_BUS_STATS_ADDR_ATTR_DECL(field, addr, \ |
---|
| 297 | + __stringify(field) "_" __stringify(addr)) |
---|
| 298 | + |
---|
| 299 | +#define MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(addr) \ |
---|
| 300 | + MDIO_BUS_STATS_ADDR_ATTR(transfers, addr); \ |
---|
| 301 | + MDIO_BUS_STATS_ADDR_ATTR(errors, addr); \ |
---|
| 302 | + MDIO_BUS_STATS_ADDR_ATTR(writes, addr); \ |
---|
| 303 | + MDIO_BUS_STATS_ADDR_ATTR(reads, addr) \ |
---|
| 304 | + |
---|
| 305 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(0); |
---|
| 306 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(1); |
---|
| 307 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(2); |
---|
| 308 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(3); |
---|
| 309 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(4); |
---|
| 310 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(5); |
---|
| 311 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(6); |
---|
| 312 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(7); |
---|
| 313 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(8); |
---|
| 314 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(9); |
---|
| 315 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(10); |
---|
| 316 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(11); |
---|
| 317 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(12); |
---|
| 318 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(13); |
---|
| 319 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(14); |
---|
| 320 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(15); |
---|
| 321 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(16); |
---|
| 322 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(17); |
---|
| 323 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(18); |
---|
| 324 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(19); |
---|
| 325 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(20); |
---|
| 326 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(21); |
---|
| 327 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(22); |
---|
| 328 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(23); |
---|
| 329 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(24); |
---|
| 330 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(25); |
---|
| 331 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(26); |
---|
| 332 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(27); |
---|
| 333 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(28); |
---|
| 334 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(29); |
---|
| 335 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(30); |
---|
| 336 | +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(31); |
---|
| 337 | + |
---|
| 338 | +#define MDIO_BUS_STATS_ADDR_ATTR_GROUP(addr) \ |
---|
| 339 | + &dev_attr_mdio_bus_addr_transfers_##addr.attr.attr, \ |
---|
| 340 | + &dev_attr_mdio_bus_addr_errors_##addr.attr.attr, \ |
---|
| 341 | + &dev_attr_mdio_bus_addr_writes_##addr.attr.attr, \ |
---|
| 342 | + &dev_attr_mdio_bus_addr_reads_##addr.attr.attr \ |
---|
| 343 | + |
---|
| 344 | +static struct attribute *mdio_bus_statistics_attrs[] = { |
---|
| 345 | + &dev_attr_mdio_bus_transfers.attr.attr, |
---|
| 346 | + &dev_attr_mdio_bus_errors.attr.attr, |
---|
| 347 | + &dev_attr_mdio_bus_writes.attr.attr, |
---|
| 348 | + &dev_attr_mdio_bus_reads.attr.attr, |
---|
| 349 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(0), |
---|
| 350 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(1), |
---|
| 351 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(2), |
---|
| 352 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(3), |
---|
| 353 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(4), |
---|
| 354 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(5), |
---|
| 355 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(6), |
---|
| 356 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(7), |
---|
| 357 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(8), |
---|
| 358 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(9), |
---|
| 359 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(10), |
---|
| 360 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(11), |
---|
| 361 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(12), |
---|
| 362 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(13), |
---|
| 363 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(14), |
---|
| 364 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(15), |
---|
| 365 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(16), |
---|
| 366 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(17), |
---|
| 367 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(18), |
---|
| 368 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(19), |
---|
| 369 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(20), |
---|
| 370 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(21), |
---|
| 371 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(22), |
---|
| 372 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(23), |
---|
| 373 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(24), |
---|
| 374 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(25), |
---|
| 375 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(26), |
---|
| 376 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(27), |
---|
| 377 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(28), |
---|
| 378 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(29), |
---|
| 379 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(30), |
---|
| 380 | + MDIO_BUS_STATS_ADDR_ATTR_GROUP(31), |
---|
| 381 | + NULL, |
---|
| 382 | +}; |
---|
| 383 | + |
---|
| 384 | +static const struct attribute_group mdio_bus_statistics_group = { |
---|
| 385 | + .name = "statistics", |
---|
| 386 | + .attrs = mdio_bus_statistics_attrs, |
---|
| 387 | +}; |
---|
| 388 | + |
---|
| 389 | +static const struct attribute_group *mdio_bus_groups[] = { |
---|
| 390 | + &mdio_bus_statistics_group, |
---|
| 391 | + NULL, |
---|
| 392 | +}; |
---|
| 393 | + |
---|
244 | 394 | static struct class mdio_bus_class = { |
---|
245 | 395 | .name = "mdio_bus", |
---|
246 | 396 | .dev_release = mdiobus_release, |
---|
| 397 | + .dev_groups = mdio_bus_groups, |
---|
247 | 398 | }; |
---|
248 | 399 | |
---|
249 | | -#if IS_ENABLED(CONFIG_OF_MDIO) |
---|
250 | | -/* Helper function for of_mdio_find_bus */ |
---|
251 | | -static int of_mdio_bus_match(struct device *dev, const void *mdio_bus_np) |
---|
| 400 | +/** |
---|
| 401 | + * mdio_find_bus - Given the name of a mdiobus, find the mii_bus. |
---|
| 402 | + * @mdio_name: The name of a mdiobus. |
---|
| 403 | + * |
---|
| 404 | + * Returns a reference to the mii_bus, or NULL if none found. The |
---|
| 405 | + * embedded struct device will have its reference count incremented, |
---|
| 406 | + * and this must be put_deviced'ed once the bus is finished with. |
---|
| 407 | + */ |
---|
| 408 | +struct mii_bus *mdio_find_bus(const char *mdio_name) |
---|
252 | 409 | { |
---|
253 | | - return dev->of_node == mdio_bus_np; |
---|
| 410 | + struct device *d; |
---|
| 411 | + |
---|
| 412 | + d = class_find_device_by_name(&mdio_bus_class, mdio_name); |
---|
| 413 | + return d ? to_mii_bus(d) : NULL; |
---|
254 | 414 | } |
---|
| 415 | +EXPORT_SYMBOL(mdio_find_bus); |
---|
| 416 | + |
---|
| 417 | +#if IS_ENABLED(CONFIG_OF_MDIO) |
---|
255 | 418 | /** |
---|
256 | 419 | * of_mdio_find_bus - Given an mii_bus node, find the mii_bus. |
---|
257 | 420 | * @mdio_bus_np: Pointer to the mii_bus. |
---|
.. | .. |
---|
272 | 435 | if (!mdio_bus_np) |
---|
273 | 436 | return NULL; |
---|
274 | 437 | |
---|
275 | | - d = class_find_device(&mdio_bus_class, NULL, mdio_bus_np, |
---|
276 | | - of_mdio_bus_match); |
---|
277 | | - |
---|
| 438 | + d = class_find_device_by_of_node(&mdio_bus_class, mdio_bus_np); |
---|
278 | 439 | return d ? to_mii_bus(d) : NULL; |
---|
279 | 440 | } |
---|
280 | 441 | EXPORT_SYMBOL(of_mdio_find_bus); |
---|
.. | .. |
---|
392 | 553 | } |
---|
393 | 554 | |
---|
394 | 555 | mutex_init(&bus->mdio_lock); |
---|
| 556 | + mutex_init(&bus->shared_lock); |
---|
395 | 557 | |
---|
396 | 558 | /* de-assert bus level PHY GPIO reset */ |
---|
397 | 559 | gpiod = devm_gpiod_get_optional(&bus->dev, "reset", GPIOD_OUT_LOW); |
---|
.. | .. |
---|
404 | 566 | bus->reset_gpiod = gpiod; |
---|
405 | 567 | |
---|
406 | 568 | gpiod_set_value_cansleep(gpiod, 1); |
---|
407 | | - udelay(bus->reset_delay_us); |
---|
| 569 | + fsleep(bus->reset_delay_us); |
---|
408 | 570 | gpiod_set_value_cansleep(gpiod, 0); |
---|
| 571 | + if (bus->reset_post_delay_us > 0) |
---|
| 572 | + fsleep(bus->reset_post_delay_us); |
---|
409 | 573 | } |
---|
410 | 574 | |
---|
411 | | - if (bus->reset) |
---|
412 | | - bus->reset(bus); |
---|
| 575 | + if (bus->reset) { |
---|
| 576 | + err = bus->reset(bus); |
---|
| 577 | + if (err) |
---|
| 578 | + goto error_reset_gpiod; |
---|
| 579 | + } |
---|
413 | 580 | |
---|
414 | 581 | for (i = 0; i < PHY_MAX_ADDR; i++) { |
---|
415 | | - if ((bus->phy_mask & (1 << i)) == 0) { |
---|
| 582 | + if ((bus->phy_mask & BIT(i)) == 0) { |
---|
416 | 583 | struct phy_device *phydev; |
---|
417 | 584 | |
---|
418 | 585 | phydev = mdiobus_scan(bus, i); |
---|
.. | .. |
---|
438 | 605 | mdiodev->device_remove(mdiodev); |
---|
439 | 606 | mdiodev->device_free(mdiodev); |
---|
440 | 607 | } |
---|
441 | | - |
---|
| 608 | +error_reset_gpiod: |
---|
442 | 609 | /* Put PHYs in RESET to save power */ |
---|
443 | 610 | if (bus->reset_gpiod) |
---|
444 | 611 | gpiod_set_value_cansleep(bus->reset_gpiod, 1); |
---|
.. | .. |
---|
462 | 629 | if (!mdiodev) |
---|
463 | 630 | continue; |
---|
464 | 631 | |
---|
465 | | - if (mdiodev->reset) |
---|
466 | | - gpiod_put(mdiodev->reset); |
---|
| 632 | + if (mdiodev->reset_gpio) |
---|
| 633 | + gpiod_put(mdiodev->reset_gpio); |
---|
467 | 634 | |
---|
468 | 635 | mdiodev->device_remove(mdiodev); |
---|
469 | 636 | mdiodev->device_free(mdiodev); |
---|
.. | .. |
---|
514 | 681 | */ |
---|
515 | 682 | struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr) |
---|
516 | 683 | { |
---|
517 | | - struct phy_device *phydev; |
---|
| 684 | + struct phy_device *phydev = ERR_PTR(-ENODEV); |
---|
518 | 685 | int err; |
---|
519 | 686 | |
---|
520 | | - phydev = get_phy_device(bus, addr, false); |
---|
| 687 | + switch (bus->probe_capabilities) { |
---|
| 688 | + case MDIOBUS_NO_CAP: |
---|
| 689 | + case MDIOBUS_C22: |
---|
| 690 | + phydev = get_phy_device(bus, addr, false); |
---|
| 691 | + break; |
---|
| 692 | + case MDIOBUS_C45: |
---|
| 693 | + phydev = get_phy_device(bus, addr, true); |
---|
| 694 | + break; |
---|
| 695 | + case MDIOBUS_C22_C45: |
---|
| 696 | + phydev = get_phy_device(bus, addr, false); |
---|
| 697 | + if (IS_ERR(phydev)) |
---|
| 698 | + phydev = get_phy_device(bus, addr, true); |
---|
| 699 | + break; |
---|
| 700 | + } |
---|
| 701 | + |
---|
521 | 702 | if (IS_ERR(phydev)) |
---|
522 | 703 | return phydev; |
---|
523 | 704 | |
---|
.. | .. |
---|
536 | 717 | return phydev; |
---|
537 | 718 | } |
---|
538 | 719 | EXPORT_SYMBOL(mdiobus_scan); |
---|
| 720 | + |
---|
| 721 | +static void mdiobus_stats_acct(struct mdio_bus_stats *stats, bool op, int ret) |
---|
| 722 | +{ |
---|
| 723 | + preempt_disable(); |
---|
| 724 | + u64_stats_update_begin(&stats->syncp); |
---|
| 725 | + |
---|
| 726 | + u64_stats_inc(&stats->transfers); |
---|
| 727 | + if (ret < 0) { |
---|
| 728 | + u64_stats_inc(&stats->errors); |
---|
| 729 | + goto out; |
---|
| 730 | + } |
---|
| 731 | + |
---|
| 732 | + if (op) |
---|
| 733 | + u64_stats_inc(&stats->reads); |
---|
| 734 | + else |
---|
| 735 | + u64_stats_inc(&stats->writes); |
---|
| 736 | +out: |
---|
| 737 | + u64_stats_update_end(&stats->syncp); |
---|
| 738 | + preempt_enable(); |
---|
| 739 | +} |
---|
539 | 740 | |
---|
540 | 741 | /** |
---|
541 | 742 | * __mdiobus_read - Unlocked version of the mdiobus_read function |
---|
.. | .. |
---|
556 | 757 | retval = bus->read(bus, addr, regnum); |
---|
557 | 758 | |
---|
558 | 759 | trace_mdio_access(bus, 1, addr, regnum, retval, retval); |
---|
| 760 | + mdiobus_stats_acct(&bus->stats[addr], true, retval); |
---|
559 | 761 | |
---|
560 | 762 | return retval; |
---|
561 | 763 | } |
---|
.. | .. |
---|
581 | 783 | err = bus->write(bus, addr, regnum, val); |
---|
582 | 784 | |
---|
583 | 785 | trace_mdio_access(bus, 0, addr, regnum, val, err); |
---|
| 786 | + mdiobus_stats_acct(&bus->stats[addr], false, err); |
---|
584 | 787 | |
---|
585 | 788 | return err; |
---|
586 | 789 | } |
---|
587 | 790 | EXPORT_SYMBOL(__mdiobus_write); |
---|
| 791 | + |
---|
| 792 | +/** |
---|
| 793 | + * __mdiobus_modify_changed - Unlocked version of the mdiobus_modify function |
---|
| 794 | + * @bus: the mii_bus struct |
---|
| 795 | + * @addr: the phy address |
---|
| 796 | + * @regnum: register number to modify |
---|
| 797 | + * @mask: bit mask of bits to clear |
---|
| 798 | + * @set: bit mask of bits to set |
---|
| 799 | + * |
---|
| 800 | + * Read, modify, and if any change, write the register value back to the |
---|
| 801 | + * device. Any error returns a negative number. |
---|
| 802 | + * |
---|
| 803 | + * NOTE: MUST NOT be called from interrupt context. |
---|
| 804 | + */ |
---|
| 805 | +int __mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum, |
---|
| 806 | + u16 mask, u16 set) |
---|
| 807 | +{ |
---|
| 808 | + int new, ret; |
---|
| 809 | + |
---|
| 810 | + ret = __mdiobus_read(bus, addr, regnum); |
---|
| 811 | + if (ret < 0) |
---|
| 812 | + return ret; |
---|
| 813 | + |
---|
| 814 | + new = (ret & ~mask) | set; |
---|
| 815 | + if (new == ret) |
---|
| 816 | + return 0; |
---|
| 817 | + |
---|
| 818 | + ret = __mdiobus_write(bus, addr, regnum, new); |
---|
| 819 | + |
---|
| 820 | + return ret < 0 ? ret : 1; |
---|
| 821 | +} |
---|
| 822 | +EXPORT_SYMBOL_GPL(__mdiobus_modify_changed); |
---|
588 | 823 | |
---|
589 | 824 | /** |
---|
590 | 825 | * mdiobus_read_nested - Nested version of the mdiobus_read function |
---|
.. | .. |
---|
602 | 837 | int mdiobus_read_nested(struct mii_bus *bus, int addr, u32 regnum) |
---|
603 | 838 | { |
---|
604 | 839 | int retval; |
---|
605 | | - |
---|
606 | | - BUG_ON(in_interrupt()); |
---|
607 | 840 | |
---|
608 | 841 | mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); |
---|
609 | 842 | retval = __mdiobus_read(bus, addr, regnum); |
---|
.. | .. |
---|
626 | 859 | int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum) |
---|
627 | 860 | { |
---|
628 | 861 | int retval; |
---|
629 | | - |
---|
630 | | - BUG_ON(in_interrupt()); |
---|
631 | 862 | |
---|
632 | 863 | mutex_lock(&bus->mdio_lock); |
---|
633 | 864 | retval = __mdiobus_read(bus, addr, regnum); |
---|
.. | .. |
---|
655 | 886 | { |
---|
656 | 887 | int err; |
---|
657 | 888 | |
---|
658 | | - BUG_ON(in_interrupt()); |
---|
659 | | - |
---|
660 | 889 | mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); |
---|
661 | 890 | err = __mdiobus_write(bus, addr, regnum, val); |
---|
662 | 891 | mutex_unlock(&bus->mdio_lock); |
---|
.. | .. |
---|
680 | 909 | { |
---|
681 | 910 | int err; |
---|
682 | 911 | |
---|
683 | | - BUG_ON(in_interrupt()); |
---|
684 | | - |
---|
685 | 912 | mutex_lock(&bus->mdio_lock); |
---|
686 | 913 | err = __mdiobus_write(bus, addr, regnum, val); |
---|
687 | 914 | mutex_unlock(&bus->mdio_lock); |
---|
.. | .. |
---|
689 | 916 | return err; |
---|
690 | 917 | } |
---|
691 | 918 | EXPORT_SYMBOL(mdiobus_write); |
---|
| 919 | + |
---|
| 920 | +/** |
---|
| 921 | + * mdiobus_modify - Convenience function for modifying a given mdio device |
---|
| 922 | + * register |
---|
| 923 | + * @bus: the mii_bus struct |
---|
| 924 | + * @addr: the phy address |
---|
| 925 | + * @regnum: register number to write |
---|
| 926 | + * @mask: bit mask of bits to clear |
---|
| 927 | + * @set: bit mask of bits to set |
---|
| 928 | + */ |
---|
| 929 | +int mdiobus_modify(struct mii_bus *bus, int addr, u32 regnum, u16 mask, u16 set) |
---|
| 930 | +{ |
---|
| 931 | + int err; |
---|
| 932 | + |
---|
| 933 | + mutex_lock(&bus->mdio_lock); |
---|
| 934 | + err = __mdiobus_modify_changed(bus, addr, regnum, mask, set); |
---|
| 935 | + mutex_unlock(&bus->mdio_lock); |
---|
| 936 | + |
---|
| 937 | + return err < 0 ? err : 0; |
---|
| 938 | +} |
---|
| 939 | +EXPORT_SYMBOL_GPL(mdiobus_modify); |
---|
692 | 940 | |
---|
693 | 941 | /** |
---|
694 | 942 | * mdio_bus_match - determine if given MDIO driver supports the given |
---|
.. | .. |
---|
726 | 974 | return 0; |
---|
727 | 975 | } |
---|
728 | 976 | |
---|
| 977 | +static struct attribute *mdio_bus_device_statistics_attrs[] = { |
---|
| 978 | + &dev_attr_mdio_bus_device_transfers.attr.attr, |
---|
| 979 | + &dev_attr_mdio_bus_device_errors.attr.attr, |
---|
| 980 | + &dev_attr_mdio_bus_device_writes.attr.attr, |
---|
| 981 | + &dev_attr_mdio_bus_device_reads.attr.attr, |
---|
| 982 | + NULL, |
---|
| 983 | +}; |
---|
| 984 | + |
---|
| 985 | +static const struct attribute_group mdio_bus_device_statistics_group = { |
---|
| 986 | + .name = "statistics", |
---|
| 987 | + .attrs = mdio_bus_device_statistics_attrs, |
---|
| 988 | +}; |
---|
| 989 | + |
---|
| 990 | +static const struct attribute_group *mdio_bus_dev_groups[] = { |
---|
| 991 | + &mdio_bus_device_statistics_group, |
---|
| 992 | + NULL, |
---|
| 993 | +}; |
---|
| 994 | + |
---|
729 | 995 | struct bus_type mdio_bus_type = { |
---|
730 | 996 | .name = "mdio_bus", |
---|
| 997 | + .dev_groups = mdio_bus_dev_groups, |
---|
731 | 998 | .match = mdio_bus_match, |
---|
732 | 999 | .uevent = mdio_uevent, |
---|
733 | 1000 | }; |
---|
.. | .. |
---|
746 | 1013 | |
---|
747 | 1014 | return ret; |
---|
748 | 1015 | } |
---|
749 | | -EXPORT_SYMBOL_GPL(mdio_bus_init); |
---|
750 | 1016 | |
---|
751 | 1017 | #if IS_ENABLED(CONFIG_PHYLIB) |
---|
752 | 1018 | void mdio_bus_exit(void) |
---|