281c56dd27
when setting up the tx descriptors for the hardware we must account for any padding between the header and the data we might have added previously. frame len is the length of the frame in the air (including FCS but no padding) and buffer len is the length of the buffer (including padding, but without FCS). changing the way ah_setup_tx_desc is called: now excluding the FCS, since it's easier to add that in the function where we need it. before this fix we sent trailing zero bytes after the packet (because frame len included the padding) which was not a big problem without WEP, but with WEP this resultes in a wrong WEP checksum and the packet is discarded - which is how i noticed at all ;) an easy way to run into header padding problems, btw, is to connect to a QoS (WME) enabled access point (eg. madwifi) - QoS data frames are 2 byte longer and will require padding. this patch applies on top of luis latest patch series from 04.02.2008. drivers/net/wireless/ath5k/base.c: Changes-licensed-under: 3-Clause-BSD drivers/net/wireless/ath5k/hw.c: Changes-licensed-under: ISC Signed-off-by: Bruno Randolf <bruno@thinktube.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
4358 lines
110 KiB
C
4358 lines
110 KiB
C
/*
|
|
* Copyright (c) 2004-2007 Reyk Floeter <reyk@openbsd.org>
|
|
* Copyright (c) 2006-2007 Nick Kossifidis <mickflemm@gmail.com>
|
|
* Copyright (c) 2007 Matthew W. S. Bell <mentor@madwifi.org>
|
|
* Copyright (c) 2007 Luis Rodriguez <mcgrof@winlab.rutgers.edu>
|
|
* Copyright (c) 2007 Pavel Roskin <proski@gnu.org>
|
|
* Copyright (c) 2007 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.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* HW related functions for Atheros Wireless LAN devices.
|
|
*/
|
|
|
|
#include <linux/pci.h>
|
|
#include <linux/delay.h>
|
|
|
|
#include "reg.h"
|
|
#include "base.h"
|
|
#include "debug.h"
|
|
|
|
/*Rate tables*/
|
|
static const struct ath5k_rate_table ath5k_rt_11a = AR5K_RATES_11A;
|
|
static const struct ath5k_rate_table ath5k_rt_11b = AR5K_RATES_11B;
|
|
static const struct ath5k_rate_table ath5k_rt_11g = AR5K_RATES_11G;
|
|
static const struct ath5k_rate_table ath5k_rt_turbo = AR5K_RATES_TURBO;
|
|
static const struct ath5k_rate_table ath5k_rt_xr = AR5K_RATES_XR;
|
|
|
|
/*Prototypes*/
|
|
static int ath5k_hw_nic_reset(struct ath5k_hw *, u32);
|
|
static int ath5k_hw_nic_wakeup(struct ath5k_hw *, int, bool);
|
|
static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *, struct ath5k_desc *,
|
|
unsigned int, unsigned int, enum ath5k_pkt_type, unsigned int,
|
|
unsigned int, unsigned int, unsigned int, unsigned int, unsigned int,
|
|
unsigned int, unsigned int);
|
|
static bool ath5k_hw_setup_xr_tx_desc(struct ath5k_hw *, struct ath5k_desc *,
|
|
unsigned int, unsigned int, unsigned int, unsigned int, unsigned int,
|
|
unsigned int);
|
|
static int ath5k_hw_proc_4word_tx_status(struct ath5k_hw *, struct ath5k_desc *);
|
|
static int ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *, struct ath5k_desc *,
|
|
unsigned int, unsigned int, enum ath5k_pkt_type, unsigned int,
|
|
unsigned int, unsigned int, unsigned int, unsigned int, unsigned int,
|
|
unsigned int, unsigned int);
|
|
static int ath5k_hw_proc_2word_tx_status(struct ath5k_hw *, struct ath5k_desc *);
|
|
static int ath5k_hw_proc_new_rx_status(struct ath5k_hw *, struct ath5k_desc *);
|
|
static int ath5k_hw_proc_old_rx_status(struct ath5k_hw *, struct ath5k_desc *);
|
|
static int ath5k_hw_get_capabilities(struct ath5k_hw *);
|
|
|
|
static int ath5k_eeprom_init(struct ath5k_hw *);
|
|
static int ath5k_eeprom_read_mac(struct ath5k_hw *, u8 *);
|
|
|
|
static int ath5k_hw_enable_pspoll(struct ath5k_hw *, u8 *, u16);
|
|
static int ath5k_hw_disable_pspoll(struct ath5k_hw *);
|
|
|
|
/*
|
|
* Enable to overwrite the country code (use "00" for debug)
|
|
*/
|
|
#if 0
|
|
#define COUNTRYCODE "00"
|
|
#endif
|
|
|
|
/*******************\
|
|
General Functions
|
|
\*******************/
|
|
|
|
/*
|
|
* Functions used internaly
|
|
*/
|
|
|
|
static inline unsigned int ath5k_hw_htoclock(unsigned int usec, bool turbo)
|
|
{
|
|
return turbo == true ? (usec * 80) : (usec * 40);
|
|
}
|
|
|
|
static inline unsigned int ath5k_hw_clocktoh(unsigned int clock, bool turbo)
|
|
{
|
|
return turbo == true ? (clock / 80) : (clock / 40);
|
|
}
|
|
|
|
/*
|
|
* Check if a register write has been completed
|
|
*/
|
|
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 == true) && (data & flag))
|
|
break;
|
|
else if ((data & flag) == val)
|
|
break;
|
|
udelay(15);
|
|
}
|
|
|
|
return (i <= 0) ? -EAGAIN : 0;
|
|
}
|
|
|
|
|
|
/***************************************\
|
|
Attach/Detach Functions
|
|
\***************************************/
|
|
|
|
/*
|
|
* Check if the device is supported and initialize the needed structs
|
|
*/
|
|
struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version)
|
|
{
|
|
struct ath5k_hw *ah;
|
|
u8 mac[ETH_ALEN];
|
|
int ret;
|
|
u32 srev;
|
|
|
|
/*If we passed the test malloc a ath5k_hw struct*/
|
|
ah = kzalloc(sizeof(struct ath5k_hw), GFP_KERNEL);
|
|
if (ah == NULL) {
|
|
ret = -ENOMEM;
|
|
ATH5K_ERR(sc, "out of memory\n");
|
|
goto err;
|
|
}
|
|
|
|
ah->ah_sc = sc;
|
|
ah->ah_iobase = sc->iobase;
|
|
|
|
/*
|
|
* HW information
|
|
*/
|
|
|
|
/* Get reg domain from eeprom */
|
|
ath5k_get_regdomain(ah);
|
|
|
|
ah->ah_op_mode = IEEE80211_IF_TYPE_STA;
|
|
ah->ah_radar.r_enabled = AR5K_TUNE_RADAR_ALERT;
|
|
ah->ah_turbo = false;
|
|
ah->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER;
|
|
ah->ah_imr = 0;
|
|
ah->ah_atim_window = 0;
|
|
ah->ah_aifs = AR5K_TUNE_AIFS;
|
|
ah->ah_cw_min = AR5K_TUNE_CWMIN;
|
|
ah->ah_limit_tx_retries = AR5K_INIT_TX_RETRY;
|
|
ah->ah_software_retry = false;
|
|
ah->ah_ant_diversity = AR5K_TUNE_ANT_DIVERSITY;
|
|
|
|
/*
|
|
* Set the mac revision based on the pci id
|
|
*/
|
|
ah->ah_version = mac_version;
|
|
|
|
/*Fill the ath5k_hw struct with the needed functions*/
|
|
if (ah->ah_version == AR5K_AR5212)
|
|
ah->ah_magic = AR5K_EEPROM_MAGIC_5212;
|
|
else if (ah->ah_version == AR5K_AR5211)
|
|
ah->ah_magic = AR5K_EEPROM_MAGIC_5211;
|
|
|
|
if (ah->ah_version == AR5K_AR5212) {
|
|
ah->ah_setup_tx_desc = ath5k_hw_setup_4word_tx_desc;
|
|
ah->ah_setup_xtx_desc = ath5k_hw_setup_xr_tx_desc;
|
|
ah->ah_proc_tx_desc = ath5k_hw_proc_4word_tx_status;
|
|
} else {
|
|
ah->ah_setup_tx_desc = ath5k_hw_setup_2word_tx_desc;
|
|
ah->ah_setup_xtx_desc = ath5k_hw_setup_xr_tx_desc;
|
|
ah->ah_proc_tx_desc = ath5k_hw_proc_2word_tx_status;
|
|
}
|
|
|
|
if (ah->ah_version == AR5K_AR5212)
|
|
ah->ah_proc_rx_desc = ath5k_hw_proc_new_rx_status;
|
|
else if (ah->ah_version <= AR5K_AR5211)
|
|
ah->ah_proc_rx_desc = ath5k_hw_proc_old_rx_status;
|
|
|
|
/* Bring device out of sleep and reset it's units */
|
|
ret = ath5k_hw_nic_wakeup(ah, AR5K_INIT_MODE, true);
|
|
if (ret)
|
|
goto err_free;
|
|
|
|
/* Get MAC, PHY and RADIO revisions */
|
|
srev = ath5k_hw_reg_read(ah, AR5K_SREV);
|
|
ah->ah_mac_srev = srev;
|
|
ah->ah_mac_version = AR5K_REG_MS(srev, AR5K_SREV_VER);
|
|
ah->ah_mac_revision = AR5K_REG_MS(srev, AR5K_SREV_REV);
|
|
ah->ah_phy_revision = ath5k_hw_reg_read(ah, AR5K_PHY_CHIP_ID) &
|
|
0xffffffff;
|
|
ah->ah_radio_5ghz_revision = ath5k_hw_radio_revision(ah,
|
|
CHANNEL_5GHZ);
|
|
|
|
if (ah->ah_version == AR5K_AR5210)
|
|
ah->ah_radio_2ghz_revision = 0;
|
|
else
|
|
ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah,
|
|
CHANNEL_2GHZ);
|
|
|
|
/* Return on unsuported chips (unsupported eeprom etc) */
|
|
if(srev >= AR5K_SREV_VER_AR5416){
|
|
ATH5K_ERR(sc, "Device not yet supported.\n");
|
|
ret = -ENODEV;
|
|
goto err_free;
|
|
}
|
|
|
|
/* Identify single chip solutions */
|
|
if((srev <= AR5K_SREV_VER_AR5414) &&
|
|
(srev >= AR5K_SREV_VER_AR2424)) {
|
|
ah->ah_single_chip = true;
|
|
} else {
|
|
ah->ah_single_chip = false;
|
|
}
|
|
|
|
/* Single chip radio */
|
|
if (ah->ah_radio_2ghz_revision == ah->ah_radio_5ghz_revision)
|
|
ah->ah_radio_2ghz_revision = 0;
|
|
|
|
/* Identify the radio chip*/
|
|
if (ah->ah_version == AR5K_AR5210) {
|
|
ah->ah_radio = AR5K_RF5110;
|
|
} else if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_5112) {
|
|
ah->ah_radio = AR5K_RF5111;
|
|
} else if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_SC1) {
|
|
ah->ah_radio = AR5K_RF5112;
|
|
} else {
|
|
ah->ah_radio = AR5K_RF5413;
|
|
}
|
|
|
|
ah->ah_phy = AR5K_PHY(0);
|
|
|
|
/*
|
|
* Get card capabilities, values, ...
|
|
*/
|
|
|
|
ret = ath5k_eeprom_init(ah);
|
|
if (ret) {
|
|
ATH5K_ERR(sc, "unable to init EEPROM\n");
|
|
goto err_free;
|
|
}
|
|
|
|
/* Get misc capabilities */
|
|
ret = ath5k_hw_get_capabilities(ah);
|
|
if (ret) {
|
|
ATH5K_ERR(sc, "unable to get device capabilities: 0x%04x\n",
|
|
sc->pdev->device);
|
|
goto err_free;
|
|
}
|
|
|
|
/* Get MAC address */
|
|
ret = ath5k_eeprom_read_mac(ah, mac);
|
|
if (ret) {
|
|
ATH5K_ERR(sc, "unable to read address from EEPROM: 0x%04x\n",
|
|
sc->pdev->device);
|
|
goto err_free;
|
|
}
|
|
|
|
ath5k_hw_set_lladdr(ah, mac);
|
|
/* Set BSSID to bcast address: ff:ff:ff:ff:ff:ff for now */
|
|
memset(ah->ah_bssid, 0xff, ETH_ALEN);
|
|
ath5k_hw_set_associd(ah, ah->ah_bssid, 0);
|
|
ath5k_hw_set_opmode(ah);
|
|
|
|
ath5k_hw_set_rfgain_opt(ah);
|
|
|
|
return ah;
|
|
err_free:
|
|
kfree(ah);
|
|
err:
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
/*
|
|
* Bring up MAC + PHY Chips
|
|
*/
|
|
static int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
|
|
{
|
|
u32 turbo, mode, clock;
|
|
int ret;
|
|
|
|
turbo = 0;
|
|
mode = 0;
|
|
clock = 0;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
|
|
/* Wakeup the device */
|
|
ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0);
|
|
if (ret) {
|
|
ATH5K_ERR(ah->ah_sc, "failed to wakeup the MAC Chip\n");
|
|
return ret;
|
|
}
|
|
|
|
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 (flags & CHANNEL_2GHZ) {
|
|
mode |= AR5K_PHY_MODE_FREQ_2GHZ;
|
|
clock |= AR5K_PHY_PLL_44MHZ;
|
|
|
|
if (flags & CHANNEL_CCK) {
|
|
mode |= AR5K_PHY_MODE_MOD_CCK;
|
|
} else if (flags & CHANNEL_OFDM) {
|
|
/* 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 {
|
|
ATH5K_ERR(ah->ah_sc,
|
|
"invalid radio modulation mode\n");
|
|
return -EINVAL;
|
|
}
|
|
} else if (flags & CHANNEL_5GHZ) {
|
|
mode |= AR5K_PHY_MODE_FREQ_5GHZ;
|
|
clock |= AR5K_PHY_PLL_40MHZ;
|
|
|
|
if (flags & CHANNEL_OFDM)
|
|
mode |= AR5K_PHY_MODE_MOD_OFDM;
|
|
else {
|
|
ATH5K_ERR(ah->ah_sc,
|
|
"invalid radio modulation mode\n");
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
ATH5K_ERR(ah->ah_sc, "invalid radio frequency mode\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (flags & CHANNEL_TURBO)
|
|
turbo = AR5K_PHY_TURBO_MODE | AR5K_PHY_TURBO_SHORT;
|
|
} else { /* Reset the device */
|
|
|
|
/* ...enable Atheros turbo mode if requested */
|
|
if (flags & CHANNEL_TURBO)
|
|
ath5k_hw_reg_write(ah, AR5K_PHY_TURBO_MODE,
|
|
AR5K_PHY_TURBO);
|
|
}
|
|
|
|
/* ...reset chipset and PCI device */
|
|
if (ah->ah_single_chip == false && ath5k_hw_nic_reset(ah,
|
|
AR5K_RESET_CTL_CHIP | AR5K_RESET_CTL_PCI)) {
|
|
ATH5K_ERR(ah->ah_sc, "failed to reset the MAC Chip + PCI\n");
|
|
return -EIO;
|
|
}
|
|
|
|
if (ah->ah_version == AR5K_AR5210)
|
|
udelay(2300);
|
|
|
|
/* ...wakeup again!*/
|
|
ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0);
|
|
if (ret) {
|
|
ATH5K_ERR(ah->ah_sc, "failed to resume the MAC Chip\n");
|
|
return ret;
|
|
}
|
|
|
|
/* ...final warm reset */
|
|
if (ath5k_hw_nic_reset(ah, 0)) {
|
|
ATH5K_ERR(ah->ah_sc, "failed to warm reset the MAC Chip\n");
|
|
return -EIO;
|
|
}
|
|
|
|
if (ah->ah_version != AR5K_AR5210) {
|
|
/* ...set the PHY operating mode */
|
|
ath5k_hw_reg_write(ah, clock, AR5K_PHY_PLL);
|
|
udelay(300);
|
|
|
|
ath5k_hw_reg_write(ah, mode, AR5K_PHY_MODE);
|
|
ath5k_hw_reg_write(ah, turbo, AR5K_PHY_TURBO);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Get the rate table for a specific operation mode
|
|
*/
|
|
const struct ath5k_rate_table *ath5k_hw_get_rate_table(struct ath5k_hw *ah,
|
|
unsigned int mode)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
|
|
if (!test_bit(mode, ah->ah_capabilities.cap_mode))
|
|
return NULL;
|
|
|
|
/* Get rate tables */
|
|
switch (mode) {
|
|
case MODE_IEEE80211A:
|
|
return &ath5k_rt_11a;
|
|
case MODE_ATHEROS_TURBO:
|
|
return &ath5k_rt_turbo;
|
|
case MODE_IEEE80211B:
|
|
return &ath5k_rt_11b;
|
|
case MODE_IEEE80211G:
|
|
return &ath5k_rt_11g;
|
|
case MODE_ATHEROS_TURBOG:
|
|
return &ath5k_rt_xr;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Free the ath5k_hw struct
|
|
*/
|
|
void ath5k_hw_detach(struct ath5k_hw *ah)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
|
|
if (ah->ah_rf_banks != NULL)
|
|
kfree(ah->ah_rf_banks);
|
|
|
|
/* assume interrupts are down */
|
|
kfree(ah);
|
|
}
|
|
|
|
/****************************\
|
|
Reset function and helpers
|
|
\****************************/
|
|
|
|
/**
|
|
* ath5k_hw_write_ofdm_timings - set OFDM timings on AR5212
|
|
*
|
|
* @ah: the &struct ath5k_hw
|
|
* @channel: the currently set channel upon reset
|
|
*
|
|
* Write the OFDM timings for the AR5212 upon reset. This is a helper for
|
|
* ath5k_hw_reset(). This seems to tune the PLL a specified frequency
|
|
* depending on the bandwidth of the channel.
|
|
*
|
|
*/
|
|
static inline int ath5k_hw_write_ofdm_timings(struct ath5k_hw *ah,
|
|
struct ieee80211_channel *channel)
|
|
{
|
|
/* Get exponent and mantissa and set it */
|
|
u32 coef_scaled, coef_exp, coef_man,
|
|
ds_coef_exp, ds_coef_man, clock;
|
|
|
|
if (!(ah->ah_version == AR5K_AR5212) ||
|
|
!(channel->val & CHANNEL_OFDM))
|
|
BUG();
|
|
|
|
/* Seems there are two PLLs, one for baseband sampling and one
|
|
* for tuning. Tuning basebands are 40 MHz or 80MHz when in
|
|
* turbo. */
|
|
clock = channel->val & CHANNEL_TURBO ? 80 : 40;
|
|
coef_scaled = ((5 * (clock << 24)) / 2) /
|
|
channel->freq;
|
|
|
|
for (coef_exp = 31; coef_exp > 0; coef_exp--)
|
|
if ((coef_scaled >> coef_exp) & 0x1)
|
|
break;
|
|
|
|
if (!coef_exp)
|
|
return -EINVAL;
|
|
|
|
coef_exp = 14 - (coef_exp - 24);
|
|
coef_man = coef_scaled +
|
|
(1 << (24 - coef_exp - 1));
|
|
ds_coef_man = coef_man >> (24 - coef_exp);
|
|
ds_coef_exp = coef_exp - 16;
|
|
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_3,
|
|
AR5K_PHY_TIMING_3_DSC_MAN, ds_coef_man);
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_3,
|
|
AR5K_PHY_TIMING_3_DSC_EXP, ds_coef_exp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ath5k_hw_write_rate_duration - set rate duration during hw resets
|
|
*
|
|
* @ah: the &struct ath5k_hw
|
|
* @driver_mode: one of enum ieee80211_phymode or our one of our own
|
|
* vendor modes
|
|
*
|
|
* Write the rate duration table for the current mode upon hw reset. This
|
|
* is a helper for ath5k_hw_reset(). It seems all this is doing is setting
|
|
* an ACK timeout for the hardware for the current mode for each rate. The
|
|
* rates which are capable of short preamble (802.11b rates 2Mbps, 5.5Mbps,
|
|
* and 11Mbps) have another register for the short preamble ACK timeout
|
|
* calculation.
|
|
*
|
|
*/
|
|
static inline void ath5k_hw_write_rate_duration(struct ath5k_hw *ah,
|
|
unsigned int driver_mode)
|
|
{
|
|
struct ath5k_softc *sc = ah->ah_sc;
|
|
const struct ath5k_rate_table *rt;
|
|
unsigned int i;
|
|
|
|
/* Get rate table for the current operating mode */
|
|
rt = ath5k_hw_get_rate_table(ah,
|
|
driver_mode);
|
|
|
|
/* Write rate duration table */
|
|
for (i = 0; i < rt->rate_count; i++) {
|
|
const struct ath5k_rate *rate, *control_rate;
|
|
u32 reg;
|
|
u16 tx_time;
|
|
|
|
rate = &rt->rates[i];
|
|
control_rate = &rt->rates[rate->control_rate];
|
|
|
|
/* Set ACK timeout */
|
|
reg = AR5K_RATE_DUR(rate->rate_code);
|
|
|
|
/* An ACK frame consists of 10 bytes. If you add the FCS,
|
|
* which ieee80211_generic_frame_duration() adds,
|
|
* its 14 bytes. Note we use the control rate and not the
|
|
* actual rate for this rate. See mac80211 tx.c
|
|
* ieee80211_duration() for a brief description of
|
|
* what rate we should choose to TX ACKs. */
|
|
tx_time = ieee80211_generic_frame_duration(sc->hw,
|
|
sc->vif, 10, control_rate->rate_kbps/100);
|
|
|
|
ath5k_hw_reg_write(ah, tx_time, reg);
|
|
|
|
if (!HAS_SHPREAMBLE(i))
|
|
continue;
|
|
|
|
/*
|
|
* We're not distinguishing short preamble here,
|
|
* This is true, all we'll get is a longer value here
|
|
* which is not necessarilly bad. We could use
|
|
* export ieee80211_frame_duration() but that needs to be
|
|
* fixed first to be properly used by mac802111 drivers:
|
|
*
|
|
* - remove erp stuff and let the routine figure ofdm
|
|
* erp rates
|
|
* - remove passing argument ieee80211_local as
|
|
* drivers don't have access to it
|
|
* - move drivers using ieee80211_generic_frame_duration()
|
|
* to this
|
|
*/
|
|
ath5k_hw_reg_write(ah, tx_time,
|
|
reg + (AR5K_SET_SHORT_PREAMBLE << 2));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Main reset function
|
|
*/
|
|
int ath5k_hw_reset(struct ath5k_hw *ah, enum ieee80211_if_types op_mode,
|
|
struct ieee80211_channel *channel, bool change_channel)
|
|
{
|
|
struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
|
|
u32 data, s_seq, s_ant, s_led[3];
|
|
unsigned int i, mode, freq, ee_mode, ant[2], driver_mode = -1;
|
|
int ret;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
|
|
s_seq = 0;
|
|
s_ant = 0;
|
|
ee_mode = 0;
|
|
freq = 0;
|
|
mode = 0;
|
|
|
|
/*
|
|
* Save some registers before a reset
|
|
*/
|
|
/*DCU/Antenna selection not available on 5210*/
|
|
if (ah->ah_version != AR5K_AR5210) {
|
|
if (change_channel == true) {
|
|
/* Seq number for queue 0 -do this for all queues ? */
|
|
s_seq = ath5k_hw_reg_read(ah,
|
|
AR5K_QUEUE_DFS_SEQNUM(0));
|
|
/*Default antenna*/
|
|
s_ant = ath5k_hw_reg_read(ah, AR5K_DEFAULT_ANTENNA);
|
|
}
|
|
}
|
|
|
|
/*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);
|
|
|
|
if (change_channel == true && ah->ah_rf_banks != NULL)
|
|
ath5k_hw_get_rf_gain(ah);
|
|
|
|
|
|
/*Wakeup the device*/
|
|
ret = ath5k_hw_nic_wakeup(ah, channel->val, false);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/*
|
|
* Initialize operating mode
|
|
*/
|
|
ah->ah_op_mode = op_mode;
|
|
|
|
/*
|
|
* 5111/5112 Settings
|
|
* 5210 only comes with RF5110
|
|
*/
|
|
if (ah->ah_version != AR5K_AR5210) {
|
|
if (ah->ah_radio != AR5K_RF5111 &&
|
|
ah->ah_radio != AR5K_RF5112 &&
|
|
ah->ah_radio != AR5K_RF5413) {
|
|
ATH5K_ERR(ah->ah_sc,
|
|
"invalid phy radio: %u\n", ah->ah_radio);
|
|
return -EINVAL;
|
|
}
|
|
|
|
switch (channel->val & CHANNEL_MODES) {
|
|
case CHANNEL_A:
|
|
mode = AR5K_INI_VAL_11A;
|
|
freq = AR5K_INI_RFGAIN_5GHZ;
|
|
ee_mode = AR5K_EEPROM_MODE_11A;
|
|
driver_mode = MODE_IEEE80211A;
|
|
break;
|
|
case CHANNEL_G:
|
|
mode = AR5K_INI_VAL_11G;
|
|
freq = AR5K_INI_RFGAIN_2GHZ;
|
|
ee_mode = AR5K_EEPROM_MODE_11G;
|
|
driver_mode = MODE_IEEE80211G;
|
|
break;
|
|
case CHANNEL_B:
|
|
mode = AR5K_INI_VAL_11B;
|
|
freq = AR5K_INI_RFGAIN_2GHZ;
|
|
ee_mode = AR5K_EEPROM_MODE_11B;
|
|
driver_mode = MODE_IEEE80211B;
|
|
break;
|
|
case CHANNEL_T:
|
|
mode = AR5K_INI_VAL_11A_TURBO;
|
|
freq = AR5K_INI_RFGAIN_5GHZ;
|
|
ee_mode = AR5K_EEPROM_MODE_11A;
|
|
driver_mode = MODE_ATHEROS_TURBO;
|
|
break;
|
|
/*Is this ok on 5211 too ?*/
|
|
case CHANNEL_TG:
|
|
mode = AR5K_INI_VAL_11G_TURBO;
|
|
freq = AR5K_INI_RFGAIN_2GHZ;
|
|
ee_mode = AR5K_EEPROM_MODE_11G;
|
|
driver_mode = MODE_ATHEROS_TURBOG;
|
|
break;
|
|
case CHANNEL_XR:
|
|
if (ah->ah_version == AR5K_AR5211) {
|
|
ATH5K_ERR(ah->ah_sc,
|
|
"XR mode not available on 5211");
|
|
return -EINVAL;
|
|
}
|
|
mode = AR5K_INI_VAL_XR;
|
|
freq = AR5K_INI_RFGAIN_5GHZ;
|
|
ee_mode = AR5K_EEPROM_MODE_11A;
|
|
driver_mode = MODE_IEEE80211A;
|
|
break;
|
|
default:
|
|
ATH5K_ERR(ah->ah_sc,
|
|
"invalid channel: %d\n", channel->freq);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* PHY access enable */
|
|
ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0));
|
|
|
|
}
|
|
|
|
ret = ath5k_hw_write_initvals(ah, mode, change_channel);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/*
|
|
* 5211/5212 Specific
|
|
*/
|
|
if (ah->ah_version != AR5K_AR5210) {
|
|
/*
|
|
* Write initial RF gain settings
|
|
* This should work for both 5111/5112
|
|
*/
|
|
ret = ath5k_hw_rfgain(ah, freq);
|
|
if (ret)
|
|
return ret;
|
|
|
|
mdelay(1);
|
|
|
|
/*
|
|
* Write some more initial register settings
|
|
*/
|
|
if (ah->ah_version > AR5K_AR5211){ /* found on 5213+ */
|
|
ath5k_hw_reg_write(ah, 0x0002a002, AR5K_PHY(11));
|
|
|
|
if (channel->val == CHANNEL_G)
|
|
ath5k_hw_reg_write(ah, 0x00f80d80, AR5K_PHY(83)); /* 0x00fc0ec0 */
|
|
else
|
|
ath5k_hw_reg_write(ah, 0x00000000, AR5K_PHY(83));
|
|
|
|
ath5k_hw_reg_write(ah, 0x000001b5, 0xa228); /* 0x000009b5 */
|
|
ath5k_hw_reg_write(ah, 0x000009b5, 0xa228);
|
|
ath5k_hw_reg_write(ah, 0x0000000f, 0x8060);
|
|
ath5k_hw_reg_write(ah, 0x00000000, 0xa254);
|
|
ath5k_hw_reg_write(ah, 0x0000000e, AR5K_PHY_SCAL);
|
|
}
|
|
|
|
/* Fix for first revision of the RF5112 RF chipset */
|
|
if (ah->ah_radio >= AR5K_RF5112 &&
|
|
ah->ah_radio_5ghz_revision <
|
|
AR5K_SREV_RAD_5112A) {
|
|
ath5k_hw_reg_write(ah, AR5K_PHY_CCKTXCTL_WORLD,
|
|
AR5K_PHY_CCKTXCTL);
|
|
if (channel->val & CHANNEL_5GHZ)
|
|
data = 0xffb81020;
|
|
else
|
|
data = 0xffb80d20;
|
|
ath5k_hw_reg_write(ah, data, AR5K_PHY_FRAME_CTL);
|
|
}
|
|
|
|
/*
|
|
* Set TX power (FIXME)
|
|
*/
|
|
ret = ath5k_hw_txpower(ah, channel, AR5K_TUNE_DEFAULT_TXPOWER);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Write rate duration table only on AR5212 and if
|
|
* virtual interface has already been brought up
|
|
* XXX: rethink this after new mode changes to
|
|
* mac80211 are integrated */
|
|
if (ah->ah_version == AR5K_AR5212 &&
|
|
ah->ah_sc->vif != NULL)
|
|
ath5k_hw_write_rate_duration(ah, driver_mode);
|
|
|
|
/*
|
|
* Write RF registers
|
|
* TODO:Does this work on 5211 (5111) ?
|
|
*/
|
|
ret = ath5k_hw_rfregs(ah, channel, mode);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/*
|
|
* Configure additional registers
|
|
*/
|
|
|
|
/* Write OFDM timings on 5212*/
|
|
if (ah->ah_version == AR5K_AR5212 &&
|
|
channel->val & CHANNEL_OFDM) {
|
|
ret = ath5k_hw_write_ofdm_timings(ah, channel);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
/*Enable/disable 802.11b mode on 5111
|
|
(enable 2111 frequency converter + CCK)*/
|
|
if (ah->ah_radio == AR5K_RF5111) {
|
|
if (driver_mode == MODE_IEEE80211B)
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_TXCFG,
|
|
AR5K_TXCFG_B_MODE);
|
|
else
|
|
AR5K_REG_DISABLE_BITS(ah, AR5K_TXCFG,
|
|
AR5K_TXCFG_B_MODE);
|
|
}
|
|
|
|
/*
|
|
* Set channel and calibrate the PHY
|
|
*/
|
|
ret = ath5k_hw_channel(ah, channel);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Set antenna mode */
|
|
AR5K_REG_MASKED_BITS(ah, AR5K_PHY(0x44),
|
|
ah->ah_antenna[ee_mode][0], 0xfffffc06);
|
|
|
|
/*
|
|
* In case a fixed antenna was set as default
|
|
* write the same settings on both AR5K_PHY_ANT_SWITCH_TABLE
|
|
* registers.
|
|
*/
|
|
if (s_ant != 0){
|
|
if (s_ant == AR5K_ANT_FIXED_A) /* 1 - Main */
|
|
ant[0] = ant[1] = AR5K_ANT_FIXED_A;
|
|
else /* 2 - Aux */
|
|
ant[0] = ant[1] = AR5K_ANT_FIXED_B;
|
|
} else {
|
|
ant[0] = AR5K_ANT_FIXED_A;
|
|
ant[1] = AR5K_ANT_FIXED_B;
|
|
}
|
|
|
|
ath5k_hw_reg_write(ah, ah->ah_antenna[ee_mode][ant[0]],
|
|
AR5K_PHY_ANT_SWITCH_TABLE_0);
|
|
ath5k_hw_reg_write(ah, ah->ah_antenna[ee_mode][ant[1]],
|
|
AR5K_PHY_ANT_SWITCH_TABLE_1);
|
|
|
|
/* Commit values from EEPROM */
|
|
if (ah->ah_radio == AR5K_RF5111)
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_FRAME_CTL,
|
|
AR5K_PHY_FRAME_CTL_TX_CLIP, ee->ee_tx_clip);
|
|
|
|
ath5k_hw_reg_write(ah,
|
|
AR5K_PHY_NF_SVAL(ee->ee_noise_floor_thr[ee_mode]),
|
|
AR5K_PHY(0x5a));
|
|
|
|
AR5K_REG_MASKED_BITS(ah, AR5K_PHY(0x11),
|
|
(ee->ee_switch_settling[ee_mode] << 7) & 0x3f80,
|
|
0xffffc07f);
|
|
AR5K_REG_MASKED_BITS(ah, AR5K_PHY(0x12),
|
|
(ee->ee_ant_tx_rx[ee_mode] << 12) & 0x3f000,
|
|
0xfffc0fff);
|
|
AR5K_REG_MASKED_BITS(ah, AR5K_PHY(0x14),
|
|
(ee->ee_adc_desired_size[ee_mode] & 0x00ff) |
|
|
((ee->ee_pga_desired_size[ee_mode] << 8) & 0xff00),
|
|
0xffff0000);
|
|
|
|
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(0x0d));
|
|
|
|
AR5K_REG_MASKED_BITS(ah, AR5K_PHY(0x0a),
|
|
ee->ee_tx_end2xlna_enable[ee_mode] << 8, 0xffff00ff);
|
|
AR5K_REG_MASKED_BITS(ah, AR5K_PHY(0x19),
|
|
(ee->ee_thr_62[ee_mode] << 12) & 0x7f000, 0xfff80fff);
|
|
AR5K_REG_MASKED_BITS(ah, AR5K_PHY(0x49), 4, 0xffffff01);
|
|
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ,
|
|
AR5K_PHY_IQ_CORR_ENABLE |
|
|
(ee->ee_i_cal[ee_mode] << AR5K_PHY_IQ_CORR_Q_I_COFF_S) |
|
|
ee->ee_q_cal[ee_mode]);
|
|
|
|
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]);
|
|
|
|
} else {
|
|
mdelay(1);
|
|
/* Disable phy and wait */
|
|
ath5k_hw_reg_write(ah, AR5K_PHY_ACT_DISABLE, AR5K_PHY_ACT);
|
|
mdelay(1);
|
|
}
|
|
|
|
/*
|
|
* Restore saved values
|
|
*/
|
|
/*DCU/Antenna selection not available on 5210*/
|
|
if (ah->ah_version != AR5K_AR5210) {
|
|
ath5k_hw_reg_write(ah, s_seq, AR5K_QUEUE_DFS_SEQNUM(0));
|
|
ath5k_hw_reg_write(ah, s_ant, AR5K_DEFAULT_ANTENNA);
|
|
}
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, s_led[0]);
|
|
ath5k_hw_reg_write(ah, s_led[1], AR5K_GPIOCR);
|
|
ath5k_hw_reg_write(ah, s_led[2], AR5K_GPIODO);
|
|
|
|
/*
|
|
* Misc
|
|
*/
|
|
/* XXX: add ah->aid once mac80211 gives this to us */
|
|
ath5k_hw_set_associd(ah, ah->ah_bssid, 0);
|
|
|
|
ath5k_hw_set_opmode(ah);
|
|
/*PISR/SISR Not available on 5210*/
|
|
if (ah->ah_version != AR5K_AR5210) {
|
|
ath5k_hw_reg_write(ah, 0xffffffff, AR5K_PISR);
|
|
/* If we later allow tuning for this, store into sc structure */
|
|
data = AR5K_TUNE_RSSI_THRES |
|
|
AR5K_TUNE_BMISS_THRES << AR5K_RSSI_THR_BMISS_S;
|
|
ath5k_hw_reg_write(ah, data, AR5K_RSSI_THR);
|
|
}
|
|
|
|
/*
|
|
* Set Rx/Tx DMA Configuration
|
|
*(passing dma size not available on 5210)
|
|
*/
|
|
if (ah->ah_version != AR5K_AR5210) {
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG, AR5K_TXCFG_SDMAMR,
|
|
AR5K_DMASIZE_512B | AR5K_TXCFG_DMASIZE);
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_SDMAMW,
|
|
AR5K_DMASIZE_512B);
|
|
}
|
|
|
|
/*
|
|
* Enable the PHY and wait until completion
|
|
*/
|
|
ath5k_hw_reg_write(ah, AR5K_PHY_ACT_ENABLE, AR5K_PHY_ACT);
|
|
|
|
/*
|
|
* 5111/5112 Specific
|
|
*/
|
|
if (ah->ah_version != AR5K_AR5210) {
|
|
data = ath5k_hw_reg_read(ah, AR5K_PHY_RX_DELAY) &
|
|
AR5K_PHY_RX_DELAY_M;
|
|
data = (channel->val & CHANNEL_CCK) ?
|
|
((data << 2) / 22) : (data / 10);
|
|
|
|
udelay(100 + data);
|
|
} else {
|
|
mdelay(1);
|
|
}
|
|
|
|
/*
|
|
* Enable calibration and wait until completion
|
|
*/
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL,
|
|
AR5K_PHY_AGCCTL_CAL);
|
|
|
|
if (ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL,
|
|
AR5K_PHY_AGCCTL_CAL, 0, false)) {
|
|
ATH5K_ERR(ah->ah_sc, "calibration timeout (%uMHz)\n",
|
|
channel->freq);
|
|
return -EAGAIN;
|
|
}
|
|
|
|
ret = ath5k_hw_noise_floor_calibration(ah, channel->freq);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ah->ah_calibration = false;
|
|
|
|
/* A and G modes can use QAM modulation which requires enabling
|
|
* I and Q calibration. Don't bother in B mode. */
|
|
if (!(driver_mode == MODE_IEEE80211B)) {
|
|
ah->ah_calibration = true;
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ,
|
|
AR5K_PHY_IQ_CAL_NUM_LOG_MAX, 15);
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ,
|
|
AR5K_PHY_IQ_RUN);
|
|
}
|
|
|
|
/*
|
|
* Reset queues and start beacon timers at the end of the reset routine
|
|
*/
|
|
for (i = 0; i < ah->ah_capabilities.cap_queues.q_tx_num; i++) {
|
|
/*No QCU on 5210*/
|
|
if (ah->ah_version != AR5K_AR5210)
|
|
AR5K_REG_WRITE_Q(ah, AR5K_QUEUE_QCUMASK(i), i);
|
|
|
|
ret = ath5k_hw_reset_tx_queue(ah, i);
|
|
if (ret) {
|
|
ATH5K_ERR(ah->ah_sc,
|
|
"failed to reset TX queue #%d\n", i);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* Pre-enable interrupts on 5211/5212*/
|
|
if (ah->ah_version != AR5K_AR5210)
|
|
ath5k_hw_set_intr(ah, AR5K_INT_RX | AR5K_INT_TX |
|
|
AR5K_INT_FATAL);
|
|
|
|
/*
|
|
* Set RF kill flags if supported by the device (read from the EEPROM)
|
|
* Disable gpio_intr for now since it results system hang.
|
|
* TODO: Handle this in ath5k_intr
|
|
*/
|
|
#if 0
|
|
if (AR5K_EEPROM_HDR_RFKILL(ah->ah_capabilities.cap_eeprom.ee_header)) {
|
|
ath5k_hw_set_gpio_input(ah, 0);
|
|
ah->ah_gpio[0] = ath5k_hw_get_gpio(ah, 0);
|
|
if (ah->ah_gpio[0] == 0)
|
|
ath5k_hw_set_gpio_intr(ah, 0, 1);
|
|
else
|
|
ath5k_hw_set_gpio_intr(ah, 0, 0);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Set the 32MHz reference clock on 5212 phy clock sleep register
|
|
*/
|
|
if (ah->ah_version == AR5K_AR5212) {
|
|
ath5k_hw_reg_write(ah, AR5K_PHY_SCR_32MHZ, AR5K_PHY_SCR);
|
|
ath5k_hw_reg_write(ah, AR5K_PHY_SLMT_32MHZ, AR5K_PHY_SLMT);
|
|
ath5k_hw_reg_write(ah, AR5K_PHY_SCAL_32MHZ, 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);
|
|
ath5k_hw_reg_write(ah, ah->ah_radio == AR5K_RF5111 ?
|
|
AR5K_PHY_SPENDING_RF5111 : AR5K_PHY_SPENDING_RF5112,
|
|
AR5K_PHY_SPENDING);
|
|
}
|
|
|
|
/*
|
|
* Disable beacons and reset the register
|
|
*/
|
|
AR5K_REG_DISABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE |
|
|
AR5K_BEACON_RESET_TSF);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Reset chipset
|
|
*/
|
|
static int ath5k_hw_nic_reset(struct ath5k_hw *ah, u32 val)
|
|
{
|
|
int ret;
|
|
u32 mask = val ? val : ~0U;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
|
|
/* 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 */
|
|
udelay(15);
|
|
|
|
if (ah->ah_version == AR5K_AR5210) {
|
|
val &= AR5K_RESET_CTL_CHIP;
|
|
mask &= AR5K_RESET_CTL_CHIP;
|
|
} 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;
|
|
}
|
|
|
|
/*
|
|
* Power management functions
|
|
*/
|
|
|
|
/*
|
|
* Sleep control
|
|
*/
|
|
int ath5k_hw_set_power(struct ath5k_hw *ah, enum ath5k_power_mode mode,
|
|
bool set_chip, u16 sleep_duration)
|
|
{
|
|
unsigned int i;
|
|
u32 staid;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
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 == true)
|
|
ath5k_hw_reg_write(ah,
|
|
AR5K_SLEEP_CTL_SLE | sleep_duration,
|
|
AR5K_SLEEP_CTL);
|
|
|
|
staid |= AR5K_STA_ID1_PWR_SV;
|
|
break;
|
|
|
|
case AR5K_PM_FULL_SLEEP:
|
|
if (set_chip == true)
|
|
ath5k_hw_reg_write(ah, AR5K_SLEEP_CTL_SLE_SLP,
|
|
AR5K_SLEEP_CTL);
|
|
|
|
staid |= AR5K_STA_ID1_PWR_SV;
|
|
break;
|
|
|
|
case AR5K_PM_AWAKE:
|
|
if (set_chip == false)
|
|
goto commit;
|
|
|
|
ath5k_hw_reg_write(ah, AR5K_SLEEP_CTL_SLE_WAKE,
|
|
AR5K_SLEEP_CTL);
|
|
|
|
for (i = 5000; 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 */
|
|
udelay(200);
|
|
ath5k_hw_reg_write(ah, AR5K_SLEEP_CTL_SLE_WAKE,
|
|
AR5K_SLEEP_CTL);
|
|
}
|
|
|
|
/* Fail if the chip didn't wake up */
|
|
if (i <= 0)
|
|
return -EIO;
|
|
|
|
staid &= ~AR5K_STA_ID1_PWR_SV;
|
|
break;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
commit:
|
|
ah->ah_power_mode = mode;
|
|
ath5k_hw_reg_write(ah, staid, AR5K_STA_ID1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***********************\
|
|
DMA Related Functions
|
|
\***********************/
|
|
|
|
/*
|
|
* Receive functions
|
|
*/
|
|
|
|
/*
|
|
* Start DMA receive
|
|
*/
|
|
void ath5k_hw_start_rx(struct ath5k_hw *ah)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
ath5k_hw_reg_write(ah, AR5K_CR_RXE, AR5K_CR);
|
|
}
|
|
|
|
/*
|
|
* Stop DMA receive
|
|
*/
|
|
int ath5k_hw_stop_rx_dma(struct ath5k_hw *ah)
|
|
{
|
|
unsigned int i;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
ath5k_hw_reg_write(ah, AR5K_CR_RXD, AR5K_CR);
|
|
|
|
/*
|
|
* It may take some time to disable the DMA receive unit
|
|
*/
|
|
for (i = 2000; i > 0 &&
|
|
(ath5k_hw_reg_read(ah, AR5K_CR) & AR5K_CR_RXE) != 0;
|
|
i--)
|
|
udelay(10);
|
|
|
|
return i ? 0 : -EBUSY;
|
|
}
|
|
|
|
/*
|
|
* Get the address of the RX Descriptor
|
|
*/
|
|
u32 ath5k_hw_get_rx_buf(struct ath5k_hw *ah)
|
|
{
|
|
return ath5k_hw_reg_read(ah, AR5K_RXDP);
|
|
}
|
|
|
|
/*
|
|
* Set the address of the RX Descriptor
|
|
*/
|
|
void ath5k_hw_put_rx_buf(struct ath5k_hw *ah, u32 phys_addr)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
|
|
/*TODO:Shouldn't we check if RX is enabled first ?*/
|
|
ath5k_hw_reg_write(ah, phys_addr, AR5K_RXDP);
|
|
}
|
|
|
|
/*
|
|
* Transmit functions
|
|
*/
|
|
|
|
/*
|
|
* Start DMA transmit for a specific queue
|
|
* (see also QCU/DCU functions)
|
|
*/
|
|
int ath5k_hw_tx_start(struct ath5k_hw *ah, unsigned int queue)
|
|
{
|
|
u32 tx_queue;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
|
|
|
|
/* Return if queue is declared inactive */
|
|
if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE)
|
|
return -EIO;
|
|
|
|
if (ah->ah_version == AR5K_AR5210) {
|
|
tx_queue = ath5k_hw_reg_read(ah, AR5K_CR);
|
|
|
|
/*
|
|
* Set the queue by type on 5210
|
|
*/
|
|
switch (ah->ah_txq[queue].tqi_type) {
|
|
case AR5K_TX_QUEUE_DATA:
|
|
tx_queue |= AR5K_CR_TXE0 & ~AR5K_CR_TXD0;
|
|
break;
|
|
case AR5K_TX_QUEUE_BEACON:
|
|
tx_queue |= AR5K_CR_TXE1 & ~AR5K_CR_TXD1;
|
|
ath5k_hw_reg_write(ah, AR5K_BCR_TQ1V | AR5K_BCR_BDMAE,
|
|
AR5K_BSR);
|
|
break;
|
|
case AR5K_TX_QUEUE_CAB:
|
|
tx_queue |= AR5K_CR_TXE1 & ~AR5K_CR_TXD1;
|
|
ath5k_hw_reg_write(ah, AR5K_BCR_TQ1FV | AR5K_BCR_TQ1V |
|
|
AR5K_BCR_BDMAE, AR5K_BSR);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
/* Start queue */
|
|
ath5k_hw_reg_write(ah, tx_queue, AR5K_CR);
|
|
} else {
|
|
/* Return if queue is disabled */
|
|
if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXD, queue))
|
|
return -EIO;
|
|
|
|
/* Start queue */
|
|
AR5K_REG_WRITE_Q(ah, AR5K_QCU_TXE, queue);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Stop DMA transmit for a specific queue
|
|
* (see also QCU/DCU functions)
|
|
*/
|
|
int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue)
|
|
{
|
|
unsigned int i = 100;
|
|
u32 tx_queue, pending;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
|
|
|
|
/* Return if queue is declared inactive */
|
|
if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE)
|
|
return -EIO;
|
|
|
|
if (ah->ah_version == AR5K_AR5210) {
|
|
tx_queue = ath5k_hw_reg_read(ah, AR5K_CR);
|
|
|
|
/*
|
|
* Set by queue type
|
|
*/
|
|
switch (ah->ah_txq[queue].tqi_type) {
|
|
case AR5K_TX_QUEUE_DATA:
|
|
tx_queue |= AR5K_CR_TXD0 & ~AR5K_CR_TXE0;
|
|
break;
|
|
case AR5K_TX_QUEUE_BEACON:
|
|
case AR5K_TX_QUEUE_CAB:
|
|
/* XXX Fix me... */
|
|
tx_queue |= AR5K_CR_TXD1 & ~AR5K_CR_TXD1;
|
|
ath5k_hw_reg_write(ah, 0, AR5K_BSR);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Stop queue */
|
|
ath5k_hw_reg_write(ah, tx_queue, AR5K_CR);
|
|
} else {
|
|
/*
|
|
* Schedule TX disable and wait until queue is empty
|
|
*/
|
|
AR5K_REG_WRITE_Q(ah, AR5K_QCU_TXD, queue);
|
|
|
|
/*Check for pending frames*/
|
|
do {
|
|
pending = ath5k_hw_reg_read(ah,
|
|
AR5K_QUEUE_STATUS(queue)) &
|
|
AR5K_QCU_STS_FRMPENDCNT;
|
|
udelay(100);
|
|
} while (--i && pending);
|
|
|
|
/* Clear register */
|
|
ath5k_hw_reg_write(ah, 0, AR5K_QCU_TXD);
|
|
}
|
|
|
|
/* TODO: Check for success else return error */
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Get the address of the TX Descriptor for a specific queue
|
|
* (see also QCU/DCU functions)
|
|
*/
|
|
u32 ath5k_hw_get_tx_buf(struct ath5k_hw *ah, unsigned int queue)
|
|
{
|
|
u16 tx_reg;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
|
|
|
|
/*
|
|
* Get the transmit queue descriptor pointer from the selected queue
|
|
*/
|
|
/*5210 doesn't have QCU*/
|
|
if (ah->ah_version == AR5K_AR5210) {
|
|
switch (ah->ah_txq[queue].tqi_type) {
|
|
case AR5K_TX_QUEUE_DATA:
|
|
tx_reg = AR5K_NOQCU_TXDP0;
|
|
break;
|
|
case AR5K_TX_QUEUE_BEACON:
|
|
case AR5K_TX_QUEUE_CAB:
|
|
tx_reg = AR5K_NOQCU_TXDP1;
|
|
break;
|
|
default:
|
|
return 0xffffffff;
|
|
}
|
|
} else {
|
|
tx_reg = AR5K_QUEUE_TXDP(queue);
|
|
}
|
|
|
|
return ath5k_hw_reg_read(ah, tx_reg);
|
|
}
|
|
|
|
/*
|
|
* Set the address of the TX Descriptor for a specific queue
|
|
* (see also QCU/DCU functions)
|
|
*/
|
|
int ath5k_hw_put_tx_buf(struct ath5k_hw *ah, unsigned int queue, u32 phys_addr)
|
|
{
|
|
u16 tx_reg;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
|
|
|
|
/*
|
|
* Set the transmit queue descriptor pointer register by type
|
|
* on 5210
|
|
*/
|
|
if (ah->ah_version == AR5K_AR5210) {
|
|
switch (ah->ah_txq[queue].tqi_type) {
|
|
case AR5K_TX_QUEUE_DATA:
|
|
tx_reg = AR5K_NOQCU_TXDP0;
|
|
break;
|
|
case AR5K_TX_QUEUE_BEACON:
|
|
case AR5K_TX_QUEUE_CAB:
|
|
tx_reg = AR5K_NOQCU_TXDP1;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
/*
|
|
* Set the transmit queue descriptor pointer for
|
|
* the selected queue on QCU for 5211+
|
|
* (this won't work if the queue is still active)
|
|
*/
|
|
if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue))
|
|
return -EIO;
|
|
|
|
tx_reg = AR5K_QUEUE_TXDP(queue);
|
|
}
|
|
|
|
/* Set descriptor pointer */
|
|
ath5k_hw_reg_write(ah, phys_addr, tx_reg);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Update tx trigger level
|
|
*/
|
|
int ath5k_hw_update_tx_triglevel(struct ath5k_hw *ah, bool increase)
|
|
{
|
|
u32 trigger_level, imr;
|
|
int ret = -EIO;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
|
|
/*
|
|
* Disable interrupts by setting the mask
|
|
*/
|
|
imr = ath5k_hw_set_intr(ah, ah->ah_imr & ~AR5K_INT_GLOBAL);
|
|
|
|
/*TODO: Boundary check on trigger_level*/
|
|
trigger_level = AR5K_REG_MS(ath5k_hw_reg_read(ah, AR5K_TXCFG),
|
|
AR5K_TXCFG_TXFULL);
|
|
|
|
if (increase == false) {
|
|
if (--trigger_level < AR5K_TUNE_MIN_TX_FIFO_THRES)
|
|
goto done;
|
|
} else
|
|
trigger_level +=
|
|
((AR5K_TUNE_MAX_TX_FIFO_THRES - trigger_level) / 2);
|
|
|
|
/*
|
|
* Update trigger level on success
|
|
*/
|
|
if (ah->ah_version == AR5K_AR5210)
|
|
ath5k_hw_reg_write(ah, trigger_level, AR5K_TRIG_LVL);
|
|
else
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG,
|
|
AR5K_TXCFG_TXFULL, trigger_level);
|
|
|
|
ret = 0;
|
|
|
|
done:
|
|
/*
|
|
* Restore interrupt mask
|
|
*/
|
|
ath5k_hw_set_intr(ah, imr);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Interrupt handling
|
|
*/
|
|
|
|
/*
|
|
* Check if we have pending interrupts
|
|
*/
|
|
bool ath5k_hw_is_intr_pending(struct ath5k_hw *ah)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
return ath5k_hw_reg_read(ah, AR5K_INTPEND);
|
|
}
|
|
|
|
/*
|
|
* Get interrupt mask (ISR)
|
|
*/
|
|
int ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask)
|
|
{
|
|
u32 data;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
|
|
/*
|
|
* Read interrupt status from the Interrupt Status register
|
|
* on 5210
|
|
*/
|
|
if (ah->ah_version == AR5K_AR5210) {
|
|
data = ath5k_hw_reg_read(ah, AR5K_ISR);
|
|
if (unlikely(data == AR5K_INT_NOCARD)) {
|
|
*interrupt_mask = data;
|
|
return -ENODEV;
|
|
}
|
|
} else {
|
|
/*
|
|
* Read interrupt status from the Read-And-Clear shadow register
|
|
* Note: PISR/SISR Not available on 5210
|
|
*/
|
|
data = ath5k_hw_reg_read(ah, AR5K_RAC_PISR);
|
|
}
|
|
|
|
/*
|
|
* Get abstract interrupt mask (driver-compatible)
|
|
*/
|
|
*interrupt_mask = (data & AR5K_INT_COMMON) & ah->ah_imr;
|
|
|
|
if (unlikely(data == AR5K_INT_NOCARD))
|
|
return -ENODEV;
|
|
|
|
if (data & (AR5K_ISR_RXOK | AR5K_ISR_RXERR))
|
|
*interrupt_mask |= AR5K_INT_RX;
|
|
|
|
if (data & (AR5K_ISR_TXOK | AR5K_ISR_TXERR
|
|
| AR5K_ISR_TXDESC | AR5K_ISR_TXEOL))
|
|
*interrupt_mask |= AR5K_INT_TX;
|
|
|
|
if (ah->ah_version != AR5K_AR5210) {
|
|
/*HIU = Host Interface Unit (PCI etc)*/
|
|
if (unlikely(data & (AR5K_ISR_HIUERR)))
|
|
*interrupt_mask |= AR5K_INT_FATAL;
|
|
|
|
/*Beacon Not Ready*/
|
|
if (unlikely(data & (AR5K_ISR_BNR)))
|
|
*interrupt_mask |= AR5K_INT_BNR;
|
|
}
|
|
|
|
/*
|
|
* XXX: BMISS interrupts may occur after association.
|
|
* I found this on 5210 code but it needs testing. If this is
|
|
* true we should disable them before assoc and re-enable them
|
|
* after a successfull assoc + some jiffies.
|
|
*/
|
|
#if 0
|
|
interrupt_mask &= ~AR5K_INT_BMISS;
|
|
#endif
|
|
|
|
/*
|
|
* In case we didn't handle anything,
|
|
* print the register value.
|
|
*/
|
|
if (unlikely(*interrupt_mask == 0 && net_ratelimit()))
|
|
ATH5K_PRINTF("0x%08x\n", data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Set interrupt mask
|
|
*/
|
|
enum ath5k_int ath5k_hw_set_intr(struct ath5k_hw *ah, enum ath5k_int new_mask)
|
|
{
|
|
enum ath5k_int old_mask, int_mask;
|
|
|
|
/*
|
|
* Disable card interrupts to prevent any race conditions
|
|
* (they will be re-enabled afterwards).
|
|
*/
|
|
ath5k_hw_reg_write(ah, AR5K_IER_DISABLE, AR5K_IER);
|
|
|
|
old_mask = ah->ah_imr;
|
|
|
|
/*
|
|
* Add additional, chipset-dependent interrupt mask flags
|
|
* and write them to the IMR (interrupt mask register).
|
|
*/
|
|
int_mask = new_mask & AR5K_INT_COMMON;
|
|
|
|
if (new_mask & AR5K_INT_RX)
|
|
int_mask |= AR5K_IMR_RXOK | AR5K_IMR_RXERR | AR5K_IMR_RXORN |
|
|
AR5K_IMR_RXDESC;
|
|
|
|
if (new_mask & AR5K_INT_TX)
|
|
int_mask |= AR5K_IMR_TXOK | AR5K_IMR_TXERR | AR5K_IMR_TXDESC |
|
|
AR5K_IMR_TXURN;
|
|
|
|
if (ah->ah_version != AR5K_AR5210) {
|
|
if (new_mask & AR5K_INT_FATAL) {
|
|
int_mask |= AR5K_IMR_HIUERR;
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_SIMR2, AR5K_SIMR2_MCABT |
|
|
AR5K_SIMR2_SSERR | AR5K_SIMR2_DPERR);
|
|
}
|
|
}
|
|
|
|
ath5k_hw_reg_write(ah, int_mask, AR5K_PIMR);
|
|
|
|
/* Store new interrupt mask */
|
|
ah->ah_imr = new_mask;
|
|
|
|
/* ..re-enable interrupts */
|
|
ath5k_hw_reg_write(ah, AR5K_IER_ENABLE, AR5K_IER);
|
|
|
|
return old_mask;
|
|
}
|
|
|
|
|
|
/*************************\
|
|
EEPROM access functions
|
|
\*************************/
|
|
|
|
/*
|
|
* Read from eeprom
|
|
*/
|
|
static int ath5k_hw_eeprom_read(struct ath5k_hw *ah, u32 offset, u16 *data)
|
|
{
|
|
u32 status, timeout;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
/*
|
|
* Initialize EEPROM access
|
|
*/
|
|
if (ah->ah_version == AR5K_AR5210) {
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_EEAE);
|
|
(void)ath5k_hw_reg_read(ah, AR5K_EEPROM_BASE + (4 * offset));
|
|
} else {
|
|
ath5k_hw_reg_write(ah, offset, AR5K_EEPROM_BASE);
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_EEPROM_CMD,
|
|
AR5K_EEPROM_CMD_READ);
|
|
}
|
|
|
|
for (timeout = AR5K_TUNE_REGISTER_TIMEOUT; timeout > 0; timeout--) {
|
|
status = ath5k_hw_reg_read(ah, AR5K_EEPROM_STATUS);
|
|
if (status & AR5K_EEPROM_STAT_RDDONE) {
|
|
if (status & AR5K_EEPROM_STAT_RDERR)
|
|
return -EIO;
|
|
*data = (u16)(ath5k_hw_reg_read(ah, AR5K_EEPROM_DATA) &
|
|
0xffff);
|
|
return 0;
|
|
}
|
|
udelay(15);
|
|
}
|
|
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
/*
|
|
* Write to eeprom - currently disabled, use at your own risk
|
|
*/
|
|
static int ath5k_hw_eeprom_write(struct ath5k_hw *ah, u32 offset, u16 data)
|
|
{
|
|
#if 0
|
|
u32 status, timeout;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
|
|
/*
|
|
* Initialize eeprom access
|
|
*/
|
|
|
|
if (ah->ah_version == AR5K_AR5210) {
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_EEAE);
|
|
} else {
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_EEPROM_CMD,
|
|
AR5K_EEPROM_CMD_RESET);
|
|
}
|
|
|
|
/*
|
|
* Write data to data register
|
|
*/
|
|
|
|
if (ah->ah_version == AR5K_AR5210) {
|
|
ath5k_hw_reg_write(ah, data, AR5K_EEPROM_BASE + (4 * offset));
|
|
} else {
|
|
ath5k_hw_reg_write(ah, offset, AR5K_EEPROM_BASE);
|
|
ath5k_hw_reg_write(ah, data, AR5K_EEPROM_DATA);
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_EEPROM_CMD,
|
|
AR5K_EEPROM_CMD_WRITE);
|
|
}
|
|
|
|
/*
|
|
* Check status
|
|
*/
|
|
|
|
for (timeout = AR5K_TUNE_REGISTER_TIMEOUT; timeout > 0; timeout--) {
|
|
status = ath5k_hw_reg_read(ah, AR5K_EEPROM_STATUS);
|
|
if (status & AR5K_EEPROM_STAT_WRDONE) {
|
|
if (status & AR5K_EEPROM_STAT_WRERR)
|
|
return EIO;
|
|
return 0;
|
|
}
|
|
udelay(15);
|
|
}
|
|
#endif
|
|
ATH5K_ERR(ah->ah_sc, "EEPROM Write is disabled!");
|
|
return -EIO;
|
|
}
|
|
|
|
/*
|
|
* Translate binary channel representation in EEPROM to frequency
|
|
*/
|
|
static u16 ath5k_eeprom_bin2freq(struct ath5k_hw *ah, u16 bin, unsigned int mode)
|
|
{
|
|
u16 val;
|
|
|
|
if (bin == AR5K_EEPROM_CHANNEL_DIS)
|
|
return bin;
|
|
|
|
if (mode == AR5K_EEPROM_MODE_11A) {
|
|
if (ah->ah_ee_version > AR5K_EEPROM_VERSION_3_2)
|
|
val = (5 * bin) + 4800;
|
|
else
|
|
val = bin > 62 ? (10 * 62) + (5 * (bin - 62)) + 5100 :
|
|
(bin * 10) + 5100;
|
|
} else {
|
|
if (ah->ah_ee_version > AR5K_EEPROM_VERSION_3_2)
|
|
val = bin + 2300;
|
|
else
|
|
val = bin + 2400;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
/*
|
|
* Read antenna infos from eeprom
|
|
*/
|
|
static int ath5k_eeprom_read_ants(struct ath5k_hw *ah, u32 *offset,
|
|
unsigned int mode)
|
|
{
|
|
struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
|
|
u32 o = *offset;
|
|
u16 val;
|
|
int ret, i = 0;
|
|
|
|
AR5K_EEPROM_READ(o++, val);
|
|
ee->ee_switch_settling[mode] = (val >> 8) & 0x7f;
|
|
ee->ee_ant_tx_rx[mode] = (val >> 2) & 0x3f;
|
|
ee->ee_ant_control[mode][i] = (val << 4) & 0x3f;
|
|
|
|
AR5K_EEPROM_READ(o++, val);
|
|
ee->ee_ant_control[mode][i++] |= (val >> 12) & 0xf;
|
|
ee->ee_ant_control[mode][i++] = (val >> 6) & 0x3f;
|
|
ee->ee_ant_control[mode][i++] = val & 0x3f;
|
|
|
|
AR5K_EEPROM_READ(o++, val);
|
|
ee->ee_ant_control[mode][i++] = (val >> 10) & 0x3f;
|
|
ee->ee_ant_control[mode][i++] = (val >> 4) & 0x3f;
|
|
ee->ee_ant_control[mode][i] = (val << 2) & 0x3f;
|
|
|
|
AR5K_EEPROM_READ(o++, val);
|
|
ee->ee_ant_control[mode][i++] |= (val >> 14) & 0x3;
|
|
ee->ee_ant_control[mode][i++] = (val >> 8) & 0x3f;
|
|
ee->ee_ant_control[mode][i++] = (val >> 2) & 0x3f;
|
|
ee->ee_ant_control[mode][i] = (val << 4) & 0x3f;
|
|
|
|
AR5K_EEPROM_READ(o++, val);
|
|
ee->ee_ant_control[mode][i++] |= (val >> 12) & 0xf;
|
|
ee->ee_ant_control[mode][i++] = (val >> 6) & 0x3f;
|
|
ee->ee_ant_control[mode][i++] = val & 0x3f;
|
|
|
|
/* Get antenna modes */
|
|
ah->ah_antenna[mode][0] =
|
|
(ee->ee_ant_control[mode][0] << 4) | 0x1;
|
|
ah->ah_antenna[mode][AR5K_ANT_FIXED_A] =
|
|
ee->ee_ant_control[mode][1] |
|
|
(ee->ee_ant_control[mode][2] << 6) |
|
|
(ee->ee_ant_control[mode][3] << 12) |
|
|
(ee->ee_ant_control[mode][4] << 18) |
|
|
(ee->ee_ant_control[mode][5] << 24);
|
|
ah->ah_antenna[mode][AR5K_ANT_FIXED_B] =
|
|
ee->ee_ant_control[mode][6] |
|
|
(ee->ee_ant_control[mode][7] << 6) |
|
|
(ee->ee_ant_control[mode][8] << 12) |
|
|
(ee->ee_ant_control[mode][9] << 18) |
|
|
(ee->ee_ant_control[mode][10] << 24);
|
|
|
|
/* return new offset */
|
|
*offset = o;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Read supported modes from eeprom
|
|
*/
|
|
static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset,
|
|
unsigned int mode)
|
|
{
|
|
struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
|
|
u32 o = *offset;
|
|
u16 val;
|
|
int ret;
|
|
|
|
AR5K_EEPROM_READ(o++, val);
|
|
ee->ee_tx_end2xlna_enable[mode] = (val >> 8) & 0xff;
|
|
ee->ee_thr_62[mode] = val & 0xff;
|
|
|
|
if (ah->ah_ee_version <= AR5K_EEPROM_VERSION_3_2)
|
|
ee->ee_thr_62[mode] = mode == AR5K_EEPROM_MODE_11A ? 15 : 28;
|
|
|
|
AR5K_EEPROM_READ(o++, val);
|
|
ee->ee_tx_end2xpa_disable[mode] = (val >> 8) & 0xff;
|
|
ee->ee_tx_frm2xpa_enable[mode] = val & 0xff;
|
|
|
|
AR5K_EEPROM_READ(o++, val);
|
|
ee->ee_pga_desired_size[mode] = (val >> 8) & 0xff;
|
|
|
|
if ((val & 0xff) & 0x80)
|
|
ee->ee_noise_floor_thr[mode] = -((((val & 0xff) ^ 0xff)) + 1);
|
|
else
|
|
ee->ee_noise_floor_thr[mode] = val & 0xff;
|
|
|
|
if (ah->ah_ee_version <= AR5K_EEPROM_VERSION_3_2)
|
|
ee->ee_noise_floor_thr[mode] =
|
|
mode == AR5K_EEPROM_MODE_11A ? -54 : -1;
|
|
|
|
AR5K_EEPROM_READ(o++, val);
|
|
ee->ee_xlna_gain[mode] = (val >> 5) & 0xff;
|
|
ee->ee_x_gain[mode] = (val >> 1) & 0xf;
|
|
ee->ee_xpd[mode] = val & 0x1;
|
|
|
|
if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0)
|
|
ee->ee_fixed_bias[mode] = (val >> 13) & 0x1;
|
|
|
|
if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_3_3) {
|
|
AR5K_EEPROM_READ(o++, val);
|
|
ee->ee_false_detect[mode] = (val >> 6) & 0x7f;
|
|
|
|
if (mode == AR5K_EEPROM_MODE_11A)
|
|
ee->ee_xr_power[mode] = val & 0x3f;
|
|
else {
|
|
ee->ee_ob[mode][0] = val & 0x7;
|
|
ee->ee_db[mode][0] = (val >> 3) & 0x7;
|
|
}
|
|
}
|
|
|
|
if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_4) {
|
|
ee->ee_i_gain[mode] = AR5K_EEPROM_I_GAIN;
|
|
ee->ee_cck_ofdm_power_delta = AR5K_EEPROM_CCK_OFDM_DELTA;
|
|
} else {
|
|
ee->ee_i_gain[mode] = (val >> 13) & 0x7;
|
|
|
|
AR5K_EEPROM_READ(o++, val);
|
|
ee->ee_i_gain[mode] |= (val << 3) & 0x38;
|
|
|
|
if (mode == AR5K_EEPROM_MODE_11G)
|
|
ee->ee_cck_ofdm_power_delta = (val >> 3) & 0xff;
|
|
}
|
|
|
|
if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0 &&
|
|
mode == AR5K_EEPROM_MODE_11A) {
|
|
ee->ee_i_cal[mode] = (val >> 8) & 0x3f;
|
|
ee->ee_q_cal[mode] = (val >> 3) & 0x1f;
|
|
}
|
|
|
|
if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_6 &&
|
|
mode == AR5K_EEPROM_MODE_11G)
|
|
ee->ee_scaled_cck_delta = (val >> 11) & 0x1f;
|
|
|
|
/* return new offset */
|
|
*offset = o;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Initialize eeprom & capabilities structs
|
|
*/
|
|
static int ath5k_eeprom_init(struct ath5k_hw *ah)
|
|
{
|
|
struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
|
|
unsigned int mode, i;
|
|
int ret;
|
|
u32 offset;
|
|
u16 val;
|
|
|
|
/* Initial TX thermal adjustment values */
|
|
ee->ee_tx_clip = 4;
|
|
ee->ee_pwd_84 = ee->ee_pwd_90 = 1;
|
|
ee->ee_gain_select = 1;
|
|
|
|
/*
|
|
* Read values from EEPROM and store them in the capability structure
|
|
*/
|
|
AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MAGIC, ee_magic);
|
|
AR5K_EEPROM_READ_HDR(AR5K_EEPROM_PROTECT, ee_protect);
|
|
AR5K_EEPROM_READ_HDR(AR5K_EEPROM_REG_DOMAIN, ee_regdomain);
|
|
AR5K_EEPROM_READ_HDR(AR5K_EEPROM_VERSION, ee_version);
|
|
AR5K_EEPROM_READ_HDR(AR5K_EEPROM_HDR, ee_header);
|
|
|
|
/* Return if we have an old EEPROM */
|
|
if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_0)
|
|
return 0;
|
|
|
|
#ifdef notyet
|
|
/*
|
|
* Validate the checksum of the EEPROM date. There are some
|
|
* devices with invalid EEPROMs.
|
|
*/
|
|
for (cksum = 0, offset = 0; offset < AR5K_EEPROM_INFO_MAX; offset++) {
|
|
AR5K_EEPROM_READ(AR5K_EEPROM_INFO(offset), val);
|
|
cksum ^= val;
|
|
}
|
|
if (cksum != AR5K_EEPROM_INFO_CKSUM) {
|
|
ATH5K_ERR(ah->ah_sc, "Invalid EEPROM checksum 0x%04x\n", cksum);
|
|
return -EIO;
|
|
}
|
|
#endif
|
|
|
|
AR5K_EEPROM_READ_HDR(AR5K_EEPROM_ANT_GAIN(ah->ah_ee_version),
|
|
ee_ant_gain);
|
|
|
|
if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) {
|
|
AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC0, ee_misc0);
|
|
AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC1, ee_misc1);
|
|
}
|
|
|
|
if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_3) {
|
|
AR5K_EEPROM_READ(AR5K_EEPROM_OBDB0_2GHZ, val);
|
|
ee->ee_ob[AR5K_EEPROM_MODE_11B][0] = val & 0x7;
|
|
ee->ee_db[AR5K_EEPROM_MODE_11B][0] = (val >> 3) & 0x7;
|
|
|
|
AR5K_EEPROM_READ(AR5K_EEPROM_OBDB1_2GHZ, val);
|
|
ee->ee_ob[AR5K_EEPROM_MODE_11G][0] = val & 0x7;
|
|
ee->ee_db[AR5K_EEPROM_MODE_11G][0] = (val >> 3) & 0x7;
|
|
}
|
|
|
|
/*
|
|
* Get conformance test limit values
|
|
*/
|
|
offset = AR5K_EEPROM_CTL(ah->ah_ee_version);
|
|
ee->ee_ctls = AR5K_EEPROM_N_CTLS(ah->ah_ee_version);
|
|
|
|
for (i = 0; i < ee->ee_ctls; i++) {
|
|
AR5K_EEPROM_READ(offset++, val);
|
|
ee->ee_ctl[i] = (val >> 8) & 0xff;
|
|
ee->ee_ctl[i + 1] = val & 0xff;
|
|
}
|
|
|
|
/*
|
|
* Get values for 802.11a (5GHz)
|
|
*/
|
|
mode = AR5K_EEPROM_MODE_11A;
|
|
|
|
ee->ee_turbo_max_power[mode] =
|
|
AR5K_EEPROM_HDR_T_5GHZ_DBM(ee->ee_header);
|
|
|
|
offset = AR5K_EEPROM_MODES_11A(ah->ah_ee_version);
|
|
|
|
ret = ath5k_eeprom_read_ants(ah, &offset, mode);
|
|
if (ret)
|
|
return ret;
|
|
|
|
AR5K_EEPROM_READ(offset++, val);
|
|
ee->ee_adc_desired_size[mode] = (s8)((val >> 8) & 0xff);
|
|
ee->ee_ob[mode][3] = (val >> 5) & 0x7;
|
|
ee->ee_db[mode][3] = (val >> 2) & 0x7;
|
|
ee->ee_ob[mode][2] = (val << 1) & 0x7;
|
|
|
|
AR5K_EEPROM_READ(offset++, val);
|
|
ee->ee_ob[mode][2] |= (val >> 15) & 0x1;
|
|
ee->ee_db[mode][2] = (val >> 12) & 0x7;
|
|
ee->ee_ob[mode][1] = (val >> 9) & 0x7;
|
|
ee->ee_db[mode][1] = (val >> 6) & 0x7;
|
|
ee->ee_ob[mode][0] = (val >> 3) & 0x7;
|
|
ee->ee_db[mode][0] = val & 0x7;
|
|
|
|
ret = ath5k_eeprom_read_modes(ah, &offset, mode);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) {
|
|
AR5K_EEPROM_READ(offset++, val);
|
|
ee->ee_margin_tx_rx[mode] = val & 0x3f;
|
|
}
|
|
|
|
/*
|
|
* Get values for 802.11b (2.4GHz)
|
|
*/
|
|
mode = AR5K_EEPROM_MODE_11B;
|
|
offset = AR5K_EEPROM_MODES_11B(ah->ah_ee_version);
|
|
|
|
ret = ath5k_eeprom_read_ants(ah, &offset, mode);
|
|
if (ret)
|
|
return ret;
|
|
|
|
AR5K_EEPROM_READ(offset++, val);
|
|
ee->ee_adc_desired_size[mode] = (s8)((val >> 8) & 0xff);
|
|
ee->ee_ob[mode][1] = (val >> 4) & 0x7;
|
|
ee->ee_db[mode][1] = val & 0x7;
|
|
|
|
ret = ath5k_eeprom_read_modes(ah, &offset, mode);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) {
|
|
AR5K_EEPROM_READ(offset++, val);
|
|
ee->ee_cal_pier[mode][0] =
|
|
ath5k_eeprom_bin2freq(ah, val & 0xff, mode);
|
|
ee->ee_cal_pier[mode][1] =
|
|
ath5k_eeprom_bin2freq(ah, (val >> 8) & 0xff, mode);
|
|
|
|
AR5K_EEPROM_READ(offset++, val);
|
|
ee->ee_cal_pier[mode][2] =
|
|
ath5k_eeprom_bin2freq(ah, val & 0xff, mode);
|
|
}
|
|
|
|
if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1)
|
|
ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f;
|
|
|
|
/*
|
|
* Get values for 802.11g (2.4GHz)
|
|
*/
|
|
mode = AR5K_EEPROM_MODE_11G;
|
|
offset = AR5K_EEPROM_MODES_11G(ah->ah_ee_version);
|
|
|
|
ret = ath5k_eeprom_read_ants(ah, &offset, mode);
|
|
if (ret)
|
|
return ret;
|
|
|
|
AR5K_EEPROM_READ(offset++, val);
|
|
ee->ee_adc_desired_size[mode] = (s8)((val >> 8) & 0xff);
|
|
ee->ee_ob[mode][1] = (val >> 4) & 0x7;
|
|
ee->ee_db[mode][1] = val & 0x7;
|
|
|
|
ret = ath5k_eeprom_read_modes(ah, &offset, mode);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) {
|
|
AR5K_EEPROM_READ(offset++, val);
|
|
ee->ee_cal_pier[mode][0] =
|
|
ath5k_eeprom_bin2freq(ah, val & 0xff, mode);
|
|
ee->ee_cal_pier[mode][1] =
|
|
ath5k_eeprom_bin2freq(ah, (val >> 8) & 0xff, mode);
|
|
|
|
AR5K_EEPROM_READ(offset++, val);
|
|
ee->ee_turbo_max_power[mode] = val & 0x7f;
|
|
ee->ee_xr_power[mode] = (val >> 7) & 0x3f;
|
|
|
|
AR5K_EEPROM_READ(offset++, val);
|
|
ee->ee_cal_pier[mode][2] =
|
|
ath5k_eeprom_bin2freq(ah, val & 0xff, mode);
|
|
|
|
if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1)
|
|
ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f;
|
|
|
|
AR5K_EEPROM_READ(offset++, val);
|
|
ee->ee_i_cal[mode] = (val >> 8) & 0x3f;
|
|
ee->ee_q_cal[mode] = (val >> 3) & 0x1f;
|
|
|
|
if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_2) {
|
|
AR5K_EEPROM_READ(offset++, val);
|
|
ee->ee_cck_ofdm_gain_delta = val & 0xff;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Read 5GHz EEPROM channels
|
|
*/
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Read the MAC address from eeprom
|
|
*/
|
|
static int ath5k_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac)
|
|
{
|
|
u8 mac_d[ETH_ALEN];
|
|
u32 total, offset;
|
|
u16 data;
|
|
int octet, ret;
|
|
|
|
memset(mac, 0, ETH_ALEN);
|
|
memset(mac_d, 0, ETH_ALEN);
|
|
|
|
ret = ath5k_hw_eeprom_read(ah, 0x20, &data);
|
|
if (ret)
|
|
return ret;
|
|
|
|
for (offset = 0x1f, octet = 0, total = 0; offset >= 0x1d; offset--) {
|
|
ret = ath5k_hw_eeprom_read(ah, offset, &data);
|
|
if (ret)
|
|
return ret;
|
|
|
|
total += data;
|
|
mac_d[octet + 1] = data & 0xff;
|
|
mac_d[octet] = data >> 8;
|
|
octet += 2;
|
|
}
|
|
|
|
memcpy(mac, mac_d, ETH_ALEN);
|
|
|
|
if (!total || total == 3 * 0xffff)
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Read/Write regulatory domain
|
|
*/
|
|
static bool ath5k_eeprom_regulation_domain(struct ath5k_hw *ah, bool write,
|
|
enum ath5k_regdom *regdomain)
|
|
{
|
|
u16 ee_regdomain;
|
|
|
|
/* Read current value */
|
|
if (write != true) {
|
|
ee_regdomain = ah->ah_capabilities.cap_eeprom.ee_regdomain;
|
|
*regdomain = ath5k_regdom_to_ieee(ee_regdomain);
|
|
return true;
|
|
}
|
|
|
|
ee_regdomain = ath5k_regdom_from_ieee(*regdomain);
|
|
|
|
/* Try to write a new value */
|
|
if (ah->ah_capabilities.cap_eeprom.ee_protect &
|
|
AR5K_EEPROM_PROTECT_WR_128_191)
|
|
return false;
|
|
if (ath5k_hw_eeprom_write(ah, AR5K_EEPROM_REG_DOMAIN, ee_regdomain)!=0)
|
|
return false;
|
|
|
|
ah->ah_capabilities.cap_eeprom.ee_regdomain = ee_regdomain;
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Use the above to write a new regulatory domain
|
|
*/
|
|
int ath5k_hw_set_regdomain(struct ath5k_hw *ah, u16 regdomain)
|
|
{
|
|
enum ath5k_regdom ieee_regdomain;
|
|
|
|
ieee_regdomain = ath5k_regdom_to_ieee(regdomain);
|
|
|
|
if (ath5k_eeprom_regulation_domain(ah, true, &ieee_regdomain) == true)
|
|
return 0;
|
|
|
|
return -EIO;
|
|
}
|
|
|
|
/*
|
|
* Fill the capabilities struct
|
|
*/
|
|
static int ath5k_hw_get_capabilities(struct ath5k_hw *ah)
|
|
{
|
|
u16 ee_header;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
/* Capabilities stored in the EEPROM */
|
|
ee_header = ah->ah_capabilities.cap_eeprom.ee_header;
|
|
|
|
if (ah->ah_version == AR5K_AR5210) {
|
|
/*
|
|
* Set radio capabilities
|
|
* (The AR5110 only supports the middle 5GHz band)
|
|
*/
|
|
ah->ah_capabilities.cap_range.range_5ghz_min = 5120;
|
|
ah->ah_capabilities.cap_range.range_5ghz_max = 5430;
|
|
ah->ah_capabilities.cap_range.range_2ghz_min = 0;
|
|
ah->ah_capabilities.cap_range.range_2ghz_max = 0;
|
|
|
|
/* Set supported modes */
|
|
__set_bit(MODE_IEEE80211A, ah->ah_capabilities.cap_mode);
|
|
__set_bit(MODE_ATHEROS_TURBO, ah->ah_capabilities.cap_mode);
|
|
} else {
|
|
/*
|
|
* XXX The tranceiver supports frequencies from 4920 to 6100GHz
|
|
* XXX and from 2312 to 2732GHz. There are problems with the
|
|
* XXX current ieee80211 implementation because the IEEE
|
|
* XXX channel mapping does not support negative channel
|
|
* XXX numbers (2312MHz is channel -19). Of course, this
|
|
* XXX doesn't matter because these channels are out of range
|
|
* XXX but some regulation domains like MKK (Japan) will
|
|
* XXX support frequencies somewhere around 4.8GHz.
|
|
*/
|
|
|
|
/*
|
|
* Set radio capabilities
|
|
*/
|
|
|
|
if (AR5K_EEPROM_HDR_11A(ee_header)) {
|
|
ah->ah_capabilities.cap_range.range_5ghz_min = 5005; /* 4920 */
|
|
ah->ah_capabilities.cap_range.range_5ghz_max = 6100;
|
|
|
|
/* Set supported modes */
|
|
__set_bit(MODE_IEEE80211A,
|
|
ah->ah_capabilities.cap_mode);
|
|
__set_bit(MODE_ATHEROS_TURBO,
|
|
ah->ah_capabilities.cap_mode);
|
|
if (ah->ah_version == AR5K_AR5212)
|
|
__set_bit(MODE_ATHEROS_TURBOG,
|
|
ah->ah_capabilities.cap_mode);
|
|
}
|
|
|
|
/* Enable 802.11b if a 2GHz capable radio (2111/5112) is
|
|
* connected */
|
|
if (AR5K_EEPROM_HDR_11B(ee_header) ||
|
|
AR5K_EEPROM_HDR_11G(ee_header)) {
|
|
ah->ah_capabilities.cap_range.range_2ghz_min = 2412; /* 2312 */
|
|
ah->ah_capabilities.cap_range.range_2ghz_max = 2732;
|
|
|
|
if (AR5K_EEPROM_HDR_11B(ee_header))
|
|
__set_bit(MODE_IEEE80211B,
|
|
ah->ah_capabilities.cap_mode);
|
|
|
|
if (AR5K_EEPROM_HDR_11G(ee_header))
|
|
__set_bit(MODE_IEEE80211G,
|
|
ah->ah_capabilities.cap_mode);
|
|
}
|
|
}
|
|
|
|
/* GPIO */
|
|
ah->ah_gpio_npins = AR5K_NUM_GPIO;
|
|
|
|
/* Set number of supported TX queues */
|
|
if (ah->ah_version == AR5K_AR5210)
|
|
ah->ah_capabilities.cap_queues.q_tx_num =
|
|
AR5K_NUM_TX_QUEUES_NOQCU;
|
|
else
|
|
ah->ah_capabilities.cap_queues.q_tx_num = AR5K_NUM_TX_QUEUES;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*********************************\
|
|
Protocol Control Unit Functions
|
|
\*********************************/
|
|
|
|
/*
|
|
* Set Operation mode
|
|
*/
|
|
int ath5k_hw_set_opmode(struct ath5k_hw *ah)
|
|
{
|
|
u32 pcu_reg, beacon_reg, low_id, high_id;
|
|
|
|
pcu_reg = 0;
|
|
beacon_reg = 0;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
|
|
switch (ah->ah_op_mode) {
|
|
case IEEE80211_IF_TYPE_IBSS:
|
|
pcu_reg |= AR5K_STA_ID1_ADHOC | AR5K_STA_ID1_DESC_ANTENNA |
|
|
(ah->ah_version == AR5K_AR5210 ?
|
|
AR5K_STA_ID1_NO_PSPOLL : 0);
|
|
beacon_reg |= AR5K_BCR_ADHOC;
|
|
break;
|
|
|
|
case IEEE80211_IF_TYPE_AP:
|
|
pcu_reg |= AR5K_STA_ID1_AP | AR5K_STA_ID1_RTS_DEF_ANTENNA |
|
|
(ah->ah_version == AR5K_AR5210 ?
|
|
AR5K_STA_ID1_NO_PSPOLL : 0);
|
|
beacon_reg |= AR5K_BCR_AP;
|
|
break;
|
|
|
|
case IEEE80211_IF_TYPE_STA:
|
|
pcu_reg |= AR5K_STA_ID1_DEFAULT_ANTENNA |
|
|
(ah->ah_version == AR5K_AR5210 ?
|
|
AR5K_STA_ID1_PWR_SV : 0);
|
|
case IEEE80211_IF_TYPE_MNTR:
|
|
pcu_reg |= AR5K_STA_ID1_DEFAULT_ANTENNA |
|
|
(ah->ah_version == AR5K_AR5210 ?
|
|
AR5K_STA_ID1_NO_PSPOLL : 0);
|
|
break;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* Set PCU registers
|
|
*/
|
|
low_id = AR5K_LOW_ID(ah->ah_sta_id);
|
|
high_id = AR5K_HIGH_ID(ah->ah_sta_id);
|
|
ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0);
|
|
ath5k_hw_reg_write(ah, pcu_reg | high_id, AR5K_STA_ID1);
|
|
|
|
/*
|
|
* Set Beacon Control Register on 5210
|
|
*/
|
|
if (ah->ah_version == AR5K_AR5210)
|
|
ath5k_hw_reg_write(ah, beacon_reg, AR5K_BCR);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* BSSID Functions
|
|
*/
|
|
|
|
/*
|
|
* Get station id
|
|
*/
|
|
void ath5k_hw_get_lladdr(struct ath5k_hw *ah, u8 *mac)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
memcpy(mac, ah->ah_sta_id, ETH_ALEN);
|
|
}
|
|
|
|
/*
|
|
* Set station id
|
|
*/
|
|
int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac)
|
|
{
|
|
u32 low_id, high_id;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
/* Set new station ID */
|
|
memcpy(ah->ah_sta_id, mac, ETH_ALEN);
|
|
|
|
low_id = AR5K_LOW_ID(mac);
|
|
high_id = AR5K_HIGH_ID(mac);
|
|
|
|
ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0);
|
|
ath5k_hw_reg_write(ah, high_id, AR5K_STA_ID1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Set BSSID
|
|
*/
|
|
void ath5k_hw_set_associd(struct ath5k_hw *ah, const u8 *bssid, u16 assoc_id)
|
|
{
|
|
u32 low_id, high_id;
|
|
u16 tim_offset = 0;
|
|
|
|
/*
|
|
* Set simple BSSID mask on 5212
|
|
*/
|
|
if (ah->ah_version == AR5K_AR5212) {
|
|
ath5k_hw_reg_write(ah, 0xfffffff, AR5K_BSS_IDM0);
|
|
ath5k_hw_reg_write(ah, 0xfffffff, AR5K_BSS_IDM1);
|
|
}
|
|
|
|
/*
|
|
* Set BSSID which triggers the "SME Join" operation
|
|
*/
|
|
low_id = AR5K_LOW_ID(bssid);
|
|
high_id = AR5K_HIGH_ID(bssid);
|
|
ath5k_hw_reg_write(ah, low_id, AR5K_BSS_ID0);
|
|
ath5k_hw_reg_write(ah, high_id | ((assoc_id & 0x3fff) <<
|
|
AR5K_BSS_ID1_AID_S), AR5K_BSS_ID1);
|
|
|
|
if (assoc_id == 0) {
|
|
ath5k_hw_disable_pspoll(ah);
|
|
return;
|
|
}
|
|
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_BEACON, AR5K_BEACON_TIM,
|
|
tim_offset ? tim_offset + 4 : 0);
|
|
|
|
ath5k_hw_enable_pspoll(ah, NULL, 0);
|
|
}
|
|
/**
|
|
* ath5k_hw_set_bssid_mask - set common bits we should listen to
|
|
*
|
|
* The bssid_mask is a utility used by AR5212 hardware to inform the hardware
|
|
* which bits of the interface's MAC address should be looked at when trying
|
|
* to decide which packets to ACK. In station mode every bit matters. In AP
|
|
* mode with a single BSS every bit matters as well. In AP mode with
|
|
* multiple BSSes not every bit matters.
|
|
*
|
|
* @ah: the &struct ath5k_hw
|
|
* @mask: the bssid_mask, a u8 array of size ETH_ALEN
|
|
*
|
|
* Note that this is a simple filter and *does* not filter out all
|
|
* relevant frames. Some non-relevant frames will get through, probability
|
|
* jocks are welcomed to compute.
|
|
*
|
|
* When handling multiple BSSes (or VAPs) you can get the BSSID mask by
|
|
* computing the set of:
|
|
*
|
|
* ~ ( MAC XOR BSSID )
|
|
*
|
|
* When you do this you are essentially computing the common bits. Later it
|
|
* is assumed the harware will "and" (&) the BSSID mask with the MAC address
|
|
* to obtain the relevant bits which should match on the destination frame.
|
|
*
|
|
* Simple example: on your card you have have two BSSes you have created with
|
|
* BSSID-01 and BSSID-02. Lets assume BSSID-01 will not use the MAC address.
|
|
* There is another BSSID-03 but you are not part of it. For simplicity's sake,
|
|
* assuming only 4 bits for a mac address and for BSSIDs you can then have:
|
|
*
|
|
* \
|
|
* MAC: 0001 |
|
|
* BSSID-01: 0100 | --> Belongs to us
|
|
* BSSID-02: 1001 |
|
|
* /
|
|
* -------------------
|
|
* BSSID-03: 0110 | --> External
|
|
* -------------------
|
|
*
|
|
* Our bssid_mask would then be:
|
|
*
|
|
* On loop iteration for BSSID-01:
|
|
* ~(0001 ^ 0100) -> ~(0101)
|
|
* -> 1010
|
|
* bssid_mask = 1010
|
|
*
|
|
* On loop iteration for BSSID-02:
|
|
* bssid_mask &= ~(0001 ^ 1001)
|
|
* bssid_mask = (1010) & ~(0001 ^ 1001)
|
|
* bssid_mask = (1010) & ~(1001)
|
|
* bssid_mask = (1010) & (0110)
|
|
* bssid_mask = 0010
|
|
*
|
|
* A bssid_mask of 0010 means "only pay attention to the second least
|
|
* significant bit". This is because its the only bit common
|
|
* amongst the MAC and all BSSIDs we support. To findout what the real
|
|
* common bit is we can simply "&" the bssid_mask now with any BSSID we have
|
|
* or our MAC address (we assume the hardware uses the MAC address).
|
|
*
|
|
* Now, suppose there's an incoming frame for BSSID-03:
|
|
*
|
|
* IFRAME-01: 0110
|
|
*
|
|
* An easy eye-inspeciton of this already should tell you that this frame
|
|
* will not pass our check. This is beacuse the bssid_mask tells the
|
|
* hardware to only look at the second least significant bit and the
|
|
* common bit amongst the MAC and BSSIDs is 0, this frame has the 2nd LSB
|
|
* as 1, which does not match 0.
|
|
*
|
|
* So with IFRAME-01 we *assume* the hardware will do:
|
|
*
|
|
* allow = (IFRAME-01 & bssid_mask) == (bssid_mask & MAC) ? 1 : 0;
|
|
* --> allow = (0110 & 0010) == (0010 & 0001) ? 1 : 0;
|
|
* --> allow = (0010) == 0000 ? 1 : 0;
|
|
* --> allow = 0
|
|
*
|
|
* Lets now test a frame that should work:
|
|
*
|
|
* IFRAME-02: 0001 (we should allow)
|
|
*
|
|
* allow = (0001 & 1010) == 1010
|
|
*
|
|
* allow = (IFRAME-02 & bssid_mask) == (bssid_mask & MAC) ? 1 : 0;
|
|
* --> allow = (0001 & 0010) == (0010 & 0001) ? 1 :0;
|
|
* --> allow = (0010) == (0010)
|
|
* --> allow = 1
|
|
*
|
|
* Other examples:
|
|
*
|
|
* IFRAME-03: 0100 --> allowed
|
|
* IFRAME-04: 1001 --> allowed
|
|
* IFRAME-05: 1101 --> allowed but its not for us!!!
|
|
*
|
|
*/
|
|
int ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask)
|
|
{
|
|
u32 low_id, high_id;
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
|
|
if (ah->ah_version == AR5K_AR5212) {
|
|
low_id = AR5K_LOW_ID(mask);
|
|
high_id = AR5K_HIGH_ID(mask);
|
|
|
|
ath5k_hw_reg_write(ah, low_id, AR5K_BSS_IDM0);
|
|
ath5k_hw_reg_write(ah, high_id, AR5K_BSS_IDM1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
return -EIO;
|
|
}
|
|
|
|
/*
|
|
* Receive start/stop functions
|
|
*/
|
|
|
|
/*
|
|
* Start receive on PCU
|
|
*/
|
|
void ath5k_hw_start_rx_pcu(struct ath5k_hw *ah)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW, AR5K_DIAG_SW_DIS_RX);
|
|
}
|
|
|
|
/*
|
|
* Stop receive on PCU
|
|
*/
|
|
void ath5k_hw_stop_pcu_recv(struct ath5k_hw *ah)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW, AR5K_DIAG_SW_DIS_RX);
|
|
}
|
|
|
|
/*
|
|
* RX Filter functions
|
|
*/
|
|
|
|
/*
|
|
* Set multicast filter
|
|
*/
|
|
void ath5k_hw_set_mcast_filter(struct ath5k_hw *ah, u32 filter0, u32 filter1)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
/* Set the multicat filter */
|
|
ath5k_hw_reg_write(ah, filter0, AR5K_MCAST_FILTER0);
|
|
ath5k_hw_reg_write(ah, filter1, AR5K_MCAST_FILTER1);
|
|
}
|
|
|
|
/*
|
|
* Set multicast filter by index
|
|
*/
|
|
int ath5k_hw_set_mcast_filterindex(struct ath5k_hw *ah, u32 index)
|
|
{
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
if (index >= 64)
|
|
return -EINVAL;
|
|
else if (index >= 32)
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_MCAST_FILTER1,
|
|
(1 << (index - 32)));
|
|
else
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_MCAST_FILTER0, (1 << index));
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Clear Multicast filter by index
|
|
*/
|
|
int ath5k_hw_clear_mcast_filter_idx(struct ath5k_hw *ah, u32 index)
|
|
{
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
if (index >= 64)
|
|
return -EINVAL;
|
|
else if (index >= 32)
|
|
AR5K_REG_DISABLE_BITS(ah, AR5K_MCAST_FILTER1,
|
|
(1 << (index - 32)));
|
|
else
|
|
AR5K_REG_DISABLE_BITS(ah, AR5K_MCAST_FILTER0, (1 << index));
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Get current rx filter
|
|
*/
|
|
u32 ath5k_hw_get_rx_filter(struct ath5k_hw *ah)
|
|
{
|
|
u32 data, filter = 0;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
filter = ath5k_hw_reg_read(ah, AR5K_RX_FILTER);
|
|
|
|
/*Radar detection for 5212*/
|
|
if (ah->ah_version == AR5K_AR5212) {
|
|
data = ath5k_hw_reg_read(ah, AR5K_PHY_ERR_FIL);
|
|
|
|
if (data & AR5K_PHY_ERR_FIL_RADAR)
|
|
filter |= AR5K_RX_FILTER_RADARERR;
|
|
if (data & (AR5K_PHY_ERR_FIL_OFDM | AR5K_PHY_ERR_FIL_CCK))
|
|
filter |= AR5K_RX_FILTER_PHYERR;
|
|
}
|
|
|
|
return filter;
|
|
}
|
|
|
|
/*
|
|
* Set rx filter
|
|
*/
|
|
void ath5k_hw_set_rx_filter(struct ath5k_hw *ah, u32 filter)
|
|
{
|
|
u32 data = 0;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
|
|
/* Set PHY error filter register on 5212*/
|
|
if (ah->ah_version == AR5K_AR5212) {
|
|
if (filter & AR5K_RX_FILTER_RADARERR)
|
|
data |= AR5K_PHY_ERR_FIL_RADAR;
|
|
if (filter & AR5K_RX_FILTER_PHYERR)
|
|
data |= AR5K_PHY_ERR_FIL_OFDM | AR5K_PHY_ERR_FIL_CCK;
|
|
}
|
|
|
|
/*
|
|
* The AR5210 uses promiscous mode to detect radar activity
|
|
*/
|
|
if (ah->ah_version == AR5K_AR5210 &&
|
|
(filter & AR5K_RX_FILTER_RADARERR)) {
|
|
filter &= ~AR5K_RX_FILTER_RADARERR;
|
|
filter |= AR5K_RX_FILTER_PROM;
|
|
}
|
|
|
|
/*Zero length DMA*/
|
|
if (data)
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_ZLFDMA);
|
|
else
|
|
AR5K_REG_DISABLE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_ZLFDMA);
|
|
|
|
/*Write RX Filter register*/
|
|
ath5k_hw_reg_write(ah, filter & 0xff, AR5K_RX_FILTER);
|
|
|
|
/*Write PHY error filter register on 5212*/
|
|
if (ah->ah_version == AR5K_AR5212)
|
|
ath5k_hw_reg_write(ah, data, AR5K_PHY_ERR_FIL);
|
|
|
|
}
|
|
|
|
/*
|
|
* Beacon related functions
|
|
*/
|
|
|
|
/*
|
|
* Get a 32bit TSF
|
|
*/
|
|
u32 ath5k_hw_get_tsf32(struct ath5k_hw *ah)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
return ath5k_hw_reg_read(ah, AR5K_TSF_L32);
|
|
}
|
|
|
|
/*
|
|
* Get the full 64bit TSF
|
|
*/
|
|
u64 ath5k_hw_get_tsf64(struct ath5k_hw *ah)
|
|
{
|
|
u64 tsf = ath5k_hw_reg_read(ah, AR5K_TSF_U32);
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
|
|
return ath5k_hw_reg_read(ah, AR5K_TSF_L32) | (tsf << 32);
|
|
}
|
|
|
|
/*
|
|
* Force a TSF reset
|
|
*/
|
|
void ath5k_hw_reset_tsf(struct ath5k_hw *ah)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_RESET_TSF);
|
|
}
|
|
|
|
/*
|
|
* Initialize beacon timers
|
|
*/
|
|
void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval)
|
|
{
|
|
u32 timer1, timer2, timer3;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
/*
|
|
* Set the additional timers by mode
|
|
*/
|
|
switch (ah->ah_op_mode) {
|
|
case IEEE80211_IF_TYPE_STA:
|
|
if (ah->ah_version == AR5K_AR5210) {
|
|
timer1 = 0xffffffff;
|
|
timer2 = 0xffffffff;
|
|
} else {
|
|
timer1 = 0x0000ffff;
|
|
timer2 = 0x0007ffff;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
timer1 = (next_beacon - AR5K_TUNE_DMA_BEACON_RESP) << 3;
|
|
timer2 = (next_beacon - AR5K_TUNE_SW_BEACON_RESP) << 3;
|
|
}
|
|
|
|
timer3 = next_beacon + (ah->ah_atim_window ? ah->ah_atim_window : 1);
|
|
|
|
/*
|
|
* Set the beacon register and enable all timers.
|
|
* (next beacon, DMA beacon, software beacon, ATIM window time)
|
|
*/
|
|
ath5k_hw_reg_write(ah, next_beacon, AR5K_TIMER0);
|
|
ath5k_hw_reg_write(ah, timer1, AR5K_TIMER1);
|
|
ath5k_hw_reg_write(ah, timer2, AR5K_TIMER2);
|
|
ath5k_hw_reg_write(ah, timer3, AR5K_TIMER3);
|
|
|
|
ath5k_hw_reg_write(ah, interval & (AR5K_BEACON_PERIOD |
|
|
AR5K_BEACON_RESET_TSF | AR5K_BEACON_ENABLE),
|
|
AR5K_BEACON);
|
|
}
|
|
|
|
#if 0
|
|
/*
|
|
* Set beacon timers
|
|
*/
|
|
int ath5k_hw_set_beacon_timers(struct ath5k_hw *ah,
|
|
const struct ath5k_beacon_state *state)
|
|
{
|
|
u32 cfp_period, next_cfp, dtim, interval, next_beacon;
|
|
|
|
/*
|
|
* TODO: should be changed through *state
|
|
* review struct ath5k_beacon_state struct
|
|
*
|
|
* XXX: These are used for cfp period bellow, are they
|
|
* ok ? Is it O.K. for tsf here to be 0 or should we use
|
|
* get_tsf ?
|
|
*/
|
|
u32 dtim_count = 0; /* XXX */
|
|
u32 cfp_count = 0; /* XXX */
|
|
u32 tsf = 0; /* XXX */
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
/* Return on an invalid beacon state */
|
|
if (state->bs_interval < 1)
|
|
return -EINVAL;
|
|
|
|
interval = state->bs_interval;
|
|
dtim = state->bs_dtim_period;
|
|
|
|
/*
|
|
* PCF support?
|
|
*/
|
|
if (state->bs_cfp_period > 0) {
|
|
/*
|
|
* Enable PCF mode and set the CFP
|
|
* (Contention Free Period) and timer registers
|
|
*/
|
|
cfp_period = state->bs_cfp_period * state->bs_dtim_period *
|
|
state->bs_interval;
|
|
next_cfp = (cfp_count * state->bs_dtim_period + dtim_count) *
|
|
state->bs_interval;
|
|
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1,
|
|
AR5K_STA_ID1_DEFAULT_ANTENNA |
|
|
AR5K_STA_ID1_PCF);
|
|
ath5k_hw_reg_write(ah, cfp_period, AR5K_CFP_PERIOD);
|
|
ath5k_hw_reg_write(ah, state->bs_cfp_max_duration,
|
|
AR5K_CFP_DUR);
|
|
ath5k_hw_reg_write(ah, (tsf + (next_cfp == 0 ? cfp_period :
|
|
next_cfp)) << 3, AR5K_TIMER2);
|
|
} else {
|
|
/* Disable PCF mode */
|
|
AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1,
|
|
AR5K_STA_ID1_DEFAULT_ANTENNA |
|
|
AR5K_STA_ID1_PCF);
|
|
}
|
|
|
|
/*
|
|
* Enable the beacon timer register
|
|
*/
|
|
ath5k_hw_reg_write(ah, state->bs_next_beacon, AR5K_TIMER0);
|
|
|
|
/*
|
|
* Start the beacon timers
|
|
*/
|
|
ath5k_hw_reg_write(ah, (ath5k_hw_reg_read(ah, AR5K_BEACON) &~
|
|
(AR5K_BEACON_PERIOD | AR5K_BEACON_TIM)) |
|
|
AR5K_REG_SM(state->bs_tim_offset ? state->bs_tim_offset + 4 : 0,
|
|
AR5K_BEACON_TIM) | AR5K_REG_SM(state->bs_interval,
|
|
AR5K_BEACON_PERIOD), AR5K_BEACON);
|
|
|
|
/*
|
|
* Write new beacon miss threshold, if it appears to be valid
|
|
* XXX: Figure out right values for min <= bs_bmiss_threshold <= max
|
|
* and return if its not in range. We can test this by reading value and
|
|
* setting value to a largest value and seeing which values register.
|
|
*/
|
|
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_RSSI_THR, AR5K_RSSI_THR_BMISS,
|
|
state->bs_bmiss_threshold);
|
|
|
|
/*
|
|
* Set sleep control register
|
|
* XXX: Didn't find this in 5210 code but since this register
|
|
* exists also in ar5k's 5210 headers i leave it as common code.
|
|
*/
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_SLEEP_CTL, AR5K_SLEEP_CTL_SLDUR,
|
|
(state->bs_sleep_duration - 3) << 3);
|
|
|
|
/*
|
|
* Set enhanced sleep registers on 5212
|
|
*/
|
|
if (ah->ah_version == AR5K_AR5212) {
|
|
if (state->bs_sleep_duration > state->bs_interval &&
|
|
roundup(state->bs_sleep_duration, interval) ==
|
|
state->bs_sleep_duration)
|
|
interval = state->bs_sleep_duration;
|
|
|
|
if (state->bs_sleep_duration > dtim && (dtim == 0 ||
|
|
roundup(state->bs_sleep_duration, dtim) ==
|
|
state->bs_sleep_duration))
|
|
dtim = state->bs_sleep_duration;
|
|
|
|
if (interval > dtim)
|
|
return -EINVAL;
|
|
|
|
next_beacon = interval == dtim ? state->bs_next_dtim :
|
|
state->bs_next_beacon;
|
|
|
|
ath5k_hw_reg_write(ah,
|
|
AR5K_REG_SM((state->bs_next_dtim - 3) << 3,
|
|
AR5K_SLEEP0_NEXT_DTIM) |
|
|
AR5K_REG_SM(10, AR5K_SLEEP0_CABTO) |
|
|
AR5K_SLEEP0_ENH_SLEEP_EN |
|
|
AR5K_SLEEP0_ASSUME_DTIM, AR5K_SLEEP0);
|
|
|
|
ath5k_hw_reg_write(ah, AR5K_REG_SM((next_beacon - 3) << 3,
|
|
AR5K_SLEEP1_NEXT_TIM) |
|
|
AR5K_REG_SM(10, AR5K_SLEEP1_BEACON_TO), AR5K_SLEEP1);
|
|
|
|
ath5k_hw_reg_write(ah,
|
|
AR5K_REG_SM(interval, AR5K_SLEEP2_TIM_PER) |
|
|
AR5K_REG_SM(dtim, AR5K_SLEEP2_DTIM_PER), AR5K_SLEEP2);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Reset beacon timers
|
|
*/
|
|
void ath5k_hw_reset_beacon(struct ath5k_hw *ah)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
/*
|
|
* Disable beacon timer
|
|
*/
|
|
ath5k_hw_reg_write(ah, 0, AR5K_TIMER0);
|
|
|
|
/*
|
|
* Disable some beacon register values
|
|
*/
|
|
AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1,
|
|
AR5K_STA_ID1_DEFAULT_ANTENNA | AR5K_STA_ID1_PCF);
|
|
ath5k_hw_reg_write(ah, AR5K_BEACON_PERIOD, AR5K_BEACON);
|
|
}
|
|
|
|
/*
|
|
* Wait for beacon queue to finish
|
|
*/
|
|
int ath5k_hw_beaconq_finish(struct ath5k_hw *ah, unsigned long phys_addr)
|
|
{
|
|
unsigned int i;
|
|
int ret;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
|
|
/* 5210 doesn't have QCU*/
|
|
if (ah->ah_version == AR5K_AR5210) {
|
|
/*
|
|
* Wait for beaconn queue to finish by checking
|
|
* Control Register and Beacon Status Register.
|
|
*/
|
|
for (i = AR5K_TUNE_BEACON_INTERVAL / 2; i > 0; i--) {
|
|
if (!(ath5k_hw_reg_read(ah, AR5K_BSR) & AR5K_BSR_TXQ1F)
|
|
||
|
|
!(ath5k_hw_reg_read(ah, AR5K_CR) & AR5K_BSR_TXQ1F))
|
|
break;
|
|
udelay(10);
|
|
}
|
|
|
|
/* Timeout... */
|
|
if (i <= 0) {
|
|
/*
|
|
* Re-schedule the beacon queue
|
|
*/
|
|
ath5k_hw_reg_write(ah, phys_addr, AR5K_NOQCU_TXDP1);
|
|
ath5k_hw_reg_write(ah, AR5K_BCR_TQ1V | AR5K_BCR_BDMAE,
|
|
AR5K_BCR);
|
|
|
|
return -EIO;
|
|
}
|
|
ret = 0;
|
|
} else {
|
|
/*5211/5212*/
|
|
ret = ath5k_hw_register_timeout(ah,
|
|
AR5K_QUEUE_STATUS(AR5K_TX_QUEUE_ID_BEACON),
|
|
AR5K_QCU_STS_FRMPENDCNT, 0, false);
|
|
|
|
if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, AR5K_TX_QUEUE_ID_BEACON))
|
|
return -EIO;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Update mib counters (statistics)
|
|
*/
|
|
void ath5k_hw_update_mib_counters(struct ath5k_hw *ah,
|
|
struct ath5k_mib_stats *statistics)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
/* Read-And-Clear */
|
|
statistics->ackrcv_bad += ath5k_hw_reg_read(ah, AR5K_ACK_FAIL);
|
|
statistics->rts_bad += ath5k_hw_reg_read(ah, AR5K_RTS_FAIL);
|
|
statistics->rts_good += ath5k_hw_reg_read(ah, AR5K_RTS_OK);
|
|
statistics->fcs_bad += ath5k_hw_reg_read(ah, AR5K_FCS_FAIL);
|
|
statistics->beacons += ath5k_hw_reg_read(ah, AR5K_BEACON_CNT);
|
|
|
|
/* Reset profile count registers on 5212*/
|
|
if (ah->ah_version == AR5K_AR5212) {
|
|
ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_TX);
|
|
ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_RX);
|
|
ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_RXCLR);
|
|
ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_CYCLE);
|
|
}
|
|
}
|
|
|
|
/** ath5k_hw_set_ack_bitrate - set bitrate for ACKs
|
|
*
|
|
* @ah: the &struct ath5k_hw
|
|
* @high: determines if to use low bit rate or now
|
|
*/
|
|
void ath5k_hw_set_ack_bitrate_high(struct ath5k_hw *ah, bool high)
|
|
{
|
|
if (ah->ah_version != AR5K_AR5212)
|
|
return;
|
|
else {
|
|
u32 val = AR5K_STA_ID1_BASE_RATE_11B | AR5K_STA_ID1_ACKCTS_6MB;
|
|
if (high)
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1, val);
|
|
else
|
|
AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, val);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* ACK/CTS Timeouts
|
|
*/
|
|
|
|
/*
|
|
* Set ACK timeout on PCU
|
|
*/
|
|
int ath5k_hw_set_ack_timeout(struct ath5k_hw *ah, unsigned int timeout)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
if (ath5k_hw_clocktoh(AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_ACK),
|
|
ah->ah_turbo) <= timeout)
|
|
return -EINVAL;
|
|
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_TIME_OUT, AR5K_TIME_OUT_ACK,
|
|
ath5k_hw_htoclock(timeout, ah->ah_turbo));
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Read the ACK timeout from PCU
|
|
*/
|
|
unsigned int ath5k_hw_get_ack_timeout(struct ath5k_hw *ah)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
|
|
return ath5k_hw_clocktoh(AR5K_REG_MS(ath5k_hw_reg_read(ah,
|
|
AR5K_TIME_OUT), AR5K_TIME_OUT_ACK), ah->ah_turbo);
|
|
}
|
|
|
|
/*
|
|
* Set CTS timeout on PCU
|
|
*/
|
|
int ath5k_hw_set_cts_timeout(struct ath5k_hw *ah, unsigned int timeout)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
if (ath5k_hw_clocktoh(AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_CTS),
|
|
ah->ah_turbo) <= timeout)
|
|
return -EINVAL;
|
|
|
|
AR5K_REG_WRITE_BITS(ah, AR5K_TIME_OUT, AR5K_TIME_OUT_CTS,
|
|
ath5k_hw_htoclock(timeout, ah->ah_turbo));
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Read CTS timeout from PCU
|
|
*/
|
|
unsigned int ath5k_hw_get_cts_timeout(struct ath5k_hw *ah)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
return ath5k_hw_clocktoh(AR5K_REG_MS(ath5k_hw_reg_read(ah,
|
|
AR5K_TIME_OUT), AR5K_TIME_OUT_CTS), ah->ah_turbo);
|
|
}
|
|
|
|
/*
|
|
* Key table (WEP) functions
|
|
*/
|
|
|
|
int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry)
|
|
{
|
|
unsigned int i;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE);
|
|
|
|
for (i = 0; i < AR5K_KEYCACHE_SIZE; i++)
|
|
ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_OFF(entry, i));
|
|
|
|
/* Set NULL encryption on non-5210*/
|
|
if (ah->ah_version != AR5K_AR5210)
|
|
ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL,
|
|
AR5K_KEYTABLE_TYPE(entry));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ath5k_hw_is_key_valid(struct ath5k_hw *ah, u16 entry)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE);
|
|
|
|
/* Check the validation flag at the end of the entry */
|
|
return ath5k_hw_reg_read(ah, AR5K_KEYTABLE_MAC1(entry)) &
|
|
AR5K_KEYTABLE_VALID;
|
|
}
|
|
|
|
int ath5k_hw_set_key(struct ath5k_hw *ah, u16 entry,
|
|
const struct ieee80211_key_conf *key, const u8 *mac)
|
|
{
|
|
unsigned int i;
|
|
__le32 key_v[5] = {};
|
|
u32 keytype;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
|
|
/* key->keylen comes in from mac80211 in bytes */
|
|
|
|
if (key->keylen > AR5K_KEYTABLE_SIZE / 8)
|
|
return -EOPNOTSUPP;
|
|
|
|
switch (key->keylen) {
|
|
/* WEP 40-bit = 40-bit entered key + 24 bit IV = 64-bit */
|
|
case 40 / 8:
|
|
memcpy(&key_v[0], key->key, 5);
|
|
keytype = AR5K_KEYTABLE_TYPE_40;
|
|
break;
|
|
|
|
/* WEP 104-bit = 104-bit entered key + 24-bit IV = 128-bit */
|
|
case 104 / 8:
|
|
memcpy(&key_v[0], &key->key[0], 6);
|
|
memcpy(&key_v[2], &key->key[6], 6);
|
|
memcpy(&key_v[4], &key->key[12], 1);
|
|
keytype = AR5K_KEYTABLE_TYPE_104;
|
|
break;
|
|
/* WEP 128-bit = 128-bit entered key + 24 bit IV = 152-bit */
|
|
case 128 / 8:
|
|
memcpy(&key_v[0], &key->key[0], 6);
|
|
memcpy(&key_v[2], &key->key[6], 6);
|
|
memcpy(&key_v[4], &key->key[12], 4);
|
|
keytype = AR5K_KEYTABLE_TYPE_128;
|
|
break;
|
|
|
|
default:
|
|
return -EINVAL; /* shouldn't happen */
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(key_v); i++)
|
|
ath5k_hw_reg_write(ah, le32_to_cpu(key_v[i]),
|
|
AR5K_KEYTABLE_OFF(entry, i));
|
|
|
|
ath5k_hw_reg_write(ah, keytype, AR5K_KEYTABLE_TYPE(entry));
|
|
|
|
return ath5k_hw_set_key_lladdr(ah, entry, mac);
|
|
}
|
|
|
|
int ath5k_hw_set_key_lladdr(struct ath5k_hw *ah, u16 entry, const u8 *mac)
|
|
{
|
|
u32 low_id, high_id;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
/* Invalid entry (key table overflow) */
|
|
AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE);
|
|
|
|
/* MAC may be NULL if it's a broadcast key. In this case no need to
|
|
* to compute AR5K_LOW_ID and AR5K_HIGH_ID as we already know it. */
|
|
if (unlikely(mac == NULL)) {
|
|
low_id = 0xffffffff;
|
|
high_id = 0xffff | AR5K_KEYTABLE_VALID;
|
|
} else {
|
|
low_id = AR5K_LOW_ID(mac);
|
|
high_id = AR5K_HIGH_ID(mac) | AR5K_KEYTABLE_VALID;
|
|
}
|
|
|
|
ath5k_hw_reg_write(ah, low_id, AR5K_KEYTABLE_MAC0(entry));
|
|
ath5k_hw_reg_write(ah, high_id, AR5K_KEYTABLE_MAC1(entry));
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/********************************************\
|
|
Queue Control Unit, DFS Control Unit Functions
|
|
\********************************************/
|
|
|
|
/*
|
|
* Initialize a transmit queue
|
|
*/
|
|
int ath5k_hw_setup_tx_queue(struct ath5k_hw *ah, enum ath5k_tx_queue queue_type,
|
|
struct ath5k_txq_info *queue_info)
|
|
{
|
|
unsigned int queue;
|
|
int ret;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
|
|
/*
|
|
* Get queue by type
|
|
*/
|
|
/*5210 only has 2 queues*/
|
|
if (ah->ah_version == AR5K_AR5210) {
|
|
switch (queue_type) {
|
|
case AR5K_TX_QUEUE_DATA:
|
|
queue = AR5K_TX_QUEUE_ID_NOQCU_DATA;
|
|
break;
|
|
case AR5K_TX_QUEUE_BEACON:
|
|
case AR5K_TX_QUEUE_CAB:
|
|
queue = AR5K_TX_QUEUE_ID_NOQCU_BEACON;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
switch (queue_type) {
|
|
case AR5K_TX_QUEUE_DATA:
|
|
for (queue = AR5K_TX_QUEUE_ID_DATA_MIN;
|
|
ah->ah_txq[queue].tqi_type !=
|
|
AR5K_TX_QUEUE_INACTIVE; queue++) {
|
|
|
|
if (queue > AR5K_TX_QUEUE_ID_DATA_MAX)
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
case AR5K_TX_QUEUE_UAPSD:
|
|
queue = AR5K_TX_QUEUE_ID_UAPSD;
|
|
break;
|
|
case AR5K_TX_QUEUE_BEACON:
|
|
queue = AR5K_TX_QUEUE_ID_BEACON;
|
|
break;
|
|
case AR5K_TX_QUEUE_CAB:
|
|
queue = AR5K_TX_QUEUE_ID_CAB;
|
|
break;
|
|
case AR5K_TX_QUEUE_XR_DATA:
|
|
if (ah->ah_version != AR5K_AR5212)
|
|
ATH5K_ERR(ah->ah_sc,
|
|
"XR data queues only supported in"
|
|
" 5212!\n");
|
|
queue = AR5K_TX_QUEUE_ID_XR_DATA;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Setup internal queue structure
|
|
*/
|
|
memset(&ah->ah_txq[queue], 0, sizeof(struct ath5k_txq_info));
|
|
ah->ah_txq[queue].tqi_type = queue_type;
|
|
|
|
if (queue_info != NULL) {
|
|
queue_info->tqi_type = queue_type;
|
|
ret = ath5k_hw_setup_tx_queueprops(ah, queue, queue_info);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
/*
|
|
* We use ah_txq_status to hold a temp value for
|
|
* the Secondary interrupt mask registers on 5211+
|
|
* check out ath5k_hw_reset_tx_queue
|
|
*/
|
|
AR5K_Q_ENABLE_BITS(ah->ah_txq_status, queue);
|
|
|
|
return queue;
|
|
}
|
|
|
|
/*
|
|
* Setup a transmit queue
|
|
*/
|
|
int ath5k_hw_setup_tx_queueprops(struct ath5k_hw *ah, int queue,
|
|
const struct ath5k_txq_info *queue_info)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
|
|
|
|
if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE)
|
|
return -EIO;
|
|
|
|
memcpy(&ah->ah_txq[queue], queue_info, sizeof(struct ath5k_txq_info));
|
|
|
|
/*XXX: Is this supported on 5210 ?*/
|
|
if ((queue_info->tqi_type == AR5K_TX_QUEUE_DATA &&
|
|
((queue_info->tqi_subtype == AR5K_WME_AC_VI) ||
|
|
(queue_info->tqi_subtype == AR5K_WME_AC_VO))) ||
|
|
queue_info->tqi_type == AR5K_TX_QUEUE_UAPSD)
|
|
ah->ah_txq[queue].tqi_flags |= AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Get properties for a specific transmit queue
|
|
*/
|
|
int ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue,
|
|
struct ath5k_txq_info *queue_info)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
memcpy(queue_info, &ah->ah_txq[queue], sizeof(struct ath5k_txq_info));
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Set a transmit queue inactive
|
|
*/
|
|
void ath5k_hw_release_tx_queue(struct ath5k_hw *ah, unsigned int queue)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
if (WARN_ON(queue >= ah->ah_capabilities.cap_queues.q_tx_num))
|
|
return;
|
|
|
|
/* This queue will be skipped in further operations */
|
|
ah->ah_txq[queue].tqi_type = AR5K_TX_QUEUE_INACTIVE;
|
|
/*For SIMR setup*/
|
|
AR5K_Q_DISABLE_BITS(ah->ah_txq_status, queue);
|
|
}
|
|
|
|
/*
|
|
* Set DFS params for a transmit queue
|
|
*/
|
|
int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue)
|
|
{
|
|
u32 cw_min, cw_max, retry_lg, retry_sh;
|
|
struct ath5k_txq_info *tq = &ah->ah_txq[queue];
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
|
|
|
|
tq = &ah->ah_txq[queue];
|
|
|
|
if (tq->tqi_type == AR5K_TX_QUEUE_INACTIVE)
|
|
return 0;
|
|
|
|
if (ah->ah_version == AR5K_AR5210) {
|
|
/* Only handle data queues, others will be ignored */
|
|
if (tq->tqi_type != AR5K_TX_QUEUE_DATA)
|
|
return 0;
|
|
|
|
/* Set Slot time */
|
|
ath5k_hw_reg_write(ah, ah->ah_turbo == true ?
|
|
AR5K_INIT_SLOT_TIME_TURBO : AR5K_INIT_SLOT_TIME,
|
|
AR5K_SLOT_TIME);
|
|
/* Set ACK_CTS timeout */
|
|
ath5k_hw_reg_write(ah, ah->ah_turbo == true ?
|
|
AR5K_INIT_ACK_CTS_TIMEOUT_TURBO :
|
|
AR5K_INIT_ACK_CTS_TIMEOUT, AR5K_SLOT_TIME);
|
|
/* Set Transmit Latency */
|
|
ath5k_hw_reg_write(ah, ah->ah_turbo == true ?
|
|
AR5K_INIT_TRANSMIT_LATENCY_TURBO :
|
|
AR5K_INIT_TRANSMIT_LATENCY, AR5K_USEC_5210);
|
|
/* Set IFS0 */
|
|
if (ah->ah_turbo == true)
|
|
ath5k_hw_reg_write(ah, ((AR5K_INIT_SIFS_TURBO +
|
|
(ah->ah_aifs + tq->tqi_aifs) *
|
|
AR5K_INIT_SLOT_TIME_TURBO) <<
|
|
AR5K_IFS0_DIFS_S) | AR5K_INIT_SIFS_TURBO,
|
|
AR5K_IFS0);
|
|
else
|
|
ath5k_hw_reg_write(ah, ((AR5K_INIT_SIFS +
|
|
(ah->ah_aifs + tq->tqi_aifs) *
|
|
AR5K_INIT_SLOT_TIME) << AR5K_IFS0_DIFS_S) |
|
|
AR5K_INIT_SIFS, AR5K_IFS0);
|
|
|
|
/* Set IFS1 */
|
|
ath5k_hw_reg_write(ah, ah->ah_turbo == true ?
|
|
AR5K_INIT_PROTO_TIME_CNTRL_TURBO :
|
|
AR5K_INIT_PROTO_TIME_CNTRL, AR5K_IFS1);
|
|
/* Set PHY register 0x9844 (??) */
|
|
ath5k_hw_reg_write(ah, ah->ah_turbo == true ?
|
|
(ath5k_hw_reg_read(ah, AR5K_PHY(17)) & ~0x7F) | 0x38 :
|
|
(ath5k_hw_reg_read(ah, AR5K_PHY(17)) & ~0x7F) | 0x1C,
|
|
AR5K_PHY(17));
|
|
/* Set Frame Control Register */
|
|
ath5k_hw_reg_write(ah, ah->ah_turbo == true ?
|
|
(AR5K_PHY_FRAME_CTL_INI | AR5K_PHY_TURBO_MODE |
|
|
AR5K_PHY_TURBO_SHORT | 0x2020) :
|
|
(AR5K_PHY_FRAME_CTL_INI | 0x1020),
|
|
AR5K_PHY_FRAME_CTL_5210);
|
|
}
|
|
|
|
/*
|
|
* Calculate cwmin/max by channel mode
|
|
*/
|
|
cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN;
|
|
cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX;
|
|
ah->ah_aifs = AR5K_TUNE_AIFS;
|
|
/*XR is only supported on 5212*/
|
|
if (IS_CHAN_XR(ah->ah_current_channel) &&
|
|
ah->ah_version == AR5K_AR5212) {
|
|
cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN_XR;
|
|
cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX_XR;
|
|
ah->ah_aifs = AR5K_TUNE_AIFS_XR;
|
|
/*B mode is not supported on 5210*/
|
|
} else if (IS_CHAN_B(ah->ah_current_channel) &&
|
|
ah->ah_version != AR5K_AR5210) {
|
|
cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN_11B;
|
|
cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX_11B;
|
|
ah->ah_aifs = AR5K_TUNE_AIFS_11B;
|
|
}
|
|
|
|
cw_min = 1;
|
|
while (cw_min < ah->ah_cw_min)
|
|
cw_min = (cw_min << 1) | 1;
|
|
|
|
cw_min = tq->tqi_cw_min < 0 ? (cw_min >> (-tq->tqi_cw_min)) :
|
|
((cw_min << tq->tqi_cw_min) + (1 << tq->tqi_cw_min) - 1);
|
|
cw_max = tq->tqi_cw_max < 0 ? (cw_max >> (-tq->tqi_cw_max)) :
|
|
((cw_max << tq->tqi_cw_max) + (1 << tq->tqi_cw_max) - 1);
|
|
|
|
/*
|
|
* Calculate and set retry limits
|
|
*/
|
|
if (ah->ah_software_retry == true) {
|
|
/* XXX Need to test this */
|
|
retry_lg = ah->ah_limit_tx_retries;
|
|
retry_sh = retry_lg = retry_lg > AR5K_DCU_RETRY_LMT_SH_RETRY ?
|
|
AR5K_DCU_RETRY_LMT_SH_RETRY : retry_lg;
|
|
} else {
|
|
retry_lg = AR5K_INIT_LG_RETRY;
|
|
retry_sh = AR5K_INIT_SH_RETRY;
|
|
}
|
|
|
|
/*No QCU/DCU [5210]*/
|
|
if (ah->ah_version == AR5K_AR5210) {
|
|
ath5k_hw_reg_write(ah,
|
|
(cw_min << AR5K_NODCU_RETRY_LMT_CW_MIN_S)
|
|
| AR5K_REG_SM(AR5K_INIT_SLG_RETRY,
|
|
AR5K_NODCU_RETRY_LMT_SLG_RETRY)
|
|
| AR5K_REG_SM(AR5K_INIT_SSH_RETRY,
|
|
AR5K_NODCU_RETRY_LMT_SSH_RETRY)
|
|
| AR5K_REG_SM(retry_lg, AR5K_NODCU_RETRY_LMT_LG_RETRY)
|
|
| AR5K_REG_SM(retry_sh, AR5K_NODCU_RETRY_LMT_SH_RETRY),
|
|
AR5K_NODCU_RETRY_LMT);
|
|
} else {
|
|
/*QCU/DCU [5211+]*/
|
|
ath5k_hw_reg_write(ah,
|
|
AR5K_REG_SM(AR5K_INIT_SLG_RETRY,
|
|
AR5K_DCU_RETRY_LMT_SLG_RETRY) |
|
|
AR5K_REG_SM(AR5K_INIT_SSH_RETRY,
|
|
AR5K_DCU_RETRY_LMT_SSH_RETRY) |
|
|
AR5K_REG_SM(retry_lg, AR5K_DCU_RETRY_LMT_LG_RETRY) |
|
|
AR5K_REG_SM(retry_sh, AR5K_DCU_RETRY_LMT_SH_RETRY),
|
|
AR5K_QUEUE_DFS_RETRY_LIMIT(queue));
|
|
|
|
/*===Rest is also for QCU/DCU only [5211+]===*/
|
|
|
|
/*
|
|
* Set initial content window (cw_min/cw_max)
|
|
* and arbitrated interframe space (aifs)...
|
|
*/
|
|
ath5k_hw_reg_write(ah,
|
|
AR5K_REG_SM(cw_min, AR5K_DCU_LCL_IFS_CW_MIN) |
|
|
AR5K_REG_SM(cw_max, AR5K_DCU_LCL_IFS_CW_MAX) |
|
|
AR5K_REG_SM(ah->ah_aifs + tq->tqi_aifs,
|
|
AR5K_DCU_LCL_IFS_AIFS),
|
|
AR5K_QUEUE_DFS_LOCAL_IFS(queue));
|
|
|
|
/*
|
|
* Set misc registers
|
|
*/
|
|
ath5k_hw_reg_write(ah, AR5K_QCU_MISC_DCU_EARLY,
|
|
AR5K_QUEUE_MISC(queue));
|
|
|
|
if (tq->tqi_cbr_period) {
|
|
ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_cbr_period,
|
|
AR5K_QCU_CBRCFG_INTVAL) |
|
|
AR5K_REG_SM(tq->tqi_cbr_overflow_limit,
|
|
AR5K_QCU_CBRCFG_ORN_THRES),
|
|
AR5K_QUEUE_CBRCFG(queue));
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
|
|
AR5K_QCU_MISC_FRSHED_CBR);
|
|
if (tq->tqi_cbr_overflow_limit)
|
|
AR5K_REG_ENABLE_BITS(ah,
|
|
AR5K_QUEUE_MISC(queue),
|
|
AR5K_QCU_MISC_CBR_THRES_ENABLE);
|
|
}
|
|
|
|
if (tq->tqi_ready_time)
|
|
ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_ready_time,
|
|
AR5K_QCU_RDYTIMECFG_INTVAL) |
|
|
AR5K_QCU_RDYTIMECFG_ENABLE,
|
|
AR5K_QUEUE_RDYTIMECFG(queue));
|
|
|
|
if (tq->tqi_burst_time) {
|
|
ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_burst_time,
|
|
AR5K_DCU_CHAN_TIME_DUR) |
|
|
AR5K_DCU_CHAN_TIME_ENABLE,
|
|
AR5K_QUEUE_DFS_CHANNEL_TIME(queue));
|
|
|
|
if (tq->tqi_flags & AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE)
|
|
AR5K_REG_ENABLE_BITS(ah,
|
|
AR5K_QUEUE_MISC(queue),
|
|
AR5K_QCU_MISC_TXE);
|
|
}
|
|
|
|
if (tq->tqi_flags & AR5K_TXQ_FLAG_BACKOFF_DISABLE)
|
|
ath5k_hw_reg_write(ah, AR5K_DCU_MISC_POST_FR_BKOFF_DIS,
|
|
AR5K_QUEUE_DFS_MISC(queue));
|
|
|
|
if (tq->tqi_flags & AR5K_TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE)
|
|
ath5k_hw_reg_write(ah, AR5K_DCU_MISC_BACKOFF_FRAG,
|
|
AR5K_QUEUE_DFS_MISC(queue));
|
|
|
|
/*
|
|
* Set registers by queue type
|
|
*/
|
|
switch (tq->tqi_type) {
|
|
case AR5K_TX_QUEUE_BEACON:
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
|
|
AR5K_QCU_MISC_FRSHED_DBA_GT |
|
|
AR5K_QCU_MISC_CBREXP_BCN |
|
|
AR5K_QCU_MISC_BCN_ENABLE);
|
|
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue),
|
|
(AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL <<
|
|
AR5K_DCU_MISC_ARBLOCK_CTL_S) |
|
|
AR5K_DCU_MISC_POST_FR_BKOFF_DIS |
|
|
AR5K_DCU_MISC_BCN_ENABLE);
|
|
|
|
ath5k_hw_reg_write(ah, ((AR5K_TUNE_BEACON_INTERVAL -
|
|
(AR5K_TUNE_SW_BEACON_RESP -
|
|
AR5K_TUNE_DMA_BEACON_RESP) -
|
|
AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF) * 1024) |
|
|
AR5K_QCU_RDYTIMECFG_ENABLE,
|
|
AR5K_QUEUE_RDYTIMECFG(queue));
|
|
break;
|
|
|
|
case AR5K_TX_QUEUE_CAB:
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
|
|
AR5K_QCU_MISC_FRSHED_DBA_GT |
|
|
AR5K_QCU_MISC_CBREXP |
|
|
AR5K_QCU_MISC_CBREXP_BCN);
|
|
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue),
|
|
(AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL <<
|
|
AR5K_DCU_MISC_ARBLOCK_CTL_S));
|
|
break;
|
|
|
|
case AR5K_TX_QUEUE_UAPSD:
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
|
|
AR5K_QCU_MISC_CBREXP);
|
|
break;
|
|
|
|
case AR5K_TX_QUEUE_DATA:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Enable interrupts for this tx queue
|
|
* in the secondary interrupt mask registers
|
|
*/
|
|
if (tq->tqi_flags & AR5K_TXQ_FLAG_TXOKINT_ENABLE)
|
|
AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txok, queue);
|
|
|
|
if (tq->tqi_flags & AR5K_TXQ_FLAG_TXERRINT_ENABLE)
|
|
AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txerr, queue);
|
|
|
|
if (tq->tqi_flags & AR5K_TXQ_FLAG_TXURNINT_ENABLE)
|
|
AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txurn, queue);
|
|
|
|
if (tq->tqi_flags & AR5K_TXQ_FLAG_TXDESCINT_ENABLE)
|
|
AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txdesc, queue);
|
|
|
|
if (tq->tqi_flags & AR5K_TXQ_FLAG_TXEOLINT_ENABLE)
|
|
AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txeol, queue);
|
|
|
|
|
|
/* Update secondary interrupt mask registers */
|
|
ah->ah_txq_imr_txok &= ah->ah_txq_status;
|
|
ah->ah_txq_imr_txerr &= ah->ah_txq_status;
|
|
ah->ah_txq_imr_txurn &= ah->ah_txq_status;
|
|
ah->ah_txq_imr_txdesc &= ah->ah_txq_status;
|
|
ah->ah_txq_imr_txeol &= ah->ah_txq_status;
|
|
|
|
ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txok,
|
|
AR5K_SIMR0_QCU_TXOK) |
|
|
AR5K_REG_SM(ah->ah_txq_imr_txdesc,
|
|
AR5K_SIMR0_QCU_TXDESC), AR5K_SIMR0);
|
|
ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txerr,
|
|
AR5K_SIMR1_QCU_TXERR) |
|
|
AR5K_REG_SM(ah->ah_txq_imr_txeol,
|
|
AR5K_SIMR1_QCU_TXEOL), AR5K_SIMR1);
|
|
ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txurn,
|
|
AR5K_SIMR2_QCU_TXURN), AR5K_SIMR2);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Get number of pending frames
|
|
* for a specific queue [5211+]
|
|
*/
|
|
u32 ath5k_hw_num_tx_pending(struct ath5k_hw *ah, unsigned int queue) {
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
|
|
|
|
/* Return if queue is declared inactive */
|
|
if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE)
|
|
return false;
|
|
|
|
/* XXX: How about AR5K_CFG_TXCNT ? */
|
|
if (ah->ah_version == AR5K_AR5210)
|
|
return false;
|
|
|
|
return AR5K_QUEUE_STATUS(queue) & AR5K_QCU_STS_FRMPENDCNT;
|
|
}
|
|
|
|
/*
|
|
* Set slot time
|
|
*/
|
|
int ath5k_hw_set_slot_time(struct ath5k_hw *ah, unsigned int slot_time)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
if (slot_time < AR5K_SLOT_TIME_9 || slot_time > AR5K_SLOT_TIME_MAX)
|
|
return -EINVAL;
|
|
|
|
if (ah->ah_version == AR5K_AR5210)
|
|
ath5k_hw_reg_write(ah, ath5k_hw_htoclock(slot_time,
|
|
ah->ah_turbo), AR5K_SLOT_TIME);
|
|
else
|
|
ath5k_hw_reg_write(ah, slot_time, AR5K_DCU_GBL_IFS_SLOT);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Get slot time
|
|
*/
|
|
unsigned int ath5k_hw_get_slot_time(struct ath5k_hw *ah)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
if (ah->ah_version == AR5K_AR5210)
|
|
return ath5k_hw_clocktoh(ath5k_hw_reg_read(ah,
|
|
AR5K_SLOT_TIME) & 0xffff, ah->ah_turbo);
|
|
else
|
|
return ath5k_hw_reg_read(ah, AR5K_DCU_GBL_IFS_SLOT) & 0xffff;
|
|
}
|
|
|
|
|
|
/******************************\
|
|
Hardware Descriptor Functions
|
|
\******************************/
|
|
|
|
/*
|
|
* TX Descriptor
|
|
*/
|
|
|
|
/*
|
|
* Initialize the 2-word tx descriptor on 5210/5211
|
|
*/
|
|
static int
|
|
ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
|
|
unsigned int pkt_len, unsigned int hdr_len, enum ath5k_pkt_type type,
|
|
unsigned int tx_power, unsigned int tx_rate0, unsigned int tx_tries0,
|
|
unsigned int key_index, unsigned int antenna_mode, unsigned int flags,
|
|
unsigned int rtscts_rate, unsigned int rtscts_duration)
|
|
{
|
|
u32 frame_type;
|
|
struct ath5k_hw_2w_tx_desc *tx_desc;
|
|
unsigned int frame_len;
|
|
|
|
tx_desc = (struct ath5k_hw_2w_tx_desc *)&desc->ds_ctl0;
|
|
|
|
/*
|
|
* Validate input
|
|
* - Zero retries don't make sense.
|
|
* - A zero rate will put the HW into a mode where it continously sends
|
|
* noise on the channel, so it is important to avoid this.
|
|
*/
|
|
if (unlikely(tx_tries0 == 0)) {
|
|
ATH5K_ERR(ah->ah_sc, "zero retries\n");
|
|
WARN_ON(1);
|
|
return -EINVAL;
|
|
}
|
|
if (unlikely(tx_rate0 == 0)) {
|
|
ATH5K_ERR(ah->ah_sc, "zero rate\n");
|
|
WARN_ON(1);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Clear status descriptor */
|
|
memset(desc->ds_hw, 0, sizeof(struct ath5k_hw_tx_status));
|
|
|
|
/* Initialize control descriptor */
|
|
tx_desc->tx_control_0 = 0;
|
|
tx_desc->tx_control_1 = 0;
|
|
|
|
/* Setup control descriptor */
|
|
|
|
/* Verify and set frame length */
|
|
|
|
/* remove padding we might have added before */
|
|
frame_len = pkt_len - (hdr_len & 3) + FCS_LEN;
|
|
|
|
if (frame_len & ~AR5K_2W_TX_DESC_CTL0_FRAME_LEN)
|
|
return -EINVAL;
|
|
|
|
tx_desc->tx_control_0 = frame_len & AR5K_2W_TX_DESC_CTL0_FRAME_LEN;
|
|
|
|
/* Verify and set buffer length */
|
|
|
|
/* NB: beacon's BufLen must be a multiple of 4 bytes */
|
|
if(type == AR5K_PKT_TYPE_BEACON)
|
|
pkt_len = roundup(pkt_len, 4);
|
|
|
|
if (pkt_len & ~AR5K_2W_TX_DESC_CTL1_BUF_LEN)
|
|
return -EINVAL;
|
|
|
|
tx_desc->tx_control_1 = pkt_len & AR5K_2W_TX_DESC_CTL1_BUF_LEN;
|
|
|
|
/*
|
|
* Verify and set header length
|
|
* XXX: I only found that on 5210 code, does it work on 5211 ?
|
|
*/
|
|
if (ah->ah_version == AR5K_AR5210) {
|
|
if (hdr_len & ~AR5K_2W_TX_DESC_CTL0_HEADER_LEN)
|
|
return -EINVAL;
|
|
tx_desc->tx_control_0 |=
|
|
AR5K_REG_SM(hdr_len, AR5K_2W_TX_DESC_CTL0_HEADER_LEN);
|
|
}
|
|
|
|
/*Diferences between 5210-5211*/
|
|
if (ah->ah_version == AR5K_AR5210) {
|
|
switch (type) {
|
|
case AR5K_PKT_TYPE_BEACON:
|
|
case AR5K_PKT_TYPE_PROBE_RESP:
|
|
frame_type = AR5K_AR5210_TX_DESC_FRAME_TYPE_NO_DELAY;
|
|
case AR5K_PKT_TYPE_PIFS:
|
|
frame_type = AR5K_AR5210_TX_DESC_FRAME_TYPE_PIFS;
|
|
default:
|
|
frame_type = type /*<< 2 ?*/;
|
|
}
|
|
|
|
tx_desc->tx_control_0 |=
|
|
AR5K_REG_SM(frame_type, AR5K_2W_TX_DESC_CTL0_FRAME_TYPE) |
|
|
AR5K_REG_SM(tx_rate0, AR5K_2W_TX_DESC_CTL0_XMIT_RATE);
|
|
} else {
|
|
tx_desc->tx_control_0 |=
|
|
AR5K_REG_SM(tx_rate0, AR5K_2W_TX_DESC_CTL0_XMIT_RATE) |
|
|
AR5K_REG_SM(antenna_mode, AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT);
|
|
tx_desc->tx_control_1 |=
|
|
AR5K_REG_SM(type, AR5K_2W_TX_DESC_CTL1_FRAME_TYPE);
|
|
}
|
|
#define _TX_FLAGS(_c, _flag) \
|
|
if (flags & AR5K_TXDESC_##_flag) \
|
|
tx_desc->tx_control_##_c |= \
|
|
AR5K_2W_TX_DESC_CTL##_c##_##_flag
|
|
|
|
_TX_FLAGS(0, CLRDMASK);
|
|
_TX_FLAGS(0, VEOL);
|
|
_TX_FLAGS(0, INTREQ);
|
|
_TX_FLAGS(0, RTSENA);
|
|
_TX_FLAGS(1, NOACK);
|
|
|
|
#undef _TX_FLAGS
|
|
|
|
/*
|
|
* WEP crap
|
|
*/
|
|
if (key_index != AR5K_TXKEYIX_INVALID) {
|
|
tx_desc->tx_control_0 |=
|
|
AR5K_2W_TX_DESC_CTL0_ENCRYPT_KEY_VALID;
|
|
tx_desc->tx_control_1 |=
|
|
AR5K_REG_SM(key_index,
|
|
AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX);
|
|
}
|
|
|
|
/*
|
|
* RTS/CTS Duration [5210 ?]
|
|
*/
|
|
if ((ah->ah_version == AR5K_AR5210) &&
|
|
(flags & (AR5K_TXDESC_RTSENA | AR5K_TXDESC_CTSENA)))
|
|
tx_desc->tx_control_1 |= rtscts_duration &
|
|
AR5K_2W_TX_DESC_CTL1_RTS_DURATION;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Initialize the 4-word tx descriptor on 5212
|
|
*/
|
|
static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah,
|
|
struct ath5k_desc *desc, unsigned int pkt_len, unsigned int hdr_len,
|
|
enum ath5k_pkt_type type, unsigned int tx_power, unsigned int tx_rate0,
|
|
unsigned int tx_tries0, unsigned int key_index,
|
|
unsigned int antenna_mode, unsigned int flags, unsigned int rtscts_rate,
|
|
unsigned int rtscts_duration)
|
|
{
|
|
struct ath5k_hw_4w_tx_desc *tx_desc;
|
|
struct ath5k_hw_tx_status *tx_status;
|
|
unsigned int frame_len;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
tx_desc = (struct ath5k_hw_4w_tx_desc *)&desc->ds_ctl0;
|
|
tx_status = (struct ath5k_hw_tx_status *)&desc->ds_hw[2];
|
|
|
|
/*
|
|
* Validate input
|
|
* - Zero retries don't make sense.
|
|
* - A zero rate will put the HW into a mode where it continously sends
|
|
* noise on the channel, so it is important to avoid this.
|
|
*/
|
|
if (unlikely(tx_tries0 == 0)) {
|
|
ATH5K_ERR(ah->ah_sc, "zero retries\n");
|
|
WARN_ON(1);
|
|
return -EINVAL;
|
|
}
|
|
if (unlikely(tx_rate0 == 0)) {
|
|
ATH5K_ERR(ah->ah_sc, "zero rate\n");
|
|
WARN_ON(1);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Clear status descriptor */
|
|
memset(tx_status, 0, sizeof(struct ath5k_hw_tx_status));
|
|
|
|
/* Initialize control descriptor */
|
|
tx_desc->tx_control_0 = 0;
|
|
tx_desc->tx_control_1 = 0;
|
|
tx_desc->tx_control_2 = 0;
|
|
tx_desc->tx_control_3 = 0;
|
|
|
|
/* Setup control descriptor */
|
|
|
|
/* Verify and set frame length */
|
|
|
|
/* remove padding we might have added before */
|
|
frame_len = pkt_len - (hdr_len & 3) + FCS_LEN;
|
|
|
|
if (frame_len & ~AR5K_4W_TX_DESC_CTL0_FRAME_LEN)
|
|
return -EINVAL;
|
|
|
|
tx_desc->tx_control_0 = frame_len & AR5K_4W_TX_DESC_CTL0_FRAME_LEN;
|
|
|
|
/* Verify and set buffer length */
|
|
|
|
/* NB: beacon's BufLen must be a multiple of 4 bytes */
|
|
if(type == AR5K_PKT_TYPE_BEACON)
|
|
pkt_len = roundup(pkt_len, 4);
|
|
|
|
if (pkt_len & ~AR5K_4W_TX_DESC_CTL1_BUF_LEN)
|
|
return -EINVAL;
|
|
|
|
tx_desc->tx_control_1 = pkt_len & AR5K_4W_TX_DESC_CTL1_BUF_LEN;
|
|
|
|
tx_desc->tx_control_0 |=
|
|
AR5K_REG_SM(tx_power, AR5K_4W_TX_DESC_CTL0_XMIT_POWER) |
|
|
AR5K_REG_SM(antenna_mode, AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT);
|
|
tx_desc->tx_control_1 |= AR5K_REG_SM(type,
|
|
AR5K_4W_TX_DESC_CTL1_FRAME_TYPE);
|
|
tx_desc->tx_control_2 = AR5K_REG_SM(tx_tries0 + AR5K_TUNE_HWTXTRIES,
|
|
AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0);
|
|
tx_desc->tx_control_3 = tx_rate0 & AR5K_4W_TX_DESC_CTL3_XMIT_RATE0;
|
|
|
|
#define _TX_FLAGS(_c, _flag) \
|
|
if (flags & AR5K_TXDESC_##_flag) \
|
|
tx_desc->tx_control_##_c |= \
|
|
AR5K_4W_TX_DESC_CTL##_c##_##_flag
|
|
|
|
_TX_FLAGS(0, CLRDMASK);
|
|
_TX_FLAGS(0, VEOL);
|
|
_TX_FLAGS(0, INTREQ);
|
|
_TX_FLAGS(0, RTSENA);
|
|
_TX_FLAGS(0, CTSENA);
|
|
_TX_FLAGS(1, NOACK);
|
|
|
|
#undef _TX_FLAGS
|
|
|
|
/*
|
|
* WEP crap
|
|
*/
|
|
if (key_index != AR5K_TXKEYIX_INVALID) {
|
|
tx_desc->tx_control_0 |= AR5K_4W_TX_DESC_CTL0_ENCRYPT_KEY_VALID;
|
|
tx_desc->tx_control_1 |= AR5K_REG_SM(key_index,
|
|
AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX);
|
|
}
|
|
|
|
/*
|
|
* RTS/CTS
|
|
*/
|
|
if (flags & (AR5K_TXDESC_RTSENA | AR5K_TXDESC_CTSENA)) {
|
|
if ((flags & AR5K_TXDESC_RTSENA) &&
|
|
(flags & AR5K_TXDESC_CTSENA))
|
|
return -EINVAL;
|
|
tx_desc->tx_control_2 |= rtscts_duration &
|
|
AR5K_4W_TX_DESC_CTL2_RTS_DURATION;
|
|
tx_desc->tx_control_3 |= AR5K_REG_SM(rtscts_rate,
|
|
AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Initialize a 4-word multirate tx descriptor on 5212
|
|
*/
|
|
static bool
|
|
ath5k_hw_setup_xr_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
|
|
unsigned int tx_rate1, u_int tx_tries1, u_int tx_rate2, u_int tx_tries2,
|
|
unsigned int tx_rate3, u_int tx_tries3)
|
|
{
|
|
struct ath5k_hw_4w_tx_desc *tx_desc;
|
|
|
|
/*
|
|
* Rates can be 0 as long as the retry count is 0 too.
|
|
* A zero rate and nonzero retry count will put the HW into a mode where
|
|
* it continously sends noise on the channel, so it is important to
|
|
* avoid this.
|
|
*/
|
|
if (unlikely((tx_rate1 == 0 && tx_tries1 != 0) ||
|
|
(tx_rate2 == 0 && tx_tries2 != 0) ||
|
|
(tx_rate3 == 0 && tx_tries3 != 0))) {
|
|
ATH5K_ERR(ah->ah_sc, "zero rate\n");
|
|
WARN_ON(1);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (ah->ah_version == AR5K_AR5212) {
|
|
tx_desc = (struct ath5k_hw_4w_tx_desc *)&desc->ds_ctl0;
|
|
|
|
#define _XTX_TRIES(_n) \
|
|
if (tx_tries##_n) { \
|
|
tx_desc->tx_control_2 |= \
|
|
AR5K_REG_SM(tx_tries##_n, \
|
|
AR5K_4W_TX_DESC_CTL2_XMIT_TRIES##_n); \
|
|
tx_desc->tx_control_3 |= \
|
|
AR5K_REG_SM(tx_rate##_n, \
|
|
AR5K_4W_TX_DESC_CTL3_XMIT_RATE##_n); \
|
|
}
|
|
|
|
_XTX_TRIES(1);
|
|
_XTX_TRIES(2);
|
|
_XTX_TRIES(3);
|
|
|
|
#undef _XTX_TRIES
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Proccess the tx status descriptor on 5210/5211
|
|
*/
|
|
static int ath5k_hw_proc_2word_tx_status(struct ath5k_hw *ah,
|
|
struct ath5k_desc *desc)
|
|
{
|
|
struct ath5k_hw_tx_status *tx_status;
|
|
struct ath5k_hw_2w_tx_desc *tx_desc;
|
|
|
|
tx_desc = (struct ath5k_hw_2w_tx_desc *)&desc->ds_ctl0;
|
|
tx_status = (struct ath5k_hw_tx_status *)&desc->ds_hw[0];
|
|
|
|
/* No frame has been send or error */
|
|
if (unlikely((tx_status->tx_status_1 & AR5K_DESC_TX_STATUS1_DONE) == 0))
|
|
return -EINPROGRESS;
|
|
|
|
/*
|
|
* Get descriptor status
|
|
*/
|
|
desc->ds_us.tx.ts_tstamp = AR5K_REG_MS(tx_status->tx_status_0,
|
|
AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP);
|
|
desc->ds_us.tx.ts_shortretry = AR5K_REG_MS(tx_status->tx_status_0,
|
|
AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT);
|
|
desc->ds_us.tx.ts_longretry = AR5K_REG_MS(tx_status->tx_status_0,
|
|
AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT);
|
|
/*TODO: desc->ds_us.tx.ts_virtcol + test*/
|
|
desc->ds_us.tx.ts_seqnum = AR5K_REG_MS(tx_status->tx_status_1,
|
|
AR5K_DESC_TX_STATUS1_SEQ_NUM);
|
|
desc->ds_us.tx.ts_rssi = AR5K_REG_MS(tx_status->tx_status_1,
|
|
AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH);
|
|
desc->ds_us.tx.ts_antenna = 1;
|
|
desc->ds_us.tx.ts_status = 0;
|
|
desc->ds_us.tx.ts_rate = AR5K_REG_MS(tx_desc->tx_control_0,
|
|
AR5K_2W_TX_DESC_CTL0_XMIT_RATE);
|
|
|
|
if ((tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK) == 0){
|
|
if (tx_status->tx_status_0 &
|
|
AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES)
|
|
desc->ds_us.tx.ts_status |= AR5K_TXERR_XRETRY;
|
|
|
|
if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN)
|
|
desc->ds_us.tx.ts_status |= AR5K_TXERR_FIFO;
|
|
|
|
if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FILTERED)
|
|
desc->ds_us.tx.ts_status |= AR5K_TXERR_FILT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Proccess a tx descriptor on 5212
|
|
*/
|
|
static int ath5k_hw_proc_4word_tx_status(struct ath5k_hw *ah,
|
|
struct ath5k_desc *desc)
|
|
{
|
|
struct ath5k_hw_tx_status *tx_status;
|
|
struct ath5k_hw_4w_tx_desc *tx_desc;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
tx_desc = (struct ath5k_hw_4w_tx_desc *)&desc->ds_ctl0;
|
|
tx_status = (struct ath5k_hw_tx_status *)&desc->ds_hw[2];
|
|
|
|
/* No frame has been send or error */
|
|
if (unlikely((tx_status->tx_status_1 & AR5K_DESC_TX_STATUS1_DONE) == 0))
|
|
return -EINPROGRESS;
|
|
|
|
/*
|
|
* Get descriptor status
|
|
*/
|
|
desc->ds_us.tx.ts_tstamp = AR5K_REG_MS(tx_status->tx_status_0,
|
|
AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP);
|
|
desc->ds_us.tx.ts_shortretry = AR5K_REG_MS(tx_status->tx_status_0,
|
|
AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT);
|
|
desc->ds_us.tx.ts_longretry = AR5K_REG_MS(tx_status->tx_status_0,
|
|
AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT);
|
|
desc->ds_us.tx.ts_seqnum = AR5K_REG_MS(tx_status->tx_status_1,
|
|
AR5K_DESC_TX_STATUS1_SEQ_NUM);
|
|
desc->ds_us.tx.ts_rssi = AR5K_REG_MS(tx_status->tx_status_1,
|
|
AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH);
|
|
desc->ds_us.tx.ts_antenna = (tx_status->tx_status_1 &
|
|
AR5K_DESC_TX_STATUS1_XMIT_ANTENNA) ? 2 : 1;
|
|
desc->ds_us.tx.ts_status = 0;
|
|
|
|
switch (AR5K_REG_MS(tx_status->tx_status_1,
|
|
AR5K_DESC_TX_STATUS1_FINAL_TS_INDEX)) {
|
|
case 0:
|
|
desc->ds_us.tx.ts_rate = tx_desc->tx_control_3 &
|
|
AR5K_4W_TX_DESC_CTL3_XMIT_RATE0;
|
|
break;
|
|
case 1:
|
|
desc->ds_us.tx.ts_rate = AR5K_REG_MS(tx_desc->tx_control_3,
|
|
AR5K_4W_TX_DESC_CTL3_XMIT_RATE1);
|
|
desc->ds_us.tx.ts_longretry +=AR5K_REG_MS(tx_desc->tx_control_2,
|
|
AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1);
|
|
break;
|
|
case 2:
|
|
desc->ds_us.tx.ts_rate = AR5K_REG_MS(tx_desc->tx_control_3,
|
|
AR5K_4W_TX_DESC_CTL3_XMIT_RATE2);
|
|
desc->ds_us.tx.ts_longretry +=AR5K_REG_MS(tx_desc->tx_control_2,
|
|
AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2);
|
|
break;
|
|
case 3:
|
|
desc->ds_us.tx.ts_rate = AR5K_REG_MS(tx_desc->tx_control_3,
|
|
AR5K_4W_TX_DESC_CTL3_XMIT_RATE3);
|
|
desc->ds_us.tx.ts_longretry +=AR5K_REG_MS(tx_desc->tx_control_2,
|
|
AR5K_4W_TX_DESC_CTL2_XMIT_TRIES3);
|
|
break;
|
|
}
|
|
|
|
if ((tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK) == 0){
|
|
if (tx_status->tx_status_0 &
|
|
AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES)
|
|
desc->ds_us.tx.ts_status |= AR5K_TXERR_XRETRY;
|
|
|
|
if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN)
|
|
desc->ds_us.tx.ts_status |= AR5K_TXERR_FIFO;
|
|
|
|
if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FILTERED)
|
|
desc->ds_us.tx.ts_status |= AR5K_TXERR_FILT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* RX Descriptor
|
|
*/
|
|
|
|
/*
|
|
* Initialize an rx descriptor
|
|
*/
|
|
int ath5k_hw_setup_rx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
|
|
u32 size, unsigned int flags)
|
|
{
|
|
struct ath5k_rx_desc *rx_desc;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
rx_desc = (struct ath5k_rx_desc *)&desc->ds_ctl0;
|
|
|
|
/*
|
|
*Clear ds_hw
|
|
* If we don't clean the status descriptor,
|
|
* while scanning we get too many results,
|
|
* most of them virtual, after some secs
|
|
* of scanning system hangs. M.F.
|
|
*/
|
|
memset(desc->ds_hw, 0, sizeof(desc->ds_hw));
|
|
|
|
/*Initialize rx descriptor*/
|
|
rx_desc->rx_control_0 = 0;
|
|
rx_desc->rx_control_1 = 0;
|
|
|
|
/* Setup descriptor */
|
|
rx_desc->rx_control_1 = size & AR5K_DESC_RX_CTL1_BUF_LEN;
|
|
if (unlikely(rx_desc->rx_control_1 != size))
|
|
return -EINVAL;
|
|
|
|
if (flags & AR5K_RXDESC_INTREQ)
|
|
rx_desc->rx_control_1 |= AR5K_DESC_RX_CTL1_INTREQ;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Proccess the rx status descriptor on 5210/5211
|
|
*/
|
|
static int ath5k_hw_proc_old_rx_status(struct ath5k_hw *ah,
|
|
struct ath5k_desc *desc)
|
|
{
|
|
struct ath5k_hw_old_rx_status *rx_status;
|
|
|
|
rx_status = (struct ath5k_hw_old_rx_status *)&desc->ds_hw[0];
|
|
|
|
/* No frame received / not ready */
|
|
if (unlikely((rx_status->rx_status_1 & AR5K_OLD_RX_DESC_STATUS1_DONE)
|
|
== 0))
|
|
return -EINPROGRESS;
|
|
|
|
/*
|
|
* Frame receive status
|
|
*/
|
|
desc->ds_us.rx.rs_datalen = rx_status->rx_status_0 &
|
|
AR5K_OLD_RX_DESC_STATUS0_DATA_LEN;
|
|
desc->ds_us.rx.rs_rssi = AR5K_REG_MS(rx_status->rx_status_0,
|
|
AR5K_OLD_RX_DESC_STATUS0_RECEIVE_SIGNAL);
|
|
desc->ds_us.rx.rs_rate = AR5K_REG_MS(rx_status->rx_status_0,
|
|
AR5K_OLD_RX_DESC_STATUS0_RECEIVE_RATE);
|
|
desc->ds_us.rx.rs_antenna = rx_status->rx_status_0 &
|
|
AR5K_OLD_RX_DESC_STATUS0_RECEIVE_ANTENNA;
|
|
desc->ds_us.rx.rs_more = rx_status->rx_status_0 &
|
|
AR5K_OLD_RX_DESC_STATUS0_MORE;
|
|
desc->ds_us.rx.rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1,
|
|
AR5K_OLD_RX_DESC_STATUS1_RECEIVE_TIMESTAMP);
|
|
desc->ds_us.rx.rs_status = 0;
|
|
|
|
/*
|
|
* Key table status
|
|
*/
|
|
if (rx_status->rx_status_1 & AR5K_OLD_RX_DESC_STATUS1_KEY_INDEX_VALID)
|
|
desc->ds_us.rx.rs_keyix = AR5K_REG_MS(rx_status->rx_status_1,
|
|
AR5K_OLD_RX_DESC_STATUS1_KEY_INDEX);
|
|
else
|
|
desc->ds_us.rx.rs_keyix = AR5K_RXKEYIX_INVALID;
|
|
|
|
/*
|
|
* Receive/descriptor errors
|
|
*/
|
|
if ((rx_status->rx_status_1 & AR5K_OLD_RX_DESC_STATUS1_FRAME_RECEIVE_OK)
|
|
== 0) {
|
|
if (rx_status->rx_status_1 & AR5K_OLD_RX_DESC_STATUS1_CRC_ERROR)
|
|
desc->ds_us.rx.rs_status |= AR5K_RXERR_CRC;
|
|
|
|
if (rx_status->rx_status_1 &
|
|
AR5K_OLD_RX_DESC_STATUS1_FIFO_OVERRUN)
|
|
desc->ds_us.rx.rs_status |= AR5K_RXERR_FIFO;
|
|
|
|
if (rx_status->rx_status_1 &
|
|
AR5K_OLD_RX_DESC_STATUS1_PHY_ERROR) {
|
|
desc->ds_us.rx.rs_status |= AR5K_RXERR_PHY;
|
|
desc->ds_us.rx.rs_phyerr =
|
|
AR5K_REG_MS(rx_status->rx_status_1,
|
|
AR5K_OLD_RX_DESC_STATUS1_PHY_ERROR);
|
|
}
|
|
|
|
if (rx_status->rx_status_1 &
|
|
AR5K_OLD_RX_DESC_STATUS1_DECRYPT_CRC_ERROR)
|
|
desc->ds_us.rx.rs_status |= AR5K_RXERR_DECRYPT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Proccess the rx status descriptor on 5212
|
|
*/
|
|
static int ath5k_hw_proc_new_rx_status(struct ath5k_hw *ah,
|
|
struct ath5k_desc *desc)
|
|
{
|
|
struct ath5k_hw_new_rx_status *rx_status;
|
|
struct ath5k_hw_rx_error *rx_err;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
rx_status = (struct ath5k_hw_new_rx_status *)&desc->ds_hw[0];
|
|
|
|
/* Overlay on error */
|
|
rx_err = (struct ath5k_hw_rx_error *)&desc->ds_hw[0];
|
|
|
|
/* No frame received / not ready */
|
|
if (unlikely((rx_status->rx_status_1 & AR5K_NEW_RX_DESC_STATUS1_DONE)
|
|
== 0))
|
|
return -EINPROGRESS;
|
|
|
|
/*
|
|
* Frame receive status
|
|
*/
|
|
desc->ds_us.rx.rs_datalen = rx_status->rx_status_0 &
|
|
AR5K_NEW_RX_DESC_STATUS0_DATA_LEN;
|
|
desc->ds_us.rx.rs_rssi = AR5K_REG_MS(rx_status->rx_status_0,
|
|
AR5K_NEW_RX_DESC_STATUS0_RECEIVE_SIGNAL);
|
|
desc->ds_us.rx.rs_rate = AR5K_REG_MS(rx_status->rx_status_0,
|
|
AR5K_NEW_RX_DESC_STATUS0_RECEIVE_RATE);
|
|
desc->ds_us.rx.rs_antenna = rx_status->rx_status_0 &
|
|
AR5K_NEW_RX_DESC_STATUS0_RECEIVE_ANTENNA;
|
|
desc->ds_us.rx.rs_more = rx_status->rx_status_0 &
|
|
AR5K_NEW_RX_DESC_STATUS0_MORE;
|
|
desc->ds_us.rx.rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1,
|
|
AR5K_NEW_RX_DESC_STATUS1_RECEIVE_TIMESTAMP);
|
|
desc->ds_us.rx.rs_status = 0;
|
|
|
|
/*
|
|
* Key table status
|
|
*/
|
|
if (rx_status->rx_status_1 & AR5K_NEW_RX_DESC_STATUS1_KEY_INDEX_VALID)
|
|
desc->ds_us.rx.rs_keyix = AR5K_REG_MS(rx_status->rx_status_1,
|
|
AR5K_NEW_RX_DESC_STATUS1_KEY_INDEX);
|
|
else
|
|
desc->ds_us.rx.rs_keyix = AR5K_RXKEYIX_INVALID;
|
|
|
|
/*
|
|
* Receive/descriptor errors
|
|
*/
|
|
if ((rx_status->rx_status_1 &
|
|
AR5K_NEW_RX_DESC_STATUS1_FRAME_RECEIVE_OK) == 0) {
|
|
if (rx_status->rx_status_1 & AR5K_NEW_RX_DESC_STATUS1_CRC_ERROR)
|
|
desc->ds_us.rx.rs_status |= AR5K_RXERR_CRC;
|
|
|
|
if (rx_status->rx_status_1 &
|
|
AR5K_NEW_RX_DESC_STATUS1_PHY_ERROR) {
|
|
desc->ds_us.rx.rs_status |= AR5K_RXERR_PHY;
|
|
desc->ds_us.rx.rs_phyerr =
|
|
AR5K_REG_MS(rx_err->rx_error_1,
|
|
AR5K_RX_DESC_ERROR1_PHY_ERROR_CODE);
|
|
}
|
|
|
|
if (rx_status->rx_status_1 &
|
|
AR5K_NEW_RX_DESC_STATUS1_DECRYPT_CRC_ERROR)
|
|
desc->ds_us.rx.rs_status |= AR5K_RXERR_DECRYPT;
|
|
|
|
if (rx_status->rx_status_1 & AR5K_NEW_RX_DESC_STATUS1_MIC_ERROR)
|
|
desc->ds_us.rx.rs_status |= AR5K_RXERR_MIC;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/****************\
|
|
GPIO Functions
|
|
\****************/
|
|
|
|
/*
|
|
* Set led state
|
|
*/
|
|
void ath5k_hw_set_ledstate(struct ath5k_hw *ah, unsigned int state)
|
|
{
|
|
u32 led;
|
|
/*5210 has different led mode handling*/
|
|
u32 led_5210;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
|
|
/*Reset led status*/
|
|
if (ah->ah_version != AR5K_AR5210)
|
|
AR5K_REG_DISABLE_BITS(ah, AR5K_PCICFG,
|
|
AR5K_PCICFG_LEDMODE | AR5K_PCICFG_LED);
|
|
else
|
|
AR5K_REG_DISABLE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_LED);
|
|
|
|
/*
|
|
* Some blinking values, define at your wish
|
|
*/
|
|
switch (state) {
|
|
case AR5K_LED_SCAN:
|
|
case AR5K_LED_AUTH:
|
|
led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_PEND;
|
|
led_5210 = AR5K_PCICFG_LED_PEND | AR5K_PCICFG_LED_BCTL;
|
|
break;
|
|
|
|
case AR5K_LED_INIT:
|
|
led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_NONE;
|
|
led_5210 = AR5K_PCICFG_LED_PEND;
|
|
break;
|
|
|
|
case AR5K_LED_ASSOC:
|
|
case AR5K_LED_RUN:
|
|
led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_ASSOC;
|
|
led_5210 = AR5K_PCICFG_LED_ASSOC;
|
|
break;
|
|
|
|
default:
|
|
led = AR5K_PCICFG_LEDMODE_PROM | AR5K_PCICFG_LED_NONE;
|
|
led_5210 = AR5K_PCICFG_LED_PEND;
|
|
break;
|
|
}
|
|
|
|
/*Write new status to the register*/
|
|
if (ah->ah_version != AR5K_AR5210)
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, led);
|
|
else
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, led_5210);
|
|
}
|
|
|
|
/*
|
|
* Set GPIO outputs
|
|
*/
|
|
int ath5k_hw_set_gpio_output(struct ath5k_hw *ah, u32 gpio)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
if (gpio > AR5K_NUM_GPIO)
|
|
return -EINVAL;
|
|
|
|
ath5k_hw_reg_write(ah, (ath5k_hw_reg_read(ah, AR5K_GPIOCR) &~
|
|
AR5K_GPIOCR_OUT(gpio)) | AR5K_GPIOCR_OUT(gpio), AR5K_GPIOCR);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Set GPIO inputs
|
|
*/
|
|
int ath5k_hw_set_gpio_input(struct ath5k_hw *ah, u32 gpio)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
if (gpio > AR5K_NUM_GPIO)
|
|
return -EINVAL;
|
|
|
|
ath5k_hw_reg_write(ah, (ath5k_hw_reg_read(ah, AR5K_GPIOCR) &~
|
|
AR5K_GPIOCR_OUT(gpio)) | AR5K_GPIOCR_IN(gpio), AR5K_GPIOCR);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Get GPIO state
|
|
*/
|
|
u32 ath5k_hw_get_gpio(struct ath5k_hw *ah, u32 gpio)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
if (gpio > AR5K_NUM_GPIO)
|
|
return 0xffffffff;
|
|
|
|
/* GPIO input magic */
|
|
return ((ath5k_hw_reg_read(ah, AR5K_GPIODI) & AR5K_GPIODI_M) >> gpio) &
|
|
0x1;
|
|
}
|
|
|
|
/*
|
|
* Set GPIO state
|
|
*/
|
|
int ath5k_hw_set_gpio(struct ath5k_hw *ah, u32 gpio, u32 val)
|
|
{
|
|
u32 data;
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
|
|
if (gpio > AR5K_NUM_GPIO)
|
|
return -EINVAL;
|
|
|
|
/* GPIO output magic */
|
|
data = ath5k_hw_reg_read(ah, AR5K_GPIODO);
|
|
|
|
data &= ~(1 << gpio);
|
|
data |= (val & 1) << gpio;
|
|
|
|
ath5k_hw_reg_write(ah, data, AR5K_GPIODO);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Initialize the GPIO interrupt (RFKill switch)
|
|
*/
|
|
void ath5k_hw_set_gpio_intr(struct ath5k_hw *ah, unsigned int gpio,
|
|
u32 interrupt_level)
|
|
{
|
|
u32 data;
|
|
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
if (gpio > AR5K_NUM_GPIO)
|
|
return;
|
|
|
|
/*
|
|
* Set the GPIO interrupt
|
|
*/
|
|
data = (ath5k_hw_reg_read(ah, AR5K_GPIOCR) &
|
|
~(AR5K_GPIOCR_INT_SEL(gpio) | AR5K_GPIOCR_INT_SELH |
|
|
AR5K_GPIOCR_INT_ENA | AR5K_GPIOCR_OUT(gpio))) |
|
|
(AR5K_GPIOCR_INT_SEL(gpio) | AR5K_GPIOCR_INT_ENA);
|
|
|
|
ath5k_hw_reg_write(ah, interrupt_level ? data :
|
|
(data | AR5K_GPIOCR_INT_SELH), AR5K_GPIOCR);
|
|
|
|
ah->ah_imr |= AR5K_IMR_GPIO;
|
|
|
|
/* Enable GPIO interrupts */
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_PIMR, AR5K_IMR_GPIO);
|
|
}
|
|
|
|
|
|
/*********************************\
|
|
Regulatory Domain/Channels Setup
|
|
\*********************************/
|
|
|
|
u16 ath5k_get_regdomain(struct ath5k_hw *ah)
|
|
{
|
|
u16 regdomain;
|
|
enum ath5k_regdom ieee_regdomain;
|
|
#ifdef COUNTRYCODE
|
|
u16 code;
|
|
#endif
|
|
|
|
ath5k_eeprom_regulation_domain(ah, false, &ieee_regdomain);
|
|
ah->ah_capabilities.cap_regdomain.reg_hw = ieee_regdomain;
|
|
|
|
#ifdef COUNTRYCODE
|
|
/*
|
|
* Get the regulation domain by country code. This will ignore
|
|
* the settings found in the EEPROM.
|
|
*/
|
|
code = ieee80211_name2countrycode(COUNTRYCODE);
|
|
ieee_regdomain = ieee80211_countrycode2regdomain(code);
|
|
#endif
|
|
|
|
regdomain = ath5k_regdom_from_ieee(ieee_regdomain);
|
|
ah->ah_capabilities.cap_regdomain.reg_current = regdomain;
|
|
|
|
return regdomain;
|
|
}
|
|
|
|
|
|
/****************\
|
|
Misc functions
|
|
\****************/
|
|
|
|
int ath5k_hw_get_capability(struct ath5k_hw *ah,
|
|
enum ath5k_capability_type cap_type,
|
|
u32 capability, u32 *result)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
|
|
switch (cap_type) {
|
|
case AR5K_CAP_NUM_TXQUEUES:
|
|
if (result) {
|
|
if (ah->ah_version == AR5K_AR5210)
|
|
*result = AR5K_NUM_TX_QUEUES_NOQCU;
|
|
else
|
|
*result = AR5K_NUM_TX_QUEUES;
|
|
goto yes;
|
|
}
|
|
case AR5K_CAP_VEOL:
|
|
goto yes;
|
|
case AR5K_CAP_COMPRESSION:
|
|
if (ah->ah_version == AR5K_AR5212)
|
|
goto yes;
|
|
else
|
|
goto no;
|
|
case AR5K_CAP_BURST:
|
|
goto yes;
|
|
case AR5K_CAP_TPC:
|
|
goto yes;
|
|
case AR5K_CAP_BSSIDMASK:
|
|
if (ah->ah_version == AR5K_AR5212)
|
|
goto yes;
|
|
else
|
|
goto no;
|
|
case AR5K_CAP_XR:
|
|
if (ah->ah_version == AR5K_AR5212)
|
|
goto yes;
|
|
else
|
|
goto no;
|
|
default:
|
|
goto no;
|
|
}
|
|
|
|
no:
|
|
return -EINVAL;
|
|
yes:
|
|
return 0;
|
|
}
|
|
|
|
static int ath5k_hw_enable_pspoll(struct ath5k_hw *ah, u8 *bssid,
|
|
u16 assoc_id)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
|
|
if (ah->ah_version == AR5K_AR5210) {
|
|
AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1,
|
|
AR5K_STA_ID1_NO_PSPOLL | AR5K_STA_ID1_DEFAULT_ANTENNA);
|
|
return 0;
|
|
}
|
|
|
|
return -EIO;
|
|
}
|
|
|
|
static int ath5k_hw_disable_pspoll(struct ath5k_hw *ah)
|
|
{
|
|
ATH5K_TRACE(ah->ah_sc);
|
|
|
|
if (ah->ah_version == AR5K_AR5210) {
|
|
AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1,
|
|
AR5K_STA_ID1_NO_PSPOLL | AR5K_STA_ID1_DEFAULT_ANTENNA);
|
|
return 0;
|
|
}
|
|
|
|
return -EIO;
|
|
}
|