diff options
author | Vivek Natarajan <vivek.natraj@gmail.com> | 2009-01-20 00:47:08 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-01-29 16:01:03 -0500 |
commit | 3cbb5dd73697b3f1c677daffe29f00ace22b71e9 (patch) | |
tree | 39fcb371ef37b9a2a53a4cdf72a8e98c64d9e98e | |
parent | c5d0569882b9c264be31dcb0758961bfc479deea (diff) |
ath9k: Enable dynamic power save in ath9k.
This patch implements dynamic power save feature for ath9k.
Signed-off-by: Vivek Natarajan <vnatarajan@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | drivers/net/wireless/ath9k/ath9k.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/ath9k/core.h | 18 | ||||
-rw-r--r-- | drivers/net/wireless/ath9k/hw.c | 4 | ||||
-rw-r--r-- | drivers/net/wireless/ath9k/hw.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/ath9k/main.c | 39 | ||||
-rw-r--r-- | drivers/net/wireless/ath9k/recv.c | 6 |
6 files changed, 66 insertions, 4 deletions
diff --git a/drivers/net/wireless/ath9k/ath9k.h b/drivers/net/wireless/ath9k/ath9k.h index 3817645b85dc..0b305b832a8c 100644 --- a/drivers/net/wireless/ath9k/ath9k.h +++ b/drivers/net/wireless/ath9k/ath9k.h | |||
@@ -793,6 +793,8 @@ struct ath_hal { | |||
793 | u16 ah_currentRD5G; | 793 | u16 ah_currentRD5G; |
794 | u16 ah_currentRD2G; | 794 | u16 ah_currentRD2G; |
795 | char ah_iso[4]; | 795 | char ah_iso[4]; |
796 | enum ath9k_power_mode ah_power_mode; | ||
797 | enum ath9k_power_mode ah_restore_mode; | ||
796 | 798 | ||
797 | struct ath9k_channel ah_channels[150]; | 799 | struct ath9k_channel ah_channels[150]; |
798 | struct ath9k_channel *ah_curchan; | 800 | struct ath9k_channel *ah_curchan; |
diff --git a/drivers/net/wireless/ath9k/core.h b/drivers/net/wireless/ath9k/core.h index f65933d9c653..0f50767712a6 100644 --- a/drivers/net/wireless/ath9k/core.h +++ b/drivers/net/wireless/ath9k/core.h | |||
@@ -676,6 +676,7 @@ enum PROT_MODE { | |||
676 | #define SC_OP_RFKILL_REGISTERED BIT(11) | 676 | #define SC_OP_RFKILL_REGISTERED BIT(11) |
677 | #define SC_OP_RFKILL_SW_BLOCKED BIT(12) | 677 | #define SC_OP_RFKILL_SW_BLOCKED BIT(12) |
678 | #define SC_OP_RFKILL_HW_BLOCKED BIT(13) | 678 | #define SC_OP_RFKILL_HW_BLOCKED BIT(13) |
679 | #define SC_OP_WAIT_FOR_BEACON BIT(14) | ||
679 | 680 | ||
680 | struct ath_bus_ops { | 681 | struct ath_bus_ops { |
681 | void (*read_cachesize)(struct ath_softc *sc, int *csz); | 682 | void (*read_cachesize)(struct ath_softc *sc, int *csz); |
@@ -709,6 +710,7 @@ struct ath_softc { | |||
709 | u32 sc_keymax; | 710 | u32 sc_keymax; |
710 | DECLARE_BITMAP(sc_keymap, ATH_KEYMAX); | 711 | DECLARE_BITMAP(sc_keymap, ATH_KEYMAX); |
711 | u8 sc_splitmic; | 712 | u8 sc_splitmic; |
713 | atomic_t ps_usecount; | ||
712 | enum ath9k_int sc_imask; | 714 | enum ath9k_int sc_imask; |
713 | enum PROT_MODE sc_protmode; | 715 | enum PROT_MODE sc_protmode; |
714 | enum ath9k_ht_extprotspacing sc_ht_extprotspacing; | 716 | enum ath9k_ht_extprotspacing sc_ht_extprotspacing; |
@@ -777,4 +779,20 @@ static inline int ath_ahb_init(void) { return 0; }; | |||
777 | static inline void ath_ahb_exit(void) {}; | 779 | static inline void ath_ahb_exit(void) {}; |
778 | #endif | 780 | #endif |
779 | 781 | ||
782 | static inline void ath9k_ps_wakeup(struct ath_softc *sc) | ||
783 | { | ||
784 | if (atomic_inc_return(&sc->ps_usecount) == 1) | ||
785 | if (sc->sc_ah->ah_power_mode != ATH9K_PM_AWAKE) { | ||
786 | sc->sc_ah->ah_restore_mode = sc->sc_ah->ah_power_mode; | ||
787 | ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE); | ||
788 | } | ||
789 | } | ||
790 | |||
791 | static inline void ath9k_ps_restore(struct ath_softc *sc) | ||
792 | { | ||
793 | if (atomic_dec_and_test(&sc->ps_usecount)) | ||
794 | if (sc->hw->conf.flags & IEEE80211_CONF_PS) | ||
795 | ath9k_hw_setpower(sc->sc_ah, | ||
796 | sc->sc_ah->ah_restore_mode); | ||
797 | } | ||
780 | #endif /* CORE_H */ | 798 | #endif /* CORE_H */ |
diff --git a/drivers/net/wireless/ath9k/hw.c b/drivers/net/wireless/ath9k/hw.c index 88c8a62e1b8a..ab15e55317c6 100644 --- a/drivers/net/wireless/ath9k/hw.c +++ b/drivers/net/wireless/ath9k/hw.c | |||
@@ -2698,7 +2698,7 @@ bool ath9k_hw_setpower(struct ath_hal *ah, | |||
2698 | int status = true, setChip = true; | 2698 | int status = true, setChip = true; |
2699 | 2699 | ||
2700 | DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT, "%s -> %s (%s)\n", | 2700 | DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT, "%s -> %s (%s)\n", |
2701 | modes[ahp->ah_powerMode], modes[mode], | 2701 | modes[ah->ah_power_mode], modes[mode], |
2702 | setChip ? "set chip " : ""); | 2702 | setChip ? "set chip " : ""); |
2703 | 2703 | ||
2704 | switch (mode) { | 2704 | switch (mode) { |
@@ -2717,7 +2717,7 @@ bool ath9k_hw_setpower(struct ath_hal *ah, | |||
2717 | "Unknown power mode %u\n", mode); | 2717 | "Unknown power mode %u\n", mode); |
2718 | return false; | 2718 | return false; |
2719 | } | 2719 | } |
2720 | ahp->ah_powerMode = mode; | 2720 | ah->ah_power_mode = mode; |
2721 | 2721 | ||
2722 | return status; | 2722 | return status; |
2723 | } | 2723 | } |
diff --git a/drivers/net/wireless/ath9k/hw.h b/drivers/net/wireless/ath9k/hw.h index d44e016f9880..087c5718707b 100644 --- a/drivers/net/wireless/ath9k/hw.h +++ b/drivers/net/wireless/ath9k/hw.h | |||
@@ -844,7 +844,6 @@ struct ath_hal_5416 { | |||
844 | bool ah_chipFullSleep; | 844 | bool ah_chipFullSleep; |
845 | u32 ah_atimWindow; | 845 | u32 ah_atimWindow; |
846 | u16 ah_antennaSwitchSwap; | 846 | u16 ah_antennaSwitchSwap; |
847 | enum ath9k_power_mode ah_powerMode; | ||
848 | enum ath9k_ant_setting ah_diversityControl; | 847 | enum ath9k_ant_setting ah_diversityControl; |
849 | 848 | ||
850 | /* Calibration */ | 849 | /* Calibration */ |
diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c index 8ad927a8870c..b494a0d7e8b5 100644 --- a/drivers/net/wireless/ath9k/main.c +++ b/drivers/net/wireless/ath9k/main.c | |||
@@ -237,6 +237,8 @@ static int ath_set_channel(struct ath_softc *sc, struct ath9k_channel *hchan) | |||
237 | if (sc->sc_flags & SC_OP_INVALID) | 237 | if (sc->sc_flags & SC_OP_INVALID) |
238 | return -EIO; | 238 | return -EIO; |
239 | 239 | ||
240 | ath9k_ps_wakeup(sc); | ||
241 | |||
240 | /* | 242 | /* |
241 | * This is only performed if the channel settings have | 243 | * This is only performed if the channel settings have |
242 | * actually changed. | 244 | * actually changed. |
@@ -287,6 +289,7 @@ static int ath_set_channel(struct ath_softc *sc, struct ath9k_channel *hchan) | |||
287 | ath_cache_conf_rate(sc, &hw->conf); | 289 | ath_cache_conf_rate(sc, &hw->conf); |
288 | ath_update_txpow(sc); | 290 | ath_update_txpow(sc); |
289 | ath9k_hw_set_interrupts(ah, sc->sc_imask); | 291 | ath9k_hw_set_interrupts(ah, sc->sc_imask); |
292 | ath9k_ps_restore(sc); | ||
290 | return 0; | 293 | return 0; |
291 | } | 294 | } |
292 | 295 | ||
@@ -559,8 +562,10 @@ irqreturn_t ath_isr(int irq, void *dev) | |||
559 | ATH9K_HW_CAP_AUTOSLEEP)) { | 562 | ATH9K_HW_CAP_AUTOSLEEP)) { |
560 | /* Clear RxAbort bit so that we can | 563 | /* Clear RxAbort bit so that we can |
561 | * receive frames */ | 564 | * receive frames */ |
565 | ath9k_hw_setpower(ah, ATH9K_PM_AWAKE); | ||
562 | ath9k_hw_setrxabort(ah, 0); | 566 | ath9k_hw_setrxabort(ah, 0); |
563 | sched = true; | 567 | sched = true; |
568 | sc->sc_flags |= SC_OP_WAIT_FOR_BEACON; | ||
564 | } | 569 | } |
565 | } | 570 | } |
566 | } | 571 | } |
@@ -1044,6 +1049,7 @@ static void ath_radio_enable(struct ath_softc *sc) | |||
1044 | struct ieee80211_channel *channel = sc->hw->conf.channel; | 1049 | struct ieee80211_channel *channel = sc->hw->conf.channel; |
1045 | int r; | 1050 | int r; |
1046 | 1051 | ||
1052 | ath9k_ps_wakeup(sc); | ||
1047 | spin_lock_bh(&sc->sc_resetlock); | 1053 | spin_lock_bh(&sc->sc_resetlock); |
1048 | 1054 | ||
1049 | r = ath9k_hw_reset(ah, ah->ah_curchan, false); | 1055 | r = ath9k_hw_reset(ah, ah->ah_curchan, false); |
@@ -1075,6 +1081,7 @@ static void ath_radio_enable(struct ath_softc *sc) | |||
1075 | ath9k_hw_set_gpio(ah, ATH_LED_PIN, 0); | 1081 | ath9k_hw_set_gpio(ah, ATH_LED_PIN, 0); |
1076 | 1082 | ||
1077 | ieee80211_wake_queues(sc->hw); | 1083 | ieee80211_wake_queues(sc->hw); |
1084 | ath9k_ps_restore(sc); | ||
1078 | } | 1085 | } |
1079 | 1086 | ||
1080 | static void ath_radio_disable(struct ath_softc *sc) | 1087 | static void ath_radio_disable(struct ath_softc *sc) |
@@ -1083,6 +1090,7 @@ static void ath_radio_disable(struct ath_softc *sc) | |||
1083 | struct ieee80211_channel *channel = sc->hw->conf.channel; | 1090 | struct ieee80211_channel *channel = sc->hw->conf.channel; |
1084 | int r; | 1091 | int r; |
1085 | 1092 | ||
1093 | ath9k_ps_wakeup(sc); | ||
1086 | ieee80211_stop_queues(sc->hw); | 1094 | ieee80211_stop_queues(sc->hw); |
1087 | 1095 | ||
1088 | /* Disable LED */ | 1096 | /* Disable LED */ |
@@ -1108,6 +1116,7 @@ static void ath_radio_disable(struct ath_softc *sc) | |||
1108 | 1116 | ||
1109 | ath9k_hw_phy_disable(ah); | 1117 | ath9k_hw_phy_disable(ah); |
1110 | ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP); | 1118 | ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP); |
1119 | ath9k_ps_restore(sc); | ||
1111 | } | 1120 | } |
1112 | 1121 | ||
1113 | static bool ath_is_rfkill_set(struct ath_softc *sc) | 1122 | static bool ath_is_rfkill_set(struct ath_softc *sc) |
@@ -1259,6 +1268,8 @@ void ath_detach(struct ath_softc *sc) | |||
1259 | struct ieee80211_hw *hw = sc->hw; | 1268 | struct ieee80211_hw *hw = sc->hw; |
1260 | int i = 0; | 1269 | int i = 0; |
1261 | 1270 | ||
1271 | ath9k_ps_wakeup(sc); | ||
1272 | |||
1262 | DPRINTF(sc, ATH_DBG_CONFIG, "Detach ATH hw\n"); | 1273 | DPRINTF(sc, ATH_DBG_CONFIG, "Detach ATH hw\n"); |
1263 | 1274 | ||
1264 | #if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) | 1275 | #if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) |
@@ -1283,6 +1294,7 @@ void ath_detach(struct ath_softc *sc) | |||
1283 | 1294 | ||
1284 | ath9k_hw_detach(sc->sc_ah); | 1295 | ath9k_hw_detach(sc->sc_ah); |
1285 | ath9k_exit_debug(sc); | 1296 | ath9k_exit_debug(sc); |
1297 | ath9k_ps_restore(sc); | ||
1286 | } | 1298 | } |
1287 | 1299 | ||
1288 | static int ath_init(u16 devid, struct ath_softc *sc) | 1300 | static int ath_init(u16 devid, struct ath_softc *sc) |
@@ -1526,7 +1538,9 @@ int ath_attach(u16 devid, struct ath_softc *sc) | |||
1526 | hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | | 1538 | hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | |
1527 | IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | | 1539 | IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | |
1528 | IEEE80211_HW_SIGNAL_DBM | | 1540 | IEEE80211_HW_SIGNAL_DBM | |
1529 | IEEE80211_HW_AMPDU_AGGREGATION; | 1541 | IEEE80211_HW_AMPDU_AGGREGATION | |
1542 | IEEE80211_HW_SUPPORTS_PS | | ||
1543 | IEEE80211_HW_PS_NULLFUNC_STACK; | ||
1530 | 1544 | ||
1531 | if (AR_SREV_9160_10_OR_LATER(sc->sc_ah)) | 1545 | if (AR_SREV_9160_10_OR_LATER(sc->sc_ah)) |
1532 | hw->flags |= IEEE80211_HW_MFP_CAPABLE; | 1546 | hw->flags |= IEEE80211_HW_MFP_CAPABLE; |
@@ -2090,6 +2104,27 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) | |||
2090 | struct ieee80211_conf *conf = &hw->conf; | 2104 | struct ieee80211_conf *conf = &hw->conf; |
2091 | 2105 | ||
2092 | mutex_lock(&sc->mutex); | 2106 | mutex_lock(&sc->mutex); |
2107 | if (changed & IEEE80211_CONF_CHANGE_PS) { | ||
2108 | if (conf->flags & IEEE80211_CONF_PS) { | ||
2109 | if ((sc->sc_imask & ATH9K_INT_TIM_TIMER) == 0) { | ||
2110 | sc->sc_imask |= ATH9K_INT_TIM_TIMER; | ||
2111 | ath9k_hw_set_interrupts(sc->sc_ah, | ||
2112 | sc->sc_imask); | ||
2113 | } | ||
2114 | ath9k_hw_setrxabort(sc->sc_ah, 1); | ||
2115 | ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP); | ||
2116 | } else { | ||
2117 | ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE); | ||
2118 | ath9k_hw_setrxabort(sc->sc_ah, 0); | ||
2119 | sc->sc_flags &= ~SC_OP_WAIT_FOR_BEACON; | ||
2120 | if (sc->sc_imask & ATH9K_INT_TIM_TIMER) { | ||
2121 | sc->sc_imask &= ~ATH9K_INT_TIM_TIMER; | ||
2122 | ath9k_hw_set_interrupts(sc->sc_ah, | ||
2123 | sc->sc_imask); | ||
2124 | } | ||
2125 | } | ||
2126 | } | ||
2127 | |||
2093 | if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { | 2128 | if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { |
2094 | struct ieee80211_channel *curchan = hw->conf.channel; | 2129 | struct ieee80211_channel *curchan = hw->conf.channel; |
2095 | int pos; | 2130 | int pos; |
@@ -2310,6 +2345,7 @@ static int ath9k_set_key(struct ieee80211_hw *hw, | |||
2310 | struct ath_softc *sc = hw->priv; | 2345 | struct ath_softc *sc = hw->priv; |
2311 | int ret = 0; | 2346 | int ret = 0; |
2312 | 2347 | ||
2348 | ath9k_ps_wakeup(sc); | ||
2313 | DPRINTF(sc, ATH_DBG_KEYCACHE, "Set HW Key\n"); | 2349 | DPRINTF(sc, ATH_DBG_KEYCACHE, "Set HW Key\n"); |
2314 | 2350 | ||
2315 | switch (cmd) { | 2351 | switch (cmd) { |
@@ -2333,6 +2369,7 @@ static int ath9k_set_key(struct ieee80211_hw *hw, | |||
2333 | ret = -EINVAL; | 2369 | ret = -EINVAL; |
2334 | } | 2370 | } |
2335 | 2371 | ||
2372 | ath9k_ps_restore(sc); | ||
2336 | return ret; | 2373 | return ret; |
2337 | } | 2374 | } |
2338 | 2375 | ||
diff --git a/drivers/net/wireless/ath9k/recv.c b/drivers/net/wireless/ath9k/recv.c index 648bb49e6734..8da08f9b463c 100644 --- a/drivers/net/wireless/ath9k/recv.c +++ b/drivers/net/wireless/ath9k/recv.c | |||
@@ -628,6 +628,12 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush) | |||
628 | } else { | 628 | } else { |
629 | sc->rx.rxotherant = 0; | 629 | sc->rx.rxotherant = 0; |
630 | } | 630 | } |
631 | |||
632 | if (ieee80211_is_beacon(hdr->frame_control) && | ||
633 | (sc->sc_flags & SC_OP_WAIT_FOR_BEACON)) { | ||
634 | sc->sc_flags &= ~SC_OP_WAIT_FOR_BEACON; | ||
635 | ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP); | ||
636 | } | ||
631 | requeue: | 637 | requeue: |
632 | list_move_tail(&bf->list, &sc->rx.rxbuf); | 638 | list_move_tail(&bf->list, &sc->rx.rxbuf); |
633 | ath_rx_buf_link(sc, bf); | 639 | ath_rx_buf_link(sc, bf); |