[PATCH] bcm43xx: Interrogate hardware-enable switch and update LEDs
[linux-3.10.git] / drivers / net / wireless / bcm43xx / bcm43xx_leds.c
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"
29 #include "bcm43xx_radio.h"
30 #include "bcm43xx.h"
31
32 #include <asm/bitops.h>
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);
39         const u16 mask = (1 << index);
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);
45         ledctl = (ledctl & mask) ? (ledctl & ~mask) : (ledctl | mask);
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
55         spin_lock_irqsave(&bcm->leds_lock, flags);
56         if (led->blink_interval) {
57                 bcm43xx_led_changestate(led);
58                 mod_timer(&led->blink_timer, jiffies + led->blink_interval);
59         }
60         spin_unlock_irqrestore(&bcm->leds_lock, flags);
61 }
62
63 static void bcm43xx_led_blink_start(struct bcm43xx_led *led,
64                                     unsigned long interval)
65 {
66         if (led->blink_interval)
67                 return;
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
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;
112                 led->activelow = 1;
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
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;
146                 setup_timer(&led->blink_timer,
147                             bcm43xx_led_blink,
148                             (unsigned long)led);
149
150                 if (sprom[i] == 0xFF) {
151                         bcm43xx_led_init_hardcoded(bcm, led, i);
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         }
170         bcm43xx_leds_switch_all(bcm, 0);
171 }
172
173 void bcm43xx_leds_update(struct bcm43xx_private *bcm, int activity)
174 {
175         struct bcm43xx_led *led;
176         struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
177         struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
178         const int transferring = (jiffies - bcm->stats.last_tx) < BCM43xx_LED_XFER_THRES;
179         int i, turn_on;
180         unsigned long interval = 0;
181         u16 ledctl;
182         unsigned long flags;
183
184         spin_lock_irqsave(&bcm->leds_lock, flags);
185         ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL);
186         for (i = 0; i < BCM43xx_NR_LEDS; i++) {
187                 led = &(bcm->leds[i]);
188
189                 turn_on = 0;
190                 switch (led->behaviour) {
191                 case BCM43xx_LED_INACTIVE:
192                         continue;
193                 case BCM43xx_LED_OFF:
194                 case BCM43xx_LED_BCM4303_3:
195                         break;
196                 case BCM43xx_LED_ON:
197                         turn_on = 1;
198                         break;
199                 case BCM43xx_LED_ACTIVITY:
200                 case BCM43xx_LED_BCM4303_0:
201                         turn_on = activity;
202                         break;
203                 case BCM43xx_LED_RADIO_ALL:
204                         turn_on = radio->enabled && bcm43xx_is_hw_radio_enabled(bcm);
205                         break;
206                 case BCM43xx_LED_RADIO_A:
207                 case BCM43xx_LED_BCM4303_2:
208                         turn_on = (radio->enabled && bcm43xx_is_hw_radio_enabled(bcm) &&
209                                    phy->type == BCM43xx_PHYTYPE_A);
210                         break;
211                 case BCM43xx_LED_RADIO_B:
212                 case BCM43xx_LED_BCM4303_1:
213                         turn_on = (radio->enabled && bcm43xx_is_hw_radio_enabled(bcm) &&
214                                    (phy->type == BCM43xx_PHYTYPE_B ||
215                                     phy->type == BCM43xx_PHYTYPE_G));
216                         break;
217                 case BCM43xx_LED_MODE_BG:
218                         if (phy->type == BCM43xx_PHYTYPE_G && bcm43xx_is_hw_radio_enabled(bcm) &&
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
250                         break;
251                 case BCM43xx_LED_ASSOC:
252                         if (bcm->softmac->associnfo.associated)
253                                 turn_on = 1;
254                         break;
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 */
266                 default:
267                         dprintkl(KERN_INFO PFX "Bad value in leds_update,"
268                                 " led->behaviour: 0x%x\n", led->behaviour);
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);
279         spin_unlock_irqrestore(&bcm->leds_lock, flags);
280 }
281
282 void bcm43xx_leds_switch_all(struct bcm43xx_private *bcm, int on)
283 {
284         struct bcm43xx_led *led;
285         u16 ledctl;
286         int i;
287         int bit_on;
288         unsigned long flags;
289
290         spin_lock_irqsave(&bcm->leds_lock, flags);
291         ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL);
292         for (i = 0; i < BCM43xx_NR_LEDS; i++) {
293                 led = &(bcm->leds[i]);
294                 if (led->behaviour == BCM43xx_LED_INACTIVE)
295                         continue;
296                 if (on)
297                         bit_on = led->activelow ? 0 : 1;
298                 else
299                         bit_on = led->activelow ? 1 : 0;
300                 if (bit_on)
301                         ledctl |= (1 << i);
302                 else
303                         ledctl &= ~(1 << i);
304         }
305         bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl);
306         spin_unlock_irqrestore(&bcm->leds_lock, flags);
307 }