| 1 | // SPDX-License-Identifier: GPL-2.0+ | 
|---|
| 2 | /* MDIO Bus interface | 
|---|
| 3 | * | 
|---|
| 4 | * Author: Andy Fleming | 
|---|
| 5 | * | 
|---|
| 6 | * Copyright (c) 2004 Freescale Semiconductor, Inc. | 
|---|
| 7 | */ | 
|---|
| 8 |  | 
|---|
| 9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 
|---|
| 10 |  | 
|---|
| 11 | #include <linux/device.h> | 
|---|
| 12 | #include <linux/errno.h> | 
|---|
| 13 | #include <linux/etherdevice.h> | 
|---|
| 14 | #include <linux/ethtool.h> | 
|---|
| 15 | #include <linux/gpio/consumer.h> | 
|---|
| 16 | #include <linux/init.h> | 
|---|
| 17 | #include <linux/io.h> | 
|---|
| 18 | #include <linux/kernel.h> | 
|---|
| 19 | #include <linux/mii.h> | 
|---|
| 20 | #include <linux/mm.h> | 
|---|
| 21 | #include <linux/module.h> | 
|---|
| 22 | #include <linux/netdevice.h> | 
|---|
| 23 | #include <linux/of_device.h> | 
|---|
| 24 | #include <linux/of_mdio.h> | 
|---|
| 25 | #include <linux/phy.h> | 
|---|
| 26 | #include <linux/reset.h> | 
|---|
| 27 | #include <linux/slab.h> | 
|---|
| 28 | #include <linux/spinlock.h> | 
|---|
| 29 | #include <linux/string.h> | 
|---|
| 30 | #include <linux/uaccess.h> | 
|---|
| 31 | #include <linux/unistd.h> | 
|---|
| 32 |  | 
|---|
| 33 | #define CREATE_TRACE_POINTS | 
|---|
| 34 | #include <trace/events/mdio.h> | 
|---|
| 35 |  | 
|---|
| 36 | static int mdiobus_register_gpiod(struct mdio_device *mdiodev) | 
|---|
| 37 | { | 
|---|
| 38 | /* Deassert the optional reset signal */ | 
|---|
| 39 | mdiodev->reset_gpio = gpiod_get_optional(dev: &mdiodev->dev, | 
|---|
| 40 | con_id: "reset", flags: GPIOD_OUT_LOW); | 
|---|
| 41 | if (IS_ERR(ptr: mdiodev->reset_gpio)) | 
|---|
| 42 | return PTR_ERR(ptr: mdiodev->reset_gpio); | 
|---|
| 43 |  | 
|---|
| 44 | if (mdiodev->reset_gpio) | 
|---|
| 45 | gpiod_set_consumer_name(desc: mdiodev->reset_gpio, name: "PHY reset"); | 
|---|
| 46 |  | 
|---|
| 47 | return 0; | 
|---|
| 48 | } | 
|---|
| 49 |  | 
|---|
| 50 | static int mdiobus_register_reset(struct mdio_device *mdiodev) | 
|---|
| 51 | { | 
|---|
| 52 | struct reset_control *reset; | 
|---|
| 53 |  | 
|---|
| 54 | reset = reset_control_get_optional_exclusive(dev: &mdiodev->dev, id: "phy"); | 
|---|
| 55 | if (IS_ERR(ptr: reset)) | 
|---|
| 56 | return PTR_ERR(ptr: reset); | 
|---|
| 57 |  | 
|---|
| 58 | mdiodev->reset_ctrl = reset; | 
|---|
| 59 |  | 
|---|
| 60 | return 0; | 
|---|
| 61 | } | 
|---|
| 62 |  | 
|---|
| 63 | int mdiobus_register_device(struct mdio_device *mdiodev) | 
|---|
| 64 | { | 
|---|
| 65 | int err; | 
|---|
| 66 |  | 
|---|
| 67 | if (mdiodev->bus->mdio_map[mdiodev->addr]) | 
|---|
| 68 | return -EBUSY; | 
|---|
| 69 |  | 
|---|
| 70 | if (mdiodev->flags & MDIO_DEVICE_FLAG_PHY) { | 
|---|
| 71 | err = mdiobus_register_gpiod(mdiodev); | 
|---|
| 72 | if (err) | 
|---|
| 73 | return err; | 
|---|
| 74 |  | 
|---|
| 75 | err = mdiobus_register_reset(mdiodev); | 
|---|
| 76 | if (err) | 
|---|
| 77 | return err; | 
|---|
| 78 |  | 
|---|
| 79 | /* Assert the reset signal */ | 
|---|
| 80 | mdio_device_reset(mdiodev, value: 1); | 
|---|
| 81 | } | 
|---|
| 82 |  | 
|---|
| 83 | mdiodev->bus->mdio_map[mdiodev->addr] = mdiodev; | 
|---|
| 84 |  | 
|---|
| 85 | return 0; | 
|---|
| 86 | } | 
|---|
| 87 | EXPORT_SYMBOL(mdiobus_register_device); | 
|---|
| 88 |  | 
|---|
| 89 | int mdiobus_unregister_device(struct mdio_device *mdiodev) | 
|---|
| 90 | { | 
|---|
| 91 | if (mdiodev->bus->mdio_map[mdiodev->addr] != mdiodev) | 
|---|
| 92 | return -EINVAL; | 
|---|
| 93 |  | 
|---|
| 94 | gpiod_put(desc: mdiodev->reset_gpio); | 
|---|
| 95 | reset_control_put(rstc: mdiodev->reset_ctrl); | 
|---|
| 96 |  | 
|---|
| 97 | mdiodev->bus->mdio_map[mdiodev->addr] = NULL; | 
|---|
| 98 |  | 
|---|
| 99 | return 0; | 
|---|
| 100 | } | 
|---|
| 101 | EXPORT_SYMBOL(mdiobus_unregister_device); | 
|---|
| 102 |  | 
|---|
| 103 | static struct mdio_device *mdiobus_find_device(struct mii_bus *bus, int addr) | 
|---|
| 104 | { | 
|---|
| 105 | bool addr_valid = addr >= 0 && addr < ARRAY_SIZE(bus->mdio_map); | 
|---|
| 106 |  | 
|---|
| 107 | if (WARN_ONCE(!addr_valid, "addr %d out of range\n", addr)) | 
|---|
| 108 | return NULL; | 
|---|
| 109 |  | 
|---|
| 110 | return bus->mdio_map[addr]; | 
|---|
| 111 | } | 
|---|
| 112 |  | 
|---|
| 113 | struct phy_device *mdiobus_get_phy(struct mii_bus *bus, int addr) | 
|---|
| 114 | { | 
|---|
| 115 | struct mdio_device *mdiodev; | 
|---|
| 116 |  | 
|---|
| 117 | mdiodev = mdiobus_find_device(bus, addr); | 
|---|
| 118 | if (!mdiodev) | 
|---|
| 119 | return NULL; | 
|---|
| 120 |  | 
|---|
| 121 | if (!(mdiodev->flags & MDIO_DEVICE_FLAG_PHY)) | 
|---|
| 122 | return NULL; | 
|---|
| 123 |  | 
|---|
| 124 | return container_of(mdiodev, struct phy_device, mdio); | 
|---|
| 125 | } | 
|---|
| 126 | EXPORT_SYMBOL(mdiobus_get_phy); | 
|---|
| 127 |  | 
|---|
| 128 | bool mdiobus_is_registered_device(struct mii_bus *bus, int addr) | 
|---|
| 129 | { | 
|---|
| 130 | return mdiobus_find_device(bus, addr) != NULL; | 
|---|
| 131 | } | 
|---|
| 132 | EXPORT_SYMBOL(mdiobus_is_registered_device); | 
|---|
| 133 |  | 
|---|
| 134 | /** | 
|---|
| 135 | * mdiobus_release - mii_bus device release callback | 
|---|
| 136 | * @d: the target struct device that contains the mii_bus | 
|---|
| 137 | * | 
|---|
| 138 | * Description: called when the last reference to an mii_bus is | 
|---|
| 139 | * dropped, to free the underlying memory. | 
|---|
| 140 | */ | 
|---|
| 141 | static void mdiobus_release(struct device *d) | 
|---|
| 142 | { | 
|---|
| 143 | struct mii_bus *bus = to_mii_bus(d); | 
|---|
| 144 |  | 
|---|
| 145 | WARN(bus->state != MDIOBUS_RELEASED && | 
|---|
| 146 | /* for compatibility with error handling in drivers */ | 
|---|
| 147 | bus->state != MDIOBUS_ALLOCATED, | 
|---|
| 148 | "%s: not in RELEASED or ALLOCATED state\n", | 
|---|
| 149 | bus->id); | 
|---|
| 150 |  | 
|---|
| 151 | if (bus->state == MDIOBUS_RELEASED) | 
|---|
| 152 | fwnode_handle_put(dev_fwnode(d)); | 
|---|
| 153 |  | 
|---|
| 154 | kfree(objp: bus); | 
|---|
| 155 | } | 
|---|
| 156 |  | 
|---|
| 157 | struct mdio_bus_stat_attr { | 
|---|
| 158 | int addr; | 
|---|
| 159 | unsigned int field_offset; | 
|---|
| 160 | }; | 
|---|
| 161 |  | 
|---|
| 162 | static u64 mdio_bus_get_stat(struct mdio_bus_stats *s, unsigned int offset) | 
|---|
| 163 | { | 
|---|
| 164 | const char *p = (const char *)s + offset; | 
|---|
| 165 | unsigned int start; | 
|---|
| 166 | u64 val = 0; | 
|---|
| 167 |  | 
|---|
| 168 | do { | 
|---|
| 169 | start = u64_stats_fetch_begin(syncp: &s->syncp); | 
|---|
| 170 | val = u64_stats_read(p: (const u64_stats_t *)p); | 
|---|
| 171 | } while (u64_stats_fetch_retry(syncp: &s->syncp, start)); | 
|---|
| 172 |  | 
|---|
| 173 | return val; | 
|---|
| 174 | } | 
|---|
| 175 |  | 
|---|
| 176 | static u64 mdio_bus_get_global_stat(struct mii_bus *bus, unsigned int offset) | 
|---|
| 177 | { | 
|---|
| 178 | unsigned int i; | 
|---|
| 179 | u64 val = 0; | 
|---|
| 180 |  | 
|---|
| 181 | for (i = 0; i < PHY_MAX_ADDR; i++) | 
|---|
| 182 | val += mdio_bus_get_stat(s: &bus->stats[i], offset); | 
|---|
| 183 |  | 
|---|
| 184 | return val; | 
|---|
| 185 | } | 
|---|
| 186 |  | 
|---|
| 187 | static ssize_t mdio_bus_stat_field_show(struct device *dev, | 
|---|
| 188 | struct device_attribute *attr, | 
|---|
| 189 | char *buf) | 
|---|
| 190 | { | 
|---|
| 191 | struct mii_bus *bus = to_mii_bus(dev); | 
|---|
| 192 | struct mdio_bus_stat_attr *sattr; | 
|---|
| 193 | struct dev_ext_attribute *eattr; | 
|---|
| 194 | u64 val; | 
|---|
| 195 |  | 
|---|
| 196 | eattr = container_of(attr, struct dev_ext_attribute, attr); | 
|---|
| 197 | sattr = eattr->var; | 
|---|
| 198 |  | 
|---|
| 199 | if (sattr->addr < 0) | 
|---|
| 200 | val = mdio_bus_get_global_stat(bus, offset: sattr->field_offset); | 
|---|
| 201 | else | 
|---|
| 202 | val = mdio_bus_get_stat(s: &bus->stats[sattr->addr], | 
|---|
| 203 | offset: sattr->field_offset); | 
|---|
| 204 |  | 
|---|
| 205 | return sysfs_emit(buf, fmt: "%llu\n", val); | 
|---|
| 206 | } | 
|---|
| 207 |  | 
|---|
| 208 | static ssize_t mdio_bus_device_stat_field_show(struct device *dev, | 
|---|
| 209 | struct device_attribute *attr, | 
|---|
| 210 | char *buf) | 
|---|
| 211 | { | 
|---|
| 212 | struct mdio_device *mdiodev = to_mdio_device(dev); | 
|---|
| 213 | struct mii_bus *bus = mdiodev->bus; | 
|---|
| 214 | struct mdio_bus_stat_attr *sattr; | 
|---|
| 215 | struct dev_ext_attribute *eattr; | 
|---|
| 216 | int addr = mdiodev->addr; | 
|---|
| 217 | u64 val; | 
|---|
| 218 |  | 
|---|
| 219 | eattr = container_of(attr, struct dev_ext_attribute, attr); | 
|---|
| 220 | sattr = eattr->var; | 
|---|
| 221 |  | 
|---|
| 222 | val = mdio_bus_get_stat(s: &bus->stats[addr], offset: sattr->field_offset); | 
|---|
| 223 |  | 
|---|
| 224 | return sysfs_emit(buf, fmt: "%llu\n", val); | 
|---|
| 225 | } | 
|---|
| 226 |  | 
|---|
| 227 | #define MDIO_BUS_STATS_ATTR_DECL(field, file)				\ | 
|---|
| 228 | static struct dev_ext_attribute dev_attr_mdio_bus_##field = {		\ | 
|---|
| 229 | .attr = { .attr = { .name = file, .mode = 0444 },		\ | 
|---|
| 230 | .show = mdio_bus_stat_field_show,			\ | 
|---|
| 231 | },								\ | 
|---|
| 232 | .var = &((struct mdio_bus_stat_attr) {				\ | 
|---|
| 233 | -1, offsetof(struct mdio_bus_stats, field)		\ | 
|---|
| 234 | }),								\ | 
|---|
| 235 | };									\ | 
|---|
| 236 | static struct dev_ext_attribute dev_attr_mdio_bus_device_##field = {	\ | 
|---|
| 237 | .attr = { .attr = { .name = file, .mode = 0444 },		\ | 
|---|
| 238 | .show = mdio_bus_device_stat_field_show,		\ | 
|---|
| 239 | },								\ | 
|---|
| 240 | .var = &((struct mdio_bus_stat_attr) {				\ | 
|---|
| 241 | -1, offsetof(struct mdio_bus_stats, field)		\ | 
|---|
| 242 | }),								\ | 
|---|
| 243 | }; | 
|---|
| 244 |  | 
|---|
| 245 | #define MDIO_BUS_STATS_ATTR(field)					\ | 
|---|
| 246 | MDIO_BUS_STATS_ATTR_DECL(field, __stringify(field)) | 
|---|
| 247 |  | 
|---|
| 248 | MDIO_BUS_STATS_ATTR(transfers); | 
|---|
| 249 | MDIO_BUS_STATS_ATTR(errors); | 
|---|
| 250 | MDIO_BUS_STATS_ATTR(writes); | 
|---|
| 251 | MDIO_BUS_STATS_ATTR(reads); | 
|---|
| 252 |  | 
|---|
| 253 | #define MDIO_BUS_STATS_ADDR_ATTR_DECL(field, addr, file)		\ | 
|---|
| 254 | static struct dev_ext_attribute dev_attr_mdio_bus_addr_##field##_##addr = { \ | 
|---|
| 255 | .attr = { .attr = { .name = file, .mode = 0444 },		\ | 
|---|
| 256 | .show = mdio_bus_stat_field_show,			\ | 
|---|
| 257 | },								\ | 
|---|
| 258 | .var = &((struct mdio_bus_stat_attr) {				\ | 
|---|
| 259 | addr, offsetof(struct mdio_bus_stats, field)		\ | 
|---|
| 260 | }),								\ | 
|---|
| 261 | } | 
|---|
| 262 |  | 
|---|
| 263 | #define MDIO_BUS_STATS_ADDR_ATTR(field, addr)				\ | 
|---|
| 264 | MDIO_BUS_STATS_ADDR_ATTR_DECL(field, addr,			\ | 
|---|
| 265 | __stringify(field) "_" __stringify(addr)) | 
|---|
| 266 |  | 
|---|
| 267 | #define MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(addr)			\ | 
|---|
| 268 | MDIO_BUS_STATS_ADDR_ATTR(transfers, addr);			\ | 
|---|
| 269 | MDIO_BUS_STATS_ADDR_ATTR(errors, addr);				\ | 
|---|
| 270 | MDIO_BUS_STATS_ADDR_ATTR(writes, addr);				\ | 
|---|
| 271 | MDIO_BUS_STATS_ADDR_ATTR(reads, addr)				\ | 
|---|
| 272 |  | 
|---|
| 273 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(0); | 
|---|
| 274 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(1); | 
|---|
| 275 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(2); | 
|---|
| 276 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(3); | 
|---|
| 277 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(4); | 
|---|
| 278 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(5); | 
|---|
| 279 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(6); | 
|---|
| 280 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(7); | 
|---|
| 281 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(8); | 
|---|
| 282 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(9); | 
|---|
| 283 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(10); | 
|---|
| 284 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(11); | 
|---|
| 285 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(12); | 
|---|
| 286 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(13); | 
|---|
| 287 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(14); | 
|---|
| 288 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(15); | 
|---|
| 289 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(16); | 
|---|
| 290 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(17); | 
|---|
| 291 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(18); | 
|---|
| 292 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(19); | 
|---|
| 293 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(20); | 
|---|
| 294 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(21); | 
|---|
| 295 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(22); | 
|---|
| 296 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(23); | 
|---|
| 297 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(24); | 
|---|
| 298 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(25); | 
|---|
| 299 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(26); | 
|---|
| 300 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(27); | 
|---|
| 301 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(28); | 
|---|
| 302 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(29); | 
|---|
| 303 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(30); | 
|---|
| 304 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(31); | 
|---|
| 305 |  | 
|---|
| 306 | #define MDIO_BUS_STATS_ADDR_ATTR_GROUP(addr)				\ | 
|---|
| 307 | &dev_attr_mdio_bus_addr_transfers_##addr.attr.attr,		\ | 
|---|
| 308 | &dev_attr_mdio_bus_addr_errors_##addr.attr.attr,		\ | 
|---|
| 309 | &dev_attr_mdio_bus_addr_writes_##addr.attr.attr,		\ | 
|---|
| 310 | &dev_attr_mdio_bus_addr_reads_##addr.attr.attr			\ | 
|---|
| 311 |  | 
|---|
| 312 | static struct attribute *mdio_bus_statistics_attrs[] = { | 
|---|
| 313 | &dev_attr_mdio_bus_transfers.attr.attr, | 
|---|
| 314 | &dev_attr_mdio_bus_errors.attr.attr, | 
|---|
| 315 | &dev_attr_mdio_bus_writes.attr.attr, | 
|---|
| 316 | &dev_attr_mdio_bus_reads.attr.attr, | 
|---|
| 317 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(0), | 
|---|
| 318 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(1), | 
|---|
| 319 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(2), | 
|---|
| 320 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(3), | 
|---|
| 321 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(4), | 
|---|
| 322 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(5), | 
|---|
| 323 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(6), | 
|---|
| 324 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(7), | 
|---|
| 325 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(8), | 
|---|
| 326 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(9), | 
|---|
| 327 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(10), | 
|---|
| 328 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(11), | 
|---|
| 329 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(12), | 
|---|
| 330 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(13), | 
|---|
| 331 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(14), | 
|---|
| 332 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(15), | 
|---|
| 333 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(16), | 
|---|
| 334 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(17), | 
|---|
| 335 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(18), | 
|---|
| 336 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(19), | 
|---|
| 337 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(20), | 
|---|
| 338 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(21), | 
|---|
| 339 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(22), | 
|---|
| 340 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(23), | 
|---|
| 341 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(24), | 
|---|
| 342 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(25), | 
|---|
| 343 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(26), | 
|---|
| 344 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(27), | 
|---|
| 345 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(28), | 
|---|
| 346 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(29), | 
|---|
| 347 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(30), | 
|---|
| 348 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(31), | 
|---|
| 349 | NULL, | 
|---|
| 350 | }; | 
|---|
| 351 |  | 
|---|
| 352 | static const struct attribute_group mdio_bus_statistics_group = { | 
|---|
| 353 | .name	= "statistics", | 
|---|
| 354 | .attrs	= mdio_bus_statistics_attrs, | 
|---|
| 355 | }; | 
|---|
| 356 |  | 
|---|
| 357 | static const struct attribute_group *mdio_bus_groups[] = { | 
|---|
| 358 | &mdio_bus_statistics_group, | 
|---|
| 359 | NULL, | 
|---|
| 360 | }; | 
|---|
| 361 |  | 
|---|
| 362 | const struct class mdio_bus_class = { | 
|---|
| 363 | .name		= "mdio_bus", | 
|---|
| 364 | .dev_release	= mdiobus_release, | 
|---|
| 365 | .dev_groups	= mdio_bus_groups, | 
|---|
| 366 | }; | 
|---|
| 367 | EXPORT_SYMBOL_GPL(mdio_bus_class); | 
|---|
| 368 |  | 
|---|
| 369 | /** | 
|---|
| 370 | * mdio_find_bus - Given the name of a mdiobus, find the mii_bus. | 
|---|
| 371 | * @mdio_name: The name of a mdiobus. | 
|---|
| 372 | * | 
|---|
| 373 | * Returns a reference to the mii_bus, or NULL if none found.  The | 
|---|
| 374 | * embedded struct device will have its reference count incremented, | 
|---|
| 375 | * and this must be put_deviced'ed once the bus is finished with. | 
|---|
| 376 | */ | 
|---|
| 377 | struct mii_bus *mdio_find_bus(const char *mdio_name) | 
|---|
| 378 | { | 
|---|
| 379 | struct device *d; | 
|---|
| 380 |  | 
|---|
| 381 | d = class_find_device_by_name(class: &mdio_bus_class, name: mdio_name); | 
|---|
| 382 | return d ? to_mii_bus(d) : NULL; | 
|---|
| 383 | } | 
|---|
| 384 | EXPORT_SYMBOL(mdio_find_bus); | 
|---|
| 385 |  | 
|---|
| 386 | #if IS_ENABLED(CONFIG_OF_MDIO) | 
|---|
| 387 | /** | 
|---|
| 388 | * of_mdio_find_bus - Given an mii_bus node, find the mii_bus. | 
|---|
| 389 | * @mdio_bus_np: Pointer to the mii_bus. | 
|---|
| 390 | * | 
|---|
| 391 | * Returns a reference to the mii_bus, or NULL if none found.  The | 
|---|
| 392 | * embedded struct device will have its reference count incremented, | 
|---|
| 393 | * and this must be put once the bus is finished with. | 
|---|
| 394 | * | 
|---|
| 395 | * Because the association of a device_node and mii_bus is made via | 
|---|
| 396 | * of_mdiobus_register(), the mii_bus cannot be found before it is | 
|---|
| 397 | * registered with of_mdiobus_register(). | 
|---|
| 398 | * | 
|---|
| 399 | */ | 
|---|
| 400 | struct mii_bus *of_mdio_find_bus(struct device_node *mdio_bus_np) | 
|---|
| 401 | { | 
|---|
| 402 | struct device *d; | 
|---|
| 403 |  | 
|---|
| 404 | if (!mdio_bus_np) | 
|---|
| 405 | return NULL; | 
|---|
| 406 |  | 
|---|
| 407 | d = class_find_device_by_of_node(&mdio_bus_class, mdio_bus_np); | 
|---|
| 408 | return d ? to_mii_bus(d) : NULL; | 
|---|
| 409 | } | 
|---|
| 410 | EXPORT_SYMBOL(of_mdio_find_bus); | 
|---|
| 411 | #endif | 
|---|
| 412 |  | 
|---|
| 413 | static void mdiobus_stats_acct(struct mdio_bus_stats *stats, bool op, int ret) | 
|---|
| 414 | { | 
|---|
| 415 | preempt_disable(); | 
|---|
| 416 | u64_stats_update_begin(syncp: &stats->syncp); | 
|---|
| 417 |  | 
|---|
| 418 | u64_stats_inc(p: &stats->transfers); | 
|---|
| 419 | if (ret < 0) { | 
|---|
| 420 | u64_stats_inc(p: &stats->errors); | 
|---|
| 421 | goto out; | 
|---|
| 422 | } | 
|---|
| 423 |  | 
|---|
| 424 | if (op) | 
|---|
| 425 | u64_stats_inc(p: &stats->reads); | 
|---|
| 426 | else | 
|---|
| 427 | u64_stats_inc(p: &stats->writes); | 
|---|
| 428 | out: | 
|---|
| 429 | u64_stats_update_end(syncp: &stats->syncp); | 
|---|
| 430 | preempt_enable(); | 
|---|
| 431 | } | 
|---|
| 432 |  | 
|---|
| 433 | /** | 
|---|
| 434 | * __mdiobus_read - Unlocked version of the mdiobus_read function | 
|---|
| 435 | * @bus: the mii_bus struct | 
|---|
| 436 | * @addr: the phy address | 
|---|
| 437 | * @regnum: register number to read | 
|---|
| 438 | * | 
|---|
| 439 | * Read a MDIO bus register. Caller must hold the mdio bus lock. | 
|---|
| 440 | * | 
|---|
| 441 | * NOTE: MUST NOT be called from interrupt context. | 
|---|
| 442 | */ | 
|---|
| 443 | int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum) | 
|---|
| 444 | { | 
|---|
| 445 | int retval; | 
|---|
| 446 |  | 
|---|
| 447 | lockdep_assert_held_once(&bus->mdio_lock); | 
|---|
| 448 |  | 
|---|
| 449 | if (addr >= PHY_MAX_ADDR) | 
|---|
| 450 | return -ENXIO; | 
|---|
| 451 |  | 
|---|
| 452 | if (bus->read) | 
|---|
| 453 | retval = bus->read(bus, addr, regnum); | 
|---|
| 454 | else | 
|---|
| 455 | retval = -EOPNOTSUPP; | 
|---|
| 456 |  | 
|---|
| 457 | trace_mdio_access(bus, read: 1, addr, regnum, val: retval, err: retval); | 
|---|
| 458 | mdiobus_stats_acct(stats: &bus->stats[addr], op: true, ret: retval); | 
|---|
| 459 |  | 
|---|
| 460 | return retval; | 
|---|
| 461 | } | 
|---|
| 462 | EXPORT_SYMBOL(__mdiobus_read); | 
|---|
| 463 |  | 
|---|
| 464 | /** | 
|---|
| 465 | * __mdiobus_write - Unlocked version of the mdiobus_write function | 
|---|
| 466 | * @bus: the mii_bus struct | 
|---|
| 467 | * @addr: the phy address | 
|---|
| 468 | * @regnum: register number to write | 
|---|
| 469 | * @val: value to write to @regnum | 
|---|
| 470 | * | 
|---|
| 471 | * Write a MDIO bus register. Caller must hold the mdio bus lock. | 
|---|
| 472 | * | 
|---|
| 473 | * NOTE: MUST NOT be called from interrupt context. | 
|---|
| 474 | */ | 
|---|
| 475 | int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val) | 
|---|
| 476 | { | 
|---|
| 477 | int err; | 
|---|
| 478 |  | 
|---|
| 479 | lockdep_assert_held_once(&bus->mdio_lock); | 
|---|
| 480 |  | 
|---|
| 481 | if (addr >= PHY_MAX_ADDR) | 
|---|
| 482 | return -ENXIO; | 
|---|
| 483 |  | 
|---|
| 484 | if (bus->write) | 
|---|
| 485 | err = bus->write(bus, addr, regnum, val); | 
|---|
| 486 | else | 
|---|
| 487 | err = -EOPNOTSUPP; | 
|---|
| 488 |  | 
|---|
| 489 | trace_mdio_access(bus, read: 0, addr, regnum, val, err); | 
|---|
| 490 | mdiobus_stats_acct(stats: &bus->stats[addr], op: false, ret: err); | 
|---|
| 491 |  | 
|---|
| 492 | return err; | 
|---|
| 493 | } | 
|---|
| 494 | EXPORT_SYMBOL(__mdiobus_write); | 
|---|
| 495 |  | 
|---|
| 496 | /** | 
|---|
| 497 | * __mdiobus_modify_changed - Unlocked version of the mdiobus_modify function | 
|---|
| 498 | * @bus: the mii_bus struct | 
|---|
| 499 | * @addr: the phy address | 
|---|
| 500 | * @regnum: register number to modify | 
|---|
| 501 | * @mask: bit mask of bits to clear | 
|---|
| 502 | * @set: bit mask of bits to set | 
|---|
| 503 | * | 
|---|
| 504 | * Read, modify, and if any change, write the register value back to the | 
|---|
| 505 | * device. Any error returns a negative number. | 
|---|
| 506 | * | 
|---|
| 507 | * NOTE: MUST NOT be called from interrupt context. | 
|---|
| 508 | */ | 
|---|
| 509 | int __mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum, | 
|---|
| 510 | u16 mask, u16 set) | 
|---|
| 511 | { | 
|---|
| 512 | int new, ret; | 
|---|
| 513 |  | 
|---|
| 514 | ret = __mdiobus_read(bus, addr, regnum); | 
|---|
| 515 | if (ret < 0) | 
|---|
| 516 | return ret; | 
|---|
| 517 |  | 
|---|
| 518 | new = (ret & ~mask) | set; | 
|---|
| 519 | if (new == ret) | 
|---|
| 520 | return 0; | 
|---|
| 521 |  | 
|---|
| 522 | ret = __mdiobus_write(bus, addr, regnum, new); | 
|---|
| 523 |  | 
|---|
| 524 | return ret < 0 ? ret : 1; | 
|---|
| 525 | } | 
|---|
| 526 | EXPORT_SYMBOL_GPL(__mdiobus_modify_changed); | 
|---|
| 527 |  | 
|---|
| 528 | /** | 
|---|
| 529 | * __mdiobus_c45_read - Unlocked version of the mdiobus_c45_read function | 
|---|
| 530 | * @bus: the mii_bus struct | 
|---|
| 531 | * @addr: the phy address | 
|---|
| 532 | * @devad: device address to read | 
|---|
| 533 | * @regnum: register number to read | 
|---|
| 534 | * | 
|---|
| 535 | * Read a MDIO bus register. Caller must hold the mdio bus lock. | 
|---|
| 536 | * | 
|---|
| 537 | * NOTE: MUST NOT be called from interrupt context. | 
|---|
| 538 | */ | 
|---|
| 539 | int __mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum) | 
|---|
| 540 | { | 
|---|
| 541 | int retval; | 
|---|
| 542 |  | 
|---|
| 543 | lockdep_assert_held_once(&bus->mdio_lock); | 
|---|
| 544 |  | 
|---|
| 545 | if (addr >= PHY_MAX_ADDR) | 
|---|
| 546 | return -ENXIO; | 
|---|
| 547 |  | 
|---|
| 548 | if (bus->read_c45) | 
|---|
| 549 | retval = bus->read_c45(bus, addr, devad, regnum); | 
|---|
| 550 | else | 
|---|
| 551 | retval = -EOPNOTSUPP; | 
|---|
| 552 |  | 
|---|
| 553 | trace_mdio_access(bus, read: 1, addr, regnum, val: retval, err: retval); | 
|---|
| 554 | mdiobus_stats_acct(stats: &bus->stats[addr], op: true, ret: retval); | 
|---|
| 555 |  | 
|---|
| 556 | return retval; | 
|---|
| 557 | } | 
|---|
| 558 | EXPORT_SYMBOL(__mdiobus_c45_read); | 
|---|
| 559 |  | 
|---|
| 560 | /** | 
|---|
| 561 | * __mdiobus_c45_write - Unlocked version of the mdiobus_write function | 
|---|
| 562 | * @bus: the mii_bus struct | 
|---|
| 563 | * @addr: the phy address | 
|---|
| 564 | * @devad: device address to read | 
|---|
| 565 | * @regnum: register number to write | 
|---|
| 566 | * @val: value to write to @regnum | 
|---|
| 567 | * | 
|---|
| 568 | * Write a MDIO bus register. Caller must hold the mdio bus lock. | 
|---|
| 569 | * | 
|---|
| 570 | * NOTE: MUST NOT be called from interrupt context. | 
|---|
| 571 | */ | 
|---|
| 572 | int __mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum, | 
|---|
| 573 | u16 val) | 
|---|
| 574 | { | 
|---|
| 575 | int err; | 
|---|
| 576 |  | 
|---|
| 577 | lockdep_assert_held_once(&bus->mdio_lock); | 
|---|
| 578 |  | 
|---|
| 579 | if (addr >= PHY_MAX_ADDR) | 
|---|
| 580 | return -ENXIO; | 
|---|
| 581 |  | 
|---|
| 582 | if (bus->write_c45) | 
|---|
| 583 | err = bus->write_c45(bus, addr, devad, regnum, val); | 
|---|
| 584 | else | 
|---|
| 585 | err = -EOPNOTSUPP; | 
|---|
| 586 |  | 
|---|
| 587 | trace_mdio_access(bus, read: 0, addr, regnum, val, err); | 
|---|
| 588 | mdiobus_stats_acct(stats: &bus->stats[addr], op: false, ret: err); | 
|---|
| 589 |  | 
|---|
| 590 | return err; | 
|---|
| 591 | } | 
|---|
| 592 | EXPORT_SYMBOL(__mdiobus_c45_write); | 
|---|
| 593 |  | 
|---|
| 594 | /** | 
|---|
| 595 | * __mdiobus_c45_modify_changed - Unlocked version of the mdiobus_modify function | 
|---|
| 596 | * @bus: the mii_bus struct | 
|---|
| 597 | * @addr: the phy address | 
|---|
| 598 | * @devad: device address to read | 
|---|
| 599 | * @regnum: register number to modify | 
|---|
| 600 | * @mask: bit mask of bits to clear | 
|---|
| 601 | * @set: bit mask of bits to set | 
|---|
| 602 | * | 
|---|
| 603 | * Read, modify, and if any change, write the register value back to the | 
|---|
| 604 | * device. Any error returns a negative number. | 
|---|
| 605 | * | 
|---|
| 606 | * NOTE: MUST NOT be called from interrupt context. | 
|---|
| 607 | */ | 
|---|
| 608 | static int __mdiobus_c45_modify_changed(struct mii_bus *bus, int addr, | 
|---|
| 609 | int devad, u32 regnum, u16 mask, | 
|---|
| 610 | u16 set) | 
|---|
| 611 | { | 
|---|
| 612 | int new, ret; | 
|---|
| 613 |  | 
|---|
| 614 | ret = __mdiobus_c45_read(bus, addr, devad, regnum); | 
|---|
| 615 | if (ret < 0) | 
|---|
| 616 | return ret; | 
|---|
| 617 |  | 
|---|
| 618 | new = (ret & ~mask) | set; | 
|---|
| 619 | if (new == ret) | 
|---|
| 620 | return 0; | 
|---|
| 621 |  | 
|---|
| 622 | ret = __mdiobus_c45_write(bus, addr, devad, regnum, new); | 
|---|
| 623 |  | 
|---|
| 624 | return ret < 0 ? ret : 1; | 
|---|
| 625 | } | 
|---|
| 626 |  | 
|---|
| 627 | /** | 
|---|
| 628 | * mdiobus_read_nested - Nested version of the mdiobus_read function | 
|---|
| 629 | * @bus: the mii_bus struct | 
|---|
| 630 | * @addr: the phy address | 
|---|
| 631 | * @regnum: register number to read | 
|---|
| 632 | * | 
|---|
| 633 | * In case of nested MDIO bus access avoid lockdep false positives by | 
|---|
| 634 | * using mutex_lock_nested(). | 
|---|
| 635 | * | 
|---|
| 636 | * NOTE: MUST NOT be called from interrupt context, | 
|---|
| 637 | * because the bus read/write functions may wait for an interrupt | 
|---|
| 638 | * to conclude the operation. | 
|---|
| 639 | */ | 
|---|
| 640 | int mdiobus_read_nested(struct mii_bus *bus, int addr, u32 regnum) | 
|---|
| 641 | { | 
|---|
| 642 | int retval; | 
|---|
| 643 |  | 
|---|
| 644 | mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); | 
|---|
| 645 | retval = __mdiobus_read(bus, addr, regnum); | 
|---|
| 646 | mutex_unlock(lock: &bus->mdio_lock); | 
|---|
| 647 |  | 
|---|
| 648 | return retval; | 
|---|
| 649 | } | 
|---|
| 650 | EXPORT_SYMBOL(mdiobus_read_nested); | 
|---|
| 651 |  | 
|---|
| 652 | /** | 
|---|
| 653 | * mdiobus_read - Convenience function for reading a given MII mgmt register | 
|---|
| 654 | * @bus: the mii_bus struct | 
|---|
| 655 | * @addr: the phy address | 
|---|
| 656 | * @regnum: register number to read | 
|---|
| 657 | * | 
|---|
| 658 | * NOTE: MUST NOT be called from interrupt context, | 
|---|
| 659 | * because the bus read/write functions may wait for an interrupt | 
|---|
| 660 | * to conclude the operation. | 
|---|
| 661 | */ | 
|---|
| 662 | int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum) | 
|---|
| 663 | { | 
|---|
| 664 | int retval; | 
|---|
| 665 |  | 
|---|
| 666 | mutex_lock(lock: &bus->mdio_lock); | 
|---|
| 667 | retval = __mdiobus_read(bus, addr, regnum); | 
|---|
| 668 | mutex_unlock(lock: &bus->mdio_lock); | 
|---|
| 669 |  | 
|---|
| 670 | return retval; | 
|---|
| 671 | } | 
|---|
| 672 | EXPORT_SYMBOL(mdiobus_read); | 
|---|
| 673 |  | 
|---|
| 674 | /** | 
|---|
| 675 | * mdiobus_c45_read - Convenience function for reading a given MII mgmt register | 
|---|
| 676 | * @bus: the mii_bus struct | 
|---|
| 677 | * @addr: the phy address | 
|---|
| 678 | * @devad: device address to read | 
|---|
| 679 | * @regnum: register number to read | 
|---|
| 680 | * | 
|---|
| 681 | * NOTE: MUST NOT be called from interrupt context, | 
|---|
| 682 | * because the bus read/write functions may wait for an interrupt | 
|---|
| 683 | * to conclude the operation. | 
|---|
| 684 | */ | 
|---|
| 685 | int mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum) | 
|---|
| 686 | { | 
|---|
| 687 | int retval; | 
|---|
| 688 |  | 
|---|
| 689 | mutex_lock(lock: &bus->mdio_lock); | 
|---|
| 690 | retval = __mdiobus_c45_read(bus, addr, devad, regnum); | 
|---|
| 691 | mutex_unlock(lock: &bus->mdio_lock); | 
|---|
| 692 |  | 
|---|
| 693 | return retval; | 
|---|
| 694 | } | 
|---|
| 695 | EXPORT_SYMBOL(mdiobus_c45_read); | 
|---|
| 696 |  | 
|---|
| 697 | /** | 
|---|
| 698 | * mdiobus_c45_read_nested - Nested version of the mdiobus_c45_read function | 
|---|
| 699 | * @bus: the mii_bus struct | 
|---|
| 700 | * @addr: the phy address | 
|---|
| 701 | * @devad: device address to read | 
|---|
| 702 | * @regnum: register number to read | 
|---|
| 703 | * | 
|---|
| 704 | * In case of nested MDIO bus access avoid lockdep false positives by | 
|---|
| 705 | * using mutex_lock_nested(). | 
|---|
| 706 | * | 
|---|
| 707 | * NOTE: MUST NOT be called from interrupt context, | 
|---|
| 708 | * because the bus read/write functions may wait for an interrupt | 
|---|
| 709 | * to conclude the operation. | 
|---|
| 710 | */ | 
|---|
| 711 | int mdiobus_c45_read_nested(struct mii_bus *bus, int addr, int devad, | 
|---|
| 712 | u32 regnum) | 
|---|
| 713 | { | 
|---|
| 714 | int retval; | 
|---|
| 715 |  | 
|---|
| 716 | mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); | 
|---|
| 717 | retval = __mdiobus_c45_read(bus, addr, devad, regnum); | 
|---|
| 718 | mutex_unlock(lock: &bus->mdio_lock); | 
|---|
| 719 |  | 
|---|
| 720 | return retval; | 
|---|
| 721 | } | 
|---|
| 722 | EXPORT_SYMBOL(mdiobus_c45_read_nested); | 
|---|
| 723 |  | 
|---|
| 724 | /** | 
|---|
| 725 | * mdiobus_write_nested - Nested version of the mdiobus_write function | 
|---|
| 726 | * @bus: the mii_bus struct | 
|---|
| 727 | * @addr: the phy address | 
|---|
| 728 | * @regnum: register number to write | 
|---|
| 729 | * @val: value to write to @regnum | 
|---|
| 730 | * | 
|---|
| 731 | * In case of nested MDIO bus access avoid lockdep false positives by | 
|---|
| 732 | * using mutex_lock_nested(). | 
|---|
| 733 | * | 
|---|
| 734 | * NOTE: MUST NOT be called from interrupt context, | 
|---|
| 735 | * because the bus read/write functions may wait for an interrupt | 
|---|
| 736 | * to conclude the operation. | 
|---|
| 737 | */ | 
|---|
| 738 | int mdiobus_write_nested(struct mii_bus *bus, int addr, u32 regnum, u16 val) | 
|---|
| 739 | { | 
|---|
| 740 | int err; | 
|---|
| 741 |  | 
|---|
| 742 | mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); | 
|---|
| 743 | err = __mdiobus_write(bus, addr, regnum, val); | 
|---|
| 744 | mutex_unlock(lock: &bus->mdio_lock); | 
|---|
| 745 |  | 
|---|
| 746 | return err; | 
|---|
| 747 | } | 
|---|
| 748 | EXPORT_SYMBOL(mdiobus_write_nested); | 
|---|
| 749 |  | 
|---|
| 750 | /** | 
|---|
| 751 | * mdiobus_write - Convenience function for writing a given MII mgmt register | 
|---|
| 752 | * @bus: the mii_bus struct | 
|---|
| 753 | * @addr: the phy address | 
|---|
| 754 | * @regnum: register number to write | 
|---|
| 755 | * @val: value to write to @regnum | 
|---|
| 756 | * | 
|---|
| 757 | * NOTE: MUST NOT be called from interrupt context, | 
|---|
| 758 | * because the bus read/write functions may wait for an interrupt | 
|---|
| 759 | * to conclude the operation. | 
|---|
| 760 | */ | 
|---|
| 761 | int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val) | 
|---|
| 762 | { | 
|---|
| 763 | int err; | 
|---|
| 764 |  | 
|---|
| 765 | mutex_lock(lock: &bus->mdio_lock); | 
|---|
| 766 | err = __mdiobus_write(bus, addr, regnum, val); | 
|---|
| 767 | mutex_unlock(lock: &bus->mdio_lock); | 
|---|
| 768 |  | 
|---|
| 769 | return err; | 
|---|
| 770 | } | 
|---|
| 771 | EXPORT_SYMBOL(mdiobus_write); | 
|---|
| 772 |  | 
|---|
| 773 | /** | 
|---|
| 774 | * mdiobus_c45_write - Convenience function for writing a given MII mgmt register | 
|---|
| 775 | * @bus: the mii_bus struct | 
|---|
| 776 | * @addr: the phy address | 
|---|
| 777 | * @devad: device address to read | 
|---|
| 778 | * @regnum: register number to write | 
|---|
| 779 | * @val: value to write to @regnum | 
|---|
| 780 | * | 
|---|
| 781 | * NOTE: MUST NOT be called from interrupt context, | 
|---|
| 782 | * because the bus read/write functions may wait for an interrupt | 
|---|
| 783 | * to conclude the operation. | 
|---|
| 784 | */ | 
|---|
| 785 | int mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum, | 
|---|
| 786 | u16 val) | 
|---|
| 787 | { | 
|---|
| 788 | int err; | 
|---|
| 789 |  | 
|---|
| 790 | mutex_lock(lock: &bus->mdio_lock); | 
|---|
| 791 | err = __mdiobus_c45_write(bus, addr, devad, regnum, val); | 
|---|
| 792 | mutex_unlock(lock: &bus->mdio_lock); | 
|---|
| 793 |  | 
|---|
| 794 | return err; | 
|---|
| 795 | } | 
|---|
| 796 | EXPORT_SYMBOL(mdiobus_c45_write); | 
|---|
| 797 |  | 
|---|
| 798 | /** | 
|---|
| 799 | * mdiobus_c45_write_nested - Nested version of the mdiobus_c45_write function | 
|---|
| 800 | * @bus: the mii_bus struct | 
|---|
| 801 | * @addr: the phy address | 
|---|
| 802 | * @devad: device address to read | 
|---|
| 803 | * @regnum: register number to write | 
|---|
| 804 | * @val: value to write to @regnum | 
|---|
| 805 | * | 
|---|
| 806 | * In case of nested MDIO bus access avoid lockdep false positives by | 
|---|
| 807 | * using mutex_lock_nested(). | 
|---|
| 808 | * | 
|---|
| 809 | * NOTE: MUST NOT be called from interrupt context, | 
|---|
| 810 | * because the bus read/write functions may wait for an interrupt | 
|---|
| 811 | * to conclude the operation. | 
|---|
| 812 | */ | 
|---|
| 813 | int mdiobus_c45_write_nested(struct mii_bus *bus, int addr, int devad, | 
|---|
| 814 | u32 regnum, u16 val) | 
|---|
| 815 | { | 
|---|
| 816 | int err; | 
|---|
| 817 |  | 
|---|
| 818 | mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); | 
|---|
| 819 | err = __mdiobus_c45_write(bus, addr, devad, regnum, val); | 
|---|
| 820 | mutex_unlock(lock: &bus->mdio_lock); | 
|---|
| 821 |  | 
|---|
| 822 | return err; | 
|---|
| 823 | } | 
|---|
| 824 | EXPORT_SYMBOL(mdiobus_c45_write_nested); | 
|---|
| 825 |  | 
|---|
| 826 | /* | 
|---|
| 827 | * __mdiobus_modify - Convenience function for modifying a given mdio device | 
|---|
| 828 | *	register | 
|---|
| 829 | * @bus: the mii_bus struct | 
|---|
| 830 | * @addr: the phy address | 
|---|
| 831 | * @regnum: register number to write | 
|---|
| 832 | * @mask: bit mask of bits to clear | 
|---|
| 833 | * @set: bit mask of bits to set | 
|---|
| 834 | */ | 
|---|
| 835 | int __mdiobus_modify(struct mii_bus *bus, int addr, u32 regnum, u16 mask, | 
|---|
| 836 | u16 set) | 
|---|
| 837 | { | 
|---|
| 838 | int err; | 
|---|
| 839 |  | 
|---|
| 840 | err = __mdiobus_modify_changed(bus, addr, regnum, mask, set); | 
|---|
| 841 |  | 
|---|
| 842 | return err < 0 ? err : 0; | 
|---|
| 843 | } | 
|---|
| 844 | EXPORT_SYMBOL_GPL(__mdiobus_modify); | 
|---|
| 845 |  | 
|---|
| 846 | /** | 
|---|
| 847 | * mdiobus_modify - Convenience function for modifying a given mdio device | 
|---|
| 848 | *	register | 
|---|
| 849 | * @bus: the mii_bus struct | 
|---|
| 850 | * @addr: the phy address | 
|---|
| 851 | * @regnum: register number to write | 
|---|
| 852 | * @mask: bit mask of bits to clear | 
|---|
| 853 | * @set: bit mask of bits to set | 
|---|
| 854 | */ | 
|---|
| 855 | int mdiobus_modify(struct mii_bus *bus, int addr, u32 regnum, u16 mask, u16 set) | 
|---|
| 856 | { | 
|---|
| 857 | int err; | 
|---|
| 858 |  | 
|---|
| 859 | mutex_lock(lock: &bus->mdio_lock); | 
|---|
| 860 | err = __mdiobus_modify(bus, addr, regnum, mask, set); | 
|---|
| 861 | mutex_unlock(lock: &bus->mdio_lock); | 
|---|
| 862 |  | 
|---|
| 863 | return err; | 
|---|
| 864 | } | 
|---|
| 865 | EXPORT_SYMBOL_GPL(mdiobus_modify); | 
|---|
| 866 |  | 
|---|
| 867 | /** | 
|---|
| 868 | * mdiobus_c45_modify - Convenience function for modifying a given mdio device | 
|---|
| 869 | *	register | 
|---|
| 870 | * @bus: the mii_bus struct | 
|---|
| 871 | * @addr: the phy address | 
|---|
| 872 | * @devad: device address to read | 
|---|
| 873 | * @regnum: register number to write | 
|---|
| 874 | * @mask: bit mask of bits to clear | 
|---|
| 875 | * @set: bit mask of bits to set | 
|---|
| 876 | */ | 
|---|
| 877 | int mdiobus_c45_modify(struct mii_bus *bus, int addr, int devad, u32 regnum, | 
|---|
| 878 | u16 mask, u16 set) | 
|---|
| 879 | { | 
|---|
| 880 | int err; | 
|---|
| 881 |  | 
|---|
| 882 | mutex_lock(lock: &bus->mdio_lock); | 
|---|
| 883 | err = __mdiobus_c45_modify_changed(bus, addr, devad, regnum, | 
|---|
| 884 | mask, set); | 
|---|
| 885 | mutex_unlock(lock: &bus->mdio_lock); | 
|---|
| 886 |  | 
|---|
| 887 | return err < 0 ? err : 0; | 
|---|
| 888 | } | 
|---|
| 889 | EXPORT_SYMBOL_GPL(mdiobus_c45_modify); | 
|---|
| 890 |  | 
|---|
| 891 | /** | 
|---|
| 892 | * mdiobus_modify_changed - Convenience function for modifying a given mdio | 
|---|
| 893 | *	device register and returning if it changed | 
|---|
| 894 | * @bus: the mii_bus struct | 
|---|
| 895 | * @addr: the phy address | 
|---|
| 896 | * @regnum: register number to write | 
|---|
| 897 | * @mask: bit mask of bits to clear | 
|---|
| 898 | * @set: bit mask of bits to set | 
|---|
| 899 | */ | 
|---|
| 900 | int mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum, | 
|---|
| 901 | u16 mask, u16 set) | 
|---|
| 902 | { | 
|---|
| 903 | int err; | 
|---|
| 904 |  | 
|---|
| 905 | mutex_lock(lock: &bus->mdio_lock); | 
|---|
| 906 | err = __mdiobus_modify_changed(bus, addr, regnum, mask, set); | 
|---|
| 907 | mutex_unlock(lock: &bus->mdio_lock); | 
|---|
| 908 |  | 
|---|
| 909 | return err; | 
|---|
| 910 | } | 
|---|
| 911 | EXPORT_SYMBOL_GPL(mdiobus_modify_changed); | 
|---|
| 912 |  | 
|---|
| 913 | /** | 
|---|
| 914 | * mdiobus_c45_modify_changed - Convenience function for modifying a given mdio | 
|---|
| 915 | *	device register and returning if it changed | 
|---|
| 916 | * @bus: the mii_bus struct | 
|---|
| 917 | * @addr: the phy address | 
|---|
| 918 | * @devad: device address to read | 
|---|
| 919 | * @regnum: register number to write | 
|---|
| 920 | * @mask: bit mask of bits to clear | 
|---|
| 921 | * @set: bit mask of bits to set | 
|---|
| 922 | */ | 
|---|
| 923 | int mdiobus_c45_modify_changed(struct mii_bus *bus, int addr, int devad, | 
|---|
| 924 | u32 regnum, u16 mask, u16 set) | 
|---|
| 925 | { | 
|---|
| 926 | int err; | 
|---|
| 927 |  | 
|---|
| 928 | mutex_lock(lock: &bus->mdio_lock); | 
|---|
| 929 | err = __mdiobus_c45_modify_changed(bus, addr, devad, regnum, mask, set); | 
|---|
| 930 | mutex_unlock(lock: &bus->mdio_lock); | 
|---|
| 931 |  | 
|---|
| 932 | return err; | 
|---|
| 933 | } | 
|---|
| 934 | EXPORT_SYMBOL_GPL(mdiobus_c45_modify_changed); | 
|---|
| 935 |  | 
|---|
| 936 | /** | 
|---|
| 937 | * mdio_bus_match - determine if given MDIO driver supports the given | 
|---|
| 938 | *		    MDIO device | 
|---|
| 939 | * @dev: target MDIO device | 
|---|
| 940 | * @drv: given MDIO driver | 
|---|
| 941 | * | 
|---|
| 942 | * Description: Given a MDIO device, and a MDIO driver, return 1 if | 
|---|
| 943 | *   the driver supports the device.  Otherwise, return 0. This may | 
|---|
| 944 | *   require calling the devices own match function, since different classes | 
|---|
| 945 | *   of MDIO devices have different match criteria. | 
|---|
| 946 | */ | 
|---|
| 947 | static int mdio_bus_match(struct device *dev, const struct device_driver *drv) | 
|---|
| 948 | { | 
|---|
| 949 | const struct mdio_driver *mdiodrv = to_mdio_driver(drv); | 
|---|
| 950 | struct mdio_device *mdio = to_mdio_device(dev); | 
|---|
| 951 |  | 
|---|
| 952 | /* Both the driver and device must type-match */ | 
|---|
| 953 | if (!(mdiodrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY) != | 
|---|
| 954 | !(mdio->flags & MDIO_DEVICE_FLAG_PHY)) | 
|---|
| 955 | return 0; | 
|---|
| 956 |  | 
|---|
| 957 | if (of_driver_match_device(dev, drv)) | 
|---|
| 958 | return 1; | 
|---|
| 959 |  | 
|---|
| 960 | if (mdio->bus_match) | 
|---|
| 961 | return mdio->bus_match(dev, drv); | 
|---|
| 962 |  | 
|---|
| 963 | return 0; | 
|---|
| 964 | } | 
|---|
| 965 |  | 
|---|
| 966 | static int mdio_uevent(const struct device *dev, struct kobj_uevent_env *env) | 
|---|
| 967 | { | 
|---|
| 968 | int rc; | 
|---|
| 969 |  | 
|---|
| 970 | /* Some devices have extra OF data and an OF-style MODALIAS */ | 
|---|
| 971 | rc = of_device_uevent_modalias(dev, env); | 
|---|
| 972 | if (rc != -ENODEV) | 
|---|
| 973 | return rc; | 
|---|
| 974 |  | 
|---|
| 975 | return 0; | 
|---|
| 976 | } | 
|---|
| 977 |  | 
|---|
| 978 | static struct attribute *mdio_bus_device_statistics_attrs[] = { | 
|---|
| 979 | &dev_attr_mdio_bus_device_transfers.attr.attr, | 
|---|
| 980 | &dev_attr_mdio_bus_device_errors.attr.attr, | 
|---|
| 981 | &dev_attr_mdio_bus_device_writes.attr.attr, | 
|---|
| 982 | &dev_attr_mdio_bus_device_reads.attr.attr, | 
|---|
| 983 | NULL, | 
|---|
| 984 | }; | 
|---|
| 985 |  | 
|---|
| 986 | static const struct attribute_group mdio_bus_device_statistics_group = { | 
|---|
| 987 | .name	= "statistics", | 
|---|
| 988 | .attrs	= mdio_bus_device_statistics_attrs, | 
|---|
| 989 | }; | 
|---|
| 990 |  | 
|---|
| 991 | static const struct attribute_group *mdio_bus_dev_groups[] = { | 
|---|
| 992 | &mdio_bus_device_statistics_group, | 
|---|
| 993 | NULL, | 
|---|
| 994 | }; | 
|---|
| 995 |  | 
|---|
| 996 | const struct bus_type mdio_bus_type = { | 
|---|
| 997 | .name		= "mdio_bus", | 
|---|
| 998 | .dev_groups	= mdio_bus_dev_groups, | 
|---|
| 999 | .match		= mdio_bus_match, | 
|---|
| 1000 | .uevent		= mdio_uevent, | 
|---|
| 1001 | }; | 
|---|
| 1002 | EXPORT_SYMBOL(mdio_bus_type); | 
|---|
| 1003 |  | 
|---|
| 1004 | static int __init mdio_bus_init(void) | 
|---|
| 1005 | { | 
|---|
| 1006 | int ret; | 
|---|
| 1007 |  | 
|---|
| 1008 | ret = class_register(class: &mdio_bus_class); | 
|---|
| 1009 | if (!ret) { | 
|---|
| 1010 | ret = bus_register(bus: &mdio_bus_type); | 
|---|
| 1011 | if (ret) | 
|---|
| 1012 | class_unregister(class: &mdio_bus_class); | 
|---|
| 1013 | } | 
|---|
| 1014 |  | 
|---|
| 1015 | return ret; | 
|---|
| 1016 | } | 
|---|
| 1017 |  | 
|---|
| 1018 | static void __exit mdio_bus_exit(void) | 
|---|
| 1019 | { | 
|---|
| 1020 | class_unregister(class: &mdio_bus_class); | 
|---|
| 1021 | bus_unregister(bus: &mdio_bus_type); | 
|---|
| 1022 | } | 
|---|
| 1023 |  | 
|---|
| 1024 | subsys_initcall(mdio_bus_init); | 
|---|
| 1025 | module_exit(mdio_bus_exit); | 
|---|
| 1026 |  | 
|---|
| 1027 | MODULE_LICENSE( "GPL"); | 
|---|
| 1028 | MODULE_DESCRIPTION( "MDIO bus/device layer"); | 
|---|
| 1029 |  | 
|---|