ar9003_wow.c 11.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 * Copyright (c) 2012 Qualcomm Atheros, Inc.
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <linux/export.h>
#include "ath9k.h"
#include "reg.h"
20
#include "reg_wow.h"
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
#include "hw-ops.h"

static void ath9k_hw_set_powermode_wow_sleep(struct ath_hw *ah)
{
	struct ath_common *common = ath9k_hw_common(ah);

	REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);

	/* set rx disable bit */
	REG_WRITE(ah, AR_CR, AR_CR_RXD);

	if (!ath9k_hw_wait(ah, AR_CR, AR_CR_RXE, 0, AH_WAIT_TIMEOUT)) {
		ath_err(common, "Failed to stop Rx DMA in 10ms AR_CR=0x%08x AR_DIAG_SW=0x%08x\n",
			REG_READ(ah, AR_CR), REG_READ(ah, AR_DIAG_SW));
		return;
	}

S
Sujith Manoharan 已提交
38 39 40 41 42 43 44 45 46
	if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) {
		if (!REG_READ(ah, AR_MAC_PCU_GEN_TIMER_TSF_SEL))
			REG_CLR_BIT(ah, AR_DIRECT_CONNECT, AR_DC_TSF2_ENABLE);
	} else if (AR_SREV_9485(ah)){
		if (!(REG_READ(ah, AR_NDP2_TIMER_MODE) &
		      AR_GEN_TIMERS2_MODE_ENABLE_MASK))
			REG_CLR_BIT(ah, AR_DIRECT_CONNECT, AR_DC_TSF2_ENABLE);
	}

S
Sujith Manoharan 已提交
47 48 49
	if (ath9k_hw_mci_is_enabled(ah))
		REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);

50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
	REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON_INT);
}

static void ath9k_wow_create_keep_alive_pattern(struct ath_hw *ah)
{
	struct ath_common *common = ath9k_hw_common(ah);
	u8 sta_mac_addr[ETH_ALEN], ap_mac_addr[ETH_ALEN];
	u32 ctl[13] = {0};
	u32 data_word[KAL_NUM_DATA_WORDS];
	u8 i;
	u32 wow_ka_data_word0;

	memcpy(sta_mac_addr, common->macaddr, ETH_ALEN);
	memcpy(ap_mac_addr, common->curbssid, ETH_ALEN);

	/* set the transmit buffer */
	ctl[0] = (KAL_FRAME_LEN | (MAX_RATE_POWER << 16));
	ctl[1] = 0;
	ctl[4] = 0;
	ctl[7] = (ah->txchainmask) << 2;
70
	ctl[2] = 0xf << 16; /* tx_tries 0 */
71

72 73 74 75 76
	if (IS_CHAN_2GHZ(ah->curchan))
		ctl[3] = 0x1b;	/* CCK_1M */
	else
		ctl[3] = 0xb;	/* OFDM_6M */

77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
	for (i = 0; i < KAL_NUM_DESC_WORDS; i++)
		REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]);

	data_word[0] = (KAL_FRAME_TYPE << 2) | (KAL_FRAME_SUB_TYPE << 4) |
		       (KAL_TO_DS << 8) | (KAL_DURATION_ID << 16);
	data_word[1] = (ap_mac_addr[3] << 24) | (ap_mac_addr[2] << 16) |
		       (ap_mac_addr[1] << 8) | (ap_mac_addr[0]);
	data_word[2] = (sta_mac_addr[1] << 24) | (sta_mac_addr[0] << 16) |
		       (ap_mac_addr[5] << 8) | (ap_mac_addr[4]);
	data_word[3] = (sta_mac_addr[5] << 24) | (sta_mac_addr[4] << 16) |
		       (sta_mac_addr[3] << 8) | (sta_mac_addr[2]);
	data_word[4] = (ap_mac_addr[3] << 24) | (ap_mac_addr[2] << 16) |
		       (ap_mac_addr[1] << 8) | (ap_mac_addr[0]);
	data_word[5] = (ap_mac_addr[5] << 8) | (ap_mac_addr[4]);

