• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500-V1.0.1.40_1.0.68/src/linux/linux-2.6/drivers/net/wireless/bcm43xx/
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
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);
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
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
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
63static 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
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
98static 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
132int 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
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	}
170	bcm43xx_leds_switch_all(bcm, 0);
171}
172
173void 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)
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
282void 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}
308