leds.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. Broadcom B43 wireless driver
  4. LED control
  5. Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
  6. Copyright (c) 2005 Stefano Brivio <stefano.brivio@polimi.it>
  7. Copyright (c) 2005-2007 Michael Buesch <m@bues.ch>
  8. Copyright (c) 2005 Danny van Dyk <kugelfang@gentoo.org>
  9. Copyright (c) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
  10. */
  11. #include "b43legacy.h"
  12. #include "leds.h"
  13. #include "rfkill.h"
  14. static void b43legacy_led_turn_on(struct b43legacy_wldev *dev, u8 led_index,
  15. bool activelow)
  16. {
  17. struct b43legacy_wl *wl = dev->wl;
  18. unsigned long flags;
  19. u16 ctl;
  20. spin_lock_irqsave(&wl->leds_lock, flags);
  21. ctl = b43legacy_read16(dev, B43legacy_MMIO_GPIO_CONTROL);
  22. if (activelow)
  23. ctl &= ~(1 << led_index);
  24. else
  25. ctl |= (1 << led_index);
  26. b43legacy_write16(dev, B43legacy_MMIO_GPIO_CONTROL, ctl);
  27. spin_unlock_irqrestore(&wl->leds_lock, flags);
  28. }
  29. static void b43legacy_led_turn_off(struct b43legacy_wldev *dev, u8 led_index,
  30. bool activelow)
  31. {
  32. struct b43legacy_wl *wl = dev->wl;
  33. unsigned long flags;
  34. u16 ctl;
  35. spin_lock_irqsave(&wl->leds_lock, flags);
  36. ctl = b43legacy_read16(dev, B43legacy_MMIO_GPIO_CONTROL);
  37. if (activelow)
  38. ctl |= (1 << led_index);
  39. else
  40. ctl &= ~(1 << led_index);
  41. b43legacy_write16(dev, B43legacy_MMIO_GPIO_CONTROL, ctl);
  42. spin_unlock_irqrestore(&wl->leds_lock, flags);
  43. }
  44. /* Callback from the LED subsystem. */
  45. static void b43legacy_led_brightness_set(struct led_classdev *led_dev,
  46. enum led_brightness brightness)
  47. {
  48. struct b43legacy_led *led = container_of(led_dev, struct b43legacy_led,
  49. led_dev);
  50. struct b43legacy_wldev *dev = led->dev;
  51. bool radio_enabled;
  52. /* Checking the radio-enabled status here is slightly racy,
  53. * but we want to avoid the locking overhead and we don't care
  54. * whether the LED has the wrong state for a second. */
  55. radio_enabled = (dev->phy.radio_on && dev->radio_hw_enable);
  56. if (brightness == LED_OFF || !radio_enabled)
  57. b43legacy_led_turn_off(dev, led->index, led->activelow);
  58. else
  59. b43legacy_led_turn_on(dev, led->index, led->activelow);
  60. }
  61. static int b43legacy_register_led(struct b43legacy_wldev *dev,
  62. struct b43legacy_led *led,
  63. const char *name,
  64. const char *default_trigger,
  65. u8 led_index, bool activelow)
  66. {
  67. int err;
  68. b43legacy_led_turn_off(dev, led_index, activelow);
  69. if (led->dev)
  70. return -EEXIST;
  71. if (!default_trigger)
  72. return -EINVAL;
  73. led->dev = dev;
  74. led->index = led_index;
  75. led->activelow = activelow;
  76. strscpy(led->name, name, sizeof(led->name));
  77. led->led_dev.name = led->name;
  78. led->led_dev.default_trigger = default_trigger;
  79. led->led_dev.brightness_set = b43legacy_led_brightness_set;
  80. err = led_classdev_register(dev->dev->dev, &led->led_dev);
  81. if (err) {
  82. b43legacywarn(dev->wl, "LEDs: Failed to register %s\n", name);
  83. led->dev = NULL;
  84. return err;
  85. }
  86. return 0;
  87. }
  88. static void b43legacy_unregister_led(struct b43legacy_led *led)
  89. {
  90. if (!led->dev)
  91. return;
  92. led_classdev_unregister(&led->led_dev);
  93. b43legacy_led_turn_off(led->dev, led->index, led->activelow);
  94. led->dev = NULL;
  95. }
  96. static void b43legacy_map_led(struct b43legacy_wldev *dev,
  97. u8 led_index,
  98. enum b43legacy_led_behaviour behaviour,
  99. bool activelow)
  100. {
  101. struct ieee80211_hw *hw = dev->wl->hw;
  102. char name[B43legacy_LED_MAX_NAME_LEN + 1];
  103. /* Map the b43 specific LED behaviour value to the
  104. * generic LED triggers. */
  105. switch (behaviour) {
  106. case B43legacy_LED_INACTIVE:
  107. break;
  108. case B43legacy_LED_OFF:
  109. b43legacy_led_turn_off(dev, led_index, activelow);
  110. break;
  111. case B43legacy_LED_ON:
  112. b43legacy_led_turn_on(dev, led_index, activelow);
  113. break;
  114. case B43legacy_LED_ACTIVITY:
  115. case B43legacy_LED_TRANSFER:
  116. case B43legacy_LED_APTRANSFER:
  117. snprintf(name, sizeof(name),
  118. "b43legacy-%s::tx", wiphy_name(hw->wiphy));
  119. b43legacy_register_led(dev, &dev->led_tx, name,
  120. ieee80211_get_tx_led_name(hw),
  121. led_index, activelow);
  122. snprintf(name, sizeof(name),
  123. "b43legacy-%s::rx", wiphy_name(hw->wiphy));
  124. b43legacy_register_led(dev, &dev->led_rx, name,
  125. ieee80211_get_rx_led_name(hw),
  126. led_index, activelow);
  127. break;
  128. case B43legacy_LED_RADIO_ALL:
  129. case B43legacy_LED_RADIO_A:
  130. case B43legacy_LED_RADIO_B:
  131. case B43legacy_LED_MODE_BG:
  132. snprintf(name, sizeof(name),
  133. "b43legacy-%s::radio", wiphy_name(hw->wiphy));
  134. b43legacy_register_led(dev, &dev->led_radio, name,
  135. ieee80211_get_radio_led_name(hw),
  136. led_index, activelow);
  137. /* Sync the RF-kill LED state with radio and switch states. */
  138. if (dev->phy.radio_on && b43legacy_is_hw_radio_enabled(dev))
  139. b43legacy_led_turn_on(dev, led_index, activelow);
  140. break;
  141. case B43legacy_LED_WEIRD:
  142. case B43legacy_LED_ASSOC:
  143. snprintf(name, sizeof(name),
  144. "b43legacy-%s::assoc", wiphy_name(hw->wiphy));
  145. b43legacy_register_led(dev, &dev->led_assoc, name,
  146. ieee80211_get_assoc_led_name(hw),
  147. led_index, activelow);
  148. break;
  149. default:
  150. b43legacywarn(dev->wl, "LEDs: Unknown behaviour 0x%02X\n",
  151. behaviour);
  152. break;
  153. }
  154. }
  155. void b43legacy_leds_init(struct b43legacy_wldev *dev)
  156. {
  157. struct ssb_bus *bus = dev->dev->bus;
  158. u8 sprom[4];
  159. int i;
  160. enum b43legacy_led_behaviour behaviour;
  161. bool activelow;
  162. sprom[0] = bus->sprom.gpio0;
  163. sprom[1] = bus->sprom.gpio1;
  164. sprom[2] = bus->sprom.gpio2;
  165. sprom[3] = bus->sprom.gpio3;
  166. for (i = 0; i < 4; i++) {
  167. if (sprom[i] == 0xFF) {
  168. /* There is no LED information in the SPROM
  169. * for this LED. Hardcode it here. */
  170. activelow = false;
  171. switch (i) {
  172. case 0:
  173. behaviour = B43legacy_LED_ACTIVITY;
  174. activelow = true;
  175. if (bus->boardinfo.vendor == PCI_VENDOR_ID_COMPAQ)
  176. behaviour = B43legacy_LED_RADIO_ALL;
  177. break;
  178. case 1:
  179. behaviour = B43legacy_LED_RADIO_B;
  180. if (bus->boardinfo.vendor == PCI_VENDOR_ID_ASUSTEK)
  181. behaviour = B43legacy_LED_ASSOC;
  182. break;
  183. case 2:
  184. behaviour = B43legacy_LED_RADIO_A;
  185. break;
  186. case 3:
  187. behaviour = B43legacy_LED_OFF;
  188. break;
  189. default:
  190. B43legacy_WARN_ON(1);
  191. return;
  192. }
  193. } else {
  194. behaviour = sprom[i] & B43legacy_LED_BEHAVIOUR;
  195. activelow = !!(sprom[i] & B43legacy_LED_ACTIVELOW);
  196. }
  197. b43legacy_map_led(dev, i, behaviour, activelow);
  198. }
  199. }
  200. void b43legacy_leds_exit(struct b43legacy_wldev *dev)
  201. {
  202. b43legacy_unregister_led(&dev->led_tx);
  203. b43legacy_unregister_led(&dev->led_rx);
  204. b43legacy_unregister_led(&dev->led_assoc);
  205. b43legacy_unregister_led(&dev->led_radio);
  206. }