92 93 94 95 96
	if (AR_SREV_9462_20_OR_LATER(ah) || AR_SREV_9565(ah)) {
		/*
		 * AR9462 2.0 and AR9565 have an extra descriptor word
		 * (time based discard) compared to other chips.
		 */
97 98 99 100 101 102 103 104 105 106
		REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + (12 * 4)), 0);
		wow_ka_data_word0 = AR_WOW_TXBUF(13);
	} else {
		wow_ka_data_word0 = AR_WOW_TXBUF(12);
	}

	for (i = 0; i < KAL_NUM_DATA_WORDS; i++)
		REG_WRITE(ah, (wow_ka_data_word0 + i*4), data_word[i]);
}

S
Sujith Manoharan 已提交
107 108 109
int ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern,
			       u8 *user_mask, int pattern_count,
			       int pattern_len)
110 111 112 113 114
{
	int i;
	u32 pattern_val, mask_val;
	u32 set, clr;

S
Sujith Manoharan 已提交
115 116
	if (pattern_count >= ah->wow.max_patterns)
		return -ENOSPC;
117

118 119 120 121
	if (pattern_count < MAX_NUM_PATTERN_LEGACY)
		REG_SET_BIT(ah, AR_WOW_PATTERN, BIT(pattern_count));
	else
		REG_SET_BIT(ah, AR_MAC_PCU_WOW4, BIT(pattern_count - 8));
122 123 124 125 126 127 128 129 130 131 132 133 134 135

	for (i = 0; i < MAX_PATTERN_SIZE; i += 4) {
		memcpy(&pattern_val, user_pattern, 4);
		REG_WRITE(ah, (AR_WOW_TB_PATTERN(pattern_count) + i),
			  pattern_val);
		user_pattern += 4;
	}

	for (i = 0; i < MAX_PATTERN_MASK_SIZE; i += 4) {
		memcpy(&mask_val, user_mask, 4);
		REG_WRITE(ah, (AR_WOW_TB_MASK(pattern_count) + i), mask_val);
		user_mask += 4;
	}

136 137 138 139 140 141
	if (pattern_count < MAX_NUM_PATTERN_LEGACY)
		ah->wow.wow_event_mask |=
			BIT(pattern_count + AR_WOW_PAT_FOUND_SHIFT);
	else
		ah->wow.wow_event_mask2 |=
			BIT((pattern_count - 8) + AR_WOW_PAT_FOUND_SHIFT);
142 143 144 145 146 147

	if (pattern_count < 4) {
		set = (pattern_len & AR_WOW_LENGTH_MAX) <<
		       AR_WOW_LEN1_SHIFT(pattern_count);
		clr = AR_WOW_LENGTH1_MASK(pattern_count);
		REG_RMW(ah, AR_WOW_LENGTH1, set, clr);
148
	} else if (pattern_count < 8) {
149 150 151 152
		set = (pattern_len & AR_WOW_LENGTH_MAX) <<
		       AR_WOW_LEN2_SHIFT(pattern_count);
		clr = AR_WOW_LENGTH2_MASK(pattern_count);
		REG_RMW(ah, AR_WOW_LENGTH2, set, clr);
153 154 155 156 157 158 159 160 161 162
	} else if (pattern_count < 12) {
		set = (pattern_len & AR_WOW_LENGTH_MAX) <<
		       AR_WOW_LEN3_SHIFT(pattern_count);
		clr = AR_WOW_LENGTH3_MASK(pattern_count);
		REG_RMW(ah, AR_WOW_LENGTH3, set, clr);
	} else if (pattern_count < MAX_NUM_PATTERN) {
		set = (pattern_len & AR_WOW_LENGTH_MAX) <<
		       AR_WOW_LEN4_SHIFT(pattern_count);
		clr = AR_WOW_LENGTH4_MASK(pattern_count);
		REG_RMW(ah, AR_WOW_LENGTH4, set, clr);
163 164
	}

S
Sujith Manoharan 已提交
165
	return 0;
166 167 168 169 170 171 172
}
EXPORT_SYMBOL(ath9k_hw_wow_apply_pattern);

