xref: /freebsd/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_power.c (revision 98e0ffaefb0f241cda3a72395d3be04192ae0d47)
176bd547bSAdrian Chadd /*
276bd547bSAdrian Chadd  * Copyright (c) 2013 Qualcomm Atheros, Inc.
376bd547bSAdrian Chadd  *
476bd547bSAdrian Chadd  * Permission to use, copy, modify, and/or distribute this software for any
576bd547bSAdrian Chadd  * purpose with or without fee is hereby granted, provided that the above
676bd547bSAdrian Chadd  * copyright notice and this permission notice appear in all copies.
776bd547bSAdrian Chadd  *
876bd547bSAdrian Chadd  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
976bd547bSAdrian Chadd  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
1076bd547bSAdrian Chadd  * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
1176bd547bSAdrian Chadd  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
1276bd547bSAdrian Chadd  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
1376bd547bSAdrian Chadd  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
1476bd547bSAdrian Chadd  * PERFORMANCE OF THIS SOFTWARE.
1576bd547bSAdrian Chadd  */
1676bd547bSAdrian Chadd 
1776bd547bSAdrian Chadd #include "opt_ah.h"
1876bd547bSAdrian Chadd 
1976bd547bSAdrian Chadd #include "ah.h"
2076bd547bSAdrian Chadd #include "ah_internal.h"
2176bd547bSAdrian Chadd 
2276bd547bSAdrian Chadd #include "ar9300/ar9300.h"
2376bd547bSAdrian Chadd #include "ar9300/ar9300reg.h"
2476bd547bSAdrian Chadd 
2576bd547bSAdrian Chadd #if ATH_WOW_OFFLOAD
ar9300_wowoffload_prep(struct ath_hal * ah)2676bd547bSAdrian Chadd void ar9300_wowoffload_prep(struct ath_hal *ah)
2776bd547bSAdrian Chadd {
2876bd547bSAdrian Chadd     struct ath_hal_9300 *ahp = AH9300(ah);
2976bd547bSAdrian Chadd 
3076bd547bSAdrian Chadd     ahp->ah_mcast_filter_l32_set = 0;
3176bd547bSAdrian Chadd     ahp->ah_mcast_filter_u32_set = 0;
3276bd547bSAdrian Chadd }
3376bd547bSAdrian Chadd 
ar9300_wowoffload_post(struct ath_hal * ah)3476bd547bSAdrian Chadd void ar9300_wowoffload_post(struct ath_hal *ah)
3576bd547bSAdrian Chadd {
3676bd547bSAdrian Chadd     struct ath_hal_9300 *ahp = AH9300(ah);
3776bd547bSAdrian Chadd     u_int32_t val;
3876bd547bSAdrian Chadd 
3976bd547bSAdrian Chadd     if (ahp->ah_mcast_filter_l32_set != 0) {
4076bd547bSAdrian Chadd         val = OS_REG_READ(ah, AR_MCAST_FIL0);
4176bd547bSAdrian Chadd         val &= ~ahp->ah_mcast_filter_l32_set;
4276bd547bSAdrian Chadd         OS_REG_WRITE(ah, AR_MCAST_FIL0, val);
4376bd547bSAdrian Chadd     }
4476bd547bSAdrian Chadd     if (ahp->ah_mcast_filter_u32_set != 0) {
4576bd547bSAdrian Chadd         val = OS_REG_READ(ah, AR_MCAST_FIL1);
4676bd547bSAdrian Chadd         val &= ~ahp->ah_mcast_filter_u32_set;
4776bd547bSAdrian Chadd         OS_REG_WRITE(ah, AR_MCAST_FIL1, val);
4876bd547bSAdrian Chadd     }
4976bd547bSAdrian Chadd 
5076bd547bSAdrian Chadd     ahp->ah_mcast_filter_l32_set = 0;
5176bd547bSAdrian Chadd     ahp->ah_mcast_filter_u32_set = 0;
5276bd547bSAdrian Chadd }
5376bd547bSAdrian Chadd 
ar9300_wowoffload_add_mcast_filter(struct ath_hal * ah,u_int8_t * mc_addr)5476bd547bSAdrian Chadd static void ar9300_wowoffload_add_mcast_filter(struct ath_hal *ah, u_int8_t *mc_addr)
5576bd547bSAdrian Chadd {
5676bd547bSAdrian Chadd     struct ath_hal_9300 *ahp = AH9300(ah);
5776bd547bSAdrian Chadd     u_int32_t reg, val;
5876bd547bSAdrian Chadd     u_int8_t  pos, high32;
5976bd547bSAdrian Chadd 
6076bd547bSAdrian Chadd     memcpy((u_int8_t *) &val, &mc_addr[0], 3);
6176bd547bSAdrian Chadd     pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
6276bd547bSAdrian Chadd     memcpy((u_int8_t *) &val, &mc_addr[3], 3);
6376bd547bSAdrian Chadd     pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
6476bd547bSAdrian Chadd     high32 = pos & 0x20;
6576bd547bSAdrian Chadd     reg = high32 ? AR_MCAST_FIL1 : AR_MCAST_FIL0;
6676bd547bSAdrian Chadd     pos &= 0x1F;
6776bd547bSAdrian Chadd 
6876bd547bSAdrian Chadd     val = OS_REG_READ(ah, reg);
6976bd547bSAdrian Chadd     if ((val & (1 << pos)) == 0) {
7076bd547bSAdrian Chadd         val |= (1 << pos);
7176bd547bSAdrian Chadd         if (high32) {
7276bd547bSAdrian Chadd             ahp->ah_mcast_filter_u32_set |= (1 << pos);
7376bd547bSAdrian Chadd         } else {
7476bd547bSAdrian Chadd             ahp->ah_mcast_filter_l32_set |= (1 << pos);
7576bd547bSAdrian Chadd         }
7676bd547bSAdrian Chadd         OS_REG_WRITE(ah, reg, val);
7776bd547bSAdrian Chadd     }
7876bd547bSAdrian Chadd }
7976bd547bSAdrian Chadd 
8076bd547bSAdrian Chadd /*
8176bd547bSAdrian Chadd  * DeviceID SWAR - EV91928
8276bd547bSAdrian Chadd  *
8376bd547bSAdrian Chadd  * During SW WOW, 0x4004[13] is set to allow BT eCPU to access WLAN MAC
8476bd547bSAdrian Chadd  * registers. Setting 00x4004[13] will prevent eeprom state machine to
8576bd547bSAdrian Chadd  * load customizable PCIE configuration registers, which lead to the PCIE
8676bd547bSAdrian Chadd  * device id stay as default 0xABCD. The SWAR to have BT eCPU to write
8776bd547bSAdrian Chadd  * to PCIE registers as soon as it detects PCIE reset is deasserted.
8876bd547bSAdrian Chadd  */
ar9300_wowoffload_download_devid_swar(struct ath_hal * ah)8976bd547bSAdrian Chadd void ar9300_wowoffload_download_devid_swar(struct ath_hal *ah)
9076bd547bSAdrian Chadd {
9176bd547bSAdrian Chadd     u_int32_t addr = AR_WOW_OFFLOAD_WLAN_REGSET_NUM;
9276bd547bSAdrian Chadd 
9376bd547bSAdrian Chadd     OS_REG_WRITE(ah, addr, 8);
9476bd547bSAdrian Chadd     addr += 4;
9576bd547bSAdrian Chadd     OS_REG_WRITE(ah, addr, 0x5000);
9676bd547bSAdrian Chadd     addr += 4;
9776bd547bSAdrian Chadd     HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) pcie_000 = %08x\n",
9876bd547bSAdrian Chadd              AH_PRIVATE(ah)->ah_config.ath_hal_pcie_000);
9976bd547bSAdrian Chadd     OS_REG_WRITE(ah, addr, AH_PRIVATE(ah)->ah_config.ath_hal_pcie_000);
10076bd547bSAdrian Chadd     addr += 4;
10176bd547bSAdrian Chadd     OS_REG_WRITE(ah, addr, 0x5008);
10276bd547bSAdrian Chadd     addr += 4;
10376bd547bSAdrian Chadd     HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) pcie_008 = %08x\n",
10476bd547bSAdrian Chadd              AH_PRIVATE(ah)->ah_config.ath_hal_pcie_008);
10576bd547bSAdrian Chadd     OS_REG_WRITE(ah, addr, AH_PRIVATE(ah)->ah_config.ath_hal_pcie_008);
10676bd547bSAdrian Chadd     addr += 4;
10776bd547bSAdrian Chadd     OS_REG_WRITE(ah, addr, 0x502c);
10876bd547bSAdrian Chadd     addr += 4;
10976bd547bSAdrian Chadd     HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) pcie_02c = %08x\n",
11076bd547bSAdrian Chadd              AH_PRIVATE(ah)->ah_config.ath_hal_pcie_02c);
11176bd547bSAdrian Chadd     OS_REG_WRITE(ah, addr, AH_PRIVATE(ah)->ah_config.ath_hal_pcie_02c);
11276bd547bSAdrian Chadd     addr += 4;
11376bd547bSAdrian Chadd     OS_REG_WRITE(ah, addr, 0x18c00);
11476bd547bSAdrian Chadd     addr += 4;
11576bd547bSAdrian Chadd     OS_REG_WRITE(ah, addr, 0x18212ede);
11676bd547bSAdrian Chadd     addr += 4;
11776bd547bSAdrian Chadd     OS_REG_WRITE(ah, addr, 0x18c04);
11876bd547bSAdrian Chadd     addr += 4;
11976bd547bSAdrian Chadd     OS_REG_WRITE(ah, addr, 0x008001d8);
12076bd547bSAdrian Chadd     addr += 4;
12176bd547bSAdrian Chadd     OS_REG_WRITE(ah, addr, 0x18c08);
12276bd547bSAdrian Chadd     addr += 4;
12376bd547bSAdrian Chadd     OS_REG_WRITE(ah, addr, 0x0003580c);
12476bd547bSAdrian Chadd     addr += 4;
12576bd547bSAdrian Chadd     OS_REG_WRITE(ah, addr, 0x570c);
12676bd547bSAdrian Chadd     addr += 4;
12776bd547bSAdrian Chadd     HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) pcie_70c = %08x\n",
12876bd547bSAdrian Chadd              AH_PRIVATE(ah)->ah_config.ath_hal_pcie_70c);
12976bd547bSAdrian Chadd     OS_REG_WRITE(ah, addr, AH_PRIVATE(ah)->ah_config.ath_hal_pcie_70c);
13076bd547bSAdrian Chadd     addr += 4;
13176bd547bSAdrian Chadd     OS_REG_WRITE(ah, addr, 0x5040);
13276bd547bSAdrian Chadd     addr += 4;
13376bd547bSAdrian Chadd     HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) pcie_040 = %08x\n",
13476bd547bSAdrian Chadd              AH_PRIVATE(ah)->ah_config.ath_hal_pcie_040);
13576bd547bSAdrian Chadd     OS_REG_WRITE(ah, addr, AH_PRIVATE(ah)->ah_config.ath_hal_pcie_040);
13676bd547bSAdrian Chadd     addr += 4;
13776bd547bSAdrian Chadd /*
13876bd547bSAdrian Chadd     A_SOC_REG_WRITE(0x45000, 0x0034168c);
13976bd547bSAdrian Chadd     A_SOC_REG_WRITE(0x45008, 0x02800001);
14076bd547bSAdrian Chadd     A_SOC_REG_WRITE(0x4502c, 0x3117168c);
14176bd547bSAdrian Chadd     A_SOC_REG_WRITE(0x58c00, 0x18212ede);
14276bd547bSAdrian Chadd     A_SOC_REG_WRITE(0x58c04, 0x000801d8);
14376bd547bSAdrian Chadd     A_SOC_REG_WRITE(0x58c08, 0x0003580c);
14476bd547bSAdrian Chadd     A_SOC_REG_WRITE(0x4570c, 0x275f3f01);
14576bd547bSAdrian Chadd     A_SOC_REG_WRITE(0x45040, 0xffc25001);
14676bd547bSAdrian Chadd */
14776bd547bSAdrian Chadd }
14876bd547bSAdrian Chadd 
14976bd547bSAdrian Chadd /* Retrieve updated information from MAC PCU buffer.
15076bd547bSAdrian Chadd  * Embedded CPU would have written the value before exiting WoW
15176bd547bSAdrian Chadd  * */
ar9300_wowoffload_retrieve_data(struct ath_hal * ah,void * buf,u_int32_t param)15276bd547bSAdrian Chadd void ar9300_wowoffload_retrieve_data(struct ath_hal *ah, void *buf, u_int32_t param)
15376bd547bSAdrian Chadd {
15476bd547bSAdrian Chadd     u_int32_t rc_lower, rc_upper;
15576bd547bSAdrian Chadd 
15676bd547bSAdrian Chadd     if (param == WOW_PARAM_REPLAY_CNTR) {
15776bd547bSAdrian Chadd         rc_lower = OS_REG_READ(ah, AR_WOW_TXBUF(0));
15876bd547bSAdrian Chadd         rc_upper = OS_REG_READ(ah, AR_WOW_TXBUF(1));
15976bd547bSAdrian Chadd         *(u_int64_t *)buf = rc_lower + (rc_upper << 32);
16076bd547bSAdrian Chadd     }
16176bd547bSAdrian Chadd     else if (param == WOW_PARAM_KEY_TSC) {
16276bd547bSAdrian Chadd         rc_lower = OS_REG_READ(ah, AR_WOW_TXBUF(2));
16376bd547bSAdrian Chadd         rc_upper = OS_REG_READ(ah, AR_WOW_TXBUF(3));
16476bd547bSAdrian Chadd         *(u_int64_t *)buf = rc_lower + (rc_upper << 32);
16576bd547bSAdrian Chadd     }
16676bd547bSAdrian Chadd     else if (param == WOW_PARAM_TX_SEQNUM) {
16776bd547bSAdrian Chadd         *(u_int32_t *)buf = OS_REG_READ(ah, AR_WOW_TXBUF(4));
16876bd547bSAdrian Chadd     }
16976bd547bSAdrian Chadd 
17076bd547bSAdrian Chadd }
17176bd547bSAdrian Chadd 
17276bd547bSAdrian Chadd /* 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)17376bd547bSAdrian Chadd u_int32_t ar9300_wowoffload_download_rekey_data(struct ath_hal *ah, u_int32_t *data, u_int32_t bytes)
17476bd547bSAdrian Chadd {
17576bd547bSAdrian Chadd     int i;
17676bd547bSAdrian Chadd     int mbox_status = OS_REG_READ(ah, AR_MBOX_CTRL_STATUS);
17776bd547bSAdrian Chadd     u_int32_t gtk_data_start;
17876bd547bSAdrian Chadd 
17976bd547bSAdrian Chadd     HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) %s, bytes=%d\n", __func__, bytes);
18076bd547bSAdrian Chadd     if (AR_SREV_JUPITER(ah) &&
18176bd547bSAdrian Chadd         (bytes > (AR_WOW_OFFLOAD_GTK_DATA_WORDS_JUPITER * 4)))
18276bd547bSAdrian Chadd     {
18376bd547bSAdrian Chadd         bytes = AR_WOW_OFFLOAD_GTK_DATA_WORDS_JUPITER * 4;
18476bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) bytes truncated to %d\n", bytes);
18576bd547bSAdrian Chadd     }
18676bd547bSAdrian Chadd     /* Check if mailbox is busy */
18776bd547bSAdrian Chadd     if (mbox_status != 0) {
18876bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: Mailbox register busy! Reg = 0x%x", __func__, mbox_status);
18976bd547bSAdrian Chadd         return 1;
19076bd547bSAdrian Chadd     }
19176bd547bSAdrian Chadd 
19276bd547bSAdrian Chadd     /* Clear status */
19376bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_EMB_CPU_WOW_STATUS, 0x0);
19476bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_WLAN_WOW_ENABLE, 0);
19576bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_WLAN_WOW_STATUS, 0xFFFFFFFF);
19676bd547bSAdrian Chadd 
19776bd547bSAdrian Chadd     if (AR_SREV_JUPITER(ah)) {
19876bd547bSAdrian Chadd         gtk_data_start = AR_WOW_OFFLOAD_GTK_DATA_START_JUPITER;
19976bd547bSAdrian Chadd     } else {
20076bd547bSAdrian Chadd         gtk_data_start = AR_WOW_OFFLOAD_GTK_DATA_START;
20176bd547bSAdrian Chadd     }
20276bd547bSAdrian Chadd     for (i = 0;i < bytes/4; i++) {
20376bd547bSAdrian Chadd         OS_REG_WRITE(ah, gtk_data_start + i * 4, data[i]);
20476bd547bSAdrian Chadd     }
20576bd547bSAdrian Chadd 
20676bd547bSAdrian Chadd     return 0;
20776bd547bSAdrian Chadd }
20876bd547bSAdrian Chadd 
ar9300_wowoffload_download_acer_magic(struct ath_hal * ah,HAL_BOOL valid,u_int8_t * datap,u_int32_t bytes)20976bd547bSAdrian Chadd void ar9300_wowoffload_download_acer_magic( struct ath_hal *ah,
21076bd547bSAdrian Chadd                                             HAL_BOOL      valid,
21176bd547bSAdrian Chadd                                             u_int8_t* datap,
21276bd547bSAdrian Chadd                                             u_int32_t bytes)
21376bd547bSAdrian Chadd {
21476bd547bSAdrian Chadd     u_int32_t *p32 = (u_int32_t *) datap;
21576bd547bSAdrian Chadd     u_int32_t l = 0, u = 0;
21676bd547bSAdrian Chadd 
21776bd547bSAdrian Chadd     if (valid) {
21876bd547bSAdrian Chadd         l = *p32;
21976bd547bSAdrian Chadd         p32++;
22076bd547bSAdrian Chadd         u = *(u_int16_t *) p32;
22176bd547bSAdrian Chadd     }
22276bd547bSAdrian Chadd 
22376bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_WOW_OFFLOAD_ACER_MAGIC_START, l);
22476bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_WOW_OFFLOAD_ACER_MAGIC_START + 4, u);
22576bd547bSAdrian Chadd 
22676bd547bSAdrian Chadd     HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,
22776bd547bSAdrian Chadd         "%s: Aer Magic: %02x-%02x-%02x-%02x-%02x-%02x\n", __func__,
22876bd547bSAdrian Chadd         datap[0], datap[1], datap[2], datap[3], datap[4], datap[5]);
22976bd547bSAdrian Chadd }
23076bd547bSAdrian Chadd 
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)23176bd547bSAdrian Chadd void ar9300_wowoffload_download_acer_swka(  struct ath_hal *ah,
23276bd547bSAdrian Chadd                                             u_int32_t  id,
23376bd547bSAdrian Chadd                                             HAL_BOOL       valid,
23476bd547bSAdrian Chadd                                             u_int32_t  period,
23576bd547bSAdrian Chadd                                             u_int32_t  size,
23676bd547bSAdrian Chadd                                             u_int32_t* datap)
23776bd547bSAdrian Chadd {
23876bd547bSAdrian Chadd     u_int32_t ka_period[2] = {
23976bd547bSAdrian Chadd         AR_WOW_OFFLOAD_ACER_KA0_PERIOD_MS,
24076bd547bSAdrian Chadd         AR_WOW_OFFLOAD_ACER_KA1_PERIOD_MS
24176bd547bSAdrian Chadd     };
24276bd547bSAdrian Chadd     u_int32_t ka_size[2] = {
24376bd547bSAdrian Chadd         AR_WOW_OFFLOAD_ACER_KA0_SIZE,
24476bd547bSAdrian Chadd         AR_WOW_OFFLOAD_ACER_KA1_SIZE
24576bd547bSAdrian Chadd     };
24676bd547bSAdrian Chadd     u_int32_t ka_data[2] = {
24776bd547bSAdrian Chadd         AR_WOW_OFFLOAD_ACER_KA0_DATA,
24876bd547bSAdrian Chadd         AR_WOW_OFFLOAD_ACER_KA1_DATA
24976bd547bSAdrian Chadd     };
25076bd547bSAdrian Chadd     u_int32_t n_data = AR_WOW_OFFLOAD_ACER_KA0_DATA_WORDS;
25176bd547bSAdrian Chadd     int i;
25276bd547bSAdrian Chadd 
25376bd547bSAdrian Chadd     if (id >= 2) {
25476bd547bSAdrian Chadd         return;
25576bd547bSAdrian Chadd     }
25676bd547bSAdrian Chadd 
25776bd547bSAdrian Chadd     if (valid) {
25876bd547bSAdrian Chadd         OS_REG_WRITE(ah, ka_period[id], period);
25976bd547bSAdrian Chadd         OS_REG_WRITE(ah, ka_size[id], size);
26076bd547bSAdrian Chadd     } else {
26176bd547bSAdrian Chadd         OS_REG_WRITE(ah, ka_period[id], 0);
26276bd547bSAdrian Chadd         OS_REG_WRITE(ah, ka_size[id], 0);
26376bd547bSAdrian Chadd     }
26476bd547bSAdrian Chadd     HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: id=%d, period=%d ms, size=%d bytes\n",
26576bd547bSAdrian Chadd             __func__, id, period, size);
26676bd547bSAdrian Chadd 
26776bd547bSAdrian Chadd     if (size < (n_data * 4)) {
26876bd547bSAdrian Chadd         n_data = (size + 3) / 4;
26976bd547bSAdrian Chadd     }
27076bd547bSAdrian Chadd     for (i=0; i<n_data * 4; i+=4) {
27176bd547bSAdrian Chadd         OS_REG_WRITE(ah, ka_data[id] + i, *datap);
27276bd547bSAdrian Chadd         /*HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) %08x\n", *datap);*/
27376bd547bSAdrian Chadd         datap++;
27476bd547bSAdrian Chadd     }
27576bd547bSAdrian Chadd }
27676bd547bSAdrian Chadd 
ar9300_wowoffload_download_arp_info(struct ath_hal * ah,u_int32_t id,u_int32_t * data)27776bd547bSAdrian Chadd void ar9300_wowoffload_download_arp_info(struct ath_hal *ah, u_int32_t id, u_int32_t *data)
27876bd547bSAdrian Chadd {
27976bd547bSAdrian Chadd     u_int32_t addr;
28076bd547bSAdrian Chadd     struct hal_wow_offload_arp_info *p_info = (struct hal_wow_offload_arp_info *) data;
28176bd547bSAdrian Chadd 
28276bd547bSAdrian Chadd     if (id == 0) {
28376bd547bSAdrian Chadd         addr = AR_WOW_OFFLOAD_ARP0_VALID;
28476bd547bSAdrian Chadd     } else if (id == 1) {
28576bd547bSAdrian Chadd         addr = AR_WOW_OFFLOAD_ARP1_VALID;
28676bd547bSAdrian Chadd     } else {
28776bd547bSAdrian Chadd         return;
28876bd547bSAdrian Chadd     }
28976bd547bSAdrian Chadd 
29076bd547bSAdrian Chadd     if (p_info->valid) {
29176bd547bSAdrian Chadd         OS_REG_WRITE(ah, addr, 0x1);
29276bd547bSAdrian Chadd         addr += 4;
29376bd547bSAdrian Chadd         OS_REG_WRITE(ah, addr, p_info->RemoteIPv4Address.u32);
29476bd547bSAdrian Chadd         addr += 4;
29576bd547bSAdrian Chadd         OS_REG_WRITE(ah, addr, p_info->HostIPv4Address.u32);
29676bd547bSAdrian Chadd         addr += 4;
29776bd547bSAdrian Chadd         OS_REG_WRITE(ah, addr, p_info->MacAddress.u32[0]);
29876bd547bSAdrian Chadd         addr += 4;
29976bd547bSAdrian Chadd         OS_REG_WRITE(ah, addr, p_info->MacAddress.u32[1]);
30076bd547bSAdrian Chadd     } else {
30176bd547bSAdrian Chadd         OS_REG_WRITE(ah, addr, 0x0);
30276bd547bSAdrian Chadd     }
30376bd547bSAdrian Chadd }
30476bd547bSAdrian Chadd 
30576bd547bSAdrian Chadd #define WOW_WRITE_NS_IPV6_ADDRESS(_ah, _buf_addr, _p_ipv6_addr) \
30676bd547bSAdrian Chadd     {                                                           \
30776bd547bSAdrian Chadd         u_int32_t   offset = (_buf_addr);                       \
30876bd547bSAdrian Chadd         u_int32_t  *p_ipv6_addr = (u_int32_t *) (_p_ipv6_addr); \
30976bd547bSAdrian Chadd         int i;                                                  \
31076bd547bSAdrian Chadd         for (i = 0; i < 4; i++) {                               \
31176bd547bSAdrian Chadd             OS_REG_WRITE((_ah), offset, *p_ipv6_addr);          \
31276bd547bSAdrian Chadd             offset += 4;                                        \
31376bd547bSAdrian Chadd             p_ipv6_addr ++;                                     \
31476bd547bSAdrian Chadd         }                                                       \
31576bd547bSAdrian Chadd     }
31676bd547bSAdrian Chadd 
ar9300_wowoffload_download_ns_info(struct ath_hal * ah,u_int32_t id,u_int32_t * data)31776bd547bSAdrian Chadd void ar9300_wowoffload_download_ns_info(struct ath_hal *ah, u_int32_t id, u_int32_t *data)
31876bd547bSAdrian Chadd {
31976bd547bSAdrian Chadd     u_int32_t addr;
32076bd547bSAdrian Chadd     struct hal_wow_offload_ns_info *p_info = (struct hal_wow_offload_ns_info *) data;
32176bd547bSAdrian Chadd     u_int8_t mc_addr[6];
32276bd547bSAdrian Chadd 
32376bd547bSAdrian Chadd     if (id == 0) {
32476bd547bSAdrian Chadd         addr = AR_WOW_OFFLOAD_NS0_VALID;
32576bd547bSAdrian Chadd     } else if (id == 1) {
32676bd547bSAdrian Chadd         addr = AR_WOW_OFFLOAD_NS1_VALID;
32776bd547bSAdrian Chadd     } else {
32876bd547bSAdrian Chadd         return;
32976bd547bSAdrian Chadd     }
33076bd547bSAdrian Chadd 
33176bd547bSAdrian Chadd     if (p_info->valid) {
33276bd547bSAdrian Chadd         OS_REG_WRITE(ah, addr, 0x1);
33376bd547bSAdrian Chadd         addr += 4;
33476bd547bSAdrian Chadd         WOW_WRITE_NS_IPV6_ADDRESS(ah, addr, &p_info->RemoteIPv6Address.u32[0]);
33576bd547bSAdrian Chadd         addr += 4 * 4;
33676bd547bSAdrian Chadd         WOW_WRITE_NS_IPV6_ADDRESS(ah, addr, &p_info->SolicitedNodeIPv6Address.u32[0]);
33776bd547bSAdrian Chadd         addr += 4 * 4;
33876bd547bSAdrian Chadd         OS_REG_WRITE(ah, addr, p_info->MacAddress.u32[0]);
33976bd547bSAdrian Chadd         addr += 4;
34076bd547bSAdrian Chadd         OS_REG_WRITE(ah, addr, p_info->MacAddress.u32[1]);
34176bd547bSAdrian Chadd         addr += 4;
34276bd547bSAdrian Chadd         WOW_WRITE_NS_IPV6_ADDRESS(ah, addr, &p_info->TargetIPv6Addresses[0].u32[0]);
34376bd547bSAdrian Chadd         addr += 4 * 4;
34476bd547bSAdrian Chadd         WOW_WRITE_NS_IPV6_ADDRESS(ah, addr, &p_info->TargetIPv6Addresses[1].u32[0]);
34576bd547bSAdrian Chadd 
34676bd547bSAdrian Chadd         mc_addr[0] = 0x33;
34776bd547bSAdrian Chadd         mc_addr[1] = 0x33;
34876bd547bSAdrian Chadd         mc_addr[2] = 0xFF;
34976bd547bSAdrian Chadd         mc_addr[3] = p_info->SolicitedNodeIPv6Address.u8[13];
35076bd547bSAdrian Chadd         mc_addr[4] = p_info->SolicitedNodeIPv6Address.u8[14];
35176bd547bSAdrian Chadd         mc_addr[5] = p_info->SolicitedNodeIPv6Address.u8[15];
35276bd547bSAdrian Chadd         ar9300_wowoffload_add_mcast_filter(ah, mc_addr);
35376bd547bSAdrian Chadd     } else {
35476bd547bSAdrian Chadd         OS_REG_WRITE(ah, addr, 0x0);
35576bd547bSAdrian Chadd     }
35676bd547bSAdrian Chadd }
35776bd547bSAdrian Chadd 
35876bd547bSAdrian Chadd /* Download transmit parameters for GTK response frame during WoW
35976bd547bSAdrian Chadd  * offload */
ar9300_wow_offload_download_hal_params(struct ath_hal * ah)36076bd547bSAdrian Chadd u_int32_t ar9300_wow_offload_download_hal_params(struct ath_hal *ah)
36176bd547bSAdrian Chadd {
36276bd547bSAdrian Chadd     u_int32_t tpc = 0x3f; /* Transmit Power Control */
36376bd547bSAdrian Chadd     u_int32_t tx_tries_series = 7;
36476bd547bSAdrian Chadd     u_int32_t tx_rate_series, transmit_rate;
36576bd547bSAdrian Chadd     u_int32_t gtk_txdesc_param_start;
36676bd547bSAdrian Chadd 
36776bd547bSAdrian Chadd     if (AH_PRIVATE(ah)->ah_curchan->channel_flags & CHANNEL_CCK) {
36876bd547bSAdrian Chadd         transmit_rate = 0x1B;    /* CCK_1M */
36976bd547bSAdrian Chadd     } else {
37076bd547bSAdrian Chadd         transmit_rate = 0xB;     /* OFDM_6M */
37176bd547bSAdrian Chadd     }
37276bd547bSAdrian Chadd 
37376bd547bSAdrian Chadd     /* Use single rate for now. Change later as need be */
37476bd547bSAdrian Chadd     tx_rate_series  = transmit_rate;
37576bd547bSAdrian Chadd     tx_tries_series = 7;
37676bd547bSAdrian Chadd 
37776bd547bSAdrian Chadd     if (AR_SREV_JUPITER(ah)) {
37876bd547bSAdrian Chadd         gtk_txdesc_param_start = AR_WOW_OFFLOAD_GTK_TXDESC_PARAM_START_JUPITER;
37976bd547bSAdrian Chadd     } else {
38076bd547bSAdrian Chadd         gtk_txdesc_param_start = AR_WOW_OFFLOAD_GTK_TXDESC_PARAM_START;
38176bd547bSAdrian Chadd     }
38276bd547bSAdrian Chadd #define AR_WOW_OFFLOAD_GTK_TXDESC_PARAM(x) (gtk_txdesc_param_start + ((x) * 4))
38376bd547bSAdrian Chadd 
38476bd547bSAdrian Chadd     /* Do not change the data order unless firmware code on embedded
38576bd547bSAdrian Chadd      * CPU is changed correspondingly */
38676bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_WOW_OFFLOAD_GTK_TXDESC_PARAM(0), tx_rate_series);
38776bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_WOW_OFFLOAD_GTK_TXDESC_PARAM(1), tx_tries_series);
38876bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_WOW_OFFLOAD_GTK_TXDESC_PARAM(2), AH9300(ah)->ah_tx_chainmask);
38976bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_WOW_OFFLOAD_GTK_TXDESC_PARAM(3), tpc);
39076bd547bSAdrian Chadd 
39176bd547bSAdrian Chadd     return 0;
39276bd547bSAdrian Chadd }
39376bd547bSAdrian Chadd 
39476bd547bSAdrian Chadd /* Indicate to the embedded CPU that host is ready to enter WoW mode.
39576bd547bSAdrian Chadd  * Embedded CPU will copy relevant information from the MAC PCU buffer
39676bd547bSAdrian Chadd  */
ar9300_wow_offload_handshake(struct ath_hal * ah,u_int32_t pattern_enable)39776bd547bSAdrian Chadd u_int32_t ar9300_wow_offload_handshake(struct ath_hal *ah, u_int32_t pattern_enable)
39876bd547bSAdrian Chadd {
39976bd547bSAdrian Chadd     int val;
40076bd547bSAdrian Chadd     int mbox_status = OS_REG_READ(ah, AR_MBOX_CTRL_STATUS);
40176bd547bSAdrian Chadd #if ATH_WOW_OFFLOAD
40276bd547bSAdrian Chadd     u_int32_t bt_handshake_timeout_us = HAL_WOW_CTRL_WAIT_BT_TO(ah) * 100000;
40376bd547bSAdrian Chadd 
40476bd547bSAdrian Chadd #define AH_DEFAULT_BT_WAIT_TIMEOUT  3000000; /* 3 sec */
40576bd547bSAdrian Chadd     if (bt_handshake_timeout_us == 0) {
40676bd547bSAdrian Chadd         bt_handshake_timeout_us = AH_DEFAULT_BT_WAIT_TIMEOUT;
40776bd547bSAdrian Chadd     }
40876bd547bSAdrian Chadd     HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) TIMEOUT: %d us\n", bt_handshake_timeout_us);
40976bd547bSAdrian Chadd #endif /* ATH_WOW_OFFLOAD */
41076bd547bSAdrian Chadd 
41176bd547bSAdrian Chadd     if (mbox_status & AR_MBOX_WOW_REQ) {
41276bd547bSAdrian Chadd         /* WOW mode request handshake is already in progress.
41376bd547bSAdrian Chadd          * Do nothing */
41476bd547bSAdrian Chadd         return 0;
41576bd547bSAdrian Chadd     }
41676bd547bSAdrian Chadd 
41776bd547bSAdrian Chadd     /* Clear status */
41876bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_MBOX_CTRL_STATUS, 0);
41976bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_EMB_CPU_WOW_STATUS, 0x0);
42076bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_WLAN_WOW_ENABLE, 0);
42176bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_WLAN_WOW_STATUS, 0xFFFFFFFF);
42276bd547bSAdrian Chadd 
42376bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_RIMT, 0);
42476bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_TIMT, 0);
42576bd547bSAdrian Chadd 
42676bd547bSAdrian Chadd     val = 0;
42776bd547bSAdrian Chadd     if (pattern_enable & AH_WOW_USER_PATTERN_EN) {
42876bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - User pattern\n");
42976bd547bSAdrian Chadd         val |= AR_EMB_CPU_WOW_ENABLE_PATTERN_MATCH;
43076bd547bSAdrian Chadd     }
43176bd547bSAdrian Chadd     else {
43276bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - User pattern\n");
43376bd547bSAdrian Chadd     }
43476bd547bSAdrian Chadd     if ((pattern_enable & AH_WOW_MAGIC_PATTERN_EN)
43576bd547bSAdrian Chadd #if ATH_WOW_OFFLOAD
43676bd547bSAdrian Chadd         || (pattern_enable & AH_WOW_ACER_MAGIC_EN)
43776bd547bSAdrian Chadd #endif
43876bd547bSAdrian Chadd         )
43976bd547bSAdrian Chadd     {
44076bd547bSAdrian Chadd         val |= AR_EMB_CPU_WOW_ENABLE_MAGIC_PATTERN;
44176bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - Magic pattern\n");
44276bd547bSAdrian Chadd     }
44376bd547bSAdrian Chadd     else {
44476bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - Magic pattern\n");
44576bd547bSAdrian Chadd     }
44676bd547bSAdrian Chadd     if ((pattern_enable & AH_WOW_LINK_CHANGE)
44776bd547bSAdrian Chadd #if ATH_WOW_OFFLOAD
44876bd547bSAdrian Chadd         || HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_KAFAIL_ENABLE)
44976bd547bSAdrian Chadd #endif
45076bd547bSAdrian Chadd         )
45176bd547bSAdrian Chadd     {
45276bd547bSAdrian Chadd         val |= AR_EMB_CPU_WOW_ENABLE_KEEP_ALIVE_FAIL;
45376bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - Kepp alive fail\n");
45476bd547bSAdrian Chadd     }
45576bd547bSAdrian Chadd     else {
45676bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - Kepp alive fail\n");
45776bd547bSAdrian Chadd     }
45876bd547bSAdrian Chadd     if (pattern_enable & AH_WOW_BEACON_MISS) {
45976bd547bSAdrian Chadd         val |= AR_EMB_CPU_WOW_ENABLE_BEACON_MISS;
46076bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - Becon Miss\n");
46176bd547bSAdrian Chadd     }
46276bd547bSAdrian Chadd     else {
46376bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - Becon Miss\n");
46476bd547bSAdrian Chadd     }
46576bd547bSAdrian Chadd 
46676bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_EMB_CPU_WOW_ENABLE, val);
46776bd547bSAdrian Chadd 
46876bd547bSAdrian Chadd     OS_REG_CLR_BIT(ah, AR_MBOX_CTRL_STATUS, AR_MBOX_WOW_CONF);
46976bd547bSAdrian Chadd     OS_REG_SET_BIT(ah, AR_MBOX_CTRL_STATUS, AR_MBOX_WOW_REQ);
47076bd547bSAdrian Chadd     OS_REG_SET_BIT(ah, AR_MBOX_CTRL_STATUS, AR_MBOX_INT_EMB_CPU);
47176bd547bSAdrian Chadd 
472*899d1cacSAdrian Chadd     if (!ath_hal_waitfor(ah, AR_MBOX_CTRL_STATUS, AR_MBOX_WOW_CONF, AR_MBOX_WOW_CONF, bt_handshake_timeout_us)) {
47376bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: WoW offload handshake failed", __func__);
47476bd547bSAdrian Chadd         return 0;
47576bd547bSAdrian Chadd     }
47676bd547bSAdrian Chadd     else {
47776bd547bSAdrian Chadd         OS_REG_CLR_BIT(ah, AR_MBOX_CTRL_STATUS, AR_MBOX_WOW_CONF);
47876bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_POWER_MGMT, "%s: WoW offload handshake successful",__func__);
47976bd547bSAdrian Chadd     }
48076bd547bSAdrian Chadd     return 1;
48176bd547bSAdrian Chadd }
48276bd547bSAdrian Chadd #endif /* ATH_WOW_OFFLOAD */
48376bd547bSAdrian Chadd 
48476bd547bSAdrian Chadd /*
48576bd547bSAdrian Chadd  * Notify Power Mgt is enabled in self-generated frames.
48676bd547bSAdrian Chadd  * If requested, force chip awake.
48776bd547bSAdrian Chadd  *
48876bd547bSAdrian Chadd  * Returns A_OK if chip is awake or successfully forced awake.
48976bd547bSAdrian Chadd  *
49076bd547bSAdrian Chadd  * WARNING WARNING WARNING
49176bd547bSAdrian Chadd  * There is a problem with the chip where sometimes it will not wake up.
49276bd547bSAdrian Chadd  */
49376bd547bSAdrian Chadd HAL_BOOL
ar9300_set_power_mode_awake(struct ath_hal * ah,int set_chip)49476bd547bSAdrian Chadd ar9300_set_power_mode_awake(struct ath_hal *ah, int set_chip)
49576bd547bSAdrian Chadd {
49676bd547bSAdrian Chadd     struct ath_hal_9300 *ahp = AH9300(ah);
49776bd547bSAdrian Chadd #define POWER_UP_TIME   10000
49876bd547bSAdrian Chadd     u_int32_t val;
49976bd547bSAdrian Chadd     int i;
50076bd547bSAdrian Chadd 
50176bd547bSAdrian Chadd     /* Set Bits 14 and 17 of AR_WA before powering on the chip. */
50276bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA), ahp->ah_wa_reg_val);
50376bd547bSAdrian Chadd     OS_DELAY(10); /* delay to allow the write to take effect. */
50476bd547bSAdrian Chadd 
50576bd547bSAdrian Chadd     if (set_chip) {
50676bd547bSAdrian Chadd         /* Do a Power-On-Reset if MAC is shutdown */
50776bd547bSAdrian Chadd         if ((OS_REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_SHUTDOWN)) {
50876bd547bSAdrian Chadd             if (ar9300_set_reset_reg(ah, HAL_RESET_POWER_ON) != AH_TRUE) {
50976bd547bSAdrian Chadd                 HALASSERT(0);
51076bd547bSAdrian Chadd                 return AH_FALSE;
51176bd547bSAdrian Chadd             }
51276bd547bSAdrian Chadd         }
51376bd547bSAdrian Chadd 
51476bd547bSAdrian Chadd         OS_REG_SET_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN);
51576bd547bSAdrian Chadd 
51676bd547bSAdrian Chadd         OS_DELAY(50);
51776bd547bSAdrian Chadd 
51876bd547bSAdrian Chadd         for (i = POWER_UP_TIME / 50; i > 0; i--) {
51976bd547bSAdrian Chadd             val = OS_REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M;
52076bd547bSAdrian Chadd             if (val == AR_RTC_STATUS_ON) {
52176bd547bSAdrian Chadd                 break;
52276bd547bSAdrian Chadd             }
52376bd547bSAdrian Chadd             OS_DELAY(50);
52476bd547bSAdrian Chadd             OS_REG_SET_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN);
52576bd547bSAdrian Chadd         }
52676bd547bSAdrian Chadd         if (i == 0) {
52776bd547bSAdrian Chadd             HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: Failed to wakeup in %uus\n",
52876bd547bSAdrian Chadd                      __func__, POWER_UP_TIME / 20);
52976bd547bSAdrian Chadd             return AH_FALSE;
53076bd547bSAdrian Chadd         }
53176bd547bSAdrian Chadd 
53276bd547bSAdrian Chadd     }
53376bd547bSAdrian Chadd 
53476bd547bSAdrian Chadd     OS_REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
53576bd547bSAdrian Chadd     return AH_TRUE;
53676bd547bSAdrian Chadd #undef POWER_UP_TIME
53776bd547bSAdrian Chadd }
53876bd547bSAdrian Chadd 
53976bd547bSAdrian Chadd /*
54076bd547bSAdrian Chadd  * Notify Power Mgt is disabled in self-generated frames.
54176bd547bSAdrian Chadd  * If requested, force chip to sleep.
54276bd547bSAdrian Chadd  */
54376bd547bSAdrian Chadd static void
ar9300_set_power_mode_sleep(struct ath_hal * ah,int set_chip)54476bd547bSAdrian Chadd ar9300_set_power_mode_sleep(struct ath_hal *ah, int set_chip)
54576bd547bSAdrian Chadd {
54676bd547bSAdrian Chadd     struct ath_hal_9300 *ahp = AH9300(ah);
54776bd547bSAdrian Chadd 
54876bd547bSAdrian Chadd     OS_REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
54976bd547bSAdrian Chadd     if (set_chip ) {
55076bd547bSAdrian Chadd         if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) {
55176bd547bSAdrian Chadd             OS_REG_WRITE(ah, AR_TIMER_MODE,
55276bd547bSAdrian Chadd                     OS_REG_READ(ah, AR_TIMER_MODE) & 0xFFFFFF00);
55376bd547bSAdrian Chadd             OS_REG_WRITE(ah, AR_GEN_TIMERS2_MODE,
55476bd547bSAdrian Chadd                     OS_REG_READ(ah, AR_GEN_TIMERS2_MODE) & 0xFFFFFF00);
55576bd547bSAdrian Chadd             OS_REG_WRITE(ah, AR_SLP32_INC,
55676bd547bSAdrian Chadd                     OS_REG_READ(ah, AR_SLP32_INC) & 0xFFF00000);
55776bd547bSAdrian Chadd             OS_REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN, 0);
55876bd547bSAdrian Chadd             OS_DELAY(100);
55976bd547bSAdrian Chadd         }
56076bd547bSAdrian Chadd         /* Clear the RTC force wake bit to allow the mac to go to sleep */
56176bd547bSAdrian Chadd         OS_REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN);
56276bd547bSAdrian Chadd 
56376bd547bSAdrian Chadd         if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) {
56476bd547bSAdrian Chadd             /*
56576bd547bSAdrian Chadd              * In Jupiter, after enter sleep mode, hardware will send
56676bd547bSAdrian Chadd              * a SYS_SLEEPING message through MCI interface. Add a
56776bd547bSAdrian Chadd              * few us delay to make sure the message can reach BT side.
56876bd547bSAdrian Chadd              */
56976bd547bSAdrian Chadd             OS_DELAY(100);
57076bd547bSAdrian Chadd         }
57176bd547bSAdrian Chadd 
57276bd547bSAdrian Chadd         if (!AR_SREV_JUPITER_10(ah)) {
57376bd547bSAdrian Chadd             /* Shutdown chip. Active low */
57476bd547bSAdrian Chadd             OS_REG_CLR_BIT(ah, AR_RTC_RESET, AR_RTC_RESET_EN);
57576bd547bSAdrian Chadd             /* Settle time */
57676bd547bSAdrian Chadd             OS_DELAY(2);
57776bd547bSAdrian Chadd         }
57876bd547bSAdrian Chadd     }
57976bd547bSAdrian Chadd 
58076bd547bSAdrian Chadd #if ATH_WOW_OFFLOAD
58176bd547bSAdrian Chadd     if (!AR_SREV_JUPITER(ah) || !HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_SET_4004_BIT14))
58276bd547bSAdrian Chadd #endif /* ATH_WOW_OFFLOAD */
58376bd547bSAdrian Chadd     {
58476bd547bSAdrian Chadd         /* Clear Bit 14 of AR_WA after putting chip into Full Sleep mode. */
58576bd547bSAdrian Chadd         OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA),
58676bd547bSAdrian Chadd                ahp->ah_wa_reg_val & ~AR_WA_D3_TO_L1_DISABLE);
58776bd547bSAdrian Chadd     }
58876bd547bSAdrian Chadd }
58976bd547bSAdrian Chadd 
59076bd547bSAdrian Chadd /*
59176bd547bSAdrian Chadd  * Notify Power Management is enabled in self-generating
59276bd547bSAdrian Chadd  * frames. If request, set power mode of chip to
59376bd547bSAdrian Chadd  * auto/normal.  Duration in units of 128us (1/8 TU).
59476bd547bSAdrian Chadd  */
59576bd547bSAdrian Chadd static void
ar9300_set_power_mode_network_sleep(struct ath_hal * ah,int set_chip)59676bd547bSAdrian Chadd ar9300_set_power_mode_network_sleep(struct ath_hal *ah, int set_chip)
59776bd547bSAdrian Chadd {
59876bd547bSAdrian Chadd     struct ath_hal_9300 *ahp = AH9300(ah);
59976bd547bSAdrian Chadd 
60076bd547bSAdrian Chadd     OS_REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
60176bd547bSAdrian Chadd     if (set_chip) {
60276bd547bSAdrian Chadd         HAL_CAPABILITIES *p_cap = &AH_PRIVATE(ah)->ah_caps;
60376bd547bSAdrian Chadd 
604e113789bSAdrian Chadd         if (! p_cap->halAutoSleepSupport) {
60576bd547bSAdrian Chadd             /* Set wake_on_interrupt bit; clear force_wake bit */
60676bd547bSAdrian Chadd             OS_REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON_INT);
60776bd547bSAdrian Chadd         }
60876bd547bSAdrian Chadd         else {
60976bd547bSAdrian Chadd             /*
61076bd547bSAdrian Chadd              * When chip goes into network sleep, it could be waken up by
61176bd547bSAdrian Chadd              * MCI_INT interrupt caused by BT's HW messages (LNA_xxx, CONT_xxx)
61276bd547bSAdrian Chadd              * which chould be in a very fast rate (~100us). This will cause
61376bd547bSAdrian Chadd              * chip to leave and re-enter network sleep mode frequently, which
61476bd547bSAdrian Chadd              * in consequence will have WLAN MCI HW to generate lots of
61576bd547bSAdrian Chadd              * SYS_WAKING and SYS_SLEEPING messages which will make BT CPU
61676bd547bSAdrian Chadd              * to busy to process.
61776bd547bSAdrian Chadd              */
61876bd547bSAdrian Chadd             if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) {
61976bd547bSAdrian Chadd                 OS_REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN,
62076bd547bSAdrian Chadd                         OS_REG_READ(ah, AR_MCI_INTERRUPT_RX_MSG_EN) &
62176bd547bSAdrian Chadd                                     ~AR_MCI_INTERRUPT_RX_HW_MSG_MASK);
62276bd547bSAdrian Chadd             }
62376bd547bSAdrian Chadd 
62476bd547bSAdrian Chadd             /* Clear the RTC force wake bit to allow the mac to go to sleep */
62576bd547bSAdrian Chadd             OS_REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN);
62676bd547bSAdrian Chadd 
62776bd547bSAdrian Chadd             if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) {
62876bd547bSAdrian Chadd                 /*
62976bd547bSAdrian Chadd                  * In Jupiter, after enter sleep mode, hardware will send
63076bd547bSAdrian Chadd                  * a SYS_SLEEPING message through MCI interface. Add a
63176bd547bSAdrian Chadd                  * few us delay to make sure the message can reach BT side.
63276bd547bSAdrian Chadd                  */
63376bd547bSAdrian Chadd                 OS_DELAY(30);
63476bd547bSAdrian Chadd             }
63576bd547bSAdrian Chadd         }
63676bd547bSAdrian Chadd     }
63776bd547bSAdrian Chadd 
63876bd547bSAdrian Chadd #if ATH_WOW_OFFLOAD
63976bd547bSAdrian Chadd     if (!AR_SREV_JUPITER(ah) || !HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_SET_4004_BIT14))
64076bd547bSAdrian Chadd #endif /* ATH_WOW_OFFLOAD */
64176bd547bSAdrian Chadd     {
64276bd547bSAdrian Chadd         /* Clear Bit 14 of AR_WA after putting chip into Sleep mode. */
64376bd547bSAdrian Chadd         OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA),
64476bd547bSAdrian Chadd                ahp->ah_wa_reg_val & ~AR_WA_D3_TO_L1_DISABLE);
64576bd547bSAdrian Chadd     }
64676bd547bSAdrian Chadd }
64776bd547bSAdrian Chadd 
64876bd547bSAdrian Chadd /*
64976bd547bSAdrian Chadd  * Set power mgt to the requested mode, and conditionally set
65076bd547bSAdrian Chadd  * the chip as well
65176bd547bSAdrian Chadd  */
65276bd547bSAdrian Chadd HAL_BOOL
ar9300_set_power_mode(struct ath_hal * ah,HAL_POWER_MODE mode,int set_chip)65376bd547bSAdrian Chadd ar9300_set_power_mode(struct ath_hal *ah, HAL_POWER_MODE mode, int set_chip)
65476bd547bSAdrian Chadd {
65576bd547bSAdrian Chadd     struct ath_hal_9300 *ahp = AH9300(ah);
656e113789bSAdrian Chadd #if defined(AH_DEBUG) || defined(AH_PRINT_FILTER)
65776bd547bSAdrian Chadd     static const char* modes[] = {
65876bd547bSAdrian Chadd         "AWAKE",
65976bd547bSAdrian Chadd         "FULL-SLEEP",
66076bd547bSAdrian Chadd         "NETWORK SLEEP",
66176bd547bSAdrian Chadd         "UNDEFINED"
66276bd547bSAdrian Chadd     };
66376bd547bSAdrian Chadd #endif
66476bd547bSAdrian Chadd     int status = AH_TRUE;
66576bd547bSAdrian Chadd 
66676bd547bSAdrian Chadd     HALDEBUG(ah, HAL_DEBUG_POWER_MGMT, "%s: %s -> %s (%s)\n", __func__,
66776bd547bSAdrian Chadd         modes[ar9300_get_power_mode(ah)], modes[mode],
66876bd547bSAdrian Chadd         set_chip ? "set chip " : "");
669823b2cc6SAdrian Chadd     OS_MARK(ah, AH_MARK_CHIP_POWER, mode);
67076bd547bSAdrian Chadd 
67176bd547bSAdrian Chadd     switch (mode) {
67276bd547bSAdrian Chadd     case HAL_PM_AWAKE:
67391e198f6SAdrian Chadd         if (set_chip)
674e6f27137SAdrian Chadd             ah->ah_powerMode = mode;
67576bd547bSAdrian Chadd         status = ar9300_set_power_mode_awake(ah, set_chip);
67676bd547bSAdrian Chadd #if ATH_SUPPORT_MCI
677e113789bSAdrian Chadd         if (AH_PRIVATE(ah)->ah_caps.halMciSupport) {
67876bd547bSAdrian Chadd             OS_REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
67976bd547bSAdrian Chadd         }
68076bd547bSAdrian Chadd #endif
681*899d1cacSAdrian Chadd         ahp->ah_chip_full_sleep = AH_FALSE;
68276bd547bSAdrian Chadd         break;
68376bd547bSAdrian Chadd     case HAL_PM_FULL_SLEEP:
68476bd547bSAdrian Chadd #if ATH_SUPPORT_MCI
685e113789bSAdrian Chadd         if (AH_PRIVATE(ah)->ah_caps.halMciSupport) {
68676bd547bSAdrian Chadd             if (ar9300_get_power_mode(ah) == HAL_PM_AWAKE) {
68776bd547bSAdrian Chadd                 if ((ar9300_mci_state(ah, HAL_MCI_STATE_ENABLE, NULL) != 0) &&
68876bd547bSAdrian Chadd                     (ahp->ah_mci_bt_state != MCI_BT_SLEEP) &&
68976bd547bSAdrian Chadd                     !ahp->ah_mci_halted_bt_gpm)
69076bd547bSAdrian Chadd                 {
69176bd547bSAdrian Chadd                     HALDEBUG(ah, HAL_DEBUG_BT_COEX,
69276bd547bSAdrian Chadd                         "(MCI) %s: HALT BT GPM (full_sleep)\n", __func__);
69376bd547bSAdrian Chadd                     ar9300_mci_send_coex_halt_bt_gpm(ah, AH_TRUE, AH_TRUE);
69476bd547bSAdrian Chadd                 }
69576bd547bSAdrian Chadd             }
69676bd547bSAdrian Chadd             ahp->ah_mci_ready = AH_FALSE;
69776bd547bSAdrian Chadd         }
69876bd547bSAdrian Chadd #endif
69976bd547bSAdrian Chadd #if ATH_SUPPORT_MCI
700e113789bSAdrian Chadd         if (AH_PRIVATE(ah)->ah_caps.halMciSupport) {
70176bd547bSAdrian Chadd             OS_REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
70276bd547bSAdrian Chadd         }
70376bd547bSAdrian Chadd #endif
70476bd547bSAdrian Chadd         ar9300_set_power_mode_sleep(ah, set_chip);
70591e198f6SAdrian Chadd         if (set_chip) {
70676bd547bSAdrian Chadd             ahp->ah_chip_full_sleep = AH_TRUE;
707e6f27137SAdrian Chadd             ah->ah_powerMode = mode;
70891e198f6SAdrian Chadd         }
70976bd547bSAdrian Chadd         break;
71076bd547bSAdrian Chadd     case HAL_PM_NETWORK_SLEEP:
71176bd547bSAdrian Chadd #if ATH_SUPPORT_MCI
712e113789bSAdrian Chadd         if (AH_PRIVATE(ah)->ah_caps.halMciSupport) {
71376bd547bSAdrian Chadd             OS_REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
71476bd547bSAdrian Chadd         }
71576bd547bSAdrian Chadd #endif
71676bd547bSAdrian Chadd         ar9300_set_power_mode_network_sleep(ah, set_chip);
71791e198f6SAdrian Chadd         if (set_chip) {
718e6f27137SAdrian Chadd             ah->ah_powerMode = mode;
71991e198f6SAdrian Chadd         }
72076bd547bSAdrian Chadd         break;
72176bd547bSAdrian Chadd     default:
72276bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_POWER_MGMT,
72376bd547bSAdrian Chadd             "%s: unknown power mode %u\n", __func__, mode);
724823b2cc6SAdrian Chadd         OS_MARK(ah, AH_MARK_CHIP_POWER_DONE, -1);
72576bd547bSAdrian Chadd         return AH_FALSE;
72676bd547bSAdrian Chadd     }
727823b2cc6SAdrian Chadd     OS_MARK(ah, AH_MARK_CHIP_POWER_DONE, status);
72876bd547bSAdrian Chadd     return status;
72976bd547bSAdrian Chadd }
73076bd547bSAdrian Chadd 
73176bd547bSAdrian Chadd /*
73276bd547bSAdrian Chadd  * Return the current sleep mode of the chip
73376bd547bSAdrian Chadd  */
73476bd547bSAdrian Chadd HAL_POWER_MODE
ar9300_get_power_mode(struct ath_hal * ah)73576bd547bSAdrian Chadd ar9300_get_power_mode(struct ath_hal *ah)
73676bd547bSAdrian Chadd {
73776bd547bSAdrian Chadd     int mode = OS_REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M;
73876bd547bSAdrian Chadd 
73976bd547bSAdrian Chadd     switch (mode) {
74076bd547bSAdrian Chadd     case AR_RTC_STATUS_ON:
74176bd547bSAdrian Chadd     case AR_RTC_STATUS_WAKEUP:
74276bd547bSAdrian Chadd         return HAL_PM_AWAKE;
74376bd547bSAdrian Chadd         break;
74476bd547bSAdrian Chadd     case AR_RTC_STATUS_SLEEP:
74576bd547bSAdrian Chadd         return HAL_PM_NETWORK_SLEEP;
74676bd547bSAdrian Chadd         break;
74776bd547bSAdrian Chadd     case AR_RTC_STATUS_SHUTDOWN:
74876bd547bSAdrian Chadd         return HAL_PM_FULL_SLEEP;
74976bd547bSAdrian Chadd         break;
75076bd547bSAdrian Chadd     default:
75176bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_POWER_MGMT,
75276bd547bSAdrian Chadd             "%s: unknown power mode 0x%x\n", __func__, mode);
75376bd547bSAdrian Chadd         return HAL_PM_UNDEFINED;
75476bd547bSAdrian Chadd     }
75576bd547bSAdrian Chadd }
75676bd547bSAdrian Chadd 
75776bd547bSAdrian Chadd /*
75876bd547bSAdrian Chadd  * Set SM power save mode
75976bd547bSAdrian Chadd  */
76076bd547bSAdrian Chadd void
ar9300_set_sm_power_mode(struct ath_hal * ah,HAL_SMPS_MODE mode)76176bd547bSAdrian Chadd ar9300_set_sm_power_mode(struct ath_hal *ah, HAL_SMPS_MODE mode)
76276bd547bSAdrian Chadd {
76376bd547bSAdrian Chadd     int regval;
76476bd547bSAdrian Chadd     struct ath_hal_9300 *ahp = AH9300(ah);
76576bd547bSAdrian Chadd 
76676bd547bSAdrian Chadd     if (ar9300_get_capability(ah, HAL_CAP_DYNAMIC_SMPS, 0, AH_NULL) != HAL_OK) {
76776bd547bSAdrian Chadd         return;
76876bd547bSAdrian Chadd     }
76976bd547bSAdrian Chadd 
77076bd547bSAdrian Chadd     /* Program low & high power chainmask settings and enable MAC control */
77176bd547bSAdrian Chadd     regval = SM(AR_PCU_SMPS_LPWR_CHNMSK_VAL, AR_PCU_SMPS_LPWR_CHNMSK) |
77276bd547bSAdrian Chadd              SM(ahp->ah_rx_chainmask, AR_PCU_SMPS_HPWR_CHNMSK) |
77376bd547bSAdrian Chadd              AR_PCU_SMPS_MAC_CHAINMASK;
77476bd547bSAdrian Chadd 
77576bd547bSAdrian Chadd     /* Program registers according to required SM power mode.*/
77676bd547bSAdrian Chadd     switch (mode) {
77776bd547bSAdrian Chadd     case HAL_SMPS_SW_CTRL_LOW_PWR:
77876bd547bSAdrian Chadd         OS_REG_WRITE(ah, AR_PCU_SMPS, regval);
77976bd547bSAdrian Chadd         break;
78076bd547bSAdrian Chadd     case HAL_SMPS_SW_CTRL_HIGH_PWR:
78176bd547bSAdrian Chadd         OS_REG_WRITE(ah, AR_PCU_SMPS, regval | AR_PCU_SMPS_SW_CTRL_HPWR);
78276bd547bSAdrian Chadd         break;
78376bd547bSAdrian Chadd     case HAL_SMPS_HW_CTRL:
78476bd547bSAdrian Chadd         OS_REG_WRITE(ah, AR_PCU_SMPS, regval | AR_PCU_SMPS_HW_CTRL_EN);
78576bd547bSAdrian Chadd         break;
78676bd547bSAdrian Chadd     case HAL_SMPS_DEFAULT:
78776bd547bSAdrian Chadd         OS_REG_WRITE(ah, AR_PCU_SMPS, 0);
78876bd547bSAdrian Chadd         break;
78976bd547bSAdrian Chadd     default:
79076bd547bSAdrian Chadd         break;
79176bd547bSAdrian Chadd     }
79276bd547bSAdrian Chadd     ahp->ah_sm_power_mode = mode;
79376bd547bSAdrian Chadd }
79476bd547bSAdrian Chadd 
79576bd547bSAdrian Chadd #if ATH_WOW
79676bd547bSAdrian Chadd #if NOT_NEEDED_FOR_OSPREY /* not compiled for darwin */
79776bd547bSAdrian Chadd /*
79876bd547bSAdrian Chadd  * This routine is called to configure the SerDes register for the
79976bd547bSAdrian Chadd  * Merlin 2.0 and above chip during WOW sleep.
80076bd547bSAdrian Chadd  */
80176bd547bSAdrian Chadd static void
ar9280_config_ser_des__wow_sleep(struct ath_hal * ah)80276bd547bSAdrian Chadd ar9280_config_ser_des__wow_sleep(struct ath_hal *ah)
80376bd547bSAdrian Chadd {
80476bd547bSAdrian Chadd     int i;
80576bd547bSAdrian Chadd     struct ath_hal_9300 *ahp = AH9300(ah);
80676bd547bSAdrian Chadd 
80776bd547bSAdrian Chadd     /*
80876bd547bSAdrian Chadd      * For WOW sleep, we reprogram the SerDes so that the PLL and CHK REQ
80976bd547bSAdrian Chadd      * are both enabled. This uses more power but the Maverick team reported
81076bd547bSAdrian Chadd      * that otherwise, WOW sleep is unstable and chip may disappears.
81176bd547bSAdrian Chadd      */
81276bd547bSAdrian Chadd     for (i = 0; i < ahp->ah_ini_pcie_serdes_wow.ia_rows; i++) {
81376bd547bSAdrian Chadd         OS_REG_WRITE(ah,
81476bd547bSAdrian Chadd             INI_RA(&ahp->ah_ini_pcie_serdes_wow, i, 0),
81576bd547bSAdrian Chadd             INI_RA(&ahp->ah_ini_pcie_serdes_wow, i, 1));
81676bd547bSAdrian Chadd     }
81776bd547bSAdrian Chadd     OS_DELAY(1000);
81876bd547bSAdrian Chadd }
81976bd547bSAdrian Chadd #endif /* if NOT_NEEDED_FOR_OSPREY */
82076bd547bSAdrian Chadd static HAL_BOOL
ar9300_wow_create_keep_alive_pattern(struct ath_hal * ah)82176bd547bSAdrian Chadd ar9300_wow_create_keep_alive_pattern(struct ath_hal *ah)
82276bd547bSAdrian Chadd {
82376bd547bSAdrian Chadd     struct ath_hal_9300 *ahp = AH9300(ah);
82476bd547bSAdrian Chadd     u_int32_t  frame_len = 28;
82576bd547bSAdrian Chadd     u_int32_t  tpc = 0x3f;
82676bd547bSAdrian Chadd     u_int32_t  transmit_rate;
82776bd547bSAdrian Chadd     u_int32_t  frame_type = 0x2;    /* Frame Type -> Data; */
82876bd547bSAdrian Chadd     u_int32_t  sub_type = 0x4;      /* Subtype -> Null Data */
82976bd547bSAdrian Chadd     u_int32_t  to_ds = 1;
83076bd547bSAdrian Chadd     u_int32_t  duration_id = 0x3d;
83176bd547bSAdrian Chadd     u_int8_t   *sta_mac_addr, *ap_mac_addr;
83276bd547bSAdrian Chadd     u_int8_t   *addr1, *addr2, *addr3;
83376bd547bSAdrian Chadd     u_int32_t  ctl[13] = { 0, };
83476bd547bSAdrian Chadd #define NUM_KA_DATA_WORDS 6
83576bd547bSAdrian Chadd     u_int32_t  data_word[NUM_KA_DATA_WORDS];
83676bd547bSAdrian Chadd     u_int32_t  i;
83776bd547bSAdrian Chadd     u_int32_t wow_ka_dataword0;
83876bd547bSAdrian Chadd 
83976bd547bSAdrian Chadd     sta_mac_addr = (u_int8_t *)ahp->ah_macaddr;
84076bd547bSAdrian Chadd     ap_mac_addr = (u_int8_t *)ahp->ah_bssid;
84176bd547bSAdrian Chadd     addr2 = sta_mac_addr;
84276bd547bSAdrian Chadd     addr1 = addr3 = ap_mac_addr;
84376bd547bSAdrian Chadd 
84476bd547bSAdrian Chadd     if (AH_PRIVATE(ah)->ah_curchan->channel_flags & CHANNEL_CCK) {
84576bd547bSAdrian Chadd         transmit_rate = 0x1B;    /* CCK_1M */
84676bd547bSAdrian Chadd     } else {
84776bd547bSAdrian Chadd         transmit_rate = 0xB;     /* OFDM_6M */
84876bd547bSAdrian Chadd     }
84976bd547bSAdrian Chadd 
85076bd547bSAdrian Chadd     /* Set the Transmit Buffer. */
85176bd547bSAdrian Chadd     ctl[0] = (frame_len | (tpc << 16));
85276bd547bSAdrian Chadd     ctl[1] = 0;
85376bd547bSAdrian Chadd     ctl[2] = (0x7 << 16);  /* tx_tries0 */
85476bd547bSAdrian Chadd     ctl[3] = transmit_rate;
85576bd547bSAdrian Chadd     ctl[4] = 0;
85676bd547bSAdrian Chadd     ctl[7] = ahp->ah_tx_chainmask << 2;
85776bd547bSAdrian Chadd 
85876bd547bSAdrian Chadd     for (i = 0; i < 13; i++) {
85976bd547bSAdrian Chadd         OS_REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]);
86076bd547bSAdrian Chadd     }
86176bd547bSAdrian Chadd 
86276bd547bSAdrian Chadd     data_word[0] =
86376bd547bSAdrian Chadd         (frame_type  <<  2) |
86476bd547bSAdrian Chadd         (sub_type    <<  4) |
86576bd547bSAdrian Chadd         (to_ds       <<  8) |
86676bd547bSAdrian Chadd         (duration_id << 16);
86776bd547bSAdrian Chadd     data_word[1] = (((u_int32_t)addr1[3] << 24) | ((u_int32_t)addr1[2] << 16) |
86876bd547bSAdrian Chadd                   ((u_int32_t)addr1[1]) << 8 | ((u_int32_t)addr1[0]));
86976bd547bSAdrian Chadd     data_word[2] = (((u_int32_t)addr2[1] << 24) | ((u_int32_t)addr2[0] << 16) |
87076bd547bSAdrian Chadd                   ((u_int32_t)addr1[5]) << 8 | ((u_int32_t)addr1[4]));
87176bd547bSAdrian Chadd     data_word[3] = (((u_int32_t)addr2[5] << 24) | ((u_int32_t)addr2[4] << 16) |
87276bd547bSAdrian Chadd                   ((u_int32_t)addr2[3]) << 8 | ((u_int32_t)addr2[2]));
87376bd547bSAdrian Chadd     data_word[4] = (((u_int32_t)addr3[3] << 24) | ((u_int32_t)addr3[2] << 16) |
87476bd547bSAdrian Chadd                   ((u_int32_t)addr3[1]) << 8 | (u_int32_t)addr3[0]);
87576bd547bSAdrian Chadd     data_word[5] = (((u_int32_t)addr3[5]) << 8 | ((u_int32_t)addr3[4]));
87676bd547bSAdrian Chadd 
87776bd547bSAdrian Chadd     if (AR_SREV_JUPITER_20_OR_LATER(ah) || AR_SREV_APHRODITE(ah)) {
87876bd547bSAdrian Chadd         /* Jupiter 2.0 has an extra descriptor word (Time based
87976bd547bSAdrian Chadd          * discard) compared to other chips */
88076bd547bSAdrian Chadd         OS_REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + 12 * 4), 0);
88176bd547bSAdrian Chadd         wow_ka_dataword0 = AR_WOW_TXBUF(13);
88276bd547bSAdrian Chadd     }
88376bd547bSAdrian Chadd     else {
88476bd547bSAdrian Chadd         wow_ka_dataword0 = AR_WOW_TXBUF(12);
88576bd547bSAdrian Chadd     }
88676bd547bSAdrian Chadd 
88776bd547bSAdrian Chadd     for (i = 0; i < NUM_KA_DATA_WORDS; i++) {
88876bd547bSAdrian Chadd         OS_REG_WRITE(ah, (wow_ka_dataword0 + i * 4), data_word[i]);
88976bd547bSAdrian Chadd     }
89076bd547bSAdrian Chadd 
89176bd547bSAdrian Chadd     return AH_TRUE;
89276bd547bSAdrian Chadd }
89376bd547bSAdrian Chadd 
89476bd547bSAdrian Chadd /* TBD: Should querying hal for hardware capability */
89576bd547bSAdrian Chadd #define MAX_PATTERN_SIZE      256
89676bd547bSAdrian Chadd #define MAX_PATTERN_MASK_SIZE  32
89776bd547bSAdrian Chadd #define MAX_NUM_USER_PATTERN    6 /* Deducting the disassoc/deauth packets */
89876bd547bSAdrian Chadd 
89976bd547bSAdrian Chadd 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)90076bd547bSAdrian Chadd ar9300_wow_apply_pattern(
90176bd547bSAdrian Chadd     struct ath_hal *ah,
90276bd547bSAdrian Chadd     u_int8_t *p_ath_pattern,
90376bd547bSAdrian Chadd     u_int8_t *p_ath_mask,
90476bd547bSAdrian Chadd     int32_t pattern_count,
90576bd547bSAdrian Chadd     u_int32_t ath_pattern_len)
90676bd547bSAdrian Chadd {
90776bd547bSAdrian Chadd     int i;
90876bd547bSAdrian Chadd     u_int32_t    reg_pat[] = {
90976bd547bSAdrian Chadd                   AR_WOW_TB_PATTERN0,
91076bd547bSAdrian Chadd                   AR_WOW_TB_PATTERN1,
91176bd547bSAdrian Chadd                   AR_WOW_TB_PATTERN2,
91276bd547bSAdrian Chadd                   AR_WOW_TB_PATTERN3,
91376bd547bSAdrian Chadd                   AR_WOW_TB_PATTERN4,
91476bd547bSAdrian Chadd                   AR_WOW_TB_PATTERN5,
91576bd547bSAdrian Chadd                   AR_WOW_TB_PATTERN6,
91676bd547bSAdrian Chadd                   AR_WOW_TB_PATTERN7
91776bd547bSAdrian Chadd                  };
91876bd547bSAdrian Chadd     u_int32_t    reg_mask[] = {
91976bd547bSAdrian Chadd                   AR_WOW_TB_MASK0,
92076bd547bSAdrian Chadd                   AR_WOW_TB_MASK1,
92176bd547bSAdrian Chadd                   AR_WOW_TB_MASK2,
92276bd547bSAdrian Chadd                   AR_WOW_TB_MASK3,
92376bd547bSAdrian Chadd                   AR_WOW_TB_MASK4,
92476bd547bSAdrian Chadd                   AR_WOW_TB_MASK5,
92576bd547bSAdrian Chadd                   AR_WOW_TB_MASK6,
92676bd547bSAdrian Chadd                   AR_WOW_TB_MASK7
92776bd547bSAdrian Chadd                  };
92876bd547bSAdrian Chadd     u_int32_t   pattern_val;
92976bd547bSAdrian Chadd     u_int32_t   mask_val;
93076bd547bSAdrian Chadd     u_int32_t   val;
93176bd547bSAdrian Chadd     u_int8_t    mask_bit = 0x1;
93276bd547bSAdrian Chadd     u_int8_t    pattern;
93376bd547bSAdrian Chadd 
93476bd547bSAdrian Chadd     /* TBD: should check count by querying the hardware capability */
93576bd547bSAdrian Chadd     if (pattern_count >= MAX_NUM_USER_PATTERN) {
93676bd547bSAdrian Chadd         return;
93776bd547bSAdrian Chadd     }
93876bd547bSAdrian Chadd 
93976bd547bSAdrian Chadd     pattern = (u_int8_t)OS_REG_READ(ah, AR_WOW_PATTERN_REG);
94076bd547bSAdrian Chadd     pattern = pattern | (mask_bit << pattern_count);
94176bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_WOW_PATTERN_REG, pattern);
94276bd547bSAdrian Chadd 
94376bd547bSAdrian Chadd     /* Set the registers for pattern */
94476bd547bSAdrian Chadd     for (i = 0; i < MAX_PATTERN_SIZE; i += 4) {
94576bd547bSAdrian Chadd         pattern_val = (((u_int32_t)p_ath_pattern[i + 0]) |
94676bd547bSAdrian Chadd                        ((u_int32_t)p_ath_pattern[i + 1] << 8) |
94776bd547bSAdrian Chadd                        ((u_int32_t)p_ath_pattern[i + 2] << 16) |
94876bd547bSAdrian Chadd                        ((u_int32_t)p_ath_pattern[i + 3] << 24));
94976bd547bSAdrian Chadd         OS_REG_WRITE(ah, (reg_pat[pattern_count] + i), pattern_val);
95076bd547bSAdrian Chadd     }
95176bd547bSAdrian Chadd 
95276bd547bSAdrian Chadd     /* Set the registers for mask */
95376bd547bSAdrian Chadd     for (i = 0; i < MAX_PATTERN_MASK_SIZE; i += 4) {
95476bd547bSAdrian Chadd         mask_val = (((u_int32_t)p_ath_mask[i + 0]) |
95576bd547bSAdrian Chadd                     ((u_int32_t)p_ath_mask[i + 1] << 8) |
95676bd547bSAdrian Chadd                     ((u_int32_t)p_ath_mask[i + 2] << 16) |
95776bd547bSAdrian Chadd                     ((u_int32_t)p_ath_mask[i + 3] << 24));
95876bd547bSAdrian Chadd         OS_REG_WRITE(ah, (reg_mask[pattern_count] + i), mask_val);
95976bd547bSAdrian Chadd     }
96076bd547bSAdrian Chadd 
96176bd547bSAdrian Chadd     /* XXX */
96276bd547bSAdrian Chadd     /* Set the pattern length to be matched */
96376bd547bSAdrian Chadd     if (pattern_count < 4) {
96476bd547bSAdrian Chadd         /* Pattern 0-3 uses AR_WOW_LENGTH1_REG register */
96576bd547bSAdrian Chadd         val = OS_REG_READ(ah, AR_WOW_LENGTH1_REG);
96676bd547bSAdrian Chadd         val = ((val & (~AR_WOW_LENGTH1_MASK(pattern_count))) |
96776bd547bSAdrian Chadd                ((ath_pattern_len & AR_WOW_LENGTH_MAX) <<
96876bd547bSAdrian Chadd                 AR_WOW_LENGTH1_SHIFT(pattern_count)));
96976bd547bSAdrian Chadd         OS_REG_WRITE(ah, AR_WOW_LENGTH1_REG, val);
97076bd547bSAdrian Chadd     } else {
97176bd547bSAdrian Chadd         /* Pattern 4-7 uses AR_WOW_LENGTH2_REG register */
97276bd547bSAdrian Chadd         val = OS_REG_READ(ah, AR_WOW_LENGTH2_REG);
97376bd547bSAdrian Chadd         val = ((val & (~AR_WOW_LENGTH2_MASK(pattern_count))) |
97476bd547bSAdrian Chadd                ((ath_pattern_len & AR_WOW_LENGTH_MAX) <<
97576bd547bSAdrian Chadd                 AR_WOW_LENGTH2_SHIFT(pattern_count)));
97676bd547bSAdrian Chadd         OS_REG_WRITE(ah, AR_WOW_LENGTH2_REG, val);
97776bd547bSAdrian Chadd     }
97876bd547bSAdrian Chadd 
97976bd547bSAdrian Chadd     AH_PRIVATE(ah)->ah_wow_event_mask |=
98076bd547bSAdrian Chadd         (1 << (pattern_count + AR_WOW_PATTERN_FOUND_SHIFT));
98176bd547bSAdrian Chadd 
98276bd547bSAdrian Chadd     return;
98376bd547bSAdrian Chadd }
98476bd547bSAdrian Chadd 
98576bd547bSAdrian Chadd HAL_BOOL
ar9300_set_power_mode_wow_sleep(struct ath_hal * ah)98676bd547bSAdrian Chadd ar9300_set_power_mode_wow_sleep(struct ath_hal *ah)
98776bd547bSAdrian Chadd {
98876bd547bSAdrian Chadd     OS_REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
98976bd547bSAdrian Chadd 
99076bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_CR, AR_CR_RXD);    /* Set receive disable bit */
991*899d1cacSAdrian Chadd     if (!ath_hal_waitfor(ah, AR_CR, AR_CR_RXE, 0, AH_WAIT_TIMEOUT)) {
99276bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_POWER_MGMT, "%s: dma failed to stop in 10ms\n"
99376bd547bSAdrian Chadd                  "AR_CR=0x%08x\nAR_DIAG_SW=0x%08x\n", __func__,
99476bd547bSAdrian Chadd                  OS_REG_READ(ah, AR_CR), OS_REG_READ(ah, AR_DIAG_SW));
99576bd547bSAdrian Chadd         return AH_FALSE;
99676bd547bSAdrian Chadd     } else {
99776bd547bSAdrian Chadd #if 0
99876bd547bSAdrian Chadd         OS_REG_WRITE(ah, AR_RXDP, 0x0);
99976bd547bSAdrian Chadd #endif
100076bd547bSAdrian Chadd 
100176bd547bSAdrian Chadd         HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE,
100276bd547bSAdrian Chadd             "%s: TODO How to disable RXDP!!\n", __func__);
100376bd547bSAdrian Chadd 
100476bd547bSAdrian Chadd #if ATH_SUPPORT_MCI
1005e113789bSAdrian Chadd         if (AH_PRIVATE(ah)->ah_caps.halMciSupport) {
100676bd547bSAdrian Chadd             OS_REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
100776bd547bSAdrian Chadd         }
100876bd547bSAdrian Chadd #endif
100976bd547bSAdrian Chadd         OS_REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON_INT);
101076bd547bSAdrian Chadd 
101176bd547bSAdrian Chadd         return AH_TRUE;
101276bd547bSAdrian Chadd     }
101376bd547bSAdrian Chadd }
101476bd547bSAdrian Chadd 
101576bd547bSAdrian Chadd 
101676bd547bSAdrian Chadd 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)101776bd547bSAdrian Chadd ar9300_wow_enable(
101876bd547bSAdrian Chadd     struct ath_hal *ah,
101976bd547bSAdrian Chadd     u_int32_t pattern_enable,
102076bd547bSAdrian Chadd     u_int32_t timeout_in_seconds,
102176bd547bSAdrian Chadd     int clearbssid,
102276bd547bSAdrian Chadd     HAL_BOOL offloadEnable)
102376bd547bSAdrian Chadd {
102476bd547bSAdrian Chadd     uint32_t init_val, val, rval = 0;
102576bd547bSAdrian Chadd     const int ka_delay = 4; /* Delay of 4 millisec between two keep_alive's */
102676bd547bSAdrian Chadd     uint32_t wow_event_mask;
102776bd547bSAdrian Chadd #if ATH_WOW_OFFLOAD
102876bd547bSAdrian Chadd     uint32_t wow_feature_enable =
102976bd547bSAdrian Chadd             //AR_WOW_OFFLOAD_ENA_GTK            |
103076bd547bSAdrian Chadd             //AR_WOW_OFFLOAD_ENA_ARP_OFFLOAD    |
103176bd547bSAdrian Chadd             //AR_WOW_OFFLOAD_ENA_NS_OFFLOAD     |
103276bd547bSAdrian Chadd             //AR_WOW_OFFLOAD_ENA_ACER_MAGIC     |
103376bd547bSAdrian Chadd             //AR_WOW_OFFLOAD_ENA_STD_MAGIC      |
103476bd547bSAdrian Chadd             //AR_WOW_OFFLOAD_ENA_4WAY_WAKE      |
103576bd547bSAdrian Chadd             //AR_WOW_OFFLOAD_ENA_SWKA           |
103676bd547bSAdrian Chadd             //AR_WOW_OFFLOAD_ENA_BT_SLEEP       |
103776bd547bSAdrian Chadd             AR_WOW_OFFLOAD_ENA_SW_NULL;
103876bd547bSAdrian Chadd #endif
103976bd547bSAdrian Chadd 
104076bd547bSAdrian Chadd     /*
104176bd547bSAdrian Chadd      * ah_wow_event_mask is a mask to the AR_WOW_PATTERN_REG register to
104276bd547bSAdrian Chadd      * indicate which WOW events that we have enabled. The WOW Events are
104376bd547bSAdrian Chadd      * from the pattern_enable in this function and pattern_count of
104476bd547bSAdrian Chadd      * ar9300_wow_apply_pattern()
104576bd547bSAdrian Chadd      */
104676bd547bSAdrian Chadd     wow_event_mask = AH_PRIVATE(ah)->ah_wow_event_mask;
104776bd547bSAdrian Chadd 
104876bd547bSAdrian Chadd     HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE,
104976bd547bSAdrian Chadd         "%s: offload: %d, pattern: %08x, event_mask: %08x\n",
105076bd547bSAdrian Chadd         __func__, offloadEnable, pattern_enable, wow_event_mask);
105176bd547bSAdrian Chadd 
105276bd547bSAdrian Chadd     /*
105376bd547bSAdrian Chadd      * Untie Power-On-Reset from the PCI-E Reset. When we are in WOW sleep,
105476bd547bSAdrian Chadd      * we do not want the Reset from the PCI-E to disturb our hw state.
105576bd547bSAdrian Chadd      */
105676bd547bSAdrian Chadd     if (AH_PRIVATE(ah)->ah_is_pci_express == AH_TRUE) {
105776bd547bSAdrian Chadd 
105876bd547bSAdrian Chadd         u_int32_t wa_reg_val;
105976bd547bSAdrian Chadd         /*
106076bd547bSAdrian Chadd          * We need to untie the internal POR (power-on-reset) to the external
106176bd547bSAdrian Chadd          * PCI-E reset. We also need to tie the PCI-E Phy reset to the PCI-E
106276bd547bSAdrian Chadd          * reset.
106376bd547bSAdrian Chadd          */
1064e113789bSAdrian Chadd         HAL_DEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE,
106576bd547bSAdrian Chadd             "%s: Untie POR and PCIE reset\n", __func__);
106676bd547bSAdrian Chadd         wa_reg_val = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_WA));
106776bd547bSAdrian Chadd         wa_reg_val = wa_reg_val & ~(AR_WA_UNTIE_RESET_EN);
106876bd547bSAdrian Chadd         wa_reg_val = wa_reg_val | AR_WA_RESET_EN | AR_WA_POR_SHORT;
106976bd547bSAdrian Chadd         /*
107076bd547bSAdrian Chadd          * This bit is to bypass the EEPROM/OTP state machine, (by clearing its
107176bd547bSAdrian Chadd          * busy state while PCIE_rst is asserted), to allow BT embedded CPU
107276bd547bSAdrian Chadd          * be able to access WLAN registers. Otherwise the eCPU access will be
107376bd547bSAdrian Chadd          * stalled as eeprom_sm is held in busy state.
107476bd547bSAdrian Chadd          *
107576bd547bSAdrian Chadd          * EV91928 is that when this bit is set, after host wakeup and PCIE_rst
107676bd547bSAdrian Chadd          * deasserted, PCIE configuration registers will be reset and DeviceID
107776bd547bSAdrian Chadd          * SubsystemID etc. registers will be different from values before
107876bd547bSAdrian Chadd          * entering sleep. This will cause Windows to detect a device removal.
107976bd547bSAdrian Chadd          *
108076bd547bSAdrian Chadd          * For HW WOW, this bit should keep as cleared.
108176bd547bSAdrian Chadd          */
108276bd547bSAdrian Chadd         if (offloadEnable) {
108376bd547bSAdrian Chadd             HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE,
108476bd547bSAdrian Chadd                 "%s: Set AR_WA.13 COLD_RESET_OVERRIDE\n", __func__);
108576bd547bSAdrian Chadd             wa_reg_val = wa_reg_val | AR_WA_COLD_RESET_OVERRIDE;
108676bd547bSAdrian Chadd 
108776bd547bSAdrian Chadd #if ATH_WOW_OFFLOAD
108876bd547bSAdrian Chadd             if (AR_SREV_JUPITER(ah)) {
108976bd547bSAdrian Chadd                 wa_reg_val = wa_reg_val | AR_WA_D3_TO_L1_DISABLE;
109076bd547bSAdrian Chadd             }
109176bd547bSAdrian Chadd #endif
109276bd547bSAdrian Chadd         }
109376bd547bSAdrian Chadd         OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA), wa_reg_val);
109476bd547bSAdrian Chadd     }
109576bd547bSAdrian Chadd 
109676bd547bSAdrian Chadd     /*
109776bd547bSAdrian Chadd      * Set the power states appropriately and enable pme.
109876bd547bSAdrian Chadd      */
109976bd547bSAdrian Chadd     val = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL));
110076bd547bSAdrian Chadd     val |=
110176bd547bSAdrian Chadd         AR_PMCTRL_HOST_PME_EN     |
110276bd547bSAdrian Chadd         AR_PMCTRL_PWR_PM_CTRL_ENA |
110376bd547bSAdrian Chadd         AR_PMCTRL_AUX_PWR_DET;
110476bd547bSAdrian Chadd 
110576bd547bSAdrian Chadd     /*
110676bd547bSAdrian Chadd      * Set and clear WOW_PME_CLEAR registers for the chip to generate next
110776bd547bSAdrian Chadd      * wow signal.
110876bd547bSAdrian Chadd      */
110976bd547bSAdrian Chadd     val |= AR_PMCTRL_WOW_PME_CLR;
111076bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL), val);
111176bd547bSAdrian Chadd     val &= ~AR_PMCTRL_WOW_PME_CLR;
111276bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL), val);
111376bd547bSAdrian Chadd 
111476bd547bSAdrian Chadd     /*
111576bd547bSAdrian Chadd      * Setup for for:
111676bd547bSAdrian Chadd      *     - beacon misses
111776bd547bSAdrian Chadd      *     - magic pattern
111876bd547bSAdrian Chadd      *     - keep alive timeout
111976bd547bSAdrian Chadd      *     - pattern matching
112076bd547bSAdrian Chadd      */
112176bd547bSAdrian Chadd 
112276bd547bSAdrian Chadd     /*
112376bd547bSAdrian Chadd      * Program some default values for keep-alives, beacon misses, etc.
112476bd547bSAdrian Chadd      */
112576bd547bSAdrian Chadd     init_val = OS_REG_READ(ah, AR_WOW_PATTERN_REG);
112676bd547bSAdrian Chadd     val = AR_WOW_BACK_OFF_SHIFT(AR_WOW_PAT_BACKOFF) | init_val;
112776bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_WOW_PATTERN_REG, val);
112876bd547bSAdrian Chadd     rval = OS_REG_READ(ah, AR_WOW_PATTERN_REG);
112976bd547bSAdrian Chadd 
113076bd547bSAdrian Chadd     val =
113176bd547bSAdrian Chadd         AR_WOW_AIFS_CNT(AR_WOW_CNT_AIFS_CNT) |
113276bd547bSAdrian Chadd         AR_WOW_SLOT_CNT(AR_WOW_CNT_SLOT_CNT) |
113376bd547bSAdrian Chadd         AR_WOW_KEEP_ALIVE_CNT(AR_WOW_CNT_KA_CNT);
113476bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_WOW_COUNT_REG, val);
113576bd547bSAdrian Chadd     rval = OS_REG_READ(ah, AR_WOW_COUNT_REG);
113676bd547bSAdrian Chadd 
113776bd547bSAdrian Chadd     if (pattern_enable & AH_WOW_BEACON_MISS) {
113876bd547bSAdrian Chadd         val = AR_WOW_BEACON_TIMO;
113976bd547bSAdrian Chadd     } else {
114076bd547bSAdrian Chadd         /* We are not using the beacon miss. Program a large value. */
114176bd547bSAdrian Chadd         val = AR_WOW_BEACON_TIMO_MAX;
114276bd547bSAdrian Chadd     }
114376bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_WOW_BCN_TIMO_REG, val);
114476bd547bSAdrian Chadd     rval = OS_REG_READ(ah, AR_WOW_BCN_TIMO_REG);
114576bd547bSAdrian Chadd 
114676bd547bSAdrian Chadd     /*
114776bd547bSAdrian Chadd      * Keep Alive Timo in ms.
114876bd547bSAdrian Chadd      */
114976bd547bSAdrian Chadd     if (pattern_enable == 0) {
115076bd547bSAdrian Chadd         val =  AR_WOW_KEEP_ALIVE_NEVER;
115176bd547bSAdrian Chadd     } else {
115276bd547bSAdrian Chadd         val =  AH_PRIVATE(ah)->ah_config.ath_hal_keep_alive_timeout * 32;
115376bd547bSAdrian Chadd     }
115476bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_WOW_KEEP_ALIVE_TIMO_REG, val);
115576bd547bSAdrian Chadd     rval = OS_REG_READ(ah, AR_WOW_KEEP_ALIVE_TIMO_REG);
115676bd547bSAdrian Chadd 
115776bd547bSAdrian Chadd     /*
115876bd547bSAdrian Chadd      * Keep Alive delay in us.
115976bd547bSAdrian Chadd      */
116076bd547bSAdrian Chadd     val = ka_delay * 1000;
116176bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_WOW_KEEP_ALIVE_DELAY_REG, val);
116276bd547bSAdrian Chadd     rval = OS_REG_READ(ah, AR_WOW_KEEP_ALIVE_DELAY_REG);
116376bd547bSAdrian Chadd 
116476bd547bSAdrian Chadd     /*
116576bd547bSAdrian Chadd      * Create keep_alive Pattern to respond to beacons.
116676bd547bSAdrian Chadd      */
116776bd547bSAdrian Chadd     ar9300_wow_create_keep_alive_pattern(ah);
116876bd547bSAdrian Chadd 
116976bd547bSAdrian Chadd     /*
117076bd547bSAdrian Chadd      * Configure Mac Wow Registers.
117176bd547bSAdrian Chadd      */
117276bd547bSAdrian Chadd 
117376bd547bSAdrian Chadd     val = OS_REG_READ(ah, AR_WOW_KEEP_ALIVE_REG);
117476bd547bSAdrian Chadd 
117576bd547bSAdrian Chadd     /*
117676bd547bSAdrian Chadd      * Send keep alive timeouts anyway.
117776bd547bSAdrian Chadd      */
117876bd547bSAdrian Chadd     val &= ~AR_WOW_KEEP_ALIVE_AUTO_DIS;
117976bd547bSAdrian Chadd 
118076bd547bSAdrian Chadd     if (pattern_enable & AH_WOW_LINK_CHANGE) {
118176bd547bSAdrian Chadd         val &= ~ AR_WOW_KEEP_ALIVE_FAIL_DIS;
118276bd547bSAdrian Chadd         wow_event_mask |= AR_WOW_KEEP_ALIVE_FAIL;
118376bd547bSAdrian Chadd     } else {
118476bd547bSAdrian Chadd         val |=  AR_WOW_KEEP_ALIVE_FAIL_DIS;
118576bd547bSAdrian Chadd     }
118676bd547bSAdrian Chadd #if ATH_WOW_OFFLOAD
118776bd547bSAdrian Chadd     if (offloadEnable) {
118876bd547bSAdrian Chadd         /* Don't enable KA frames yet. BT CPU is not
118976bd547bSAdrian Chadd          * yet ready. */
119076bd547bSAdrian Chadd     }
119176bd547bSAdrian Chadd     else
119276bd547bSAdrian Chadd #endif /* ATH_WOW_OFFLOAD */
119376bd547bSAdrian Chadd     {
119476bd547bSAdrian Chadd         OS_REG_WRITE(ah, AR_WOW_KEEP_ALIVE_REG, val);
119576bd547bSAdrian Chadd         val = OS_REG_READ(ah, AR_WOW_KEEP_ALIVE_REG);
119676bd547bSAdrian Chadd     }
119776bd547bSAdrian Chadd 
119876bd547bSAdrian Chadd 
119976bd547bSAdrian Chadd     /*
120076bd547bSAdrian Chadd      * We are relying on a bmiss failure. Ensure we have enough
120176bd547bSAdrian Chadd      * threshold to prevent AH_FALSE positives.
120276bd547bSAdrian Chadd      */
120376bd547bSAdrian Chadd     OS_REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_THR_BM_THR,
120476bd547bSAdrian Chadd         AR_WOW_BMISSTHRESHOLD);
120576bd547bSAdrian Chadd 
120676bd547bSAdrian Chadd     val = OS_REG_READ(ah, AR_WOW_BCN_EN_REG);
120776bd547bSAdrian Chadd     if (pattern_enable & AH_WOW_BEACON_MISS) {
120876bd547bSAdrian Chadd         val |= AR_WOW_BEACON_FAIL_EN;
120976bd547bSAdrian Chadd         wow_event_mask |= AR_WOW_BEACON_FAIL;
121076bd547bSAdrian Chadd     } else {
121176bd547bSAdrian Chadd         val &= ~AR_WOW_BEACON_FAIL_EN;
121276bd547bSAdrian Chadd     }
121376bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_WOW_BCN_EN_REG, val);
121476bd547bSAdrian Chadd     val = OS_REG_READ(ah, AR_WOW_BCN_EN_REG);
121576bd547bSAdrian Chadd 
121676bd547bSAdrian Chadd     /*
121776bd547bSAdrian Chadd      * Enable the magic packet registers.
121876bd547bSAdrian Chadd      */
121976bd547bSAdrian Chadd     val = OS_REG_READ(ah, AR_WOW_PATTERN_REG);
122076bd547bSAdrian Chadd     if ((pattern_enable & AH_WOW_MAGIC_PATTERN_EN)
122176bd547bSAdrian Chadd #if ATH_WOW_OFFLOAD
122276bd547bSAdrian Chadd         || (pattern_enable & AH_WOW_ACER_MAGIC_EN)
122376bd547bSAdrian Chadd #endif
122476bd547bSAdrian Chadd         )
122576bd547bSAdrian Chadd     {
122676bd547bSAdrian Chadd         val |= AR_WOW_MAGIC_EN;
122776bd547bSAdrian Chadd         wow_event_mask |= AR_WOW_MAGIC_PAT_FOUND;
122876bd547bSAdrian Chadd     } else {
122976bd547bSAdrian Chadd         val &= ~AR_WOW_MAGIC_EN;
123076bd547bSAdrian Chadd     }
123176bd547bSAdrian Chadd     val |= AR_WOW_MAC_INTR_EN;
123276bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_WOW_PATTERN_REG, val);
123376bd547bSAdrian Chadd     val = OS_REG_READ(ah, AR_WOW_PATTERN_REG);
123476bd547bSAdrian Chadd 
123576bd547bSAdrian Chadd #if ATH_WOW_OFFLOAD
123676bd547bSAdrian Chadd     if (HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_FORCE_BT_SLEEP)) {
123776bd547bSAdrian Chadd         wow_feature_enable |= AR_WOW_OFFLOAD_ENA_BT_SLEEP;
123876bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - BT SLEEP\n");
123976bd547bSAdrian Chadd     } else {
124076bd547bSAdrian Chadd         wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_BT_SLEEP;
124176bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - BT SLEEP\n");
124276bd547bSAdrian Chadd     }
124376bd547bSAdrian Chadd 
124476bd547bSAdrian Chadd     if (HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_SW_NULL_DISABLE)) {
124576bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - SW NULL\n");
124676bd547bSAdrian Chadd         wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_SW_NULL;
124776bd547bSAdrian Chadd     } else {
124876bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - SW NULL\n");
124976bd547bSAdrian Chadd         wow_feature_enable |= AR_WOW_OFFLOAD_ENA_SW_NULL;
125076bd547bSAdrian Chadd     }
125176bd547bSAdrian Chadd 
125276bd547bSAdrian Chadd     if (HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_DEVID_SWAR_DISABLE)) {
125376bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - DevID SWAR\n");
125476bd547bSAdrian Chadd         wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_DEVID_SWAR;
125576bd547bSAdrian Chadd     } else {
125676bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - DevID SWAR\n");
125776bd547bSAdrian Chadd         wow_feature_enable |= AR_WOW_OFFLOAD_ENA_DEVID_SWAR;
125876bd547bSAdrian Chadd     }
125976bd547bSAdrian Chadd 
126076bd547bSAdrian Chadd     if (pattern_enable & AH_WOW_ACER_KEEP_ALIVE_EN) {
126176bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - Acer SWKA\n");
126276bd547bSAdrian Chadd         wow_feature_enable |= AR_WOW_OFFLOAD_ENA_SWKA;
126376bd547bSAdrian Chadd     } else {
126476bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - Acer SWKA\n");
126576bd547bSAdrian Chadd         wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_SWKA;
126676bd547bSAdrian Chadd     }
126776bd547bSAdrian Chadd 
126876bd547bSAdrian Chadd     if (pattern_enable & AH_WOW_ACER_MAGIC_EN) {
126976bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - Standard Magic\n");
127076bd547bSAdrian Chadd         wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_STD_MAGIC;
127176bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - Acer Magic\n");
127276bd547bSAdrian Chadd         wow_feature_enable |= AR_WOW_OFFLOAD_ENA_ACER_MAGIC;
127376bd547bSAdrian Chadd     } else {
127476bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - Standard Magic\n");
127576bd547bSAdrian Chadd         wow_feature_enable |= AR_WOW_OFFLOAD_ENA_STD_MAGIC;
127676bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - Acer Magic\n");
127776bd547bSAdrian Chadd         wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_ACER_MAGIC;
127876bd547bSAdrian Chadd     }
127976bd547bSAdrian Chadd 
128076bd547bSAdrian Chadd     if ((pattern_enable & AH_WOW_4WAY_HANDSHAKE_EN) ||
128176bd547bSAdrian Chadd         HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_FORCE_4WAY_HS_WAKE)) {
128276bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - 4Way Handshake\n");
128376bd547bSAdrian Chadd         wow_feature_enable |= AR_WOW_OFFLOAD_ENA_4WAY_WAKE;
128476bd547bSAdrian Chadd     } else {
128576bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - 4Way Handshake\n");
128676bd547bSAdrian Chadd         wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_4WAY_WAKE;
128776bd547bSAdrian Chadd     }
128876bd547bSAdrian Chadd 
128976bd547bSAdrian Chadd     if((pattern_enable & AH_WOW_AP_ASSOCIATION_LOST_EN) ||
129076bd547bSAdrian Chadd         HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_FORCE_AP_LOSS_WAKE))
129176bd547bSAdrian Chadd     {
129276bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - AP loss wake\n");
129376bd547bSAdrian Chadd         wow_feature_enable |= AR_WOW_OFFLOAD_ENA_AP_LOSS_WAKE;
129476bd547bSAdrian Chadd     } else {
129576bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - AP loss wake\n");
129676bd547bSAdrian Chadd         wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_AP_LOSS_WAKE;
129776bd547bSAdrian Chadd     }
129876bd547bSAdrian Chadd 
129976bd547bSAdrian Chadd     if((pattern_enable & AH_WOW_GTK_HANDSHAKE_ERROR_EN) ||
130076bd547bSAdrian Chadd         HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_FORCE_GTK_ERR_WAKE))
130176bd547bSAdrian Chadd     {
130276bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - GTK error wake\n");
130376bd547bSAdrian Chadd         wow_feature_enable |= AR_WOW_OFFLOAD_ENA_GTK_ERROR_WAKE;
130476bd547bSAdrian Chadd     } else {
130576bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - GTK error wake\n");
130676bd547bSAdrian Chadd         wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_GTK_ERROR_WAKE;
130776bd547bSAdrian Chadd     }
130876bd547bSAdrian Chadd 
130976bd547bSAdrian Chadd     if (pattern_enable & AH_WOW_GTK_OFFLOAD_EN) {
131076bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - GTK offload\n");
131176bd547bSAdrian Chadd         wow_feature_enable |= AR_WOW_OFFLOAD_ENA_GTK;
131276bd547bSAdrian Chadd     } else {
131376bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - GTK offload\n");
131476bd547bSAdrian Chadd         wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_GTK;
131576bd547bSAdrian Chadd     }
131676bd547bSAdrian Chadd 
131776bd547bSAdrian Chadd     if (pattern_enable & AH_WOW_ARP_OFFLOAD_EN) {
131876bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - ARP offload\n");
131976bd547bSAdrian Chadd         wow_feature_enable |= AR_WOW_OFFLOAD_ENA_ARP_OFFLOAD;
132076bd547bSAdrian Chadd     } else {
132176bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - ARP offload\n");
132276bd547bSAdrian Chadd         wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_ARP_OFFLOAD;
132376bd547bSAdrian Chadd     }
132476bd547bSAdrian Chadd 
132576bd547bSAdrian Chadd     if (pattern_enable & AH_WOW_NS_OFFLOAD_EN) {
132676bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - NS offload\n");
132776bd547bSAdrian Chadd         wow_feature_enable |= AR_WOW_OFFLOAD_ENA_NS_OFFLOAD;
132876bd547bSAdrian Chadd     } else {
132976bd547bSAdrian Chadd         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - NS offload\n");
133076bd547bSAdrian Chadd         wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_NS_OFFLOAD;
133176bd547bSAdrian Chadd     }
133276bd547bSAdrian Chadd 
133376bd547bSAdrian Chadd #endif /* ATH_WOW_OFFLOAD */
133476bd547bSAdrian Chadd 
133576bd547bSAdrian Chadd     /* For Kite and later version of the chips
133676bd547bSAdrian Chadd      * enable wow pattern match for packets less than
133776bd547bSAdrian Chadd      * 256 bytes for all patterns.
133876bd547bSAdrian Chadd      */
133976bd547bSAdrian Chadd     /* XXX */
134076bd547bSAdrian Chadd     OS_REG_WRITE(
134176bd547bSAdrian Chadd         ah, AR_WOW_PATTERN_MATCH_LT_256B_REG, AR_WOW_PATTERN_SUPPORTED);
134276bd547bSAdrian Chadd 
134376bd547bSAdrian Chadd     /*
134476bd547bSAdrian Chadd      * Set the power states appropriately and enable PME.
134576bd547bSAdrian Chadd      */
134676bd547bSAdrian Chadd     val = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL));
134776bd547bSAdrian Chadd     val |=
134876bd547bSAdrian Chadd         AR_PMCTRL_PWR_STATE_D1D3 |
134976bd547bSAdrian Chadd         AR_PMCTRL_HOST_PME_EN    |
135076bd547bSAdrian Chadd         AR_PMCTRL_PWR_PM_CTRL_ENA;
135176bd547bSAdrian Chadd     val &= ~AR_PCIE_PM_CTRL_ENA;
135276bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL), val);
135376bd547bSAdrian Chadd 
135476bd547bSAdrian Chadd     /* Wake on Timer Interrupt. Test mode only. Used in Manufacturing line. */
135576bd547bSAdrian Chadd     if (timeout_in_seconds) {
135676bd547bSAdrian Chadd         /* convert Timeout to u_secs */
135776bd547bSAdrian Chadd         OS_REG_WRITE(ah, AR_NEXT_NDP_TIMER,
135876bd547bSAdrian Chadd             OS_REG_READ(ah, AR_TSF_L32) + timeout_in_seconds * 1000000 );
135976bd547bSAdrian Chadd         /* timer_period = 30 seconds always */
136076bd547bSAdrian Chadd         OS_REG_WRITE(ah, AR_NDP_PERIOD, 30 * 1000000);
136176bd547bSAdrian Chadd         OS_REG_WRITE(ah, AR_TIMER_MODE, OS_REG_READ(ah, AR_TIMER_MODE) | 0x80);
136276bd547bSAdrian Chadd         OS_REG_WRITE(ah, AR_IMR_S5, OS_REG_READ(ah, AR_IMR_S5) | 0x80);
136376bd547bSAdrian Chadd         OS_REG_WRITE(ah, AR_IMR, OS_REG_READ(ah, AR_IMR) | AR_IMR_GENTMR);
136476bd547bSAdrian Chadd         if (clearbssid) {
136576bd547bSAdrian Chadd             OS_REG_WRITE(ah, AR_BSS_ID0, 0);
136676bd547bSAdrian Chadd             OS_REG_WRITE(ah, AR_BSS_ID1, 0);
136776bd547bSAdrian Chadd         }
136876bd547bSAdrian Chadd     }
136976bd547bSAdrian Chadd 
137076bd547bSAdrian Chadd     /* Enable Seq# generation when asleep. */
137176bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_STA_ID1,
137276bd547bSAdrian Chadd                      OS_REG_READ(ah, AR_STA_ID1) & ~AR_STA_ID1_PRESERVE_SEQNUM);
137376bd547bSAdrian Chadd 
137476bd547bSAdrian Chadd     AH_PRIVATE(ah)->ah_wow_event_mask = wow_event_mask;
137576bd547bSAdrian Chadd 
137676bd547bSAdrian Chadd #if ATH_WOW_OFFLOAD
137776bd547bSAdrian Chadd     if (offloadEnable) {
137876bd547bSAdrian Chadd         /* Force MAC awake before entering SW WoW mode */
137976bd547bSAdrian Chadd         OS_REG_SET_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN);
138076bd547bSAdrian Chadd #if ATH_SUPPORT_MCI
1381e113789bSAdrian Chadd         if (AH_PRIVATE(ah)->ah_caps.halMciSupport) {
138276bd547bSAdrian Chadd             OS_REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
138376bd547bSAdrian Chadd         }
138476bd547bSAdrian Chadd #endif
138576bd547bSAdrian Chadd 
138676bd547bSAdrian Chadd         OS_REG_WRITE(ah, AR_WOW_OFFLOAD_COMMAND_JUPITER, wow_feature_enable);
138776bd547bSAdrian Chadd         OS_REG_WRITE(ah, AR_WOW_OFFLOAD_STATUS_JUPITER, 0x0);
138876bd547bSAdrian Chadd         if (wow_feature_enable & AR_WOW_OFFLOAD_ENA_SW_NULL) {
138976bd547bSAdrian Chadd             OS_REG_WRITE(ah, AR_WOW_SW_NULL_PARAMETER,
139076bd547bSAdrian Chadd                 ((1000) |
139176bd547bSAdrian Chadd                 (4 << AR_WOW_SW_NULL_SHORT_PERIOD_MASK_S)));
139276bd547bSAdrian Chadd         }
139376bd547bSAdrian Chadd 
139476bd547bSAdrian Chadd         if (wow_feature_enable & AR_WOW_OFFLOAD_ENA_DEVID_SWAR) {
139576bd547bSAdrian Chadd             ar9300_wowoffload_download_devid_swar(ah);
139676bd547bSAdrian Chadd         }
139776bd547bSAdrian Chadd 
139876bd547bSAdrian Chadd         ar9300_wow_offload_download_hal_params(ah);
139976bd547bSAdrian Chadd         ar9300_wow_offload_handshake(ah, pattern_enable);
140076bd547bSAdrian Chadd         AH9300(ah)->ah_chip_full_sleep = AH_FALSE;
140176bd547bSAdrian Chadd 
140276bd547bSAdrian Chadd         //OS_REG_SET_BIT(ah, AR_SW_WOW_CONTROL, AR_HW_WOW_DISABLE);
140376bd547bSAdrian Chadd     }
140476bd547bSAdrian Chadd     else
140576bd547bSAdrian Chadd #endif /* ATH_WOW_OFFLOAD */
140676bd547bSAdrian Chadd     {
140776bd547bSAdrian Chadd #if ATH_SUPPORT_MCI
1408e113789bSAdrian Chadd         if (AH_PRIVATE(ah)->ah_caps.halMciSupport) {
140976bd547bSAdrian Chadd             OS_REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
141076bd547bSAdrian Chadd         }
141176bd547bSAdrian Chadd #endif
141276bd547bSAdrian Chadd         ar9300_set_power_mode_wow_sleep(ah);
141376bd547bSAdrian Chadd         AH9300(ah)->ah_chip_full_sleep = AH_TRUE;
141476bd547bSAdrian Chadd     }
141576bd547bSAdrian Chadd 
141676bd547bSAdrian Chadd     return (AH_TRUE);
141776bd547bSAdrian Chadd }
141876bd547bSAdrian Chadd 
141976bd547bSAdrian Chadd u_int32_t
142076bd547bSAdrian Chadd //ar9300_wow_wake_up(struct ath_hal *ah, u_int8_t  *chipPatternBytes)
ar9300_wow_wake_up(struct ath_hal * ah,HAL_BOOL offloadEnabled)142176bd547bSAdrian Chadd ar9300_wow_wake_up(struct ath_hal *ah, HAL_BOOL offloadEnabled)
142276bd547bSAdrian Chadd {
142376bd547bSAdrian Chadd     uint32_t wow_status = 0;
142476bd547bSAdrian Chadd     uint32_t val = 0, rval;
142576bd547bSAdrian Chadd 
142676bd547bSAdrian Chadd     OS_REG_CLR_BIT(ah, AR_SW_WOW_CONTROL, AR_HW_WOW_DISABLE);
142776bd547bSAdrian Chadd     OS_REG_CLR_BIT(ah, AR_SW_WOW_CONTROL, AR_SW_WOW_ENABLE);
142876bd547bSAdrian Chadd 
142976bd547bSAdrian Chadd #if ATH_WOW_OFFLOAD
143076bd547bSAdrian Chadd     /* If WoW was offloaded to embedded CPU, use the global
143176bd547bSAdrian Chadd      * shared register to know the wakeup reason */
143276bd547bSAdrian Chadd     if (offloadEnabled) {
143376bd547bSAdrian Chadd         val = OS_REG_READ(ah, AR_EMB_CPU_WOW_STATUS);
143476bd547bSAdrian Chadd         if (val) {
143576bd547bSAdrian Chadd             if (val & AR_EMB_CPU_WOW_STATUS_MAGIC_PATTERN) {
143676bd547bSAdrian Chadd                 HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) SW MAGIC_PATTERN\n");
143776bd547bSAdrian Chadd                 wow_status |= AH_WOW_MAGIC_PATTERN_EN;
143876bd547bSAdrian Chadd             }
143976bd547bSAdrian Chadd             if (val & AR_EMB_CPU_WOW_STATUS_PATTERN_MATCH) {
144076bd547bSAdrian Chadd                 HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) SW USER_PATTERN\n");
144176bd547bSAdrian Chadd                 wow_status |= AH_WOW_USER_PATTERN_EN;
144276bd547bSAdrian Chadd             }
144376bd547bSAdrian Chadd             if (val & AR_EMB_CPU_WOW_STATUS_KEEP_ALIVE_FAIL) {
144476bd547bSAdrian Chadd                 HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) SW KEEP_ALIVE_FAIL\n");
144576bd547bSAdrian Chadd                 wow_status |= AH_WOW_LINK_CHANGE;
144676bd547bSAdrian Chadd             }
144776bd547bSAdrian Chadd             if (val & AR_EMB_CPU_WOW_STATUS_BEACON_MISS) {
144876bd547bSAdrian Chadd                 HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) SW BEACON_FAIL\n");
144976bd547bSAdrian Chadd                 wow_status |= AH_WOW_BEACON_MISS;
145076bd547bSAdrian Chadd             }
145176bd547bSAdrian Chadd         }
145276bd547bSAdrian Chadd 
145376bd547bSAdrian Chadd         /* Clear status and mask registers */
145476bd547bSAdrian Chadd         OS_REG_WRITE(ah, AR_EMB_CPU_WOW_STATUS, 0x0);
145576bd547bSAdrian Chadd         OS_REG_WRITE(ah, AR_EMB_CPU_WOW_ENABLE, 0);
145676bd547bSAdrian Chadd         OS_REG_WRITE(ah, AR_MBOX_CTRL_STATUS, 0);
145776bd547bSAdrian Chadd 
145876bd547bSAdrian Chadd     }
145976bd547bSAdrian Chadd     else
146076bd547bSAdrian Chadd #endif /* ATH_WOW_OFFLOAD */
146176bd547bSAdrian Chadd     {
146276bd547bSAdrian Chadd         /*
146376bd547bSAdrian Chadd          * Read the WOW Status register to know the wakeup reason.
146476bd547bSAdrian Chadd          */
146576bd547bSAdrian Chadd         rval = OS_REG_READ(ah, AR_WOW_PATTERN_REG);
146676bd547bSAdrian Chadd         val = AR_WOW_STATUS(rval);
146776bd547bSAdrian Chadd 
146876bd547bSAdrian Chadd         /*
146976bd547bSAdrian Chadd          * Mask only the WOW events that we have enabled.
147076bd547bSAdrian Chadd          * Sometimes we have spurious WOW events from the AR_WOW_PATTERN_REG
147176bd547bSAdrian Chadd          * register. This mask will clean it up.
147276bd547bSAdrian Chadd          */
147376bd547bSAdrian Chadd         val &= AH_PRIVATE(ah)->ah_wow_event_mask;
147476bd547bSAdrian Chadd 
147576bd547bSAdrian Chadd         if (val) {
147676bd547bSAdrian Chadd             if (val & AR_WOW_MAGIC_PAT_FOUND) {
147776bd547bSAdrian Chadd                 HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) HW MAGIC_PATTERN\n");
147876bd547bSAdrian Chadd                 wow_status |= AH_WOW_MAGIC_PATTERN_EN;
147976bd547bSAdrian Chadd             }
148076bd547bSAdrian Chadd             if (AR_WOW_PATTERN_FOUND(val)) {
148176bd547bSAdrian Chadd                 //int  i, offset;
148276bd547bSAdrian Chadd                 //offset = OS_REG_READ(ah, AR_WOW_RXBUF_START_ADDR);
148376bd547bSAdrian Chadd                 //// Read matched pattern for wake packet detection indication.
148476bd547bSAdrian Chadd                 //for( i = 0; i< MAX_PATTERN_SIZE/4; i+=4)
148576bd547bSAdrian Chadd                 //{
148676bd547bSAdrian Chadd                 //    // RX FIFO is only 8K wrapping.
148776bd547bSAdrian Chadd                 //    if(offset >= 8 * 1024 / 4) offset = 0;
148876bd547bSAdrian Chadd                 //    *(u_int32_t*)(chipPatternBytes + i) = OS_REG_READ( ah,offset );
148976bd547bSAdrian Chadd                 //    offset++;
149076bd547bSAdrian Chadd                 //}
149176bd547bSAdrian Chadd                 wow_status |= AH_WOW_USER_PATTERN_EN;
149276bd547bSAdrian Chadd                 HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) HW USER_PATTERN\n");
149376bd547bSAdrian Chadd             }
149476bd547bSAdrian Chadd             if (val & AR_WOW_KEEP_ALIVE_FAIL) {
149576bd547bSAdrian Chadd                 HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) HW KEEP_ALIVE_FAIL\n");
149676bd547bSAdrian Chadd                 wow_status |= AH_WOW_LINK_CHANGE;
149776bd547bSAdrian Chadd             }
149876bd547bSAdrian Chadd             if (val & AR_WOW_BEACON_FAIL) {
149976bd547bSAdrian Chadd                 HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) HW BEACON_FAIL\n");
150076bd547bSAdrian Chadd                 wow_status |= AH_WOW_BEACON_MISS;
150176bd547bSAdrian Chadd             }
150276bd547bSAdrian Chadd         }
150376bd547bSAdrian Chadd     }
150476bd547bSAdrian Chadd 
150576bd547bSAdrian Chadd     /*
150676bd547bSAdrian Chadd      * Set and clear WOW_PME_CLEAR registers for the chip to generate next
150776bd547bSAdrian Chadd      * wow signal.
150876bd547bSAdrian Chadd      * Disable D3 before accessing other registers ?
150976bd547bSAdrian Chadd      */
151076bd547bSAdrian Chadd     val = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL));
151176bd547bSAdrian Chadd     /* Check the bit value 0x01000000 (7-10)? */
151276bd547bSAdrian Chadd     val &= ~AR_PMCTRL_PWR_STATE_D1D3;
151376bd547bSAdrian Chadd     val |= AR_PMCTRL_WOW_PME_CLR;
151476bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL), val);
151576bd547bSAdrian Chadd 
151676bd547bSAdrian Chadd     /*
151776bd547bSAdrian Chadd      * Clear all events.
151876bd547bSAdrian Chadd      */
151976bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_WOW_PATTERN_REG,
152076bd547bSAdrian Chadd         AR_WOW_CLEAR_EVENTS(OS_REG_READ(ah, AR_WOW_PATTERN_REG)));
152176bd547bSAdrian Chadd 
152276bd547bSAdrian Chadd     //HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE,
152376bd547bSAdrian Chadd     //    "%s: Skip PCIE WA programming\n", __func__);
152476bd547bSAdrian Chadd #if 0
152576bd547bSAdrian Chadd     /*
152676bd547bSAdrian Chadd      * Tie reset register.
152776bd547bSAdrian Chadd      * FIXME: Per David Quan not tieing it back might have some repurcussions.
152876bd547bSAdrian Chadd      */
152976bd547bSAdrian Chadd     /* XXX */
153076bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA), OS_REG_READ(ah, AR_WA) |
153176bd547bSAdrian Chadd             AR_WA_UNTIE_RESET_EN | AR_WA_POR_SHORT | AR_WA_RESET_EN);
153276bd547bSAdrian Chadd #endif
153376bd547bSAdrian Chadd 
153476bd547bSAdrian Chadd     /* Restore the Beacon Threshold to init value */
153576bd547bSAdrian Chadd     OS_REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_THR_BM_THR, INIT_RSSI_THR);
153676bd547bSAdrian Chadd 
153776bd547bSAdrian Chadd     /*
153876bd547bSAdrian Chadd      * Restore the way the PCI-E Reset, Power-On-Reset, external PCIE_POR_SHORT
153976bd547bSAdrian Chadd      * pins are tied to its original value. Previously just before WOW sleep,
154076bd547bSAdrian Chadd      * we untie the PCI-E Reset to our Chip's Power On Reset so that
154176bd547bSAdrian Chadd      * any PCI-E reset from the bus will not reset our chip.
154276bd547bSAdrian Chadd      */
154376bd547bSAdrian Chadd     HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE, "%s: restore AR_WA\n", __func__);
154476bd547bSAdrian Chadd     if (AH_PRIVATE(ah)->ah_is_pci_express == AH_TRUE) {
154576bd547bSAdrian Chadd         ar9300_config_pci_power_save(ah, 0, 0);
154676bd547bSAdrian Chadd     }
154776bd547bSAdrian Chadd 
154876bd547bSAdrian Chadd     AH_PRIVATE(ah)->ah_wow_event_mask = 0;
154976bd547bSAdrian Chadd     HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE,
155076bd547bSAdrian Chadd         "(WOW) wow_status=%08x\n", wow_status);
155176bd547bSAdrian Chadd 
155276bd547bSAdrian Chadd     return (wow_status);
155376bd547bSAdrian Chadd }
155476bd547bSAdrian Chadd 
155576bd547bSAdrian Chadd void
ar9300_wow_set_gpio_reset_low(struct ath_hal * ah)155676bd547bSAdrian Chadd ar9300_wow_set_gpio_reset_low(struct ath_hal *ah)
155776bd547bSAdrian Chadd {
155876bd547bSAdrian Chadd     uint32_t val;
155976bd547bSAdrian Chadd 
156076bd547bSAdrian Chadd     val = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_GPIO_OE_OUT));
156176bd547bSAdrian Chadd     val |= (1 << (2 * 2));
156276bd547bSAdrian Chadd     OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_GPIO_OE_OUT), val);
156376bd547bSAdrian Chadd     val = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_GPIO_OE_OUT));
156476bd547bSAdrian Chadd     /* val = OS_REG_READ(ah,AR_GPIO_IN_OUT ); */
156576bd547bSAdrian Chadd }
156676bd547bSAdrian Chadd #endif /* ATH_WOW */
1567