blob: 8f198befba390e1f325ee03f77bdc04b5896bc44 [file] [log] [blame]
John W. Linvillef2223132006-01-23 16:59:58 -05001/*
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"
Larry Finger01917382006-12-30 23:30:32 -060029#include "bcm43xx_radio.h"
John W. Linvillef2223132006-01-23 16:59:58 -050030#include "bcm43xx.h"
31
32#include <asm/bitops.h>
33
34
35static void bcm43xx_led_changestate(struct bcm43xx_led *led)
36{
37 struct bcm43xx_private *bcm = led->bcm;
38 const int index = bcm43xx_led_index(led);
Michael Bueschdcfd7202006-02-12 20:25:55 +010039 const u16 mask = (1 << index);
John W. Linvillef2223132006-01-23 16:59:58 -050040 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);
Michael Bueschdcfd7202006-02-12 20:25:55 +010045 ledctl = (ledctl & mask) ? (ledctl & ~mask) : (ledctl | mask);
John W. Linvillef2223132006-01-23 16:59:58 -050046 bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl);
47}
48
49static 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
Michael Bueschefa6a372006-06-27 21:38:40 +020055 spin_lock_irqsave(&bcm->leds_lock, flags);
John W. Linvillef2223132006-01-23 16:59:58 -050056 if (led->blink_interval) {
57 bcm43xx_led_changestate(led);
58 mod_timer(&led->blink_timer, jiffies + led->blink_interval);
59 }
Michael Bueschefa6a372006-06-27 21:38:40 +020060 spin_unlock_irqrestore(&bcm->leds_lock, flags);
John W. Linvillef2223132006-01-23 16:59:58 -050061}
62
63static void bcm43xx_led_blink_start(struct bcm43xx_led *led,
64 unsigned long interval)
65{
Michael Bueschdcfd7202006-02-12 20:25:55 +010066 if (led->blink_interval)
67 return;
John W. Linvillef2223132006-01-23 16:59:58 -050068 led->blink_interval = interval;
69 bcm43xx_led_changestate(led);
70 led->blink_timer.expires = jiffies + interval;
71 add_timer(&led->blink_timer);
72}
73
74static 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
Michael Bueschdcfd7202006-02-12 20:25:55 +010098static 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;
Larry Finger01917382006-12-30 23:30:32 -0600112 led->activelow = 1;
Michael Bueschdcfd7202006-02-12 20:25:55 +0100113 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
John W. Linvillef2223132006-01-23 16:59:58 -0500132int 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;
Michael Bueschdcfd7202006-02-12 20:25:55 +0100146 setup_timer(&led->blink_timer,
147 bcm43xx_led_blink,
148 (unsigned long)led);
John W. Linvillef2223132006-01-23 16:59:58 -0500149
150 if (sprom[i] == 0xFF) {
Michael Bueschdcfd7202006-02-12 20:25:55 +0100151 bcm43xx_led_init_hardcoded(bcm, led, i);
John W. Linvillef2223132006-01-23 16:59:58 -0500152 } 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
161void 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 }
Michael Buesch714eece2006-03-18 21:28:46 +0100170 bcm43xx_leds_switch_all(bcm, 0);
John W. Linvillef2223132006-01-23 16:59:58 -0500171}
172
173void bcm43xx_leds_update(struct bcm43xx_private *bcm, int activity)
174{
175 struct bcm43xx_led *led;
Michael Buesche9357c02006-03-13 19:27:34 +0100176 struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
177 struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
John W. Linvillef2223132006-01-23 16:59:58 -0500178 const int transferring = (jiffies - bcm->stats.last_tx) < BCM43xx_LED_XFER_THRES;
Michael Bueschdcfd7202006-02-12 20:25:55 +0100179 int i, turn_on;
John W. Linvillef2223132006-01-23 16:59:58 -0500180 unsigned long interval = 0;
181 u16 ledctl;
Michael Bueschefa6a372006-06-27 21:38:40 +0200182 unsigned long flags;
John W. Linvillef2223132006-01-23 16:59:58 -0500183
Michael Bueschefa6a372006-06-27 21:38:40 +0200184 spin_lock_irqsave(&bcm->leds_lock, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500185 ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL);
186 for (i = 0; i < BCM43xx_NR_LEDS; i++) {
187 led = &(bcm->leds[i]);
John W. Linvillef2223132006-01-23 16:59:58 -0500188
Michael Bueschdcfd7202006-02-12 20:25:55 +0100189 turn_on = 0;
John W. Linvillef2223132006-01-23 16:59:58 -0500190 switch (led->behaviour) {
Michael Bueschdcfd7202006-02-12 20:25:55 +0100191 case BCM43xx_LED_INACTIVE:
192 continue;
John W. Linvillef2223132006-01-23 16:59:58 -0500193 case BCM43xx_LED_OFF:
Larry Fingerdf6d7c92006-10-17 23:38:26 -0500194 case BCM43xx_LED_BCM4303_3:
John W. Linvillef2223132006-01-23 16:59:58 -0500195 break;
196 case BCM43xx_LED_ON:
197 turn_on = 1;
198 break;
199 case BCM43xx_LED_ACTIVITY:
Larry Fingerdf6d7c92006-10-17 23:38:26 -0500200 case BCM43xx_LED_BCM4303_0:
John W. Linvillef2223132006-01-23 16:59:58 -0500201 turn_on = activity;
202 break;
203 case BCM43xx_LED_RADIO_ALL:
Larry Finger01917382006-12-30 23:30:32 -0600204 turn_on = radio->enabled && bcm43xx_is_hw_radio_enabled(bcm);
John W. Linvillef2223132006-01-23 16:59:58 -0500205 break;
206 case BCM43xx_LED_RADIO_A:
Larry Fingerdf6d7c92006-10-17 23:38:26 -0500207 case BCM43xx_LED_BCM4303_2:
Larry Finger01917382006-12-30 23:30:32 -0600208 turn_on = (radio->enabled && bcm43xx_is_hw_radio_enabled(bcm) &&
209 phy->type == BCM43xx_PHYTYPE_A);
John W. Linvillef2223132006-01-23 16:59:58 -0500210 break;
211 case BCM43xx_LED_RADIO_B:
Larry Fingerdf6d7c92006-10-17 23:38:26 -0500212 case BCM43xx_LED_BCM4303_1:
Larry Finger01917382006-12-30 23:30:32 -0600213 turn_on = (radio->enabled && bcm43xx_is_hw_radio_enabled(bcm) &&
John W. Linvillef2223132006-01-23 16:59:58 -0500214 (phy->type == BCM43xx_PHYTYPE_B ||
215 phy->type == BCM43xx_PHYTYPE_G));
216 break;
217 case BCM43xx_LED_MODE_BG:
Larry Finger01917382006-12-30 23:30:32 -0600218 if (phy->type == BCM43xx_PHYTYPE_G && bcm43xx_is_hw_radio_enabled(bcm) &&
John W. Linvillef2223132006-01-23 16:59:58 -0500219 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
John W. Linvillef2223132006-01-23 16:59:58 -0500250 break;
251 case BCM43xx_LED_ASSOC:
Michael Buesch7c28ad22006-09-27 15:26:33 +0300252 if (bcm->softmac->associnfo.associated)
John W. Linvillef2223132006-01-23 16:59:58 -0500253 turn_on = 1;
254 break;
Michael Bueschdcfd7202006-02-12 20:25:55 +0100255#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 */
John W. Linvillef2223132006-01-23 16:59:58 -0500266 default:
Larry Fingerdf6d7c92006-10-17 23:38:26 -0500267 dprintkl(KERN_INFO PFX "Bad value in leds_update,"
268 " led->behaviour: 0x%x\n", led->behaviour);
John W. Linvillef2223132006-01-23 16:59:58 -0500269 };
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);
Michael Bueschefa6a372006-06-27 21:38:40 +0200279 spin_unlock_irqrestore(&bcm->leds_lock, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500280}
281
Michael Buesch714eece2006-03-18 21:28:46 +0100282void bcm43xx_leds_switch_all(struct bcm43xx_private *bcm, int on)
John W. Linvillef2223132006-01-23 16:59:58 -0500283{
284 struct bcm43xx_led *led;
Michael Buesch714eece2006-03-18 21:28:46 +0100285 u16 ledctl;
John W. Linvillef2223132006-01-23 16:59:58 -0500286 int i;
Michael Buesch714eece2006-03-18 21:28:46 +0100287 int bit_on;
Michael Bueschefa6a372006-06-27 21:38:40 +0200288 unsigned long flags;
John W. Linvillef2223132006-01-23 16:59:58 -0500289
Michael Bueschefa6a372006-06-27 21:38:40 +0200290 spin_lock_irqsave(&bcm->leds_lock, flags);
Michael Buesch714eece2006-03-18 21:28:46 +0100291 ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL);
John W. Linvillef2223132006-01-23 16:59:58 -0500292 for (i = 0; i < BCM43xx_NR_LEDS; i++) {
293 led = &(bcm->leds[i]);
294 if (led->behaviour == BCM43xx_LED_INACTIVE)
295 continue;
Michael Buesch714eece2006-03-18 21:28:46 +0100296 if (on)
297 bit_on = led->activelow ? 0 : 1;
298 else
299 bit_on = led->activelow ? 1 : 0;
300 if (bit_on)
John W. Linvillef2223132006-01-23 16:59:58 -0500301 ledctl |= (1 << i);
Michael Buesch714eece2006-03-18 21:28:46 +0100302 else
303 ledctl &= ~(1 << i);
John W. Linvillef2223132006-01-23 16:59:58 -0500304 }
305 bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl);
Michael Bueschefa6a372006-06-27 21:38:40 +0200306 spin_unlock_irqrestore(&bcm->leds_lock, flags);
John W. Linvillef2223132006-01-23 16:59:58 -0500307}