u32 ath9k_hw_wow_wakeup(struct ath_hw *ah)
{
	u32 wow_status = 0;
	u32 val = 0, rval;
173

174 175 176 177 178 179 180 181 182 183 184 185 186
	/*
	 * read the WoW status register to know
	 * the wakeup reason
	 */
	rval = REG_READ(ah, AR_WOW_PATTERN);
	val = AR_WOW_STATUS(rval);

	/*
	 * mask only the WoW events that we have enabled. Sometimes
	 * we have spurious WoW events from the AR_WOW_PATTERN
	 * register. This mask will clean it up.
	 */

187
	val &= ah->wow.wow_event_mask;
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227

	if (val) {
		if (val & AR_WOW_MAGIC_PAT_FOUND)
			wow_status |= AH_WOW_MAGIC_PATTERN_EN;
		if (AR_WOW_PATTERN_FOUND(val))
			wow_status |= AH_WOW_USER_PATTERN_EN;
		if (val & AR_WOW_KEEP_ALIVE_FAIL)
			wow_status |= AH_WOW_LINK_CHANGE;
		if (val & AR_WOW_BEACON_FAIL)
			wow_status |= AH_WOW_BEACON_MISS;
	}

	/*
	 * set and clear WOW_PME_CLEAR registers for the chip to
	 * generate next wow signal.
	 * disable D3 before accessing other registers ?
	 */

	/* do we need to check the bit value 0x01000000 (7-10) ?? */
	REG_RMW(ah, AR_PCIE_PM_CTRL, AR_PMCTRL_WOW_PME_CLR,
		AR_PMCTRL_PWR_STATE_D1D3);

	/*
	 * clear all events
	 */
	REG_WRITE(ah, AR_WOW_PATTERN,
		  AR_WOW_CLEAR_EVENTS(REG_READ(ah, AR_WOW_PATTERN)));

	/*
	 * restore the beacon threshold to init value
	 */
	REG_WRITE(ah, AR_RSSI_THR, INIT_RSSI_THR);

	/*
	 * Restore the way the PCI-E reset, Power-On-Reset, external
	 * PCIE_POR_SHORT pins are tied to its original value.
	 * Previously just before WoW sleep, we untie the PCI-E
	 * reset to our Chip's Power On Reset so that any PCI-E
	 * reset from the bus will not reset our chip
	 */
228
	if (ah->is_pciexpress)
229 230
		ath9k_hw_configpcipowersave(ah, false);

231
	ah->wow.wow_event_mask = 0;
232 233 234 235 236

	return wow_status;
}
EXPORT_SYMBOL(ath9k_hw_wow_wakeup);

237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
static void ath9k_hw_wow_set_arwr_reg(struct ath_hw *ah)
{
	u32 wa_reg;

	if (!ah->is_pciexpress)
		return;

	/*
	 * We need to untie the internal POR (power-on-reset)
	 * to the external PCI-E reset. We also need to tie
	 * the PCI-E Phy reset to the PCI-E reset.
	 */
	wa_reg = REG_READ(ah, AR_WA);
	wa_reg &= ~AR_WA_UNTIE_RESET_EN;
	wa_reg |= AR_WA_RESET_EN;
	wa_reg |= AR_WA_POR_SHORT;

	REG_WRITE(ah, AR_WA, wa_reg);
}

