diff options
author | Nick Kossifidis <mick@madwifi-project.org> | 2009-08-09 20:29:02 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-08-14 09:13:56 -0400 |
commit | edd7fc7003f31da48d06e215a93ea966a22c2a03 (patch) | |
tree | 5627da2c85e9a5e28ec047c3914cdf75425187c9 /drivers | |
parent | d1cb0bdac180a4afdd3c001acb2618d2a62d9abe (diff) |
ath5k: Wakeup fixes
* Don't put chip to full sleep because there are problems during
wakeup. Instead hold MAC/Baseband on warm reset state via a new
function ath5k_hw_on_hold.
* Minor cleanups
Signed-off-by: Nick Kossifidis <mickflemm@gmail.com>
Tested-by: Ben Greear <greearb@candelatech.com>
Tested-by: Johannes Stezenbach <js@sig21.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/ath/ath5k/ath5k.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath5k/attach.c | 2 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath5k/base.c | 44 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath5k/reset.c | 155 |
4 files changed, 140 insertions, 62 deletions
diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index 91375113916b..1047a6cb1d92 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h | |||
@@ -1157,6 +1157,7 @@ extern void ath5k_unregister_leds(struct ath5k_softc *sc); | |||
1157 | 1157 | ||
1158 | /* Reset Functions */ | 1158 | /* Reset Functions */ |
1159 | extern int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial); | 1159 | extern int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial); |
1160 | extern int ath5k_hw_on_hold(struct ath5k_hw *ah); | ||
1160 | extern int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, struct ieee80211_channel *channel, bool change_channel); | 1161 | extern int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, struct ieee80211_channel *channel, bool change_channel); |
1161 | /* Power management functions */ | 1162 | /* Power management functions */ |
1162 | extern int ath5k_hw_set_power(struct ath5k_hw *ah, enum ath5k_power_mode mode, bool set_chip, u16 sleep_duration); | 1163 | extern int ath5k_hw_set_power(struct ath5k_hw *ah, enum ath5k_power_mode mode, bool set_chip, u16 sleep_duration); |
diff --git a/drivers/net/wireless/ath/ath5k/attach.c b/drivers/net/wireless/ath/ath5k/attach.c index 626306592cf8..238eeb7e166e 100644 --- a/drivers/net/wireless/ath/ath5k/attach.c +++ b/drivers/net/wireless/ath/ath5k/attach.c | |||
@@ -145,7 +145,7 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version) | |||
145 | goto err_free; | 145 | goto err_free; |
146 | 146 | ||
147 | /* Bring device out of sleep and reset it's units */ | 147 | /* Bring device out of sleep and reset it's units */ |
148 | ret = ath5k_hw_nic_wakeup(ah, CHANNEL_B, true); | 148 | ret = ath5k_hw_nic_wakeup(ah, 0, true); |
149 | if (ret) | 149 | if (ret) |
150 | goto err_free; | 150 | goto err_free; |
151 | 151 | ||
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 5d5028538ac2..0370cba8356c 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c | |||
@@ -2446,27 +2446,29 @@ ath5k_stop_hw(struct ath5k_softc *sc) | |||
2446 | ret = ath5k_stop_locked(sc); | 2446 | ret = ath5k_stop_locked(sc); |
2447 | if (ret == 0 && !test_bit(ATH_STAT_INVALID, sc->status)) { | 2447 | if (ret == 0 && !test_bit(ATH_STAT_INVALID, sc->status)) { |
2448 | /* | 2448 | /* |
2449 | * Set the chip in full sleep mode. Note that we are | 2449 | * Don't set the card in full sleep mode! |
2450 | * careful to do this only when bringing the interface | 2450 | * |
2451 | * completely to a stop. When the chip is in this state | 2451 | * a) When the device is in this state it must be carefully |
2452 | * it must be carefully woken up or references to | 2452 | * woken up or references to registers in the PCI clock |
2453 | * registers in the PCI clock domain may freeze the bus | 2453 | * domain may freeze the bus (and system). This varies |
2454 | * (and system). This varies by chip and is mostly an | 2454 | * by chip and is mostly an issue with newer parts |
2455 | * issue with newer parts that go to sleep more quickly. | 2455 | * (madwifi sources mentioned srev >= 0x78) that go to |
2456 | */ | 2456 | * sleep more quickly. |
2457 | if (sc->ah->ah_mac_srev >= 0x78) { | 2457 | * |
2458 | /* | 2458 | * b) On older chips full sleep results a weird behaviour |
2459 | * XXX | 2459 | * during wakeup. I tested various cards with srev < 0x78 |
2460 | * don't put newer MAC revisions > 7.8 to sleep because | 2460 | * and they don't wake up after module reload, a second |
2461 | * of the above mentioned problems | 2461 | * module reload is needed to bring the card up again. |
2462 | */ | 2462 | * |
2463 | ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "mac version > 7.8, " | 2463 | * Until we figure out what's going on don't enable |
2464 | "not putting device to sleep\n"); | 2464 | * full chip reset on any chip (this is what Legacy HAL |
2465 | } else { | 2465 | * and Sam's HAL do anyway). Instead Perform a full reset |
2466 | ATH5K_DBG(sc, ATH5K_DEBUG_RESET, | 2466 | * on the device (same as initial state after attach) and |
2467 | "putting device to full sleep\n"); | 2467 | * leave it idle (keep MAC/BB on warm reset) */ |
2468 | ath5k_hw_set_power(sc->ah, AR5K_PM_FULL_SLEEP, true, 0); | 2468 | ret = ath5k_hw_on_hold(sc->ah); |
2469 | } | 2469 | |
2470 | ATH5K_DBG(sc, ATH5K_DEBUG_RESET, | ||
2471 | "putting device to sleep\n"); | ||
2470 | } | 2472 | } |
2471 | ath5k_txbuf_free(sc, sc->bbuf); | 2473 | ath5k_txbuf_free(sc, sc->bbuf); |
2472 | 2474 | ||
diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c index 86733fdb4774..34e13c700849 100644 --- a/drivers/net/wireless/ath/ath5k/reset.c +++ b/drivers/net/wireless/ath/ath5k/reset.c | |||
@@ -258,29 +258,35 @@ int ath5k_hw_set_power(struct ath5k_hw *ah, enum ath5k_power_mode mode, | |||
258 | if (!set_chip) | 258 | if (!set_chip) |
259 | goto commit; | 259 | goto commit; |
260 | 260 | ||
261 | /* Preserve sleep duration */ | ||
262 | data = ath5k_hw_reg_read(ah, AR5K_SLEEP_CTL); | 261 | data = ath5k_hw_reg_read(ah, AR5K_SLEEP_CTL); |
262 | |||
263 | /* If card is down we 'll get 0xffff... so we | ||
264 | * need to clean this up before we write the register | ||
265 | */ | ||
263 | if (data & 0xffc00000) | 266 | if (data & 0xffc00000) |
264 | data = 0; | 267 | data = 0; |
265 | else | 268 | else |
266 | data = data & 0xfffcffff; | 269 | /* Preserve sleep duration etc */ |
270 | data = data & ~AR5K_SLEEP_CTL_SLE; | ||
267 | 271 | ||
268 | ath5k_hw_reg_write(ah, data, AR5K_SLEEP_CTL); | 272 | ath5k_hw_reg_write(ah, data | AR5K_SLEEP_CTL_SLE_WAKE, |
273 | AR5K_SLEEP_CTL); | ||
269 | udelay(15); | 274 | udelay(15); |
270 | 275 | ||
271 | for (i = 50; i > 0; i--) { | 276 | for (i = 200; i > 0; i--) { |
272 | /* Check if the chip did wake up */ | 277 | /* Check if the chip did wake up */ |
273 | if ((ath5k_hw_reg_read(ah, AR5K_PCICFG) & | 278 | if ((ath5k_hw_reg_read(ah, AR5K_PCICFG) & |
274 | AR5K_PCICFG_SPWR_DN) == 0) | 279 | AR5K_PCICFG_SPWR_DN) == 0) |
275 | break; | 280 | break; |
276 | 281 | ||
277 | /* Wait a bit and retry */ | 282 | /* Wait a bit and retry */ |
278 | udelay(200); | 283 | udelay(50); |
279 | ath5k_hw_reg_write(ah, data, AR5K_SLEEP_CTL); | 284 | ath5k_hw_reg_write(ah, data | AR5K_SLEEP_CTL_SLE_WAKE, |
285 | AR5K_SLEEP_CTL); | ||
280 | } | 286 | } |
281 | 287 | ||
282 | /* Fail if the chip didn't wake up */ | 288 | /* Fail if the chip didn't wake up */ |
283 | if (i <= 0) | 289 | if (i == 0) |
284 | return -EIO; | 290 | return -EIO; |
285 | 291 | ||
286 | break; | 292 | break; |
@@ -296,6 +302,64 @@ commit: | |||
296 | } | 302 | } |
297 | 303 | ||
298 | /* | 304 | /* |
305 | * Put device on hold | ||
306 | * | ||
307 | * Put MAC and Baseband on warm reset and | ||
308 | * keep that state (don't clean sleep control | ||
309 | * register). After this MAC and Baseband are | ||
310 | * disabled and a full reset is needed to come | ||
311 | * back. This way we save as much power as possible | ||
312 | * without puting the card on full sleep. | ||
313 | */ | ||
314 | int ath5k_hw_on_hold(struct ath5k_hw *ah) | ||
315 | { | ||
316 | struct pci_dev *pdev = ah->ah_sc->pdev; | ||
317 | u32 bus_flags; | ||
318 | int ret; | ||
319 | |||
320 | /* Make sure device is awake */ | ||
321 | ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0); | ||
322 | if (ret) { | ||
323 | ATH5K_ERR(ah->ah_sc, "failed to wakeup the MAC Chip\n"); | ||
324 | return ret; | ||
325 | } | ||
326 | |||
327 | /* | ||
328 | * Put chipset on warm reset... | ||
329 | * | ||
330 | * Note: puting PCI core on warm reset on PCI-E cards | ||
331 | * results card to hang and always return 0xffff... so | ||
332 | * we ingore that flag for PCI-E cards. On PCI cards | ||
333 | * this flag gets cleared after 64 PCI clocks. | ||
334 | */ | ||
335 | bus_flags = (pdev->is_pcie) ? 0 : AR5K_RESET_CTL_PCI; | ||
336 | |||
337 | if (ah->ah_version == AR5K_AR5210) { | ||
338 | ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU | | ||
339 | AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_DMA | | ||
340 | AR5K_RESET_CTL_PHY | AR5K_RESET_CTL_PCI); | ||
341 | mdelay(2); | ||
342 | } else { | ||
343 | ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU | | ||
344 | AR5K_RESET_CTL_BASEBAND | bus_flags); | ||
345 | } | ||
346 | |||
347 | if (ret) { | ||
348 | ATH5K_ERR(ah->ah_sc, "failed to put device on warm reset\n"); | ||
349 | return -EIO; | ||
350 | } | ||
351 | |||
352 | /* ...wakeup again!*/ | ||
353 | ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0); | ||
354 | if (ret) { | ||
355 | ATH5K_ERR(ah->ah_sc, "failed to put device on hold\n"); | ||
356 | return ret; | ||
357 | } | ||
358 | |||
359 | return ret; | ||
360 | } | ||
361 | |||
362 | /* | ||
299 | * Bring up MAC + PHY Chips and program PLL | 363 | * Bring up MAC + PHY Chips and program PLL |
300 | * TODO: Half/Quarter rate support | 364 | * TODO: Half/Quarter rate support |
301 | */ | 365 | */ |
@@ -318,6 +382,50 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial) | |||
318 | return ret; | 382 | return ret; |
319 | } | 383 | } |
320 | 384 | ||
385 | /* | ||
386 | * Put chipset on warm reset... | ||
387 | * | ||
388 | * Note: puting PCI core on warm reset on PCI-E cards | ||
389 | * results card to hang and always return 0xffff... so | ||
390 | * we ingore that flag for PCI-E cards. On PCI cards | ||
391 | * this flag gets cleared after 64 PCI clocks. | ||
392 | */ | ||
393 | bus_flags = (pdev->is_pcie) ? 0 : AR5K_RESET_CTL_PCI; | ||
394 | |||
395 | if (ah->ah_version == AR5K_AR5210) { | ||
396 | ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU | | ||
397 | AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_DMA | | ||
398 | AR5K_RESET_CTL_PHY | AR5K_RESET_CTL_PCI); | ||
399 | mdelay(2); | ||
400 | } else { | ||
401 | ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU | | ||
402 | AR5K_RESET_CTL_BASEBAND | bus_flags); | ||
403 | } | ||
404 | |||
405 | if (ret) { | ||
406 | ATH5K_ERR(ah->ah_sc, "failed to reset the MAC Chip\n"); | ||
407 | return -EIO; | ||
408 | } | ||
409 | |||
410 | /* ...wakeup again!...*/ | ||
411 | ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0); | ||
412 | if (ret) { | ||
413 | ATH5K_ERR(ah->ah_sc, "failed to resume the MAC Chip\n"); | ||
414 | return ret; | ||
415 | } | ||
416 | |||
417 | /* ...clear reset control register and pull device out of | ||
418 | * warm reset */ | ||
419 | if (ath5k_hw_nic_reset(ah, 0)) { | ||
420 | ATH5K_ERR(ah->ah_sc, "failed to warm reset the MAC Chip\n"); | ||
421 | return -EIO; | ||
422 | } | ||
423 | |||
424 | /* On initialization skip PLL programming since we don't have | ||
425 | * a channel / mode set yet */ | ||
426 | if (initial) | ||
427 | return 0; | ||
428 | |||
321 | if (ah->ah_version != AR5K_AR5210) { | 429 | if (ah->ah_version != AR5K_AR5210) { |
322 | /* | 430 | /* |
323 | * Get channel mode flags | 431 | * Get channel mode flags |
@@ -383,39 +491,6 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial) | |||
383 | AR5K_PHY_TURBO); | 491 | AR5K_PHY_TURBO); |
384 | } | 492 | } |
385 | 493 | ||
386 | /* reseting PCI on PCI-E cards results card to hang | ||
387 | * and always return 0xffff... so we ingore that flag | ||
388 | * for PCI-E cards */ | ||
389 | bus_flags = (pdev->is_pcie) ? 0 : AR5K_RESET_CTL_PCI; | ||
390 | |||
391 | /* Reset chipset */ | ||
392 | if (ah->ah_version == AR5K_AR5210) { | ||
393 | ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU | | ||
394 | AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_DMA | | ||
395 | AR5K_RESET_CTL_PHY | AR5K_RESET_CTL_PCI); | ||
396 | mdelay(2); | ||
397 | } else { | ||
398 | ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU | | ||
399 | AR5K_RESET_CTL_BASEBAND | bus_flags); | ||
400 | } | ||
401 | if (ret) { | ||
402 | ATH5K_ERR(ah->ah_sc, "failed to reset the MAC Chip\n"); | ||
403 | return -EIO; | ||
404 | } | ||
405 | |||
406 | /* ...wakeup again!*/ | ||
407 | ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0); | ||
408 | if (ret) { | ||
409 | ATH5K_ERR(ah->ah_sc, "failed to resume the MAC Chip\n"); | ||
410 | return ret; | ||
411 | } | ||
412 | |||
413 | /* ...final warm reset */ | ||
414 | if (ath5k_hw_nic_reset(ah, 0)) { | ||
415 | ATH5K_ERR(ah->ah_sc, "failed to warm reset the MAC Chip\n"); | ||
416 | return -EIO; | ||
417 | } | ||
418 | |||
419 | if (ah->ah_version != AR5K_AR5210) { | 494 | if (ah->ah_version != AR5K_AR5210) { |
420 | 495 | ||
421 | /* ...update PLL if needed */ | 496 | /* ...update PLL if needed */ |