phy_led_triggers.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /* Copyright (C) 2016 National Instruments Corp. */
  3. #include <linux/leds.h>
  4. #include <linux/phy.h>
  5. #include <linux/phy_led_triggers.h>
  6. #include <linux/netdevice.h>
  7. #include "phylib-internal.h"
  8. static struct phy_led_trigger *phy_speed_to_led_trigger(struct phy_device *phy,
  9. unsigned int speed)
  10. {
  11. unsigned int i;
  12. for (i = 0; i < phy->phy_num_led_triggers; i++) {
  13. if (phy->phy_led_triggers[i].speed == speed)
  14. return &phy->phy_led_triggers[i];
  15. }
  16. return NULL;
  17. }
  18. static void phy_led_trigger_no_link(struct phy_device *phy)
  19. {
  20. if (phy->last_triggered) {
  21. led_trigger_event(&phy->last_triggered->trigger, LED_OFF);
  22. led_trigger_event(&phy->led_link_trigger->trigger, LED_OFF);
  23. phy->last_triggered = NULL;
  24. }
  25. }
  26. void phy_led_trigger_change_speed(struct phy_device *phy)
  27. {
  28. struct phy_led_trigger *plt;
  29. if (!phy->link)
  30. return phy_led_trigger_no_link(phy);
  31. if (phy->speed == 0)
  32. return;
  33. plt = phy_speed_to_led_trigger(phy, phy->speed);
  34. if (!plt) {
  35. netdev_alert(phy->attached_dev,
  36. "No phy led trigger registered for speed(%d)\n",
  37. phy->speed);
  38. return phy_led_trigger_no_link(phy);
  39. }
  40. if (plt != phy->last_triggered) {
  41. if (!phy->last_triggered)
  42. led_trigger_event(&phy->led_link_trigger->trigger,
  43. LED_FULL);
  44. else
  45. led_trigger_event(&phy->last_triggered->trigger, LED_OFF);
  46. led_trigger_event(&plt->trigger, LED_FULL);
  47. phy->last_triggered = plt;
  48. }
  49. }
  50. EXPORT_SYMBOL_GPL(phy_led_trigger_change_speed);
  51. static void phy_led_trigger_format_name(struct phy_device *phy, char *buf,
  52. size_t size, const char *suffix)
  53. {
  54. snprintf(buf, size, PHY_ID_FMT ":%s",
  55. phy->mdio.bus->id, phy->mdio.addr, suffix);
  56. }
  57. static int phy_led_trigger_register(struct phy_device *phy,
  58. struct phy_led_trigger *plt,
  59. unsigned int speed,
  60. const char *suffix)
  61. {
  62. plt->speed = speed;
  63. phy_led_trigger_format_name(phy, plt->name, sizeof(plt->name), suffix);
  64. plt->trigger.name = plt->name;
  65. return led_trigger_register(&plt->trigger);
  66. }
  67. static void phy_led_trigger_unregister(struct phy_led_trigger *plt)
  68. {
  69. led_trigger_unregister(&plt->trigger);
  70. }
  71. int phy_led_triggers_register(struct phy_device *phy)
  72. {
  73. int i, err;
  74. unsigned int speeds[50];
  75. phy->phy_num_led_triggers = phy_supported_speeds(phy, speeds,
  76. ARRAY_SIZE(speeds));
  77. if (!phy->phy_num_led_triggers)
  78. return 0;
  79. phy->led_link_trigger = kzalloc_obj(*phy->led_link_trigger);
  80. if (!phy->led_link_trigger) {
  81. err = -ENOMEM;
  82. goto out_clear;
  83. }
  84. err = phy_led_trigger_register(phy, phy->led_link_trigger, 0, "link");
  85. if (err)
  86. goto out_free_link;
  87. phy->phy_led_triggers = kzalloc_objs(struct phy_led_trigger,
  88. phy->phy_num_led_triggers);
  89. if (!phy->phy_led_triggers) {
  90. err = -ENOMEM;
  91. goto out_unreg_link;
  92. }
  93. for (i = 0; i < phy->phy_num_led_triggers; i++) {
  94. err = phy_led_trigger_register(phy, &phy->phy_led_triggers[i],
  95. speeds[i],
  96. phy_speed_to_str(speeds[i]));
  97. if (err)
  98. goto out_unreg;
  99. }
  100. phy->last_triggered = NULL;
  101. phy_led_trigger_change_speed(phy);
  102. return 0;
  103. out_unreg:
  104. while (i--)
  105. phy_led_trigger_unregister(&phy->phy_led_triggers[i]);
  106. kfree(phy->phy_led_triggers);
  107. out_unreg_link:
  108. phy_led_trigger_unregister(phy->led_link_trigger);
  109. out_free_link:
  110. kfree(phy->led_link_trigger);
  111. phy->led_link_trigger = NULL;
  112. out_clear:
  113. phy->phy_num_led_triggers = 0;
  114. return err;
  115. }
  116. EXPORT_SYMBOL_GPL(phy_led_triggers_register);
  117. void phy_led_triggers_unregister(struct phy_device *phy)
  118. {
  119. int i;
  120. for (i = 0; i < phy->phy_num_led_triggers; i++)
  121. phy_led_trigger_unregister(&phy->phy_led_triggers[i]);
  122. kfree(phy->phy_led_triggers);
  123. phy->phy_led_triggers = NULL;
  124. if (phy->led_link_trigger) {
  125. phy_led_trigger_unregister(phy->led_link_trigger);
  126. kfree(phy->led_link_trigger);
  127. phy->led_link_trigger = NULL;
  128. }
  129. }
  130. EXPORT_SYMBOL_GPL(phy_led_triggers_unregister);