mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-23 17:02:23 +07:00
4a2f248f9e
ath5k updates the channel pointer and after that it stops the Rx logic and apply channel to HW. In case of channel switch, such sequence creates a small window when a frame, which is received on the old channel is considered as a frame received on the new one. The most notable consequence of this situation occurs during the switch from 2 GHz band (CCK+OFDM) to the 5GHz band (OFDM-only). Frame received with CCK rate, e.g. beacon received at the 1mbps, causes the following warning: WARNING: at ath5k/base.c:589 ath5k_tasklet_rx+0x318/0x6ec [ath5k]() invalid hw_rix: 1a [..] Call Trace: [<802656a8>] show_stack+0x48/0x70 [<802dd92c>] warn_slowpath_common+0x88/0xbc [<802dd98c>] warn_slowpath_fmt+0x2c/0x38 [<81b51be8>] ath5k_tasklet_rx+0x318/0x6ec [ath5k] [<8028ac64>] tasklet_action+0x8c/0xf0 [<80075804>] __do_softirq+0x180/0x32c [<80196ce8>] irq_exit+0x54/0x70 [<80041848>] ret_from_irq+0x0/0x4 [<80182fdc>] ioread32+0x4/0xc [<81b4c42c>] ath5k_hw_set_sleep_clock+0x2ec/0x474 [ath5k] [<81b4cf28>] ath5k_hw_reset+0x50/0xeb8 [ath5k] [<81b50900>] ath5k_reset+0xd4/0x310 [ath5k] [<81b557e8>] ath5k_config+0x4c/0x104 [ath5k] [<80d01770>] ieee80211_hw_config+0x2f4/0x35c [mac80211] [<80d09aa8>] ieee80211_scan_work+0x2e4/0x414 [mac80211] [<8022c3f4>] process_one_work+0x28c/0x400 [<802df8f8>] worker_thread+0x258/0x3c0 [<801b5710>] kthread+0xe0/0xec [<800418a8>] ret_from_kernel_thread+0x14/0x1c The easiest way to reproduce this warning is to run scan with dualband NIC in noisy environments, when the channel 11 runs multiple APs. In my tests if the APs num >= 12, the warning appears in the first few seconds of scanning. In order to fix this, the Rx disable code moved to a higher level and placed before the channel pointer update. This is also makes the code a bit more symmetrical, since we disable and enable the Rx in the same function. In fact, at the pointer update time new frames should not appear, because interrupt generation at this point should already be disabled. The next patch should address this issue. CC: Jiri Slaby <jirislaby@gmail.com> CC: Nick Kossifidis <mickflemm@gmail.com> CC: Luis R. Rodriguez <mcgrof@do-not-panic.com> Reported-by: Christophe Prevotaux <cprevotaux@nltinc.com> Tested-by: Christophe Prevotaux <cprevotaux@nltinc.com> Tested-by: Eric Bree <ebree@nltinc.com> Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com> Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
1381 lines
37 KiB
C
1381 lines
37 KiB
C
/*
|
|
* Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
|
|
* Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com>
|
|
* Copyright (c) 2007-2008 Luis Rodriguez <mcgrof@winlab.rutgers.edu>
|
|
* Copyright (c) 2007-2008 Pavel Roskin <proski@gnu.org>
|
|
* Copyright (c) 2007-2008 Jiri Slaby <jirislaby@gmail.com>
|
|
*
|
|
* Permission to use, copy, modify, and 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.
|
|
*
|
|
*/
|
|
|
|
/****************************\
|
|
Reset function and helpers
|
|
\****************************/
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
#include <asm/unaligned.h>
|
|
|
|
#include <linux/pci.h> /* To determine if a card is pci-e */
|
|
#include <linux/log2.h>
|
|
#include <linux/platform_device.h>
|
|
#include "ath5k.h"
|
|
#include "reg.h"
|
|
#include "debug.h"
|
|
|
|
|
|
/**
|
|
* DOC: Reset function and helpers
|
|
*
|
|
* Here we implement the main reset routine, used to bring the card
|
|
* to a working state and ready to receive. We also handle routines
|
|
* that don't fit on other places such as clock, sleep and power control
|
|
*/
|
|
|
|
|
|
/******************\
|
|
* Helper functions *
|
|
\******************/
|
|
|
|
/**
|
|
* ath5k_hw_register_timeout() - Poll a register for a flag/field change
|
|
* @ah: The &struct ath5k_hw
|
|
* @reg: The register to read
|
|
* @flag: The flag/field to check on the register
|
|
* @val: The field value we expect (if we check a field)
|
|
* @is_set: Instead of checking if the flag got cleared, check if it got set
|
|
*
|
|
* Some registers contain flags that indicate that an operation is
|
|
* running. We use this function to poll these registers and check
|
|
* if these flags get cleared. We also use it to poll a register
|
|
* field (containing multiple flags) until it gets a specific value.
|
|
*
|
|
* Returns -EAGAIN if we exceeded AR5K_TUNE_REGISTER_TIMEOUT * 15us or 0
|
|
*/
|
|
int
|
|
ath5k_hw_register_timeout(struct ath5k_hw *ah, u32 reg, u32 flag, u32 val,
|
|
bool is_set)
|
|
{
|
|
int i;
|
|
u32 data;
|
|
|
|
for (i = AR5K_TUNE_REGISTER_TIMEOUT; i > 0; i--) {
|
|
data = ath5k_hw_reg_read(ah, reg);
|
|
if (is_set && (data & flag))
|
|
break;
|
|
else if ((data & flag) == val)
|
|
break;
|
|
udelay(15);
|
|
}
|
|
|
|
return (i <= 0) ? -EAGAIN : 0;
|
|
}
|
|
|
|
|
|
/*************************\
|
|
* Clock related functions *
|
|
\*************************/
|
|
|
|
/**
|
|
* ath5k_hw_htoclock() - Translate usec to hw clock units
|
|
* @ah: The &struct ath5k_hw
|
|
* @usec: value in microseconds
|
|
*
|
|
* Translate usecs to hw clock units based on the current
|
|
* hw clock rate.
|
|
*
|
|
* Returns number of clock units
|
|
*/
|
|
unsigned int
|
|
ath5k_hw_htoclock(struct ath5k_hw *ah, unsigned int usec)
|
|
{
|
|
struct ath_common *common = ath5k_hw_common(ah);
|
|
return usec * common->clockrate;
|
|
}
|
|
|
|
/**
|
|
* ath5k_hw_clocktoh() - Translate hw clock units to usec
|
|
* @ah: The &struct ath5k_hw
|
|
* @clock: value in hw clock units
|
|
*
|
|
* Translate hw clock units to usecs based on the current
|
|
* hw clock rate.
|
|
*
|
|
* Returns number of usecs
|
|
*/
|
|
unsigned int
|
|
ath5k_hw_clocktoh(struct ath5k_hw *ah, unsigned int clock)
|
|
{
|
|
struct ath_common *common = ath5k_hw_common(ah);
|
|
return clock / common->clockrate;
|
|
}
|
|
|
|
/**
|
|
* ath5k_hw_init_core_clock() - Initialize core clock
|
|
* @ah: The &struct ath5k_hw
|
|
*
|
|
* Initialize core clock parameters (usec, usec32, latencies etc),
|
|
* based on current bwmode and chipset properties.
|
|
*/
|
|
static void
|
|
ath5k_hw_init_core_clock(struct ath5k_hw *ah)
|
|
{
|
|
struct ieee80211_channel *channel = ah->ah_current_channel;
|
|
struct ath_common *common = ath5k_hw_common(ah);
|
|
u32 usec_reg, txlat, rxlat, usec, clock, sclock, txf2txs;
|
|
|
|
/*
|
|
* Set core clock frequency
|
|
*/
|
|
switch (channel->hw_value) {
|
|
case AR5K_MODE_11A:
|
|
clock = 40;
|
|
break;
|
|
case AR5K_MODE_11B:
|
|
clock = 22;
|
|
break;
|
|
case AR5K_MODE_11G:
|
|
default:
|
|
clock = 44;
|
|
break;
|
|
}
|
|
|
|
/* Use clock multiplier for non-default
|
|
* bwmode */
|
|
switch (ah->ah_bwmode) {
|
|
case AR5K_BWMODE_40MHZ:
|
|
clock *= 2;
|
|
break;
|
|
case AR5K_BWMODE_10MHZ:
|
|
clock /= 2;
|
|
break;
|
|
case AR5K_BWMODE_5MHZ:
|
|
clock /= 4;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
common->clockrate = clock;
|
|
|
|
/*
|
|
* Set USEC parameters
|
|
*/
|
|
/* Set USEC counter on PCU*/
|
|
usec = clock - 1;
|
|
usec = AR5K_REG_SM(usec, AR5K_USEC_1);
|
|
|
|
/* Set usec duration on DCU */
|
|
if (ah->ah_version != AR5K_AR5210)
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_DCU_GBL_IFS_MISC,
|
|
AR5K_DCU_GBL_IFS_MISC_USEC_DUR,
|
|
clock);
|
|
|
|
/* Set 32MHz USEC counter */
|
|
if ((ah->ah_radio == AR5K_RF5112) ||
|
|
(ah->ah_radio == AR5K_RF2413) ||
|
|
(ah->ah_radio == AR5K_RF5413) ||
|
|
(ah->ah_radio == AR5K_RF2316) ||
|
|
(ah->ah_radio == AR5K_RF2317))
|
|
/* Remain on 40MHz clock ? */
|
|
sclock = 40 - 1;
|
|
else
|
|
sclock = 32 - 1;
|
|
sclock = AR5K_REG_SM(sclock, AR5K_USEC_32);
|
|
|
|
/*
|
|
* Set tx/rx latencies
|
|
*/
|
|
usec_reg = ath5k_hw_reg_read(ah, AR5K_USEC_5211);
|
|
txlat = AR5K_REG_MS(usec_reg, AR5K_USEC_TX_LATENCY_5211);
|
|
rxlat = AR5K_REG_MS(usec_reg, AR5K_USEC_RX_LATENCY_5211);
|
|
|
|
/*
|
|
* Set default Tx frame to Tx data start delay
|
|
*/
|
|
txf2txs = AR5K_INIT_TXF2TXD_START_DEFAULT;
|
|
|
|
/*
|
|
* 5210 initvals don't include usec settings
|
|
* so we need to use magic values here for
|
|
* tx/rx latencies
|
|
*/
|
|
if (ah->ah_version == AR5K_AR5210) {
|
|
/* same for turbo */
|
|
txlat = AR5K_INIT_TX_LATENCY_5210;
|
|
rxlat = AR5K_INIT_RX_LATENCY_5210;
|
|
}
|
|
|
|
if (ah->ah_mac_srev < AR5K_SREV_AR5211) {
|
|
/* 5311 has different tx/rx latency masks
|
|
* from 5211, since we deal 5311 the same
|
|
* as 5211 when setting initvals, shift
|
|
* values here to their proper locations
|
|
*
|
|
* Note: Initvals indicate tx/rx/ latencies
|
|
* are the same for turbo mode */
|
|
txlat = AR5K_REG_SM(txlat, AR5K_USEC_TX_LATENCY_5210);
|
|
rxlat = AR5K_REG_SM(rxlat, AR5K_USEC_RX_LATENCY_5210);
|
|
} else
|
|
switch (ah->ah_bwmode) {
|
|
case AR5K_BWMODE_10MHZ:
|
|
txlat = AR5K_REG_SM(txlat * 2,
|
|
AR5K_USEC_TX_LATENCY_5211);
|
|
rxlat = AR5K_REG_SM(AR5K_INIT_RX_LAT_MAX,
|
|
AR5K_USEC_RX_LATENCY_5211);
|
|
txf2txs = AR5K_INIT_TXF2TXD_START_DELAY_10MHZ;
|
|
break;
|
|
case AR5K_BWMODE_5MHZ:
|
|
txlat = AR5K_REG_SM(txlat * 4,
|
|
AR5K_USEC_TX_LATENCY_5211);
|
|
rxlat = AR5K_REG_SM(AR5K_INIT_RX_LAT_MAX,
|
|
AR5K_USEC_RX_LATENCY_5211);
|
|
txf2txs = AR5K_INIT_TXF2TXD_START_DELAY_5MHZ;
|
|
break;
|
|
case AR5K_BWMODE_40MHZ:
|
|
txlat = AR5K_INIT_TX_LAT_MIN;
|
|
rxlat = AR5K_REG_SM(rxlat / 2,
|
|
AR5K_USEC_RX_LATENCY_5211);
|
|
txf2txs = AR5K_INIT_TXF2TXD_START_DEFAULT;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
usec_reg = (usec | sclock | txlat | rxlat);
|
|
ath5k_hw_reg_write(ah, usec_reg, AR5K_USEC);
|
|
|
|
/* On 5112 set tx frame to tx data start delay */
|
|
if (ah->ah_radio == AR5K_RF5112) {
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_RF_CTL2,
|
|
AR5K_PHY_RF_CTL2_TXF2TXD_START,
|
|
txf2txs);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ath5k_hw_set_sleep_clock() - Setup sleep clock operation
|
|
* @ah: The &struct ath5k_hw
|
|
* @enable: Enable sleep clock operation (false to disable)
|
|
*
|
|
* If there is an external 32KHz crystal available, use it
|
|
* as ref. clock instead of 32/40MHz clock and baseband clocks
|
|
* to save power during sleep or restore normal 32/40MHz
|
|
* operation.
|
|
*
|
|
* NOTE: When operating on 32KHz certain PHY registers (27 - 31,
|
|
* 123 - 127) require delay on access.
|
|
*/
|
|
static void
|
|
ath5k_hw_set_sleep_clock(struct ath5k_hw *ah, bool enable)
|
|
{
|
|
struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
|
|
u32 scal, spending, sclock;
|
|
|
|
/* Only set 32KHz settings if we have an external
|
|
* 32KHz crystal present */
|
|
if ((AR5K_EEPROM_HAS32KHZCRYSTAL(ee->ee_misc1) ||
|
|
AR5K_EEPROM_HAS32KHZCRYSTAL_OLD(ee->ee_misc1)) &&
|
|
enable) {
|
|
|
|
/* 1 usec/cycle */
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_USEC_5211, AR5K_USEC_32, 1);
|
|
/* Set up tsf increment on each cycle */
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_TSF_PARM, AR5K_TSF_PARM_INC, 61);
|
|
|
|
/* Set baseband sleep control registers
|
|
* and sleep control rate */
|
|
ath5k_hw_reg_write(ah, 0x1f, AR5K_PHY_SCR);
|
|
|
|
if ((ah->ah_radio == AR5K_RF5112) ||
|
|
(ah->ah_radio == AR5K_RF5413) ||
|
|
(ah->ah_radio == AR5K_RF2316) ||
|
|
(ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4)))
|
|
spending = 0x14;
|
|
else
|
|
spending = 0x18;
|
|
ath5k_hw_reg_write(ah, spending, AR5K_PHY_SPENDING);
|
|
|
|
if ((ah->ah_radio == AR5K_RF5112) ||
|
|
(ah->ah_radio == AR5K_RF5413) ||
|
|
(ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))) {
|
|
ath5k_hw_reg_write(ah, 0x26, AR5K_PHY_SLMT);
|
|
ath5k_hw_reg_write(ah, 0x0d, AR5K_PHY_SCAL);
|
|
ath5k_hw_reg_write(ah, 0x07, AR5K_PHY_SCLOCK);
|
|
ath5k_hw_reg_write(ah, 0x3f, AR5K_PHY_SDELAY);
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_PCICFG,
|
|
AR5K_PCICFG_SLEEP_CLOCK_RATE, 0x02);
|
|
} else {
|
|
ath5k_hw_reg_write(ah, 0x0a, AR5K_PHY_SLMT);
|
|
ath5k_hw_reg_write(ah, 0x0c, AR5K_PHY_SCAL);
|
|
ath5k_hw_reg_write(ah, 0x03, AR5K_PHY_SCLOCK);
|
|
ath5k_hw_reg_write(ah, 0x20, AR5K_PHY_SDELAY);
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_PCICFG,
|
|
AR5K_PCICFG_SLEEP_CLOCK_RATE, 0x03);
|
|
}
|
|
|
|
/* Enable sleep clock operation */
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG,
|
|
AR5K_PCICFG_SLEEP_CLOCK_EN);
|
|
|
|
} else {
|
|
|
|
/* Disable sleep clock operation and
|
|
* restore default parameters */
|
|
AR5K_REG_DISABLE_BITS(ah, AR5K_PCICFG,
|
|
AR5K_PCICFG_SLEEP_CLOCK_EN);
|
|
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_PCICFG,
|
|
AR5K_PCICFG_SLEEP_CLOCK_RATE, 0);
|
|
|
|
/* Set DAC/ADC delays */
|
|
ath5k_hw_reg_write(ah, 0x1f, AR5K_PHY_SCR);
|
|
ath5k_hw_reg_write(ah, AR5K_PHY_SLMT_32MHZ, AR5K_PHY_SLMT);
|
|
|
|
if (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))
|
|
scal = AR5K_PHY_SCAL_32MHZ_2417;
|
|
else if (ee->ee_is_hb63)
|
|
scal = AR5K_PHY_SCAL_32MHZ_HB63;
|
|
else
|
|
scal = AR5K_PHY_SCAL_32MHZ;
|
|
ath5k_hw_reg_write(ah, scal, AR5K_PHY_SCAL);
|
|
|
|
ath5k_hw_reg_write(ah, AR5K_PHY_SCLOCK_32MHZ, AR5K_PHY_SCLOCK);
|
|
ath5k_hw_reg_write(ah, AR5K_PHY_SDELAY_32MHZ, AR5K_PHY_SDELAY);
|
|
|
|
if ((ah->ah_radio == AR5K_RF5112) ||
|
|
(ah->ah_radio == AR5K_RF5413) ||
|
|
(ah->ah_radio == AR5K_RF2316) ||
|
|
(ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4)))
|
|
spending = 0x14;
|
|
else
|
|
spending = 0x18;
|
|
ath5k_hw_reg_write(ah, spending, AR5K_PHY_SPENDING);
|
|
|
|
/* Set up tsf increment on each cycle */
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_TSF_PARM, AR5K_TSF_PARM_INC, 1);
|
|
|
|
if ((ah->ah_radio == AR5K_RF5112) ||
|
|
(ah->ah_radio == AR5K_RF5413) ||
|
|
(ah->ah_radio == AR5K_RF2316) ||
|
|
(ah->ah_radio == AR5K_RF2317))
|
|
sclock = 40 - 1;
|
|
else
|
|
sclock = 32 - 1;
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_USEC_5211, AR5K_USEC_32, sclock);
|
|
}
|
|
}
|
|
|
|
|
|
/*********************\
|
|
* Reset/Sleep control *
|
|
\*********************/
|
|
|
|
/**
|
|
* ath5k_hw_nic_reset() - Reset the various chipset units
|
|
* @ah: The &struct ath5k_hw
|
|
* @val: Mask to indicate what units to reset
|
|
*
|
|
* To reset the various chipset units we need to write
|
|
* the mask to AR5K_RESET_CTL and poll the register until
|
|
* all flags are cleared.
|
|
*
|
|
* Returns 0 if we are O.K. or -EAGAIN (from athk5_hw_register_timeout)
|
|
*/
|
|
static int
|
|
ath5k_hw_nic_reset(struct ath5k_hw *ah, u32 val)
|
|
{
|
|
int ret;
|
|
u32 mask = val ? val : ~0U;
|
|
|
|
/* Read-and-clear RX Descriptor Pointer*/
|
|
ath5k_hw_reg_read(ah, AR5K_RXDP);
|
|
|
|
/*
|
|
* Reset the device and wait until success
|
|
*/
|
|
ath5k_hw_reg_write(ah, val, AR5K_RESET_CTL);
|
|
|
|
/* Wait at least 128 PCI clocks */
|
|
usleep_range(15, 20);
|
|
|
|
if (ah->ah_version == AR5K_AR5210) {
|
|
val &= AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_DMA
|
|
| AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_PHY;
|
|
mask &= AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_DMA
|
|
| AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_PHY;
|
|
} else {
|
|
val &= AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_BASEBAND;
|
|
mask &= AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_BASEBAND;
|
|
}
|
|
|
|
ret = ath5k_hw_register_timeout(ah, AR5K_RESET_CTL, mask, val, false);
|
|
|
|
/*
|
|
* Reset configuration register (for hw byte-swap). Note that this
|
|
* is only set for big endian. We do the necessary magic in
|
|
* AR5K_INIT_CFG.
|
|
*/
|
|
if ((val & AR5K_RESET_CTL_PCU) == 0)
|
|
ath5k_hw_reg_write(ah, AR5K_INIT_CFG, AR5K_CFG);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* ath5k_hw_wisoc_reset() - Reset AHB chipset
|
|
* @ah: The &struct ath5k_hw
|
|
* @flags: Mask to indicate what units to reset
|
|
*
|
|
* Same as ath5k_hw_nic_reset but for AHB based devices
|
|
*
|
|
* Returns 0 if we are O.K. or -EAGAIN (from athk5_hw_register_timeout)
|
|
*/
|
|
static int
|
|
ath5k_hw_wisoc_reset(struct ath5k_hw *ah, u32 flags)
|
|
{
|
|
u32 mask = flags ? flags : ~0U;
|
|
u32 __iomem *reg;
|
|
u32 regval;
|
|
u32 val = 0;
|
|
|
|
/* ah->ah_mac_srev is not available at this point yet */
|
|
if (ah->devid >= AR5K_SREV_AR2315_R6) {
|
|
reg = (u32 __iomem *) AR5K_AR2315_RESET;
|
|
if (mask & AR5K_RESET_CTL_PCU)
|
|
val |= AR5K_AR2315_RESET_WMAC;
|
|
if (mask & AR5K_RESET_CTL_BASEBAND)
|
|
val |= AR5K_AR2315_RESET_BB_WARM;
|
|
} else {
|
|
reg = (u32 __iomem *) AR5K_AR5312_RESET;
|
|
if (to_platform_device(ah->dev)->id == 0) {
|
|
if (mask & AR5K_RESET_CTL_PCU)
|
|
val |= AR5K_AR5312_RESET_WMAC0;
|
|
if (mask & AR5K_RESET_CTL_BASEBAND)
|
|
val |= AR5K_AR5312_RESET_BB0_COLD |
|
|
AR5K_AR5312_RESET_BB0_WARM;
|
|
} else {
|
|
if (mask & AR5K_RESET_CTL_PCU)
|
|
val |= AR5K_AR5312_RESET_WMAC1;
|
|
if (mask & AR5K_RESET_CTL_BASEBAND)
|
|
val |= AR5K_AR5312_RESET_BB1_COLD |
|
|
AR5K_AR5312_RESET_BB1_WARM;
|
|
}
|
|
}
|
|
|
|
/* Put BB/MAC into reset */
|
|
regval = ioread32(reg);
|
|
iowrite32(regval | val, reg);
|
|
regval = ioread32(reg);
|
|
udelay(100); /* NB: should be atomic */
|
|
|
|
/* Bring BB/MAC out of reset */
|
|
iowrite32(regval & ~val, reg);
|
|
regval = ioread32(reg);
|
|
|
|
/*
|
|
* Reset configuration register (for hw byte-swap). Note that this
|
|
* is only set for big endian. We do the necessary magic in
|
|
* AR5K_INIT_CFG.
|
|
*/
|
|
if ((flags & AR5K_RESET_CTL_PCU) == 0)
|
|
ath5k_hw_reg_write(ah, AR5K_INIT_CFG, AR5K_CFG);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ath5k_hw_set_power_mode() - Set power mode
|
|
* @ah: The &struct ath5k_hw
|
|
* @mode: One of enum ath5k_power_mode
|
|
* @set_chip: Set to true to write sleep control register
|
|
* @sleep_duration: How much time the device is allowed to sleep
|
|
* when sleep logic is enabled (in 128 microsecond increments).
|
|
*
|
|
* This function is used to configure sleep policy and allowed
|
|
* sleep modes. For more information check out the sleep control
|
|
* register on reg.h and STA_ID1.
|
|
*
|
|
* Returns 0 on success, -EIO if chip didn't wake up or -EINVAL if an invalid
|
|
* mode is requested.
|
|
*/
|
|
static int
|
|
ath5k_hw_set_power_mode(struct ath5k_hw *ah, enum ath5k_power_mode mode,
|
|
bool set_chip, u16 sleep_duration)
|
|
{
|
|
unsigned int i;
|
|
u32 staid, data;
|
|
|
|
staid = ath5k_hw_reg_read(ah, AR5K_STA_ID1);
|
|
|
|
switch (mode) {
|
|
case AR5K_PM_AUTO:
|
|
staid &= ~AR5K_STA_ID1_DEFAULT_ANTENNA;
|
|
/* fallthrough */
|
|
case AR5K_PM_NETWORK_SLEEP:
|
|
if (set_chip)
|
|
ath5k_hw_reg_write(ah,
|
|
AR5K_SLEEP_CTL_SLE_ALLOW |
|
|
sleep_duration,
|
|
AR5K_SLEEP_CTL);
|
|
|
|
staid |= AR5K_STA_ID1_PWR_SV;
|
|
break;
|
|
|
|
case AR5K_PM_FULL_SLEEP:
|
|
if (set_chip)
|
|
ath5k_hw_reg_write(ah, AR5K_SLEEP_CTL_SLE_SLP,
|
|
AR5K_SLEEP_CTL);
|
|
|
|
staid |= AR5K_STA_ID1_PWR_SV;
|
|
break;
|
|
|
|
case AR5K_PM_AWAKE:
|
|
|
|
staid &= ~AR5K_STA_ID1_PWR_SV;
|
|
|
|
if (!set_chip)
|
|
goto commit;
|
|
|
|
data = ath5k_hw_reg_read(ah, AR5K_SLEEP_CTL);
|
|
|
|
/* If card is down we 'll get 0xffff... so we
|
|
* need to clean this up before we write the register
|
|
*/
|
|
if (data & 0xffc00000)
|
|
data = 0;
|
|
else
|
|
/* Preserve sleep duration etc */
|
|
data = data & ~AR5K_SLEEP_CTL_SLE;
|
|
|
|
ath5k_hw_reg_write(ah, data | AR5K_SLEEP_CTL_SLE_WAKE,
|
|
AR5K_SLEEP_CTL);
|
|
usleep_range(15, 20);
|
|
|
|
for (i = 200; i > 0; i--) {
|
|
/* Check if the chip did wake up */
|
|
if ((ath5k_hw_reg_read(ah, AR5K_PCICFG) &
|
|
AR5K_PCICFG_SPWR_DN) == 0)
|
|
break;
|
|
|
|
/* Wait a bit and retry */
|
|
usleep_range(50, 75);
|
|
ath5k_hw_reg_write(ah, data | AR5K_SLEEP_CTL_SLE_WAKE,
|
|
AR5K_SLEEP_CTL);
|
|
}
|
|
|
|
/* Fail if the chip didn't wake up */
|
|
if (i == 0)
|
|
return -EIO;
|
|
|
|
break;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
commit:
|
|
ath5k_hw_reg_write(ah, staid, AR5K_STA_ID1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ath5k_hw_on_hold() - Put device on hold
|
|
* @ah: The &struct ath5k_hw
|
|
*
|
|
* Put MAC and Baseband on warm reset and keep that state
|
|
* (don't clean sleep control register). After this MAC
|
|
* and Baseband are disabled and a full reset is needed
|
|
* to come back. This way we save as much power as possible
|
|
* without putting the card on full sleep.
|
|
*
|
|
* Returns 0 on success or -EIO on error
|
|
*/
|
|
int
|
|
ath5k_hw_on_hold(struct ath5k_hw *ah)
|
|
{
|
|
struct pci_dev *pdev = ah->pdev;
|
|
u32 bus_flags;
|
|
int ret;
|
|
|
|
if (ath5k_get_bus_type(ah) == ATH_AHB)
|
|
return 0;
|
|
|
|
/* Make sure device is awake */
|
|
ret = ath5k_hw_set_power_mode(ah, AR5K_PM_AWAKE, true, 0);
|
|
if (ret) {
|
|
ATH5K_ERR(ah, "failed to wakeup the MAC Chip\n");
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Put chipset on warm reset...
|
|
*
|
|
* Note: putting PCI core on warm reset on PCI-E cards
|
|
* results card to hang and always return 0xffff... so
|
|
* we ignore that flag for PCI-E cards. On PCI cards
|
|
* this flag gets cleared after 64 PCI clocks.
|
|
*/
|
|
bus_flags = (pdev && pci_is_pcie(pdev)) ? 0 : AR5K_RESET_CTL_PCI;
|
|
|
|
if (ah->ah_version == AR5K_AR5210) {
|
|
ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU |
|
|
AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_DMA |
|
|
AR5K_RESET_CTL_PHY | AR5K_RESET_CTL_PCI);
|
|
usleep_range(2000, 2500);
|
|
} else {
|
|
ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU |
|
|
AR5K_RESET_CTL_BASEBAND | bus_flags);
|
|
}
|
|
|
|
if (ret) {
|
|
ATH5K_ERR(ah, "failed to put device on warm reset\n");
|
|
return -EIO;
|
|
}
|
|
|
|
/* ...wakeup again!*/
|
|
ret = ath5k_hw_set_power_mode(ah, AR5K_PM_AWAKE, true, 0);
|
|
if (ret) {
|
|
ATH5K_ERR(ah, "failed to put device on hold\n");
|
|
return ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* ath5k_hw_nic_wakeup() - Force card out of sleep
|
|
* @ah: The &struct ath5k_hw
|
|
* @channel: The &struct ieee80211_channel
|
|
*
|
|
* Bring up MAC + PHY Chips and program PLL
|
|
* NOTE: Channel is NULL for the initial wakeup.
|
|
*
|
|
* Returns 0 on success, -EIO on hw failure or -EINVAL for false channel infos
|
|
*/
|
|
int
|
|
ath5k_hw_nic_wakeup(struct ath5k_hw *ah, struct ieee80211_channel *channel)
|
|
{
|
|
struct pci_dev *pdev = ah->pdev;
|
|
u32 turbo, mode, clock, bus_flags;
|
|
int ret;
|
|
|
|
turbo = 0;
|
|
mode = 0;
|
|
clock = 0;
|
|
|
|
if ((ath5k_get_bus_type(ah) != ATH_AHB) || channel) {
|
|
/* Wakeup the device */
|
|
ret = ath5k_hw_set_power_mode(ah, AR5K_PM_AWAKE, true, 0);
|
|
if (ret) {
|
|
ATH5K_ERR(ah, "failed to wakeup the MAC Chip\n");
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Put chipset on warm reset...
|
|
*
|
|
* Note: putting PCI core on warm reset on PCI-E cards
|
|
* results card to hang and always return 0xffff... so
|
|
* we ignore that flag for PCI-E cards. On PCI cards
|
|
* this flag gets cleared after 64 PCI clocks.
|
|
*/
|
|
bus_flags = (pdev && pci_is_pcie(pdev)) ? 0 : AR5K_RESET_CTL_PCI;
|
|
|
|
if (ah->ah_version == AR5K_AR5210) {
|
|
ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU |
|
|
AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_DMA |
|
|
AR5K_RESET_CTL_PHY | AR5K_RESET_CTL_PCI);
|
|
usleep_range(2000, 2500);
|
|
} else {
|
|
if (ath5k_get_bus_type(ah) == ATH_AHB)
|
|
ret = ath5k_hw_wisoc_reset(ah, AR5K_RESET_CTL_PCU |
|
|
AR5K_RESET_CTL_BASEBAND);
|
|
else
|
|
ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU |
|
|
AR5K_RESET_CTL_BASEBAND | bus_flags);
|
|
}
|
|
|
|
if (ret) {
|
|
ATH5K_ERR(ah, "failed to reset the MAC Chip\n");
|
|
return -EIO;
|
|
}
|
|
|
|
/* ...wakeup again!...*/
|
|
ret = ath5k_hw_set_power_mode(ah, AR5K_PM_AWAKE, true, 0);
|
|
if (ret) {
|
|
ATH5K_ERR(ah, "failed to resume the MAC Chip\n");
|
|
return ret;
|
|
}
|
|
|
|
/* ...reset configuration register on Wisoc ...
|
|
* ...clear reset control register and pull device out of
|
|
* warm reset on others */
|
|
if (ath5k_get_bus_type(ah) == ATH_AHB)
|
|
ret = ath5k_hw_wisoc_reset(ah, 0);
|
|
else
|
|
ret = ath5k_hw_nic_reset(ah, 0);
|
|
|
|
if (ret) {
|
|
ATH5K_ERR(ah, "failed to warm reset the MAC Chip\n");
|
|
return -EIO;
|
|
}
|
|
|
|
/* On initialization skip PLL programming since we don't have
|
|
* a channel / mode set yet */
|
|
if (!channel)
|
|
return 0;
|
|
|
|
if (ah->ah_version != AR5K_AR5210) {
|
|
/*
|
|
* Get channel mode flags
|
|
*/
|
|
|
|
if (ah->ah_radio >= AR5K_RF5112) {
|
|
mode = AR5K_PHY_MODE_RAD_RF5112;
|
|
clock = AR5K_PHY_PLL_RF5112;
|
|
} else {
|
|
mode = AR5K_PHY_MODE_RAD_RF5111; /*Zero*/
|
|
clock = AR5K_PHY_PLL_RF5111; /*Zero*/
|
|
}
|
|
|
|
if (channel->band == IEEE80211_BAND_2GHZ) {
|
|
mode |= AR5K_PHY_MODE_FREQ_2GHZ;
|
|
clock |= AR5K_PHY_PLL_44MHZ;
|
|
|
|
if (channel->hw_value == AR5K_MODE_11B) {
|
|
mode |= AR5K_PHY_MODE_MOD_CCK;
|
|
} else {
|
|
/* XXX Dynamic OFDM/CCK is not supported by the
|
|
* AR5211 so we set MOD_OFDM for plain g (no
|
|
* CCK headers) operation. We need to test
|
|
* this, 5211 might support ofdm-only g after
|
|
* all, there are also initial register values
|
|
* in the code for g mode (see initvals.c).
|
|
*/
|
|
if (ah->ah_version == AR5K_AR5211)
|
|
mode |= AR5K_PHY_MODE_MOD_OFDM;
|
|
else
|
|
mode |= AR5K_PHY_MODE_MOD_DYN;
|
|
}
|
|
} else if (channel->band == IEEE80211_BAND_5GHZ) {
|
|
mode |= (AR5K_PHY_MODE_FREQ_5GHZ |
|
|
AR5K_PHY_MODE_MOD_OFDM);
|
|
|
|
/* Different PLL setting for 5413 */
|
|
if (ah->ah_radio == AR5K_RF5413)
|
|
clock = AR5K_PHY_PLL_40MHZ_5413;
|
|
else
|
|
clock |= AR5K_PHY_PLL_40MHZ;
|
|
} else {
|
|
ATH5K_ERR(ah, "invalid radio frequency mode\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*XXX: Can bwmode be used with dynamic mode ?
|
|
* (I don't think it supports 44MHz) */
|
|
/* On 2425 initvals TURBO_SHORT is not present */
|
|
if (ah->ah_bwmode == AR5K_BWMODE_40MHZ) {
|
|
turbo = AR5K_PHY_TURBO_MODE;
|
|
if (ah->ah_radio != AR5K_RF2425)
|
|
turbo |= AR5K_PHY_TURBO_SHORT;
|
|
} else if (ah->ah_bwmode != AR5K_BWMODE_DEFAULT) {
|
|
if (ah->ah_radio == AR5K_RF5413) {
|
|
mode |= (ah->ah_bwmode == AR5K_BWMODE_10MHZ) ?
|
|
AR5K_PHY_MODE_HALF_RATE :
|
|
AR5K_PHY_MODE_QUARTER_RATE;
|
|
} else if (ah->ah_version == AR5K_AR5212) {
|
|
clock |= (ah->ah_bwmode == AR5K_BWMODE_10MHZ) ?
|
|
AR5K_PHY_PLL_HALF_RATE :
|
|
AR5K_PHY_PLL_QUARTER_RATE;
|
|
}
|
|
}
|
|
|
|
} else { /* Reset the device */
|
|
|
|
/* ...enable Atheros turbo mode if requested */
|
|
if (ah->ah_bwmode == AR5K_BWMODE_40MHZ)
|
|
ath5k_hw_reg_write(ah, AR5K_PHY_TURBO_MODE,
|
|
AR5K_PHY_TURBO);
|
|
}
|
|
|
|
if (ah->ah_version != AR5K_AR5210) {
|
|
|
|
/* ...update PLL if needed */
|
|
if (ath5k_hw_reg_read(ah, AR5K_PHY_PLL) != clock) {
|
|
ath5k_hw_reg_write(ah, clock, AR5K_PHY_PLL);
|
|
usleep_range(300, 350);
|
|
}
|
|
|
|
/* ...set the PHY operating mode */
|
|
ath5k_hw_reg_write(ah, mode, AR5K_PHY_MODE);
|
|
ath5k_hw_reg_write(ah, turbo, AR5K_PHY_TURBO);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**************************************\
|
|
* Post-initvals register modifications *
|
|
\**************************************/
|
|
|
|
/**
|
|
* ath5k_hw_tweak_initval_settings() - Tweak initial settings
|
|
* @ah: The &struct ath5k_hw
|
|
* @channel: The &struct ieee80211_channel
|
|
*
|
|
* Some settings are not handled on initvals, e.g. bwmode
|
|
* settings, some phy settings, workarounds etc that in general
|
|
* don't fit anywhere else or are too small to introduce a separate
|
|
* function for each one. So we have this function to handle
|
|
* them all during reset and complete card's initialization.
|
|
*/
|
|
static void
|
|
ath5k_hw_tweak_initval_settings(struct ath5k_hw *ah,
|
|
struct ieee80211_channel *channel)
|
|
{
|
|
if (ah->ah_version == AR5K_AR5212 &&
|
|
ah->ah_phy_revision >= AR5K_SREV_PHY_5212A) {
|
|
|
|
/* Setup ADC control */
|
|
ath5k_hw_reg_write(ah,
|
|
(AR5K_REG_SM(2,
|
|
AR5K_PHY_ADC_CTL_INBUFGAIN_OFF) |
|
|
AR5K_REG_SM(2,
|
|
AR5K_PHY_ADC_CTL_INBUFGAIN_ON) |
|
|
AR5K_PHY_ADC_CTL_PWD_DAC_OFF |
|
|
AR5K_PHY_ADC_CTL_PWD_ADC_OFF),
|
|
AR5K_PHY_ADC_CTL);
|
|
|
|
|
|
|
|
/* Disable barker RSSI threshold */
|
|
AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_DAG_CCK_CTL,
|
|
AR5K_PHY_DAG_CCK_CTL_EN_RSSI_THR);
|
|
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DAG_CCK_CTL,
|
|
AR5K_PHY_DAG_CCK_CTL_RSSI_THR, 2);
|
|
|
|
/* Set the mute mask */
|
|
ath5k_hw_reg_write(ah, 0x0000000f, AR5K_SEQ_MASK);
|
|
}
|
|
|
|
/* Clear PHY_BLUETOOTH to allow RX_CLEAR line debug */
|
|
if (ah->ah_phy_revision >= AR5K_SREV_PHY_5212B)
|
|
ath5k_hw_reg_write(ah, 0, AR5K_PHY_BLUETOOTH);
|
|
|
|
/* Enable DCU double buffering */
|
|
if (ah->ah_phy_revision > AR5K_SREV_PHY_5212B)
|
|
AR5K_REG_DISABLE_BITS(ah, AR5K_TXCFG,
|
|
AR5K_TXCFG_DCU_DBL_BUF_DIS);
|
|
|
|
/* Set fast ADC */
|
|
if ((ah->ah_radio == AR5K_RF5413) ||
|
|
(ah->ah_radio == AR5K_RF2317) ||
|
|
(ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))) {
|
|
u32 fast_adc = true;
|
|
|
|
if (channel->center_freq == 2462 ||
|
|
channel->center_freq == 2467)
|
|
fast_adc = 0;
|
|
|
|
/* Only update if needed */
|
|
if (ath5k_hw_reg_read(ah, AR5K_PHY_FAST_ADC) != fast_adc)
|
|
ath5k_hw_reg_write(ah, fast_adc,
|
|
AR5K_PHY_FAST_ADC);
|
|
}
|
|
|
|
/* Fix for first revision of the RF5112 RF chipset */
|
|
if (ah->ah_radio == AR5K_RF5112 &&
|
|
ah->ah_radio_5ghz_revision <
|
|
AR5K_SREV_RAD_5112A) {
|
|
u32 data;
|
|
ath5k_hw_reg_write(ah, AR5K_PHY_CCKTXCTL_WORLD,
|
|
AR5K_PHY_CCKTXCTL);
|
|
if (channel->band == IEEE80211_BAND_5GHZ)
|
|
data = 0xffb81020;
|
|
else
|
|
data = 0xffb80d20;
|
|
ath5k_hw_reg_write(ah, data, AR5K_PHY_FRAME_CTL);
|
|
}
|
|
|
|
if (ah->ah_mac_srev < AR5K_SREV_AR5211) {
|
|
/* Clear QCU/DCU clock gating register */
|
|
ath5k_hw_reg_write(ah, 0, AR5K_QCUDCU_CLKGT);
|
|
/* Set DAC/ADC delays */
|
|
ath5k_hw_reg_write(ah, AR5K_PHY_SCAL_32MHZ_5311,
|
|
AR5K_PHY_SCAL);
|
|
/* Enable PCU FIFO corruption ECO */
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5211,
|
|
AR5K_DIAG_SW_ECO_ENABLE);
|
|
}
|
|
|
|
if (ah->ah_bwmode) {
|
|
/* Increase PHY switch and AGC settling time
|
|
* on turbo mode (ath5k_hw_commit_eeprom_settings
|
|
* will override settling time if available) */
|
|
if (ah->ah_bwmode == AR5K_BWMODE_40MHZ) {
|
|
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SETTLING,
|
|
AR5K_PHY_SETTLING_AGC,
|
|
AR5K_AGC_SETTLING_TURBO);
|
|
|
|
/* XXX: Initvals indicate we only increase
|
|
* switch time on AR5212, 5211 and 5210
|
|
* only change agc time (bug?) */
|
|
if (ah->ah_version == AR5K_AR5212)
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SETTLING,
|
|
AR5K_PHY_SETTLING_SWITCH,
|
|
AR5K_SWITCH_SETTLING_TURBO);
|
|
|
|
if (ah->ah_version == AR5K_AR5210) {
|
|
/* Set Frame Control Register */
|
|
ath5k_hw_reg_write(ah,
|
|
(AR5K_PHY_FRAME_CTL_INI |
|
|
AR5K_PHY_TURBO_MODE |
|
|
AR5K_PHY_TURBO_SHORT | 0x2020),
|
|
AR5K_PHY_FRAME_CTL_5210);
|
|
}
|
|
/* On 5413 PHY force window length for half/quarter rate*/
|
|
} else if ((ah->ah_mac_srev >= AR5K_SREV_AR5424) &&
|
|
(ah->ah_mac_srev <= AR5K_SREV_AR5414)) {
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_FRAME_CTL_5211,
|
|
AR5K_PHY_FRAME_CTL_WIN_LEN,
|
|
3);
|
|
}
|
|
} else if (ah->ah_version == AR5K_AR5210) {
|
|
/* Set Frame Control Register for normal operation */
|
|
ath5k_hw_reg_write(ah, (AR5K_PHY_FRAME_CTL_INI | 0x1020),
|
|
AR5K_PHY_FRAME_CTL_5210);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ath5k_hw_commit_eeprom_settings() - Commit settings from EEPROM
|
|
* @ah: The &struct ath5k_hw
|
|
* @channel: The &struct ieee80211_channel
|
|
*
|
|
* Use settings stored on EEPROM to properly initialize the card
|
|
* based on various infos and per-mode calibration data.
|
|
*/
|
|
static void
|
|
ath5k_hw_commit_eeprom_settings(struct ath5k_hw *ah,
|
|
struct ieee80211_channel *channel)
|
|
{
|
|
struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
|
|
s16 cck_ofdm_pwr_delta;
|
|
u8 ee_mode;
|
|
|
|
/* TODO: Add support for AR5210 EEPROM */
|
|
if (ah->ah_version == AR5K_AR5210)
|
|
return;
|
|
|
|
ee_mode = ath5k_eeprom_mode_from_channel(ah, channel);
|
|
|
|
/* Adjust power delta for channel 14 */
|
|
if (channel->center_freq == 2484)
|
|
cck_ofdm_pwr_delta =
|
|
((ee->ee_cck_ofdm_power_delta -
|
|
ee->ee_scaled_cck_delta) * 2) / 10;
|
|
else
|
|
cck_ofdm_pwr_delta =
|
|
(ee->ee_cck_ofdm_power_delta * 2) / 10;
|
|
|
|
/* Set CCK to OFDM power delta on tx power
|
|
* adjustment register */
|
|
if (ah->ah_phy_revision >= AR5K_SREV_PHY_5212A) {
|
|
if (channel->hw_value == AR5K_MODE_11G)
|
|
ath5k_hw_reg_write(ah,
|
|
AR5K_REG_SM((ee->ee_cck_ofdm_gain_delta * -1),
|
|
AR5K_PHY_TX_PWR_ADJ_CCK_GAIN_DELTA) |
|
|
AR5K_REG_SM((cck_ofdm_pwr_delta * -1),
|
|
AR5K_PHY_TX_PWR_ADJ_CCK_PCDAC_INDEX),
|
|
AR5K_PHY_TX_PWR_ADJ);
|
|
else
|
|
ath5k_hw_reg_write(ah, 0, AR5K_PHY_TX_PWR_ADJ);
|
|
} else {
|
|
/* For older revs we scale power on sw during tx power
|
|
* setup */
|
|
ah->ah_txpower.txp_cck_ofdm_pwr_delta = cck_ofdm_pwr_delta;
|
|
ah->ah_txpower.txp_cck_ofdm_gainf_delta =
|
|
ee->ee_cck_ofdm_gain_delta;
|
|
}
|
|
|
|
/* XXX: necessary here? is called from ath5k_hw_set_antenna_mode()
|
|
* too */
|
|
ath5k_hw_set_antenna_switch(ah, ee_mode);
|
|
|
|
/* Noise floor threshold */
|
|
ath5k_hw_reg_write(ah,
|
|
AR5K_PHY_NF_SVAL(ee->ee_noise_floor_thr[ee_mode]),
|
|
AR5K_PHY_NFTHRES);
|
|
|
|
if ((ah->ah_bwmode == AR5K_BWMODE_40MHZ) &&
|
|
(ah->ah_ee_version >= AR5K_EEPROM_VERSION_5_0)) {
|
|
/* Switch settling time (Turbo) */
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SETTLING,
|
|
AR5K_PHY_SETTLING_SWITCH,
|
|
ee->ee_switch_settling_turbo[ee_mode]);
|
|
|
|
/* Tx/Rx attenuation (Turbo) */
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_GAIN,
|
|
AR5K_PHY_GAIN_TXRX_ATTEN,
|
|
ee->ee_atn_tx_rx_turbo[ee_mode]);
|
|
|
|
/* ADC/PGA desired size (Turbo) */
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DESIRED_SIZE,
|
|
AR5K_PHY_DESIRED_SIZE_ADC,
|
|
ee->ee_adc_desired_size_turbo[ee_mode]);
|
|
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DESIRED_SIZE,
|
|
AR5K_PHY_DESIRED_SIZE_PGA,
|
|
ee->ee_pga_desired_size_turbo[ee_mode]);
|
|
|
|
/* Tx/Rx margin (Turbo) */
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_GAIN_2GHZ,
|
|
AR5K_PHY_GAIN_2GHZ_MARGIN_TXRX,
|
|
ee->ee_margin_tx_rx_turbo[ee_mode]);
|
|
|
|
} else {
|
|
/* Switch settling time */
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SETTLING,
|
|
AR5K_PHY_SETTLING_SWITCH,
|
|
ee->ee_switch_settling[ee_mode]);
|
|
|
|
/* Tx/Rx attenuation */
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_GAIN,
|
|
AR5K_PHY_GAIN_TXRX_ATTEN,
|
|
ee->ee_atn_tx_rx[ee_mode]);
|
|
|
|
/* ADC/PGA desired size */
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DESIRED_SIZE,
|
|
AR5K_PHY_DESIRED_SIZE_ADC,
|
|
ee->ee_adc_desired_size[ee_mode]);
|
|
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DESIRED_SIZE,
|
|
AR5K_PHY_DESIRED_SIZE_PGA,
|
|
ee->ee_pga_desired_size[ee_mode]);
|
|
|
|
/* Tx/Rx margin */
|
|
if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1)
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_GAIN_2GHZ,
|
|
AR5K_PHY_GAIN_2GHZ_MARGIN_TXRX,
|
|
ee->ee_margin_tx_rx[ee_mode]);
|
|
}
|
|
|
|
/* XPA delays */
|
|
ath5k_hw_reg_write(ah,
|
|
(ee->ee_tx_end2xpa_disable[ee_mode] << 24) |
|
|
(ee->ee_tx_end2xpa_disable[ee_mode] << 16) |
|
|
(ee->ee_tx_frm2xpa_enable[ee_mode] << 8) |
|
|
(ee->ee_tx_frm2xpa_enable[ee_mode]), AR5K_PHY_RF_CTL4);
|
|
|
|
/* XLNA delay */
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_RF_CTL3,
|
|
AR5K_PHY_RF_CTL3_TXE2XLNA_ON,
|
|
ee->ee_tx_end2xlna_enable[ee_mode]);
|
|
|
|
/* Thresh64 (ANI) */
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_NF,
|
|
AR5K_PHY_NF_THRESH62,
|
|
ee->ee_thr_62[ee_mode]);
|
|
|
|
/* False detect backoff for channels
|
|
* that have spur noise. Write the new
|
|
* cyclic power RSSI threshold. */
|
|
if (ath5k_hw_chan_has_spur_noise(ah, channel))
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_OFDM_SELFCORR,
|
|
AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1,
|
|
AR5K_INIT_CYCRSSI_THR1 +
|
|
ee->ee_false_detect[ee_mode]);
|
|
else
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_OFDM_SELFCORR,
|
|
AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1,
|
|
AR5K_INIT_CYCRSSI_THR1);
|
|
|
|
/* I/Q correction (set enable bit last to match HAL sources) */
|
|
/* TODO: Per channel i/q infos ? */
|
|
if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) {
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_Q_I_COFF,
|
|
ee->ee_i_cal[ee_mode]);
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_Q_Q_COFF,
|
|
ee->ee_q_cal[ee_mode]);
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_ENABLE);
|
|
}
|
|
|
|
/* Heavy clipping -disable for now */
|
|
if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_5_1)
|
|
ath5k_hw_reg_write(ah, 0, AR5K_PHY_HEAVY_CLIP_ENABLE);
|
|
}
|
|
|
|
|
|
/*********************\
|
|
* Main reset function *
|
|
\*********************/
|
|
|
|
/**
|
|
* ath5k_hw_reset() - The main reset function
|
|
* @ah: The &struct ath5k_hw
|
|
* @op_mode: One of enum nl80211_iftype
|
|
* @channel: The &struct ieee80211_channel
|
|
* @fast: Enable fast channel switching
|
|
* @skip_pcu: Skip pcu initialization
|
|
*
|
|
* This is the function we call each time we want to (re)initialize the
|
|
* card and pass new settings to hw. We also call it when hw runs into
|
|
* trouble to make it come back to a working state.
|
|
*
|
|
* Returns 0 on success, -EINVAL on false op_mode or channel infos, or -EIO
|
|
* on failure.
|
|
*/
|
|
int
|
|
ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
|
|
struct ieee80211_channel *channel, bool fast, bool skip_pcu)
|
|
{
|
|
u32 s_seq[10], s_led[3], tsf_up, tsf_lo;
|
|
u8 mode;
|
|
int i, ret;
|
|
|
|
tsf_up = 0;
|
|
tsf_lo = 0;
|
|
mode = 0;
|
|
|
|
/*
|
|
* Sanity check for fast flag
|
|
* Fast channel change only available
|
|
* on AR2413/AR5413.
|
|
*/
|
|
if (fast && (ah->ah_radio != AR5K_RF2413) &&
|
|
(ah->ah_radio != AR5K_RF5413))
|
|
fast = false;
|
|
|
|
/* Disable sleep clock operation
|
|
* to avoid register access delay on certain
|
|
* PHY registers */
|
|
if (ah->ah_version == AR5K_AR5212)
|
|
ath5k_hw_set_sleep_clock(ah, false);
|
|
|
|
mode = channel->hw_value;
|
|
switch (mode) {
|
|
case AR5K_MODE_11A:
|
|
break;
|
|
case AR5K_MODE_11G:
|
|
if (ah->ah_version <= AR5K_AR5211) {
|
|
ATH5K_ERR(ah,
|
|
"G mode not available on 5210/5211");
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
case AR5K_MODE_11B:
|
|
if (ah->ah_version < AR5K_AR5211) {
|
|
ATH5K_ERR(ah,
|
|
"B mode not available on 5210");
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
default:
|
|
ATH5K_ERR(ah,
|
|
"invalid channel: %d\n", channel->center_freq);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* If driver requested fast channel change and DMA has stopped
|
|
* go on. If it fails continue with a normal reset.
|
|
*/
|
|
if (fast) {
|
|
ret = ath5k_hw_phy_init(ah, channel, mode, true);
|
|
if (ret) {
|
|
ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
|
|
"fast chan change failed, falling back to normal reset\n");
|
|
/* Non fatal, can happen eg.
|
|
* on mode change */
|
|
ret = 0;
|
|
} else {
|
|
ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
|
|
"fast chan change successful\n");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Save some registers before a reset
|
|
*/
|
|
if (ah->ah_version != AR5K_AR5210) {
|
|
/*
|
|
* Save frame sequence count
|
|
* For revs. after Oahu, only save
|
|
* seq num for DCU 0 (Global seq num)
|
|
*/
|
|
if (ah->ah_mac_srev < AR5K_SREV_AR5211) {
|
|
|
|
for (i = 0; i < 10; i++)
|
|
s_seq[i] = ath5k_hw_reg_read(ah,
|
|
AR5K_QUEUE_DCU_SEQNUM(i));
|
|
|
|
} else {
|
|
s_seq[0] = ath5k_hw_reg_read(ah,
|
|
AR5K_QUEUE_DCU_SEQNUM(0));
|
|
}
|
|
|
|
/* TSF accelerates on AR5211 during reset
|
|
* As a workaround save it here and restore
|
|
* it later so that it's back in time after
|
|
* reset. This way it'll get re-synced on the
|
|
* next beacon without breaking ad-hoc.
|
|
*
|
|
* On AR5212 TSF is almost preserved across a
|
|
* reset so it stays back in time anyway and
|
|
* we don't have to save/restore it.
|
|
*
|
|
* XXX: Since this breaks power saving we have
|
|
* to disable power saving until we receive the
|
|
* next beacon, so we can resync beacon timers */
|
|
if (ah->ah_version == AR5K_AR5211) {
|
|
tsf_up = ath5k_hw_reg_read(ah, AR5K_TSF_U32);
|
|
tsf_lo = ath5k_hw_reg_read(ah, AR5K_TSF_L32);
|
|
}
|
|
}
|
|
|
|
|
|
/*GPIOs*/
|
|
s_led[0] = ath5k_hw_reg_read(ah, AR5K_PCICFG) &
|
|
AR5K_PCICFG_LEDSTATE;
|
|
s_led[1] = ath5k_hw_reg_read(ah, AR5K_GPIOCR);
|
|
s_led[2] = ath5k_hw_reg_read(ah, AR5K_GPIODO);
|
|
|
|
|
|
/*
|
|
* Since we are going to write rf buffer
|
|
* check if we have any pending gain_F
|
|
* optimization settings
|
|
*/
|
|
if (ah->ah_version == AR5K_AR5212 &&
|
|
(ah->ah_radio <= AR5K_RF5112)) {
|
|
if (!fast && ah->ah_rf_banks != NULL)
|
|
ath5k_hw_gainf_calibrate(ah);
|
|
}
|
|
|
|
/* Wakeup the device */
|
|
ret = ath5k_hw_nic_wakeup(ah, channel);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* PHY access enable */
|
|
if (ah->ah_mac_srev >= AR5K_SREV_AR5211)
|
|
ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0));
|
|
else
|
|
ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ | 0x40,
|
|
AR5K_PHY(0));
|
|
|
|
/* Write initial settings */
|
|
ret = ath5k_hw_write_initvals(ah, mode, skip_pcu);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Initialize core clock settings */
|
|
ath5k_hw_init_core_clock(ah);
|
|
|
|
/*
|
|
* Tweak initval settings for revised
|
|
* chipsets and add some more config
|
|
* bits
|
|
*/
|
|
ath5k_hw_tweak_initval_settings(ah, channel);
|
|
|
|
/* Commit values from EEPROM */
|
|
ath5k_hw_commit_eeprom_settings(ah, channel);
|
|
|
|
|
|
/*
|
|
* Restore saved values
|
|
*/
|
|
|
|
/* Seqnum, TSF */
|
|
if (ah->ah_version != AR5K_AR5210) {
|
|
if (ah->ah_mac_srev < AR5K_SREV_AR5211) {
|
|
for (i = 0; i < 10; i++)
|
|
ath5k_hw_reg_write(ah, s_seq[i],
|
|
AR5K_QUEUE_DCU_SEQNUM(i));
|
|
} else {
|
|
ath5k_hw_reg_write(ah, s_seq[0],
|
|
AR5K_QUEUE_DCU_SEQNUM(0));
|
|
}
|
|
|
|
if (ah->ah_version == AR5K_AR5211) {
|
|
ath5k_hw_reg_write(ah, tsf_up, AR5K_TSF_U32);
|
|
ath5k_hw_reg_write(ah, tsf_lo, AR5K_TSF_L32);
|
|
}
|
|
}
|
|
|
|
/* Ledstate */
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, s_led[0]);
|
|
|
|
/* Gpio settings */
|
|
ath5k_hw_reg_write(ah, s_led[1], AR5K_GPIOCR);
|
|
ath5k_hw_reg_write(ah, s_led[2], AR5K_GPIODO);
|
|
|
|
/*
|
|
* Initialize PCU
|
|
*/
|
|
ath5k_hw_pcu_init(ah, op_mode);
|
|
|
|
/*
|
|
* Initialize PHY
|
|
*/
|
|
ret = ath5k_hw_phy_init(ah, channel, mode, false);
|
|
if (ret) {
|
|
ATH5K_ERR(ah,
|
|
"failed to initialize PHY (%i) !\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Configure QCUs/DCUs
|
|
*/
|
|
ret = ath5k_hw_init_queues(ah);
|
|
if (ret)
|
|
return ret;
|
|
|
|
|
|
/*
|
|
* Initialize DMA/Interrupts
|
|
*/
|
|
ath5k_hw_dma_init(ah);
|
|
|
|
|
|
/*
|
|
* Enable 32KHz clock function for AR5212+ chips
|
|
* Set clocks to 32KHz operation and use an
|
|
* external 32KHz crystal when sleeping if one
|
|
* exists.
|
|
* Disabled by default because it is also disabled in
|
|
* other drivers and it is known to cause stability
|
|
* issues on some devices
|
|
*/
|
|
if (ah->ah_use_32khz_clock && ah->ah_version == AR5K_AR5212 &&
|
|
op_mode != NL80211_IFTYPE_AP)
|
|
ath5k_hw_set_sleep_clock(ah, true);
|
|
|
|
/*
|
|
* Disable beacons and reset the TSF
|
|
*/
|
|
AR5K_REG_DISABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE);
|
|
ath5k_hw_reset_tsf(ah);
|
|
return 0;
|
|
}
|