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