257 258 259
void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
{
	u32 wow_event_mask;
260
	u32 keep_alive, magic_pattern, host_pm_ctrl;
261

262
	wow_event_mask = ah->wow.wow_event_mask;
263 264

	/*
265 266 267 268 269 270 271 272 273 274 275 276 277 278
	 * AR_PMCTRL_HOST_PME_EN - Override PME enable in configuration
	 *                         space and allow MAC to generate WoW anyway.
	 *
	 * AR_PMCTRL_PWR_PM_CTRL_ENA - ???
	 *
	 * AR_PMCTRL_AUX_PWR_DET - PCI core SYS_AUX_PWR_DET signal,
	 *                         needs to be set for WoW in PCI mode.
	 *
	 * AR_PMCTRL_WOW_PME_CLR - WoW Clear Signal going to the MAC.
	 *
	 * Set the power states appropriately and enable PME.
	 *
	 * Set and clear WOW_PME_CLEAR for the chip
	 * to generate next wow signal.
279
	 */
280 281 282 283 284
	REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PMCTRL_HOST_PME_EN |
		    			 AR_PMCTRL_PWR_PM_CTRL_ENA |
		    			 AR_PMCTRL_AUX_PWR_DET |
		    			 AR_PMCTRL_WOW_PME_CLR);
	REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, AR_PMCTRL_WOW_PME_CLR);
285 286

	/*
287 288 289 290 291 292
	 * Random Backoff.
	 *
	 * 31:28 in AR_WOW_PATTERN : Indicates the number of bits used in the
	 *                           contention window. For value N,
	 *                           the random backoff will be selected between
	 *                           0 and (2 ^ N) - 1.
293
	 */
294 295
	REG_SET_BIT(ah, AR_WOW_PATTERN,
		    AR_WOW_BACK_OFF_SHIFT(AR_WOW_PAT_BACKOFF));
296 297

	/*
298
	 * AIFS time, Slot time, Keep Alive count.
299
	 */
300 301 302
	REG_SET_BIT(ah, AR_WOW_COUNT, AR_WOW_AIFS_CNT(AR_WOW_CNT_AIFS_CNT) |
		    		      AR_WOW_SLOT_CNT(AR_WOW_CNT_SLOT_CNT) |
		    		      AR_WOW_KEEP_ALIVE_CNT(AR_WOW_CNT_KA_CNT));
303
	/*
304
	 * Beacon timeout.
305 306
	 */
	if (pattern_enable & AH_WOW_BEACON_MISS)
307
		REG_WRITE(ah, AR_WOW_BCN_TIMO, AR_WOW_BEACON_TIMO);
308
	else
309
		REG_WRITE(ah, AR_WOW_BCN_TIMO, AR_WOW_BEACON_TIMO_MAX);
310 311

	/*
312
	 * Keep alive timeout in ms.
313
	 */
314
	if (!pattern_enable)
315
		REG_WRITE(ah, AR_WOW_KEEP_ALIVE_TIMO, AR_WOW_KEEP_ALIVE_NEVER);
316
	else
317
		REG_WRITE(ah, AR_WOW_KEEP_ALIVE_TIMO, KAL_TIMEOUT * 32);
318 319

	/*
320
	 * Keep alive delay in us.
321
	 */
322
	REG_WRITE(ah, AR_WOW_KEEP_ALIVE_DELAY, KAL_DELAY * 1000);
323 324

	/*
325
	 * Create keep alive pattern to respond to beacons.
326 327 328 329
	 */
	ath9k_wow_create_keep_alive_pattern(ah);

	/*
330
	 * Configure keep alive register.
331
	 */
332 333
	keep_alive = REG_READ(ah, AR_WOW_KEEP_ALIVE);

334
	/* Send keep alive timeouts anyway */
335
	keep_alive &= ~AR_WOW_KEEP_ALIVE_AUTO_DIS;
336

