phy_led_triggers.c 3.8 KB

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