2008-08-04 15:16:41 +08:00
|
|
|
/*
|
2011-05-17 16:06:18 +08:00
|
|
|
* Copyright (c) 2008-2011 Atheros Communications Inc.
|
2008-08-04 15:16:41 +08:00
|
|
|
*
|
|
|
|
* 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/io.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2011-07-04 03:21:01 +08:00
|
|
|
#include <linux/module.h>
|
2008-08-04 15:16:41 +08:00
|
|
|
#include <asm/unaligned.h>
|
|
|
|
|
2009-09-09 17:33:11 +08:00
|
|
|
#include "hw.h"
|
2010-04-16 05:38:06 +08:00
|
|
|
#include "hw-ops.h"
|
2009-09-14 14:39:31 +08:00
|
|
|
#include "rc.h"
|
2010-04-16 05:39:28 +08:00
|
|
|
#include "ar9003_mac.h"
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
static bool ath9k_hw_set_reset_reg(struct ath_hw *ah, u32 type);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-09-24 11:07:00 +08:00
|
|
|
MODULE_AUTHOR("Atheros Communications");
|
|
|
|
MODULE_DESCRIPTION("Support for Atheros 802.11n wireless LAN cards.");
|
|
|
|
MODULE_SUPPORTED_DEVICE("Atheros 802.11n WLAN cards");
|
|
|
|
MODULE_LICENSE("Dual BSD/GPL");
|
|
|
|
|
|
|
|
static int __init ath9k_init(void)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
module_init(ath9k_init);
|
|
|
|
|
|
|
|
static void __exit ath9k_exit(void)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
module_exit(ath9k_exit);
|
|
|
|
|
2010-04-16 05:38:06 +08:00
|
|
|
/* Private hardware callbacks */
|
|
|
|
|
|
|
|
static void ath9k_hw_init_cal_settings(struct ath_hw *ah)
|
|
|
|
{
|
|
|
|
ath9k_hw_private_ops(ah)->init_cal_settings(ah);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath9k_hw_init_mode_regs(struct ath_hw *ah)
|
|
|
|
{
|
|
|
|
ath9k_hw_private_ops(ah)->init_mode_regs(ah);
|
|
|
|
}
|
|
|
|
|
2010-04-16 05:38:17 +08:00
|
|
|
static u32 ath9k_hw_compute_pll_control(struct ath_hw *ah,
|
|
|
|
struct ath9k_channel *chan)
|
|
|
|
{
|
|
|
|
return ath9k_hw_private_ops(ah)->compute_pll_control(ah, chan);
|
|
|
|
}
|
|
|
|
|
2010-04-16 05:39:05 +08:00
|
|
|
static void ath9k_hw_init_mode_gain_regs(struct ath_hw *ah)
|
|
|
|
{
|
|
|
|
if (!ath9k_hw_private_ops(ah)->init_mode_gain_regs)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ath9k_hw_private_ops(ah)->init_mode_gain_regs(ah);
|
|
|
|
}
|
|
|
|
|
2010-06-12 12:33:45 +08:00
|
|
|
static void ath9k_hw_ani_cache_ini_regs(struct ath_hw *ah)
|
|
|
|
{
|
|
|
|
/* You will not have this callback if using the old ANI */
|
|
|
|
if (!ath9k_hw_private_ops(ah)->ani_cache_ini_regs)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ath9k_hw_private_ops(ah)->ani_cache_ini_regs(ah);
|
|
|
|
}
|
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
/********************/
|
|
|
|
/* Helper Functions */
|
|
|
|
/********************/
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-10-09 04:13:51 +08:00
|
|
|
static void ath9k_hw_set_clockrate(struct ath_hw *ah)
|
2008-10-29 12:46:30 +08:00
|
|
|
{
|
2009-09-13 15:03:27 +08:00
|
|
|
struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
|
2010-10-09 04:13:51 +08:00
|
|
|
struct ath_common *common = ath9k_hw_common(ah);
|
|
|
|
unsigned int clockrate;
|
2009-02-09 15:57:12 +08:00
|
|
|
|
2011-07-09 12:12:49 +08:00
|
|
|
/* AR9287 v1.3+ uses async FIFO and runs the MAC at 117 MHz */
|
|
|
|
if (AR_SREV_9287(ah) && AR_SREV_9287_13_OR_LATER(ah))
|
|
|
|
clockrate = 117;
|
|
|
|
else if (!ah->curchan) /* should really check for CCK instead */
|
2010-10-09 04:13:51 +08:00
|
|
|
clockrate = ATH9K_CLOCK_RATE_CCK;
|
|
|
|
else if (conf->channel->band == IEEE80211_BAND_2GHZ)
|
|
|
|
clockrate = ATH9K_CLOCK_RATE_2GHZ_OFDM;
|
|
|
|
else if (ah->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK)
|
|
|
|
clockrate = ATH9K_CLOCK_FAST_RATE_5GHZ_OFDM;
|
2010-04-27 03:04:33 +08:00
|
|
|
else
|
2010-10-09 04:13:51 +08:00
|
|
|
clockrate = ATH9K_CLOCK_RATE_5GHZ_OFDM;
|
|
|
|
|
|
|
|
if (conf_is_ht40(conf))
|
|
|
|
clockrate *= 2;
|
|
|
|
|
2011-07-09 12:12:48 +08:00
|
|
|
if (ah->curchan) {
|
|
|
|
if (IS_CHAN_HALF_RATE(ah->curchan))
|
|
|
|
clockrate /= 2;
|
|
|
|
if (IS_CHAN_QUARTER_RATE(ah->curchan))
|
|
|
|
clockrate /= 4;
|
|
|
|
}
|
|
|
|
|
2010-10-09 04:13:51 +08:00
|
|
|
common->clockrate = clockrate;
|
2008-10-29 12:46:30 +08:00
|
|
|
}
|
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
static u32 ath9k_hw_mac_to_clks(struct ath_hw *ah, u32 usecs)
|
2008-10-29 12:46:30 +08:00
|
|
|
{
|
2010-10-09 04:13:51 +08:00
|
|
|
struct ath_common *common = ath9k_hw_common(ah);
|
2009-02-09 15:57:12 +08:00
|
|
|
|
2010-10-09 04:13:51 +08:00
|
|
|
return usecs * common->clockrate;
|
2008-10-29 12:46:30 +08:00
|
|
|
}
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-02-16 15:53:20 +08:00
|
|
|
bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2009-02-16 15:53:20 +08:00
|
|
|
BUG_ON(timeout < AH_TIME_QUANTUM);
|
|
|
|
|
|
|
|
for (i = 0; i < (timeout / AH_TIME_QUANTUM); i++) {
|
2008-08-04 15:16:41 +08:00
|
|
|
if ((REG_READ(ah, reg) & mask) == val)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
udelay(AH_TIME_QUANTUM);
|
|
|
|
}
|
2008-11-29 00:48:05 +08:00
|
|
|
|
2010-12-03 11:12:37 +08:00
|
|
|
ath_dbg(ath9k_hw_common(ah), ATH_DBG_ANY,
|
|
|
|
"timeout (%d us) on reg 0x%x: 0x%08x & 0x%08x != 0x%08x\n",
|
|
|
|
timeout, reg, REG_READ(ah, reg), mask, val);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
return false;
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_wait);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2011-03-24 03:57:27 +08:00
|
|
|
void ath9k_hw_write_array(struct ath_hw *ah, struct ar5416IniArray *array,
|
|
|
|
int column, unsigned int *writecnt)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
|
|
|
|
ENABLE_REGWRITE_BUFFER(ah);
|
|
|
|
for (r = 0; r < array->ia_rows; r++) {
|
|
|
|
REG_WRITE(ah, INI_RA(array, r, 0),
|
|
|
|
INI_RA(array, r, column));
|
|
|
|
DO_DELAY(*writecnt);
|
|
|
|
}
|
|
|
|
REGWRITE_BUFFER_FLUSH(ah);
|
|
|
|
}
|
|
|
|
|
2008-08-04 15:16:41 +08:00
|
|
|
u32 ath9k_hw_reverse_bits(u32 val, u32 n)
|
|
|
|
{
|
|
|
|
u32 retval;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0, retval = 0; i < n; i++) {
|
|
|
|
retval = (retval << 1) | (val & 1);
|
|
|
|
val >>= 1;
|
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
u16 ath9k_hw_computetxtime(struct ath_hw *ah,
|
2009-11-24 05:21:01 +08:00
|
|
|
u8 phy, int kbps,
|
2008-10-29 12:46:30 +08:00
|
|
|
u32 frameLen, u16 rateix,
|
|
|
|
bool shortPreamble)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2008-10-29 12:46:30 +08:00
|
|
|
u32 bitsPerSymbol, numBits, numSymbols, phyTime, txTime;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
if (kbps == 0)
|
|
|
|
return 0;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-11-24 05:21:01 +08:00
|
|
|
switch (phy) {
|
2008-11-18 11:38:13 +08:00
|
|
|
case WLAN_RC_PHY_CCK:
|
2008-10-29 12:46:30 +08:00
|
|
|
phyTime = CCK_PREAMBLE_BITS + CCK_PLCP_BITS;
|
2009-11-24 05:21:01 +08:00
|
|
|
if (shortPreamble)
|
2008-10-29 12:46:30 +08:00
|
|
|
phyTime >>= 1;
|
|
|
|
numBits = frameLen << 3;
|
|
|
|
txTime = CCK_SIFS_TIME + phyTime + ((numBits * 1000) / kbps);
|
|
|
|
break;
|
2008-11-18 11:38:13 +08:00
|
|
|
case WLAN_RC_PHY_OFDM:
|
2009-02-09 15:57:26 +08:00
|
|
|
if (ah->curchan && IS_CHAN_QUARTER_RATE(ah->curchan)) {
|
2008-10-29 12:46:30 +08:00
|
|
|
bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME_QUARTER) / 1000;
|
|
|
|
numBits = OFDM_PLCP_BITS + (frameLen << 3);
|
|
|
|
numSymbols = DIV_ROUND_UP(numBits, bitsPerSymbol);
|
|
|
|
txTime = OFDM_SIFS_TIME_QUARTER
|
|
|
|
+ OFDM_PREAMBLE_TIME_QUARTER
|
|
|
|
+ (numSymbols * OFDM_SYMBOL_TIME_QUARTER);
|
2009-02-09 15:57:26 +08:00
|
|
|
} else if (ah->curchan &&
|
|
|
|
IS_CHAN_HALF_RATE(ah->curchan)) {
|
2008-10-29 12:46:30 +08:00
|
|
|
bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME_HALF) / 1000;
|
|
|
|
numBits = OFDM_PLCP_BITS + (frameLen << 3);
|
|
|
|
numSymbols = DIV_ROUND_UP(numBits, bitsPerSymbol);
|
|
|
|
txTime = OFDM_SIFS_TIME_HALF +
|
|
|
|
OFDM_PREAMBLE_TIME_HALF
|
|
|
|
+ (numSymbols * OFDM_SYMBOL_TIME_HALF);
|
|
|
|
} else {
|
|
|
|
bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME) / 1000;
|
|
|
|
numBits = OFDM_PLCP_BITS + (frameLen << 3);
|
|
|
|
numSymbols = DIV_ROUND_UP(numBits, bitsPerSymbol);
|
|
|
|
txTime = OFDM_SIFS_TIME + OFDM_PREAMBLE_TIME
|
|
|
|
+ (numSymbols * OFDM_SYMBOL_TIME);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2010-12-03 11:12:36 +08:00
|
|
|
ath_err(ath9k_hw_common(ah),
|
|
|
|
"Unknown phy %u (rate ix %u)\n", phy, rateix);
|
2008-10-29 12:46:30 +08:00
|
|
|
txTime = 0;
|
|
|
|
break;
|
|
|
|
}
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
return txTime;
|
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_computetxtime);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
void ath9k_hw_get_channel_centers(struct ath_hw *ah,
|
2008-10-29 12:46:30 +08:00
|
|
|
struct ath9k_channel *chan,
|
|
|
|
struct chan_centers *centers)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2008-10-29 12:46:30 +08:00
|
|
|
int8_t extoff;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
if (!IS_CHAN_HT40(chan)) {
|
|
|
|
centers->ctl_center = centers->ext_center =
|
|
|
|
centers->synth_center = chan->channel;
|
|
|
|
return;
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
if ((chan->chanmode == CHANNEL_A_HT40PLUS) ||
|
|
|
|
(chan->chanmode == CHANNEL_G_HT40PLUS)) {
|
|
|
|
centers->synth_center =
|
|
|
|
chan->channel + HT40_CHANNEL_CENTER_SHIFT;
|
|
|
|
extoff = 1;
|
|
|
|
} else {
|
|
|
|
centers->synth_center =
|
|
|
|
chan->channel - HT40_CHANNEL_CENTER_SHIFT;
|
|
|
|
extoff = -1;
|
|
|
|
}
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
centers->ctl_center =
|
|
|
|
centers->synth_center - (extoff * HT40_CHANNEL_CENTER_SHIFT);
|
2009-09-14 13:05:04 +08:00
|
|
|
/* 25 MHz spacing is supported by hw but not on upper layers */
|
2008-10-29 12:46:30 +08:00
|
|
|
centers->ext_center =
|
2009-09-14 13:05:04 +08:00
|
|
|
centers->synth_center + (extoff * HT40_CHANNEL_CENTER_SHIFT);
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
/******************/
|
|
|
|
/* Chip Revisions */
|
|
|
|
/******************/
|
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
static void ath9k_hw_read_revisions(struct ath_hw *ah)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2008-10-29 12:46:30 +08:00
|
|
|
u32 val;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2011-04-19 21:59:18 +08:00
|
|
|
switch (ah->hw_version.devid) {
|
|
|
|
case AR5416_AR9100_DEVID:
|
|
|
|
ah->hw_version.macVersion = AR_SREV_VERSION_9100;
|
|
|
|
break;
|
2011-06-21 17:23:23 +08:00
|
|
|
case AR9300_DEVID_AR9330:
|
|
|
|
ah->hw_version.macVersion = AR_SREV_VERSION_9330;
|
|
|
|
if (ah->get_mac_revision) {
|
|
|
|
ah->hw_version.macRev = ah->get_mac_revision();
|
|
|
|
} else {
|
|
|
|
val = REG_READ(ah, AR_SREV);
|
|
|
|
ah->hw_version.macRev = MS(val, AR_SREV_REVISION2);
|
|
|
|
}
|
|
|
|
return;
|
2011-04-19 21:59:18 +08:00
|
|
|
case AR9300_DEVID_AR9340:
|
|
|
|
ah->hw_version.macVersion = AR_SREV_VERSION_9340;
|
|
|
|
val = REG_READ(ah, AR_SREV);
|
|
|
|
ah->hw_version.macRev = MS(val, AR_SREV_REVISION2);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
val = REG_READ(ah, AR_SREV) & AR_SREV_ID;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
if (val == 0xFF) {
|
|
|
|
val = REG_READ(ah, AR_SREV);
|
2009-02-09 15:57:06 +08:00
|
|
|
ah->hw_version.macVersion =
|
|
|
|
(val & AR_SREV_VERSION2) >> AR_SREV_TYPE2_S;
|
|
|
|
ah->hw_version.macRev = MS(val, AR_SREV_REVISION2);
|
2011-09-30 14:01:28 +08:00
|
|
|
|
2011-10-13 13:30:44 +08:00
|
|
|
if (AR_SREV_9462(ah))
|
2011-09-30 14:01:28 +08:00
|
|
|
ah->is_pciexpress = true;
|
|
|
|
else
|
|
|
|
ah->is_pciexpress = (val &
|
|
|
|
AR_SREV_TYPE2_HOST_MODE) ? 0 : 1;
|
2008-10-29 12:46:30 +08:00
|
|
|
} else {
|
|
|
|
if (!AR_SREV_9100(ah))
|
2009-02-09 15:57:06 +08:00
|
|
|
ah->hw_version.macVersion = MS(val, AR_SREV_VERSION);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-02-09 15:57:06 +08:00
|
|
|
ah->hw_version.macRev = val & AR_SREV_REVISION;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-02-09 15:57:06 +08:00
|
|
|
if (ah->hw_version.macVersion == AR_SREV_VERSION_5416_PCIE)
|
2009-02-09 15:57:26 +08:00
|
|
|
ah->is_pciexpress = true;
|
2008-10-29 12:46:30 +08:00
|
|
|
}
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
/************************************/
|
|
|
|
/* HW Attach, Detach, Init Routines */
|
|
|
|
/************************************/
|
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
static void ath9k_hw_disablepcie(struct ath_hw *ah)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2010-12-12 07:51:07 +08:00
|
|
|
if (!AR_SREV_5416(ah))
|
2008-10-29 12:46:30 +08:00
|
|
|
return;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fc00);
|
|
|
|
REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924);
|
|
|
|
REG_WRITE(ah, AR_PCIE_SERDES, 0x28000029);
|
|
|
|
REG_WRITE(ah, AR_PCIE_SERDES, 0x57160824);
|
|
|
|
REG_WRITE(ah, AR_PCIE_SERDES, 0x25980579);
|
|
|
|
REG_WRITE(ah, AR_PCIE_SERDES, 0x00000000);
|
|
|
|
REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40);
|
|
|
|
REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554);
|
|
|
|
REG_WRITE(ah, AR_PCIE_SERDES, 0x000e1007);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000);
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
|
2011-07-29 21:59:08 +08:00
|
|
|
static void ath9k_hw_aspm_init(struct ath_hw *ah)
|
|
|
|
{
|
|
|
|
struct ath_common *common = ath9k_hw_common(ah);
|
|
|
|
|
|
|
|
if (common->bus_ops->aspm_init)
|
|
|
|
common->bus_ops->aspm_init(common);
|
|
|
|
}
|
|
|
|
|
2010-04-16 05:38:29 +08:00
|
|
|
/* This should work for all families including legacy */
|
2009-02-09 15:57:12 +08:00
|
|
|
static bool ath9k_hw_chip_test(struct ath_hw *ah)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2009-09-13 17:42:02 +08:00
|
|
|
struct ath_common *common = ath9k_hw_common(ah);
|
2010-04-16 05:38:29 +08:00
|
|
|
u32 regAddr[2] = { AR_STA_ID0 };
|
2008-10-29 12:46:30 +08:00
|
|
|
u32 regHold[2];
|
2010-11-21 10:38:53 +08:00
|
|
|
static const u32 patternData[4] = {
|
|
|
|
0x55555555, 0xaaaaaaaa, 0x66666666, 0x99999999
|
|
|
|
};
|
2010-04-16 05:38:29 +08:00
|
|
|
int i, j, loop_max;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-04-16 05:38:29 +08:00
|
|
|
if (!AR_SREV_9300_20_OR_LATER(ah)) {
|
|
|
|
loop_max = 2;
|
|
|
|
regAddr[1] = AR_PHY_BASE + (8 << 2);
|
|
|
|
} else
|
|
|
|
loop_max = 1;
|
|
|
|
|
|
|
|
for (i = 0; i < loop_max; i++) {
|
2008-10-29 12:46:30 +08:00
|
|
|
u32 addr = regAddr[i];
|
|
|
|
u32 wrData, rdData;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
regHold[i] = REG_READ(ah, addr);
|
|
|
|
for (j = 0; j < 0x100; j++) {
|
|
|
|
wrData = (j << 16) | j;
|
|
|
|
REG_WRITE(ah, addr, wrData);
|
|
|
|
rdData = REG_READ(ah, addr);
|
|
|
|
if (rdData != wrData) {
|
2010-12-03 11:12:36 +08:00
|
|
|
ath_err(common,
|
|
|
|
"address test failed addr: 0x%08x - wr:0x%08x != rd:0x%08x\n",
|
|
|
|
addr, wrData, rdData);
|
2008-10-29 12:46:30 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (j = 0; j < 4; j++) {
|
|
|
|
wrData = patternData[j];
|
|
|
|
REG_WRITE(ah, addr, wrData);
|
|
|
|
rdData = REG_READ(ah, addr);
|
|
|
|
if (wrData != rdData) {
|
2010-12-03 11:12:36 +08:00
|
|
|
ath_err(common,
|
|
|
|
"address test failed addr: 0x%08x - wr:0x%08x != rd:0x%08x\n",
|
|
|
|
addr, wrData, rdData);
|
2008-10-29 12:46:30 +08:00
|
|
|
return false;
|
|
|
|
}
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_WRITE(ah, regAddr[i], regHold[i]);
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
2008-10-29 12:46:30 +08:00
|
|
|
udelay(100);
|
2009-02-09 15:57:12 +08:00
|
|
|
|
2008-08-04 15:16:41 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2009-08-04 03:24:43 +08:00
|
|
|
static void ath9k_hw_init_config(struct ath_hw *ah)
|
2008-10-29 12:46:30 +08:00
|
|
|
{
|
|
|
|
int i;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-02-09 15:57:26 +08:00
|
|
|
ah->config.dma_beacon_response_time = 2;
|
|
|
|
ah->config.sw_beacon_response_time = 10;
|
|
|
|
ah->config.additional_swba_backoff = 0;
|
|
|
|
ah->config.ack_6mb = 0x0;
|
|
|
|
ah->config.cwm_ignore_extcca = 0;
|
|
|
|
ah->config.pcie_clock_req = 0;
|
|
|
|
ah->config.pcie_waen = 0;
|
|
|
|
ah->config.analog_shiftreg = 1;
|
2010-06-12 12:33:46 +08:00
|
|
|
ah->config.enable_ani = true;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) {
|
2009-02-09 15:57:26 +08:00
|
|
|
ah->config.spurchans[i][0] = AR_NO_SPUR;
|
|
|
|
ah->config.spurchans[i][1] = AR_NO_SPUR;
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
|
2011-01-21 09:47:39 +08:00
|
|
|
/* PAPRD needs some more work to be enabled */
|
|
|
|
ah->config.paprd_disable = 1;
|
|
|
|
|
2009-12-14 17:27:00 +08:00
|
|
|
ah->config.rx_intr_mitigation = true;
|
2010-06-22 06:38:49 +08:00
|
|
|
ah->config.pcieSerDesWrite = true;
|
2009-03-13 06:18:49 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We need this for PCI devices only (Cardbus, PCI, miniPCI)
|
|
|
|
* _and_ if on non-uniprocessor systems (Multiprocessor/HT).
|
|
|
|
* This means we use it for all AR5416 devices, and the few
|
|
|
|
* minor PCI AR9280 devices out there.
|
|
|
|
*
|
|
|
|
* Serialization is required because these devices do not handle
|
|
|
|
* well the case of two concurrent reads/writes due to the latency
|
|
|
|
* involved. During one read/write another read/write can be issued
|
|
|
|
* on another CPU while the previous read/write may still be working
|
|
|
|
* on our hardware, if we hit this case the hardware poops in a loop.
|
|
|
|
* We prevent this by serializing reads and writes.
|
|
|
|
*
|
|
|
|
* This issue is not present on PCI-Express devices or pre-AR5416
|
|
|
|
* devices (legacy, 802.11abg).
|
|
|
|
*/
|
|
|
|
if (num_possible_cpus() > 1)
|
2009-03-18 06:01:30 +08:00
|
|
|
ah->config.serialize_regmode = SER_REG_MODE_AUTO;
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
|
2009-08-04 03:24:42 +08:00
|
|
|
static void ath9k_hw_init_defaults(struct ath_hw *ah)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2009-08-18 09:07:23 +08:00
|
|
|
struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
|
|
|
|
|
|
|
|
regulatory->country_code = CTRY_DEFAULT;
|
|
|
|
regulatory->power_limit = MAX_RATE_POWER;
|
|
|
|
|
2009-02-09 15:57:06 +08:00
|
|
|
ah->hw_version.magic = AR5416_MAGIC;
|
|
|
|
ah->hw_version.subvendorid = 0;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-02-09 15:57:26 +08:00
|
|
|
ah->atim_window = 0;
|
2010-06-12 23:22:32 +08:00
|
|
|
ah->sta_id1_defaults =
|
|
|
|
AR_STA_ID1_CRPT_MIC_ENABLE |
|
|
|
|
AR_STA_ID1_MCAST_KSRCH;
|
2011-03-19 20:55:41 +08:00
|
|
|
if (AR_SREV_9100(ah))
|
|
|
|
ah->sta_id1_defaults |= AR_STA_ID1_AR9100_BA_FIX;
|
2009-02-09 15:57:26 +08:00
|
|
|
ah->enable_32kHz_clock = DONT_USE_32KHZ;
|
2011-08-27 13:52:59 +08:00
|
|
|
ah->slottime = ATH9K_SLOT_TIME_9;
|
2009-02-09 15:57:26 +08:00
|
|
|
ah->globaltxtimeout = (u32) -1;
|
2009-07-24 23:27:22 +08:00
|
|
|
ah->power_mode = ATH9K_PM_UNDEFINED;
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
static int ath9k_hw_init_macaddr(struct ath_hw *ah)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2009-09-11 00:22:37 +08:00
|
|
|
struct ath_common *common = ath9k_hw_common(ah);
|
2008-08-04 15:16:41 +08:00
|
|
|
u32 sum;
|
|
|
|
int i;
|
|
|
|
u16 eeval;
|
2010-11-21 10:38:53 +08:00
|
|
|
static const u32 EEP_MAC[] = { EEP_MAC_LSW, EEP_MAC_MID, EEP_MAC_MSW };
|
2008-08-04 15:16:41 +08:00
|
|
|
|
|
|
|
sum = 0;
|
|
|
|
for (i = 0; i < 3; i++) {
|
2010-04-16 05:39:13 +08:00
|
|
|
eeval = ah->eep_ops->get_eeprom(ah, EEP_MAC[i]);
|
2008-08-04 15:16:41 +08:00
|
|
|
sum += eeval;
|
2009-09-11 00:22:37 +08:00
|
|
|
common->macaddr[2 * i] = eeval >> 8;
|
|
|
|
common->macaddr[2 * i + 1] = eeval & 0xff;
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
2009-03-30 17:58:25 +08:00
|
|
|
if (sum == 0 || sum == 0xffff * 3)
|
2008-08-04 15:16:41 +08:00
|
|
|
return -EADDRNOTAVAIL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-08-04 03:24:46 +08:00
|
|
|
static int ath9k_hw_post_init(struct ath_hw *ah)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2011-01-04 15:46:37 +08:00
|
|
|
struct ath_common *common = ath9k_hw_common(ah);
|
2008-10-29 12:46:30 +08:00
|
|
|
int ecode;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2011-01-04 15:46:37 +08:00
|
|
|
if (common->bus_ops->ath_bus_type != ATH_USB) {
|
2010-03-17 16:55:16 +08:00
|
|
|
if (!ath9k_hw_chip_test(ah))
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-04-16 05:39:18 +08:00
|
|
|
if (!AR_SREV_9300_20_OR_LATER(ah)) {
|
|
|
|
ecode = ar9002_hw_rf_claim(ah);
|
|
|
|
if (ecode != 0)
|
|
|
|
return ecode;
|
|
|
|
}
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-08-04 03:24:46 +08:00
|
|
|
ecode = ath9k_hw_eeprom_init(ah);
|
2008-10-29 12:46:30 +08:00
|
|
|
if (ecode != 0)
|
|
|
|
return ecode;
|
2009-03-13 11:25:55 +08:00
|
|
|
|
2010-12-03 11:12:37 +08:00
|
|
|
ath_dbg(ath9k_hw_common(ah), ATH_DBG_CONFIG,
|
|
|
|
"Eeprom VER: %d, REV: %d\n",
|
|
|
|
ah->eep_ops->get_eeprom_ver(ah),
|
|
|
|
ah->eep_ops->get_eeprom_rev(ah));
|
2009-03-13 11:25:55 +08:00
|
|
|
|
2010-04-16 05:38:14 +08:00
|
|
|
ecode = ath9k_hw_rf_alloc_ext_banks(ah);
|
|
|
|
if (ecode) {
|
2010-12-03 11:12:36 +08:00
|
|
|
ath_err(ath9k_hw_common(ah),
|
|
|
|
"Failed allocating banks for external radio\n");
|
2010-11-08 23:10:53 +08:00
|
|
|
ath9k_hw_rf_free_ext_banks(ah);
|
2010-04-16 05:38:14 +08:00
|
|
|
return ecode;
|
2009-10-19 14:33:37 +08:00
|
|
|
}
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2011-04-19 21:59:05 +08:00
|
|
|
if (!AR_SREV_9100(ah) && !AR_SREV_9340(ah)) {
|
2008-10-29 12:46:30 +08:00
|
|
|
ath9k_hw_ani_setup(ah);
|
2009-08-04 03:24:46 +08:00
|
|
|
ath9k_hw_ani_init(ah);
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-04-16 05:38:19 +08:00
|
|
|
static void ath9k_hw_attach_ops(struct ath_hw *ah)
|
2009-08-04 03:24:39 +08:00
|
|
|
{
|
2010-04-16 05:38:19 +08:00
|
|
|
if (AR_SREV_9300_20_OR_LATER(ah))
|
|
|
|
ar9003_hw_attach_ops(ah);
|
|
|
|
else
|
|
|
|
ar9002_hw_attach_ops(ah);
|
2009-08-04 03:24:45 +08:00
|
|
|
}
|
|
|
|
|
2010-04-16 05:38:06 +08:00
|
|
|
/* Called for all hardware families */
|
|
|
|
static int __ath9k_hw_init(struct ath_hw *ah)
|
2009-08-04 03:24:45 +08:00
|
|
|
{
|
2009-09-13 17:42:02 +08:00
|
|
|
struct ath_common *common = ath9k_hw_common(ah);
|
2009-08-04 03:24:54 +08:00
|
|
|
int r = 0;
|
2009-08-04 03:24:45 +08:00
|
|
|
|
2010-12-22 23:44:20 +08:00
|
|
|
ath9k_hw_read_revisions(ah);
|
|
|
|
|
2010-12-22 21:47:18 +08:00
|
|
|
/*
|
|
|
|
* Read back AR_WA into a permanent copy and set bits 14 and 17.
|
|
|
|
* We need to do this to avoid RMW of this register. We cannot
|
|
|
|
* read the reg when chip is asleep.
|
|
|
|
*/
|
|
|
|
ah->WARegVal = REG_READ(ah, AR_WA);
|
|
|
|
ah->WARegVal |= (AR_WA_D3_L1_DISABLE |
|
|
|
|
AR_WA_ASPM_TIMER_BASED_DISABLE);
|
|
|
|
|
2009-08-04 03:24:45 +08:00
|
|
|
if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) {
|
2010-12-03 11:12:36 +08:00
|
|
|
ath_err(common, "Couldn't reset chip\n");
|
2009-08-04 03:24:54 +08:00
|
|
|
return -EIO;
|
2009-08-04 03:24:45 +08:00
|
|
|
}
|
|
|
|
|
2011-10-13 13:30:44 +08:00
|
|
|
if (AR_SREV_9462(ah))
|
2011-10-13 13:19:13 +08:00
|
|
|
ah->WARegVal &= ~AR_WA_D3_L1_DISABLE;
|
|
|
|
|
2010-04-16 05:38:20 +08:00
|
|
|
ath9k_hw_init_defaults(ah);
|
|
|
|
ath9k_hw_init_config(ah);
|
|
|
|
|
2010-04-16 05:38:19 +08:00
|
|
|
ath9k_hw_attach_ops(ah);
|
2010-04-16 05:38:06 +08:00
|
|
|
|
2009-09-10 12:10:09 +08:00
|
|
|
if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) {
|
2010-12-03 11:12:36 +08:00
|
|
|
ath_err(common, "Couldn't wakeup chip\n");
|
2009-08-04 03:24:54 +08:00
|
|
|
return -EIO;
|
2009-08-04 03:24:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ah->config.serialize_regmode == SER_REG_MODE_AUTO) {
|
|
|
|
if (ah->hw_version.macVersion == AR_SREV_VERSION_5416_PCI ||
|
2010-07-28 22:06:35 +08:00
|
|
|
((AR_SREV_9160(ah) || AR_SREV_9280(ah)) &&
|
|
|
|
!ah->is_pciexpress)) {
|
2009-08-04 03:24:45 +08:00
|
|
|
ah->config.serialize_regmode =
|
|
|
|
SER_REG_MODE_ON;
|
|
|
|
} else {
|
|
|
|
ah->config.serialize_regmode =
|
|
|
|
SER_REG_MODE_OFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-03 11:12:37 +08:00
|
|
|
ath_dbg(common, ATH_DBG_RESET, "serialize_regmode is %d\n",
|
2009-08-04 03:24:45 +08:00
|
|
|
ah->config.serialize_regmode);
|
|
|
|
|
2009-11-25 10:37:57 +08:00
|
|
|
if (AR_SREV_9285(ah) || AR_SREV_9271(ah))
|
|
|
|
ah->config.max_txtrig_level = MAX_TX_FIFO_THRESHOLD >> 1;
|
|
|
|
else
|
|
|
|
ah->config.max_txtrig_level = MAX_TX_FIFO_THRESHOLD;
|
|
|
|
|
2010-12-12 07:51:12 +08:00
|
|
|
switch (ah->hw_version.macVersion) {
|
|
|
|
case AR_SREV_VERSION_5416_PCI:
|
|
|
|
case AR_SREV_VERSION_5416_PCIE:
|
|
|
|
case AR_SREV_VERSION_9160:
|
|
|
|
case AR_SREV_VERSION_9100:
|
|
|
|
case AR_SREV_VERSION_9280:
|
|
|
|
case AR_SREV_VERSION_9285:
|
|
|
|
case AR_SREV_VERSION_9287:
|
|
|
|
case AR_SREV_VERSION_9271:
|
|
|
|
case AR_SREV_VERSION_9300:
|
2011-06-21 17:23:21 +08:00
|
|
|
case AR_SREV_VERSION_9330:
|
2010-12-12 07:51:12 +08:00
|
|
|
case AR_SREV_VERSION_9485:
|
2011-04-19 21:59:20 +08:00
|
|
|
case AR_SREV_VERSION_9340:
|
2011-10-13 13:30:44 +08:00
|
|
|
case AR_SREV_VERSION_9462:
|
2010-12-12 07:51:12 +08:00
|
|
|
break;
|
|
|
|
default:
|
2010-12-03 11:12:36 +08:00
|
|
|
ath_err(common,
|
|
|
|
"Mac Chip Rev 0x%02x.%x is not supported by this driver\n",
|
|
|
|
ah->hw_version.macVersion, ah->hw_version.macRev);
|
2009-08-04 03:24:54 +08:00
|
|
|
return -EOPNOTSUPP;
|
2009-08-04 03:24:45 +08:00
|
|
|
}
|
|
|
|
|
2011-06-21 17:23:21 +08:00
|
|
|
if (AR_SREV_9271(ah) || AR_SREV_9100(ah) || AR_SREV_9340(ah) ||
|
|
|
|
AR_SREV_9330(ah))
|
2009-08-04 11:14:12 +08:00
|
|
|
ah->is_pciexpress = false;
|
|
|
|
|
2009-08-04 03:24:45 +08:00
|
|
|
ah->hw_version.phyRev = REG_READ(ah, AR_PHY_CHIP_ID);
|
|
|
|
ath9k_hw_init_cal_settings(ah);
|
|
|
|
|
|
|
|
ah->ani_function = ATH9K_ANI_ALL;
|
2010-09-22 18:34:52 +08:00
|
|
|
if (AR_SREV_9280_20_OR_LATER(ah) && !AR_SREV_9300_20_OR_LATER(ah))
|
2009-08-04 03:24:45 +08:00
|
|
|
ah->ani_function &= ~ATH9K_ANI_NOISE_IMMUNITY_LEVEL;
|
2010-06-12 12:33:45 +08:00
|
|
|
if (!AR_SREV_9300_20_OR_LATER(ah))
|
|
|
|
ah->ani_function &= ~ATH9K_ANI_MRC_CCK;
|
2009-08-04 03:24:45 +08:00
|
|
|
|
|
|
|
ath9k_hw_init_mode_regs(ah);
|
|
|
|
|
2011-08-05 19:10:34 +08:00
|
|
|
if (!ah->is_pciexpress)
|
2009-08-04 03:24:45 +08:00
|
|
|
ath9k_hw_disablepcie(ah);
|
|
|
|
|
2010-04-16 05:39:04 +08:00
|
|
|
if (!AR_SREV_9300_20_OR_LATER(ah))
|
|
|
|
ar9002_hw_cck_chan14_spread(ah);
|
2009-09-18 17:34:07 +08:00
|
|
|
|
2009-08-04 03:24:46 +08:00
|
|
|
r = ath9k_hw_post_init(ah);
|
2009-08-04 03:24:45 +08:00
|
|
|
if (r)
|
2009-08-04 03:24:54 +08:00
|
|
|
return r;
|
2009-08-04 03:24:45 +08:00
|
|
|
|
|
|
|
ath9k_hw_init_mode_gain_regs(ah);
|
2009-11-27 19:01:35 +08:00
|
|
|
r = ath9k_hw_fill_cap_info(ah);
|
|
|
|
if (r)
|
|
|
|
return r;
|
|
|
|
|
2011-08-05 19:10:34 +08:00
|
|
|
if (ah->is_pciexpress)
|
|
|
|
ath9k_hw_aspm_init(ah);
|
|
|
|
|
2009-08-04 03:24:36 +08:00
|
|
|
r = ath9k_hw_init_macaddr(ah);
|
|
|
|
if (r) {
|
2010-12-03 11:12:36 +08:00
|
|
|
ath_err(common, "Failed to initialize MAC address\n");
|
2009-08-04 03:24:54 +08:00
|
|
|
return r;
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
|
2009-08-04 11:14:12 +08:00
|
|
|
if (AR_SREV_9285(ah) || AR_SREV_9271(ah))
|
2009-02-09 15:57:26 +08:00
|
|
|
ah->tx_trig_level = (AR_FTRIG_256B >> AR_FTRIG_S);
|
2008-10-29 12:46:30 +08:00
|
|
|
else
|
2009-02-09 15:57:26 +08:00
|
|
|
ah->tx_trig_level = (AR_FTRIG_512B >> AR_FTRIG_S);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2011-06-21 17:23:30 +08:00
|
|
|
if (AR_SREV_9330(ah))
|
|
|
|
ah->bb_watchdog_timeout_ms = 85;
|
|
|
|
else
|
|
|
|
ah->bb_watchdog_timeout_ms = 25;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-10-07 09:19:07 +08:00
|
|
|
common->state = ATH_HW_INITIALIZED;
|
|
|
|
|
2009-08-04 03:24:36 +08:00
|
|
|
return 0;
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
|
2010-04-16 05:38:06 +08:00
|
|
|
int ath9k_hw_init(struct ath_hw *ah)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2010-04-16 05:38:06 +08:00
|
|
|
int ret;
|
|
|
|
struct ath_common *common = ath9k_hw_common(ah);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-04-16 05:38:06 +08:00
|
|
|
/* These are all the AR5008/AR9001/AR9002 hardware family of chipsets */
|
|
|
|
switch (ah->hw_version.devid) {
|
|
|
|
case AR5416_DEVID_PCI:
|
|
|
|
case AR5416_DEVID_PCIE:
|
|
|
|
case AR5416_AR9100_DEVID:
|
|
|
|
case AR9160_DEVID_PCI:
|
|
|
|
case AR9280_DEVID_PCI:
|
|
|
|
case AR9280_DEVID_PCIE:
|
|
|
|
case AR9285_DEVID_PCIE:
|
2010-04-16 05:38:18 +08:00
|
|
|
case AR9287_DEVID_PCI:
|
|
|
|
case AR9287_DEVID_PCIE:
|
2010-04-16 05:38:06 +08:00
|
|
|
case AR2427_DEVID_PCIE:
|
2010-04-16 05:38:18 +08:00
|
|
|
case AR9300_DEVID_PCIE:
|
2010-12-06 20:27:36 +08:00
|
|
|
case AR9300_DEVID_AR9485_PCIE:
|
2011-06-21 17:23:52 +08:00
|
|
|
case AR9300_DEVID_AR9330:
|
2011-04-19 21:59:20 +08:00
|
|
|
case AR9300_DEVID_AR9340:
|
2011-08-25 06:36:08 +08:00
|
|
|
case AR9300_DEVID_AR9580:
|
2011-10-13 13:30:44 +08:00
|
|
|
case AR9300_DEVID_AR9462:
|
2010-04-16 05:38:06 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (common->bus_ops->ath_bus_type == ATH_USB)
|
|
|
|
break;
|
2010-12-03 11:12:36 +08:00
|
|
|
ath_err(common, "Hardware device ID 0x%04x not supported\n",
|
|
|
|
ah->hw_version.devid);
|
2010-04-16 05:38:06 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-04-16 05:38:06 +08:00
|
|
|
ret = __ath9k_hw_init(ah);
|
|
|
|
if (ret) {
|
2010-12-03 11:12:36 +08:00
|
|
|
ath_err(common,
|
|
|
|
"Unable to initialize hardware; initialization status: %d\n",
|
|
|
|
ret);
|
2010-04-16 05:38:06 +08:00
|
|
|
return ret;
|
|
|
|
}
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-04-16 05:38:06 +08:00
|
|
|
return 0;
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
2010-04-16 05:38:06 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_init);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
static void ath9k_hw_init_qos(struct ath_hw *ah)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2010-04-16 14:23:57 +08:00
|
|
|
ENABLE_REGWRITE_BUFFER(ah);
|
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_WRITE(ah, AR_MIC_QOS_CONTROL, 0x100aa);
|
|
|
|
REG_WRITE(ah, AR_MIC_QOS_SELECT, 0x3210);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_WRITE(ah, AR_QOS_NO_ACK,
|
|
|
|
SM(2, AR_QOS_NO_ACK_TWO_BIT) |
|
|
|
|
SM(5, AR_QOS_NO_ACK_BIT_OFF) |
|
|
|
|
SM(0, AR_QOS_NO_ACK_BYTE_OFF));
|
|
|
|
|
|
|
|
REG_WRITE(ah, AR_TXOP_X, AR_TXOP_X_VAL);
|
|
|
|
REG_WRITE(ah, AR_TXOP_0_3, 0xFFFFFFFF);
|
|
|
|
REG_WRITE(ah, AR_TXOP_4_7, 0xFFFFFFFF);
|
|
|
|
REG_WRITE(ah, AR_TXOP_8_11, 0xFFFFFFFF);
|
|
|
|
REG_WRITE(ah, AR_TXOP_12_15, 0xFFFFFFFF);
|
2010-04-16 14:23:57 +08:00
|
|
|
|
|
|
|
REGWRITE_BUFFER_FLUSH(ah);
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
|
2011-04-22 14:02:12 +08:00
|
|
|
u32 ar9003_get_pll_sqsum_dvc(struct ath_hw *ah)
|
2011-01-27 17:15:07 +08:00
|
|
|
{
|
2011-03-24 03:57:26 +08:00
|
|
|
REG_CLR_BIT(ah, PLL3, PLL3_DO_MEAS_MASK);
|
|
|
|
udelay(100);
|
|
|
|
REG_SET_BIT(ah, PLL3, PLL3_DO_MEAS_MASK);
|
2011-01-27 17:15:07 +08:00
|
|
|
|
2011-03-24 03:57:26 +08:00
|
|
|
while ((REG_READ(ah, PLL4) & PLL4_MEAS_DONE) == 0)
|
|
|
|
udelay(100);
|
2011-01-27 17:15:07 +08:00
|
|
|
|
2011-03-24 03:57:26 +08:00
|
|
|
return (REG_READ(ah, PLL3) & SQSUM_DVC_MASK) >> 3;
|
2011-01-27 17:15:07 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ar9003_get_pll_sqsum_dvc);
|
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
static void ath9k_hw_init_pll(struct ath_hw *ah,
|
2008-10-29 12:46:30 +08:00
|
|
|
struct ath9k_channel *chan)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2010-12-06 20:27:44 +08:00
|
|
|
u32 pll;
|
|
|
|
|
2011-01-27 17:15:09 +08:00
|
|
|
if (AR_SREV_9485(ah)) {
|
|
|
|
|
2011-04-11 19:09:40 +08:00
|
|
|
/* program BB PLL ki and kd value, ki=0x4, kd=0x40 */
|
|
|
|
REG_RMW_FIELD(ah, AR_CH0_BB_DPLL2,
|
|
|
|
AR_CH0_BB_DPLL2_PLL_PWD, 0x1);
|
|
|
|
REG_RMW_FIELD(ah, AR_CH0_BB_DPLL2,
|
|
|
|
AR_CH0_DPLL2_KD, 0x40);
|
|
|
|
REG_RMW_FIELD(ah, AR_CH0_BB_DPLL2,
|
|
|
|
AR_CH0_DPLL2_KI, 0x4);
|
2011-01-27 17:15:09 +08:00
|
|
|
|
2011-04-11 19:09:40 +08:00
|
|
|
REG_RMW_FIELD(ah, AR_CH0_BB_DPLL1,
|
|
|
|
AR_CH0_BB_DPLL1_REFDIV, 0x5);
|
|
|
|
REG_RMW_FIELD(ah, AR_CH0_BB_DPLL1,
|
|
|
|
AR_CH0_BB_DPLL1_NINI, 0x58);
|
|
|
|
REG_RMW_FIELD(ah, AR_CH0_BB_DPLL1,
|
|
|
|
AR_CH0_BB_DPLL1_NFRAC, 0x0);
|
2011-01-27 17:15:09 +08:00
|
|
|
|
|
|
|
REG_RMW_FIELD(ah, AR_CH0_BB_DPLL2,
|
2011-04-11 19:09:40 +08:00
|
|
|
AR_CH0_BB_DPLL2_OUTDIV, 0x1);
|
|
|
|
REG_RMW_FIELD(ah, AR_CH0_BB_DPLL2,
|
|
|
|
AR_CH0_BB_DPLL2_LOCAL_PLL, 0x1);
|
2011-01-27 17:15:09 +08:00
|
|
|
REG_RMW_FIELD(ah, AR_CH0_BB_DPLL2,
|
2011-04-11 19:09:40 +08:00
|
|
|
AR_CH0_BB_DPLL2_EN_NEGTRIG, 0x1);
|
2011-01-27 17:15:09 +08:00
|
|
|
|
2011-04-11 19:09:40 +08:00
|
|
|
/* program BB PLL phase_shift to 0x6 */
|
2011-01-27 17:15:09 +08:00
|
|
|
REG_RMW_FIELD(ah, AR_CH0_BB_DPLL3,
|
2011-04-11 19:09:40 +08:00
|
|
|
AR_CH0_BB_DPLL3_PHASE_SHIFT, 0x6);
|
|
|
|
|
|
|
|
REG_RMW_FIELD(ah, AR_CH0_BB_DPLL2,
|
|
|
|
AR_CH0_BB_DPLL2_PLL_PWD, 0x0);
|
2011-03-10 13:35:42 +08:00
|
|
|
udelay(1000);
|
2011-06-21 17:23:29 +08:00
|
|
|
} else if (AR_SREV_9330(ah)) {
|
|
|
|
u32 ddr_dpll2, pll_control2, kd;
|
|
|
|
|
|
|
|
if (ah->is_clk_25mhz) {
|
|
|
|
ddr_dpll2 = 0x18e82f01;
|
|
|
|
pll_control2 = 0xe04a3d;
|
|
|
|
kd = 0x1d;
|
|
|
|
} else {
|
|
|
|
ddr_dpll2 = 0x19e82f01;
|
|
|
|
pll_control2 = 0x886666;
|
|
|
|
kd = 0x3d;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* program DDR PLL ki and kd value */
|
|
|
|
REG_WRITE(ah, AR_CH0_DDR_DPLL2, ddr_dpll2);
|
|
|
|
|
|
|
|
/* program DDR PLL phase_shift */
|
|
|
|
REG_RMW_FIELD(ah, AR_CH0_DDR_DPLL3,
|
|
|
|
AR_CH0_DPLL3_PHASE_SHIFT, 0x1);
|
|
|
|
|
|
|
|
REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x1142c);
|
|
|
|
udelay(1000);
|
|
|
|
|
|
|
|
/* program refdiv, nint, frac to RTC register */
|
|
|
|
REG_WRITE(ah, AR_RTC_PLL_CONTROL2, pll_control2);
|
|
|
|
|
|
|
|
/* program BB PLL kd and ki value */
|
|
|
|
REG_RMW_FIELD(ah, AR_CH0_BB_DPLL2, AR_CH0_DPLL2_KD, kd);
|
|
|
|
REG_RMW_FIELD(ah, AR_CH0_BB_DPLL2, AR_CH0_DPLL2_KI, 0x06);
|
|
|
|
|
|
|
|
/* program BB PLL phase_shift */
|
|
|
|
REG_RMW_FIELD(ah, AR_CH0_BB_DPLL3,
|
|
|
|
AR_CH0_BB_DPLL3_PHASE_SHIFT, 0x1);
|
2011-04-20 12:56:15 +08:00
|
|
|
} else if (AR_SREV_9340(ah)) {
|
|
|
|
u32 regval, pll2_divint, pll2_divfrac, refdiv;
|
|
|
|
|
|
|
|
REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x1142c);
|
|
|
|
udelay(1000);
|
|
|
|
|
|
|
|
REG_SET_BIT(ah, AR_PHY_PLL_MODE, 0x1 << 16);
|
|
|
|
udelay(100);
|
|
|
|
|
|
|
|
if (ah->is_clk_25mhz) {
|
|
|
|
pll2_divint = 0x54;
|
|
|
|
pll2_divfrac = 0x1eb85;
|
|
|
|
refdiv = 3;
|
|
|
|
} else {
|
|
|
|
pll2_divint = 88;
|
|
|
|
pll2_divfrac = 0;
|
|
|
|
refdiv = 5;
|
|
|
|
}
|
|
|
|
|
|
|
|
regval = REG_READ(ah, AR_PHY_PLL_MODE);
|
|
|
|
regval |= (0x1 << 16);
|
|
|
|
REG_WRITE(ah, AR_PHY_PLL_MODE, regval);
|
|
|
|
udelay(100);
|
|
|
|
|
|
|
|
REG_WRITE(ah, AR_PHY_PLL_CONTROL, (refdiv << 27) |
|
|
|
|
(pll2_divint << 18) | pll2_divfrac);
|
|
|
|
udelay(100);
|
|
|
|
|
|
|
|
regval = REG_READ(ah, AR_PHY_PLL_MODE);
|
|
|
|
regval = (regval & 0x80071fff) | (0x1 << 30) | (0x1 << 13) |
|
|
|
|
(0x4 << 26) | (0x18 << 19);
|
|
|
|
REG_WRITE(ah, AR_PHY_PLL_MODE, regval);
|
|
|
|
REG_WRITE(ah, AR_PHY_PLL_MODE,
|
|
|
|
REG_READ(ah, AR_PHY_PLL_MODE) & 0xfffeffff);
|
|
|
|
udelay(1000);
|
2011-01-27 17:15:09 +08:00
|
|
|
}
|
2010-12-06 20:27:44 +08:00
|
|
|
|
|
|
|
pll = ath9k_hw_compute_pll_control(ah, chan);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-01-15 03:17:09 +08:00
|
|
|
REG_WRITE(ah, AR_RTC_PLL_CONTROL, pll);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2011-06-21 17:23:29 +08:00
|
|
|
if (AR_SREV_9485(ah) || AR_SREV_9340(ah) || AR_SREV_9330(ah))
|
2011-04-11 19:09:40 +08:00
|
|
|
udelay(1000);
|
|
|
|
|
2009-10-19 14:33:34 +08:00
|
|
|
/* Switch the core clock for ar9271 to 117Mhz */
|
|
|
|
if (AR_SREV_9271(ah)) {
|
2010-03-17 16:55:22 +08:00
|
|
|
udelay(500);
|
|
|
|
REG_WRITE(ah, 0x50040, 0x304);
|
2009-10-19 14:33:34 +08:00
|
|
|
}
|
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
udelay(RTC_PLL_SETTLE_DELAY);
|
|
|
|
|
|
|
|
REG_WRITE(ah, AR_RTC_SLEEP_CLK, AR_RTC_FORCE_DERIVED_CLK);
|
2011-04-20 12:56:15 +08:00
|
|
|
|
|
|
|
if (AR_SREV_9340(ah)) {
|
|
|
|
if (ah->is_clk_25mhz) {
|
|
|
|
REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x17c << 1);
|
|
|
|
REG_WRITE(ah, AR_SLP32_MODE, 0x0010f3d7);
|
|
|
|
REG_WRITE(ah, AR_SLP32_INC, 0x0001e7ae);
|
|
|
|
} else {
|
|
|
|
REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x261 << 1);
|
|
|
|
REG_WRITE(ah, AR_SLP32_MODE, 0x0010f400);
|
|
|
|
REG_WRITE(ah, AR_SLP32_INC, 0x0001e800);
|
|
|
|
}
|
|
|
|
udelay(100);
|
|
|
|
}
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
static void ath9k_hw_init_interrupt_masks(struct ath_hw *ah,
|
2008-12-02 05:38:55 +08:00
|
|
|
enum nl80211_iftype opmode)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2011-04-19 21:59:19 +08:00
|
|
|
u32 sync_default = AR_INTR_SYNC_DEFAULT;
|
2010-04-01 06:05:37 +08:00
|
|
|
u32 imr_reg = AR_IMR_TXERR |
|
2008-10-29 12:46:30 +08:00
|
|
|
AR_IMR_TXURN |
|
|
|
|
AR_IMR_RXERR |
|
|
|
|
AR_IMR_RXORN |
|
|
|
|
AR_IMR_BCNMISC;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2011-04-19 21:59:19 +08:00
|
|
|
if (AR_SREV_9340(ah))
|
|
|
|
sync_default &= ~AR_INTR_SYNC_HOST1_FATAL;
|
|
|
|
|
2010-04-16 05:39:07 +08:00
|
|
|
if (AR_SREV_9300_20_OR_LATER(ah)) {
|
|
|
|
imr_reg |= AR_IMR_RXOK_HP;
|
|
|
|
if (ah->config.rx_intr_mitigation)
|
|
|
|
imr_reg |= AR_IMR_RXINTM | AR_IMR_RXMINTR;
|
|
|
|
else
|
|
|
|
imr_reg |= AR_IMR_RXOK_LP;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-04-16 05:39:07 +08:00
|
|
|
} else {
|
|
|
|
if (ah->config.rx_intr_mitigation)
|
|
|
|
imr_reg |= AR_IMR_RXINTM | AR_IMR_RXMINTR;
|
|
|
|
else
|
|
|
|
imr_reg |= AR_IMR_RXOK;
|
|
|
|
}
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-04-16 05:39:07 +08:00
|
|
|
if (ah->config.tx_intr_mitigation)
|
|
|
|
imr_reg |= AR_IMR_TXINTM | AR_IMR_TXMINTR;
|
|
|
|
else
|
|
|
|
imr_reg |= AR_IMR_TXOK;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-12-02 05:38:55 +08:00
|
|
|
if (opmode == NL80211_IFTYPE_AP)
|
2010-04-01 06:05:37 +08:00
|
|
|
imr_reg |= AR_IMR_MIB;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-04-16 14:23:57 +08:00
|
|
|
ENABLE_REGWRITE_BUFFER(ah);
|
|
|
|
|
2010-04-01 06:05:37 +08:00
|
|
|
REG_WRITE(ah, AR_IMR, imr_reg);
|
2010-02-24 07:15:27 +08:00
|
|
|
ah->imrs2_reg |= AR_IMR_S2_GTT;
|
|
|
|
REG_WRITE(ah, AR_IMR_S2, ah->imrs2_reg);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
if (!AR_SREV_9100(ah)) {
|
|
|
|
REG_WRITE(ah, AR_INTR_SYNC_CAUSE, 0xFFFFFFFF);
|
2011-04-19 21:59:19 +08:00
|
|
|
REG_WRITE(ah, AR_INTR_SYNC_ENABLE, sync_default);
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_WRITE(ah, AR_INTR_SYNC_MASK, 0);
|
|
|
|
}
|
2010-04-16 05:39:07 +08:00
|
|
|
|
2010-04-16 14:23:57 +08:00
|
|
|
REGWRITE_BUFFER_FLUSH(ah);
|
|
|
|
|
2010-04-16 05:39:07 +08:00
|
|
|
if (AR_SREV_9300_20_OR_LATER(ah)) {
|
|
|
|
REG_WRITE(ah, AR_INTR_PRIO_ASYNC_ENABLE, 0);
|
|
|
|
REG_WRITE(ah, AR_INTR_PRIO_ASYNC_MASK, 0);
|
|
|
|
REG_WRITE(ah, AR_INTR_PRIO_SYNC_ENABLE, 0);
|
|
|
|
REG_WRITE(ah, AR_INTR_PRIO_SYNC_MASK, 0);
|
|
|
|
}
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
|
2011-07-09 12:12:50 +08:00
|
|
|
static void ath9k_hw_set_sifs_time(struct ath_hw *ah, u32 us)
|
|
|
|
{
|
|
|
|
u32 val = ath9k_hw_mac_to_clks(ah, us - 2);
|
|
|
|
val = min(val, (u32) 0xFFFF);
|
|
|
|
REG_WRITE(ah, AR_D_GBL_IFS_SIFS, val);
|
|
|
|
}
|
|
|
|
|
2010-01-15 09:33:40 +08:00
|
|
|
static void ath9k_hw_setslottime(struct ath_hw *ah, u32 us)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2010-01-15 09:33:40 +08:00
|
|
|
u32 val = ath9k_hw_mac_to_clks(ah, us);
|
|
|
|
val = min(val, (u32) 0xFFFF);
|
|
|
|
REG_WRITE(ah, AR_D_GBL_IFS_SLOT, val);
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
|
2010-01-15 09:33:40 +08:00
|
|
|
static void ath9k_hw_set_ack_timeout(struct ath_hw *ah, u32 us)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2010-01-15 09:33:40 +08:00
|
|
|
u32 val = ath9k_hw_mac_to_clks(ah, us);
|
|
|
|
val = min(val, (u32) MS(0xFFFFFFFF, AR_TIME_OUT_ACK));
|
|
|
|
REG_RMW_FIELD(ah, AR_TIME_OUT, AR_TIME_OUT_ACK, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath9k_hw_set_cts_timeout(struct ath_hw *ah, u32 us)
|
|
|
|
{
|
|
|
|
u32 val = ath9k_hw_mac_to_clks(ah, us);
|
|
|
|
val = min(val, (u32) MS(0xFFFFFFFF, AR_TIME_OUT_CTS));
|
|
|
|
REG_RMW_FIELD(ah, AR_TIME_OUT, AR_TIME_OUT_CTS, val);
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
2008-10-29 12:46:30 +08:00
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
static bool ath9k_hw_set_global_txtimeout(struct ath_hw *ah, u32 tu)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
|
|
|
if (tu > 0xFFFF) {
|
2010-12-03 11:12:37 +08:00
|
|
|
ath_dbg(ath9k_hw_common(ah), ATH_DBG_XMIT,
|
|
|
|
"bad global tx timeout %u\n", tu);
|
2009-02-09 15:57:26 +08:00
|
|
|
ah->globaltxtimeout = (u32) -1;
|
2008-08-04 15:16:41 +08:00
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
REG_RMW_FIELD(ah, AR_GTXTO, AR_GTXTO_TIMEOUT_LIMIT, tu);
|
2009-02-09 15:57:26 +08:00
|
|
|
ah->globaltxtimeout = tu;
|
2008-08-04 15:16:41 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-15 09:33:40 +08:00
|
|
|
void ath9k_hw_init_global_settings(struct ath_hw *ah)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2011-07-09 12:12:50 +08:00
|
|
|
struct ath_common *common = ath9k_hw_common(ah);
|
|
|
|
struct ieee80211_conf *conf = &common->hw->conf;
|
|
|
|
const struct ath9k_channel *chan = ah->curchan;
|
2011-08-28 07:52:10 +08:00
|
|
|
int acktimeout, ctstimeout;
|
2010-01-15 09:34:58 +08:00
|
|
|
int slottime;
|
2010-01-15 09:33:40 +08:00
|
|
|
int sifstime;
|
2011-07-09 12:12:50 +08:00
|
|
|
int rx_lat = 0, tx_lat = 0, eifs = 0;
|
|
|
|
u32 reg;
|
2010-01-15 09:33:40 +08:00
|
|
|
|
2010-12-03 11:12:37 +08:00
|
|
|
ath_dbg(ath9k_hw_common(ah), ATH_DBG_RESET, "ah->misc_mode 0x%x\n",
|
|
|
|
ah->misc_mode);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2011-07-09 12:12:50 +08:00
|
|
|
if (!chan)
|
|
|
|
return;
|
|
|
|
|
2009-02-09 15:57:26 +08:00
|
|
|
if (ah->misc_mode != 0)
|
2011-03-24 03:57:26 +08:00
|
|
|
REG_SET_BIT(ah, AR_PCU_MISC, ah->misc_mode);
|
2010-01-15 09:33:40 +08:00
|
|
|
|
2011-08-31 13:17:30 +08:00
|
|
|
if (IS_CHAN_A_FAST_CLOCK(ah, chan))
|
|
|
|
rx_lat = 41;
|
|
|
|
else
|
|
|
|
rx_lat = 37;
|
2011-07-09 12:12:50 +08:00
|
|
|
tx_lat = 54;
|
|
|
|
|
|
|
|
if (IS_CHAN_HALF_RATE(chan)) {
|
|
|
|
eifs = 175;
|
|
|
|
rx_lat *= 2;
|
|
|
|
tx_lat *= 2;
|
|
|
|
if (IS_CHAN_A_FAST_CLOCK(ah, chan))
|
|
|
|
tx_lat += 11;
|
|
|
|
|
|
|
|
slottime = 13;
|
|
|
|
sifstime = 32;
|
|
|
|
} else if (IS_CHAN_QUARTER_RATE(chan)) {
|
|
|
|
eifs = 340;
|
2011-08-31 13:17:30 +08:00
|
|
|
rx_lat = (rx_lat * 4) - 1;
|
2011-07-09 12:12:50 +08:00
|
|
|
tx_lat *= 4;
|
|
|
|
if (IS_CHAN_A_FAST_CLOCK(ah, chan))
|
|
|
|
tx_lat += 22;
|
|
|
|
|
|
|
|
slottime = 21;
|
|
|
|
sifstime = 64;
|
|
|
|
} else {
|
2011-08-27 14:43:21 +08:00
|
|
|
if (AR_SREV_9287(ah) && AR_SREV_9287_13_OR_LATER(ah)) {
|
|
|
|
eifs = AR_D_GBL_IFS_EIFS_ASYNC_FIFO;
|
|
|
|
reg = AR_USEC_ASYNC_FIFO;
|
|
|
|
} else {
|
|
|
|
eifs = REG_READ(ah, AR_D_GBL_IFS_EIFS)/
|
|
|
|
common->clockrate;
|
|
|
|
reg = REG_READ(ah, AR_USEC);
|
|
|
|
}
|
2011-07-09 12:12:50 +08:00
|
|
|
rx_lat = MS(reg, AR_USEC_RX_LAT);
|
|
|
|
tx_lat = MS(reg, AR_USEC_TX_LAT);
|
|
|
|
|
|
|
|
slottime = ah->slottime;
|
|
|
|
if (IS_CHAN_5GHZ(chan))
|
|
|
|
sifstime = 16;
|
|
|
|
else
|
|
|
|
sifstime = 10;
|
|
|
|
}
|
2010-01-15 09:33:40 +08:00
|
|
|
|
2010-01-15 09:34:58 +08:00
|
|
|
/* As defined by IEEE 802.11-2007 17.3.8.6 */
|
2011-07-09 12:12:50 +08:00
|
|
|
acktimeout = slottime + sifstime + 3 * ah->coverage_class;
|
2011-08-28 07:52:10 +08:00
|
|
|
ctstimeout = acktimeout;
|
2010-02-12 01:07:19 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Workaround for early ACK timeouts, add an offset to match the
|
|
|
|
* initval's 64us ack timeout value.
|
|
|
|
* This was initially only meant to work around an issue with delayed
|
|
|
|
* BA frames in some implementations, but it has been found to fix ACK
|
|
|
|
* timeout issues in other cases as well.
|
|
|
|
*/
|
|
|
|
if (conf->channel && conf->channel->band == IEEE80211_BAND_2GHZ)
|
|
|
|
acktimeout += 64 - sifstime - ah->slottime;
|
|
|
|
|
2011-07-09 12:12:50 +08:00
|
|
|
ath9k_hw_set_sifs_time(ah, sifstime);
|
|
|
|
ath9k_hw_setslottime(ah, slottime);
|
2010-01-15 09:33:40 +08:00
|
|
|
ath9k_hw_set_ack_timeout(ah, acktimeout);
|
2011-08-28 07:52:10 +08:00
|
|
|
ath9k_hw_set_cts_timeout(ah, ctstimeout);
|
2009-02-09 15:57:26 +08:00
|
|
|
if (ah->globaltxtimeout != (u32) -1)
|
|
|
|
ath9k_hw_set_global_txtimeout(ah, ah->globaltxtimeout);
|
2011-07-09 12:12:50 +08:00
|
|
|
|
|
|
|
REG_WRITE(ah, AR_D_GBL_IFS_EIFS, ath9k_hw_mac_to_clks(ah, eifs));
|
|
|
|
REG_RMW(ah, AR_USEC,
|
|
|
|
(common->clockrate - 1) |
|
|
|
|
SM(rx_lat, AR_USEC_RX_LAT) |
|
|
|
|
SM(tx_lat, AR_USEC_TX_LAT),
|
|
|
|
AR_USEC_TX_LAT | AR_USEC_RX_LAT | AR_USEC_USEC);
|
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
}
|
2010-01-15 09:33:40 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_init_global_settings);
|
2008-10-29 12:46:30 +08:00
|
|
|
|
2010-01-08 13:06:07 +08:00
|
|
|
void ath9k_hw_deinit(struct ath_hw *ah)
|
2008-10-29 12:46:30 +08:00
|
|
|
{
|
2009-10-07 09:19:07 +08:00
|
|
|
struct ath_common *common = ath9k_hw_common(ah);
|
|
|
|
|
2010-03-17 16:55:24 +08:00
|
|
|
if (common->state < ATH_HW_INITIALIZED)
|
2009-10-07 09:19:07 +08:00
|
|
|
goto free_hw;
|
|
|
|
|
2009-09-10 12:10:09 +08:00
|
|
|
ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP);
|
2009-10-07 09:19:07 +08:00
|
|
|
|
|
|
|
free_hw:
|
2010-04-16 05:38:14 +08:00
|
|
|
ath9k_hw_rf_free_ext_banks(ah);
|
2008-10-29 12:46:30 +08:00
|
|
|
}
|
2010-01-08 13:06:07 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_deinit);
|
2008-10-29 12:46:30 +08:00
|
|
|
|
|
|
|
/*******/
|
|
|
|
/* INI */
|
|
|
|
/*******/
|
|
|
|
|
2010-04-16 05:38:14 +08:00
|
|
|
u32 ath9k_regd_get_ctl(struct ath_regulatory *reg, struct ath9k_channel *chan)
|
2009-03-31 10:30:29 +08:00
|
|
|
{
|
|
|
|
u32 ctl = ath_regd_get_band_ctl(reg, chan->chan->band);
|
|
|
|
|
|
|
|
if (IS_CHAN_B(chan))
|
|
|
|
ctl |= CTL_11B;
|
|
|
|
else if (IS_CHAN_G(chan))
|
|
|
|
ctl |= CTL_11G;
|
|
|
|
else
|
|
|
|
ctl |= CTL_11A;
|
|
|
|
|
|
|
|
return ctl;
|
|
|
|
}
|
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
/****************************************/
|
|
|
|
/* Reset and Channel Switching Routines */
|
|
|
|
/****************************************/
|
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
static inline void ath9k_hw_set_dma(struct ath_hw *ah)
|
2008-10-29 12:46:30 +08:00
|
|
|
{
|
2010-04-16 05:39:22 +08:00
|
|
|
struct ath_common *common = ath9k_hw_common(ah);
|
2008-10-29 12:46:30 +08:00
|
|
|
|
2010-04-16 14:23:57 +08:00
|
|
|
ENABLE_REGWRITE_BUFFER(ah);
|
|
|
|
|
2009-08-04 11:14:12 +08:00
|
|
|
/*
|
|
|
|
* set AHB_MODE not to do cacheline prefetches
|
|
|
|
*/
|
2011-03-24 03:57:26 +08:00
|
|
|
if (!AR_SREV_9300_20_OR_LATER(ah))
|
|
|
|
REG_SET_BIT(ah, AR_AHB_MODE, AR_AHB_PREFETCH_RD_EN);
|
2008-10-29 12:46:30 +08:00
|
|
|
|
2009-08-04 11:14:12 +08:00
|
|
|
/*
|
|
|
|
* let mac dma reads be in 128 byte chunks
|
|
|
|
*/
|
2011-03-24 03:57:26 +08:00
|
|
|
REG_RMW(ah, AR_TXCFG, AR_TXCFG_DMASZ_128B, AR_TXCFG_DMASZ_MASK);
|
2008-10-29 12:46:30 +08:00
|
|
|
|
2010-04-16 14:23:57 +08:00
|
|
|
REGWRITE_BUFFER_FLUSH(ah);
|
|
|
|
|
2009-08-04 11:14:12 +08:00
|
|
|
/*
|
|
|
|
* Restore TX Trigger Level to its pre-reset value.
|
|
|
|
* The initial value depends on whether aggregation is enabled, and is
|
|
|
|
* adjusted whenever underruns are detected.
|
|
|
|
*/
|
2010-04-16 05:39:22 +08:00
|
|
|
if (!AR_SREV_9300_20_OR_LATER(ah))
|
|
|
|
REG_RMW_FIELD(ah, AR_TXCFG, AR_FTRIG, ah->tx_trig_level);
|
2008-10-29 12:46:30 +08:00
|
|
|
|
2010-04-16 14:23:57 +08:00
|
|
|
ENABLE_REGWRITE_BUFFER(ah);
|
2008-10-29 12:46:30 +08:00
|
|
|
|
2009-08-04 11:14:12 +08:00
|
|
|
/*
|
|
|
|
* let mac dma writes be in 128 byte chunks
|
|
|
|
*/
|
2011-03-24 03:57:26 +08:00
|
|
|
REG_RMW(ah, AR_RXCFG, AR_RXCFG_DMASZ_128B, AR_RXCFG_DMASZ_MASK);
|
2008-10-29 12:46:30 +08:00
|
|
|
|
2009-08-04 11:14:12 +08:00
|
|
|
/*
|
|
|
|
* Setup receive FIFO threshold to hold off TX activities
|
|
|
|
*/
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_WRITE(ah, AR_RXFIFO_CFG, 0x200);
|
|
|
|
|
2010-04-16 05:39:22 +08:00
|
|
|
if (AR_SREV_9300_20_OR_LATER(ah)) {
|
|
|
|
REG_RMW_FIELD(ah, AR_RXBP_THRESH, AR_RXBP_THRESH_HP, 0x1);
|
|
|
|
REG_RMW_FIELD(ah, AR_RXBP_THRESH, AR_RXBP_THRESH_LP, 0x1);
|
|
|
|
|
|
|
|
ath9k_hw_set_rx_bufsize(ah, common->rx_bufsize -
|
|
|
|
ah->caps.rx_status_len);
|
|
|
|
}
|
|
|
|
|
2009-08-04 11:14:12 +08:00
|
|
|
/*
|
|
|
|
* reduce the number of usable entries in PCU TXBUF to avoid
|
|
|
|
* wrap around issues.
|
|
|
|
*/
|
2008-10-29 12:46:30 +08:00
|
|
|
if (AR_SREV_9285(ah)) {
|
2009-08-04 11:14:12 +08:00
|
|
|
/* For AR9285 the number of Fifos are reduced to half.
|
|
|
|
* So set the usable tx buf size also to half to
|
|
|
|
* avoid data/delimiter underruns
|
|
|
|
*/
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_WRITE(ah, AR_PCU_TXBUF_CTRL,
|
|
|
|
AR_9285_PCU_TXBUF_CTRL_USABLE_SIZE);
|
2009-08-04 11:14:12 +08:00
|
|
|
} else if (!AR_SREV_9271(ah)) {
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_WRITE(ah, AR_PCU_TXBUF_CTRL,
|
|
|
|
AR_PCU_TXBUF_CTRL_USABLE_SIZE);
|
|
|
|
}
|
2010-04-16 05:39:27 +08:00
|
|
|
|
2010-04-16 14:23:57 +08:00
|
|
|
REGWRITE_BUFFER_FLUSH(ah);
|
|
|
|
|
2010-04-16 05:39:27 +08:00
|
|
|
if (AR_SREV_9300_20_OR_LATER(ah))
|
|
|
|
ath9k_hw_reset_txstatus_ring(ah);
|
2008-10-29 12:46:30 +08:00
|
|
|
}
|
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
static void ath9k_hw_set_operating_mode(struct ath_hw *ah, int opmode)
|
2008-10-29 12:46:30 +08:00
|
|
|
{
|
2011-03-24 03:57:26 +08:00
|
|
|
u32 mask = AR_STA_ID1_STA_AP | AR_STA_ID1_ADHOC;
|
|
|
|
u32 set = AR_STA_ID1_KSRCH_MODE;
|
2008-10-29 12:46:30 +08:00
|
|
|
|
|
|
|
switch (opmode) {
|
2008-12-02 05:38:55 +08:00
|
|
|
case NL80211_IFTYPE_ADHOC:
|
2009-03-21 10:59:59 +08:00
|
|
|
case NL80211_IFTYPE_MESH_POINT:
|
2011-03-24 03:57:26 +08:00
|
|
|
set |= AR_STA_ID1_ADHOC;
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION);
|
2008-08-04 15:16:41 +08:00
|
|
|
break;
|
2011-03-24 03:57:26 +08:00
|
|
|
case NL80211_IFTYPE_AP:
|
|
|
|
set |= AR_STA_ID1_STA_AP;
|
|
|
|
/* fall through */
|
2008-12-02 05:38:55 +08:00
|
|
|
case NL80211_IFTYPE_STATION:
|
2011-03-24 03:57:26 +08:00
|
|
|
REG_CLR_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION);
|
2008-08-04 15:16:41 +08:00
|
|
|
break;
|
2010-10-27 21:01:15 +08:00
|
|
|
default:
|
2011-03-24 03:57:26 +08:00
|
|
|
if (!ah->is_monitoring)
|
|
|
|
set = 0;
|
2010-10-27 21:01:15 +08:00
|
|
|
break;
|
2008-10-29 12:46:30 +08:00
|
|
|
}
|
2011-03-24 03:57:26 +08:00
|
|
|
REG_RMW(ah, AR_STA_ID1, set, mask);
|
2008-10-29 12:46:30 +08:00
|
|
|
}
|
|
|
|
|
2010-04-16 05:38:14 +08:00
|
|
|
void ath9k_hw_get_delta_slope_vals(struct ath_hw *ah, u32 coef_scaled,
|
|
|
|
u32 *coef_mantissa, u32 *coef_exponent)
|
2008-10-29 12:46:30 +08:00
|
|
|
{
|
|
|
|
u32 coef_exp, coef_man;
|
|
|
|
|
|
|
|
for (coef_exp = 31; coef_exp > 0; coef_exp--)
|
|
|
|
if ((coef_scaled >> coef_exp) & 0x1)
|
|
|
|
break;
|
|
|
|
|
|
|
|
coef_exp = 14 - (coef_exp - COEF_SCALE_S);
|
|
|
|
|
|
|
|
coef_man = coef_scaled + (1 << (COEF_SCALE_S - coef_exp - 1));
|
|
|
|
|
|
|
|
*coef_mantissa = coef_man >> (COEF_SCALE_S - coef_exp);
|
|
|
|
*coef_exponent = coef_exp - 16;
|
|
|
|
}
|
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
static bool ath9k_hw_set_reset(struct ath_hw *ah, int type)
|
2008-10-29 12:46:30 +08:00
|
|
|
{
|
|
|
|
u32 rst_flags;
|
|
|
|
u32 tmpReg;
|
|
|
|
|
2009-02-16 15:53:12 +08:00
|
|
|
if (AR_SREV_9100(ah)) {
|
2011-03-24 03:57:26 +08:00
|
|
|
REG_RMW_FIELD(ah, AR_RTC_DERIVED_CLK,
|
|
|
|
AR_RTC_DERIVED_CLK_PERIOD, 1);
|
2009-02-16 15:53:12 +08:00
|
|
|
(void)REG_READ(ah, AR_RTC_DERIVED_CLK);
|
|
|
|
}
|
|
|
|
|
2010-04-16 14:23:57 +08:00
|
|
|
ENABLE_REGWRITE_BUFFER(ah);
|
|
|
|
|
2010-06-22 06:38:47 +08:00
|
|
|
if (AR_SREV_9300_20_OR_LATER(ah)) {
|
|
|
|
REG_WRITE(ah, AR_WA, ah->WARegVal);
|
|
|
|
udelay(10);
|
|
|
|
}
|
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN |
|
|
|
|
AR_RTC_FORCE_WAKE_ON_INT);
|
|
|
|
|
|
|
|
if (AR_SREV_9100(ah)) {
|
|
|
|
rst_flags = AR_RTC_RC_MAC_WARM | AR_RTC_RC_MAC_COLD |
|
|
|
|
AR_RTC_RC_COLD_RESET | AR_RTC_RC_WARM_RESET;
|
|
|
|
} else {
|
|
|
|
tmpReg = REG_READ(ah, AR_INTR_SYNC_CAUSE);
|
|
|
|
if (tmpReg &
|
|
|
|
(AR_INTR_SYNC_LOCAL_TIMEOUT |
|
|
|
|
AR_INTR_SYNC_RADM_CPL_TIMEOUT)) {
|
2010-04-16 05:38:12 +08:00
|
|
|
u32 val;
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_WRITE(ah, AR_INTR_SYNC_ENABLE, 0);
|
2010-04-16 05:38:12 +08:00
|
|
|
|
|
|
|
val = AR_RC_HOSTIF;
|
|
|
|
if (!AR_SREV_9300_20_OR_LATER(ah))
|
|
|
|
val |= AR_RC_AHB;
|
|
|
|
REG_WRITE(ah, AR_RC, val);
|
|
|
|
|
|
|
|
} else if (!AR_SREV_9300_20_OR_LATER(ah))
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_WRITE(ah, AR_RC, AR_RC_AHB);
|
|
|
|
|
|
|
|
rst_flags = AR_RTC_RC_MAC_WARM;
|
|
|
|
if (type == ATH9K_RESET_COLD)
|
|
|
|
rst_flags |= AR_RTC_RC_MAC_COLD;
|
|
|
|
}
|
|
|
|
|
2011-06-21 17:23:51 +08:00
|
|
|
if (AR_SREV_9330(ah)) {
|
|
|
|
int npend = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* AR9330 WAR:
|
|
|
|
* call external reset function to reset WMAC if:
|
|
|
|
* - doing a cold reset
|
|
|
|
* - we have pending frames in the TX queues
|
|
|
|
*/
|
|
|
|
|
|
|
|
for (i = 0; i < AR_NUM_QCU; i++) {
|
|
|
|
npend = ath9k_hw_numtxpending(ah, i);
|
|
|
|
if (npend)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ah->external_reset &&
|
|
|
|
(npend || type == ATH9K_RESET_COLD)) {
|
|
|
|
int reset_err = 0;
|
|
|
|
|
|
|
|
ath_dbg(ath9k_hw_common(ah), ATH_DBG_RESET,
|
|
|
|
"reset MAC via external reset\n");
|
|
|
|
|
|
|
|
reset_err = ah->external_reset();
|
|
|
|
if (reset_err) {
|
|
|
|
ath_err(ath9k_hw_common(ah),
|
|
|
|
"External reset failed, err=%d\n",
|
|
|
|
reset_err);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
REG_WRITE(ah, AR_RTC_RESET, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-15 03:17:09 +08:00
|
|
|
REG_WRITE(ah, AR_RTC_RC, rst_flags);
|
2010-04-16 14:23:57 +08:00
|
|
|
|
|
|
|
REGWRITE_BUFFER_FLUSH(ah);
|
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
udelay(50);
|
|
|
|
|
2009-01-15 03:17:09 +08:00
|
|
|
REG_WRITE(ah, AR_RTC_RC, 0);
|
2009-02-16 15:53:20 +08:00
|
|
|
if (!ath9k_hw_wait(ah, AR_RTC_RC, AR_RTC_RC_M, 0, AH_WAIT_TIMEOUT)) {
|
2010-12-03 11:12:37 +08:00
|
|
|
ath_dbg(ath9k_hw_common(ah), ATH_DBG_RESET,
|
|
|
|
"RTC stuck in MAC reset\n");
|
2008-10-29 12:46:30 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!AR_SREV_9100(ah))
|
|
|
|
REG_WRITE(ah, AR_RC, 0);
|
|
|
|
|
|
|
|
if (AR_SREV_9100(ah))
|
|
|
|
udelay(50);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
static bool ath9k_hw_set_reset_power_on(struct ath_hw *ah)
|
2008-10-29 12:46:30 +08:00
|
|
|
{
|
2010-04-16 14:23:57 +08:00
|
|
|
ENABLE_REGWRITE_BUFFER(ah);
|
|
|
|
|
2010-06-22 06:38:47 +08:00
|
|
|
if (AR_SREV_9300_20_OR_LATER(ah)) {
|
|
|
|
REG_WRITE(ah, AR_WA, ah->WARegVal);
|
|
|
|
udelay(10);
|
|
|
|
}
|
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN |
|
|
|
|
AR_RTC_FORCE_WAKE_ON_INT);
|
|
|
|
|
2010-04-16 05:38:12 +08:00
|
|
|
if (!AR_SREV_9100(ah) && !AR_SREV_9300_20_OR_LATER(ah))
|
2009-08-31 20:18:36 +08:00
|
|
|
REG_WRITE(ah, AR_RC, AR_RC_AHB);
|
|
|
|
|
2009-01-15 03:17:09 +08:00
|
|
|
REG_WRITE(ah, AR_RTC_RESET, 0);
|
2009-08-31 20:18:36 +08:00
|
|
|
|
2010-04-16 14:23:57 +08:00
|
|
|
REGWRITE_BUFFER_FLUSH(ah);
|
|
|
|
|
2010-04-16 05:38:30 +08:00
|
|
|
if (!AR_SREV_9300_20_OR_LATER(ah))
|
|
|
|
udelay(2);
|
|
|
|
|
|
|
|
if (!AR_SREV_9100(ah) && !AR_SREV_9300_20_OR_LATER(ah))
|
2009-08-31 20:18:36 +08:00
|
|
|
REG_WRITE(ah, AR_RC, 0);
|
|
|
|
|
2009-01-15 03:17:09 +08:00
|
|
|
REG_WRITE(ah, AR_RTC_RESET, 1);
|
2008-10-29 12:46:30 +08:00
|
|
|
|
|
|
|
if (!ath9k_hw_wait(ah,
|
|
|
|
AR_RTC_STATUS,
|
|
|
|
AR_RTC_STATUS_M,
|
2009-02-16 15:53:20 +08:00
|
|
|
AR_RTC_STATUS_ON,
|
|
|
|
AH_WAIT_TIMEOUT)) {
|
2010-12-03 11:12:37 +08:00
|
|
|
ath_dbg(ath9k_hw_common(ah), ATH_DBG_RESET,
|
|
|
|
"RTC not waking up\n");
|
2008-10-29 12:46:30 +08:00
|
|
|
return false;
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
return ath9k_hw_set_reset(ah, ATH9K_RESET_WARM);
|
|
|
|
}
|
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
static bool ath9k_hw_set_reset_reg(struct ath_hw *ah, u32 type)
|
2008-10-29 12:46:30 +08:00
|
|
|
{
|
2011-11-30 13:11:25 +08:00
|
|
|
bool ret = false;
|
2011-09-14 01:08:18 +08:00
|
|
|
|
2010-06-22 06:38:47 +08:00
|
|
|
if (AR_SREV_9300_20_OR_LATER(ah)) {
|
|
|
|
REG_WRITE(ah, AR_WA, ah->WARegVal);
|
|
|
|
udelay(10);
|
|
|
|
}
|
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_WRITE(ah, AR_RTC_FORCE_WAKE,
|
|
|
|
AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT);
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case ATH9K_RESET_POWER_ON:
|
2011-11-30 13:11:25 +08:00
|
|
|
ret = ath9k_hw_set_reset_power_on(ah);
|
|
|
|
break;
|
2008-10-29 12:46:30 +08:00
|
|
|
case ATH9K_RESET_WARM:
|
|
|
|
case ATH9K_RESET_COLD:
|
2011-11-30 13:11:25 +08:00
|
|
|
ret = ath9k_hw_set_reset(ah, type);
|
|
|
|
break;
|
2008-10-29 12:46:30 +08:00
|
|
|
default:
|
2011-11-30 13:11:25 +08:00
|
|
|
break;
|
2008-10-29 12:46:30 +08:00
|
|
|
}
|
2011-11-30 13:11:25 +08:00
|
|
|
|
|
|
|
if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI)
|
|
|
|
REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
|
|
|
|
|
|
|
|
return ret;
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
static bool ath9k_hw_chip_reset(struct ath_hw *ah,
|
2008-10-29 12:46:30 +08:00
|
|
|
struct ath9k_channel *chan)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2009-09-17 11:57:59 +08:00
|
|
|
if (AR_SREV_9280(ah) && ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL)) {
|
2009-02-12 16:27:03 +08:00
|
|
|
if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON))
|
|
|
|
return false;
|
|
|
|
} else if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_WARM))
|
2008-10-29 12:46:30 +08:00
|
|
|
return false;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-09-10 12:10:09 +08:00
|
|
|
if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE))
|
2008-10-29 12:46:30 +08:00
|
|
|
return false;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-02-09 15:57:26 +08:00
|
|
|
ah->chip_fullsleep = false;
|
2008-10-29 12:46:30 +08:00
|
|
|
ath9k_hw_init_pll(ah, chan);
|
|
|
|
ath9k_hw_set_rfmode(ah, chan);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
return true;
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
static bool ath9k_hw_channel_change(struct ath_hw *ah,
|
2009-09-14 14:04:44 +08:00
|
|
|
struct ath9k_channel *chan)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2009-09-13 17:42:02 +08:00
|
|
|
struct ath_common *common = ath9k_hw_common(ah);
|
2010-04-16 05:38:14 +08:00
|
|
|
u32 qnum;
|
2009-10-19 14:33:40 +08:00
|
|
|
int r;
|
2011-10-13 13:30:35 +08:00
|
|
|
bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA);
|
|
|
|
bool band_switch, mode_diff;
|
|
|
|
u8 ini_reloaded;
|
|
|
|
|
|
|
|
band_switch = (chan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ)) !=
|
|
|
|
(ah->curchan->channelFlags & (CHANNEL_2GHZ |
|
|
|
|
CHANNEL_5GHZ));
|
|
|
|
mode_diff = (chan->chanmode != ah->curchan->chanmode);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
|
|
|
for (qnum = 0; qnum < AR_NUM_QCU; qnum++) {
|
|
|
|
if (ath9k_hw_numtxpending(ah, qnum)) {
|
2010-12-03 11:12:37 +08:00
|
|
|
ath_dbg(common, ATH_DBG_QUEUE,
|
|
|
|
"Transmit frames pending on queue %d\n", qnum);
|
2008-08-04 15:16:41 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-16 05:38:14 +08:00
|
|
|
if (!ath9k_hw_rfbus_req(ah)) {
|
2010-12-03 11:12:36 +08:00
|
|
|
ath_err(common, "Could not kill baseband RX\n");
|
2008-08-04 15:16:41 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-10-13 13:30:35 +08:00
|
|
|
if (edma && (band_switch || mode_diff)) {
|
|
|
|
ath9k_hw_mark_phy_inactive(ah);
|
|
|
|
udelay(5);
|
|
|
|
|
|
|
|
ath9k_hw_init_pll(ah, NULL);
|
|
|
|
|
|
|
|
if (ath9k_hw_fast_chan_change(ah, chan, &ini_reloaded)) {
|
|
|
|
ath_err(common, "Failed to do fast channel change\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-16 05:38:14 +08:00
|
|
|
ath9k_hw_set_channel_regs(ah, chan);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-04-16 05:38:14 +08:00
|
|
|
r = ath9k_hw_rf_set_freq(ah, chan);
|
2009-10-19 14:33:40 +08:00
|
|
|
if (r) {
|
2010-12-03 11:12:36 +08:00
|
|
|
ath_err(common, "Failed to set channel\n");
|
2009-10-19 14:33:40 +08:00
|
|
|
return false;
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
2010-10-09 04:13:51 +08:00
|
|
|
ath9k_hw_set_clockrate(ah);
|
2011-10-09 02:06:20 +08:00
|
|
|
ath9k_hw_apply_txpower(ah, chan);
|
2010-04-16 05:38:14 +08:00
|
|
|
ath9k_hw_rfbus_done(ah);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan))
|
|
|
|
ath9k_hw_set_delta_slope(ah, chan);
|
|
|
|
|
2010-04-16 05:38:14 +08:00
|
|
|
ath9k_hw_spur_mitigate_freq(ah, chan);
|
2008-10-29 12:46:30 +08:00
|
|
|
|
2011-10-13 13:30:35 +08:00
|
|
|
if (edma && (band_switch || mode_diff)) {
|
2011-10-13 13:30:42 +08:00
|
|
|
ah->ah_flags |= AH_FASTCC;
|
2011-10-13 13:30:35 +08:00
|
|
|
if (band_switch || ini_reloaded)
|
|
|
|
ah->eep_ops->set_board_values(ah, chan);
|
|
|
|
|
|
|
|
ath9k_hw_init_bb(ah, chan);
|
|
|
|
|
|
|
|
if (band_switch || ini_reloaded)
|
|
|
|
ath9k_hw_init_cal(ah, chan);
|
2011-10-13 13:30:42 +08:00
|
|
|
ah->ah_flags &= ~AH_FASTCC;
|
2011-10-13 13:30:35 +08:00
|
|
|
}
|
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-03-19 20:55:38 +08:00
|
|
|
static void ath9k_hw_apply_gpio_override(struct ath_hw *ah)
|
|
|
|
{
|
|
|
|
u32 gpio_mask = ah->gpio_mask;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; gpio_mask; i++, gpio_mask >>= 1) {
|
|
|
|
if (!(gpio_mask & 1))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
ath9k_hw_cfg_output(ah, i, AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
|
|
|
|
ath9k_hw_set_gpio(ah, i, !!(ah->gpio_val & BIT(i)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-20 01:57:29 +08:00
|
|
|
bool ath9k_hw_check_alive(struct ath_hw *ah)
|
2009-06-13 17:20:26 +08:00
|
|
|
{
|
2010-04-20 01:57:29 +08:00
|
|
|
int count = 50;
|
|
|
|
u32 reg;
|
|
|
|
|
2010-09-22 18:34:53 +08:00
|
|
|
if (AR_SREV_9285_12_OR_LATER(ah))
|
2010-04-20 01:57:29 +08:00
|
|
|
return true;
|
|
|
|
|
|
|
|
do {
|
|
|
|
reg = REG_READ(ah, AR_OBS_BUS_1);
|
2009-06-13 17:20:26 +08:00
|
|
|
|
2010-04-20 01:57:29 +08:00
|
|
|
if ((reg & 0x7E7FFFEF) == 0x00702400)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
switch (reg & 0x7E000B00) {
|
|
|
|
case 0x1E000000:
|
|
|
|
case 0x52000B00:
|
|
|
|
case 0x18000B00:
|
|
|
|
continue;
|
|
|
|
default:
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} while (count-- > 0);
|
2009-06-13 17:20:26 +08:00
|
|
|
|
2010-04-20 01:57:29 +08:00
|
|
|
return false;
|
2009-06-13 17:20:26 +08:00
|
|
|
}
|
2010-04-20 01:57:29 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_check_alive);
|
2009-06-13 17:20:26 +08:00
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
|
2010-07-31 06:12:00 +08:00
|
|
|
struct ath9k_hw_cal_data *caldata, bool bChannelChange)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2009-09-11 00:22:37 +08:00
|
|
|
struct ath_common *common = ath9k_hw_common(ah);
|
2008-08-04 15:16:41 +08:00
|
|
|
u32 saveLedState;
|
2009-02-09 15:57:26 +08:00
|
|
|
struct ath9k_channel *curchan = ah->curchan;
|
2008-08-04 15:16:41 +08:00
|
|
|
u32 saveDefAntenna;
|
|
|
|
u32 macStaId1;
|
2009-09-17 11:55:25 +08:00
|
|
|
u64 tsf = 0;
|
2010-04-16 05:38:14 +08:00
|
|
|
int i, r;
|
2011-10-13 13:30:42 +08:00
|
|
|
bool allow_fbs = false;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-09-10 12:10:09 +08:00
|
|
|
if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE))
|
2008-12-24 07:58:40 +08:00
|
|
|
return -EIO;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-09-29 23:15:27 +08:00
|
|
|
if (curchan && !ah->chip_fullsleep)
|
2008-08-04 15:16:41 +08:00
|
|
|
ath9k_hw_getnf(ah, curchan);
|
|
|
|
|
2010-07-31 06:12:00 +08:00
|
|
|
ah->caldata = caldata;
|
|
|
|
if (caldata &&
|
|
|
|
(chan->channel != caldata->channel ||
|
|
|
|
(chan->channelFlags & ~CHANNEL_CW_INT) !=
|
|
|
|
(caldata->channelFlags & ~CHANNEL_CW_INT))) {
|
|
|
|
/* Operating channel changed, reset channel calibration data */
|
|
|
|
memset(caldata, 0, sizeof(*caldata));
|
|
|
|
ath9k_init_nfcal_hist_buffer(ah, chan);
|
|
|
|
}
|
2011-07-28 20:08:56 +08:00
|
|
|
ah->noise = ath9k_hw_getchan_noise(ah, chan);
|
2010-07-31 06:12:00 +08:00
|
|
|
|
2011-10-13 13:30:39 +08:00
|
|
|
if (AR_SREV_9280(ah) && common->bus_ops->ath_bus_type == ATH_PCI)
|
2011-09-14 16:50:30 +08:00
|
|
|
bChannelChange = false;
|
|
|
|
|
2011-10-13 13:30:42 +08:00
|
|
|
if (caldata &&
|
|
|
|
caldata->done_txiqcal_once &&
|
|
|
|
caldata->done_txclcal_once &&
|
|
|
|
caldata->rtt_hist.num_readings)
|
|
|
|
allow_fbs = true;
|
|
|
|
|
2008-08-04 15:16:41 +08:00
|
|
|
if (bChannelChange &&
|
2009-02-09 15:57:26 +08:00
|
|
|
(ah->chip_fullsleep != true) &&
|
|
|
|
(ah->curchan != NULL) &&
|
|
|
|
(chan->channel != ah->curchan->channel) &&
|
2011-10-13 13:30:42 +08:00
|
|
|
(allow_fbs ||
|
|
|
|
((chan->channelFlags & CHANNEL_ALL) ==
|
|
|
|
(ah->curchan->channelFlags & CHANNEL_ALL)))) {
|
2009-09-14 14:04:44 +08:00
|
|
|
if (ath9k_hw_channel_change(ah, chan)) {
|
2009-02-09 15:57:26 +08:00
|
|
|
ath9k_hw_loadnf(ah, ah->curchan);
|
2010-07-31 03:02:09 +08:00
|
|
|
ath9k_hw_start_nfcal(ah, true);
|
2010-09-03 18:30:00 +08:00
|
|
|
if (AR_SREV_9271(ah))
|
|
|
|
ar9002_hw_load_ani_reg(ah, chan);
|
2008-12-24 07:58:40 +08:00
|
|
|
return 0;
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
saveDefAntenna = REG_READ(ah, AR_DEF_ANTENNA);
|
|
|
|
if (saveDefAntenna == 0)
|
|
|
|
saveDefAntenna = 1;
|
|
|
|
|
|
|
|
macStaId1 = REG_READ(ah, AR_STA_ID1) & AR_STA_ID1_BASE_RATE_11B;
|
|
|
|
|
2009-09-17 11:55:25 +08:00
|
|
|
/* For chips on which RTC reset is done, save TSF before it gets cleared */
|
2010-06-30 08:07:48 +08:00
|
|
|
if (AR_SREV_9100(ah) ||
|
|
|
|
(AR_SREV_9280(ah) && ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL)))
|
2009-09-17 11:55:25 +08:00
|
|
|
tsf = ath9k_hw_gettsf64(ah);
|
|
|
|
|
2008-08-04 15:16:41 +08:00
|
|
|
saveLedState = REG_READ(ah, AR_CFG_LED) &
|
|
|
|
(AR_CFG_LED_ASSOC_CTL | AR_CFG_LED_MODE_SEL |
|
|
|
|
AR_CFG_LED_BLINK_THRESH_SEL | AR_CFG_LED_BLINK_SLOW);
|
|
|
|
|
|
|
|
ath9k_hw_mark_phy_inactive(ah);
|
|
|
|
|
2010-12-15 23:30:53 +08:00
|
|
|
ah->paprd_table_write_done = false;
|
|
|
|
|
2010-03-17 16:55:23 +08:00
|
|
|
/* Only required on the first reset */
|
2009-08-04 11:14:12 +08:00
|
|
|
if (AR_SREV_9271(ah) && ah->htc_reset_init) {
|
|
|
|
REG_WRITE(ah,
|
|
|
|
AR9271_RESET_POWER_DOWN_CONTROL,
|
|
|
|
AR9271_RADIO_RF_RST);
|
|
|
|
udelay(50);
|
|
|
|
}
|
|
|
|
|
2008-08-04 15:16:41 +08:00
|
|
|
if (!ath9k_hw_chip_reset(ah, chan)) {
|
2010-12-03 11:12:36 +08:00
|
|
|
ath_err(common, "Chip reset failed\n");
|
2008-12-24 07:58:40 +08:00
|
|
|
return -EINVAL;
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
|
2010-03-17 16:55:23 +08:00
|
|
|
/* Only required on the first reset */
|
2009-08-04 11:14:12 +08:00
|
|
|
if (AR_SREV_9271(ah) && ah->htc_reset_init) {
|
|
|
|
ah->htc_reset_init = false;
|
|
|
|
REG_WRITE(ah,
|
|
|
|
AR9271_RESET_POWER_DOWN_CONTROL,
|
|
|
|
AR9271_GATE_MAC_CTL);
|
|
|
|
udelay(50);
|
|
|
|
}
|
|
|
|
|
2009-09-17 11:55:25 +08:00
|
|
|
/* Restore TSF */
|
2010-06-30 08:07:48 +08:00
|
|
|
if (tsf)
|
2009-09-17 11:55:25 +08:00
|
|
|
ath9k_hw_settsf64(ah, tsf);
|
|
|
|
|
2010-09-22 18:34:52 +08:00
|
|
|
if (AR_SREV_9280_20_OR_LATER(ah))
|
2009-01-21 21:54:13 +08:00
|
|
|
REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_JTAG_DISABLE);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-06-01 17:44:10 +08:00
|
|
|
if (!AR_SREV_9300_20_OR_LATER(ah))
|
|
|
|
ar9002_hw_enable_async_fifo(ah);
|
|
|
|
|
2009-09-14 14:04:44 +08:00
|
|
|
r = ath9k_hw_process_ini(ah, chan);
|
2008-12-24 07:58:40 +08:00
|
|
|
if (r)
|
|
|
|
return r;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-06-30 08:07:48 +08:00
|
|
|
/*
|
|
|
|
* Some AR91xx SoC devices frequently fail to accept TSF writes
|
|
|
|
* right after the chip reset. When that happens, write a new
|
|
|
|
* value after the initvals have been applied, with an offset
|
|
|
|
* based on measured time difference
|
|
|
|
*/
|
|
|
|
if (AR_SREV_9100(ah) && (ath9k_hw_gettsf64(ah) < tsf)) {
|
|
|
|
tsf += 1500;
|
|
|
|
ath9k_hw_settsf64(ah, tsf);
|
|
|
|
}
|
|
|
|
|
2009-01-08 19:32:13 +08:00
|
|
|
/* Setup MFP options for CCMP */
|
|
|
|
if (AR_SREV_9280_20_OR_LATER(ah)) {
|
|
|
|
/* Mask Retry(b11), PwrMgt(b12), MoreData(b13) to 0 in mgmt
|
|
|
|
* frames when constructing CCMP AAD. */
|
|
|
|
REG_RMW_FIELD(ah, AR_AES_MUTE_MASK1, AR_AES_MUTE_MASK1_FC_MGMT,
|
|
|
|
0xc7ff);
|
|
|
|
ah->sw_mgmt_crypto = false;
|
|
|
|
} else if (AR_SREV_9160_10_OR_LATER(ah)) {
|
|
|
|
/* Disable hardware crypto for management frames */
|
|
|
|
REG_CLR_BIT(ah, AR_PCU_MISC_MODE2,
|
|
|
|
AR_PCU_MISC_MODE2_MGMT_CRYPTO_ENABLE);
|
|
|
|
REG_SET_BIT(ah, AR_PCU_MISC_MODE2,
|
|
|
|
AR_PCU_MISC_MODE2_NO_CRYPTO_FOR_NON_DATA_PKT);
|
|
|
|
ah->sw_mgmt_crypto = true;
|
|
|
|
} else
|
|
|
|
ah->sw_mgmt_crypto = true;
|
|
|
|
|
2008-08-04 15:16:41 +08:00
|
|
|
if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan))
|
|
|
|
ath9k_hw_set_delta_slope(ah, chan);
|
|
|
|
|
2010-04-16 05:38:14 +08:00
|
|
|
ath9k_hw_spur_mitigate_freq(ah, chan);
|
2009-03-13 11:26:05 +08:00
|
|
|
ah->eep_ops->set_board_values(ah, chan);
|
2009-10-19 14:33:45 +08:00
|
|
|
|
2010-04-16 14:23:57 +08:00
|
|
|
ENABLE_REGWRITE_BUFFER(ah);
|
|
|
|
|
2009-09-11 00:22:37 +08:00
|
|
|
REG_WRITE(ah, AR_STA_ID0, get_unaligned_le32(common->macaddr));
|
|
|
|
REG_WRITE(ah, AR_STA_ID1, get_unaligned_le16(common->macaddr + 4)
|
2008-08-04 15:16:41 +08:00
|
|
|
| macStaId1
|
|
|
|
| AR_STA_ID1_RTS_USE_DEF
|
2009-02-09 15:57:26 +08:00
|
|
|
| (ah->config.
|
2008-08-07 13:22:38 +08:00
|
|
|
ack_6mb ? AR_STA_ID1_ACKCTS_6MB : 0)
|
2009-02-09 15:57:26 +08:00
|
|
|
| ah->sta_id1_defaults);
|
2009-09-11 08:52:45 +08:00
|
|
|
ath_hw_setbssidmask(common);
|
2008-08-04 15:16:41 +08:00
|
|
|
REG_WRITE(ah, AR_DEF_ANTENNA, saveDefAntenna);
|
2009-09-10 23:57:00 +08:00
|
|
|
ath9k_hw_write_associd(ah);
|
2008-08-04 15:16:41 +08:00
|
|
|
REG_WRITE(ah, AR_ISR, ~0);
|
|
|
|
REG_WRITE(ah, AR_RSSI_THR, INIT_RSSI_THR);
|
|
|
|
|
2010-04-16 14:23:57 +08:00
|
|
|
REGWRITE_BUFFER_FLUSH(ah);
|
|
|
|
|
2011-01-27 00:29:05 +08:00
|
|
|
ath9k_hw_set_operating_mode(ah, ah->opmode);
|
|
|
|
|
2010-04-16 05:38:14 +08:00
|
|
|
r = ath9k_hw_rf_set_freq(ah, chan);
|
2009-10-19 14:33:40 +08:00
|
|
|
if (r)
|
|
|
|
return r;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-10-09 04:13:51 +08:00
|
|
|
ath9k_hw_set_clockrate(ah);
|
|
|
|
|
2010-04-16 14:23:57 +08:00
|
|
|
ENABLE_REGWRITE_BUFFER(ah);
|
|
|
|
|
2008-08-04 15:16:41 +08:00
|
|
|
for (i = 0; i < AR_NUM_DCU; i++)
|
|
|
|
REG_WRITE(ah, AR_DQCUMASK(i), 1 << i);
|
|
|
|
|
2010-04-16 14:23:57 +08:00
|
|
|
REGWRITE_BUFFER_FLUSH(ah);
|
|
|
|
|
2009-02-09 15:57:26 +08:00
|
|
|
ah->intr_txqs = 0;
|
2011-03-24 03:57:28 +08:00
|
|
|
for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
|
2008-08-04 15:16:41 +08:00
|
|
|
ath9k_hw_resettxqueue(ah, i);
|
|
|
|
|
2009-02-09 15:57:26 +08:00
|
|
|
ath9k_hw_init_interrupt_masks(ah, ah->opmode);
|
2010-06-12 12:33:45 +08:00
|
|
|
ath9k_hw_ani_cache_ini_regs(ah);
|
2008-08-04 15:16:41 +08:00
|
|
|
ath9k_hw_init_qos(ah);
|
|
|
|
|
2009-02-09 15:57:26 +08:00
|
|
|
if (ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
|
2010-12-17 07:57:01 +08:00
|
|
|
ath9k_hw_cfg_gpio_input(ah, ah->rfkill_gpio);
|
2009-06-13 17:20:26 +08:00
|
|
|
|
2010-01-15 09:33:40 +08:00
|
|
|
ath9k_hw_init_global_settings(ah);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2011-07-09 12:12:51 +08:00
|
|
|
if (AR_SREV_9287(ah) && AR_SREV_9287_13_OR_LATER(ah)) {
|
|
|
|
REG_SET_BIT(ah, AR_MAC_PCU_LOGIC_ANALYZER,
|
|
|
|
AR_MAC_PCU_LOGIC_ANALYZER_DISBUG20768);
|
|
|
|
REG_RMW_FIELD(ah, AR_AHB_MODE, AR_AHB_CUSTOM_BURST_EN,
|
|
|
|
AR_AHB_CUSTOM_BURST_ASYNC_FIFO_VAL);
|
|
|
|
REG_SET_BIT(ah, AR_PCU_MISC_MODE2,
|
|
|
|
AR_PCU_MISC_MODE2_ENABLE_AGGWEP);
|
2009-07-23 13:29:57 +08:00
|
|
|
}
|
|
|
|
|
2011-03-24 03:57:26 +08:00
|
|
|
REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PRESERVE_SEQNUM);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
|
|
|
ath9k_hw_set_dma(ah);
|
|
|
|
|
|
|
|
REG_WRITE(ah, AR_OBS, 8);
|
|
|
|
|
2009-12-14 17:27:00 +08:00
|
|
|
if (ah->config.rx_intr_mitigation) {
|
2008-08-04 15:16:41 +08:00
|
|
|
REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, 500);
|
|
|
|
REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_FIRST, 2000);
|
|
|
|
}
|
|
|
|
|
2010-04-16 05:39:19 +08:00
|
|
|
if (ah->config.tx_intr_mitigation) {
|
|
|
|
REG_RMW_FIELD(ah, AR_TIMT, AR_TIMT_LAST, 300);
|
|
|
|
REG_RMW_FIELD(ah, AR_TIMT, AR_TIMT_FIRST, 750);
|
|
|
|
}
|
|
|
|
|
2008-08-04 15:16:41 +08:00
|
|
|
ath9k_hw_init_bb(ah, chan);
|
|
|
|
|
2011-10-13 13:30:37 +08:00
|
|
|
if (caldata) {
|
2011-10-13 13:30:35 +08:00
|
|
|
caldata->done_txiqcal_once = false;
|
2011-10-13 13:30:37 +08:00
|
|
|
caldata->done_txclcal_once = false;
|
2011-10-13 13:30:41 +08:00
|
|
|
caldata->rtt_hist.num_readings = 0;
|
2011-10-13 13:30:37 +08:00
|
|
|
}
|
2008-12-24 07:58:40 +08:00
|
|
|
if (!ath9k_hw_init_cal(ah, chan))
|
2009-06-29 00:26:32 +08:00
|
|
|
return -EIO;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2011-10-25 19:17:36 +08:00
|
|
|
ath9k_hw_loadnf(ah, chan);
|
|
|
|
ath9k_hw_start_nfcal(ah, true);
|
|
|
|
|
2010-04-16 14:23:57 +08:00
|
|
|
ENABLE_REGWRITE_BUFFER(ah);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-04-16 05:38:14 +08:00
|
|
|
ath9k_hw_restore_chainmask(ah);
|
2008-08-04 15:16:41 +08:00
|
|
|
REG_WRITE(ah, AR_CFG_LED, saveLedState | AR_CFG_SCLK_32KHZ);
|
|
|
|
|
2010-04-16 14:23:57 +08:00
|
|
|
REGWRITE_BUFFER_FLUSH(ah);
|
|
|
|
|
2009-08-04 11:14:12 +08:00
|
|
|
/*
|
|
|
|
* For big endian systems turn on swapping for descriptors
|
|
|
|
*/
|
2008-08-04 15:16:41 +08:00
|
|
|
if (AR_SREV_9100(ah)) {
|
|
|
|
u32 mask;
|
|
|
|
mask = REG_READ(ah, AR_CFG);
|
|
|
|
if (mask & (AR_CFG_SWRB | AR_CFG_SWTB | AR_CFG_SWRG)) {
|
2010-12-03 11:12:37 +08:00
|
|
|
ath_dbg(common, ATH_DBG_RESET,
|
2008-11-29 00:48:05 +08:00
|
|
|
"CFG Byte Swap Set 0x%x\n", mask);
|
2008-08-04 15:16:41 +08:00
|
|
|
} else {
|
|
|
|
mask =
|
|
|
|
INIT_CONFIG_STATUS | AR_CFG_SWRB | AR_CFG_SWTB;
|
|
|
|
REG_WRITE(ah, AR_CFG, mask);
|
2010-12-03 11:12:37 +08:00
|
|
|
ath_dbg(common, ATH_DBG_RESET,
|
2008-11-29 00:48:05 +08:00
|
|
|
"Setting CFG 0x%x\n", REG_READ(ah, AR_CFG));
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
} else {
|
2010-06-02 18:23:31 +08:00
|
|
|
if (common->bus_ops->ath_bus_type == ATH_USB) {
|
|
|
|
/* Configure AR9271 target WLAN */
|
|
|
|
if (AR_SREV_9271(ah))
|
|
|
|
REG_WRITE(ah, AR_CFG, AR_CFG_SWRB | AR_CFG_SWTB);
|
|
|
|
else
|
|
|
|
REG_WRITE(ah, AR_CFG, AR_CFG_SWTD | AR_CFG_SWRD);
|
|
|
|
}
|
2008-08-04 15:16:41 +08:00
|
|
|
#ifdef __BIG_ENDIAN
|
2011-06-21 17:23:35 +08:00
|
|
|
else if (AR_SREV_9330(ah) || AR_SREV_9340(ah))
|
2011-04-19 21:59:14 +08:00
|
|
|
REG_RMW(ah, AR_CFG, AR_CFG_SWRB | AR_CFG_SWTB, 0);
|
|
|
|
else
|
2009-08-04 11:14:12 +08:00
|
|
|
REG_WRITE(ah, AR_CFG, AR_CFG_SWTD | AR_CFG_SWRD);
|
2008-08-04 15:16:41 +08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2009-09-10 05:52:02 +08:00
|
|
|
if (ah->btcoex_hw.enabled)
|
2009-08-26 23:38:45 +08:00
|
|
|
ath9k_hw_btcoex_enable(ah);
|
|
|
|
|
2011-05-20 20:22:13 +08:00
|
|
|
if (AR_SREV_9300_20_OR_LATER(ah)) {
|
2010-05-14 01:33:43 +08:00
|
|
|
ar9003_hw_bb_watchdog_config(ah);
|
2010-04-16 05:39:25 +08:00
|
|
|
|
2011-05-20 20:22:13 +08:00
|
|
|
ar9003_hw_disable_phy_restart(ah);
|
|
|
|
}
|
|
|
|
|
2011-03-19 20:55:38 +08:00
|
|
|
ath9k_hw_apply_gpio_override(ah);
|
|
|
|
|
2008-12-24 07:58:40 +08:00
|
|
|
return 0;
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_reset);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
/******************************/
|
|
|
|
/* Power Management (Chipset) */
|
|
|
|
/******************************/
|
|
|
|
|
2010-04-16 05:38:12 +08:00
|
|
|
/*
|
|
|
|
* Notify Power Mgt is disabled in self-generated frames.
|
|
|
|
* If requested, force chip to sleep.
|
|
|
|
*/
|
2009-02-09 15:57:12 +08:00
|
|
|
static void ath9k_set_power_sleep(struct ath_hw *ah, int setChip)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
|
|
|
|
if (setChip) {
|
2011-10-13 13:30:44 +08:00
|
|
|
if (AR_SREV_9462(ah)) {
|
2011-09-14 01:08:18 +08:00
|
|
|
REG_WRITE(ah, AR_TIMER_MODE,
|
|
|
|
REG_READ(ah, AR_TIMER_MODE) & 0xFFFFFF00);
|
|
|
|
REG_WRITE(ah, AR_NDP2_TIMER_MODE, REG_READ(ah,
|
|
|
|
AR_NDP2_TIMER_MODE) & 0xFFFFFF00);
|
|
|
|
REG_WRITE(ah, AR_SLP32_INC,
|
|
|
|
REG_READ(ah, AR_SLP32_INC) & 0xFFF00000);
|
|
|
|
/* xxx Required for WLAN only case ? */
|
|
|
|
REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN, 0);
|
|
|
|
udelay(100);
|
|
|
|
}
|
|
|
|
|
2010-04-16 05:38:12 +08:00
|
|
|
/*
|
|
|
|
* Clear the RTC force wake bit to allow the
|
|
|
|
* mac to go to sleep.
|
|
|
|
*/
|
2011-09-14 01:08:18 +08:00
|
|
|
REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN);
|
|
|
|
|
2011-10-13 13:30:44 +08:00
|
|
|
if (AR_SREV_9462(ah))
|
2011-09-14 01:08:18 +08:00
|
|
|
udelay(100);
|
|
|
|
|
2010-04-16 05:38:12 +08:00
|
|
|
if (!AR_SREV_9100(ah) && !AR_SREV_9300_20_OR_LATER(ah))
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_WRITE(ah, AR_RC, AR_RC_AHB | AR_RC_HOSTIF);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-04-16 05:38:12 +08:00
|
|
|
/* Shutdown chip. Active low */
|
2011-09-14 01:08:18 +08:00
|
|
|
if (!AR_SREV_5416(ah) &&
|
2011-10-13 13:30:44 +08:00
|
|
|
!AR_SREV_9271(ah) && !AR_SREV_9462_10(ah)) {
|
2011-09-14 01:08:18 +08:00
|
|
|
REG_CLR_BIT(ah, AR_RTC_RESET, AR_RTC_RESET_EN);
|
|
|
|
udelay(2);
|
|
|
|
}
|
2008-10-29 12:46:30 +08:00
|
|
|
}
|
2010-06-22 06:38:47 +08:00
|
|
|
|
|
|
|
/* Clear Bit 14 of AR_WA after putting chip into Full Sleep mode. */
|
2011-10-13 13:19:13 +08:00
|
|
|
REG_WRITE(ah, AR_WA, ah->WARegVal & ~AR_WA_D3_L1_DISABLE);
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
|
2010-04-16 05:38:16 +08:00
|
|
|
/*
|
|
|
|
* Notify Power Management is enabled in self-generating
|
|
|
|
* frames. If request, set power mode of chip to
|
|
|
|
* auto/normal. Duration in units of 128us (1/8 TU).
|
|
|
|
*/
|
2009-02-09 15:57:12 +08:00
|
|
|
static void ath9k_set_power_network_sleep(struct ath_hw *ah, int setChip)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2011-09-14 01:08:18 +08:00
|
|
|
u32 val;
|
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
|
|
|
|
if (setChip) {
|
2009-02-09 15:57:26 +08:00
|
|
|
struct ath9k_hw_capabilities *pCap = &ah->caps;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) {
|
2010-04-16 05:38:16 +08:00
|
|
|
/* Set WakeOnInterrupt bit; clear ForceWake bit */
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_WRITE(ah, AR_RTC_FORCE_WAKE,
|
|
|
|
AR_RTC_FORCE_WAKE_ON_INT);
|
|
|
|
} else {
|
2011-09-14 01:08:18 +08:00
|
|
|
|
|
|
|
/* When chip goes into network sleep, it could be waken
|
|
|
|
* up by MCI_INT interrupt caused by BT's HW messages
|
|
|
|
* (LNA_xxx, CONT_xxx) which chould be in a very fast
|
|
|
|
* rate (~100us). This will cause chip to leave and
|
|
|
|
* re-enter network sleep mode frequently, which in
|
|
|
|
* consequence will have WLAN MCI HW to generate lots of
|
|
|
|
* SYS_WAKING and SYS_SLEEPING messages which will make
|
|
|
|
* BT CPU to busy to process.
|
|
|
|
*/
|
2011-10-13 13:30:44 +08:00
|
|
|
if (AR_SREV_9462(ah)) {
|
2011-09-14 01:08:18 +08:00
|
|
|
val = REG_READ(ah, AR_MCI_INTERRUPT_RX_MSG_EN) &
|
|
|
|
~AR_MCI_INTERRUPT_RX_HW_MSG_MASK;
|
|
|
|
REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN, val);
|
|
|
|
}
|
2010-04-16 05:38:16 +08:00
|
|
|
/*
|
|
|
|
* Clear the RTC force wake bit to allow the
|
|
|
|
* mac to go to sleep.
|
|
|
|
*/
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE,
|
|
|
|
AR_RTC_FORCE_WAKE_EN);
|
2011-09-14 01:08:18 +08:00
|
|
|
|
2011-10-13 13:30:44 +08:00
|
|
|
if (AR_SREV_9462(ah))
|
2011-09-14 01:08:18 +08:00
|
|
|
udelay(30);
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
}
|
2010-06-22 06:38:47 +08:00
|
|
|
|
|
|
|
/* Clear Bit 14 of AR_WA after putting chip into Net Sleep mode. */
|
|
|
|
if (AR_SREV_9300_20_OR_LATER(ah))
|
|
|
|
REG_WRITE(ah, AR_WA, ah->WARegVal & ~AR_WA_D3_L1_DISABLE);
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
static bool ath9k_hw_set_power_awake(struct ath_hw *ah, int setChip)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2008-10-29 12:46:30 +08:00
|
|
|
u32 val;
|
|
|
|
int i;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-06-22 06:38:47 +08:00
|
|
|
/* Set Bits 14 and 17 of AR_WA before powering on the chip. */
|
|
|
|
if (AR_SREV_9300_20_OR_LATER(ah)) {
|
|
|
|
REG_WRITE(ah, AR_WA, ah->WARegVal);
|
|
|
|
udelay(10);
|
|
|
|
}
|
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
if (setChip) {
|
|
|
|
if ((REG_READ(ah, AR_RTC_STATUS) &
|
|
|
|
AR_RTC_STATUS_M) == AR_RTC_STATUS_SHUTDOWN) {
|
|
|
|
if (ath9k_hw_set_reset_reg(ah,
|
|
|
|
ATH9K_RESET_POWER_ON) != true) {
|
|
|
|
return false;
|
|
|
|
}
|
2010-04-16 05:38:15 +08:00
|
|
|
if (!AR_SREV_9300_20_OR_LATER(ah))
|
|
|
|
ath9k_hw_init_pll(ah, NULL);
|
2008-10-29 12:46:30 +08:00
|
|
|
}
|
|
|
|
if (AR_SREV_9100(ah))
|
|
|
|
REG_SET_BIT(ah, AR_RTC_RESET,
|
|
|
|
AR_RTC_RESET_EN);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_SET_BIT(ah, AR_RTC_FORCE_WAKE,
|
|
|
|
AR_RTC_FORCE_WAKE_EN);
|
|
|
|
udelay(50);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
for (i = POWER_UP_TIME / 50; i > 0; i--) {
|
|
|
|
val = REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M;
|
|
|
|
if (val == AR_RTC_STATUS_ON)
|
|
|
|
break;
|
|
|
|
udelay(50);
|
|
|
|
REG_SET_BIT(ah, AR_RTC_FORCE_WAKE,
|
|
|
|
AR_RTC_FORCE_WAKE_EN);
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
2008-10-29 12:46:30 +08:00
|
|
|
if (i == 0) {
|
2010-12-03 11:12:36 +08:00
|
|
|
ath_err(ath9k_hw_common(ah),
|
|
|
|
"Failed to wakeup in %uus\n",
|
|
|
|
POWER_UP_TIME / 20);
|
2008-10-29 12:46:30 +08:00
|
|
|
return false;
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
return true;
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
|
2009-09-10 12:10:09 +08:00
|
|
|
bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2009-09-13 17:42:02 +08:00
|
|
|
struct ath_common *common = ath9k_hw_common(ah);
|
2011-11-30 13:11:24 +08:00
|
|
|
struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;
|
2009-02-09 15:57:12 +08:00
|
|
|
int status = true, setChip = true;
|
2008-10-29 12:46:30 +08:00
|
|
|
static const char *modes[] = {
|
|
|
|
"AWAKE",
|
|
|
|
"FULL-SLEEP",
|
|
|
|
"NETWORK SLEEP",
|
|
|
|
"UNDEFINED"
|
|
|
|
};
|
|
|
|
|
2009-07-24 23:27:22 +08:00
|
|
|
if (ah->power_mode == mode)
|
|
|
|
return status;
|
|
|
|
|
2010-12-03 11:12:37 +08:00
|
|
|
ath_dbg(common, ATH_DBG_RESET, "%s -> %s\n",
|
|
|
|
modes[ah->power_mode], modes[mode]);
|
2008-10-29 12:46:30 +08:00
|
|
|
|
|
|
|
switch (mode) {
|
|
|
|
case ATH9K_PM_AWAKE:
|
|
|
|
status = ath9k_hw_set_power_awake(ah, setChip);
|
2011-11-30 13:11:24 +08:00
|
|
|
|
|
|
|
if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI)
|
|
|
|
REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
|
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
break;
|
|
|
|
case ATH9K_PM_FULL_SLEEP:
|
2011-11-30 13:11:24 +08:00
|
|
|
|
|
|
|
if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI) {
|
|
|
|
if (ar9003_mci_state(ah, MCI_STATE_ENABLE, NULL) &&
|
|
|
|
(mci->bt_state != MCI_BT_SLEEP) &&
|
|
|
|
!mci->halted_bt_gpm) {
|
|
|
|
ath_dbg(common, ATH_DBG_MCI, "MCI halt BT GPM"
|
|
|
|
"(full_sleep)");
|
|
|
|
ar9003_mci_send_coex_halt_bt_gpm(ah,
|
|
|
|
true, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
mci->ready = false;
|
|
|
|
REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
|
|
|
|
}
|
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
ath9k_set_power_sleep(ah, setChip);
|
2009-02-09 15:57:26 +08:00
|
|
|
ah->chip_fullsleep = true;
|
2008-10-29 12:46:30 +08:00
|
|
|
break;
|
|
|
|
case ATH9K_PM_NETWORK_SLEEP:
|
2011-11-30 13:11:24 +08:00
|
|
|
|
|
|
|
if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI)
|
|
|
|
REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
|
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
ath9k_set_power_network_sleep(ah, setChip);
|
|
|
|
break;
|
2008-08-04 15:16:41 +08:00
|
|
|
default:
|
2010-12-03 11:12:36 +08:00
|
|
|
ath_err(common, "Unknown power mode %u\n", mode);
|
2008-08-04 15:16:41 +08:00
|
|
|
return false;
|
|
|
|
}
|
2009-02-09 15:57:26 +08:00
|
|
|
ah->power_mode = mode;
|
2008-10-29 12:46:30 +08:00
|
|
|
|
2010-12-08 07:13:23 +08:00
|
|
|
/*
|
|
|
|
* XXX: If this warning never comes up after a while then
|
|
|
|
* simply keep the ATH_DBG_WARN_ON_ONCE() but make
|
|
|
|
* ath9k_hw_setpower() return type void.
|
|
|
|
*/
|
2010-12-20 10:32:42 +08:00
|
|
|
|
|
|
|
if (!(ah->ah_flags & AH_UNPLUGGED))
|
|
|
|
ATH_DBG_WARN_ON_ONCE(!status);
|
2010-12-08 07:13:23 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
return status;
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_setpower);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
/*******************/
|
|
|
|
/* Beacon Handling */
|
|
|
|
/*******************/
|
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
|
|
|
int flags = 0;
|
|
|
|
|
2010-04-16 14:23:57 +08:00
|
|
|
ENABLE_REGWRITE_BUFFER(ah);
|
|
|
|
|
2009-02-09 15:57:26 +08:00
|
|
|
switch (ah->opmode) {
|
2008-12-02 05:38:55 +08:00
|
|
|
case NL80211_IFTYPE_ADHOC:
|
2009-03-21 10:59:59 +08:00
|
|
|
case NL80211_IFTYPE_MESH_POINT:
|
2008-08-04 15:16:41 +08:00
|
|
|
REG_SET_BIT(ah, AR_TXCFG,
|
|
|
|
AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY);
|
2011-03-23 04:54:17 +08:00
|
|
|
REG_WRITE(ah, AR_NEXT_NDP_TIMER, next_beacon +
|
|
|
|
TU_TO_USEC(ah->atim_window ? ah->atim_window : 1));
|
2008-08-04 15:16:41 +08:00
|
|
|
flags |= AR_NDP_TIMER_EN;
|
2008-12-02 05:38:55 +08:00
|
|
|
case NL80211_IFTYPE_AP:
|
2011-03-23 04:54:17 +08:00
|
|
|
REG_WRITE(ah, AR_NEXT_TBTT_TIMER, next_beacon);
|
|
|
|
REG_WRITE(ah, AR_NEXT_DMA_BEACON_ALERT, next_beacon -
|
|
|
|
TU_TO_USEC(ah->config.dma_beacon_response_time));
|
|
|
|
REG_WRITE(ah, AR_NEXT_SWBA, next_beacon -
|
|
|
|
TU_TO_USEC(ah->config.sw_beacon_response_time));
|
2008-08-04 15:16:41 +08:00
|
|
|
flags |=
|
|
|
|
AR_TBTT_TIMER_EN | AR_DBA_TIMER_EN | AR_SWBA_TIMER_EN;
|
|
|
|
break;
|
2008-12-02 05:38:55 +08:00
|
|
|
default:
|
2010-12-03 11:12:37 +08:00
|
|
|
ath_dbg(ath9k_hw_common(ah), ATH_DBG_BEACON,
|
|
|
|
"%s: unsupported opmode: %d\n",
|
|
|
|
__func__, ah->opmode);
|
2008-12-02 05:38:55 +08:00
|
|
|
return;
|
|
|
|
break;
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
|
2011-03-23 04:54:17 +08:00
|
|
|
REG_WRITE(ah, AR_BEACON_PERIOD, beacon_period);
|
|
|
|
REG_WRITE(ah, AR_DMA_BEACON_PERIOD, beacon_period);
|
|
|
|
REG_WRITE(ah, AR_SWBA_PERIOD, beacon_period);
|
|
|
|
REG_WRITE(ah, AR_NDP_PERIOD, beacon_period);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-04-16 14:23:57 +08:00
|
|
|
REGWRITE_BUFFER_FLUSH(ah);
|
|
|
|
|
2008-08-04 15:16:41 +08:00
|
|
|
REG_SET_BIT(ah, AR_TIMER_MODE, flags);
|
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_beaconinit);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah,
|
2008-10-29 12:46:30 +08:00
|
|
|
const struct ath9k_beacon_state *bs)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
|
|
|
u32 nextTbtt, beaconintval, dtimperiod, beacontimeout;
|
2009-02-09 15:57:26 +08:00
|
|
|
struct ath9k_hw_capabilities *pCap = &ah->caps;
|
2009-09-13 17:42:02 +08:00
|
|
|
struct ath_common *common = ath9k_hw_common(ah);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-04-16 14:23:57 +08:00
|
|
|
ENABLE_REGWRITE_BUFFER(ah);
|
|
|
|
|
2008-08-04 15:16:41 +08:00
|
|
|
REG_WRITE(ah, AR_NEXT_TBTT_TIMER, TU_TO_USEC(bs->bs_nexttbtt));
|
|
|
|
|
|
|
|
REG_WRITE(ah, AR_BEACON_PERIOD,
|
2011-05-20 20:22:11 +08:00
|
|
|
TU_TO_USEC(bs->bs_intval));
|
2008-08-04 15:16:41 +08:00
|
|
|
REG_WRITE(ah, AR_DMA_BEACON_PERIOD,
|
2011-05-20 20:22:11 +08:00
|
|
|
TU_TO_USEC(bs->bs_intval));
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-04-16 14:23:57 +08:00
|
|
|
REGWRITE_BUFFER_FLUSH(ah);
|
|
|
|
|
2008-08-04 15:16:41 +08:00
|
|
|
REG_RMW_FIELD(ah, AR_RSSI_THR,
|
|
|
|
AR_RSSI_THR_BM_THR, bs->bs_bmissthreshold);
|
|
|
|
|
2011-05-20 20:22:11 +08:00
|
|
|
beaconintval = bs->bs_intval;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
|
|
|
if (bs->bs_sleepduration > beaconintval)
|
|
|
|
beaconintval = bs->bs_sleepduration;
|
|
|
|
|
|
|
|
dtimperiod = bs->bs_dtimperiod;
|
|
|
|
if (bs->bs_sleepduration > dtimperiod)
|
|
|
|
dtimperiod = bs->bs_sleepduration;
|
|
|
|
|
|
|
|
if (beaconintval == dtimperiod)
|
|
|
|
nextTbtt = bs->bs_nextdtim;
|
|
|
|
else
|
|
|
|
nextTbtt = bs->bs_nexttbtt;
|
|
|
|
|
2010-12-03 11:12:37 +08:00
|
|
|
ath_dbg(common, ATH_DBG_BEACON, "next DTIM %d\n", bs->bs_nextdtim);
|
|
|
|
ath_dbg(common, ATH_DBG_BEACON, "next beacon %d\n", nextTbtt);
|
|
|
|
ath_dbg(common, ATH_DBG_BEACON, "beacon period %d\n", beaconintval);
|
|
|
|
ath_dbg(common, ATH_DBG_BEACON, "DTIM period %d\n", dtimperiod);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-04-16 14:23:57 +08:00
|
|
|
ENABLE_REGWRITE_BUFFER(ah);
|
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_WRITE(ah, AR_NEXT_DTIM,
|
|
|
|
TU_TO_USEC(bs->bs_nextdtim - SLEEP_SLOP));
|
|
|
|
REG_WRITE(ah, AR_NEXT_TIM, TU_TO_USEC(nextTbtt - SLEEP_SLOP));
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_WRITE(ah, AR_SLEEP1,
|
|
|
|
SM((CAB_TIMEOUT_VAL << 3), AR_SLEEP1_CAB_TIMEOUT)
|
|
|
|
| AR_SLEEP1_ASSUME_DTIM);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
if (pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)
|
|
|
|
beacontimeout = (BEACON_TIMEOUT_VAL << 3);
|
|
|
|
else
|
|
|
|
beacontimeout = MIN_BEACON_TIMEOUT_VAL;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_WRITE(ah, AR_SLEEP2,
|
|
|
|
SM(beacontimeout, AR_SLEEP2_BEACON_TIMEOUT));
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_WRITE(ah, AR_TIM_PERIOD, TU_TO_USEC(beaconintval));
|
|
|
|
REG_WRITE(ah, AR_DTIM_PERIOD, TU_TO_USEC(dtimperiod));
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-04-16 14:23:57 +08:00
|
|
|
REGWRITE_BUFFER_FLUSH(ah);
|
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_SET_BIT(ah, AR_TIMER_MODE,
|
|
|
|
AR_TBTT_TIMER_EN | AR_TIM_TIMER_EN |
|
|
|
|
AR_DTIM_TIMER_EN);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-02-12 12:36:47 +08:00
|
|
|
/* TSF Out of Range Threshold */
|
|
|
|
REG_WRITE(ah, AR_TSFOOR_THRESHOLD, bs->bs_tsfoor_threshold);
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_set_sta_beacon_timers);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
/*******************/
|
|
|
|
/* HW Capabilities */
|
|
|
|
/*******************/
|
|
|
|
|
2011-07-19 14:46:44 +08:00
|
|
|
static u8 fixup_chainmask(u8 chip_chainmask, u8 eeprom_chainmask)
|
|
|
|
{
|
|
|
|
eeprom_chainmask &= chip_chainmask;
|
|
|
|
if (eeprom_chainmask)
|
|
|
|
return eeprom_chainmask;
|
|
|
|
else
|
|
|
|
return chip_chainmask;
|
|
|
|
}
|
|
|
|
|
2009-11-27 19:01:35 +08:00
|
|
|
int ath9k_hw_fill_cap_info(struct ath_hw *ah)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2009-02-09 15:57:26 +08:00
|
|
|
struct ath9k_hw_capabilities *pCap = &ah->caps;
|
2009-08-18 09:07:23 +08:00
|
|
|
struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
|
2009-09-13 17:42:02 +08:00
|
|
|
struct ath_common *common = ath9k_hw_common(ah);
|
2009-09-10 05:52:02 +08:00
|
|
|
struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw;
|
2011-07-19 14:46:44 +08:00
|
|
|
unsigned int chip_chainmask;
|
2009-08-18 09:07:23 +08:00
|
|
|
|
2011-04-20 13:30:34 +08:00
|
|
|
u16 eeval;
|
2010-12-06 20:27:43 +08:00
|
|
|
u8 ant_div_ctl1, tx_chainmask, rx_chainmask;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-02-09 15:57:24 +08:00
|
|
|
eeval = ah->eep_ops->get_eeprom(ah, EEP_REG_0);
|
2009-08-18 09:07:23 +08:00
|
|
|
regulatory->current_rd = eeval;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-02-09 15:57:26 +08:00
|
|
|
if (ah->opmode != NL80211_IFTYPE_AP &&
|
2009-02-09 15:57:06 +08:00
|
|
|
ah->hw_version.subvendorid == AR_SUBVENDOR_ID_NEW_A) {
|
2009-08-18 09:07:23 +08:00
|
|
|
if (regulatory->current_rd == 0x64 ||
|
|
|
|
regulatory->current_rd == 0x65)
|
|
|
|
regulatory->current_rd += 5;
|
|
|
|
else if (regulatory->current_rd == 0x41)
|
|
|
|
regulatory->current_rd = 0x43;
|
2010-12-03 11:12:37 +08:00
|
|
|
ath_dbg(common, ATH_DBG_REGULATORY,
|
|
|
|
"regdomain mapped to 0x%x\n", regulatory->current_rd);
|
2008-10-29 12:46:30 +08:00
|
|
|
}
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-02-09 15:57:24 +08:00
|
|
|
eeval = ah->eep_ops->get_eeprom(ah, EEP_OP_MODE);
|
2009-11-27 19:01:35 +08:00
|
|
|
if ((eeval & (AR5416_OPFLAGS_11G | AR5416_OPFLAGS_11A)) == 0) {
|
2010-12-03 11:12:36 +08:00
|
|
|
ath_err(common,
|
|
|
|
"no band has been marked as supported in EEPROM\n");
|
2009-11-27 19:01:35 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2010-10-14 22:02:39 +08:00
|
|
|
if (eeval & AR5416_OPFLAGS_11A)
|
|
|
|
pCap->hw_caps |= ATH9K_HW_CAP_5GHZ;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-10-14 22:02:39 +08:00
|
|
|
if (eeval & AR5416_OPFLAGS_11G)
|
|
|
|
pCap->hw_caps |= ATH9K_HW_CAP_2GHZ;
|
2008-10-29 12:46:30 +08:00
|
|
|
|
2011-07-19 14:46:44 +08:00
|
|
|
if (AR_SREV_9485(ah) || AR_SREV_9285(ah) || AR_SREV_9330(ah))
|
|
|
|
chip_chainmask = 1;
|
|
|
|
else if (!AR_SREV_9280_20_OR_LATER(ah))
|
|
|
|
chip_chainmask = 7;
|
|
|
|
else if (!AR_SREV_9300_20_OR_LATER(ah) || AR_SREV_9340(ah))
|
|
|
|
chip_chainmask = 3;
|
|
|
|
else
|
|
|
|
chip_chainmask = 7;
|
|
|
|
|
2009-02-09 15:57:24 +08:00
|
|
|
pCap->tx_chainmask = ah->eep_ops->get_eeprom(ah, EEP_TX_MASK);
|
2009-08-04 11:14:12 +08:00
|
|
|
/*
|
|
|
|
* For AR9271 we will temporarilly uses the rx chainmax as read from
|
|
|
|
* the EEPROM.
|
|
|
|
*/
|
2009-02-20 17:43:23 +08:00
|
|
|
if ((ah->hw_version.devid == AR5416_DEVID_PCI) &&
|
2009-08-04 11:14:12 +08:00
|
|
|
!(eeval & AR5416_OPFLAGS_11A) &&
|
|
|
|
!(AR_SREV_9271(ah)))
|
|
|
|
/* CB71: GPIO 0 is pulled down to indicate 3 rx chains */
|
2009-02-20 17:43:23 +08:00
|
|
|
pCap->rx_chainmask = ath9k_hw_gpio_get(ah, 0) ? 0x5 : 0x7;
|
2011-03-19 20:55:42 +08:00
|
|
|
else if (AR_SREV_9100(ah))
|
|
|
|
pCap->rx_chainmask = 0x7;
|
2009-02-20 17:43:23 +08:00
|
|
|
else
|
2009-08-04 11:14:12 +08:00
|
|
|
/* Use rx_chainmask from EEPROM. */
|
2009-02-20 17:43:23 +08:00
|
|
|
pCap->rx_chainmask = ah->eep_ops->get_eeprom(ah, EEP_RX_MASK);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2011-07-19 14:46:44 +08:00
|
|
|
pCap->tx_chainmask = fixup_chainmask(chip_chainmask, pCap->tx_chainmask);
|
|
|
|
pCap->rx_chainmask = fixup_chainmask(chip_chainmask, pCap->rx_chainmask);
|
2011-09-03 07:40:23 +08:00
|
|
|
ah->txchainmask = pCap->tx_chainmask;
|
|
|
|
ah->rxchainmask = pCap->rx_chainmask;
|
2011-07-19 14:46:44 +08:00
|
|
|
|
2010-09-22 18:34:52 +08:00
|
|
|
ah->misc_mode |= AR_PCU_MIC_NEW_LOC_ENA;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-11-22 22:39:39 +08:00
|
|
|
/* enable key search for every frame in an aggregate */
|
|
|
|
if (AR_SREV_9300_20_OR_LATER(ah))
|
|
|
|
ah->misc_mode |= AR_PCU_ALWAYS_PERFORM_KEYSEARCH;
|
|
|
|
|
2010-09-17 10:36:25 +08:00
|
|
|
common->crypt_caps |= ATH_CRYPT_CAP_CIPHER_AESCCM;
|
|
|
|
|
2011-03-24 03:57:29 +08:00
|
|
|
if (ah->hw_version.devid != AR2427_DEVID_PCIE)
|
2008-10-29 12:46:30 +08:00
|
|
|
pCap->hw_caps |= ATH9K_HW_CAP_HT;
|
|
|
|
else
|
|
|
|
pCap->hw_caps &= ~ATH9K_HW_CAP_HT;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-03-17 16:55:15 +08:00
|
|
|
if (AR_SREV_9271(ah))
|
|
|
|
pCap->num_gpio_pins = AR9271_NUM_GPIO;
|
2010-06-30 17:16:31 +08:00
|
|
|
else if (AR_DEVID_7010(ah))
|
|
|
|
pCap->num_gpio_pins = AR7010_NUM_GPIO;
|
2011-09-30 14:01:27 +08:00
|
|
|
else if (AR_SREV_9300_20_OR_LATER(ah))
|
|
|
|
pCap->num_gpio_pins = AR9300_NUM_GPIO;
|
|
|
|
else if (AR_SREV_9287_11_OR_LATER(ah))
|
|
|
|
pCap->num_gpio_pins = AR9287_NUM_GPIO;
|
2010-09-22 18:34:53 +08:00
|
|
|
else if (AR_SREV_9285_12_OR_LATER(ah))
|
2008-12-24 20:33:58 +08:00
|
|
|
pCap->num_gpio_pins = AR9285_NUM_GPIO;
|
2010-09-22 18:34:52 +08:00
|
|
|
else if (AR_SREV_9280_20_OR_LATER(ah))
|
2008-10-29 12:46:30 +08:00
|
|
|
pCap->num_gpio_pins = AR928X_NUM_GPIO;
|
|
|
|
else
|
|
|
|
pCap->num_gpio_pins = AR_NUM_GPIO;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
if (AR_SREV_9160_10_OR_LATER(ah) || AR_SREV_9100(ah)) {
|
|
|
|
pCap->hw_caps |= ATH9K_HW_CAP_CST;
|
|
|
|
pCap->rts_aggr_limit = ATH_AMPDU_LIMIT_MAX;
|
|
|
|
} else {
|
|
|
|
pCap->rts_aggr_limit = (8 * 1024);
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
|
2008-11-13 20:30:02 +08:00
|
|
|
#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
|
2009-02-09 15:57:26 +08:00
|
|
|
ah->rfsilent = ah->eep_ops->get_eeprom(ah, EEP_RF_SILENT);
|
|
|
|
if (ah->rfsilent & EEP_RFSILENT_ENABLED) {
|
|
|
|
ah->rfkill_gpio =
|
|
|
|
MS(ah->rfsilent, EEP_RFSILENT_GPIO_SEL);
|
|
|
|
ah->rfkill_polarity =
|
|
|
|
MS(ah->rfsilent, EEP_RFSILENT_POLARITY);
|
2008-10-29 12:46:30 +08:00
|
|
|
|
|
|
|
pCap->hw_caps |= ATH9K_HW_CAP_RFSILENT;
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
2008-10-29 12:46:30 +08:00
|
|
|
#endif
|
2010-05-18 09:57:56 +08:00
|
|
|
if (AR_SREV_9271(ah) || AR_SREV_9300_20_OR_LATER(ah))
|
2010-04-05 17:18:05 +08:00
|
|
|
pCap->hw_caps |= ATH9K_HW_CAP_AUTOSLEEP;
|
|
|
|
else
|
|
|
|
pCap->hw_caps &= ~ATH9K_HW_CAP_AUTOSLEEP;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-12-08 22:13:48 +08:00
|
|
|
if (AR_SREV_9280(ah) || AR_SREV_9285(ah))
|
2008-10-29 12:46:30 +08:00
|
|
|
pCap->hw_caps &= ~ATH9K_HW_CAP_4KB_SPLITTRANS;
|
|
|
|
else
|
|
|
|
pCap->hw_caps |= ATH9K_HW_CAP_4KB_SPLITTRANS;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2011-04-26 13:09:53 +08:00
|
|
|
if (common->btcoex_enabled) {
|
|
|
|
if (AR_SREV_9300_20_OR_LATER(ah)) {
|
2009-09-10 05:52:02 +08:00
|
|
|
btcoex_hw->scheme = ATH_BTCOEX_CFG_3WIRE;
|
2011-04-26 13:09:53 +08:00
|
|
|
btcoex_hw->btactive_gpio = ATH_BTACTIVE_GPIO_9300;
|
|
|
|
btcoex_hw->wlanactive_gpio = ATH_WLANACTIVE_GPIO_9300;
|
|
|
|
btcoex_hw->btpriority_gpio = ATH_BTPRIORITY_GPIO_9300;
|
|
|
|
} else if (AR_SREV_9280_20_OR_LATER(ah)) {
|
|
|
|
btcoex_hw->btactive_gpio = ATH_BTACTIVE_GPIO_9280;
|
|
|
|
btcoex_hw->wlanactive_gpio = ATH_WLANACTIVE_GPIO_9280;
|
|
|
|
|
|
|
|
if (AR_SREV_9285(ah)) {
|
|
|
|
btcoex_hw->scheme = ATH_BTCOEX_CFG_3WIRE;
|
|
|
|
btcoex_hw->btpriority_gpio =
|
|
|
|
ATH_BTPRIORITY_GPIO_9285;
|
|
|
|
} else {
|
|
|
|
btcoex_hw->scheme = ATH_BTCOEX_CFG_2WIRE;
|
|
|
|
}
|
2009-09-09 17:55:52 +08:00
|
|
|
}
|
2009-08-26 23:38:47 +08:00
|
|
|
} else {
|
2009-09-10 05:52:02 +08:00
|
|
|
btcoex_hw->scheme = ATH_BTCOEX_CFG_NONE;
|
2009-01-02 18:05:46 +08:00
|
|
|
}
|
2009-11-27 19:01:35 +08:00
|
|
|
|
2010-04-16 05:38:25 +08:00
|
|
|
if (AR_SREV_9300_20_OR_LATER(ah)) {
|
2010-12-06 20:27:40 +08:00
|
|
|
pCap->hw_caps |= ATH9K_HW_CAP_EDMA | ATH9K_HW_CAP_FASTCLOCK;
|
2011-06-21 17:23:31 +08:00
|
|
|
if (!AR_SREV_9330(ah) && !AR_SREV_9485(ah))
|
2010-12-06 20:27:40 +08:00
|
|
|
pCap->hw_caps |= ATH9K_HW_CAP_LDPC;
|
|
|
|
|
2010-04-16 05:38:25 +08:00
|
|
|
pCap->rx_hp_qdepth = ATH9K_HW_RX_HP_QDEPTH;
|
|
|
|
pCap->rx_lp_qdepth = ATH9K_HW_RX_LP_QDEPTH;
|
|
|
|
pCap->rx_status_len = sizeof(struct ar9003_rxs);
|
2010-04-16 05:38:41 +08:00
|
|
|
pCap->tx_desc_len = sizeof(struct ar9003_txc);
|
2010-04-16 05:39:34 +08:00
|
|
|
pCap->txs_len = sizeof(struct ar9003_txs);
|
2011-01-21 09:47:39 +08:00
|
|
|
if (!ah->config.paprd_disable &&
|
|
|
|
ah->eep_ops->get_eeprom(ah, EEP_PAPRD))
|
2010-06-12 12:33:59 +08:00
|
|
|
pCap->hw_caps |= ATH9K_HW_CAP_PAPRD;
|
2010-04-16 05:38:41 +08:00
|
|
|
} else {
|
|
|
|
pCap->tx_desc_len = sizeof(struct ath_desc);
|
2011-07-09 12:12:47 +08:00
|
|
|
if (AR_SREV_9280_20(ah))
|
2010-04-27 03:04:35 +08:00
|
|
|
pCap->hw_caps |= ATH9K_HW_CAP_FASTCLOCK;
|
2010-04-16 05:38:25 +08:00
|
|
|
}
|
2010-04-16 05:38:24 +08:00
|
|
|
|
2010-04-16 05:39:16 +08:00
|
|
|
if (AR_SREV_9300_20_OR_LATER(ah))
|
|
|
|
pCap->hw_caps |= ATH9K_HW_CAP_RAC_SUPPORTED;
|
|
|
|
|
2010-11-10 21:03:16 +08:00
|
|
|
if (AR_SREV_9300_20_OR_LATER(ah))
|
|
|
|
ah->ent_mode = REG_READ(ah, AR_ENT_OTP);
|
|
|
|
|
2010-09-22 18:34:54 +08:00
|
|
|
if (AR_SREV_9287_11_OR_LATER(ah) || AR_SREV_9271(ah))
|
2010-05-14 09:42:38 +08:00
|
|
|
pCap->hw_caps |= ATH9K_HW_CAP_SGI_20;
|
|
|
|
|
2010-09-02 16:34:41 +08:00
|
|
|
if (AR_SREV_9285(ah))
|
|
|
|
if (ah->eep_ops->get_eeprom(ah, EEP_MODAL_VER) >= 3) {
|
|
|
|
ant_div_ctl1 =
|
|
|
|
ah->eep_ops->get_eeprom(ah, EEP_ANT_DIV_CTL1);
|
|
|
|
if ((ant_div_ctl1 & 0x1) && ((ant_div_ctl1 >> 3) & 0x1))
|
|
|
|
pCap->hw_caps |= ATH9K_HW_CAP_ANT_DIV_COMB;
|
|
|
|
}
|
2010-11-23 23:12:27 +08:00
|
|
|
if (AR_SREV_9300_20_OR_LATER(ah)) {
|
|
|
|
if (ah->eep_ops->get_eeprom(ah, EEP_CHAIN_MASK_REDUCE))
|
|
|
|
pCap->hw_caps |= ATH9K_HW_CAP_APM;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-06-21 17:23:41 +08:00
|
|
|
if (AR_SREV_9330(ah) || AR_SREV_9485(ah)) {
|
2011-05-13 22:59:31 +08:00
|
|
|
ant_div_ctl1 = ah->eep_ops->get_eeprom(ah, EEP_ANT_DIV_CTL1);
|
|
|
|
/*
|
|
|
|
* enable the diversity-combining algorithm only when
|
|
|
|
* both enable_lna_div and enable_fast_div are set
|
|
|
|
* Table for Diversity
|
|
|
|
* ant_div_alt_lnaconf bit 0-1
|
|
|
|
* ant_div_main_lnaconf bit 2-3
|
|
|
|
* ant_div_alt_gaintb bit 4
|
|
|
|
* ant_div_main_gaintb bit 5
|
|
|
|
* enable_ant_div_lnadiv bit 6
|
|
|
|
* enable_ant_fast_div bit 7
|
|
|
|
*/
|
|
|
|
if ((ant_div_ctl1 >> 0x6) == 0x3)
|
|
|
|
pCap->hw_caps |= ATH9K_HW_CAP_ANT_DIV_COMB;
|
|
|
|
}
|
2010-09-02 16:34:41 +08:00
|
|
|
|
2010-12-06 20:27:42 +08:00
|
|
|
if (AR_SREV_9485_10(ah)) {
|
|
|
|
pCap->pcie_lcr_extsync_en = true;
|
|
|
|
pCap->pcie_lcr_offset = 0x80;
|
|
|
|
}
|
|
|
|
|
2010-12-06 20:27:43 +08:00
|
|
|
tx_chainmask = pCap->tx_chainmask;
|
|
|
|
rx_chainmask = pCap->rx_chainmask;
|
|
|
|
while (tx_chainmask || rx_chainmask) {
|
|
|
|
if (tx_chainmask & BIT(0))
|
|
|
|
pCap->max_txchains++;
|
|
|
|
if (rx_chainmask & BIT(0))
|
|
|
|
pCap->max_rxchains++;
|
|
|
|
|
|
|
|
tx_chainmask >>= 1;
|
|
|
|
rx_chainmask >>= 1;
|
|
|
|
}
|
|
|
|
|
2011-10-13 13:30:38 +08:00
|
|
|
if (AR_SREV_9300_20_OR_LATER(ah)) {
|
|
|
|
ah->enabled_cals |= TX_IQ_CAL;
|
|
|
|
if (!AR_SREV_9330(ah))
|
|
|
|
ah->enabled_cals |= TX_IQ_ON_AGC_CAL;
|
|
|
|
}
|
2011-10-13 13:30:44 +08:00
|
|
|
if (AR_SREV_9462(ah))
|
2011-10-24 20:49:49 +08:00
|
|
|
pCap->hw_caps |= ATH9K_HW_CAP_RTT | ATH9K_HW_CAP_MCI;
|
2011-10-13 13:30:41 +08:00
|
|
|
|
2009-11-27 19:01:35 +08:00
|
|
|
return 0;
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
/****************************/
|
|
|
|
/* GPIO / RFKILL / Antennae */
|
|
|
|
/****************************/
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
static void ath9k_hw_gpio_cfg_output_mux(struct ath_hw *ah,
|
2008-10-29 12:46:30 +08:00
|
|
|
u32 gpio, u32 type)
|
|
|
|
{
|
|
|
|
int addr;
|
|
|
|
u32 gpio_shift, tmp;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
if (gpio > 11)
|
|
|
|
addr = AR_GPIO_OUTPUT_MUX3;
|
|
|
|
else if (gpio > 5)
|
|
|
|
addr = AR_GPIO_OUTPUT_MUX2;
|
|
|
|
else
|
|
|
|
addr = AR_GPIO_OUTPUT_MUX1;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
gpio_shift = (gpio % 6) * 5;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
if (AR_SREV_9280_20_OR_LATER(ah)
|
|
|
|
|| (addr != AR_GPIO_OUTPUT_MUX1)) {
|
|
|
|
REG_RMW(ah, addr, (type << gpio_shift),
|
|
|
|
(0x1f << gpio_shift));
|
2008-08-04 15:16:41 +08:00
|
|
|
} else {
|
2008-10-29 12:46:30 +08:00
|
|
|
tmp = REG_READ(ah, addr);
|
|
|
|
tmp = ((tmp & 0x1F0) << 1) | (tmp & ~0x1F0);
|
|
|
|
tmp &= ~(0x1f << gpio_shift);
|
|
|
|
tmp |= (type << gpio_shift);
|
|
|
|
REG_WRITE(ah, addr, tmp);
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
void ath9k_hw_cfg_gpio_input(struct ath_hw *ah, u32 gpio)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2008-10-29 12:46:30 +08:00
|
|
|
u32 gpio_shift;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-09-14 14:28:00 +08:00
|
|
|
BUG_ON(gpio >= ah->caps.num_gpio_pins);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-06-30 17:16:31 +08:00
|
|
|
if (AR_DEVID_7010(ah)) {
|
|
|
|
gpio_shift = gpio;
|
|
|
|
REG_RMW(ah, AR7010_GPIO_OE,
|
|
|
|
(AR7010_GPIO_OE_AS_INPUT << gpio_shift),
|
|
|
|
(AR7010_GPIO_OE_MASK << gpio_shift));
|
|
|
|
return;
|
|
|
|
}
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-06-30 17:16:31 +08:00
|
|
|
gpio_shift = gpio << 1;
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_RMW(ah,
|
|
|
|
AR_GPIO_OE_OUT,
|
|
|
|
(AR_GPIO_OE_OUT_DRV_NO << gpio_shift),
|
|
|
|
(AR_GPIO_OE_OUT_DRV << gpio_shift));
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_cfg_gpio_input);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2008-12-24 20:33:58 +08:00
|
|
|
#define MS_REG_READ(x, y) \
|
|
|
|
(MS(REG_READ(ah, AR_GPIO_IN_OUT), x##_GPIO_IN_VAL) & (AR_GPIO_BIT(y)))
|
|
|
|
|
2009-02-09 15:57:26 +08:00
|
|
|
if (gpio >= ah->caps.num_gpio_pins)
|
2008-10-29 12:46:30 +08:00
|
|
|
return 0xffffffff;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-06-30 17:16:31 +08:00
|
|
|
if (AR_DEVID_7010(ah)) {
|
|
|
|
u32 val;
|
|
|
|
val = REG_READ(ah, AR7010_GPIO_IN);
|
|
|
|
return (MS(val, AR7010_GPIO_IN_VAL) & AR_GPIO_BIT(gpio)) == 0;
|
|
|
|
} else if (AR_SREV_9300_20_OR_LATER(ah))
|
2010-12-01 15:24:09 +08:00
|
|
|
return (MS(REG_READ(ah, AR_GPIO_IN), AR9300_GPIO_IN_VAL) &
|
|
|
|
AR_GPIO_BIT(gpio)) != 0;
|
2010-04-16 05:38:11 +08:00
|
|
|
else if (AR_SREV_9271(ah))
|
2010-03-17 16:55:15 +08:00
|
|
|
return MS_REG_READ(AR9271, gpio) != 0;
|
2010-09-22 18:34:54 +08:00
|
|
|
else if (AR_SREV_9287_11_OR_LATER(ah))
|
2009-07-23 13:29:57 +08:00
|
|
|
return MS_REG_READ(AR9287, gpio) != 0;
|
2010-09-22 18:34:53 +08:00
|
|
|
else if (AR_SREV_9285_12_OR_LATER(ah))
|
2008-12-24 20:33:58 +08:00
|
|
|
return MS_REG_READ(AR9285, gpio) != 0;
|
2010-09-22 18:34:52 +08:00
|
|
|
else if (AR_SREV_9280_20_OR_LATER(ah))
|
2008-12-24 20:33:58 +08:00
|
|
|
return MS_REG_READ(AR928X, gpio) != 0;
|
|
|
|
else
|
|
|
|
return MS_REG_READ(AR, gpio) != 0;
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_gpio_get);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
void ath9k_hw_cfg_output(struct ath_hw *ah, u32 gpio,
|
2008-10-29 12:46:30 +08:00
|
|
|
u32 ah_signal_type)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2008-10-29 12:46:30 +08:00
|
|
|
u32 gpio_shift;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-06-30 17:16:31 +08:00
|
|
|
if (AR_DEVID_7010(ah)) {
|
|
|
|
gpio_shift = gpio;
|
|
|
|
REG_RMW(ah, AR7010_GPIO_OE,
|
|
|
|
(AR7010_GPIO_OE_AS_OUTPUT << gpio_shift),
|
|
|
|
(AR7010_GPIO_OE_MASK << gpio_shift));
|
|
|
|
return;
|
|
|
|
}
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-06-30 17:16:31 +08:00
|
|
|
ath9k_hw_gpio_cfg_output_mux(ah, gpio, ah_signal_type);
|
2008-10-29 12:46:30 +08:00
|
|
|
gpio_shift = 2 * gpio;
|
|
|
|
REG_RMW(ah,
|
|
|
|
AR_GPIO_OE_OUT,
|
|
|
|
(AR_GPIO_OE_OUT_DRV_ALL << gpio_shift),
|
|
|
|
(AR_GPIO_OE_OUT_DRV << gpio_shift));
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_cfg_output);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2010-06-30 17:16:31 +08:00
|
|
|
if (AR_DEVID_7010(ah)) {
|
|
|
|
val = val ? 0 : 1;
|
|
|
|
REG_RMW(ah, AR7010_GPIO_OUT, ((val&1) << gpio),
|
|
|
|
AR_GPIO_BIT(gpio));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-03-17 16:55:15 +08:00
|
|
|
if (AR_SREV_9271(ah))
|
|
|
|
val = ~val;
|
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_RMW(ah, AR_GPIO_IN_OUT, ((val & 1) << gpio),
|
|
|
|
AR_GPIO_BIT(gpio));
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_set_gpio);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
u32 ath9k_hw_getdefantenna(struct ath_hw *ah)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2008-10-29 12:46:30 +08:00
|
|
|
return REG_READ(ah, AR_DEF_ANTENNA) & 0x7;
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_getdefantenna);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_WRITE(ah, AR_DEF_ANTENNA, (antenna & 0x7));
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_setantenna);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
/*********************/
|
|
|
|
/* General Operation */
|
|
|
|
/*********************/
|
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
u32 ath9k_hw_getrxfilter(struct ath_hw *ah)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2008-10-29 12:46:30 +08:00
|
|
|
u32 bits = REG_READ(ah, AR_RX_FILTER);
|
|
|
|
u32 phybits = REG_READ(ah, AR_PHY_ERR);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
if (phybits & AR_PHY_ERR_RADAR)
|
|
|
|
bits |= ATH9K_RX_FILTER_PHYRADAR;
|
|
|
|
if (phybits & (AR_PHY_ERR_OFDM_TIMING | AR_PHY_ERR_CCK_TIMING))
|
|
|
|
bits |= ATH9K_RX_FILTER_PHYERR;
|
2008-08-14 15:56:55 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
return bits;
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_getrxfilter);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
void ath9k_hw_setrxfilter(struct ath_hw *ah, u32 bits)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2008-10-29 12:46:30 +08:00
|
|
|
u32 phybits;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-04-16 14:23:57 +08:00
|
|
|
ENABLE_REGWRITE_BUFFER(ah);
|
|
|
|
|
2011-10-13 13:30:44 +08:00
|
|
|
if (AR_SREV_9462(ah))
|
2011-09-14 01:08:18 +08:00
|
|
|
bits |= ATH9K_RX_FILTER_CONTROL_WRAPPER;
|
|
|
|
|
2009-09-03 14:38:43 +08:00
|
|
|
REG_WRITE(ah, AR_RX_FILTER, bits);
|
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
phybits = 0;
|
|
|
|
if (bits & ATH9K_RX_FILTER_PHYRADAR)
|
|
|
|
phybits |= AR_PHY_ERR_RADAR;
|
|
|
|
if (bits & ATH9K_RX_FILTER_PHYERR)
|
|
|
|
phybits |= AR_PHY_ERR_OFDM_TIMING | AR_PHY_ERR_CCK_TIMING;
|
|
|
|
REG_WRITE(ah, AR_PHY_ERR, phybits);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
if (phybits)
|
2011-03-24 03:57:26 +08:00
|
|
|
REG_SET_BIT(ah, AR_RXCFG, AR_RXCFG_ZLFDMA);
|
2008-10-29 12:46:30 +08:00
|
|
|
else
|
2011-03-24 03:57:26 +08:00
|
|
|
REG_CLR_BIT(ah, AR_RXCFG, AR_RXCFG_ZLFDMA);
|
2010-04-16 14:23:57 +08:00
|
|
|
|
|
|
|
REGWRITE_BUFFER_FLUSH(ah);
|
2008-10-29 12:46:30 +08:00
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_setrxfilter);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
bool ath9k_hw_phy_disable(struct ath_hw *ah)
|
2008-10-29 12:46:30 +08:00
|
|
|
{
|
2009-09-18 17:37:03 +08:00
|
|
|
if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_WARM))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
ath9k_hw_init_pll(ah, NULL);
|
|
|
|
return true;
|
2008-10-29 12:46:30 +08:00
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_phy_disable);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
bool ath9k_hw_disable(struct ath_hw *ah)
|
2008-10-29 12:46:30 +08:00
|
|
|
{
|
2009-09-10 12:10:09 +08:00
|
|
|
if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE))
|
2008-10-29 12:46:30 +08:00
|
|
|
return false;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-09-18 17:37:03 +08:00
|
|
|
if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_COLD))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
ath9k_hw_init_pll(ah, NULL);
|
|
|
|
return true;
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_disable);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2011-10-09 02:06:20 +08:00
|
|
|
static int get_antenna_gain(struct ath_hw *ah, struct ath9k_channel *chan)
|
|
|
|
{
|
|
|
|
enum eeprom_param gain_param;
|
|
|
|
|
|
|
|
if (IS_CHAN_2GHZ(chan))
|
|
|
|
gain_param = EEP_ANTENNA_GAIN_2G;
|
|
|
|
else
|
|
|
|
gain_param = EEP_ANTENNA_GAIN_5G;
|
|
|
|
|
|
|
|
return ah->eep_ops->get_eeprom(ah, gain_param);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ath9k_hw_apply_txpower(struct ath_hw *ah, struct ath9k_channel *chan)
|
|
|
|
{
|
|
|
|
struct ath_regulatory *reg = ath9k_hw_regulatory(ah);
|
|
|
|
struct ieee80211_channel *channel;
|
|
|
|
int chan_pwr, new_pwr, max_gain;
|
|
|
|
int ant_gain, ant_reduction = 0;
|
|
|
|
|
|
|
|
if (!chan)
|
|
|
|
return;
|
|
|
|
|
|
|
|
channel = chan->chan;
|
|
|
|
chan_pwr = min_t(int, channel->max_power * 2, MAX_RATE_POWER);
|
|
|
|
new_pwr = min_t(int, chan_pwr, reg->power_limit);
|
|
|
|
max_gain = chan_pwr - new_pwr + channel->max_antenna_gain * 2;
|
|
|
|
|
|
|
|
ant_gain = get_antenna_gain(ah, chan);
|
|
|
|
if (ant_gain > max_gain)
|
|
|
|
ant_reduction = ant_gain - max_gain;
|
|
|
|
|
|
|
|
ah->eep_ops->set_txpower(ah, chan,
|
|
|
|
ath9k_regd_get_ctl(reg, chan),
|
|
|
|
ant_reduction, new_pwr, false);
|
|
|
|
}
|
|
|
|
|
2010-10-20 09:08:53 +08:00
|
|
|
void ath9k_hw_set_txpowerlimit(struct ath_hw *ah, u32 limit, bool test)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2011-10-09 02:06:20 +08:00
|
|
|
struct ath_regulatory *reg = ath9k_hw_regulatory(ah);
|
2009-02-09 15:57:26 +08:00
|
|
|
struct ath9k_channel *chan = ah->curchan;
|
2009-01-23 07:16:48 +08:00
|
|
|
struct ieee80211_channel *channel = chan->chan;
|
2011-07-27 21:01:05 +08:00
|
|
|
|
2011-10-17 15:28:23 +08:00
|
|
|
reg->power_limit = min_t(u32, limit, MAX_RATE_POWER);
|
2011-07-27 21:01:05 +08:00
|
|
|
if (test)
|
2011-10-09 02:06:20 +08:00
|
|
|
channel->max_power = MAX_RATE_POWER / 2;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2011-10-09 02:06:20 +08:00
|
|
|
ath9k_hw_apply_txpower(ah, chan);
|
2008-10-04 06:45:27 +08:00
|
|
|
|
2011-10-09 02:06:20 +08:00
|
|
|
if (test)
|
|
|
|
channel->max_power = DIV_ROUND_UP(reg->max_power_level, 2);
|
2008-10-04 06:45:27 +08:00
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_set_txpowerlimit);
|
2008-10-04 06:45:27 +08:00
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
void ath9k_hw_setopmode(struct ath_hw *ah)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2009-02-09 15:57:26 +08:00
|
|
|
ath9k_hw_set_operating_mode(ah, ah->opmode);
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_setopmode);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
void ath9k_hw_setmcastfilter(struct ath_hw *ah, u32 filter0, u32 filter1)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_WRITE(ah, AR_MCAST_FIL0, filter0);
|
|
|
|
REG_WRITE(ah, AR_MCAST_FIL1, filter1);
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_setmcastfilter);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-09-10 23:50:20 +08:00
|
|
|
void ath9k_hw_write_associd(struct ath_hw *ah)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2009-09-11 00:22:37 +08:00
|
|
|
struct ath_common *common = ath9k_hw_common(ah);
|
|
|
|
|
|
|
|
REG_WRITE(ah, AR_BSS_ID0, get_unaligned_le32(common->curbssid));
|
|
|
|
REG_WRITE(ah, AR_BSS_ID1, get_unaligned_le16(common->curbssid + 4) |
|
|
|
|
((common->curaid & 0x3fff) << AR_BSS_ID1_AID_S));
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_write_associd);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-04-16 06:07:26 +08:00
|
|
|
#define ATH9K_MAX_TSF_READ 10
|
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
u64 ath9k_hw_gettsf64(struct ath_hw *ah)
|
2008-08-04 15:16:41 +08:00
|
|
|
{
|
2010-04-16 06:07:26 +08:00
|
|
|
u32 tsf_lower, tsf_upper1, tsf_upper2;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
tsf_upper1 = REG_READ(ah, AR_TSF_U32);
|
|
|
|
for (i = 0; i < ATH9K_MAX_TSF_READ; i++) {
|
|
|
|
tsf_lower = REG_READ(ah, AR_TSF_L32);
|
|
|
|
tsf_upper2 = REG_READ(ah, AR_TSF_U32);
|
|
|
|
if (tsf_upper2 == tsf_upper1)
|
|
|
|
break;
|
|
|
|
tsf_upper1 = tsf_upper2;
|
|
|
|
}
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-04-16 06:07:26 +08:00
|
|
|
WARN_ON( i == ATH9K_MAX_TSF_READ );
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2010-04-16 06:07:26 +08:00
|
|
|
return (((u64)tsf_upper1 << 32) | tsf_lower);
|
2008-10-29 12:46:30 +08:00
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_gettsf64);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
void ath9k_hw_settsf64(struct ath_hw *ah, u64 tsf64)
|
2009-01-23 12:44:21 +08:00
|
|
|
{
|
|
|
|
REG_WRITE(ah, AR_TSF_L32, tsf64 & 0xffffffff);
|
2009-03-03 06:28:38 +08:00
|
|
|
REG_WRITE(ah, AR_TSF_U32, (tsf64 >> 32) & 0xffffffff);
|
2009-01-23 12:44:21 +08:00
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_settsf64);
|
2009-01-23 12:44:21 +08:00
|
|
|
|
2009-02-09 15:57:12 +08:00
|
|
|
void ath9k_hw_reset_tsf(struct ath_hw *ah)
|
2008-10-29 12:46:30 +08:00
|
|
|
{
|
2009-06-21 06:02:15 +08:00
|
|
|
if (!ath9k_hw_wait(ah, AR_SLP32_MODE, AR_SLP32_TSF_WRITE_STATUS, 0,
|
|
|
|
AH_TSF_WRITE_TIMEOUT))
|
2010-12-03 11:12:37 +08:00
|
|
|
ath_dbg(ath9k_hw_common(ah), ATH_DBG_RESET,
|
|
|
|
"AR_SLP32_TSF_WRITE_STATUS limit exceeded\n");
|
2009-06-21 06:02:15 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_WRITE(ah, AR_RESET_TSF, AR_RESET_TSF_ONCE);
|
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_reset_tsf);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-08-07 12:15:09 +08:00
|
|
|
void ath9k_hw_set_tsfadjust(struct ath_hw *ah, u32 setting)
|
2008-10-29 12:46:30 +08:00
|
|
|
{
|
|
|
|
if (setting)
|
2009-02-09 15:57:26 +08:00
|
|
|
ah->misc_mode |= AR_PCU_TX_ADD_TSF;
|
2008-10-29 12:46:30 +08:00
|
|
|
else
|
2009-02-09 15:57:26 +08:00
|
|
|
ah->misc_mode &= ~AR_PCU_TX_ADD_TSF;
|
2008-10-29 12:46:30 +08:00
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_set_tsfadjust);
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2009-09-14 14:04:44 +08:00
|
|
|
void ath9k_hw_set11nmac2040(struct ath_hw *ah)
|
2008-10-29 12:46:30 +08:00
|
|
|
{
|
2009-09-14 14:04:44 +08:00
|
|
|
struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
|
2008-10-29 12:46:30 +08:00
|
|
|
u32 macmode;
|
|
|
|
|
2009-09-14 14:04:44 +08:00
|
|
|
if (conf_is_ht40(conf) && !ah->config.cwm_ignore_extcca)
|
2008-10-29 12:46:30 +08:00
|
|
|
macmode = AR_2040_JOINED_RX_CLEAR;
|
|
|
|
else
|
|
|
|
macmode = 0;
|
2008-08-04 15:16:41 +08:00
|
|
|
|
2008-10-29 12:46:30 +08:00
|
|
|
REG_WRITE(ah, AR_2040_MODE, macmode);
|
2008-08-04 15:16:41 +08:00
|
|
|
}
|
2009-08-26 23:38:49 +08:00
|
|
|
|
|
|
|
/* HW Generic timers configuration */
|
|
|
|
|
|
|
|
static const struct ath_gen_timer_configuration gen_tmr_configuration[] =
|
|
|
|
{
|
|
|
|
{AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080},
|
|
|
|
{AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080},
|
|
|
|
{AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080},
|
|
|
|
{AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080},
|
|
|
|
{AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080},
|
|
|
|
{AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080},
|
|
|
|
{AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080},
|
|
|
|
{AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080},
|
|
|
|
{AR_NEXT_NDP2_TIMER, AR_NDP2_PERIOD, AR_NDP2_TIMER_MODE, 0x0001},
|
|
|
|
{AR_NEXT_NDP2_TIMER + 1*4, AR_NDP2_PERIOD + 1*4,
|
|
|
|
AR_NDP2_TIMER_MODE, 0x0002},
|
|
|
|
{AR_NEXT_NDP2_TIMER + 2*4, AR_NDP2_PERIOD + 2*4,
|
|
|
|
AR_NDP2_TIMER_MODE, 0x0004},
|
|
|
|
{AR_NEXT_NDP2_TIMER + 3*4, AR_NDP2_PERIOD + 3*4,
|
|
|
|
AR_NDP2_TIMER_MODE, 0x0008},
|
|
|
|
{AR_NEXT_NDP2_TIMER + 4*4, AR_NDP2_PERIOD + 4*4,
|
|
|
|
AR_NDP2_TIMER_MODE, 0x0010},
|
|
|
|
{AR_NEXT_NDP2_TIMER + 5*4, AR_NDP2_PERIOD + 5*4,
|
|
|
|
AR_NDP2_TIMER_MODE, 0x0020},
|
|
|
|
{AR_NEXT_NDP2_TIMER + 6*4, AR_NDP2_PERIOD + 6*4,
|
|
|
|
AR_NDP2_TIMER_MODE, 0x0040},
|
|
|
|
{AR_NEXT_NDP2_TIMER + 7*4, AR_NDP2_PERIOD + 7*4,
|
|
|
|
AR_NDP2_TIMER_MODE, 0x0080}
|
|
|
|
};
|
|
|
|
|
|
|
|
/* HW generic timer primitives */
|
|
|
|
|
|
|
|
/* compute and clear index of rightmost 1 */
|
|
|
|
static u32 rightmost_index(struct ath_gen_timer_table *timer_table, u32 *mask)
|
|
|
|
{
|
|
|
|
u32 b;
|
|
|
|
|
|
|
|
b = *mask;
|
|
|
|
b &= (0-b);
|
|
|
|
*mask &= ~b;
|
|
|
|
b *= debruijn32;
|
|
|
|
b >>= 27;
|
|
|
|
|
|
|
|
return timer_table->gen_timer_index[b];
|
|
|
|
}
|
|
|
|
|
2011-03-23 04:54:17 +08:00
|
|
|
u32 ath9k_hw_gettsf32(struct ath_hw *ah)
|
2009-08-26 23:38:49 +08:00
|
|
|
{
|
|
|
|
return REG_READ(ah, AR_TSF_L32);
|
|
|
|
}
|
2011-03-23 04:54:17 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_gettsf32);
|
2009-08-26 23:38:49 +08:00
|
|
|
|
|
|
|
struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah,
|
|
|
|
void (*trigger)(void *),
|
|
|
|
void (*overflow)(void *),
|
|
|
|
void *arg,
|
|
|
|
u8 timer_index)
|
|
|
|
{
|
|
|
|
struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
|
|
|
|
struct ath_gen_timer *timer;
|
|
|
|
|
|
|
|
timer = kzalloc(sizeof(struct ath_gen_timer), GFP_KERNEL);
|
|
|
|
|
|
|
|
if (timer == NULL) {
|
2010-12-03 11:12:36 +08:00
|
|
|
ath_err(ath9k_hw_common(ah),
|
|
|
|
"Failed to allocate memory for hw timer[%d]\n",
|
|
|
|
timer_index);
|
2009-08-26 23:38:49 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* allocate a hardware generic timer slot */
|
|
|
|
timer_table->timers[timer_index] = timer;
|
|
|
|
timer->index = timer_index;
|
|
|
|
timer->trigger = trigger;
|
|
|
|
timer->overflow = overflow;
|
|
|
|
timer->arg = arg;
|
|
|
|
|
|
|
|
return timer;
|
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath_gen_timer_alloc);
|
2009-08-26 23:38:49 +08:00
|
|
|
|
2009-09-13 17:08:34 +08:00
|
|
|
void ath9k_hw_gen_timer_start(struct ath_hw *ah,
|
|
|
|
struct ath_gen_timer *timer,
|
2011-04-21 21:03:27 +08:00
|
|
|
u32 trig_timeout,
|
2009-09-13 17:08:34 +08:00
|
|
|
u32 timer_period)
|
2009-08-26 23:38:49 +08:00
|
|
|
{
|
|
|
|
struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
|
2011-04-21 21:03:27 +08:00
|
|
|
u32 tsf, timer_next;
|
2009-08-26 23:38:49 +08:00
|
|
|
|
|
|
|
BUG_ON(!timer_period);
|
|
|
|
|
|
|
|
set_bit(timer->index, &timer_table->timer_mask.timer_bits);
|
|
|
|
|
|
|
|
tsf = ath9k_hw_gettsf32(ah);
|
|
|
|
|
2011-04-21 21:03:27 +08:00
|
|
|
timer_next = tsf + trig_timeout;
|
|
|
|
|
2010-12-03 11:12:37 +08:00
|
|
|
ath_dbg(ath9k_hw_common(ah), ATH_DBG_HWTIMER,
|
|
|
|
"current tsf %x period %x timer_next %x\n",
|
|
|
|
tsf, timer_period, timer_next);
|
2009-08-26 23:38:49 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Program generic timer registers
|
|
|
|
*/
|
|
|
|
REG_WRITE(ah, gen_tmr_configuration[timer->index].next_addr,
|
|
|
|
timer_next);
|
|
|
|
REG_WRITE(ah, gen_tmr_configuration[timer->index].period_addr,
|
|
|
|
timer_period);
|
|
|
|
REG_SET_BIT(ah, gen_tmr_configuration[timer->index].mode_addr,
|
|
|
|
gen_tmr_configuration[timer->index].mode_mask);
|
|
|
|
|
2011-10-13 13:30:44 +08:00
|
|
|
if (AR_SREV_9462(ah)) {
|
2011-09-14 01:08:18 +08:00
|
|
|
/*
|
2011-10-13 13:30:44 +08:00
|
|
|
* Starting from AR9462, each generic timer can select which tsf
|
2011-09-14 01:08:18 +08:00
|
|
|
* to use. But we still follow the old rule, 0 - 7 use tsf and
|
|
|
|
* 8 - 15 use tsf2.
|
|
|
|
*/
|
|
|
|
if ((timer->index < AR_GEN_TIMER_BANK_1_LEN))
|
|
|
|
REG_CLR_BIT(ah, AR_MAC_PCU_GEN_TIMER_TSF_SEL,
|
|
|
|
(1 << timer->index));
|
|
|
|
else
|
|
|
|
REG_SET_BIT(ah, AR_MAC_PCU_GEN_TIMER_TSF_SEL,
|
|
|
|
(1 << timer->index));
|
|
|
|
}
|
|
|
|
|
2009-08-26 23:38:49 +08:00
|
|
|
/* Enable both trigger and thresh interrupt masks */
|
|
|
|
REG_SET_BIT(ah, AR_IMR_S5,
|
|
|
|
(SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_THRESH) |
|
|
|
|
SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_TRIG)));
|
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_gen_timer_start);
|
2009-08-26 23:38:49 +08:00
|
|
|
|
2009-09-13 17:08:34 +08:00
|
|
|
void ath9k_hw_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer)
|
2009-08-26 23:38:49 +08:00
|
|
|
{
|
|
|
|
struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
|
|
|
|
|
|
|
|
if ((timer->index < AR_FIRST_NDP_TIMER) ||
|
|
|
|
(timer->index >= ATH_MAX_GEN_TIMER)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear generic timer enable bits. */
|
|
|
|
REG_CLR_BIT(ah, gen_tmr_configuration[timer->index].mode_addr,
|
|
|
|
gen_tmr_configuration[timer->index].mode_mask);
|
|
|
|
|
|
|
|
/* Disable both trigger and thresh interrupt masks */
|
|
|
|
REG_CLR_BIT(ah, AR_IMR_S5,
|
|
|
|
(SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_THRESH) |
|
|
|
|
SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_TRIG)));
|
|
|
|
|
|
|
|
clear_bit(timer->index, &timer_table->timer_mask.timer_bits);
|
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath9k_hw_gen_timer_stop);
|
2009-08-26 23:38:49 +08:00
|
|
|
|
|
|
|
void ath_gen_timer_free(struct ath_hw *ah, struct ath_gen_timer *timer)
|
|
|
|
{
|
|
|
|
struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
|
|
|
|
|
|
|
|
/* free the hardware generic timer slot */
|
|
|
|
timer_table->timers[timer->index] = NULL;
|
|
|
|
kfree(timer);
|
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath_gen_timer_free);
|
2009-08-26 23:38:49 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Generic Timer Interrupts handling
|
|
|
|
*/
|
|
|
|
void ath_gen_timer_isr(struct ath_hw *ah)
|
|
|
|
{
|
|
|
|
struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
|
|
|
|
struct ath_gen_timer *timer;
|
2009-09-13 17:42:02 +08:00
|
|
|
struct ath_common *common = ath9k_hw_common(ah);
|
2009-08-26 23:38:49 +08:00
|
|
|
u32 trigger_mask, thresh_mask, index;
|
|
|
|
|
|
|
|
/* get hardware generic timer interrupt status */
|
|
|
|
trigger_mask = ah->intr_gen_timer_trigger;
|
|
|
|
thresh_mask = ah->intr_gen_timer_thresh;
|
|
|
|
trigger_mask &= timer_table->timer_mask.val;
|
|
|
|
thresh_mask &= timer_table->timer_mask.val;
|
|
|
|
|
|
|
|
trigger_mask &= ~thresh_mask;
|
|
|
|
|
|
|
|
while (thresh_mask) {
|
|
|
|
index = rightmost_index(timer_table, &thresh_mask);
|
|
|
|
timer = timer_table->timers[index];
|
|
|
|
BUG_ON(!timer);
|
2010-12-03 11:12:37 +08:00
|
|
|
ath_dbg(common, ATH_DBG_HWTIMER,
|
|
|
|
"TSF overflow for Gen timer %d\n", index);
|
2009-08-26 23:38:49 +08:00
|
|
|
timer->overflow(timer->arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (trigger_mask) {
|
|
|
|
index = rightmost_index(timer_table, &trigger_mask);
|
|
|
|
timer = timer_table->timers[index];
|
|
|
|
BUG_ON(!timer);
|
2010-12-03 11:12:37 +08:00
|
|
|
ath_dbg(common, ATH_DBG_HWTIMER,
|
|
|
|
"Gen timer[%d] trigger\n", index);
|
2009-08-26 23:38:49 +08:00
|
|
|
timer->trigger(timer->arg);
|
|
|
|
}
|
|
|
|
}
|
2009-09-24 11:07:00 +08:00
|
|
|
EXPORT_SYMBOL(ath_gen_timer_isr);
|
2009-10-28 00:59:33 +08:00
|
|
|
|
2010-03-17 16:55:23 +08:00
|
|
|
/********/
|
|
|
|
/* HTC */
|
|
|
|
/********/
|
|
|
|
|
|
|
|
void ath9k_hw_htc_resetinit(struct ath_hw *ah)
|
|
|
|
{
|
|
|
|
ah->htc_reset_init = true;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ath9k_hw_htc_resetinit);
|
|
|
|
|
2009-10-28 00:59:33 +08:00
|
|
|
static struct {
|
|
|
|
u32 version;
|
|
|
|
const char * name;
|
|
|
|
} ath_mac_bb_names[] = {
|
|
|
|
/* Devices with external radios */
|
|
|
|
{ AR_SREV_VERSION_5416_PCI, "5416" },
|
|
|
|
{ AR_SREV_VERSION_5416_PCIE, "5418" },
|
|
|
|
{ AR_SREV_VERSION_9100, "9100" },
|
|
|
|
{ AR_SREV_VERSION_9160, "9160" },
|
|
|
|
/* Single-chip solutions */
|
|
|
|
{ AR_SREV_VERSION_9280, "9280" },
|
|
|
|
{ AR_SREV_VERSION_9285, "9285" },
|
2009-10-28 00:59:35 +08:00
|
|
|
{ AR_SREV_VERSION_9287, "9287" },
|
|
|
|
{ AR_SREV_VERSION_9271, "9271" },
|
2010-04-16 05:39:20 +08:00
|
|
|
{ AR_SREV_VERSION_9300, "9300" },
|
2011-06-21 17:23:21 +08:00
|
|
|
{ AR_SREV_VERSION_9330, "9330" },
|
2011-08-26 03:33:48 +08:00
|
|
|
{ AR_SREV_VERSION_9340, "9340" },
|
2011-04-01 19:46:33 +08:00
|
|
|
{ AR_SREV_VERSION_9485, "9485" },
|
2011-10-13 13:30:44 +08:00
|
|
|
{ AR_SREV_VERSION_9462, "9462" },
|
2009-10-28 00:59:33 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/* For devices with external radios */
|
|
|
|
static struct {
|
|
|
|
u16 version;
|
|
|
|
const char * name;
|
|
|
|
} ath_rf_names[] = {
|
|
|
|
{ 0, "5133" },
|
|
|
|
{ AR_RAD5133_SREV_MAJOR, "5133" },
|
|
|
|
{ AR_RAD5122_SREV_MAJOR, "5122" },
|
|
|
|
{ AR_RAD2133_SREV_MAJOR, "2133" },
|
|
|
|
{ AR_RAD2122_SREV_MAJOR, "2122" }
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return the MAC/BB name. "????" is returned if the MAC/BB is unknown.
|
|
|
|
*/
|
2009-10-28 00:59:34 +08:00
|
|
|
static const char *ath9k_hw_mac_bb_name(u32 mac_bb_version)
|
2009-10-28 00:59:33 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i=0; i<ARRAY_SIZE(ath_mac_bb_names); i++) {
|
|
|
|
if (ath_mac_bb_names[i].version == mac_bb_version) {
|
|
|
|
return ath_mac_bb_names[i].name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return "????";
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return the RF name. "????" is returned if the RF is unknown.
|
|
|
|
* Used for devices with external radios.
|
|
|
|
*/
|
2009-10-28 00:59:34 +08:00
|
|
|
static const char *ath9k_hw_rf_name(u16 rf_version)
|
2009-10-28 00:59:33 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i=0; i<ARRAY_SIZE(ath_rf_names); i++) {
|
|
|
|
if (ath_rf_names[i].version == rf_version) {
|
|
|
|
return ath_rf_names[i].name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return "????";
|
|
|
|
}
|
2009-10-28 00:59:34 +08:00
|
|
|
|
|
|
|
void ath9k_hw_name(struct ath_hw *ah, char *hw_name, size_t len)
|
|
|
|
{
|
|
|
|
int used;
|
|
|
|
|
|
|
|
/* chipsets >= AR9280 are single-chip */
|
2010-09-22 18:34:52 +08:00
|
|
|
if (AR_SREV_9280_20_OR_LATER(ah)) {
|
2009-10-28 00:59:34 +08:00
|
|
|
used = snprintf(hw_name, len,
|
|
|
|
"Atheros AR%s Rev:%x",
|
|
|
|
ath9k_hw_mac_bb_name(ah->hw_version.macVersion),
|
|
|
|
ah->hw_version.macRev);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
used = snprintf(hw_name, len,
|
|
|
|
"Atheros AR%s MAC/BB Rev:%x AR%s RF Rev:%x",
|
|
|
|
ath9k_hw_mac_bb_name(ah->hw_version.macVersion),
|
|
|
|
ah->hw_version.macRev,
|
|
|
|
ath9k_hw_rf_name((ah->hw_version.analog5GhzRev &
|
|
|
|
AR_RADIO_SREV_MAJOR)),
|
|
|
|
ah->hw_version.phyRev);
|
|
|
|
}
|
|
|
|
|
|
|
|
hw_name[used] = '\0';
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ath9k_hw_name);
|