Commit edd7fc70 authored by Nick Kossifidis's avatar Nick Kossifidis Committed by John W. Linville

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: default avatarNick Kossifidis <mickflemm@gmail.com>
Tested-by: default avatarBen Greear <greearb@candelatech.com>
Tested-by: default avatarJohannes Stezenbach <js@sig21.net>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent d1cb0bda
...@@ -1157,6 +1157,7 @@ extern void ath5k_unregister_leds(struct ath5k_softc *sc); ...@@ -1157,6 +1157,7 @@ extern void ath5k_unregister_leds(struct ath5k_softc *sc);
/* Reset Functions */ /* Reset Functions */
extern int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial); extern int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial);
extern int ath5k_hw_on_hold(struct ath5k_hw *ah);
extern int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, struct ieee80211_channel *channel, bool change_channel); extern int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, struct ieee80211_channel *channel, bool change_channel);
/* Power management functions */ /* Power management functions */
extern int ath5k_hw_set_power(struct ath5k_hw *ah, enum ath5k_power_mode mode, bool set_chip, u16 sleep_duration); extern int ath5k_hw_set_power(struct ath5k_hw *ah, enum ath5k_power_mode mode, bool set_chip, u16 sleep_duration);
......
...@@ -145,7 +145,7 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version) ...@@ -145,7 +145,7 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version)
goto err_free; goto err_free;
/* Bring device out of sleep and reset it's units */ /* Bring device out of sleep and reset it's units */
ret = ath5k_hw_nic_wakeup(ah, CHANNEL_B, true); ret = ath5k_hw_nic_wakeup(ah, 0, true);
if (ret) if (ret)
goto err_free; goto err_free;
......
...@@ -2446,27 +2446,29 @@ ath5k_stop_hw(struct ath5k_softc *sc) ...@@ -2446,27 +2446,29 @@ ath5k_stop_hw(struct ath5k_softc *sc)
ret = ath5k_stop_locked(sc); ret = ath5k_stop_locked(sc);
if (ret == 0 && !test_bit(ATH_STAT_INVALID, sc->status)) { if (ret == 0 && !test_bit(ATH_STAT_INVALID, sc->status)) {
/* /*
* Set the chip in full sleep mode. Note that we are * Don't set the card in full sleep mode!
* careful to do this only when bringing the interface *
* completely to a stop. When the chip is in this state * a) When the device is in this state it must be carefully
* it must be carefully woken up or references to * woken up or references to registers in the PCI clock
* registers in the PCI clock domain may freeze the bus * domain may freeze the bus (and system). This varies
* (and system). This varies by chip and is mostly an * by chip and is mostly an issue with newer parts
* issue with newer parts that go to sleep more quickly. * (madwifi sources mentioned srev >= 0x78) that go to
*/ * sleep more quickly.
if (sc->ah->ah_mac_srev >= 0x78) { *
/* * b) On older chips full sleep results a weird behaviour
* XXX * during wakeup. I tested various cards with srev < 0x78
* don't put newer MAC revisions > 7.8 to sleep because * and they don't wake up after module reload, a second
* of the above mentioned problems * module reload is needed to bring the card up again.
*/ *
ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "mac version > 7.8, " * Until we figure out what's going on don't enable
"not putting device to sleep\n"); * full chip reset on any chip (this is what Legacy HAL
} else { * and Sam's HAL do anyway). Instead Perform a full reset
ATH5K_DBG(sc, ATH5K_DEBUG_RESET, * on the device (same as initial state after attach) and
"putting device to full sleep\n"); * leave it idle (keep MAC/BB on warm reset) */
ath5k_hw_set_power(sc->ah, AR5K_PM_FULL_SLEEP, true, 0); ret = ath5k_hw_on_hold(sc->ah);
}
ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
"putting device to sleep\n");
} }
ath5k_txbuf_free(sc, sc->bbuf); ath5k_txbuf_free(sc, sc->bbuf);
......
...@@ -258,29 +258,35 @@ int ath5k_hw_set_power(struct ath5k_hw *ah, enum ath5k_power_mode mode, ...@@ -258,29 +258,35 @@ int ath5k_hw_set_power(struct ath5k_hw *ah, enum ath5k_power_mode mode,
if (!set_chip) if (!set_chip)
goto commit; goto commit;
/* Preserve sleep duration */
data = ath5k_hw_reg_read(ah, AR5K_SLEEP_CTL); data = ath5k_hw_reg_read(ah, AR5K_SLEEP_CTL);
/* If card is down we 'll get 0xffff... so we
* need to clean this up before we write the register
*/
if (data & 0xffc00000) if (data & 0xffc00000)
data = 0; data = 0;
else else
data = data & 0xfffcffff; /* Preserve sleep duration etc */
data = data & ~AR5K_SLEEP_CTL_SLE;
ath5k_hw_reg_write(ah, data, AR5K_SLEEP_CTL); ath5k_hw_reg_write(ah, data | AR5K_SLEEP_CTL_SLE_WAKE,
AR5K_SLEEP_CTL);
udelay(15); udelay(15);
for (i = 50; i > 0; i--) { for (i = 200; i > 0; i--) {
/* Check if the chip did wake up */ /* Check if the chip did wake up */
if ((ath5k_hw_reg_read(ah, AR5K_PCICFG) & if ((ath5k_hw_reg_read(ah, AR5K_PCICFG) &
AR5K_PCICFG_SPWR_DN) == 0) AR5K_PCICFG_SPWR_DN) == 0)
break; break;
/* Wait a bit and retry */ /* Wait a bit and retry */
udelay(200); udelay(50);
ath5k_hw_reg_write(ah, data, AR5K_SLEEP_CTL); ath5k_hw_reg_write(ah, data | AR5K_SLEEP_CTL_SLE_WAKE,
AR5K_SLEEP_CTL);
} }
/* Fail if the chip didn't wake up */ /* Fail if the chip didn't wake up */
if (i <= 0) if (i == 0)
return -EIO; return -EIO;
break; break;
...@@ -295,6 +301,64 @@ commit: ...@@ -295,6 +301,64 @@ commit:
return 0; return 0;
} }
/*
* Put device on hold
*
* Put MAC and Baseband on warm reset and
* keep that state (don't clean sleep control
* register). After this MAC and Baseband are
* disabled and a full reset is needed to come
* back. This way we save as much power as possible
* without puting the card on full sleep.
*/
int ath5k_hw_on_hold(struct ath5k_hw *ah)
{
struct pci_dev *pdev = ah->ah_sc->pdev;
u32 bus_flags;
int ret;
/* Make sure device is awake */
ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0);
if (ret) {
ATH5K_ERR(ah->ah_sc, "failed to wakeup the MAC Chip\n");
return ret;
}
/*
* Put chipset on warm reset...
*
* Note: puting PCI core on warm reset on PCI-E cards
* results card to hang and always return 0xffff... so
* we ingore that flag for PCI-E cards. On PCI cards
* this flag gets cleared after 64 PCI clocks.
*/
bus_flags = (pdev->is_pcie) ? 0 : AR5K_RESET_CTL_PCI;
if (ah->ah_version == AR5K_AR5210) {
ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU |
AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_DMA |
AR5K_RESET_CTL_PHY | AR5K_RESET_CTL_PCI);
mdelay(2);
} else {
ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU |
AR5K_RESET_CTL_BASEBAND | bus_flags);
}
if (ret) {
ATH5K_ERR(ah->ah_sc, "failed to put device on warm reset\n");
return -EIO;
}
/* ...wakeup again!*/
ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0);
if (ret) {
ATH5K_ERR(ah->ah_sc, "failed to put device on hold\n");
return ret;
}
return ret;
}
/* /*
* Bring up MAC + PHY Chips and program PLL * Bring up MAC + PHY Chips and program PLL
* TODO: Half/Quarter rate support * TODO: Half/Quarter rate support
...@@ -318,6 +382,50 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial) ...@@ -318,6 +382,50 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
return ret; return ret;
} }
/*
* Put chipset on warm reset...
*
* Note: puting PCI core on warm reset on PCI-E cards
* results card to hang and always return 0xffff... so
* we ingore that flag for PCI-E cards. On PCI cards
* this flag gets cleared after 64 PCI clocks.
*/
bus_flags = (pdev->is_pcie) ? 0 : AR5K_RESET_CTL_PCI;
if (ah->ah_version == AR5K_AR5210) {
ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU |
AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_DMA |
AR5K_RESET_CTL_PHY | AR5K_RESET_CTL_PCI);
mdelay(2);
} else {
ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU |
AR5K_RESET_CTL_BASEBAND | bus_flags);
}
if (ret) {
ATH5K_ERR(ah->ah_sc, "failed to reset the MAC Chip\n");
return -EIO;
}
/* ...wakeup again!...*/
ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0);
if (ret) {
ATH5K_ERR(ah->ah_sc, "failed to resume the MAC Chip\n");
return ret;
}
/* ...clear reset control register and pull device out of
* warm reset */
if (ath5k_hw_nic_reset(ah, 0)) {
ATH5K_ERR(ah->ah_sc, "failed to warm reset the MAC Chip\n");
return -EIO;
}
/* On initialization skip PLL programming since we don't have
* a channel / mode set yet */
if (initial)
return 0;
if (ah->ah_version != AR5K_AR5210) { if (ah->ah_version != AR5K_AR5210) {
/* /*
* Get channel mode flags * Get channel mode flags
...@@ -383,39 +491,6 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial) ...@@ -383,39 +491,6 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
AR5K_PHY_TURBO); AR5K_PHY_TURBO);
} }
/* reseting PCI on PCI-E cards results card to hang
* and always return 0xffff... so we ingore that flag
* for PCI-E cards */
bus_flags = (pdev->is_pcie) ? 0 : AR5K_RESET_CTL_PCI;
/* Reset chipset */
if (ah->ah_version == AR5K_AR5210) {
ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU |
AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_DMA |
AR5K_RESET_CTL_PHY | AR5K_RESET_CTL_PCI);
mdelay(2);
} else {
ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU |
AR5K_RESET_CTL_BASEBAND | bus_flags);
}
if (ret) {
ATH5K_ERR(ah->ah_sc, "failed to reset the MAC Chip\n");
return -EIO;
}
/* ...wakeup again!*/
ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0);
if (ret) {
ATH5K_ERR(ah->ah_sc, "failed to resume the MAC Chip\n");
return ret;
}
/* ...final warm reset */
if (ath5k_hw_nic_reset(ah, 0)) {
ATH5K_ERR(ah->ah_sc, "failed to warm reset the MAC Chip\n");
return -EIO;
}
if (ah->ah_version != AR5K_AR5210) { if (ah->ah_version != AR5K_AR5210) {
/* ...update PLL if needed */ /* ...update PLL if needed */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment