add idl4k kernel firmware version 1.13.0.105

This commit is contained in:
Jaroslav Kysela
2015-03-26 17:22:37 +01:00
parent 5194d2792e
commit e9070cdc77
31064 changed files with 12769984 additions and 0 deletions

View File

@@ -0,0 +1,39 @@
config ATH5K
tristate "Atheros 5xxx wireless cards support"
depends on PCI && MAC80211 && WLAN_80211
select MAC80211_LEDS
select LEDS_CLASS
select NEW_LEDS
---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

View File

@@ -0,0 +1,16 @@
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
ath5k-y += led.o
ath5k-y += rfkill.o
ath5k-$(CONFIG_ATH5K_DEBUG) += debug.o
obj-$(CONFIG_ATH5K) += ath5k.o

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,374 @@
/*
* 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 *
\*************************************/
#include <linux/pci.h>
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
#include "base.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->ah_sc, "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->ah_sc, "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_attach - Check if hw is supported and init the needed structs
*
* @sc: The &struct ath5k_softc we got from the driver's attach function
*
* 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.
*/
struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc)
{
struct ath5k_hw *ah;
struct pci_dev *pdev = sc->pdev;
struct ath5k_eeprom_info *ee;
int ret;
u32 srev;
/*If we passed the test malloc a ath5k_hw struct*/
ah = kzalloc(sizeof(struct ath5k_hw), GFP_KERNEL);
if (ah == NULL) {
ret = -ENOMEM;
ATH5K_ERR(sc, "out of memory\n");
goto err;
}
ah->ah_sc = sc;
ah->ah_iobase = sc->iobase;
/*
* HW information
*/
ah->ah_op_mode = NL80211_IFTYPE_STATION;
ah->ah_radar.r_enabled = AR5K_TUNE_RADAR_ALERT;
ah->ah_turbo = false;
ah->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER;
ah->ah_imr = 0;
ah->ah_atim_window = 0;
ah->ah_aifs = AR5K_TUNE_AIFS;
ah->ah_cw_min = AR5K_TUNE_CWMIN;
ah->ah_limit_tx_retries = AR5K_INIT_TX_RETRY;
ah->ah_software_retry = false;
ah->ah_current_channel = &sc->channels[0];
/*
* Find the mac version
*/
srev = ath5k_hw_reg_read(ah, AR5K_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;
/*Fill the ath5k_hw struct with the needed functions*/
ret = ath5k_hw_init_desc_functions(ah);
if (ret)
goto err_free;
/* Bring device out of sleep and reset it's units */
ret = ath5k_hw_nic_wakeup(ah, 0, true);
if (ret)
goto err_free;
/* Get MAC, PHY and RADIO revisions */
ah->ah_mac_srev = srev;
ah->ah_mac_version = AR5K_REG_MS(srev, AR5K_SREV_VER);
ah->ah_mac_revision = AR5K_REG_MS(srev, AR5K_SREV_REV);
ah->ah_phy_revision = ath5k_hw_reg_read(ah, AR5K_PHY_CHIP_ID) &
0xffffffff;
ah->ah_radio_5ghz_revision = ath5k_hw_radio_revision(ah,
CHANNEL_5GHZ);
ah->ah_phy = AR5K_PHY(0);
/* Try to identify radio chip based on it's 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,
CHANNEL_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,
CHANNEL_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,
CHANNEL_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_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(sc, "Couldn't identify radio revision.\n");
ret = -ENODEV;
goto err_free;
}
}
/* Return on unsuported chips (unsupported eeprom etc) */
if ((srev >= AR5K_SREV_AR5416) &&
(srev < AR5K_SREV_AR2425)) {
ATH5K_ERR(sc, "Device not yet supported.\n");
ret = -ENODEV;
goto err_free;
}
/*
* POST
*/
ret = ath5k_hw_post(ah);
if (ret)
goto err_free;
/* 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(sc, "unable to init EEPROM\n");
goto err_free;
}
/*
* Write PCI-E power save settings
*/
if ((ah->ah_version == AR5K_AR5212) && (pdev->is_pcie)) {
struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
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 programing 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);
mdelay(1);
}
/* Get misc capabilities */
ret = ath5k_hw_set_capabilities(ah);
if (ret) {
ATH5K_ERR(sc, "unable to get device capabilities: 0x%04x\n",
sc->pdev->device);
goto err_free;
}
/* Crypto settings */
ee = &ah->ah_capabilities.cap_eeprom;
ah->ah_aes_support = srev >= AR5K_SREV_AR5212_V4 &&
(ee->ee_version >= AR5K_EEPROM_VERSION_5_0 &&
!AR5K_EEPROM_AES_DIS(ee->ee_misc5));
if (srev >= AR5K_SREV_AR2414) {
ah->ah_combined_mic = true;
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, (u8[ETH_ALEN]){});
/* Set BSSID to bcast address: ff:ff:ff:ff:ff:ff for now */
memset(ah->ah_bssid, 0xff, ETH_ALEN);
ath5k_hw_set_associd(ah, ah->ah_bssid, 0);
ath5k_hw_set_opmode(ah);
ath5k_hw_rfgain_opt_init(ah);
/* turn on HW LEDs */
ath5k_hw_set_ledstate(ah, AR5K_LED_INIT);
return ah;
err_free:
kfree(ah);
err:
return ERR_PTR(ret);
}
/**
* ath5k_hw_detach - Free the ath5k_hw struct
*
* @ah: The &struct ath5k_hw
*/
void ath5k_hw_detach(struct ath5k_hw *ah)
{
ATH5K_TRACE(ah->ah_sc);
__set_bit(ATH_STAT_INVALID, ah->ah_sc->status);
if (ah->ah_rf_banks != NULL)
kfree(ah->ah_rf_banks);
ath5k_eeprom_detach(ah);
/* assume interrupts are down */
kfree(ah);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,216 @@
/*-
* 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.
*
*/
/*
* Defintions for the Atheros Wireless LAN controller driver.
*/
#ifndef _DEV_ATH_ATHVAR_H
#define _DEV_ATH_ATHVAR_H
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/wireless.h>
#include <linux/if_ether.h>
#include <linux/leds.h>
#include <linux/rfkill.h>
#include "ath5k.h"
#include "debug.h"
#include "../regd.h"
#include "../ath.h"
#define ATH_RXBUF 40 /* number of RX buffers */
#define ATH_TXBUF 200 /* number of TX buffers */
#define ATH_BCBUF 1 /* number of beacon buffers */
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 */
};
/*
* Data transmit queue state. One of these exists for each
* hardware transmit queue. Packets sent to us from above
* are assigned to queues based on their priority. Not all
* devices support a complete set of hardware transmit queues.
* For those devices the array sc_ac2q will map multiple
* priorities to fewer hardware queues (typically all to one
* hardware queue).
*/
struct ath5k_txq {
unsigned int qnum; /* hardware q number */
u32 *link; /* link ptr in last TX desc */
struct list_head q; /* transmit queue */
spinlock_t lock; /* lock on q and link */
bool setup;
};
#define ATH5K_LED_MAX_NAME_LEN 31
/*
* State for LED triggers
*/
struct ath5k_led
{
char name[ATH5K_LED_MAX_NAME_LEN + 1]; /* name of the LED in sysfs */
struct ath5k_softc *sc; /* driver state */
struct led_classdev led_dev; /* led classdev */
};
/* Rfkill */
struct ath5k_rfkill {
/* GPIO PIN for rfkill */
u16 gpio;
/* polarity of rfkill GPIO PIN */
bool polarity;
/* RFKILL toggle tasklet */
struct tasklet_struct toggleq;
};
#if CHAN_DEBUG
#define ATH_CHAN_MAX (26+26+26+200+200)
#else
#define ATH_CHAN_MAX (14+14+14+252+20)
#endif
/* Software Carrier, keeps track of the driver state
* associated with an instance of a device */
struct ath5k_softc {
struct pci_dev *pdev; /* for dma mapping */
struct ath_common common;
void __iomem *iobase; /* address of the device */
struct mutex lock; /* dev-level lock */
struct ieee80211_tx_queue_stats tx_stats[AR5K_NUM_TX_QUEUES];
struct ieee80211_low_level_stats ll_stats;
struct ieee80211_hw *hw; /* IEEE 802.11 common */
struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
struct ieee80211_channel channels[ATH_CHAN_MAX];
struct ieee80211_rate rates[IEEE80211_NUM_BANDS][AR5K_MAX_RATES];
s8 rate_idx[IEEE80211_NUM_BANDS][AR5K_MAX_RATES];
enum nl80211_iftype opmode;
struct ath5k_hw *ah; /* Atheros HW */
struct ieee80211_supported_band *curband;
#ifdef CONFIG_ATH5K_DEBUG
struct ath5k_dbg_info debug; /* debug info */
#endif /* CONFIG_ATH5K_DEBUG */
struct ath5k_buf *bufptr; /* allocated buffer ptr */
struct ath5k_desc *desc; /* TX/RX descriptors */
dma_addr_t desc_daddr; /* DMA (physical) address */
size_t desc_len; /* size of TX/RX descriptors */
DECLARE_BITMAP(status, 5);
#define ATH_STAT_INVALID 0 /* disable hardware accesses */
#define ATH_STAT_MRRETRY 1 /* multi-rate retry support */
#define ATH_STAT_PROMISC 2
#define ATH_STAT_LEDSOFT 3 /* enable LED gpio status */
#define ATH_STAT_STARTED 4 /* opened & irqs enabled */
unsigned int filter_flags; /* HW flags, AR5K_RX_FILTER_* */
unsigned int curmode; /* current phy mode */
struct ieee80211_channel *curchan; /* current h/w channel */
struct ieee80211_vif *vif;
enum ath5k_int imask; /* interrupt mask copy */
DECLARE_BITMAP(keymap, AR5K_KEYCACHE_SIZE); /* key use bit map */
u8 bssidmask[ETH_ALEN];
unsigned int led_pin, /* GPIO pin for driving LED */
led_on; /* pin setting for LED on */
struct tasklet_struct restq; /* reset tasklet */
unsigned int rxbufsize; /* rx size based on mtu */
struct list_head rxbuf; /* receive buffer */
spinlock_t rxbuflock;
u32 *rxlink; /* link ptr in last RX desc */
struct tasklet_struct rxtq; /* rx intr tasklet */
struct ath5k_led rx_led; /* rx led */
struct list_head txbuf; /* transmit buffer */
spinlock_t txbuflock;
unsigned int txbuf_len; /* buf count in txbuf list */
struct ath5k_txq txqs[AR5K_NUM_TX_QUEUES]; /* tx queues */
struct ath5k_txq *txq; /* main tx queue */
struct tasklet_struct txtq; /* tx intr tasklet */
struct ath5k_led tx_led; /* tx led */
struct ath5k_rfkill rf_kill;
struct tasklet_struct calib; /* calibration tasklet */
spinlock_t block; /* protects beacon */
struct tasklet_struct beacontq; /* beacon intr tasklet */
struct ath5k_buf *bbuf; /* beacon buffer */
unsigned int bhalq, /* SW q for outgoing beacons */
bmisscount, /* missed beacon transmits */
bintval, /* beacon interval in TU */
bsent;
unsigned int nexttbtt; /* next beacon time in TU */
struct ath5k_txq *cabq; /* content after beacon */
int power_level; /* Requested tx power in dbm */
bool assoc; /* assocate state */
bool enable_beacon; /* true if beacons are on */
};
#define ath5k_hw_hasbssidmask(_ah) \
(ath5k_hw_get_capability(_ah, AR5K_CAP_BSSIDMASK, 0, NULL) == 0)
#define ath5k_hw_hasveol(_ah) \
(ath5k_hw_get_capability(_ah, AR5K_CAP_VEOL, 0, NULL) == 0)
static inline struct ath_common *ath5k_hw_common(struct ath5k_hw *ah)
{
return &ah->ah_sc->common;
}
static inline struct ath_regulatory *ath5k_hw_regulatory(struct ath5k_hw *ah)
{
return &(ath5k_hw_common(ah)->regulatory);
}
#endif

View File

