diff options
author | Felix Fietkau <nbd@openwrt.org> | 2011-11-16 07:08:41 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-12-07 15:19:34 -0500 |
commit | c0c1174144dd619456be5930d733028a055ef425 (patch) | |
tree | 21935e2a32845647269a5c7848d22286c01e024b | |
parent | c8e8868e3bf2ee0b6e606ce43af023b5f6edc954 (diff) |
ath9k: rework power state handling
Turning off the radio when mac80211 tells the driver that it's idle is not
a good idea, as idle interfaces might still occasionally scan or send packets.
The only time the radio can be safely turned off is when drv_stop has been
called. In the mean time, use sc->ps_idle only to indicate network sleep vs
full sleep.
Move the LED GPIO changes out of the PCI suspend/resume path, the start/stop
functions already take care of that.
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | drivers/net/wireless/ath/ath9k/main.c | 156 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/pci.c | 21 |
2 files changed, 47 insertions, 130 deletions
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 3733828e72bc..8b0feecf3fae 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c | |||
@@ -887,82 +887,6 @@ chip_reset: | |||
887 | #undef SCHED_INTR | 887 | #undef SCHED_INTR |
888 | } | 888 | } |
889 | 889 | ||
890 | static void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw) | ||
891 | { | ||
892 | struct ath_hw *ah = sc->sc_ah; | ||
893 | struct ath_common *common = ath9k_hw_common(ah); | ||
894 | struct ieee80211_channel *channel = hw->conf.channel; | ||
895 | int r; | ||
896 | |||
897 | ath9k_ps_wakeup(sc); | ||
898 | spin_lock_bh(&sc->sc_pcu_lock); | ||
899 | atomic_set(&ah->intr_ref_cnt, -1); | ||
900 | |||
901 | ath9k_hw_configpcipowersave(ah, false); | ||
902 | |||
903 | if (!ah->curchan) | ||
904 | ah->curchan = ath9k_cmn_get_curchannel(sc->hw, ah); | ||
905 | |||
906 | r = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false); | ||
907 | if (r) { | ||
908 | ath_err(common, | ||
909 | "Unable to reset channel (%u MHz), reset status %d\n", | ||
910 | channel->center_freq, r); | ||
911 | } | ||
912 | |||
913 | ath_complete_reset(sc, true); | ||
914 | |||
915 | /* Enable LED */ | ||
916 | ath9k_hw_cfg_output(ah, ah->led_pin, | ||
917 | AR_GPIO_OUTPUT_MUX_AS_OUTPUT); | ||
918 | ath9k_hw_set_gpio(ah, ah->led_pin, 0); | ||
919 | |||
920 | spin_unlock_bh(&sc->sc_pcu_lock); | ||
921 | |||
922 | ath9k_ps_restore(sc); | ||
923 | } | ||
924 | |||
925 | void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw) | ||
926 | { | ||
927 | struct ath_hw *ah = sc->sc_ah; | ||
928 | struct ieee80211_channel *channel = hw->conf.channel; | ||
929 | int r; | ||
930 | |||
931 | ath9k_ps_wakeup(sc); | ||
932 | |||
933 | ath_cancel_work(sc); | ||
934 | |||
935 | spin_lock_bh(&sc->sc_pcu_lock); | ||
936 | |||
937 | /* | ||
938 | * Keep the LED on when the radio is disabled | ||
939 | * during idle unassociated state. | ||
940 | */ | ||
941 | if (!sc->ps_idle) { | ||
942 | ath9k_hw_set_gpio(ah, ah->led_pin, 1); | ||
943 | ath9k_hw_cfg_gpio_input(ah, ah->led_pin); | ||
944 | } | ||
945 | |||
946 | ath_prepare_reset(sc, false, true); | ||
947 | |||
948 | if (!ah->curchan) | ||
949 | ah->curchan = ath9k_cmn_get_curchannel(hw, ah); | ||
950 | |||
951 | r = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false); | ||
952 | if (r) { | ||
953 | ath_err(ath9k_hw_common(sc->sc_ah), | ||
954 | "Unable to reset channel (%u MHz), reset status %d\n", | ||
955 | channel->center_freq, r); | ||
956 | } | ||
957 | |||
958 | ath9k_hw_phy_disable(ah); | ||
959 | |||
960 | ath9k_hw_configpcipowersave(ah, true); | ||
961 | |||
962 | spin_unlock_bh(&sc->sc_pcu_lock); | ||
963 | ath9k_ps_restore(sc); | ||
964 | } | ||
965 | |||
966 | static int ath_reset(struct ath_softc *sc, bool retry_tx) | 890 | static int ath_reset(struct ath_softc *sc, bool retry_tx) |
967 | { | 891 | { |
968 | int r; | 892 | int r; |
@@ -1098,6 +1022,9 @@ static int ath9k_start(struct ieee80211_hw *hw) | |||
1098 | * and then setup of the interrupt mask. | 1022 | * and then setup of the interrupt mask. |
1099 | */ | 1023 | */ |
1100 | spin_lock_bh(&sc->sc_pcu_lock); | 1024 | spin_lock_bh(&sc->sc_pcu_lock); |
1025 | |||
1026 | atomic_set(&ah->intr_ref_cnt, -1); | ||
1027 | |||
1101 | r = ath9k_hw_reset(ah, init_channel, ah->caldata, false); | 1028 | r = ath9k_hw_reset(ah, init_channel, ah->caldata, false); |
1102 | if (r) { | 1029 | if (r) { |
1103 | ath_err(common, | 1030 | ath_err(common, |
@@ -1139,6 +1066,18 @@ static int ath9k_start(struct ieee80211_hw *hw) | |||
1139 | goto mutex_unlock; | 1066 | goto mutex_unlock; |
1140 | } | 1067 | } |
1141 | 1068 | ||
1069 | if (ah->led_pin >= 0) { | ||
1070 | ath9k_hw_cfg_output(ah, ah->led_pin, | ||
1071 | AR_GPIO_OUTPUT_MUX_AS_OUTPUT); | ||
1072 | ath9k_hw_set_gpio(ah, ah->led_pin, 0); | ||
1073 | } | ||
1074 | |||
1075 | /* | ||
1076 | * Reset key cache to sane defaults (all entries cleared) instead of | ||
1077 | * semi-random values after suspend/resume. | ||
1078 | */ | ||
1079 | ath9k_cmn_init_crypto(sc->sc_ah); | ||
1080 | |||
1142 | spin_unlock_bh(&sc->sc_pcu_lock); | 1081 | spin_unlock_bh(&sc->sc_pcu_lock); |
1143 | 1082 | ||
1144 | if ((ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE) && | 1083 | if ((ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE) && |
@@ -1237,6 +1176,7 @@ static void ath9k_stop(struct ieee80211_hw *hw) | |||
1237 | struct ath_softc *sc = hw->priv; | 1176 | struct ath_softc *sc = hw->priv; |
1238 | struct ath_hw *ah = sc->sc_ah; | 1177 | struct ath_hw *ah = sc->sc_ah; |
1239 | struct ath_common *common = ath9k_hw_common(ah); | 1178 | struct ath_common *common = ath9k_hw_common(ah); |
1179 | bool prev_idle; | ||
1240 | 1180 | ||
1241 | mutex_lock(&sc->mutex); | 1181 | mutex_lock(&sc->mutex); |
1242 | 1182 | ||
@@ -1267,35 +1207,45 @@ static void ath9k_stop(struct ieee80211_hw *hw) | |||
1267 | * before setting the invalid flag. */ | 1207 | * before setting the invalid flag. */ |
1268 | ath9k_hw_disable_interrupts(ah); | 1208 | ath9k_hw_disable_interrupts(ah); |
1269 | 1209 | ||
1270 | if (!(sc->sc_flags & SC_OP_INVALID)) { | 1210 | spin_unlock_bh(&sc->sc_pcu_lock); |
1271 | ath_drain_all_txq(sc, false); | 1211 | |
1272 | ath_stoprecv(sc); | 1212 | /* we can now sync irq and kill any running tasklets, since we already |
1273 | ath9k_hw_phy_disable(ah); | 1213 | * disabled interrupts and not holding a spin lock */ |
1274 | } else | 1214 | synchronize_irq(sc->irq); |
1275 | sc->rx.rxlink = NULL; | 1215 | tasklet_kill(&sc->intr_tq); |
1216 | tasklet_kill(&sc->bcon_tasklet); | ||
1217 | |||
1218 | prev_idle = sc->ps_idle; | ||
1219 | sc->ps_idle = true; | ||
1220 | |||
1221 | spin_lock_bh(&sc->sc_pcu_lock); | ||
1222 | |||
1223 | if (ah->led_pin >= 0) { | ||
1224 | ath9k_hw_set_gpio(ah, ah->led_pin, 1); | ||
1225 | ath9k_hw_cfg_gpio_input(ah, ah->led_pin); | ||
1226 | } | ||
1227 | |||
1228 | ath_prepare_reset(sc, false, true); | ||
1276 | 1229 | ||
1277 | if (sc->rx.frag) { | 1230 | if (sc->rx.frag) { |
1278 | dev_kfree_skb_any(sc->rx.frag); | 1231 | dev_kfree_skb_any(sc->rx.frag); |
1279 | sc->rx.frag = NULL; | 1232 | sc->rx.frag = NULL; |
1280 | } | 1233 | } |
1281 | 1234 | ||
1282 | /* disable HAL and put h/w to sleep */ | 1235 | if (!ah->curchan) |
1283 | ath9k_hw_disable(ah); | 1236 | ah->curchan = ath9k_cmn_get_curchannel(hw, ah); |
1284 | 1237 | ||
1285 | spin_unlock_bh(&sc->sc_pcu_lock); | 1238 | ath9k_hw_reset(ah, ah->curchan, ah->caldata, false); |
1239 | ath9k_hw_phy_disable(ah); | ||
1286 | 1240 | ||
1287 | /* we can now sync irq and kill any running tasklets, since we already | 1241 | ath9k_hw_configpcipowersave(ah, true); |
1288 | * disabled interrupts and not holding a spin lock */ | ||
1289 | synchronize_irq(sc->irq); | ||
1290 | tasklet_kill(&sc->intr_tq); | ||
1291 | tasklet_kill(&sc->bcon_tasklet); | ||
1292 | 1242 | ||
1293 | ath9k_ps_restore(sc); | 1243 | spin_unlock_bh(&sc->sc_pcu_lock); |
1294 | 1244 | ||
1295 | sc->ps_idle = true; | 1245 | ath9k_ps_restore(sc); |
1296 | ath_radio_disable(sc, hw); | ||
1297 | 1246 | ||
1298 | sc->sc_flags |= SC_OP_INVALID; | 1247 | sc->sc_flags |= SC_OP_INVALID; |
1248 | sc->ps_idle = prev_idle; | ||
1299 | 1249 | ||
1300 | mutex_unlock(&sc->mutex); | 1250 | mutex_unlock(&sc->mutex); |
1301 | 1251 | ||
@@ -1635,8 +1585,8 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) | |||
1635 | struct ath_hw *ah = sc->sc_ah; | 1585 | struct ath_hw *ah = sc->sc_ah; |
1636 | struct ath_common *common = ath9k_hw_common(ah); | 1586 | struct ath_common *common = ath9k_hw_common(ah); |
1637 | struct ieee80211_conf *conf = &hw->conf; | 1587 | struct ieee80211_conf *conf = &hw->conf; |
1638 | bool disable_radio = false; | ||
1639 | 1588 | ||
1589 | ath9k_ps_wakeup(sc); | ||
1640 | mutex_lock(&sc->mutex); | 1590 | mutex_lock(&sc->mutex); |
1641 | 1591 | ||
1642 | /* | 1592 | /* |
@@ -1645,16 +1595,8 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) | |||
1645 | * of the changes. Likewise we must only disable the radio towards | 1595 | * of the changes. Likewise we must only disable the radio towards |
1646 | * the end. | 1596 | * the end. |
1647 | */ | 1597 | */ |
1648 | if (changed & IEEE80211_CONF_CHANGE_IDLE) { | 1598 | if (changed & IEEE80211_CONF_CHANGE_IDLE) |
1649 | sc->ps_idle = !!(conf->flags & IEEE80211_CONF_IDLE); | 1599 | sc->ps_idle = !!(conf->flags & IEEE80211_CONF_IDLE); |
1650 | if (!sc->ps_idle) { | ||
1651 | ath_radio_enable(sc, hw); | ||
1652 | ath_dbg(common, ATH_DBG_CONFIG, | ||
1653 | "not-idle: enabling radio\n"); | ||
1654 | } else { | ||
1655 | disable_radio = true; | ||
1656 | } | ||
1657 | } | ||
1658 | 1600 | ||
1659 | /* | 1601 | /* |
1660 | * We just prepare to enable PS. We have to wait until our AP has | 1602 | * We just prepare to enable PS. We have to wait until our AP has |
@@ -1760,18 +1702,12 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) | |||
1760 | ath_dbg(common, ATH_DBG_CONFIG, | 1702 | ath_dbg(common, ATH_DBG_CONFIG, |
1761 | "Set power: %d\n", conf->power_level); | 1703 | "Set power: %d\n", conf->power_level); |
1762 | sc->config.txpowlimit = 2 * conf->power_level; | 1704 | sc->config.txpowlimit = 2 * conf->power_level; |
1763 | ath9k_ps_wakeup(sc); | ||
1764 | ath9k_cmn_update_txpow(ah, sc->curtxpow, | 1705 | ath9k_cmn_update_txpow(ah, sc->curtxpow, |
1765 | sc->config.txpowlimit, &sc->curtxpow); | 1706 | sc->config.txpowlimit, &sc->curtxpow); |
1766 | ath9k_ps_restore(sc); | ||
1767 | } | ||
1768 | |||
1769 | if (disable_radio) { | ||
1770 | ath_dbg(common, ATH_DBG_CONFIG, "idle: disabling radio\n"); | ||
1771 | ath_radio_disable(sc, hw); | ||
1772 | } | 1707 | } |
1773 | 1708 | ||
1774 | mutex_unlock(&sc->mutex); | 1709 | mutex_unlock(&sc->mutex); |
1710 | ath9k_ps_restore(sc); | ||
1775 | 1711 | ||
1776 | return 0; | 1712 | return 0; |
1777 | } | 1713 | } |
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index 2dcdf63cb390..a439edc5dc06 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c | |||
@@ -307,12 +307,11 @@ static int ath_pci_suspend(struct device *device) | |||
307 | struct ieee80211_hw *hw = pci_get_drvdata(pdev); | 307 | struct ieee80211_hw *hw = pci_get_drvdata(pdev); |
308 | struct ath_softc *sc = hw->priv; | 308 | struct ath_softc *sc = hw->priv; |
309 | 309 | ||
310 | ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1); | ||
311 | |||
312 | /* The device has to be moved to FULLSLEEP forcibly. | 310 | /* The device has to be moved to FULLSLEEP forcibly. |
313 | * Otherwise the chip never moved to full sleep, | 311 | * Otherwise the chip never moved to full sleep, |
314 | * when no interface is up. | 312 | * when no interface is up. |
315 | */ | 313 | */ |
314 | ath9k_hw_disable(sc->sc_ah); | ||
316 | ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP); | 315 | ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP); |
317 | 316 | ||
318 | return 0; | 317 | return 0; |
@@ -321,8 +320,6 @@ static int ath_pci_suspend(struct device *device) | |||
321 | static int ath_pci_resume(struct device *device) | 320 | static int ath_pci_resume(struct device *device) |
322 | { | 321 | { |
323 | struct pci_dev *pdev = to_pci_dev(device); | 322 | struct pci_dev *pdev = to_pci_dev(device); |
324 | struct ieee80211_hw *hw = pci_get_drvdata(pdev); | ||
325 | struct ath_softc *sc = hw->priv; | ||
326 | u32 val; | 323 | u32 val; |
327 | 324 | ||
328 | /* | 325 | /* |
@@ -334,22 +331,6 @@ static int ath_pci_resume(struct device *device) | |||
334 | if ((val & 0x0000ff00) != 0) | 331 | if ((val & 0x0000ff00) != 0) |
335 | pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); | 332 | pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); |
336 | 333 | ||
337 | ath9k_ps_wakeup(sc); | ||
338 | /* Enable LED */ | ||
339 | ath9k_hw_cfg_output(sc->sc_ah, sc->sc_ah->led_pin, | ||
340 | AR_GPIO_OUTPUT_MUX_AS_OUTPUT); | ||
341 | ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 0); | ||
342 | |||
343 | /* | ||
344 | * Reset key cache to sane defaults (all entries cleared) instead of | ||
345 | * semi-random values after suspend/resume. | ||
346 | */ | ||
347 | ath9k_cmn_init_crypto(sc->sc_ah); | ||
348 | ath9k_ps_restore(sc); | ||
349 | |||
350 | sc->ps_idle = true; | ||
351 | ath_radio_disable(sc, hw); | ||
352 | |||
353 | return 0; | 334 | return 0; |
354 | } | 335 | } |
355 | 336 | ||