Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,73 @@
# SPDX-License-Identifier: ISC
config ATH5K
tristate "Atheros 5xxx wireless cards support"
depends on (PCI || ATH25) && MAC80211
select ATH_COMMON
select MAC80211_LEDS if LEDS_CLASS=y || LEDS_CLASS=MAC80211
select ATH5K_AHB if ATH25
select ATH5K_PCI if !ATH25
help
This module adds support for wireless adapters based on
Atheros 5xxx chipset.
Currently the following chip versions are supported:
MAC: AR5211 AR5212
PHY: RF5111/2111 RF5112/2112 RF5413/2413
This driver uses the kernel's mac80211 subsystem.
If you choose to build a module, it'll be called ath5k. Say M if
unsure.
config ATH5K_DEBUG
bool "Atheros 5xxx debugging"
depends on ATH5K
help
Atheros 5xxx debugging messages.
Say Y, if and you will get debug options for ath5k.
To use this, you need to mount debugfs:
mount -t debugfs debug /sys/kernel/debug
You will get access to files under:
/sys/kernel/debug/ath5k/phy0/
To enable debug, pass the debug level to the debug module
parameter. For example:
modprobe ath5k debug=0x00000400
config ATH5K_TRACER
bool "Atheros 5xxx tracer"
depends on ATH5K
depends on EVENT_TRACING
help
Say Y here to enable tracepoints for the ath5k driver
using the kernel tracing infrastructure. Select this
option if you are interested in debugging the driver.
If unsure, say N.
config ATH5K_AHB
bool "Atheros 5xxx AHB bus support"
depends on ATH25 && ATH5K
help
This adds support for WiSoC type chipsets of the 5xxx Atheros
family.
config ATH5K_PCI
bool "Atheros 5xxx PCI bus support"
depends on (!ATH25 && PCI)
help
This adds support for PCI type chipsets of the 5xxx Atheros
family.
config ATH5K_TEST_CHANNELS
bool "Enables testing channels on ath5k"
depends on ATH5K && CFG80211_CERTIFICATION_ONUS
help
This enables non-standard IEEE 802.11 channels on ath5k, which
can be used for research purposes. This option should be disabled
unless doing research.
@@ -0,0 +1,23 @@
# SPDX-License-Identifier: ISC
ath5k-y += caps.o
ath5k-y += initvals.o
ath5k-y += eeprom.o
ath5k-y += gpio.o
ath5k-y += desc.o
ath5k-y += dma.o
ath5k-y += qcu.o
ath5k-y += pcu.o
ath5k-y += phy.o
ath5k-y += reset.o
ath5k-y += attach.o
ath5k-y += base.o
CFLAGS_base.o += -I$(src)
ath5k-y += led.o
ath5k-y += rfkill.o
ath5k-y += ani.o
ath5k-y += sysfs.o
ath5k-y += mac80211-ops.o
ath5k-$(CONFIG_ATH5K_DEBUG) += debug.o
ath5k-$(CONFIG_ATH5K_AHB) += ahb.o
ath5k-$(CONFIG_ATH5K_PCI) += pci.o
obj-$(CONFIG_ATH5K) += ath5k.o
@@ -0,0 +1,228 @@
/*
* Copyright (c) 2008-2009 Atheros Communications Inc.
* Copyright (c) 2009 Gabor Juhos <juhosg@openwrt.org>
* Copyright (c) 2009 Imre Kaloz <kaloz@openwrt.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/module.h>
#include <linux/nl80211.h>
#include <linux/platform_device.h>
#include <linux/etherdevice.h>
#include <ath25_platform.h>
#include "ath5k.h"
#include "debug.h"
#include "base.h"
#include "reg.h"
/* return bus cachesize in 4B word units */
static void ath5k_ahb_read_cachesize(struct ath_common *common, int *csz)
{
*csz = L1_CACHE_BYTES >> 2;
}
static bool
ath5k_ahb_eeprom_read(struct ath_common *common, u32 off, u16 *data)
{
struct ath5k_hw *ah = common->priv;
struct platform_device *pdev = to_platform_device(ah->dev);
struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev);
u16 *eeprom, *eeprom_end;
eeprom = (u16 *) bcfg->radio;
eeprom_end = ((void *) bcfg->config) + BOARD_CONFIG_BUFSZ;
eeprom += off;
if (eeprom > eeprom_end)
return false;
*data = *eeprom;
return true;
}
int ath5k_hw_read_srev(struct ath5k_hw *ah)
{
struct platform_device *pdev = to_platform_device(ah->dev);
struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev);
ah->ah_mac_srev = bcfg->devid;
return 0;
}
static int ath5k_ahb_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac)
{
struct platform_device *pdev = to_platform_device(ah->dev);
struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev);
u8 *cfg_mac;
if (to_platform_device(ah->dev)->id == 0)
cfg_mac = bcfg->config->wlan0_mac;
else
cfg_mac = bcfg->config->wlan1_mac;
memcpy(mac, cfg_mac, ETH_ALEN);
return 0;
}
static const struct ath_bus_ops ath_ahb_bus_ops = {
.ath_bus_type = ATH_AHB,
.read_cachesize = ath5k_ahb_read_cachesize,
.eeprom_read = ath5k_ahb_eeprom_read,
.eeprom_read_mac = ath5k_ahb_eeprom_read_mac,
};
/*Initialization*/
static int ath_ahb_probe(struct platform_device *pdev)
{
struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev);
struct ath5k_hw *ah;
struct ieee80211_hw *hw;
struct resource *res;
void __iomem *mem;
int irq;
int ret = 0;
u32 reg;
if (!dev_get_platdata(&pdev->dev)) {
dev_err(&pdev->dev, "no platform data specified\n");
ret = -EINVAL;
goto err_out;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "no memory resource found\n");
ret = -ENXIO;
goto err_out;
}
mem = ioremap(res->start, resource_size(res));
if (mem == NULL) {
dev_err(&pdev->dev, "ioremap failed\n");
ret = -ENOMEM;
goto err_out;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
ret = irq;
goto err_iounmap;
}
hw = ieee80211_alloc_hw(sizeof(struct ath5k_hw), &ath5k_hw_ops);
if (hw == NULL) {
dev_err(&pdev->dev, "no memory for ieee80211_hw\n");
ret = -ENOMEM;
goto err_iounmap;
}
ah = hw->priv;
ah->hw = hw;
ah->dev = &pdev->dev;
ah->iobase = mem;
ah->irq = irq;
ah->devid = bcfg->devid;
if (bcfg->devid >= AR5K_SREV_AR2315_R6) {
/* Enable WMAC AHB arbitration */
reg = ioread32((void __iomem *) AR5K_AR2315_AHB_ARB_CTL);
reg |= AR5K_AR2315_AHB_ARB_CTL_WLAN;
iowrite32(reg, (void __iomem *) AR5K_AR2315_AHB_ARB_CTL);
/* Enable global WMAC swapping */
reg = ioread32((void __iomem *) AR5K_AR2315_BYTESWAP);
reg |= AR5K_AR2315_BYTESWAP_WMAC;
iowrite32(reg, (void __iomem *) AR5K_AR2315_BYTESWAP);
} else {
/* Enable WMAC DMA access (assuming 5312 or 231x*/
/* TODO: check other platforms */
reg = ioread32((void __iomem *) AR5K_AR5312_ENABLE);
if (to_platform_device(ah->dev)->id == 0)
reg |= AR5K_AR5312_ENABLE_WLAN0;
else
reg |= AR5K_AR5312_ENABLE_WLAN1;
iowrite32(reg, (void __iomem *) AR5K_AR5312_ENABLE);
/*
* On a dual-band AR5312, the multiband radio is only
* used as pass-through. Disable 2 GHz support in the
* driver for it
*/
if (to_platform_device(ah->dev)->id == 0 &&
(bcfg->config->flags & (BD_WLAN0 | BD_WLAN1)) ==
(BD_WLAN1 | BD_WLAN0))
ah->ah_capabilities.cap_needs_2GHz_ovr = true;
else
ah->ah_capabilities.cap_needs_2GHz_ovr = false;
}
ret = ath5k_init_ah(ah, &ath_ahb_bus_ops);
if (ret != 0) {
dev_err(&pdev->dev, "failed to attach device, err=%d\n", ret);
ret = -ENODEV;
goto err_free_hw;
}
platform_set_drvdata(pdev, hw);
return 0;
err_free_hw:
ieee80211_free_hw(hw);
err_iounmap:
iounmap(mem);
err_out:
return ret;
}
static void ath_ahb_remove(struct platform_device *pdev)
{
struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev);
struct ieee80211_hw *hw = platform_get_drvdata(pdev);
struct ath5k_hw *ah;
u32 reg;
if (!hw)
return;
ah = hw->priv;
if (bcfg->devid >= AR5K_SREV_AR2315_R6) {
/* Disable WMAC AHB arbitration */
reg = ioread32((void __iomem *) AR5K_AR2315_AHB_ARB_CTL);
reg &= ~AR5K_AR2315_AHB_ARB_CTL_WLAN;
iowrite32(reg, (void __iomem *) AR5K_AR2315_AHB_ARB_CTL);
} else {
/*Stop DMA access */
reg = ioread32((void __iomem *) AR5K_AR5312_ENABLE);
if (to_platform_device(ah->dev)->id == 0)
reg &= ~AR5K_AR5312_ENABLE_WLAN0;
else
reg &= ~AR5K_AR5312_ENABLE_WLAN1;
iowrite32(reg, (void __iomem *) AR5K_AR5312_ENABLE);
}
ath5k_deinit_ah(ah);
iounmap(ah->iobase);
ieee80211_free_hw(hw);
}
static struct platform_driver ath_ahb_driver = {
.probe = ath_ahb_probe,
.remove = ath_ahb_remove,
.driver = {
.name = "ar231x-wmac",
},
};
module_platform_driver(ath_ahb_driver);
@@ -0,0 +1,754 @@
/*
* Copyright (C) 2010 Bruno Randolf <br1@einfach.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
#include "ani.h"
/**
* DOC: Basic ANI Operation
*
* Adaptive Noise Immunity (ANI) controls five noise immunity parameters
* depending on the amount of interference in the environment, increasing
* or reducing sensitivity as necessary.
*
* The parameters are:
*
* - "noise immunity"
*
* - "spur immunity"
*
* - "firstep level"
*
* - "OFDM weak signal detection"
*
* - "CCK weak signal detection"
*
* Basically we look at the amount of ODFM and CCK timing errors we get and then
* raise or lower immunity accordingly by setting one or more of these
* parameters.
*
* Newer chipsets have PHY error counters in hardware which will generate a MIB
* interrupt when they overflow. Older hardware has too enable PHY error frames
* by setting a RX flag and then count every single PHY error. When a specified
* threshold of errors has been reached we will raise immunity.
* Also we regularly check the amount of errors and lower or raise immunity as
* necessary.
*/
/***********************\
* ANI parameter control *
\***********************/
/**
* ath5k_ani_set_noise_immunity_level() - Set noise immunity level
* @ah: The &struct ath5k_hw
* @level: level between 0 and @ATH5K_ANI_MAX_NOISE_IMM_LVL
*/
void
ath5k_ani_set_noise_immunity_level(struct ath5k_hw *ah, int level)
{
/* TODO:
* ANI documents suggest the following five levels to use, but the HAL
* and ath9k use only the last two levels, making this
* essentially an on/off option. There *may* be a reason for this (???),
* so i stick with the HAL version for now...
*/
#if 0
static const s8 lo[] = { -52, -56, -60, -64, -70 };
static const s8 hi[] = { -18, -18, -16, -14, -12 };
static const s8 sz[] = { -34, -41, -48, -55, -62 };
static const s8 fr[] = { -70, -72, -75, -78, -80 };
#else
static const s8 lo[] = { -64, -70 };
static const s8 hi[] = { -14, -12 };
static const s8 sz[] = { -55, -62 };
static const s8 fr[] = { -78, -80 };
#endif
if (level < 0 || level >= ARRAY_SIZE(sz)) {
ATH5K_ERR(ah, "noise immunity level %d out of range",
level);
return;
}
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DESIRED_SIZE,
AR5K_PHY_DESIRED_SIZE_TOT, sz[level]);
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_AGCCOARSE,
AR5K_PHY_AGCCOARSE_LO, lo[level]);
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_AGCCOARSE,
AR5K_PHY_AGCCOARSE_HI, hi[level]);
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SIG,
AR5K_PHY_SIG_FIRPWR, fr[level]);
ah->ani_state.noise_imm_level = level;
ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level);
}
/**
* ath5k_ani_set_spur_immunity_level() - Set spur immunity level
* @ah: The &struct ath5k_hw
* @level: level between 0 and @max_spur_level (the maximum level is dependent
* on the chip revision).
*/
void
ath5k_ani_set_spur_immunity_level(struct ath5k_hw *ah, int level)
{
static const int val[] = { 2, 4, 6, 8, 10, 12, 14, 16 };
if (level < 0 || level >= ARRAY_SIZE(val) ||
level > ah->ani_state.max_spur_level) {
ATH5K_ERR(ah, "spur immunity level %d out of range",
level);
return;
}
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_OFDM_SELFCORR,
AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1, val[level]);
ah->ani_state.spur_level = level;
ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level);
}
/**
* ath5k_ani_set_firstep_level() - Set "firstep" level
* @ah: The &struct ath5k_hw
* @level: level between 0 and @ATH5K_ANI_MAX_FIRSTEP_LVL
*/
void
ath5k_ani_set_firstep_level(struct ath5k_hw *ah, int level)
{
static const int val[] = { 0, 4, 8 };
if (level < 0 || level >= ARRAY_SIZE(val)) {
ATH5K_ERR(ah, "firstep level %d out of range", level);
return;
}
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SIG,
AR5K_PHY_SIG_FIRSTEP, val[level]);
ah->ani_state.firstep_level = level;
ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level);
}
/**
* ath5k_ani_set_ofdm_weak_signal_detection() - Set OFDM weak signal detection
* @ah: The &struct ath5k_hw
* @on: turn on or off
*/
void
ath5k_ani_set_ofdm_weak_signal_detection(struct ath5k_hw *ah, bool on)
{
static const int m1l[] = { 127, 50 };
static const int m2l[] = { 127, 40 };
static const int m1[] = { 127, 0x4d };
static const int m2[] = { 127, 0x40 };
static const int m2cnt[] = { 31, 16 };
static const int m2lcnt[] = { 63, 48 };
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR,
AR5K_PHY_WEAK_OFDM_LOW_THR_M1, m1l[on]);
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR,
AR5K_PHY_WEAK_OFDM_LOW_THR_M2, m2l[on]);
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_HIGH_THR,
AR5K_PHY_WEAK_OFDM_HIGH_THR_M1, m1[on]);
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_HIGH_THR,
AR5K_PHY_WEAK_OFDM_HIGH_THR_M2, m2[on]);
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_HIGH_THR,
AR5K_PHY_WEAK_OFDM_HIGH_THR_M2_COUNT, m2cnt[on]);
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR,
AR5K_PHY_WEAK_OFDM_LOW_THR_M2_COUNT, m2lcnt[on]);
if (on)
AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR,
AR5K_PHY_WEAK_OFDM_LOW_THR_SELFCOR_EN);
else
AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR,
AR5K_PHY_WEAK_OFDM_LOW_THR_SELFCOR_EN);
ah->ani_state.ofdm_weak_sig = on;
ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "turned %s",
on ? "on" : "off");
}
/**
* ath5k_ani_set_cck_weak_signal_detection() - Set CCK weak signal detection
* @ah: The &struct ath5k_hw
* @on: turn on or off
*/
void
ath5k_ani_set_cck_weak_signal_detection(struct ath5k_hw *ah, bool on)
{
static const int val[] = { 8, 6 };
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_CCK_CROSSCORR,
AR5K_PHY_CCK_CROSSCORR_WEAK_SIG_THR, val[on]);
ah->ani_state.cck_weak_sig = on;
ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "turned %s",
on ? "on" : "off");
}
/***************\
* ANI algorithm *
\***************/
/**
* ath5k_ani_raise_immunity() - Increase noise immunity
* @ah: The &struct ath5k_hw
* @as: The &struct ath5k_ani_state
* @ofdm_trigger: If this is true we are called because of too many OFDM errors,
* the algorithm will tune more parameters then.
*
* Try to raise noise immunity (=decrease sensitivity) in several steps
* depending on the average RSSI of the beacons we received.
*/
static void
ath5k_ani_raise_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as,
bool ofdm_trigger)
{
int rssi = ewma_beacon_rssi_read(&ah->ah_beacon_rssi_avg);
ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "raise immunity (%s)",
ofdm_trigger ? "ODFM" : "CCK");
/* first: raise noise immunity */
if (as->noise_imm_level < ATH5K_ANI_MAX_NOISE_IMM_LVL) {
ath5k_ani_set_noise_immunity_level(ah, as->noise_imm_level + 1);
return;
}
/* only OFDM: raise spur immunity level */
if (ofdm_trigger &&
as->spur_level < ah->ani_state.max_spur_level) {
ath5k_ani_set_spur_immunity_level(ah, as->spur_level + 1);
return;
}
/* AP mode */
if (ah->opmode == NL80211_IFTYPE_AP) {
if (as->firstep_level < ATH5K_ANI_MAX_FIRSTEP_LVL)
ath5k_ani_set_firstep_level(ah, as->firstep_level + 1);
return;
}
/* STA and IBSS mode */
/* TODO: for IBSS mode it would be better to keep a beacon RSSI average
* per each neighbour node and use the minimum of these, to make sure we
* don't shut out a remote node by raising immunity too high. */
if (rssi > ATH5K_ANI_RSSI_THR_HIGH) {
ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
"beacon RSSI high");
/* only OFDM: beacon RSSI is high, we can disable ODFM weak
* signal detection */
if (ofdm_trigger && as->ofdm_weak_sig) {
ath5k_ani_set_ofdm_weak_signal_detection(ah, false);
ath5k_ani_set_spur_immunity_level(ah, 0);
return;
}
/* as a last resort or CCK: raise firstep level */
if (as->firstep_level < ATH5K_ANI_MAX_FIRSTEP_LVL) {
ath5k_ani_set_firstep_level(ah, as->firstep_level + 1);
return;
}
} else if (rssi > ATH5K_ANI_RSSI_THR_LOW) {
/* beacon RSSI in mid range, we need OFDM weak signal detect,
* but can raise firstep level */
ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
"beacon RSSI mid");
if (ofdm_trigger && !as->ofdm_weak_sig)
ath5k_ani_set_ofdm_weak_signal_detection(ah, true);
if (as->firstep_level < ATH5K_ANI_MAX_FIRSTEP_LVL)
ath5k_ani_set_firstep_level(ah, as->firstep_level + 1);
return;
} else if (ah->ah_current_channel->band == NL80211_BAND_2GHZ) {
/* beacon RSSI is low. in B/G mode turn of OFDM weak signal
* detect and zero firstep level to maximize CCK sensitivity */
ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
"beacon RSSI low, 2GHz");
if (ofdm_trigger && as->ofdm_weak_sig)
ath5k_ani_set_ofdm_weak_signal_detection(ah, false);
if (as->firstep_level > 0)
ath5k_ani_set_firstep_level(ah, 0);
return;
}
/* TODO: why not?:
if (as->cck_weak_sig == true) {
ath5k_ani_set_cck_weak_signal_detection(ah, false);
}
*/
}
/**
* ath5k_ani_lower_immunity() - Decrease noise immunity
* @ah: The &struct ath5k_hw
* @as: The &struct ath5k_ani_state
*
* Try to lower noise immunity (=increase sensitivity) in several steps
* depending on the average RSSI of the beacons we received.
*/
static void
ath5k_ani_lower_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as)
{
int rssi = ewma_beacon_rssi_read(&ah->ah_beacon_rssi_avg);
ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "lower immunity");
if (ah->opmode == NL80211_IFTYPE_AP) {
/* AP mode */
if (as->firstep_level > 0) {
ath5k_ani_set_firstep_level(ah, as->firstep_level - 1);
return;
}
} else {
/* STA and IBSS mode (see TODO above) */
if (rssi > ATH5K_ANI_RSSI_THR_HIGH) {
/* beacon signal is high, leave OFDM weak signal
* detection off or it may oscillate
* TODO: who said it's off??? */
} else if (rssi > ATH5K_ANI_RSSI_THR_LOW) {
/* beacon RSSI is mid-range: turn on ODFM weak signal
* detection and next, lower firstep level */
if (!as->ofdm_weak_sig) {
ath5k_ani_set_ofdm_weak_signal_detection(ah,
true);
return;
}
if (as->firstep_level > 0) {
ath5k_ani_set_firstep_level(ah,
as->firstep_level - 1);
return;
}
} else {
/* beacon signal is low: only reduce firstep level */
if (as->firstep_level > 0) {
ath5k_ani_set_firstep_level(ah,
as->firstep_level - 1);
return;
}
}
}
/* all modes */
if (as->spur_level > 0) {
ath5k_ani_set_spur_immunity_level(ah, as->spur_level - 1);
return;
}
/* finally, reduce noise immunity */
if (as->noise_imm_level > 0) {
ath5k_ani_set_noise_immunity_level(ah, as->noise_imm_level - 1);
return;
}
}
/**
* ath5k_hw_ani_get_listen_time() - Update counters and return listening time
* @ah: The &struct ath5k_hw
* @as: The &struct ath5k_ani_state
*
* Return an approximation of the time spent "listening" in milliseconds (ms)
* since the last call of this function.
* Save a snapshot of the counter values for debugging/statistics.
*/
static int
ath5k_hw_ani_get_listen_time(struct ath5k_hw *ah, struct ath5k_ani_state *as)
{
struct ath_common *common = ath5k_hw_common(ah);
int listen;
spin_lock_bh(&common->cc_lock);
ath_hw_cycle_counters_update(common);
memcpy(&as->last_cc, &common->cc_ani, sizeof(as->last_cc));
/* clears common->cc_ani */
listen = ath_hw_get_listen_time(common);
spin_unlock_bh(&common->cc_lock);
return listen;
}
/**
* ath5k_ani_save_and_clear_phy_errors() - Clear and save PHY error counters
* @ah: The &struct ath5k_hw
* @as: The &struct ath5k_ani_state
*
* Clear the PHY error counters as soon as possible, since this might be called
* from a MIB interrupt and we want to make sure we don't get interrupted again.
* Add the count of CCK and OFDM errors to our internal state, so it can be used
* by the algorithm later.
*
* Will be called from interrupt and tasklet context.
* Returns 0 if both counters are zero.
*/
static int
ath5k_ani_save_and_clear_phy_errors(struct ath5k_hw *ah,
struct ath5k_ani_state *as)
{
unsigned int ofdm_err, cck_err;
if (!ah->ah_capabilities.cap_has_phyerr_counters)
return 0;
ofdm_err = ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1);
cck_err = ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2);
/* reset counters first, we might be in a hurry (interrupt) */
ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_OFDM_TRIG_HIGH,
AR5K_PHYERR_CNT1);
ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_CCK_TRIG_HIGH,
AR5K_PHYERR_CNT2);
ofdm_err = ATH5K_ANI_OFDM_TRIG_HIGH - (ATH5K_PHYERR_CNT_MAX - ofdm_err);
cck_err = ATH5K_ANI_CCK_TRIG_HIGH - (ATH5K_PHYERR_CNT_MAX - cck_err);
/* sometimes both can be zero, especially when there is a superfluous
* second interrupt. detect that here and return an error. */
if (ofdm_err <= 0 && cck_err <= 0)
return 0;
/* avoid negative values should one of the registers overflow */
if (ofdm_err > 0) {
as->ofdm_errors += ofdm_err;
as->sum_ofdm_errors += ofdm_err;
}
if (cck_err > 0) {
as->cck_errors += cck_err;
as->sum_cck_errors += cck_err;
}
return 1;
}
/**
* ath5k_ani_period_restart() - Restart ANI period
* @as: The &struct ath5k_ani_state
*
* Just reset counters, so they are clear for the next "ani period".
*/
static void
ath5k_ani_period_restart(struct ath5k_ani_state *as)
{
/* keep last values for debugging */
as->last_ofdm_errors = as->ofdm_errors;
as->last_cck_errors = as->cck_errors;
as->last_listen = as->listen_time;
as->ofdm_errors = 0;
as->cck_errors = 0;
as->listen_time = 0;
}
/**
* ath5k_ani_calibration() - The main ANI calibration function
* @ah: The &struct ath5k_hw
*
* We count OFDM and CCK errors relative to the time where we did not send or
* receive ("listen" time) and raise or lower immunity accordingly.
* This is called regularly (every second) from the calibration timer, but also
* when an error threshold has been reached.
*
* In order to synchronize access from different contexts, this should be
* called only indirectly by scheduling the ANI tasklet!
*/
void
ath5k_ani_calibration(struct ath5k_hw *ah)
{
struct ath5k_ani_state *as = &ah->ani_state;
int listen, ofdm_high, ofdm_low, cck_high, cck_low;
/* get listen time since last call and add it to the counter because we
* might not have restarted the "ani period" last time.
* always do this to calculate the busy time also in manual mode */
listen = ath5k_hw_ani_get_listen_time(ah, as);
as->listen_time += listen;
if (as->ani_mode != ATH5K_ANI_MODE_AUTO)
return;
ath5k_ani_save_and_clear_phy_errors(ah, as);
ofdm_high = as->listen_time * ATH5K_ANI_OFDM_TRIG_HIGH / 1000;
cck_high = as->listen_time * ATH5K_ANI_CCK_TRIG_HIGH / 1000;
ofdm_low = as->listen_time * ATH5K_ANI_OFDM_TRIG_LOW / 1000;
cck_low = as->listen_time * ATH5K_ANI_CCK_TRIG_LOW / 1000;
ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
"listen %d (now %d)", as->listen_time, listen);
ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
"check high ofdm %d/%d cck %d/%d",
as->ofdm_errors, ofdm_high, as->cck_errors, cck_high);
if (as->ofdm_errors > ofdm_high || as->cck_errors > cck_high) {
/* too many PHY errors - we have to raise immunity */
bool ofdm_flag = as->ofdm_errors > ofdm_high;
ath5k_ani_raise_immunity(ah, as, ofdm_flag);
ath5k_ani_period_restart(as);
} else if (as->listen_time > 5 * ATH5K_ANI_LISTEN_PERIOD) {
/* If more than 5 (TODO: why 5?) periods have passed and we got
* relatively little errors we can try to lower immunity */
ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
"check low ofdm %d/%d cck %d/%d",
as->ofdm_errors, ofdm_low, as->cck_errors, cck_low);
if (as->ofdm_errors <= ofdm_low && as->cck_errors <= cck_low)
ath5k_ani_lower_immunity(ah, as);
ath5k_ani_period_restart(as);
}
}
/*******************\
* Interrupt handler *
\*******************/
/**
* ath5k_ani_mib_intr() - Interrupt handler for ANI MIB counters
* @ah: The &struct ath5k_hw
*
* Just read & reset the registers quickly, so they don't generate more
* interrupts, save the counters and schedule the tasklet to decide whether
* to raise immunity or not.
*
* We just need to handle PHY error counters, ath5k_hw_update_mib_counters()
* should take care of all "normal" MIB interrupts.
*/
void
ath5k_ani_mib_intr(struct ath5k_hw *ah)
{
struct ath5k_ani_state *as = &ah->ani_state;
/* nothing to do here if HW does not have PHY error counters - they
* can't be the reason for the MIB interrupt then */
if (!ah->ah_capabilities.cap_has_phyerr_counters)
return;
/* not in use but clear anyways */
ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT);
ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT);
if (ah->ani_state.ani_mode != ATH5K_ANI_MODE_AUTO)
return;
/* If one of the errors triggered, we can get a superfluous second
* interrupt, even though we have already reset the register. The
* function detects that so we can return early. */
if (ath5k_ani_save_and_clear_phy_errors(ah, as) == 0)
return;
if (as->ofdm_errors > ATH5K_ANI_OFDM_TRIG_HIGH ||
as->cck_errors > ATH5K_ANI_CCK_TRIG_HIGH)
tasklet_schedule(&ah->ani_tasklet);
}
/**
* ath5k_ani_phy_error_report - Used by older HW to report PHY errors
*
* @ah: The &struct ath5k_hw
* @phyerr: One of enum ath5k_phy_error_code
*
* This is used by hardware without PHY error counters to report PHY errors
* on a frame-by-frame basis, instead of the interrupt.
*/
void
ath5k_ani_phy_error_report(struct ath5k_hw *ah,
enum ath5k_phy_error_code phyerr)
{
struct ath5k_ani_state *as = &ah->ani_state;
if (phyerr == AR5K_RX_PHY_ERROR_OFDM_TIMING) {
as->ofdm_errors++;
if (as->ofdm_errors > ATH5K_ANI_OFDM_TRIG_HIGH)
tasklet_schedule(&ah->ani_tasklet);
} else if (phyerr == AR5K_RX_PHY_ERROR_CCK_TIMING) {
as->cck_errors++;
if (as->cck_errors > ATH5K_ANI_CCK_TRIG_HIGH)
tasklet_schedule(&ah->ani_tasklet);
}
}
/****************\
* Initialization *
\****************/
/**
* ath5k_enable_phy_err_counters() - Enable PHY error counters
* @ah: The &struct ath5k_hw
*
* Enable PHY error counters for OFDM and CCK timing errors.
*/
static void
ath5k_enable_phy_err_counters(struct ath5k_hw *ah)
{
ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_OFDM_TRIG_HIGH,
AR5K_PHYERR_CNT1);
ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_CCK_TRIG_HIGH,
AR5K_PHYERR_CNT2);
ath5k_hw_reg_write(ah, AR5K_PHY_ERR_FIL_OFDM, AR5K_PHYERR_CNT1_MASK);
ath5k_hw_reg_write(ah, AR5K_PHY_ERR_FIL_CCK, AR5K_PHYERR_CNT2_MASK);
/* not in use */
ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT);
ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT);
}
/**
* ath5k_disable_phy_err_counters() - Disable PHY error counters
* @ah: The &struct ath5k_hw
*
* Disable PHY error counters for OFDM and CCK timing errors.
*/
static void
ath5k_disable_phy_err_counters(struct ath5k_hw *ah)
{
ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT1);
ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT2);
ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT1_MASK);
ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT2_MASK);
/* not in use */
ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT);
ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT);
}
/**
* ath5k_ani_init() - Initialize ANI
* @ah: The &struct ath5k_hw
* @mode: One of enum ath5k_ani_mode
*
* Initialize ANI according to mode.
*/
void
ath5k_ani_init(struct ath5k_hw *ah, enum ath5k_ani_mode mode)
{
/* ANI is only possible on 5212 and newer */
if (ah->ah_version < AR5K_AR5212)
return;
if (mode < ATH5K_ANI_MODE_OFF || mode > ATH5K_ANI_MODE_AUTO) {
ATH5K_ERR(ah, "ANI mode %d out of range", mode);
return;
}
/* clear old state information */
memset(&ah->ani_state, 0, sizeof(ah->ani_state));
/* older hardware has more spur levels than newer */
if (ah->ah_mac_srev < AR5K_SREV_AR2414)
ah->ani_state.max_spur_level = 7;
else
ah->ani_state.max_spur_level = 2;
/* initial values for our ani parameters */
if (mode == ATH5K_ANI_MODE_OFF) {
ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "ANI off\n");
} else if (mode == ATH5K_ANI_MODE_MANUAL_LOW) {
ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
"ANI manual low -> high sensitivity\n");
ath5k_ani_set_noise_immunity_level(ah, 0);
ath5k_ani_set_spur_immunity_level(ah, 0);
ath5k_ani_set_firstep_level(ah, 0);
ath5k_ani_set_ofdm_weak_signal_detection(ah, true);
ath5k_ani_set_cck_weak_signal_detection(ah, true);
} else if (mode == ATH5K_ANI_MODE_MANUAL_HIGH) {
ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
"ANI manual high -> low sensitivity\n");
ath5k_ani_set_noise_immunity_level(ah,
ATH5K_ANI_MAX_NOISE_IMM_LVL);
ath5k_ani_set_spur_immunity_level(ah,
ah->ani_state.max_spur_level);
ath5k_ani_set_firstep_level(ah, ATH5K_ANI_MAX_FIRSTEP_LVL);
ath5k_ani_set_ofdm_weak_signal_detection(ah, false);
ath5k_ani_set_cck_weak_signal_detection(ah, false);
} else if (mode == ATH5K_ANI_MODE_AUTO) {
ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "ANI auto\n");
ath5k_ani_set_noise_immunity_level(ah, 0);
ath5k_ani_set_spur_immunity_level(ah, 0);
ath5k_ani_set_firstep_level(ah, 0);
ath5k_ani_set_ofdm_weak_signal_detection(ah, true);
ath5k_ani_set_cck_weak_signal_detection(ah, false);
}
/* newer hardware has PHY error counter registers which we can use to
* get OFDM and CCK error counts. older hardware has to set rxfilter and
* report every single PHY error by calling ath5k_ani_phy_error_report()
*/
if (mode == ATH5K_ANI_MODE_AUTO) {
if (ah->ah_capabilities.cap_has_phyerr_counters)
ath5k_enable_phy_err_counters(ah);
else
ath5k_hw_set_rx_filter(ah, ath5k_hw_get_rx_filter(ah) |
AR5K_RX_FILTER_PHYERR);
} else {
if (ah->ah_capabilities.cap_has_phyerr_counters)
ath5k_disable_phy_err_counters(ah);
else
ath5k_hw_set_rx_filter(ah, ath5k_hw_get_rx_filter(ah) &
~AR5K_RX_FILTER_PHYERR);
}
ah->ani_state.ani_mode = mode;
}
/**************\
* Debug output *
\**************/
#ifdef CONFIG_ATH5K_DEBUG
/**
* ath5k_ani_print_counters() - Print ANI counters
* @ah: The &struct ath5k_hw
*
* Used for debugging ANI
*/
void
ath5k_ani_print_counters(struct ath5k_hw *ah)
{
/* clears too */
pr_notice("ACK fail\t%d\n", ath5k_hw_reg_read(ah, AR5K_ACK_FAIL));
pr_notice("RTS fail\t%d\n", ath5k_hw_reg_read(ah, AR5K_RTS_FAIL));
pr_notice("RTS success\t%d\n", ath5k_hw_reg_read(ah, AR5K_RTS_OK));
pr_notice("FCS error\t%d\n", ath5k_hw_reg_read(ah, AR5K_FCS_FAIL));
/* no clear */
pr_notice("tx\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_TX));
pr_notice("rx\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_RX));
pr_notice("busy\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_RXCLR));
pr_notice("cycles\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_CYCLE));
pr_notice("AR5K_PHYERR_CNT1\t%d\n",
ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1));
pr_notice("AR5K_PHYERR_CNT2\t%d\n",
ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2));
pr_notice("AR5K_OFDM_FIL_CNT\t%d\n",
ath5k_hw_reg_read(ah, AR5K_OFDM_FIL_CNT));
pr_notice("AR5K_CCK_FIL_CNT\t%d\n",
ath5k_hw_reg_read(ah, AR5K_CCK_FIL_CNT));
}
#endif
@@ -0,0 +1,119 @@
/*
* Copyright (C) 2010 Bruno Randolf <br1@einfach.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef ANI_H
#define ANI_H
#include "../ath.h"
enum ath5k_phy_error_code;
/* these thresholds are relative to the ATH5K_ANI_LISTEN_PERIOD */
#define ATH5K_ANI_LISTEN_PERIOD 100
#define ATH5K_ANI_OFDM_TRIG_HIGH 500
#define ATH5K_ANI_OFDM_TRIG_LOW 200
#define ATH5K_ANI_CCK_TRIG_HIGH 200
#define ATH5K_ANI_CCK_TRIG_LOW 100
/* average beacon RSSI thresholds */
#define ATH5K_ANI_RSSI_THR_HIGH 40
#define ATH5K_ANI_RSSI_THR_LOW 7
/* maximum available levels */
#define ATH5K_ANI_MAX_FIRSTEP_LVL 2
#define ATH5K_ANI_MAX_NOISE_IMM_LVL 1
/**
* enum ath5k_ani_mode - mode for ANI / noise sensitivity
*
* @ATH5K_ANI_MODE_OFF: Turn ANI off. This can be useful to just stop the ANI
* algorithm after it has been on auto mode.
* @ATH5K_ANI_MODE_MANUAL_LOW: Manually set all immunity parameters to low,
* maximizing sensitivity. ANI will not run.
* @ATH5K_ANI_MODE_MANUAL_HIGH: Manually set all immunity parameters to high,
* minimizing sensitivity. ANI will not run.
* @ATH5K_ANI_MODE_AUTO: Automatically control immunity parameters based on the
* amount of OFDM and CCK frame errors (default).
*/
enum ath5k_ani_mode {
ATH5K_ANI_MODE_OFF = 0,
ATH5K_ANI_MODE_MANUAL_LOW = 1,
ATH5K_ANI_MODE_MANUAL_HIGH = 2,
ATH5K_ANI_MODE_AUTO = 3
};
/**
* struct ath5k_ani_state - ANI state and associated counters
* @ani_mode: One of enum ath5k_ani_mode
* @noise_imm_level: Noise immunity level
* @spur_level: Spur immunity level
* @firstep_level: FIRstep level
* @ofdm_weak_sig: OFDM weak signal detection state (on/off)
* @cck_weak_sig: CCK weak signal detection state (on/off)
* @max_spur_level: Max spur immunity level (chip specific)
* @listen_time: Listen time
* @ofdm_errors: OFDM timing error count
* @cck_errors: CCK timing error count
* @last_cc: The &struct ath_cycle_counters (for stats)
* @last_listen: Listen time from previous run (for stats)
* @last_ofdm_errors: OFDM timing error count from previous run (for tats)
* @last_cck_errors: CCK timing error count from previous run (for stats)
* @sum_ofdm_errors: Sum of OFDM timing errors (for stats)
* @sum_cck_errors: Sum of all CCK timing errors (for stats)
*/
struct ath5k_ani_state {
enum ath5k_ani_mode ani_mode;
/* state */
int noise_imm_level;
int spur_level;
int firstep_level;
bool ofdm_weak_sig;
bool cck_weak_sig;
int max_spur_level;
/* used by the algorithm */
unsigned int listen_time;
unsigned int ofdm_errors;
unsigned int cck_errors;
/* debug/statistics only: numbers from last ANI calibration */
struct ath_cycle_counters last_cc;
unsigned int last_listen;
unsigned int last_ofdm_errors;
unsigned int last_cck_errors;
unsigned int sum_ofdm_errors;
unsigned int sum_cck_errors;
};
void ath5k_ani_init(struct ath5k_hw *ah, enum ath5k_ani_mode mode);
void ath5k_ani_mib_intr(struct ath5k_hw *ah);
void ath5k_ani_calibration(struct ath5k_hw *ah);
void ath5k_ani_phy_error_report(struct ath5k_hw *ah,
enum ath5k_phy_error_code phyerr);
/* for manual control */
void ath5k_ani_set_noise_immunity_level(struct ath5k_hw *ah, int level);
void ath5k_ani_set_spur_immunity_level(struct ath5k_hw *ah, int level);
void ath5k_ani_set_firstep_level(struct ath5k_hw *ah, int level);
void ath5k_ani_set_ofdm_weak_signal_detection(struct ath5k_hw *ah, bool on);
void ath5k_ani_set_cck_weak_signal_detection(struct ath5k_hw *ah, bool on);
void ath5k_ani_print_counters(struct ath5k_hw *ah);
#endif /* ANI_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,359 @@
/*
* Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@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.
*
*/
/*************************************\
* Attach/Detach Functions and helpers *
\*************************************/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/pci.h>
#include <linux/slab.h>
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
/**
* ath5k_hw_post() - Power On Self Test helper function
* @ah: The &struct ath5k_hw
*/
static int ath5k_hw_post(struct ath5k_hw *ah)
{
static const u32 static_pattern[4] = {
0x55555555, 0xaaaaaaaa,
0x66666666, 0x99999999
};
static const u16 regs[2] = { AR5K_STA_ID0, AR5K_PHY(8) };
int i, c;
u16 cur_reg;
u32 var_pattern;
u32 init_val;
u32 cur_val;
for (c = 0; c < 2; c++) {
cur_reg = regs[c];
/* Save previous value */
init_val = ath5k_hw_reg_read(ah, cur_reg);
for (i = 0; i < 256; i++) {
var_pattern = i << 16 | i;
ath5k_hw_reg_write(ah, var_pattern, cur_reg);
cur_val = ath5k_hw_reg_read(ah, cur_reg);
if (cur_val != var_pattern) {
ATH5K_ERR(ah, "POST Failed !!!\n");
return -EAGAIN;
}
/* Found on ndiswrapper dumps */
var_pattern = 0x0039080f;
ath5k_hw_reg_write(ah, var_pattern, cur_reg);
}
for (i = 0; i < 4; i++) {
var_pattern = static_pattern[i];
ath5k_hw_reg_write(ah, var_pattern, cur_reg);
cur_val = ath5k_hw_reg_read(ah, cur_reg);
if (cur_val != var_pattern) {
ATH5K_ERR(ah, "POST Failed !!!\n");
return -EAGAIN;
}
/* Found on ndiswrapper dumps */
var_pattern = 0x003b080f;
ath5k_hw_reg_write(ah, var_pattern, cur_reg);
}
/* Restore previous value */
ath5k_hw_reg_write(ah, init_val, cur_reg);
}
return 0;
}
/**
* ath5k_hw_init() - Check if hw is supported and init the needed structs
* @ah: The &struct ath5k_hw associated with the device
*
* Check if the device is supported, perform a POST and initialize the needed
* structs. Returns -ENOMEM if we don't have memory for the needed structs,
* -ENODEV if the device is not supported or prints an error msg if something
* else went wrong.
*/
int ath5k_hw_init(struct ath5k_hw *ah)
{
static const u8 zero_mac[ETH_ALEN] = { };
struct ath_common *common = ath5k_hw_common(ah);
struct pci_dev *pdev = ah->pdev;
struct ath5k_eeprom_info *ee;
int ret;
u32 srev;
/*
* HW information
*/
ah->ah_bwmode = AR5K_BWMODE_DEFAULT;
ah->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER;
ah->ah_imr = 0;
ah->ah_retry_short = AR5K_INIT_RETRY_SHORT;
ah->ah_retry_long = AR5K_INIT_RETRY_LONG;
ah->ah_ant_mode = AR5K_ANTMODE_DEFAULT;
ah->ah_noise_floor = -95; /* until first NF calibration is run */
ah->ani_state.ani_mode = ATH5K_ANI_MODE_AUTO;
ah->ah_current_channel = &ah->channels[0];
/*
* Find the mac version
*/
ath5k_hw_read_srev(ah);
srev = ah->ah_mac_srev;
if (srev < AR5K_SREV_AR5311)
ah->ah_version = AR5K_AR5210;
else if (srev < AR5K_SREV_AR5212)
ah->ah_version = AR5K_AR5211;
else
ah->ah_version = AR5K_AR5212;
/* Get the MAC version */
ah->ah_mac_version = AR5K_REG_MS(srev, AR5K_SREV_VER);
/* Fill the ath5k_hw struct with the needed functions */
ret = ath5k_hw_init_desc_functions(ah);
if (ret)
goto err;
/* Bring device out of sleep and reset its units */
ret = ath5k_hw_nic_wakeup(ah, NULL);
if (ret)
goto err;
/* Get PHY and RADIO revisions */
ah->ah_phy_revision = ath5k_hw_reg_read(ah, AR5K_PHY_CHIP_ID) &
0xffffffff;
ah->ah_radio_5ghz_revision = ath5k_hw_radio_revision(ah,
NL80211_BAND_5GHZ);
/* Try to identify radio chip based on its srev */
switch (ah->ah_radio_5ghz_revision & 0xf0) {
case AR5K_SREV_RAD_5111:
ah->ah_radio = AR5K_RF5111;
ah->ah_single_chip = false;
ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah,
NL80211_BAND_2GHZ);
break;
case AR5K_SREV_RAD_5112:
case AR5K_SREV_RAD_2112:
ah->ah_radio = AR5K_RF5112;
ah->ah_single_chip = false;
ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah,
NL80211_BAND_2GHZ);
break;
case AR5K_SREV_RAD_2413:
ah->ah_radio = AR5K_RF2413;
ah->ah_single_chip = true;
break;
case AR5K_SREV_RAD_5413:
ah->ah_radio = AR5K_RF5413;
ah->ah_single_chip = true;
break;
case AR5K_SREV_RAD_2316:
ah->ah_radio = AR5K_RF2316;
ah->ah_single_chip = true;
break;
case AR5K_SREV_RAD_2317:
ah->ah_radio = AR5K_RF2317;
ah->ah_single_chip = true;
break;
case AR5K_SREV_RAD_5424:
if (ah->ah_mac_version == AR5K_SREV_AR2425 ||
ah->ah_mac_version == AR5K_SREV_AR2417) {
ah->ah_radio = AR5K_RF2425;
ah->ah_single_chip = true;
} else {
ah->ah_radio = AR5K_RF5413;
ah->ah_single_chip = true;
}
break;
default:
/* Identify radio based on mac/phy srev */
if (ah->ah_version == AR5K_AR5210) {
ah->ah_radio = AR5K_RF5110;
ah->ah_single_chip = false;
} else if (ah->ah_version == AR5K_AR5211) {
ah->ah_radio = AR5K_RF5111;
ah->ah_single_chip = false;
ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah,
NL80211_BAND_2GHZ);
} else if (ah->ah_mac_version == (AR5K_SREV_AR2425 >> 4) ||
ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4) ||
ah->ah_phy_revision == AR5K_SREV_PHY_2425) {
ah->ah_radio = AR5K_RF2425;
ah->ah_single_chip = true;
ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_2425;
} else if (srev == AR5K_SREV_AR5213A &&
ah->ah_phy_revision == AR5K_SREV_PHY_5212B) {
ah->ah_radio = AR5K_RF5112;
ah->ah_single_chip = false;
ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_5112B;
} else if (ah->ah_mac_version == (AR5K_SREV_AR2415 >> 4) ||
ah->ah_mac_version == (AR5K_SREV_AR2315_R6 >> 4)) {
ah->ah_radio = AR5K_RF2316;
ah->ah_single_chip = true;
ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_2316;
} else if (ah->ah_mac_version == (AR5K_SREV_AR5414 >> 4) ||
ah->ah_phy_revision == AR5K_SREV_PHY_5413) {
ah->ah_radio = AR5K_RF5413;
ah->ah_single_chip = true;
ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_5413;
} else if (ah->ah_mac_version == (AR5K_SREV_AR2414 >> 4) ||
ah->ah_phy_revision == AR5K_SREV_PHY_2413) {
ah->ah_radio = AR5K_RF2413;
ah->ah_single_chip = true;
ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_2413;
} else {
ATH5K_ERR(ah, "Couldn't identify radio revision.\n");
ret = -ENODEV;
goto err;
}
}
/* Return on unsupported chips (unsupported eeprom etc) */
if ((srev >= AR5K_SREV_AR5416) && (srev < AR5K_SREV_AR2425)) {
ATH5K_ERR(ah, "Device not yet supported.\n");
ret = -ENODEV;
goto err;
}
/*
* POST
*/
ret = ath5k_hw_post(ah);
if (ret)
goto err;
/* Enable pci core retry fix on Hainan (5213A) and later chips */
if (srev >= AR5K_SREV_AR5213A)
AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_RETRY_FIX);
/*
* Get card capabilities, calibration values etc
* TODO: EEPROM work
*/
ret = ath5k_eeprom_init(ah);
if (ret) {
ATH5K_ERR(ah, "unable to init EEPROM\n");
goto err;
}
ee = &ah->ah_capabilities.cap_eeprom;
/*
* Write PCI-E power save settings
*/
if ((ah->ah_version == AR5K_AR5212) && pdev && (pci_is_pcie(pdev))) {
ath5k_hw_reg_write(ah, 0x9248fc00, AR5K_PCIE_SERDES);
ath5k_hw_reg_write(ah, 0x24924924, AR5K_PCIE_SERDES);
/* Shut off RX when elecidle is asserted */
ath5k_hw_reg_write(ah, 0x28000039, AR5K_PCIE_SERDES);
ath5k_hw_reg_write(ah, 0x53160824, AR5K_PCIE_SERDES);
/* If serdes programming is enabled, increase PCI-E
* tx power for systems with long trace from host
* to minicard connector. */
if (ee->ee_serdes)
ath5k_hw_reg_write(ah, 0xe5980579, AR5K_PCIE_SERDES);
else
ath5k_hw_reg_write(ah, 0xf6800579, AR5K_PCIE_SERDES);
/* Shut off PLL and CLKREQ active in L1 */
ath5k_hw_reg_write(ah, 0x001defff, AR5K_PCIE_SERDES);
/* Preserve other settings */
ath5k_hw_reg_write(ah, 0x1aaabe40, AR5K_PCIE_SERDES);
ath5k_hw_reg_write(ah, 0xbe105554, AR5K_PCIE_SERDES);
ath5k_hw_reg_write(ah, 0x000e3007, AR5K_PCIE_SERDES);
/* Reset SERDES to load new settings */
ath5k_hw_reg_write(ah, 0x00000000, AR5K_PCIE_SERDES_RESET);
usleep_range(1000, 1500);
}
/* Get misc capabilities */
ret = ath5k_hw_set_capabilities(ah);
if (ret) {
ATH5K_ERR(ah, "unable to get device capabilities\n");
goto err;
}
/* Crypto settings */
common->keymax = (ah->ah_version == AR5K_AR5210 ?
AR5K_KEYTABLE_SIZE_5210 : AR5K_KEYTABLE_SIZE_5211);
if (srev >= AR5K_SREV_AR5212_V4 &&
(ee->ee_version < AR5K_EEPROM_VERSION_5_0 ||
!AR5K_EEPROM_AES_DIS(ee->ee_misc5)))
common->crypt_caps |= ATH_CRYPT_CAP_CIPHER_AESCCM;
if (srev >= AR5K_SREV_AR2414) {
common->crypt_caps |= ATH_CRYPT_CAP_MIC_COMBINED;
AR5K_REG_ENABLE_BITS(ah, AR5K_MISC_MODE,
AR5K_MISC_MODE_COMBINED_MIC);
}
/* MAC address is cleared until add_interface */
ath5k_hw_set_lladdr(ah, zero_mac);
/* Set BSSID to bcast address: ff:ff:ff:ff:ff:ff for now */
eth_broadcast_addr(common->curbssid);
ath5k_hw_set_bssid(ah);
ath5k_hw_set_opmode(ah, ah->opmode);
ath5k_hw_rfgain_opt_init(ah);
ath5k_hw_init_nfcal_hist(ah);
/* turn on HW LEDs */
ath5k_hw_set_ledstate(ah, AR5K_LED_INIT);
return 0;
err:
return ret;
}
/**
* ath5k_hw_deinit() - Free the &struct ath5k_hw
* @ah: The &struct ath5k_hw
*/
void ath5k_hw_deinit(struct ath5k_hw *ah)
{
__set_bit(ATH_STAT_INVALID, ah->status);
kfree(ah->ah_rf_banks);
ath5k_eeprom_detach(ah);
/* assume interrupts are down */
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,121 @@
/*-
* Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
* redistribution must be conditioned upon including a substantially
* similar Disclaimer requirement for further binary redistribution.
* 3. Neither the names of the above-listed copyright holders nor the names
* of any contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGES.
*
*/
/*
* Definitions for the Atheros Wireless LAN controller driver.
*/
#ifndef _DEV_ATH5K_BASE_H
#define _DEV_ATH5K_BASE_H
struct ieee80211_vif;
struct ieee80211_hw;
struct ath5k_hw;
struct ath5k_txq;
struct ieee80211_channel;
struct ath_bus_ops;
struct ieee80211_tx_control;
enum nl80211_iftype;
enum ath5k_srev_type {
AR5K_VERSION_MAC,
AR5K_VERSION_RAD,
};
struct ath5k_srev_name {
const char *sr_name;
enum ath5k_srev_type sr_type;
u_int sr_val;
};
struct ath5k_buf {
struct list_head list;
struct ath5k_desc *desc; /* virtual addr of desc */
dma_addr_t daddr; /* physical addr of desc */
struct sk_buff *skb; /* skbuff for buf */
dma_addr_t skbaddr; /* physical addr of skb data */
struct ieee80211_tx_rate rates[4]; /* number of multi-rate stages */
};
struct ath5k_vif {
bool assoc; /* are we associated or not */
enum nl80211_iftype opmode;
int bslot;
struct ath5k_buf *bbuf; /* beacon buffer */
};
struct ath5k_vif_iter_data {
const u8 *hw_macaddr;
u8 mask[ETH_ALEN];
u8 active_mac[ETH_ALEN]; /* first active MAC */
bool need_set_hw_addr;
bool found_active;
bool any_assoc;
enum nl80211_iftype opmode;
int n_stas;
};
void ath5k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif);
bool ath5k_any_vif_assoc(struct ath5k_hw *ah);
int ath5k_start(struct ieee80211_hw *hw);
void ath5k_stop(struct ieee80211_hw *hw, bool suspend);
void ath5k_beacon_update_timers(struct ath5k_hw *ah, u64 bc_tsf);
int ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
void ath5k_beacon_config(struct ath5k_hw *ah);
void ath5k_set_beacon_filter(struct ieee80211_hw *hw, bool enable);
void ath5k_update_bssid_mask_and_opmode(struct ath5k_hw *ah,
struct ieee80211_vif *vif);
int ath5k_chan_set(struct ath5k_hw *ah, struct cfg80211_chan_def *chandef);
void ath5k_txbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf);
void ath5k_rxbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf);
void ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
struct ath5k_txq *txq, struct ieee80211_tx_control *control);
const char *ath5k_chip_name(enum ath5k_srev_type type, u_int16_t val);
int ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops);
void ath5k_deinit_ah(struct ath5k_hw *ah);
/* Check whether BSSID mask is supported */
#define ath5k_hw_hasbssidmask(_ah) (ah->ah_version == AR5K_AR5212)
/* Check whether virtual EOL is supported */
#define ath5k_hw_hasveol(_ah) (ah->ah_version != AR5K_AR5210)
#endif /* _DEV_ATH5K_BASE_H */
@@ -0,0 +1,154 @@
/*
* Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com>
* Copyright (c) 2007-2008 Jiri Slaby <jirislaby@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
/**************\
* Capabilities *
\**************/
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
#include "../regd.h"
/*
* Fill the capabilities struct
* TODO: Merge this with EEPROM code when we are done with it
*/
int ath5k_hw_set_capabilities(struct ath5k_hw *ah)
{
struct ath5k_capabilities *caps = &ah->ah_capabilities;
u16 ee_header;
/* Capabilities stored in the EEPROM */
ee_header = caps->cap_eeprom.ee_header;
if (ah->ah_version == AR5K_AR5210) {
/*
* Set radio capabilities
* (The AR5110 only supports the middle 5GHz band)
*/
caps->cap_range.range_5ghz_min = 5120;
caps->cap_range.range_5ghz_max = 5430;
caps->cap_range.range_2ghz_min = 0;
caps->cap_range.range_2ghz_max = 0;
/* Set supported modes */
__set_bit(AR5K_MODE_11A, caps->cap_mode);
} else {
/*
* XXX The transceiver supports frequencies from 4920 to 6100MHz
* XXX and from 2312 to 2732MHz. 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 the
* XXX legal range.
*/
/*
* Set radio capabilities
*/
if (AR5K_EEPROM_HDR_11A(ee_header)) {
if (ath_is_49ghz_allowed(caps->cap_eeprom.ee_regdomain))
caps->cap_range.range_5ghz_min = 4920;
else
caps->cap_range.range_5ghz_min = 5005;
caps->cap_range.range_5ghz_max = 6100;
/* Set supported modes */
__set_bit(AR5K_MODE_11A, caps->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_version != AR5K_AR5211)) {
/* 2312 */
caps->cap_range.range_2ghz_min = 2412;
caps->cap_range.range_2ghz_max = 2732;
/* Override 2GHz modes on SoCs that need it
* NOTE: cap_needs_2GHz_ovr gets set from
* ath_ahb_probe */
if (!caps->cap_needs_2GHz_ovr) {
if (AR5K_EEPROM_HDR_11B(ee_header))
__set_bit(AR5K_MODE_11B,
caps->cap_mode);
if (AR5K_EEPROM_HDR_11G(ee_header) &&
ah->ah_version != AR5K_AR5211)
__set_bit(AR5K_MODE_11G,
caps->cap_mode);
}
}
}
if ((ah->ah_radio_5ghz_revision & 0xf0) == AR5K_SREV_RAD_2112)
__clear_bit(AR5K_MODE_11A, caps->cap_mode);
/* Set number of supported TX queues */
if (ah->ah_version == AR5K_AR5210)
caps->cap_queues.q_tx_num = AR5K_NUM_TX_QUEUES_NOQCU;
else
caps->cap_queues.q_tx_num = AR5K_NUM_TX_QUEUES;
/* Newer hardware has PHY error counters */
if (ah->ah_mac_srev >= AR5K_SREV_AR5213A)
caps->cap_has_phyerr_counters = true;
else
caps->cap_has_phyerr_counters = false;
/* MACs since AR5212 have MRR support */
if (ah->ah_version == AR5K_AR5212)
caps->cap_has_mrr_support = true;
else
caps->cap_has_mrr_support = false;
return 0;
}
/*
* TODO: Following functions should be part of a new function
* set_capability
*/
int ath5k_hw_enable_pspoll(struct ath5k_hw *ah, u8 *bssid,
u16 assoc_id)
{
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;
}
int ath5k_hw_disable_pspoll(struct ath5k_hw *ah)
{
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;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,163 @@
/*
* Copyright (c) 2007 Bruno Randolf <bruno@thinktube.com>
*
* This file is free software: you may copy, redistribute and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 2 of the License, or (at your
* option) any later version.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
* Copyright (c) 2004-2005 Atheros Communications, Inc.
* Copyright (c) 2006 Devicescape Software, Inc.
* Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com>
* Copyright (c) 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
* redistribution must be conditioned upon including a substantially
* similar Disclaimer requirement for further binary redistribution.
* 3. Neither the names of the above-listed copyright holders nor the names
* of any contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGES.
*/
#ifndef _ATH5K_DEBUG_H
#define _ATH5K_DEBUG_H
struct ath5k_hw;
struct sk_buff;
struct ath5k_buf;
struct ath5k_dbg_info {
unsigned int level; /* debug level */
};
/**
* enum ath5k_debug_level - ath5k debug level
*
* @ATH5K_DEBUG_RESET: reset processing
* @ATH5K_DEBUG_INTR: interrupt handling
* @ATH5K_DEBUG_MODE: mode init/setup
* @ATH5K_DEBUG_XMIT: basic xmit operation
* @ATH5K_DEBUG_BEACON: beacon handling
* @ATH5K_DEBUG_CALIBRATE: periodic calibration
* @ATH5K_DEBUG_TXPOWER: transmit power setting
* @ATH5K_DEBUG_LED: led management
* @ATH5K_DEBUG_DUMPBANDS: dump bands
* @ATH5K_DEBUG_DMA: debug dma start/stop
* @ATH5K_DEBUG_ANI: debug Adaptive Noise Immunity
* @ATH5K_DEBUG_DESC: descriptor setup
* @ATH5K_DEBUG_ANY: show at any debug level
*
* The debug level is used to control the amount and type of debugging output
* we want to see. The debug level is given in calls to ATH5K_DBG to specify
* where the message should appear, and the user can control the debugging
* messages he wants to see, either by the module parameter 'debug' on module
* load, or dynamically by using debugfs 'ath5k/phyX/debug'. these levels can
* be combined together by bitwise OR.
*/
enum ath5k_debug_level {
ATH5K_DEBUG_RESET = 0x00000001,
ATH5K_DEBUG_INTR = 0x00000002,
ATH5K_DEBUG_MODE = 0x00000004,
ATH5K_DEBUG_XMIT = 0x00000008,
ATH5K_DEBUG_BEACON = 0x00000010,
ATH5K_DEBUG_CALIBRATE = 0x00000020,
ATH5K_DEBUG_TXPOWER = 0x00000040,
ATH5K_DEBUG_LED = 0x00000080,
ATH5K_DEBUG_DUMPBANDS = 0x00000400,
ATH5K_DEBUG_DMA = 0x00000800,
ATH5K_DEBUG_ANI = 0x00002000,
ATH5K_DEBUG_DESC = 0x00004000,
ATH5K_DEBUG_ANY = 0xffffffff
};
#ifdef CONFIG_ATH5K_DEBUG
#define ATH5K_DBG(_sc, _m, _fmt, ...) do { \
if (unlikely((_sc)->debug.level & (_m) && net_ratelimit())) \
ATH5K_PRINTK(_sc, KERN_DEBUG, "(%s:%d): " _fmt, \
__func__, __LINE__, ##__VA_ARGS__); \
} while (0)
#define ATH5K_DBG_UNLIMIT(_sc, _m, _fmt, ...) do { \
if (unlikely((_sc)->debug.level & (_m))) \
ATH5K_PRINTK(_sc, KERN_DEBUG, "(%s:%d): " _fmt, \
__func__, __LINE__, ##__VA_ARGS__); \
} while (0)
void
ath5k_debug_init_device(struct ath5k_hw *ah);
void
ath5k_debug_printrxbuffs(struct ath5k_hw *ah);
void
ath5k_debug_dump_bands(struct ath5k_hw *ah);
void
ath5k_debug_printtxbuf(struct ath5k_hw *ah, struct ath5k_buf *bf);
#else /* no debugging */
#include <linux/compiler.h>
static inline __printf(3, 4) void
ATH5K_DBG(struct ath5k_hw *ah, unsigned int m, const char *fmt, ...) {}
static inline __printf(3, 4) void
ATH5K_DBG_UNLIMIT(struct ath5k_hw *ah, unsigned int m, const char *fmt, ...)
{}
static inline void
ath5k_debug_init_device(struct ath5k_hw *ah) {}
static inline void
ath5k_debug_printrxbuffs(struct ath5k_hw *ah) {}
static inline void
ath5k_debug_dump_bands(struct ath5k_hw *ah) {}
static inline void
ath5k_debug_printtxbuf(struct ath5k_hw *ah, struct ath5k_buf *bf) {}
#endif /* ifdef CONFIG_ATH5K_DEBUG */
#endif /* ifndef _ATH5K_DEBUG_H */
@@ -0,0 +1,786 @@
/*
* Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com>
* Copyright (c) 2007-2008 Pavel Roskin <proski@gnu.org>
*
* 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.
*
*/
/******************************\
Hardware Descriptor Functions
\******************************/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
/**
* DOC: Hardware descriptor functions
*
* Here we handle the processing of the low-level hw descriptors
* that hw reads and writes via DMA for each TX and RX attempt (that means
* we can also have descriptors for failed TX/RX tries). We have two kind of
* descriptors for RX and TX, control descriptors tell the hw how to send or
* receive a packet where to read/write it from/to etc and status descriptors
* that contain information about how the packet was sent or received (errors
* included).
*
* Descriptor format is not exactly the same for each MAC chip version so we
* have function pointers on &struct ath5k_hw we initialize at runtime based on
* the chip used.
*/
/************************\
* TX Control descriptors *
\************************/
/**
* ath5k_hw_setup_2word_tx_desc() - Initialize a 2-word tx control descriptor
* @ah: The &struct ath5k_hw
* @desc: The &struct ath5k_desc
* @pkt_len: Frame length in bytes
* @hdr_len: Header length in bytes (only used on AR5210)
* @padsize: Any padding we've added to the frame length
* @type: One of enum ath5k_pkt_type
* @tx_power: Tx power in 0.5dB steps
* @tx_rate0: HW idx for transmission rate
* @tx_tries0: Max number of retransmissions
* @key_index: Index on key table to use for encryption
* @antenna_mode: Which antenna to use (0 for auto)
* @flags: One of AR5K_TXDESC_* flags (desc.h)
* @rtscts_rate: HW idx for RTS/CTS transmission rate
* @rtscts_duration: What to put on duration field on the header of RTS/CTS
*
* Internal function to initialize a 2-Word TX control descriptor
* found on AR5210 and AR5211 MACs chips.
*
* Returns 0 on success or -EINVAL on false input
*/
static int
ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah,
struct ath5k_desc *desc,
unsigned int pkt_len, unsigned int hdr_len,
int padsize,
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_ctl *tx_ctl;
unsigned int frame_len;
tx_ctl = &desc->ud.ds_tx5210.tx_ctl;
/*
* Validate input
* - Zero retries don't make sense.
* - A zero rate will put the HW into a mode where it continuously sends
* noise on the channel, so it is important to avoid this.
*/
if (unlikely(tx_tries0 == 0)) {
ATH5K_ERR(ah, "zero retries\n");
WARN_ON(1);
return -EINVAL;
}
if (unlikely(tx_rate0 == 0)) {
ATH5K_ERR(ah, "zero rate\n");
WARN_ON(1);
return -EINVAL;
}
/* Clear descriptor */
memset(&desc->ud.ds_tx5210, 0, sizeof(struct ath5k_hw_5210_tx_desc));
/* Setup control descriptor */
/* Verify and set frame length */
/* remove padding we might have added before */
frame_len = pkt_len - padsize + FCS_LEN;
if (frame_len & ~AR5K_2W_TX_DESC_CTL0_FRAME_LEN)
return -EINVAL;
tx_ctl->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_ctl->tx_control_1 = pkt_len & AR5K_2W_TX_DESC_CTL1_BUF_LEN;
/*
* Verify and set header length (only 5210)
*/
if (ah->ah_version == AR5K_AR5210) {
if (hdr_len & ~AR5K_2W_TX_DESC_CTL0_HEADER_LEN_5210)
return -EINVAL;
tx_ctl->tx_control_0 |=
AR5K_REG_SM(hdr_len, AR5K_2W_TX_DESC_CTL0_HEADER_LEN_5210);
}
/*Differences 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;
break;
case AR5K_PKT_TYPE_PIFS:
frame_type = AR5K_AR5210_TX_DESC_FRAME_TYPE_PIFS;
break;
default:
frame_type = type;
break;
}
tx_ctl->tx_control_0 |=
AR5K_REG_SM(frame_type, AR5K_2W_TX_DESC_CTL0_FRAME_TYPE_5210) |
AR5K_REG_SM(tx_rate0, AR5K_2W_TX_DESC_CTL0_XMIT_RATE);
} else {
tx_ctl->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_ctl->tx_control_1 |=
AR5K_REG_SM(type, AR5K_2W_TX_DESC_CTL1_FRAME_TYPE_5211);
}
#define _TX_FLAGS(_c, _flag) \
if (flags & AR5K_TXDESC_##_flag) { \
tx_ctl->tx_control_##_c |= \
AR5K_2W_TX_DESC_CTL##_c##_##_flag; \
}
#define _TX_FLAGS_5211(_c, _flag) \
if (flags & AR5K_TXDESC_##_flag) { \
tx_ctl->tx_control_##_c |= \
AR5K_2W_TX_DESC_CTL##_c##_##_flag##_5211; \
}
_TX_FLAGS(0, CLRDMASK);
_TX_FLAGS(0, INTREQ);
_TX_FLAGS(0, RTSENA);
if (ah->ah_version == AR5K_AR5211) {
_TX_FLAGS_5211(0, VEOL);
_TX_FLAGS_5211(1, NOACK);
}
#undef _TX_FLAGS
#undef _TX_FLAGS_5211
/*
* WEP crap
*/
if (key_index != AR5K_TXKEYIX_INVALID) {
tx_ctl->tx_control_0 |=
AR5K_2W_TX_DESC_CTL0_ENCRYPT_KEY_VALID;
tx_ctl->tx_control_1 |=
AR5K_REG_SM(key_index,
AR5K_2W_TX_DESC_CTL1_ENC_KEY_IDX);
}
/*
* RTS/CTS Duration [5210 ?]
*/
if ((ah->ah_version == AR5K_AR5210) &&
(flags & (AR5K_TXDESC_RTSENA | AR5K_TXDESC_CTSENA)))
tx_ctl->tx_control_1 |= rtscts_duration &
AR5K_2W_TX_DESC_CTL1_RTS_DURATION_5210;
return 0;
}
/**
* ath5k_hw_setup_4word_tx_desc() - Initialize a 4-word tx control descriptor
* @ah: The &struct ath5k_hw
* @desc: The &struct ath5k_desc
* @pkt_len: Frame length in bytes
* @hdr_len: Header length in bytes (only used on AR5210)
* @padsize: Any padding we've added to the frame length
* @type: One of enum ath5k_pkt_type
* @tx_power: Tx power in 0.5dB steps
* @tx_rate0: HW idx for transmission rate
* @tx_tries0: Max number of retransmissions
* @key_index: Index on key table to use for encryption
* @antenna_mode: Which antenna to use (0 for auto)
* @flags: One of AR5K_TXDESC_* flags (desc.h)
* @rtscts_rate: HW idx for RTS/CTS transmission rate
* @rtscts_duration: What to put on duration field on the header of RTS/CTS
*
* Internal function to initialize a 4-Word TX control descriptor
* found on AR5212 and later MACs chips.
*
* Returns 0 on success or -EINVAL on false input
*/
static int
ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah,
struct ath5k_desc *desc,
unsigned int pkt_len, unsigned int hdr_len,
int padsize,
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_ctl *tx_ctl;
unsigned int frame_len;
/*
* Use local variables for these to reduce load/store access on
* uncached memory
*/
u32 txctl0 = 0, txctl1 = 0, txctl2 = 0, txctl3 = 0;
tx_ctl = &desc->ud.ds_tx5212.tx_ctl;
/*
* Validate input
* - Zero retries don't make sense.
* - A zero rate will put the HW into a mode where it continuously sends
* noise on the channel, so it is important to avoid this.
*/
if (unlikely(tx_tries0 == 0)) {
ATH5K_ERR(ah, "zero retries\n");
WARN_ON(1);
return -EINVAL;
}
if (unlikely(tx_rate0 == 0)) {
ATH5K_ERR(ah, "zero rate\n");
WARN_ON(1);
return -EINVAL;
}
tx_power += ah->ah_txpower.txp_offset;
if (tx_power > AR5K_TUNE_MAX_TXPOWER)
tx_power = AR5K_TUNE_MAX_TXPOWER;
/* Clear descriptor status area */
memset(&desc->ud.ds_tx5212.tx_stat, 0,
sizeof(desc->ud.ds_tx5212.tx_stat));
/* Setup control descriptor */
/* Verify and set frame length */
/* remove padding we might have added before */
frame_len = pkt_len - padsize + FCS_LEN;
if (frame_len & ~AR5K_4W_TX_DESC_CTL0_FRAME_LEN)
return -EINVAL;
txctl0 = 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;
txctl1 = pkt_len & AR5K_4W_TX_DESC_CTL1_BUF_LEN;
txctl0 |= 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);
txctl1 |= AR5K_REG_SM(type, AR5K_4W_TX_DESC_CTL1_FRAME_TYPE);
txctl2 = AR5K_REG_SM(tx_tries0, AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0);
txctl3 = tx_rate0 & AR5K_4W_TX_DESC_CTL3_XMIT_RATE0;
#define _TX_FLAGS(_c, _flag) \
if (flags & AR5K_TXDESC_##_flag) { \
txctl##_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) {
txctl0 |= AR5K_4W_TX_DESC_CTL0_ENCRYPT_KEY_VALID;
txctl1 |= AR5K_REG_SM(key_index,
AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_IDX);
}
/*
* RTS/CTS
*/
if (flags & (AR5K_TXDESC_RTSENA | AR5K_TXDESC_CTSENA)) {
if ((flags & AR5K_TXDESC_RTSENA) &&
(flags & AR5K_TXDESC_CTSENA))
return -EINVAL;
txctl2 |= rtscts_duration & AR5K_4W_TX_DESC_CTL2_RTS_DURATION;
txctl3 |= AR5K_REG_SM(rtscts_rate,
AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE);
}
tx_ctl->tx_control_0 = txctl0;
tx_ctl->tx_control_1 = txctl1;
tx_ctl->tx_control_2 = txctl2;
tx_ctl->tx_control_3 = txctl3;
return 0;
}
/**
* ath5k_hw_setup_mrr_tx_desc() - Initialize an MRR tx control descriptor
* @ah: The &struct ath5k_hw
* @desc: The &struct ath5k_desc
* @tx_rate1: HW idx for rate used on transmission series 1
* @tx_tries1: Max number of retransmissions for transmission series 1
* @tx_rate2: HW idx for rate used on transmission series 2
* @tx_tries2: Max number of retransmissions for transmission series 2
* @tx_rate3: HW idx for rate used on transmission series 3
* @tx_tries3: Max number of retransmissions for transmission series 3
*
* Multi rate retry (MRR) tx control descriptors are available only on AR5212
* MACs, they are part of the normal 4-word tx control descriptor (see above)
* but we handle them through a separate function for better abstraction.
*
* Returns 0 on success or -EINVAL on invalid input
*/
int
ath5k_hw_setup_mrr_tx_desc(struct ath5k_hw *ah,
struct ath5k_desc *desc,
u_int tx_rate1, u_int tx_tries1,
u_int tx_rate2, u_int tx_tries2,
u_int tx_rate3, u_int tx_tries3)
{
struct ath5k_hw_4w_tx_ctl *tx_ctl;
/* no mrr support for cards older than 5212 */
if (ah->ah_version < AR5K_AR5212)
return 0;
/*
* 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 continuously 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, "zero rate\n");
WARN_ON(1);
return -EINVAL;
}
if (ah->ah_version == AR5K_AR5212) {
tx_ctl = &desc->ud.ds_tx5212.tx_ctl;
#define _XTX_TRIES(_n) \
if (tx_tries##_n) { \
tx_ctl->tx_control_2 |= \
AR5K_REG_SM(tx_tries##_n, \
AR5K_4W_TX_DESC_CTL2_XMIT_TRIES##_n); \
tx_ctl->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 1;
}
return 0;
}
/***********************\
* TX Status descriptors *
\***********************/
/**
* ath5k_hw_proc_2word_tx_status() - Process a tx status descriptor on 5210/1
* @ah: The &struct ath5k_hw
* @desc: The &struct ath5k_desc
* @ts: The &struct ath5k_tx_status
*/
static int
ath5k_hw_proc_2word_tx_status(struct ath5k_hw *ah,
struct ath5k_desc *desc,
struct ath5k_tx_status *ts)
{
struct ath5k_hw_tx_status *tx_status;
tx_status = &desc->ud.ds_tx5210.tx_stat;
/* 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
*/
ts->ts_tstamp = AR5K_REG_MS(tx_status->tx_status_0,
AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP);
ts->ts_shortretry = AR5K_REG_MS(tx_status->tx_status_0,
AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT);
ts->ts_final_retry = AR5K_REG_MS(tx_status->tx_status_0,
AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT);
/*TODO: ts->ts_virtcol + test*/
ts->ts_seqnum = AR5K_REG_MS(tx_status->tx_status_1,
AR5K_DESC_TX_STATUS1_SEQ_NUM);
ts->ts_rssi = AR5K_REG_MS(tx_status->tx_status_1,
AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH);
ts->ts_antenna = 1;
ts->ts_status = 0;
ts->ts_final_idx = 0;
if (!(tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK)) {
if (tx_status->tx_status_0 &
AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES)
ts->ts_status |= AR5K_TXERR_XRETRY;
if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN)
ts->ts_status |= AR5K_TXERR_FIFO;
if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FILTERED)
ts->ts_status |= AR5K_TXERR_FILT;
}
return 0;
}
/**
* ath5k_hw_proc_4word_tx_status() - Process a tx status descriptor on 5212
* @ah: The &struct ath5k_hw
* @desc: The &struct ath5k_desc
* @ts: The &struct ath5k_tx_status
*/
static int
ath5k_hw_proc_4word_tx_status(struct ath5k_hw *ah,
struct ath5k_desc *desc,
struct ath5k_tx_status *ts)
{
struct ath5k_hw_tx_status *tx_status;
u32 txstat0, txstat1;
tx_status = &desc->ud.ds_tx5212.tx_stat;
txstat1 = READ_ONCE(tx_status->tx_status_1);
/* No frame has been send or error */
if (unlikely(!(txstat1 & AR5K_DESC_TX_STATUS1_DONE)))
return -EINPROGRESS;
txstat0 = READ_ONCE(tx_status->tx_status_0);
/*
* Get descriptor status
*/
ts->ts_tstamp = AR5K_REG_MS(txstat0,
AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP);
ts->ts_shortretry = AR5K_REG_MS(txstat0,
AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT);
ts->ts_final_retry = AR5K_REG_MS(txstat0,
AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT);
ts->ts_seqnum = AR5K_REG_MS(txstat1,
AR5K_DESC_TX_STATUS1_SEQ_NUM);
ts->ts_rssi = AR5K_REG_MS(txstat1,
AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH);
ts->ts_antenna = (txstat1 &
AR5K_DESC_TX_STATUS1_XMIT_ANTENNA_5212) ? 2 : 1;
ts->ts_status = 0;
ts->ts_final_idx = AR5K_REG_MS(txstat1,
AR5K_DESC_TX_STATUS1_FINAL_TS_IX_5212);
/* TX error */
if (!(txstat0 & AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK)) {
if (txstat0 & AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES)
ts->ts_status |= AR5K_TXERR_XRETRY;
if (txstat0 & AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN)
ts->ts_status |= AR5K_TXERR_FIFO;
if (txstat0 & AR5K_DESC_TX_STATUS0_FILTERED)
ts->ts_status |= AR5K_TXERR_FILT;
}
return 0;
}
/****************\
* RX Descriptors *
\****************/
/**
* ath5k_hw_setup_rx_desc() - Initialize an rx control descriptor
* @ah: The &struct ath5k_hw
* @desc: The &struct ath5k_desc
* @size: RX buffer length in bytes
* @flags: One of AR5K_RXDESC_* flags
*/
int
ath5k_hw_setup_rx_desc(struct ath5k_hw *ah,
struct ath5k_desc *desc,
u32 size, unsigned int flags)
{
struct ath5k_hw_rx_ctl *rx_ctl;
rx_ctl = &desc->ud.ds_rx.rx_ctl;
/*
* Clear the descriptor
* 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->ud.ds_rx, 0, sizeof(struct ath5k_hw_all_rx_desc));
if (unlikely(size & ~AR5K_DESC_RX_CTL1_BUF_LEN))
return -EINVAL;
/* Setup descriptor */
rx_ctl->rx_control_1 = size & AR5K_DESC_RX_CTL1_BUF_LEN;
if (flags & AR5K_RXDESC_INTREQ)
rx_ctl->rx_control_1 |= AR5K_DESC_RX_CTL1_INTREQ;
return 0;
}
/**
* ath5k_hw_proc_5210_rx_status() - Process the rx status descriptor on 5210/1
* @ah: The &struct ath5k_hw
* @desc: The &struct ath5k_desc
* @rs: The &struct ath5k_rx_status
*
* Internal function used to process an RX status descriptor
* on AR5210/5211 MAC.
*
* Returns 0 on success or -EINPROGRESS in case we haven't received the who;e
* frame yet.
*/
static int
ath5k_hw_proc_5210_rx_status(struct ath5k_hw *ah,
struct ath5k_desc *desc,
struct ath5k_rx_status *rs)
{
struct ath5k_hw_rx_status *rx_status;
rx_status = &desc->ud.ds_rx.rx_stat;
/* No frame received / not ready */
if (unlikely(!(rx_status->rx_status_1 &
AR5K_5210_RX_DESC_STATUS1_DONE)))
return -EINPROGRESS;
memset(rs, 0, sizeof(struct ath5k_rx_status));
/*
* Frame receive status
*/
rs->rs_datalen = rx_status->rx_status_0 &
AR5K_5210_RX_DESC_STATUS0_DATA_LEN;
rs->rs_rssi = AR5K_REG_MS(rx_status->rx_status_0,
AR5K_5210_RX_DESC_STATUS0_RECEIVE_SIGNAL);
rs->rs_rate = AR5K_REG_MS(rx_status->rx_status_0,
AR5K_5210_RX_DESC_STATUS0_RECEIVE_RATE);
rs->rs_more = !!(rx_status->rx_status_0 &
AR5K_5210_RX_DESC_STATUS0_MORE);
/* TODO: this timestamp is 13 bit, later on we assume 15 bit!
* also the HAL code for 5210 says the timestamp is bits [10..22] of the
* TSF, and extends the timestamp here to 15 bit.
* we need to check on 5210...
*/
rs->rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1,
AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP);
if (ah->ah_version == AR5K_AR5211)
rs->rs_antenna = AR5K_REG_MS(rx_status->rx_status_0,
AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANT_5211);
else
rs->rs_antenna = (rx_status->rx_status_0 &
AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANT_5210)
? 2 : 1;
/*
* Key table status
*/
if (rx_status->rx_status_1 & AR5K_5210_RX_DESC_STATUS1_KEY_INDEX_VALID)
rs->rs_keyix = AR5K_REG_MS(rx_status->rx_status_1,
AR5K_5210_RX_DESC_STATUS1_KEY_INDEX);
else
rs->rs_keyix = AR5K_RXKEYIX_INVALID;
/*
* Receive/descriptor errors
*/
if (!(rx_status->rx_status_1 &
AR5K_5210_RX_DESC_STATUS1_FRAME_RECEIVE_OK)) {
if (rx_status->rx_status_1 &
AR5K_5210_RX_DESC_STATUS1_CRC_ERROR)
rs->rs_status |= AR5K_RXERR_CRC;
/* only on 5210 */
if ((ah->ah_version == AR5K_AR5210) &&
(rx_status->rx_status_1 &
AR5K_5210_RX_DESC_STATUS1_FIFO_OVERRUN_5210))
rs->rs_status |= AR5K_RXERR_FIFO;
if (rx_status->rx_status_1 &
AR5K_5210_RX_DESC_STATUS1_PHY_ERROR) {
rs->rs_status |= AR5K_RXERR_PHY;
rs->rs_phyerr = AR5K_REG_MS(rx_status->rx_status_1,
AR5K_5210_RX_DESC_STATUS1_PHY_ERROR);
}
if (rx_status->rx_status_1 &
AR5K_5210_RX_DESC_STATUS1_DECRYPT_CRC_ERROR)
rs->rs_status |= AR5K_RXERR_DECRYPT;
}
return 0;
}
/**
* ath5k_hw_proc_5212_rx_status() - Process the rx status descriptor on 5212
* @ah: The &struct ath5k_hw
* @desc: The &struct ath5k_desc
* @rs: The &struct ath5k_rx_status
*
* Internal function used to process an RX status descriptor
* on AR5212 and later MAC.
*
* Returns 0 on success or -EINPROGRESS in case we haven't received the who;e
* frame yet.
*/
static int
ath5k_hw_proc_5212_rx_status(struct ath5k_hw *ah,
struct ath5k_desc *desc,
struct ath5k_rx_status *rs)
{
struct ath5k_hw_rx_status *rx_status;
u32 rxstat0, rxstat1;
rx_status = &desc->ud.ds_rx.rx_stat;
rxstat1 = READ_ONCE(rx_status->rx_status_1);
/* No frame received / not ready */
if (unlikely(!(rxstat1 & AR5K_5212_RX_DESC_STATUS1_DONE)))
return -EINPROGRESS;
memset(rs, 0, sizeof(struct ath5k_rx_status));
rxstat0 = READ_ONCE(rx_status->rx_status_0);
/*
* Frame receive status
*/
rs->rs_datalen = rxstat0 & AR5K_5212_RX_DESC_STATUS0_DATA_LEN;
rs->rs_rssi = AR5K_REG_MS(rxstat0,
AR5K_5212_RX_DESC_STATUS0_RECEIVE_SIGNAL);
rs->rs_rate = AR5K_REG_MS(rxstat0,
AR5K_5212_RX_DESC_STATUS0_RECEIVE_RATE);
rs->rs_antenna = AR5K_REG_MS(rxstat0,
AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA);
rs->rs_more = !!(rxstat0 & AR5K_5212_RX_DESC_STATUS0_MORE);
rs->rs_tstamp = AR5K_REG_MS(rxstat1,
AR5K_5212_RX_DESC_STATUS1_RECEIVE_TIMESTAMP);
/*
* Key table status
*/
if (rxstat1 & AR5K_5212_RX_DESC_STATUS1_KEY_INDEX_VALID)
rs->rs_keyix = AR5K_REG_MS(rxstat1,
AR5K_5212_RX_DESC_STATUS1_KEY_INDEX);
else
rs->rs_keyix = AR5K_RXKEYIX_INVALID;
/*
* Receive/descriptor errors
*/
if (!(rxstat1 & AR5K_5212_RX_DESC_STATUS1_FRAME_RECEIVE_OK)) {
if (rxstat1 & AR5K_5212_RX_DESC_STATUS1_CRC_ERROR)
rs->rs_status |= AR5K_RXERR_CRC;
if (rxstat1 & AR5K_5212_RX_DESC_STATUS1_PHY_ERROR) {
rs->rs_status |= AR5K_RXERR_PHY;
rs->rs_phyerr = AR5K_REG_MS(rxstat1,
AR5K_5212_RX_DESC_STATUS1_PHY_ERROR_CODE);
if (!ah->ah_capabilities.cap_has_phyerr_counters)
ath5k_ani_phy_error_report(ah, rs->rs_phyerr);
}
if (rxstat1 & AR5K_5212_RX_DESC_STATUS1_DECRYPT_CRC_ERROR)
rs->rs_status |= AR5K_RXERR_DECRYPT;
if (rxstat1 & AR5K_5212_RX_DESC_STATUS1_MIC_ERROR)
rs->rs_status |= AR5K_RXERR_MIC;
}
return 0;
}
/********\
* Attach *
\********/
/**
* ath5k_hw_init_desc_functions() - Init function pointers inside ah
* @ah: The &struct ath5k_hw
*
* Maps the internal descriptor functions to the function pointers on ah, used
* from above. This is used as an abstraction layer to handle the various chips
* the same way.
*/
int
ath5k_hw_init_desc_functions(struct ath5k_hw *ah)
{
if (ah->ah_version == AR5K_AR5212) {
ah->ah_setup_tx_desc = ath5k_hw_setup_4word_tx_desc;
ah->ah_proc_tx_desc = ath5k_hw_proc_4word_tx_status;
ah->ah_proc_rx_desc = ath5k_hw_proc_5212_rx_status;
} else if (ah->ah_version <= AR5K_AR5211) {
ah->ah_setup_tx_desc = ath5k_hw_setup_2word_tx_desc;
ah->ah_proc_tx_desc = ath5k_hw_proc_2word_tx_status;
ah->ah_proc_rx_desc = ath5k_hw_proc_5210_rx_status;
} else
return -ENOTSUPP;
return 0;
}
@@ -0,0 +1,367 @@
/*
* Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@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.
*
*/
/*
* RX/TX descriptor structures
*/
/**
* struct ath5k_hw_rx_ctl - Common hardware RX control descriptor
* @rx_control_0: RX control word 0
* @rx_control_1: RX control word 1
*/
struct ath5k_hw_rx_ctl {
u32 rx_control_0;
u32 rx_control_1;
} __packed __aligned(4);
/* RX control word 1 fields/flags */
#define AR5K_DESC_RX_CTL1_BUF_LEN 0x00000fff /* data buffer length */
#define AR5K_DESC_RX_CTL1_INTREQ 0x00002000 /* RX interrupt request */
/**
* struct ath5k_hw_rx_status - Common hardware RX status descriptor
* @rx_status_0: RX status word 0
* @rx_status_1: RX status word 1
*
* 5210, 5211 and 5212 differ only in the fields and flags defined below
*/
struct ath5k_hw_rx_status {
u32 rx_status_0;
u32 rx_status_1;
} __packed __aligned(4);
/* 5210/5211 */
/* RX status word 0 fields/flags */
#define AR5K_5210_RX_DESC_STATUS0_DATA_LEN 0x00000fff /* RX data length */
#define AR5K_5210_RX_DESC_STATUS0_MORE 0x00001000 /* more desc for this frame */
#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANT_5210 0x00004000 /* [5210] receive on ant 1 */
#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_RATE 0x00078000 /* reception rate */
#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_RATE_S 15
#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_SIGNAL 0x07f80000 /* rssi */
#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_SIGNAL_S 19
#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANT_5211 0x38000000 /* [5211] receive antenna */
#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANT_5211_S 27
/* RX status word 1 fields/flags */
#define AR5K_5210_RX_DESC_STATUS1_DONE 0x00000001 /* descriptor complete */
#define AR5K_5210_RX_DESC_STATUS1_FRAME_RECEIVE_OK 0x00000002 /* reception success */
#define AR5K_5210_RX_DESC_STATUS1_CRC_ERROR 0x00000004 /* CRC error */
#define AR5K_5210_RX_DESC_STATUS1_FIFO_OVERRUN_5210 0x00000008 /* [5210] FIFO overrun */
#define AR5K_5210_RX_DESC_STATUS1_DECRYPT_CRC_ERROR 0x00000010 /* decryption CRC failure */
#define AR5K_5210_RX_DESC_STATUS1_PHY_ERROR 0x000000e0 /* PHY error */
#define AR5K_5210_RX_DESC_STATUS1_PHY_ERROR_S 5
#define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX_VALID 0x00000100 /* key index valid */
#define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX 0x00007e00 /* decryption key index */
#define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX_S 9
#define AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP 0x0fff8000 /* 13 bit of TSF */
#define AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S 15
#define AR5K_5210_RX_DESC_STATUS1_KEY_CACHE_MISS 0x10000000 /* key cache miss */
/* 5212 */
/* RX status word 0 fields/flags */
#define AR5K_5212_RX_DESC_STATUS0_DATA_LEN 0x00000fff /* RX data length */
#define AR5K_5212_RX_DESC_STATUS0_MORE 0x00001000 /* more desc for this frame */
#define AR5K_5212_RX_DESC_STATUS0_DECOMP_CRC_ERROR 0x00002000 /* decompression CRC error */
#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_RATE 0x000f8000 /* reception rate */
#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_RATE_S 15
#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_SIGNAL 0x0ff00000 /* rssi */
#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_SIGNAL_S 20
#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA 0xf0000000 /* receive antenna */
#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA_S 28
/* RX status word 1 fields/flags */
#define AR5K_5212_RX_DESC_STATUS1_DONE 0x00000001 /* descriptor complete */
#define AR5K_5212_RX_DESC_STATUS1_FRAME_RECEIVE_OK 0x00000002 /* frame reception success */
#define AR5K_5212_RX_DESC_STATUS1_CRC_ERROR 0x00000004 /* CRC error */
#define AR5K_5212_RX_DESC_STATUS1_DECRYPT_CRC_ERROR 0x00000008 /* decryption CRC failure */
#define AR5K_5212_RX_DESC_STATUS1_PHY_ERROR 0x00000010 /* PHY error */
#define AR5K_5212_RX_DESC_STATUS1_MIC_ERROR 0x00000020 /* MIC decrypt error */
#define AR5K_5212_RX_DESC_STATUS1_KEY_INDEX_VALID 0x00000100 /* key index valid */
#define AR5K_5212_RX_DESC_STATUS1_KEY_INDEX 0x0000fe00 /* decryption key index */
#define AR5K_5212_RX_DESC_STATUS1_KEY_INDEX_S 9
#define AR5K_5212_RX_DESC_STATUS1_RECEIVE_TIMESTAMP 0x7fff0000 /* first 15bit of the TSF */
#define AR5K_5212_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S 16
#define AR5K_5212_RX_DESC_STATUS1_KEY_CACHE_MISS 0x80000000 /* key cache miss */
#define AR5K_5212_RX_DESC_STATUS1_PHY_ERROR_CODE 0x0000ff00 /* phy error code overlays key index and valid fields */
#define AR5K_5212_RX_DESC_STATUS1_PHY_ERROR_CODE_S 8
/**
* enum ath5k_phy_error_code - PHY Error codes
* @AR5K_RX_PHY_ERROR_UNDERRUN: Transmit underrun, [5210] No error
* @AR5K_RX_PHY_ERROR_TIMING: Timing error
* @AR5K_RX_PHY_ERROR_PARITY: Illegal parity
* @AR5K_RX_PHY_ERROR_RATE: Illegal rate
* @AR5K_RX_PHY_ERROR_LENGTH: Illegal length
* @AR5K_RX_PHY_ERROR_RADAR: Radar detect, [5210] 64 QAM rate
* @AR5K_RX_PHY_ERROR_SERVICE: Illegal service
* @AR5K_RX_PHY_ERROR_TOR: Transmit override receive
* @AR5K_RX_PHY_ERROR_OFDM_TIMING: OFDM Timing error [5212+]
* @AR5K_RX_PHY_ERROR_OFDM_SIGNAL_PARITY: OFDM Signal parity error [5212+]
* @AR5K_RX_PHY_ERROR_OFDM_RATE_ILLEGAL: OFDM Illegal rate [5212+]
* @AR5K_RX_PHY_ERROR_OFDM_LENGTH_ILLEGAL: OFDM Illegal length [5212+]
* @AR5K_RX_PHY_ERROR_OFDM_POWER_DROP: OFDM Power drop [5212+]
* @AR5K_RX_PHY_ERROR_OFDM_SERVICE: OFDM Service (?) [5212+]
* @AR5K_RX_PHY_ERROR_OFDM_RESTART: OFDM Restart (?) [5212+]
* @AR5K_RX_PHY_ERROR_CCK_TIMING: CCK Timing error [5212+]
* @AR5K_RX_PHY_ERROR_CCK_HEADER_CRC: Header CRC error [5212+]
* @AR5K_RX_PHY_ERROR_CCK_RATE_ILLEGAL: Illegal rate [5212+]
* @AR5K_RX_PHY_ERROR_CCK_SERVICE: CCK Service (?) [5212+]
* @AR5K_RX_PHY_ERROR_CCK_RESTART: CCK Restart (?) [5212+]
*/
enum ath5k_phy_error_code {
AR5K_RX_PHY_ERROR_UNDERRUN = 0,
AR5K_RX_PHY_ERROR_TIMING = 1,
AR5K_RX_PHY_ERROR_PARITY = 2,
AR5K_RX_PHY_ERROR_RATE = 3,
AR5K_RX_PHY_ERROR_LENGTH = 4,
AR5K_RX_PHY_ERROR_RADAR = 5,
AR5K_RX_PHY_ERROR_SERVICE = 6,
AR5K_RX_PHY_ERROR_TOR = 7,
AR5K_RX_PHY_ERROR_OFDM_TIMING = 17,
AR5K_RX_PHY_ERROR_OFDM_SIGNAL_PARITY = 18,
AR5K_RX_PHY_ERROR_OFDM_RATE_ILLEGAL = 19,
AR5K_RX_PHY_ERROR_OFDM_LENGTH_ILLEGAL = 20,
AR5K_RX_PHY_ERROR_OFDM_POWER_DROP = 21,
AR5K_RX_PHY_ERROR_OFDM_SERVICE = 22,
AR5K_RX_PHY_ERROR_OFDM_RESTART = 23,
AR5K_RX_PHY_ERROR_CCK_TIMING = 25,
AR5K_RX_PHY_ERROR_CCK_HEADER_CRC = 26,
AR5K_RX_PHY_ERROR_CCK_RATE_ILLEGAL = 27,
AR5K_RX_PHY_ERROR_CCK_SERVICE = 30,
AR5K_RX_PHY_ERROR_CCK_RESTART = 31,
};
/**
* struct ath5k_hw_2w_tx_ctl - 5210/5211 hardware 2-word TX control descriptor
* @tx_control_0: TX control word 0
* @tx_control_1: TX control word 1
*/
struct ath5k_hw_2w_tx_ctl {
u32 tx_control_0;
u32 tx_control_1;
} __packed __aligned(4);
/* TX control word 0 fields/flags */
#define AR5K_2W_TX_DESC_CTL0_FRAME_LEN 0x00000fff /* frame length */
#define AR5K_2W_TX_DESC_CTL0_HEADER_LEN_5210 0x0003f000 /* [5210] header length */
#define AR5K_2W_TX_DESC_CTL0_HEADER_LEN_5210_S 12
#define AR5K_2W_TX_DESC_CTL0_XMIT_RATE 0x003c0000 /* tx rate */
#define AR5K_2W_TX_DESC_CTL0_XMIT_RATE_S 18
#define AR5K_2W_TX_DESC_CTL0_RTSENA 0x00400000 /* RTS/CTS enable */
#define AR5K_2W_TX_DESC_CTL0_LONG_PACKET_5210 0x00800000 /* [5210] long packet */
#define AR5K_2W_TX_DESC_CTL0_VEOL_5211 0x00800000 /* [5211] virtual end-of-list */
#define AR5K_2W_TX_DESC_CTL0_CLRDMASK 0x01000000 /* clear destination mask */
#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5210 0x02000000 /* [5210] antenna selection */
#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5211 0x1e000000 /* [5211] antenna selection */
#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT \
(ah->ah_version == AR5K_AR5210 ? \
AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5210 : \
AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5211)
#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_S 25
#define AR5K_2W_TX_DESC_CTL0_FRAME_TYPE_5210 0x1c000000 /* [5210] frame type */
#define AR5K_2W_TX_DESC_CTL0_FRAME_TYPE_5210_S 26
#define AR5K_2W_TX_DESC_CTL0_INTREQ 0x20000000 /* TX interrupt request */
#define AR5K_2W_TX_DESC_CTL0_ENCRYPT_KEY_VALID 0x40000000 /* key is valid */
/* TX control word 1 fields/flags */
#define AR5K_2W_TX_DESC_CTL1_BUF_LEN 0x00000fff /* data buffer length */
#define AR5K_2W_TX_DESC_CTL1_MORE 0x00001000 /* more desc for this frame */
#define AR5K_2W_TX_DESC_CTL1_ENC_KEY_IDX_5210 0x0007e000 /* [5210] key table index */
#define AR5K_2W_TX_DESC_CTL1_ENC_KEY_IDX_5211 0x000fe000 /* [5211] key table index */
#define AR5K_2W_TX_DESC_CTL1_ENC_KEY_IDX \
(ah->ah_version == AR5K_AR5210 ? \
AR5K_2W_TX_DESC_CTL1_ENC_KEY_IDX_5210 : \
AR5K_2W_TX_DESC_CTL1_ENC_KEY_IDX_5211)
#define AR5K_2W_TX_DESC_CTL1_ENC_KEY_IDX_S 13
#define AR5K_2W_TX_DESC_CTL1_FRAME_TYPE_5211 0x00700000 /* [5211] frame type */
#define AR5K_2W_TX_DESC_CTL1_FRAME_TYPE_5211_S 20
#define AR5K_2W_TX_DESC_CTL1_NOACK_5211 0x00800000 /* [5211] no ACK */
#define AR5K_2W_TX_DESC_CTL1_RTS_DURATION_5210 0xfff80000 /* [5210] lower 13 bit of duration */
/* Frame types */
#define AR5K_AR5210_TX_DESC_FRAME_TYPE_NORMAL 0
#define AR5K_AR5210_TX_DESC_FRAME_TYPE_ATIM 1
#define AR5K_AR5210_TX_DESC_FRAME_TYPE_PSPOLL 2
#define AR5K_AR5210_TX_DESC_FRAME_TYPE_NO_DELAY 3
#define AR5K_AR5211_TX_DESC_FRAME_TYPE_BEACON 3
#define AR5K_AR5210_TX_DESC_FRAME_TYPE_PIFS 4
#define AR5K_AR5211_TX_DESC_FRAME_TYPE_PRESP 4
/**
* struct ath5k_hw_4w_tx_ctl - 5212 hardware 4-word TX control descriptor
* @tx_control_0: TX control word 0
* @tx_control_1: TX control word 1
* @tx_control_2: TX control word 2
* @tx_control_3: TX control word 3
*/
struct ath5k_hw_4w_tx_ctl {
u32 tx_control_0;
u32 tx_control_1;
u32 tx_control_2;
u32 tx_control_3;
} __packed __aligned(4);
/* TX control word 0 fields/flags */
#define AR5K_4W_TX_DESC_CTL0_FRAME_LEN 0x00000fff /* frame length */
#define AR5K_4W_TX_DESC_CTL0_XMIT_POWER 0x003f0000 /* transmit power */
#define AR5K_4W_TX_DESC_CTL0_XMIT_POWER_S 16
#define AR5K_4W_TX_DESC_CTL0_RTSENA 0x00400000 /* RTS/CTS enable */
#define AR5K_4W_TX_DESC_CTL0_VEOL 0x00800000 /* virtual end-of-list */
#define AR5K_4W_TX_DESC_CTL0_CLRDMASK 0x01000000 /* clear destination mask */
#define AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT 0x1e000000 /* TX antenna selection */
#define AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT_S 25
#define AR5K_4W_TX_DESC_CTL0_INTREQ 0x20000000 /* TX interrupt request */
#define AR5K_4W_TX_DESC_CTL0_ENCRYPT_KEY_VALID 0x40000000 /* destination index valid */
#define AR5K_4W_TX_DESC_CTL0_CTSENA 0x80000000 /* precede frame with CTS */
/* TX control word 1 fields/flags */
#define AR5K_4W_TX_DESC_CTL1_BUF_LEN 0x00000fff /* data buffer length */
#define AR5K_4W_TX_DESC_CTL1_MORE 0x00001000 /* more desc for this frame */
#define AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_IDX 0x000fe000 /* destination table index */
#define AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_IDX_S 13
#define AR5K_4W_TX_DESC_CTL1_FRAME_TYPE 0x00f00000 /* frame type */
#define AR5K_4W_TX_DESC_CTL1_FRAME_TYPE_S 20
#define AR5K_4W_TX_DESC_CTL1_NOACK 0x01000000 /* no ACK */
#define AR5K_4W_TX_DESC_CTL1_COMP_PROC 0x06000000 /* compression processing */
#define AR5K_4W_TX_DESC_CTL1_COMP_PROC_S 25
#define AR5K_4W_TX_DESC_CTL1_COMP_IV_LEN 0x18000000 /* length of frame IV */
#define AR5K_4W_TX_DESC_CTL1_COMP_IV_LEN_S 27
#define AR5K_4W_TX_DESC_CTL1_COMP_ICV_LEN 0x60000000 /* length of frame ICV */
#define AR5K_4W_TX_DESC_CTL1_COMP_ICV_LEN_S 29
/* TX control word 2 fields/flags */
#define AR5K_4W_TX_DESC_CTL2_RTS_DURATION 0x00007fff /* RTS/CTS duration */
#define AR5K_4W_TX_DESC_CTL2_DURATION_UPD_EN 0x00008000 /* frame duration update */
#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0 0x000f0000 /* series 0 max attempts */
#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0_S 16
#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1 0x00f00000 /* series 1 max attempts */
#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1_S 20
#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2 0x0f000000 /* series 2 max attempts */
#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2_S 24
#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES3 0xf0000000 /* series 3 max attempts */
#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES3_S 28
/* TX control word 3 fields/flags */
#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE0 0x0000001f /* series 0 tx rate */
#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE1 0x000003e0 /* series 1 tx rate */
#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE1_S 5
#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE2 0x00007c00 /* series 2 tx rate */
#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE2_S 10
#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE3 0x000f8000 /* series 3 tx rate */
#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE3_S 15
#define AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE 0x01f00000 /* RTS or CTS rate */
#define AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE_S 20
/**
* struct ath5k_hw_tx_status - Common TX status descriptor
* @tx_status_0: TX status word 0
* @tx_status_1: TX status word 1
*/
struct ath5k_hw_tx_status {
u32 tx_status_0;
u32 tx_status_1;
} __packed __aligned(4);
/* TX status word 0 fields/flags */
#define AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK 0x00000001 /* TX success */
#define AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES 0x00000002 /* excessive retries */
#define AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN 0x00000004 /* FIFO underrun */
#define AR5K_DESC_TX_STATUS0_FILTERED 0x00000008 /* TX filter indication */
/* according to the HAL sources the spec has short/long retry counts reversed.
* we have it reversed to the HAL sources as well, for 5210 and 5211.
* For 5212 these fields are defined as RTS_FAIL_COUNT and DATA_FAIL_COUNT,
* but used respectively as SHORT and LONG retry count in the code later. This
* is consistent with the definitions here... TODO: check */
#define AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT 0x000000f0 /* short retry count */
#define AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT_S 4
#define AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT 0x00000f00 /* long retry count */
#define AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT_S 8
#define AR5K_DESC_TX_STATUS0_VIRTCOLL_CT_5211 0x0000f000 /* [5211+] virtual collision count */
#define AR5K_DESC_TX_STATUS0_VIRTCOLL_CT_5212_S 12
#define AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP 0xffff0000 /* TX timestamp */
#define AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP_S 16
/* TX status word 1 fields/flags */
#define AR5K_DESC_TX_STATUS1_DONE 0x00000001 /* descriptor complete */
#define AR5K_DESC_TX_STATUS1_SEQ_NUM 0x00001ffe /* TX sequence number */
#define AR5K_DESC_TX_STATUS1_SEQ_NUM_S 1
#define AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH 0x001fe000 /* signal strength of ACK */
#define AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH_S 13
#define AR5K_DESC_TX_STATUS1_FINAL_TS_IX_5212 0x00600000 /* [5212] final TX attempt series ix */
#define AR5K_DESC_TX_STATUS1_FINAL_TS_IX_5212_S 21
#define AR5K_DESC_TX_STATUS1_COMP_SUCCESS_5212 0x00800000 /* [5212] compression status */
#define AR5K_DESC_TX_STATUS1_XMIT_ANTENNA_5212 0x01000000 /* [5212] transmit antenna */
/**
* struct ath5k_hw_5210_tx_desc - 5210/5211 hardware TX descriptor
* @tx_ctl: The &struct ath5k_hw_2w_tx_ctl
* @tx_stat: The &struct ath5k_hw_tx_status
*/
struct ath5k_hw_5210_tx_desc {
struct ath5k_hw_2w_tx_ctl tx_ctl;
struct ath5k_hw_tx_status tx_stat;
} __packed __aligned(4);
/**
* struct ath5k_hw_5212_tx_desc - 5212 hardware TX descriptor
* @tx_ctl: The &struct ath5k_hw_4w_tx_ctl
* @tx_stat: The &struct ath5k_hw_tx_status
*/
struct ath5k_hw_5212_tx_desc {
struct ath5k_hw_4w_tx_ctl tx_ctl;
struct ath5k_hw_tx_status tx_stat;
} __packed __aligned(4);
/**
* struct ath5k_hw_all_rx_desc - Common hardware RX descriptor
* @rx_ctl: The &struct ath5k_hw_rx_ctl
* @rx_stat: The &struct ath5k_hw_rx_status
*/
struct ath5k_hw_all_rx_desc {
struct ath5k_hw_rx_ctl rx_ctl;
struct ath5k_hw_rx_status rx_stat;
} __packed __aligned(4);
/**
* struct ath5k_desc - Atheros hardware DMA descriptor
* @ds_link: Physical address of the next descriptor
* @ds_data: Physical address of data buffer (skb)
* @ud: Union containing hw_5xxx_tx_desc structs and hw_all_rx_desc
*
* This is read and written to by the hardware
*/
struct ath5k_desc {
u32 ds_link;
u32 ds_data;
union {
struct ath5k_hw_5210_tx_desc ds_tx5210;
struct ath5k_hw_5212_tx_desc ds_tx5212;
struct ath5k_hw_all_rx_desc ds_rx;
} ud;
} __packed __aligned(4);
#define AR5K_RXDESC_INTREQ 0x0020
#define AR5K_TXDESC_CLRDMASK 0x0001
#define AR5K_TXDESC_NOACK 0x0002 /*[5211+]*/
#define AR5K_TXDESC_RTSENA 0x0004
#define AR5K_TXDESC_CTSENA 0x0008
#define AR5K_TXDESC_INTREQ 0x0010
#define AR5K_TXDESC_VEOL 0x0020 /*[5211+]*/
@@ -0,0 +1,913 @@
/*
* Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@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.
*
*/
/*************************************\
* DMA and interrupt masking functions *
\*************************************/
/**
* DOC: DMA and interrupt masking functions
*
* Here we setup descriptor pointers (rxdp/txdp) start/stop dma engine and
* handle queue setup for 5210 chipset (rest are handled on qcu.c).
* Also we setup interrupt mask register (IMR) and read the various interrupt
* status registers (ISR).
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
/*********\
* Receive *
\*********/
/**
* ath5k_hw_start_rx_dma() - Start DMA receive
* @ah: The &struct ath5k_hw
*/
void
ath5k_hw_start_rx_dma(struct ath5k_hw *ah)
{
ath5k_hw_reg_write(ah, AR5K_CR_RXE, AR5K_CR);
ath5k_hw_reg_read(ah, AR5K_CR);
}
/**
* ath5k_hw_stop_rx_dma() - Stop DMA receive
* @ah: The &struct ath5k_hw
*/
static int
ath5k_hw_stop_rx_dma(struct ath5k_hw *ah)
{
unsigned int i;
ath5k_hw_reg_write(ah, AR5K_CR_RXD, AR5K_CR);
/*
* It may take some time to disable the DMA receive unit
*/
for (i = 1000; i > 0 &&
(ath5k_hw_reg_read(ah, AR5K_CR) & AR5K_CR_RXE) != 0;
i--)
udelay(100);
if (!i)
ATH5K_DBG(ah, ATH5K_DEBUG_DMA,
"failed to stop RX DMA !\n");
return i ? 0 : -EBUSY;
}
/**
* ath5k_hw_get_rxdp() - Get RX Descriptor's address
* @ah: The &struct ath5k_hw
*/
u32
ath5k_hw_get_rxdp(struct ath5k_hw *ah)
{
return ath5k_hw_reg_read(ah, AR5K_RXDP);
}
/**
* ath5k_hw_set_rxdp() - Set RX Descriptor's address
* @ah: The &struct ath5k_hw
* @phys_addr: RX descriptor address
*
* Returns -EIO if rx is active
*/
int
ath5k_hw_set_rxdp(struct ath5k_hw *ah, u32 phys_addr)
{
if (ath5k_hw_reg_read(ah, AR5K_CR) & AR5K_CR_RXE) {
ATH5K_DBG(ah, ATH5K_DEBUG_DMA,
"tried to set RXDP while rx was active !\n");
return -EIO;
}
ath5k_hw_reg_write(ah, phys_addr, AR5K_RXDP);
return 0;
}
/**********\
* Transmit *
\**********/
/**
* ath5k_hw_start_tx_dma() - Start DMA transmit for a specific queue
* @ah: The &struct ath5k_hw
* @queue: The hw queue number
*
* Start DMA transmit for a specific queue and since 5210 doesn't have
* QCU/DCU, set up queue parameters for 5210 here based on queue type (one
* queue for normal data and one queue for beacons). For queue setup
* on newer chips check out qcu.c. Returns -EINVAL if queue number is out
* of range or if queue is already disabled.
*
* NOTE: Must be called after setting up tx control descriptor for that
* queue (see below).
*/
int
ath5k_hw_start_tx_dma(struct ath5k_hw *ah, unsigned int queue)
{
u32 tx_queue;
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 -EINVAL;
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);
ath5k_hw_reg_read(ah, 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;
}
/**
* ath5k_hw_stop_tx_dma() - Stop DMA transmit on a specific queue
* @ah: The &struct ath5k_hw
* @queue: The hw queue number
*
* Stop DMA transmit on a specific hw queue and drain queue so we don't
* have any pending frames. Returns -EBUSY if we still have pending frames,
* -EINVAL if queue number is out of range or inactive.
*/
static int
ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue)
{
unsigned int i = 40;
u32 tx_queue, pending;
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 -EINVAL;
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);
ath5k_hw_reg_read(ah, AR5K_CR);
} else {
/*
* Enable DCU early termination to quickly
* flush any pending frames from QCU
*/
AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
AR5K_QCU_MISC_DCU_EARLY);
/*
* Schedule TX disable and wait until queue is empty
*/
AR5K_REG_WRITE_Q(ah, AR5K_QCU_TXD, queue);
/* Wait for queue to stop */
for (i = 1000; i > 0 &&
(AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue) != 0);
i--)
udelay(100);
if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue))
ATH5K_DBG(ah, ATH5K_DEBUG_DMA,
"queue %i didn't stop !\n", queue);
/* Check for pending frames */
i = 1000;
do {
pending = ath5k_hw_reg_read(ah,
AR5K_QUEUE_STATUS(queue)) &
AR5K_QCU_STS_FRMPENDCNT;
udelay(100);
} while (--i && pending);
/* For 2413+ order PCU to drop packets using
* QUIET mechanism */
if (ah->ah_mac_version >= (AR5K_SREV_AR2414 >> 4) &&
pending) {
/* Set periodicity and duration */
ath5k_hw_reg_write(ah,
AR5K_REG_SM(100, AR5K_QUIET_CTL2_QT_PER)|
AR5K_REG_SM(10, AR5K_QUIET_CTL2_QT_DUR),
AR5K_QUIET_CTL2);
/* Enable quiet period for current TSF */
ath5k_hw_reg_write(ah,
AR5K_QUIET_CTL1_QT_EN |
AR5K_REG_SM(ath5k_hw_reg_read(ah,
AR5K_TSF_L32_5211) >> 10,
AR5K_QUIET_CTL1_NEXT_QT_TSF),
AR5K_QUIET_CTL1);
/* Force channel idle high */
AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5211,
AR5K_DIAG_SW_CHANNEL_IDLE_HIGH);
/* Wait a while and disable mechanism */
udelay(400);
AR5K_REG_DISABLE_BITS(ah, AR5K_QUIET_CTL1,
AR5K_QUIET_CTL1_QT_EN);
/* Re-check for pending frames */
i = 100;
do {
pending = ath5k_hw_reg_read(ah,
AR5K_QUEUE_STATUS(queue)) &
AR5K_QCU_STS_FRMPENDCNT;
udelay(100);
} while (--i && pending);
AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW_5211,
AR5K_DIAG_SW_CHANNEL_IDLE_HIGH);
if (pending)
ATH5K_DBG(ah, ATH5K_DEBUG_DMA,
"quiet mechanism didn't work q:%i !\n",
queue);
}
/*
* Disable DCU early termination
*/
AR5K_REG_DISABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
AR5K_QCU_MISC_DCU_EARLY);
/* Clear register */
ath5k_hw_reg_write(ah, 0, AR5K_QCU_TXD);
if (pending) {
ATH5K_DBG(ah, ATH5K_DEBUG_DMA,
"tx dma didn't stop (q:%i, frm:%i) !\n",
queue, pending);
return -EBUSY;
}
}
/* TODO: Check for success on 5210 else return error */
return 0;
}
/**
* ath5k_hw_stop_beacon_queue() - Stop beacon queue
* @ah: The &struct ath5k_hw
* @queue: The queue number
*
* Returns -EIO if queue didn't stop
*/
int
ath5k_hw_stop_beacon_queue(struct ath5k_hw *ah, unsigned int queue)
{
int ret;
ret = ath5k_hw_stop_tx_dma(ah, queue);
if (ret) {
ATH5K_DBG(ah, ATH5K_DEBUG_DMA,
"beacon queue didn't stop !\n");
return -EIO;
}
return 0;
}
/**
* ath5k_hw_get_txdp() - Get TX Descriptor's address for a specific queue
* @ah: The &struct ath5k_hw
* @queue: The hw queue number
*
* Get TX descriptor's address for a specific queue. For 5210 we ignore
* the queue number and use tx queue type since we only have 2 queues.
* We use TXDP0 for normal data queue and TXDP1 for beacon queue.
* For newer chips with QCU/DCU we just read the corresponding TXDP register.
*
* XXX: Is TXDP read and clear ?
*/
u32
ath5k_hw_get_txdp(struct ath5k_hw *ah, unsigned int queue)
{
u16 tx_reg;
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);
}
/**
* ath5k_hw_set_txdp() - Set TX Descriptor's address for a specific queue
* @ah: The &struct ath5k_hw
* @queue: The hw queue number
* @phys_addr: The physical address
*
* Set TX descriptor's address for a specific queue. For 5210 we ignore
* the queue number and we use tx queue type since we only have 2 queues
* so as above we use TXDP0 for normal data queue and TXDP1 for beacon queue.
* For newer chips with QCU/DCU we just set the corresponding TXDP register.
* Returns -EINVAL if queue type is invalid for 5210 and -EIO if queue is still
* active.
*/
int
ath5k_hw_set_txdp(struct ath5k_hw *ah, unsigned int queue, u32 phys_addr)
{
u16 tx_reg;
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;
}
/**
* ath5k_hw_update_tx_triglevel() - Update tx trigger level
* @ah: The &struct ath5k_hw
* @increase: Flag to force increase of trigger level
*
* This function increases/decreases the tx trigger level for the tx fifo
* buffer (aka FIFO threshold) that is used to indicate when PCU flushes
* the buffer and transmits its data. Lowering this results sending small
* frames more quickly but can lead to tx underruns, raising it a lot can
* result other problems. Right now we start with the lowest possible
* (64Bytes) and if we get tx underrun we increase it using the increase
* flag. Returns -EIO if we have reached maximum/minimum.
*
* XXX: Link this with tx DMA size ?
* XXX2: Use it to save interrupts ?
*/
int
ath5k_hw_update_tx_triglevel(struct ath5k_hw *ah, bool increase)
{
u32 trigger_level, imr;
int ret = -EIO;
/*
* Disable interrupts by setting the mask
*/
imr = ath5k_hw_set_imr(ah, ah->ah_imr & ~AR5K_INT_GLOBAL);
trigger_level = AR5K_REG_MS(ath5k_hw_reg_read(ah, AR5K_TXCFG),
AR5K_TXCFG_TXFULL);
if (!increase) {
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_imr(ah, imr);
return ret;
}
/*******************\
* Interrupt masking *
\*******************/
/**
* ath5k_hw_is_intr_pending() - Check if we have pending interrupts
* @ah: The &struct ath5k_hw
*
* Check if we have pending interrupts to process. Returns 1 if we
* have pending interrupts and 0 if we haven't.
*/
bool
ath5k_hw_is_intr_pending(struct ath5k_hw *ah)
{
return ath5k_hw_reg_read(ah, AR5K_INTPEND) == 1 ? 1 : 0;
}
/**
* ath5k_hw_get_isr() - Get interrupt status
* @ah: The @struct ath5k_hw
* @interrupt_mask: Driver's interrupt mask used to filter out
* interrupts in sw.
*
* This function is used inside our interrupt handler to determine the reason
* for the interrupt by reading Primary Interrupt Status Register. Returns an
* abstract interrupt status mask which is mostly ISR with some uncommon bits
* being mapped on some standard non hw-specific positions
* (check out &ath5k_int).
*
* NOTE: We do write-to-clear, so the active PISR/SISR bits at the time this
* function gets called are cleared on return.
*/
int
ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask)
{
u32 data = 0;
/*
* Read interrupt status from Primary Interrupt
* Register.
*
* Note: PISR/SISR Not available on 5210
*/
if (ah->ah_version == AR5K_AR5210) {
u32 isr = 0;
isr = ath5k_hw_reg_read(ah, AR5K_ISR);
if (unlikely(isr == AR5K_INT_NOCARD)) {
*interrupt_mask = isr;
return -ENODEV;
}
/*
* Filter out the non-common bits from the interrupt
* status.
*/
*interrupt_mask = (isr & AR5K_INT_COMMON) & ah->ah_imr;
/* Hanlde INT_FATAL */
if (unlikely(isr & (AR5K_ISR_SSERR | AR5K_ISR_MCABT
| AR5K_ISR_DPERR)))
*interrupt_mask |= AR5K_INT_FATAL;
/*
* 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 successful assoc + some jiffies.
interrupt_mask &= ~AR5K_INT_BMISS;
*/
data = isr;
} else {
u32 pisr = 0;
u32 pisr_clear = 0;
u32 sisr0 = 0;
u32 sisr1 = 0;
u32 sisr2 = 0;
u32 sisr3 = 0;
u32 sisr4 = 0;
/* Read PISR and SISRs... */
pisr = ath5k_hw_reg_read(ah, AR5K_PISR);
if (unlikely(pisr == AR5K_INT_NOCARD)) {
*interrupt_mask = pisr;
return -ENODEV;
}
sisr0 = ath5k_hw_reg_read(ah, AR5K_SISR0);
sisr1 = ath5k_hw_reg_read(ah, AR5K_SISR1);
sisr2 = ath5k_hw_reg_read(ah, AR5K_SISR2);
sisr3 = ath5k_hw_reg_read(ah, AR5K_SISR3);
sisr4 = ath5k_hw_reg_read(ah, AR5K_SISR4);
/*
* PISR holds the logical OR of interrupt bits
* from SISR registers:
*
* TXOK and TXDESC -> Logical OR of TXOK and TXDESC
* per-queue bits on SISR0
*
* TXERR and TXEOL -> Logical OR of TXERR and TXEOL
* per-queue bits on SISR1
*
* TXURN -> Logical OR of TXURN per-queue bits on SISR2
*
* HIUERR -> Logical OR of MCABT, SSERR and DPER bits on SISR2
*
* BCNMISC -> Logical OR of TIM, CAB_END, DTIM_SYNC
* BCN_TIMEOUT, CAB_TIMEOUT and DTIM
* (and TSFOOR ?) bits on SISR2
*
* QCBRORN and QCBRURN -> Logical OR of QCBRORN and
* QCBRURN per-queue bits on SISR3
* QTRIG -> Logical OR of QTRIG per-queue bits on SISR4
*
* If we clean these bits on PISR we 'll also clear all
* related bits from SISRs, e.g. if we write the TXOK bit on
* PISR we 'll clean all TXOK bits from SISR0 so if a new TXOK
* interrupt got fired for another queue while we were reading
* the interrupt registers and we write back the TXOK bit on
* PISR we 'll lose it. So make sure that we don't write back
* on PISR any bits that come from SISRs. Clearing them from
* SISRs will also clear PISR so no need to worry here.
*/
/* XXX: There seems to be an issue on some cards
* with tx interrupt flags not being updated
* on PISR despite that all Tx interrupt bits
* are cleared on SISRs. Since we handle all
* Tx queues all together it shouldn't be an
* issue if we clear Tx interrupt flags also
* on PISR to avoid that.
*/
pisr_clear = (pisr & ~AR5K_ISR_BITS_FROM_SISRS) |
(pisr & AR5K_INT_TX_ALL);
/*
* Write to clear them...
* Note: This means that each bit we write back
* to the registers will get cleared, leaving the
* rest unaffected. So this won't affect new interrupts
* we didn't catch while reading/processing, we 'll get
* them next time get_isr gets called.
*/
ath5k_hw_reg_write(ah, sisr0, AR5K_SISR0);
ath5k_hw_reg_write(ah, sisr1, AR5K_SISR1);
ath5k_hw_reg_write(ah, sisr2, AR5K_SISR2);
ath5k_hw_reg_write(ah, sisr3, AR5K_SISR3);
ath5k_hw_reg_write(ah, sisr4, AR5K_SISR4);
ath5k_hw_reg_write(ah, pisr_clear, AR5K_PISR);
/* Flush previous write */
ath5k_hw_reg_read(ah, AR5K_PISR);
/*
* Filter out the non-common bits from the interrupt
* status.
*/
*interrupt_mask = (pisr & AR5K_INT_COMMON) & ah->ah_imr;
ah->ah_txq_isr_txok_all = 0;
/* We treat TXOK,TXDESC, TXERR and TXEOL
* the same way (schedule the tx tasklet)
* so we track them all together per queue */
if (pisr & AR5K_ISR_TXOK)
ah->ah_txq_isr_txok_all |= AR5K_REG_MS(sisr0,
AR5K_SISR0_QCU_TXOK);
if (pisr & AR5K_ISR_TXDESC)
ah->ah_txq_isr_txok_all |= AR5K_REG_MS(sisr0,
AR5K_SISR0_QCU_TXDESC);
if (pisr & AR5K_ISR_TXERR)
ah->ah_txq_isr_txok_all |= AR5K_REG_MS(sisr1,
AR5K_SISR1_QCU_TXERR);
if (pisr & AR5K_ISR_TXEOL)
ah->ah_txq_isr_txok_all |= AR5K_REG_MS(sisr1,
AR5K_SISR1_QCU_TXEOL);
/* Misc Beacon related interrupts */
/* For AR5211 */
if (pisr & AR5K_ISR_TIM)
*interrupt_mask |= AR5K_INT_TIM;
/* For AR5212+ */
if (pisr & AR5K_ISR_BCNMISC) {
if (sisr2 & AR5K_SISR2_TIM)
*interrupt_mask |= AR5K_INT_TIM;
if (sisr2 & AR5K_SISR2_DTIM)
*interrupt_mask |= AR5K_INT_DTIM;
if (sisr2 & AR5K_SISR2_DTIM_SYNC)
*interrupt_mask |= AR5K_INT_DTIM_SYNC;
if (sisr2 & AR5K_SISR2_BCN_TIMEOUT)
*interrupt_mask |= AR5K_INT_BCN_TIMEOUT;
if (sisr2 & AR5K_SISR2_CAB_TIMEOUT)
*interrupt_mask |= AR5K_INT_CAB_TIMEOUT;
}
/* Below interrupts are unlikely to happen */
/* HIU = Host Interface Unit (PCI etc)
* Can be one of MCABT, SSERR, DPERR from SISR2 */
if (unlikely(pisr & (AR5K_ISR_HIUERR)))
*interrupt_mask |= AR5K_INT_FATAL;
/*Beacon Not Ready*/
if (unlikely(pisr & (AR5K_ISR_BNR)))
*interrupt_mask |= AR5K_INT_BNR;
/* A queue got CBR overrun */
if (unlikely(pisr & (AR5K_ISR_QCBRORN)))
*interrupt_mask |= AR5K_INT_QCBRORN;
/* A queue got CBR underrun */
if (unlikely(pisr & (AR5K_ISR_QCBRURN)))
*interrupt_mask |= AR5K_INT_QCBRURN;
/* A queue got triggered */
if (unlikely(pisr & (AR5K_ISR_QTRIG)))
*interrupt_mask |= AR5K_INT_QTRIG;
data = pisr;
}
/*
* In case we didn't handle anything,
* print the register value.
*/
if (unlikely(*interrupt_mask == 0 && net_ratelimit()))
ATH5K_PRINTF("ISR: 0x%08x IMR: 0x%08x\n", data, ah->ah_imr);
return 0;
}
/**
* ath5k_hw_set_imr() - Set interrupt mask
* @ah: The &struct ath5k_hw
* @new_mask: The new interrupt mask to be set
*
* Set the interrupt mask in hw to save interrupts. We do that by mapping
* ath5k_int bits to hw-specific bits to remove abstraction and writing
* Interrupt Mask Register.
*/
enum ath5k_int
ath5k_hw_set_imr(struct ath5k_hw *ah, enum ath5k_int new_mask)
{
enum ath5k_int old_mask, int_mask;
old_mask = ah->ah_imr;
/*
* Disable card interrupts to prevent any race conditions
* (they will be re-enabled afterwards if AR5K_INT GLOBAL
* is set again on the new mask).
*/
if (old_mask & AR5K_INT_GLOBAL) {
ath5k_hw_reg_write(ah, AR5K_IER_DISABLE, AR5K_IER);
ath5k_hw_reg_read(ah, AR5K_IER);
}
/*
* Add additional, chipset-dependent interrupt mask flags
* and write them to the IMR (interrupt mask register).
*/
int_mask = new_mask & AR5K_INT_COMMON;
if (ah->ah_version != AR5K_AR5210) {
/* Preserve per queue TXURN interrupt mask */
u32 simr2 = ath5k_hw_reg_read(ah, AR5K_SIMR2)
& AR5K_SIMR2_QCU_TXURN;
/* Fatal interrupt abstraction for 5211+ */
if (new_mask & AR5K_INT_FATAL) {
int_mask |= AR5K_IMR_HIUERR;
simr2 |= (AR5K_SIMR2_MCABT | AR5K_SIMR2_SSERR
| AR5K_SIMR2_DPERR);
}
/* Misc beacon related interrupts */
if (new_mask & AR5K_INT_TIM)
int_mask |= AR5K_IMR_TIM;
if (new_mask & AR5K_INT_TIM)
simr2 |= AR5K_SISR2_TIM;
if (new_mask & AR5K_INT_DTIM)
simr2 |= AR5K_SISR2_DTIM;
if (new_mask & AR5K_INT_DTIM_SYNC)
simr2 |= AR5K_SISR2_DTIM_SYNC;
if (new_mask & AR5K_INT_BCN_TIMEOUT)
simr2 |= AR5K_SISR2_BCN_TIMEOUT;
if (new_mask & AR5K_INT_CAB_TIMEOUT)
simr2 |= AR5K_SISR2_CAB_TIMEOUT;
/*Beacon Not Ready*/
if (new_mask & AR5K_INT_BNR)
int_mask |= AR5K_INT_BNR;
/* Note: Per queue interrupt masks
* are set via ath5k_hw_reset_tx_queue() (qcu.c) */
ath5k_hw_reg_write(ah, int_mask, AR5K_PIMR);
ath5k_hw_reg_write(ah, simr2, AR5K_SIMR2);
} else {
/* Fatal interrupt abstraction for 5210 */
if (new_mask & AR5K_INT_FATAL)
int_mask |= (AR5K_IMR_SSERR | AR5K_IMR_MCABT
| AR5K_IMR_HIUERR | AR5K_IMR_DPERR);
/* Only common interrupts left for 5210 (no SIMRs) */
ath5k_hw_reg_write(ah, int_mask, AR5K_IMR);
}
/* If RXNOFRM interrupt is masked disable it
* by setting AR5K_RXNOFRM to zero */
if (!(new_mask & AR5K_INT_RXNOFRM))
ath5k_hw_reg_write(ah, 0, AR5K_RXNOFRM);
/* Store new interrupt mask */
ah->ah_imr = new_mask;
/* ..re-enable interrupts if AR5K_INT_GLOBAL is set */
if (new_mask & AR5K_INT_GLOBAL) {
ath5k_hw_reg_write(ah, AR5K_IER_ENABLE, AR5K_IER);
ath5k_hw_reg_read(ah, AR5K_IER);
}
return old_mask;
}
/********************\
Init/Stop functions
\********************/
/**
* ath5k_hw_dma_init() - Initialize DMA unit
* @ah: The &struct ath5k_hw
*
* Set DMA size and pre-enable interrupts
* (driver handles tx/rx buffer setup and
* dma start/stop)
*
* XXX: Save/restore RXDP/TXDP registers ?
*/
void
ath5k_hw_dma_init(struct ath5k_hw *ah)
{
/*
* Set Rx/Tx DMA Configuration
*
* Set standard DMA size (128). Note that
* a DMA size of 512 causes rx overruns and tx errors
* on pci-e cards (tested on 5424 but since rx overruns
* also occur on 5416/5418 with madwifi we set 128
* for all PCI-E cards to be safe).
*
* XXX: need to check 5210 for this
* TODO: Check out tx trigger level, it's always 64 on dumps but I
* guess we can tweak it and see how it goes ;-)
*/
if (ah->ah_version != AR5K_AR5210) {
AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG,
AR5K_TXCFG_SDMAMR, AR5K_DMASIZE_128B);
AR5K_REG_WRITE_BITS(ah, AR5K_RXCFG,
AR5K_RXCFG_SDMAMW, AR5K_DMASIZE_128B);
}
/* Pre-enable interrupts on 5211/5212*/
if (ah->ah_version != AR5K_AR5210)
ath5k_hw_set_imr(ah, ah->ah_imr);
}
/**
* ath5k_hw_dma_stop() - stop DMA unit
* @ah: The &struct ath5k_hw
*
* Stop tx/rx DMA and interrupts. Returns
* -EBUSY if tx or rx dma failed to stop.
*
* XXX: Sometimes DMA unit hangs and we have
* stuck frames on tx queues, only a reset
* can fix that.
*/
int
ath5k_hw_dma_stop(struct ath5k_hw *ah)
{
int i, qmax, err;
err = 0;
/* Disable interrupts */
ath5k_hw_set_imr(ah, 0);
/* Stop rx dma */
err = ath5k_hw_stop_rx_dma(ah);
if (err)
return err;
/* Clear any pending interrupts
* and disable tx dma */
if (ah->ah_version != AR5K_AR5210) {
ath5k_hw_reg_write(ah, 0xffffffff, AR5K_PISR);
qmax = AR5K_NUM_TX_QUEUES;
} else {
/* PISR/SISR Not available on 5210 */
ath5k_hw_reg_read(ah, AR5K_ISR);
qmax = AR5K_NUM_TX_QUEUES_NOQCU;
}
for (i = 0; i < qmax; i++) {
err = ath5k_hw_stop_tx_dma(ah, i);
/* -EINVAL -> queue inactive */
if (err && err != -EINVAL)
return err;
}
return 0;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,492 @@
/*
* Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@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.
*
*/
/*
* Common ar5xxx EEPROM data offsets (set these on AR5K_EEPROM_BASE)
*/
#define AR5K_EEPROM_PCIE_OFFSET 0x02 /* Contains offset to PCI-E infos */
#define AR5K_EEPROM_PCIE_SERDES_SECTION 0x40 /* PCIE_OFFSET points here when
* SERDES infos are present */
#define AR5K_EEPROM_MAGIC 0x003d /* EEPROM Magic number */
#define AR5K_EEPROM_MAGIC_VALUE 0x5aa5 /* Default - found on EEPROM */
#define AR5K_EEPROM_IS_HB63 0x000b /* Talon detect */
#define AR5K_EEPROM_RFKILL 0x0f
#define AR5K_EEPROM_RFKILL_GPIO_SEL 0x0000001c
#define AR5K_EEPROM_RFKILL_GPIO_SEL_S 2
#define AR5K_EEPROM_RFKILL_POLARITY 0x00000002
#define AR5K_EEPROM_RFKILL_POLARITY_S 1
#define AR5K_EEPROM_REG_DOMAIN 0x00bf /* EEPROM regdom */
/* FLASH(EEPROM) Defines for AR531X chips */
#define AR5K_EEPROM_SIZE_LOWER 0x1b /* size info -- lower */
#define AR5K_EEPROM_SIZE_UPPER 0x1c /* size info -- upper */
#define AR5K_EEPROM_SIZE_UPPER_MASK 0xfff0
#define AR5K_EEPROM_SIZE_UPPER_SHIFT 4
#define AR5K_EEPROM_SIZE_ENDLOC_SHIFT 12
#define AR5K_EEPROM_CHECKSUM 0x00c0 /* EEPROM checksum */
#define AR5K_EEPROM_INFO_BASE 0x00c0 /* EEPROM header */
#define AR5K_EEPROM_INFO_MAX (0x400 - AR5K_EEPROM_INFO_BASE)
#define AR5K_EEPROM_INFO_CKSUM 0xffff
#define AR5K_EEPROM_INFO(_n) (AR5K_EEPROM_INFO_BASE + (_n))
#define AR5K_EEPROM_VERSION AR5K_EEPROM_INFO(1) /* EEPROM Version */
#define AR5K_EEPROM_VERSION_3_0 0x3000 /* No idea what's going on before this version */
#define AR5K_EEPROM_VERSION_3_1 0x3001 /* ob/db values for 2GHz (ar5211_rfregs) */
#define AR5K_EEPROM_VERSION_3_2 0x3002 /* different frequency representation (eeprom_bin2freq) */
#define AR5K_EEPROM_VERSION_3_3 0x3003 /* offsets changed, has 32 CTLs (see below) and ee_false_detect (eeprom_read_modes) */
#define AR5K_EEPROM_VERSION_3_4 0x3004 /* has ee_i_gain, ee_cck_ofdm_power_delta (eeprom_read_modes) */
#define AR5K_EEPROM_VERSION_4_0 0x4000 /* has ee_misc, ee_cal_pier, ee_turbo_max_power and ee_xr_power (eeprom_init) */
#define AR5K_EEPROM_VERSION_4_1 0x4001 /* has ee_margin_tx_rx (eeprom_init) */
#define AR5K_EEPROM_VERSION_4_2 0x4002 /* has ee_cck_ofdm_gain_delta (eeprom_init) */
#define AR5K_EEPROM_VERSION_4_3 0x4003 /* power calibration changes */
#define AR5K_EEPROM_VERSION_4_4 0x4004
#define AR5K_EEPROM_VERSION_4_5 0x4005
#define AR5K_EEPROM_VERSION_4_6 0x4006 /* has ee_scaled_cck_delta */
#define AR5K_EEPROM_VERSION_4_7 0x3007 /* 4007 ? */
#define AR5K_EEPROM_VERSION_4_9 0x4009 /* EAR futureproofing */
#define AR5K_EEPROM_VERSION_5_0 0x5000 /* Has 2413 PDADC calibration etc */
#define AR5K_EEPROM_VERSION_5_1 0x5001 /* Has capability values */
#define AR5K_EEPROM_VERSION_5_3 0x5003 /* Has spur mitigation tables */
#define AR5K_EEPROM_MODE_11A 0
#define AR5K_EEPROM_MODE_11B 1
#define AR5K_EEPROM_MODE_11G 2
#define AR5K_EEPROM_HDR AR5K_EEPROM_INFO(2) /* Header that contains the device caps */
#define AR5K_EEPROM_HDR_11A(_v) (((_v) >> AR5K_EEPROM_MODE_11A) & 0x1)
#define AR5K_EEPROM_HDR_11B(_v) (((_v) >> AR5K_EEPROM_MODE_11B) & 0x1)
#define AR5K_EEPROM_HDR_11G(_v) (((_v) >> AR5K_EEPROM_MODE_11G) & 0x1)
#define AR5K_EEPROM_HDR_T_2GHZ_DIS(_v) (((_v) >> 3) & 0x1) /* Disable turbo for 2GHz */
#define AR5K_EEPROM_HDR_T_5GHZ_DBM(_v) (((_v) >> 4) & 0x7f) /* Max turbo power for < 2W power consumption */
#define AR5K_EEPROM_HDR_DEVICE(_v) (((_v) >> 11) & 0x7) /* Device type (1 Cardbus, 2 PCI, 3 MiniPCI, 4 AP) */
#define AR5K_EEPROM_HDR_RFKILL(_v) (((_v) >> 14) & 0x1) /* Device has RFKill support */
#define AR5K_EEPROM_HDR_T_5GHZ_DIS(_v) (((_v) >> 15) & 0x1) /* Disable turbo for 5GHz */
/* Newer EEPROMs are using a different offset */
#define AR5K_EEPROM_OFF(_v, _v3_0, _v3_3) \
(((_v) >= AR5K_EEPROM_VERSION_3_3) ? _v3_3 : _v3_0)
#define AR5K_EEPROM_ANT_GAIN(_v) AR5K_EEPROM_OFF(_v, 0x00c4, 0x00c3)
#define AR5K_EEPROM_ANT_GAIN_5GHZ(_v) ((s8)(((_v) >> 8) & 0xff))
#define AR5K_EEPROM_ANT_GAIN_2GHZ(_v) ((s8)((_v) & 0xff))
/* Misc values available since EEPROM 4.0 */
#define AR5K_EEPROM_MISC0 AR5K_EEPROM_INFO(4)
#define AR5K_EEPROM_EARSTART(_v) ((_v) & 0xfff)
#define AR5K_EEPROM_HDR_XR2_DIS(_v) (((_v) >> 12) & 0x1)
#define AR5K_EEPROM_HDR_XR5_DIS(_v) (((_v) >> 13) & 0x1)
#define AR5K_EEPROM_EEMAP(_v) (((_v) >> 14) & 0x3)
#define AR5K_EEPROM_MISC1 AR5K_EEPROM_INFO(5)
#define AR5K_EEPROM_TARGET_PWRSTART(_v) ((_v) & 0xfff)
#define AR5K_EEPROM_HAS32KHZCRYSTAL(_v) (((_v) >> 14) & 0x1) /* has 32KHz crystal for sleep mode */
#define AR5K_EEPROM_HAS32KHZCRYSTAL_OLD(_v) (((_v) >> 15) & 0x1)
#define AR5K_EEPROM_MISC2 AR5K_EEPROM_INFO(6)
#define AR5K_EEPROM_EEP_FILE_VERSION(_v) (((_v) >> 8) & 0xff)
#define AR5K_EEPROM_EAR_FILE_VERSION(_v) ((_v) & 0xff)
#define AR5K_EEPROM_MISC3 AR5K_EEPROM_INFO(7)
#define AR5K_EEPROM_ART_BUILD_NUM(_v) (((_v) >> 10) & 0x3f)
#define AR5K_EEPROM_EAR_FILE_ID(_v) ((_v) & 0xff)
#define AR5K_EEPROM_MISC4 AR5K_EEPROM_INFO(8)
#define AR5K_EEPROM_CAL_DATA_START(_v) (((_v) >> 4) & 0xfff)
#define AR5K_EEPROM_MASK_R0(_v) (((_v) >> 2) & 0x3) /* modes supported by radio 0 (bit 1: G, bit 2: A) */
#define AR5K_EEPROM_MASK_R1(_v) ((_v) & 0x3) /* modes supported by radio 1 (bit 1: G, bit 2: A) */
#define AR5K_EEPROM_MISC5 AR5K_EEPROM_INFO(9)
#define AR5K_EEPROM_COMP_DIS(_v) ((_v) & 0x1) /* disable compression */
#define AR5K_EEPROM_AES_DIS(_v) (((_v) >> 1) & 0x1) /* disable AES */
#define AR5K_EEPROM_FF_DIS(_v) (((_v) >> 2) & 0x1) /* disable fast frames */
#define AR5K_EEPROM_BURST_DIS(_v) (((_v) >> 3) & 0x1) /* disable bursting */
#define AR5K_EEPROM_MAX_QCU(_v) (((_v) >> 4) & 0xf) /* max number of QCUs. defaults to 10 */
#define AR5K_EEPROM_HEAVY_CLIP_EN(_v) (((_v) >> 8) & 0x1) /* enable heavy clipping */
#define AR5K_EEPROM_KEY_CACHE_SIZE(_v) (((_v) >> 12) & 0xf) /* key cache size. defaults to 128 */
#define AR5K_EEPROM_MISC6 AR5K_EEPROM_INFO(10)
#define AR5K_EEPROM_TX_CHAIN_DIS ((_v) & 0x7) /* MIMO chains disabled for TX bitmask */
#define AR5K_EEPROM_RX_CHAIN_DIS (((_v) >> 3) & 0x7) /* MIMO chains disabled for RX bitmask */
#define AR5K_EEPROM_FCC_MID_EN (((_v) >> 6) & 0x1) /* 5.47-5.7GHz supported */
#define AR5K_EEPROM_JAP_U1EVEN_EN (((_v) >> 7) & 0x1) /* Japan UNII1 band (5.15-5.25GHz) on even channels (5180, 5200, 5220, 5240) supported */
#define AR5K_EEPROM_JAP_U2_EN (((_v) >> 8) & 0x1) /* Japan UNII2 band (5.25-5.35GHz) supported */
#define AR5K_EEPROM_JAP_MID_EN (((_v) >> 9) & 0x1) /* Japan band from 5.47-5.7GHz supported */
#define AR5K_EEPROM_JAP_U1ODD_EN (((_v) >> 10) & 0x1) /* Japan UNII2 band (5.15-5.25GHz) on odd channels (5170, 5190, 5210, 5230) supported */
#define AR5K_EEPROM_JAP_11A_NEW_EN (((_v) >> 11) & 0x1) /* Japan A mode enabled (using even channels) */
/* calibration settings */
#define AR5K_EEPROM_MODES_11A(_v) AR5K_EEPROM_OFF(_v, 0x00c5, 0x00d4)
#define AR5K_EEPROM_MODES_11B(_v) AR5K_EEPROM_OFF(_v, 0x00d0, 0x00f2)
#define AR5K_EEPROM_MODES_11G(_v) AR5K_EEPROM_OFF(_v, 0x00da, 0x010d)
#define AR5K_EEPROM_CTL(_v) AR5K_EEPROM_OFF(_v, 0x00e4, 0x0128) /* Conformance test limits */
#define AR5K_EEPROM_GROUPS_START(_v) AR5K_EEPROM_OFF(_v, 0x0100, 0x0150) /* Start of Groups */
#define AR5K_EEPROM_GROUP1_OFFSET 0x0
#define AR5K_EEPROM_GROUP2_OFFSET 0x5
#define AR5K_EEPROM_GROUP3_OFFSET 0x37
#define AR5K_EEPROM_GROUP4_OFFSET 0x46
#define AR5K_EEPROM_GROUP5_OFFSET 0x55
#define AR5K_EEPROM_GROUP6_OFFSET 0x65
#define AR5K_EEPROM_GROUP7_OFFSET 0x69
#define AR5K_EEPROM_GROUP8_OFFSET 0x6f
#define AR5K_EEPROM_TARGET_PWR_OFF_11A(_v) AR5K_EEPROM_OFF(_v, AR5K_EEPROM_GROUPS_START(_v) + \
AR5K_EEPROM_GROUP5_OFFSET, 0x0000)
#define AR5K_EEPROM_TARGET_PWR_OFF_11B(_v) AR5K_EEPROM_OFF(_v, AR5K_EEPROM_GROUPS_START(_v) + \
AR5K_EEPROM_GROUP6_OFFSET, 0x0010)
#define AR5K_EEPROM_TARGET_PWR_OFF_11G(_v) AR5K_EEPROM_OFF(_v, AR5K_EEPROM_GROUPS_START(_v) + \
AR5K_EEPROM_GROUP7_OFFSET, 0x0014)
/* [3.1 - 3.3] */
#define AR5K_EEPROM_OBDB0_2GHZ 0x00ec
#define AR5K_EEPROM_OBDB1_2GHZ 0x00ed
#define AR5K_EEPROM_PROTECT 0x003f /* EEPROM protect status */
#define AR5K_EEPROM_PROTECT_RD_0_31 0x0001 /* Read protection bit for offsets 0x0 - 0x1f */
#define AR5K_EEPROM_PROTECT_WR_0_31 0x0002 /* Write protection bit for offsets 0x0 - 0x1f */
#define AR5K_EEPROM_PROTECT_RD_32_63 0x0004 /* 0x20 - 0x3f */
#define AR5K_EEPROM_PROTECT_WR_32_63 0x0008
#define AR5K_EEPROM_PROTECT_RD_64_127 0x0010 /* 0x40 - 0x7f */
#define AR5K_EEPROM_PROTECT_WR_64_127 0x0020
#define AR5K_EEPROM_PROTECT_RD_128_191 0x0040 /* 0x80 - 0xbf (regdom) */
#define AR5K_EEPROM_PROTECT_WR_128_191 0x0080
#define AR5K_EEPROM_PROTECT_RD_192_207 0x0100 /* 0xc0 - 0xcf */
#define AR5K_EEPROM_PROTECT_WR_192_207 0x0200
#define AR5K_EEPROM_PROTECT_RD_208_223 0x0400 /* 0xd0 - 0xdf */
#define AR5K_EEPROM_PROTECT_WR_208_223 0x0800
#define AR5K_EEPROM_PROTECT_RD_224_239 0x1000 /* 0xe0 - 0xef */
#define AR5K_EEPROM_PROTECT_WR_224_239 0x2000
#define AR5K_EEPROM_PROTECT_RD_240_255 0x4000 /* 0xf0 - 0xff */
#define AR5K_EEPROM_PROTECT_WR_240_255 0x8000
/* Some EEPROM defines */
#define AR5K_EEPROM_EEP_SCALE 100
#define AR5K_EEPROM_EEP_DELTA 10
#define AR5K_EEPROM_N_MODES 3
#define AR5K_EEPROM_N_5GHZ_CHAN 10
#define AR5K_EEPROM_N_5GHZ_RATE_CHAN 8
#define AR5K_EEPROM_N_2GHZ_CHAN 3
#define AR5K_EEPROM_N_2GHZ_CHAN_2413 4
#define AR5K_EEPROM_N_2GHZ_CHAN_MAX 4
#define AR5K_EEPROM_MAX_CHAN 10
#define AR5K_EEPROM_N_PWR_POINTS_5111 11
#define AR5K_EEPROM_N_PCDAC 11
#define AR5K_EEPROM_N_PHASE_CAL 5
#define AR5K_EEPROM_N_TEST_FREQ 8
#define AR5K_EEPROM_N_EDGES 8
#define AR5K_EEPROM_N_INTERCEPTS 11
#define AR5K_EEPROM_FREQ_M(_v) AR5K_EEPROM_OFF(_v, 0x7f, 0xff)
#define AR5K_EEPROM_PCDAC_M 0x3f
#define AR5K_EEPROM_PCDAC_START 1
#define AR5K_EEPROM_PCDAC_STOP 63
#define AR5K_EEPROM_PCDAC_STEP 1
#define AR5K_EEPROM_NON_EDGE_M 0x40
#define AR5K_EEPROM_CHANNEL_POWER 8
#define AR5K_EEPROM_N_OBDB 4
#define AR5K_EEPROM_OBDB_DIS 0xffff
#define AR5K_EEPROM_CHANNEL_DIS 0xff
#define AR5K_EEPROM_SCALE_OC_DELTA(_x) (((_x) * 2) / 10)
#define AR5K_EEPROM_N_CTLS(_v) AR5K_EEPROM_OFF(_v, 16, 32)
#define AR5K_EEPROM_MAX_CTLS 32
#define AR5K_EEPROM_N_PD_CURVES 4
#define AR5K_EEPROM_N_XPD0_POINTS 4
#define AR5K_EEPROM_N_XPD3_POINTS 3
#define AR5K_EEPROM_N_PD_GAINS 4
#define AR5K_EEPROM_N_PD_POINTS 5
#define AR5K_EEPROM_N_INTERCEPT_10_2GHZ 35
#define AR5K_EEPROM_N_INTERCEPT_10_5GHZ 55
#define AR5K_EEPROM_POWER_M 0x3f
#define AR5K_EEPROM_POWER_MIN 0
#define AR5K_EEPROM_POWER_MAX 3150
#define AR5K_EEPROM_POWER_STEP 50
#define AR5K_EEPROM_POWER_TABLE_SIZE 64
#define AR5K_EEPROM_N_POWER_LOC_11B 4
#define AR5K_EEPROM_N_POWER_LOC_11G 6
#define AR5K_EEPROM_I_GAIN 10
#define AR5K_EEPROM_CCK_OFDM_DELTA 15
#define AR5K_EEPROM_N_IQ_CAL 2
/* 5GHz/2GHz */
enum ath5k_eeprom_freq_bands {
AR5K_EEPROM_BAND_5GHZ = 0,
AR5K_EEPROM_BAND_2GHZ = 1,
AR5K_EEPROM_N_FREQ_BANDS,
};
/* Spur chans per freq band */
#define AR5K_EEPROM_N_SPUR_CHANS 5
/* fbin value for chan 2464 x2 */
#define AR5K_EEPROM_5413_SPUR_CHAN_1 1640
/* fbin value for chan 2420 x2 */
#define AR5K_EEPROM_5413_SPUR_CHAN_2 1200
#define AR5K_EEPROM_SPUR_CHAN_MASK 0x3FFF
#define AR5K_EEPROM_NO_SPUR 0x8000
#define AR5K_SPUR_CHAN_WIDTH 87
#define AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz 3125
#define AR5K_SPUR_SYMBOL_WIDTH_TURBO_100Hz 6250
#define AR5K_EEPROM_READ(_o, _v) do { \
if (!ath5k_hw_nvram_read(ah, (_o), &(_v))) \
return -EIO; \
} while (0)
#define AR5K_EEPROM_READ_HDR(_o, _v) \
AR5K_EEPROM_READ(_o, ah->ah_capabilities.cap_eeprom._v); \
enum ath5k_ant_table {
AR5K_ANT_CTL = 0, /* Idle switch table settings */
AR5K_ANT_SWTABLE_A = 1, /* Switch table for antenna A */
AR5K_ANT_SWTABLE_B = 2, /* Switch table for antenna B */
AR5K_ANT_MAX,
};
enum ath5k_ctl_mode {
AR5K_CTL_11A = 0,
AR5K_CTL_11B = 1,
AR5K_CTL_11G = 2,
AR5K_CTL_TURBO = 3,
AR5K_CTL_TURBOG = 4,
AR5K_CTL_2GHT20 = 5,
AR5K_CTL_5GHT20 = 6,
AR5K_CTL_2GHT40 = 7,
AR5K_CTL_5GHT40 = 8,
AR5K_CTL_MODE_M = 15,
};
/* Per channel calibration data, used for power table setup */
struct ath5k_chan_pcal_info_rf5111 {
/* Power levels in half dBm units
* for one power curve. */
u8 pwr[AR5K_EEPROM_N_PWR_POINTS_5111];
/* PCDAC table steps
* for the above values */
u8 pcdac[AR5K_EEPROM_N_PWR_POINTS_5111];
/* Starting PCDAC step */
u8 pcdac_min;
/* Final PCDAC step */
u8 pcdac_max;
};
struct ath5k_chan_pcal_info_rf5112 {
/* Power levels in quarter dBm units
* for lower (0) and higher (3)
* level curves in 0.25dB units */
s8 pwr_x0[AR5K_EEPROM_N_XPD0_POINTS];
s8 pwr_x3[AR5K_EEPROM_N_XPD3_POINTS];
/* PCDAC table steps
* for the above values */
u8 pcdac_x0[AR5K_EEPROM_N_XPD0_POINTS];
u8 pcdac_x3[AR5K_EEPROM_N_XPD3_POINTS];
};
struct ath5k_chan_pcal_info_rf2413 {
/* Starting pwr/pddac values */
s8 pwr_i[AR5K_EEPROM_N_PD_GAINS];
u8 pddac_i[AR5K_EEPROM_N_PD_GAINS];
/* (pwr,pddac) points
* power levels in 0.5dB units */
s8 pwr[AR5K_EEPROM_N_PD_GAINS]
[AR5K_EEPROM_N_PD_POINTS];
u8 pddac[AR5K_EEPROM_N_PD_GAINS]
[AR5K_EEPROM_N_PD_POINTS];
};
enum ath5k_powertable_type {
AR5K_PWRTABLE_PWR_TO_PCDAC = 0,
AR5K_PWRTABLE_LINEAR_PCDAC = 1,
AR5K_PWRTABLE_PWR_TO_PDADC = 2,
};
struct ath5k_pdgain_info {
u8 pd_points;
u8 *pd_step;
/* Power values are in
* 0.25dB units */
s16 *pd_pwr;
};
struct ath5k_chan_pcal_info {
/* Frequency */
u16 freq;
/* Tx power boundaries */
s16 max_pwr;
s16 min_pwr;
union {
struct ath5k_chan_pcal_info_rf5111 rf5111_info;
struct ath5k_chan_pcal_info_rf5112 rf5112_info;
struct ath5k_chan_pcal_info_rf2413 rf2413_info;
};
/* Raw values used by phy code
* Curves are stored in order from lower
* gain to higher gain (max txpower -> min txpower) */
struct ath5k_pdgain_info *pd_curves;
};
/* Per rate calibration data for each mode,
* used for rate power table setup.
* Note: Values in 0.5dB units */
struct ath5k_rate_pcal_info {
u16 freq; /* Frequency */
/* Power level for 6-24Mbit/s rates or
* 1Mb rate */
u16 target_power_6to24;
/* Power level for 36Mbit rate or
* 2Mb rate */
u16 target_power_36;
/* Power level for 48Mbit rate or
* 5.5Mbit rate */
u16 target_power_48;
/* Power level for 54Mbit rate or
* 11Mbit rate */
u16 target_power_54;
};
/* Power edges for conformance test limits */
struct ath5k_edge_power {
u16 freq;
u16 edge; /* in half dBm */
bool flag;
};
/**
* struct ath5k_eeprom_info - EEPROM calibration data
*
* @ee_regdomain: ath/regd.c takes care of COUNTRY_ERD and WORLDWIDE_ROAMING
* flags
* @ee_ant_gain: Antenna gain in 0.5dB steps signed [5211 only?]
* @ee_cck_ofdm_gain_delta: difference in gainF to output the same power for
* OFDM and CCK packets
* @ee_cck_ofdm_power_delta: power difference between OFDM (6Mbps) and CCK
* (11Mbps) rate in G mode. 0.1dB steps
* @ee_scaled_cck_delta: for Japan Channel 14: 0.1dB resolution
*
* @ee_i_cal: Initial I coefficient to correct I/Q mismatch in the receive path
* @ee_q_cal: Initial Q coefficient to correct I/Q mismatch in the receive path
* @ee_fixed_bias: use ee_ob and ee_db settings or use automatic control
* @ee_switch_settling: RX/TX Switch settling time
* @ee_atn_tx_rx: Difference in attenuation between TX and RX in 1dB steps
* @ee_ant_control: Antenna Control Settings
* @ee_ob: Bias current for Output stage of PA
* B/G mode: Index [0] is used for AR2112/5112, otherwise [1]
* A mode: [0] 5.15-5.25 [1] 5.25-5.50 [2] 5.50-5.70 [3] 5.70-5.85 GHz
* @ee_db: Bias current for Output stage of PA. see @ee_ob
* @ee_tx_end2xlna_enable: Time difference from when BB finishes sending a frame
* to when the external LNA is activated
* @ee_tx_end2xpa_disable: Time difference from when BB finishes sending a frame
* to when the external PA switch is deactivated
* @ee_tx_frm2xpa_enable: Time difference from when MAC sends frame to when
* external PA switch is activated
* @ee_thr_62: Clear Channel Assessment (CCA) sensitivity
* (IEEE802.11a section 17.3.10.5 )
* @ee_xlna_gain: Total gain of the LNA (information only)
* @ee_xpd: Use external (1) or internal power detector
* @ee_x_gain: Gain for external power detector output (differences in EEMAP
* versions!)
* @ee_i_gain: Initial gain value after reset
* @ee_margin_tx_rx: Margin in dB when final attenuation stage should be used
*
* @ee_false_detect: Backoff in Sensitivity (dB) on channels with spur signals
* @ee_noise_floor_thr: Noise floor threshold in 1dB steps
* @ee_adc_desired_size: Desired amplitude for ADC, used by AGC; in 0.5 dB steps
* @ee_pga_desired_size: Desired output of PGA (for BB gain) in 0.5 dB steps
* @ee_pd_gain_overlap: PD ADC curves need to overlap in 0.5dB steps (ee_map>=2)
*/
struct ath5k_eeprom_info {
/* Header information */
u16 ee_magic;
u16 ee_protect;
u16 ee_regdomain;
u16 ee_version;
u16 ee_header;
u16 ee_ant_gain;
u8 ee_rfkill_pin;
bool ee_rfkill_pol;
bool ee_is_hb63;
bool ee_serdes;
u16 ee_misc0;
u16 ee_misc1;
u16 ee_misc2;
u16 ee_misc3;
u16 ee_misc4;
u16 ee_misc5;
u16 ee_misc6;
u16 ee_cck_ofdm_gain_delta;
u16 ee_cck_ofdm_power_delta;
u16 ee_scaled_cck_delta;
/* RF Calibration settings (reset, rfregs) */
u16 ee_i_cal[AR5K_EEPROM_N_MODES];
u16 ee_q_cal[AR5K_EEPROM_N_MODES];
u16 ee_fixed_bias[AR5K_EEPROM_N_MODES];
u16 ee_turbo_max_power[AR5K_EEPROM_N_MODES];
u16 ee_xr_power[AR5K_EEPROM_N_MODES];
u16 ee_switch_settling[AR5K_EEPROM_N_MODES];
u16 ee_atn_tx_rx[AR5K_EEPROM_N_MODES];
u16 ee_ant_control[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_PCDAC];
u16 ee_ob[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_OBDB];
u16 ee_db[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_OBDB];
u16 ee_tx_end2xlna_enable[AR5K_EEPROM_N_MODES];
u16 ee_tx_end2xpa_disable[AR5K_EEPROM_N_MODES];
u16 ee_tx_frm2xpa_enable[AR5K_EEPROM_N_MODES];
u16 ee_thr_62[AR5K_EEPROM_N_MODES];
u16 ee_xlna_gain[AR5K_EEPROM_N_MODES];
u16 ee_xpd[AR5K_EEPROM_N_MODES];
u16 ee_x_gain[AR5K_EEPROM_N_MODES];
u16 ee_i_gain[AR5K_EEPROM_N_MODES];
u16 ee_margin_tx_rx[AR5K_EEPROM_N_MODES];
u16 ee_switch_settling_turbo[AR5K_EEPROM_N_MODES];
u16 ee_margin_tx_rx_turbo[AR5K_EEPROM_N_MODES];
u16 ee_atn_tx_rx_turbo[AR5K_EEPROM_N_MODES];
/* Power calibration data */
u16 ee_false_detect[AR5K_EEPROM_N_MODES];
/* Number of pd gain curves per mode */
u8 ee_pd_gains[AR5K_EEPROM_N_MODES];
/* Back mapping pdcurve number -> pdcurve index in pd->pd_curves */
u8 ee_pdc_to_idx[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_PD_GAINS];
u8 ee_n_piers[AR5K_EEPROM_N_MODES];
struct ath5k_chan_pcal_info ee_pwr_cal_a[AR5K_EEPROM_N_5GHZ_CHAN];
struct ath5k_chan_pcal_info ee_pwr_cal_b[AR5K_EEPROM_N_2GHZ_CHAN_MAX];
struct ath5k_chan_pcal_info ee_pwr_cal_g[AR5K_EEPROM_N_2GHZ_CHAN_MAX];
/* Per rate target power levels */
u8 ee_rate_target_pwr_num[AR5K_EEPROM_N_MODES];
struct ath5k_rate_pcal_info ee_rate_tpwr_a[AR5K_EEPROM_N_5GHZ_CHAN];
struct ath5k_rate_pcal_info ee_rate_tpwr_b[AR5K_EEPROM_N_2GHZ_CHAN_MAX];
struct ath5k_rate_pcal_info ee_rate_tpwr_g[AR5K_EEPROM_N_2GHZ_CHAN_MAX];
/* Conformance test limits (Unused) */
u8 ee_ctls;
u8 ee_ctl[AR5K_EEPROM_MAX_CTLS];
struct ath5k_edge_power ee_ctl_pwr[AR5K_EEPROM_N_EDGES * AR5K_EEPROM_MAX_CTLS];
/* Noise Floor Calibration settings */
s16 ee_noise_floor_thr[AR5K_EEPROM_N_MODES];
s8 ee_adc_desired_size[AR5K_EEPROM_N_MODES];
s8 ee_pga_desired_size[AR5K_EEPROM_N_MODES];
s8 ee_adc_desired_size_turbo[AR5K_EEPROM_N_MODES];
s8 ee_pga_desired_size_turbo[AR5K_EEPROM_N_MODES];
s8 ee_pd_gain_overlap;
/* Spur mitigation data (fbin values for spur channels) */
u16 ee_spur_chans[AR5K_EEPROM_N_SPUR_CHANS][AR5K_EEPROM_N_FREQ_BANDS];
};
@@ -0,0 +1,213 @@
/*
* Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@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.
*
*/
/****************\
GPIO Functions
\****************/
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
/**
* DOC: GPIO/LED functions
*
* Here we control the 6 bidirectional GPIO pins provided by the hw.
* We can set a GPIO pin to be an input or an output pin on GPIO control
* register and then read or set its status from GPIO data input/output
* registers.
*
* We also control the two LED pins provided by the hw, LED_0 is our
* "power" LED and LED_1 is our "network activity" LED but many scenarios
* are available from hw. Vendors might also provide LEDs connected to the
* GPIO pins, we handle them through the LED subsystem on led.c
*/
/**
* ath5k_hw_set_ledstate() - Set led state
* @ah: The &struct ath5k_hw
* @state: One of AR5K_LED_*
*
* Used to set the LED blinking state. This only
* works for the LED connected to the LED_0, LED_1 pins,
* not the GPIO based.
*/
void
ath5k_hw_set_ledstate(struct ath5k_hw *ah, unsigned int state)
{
u32 led;
/*5210 has different led mode handling*/
u32 led_5210;
/*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);
}
/**
* ath5k_hw_set_gpio_input() - Set GPIO inputs
* @ah: The &struct ath5k_hw
* @gpio: GPIO pin to set as input
*/
int
ath5k_hw_set_gpio_input(struct ath5k_hw *ah, u32 gpio)
{
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;
}
/**
* ath5k_hw_set_gpio_output() - Set GPIO outputs
* @ah: The &struct ath5k_hw
* @gpio: The GPIO pin to set as output
*/
int
ath5k_hw_set_gpio_output(struct ath5k_hw *ah, u32 gpio)
{
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;
}
/**
* ath5k_hw_get_gpio() - Get GPIO state
* @ah: The &struct ath5k_hw
* @gpio: The GPIO pin to read
*/
u32
ath5k_hw_get_gpio(struct ath5k_hw *ah, u32 gpio)
{
if (gpio >= AR5K_NUM_GPIO)
return 0xffffffff;
/* GPIO input magic */
return ((ath5k_hw_reg_read(ah, AR5K_GPIODI) & AR5K_GPIODI_M) >> gpio) &
0x1;
}
/**
* ath5k_hw_set_gpio() - Set GPIO state
* @ah: The &struct ath5k_hw
* @gpio: The GPIO pin to set
* @val: Value to set (boolean)
*/
int
ath5k_hw_set_gpio(struct ath5k_hw *ah, u32 gpio, u32 val)
{
u32 data;
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;
}
/**
* ath5k_hw_set_gpio_intr() - Initialize the GPIO interrupt (RFKill switch)
* @ah: The &struct ath5k_hw
* @gpio: The GPIO pin to use
* @interrupt_level: True to generate interrupt on active pin (high)
*
* This function is used to set up the GPIO interrupt for the hw RFKill switch.
* That switch is connected to a GPIO pin and it's number is stored on EEPROM.
* It can either open or close the circuit to indicate that we should disable
* RF/Wireless to save power (we also get that from EEPROM).
*/
void
ath5k_hw_set_gpio_intr(struct ath5k_hw *ah, unsigned int gpio,
u32 interrupt_level)
{
u32 data;
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);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,204 @@
/*
* Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
* Copyright (c) 2004-2005 Atheros Communications, Inc.
* Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com>
* Copyright (c) 2009 Bob Copeland <me@bobcopeland.com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
* redistribution must be conditioned upon including a substantially
* similar Disclaimer requirement for further binary redistribution.
* 3. Neither the names of the above-listed copyright holders nor the names
* of any contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGES.
*
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/pci.h>
#include "ath5k.h"
#define ATH_SDEVICE(subv, subd) \
.vendor = PCI_ANY_ID, .device = PCI_ANY_ID, \
.subvendor = (subv), .subdevice = (subd)
#define ATH_LED(pin, polarity) .driver_data = (((pin) << 8) | (polarity))
#define ATH_PIN(data) ((data) >> 8)
#define ATH_POLARITY(data) ((data) & 0xff)
/* Devices we match on for LED config info (typically laptops) */
static const struct pci_device_id ath5k_led_devices[] = {
/* AR5211 */
{ PCI_VDEVICE(ATHEROS, PCI_DEVICE_ID_ATHEROS_AR5211), ATH_LED(0, 0) },
/* HP Compaq nc6xx, nc4000, nx6000 */
{ ATH_SDEVICE(PCI_VENDOR_ID_COMPAQ, PCI_ANY_ID), ATH_LED(1, 1) },
/* Acer Aspire One A150 (maximlevitsky@gmail.com) */
{ ATH_SDEVICE(PCI_VENDOR_ID_FOXCONN, 0xe008), ATH_LED(3, 0) },
/* Acer Aspire One AO531h AO751h (keng-yu.lin@canonical.com) */
{ ATH_SDEVICE(PCI_VENDOR_ID_FOXCONN, 0xe00d), ATH_LED(3, 0) },
/* Acer Ferrari 5000 (russ.dill@gmail.com) */
{ ATH_SDEVICE(PCI_VENDOR_ID_AMBIT, 0x0422), ATH_LED(1, 1) },
/* E-machines E510 (tuliom@gmail.com) */
{ ATH_SDEVICE(PCI_VENDOR_ID_AMBIT, 0x0428), ATH_LED(3, 0) },
/* BenQ Joybook R55v (nowymarluk@wp.pl) */
{ ATH_SDEVICE(PCI_VENDOR_ID_QMI, 0x0100), ATH_LED(1, 0) },
/* Acer Extensa 5620z (nekoreeve@gmail.com) */
{ ATH_SDEVICE(PCI_VENDOR_ID_QMI, 0x0105), ATH_LED(3, 0) },
/* Fukato Datacask Jupiter 1014a (mrb74@gmx.at) */
{ ATH_SDEVICE(PCI_VENDOR_ID_AZWAVE, 0x1026), ATH_LED(3, 0) },
/* IBM ThinkPad AR5BXB6 (legovini@spiro.fisica.unipd.it) */
{ ATH_SDEVICE(PCI_VENDOR_ID_IBM, 0x058a), ATH_LED(1, 0) },
/* HP Compaq CQ60-206US (ddreggors@jumptv.com) */
{ ATH_SDEVICE(PCI_VENDOR_ID_HP, 0x0137a), ATH_LED(3, 1) },
/* HP Compaq C700 (nitrousnrg@gmail.com) */
{ ATH_SDEVICE(PCI_VENDOR_ID_HP, 0x0137b), ATH_LED(3, 0) },
/* LiteOn AR5BXB63 (magooz@salug.it) */
{ ATH_SDEVICE(PCI_VENDOR_ID_ATHEROS, 0x3067), ATH_LED(3, 0) },
/* IBM-specific AR5212 (all others) */
{ PCI_VDEVICE(ATHEROS, PCI_DEVICE_ID_ATHEROS_AR5212_IBM), ATH_LED(0, 0) },
/* Dell Vostro A860 (shahar@shahar-or.co.il) */
{ ATH_SDEVICE(PCI_VENDOR_ID_QMI, 0x0112), ATH_LED(3, 0) },
{ }
};
void ath5k_led_enable(struct ath5k_hw *ah)
{
if (IS_ENABLED(CONFIG_MAC80211_LEDS) &&
test_bit(ATH_STAT_LEDSOFT, ah->status)) {
ath5k_hw_set_gpio_output(ah, ah->led_pin);
ath5k_led_off(ah);
}
}
static void ath5k_led_on(struct ath5k_hw *ah)
{
if (!test_bit(ATH_STAT_LEDSOFT, ah->status))
return;
ath5k_hw_set_gpio(ah, ah->led_pin, ah->led_on);
}
void ath5k_led_off(struct ath5k_hw *ah)
{
if (!IS_ENABLED(CONFIG_MAC80211_LEDS) ||
!test_bit(ATH_STAT_LEDSOFT, ah->status))
return;
ath5k_hw_set_gpio(ah, ah->led_pin, !ah->led_on);
}
static void
ath5k_led_brightness_set(struct led_classdev *led_dev,
enum led_brightness brightness)
{
struct ath5k_led *led = container_of(led_dev, struct ath5k_led,
led_dev);
if (brightness == LED_OFF)
ath5k_led_off(led->ah);
else
ath5k_led_on(led->ah);
}
static int
ath5k_register_led(struct ath5k_hw *ah, struct ath5k_led *led,
const char *name, const char *trigger)
{
int err;
led->ah = ah;
strscpy(led->name, name, sizeof(led->name));
led->led_dev.name = led->name;
led->led_dev.default_trigger = trigger;
led->led_dev.brightness_set = ath5k_led_brightness_set;
err = led_classdev_register(ah->dev, &led->led_dev);
if (err) {
ATH5K_WARN(ah, "could not register LED %s\n", name);
led->ah = NULL;
}
return err;
}
static void
ath5k_unregister_led(struct ath5k_led *led)
{
if (!IS_ENABLED(CONFIG_MAC80211_LEDS) || !led->ah)
return;
led_classdev_unregister(&led->led_dev);
ath5k_led_off(led->ah);
led->ah = NULL;
}
void ath5k_unregister_leds(struct ath5k_hw *ah)
{
ath5k_unregister_led(&ah->rx_led);
ath5k_unregister_led(&ah->tx_led);
}
int ath5k_init_leds(struct ath5k_hw *ah)
{
int ret = 0;
struct ieee80211_hw *hw = ah->hw;
#ifndef CONFIG_ATH5K_AHB
struct pci_dev *pdev = ah->pdev;
#endif
char name[ATH5K_LED_MAX_NAME_LEN + 1];
const struct pci_device_id *match;
if (!IS_ENABLED(CONFIG_MAC80211_LEDS) || !ah->pdev)
return 0;
#ifdef CONFIG_ATH5K_AHB
match = NULL;
#else
match = pci_match_id(&ath5k_led_devices[0], pdev);
#endif
if (match) {
__set_bit(ATH_STAT_LEDSOFT, ah->status);
ah->led_pin = ATH_PIN(match->driver_data);
ah->led_on = ATH_POLARITY(match->driver_data);
}
if (!test_bit(ATH_STAT_LEDSOFT, ah->status))
goto out;
ath5k_led_enable(ah);
snprintf(name, sizeof(name), "ath5k-%s::rx", wiphy_name(hw->wiphy));
ret = ath5k_register_led(ah, &ah->rx_led, name,
ieee80211_get_rx_led_name(hw));
if (ret)
goto out;
snprintf(name, sizeof(name), "ath5k-%s::tx", wiphy_name(hw->wiphy));
ret = ath5k_register_led(ah, &ah->tx_led, name,
ieee80211_get_tx_led_name(hw));
out:
return ret;
}
@@ -0,0 +1,829 @@
/*-
* Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
* Copyright (c) 2004-2005 Atheros Communications, Inc.
* Copyright (c) 2006 Devicescape Software, Inc.
* Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com>
* Copyright (c) 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
* Copyright (c) 2010 Bruno Randolf <br1@einfach.org>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
* redistribution must be conditioned upon including a substantially
* similar Disclaimer requirement for further binary redistribution.
* 3. Neither the names of the above-listed copyright holders nor the names
* of any contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGES.
*
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <net/mac80211.h>
#include <linux/unaligned.h>
#include "ath5k.h"
#include "base.h"
#include "reg.h"
/********************\
* Mac80211 functions *
\********************/
static void
ath5k_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
struct sk_buff *skb)
{
struct ath5k_hw *ah = hw->priv;
u16 qnum = skb_get_queue_mapping(skb);
if (WARN_ON(qnum >= ah->ah_capabilities.cap_queues.q_tx_num)) {
ieee80211_free_txskb(hw, skb);
return;
}
ath5k_tx_queue(hw, skb, &ah->txqs[qnum], control);
}
static int
ath5k_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
struct ath5k_hw *ah = hw->priv;
int ret;
struct ath5k_vif *avf = (void *)vif->drv_priv;
mutex_lock(&ah->lock);
if ((vif->type == NL80211_IFTYPE_AP ||
vif->type == NL80211_IFTYPE_ADHOC)
&& (ah->num_ap_vifs + ah->num_adhoc_vifs) >= ATH_BCBUF) {
ret = -ELNRNG;
goto end;
}
/* Don't allow other interfaces if one ad-hoc is configured.
* TODO: Fix the problems with ad-hoc and multiple other interfaces.
* We would need to operate the HW in ad-hoc mode to allow TSF updates
* for the IBSS, but this breaks with additional AP or STA interfaces
* at the moment. */
if (ah->num_adhoc_vifs ||
(ah->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) {
ATH5K_ERR(ah, "Only one single ad-hoc interface is allowed.\n");
ret = -ELNRNG;
goto end;
}
switch (vif->type) {
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_MESH_POINT:
avf->opmode = vif->type;
break;
default:
ret = -EOPNOTSUPP;
goto end;
}
ah->nvifs++;
ATH5K_DBG(ah, ATH5K_DEBUG_MODE, "add interface mode %d\n", avf->opmode);
/* Assign the vap/adhoc to a beacon xmit slot. */
if ((avf->opmode == NL80211_IFTYPE_AP) ||
(avf->opmode == NL80211_IFTYPE_ADHOC) ||
(avf->opmode == NL80211_IFTYPE_MESH_POINT)) {
int slot;
WARN_ON(list_empty(&ah->bcbuf));
avf->bbuf = list_first_entry(&ah->bcbuf, struct ath5k_buf,
list);
list_del(&avf->bbuf->list);
avf->bslot = 0;
for (slot = 0; slot < ATH_BCBUF; slot++) {
if (!ah->bslot[slot]) {
avf->bslot = slot;
break;
}
}
BUG_ON(ah->bslot[avf->bslot] != NULL);
ah->bslot[avf->bslot] = vif;
if (avf->opmode == NL80211_IFTYPE_AP)
ah->num_ap_vifs++;
else if (avf->opmode == NL80211_IFTYPE_ADHOC)
ah->num_adhoc_vifs++;
else if (avf->opmode == NL80211_IFTYPE_MESH_POINT)
ah->num_mesh_vifs++;
}
/* Any MAC address is fine, all others are included through the
* filter.
*/
ath5k_hw_set_lladdr(ah, vif->addr);
ath5k_update_bssid_mask_and_opmode(ah, vif);
ret = 0;
end:
mutex_unlock(&ah->lock);
return ret;
}
static void
ath5k_remove_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
struct ath5k_hw *ah = hw->priv;
struct ath5k_vif *avf = (void *)vif->drv_priv;
unsigned int i;
mutex_lock(&ah->lock);
ah->nvifs--;
if (avf->bbuf) {
ath5k_txbuf_free_skb(ah, avf->bbuf);
list_add_tail(&avf->bbuf->list, &ah->bcbuf);
for (i = 0; i < ATH_BCBUF; i++) {
if (ah->bslot[i] == vif) {
ah->bslot[i] = NULL;
break;
}
}
avf->bbuf = NULL;
}
if (avf->opmode == NL80211_IFTYPE_AP)
ah->num_ap_vifs--;
else if (avf->opmode == NL80211_IFTYPE_ADHOC)
ah->num_adhoc_vifs--;
else if (avf->opmode == NL80211_IFTYPE_MESH_POINT)
ah->num_mesh_vifs--;
ath5k_update_bssid_mask_and_opmode(ah, NULL);
mutex_unlock(&ah->lock);
}
/*
* TODO: Phy disable/diversity etc
*/
static int
ath5k_config(struct ieee80211_hw *hw, int radio_idx, u32 changed)
{
struct ath5k_hw *ah = hw->priv;
struct ieee80211_conf *conf = &hw->conf;
int ret = 0;
int i;
mutex_lock(&ah->lock);
if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
ret = ath5k_chan_set(ah, &conf->chandef);
if (ret < 0)
goto unlock;
}
if ((changed & IEEE80211_CONF_CHANGE_POWER) &&
(ah->ah_txpower.txp_requested != conf->power_level)) {
ah->ah_txpower.txp_requested = conf->power_level;
/* Half dB steps */
ath5k_hw_set_txpower_limit(ah, (conf->power_level * 2));
}
if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) {
ah->ah_retry_long = conf->long_frame_max_tx_count;
ah->ah_retry_short = conf->short_frame_max_tx_count;
for (i = 0; i < ah->ah_capabilities.cap_queues.q_tx_num; i++)
ath5k_hw_set_tx_retry_limits(ah, i);
}
/* TODO:
* 1) Move this on config_interface and handle each case
* separately eg. when we have only one STA vif, use
* AR5K_ANTMODE_SINGLE_AP
*
* 2) Allow the user to change antenna mode eg. when only
* one antenna is present
*
* 3) Allow the user to set default/tx antenna when possible
*
* 4) Default mode should handle 90% of the cases, together
* with fixed a/b and single AP modes we should be able to
* handle 99%. Sectored modes are extreme cases and i still
* haven't found a usage for them. If we decide to support them,
* then we must allow the user to set how many tx antennas we
* have available
*/
ath5k_hw_set_antenna_mode(ah, ah->ah_ant_mode);
unlock:
mutex_unlock(&ah->lock);
return ret;
}
static void
ath5k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf, u64 changes)
{
struct ath5k_vif *avf = (void *)vif->drv_priv;
struct ath5k_hw *ah = hw->priv;
struct ath_common *common = ath5k_hw_common(ah);
mutex_lock(&ah->lock);
if (changes & BSS_CHANGED_BSSID) {
/* Cache for later use during resets */
memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
common->curaid = 0;
ath5k_hw_set_bssid(ah);
}
if (changes & BSS_CHANGED_BEACON_INT)
ah->bintval = bss_conf->beacon_int;
if (changes & BSS_CHANGED_ERP_SLOT) {
int slot_time;
ah->ah_short_slot = bss_conf->use_short_slot;
slot_time = ath5k_hw_get_default_slottime(ah) +
3 * ah->ah_coverage_class;
ath5k_hw_set_ifs_intervals(ah, slot_time);
}
if (changes & BSS_CHANGED_ASSOC) {
avf->assoc = vif->cfg.assoc;
if (vif->cfg.assoc)
ah->assoc = vif->cfg.assoc;
else
ah->assoc = ath5k_any_vif_assoc(ah);
if (ah->opmode == NL80211_IFTYPE_STATION)
ath5k_set_beacon_filter(hw, ah->assoc);
ath5k_hw_set_ledstate(ah, ah->assoc ?
AR5K_LED_ASSOC : AR5K_LED_INIT);
if (vif->cfg.assoc) {
ATH5K_DBG(ah, ATH5K_DEBUG_ANY,
"Bss Info ASSOC %d, bssid: %pM\n",
vif->cfg.aid, common->curbssid);
common->curaid = vif->cfg.aid;
ath5k_hw_set_bssid(ah);
/* Once ANI is available you would start it here */
}
}
if (changes & BSS_CHANGED_BEACON) {
spin_lock_bh(&ah->block);
ath5k_beacon_update(hw, vif);
spin_unlock_bh(&ah->block);
}
if (changes & BSS_CHANGED_BEACON_ENABLED)
ah->enable_beacon = bss_conf->enable_beacon;
if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED |
BSS_CHANGED_BEACON_INT))
ath5k_beacon_config(ah);
mutex_unlock(&ah->lock);
}
static u64
ath5k_prepare_multicast(struct ieee80211_hw *hw,
struct netdev_hw_addr_list *mc_list)
{
u32 mfilt[2], val;
u8 pos;
struct netdev_hw_addr *ha;
mfilt[0] = 0;
mfilt[1] = 0;
netdev_hw_addr_list_for_each(ha, mc_list) {
/* calculate XOR of eight 6-bit values */
val = get_unaligned_le32(ha->addr + 0);
pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
val = get_unaligned_le32(ha->addr + 3);
pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
pos &= 0x3f;
mfilt[pos / 32] |= (1 << (pos % 32));
/* XXX: we might be able to just do this instead,
* but not sure, needs testing, if we do use this we'd
* need to inform below not to reset the mcast */
/* ath5k_hw_set_mcast_filterindex(ah,
* ha->addr[5]); */
}
return ((u64)(mfilt[1]) << 32) | mfilt[0];
}
/*
* o always accept unicast, broadcast, and multicast traffic
* o multicast traffic for all BSSIDs will be enabled if mac80211
* says it should be
* o maintain current state of phy ofdm or phy cck error reception.
* If the hardware detects any of these type of errors then
* ath5k_hw_get_rx_filter() will pass to us the respective
* hardware filters to be able to receive these type of frames.
* o probe request frames are accepted only when operating in
* hostap, adhoc, or monitor modes
* o enable promiscuous mode according to the interface state
* o accept beacons:
* - when operating in adhoc mode so the 802.11 layer creates
* node table entries for peers,
* - when operating in station mode for collecting rssi data when
* the station is otherwise quiet, or
* - when scanning
*/
static void
ath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,
unsigned int *new_flags, u64 multicast)
{
#define SUPPORTED_FIF_FLAGS \
(FIF_ALLMULTI | FIF_FCSFAIL | \
FIF_PLCPFAIL | FIF_CONTROL | FIF_OTHER_BSS | \
FIF_BCN_PRBRESP_PROMISC)
struct ath5k_hw *ah = hw->priv;
u32 mfilt[2], rfilt;
struct ath5k_vif_iter_data iter_data; /* to count STA interfaces */
mutex_lock(&ah->lock);
mfilt[0] = multicast;
mfilt[1] = multicast >> 32;
/* Only deal with supported flags */
*new_flags &= SUPPORTED_FIF_FLAGS;
/* If HW detects any phy or radar errors, leave those filters on.
* Also, always enable Unicast, Broadcasts and Multicast
* XXX: move unicast, bssid broadcasts and multicast to mac80211 */
rfilt = (ath5k_hw_get_rx_filter(ah) & (AR5K_RX_FILTER_PHYERR)) |
(AR5K_RX_FILTER_UCAST | AR5K_RX_FILTER_BCAST |
AR5K_RX_FILTER_MCAST);
/* Note, AR5K_RX_FILTER_MCAST is already enabled */
if (*new_flags & FIF_ALLMULTI) {
mfilt[0] = ~0;
mfilt[1] = ~0;
}
/* This is the best we can do */
if (*new_flags & (FIF_FCSFAIL | FIF_PLCPFAIL))
rfilt |= AR5K_RX_FILTER_PHYERR;
/* FIF_BCN_PRBRESP_PROMISC really means to enable beacons
* and probes for any BSSID */
if ((*new_flags & FIF_BCN_PRBRESP_PROMISC) || (ah->nvifs > 1))
rfilt |= AR5K_RX_FILTER_BEACON;
/* FIF_CONTROL doc says we should only pass on control frames for this
* station. This needs testing. I believe right now this
* enables *all* control frames, which is OK.. but
* we should see if we can improve on granularity */
if (*new_flags & FIF_CONTROL)
rfilt |= AR5K_RX_FILTER_CONTROL;
/* Additional settings per mode -- this is per ath5k */
/* XXX move these to mac80211, and add a beacon IFF flag to mac80211 */
switch (ah->opmode) {
case NL80211_IFTYPE_MESH_POINT:
rfilt |= AR5K_RX_FILTER_CONTROL |
AR5K_RX_FILTER_BEACON |
AR5K_RX_FILTER_PROBEREQ |
AR5K_RX_FILTER_PROM;
break;
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_ADHOC:
rfilt |= AR5K_RX_FILTER_PROBEREQ |
AR5K_RX_FILTER_BEACON;
break;
case NL80211_IFTYPE_STATION:
if (ah->assoc)
rfilt |= AR5K_RX_FILTER_BEACON;
break;
default:
break;
}
iter_data.hw_macaddr = NULL;
iter_data.n_stas = 0;
iter_data.need_set_hw_addr = false;
ieee80211_iterate_active_interfaces_atomic(
ah->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
ath5k_vif_iter, &iter_data);
/* Set up RX Filter */
if (iter_data.n_stas > 1) {
/* If you have multiple STA interfaces connected to
* different APs, ARPs are not received (most of the time?)
* Enabling PROMISC appears to fix that problem.
*/
rfilt |= AR5K_RX_FILTER_PROM;
}
/* Set filters */
ath5k_hw_set_rx_filter(ah, rfilt);
/* Set multicast bits */
ath5k_hw_set_mcast_filter(ah, mfilt[0], mfilt[1]);
/* Set the cached hw filter flags, this will later actually
* be set in HW */
ah->filter_flags = rfilt;
/* Store current FIF filter flags */
ah->fif_filter_flags = *new_flags;
mutex_unlock(&ah->lock);
}
static int
ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct ieee80211_vif *vif, struct ieee80211_sta *sta,
struct ieee80211_key_conf *key)
{
struct ath5k_hw *ah = hw->priv;
struct ath_common *common = ath5k_hw_common(ah);
int ret = 0;
if (ath5k_modparam_nohwcrypt)
return -EOPNOTSUPP;
if (key->flags & IEEE80211_KEY_FLAG_RX_MGMT)
return -EOPNOTSUPP;
if (vif->type == NL80211_IFTYPE_ADHOC &&
(key->cipher == WLAN_CIPHER_SUITE_TKIP ||
key->cipher == WLAN_CIPHER_SUITE_CCMP) &&
!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
/* don't program group keys when using IBSS_RSN */
return -EOPNOTSUPP;
}
switch (key->cipher) {
case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
case WLAN_CIPHER_SUITE_TKIP:
break;
case WLAN_CIPHER_SUITE_CCMP:
if (common->crypt_caps & ATH_CRYPT_CAP_CIPHER_AESCCM)
break;
return -EOPNOTSUPP;
default:
return -EOPNOTSUPP;
}
mutex_lock(&ah->lock);
switch (cmd) {
case SET_KEY:
ret = ath_key_config(common, vif, sta, key);
if (ret >= 0) {
key->hw_key_idx = ret;
/* push IV and Michael MIC generation to stack */
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
if (key->cipher == WLAN_CIPHER_SUITE_CCMP)
key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
ret = 0;
}
break;
case DISABLE_KEY:
ath_key_delete(common, key->hw_key_idx);
break;
default:
ret = -EINVAL;
}
mutex_unlock(&ah->lock);
return ret;
}
static void
ath5k_sw_scan_start(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
const u8 *mac_addr)
{
struct ath5k_hw *ah = hw->priv;
if (!ah->assoc)
ath5k_hw_set_ledstate(ah, AR5K_LED_SCAN);
}
static void
ath5k_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
struct ath5k_hw *ah = hw->priv;
ath5k_hw_set_ledstate(ah, ah->assoc ?
AR5K_LED_ASSOC : AR5K_LED_INIT);
}
static int
ath5k_get_stats(struct ieee80211_hw *hw,
struct ieee80211_low_level_stats *stats)
{
struct ath5k_hw *ah = hw->priv;
/* Force update */
ath5k_hw_update_mib_counters(ah);
stats->dot11ACKFailureCount = ah->stats.ack_fail;
stats->dot11RTSFailureCount = ah->stats.rts_fail;
stats->dot11RTSSuccessCount = ah->stats.rts_ok;
stats->dot11FCSErrorCount = ah->stats.fcs_error;
return 0;
}
static int
ath5k_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
unsigned int link_id, u16 queue,
const struct ieee80211_tx_queue_params *params)
{
struct ath5k_hw *ah = hw->priv;
struct ath5k_txq_info qi;
int ret = 0;
if (queue >= ah->ah_capabilities.cap_queues.q_tx_num)
return 0;
mutex_lock(&ah->lock);
ath5k_hw_get_tx_queueprops(ah, queue, &qi);
qi.tqi_aifs = params->aifs;
qi.tqi_cw_min = params->cw_min;
qi.tqi_cw_max = params->cw_max;
qi.tqi_burst_time = params->txop * 32;
ATH5K_DBG(ah, ATH5K_DEBUG_ANY,
"Configure tx [queue %d], "
"aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
queue, params->aifs, params->cw_min,
params->cw_max, params->txop);
if (ath5k_hw_set_tx_queueprops(ah, queue, &qi)) {
ATH5K_ERR(ah,
"Unable to update hardware queue %u!\n", queue);
ret = -EIO;
} else
ath5k_hw_reset_tx_queue(ah, queue);
mutex_unlock(&ah->lock);
return ret;
}
static u64
ath5k_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
struct ath5k_hw *ah = hw->priv;
return ath5k_hw_get_tsf64(ah);
}
static void
ath5k_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u64 tsf)
{
struct ath5k_hw *ah = hw->priv;
ath5k_hw_set_tsf64(ah, tsf);
}
static void
ath5k_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
struct ath5k_hw *ah = hw->priv;
/*
* in IBSS mode we need to update the beacon timers too.
* this will also reset the TSF if we call it with 0
*/
if (ah->opmode == NL80211_IFTYPE_ADHOC)
ath5k_beacon_update_timers(ah, 0);
else
ath5k_hw_reset_tsf(ah);
}
static int
ath5k_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey)
{
struct ath5k_hw *ah = hw->priv;
struct ieee80211_conf *conf = &hw->conf;
struct ath_common *common = ath5k_hw_common(ah);
struct ath_cycle_counters *cc = &common->cc_survey;
unsigned int div = common->clockrate * 1000;
if (idx != 0)
return -ENOENT;
spin_lock_bh(&common->cc_lock);
ath_hw_cycle_counters_update(common);
if (cc->cycles > 0) {
ah->survey.time += cc->cycles / div;
ah->survey.time_busy += cc->rx_busy / div;
ah->survey.time_rx += cc->rx_frame / div;
ah->survey.time_tx += cc->tx_frame / div;
}
memset(cc, 0, sizeof(*cc));
spin_unlock_bh(&common->cc_lock);
memcpy(survey, &ah->survey, sizeof(*survey));
survey->channel = conf->chandef.chan;
survey->noise = ah->ah_noise_floor;
survey->filled = SURVEY_INFO_NOISE_DBM |
SURVEY_INFO_IN_USE |
SURVEY_INFO_TIME |
SURVEY_INFO_TIME_BUSY |
SURVEY_INFO_TIME_RX |
SURVEY_INFO_TIME_TX;
return 0;
}
/**
* ath5k_set_coverage_class - Set IEEE 802.11 coverage class
*
* @hw: struct ieee80211_hw pointer
* @radio_idx: Radio index
* @coverage_class: IEEE 802.11 coverage class number
*
* Mac80211 callback. Sets slot time, ACK timeout and CTS timeout for given
* coverage class. The values are persistent, they are restored after device
* reset.
*/
static void
ath5k_set_coverage_class(struct ieee80211_hw *hw, int radio_idx,
s16 coverage_class)
{
struct ath5k_hw *ah = hw->priv;
mutex_lock(&ah->lock);
ath5k_hw_set_coverage_class(ah, coverage_class);
mutex_unlock(&ah->lock);
}
static int
ath5k_set_antenna(struct ieee80211_hw *hw, int radio_idx, u32 tx_ant,
u32 rx_ant)
{
struct ath5k_hw *ah = hw->priv;
if (tx_ant == 1 && rx_ant == 1)
ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_FIXED_A);
else if (tx_ant == 2 && rx_ant == 2)
ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_FIXED_B);
else if ((tx_ant & 3) == 3 && (rx_ant & 3) == 3)
ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_DEFAULT);
else
return -EINVAL;
return 0;
}
static int
ath5k_get_antenna(struct ieee80211_hw *hw, int radio_idx,
u32 *tx_ant, u32 *rx_ant)
{
struct ath5k_hw *ah = hw->priv;
switch (ah->ah_ant_mode) {
case AR5K_ANTMODE_FIXED_A:
*tx_ant = 1; *rx_ant = 1; break;
case AR5K_ANTMODE_FIXED_B:
*tx_ant = 2; *rx_ant = 2; break;
case AR5K_ANTMODE_DEFAULT:
*tx_ant = 3; *rx_ant = 3; break;
}
return 0;
}
static void ath5k_get_ringparam(struct ieee80211_hw *hw,
u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max)
{
struct ath5k_hw *ah = hw->priv;
*tx = ah->txqs[AR5K_TX_QUEUE_ID_DATA_MIN].txq_max;
*tx_max = ATH5K_TXQ_LEN_MAX;
*rx = *rx_max = ATH_RXBUF;
}
static int ath5k_set_ringparam(struct ieee80211_hw *hw, u32 tx, u32 rx)
{
struct ath5k_hw *ah = hw->priv;
u16 qnum;
/* only support setting tx ring size for now */
if (rx != ATH_RXBUF)
return -EINVAL;
/* restrict tx ring size min/max */
if (!tx || tx > ATH5K_TXQ_LEN_MAX)
return -EINVAL;
for (qnum = 0; qnum < ARRAY_SIZE(ah->txqs); qnum++) {
if (!ah->txqs[qnum].setup)
continue;
if (ah->txqs[qnum].qnum < AR5K_TX_QUEUE_ID_DATA_MIN ||
ah->txqs[qnum].qnum > AR5K_TX_QUEUE_ID_DATA_MAX)
continue;
ah->txqs[qnum].txq_max = tx;
if (ah->txqs[qnum].txq_len >= ah->txqs[qnum].txq_max)
ieee80211_stop_queue(hw, ah->txqs[qnum].qnum);
}
return 0;
}
const struct ieee80211_ops ath5k_hw_ops = {
.add_chanctx = ieee80211_emulate_add_chanctx,
.remove_chanctx = ieee80211_emulate_remove_chanctx,
.change_chanctx = ieee80211_emulate_change_chanctx,
.switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx,
.tx = ath5k_tx,
.wake_tx_queue = ieee80211_handle_wake_tx_queue,
.start = ath5k_start,
.stop = ath5k_stop,
.add_interface = ath5k_add_interface,
/* .change_interface = not implemented */
.remove_interface = ath5k_remove_interface,
.config = ath5k_config,
.bss_info_changed = ath5k_bss_info_changed,
.prepare_multicast = ath5k_prepare_multicast,
.configure_filter = ath5k_configure_filter,
/* .set_tim = not implemented */
.set_key = ath5k_set_key,
/* .update_tkip_key = not implemented */
/* .hw_scan = not implemented */
.sw_scan_start = ath5k_sw_scan_start,
.sw_scan_complete = ath5k_sw_scan_complete,
.get_stats = ath5k_get_stats,
/* .set_frag_threshold = not implemented */
/* .set_rts_threshold = not implemented */
/* .sta_add = not implemented */
/* .sta_remove = not implemented */
/* .sta_notify = not implemented */
.conf_tx = ath5k_conf_tx,
.get_tsf = ath5k_get_tsf,
.set_tsf = ath5k_set_tsf,
.reset_tsf = ath5k_reset_tsf,
/* .tx_last_beacon = not implemented */
/* .ampdu_action = not needed */
.get_survey = ath5k_get_survey,
.set_coverage_class = ath5k_set_coverage_class,
/* .rfkill_poll = not implemented */
/* .flush = not implemented */
/* .channel_switch = not implemented */
/* .napi_poll = not implemented */
.set_antenna = ath5k_set_antenna,
.get_antenna = ath5k_get_antenna,
.set_ringparam = ath5k_set_ringparam,
.get_ringparam = ath5k_get_ringparam,
};
@@ -0,0 +1,343 @@
/*
* Copyright (c) 2008-2009 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/nl80211.h>
#include <linux/pci.h>
#include <linux/etherdevice.h>
#include <linux/module.h>
#include "../ath.h"
#include "ath5k.h"
#include "debug.h"
#include "base.h"
#include "reg.h"
/* Known PCI ids */
static const struct pci_device_id ath5k_pci_id_table[] = {
{ PCI_VDEVICE(ATHEROS, 0x0207) }, /* 5210 early */
{ PCI_VDEVICE(ATHEROS, 0x0007) }, /* 5210 */
{ PCI_VDEVICE(ATHEROS, 0x0011) }, /* 5311 - this is on AHB bus !*/
{ PCI_VDEVICE(ATHEROS, 0x0012) }, /* 5211 */
{ PCI_VDEVICE(ATHEROS, 0x0013) }, /* 5212 */
{ PCI_VDEVICE(3COM_2, 0x0013) }, /* 3com 5212 */
{ PCI_VDEVICE(3COM, 0x0013) }, /* 3com 3CRDAG675 5212 */
{ PCI_VDEVICE(ATHEROS, 0x1014) }, /* IBM minipci 5212 */
{ PCI_VDEVICE(ATHEROS, 0x0014) }, /* 5212 compatible */
{ PCI_VDEVICE(ATHEROS, 0x0015) }, /* 5212 compatible */
{ PCI_VDEVICE(ATHEROS, 0x0016) }, /* 5212 compatible */
{ PCI_VDEVICE(ATHEROS, 0x0017) }, /* 5212 compatible */
{ PCI_VDEVICE(ATHEROS, 0x0018) }, /* 5212 compatible */
{ PCI_VDEVICE(ATHEROS, 0x0019) }, /* 5212 compatible */
{ PCI_VDEVICE(ATHEROS, 0x001a) }, /* 2413 Griffin-lite */
{ PCI_VDEVICE(ATHEROS, 0x001b) }, /* 5413 Eagle */
{ PCI_VDEVICE(ATHEROS, 0x001c) }, /* PCI-E cards */
{ PCI_VDEVICE(ATHEROS, 0x001d) }, /* 2417 Nala */
{ PCI_VDEVICE(ATHEROS, 0xff16) }, /* Gigaset SX76[23] AR241[34]A */
{ PCI_VDEVICE(ATHEROS, 0xff1a) }, /* Arcadyan ARV45XX AR2417 */
{ PCI_VDEVICE(ATHEROS, 0xff1b) }, /* AR5BXB63 */
{ 0 }
};
MODULE_DEVICE_TABLE(pci, ath5k_pci_id_table);
/* return bus cachesize in 4B word units */
static void ath5k_pci_read_cachesize(struct ath_common *common, int *csz)
{
struct ath5k_hw *ah = common->priv;
u8 u8tmp;
pci_read_config_byte(ah->pdev, PCI_CACHE_LINE_SIZE, &u8tmp);
*csz = (int)u8tmp;
/*
* This check was put in to avoid "unpleasant" consequences if
* the bootrom has not fully initialized all PCI devices.
* Sometimes the cache line size register is not set
*/
if (*csz == 0)
*csz = L1_CACHE_BYTES >> 2; /* Use the default size */
}
/*
* Read from eeprom
*/
static bool
ath5k_pci_eeprom_read(struct ath_common *common, u32 offset, u16 *data)
{
struct ath5k_hw *ah = common->ah;
u32 status, timeout;
/*
* 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 false;
*data = (u16)(ath5k_hw_reg_read(ah, AR5K_EEPROM_DATA) &
0xffff);
return true;
}
usleep_range(15, 20);
}
return false;
}
int ath5k_hw_read_srev(struct ath5k_hw *ah)
{
ah->ah_mac_srev = ath5k_hw_reg_read(ah, AR5K_SREV);
return 0;
}
/*
* Read the MAC address from eeprom or platform_data
*/
static int ath5k_pci_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac)
{
u8 mac_d[ETH_ALEN] = {};
u32 total, offset;
u16 data;
int octet;
AR5K_EEPROM_READ(0x20, data);
for (offset = 0x1f, octet = 0, total = 0; offset >= 0x1d; offset--) {
AR5K_EEPROM_READ(offset, data);
total += data;
mac_d[octet + 1] = data & 0xff;
mac_d[octet] = data >> 8;
octet += 2;
}
if (!total || total == 3 * 0xffff)
return -EINVAL;
memcpy(mac, mac_d, ETH_ALEN);
return 0;
}
/* Common ath_bus_opts structure */
static const struct ath_bus_ops ath_pci_bus_ops = {
.ath_bus_type = ATH_PCI,
.read_cachesize = ath5k_pci_read_cachesize,
.eeprom_read = ath5k_pci_eeprom_read,
.eeprom_read_mac = ath5k_pci_eeprom_read_mac,
};
/********************\
* PCI Initialization *
\********************/
static int
ath5k_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
void __iomem *mem;
struct ath5k_hw *ah;
struct ieee80211_hw *hw;
int ret;
u8 csz;
/*
* L0s needs to be disabled on all ath5k cards.
*
* For distributions shipping with CONFIG_PCIEASPM (this will be enabled
* by default in the future in 2.6.36) this will also mean both L1 and
* L0s will be disabled when a pre 1.1 PCIe device is detected. We do
* know L1 works correctly even for all ath5k pre 1.1 PCIe devices
* though but cannot currently undue the effect of a blacklist, for
* details you can read pcie_aspm_sanity_check() and see how it adjusts
* the device link capability.
*
* It may be possible in the future to implement some PCI API to allow
* drivers to override blacklists for pre 1.1 PCIe but for now it is
* best to accept that both L0s and L1 will be disabled completely for
* distributions shipping with CONFIG_PCIEASPM rather than having this
* issue present. Motivation for adding this new API will be to help
* with power consumption for some of these devices.
*/
pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S);
ret = pci_enable_device(pdev);
if (ret) {
dev_err(&pdev->dev, "can't enable device\n");
goto err;
}
/* XXX 32-bit addressing only */
ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
if (ret) {
dev_err(&pdev->dev, "32-bit DMA not available\n");
goto err_dis;
}
/*
* Cache line size is used to size and align various
* structures used to communicate with the hardware.
*/
pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &csz);
if (csz == 0) {
/*
* Linux 2.4.18 (at least) writes the cache line size
* register as a 16-bit wide register which is wrong.
* We must have this setup properly for rx buffer
* DMA to work so force a reasonable value here if it
* comes up zero.
*/
csz = L1_CACHE_BYTES >> 2;
pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, csz);
}
/*
* The default setting of latency timer yields poor results,
* set it to the value used by other systems. It may be worth
* tweaking this setting more.
*/
pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8);
/* Enable bus mastering */
pci_set_master(pdev);
/*
* Disable the RETRY_TIMEOUT register (0x41) to keep
* PCI Tx retries from interfering with C3 CPU state.
*/
pci_write_config_byte(pdev, 0x41, 0);
ret = pci_request_region(pdev, 0, "ath5k");
if (ret) {
dev_err(&pdev->dev, "cannot reserve PCI memory region\n");
goto err_dis;
}
mem = pci_iomap(pdev, 0, 0);
if (!mem) {
dev_err(&pdev->dev, "cannot remap PCI memory region\n");
ret = -EIO;
goto err_reg;
}
/*
* Allocate hw (mac80211 main struct)
* and hw->priv (driver private data)
*/
hw = ieee80211_alloc_hw(sizeof(*ah), &ath5k_hw_ops);
if (hw == NULL) {
dev_err(&pdev->dev, "cannot allocate ieee80211_hw\n");
ret = -ENOMEM;
goto err_map;
}
dev_info(&pdev->dev, "registered as '%s'\n", wiphy_name(hw->wiphy));
ah = hw->priv;
ah->hw = hw;
ah->pdev = pdev;
ah->dev = &pdev->dev;
ah->irq = pdev->irq;
ah->devid = id->device;
ah->iobase = mem; /* So we can unmap it on detach */
/* Initialize */
ret = ath5k_init_ah(ah, &ath_pci_bus_ops);
if (ret)
goto err_free;
/* Set private data */
pci_set_drvdata(pdev, hw);
return 0;
err_free:
ieee80211_free_hw(hw);
err_map:
pci_iounmap(pdev, mem);
err_reg:
pci_release_region(pdev, 0);
err_dis:
pci_disable_device(pdev);
err:
return ret;
}
static void
ath5k_pci_remove(struct pci_dev *pdev)
{
struct ieee80211_hw *hw = pci_get_drvdata(pdev);
struct ath5k_hw *ah = hw->priv;
ath5k_deinit_ah(ah);
pci_iounmap(pdev, ah->iobase);
pci_release_region(pdev, 0);
pci_disable_device(pdev);
ieee80211_free_hw(hw);
}
#ifdef CONFIG_PM_SLEEP
static int ath5k_pci_suspend(struct device *dev)
{
struct ieee80211_hw *hw = dev_get_drvdata(dev);
struct ath5k_hw *ah = hw->priv;
ath5k_led_off(ah);
return 0;
}
static int ath5k_pci_resume(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct ieee80211_hw *hw = pci_get_drvdata(pdev);
struct ath5k_hw *ah = hw->priv;
/*
* Suspend/Resume resets the PCI configuration space, so we have to
* re-disable the RETRY_TIMEOUT register (0x41) to keep
* PCI Tx retries from interfering with C3 CPU state
*/
pci_write_config_byte(pdev, 0x41, 0);
ath5k_led_enable(ah);
return 0;
}
static SIMPLE_DEV_PM_OPS(ath5k_pm_ops, ath5k_pci_suspend, ath5k_pci_resume);
#define ATH5K_PM_OPS (&ath5k_pm_ops)
#else
#define ATH5K_PM_OPS NULL
#endif /* CONFIG_PM_SLEEP */
static struct pci_driver ath5k_pci_driver = {
.name = KBUILD_MODNAME,
.id_table = ath5k_pci_id_table,
.probe = ath5k_pci_probe,
.remove = ath5k_pci_remove,
.driver.pm = ATH5K_PM_OPS,
};
module_pci_driver(ath5k_pci_driver);
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,729 @@
/*
* Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@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.
*
*/
/********************************************\
Queue Control Unit, DCF Control Unit Functions
\********************************************/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
#include <linux/log2.h>
/**
* DOC: Queue Control Unit (QCU)/DCF Control Unit (DCU) functions
*
* Here we setup parameters for the 12 available TX queues. Note that
* on the various registers we can usually only map the first 10 of them so
* basically we have 10 queues to play with. Each queue has a matching
* QCU that controls when the queue will get triggered and multiple QCUs
* can be mapped to a single DCU that controls the various DFS parameters
* for the various queues. In our setup we have a 1:1 mapping between QCUs
* and DCUs allowing us to have different DFS settings for each queue.
*
* When a frame goes into a TX queue, QCU decides when it'll trigger a
* transmission based on various criteria (such as how many data we have inside
* it's buffer or -if it's a beacon queue- if it's time to fire up the queue
* based on TSF etc), DCU adds backoff, IFSes etc and then a scheduler
* (arbitrator) decides the priority of each QCU based on it's configuration
* (e.g. beacons are always transmitted when they leave DCU bypassing all other
* frames from other queues waiting to be transmitted). After a frame leaves
* the DCU it goes to PCU for further processing and then to PHY for
* the actual transmission.
*/
/******************\
* Helper functions *
\******************/
/**
* ath5k_hw_num_tx_pending() - Get number of pending frames for a given queue
* @ah: The &struct ath5k_hw
* @queue: One of enum ath5k_tx_queue_id
*/
u32
ath5k_hw_num_tx_pending(struct ath5k_hw *ah, unsigned int queue)
{
u32 pending;
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;
pending = ath5k_hw_reg_read(ah, AR5K_QUEUE_STATUS(queue));
pending &= AR5K_QCU_STS_FRMPENDCNT;
/* It's possible to have no frames pending even if TXE
* is set. To indicate that q has not stopped return
* true */
if (!pending && AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue))
return true;
return pending;
}
/**
* ath5k_hw_release_tx_queue() - Set a transmit queue inactive
* @ah: The &struct ath5k_hw
* @queue: One of enum ath5k_tx_queue_id
*/
void
ath5k_hw_release_tx_queue(struct ath5k_hw *ah, unsigned int queue)
{
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);
}
/**
* ath5k_cw_validate() - Make sure the given cw is valid
* @cw_req: The contention window value to check
*
* Make sure cw is a power of 2 minus 1 and smaller than 1024
*/
static u16
ath5k_cw_validate(u16 cw_req)
{
cw_req = min(cw_req, (u16)1023);
/* Check if cw_req + 1 a power of 2 */
if (is_power_of_2(cw_req + 1))
return cw_req;
/* Check if cw_req is a power of 2 */
if (is_power_of_2(cw_req))
return cw_req - 1;
/* If none of the above is correct
* find the closest power of 2 */
cw_req = (u16) roundup_pow_of_two(cw_req) - 1;
return cw_req;
}
/**
* ath5k_hw_get_tx_queueprops() - Get properties for a transmit queue
* @ah: The &struct ath5k_hw
* @queue: One of enum ath5k_tx_queue_id
* @queue_info: The &struct ath5k_txq_info to fill
*/
int
ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue,
struct ath5k_txq_info *queue_info)
{
memcpy(queue_info, &ah->ah_txq[queue], sizeof(struct ath5k_txq_info));
return 0;
}
/**
* ath5k_hw_set_tx_queueprops() - Set properties for a transmit queue
* @ah: The &struct ath5k_hw
* @queue: One of enum ath5k_tx_queue_id
* @qinfo: The &struct ath5k_txq_info to use
*
* Returns 0 on success or -EIO if queue is inactive
*/
int
ath5k_hw_set_tx_queueprops(struct ath5k_hw *ah, int queue,
const struct ath5k_txq_info *qinfo)
{
struct ath5k_txq_info *qi;
AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
qi = &ah->ah_txq[queue];
if (qi->tqi_type == AR5K_TX_QUEUE_INACTIVE)
return -EIO;
/* copy and validate values */
qi->tqi_type = qinfo->tqi_type;
qi->tqi_subtype = qinfo->tqi_subtype;
qi->tqi_flags = qinfo->tqi_flags;
/*
* According to the docs: Although the AIFS field is 8 bit wide,
* the maximum supported value is 0xFC. Setting it higher than that
* will cause the DCU to hang.
*/
qi->tqi_aifs = min(qinfo->tqi_aifs, (u8)0xFC);
qi->tqi_cw_min = ath5k_cw_validate(qinfo->tqi_cw_min);
qi->tqi_cw_max = ath5k_cw_validate(qinfo->tqi_cw_max);
qi->tqi_cbr_period = qinfo->tqi_cbr_period;
qi->tqi_cbr_overflow_limit = qinfo->tqi_cbr_overflow_limit;
qi->tqi_burst_time = qinfo->tqi_burst_time;
qi->tqi_ready_time = qinfo->tqi_ready_time;
/*XXX: Is this supported on 5210 ?*/
/*XXX: Is this correct for AR5K_WME_AC_VI,VO ???*/
if ((qinfo->tqi_type == AR5K_TX_QUEUE_DATA &&
((qinfo->tqi_subtype == AR5K_WME_AC_VI) ||
(qinfo->tqi_subtype == AR5K_WME_AC_VO))) ||
qinfo->tqi_type == AR5K_TX_QUEUE_UAPSD)
qi->tqi_flags |= AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS;
return 0;
}
/**
* ath5k_hw_setup_tx_queue() - Initialize a transmit queue
* @ah: The &struct ath5k_hw
* @queue_type: One of enum ath5k_tx_queue
* @queue_info: The &struct ath5k_txq_info to use
*
* Returns 0 on success, -EINVAL on invalid arguments
*/
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;
/*
* Get queue by type
*/
/* 5210 only has 2 queues */
if (ah->ah_capabilities.cap_queues.q_tx_num == 2) {
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:
queue = queue_info->tqi_subtype;
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;
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_set_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;
}
/*******************************\
* Single QCU/DCU initialization *
\*******************************/
/**
* ath5k_hw_set_tx_retry_limits() - Set tx retry limits on DCU
* @ah: The &struct ath5k_hw
* @queue: One of enum ath5k_tx_queue_id
*
* This function is used when initializing a queue, to set
* retry limits based on ah->ah_retry_* and the chipset used.
*/
void
ath5k_hw_set_tx_retry_limits(struct ath5k_hw *ah,
unsigned int queue)
{
/* Single data queue on AR5210 */
if (ah->ah_version == AR5K_AR5210) {
struct ath5k_txq_info *tq = &ah->ah_txq[queue];
if (queue > 0)
return;
ath5k_hw_reg_write(ah,
(tq->tqi_cw_min << AR5K_NODCU_RETRY_LMT_CW_MIN_S)
| AR5K_REG_SM(ah->ah_retry_long,
AR5K_NODCU_RETRY_LMT_SLG_RETRY)
| AR5K_REG_SM(ah->ah_retry_short,
AR5K_NODCU_RETRY_LMT_SSH_RETRY)
| AR5K_REG_SM(ah->ah_retry_long,
AR5K_NODCU_RETRY_LMT_LG_RETRY)
| AR5K_REG_SM(ah->ah_retry_short,
AR5K_NODCU_RETRY_LMT_SH_RETRY),
AR5K_NODCU_RETRY_LMT);
/* DCU on AR5211+ */
} else {
ath5k_hw_reg_write(ah,
AR5K_REG_SM(ah->ah_retry_long,
AR5K_DCU_RETRY_LMT_RTS)
| AR5K_REG_SM(ah->ah_retry_long,
AR5K_DCU_RETRY_LMT_STA_RTS)
| AR5K_REG_SM(max(ah->ah_retry_long, ah->ah_retry_short),
AR5K_DCU_RETRY_LMT_STA_DATA),
AR5K_QUEUE_DFS_RETRY_LIMIT(queue));
}
}
/**
* ath5k_hw_reset_tx_queue() - Initialize a single hw queue
* @ah: The &struct ath5k_hw
* @queue: One of enum ath5k_tx_queue_id
*
* Set DCF properties for the given transmit queue on DCU
* and configures all queue-specific parameters.
*/
int
ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue)
{
struct ath5k_txq_info *tq = &ah->ah_txq[queue];
AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
/* Skip if queue inactive or if we are on AR5210
* that doesn't have QCU/DCU */
if ((ah->ah_version == AR5K_AR5210) ||
(tq->tqi_type == AR5K_TX_QUEUE_INACTIVE))
return 0;
/*
* Set contention window (cw_min/cw_max)
* and arbitrated interframe space (aifs)...
*/
ath5k_hw_reg_write(ah,
AR5K_REG_SM(tq->tqi_cw_min, AR5K_DCU_LCL_IFS_CW_MIN) |
AR5K_REG_SM(tq->tqi_cw_max, AR5K_DCU_LCL_IFS_CW_MAX) |
AR5K_REG_SM(tq->tqi_aifs, AR5K_DCU_LCL_IFS_AIFS),
AR5K_QUEUE_DFS_LOCAL_IFS(queue));
/*
* Set tx retry limits for this queue
*/
ath5k_hw_set_tx_retry_limits(ah, queue);
/*
* Set misc registers
*/
/* Enable DCU to wait for next fragment from QCU */
AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue),
AR5K_DCU_MISC_FRAG_WAIT);
/* On Maui and Spirit use the global seqnum on DCU */
if (ah->ah_mac_version < AR5K_SREV_AR5211)
AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue),
AR5K_DCU_MISC_SEQNUM_CTL);
/* Constant bit rate period */
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);
}
/* Ready time interval */
if (tq->tqi_ready_time && (tq->tqi_type != AR5K_TX_QUEUE_CAB))
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_RDY_VEOL_POLICY);
}
/* Enable/disable Post frame backoff */
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));
/* Enable/disable fragmentation burst backoff */
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_DIS |
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_ARBLOCK_IGNORE |
AR5K_DCU_MISC_POST_FR_BKOFF_DIS |
AR5K_DCU_MISC_BCN_ENABLE);
break;
case AR5K_TX_QUEUE_CAB:
/* XXX: use BCN_SENT_GT, if we can figure out how */
AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
AR5K_QCU_MISC_FRSHED_DBA_GT |
AR5K_QCU_MISC_CBREXP_DIS |
AR5K_QCU_MISC_CBREXP_BCN_DIS);
ath5k_hw_reg_write(ah, ((tq->tqi_ready_time -
(AR5K_TUNE_SW_BEACON_RESP -
AR5K_TUNE_DMA_BEACON_RESP) -
AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF) * 1024) |
AR5K_QCU_RDYTIMECFG_ENABLE,
AR5K_QUEUE_RDYTIMECFG(queue));
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_DIS);
break;
case AR5K_TX_QUEUE_DATA:
default:
break;
}
/* TODO: Handle frame compression */
/*
* 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);
if (tq->tqi_flags & AR5K_TXQ_FLAG_CBRORNINT_ENABLE)
AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_cbrorn, queue);
if (tq->tqi_flags & AR5K_TXQ_FLAG_CBRURNINT_ENABLE)
AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_cbrurn, queue);
if (tq->tqi_flags & AR5K_TXQ_FLAG_QTRIGINT_ENABLE)
AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_qtrig, queue);
if (tq->tqi_flags & AR5K_TXQ_FLAG_TXNOFRMINT_ENABLE)
AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_nofrm, queue);
/* Update secondary interrupt mask registers */
/* Filter out inactive queues */
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;
ah->ah_txq_imr_cbrorn &= ah->ah_txq_status;
ah->ah_txq_imr_cbrurn &= ah->ah_txq_status;
ah->ah_txq_imr_qtrig &= ah->ah_txq_status;
ah->ah_txq_imr_nofrm &= 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);
/* Update SIMR2 but don't overwrite rest simr2 settings */
AR5K_REG_DISABLE_BITS(ah, AR5K_SIMR2, AR5K_SIMR2_QCU_TXURN);
AR5K_REG_ENABLE_BITS(ah, AR5K_SIMR2,
AR5K_REG_SM(ah->ah_txq_imr_txurn,
AR5K_SIMR2_QCU_TXURN));
ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_cbrorn,
AR5K_SIMR3_QCBRORN) |
AR5K_REG_SM(ah->ah_txq_imr_cbrurn,
AR5K_SIMR3_QCBRURN),
AR5K_SIMR3);
ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_qtrig,
AR5K_SIMR4_QTRIG), AR5K_SIMR4);
/* Set TXNOFRM_QCU for the queues with TXNOFRM enabled */
ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_nofrm,
AR5K_TXNOFRM_QCU), AR5K_TXNOFRM);
/* No queue has TXNOFRM enabled, disable the interrupt
* by setting AR5K_TXNOFRM to zero */
if (ah->ah_txq_imr_nofrm == 0)
ath5k_hw_reg_write(ah, 0, AR5K_TXNOFRM);
/* Set QCU mask for this DCU to save power */
AR5K_REG_WRITE_Q(ah, AR5K_QUEUE_QCUMASK(queue), queue);
return 0;
}
/**************************\
* Global QCU/DCU functions *
\**************************/
/**
* ath5k_hw_set_ifs_intervals() - Set global inter-frame spaces on DCU
* @ah: The &struct ath5k_hw
* @slot_time: Slot time in us
*
* Sets the global IFS intervals on DCU (also works on AR5210) for
* the given slot time and the current bwmode.
*/
int ath5k_hw_set_ifs_intervals(struct ath5k_hw *ah, unsigned int slot_time)
{
struct ieee80211_channel *channel = ah->ah_current_channel;
enum nl80211_band band;
struct ieee80211_supported_band *sband;
struct ieee80211_rate *rate;
u32 ack_tx_time, eifs, eifs_clock, sifs, sifs_clock;
u32 slot_time_clock = ath5k_hw_htoclock(ah, slot_time);
u32 rate_flags, i;
if (slot_time < 6 || slot_time_clock > AR5K_SLOT_TIME_MAX)
return -EINVAL;
sifs = ath5k_hw_get_default_sifs(ah);
sifs_clock = ath5k_hw_htoclock(ah, sifs - 2);
/* EIFS
* Txtime of ack at lowest rate + SIFS + DIFS
* (DIFS = SIFS + 2 * Slot time)
*
* Note: HAL has some predefined values for EIFS
* Turbo: (37 + 2 * 6)
* Default: (74 + 2 * 9)
* Half: (149 + 2 * 13)
* Quarter: (298 + 2 * 21)
*
* (74 + 2 * 6) for AR5210 default and turbo !
*
* According to the formula we have
* ack_tx_time = 25 for turbo and
* ack_tx_time = 42.5 * clock multiplier
* for default/half/quarter.
*
* This can't be right, 42 is what we would get
* from ath5k_hw_get_frame_dur_for_bwmode or
* ieee80211_generic_frame_duration for zero frame
* length and without SIFS !
*
* Also we have different lowest rate for 802.11a
*/
if (channel->band == NL80211_BAND_5GHZ)
band = NL80211_BAND_5GHZ;
else
band = NL80211_BAND_2GHZ;
switch (ah->ah_bwmode) {
case AR5K_BWMODE_5MHZ:
rate_flags = IEEE80211_RATE_SUPPORTS_5MHZ;
break;
case AR5K_BWMODE_10MHZ:
rate_flags = IEEE80211_RATE_SUPPORTS_10MHZ;
break;
default:
rate_flags = 0;
break;
}
sband = &ah->sbands[band];
rate = NULL;
for (i = 0; i < sband->n_bitrates; i++) {
if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
continue;
rate = &sband->bitrates[i];
break;
}
if (WARN_ON(!rate))
return -EINVAL;
ack_tx_time = ath5k_hw_get_frame_duration(ah, band, 10, rate, false);
/* ack_tx_time includes an SIFS already */
eifs = ack_tx_time + sifs + 2 * slot_time;
eifs_clock = ath5k_hw_htoclock(ah, eifs);
/* Set IFS settings on AR5210 */
if (ah->ah_version == AR5K_AR5210) {
u32 pifs, pifs_clock, difs, difs_clock;
/* Set slot time */
ath5k_hw_reg_write(ah, slot_time_clock, AR5K_SLOT_TIME);
/* Set EIFS */
eifs_clock = AR5K_REG_SM(eifs_clock, AR5K_IFS1_EIFS);
/* PIFS = Slot time + SIFS */
pifs = slot_time + sifs;
pifs_clock = ath5k_hw_htoclock(ah, pifs);
pifs_clock = AR5K_REG_SM(pifs_clock, AR5K_IFS1_PIFS);
/* DIFS = SIFS + 2 * Slot time */
difs = sifs + 2 * slot_time;
difs_clock = ath5k_hw_htoclock(ah, difs);
/* Set SIFS/DIFS */
ath5k_hw_reg_write(ah, (difs_clock <<
AR5K_IFS0_DIFS_S) | sifs_clock,
AR5K_IFS0);
/* Set PIFS/EIFS and preserve AR5K_INIT_CARR_SENSE_EN */
ath5k_hw_reg_write(ah, pifs_clock | eifs_clock |
(AR5K_INIT_CARR_SENSE_EN << AR5K_IFS1_CS_EN_S),
AR5K_IFS1);
return 0;
}
/* Set IFS slot time */
ath5k_hw_reg_write(ah, slot_time_clock, AR5K_DCU_GBL_IFS_SLOT);
/* Set EIFS interval */
ath5k_hw_reg_write(ah, eifs_clock, AR5K_DCU_GBL_IFS_EIFS);
/* Set SIFS interval in usecs */
AR5K_REG_WRITE_BITS(ah, AR5K_DCU_GBL_IFS_MISC,
AR5K_DCU_GBL_IFS_MISC_SIFS_DUR_USEC,
sifs);
/* Set SIFS interval in clock cycles */
ath5k_hw_reg_write(ah, sifs_clock, AR5K_DCU_GBL_IFS_SIFS);
return 0;
}
/**
* ath5k_hw_init_queues() - Initialize tx queues
* @ah: The &struct ath5k_hw
*
* Initializes all tx queues based on information on
* ah->ah_txq* set by the driver
*/
int
ath5k_hw_init_queues(struct ath5k_hw *ah)
{
int i, ret;
/* TODO: HW Compression support for data queues */
/* TODO: Burst prefetch for data queues */
/*
* Reset queues and start beacon timers at the end of the reset routine
* This also sets QCU mask on each DCU for 1:1 qcu to dcu mapping
* Note: If we want we can assign multiple qcus on one dcu.
*/
if (ah->ah_version != AR5K_AR5210)
for (i = 0; i < ah->ah_capabilities.cap_queues.q_tx_num; i++) {
ret = ath5k_hw_reset_tx_queue(ah, i);
if (ret) {
ATH5K_ERR(ah,
"failed to reset TX queue #%d\n", i);
return ret;
}
}
else
/* No QCU/DCU on AR5210, just set tx
* retry limits. We set IFS parameters
* on ath5k_hw_set_ifs_intervals */
ath5k_hw_set_tx_retry_limits(ah, 0);
/* Set the turbo flag when operating on 40MHz */
if (ah->ah_bwmode == AR5K_BWMODE_40MHZ)
AR5K_REG_ENABLE_BITS(ah, AR5K_DCU_GBL_IFS_MISC,
AR5K_DCU_GBL_IFS_MISC_TURBO_MODE);
/* If we didn't set IFS timings through
* ath5k_hw_set_coverage_class make sure
* we set them here */
if (!ah->ah_coverage_class) {
unsigned int slot_time = ath5k_hw_get_default_slottime(ah);
ath5k_hw_set_ifs_intervals(ah, slot_time);
}
return 0;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,853 @@
/*
* RF Buffer handling functions
*
* Copyright (c) 2009 Nick Kossifidis <mickflemm@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.
*
*/
/**
* DOC: RF Buffer registers
*
* There are some special registers on the RF chip
* that control various operation settings related mostly to
* the analog parts (channel, gain adjustment etc).
*
* We don't write on those registers directly but
* we send a data packet on the chip, using a special register,
* that holds all the settings we need. After we've sent the
* data packet, we write on another special register to notify hw
* to apply the settings. This is done so that control registers
* can be dynamically programmed during operation and the settings
* are applied faster on the hw.
*
* We call each data packet an "RF Bank" and all the data we write
* (all RF Banks) "RF Buffer". This file holds initial RF Buffer
* data for the different RF chips, and various info to match RF
* Buffer offsets with specific RF registers so that we can access
* them. We tweak these settings on rfregs_init function.
*
* Also check out reg.h and U.S. Patent 6677779 B1 (about buffer
* registers and control registers):
*
* https://www.google.com/patents?id=qNURAAAAEBAJ
*/
/**
* struct ath5k_ini_rfbuffer - Initial RF Buffer settings
* @rfb_bank: RF Bank number
* @rfb_ctrl_register: RF Buffer control register
* @rfb_mode_data: RF Buffer data for each mode
*
* Struct to hold default mode specific RF
* register values (RF Banks) for each chip.
*/
struct ath5k_ini_rfbuffer {
u8 rfb_bank;
u16 rfb_ctrl_register;
u32 rfb_mode_data[3];
};
/**
* struct ath5k_rfb_field - An RF Buffer field (register/value)
* @len: Field length
* @pos: Offset on the raw packet
* @col: Used for shifting
*
* Struct to hold RF Buffer field
* infos used to access certain RF
* analog registers
*/
struct ath5k_rfb_field {
u8 len;
u16 pos;
u8 col;
};
/**
* struct ath5k_rf_reg - RF analog register definition
* @bank: RF Buffer Bank number
* @index: Register's index on ath5k_rf_regx_idx
* @field: The &struct ath5k_rfb_field
*
* We use this struct to define the set of RF registers
* on each chip that we want to tweak. Some RF registers
* are common between different chip versions so this saves
* us space and complexity because we can refer to an rf
* register by it's index no matter what chip we work with
* as long as it has that register.
*/
struct ath5k_rf_reg {
u8 bank;
u8 index;
struct ath5k_rfb_field field;
};
/**
* enum ath5k_rf_regs_idx - Map RF registers to indexes
*
* We do this to handle common bits and make our
* life easier by using an index for each register
* instead of a full rfb_field
*/
enum ath5k_rf_regs_idx {
/* BANK 2 */
AR5K_RF_TURBO = 0,
/* BANK 6 */
AR5K_RF_OB_2GHZ,
AR5K_RF_OB_5GHZ,
AR5K_RF_DB_2GHZ,
AR5K_RF_DB_5GHZ,
AR5K_RF_FIXED_BIAS_A,
AR5K_RF_FIXED_BIAS_B,
AR5K_RF_PWD_XPD,
AR5K_RF_XPD_SEL,
AR5K_RF_XPD_GAIN,
AR5K_RF_PD_GAIN_LO,
AR5K_RF_PD_GAIN_HI,
AR5K_RF_HIGH_VC_CP,
AR5K_RF_MID_VC_CP,
AR5K_RF_LOW_VC_CP,
AR5K_RF_PUSH_UP,
AR5K_RF_PAD2GND,
AR5K_RF_XB2_LVL,
AR5K_RF_XB5_LVL,
AR5K_RF_PWD_ICLOBUF_2G,
AR5K_RF_PWD_84,
AR5K_RF_PWD_90,
AR5K_RF_PWD_130,
AR5K_RF_PWD_131,
AR5K_RF_PWD_132,
AR5K_RF_PWD_136,
AR5K_RF_PWD_137,
AR5K_RF_PWD_138,
AR5K_RF_PWD_166,
AR5K_RF_PWD_167,
AR5K_RF_DERBY_CHAN_SEL_MODE,
/* BANK 7 */
AR5K_RF_GAIN_I,
AR5K_RF_PLO_SEL,
AR5K_RF_RFGAIN_SEL,
AR5K_RF_RFGAIN_STEP,
AR5K_RF_WAIT_S,
AR5K_RF_WAIT_I,
AR5K_RF_MAX_TIME,
AR5K_RF_MIXVGA_OVR,
AR5K_RF_MIXGAIN_OVR,
AR5K_RF_MIXGAIN_STEP,
AR5K_RF_PD_DELAY_A,
AR5K_RF_PD_DELAY_B,
AR5K_RF_PD_DELAY_XR,
AR5K_RF_PD_PERIOD_A,
AR5K_RF_PD_PERIOD_B,
AR5K_RF_PD_PERIOD_XR,
};
/*******************\
* RF5111 (Sombrero) *
\*******************/
/* BANK 2 len pos col */
#define AR5K_RF5111_RF_TURBO { 1, 3, 0 }
/* BANK 6 len pos col */
#define AR5K_RF5111_OB_2GHZ { 3, 119, 0 }
#define AR5K_RF5111_DB_2GHZ { 3, 122, 0 }
#define AR5K_RF5111_OB_5GHZ { 3, 104, 0 }
#define AR5K_RF5111_DB_5GHZ { 3, 107, 0 }
#define AR5K_RF5111_PWD_XPD { 1, 95, 0 }
#define AR5K_RF5111_XPD_GAIN { 4, 96, 0 }
/* Access to PWD registers */
#define AR5K_RF5111_PWD(_n) { 1, (135 - _n), 3 }
/* BANK 7 len pos col */
#define AR5K_RF5111_GAIN_I { 6, 29, 0 }
#define AR5K_RF5111_PLO_SEL { 1, 4, 0 }
#define AR5K_RF5111_RFGAIN_SEL { 1, 36, 0 }
#define AR5K_RF5111_RFGAIN_STEP { 6, 37, 0 }
/* Only on AR5212 BaseBand and up */
#define AR5K_RF5111_WAIT_S { 5, 19, 0 }
#define AR5K_RF5111_WAIT_I { 5, 24, 0 }
#define AR5K_RF5111_MAX_TIME { 2, 49, 0 }
static const struct ath5k_rf_reg rf_regs_5111[] = {
{2, AR5K_RF_TURBO, AR5K_RF5111_RF_TURBO},
{6, AR5K_RF_OB_2GHZ, AR5K_RF5111_OB_2GHZ},
{6, AR5K_RF_DB_2GHZ, AR5K_RF5111_DB_2GHZ},
{6, AR5K_RF_OB_5GHZ, AR5K_RF5111_OB_5GHZ},
{6, AR5K_RF_DB_5GHZ, AR5K_RF5111_DB_5GHZ},
{6, AR5K_RF_PWD_XPD, AR5K_RF5111_PWD_XPD},
{6, AR5K_RF_XPD_GAIN, AR5K_RF5111_XPD_GAIN},
{6, AR5K_RF_PWD_84, AR5K_RF5111_PWD(84)},
{6, AR5K_RF_PWD_90, AR5K_RF5111_PWD(90)},
{7, AR5K_RF_GAIN_I, AR5K_RF5111_GAIN_I},
{7, AR5K_RF_PLO_SEL, AR5K_RF5111_PLO_SEL},
{7, AR5K_RF_RFGAIN_SEL, AR5K_RF5111_RFGAIN_SEL},
{7, AR5K_RF_RFGAIN_STEP, AR5K_RF5111_RFGAIN_STEP},
{7, AR5K_RF_WAIT_S, AR5K_RF5111_WAIT_S},
{7, AR5K_RF_WAIT_I, AR5K_RF5111_WAIT_I},
{7, AR5K_RF_MAX_TIME, AR5K_RF5111_MAX_TIME}
};
/* Default mode specific settings */
static const struct ath5k_ini_rfbuffer rfb_5111[] = {
/* BANK / C.R. A/XR B G */
{ 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 0, 0x989c, { 0x00380000, 0x00380000, 0x00380000 } },
{ 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 0, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 0, 0x989c, { 0x00000000, 0x000000c0, 0x00000080 } },
{ 0, 0x989c, { 0x000400f9, 0x000400ff, 0x000400fd } },
{ 0, 0x98d4, { 0x00000000, 0x00000004, 0x00000004 } },
{ 1, 0x98d4, { 0x00000020, 0x00000020, 0x00000020 } },
{ 2, 0x98d4, { 0x00000010, 0x00000010, 0x00000010 } },
{ 3, 0x98d8, { 0x00601068, 0x00601068, 0x00601068 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x10000000, 0x10000000, 0x10000000 } },
{ 6, 0x989c, { 0x04000000, 0x04000000, 0x04000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x0a000000, 0x00000000 } },
{ 6, 0x989c, { 0x003800c0, 0x023800c0, 0x003800c0 } },
{ 6, 0x989c, { 0x00020006, 0x00000006, 0x00020006 } },
{ 6, 0x989c, { 0x00000089, 0x00000089, 0x00000089 } },
{ 6, 0x989c, { 0x000000a0, 0x000000a0, 0x000000a0 } },
{ 6, 0x989c, { 0x00040007, 0x00040007, 0x00040007 } },
{ 6, 0x98d4, { 0x0000001a, 0x0000001a, 0x0000001a } },
{ 7, 0x989c, { 0x00000040, 0x00000040, 0x00000040 } },
{ 7, 0x989c, { 0x00000010, 0x00000010, 0x00000010 } },
{ 7, 0x989c, { 0x00000008, 0x00000008, 0x00000008 } },
{ 7, 0x989c, { 0x0000004f, 0x0000004f, 0x0000004f } },
{ 7, 0x989c, { 0x000000f1, 0x00000061, 0x000000f1 } },
{ 7, 0x989c, { 0x0000904f, 0x0000904c, 0x0000904f } },
{ 7, 0x989c, { 0x0000125a, 0x0000129a, 0x0000125a } },
{ 7, 0x98cc, { 0x0000000e, 0x0000000f, 0x0000000e } },
};
/***********************\
* RF5112/RF2112 (Derby) *
\***********************/
/* BANK 2 (Common) len pos col */
#define AR5K_RF5112X_RF_TURBO { 1, 1, 2 }
/* BANK 7 (Common) len pos col */
#define AR5K_RF5112X_GAIN_I { 6, 14, 0 }
#define AR5K_RF5112X_MIXVGA_OVR { 1, 36, 0 }
#define AR5K_RF5112X_MIXGAIN_OVR { 2, 37, 0 }
#define AR5K_RF5112X_MIXGAIN_STEP { 4, 32, 0 }
#define AR5K_RF5112X_PD_DELAY_A { 4, 58, 0 }
#define AR5K_RF5112X_PD_DELAY_B { 4, 62, 0 }
#define AR5K_RF5112X_PD_DELAY_XR { 4, 66, 0 }
#define AR5K_RF5112X_PD_PERIOD_A { 4, 70, 0 }
#define AR5K_RF5112X_PD_PERIOD_B { 4, 74, 0 }
#define AR5K_RF5112X_PD_PERIOD_XR { 4, 78, 0 }
/* RFX112 (Derby 1) */
/* BANK 6 len pos col */
#define AR5K_RF5112_OB_2GHZ { 3, 269, 0 }
#define AR5K_RF5112_DB_2GHZ { 3, 272, 0 }
#define AR5K_RF5112_OB_5GHZ { 3, 261, 0 }
#define AR5K_RF5112_DB_5GHZ { 3, 264, 0 }
#define AR5K_RF5112_FIXED_BIAS_A { 1, 260, 0 }
#define AR5K_RF5112_FIXED_BIAS_B { 1, 259, 0 }
#define AR5K_RF5112_XPD_SEL { 1, 284, 0 }
#define AR5K_RF5112_XPD_GAIN { 2, 252, 0 }
/* Access to PWD registers */
#define AR5K_RF5112_PWD(_n) { 1, (302 - _n), 3 }
static const struct ath5k_rf_reg rf_regs_5112[] = {
{2, AR5K_RF_TURBO, AR5K_RF5112X_RF_TURBO},
{6, AR5K_RF_OB_2GHZ, AR5K_RF5112_OB_2GHZ},
{6, AR5K_RF_DB_2GHZ, AR5K_RF5112_DB_2GHZ},
{6, AR5K_RF_OB_5GHZ, AR5K_RF5112_OB_5GHZ},
{6, AR5K_RF_DB_5GHZ, AR5K_RF5112_DB_5GHZ},
{6, AR5K_RF_FIXED_BIAS_A, AR5K_RF5112_FIXED_BIAS_A},
{6, AR5K_RF_FIXED_BIAS_B, AR5K_RF5112_FIXED_BIAS_B},
{6, AR5K_RF_XPD_SEL, AR5K_RF5112_XPD_SEL},
{6, AR5K_RF_XPD_GAIN, AR5K_RF5112_XPD_GAIN},
{6, AR5K_RF_PWD_130, AR5K_RF5112_PWD(130)},
{6, AR5K_RF_PWD_131, AR5K_RF5112_PWD(131)},
{6, AR5K_RF_PWD_132, AR5K_RF5112_PWD(132)},
{6, AR5K_RF_PWD_136, AR5K_RF5112_PWD(136)},
{6, AR5K_RF_PWD_137, AR5K_RF5112_PWD(137)},
{6, AR5K_RF_PWD_138, AR5K_RF5112_PWD(138)},
{7, AR5K_RF_GAIN_I, AR5K_RF5112X_GAIN_I},
{7, AR5K_RF_MIXVGA_OVR, AR5K_RF5112X_MIXVGA_OVR},
{7, AR5K_RF_MIXGAIN_OVR, AR5K_RF5112X_MIXGAIN_OVR},
{7, AR5K_RF_MIXGAIN_STEP, AR5K_RF5112X_MIXGAIN_STEP},
{7, AR5K_RF_PD_DELAY_A, AR5K_RF5112X_PD_DELAY_A},
{7, AR5K_RF_PD_DELAY_B, AR5K_RF5112X_PD_DELAY_B},
{7, AR5K_RF_PD_DELAY_XR, AR5K_RF5112X_PD_DELAY_XR},
{7, AR5K_RF_PD_PERIOD_A, AR5K_RF5112X_PD_PERIOD_A},
{7, AR5K_RF_PD_PERIOD_B, AR5K_RF5112X_PD_PERIOD_B},
{7, AR5K_RF_PD_PERIOD_XR, AR5K_RF5112X_PD_PERIOD_XR},
};
/* Default mode specific settings */
static const struct ath5k_ini_rfbuffer rfb_5112[] = {
/* BANK / C.R. A/XR B G */
{ 1, 0x98d4, { 0x00000020, 0x00000020, 0x00000020 } },
{ 2, 0x98d0, { 0x03060408, 0x03060408, 0x03060408 } },
{ 3, 0x98dc, { 0x00a0c0c0, 0x00e0c0c0, 0x00e0c0c0 } },
{ 6, 0x989c, { 0x00a00000, 0x00a00000, 0x00a00000 } },
{ 6, 0x989c, { 0x000a0000, 0x000a0000, 0x000a0000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00660000, 0x00660000, 0x00660000 } },
{ 6, 0x989c, { 0x00db0000, 0x00db0000, 0x00db0000 } },
{ 6, 0x989c, { 0x00f10000, 0x00f10000, 0x00f10000 } },
{ 6, 0x989c, { 0x00120000, 0x00120000, 0x00120000 } },
{ 6, 0x989c, { 0x00120000, 0x00120000, 0x00120000 } },
{ 6, 0x989c, { 0x00730000, 0x00730000, 0x00730000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x000c0000, 0x000c0000, 0x000c0000 } },
{ 6, 0x989c, { 0x00ff0000, 0x00ff0000, 0x00ff0000 } },
{ 6, 0x989c, { 0x00ff0000, 0x00ff0000, 0x00ff0000 } },
{ 6, 0x989c, { 0x008b0000, 0x008b0000, 0x008b0000 } },
{ 6, 0x989c, { 0x00600000, 0x00600000, 0x00600000 } },
{ 6, 0x989c, { 0x000c0000, 0x000c0000, 0x000c0000 } },
{ 6, 0x989c, { 0x00840000, 0x00840000, 0x00840000 } },
{ 6, 0x989c, { 0x00640000, 0x00640000, 0x00640000 } },
{ 6, 0x989c, { 0x00200000, 0x00200000, 0x00200000 } },
{ 6, 0x989c, { 0x00240000, 0x00240000, 0x00240000 } },
{ 6, 0x989c, { 0x00250000, 0x00250000, 0x00250000 } },
{ 6, 0x989c, { 0x00110000, 0x00110000, 0x00110000 } },
{ 6, 0x989c, { 0x00110000, 0x00110000, 0x00110000 } },
{ 6, 0x989c, { 0x00510000, 0x00510000, 0x00510000 } },
{ 6, 0x989c, { 0x1c040000, 0x1c040000, 0x1c040000 } },
{ 6, 0x989c, { 0x000a0000, 0x000a0000, 0x000a0000 } },
{ 6, 0x989c, { 0x00a10000, 0x00a10000, 0x00a10000 } },
{ 6, 0x989c, { 0x00400000, 0x00400000, 0x00400000 } },
{ 6, 0x989c, { 0x03090000, 0x03090000, 0x03090000 } },
{ 6, 0x989c, { 0x06000000, 0x06000000, 0x06000000 } },
{ 6, 0x989c, { 0x000000b0, 0x000000a8, 0x000000a8 } },
{ 6, 0x989c, { 0x0000002e, 0x0000002e, 0x0000002e } },
{ 6, 0x989c, { 0x006c4a41, 0x006c4af1, 0x006c4a61 } },
{ 6, 0x989c, { 0x0050892a, 0x0050892b, 0x0050892b } },
{ 6, 0x989c, { 0x00842400, 0x00842400, 0x00842400 } },
{ 6, 0x989c, { 0x00c69200, 0x00c69200, 0x00c69200 } },
{ 6, 0x98d0, { 0x0002000c, 0x0002000c, 0x0002000c } },
{ 7, 0x989c, { 0x00000094, 0x00000094, 0x00000094 } },
{ 7, 0x989c, { 0x00000091, 0x00000091, 0x00000091 } },
{ 7, 0x989c, { 0x0000000a, 0x00000012, 0x00000012 } },
{ 7, 0x989c, { 0x00000080, 0x00000080, 0x00000080 } },
{ 7, 0x989c, { 0x000000c1, 0x000000c1, 0x000000c1 } },
{ 7, 0x989c, { 0x00000060, 0x00000060, 0x00000060 } },
{ 7, 0x989c, { 0x000000f0, 0x000000f0, 0x000000f0 } },
{ 7, 0x989c, { 0x00000022, 0x00000022, 0x00000022 } },
{ 7, 0x989c, { 0x00000092, 0x00000092, 0x00000092 } },
{ 7, 0x989c, { 0x000000d4, 0x000000d4, 0x000000d4 } },
{ 7, 0x989c, { 0x000014cc, 0x000014cc, 0x000014cc } },
{ 7, 0x989c, { 0x0000048c, 0x0000048c, 0x0000048c } },
{ 7, 0x98c4, { 0x00000003, 0x00000003, 0x00000003 } },
};
/* RFX112A (Derby 2) */
/* BANK 6 len pos col */
#define AR5K_RF5112A_OB_2GHZ { 3, 287, 0 }
#define AR5K_RF5112A_DB_2GHZ { 3, 290, 0 }
#define AR5K_RF5112A_OB_5GHZ { 3, 279, 0 }
#define AR5K_RF5112A_DB_5GHZ { 3, 282, 0 }
#define AR5K_RF5112A_FIXED_BIAS_A { 1, 278, 0 }
#define AR5K_RF5112A_FIXED_BIAS_B { 1, 277, 0 }
#define AR5K_RF5112A_XPD_SEL { 1, 302, 0 }
#define AR5K_RF5112A_PDGAINLO { 2, 270, 0 }
#define AR5K_RF5112A_PDGAINHI { 2, 257, 0 }
/* Access to PWD registers */
#define AR5K_RF5112A_PWD(_n) { 1, (306 - _n), 3 }
/* Voltage regulators */
#define AR5K_RF5112A_HIGH_VC_CP { 2, 90, 2 }
#define AR5K_RF5112A_MID_VC_CP { 2, 92, 2 }
#define AR5K_RF5112A_LOW_VC_CP { 2, 94, 2 }
#define AR5K_RF5112A_PUSH_UP { 1, 254, 2 }
/* Power consumption */
#define AR5K_RF5112A_PAD2GND { 1, 281, 1 }
#define AR5K_RF5112A_XB2_LVL { 2, 1, 3 }
#define AR5K_RF5112A_XB5_LVL { 2, 3, 3 }
static const struct ath5k_rf_reg rf_regs_5112a[] = {
{2, AR5K_RF_TURBO, AR5K_RF5112X_RF_TURBO},
{6, AR5K_RF_OB_2GHZ, AR5K_RF5112A_OB_2GHZ},
{6, AR5K_RF_DB_2GHZ, AR5K_RF5112A_DB_2GHZ},
{6, AR5K_RF_OB_5GHZ, AR5K_RF5112A_OB_5GHZ},
{6, AR5K_RF_DB_5GHZ, AR5K_RF5112A_DB_5GHZ},
{6, AR5K_RF_FIXED_BIAS_A, AR5K_RF5112A_FIXED_BIAS_A},
{6, AR5K_RF_FIXED_BIAS_B, AR5K_RF5112A_FIXED_BIAS_B},
{6, AR5K_RF_XPD_SEL, AR5K_RF5112A_XPD_SEL},
{6, AR5K_RF_PD_GAIN_LO, AR5K_RF5112A_PDGAINLO},
{6, AR5K_RF_PD_GAIN_HI, AR5K_RF5112A_PDGAINHI},
{6, AR5K_RF_PWD_130, AR5K_RF5112A_PWD(130)},
{6, AR5K_RF_PWD_131, AR5K_RF5112A_PWD(131)},
{6, AR5K_RF_PWD_132, AR5K_RF5112A_PWD(132)},
{6, AR5K_RF_PWD_136, AR5K_RF5112A_PWD(136)},
{6, AR5K_RF_PWD_137, AR5K_RF5112A_PWD(137)},
{6, AR5K_RF_PWD_138, AR5K_RF5112A_PWD(138)},
{6, AR5K_RF_PWD_166, AR5K_RF5112A_PWD(166)},
{6, AR5K_RF_PWD_167, AR5K_RF5112A_PWD(167)},
{6, AR5K_RF_HIGH_VC_CP, AR5K_RF5112A_HIGH_VC_CP},
{6, AR5K_RF_MID_VC_CP, AR5K_RF5112A_MID_VC_CP},
{6, AR5K_RF_LOW_VC_CP, AR5K_RF5112A_LOW_VC_CP},
{6, AR5K_RF_PUSH_UP, AR5K_RF5112A_PUSH_UP},
{6, AR5K_RF_PAD2GND, AR5K_RF5112A_PAD2GND},
{6, AR5K_RF_XB2_LVL, AR5K_RF5112A_XB2_LVL},
{6, AR5K_RF_XB5_LVL, AR5K_RF5112A_XB5_LVL},
{7, AR5K_RF_GAIN_I, AR5K_RF5112X_GAIN_I},
{7, AR5K_RF_MIXVGA_OVR, AR5K_RF5112X_MIXVGA_OVR},
{7, AR5K_RF_MIXGAIN_OVR, AR5K_RF5112X_MIXGAIN_OVR},
{7, AR5K_RF_MIXGAIN_STEP, AR5K_RF5112X_MIXGAIN_STEP},
{7, AR5K_RF_PD_DELAY_A, AR5K_RF5112X_PD_DELAY_A},
{7, AR5K_RF_PD_DELAY_B, AR5K_RF5112X_PD_DELAY_B},
{7, AR5K_RF_PD_DELAY_XR, AR5K_RF5112X_PD_DELAY_XR},
{7, AR5K_RF_PD_PERIOD_A, AR5K_RF5112X_PD_PERIOD_A},
{7, AR5K_RF_PD_PERIOD_B, AR5K_RF5112X_PD_PERIOD_B},
{7, AR5K_RF_PD_PERIOD_XR, AR5K_RF5112X_PD_PERIOD_XR},
};
/* Default mode specific settings */
static const struct ath5k_ini_rfbuffer rfb_5112a[] = {
/* BANK / C.R. A/XR B G */
{ 1, 0x98d4, { 0x00000020, 0x00000020, 0x00000020 } },
{ 2, 0x98d0, { 0x03060408, 0x03060408, 0x03060408 } },
{ 3, 0x98dc, { 0x00a020c0, 0x00e020c0, 0x00e020c0 } },
{ 6, 0x989c, { 0x0f000000, 0x0f000000, 0x0f000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00800000, 0x00800000, 0x00800000 } },
{ 6, 0x989c, { 0x002a0000, 0x002a0000, 0x002a0000 } },
{ 6, 0x989c, { 0x00010000, 0x00010000, 0x00010000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00180000, 0x00180000, 0x00180000 } },
{ 6, 0x989c, { 0x00600000, 0x006e0000, 0x006e0000 } },
{ 6, 0x989c, { 0x00c70000, 0x00c70000, 0x00c70000 } },
{ 6, 0x989c, { 0x004b0000, 0x004b0000, 0x004b0000 } },
{ 6, 0x989c, { 0x04480000, 0x04480000, 0x04480000 } },
{ 6, 0x989c, { 0x004c0000, 0x004c0000, 0x004c0000 } },
{ 6, 0x989c, { 0x00e40000, 0x00e40000, 0x00e40000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00fc0000, 0x00fc0000, 0x00fc0000 } },
{ 6, 0x989c, { 0x00ff0000, 0x00ff0000, 0x00ff0000 } },
{ 6, 0x989c, { 0x043f0000, 0x043f0000, 0x043f0000 } },
{ 6, 0x989c, { 0x000c0000, 0x000c0000, 0x000c0000 } },
{ 6, 0x989c, { 0x02190000, 0x02190000, 0x02190000 } },
{ 6, 0x989c, { 0x00240000, 0x00240000, 0x00240000 } },
{ 6, 0x989c, { 0x00b40000, 0x00b40000, 0x00b40000 } },
{ 6, 0x989c, { 0x00990000, 0x00990000, 0x00990000 } },
{ 6, 0x989c, { 0x00500000, 0x00500000, 0x00500000 } },
{ 6, 0x989c, { 0x002a0000, 0x002a0000, 0x002a0000 } },
{ 6, 0x989c, { 0x00120000, 0x00120000, 0x00120000 } },
{ 6, 0x989c, { 0xc0320000, 0xc0320000, 0xc0320000 } },
{ 6, 0x989c, { 0x01740000, 0x01740000, 0x01740000 } },
{ 6, 0x989c, { 0x00110000, 0x00110000, 0x00110000 } },
{ 6, 0x989c, { 0x86280000, 0x86280000, 0x86280000 } },
{ 6, 0x989c, { 0x31840000, 0x31840000, 0x31840000 } },
{ 6, 0x989c, { 0x00f20080, 0x00f20080, 0x00f20080 } },
{ 6, 0x989c, { 0x00270019, 0x00270019, 0x00270019 } },
{ 6, 0x989c, { 0x00000003, 0x00000003, 0x00000003 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x000000b2, 0x000000b2, 0x000000b2 } },
{ 6, 0x989c, { 0x00b02084, 0x00b02084, 0x00b02084 } },
{ 6, 0x989c, { 0x004125a4, 0x004125a4, 0x004125a4 } },
{ 6, 0x989c, { 0x00119220, 0x00119220, 0x00119220 } },
{ 6, 0x989c, { 0x001a4800, 0x001a4800, 0x001a4800 } },
{ 6, 0x98d8, { 0x000b0230, 0x000b0230, 0x000b0230 } },
{ 7, 0x989c, { 0x00000094, 0x00000094, 0x00000094 } },
{ 7, 0x989c, { 0x00000091, 0x00000091, 0x00000091 } },
{ 7, 0x989c, { 0x00000012, 0x00000012, 0x00000012 } },
{ 7, 0x989c, { 0x00000080, 0x00000080, 0x00000080 } },
{ 7, 0x989c, { 0x000000d9, 0x000000d9, 0x000000d9 } },
{ 7, 0x989c, { 0x00000060, 0x00000060, 0x00000060 } },
{ 7, 0x989c, { 0x000000f0, 0x000000f0, 0x000000f0 } },
{ 7, 0x989c, { 0x000000a2, 0x000000a2, 0x000000a2 } },
{ 7, 0x989c, { 0x00000052, 0x00000052, 0x00000052 } },
{ 7, 0x989c, { 0x000000d4, 0x000000d4, 0x000000d4 } },
{ 7, 0x989c, { 0x000014cc, 0x000014cc, 0x000014cc } },
{ 7, 0x989c, { 0x0000048c, 0x0000048c, 0x0000048c } },
{ 7, 0x98c4, { 0x00000003, 0x00000003, 0x00000003 } },
};
/******************\
* RF2413 (Griffin) *
\******************/
/* BANK 2 len pos col */
#define AR5K_RF2413_RF_TURBO { 1, 1, 2 }
/* BANK 6 len pos col */
#define AR5K_RF2413_OB_2GHZ { 3, 168, 0 }
#define AR5K_RF2413_DB_2GHZ { 3, 165, 0 }
static const struct ath5k_rf_reg rf_regs_2413[] = {
{2, AR5K_RF_TURBO, AR5K_RF2413_RF_TURBO},
{6, AR5K_RF_OB_2GHZ, AR5K_RF2413_OB_2GHZ},
{6, AR5K_RF_DB_2GHZ, AR5K_RF2413_DB_2GHZ},
};
/* Default mode specific settings
* XXX: a/aTurbo ???
*/
static const struct ath5k_ini_rfbuffer rfb_2413[] = {
/* BANK / C.R. A/XR B G */
{ 1, 0x98d4, { 0x00000020, 0x00000020, 0x00000020 } },
{ 2, 0x98d0, { 0x02001408, 0x02001408, 0x02001408 } },
{ 3, 0x98dc, { 0x00a020c0, 0x00e020c0, 0x00e020c0 } },
{ 6, 0x989c, { 0xf0000000, 0xf0000000, 0xf0000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x03000000, 0x03000000, 0x03000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x40400000, 0x40400000, 0x40400000 } },
{ 6, 0x989c, { 0x65050000, 0x65050000, 0x65050000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00420000, 0x00420000, 0x00420000 } },
{ 6, 0x989c, { 0x00b50000, 0x00b50000, 0x00b50000 } },
{ 6, 0x989c, { 0x00030000, 0x00030000, 0x00030000 } },
{ 6, 0x989c, { 0x00f70000, 0x00f70000, 0x00f70000 } },
{ 6, 0x989c, { 0x009d0000, 0x009d0000, 0x009d0000 } },
{ 6, 0x989c, { 0x00220000, 0x00220000, 0x00220000 } },
{ 6, 0x989c, { 0x04220000, 0x04220000, 0x04220000 } },
{ 6, 0x989c, { 0x00230018, 0x00230018, 0x00230018 } },
{ 6, 0x989c, { 0x00280000, 0x00280060, 0x00280060 } },
{ 6, 0x989c, { 0x005000c0, 0x005000c3, 0x005000c3 } },
{ 6, 0x989c, { 0x0004007f, 0x0004007f, 0x0004007f } },
{ 6, 0x989c, { 0x00000458, 0x00000458, 0x00000458 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x0000c000, 0x0000c000, 0x0000c000 } },
{ 6, 0x98d8, { 0x00400230, 0x00400230, 0x00400230 } },
{ 7, 0x989c, { 0x00006400, 0x00006400, 0x00006400 } },
{ 7, 0x989c, { 0x00000800, 0x00000800, 0x00000800 } },
{ 7, 0x98cc, { 0x0000000e, 0x0000000e, 0x0000000e } },
};
/***************************\
* RF2315/RF2316 (Cobra SoC) *
\***************************/
/* BANK 2 len pos col */
#define AR5K_RF2316_RF_TURBO { 1, 1, 2 }
/* BANK 6 len pos col */
#define AR5K_RF2316_OB_2GHZ { 3, 178, 0 }
#define AR5K_RF2316_DB_2GHZ { 3, 175, 0 }
static const struct ath5k_rf_reg rf_regs_2316[] = {
{2, AR5K_RF_TURBO, AR5K_RF2316_RF_TURBO},
{6, AR5K_RF_OB_2GHZ, AR5K_RF2316_OB_2GHZ},
{6, AR5K_RF_DB_2GHZ, AR5K_RF2316_DB_2GHZ},
};
/* Default mode specific settings */
static const struct ath5k_ini_rfbuffer rfb_2316[] = {
/* BANK / C.R. A/XR B G */
{ 1, 0x98d4, { 0x00000020, 0x00000020, 0x00000020 } },
{ 2, 0x98d0, { 0x02001408, 0x02001408, 0x02001408 } },
{ 3, 0x98dc, { 0x00a020c0, 0x00e020c0, 0x00e020c0 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0xc0000000, 0xc0000000, 0xc0000000 } },
{ 6, 0x989c, { 0x0f000000, 0x0f000000, 0x0f000000 } },
{ 6, 0x989c, { 0x02000000, 0x02000000, 0x02000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0xf8000000, 0xf8000000, 0xf8000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x95150000, 0x95150000, 0x95150000 } },
{ 6, 0x989c, { 0xc1000000, 0xc1000000, 0xc1000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00080000, 0x00080000, 0x00080000 } },
{ 6, 0x989c, { 0x00d50000, 0x00d50000, 0x00d50000 } },
{ 6, 0x989c, { 0x000e0000, 0x000e0000, 0x000e0000 } },
{ 6, 0x989c, { 0x00dc0000, 0x00dc0000, 0x00dc0000 } },
{ 6, 0x989c, { 0x00770000, 0x00770000, 0x00770000 } },
{ 6, 0x989c, { 0x008a0000, 0x008a0000, 0x008a0000 } },
{ 6, 0x989c, { 0x10880000, 0x10880000, 0x10880000 } },
{ 6, 0x989c, { 0x008c0060, 0x008c0060, 0x008c0060 } },
{ 6, 0x989c, { 0x00a00000, 0x00a00080, 0x00a00080 } },
{ 6, 0x989c, { 0x00400000, 0x0040000d, 0x0040000d } },
{ 6, 0x989c, { 0x00110400, 0x00110400, 0x00110400 } },
{ 6, 0x989c, { 0x00000060, 0x00000060, 0x00000060 } },
{ 6, 0x989c, { 0x00000001, 0x00000001, 0x00000001 } },
{ 6, 0x989c, { 0x00000b00, 0x00000b00, 0x00000b00 } },
{ 6, 0x989c, { 0x00000be8, 0x00000be8, 0x00000be8 } },
{ 6, 0x98c0, { 0x00010000, 0x00010000, 0x00010000 } },
{ 7, 0x989c, { 0x00006400, 0x00006400, 0x00006400 } },
{ 7, 0x989c, { 0x00000800, 0x00000800, 0x00000800 } },
{ 7, 0x98cc, { 0x0000000e, 0x0000000e, 0x0000000e } },
};
/******************************\
* RF5413/RF5424 (Eagle/Condor) *
\******************************/
/* BANK 6 len pos col */
#define AR5K_RF5413_OB_2GHZ { 3, 241, 0 }
#define AR5K_RF5413_DB_2GHZ { 3, 238, 0 }
#define AR5K_RF5413_OB_5GHZ { 3, 247, 0 }
#define AR5K_RF5413_DB_5GHZ { 3, 244, 0 }
#define AR5K_RF5413_PWD_ICLOBUF2G { 3, 131, 3 }
#define AR5K_RF5413_DERBY_CHAN_SEL_MODE { 1, 291, 2 }
static const struct ath5k_rf_reg rf_regs_5413[] = {
{6, AR5K_RF_OB_2GHZ, AR5K_RF5413_OB_2GHZ},
{6, AR5K_RF_DB_2GHZ, AR5K_RF5413_DB_2GHZ},
{6, AR5K_RF_OB_5GHZ, AR5K_RF5413_OB_5GHZ},
{6, AR5K_RF_DB_5GHZ, AR5K_RF5413_DB_5GHZ},
{6, AR5K_RF_PWD_ICLOBUF_2G, AR5K_RF5413_PWD_ICLOBUF2G},
{6, AR5K_RF_DERBY_CHAN_SEL_MODE, AR5K_RF5413_DERBY_CHAN_SEL_MODE},
};
/* Default mode specific settings */
static const struct ath5k_ini_rfbuffer rfb_5413[] = {
/* BANK / C.R. A/XR B G */
{ 1, 0x98d4, { 0x00000020, 0x00000020, 0x00000020 } },
{ 2, 0x98d0, { 0x00000008, 0x00000008, 0x00000008 } },
{ 3, 0x98dc, { 0x00a000c0, 0x00e000c0, 0x00e000c0 } },
{ 6, 0x989c, { 0x33000000, 0x33000000, 0x33000000 } },
{ 6, 0x989c, { 0x01000000, 0x01000000, 0x01000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x1f000000, 0x1f000000, 0x1f000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00b80000, 0x00b80000, 0x00b80000 } },
{ 6, 0x989c, { 0x00b70000, 0x00b70000, 0x00b70000 } },
{ 6, 0x989c, { 0x00840000, 0x00840000, 0x00840000 } },
{ 6, 0x989c, { 0x00980000, 0x00980000, 0x00980000 } },
{ 6, 0x989c, { 0x00c00000, 0x00c00000, 0x00c00000 } },
{ 6, 0x989c, { 0x00ff0000, 0x00ff0000, 0x00ff0000 } },
{ 6, 0x989c, { 0x00ff0000, 0x00ff0000, 0x00ff0000 } },
{ 6, 0x989c, { 0x00ff0000, 0x00ff0000, 0x00ff0000 } },
{ 6, 0x989c, { 0x00ff0000, 0x00ff0000, 0x00ff0000 } },
{ 6, 0x989c, { 0x00d70000, 0x00d70000, 0x00d70000 } },
{ 6, 0x989c, { 0x00610000, 0x00610000, 0x00610000 } },
{ 6, 0x989c, { 0x00fe0000, 0x00fe0000, 0x00fe0000 } },
{ 6, 0x989c, { 0x00de0000, 0x00de0000, 0x00de0000 } },
{ 6, 0x989c, { 0x007f0000, 0x007f0000, 0x007f0000 } },
{ 6, 0x989c, { 0x043d0000, 0x043d0000, 0x043d0000 } },
{ 6, 0x989c, { 0x00770000, 0x00770000, 0x00770000 } },
{ 6, 0x989c, { 0x00440000, 0x00440000, 0x00440000 } },
{ 6, 0x989c, { 0x00980000, 0x00980000, 0x00980000 } },
{ 6, 0x989c, { 0x00100080, 0x00100080, 0x00100080 } },
{ 6, 0x989c, { 0x0005c034, 0x0005c034, 0x0005c034 } },
{ 6, 0x989c, { 0x003100f0, 0x003100f0, 0x003100f0 } },
{ 6, 0x989c, { 0x000c011f, 0x000c011f, 0x000c011f } },
{ 6, 0x989c, { 0x00510040, 0x00510040, 0x00510040 } },
{ 6, 0x989c, { 0x005000da, 0x005000da, 0x005000da } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00004044, 0x00004044, 0x00004044 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x000060c0, 0x000060c0, 0x000060c0 } },
{ 6, 0x989c, { 0x00002c00, 0x00003600, 0x00003600 } },
{ 6, 0x98c8, { 0x00000403, 0x00040403, 0x00040403 } },
{ 7, 0x989c, { 0x00006400, 0x00006400, 0x00006400 } },
{ 7, 0x989c, { 0x00000800, 0x00000800, 0x00000800 } },
{ 7, 0x98cc, { 0x0000000e, 0x0000000e, 0x0000000e } },
};
/***************************\
* RF2425/RF2417 (Swan/Nala) *
* AR2317 (Spider SoC) *
\***************************/
/* BANK 2 len pos col */
#define AR5K_RF2425_RF_TURBO { 1, 1, 2 }
/* BANK 6 len pos col */
#define AR5K_RF2425_OB_2GHZ { 3, 193, 0 }
#define AR5K_RF2425_DB_2GHZ { 3, 190, 0 }
static const struct ath5k_rf_reg rf_regs_2425[] = {
{2, AR5K_RF_TURBO, AR5K_RF2425_RF_TURBO},
{6, AR5K_RF_OB_2GHZ, AR5K_RF2425_OB_2GHZ},
{6, AR5K_RF_DB_2GHZ, AR5K_RF2425_DB_2GHZ},
};
/* Default mode specific settings
*/
static const struct ath5k_ini_rfbuffer rfb_2425[] = {
/* BANK / C.R. A/XR B G */
{ 1, 0x98d4, { 0x00000020, 0x00000020, 0x00000020 } },
{ 2, 0x98d0, { 0x02001408, 0x02001408, 0x02001408 } },
{ 3, 0x98dc, { 0x00a020c0, 0x00e020c0, 0x00e020c0 } },
{ 6, 0x989c, { 0x10000000, 0x10000000, 0x10000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x002a0000, 0x002a0000, 0x002a0000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00100000, 0x00100000, 0x00100000 } },
{ 6, 0x989c, { 0x00020000, 0x00020000, 0x00020000 } },
{ 6, 0x989c, { 0x00730000, 0x00730000, 0x00730000 } },
{ 6, 0x989c, { 0x00f80000, 0x00f80000, 0x00f80000 } },
{ 6, 0x989c, { 0x00e70000, 0x00e70000, 0x00e70000 } },
{ 6, 0x989c, { 0x00140000, 0x00140000, 0x00140000 } },
{ 6, 0x989c, { 0x00910040, 0x00910040, 0x00910040 } },
{ 6, 0x989c, { 0x0007001a, 0x0007001a, 0x0007001a } },
{ 6, 0x989c, { 0x00410000, 0x00410000, 0x00410000 } },
{ 6, 0x989c, { 0x00810000, 0x00810060, 0x00810060 } },
{ 6, 0x989c, { 0x00020800, 0x00020803, 0x00020803 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00001660, 0x00001660, 0x00001660 } },
{ 6, 0x989c, { 0x00001688, 0x00001688, 0x00001688 } },
{ 6, 0x98c4, { 0x00000001, 0x00000001, 0x00000001 } },
{ 7, 0x989c, { 0x00006400, 0x00006400, 0x00006400 } },
{ 7, 0x989c, { 0x00000800, 0x00000800, 0x00000800 } },
{ 7, 0x98cc, { 0x0000000e, 0x0000000e, 0x0000000e } },
};
/*
* TODO: Handle the few differences with swan during
* bank modification and get rid of this
*/
static const struct ath5k_ini_rfbuffer rfb_2317[] = {
/* BANK / C.R. A/XR B G */
{ 1, 0x98d4, { 0x00000020, 0x00000020, 0x00000020 } },
{ 2, 0x98d0, { 0x02001408, 0x02001408, 0x02001408 } },
{ 3, 0x98dc, { 0x00a020c0, 0x00e020c0, 0x00e020c0 } },
{ 6, 0x989c, { 0x10000000, 0x10000000, 0x10000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x002a0000, 0x002a0000, 0x002a0000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00100000, 0x00100000, 0x00100000 } },
{ 6, 0x989c, { 0x00020000, 0x00020000, 0x00020000 } },
{ 6, 0x989c, { 0x00730000, 0x00730000, 0x00730000 } },
{ 6, 0x989c, { 0x00f80000, 0x00f80000, 0x00f80000 } },
{ 6, 0x989c, { 0x00e70000, 0x00e70000, 0x00e70000 } },
{ 6, 0x989c, { 0x00140100, 0x00140100, 0x00140100 } },
{ 6, 0x989c, { 0x00910040, 0x00910040, 0x00910040 } },
{ 6, 0x989c, { 0x0007001a, 0x0007001a, 0x0007001a } },
{ 6, 0x989c, { 0x00410000, 0x00410000, 0x00410000 } },
{ 6, 0x989c, { 0x00810000, 0x00810060, 0x00810060 } },
{ 6, 0x989c, { 0x00020800, 0x00020803, 0x00020803 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00001660, 0x00001660, 0x00001660 } },
{ 6, 0x989c, { 0x00009688, 0x00009688, 0x00009688 } },
{ 6, 0x98c4, { 0x00000001, 0x00000001, 0x00000001 } },
{ 7, 0x989c, { 0x00006400, 0x00006400, 0x00006400 } },
{ 7, 0x989c, { 0x00000800, 0x00000800, 0x00000800 } },
{ 7, 0x98cc, { 0x0000000e, 0x0000000e, 0x0000000e } },
};
/*
* TODO: Handle the few differences with swan during
* bank modification and get rid of this
*/
static const struct ath5k_ini_rfbuffer rfb_2417[] = {
/* BANK / C.R. A/XR B G */
{ 1, 0x98d4, { 0x00000020, 0x00000020, 0x00000020 } },
{ 2, 0x98d0, { 0x02001408, 0x02001408, 0x02001408 } },
{ 3, 0x98dc, { 0x00a020c0, 0x00e020c0, 0x00e020c0 } },
{ 6, 0x989c, { 0x10000000, 0x10000000, 0x10000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x002a0000, 0x002a0000, 0x002a0000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00100000, 0x00100000, 0x00100000 } },
{ 6, 0x989c, { 0x00020000, 0x00020000, 0x00020000 } },
{ 6, 0x989c, { 0x00730000, 0x00730000, 0x00730000 } },
{ 6, 0x989c, { 0x00f80000, 0x00f80000, 0x00f80000 } },
{ 6, 0x989c, { 0x00e70000, 0x80e70000, 0x80e70000 } },
{ 6, 0x989c, { 0x00140000, 0x00140000, 0x00140000 } },
{ 6, 0x989c, { 0x00910040, 0x00910040, 0x00910040 } },
{ 6, 0x989c, { 0x0007001a, 0x0207001a, 0x0207001a } },
{ 6, 0x989c, { 0x00410000, 0x00410000, 0x00410000 } },
{ 6, 0x989c, { 0x00810000, 0x00810060, 0x00810060 } },
{ 6, 0x989c, { 0x00020800, 0x00020803, 0x00020803 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00000000, 0x00000000, 0x00000000 } },
{ 6, 0x989c, { 0x00001660, 0x00001660, 0x00001660 } },
{ 6, 0x989c, { 0x00001688, 0x00001688, 0x00001688 } },
{ 6, 0x98c4, { 0x00000001, 0x00000001, 0x00000001 } },
{ 7, 0x989c, { 0x00006400, 0x00006400, 0x00006400 } },
{ 7, 0x989c, { 0x00000800, 0x00000800, 0x00000800 } },
{ 7, 0x98cc, { 0x0000000e, 0x0000000e, 0x0000000e } },
};
@@ -0,0 +1,534 @@
/*
* RF Gain optimization
*
* Copyright (c) 2004-2009 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2006-2009 Nick Kossifidis <mickflemm@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.
*
*/
/**
* struct ath5k_ini_rfgain - RF Gain table
* @rfg_register: RF Gain register address
* @rfg_value: Register value for 5 and 2GHz
*
* Mode-specific RF Gain table (64bytes) for RF5111/5112
* (RF5110 only comes with AR5210 and only supports a/turbo a mode so initial
* RF Gain values are included in AR5K_AR5210_INI)
*/
struct ath5k_ini_rfgain {
u16 rfg_register;
u32 rfg_value[2]; /* [freq (see below)] */
};
/* Initial RF Gain settings for RF5111 */
static const struct ath5k_ini_rfgain rfgain_5111[] = {
/* 5GHz 2GHz */
{ AR5K_RF_GAIN(0), { 0x000001a9, 0x00000000 } },
{ AR5K_RF_GAIN(1), { 0x000001e9, 0x00000040 } },
{ AR5K_RF_GAIN(2), { 0x00000029, 0x00000080 } },
{ AR5K_RF_GAIN(3), { 0x00000069, 0x00000150 } },
{ AR5K_RF_GAIN(4), { 0x00000199, 0x00000190 } },
{ AR5K_RF_GAIN(5), { 0x000001d9, 0x000001d0 } },
{ AR5K_RF_GAIN(6), { 0x00000019, 0x00000010 } },
{ AR5K_RF_GAIN(7), { 0x00000059, 0x00000044 } },
{ AR5K_RF_GAIN(8), { 0x00000099, 0x00000084 } },
{ AR5K_RF_GAIN(9), { 0x000001a5, 0x00000148 } },
{ AR5K_RF_GAIN(10), { 0x000001e5, 0x00000188 } },
{ AR5K_RF_GAIN(11), { 0x00000025, 0x000001c8 } },
{ AR5K_RF_GAIN(12), { 0x000001c8, 0x00000014 } },
{ AR5K_RF_GAIN(13), { 0x00000008, 0x00000042 } },
{ AR5K_RF_GAIN(14), { 0x00000048, 0x00000082 } },
{ AR5K_RF_GAIN(15), { 0x00000088, 0x00000178 } },
{ AR5K_RF_GAIN(16), { 0x00000198, 0x000001b8 } },
{ AR5K_RF_GAIN(17), { 0x000001d8, 0x000001f8 } },
{ AR5K_RF_GAIN(18), { 0x00000018, 0x00000012 } },
{ AR5K_RF_GAIN(19), { 0x00000058, 0x00000052 } },
{ AR5K_RF_GAIN(20), { 0x00000098, 0x00000092 } },
{ AR5K_RF_GAIN(21), { 0x000001a4, 0x0000017c } },
{ AR5K_RF_GAIN(22), { 0x000001e4, 0x000001bc } },
{ AR5K_RF_GAIN(23), { 0x00000024, 0x000001fc } },
{ AR5K_RF_GAIN(24), { 0x00000064, 0x0000000a } },
{ AR5K_RF_GAIN(25), { 0x000000a4, 0x0000004a } },
{ AR5K_RF_GAIN(26), { 0x000000e4, 0x0000008a } },
{ AR5K_RF_GAIN(27), { 0x0000010a, 0x0000015a } },
{ AR5K_RF_GAIN(28), { 0x0000014a, 0x0000019a } },
{ AR5K_RF_GAIN(29), { 0x0000018a, 0x000001da } },
{ AR5K_RF_GAIN(30), { 0x000001ca, 0x0000000e } },
{ AR5K_RF_GAIN(31), { 0x0000000a, 0x0000004e } },
{ AR5K_RF_GAIN(32), { 0x0000004a, 0x0000008e } },
{ AR5K_RF_GAIN(33), { 0x0000008a, 0x0000015e } },
{ AR5K_RF_GAIN(34), { 0x000001ba, 0x0000019e } },
{ AR5K_RF_GAIN(35), { 0x000001fa, 0x000001de } },
{ AR5K_RF_GAIN(36), { 0x0000003a, 0x00000009 } },
{ AR5K_RF_GAIN(37), { 0x0000007a, 0x00000049 } },
{ AR5K_RF_GAIN(38), { 0x00000186, 0x00000089 } },
{ AR5K_RF_GAIN(39), { 0x000001c6, 0x00000179 } },
{ AR5K_RF_GAIN(40), { 0x00000006, 0x000001b9 } },
{ AR5K_RF_GAIN(41), { 0x00000046, 0x000001f9 } },
{ AR5K_RF_GAIN(42), { 0x00000086, 0x00000039 } },
{ AR5K_RF_GAIN(43), { 0x000000c6, 0x00000079 } },
{ AR5K_RF_GAIN(44), { 0x000000c6, 0x000000b9 } },
{ AR5K_RF_GAIN(45), { 0x000000c6, 0x000001bd } },
{ AR5K_RF_GAIN(46), { 0x000000c6, 0x000001fd } },
{ AR5K_RF_GAIN(47), { 0x000000c6, 0x0000003d } },
{ AR5K_RF_GAIN(48), { 0x000000c6, 0x0000007d } },
{ AR5K_RF_GAIN(49), { 0x000000c6, 0x000000bd } },
{ AR5K_RF_GAIN(50), { 0x000000c6, 0x000000fd } },
{ AR5K_RF_GAIN(51), { 0x000000c6, 0x000000fd } },
{ AR5K_RF_GAIN(52), { 0x000000c6, 0x000000fd } },
{ AR5K_RF_GAIN(53), { 0x000000c6, 0x000000fd } },
{ AR5K_RF_GAIN(54), { 0x000000c6, 0x000000fd } },
{ AR5K_RF_GAIN(55), { 0x000000c6, 0x000000fd } },
{ AR5K_RF_GAIN(56), { 0x000000c6, 0x000000fd } },
{ AR5K_RF_GAIN(57), { 0x000000c6, 0x000000fd } },
{ AR5K_RF_GAIN(58), { 0x000000c6, 0x000000fd } },
{ AR5K_RF_GAIN(59), { 0x000000c6, 0x000000fd } },
{ AR5K_RF_GAIN(60), { 0x000000c6, 0x000000fd } },
{ AR5K_RF_GAIN(61), { 0x000000c6, 0x000000fd } },
{ AR5K_RF_GAIN(62), { 0x000000c6, 0x000000fd } },
{ AR5K_RF_GAIN(63), { 0x000000c6, 0x000000fd } },
};
/* Initial RF Gain settings for RF5112 */
static const struct ath5k_ini_rfgain rfgain_5112[] = {
/* 5GHz 2GHz */
{ AR5K_RF_GAIN(0), { 0x00000007, 0x00000007 } },
{ AR5K_RF_GAIN(1), { 0x00000047, 0x00000047 } },
{ AR5K_RF_GAIN(2), { 0x00000087, 0x00000087 } },
{ AR5K_RF_GAIN(3), { 0x000001a0, 0x000001a0 } },
{ AR5K_RF_GAIN(4), { 0x000001e0, 0x000001e0 } },
{ AR5K_RF_GAIN(5), { 0x00000020, 0x00000020 } },
{ AR5K_RF_GAIN(6), { 0x00000060, 0x00000060 } },
{ AR5K_RF_GAIN(7), { 0x000001a1, 0x000001a1 } },
{ AR5K_RF_GAIN(8), { 0x000001e1, 0x000001e1 } },
{ AR5K_RF_GAIN(9), { 0x00000021, 0x00000021 } },
{ AR5K_RF_GAIN(10), { 0x00000061, 0x00000061 } },
{ AR5K_RF_GAIN(11), { 0x00000162, 0x00000162 } },
{ AR5K_RF_GAIN(12), { 0x000001a2, 0x000001a2 } },
{ AR5K_RF_GAIN(13), { 0x000001e2, 0x000001e2 } },
{ AR5K_RF_GAIN(14), { 0x00000022, 0x00000022 } },
{ AR5K_RF_GAIN(15), { 0x00000062, 0x00000062 } },
{ AR5K_RF_GAIN(16), { 0x00000163, 0x00000163 } },
{ AR5K_RF_GAIN(17), { 0x000001a3, 0x000001a3 } },
{ AR5K_RF_GAIN(18), { 0x000001e3, 0x000001e3 } },
{ AR5K_RF_GAIN(19), { 0x00000023, 0x00000023 } },
{ AR5K_RF_GAIN(20), { 0x00000063, 0x00000063 } },
{ AR5K_RF_GAIN(21), { 0x00000184, 0x00000184 } },
{ AR5K_RF_GAIN(22), { 0x000001c4, 0x000001c4 } },
{ AR5K_RF_GAIN(23), { 0x00000004, 0x00000004 } },
{ AR5K_RF_GAIN(24), { 0x000001ea, 0x0000000b } },
{ AR5K_RF_GAIN(25), { 0x0000002a, 0x0000004b } },
{ AR5K_RF_GAIN(26), { 0x0000006a, 0x0000008b } },
{ AR5K_RF_GAIN(27), { 0x000000aa, 0x000001ac } },
{ AR5K_RF_GAIN(28), { 0x000001ab, 0x000001ec } },
{ AR5K_RF_GAIN(29), { 0x000001eb, 0x0000002c } },
{ AR5K_RF_GAIN(30), { 0x0000002b, 0x00000012 } },
{ AR5K_RF_GAIN(31), { 0x0000006b, 0x00000052 } },
{ AR5K_RF_GAIN(32), { 0x000000ab, 0x00000092 } },
{ AR5K_RF_GAIN(33), { 0x000001ac, 0x00000193 } },
{ AR5K_RF_GAIN(34), { 0x000001ec, 0x000001d3 } },
{ AR5K_RF_GAIN(35), { 0x0000002c, 0x00000013 } },
{ AR5K_RF_GAIN(36), { 0x0000003a, 0x00000053 } },
{ AR5K_RF_GAIN(37), { 0x0000007a, 0x00000093 } },
{ AR5K_RF_GAIN(38), { 0x000000ba, 0x00000194 } },
{ AR5K_RF_GAIN(39), { 0x000001bb, 0x000001d4 } },
{ AR5K_RF_GAIN(40), { 0x000001fb, 0x00000014 } },
{ AR5K_RF_GAIN(41), { 0x0000003b, 0x0000003a } },
{ AR5K_RF_GAIN(42), { 0x0000007b, 0x0000007a } },
{ AR5K_RF_GAIN(43), { 0x000000bb, 0x000000ba } },
{ AR5K_RF_GAIN(44), { 0x000001bc, 0x000001bb } },
{ AR5K_RF_GAIN(45), { 0x000001fc, 0x000001fb } },
{ AR5K_RF_GAIN(46), { 0x0000003c, 0x0000003b } },
{ AR5K_RF_GAIN(47), { 0x0000007c, 0x0000007b } },
{ AR5K_RF_GAIN(48), { 0x000000bc, 0x000000bb } },
{ AR5K_RF_GAIN(49), { 0x000000fc, 0x000001bc } },
{ AR5K_RF_GAIN(50), { 0x000000fc, 0x000001fc } },
{ AR5K_RF_GAIN(51), { 0x000000fc, 0x0000003c } },
{ AR5K_RF_GAIN(52), { 0x000000fc, 0x0000007c } },
{ AR5K_RF_GAIN(53), { 0x000000fc, 0x000000bc } },
{ AR5K_RF_GAIN(54), { 0x000000fc, 0x000000fc } },
{ AR5K_RF_GAIN(55), { 0x000000fc, 0x000000fc } },
{ AR5K_RF_GAIN(56), { 0x000000fc, 0x000000fc } },
{ AR5K_RF_GAIN(57), { 0x000000fc, 0x000000fc } },
{ AR5K_RF_GAIN(58), { 0x000000fc, 0x000000fc } },
{ AR5K_RF_GAIN(59), { 0x000000fc, 0x000000fc } },
{ AR5K_RF_GAIN(60), { 0x000000fc, 0x000000fc } },
{ AR5K_RF_GAIN(61), { 0x000000fc, 0x000000fc } },
{ AR5K_RF_GAIN(62), { 0x000000fc, 0x000000fc } },
{ AR5K_RF_GAIN(63), { 0x000000fc, 0x000000fc } },
};
/* Initial RF Gain settings for RF2413 */
static const struct ath5k_ini_rfgain rfgain_2413[] = {
{ AR5K_RF_GAIN(0), { 0x00000000, 0x00000000 } },
{ AR5K_RF_GAIN(1), { 0x00000000, 0x00000040 } },
{ AR5K_RF_GAIN(2), { 0x00000000, 0x00000080 } },
{ AR5K_RF_GAIN(3), { 0x00000000, 0x00000181 } },
{ AR5K_RF_GAIN(4), { 0x00000000, 0x000001c1 } },
{ AR5K_RF_GAIN(5), { 0x00000000, 0x00000001 } },
{ AR5K_RF_GAIN(6), { 0x00000000, 0x00000041 } },
{ AR5K_RF_GAIN(7), { 0x00000000, 0x00000081 } },
{ AR5K_RF_GAIN(8), { 0x00000000, 0x00000168 } },
{ AR5K_RF_GAIN(9), { 0x00000000, 0x000001a8 } },
{ AR5K_RF_GAIN(10), { 0x00000000, 0x000001e8 } },
{ AR5K_RF_GAIN(11), { 0x00000000, 0x00000028 } },
{ AR5K_RF_GAIN(12), { 0x00000000, 0x00000068 } },
{ AR5K_RF_GAIN(13), { 0x00000000, 0x00000189 } },
{ AR5K_RF_GAIN(14), { 0x00000000, 0x000001c9 } },
{ AR5K_RF_GAIN(15), { 0x00000000, 0x00000009 } },
{ AR5K_RF_GAIN(16), { 0x00000000, 0x00000049 } },
{ AR5K_RF_GAIN(17), { 0x00000000, 0x00000089 } },
{ AR5K_RF_GAIN(18), { 0x00000000, 0x00000190 } },
{ AR5K_RF_GAIN(19), { 0x00000000, 0x000001d0 } },
{ AR5K_RF_GAIN(20), { 0x00000000, 0x00000010 } },
{ AR5K_RF_GAIN(21), { 0x00000000, 0x00000050 } },
{ AR5K_RF_GAIN(22), { 0x00000000, 0x00000090 } },
{ AR5K_RF_GAIN(23), { 0x00000000, 0x00000191 } },
{ AR5K_RF_GAIN(24), { 0x00000000, 0x000001d1 } },
{ AR5K_RF_GAIN(25), { 0x00000000, 0x00000011 } },
{ AR5K_RF_GAIN(26), { 0x00000000, 0x00000051 } },
{ AR5K_RF_GAIN(27), { 0x00000000, 0x00000091 } },
{ AR5K_RF_GAIN(28), { 0x00000000, 0x00000178 } },
{ AR5K_RF_GAIN(29), { 0x00000000, 0x000001b8 } },
{ AR5K_RF_GAIN(30), { 0x00000000, 0x000001f8 } },
{ AR5K_RF_GAIN(31), { 0x00000000, 0x00000038 } },
{ AR5K_RF_GAIN(32), { 0x00000000, 0x00000078 } },
{ AR5K_RF_GAIN(33), { 0x00000000, 0x00000199 } },
{ AR5K_RF_GAIN(34), { 0x00000000, 0x000001d9 } },
{ AR5K_RF_GAIN(35), { 0x00000000, 0x00000019 } },
{ AR5K_RF_GAIN(36), { 0x00000000, 0x00000059 } },
{ AR5K_RF_GAIN(37), { 0x00000000, 0x00000099 } },
{ AR5K_RF_GAIN(38), { 0x00000000, 0x000000d9 } },
{ AR5K_RF_GAIN(39), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(40), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(41), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(42), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(43), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(44), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(45), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(46), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(47), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(48), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(49), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(50), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(51), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(52), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(53), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(54), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(55), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(56), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(57), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(58), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(59), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(60), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(61), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(62), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(63), { 0x00000000, 0x000000f9 } },
};
/* Initial RF Gain settings for AR2316 */
static const struct ath5k_ini_rfgain rfgain_2316[] = {
{ AR5K_RF_GAIN(0), { 0x00000000, 0x00000000 } },
{ AR5K_RF_GAIN(1), { 0x00000000, 0x00000040 } },
{ AR5K_RF_GAIN(2), { 0x00000000, 0x00000080 } },
{ AR5K_RF_GAIN(3), { 0x00000000, 0x000000c0 } },
{ AR5K_RF_GAIN(4), { 0x00000000, 0x000000e0 } },
{ AR5K_RF_GAIN(5), { 0x00000000, 0x000000e0 } },
{ AR5K_RF_GAIN(6), { 0x00000000, 0x00000128 } },
{ AR5K_RF_GAIN(7), { 0x00000000, 0x00000128 } },
{ AR5K_RF_GAIN(8), { 0x00000000, 0x00000128 } },
{ AR5K_RF_GAIN(9), { 0x00000000, 0x00000168 } },
{ AR5K_RF_GAIN(10), { 0x00000000, 0x000001a8 } },
{ AR5K_RF_GAIN(11), { 0x00000000, 0x000001e8 } },
{ AR5K_RF_GAIN(12), { 0x00000000, 0x00000028 } },
{ AR5K_RF_GAIN(13), { 0x00000000, 0x00000068 } },
{ AR5K_RF_GAIN(14), { 0x00000000, 0x000000a8 } },
{ AR5K_RF_GAIN(15), { 0x00000000, 0x000000e8 } },
{ AR5K_RF_GAIN(16), { 0x00000000, 0x000000e8 } },
{ AR5K_RF_GAIN(17), { 0x00000000, 0x00000130 } },
{ AR5K_RF_GAIN(18), { 0x00000000, 0x00000130 } },
{ AR5K_RF_GAIN(19), { 0x00000000, 0x00000170 } },
{ AR5K_RF_GAIN(20), { 0x00000000, 0x000001b0 } },
{ AR5K_RF_GAIN(21), { 0x00000000, 0x000001f0 } },
{ AR5K_RF_GAIN(22), { 0x00000000, 0x00000030 } },
{ AR5K_RF_GAIN(23), { 0x00000000, 0x00000070 } },
{ AR5K_RF_GAIN(24), { 0x00000000, 0x000000b0 } },
{ AR5K_RF_GAIN(25), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(26), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(27), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(28), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(29), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(30), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(31), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(32), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(33), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(34), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(35), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(36), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(37), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(38), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(39), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(40), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(41), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(42), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(43), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(44), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(45), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(46), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(47), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(48), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(49), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(50), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(51), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(52), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(53), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(54), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(55), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(56), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(57), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(58), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(59), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(60), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(61), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(62), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(63), { 0x00000000, 0x000000f0 } },
};
/* Initial RF Gain settings for RF5413 */
static const struct ath5k_ini_rfgain rfgain_5413[] = {
/* 5GHz 2GHz */
{ AR5K_RF_GAIN(0), { 0x00000000, 0x00000000 } },
{ AR5K_RF_GAIN(1), { 0x00000040, 0x00000040 } },
{ AR5K_RF_GAIN(2), { 0x00000080, 0x00000080 } },
{ AR5K_RF_GAIN(3), { 0x000001a1, 0x00000161 } },
{ AR5K_RF_GAIN(4), { 0x000001e1, 0x000001a1 } },
{ AR5K_RF_GAIN(5), { 0x00000021, 0x000001e1 } },
{ AR5K_RF_GAIN(6), { 0x00000061, 0x00000021 } },
{ AR5K_RF_GAIN(7), { 0x00000188, 0x00000061 } },
{ AR5K_RF_GAIN(8), { 0x000001c8, 0x00000188 } },
{ AR5K_RF_GAIN(9), { 0x00000008, 0x000001c8 } },
{ AR5K_RF_GAIN(10), { 0x00000048, 0x00000008 } },
{ AR5K_RF_GAIN(11), { 0x00000088, 0x00000048 } },
{ AR5K_RF_GAIN(12), { 0x000001a9, 0x00000088 } },
{ AR5K_RF_GAIN(13), { 0x000001e9, 0x00000169 } },
{ AR5K_RF_GAIN(14), { 0x00000029, 0x000001a9 } },
{ AR5K_RF_GAIN(15), { 0x00000069, 0x000001e9 } },
{ AR5K_RF_GAIN(16), { 0x000001d0, 0x00000029 } },
{ AR5K_RF_GAIN(17), { 0x00000010, 0x00000069 } },
{ AR5K_RF_GAIN(18), { 0x00000050, 0x00000190 } },
{ AR5K_RF_GAIN(19), { 0x00000090, 0x000001d0 } },
{ AR5K_RF_GAIN(20), { 0x000001b1, 0x00000010 } },
{ AR5K_RF_GAIN(21), { 0x000001f1, 0x00000050 } },
{ AR5K_RF_GAIN(22), { 0x00000031, 0x00000090 } },
{ AR5K_RF_GAIN(23), { 0x00000071, 0x00000171 } },
{ AR5K_RF_GAIN(24), { 0x000001b8, 0x000001b1 } },
{ AR5K_RF_GAIN(25), { 0x000001f8, 0x000001f1 } },
{ AR5K_RF_GAIN(26), { 0x00000038, 0x00000031 } },
{ AR5K_RF_GAIN(27), { 0x00000078, 0x00000071 } },
{ AR5K_RF_GAIN(28), { 0x00000199, 0x00000198 } },
{ AR5K_RF_GAIN(29), { 0x000001d9, 0x000001d8 } },
{ AR5K_RF_GAIN(30), { 0x00000019, 0x00000018 } },
{ AR5K_RF_GAIN(31), { 0x00000059, 0x00000058 } },
{ AR5K_RF_GAIN(32), { 0x00000099, 0x00000098 } },
{ AR5K_RF_GAIN(33), { 0x000000d9, 0x00000179 } },
{ AR5K_RF_GAIN(34), { 0x000000f9, 0x000001b9 } },
{ AR5K_RF_GAIN(35), { 0x000000f9, 0x000001f9 } },
{ AR5K_RF_GAIN(36), { 0x000000f9, 0x00000039 } },
{ AR5K_RF_GAIN(37), { 0x000000f9, 0x00000079 } },
{ AR5K_RF_GAIN(38), { 0x000000f9, 0x000000b9 } },
{ AR5K_RF_GAIN(39), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(40), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(41), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(42), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(43), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(44), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(45), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(46), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(47), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(48), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(49), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(50), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(51), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(52), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(53), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(54), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(55), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(56), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(57), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(58), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(59), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(60), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(61), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(62), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(63), { 0x000000f9, 0x000000f9 } },
};
/* Initial RF Gain settings for RF2425 */
static const struct ath5k_ini_rfgain rfgain_2425[] = {
{ AR5K_RF_GAIN(0), { 0x00000000, 0x00000000 } },
{ AR5K_RF_GAIN(1), { 0x00000000, 0x00000040 } },
{ AR5K_RF_GAIN(2), { 0x00000000, 0x00000080 } },
{ AR5K_RF_GAIN(3), { 0x00000000, 0x00000181 } },
{ AR5K_RF_GAIN(4), { 0x00000000, 0x000001c1 } },
{ AR5K_RF_GAIN(5), { 0x00000000, 0x00000001 } },
{ AR5K_RF_GAIN(6), { 0x00000000, 0x00000041 } },
{ AR5K_RF_GAIN(7), { 0x00000000, 0x00000081 } },
{ AR5K_RF_GAIN(8), { 0x00000000, 0x00000188 } },
{ AR5K_RF_GAIN(9), { 0x00000000, 0x000001c8 } },
{ AR5K_RF_GAIN(10), { 0x00000000, 0x00000008 } },
{ AR5K_RF_GAIN(11), { 0x00000000, 0x00000048 } },
{ AR5K_RF_GAIN(12), { 0x00000000, 0x00000088 } },
{ AR5K_RF_GAIN(13), { 0x00000000, 0x00000189 } },
{ AR5K_RF_GAIN(14), { 0x00000000, 0x000001c9 } },
{ AR5K_RF_GAIN(15), { 0x00000000, 0x00000009 } },
{ AR5K_RF_GAIN(16), { 0x00000000, 0x00000049 } },
{ AR5K_RF_GAIN(17), { 0x00000000, 0x00000089 } },
{ AR5K_RF_GAIN(18), { 0x00000000, 0x000001b0 } },
{ AR5K_RF_GAIN(19), { 0x00000000, 0x000001f0 } },
{ AR5K_RF_GAIN(20), { 0x00000000, 0x00000030 } },
{ AR5K_RF_GAIN(21), { 0x00000000, 0x00000070 } },
{ AR5K_RF_GAIN(22), { 0x00000000, 0x00000171 } },
{ AR5K_RF_GAIN(23), { 0x00000000, 0x000001b1 } },
{ AR5K_RF_GAIN(24), { 0x00000000, 0x000001f1 } },
{ AR5K_RF_GAIN(25), { 0x00000000, 0x00000031 } },
{ AR5K_RF_GAIN(26), { 0x00000000, 0x00000071 } },
{ AR5K_RF_GAIN(27), { 0x00000000, 0x000001b8 } },
{ AR5K_RF_GAIN(28), { 0x00000000, 0x000001f8 } },
{ AR5K_RF_GAIN(29), { 0x00000000, 0x00000038 } },
{ AR5K_RF_GAIN(30), { 0x00000000, 0x00000078 } },
{ AR5K_RF_GAIN(31), { 0x00000000, 0x000000b8 } },
{ AR5K_RF_GAIN(32), { 0x00000000, 0x000001b9 } },
{ AR5K_RF_GAIN(33), { 0x00000000, 0x000001f9 } },
{ AR5K_RF_GAIN(34), { 0x00000000, 0x00000039 } },
{ AR5K_RF_GAIN(35), { 0x00000000, 0x00000079 } },
{ AR5K_RF_GAIN(36), { 0x00000000, 0x000000b9 } },
{ AR5K_RF_GAIN(37), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(38), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(39), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(40), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(41), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(42), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(43), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(44), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(45), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(46), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(47), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(48), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(49), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(50), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(51), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(52), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(53), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(54), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(55), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(56), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(57), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(58), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(59), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(60), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(61), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(62), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(63), { 0x00000000, 0x000000f9 } },
};
#define AR5K_GAIN_CRN_FIX_BITS_5111 4
#define AR5K_GAIN_CRN_FIX_BITS_5112 7
#define AR5K_GAIN_CRN_MAX_FIX_BITS AR5K_GAIN_CRN_FIX_BITS_5112
#define AR5K_GAIN_DYN_ADJUST_HI_MARGIN 15
#define AR5K_GAIN_DYN_ADJUST_LO_MARGIN 20
#define AR5K_GAIN_CCK_PROBE_CORR 5
#define AR5K_GAIN_CCK_OFDM_GAIN_DELTA 15
#define AR5K_GAIN_STEP_COUNT 10
/* Check if our current measurement is inside our
* current variable attenuation window */
#define AR5K_GAIN_CHECK_ADJUST(_g) \
((_g)->g_current <= (_g)->g_low || (_g)->g_current >= (_g)->g_high)
/**
* struct ath5k_gain_opt_step - An RF gain optimization step
* @gos_param: Set of parameters
* @gos_gain: Gain
*/
struct ath5k_gain_opt_step {
s8 gos_param[AR5K_GAIN_CRN_MAX_FIX_BITS];
s8 gos_gain;
};
/**
* struct ath5k_gain_opt - RF Gain optimization ladder
* @go_default: The default step
* @go_steps_count: How many optimization steps
* @go_step: Array of &struct ath5k_gain_opt_step
*/
struct ath5k_gain_opt {
u8 go_default;
u8 go_steps_count;
const struct ath5k_gain_opt_step go_step[AR5K_GAIN_STEP_COUNT];
};
/*
* RF5111
* Parameters on gos_param:
* 1) Tx clip PHY register
* 2) PWD 90 RF register
* 3) PWD 84 RF register
* 4) RFGainSel RF register
*/
static const struct ath5k_gain_opt rfgain_opt_5111 = {
4,
9,
{
{ { 4, 1, 1, 1 }, 6 },
{ { 4, 0, 1, 1 }, 4 },
{ { 3, 1, 1, 1 }, 3 },
{ { 4, 0, 0, 1 }, 1 },
{ { 4, 1, 1, 0 }, 0 },
{ { 4, 0, 1, 0 }, -2 },
{ { 3, 1, 1, 0 }, -3 },
{ { 4, 0, 0, 0 }, -4 },
{ { 2, 1, 1, 0 }, -6 }
}
};
/*
* RF5112
* Parameters on gos_param:
* 1) Mixgain ovr RF register
* 2) PWD 138 RF register
* 3) PWD 137 RF register
* 4) PWD 136 RF register
* 5) PWD 132 RF register
* 6) PWD 131 RF register
* 7) PWD 130 RF register
*/
static const struct ath5k_gain_opt rfgain_opt_5112 = {
1,
8,
{
{ { 3, 0, 0, 0, 0, 0, 0 }, 6 },
{ { 2, 0, 0, 0, 0, 0, 0 }, 0 },
{ { 1, 0, 0, 0, 0, 0, 0 }, -3 },
{ { 0, 0, 0, 0, 0, 0, 0 }, -6 },
{ { 0, 1, 1, 0, 0, 0, 0 }, -8 },
{ { 0, 1, 1, 0, 1, 1, 0 }, -10 },
{ { 0, 1, 0, 1, 1, 1, 0 }, -13 },
{ { 0, 1, 0, 1, 1, 0, 1 }, -16 },
}
};
@@ -0,0 +1,115 @@
/*
* RFKILL support for ath5k
*
* Copyright (c) 2009 Tobias Doerffel <tobias.doerffel@gmail.com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
* redistribution must be conditioned upon including a substantially
* similar Disclaimer requirement for further binary redistribution.
* 3. Neither the names of the above-listed copyright holders nor the names
* of any contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGES.
*/
#include "ath5k.h"
static inline void ath5k_rfkill_disable(struct ath5k_hw *ah)
{
ATH5K_DBG(ah, ATH5K_DEBUG_ANY, "rfkill disable (gpio:%d polarity:%d)\n",
ah->rf_kill.gpio, ah->rf_kill.polarity);
ath5k_hw_set_gpio_output(ah, ah->rf_kill.gpio);
ath5k_hw_set_gpio(ah, ah->rf_kill.gpio, !ah->rf_kill.polarity);
}
static inline void ath5k_rfkill_enable(struct ath5k_hw *ah)
{
ATH5K_DBG(ah, ATH5K_DEBUG_ANY, "rfkill enable (gpio:%d polarity:%d)\n",
ah->rf_kill.gpio, ah->rf_kill.polarity);
ath5k_hw_set_gpio_output(ah, ah->rf_kill.gpio);
ath5k_hw_set_gpio(ah, ah->rf_kill.gpio, ah->rf_kill.polarity);
}
static inline void ath5k_rfkill_set_intr(struct ath5k_hw *ah, bool enable)
{
u32 curval;
ath5k_hw_set_gpio_input(ah, ah->rf_kill.gpio);
curval = ath5k_hw_get_gpio(ah, ah->rf_kill.gpio);
ath5k_hw_set_gpio_intr(ah, ah->rf_kill.gpio, enable ?
!!curval : !curval);
}
static bool
ath5k_is_rfkill_set(struct ath5k_hw *ah)
{
/* configuring GPIO for input for some reason disables rfkill */
/*ath5k_hw_set_gpio_input(ah, ah->rf_kill.gpio);*/
return ath5k_hw_get_gpio(ah, ah->rf_kill.gpio) ==
ah->rf_kill.polarity;
}
static void
ath5k_tasklet_rfkill_toggle(struct tasklet_struct *t)
{
struct ath5k_hw *ah = from_tasklet(ah, t, rf_kill.toggleq);
bool blocked;
blocked = ath5k_is_rfkill_set(ah);
wiphy_rfkill_set_hw_state(ah->hw->wiphy, blocked);
}
void
ath5k_rfkill_hw_start(struct ath5k_hw *ah)
{
/* read rfkill GPIO configuration from EEPROM header */
ah->rf_kill.gpio = ah->ah_capabilities.cap_eeprom.ee_rfkill_pin;
ah->rf_kill.polarity = ah->ah_capabilities.cap_eeprom.ee_rfkill_pol;
tasklet_setup(&ah->rf_kill.toggleq, ath5k_tasklet_rfkill_toggle);
ath5k_rfkill_disable(ah);
/* enable interrupt for rfkill switch */
if (AR5K_EEPROM_HDR_RFKILL(ah->ah_capabilities.cap_eeprom.ee_header))
ath5k_rfkill_set_intr(ah, true);
}
void
ath5k_rfkill_hw_stop(struct ath5k_hw *ah)
{
/* disable interrupt for rfkill switch */
if (AR5K_EEPROM_HDR_RFKILL(ah->ah_capabilities.cap_eeprom.ee_header))
ath5k_rfkill_set_intr(ah, false);
tasklet_kill(&ah->rf_kill.toggleq);
/* enable RFKILL when stopping HW so Wifi LED is turned off */
ath5k_rfkill_enable(ah);
}
@@ -0,0 +1,123 @@
// SPDX-License-Identifier: GPL-2.0
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/device.h>
#include <linux/pci.h>
#include "ath5k.h"
#include "reg.h"
#define SIMPLE_SHOW_STORE(name, get, set) \
static ssize_t ath5k_attr_show_##name(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
struct ieee80211_hw *hw = dev_get_drvdata(dev); \
struct ath5k_hw *ah = hw->priv; \
return sysfs_emit(buf, "%d\n", get); \
} \
\
static ssize_t ath5k_attr_store_##name(struct device *dev, \
struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
struct ieee80211_hw *hw = dev_get_drvdata(dev); \
struct ath5k_hw *ah = hw->priv; \
int val, ret; \
\
ret = kstrtoint(buf, 10, &val); \
if (ret < 0) \
return ret; \
set(ah, val); \
return count; \
} \
static DEVICE_ATTR(name, 0644, \
ath5k_attr_show_##name, ath5k_attr_store_##name)
#define SIMPLE_SHOW(name, get) \
static ssize_t ath5k_attr_show_##name(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
struct ieee80211_hw *hw = dev_get_drvdata(dev); \
struct ath5k_hw *ah = hw->priv; \
return sysfs_emit(buf, "%d\n", get); \
} \
static DEVICE_ATTR(name, 0444, ath5k_attr_show_##name, NULL)
/*** ANI ***/
SIMPLE_SHOW_STORE(ani_mode, ah->ani_state.ani_mode, ath5k_ani_init);
SIMPLE_SHOW_STORE(noise_immunity_level, ah->ani_state.noise_imm_level,
ath5k_ani_set_noise_immunity_level);
SIMPLE_SHOW_STORE(spur_level, ah->ani_state.spur_level,
ath5k_ani_set_spur_immunity_level);
SIMPLE_SHOW_STORE(firstep_level, ah->ani_state.firstep_level,
ath5k_ani_set_firstep_level);
SIMPLE_SHOW_STORE(ofdm_weak_signal_detection, ah->ani_state.ofdm_weak_sig,
ath5k_ani_set_ofdm_weak_signal_detection);
SIMPLE_SHOW_STORE(cck_weak_signal_detection, ah->ani_state.cck_weak_sig,
ath5k_ani_set_cck_weak_signal_detection);
SIMPLE_SHOW(spur_level_max, ah->ani_state.max_spur_level);
static ssize_t ath5k_attr_show_noise_immunity_level_max(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return sysfs_emit(buf, "%d\n", ATH5K_ANI_MAX_NOISE_IMM_LVL);
}
static DEVICE_ATTR(noise_immunity_level_max, 0444,
ath5k_attr_show_noise_immunity_level_max, NULL);
static ssize_t ath5k_attr_show_firstep_level_max(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return sysfs_emit(buf, "%d\n", ATH5K_ANI_MAX_FIRSTEP_LVL);
}
static DEVICE_ATTR(firstep_level_max, 0444,
ath5k_attr_show_firstep_level_max, NULL);
static struct attribute *ath5k_sysfs_entries_ani[] = {
&dev_attr_ani_mode.attr,
&dev_attr_noise_immunity_level.attr,
&dev_attr_spur_level.attr,
&dev_attr_firstep_level.attr,
&dev_attr_ofdm_weak_signal_detection.attr,
&dev_attr_cck_weak_signal_detection.attr,
&dev_attr_noise_immunity_level_max.attr,
&dev_attr_spur_level_max.attr,
&dev_attr_firstep_level_max.attr,
NULL
};
static struct attribute_group ath5k_attribute_group_ani = {
.name = "ani",
.attrs = ath5k_sysfs_entries_ani,
};
/*** register / unregister ***/
int
ath5k_sysfs_register(struct ath5k_hw *ah)
{
struct device *dev = ah->dev;
int err;
err = sysfs_create_group(&dev->kobj, &ath5k_attribute_group_ani);
if (err) {
ATH5K_ERR(ah, "failed to create sysfs group\n");
return err;
}
return 0;
}
void
ath5k_sysfs_unregister(struct ath5k_hw *ah)
{
struct device *dev = ah->dev;
sysfs_remove_group(&dev->kobj, &ath5k_attribute_group_ani);
}
@@ -0,0 +1,107 @@
/* SPDX-License-Identifier: GPL-2.0 */
#if !defined(__TRACE_ATH5K_H) || defined(TRACE_HEADER_MULTI_READ)
#define __TRACE_ATH5K_H
#include <linux/tracepoint.h>
#if !defined(CONFIG_ATH5K_TRACER) || defined(__CHECKER__)
#undef TRACE_EVENT
#define TRACE_EVENT(name, proto, ...) \
static inline void trace_ ## name(proto) {}
#endif
struct sk_buff;
struct ath5k_txq;
struct ath5k_tx_status;
#undef TRACE_SYSTEM
#define TRACE_SYSTEM ath5k
TRACE_EVENT(ath5k_rx,
TP_PROTO(struct ath5k_hw *priv, struct sk_buff *skb),
TP_ARGS(priv, skb),
TP_STRUCT__entry(
__field(struct ath5k_hw *, priv)
__field(unsigned long, skbaddr)
__dynamic_array(u8, frame, skb->len)
),
TP_fast_assign(
__entry->priv = priv;
__entry->skbaddr = (unsigned long) skb;
memcpy(__get_dynamic_array(frame), skb->data, skb->len);
),
TP_printk(
"[%p] RX skb=%lx", __entry->priv, __entry->skbaddr
)
);
TRACE_EVENT(ath5k_tx,
TP_PROTO(struct ath5k_hw *priv, struct sk_buff *skb,
struct ath5k_txq *q),
TP_ARGS(priv, skb, q),
TP_STRUCT__entry(
__field(struct ath5k_hw *, priv)
__field(unsigned long, skbaddr)
__field(u8, qnum)
__dynamic_array(u8, frame, skb->len)
),
TP_fast_assign(
__entry->priv = priv;
__entry->skbaddr = (unsigned long) skb;
__entry->qnum = (u8) q->qnum;
memcpy(__get_dynamic_array(frame), skb->data, skb->len);
),
TP_printk(
"[%p] TX skb=%lx q=%d", __entry->priv, __entry->skbaddr,
__entry->qnum
)
);
TRACE_EVENT(ath5k_tx_complete,
TP_PROTO(struct ath5k_hw *priv, struct sk_buff *skb,
struct ath5k_txq *q, struct ath5k_tx_status *ts),
TP_ARGS(priv, skb, q, ts),
TP_STRUCT__entry(
__field(struct ath5k_hw *, priv)
__field(unsigned long, skbaddr)
__field(u8, qnum)
__field(u8, ts_status)
__field(s8, ts_rssi)
__field(u8, ts_antenna)
),
TP_fast_assign(
__entry->priv = priv;
__entry->skbaddr = (unsigned long) skb;
__entry->qnum = (u8) q->qnum;
__entry->ts_status = ts->ts_status;
__entry->ts_rssi = ts->ts_rssi;
__entry->ts_antenna = ts->ts_antenna;
),
TP_printk(
"[%p] TX end skb=%lx q=%d stat=%x rssi=%d ant=%x",
__entry->priv, __entry->skbaddr, __entry->qnum,
__entry->ts_status, __entry->ts_rssi, __entry->ts_antenna
)
);
#endif /* __TRACE_ATH5K_H */
#if defined(CONFIG_ATH5K_TRACER) && !defined(__CHECKER__)
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE trace
#include <trace/define_trace.h>
#endif