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>
This commit is contained in:
parent
d1cb0bdac1
commit
edd7fc7003
|
@ -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)
|
||||||
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)
|
||||||
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,
|
||||||
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:
|
||||||
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)
|
||||||
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)
|
||||||
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 */
|
||||||
|
|
Loading…
Reference in New Issue