1b7d5e03cSMatthew Dillon /*
2b7d5e03cSMatthew Dillon  * Copyright (c) 2013 Qualcomm Atheros, Inc.
3b7d5e03cSMatthew Dillon  *
4b7d5e03cSMatthew Dillon  * Permission to use, copy, modify, and/or distribute this software for any
5b7d5e03cSMatthew Dillon  * purpose with or without fee is hereby granted, provided that the above
6b7d5e03cSMatthew Dillon  * copyright notice and this permission notice appear in all copies.
7b7d5e03cSMatthew Dillon  *
8b7d5e03cSMatthew Dillon  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9b7d5e03cSMatthew Dillon  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10b7d5e03cSMatthew Dillon  * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11b7d5e03cSMatthew Dillon  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12b7d5e03cSMatthew Dillon  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
13b7d5e03cSMatthew Dillon  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14b7d5e03cSMatthew Dillon  * PERFORMANCE OF THIS SOFTWARE.
15b7d5e03cSMatthew Dillon  */
16b7d5e03cSMatthew Dillon 
17b7d5e03cSMatthew Dillon #include "opt_ah.h"
18b7d5e03cSMatthew Dillon 
19b7d5e03cSMatthew Dillon #include "ah.h"
20b7d5e03cSMatthew Dillon #include "ah_internal.h"
21b7d5e03cSMatthew Dillon 
22b7d5e03cSMatthew Dillon #include "ar9300/ar9300.h"
23b7d5e03cSMatthew Dillon #include "ar9300/ar9300reg.h"
24b7d5e03cSMatthew Dillon 
25b7d5e03cSMatthew Dillon #if ATH_WOW_OFFLOAD
ar9300_wowoffload_prep(struct ath_hal * ah)26b7d5e03cSMatthew Dillon void ar9300_wowoffload_prep(struct ath_hal *ah)
27b7d5e03cSMatthew Dillon {
28b7d5e03cSMatthew Dillon     struct ath_hal_9300 *ahp = AH9300(ah);
29b7d5e03cSMatthew Dillon 
30b7d5e03cSMatthew Dillon     ahp->ah_mcast_filter_l32_set = 0;
31b7d5e03cSMatthew Dillon     ahp->ah_mcast_filter_u32_set = 0;
32b7d5e03cSMatthew Dillon }
33b7d5e03cSMatthew Dillon 
ar9300_wowoffload_post(struct ath_hal * ah)34b7d5e03cSMatthew Dillon void ar9300_wowoffload_post(struct ath_hal *ah)
35b7d5e03cSMatthew Dillon {
36b7d5e03cSMatthew Dillon     struct ath_hal_9300 *ahp = AH9300(ah);
37b7d5e03cSMatthew Dillon     u_int32_t val;
38b7d5e03cSMatthew Dillon 
39b7d5e03cSMatthew Dillon     if (ahp->ah_mcast_filter_l32_set != 0) {
40b7d5e03cSMatthew Dillon         val = OS_REG_READ(ah, AR_MCAST_FIL0);
41b7d5e03cSMatthew Dillon         val &= ~ahp->ah_mcast_filter_l32_set;
42b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, AR_MCAST_FIL0, val);
43b7d5e03cSMatthew Dillon     }
44b7d5e03cSMatthew Dillon     if (ahp->ah_mcast_filter_u32_set != 0) {
45b7d5e03cSMatthew Dillon         val = OS_REG_READ(ah, AR_MCAST_FIL1);
46b7d5e03cSMatthew Dillon         val &= ~ahp->ah_mcast_filter_u32_set;
47b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, AR_MCAST_FIL1, val);
48b7d5e03cSMatthew Dillon     }
49b7d5e03cSMatthew Dillon 
50b7d5e03cSMatthew Dillon     ahp->ah_mcast_filter_l32_set = 0;
51b7d5e03cSMatthew Dillon     ahp->ah_mcast_filter_u32_set = 0;
52b7d5e03cSMatthew Dillon }
53b7d5e03cSMatthew Dillon 
ar9300_wowoffload_add_mcast_filter(struct ath_hal * ah,u_int8_t * mc_addr)54b7d5e03cSMatthew Dillon static void ar9300_wowoffload_add_mcast_filter(struct ath_hal *ah, u_int8_t *mc_addr)
55b7d5e03cSMatthew Dillon {
56b7d5e03cSMatthew Dillon     struct ath_hal_9300 *ahp = AH9300(ah);
57b7d5e03cSMatthew Dillon     u_int32_t reg, val;
58b7d5e03cSMatthew Dillon     u_int8_t  pos, high32;
59b7d5e03cSMatthew Dillon 
60b7d5e03cSMatthew Dillon     memcpy((u_int8_t *) &val, &mc_addr[0], 3);
61b7d5e03cSMatthew Dillon     pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
62b7d5e03cSMatthew Dillon     memcpy((u_int8_t *) &val, &mc_addr[3], 3);
63b7d5e03cSMatthew Dillon     pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
64b7d5e03cSMatthew Dillon     high32 = pos & 0x20;
65b7d5e03cSMatthew Dillon     reg = high32 ? AR_MCAST_FIL1 : AR_MCAST_FIL0;
66b7d5e03cSMatthew Dillon     pos &= 0x1F;
67b7d5e03cSMatthew Dillon 
68b7d5e03cSMatthew Dillon     val = OS_REG_READ(ah, reg);
69b7d5e03cSMatthew Dillon     if ((val & (1 << pos)) == 0) {
70b7d5e03cSMatthew Dillon         val |= (1 << pos);
71b7d5e03cSMatthew Dillon         if (high32) {
72b7d5e03cSMatthew Dillon             ahp->ah_mcast_filter_u32_set |= (1 << pos);
73b7d5e03cSMatthew Dillon         } else {
74b7d5e03cSMatthew Dillon             ahp->ah_mcast_filter_l32_set |= (1 << pos);
75b7d5e03cSMatthew Dillon         }
76b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, reg, val);
77b7d5e03cSMatthew Dillon     }
78b7d5e03cSMatthew Dillon }
79b7d5e03cSMatthew Dillon 
80b7d5e03cSMatthew Dillon /*
81b7d5e03cSMatthew Dillon  * DeviceID SWAR - EV91928
82b7d5e03cSMatthew Dillon  *
83b7d5e03cSMatthew Dillon  * During SW WOW, 0x4004[13] is set to allow BT eCPU to access WLAN MAC
84b7d5e03cSMatthew Dillon  * registers. Setting 00x4004[13] will prevent eeprom state machine to
85b7d5e03cSMatthew Dillon  * load customizable PCIE configuration registers, which lead to the PCIE
86b7d5e03cSMatthew Dillon  * device id stay as default 0xABCD. The SWAR to have BT eCPU to write
87b7d5e03cSMatthew Dillon  * to PCIE registers as soon as it detects PCIE reset is deasserted.
88b7d5e03cSMatthew Dillon  */
ar9300_wowoffload_download_devid_swar(struct ath_hal * ah)89b7d5e03cSMatthew Dillon void ar9300_wowoffload_download_devid_swar(struct ath_hal *ah)
90b7d5e03cSMatthew Dillon {
91b7d5e03cSMatthew Dillon     u_int32_t addr = AR_WOW_OFFLOAD_WLAN_REGSET_NUM;
92b7d5e03cSMatthew Dillon 
93b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, addr, 8);
94b7d5e03cSMatthew Dillon     addr += 4;
95b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, addr, 0x5000);
96b7d5e03cSMatthew Dillon     addr += 4;
97b7d5e03cSMatthew Dillon     HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) pcie_000 = %08x\n",
98b7d5e03cSMatthew Dillon              AH_PRIVATE(ah)->ah_config.ath_hal_pcie_000);
99b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, addr, AH_PRIVATE(ah)->ah_config.ath_hal_pcie_000);
100b7d5e03cSMatthew Dillon     addr += 4;
101b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, addr, 0x5008);
102b7d5e03cSMatthew Dillon     addr += 4;
103b7d5e03cSMatthew Dillon     HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) pcie_008 = %08x\n",
104b7d5e03cSMatthew Dillon              AH_PRIVATE(ah)->ah_config.ath_hal_pcie_008);
105b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, addr, AH_PRIVATE(ah)->ah_config.ath_hal_pcie_008);
106b7d5e03cSMatthew Dillon     addr += 4;
107b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, addr, 0x502c);
108b7d5e03cSMatthew Dillon     addr += 4;
109b7d5e03cSMatthew Dillon     HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) pcie_02c = %08x\n",
110b7d5e03cSMatthew Dillon              AH_PRIVATE(ah)->ah_config.ath_hal_pcie_02c);
111b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, addr, AH_PRIVATE(ah)->ah_config.ath_hal_pcie_02c);
112b7d5e03cSMatthew Dillon     addr += 4;
113b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, addr, 0x18c00);
114b7d5e03cSMatthew Dillon     addr += 4;
115b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, addr, 0x18212ede);
116b7d5e03cSMatthew Dillon     addr += 4;
117b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, addr, 0x18c04);
118b7d5e03cSMatthew Dillon     addr += 4;
119b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, addr, 0x008001d8);
120b7d5e03cSMatthew Dillon     addr += 4;
121b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, addr, 0x18c08);
122b7d5e03cSMatthew Dillon     addr += 4;
123b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, addr, 0x0003580c);
124b7d5e03cSMatthew Dillon     addr += 4;
125b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, addr, 0x570c);
126b7d5e03cSMatthew Dillon     addr += 4;
127b7d5e03cSMatthew Dillon     HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) pcie_70c = %08x\n",
128b7d5e03cSMatthew Dillon              AH_PRIVATE(ah)->ah_config.ath_hal_pcie_70c);
129b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, addr, AH_PRIVATE(ah)->ah_config.ath_hal_pcie_70c);
130b7d5e03cSMatthew Dillon     addr += 4;
131b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, addr, 0x5040);
132b7d5e03cSMatthew Dillon     addr += 4;
133b7d5e03cSMatthew Dillon     HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) pcie_040 = %08x\n",
134b7d5e03cSMatthew Dillon              AH_PRIVATE(ah)->ah_config.ath_hal_pcie_040);
135b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, addr, AH_PRIVATE(ah)->ah_config.ath_hal_pcie_040);
136b7d5e03cSMatthew Dillon     addr += 4;
137b7d5e03cSMatthew Dillon /*
138b7d5e03cSMatthew Dillon     A_SOC_REG_WRITE(0x45000, 0x0034168c);
139b7d5e03cSMatthew Dillon     A_SOC_REG_WRITE(0x45008, 0x02800001);
140b7d5e03cSMatthew Dillon     A_SOC_REG_WRITE(0x4502c, 0x3117168c);
141b7d5e03cSMatthew Dillon     A_SOC_REG_WRITE(0x58c00, 0x18212ede);
142b7d5e03cSMatthew Dillon     A_SOC_REG_WRITE(0x58c04, 0x000801d8);
143b7d5e03cSMatthew Dillon     A_SOC_REG_WRITE(0x58c08, 0x0003580c);
144b7d5e03cSMatthew Dillon     A_SOC_REG_WRITE(0x4570c, 0x275f3f01);
145b7d5e03cSMatthew Dillon     A_SOC_REG_WRITE(0x45040, 0xffc25001);
146b7d5e03cSMatthew Dillon */
147b7d5e03cSMatthew Dillon }
148b7d5e03cSMatthew Dillon 
149b7d5e03cSMatthew Dillon /* Retrieve updated information from MAC PCU buffer.
150b7d5e03cSMatthew Dillon  * Embedded CPU would have written the value before exiting WoW
151b7d5e03cSMatthew Dillon  * */
ar9300_wowoffload_retrieve_data(struct ath_hal * ah,void * buf,u_int32_t param)152b7d5e03cSMatthew Dillon void ar9300_wowoffload_retrieve_data(struct ath_hal *ah, void *buf, u_int32_t param)
153b7d5e03cSMatthew Dillon {
154b7d5e03cSMatthew Dillon     u_int32_t rc_lower, rc_upper;
155b7d5e03cSMatthew Dillon 
156b7d5e03cSMatthew Dillon     if (param == WOW_PARAM_REPLAY_CNTR) {
157b7d5e03cSMatthew Dillon         rc_lower = OS_REG_READ(ah, AR_WOW_TXBUF(0));
158b7d5e03cSMatthew Dillon         rc_upper = OS_REG_READ(ah, AR_WOW_TXBUF(1));
159b7d5e03cSMatthew Dillon         *(u_int64_t *)buf = rc_lower + (rc_upper << 32);
160b7d5e03cSMatthew Dillon     }
161b7d5e03cSMatthew Dillon     else if (param == WOW_PARAM_KEY_TSC) {
162b7d5e03cSMatthew Dillon         rc_lower = OS_REG_READ(ah, AR_WOW_TXBUF(2));
163b7d5e03cSMatthew Dillon         rc_upper = OS_REG_READ(ah, AR_WOW_TXBUF(3));
164b7d5e03cSMatthew Dillon         *(u_int64_t *)buf = rc_lower + (rc_upper << 32);
165b7d5e03cSMatthew Dillon     }
166b7d5e03cSMatthew Dillon     else if (param == WOW_PARAM_TX_SEQNUM) {
167b7d5e03cSMatthew Dillon         *(u_int32_t *)buf = OS_REG_READ(ah, AR_WOW_TXBUF(4));
168b7d5e03cSMatthew Dillon     }
169b7d5e03cSMatthew Dillon 
170b7d5e03cSMatthew Dillon }
171b7d5e03cSMatthew Dillon 
172b7d5e03cSMatthew Dillon /* Download GTK rekey related information to the embedded CPU */
ar9300_wowoffload_download_rekey_data(struct ath_hal * ah,u_int32_t * data,u_int32_t bytes)173b7d5e03cSMatthew Dillon u_int32_t ar9300_wowoffload_download_rekey_data(struct ath_hal *ah, u_int32_t *data, u_int32_t bytes)
174b7d5e03cSMatthew Dillon {
175b7d5e03cSMatthew Dillon     int i;
176b7d5e03cSMatthew Dillon     int mbox_status = OS_REG_READ(ah, AR_MBOX_CTRL_STATUS);
177b7d5e03cSMatthew Dillon     u_int32_t gtk_data_start;
178b7d5e03cSMatthew Dillon 
179b7d5e03cSMatthew Dillon     HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) %s, bytes=%d\n", __func__, bytes);
180b7d5e03cSMatthew Dillon     if (AR_SREV_JUPITER(ah) &&
181b7d5e03cSMatthew Dillon         (bytes > (AR_WOW_OFFLOAD_GTK_DATA_WORDS_JUPITER * 4)))
182b7d5e03cSMatthew Dillon     {
183b7d5e03cSMatthew Dillon         bytes = AR_WOW_OFFLOAD_GTK_DATA_WORDS_JUPITER * 4;
184b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) bytes truncated to %d\n", bytes);
185b7d5e03cSMatthew Dillon     }
186b7d5e03cSMatthew Dillon     /* Check if mailbox is busy */
187b7d5e03cSMatthew Dillon     if (mbox_status != 0) {
188b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: Mailbox register busy! Reg = 0x%x", __func__, mbox_status);
189b7d5e03cSMatthew Dillon         return 1;
190b7d5e03cSMatthew Dillon     }
191b7d5e03cSMatthew Dillon 
192b7d5e03cSMatthew Dillon     /* Clear status */
193b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_EMB_CPU_WOW_STATUS, 0x0);
194b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_WLAN_WOW_ENABLE, 0);
195b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_WLAN_WOW_STATUS, 0xFFFFFFFF);
196b7d5e03cSMatthew Dillon 
197b7d5e03cSMatthew Dillon     if (AR_SREV_JUPITER(ah)) {
198b7d5e03cSMatthew Dillon         gtk_data_start = AR_WOW_OFFLOAD_GTK_DATA_START_JUPITER;
199b7d5e03cSMatthew Dillon     } else {
200b7d5e03cSMatthew Dillon         gtk_data_start = AR_WOW_OFFLOAD_GTK_DATA_START;
201b7d5e03cSMatthew Dillon     }
202b7d5e03cSMatthew Dillon     for (i = 0;i < bytes/4; i++) {
203b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, gtk_data_start + i * 4, data[i]);
204b7d5e03cSMatthew Dillon     }
205b7d5e03cSMatthew Dillon 
206b7d5e03cSMatthew Dillon     return 0;
207b7d5e03cSMatthew Dillon }
208b7d5e03cSMatthew Dillon 
ar9300_wowoffload_download_acer_magic(struct ath_hal * ah,HAL_BOOL valid,u_int8_t * datap,u_int32_t bytes)209b7d5e03cSMatthew Dillon void ar9300_wowoffload_download_acer_magic( struct ath_hal *ah,
210b7d5e03cSMatthew Dillon                                             HAL_BOOL      valid,
211b7d5e03cSMatthew Dillon                                             u_int8_t* datap,
212b7d5e03cSMatthew Dillon                                             u_int32_t bytes)
213b7d5e03cSMatthew Dillon {
214b7d5e03cSMatthew Dillon     u_int32_t *p32 = (u_int32_t *) datap;
215b7d5e03cSMatthew Dillon     u_int32_t l = 0, u = 0;
216b7d5e03cSMatthew Dillon 
217b7d5e03cSMatthew Dillon     if (valid) {
218b7d5e03cSMatthew Dillon         l = *p32;
219b7d5e03cSMatthew Dillon         p32++;
220b7d5e03cSMatthew Dillon         u = *(u_int16_t *) p32;
221b7d5e03cSMatthew Dillon     }
222b7d5e03cSMatthew Dillon 
223b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_WOW_OFFLOAD_ACER_MAGIC_START, l);
224b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_WOW_OFFLOAD_ACER_MAGIC_START + 4, u);
225b7d5e03cSMatthew Dillon 
226b7d5e03cSMatthew Dillon     HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,
227b7d5e03cSMatthew Dillon         "%s: Aer Magic: %02x-%02x-%02x-%02x-%02x-%02x\n", __func__,
228b7d5e03cSMatthew Dillon         datap[0], datap[1], datap[2], datap[3], datap[4], datap[5]);
229b7d5e03cSMatthew Dillon }
230b7d5e03cSMatthew Dillon 
ar9300_wowoffload_download_acer_swka(struct ath_hal * ah,u_int32_t id,HAL_BOOL valid,u_int32_t period,u_int32_t size,u_int32_t * datap)231b7d5e03cSMatthew Dillon void ar9300_wowoffload_download_acer_swka(  struct ath_hal *ah,
232b7d5e03cSMatthew Dillon                                             u_int32_t  id,
233b7d5e03cSMatthew Dillon                                             HAL_BOOL       valid,
234b7d5e03cSMatthew Dillon                                             u_int32_t  period,
235b7d5e03cSMatthew Dillon                                             u_int32_t  size,
236b7d5e03cSMatthew Dillon                                             u_int32_t* datap)
237b7d5e03cSMatthew Dillon {
238b7d5e03cSMatthew Dillon     u_int32_t ka_period[2] = {
239b7d5e03cSMatthew Dillon         AR_WOW_OFFLOAD_ACER_KA0_PERIOD_MS,
240b7d5e03cSMatthew Dillon         AR_WOW_OFFLOAD_ACER_KA1_PERIOD_MS
241b7d5e03cSMatthew Dillon     };
242b7d5e03cSMatthew Dillon     u_int32_t ka_size[2] = {
243b7d5e03cSMatthew Dillon         AR_WOW_OFFLOAD_ACER_KA0_SIZE,
244b7d5e03cSMatthew Dillon         AR_WOW_OFFLOAD_ACER_KA1_SIZE
245b7d5e03cSMatthew Dillon     };
246b7d5e03cSMatthew Dillon     u_int32_t ka_data[2] = {
247b7d5e03cSMatthew Dillon         AR_WOW_OFFLOAD_ACER_KA0_DATA,
248b7d5e03cSMatthew Dillon         AR_WOW_OFFLOAD_ACER_KA1_DATA
249b7d5e03cSMatthew Dillon     };
250b7d5e03cSMatthew Dillon     u_int32_t n_data = AR_WOW_OFFLOAD_ACER_KA0_DATA_WORDS;
251b7d5e03cSMatthew Dillon     int i;
252b7d5e03cSMatthew Dillon 
253b7d5e03cSMatthew Dillon     if (id >= 2) {
254b7d5e03cSMatthew Dillon         return;
255b7d5e03cSMatthew Dillon     }
256b7d5e03cSMatthew Dillon 
257b7d5e03cSMatthew Dillon     if (valid) {
258b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, ka_period[id], period);
259b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, ka_size[id], size);
260b7d5e03cSMatthew Dillon     } else {
261b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, ka_period[id], 0);
262b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, ka_size[id], 0);
263b7d5e03cSMatthew Dillon     }
264b7d5e03cSMatthew Dillon     HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: id=%d, period=%d ms, size=%d bytes\n",
265b7d5e03cSMatthew Dillon             __func__, id, period, size);
266b7d5e03cSMatthew Dillon 
267b7d5e03cSMatthew Dillon     if (size < (n_data * 4)) {
268b7d5e03cSMatthew Dillon         n_data = (size + 3) / 4;
269b7d5e03cSMatthew Dillon     }
270b7d5e03cSMatthew Dillon     for (i=0; i<n_data * 4; i+=4) {
271b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, ka_data[id] + i, *datap);
272b7d5e03cSMatthew Dillon         /*HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) %08x\n", *datap);*/
273b7d5e03cSMatthew Dillon         datap++;
274b7d5e03cSMatthew Dillon     }
275b7d5e03cSMatthew Dillon }
276b7d5e03cSMatthew Dillon 
ar9300_wowoffload_download_arp_info(struct ath_hal * ah,u_int32_t id,u_int32_t * data)277b7d5e03cSMatthew Dillon void ar9300_wowoffload_download_arp_info(struct ath_hal *ah, u_int32_t id, u_int32_t *data)
278b7d5e03cSMatthew Dillon {
279b7d5e03cSMatthew Dillon     u_int32_t addr;
280b7d5e03cSMatthew Dillon     struct hal_wow_offload_arp_info *p_info = (struct hal_wow_offload_arp_info *) data;
281b7d5e03cSMatthew Dillon 
282b7d5e03cSMatthew Dillon     if (id == 0) {
283b7d5e03cSMatthew Dillon         addr = AR_WOW_OFFLOAD_ARP0_VALID;
284b7d5e03cSMatthew Dillon     } else if (id == 1) {
285b7d5e03cSMatthew Dillon         addr = AR_WOW_OFFLOAD_ARP1_VALID;
286b7d5e03cSMatthew Dillon     } else {
287b7d5e03cSMatthew Dillon         return;
288b7d5e03cSMatthew Dillon     }
289b7d5e03cSMatthew Dillon 
290b7d5e03cSMatthew Dillon     if (p_info->valid) {
291b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, addr, 0x1);
292b7d5e03cSMatthew Dillon         addr += 4;
293b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, addr, p_info->RemoteIPv4Address.u32);
294b7d5e03cSMatthew Dillon         addr += 4;
295b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, addr, p_info->HostIPv4Address.u32);
296b7d5e03cSMatthew Dillon         addr += 4;
297b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, addr, p_info->MacAddress.u32[0]);
298b7d5e03cSMatthew Dillon         addr += 4;
299b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, addr, p_info->MacAddress.u32[1]);
300b7d5e03cSMatthew Dillon     } else {
301b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, addr, 0x0);
302b7d5e03cSMatthew Dillon     }
303b7d5e03cSMatthew Dillon }
304b7d5e03cSMatthew Dillon 
305b7d5e03cSMatthew Dillon #define WOW_WRITE_NS_IPV6_ADDRESS(_ah, _buf_addr, _p_ipv6_addr) \
306b7d5e03cSMatthew Dillon     {                                                           \
307b7d5e03cSMatthew Dillon         u_int32_t   offset = (_buf_addr);                       \
308b7d5e03cSMatthew Dillon         u_int32_t  *p_ipv6_addr = (u_int32_t *) (_p_ipv6_addr); \
309b7d5e03cSMatthew Dillon         int i;                                                  \
310b7d5e03cSMatthew Dillon         for (i = 0; i < 4; i++) {                               \
311b7d5e03cSMatthew Dillon             OS_REG_WRITE((_ah), offset, *p_ipv6_addr);          \
312b7d5e03cSMatthew Dillon             offset += 4;                                        \
313b7d5e03cSMatthew Dillon             p_ipv6_addr ++;                                     \
314b7d5e03cSMatthew Dillon         }                                                       \
315b7d5e03cSMatthew Dillon     }
316b7d5e03cSMatthew Dillon 
ar9300_wowoffload_download_ns_info(struct ath_hal * ah,u_int32_t id,u_int32_t * data)317b7d5e03cSMatthew Dillon void ar9300_wowoffload_download_ns_info(struct ath_hal *ah, u_int32_t id, u_int32_t *data)
318b7d5e03cSMatthew Dillon {
319b7d5e03cSMatthew Dillon     u_int32_t addr;
320b7d5e03cSMatthew Dillon     struct hal_wow_offload_ns_info *p_info = (struct hal_wow_offload_ns_info *) data;
321b7d5e03cSMatthew Dillon     u_int8_t mc_addr[6];
322b7d5e03cSMatthew Dillon 
323b7d5e03cSMatthew Dillon     if (id == 0) {
324b7d5e03cSMatthew Dillon         addr = AR_WOW_OFFLOAD_NS0_VALID;
325b7d5e03cSMatthew Dillon     } else if (id == 1) {
326b7d5e03cSMatthew Dillon         addr = AR_WOW_OFFLOAD_NS1_VALID;
327b7d5e03cSMatthew Dillon     } else {
328b7d5e03cSMatthew Dillon         return;
329b7d5e03cSMatthew Dillon     }
330b7d5e03cSMatthew Dillon 
331b7d5e03cSMatthew Dillon     if (p_info->valid) {
332b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, addr, 0x1);
333b7d5e03cSMatthew Dillon         addr += 4;
334b7d5e03cSMatthew Dillon         WOW_WRITE_NS_IPV6_ADDRESS(ah, addr, &p_info->RemoteIPv6Address.u32[0]);
335b7d5e03cSMatthew Dillon         addr += 4 * 4;
336b7d5e03cSMatthew Dillon         WOW_WRITE_NS_IPV6_ADDRESS(ah, addr, &p_info->SolicitedNodeIPv6Address.u32[0]);
337b7d5e03cSMatthew Dillon         addr += 4 * 4;
338b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, addr, p_info->MacAddress.u32[0]);
339b7d5e03cSMatthew Dillon         addr += 4;
340b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, addr, p_info->MacAddress.u32[1]);
341b7d5e03cSMatthew Dillon         addr += 4;
342b7d5e03cSMatthew Dillon         WOW_WRITE_NS_IPV6_ADDRESS(ah, addr, &p_info->TargetIPv6Addresses[0].u32[0]);
343b7d5e03cSMatthew Dillon         addr += 4 * 4;
344b7d5e03cSMatthew Dillon         WOW_WRITE_NS_IPV6_ADDRESS(ah, addr, &p_info->TargetIPv6Addresses[1].u32[0]);
345b7d5e03cSMatthew Dillon 
346b7d5e03cSMatthew Dillon         mc_addr[0] = 0x33;
347b7d5e03cSMatthew Dillon         mc_addr[1] = 0x33;
348b7d5e03cSMatthew Dillon         mc_addr[2] = 0xFF;
349b7d5e03cSMatthew Dillon         mc_addr[3] = p_info->SolicitedNodeIPv6Address.u8[13];
350b7d5e03cSMatthew Dillon         mc_addr[4] = p_info->SolicitedNodeIPv6Address.u8[14];
351b7d5e03cSMatthew Dillon         mc_addr[5] = p_info->SolicitedNodeIPv6Address.u8[15];
352b7d5e03cSMatthew Dillon         ar9300_wowoffload_add_mcast_filter(ah, mc_addr);
353b7d5e03cSMatthew Dillon     } else {
354b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, addr, 0x0);
355b7d5e03cSMatthew Dillon     }
356b7d5e03cSMatthew Dillon }
357b7d5e03cSMatthew Dillon 
358b7d5e03cSMatthew Dillon /* Download transmit parameters for GTK response frame during WoW
359b7d5e03cSMatthew Dillon  * offload */
ar9300_wow_offload_download_hal_params(struct ath_hal * ah)360b7d5e03cSMatthew Dillon u_int32_t ar9300_wow_offload_download_hal_params(struct ath_hal *ah)
361b7d5e03cSMatthew Dillon {
362b7d5e03cSMatthew Dillon     u_int32_t tpc = 0x3f; /* Transmit Power Control */
363b7d5e03cSMatthew Dillon     u_int32_t tx_tries_series = 7;
364b7d5e03cSMatthew Dillon     u_int32_t tx_rate_series, transmit_rate;
365b7d5e03cSMatthew Dillon     u_int32_t gtk_txdesc_param_start;
366b7d5e03cSMatthew Dillon 
367b7d5e03cSMatthew Dillon     if (AH_PRIVATE(ah)->ah_curchan->channel_flags & CHANNEL_CCK) {
368b7d5e03cSMatthew Dillon         transmit_rate = 0x1B;    /* CCK_1M */
369b7d5e03cSMatthew Dillon     } else {
370b7d5e03cSMatthew Dillon         transmit_rate = 0xB;     /* OFDM_6M */
371b7d5e03cSMatthew Dillon     }
372b7d5e03cSMatthew Dillon 
373b7d5e03cSMatthew Dillon     /* Use single rate for now. Change later as need be */
374b7d5e03cSMatthew Dillon     tx_rate_series  = transmit_rate;
375b7d5e03cSMatthew Dillon     tx_tries_series = 7;
376b7d5e03cSMatthew Dillon 
377b7d5e03cSMatthew Dillon     if (AR_SREV_JUPITER(ah)) {
378b7d5e03cSMatthew Dillon         gtk_txdesc_param_start = AR_WOW_OFFLOAD_GTK_TXDESC_PARAM_START_JUPITER;
379b7d5e03cSMatthew Dillon     } else {
380b7d5e03cSMatthew Dillon         gtk_txdesc_param_start = AR_WOW_OFFLOAD_GTK_TXDESC_PARAM_START;
381b7d5e03cSMatthew Dillon     }
382b7d5e03cSMatthew Dillon #define AR_WOW_OFFLOAD_GTK_TXDESC_PARAM(x) (gtk_txdesc_param_start + ((x) * 4))
383b7d5e03cSMatthew Dillon 
384b7d5e03cSMatthew Dillon     /* Do not change the data order unless firmware code on embedded
385b7d5e03cSMatthew Dillon      * CPU is changed correspondingly */
386b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_WOW_OFFLOAD_GTK_TXDESC_PARAM(0), tx_rate_series);
387b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_WOW_OFFLOAD_GTK_TXDESC_PARAM(1), tx_tries_series);
388b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_WOW_OFFLOAD_GTK_TXDESC_PARAM(2), AH9300(ah)->ah_tx_chainmask);
389b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_WOW_OFFLOAD_GTK_TXDESC_PARAM(3), tpc);
390b7d5e03cSMatthew Dillon 
391b7d5e03cSMatthew Dillon     return 0;
392b7d5e03cSMatthew Dillon }
393b7d5e03cSMatthew Dillon 
394b7d5e03cSMatthew Dillon /* Indicate to the embedded CPU that host is ready to enter WoW mode.
395b7d5e03cSMatthew Dillon  * Embedded CPU will copy relevant information from the MAC PCU buffer
396b7d5e03cSMatthew Dillon  */
ar9300_wow_offload_handshake(struct ath_hal * ah,u_int32_t pattern_enable)397b7d5e03cSMatthew Dillon u_int32_t ar9300_wow_offload_handshake(struct ath_hal *ah, u_int32_t pattern_enable)
398b7d5e03cSMatthew Dillon {
399b7d5e03cSMatthew Dillon     int val;
400b7d5e03cSMatthew Dillon     int mbox_status = OS_REG_READ(ah, AR_MBOX_CTRL_STATUS);
401b7d5e03cSMatthew Dillon #if ATH_WOW_OFFLOAD
402b7d5e03cSMatthew Dillon     u_int32_t bt_handshake_timeout_us = HAL_WOW_CTRL_WAIT_BT_TO(ah) * 100000;
403b7d5e03cSMatthew Dillon 
404b7d5e03cSMatthew Dillon #define AH_DEFAULT_BT_WAIT_TIMEOUT  3000000; /* 3 sec */
405b7d5e03cSMatthew Dillon     if (bt_handshake_timeout_us == 0) {
406b7d5e03cSMatthew Dillon         bt_handshake_timeout_us = AH_DEFAULT_BT_WAIT_TIMEOUT;
407b7d5e03cSMatthew Dillon     }
408b7d5e03cSMatthew Dillon     HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) TIMEOUT: %d us\n", bt_handshake_timeout_us);
409b7d5e03cSMatthew Dillon #endif /* ATH_WOW_OFFLOAD */
410b7d5e03cSMatthew Dillon 
411b7d5e03cSMatthew Dillon     if (mbox_status & AR_MBOX_WOW_REQ) {
412b7d5e03cSMatthew Dillon         /* WOW mode request handshake is already in progress.
413b7d5e03cSMatthew Dillon          * Do nothing */
414b7d5e03cSMatthew Dillon         return 0;
415b7d5e03cSMatthew Dillon     }
416b7d5e03cSMatthew Dillon 
417b7d5e03cSMatthew Dillon     /* Clear status */
418b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_MBOX_CTRL_STATUS, 0);
419b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_EMB_CPU_WOW_STATUS, 0x0);
420b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_WLAN_WOW_ENABLE, 0);
421b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_WLAN_WOW_STATUS, 0xFFFFFFFF);
422b7d5e03cSMatthew Dillon 
423b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_RIMT, 0);
424b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_TIMT, 0);
425b7d5e03cSMatthew Dillon 
426b7d5e03cSMatthew Dillon     val = 0;
427b7d5e03cSMatthew Dillon     if (pattern_enable & AH_WOW_USER_PATTERN_EN) {
428b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - User pattern\n");
429b7d5e03cSMatthew Dillon         val |= AR_EMB_CPU_WOW_ENABLE_PATTERN_MATCH;
430b7d5e03cSMatthew Dillon     }
431b7d5e03cSMatthew Dillon     else {
432b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - User pattern\n");
433b7d5e03cSMatthew Dillon     }
434b7d5e03cSMatthew Dillon     if ((pattern_enable & AH_WOW_MAGIC_PATTERN_EN)
435b7d5e03cSMatthew Dillon #if ATH_WOW_OFFLOAD
436b7d5e03cSMatthew Dillon         || (pattern_enable & AH_WOW_ACER_MAGIC_EN)
437b7d5e03cSMatthew Dillon #endif
438b7d5e03cSMatthew Dillon         )
439b7d5e03cSMatthew Dillon     {
440b7d5e03cSMatthew Dillon         val |= AR_EMB_CPU_WOW_ENABLE_MAGIC_PATTERN;
441b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - Magic pattern\n");
442b7d5e03cSMatthew Dillon     }
443b7d5e03cSMatthew Dillon     else {
444b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - Magic pattern\n");
445b7d5e03cSMatthew Dillon     }
446b7d5e03cSMatthew Dillon     if ((pattern_enable & AH_WOW_LINK_CHANGE)
447b7d5e03cSMatthew Dillon #if ATH_WOW_OFFLOAD
448b7d5e03cSMatthew Dillon         || HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_KAFAIL_ENABLE)
449b7d5e03cSMatthew Dillon #endif
450b7d5e03cSMatthew Dillon         )
451b7d5e03cSMatthew Dillon     {
452b7d5e03cSMatthew Dillon         val |= AR_EMB_CPU_WOW_ENABLE_KEEP_ALIVE_FAIL;
453b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - Kepp alive fail\n");
454b7d5e03cSMatthew Dillon     }
455b7d5e03cSMatthew Dillon     else {
456b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - Kepp alive fail\n");
457b7d5e03cSMatthew Dillon     }
458b7d5e03cSMatthew Dillon     if (pattern_enable & AH_WOW_BEACON_MISS) {
459b7d5e03cSMatthew Dillon         val |= AR_EMB_CPU_WOW_ENABLE_BEACON_MISS;
460b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - Becon Miss\n");
461b7d5e03cSMatthew Dillon     }
462b7d5e03cSMatthew Dillon     else {
463b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - Becon Miss\n");
464b7d5e03cSMatthew Dillon     }
465b7d5e03cSMatthew Dillon 
466b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_EMB_CPU_WOW_ENABLE, val);
467b7d5e03cSMatthew Dillon 
468b7d5e03cSMatthew Dillon     OS_REG_CLR_BIT(ah, AR_MBOX_CTRL_STATUS, AR_MBOX_WOW_CONF);
469b7d5e03cSMatthew Dillon     OS_REG_SET_BIT(ah, AR_MBOX_CTRL_STATUS, AR_MBOX_WOW_REQ);
470b7d5e03cSMatthew Dillon     OS_REG_SET_BIT(ah, AR_MBOX_CTRL_STATUS, AR_MBOX_INT_EMB_CPU);
471b7d5e03cSMatthew Dillon 
472*a20e5e51SMatthew Dillon     if (!ath_hal_waitfor(ah, AR_MBOX_CTRL_STATUS, AR_MBOX_WOW_CONF, AR_MBOX_WOW_CONF, bt_handshake_timeout_us)) {
473b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: WoW offload handshake failed", __func__);
474b7d5e03cSMatthew Dillon         return 0;
475b7d5e03cSMatthew Dillon     }
476b7d5e03cSMatthew Dillon     else {
477b7d5e03cSMatthew Dillon         OS_REG_CLR_BIT(ah, AR_MBOX_CTRL_STATUS, AR_MBOX_WOW_CONF);
478b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_POWER_MGMT, "%s: WoW offload handshake successful",__func__);
479b7d5e03cSMatthew Dillon     }
480b7d5e03cSMatthew Dillon     return 1;
481b7d5e03cSMatthew Dillon }
482b7d5e03cSMatthew Dillon #endif /* ATH_WOW_OFFLOAD */
483b7d5e03cSMatthew Dillon 
484b7d5e03cSMatthew Dillon /*
485b7d5e03cSMatthew Dillon  * Notify Power Mgt is enabled in self-generated frames.
486b7d5e03cSMatthew Dillon  * If requested, force chip awake.
487b7d5e03cSMatthew Dillon  *
488b7d5e03cSMatthew Dillon  * Returns A_OK if chip is awake or successfully forced awake.
489b7d5e03cSMatthew Dillon  *
490b7d5e03cSMatthew Dillon  * WARNING WARNING WARNING
491b7d5e03cSMatthew Dillon  * There is a problem with the chip where sometimes it will not wake up.
492b7d5e03cSMatthew Dillon  */
493b7d5e03cSMatthew Dillon HAL_BOOL
ar9300_set_power_mode_awake(struct ath_hal * ah,int set_chip)494b7d5e03cSMatthew Dillon ar9300_set_power_mode_awake(struct ath_hal *ah, int set_chip)
495b7d5e03cSMatthew Dillon {
496b7d5e03cSMatthew Dillon     struct ath_hal_9300 *ahp = AH9300(ah);
497b7d5e03cSMatthew Dillon #define POWER_UP_TIME   10000
498b7d5e03cSMatthew Dillon     u_int32_t val;
499b7d5e03cSMatthew Dillon     int i;
500b7d5e03cSMatthew Dillon 
501b7d5e03cSMatthew Dillon     /* Set Bits 14 and 17 of AR_WA before powering on the chip. */
502b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA), ahp->ah_wa_reg_val);
503b7d5e03cSMatthew Dillon     OS_DELAY(10); /* delay to allow the write to take effect. */
504b7d5e03cSMatthew Dillon 
505b7d5e03cSMatthew Dillon     if (set_chip) {
506b7d5e03cSMatthew Dillon         /* Do a Power-On-Reset if MAC is shutdown */
507b7d5e03cSMatthew Dillon         if ((OS_REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_SHUTDOWN)) {
508b7d5e03cSMatthew Dillon             if (ar9300_set_reset_reg(ah, HAL_RESET_POWER_ON) != AH_TRUE) {
509b7d5e03cSMatthew Dillon                 HALASSERT(0);
510b7d5e03cSMatthew Dillon                 return AH_FALSE;
511b7d5e03cSMatthew Dillon             }
512b7d5e03cSMatthew Dillon         }
513b7d5e03cSMatthew Dillon 
514b7d5e03cSMatthew Dillon         OS_REG_SET_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN);
515b7d5e03cSMatthew Dillon 
516b7d5e03cSMatthew Dillon         OS_DELAY(50);
517b7d5e03cSMatthew Dillon 
518b7d5e03cSMatthew Dillon         for (i = POWER_UP_TIME / 50; i > 0; i--) {
519b7d5e03cSMatthew Dillon             val = OS_REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M;
520b7d5e03cSMatthew Dillon             if (val == AR_RTC_STATUS_ON) {
521b7d5e03cSMatthew Dillon                 break;
522b7d5e03cSMatthew Dillon             }
523b7d5e03cSMatthew Dillon             OS_DELAY(50);
524b7d5e03cSMatthew Dillon             OS_REG_SET_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN);
525b7d5e03cSMatthew Dillon         }
526b7d5e03cSMatthew Dillon         if (i == 0) {
527b7d5e03cSMatthew Dillon             HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: Failed to wakeup in %uus\n",
528b7d5e03cSMatthew Dillon                      __func__, POWER_UP_TIME / 20);
529b7d5e03cSMatthew Dillon             return AH_FALSE;
530b7d5e03cSMatthew Dillon         }
531b7d5e03cSMatthew Dillon 
532b7d5e03cSMatthew Dillon     }
533b7d5e03cSMatthew Dillon 
534b7d5e03cSMatthew Dillon     OS_REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
535b7d5e03cSMatthew Dillon     return AH_TRUE;
536b7d5e03cSMatthew Dillon #undef POWER_UP_TIME
537b7d5e03cSMatthew Dillon }
538b7d5e03cSMatthew Dillon 
539b7d5e03cSMatthew Dillon /*
540b7d5e03cSMatthew Dillon  * Notify Power Mgt is disabled in self-generated frames.
541b7d5e03cSMatthew Dillon  * If requested, force chip to sleep.
542b7d5e03cSMatthew Dillon  */
543b7d5e03cSMatthew Dillon static void
ar9300_set_power_mode_sleep(struct ath_hal * ah,int set_chip)544b7d5e03cSMatthew Dillon ar9300_set_power_mode_sleep(struct ath_hal *ah, int set_chip)
545b7d5e03cSMatthew Dillon {
546b7d5e03cSMatthew Dillon     struct ath_hal_9300 *ahp = AH9300(ah);
547b7d5e03cSMatthew Dillon 
548b7d5e03cSMatthew Dillon     OS_REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
549b7d5e03cSMatthew Dillon     if (set_chip ) {
550b7d5e03cSMatthew Dillon         if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) {
551b7d5e03cSMatthew Dillon             OS_REG_WRITE(ah, AR_TIMER_MODE,
552b7d5e03cSMatthew Dillon                     OS_REG_READ(ah, AR_TIMER_MODE) & 0xFFFFFF00);
553b7d5e03cSMatthew Dillon             OS_REG_WRITE(ah, AR_GEN_TIMERS2_MODE,
554b7d5e03cSMatthew Dillon                     OS_REG_READ(ah, AR_GEN_TIMERS2_MODE) & 0xFFFFFF00);
555b7d5e03cSMatthew Dillon             OS_REG_WRITE(ah, AR_SLP32_INC,
556b7d5e03cSMatthew Dillon                     OS_REG_READ(ah, AR_SLP32_INC) & 0xFFF00000);
557b7d5e03cSMatthew Dillon             OS_REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN, 0);
558b7d5e03cSMatthew Dillon             OS_DELAY(100);
559b7d5e03cSMatthew Dillon         }
560b7d5e03cSMatthew Dillon         /* Clear the RTC force wake bit to allow the mac to go to sleep */
561b7d5e03cSMatthew Dillon         OS_REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN);
562b7d5e03cSMatthew Dillon 
563b7d5e03cSMatthew Dillon         if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) {
564b7d5e03cSMatthew Dillon             /*
565b7d5e03cSMatthew Dillon              * In Jupiter, after enter sleep mode, hardware will send
566b7d5e03cSMatthew Dillon              * a SYS_SLEEPING message through MCI interface. Add a
567b7d5e03cSMatthew Dillon              * few us delay to make sure the message can reach BT side.
568b7d5e03cSMatthew Dillon              */
569b7d5e03cSMatthew Dillon             OS_DELAY(100);
570b7d5e03cSMatthew Dillon         }
571b7d5e03cSMatthew Dillon 
572b7d5e03cSMatthew Dillon         if (!AR_SREV_JUPITER_10(ah)) {
573b7d5e03cSMatthew Dillon             /* Shutdown chip. Active low */
574b7d5e03cSMatthew Dillon             OS_REG_CLR_BIT(ah, AR_RTC_RESET, AR_RTC_RESET_EN);
575b7d5e03cSMatthew Dillon             /* Settle time */
576b7d5e03cSMatthew Dillon             OS_DELAY(2);
577b7d5e03cSMatthew Dillon         }
578b7d5e03cSMatthew Dillon     }
579b7d5e03cSMatthew Dillon 
580b7d5e03cSMatthew Dillon #if ATH_WOW_OFFLOAD
581b7d5e03cSMatthew Dillon     if (!AR_SREV_JUPITER(ah) || !HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_SET_4004_BIT14))
582b7d5e03cSMatthew Dillon #endif /* ATH_WOW_OFFLOAD */
583b7d5e03cSMatthew Dillon     {
584b7d5e03cSMatthew Dillon         /* Clear Bit 14 of AR_WA after putting chip into Full Sleep mode. */
585b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA),
586b7d5e03cSMatthew Dillon                ahp->ah_wa_reg_val & ~AR_WA_D3_TO_L1_DISABLE);
587b7d5e03cSMatthew Dillon     }
588b7d5e03cSMatthew Dillon }
589b7d5e03cSMatthew Dillon 
590b7d5e03cSMatthew Dillon /*
591b7d5e03cSMatthew Dillon  * Notify Power Management is enabled in self-generating
592b7d5e03cSMatthew Dillon  * frames. If request, set power mode of chip to
593b7d5e03cSMatthew Dillon  * auto/normal.  Duration in units of 128us (1/8 TU).
594b7d5e03cSMatthew Dillon  */
595b7d5e03cSMatthew Dillon static void
ar9300_set_power_mode_network_sleep(struct ath_hal * ah,int set_chip)596b7d5e03cSMatthew Dillon ar9300_set_power_mode_network_sleep(struct ath_hal *ah, int set_chip)
597b7d5e03cSMatthew Dillon {
598b7d5e03cSMatthew Dillon     struct ath_hal_9300 *ahp = AH9300(ah);
599b7d5e03cSMatthew Dillon 
600b7d5e03cSMatthew Dillon     OS_REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
601b7d5e03cSMatthew Dillon     if (set_chip) {
602b7d5e03cSMatthew Dillon         HAL_CAPABILITIES *p_cap = &AH_PRIVATE(ah)->ah_caps;
603b7d5e03cSMatthew Dillon 
604b7d5e03cSMatthew Dillon         if (! p_cap->halAutoSleepSupport) {
605b7d5e03cSMatthew Dillon             /* Set wake_on_interrupt bit; clear force_wake bit */
606b7d5e03cSMatthew Dillon             OS_REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON_INT);
607b7d5e03cSMatthew Dillon         }
608b7d5e03cSMatthew Dillon         else {
609b7d5e03cSMatthew Dillon             /*
610b7d5e03cSMatthew Dillon              * When chip goes into network sleep, it could be waken up by
611b7d5e03cSMatthew Dillon              * MCI_INT interrupt caused by BT's HW messages (LNA_xxx, CONT_xxx)
612b7d5e03cSMatthew Dillon              * which chould be in a very fast rate (~100us). This will cause
613b7d5e03cSMatthew Dillon              * chip to leave and re-enter network sleep mode frequently, which
614b7d5e03cSMatthew Dillon              * in consequence will have WLAN MCI HW to generate lots of
615b7d5e03cSMatthew Dillon              * SYS_WAKING and SYS_SLEEPING messages which will make BT CPU
616b7d5e03cSMatthew Dillon              * to busy to process.
617b7d5e03cSMatthew Dillon              */
618b7d5e03cSMatthew Dillon             if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) {
619b7d5e03cSMatthew Dillon                 OS_REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN,
620b7d5e03cSMatthew Dillon                         OS_REG_READ(ah, AR_MCI_INTERRUPT_RX_MSG_EN) &
621b7d5e03cSMatthew Dillon                                     ~AR_MCI_INTERRUPT_RX_HW_MSG_MASK);
622b7d5e03cSMatthew Dillon             }
623b7d5e03cSMatthew Dillon 
624b7d5e03cSMatthew Dillon             /* Clear the RTC force wake bit to allow the mac to go to sleep */
625b7d5e03cSMatthew Dillon             OS_REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN);
626b7d5e03cSMatthew Dillon 
627b7d5e03cSMatthew Dillon             if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) {
628b7d5e03cSMatthew Dillon                 /*
629b7d5e03cSMatthew Dillon                  * In Jupiter, after enter sleep mode, hardware will send
630b7d5e03cSMatthew Dillon                  * a SYS_SLEEPING message through MCI interface. Add a
631b7d5e03cSMatthew Dillon                  * few us delay to make sure the message can reach BT side.
632b7d5e03cSMatthew Dillon                  */
633b7d5e03cSMatthew Dillon                 OS_DELAY(30);
634b7d5e03cSMatthew Dillon             }
635b7d5e03cSMatthew Dillon         }
636b7d5e03cSMatthew Dillon     }
637b7d5e03cSMatthew Dillon 
638b7d5e03cSMatthew Dillon #if ATH_WOW_OFFLOAD
639b7d5e03cSMatthew Dillon     if (!AR_SREV_JUPITER(ah) || !HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_SET_4004_BIT14))
640b7d5e03cSMatthew Dillon #endif /* ATH_WOW_OFFLOAD */
641b7d5e03cSMatthew Dillon     {
642b7d5e03cSMatthew Dillon         /* Clear Bit 14 of AR_WA after putting chip into Sleep mode. */
643b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA),
644b7d5e03cSMatthew Dillon                ahp->ah_wa_reg_val & ~AR_WA_D3_TO_L1_DISABLE);
645b7d5e03cSMatthew Dillon     }
646b7d5e03cSMatthew Dillon }
647b7d5e03cSMatthew Dillon 
648b7d5e03cSMatthew Dillon /*
649b7d5e03cSMatthew Dillon  * Set power mgt to the requested mode, and conditionally set
650b7d5e03cSMatthew Dillon  * the chip as well
651b7d5e03cSMatthew Dillon  */
652b7d5e03cSMatthew Dillon HAL_BOOL
ar9300_set_power_mode(struct ath_hal * ah,HAL_POWER_MODE mode,int set_chip)653b7d5e03cSMatthew Dillon ar9300_set_power_mode(struct ath_hal *ah, HAL_POWER_MODE mode, int set_chip)
654b7d5e03cSMatthew Dillon {
655b7d5e03cSMatthew Dillon     struct ath_hal_9300 *ahp = AH9300(ah);
656b7d5e03cSMatthew Dillon #if defined(AH_DEBUG) || defined(AH_PRINT_FILTER)
657b7d5e03cSMatthew Dillon     static const char* modes[] = {
658b7d5e03cSMatthew Dillon         "AWAKE",
659b7d5e03cSMatthew Dillon         "FULL-SLEEP",
660b7d5e03cSMatthew Dillon         "NETWORK SLEEP",
661b7d5e03cSMatthew Dillon         "UNDEFINED"
662b7d5e03cSMatthew Dillon     };
663b7d5e03cSMatthew Dillon #endif
664b7d5e03cSMatthew Dillon     int status = AH_TRUE;
665b7d5e03cSMatthew Dillon 
666b7d5e03cSMatthew Dillon     HALDEBUG(ah, HAL_DEBUG_POWER_MGMT, "%s: %s -> %s (%s)\n", __func__,
667b7d5e03cSMatthew Dillon         modes[ar9300_get_power_mode(ah)], modes[mode],
668b7d5e03cSMatthew Dillon         set_chip ? "set chip " : "");
6694b8649cbSMatthew Dillon     OS_MARK(ah, AH_MARK_CHIP_POWER, mode);
6704b8649cbSMatthew Dillon 
671b7d5e03cSMatthew Dillon     switch (mode) {
672b7d5e03cSMatthew Dillon     case HAL_PM_AWAKE:
673d98a0bcfSMatthew Dillon         if (set_chip)
674d98a0bcfSMatthew Dillon             ah->ah_powerMode = mode;
675b7d5e03cSMatthew Dillon         status = ar9300_set_power_mode_awake(ah, set_chip);
676b7d5e03cSMatthew Dillon #if ATH_SUPPORT_MCI
677b7d5e03cSMatthew Dillon         if (AH_PRIVATE(ah)->ah_caps.halMciSupport) {
678b7d5e03cSMatthew Dillon             OS_REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
679b7d5e03cSMatthew Dillon         }
680b7d5e03cSMatthew Dillon #endif
681*a20e5e51SMatthew Dillon         ahp->ah_chip_full_sleep = AH_FALSE;
682b7d5e03cSMatthew Dillon         break;
683b7d5e03cSMatthew Dillon     case HAL_PM_FULL_SLEEP:
684b7d5e03cSMatthew Dillon #if ATH_SUPPORT_MCI
685b7d5e03cSMatthew Dillon         if (AH_PRIVATE(ah)->ah_caps.halMciSupport) {
686b7d5e03cSMatthew Dillon             if (ar9300_get_power_mode(ah) == HAL_PM_AWAKE) {
687b7d5e03cSMatthew Dillon                 if ((ar9300_mci_state(ah, HAL_MCI_STATE_ENABLE, NULL) != 0) &&
688b7d5e03cSMatthew Dillon                     (ahp->ah_mci_bt_state != MCI_BT_SLEEP) &&
689b7d5e03cSMatthew Dillon                     !ahp->ah_mci_halted_bt_gpm)
690b7d5e03cSMatthew Dillon                 {
691b7d5e03cSMatthew Dillon                     HALDEBUG(ah, HAL_DEBUG_BT_COEX,
692b7d5e03cSMatthew Dillon                         "(MCI) %s: HALT BT GPM (full_sleep)\n", __func__);
693b7d5e03cSMatthew Dillon                     ar9300_mci_send_coex_halt_bt_gpm(ah, AH_TRUE, AH_TRUE);
694b7d5e03cSMatthew Dillon                 }
695b7d5e03cSMatthew Dillon             }
696b7d5e03cSMatthew Dillon             ahp->ah_mci_ready = AH_FALSE;
697b7d5e03cSMatthew Dillon         }
698b7d5e03cSMatthew Dillon #endif
699b7d5e03cSMatthew Dillon #if ATH_SUPPORT_MCI
700b7d5e03cSMatthew Dillon         if (AH_PRIVATE(ah)->ah_caps.halMciSupport) {
701b7d5e03cSMatthew Dillon             OS_REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
702b7d5e03cSMatthew Dillon         }
703b7d5e03cSMatthew Dillon #endif
704b7d5e03cSMatthew Dillon         ar9300_set_power_mode_sleep(ah, set_chip);
705d98a0bcfSMatthew Dillon         if (set_chip) {
706b7d5e03cSMatthew Dillon             ahp->ah_chip_full_sleep = AH_TRUE;
707d98a0bcfSMatthew Dillon             ah->ah_powerMode = mode;
708d98a0bcfSMatthew Dillon         }
709b7d5e03cSMatthew Dillon         break;
710b7d5e03cSMatthew Dillon     case HAL_PM_NETWORK_SLEEP:
711b7d5e03cSMatthew Dillon #if ATH_SUPPORT_MCI
712b7d5e03cSMatthew Dillon         if (AH_PRIVATE(ah)->ah_caps.halMciSupport) {
713b7d5e03cSMatthew Dillon             OS_REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
714b7d5e03cSMatthew Dillon         }
715b7d5e03cSMatthew Dillon #endif
716b7d5e03cSMatthew Dillon         ar9300_set_power_mode_network_sleep(ah, set_chip);
717d98a0bcfSMatthew Dillon         if (set_chip) {
718d98a0bcfSMatthew Dillon             ah->ah_powerMode = mode;
719d98a0bcfSMatthew Dillon         }
720b7d5e03cSMatthew Dillon         break;
721b7d5e03cSMatthew Dillon     default:
722b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_POWER_MGMT,
723b7d5e03cSMatthew Dillon             "%s: unknown power mode %u\n", __func__, mode);
7244b8649cbSMatthew Dillon         OS_MARK(ah, AH_MARK_CHIP_POWER_DONE, -1);
725b7d5e03cSMatthew Dillon         return AH_FALSE;
726b7d5e03cSMatthew Dillon     }
7274b8649cbSMatthew Dillon     OS_MARK(ah, AH_MARK_CHIP_POWER_DONE, status);
728b7d5e03cSMatthew Dillon     return status;
729b7d5e03cSMatthew Dillon }
730b7d5e03cSMatthew Dillon 
731b7d5e03cSMatthew Dillon /*
732b7d5e03cSMatthew Dillon  * Return the current sleep mode of the chip
733b7d5e03cSMatthew Dillon  */
734b7d5e03cSMatthew Dillon HAL_POWER_MODE
ar9300_get_power_mode(struct ath_hal * ah)735b7d5e03cSMatthew Dillon ar9300_get_power_mode(struct ath_hal *ah)
736b7d5e03cSMatthew Dillon {
737b7d5e03cSMatthew Dillon     int mode = OS_REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M;
738b7d5e03cSMatthew Dillon 
739b7d5e03cSMatthew Dillon     switch (mode) {
740b7d5e03cSMatthew Dillon     case AR_RTC_STATUS_ON:
741b7d5e03cSMatthew Dillon     case AR_RTC_STATUS_WAKEUP:
742b7d5e03cSMatthew Dillon         return HAL_PM_AWAKE;
743b7d5e03cSMatthew Dillon         break;
744b7d5e03cSMatthew Dillon     case AR_RTC_STATUS_SLEEP:
745b7d5e03cSMatthew Dillon         return HAL_PM_NETWORK_SLEEP;
746b7d5e03cSMatthew Dillon         break;
747b7d5e03cSMatthew Dillon     case AR_RTC_STATUS_SHUTDOWN:
748b7d5e03cSMatthew Dillon         return HAL_PM_FULL_SLEEP;
749b7d5e03cSMatthew Dillon         break;
750b7d5e03cSMatthew Dillon     default:
751b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_POWER_MGMT,
752b7d5e03cSMatthew Dillon             "%s: unknown power mode 0x%x\n", __func__, mode);
753b7d5e03cSMatthew Dillon         return HAL_PM_UNDEFINED;
754b7d5e03cSMatthew Dillon     }
755b7d5e03cSMatthew Dillon }
756b7d5e03cSMatthew Dillon 
757b7d5e03cSMatthew Dillon /*
758b7d5e03cSMatthew Dillon  * Set SM power save mode
759b7d5e03cSMatthew Dillon  */
760b7d5e03cSMatthew Dillon void
ar9300_set_sm_power_mode(struct ath_hal * ah,HAL_SMPS_MODE mode)761b7d5e03cSMatthew Dillon ar9300_set_sm_power_mode(struct ath_hal *ah, HAL_SMPS_MODE mode)
762b7d5e03cSMatthew Dillon {
763b7d5e03cSMatthew Dillon     int regval;
764b7d5e03cSMatthew Dillon     struct ath_hal_9300 *ahp = AH9300(ah);
765b7d5e03cSMatthew Dillon 
766b7d5e03cSMatthew Dillon     if (ar9300_get_capability(ah, HAL_CAP_DYNAMIC_SMPS, 0, AH_NULL) != HAL_OK) {
767b7d5e03cSMatthew Dillon         return;
768b7d5e03cSMatthew Dillon     }
769b7d5e03cSMatthew Dillon 
770b7d5e03cSMatthew Dillon     /* Program low & high power chainmask settings and enable MAC control */
771b7d5e03cSMatthew Dillon     regval = SM(AR_PCU_SMPS_LPWR_CHNMSK_VAL, AR_PCU_SMPS_LPWR_CHNMSK) |
772b7d5e03cSMatthew Dillon              SM(ahp->ah_rx_chainmask, AR_PCU_SMPS_HPWR_CHNMSK) |
773b7d5e03cSMatthew Dillon              AR_PCU_SMPS_MAC_CHAINMASK;
774b7d5e03cSMatthew Dillon 
775b7d5e03cSMatthew Dillon     /* Program registers according to required SM power mode.*/
776b7d5e03cSMatthew Dillon     switch (mode) {
777b7d5e03cSMatthew Dillon     case HAL_SMPS_SW_CTRL_LOW_PWR:
778b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, AR_PCU_SMPS, regval);
779b7d5e03cSMatthew Dillon         break;
780b7d5e03cSMatthew Dillon     case HAL_SMPS_SW_CTRL_HIGH_PWR:
781b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, AR_PCU_SMPS, regval | AR_PCU_SMPS_SW_CTRL_HPWR);
782b7d5e03cSMatthew Dillon         break;
783b7d5e03cSMatthew Dillon     case HAL_SMPS_HW_CTRL:
784b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, AR_PCU_SMPS, regval | AR_PCU_SMPS_HW_CTRL_EN);
785b7d5e03cSMatthew Dillon         break;
786b7d5e03cSMatthew Dillon     case HAL_SMPS_DEFAULT:
787b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, AR_PCU_SMPS, 0);
788b7d5e03cSMatthew Dillon         break;
789b7d5e03cSMatthew Dillon     default:
790b7d5e03cSMatthew Dillon         break;
791b7d5e03cSMatthew Dillon     }
792b7d5e03cSMatthew Dillon     ahp->ah_sm_power_mode = mode;
793b7d5e03cSMatthew Dillon }
794b7d5e03cSMatthew Dillon 
795b7d5e03cSMatthew Dillon #if ATH_WOW
796b7d5e03cSMatthew Dillon #if NOT_NEEDED_FOR_OSPREY /* not compiled for darwin */
797b7d5e03cSMatthew Dillon /*
798b7d5e03cSMatthew Dillon  * This routine is called to configure the SerDes register for the
799b7d5e03cSMatthew Dillon  * Merlin 2.0 and above chip during WOW sleep.
800b7d5e03cSMatthew Dillon  */
801b7d5e03cSMatthew Dillon static void
ar9280_config_ser_des__wow_sleep(struct ath_hal * ah)802b7d5e03cSMatthew Dillon ar9280_config_ser_des__wow_sleep(struct ath_hal *ah)
803b7d5e03cSMatthew Dillon {
804b7d5e03cSMatthew Dillon     int i;
805b7d5e03cSMatthew Dillon     struct ath_hal_9300 *ahp = AH9300(ah);
806b7d5e03cSMatthew Dillon 
807b7d5e03cSMatthew Dillon     /*
808b7d5e03cSMatthew Dillon      * For WOW sleep, we reprogram the SerDes so that the PLL and CHK REQ
809b7d5e03cSMatthew Dillon      * are both enabled. This uses more power but the Maverick team reported
810b7d5e03cSMatthew Dillon      * that otherwise, WOW sleep is unstable and chip may disappears.
811b7d5e03cSMatthew Dillon      */
812b7d5e03cSMatthew Dillon     for (i = 0; i < ahp->ah_ini_pcie_serdes_wow.ia_rows; i++) {
813b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah,
814b7d5e03cSMatthew Dillon             INI_RA(&ahp->ah_ini_pcie_serdes_wow, i, 0),
815b7d5e03cSMatthew Dillon             INI_RA(&ahp->ah_ini_pcie_serdes_wow, i, 1));
816b7d5e03cSMatthew Dillon     }
817b7d5e03cSMatthew Dillon     OS_DELAY(1000);
818b7d5e03cSMatthew Dillon }
819b7d5e03cSMatthew Dillon #endif /* if NOT_NEEDED_FOR_OSPREY */
820b7d5e03cSMatthew Dillon static HAL_BOOL
ar9300_wow_create_keep_alive_pattern(struct ath_hal * ah)821b7d5e03cSMatthew Dillon ar9300_wow_create_keep_alive_pattern(struct ath_hal *ah)
822b7d5e03cSMatthew Dillon {
823b7d5e03cSMatthew Dillon     struct ath_hal_9300 *ahp = AH9300(ah);
824b7d5e03cSMatthew Dillon     u_int32_t  frame_len = 28;
825b7d5e03cSMatthew Dillon     u_int32_t  tpc = 0x3f;
826b7d5e03cSMatthew Dillon     u_int32_t  transmit_rate;
827b7d5e03cSMatthew Dillon     u_int32_t  frame_type = 0x2;    /* Frame Type -> Data; */
828b7d5e03cSMatthew Dillon     u_int32_t  sub_type = 0x4;      /* Subtype -> Null Data */
829b7d5e03cSMatthew Dillon     u_int32_t  to_ds = 1;
830b7d5e03cSMatthew Dillon     u_int32_t  duration_id = 0x3d;
831b7d5e03cSMatthew Dillon     u_int8_t   *sta_mac_addr, *ap_mac_addr;
832b7d5e03cSMatthew Dillon     u_int8_t   *addr1, *addr2, *addr3;
833b7d5e03cSMatthew Dillon     u_int32_t  ctl[13] = { 0, };
834b7d5e03cSMatthew Dillon #define NUM_KA_DATA_WORDS 6
835b7d5e03cSMatthew Dillon     u_int32_t  data_word[NUM_KA_DATA_WORDS];
836b7d5e03cSMatthew Dillon     u_int32_t  i;
837b7d5e03cSMatthew Dillon     u_int32_t wow_ka_dataword0;
838b7d5e03cSMatthew Dillon 
839b7d5e03cSMatthew Dillon     sta_mac_addr = (u_int8_t *)ahp->ah_macaddr;
840b7d5e03cSMatthew Dillon     ap_mac_addr = (u_int8_t *)ahp->ah_bssid;
841b7d5e03cSMatthew Dillon     addr2 = sta_mac_addr;
842b7d5e03cSMatthew Dillon     addr1 = addr3 = ap_mac_addr;
843b7d5e03cSMatthew Dillon 
844b7d5e03cSMatthew Dillon     if (AH_PRIVATE(ah)->ah_curchan->channel_flags & CHANNEL_CCK) {
845b7d5e03cSMatthew Dillon         transmit_rate = 0x1B;    /* CCK_1M */
846b7d5e03cSMatthew Dillon     } else {
847b7d5e03cSMatthew Dillon         transmit_rate = 0xB;     /* OFDM_6M */
848b7d5e03cSMatthew Dillon     }
849b7d5e03cSMatthew Dillon 
850b7d5e03cSMatthew Dillon     /* Set the Transmit Buffer. */
851b7d5e03cSMatthew Dillon     ctl[0] = (frame_len | (tpc << 16));
852b7d5e03cSMatthew Dillon     ctl[1] = 0;
853b7d5e03cSMatthew Dillon     ctl[2] = (0x7 << 16);  /* tx_tries0 */
854b7d5e03cSMatthew Dillon     ctl[3] = transmit_rate;
855b7d5e03cSMatthew Dillon     ctl[4] = 0;
856b7d5e03cSMatthew Dillon     ctl[7] = ahp->ah_tx_chainmask << 2;
857b7d5e03cSMatthew Dillon 
858b7d5e03cSMatthew Dillon     for (i = 0; i < 13; i++) {
859b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]);
860b7d5e03cSMatthew Dillon     }
861b7d5e03cSMatthew Dillon 
862b7d5e03cSMatthew Dillon     data_word[0] =
863b7d5e03cSMatthew Dillon         (frame_type  <<  2) |
864b7d5e03cSMatthew Dillon         (sub_type    <<  4) |
865b7d5e03cSMatthew Dillon         (to_ds       <<  8) |
866b7d5e03cSMatthew Dillon         (duration_id << 16);
867b7d5e03cSMatthew Dillon     data_word[1] = (((u_int32_t)addr1[3] << 24) | ((u_int32_t)addr1[2] << 16) |
868b7d5e03cSMatthew Dillon                   ((u_int32_t)addr1[1]) << 8 | ((u_int32_t)addr1[0]));
869b7d5e03cSMatthew Dillon     data_word[2] = (((u_int32_t)addr2[1] << 24) | ((u_int32_t)addr2[0] << 16) |
870b7d5e03cSMatthew Dillon                   ((u_int32_t)addr1[5]) << 8 | ((u_int32_t)addr1[4]));
871b7d5e03cSMatthew Dillon     data_word[3] = (((u_int32_t)addr2[5] << 24) | ((u_int32_t)addr2[4] << 16) |
872b7d5e03cSMatthew Dillon                   ((u_int32_t)addr2[3]) << 8 | ((u_int32_t)addr2[2]));
873b7d5e03cSMatthew Dillon     data_word[4] = (((u_int32_t)addr3[3] << 24) | ((u_int32_t)addr3[2] << 16) |
874b7d5e03cSMatthew Dillon                   ((u_int32_t)addr3[1]) << 8 | (u_int32_t)addr3[0]);
875b7d5e03cSMatthew Dillon     data_word[5] = (((u_int32_t)addr3[5]) << 8 | ((u_int32_t)addr3[4]));
876b7d5e03cSMatthew Dillon 
877b7d5e03cSMatthew Dillon     if (AR_SREV_JUPITER_20_OR_LATER(ah) || AR_SREV_APHRODITE(ah)) {
878b7d5e03cSMatthew Dillon         /* Jupiter 2.0 has an extra descriptor word (Time based
879b7d5e03cSMatthew Dillon          * discard) compared to other chips */
880b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + 12 * 4), 0);
881b7d5e03cSMatthew Dillon         wow_ka_dataword0 = AR_WOW_TXBUF(13);
882b7d5e03cSMatthew Dillon     }
883b7d5e03cSMatthew Dillon     else {
884b7d5e03cSMatthew Dillon         wow_ka_dataword0 = AR_WOW_TXBUF(12);
885b7d5e03cSMatthew Dillon     }
886b7d5e03cSMatthew Dillon 
887b7d5e03cSMatthew Dillon     for (i = 0; i < NUM_KA_DATA_WORDS; i++) {
888b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, (wow_ka_dataword0 + i * 4), data_word[i]);
889b7d5e03cSMatthew Dillon     }
890b7d5e03cSMatthew Dillon 
891b7d5e03cSMatthew Dillon     return AH_TRUE;
892b7d5e03cSMatthew Dillon }
893b7d5e03cSMatthew Dillon 
894b7d5e03cSMatthew Dillon /* TBD: Should querying hal for hardware capability */
895b7d5e03cSMatthew Dillon #define MAX_PATTERN_SIZE      256
896b7d5e03cSMatthew Dillon #define MAX_PATTERN_MASK_SIZE  32
897b7d5e03cSMatthew Dillon #define MAX_NUM_USER_PATTERN    6 /* Deducting the disassoc/deauth packets */
898b7d5e03cSMatthew Dillon 
899b7d5e03cSMatthew Dillon void
ar9300_wow_apply_pattern(struct ath_hal * ah,u_int8_t * p_ath_pattern,u_int8_t * p_ath_mask,int32_t pattern_count,u_int32_t ath_pattern_len)900b7d5e03cSMatthew Dillon ar9300_wow_apply_pattern(
901b7d5e03cSMatthew Dillon     struct ath_hal *ah,
902b7d5e03cSMatthew Dillon     u_int8_t *p_ath_pattern,
903b7d5e03cSMatthew Dillon     u_int8_t *p_ath_mask,
904b7d5e03cSMatthew Dillon     int32_t pattern_count,
905b7d5e03cSMatthew Dillon     u_int32_t ath_pattern_len)
906b7d5e03cSMatthew Dillon {
907b7d5e03cSMatthew Dillon     int i;
908b7d5e03cSMatthew Dillon     u_int32_t    reg_pat[] = {
909b7d5e03cSMatthew Dillon                   AR_WOW_TB_PATTERN0,
910b7d5e03cSMatthew Dillon                   AR_WOW_TB_PATTERN1,
911b7d5e03cSMatthew Dillon                   AR_WOW_TB_PATTERN2,
912b7d5e03cSMatthew Dillon                   AR_WOW_TB_PATTERN3,
913b7d5e03cSMatthew Dillon                   AR_WOW_TB_PATTERN4,
914b7d5e03cSMatthew Dillon                   AR_WOW_TB_PATTERN5,
915b7d5e03cSMatthew Dillon                   AR_WOW_TB_PATTERN6,
916b7d5e03cSMatthew Dillon                   AR_WOW_TB_PATTERN7
917b7d5e03cSMatthew Dillon                  };
918b7d5e03cSMatthew Dillon     u_int32_t    reg_mask[] = {
919b7d5e03cSMatthew Dillon                   AR_WOW_TB_MASK0,
920b7d5e03cSMatthew Dillon                   AR_WOW_TB_MASK1,
921b7d5e03cSMatthew Dillon                   AR_WOW_TB_MASK2,
922b7d5e03cSMatthew Dillon                   AR_WOW_TB_MASK3,
923b7d5e03cSMatthew Dillon                   AR_WOW_TB_MASK4,
924b7d5e03cSMatthew Dillon                   AR_WOW_TB_MASK5,
925b7d5e03cSMatthew Dillon                   AR_WOW_TB_MASK6,
926b7d5e03cSMatthew Dillon                   AR_WOW_TB_MASK7
927b7d5e03cSMatthew Dillon                  };
928b7d5e03cSMatthew Dillon     u_int32_t   pattern_val;
929b7d5e03cSMatthew Dillon     u_int32_t   mask_val;
930b7d5e03cSMatthew Dillon     u_int32_t   val;
931b7d5e03cSMatthew Dillon     u_int8_t    mask_bit = 0x1;
932b7d5e03cSMatthew Dillon     u_int8_t    pattern;
933b7d5e03cSMatthew Dillon 
934b7d5e03cSMatthew Dillon     /* TBD: should check count by querying the hardware capability */
935b7d5e03cSMatthew Dillon     if (pattern_count >= MAX_NUM_USER_PATTERN) {
936b7d5e03cSMatthew Dillon         return;
937b7d5e03cSMatthew Dillon     }
938b7d5e03cSMatthew Dillon 
939b7d5e03cSMatthew Dillon     pattern = (u_int8_t)OS_REG_READ(ah, AR_WOW_PATTERN_REG);
940b7d5e03cSMatthew Dillon     pattern = pattern | (mask_bit << pattern_count);
941b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_WOW_PATTERN_REG, pattern);
942b7d5e03cSMatthew Dillon 
943b7d5e03cSMatthew Dillon     /* Set the registers for pattern */
944b7d5e03cSMatthew Dillon     for (i = 0; i < MAX_PATTERN_SIZE; i += 4) {
945b7d5e03cSMatthew Dillon         pattern_val = (((u_int32_t)p_ath_pattern[i + 0]) |
946b7d5e03cSMatthew Dillon                        ((u_int32_t)p_ath_pattern[i + 1] << 8) |
947b7d5e03cSMatthew Dillon                        ((u_int32_t)p_ath_pattern[i + 2] << 16) |
948b7d5e03cSMatthew Dillon                        ((u_int32_t)p_ath_pattern[i + 3] << 24));
949b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, (reg_pat[pattern_count] + i), pattern_val);
950b7d5e03cSMatthew Dillon     }
951b7d5e03cSMatthew Dillon 
952b7d5e03cSMatthew Dillon     /* Set the registers for mask */
953b7d5e03cSMatthew Dillon     for (i = 0; i < MAX_PATTERN_MASK_SIZE; i += 4) {
954b7d5e03cSMatthew Dillon         mask_val = (((u_int32_t)p_ath_mask[i + 0]) |
955b7d5e03cSMatthew Dillon                     ((u_int32_t)p_ath_mask[i + 1] << 8) |
956b7d5e03cSMatthew Dillon                     ((u_int32_t)p_ath_mask[i + 2] << 16) |
957b7d5e03cSMatthew Dillon                     ((u_int32_t)p_ath_mask[i + 3] << 24));
958b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, (reg_mask[pattern_count] + i), mask_val);
959b7d5e03cSMatthew Dillon     }
960b7d5e03cSMatthew Dillon 
961b7d5e03cSMatthew Dillon     /* XXX */
962b7d5e03cSMatthew Dillon     /* Set the pattern length to be matched */
963b7d5e03cSMatthew Dillon     if (pattern_count < 4) {
964b7d5e03cSMatthew Dillon         /* Pattern 0-3 uses AR_WOW_LENGTH1_REG register */
965b7d5e03cSMatthew Dillon         val = OS_REG_READ(ah, AR_WOW_LENGTH1_REG);
966b7d5e03cSMatthew Dillon         val = ((val & (~AR_WOW_LENGTH1_MASK(pattern_count))) |
967b7d5e03cSMatthew Dillon                ((ath_pattern_len & AR_WOW_LENGTH_MAX) <<
968b7d5e03cSMatthew Dillon                 AR_WOW_LENGTH1_SHIFT(pattern_count)));
969b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, AR_WOW_LENGTH1_REG, val);
970b7d5e03cSMatthew Dillon     } else {
971b7d5e03cSMatthew Dillon         /* Pattern 4-7 uses AR_WOW_LENGTH2_REG register */
972b7d5e03cSMatthew Dillon         val = OS_REG_READ(ah, AR_WOW_LENGTH2_REG);
973b7d5e03cSMatthew Dillon         val = ((val & (~AR_WOW_LENGTH2_MASK(pattern_count))) |
974b7d5e03cSMatthew Dillon                ((ath_pattern_len & AR_WOW_LENGTH_MAX) <<
975b7d5e03cSMatthew Dillon                 AR_WOW_LENGTH2_SHIFT(pattern_count)));
976b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, AR_WOW_LENGTH2_REG, val);
977b7d5e03cSMatthew Dillon     }
978b7d5e03cSMatthew Dillon 
979b7d5e03cSMatthew Dillon     AH_PRIVATE(ah)->ah_wow_event_mask |=
980b7d5e03cSMatthew Dillon         (1 << (pattern_count + AR_WOW_PATTERN_FOUND_SHIFT));
981b7d5e03cSMatthew Dillon 
982b7d5e03cSMatthew Dillon     return;
983b7d5e03cSMatthew Dillon }
984b7d5e03cSMatthew Dillon 
985b7d5e03cSMatthew Dillon HAL_BOOL
ar9300_set_power_mode_wow_sleep(struct ath_hal * ah)986b7d5e03cSMatthew Dillon ar9300_set_power_mode_wow_sleep(struct ath_hal *ah)
987b7d5e03cSMatthew Dillon {
988b7d5e03cSMatthew Dillon     OS_REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
989b7d5e03cSMatthew Dillon 
990b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_CR, AR_CR_RXD);    /* Set receive disable bit */
991*a20e5e51SMatthew Dillon     if (!ath_hal_waitfor(ah, AR_CR, AR_CR_RXE, 0, AH_WAIT_TIMEOUT)) {
992b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_POWER_MGMT, "%s: dma failed to stop in 10ms\n"
993b7d5e03cSMatthew Dillon                  "AR_CR=0x%08x\nAR_DIAG_SW=0x%08x\n", __func__,
994b7d5e03cSMatthew Dillon                  OS_REG_READ(ah, AR_CR), OS_REG_READ(ah, AR_DIAG_SW));
995b7d5e03cSMatthew Dillon         return AH_FALSE;
996b7d5e03cSMatthew Dillon     } else {
997b7d5e03cSMatthew Dillon #if 0
998b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, AR_RXDP, 0x0);
999b7d5e03cSMatthew Dillon #endif
1000b7d5e03cSMatthew Dillon 
1001b7d5e03cSMatthew Dillon         HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE,
1002b7d5e03cSMatthew Dillon             "%s: TODO How to disable RXDP!!\n", __func__);
1003b7d5e03cSMatthew Dillon 
1004b7d5e03cSMatthew Dillon #if ATH_SUPPORT_MCI
1005b7d5e03cSMatthew Dillon         if (AH_PRIVATE(ah)->ah_caps.halMciSupport) {
1006b7d5e03cSMatthew Dillon             OS_REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
1007b7d5e03cSMatthew Dillon         }
1008b7d5e03cSMatthew Dillon #endif
1009b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON_INT);
1010b7d5e03cSMatthew Dillon 
1011b7d5e03cSMatthew Dillon         return AH_TRUE;
1012b7d5e03cSMatthew Dillon     }
1013b7d5e03cSMatthew Dillon }
1014b7d5e03cSMatthew Dillon 
1015b7d5e03cSMatthew Dillon 
1016b7d5e03cSMatthew Dillon HAL_BOOL
ar9300_wow_enable(struct ath_hal * ah,u_int32_t pattern_enable,u_int32_t timeout_in_seconds,int clearbssid,HAL_BOOL offloadEnable)1017b7d5e03cSMatthew Dillon ar9300_wow_enable(
1018b7d5e03cSMatthew Dillon     struct ath_hal *ah,
1019b7d5e03cSMatthew Dillon     u_int32_t pattern_enable,
1020b7d5e03cSMatthew Dillon     u_int32_t timeout_in_seconds,
1021b7d5e03cSMatthew Dillon     int clearbssid,
1022b7d5e03cSMatthew Dillon     HAL_BOOL offloadEnable)
1023b7d5e03cSMatthew Dillon {
1024b7d5e03cSMatthew Dillon     uint32_t init_val, val, rval = 0;
1025b7d5e03cSMatthew Dillon     const int ka_delay = 4; /* Delay of 4 millisec between two keep_alive's */
1026b7d5e03cSMatthew Dillon     uint32_t wow_event_mask;
1027b7d5e03cSMatthew Dillon #if ATH_WOW_OFFLOAD
1028b7d5e03cSMatthew Dillon     uint32_t wow_feature_enable =
1029b7d5e03cSMatthew Dillon             //AR_WOW_OFFLOAD_ENA_GTK            |
1030b7d5e03cSMatthew Dillon             //AR_WOW_OFFLOAD_ENA_ARP_OFFLOAD    |
1031b7d5e03cSMatthew Dillon             //AR_WOW_OFFLOAD_ENA_NS_OFFLOAD     |
1032b7d5e03cSMatthew Dillon             //AR_WOW_OFFLOAD_ENA_ACER_MAGIC     |
1033b7d5e03cSMatthew Dillon             //AR_WOW_OFFLOAD_ENA_STD_MAGIC      |
1034b7d5e03cSMatthew Dillon             //AR_WOW_OFFLOAD_ENA_4WAY_WAKE      |
1035b7d5e03cSMatthew Dillon             //AR_WOW_OFFLOAD_ENA_SWKA           |
1036b7d5e03cSMatthew Dillon             //AR_WOW_OFFLOAD_ENA_BT_SLEEP       |
1037b7d5e03cSMatthew Dillon             AR_WOW_OFFLOAD_ENA_SW_NULL;
1038b7d5e03cSMatthew Dillon #endif
1039b7d5e03cSMatthew Dillon 
1040b7d5e03cSMatthew Dillon     /*
1041b7d5e03cSMatthew Dillon      * ah_wow_event_mask is a mask to the AR_WOW_PATTERN_REG register to
1042b7d5e03cSMatthew Dillon      * indicate which WOW events that we have enabled. The WOW Events are
1043b7d5e03cSMatthew Dillon      * from the pattern_enable in this function and pattern_count of
1044b7d5e03cSMatthew Dillon      * ar9300_wow_apply_pattern()
1045b7d5e03cSMatthew Dillon      */
1046b7d5e03cSMatthew Dillon     wow_event_mask = AH_PRIVATE(ah)->ah_wow_event_mask;
1047b7d5e03cSMatthew Dillon 
1048b7d5e03cSMatthew Dillon     HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE,
1049b7d5e03cSMatthew Dillon         "%s: offload: %d, pattern: %08x, event_mask: %08x\n",
1050b7d5e03cSMatthew Dillon         __func__, offloadEnable, pattern_enable, wow_event_mask);
1051b7d5e03cSMatthew Dillon 
1052b7d5e03cSMatthew Dillon     /*
1053b7d5e03cSMatthew Dillon      * Untie Power-On-Reset from the PCI-E Reset. When we are in WOW sleep,
1054b7d5e03cSMatthew Dillon      * we do not want the Reset from the PCI-E to disturb our hw state.
1055b7d5e03cSMatthew Dillon      */
1056b7d5e03cSMatthew Dillon     if (AH_PRIVATE(ah)->ah_is_pci_express == AH_TRUE) {
1057b7d5e03cSMatthew Dillon 
1058b7d5e03cSMatthew Dillon         u_int32_t wa_reg_val;
1059b7d5e03cSMatthew Dillon         /*
1060b7d5e03cSMatthew Dillon          * We need to untie the internal POR (power-on-reset) to the external
1061b7d5e03cSMatthew Dillon          * PCI-E reset. We also need to tie the PCI-E Phy reset to the PCI-E
1062b7d5e03cSMatthew Dillon          * reset.
1063b7d5e03cSMatthew Dillon          */
1064b7d5e03cSMatthew Dillon         HAL_DEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE,
1065b7d5e03cSMatthew Dillon             "%s: Untie POR and PCIE reset\n", __func__);
1066b7d5e03cSMatthew Dillon         wa_reg_val = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_WA));
1067b7d5e03cSMatthew Dillon         wa_reg_val = wa_reg_val & ~(AR_WA_UNTIE_RESET_EN);
1068b7d5e03cSMatthew Dillon         wa_reg_val = wa_reg_val | AR_WA_RESET_EN | AR_WA_POR_SHORT;
1069b7d5e03cSMatthew Dillon         /*
1070b7d5e03cSMatthew Dillon          * This bit is to bypass the EEPROM/OTP state machine, (by clearing its
1071b7d5e03cSMatthew Dillon          * busy state while PCIE_rst is asserted), to allow BT embedded CPU
1072b7d5e03cSMatthew Dillon          * be able to access WLAN registers. Otherwise the eCPU access will be
1073b7d5e03cSMatthew Dillon          * stalled as eeprom_sm is held in busy state.
1074b7d5e03cSMatthew Dillon          *
1075b7d5e03cSMatthew Dillon          * EV91928 is that when this bit is set, after host wakeup and PCIE_rst
1076b7d5e03cSMatthew Dillon          * deasserted, PCIE configuration registers will be reset and DeviceID
1077b7d5e03cSMatthew Dillon          * SubsystemID etc. registers will be different from values before
1078b7d5e03cSMatthew Dillon          * entering sleep. This will cause Windows to detect a device removal.
1079b7d5e03cSMatthew Dillon          *
1080b7d5e03cSMatthew Dillon          * For HW WOW, this bit should keep as cleared.
1081b7d5e03cSMatthew Dillon          */
1082b7d5e03cSMatthew Dillon         if (offloadEnable) {
1083b7d5e03cSMatthew Dillon             HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE,
1084b7d5e03cSMatthew Dillon                 "%s: Set AR_WA.13 COLD_RESET_OVERRIDE\n", __func__);
1085b7d5e03cSMatthew Dillon             wa_reg_val = wa_reg_val | AR_WA_COLD_RESET_OVERRIDE;
1086b7d5e03cSMatthew Dillon 
1087b7d5e03cSMatthew Dillon #if ATH_WOW_OFFLOAD
1088b7d5e03cSMatthew Dillon             if (AR_SREV_JUPITER(ah)) {
1089b7d5e03cSMatthew Dillon                 wa_reg_val = wa_reg_val | AR_WA_D3_TO_L1_DISABLE;
1090b7d5e03cSMatthew Dillon             }
1091b7d5e03cSMatthew Dillon #endif
1092b7d5e03cSMatthew Dillon         }
1093b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA), wa_reg_val);
1094b7d5e03cSMatthew Dillon     }
1095b7d5e03cSMatthew Dillon 
1096b7d5e03cSMatthew Dillon     /*
1097b7d5e03cSMatthew Dillon      * Set the power states appropriately and enable pme.
1098b7d5e03cSMatthew Dillon      */
1099b7d5e03cSMatthew Dillon     val = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL));
1100b7d5e03cSMatthew Dillon     val |=
1101b7d5e03cSMatthew Dillon         AR_PMCTRL_HOST_PME_EN     |
1102b7d5e03cSMatthew Dillon         AR_PMCTRL_PWR_PM_CTRL_ENA |
1103b7d5e03cSMatthew Dillon         AR_PMCTRL_AUX_PWR_DET;
1104b7d5e03cSMatthew Dillon 
1105b7d5e03cSMatthew Dillon     /*
1106b7d5e03cSMatthew Dillon      * Set and clear WOW_PME_CLEAR registers for the chip to generate next
1107b7d5e03cSMatthew Dillon      * wow signal.
1108b7d5e03cSMatthew Dillon      */
1109b7d5e03cSMatthew Dillon     val |= AR_PMCTRL_WOW_PME_CLR;
1110b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL), val);
1111b7d5e03cSMatthew Dillon     val &= ~AR_PMCTRL_WOW_PME_CLR;
1112b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL), val);
1113b7d5e03cSMatthew Dillon 
1114b7d5e03cSMatthew Dillon     /*
1115b7d5e03cSMatthew Dillon      * Setup for for:
1116b7d5e03cSMatthew Dillon      *     - beacon misses
1117b7d5e03cSMatthew Dillon      *     - magic pattern
1118b7d5e03cSMatthew Dillon      *     - keep alive timeout
1119b7d5e03cSMatthew Dillon      *     - pattern matching
1120b7d5e03cSMatthew Dillon      */
1121b7d5e03cSMatthew Dillon 
1122b7d5e03cSMatthew Dillon     /*
1123b7d5e03cSMatthew Dillon      * Program some default values for keep-alives, beacon misses, etc.
1124b7d5e03cSMatthew Dillon      */
1125b7d5e03cSMatthew Dillon     init_val = OS_REG_READ(ah, AR_WOW_PATTERN_REG);
1126b7d5e03cSMatthew Dillon     val = AR_WOW_BACK_OFF_SHIFT(AR_WOW_PAT_BACKOFF) | init_val;
1127b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_WOW_PATTERN_REG, val);
1128b7d5e03cSMatthew Dillon     rval = OS_REG_READ(ah, AR_WOW_PATTERN_REG);
1129b7d5e03cSMatthew Dillon 
1130b7d5e03cSMatthew Dillon     val =
1131b7d5e03cSMatthew Dillon         AR_WOW_AIFS_CNT(AR_WOW_CNT_AIFS_CNT) |
1132b7d5e03cSMatthew Dillon         AR_WOW_SLOT_CNT(AR_WOW_CNT_SLOT_CNT) |
1133b7d5e03cSMatthew Dillon         AR_WOW_KEEP_ALIVE_CNT(AR_WOW_CNT_KA_CNT);
1134b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_WOW_COUNT_REG, val);
1135b7d5e03cSMatthew Dillon     rval = OS_REG_READ(ah, AR_WOW_COUNT_REG);
1136b7d5e03cSMatthew Dillon 
1137b7d5e03cSMatthew Dillon     if (pattern_enable & AH_WOW_BEACON_MISS) {
1138b7d5e03cSMatthew Dillon         val = AR_WOW_BEACON_TIMO;
1139b7d5e03cSMatthew Dillon     } else {
1140b7d5e03cSMatthew Dillon         /* We are not using the beacon miss. Program a large value. */
1141b7d5e03cSMatthew Dillon         val = AR_WOW_BEACON_TIMO_MAX;
1142b7d5e03cSMatthew Dillon     }
1143b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_WOW_BCN_TIMO_REG, val);
1144b7d5e03cSMatthew Dillon     rval = OS_REG_READ(ah, AR_WOW_BCN_TIMO_REG);
1145b7d5e03cSMatthew Dillon 
1146b7d5e03cSMatthew Dillon     /*
1147b7d5e03cSMatthew Dillon      * Keep Alive Timo in ms.
1148b7d5e03cSMatthew Dillon      */
1149b7d5e03cSMatthew Dillon     if (pattern_enable == 0) {
1150b7d5e03cSMatthew Dillon         val =  AR_WOW_KEEP_ALIVE_NEVER;
1151b7d5e03cSMatthew Dillon     } else {
1152b7d5e03cSMatthew Dillon         val =  AH_PRIVATE(ah)->ah_config.ath_hal_keep_alive_timeout * 32;
1153b7d5e03cSMatthew Dillon     }
1154b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_WOW_KEEP_ALIVE_TIMO_REG, val);
1155b7d5e03cSMatthew Dillon     rval = OS_REG_READ(ah, AR_WOW_KEEP_ALIVE_TIMO_REG);
1156b7d5e03cSMatthew Dillon 
1157b7d5e03cSMatthew Dillon     /*
1158b7d5e03cSMatthew Dillon      * Keep Alive delay in us.
1159b7d5e03cSMatthew Dillon      */
1160b7d5e03cSMatthew Dillon     val = ka_delay * 1000;
1161b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_WOW_KEEP_ALIVE_DELAY_REG, val);
1162b7d5e03cSMatthew Dillon     rval = OS_REG_READ(ah, AR_WOW_KEEP_ALIVE_DELAY_REG);
1163b7d5e03cSMatthew Dillon 
1164b7d5e03cSMatthew Dillon     /*
1165b7d5e03cSMatthew Dillon      * Create keep_alive Pattern to respond to beacons.
1166b7d5e03cSMatthew Dillon      */
1167b7d5e03cSMatthew Dillon     ar9300_wow_create_keep_alive_pattern(ah);
1168b7d5e03cSMatthew Dillon 
1169b7d5e03cSMatthew Dillon     /*
1170b7d5e03cSMatthew Dillon      * Configure Mac Wow Registers.
1171b7d5e03cSMatthew Dillon      */
1172b7d5e03cSMatthew Dillon 
1173b7d5e03cSMatthew Dillon     val = OS_REG_READ(ah, AR_WOW_KEEP_ALIVE_REG);
1174b7d5e03cSMatthew Dillon 
1175b7d5e03cSMatthew Dillon     /*
1176b7d5e03cSMatthew Dillon      * Send keep alive timeouts anyway.
1177b7d5e03cSMatthew Dillon      */
1178b7d5e03cSMatthew Dillon     val &= ~AR_WOW_KEEP_ALIVE_AUTO_DIS;
1179b7d5e03cSMatthew Dillon 
1180b7d5e03cSMatthew Dillon     if (pattern_enable & AH_WOW_LINK_CHANGE) {
1181b7d5e03cSMatthew Dillon         val &= ~ AR_WOW_KEEP_ALIVE_FAIL_DIS;
1182b7d5e03cSMatthew Dillon         wow_event_mask |= AR_WOW_KEEP_ALIVE_FAIL;
1183b7d5e03cSMatthew Dillon     } else {
1184b7d5e03cSMatthew Dillon         val |=  AR_WOW_KEEP_ALIVE_FAIL_DIS;
1185b7d5e03cSMatthew Dillon     }
1186b7d5e03cSMatthew Dillon #if ATH_WOW_OFFLOAD
1187b7d5e03cSMatthew Dillon     if (offloadEnable) {
1188b7d5e03cSMatthew Dillon         /* Don't enable KA frames yet. BT CPU is not
1189b7d5e03cSMatthew Dillon          * yet ready. */
1190b7d5e03cSMatthew Dillon     }
1191b7d5e03cSMatthew Dillon     else
1192b7d5e03cSMatthew Dillon #endif /* ATH_WOW_OFFLOAD */
1193b7d5e03cSMatthew Dillon     {
1194b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, AR_WOW_KEEP_ALIVE_REG, val);
1195b7d5e03cSMatthew Dillon         val = OS_REG_READ(ah, AR_WOW_KEEP_ALIVE_REG);
1196b7d5e03cSMatthew Dillon     }
1197b7d5e03cSMatthew Dillon 
1198b7d5e03cSMatthew Dillon 
1199b7d5e03cSMatthew Dillon     /*
1200b7d5e03cSMatthew Dillon      * We are relying on a bmiss failure. Ensure we have enough
1201b7d5e03cSMatthew Dillon      * threshold to prevent AH_FALSE positives.
1202b7d5e03cSMatthew Dillon      */
1203b7d5e03cSMatthew Dillon     OS_REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_THR_BM_THR,
1204b7d5e03cSMatthew Dillon         AR_WOW_BMISSTHRESHOLD);
1205b7d5e03cSMatthew Dillon 
1206b7d5e03cSMatthew Dillon     val = OS_REG_READ(ah, AR_WOW_BCN_EN_REG);
1207b7d5e03cSMatthew Dillon     if (pattern_enable & AH_WOW_BEACON_MISS) {
1208b7d5e03cSMatthew Dillon         val |= AR_WOW_BEACON_FAIL_EN;
1209b7d5e03cSMatthew Dillon         wow_event_mask |= AR_WOW_BEACON_FAIL;
1210b7d5e03cSMatthew Dillon     } else {
1211b7d5e03cSMatthew Dillon         val &= ~AR_WOW_BEACON_FAIL_EN;
1212b7d5e03cSMatthew Dillon     }
1213b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_WOW_BCN_EN_REG, val);
1214b7d5e03cSMatthew Dillon     val = OS_REG_READ(ah, AR_WOW_BCN_EN_REG);
1215b7d5e03cSMatthew Dillon 
1216b7d5e03cSMatthew Dillon     /*
1217b7d5e03cSMatthew Dillon      * Enable the magic packet registers.
1218b7d5e03cSMatthew Dillon      */
1219b7d5e03cSMatthew Dillon     val = OS_REG_READ(ah, AR_WOW_PATTERN_REG);
1220b7d5e03cSMatthew Dillon     if ((pattern_enable & AH_WOW_MAGIC_PATTERN_EN)
1221b7d5e03cSMatthew Dillon #if ATH_WOW_OFFLOAD
1222b7d5e03cSMatthew Dillon         || (pattern_enable & AH_WOW_ACER_MAGIC_EN)
1223b7d5e03cSMatthew Dillon #endif
1224b7d5e03cSMatthew Dillon         )
1225b7d5e03cSMatthew Dillon     {
1226b7d5e03cSMatthew Dillon         val |= AR_WOW_MAGIC_EN;
1227b7d5e03cSMatthew Dillon         wow_event_mask |= AR_WOW_MAGIC_PAT_FOUND;
1228b7d5e03cSMatthew Dillon     } else {
1229b7d5e03cSMatthew Dillon         val &= ~AR_WOW_MAGIC_EN;
1230b7d5e03cSMatthew Dillon     }
1231b7d5e03cSMatthew Dillon     val |= AR_WOW_MAC_INTR_EN;
1232b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_WOW_PATTERN_REG, val);
1233b7d5e03cSMatthew Dillon     val = OS_REG_READ(ah, AR_WOW_PATTERN_REG);
1234b7d5e03cSMatthew Dillon 
1235b7d5e03cSMatthew Dillon #if ATH_WOW_OFFLOAD
1236b7d5e03cSMatthew Dillon     if (HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_FORCE_BT_SLEEP)) {
1237b7d5e03cSMatthew Dillon         wow_feature_enable |= AR_WOW_OFFLOAD_ENA_BT_SLEEP;
1238b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - BT SLEEP\n");
1239b7d5e03cSMatthew Dillon     } else {
1240b7d5e03cSMatthew Dillon         wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_BT_SLEEP;
1241b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - BT SLEEP\n");
1242b7d5e03cSMatthew Dillon     }
1243b7d5e03cSMatthew Dillon 
1244b7d5e03cSMatthew Dillon     if (HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_SW_NULL_DISABLE)) {
1245b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - SW NULL\n");
1246b7d5e03cSMatthew Dillon         wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_SW_NULL;
1247b7d5e03cSMatthew Dillon     } else {
1248b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - SW NULL\n");
1249b7d5e03cSMatthew Dillon         wow_feature_enable |= AR_WOW_OFFLOAD_ENA_SW_NULL;
1250b7d5e03cSMatthew Dillon     }
1251b7d5e03cSMatthew Dillon 
1252b7d5e03cSMatthew Dillon     if (HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_DEVID_SWAR_DISABLE)) {
1253b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - DevID SWAR\n");
1254b7d5e03cSMatthew Dillon         wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_DEVID_SWAR;
1255b7d5e03cSMatthew Dillon     } else {
1256b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - DevID SWAR\n");
1257b7d5e03cSMatthew Dillon         wow_feature_enable |= AR_WOW_OFFLOAD_ENA_DEVID_SWAR;
1258b7d5e03cSMatthew Dillon     }
1259b7d5e03cSMatthew Dillon 
1260b7d5e03cSMatthew Dillon     if (pattern_enable & AH_WOW_ACER_KEEP_ALIVE_EN) {
1261b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - Acer SWKA\n");
1262b7d5e03cSMatthew Dillon         wow_feature_enable |= AR_WOW_OFFLOAD_ENA_SWKA;
1263b7d5e03cSMatthew Dillon     } else {
1264b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - Acer SWKA\n");
1265b7d5e03cSMatthew Dillon         wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_SWKA;
1266b7d5e03cSMatthew Dillon     }
1267b7d5e03cSMatthew Dillon 
1268b7d5e03cSMatthew Dillon     if (pattern_enable & AH_WOW_ACER_MAGIC_EN) {
1269b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - Standard Magic\n");
1270b7d5e03cSMatthew Dillon         wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_STD_MAGIC;
1271b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - Acer Magic\n");
1272b7d5e03cSMatthew Dillon         wow_feature_enable |= AR_WOW_OFFLOAD_ENA_ACER_MAGIC;
1273b7d5e03cSMatthew Dillon     } else {
1274b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - Standard Magic\n");
1275b7d5e03cSMatthew Dillon         wow_feature_enable |= AR_WOW_OFFLOAD_ENA_STD_MAGIC;
1276b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - Acer Magic\n");
1277b7d5e03cSMatthew Dillon         wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_ACER_MAGIC;
1278b7d5e03cSMatthew Dillon     }
1279b7d5e03cSMatthew Dillon 
1280b7d5e03cSMatthew Dillon     if ((pattern_enable & AH_WOW_4WAY_HANDSHAKE_EN) ||
1281b7d5e03cSMatthew Dillon         HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_FORCE_4WAY_HS_WAKE)) {
1282b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - 4Way Handshake\n");
1283b7d5e03cSMatthew Dillon         wow_feature_enable |= AR_WOW_OFFLOAD_ENA_4WAY_WAKE;
1284b7d5e03cSMatthew Dillon     } else {
1285b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - 4Way Handshake\n");
1286b7d5e03cSMatthew Dillon         wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_4WAY_WAKE;
1287b7d5e03cSMatthew Dillon     }
1288b7d5e03cSMatthew Dillon 
1289b7d5e03cSMatthew Dillon     if((pattern_enable & AH_WOW_AP_ASSOCIATION_LOST_EN) ||
1290b7d5e03cSMatthew Dillon         HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_FORCE_AP_LOSS_WAKE))
1291b7d5e03cSMatthew Dillon     {
1292b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - AP loss wake\n");
1293b7d5e03cSMatthew Dillon         wow_feature_enable |= AR_WOW_OFFLOAD_ENA_AP_LOSS_WAKE;
1294b7d5e03cSMatthew Dillon     } else {
1295b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - AP loss wake\n");
1296b7d5e03cSMatthew Dillon         wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_AP_LOSS_WAKE;
1297b7d5e03cSMatthew Dillon     }
1298b7d5e03cSMatthew Dillon 
1299b7d5e03cSMatthew Dillon     if((pattern_enable & AH_WOW_GTK_HANDSHAKE_ERROR_EN) ||
1300b7d5e03cSMatthew Dillon         HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_FORCE_GTK_ERR_WAKE))
1301b7d5e03cSMatthew Dillon     {
1302b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - GTK error wake\n");
1303b7d5e03cSMatthew Dillon         wow_feature_enable |= AR_WOW_OFFLOAD_ENA_GTK_ERROR_WAKE;
1304b7d5e03cSMatthew Dillon     } else {
1305b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - GTK error wake\n");
1306b7d5e03cSMatthew Dillon         wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_GTK_ERROR_WAKE;
1307b7d5e03cSMatthew Dillon     }
1308b7d5e03cSMatthew Dillon 
1309b7d5e03cSMatthew Dillon     if (pattern_enable & AH_WOW_GTK_OFFLOAD_EN) {
1310b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - GTK offload\n");
1311b7d5e03cSMatthew Dillon         wow_feature_enable |= AR_WOW_OFFLOAD_ENA_GTK;
1312b7d5e03cSMatthew Dillon     } else {
1313b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - GTK offload\n");
1314b7d5e03cSMatthew Dillon         wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_GTK;
1315b7d5e03cSMatthew Dillon     }
1316b7d5e03cSMatthew Dillon 
1317b7d5e03cSMatthew Dillon     if (pattern_enable & AH_WOW_ARP_OFFLOAD_EN) {
1318b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - ARP offload\n");
1319b7d5e03cSMatthew Dillon         wow_feature_enable |= AR_WOW_OFFLOAD_ENA_ARP_OFFLOAD;
1320b7d5e03cSMatthew Dillon     } else {
1321b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - ARP offload\n");
1322b7d5e03cSMatthew Dillon         wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_ARP_OFFLOAD;
1323b7d5e03cSMatthew Dillon     }
1324b7d5e03cSMatthew Dillon 
1325b7d5e03cSMatthew Dillon     if (pattern_enable & AH_WOW_NS_OFFLOAD_EN) {
1326b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - NS offload\n");
1327b7d5e03cSMatthew Dillon         wow_feature_enable |= AR_WOW_OFFLOAD_ENA_NS_OFFLOAD;
1328b7d5e03cSMatthew Dillon     } else {
1329b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - NS offload\n");
1330b7d5e03cSMatthew Dillon         wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_NS_OFFLOAD;
1331b7d5e03cSMatthew Dillon     }
1332b7d5e03cSMatthew Dillon 
1333b7d5e03cSMatthew Dillon #endif /* ATH_WOW_OFFLOAD */
1334b7d5e03cSMatthew Dillon 
1335b7d5e03cSMatthew Dillon     /* For Kite and later version of the chips
1336b7d5e03cSMatthew Dillon      * enable wow pattern match for packets less than
1337b7d5e03cSMatthew Dillon      * 256 bytes for all patterns.
1338b7d5e03cSMatthew Dillon      */
1339b7d5e03cSMatthew Dillon     /* XXX */
1340b7d5e03cSMatthew Dillon     OS_REG_WRITE(
1341b7d5e03cSMatthew Dillon         ah, AR_WOW_PATTERN_MATCH_LT_256B_REG, AR_WOW_PATTERN_SUPPORTED);
1342b7d5e03cSMatthew Dillon 
1343b7d5e03cSMatthew Dillon     /*
1344b7d5e03cSMatthew Dillon      * Set the power states appropriately and enable PME.
1345b7d5e03cSMatthew Dillon      */
1346b7d5e03cSMatthew Dillon     val = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL));
1347b7d5e03cSMatthew Dillon     val |=
1348b7d5e03cSMatthew Dillon         AR_PMCTRL_PWR_STATE_D1D3 |
1349b7d5e03cSMatthew Dillon         AR_PMCTRL_HOST_PME_EN    |
1350b7d5e03cSMatthew Dillon         AR_PMCTRL_PWR_PM_CTRL_ENA;
1351b7d5e03cSMatthew Dillon     val &= ~AR_PCIE_PM_CTRL_ENA;
1352b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL), val);
1353b7d5e03cSMatthew Dillon 
1354b7d5e03cSMatthew Dillon     /* Wake on Timer Interrupt. Test mode only. Used in Manufacturing line. */
1355b7d5e03cSMatthew Dillon     if (timeout_in_seconds) {
1356b7d5e03cSMatthew Dillon         /* convert Timeout to u_secs */
1357b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, AR_NEXT_NDP_TIMER,
1358b7d5e03cSMatthew Dillon             OS_REG_READ(ah, AR_TSF_L32) + timeout_in_seconds * 1000000 );
1359b7d5e03cSMatthew Dillon         /* timer_period = 30 seconds always */
1360b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, AR_NDP_PERIOD, 30 * 1000000);
1361b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, AR_TIMER_MODE, OS_REG_READ(ah, AR_TIMER_MODE) | 0x80);
1362b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, AR_IMR_S5, OS_REG_READ(ah, AR_IMR_S5) | 0x80);
1363b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, AR_IMR, OS_REG_READ(ah, AR_IMR) | AR_IMR_GENTMR);
1364b7d5e03cSMatthew Dillon         if (clearbssid) {
1365b7d5e03cSMatthew Dillon             OS_REG_WRITE(ah, AR_BSS_ID0, 0);
1366b7d5e03cSMatthew Dillon             OS_REG_WRITE(ah, AR_BSS_ID1, 0);
1367b7d5e03cSMatthew Dillon         }
1368b7d5e03cSMatthew Dillon     }
1369b7d5e03cSMatthew Dillon 
1370b7d5e03cSMatthew Dillon     /* Enable Seq# generation when asleep. */
1371b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_STA_ID1,
1372b7d5e03cSMatthew Dillon                      OS_REG_READ(ah, AR_STA_ID1) & ~AR_STA_ID1_PRESERVE_SEQNUM);
1373b7d5e03cSMatthew Dillon 
1374b7d5e03cSMatthew Dillon     AH_PRIVATE(ah)->ah_wow_event_mask = wow_event_mask;
1375b7d5e03cSMatthew Dillon 
1376b7d5e03cSMatthew Dillon #if ATH_WOW_OFFLOAD
1377b7d5e03cSMatthew Dillon     if (offloadEnable) {
1378b7d5e03cSMatthew Dillon         /* Force MAC awake before entering SW WoW mode */
1379b7d5e03cSMatthew Dillon         OS_REG_SET_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN);
1380b7d5e03cSMatthew Dillon #if ATH_SUPPORT_MCI
1381b7d5e03cSMatthew Dillon         if (AH_PRIVATE(ah)->ah_caps.halMciSupport) {
1382b7d5e03cSMatthew Dillon             OS_REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
1383b7d5e03cSMatthew Dillon         }
1384b7d5e03cSMatthew Dillon #endif
1385b7d5e03cSMatthew Dillon 
1386b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, AR_WOW_OFFLOAD_COMMAND_JUPITER, wow_feature_enable);
1387b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, AR_WOW_OFFLOAD_STATUS_JUPITER, 0x0);
1388b7d5e03cSMatthew Dillon         if (wow_feature_enable & AR_WOW_OFFLOAD_ENA_SW_NULL) {
1389b7d5e03cSMatthew Dillon             OS_REG_WRITE(ah, AR_WOW_SW_NULL_PARAMETER,
1390b7d5e03cSMatthew Dillon                 ((1000) |
1391b7d5e03cSMatthew Dillon                 (4 << AR_WOW_SW_NULL_SHORT_PERIOD_MASK_S)));
1392b7d5e03cSMatthew Dillon         }
1393b7d5e03cSMatthew Dillon 
1394b7d5e03cSMatthew Dillon         if (wow_feature_enable & AR_WOW_OFFLOAD_ENA_DEVID_SWAR) {
1395b7d5e03cSMatthew Dillon             ar9300_wowoffload_download_devid_swar(ah);
1396b7d5e03cSMatthew Dillon         }
1397b7d5e03cSMatthew Dillon 
1398b7d5e03cSMatthew Dillon         ar9300_wow_offload_download_hal_params(ah);
1399b7d5e03cSMatthew Dillon         ar9300_wow_offload_handshake(ah, pattern_enable);
1400b7d5e03cSMatthew Dillon         AH9300(ah)->ah_chip_full_sleep = AH_FALSE;
1401b7d5e03cSMatthew Dillon 
1402b7d5e03cSMatthew Dillon         //OS_REG_SET_BIT(ah, AR_SW_WOW_CONTROL, AR_HW_WOW_DISABLE);
1403b7d5e03cSMatthew Dillon     }
1404b7d5e03cSMatthew Dillon     else
1405b7d5e03cSMatthew Dillon #endif /* ATH_WOW_OFFLOAD */
1406b7d5e03cSMatthew Dillon     {
1407b7d5e03cSMatthew Dillon #if ATH_SUPPORT_MCI
1408b7d5e03cSMatthew Dillon         if (AH_PRIVATE(ah)->ah_caps.halMciSupport) {
1409b7d5e03cSMatthew Dillon             OS_REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
1410b7d5e03cSMatthew Dillon         }
1411b7d5e03cSMatthew Dillon #endif
1412b7d5e03cSMatthew Dillon         ar9300_set_power_mode_wow_sleep(ah);
1413b7d5e03cSMatthew Dillon         AH9300(ah)->ah_chip_full_sleep = AH_TRUE;
1414b7d5e03cSMatthew Dillon     }
1415b7d5e03cSMatthew Dillon 
1416b7d5e03cSMatthew Dillon     return (AH_TRUE);
1417b7d5e03cSMatthew Dillon }
1418b7d5e03cSMatthew Dillon 
1419b7d5e03cSMatthew Dillon u_int32_t
1420b7d5e03cSMatthew Dillon //ar9300_wow_wake_up(struct ath_hal *ah, u_int8_t  *chipPatternBytes)
ar9300_wow_wake_up(struct ath_hal * ah,HAL_BOOL offloadEnabled)1421b7d5e03cSMatthew Dillon ar9300_wow_wake_up(struct ath_hal *ah, HAL_BOOL offloadEnabled)
1422b7d5e03cSMatthew Dillon {
1423b7d5e03cSMatthew Dillon     uint32_t wow_status = 0;
1424b7d5e03cSMatthew Dillon     uint32_t val = 0, rval;
1425b7d5e03cSMatthew Dillon 
1426b7d5e03cSMatthew Dillon     OS_REG_CLR_BIT(ah, AR_SW_WOW_CONTROL, AR_HW_WOW_DISABLE);
1427b7d5e03cSMatthew Dillon     OS_REG_CLR_BIT(ah, AR_SW_WOW_CONTROL, AR_SW_WOW_ENABLE);
1428b7d5e03cSMatthew Dillon 
1429b7d5e03cSMatthew Dillon #if ATH_WOW_OFFLOAD
1430b7d5e03cSMatthew Dillon     /* If WoW was offloaded to embedded CPU, use the global
1431b7d5e03cSMatthew Dillon      * shared register to know the wakeup reason */
1432b7d5e03cSMatthew Dillon     if (offloadEnabled) {
1433b7d5e03cSMatthew Dillon         val = OS_REG_READ(ah, AR_EMB_CPU_WOW_STATUS);
1434b7d5e03cSMatthew Dillon         if (val) {
1435b7d5e03cSMatthew Dillon             if (val & AR_EMB_CPU_WOW_STATUS_MAGIC_PATTERN) {
1436b7d5e03cSMatthew Dillon                 HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) SW MAGIC_PATTERN\n");
1437b7d5e03cSMatthew Dillon                 wow_status |= AH_WOW_MAGIC_PATTERN_EN;
1438b7d5e03cSMatthew Dillon             }
1439b7d5e03cSMatthew Dillon             if (val & AR_EMB_CPU_WOW_STATUS_PATTERN_MATCH) {
1440b7d5e03cSMatthew Dillon                 HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) SW USER_PATTERN\n");
1441b7d5e03cSMatthew Dillon                 wow_status |= AH_WOW_USER_PATTERN_EN;
1442b7d5e03cSMatthew Dillon             }
1443b7d5e03cSMatthew Dillon             if (val & AR_EMB_CPU_WOW_STATUS_KEEP_ALIVE_FAIL) {
1444b7d5e03cSMatthew Dillon                 HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) SW KEEP_ALIVE_FAIL\n");
1445b7d5e03cSMatthew Dillon                 wow_status |= AH_WOW_LINK_CHANGE;
1446b7d5e03cSMatthew Dillon             }
1447b7d5e03cSMatthew Dillon             if (val & AR_EMB_CPU_WOW_STATUS_BEACON_MISS) {
1448b7d5e03cSMatthew Dillon                 HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) SW BEACON_FAIL\n");
1449b7d5e03cSMatthew Dillon                 wow_status |= AH_WOW_BEACON_MISS;
1450b7d5e03cSMatthew Dillon             }
1451b7d5e03cSMatthew Dillon         }
1452b7d5e03cSMatthew Dillon 
1453b7d5e03cSMatthew Dillon         /* Clear status and mask registers */
1454b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, AR_EMB_CPU_WOW_STATUS, 0x0);
1455b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, AR_EMB_CPU_WOW_ENABLE, 0);
1456b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, AR_MBOX_CTRL_STATUS, 0);
1457b7d5e03cSMatthew Dillon 
1458b7d5e03cSMatthew Dillon     }
1459b7d5e03cSMatthew Dillon     else
1460b7d5e03cSMatthew Dillon #endif /* ATH_WOW_OFFLOAD */
1461b7d5e03cSMatthew Dillon     {
1462b7d5e03cSMatthew Dillon         /*
1463b7d5e03cSMatthew Dillon          * Read the WOW Status register to know the wakeup reason.
1464b7d5e03cSMatthew Dillon          */
1465b7d5e03cSMatthew Dillon         rval = OS_REG_READ(ah, AR_WOW_PATTERN_REG);
1466b7d5e03cSMatthew Dillon         val = AR_WOW_STATUS(rval);
1467b7d5e03cSMatthew Dillon 
1468b7d5e03cSMatthew Dillon         /*
1469b7d5e03cSMatthew Dillon          * Mask only the WOW events that we have enabled.
1470b7d5e03cSMatthew Dillon          * Sometimes we have spurious WOW events from the AR_WOW_PATTERN_REG
1471b7d5e03cSMatthew Dillon          * register. This mask will clean it up.
1472b7d5e03cSMatthew Dillon          */
1473b7d5e03cSMatthew Dillon         val &= AH_PRIVATE(ah)->ah_wow_event_mask;
1474b7d5e03cSMatthew Dillon 
1475b7d5e03cSMatthew Dillon         if (val) {
1476b7d5e03cSMatthew Dillon             if (val & AR_WOW_MAGIC_PAT_FOUND) {
1477b7d5e03cSMatthew Dillon                 HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) HW MAGIC_PATTERN\n");
1478b7d5e03cSMatthew Dillon                 wow_status |= AH_WOW_MAGIC_PATTERN_EN;
1479b7d5e03cSMatthew Dillon             }
1480b7d5e03cSMatthew Dillon             if (AR_WOW_PATTERN_FOUND(val)) {
1481b7d5e03cSMatthew Dillon                 //int  i, offset;
1482b7d5e03cSMatthew Dillon                 //offset = OS_REG_READ(ah, AR_WOW_RXBUF_START_ADDR);
1483b7d5e03cSMatthew Dillon                 //// Read matched pattern for wake packet detection indication.
1484b7d5e03cSMatthew Dillon                 //for( i = 0; i< MAX_PATTERN_SIZE/4; i+=4)
1485b7d5e03cSMatthew Dillon                 //{
1486b7d5e03cSMatthew Dillon                 //    // RX FIFO is only 8K wrapping.
1487b7d5e03cSMatthew Dillon                 //    if(offset >= 8 * 1024 / 4) offset = 0;
1488b7d5e03cSMatthew Dillon                 //    *(u_int32_t*)(chipPatternBytes + i) = OS_REG_READ( ah,offset );
1489b7d5e03cSMatthew Dillon                 //    offset++;
1490b7d5e03cSMatthew Dillon                 //}
1491b7d5e03cSMatthew Dillon                 wow_status |= AH_WOW_USER_PATTERN_EN;
1492b7d5e03cSMatthew Dillon                 HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) HW USER_PATTERN\n");
1493b7d5e03cSMatthew Dillon             }
1494b7d5e03cSMatthew Dillon             if (val & AR_WOW_KEEP_ALIVE_FAIL) {
1495b7d5e03cSMatthew Dillon                 HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) HW KEEP_ALIVE_FAIL\n");
1496b7d5e03cSMatthew Dillon                 wow_status |= AH_WOW_LINK_CHANGE;
1497b7d5e03cSMatthew Dillon             }
1498b7d5e03cSMatthew Dillon             if (val & AR_WOW_BEACON_FAIL) {
1499b7d5e03cSMatthew Dillon                 HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) HW BEACON_FAIL\n");
1500b7d5e03cSMatthew Dillon                 wow_status |= AH_WOW_BEACON_MISS;
1501b7d5e03cSMatthew Dillon             }
1502b7d5e03cSMatthew Dillon         }
1503b7d5e03cSMatthew Dillon     }
1504b7d5e03cSMatthew Dillon 
1505b7d5e03cSMatthew Dillon     /*
1506b7d5e03cSMatthew Dillon      * Set and clear WOW_PME_CLEAR registers for the chip to generate next
1507b7d5e03cSMatthew Dillon      * wow signal.
1508b7d5e03cSMatthew Dillon      * Disable D3 before accessing other registers ?
1509b7d5e03cSMatthew Dillon      */
1510b7d5e03cSMatthew Dillon     val = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL));
1511b7d5e03cSMatthew Dillon     /* Check the bit value 0x01000000 (7-10)? */
1512b7d5e03cSMatthew Dillon     val &= ~AR_PMCTRL_PWR_STATE_D1D3;
1513b7d5e03cSMatthew Dillon     val |= AR_PMCTRL_WOW_PME_CLR;
1514b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL), val);
1515b7d5e03cSMatthew Dillon 
1516b7d5e03cSMatthew Dillon     /*
1517b7d5e03cSMatthew Dillon      * Clear all events.
1518b7d5e03cSMatthew Dillon      */
1519b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_WOW_PATTERN_REG,
1520b7d5e03cSMatthew Dillon         AR_WOW_CLEAR_EVENTS(OS_REG_READ(ah, AR_WOW_PATTERN_REG)));
1521b7d5e03cSMatthew Dillon 
1522b7d5e03cSMatthew Dillon     //HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE,
1523b7d5e03cSMatthew Dillon     //    "%s: Skip PCIE WA programming\n", __func__);
1524b7d5e03cSMatthew Dillon #if 0
1525b7d5e03cSMatthew Dillon     /*
1526b7d5e03cSMatthew Dillon      * Tie reset register.
1527b7d5e03cSMatthew Dillon      * FIXME: Per David Quan not tieing it back might have some repurcussions.
1528b7d5e03cSMatthew Dillon      */
1529b7d5e03cSMatthew Dillon     /* XXX */
1530b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA), OS_REG_READ(ah, AR_WA) |
1531b7d5e03cSMatthew Dillon             AR_WA_UNTIE_RESET_EN | AR_WA_POR_SHORT | AR_WA_RESET_EN);
1532b7d5e03cSMatthew Dillon #endif
1533b7d5e03cSMatthew Dillon 
1534b7d5e03cSMatthew Dillon     /* Restore the Beacon Threshold to init value */
1535b7d5e03cSMatthew Dillon     OS_REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_THR_BM_THR, INIT_RSSI_THR);
1536b7d5e03cSMatthew Dillon 
1537b7d5e03cSMatthew Dillon     /*
1538b7d5e03cSMatthew Dillon      * Restore the way the PCI-E Reset, Power-On-Reset, external PCIE_POR_SHORT
1539b7d5e03cSMatthew Dillon      * pins are tied to its original value. Previously just before WOW sleep,
1540b7d5e03cSMatthew Dillon      * we untie the PCI-E Reset to our Chip's Power On Reset so that
1541b7d5e03cSMatthew Dillon      * any PCI-E reset from the bus will not reset our chip.
1542b7d5e03cSMatthew Dillon      */
1543b7d5e03cSMatthew Dillon     HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE, "%s: restore AR_WA\n", __func__);
1544b7d5e03cSMatthew Dillon     if (AH_PRIVATE(ah)->ah_is_pci_express == AH_TRUE) {
1545b7d5e03cSMatthew Dillon         ar9300_config_pci_power_save(ah, 0, 0);
1546b7d5e03cSMatthew Dillon     }
1547b7d5e03cSMatthew Dillon 
1548b7d5e03cSMatthew Dillon     AH_PRIVATE(ah)->ah_wow_event_mask = 0;
1549b7d5e03cSMatthew Dillon     HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE,
1550b7d5e03cSMatthew Dillon         "(WOW) wow_status=%08x\n", wow_status);
1551b7d5e03cSMatthew Dillon 
1552b7d5e03cSMatthew Dillon     return (wow_status);
1553b7d5e03cSMatthew Dillon }
1554b7d5e03cSMatthew Dillon 
1555b7d5e03cSMatthew Dillon void
ar9300_wow_set_gpio_reset_low(struct ath_hal * ah)1556b7d5e03cSMatthew Dillon ar9300_wow_set_gpio_reset_low(struct ath_hal *ah)
1557b7d5e03cSMatthew Dillon {
1558b7d5e03cSMatthew Dillon     uint32_t val;
1559b7d5e03cSMatthew Dillon 
1560b7d5e03cSMatthew Dillon     val = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_GPIO_OE_OUT));
1561b7d5e03cSMatthew Dillon     val |= (1 << (2 * 2));
1562b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_GPIO_OE_OUT), val);
1563b7d5e03cSMatthew Dillon     val = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_GPIO_OE_OUT));
1564b7d5e03cSMatthew Dillon     /* val = OS_REG_READ(ah,AR_GPIO_IN_OUT ); */
1565b7d5e03cSMatthew Dillon }
1566b7d5e03cSMatthew Dillon #endif /* ATH_WOW */
1567