linux/drivers/net/wireless/ath/ath9k/wow.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2012 Qualcomm Atheros, Inc.
   3 *
   4 * Permission to use, copy, modify, and/or distribute this software for any
   5 * purpose with or without fee is hereby granted, provided that the above
   6 * copyright notice and this permission notice appear in all copies.
   7 *
   8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15 */
  16
  17#include <linux/export.h>
  18#include "ath9k.h"
  19#include "reg.h"
  20#include "hw-ops.h"
  21
  22const char *ath9k_hw_wow_event_to_string(u32 wow_event)
  23{
  24        if (wow_event & AH_WOW_MAGIC_PATTERN_EN)
  25                return "Magic pattern";
  26        if (wow_event & AH_WOW_USER_PATTERN_EN)
  27                return "User pattern";
  28        if (wow_event & AH_WOW_LINK_CHANGE)
  29                return "Link change";
  30        if (wow_event & AH_WOW_BEACON_MISS)
  31                return "Beacon miss";
  32
  33        return  "unknown reason";
  34}
  35EXPORT_SYMBOL(ath9k_hw_wow_event_to_string);
  36
  37static void ath9k_hw_set_powermode_wow_sleep(struct ath_hw *ah)
  38{
  39        struct ath_common *common = ath9k_hw_common(ah);
  40
  41        REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
  42
  43        /* set rx disable bit */
  44        REG_WRITE(ah, AR_CR, AR_CR_RXD);
  45
  46        if (!ath9k_hw_wait(ah, AR_CR, AR_CR_RXE, 0, AH_WAIT_TIMEOUT)) {
  47                ath_err(common, "Failed to stop Rx DMA in 10ms AR_CR=0x%08x AR_DIAG_SW=0x%08x\n",
  48                        REG_READ(ah, AR_CR), REG_READ(ah, AR_DIAG_SW));
  49                return;
  50        }
  51
  52        REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON_INT);
  53}
  54
  55static void ath9k_wow_create_keep_alive_pattern(struct ath_hw *ah)
  56{
  57        struct ath_common *common = ath9k_hw_common(ah);
  58        u8 sta_mac_addr[ETH_ALEN], ap_mac_addr[ETH_ALEN];
  59        u32 ctl[13] = {0};
  60        u32 data_word[KAL_NUM_DATA_WORDS];
  61        u8 i;
  62        u32 wow_ka_data_word0;
  63
  64        memcpy(sta_mac_addr, common->macaddr, ETH_ALEN);
  65        memcpy(ap_mac_addr, common->curbssid, ETH_ALEN);
  66
  67        /* set the transmit buffer */
  68        ctl[0] = (KAL_FRAME_LEN | (MAX_RATE_POWER << 16));
  69        ctl[1] = 0;
  70        ctl[3] = 0xb;   /* OFDM_6M hardware value for this rate */
  71        ctl[4] = 0;
  72        ctl[7] = (ah->txchainmask) << 2;
  73        ctl[2] = 0xf << 16; /* tx_tries 0 */
  74
  75        for (i = 0; i < KAL_NUM_DESC_WORDS; i++)
  76                REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]);
  77
  78        REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]);
  79
  80        data_word[0] = (KAL_FRAME_TYPE << 2) | (KAL_FRAME_SUB_TYPE << 4) |
  81                       (KAL_TO_DS << 8) | (KAL_DURATION_ID << 16);
  82        data_word[1] = (ap_mac_addr[3] << 24) | (ap_mac_addr[2] << 16) |
  83                       (ap_mac_addr[1] << 8) | (ap_mac_addr[0]);
  84        data_word[2] = (sta_mac_addr[1] << 24) | (sta_mac_addr[0] << 16) |
  85                       (ap_mac_addr[5] << 8) | (ap_mac_addr[4]);
  86        data_word[3] = (sta_mac_addr[5] << 24) | (sta_mac_addr[4] << 16) |
  87                       (sta_mac_addr[3] << 8) | (sta_mac_addr[2]);
  88        data_word[4] = (ap_mac_addr[3] << 24) | (ap_mac_addr[2] << 16) |
  89                       (ap_mac_addr[1] << 8) | (ap_mac_addr[0]);
  90        data_word[5] = (ap_mac_addr[5] << 8) | (ap_mac_addr[4]);
  91
  92        if (AR_SREV_9462_20(ah)) {
  93                /* AR9462 2.0 has an extra descriptor word (time based
  94                 * discard) compared to other chips */
  95                REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + (12 * 4)), 0);
  96                wow_ka_data_word0 = AR_WOW_TXBUF(13);
  97        } else {
  98                wow_ka_data_word0 = AR_WOW_TXBUF(12);
  99        }
 100
 101        for (i = 0; i < KAL_NUM_DATA_WORDS; i++)
 102                REG_WRITE(ah, (wow_ka_data_word0 + i*4), data_word[i]);
 103
 104}
 105
 106void ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern,
 107                                u8 *user_mask, int pattern_count,
 108                                int pattern_len)
 109{
 110        int i;
 111        u32 pattern_val, mask_val;
 112        u32 set, clr;
 113
 114        /* FIXME: should check count by querying the hardware capability */
 115        if (pattern_count >= MAX_NUM_PATTERN)
 116                return;
 117
 118        REG_SET_BIT(ah, AR_WOW_PATTERN, BIT(pattern_count));
 119
 120        /* set the registers for pattern */
 121        for (i = 0; i < MAX_PATTERN_SIZE; i += 4) {
 122                memcpy(&pattern_val, user_pattern, 4);
 123                REG_WRITE(ah, (AR_WOW_TB_PATTERN(pattern_count) + i),
 124                          pattern_val);
 125                user_pattern += 4;
 126        }
 127
 128        /* set the registers for mask */
 129        for (i = 0; i < MAX_PATTERN_MASK_SIZE; i += 4) {
 130                memcpy(&mask_val, user_mask, 4);
 131                REG_WRITE(ah, (AR_WOW_TB_MASK(pattern_count) + i), mask_val);
 132                user_mask += 4;
 133        }
 134
 135        /* set the pattern length to be matched
 136         *
 137         * AR_WOW_LENGTH1_REG1
 138         * bit 31:24 pattern 0 length
 139         * bit 23:16 pattern 1 length
 140         * bit 15:8 pattern 2 length
 141         * bit 7:0 pattern 3 length
 142         *
 143         * AR_WOW_LENGTH1_REG2
 144         * bit 31:24 pattern 4 length
 145         * bit 23:16 pattern 5 length
 146         * bit 15:8 pattern 6 length
 147         * bit 7:0 pattern 7 length
 148         *
 149         * the below logic writes out the new
 150         * pattern length for the corresponding
 151         * pattern_count, while masking out the
 152         * other fields
 153         */
 154
 155        ah->wow_event_mask |= BIT(pattern_count + AR_WOW_PAT_FOUND_SHIFT);
 156
 157        if (pattern_count < 4) {
 158                /* Pattern 0-3 uses AR_WOW_LENGTH1 register */
 159                set = (pattern_len & AR_WOW_LENGTH_MAX) <<
 160                       AR_WOW_LEN1_SHIFT(pattern_count);
 161                clr = AR_WOW_LENGTH1_MASK(pattern_count);
 162                REG_RMW(ah, AR_WOW_LENGTH1, set, clr);
 163        } else {
 164                /* Pattern 4-7 uses AR_WOW_LENGTH2 register */
 165                set = (pattern_len & AR_WOW_LENGTH_MAX) <<
 166                       AR_WOW_LEN2_SHIFT(pattern_count);
 167                clr = AR_WOW_LENGTH2_MASK(pattern_count);
 168                REG_RMW(ah, AR_WOW_LENGTH2, set, clr);
 169        }
 170
 171}
 172EXPORT_SYMBOL(ath9k_hw_wow_apply_pattern);
 173
 174u32 ath9k_hw_wow_wakeup(struct ath_hw *ah)
 175{
 176        u32 wow_status = 0;
 177        u32 val = 0, rval;
 178
 179        /*
 180         * read the WoW status register to know
 181         * the wakeup reason
 182         */
 183        rval = REG_READ(ah, AR_WOW_PATTERN);
 184        val = AR_WOW_STATUS(rval);
 185
 186        /*
 187         * mask only the WoW events that we have enabled. Sometimes
 188         * we have spurious WoW events from the AR_WOW_PATTERN
 189         * register. This mask will clean it up.
 190         */
 191
 192        val &= ah->wow_event_mask;
 193
 194        if (val) {
 195                if (val & AR_WOW_MAGIC_PAT_FOUND)
 196                        wow_status |= AH_WOW_MAGIC_PATTERN_EN;
 197                if (AR_WOW_PATTERN_FOUND(val))
 198                        wow_status |= AH_WOW_USER_PATTERN_EN;
 199                if (val & AR_WOW_KEEP_ALIVE_FAIL)
 200                        wow_status |= AH_WOW_LINK_CHANGE;
 201                if (val & AR_WOW_BEACON_FAIL)
 202                        wow_status |= AH_WOW_BEACON_MISS;
 203        }
 204
 205        /*
 206         * set and clear WOW_PME_CLEAR registers for the chip to
 207         * generate next wow signal.
 208         * disable D3 before accessing other registers ?
 209         */
 210
 211        /* do we need to check the bit value 0x01000000 (7-10) ?? */
 212        REG_RMW(ah, AR_PCIE_PM_CTRL, AR_PMCTRL_WOW_PME_CLR,
 213                AR_PMCTRL_PWR_STATE_D1D3);
 214
 215        /*
 216         * clear all events
 217         */
 218        REG_WRITE(ah, AR_WOW_PATTERN,
 219                  AR_WOW_CLEAR_EVENTS(REG_READ(ah, AR_WOW_PATTERN)));
 220
 221        /*
 222         * restore the beacon threshold to init value
 223         */
 224        REG_WRITE(ah, AR_RSSI_THR, INIT_RSSI_THR);
 225
 226        /*
 227         * Restore the way the PCI-E reset, Power-On-Reset, external
 228         * PCIE_POR_SHORT pins are tied to its original value.
 229         * Previously just before WoW sleep, we untie the PCI-E
 230         * reset to our Chip's Power On Reset so that any PCI-E
 231         * reset from the bus will not reset our chip
 232         */
 233        if (ah->is_pciexpress)
 234                ath9k_hw_configpcipowersave(ah, false);
 235
 236        ah->wow_event_mask = 0;
 237
 238        return wow_status;
 239}
 240EXPORT_SYMBOL(ath9k_hw_wow_wakeup);
 241
 242void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
 243{
 244        u32 wow_event_mask;
 245        u32 set, clr;
 246
 247        /*
 248         * wow_event_mask is a mask to the AR_WOW_PATTERN register to
 249         * indicate which WoW events we have enabled. The WoW events
 250         * are from the 'pattern_enable' in this function and
 251         * 'pattern_count' of ath9k_hw_wow_apply_pattern()
 252         */
 253        wow_event_mask = ah->wow_event_mask;
 254
 255        /*
 256         * Untie Power-on-Reset from the PCI-E-Reset. When we are in
 257         * WOW sleep, we do want the Reset from the PCI-E to disturb
 258         * our hw state
 259         */
 260        if (ah->is_pciexpress) {
 261                /*
 262                 * we need to untie the internal POR (power-on-reset)
 263                 * to the external PCI-E reset. We also need to tie
 264                 * the PCI-E Phy reset to the PCI-E reset.
 265                 */
 266                set = AR_WA_RESET_EN | AR_WA_POR_SHORT;
 267                clr = AR_WA_UNTIE_RESET_EN | AR_WA_D3_L1_DISABLE;
 268                REG_RMW(ah, AR_WA, set, clr);
 269        }
 270
 271        /*
 272         * set the power states appropriately and enable PME
 273         */
 274        set = AR_PMCTRL_HOST_PME_EN | AR_PMCTRL_PWR_PM_CTRL_ENA |
 275              AR_PMCTRL_AUX_PWR_DET | AR_PMCTRL_WOW_PME_CLR;
 276
 277        /*
 278         * set and clear WOW_PME_CLEAR registers for the chip
 279         * to generate next wow signal.
 280         */
 281        REG_SET_BIT(ah, AR_PCIE_PM_CTRL, set);
 282        clr = AR_PMCTRL_WOW_PME_CLR;
 283        REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, clr);
 284
 285        /*
 286         * Setup for:
 287         *      - beacon misses
 288         *      - magic pattern
 289         *      - keep alive timeout
 290         *      - pattern matching
 291         */
 292
 293        /*
 294         * Program default values for pattern backoff, aifs/slot/KAL count,
 295         * beacon miss timeout, KAL timeout, etc.
 296         */
 297        set = AR_WOW_BACK_OFF_SHIFT(AR_WOW_PAT_BACKOFF);
 298        REG_SET_BIT(ah, AR_WOW_PATTERN, set);
 299
 300        set = AR_WOW_AIFS_CNT(AR_WOW_CNT_AIFS_CNT) |
 301              AR_WOW_SLOT_CNT(AR_WOW_CNT_SLOT_CNT) |
 302              AR_WOW_KEEP_ALIVE_CNT(AR_WOW_CNT_KA_CNT);
 303        REG_SET_BIT(ah, AR_WOW_COUNT, set);
 304
 305        if (pattern_enable & AH_WOW_BEACON_MISS)
 306                set = AR_WOW_BEACON_TIMO;
 307        /* We are not using beacon miss, program a large value */
 308        else
 309                set = AR_WOW_BEACON_TIMO_MAX;
 310
 311        REG_WRITE(ah, AR_WOW_BCN_TIMO, set);
 312
 313        /*
 314         * Keep alive timo in ms except AR9280
 315         */
 316        if (!pattern_enable)
 317                set = AR_WOW_KEEP_ALIVE_NEVER;
 318        else
 319                set = KAL_TIMEOUT * 32;
 320
 321        REG_WRITE(ah, AR_WOW_KEEP_ALIVE_TIMO, set);
 322
 323        /*
 324         * Keep alive delay in us. based on 'power on clock',
 325         * therefore in usec
 326         */
 327        set = KAL_DELAY * 1000;
 328        REG_WRITE(ah, AR_WOW_KEEP_ALIVE_DELAY, set);
 329
 330        /*
 331         * Create keep alive pattern to respond to beacons
 332         */
 333        ath9k_wow_create_keep_alive_pattern(ah);
 334
 335        /*
 336         * Configure MAC WoW Registers
 337         */
 338        set = 0;
 339        /* Send keep alive timeouts anyway */
 340        clr = AR_WOW_KEEP_ALIVE_AUTO_DIS;
 341
 342        if (pattern_enable & AH_WOW_LINK_CHANGE)
 343                wow_event_mask |= AR_WOW_KEEP_ALIVE_FAIL;
 344        else
 345                set = AR_WOW_KEEP_ALIVE_FAIL_DIS;
 346
 347        set = AR_WOW_KEEP_ALIVE_FAIL_DIS;
 348        REG_RMW(ah, AR_WOW_KEEP_ALIVE, set, clr);
 349
 350        /*
 351         * we are relying on a bmiss failure. ensure we have
 352         * enough threshold to prevent false positives
 353         */
 354        REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_THR_BM_THR,
 355                      AR_WOW_BMISSTHRESHOLD);
 356
 357        set = 0;
 358        clr = 0;
 359
 360        if (pattern_enable & AH_WOW_BEACON_MISS) {
 361                set = AR_WOW_BEACON_FAIL_EN;
 362                wow_event_mask |= AR_WOW_BEACON_FAIL;
 363        } else {
 364                clr = AR_WOW_BEACON_FAIL_EN;
 365        }
 366
 367        REG_RMW(ah, AR_WOW_BCN_EN, set, clr);
 368
 369        set = 0;
 370        clr = 0;
 371        /*
 372         * Enable the magic packet registers
 373         */
 374        if (pattern_enable & AH_WOW_MAGIC_PATTERN_EN) {
 375                set = AR_WOW_MAGIC_EN;
 376                wow_event_mask |= AR_WOW_MAGIC_PAT_FOUND;
 377        } else {
 378                clr = AR_WOW_MAGIC_EN;
 379        }
 380        set |= AR_WOW_MAC_INTR_EN;
 381        REG_RMW(ah, AR_WOW_PATTERN, set, clr);
 382
 383        REG_WRITE(ah, AR_WOW_PATTERN_MATCH_LT_256B,
 384                  AR_WOW_PATTERN_SUPPORTED);
 385
 386        /*
 387         * Set the power states appropriately and enable PME
 388         */
 389        clr = 0;
 390        set = AR_PMCTRL_PWR_STATE_D1D3 | AR_PMCTRL_HOST_PME_EN |
 391              AR_PMCTRL_PWR_PM_CTRL_ENA;
 392
 393        clr = AR_PCIE_PM_CTRL_ENA;
 394        REG_RMW(ah, AR_PCIE_PM_CTRL, set, clr);
 395
 396        /*
 397         * this is needed to prevent the chip waking up
 398         * the host within 3-4 seconds with certain
 399         * platform/BIOS. The fix is to enable
 400         * D1 & D3 to match original definition and
 401         * also match the OTP value. Anyway this
 402         * is more related to SW WOW.
 403         */
 404        clr = AR_PMCTRL_PWR_STATE_D1D3;
 405        REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, clr);
 406
 407        set = AR_PMCTRL_PWR_STATE_D1D3_REAL;
 408        REG_SET_BIT(ah, AR_PCIE_PM_CTRL, set);
 409
 410        REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PRESERVE_SEQNUM);
 411
 412        /* to bring down WOW power low margin */
 413        set = BIT(13);
 414        REG_SET_BIT(ah, AR_PCIE_PHY_REG3, set);
 415        /* HW WoW */
 416        clr = BIT(5);
 417        REG_CLR_BIT(ah, AR_PCU_MISC_MODE3, clr);
 418
 419        ath9k_hw_set_powermode_wow_sleep(ah);
 420        ah->wow_event_mask = wow_event_mask;
 421}
 422EXPORT_SYMBOL(ath9k_hw_wow_enable);
 423