337 338
	if (pattern_enable & AH_WOW_LINK_CHANGE) {
		keep_alive &= ~AR_WOW_KEEP_ALIVE_FAIL_DIS;
339
		wow_event_mask |= AR_WOW_KEEP_ALIVE_FAIL;
340 341 342
	} else {
		keep_alive |= AR_WOW_KEEP_ALIVE_FAIL_DIS;
	}
343

344
	REG_WRITE(ah, AR_WOW_KEEP_ALIVE, keep_alive);
345 346

	/*
347 348
	 * We are relying on a bmiss failure, ensure we have
	 * enough threshold to prevent false positives.
349 350 351 352 353 354
	 */
	REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_THR_BM_THR,
		      AR_WOW_BMISSTHRESHOLD);

	if (pattern_enable & AH_WOW_BEACON_MISS) {
		wow_event_mask |= AR_WOW_BEACON_FAIL;
355
		REG_SET_BIT(ah, AR_WOW_BCN_EN, AR_WOW_BEACON_FAIL_EN);
356
	} else {
357
		REG_CLR_BIT(ah, AR_WOW_BCN_EN, AR_WOW_BEACON_FAIL_EN);
358 359 360
	}

	/*
361
	 * Enable the magic packet registers.
362
	 */
363 364 365
	magic_pattern = REG_READ(ah, AR_WOW_PATTERN);
	magic_pattern |= AR_WOW_MAC_INTR_EN;

366
	if (pattern_enable & AH_WOW_MAGIC_PATTERN_EN) {
367
		magic_pattern |= AR_WOW_MAGIC_EN;
368 369
		wow_event_mask |= AR_WOW_MAGIC_PAT_FOUND;
	} else {
370
		magic_pattern &= ~AR_WOW_MAGIC_EN;
371 372
	}

373 374 375 376 377 378
	REG_WRITE(ah, AR_WOW_PATTERN, magic_pattern);

	/*
	 * Enable pattern matching for packets which are less
	 * than 256 bytes.
	 */
379 380
	REG_WRITE(ah, AR_WOW_PATTERN_MATCH_LT_256B,
		  AR_WOW_PATTERN_SUPPORTED);
381 382

	/*
383
	 * Set the power states appropriately and enable PME.
384
	 */
385 386 387 388 389
	host_pm_ctrl = REG_READ(ah, AR_PCIE_PM_CTRL);
	host_pm_ctrl |= AR_PMCTRL_PWR_STATE_D1D3 |
			AR_PMCTRL_HOST_PME_EN |
			AR_PMCTRL_PWR_PM_CTRL_ENA;
	host_pm_ctrl &= ~AR_PCIE_PM_CTRL_ENA;
390

391 392 393 394 395 396 397 398 399 400 401
	if (AR_SREV_9462(ah)) {
		/*
		 * This is needed to prevent the chip waking up
		 * the host within 3-4 seconds with certain
		 * platform/BIOS.
		 */
		host_pm_ctrl &= ~AR_PMCTRL_PWR_STATE_D1D3;
		host_pm_ctrl |= AR_PMCTRL_PWR_STATE_D1D3_REAL;
	}

	REG_WRITE(ah, AR_PCIE_PM_CTRL, host_pm_ctrl);
402

403
	/*
404
	 * Enable sequence number generation when asleep.
405
	 */
406 407
	REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PRESERVE_SEQNUM);

408 409 410
	/* To bring down WOW power low margin */
	REG_SET_BIT(ah, AR_PCIE_PHY_REG3, BIT(13));

411 412
	ath9k_hw_wow_set_arwr_reg(ah);

S
Sujith Manoharan 已提交
413 414 415
	if (ath9k_hw_mci_is_enabled(ah))
		REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);

416
	/* HW WoW */
417
	REG_CLR_BIT(ah, AR_PCU_MISC_MODE3, BIT(5));
418 419

	ath9k_hw_set_powermode_wow_sleep(ah);
420
	ah->wow.wow_event_mask = wow_event_mask;
421 422
}
EXPORT_SYMBOL(ath9k_hw_wow_enable);