Commit | Line | Data |
---|---|---|
f222313a JL |
1 | /* |
2 | ||
3 | Broadcom BCM43xx wireless driver | |
4 | ||
5 | Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>, | |
6 | Stefano Brivio <st3@riseup.net> | |
7 | Michael Buesch <mbuesch@freenet.de> | |
8 | Danny van Dyk <kugelfang@gentoo.org> | |
9 | Andreas Jaggi <andreas.jaggi@waterwave.ch> | |
10 | ||
11 | This program is free software; you can redistribute it and/or modify | |
12 | it under the terms of the GNU General Public License as published by | |
13 | the Free Software Foundation; either version 2 of the License, or | |
14 | (at your option) any later version. | |
15 | ||
16 | This program is distributed in the hope that it will be useful, | |
17 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | GNU General Public License for more details. | |
20 | ||
21 | You should have received a copy of the GNU General Public License | |
22 | along with this program; see the file COPYING. If not, write to | |
23 | the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, | |
24 | Boston, MA 02110-1301, USA. | |
25 | ||
26 | */ | |
27 | ||
28 | #include "bcm43xx_leds.h" | |
01917382 | 29 | #include "bcm43xx_radio.h" |
f222313a JL |
30 | #include "bcm43xx.h" |
31 | ||
1977f032 | 32 | #include <linux/bitops.h> |
f222313a JL |
33 | |
34 | ||
35 | static void bcm43xx_led_changestate(struct bcm43xx_led *led) | |
36 | { | |
37 | struct bcm43xx_private *bcm = led->bcm; | |
38 | const int index = bcm43xx_led_index(led); | |
dcfd720b | 39 | const u16 mask = (1 << index); |
f222313a JL |
40 | u16 ledctl; |
41 | ||
42 | assert(index >= 0 && index < BCM43xx_NR_LEDS); | |
43 | assert(led->blink_interval); | |
44 | ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL); | |
dcfd720b | 45 | ledctl = (ledctl & mask) ? (ledctl & ~mask) : (ledctl | mask); |
f222313a JL |
46 | bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl); |
47 | } | |
48 | ||
49 | static void bcm43xx_led_blink(unsigned long d) | |
50 | { | |
51 | struct bcm43xx_led *led = (struct bcm43xx_led *)d; | |
52 | struct bcm43xx_private *bcm = led->bcm; | |
53 | unsigned long flags; | |
54 | ||
efa6a370 | 55 | spin_lock_irqsave(&bcm->leds_lock, flags); |
f222313a JL |
56 | if (led->blink_interval) { |
57 | bcm43xx_led_changestate(led); | |
58 | mod_timer(&led->blink_timer, jiffies + led->blink_interval); | |
59 | } | |
efa6a370 | 60 | spin_unlock_irqrestore(&bcm->leds_lock, flags); |
f222313a JL |
61 | } |
62 | ||
63 | static void bcm43xx_led_blink_start(struct bcm43xx_led *led, | |
64 | unsigned long interval) | |
65 | { | |
dcfd720b MB |
66 | if (led->blink_interval) |
67 | return; | |
f222313a JL |
68 | led->blink_interval = interval; |
69 | bcm43xx_led_changestate(led); | |
70 | led->blink_timer.expires = jiffies + interval; | |
71 | add_timer(&led->blink_timer); | |
72 | } | |
73 | ||
74 | static void bcm43xx_led_blink_stop(struct bcm43xx_led *led, int sync) | |
75 | { | |
76 | struct bcm43xx_private *bcm = led->bcm; | |
77 | const int index = bcm43xx_led_index(led); | |
78 | u16 ledctl; | |
79 | ||
80 | if (!led->blink_interval) | |
81 | return; | |
82 | if (unlikely(sync)) | |
83 | del_timer_sync(&led->blink_timer); | |
84 | else | |
85 | del_timer(&led->blink_timer); | |
86 | led->blink_interval = 0; | |
87 | ||
88 | /* Make sure the LED is turned off. */ | |
89 | assert(index >= 0 && index < BCM43xx_NR_LEDS); | |
90 | ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL); | |
91 | if (led->activelow) | |
92 | ledctl |= (1 << index); | |
93 | else | |
94 | ledctl &= ~(1 << index); | |
95 | bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl); | |
96 | } | |
97 | ||
dcfd720b MB |
98 | static void bcm43xx_led_init_hardcoded(struct bcm43xx_private *bcm, |
99 | struct bcm43xx_led *led, | |
100 | int led_index) | |
101 | { | |
102 | /* This function is called, if the behaviour (and activelow) | |
103 | * information for a LED is missing in the SPROM. | |
104 | * We hardcode the behaviour values for various devices here. | |
105 | * Note that the BCM43xx_LED_TEST_XXX behaviour values can | |
106 | * be used to figure out which led is mapped to which index. | |
107 | */ | |
108 | ||
109 | switch (led_index) { | |
110 | case 0: | |
111 | led->behaviour = BCM43xx_LED_ACTIVITY; | |
01917382 | 112 | led->activelow = 1; |
dcfd720b MB |
113 | if (bcm->board_vendor == PCI_VENDOR_ID_COMPAQ) |
114 | led->behaviour = BCM43xx_LED_RADIO_ALL; | |
115 | break; | |
116 | case 1: | |
117 | led->behaviour = BCM43xx_LED_RADIO_B; | |
118 | if (bcm->board_vendor == PCI_VENDOR_ID_ASUSTEK) | |
119 | led->behaviour = BCM43xx_LED_ASSOC; | |
120 | break; | |
121 | case 2: | |
122 | led->behaviour = BCM43xx_LED_RADIO_A; | |
123 | break; | |
124 | case 3: | |
125 | led->behaviour = BCM43xx_LED_OFF; | |
126 | break; | |
127 | default: | |
128 | assert(0); | |
129 | } | |
130 | } | |
131 | ||
f222313a JL |
132 | int bcm43xx_leds_init(struct bcm43xx_private *bcm) |
133 | { | |
134 | struct bcm43xx_led *led; | |
135 | u8 sprom[4]; | |
136 | int i; | |
137 | ||
138 | sprom[0] = bcm->sprom.wl0gpio0; | |
139 | sprom[1] = bcm->sprom.wl0gpio1; | |
140 | sprom[2] = bcm->sprom.wl0gpio2; | |
141 | sprom[3] = bcm->sprom.wl0gpio3; | |
142 | ||
143 | for (i = 0; i < BCM43xx_NR_LEDS; i++) { | |
144 | led = &(bcm->leds[i]); | |
145 | led->bcm = bcm; | |
dcfd720b MB |
146 | setup_timer(&led->blink_timer, |
147 | bcm43xx_led_blink, | |
148 | (unsigned long)led); | |
f222313a JL |
149 | |
150 | if (sprom[i] == 0xFF) { | |
dcfd720b | 151 | bcm43xx_led_init_hardcoded(bcm, led, i); |
f222313a JL |
152 | } else { |
153 | led->behaviour = sprom[i] & BCM43xx_LED_BEHAVIOUR; | |
154 | led->activelow = !!(sprom[i] & BCM43xx_LED_ACTIVELOW); | |
155 | } | |
156 | } | |
157 | ||
158 | return 0; | |
159 | } | |
160 | ||
161 | void bcm43xx_leds_exit(struct bcm43xx_private *bcm) | |
162 | { | |
163 | struct bcm43xx_led *led; | |
164 | int i; | |
165 | ||
166 | for (i = 0; i < BCM43xx_NR_LEDS; i++) { | |
167 | led = &(bcm->leds[i]); | |
168 | bcm43xx_led_blink_stop(led, 1); | |
169 | } | |
714eece7 | 170 | bcm43xx_leds_switch_all(bcm, 0); |
f222313a JL |
171 | } |
172 | ||
173 | void bcm43xx_leds_update(struct bcm43xx_private *bcm, int activity) | |
174 | { | |
175 | struct bcm43xx_led *led; | |
e9357c05 MB |
176 | struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); |
177 | struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); | |
f222313a | 178 | const int transferring = (jiffies - bcm->stats.last_tx) < BCM43xx_LED_XFER_THRES; |
dcfd720b | 179 | int i, turn_on; |
f222313a JL |
180 | unsigned long interval = 0; |
181 | u16 ledctl; | |
efa6a370 | 182 | unsigned long flags; |
f222313a | 183 | |
efa6a370 | 184 | spin_lock_irqsave(&bcm->leds_lock, flags); |
f222313a JL |
185 | ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL); |
186 | for (i = 0; i < BCM43xx_NR_LEDS; i++) { | |
187 | led = &(bcm->leds[i]); | |
f222313a | 188 | |
dcfd720b | 189 | turn_on = 0; |
f222313a | 190 | switch (led->behaviour) { |
dcfd720b MB |
191 | case BCM43xx_LED_INACTIVE: |
192 | continue; | |
f222313a | 193 | case BCM43xx_LED_OFF: |
df6d7c94 | 194 | case BCM43xx_LED_BCM4303_3: |
f222313a JL |
195 | break; |
196 | case BCM43xx_LED_ON: | |
197 | turn_on = 1; | |
198 | break; | |
199 | case BCM43xx_LED_ACTIVITY: | |
df6d7c94 | 200 | case BCM43xx_LED_BCM4303_0: |
f222313a JL |
201 | turn_on = activity; |
202 | break; | |
203 | case BCM43xx_LED_RADIO_ALL: | |
01917382 | 204 | turn_on = radio->enabled && bcm43xx_is_hw_radio_enabled(bcm); |
f222313a JL |
205 | break; |
206 | case BCM43xx_LED_RADIO_A: | |
df6d7c94 | 207 | case BCM43xx_LED_BCM4303_2: |
01917382 LF |
208 | turn_on = (radio->enabled && bcm43xx_is_hw_radio_enabled(bcm) && |
209 | phy->type == BCM43xx_PHYTYPE_A); | |
f222313a JL |
210 | break; |
211 | case BCM43xx_LED_RADIO_B: | |
df6d7c94 | 212 | case BCM43xx_LED_BCM4303_1: |
01917382 | 213 | turn_on = (radio->enabled && bcm43xx_is_hw_radio_enabled(bcm) && |
f222313a JL |
214 | (phy->type == BCM43xx_PHYTYPE_B || |
215 | phy->type == BCM43xx_PHYTYPE_G)); | |
216 | break; | |
217 | case BCM43xx_LED_MODE_BG: | |
01917382 | 218 | if (phy->type == BCM43xx_PHYTYPE_G && bcm43xx_is_hw_radio_enabled(bcm) && |
f222313a JL |
219 | 1/*FIXME: using G rates.*/) |
220 | turn_on = 1; | |
221 | break; | |
222 | case BCM43xx_LED_TRANSFER: | |
223 | if (transferring) | |
224 | bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_MEDIUM); | |
225 | else | |
226 | bcm43xx_led_blink_stop(led, 0); | |
227 | continue; | |
228 | case BCM43xx_LED_APTRANSFER: | |
229 | if (bcm->ieee->iw_mode == IW_MODE_MASTER) { | |
230 | if (transferring) { | |
231 | interval = BCM43xx_LEDBLINK_FAST; | |
232 | turn_on = 1; | |
233 | } | |
234 | } else { | |
235 | turn_on = 1; | |
236 | if (0/*TODO: not assoc*/) | |
237 | interval = BCM43xx_LEDBLINK_SLOW; | |
238 | else if (transferring) | |
239 | interval = BCM43xx_LEDBLINK_FAST; | |
240 | else | |
241 | turn_on = 0; | |
242 | } | |
243 | if (turn_on) | |
244 | bcm43xx_led_blink_start(led, interval); | |
245 | else | |
246 | bcm43xx_led_blink_stop(led, 0); | |
247 | continue; | |
248 | case BCM43xx_LED_WEIRD: | |
249 | //TODO | |
f222313a JL |
250 | break; |
251 | case BCM43xx_LED_ASSOC: | |
7c28ad2d | 252 | if (bcm->softmac->associnfo.associated) |
f222313a JL |
253 | turn_on = 1; |
254 | break; | |
dcfd720b MB |
255 | #ifdef CONFIG_BCM43XX_DEBUG |
256 | case BCM43xx_LED_TEST_BLINKSLOW: | |
257 | bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_SLOW); | |
258 | continue; | |
259 | case BCM43xx_LED_TEST_BLINKMEDIUM: | |
260 | bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_MEDIUM); | |
261 | continue; | |
262 | case BCM43xx_LED_TEST_BLINKFAST: | |
263 | bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_FAST); | |
264 | continue; | |
265 | #endif /* CONFIG_BCM43XX_DEBUG */ | |
f222313a | 266 | default: |
df6d7c94 LF |
267 | dprintkl(KERN_INFO PFX "Bad value in leds_update," |
268 | " led->behaviour: 0x%x\n", led->behaviour); | |
f222313a JL |
269 | }; |
270 | ||
271 | if (led->activelow) | |
272 | turn_on = !turn_on; | |
273 | if (turn_on) | |
274 | ledctl |= (1 << i); | |
275 | else | |
276 | ledctl &= ~(1 << i); | |
277 | } | |
278 | bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl); | |
efa6a370 | 279 | spin_unlock_irqrestore(&bcm->leds_lock, flags); |
f222313a JL |
280 | } |
281 | ||
714eece7 | 282 | void bcm43xx_leds_switch_all(struct bcm43xx_private *bcm, int on) |
f222313a JL |
283 | { |
284 | struct bcm43xx_led *led; | |
714eece7 | 285 | u16 ledctl; |
f222313a | 286 | int i; |
714eece7 | 287 | int bit_on; |
efa6a370 | 288 | unsigned long flags; |
f222313a | 289 | |
efa6a370 | 290 | spin_lock_irqsave(&bcm->leds_lock, flags); |
714eece7 | 291 | ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL); |
f222313a JL |
292 | for (i = 0; i < BCM43xx_NR_LEDS; i++) { |
293 | led = &(bcm->leds[i]); | |
294 | if (led->behaviour == BCM43xx_LED_INACTIVE) | |
295 | continue; | |
714eece7 MB |
296 | if (on) |
297 | bit_on = led->activelow ? 0 : 1; | |
298 | else | |
299 | bit_on = led->activelow ? 1 : 0; | |
300 | if (bit_on) | |
f222313a | 301 | ledctl |= (1 << i); |
714eece7 MB |
302 | else |
303 | ledctl &= ~(1 << i); | |
f222313a JL |
304 | } |
305 | bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl); | |
efa6a370 | 306 | spin_unlock_irqrestore(&bcm->leds_lock, flags); |
f222313a | 307 | } |