1 // SPDX-License-Identifier: GPL-2.0-or-later
4 Broadcom B43 wireless driver
7 Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
8 Copyright (c) 2005 Stefano Brivio <stefano.brivio@polimi.it>
9 Copyright (c) 2005-2007 Michael Buesch <m@bues.ch>
10 Copyright (c) 2005 Danny van Dyk <kugelfang@gentoo.org>
11 Copyright (c) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
21 static void b43_led_turn_on(struct b43_wldev
*dev
, u8 led_index
,
26 ctl
= b43_read16(dev
, B43_MMIO_GPIO_CONTROL
);
28 ctl
&= ~(1 << led_index
);
30 ctl
|= (1 << led_index
);
31 b43_write16(dev
, B43_MMIO_GPIO_CONTROL
, ctl
);
34 static void b43_led_turn_off(struct b43_wldev
*dev
, u8 led_index
,
39 ctl
= b43_read16(dev
, B43_MMIO_GPIO_CONTROL
);
41 ctl
|= (1 << led_index
);
43 ctl
&= ~(1 << led_index
);
44 b43_write16(dev
, B43_MMIO_GPIO_CONTROL
, ctl
);
47 static void b43_led_update(struct b43_wldev
*dev
,
56 radio_enabled
= (dev
->phy
.radio_on
&& dev
->radio_hw_enable
);
58 /* The led->state read is racy, but we don't care. In case we raced
59 * with the brightness_set handler, we will be called again soon
60 * to fixup our state. */
62 turn_on
= atomic_read(&led
->state
) != LED_OFF
;
65 if (turn_on
== led
->hw_state
)
67 led
->hw_state
= turn_on
;
70 b43_led_turn_on(dev
, led
->index
, led
->activelow
);
72 b43_led_turn_off(dev
, led
->index
, led
->activelow
);
75 static void b43_leds_work(struct work_struct
*work
)
77 struct b43_leds
*leds
= container_of(work
, struct b43_leds
, work
);
78 struct b43_wl
*wl
= container_of(leds
, struct b43_wl
, leds
);
79 struct b43_wldev
*dev
;
81 mutex_lock(&wl
->mutex
);
82 dev
= wl
->current_dev
;
83 if (unlikely(!dev
|| b43_status(dev
) < B43_STAT_STARTED
))
86 b43_led_update(dev
, &wl
->leds
.led_tx
);
87 b43_led_update(dev
, &wl
->leds
.led_rx
);
88 b43_led_update(dev
, &wl
->leds
.led_radio
);
89 b43_led_update(dev
, &wl
->leds
.led_assoc
);
92 mutex_unlock(&wl
->mutex
);
95 /* Callback from the LED subsystem. */
96 static void b43_led_brightness_set(struct led_classdev
*led_dev
,
97 enum led_brightness brightness
)
99 struct b43_led
*led
= container_of(led_dev
, struct b43_led
, led_dev
);
100 struct b43_wl
*wl
= led
->wl
;
102 if (likely(!wl
->leds
.stop
)) {
103 atomic_set(&led
->state
, brightness
);
104 ieee80211_queue_work(wl
->hw
, &wl
->leds
.work
);
108 static int b43_register_led(struct b43_wldev
*dev
, struct b43_led
*led
,
109 const char *name
, const char *default_trigger
,
110 u8 led_index
, bool activelow
)
116 if (!default_trigger
)
119 led
->index
= led_index
;
120 led
->activelow
= activelow
;
121 strlcpy(led
->name
, name
, sizeof(led
->name
));
122 atomic_set(&led
->state
, 0);
124 led
->led_dev
.name
= led
->name
;
125 led
->led_dev
.default_trigger
= default_trigger
;
126 led
->led_dev
.brightness_set
= b43_led_brightness_set
;
128 err
= led_classdev_register(dev
->dev
->dev
, &led
->led_dev
);
130 b43warn(dev
->wl
, "LEDs: Failed to register %s\n", name
);
138 static void b43_unregister_led(struct b43_led
*led
)
142 led_classdev_unregister(&led
->led_dev
);
146 static void b43_map_led(struct b43_wldev
*dev
,
148 enum b43_led_behaviour behaviour
,
151 struct ieee80211_hw
*hw
= dev
->wl
->hw
;
152 char name
[B43_LED_MAX_NAME_LEN
+ 1];
154 /* Map the b43 specific LED behaviour value to the
155 * generic LED triggers. */
157 case B43_LED_INACTIVE
:
161 case B43_LED_ACTIVITY
:
162 case B43_LED_TRANSFER
:
163 case B43_LED_APTRANSFER
:
164 snprintf(name
, sizeof(name
),
165 "b43-%s::tx", wiphy_name(hw
->wiphy
));
166 b43_register_led(dev
, &dev
->wl
->leds
.led_tx
, name
,
167 ieee80211_get_tx_led_name(hw
),
168 led_index
, activelow
);
169 snprintf(name
, sizeof(name
),
170 "b43-%s::rx", wiphy_name(hw
->wiphy
));
171 b43_register_led(dev
, &dev
->wl
->leds
.led_rx
, name
,
172 ieee80211_get_rx_led_name(hw
),
173 led_index
, activelow
);
175 case B43_LED_RADIO_ALL
:
176 case B43_LED_RADIO_A
:
177 case B43_LED_RADIO_B
:
178 case B43_LED_MODE_BG
:
179 snprintf(name
, sizeof(name
),
180 "b43-%s::radio", wiphy_name(hw
->wiphy
));
181 b43_register_led(dev
, &dev
->wl
->leds
.led_radio
, name
,
182 ieee80211_get_radio_led_name(hw
),
183 led_index
, activelow
);
187 snprintf(name
, sizeof(name
),
188 "b43-%s::assoc", wiphy_name(hw
->wiphy
));
189 b43_register_led(dev
, &dev
->wl
->leds
.led_assoc
, name
,
190 ieee80211_get_assoc_led_name(hw
),
191 led_index
, activelow
);
194 b43warn(dev
->wl
, "LEDs: Unknown behaviour 0x%02X\n",
200 static void b43_led_get_sprominfo(struct b43_wldev
*dev
,
201 unsigned int led_index
,
202 enum b43_led_behaviour
*behaviour
,
207 sprom
[0] = dev
->dev
->bus_sprom
->gpio0
;
208 sprom
[1] = dev
->dev
->bus_sprom
->gpio1
;
209 sprom
[2] = dev
->dev
->bus_sprom
->gpio2
;
210 sprom
[3] = dev
->dev
->bus_sprom
->gpio3
;
212 if ((sprom
[0] & sprom
[1] & sprom
[2] & sprom
[3]) == 0xff) {
213 /* There is no LED information in the SPROM
214 * for this LED. Hardcode it here. */
218 *behaviour
= B43_LED_ACTIVITY
;
220 if (dev
->dev
->board_vendor
== PCI_VENDOR_ID_COMPAQ
)
221 *behaviour
= B43_LED_RADIO_ALL
;
224 *behaviour
= B43_LED_RADIO_B
;
225 if (dev
->dev
->board_vendor
== PCI_VENDOR_ID_ASUSTEK
)
226 *behaviour
= B43_LED_ASSOC
;
229 *behaviour
= B43_LED_RADIO_A
;
232 *behaviour
= B43_LED_OFF
;
235 *behaviour
= B43_LED_OFF
;
240 /* keep LED disabled if no mapping is defined */
241 if (sprom
[led_index
] == 0xff)
242 *behaviour
= B43_LED_OFF
;
244 *behaviour
= sprom
[led_index
] & B43_LED_BEHAVIOUR
;
245 *activelow
= !!(sprom
[led_index
] & B43_LED_ACTIVELOW
);
249 void b43_leds_init(struct b43_wldev
*dev
)
253 enum b43_led_behaviour behaviour
;
256 /* Sync the RF-kill LED state (if we have one) with radio and switch states. */
257 led
= &dev
->wl
->leds
.led_radio
;
259 if (dev
->phy
.radio_on
&& b43_is_hw_radio_enabled(dev
)) {
260 b43_led_turn_on(dev
, led
->index
, led
->activelow
);
261 led
->hw_state
= true;
262 atomic_set(&led
->state
, 1);
264 b43_led_turn_off(dev
, led
->index
, led
->activelow
);
265 led
->hw_state
= false;
266 atomic_set(&led
->state
, 0);
270 /* Initialize TX/RX/ASSOC leds */
271 led
= &dev
->wl
->leds
.led_tx
;
273 b43_led_turn_off(dev
, led
->index
, led
->activelow
);
274 led
->hw_state
= false;
275 atomic_set(&led
->state
, 0);
277 led
= &dev
->wl
->leds
.led_rx
;
279 b43_led_turn_off(dev
, led
->index
, led
->activelow
);
280 led
->hw_state
= false;
281 atomic_set(&led
->state
, 0);
283 led
= &dev
->wl
->leds
.led_assoc
;
285 b43_led_turn_off(dev
, led
->index
, led
->activelow
);
286 led
->hw_state
= false;
287 atomic_set(&led
->state
, 0);
290 /* Initialize other LED states. */
291 for (i
= 0; i
< B43_MAX_NR_LEDS
; i
++) {
292 b43_led_get_sprominfo(dev
, i
, &behaviour
, &activelow
);
295 b43_led_turn_off(dev
, i
, activelow
);
298 b43_led_turn_on(dev
, i
, activelow
);
301 /* Leave others as-is. */
306 dev
->wl
->leds
.stop
= 0;
309 void b43_leds_exit(struct b43_wldev
*dev
)
311 struct b43_leds
*leds
= &dev
->wl
->leds
;
313 b43_led_turn_off(dev
, leds
->led_tx
.index
, leds
->led_tx
.activelow
);
314 b43_led_turn_off(dev
, leds
->led_rx
.index
, leds
->led_rx
.activelow
);
315 b43_led_turn_off(dev
, leds
->led_assoc
.index
, leds
->led_assoc
.activelow
);
316 b43_led_turn_off(dev
, leds
->led_radio
.index
, leds
->led_radio
.activelow
);
319 void b43_leds_stop(struct b43_wldev
*dev
)
321 struct b43_leds
*leds
= &dev
->wl
->leds
;
324 cancel_work_sync(&leds
->work
);
327 void b43_leds_register(struct b43_wldev
*dev
)
330 enum b43_led_behaviour behaviour
;
333 INIT_WORK(&dev
->wl
->leds
.work
, b43_leds_work
);
335 /* Register the LEDs to the LED subsystem. */
336 for (i
= 0; i
< B43_MAX_NR_LEDS
; i
++) {
337 b43_led_get_sprominfo(dev
, i
, &behaviour
, &activelow
);
338 b43_map_led(dev
, i
, behaviour
, activelow
);
342 void b43_leds_unregister(struct b43_wl
*wl
)
344 struct b43_leds
*leds
= &wl
->leds
;
346 b43_unregister_led(&leds
->led_tx
);
347 b43_unregister_led(&leds
->led_rx
);
348 b43_unregister_led(&leds
->led_assoc
);
349 b43_unregister_led(&leds
->led_radio
);