bcm43xx_leds.c 7.49 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
/*

  Broadcom BCM43xx wireless driver

  Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
                     Stefano Brivio <st3@riseup.net>
                     Michael Buesch <mbuesch@freenet.de>
                     Danny van Dyk <kugelfang@gentoo.org>
                     Andreas Jaggi <andreas.jaggi@waterwave.ch>

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; see the file COPYING.  If not, write to
  the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
  Boston, MA 02110-1301, USA.

*/

#include "bcm43xx_leds.h"
#include "bcm43xx.h"

#include <asm/bitops.h>


static void bcm43xx_led_changestate(struct bcm43xx_led *led)
{
	struct bcm43xx_private *bcm = led->bcm;
	const int index = bcm43xx_led_index(led);
38
	const u16 mask = (1 << index);
39 40 41 42 43
	u16 ledctl;

	assert(index >= 0 && index < BCM43xx_NR_LEDS);
	assert(led->blink_interval);
	ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL);
44
	ledctl = (ledctl & mask) ? (ledctl & ~mask) : (ledctl | mask);
45 46 47 48 49 50 51 52 53
	bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl);
}

static void bcm43xx_led_blink(unsigned long d)
{
	struct bcm43xx_led *led = (struct bcm43xx_led *)d;
	struct bcm43xx_private *bcm = led->bcm;
	unsigned long flags;

54
	bcm43xx_lock_irqonly(bcm, flags);
55 56 57 58
	if (led->blink_interval) {
		bcm43xx_led_changestate(led);
		mod_timer(&led->blink_timer, jiffies + led->blink_interval);
	}
59
	bcm43xx_unlock_irqonly(bcm, flags);
60 61 62 63 64
}

static void bcm43xx_led_blink_start(struct bcm43xx_led *led,
				    unsigned long interval)
{
65 66
	if (led->blink_interval)
		return;
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
	led->blink_interval = interval;
	bcm43xx_led_changestate(led);
	led->blink_timer.expires = jiffies + interval;
	add_timer(&led->blink_timer);
}

static void bcm43xx_led_blink_stop(struct bcm43xx_led *led, int sync)
{
	struct bcm43xx_private *bcm = led->bcm;
	const int index = bcm43xx_led_index(led);
	u16 ledctl;

	if (!led->blink_interval)
		return;
	if (unlikely(sync))
		del_timer_sync(&led->blink_timer);
	else
		del_timer(&led->blink_timer);
	led->blink_interval = 0;

	/* Make sure the LED is turned off. */
	assert(index >= 0 && index < BCM43xx_NR_LEDS);
	ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL);
	if (led->activelow)
		ledctl |= (1 << index);
	else
		ledctl &= ~(1 << index);
	bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl);
}

97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
static void bcm43xx_led_init_hardcoded(struct bcm43xx_private *bcm,
				       struct bcm43xx_led *led,
				       int led_index)
{
	/* This function is called, if the behaviour (and activelow)
	 * information for a LED is missing in the SPROM.
	 * We hardcode the behaviour values for various devices here.
	 * Note that the BCM43xx_LED_TEST_XXX behaviour values can
	 * be used to figure out which led is mapped to which index.
	 */

	switch (led_index) {
	case 0:
		led->behaviour = BCM43xx_LED_ACTIVITY;
		if (bcm->board_vendor == PCI_VENDOR_ID_COMPAQ)
			led->behaviour = BCM43xx_LED_RADIO_ALL;
		break;
	case 1:
		led->behaviour = BCM43xx_LED_RADIO_B;
		if (bcm->board_vendor == PCI_VENDOR_ID_ASUSTEK)
			led->behaviour = BCM43xx_LED_ASSOC;
		break;
	case 2:
		led->behaviour = BCM43xx_LED_RADIO_A;
		break;
	case 3:
		led->behaviour = BCM43xx_LED_OFF;
		break;
	default:
		assert(0);
	}
}

130 131 132 133 134 135 136 137 138 139 140 141 142 143
int bcm43xx_leds_init(struct bcm43xx_private *bcm)
{
	struct bcm43xx_led *led;
	u8 sprom[4];
	int i;

	sprom[0] = bcm->sprom.wl0gpio0;
	sprom[1] = bcm->sprom.wl0gpio1;
	sprom[2] = bcm->sprom.wl0gpio2;
	sprom[3] = bcm->sprom.wl0gpio3;

	for (i = 0; i < BCM43xx_NR_LEDS; i++) {
		led = &(bcm->leds[i]);
		led->bcm = bcm;
144 145 146
		setup_timer(&led->blink_timer,
			    bcm43xx_led_blink,
			    (unsigned long)led);
147 148

		if (sprom[i] == 0xFF) {
149
			bcm43xx_led_init_hardcoded(bcm, led, i);
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
		} else {
			led->behaviour = sprom[i] & BCM43xx_LED_BEHAVIOUR;
			led->activelow = !!(sprom[i] & BCM43xx_LED_ACTIVELOW);
		}
	}

	return 0;
}

