1250003Sadrian/*
2250003Sadrian * Copyright (c) 2013 Qualcomm Atheros, Inc.
3250003Sadrian *
4250003Sadrian * Permission to use, copy, modify, and/or distribute this software for any
5250003Sadrian * purpose with or without fee is hereby granted, provided that the above
6250003Sadrian * copyright notice and this permission notice appear in all copies.
7250003Sadrian *
8250003Sadrian * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9250003Sadrian * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10250003Sadrian * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11250003Sadrian * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12250003Sadrian * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
13250003Sadrian * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14250003Sadrian * PERFORMANCE OF THIS SOFTWARE.
15250003Sadrian */
16250003Sadrian
17250003Sadrian#include "opt_ah.h"
18250003Sadrian
19250003Sadrian#include "ah.h"
20250003Sadrian#include "ah_internal.h"
21250003Sadrian
22250003Sadrian#include "ar9300/ar9300.h"
23250003Sadrian#include "ar9300/ar9300reg.h"
24250003Sadrian
25250003Sadrian#if ATH_WOW_OFFLOAD
26250003Sadrianvoid ar9300_wowoffload_prep(struct ath_hal *ah)
27250003Sadrian{
28250003Sadrian    struct ath_hal_9300 *ahp = AH9300(ah);
29250003Sadrian
30250003Sadrian    ahp->ah_mcast_filter_l32_set = 0;
31250003Sadrian    ahp->ah_mcast_filter_u32_set = 0;
32250003Sadrian}
33250003Sadrian
34250003Sadrianvoid ar9300_wowoffload_post(struct ath_hal *ah)
35250003Sadrian{
36250003Sadrian    struct ath_hal_9300 *ahp = AH9300(ah);
37250003Sadrian    u_int32_t val;
38250003Sadrian
39250003Sadrian    if (ahp->ah_mcast_filter_l32_set != 0) {
40250003Sadrian        val = OS_REG_READ(ah, AR_MCAST_FIL0);
41250003Sadrian        val &= ~ahp->ah_mcast_filter_l32_set;
42250003Sadrian        OS_REG_WRITE(ah, AR_MCAST_FIL0, val);
43250003Sadrian    }
44250003Sadrian    if (ahp->ah_mcast_filter_u32_set != 0) {
45250003Sadrian        val = OS_REG_READ(ah, AR_MCAST_FIL1);
46250003Sadrian        val &= ~ahp->ah_mcast_filter_u32_set;
47250003Sadrian        OS_REG_WRITE(ah, AR_MCAST_FIL1, val);
48250003Sadrian    }
49250003Sadrian
50250003Sadrian    ahp->ah_mcast_filter_l32_set = 0;
51250003Sadrian    ahp->ah_mcast_filter_u32_set = 0;
52250003Sadrian}
53250003Sadrian
54250003Sadrianstatic void ar9300_wowoffload_add_mcast_filter(struct ath_hal *ah, u_int8_t *mc_addr)
55250003Sadrian{
56250003Sadrian    struct ath_hal_9300 *ahp = AH9300(ah);
57250003Sadrian    u_int32_t reg, val;
58250003Sadrian    u_int8_t  pos, high32;
59250003Sadrian
60250003Sadrian    memcpy((u_int8_t *) &val, &mc_addr[0], 3);
61250003Sadrian    pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
62250003Sadrian    memcpy((u_int8_t *) &val, &mc_addr[3], 3);
63250003Sadrian    pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
64250003Sadrian    high32 = pos & 0x20;
65250003Sadrian    reg = high32 ? AR_MCAST_FIL1 : AR_MCAST_FIL0;
66250003Sadrian    pos &= 0x1F;
67250003Sadrian
68250003Sadrian    val = OS_REG_READ(ah, reg);
69250003Sadrian    if ((val & (1 << pos)) == 0) {
70250003Sadrian        val |= (1 << pos);
71250003Sadrian        if (high32) {
72250003Sadrian            ahp->ah_mcast_filter_u32_set |= (1 << pos);
73250003Sadrian        } else {
74250003Sadrian            ahp->ah_mcast_filter_l32_set |= (1 << pos);
75250003Sadrian        }
76250003Sadrian        OS_REG_WRITE(ah, reg, val);
77250003Sadrian    }
78250003Sadrian}
79250003Sadrian
80250003Sadrian/*
81250003Sadrian * DeviceID SWAR - EV91928
82250003Sadrian *
83250003Sadrian * During SW WOW, 0x4004[13] is set to allow BT eCPU to access WLAN MAC
84250003Sadrian * registers. Setting 00x4004[13] will prevent eeprom state machine to
85250003Sadrian * load customizable PCIE configuration registers, which lead to the PCIE
86250003Sadrian * device id stay as default 0xABCD. The SWAR to have BT eCPU to write
87250003Sadrian * to PCIE registers as soon as it detects PCIE reset is deasserted.
88250003Sadrian */
89250003Sadrianvoid ar9300_wowoffload_download_devid_swar(struct ath_hal *ah)
90250003Sadrian{
91250003Sadrian    u_int32_t addr = AR_WOW_OFFLOAD_WLAN_REGSET_NUM;
92250003Sadrian
93250003Sadrian    OS_REG_WRITE(ah, addr, 8);
94250003Sadrian    addr += 4;
95250003Sadrian    OS_REG_WRITE(ah, addr, 0x5000);
96250003Sadrian    addr += 4;
97250003Sadrian    HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) pcie_000 = %08x\n",
98250003Sadrian             AH_PRIVATE(ah)->ah_config.ath_hal_pcie_000);
99250003Sadrian    OS_REG_WRITE(ah, addr, AH_PRIVATE(ah)->ah_config.ath_hal_pcie_000);
100250003Sadrian    addr += 4;
101250003Sadrian    OS_REG_WRITE(ah, addr, 0x5008);
102250003Sadrian    addr += 4;
103250003Sadrian    HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) pcie_008 = %08x\n",
104250003Sadrian             AH_PRIVATE(ah)->ah_config.ath_hal_pcie_008);
105250003Sadrian    OS_REG_WRITE(ah, addr, AH_PRIVATE(ah)->ah_config.ath_hal_pcie_008);
106250003Sadrian    addr += 4;
107250003Sadrian    OS_REG_WRITE(ah, addr, 0x502c);
108250003Sadrian    addr += 4;
109250003Sadrian    HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) pcie_02c = %08x\n",
110250003Sadrian             AH_PRIVATE(ah)->ah_config.ath_hal_pcie_02c);
111250003Sadrian    OS_REG_WRITE(ah, addr, AH_PRIVATE(ah)->ah_config.ath_hal_pcie_02c);
112250003Sadrian    addr += 4;
113250003Sadrian    OS_REG_WRITE(ah, addr, 0x18c00);
114250003Sadrian    addr += 4;
115250003Sadrian    OS_REG_WRITE(ah, addr, 0x18212ede);
116250003Sadrian    addr += 4;
117250003Sadrian    OS_REG_WRITE(ah, addr, 0x18c04);
118250003Sadrian    addr += 4;
119250003Sadrian    OS_REG_WRITE(ah, addr, 0x008001d8);
120250003Sadrian    addr += 4;
121250003Sadrian    OS_REG_WRITE(ah, addr, 0x18c08);
122250003Sadrian    addr += 4;
123250003Sadrian    OS_REG_WRITE(ah, addr, 0x0003580c);
124250003Sadrian    addr += 4;
125250003Sadrian    OS_REG_WRITE(ah, addr, 0x570c);
126250003Sadrian    addr += 4;
127250003Sadrian    HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) pcie_70c = %08x\n",
128250003Sadrian             AH_PRIVATE(ah)->ah_config.ath_hal_pcie_70c);
129250003Sadrian    OS_REG_WRITE(ah, addr, AH_PRIVATE(ah)->ah_config.ath_hal_pcie_70c);
130250003Sadrian    addr += 4;
131250003Sadrian    OS_REG_WRITE(ah, addr, 0x5040);
132250003Sadrian    addr += 4;
133250003Sadrian    HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) pcie_040 = %08x\n",
134250003Sadrian             AH_PRIVATE(ah)->ah_config.ath_hal_pcie_040);
135250003Sadrian    OS_REG_WRITE(ah, addr, AH_PRIVATE(ah)->ah_config.ath_hal_pcie_040);
136250003Sadrian    addr += 4;
137250003Sadrian/*
138250003Sadrian    A_SOC_REG_WRITE(0x45000, 0x0034168c);
139250003Sadrian    A_SOC_REG_WRITE(0x45008, 0x02800001);
140250003Sadrian    A_SOC_REG_WRITE(0x4502c, 0x3117168c);
141250003Sadrian    A_SOC_REG_WRITE(0x58c00, 0x18212ede);
142250003Sadrian    A_SOC_REG_WRITE(0x58c04, 0x000801d8);
143250003Sadrian    A_SOC_REG_WRITE(0x58c08, 0x0003580c);
144250003Sadrian    A_SOC_REG_WRITE(0x4570c, 0x275f3f01);
145250003Sadrian    A_SOC_REG_WRITE(0x45040, 0xffc25001);
146250003Sadrian*/
147250003Sadrian}
148250003Sadrian
149250003Sadrian/* Retrieve updated information from MAC PCU buffer.
150250003Sadrian * Embedded CPU would have written the value before exiting WoW
151250003Sadrian * */
152250003Sadrianvoid ar9300_wowoffload_retrieve_data(struct ath_hal *ah, void *buf, u_int32_t param)
153250003Sadrian{
154250003Sadrian    u_int32_t rc_lower, rc_upper;
155250003Sadrian
156250003Sadrian    if (param == WOW_PARAM_REPLAY_CNTR) {
157250003Sadrian        rc_lower = OS_REG_READ(ah, AR_WOW_TXBUF(0));
158250003Sadrian        rc_upper = OS_REG_READ(ah, AR_WOW_TXBUF(1));
159250003Sadrian        *(u_int64_t *)buf = rc_lower + (rc_upper << 32);
160250003Sadrian    }
161250003Sadrian    else if (param == WOW_PARAM_KEY_TSC) {
162250003Sadrian        rc_lower = OS_REG_READ(ah, AR_WOW_TXBUF(2));
163250003Sadrian        rc_upper = OS_REG_READ(ah, AR_WOW_TXBUF(3));
164250003Sadrian        *(u_int64_t *)buf = rc_lower + (rc_upper << 32);
165250003Sadrian    }
166250003Sadrian    else if (param == WOW_PARAM_TX_SEQNUM) {
167250003Sadrian        *(u_int32_t *)buf = OS_REG_READ(ah, AR_WOW_TXBUF(4));
168250003Sadrian    }
169250003Sadrian
170250003Sadrian}
171250003Sadrian
172250003Sadrian/* Download GTK rekey related information to the embedded CPU */
173250003Sadrianu_int32_t ar9300_wowoffload_download_rekey_data(struct ath_hal *ah, u_int32_t *data, u_int32_t bytes)
174250003Sadrian{
175250003Sadrian    int i;
176250003Sadrian    int mbox_status = OS_REG_READ(ah, AR_MBOX_CTRL_STATUS);
177250003Sadrian    u_int32_t gtk_data_start;
178250003Sadrian
179250003Sadrian    HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) %s, bytes=%d\n", __func__, bytes);
180250003Sadrian    if (AR_SREV_JUPITER(ah) &&
181250003Sadrian        (bytes > (AR_WOW_OFFLOAD_GTK_DATA_WORDS_JUPITER * 4)))
182250003Sadrian    {
183250003Sadrian        bytes = AR_WOW_OFFLOAD_GTK_DATA_WORDS_JUPITER * 4;
184250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) bytes truncated to %d\n", bytes);
185250003Sadrian    }
186250003Sadrian    /* Check if mailbox is busy */
187250003Sadrian    if (mbox_status != 0) {
188250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: Mailbox register busy! Reg = 0x%x", __func__, mbox_status);
189250003Sadrian        return 1;
190250003Sadrian    }
191250003Sadrian
192250003Sadrian    /* Clear status */
193250003Sadrian    OS_REG_WRITE(ah, AR_EMB_CPU_WOW_STATUS, 0x0);
194250003Sadrian    OS_REG_WRITE(ah, AR_WLAN_WOW_ENABLE, 0);
195250003Sadrian    OS_REG_WRITE(ah, AR_WLAN_WOW_STATUS, 0xFFFFFFFF);
196250003Sadrian
197250003Sadrian    if (AR_SREV_JUPITER(ah)) {
198250003Sadrian        gtk_data_start = AR_WOW_OFFLOAD_GTK_DATA_START_JUPITER;
199250003Sadrian    } else {
200250003Sadrian        gtk_data_start = AR_WOW_OFFLOAD_GTK_DATA_START;
201250003Sadrian    }
202250003Sadrian    for (i = 0;i < bytes/4; i++) {
203250003Sadrian        OS_REG_WRITE(ah, gtk_data_start + i * 4, data[i]);
204250003Sadrian    }
205250003Sadrian
206250003Sadrian    return 0;
207250003Sadrian}
208250003Sadrian
209250003Sadrianvoid ar9300_wowoffload_download_acer_magic( struct ath_hal *ah,
210250003Sadrian                                            HAL_BOOL      valid,
211250003Sadrian                                            u_int8_t* datap,
212250003Sadrian                                            u_int32_t bytes)
213250003Sadrian{
214250003Sadrian    u_int32_t *p32 = (u_int32_t *) datap;
215250003Sadrian    u_int32_t l = 0, u = 0;
216250003Sadrian
217250003Sadrian    if (valid) {
218250003Sadrian        l = *p32;
219250003Sadrian        p32++;
220250003Sadrian        u = *(u_int16_t *) p32;
221250003Sadrian    }
222250003Sadrian
223250003Sadrian    OS_REG_WRITE(ah, AR_WOW_OFFLOAD_ACER_MAGIC_START, l);
224250003Sadrian    OS_REG_WRITE(ah, AR_WOW_OFFLOAD_ACER_MAGIC_START + 4, u);
225250003Sadrian
226250003Sadrian    HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,
227250003Sadrian        "%s: Aer Magic: %02x-%02x-%02x-%02x-%02x-%02x\n", __func__,
228250003Sadrian        datap[0], datap[1], datap[2], datap[3], datap[4], datap[5]);
229250003Sadrian}
230250003Sadrian
231250003Sadrianvoid ar9300_wowoffload_download_acer_swka(  struct ath_hal *ah,
232250003Sadrian                                            u_int32_t  id,
233250003Sadrian                                            HAL_BOOL       valid,
234250003Sadrian                                            u_int32_t  period,
235250003Sadrian                                            u_int32_t  size,
236250003Sadrian                                            u_int32_t* datap)
237250003Sadrian{
238250003Sadrian    u_int32_t ka_period[2] = {
239250003Sadrian        AR_WOW_OFFLOAD_ACER_KA0_PERIOD_MS,
240250003Sadrian        AR_WOW_OFFLOAD_ACER_KA1_PERIOD_MS
241250003Sadrian    };
242250003Sadrian    u_int32_t ka_size[2] = {
243250003Sadrian        AR_WOW_OFFLOAD_ACER_KA0_SIZE,
244250003Sadrian        AR_WOW_OFFLOAD_ACER_KA1_SIZE
245250003Sadrian    };
246250003Sadrian    u_int32_t ka_data[2] = {
247250003Sadrian        AR_WOW_OFFLOAD_ACER_KA0_DATA,
248250003Sadrian        AR_WOW_OFFLOAD_ACER_KA1_DATA
249250003Sadrian    };
250250003Sadrian    u_int32_t n_data = AR_WOW_OFFLOAD_ACER_KA0_DATA_WORDS;
251250003Sadrian    int i;
252250003Sadrian
253250003Sadrian    if (id >= 2) {
254250003Sadrian        return;
255250003Sadrian    }
256250003Sadrian
257250003Sadrian    if (valid) {
258250003Sadrian        OS_REG_WRITE(ah, ka_period[id], period);
259250003Sadrian        OS_REG_WRITE(ah, ka_size[id], size);
260250003Sadrian    } else {
261250003Sadrian        OS_REG_WRITE(ah, ka_period[id], 0);
262250003Sadrian        OS_REG_WRITE(ah, ka_size[id], 0);
263250003Sadrian    }
264250003Sadrian    HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: id=%d, period=%d ms, size=%d bytes\n",
265250003Sadrian            __func__, id, period, size);
266250003Sadrian
267250003Sadrian    if (size < (n_data * 4)) {
268250003Sadrian        n_data = (size + 3) / 4;
269250003Sadrian    }
270250003Sadrian    for (i=0; i<n_data * 4; i+=4) {
271250003Sadrian        OS_REG_WRITE(ah, ka_data[id] + i, *datap);
272250003Sadrian        /*HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) %08x\n", *datap);*/
273250003Sadrian        datap++;
274250003Sadrian    }
275250003Sadrian}
276250003Sadrian
277250003Sadrianvoid ar9300_wowoffload_download_arp_info(struct ath_hal *ah, u_int32_t id, u_int32_t *data)
278250003Sadrian{
279250003Sadrian    u_int32_t addr;
280250003Sadrian    struct hal_wow_offload_arp_info *p_info = (struct hal_wow_offload_arp_info *) data;
281250003Sadrian
282250003Sadrian    if (id == 0) {
283250003Sadrian        addr = AR_WOW_OFFLOAD_ARP0_VALID;
284250003Sadrian    } else if (id == 1) {
285250003Sadrian        addr = AR_WOW_OFFLOAD_ARP1_VALID;
286250003Sadrian    } else {
287250003Sadrian        return;
288250003Sadrian    }
289250003Sadrian
290250003Sadrian    if (p_info->valid) {
291250003Sadrian        OS_REG_WRITE(ah, addr, 0x1);
292250003Sadrian        addr += 4;
293250003Sadrian        OS_REG_WRITE(ah, addr, p_info->RemoteIPv4Address.u32);
294250003Sadrian        addr += 4;
295250003Sadrian        OS_REG_WRITE(ah, addr, p_info->HostIPv4Address.u32);
296250003Sadrian        addr += 4;
297250003Sadrian        OS_REG_WRITE(ah, addr, p_info->MacAddress.u32[0]);
298250003Sadrian        addr += 4;
299250003Sadrian        OS_REG_WRITE(ah, addr, p_info->MacAddress.u32[1]);
300250003Sadrian    } else {
301250003Sadrian        OS_REG_WRITE(ah, addr, 0x0);
302250003Sadrian    }
303250003Sadrian}
304250003Sadrian
305250003Sadrian#define WOW_WRITE_NS_IPV6_ADDRESS(_ah, _buf_addr, _p_ipv6_addr) \
306250003Sadrian    {                                                           \
307250003Sadrian        u_int32_t   offset = (_buf_addr);                       \
308250003Sadrian        u_int32_t  *p_ipv6_addr = (u_int32_t *) (_p_ipv6_addr); \
309250003Sadrian        int i;                                                  \
310250003Sadrian        for (i = 0; i < 4; i++) {                               \
311250003Sadrian            OS_REG_WRITE((_ah), offset, *p_ipv6_addr);          \
312250003Sadrian            offset += 4;                                        \
313250003Sadrian            p_ipv6_addr ++;                                     \
314250003Sadrian        }                                                       \
315250003Sadrian    }
316250003Sadrian
317250003Sadrianvoid ar9300_wowoffload_download_ns_info(struct ath_hal *ah, u_int32_t id, u_int32_t *data)
318250003Sadrian{
319250003Sadrian    u_int32_t addr;
320250003Sadrian    struct hal_wow_offload_ns_info *p_info = (struct hal_wow_offload_ns_info *) data;
321250003Sadrian    u_int8_t mc_addr[6];
322250003Sadrian
323250003Sadrian    if (id == 0) {
324250003Sadrian        addr = AR_WOW_OFFLOAD_NS0_VALID;
325250003Sadrian    } else if (id == 1) {
326250003Sadrian        addr = AR_WOW_OFFLOAD_NS1_VALID;
327250003Sadrian    } else {
328250003Sadrian        return;
329250003Sadrian    }
330250003Sadrian
331250003Sadrian    if (p_info->valid) {
332250003Sadrian        OS_REG_WRITE(ah, addr, 0x1);
333250003Sadrian        addr += 4;
334250003Sadrian        WOW_WRITE_NS_IPV6_ADDRESS(ah, addr, &p_info->RemoteIPv6Address.u32[0]);
335250003Sadrian        addr += 4 * 4;
336250003Sadrian        WOW_WRITE_NS_IPV6_ADDRESS(ah, addr, &p_info->SolicitedNodeIPv6Address.u32[0]);
337250003Sadrian        addr += 4 * 4;
338250003Sadrian        OS_REG_WRITE(ah, addr, p_info->MacAddress.u32[0]);
339250003Sadrian        addr += 4;
340250003Sadrian        OS_REG_WRITE(ah, addr, p_info->MacAddress.u32[1]);
341250003Sadrian        addr += 4;
342250003Sadrian        WOW_WRITE_NS_IPV6_ADDRESS(ah, addr, &p_info->TargetIPv6Addresses[0].u32[0]);
343250003Sadrian        addr += 4 * 4;
344250003Sadrian        WOW_WRITE_NS_IPV6_ADDRESS(ah, addr, &p_info->TargetIPv6Addresses[1].u32[0]);
345250003Sadrian
346250003Sadrian        mc_addr[0] = 0x33;
347250003Sadrian        mc_addr[1] = 0x33;
348250003Sadrian        mc_addr[2] = 0xFF;
349250003Sadrian        mc_addr[3] = p_info->SolicitedNodeIPv6Address.u8[13];
350250003Sadrian        mc_addr[4] = p_info->SolicitedNodeIPv6Address.u8[14];
351250003Sadrian        mc_addr[5] = p_info->SolicitedNodeIPv6Address.u8[15];
352250003Sadrian        ar9300_wowoffload_add_mcast_filter(ah, mc_addr);
353250003Sadrian    } else {
354250003Sadrian        OS_REG_WRITE(ah, addr, 0x0);
355250003Sadrian    }
356250003Sadrian}
357250003Sadrian
358250003Sadrian/* Download transmit parameters for GTK response frame during WoW
359250003Sadrian * offload */
360250003Sadrianu_int32_t ar9300_wow_offload_download_hal_params(struct ath_hal *ah)
361250003Sadrian{
362250003Sadrian    u_int32_t tpc = 0x3f; /* Transmit Power Control */
363250003Sadrian    u_int32_t tx_tries_series = 7;
364250003Sadrian    u_int32_t tx_rate_series, transmit_rate;
365250003Sadrian    u_int32_t gtk_txdesc_param_start;
366250003Sadrian
367250003Sadrian    if (AH_PRIVATE(ah)->ah_curchan->channel_flags & CHANNEL_CCK) {
368250003Sadrian        transmit_rate = 0x1B;    /* CCK_1M */
369250003Sadrian    } else {
370250003Sadrian        transmit_rate = 0xB;     /* OFDM_6M */
371250003Sadrian    }
372250003Sadrian
373250003Sadrian    /* Use single rate for now. Change later as need be */
374250003Sadrian    tx_rate_series  = transmit_rate;
375250003Sadrian    tx_tries_series = 7;
376250003Sadrian
377250003Sadrian    if (AR_SREV_JUPITER(ah)) {
378250003Sadrian        gtk_txdesc_param_start = AR_WOW_OFFLOAD_GTK_TXDESC_PARAM_START_JUPITER;
379250003Sadrian    } else {
380250003Sadrian        gtk_txdesc_param_start = AR_WOW_OFFLOAD_GTK_TXDESC_PARAM_START;
381250003Sadrian    }
382250003Sadrian#define AR_WOW_OFFLOAD_GTK_TXDESC_PARAM(x) (gtk_txdesc_param_start + ((x) * 4))
383250003Sadrian
384250003Sadrian    /* Do not change the data order unless firmware code on embedded
385250003Sadrian     * CPU is changed correspondingly */
386250003Sadrian    OS_REG_WRITE(ah, AR_WOW_OFFLOAD_GTK_TXDESC_PARAM(0), tx_rate_series);
387250003Sadrian    OS_REG_WRITE(ah, AR_WOW_OFFLOAD_GTK_TXDESC_PARAM(1), tx_tries_series);
388250003Sadrian    OS_REG_WRITE(ah, AR_WOW_OFFLOAD_GTK_TXDESC_PARAM(2), AH9300(ah)->ah_tx_chainmask);
389250003Sadrian    OS_REG_WRITE(ah, AR_WOW_OFFLOAD_GTK_TXDESC_PARAM(3), tpc);
390250003Sadrian
391250003Sadrian    return 0;
392250003Sadrian}
393250003Sadrian
394250003Sadrian/* Indicate to the embedded CPU that host is ready to enter WoW mode.
395250003Sadrian * Embedded CPU will copy relevant information from the MAC PCU buffer
396250003Sadrian */
397250003Sadrianu_int32_t ar9300_wow_offload_handshake(struct ath_hal *ah, u_int32_t pattern_enable)
398250003Sadrian{
399250003Sadrian    int val;
400250003Sadrian    int mbox_status = OS_REG_READ(ah, AR_MBOX_CTRL_STATUS);
401250003Sadrian#if ATH_WOW_OFFLOAD
402250003Sadrian    u_int32_t bt_handshake_timeout_us = HAL_WOW_CTRL_WAIT_BT_TO(ah) * 100000;
403250003Sadrian
404250003Sadrian#define AH_DEFAULT_BT_WAIT_TIMEOUT  3000000; /* 3 sec */
405250003Sadrian    if (bt_handshake_timeout_us == 0) {
406250003Sadrian        bt_handshake_timeout_us = AH_DEFAULT_BT_WAIT_TIMEOUT;
407250003Sadrian    }
408250003Sadrian    HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) TIMEOUT: %d us\n", bt_handshake_timeout_us);
409250003Sadrian#endif /* ATH_WOW_OFFLOAD */
410250003Sadrian
411250003Sadrian    if (mbox_status & AR_MBOX_WOW_REQ) {
412250003Sadrian        /* WOW mode request handshake is already in progress.
413250003Sadrian         * Do nothing */
414250003Sadrian        return 0;
415250003Sadrian    }
416250003Sadrian
417250003Sadrian    /* Clear status */
418250003Sadrian    OS_REG_WRITE(ah, AR_MBOX_CTRL_STATUS, 0);
419250003Sadrian    OS_REG_WRITE(ah, AR_EMB_CPU_WOW_STATUS, 0x0);
420250003Sadrian    OS_REG_WRITE(ah, AR_WLAN_WOW_ENABLE, 0);
421250003Sadrian    OS_REG_WRITE(ah, AR_WLAN_WOW_STATUS, 0xFFFFFFFF);
422250003Sadrian
423250003Sadrian    OS_REG_WRITE(ah, AR_RIMT, 0);
424250003Sadrian    OS_REG_WRITE(ah, AR_TIMT, 0);
425250003Sadrian
426250003Sadrian    val = 0;
427250003Sadrian    if (pattern_enable & AH_WOW_USER_PATTERN_EN) {
428250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - User pattern\n");
429250003Sadrian        val |= AR_EMB_CPU_WOW_ENABLE_PATTERN_MATCH;
430250003Sadrian    }
431250003Sadrian    else {
432250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - User pattern\n");
433250003Sadrian    }
434250003Sadrian    if ((pattern_enable & AH_WOW_MAGIC_PATTERN_EN)
435250003Sadrian#if ATH_WOW_OFFLOAD
436250003Sadrian        || (pattern_enable & AH_WOW_ACER_MAGIC_EN)
437250003Sadrian#endif
438250003Sadrian        )
439250003Sadrian    {
440250003Sadrian        val |= AR_EMB_CPU_WOW_ENABLE_MAGIC_PATTERN;
441250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - Magic pattern\n");
442250003Sadrian    }
443250003Sadrian    else {
444250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - Magic pattern\n");
445250003Sadrian    }
446250003Sadrian    if ((pattern_enable & AH_WOW_LINK_CHANGE)
447250003Sadrian#if ATH_WOW_OFFLOAD
448250003Sadrian        || HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_KAFAIL_ENABLE)
449250003Sadrian#endif
450250003Sadrian        )
451250003Sadrian    {
452250003Sadrian        val |= AR_EMB_CPU_WOW_ENABLE_KEEP_ALIVE_FAIL;
453250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - Kepp alive fail\n");
454250003Sadrian    }
455250003Sadrian    else {
456250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - Kepp alive fail\n");
457250003Sadrian    }
458250003Sadrian    if (pattern_enable & AH_WOW_BEACON_MISS) {
459250003Sadrian        val |= AR_EMB_CPU_WOW_ENABLE_BEACON_MISS;
460250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - Becon Miss\n");
461250003Sadrian    }
462250003Sadrian    else {
463250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - Becon Miss\n");
464250003Sadrian    }
465250003Sadrian
466250003Sadrian    OS_REG_WRITE(ah, AR_EMB_CPU_WOW_ENABLE, val);
467250003Sadrian
468250003Sadrian    OS_REG_CLR_BIT(ah, AR_MBOX_CTRL_STATUS, AR_MBOX_WOW_CONF);
469250003Sadrian    OS_REG_SET_BIT(ah, AR_MBOX_CTRL_STATUS, AR_MBOX_WOW_REQ);
470250003Sadrian    OS_REG_SET_BIT(ah, AR_MBOX_CTRL_STATUS, AR_MBOX_INT_EMB_CPU);
471250003Sadrian
472278741Sadrian    if (!ath_hal_waitfor(ah, AR_MBOX_CTRL_STATUS, AR_MBOX_WOW_CONF, AR_MBOX_WOW_CONF, bt_handshake_timeout_us)) {
473250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: WoW offload handshake failed", __func__);
474250003Sadrian        return 0;
475250003Sadrian    }
476250003Sadrian    else {
477250003Sadrian        OS_REG_CLR_BIT(ah, AR_MBOX_CTRL_STATUS, AR_MBOX_WOW_CONF);
478250003Sadrian        HALDEBUG(ah, HAL_DEBUG_POWER_MGMT, "%s: WoW offload handshake successful",__func__);
479250003Sadrian    }
480250003Sadrian    return 1;
481250003Sadrian}
482250003Sadrian#endif /* ATH_WOW_OFFLOAD */
483250003Sadrian
484250003Sadrian/*
485250003Sadrian * Notify Power Mgt is enabled in self-generated frames.
486250003Sadrian * If requested, force chip awake.
487250003Sadrian *
488250003Sadrian * Returns A_OK if chip is awake or successfully forced awake.
489250003Sadrian *
490250003Sadrian * WARNING WARNING WARNING
491250003Sadrian * There is a problem with the chip where sometimes it will not wake up.
492250003Sadrian */
493250003SadrianHAL_BOOL
494250003Sadrianar9300_set_power_mode_awake(struct ath_hal *ah, int set_chip)
495250003Sadrian{
496250003Sadrian    struct ath_hal_9300 *ahp = AH9300(ah);
497250003Sadrian#define POWER_UP_TIME   10000
498250003Sadrian    u_int32_t val;
499250003Sadrian    int i;
500250003Sadrian
501250003Sadrian    /* Set Bits 14 and 17 of AR_WA before powering on the chip. */
502250003Sadrian    OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA), ahp->ah_wa_reg_val);
503250003Sadrian    OS_DELAY(10); /* delay to allow the write to take effect. */
504250003Sadrian
505250003Sadrian    if (set_chip) {
506250003Sadrian        /* Do a Power-On-Reset if MAC is shutdown */
507250003Sadrian        if ((OS_REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_SHUTDOWN)) {
508250003Sadrian            if (ar9300_set_reset_reg(ah, HAL_RESET_POWER_ON) != AH_TRUE) {
509250003Sadrian                HALASSERT(0);
510250003Sadrian                return AH_FALSE;
511250003Sadrian            }
512250003Sadrian        }
513250003Sadrian
514250003Sadrian        OS_REG_SET_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN);
515250003Sadrian
516250003Sadrian        OS_DELAY(50);
517250003Sadrian
518250003Sadrian        for (i = POWER_UP_TIME / 50; i > 0; i--) {
519250003Sadrian            val = OS_REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M;
520250003Sadrian            if (val == AR_RTC_STATUS_ON) {
521250003Sadrian                break;
522250003Sadrian            }
523250003Sadrian            OS_DELAY(50);
524250003Sadrian            OS_REG_SET_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN);
525250003Sadrian        }
526250003Sadrian        if (i == 0) {
527250003Sadrian            HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: Failed to wakeup in %uus\n",
528250003Sadrian                     __func__, POWER_UP_TIME / 20);
529250003Sadrian            return AH_FALSE;
530250003Sadrian        }
531250003Sadrian
532250003Sadrian    }
533250003Sadrian
534250003Sadrian    OS_REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
535250003Sadrian    return AH_TRUE;
536250003Sadrian#undef POWER_UP_TIME
537250003Sadrian}
538250003Sadrian
539250003Sadrian/*
540250003Sadrian * Notify Power Mgt is disabled in self-generated frames.
541250003Sadrian * If requested, force chip to sleep.
542250003Sadrian */
543250003Sadrianstatic void
544250003Sadrianar9300_set_power_mode_sleep(struct ath_hal *ah, int set_chip)
545250003Sadrian{
546250003Sadrian    struct ath_hal_9300 *ahp = AH9300(ah);
547250003Sadrian
548250003Sadrian    OS_REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
549250003Sadrian    if (set_chip ) {
550250003Sadrian        if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) {
551250003Sadrian            OS_REG_WRITE(ah, AR_TIMER_MODE,
552250003Sadrian                    OS_REG_READ(ah, AR_TIMER_MODE) & 0xFFFFFF00);
553250003Sadrian            OS_REG_WRITE(ah, AR_GEN_TIMERS2_MODE,
554250003Sadrian                    OS_REG_READ(ah, AR_GEN_TIMERS2_MODE) & 0xFFFFFF00);
555250003Sadrian            OS_REG_WRITE(ah, AR_SLP32_INC,
556250003Sadrian                    OS_REG_READ(ah, AR_SLP32_INC) & 0xFFF00000);
557250003Sadrian            OS_REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN, 0);
558250003Sadrian            OS_DELAY(100);
559250003Sadrian        }
560250003Sadrian        /* Clear the RTC force wake bit to allow the mac to go to sleep */
561250003Sadrian        OS_REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN);
562250003Sadrian
563250003Sadrian        if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) {
564250003Sadrian            /*
565250003Sadrian             * In Jupiter, after enter sleep mode, hardware will send
566250003Sadrian             * a SYS_SLEEPING message through MCI interface. Add a
567250003Sadrian             * few us delay to make sure the message can reach BT side.
568250003Sadrian             */
569250003Sadrian            OS_DELAY(100);
570250003Sadrian        }
571250003Sadrian
572250003Sadrian        if (!AR_SREV_JUPITER_10(ah)) {
573250003Sadrian            /* Shutdown chip. Active low */
574250003Sadrian            OS_REG_CLR_BIT(ah, AR_RTC_RESET, AR_RTC_RESET_EN);
575250003Sadrian            /* Settle time */
576250003Sadrian            OS_DELAY(2);
577250003Sadrian        }
578250003Sadrian    }
579250003Sadrian
580250003Sadrian#if ATH_WOW_OFFLOAD
581250003Sadrian    if (!AR_SREV_JUPITER(ah) || !HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_SET_4004_BIT14))
582250003Sadrian#endif /* ATH_WOW_OFFLOAD */
583250003Sadrian    {
584250003Sadrian        /* Clear Bit 14 of AR_WA after putting chip into Full Sleep mode. */
585250003Sadrian        OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA),
586250003Sadrian               ahp->ah_wa_reg_val & ~AR_WA_D3_TO_L1_DISABLE);
587250003Sadrian    }
588250003Sadrian}
589250003Sadrian
590250003Sadrian/*
591250003Sadrian * Notify Power Management is enabled in self-generating
592250003Sadrian * frames. If request, set power mode of chip to
593250003Sadrian * auto/normal.  Duration in units of 128us (1/8 TU).
594250003Sadrian */
595250003Sadrianstatic void
596250003Sadrianar9300_set_power_mode_network_sleep(struct ath_hal *ah, int set_chip)
597250003Sadrian{
598250003Sadrian    struct ath_hal_9300 *ahp = AH9300(ah);
599250003Sadrian
600250003Sadrian    OS_REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
601250003Sadrian    if (set_chip) {
602250003Sadrian        HAL_CAPABILITIES *p_cap = &AH_PRIVATE(ah)->ah_caps;
603250003Sadrian
604250008Sadrian        if (! p_cap->halAutoSleepSupport) {
605250003Sadrian            /* Set wake_on_interrupt bit; clear force_wake bit */
606250003Sadrian            OS_REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON_INT);
607250003Sadrian        }
608250003Sadrian        else {
609250003Sadrian            /*
610250003Sadrian             * When chip goes into network sleep, it could be waken up by
611250003Sadrian             * MCI_INT interrupt caused by BT's HW messages (LNA_xxx, CONT_xxx)
612250003Sadrian             * which chould be in a very fast rate (~100us). This will cause
613250003Sadrian             * chip to leave and re-enter network sleep mode frequently, which
614250003Sadrian             * in consequence will have WLAN MCI HW to generate lots of
615250003Sadrian             * SYS_WAKING and SYS_SLEEPING messages which will make BT CPU
616250003Sadrian             * to busy to process.
617250003Sadrian             */
618250003Sadrian            if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) {
619250003Sadrian                OS_REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN,
620250003Sadrian                        OS_REG_READ(ah, AR_MCI_INTERRUPT_RX_MSG_EN) &
621250003Sadrian                                    ~AR_MCI_INTERRUPT_RX_HW_MSG_MASK);
622250003Sadrian            }
623250003Sadrian
624250003Sadrian            /* Clear the RTC force wake bit to allow the mac to go to sleep */
625250003Sadrian            OS_REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN);
626250003Sadrian
627250003Sadrian            if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) {
628250003Sadrian                /*
629250003Sadrian                 * In Jupiter, after enter sleep mode, hardware will send
630250003Sadrian                 * a SYS_SLEEPING message through MCI interface. Add a
631250003Sadrian                 * few us delay to make sure the message can reach BT side.
632250003Sadrian                 */
633250003Sadrian                OS_DELAY(30);
634250003Sadrian            }
635250003Sadrian        }
636250003Sadrian    }
637250003Sadrian
638250003Sadrian#if ATH_WOW_OFFLOAD
639250003Sadrian    if (!AR_SREV_JUPITER(ah) || !HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_SET_4004_BIT14))
640250003Sadrian#endif /* ATH_WOW_OFFLOAD */
641250003Sadrian    {
642250003Sadrian        /* Clear Bit 14 of AR_WA after putting chip into Sleep mode. */
643250003Sadrian        OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA),
644250003Sadrian               ahp->ah_wa_reg_val & ~AR_WA_D3_TO_L1_DISABLE);
645250003Sadrian    }
646250003Sadrian}
647250003Sadrian
648250003Sadrian/*
649250003Sadrian * Set power mgt to the requested mode, and conditionally set
650250003Sadrian * the chip as well
651250003Sadrian */
652250003SadrianHAL_BOOL
653250003Sadrianar9300_set_power_mode(struct ath_hal *ah, HAL_POWER_MODE mode, int set_chip)
654250003Sadrian{
655250003Sadrian    struct ath_hal_9300 *ahp = AH9300(ah);
656250008Sadrian#if defined(AH_DEBUG) || defined(AH_PRINT_FILTER)
657250003Sadrian    static const char* modes[] = {
658250003Sadrian        "AWAKE",
659250003Sadrian        "FULL-SLEEP",
660250003Sadrian        "NETWORK SLEEP",
661250003Sadrian        "UNDEFINED"
662250003Sadrian    };
663250003Sadrian#endif
664250003Sadrian    int status = AH_TRUE;
665250003Sadrian
666250003Sadrian    HALDEBUG(ah, HAL_DEBUG_POWER_MGMT, "%s: %s -> %s (%s)\n", __func__,
667250003Sadrian        modes[ar9300_get_power_mode(ah)], modes[mode],
668250003Sadrian        set_chip ? "set chip " : "");
669269793Sadrian    OS_MARK(ah, AH_MARK_CHIP_POWER, mode);
670250003Sadrian
671250003Sadrian    switch (mode) {
672250003Sadrian    case HAL_PM_AWAKE:
673265113Sadrian        if (set_chip)
674265113Sadrian            ah->ah_powerMode = mode;
675250003Sadrian        status = ar9300_set_power_mode_awake(ah, set_chip);
676250003Sadrian#if ATH_SUPPORT_MCI
677250008Sadrian        if (AH_PRIVATE(ah)->ah_caps.halMciSupport) {
678250003Sadrian            OS_REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
679250003Sadrian        }
680250003Sadrian#endif
681278741Sadrian        ahp->ah_chip_full_sleep = AH_FALSE;
682250003Sadrian        break;
683250003Sadrian    case HAL_PM_FULL_SLEEP:
684250003Sadrian#if ATH_SUPPORT_MCI
685250008Sadrian        if (AH_PRIVATE(ah)->ah_caps.halMciSupport) {
686250003Sadrian            if (ar9300_get_power_mode(ah) == HAL_PM_AWAKE) {
687250003Sadrian                if ((ar9300_mci_state(ah, HAL_MCI_STATE_ENABLE, NULL) != 0) &&
688250003Sadrian                    (ahp->ah_mci_bt_state != MCI_BT_SLEEP) &&
689250003Sadrian                    !ahp->ah_mci_halted_bt_gpm)
690250003Sadrian                {
691250003Sadrian                    HALDEBUG(ah, HAL_DEBUG_BT_COEX,
692250003Sadrian                        "(MCI) %s: HALT BT GPM (full_sleep)\n", __func__);
693250003Sadrian                    ar9300_mci_send_coex_halt_bt_gpm(ah, AH_TRUE, AH_TRUE);
694250003Sadrian                }
695250003Sadrian            }
696250003Sadrian            ahp->ah_mci_ready = AH_FALSE;
697250003Sadrian        }
698250003Sadrian#endif
699250003Sadrian#if ATH_SUPPORT_MCI
700250008Sadrian        if (AH_PRIVATE(ah)->ah_caps.halMciSupport) {
701250003Sadrian            OS_REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
702250003Sadrian        }
703250003Sadrian#endif
704250003Sadrian        ar9300_set_power_mode_sleep(ah, set_chip);
705265113Sadrian        if (set_chip) {
706265113Sadrian            ahp->ah_chip_full_sleep = AH_TRUE;
707265113Sadrian            ah->ah_powerMode = mode;
708265113Sadrian        }
709250003Sadrian        break;
710250003Sadrian    case HAL_PM_NETWORK_SLEEP:
711250003Sadrian#if ATH_SUPPORT_MCI
712250008Sadrian        if (AH_PRIVATE(ah)->ah_caps.halMciSupport) {
713250003Sadrian            OS_REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
714250003Sadrian        }
715250003Sadrian#endif
716250003Sadrian        ar9300_set_power_mode_network_sleep(ah, set_chip);
717265113Sadrian        if (set_chip) {
718265113Sadrian            ah->ah_powerMode = mode;
719265113Sadrian        }
720250003Sadrian        break;
721250003Sadrian    default:
722250003Sadrian        HALDEBUG(ah, HAL_DEBUG_POWER_MGMT,
723250003Sadrian            "%s: unknown power mode %u\n", __func__, mode);
724269793Sadrian        OS_MARK(ah, AH_MARK_CHIP_POWER_DONE, -1);
725250003Sadrian        return AH_FALSE;
726250003Sadrian    }
727269793Sadrian    OS_MARK(ah, AH_MARK_CHIP_POWER_DONE, status);
728250003Sadrian    return status;
729250003Sadrian}
730250003Sadrian
731250003Sadrian/*
732250003Sadrian * Return the current sleep mode of the chip
733250003Sadrian */
734250003SadrianHAL_POWER_MODE
735250003Sadrianar9300_get_power_mode(struct ath_hal *ah)
736250003Sadrian{
737250003Sadrian    int mode = OS_REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M;
738250003Sadrian
739250003Sadrian    switch (mode) {
740250003Sadrian    case AR_RTC_STATUS_ON:
741250003Sadrian    case AR_RTC_STATUS_WAKEUP:
742250003Sadrian        return HAL_PM_AWAKE;
743250003Sadrian        break;
744250003Sadrian    case AR_RTC_STATUS_SLEEP:
745250003Sadrian        return HAL_PM_NETWORK_SLEEP;
746250003Sadrian        break;
747250003Sadrian    case AR_RTC_STATUS_SHUTDOWN:
748250003Sadrian        return HAL_PM_FULL_SLEEP;
749250003Sadrian        break;
750250003Sadrian    default:
751250003Sadrian        HALDEBUG(ah, HAL_DEBUG_POWER_MGMT,
752250003Sadrian            "%s: unknown power mode 0x%x\n", __func__, mode);
753250003Sadrian        return HAL_PM_UNDEFINED;
754250003Sadrian    }
755250003Sadrian}
756250003Sadrian
757250003Sadrian/*
758250003Sadrian * Set SM power save mode
759250003Sadrian */
760250003Sadrianvoid
761250003Sadrianar9300_set_sm_power_mode(struct ath_hal *ah, HAL_SMPS_MODE mode)
762250003Sadrian{
763250003Sadrian    int regval;
764250003Sadrian    struct ath_hal_9300 *ahp = AH9300(ah);
765250003Sadrian
766250003Sadrian    if (ar9300_get_capability(ah, HAL_CAP_DYNAMIC_SMPS, 0, AH_NULL) != HAL_OK) {
767250003Sadrian        return;
768250003Sadrian    }
769250003Sadrian
770250003Sadrian    /* Program low & high power chainmask settings and enable MAC control */
771250003Sadrian    regval = SM(AR_PCU_SMPS_LPWR_CHNMSK_VAL, AR_PCU_SMPS_LPWR_CHNMSK) |
772250003Sadrian             SM(ahp->ah_rx_chainmask, AR_PCU_SMPS_HPWR_CHNMSK) |
773250003Sadrian             AR_PCU_SMPS_MAC_CHAINMASK;
774250003Sadrian
775250003Sadrian    /* Program registers according to required SM power mode.*/
776250003Sadrian    switch (mode) {
777250003Sadrian    case HAL_SMPS_SW_CTRL_LOW_PWR:
778250003Sadrian        OS_REG_WRITE(ah, AR_PCU_SMPS, regval);
779250003Sadrian        break;
780250003Sadrian    case HAL_SMPS_SW_CTRL_HIGH_PWR:
781250003Sadrian        OS_REG_WRITE(ah, AR_PCU_SMPS, regval | AR_PCU_SMPS_SW_CTRL_HPWR);
782250003Sadrian        break;
783250003Sadrian    case HAL_SMPS_HW_CTRL:
784250003Sadrian        OS_REG_WRITE(ah, AR_PCU_SMPS, regval | AR_PCU_SMPS_HW_CTRL_EN);
785250003Sadrian        break;
786250003Sadrian    case HAL_SMPS_DEFAULT:
787250003Sadrian        OS_REG_WRITE(ah, AR_PCU_SMPS, 0);
788250003Sadrian        break;
789250003Sadrian    default:
790250003Sadrian        break;
791250003Sadrian    }
792250003Sadrian    ahp->ah_sm_power_mode = mode;
793250003Sadrian}
794250003Sadrian
795250003Sadrian#if ATH_WOW
796250003Sadrian#if NOT_NEEDED_FOR_OSPREY /* not compiled for darwin */
797250003Sadrian/*
798250003Sadrian * This routine is called to configure the SerDes register for the
799250003Sadrian * Merlin 2.0 and above chip during WOW sleep.
800250003Sadrian */
801250003Sadrianstatic void
802250003Sadrianar9280_config_ser_des__wow_sleep(struct ath_hal *ah)
803250003Sadrian{
804250003Sadrian    int i;
805250003Sadrian    struct ath_hal_9300 *ahp = AH9300(ah);
806250003Sadrian
807250003Sadrian    /*
808250003Sadrian     * For WOW sleep, we reprogram the SerDes so that the PLL and CHK REQ
809250003Sadrian     * are both enabled. This uses more power but the Maverick team reported
810250003Sadrian     * that otherwise, WOW sleep is unstable and chip may disappears.
811250003Sadrian     */
812250003Sadrian    for (i = 0; i < ahp->ah_ini_pcie_serdes_wow.ia_rows; i++) {
813250003Sadrian        OS_REG_WRITE(ah,
814250003Sadrian            INI_RA(&ahp->ah_ini_pcie_serdes_wow, i, 0),
815250003Sadrian            INI_RA(&ahp->ah_ini_pcie_serdes_wow, i, 1));
816250003Sadrian    }
817250003Sadrian    OS_DELAY(1000);
818250003Sadrian}
819250003Sadrian#endif /* if NOT_NEEDED_FOR_OSPREY */
820250003Sadrianstatic HAL_BOOL
821250003Sadrianar9300_wow_create_keep_alive_pattern(struct ath_hal *ah)
822250003Sadrian{
823250003Sadrian    struct ath_hal_9300 *ahp = AH9300(ah);
824250003Sadrian    u_int32_t  frame_len = 28;
825250003Sadrian    u_int32_t  tpc = 0x3f;
826250003Sadrian    u_int32_t  transmit_rate;
827250003Sadrian    u_int32_t  frame_type = 0x2;    /* Frame Type -> Data; */
828250003Sadrian    u_int32_t  sub_type = 0x4;      /* Subtype -> Null Data */
829250003Sadrian    u_int32_t  to_ds = 1;
830250003Sadrian    u_int32_t  duration_id = 0x3d;
831250003Sadrian    u_int8_t   *sta_mac_addr, *ap_mac_addr;
832250003Sadrian    u_int8_t   *addr1, *addr2, *addr3;
833250003Sadrian    u_int32_t  ctl[13] = { 0, };
834250003Sadrian#define NUM_KA_DATA_WORDS 6
835250003Sadrian    u_int32_t  data_word[NUM_KA_DATA_WORDS];
836250003Sadrian    u_int32_t  i;
837250003Sadrian    u_int32_t wow_ka_dataword0;
838250003Sadrian
839250003Sadrian    sta_mac_addr = (u_int8_t *)ahp->ah_macaddr;
840250003Sadrian    ap_mac_addr = (u_int8_t *)ahp->ah_bssid;
841250003Sadrian    addr2 = sta_mac_addr;
842250003Sadrian    addr1 = addr3 = ap_mac_addr;
843250003Sadrian
844250003Sadrian    if (AH_PRIVATE(ah)->ah_curchan->channel_flags & CHANNEL_CCK) {
845250003Sadrian        transmit_rate = 0x1B;    /* CCK_1M */
846250003Sadrian    } else {
847250003Sadrian        transmit_rate = 0xB;     /* OFDM_6M */
848250003Sadrian    }
849250003Sadrian
850250003Sadrian    /* Set the Transmit Buffer. */
851250003Sadrian    ctl[0] = (frame_len | (tpc << 16));
852250003Sadrian    ctl[1] = 0;
853250003Sadrian    ctl[2] = (0x7 << 16);  /* tx_tries0 */
854250003Sadrian    ctl[3] = transmit_rate;
855250003Sadrian    ctl[4] = 0;
856250003Sadrian    ctl[7] = ahp->ah_tx_chainmask << 2;
857250003Sadrian
858250003Sadrian    for (i = 0; i < 13; i++) {
859250003Sadrian        OS_REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]);
860250003Sadrian    }
861250003Sadrian
862250003Sadrian    data_word[0] =
863250003Sadrian        (frame_type  <<  2) |
864250003Sadrian        (sub_type    <<  4) |
865250003Sadrian        (to_ds       <<  8) |
866250003Sadrian        (duration_id << 16);
867250003Sadrian    data_word[1] = (((u_int32_t)addr1[3] << 24) | ((u_int32_t)addr1[2] << 16) |
868250003Sadrian                  ((u_int32_t)addr1[1]) << 8 | ((u_int32_t)addr1[0]));
869250003Sadrian    data_word[2] = (((u_int32_t)addr2[1] << 24) | ((u_int32_t)addr2[0] << 16) |
870250003Sadrian                  ((u_int32_t)addr1[5]) << 8 | ((u_int32_t)addr1[4]));
871250003Sadrian    data_word[3] = (((u_int32_t)addr2[5] << 24) | ((u_int32_t)addr2[4] << 16) |
872250003Sadrian                  ((u_int32_t)addr2[3]) << 8 | ((u_int32_t)addr2[2]));
873250003Sadrian    data_word[4] = (((u_int32_t)addr3[3] << 24) | ((u_int32_t)addr3[2] << 16) |
874250003Sadrian                  ((u_int32_t)addr3[1]) << 8 | (u_int32_t)addr3[0]);
875250003Sadrian    data_word[5] = (((u_int32_t)addr3[5]) << 8 | ((u_int32_t)addr3[4]));
876250003Sadrian
877250003Sadrian    if (AR_SREV_JUPITER_20_OR_LATER(ah) || AR_SREV_APHRODITE(ah)) {
878250003Sadrian        /* Jupiter 2.0 has an extra descriptor word (Time based
879250003Sadrian         * discard) compared to other chips */
880250003Sadrian        OS_REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + 12 * 4), 0);
881250003Sadrian        wow_ka_dataword0 = AR_WOW_TXBUF(13);
882250003Sadrian    }
883250003Sadrian    else {
884250003Sadrian        wow_ka_dataword0 = AR_WOW_TXBUF(12);
885250003Sadrian    }
886250003Sadrian
887250003Sadrian    for (i = 0; i < NUM_KA_DATA_WORDS; i++) {
888250003Sadrian        OS_REG_WRITE(ah, (wow_ka_dataword0 + i * 4), data_word[i]);
889250003Sadrian    }
890250003Sadrian
891250003Sadrian    return AH_TRUE;
892250003Sadrian}
893250003Sadrian
894250003Sadrian/* TBD: Should querying hal for hardware capability */
895250003Sadrian#define MAX_PATTERN_SIZE      256
896250003Sadrian#define MAX_PATTERN_MASK_SIZE  32
897250003Sadrian#define MAX_NUM_USER_PATTERN    6 /* Deducting the disassoc/deauth packets */
898250003Sadrian
899250003Sadrianvoid
900250003Sadrianar9300_wow_apply_pattern(
901250003Sadrian    struct ath_hal *ah,
902250003Sadrian    u_int8_t *p_ath_pattern,
903250003Sadrian    u_int8_t *p_ath_mask,
904250003Sadrian    int32_t pattern_count,
905250003Sadrian    u_int32_t ath_pattern_len)
906250003Sadrian{
907250003Sadrian    int i;
908250003Sadrian    u_int32_t    reg_pat[] = {
909250003Sadrian                  AR_WOW_TB_PATTERN0,
910250003Sadrian                  AR_WOW_TB_PATTERN1,
911250003Sadrian                  AR_WOW_TB_PATTERN2,
912250003Sadrian                  AR_WOW_TB_PATTERN3,
913250003Sadrian                  AR_WOW_TB_PATTERN4,
914250003Sadrian                  AR_WOW_TB_PATTERN5,
915250003Sadrian                  AR_WOW_TB_PATTERN6,
916250003Sadrian                  AR_WOW_TB_PATTERN7
917250003Sadrian                 };
918250003Sadrian    u_int32_t    reg_mask[] = {
919250003Sadrian                  AR_WOW_TB_MASK0,
920250003Sadrian                  AR_WOW_TB_MASK1,
921250003Sadrian                  AR_WOW_TB_MASK2,
922250003Sadrian                  AR_WOW_TB_MASK3,
923250003Sadrian                  AR_WOW_TB_MASK4,
924250003Sadrian                  AR_WOW_TB_MASK5,
925250003Sadrian                  AR_WOW_TB_MASK6,
926250003Sadrian                  AR_WOW_TB_MASK7
927250003Sadrian                 };
928250003Sadrian    u_int32_t   pattern_val;
929250003Sadrian    u_int32_t   mask_val;
930250003Sadrian    u_int32_t   val;
931250003Sadrian    u_int8_t    mask_bit = 0x1;
932250003Sadrian    u_int8_t    pattern;
933250003Sadrian
934250003Sadrian    /* TBD: should check count by querying the hardware capability */
935250003Sadrian    if (pattern_count >= MAX_NUM_USER_PATTERN) {
936250003Sadrian        return;
937250003Sadrian    }
938250003Sadrian
939250003Sadrian    pattern = (u_int8_t)OS_REG_READ(ah, AR_WOW_PATTERN_REG);
940250003Sadrian    pattern = pattern | (mask_bit << pattern_count);
941250003Sadrian    OS_REG_WRITE(ah, AR_WOW_PATTERN_REG, pattern);
942250003Sadrian
943250003Sadrian    /* Set the registers for pattern */
944250003Sadrian    for (i = 0; i < MAX_PATTERN_SIZE; i += 4) {
945250003Sadrian        pattern_val = (((u_int32_t)p_ath_pattern[i + 0]) |
946250003Sadrian                       ((u_int32_t)p_ath_pattern[i + 1] << 8) |
947250003Sadrian                       ((u_int32_t)p_ath_pattern[i + 2] << 16) |
948250003Sadrian                       ((u_int32_t)p_ath_pattern[i + 3] << 24));
949250003Sadrian        OS_REG_WRITE(ah, (reg_pat[pattern_count] + i), pattern_val);
950250003Sadrian    }
951250003Sadrian
952250003Sadrian    /* Set the registers for mask */
953250003Sadrian    for (i = 0; i < MAX_PATTERN_MASK_SIZE; i += 4) {
954250003Sadrian        mask_val = (((u_int32_t)p_ath_mask[i + 0]) |
955250003Sadrian                    ((u_int32_t)p_ath_mask[i + 1] << 8) |
956250003Sadrian                    ((u_int32_t)p_ath_mask[i + 2] << 16) |
957250003Sadrian                    ((u_int32_t)p_ath_mask[i + 3] << 24));
958250003Sadrian        OS_REG_WRITE(ah, (reg_mask[pattern_count] + i), mask_val);
959250003Sadrian    }
960250003Sadrian
961250003Sadrian    /* XXX */
962250003Sadrian    /* Set the pattern length to be matched */
963250003Sadrian    if (pattern_count < 4) {
964250003Sadrian        /* Pattern 0-3 uses AR_WOW_LENGTH1_REG register */
965250003Sadrian        val = OS_REG_READ(ah, AR_WOW_LENGTH1_REG);
966250003Sadrian        val = ((val & (~AR_WOW_LENGTH1_MASK(pattern_count))) |
967250003Sadrian               ((ath_pattern_len & AR_WOW_LENGTH_MAX) <<
968250003Sadrian                AR_WOW_LENGTH1_SHIFT(pattern_count)));
969250003Sadrian        OS_REG_WRITE(ah, AR_WOW_LENGTH1_REG, val);
970250003Sadrian    } else {
971250003Sadrian        /* Pattern 4-7 uses AR_WOW_LENGTH2_REG register */
972250003Sadrian        val = OS_REG_READ(ah, AR_WOW_LENGTH2_REG);
973250003Sadrian        val = ((val & (~AR_WOW_LENGTH2_MASK(pattern_count))) |
974250003Sadrian               ((ath_pattern_len & AR_WOW_LENGTH_MAX) <<
975250003Sadrian                AR_WOW_LENGTH2_SHIFT(pattern_count)));
976250003Sadrian        OS_REG_WRITE(ah, AR_WOW_LENGTH2_REG, val);
977250003Sadrian    }
978250003Sadrian
979250003Sadrian    AH_PRIVATE(ah)->ah_wow_event_mask |=
980250003Sadrian        (1 << (pattern_count + AR_WOW_PATTERN_FOUND_SHIFT));
981250003Sadrian
982250003Sadrian    return;
983250003Sadrian}
984250003Sadrian
985250003SadrianHAL_BOOL
986250003Sadrianar9300_set_power_mode_wow_sleep(struct ath_hal *ah)
987250003Sadrian{
988250003Sadrian    OS_REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
989250003Sadrian
990250003Sadrian    OS_REG_WRITE(ah, AR_CR, AR_CR_RXD);    /* Set receive disable bit */
991278741Sadrian    if (!ath_hal_waitfor(ah, AR_CR, AR_CR_RXE, 0, AH_WAIT_TIMEOUT)) {
992250003Sadrian        HALDEBUG(ah, HAL_DEBUG_POWER_MGMT, "%s: dma failed to stop in 10ms\n"
993250003Sadrian                 "AR_CR=0x%08x\nAR_DIAG_SW=0x%08x\n", __func__,
994250003Sadrian                 OS_REG_READ(ah, AR_CR), OS_REG_READ(ah, AR_DIAG_SW));
995250003Sadrian        return AH_FALSE;
996250003Sadrian    } else {
997250003Sadrian#if 0
998250003Sadrian        OS_REG_WRITE(ah, AR_RXDP, 0x0);
999250003Sadrian#endif
1000250003Sadrian
1001250003Sadrian        HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE,
1002250003Sadrian            "%s: TODO How to disable RXDP!!\n", __func__);
1003250003Sadrian
1004250003Sadrian#if ATH_SUPPORT_MCI
1005250008Sadrian        if (AH_PRIVATE(ah)->ah_caps.halMciSupport) {
1006250003Sadrian            OS_REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
1007250003Sadrian        }
1008250003Sadrian#endif
1009250003Sadrian        OS_REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON_INT);
1010250003Sadrian
1011250003Sadrian        return AH_TRUE;
1012250003Sadrian    }
1013250003Sadrian}
1014250003Sadrian
1015250003Sadrian
1016250003SadrianHAL_BOOL
1017250003Sadrianar9300_wow_enable(
1018250003Sadrian    struct ath_hal *ah,
1019250003Sadrian    u_int32_t pattern_enable,
1020250003Sadrian    u_int32_t timeout_in_seconds,
1021250003Sadrian    int clearbssid,
1022250003Sadrian    HAL_BOOL offloadEnable)
1023250003Sadrian{
1024250003Sadrian    uint32_t init_val, val, rval = 0;
1025250003Sadrian    const int ka_delay = 4; /* Delay of 4 millisec between two keep_alive's */
1026250003Sadrian    uint32_t wow_event_mask;
1027250003Sadrian#if ATH_WOW_OFFLOAD
1028250003Sadrian    uint32_t wow_feature_enable =
1029250003Sadrian            //AR_WOW_OFFLOAD_ENA_GTK            |
1030250003Sadrian            //AR_WOW_OFFLOAD_ENA_ARP_OFFLOAD    |
1031250003Sadrian            //AR_WOW_OFFLOAD_ENA_NS_OFFLOAD     |
1032250003Sadrian            //AR_WOW_OFFLOAD_ENA_ACER_MAGIC     |
1033250003Sadrian            //AR_WOW_OFFLOAD_ENA_STD_MAGIC      |
1034250003Sadrian            //AR_WOW_OFFLOAD_ENA_4WAY_WAKE      |
1035250003Sadrian            //AR_WOW_OFFLOAD_ENA_SWKA           |
1036250003Sadrian            //AR_WOW_OFFLOAD_ENA_BT_SLEEP       |
1037250003Sadrian            AR_WOW_OFFLOAD_ENA_SW_NULL;
1038250003Sadrian#endif
1039250003Sadrian
1040250003Sadrian    /*
1041250003Sadrian     * ah_wow_event_mask is a mask to the AR_WOW_PATTERN_REG register to
1042250003Sadrian     * indicate which WOW events that we have enabled. The WOW Events are
1043250003Sadrian     * from the pattern_enable in this function and pattern_count of
1044250003Sadrian     * ar9300_wow_apply_pattern()
1045250003Sadrian     */
1046250003Sadrian    wow_event_mask = AH_PRIVATE(ah)->ah_wow_event_mask;
1047250003Sadrian
1048250003Sadrian    HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE,
1049250003Sadrian        "%s: offload: %d, pattern: %08x, event_mask: %08x\n",
1050250003Sadrian        __func__, offloadEnable, pattern_enable, wow_event_mask);
1051250003Sadrian
1052250003Sadrian    /*
1053250003Sadrian     * Untie Power-On-Reset from the PCI-E Reset. When we are in WOW sleep,
1054250003Sadrian     * we do not want the Reset from the PCI-E to disturb our hw state.
1055250003Sadrian     */
1056250003Sadrian    if (AH_PRIVATE(ah)->ah_is_pci_express == AH_TRUE) {
1057250003Sadrian
1058250003Sadrian        u_int32_t wa_reg_val;
1059250003Sadrian        /*
1060250003Sadrian         * We need to untie the internal POR (power-on-reset) to the external
1061250003Sadrian         * PCI-E reset. We also need to tie the PCI-E Phy reset to the PCI-E
1062250003Sadrian         * reset.
1063250003Sadrian         */
1064250008Sadrian        HAL_DEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE,
1065250003Sadrian            "%s: Untie POR and PCIE reset\n", __func__);
1066250003Sadrian        wa_reg_val = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_WA));
1067250003Sadrian        wa_reg_val = wa_reg_val & ~(AR_WA_UNTIE_RESET_EN);
1068250003Sadrian        wa_reg_val = wa_reg_val | AR_WA_RESET_EN | AR_WA_POR_SHORT;
1069250003Sadrian        /*
1070250003Sadrian         * This bit is to bypass the EEPROM/OTP state machine, (by clearing its
1071250003Sadrian         * busy state while PCIE_rst is asserted), to allow BT embedded CPU
1072250003Sadrian         * be able to access WLAN registers. Otherwise the eCPU access will be
1073250003Sadrian         * stalled as eeprom_sm is held in busy state.
1074250003Sadrian         *
1075250003Sadrian         * EV91928 is that when this bit is set, after host wakeup and PCIE_rst
1076250003Sadrian         * deasserted, PCIE configuration registers will be reset and DeviceID
1077250003Sadrian         * SubsystemID etc. registers will be different from values before
1078250003Sadrian         * entering sleep. This will cause Windows to detect a device removal.
1079250003Sadrian         *
1080250003Sadrian         * For HW WOW, this bit should keep as cleared.
1081250003Sadrian         */
1082250003Sadrian        if (offloadEnable) {
1083250003Sadrian            HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE,
1084250003Sadrian                "%s: Set AR_WA.13 COLD_RESET_OVERRIDE\n", __func__);
1085250003Sadrian            wa_reg_val = wa_reg_val | AR_WA_COLD_RESET_OVERRIDE;
1086250003Sadrian
1087250003Sadrian#if ATH_WOW_OFFLOAD
1088250003Sadrian            if (AR_SREV_JUPITER(ah)) {
1089250003Sadrian                wa_reg_val = wa_reg_val | AR_WA_D3_TO_L1_DISABLE;
1090250003Sadrian            }
1091250003Sadrian#endif
1092250003Sadrian        }
1093250003Sadrian        OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA), wa_reg_val);
1094250003Sadrian    }
1095250003Sadrian
1096250003Sadrian    /*
1097250003Sadrian     * Set the power states appropriately and enable pme.
1098250003Sadrian     */
1099250003Sadrian    val = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL));
1100250003Sadrian    val |=
1101250003Sadrian        AR_PMCTRL_HOST_PME_EN     |
1102250003Sadrian        AR_PMCTRL_PWR_PM_CTRL_ENA |
1103250003Sadrian        AR_PMCTRL_AUX_PWR_DET;
1104250003Sadrian
1105250003Sadrian    /*
1106250003Sadrian     * Set and clear WOW_PME_CLEAR registers for the chip to generate next
1107250003Sadrian     * wow signal.
1108250003Sadrian     */
1109250003Sadrian    val |= AR_PMCTRL_WOW_PME_CLR;
1110250003Sadrian    OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL), val);
1111250003Sadrian    val &= ~AR_PMCTRL_WOW_PME_CLR;
1112250003Sadrian    OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL), val);
1113250003Sadrian
1114250003Sadrian    /*
1115250003Sadrian     * Setup for for:
1116250003Sadrian     *     - beacon misses
1117250003Sadrian     *     - magic pattern
1118250003Sadrian     *     - keep alive timeout
1119250003Sadrian     *     - pattern matching
1120250003Sadrian     */
1121250003Sadrian
1122250003Sadrian    /*
1123250003Sadrian     * Program some default values for keep-alives, beacon misses, etc.
1124250003Sadrian     */
1125250003Sadrian    init_val = OS_REG_READ(ah, AR_WOW_PATTERN_REG);
1126250003Sadrian    val = AR_WOW_BACK_OFF_SHIFT(AR_WOW_PAT_BACKOFF) | init_val;
1127250003Sadrian    OS_REG_WRITE(ah, AR_WOW_PATTERN_REG, val);
1128250003Sadrian    rval = OS_REG_READ(ah, AR_WOW_PATTERN_REG);
1129250003Sadrian
1130250003Sadrian    val =
1131250003Sadrian        AR_WOW_AIFS_CNT(AR_WOW_CNT_AIFS_CNT) |
1132250003Sadrian        AR_WOW_SLOT_CNT(AR_WOW_CNT_SLOT_CNT) |
1133250003Sadrian        AR_WOW_KEEP_ALIVE_CNT(AR_WOW_CNT_KA_CNT);
1134250003Sadrian    OS_REG_WRITE(ah, AR_WOW_COUNT_REG, val);
1135250003Sadrian    rval = OS_REG_READ(ah, AR_WOW_COUNT_REG);
1136250003Sadrian
1137250003Sadrian    if (pattern_enable & AH_WOW_BEACON_MISS) {
1138250003Sadrian        val = AR_WOW_BEACON_TIMO;
1139250003Sadrian    } else {
1140250003Sadrian        /* We are not using the beacon miss. Program a large value. */
1141250003Sadrian        val = AR_WOW_BEACON_TIMO_MAX;
1142250003Sadrian    }
1143250003Sadrian    OS_REG_WRITE(ah, AR_WOW_BCN_TIMO_REG, val);
1144250003Sadrian    rval = OS_REG_READ(ah, AR_WOW_BCN_TIMO_REG);
1145250003Sadrian
1146250003Sadrian    /*
1147250003Sadrian     * Keep Alive Timo in ms.
1148250003Sadrian     */
1149250003Sadrian    if (pattern_enable == 0) {
1150250003Sadrian        val =  AR_WOW_KEEP_ALIVE_NEVER;
1151250003Sadrian    } else {
1152250003Sadrian        val =  AH_PRIVATE(ah)->ah_config.ath_hal_keep_alive_timeout * 32;
1153250003Sadrian    }
1154250003Sadrian    OS_REG_WRITE(ah, AR_WOW_KEEP_ALIVE_TIMO_REG, val);
1155250003Sadrian    rval = OS_REG_READ(ah, AR_WOW_KEEP_ALIVE_TIMO_REG);
1156250003Sadrian
1157250003Sadrian    /*
1158250003Sadrian     * Keep Alive delay in us.
1159250003Sadrian     */
1160250003Sadrian    val = ka_delay * 1000;
1161250003Sadrian    OS_REG_WRITE(ah, AR_WOW_KEEP_ALIVE_DELAY_REG, val);
1162250003Sadrian    rval = OS_REG_READ(ah, AR_WOW_KEEP_ALIVE_DELAY_REG);
1163250003Sadrian
1164250003Sadrian    /*
1165250003Sadrian     * Create keep_alive Pattern to respond to beacons.
1166250003Sadrian     */
1167250003Sadrian    ar9300_wow_create_keep_alive_pattern(ah);
1168250003Sadrian
1169250003Sadrian    /*
1170250003Sadrian     * Configure Mac Wow Registers.
1171250003Sadrian     */
1172250003Sadrian
1173250003Sadrian    val = OS_REG_READ(ah, AR_WOW_KEEP_ALIVE_REG);
1174250003Sadrian
1175250003Sadrian    /*
1176250003Sadrian     * Send keep alive timeouts anyway.
1177250003Sadrian     */
1178250003Sadrian    val &= ~AR_WOW_KEEP_ALIVE_AUTO_DIS;
1179250003Sadrian
1180250003Sadrian    if (pattern_enable & AH_WOW_LINK_CHANGE) {
1181250003Sadrian        val &= ~ AR_WOW_KEEP_ALIVE_FAIL_DIS;
1182250003Sadrian        wow_event_mask |= AR_WOW_KEEP_ALIVE_FAIL;
1183250003Sadrian    } else {
1184250003Sadrian        val |=  AR_WOW_KEEP_ALIVE_FAIL_DIS;
1185250003Sadrian    }
1186250003Sadrian#if ATH_WOW_OFFLOAD
1187250003Sadrian    if (offloadEnable) {
1188250003Sadrian        /* Don't enable KA frames yet. BT CPU is not
1189250003Sadrian         * yet ready. */
1190250003Sadrian    }
1191250003Sadrian    else
1192250003Sadrian#endif /* ATH_WOW_OFFLOAD */
1193250003Sadrian    {
1194250003Sadrian        OS_REG_WRITE(ah, AR_WOW_KEEP_ALIVE_REG, val);
1195250003Sadrian        val = OS_REG_READ(ah, AR_WOW_KEEP_ALIVE_REG);
1196250003Sadrian    }
1197250003Sadrian
1198250003Sadrian
1199250003Sadrian    /*
1200250003Sadrian     * We are relying on a bmiss failure. Ensure we have enough
1201250003Sadrian     * threshold to prevent AH_FALSE positives.
1202250003Sadrian     */
1203250003Sadrian    OS_REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_THR_BM_THR,
1204250003Sadrian        AR_WOW_BMISSTHRESHOLD);
1205250003Sadrian
1206250003Sadrian    val = OS_REG_READ(ah, AR_WOW_BCN_EN_REG);
1207250003Sadrian    if (pattern_enable & AH_WOW_BEACON_MISS) {
1208250003Sadrian        val |= AR_WOW_BEACON_FAIL_EN;
1209250003Sadrian        wow_event_mask |= AR_WOW_BEACON_FAIL;
1210250003Sadrian    } else {
1211250003Sadrian        val &= ~AR_WOW_BEACON_FAIL_EN;
1212250003Sadrian    }
1213250003Sadrian    OS_REG_WRITE(ah, AR_WOW_BCN_EN_REG, val);
1214250003Sadrian    val = OS_REG_READ(ah, AR_WOW_BCN_EN_REG);
1215250003Sadrian
1216250003Sadrian    /*
1217250003Sadrian     * Enable the magic packet registers.
1218250003Sadrian     */
1219250003Sadrian    val = OS_REG_READ(ah, AR_WOW_PATTERN_REG);
1220250003Sadrian    if ((pattern_enable & AH_WOW_MAGIC_PATTERN_EN)
1221250003Sadrian#if ATH_WOW_OFFLOAD
1222250003Sadrian        || (pattern_enable & AH_WOW_ACER_MAGIC_EN)
1223250003Sadrian#endif
1224250003Sadrian        )
1225250003Sadrian    {
1226250003Sadrian        val |= AR_WOW_MAGIC_EN;
1227250003Sadrian        wow_event_mask |= AR_WOW_MAGIC_PAT_FOUND;
1228250003Sadrian    } else {
1229250003Sadrian        val &= ~AR_WOW_MAGIC_EN;
1230250003Sadrian    }
1231250003Sadrian    val |= AR_WOW_MAC_INTR_EN;
1232250003Sadrian    OS_REG_WRITE(ah, AR_WOW_PATTERN_REG, val);
1233250003Sadrian    val = OS_REG_READ(ah, AR_WOW_PATTERN_REG);
1234250003Sadrian
1235250003Sadrian#if ATH_WOW_OFFLOAD
1236250003Sadrian    if (HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_FORCE_BT_SLEEP)) {
1237250003Sadrian        wow_feature_enable |= AR_WOW_OFFLOAD_ENA_BT_SLEEP;
1238250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - BT SLEEP\n");
1239250003Sadrian    } else {
1240250003Sadrian        wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_BT_SLEEP;
1241250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - BT SLEEP\n");
1242250003Sadrian    }
1243250003Sadrian
1244250003Sadrian    if (HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_SW_NULL_DISABLE)) {
1245250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - SW NULL\n");
1246250003Sadrian        wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_SW_NULL;
1247250003Sadrian    } else {
1248250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - SW NULL\n");
1249250003Sadrian        wow_feature_enable |= AR_WOW_OFFLOAD_ENA_SW_NULL;
1250250003Sadrian    }
1251250003Sadrian
1252250003Sadrian    if (HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_DEVID_SWAR_DISABLE)) {
1253250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - DevID SWAR\n");
1254250003Sadrian        wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_DEVID_SWAR;
1255250003Sadrian    } else {
1256250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - DevID SWAR\n");
1257250003Sadrian        wow_feature_enable |= AR_WOW_OFFLOAD_ENA_DEVID_SWAR;
1258250003Sadrian    }
1259250003Sadrian
1260250003Sadrian    if (pattern_enable & AH_WOW_ACER_KEEP_ALIVE_EN) {
1261250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - Acer SWKA\n");
1262250003Sadrian        wow_feature_enable |= AR_WOW_OFFLOAD_ENA_SWKA;
1263250003Sadrian    } else {
1264250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - Acer SWKA\n");
1265250003Sadrian        wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_SWKA;
1266250003Sadrian    }
1267250003Sadrian
1268250003Sadrian    if (pattern_enable & AH_WOW_ACER_MAGIC_EN) {
1269250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - Standard Magic\n");
1270250003Sadrian        wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_STD_MAGIC;
1271250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - Acer Magic\n");
1272250003Sadrian        wow_feature_enable |= AR_WOW_OFFLOAD_ENA_ACER_MAGIC;
1273250003Sadrian    } else {
1274250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - Standard Magic\n");
1275250003Sadrian        wow_feature_enable |= AR_WOW_OFFLOAD_ENA_STD_MAGIC;
1276250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - Acer Magic\n");
1277250003Sadrian        wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_ACER_MAGIC;
1278250003Sadrian    }
1279250003Sadrian
1280250003Sadrian    if ((pattern_enable & AH_WOW_4WAY_HANDSHAKE_EN) ||
1281250003Sadrian        HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_FORCE_4WAY_HS_WAKE)) {
1282250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - 4Way Handshake\n");
1283250003Sadrian        wow_feature_enable |= AR_WOW_OFFLOAD_ENA_4WAY_WAKE;
1284250003Sadrian    } else {
1285250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - 4Way Handshake\n");
1286250003Sadrian        wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_4WAY_WAKE;
1287250003Sadrian    }
1288250003Sadrian
1289250003Sadrian    if((pattern_enable & AH_WOW_AP_ASSOCIATION_LOST_EN) ||
1290250003Sadrian        HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_FORCE_AP_LOSS_WAKE))
1291250003Sadrian    {
1292250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - AP loss wake\n");
1293250003Sadrian        wow_feature_enable |= AR_WOW_OFFLOAD_ENA_AP_LOSS_WAKE;
1294250003Sadrian    } else {
1295250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - AP loss wake\n");
1296250003Sadrian        wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_AP_LOSS_WAKE;
1297250003Sadrian    }
1298250003Sadrian
1299250003Sadrian    if((pattern_enable & AH_WOW_GTK_HANDSHAKE_ERROR_EN) ||
1300250003Sadrian        HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_FORCE_GTK_ERR_WAKE))
1301250003Sadrian    {
1302250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - GTK error wake\n");
1303250003Sadrian        wow_feature_enable |= AR_WOW_OFFLOAD_ENA_GTK_ERROR_WAKE;
1304250003Sadrian    } else {
1305250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - GTK error wake\n");
1306250003Sadrian        wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_GTK_ERROR_WAKE;
1307250003Sadrian    }
1308250003Sadrian
1309250003Sadrian    if (pattern_enable & AH_WOW_GTK_OFFLOAD_EN) {
1310250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - GTK offload\n");
1311250003Sadrian        wow_feature_enable |= AR_WOW_OFFLOAD_ENA_GTK;
1312250003Sadrian    } else {
1313250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - GTK offload\n");
1314250003Sadrian        wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_GTK;
1315250003Sadrian    }
1316250003Sadrian
1317250003Sadrian    if (pattern_enable & AH_WOW_ARP_OFFLOAD_EN) {
1318250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - ARP offload\n");
1319250003Sadrian        wow_feature_enable |= AR_WOW_OFFLOAD_ENA_ARP_OFFLOAD;
1320250003Sadrian    } else {
1321250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - ARP offload\n");
1322250003Sadrian        wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_ARP_OFFLOAD;
1323250003Sadrian    }
1324250003Sadrian
1325250003Sadrian    if (pattern_enable & AH_WOW_NS_OFFLOAD_EN) {
1326250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - NS offload\n");
1327250003Sadrian        wow_feature_enable |= AR_WOW_OFFLOAD_ENA_NS_OFFLOAD;
1328250003Sadrian    } else {
1329250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - NS offload\n");
1330250003Sadrian        wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_NS_OFFLOAD;
1331250003Sadrian    }
1332250003Sadrian
1333250003Sadrian#endif /* ATH_WOW_OFFLOAD */
1334250003Sadrian
1335250003Sadrian    /* For Kite and later version of the chips
1336250003Sadrian     * enable wow pattern match for packets less than
1337250003Sadrian     * 256 bytes for all patterns.
1338250003Sadrian     */
1339250003Sadrian    /* XXX */
1340250003Sadrian    OS_REG_WRITE(
1341250003Sadrian        ah, AR_WOW_PATTERN_MATCH_LT_256B_REG, AR_WOW_PATTERN_SUPPORTED);
1342250003Sadrian
1343250003Sadrian    /*
1344250003Sadrian     * Set the power states appropriately and enable PME.
1345250003Sadrian     */
1346250003Sadrian    val = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL));
1347250003Sadrian    val |=
1348250003Sadrian        AR_PMCTRL_PWR_STATE_D1D3 |
1349250003Sadrian        AR_PMCTRL_HOST_PME_EN    |
1350250003Sadrian        AR_PMCTRL_PWR_PM_CTRL_ENA;
1351250003Sadrian    val &= ~AR_PCIE_PM_CTRL_ENA;
1352250003Sadrian    OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL), val);
1353250003Sadrian
1354250003Sadrian    /* Wake on Timer Interrupt. Test mode only. Used in Manufacturing line. */
1355250003Sadrian    if (timeout_in_seconds) {
1356250003Sadrian        /* convert Timeout to u_secs */
1357250003Sadrian        OS_REG_WRITE(ah, AR_NEXT_NDP_TIMER,
1358250003Sadrian            OS_REG_READ(ah, AR_TSF_L32) + timeout_in_seconds * 1000000 );
1359250003Sadrian        /* timer_period = 30 seconds always */
1360250003Sadrian        OS_REG_WRITE(ah, AR_NDP_PERIOD, 30 * 1000000);
1361250003Sadrian        OS_REG_WRITE(ah, AR_TIMER_MODE, OS_REG_READ(ah, AR_TIMER_MODE) | 0x80);
1362250003Sadrian        OS_REG_WRITE(ah, AR_IMR_S5, OS_REG_READ(ah, AR_IMR_S5) | 0x80);
1363250003Sadrian        OS_REG_WRITE(ah, AR_IMR, OS_REG_READ(ah, AR_IMR) | AR_IMR_GENTMR);
1364250003Sadrian        if (clearbssid) {
1365250003Sadrian            OS_REG_WRITE(ah, AR_BSS_ID0, 0);
1366250003Sadrian            OS_REG_WRITE(ah, AR_BSS_ID1, 0);
1367250003Sadrian        }
1368250003Sadrian    }
1369250003Sadrian
1370250003Sadrian    /* Enable Seq# generation when asleep. */
1371250003Sadrian    OS_REG_WRITE(ah, AR_STA_ID1,
1372250003Sadrian                     OS_REG_READ(ah, AR_STA_ID1) & ~AR_STA_ID1_PRESERVE_SEQNUM);
1373250003Sadrian
1374250003Sadrian    AH_PRIVATE(ah)->ah_wow_event_mask = wow_event_mask;
1375250003Sadrian
1376250003Sadrian#if ATH_WOW_OFFLOAD
1377250003Sadrian    if (offloadEnable) {
1378250003Sadrian        /* Force MAC awake before entering SW WoW mode */
1379250003Sadrian        OS_REG_SET_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN);
1380250003Sadrian#if ATH_SUPPORT_MCI
1381250008Sadrian        if (AH_PRIVATE(ah)->ah_caps.halMciSupport) {
1382250003Sadrian            OS_REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
1383250003Sadrian        }
1384250003Sadrian#endif
1385250003Sadrian
1386250003Sadrian        OS_REG_WRITE(ah, AR_WOW_OFFLOAD_COMMAND_JUPITER, wow_feature_enable);
1387250003Sadrian        OS_REG_WRITE(ah, AR_WOW_OFFLOAD_STATUS_JUPITER, 0x0);
1388250003Sadrian        if (wow_feature_enable & AR_WOW_OFFLOAD_ENA_SW_NULL) {
1389250003Sadrian            OS_REG_WRITE(ah, AR_WOW_SW_NULL_PARAMETER,
1390250003Sadrian                ((1000) |
1391250003Sadrian                (4 << AR_WOW_SW_NULL_SHORT_PERIOD_MASK_S)));
1392250003Sadrian        }
1393250003Sadrian
1394250003Sadrian        if (wow_feature_enable & AR_WOW_OFFLOAD_ENA_DEVID_SWAR) {
1395250003Sadrian            ar9300_wowoffload_download_devid_swar(ah);
1396250003Sadrian        }
1397250003Sadrian
1398250003Sadrian        ar9300_wow_offload_download_hal_params(ah);
1399250003Sadrian        ar9300_wow_offload_handshake(ah, pattern_enable);
1400250003Sadrian        AH9300(ah)->ah_chip_full_sleep = AH_FALSE;
1401250003Sadrian
1402250003Sadrian        //OS_REG_SET_BIT(ah, AR_SW_WOW_CONTROL, AR_HW_WOW_DISABLE);
1403250003Sadrian    }
1404250003Sadrian    else
1405250003Sadrian#endif /* ATH_WOW_OFFLOAD */
1406250003Sadrian    {
1407250003Sadrian#if ATH_SUPPORT_MCI
1408250008Sadrian        if (AH_PRIVATE(ah)->ah_caps.halMciSupport) {
1409250003Sadrian            OS_REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
1410250003Sadrian        }
1411250003Sadrian#endif
1412250003Sadrian        ar9300_set_power_mode_wow_sleep(ah);
1413250003Sadrian        AH9300(ah)->ah_chip_full_sleep = AH_TRUE;
1414250003Sadrian    }
1415250003Sadrian
1416250003Sadrian    return (AH_TRUE);
1417250003Sadrian}
1418250003Sadrian
1419250003Sadrianu_int32_t
1420250003Sadrian//ar9300_wow_wake_up(struct ath_hal *ah, u_int8_t  *chipPatternBytes)
1421250003Sadrianar9300_wow_wake_up(struct ath_hal *ah, HAL_BOOL offloadEnabled)
1422250003Sadrian{
1423250003Sadrian    uint32_t wow_status = 0;
1424250003Sadrian    uint32_t val = 0, rval;
1425250003Sadrian
1426250003Sadrian    OS_REG_CLR_BIT(ah, AR_SW_WOW_CONTROL, AR_HW_WOW_DISABLE);
1427250003Sadrian    OS_REG_CLR_BIT(ah, AR_SW_WOW_CONTROL, AR_SW_WOW_ENABLE);
1428250003Sadrian
1429250003Sadrian#if ATH_WOW_OFFLOAD
1430250003Sadrian    /* If WoW was offloaded to embedded CPU, use the global
1431250003Sadrian     * shared register to know the wakeup reason */
1432250003Sadrian    if (offloadEnabled) {
1433250003Sadrian        val = OS_REG_READ(ah, AR_EMB_CPU_WOW_STATUS);
1434250003Sadrian        if (val) {
1435250003Sadrian            if (val & AR_EMB_CPU_WOW_STATUS_MAGIC_PATTERN) {
1436250003Sadrian                HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) SW MAGIC_PATTERN\n");
1437250003Sadrian                wow_status |= AH_WOW_MAGIC_PATTERN_EN;
1438250003Sadrian            }
1439250003Sadrian            if (val & AR_EMB_CPU_WOW_STATUS_PATTERN_MATCH) {
1440250003Sadrian                HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) SW USER_PATTERN\n");
1441250003Sadrian                wow_status |= AH_WOW_USER_PATTERN_EN;
1442250003Sadrian            }
1443250003Sadrian            if (val & AR_EMB_CPU_WOW_STATUS_KEEP_ALIVE_FAIL) {
1444250003Sadrian                HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) SW KEEP_ALIVE_FAIL\n");
1445250003Sadrian                wow_status |= AH_WOW_LINK_CHANGE;
1446250003Sadrian            }
1447250003Sadrian            if (val & AR_EMB_CPU_WOW_STATUS_BEACON_MISS) {
1448250003Sadrian                HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) SW BEACON_FAIL\n");
1449250003Sadrian                wow_status |= AH_WOW_BEACON_MISS;
1450250003Sadrian            }
1451250003Sadrian        }
1452250003Sadrian
1453250003Sadrian        /* Clear status and mask registers */
1454250003Sadrian        OS_REG_WRITE(ah, AR_EMB_CPU_WOW_STATUS, 0x0);
1455250003Sadrian        OS_REG_WRITE(ah, AR_EMB_CPU_WOW_ENABLE, 0);
1456250003Sadrian        OS_REG_WRITE(ah, AR_MBOX_CTRL_STATUS, 0);
1457250003Sadrian
1458250003Sadrian    }
1459250003Sadrian    else
1460250003Sadrian#endif /* ATH_WOW_OFFLOAD */
1461250003Sadrian    {
1462250003Sadrian        /*
1463250003Sadrian         * Read the WOW Status register to know the wakeup reason.
1464250003Sadrian         */
1465250003Sadrian        rval = OS_REG_READ(ah, AR_WOW_PATTERN_REG);
1466250003Sadrian        val = AR_WOW_STATUS(rval);
1467250003Sadrian
1468250003Sadrian        /*
1469250003Sadrian         * Mask only the WOW events that we have enabled.
1470250003Sadrian         * Sometimes we have spurious WOW events from the AR_WOW_PATTERN_REG
1471250003Sadrian         * register. This mask will clean it up.
1472250003Sadrian         */
1473250003Sadrian        val &= AH_PRIVATE(ah)->ah_wow_event_mask;
1474250003Sadrian
1475250003Sadrian        if (val) {
1476250003Sadrian            if (val & AR_WOW_MAGIC_PAT_FOUND) {
1477250003Sadrian                HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) HW MAGIC_PATTERN\n");
1478250003Sadrian                wow_status |= AH_WOW_MAGIC_PATTERN_EN;
1479250003Sadrian            }
1480250003Sadrian            if (AR_WOW_PATTERN_FOUND(val)) {
1481250003Sadrian                //int  i, offset;
1482250003Sadrian                //offset = OS_REG_READ(ah, AR_WOW_RXBUF_START_ADDR);
1483250003Sadrian                //// Read matched pattern for wake packet detection indication.
1484250003Sadrian                //for( i = 0; i< MAX_PATTERN_SIZE/4; i+=4)
1485250003Sadrian                //{
1486250003Sadrian                //    // RX FIFO is only 8K wrapping.
1487250003Sadrian                //    if(offset >= 8 * 1024 / 4) offset = 0;
1488250003Sadrian                //    *(u_int32_t*)(chipPatternBytes + i) = OS_REG_READ( ah,offset );
1489250003Sadrian                //    offset++;
1490250003Sadrian                //}
1491250003Sadrian                wow_status |= AH_WOW_USER_PATTERN_EN;
1492250003Sadrian                HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) HW USER_PATTERN\n");
1493250003Sadrian            }
1494250003Sadrian            if (val & AR_WOW_KEEP_ALIVE_FAIL) {
1495250003Sadrian                HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) HW KEEP_ALIVE_FAIL\n");
1496250003Sadrian                wow_status |= AH_WOW_LINK_CHANGE;
1497250003Sadrian            }
1498250003Sadrian            if (val & AR_WOW_BEACON_FAIL) {
1499250003Sadrian                HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) HW BEACON_FAIL\n");
1500250003Sadrian                wow_status |= AH_WOW_BEACON_MISS;
1501250003Sadrian            }
1502250003Sadrian        }
1503250003Sadrian    }
1504250003Sadrian
1505250003Sadrian    /*
1506250003Sadrian     * Set and clear WOW_PME_CLEAR registers for the chip to generate next
1507250003Sadrian     * wow signal.
1508250003Sadrian     * Disable D3 before accessing other registers ?
1509250003Sadrian     */
1510250003Sadrian    val = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL));
1511250003Sadrian    /* Check the bit value 0x01000000 (7-10)? */
1512250003Sadrian    val &= ~AR_PMCTRL_PWR_STATE_D1D3;
1513250003Sadrian    val |= AR_PMCTRL_WOW_PME_CLR;
1514250003Sadrian    OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL), val);
1515250003Sadrian
1516250003Sadrian    /*
1517250003Sadrian     * Clear all events.
1518250003Sadrian     */
1519250003Sadrian    OS_REG_WRITE(ah, AR_WOW_PATTERN_REG,
1520250003Sadrian        AR_WOW_CLEAR_EVENTS(OS_REG_READ(ah, AR_WOW_PATTERN_REG)));
1521250003Sadrian
1522250003Sadrian    //HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE,
1523250003Sadrian    //    "%s: Skip PCIE WA programming\n", __func__);
1524250003Sadrian#if 0
1525250003Sadrian    /*
1526250003Sadrian     * Tie reset register.
1527250003Sadrian     * FIXME: Per David Quan not tieing it back might have some repurcussions.
1528250003Sadrian     */
1529250003Sadrian    /* XXX */
1530250003Sadrian    OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA), OS_REG_READ(ah, AR_WA) |
1531250003Sadrian            AR_WA_UNTIE_RESET_EN | AR_WA_POR_SHORT | AR_WA_RESET_EN);
1532250003Sadrian#endif
1533250003Sadrian
1534250003Sadrian    /* Restore the Beacon Threshold to init value */
1535250003Sadrian    OS_REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_THR_BM_THR, INIT_RSSI_THR);
1536250003Sadrian
1537250003Sadrian    /*
1538250003Sadrian     * Restore the way the PCI-E Reset, Power-On-Reset, external PCIE_POR_SHORT
1539250003Sadrian     * pins are tied to its original value. Previously just before WOW sleep,
1540250003Sadrian     * we untie the PCI-E Reset to our Chip's Power On Reset so that
1541250003Sadrian     * any PCI-E reset from the bus will not reset our chip.
1542250003Sadrian     */
1543250003Sadrian    HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE, "%s: restore AR_WA\n", __func__);
1544250003Sadrian    if (AH_PRIVATE(ah)->ah_is_pci_express == AH_TRUE) {
1545250003Sadrian        ar9300_config_pci_power_save(ah, 0, 0);
1546250003Sadrian    }
1547250003Sadrian
1548250003Sadrian    AH_PRIVATE(ah)->ah_wow_event_mask = 0;
1549250003Sadrian    HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE,
1550250003Sadrian        "(WOW) wow_status=%08x\n", wow_status);
1551250003Sadrian
1552250003Sadrian    return (wow_status);
1553250003Sadrian}
1554250003Sadrian
1555250003Sadrianvoid
1556250003Sadrianar9300_wow_set_gpio_reset_low(struct ath_hal *ah)
1557250003Sadrian{
1558250003Sadrian    uint32_t val;
1559250003Sadrian
1560250003Sadrian    val = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_GPIO_OE_OUT));
1561250003Sadrian    val |= (1 << (2 * 2));
1562250003Sadrian    OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_GPIO_OE_OUT), val);
1563250003Sadrian    val = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_GPIO_OE_OUT));
1564250003Sadrian    /* val = OS_REG_READ(ah,AR_GPIO_IN_OUT ); */
1565250003Sadrian}
1566250003Sadrian#endif /* ATH_WOW */
1567