@@ -0,0 +1,195 @@
/*
* 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 "base.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)
{
u16 ee_header;
ATH5K_TRACE(ah->ah_sc);
/* Capabilities stored in the EEPROM */
ee_header = ah->ah_capabilities.cap_eeprom.ee_header;
if (ah->ah_version == AR5K_AR5210) {
/*
* Set radio capabilities
* (The AR5110 only supports the middle 5GHz band)
*/
ah->ah_capabilities.cap_range.range_5ghz_min = 5120;
ah->ah_capabilities.cap_range.range_5ghz_max = 5430;
ah->ah_capabilities.cap_range.range_2ghz_min = 0;
ah->ah_capabilities.cap_range.range_2ghz_max = 0;
/* Set supported modes */
__set_bit(AR5K_MODE_11A, ah->ah_capabilities.cap_mode);
__set_bit(AR5K_MODE_11A_TURBO, ah->ah_capabilities.cap_mode);
} else {
/*
* XXX The tranceiver supports frequencies from 4920 to 6100GHz
* XXX and from 2312 to 2732GHz. There are problems with the
* XXX current ieee80211 implementation because the IEEE
* XXX channel mapping does not support negative channel
* XXX numbers (2312MHz is channel -19). Of course, this
* XXX doesn't matter because these channels are out of range
* XXX but some regulation domains like MKK (Japan) will
* XXX support frequencies somewhere around 4.8GHz.
*/
/*
* Set radio capabilities
*/
if (AR5K_EEPROM_HDR_11A(ee_header)) {
/* 4920 */
ah->ah_capabilities.cap_range.range_5ghz_min = 5005;
ah->ah_capabilities.cap_range.range_5ghz_max = 6100;
/* Set supported modes */
__set_bit(AR5K_MODE_11A,
ah->ah_capabilities.cap_mode);
__set_bit(AR5K_MODE_11A_TURBO,
ah->ah_capabilities.cap_mode);
if (ah->ah_version == AR5K_AR5212)
__set_bit(AR5K_MODE_11G_TURBO,
ah->ah_capabilities.cap_mode);
}
/* Enable 802.11b if a 2GHz capable radio (2111/5112) is
* connected */
if (AR5K_EEPROM_HDR_11B(ee_header) ||
(AR5K_EEPROM_HDR_11G(ee_header) &&
ah->ah_version != AR5K_AR5211)) {
/* 2312 */
ah->ah_capabilities.cap_range.range_2ghz_min = 2412;
ah->ah_capabilities.cap_range.range_2ghz_max = 2732;
if (AR5K_EEPROM_HDR_11B(ee_header))
__set_bit(AR5K_MODE_11B,
ah->ah_capabilities.cap_mode);
if (AR5K_EEPROM_HDR_11G(ee_header) &&
ah->ah_version != AR5K_AR5211)
__set_bit(AR5K_MODE_11G,
ah->ah_capabilities.cap_mode);
}
}
/* GPIO */
ah->ah_gpio_npins = AR5K_NUM_GPIO;
/* Set number of supported TX queues */
if (ah->ah_version == AR5K_AR5210)
ah->ah_capabilities.cap_queues.q_tx_num =
AR5K_NUM_TX_QUEUES_NOQCU;
else
ah->ah_capabilities.cap_queues.q_tx_num = AR5K_NUM_TX_QUEUES;
return 0;
}
/* Main function used by the driver part to check caps */
int ath5k_hw_get_capability(struct ath5k_hw *ah,
enum ath5k_capability_type cap_type,
u32 capability, u32 *result)
{
ATH5K_TRACE(ah->ah_sc);
switch (cap_type) {
case AR5K_CAP_NUM_TXQUEUES:
if (result) {
if (ah->ah_version == AR5K_AR5210)
*result = AR5K_NUM_TX_QUEUES_NOQCU;
else
*result = AR5K_NUM_TX_QUEUES;
goto yes;
}
case AR5K_CAP_VEOL:
goto yes;
case AR5K_CAP_COMPRESSION:
if (ah->ah_version == AR5K_AR5212)
goto yes;
else
goto no;
case AR5K_CAP_BURST:
goto yes;
case AR5K_CAP_TPC:
goto yes;
case AR5K_CAP_BSSIDMASK:
if (ah->ah_version == AR5K_AR5212)
goto yes;
else
goto no;
case AR5K_CAP_XR:
if (ah->ah_version == AR5K_AR5212)
goto yes;
else
goto no;
default:
goto no;
}
no:
return -EINVAL;
yes:
return 0;
}
/*
* 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)
{
ATH5K_TRACE(ah->ah_sc);
if (ah->ah_version == AR5K_AR5210) {
AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1,
AR5K_STA_ID1_NO_PSPOLL | AR5K_STA_ID1_DEFAULT_ANTENNA);
return 0;
}
return -EIO;
}
int ath5k_hw_disable_pspoll(struct ath5k_hw *ah)
{
ATH5K_TRACE(ah->ah_sc);
if (ah->ah_version == AR5K_AR5210) {
AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1,
AR5K_STA_ID1_NO_PSPOLL | AR5K_STA_ID1_DEFAULT_ANTENNA);
return 0;
}
return -EIO;
}

View File

@@ -0,0 +1,540 @@
/*
* Copyright (c) 2007-2008 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.
*/
#include "base.h"
#include "debug.h"
static unsigned int ath5k_debug;
module_param_named(debug, ath5k_debug, uint, 0);
#ifdef CONFIG_ATH5K_DEBUG
#include <linux/seq_file.h>
#include "reg.h"
static struct dentry *ath5k_global_debugfs;
static int ath5k_debugfs_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
return 0;
}
/* debugfs: registers */
struct reg {
const char *name;
int addr;
};
#define REG_STRUCT_INIT(r) { #r, r }
/* just a few random registers, might want to add more */
static const struct reg regs[] = {
REG_STRUCT_INIT(AR5K_CR),
REG_STRUCT_INIT(AR5K_RXDP),
REG_STRUCT_INIT(AR5K_CFG),
REG_STRUCT_INIT(AR5K_IER),
REG_STRUCT_INIT(AR5K_BCR),
REG_STRUCT_INIT(AR5K_RTSD0),
REG_STRUCT_INIT(AR5K_RTSD1),
REG_STRUCT_INIT(AR5K_TXCFG),
REG_STRUCT_INIT(AR5K_RXCFG),
REG_STRUCT_INIT(AR5K_RXJLA),
REG_STRUCT_INIT(AR5K_MIBC),
REG_STRUCT_INIT(AR5K_TOPS),
REG_STRUCT_INIT(AR5K_RXNOFRM),
REG_STRUCT_INIT(AR5K_TXNOFRM),
REG_STRUCT_INIT(AR5K_RPGTO),
REG_STRUCT_INIT(AR5K_RFCNT),
REG_STRUCT_INIT(AR5K_MISC),
REG_STRUCT_INIT(AR5K_QCUDCU_CLKGT),
REG_STRUCT_INIT(AR5K_ISR),
REG_STRUCT_INIT(AR5K_PISR),
REG_STRUCT_INIT(AR5K_SISR0),
REG_STRUCT_INIT(AR5K_SISR1),
REG_STRUCT_INIT(AR5K_SISR2),
REG_STRUCT_INIT(AR5K_SISR3),
REG_STRUCT_INIT(AR5K_SISR4),
REG_STRUCT_INIT(AR5K_IMR),
REG_STRUCT_INIT(AR5K_PIMR),
REG_STRUCT_INIT(AR5K_SIMR0),
REG_STRUCT_INIT(AR5K_SIMR1),
REG_STRUCT_INIT(AR5K_SIMR2),
REG_STRUCT_INIT(AR5K_SIMR3),
REG_STRUCT_INIT(AR5K_SIMR4),
REG_STRUCT_INIT(AR5K_DCM_ADDR),
REG_STRUCT_INIT(AR5K_DCCFG),
REG_STRUCT_INIT(AR5K_CCFG),
REG_STRUCT_INIT(AR5K_CPC0),
REG_STRUCT_INIT(AR5K_CPC1),
REG_STRUCT_INIT(AR5K_CPC2),
REG_STRUCT_INIT(AR5K_CPC3),
REG_STRUCT_INIT(AR5K_CPCOVF),
REG_STRUCT_INIT(AR5K_RESET_CTL),
REG_STRUCT_INIT(AR5K_SLEEP_CTL),
REG_STRUCT_INIT(AR5K_INTPEND),
REG_STRUCT_INIT(AR5K_SFR),
REG_STRUCT_INIT(AR5K_PCICFG),
REG_STRUCT_INIT(AR5K_GPIOCR),
REG_STRUCT_INIT(AR5K_GPIODO),
REG_STRUCT_INIT(AR5K_SREV),
};
static void *reg_start(struct seq_file *seq, loff_t *pos)
{
return *pos < ARRAY_SIZE(regs) ? (void *)&regs[*pos] : NULL;
}
static void reg_stop(struct seq_file *seq, void *p)
{
/* nothing to do */
}
static void *reg_next(struct seq_file *seq, void *p, loff_t *pos)
{
++*pos;
return *pos < ARRAY_SIZE(regs) ? (void *)&regs[*pos] : NULL;
}
static int reg_show(struct seq_file *seq, void *p)
{
struct ath5k_softc *sc = seq->private;
struct reg *r = p;
seq_printf(seq, "%-25s0x%08x\n", r->name,
ath5k_hw_reg_read(sc->ah, r->addr));
return 0;
}
static const struct seq_operations register_seq_ops = {
.start = reg_start,
.next = reg_next,
.stop = reg_stop,
.show = reg_show
};
static int open_file_registers(struct inode *inode, struct file *file)
{
struct seq_file *s;
int res;
res = seq_open(file, &register_seq_ops);
if (res == 0) {
s = file->private_data;
s->private = inode->i_private;
}
return res;
}
static const struct file_operations fops_registers = {
.open = open_file_registers,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
.owner = THIS_MODULE,
};
/* debugfs: beacons */
static ssize_t read_file_beacon(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath5k_softc *sc = file->private_data;
struct ath5k_hw *ah = sc->ah;
char buf[500];
unsigned int len = 0;
unsigned int v;
u64 tsf;
v = ath5k_hw_reg_read(sc->ah, AR5K_BEACON);
len += snprintf(buf+len, sizeof(buf)-len,
"%-24s0x%08x\tintval: %d\tTIM: 0x%x\n",
"AR5K_BEACON", v, v & AR5K_BEACON_PERIOD,
(v & AR5K_BEACON_TIM) >> AR5K_BEACON_TIM_S);
len += snprintf(buf+len, sizeof(buf)-len, "%-24s0x%08x\n",
"AR5K_LAST_TSTP", ath5k_hw_reg_read(sc->ah, AR5K_LAST_TSTP));
len += snprintf(buf+len, sizeof(buf)-len, "%-24s0x%08x\n\n",
"AR5K_BEACON_CNT", ath5k_hw_reg_read(sc->ah, AR5K_BEACON_CNT));
v = ath5k_hw_reg_read(sc->ah, AR5K_TIMER0);
len += snprintf(buf+len, sizeof(buf)-len, "%-24s0x%08x\tTU: %08x\n",
"AR5K_TIMER0 (TBTT)", v, v);
v = ath5k_hw_reg_read(sc->ah, AR5K_TIMER1);
len += snprintf(buf+len, sizeof(buf)-len, "%-24s0x%08x\tTU: %08x\n",
"AR5K_TIMER1 (DMA)", v, v >> 3);
v = ath5k_hw_reg_read(sc->ah, AR5K_TIMER2);
len += snprintf(buf+len, sizeof(buf)-len, "%-24s0x%08x\tTU: %08x\n",
"AR5K_TIMER2 (SWBA)", v, v >> 3);
v = ath5k_hw_reg_read(sc->ah, AR5K_TIMER3);
len += snprintf(buf+len, sizeof(buf)-len, "%-24s0x%08x\tTU: %08x\n",
"AR5K_TIMER3 (ATIM)", v, v);
tsf = ath5k_hw_get_tsf64(sc->ah);
len += snprintf(buf+len, sizeof(buf)-len,
"TSF\t\t0x%016llx\tTU: %08x\n",
(unsigned long long)tsf, TSF_TO_TU(tsf));
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static ssize_t write_file_beacon(struct file *file,
const char __user *userbuf,
size_t count, loff_t *ppos)
{
struct ath5k_softc *sc = file->private_data;
struct ath5k_hw *ah = sc->ah;
char buf[20];
if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
return -EFAULT;
if (strncmp(buf, "disable", 7) == 0) {
AR5K_REG_DISABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE);
printk(KERN_INFO "debugfs disable beacons\n");
} else if (strncmp(buf, "enable", 6) == 0) {
AR5K_REG_ENABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE);
printk(KERN_INFO "debugfs enable beacons\n");
}
return count;
}
static const struct file_operations fops_beacon = {
.read = read_file_beacon,
.write = write_file_beacon,
.open = ath5k_debugfs_open,
.owner = THIS_MODULE,
};
/* debugfs: reset */
static ssize_t write_file_reset(struct file *file,
const char __user *userbuf,
size_t count, loff_t *ppos)
{
struct ath5k_softc *sc = file->private_data;
tasklet_schedule(&sc->restq);
return count;
}
static const struct file_operations fops_reset = {
.write = write_file_reset,
.open = ath5k_debugfs_open,
.owner = THIS_MODULE,
};
/* debugfs: debug level */
static const struct {
enum ath5k_debug_level level;
const char *name;
const char *desc;
} dbg_info[] = {
{ ATH5K_DEBUG_RESET, "reset", "reset and initialization" },
{ ATH5K_DEBUG_INTR, "intr", "interrupt handling" },
{ ATH5K_DEBUG_MODE, "mode", "mode init/setup" },
{ ATH5K_DEBUG_XMIT, "xmit", "basic xmit operation" },
{ ATH5K_DEBUG_BEACON, "beacon", "beacon handling" },
{ ATH5K_DEBUG_CALIBRATE, "calib", "periodic calibration" },
{ ATH5K_DEBUG_TXPOWER, "txpower", "transmit power setting" },
{ ATH5K_DEBUG_LED, "led", "LED management" },
{ ATH5K_DEBUG_DUMP_RX, "dumprx", "print received skb content" },
{ ATH5K_DEBUG_DUMP_TX, "dumptx", "print transmit skb content" },
{ ATH5K_DEBUG_DUMPBANDS, "dumpbands", "dump bands" },
{ ATH5K_DEBUG_TRACE, "trace", "trace function calls" },
{ ATH5K_DEBUG_ANY, "all", "show all debug levels" },
};
static ssize_t read_file_debug(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath5k_softc *sc = file->private_data;
char buf[700];
unsigned int len = 0;
unsigned int i;
len += snprintf(buf+len, sizeof(buf)-len,
"DEBUG LEVEL: 0x%08x\n\n", sc->debug.level);
for (i = 0; i < ARRAY_SIZE(dbg_info) - 1; i++) {
len += snprintf(buf+len, sizeof(buf)-len,
"%10s %c 0x%08x - %s\n", dbg_info[i].name,
sc->debug.level & dbg_info[i].level ? '+' : ' ',
dbg_info[i].level, dbg_info[i].desc);
}
len += snprintf(buf+len, sizeof(buf)-len,
"%10s %c 0x%08x - %s\n", dbg_info[i].name,
sc->debug.level == dbg_info[i].level ? '+' : ' ',
dbg_info[i].level, dbg_info[i].desc);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static ssize_t write_file_debug(struct file *file,
const char __user *userbuf,
size_t count, loff_t *ppos)
{
struct ath5k_softc *sc = file->private_data;
unsigned int i;
char buf[20];
if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
return -EFAULT;
for (i = 0; i < ARRAY_SIZE(dbg_info); i++) {
if (strncmp(buf, dbg_info[i].name,
strlen(dbg_info[i].name)) == 0) {
sc->debug.level ^= dbg_info[i].level; /* toggle bit */
break;
}
}
return count;
}
static const struct file_operations fops_debug = {
.read = read_file_debug,
.write = write_file_debug,
.open = ath5k_debugfs_open,
.owner = THIS_MODULE,
};
/* init */
void
ath5k_debug_init(void)
{
ath5k_global_debugfs = debugfs_create_dir("ath5k", NULL);
}
void
ath5k_debug_init_device(struct ath5k_softc *sc)
{
sc->debug.level = ath5k_debug;
sc->debug.debugfs_phydir = debugfs_create_dir(wiphy_name(sc->hw->wiphy),
ath5k_global_debugfs);
sc->debug.debugfs_debug = debugfs_create_file("debug",
S_IWUSR | S_IRUSR,
sc->debug.debugfs_phydir, sc, &fops_debug);
sc->debug.debugfs_registers = debugfs_create_file("registers", S_IRUSR,
sc->debug.debugfs_phydir, sc, &fops_registers);
sc->debug.debugfs_beacon = debugfs_create_file("beacon",
S_IWUSR | S_IRUSR,
sc->debug.debugfs_phydir, sc, &fops_beacon);
sc->debug.debugfs_reset = debugfs_create_file("reset", S_IWUSR,
sc->debug.debugfs_phydir, sc, &fops_reset);
}
void
ath5k_debug_finish(void)
{
debugfs_remove(ath5k_global_debugfs);
}
void
ath5k_debug_finish_device(struct ath5k_softc *sc)
{
debugfs_remove(sc->debug.debugfs_debug);
debugfs_remove(sc->debug.debugfs_registers);
debugfs_remove(sc->debug.debugfs_beacon);
debugfs_remove(sc->debug.debugfs_reset);
debugfs_remove(sc->debug.debugfs_phydir);
}
/* functions used in other places */
void
ath5k_debug_dump_bands(struct ath5k_softc *sc)
{
unsigned int b, i;
if (likely(!(sc->debug.level & ATH5K_DEBUG_DUMPBANDS)))
return;
BUG_ON(!sc->sbands);
for (b = 0; b < IEEE80211_NUM_BANDS; b++) {
struct ieee80211_supported_band *band = &sc->sbands[b];
char bname[6];
switch (band->band) {
case IEEE80211_BAND_2GHZ:
strcpy(bname, "2 GHz");
break;
case IEEE80211_BAND_5GHZ:
strcpy(bname, "5 GHz");
break;
default:
printk(KERN_DEBUG "Band not supported: %d\n",
band->band);
return;
}
printk(KERN_DEBUG "Band %s: channels %d, rates %d\n", bname,
band->n_channels, band->n_bitrates);
printk(KERN_DEBUG " channels:\n");
for (i = 0; i < band->n_channels; i++)
printk(KERN_DEBUG " %3d %d %.4x %.4x\n",
ieee80211_frequency_to_channel(
band->channels[i].center_freq),
band->channels[i].center_freq,
band->channels[i].hw_value,
band->channels[i].flags);
printk(KERN_DEBUG " rates:\n");
for (i = 0; i < band->n_bitrates; i++)
printk(KERN_DEBUG " %4d %.4x %.4x %.4x\n",
band->bitrates[i].bitrate,
band->bitrates[i].hw_value,
band->bitrates[i].flags,
band->bitrates[i].hw_value_short);
}
}
static inline void
ath5k_debug_printrxbuf(struct ath5k_buf *bf, int done,
struct ath5k_rx_status *rs)
{
struct ath5k_desc *ds = bf->desc;
struct ath5k_hw_all_rx_desc *rd = &ds->ud.ds_rx;
printk(KERN_DEBUG "R (%p %llx) %08x %08x %08x %08x %08x %08x %c\n",
ds, (unsigned long long)bf->daddr,
ds->ds_link, ds->ds_data,
rd->rx_ctl.rx_control_0, rd->rx_ctl.rx_control_1,
rd->u.rx_stat.rx_status_0, rd->u.rx_stat.rx_status_0,
!done ? ' ' : (rs->rs_status == 0) ? '*' : '!');
}
void
ath5k_debug_printrxbuffs(struct ath5k_softc *sc, struct ath5k_hw *ah)
{
struct ath5k_desc *ds;
struct ath5k_buf *bf;
struct ath5k_rx_status rs = {};
int status;
if (likely(!(sc->debug.level & ATH5K_DEBUG_RESET)))
return;
printk(KERN_DEBUG "rx queue %x, link %p\n",
ath5k_hw_get_rxdp(ah), sc->rxlink);
spin_lock_bh(&sc->rxbuflock);
list_for_each_entry(bf, &sc->rxbuf, list) {
ds = bf->desc;
status = ah->ah_proc_rx_desc(ah, ds, &rs);
if (!status)
ath5k_debug_printrxbuf(bf, status == 0, &rs);
}
spin_unlock_bh(&sc->rxbuflock);
}
void
ath5k_debug_dump_skb(struct ath5k_softc *sc,
struct sk_buff *skb, const char *prefix, int tx)
{
char buf[16];
if (likely(!((tx && (sc->debug.level & ATH5K_DEBUG_DUMP_TX)) ||
(!tx && (sc->debug.level & ATH5K_DEBUG_DUMP_RX)))))
return;
snprintf(buf, sizeof(buf), "%s %s", wiphy_name(sc->hw->wiphy), prefix);
print_hex_dump_bytes(buf, DUMP_PREFIX_NONE, skb->data,
min(200U, skb->len));
printk(KERN_DEBUG "\n");
}
void
ath5k_debug_printtxbuf(struct ath5k_softc *sc, struct ath5k_buf *bf)
{
struct ath5k_desc *ds = bf->desc;
struct ath5k_hw_5212_tx_desc *td = &ds->ud.ds_tx5212;
struct ath5k_tx_status ts = {};
int done;
if (likely(!(sc->debug.level & ATH5K_DEBUG_RESET)))
return;
done = sc->ah->ah_proc_tx_desc(sc->ah, bf->desc, &ts);
printk(KERN_DEBUG "T (%p %llx) %08x %08x %08x %08x %08x %08x %08x "
"%08x %c\n", ds, (unsigned long long)bf->daddr, ds->ds_link,
ds->ds_data, td->tx_ctl.tx_control_0, td->tx_ctl.tx_control_1,
td->tx_ctl.tx_control_2, td->tx_ctl.tx_control_3,
td->tx_stat.tx_status_0, td->tx_stat.tx_status_1,
done ? ' ' : (ts.ts_status == 0) ? '*' : '!');
}
#endif /* ifdef CONFIG_ATH5K_DEBUG */

View File

@@ -0,0 +1,203 @@
/*
* 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_softc;
struct ath5k_hw;
struct sk_buff;
struct ath5k_buf;
struct ath5k_dbg_info {
unsigned int level; /* debug level */
/* debugfs entries */
struct dentry *debugfs_phydir;
struct dentry *debugfs_debug;
struct dentry *debugfs_registers;
struct dentry *debugfs_beacon;
struct dentry *debugfs_reset;
};
/**
* 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_DUMP_RX: print received skb content
* @ATH5K_DEBUG_DUMP_TX: print transmit skb content
* @ATH5K_DEBUG_DUMPBANDS: dump bands
* @ATH5K_DEBUG_TRACE: trace function calls
* @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_DUMP_RX = 0x00000100,
ATH5K_DEBUG_DUMP_TX = 0x00000200,
ATH5K_DEBUG_DUMPBANDS = 0x00000400,
ATH5K_DEBUG_TRACE = 0x00001000,
ATH5K_DEBUG_ANY = 0xffffffff
};
#ifdef CONFIG_ATH5K_DEBUG
#define ATH5K_TRACE(_sc) do { \
if (unlikely((_sc)->debug.level & ATH5K_DEBUG_TRACE)) \
printk(KERN_DEBUG "ath5k trace %s:%d\n", __func__, __LINE__); \
} while (0)
#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(void);
void
ath5k_debug_init_device(struct ath5k_softc *sc);
void
ath5k_debug_finish(void);
void
ath5k_debug_finish_device(struct ath5k_softc *sc);
void
ath5k_debug_printrxbuffs(struct ath5k_softc *sc, struct ath5k_hw *ah);
void
ath5k_debug_dump_bands(struct ath5k_softc *sc);
void
ath5k_debug_dump_skb(struct ath5k_softc *sc,
struct sk_buff *skb, const char *prefix, int tx);
void
ath5k_debug_printtxbuf(struct ath5k_softc *sc, struct ath5k_buf *bf);
#else /* no debugging */
#include <linux/compiler.h>
#define ATH5K_TRACE(_sc) typecheck(struct ath5k_softc *, (_sc))
static inline void __attribute__ ((format (printf, 3, 4)))
ATH5K_DBG(struct ath5k_softc *sc, unsigned int m, const char *fmt, ...) {}
static inline void __attribute__ ((format (printf, 3, 4)))
ATH5K_DBG_UNLIMIT(struct ath5k_softc *sc, unsigned int m, const char *fmt, ...)
{}
static inline void
ath5k_debug_init(void) {}
static inline void
ath5k_debug_init_device(struct ath5k_softc *sc) {}
static inline void
ath5k_debug_finish(void) {}
static inline void
ath5k_debug_finish_device(struct ath5k_softc *sc) {}
static inline void
ath5k_debug_printrxbuffs(struct ath5k_softc *sc, struct ath5k_hw *ah) {}
static inline void
ath5k_debug_dump_bands(struct ath5k_softc *sc) {}
static inline void
ath5k_debug_dump_skb(struct ath5k_softc *sc,
struct sk_buff *skb, const char *prefix, int tx) {}
static inline void
ath5k_debug_printtxbuf(struct ath5k_softc *sc, struct ath5k_buf *bf) {}
#endif /* ifdef CONFIG_ATH5K_DEBUG */
#endif /* ifndef _ATH5K_DEBUG_H */