void bcm43xx_leds_exit(struct bcm43xx_private *bcm)
{
	struct bcm43xx_led *led;
	int i;

	for (i = 0; i < BCM43xx_NR_LEDS; i++) {
		led = &(bcm->leds[i]);
		bcm43xx_led_blink_stop(led, 1);
	}
168
	bcm43xx_leds_switch_all(bcm, 0);
169 170 171 172 173
}

void bcm43xx_leds_update(struct bcm43xx_private *bcm, int activity)
{
	struct bcm43xx_led *led;
174 175
	struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
	struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
176
	const int transferring = (jiffies - bcm->stats.last_tx) < BCM43xx_LED_XFER_THRES;
177
	int i, turn_on;
178 179 180 181 182 183 184
	unsigned long interval = 0;
	u16 ledctl;

	ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL);
	for (i = 0; i < BCM43xx_NR_LEDS; i++) {
		led = &(bcm->leds[i]);

185
		turn_on = 0;
186
		switch (led->behaviour) {
187 188
		case BCM43xx_LED_INACTIVE:
			continue;
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
		case BCM43xx_LED_OFF:
			break;
		case BCM43xx_LED_ON:
			turn_on = 1;
			break;
		case BCM43xx_LED_ACTIVITY:
			turn_on = activity;
			break;
		case BCM43xx_LED_RADIO_ALL:
			turn_on = radio->enabled;
			break;
		case BCM43xx_LED_RADIO_A:
			turn_on = (radio->enabled && phy->type == BCM43xx_PHYTYPE_A);
			break;
		case BCM43xx_LED_RADIO_B:
			turn_on = (radio->enabled &&
				   (phy->type == BCM43xx_PHYTYPE_B ||
				    phy->type == BCM43xx_PHYTYPE_G));
			break;
		case BCM43xx_LED_MODE_BG:
			if (phy->type == BCM43xx_PHYTYPE_G &&
			    1/*FIXME: using G rates.*/)
				turn_on = 1;
			break;
		case BCM43xx_LED_TRANSFER:
			if (transferring)
				bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_MEDIUM);
			else
				bcm43xx_led_blink_stop(led, 0);
			continue;
		case BCM43xx_LED_APTRANSFER:
			if (bcm->ieee->iw_mode == IW_MODE_MASTER) {
				if (transferring) {
					interval = BCM43xx_LEDBLINK_FAST;
					turn_on = 1;
				}
			} else {
				turn_on = 1;
				if (0/*TODO: not assoc*/)
					interval = BCM43xx_LEDBLINK_SLOW;
				else if (transferring)
					interval = BCM43xx_LEDBLINK_FAST;
				else
					turn_on = 0;
			}
			if (turn_on)
				bcm43xx_led_blink_start(led, interval);
			else
				bcm43xx_led_blink_stop(led, 0);
			continue;
		case BCM43xx_LED_WEIRD:
			//TODO
			break;
		case BCM43xx_LED_ASSOC:
243
			if (bcm->softmac->associated)
244 245
				turn_on = 1;
			break;
246 247 248 249 250 251 252 253 254 255 256
#ifdef CONFIG_BCM43XX_DEBUG
		case BCM43xx_LED_TEST_BLINKSLOW:
			bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_SLOW);
			continue;
		case BCM43xx_LED_TEST_BLINKMEDIUM:
			bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_MEDIUM);
			continue;
		case BCM43xx_LED_TEST_BLINKFAST:
			bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_FAST);
			continue;
#endif /* CONFIG_BCM43XX_DEBUG */
257 258 259 260 261 262 263 264 265 266 267 268 269 270
		default:
			assert(0);
		};

		if (led->activelow)
			turn_on = !turn_on;
		if (turn_on)
			ledctl |= (1 << i);
		else
			ledctl &= ~(1 << i);
	}
	bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl);
}

271
void bcm43xx_leds_switch_all(struct bcm43xx_private *bcm, int on)
272 273
{
	struct bcm43xx_led *led;
274
	u16 ledctl;
275
	int i;
276
	int bit_on;
277

278
	ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL);
279 280 281 282
	for (i = 0; i < BCM43xx_NR_LEDS; i++) {
		led = &(bcm->leds[i]);
		if (led->behaviour == BCM43xx_LED_INACTIVE)
			continue;
283 284 285 286 287
		if (on)
			bit_on = led->activelow ? 0 : 1;
		else
			bit_on = led->activelow ? 1 : 0;
		if (bit_on)
288
			ledctl |= (1 << i);
289 290
		else
			ledctl &= ~(1 << i);
291 292 293
	}
	bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl);
}