| @@ -158,9 +158,11 @@ struct mii_bus *mdiobus_alloc_size(size_t size) · 157 unmodified lines |
| 158 | if (size) | 158 | if (size) |
| 159 | bus->priv = (void *)bus + aligned_size; | 159 | bus->priv = (void *)bus + aligned_size; |
| 160 | | 160 | |
| 161 | /* Initialise the interrupts to polling */ | 161 | /* Initialise the interrupts to polling and 64-bit seqcounts */ |
| 162 | for (i = 0; i < PHY_MAX_ADDR; i++) | 162 | for (i = 0; i < PHY_MAX_ADDR; i++) { |
| 163 | bus->irq[i] = PHY_POLL; | 163 | bus->irq[i] = PHY_POLL; |
| | 164 | u64_stats_init(&bus->stats[i].syncp); |
| | 165 | } |
| 164 | | 166 | |
| 165 | return bus; | 167 | return bus; |
| 166 | } | 168 | } |
| @@ -249,9 +251,215 @@ static void mdiobus_release(struct device *d) · 82 unmodified lines |
| 249 | kfree(bus); | 251 | kfree(bus); |
| 250 | } | 252 | } |
| 251 | | 253 | |
| | 254 | struct mdio_bus_stat_attr { |
| | 255 | int addr; |
| | 256 | unsigned int field_offset; |
| | 257 | }; |
| | 258 | |
| | 259 | static u64 mdio_bus_get_stat(struct mdio_bus_stats *s, unsigned int offset) |
| | 260 | { |
| | 261 | const char *p = (const char *)s + offset; |
| | 262 | unsigned int start; |
| | 263 | u64 val = 0; |
| | 264 | |
| | 265 | do { |
| | 266 | start = u64_stats_fetch_begin(&s->syncp); |
| | 267 | val = u64_stats_read((const u64_stats_t *)p); |
| | 268 | } while (u64_stats_fetch_retry(&s->syncp, start)); |
| | 269 | |
| | 270 | return val; |
| | 271 | } |
| | 272 | |
| | 273 | static u64 mdio_bus_get_global_stat(struct mii_bus *bus, unsigned int offset) |
| | 274 | { |
| | 275 | unsigned int i; |
| | 276 | u64 val = 0; |
| | 277 | |
| | 278 | for (i = 0; i < PHY_MAX_ADDR; i++) |
| | 279 | val += mdio_bus_get_stat(&bus->stats[i], offset); |
| | 280 | |
| | 281 | return val; |
| | 282 | } |
| | 283 | |
| | 284 | static ssize_t mdio_bus_stat_field_show(struct device *dev, |
| | 285 | struct device_attribute *attr, |
| | 286 | char *buf) |
| | 287 | { |
| | 288 | struct mii_bus *bus = to_mii_bus(dev); |
| | 289 | struct mdio_bus_stat_attr *sattr; |
| | 290 | struct dev_ext_attribute *eattr; |
| | 291 | u64 val; |
| | 292 | |
| | 293 | eattr = container_of(attr, struct dev_ext_attribute, attr); |
| | 294 | sattr = eattr->var; |
| | 295 | |
| | 296 | if (sattr->addr < 0) |
| | 297 | val = mdio_bus_get_global_stat(bus, sattr->field_offset); |
| | 298 | else |
| | 299 | val = mdio_bus_get_stat(&bus->stats[sattr->addr], |
| | 300 | sattr->field_offset); |
| | 301 | |
| | 302 | return sprintf(buf, "%llu\n", val); |
| | 303 | } |
| | 304 | |
| | 305 | static ssize_t mdio_bus_device_stat_field_show(struct device *dev, |
| | 306 | struct device_attribute *attr, |
| | 307 | char *buf) |
| | 308 | { |
| | 309 | struct mdio_device *mdiodev = to_mdio_device(dev); |
| | 310 | struct mii_bus *bus = mdiodev->bus; |
| | 311 | struct mdio_bus_stat_attr *sattr; |
| | 312 | struct dev_ext_attribute *eattr; |
| | 313 | int addr = mdiodev->addr; |
| | 314 | u64 val; |
| | 315 | |
| | 316 | eattr = container_of(attr, struct dev_ext_attribute, attr); |
| | 317 | sattr = eattr->var; |
| | 318 | |
| | 319 | val = mdio_bus_get_stat(&bus->stats[addr], sattr->field_offset); |
| | 320 | |
| | 321 | return sprintf(buf, "%llu\n", val); |
| | 322 | } |
| | 323 | |
| | 324 | #define MDIO_BUS_STATS_ATTR_DECL(field, file) \ |
| | 325 | static struct dev_ext_attribute dev_attr_mdio_bus_##field = { \ |
| | 326 | .attr = { .attr = { .name = file, .mode = 0444 }, \ |
| | 327 | .show = mdio_bus_stat_field_show, \ |
| | 328 | }, \ |
| | 329 | .var = &((struct mdio_bus_stat_attr) { \ |
| | 330 | -1, offsetof(struct mdio_bus_stats, field) \ |
| | 331 | }), \ |
| | 332 | }; \ |
| | 333 | static struct dev_ext_attribute dev_attr_mdio_bus_device_##field = { \ |
| | 334 | .attr = { .attr = { .name = file, .mode = 0444 }, \ |
| | 335 | .show = mdio_bus_device_stat_field_show, \ |
| | 336 | }, \ |
| | 337 | .var = &((struct mdio_bus_stat_attr) { \ |
| | 338 | -1, offsetof(struct mdio_bus_stats, field) \ |
| | 339 | }), \ |
| | 340 | }; |
| | 341 | |
| | 342 | #define MDIO_BUS_STATS_ATTR(field) \ |
| | 343 | MDIO_BUS_STATS_ATTR_DECL(field, __stringify(field)) |
| | 344 | |
| | 345 | MDIO_BUS_STATS_ATTR(transfers); |
| | 346 | MDIO_BUS_STATS_ATTR(errors); |
| | 347 | MDIO_BUS_STATS_ATTR(writes); |
| | 348 | MDIO_BUS_STATS_ATTR(reads); |
| | 349 | |
| | 350 | #define MDIO_BUS_STATS_ADDR_ATTR_DECL(field, addr, file) \ |
| | 351 | static struct dev_ext_attribute dev_attr_mdio_bus_addr_##field##_##addr = { \ |
| | 352 | .attr = { .attr = { .name = file, .mode = 0444 }, \ |
| | 353 | .show = mdio_bus_stat_field_show, \ |
| | 354 | }, \ |
| | 355 | .var = &((struct mdio_bus_stat_attr) { \ |
| | 356 | addr, offsetof(struct mdio_bus_stats, field) \ |
| | 357 | }), \ |
| | 358 | } |
| | 359 | |
| | 360 | #define MDIO_BUS_STATS_ADDR_ATTR(field, addr) \ |
| | 361 | MDIO_BUS_STATS_ADDR_ATTR_DECL(field, addr, \ |
| | 362 | __stringify(field) "_" __stringify(addr)) |
| | 363 | |
| | 364 | #define MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(addr) \ |
| | 365 | MDIO_BUS_STATS_ADDR_ATTR(transfers, addr); \ |
| | 366 | MDIO_BUS_STATS_ADDR_ATTR(errors, addr); \ |
| | 367 | MDIO_BUS_STATS_ADDR_ATTR(writes, addr); \ |
| | 368 | MDIO_BUS_STATS_ADDR_ATTR(reads, addr) \ |
| | 369 | |
| | 370 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(0); |
| | 371 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(1); |
| | 372 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(2); |
| | 373 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(3); |
| | 374 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(4); |
| | 375 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(5); |
| | 376 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(6); |
| | 377 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(7); |
| | 378 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(8); |
| | 379 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(9); |
| | 380 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(10); |
| | 381 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(11); |
| | 382 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(12); |
| | 383 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(13); |
| | 384 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(14); |
| | 385 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(15); |
| | 386 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(16); |
| | 387 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(17); |
| | 388 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(18); |
| | 389 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(19); |
| | 390 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(20); |
| | 391 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(21); |
| | 392 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(22); |
| | 393 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(23); |
| | 394 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(24); |
| | 395 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(25); |
| | 396 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(26); |
| | 397 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(27); |
| | 398 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(28); |
| | 399 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(29); |
| | 400 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(30); |
| | 401 | MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(31); |
| | 402 | |
| | 403 | #define MDIO_BUS_STATS_ADDR_ATTR_GROUP(addr) \ |
| | 404 | &dev_attr_mdio_bus_addr_transfers_##addr.attr.attr, \ |
| | 405 | &dev_attr_mdio_bus_addr_errors_##addr.attr.attr, \ |
| | 406 | &dev_attr_mdio_bus_addr_writes_##addr.attr.attr, \ |
| | 407 | &dev_attr_mdio_bus_addr_reads_##addr.attr.attr \ |
| | 408 | |
| | 409 | static struct attribute *mdio_bus_statistics_attrs[] = { |
| | 410 | &dev_attr_mdio_bus_transfers.attr.attr, |
| | 411 | &dev_attr_mdio_bus_errors.attr.attr, |
| | 412 | &dev_attr_mdio_bus_writes.attr.attr, |
| | 413 | &dev_attr_mdio_bus_reads.attr.attr, |
| | 414 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(0), |
| | 415 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(1), |
| | 416 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(2), |
| | 417 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(3), |
| | 418 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(4), |
| | 419 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(5), |
| | 420 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(6), |
| | 421 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(7), |
| | 422 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(8), |
| | 423 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(9), |
| | 424 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(10), |
| | 425 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(11), |
| | 426 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(12), |
| | 427 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(13), |
| | 428 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(14), |
| | 429 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(15), |
| | 430 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(16), |
| | 431 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(17), |
| | 432 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(18), |
| | 433 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(19), |
| | 434 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(20), |
| | 435 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(21), |
| | 436 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(22), |
| | 437 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(23), |
| | 438 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(24), |
| | 439 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(25), |
| | 440 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(26), |
| | 441 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(27), |
| | 442 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(28), |
| | 443 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(29), |
| | 444 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(30), |
| | 445 | MDIO_BUS_STATS_ADDR_ATTR_GROUP(31), |
| | 446 | NULL, |
| | 447 | }; |
| | 448 | |
| | 449 | static const struct attribute_group mdio_bus_statistics_group = { |
| | 450 | .name = "statistics", |
| | 451 | .attrs = mdio_bus_statistics_attrs, |
| | 452 | }; |
| | 453 | |
| | 454 | static const struct attribute_group *mdio_bus_groups[] = { |
| | 455 | &mdio_bus_statistics_group, |
| | 456 | NULL, |
| | 457 | }; |
| | 458 | |
| 252 | static struct class mdio_bus_class = { | 459 | static struct class mdio_bus_class = { |
| 253 | .name = "mdio_bus", | 460 | .name = "mdio_bus", |
| 254 | .dev_release = mdiobus_release, | 461 | .dev_release = mdiobus_release, |
| | 462 | .dev_groups = mdio_bus_groups, |
| 255 | }; | 463 | }; |
| 256 | | 464 | |
| 257 | #if IS_ENABLED(CONFIG_OF_MDIO) | 465 | #if IS_ENABLED(CONFIG_OF_MDIO) |
| @@ -530,6 +738,24 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr) · 272 unmodified lines |
| 530 | } | 738 | } |
| 531 | EXPORT_SYMBOL(mdiobus_scan); | 739 | EXPORT_SYMBOL(mdiobus_scan); |
| 532 | | 740 | |
| | 741 | static void mdiobus_stats_acct(struct mdio_bus_stats *stats, bool op, int ret) |
| | 742 | { |
| | 743 | u64_stats_update_begin(&stats->syncp); |
| | 744 | |
| | 745 | u64_stats_inc(&stats->transfers); |
| | 746 | if (ret < 0) { |
| | 747 | u64_stats_inc(&stats->errors); |
| | 748 | goto out; |
| | 749 | } |
| | 750 | |
| | 751 | if (op) |
| | 752 | u64_stats_inc(&stats->reads); |
| | 753 | else |
| | 754 | u64_stats_inc(&stats->writes); |
| | 755 | out: |
| | 756 | u64_stats_update_end(&stats->syncp); |
| | 757 | } |
| | 758 | |
| 533 | /** | 759 | /** |
| 534 | * __mdiobus_read - Unlocked version of the mdiobus_read function | 760 | * __mdiobus_read - Unlocked version of the mdiobus_read function |
| 535 | * @bus: the mii_bus struct | 761 | * @bus: the mii_bus struct |
| @@ -549,6 +775,7 @@ int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum) · 13 unmodified lines |
| 549 | retval = bus->read(bus, addr, regnum); | 775 | retval = bus->read(bus, addr, regnum); |
| 550 | | 776 | |
| 551 | trace_mdio_access(bus, 1, addr, regnum, retval, retval); | 777 | trace_mdio_access(bus, 1, addr, regnum, retval, retval); |
| | 778 | mdiobus_stats_acct(&bus->stats[addr], true, retval); |
| 552 | | 779 | |
| 553 | return retval; | 780 | return retval; |
| 554 | } | 781 | } |
| @@ -574,6 +801,7 @@ int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val) · 19 unmodified lines |
| 574 | err = bus->write(bus, addr, regnum, val); | 801 | err = bus->write(bus, addr, regnum, val); |
| 575 | | 802 | |
| 576 | trace_mdio_access(bus, 0, addr, regnum, val, err); | 803 | trace_mdio_access(bus, 0, addr, regnum, val, err); |
| | 804 | mdiobus_stats_acct(&bus->stats[addr], false, err); |
| 577 | | 805 | |
| 578 | return err; | 806 | return err; |
| 579 | } | 807 | } |
| @@ -719,8 +947,27 @@ static int mdio_uevent(struct device *dev, struct kobj_uevent_env *env) · 139 unmodified lines |
| 719 | return 0; | 947 | return 0; |
| 720 | } | 948 | } |
| 721 | | 949 | |
| | 950 | static struct attribute *mdio_bus_device_statistics_attrs[] = { |
| | 951 | &dev_attr_mdio_bus_device_transfers.attr.attr, |
| | 952 | &dev_attr_mdio_bus_device_errors.attr.attr, |
| | 953 | &dev_attr_mdio_bus_device_writes.attr.attr, |
| | 954 | &dev_attr_mdio_bus_device_reads.attr.attr, |
| | 955 | NULL, |
| | 956 | }; |
| | 957 | |
| | 958 | static const struct attribute_group mdio_bus_device_statistics_group = { |
| | 959 | .name = "statistics", |
| | 960 | .attrs = mdio_bus_device_statistics_attrs, |
| | 961 | }; |
| | 962 | |
| | 963 | static const struct attribute_group *mdio_bus_dev_groups[] = { |
| | 964 | &mdio_bus_device_statistics_group, |
| | 965 | NULL, |
| | 966 | }; |
| | 967 | |
| 722 | struct bus_type mdio_bus_type = { | 968 | struct bus_type mdio_bus_type = { |
| 723 | .name = "mdio_bus", | 969 | .name = "mdio_bus", |
| | 970 | .dev_groups = mdio_bus_dev_groups, |
| 724 | .match = mdio_bus_match, | 971 | .match = mdio_bus_match, |
| 725 | .uevent = mdio_uevent, | 972 | .uevent = mdio_uevent, |
| 726 | }; | 973 | }; |