View File

@@ -0,0 +1,696 @@
/*
* 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
\******************************/
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
#include "base.h"
/*
* TX Descriptors
*/
/*
* Initialize the 2-word tx control descriptor on 5210/5211
*/
static int
ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
unsigned int pkt_len, unsigned int hdr_len, enum ath5k_pkt_type type,
unsigned int tx_power, unsigned int tx_rate0, unsigned int tx_tries0,
unsigned int key_index, unsigned int antenna_mode, unsigned int flags,
unsigned int rtscts_rate, unsigned int rtscts_duration)
{
u32 frame_type;
struct ath5k_hw_2w_tx_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 continously sends
* noise on the channel, so it is important to avoid this.
*/
if (unlikely(tx_tries0 == 0)) {
ATH5K_ERR(ah->ah_sc, "zero retries\n");
WARN_ON(1);
return -EINVAL;
}
if (unlikely(tx_rate0 == 0)) {
ATH5K_ERR(ah->ah_sc, "zero rate\n");
WARN_ON(1);
return -EINVAL;
}
/* Clear 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 - ath5k_pad_size(hdr_len) + 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
* XXX: I only found that on 5210 code, does it work on 5211 ?
*/
if (ah->ah_version == AR5K_AR5210) {
if (hdr_len & ~AR5K_2W_TX_DESC_CTL0_HEADER_LEN)
return -EINVAL;
tx_ctl->tx_control_0 |=
AR5K_REG_SM(hdr_len, AR5K_2W_TX_DESC_CTL0_HEADER_LEN);
}
/*Diferences between 5210-5211*/
if (ah->ah_version == AR5K_AR5210) {
switch (type) {
case AR5K_PKT_TYPE_BEACON:
case AR5K_PKT_TYPE_PROBE_RESP:
frame_type = AR5K_AR5210_TX_DESC_FRAME_TYPE_NO_DELAY;
case AR5K_PKT_TYPE_PIFS:
frame_type = AR5K_AR5210_TX_DESC_FRAME_TYPE_PIFS;
default:
frame_type = type /*<< 2 ?*/;
}
tx_ctl->tx_control_0 |=
AR5K_REG_SM(frame_type, AR5K_2W_TX_DESC_CTL0_FRAME_TYPE) |
AR5K_REG_SM(tx_rate0, AR5K_2W_TX_DESC_CTL0_XMIT_RATE);
} else {
tx_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);
}
#define _TX_FLAGS(_c, _flag) \
if (flags & AR5K_TXDESC_##_flag) { \
tx_ctl->tx_control_##_c |= \
AR5K_2W_TX_DESC_CTL##_c##_##_flag; \
}
_TX_FLAGS(0, CLRDMASK);
_TX_FLAGS(0, VEOL);
_TX_FLAGS(0, INTREQ);
_TX_FLAGS(0, RTSENA);
_TX_FLAGS(1, NOACK);
#undef _TX_FLAGS
/*
* WEP crap
*/
if (key_index != AR5K_TXKEYIX_INVALID) {
tx_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_ENCRYPT_KEY_INDEX);
}
/*
* 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;
return 0;
}
/*
* Initialize the 4-word tx control descriptor on 5212
*/
static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah,
struct ath5k_desc *desc, unsigned int pkt_len, unsigned int hdr_len,
enum ath5k_pkt_type type, unsigned int tx_power, unsigned int tx_rate0,
unsigned int tx_tries0, unsigned int key_index,
unsigned int antenna_mode, unsigned int flags,
unsigned int rtscts_rate,
unsigned int rtscts_duration)
{
struct ath5k_hw_4w_tx_ctl *tx_ctl;
unsigned int frame_len;
ATH5K_TRACE(ah->ah_sc);
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 continously sends
* noise on the channel, so it is important to avoid this.
*/
if (unlikely(tx_tries0 == 0)) {
ATH5K_ERR(ah->ah_sc, "zero retries\n");
WARN_ON(1);
return -EINVAL;
}
if (unlikely(tx_rate0 == 0)) {
ATH5K_ERR(ah->ah_sc, "zero rate\n");
WARN_ON(1);
return -EINVAL;
}
tx_power += ah->ah_txpower.txp_offset;
if (tx_power > AR5K_TUNE_MAX_TXPOWER)
tx_power = AR5K_TUNE_MAX_TXPOWER;
/* Clear descriptor */
memset(&desc->ud.ds_tx5212, 0, sizeof(struct ath5k_hw_5212_tx_desc));
/* Setup control descriptor */
/* Verify and set frame length */
/* remove padding we might have added before */
frame_len = pkt_len - ath5k_pad_size(hdr_len) + FCS_LEN;
if (frame_len & ~AR5K_4W_TX_DESC_CTL0_FRAME_LEN)
return -EINVAL;
tx_ctl->tx_control_0 = frame_len & AR5K_4W_TX_DESC_CTL0_FRAME_LEN;
/* Verify and set buffer length */
/* NB: beacon's BufLen must be a multiple of 4 bytes */
if (type == AR5K_PKT_TYPE_BEACON)
pkt_len = roundup(pkt_len, 4);
if (pkt_len & ~AR5K_4W_TX_DESC_CTL1_BUF_LEN)
return -EINVAL;
tx_ctl->tx_control_1 = pkt_len & AR5K_4W_TX_DESC_CTL1_BUF_LEN;
tx_ctl->tx_control_0 |=
AR5K_REG_SM(tx_power, AR5K_4W_TX_DESC_CTL0_XMIT_POWER) |
AR5K_REG_SM(antenna_mode, AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT);
tx_ctl->tx_control_1 |= AR5K_REG_SM(type,
AR5K_4W_TX_DESC_CTL1_FRAME_TYPE);
tx_ctl->tx_control_2 = AR5K_REG_SM(tx_tries0 + AR5K_TUNE_HWTXTRIES,
AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0);
tx_ctl->tx_control_3 = tx_rate0 & AR5K_4W_TX_DESC_CTL3_XMIT_RATE0;
#define _TX_FLAGS(_c, _flag) \
if (flags & AR5K_TXDESC_##_flag) { \
tx_ctl->tx_control_##_c |= \
AR5K_4W_TX_DESC_CTL##_c##_##_flag; \
}
_TX_FLAGS(0, CLRDMASK);
_TX_FLAGS(0, VEOL);
_TX_FLAGS(0, INTREQ);
_TX_FLAGS(0, RTSENA);
_TX_FLAGS(0, CTSENA);
_TX_FLAGS(1, NOACK);
#undef _TX_FLAGS
/*
* WEP crap
*/
if (key_index != AR5K_TXKEYIX_INVALID) {
tx_ctl->tx_control_0 |= AR5K_4W_TX_DESC_CTL0_ENCRYPT_KEY_VALID;
tx_ctl->tx_control_1 |= AR5K_REG_SM(key_index,
AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX);
}
/*
* RTS/CTS
*/
if (flags & (AR5K_TXDESC_RTSENA | AR5K_TXDESC_CTSENA)) {
if ((flags & AR5K_TXDESC_RTSENA) &&
(flags & AR5K_TXDESC_CTSENA))
return -EINVAL;
tx_ctl->tx_control_2 |= rtscts_duration &
AR5K_4W_TX_DESC_CTL2_RTS_DURATION;
tx_ctl->tx_control_3 |= AR5K_REG_SM(rtscts_rate,
AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE);
}
return 0;
}
/*
* Initialize a 4-word multi rate retry tx control descriptor on 5212
*/
static int
ath5k_hw_setup_mrr_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
unsigned int tx_rate1, u_int tx_tries1, u_int tx_rate2,
u_int tx_tries2, unsigned int tx_rate3, u_int tx_tries3)
{
struct ath5k_hw_4w_tx_ctl *tx_ctl;
/*
* Rates can be 0 as long as the retry count is 0 too.
* A zero rate and nonzero retry count will put the HW into a mode where
* it continously sends noise on the channel, so it is important to
* avoid this.
*/
if (unlikely((tx_rate1 == 0 && tx_tries1 != 0) ||
(tx_rate2 == 0 && tx_tries2 != 0) ||
(tx_rate3 == 0 && tx_tries3 != 0))) {
ATH5K_ERR(ah->ah_sc, "zero rate\n");
WARN_ON(1);
return -EINVAL;
}
if (ah->ah_version == AR5K_AR5212) {
tx_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;
}
/* no mrr support for cards older than 5212 */
static int
ath5k_hw_setup_no_mrr(struct ath5k_hw *ah, struct ath5k_desc *desc,
unsigned int tx_rate1, u_int tx_tries1, u_int tx_rate2,
u_int tx_tries2, unsigned int tx_rate3, u_int tx_tries3)
{
return 0;
}
/*
* Proccess the tx status descriptor on 5210/5211
*/
static int ath5k_hw_proc_2word_tx_status(struct ath5k_hw *ah,
struct ath5k_desc *desc, struct ath5k_tx_status *ts)
{
struct ath5k_hw_2w_tx_ctl *tx_ctl;
struct ath5k_hw_tx_status *tx_status;
ATH5K_TRACE(ah->ah_sc);
tx_ctl = &desc->ud.ds_tx5210.tx_ctl;
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_longretry = 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_rate[0] = AR5K_REG_MS(tx_ctl->tx_control_0,
AR5K_2W_TX_DESC_CTL0_XMIT_RATE);
ts->ts_retry[0] = ts->ts_longretry;
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;
}
/*
* Proccess a tx status descriptor on 5212
*/
static int ath5k_hw_proc_4word_tx_status(struct ath5k_hw *ah,
struct ath5k_desc *desc, struct ath5k_tx_status *ts)
{
struct ath5k_hw_4w_tx_ctl *tx_ctl;
struct ath5k_hw_tx_status *tx_status;
ATH5K_TRACE(ah->ah_sc);
tx_ctl = &desc->ud.ds_tx5212.tx_ctl;
tx_status = &desc->ud.ds_tx5212.tx_stat;
/* No frame has been send or error */
if (unlikely(!(tx_status->tx_status_1 & AR5K_DESC_TX_STATUS1_DONE)))
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_longretry = AR5K_REG_MS(tx_status->tx_status_0,
AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT);
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 = (tx_status->tx_status_1 &
AR5K_DESC_TX_STATUS1_XMIT_ANTENNA) ? 2 : 1;
ts->ts_status = 0;
ts->ts_final_idx = AR5K_REG_MS(tx_status->tx_status_1,
AR5K_DESC_TX_STATUS1_FINAL_TS_INDEX);
/* The longretry counter has the number of un-acked retries
* for the final rate. To get the total number of retries
* we have to add the retry counters for the other rates
* as well
*/
ts->ts_retry[ts->ts_final_idx] = ts->ts_longretry;
switch (ts->ts_final_idx) {
case 3:
ts->ts_rate[3] = AR5K_REG_MS(tx_ctl->tx_control_3,
AR5K_4W_TX_DESC_CTL3_XMIT_RATE3);
ts->ts_retry[2] = AR5K_REG_MS(tx_ctl->tx_control_2,
AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2);
ts->ts_longretry += ts->ts_retry[2];
/* fall through */
case 2:
ts->ts_rate[2] = AR5K_REG_MS(tx_ctl->tx_control_3,
AR5K_4W_TX_DESC_CTL3_XMIT_RATE2);
ts->ts_retry[1] = AR5K_REG_MS(tx_ctl->tx_control_2,
AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1);
ts->ts_longretry += ts->ts_retry[1];
/* fall through */
case 1:
ts->ts_rate[1] = AR5K_REG_MS(tx_ctl->tx_control_3,
AR5K_4W_TX_DESC_CTL3_XMIT_RATE1);
ts->ts_retry[0] = AR5K_REG_MS(tx_ctl->tx_control_2,
AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1);
ts->ts_longretry += ts->ts_retry[0];
/* fall through */
case 0:
ts->ts_rate[0] = tx_ctl->tx_control_3 &
AR5K_4W_TX_DESC_CTL3_XMIT_RATE0;
break;
}
/* TX error */
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;
}
/*
* RX Descriptors
*/
/*
* Initialize an rx control descriptor
*/
static 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;
ATH5K_TRACE(ah->ah_sc);
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));
/* Setup descriptor */
rx_ctl->rx_control_1 = size & AR5K_DESC_RX_CTL1_BUF_LEN;
if (unlikely(rx_ctl->rx_control_1 != size))
return -EINVAL;
if (flags & AR5K_RXDESC_INTREQ)
rx_ctl->rx_control_1 |= AR5K_DESC_RX_CTL1_INTREQ;
return 0;
}
/*
* Proccess the rx status descriptor on 5210/5211
*/
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.u.rx_stat;
/* No frame received / not ready */
if (unlikely(!(rx_status->rx_status_1 &
AR5K_5210_RX_DESC_STATUS1_DONE)))
return -EINPROGRESS;
/*
* 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_antenna = AR5K_REG_MS(rx_status->rx_status_0,
AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANTENNA);
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 */
rs->rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1,
AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP);
rs->rs_status = 0;
rs->rs_phyerr = 0;
/*
* 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;
if (rx_status->rx_status_1 &
AR5K_5210_RX_DESC_STATUS1_FIFO_OVERRUN)
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;
}
/*
* Proccess the rx status descriptor on 5212
*/
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;
struct ath5k_hw_rx_error *rx_err;
ATH5K_TRACE(ah->ah_sc);
rx_status = &desc->ud.ds_rx.u.rx_stat;
/* Overlay on error */
rx_err = &desc->ud.ds_rx.u.rx_err;
/* No frame received / not ready */
if (unlikely(!(rx_status->rx_status_1 &
AR5K_5212_RX_DESC_STATUS1_DONE)))
return -EINPROGRESS;
/*
* Frame receive status
*/
rs->rs_datalen = rx_status->rx_status_0 &
AR5K_5212_RX_DESC_STATUS0_DATA_LEN;
rs->rs_rssi = AR5K_REG_MS(rx_status->rx_status_0,
AR5K_5212_RX_DESC_STATUS0_RECEIVE_SIGNAL);
rs->rs_rate = AR5K_REG_MS(rx_status->rx_status_0,
AR5K_5212_RX_DESC_STATUS0_RECEIVE_RATE);
rs->rs_antenna = AR5K_REG_MS(rx_status->rx_status_0,
AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA);
rs->rs_more = !!(rx_status->rx_status_0 &
AR5K_5212_RX_DESC_STATUS0_MORE);
rs->rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1,
AR5K_5212_RX_DESC_STATUS1_RECEIVE_TIMESTAMP);
rs->rs_status = 0;
rs->rs_phyerr = 0;
/*
* Key table status
*/
if (rx_status->rx_status_1 & AR5K_5212_RX_DESC_STATUS1_KEY_INDEX_VALID)
rs->rs_keyix = AR5K_REG_MS(rx_status->rx_status_1,
AR5K_5212_RX_DESC_STATUS1_KEY_INDEX);
else
rs->rs_keyix = AR5K_RXKEYIX_INVALID;
/*
* Receive/descriptor errors
*/
if (!(rx_status->rx_status_1 &
AR5K_5212_RX_DESC_STATUS1_FRAME_RECEIVE_OK)) {
if (rx_status->rx_status_1 &
AR5K_5212_RX_DESC_STATUS1_CRC_ERROR)
rs->rs_status |= AR5K_RXERR_CRC;
if (rx_status->rx_status_1 &
AR5K_5212_RX_DESC_STATUS1_PHY_ERROR) {
rs->rs_status |= AR5K_RXERR_PHY;
rs->rs_phyerr |= AR5K_REG_MS(rx_err->rx_error_1,
AR5K_RX_DESC_ERROR1_PHY_ERROR_CODE);
}
if (rx_status->rx_status_1 &
AR5K_5212_RX_DESC_STATUS1_DECRYPT_CRC_ERROR)
rs->rs_status |= AR5K_RXERR_DECRYPT;
if (rx_status->rx_status_1 &
AR5K_5212_RX_DESC_STATUS1_MIC_ERROR)
rs->rs_status |= AR5K_RXERR_MIC;
}
return 0;
}
/*
* Init function pointers inside ath5k_hw struct
*/
int ath5k_hw_init_desc_functions(struct ath5k_hw *ah)
{
if (ah->ah_version != AR5K_AR5210 &&
ah->ah_version != AR5K_AR5211 &&
ah->ah_version != AR5K_AR5212)
return -ENOTSUPP;
/* XXX: What is this magic value and where is it used ? */
if (ah->ah_version == AR5K_AR5212)
ah->ah_magic = AR5K_EEPROM_MAGIC_5212;
else if (ah->ah_version == AR5K_AR5211)
ah->ah_magic = AR5K_EEPROM_MAGIC_5211;
if (ah->ah_version == AR5K_AR5212) {
ah->ah_setup_rx_desc = ath5k_hw_setup_rx_desc;
ah->ah_setup_tx_desc = ath5k_hw_setup_4word_tx_desc;
ah->ah_setup_mrr_tx_desc = ath5k_hw_setup_mrr_tx_desc;
ah->ah_proc_tx_desc = ath5k_hw_proc_4word_tx_status;
} else {
ah->ah_setup_rx_desc = ath5k_hw_setup_rx_desc;
ah->ah_setup_tx_desc = ath5k_hw_setup_2word_tx_desc;
ah->ah_setup_mrr_tx_desc = ath5k_hw_setup_no_mrr;
ah->ah_proc_tx_desc = ath5k_hw_proc_2word_tx_status;
}
if (ah->ah_version == AR5K_AR5212)
ah->ah_proc_rx_desc = ath5k_hw_proc_5212_rx_status;
else if (ah->ah_version <= AR5K_AR5211)
ah->ah_proc_rx_desc = ath5k_hw_proc_5210_rx_status;
return 0;
}

View File

@@ -0,0 +1,332 @@
/*
* 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.
*
*/
/*
* Internal RX/TX descriptor structures
* (rX: reserved fields possibily used by future versions of the ar5k chipset)
*/
/*
* common hardware RX control descriptor
*/
struct ath5k_hw_rx_ctl {
u32 rx_control_0; /* RX control word 0 */
u32 rx_control_1; /* RX control word 1 */
} __packed;
/* RX control word 0 field/sflags */
#define AR5K_DESC_RX_CTL0 0x00000000
/* RX control word 1 fields/flags */
#define AR5K_DESC_RX_CTL1_BUF_LEN 0x00000fff
#define AR5K_DESC_RX_CTL1_INTREQ 0x00002000
/*
* common hardware RX status descriptor
* 5210/11 and 5212 differ only in the flags defined below
*/
struct ath5k_hw_rx_status {
u32 rx_status_0; /* RX status word 0 */
u32 rx_status_1; /* RX status word 1 */
} __packed;
/* 5210/5211 */
/* RX status word 0 fields/flags */
#define AR5K_5210_RX_DESC_STATUS0_DATA_LEN 0x00000fff
#define AR5K_5210_RX_DESC_STATUS0_MORE 0x00001000
#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_RATE 0x00078000
#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_RATE_S 15
#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_SIGNAL 0x07f80000
#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_SIGNAL_S 19
#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANTENNA 0x38000000
#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANTENNA_S 27
/* RX status word 1 fields/flags */
#define AR5K_5210_RX_DESC_STATUS1_DONE 0x00000001
#define AR5K_5210_RX_DESC_STATUS1_FRAME_RECEIVE_OK 0x00000002
#define AR5K_5210_RX_DESC_STATUS1_CRC_ERROR 0x00000004
#define AR5K_5210_RX_DESC_STATUS1_FIFO_OVERRUN 0x00000008
#define AR5K_5210_RX_DESC_STATUS1_DECRYPT_CRC_ERROR 0x00000010
#define AR5K_5210_RX_DESC_STATUS1_PHY_ERROR 0x000000e0
#define AR5K_5210_RX_DESC_STATUS1_PHY_ERROR_S 5
#define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX_VALID 0x00000100
#define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX 0x00007e00
#define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX_S 9
#define AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP 0x0fff8000
#define AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S 15
#define AR5K_5210_RX_DESC_STATUS1_KEY_CACHE_MISS 0x10000000
/* 5212 */
/* RX status word 0 fields/flags */
#define AR5K_5212_RX_DESC_STATUS0_DATA_LEN 0x00000fff
#define AR5K_5212_RX_DESC_STATUS0_MORE 0x00001000
#define AR5K_5212_RX_DESC_STATUS0_DECOMP_CRC_ERROR 0x00002000
#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_RATE 0x000f8000
#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_RATE_S 15
#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_SIGNAL 0x0ff00000
#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_SIGNAL_S 20
#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA 0xf0000000
#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA_S 28
/* RX status word 1 fields/flags */
#define AR5K_5212_RX_DESC_STATUS1_DONE 0x00000001
#define AR5K_5212_RX_DESC_STATUS1_FRAME_RECEIVE_OK 0x00000002
#define AR5K_5212_RX_DESC_STATUS1_CRC_ERROR 0x00000004
#define AR5K_5212_RX_DESC_STATUS1_DECRYPT_CRC_ERROR 0x00000008
#define AR5K_5212_RX_DESC_STATUS1_PHY_ERROR 0x00000010
#define AR5K_5212_RX_DESC_STATUS1_MIC_ERROR 0x00000020
#define AR5K_5212_RX_DESC_STATUS1_KEY_INDEX_VALID 0x00000100
#define AR5K_5212_RX_DESC_STATUS1_KEY_INDEX 0x0000fe00
#define AR5K_5212_RX_DESC_STATUS1_KEY_INDEX_S 9
#define AR5K_5212_RX_DESC_STATUS1_RECEIVE_TIMESTAMP 0x7fff0000
#define AR5K_5212_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S 16
#define AR5K_5212_RX_DESC_STATUS1_KEY_CACHE_MISS 0x80000000
/*
* common hardware RX error descriptor
*/
struct ath5k_hw_rx_error {
u32 rx_error_0; /* RX status word 0 */
u32 rx_error_1; /* RX status word 1 */
} __packed;
/* RX error word 0 fields/flags */
#define AR5K_RX_DESC_ERROR0 0x00000000
/* RX error word 1 fields/flags */
#define AR5K_RX_DESC_ERROR1_PHY_ERROR_CODE 0x0000ff00
#define AR5K_RX_DESC_ERROR1_PHY_ERROR_CODE_S 8
/* PHY Error codes */
#define AR5K_DESC_RX_PHY_ERROR_NONE 0x00
#define AR5K_DESC_RX_PHY_ERROR_TIMING 0x20
#define AR5K_DESC_RX_PHY_ERROR_PARITY 0x40
#define AR5K_DESC_RX_PHY_ERROR_RATE 0x60
#define AR5K_DESC_RX_PHY_ERROR_LENGTH 0x80
#define AR5K_DESC_RX_PHY_ERROR_64QAM 0xa0
#define AR5K_DESC_RX_PHY_ERROR_SERVICE 0xc0
#define AR5K_DESC_RX_PHY_ERROR_TRANSMITOVR 0xe0
/*
* 5210/5211 hardware 2-word TX control descriptor
*/
struct ath5k_hw_2w_tx_ctl {
u32 tx_control_0; /* TX control word 0 */
u32 tx_control_1; /* TX control word 1 */
} __packed;
/* TX control word 0 fields/flags */
#define AR5K_2W_TX_DESC_CTL0_FRAME_LEN 0x00000fff
#define AR5K_2W_TX_DESC_CTL0_HEADER_LEN 0x0003f000 /*[5210 ?]*/
#define AR5K_2W_TX_DESC_CTL0_HEADER_LEN_S 12
#define AR5K_2W_TX_DESC_CTL0_XMIT_RATE 0x003c0000
#define AR5K_2W_TX_DESC_CTL0_XMIT_RATE_S 18
#define AR5K_2W_TX_DESC_CTL0_RTSENA 0x00400000
#define AR5K_2W_TX_DESC_CTL0_CLRDMASK 0x01000000
#define AR5K_2W_TX_DESC_CTL0_LONG_PACKET 0x00800000 /*[5210]*/
#define AR5K_2W_TX_DESC_CTL0_VEOL 0x00800000 /*[5211]*/
#define AR5K_2W_TX_DESC_CTL0_FRAME_TYPE 0x1c000000 /*[5210]*/
#define AR5K_2W_TX_DESC_CTL0_FRAME_TYPE_S 26
#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5210 0x02000000
#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5211 0x1e000000
#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_INTREQ 0x20000000
#define AR5K_2W_TX_DESC_CTL0_ENCRYPT_KEY_VALID 0x40000000
/* TX control word 1 fields/flags */
#define AR5K_2W_TX_DESC_CTL1_BUF_LEN 0x00000fff
#define AR5K_2W_TX_DESC_CTL1_MORE 0x00001000
#define AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_5210 0x0007e000
#define AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_5211 0x000fe000
#define AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX \
(ah->ah_version == AR5K_AR5210 ? \
AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_5210 : \
AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_5211)
#define AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_S 13
#define AR5K_2W_TX_DESC_CTL1_FRAME_TYPE 0x00700000 /*[5211]*/
#define AR5K_2W_TX_DESC_CTL1_FRAME_TYPE_S 20
#define AR5K_2W_TX_DESC_CTL1_NOACK 0x00800000 /*[5211]*/
#define AR5K_2W_TX_DESC_CTL1_RTS_DURATION 0xfff80000 /*[5210 ?]*/
/* Frame types */
#define AR5K_AR5210_TX_DESC_FRAME_TYPE_NORMAL 0x00
#define AR5K_AR5210_TX_DESC_FRAME_TYPE_ATIM 0x04
#define AR5K_AR5210_TX_DESC_FRAME_TYPE_PSPOLL 0x08
#define AR5K_AR5210_TX_DESC_FRAME_TYPE_NO_DELAY 0x0c
#define AR5K_AR5210_TX_DESC_FRAME_TYPE_PIFS 0x10
/*
* 5212 hardware 4-word TX control descriptor
*/
struct ath5k_hw_4w_tx_ctl {
u32 tx_control_0; /* TX control word 0 */
#define AR5K_4W_TX_DESC_CTL0_FRAME_LEN 0x00000fff
#define AR5K_4W_TX_DESC_CTL0_XMIT_POWER 0x003f0000
#define AR5K_4W_TX_DESC_CTL0_XMIT_POWER_S 16
#define AR5K_4W_TX_DESC_CTL0_RTSENA 0x00400000
#define AR5K_4W_TX_DESC_CTL0_VEOL 0x00800000
#define AR5K_4W_TX_DESC_CTL0_CLRDMASK 0x01000000
#define AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT 0x1e000000
#define AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT_S 25
#define AR5K_4W_TX_DESC_CTL0_INTREQ 0x20000000
#define AR5K_4W_TX_DESC_CTL0_ENCRYPT_KEY_VALID 0x40000000
#define AR5K_4W_TX_DESC_CTL0_CTSENA 0x80000000
u32 tx_control_1; /* TX control word 1 */
#define AR5K_4W_TX_DESC_CTL1_BUF_LEN 0x00000fff
#define AR5K_4W_TX_DESC_CTL1_MORE 0x00001000
#define AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX 0x000fe000
#define AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_S 13
#define AR5K_4W_TX_DESC_CTL1_FRAME_TYPE 0x00f00000
#define AR5K_4W_TX_DESC_CTL1_FRAME_TYPE_S 20
#define AR5K_4W_TX_DESC_CTL1_NOACK 0x01000000
#define AR5K_4W_TX_DESC_CTL1_COMP_PROC 0x06000000
#define AR5K_4W_TX_DESC_CTL1_COMP_PROC_S 25
#define AR5K_4W_TX_DESC_CTL1_COMP_IV_LEN 0x18000000
#define AR5K_4W_TX_DESC_CTL1_COMP_IV_LEN_S 27
#define AR5K_4W_TX_DESC_CTL1_COMP_ICV_LEN 0x60000000
#define AR5K_4W_TX_DESC_CTL1_COMP_ICV_LEN_S 29
u32 tx_control_2; /* TX control word 2 */
#define AR5K_4W_TX_DESC_CTL2_RTS_DURATION 0x00007fff
#define AR5K_4W_TX_DESC_CTL2_DURATION_UPDATE_ENABLE 0x00008000
#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0 0x000f0000
#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0_S 16
#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1 0x00f00000
#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1_S 20
#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2 0x0f000000
#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2_S 24
#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES3 0xf0000000
#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES3_S 28
u32 tx_control_3; /* TX control word 3 */
#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE0 0x0000001f
#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE1 0x000003e0
#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE1_S 5
#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE2 0x00007c00
#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE2_S 10
#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE3 0x000f8000
#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE3_S 15
#define AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE 0x01f00000
#define AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE_S 20
} __packed;
/*
* Common TX status descriptor
*/
struct ath5k_hw_tx_status {
u32 tx_status_0; /* TX status word 0 */
u32 tx_status_1; /* TX status word 1 */
} __packed;
/* TX status word 0 fields/flags */
#define AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK 0x00000001
#define AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES 0x00000002
#define AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN 0x00000004
#define AR5K_DESC_TX_STATUS0_FILTERED 0x00000008
/*???
#define AR5K_DESC_TX_STATUS0_RTS_FAIL_COUNT 0x000000f0
#define AR5K_DESC_TX_STATUS0_RTS_FAIL_COUNT_S 4
*/
#define AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT 0x000000f0
#define AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT_S 4
/*???
#define AR5K_DESC_TX_STATUS0_DATA_FAIL_COUNT 0x00000f00
#define AR5K_DESC_TX_STATUS0_DATA_FAIL_COUNT_S 8
*/
#define AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT 0x00000f00
#define AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT_S 8
#define AR5K_DESC_TX_STATUS0_VIRT_COLL_COUNT 0x0000f000
#define AR5K_DESC_TX_STATUS0_VIRT_COLL_COUNT_S 12
#define AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP 0xffff0000
#define AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP_S 16
/* TX status word 1 fields/flags */
#define AR5K_DESC_TX_STATUS1_DONE 0x00000001
#define AR5K_DESC_TX_STATUS1_SEQ_NUM 0x00001ffe
#define AR5K_DESC_TX_STATUS1_SEQ_NUM_S 1
#define AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH 0x001fe000
#define AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH_S 13
#define AR5K_DESC_TX_STATUS1_FINAL_TS_INDEX 0x00600000
#define AR5K_DESC_TX_STATUS1_FINAL_TS_INDEX_S 21
#define AR5K_DESC_TX_STATUS1_COMP_SUCCESS 0x00800000
#define AR5K_DESC_TX_STATUS1_XMIT_ANTENNA 0x01000000
/*
* 5210/5211 hardware TX descriptor
*/
struct ath5k_hw_5210_tx_desc {
struct ath5k_hw_2w_tx_ctl tx_ctl;
struct ath5k_hw_tx_status tx_stat;
} __packed;
/*
* 5212 hardware TX descriptor
*/
struct ath5k_hw_5212_tx_desc {
struct ath5k_hw_4w_tx_ctl tx_ctl;
struct ath5k_hw_tx_status tx_stat;
} __packed;
/*
* common hardware RX descriptor
*/
struct ath5k_hw_all_rx_desc {
struct ath5k_hw_rx_ctl rx_ctl;
union {
struct ath5k_hw_rx_status rx_stat;
struct ath5k_hw_rx_error rx_err;
} u;
} __packed;
/*
* Atheros hardware descriptor
* This is read and written to by the hardware
*/
struct ath5k_desc {
u32 ds_link; /* physical address of the next descriptor */
u32 ds_data; /* physical address of data buffer (skb) */
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;
#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+]*/

View File

@@ -0,0 +1,703 @@
/*
* 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 *
\*************************************/
/*
* dma.c - 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 iterrupt
* status registers (ISR).
*
* TODO: Handle SISR on 5211+ and introduce a function to return the queue
* number that resulted the interrupt.
*/
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
#include "base.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_TRACE(ah->ah_sc);
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
*/
int ath5k_hw_stop_rx_dma(struct ath5k_hw *ah)
{
unsigned int i;
ATH5K_TRACE(ah->ah_sc);
ath5k_hw_reg_write(ah, AR5K_CR_RXD, AR5K_CR);
/*
* It may take some time to disable the DMA receive unit
*/
for (i = 1000; i > 0 &&
(ath5k_hw_reg_read(ah, AR5K_CR) & AR5K_CR_RXE) != 0;
i--)
udelay(10);
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
*
* XXX: Should we check if rx is enabled before setting rxdp ?
*/
void ath5k_hw_set_rxdp(struct ath5k_hw *ah, u32 phys_addr)
{
ATH5K_TRACE(ah->ah_sc);
ath5k_hw_reg_write(ah, phys_addr, AR5K_RXDP);
}
/**********\
* 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;
ATH5K_TRACE(ah->ah_sc);
AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
/* Return if queue is declared inactive */
if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE)
return -EIO;
if (ah->ah_version == AR5K_AR5210) {
tx_queue = ath5k_hw_reg_read(ah, AR5K_CR);
/*
* Set the queue by type on 5210
*/
switch (ah->ah_txq[queue].tqi_type) {
case AR5K_TX_QUEUE_DATA:
tx_queue |= AR5K_CR_TXE0 & ~AR5K_CR_TXD0;
break;
case AR5K_TX_QUEUE_BEACON:
tx_queue |= AR5K_CR_TXE1 & ~AR5K_CR_TXD1;
ath5k_hw_reg_write(ah, AR5K_BCR_TQ1V | AR5K_BCR_BDMAE,
AR5K_BSR);
break;
case AR5K_TX_QUEUE_CAB:
tx_queue |= AR5K_CR_TXE1 & ~AR5K_CR_TXD1;
ath5k_hw_reg_write(ah, AR5K_BCR_TQ1FV | AR5K_BCR_TQ1V |
AR5K_BCR_BDMAE, AR5K_BSR);
break;
default:
return -EINVAL;
}
/* Start queue */
ath5k_hw_reg_write(ah, tx_queue, AR5K_CR);
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.
*
*/
int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue)
{
unsigned int i = 40;
u32 tx_queue, pending;
ATH5K_TRACE(ah->ah_sc);
AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
/* Return if queue is declared inactive */
if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE)
return -EIO;
if (ah->ah_version == AR5K_AR5210) {
tx_queue = ath5k_hw_reg_read(ah, AR5K_CR);
/*
* Set by queue type
*/
switch (ah->ah_txq[queue].tqi_type) {
case AR5K_TX_QUEUE_DATA:
tx_queue |= AR5K_CR_TXD0 & ~AR5K_CR_TXE0;
break;
case AR5K_TX_QUEUE_BEACON:
case AR5K_TX_QUEUE_CAB:
/* XXX Fix me... */
tx_queue |= AR5K_CR_TXD1 & ~AR5K_CR_TXD1;
ath5k_hw_reg_write(ah, 0, AR5K_BSR);
break;
default:
return -EINVAL;
}
/* Stop queue */
ath5k_hw_reg_write(ah, tx_queue, AR5K_CR);
ath5k_hw_reg_read(ah, AR5K_CR);
} else {
/*
* Schedule TX disable and wait until queue is empty
*/
AR5K_REG_WRITE_Q(ah, AR5K_QCU_TXD, queue);
/*Check for pending frames*/
do {
pending = ath5k_hw_reg_read(ah,
AR5K_QUEUE_STATUS(queue)) &
AR5K_QCU_STS_FRMPENDCNT;
udelay(100);
} while (--i && pending);
/* 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_CHANEL_IDLE_HIGH);
/* Wait a while and disable mechanism */
udelay(200);
AR5K_REG_DISABLE_BITS(ah, AR5K_QUIET_CTL1,
AR5K_QUIET_CTL1_QT_EN);
/* Re-check for pending frames */
i = 40;
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_CHANEL_IDLE_HIGH);
}
/* Clear register */
ath5k_hw_reg_write(ah, 0, AR5K_QCU_TXD);
if (pending)
return -EBUSY;
}
/* TODO: Check for success on 5210 else return error */
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;
ATH5K_TRACE(ah->ah_sc);
AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
/*
* Get the transmit queue descriptor pointer from the selected queue
*/
/*5210 doesn't have QCU*/
if (ah->ah_version == AR5K_AR5210) {
switch (ah->ah_txq[queue].tqi_type) {
case AR5K_TX_QUEUE_DATA:
tx_reg = AR5K_NOQCU_TXDP0;
break;
case AR5K_TX_QUEUE_BEACON:
case AR5K_TX_QUEUE_CAB:
tx_reg = AR5K_NOQCU_TXDP1;
break;
default:
return 0xffffffff;
}
} else {
tx_reg = AR5K_QUEUE_TXDP(queue);
}
return ath5k_hw_reg_read(ah, tx_reg);
}
/**
* ath5k_hw_set_txdp - Set TX Descriptor's address for a specific queue
*
* @ah: The &struct ath5k_hw
* @queue: The hw queue number
*
* 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;
ATH5K_TRACE(ah->ah_sc);
AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
/*
* Set the transmit queue descriptor pointer register by type
* on 5210
*/
if (ah->ah_version == AR5K_AR5210) {
switch (ah->ah_txq[queue].tqi_type) {
case AR5K_TX_QUEUE_DATA:
tx_reg = AR5K_NOQCU_TXDP0;
break;
case AR5K_TX_QUEUE_BEACON:
case AR5K_TX_QUEUE_CAB:
tx_reg = AR5K_NOQCU_TXDP1;
break;
default:
return -EINVAL;
}
} else {
/*
* Set the transmit queue descriptor pointer for
* the selected queue on QCU for 5211+
* (this won't work if the queue is still active)
*/
if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue))
return -EIO;
tx_reg = AR5K_QUEUE_TXDP(queue);
}
/* Set descriptor pointer */
ath5k_hw_reg_write(ah, phys_addr, tx_reg);
return 0;
}
/**
* 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 it's data. Lowering this results sending small
* frames more quickly but can lead to tx underruns, raising it a lot can
* result other problems (i think bmiss is related). 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 have reached maximum/minimum.
*
* XXX: Link this with tx DMA size ?
* XXX: Use it to save interrupts ?
* TODO: Needs testing, i think it's related to bmiss...
*/
int ath5k_hw_update_tx_triglevel(struct ath5k_hw *ah, bool increase)
{
u32 trigger_level, imr;
int ret = -EIO;
ATH5K_TRACE(ah->ah_sc);
/*
* Disable interrupts by setting the mask
*/
imr = ath5k_hw_set_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)
{
ATH5K_TRACE(ah->ah_sc);
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 use read-and-clear register, so after this function is called ISR
* is zeroed.
*/
int ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask)
{
u32 data;
ATH5K_TRACE(ah->ah_sc);
/*
* Read interrupt status from the Interrupt Status register
* on 5210
*/
if (ah->ah_version == AR5K_AR5210) {
data = ath5k_hw_reg_read(ah, AR5K_ISR);
if (unlikely(data == AR5K_INT_NOCARD)) {
*interrupt_mask = data;
return -ENODEV;
}
} else {
/*
* Read interrupt status from Interrupt
* Status Register shadow copy (Read And Clear)
*
* Note: PISR/SISR Not available on 5210
*/
data = ath5k_hw_reg_read(ah, AR5K_RAC_PISR);
if (unlikely(data == AR5K_INT_NOCARD)) {
*interrupt_mask = data;
return -ENODEV;
}
}
/*
* Get abstract interrupt mask (driver-compatible)
*/
*interrupt_mask = (data & AR5K_INT_COMMON) & ah->ah_imr;
if (ah->ah_version != AR5K_AR5210) {
u32 sisr2 = ath5k_hw_reg_read(ah, AR5K_RAC_SISR2);
/*HIU = Host Interface Unit (PCI etc)*/
if (unlikely(data & (AR5K_ISR_HIUERR)))
*interrupt_mask |= AR5K_INT_FATAL;
/*Beacon Not Ready*/
if (unlikely(data & (AR5K_ISR_BNR)))
*interrupt_mask |= AR5K_INT_BNR;
if (unlikely(sisr2 & (AR5K_SISR2_SSERR |
AR5K_SISR2_DPERR |
AR5K_SISR2_MCABT)))
*interrupt_mask |= AR5K_INT_FATAL;
if (data & AR5K_ISR_TIM)
*interrupt_mask |= AR5K_INT_TIM;
if (data & 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;
}
if (data & AR5K_ISR_RXDOPPLER)
*interrupt_mask |= AR5K_INT_RX_DOPPLER;
if (data & AR5K_ISR_QCBRORN) {
*interrupt_mask |= AR5K_INT_QCBRORN;
ah->ah_txq_isr |= AR5K_REG_MS(
ath5k_hw_reg_read(ah, AR5K_RAC_SISR3),
AR5K_SISR3_QCBRORN);
}
if (data & AR5K_ISR_QCBRURN) {
*interrupt_mask |= AR5K_INT_QCBRURN;
ah->ah_txq_isr |= AR5K_REG_MS(
ath5k_hw_reg_read(ah, AR5K_RAC_SISR3),
AR5K_SISR3_QCBRURN);
}
if (data & AR5K_ISR_QTRIG) {
*interrupt_mask |= AR5K_INT_QTRIG;
ah->ah_txq_isr |= AR5K_REG_MS(
ath5k_hw_reg_read(ah, AR5K_RAC_SISR4),
AR5K_SISR4_QTRIG);
}
if (data & AR5K_ISR_TXOK)
ah->ah_txq_isr |= AR5K_REG_MS(
ath5k_hw_reg_read(ah, AR5K_RAC_SISR0),
AR5K_SISR0_QCU_TXOK);
if (data & AR5K_ISR_TXDESC)
ah->ah_txq_isr |= AR5K_REG_MS(
ath5k_hw_reg_read(ah, AR5K_RAC_SISR0),
AR5K_SISR0_QCU_TXDESC);
if (data & AR5K_ISR_TXERR)
ah->ah_txq_isr |= AR5K_REG_MS(
ath5k_hw_reg_read(ah, AR5K_RAC_SISR1),
AR5K_SISR1_QCU_TXERR);
if (data & AR5K_ISR_TXEOL)
ah->ah_txq_isr |= AR5K_REG_MS(
ath5k_hw_reg_read(ah, AR5K_RAC_SISR1),
AR5K_SISR1_QCU_TXEOL);
if (data & AR5K_ISR_TXURN)
ah->ah_txq_isr |= AR5K_REG_MS(
ath5k_hw_reg_read(ah, AR5K_RAC_SISR2),
AR5K_SISR2_QCU_TXURN);
} else {
if (unlikely(data & (AR5K_ISR_SSERR | AR5K_ISR_MCABT
| AR5K_ISR_HIUERR | 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;
*/
}
/*
* 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;
if (new_mask & AR5K_INT_FATAL) {
int_mask |= AR5K_IMR_HIUERR;
simr2 |= (AR5K_SIMR2_MCABT | AR5K_SIMR2_SSERR
| AR5K_SIMR2_DPERR);
}
/*Beacon Not Ready*/
if (new_mask & AR5K_INT_BNR)
int_mask |= AR5K_INT_BNR;
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;
if (new_mask & AR5K_INT_RX_DOPPLER)
int_mask |= AR5K_IMR_RXDOPPLER;
/* Note: Per queue interrupt masks
* are set via reset_tx_queue (qcu.c) */
ath5k_hw_reg_write(ah, int_mask, AR5K_PIMR);
ath5k_hw_reg_write(ah, simr2, AR5K_SIMR2);
} else {
if (new_mask & AR5K_INT_FATAL)
int_mask |= (AR5K_IMR_SSERR | AR5K_IMR_MCABT
| AR5K_IMR_HIUERR | AR5K_IMR_DPERR);
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;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,479 @@
/*
* 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_MAGIC_5212 0x0000145c /* 5212 */
#define AR5K_EEPROM_MAGIC_5211 0x0000145b /* 5211 */
#define AR5K_EEPROM_MAGIC_5210 0x0000145a /* 5210 */
#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 a/XR mode (eeprom_init) */
#define AR5K_EEPROM_HDR_DEVICE(_v) (((_v) >> 11) & 0x7)
#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)
#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)
#define AR5K_EEPROM_MASK_R1(_v) ((_v) & 0x3)
#define AR5K_EEPROM_MISC5 AR5K_EEPROM_INFO(9)
#define AR5K_EEPROM_COMP_DIS(_v) ((_v) & 0x1)
#define AR5K_EEPROM_AES_DIS(_v) (((_v) >> 1) & 0x1)
#define AR5K_EEPROM_FF_DIS(_v) (((_v) >> 2) & 0x1)
#define AR5K_EEPROM_BURST_DIS(_v) (((_v) >> 3) & 0x1)
#define AR5K_EEPROM_MAX_QCU(_v) (((_v) >> 4) & 0xf)
#define AR5K_EEPROM_HEAVY_CLIP_EN(_v) (((_v) >> 8) & 0x1)
#define AR5K_EEPROM_KEY_CACHE_SIZE(_v) (((_v) >> 12) & 0xf)
#define AR5K_EEPROM_MISC6 AR5K_EEPROM_INFO(10)
#define AR5K_EEPROM_TX_CHAIN_DIS ((_v) & 0x8)
#define AR5K_EEPROM_RX_CHAIN_DIS (((_v) >> 3) & 0x8)
#define AR5K_EEPROM_FCC_MID_EN (((_v) >> 6) & 0x1)
#define AR5K_EEPROM_JAP_U1EVEN_EN (((_v) >> 7) & 0x1)
#define AR5K_EEPROM_JAP_U2_EN (((_v) >> 8) & 0x1)
#define AR5K_EEPROM_JAP_U1ODD_EN (((_v) >> 9) & 0x1)
#define AR5K_EEPROM_JAP_11A_NEW_EN (((_v) >> 10) & 0x1)
/* 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_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 { \
ret = ath5k_hw_eeprom_read(ah, (_o), &(_v)); \
if (ret) \
return ret; \
} 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,
};
/* Default CTL ids for the 3 main reg domains.
* Atheros only uses these by default but vendors
* can have up to 32 different CTLs for different
* scenarios. Note that theese values are ORed with
* the mode id (above) so we can have up to 24 CTL
* datasets out of these 3 main regdomains. That leaves
* 8 ids that can be used by vendors and since 0x20 is
* missing from HAL sources i guess this is the set of
* custom CTLs vendors can use. */
#define AR5K_CTL_FCC 0x10
#define AR5K_CTL_CUSTOM 0x20
#define AR5K_CTL_ETSI 0x30
#define AR5K_CTL_MKK 0x40
/* Indicates a CTL with only mode set and
* no reg domain mapping, such CTLs are used
* for world roaming domains or simply when
* a reg domain is not set */
#define AR5K_CTL_NO_REGDOMAIN 0xf0
/* Indicates an empty (invalid) CTL */
#define AR5K_CTL_NO_CTL 0xff
/* 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;
};
/* EEPROM calibration data */
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];
/* Antenna raw switch tables */
u32 ee_antenna[AR5K_EEPROM_N_MODES][AR5K_ANT_MAX];
};

View File

@@ -0,0 +1,176 @@
/*
* 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"
#include "base.h"
/*
* Set led state
*/
void ath5k_hw_set_ledstate(struct ath5k_hw *ah, unsigned int state)
{
u32 led;
/*5210 has different led mode handling*/
u32 led_5210;
ATH5K_TRACE(ah->ah_sc);
/*Reset led status*/
if (ah->ah_version != AR5K_AR5210)
AR5K_REG_DISABLE_BITS(ah, AR5K_PCICFG,
AR5K_PCICFG_LEDMODE | AR5K_PCICFG_LED);
else
AR5K_REG_DISABLE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_LED);
/*
* Some blinking values, define at your wish
*/
switch (state) {
case AR5K_LED_SCAN:
case AR5K_LED_AUTH:
led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_PEND;
led_5210 = AR5K_PCICFG_LED_PEND | AR5K_PCICFG_LED_BCTL;
break;
case AR5K_LED_INIT:
led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_NONE;
led_5210 = AR5K_PCICFG_LED_PEND;
break;
case AR5K_LED_ASSOC:
case AR5K_LED_RUN:
led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_ASSOC;
led_5210 = AR5K_PCICFG_LED_ASSOC;
break;
default:
led = AR5K_PCICFG_LEDMODE_PROM | AR5K_PCICFG_LED_NONE;
led_5210 = AR5K_PCICFG_LED_PEND;
break;
}
/*Write new status to the register*/
if (ah->ah_version != AR5K_AR5210)
AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, led);
else
AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, led_5210);
}
/*
* Set GPIO inputs
*/
int ath5k_hw_set_gpio_input(struct ath5k_hw *ah, u32 gpio)
{
ATH5K_TRACE(ah->ah_sc);
if (gpio >= AR5K_NUM_GPIO)
return -EINVAL;
ath5k_hw_reg_write(ah,
(ath5k_hw_reg_read(ah, AR5K_GPIOCR) & ~AR5K_GPIOCR_OUT(gpio))
| AR5K_GPIOCR_IN(gpio), AR5K_GPIOCR);
return 0;
}
/*
* Set GPIO outputs
*/
int ath5k_hw_set_gpio_output(struct ath5k_hw *ah, u32 gpio)
{
ATH5K_TRACE(ah->ah_sc);
if (gpio >= AR5K_NUM_GPIO)
return -EINVAL;
ath5k_hw_reg_write(ah,
(ath5k_hw_reg_read(ah, AR5K_GPIOCR) & ~AR5K_GPIOCR_OUT(gpio))
| AR5K_GPIOCR_OUT(gpio), AR5K_GPIOCR);
return 0;
}
/*
* Get GPIO state
*/
u32 ath5k_hw_get_gpio(struct ath5k_hw *ah, u32 gpio)
{
ATH5K_TRACE(ah->ah_sc);
if (gpio >= AR5K_NUM_GPIO)
return 0xffffffff;
/* GPIO input magic */
return ((ath5k_hw_reg_read(ah, AR5K_GPIODI) & AR5K_GPIODI_M) >> gpio) &
0x1;
}
/*
* Set GPIO state
*/
int ath5k_hw_set_gpio(struct ath5k_hw *ah, u32 gpio, u32 val)
{
u32 data;
ATH5K_TRACE(ah->ah_sc);
if (gpio >= AR5K_NUM_GPIO)
return -EINVAL;
/* GPIO output magic */
data = ath5k_hw_reg_read(ah, AR5K_GPIODO);
data &= ~(1 << gpio);
data |= (val & 1) << gpio;
ath5k_hw_reg_write(ah, data, AR5K_GPIODO);
return 0;
}
/*
* Initialize the GPIO interrupt (RFKill switch)
*/
void ath5k_hw_set_gpio_intr(struct ath5k_hw *ah, unsigned int gpio,
u32 interrupt_level)
{
u32 data;
ATH5K_TRACE(ah->ah_sc);
if (gpio >= AR5K_NUM_GPIO)
return;
/*
* Set the GPIO interrupt
*/
data = (ath5k_hw_reg_read(ah, AR5K_GPIOCR) &
~(AR5K_GPIOCR_INT_SEL(gpio) | AR5K_GPIOCR_INT_SELH |
AR5K_GPIOCR_INT_ENA | AR5K_GPIOCR_OUT(gpio))) |
(AR5K_GPIOCR_INT_SEL(gpio) | AR5K_GPIOCR_INT_ENA);
ath5k_hw_reg_write(ah, interrupt_level ? data :
(data | AR5K_GPIOCR_INT_SELH), AR5K_GPIOCR);
ah->ah_imr |= AR5K_IMR_GPIO;
/* Enable GPIO interrupts */
AR5K_REG_ENABLE_BITS(ah, AR5K_PIMR, AR5K_IMR_GPIO);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,186 @@
/*
* 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.
*
*/
#include <linux/pci.h>
#include "ath5k.h"
#include "base.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 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, 1) },
/* IBM-specific AR5212 (all others) */
{ PCI_VDEVICE(ATHEROS, PCI_DEVICE_ID_ATHEROS_AR5212_IBM), ATH_LED(0, 0) },
{ }
};
void ath5k_led_enable(struct ath5k_softc *sc)
{
if (test_bit(ATH_STAT_LEDSOFT, sc->status)) {
ath5k_hw_set_gpio_output(sc->ah, sc->led_pin);
ath5k_led_off(sc);
}
}
static void ath5k_led_on(struct ath5k_softc *sc)
{
if (!test_bit(ATH_STAT_LEDSOFT, sc->status))
return;
ath5k_hw_set_gpio(sc->ah, sc->led_pin, sc->led_on);
}
void ath5k_led_off(struct ath5k_softc *sc)
{
if (!test_bit(ATH_STAT_LEDSOFT, sc->status))
return;
ath5k_hw_set_gpio(sc->ah, sc->led_pin, !sc->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->sc);
else
ath5k_led_on(led->sc);
}
static int
ath5k_register_led(struct ath5k_softc *sc, struct ath5k_led *led,
const char *name, char *trigger)
{
int err;
led->sc = sc;
strncpy(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(&sc->pdev->dev, &led->led_dev);
if (err) {
ATH5K_WARN(sc, "could not register LED %s\n", name);
led->sc = NULL;
}
return err;
}
static void
ath5k_unregister_led(struct ath5k_led *led)
{
if (!led->sc)
return;
led_classdev_unregister(&led->led_dev);
ath5k_led_off(led->sc);
led->sc = NULL;
}
void ath5k_unregister_leds(struct ath5k_softc *sc)
{
ath5k_unregister_led(&sc->rx_led);
ath5k_unregister_led(&sc->tx_led);
}
int ath5k_init_leds(struct ath5k_softc *sc)
{
int ret = 0;
struct ieee80211_hw *hw = sc->hw;
struct pci_dev *pdev = sc->pdev;
char name[ATH5K_LED_MAX_NAME_LEN + 1];
const struct pci_device_id *match;
match = pci_match_id(&ath5k_led_devices[0], pdev);
if (match) {
__set_bit(ATH_STAT_LEDSOFT, sc->status);
sc->led_pin = ATH_PIN(match->driver_data);
sc->led_on = ATH_POLARITY(match->driver_data);
}
if (!test_bit(ATH_STAT_LEDSOFT, sc->status))
goto out;
ath5k_led_enable(sc);
snprintf(name, sizeof(name), "ath5k-%s::rx", wiphy_name(hw->wiphy));
ret = ath5k_register_led(sc, &sc->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(sc, &sc->tx_led, name,
ieee80211_get_tx_led_name(hw));
out:
return ret;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,549 @@
/*
* 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, DFS Control Unit Functions
\********************************************/
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
#include "base.h"
/*
* Get properties for a transmit queue
*/
int ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue,
struct ath5k_txq_info *queue_info)
{
ATH5K_TRACE(ah->ah_sc);
memcpy(queue_info, &ah->ah_txq[queue], sizeof(struct ath5k_txq_info));
return 0;
}
/*
* Set properties for a transmit queue
*/
int ath5k_hw_set_tx_queueprops(struct ath5k_hw *ah, int queue,
const struct ath5k_txq_info *queue_info)
{
ATH5K_TRACE(ah->ah_sc);
AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE)
return -EIO;
memcpy(&ah->ah_txq[queue], queue_info, sizeof(struct ath5k_txq_info));
/*XXX: Is this supported on 5210 ?*/
if ((queue_info->tqi_type == AR5K_TX_QUEUE_DATA &&
((queue_info->tqi_subtype == AR5K_WME_AC_VI) ||
(queue_info->tqi_subtype == AR5K_WME_AC_VO))) ||
queue_info->tqi_type == AR5K_TX_QUEUE_UAPSD)
ah->ah_txq[queue].tqi_flags |= AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS;
return 0;
}
/*
* Initialize a transmit queue
*/
int ath5k_hw_setup_tx_queue(struct ath5k_hw *ah, enum ath5k_tx_queue queue_type,
struct ath5k_txq_info *queue_info)
{
unsigned int queue;
int ret;
ATH5K_TRACE(ah->ah_sc);
/*
* Get queue by type
*/
/*5210 only has 2 queues*/
if (ah->ah_version == AR5K_AR5210) {
switch (queue_type) {
case AR5K_TX_QUEUE_DATA:
queue = AR5K_TX_QUEUE_ID_NOQCU_DATA;
break;
case AR5K_TX_QUEUE_BEACON:
case AR5K_TX_QUEUE_CAB:
queue = AR5K_TX_QUEUE_ID_NOQCU_BEACON;
break;
default:
return -EINVAL;
}
} else {
switch (queue_type) {
case AR5K_TX_QUEUE_DATA:
for (queue = AR5K_TX_QUEUE_ID_DATA_MIN;
ah->ah_txq[queue].tqi_type !=
AR5K_TX_QUEUE_INACTIVE; queue++) {
if (queue > AR5K_TX_QUEUE_ID_DATA_MAX)
return -EINVAL;
}
break;
case AR5K_TX_QUEUE_UAPSD:
queue = AR5K_TX_QUEUE_ID_UAPSD;
break;
case AR5K_TX_QUEUE_BEACON:
queue = AR5K_TX_QUEUE_ID_BEACON;
break;
case AR5K_TX_QUEUE_CAB:
queue = AR5K_TX_QUEUE_ID_CAB;
break;
case AR5K_TX_QUEUE_XR_DATA:
if (ah->ah_version != AR5K_AR5212)
ATH5K_ERR(ah->ah_sc,
"XR data queues only supported in"
" 5212!\n");
queue = AR5K_TX_QUEUE_ID_XR_DATA;
break;
default:
return -EINVAL;
}
}
/*
* Setup internal queue structure
*/
memset(&ah->ah_txq[queue], 0, sizeof(struct ath5k_txq_info));
ah->ah_txq[queue].tqi_type = queue_type;
if (queue_info != NULL) {
queue_info->tqi_type = queue_type;
ret = ath5k_hw_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;
}
/*
* Get number of pending frames
* for a specific queue [5211+]
*/
u32 ath5k_hw_num_tx_pending(struct ath5k_hw *ah, unsigned int queue)
{
u32 pending;
ATH5K_TRACE(ah->ah_sc);
AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
/* Return if queue is declared inactive */
if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE)
return 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;
}
/*
* Set a transmit queue inactive
*/
void ath5k_hw_release_tx_queue(struct ath5k_hw *ah, unsigned int queue)
{
ATH5K_TRACE(ah->ah_sc);
if (WARN_ON(queue >= ah->ah_capabilities.cap_queues.q_tx_num))
return;
/* This queue will be skipped in further operations */
ah->ah_txq[queue].tqi_type = AR5K_TX_QUEUE_INACTIVE;
/*For SIMR setup*/
AR5K_Q_DISABLE_BITS(ah->ah_txq_status, queue);
}
/*
* Set DFS properties for a transmit queue on DCU
*/
int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue)
{
u32 cw_min, cw_max, retry_lg, retry_sh;
struct ath5k_txq_info *tq = &ah->ah_txq[queue];
ATH5K_TRACE(ah->ah_sc);
AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
tq = &ah->ah_txq[queue];
if (tq->tqi_type == AR5K_TX_QUEUE_INACTIVE)
return 0;
if (ah->ah_version == AR5K_AR5210) {
/* Only handle data queues, others will be ignored */
if (tq->tqi_type != AR5K_TX_QUEUE_DATA)
return 0;
/* Set Slot time */
ath5k_hw_reg_write(ah, ah->ah_turbo ?
AR5K_INIT_SLOT_TIME_TURBO : AR5K_INIT_SLOT_TIME,
AR5K_SLOT_TIME);
/* Set ACK_CTS timeout */
ath5k_hw_reg_write(ah, ah->ah_turbo ?
AR5K_INIT_ACK_CTS_TIMEOUT_TURBO :
AR5K_INIT_ACK_CTS_TIMEOUT, AR5K_SLOT_TIME);
/* Set Transmit Latency */
ath5k_hw_reg_write(ah, ah->ah_turbo ?
AR5K_INIT_TRANSMIT_LATENCY_TURBO :
AR5K_INIT_TRANSMIT_LATENCY, AR5K_USEC_5210);
/* Set IFS0 */
if (ah->ah_turbo) {
ath5k_hw_reg_write(ah, ((AR5K_INIT_SIFS_TURBO +
(ah->ah_aifs + tq->tqi_aifs) *
AR5K_INIT_SLOT_TIME_TURBO) <<
AR5K_IFS0_DIFS_S) | AR5K_INIT_SIFS_TURBO,
AR5K_IFS0);
} else {
ath5k_hw_reg_write(ah, ((AR5K_INIT_SIFS +
(ah->ah_aifs + tq->tqi_aifs) *
AR5K_INIT_SLOT_TIME) << AR5K_IFS0_DIFS_S) |
AR5K_INIT_SIFS, AR5K_IFS0);
}
/* Set IFS1 */
ath5k_hw_reg_write(ah, ah->ah_turbo ?
AR5K_INIT_PROTO_TIME_CNTRL_TURBO :
AR5K_INIT_PROTO_TIME_CNTRL, AR5K_IFS1);
/* Set AR5K_PHY_SETTLING */
ath5k_hw_reg_write(ah, ah->ah_turbo ?
(ath5k_hw_reg_read(ah, AR5K_PHY_SETTLING) & ~0x7F)
| 0x38 :
(ath5k_hw_reg_read(ah, AR5K_PHY_SETTLING) & ~0x7F)
| 0x1C,
AR5K_PHY_SETTLING);
/* Set Frame Control Register */
ath5k_hw_reg_write(ah, ah->ah_turbo ?
(AR5K_PHY_FRAME_CTL_INI | AR5K_PHY_TURBO_MODE |
AR5K_PHY_TURBO_SHORT | 0x2020) :
(AR5K_PHY_FRAME_CTL_INI | 0x1020),
AR5K_PHY_FRAME_CTL_5210);
}
/*
* Calculate cwmin/max by channel mode
*/
cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN;
cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX;
ah->ah_aifs = AR5K_TUNE_AIFS;
/*XR is only supported on 5212*/
if (IS_CHAN_XR(ah->ah_current_channel) &&
ah->ah_version == AR5K_AR5212) {
cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN_XR;
cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX_XR;
ah->ah_aifs = AR5K_TUNE_AIFS_XR;
/*B mode is not supported on 5210*/
} else if (IS_CHAN_B(ah->ah_current_channel) &&
ah->ah_version != AR5K_AR5210) {
cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN_11B;
cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX_11B;
ah->ah_aifs = AR5K_TUNE_AIFS_11B;
}
cw_min = 1;
while (cw_min < ah->ah_cw_min)
cw_min = (cw_min << 1) | 1;
cw_min = tq->tqi_cw_min < 0 ? (cw_min >> (-tq->tqi_cw_min)) :
((cw_min << tq->tqi_cw_min) + (1 << tq->tqi_cw_min) - 1);
cw_max = tq->tqi_cw_max < 0 ? (cw_max >> (-tq->tqi_cw_max)) :
((cw_max << tq->tqi_cw_max) + (1 << tq->tqi_cw_max) - 1);
/*
* Calculate and set retry limits
*/
if (ah->ah_software_retry) {
/* XXX Need to test this */
retry_lg = ah->ah_limit_tx_retries;
retry_sh = retry_lg = retry_lg > AR5K_DCU_RETRY_LMT_SH_RETRY ?
AR5K_DCU_RETRY_LMT_SH_RETRY : retry_lg;
} else {
retry_lg = AR5K_INIT_LG_RETRY;
retry_sh = AR5K_INIT_SH_RETRY;
}
/*No QCU/DCU [5210]*/
if (ah->ah_version == AR5K_AR5210) {
ath5k_hw_reg_write(ah,
(cw_min << AR5K_NODCU_RETRY_LMT_CW_MIN_S)
| AR5K_REG_SM(AR5K_INIT_SLG_RETRY,
AR5K_NODCU_RETRY_LMT_SLG_RETRY)
| AR5K_REG_SM(AR5K_INIT_SSH_RETRY,
AR5K_NODCU_RETRY_LMT_SSH_RETRY)
| AR5K_REG_SM(retry_lg, AR5K_NODCU_RETRY_LMT_LG_RETRY)
| AR5K_REG_SM(retry_sh, AR5K_NODCU_RETRY_LMT_SH_RETRY),
AR5K_NODCU_RETRY_LMT);
} else {
/*QCU/DCU [5211+]*/
ath5k_hw_reg_write(ah,
AR5K_REG_SM(AR5K_INIT_SLG_RETRY,
AR5K_DCU_RETRY_LMT_SLG_RETRY) |
AR5K_REG_SM(AR5K_INIT_SSH_RETRY,
AR5K_DCU_RETRY_LMT_SSH_RETRY) |
AR5K_REG_SM(retry_lg, AR5K_DCU_RETRY_LMT_LG_RETRY) |
AR5K_REG_SM(retry_sh, AR5K_DCU_RETRY_LMT_SH_RETRY),
AR5K_QUEUE_DFS_RETRY_LIMIT(queue));
/*===Rest is also for QCU/DCU only [5211+]===*/
/*
* Set initial content window (cw_min/cw_max)
* and arbitrated interframe space (aifs)...
*/
ath5k_hw_reg_write(ah,
AR5K_REG_SM(cw_min, AR5K_DCU_LCL_IFS_CW_MIN) |
AR5K_REG_SM(cw_max, AR5K_DCU_LCL_IFS_CW_MAX) |
AR5K_REG_SM(ah->ah_aifs + tq->tqi_aifs,
AR5K_DCU_LCL_IFS_AIFS),
AR5K_QUEUE_DFS_LOCAL_IFS(queue));
/*
* Set misc registers
*/
/* Enable DCU early termination for this queue */
AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
AR5K_QCU_MISC_DCU_EARLY);
/* 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);
if (tq->tqi_cbr_period) {
ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_cbr_period,
AR5K_QCU_CBRCFG_INTVAL) |
AR5K_REG_SM(tq->tqi_cbr_overflow_limit,
AR5K_QCU_CBRCFG_ORN_THRES),
AR5K_QUEUE_CBRCFG(queue));
AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
AR5K_QCU_MISC_FRSHED_CBR);
if (tq->tqi_cbr_overflow_limit)
AR5K_REG_ENABLE_BITS(ah,
AR5K_QUEUE_MISC(queue),
AR5K_QCU_MISC_CBR_THRES_ENABLE);
}
if (tq->tqi_ready_time &&
(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);
}
if (tq->tqi_flags & AR5K_TXQ_FLAG_BACKOFF_DISABLE)
ath5k_hw_reg_write(ah, AR5K_DCU_MISC_POST_FR_BKOFF_DIS,
AR5K_QUEUE_DFS_MISC(queue));
if (tq->tqi_flags & AR5K_TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE)
ath5k_hw_reg_write(ah, AR5K_DCU_MISC_BACKOFF_FRAG,
AR5K_QUEUE_DFS_MISC(queue));
/*
* Set registers by queue type
*/
switch (tq->tqi_type) {
case AR5K_TX_QUEUE_BEACON:
AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
AR5K_QCU_MISC_FRSHED_DBA_GT |
AR5K_QCU_MISC_CBREXP_BCN_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;
}
/*
* Get slot time from DCU
*/
unsigned int ath5k_hw_get_slot_time(struct ath5k_hw *ah)
{
ATH5K_TRACE(ah->ah_sc);
if (ah->ah_version == AR5K_AR5210)
return ath5k_hw_clocktoh(ath5k_hw_reg_read(ah,
AR5K_SLOT_TIME) & 0xffff, ah->ah_turbo);
else
return ath5k_hw_reg_read(ah, AR5K_DCU_GBL_IFS_SLOT) & 0xffff;
}
/*
* Set slot time on DCU
*/
int ath5k_hw_set_slot_time(struct ath5k_hw *ah, unsigned int slot_time)
{
ATH5K_TRACE(ah->ah_sc);
if (slot_time < AR5K_SLOT_TIME_9 || slot_time > AR5K_SLOT_TIME_MAX)
return -EINVAL;
if (ah->ah_version == AR5K_AR5210)
ath5k_hw_reg_write(ah, ath5k_hw_htoclock(slot_time,
ah->ah_turbo), AR5K_SLOT_TIME);
else
ath5k_hw_reg_write(ah, slot_time, AR5K_DCU_GBL_IFS_SLOT);
return 0;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,516 @@
/*
* 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.
*
*/
/*
* 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; /* RF Gain register address */
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 {
s8 gos_param[AR5K_GAIN_CRN_MAX_FIX_BITS];
s8 gos_gain;
};
struct ath5k_gain_opt {
u8 go_default;
u8 go_steps_count;
const struct ath5k_gain_opt_step go_step[AR5K_GAIN_STEP_COUNT];
};
/*
* 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 }
}
};
/*
* 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 },
}
};

View File

@@ -0,0 +1,121 @@
/*
* 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 "base.h"
static inline void ath5k_rfkill_disable(struct ath5k_softc *sc)
{
ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "rfkill disable (gpio:%d polarity:%d)\n",
sc->rf_kill.gpio, sc->rf_kill.polarity);
ath5k_hw_set_gpio_output(sc->ah, sc->rf_kill.gpio);
ath5k_hw_set_gpio(sc->ah, sc->rf_kill.gpio, !sc->rf_kill.polarity);
}
static inline void ath5k_rfkill_enable(struct ath5k_softc *sc)
{
ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "rfkill enable (gpio:%d polarity:%d)\n",
sc->rf_kill.gpio, sc->rf_kill.polarity);
ath5k_hw_set_gpio_output(sc->ah, sc->rf_kill.gpio);
ath5k_hw_set_gpio(sc->ah, sc->rf_kill.gpio, sc->rf_kill.polarity);
}
static inline void ath5k_rfkill_set_intr(struct ath5k_softc *sc, bool enable)
{
struct ath5k_hw *ah = sc->ah;
u32 curval;
ath5k_hw_set_gpio_input(ah, sc->rf_kill.gpio);
curval = ath5k_hw_get_gpio(ah, sc->rf_kill.gpio);
ath5k_hw_set_gpio_intr(ah, sc->rf_kill.gpio, enable ?
!!curval : !curval);
}
static bool
ath5k_is_rfkill_set(struct ath5k_softc *sc)
{
/* configuring GPIO for input for some reason disables rfkill */
/*ath5k_hw_set_gpio_input(sc->ah, sc->rf_kill.gpio);*/
return ath5k_hw_get_gpio(sc->ah, sc->rf_kill.gpio) ==
sc->rf_kill.polarity;
}
static void
ath5k_tasklet_rfkill_toggle(unsigned long data)
{
struct ath5k_softc *sc = (void *)data;
bool blocked;
blocked = ath5k_is_rfkill_set(sc);
wiphy_rfkill_set_hw_state(sc->hw->wiphy, blocked);
}
void
ath5k_rfkill_hw_start(struct ath5k_hw *ah)
{
struct ath5k_softc *sc = ah->ah_sc;
/* read rfkill GPIO configuration from EEPROM header */
sc->rf_kill.gpio = ah->ah_capabilities.cap_eeprom.ee_rfkill_pin;
sc->rf_kill.polarity = ah->ah_capabilities.cap_eeprom.ee_rfkill_pol;
tasklet_init(&sc->rf_kill.toggleq, ath5k_tasklet_rfkill_toggle,
(unsigned long)sc);
ath5k_rfkill_disable(sc);
/* enable interrupt for rfkill switch */
if (AR5K_EEPROM_HDR_RFKILL(ah->ah_capabilities.cap_eeprom.ee_header))
ath5k_rfkill_set_intr(sc, true);
}
void
ath5k_rfkill_hw_stop(struct ath5k_hw *ah)
{
struct ath5k_softc *sc = ah->ah_sc;
/* disable interrupt for rfkill switch */
if (AR5K_EEPROM_HDR_RFKILL(ah->ah_capabilities.cap_eeprom.ee_header))
ath5k_rfkill_set_intr(sc, false);
tasklet_kill(&sc->rf_kill.toggleq);
/* enable RFKILL when stopping HW so Wifi LED is turned off */
ath5k_rfkill_enable(sc);
}