diff options
| author | Jiri Slaby <jirislaby@gmail.com> | 2007-08-12 17:33:16 +0200 | 
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2008-01-28 15:09:35 -0800 | 
| commit | fa1c114fdaa605496045e56c42d0c8aa4c139e57 (patch) | |
| tree | df8345d8ef17cea23da3c0bbe388729b79920bfe | |
| parent | 3543f8069d3cc932202e64095d1d3986a10d34ed (diff) | |
| download | linux-fa1c114fdaa605496045e56c42d0c8aa4c139e57.tar.bz2 | |
[PATCH] Net: add ath5k wireless driver
add ath5k wireless driver
Portions of this driver are covered by one or both of the ISC and
3-clause BSD licenses.  Specific license information is cited at the top
of each file.
Acked-by and Signed-off-by information is collected from individual
patches as collected in the wireless-2.6 tree prior to upstream
submission.
Acked-by: Matthew W. S. Bell  <mentor@madwifi.org>
Acked-by: Michael Taylor <mike.taylor@apprion.com>
Acked-by: Pavel Roskin <proski@gnu.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Bradley M. Kuhn <bkuhn@softwarefreedom.org>
Signed-off-by: Bruno Randolf <bruno@thinktube.com>
Signed-off-by: Dave Young <hidave.darkstar@gmail.com>
Signed-off-by: Francesco Gringoli <francesco.gringoli@ing.unibs.it>
Signed-off-by: Jiri Slaby <jirislaby@gmail.com>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: Karen Sandler <karen@softwarefreedom.org>
Signed-off-by: Krzysztof Halasa <khc@pm.waw.pl>
Signed-off-by: Luis R. Rodriguez <mcgrof@gmail.com>
Signed-off-by: Matt Norwood <norwood@softwarefreedom.org>
Signed-off-by: Nick Kossifidis <mickflemm@gmail.com>
Signed-off-by: Richard Fontana <fontana@softwarefreedom.org>
Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
Signed-off-by: Ulrich Meis <meis@nets.rwth-aachen.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
| -rw-r--r-- | MAINTAINERS | 11 | ||||
| -rw-r--r-- | drivers/net/wireless/Kconfig | 17 | ||||
| -rw-r--r-- | drivers/net/wireless/Makefile | 2 | ||||
| -rw-r--r-- | drivers/net/wireless/ath5k/Makefile | 2 | ||||
| -rw-r--r-- | drivers/net/wireless/ath5k/ath5k.h | 1173 | ||||
| -rw-r--r-- | drivers/net/wireless/ath5k/base.c | 2817 | ||||
| -rw-r--r-- | drivers/net/wireless/ath5k/base.h | 178 | ||||
| -rw-r--r-- | drivers/net/wireless/ath5k/debug.c | 469 | ||||
| -rw-r--r-- | drivers/net/wireless/ath5k/debug.h | 216 | ||||
| -rw-r--r-- | drivers/net/wireless/ath5k/hw.c | 4349 | ||||
| -rw-r--r-- | drivers/net/wireless/ath5k/hw.h | 588 | ||||
| -rw-r--r-- | drivers/net/wireless/ath5k/initvals.c | 1347 | ||||
| -rw-r--r-- | drivers/net/wireless/ath5k/phy.c | 2071 | ||||
| -rw-r--r-- | drivers/net/wireless/ath5k/reg.h | 1987 | ||||
| -rw-r--r-- | drivers/net/wireless/ath5k/regdom.c | 121 | ||||
| -rw-r--r-- | drivers/net/wireless/ath5k/regdom.h | 500 | 
16 files changed, 15848 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index a19469318ded..ba05e8058689 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -646,6 +646,17 @@ M:	ecashin@coraid.com  W:	http://www.coraid.com/support/linux  S:	Supported +ATHEROS ATH5K WIRELESS DRIVER +P:	Jiri Slaby +M:	jirislaby@gmail.com +P:	Nick Kossifidis +M:	mickflemm@gmail.com +P:	Luis R. Rodriguez +M:	mcgrof@gmail.com +L:	linux-wireless@vger.kernel.org +L:	ath5k-devel@lists.ath5k.org +S:	Maintained +  ATL1 ETHERNET DRIVER  P:	Jay Cliburn  M:	jcliburn@gmail.com diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 2c08c0a5a0df..50a8b6c2fa00 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -648,6 +648,23 @@ config P54_PCI  	  If you choose to build a module, it'll be called p54pci. +config ATH5K +	tristate "Atheros 5xxx wireless cards support" +	depends on PCI && MAC80211 && WLAN_80211 && EXPERIMENTAL +	---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. +  source "drivers/net/wireless/iwlwifi/Kconfig"  source "drivers/net/wireless/hostap/Kconfig"  source "drivers/net/wireless/bcm43xx/Kconfig" diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index d48f7d19745b..7e1535ece17a 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -59,3 +59,5 @@ obj-$(CONFIG_RT2X00)	+= rt2x00/  obj-$(CONFIG_P54_COMMON)	+= p54common.o  obj-$(CONFIG_P54_USB)		+= p54usb.o  obj-$(CONFIG_P54_PCI)		+= p54pci.o + +obj-$(CONFIG_ATH5K)	+= ath5k/ diff --git a/drivers/net/wireless/ath5k/Makefile b/drivers/net/wireless/ath5k/Makefile new file mode 100644 index 000000000000..321641f99e13 --- /dev/null +++ b/drivers/net/wireless/ath5k/Makefile @@ -0,0 +1,2 @@ +ath5k-objs		= base.o hw.o regdom.o initvals.o phy.o debug.o +obj-$(CONFIG_ATH5K)	+= ath5k.o diff --git a/drivers/net/wireless/ath5k/ath5k.h b/drivers/net/wireless/ath5k/ath5k.h new file mode 100644 index 000000000000..878609f1bf39 --- /dev/null +++ b/drivers/net/wireless/ath5k/ath5k.h @@ -0,0 +1,1173 @@ +/* + * Copyright (c) 2004-2007 Reyk Floeter <reyk@openbsd.org> + * Copyright (c) 2006-2007 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. + */ + +#ifndef _ATH5K_H +#define _ATH5K_H + +/* Set this to 1 to disable regulatory domain restrictions for channel tests. + * WARNING: This is for debuging only and has side effects (eg. scan takes too + * long and results timeouts). It's also illegal to tune to some of the + * supported frequencies in some countries, so use this at your own risk, + * you've been warned. */ +#define CHAN_DEBUG	0 + +#include <linux/io.h> +#include <linux/types.h> +#include <net/mac80211.h> + +#include "hw.h" +#include "regdom.h" + +/* PCI IDs */ +#define PCI_DEVICE_ID_ATHEROS_AR5210 		0x0007 /* AR5210 */ +#define PCI_DEVICE_ID_ATHEROS_AR5311 		0x0011 /* AR5311 */ +#define PCI_DEVICE_ID_ATHEROS_AR5211 		0x0012 /* AR5211 */ +#define PCI_DEVICE_ID_ATHEROS_AR5212 		0x0013 /* AR5212 */ +#define PCI_DEVICE_ID_3COM_3CRDAG675 		0x0013 /* 3CRDAG675 (Atheros AR5212) */ +#define PCI_DEVICE_ID_3COM_2_3CRPAG175 		0x0013 /* 3CRPAG175 (Atheros AR5212) */ +#define PCI_DEVICE_ID_ATHEROS_AR5210_AP 	0x0207 /* AR5210 (Early) */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_IBM	0x1014 /* AR5212 (IBM MiniPCI) */ +#define PCI_DEVICE_ID_ATHEROS_AR5210_DEFAULT 	0x1107 /* AR5210 (no eeprom) */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_DEFAULT 	0x1113 /* AR5212 (no eeprom) */ +#define PCI_DEVICE_ID_ATHEROS_AR5211_DEFAULT 	0x1112 /* AR5211 (no eeprom) */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_FPGA 	0xf013 /* AR5212 (emulation board) */ +#define PCI_DEVICE_ID_ATHEROS_AR5211_LEGACY 	0xff12 /* AR5211 (emulation board) */ +#define PCI_DEVICE_ID_ATHEROS_AR5211_FPGA11B 	0xf11b /* AR5211 (emulation board) */ +#define PCI_DEVICE_ID_ATHEROS_AR5312_REV2 	0x0052 /* AR5312 WMAC (AP31) */ +#define PCI_DEVICE_ID_ATHEROS_AR5312_REV7 	0x0057 /* AR5312 WMAC (AP30-040) */ +#define PCI_DEVICE_ID_ATHEROS_AR5312_REV8 	0x0058 /* AR5312 WMAC (AP43-030) */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_0014 	0x0014 /* AR5212 compatible */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_0015 	0x0015 /* AR5212 compatible */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_0016 	0x0016 /* AR5212 compatible */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_0017 	0x0017 /* AR5212 compatible */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_0018 	0x0018 /* AR5212 compatible */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_0019 	0x0019 /* AR5212 compatible */ +#define PCI_DEVICE_ID_ATHEROS_AR2413 		0x001a /* AR2413 (Griffin-lite) */ +#define PCI_DEVICE_ID_ATHEROS_AR5413 		0x001b /* AR5413 (Eagle) */ +#define PCI_DEVICE_ID_ATHEROS_AR5424 		0x001c /* AR5424 (Condor PCI-E) */ +#define PCI_DEVICE_ID_ATHEROS_AR5416 		0x0023 /* AR5416 */ +#define PCI_DEVICE_ID_ATHEROS_AR5418 		0x0024 /* AR5418 */ + +/****************************\ +  GENERIC DRIVER DEFINITIONS +\****************************/ + +#define ATH5K_PRINTF(fmt, ...)   printk("%s: " fmt, __func__, ##__VA_ARGS__) + +#define ATH5K_PRINTK(_sc, _level, _fmt, ...) \ +	printk(_level "ath5k %s: " _fmt, \ +		((_sc) && (_sc)->hw) ? wiphy_name((_sc)->hw->wiphy) : "", \ +		##__VA_ARGS__) + +#define ATH5K_PRINTK_LIMIT(_sc, _level, _fmt, ...) do { \ +	if (net_ratelimit()) \ +		ATH5K_PRINTK(_sc, _level, _fmt, ##__VA_ARGS__); \ +	} while (0) + +#define ATH5K_INFO(_sc, _fmt, ...) \ +	ATH5K_PRINTK(_sc, KERN_INFO, _fmt, ##__VA_ARGS__) + +#define ATH5K_WARN(_sc, _fmt, ...) \ +	ATH5K_PRINTK_LIMIT(_sc, KERN_WARNING, _fmt, ##__VA_ARGS__) + +#define ATH5K_ERR(_sc, _fmt, ...) \ +	ATH5K_PRINTK_LIMIT(_sc, KERN_ERR, _fmt, ##__VA_ARGS__) + +/* + * Some tuneable values (these should be changeable by the user) + */ +#define AR5K_TUNE_DMA_BEACON_RESP		2 +#define AR5K_TUNE_SW_BEACON_RESP		10 +#define AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF	0 +#define AR5K_TUNE_RADAR_ALERT			false +#define AR5K_TUNE_MIN_TX_FIFO_THRES		1 +#define AR5K_TUNE_MAX_TX_FIFO_THRES		((IEEE80211_MAX_LEN / 64) + 1) +#define AR5K_TUNE_REGISTER_TIMEOUT		20000 +/* Register for RSSI threshold has a mask of 0xff, so 255 seems to + * be the max value. */ +#define AR5K_TUNE_RSSI_THRES                   129 +/* This must be set when setting the RSSI threshold otherwise it can + * prevent a reset. If AR5K_RSSI_THR is read after writing to it + * the BMISS_THRES will be seen as 0, seems harware doesn't keep + * track of it. Max value depends on harware. For AR5210 this is just 7. + * For AR5211+ this seems to be up to 255. */ +#define AR5K_TUNE_BMISS_THRES                  7 +#define AR5K_TUNE_REGISTER_DWELL_TIME		20000 +#define AR5K_TUNE_BEACON_INTERVAL		100 +#define AR5K_TUNE_AIFS				2 +#define AR5K_TUNE_AIFS_11B			2 +#define AR5K_TUNE_AIFS_XR			0 +#define AR5K_TUNE_CWMIN				15 +#define AR5K_TUNE_CWMIN_11B			31 +#define AR5K_TUNE_CWMIN_XR			3 +#define AR5K_TUNE_CWMAX				1023 +#define AR5K_TUNE_CWMAX_11B			1023 +#define AR5K_TUNE_CWMAX_XR			7 +#define AR5K_TUNE_NOISE_FLOOR			-72 +#define AR5K_TUNE_MAX_TXPOWER			60 +#define AR5K_TUNE_DEFAULT_TXPOWER		30 +#define AR5K_TUNE_TPC_TXPOWER			true +#define AR5K_TUNE_ANT_DIVERSITY			true +#define AR5K_TUNE_HWTXTRIES			4 + +/* token to use for aifs, cwmin, cwmax in MadWiFi */ +#define	AR5K_TXQ_USEDEFAULT	((u32) -1) + +/* GENERIC CHIPSET DEFINITIONS */ + +/* MAC Chips */ +enum ath5k_version { +	AR5K_AR5210	= 0, +	AR5K_AR5211	= 1, +	AR5K_AR5212	= 2, +}; + +/* PHY Chips */ +enum ath5k_radio { +	AR5K_RF5110	= 0, +	AR5K_RF5111	= 1, +	AR5K_RF5112	= 2, +	AR5K_RF5413	= 3, +}; + +/* + * Common silicon revision/version values + */ + +enum ath5k_srev_type { +	AR5K_VERSION_VER, +	AR5K_VERSION_RAD, +}; + +struct ath5k_srev_name { +	const char		*sr_name; +	enum ath5k_srev_type	sr_type; +	u_int			sr_val; +}; + +#define AR5K_SREV_UNKNOWN	0xffff + +#define AR5K_SREV_VER_AR5210	0x00 +#define AR5K_SREV_VER_AR5311	0x10 +#define AR5K_SREV_VER_AR5311A	0x20 +#define AR5K_SREV_VER_AR5311B	0x30 +#define AR5K_SREV_VER_AR5211	0x40 +#define AR5K_SREV_VER_AR5212	0x50 +#define AR5K_SREV_VER_AR5213	0x55 +#define AR5K_SREV_VER_AR5213A	0x59 +#define AR5K_SREV_VER_AR2424	0xa0 +#define AR5K_SREV_VER_AR5424	0xa3 +#define AR5K_SREV_VER_AR5413	0xa4 +#define AR5K_SREV_VER_AR5414	0xa5 +#define AR5K_SREV_VER_AR5416	0xc0	/* ? */ +#define AR5K_SREV_VER_AR5418	0xca + +#define AR5K_SREV_RAD_5110	0x00 +#define AR5K_SREV_RAD_5111	0x10 +#define AR5K_SREV_RAD_5111A	0x15 +#define AR5K_SREV_RAD_2111	0x20 +#define AR5K_SREV_RAD_5112	0x30 +#define AR5K_SREV_RAD_5112A	0x35 +#define AR5K_SREV_RAD_2112	0x40 +#define AR5K_SREV_RAD_2112A	0x45 +#define AR5K_SREV_RAD_SC1	0x63	/* Found on 5413/5414 */ +#define AR5K_SREV_RAD_SC2	0xa2	/* Found on 2424/5424 */ +#define AR5K_SREV_RAD_5133	0xc0	/* MIMO found on 5418 */ + +/* IEEE defs */ + +#define IEEE80211_MAX_LEN       2500 + +/* TODO add support to mac80211 for vendor-specific rates and modes */ + +/* + * Some of this information is based on Documentation from: + * + * http://madwifi.org/wiki/ChipsetFeatures/SuperAG + * + * Modulation for Atheros' eXtended Range - range enhancing extension that is + * supposed to double the distance an Atheros client device can keep a + * connection with an Atheros access point. This is achieved by increasing + * the receiver sensitivity up to, -105dBm, which is about 20dB above what + * the 802.11 specifications demand. In addition, new (proprietary) data rates + * are introduced: 3, 2, 1, 0.5 and 0.25 MBit/s. + * + * Please note that can you either use XR or TURBO but you cannot use both, + * they are exclusive. + * + */ +#define MODULATION_XR 		0x00000200 +/* + * Modulation for Atheros' Turbo G and Turbo A, its supposed to provide a + * throughput transmission speed up to 40Mbit/s-60Mbit/s at a 108Mbit/s + * signaling rate achieved through the bonding of two 54Mbit/s 802.11g + * channels. To use this feature your Access Point must also suport it. + * There is also a distinction between "static" and "dynamic" turbo modes: + * + * - Static: is the dumb version: devices set to this mode stick to it until + *     the mode is turned off. + * - Dynamic: is the intelligent version, the network decides itself if it + *     is ok to use turbo. As soon as traffic is detected on adjacent channels + *     (which would get used in turbo mode), or when a non-turbo station joins + *     the network, turbo mode won't be used until the situation changes again. + *     Dynamic mode is achieved by Atheros' Adaptive Radio (AR) feature which + *     monitors the used radio band in order to decide whether turbo mode may + *     be used or not. + * + * This article claims Super G sticks to bonding of channels 5 and 6 for + * USA: + * + * http://www.pcworld.com/article/id,113428-page,1/article.html + * + * The channel bonding seems to be driver specific though. In addition to + * deciding what channels will be used, these "Turbo" modes are accomplished + * by also enabling the following features: + * + * - Bursting: allows multiple frames to be sent at once, rather than pausing + *     after each frame. Bursting is a standards-compliant feature that can be + *     used with any Access Point. + * - Fast frames: increases the amount of information that can be sent per + *     frame, also resulting in a reduction of transmission overhead. It is a + *     proprietary feature that needs to be supported by the Access Point. + * - Compression: data frames are compressed in real time using a Lempel Ziv + *     algorithm. This is done transparently. Once this feature is enabled, + *     compression and decompression takes place inside the chipset, without + *     putting additional load on the host CPU. + * + */ +#define MODULATION_TURBO	0x00000080 + +enum ath5k_vendor_mode { +	MODE_ATHEROS_TURBO = NUM_IEEE80211_MODES+1, +	MODE_ATHEROS_TURBOG +}; + +/* Number of supported mac80211 enum ieee80211_phymode modes by this driver */ +#define NUM_DRIVER_MODES	3 + +/* adding this flag to rate_code enables short preamble, see ar5212_reg.h */ +#define AR5K_SET_SHORT_PREAMBLE 0x04 + +#define HAS_SHPREAMBLE(_ix) (rt->rates[_ix].modulation == IEEE80211_RATE_CCK_2) +#define SHPREAMBLE_FLAG(_ix) (HAS_SHPREAMBLE(_ix) ? AR5K_SET_SHORT_PREAMBLE : 0) + +/****************\ +  TX DEFINITIONS +\****************/ + +/* + * Tx Descriptor + */ +struct ath5k_tx_status { +	u16	ts_seqnum; +	u16	ts_tstamp; +	u8	ts_status; +	u8	ts_rate; +	s8	ts_rssi; +	u8	ts_shortretry; +	u8	ts_longretry; +	u8	ts_virtcol; +	u8	ts_antenna; +}; + +#define AR5K_TXSTAT_ALTRATE	0x80 +#define AR5K_TXERR_XRETRY	0x01 +#define AR5K_TXERR_FILT		0x02 +#define AR5K_TXERR_FIFO		0x04 + +/** + * enum ath5k_tx_queue - Queue types used to classify tx queues. + * @AR5K_TX_QUEUE_INACTIVE: q is unused -- see ath5k_hw_release_tx_queue + * @AR5K_TX_QUEUE_DATA: A normal data queue + * @AR5K_TX_QUEUE_XR_DATA: An XR-data queue + * @AR5K_TX_QUEUE_BEACON: The beacon queue + * @AR5K_TX_QUEUE_CAB: The after-beacon queue + * @AR5K_TX_QUEUE_UAPSD: Unscheduled Automatic Power Save Delivery queue + */ +enum ath5k_tx_queue { +	AR5K_TX_QUEUE_INACTIVE = 0, +	AR5K_TX_QUEUE_DATA, +	AR5K_TX_QUEUE_XR_DATA, +	AR5K_TX_QUEUE_BEACON, +	AR5K_TX_QUEUE_CAB, +	AR5K_TX_QUEUE_UAPSD, +}; + +#define	AR5K_NUM_TX_QUEUES		10 +#define	AR5K_NUM_TX_QUEUES_NOQCU	2 + +/* + * Queue syb-types to classify normal data queues. + * These are the 4 Access Categories as defined in + * WME spec. 0 is the lowest priority and 4 is the + * highest. Normal data that hasn't been classified + * goes to the Best Effort AC. + */ +enum ath5k_tx_queue_subtype { +	AR5K_WME_AC_BK = 0,	/*Background traffic*/ +	AR5K_WME_AC_BE, 	/*Best-effort (normal) traffic)*/ +	AR5K_WME_AC_VI, 	/*Video traffic*/ +	AR5K_WME_AC_VO, 	/*Voice traffic*/ +}; + +/* + * Queue ID numbers as returned by the hw functions, each number + * represents a hw queue. If hw does not support hw queues + * (eg 5210) all data goes in one queue. These match + * d80211 definitions (net80211/MadWiFi don't use them). + */ +enum ath5k_tx_queue_id { +	AR5K_TX_QUEUE_ID_NOQCU_DATA	= 0, +	AR5K_TX_QUEUE_ID_NOQCU_BEACON	= 1, +	AR5K_TX_QUEUE_ID_DATA_MIN	= 0, /*IEEE80211_TX_QUEUE_DATA0*/ +	AR5K_TX_QUEUE_ID_DATA_MAX	= 4, /*IEEE80211_TX_QUEUE_DATA4*/ +	AR5K_TX_QUEUE_ID_DATA_SVP	= 5, /*IEEE80211_TX_QUEUE_SVP - Spectralink Voice Protocol*/ +	AR5K_TX_QUEUE_ID_CAB		= 6, /*IEEE80211_TX_QUEUE_AFTER_BEACON*/ +	AR5K_TX_QUEUE_ID_BEACON		= 7, /*IEEE80211_TX_QUEUE_BEACON*/ +	AR5K_TX_QUEUE_ID_UAPSD		= 8, +	AR5K_TX_QUEUE_ID_XR_DATA	= 9, +}; + + +/* + * Flags to set hw queue's parameters... + */ +#define AR5K_TXQ_FLAG_TXOKINT_ENABLE		0x0001	/* Enable TXOK interrupt */ +#define AR5K_TXQ_FLAG_TXERRINT_ENABLE		0x0002	/* Enable TXERR interrupt */ +#define AR5K_TXQ_FLAG_TXEOLINT_ENABLE		0x0004	/* Enable TXEOL interrupt -not used- */ +#define AR5K_TXQ_FLAG_TXDESCINT_ENABLE		0x0008	/* Enable TXDESC interrupt -not used- */ +#define AR5K_TXQ_FLAG_TXURNINT_ENABLE		0x0010	/* Enable TXURN interrupt */ +#define AR5K_TXQ_FLAG_BACKOFF_DISABLE		0x0020	/* Disable random post-backoff */ +#define AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE	0x0040	/* Enable ready time expiry policy (?)*/ +#define AR5K_TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE	0x0080	/* Enable backoff while bursting */ +#define AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS		0x0100	/* Disable backoff while bursting */ +#define AR5K_TXQ_FLAG_COMPRESSION_ENABLE	0x0200	/* Enable hw compression -not implemented-*/ + +/* + * A struct to hold tx queue's parameters + */ +struct ath5k_txq_info { +	enum ath5k_tx_queue tqi_type; +	enum ath5k_tx_queue_subtype tqi_subtype; +	u16	tqi_flags;	/* Tx queue flags (see above) */ +	u32	tqi_aifs;	/* Arbitrated Interframe Space */ +	s32	tqi_cw_min;	/* Minimum Contention Window */ +	s32	tqi_cw_max;	/* Maximum Contention Window */ +	u32	tqi_cbr_period; /* Constant bit rate period */ +	u32	tqi_cbr_overflow_limit; +	u32	tqi_burst_time; +	u32	tqi_ready_time; /* Not used */ +}; + +/* + * Transmit packet types. + * These are not fully used inside OpenHAL yet + */ +enum ath5k_pkt_type { +	AR5K_PKT_TYPE_NORMAL		= 0, +	AR5K_PKT_TYPE_ATIM		= 1, +	AR5K_PKT_TYPE_PSPOLL		= 2, +	AR5K_PKT_TYPE_BEACON		= 3, +	AR5K_PKT_TYPE_PROBE_RESP	= 4, +	AR5K_PKT_TYPE_PIFS		= 5, +}; + +/* + * TX power and TPC settings + */ +#define AR5K_TXPOWER_OFDM(_r, _v)	(			\ +	((0 & 1) << ((_v) + 6)) |				\ +	(((ah->ah_txpower.txp_rates[(_r)]) & 0x3f) << (_v))	\ +) + +#define AR5K_TXPOWER_CCK(_r, _v)	(			\ +	(ah->ah_txpower.txp_rates[(_r)] & 0x3f) << (_v)	\ +) + +/* + * DMA size definitions (2^n+2) + */ +enum ath5k_dmasize { +	AR5K_DMASIZE_4B	= 0, +	AR5K_DMASIZE_8B, +	AR5K_DMASIZE_16B, +	AR5K_DMASIZE_32B, +	AR5K_DMASIZE_64B, +	AR5K_DMASIZE_128B, +	AR5K_DMASIZE_256B, +	AR5K_DMASIZE_512B +}; + + +/****************\ +  RX DEFINITIONS +\****************/ + +/* + * Rx Descriptor + */ +struct ath5k_rx_status { +	u16	rs_datalen; +	u16	rs_tstamp; +	u8	rs_status; +	u8	rs_phyerr; +	s8	rs_rssi; +	u8	rs_keyix; +	u8	rs_rate; +	u8	rs_antenna; +	u8	rs_more; +}; + +#define AR5K_RXERR_CRC		0x01 +#define AR5K_RXERR_PHY		0x02 +#define AR5K_RXERR_FIFO		0x04 +#define AR5K_RXERR_DECRYPT	0x08 +#define AR5K_RXERR_MIC		0x10 +#define AR5K_RXKEYIX_INVALID	((u8) - 1) +#define AR5K_TXKEYIX_INVALID	((u32) - 1) + +struct ath5k_mib_stats { +	u32	ackrcv_bad; +	u32	rts_bad; +	u32	rts_good; +	u32	fcs_bad; +	u32	beacons; +}; + + + + +/**************************\ + BEACON TIMERS DEFINITIONS +\**************************/ + +#define AR5K_BEACON_PERIOD	0x0000ffff +#define AR5K_BEACON_ENA		0x00800000 /*enable beacon xmit*/ +#define AR5K_BEACON_RESET_TSF	0x01000000 /*force a TSF reset*/ + +#if 0 +/** + * struct ath5k_beacon_state - Per-station beacon timer state. + * @bs_interval: in TU's, can also include the above flags + * @bs_cfp_max_duration: if non-zero hw is setup to coexist with a + * 	Point Coordination Function capable AP + */ +struct ath5k_beacon_state { +	u32	bs_next_beacon; +	u32	bs_next_dtim; +	u32	bs_interval; +	u8	bs_dtim_period; +	u8	bs_cfp_period; +	u16	bs_cfp_max_duration; +	u16	bs_cfp_du_remain; +	u16	bs_tim_offset; +	u16	bs_sleep_duration; +	u16	bs_bmiss_threshold; +	u32  	bs_cfp_next; +}; +#endif + + +/* + * TSF to TU conversion: + * + * TSF is a 64bit value in usec (microseconds). + * TU is a 32bit value in roughly msec (milliseconds): usec / 1024 + * (1000ms equals 976 TU) + */ +#define TSF_TO_TU(_tsf) (u32)((_tsf) >> 10) + + + +/********************\ +  COMMON DEFINITIONS +\********************/ + +/* + * Atheros descriptor + */ +struct ath5k_desc { +	u32	ds_link; +	u32	ds_data; +	u32	ds_ctl0; +	u32	ds_ctl1; +	u32	ds_hw[4]; + +	union { +		struct ath5k_rx_status rx; +		struct ath5k_tx_status tx; +	} ds_us; + +#define ds_rxstat ds_us.rx +#define ds_txstat ds_us.tx + +} __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+]*/ + +#define AR5K_SLOT_TIME_9	396 +#define AR5K_SLOT_TIME_20	880 +#define AR5K_SLOT_TIME_MAX	0xffff + +/* channel_flags */ +#define	CHANNEL_CW_INT	0x0008	/* Contention Window interference detected */ +#define	CHANNEL_TURBO	0x0010	/* Turbo Channel */ +#define	CHANNEL_CCK	0x0020	/* CCK channel */ +#define	CHANNEL_OFDM	0x0040	/* OFDM channel */ +#define	CHANNEL_2GHZ	0x0080	/* 2GHz channel. */ +#define	CHANNEL_5GHZ	0x0100	/* 5GHz channel */ +#define	CHANNEL_PASSIVE	0x0200	/* Only passive scan allowed */ +#define	CHANNEL_DYN	0x0400	/* Dynamic CCK-OFDM channel (for g operation) */ +#define	CHANNEL_XR	0x0800	/* XR channel */ + +#define	CHANNEL_A	(CHANNEL_5GHZ|CHANNEL_OFDM) +#define	CHANNEL_B	(CHANNEL_2GHZ|CHANNEL_CCK) +#define	CHANNEL_G	(CHANNEL_2GHZ|CHANNEL_OFDM) +#define	CHANNEL_T	(CHANNEL_5GHZ|CHANNEL_OFDM|CHANNEL_TURBO) +#define	CHANNEL_TG	(CHANNEL_2GHZ|CHANNEL_OFDM|CHANNEL_TURBO) +#define	CHANNEL_108A	CHANNEL_T +#define	CHANNEL_108G	CHANNEL_TG +#define	CHANNEL_X	(CHANNEL_5GHZ|CHANNEL_OFDM|CHANNEL_XR) + +#define	CHANNEL_ALL 	(CHANNEL_OFDM|CHANNEL_CCK|CHANNEL_2GHZ|CHANNEL_5GHZ| \ +		CHANNEL_TURBO) + +#define	CHANNEL_ALL_NOTURBO 	(CHANNEL_ALL & ~CHANNEL_TURBO) +#define CHANNEL_MODES		CHANNEL_ALL + +/* + * Used internaly in OpenHAL (ar5211.c/ar5212.c + * for reset_tx_queue). Also see struct struct ieee80211_channel. + */ +#define IS_CHAN_XR(_c)	((_c.val & CHANNEL_XR) != 0) +#define IS_CHAN_B(_c)	((_c.val & CHANNEL_B) != 0) + +/* + * The following structure will be used to map 2GHz channels to + * 5GHz Atheros channels. + */ +struct ath5k_athchan_2ghz { +	u32	a2_flags; +	u16	a2_athchan; +}; + +/* + * Rate definitions + * TODO: Clean them up or move them on mac80211 -most of these infos are + * 	 used by the rate control algorytm on MadWiFi. + */ + +/* Max number of rates on the rate table and what it seems + * Atheros hardware supports */ +#define AR5K_MAX_RATES 32 + +/** + * struct ath5k_rate - rate structure + * @valid: is this a valid rate for the current mode + * @modulation: respective mac80211 modulation + * @rate_kbps: rate in kbit/s + * @rate_code: hardware rate value, used in &struct ath5k_desc, on RX on + *     &struct ath5k_rx_status.rs_rate and on TX on + *     &struct ath5k_tx_status.ts_rate. Seems the ar5xxx harware supports + *     up to 32 rates, indexed by 1-32. This means we really only need + *     6 bits for the rate_code. + * @dot11_rate: respective IEEE-802.11 rate value + * @control_rate: index of rate assumed to be used to send control frames. + *     This can be used to set override the value on the rate duration + *     registers. This is only useful if we can override in the harware at + *     what rate we want to send control frames at. Note that IEEE-802.11 + *     Ch. 9.6 (after IEEE 802.11g changes) defines the rate at which we + *     should send ACK/CTS, if we change this value we can be breaking + *     the spec. + * + * This structure is used to get the RX rate or set the TX rate on the + * hardware descriptors. It is also used for internal modulation control + * and settings. + * + * On RX after the &struct ath5k_desc is parsed by the appropriate + * ah_proc_rx_desc() the respective hardware rate value is set in + * &struct ath5k_rx_status.rs_rate. On TX the desired rate is set in + * &struct ath5k_tx_status.ts_rate which is later used to setup the + * &struct ath5k_desc correctly. This is the hardware rate map we are + * aware of: + * + * rate_code   1       2       3       4       5       6       7       8 + * rate_kbps   3000    1000    ?       ?       ?       2000    500     48000 + * + * rate_code   9       10      11      12      13      14      15      16 + * rate_kbps   24000   12000   6000    54000   36000   18000   9000    ? + * + * rate_code   17      18      19      20      21      22      23      24 + * rate_kbps   ?       ?       ?       ?       ?       ?       ?       11000 + * + * rate_code   25      26      27      28      29      30      31      32 + * rate_kbps   5500    2000    1000    ?       ?       ?       ?       ? + * + */ +struct ath5k_rate { +	u8	valid; +	u32	modulation; +	u16	rate_kbps; +	u8	rate_code; +	u8	dot11_rate; +	u8	control_rate; +}; + +/* XXX: GRR all this stuff to get leds blinking ??? (check out setcurmode) */ +struct ath5k_rate_table { +	u16	rate_count; +	u8	rate_code_to_index[AR5K_MAX_RATES];	/* Back-mapping */ +	struct ath5k_rate rates[AR5K_MAX_RATES]; +}; + +/* + * Rate tables... + */ +#define AR5K_RATES_11A { 8, {					\ +	255, 255, 255, 255, 255, 255, 255, 255, 6, 4, 2, 0,	\ +	7, 5, 3, 1, 255, 255, 255, 255, 255, 255, 255, 255,	\ +	255, 255, 255, 255, 255, 255, 255, 255 }, {		\ +	{ 1, IEEE80211_RATE_OFDM, 6000, 11, 140, 0 },		\ +	{ 1, IEEE80211_RATE_OFDM, 9000, 15, 18, 0 },		\ +	{ 1, IEEE80211_RATE_OFDM, 12000, 10, 152, 2 },		\ +	{ 1, IEEE80211_RATE_OFDM, 18000, 14, 36, 2 },		\ +	{ 1, IEEE80211_RATE_OFDM, 24000, 9, 176, 4 },		\ +	{ 1, IEEE80211_RATE_OFDM, 36000, 13, 72, 4 },		\ +	{ 1, IEEE80211_RATE_OFDM, 48000, 8, 96, 4 },		\ +	{ 1, IEEE80211_RATE_OFDM, 54000, 12, 108, 4 } }		\ +} + +#define AR5K_RATES_11B { 4, {						\ +	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,	\ +	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,	\ +	3, 2, 1, 0, 255, 255, 255, 255 }, {				\ +	{ 1, IEEE80211_RATE_CCK, 1000, 27, 130, 0 },	\ +	{ 1, IEEE80211_RATE_CCK_2, 2000, 26, 132, 1 },	\ +	{ 1, IEEE80211_RATE_CCK_2, 5500, 25, 139, 1 },	\ +	{ 1, IEEE80211_RATE_CCK_2, 11000, 24, 150, 1 } }	\ +} + +#define AR5K_RATES_11G { 12, {					\ +	255, 255, 255, 255, 255, 255, 255, 255, 10, 8, 6, 4,	\ +	11, 9, 7, 5, 255, 255, 255, 255, 255, 255, 255, 255,	\ +	3, 2, 1, 0, 255, 255, 255, 255 }, {			\ +	{ 1, IEEE80211_RATE_CCK, 1000, 27, 2, 0 },		\ +	{ 1, IEEE80211_RATE_CCK_2, 2000, 26, 4, 1 },		\ +	{ 1, IEEE80211_RATE_CCK_2, 5500, 25, 11, 1 },		\ +	{ 1, IEEE80211_RATE_CCK_2, 11000, 24, 22, 1 },	\ +	{ 0, IEEE80211_RATE_OFDM, 6000, 11, 12, 4 },	\ +	{ 0, IEEE80211_RATE_OFDM, 9000, 15, 18, 4 },	\ +	{ 1, IEEE80211_RATE_OFDM, 12000, 10, 24, 6 },	\ +	{ 1, IEEE80211_RATE_OFDM, 18000, 14, 36, 6 },	\ +	{ 1, IEEE80211_RATE_OFDM, 24000, 9, 48, 8 },	\ +	{ 1, IEEE80211_RATE_OFDM, 36000, 13, 72, 8 },	\ +	{ 1, IEEE80211_RATE_OFDM, 48000, 8, 96, 8 },	\ +	{ 1, IEEE80211_RATE_OFDM, 54000, 12, 108, 8 } }	\ +} + +#define AR5K_RATES_TURBO { 8, {					\ +	255, 255, 255, 255, 255, 255, 255, 255, 6, 4, 2, 0,	\ +	7, 5, 3, 1, 255, 255, 255, 255, 255, 255, 255, 255,	\ +	255, 255, 255, 255, 255, 255, 255, 255 }, {		\ +	{ 1, MODULATION_TURBO, 6000, 11, 140, 0 },	\ +	{ 1, MODULATION_TURBO, 9000, 15, 18, 0 },	\ +	{ 1, MODULATION_TURBO, 12000, 10, 152, 2 },	\ +	{ 1, MODULATION_TURBO, 18000, 14, 36, 2 },	\ +	{ 1, MODULATION_TURBO, 24000, 9, 176, 4 },	\ +	{ 1, MODULATION_TURBO, 36000, 13, 72, 4 },	\ +	{ 1, MODULATION_TURBO, 48000, 8, 96, 4 },	\ +	{ 1, MODULATION_TURBO, 54000, 12, 108, 4 } }	\ +} + +#define AR5K_RATES_XR { 12, {					\ +	255, 3, 1, 255, 255, 255, 2, 0, 10, 8, 6, 4,		\ +	11, 9, 7, 5, 255, 255, 255, 255, 255, 255, 255, 255,	\ +	255, 255, 255, 255, 255, 255, 255, 255 }, {		\ +	{ 1, MODULATION_XR, 500, 7, 129, 0 },		\ +	{ 1, MODULATION_XR, 1000, 2, 139, 1 },		\ +	{ 1, MODULATION_XR, 2000, 6, 150, 2 },		\ +	{ 1, MODULATION_XR, 3000, 1, 150, 3 },		\ +	{ 1, IEEE80211_RATE_OFDM, 6000, 11, 140, 4 },	\ +	{ 1, IEEE80211_RATE_OFDM, 9000, 15, 18, 4 },	\ +	{ 1, IEEE80211_RATE_OFDM, 12000, 10, 152, 6 },	\ +	{ 1, IEEE80211_RATE_OFDM, 18000, 14, 36, 6 },	\ +	{ 1, IEEE80211_RATE_OFDM, 24000, 9, 176, 8 },	\ +	{ 1, IEEE80211_RATE_OFDM, 36000, 13, 72, 8 },	\ +	{ 1, IEEE80211_RATE_OFDM, 48000, 8, 96, 8 },	\ +	{ 1, IEEE80211_RATE_OFDM, 54000, 12, 108, 8 } }	\ +} + +/* + * Crypto definitions + */ + +#define AR5K_KEYCACHE_SIZE	8 + +/***********************\ + HW RELATED DEFINITIONS +\***********************/ + +/* + * Misc definitions + */ +#define	AR5K_RSSI_EP_MULTIPLIER	(1<<7) + +#define AR5K_ASSERT_ENTRY(_e, _s) do {		\ +	if (_e >= _s)				\ +		return (false);			\ +} while (0) + + +enum ath5k_ant_setting { +	AR5K_ANT_VARIABLE	= 0,	/* variable by programming */ +	AR5K_ANT_FIXED_A	= 1,	/* fixed to 11a frequencies */ +	AR5K_ANT_FIXED_B	= 2,	/* fixed to 11b frequencies */ +	AR5K_ANT_MAX		= 3, +}; + +/* + * Hardware interrupt abstraction + */ + +/** + * enum ath5k_int - Hardware interrupt masks helpers + * + * @AR5K_INT_RX: mask to identify received frame interrupts, of type + * 	AR5K_ISR_RXOK or AR5K_ISR_RXERR + * @AR5K_INT_RXDESC: Request RX descriptor/Read RX descriptor (?) + * @AR5K_INT_RXNOFRM: No frame received (?) + * @AR5K_INT_RXEOL: received End Of List for VEOL (Virtual End Of List). The + * 	Queue Control Unit (QCU) signals an EOL interrupt only if a descriptor's + * 	LinkPtr is NULL. For more details, refer to: + * 	http://www.freepatentsonline.com/20030225739.html + * @AR5K_INT_RXORN: Indicates we got RX overrun (eg. no more descriptors). + * 	Note that Rx overrun is not always fatal, on some chips we can continue + * 	operation without reseting the card, that's why int_fatal is not + * 	common for all chips. + * @AR5K_INT_TX: mask to identify received frame interrupts, of type + * 	AR5K_ISR_TXOK or AR5K_ISR_TXERR + * @AR5K_INT_TXDESC: Request TX descriptor/Read TX status descriptor (?) + * @AR5K_INT_TXURN: received when we should increase the TX trigger threshold + * 	We currently do increments on interrupt by + * 	(AR5K_TUNE_MAX_TX_FIFO_THRES - current_trigger_level) / 2 + * @AR5K_INT_MIB: Indicates the Management Information Base counters should be + * 	checked. We should do this with ath5k_hw_update_mib_counters() but + * 	it seems we should also then do some noise immunity work. + * @AR5K_INT_RXPHY: RX PHY Error + * @AR5K_INT_RXKCM: ?? + * @AR5K_INT_SWBA: SoftWare Beacon Alert - indicates its time to send a + * 	beacon that must be handled in software. The alternative is if you + * 	have VEOL support, in that case you let the hardware deal with things. + * @AR5K_INT_BMISS: If in STA mode this indicates we have stopped seeing + * 	beacons from the AP have associated with, we should probably try to + * 	reassociate. When in IBSS mode this might mean we have not received + * 	any beacons from any local stations. Note that every station in an + * 	IBSS schedules to send beacons at the Target Beacon Transmission Time + * 	(TBTT) with a random backoff. + * @AR5K_INT_BNR: Beacon Not Ready interrupt - ?? + * @AR5K_INT_GPIO: GPIO interrupt is used for RF Kill, disabled for now + * 	until properly handled + * @AR5K_INT_FATAL: Fatal errors were encountered, typically caused by DMA + * 	errors. These types of errors we can enable seem to be of type + * 	AR5K_SIMR2_MCABT, AR5K_SIMR2_SSERR and AR5K_SIMR2_DPERR. + * @AR5K_INT_GLOBAL: Seems to be used to clear and set the IER + * @AR5K_INT_NOCARD: signals the card has been removed + * @AR5K_INT_COMMON: common interrupts shared amogst MACs with the same + * 	bit value + * + * These are mapped to take advantage of some common bits + * between the MACs, to be able to set intr properties + * easier. Some of them are not used yet inside hw.c. Most map + * to the respective hw interrupt value as they are common amogst different + * MACs. + */ +enum ath5k_int { +	AR5K_INT_RX	= 0x00000001, /* Not common */ +	AR5K_INT_RXDESC	= 0x00000002, +	AR5K_INT_RXNOFRM = 0x00000008, +	AR5K_INT_RXEOL	= 0x00000010, +	AR5K_INT_RXORN	= 0x00000020, +	AR5K_INT_TX	= 0x00000040, /* Not common */ +	AR5K_INT_TXDESC	= 0x00000080, +	AR5K_INT_TXURN	= 0x00000800, +	AR5K_INT_MIB	= 0x00001000, +	AR5K_INT_RXPHY	= 0x00004000, +	AR5K_INT_RXKCM	= 0x00008000, +	AR5K_INT_SWBA	= 0x00010000, +	AR5K_INT_BMISS	= 0x00040000, +	AR5K_INT_BNR	= 0x00100000, /* Not common */ +	AR5K_INT_GPIO	= 0x01000000, +	AR5K_INT_FATAL	= 0x40000000, /* Not common */ +	AR5K_INT_GLOBAL	= 0x80000000, + +	AR5K_INT_COMMON  = AR5K_INT_RXNOFRM +			| AR5K_INT_RXDESC +			| AR5K_INT_RXEOL +			| AR5K_INT_RXORN +			| AR5K_INT_TXURN +			| AR5K_INT_TXDESC +			| AR5K_INT_MIB +			| AR5K_INT_RXPHY +			| AR5K_INT_RXKCM +			| AR5K_INT_SWBA +			| AR5K_INT_BMISS +			| AR5K_INT_GPIO, +	AR5K_INT_NOCARD	= 0xffffffff +}; + +/* + * Power management + */ +enum ath5k_power_mode { +	AR5K_PM_UNDEFINED = 0, +	AR5K_PM_AUTO, +	AR5K_PM_AWAKE, +	AR5K_PM_FULL_SLEEP, +	AR5K_PM_NETWORK_SLEEP, +}; + +/* + * These match net80211 definitions (not used in + * d80211). + */ +#define AR5K_LED_INIT	0 /*IEEE80211_S_INIT*/ +#define AR5K_LED_SCAN	1 /*IEEE80211_S_SCAN*/ +#define AR5K_LED_AUTH	2 /*IEEE80211_S_AUTH*/ +#define AR5K_LED_ASSOC	3 /*IEEE80211_S_ASSOC*/ +#define AR5K_LED_RUN	4 /*IEEE80211_S_RUN*/ + +/* GPIO-controlled software LED */ +#define AR5K_SOFTLED_PIN	0 +#define AR5K_SOFTLED_ON		0 +#define AR5K_SOFTLED_OFF	1 + +/* + * Chipset capabilities -see ath5k_hw_get_capability- + * get_capability function is not yet fully implemented + * in OpenHAL so most of these don't work yet... + */ +enum ath5k_capability_type { +	AR5K_CAP_REG_DMN		= 0,	/* Used to get current reg. domain id */ +	AR5K_CAP_TKIP_MIC		= 2,	/* Can handle TKIP MIC in hardware */ +	AR5K_CAP_TKIP_SPLIT		= 3,	/* TKIP uses split keys */ +	AR5K_CAP_PHYCOUNTERS		= 4,	/* PHY error counters */ +	AR5K_CAP_DIVERSITY		= 5,	/* Supports fast diversity */ +	AR5K_CAP_NUM_TXQUEUES		= 6,	/* Used to get max number of hw txqueues */ +	AR5K_CAP_VEOL			= 7,	/* Supports virtual EOL */ +	AR5K_CAP_COMPRESSION		= 8,	/* Supports compression */ +	AR5K_CAP_BURST			= 9,	/* Supports packet bursting */ +	AR5K_CAP_FASTFRAME		= 10,	/* Supports fast frames */ +	AR5K_CAP_TXPOW			= 11,	/* Used to get global tx power limit */ +	AR5K_CAP_TPC			= 12,	/* Can do per-packet tx power control (needed for 802.11a) */ +	AR5K_CAP_BSSIDMASK		= 13,	/* Supports bssid mask */ +	AR5K_CAP_MCAST_KEYSRCH		= 14,	/* Supports multicast key search */ +	AR5K_CAP_TSF_ADJUST		= 15,	/* Supports beacon tsf adjust */ +	AR5K_CAP_XR			= 16,	/* Supports XR mode */ +	AR5K_CAP_WME_TKIPMIC 		= 17,	/* Supports TKIP MIC when using WMM */ +	AR5K_CAP_CHAN_HALFRATE 		= 18,	/* Supports half rate channels */ +	AR5K_CAP_CHAN_QUARTERRATE 	= 19,	/* Supports quarter rate channels */ +	AR5K_CAP_RFSILENT		= 20,	/* Supports RFsilent */ +}; + +struct ath5k_capabilities { +	/* +	 * Supported PHY modes +	 * (ie. CHANNEL_A, CHANNEL_B, ...) +	 */ +	DECLARE_BITMAP(cap_mode, NUM_DRIVER_MODES); + +	/* +	 * Frequency range (without regulation restrictions) +	 */ +	struct { +		u16	range_2ghz_min; +		u16	range_2ghz_max; +		u16	range_5ghz_min; +		u16	range_5ghz_max; +	} cap_range; + +	/* +	 * Active regulation domain settings +	 */ +	struct { +		enum ath5k_regdom reg_current; +		enum ath5k_regdom reg_hw; +	} cap_regdomain; + +	/* +	 * Values stored in the EEPROM (some of them...) +	 */ +	struct ath5k_eeprom_info	cap_eeprom; + +	/* +	 * Queue information +	 */ +	struct { +		u8	q_tx_num; +	} cap_queues; +}; + + +/***************************************\ +  HARDWARE ABSTRACTION LAYER STRUCTURE +\***************************************/ + +/* + * Misc defines + */ + +#define AR5K_MAX_GPIO		10 +#define AR5K_MAX_RF_BANKS	8 + +struct ath5k_hw { +	u32			ah_magic; + +	struct ath5k_softc	*ah_sc; +	void __iomem		*ah_iobase; + +	enum ath5k_int		ah_imr; + +	enum ieee80211_if_types	ah_op_mode; +	enum ath5k_power_mode	ah_power_mode; +	struct ieee80211_channel ah_current_channel; +	bool			ah_turbo; +	bool			ah_calibration; +	bool			ah_running; +	bool			ah_single_chip; +	enum ath5k_rfgain	ah_rf_gain; + +	u32			ah_mac_srev; +	u16			ah_mac_version; +	u16			ah_mac_revision; +	u16			ah_phy_revision; +	u16			ah_radio_5ghz_revision; +	u16			ah_radio_2ghz_revision; + +	enum ath5k_version	ah_version; +	enum ath5k_radio	ah_radio; +	u32			ah_phy; + +	bool			ah_5ghz; +	bool			ah_2ghz; + +#define ah_regdomain		ah_capabilities.cap_regdomain.reg_current +#define ah_regdomain_hw		ah_capabilities.cap_regdomain.reg_hw +#define ah_modes		ah_capabilities.cap_mode +#define ah_ee_version		ah_capabilities.cap_eeprom.ee_version + +	u32			ah_atim_window; +	u32			ah_aifs; +	u32			ah_cw_min; +	u32			ah_cw_max; +	bool			ah_software_retry; +	u32			ah_limit_tx_retries; + +	u32			ah_antenna[AR5K_EEPROM_N_MODES][AR5K_ANT_MAX]; +	bool			ah_ant_diversity; + +	u8			ah_sta_id[ETH_ALEN]; + +	/* Current BSSID we are trying to assoc to / creating. +	 * This is passed by mac80211 on config_interface() and cached here for +	 * use in resets */ +	u8			ah_bssid[ETH_ALEN]; + +	u32			ah_gpio[AR5K_MAX_GPIO]; +	int			ah_gpio_npins; + +	struct ath5k_capabilities ah_capabilities; + +	struct ath5k_txq_info	ah_txq[AR5K_NUM_TX_QUEUES]; +	u32			ah_txq_status; +	u32			ah_txq_imr_txok; +	u32			ah_txq_imr_txerr; +	u32			ah_txq_imr_txurn; +	u32			ah_txq_imr_txdesc; +	u32			ah_txq_imr_txeol; +	u32			*ah_rf_banks; +	size_t			ah_rf_banks_size; +	struct ath5k_gain	ah_gain; +	u32			ah_offset[AR5K_MAX_RF_BANKS]; + +	struct { +		u16		txp_pcdac[AR5K_EEPROM_POWER_TABLE_SIZE]; +		u16		txp_rates[AR5K_MAX_RATES]; +		s16		txp_min; +		s16		txp_max; +		bool		txp_tpc; +		s16		txp_ofdm; +	} ah_txpower; + +	struct { +		bool		r_enabled; +		int		r_last_alert; +		struct ieee80211_channel r_last_channel; +	} ah_radar; + +	/* noise floor from last periodic calibration */ +	s32			ah_noise_floor; + +	/* +	 * Function pointers +	 */ +	int (*ah_setup_tx_desc)(struct ath5k_hw *, struct ath5k_desc *, +		unsigned int, unsigned int, enum ath5k_pkt_type, unsigned int, +		unsigned int, unsigned int, unsigned int, unsigned int, +		unsigned int, unsigned int, unsigned int); +	bool (*ah_setup_xtx_desc)(struct ath5k_hw *, struct ath5k_desc *, +		unsigned int, unsigned int, unsigned int, unsigned int, +		unsigned int, unsigned int); +	int (*ah_proc_tx_desc)(struct ath5k_hw *, struct ath5k_desc *); +	int (*ah_proc_rx_desc)(struct ath5k_hw *, struct ath5k_desc *); +}; + +/* + * Prototypes + */ + +/* General Functions */ +extern int ath5k_hw_register_timeout(struct ath5k_hw *ah, u32 reg, u32 flag, u32 val, bool is_set); +/* Attach/Detach Functions */ +extern struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version); +extern const struct ath5k_rate_table *ath5k_hw_get_rate_table(struct ath5k_hw *ah, unsigned int mode); +extern void ath5k_hw_detach(struct ath5k_hw *ah); +/* Reset Functions */ +extern int ath5k_hw_reset(struct ath5k_hw *ah, enum ieee80211_if_types op_mode, struct ieee80211_channel *channel, bool change_channel); +/* Power management functions */ +extern int ath5k_hw_set_power(struct ath5k_hw *ah, enum ath5k_power_mode mode, bool set_chip, u16 sleep_duration); +/* DMA Related Functions */ +extern void ath5k_hw_start_rx(struct ath5k_hw *ah); +extern int ath5k_hw_stop_rx_dma(struct ath5k_hw *ah); +extern u32 ath5k_hw_get_rx_buf(struct ath5k_hw *ah); +extern void ath5k_hw_put_rx_buf(struct ath5k_hw *ah, u32 phys_addr); +extern int ath5k_hw_tx_start(struct ath5k_hw *ah, unsigned int queue); +extern int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue); +extern u32 ath5k_hw_get_tx_buf(struct ath5k_hw *ah, unsigned int queue); +extern int ath5k_hw_put_tx_buf(struct ath5k_hw *ah, unsigned int queue, u32 phys_addr); +extern int ath5k_hw_update_tx_triglevel(struct ath5k_hw *ah, bool increase); +/* Interrupt handling */ +extern bool ath5k_hw_is_intr_pending(struct ath5k_hw *ah); +extern int ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask); +extern enum ath5k_int ath5k_hw_set_intr(struct ath5k_hw *ah, enum ath5k_int new_mask); +/* EEPROM access functions */ +extern int ath5k_hw_set_regdomain(struct ath5k_hw *ah, u16 regdomain); +/* Protocol Control Unit Functions */ +extern int ath5k_hw_set_opmode(struct ath5k_hw *ah); +/* BSSID Functions */ +extern void ath5k_hw_get_lladdr(struct ath5k_hw *ah, u8 *mac); +extern int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac); +extern void ath5k_hw_set_associd(struct ath5k_hw *ah, const u8 *bssid, u16 assoc_id); +extern int ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask); +/* Receive start/stop functions */ +extern void ath5k_hw_start_rx_pcu(struct ath5k_hw *ah); +extern void ath5k_hw_stop_pcu_recv(struct ath5k_hw *ah); +/* RX Filter functions */ +extern void ath5k_hw_set_mcast_filter(struct ath5k_hw *ah, u32 filter0, u32 filter1); +extern int ath5k_hw_set_mcast_filterindex(struct ath5k_hw *ah, u32 index); +extern int ath5k_hw_clear_mcast_filter_idx(struct ath5k_hw *ah, u32 index); +extern u32 ath5k_hw_get_rx_filter(struct ath5k_hw *ah); +extern void ath5k_hw_set_rx_filter(struct ath5k_hw *ah, u32 filter); +/* Beacon related functions */ +extern u32 ath5k_hw_get_tsf32(struct ath5k_hw *ah); +extern u64 ath5k_hw_get_tsf64(struct ath5k_hw *ah); +extern void ath5k_hw_reset_tsf(struct ath5k_hw *ah); +extern void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval); +#if 0 +extern int ath5k_hw_set_beacon_timers(struct ath5k_hw *ah, const struct ath5k_beacon_state *state); +extern void ath5k_hw_reset_beacon(struct ath5k_hw *ah); +extern int ath5k_hw_beaconq_finish(struct ath5k_hw *ah, unsigned long phys_addr); +#endif +extern void ath5k_hw_update_mib_counters(struct ath5k_hw *ah, struct ath5k_mib_stats *statistics); +/* ACK bit rate */ +void ath5k_hw_set_ack_bitrate_high(struct ath5k_hw *ah, bool high); +/* ACK/CTS Timeouts */ +extern int ath5k_hw_set_ack_timeout(struct ath5k_hw *ah, unsigned int timeout); +extern unsigned int ath5k_hw_get_ack_timeout(struct ath5k_hw *ah); +extern int ath5k_hw_set_cts_timeout(struct ath5k_hw *ah, unsigned int timeout); +extern unsigned int ath5k_hw_get_cts_timeout(struct ath5k_hw *ah); +/* Key table (WEP) functions */ +extern int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry); +extern int ath5k_hw_is_key_valid(struct ath5k_hw *ah, u16 entry); +extern int ath5k_hw_set_key(struct ath5k_hw *ah, u16 entry, const struct ieee80211_key_conf *key, const u8 *mac); +extern int ath5k_hw_set_key_lladdr(struct ath5k_hw *ah, u16 entry, const u8 *mac); +/* Queue Control Unit, DFS Control Unit Functions */ +extern int ath5k_hw_setup_tx_queue(struct ath5k_hw *ah, enum ath5k_tx_queue queue_type, struct ath5k_txq_info *queue_info); +extern int ath5k_hw_setup_tx_queueprops(struct ath5k_hw *ah, int queue, const struct ath5k_txq_info *queue_info); +extern int ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue, struct ath5k_txq_info *queue_info); +extern void ath5k_hw_release_tx_queue(struct ath5k_hw *ah, unsigned int queue); +extern int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue); +extern u32 ath5k_hw_num_tx_pending(struct ath5k_hw *ah, unsigned int queue); +extern int ath5k_hw_set_slot_time(struct ath5k_hw *ah, unsigned int slot_time); +extern unsigned int ath5k_hw_get_slot_time(struct ath5k_hw *ah); +/* Hardware Descriptor Functions */ +extern int ath5k_hw_setup_rx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc, u32 size, unsigned int flags); +/* GPIO Functions */ +extern void ath5k_hw_set_ledstate(struct ath5k_hw *ah, unsigned int state); +extern int ath5k_hw_set_gpio_output(struct ath5k_hw *ah, u32 gpio); +extern int ath5k_hw_set_gpio_input(struct ath5k_hw *ah, u32 gpio); +extern u32 ath5k_hw_get_gpio(struct ath5k_hw *ah, u32 gpio); +extern int ath5k_hw_set_gpio(struct ath5k_hw *ah, u32 gpio, u32 val); +extern void ath5k_hw_set_gpio_intr(struct ath5k_hw *ah, unsigned int gpio, u32 interrupt_level); +/* Regulatory Domain/Channels Setup */ +extern u16 ath5k_get_regdomain(struct ath5k_hw *ah); +/* Misc functions */ +extern int ath5k_hw_get_capability(struct ath5k_hw *ah, enum ath5k_capability_type cap_type, u32 capability, u32 *result); + + +/* Initial register settings functions */ +extern int ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool change_channel); +/* Initialize RF */ +extern int ath5k_hw_rfregs(struct ath5k_hw *ah, struct ieee80211_channel *channel, unsigned int mode); +extern int ath5k_hw_rfgain(struct ath5k_hw *ah, unsigned int freq); +extern enum ath5k_rfgain ath5k_hw_get_rf_gain(struct ath5k_hw *ah); +extern int ath5k_hw_set_rfgain_opt(struct ath5k_hw *ah); + + +/* PHY/RF channel functions */ +extern bool ath5k_channel_ok(struct ath5k_hw *ah, u16 freq, unsigned int flags); +extern int ath5k_hw_channel(struct ath5k_hw *ah, struct ieee80211_channel *channel); +/* PHY calibration */ +extern int ath5k_hw_phy_calibrate(struct ath5k_hw *ah, struct ieee80211_channel *channel); +extern int ath5k_hw_phy_disable(struct ath5k_hw *ah); +/* Misc PHY functions */ +extern u16 ath5k_hw_radio_revision(struct ath5k_hw *ah, unsigned int chan); +extern void ath5k_hw_set_def_antenna(struct ath5k_hw *ah, unsigned int ant); +extern unsigned int ath5k_hw_get_def_antenna(struct ath5k_hw *ah); +extern int ath5k_hw_noise_floor_calibration(struct ath5k_hw *ah, short freq); +/* TX power setup */ +extern int ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel, unsigned int txpower); +extern int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, unsigned int power); + + +static inline u32 ath5k_hw_reg_read(struct ath5k_hw *ah, u16 reg) +{ +	return ioread32(ah->ah_iobase + reg); +} + +static inline void ath5k_hw_reg_write(struct ath5k_hw *ah, u32 val, u16 reg) +{ +	iowrite32(val, ah->ah_iobase + reg); +} + +#endif diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c new file mode 100644 index 000000000000..d3d37282f3dc --- /dev/null +++ b/drivers/net/wireless/ath5k/base.c @@ -0,0 +1,2817 @@ +/*- + * 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 <linux/version.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/if.h> +#include <linux/netdevice.h> +#include <linux/cache.h> +#include <linux/pci.h> +#include <linux/ethtool.h> +#include <linux/uaccess.h> + +#include <net/ieee80211_radiotap.h> + +#include <asm/unaligned.h> + +#include "base.h" +#include "reg.h" +#include "debug.h" + +/* unaligned little endian access */ +#define LE_READ_2(_p) (le16_to_cpu(get_unaligned((__le16 *)(_p)))) +#define LE_READ_4(_p) (le32_to_cpu(get_unaligned((__le32 *)(_p)))) + +enum { +	ATH_LED_TX, +	ATH_LED_RX, +}; + +static int ath5k_calinterval = 10; /* Calibrate PHY every 10 secs (TODO: Fixme) */ + + +/******************\ +* Internal defines * +\******************/ + +/* Module info */ +MODULE_AUTHOR("Jiri Slaby"); +MODULE_AUTHOR("Nick Kossifidis"); +MODULE_DESCRIPTION("Support for 5xxx series of Atheros 802.11 wireless LAN cards."); +MODULE_SUPPORTED_DEVICE("Atheros 5xxx WLAN cards"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_VERSION("0.1.1 (EXPERIMENTAL)"); + + +/* Known PCI ids */ +static struct pci_device_id ath5k_pci_id_table[] __devinitdata = { +	{ PCI_VDEVICE(ATHEROS, 0x0207), .driver_data = AR5K_AR5210 }, /* 5210 early */ +	{ PCI_VDEVICE(ATHEROS, 0x0007), .driver_data = AR5K_AR5210 }, /* 5210 */ +	{ PCI_VDEVICE(ATHEROS, 0x0011), .driver_data = AR5K_AR5211 }, /* 5311 - this is on AHB bus !*/ +	{ PCI_VDEVICE(ATHEROS, 0x0012), .driver_data = AR5K_AR5211 }, /* 5211 */ +	{ PCI_VDEVICE(ATHEROS, 0x0013), .driver_data = AR5K_AR5212 }, /* 5212 */ +	{ PCI_VDEVICE(3COM_2,  0x0013), .driver_data = AR5K_AR5212 }, /* 3com 5212 */ +	{ PCI_VDEVICE(3COM,    0x0013), .driver_data = AR5K_AR5212 }, /* 3com 3CRDAG675 5212 */ +	{ PCI_VDEVICE(ATHEROS, 0x1014), .driver_data = AR5K_AR5212 }, /* IBM minipci 5212 */ +	{ PCI_VDEVICE(ATHEROS, 0x0014), .driver_data = AR5K_AR5212 }, /* 5212 combatible */ +	{ PCI_VDEVICE(ATHEROS, 0x0015), .driver_data = AR5K_AR5212 }, /* 5212 combatible */ +	{ PCI_VDEVICE(ATHEROS, 0x0016), .driver_data = AR5K_AR5212 }, /* 5212 combatible */ +	{ PCI_VDEVICE(ATHEROS, 0x0017), .driver_data = AR5K_AR5212 }, /* 5212 combatible */ +	{ PCI_VDEVICE(ATHEROS, 0x0018), .driver_data = AR5K_AR5212 }, /* 5212 combatible */ +	{ PCI_VDEVICE(ATHEROS, 0x0019), .driver_data = AR5K_AR5212 }, /* 5212 combatible */ +	{ PCI_VDEVICE(ATHEROS, 0x001a), .driver_data = AR5K_AR5212 }, /* 2413 Griffin-lite */ +	{ PCI_VDEVICE(ATHEROS, 0x001b), .driver_data = AR5K_AR5212 }, /* 5413 Eagle */ +	{ PCI_VDEVICE(ATHEROS, 0x001c), .driver_data = AR5K_AR5212 }, /* 5424 Condor (PCI-E)*/ +	{ PCI_VDEVICE(ATHEROS, 0x0023), .driver_data = AR5K_AR5212 }, /* 5416 */ +	{ PCI_VDEVICE(ATHEROS, 0x0024), .driver_data = AR5K_AR5212 }, /* 5418 */ +	{ 0 } +}; +MODULE_DEVICE_TABLE(pci, ath5k_pci_id_table); + +/* Known SREVs */ +static struct ath5k_srev_name srev_names[] = { +	{ "5210",	AR5K_VERSION_VER,	AR5K_SREV_VER_AR5210 }, +	{ "5311",	AR5K_VERSION_VER,	AR5K_SREV_VER_AR5311 }, +	{ "5311A",	AR5K_VERSION_VER,	AR5K_SREV_VER_AR5311A }, +	{ "5311B",	AR5K_VERSION_VER,	AR5K_SREV_VER_AR5311B }, +	{ "5211",	AR5K_VERSION_VER,	AR5K_SREV_VER_AR5211 }, +	{ "5212",	AR5K_VERSION_VER,	AR5K_SREV_VER_AR5212 }, +	{ "5213",	AR5K_VERSION_VER,	AR5K_SREV_VER_AR5213 }, +	{ "5213A",	AR5K_VERSION_VER,	AR5K_SREV_VER_AR5213A }, +	{ "2424",	AR5K_VERSION_VER,	AR5K_SREV_VER_AR2424 }, +	{ "5424",	AR5K_VERSION_VER,	AR5K_SREV_VER_AR5424 }, +	{ "5413",	AR5K_VERSION_VER,	AR5K_SREV_VER_AR5413 }, +	{ "5414",	AR5K_VERSION_VER,	AR5K_SREV_VER_AR5414 }, +	{ "5416",	AR5K_VERSION_VER,	AR5K_SREV_VER_AR5416 }, +	{ "5418",	AR5K_VERSION_VER,	AR5K_SREV_VER_AR5418 }, +	{ "xxxxx",	AR5K_VERSION_VER,	AR5K_SREV_UNKNOWN }, +	{ "5110",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_5110 }, +	{ "5111",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_5111 }, +	{ "2111",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_2111 }, +	{ "5112",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_5112 }, +	{ "5112A",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_5112A }, +	{ "2112",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_2112 }, +	{ "2112A",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_2112A }, +	{ "SChip",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_SC1 }, +	{ "SChip",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_SC2 }, +	{ "5133",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_5133 }, +	{ "xxxxx",	AR5K_VERSION_RAD,	AR5K_SREV_UNKNOWN }, +}; + +/* + * Prototypes - PCI stack related functions + */ +static int __devinit	ath5k_pci_probe(struct pci_dev *pdev, +				const struct pci_device_id *id); +static void __devexit	ath5k_pci_remove(struct pci_dev *pdev); +#ifdef CONFIG_PM +static int		ath5k_pci_suspend(struct pci_dev *pdev, +					pm_message_t state); +static int		ath5k_pci_resume(struct pci_dev *pdev); +#else +#define ath5k_pci_suspend NULL +#define ath5k_pci_resume NULL +#endif /* CONFIG_PM */ + +static struct pci_driver ath5k_pci_drv_id = { +	.name		= "ath5k_pci", +	.id_table	= ath5k_pci_id_table, +	.probe		= ath5k_pci_probe, +	.remove		= __devexit_p(ath5k_pci_remove), +	.suspend	= ath5k_pci_suspend, +	.resume		= ath5k_pci_resume, +}; + + + +/* + * Prototypes - MAC 802.11 stack related functions + */ +static int ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb, +		struct ieee80211_tx_control *ctl); +static int ath5k_reset(struct ieee80211_hw *hw); +static int ath5k_start(struct ieee80211_hw *hw); +static void ath5k_stop(struct ieee80211_hw *hw); +static int ath5k_add_interface(struct ieee80211_hw *hw, +		struct ieee80211_if_init_conf *conf); +static void ath5k_remove_interface(struct ieee80211_hw *hw, +		struct ieee80211_if_init_conf *conf); +static int ath5k_config(struct ieee80211_hw *hw, +		struct ieee80211_conf *conf); +static int ath5k_config_interface(struct ieee80211_hw *hw, int if_id, +		struct ieee80211_if_conf *conf); +static void ath5k_configure_filter(struct ieee80211_hw *hw, +		unsigned int changed_flags, +		unsigned int *new_flags, +		int mc_count, struct dev_mc_list *mclist); +static int ath5k_set_key(struct ieee80211_hw *hw, +		enum set_key_cmd cmd, +		const u8 *local_addr, const u8 *addr, +		struct ieee80211_key_conf *key); +static int ath5k_get_stats(struct ieee80211_hw *hw, +		struct ieee80211_low_level_stats *stats); +static int ath5k_get_tx_stats(struct ieee80211_hw *hw, +		struct ieee80211_tx_queue_stats *stats); +static u64 ath5k_get_tsf(struct ieee80211_hw *hw); +static void ath5k_reset_tsf(struct ieee80211_hw *hw); +static int ath5k_beacon_update(struct ieee80211_hw *hw, +		struct sk_buff *skb, +		struct ieee80211_tx_control *ctl); + +static struct ieee80211_ops ath5k_hw_ops = { +	.tx 		= ath5k_tx, +	.start 		= ath5k_start, +	.stop 		= ath5k_stop, +	.add_interface 	= ath5k_add_interface, +	.remove_interface = ath5k_remove_interface, +	.config 	= ath5k_config, +	.config_interface = ath5k_config_interface, +	.configure_filter = ath5k_configure_filter, +	.set_key 	= ath5k_set_key, +	.get_stats 	= ath5k_get_stats, +	.conf_tx 	= NULL, +	.get_tx_stats 	= ath5k_get_tx_stats, +	.get_tsf 	= ath5k_get_tsf, +	.reset_tsf 	= ath5k_reset_tsf, +	.beacon_update 	= ath5k_beacon_update, +}; + +/* + * Prototypes - Internal functions + */ +/* Attach detach */ +static int 	ath5k_attach(struct pci_dev *pdev, +			struct ieee80211_hw *hw); +static void 	ath5k_detach(struct pci_dev *pdev, +			struct ieee80211_hw *hw); +/* Channel/mode setup */ +static inline short ath5k_ieee2mhz(short chan); +static unsigned int ath5k_copy_rates(struct ieee80211_rate *rates, +				const struct ath5k_rate_table *rt, +				unsigned int max); +static unsigned int ath5k_copy_channels(struct ath5k_hw *ah, +				struct ieee80211_channel *channels, +				unsigned int mode, +				unsigned int max); +static int 	ath5k_getchannels(struct ieee80211_hw *hw); +static int 	ath5k_chan_set(struct ath5k_softc *sc, +				struct ieee80211_channel *chan); +static void	ath5k_setcurmode(struct ath5k_softc *sc, +				unsigned int mode); +static void	ath5k_mode_setup(struct ath5k_softc *sc); +/* Descriptor setup */ +static int	ath5k_desc_alloc(struct ath5k_softc *sc, +				struct pci_dev *pdev); +static void	ath5k_desc_free(struct ath5k_softc *sc, +				struct pci_dev *pdev); +/* Buffers setup */ +static int 	ath5k_rxbuf_setup(struct ath5k_softc *sc, +				struct ath5k_buf *bf); +static int 	ath5k_txbuf_setup(struct ath5k_softc *sc, +				struct ath5k_buf *bf, +				struct ieee80211_tx_control *ctl); + +static inline void ath5k_txbuf_free(struct ath5k_softc *sc, +				struct ath5k_buf *bf) +{ +	BUG_ON(!bf); +	if (!bf->skb) +		return; +	pci_unmap_single(sc->pdev, bf->skbaddr, bf->skb->len, +			PCI_DMA_TODEVICE); +	dev_kfree_skb(bf->skb); +	bf->skb = NULL; +} + +/* Queues setup */ +static struct 	ath5k_txq *ath5k_txq_setup(struct ath5k_softc *sc, +				int qtype, int subtype); +static int 	ath5k_beaconq_setup(struct ath5k_hw *ah); +static int 	ath5k_beaconq_config(struct ath5k_softc *sc); +static void 	ath5k_txq_drainq(struct ath5k_softc *sc, +				struct ath5k_txq *txq); +static void 	ath5k_txq_cleanup(struct ath5k_softc *sc); +static void 	ath5k_txq_release(struct ath5k_softc *sc); +/* Rx handling */ +static int 	ath5k_rx_start(struct ath5k_softc *sc); +static void 	ath5k_rx_stop(struct ath5k_softc *sc); +static unsigned int ath5k_rx_decrypted(struct ath5k_softc *sc, +					struct ath5k_desc *ds, +					struct sk_buff *skb); +static void 	ath5k_tasklet_rx(unsigned long data); +/* Tx handling */ +static void 	ath5k_tx_processq(struct ath5k_softc *sc, +				struct ath5k_txq *txq); +static void 	ath5k_tasklet_tx(unsigned long data); +/* Beacon handling */ +static int 	ath5k_beacon_setup(struct ath5k_softc *sc, +				struct ath5k_buf *bf, +				struct ieee80211_tx_control *ctl); +static void 	ath5k_beacon_send(struct ath5k_softc *sc); +static void 	ath5k_beacon_config(struct ath5k_softc *sc); + +static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp) +{ +	u64 tsf = ath5k_hw_get_tsf64(ah); + +	if ((tsf & 0x7fff) < rstamp) +		tsf -= 0x8000; + +	return (tsf & ~0x7fff) | rstamp; +} + +/* Interrupt handling */ +static int 	ath5k_init(struct ath5k_softc *sc); +static int 	ath5k_stop_locked(struct ath5k_softc *sc); +static int 	ath5k_stop_hw(struct ath5k_softc *sc); +static irqreturn_t ath5k_intr(int irq, void *dev_id); +static void 	ath5k_tasklet_reset(unsigned long data); + +static void 	ath5k_calibrate(unsigned long data); +/* LED functions */ +static void 	ath5k_led_off(unsigned long data); +static void 	ath5k_led_blink(struct ath5k_softc *sc, +				unsigned int on, +				unsigned int off); +static void 	ath5k_led_event(struct ath5k_softc *sc, +				int event); + + +/* + * Module init/exit functions + */ +static int __init +init_ath5k_pci(void) +{ +	int ret; + +	ath5k_debug_init(); + +	ret = pci_register_driver(&ath5k_pci_drv_id); +	if (ret) { +		printk(KERN_ERR "ath5k_pci: can't register pci driver\n"); +		return ret; +	} + +	return 0; +} + +static void __exit +exit_ath5k_pci(void) +{ +	pci_unregister_driver(&ath5k_pci_drv_id); + +	ath5k_debug_finish(); +} + +module_init(init_ath5k_pci); +module_exit(exit_ath5k_pci); + + +/********************\ +* PCI Initialization * +\********************/ + +static const char * +ath5k_chip_name(enum ath5k_srev_type type, u_int16_t val) +{ +	const char *name = "xxxxx"; +	unsigned int i; + +	for (i = 0; i < ARRAY_SIZE(srev_names); i++) { +		if (srev_names[i].sr_type != type) +			continue; +		if ((val & 0xff) < srev_names[i + 1].sr_val) { +			name = srev_names[i].sr_name; +			break; +		} +	} + +	return name; +} + +static int __devinit +ath5k_pci_probe(struct pci_dev *pdev, +		const struct pci_device_id *id) +{ +	void __iomem *mem; +	struct ath5k_softc *sc; +	struct ieee80211_hw *hw; +	int ret; +	u8 csz; + +	ret = pci_enable_device(pdev); +	if (ret) { +		dev_err(&pdev->dev, "can't enable device\n"); +		goto err; +	} + +	/* XXX 32-bit addressing only */ +	ret = pci_set_dma_mask(pdev, DMA_32BIT_MASK); +	if (ret) { +		dev_err(&pdev->dev, "32-bit DMA not available\n"); +		goto err_dis; +	} + +	/* +	 * Cache line size is used to size and align various +	 * structures used to communicate with the hardware. +	 */ +	pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &csz); +	if (csz == 0) { +		/* +		 * Linux 2.4.18 (at least) writes the cache line size +		 * register as a 16-bit wide register which is wrong. +		 * We must have this setup properly for rx buffer +		 * DMA to work so force a reasonable value here if it +		 * comes up zero. +		 */ +		csz = L1_CACHE_BYTES / sizeof(u32); +		pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, csz); +	} +	/* +	 * The default setting of latency timer yields poor results, +	 * set it to the value used by other systems.  It may be worth +	 * tweaking this setting more. +	 */ +	pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8); + +	/* Enable bus mastering */ +	pci_set_master(pdev); + +	/* +	 * Disable the RETRY_TIMEOUT register (0x41) to keep +	 * PCI Tx retries from interfering with C3 CPU state. +	 */ +	pci_write_config_byte(pdev, 0x41, 0); + +	ret = pci_request_region(pdev, 0, "ath5k"); +	if (ret) { +		dev_err(&pdev->dev, "cannot reserve PCI memory region\n"); +		goto err_dis; +	} + +	mem = pci_iomap(pdev, 0, 0); +	if (!mem) { +		dev_err(&pdev->dev, "cannot remap PCI memory region\n") ; +		ret = -EIO; +		goto err_reg; +	} + +	/* +	 * Allocate hw (mac80211 main struct) +	 * and hw->priv (driver private data) +	 */ +	hw = ieee80211_alloc_hw(sizeof(*sc), &ath5k_hw_ops); +	if (hw == NULL) { +		dev_err(&pdev->dev, "cannot allocate ieee80211_hw\n"); +		ret = -ENOMEM; +		goto err_map; +	} + +	dev_info(&pdev->dev, "registered as '%s'\n", wiphy_name(hw->wiphy)); + +	/* Initialize driver private data */ +	SET_IEEE80211_DEV(hw, &pdev->dev); +	hw->flags = IEEE80211_HW_RX_INCLUDES_FCS; +	hw->extra_tx_headroom = 2; +	hw->channel_change_time = 5000; +	/* these names are misleading */ +	hw->max_rssi = -110; /* signal in dBm */ +	hw->max_noise = -110; /* noise in dBm */ +	hw->max_signal = 100; /* we will provide a percentage based on rssi */ +	sc = hw->priv; +	sc->hw = hw; +	sc->pdev = pdev; + +	ath5k_debug_init_device(sc); + +	/* +	 * Mark the device as detached to avoid processing +	 * interrupts until setup is complete. +	 */ +	__set_bit(ATH_STAT_INVALID, sc->status); + +	sc->iobase = mem; /* So we can unmap it on detach */ +	sc->cachelsz = csz * sizeof(u32); /* convert to bytes */ +	sc->opmode = IEEE80211_IF_TYPE_STA; +	mutex_init(&sc->lock); +	spin_lock_init(&sc->rxbuflock); +	spin_lock_init(&sc->txbuflock); + +	/* Set private data */ +	pci_set_drvdata(pdev, hw); + +	/* Enable msi for devices that support it */ +	pci_enable_msi(pdev); + +	/* Setup interrupt handler */ +	ret = request_irq(pdev->irq, ath5k_intr, IRQF_SHARED, "ath", sc); +	if (ret) { +		ATH5K_ERR(sc, "request_irq failed\n"); +		goto err_free; +	} + +	/* Initialize device */ +	sc->ah = ath5k_hw_attach(sc, id->driver_data); +	if (IS_ERR(sc->ah)) { +		ret = PTR_ERR(sc->ah); +		goto err_irq; +	} + +	/* Finish private driver data initialization */ +	ret = ath5k_attach(pdev, hw); +	if (ret) +		goto err_ah; + +	ATH5K_INFO(sc, "Atheros AR%s chip found (MAC: 0x%x, PHY: 0x%x)\n", +			ath5k_chip_name(AR5K_VERSION_VER,sc->ah->ah_mac_srev), +					sc->ah->ah_mac_srev, +					sc->ah->ah_phy_revision); + +	if(!sc->ah->ah_single_chip){ +		/* Single chip radio (!RF5111) */ +		if(sc->ah->ah_radio_5ghz_revision && !sc->ah->ah_radio_2ghz_revision) { +			/* No 5GHz support -> report 2GHz radio */ +			if(!test_bit(MODE_IEEE80211A, sc->ah->ah_capabilities.cap_mode)){ +				ATH5K_INFO(sc, "RF%s 2GHz radio found (0x%x)\n", +					ath5k_chip_name(AR5K_VERSION_RAD,sc->ah->ah_radio_5ghz_revision), +							sc->ah->ah_radio_5ghz_revision); +			/* No 2GHz support (5110 and some 5Ghz only cards) -> report 5Ghz radio */ +			} else if(!test_bit(MODE_IEEE80211B, sc->ah->ah_capabilities.cap_mode)){ +				ATH5K_INFO(sc, "RF%s 5GHz radio found (0x%x)\n", +					ath5k_chip_name(AR5K_VERSION_RAD,sc->ah->ah_radio_5ghz_revision), +							sc->ah->ah_radio_5ghz_revision); +			/* Multiband radio */ +			} else { +				ATH5K_INFO(sc, "RF%s multiband radio found" +					" (0x%x)\n", +					ath5k_chip_name(AR5K_VERSION_RAD,sc->ah->ah_radio_5ghz_revision), +							sc->ah->ah_radio_5ghz_revision); +			} +		} +		/* Multi chip radio (RF5111 - RF2111) -> report both 2GHz/5GHz radios */ +		else if(sc->ah->ah_radio_5ghz_revision && sc->ah->ah_radio_2ghz_revision){ +			ATH5K_INFO(sc, "RF%s 5GHz radio found (0x%x)\n", +				ath5k_chip_name(AR5K_VERSION_RAD,sc->ah->ah_radio_5ghz_revision), +						sc->ah->ah_radio_5ghz_revision); +			ATH5K_INFO(sc, "RF%s 2GHz radio found (0x%x)\n", +				ath5k_chip_name(AR5K_VERSION_RAD,sc->ah->ah_radio_2ghz_revision), +						sc->ah->ah_radio_2ghz_revision); +		} +	} + + +	/* ready to process interrupts */ +	__clear_bit(ATH_STAT_INVALID, sc->status); + +	return 0; +err_ah: +	ath5k_hw_detach(sc->ah); +err_irq: +	free_irq(pdev->irq, sc); +err_free: +	pci_disable_msi(pdev); +	ieee80211_free_hw(hw); +err_map: +	pci_iounmap(pdev, mem); +err_reg: +	pci_release_region(pdev, 0); +err_dis: +	pci_disable_device(pdev); +err: +	return ret; +} + +static void __devexit +ath5k_pci_remove(struct pci_dev *pdev) +{ +	struct ieee80211_hw *hw = pci_get_drvdata(pdev); +	struct ath5k_softc *sc = hw->priv; + +	ath5k_debug_finish_device(sc); +	ath5k_detach(pdev, hw); +	ath5k_hw_detach(sc->ah); +	free_irq(pdev->irq, sc); +	pci_disable_msi(pdev); +	pci_iounmap(pdev, sc->iobase); +	pci_release_region(pdev, 0); +	pci_disable_device(pdev); +	ieee80211_free_hw(hw); +} + +#ifdef CONFIG_PM +static int +ath5k_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ +	struct ieee80211_hw *hw = pci_get_drvdata(pdev); +	struct ath5k_softc *sc = hw->priv; + +	if (test_bit(ATH_STAT_LEDSOFT, sc->status)) +		ath5k_hw_set_gpio(sc->ah, sc->led_pin, 1); + +	ath5k_stop_hw(sc); +	pci_save_state(pdev); +	pci_disable_device(pdev); +	pci_set_power_state(pdev, PCI_D3hot); + +	return 0; +} + +static int +ath5k_pci_resume(struct pci_dev *pdev) +{ +	struct ieee80211_hw *hw = pci_get_drvdata(pdev); +	struct ath5k_softc *sc = hw->priv; +	int err; + +	err = pci_set_power_state(pdev, PCI_D0); +	if (err) +		return err; + +	err = pci_enable_device(pdev); +	if (err) +		return err; + +	pci_restore_state(pdev); +	/* +	 * Suspend/Resume resets the PCI configuration space, so we have to +	 * re-disable the RETRY_TIMEOUT register (0x41) to keep +	 * PCI Tx retries from interfering with C3 CPU state +	 */ +	pci_write_config_byte(pdev, 0x41, 0); + +	ath5k_init(sc); +	if (test_bit(ATH_STAT_LEDSOFT, sc->status)) { +		ath5k_hw_set_gpio_output(sc->ah, sc->led_pin); +		ath5k_hw_set_gpio(sc->ah, sc->led_pin, 0); +	} + +	return 0; +} +#endif /* CONFIG_PM */ + + + +/***********************\ +* Driver Initialization * +\***********************/ + +static int +ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw) +{ +	struct ath5k_softc *sc = hw->priv; +	struct ath5k_hw *ah = sc->ah; +	u8 mac[ETH_ALEN]; +	unsigned int i; +	int ret; + +	ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "devid 0x%x\n", pdev->device); + +	/* +	 * Check if the MAC has multi-rate retry support. +	 * We do this by trying to setup a fake extended +	 * descriptor.  MAC's that don't have support will +	 * return false w/o doing anything.  MAC's that do +	 * support it will return true w/o doing anything. +	 */ +	if (ah->ah_setup_xtx_desc(ah, NULL, 0, 0, 0, 0, 0, 0)) +		__set_bit(ATH_STAT_MRRETRY, sc->status); + +	/* +	 * Reset the key cache since some parts do not +	 * reset the contents on initial power up. +	 */ +	for (i = 0; i < AR5K_KEYCACHE_SIZE; i++) +		ath5k_hw_reset_key(ah, i); + +	/* +	 * Collect the channel list.  The 802.11 layer +	 * is resposible for filtering this list based +	 * on settings like the phy mode and regulatory +	 * domain restrictions. +	 */ +	ret = ath5k_getchannels(hw); +	if (ret) { +		ATH5K_ERR(sc, "can't get channels\n"); +		goto err; +	} + +	/* NB: setup here so ath5k_rate_update is happy */ +	if (test_bit(MODE_IEEE80211A, ah->ah_modes)) +		ath5k_setcurmode(sc, MODE_IEEE80211A); +	else +		ath5k_setcurmode(sc, MODE_IEEE80211B); + +	/* +	 * Allocate tx+rx descriptors and populate the lists. +	 */ +	ret = ath5k_desc_alloc(sc, pdev); +	if (ret) { +		ATH5K_ERR(sc, "can't allocate descriptors\n"); +		goto err; +	} + +	/* +	 * Allocate hardware transmit queues: one queue for +	 * beacon frames and one data queue for each QoS +	 * priority.  Note that hw functions handle reseting +	 * these queues at the needed time. +	 */ +	ret = ath5k_beaconq_setup(ah); +	if (ret < 0) { +		ATH5K_ERR(sc, "can't setup a beacon xmit queue\n"); +		goto err_desc; +	} +	sc->bhalq = ret; + +	sc->txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BK); +	if (IS_ERR(sc->txq)) { +		ATH5K_ERR(sc, "can't setup xmit queue\n"); +		ret = PTR_ERR(sc->txq); +		goto err_bhal; +	} + +	tasklet_init(&sc->rxtq, ath5k_tasklet_rx, (unsigned long)sc); +	tasklet_init(&sc->txtq, ath5k_tasklet_tx, (unsigned long)sc); +	tasklet_init(&sc->restq, ath5k_tasklet_reset, (unsigned long)sc); +	setup_timer(&sc->calib_tim, ath5k_calibrate, (unsigned long)sc); +	setup_timer(&sc->led_tim, ath5k_led_off, (unsigned long)sc); + +	sc->led_on = 0; /* low true */ +	/* +	 * Auto-enable soft led processing for IBM cards and for +	 * 5211 minipci cards. +	 */ +	if (pdev->device == PCI_DEVICE_ID_ATHEROS_AR5212_IBM || +			pdev->device == PCI_DEVICE_ID_ATHEROS_AR5211) { +		__set_bit(ATH_STAT_LEDSOFT, sc->status); +		sc->led_pin = 0; +	} +	/* Enable softled on PIN1 on HP Compaq nc6xx, nc4000 & nx5000 laptops */ +	if (pdev->subsystem_vendor == PCI_VENDOR_ID_COMPAQ) { +		__set_bit(ATH_STAT_LEDSOFT, sc->status); +		sc->led_pin = 0; +	} +	if (test_bit(ATH_STAT_LEDSOFT, sc->status)) { +		ath5k_hw_set_gpio_output(ah, sc->led_pin); +		ath5k_hw_set_gpio(ah, sc->led_pin, !sc->led_on); +	} + +	ath5k_hw_get_lladdr(ah, mac); +	SET_IEEE80211_PERM_ADDR(hw, mac); +	/* All MAC address bits matter for ACKs */ +	memset(sc->bssidmask, 0xff, ETH_ALEN); +	ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask); + +	ret = ieee80211_register_hw(hw); +	if (ret) { +		ATH5K_ERR(sc, "can't register ieee80211 hw\n"); +		goto err_queues; +	} + +	return 0; +err_queues: +	ath5k_txq_release(sc); +err_bhal: +	ath5k_hw_release_tx_queue(ah, sc->bhalq); +err_desc: +	ath5k_desc_free(sc, pdev); +err: +	return ret; +} + +static void +ath5k_detach(struct pci_dev *pdev, struct ieee80211_hw *hw) +{ +	struct ath5k_softc *sc = hw->priv; + +	/* +	 * NB: the order of these is important: +	 * o call the 802.11 layer before detaching ath5k_hw to +	 *   insure callbacks into the driver to delete global +	 *   key cache entries can be handled +	 * o reclaim the tx queue data structures after calling +	 *   the 802.11 layer as we'll get called back to reclaim +	 *   node state and potentially want to use them +	 * o to cleanup the tx queues the hal is called, so detach +	 *   it last +	 * XXX: ??? detach ath5k_hw ??? +	 * Other than that, it's straightforward... +	 */ +	ieee80211_unregister_hw(hw); +	ath5k_desc_free(sc, pdev); +	ath5k_txq_release(sc); +	ath5k_hw_release_tx_queue(sc->ah, sc->bhalq); + +	/* +	 * NB: can't reclaim these until after ieee80211_ifdetach +	 * returns because we'll get called back to reclaim node +	 * state and potentially want to use them. +	 */ +} + + + + +/********************\ +* Channel/mode setup * +\********************/ + +/* + * Convert IEEE channel number to MHz frequency. + */ +static inline short +ath5k_ieee2mhz(short chan) +{ +	if (chan <= 14 || chan >= 27) +		return ieee80211chan2mhz(chan); +	else +		return 2212 + chan * 20; +} + +static unsigned int +ath5k_copy_rates(struct ieee80211_rate *rates, +		const struct ath5k_rate_table *rt, +		unsigned int max) +{ +	unsigned int i, count; + +	if (rt == NULL) +		return 0; + +	for (i = 0, count = 0; i < rt->rate_count && max > 0; i++) { +		if (!rt->rates[i].valid) +			continue; +		rates->rate = rt->rates[i].rate_kbps / 100; +		rates->val = rt->rates[i].rate_code; +		rates->flags = rt->rates[i].modulation; +		rates++; +		count++; +		max--; +	} + +	return count; +} + +static unsigned int +ath5k_copy_channels(struct ath5k_hw *ah, +		struct ieee80211_channel *channels, +		unsigned int mode, +		unsigned int max) +{ +	static const struct { unsigned int mode, mask, chan; } map[] = { +		[MODE_IEEE80211A] = { CHANNEL_OFDM, CHANNEL_OFDM | CHANNEL_TURBO, CHANNEL_A }, +		[MODE_ATHEROS_TURBO] = { CHANNEL_OFDM|CHANNEL_TURBO, CHANNEL_OFDM | CHANNEL_TURBO, CHANNEL_T }, +		[MODE_IEEE80211B] = { CHANNEL_CCK, CHANNEL_CCK, CHANNEL_B }, +		[MODE_IEEE80211G] = { CHANNEL_OFDM, CHANNEL_OFDM, CHANNEL_G }, +		[MODE_ATHEROS_TURBOG] = { CHANNEL_OFDM | CHANNEL_TURBO, CHANNEL_OFDM | CHANNEL_TURBO, CHANNEL_TG }, +	}; +	static const struct ath5k_regchannel chans_2ghz[] = +		IEEE80211_CHANNELS_2GHZ; +	static const struct ath5k_regchannel chans_5ghz[] = +		IEEE80211_CHANNELS_5GHZ; +	const struct ath5k_regchannel *chans; +	enum ath5k_regdom dmn; +	unsigned int i, count, size, chfreq, all, f, ch; + +	if (!test_bit(mode, ah->ah_modes)) +		return 0; + +	all = ah->ah_regdomain == DMN_DEFAULT || CHAN_DEBUG == 1; + +	switch (mode) { +	case MODE_IEEE80211A: +	case MODE_ATHEROS_TURBO: +		/* 1..220, but 2GHz frequencies are filtered by check_channel */ +		size = all ? 220 : ARRAY_SIZE(chans_5ghz); +		chans = chans_5ghz; +		dmn = ath5k_regdom2flag(ah->ah_regdomain, +				IEEE80211_CHANNELS_5GHZ_MIN); +		chfreq = CHANNEL_5GHZ; +		break; +	case MODE_IEEE80211B: +	case MODE_IEEE80211G: +	case MODE_ATHEROS_TURBOG: +		size = all ? 26 : ARRAY_SIZE(chans_2ghz); +		chans = chans_2ghz; +		dmn = ath5k_regdom2flag(ah->ah_regdomain, +				IEEE80211_CHANNELS_2GHZ_MIN); +		chfreq = CHANNEL_2GHZ; +		break; +	default: +		ATH5K_WARN(ah->ah_sc, "bad mode, not copying channels\n"); +		return 0; +	} + +	for (i = 0, count = 0; i < size && max > 0; i++) { +		ch = all ? i + 1 : chans[i].chan; +		f = ath5k_ieee2mhz(ch); +		/* Check if channel is supported by the chipset */ +		if (!ath5k_channel_ok(ah, f, chfreq)) +			continue; + +		/* Match regulation domain */ +		if (!all && !(IEEE80211_DMN(chans[i].domain) & +							IEEE80211_DMN(dmn))) +			continue; + +		if (!all && (chans[i].mode & map[mode].mask) != map[mode].mode) +			continue; + +		/* Write channel and increment counter */ +		channels->chan = ch; +		channels->freq = f; +		channels->val = map[mode].chan; +		channels++; +		count++; +		max--; +	} + +	return count; +} + +/* Only tries to register modes our EEPROM says it can support */ +#define REGISTER_MODE(m) do { \ +	ret = ath5k_register_mode(hw, m); \ +	if (ret) \ +		return ret; \ +} while (0) \ + +static inline int +ath5k_register_mode(struct ieee80211_hw *hw, u8 m) +{ +	struct ath5k_softc *sc = hw->priv; +	struct ieee80211_hw_mode *modes = sc->modes; +	unsigned int i; +	int ret; + +	if (!test_bit(m, sc->ah->ah_capabilities.cap_mode)) +		return 0; + +	for (i = 0; i < NUM_DRIVER_MODES; i++) { +		if (modes[i].mode != m || !modes[i].num_channels) +			continue; +		ret = ieee80211_register_hwmode(hw, &modes[i]); +		if (ret) { +			ATH5K_ERR(sc, "can't register hwmode %u\n", m); +			return ret; +		} +		return 0; +	} +	BUG(); +} + +static int +ath5k_getchannels(struct ieee80211_hw *hw) +{ +	struct ath5k_softc *sc = hw->priv; +	struct ath5k_hw *ah = sc->ah; +	struct ieee80211_hw_mode *modes = sc->modes; +	unsigned int i, max_r, max_c; +	int ret; + +	BUILD_BUG_ON(ARRAY_SIZE(sc->modes) < 3); + +	/* The order here does not matter */ +	modes[0].mode = MODE_IEEE80211G; +	modes[1].mode = MODE_IEEE80211B; +	modes[2].mode = MODE_IEEE80211A; + +	max_r = ARRAY_SIZE(sc->rates); +	max_c = ARRAY_SIZE(sc->channels); + +	for (i = 0; i < NUM_DRIVER_MODES; i++) { +		struct ieee80211_hw_mode *mode = &modes[i]; +		const struct ath5k_rate_table *hw_rates; + +		if (i == 0) { +			modes[0].rates	= sc->rates; +			modes->channels	= sc->channels; +		} else { +			struct ieee80211_hw_mode *prev_mode = &modes[i-1]; +			int prev_num_r	= prev_mode->num_rates; +			int prev_num_c	= prev_mode->num_channels; +			mode->rates	= &prev_mode->rates[prev_num_r]; +			mode->channels	= &prev_mode->channels[prev_num_c]; +		} + +		hw_rates = ath5k_hw_get_rate_table(ah, mode->mode); +		mode->num_rates    = ath5k_copy_rates(mode->rates, hw_rates, +			max_r); +		mode->num_channels = ath5k_copy_channels(ah, mode->channels, +			mode->mode, max_c); +		max_r -= mode->num_rates; +		max_c -= mode->num_channels; +	} + +	/* We try to register all modes this driver supports. We don't bother +	 * with MODE_IEEE80211B for AR5212 as MODE_IEEE80211G already accounts +	 * for that as per mac80211. Then, REGISTER_MODE() will will actually +	 * check the eeprom reading for more reliable capability information. +	 * Order matters here as per mac80211's latest preference. This will +	 * all hopefullly soon go away. */ + +	REGISTER_MODE(MODE_IEEE80211G); +	if (ah->ah_version != AR5K_AR5212) +		REGISTER_MODE(MODE_IEEE80211B); +	REGISTER_MODE(MODE_IEEE80211A); + +	ath5k_debug_dump_modes(sc, modes); + +	return ret; +} + +/* + * Set/change channels.  If the channel is really being changed, + * it's done by reseting the chip.  To accomplish this we must + * first cleanup any pending DMA, then restart stuff after a la + * ath5k_init. + */ +static int +ath5k_chan_set(struct ath5k_softc *sc, struct ieee80211_channel *chan) +{ +	struct ath5k_hw *ah = sc->ah; +	int ret; + +	ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "%u (%u MHz) -> %u (%u MHz)\n", +		sc->curchan->chan, sc->curchan->freq, +		chan->chan, chan->freq); + +	if (chan->freq != sc->curchan->freq || chan->val != sc->curchan->val) { +		/* +		 * To switch channels clear any pending DMA operations; +		 * wait long enough for the RX fifo to drain, reset the +		 * hardware at the new frequency, and then re-enable +		 * the relevant bits of the h/w. +		 */ +		ath5k_hw_set_intr(ah, 0);	/* disable interrupts */ +		ath5k_txq_cleanup(sc);		/* clear pending tx frames */ +		ath5k_rx_stop(sc);		/* turn off frame recv */ +		ret = ath5k_hw_reset(ah, sc->opmode, chan, true); +		if (ret) { +			ATH5K_ERR(sc, "%s: unable to reset channel %u " +				"(%u Mhz)\n", __func__, chan->chan, chan->freq); +			return ret; +		} +		sc->curchan = chan; +		ath5k_hw_set_txpower_limit(sc->ah, 0); + +		/* +		 * Re-enable rx framework. +		 */ +		ret = ath5k_rx_start(sc); +		if (ret) { +			ATH5K_ERR(sc, "%s: unable to restart recv logic\n", +					__func__); +			return ret; +		} + +		/* +		 * Change channels and update the h/w rate map +		 * if we're switching; e.g. 11a to 11b/g. +		 * +		 * XXX needed? +		 */ +/*		ath5k_chan_change(sc, chan); */ + +		ath5k_beacon_config(sc); +		/* +		 * Re-enable interrupts. +		 */ +		ath5k_hw_set_intr(ah, sc->imask); +	} + +	return 0; +} + +static void +ath5k_setcurmode(struct ath5k_softc *sc, unsigned int mode) +{ +	if (unlikely(test_bit(ATH_STAT_LEDSOFT, sc->status))) { +		/* from Atheros NDIS driver, w/ permission */ +		static const struct { +			u16 rate;	/* tx/rx 802.11 rate */ +			u16 timeOn;	/* LED on time (ms) */ +			u16 timeOff;	/* LED off time (ms) */ +		} blinkrates[] = { +			{ 108,  40,  10 }, +			{  96,  44,  11 }, +			{  72,  50,  13 }, +			{  48,  57,  14 }, +			{  36,  67,  16 }, +			{  24,  80,  20 }, +			{  22, 100,  25 }, +			{  18, 133,  34 }, +			{  12, 160,  40 }, +			{  10, 200,  50 }, +			{   6, 240,  58 }, +			{   4, 267,  66 }, +			{   2, 400, 100 }, +			{   0, 500, 130 } +		}; +		const struct ath5k_rate_table *rt = +				ath5k_hw_get_rate_table(sc->ah, mode); +		unsigned int i, j; + +		BUG_ON(rt == NULL); + +		memset(sc->hwmap, 0, sizeof(sc->hwmap)); +		for (i = 0; i < 32; i++) { +			u8 ix = rt->rate_code_to_index[i]; +			if (ix == 0xff) { +				sc->hwmap[i].ledon = msecs_to_jiffies(500); +				sc->hwmap[i].ledoff = msecs_to_jiffies(130); +				continue; +			} +			sc->hwmap[i].txflags = IEEE80211_RADIOTAP_F_DATAPAD; +			if (SHPREAMBLE_FLAG(ix) || rt->rates[ix].modulation == +					IEEE80211_RATE_OFDM) +				sc->hwmap[i].txflags |= +						IEEE80211_RADIOTAP_F_SHORTPRE; +			/* receive frames include FCS */ +			sc->hwmap[i].rxflags = sc->hwmap[i].txflags | +					IEEE80211_RADIOTAP_F_FCS; +			/* setup blink rate table to avoid per-packet lookup */ +			for (j = 0; j < ARRAY_SIZE(blinkrates) - 1; j++) +				if (blinkrates[j].rate == /* XXX why 7f? */ +						(rt->rates[ix].dot11_rate&0x7f)) +					break; + +			sc->hwmap[i].ledon = msecs_to_jiffies(blinkrates[j]. +					timeOn); +			sc->hwmap[i].ledoff = msecs_to_jiffies(blinkrates[j]. +					timeOff); +		} +	} + +	sc->curmode = mode; +} + +static void +ath5k_mode_setup(struct ath5k_softc *sc) +{ +	struct ath5k_hw *ah = sc->ah; +	u32 rfilt; + +	/* configure rx filter */ +	rfilt = sc->filter_flags; +	ath5k_hw_set_rx_filter(ah, rfilt); + +	if (ath5k_hw_hasbssidmask(ah)) +		ath5k_hw_set_bssid_mask(ah, sc->bssidmask); + +	/* configure operational mode */ +	ath5k_hw_set_opmode(ah); + +	ath5k_hw_set_mcast_filter(ah, 0, 0); +	ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "RX filter 0x%x\n", rfilt); +} + + + + +/***************\ +* Buffers setup * +\***************/ + +static int +ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) +{ +	struct ath5k_hw *ah = sc->ah; +	struct sk_buff *skb = bf->skb; +	struct ath5k_desc *ds; + +	if (likely(skb == NULL)) { +		unsigned int off; + +		/* +		 * Allocate buffer with headroom_needed space for the +		 * fake physical layer header at the start. +		 */ +		skb = dev_alloc_skb(sc->rxbufsize + sc->cachelsz - 1); +		if (unlikely(skb == NULL)) { +			ATH5K_ERR(sc, "can't alloc skbuff of size %u\n", +					sc->rxbufsize + sc->cachelsz - 1); +			return -ENOMEM; +		} +		/* +		 * Cache-line-align.  This is important (for the +		 * 5210 at least) as not doing so causes bogus data +		 * in rx'd frames. +		 */ +		off = ((unsigned long)skb->data) % sc->cachelsz; +		if (off != 0) +			skb_reserve(skb, sc->cachelsz - off); + +		bf->skb = skb; +		bf->skbaddr = pci_map_single(sc->pdev, +			skb->data, sc->rxbufsize, PCI_DMA_FROMDEVICE); +		if (unlikely(pci_dma_mapping_error(bf->skbaddr))) { +			ATH5K_ERR(sc, "%s: DMA mapping failed\n", __func__); +			dev_kfree_skb(skb); +			bf->skb = NULL; +			return -ENOMEM; +		} +	} + +	/* +	 * Setup descriptors.  For receive we always terminate +	 * the descriptor list with a self-linked entry so we'll +	 * not get overrun under high load (as can happen with a +	 * 5212 when ANI processing enables PHY error frames). +	 * +	 * To insure the last descriptor is self-linked we create +	 * each descriptor as self-linked and add it to the end.  As +	 * each additional descriptor is added the previous self-linked +	 * entry is ``fixed'' naturally.  This should be safe even +	 * if DMA is happening.  When processing RX interrupts we +	 * never remove/process the last, self-linked, entry on the +	 * descriptor list.  This insures the hardware always has +	 * someplace to write a new frame. +	 */ +	ds = bf->desc; +	ds->ds_link = bf->daddr;	/* link to self */ +	ds->ds_data = bf->skbaddr; +	ath5k_hw_setup_rx_desc(ah, ds, +		skb_tailroom(skb),	/* buffer size */ +		0); + +	if (sc->rxlink != NULL) +		*sc->rxlink = bf->daddr; +	sc->rxlink = &ds->ds_link; +	return 0; +} + +static int +ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf, +		struct ieee80211_tx_control *ctl) +{ +	struct ath5k_hw *ah = sc->ah; +	struct ath5k_txq *txq = sc->txq; +	struct ath5k_desc *ds = bf->desc; +	struct sk_buff *skb = bf->skb; +	unsigned int pktlen, flags, keyidx = AR5K_TXKEYIX_INVALID; +	int ret; + +	flags = AR5K_TXDESC_INTREQ | AR5K_TXDESC_CLRDMASK; +	bf->ctl = *ctl; +	/* XXX endianness */ +	bf->skbaddr = pci_map_single(sc->pdev, skb->data, skb->len, +			PCI_DMA_TODEVICE); + +	if (ctl->flags & IEEE80211_TXCTL_NO_ACK) +		flags |= AR5K_TXDESC_NOACK; + +	pktlen = skb->len + FCS_LEN; + +	if (!(ctl->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT)) { +		keyidx = ctl->key_idx; +		pktlen += ctl->icv_len; +	} + +	ret = ah->ah_setup_tx_desc(ah, ds, pktlen, +		ieee80211_get_hdrlen_from_skb(skb), AR5K_PKT_TYPE_NORMAL, +		(ctl->power_level * 2), ctl->tx_rate, ctl->retry_limit, keyidx, 0, flags, 0, 0); +	if (ret) +		goto err_unmap; + +	ds->ds_link = 0; +	ds->ds_data = bf->skbaddr; + +	spin_lock_bh(&txq->lock); +	list_add_tail(&bf->list, &txq->q); +	sc->tx_stats.data[txq->qnum].len++; +	if (txq->link == NULL) /* is this first packet? */ +		ath5k_hw_put_tx_buf(ah, txq->qnum, bf->daddr); +	else /* no, so only link it */ +		*txq->link = bf->daddr; + +	txq->link = &ds->ds_link; +	ath5k_hw_tx_start(ah, txq->qnum); +	spin_unlock_bh(&txq->lock); + +	return 0; +err_unmap: +	pci_unmap_single(sc->pdev, bf->skbaddr, skb->len, PCI_DMA_TODEVICE); +	return ret; +} + +/*******************\ +* Descriptors setup * +\*******************/ + +static int +ath5k_desc_alloc(struct ath5k_softc *sc, struct pci_dev *pdev) +{ +	struct ath5k_desc *ds; +	struct ath5k_buf *bf; +	dma_addr_t da; +	unsigned int i; +	int ret; + +	/* allocate descriptors */ +	sc->desc_len = sizeof(struct ath5k_desc) * +			(ATH_TXBUF + ATH_RXBUF + ATH_BCBUF + 1); +	sc->desc = pci_alloc_consistent(pdev, sc->desc_len, &sc->desc_daddr); +	if (sc->desc == NULL) { +		ATH5K_ERR(sc, "can't allocate descriptors\n"); +		ret = -ENOMEM; +		goto err; +	} +	ds = sc->desc; +	da = sc->desc_daddr; +	ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "DMA map: %p (%zu) -> %llx\n", +		ds, sc->desc_len, (unsigned long long)sc->desc_daddr); + +	bf = kcalloc(1 + ATH_TXBUF + ATH_RXBUF + ATH_BCBUF, +			sizeof(struct ath5k_buf), GFP_KERNEL); +	if (bf == NULL) { +		ATH5K_ERR(sc, "can't allocate bufptr\n"); +		ret = -ENOMEM; +		goto err_free; +	} +	sc->bufptr = bf; + +	INIT_LIST_HEAD(&sc->rxbuf); +	for (i = 0; i < ATH_RXBUF; i++, bf++, ds++, da += sizeof(*ds)) { +		bf->desc = ds; +		bf->daddr = da; +		list_add_tail(&bf->list, &sc->rxbuf); +	} + +	INIT_LIST_HEAD(&sc->txbuf); +	sc->txbuf_len = ATH_TXBUF; +	for (i = 0; i < ATH_TXBUF; i++, bf++, ds++, +			da += sizeof(*ds)) { +		bf->desc = ds; +		bf->daddr = da; +		list_add_tail(&bf->list, &sc->txbuf); +	} + +	/* beacon buffer */ +	bf->desc = ds; +	bf->daddr = da; +	sc->bbuf = bf; + +	return 0; +err_free: +	pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr); +err: +	sc->desc = NULL; +	return ret; +} + +static void +ath5k_desc_free(struct ath5k_softc *sc, struct pci_dev *pdev) +{ +	struct ath5k_buf *bf; + +	ath5k_txbuf_free(sc, sc->bbuf); +	list_for_each_entry(bf, &sc->txbuf, list) +		ath5k_txbuf_free(sc, bf); +	list_for_each_entry(bf, &sc->rxbuf, list) +		ath5k_txbuf_free(sc, bf); + +	/* Free memory associated with all descriptors */ +	pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr); + +	kfree(sc->bufptr); +	sc->bufptr = NULL; +} + + + + + +/**************\ +* Queues setup * +\**************/ + +static struct ath5k_txq * +ath5k_txq_setup(struct ath5k_softc *sc, +		int qtype, int subtype) +{ +	struct ath5k_hw *ah = sc->ah; +	struct ath5k_txq *txq; +	struct ath5k_txq_info qi = { +		.tqi_subtype = subtype, +		.tqi_aifs = AR5K_TXQ_USEDEFAULT, +		.tqi_cw_min = AR5K_TXQ_USEDEFAULT, +		.tqi_cw_max = AR5K_TXQ_USEDEFAULT +	}; +	int qnum; + +	/* +	 * Enable interrupts only for EOL and DESC conditions. +	 * We mark tx descriptors to receive a DESC interrupt +	 * when a tx queue gets deep; otherwise waiting for the +	 * EOL to reap descriptors.  Note that this is done to +	 * reduce interrupt load and this only defers reaping +	 * descriptors, never transmitting frames.  Aside from +	 * reducing interrupts this also permits more concurrency. +	 * The only potential downside is if the tx queue backs +	 * up in which case the top half of the kernel may backup +	 * due to a lack of tx descriptors. +	 */ +	qi.tqi_flags = AR5K_TXQ_FLAG_TXEOLINT_ENABLE | +				AR5K_TXQ_FLAG_TXDESCINT_ENABLE; +	qnum = ath5k_hw_setup_tx_queue(ah, qtype, &qi); +	if (qnum < 0) { +		/* +		 * NB: don't print a message, this happens +		 * normally on parts with too few tx queues +		 */ +		return ERR_PTR(qnum); +	} +	if (qnum >= ARRAY_SIZE(sc->txqs)) { +		ATH5K_ERR(sc, "hw qnum %u out of range, max %tu!\n", +			qnum, ARRAY_SIZE(sc->txqs)); +		ath5k_hw_release_tx_queue(ah, qnum); +		return ERR_PTR(-EINVAL); +	} +	txq = &sc->txqs[qnum]; +	if (!txq->setup) { +		txq->qnum = qnum; +		txq->link = NULL; +		INIT_LIST_HEAD(&txq->q); +		spin_lock_init(&txq->lock); +		txq->setup = true; +	} +	return &sc->txqs[qnum]; +} + +static int +ath5k_beaconq_setup(struct ath5k_hw *ah) +{ +	struct ath5k_txq_info qi = { +		.tqi_aifs = AR5K_TXQ_USEDEFAULT, +		.tqi_cw_min = AR5K_TXQ_USEDEFAULT, +		.tqi_cw_max = AR5K_TXQ_USEDEFAULT, +		/* NB: for dynamic turbo, don't enable any other interrupts */ +		.tqi_flags = AR5K_TXQ_FLAG_TXDESCINT_ENABLE +	}; + +	return ath5k_hw_setup_tx_queue(ah, AR5K_TX_QUEUE_BEACON, &qi); +} + +static int +ath5k_beaconq_config(struct ath5k_softc *sc) +{ +	struct ath5k_hw *ah = sc->ah; +	struct ath5k_txq_info qi; +	int ret; + +	ret = ath5k_hw_get_tx_queueprops(ah, sc->bhalq, &qi); +	if (ret) +		return ret; +	if (sc->opmode == IEEE80211_IF_TYPE_AP || +	    sc->opmode == IEEE80211_IF_TYPE_IBSS) { +		/* +		 * Always burst out beacon and CAB traffic +		 * (aifs = cwmin = cwmax = 0) +		 */ +		qi.tqi_aifs = 0; +		qi.tqi_cw_min = 0; +		qi.tqi_cw_max = 0; +	} + +	ret = ath5k_hw_setup_tx_queueprops(ah, sc->bhalq, &qi); +	if (ret) { +		ATH5K_ERR(sc, "%s: unable to update parameters for beacon " +			"hardware queue!\n", __func__); +		return ret; +	} + +	return ath5k_hw_reset_tx_queue(ah, sc->bhalq); /* push to h/w */; +} + +static void +ath5k_txq_drainq(struct ath5k_softc *sc, struct ath5k_txq *txq) +{ +	struct ath5k_buf *bf, *bf0; + +	/* +	 * NB: this assumes output has been stopped and +	 *     we do not need to block ath5k_tx_tasklet +	 */ +	spin_lock_bh(&txq->lock); +	list_for_each_entry_safe(bf, bf0, &txq->q, list) { +		ath5k_debug_printtxbuf(sc, bf, !sc->ah->ah_proc_tx_desc(sc->ah, +					bf->desc)); + +		ath5k_txbuf_free(sc, bf); + +		spin_lock_bh(&sc->txbuflock); +		sc->tx_stats.data[txq->qnum].len--; +		list_move_tail(&bf->list, &sc->txbuf); +		sc->txbuf_len++; +		spin_unlock_bh(&sc->txbuflock); +	} +	txq->link = NULL; +	spin_unlock_bh(&txq->lock); +} + +/* + * Drain the transmit queues and reclaim resources. + */ +static void +ath5k_txq_cleanup(struct ath5k_softc *sc) +{ +	struct ath5k_hw *ah = sc->ah; +	unsigned int i; + +	/* XXX return value */ +	if (likely(!test_bit(ATH_STAT_INVALID, sc->status))) { +		/* don't touch the hardware if marked invalid */ +		ath5k_hw_stop_tx_dma(ah, sc->bhalq); +		ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "beacon queue %x\n", +			ath5k_hw_get_tx_buf(ah, sc->bhalq)); +		for (i = 0; i < ARRAY_SIZE(sc->txqs); i++) +			if (sc->txqs[i].setup) { +				ath5k_hw_stop_tx_dma(ah, sc->txqs[i].qnum); +				ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "txq [%u] %x, " +					"link %p\n", +					sc->txqs[i].qnum, +					ath5k_hw_get_tx_buf(ah, +							sc->txqs[i].qnum), +					sc->txqs[i].link); +			} +	} +	ieee80211_start_queues(sc->hw); /* XXX move to callers */ + +	for (i = 0; i < ARRAY_SIZE(sc->txqs); i++) +		if (sc->txqs[i].setup) +			ath5k_txq_drainq(sc, &sc->txqs[i]); +} + +static void +ath5k_txq_release(struct ath5k_softc *sc) +{ +	struct ath5k_txq *txq = sc->txqs; +	unsigned int i; + +	for (i = 0; i < ARRAY_SIZE(sc->txqs); i++, txq++) +		if (txq->setup) { +			ath5k_hw_release_tx_queue(sc->ah, txq->qnum); +			txq->setup = false; +		} +} + + + + +/*************\ +* RX Handling * +\*************/ + +/* + * Enable the receive h/w following a reset. + */ +static int +ath5k_rx_start(struct ath5k_softc *sc) +{ +	struct ath5k_hw *ah = sc->ah; +	struct ath5k_buf *bf; +	int ret; + +	sc->rxbufsize = roundup(IEEE80211_MAX_LEN, sc->cachelsz); + +	ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "cachelsz %u rxbufsize %u\n", +		sc->cachelsz, sc->rxbufsize); + +	sc->rxlink = NULL; + +	spin_lock_bh(&sc->rxbuflock); +	list_for_each_entry(bf, &sc->rxbuf, list) { +		ret = ath5k_rxbuf_setup(sc, bf); +		if (ret != 0) { +			spin_unlock_bh(&sc->rxbuflock); +			goto err; +		} +	} +	bf = list_first_entry(&sc->rxbuf, struct ath5k_buf, list); +	spin_unlock_bh(&sc->rxbuflock); + +	ath5k_hw_put_rx_buf(ah, bf->daddr); +	ath5k_hw_start_rx(ah);		/* enable recv descriptors */ +	ath5k_mode_setup(sc);		/* set filters, etc. */ +	ath5k_hw_start_rx_pcu(ah);	/* re-enable PCU/DMA engine */ + +	return 0; +err: +	return ret; +} + +/* + * Disable the receive h/w in preparation for a reset. + */ +static void +ath5k_rx_stop(struct ath5k_softc *sc) +{ +	struct ath5k_hw *ah = sc->ah; + +	ath5k_hw_stop_pcu_recv(ah);	/* disable PCU */ +	ath5k_hw_set_rx_filter(ah, 0);	/* clear recv filter */ +	ath5k_hw_stop_rx_dma(ah);	/* disable DMA engine */ +	mdelay(3);			/* 3ms is long enough for 1 frame */ + +	ath5k_debug_printrxbuffs(sc, ah); + +	sc->rxlink = NULL;		/* just in case */ +} + +static unsigned int +ath5k_rx_decrypted(struct ath5k_softc *sc, struct ath5k_desc *ds, +		struct sk_buff *skb) +{ +	struct ieee80211_hdr *hdr = (void *)skb->data; +	unsigned int keyix, hlen = ieee80211_get_hdrlen_from_skb(skb); + +	if (!(ds->ds_rxstat.rs_status & AR5K_RXERR_DECRYPT) && +			ds->ds_rxstat.rs_keyix != AR5K_RXKEYIX_INVALID) +		return RX_FLAG_DECRYPTED; + +	/* Apparently when a default key is used to decrypt the packet +	   the hw does not set the index used to decrypt.  In such cases +	   get the index from the packet. */ +	if ((le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_PROTECTED) && +			!(ds->ds_rxstat.rs_status & AR5K_RXERR_DECRYPT) && +			skb->len >= hlen + 4) { +		keyix = skb->data[hlen + 3] >> 6; + +		if (test_bit(keyix, sc->keymap)) +			return RX_FLAG_DECRYPTED; +	} + +	return 0; +} + +static void +ath5k_tasklet_rx(unsigned long data) +{ +	struct ieee80211_rx_status rxs = {}; +	struct sk_buff *skb; +	struct ath5k_softc *sc = (void *)data; +	struct ath5k_buf *bf; +	struct ath5k_desc *ds; +	u16 len; +	u8 stat; +	int ret; +	int hdrlen; +	int pad; + +	spin_lock(&sc->rxbuflock); +	do { +		if (unlikely(list_empty(&sc->rxbuf))) { +			ATH5K_WARN(sc, "empty rx buf pool\n"); +			break; +		} +		bf = list_first_entry(&sc->rxbuf, struct ath5k_buf, list); +		BUG_ON(bf->skb == NULL); +		skb = bf->skb; +		ds = bf->desc; + +		/* TODO only one segment */ +		pci_dma_sync_single_for_cpu(sc->pdev, sc->desc_daddr, +				sc->desc_len, PCI_DMA_FROMDEVICE); + +		if (unlikely(ds->ds_link == bf->daddr)) /* this is the end */ +			break; + +		ret = sc->ah->ah_proc_rx_desc(sc->ah, ds); +		if (unlikely(ret == -EINPROGRESS)) +			break; +		else if (unlikely(ret)) { +			ATH5K_ERR(sc, "error in processing rx descriptor\n"); +			return; +		} + +		if (unlikely(ds->ds_rxstat.rs_more)) { +			ATH5K_WARN(sc, "unsupported jumbo\n"); +			goto next; +		} + +		stat = ds->ds_rxstat.rs_status; +		if (unlikely(stat)) { +			if (stat & AR5K_RXERR_PHY) +				goto next; +			if (stat & AR5K_RXERR_DECRYPT) { +				/* +				 * Decrypt error.  If the error occurred +				 * because there was no hardware key, then +				 * let the frame through so the upper layers +				 * can process it.  This is necessary for 5210 +				 * parts which have no way to setup a ``clear'' +				 * key cache entry. +				 * +				 * XXX do key cache faulting +				 */ +				if (ds->ds_rxstat.rs_keyix == +						AR5K_RXKEYIX_INVALID && +						!(stat & AR5K_RXERR_CRC)) +					goto accept; +			} +			if (stat & AR5K_RXERR_MIC) { +				rxs.flag |= RX_FLAG_MMIC_ERROR; +				goto accept; +			} + +			/* let crypto-error packets fall through in MNTR */ +			if ((stat & ~(AR5K_RXERR_DECRYPT|AR5K_RXERR_MIC)) || +					sc->opmode != IEEE80211_IF_TYPE_MNTR) +				goto next; +		} +accept: +		len = ds->ds_rxstat.rs_datalen; +		pci_dma_sync_single_for_cpu(sc->pdev, bf->skbaddr, len, +				PCI_DMA_FROMDEVICE); +		pci_unmap_single(sc->pdev, bf->skbaddr, sc->rxbufsize, +				PCI_DMA_FROMDEVICE); +		bf->skb = NULL; + +		skb_put(skb, len); + +		/* +		 * the hardware adds a padding to 4 byte boundaries between +		 * the header and the payload data if the header length is +		 * not multiples of 4 - remove it +		 */ +		hdrlen = ieee80211_get_hdrlen_from_skb(skb); +		if (hdrlen & 3) { +			pad = hdrlen % 4; +			memmove(skb->data + pad, skb->data, hdrlen); +			skb_pull(skb, pad); +		} + +		if (sc->opmode == IEEE80211_IF_TYPE_MNTR) +			rxs.mactime = ath5k_extend_tsf(sc->ah, +					ds->ds_rxstat.rs_tstamp); +		else +			rxs.mactime = ds->ds_rxstat.rs_tstamp; +		rxs.freq = sc->curchan->freq; +		rxs.channel = sc->curchan->chan; +		rxs.phymode = sc->curmode; + +		/* +		 * signal quality: +		 * the names here are misleading and the usage of these +		 * values by iwconfig makes it even worse +		 */ +		/* noise floor in dBm, from the last noise calibration */ +		rxs.noise = sc->ah->ah_noise_floor; +		/* signal level in dBm */ +		rxs.ssi = rxs.noise + ds->ds_rxstat.rs_rssi; +		/* +		 * "signal" is actually displayed as Link Quality by iwconfig +		 * we provide a percentage based on rssi (assuming max rssi 64) +		 */ +		rxs.signal = ds->ds_rxstat.rs_rssi * 100 / 64; + +		rxs.antenna = ds->ds_rxstat.rs_antenna; +		rxs.rate = ds->ds_rxstat.rs_rate; +		rxs.flag |= ath5k_rx_decrypted(sc, ds, skb); + +		ath5k_debug_dump_skb(sc, skb, "RX  ", 0); + +		__ieee80211_rx(sc->hw, skb, &rxs); +		sc->led_rxrate = ds->ds_rxstat.rs_rate; +		ath5k_led_event(sc, ATH_LED_RX); +next: +		list_move_tail(&bf->list, &sc->rxbuf); +	} while (ath5k_rxbuf_setup(sc, bf) == 0); +	spin_unlock(&sc->rxbuflock); +} + + + + +/*************\ +* TX Handling * +\*************/ + +static void +ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq) +{ +	struct ieee80211_tx_status txs = {}; +	struct ath5k_buf *bf, *bf0; +	struct ath5k_desc *ds; +	struct sk_buff *skb; +	int ret; + +	spin_lock(&txq->lock); +	list_for_each_entry_safe(bf, bf0, &txq->q, list) { +		ds = bf->desc; + +		/* TODO only one segment */ +		pci_dma_sync_single_for_cpu(sc->pdev, sc->desc_daddr, +				sc->desc_len, PCI_DMA_FROMDEVICE); +		ret = sc->ah->ah_proc_tx_desc(sc->ah, ds); +		if (unlikely(ret == -EINPROGRESS)) +			break; +		else if (unlikely(ret)) { +			ATH5K_ERR(sc, "error %d while processing queue %u\n", +				ret, txq->qnum); +			break; +		} + +		skb = bf->skb; +		bf->skb = NULL; +		pci_unmap_single(sc->pdev, bf->skbaddr, skb->len, +				PCI_DMA_TODEVICE); + +		txs.control = bf->ctl; +		txs.retry_count = ds->ds_txstat.ts_shortretry + +			ds->ds_txstat.ts_longretry / 6; +		if (unlikely(ds->ds_txstat.ts_status)) { +			sc->ll_stats.dot11ACKFailureCount++; +			if (ds->ds_txstat.ts_status & AR5K_TXERR_XRETRY) +				txs.excessive_retries = 1; +			else if (ds->ds_txstat.ts_status & AR5K_TXERR_FILT) +				txs.flags |= IEEE80211_TX_STATUS_TX_FILTERED; +		} else { +			txs.flags |= IEEE80211_TX_STATUS_ACK; +			txs.ack_signal = ds->ds_txstat.ts_rssi; +		} + +		ieee80211_tx_status(sc->hw, skb, &txs); +		sc->tx_stats.data[txq->qnum].count++; + +		spin_lock(&sc->txbuflock); +		sc->tx_stats.data[txq->qnum].len--; +		list_move_tail(&bf->list, &sc->txbuf); +		sc->txbuf_len++; +		spin_unlock(&sc->txbuflock); +	} +	if (likely(list_empty(&txq->q))) +		txq->link = NULL; +	spin_unlock(&txq->lock); +	if (sc->txbuf_len > ATH_TXBUF / 5) +		ieee80211_wake_queues(sc->hw); +} + +static void +ath5k_tasklet_tx(unsigned long data) +{ +	struct ath5k_softc *sc = (void *)data; + +	ath5k_tx_processq(sc, sc->txq); + +	ath5k_led_event(sc, ATH_LED_TX); +} + + + + +/*****************\ +* Beacon handling * +\*****************/ + +/* + * Setup the beacon frame for transmit. + */ +static int +ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf, +		struct ieee80211_tx_control *ctl) +{ +	struct sk_buff *skb = bf->skb; +	struct ath5k_hw *ah = sc->ah; +	struct ath5k_desc *ds; +	int ret, antenna = 0; +	u32 flags; + +	bf->skbaddr = pci_map_single(sc->pdev, skb->data, skb->len, +			PCI_DMA_TODEVICE); +	ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, "skb %p [data %p len %u] " +			"skbaddr %llx\n", skb, skb->data, skb->len, +			(unsigned long long)bf->skbaddr); +	if (pci_dma_mapping_error(bf->skbaddr)) { +		ATH5K_ERR(sc, "beacon DMA mapping failed\n"); +		return -EIO; +	} + +	ds = bf->desc; + +	flags = AR5K_TXDESC_NOACK; +	if (sc->opmode == IEEE80211_IF_TYPE_IBSS && ath5k_hw_hasveol(ah)) { +		ds->ds_link = bf->daddr;	/* self-linked */ +		flags |= AR5K_TXDESC_VEOL; +		/* +		 * Let hardware handle antenna switching if txantenna is not set +		 */ +	} else { +		ds->ds_link = 0; +		/* +		 * Switch antenna every 4 beacons if txantenna is not set +		 * XXX assumes two antennas +		 */ +		if (antenna == 0) +			antenna = sc->bsent & 4 ? 2 : 1; +	} + +	ds->ds_data = bf->skbaddr; +	ret = ah->ah_setup_tx_desc(ah, ds, skb->len + FCS_LEN, +			ieee80211_get_hdrlen_from_skb(skb), +			AR5K_PKT_TYPE_BEACON, (ctl->power_level * 2), ctl->tx_rate, 1, +			AR5K_TXKEYIX_INVALID, antenna, flags, 0, 0); +	if (ret) +		goto err_unmap; + +	return 0; +err_unmap: +	pci_unmap_single(sc->pdev, bf->skbaddr, skb->len, PCI_DMA_TODEVICE); +	return ret; +} + +/* + * Transmit a beacon frame at SWBA.  Dynamic updates to the + * frame contents are done as needed and the slot time is + * also adjusted based on current state. + * + * this is usually called from interrupt context (ath5k_intr()) + * but also from ath5k_beacon_config() in IBSS mode which in turn + * can be called from a tasklet and user context + */ +static void +ath5k_beacon_send(struct ath5k_softc *sc) +{ +	struct ath5k_buf *bf = sc->bbuf; +	struct ath5k_hw *ah = sc->ah; + +	ATH5K_DBG(sc, ATH5K_DEBUG_BEACON_PROC, "in beacon_send\n"); + +	if (unlikely(bf->skb == NULL || sc->opmode == IEEE80211_IF_TYPE_STA || +			sc->opmode == IEEE80211_IF_TYPE_MNTR)) { +		ATH5K_WARN(sc, "bf=%p bf_skb=%p\n", bf, bf ? bf->skb : NULL); +		return; +	} +	/* +	 * Check if the previous beacon has gone out.  If +	 * not don't don't try to post another, skip this +	 * period and wait for the next.  Missed beacons +	 * indicate a problem and should not occur.  If we +	 * miss too many consecutive beacons reset the device. +	 */ +	if (unlikely(ath5k_hw_num_tx_pending(ah, sc->bhalq) != 0)) { +		sc->bmisscount++; +		ATH5K_DBG(sc, ATH5K_DEBUG_BEACON_PROC, +			"missed %u consecutive beacons\n", sc->bmisscount); +		if (sc->bmisscount > 3) {		/* NB: 3 is a guess */ +			ATH5K_DBG(sc, ATH5K_DEBUG_BEACON_PROC, +				"stuck beacon time (%u missed)\n", +				sc->bmisscount); +			tasklet_schedule(&sc->restq); +		} +		return; +	} +	if (unlikely(sc->bmisscount != 0)) { +		ATH5K_DBG(sc, ATH5K_DEBUG_BEACON_PROC, +			"resume beacon xmit after %u misses\n", +			sc->bmisscount); +		sc->bmisscount = 0; +	} + +	/* +	 * Stop any current dma and put the new frame on the queue. +	 * This should never fail since we check above that no frames +	 * are still pending on the queue. +	 */ +	if (unlikely(ath5k_hw_stop_tx_dma(ah, sc->bhalq))) { +		ATH5K_WARN(sc, "beacon queue %u didn't stop?\n", sc->bhalq); +		/* NB: hw still stops DMA, so proceed */ +	} +	pci_dma_sync_single_for_cpu(sc->pdev, bf->skbaddr, bf->skb->len, +			PCI_DMA_TODEVICE); + +	ath5k_hw_put_tx_buf(ah, sc->bhalq, bf->daddr); +	ath5k_hw_tx_start(ah, sc->bhalq); +	ATH5K_DBG(sc, ATH5K_DEBUG_BEACON_PROC, "TXDP[%u] = %llx (%p)\n", +		sc->bhalq, (unsigned long long)bf->daddr, bf->desc); + +	sc->bsent++; +} + + +static void +ath5k_beacon_update_timers(struct ath5k_softc *sc) +{ +	struct ath5k_hw *ah = sc->ah; +	u32 uninitialized_var(nexttbtt), intval, tsftu; +	u64 tsf; + +	intval = sc->bintval & AR5K_BEACON_PERIOD; +	if (WARN_ON(!intval)) +		return; + +	/* current TSF converted to TU */ +	tsf = ath5k_hw_get_tsf64(ah); +	tsftu = TSF_TO_TU(tsf); + +	/* +	 * Pull nexttbtt forward to reflect the current +	 * TSF. Add one intval otherwise the timespan +	 * can be too short for ibss merges. +	 */ +	nexttbtt = tsftu + 2 * intval; + +	ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, +		"hw tsftu %u nexttbtt %u intval %u\n", tsftu, nexttbtt, intval); + +	intval |= AR5K_BEACON_ENA; + +	ath5k_hw_init_beacon(ah, nexttbtt, intval); +} + + +/* + * Configure the beacon timers and interrupts based on the operating mode + * + * When operating in station mode we want to receive a BMISS interrupt when we + * stop seeing beacons from the AP we've associated with so we can look for + * another AP to associate with. + * + * In IBSS mode we need to configure the beacon timers and use a self-linked tx + * descriptor if possible. If the hardware cannot deal with that we enable SWBA + * interrupts to send the beacons from the interrupt handler. + */ +static void +ath5k_beacon_config(struct ath5k_softc *sc) +{ +	struct ath5k_hw *ah = sc->ah; + +	ath5k_hw_set_intr(ah, 0); +	sc->bmisscount = 0; + +	if (sc->opmode == IEEE80211_IF_TYPE_STA) { +		sc->imask |= AR5K_INT_BMISS; +	} else if (sc->opmode == IEEE80211_IF_TYPE_IBSS) { +		/* +		 * In IBSS mode enable the beacon timers but only enable SWBA +		 * interrupts if we need to manually prepare beacon frames. +		 * Otherwise we use a self-linked tx descriptor and let the +		 * hardware deal with things. In that case we have to load it +		 * only once here. +		 */ +		ath5k_beaconq_config(sc); +		ath5k_beacon_update_timers(sc); + +		if (!ath5k_hw_hasveol(ah)) +			sc->imask |= AR5K_INT_SWBA; +		else +			ath5k_beacon_send(sc); +	} +	/* TODO else AP */ + +	ath5k_hw_set_intr(ah, sc->imask); +} + + +/********************\ +* Interrupt handling * +\********************/ + +static int +ath5k_init(struct ath5k_softc *sc) +{ +	int ret; + +	mutex_lock(&sc->lock); + +	ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "mode %d\n", sc->opmode); + +	/* +	 * Stop anything previously setup.  This is safe +	 * no matter this is the first time through or not. +	 */ +	ath5k_stop_locked(sc); + +	/* +	 * The basic interface to setting the hardware in a good +	 * state is ``reset''.  On return the hardware is known to +	 * be powered up and with interrupts disabled.  This must +	 * be followed by initialization of the appropriate bits +	 * and then setup of the interrupt mask. +	 */ +	sc->curchan = sc->hw->conf.chan; +	ret = ath5k_hw_reset(sc->ah, sc->opmode, sc->curchan, false); +	if (ret) { +		ATH5K_ERR(sc, "unable to reset hardware: %d\n", ret); +		goto done; +	} +	/* +	 * This is needed only to setup initial state +	 * but it's best done after a reset. +	 */ +	ath5k_hw_set_txpower_limit(sc->ah, 0); + +	/* +	 * Setup the hardware after reset: the key cache +	 * is filled as needed and the receive engine is +	 * set going.  Frame transmit is handled entirely +	 * in the frame output path; there's nothing to do +	 * here except setup the interrupt mask. +	 */ +	ret = ath5k_rx_start(sc); +	if (ret) +		goto done; + +	/* +	 * Enable interrupts. +	 */ +	sc->imask = AR5K_INT_RX | AR5K_INT_TX | AR5K_INT_RXEOL | +		AR5K_INT_RXORN | AR5K_INT_FATAL | AR5K_INT_GLOBAL; + +	ath5k_hw_set_intr(sc->ah, sc->imask); +	/* Set ack to be sent at low bit-rates */ +	ath5k_hw_set_ack_bitrate_high(sc->ah, false); + +	mod_timer(&sc->calib_tim, round_jiffies(jiffies + +			msecs_to_jiffies(ath5k_calinterval * 1000))); + +	ret = 0; +done: +	mutex_unlock(&sc->lock); +	return ret; +} + +static int +ath5k_stop_locked(struct ath5k_softc *sc) +{ +	struct ath5k_hw *ah = sc->ah; + +	ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "invalid %u\n", +			test_bit(ATH_STAT_INVALID, sc->status)); + +	/* +	 * Shutdown the hardware and driver: +	 *    stop output from above +	 *    disable interrupts +	 *    turn off timers +	 *    turn off the radio +	 *    clear transmit machinery +	 *    clear receive machinery +	 *    drain and release tx queues +	 *    reclaim beacon resources +	 *    power down hardware +	 * +	 * Note that some of this work is not possible if the +	 * hardware is gone (invalid). +	 */ +	ieee80211_stop_queues(sc->hw); + +	if (!test_bit(ATH_STAT_INVALID, sc->status)) { +		if (test_bit(ATH_STAT_LEDSOFT, sc->status)) { +			del_timer_sync(&sc->led_tim); +			ath5k_hw_set_gpio(ah, sc->led_pin, !sc->led_on); +			__clear_bit(ATH_STAT_LEDBLINKING, sc->status); +		} +		ath5k_hw_set_intr(ah, 0); +	} +	ath5k_txq_cleanup(sc); +	if (!test_bit(ATH_STAT_INVALID, sc->status)) { +		ath5k_rx_stop(sc); +		ath5k_hw_phy_disable(ah); +	} else +		sc->rxlink = NULL; + +	return 0; +} + +/* + * Stop the device, grabbing the top-level lock to protect + * against concurrent entry through ath5k_init (which can happen + * if another thread does a system call and the thread doing the + * stop is preempted). + */ +static int +ath5k_stop_hw(struct ath5k_softc *sc) +{ +	int ret; + +	mutex_lock(&sc->lock); +	ret = ath5k_stop_locked(sc); +	if (ret == 0 && !test_bit(ATH_STAT_INVALID, sc->status)) { +		/* +		 * Set the chip in full sleep mode.  Note that we are +		 * careful to do this only when bringing the interface +		 * completely to a stop.  When the chip is in this state +		 * it must be carefully woken up or references to +		 * registers in the PCI clock domain may freeze the bus +		 * (and system).  This varies by chip and is mostly an +		 * issue with newer parts that go to sleep more quickly. +		 */ +		if (sc->ah->ah_mac_srev >= 0x78) { +			/* +			 * XXX +			 * don't put newer MAC revisions > 7.8 to sleep because +			 * of the above mentioned problems +			 */ +			ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "mac version > 7.8, " +				"not putting device to sleep\n"); +		} else { +			ATH5K_DBG(sc, ATH5K_DEBUG_RESET, +				"putting device to full sleep\n"); +			ath5k_hw_set_power(sc->ah, AR5K_PM_FULL_SLEEP, true, 0); +		} +	} +	ath5k_txbuf_free(sc, sc->bbuf); +	mutex_unlock(&sc->lock); + +	del_timer_sync(&sc->calib_tim); + +	return ret; +} + +static irqreturn_t +ath5k_intr(int irq, void *dev_id) +{ +	struct ath5k_softc *sc = dev_id; +	struct ath5k_hw *ah = sc->ah; +	enum ath5k_int status; +	unsigned int counter = 1000; + +	if (unlikely(test_bit(ATH_STAT_INVALID, sc->status) || +				!ath5k_hw_is_intr_pending(ah))) +		return IRQ_NONE; + +	do { +		/* +		 * Figure out the reason(s) for the interrupt.  Note +		 * that get_isr returns a pseudo-ISR that may include +		 * bits we haven't explicitly enabled so we mask the +		 * value to insure we only process bits we requested. +		 */ +		ath5k_hw_get_isr(ah, &status);		/* NB: clears IRQ too */ +		ATH5K_DBG(sc, ATH5K_DEBUG_INTR, "status 0x%x/0x%x\n", +				status, sc->imask); +		status &= sc->imask; /* discard unasked for bits */ +		if (unlikely(status & AR5K_INT_FATAL)) { +			/* +			 * Fatal errors are unrecoverable. +			 * Typically these are caused by DMA errors. +			 */ +			tasklet_schedule(&sc->restq); +		} else if (unlikely(status & AR5K_INT_RXORN)) { +			tasklet_schedule(&sc->restq); +		} else { +			if (status & AR5K_INT_SWBA) { +				/* +				* Software beacon alert--time to send a beacon. +				* Handle beacon transmission directly; deferring +				* this is too slow to meet timing constraints +				* under load. +				*/ +				ath5k_beacon_send(sc); +			} +			if (status & AR5K_INT_RXEOL) { +				/* +				* NB: the hardware should re-read the link when +				*     RXE bit is written, but it doesn't work at +				*     least on older hardware revs. +				*/ +				sc->rxlink = NULL; +			} +			if (status & AR5K_INT_TXURN) { +				/* bump tx trigger level */ +				ath5k_hw_update_tx_triglevel(ah, true); +			} +			if (status & AR5K_INT_RX) +				tasklet_schedule(&sc->rxtq); +			if (status & AR5K_INT_TX) +				tasklet_schedule(&sc->txtq); +			if (status & AR5K_INT_BMISS) { +			} +			if (status & AR5K_INT_MIB) { +				/* TODO */ +			} +		} +	} while (ath5k_hw_is_intr_pending(ah) && counter-- > 0); + +	if (unlikely(!counter)) +		ATH5K_WARN(sc, "too many interrupts, giving up for now\n"); + +	return IRQ_HANDLED; +} + +static void +ath5k_tasklet_reset(unsigned long data) +{ +	struct ath5k_softc *sc = (void *)data; + +	ath5k_reset(sc->hw); +} + +/* + * Periodically recalibrate the PHY to account + * for temperature/environment changes. + */ +static void +ath5k_calibrate(unsigned long data) +{ +	struct ath5k_softc *sc = (void *)data; +	struct ath5k_hw *ah = sc->ah; + +	ATH5K_DBG(sc, ATH5K_DEBUG_CALIBRATE, "channel %u/%x\n", +		sc->curchan->chan, sc->curchan->val); + +	if (ath5k_hw_get_rf_gain(ah) == AR5K_RFGAIN_NEED_CHANGE) { +		/* +		 * Rfgain is out of bounds, reset the chip +		 * to load new gain values. +		 */ +		ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "calibration, resetting\n"); +		ath5k_reset(sc->hw); +	} +	if (ath5k_hw_phy_calibrate(ah, sc->curchan)) +		ATH5K_ERR(sc, "calibration of channel %u failed\n", +				sc->curchan->chan); + +	mod_timer(&sc->calib_tim, round_jiffies(jiffies + +			msecs_to_jiffies(ath5k_calinterval * 1000))); +} + + + +/***************\ +* LED functions * +\***************/ + +static void +ath5k_led_off(unsigned long data) +{ +	struct ath5k_softc *sc = (void *)data; + +	if (test_bit(ATH_STAT_LEDENDBLINK, sc->status)) +		__clear_bit(ATH_STAT_LEDBLINKING, sc->status); +	else { +		__set_bit(ATH_STAT_LEDENDBLINK, sc->status); +		ath5k_hw_set_gpio(sc->ah, sc->led_pin, !sc->led_on); +		mod_timer(&sc->led_tim, jiffies + sc->led_off); +	} +} + +/* + * Blink the LED according to the specified on/off times. + */ +static void +ath5k_led_blink(struct ath5k_softc *sc, unsigned int on, +		unsigned int off) +{ +	ATH5K_DBG(sc, ATH5K_DEBUG_LED, "on %u off %u\n", on, off); +	ath5k_hw_set_gpio(sc->ah, sc->led_pin, sc->led_on); +	__set_bit(ATH_STAT_LEDBLINKING, sc->status); +	__clear_bit(ATH_STAT_LEDENDBLINK, sc->status); +	sc->led_off = off; +	mod_timer(&sc->led_tim, jiffies + on); +} + +static void +ath5k_led_event(struct ath5k_softc *sc, int event) +{ +	if (likely(!test_bit(ATH_STAT_LEDSOFT, sc->status))) +		return; +	if (unlikely(test_bit(ATH_STAT_LEDBLINKING, sc->status))) +		return; /* don't interrupt active blink */ +	switch (event) { +	case ATH_LED_TX: +		ath5k_led_blink(sc, sc->hwmap[sc->led_txrate].ledon, +			sc->hwmap[sc->led_txrate].ledoff); +		break; +	case ATH_LED_RX: +		ath5k_led_blink(sc, sc->hwmap[sc->led_rxrate].ledon, +			sc->hwmap[sc->led_rxrate].ledoff); +		break; +	} +} + + + + +/********************\ +* Mac80211 functions * +\********************/ + +static int +ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb, +			struct ieee80211_tx_control *ctl) +{ +	struct ath5k_softc *sc = hw->priv; +	struct ath5k_buf *bf; +	unsigned long flags; +	int hdrlen; +	int pad; + +	ath5k_debug_dump_skb(sc, skb, "TX  ", 1); + +	if (sc->opmode == IEEE80211_IF_TYPE_MNTR) +		ATH5K_DBG(sc, ATH5K_DEBUG_XMIT, "tx in monitor (scan?)\n"); + +	/* +	 * the hardware expects the header padded to 4 byte boundaries +	 * if this is not the case we add the padding after the header +	 */ +	hdrlen = ieee80211_get_hdrlen_from_skb(skb); +	if (hdrlen & 3) { +		pad = hdrlen % 4; +		if (skb_headroom(skb) < pad) { +			ATH5K_ERR(sc, "tx hdrlen not %%4: %d not enough" +				" headroom to pad %d\n", hdrlen, pad); +			return -1; +		} +		skb_push(skb, pad); +		memmove(skb->data, skb->data+pad, hdrlen); +	} + +	sc->led_txrate = ctl->tx_rate; + +	spin_lock_irqsave(&sc->txbuflock, flags); +	if (list_empty(&sc->txbuf)) { +		ATH5K_ERR(sc, "no further txbuf available, dropping packet\n"); +		spin_unlock_irqrestore(&sc->txbuflock, flags); +		ieee80211_stop_queue(hw, ctl->queue); +		return -1; +	} +	bf = list_first_entry(&sc->txbuf, struct ath5k_buf, list); +	list_del(&bf->list); +	sc->txbuf_len--; +	if (list_empty(&sc->txbuf)) +		ieee80211_stop_queues(hw); +	spin_unlock_irqrestore(&sc->txbuflock, flags); + +	bf->skb = skb; + +	if (ath5k_txbuf_setup(sc, bf, ctl)) { +		bf->skb = NULL; +		spin_lock_irqsave(&sc->txbuflock, flags); +		list_add_tail(&bf->list, &sc->txbuf); +		sc->txbuf_len++; +		spin_unlock_irqrestore(&sc->txbuflock, flags); +		dev_kfree_skb_any(skb); +		return 0; +	} + +	return 0; +} + +static int +ath5k_reset(struct ieee80211_hw *hw) +{ +	struct ath5k_softc *sc = hw->priv; +	struct ath5k_hw *ah = sc->ah; +	int ret; + +	ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "resetting\n"); +	/* +	 * Convert to a hw channel description with the flags +	 * constrained to reflect the current operating mode. +	 */ +	sc->curchan = hw->conf.chan; + +	ath5k_hw_set_intr(ah, 0); +	ath5k_txq_cleanup(sc); +	ath5k_rx_stop(sc); + +	ret = ath5k_hw_reset(ah, sc->opmode, sc->curchan, true); +	if (unlikely(ret)) { +		ATH5K_ERR(sc, "can't reset hardware (%d)\n", ret); +		goto err; +	} +	ath5k_hw_set_txpower_limit(sc->ah, 0); + +	ret = ath5k_rx_start(sc); +	if (unlikely(ret)) { +		ATH5K_ERR(sc, "can't start recv logic\n"); +		goto err; +	} +	/* +	 * We may be doing a reset in response to an ioctl +	 * that changes the channel so update any state that +	 * might change as a result. +	 * +	 * XXX needed? +	 */ +/*	ath5k_chan_change(sc, c); */ +	ath5k_beacon_config(sc); +	/* intrs are started by ath5k_beacon_config */ + +	ieee80211_wake_queues(hw); + +	return 0; +err: +	return ret; +} + +static int ath5k_start(struct ieee80211_hw *hw) +{ +	return ath5k_init(hw->priv); +} + +static void ath5k_stop(struct ieee80211_hw *hw) +{ +	ath5k_stop_hw(hw->priv); +} + +static int ath5k_add_interface(struct ieee80211_hw *hw, +		struct ieee80211_if_init_conf *conf) +{ +	struct ath5k_softc *sc = hw->priv; +	int ret; + +	mutex_lock(&sc->lock); +	if (sc->iface_id) { +		ret = 0; +		goto end; +	} + +	sc->iface_id = conf->if_id; + +	switch (conf->type) { +	case IEEE80211_IF_TYPE_STA: +	case IEEE80211_IF_TYPE_IBSS: +	case IEEE80211_IF_TYPE_MNTR: +		sc->opmode = conf->type; +		break; +	default: +		ret = -EOPNOTSUPP; +		goto end; +	} +	ret = 0; +end: +	mutex_unlock(&sc->lock); +	return ret; +} + +static void +ath5k_remove_interface(struct ieee80211_hw *hw, +			struct ieee80211_if_init_conf *conf) +{ +	struct ath5k_softc *sc = hw->priv; + +	mutex_lock(&sc->lock); +	if (sc->iface_id != conf->if_id) +		goto end; + +	sc->iface_id = 0; +end: +	mutex_unlock(&sc->lock); +} + +static int +ath5k_config(struct ieee80211_hw *hw, +			struct ieee80211_conf *conf) +{ +	struct ath5k_softc *sc = hw->priv; + +	sc->bintval = conf->beacon_int * 1000 / 1024; +	ath5k_setcurmode(sc, conf->phymode); + +	return ath5k_chan_set(sc, conf->chan); +} + +static int +ath5k_config_interface(struct ieee80211_hw *hw, int if_id, +			struct ieee80211_if_conf *conf) +{ +	struct ath5k_softc *sc = hw->priv; +	struct ath5k_hw *ah = sc->ah; +	int ret; + +	/* Set to a reasonable value. Note that this will +	 * be set to mac80211's value at ath5k_config(). */ +	sc->bintval = 1000 * 1000 / 1024; +	mutex_lock(&sc->lock); +	if (sc->iface_id != if_id) { +		ret = -EIO; +		goto unlock; +	} +	if (conf->bssid) { +		/* Cache for later use during resets */ +		memcpy(ah->ah_bssid, conf->bssid, ETH_ALEN); +		/* XXX: assoc id is set to 0 for now, mac80211 doesn't have +		 * a clean way of letting us retrieve this yet. */ +		ath5k_hw_set_associd(ah, ah->ah_bssid, 0); +	} +	mutex_unlock(&sc->lock); + +	return ath5k_reset(hw); +unlock: +	mutex_unlock(&sc->lock); +	return ret; +} + +#define SUPPORTED_FIF_FLAGS \ +	FIF_PROMISC_IN_BSS |  FIF_ALLMULTI | FIF_FCSFAIL | \ +	FIF_PLCPFAIL | FIF_CONTROL | FIF_OTHER_BSS | \ +	FIF_BCN_PRBRESP_PROMISC +/* + * o always accept unicast, broadcast, and multicast traffic + * o multicast traffic for all BSSIDs will be enabled if mac80211 + *   says it should be + * o maintain current state of phy ofdm or phy cck error reception. + *   If the hardware detects any of these type of errors then + *   ath5k_hw_get_rx_filter() will pass to us the respective + *   hardware filters to be able to receive these type of frames. + * o probe request frames are accepted only when operating in + *   hostap, adhoc, or monitor modes + * o enable promiscuous mode according to the interface state + * o accept beacons: + *   - when operating in adhoc mode so the 802.11 layer creates + *     node table entries for peers, + *   - when operating in station mode for collecting rssi data when + *     the station is otherwise quiet, or + *   - when scanning + */ +static void ath5k_configure_filter(struct ieee80211_hw *hw, +		unsigned int changed_flags, +		unsigned int *new_flags, +		int mc_count, struct dev_mc_list *mclist) +{ +	struct ath5k_softc *sc = hw->priv; +	struct ath5k_hw *ah = sc->ah; +	u32 mfilt[2], val, rfilt; +	u8 pos; +	int i; + +	mfilt[0] = 0; +	mfilt[1] = 0; + +	/* Only deal with supported flags */ +	changed_flags &= SUPPORTED_FIF_FLAGS; +	*new_flags &= SUPPORTED_FIF_FLAGS; + +	/* If HW detects any phy or radar errors, leave those filters on. +	 * Also, always enable Unicast, Broadcasts and Multicast +	 * XXX: move unicast, bssid broadcasts and multicast to mac80211 */ +	rfilt = (ath5k_hw_get_rx_filter(ah) & (AR5K_RX_FILTER_PHYERR)) | +		(AR5K_RX_FILTER_UCAST | AR5K_RX_FILTER_BCAST | +		AR5K_RX_FILTER_MCAST); + +	if (changed_flags & (FIF_PROMISC_IN_BSS | FIF_OTHER_BSS)) { +		if (*new_flags & FIF_PROMISC_IN_BSS) { +			rfilt |= AR5K_RX_FILTER_PROM; +			__set_bit(ATH_STAT_PROMISC, sc->status); +		} +		else +			__clear_bit(ATH_STAT_PROMISC, sc->status); +	} + +	/* Note, AR5K_RX_FILTER_MCAST is already enabled */ +	if (*new_flags & FIF_ALLMULTI) { +		mfilt[0] =  ~0; +		mfilt[1] =  ~0; +	} else { +		for (i = 0; i < mc_count; i++) { +			if (!mclist) +				break; +			/* calculate XOR of eight 6-bit values */ +			val = LE_READ_4(mclist->dmi_addr + 0); +			pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; +			val = LE_READ_4(mclist->dmi_addr + 3); +			pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; +			pos &= 0x3f; +			mfilt[pos / 32] |= (1 << (pos % 32)); +			/* XXX: we might be able to just do this instead, +			* but not sure, needs testing, if we do use this we'd +			* neet to inform below to not reset the mcast */ +			/* ath5k_hw_set_mcast_filterindex(ah, +			 *      mclist->dmi_addr[5]); */ +			mclist = mclist->next; +		} +	} + +	/* This is the best we can do */ +	if (*new_flags & (FIF_FCSFAIL | FIF_PLCPFAIL)) +		rfilt |= AR5K_RX_FILTER_PHYERR; + +	/* FIF_BCN_PRBRESP_PROMISC really means to enable beacons +	* and probes for any BSSID, this needs testing */ +	if (*new_flags & FIF_BCN_PRBRESP_PROMISC) +		rfilt |= AR5K_RX_FILTER_BEACON | AR5K_RX_FILTER_PROBEREQ; + +	/* FIF_CONTROL doc says that if FIF_PROMISC_IN_BSS is not +	 * set we should only pass on control frames for this +	 * station. This needs testing. I believe right now this +	 * enables *all* control frames, which is OK.. but +	 * but we should see if we can improve on granularity */ +	if (*new_flags & FIF_CONTROL) +		rfilt |= AR5K_RX_FILTER_CONTROL; + +	/* Additional settings per mode -- this is per ath5k */ + +	/* XXX move these to mac80211, and add a beacon IFF flag to mac80211 */ + +	if (sc->opmode == IEEE80211_IF_TYPE_MNTR) +		rfilt |= AR5K_RX_FILTER_CONTROL | AR5K_RX_FILTER_BEACON | +			AR5K_RX_FILTER_PROBEREQ | AR5K_RX_FILTER_PROM; +	if (sc->opmode != IEEE80211_IF_TYPE_STA) +		rfilt |= AR5K_RX_FILTER_PROBEREQ; +	if (sc->opmode != IEEE80211_IF_TYPE_AP && +		test_bit(ATH_STAT_PROMISC, sc->status)) +		rfilt |= AR5K_RX_FILTER_PROM; +	if (sc->opmode == IEEE80211_IF_TYPE_STA || +		sc->opmode == IEEE80211_IF_TYPE_IBSS) { +		rfilt |= AR5K_RX_FILTER_BEACON; +	} + +	/* Set filters */ +	ath5k_hw_set_rx_filter(ah,rfilt); + +	/* Set multicast bits */ +	ath5k_hw_set_mcast_filter(ah, mfilt[0], mfilt[1]); +	/* Set the cached hw filter flags, this will alter actually +	 * be set in HW */ +	sc->filter_flags = rfilt; +} + +static int +ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, +		const u8 *local_addr, const u8 *addr, +		struct ieee80211_key_conf *key) +{ +	struct ath5k_softc *sc = hw->priv; +	int ret = 0; + +	switch(key->alg) { +	case ALG_WEP: +		break; +	case ALG_TKIP: +	case ALG_CCMP: +		return -EOPNOTSUPP; +	default: +		WARN_ON(1); +		return -EINVAL; +	} + +	mutex_lock(&sc->lock); + +	switch (cmd) { +	case SET_KEY: +		ret = ath5k_hw_set_key(sc->ah, key->keyidx, key, addr); +		if (ret) { +			ATH5K_ERR(sc, "can't set the key\n"); +			goto unlock; +		} +		__set_bit(key->keyidx, sc->keymap); +		key->hw_key_idx = key->keyidx; +		break; +	case DISABLE_KEY: +		ath5k_hw_reset_key(sc->ah, key->keyidx); +		__clear_bit(key->keyidx, sc->keymap); +		break; +	default: +		ret = -EINVAL; +		goto unlock; +	} + +unlock: +	mutex_unlock(&sc->lock); +	return ret; +} + +static int +ath5k_get_stats(struct ieee80211_hw *hw, +		struct ieee80211_low_level_stats *stats) +{ +	struct ath5k_softc *sc = hw->priv; + +	memcpy(stats, &sc->ll_stats, sizeof(sc->ll_stats)); + +	return 0; +} + +static int +ath5k_get_tx_stats(struct ieee80211_hw *hw, +		struct ieee80211_tx_queue_stats *stats) +{ +	struct ath5k_softc *sc = hw->priv; + +	memcpy(stats, &sc->tx_stats, sizeof(sc->tx_stats)); + +	return 0; +} + +static u64 +ath5k_get_tsf(struct ieee80211_hw *hw) +{ +	struct ath5k_softc *sc = hw->priv; + +	return ath5k_hw_get_tsf64(sc->ah); +} + +static void +ath5k_reset_tsf(struct ieee80211_hw *hw) +{ +	struct ath5k_softc *sc = hw->priv; + +	ath5k_hw_reset_tsf(sc->ah); +} + +static int +ath5k_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb, +			struct ieee80211_tx_control *ctl) +{ +	struct ath5k_softc *sc = hw->priv; +	int ret; + +	ath5k_debug_dump_skb(sc, skb, "BC  ", 1); + +	mutex_lock(&sc->lock); + +	if (sc->opmode != IEEE80211_IF_TYPE_IBSS) { +		ret = -EIO; +		goto end; +	} + +	ath5k_txbuf_free(sc, sc->bbuf); +	sc->bbuf->skb = skb; +	ret = ath5k_beacon_setup(sc, sc->bbuf, ctl); +	if (ret) +		sc->bbuf->skb = NULL; +	else +		ath5k_beacon_config(sc); + +end: +	mutex_unlock(&sc->lock); +	return ret; +} + diff --git a/drivers/net/wireless/ath5k/base.h b/drivers/net/wireless/ath5k/base.h new file mode 100644 index 000000000000..927d67db3dc2 --- /dev/null +++ b/drivers/net/wireless/ath5k/base.h @@ -0,0 +1,178 @@ +/*- + * 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 "ath5k.h" +#include "debug.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; +	unsigned int		flags;	/* tx descriptor flags */ +	struct ath5k_desc	*desc;	/* virtual addr of desc */ +	dma_addr_t		daddr;	/* physical addr of desc */ +	struct sk_buff		*skb;	/* skbuff for buf */ +	dma_addr_t		skbaddr;/* physical addr of skb data */ +	struct ieee80211_tx_control ctl; +}; + +/* + * 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; +}; + +#if CHAN_DEBUG +#define ATH_CHAN_MAX	(26+26+26+200+200) +#else +#define ATH_CHAN_MAX	(14+14+14+252+20)	/* XXX what's the max? */ +#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 */ +	void __iomem		*iobase;	/* address of the device */ +	struct mutex		lock;		/* dev-level lock */ +	struct ieee80211_tx_queue_stats tx_stats; +	struct ieee80211_low_level_stats ll_stats; +	struct ieee80211_hw	*hw;		/* IEEE 802.11 common */ +	struct ieee80211_hw_mode modes[NUM_DRIVER_MODES]; +	struct ieee80211_channel channels[ATH_CHAN_MAX]; +	struct ieee80211_rate	rates[AR5K_MAX_RATES * NUM_DRIVER_MODES]; +	enum ieee80211_if_types	opmode; +	struct ath5k_hw		*ah;		/* Atheros HW */ + +#if ATH5K_DEBUG +	struct ath5k_dbg_info	debug;		/* debug info */ +#endif + +	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 */ +	u16			cachelsz;	/* cache line size */ + +	DECLARE_BITMAP(status, 6); +#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_LEDBLINKING	3		/* LED blink operation active */ +#define ATH_STAT_LEDENDBLINK	4		/* finish LED blink operation */ +#define ATH_STAT_LEDSOFT	5		/* enable LED gpio status */ + +	unsigned int		filter_flags;	/* HW flags, AR5K_RX_FILTER_* */ +	unsigned int		curmode;	/* current phy mode */ +	struct ieee80211_channel *curchan;	/* current h/w channel */ + +	int 			iface_id;	/* add/remove_interface id */ + +	struct { +		u8	rxflags;	/* radiotap rx flags */ +		u8	txflags;	/* radiotap tx flags */ +		u16	ledon;		/* softled on time */ +		u16	ledoff;		/* softled off time */ +	} hwmap[32];				/* h/w rate ix mappings */ + +	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 */ +				led_off;	/* off time for current blink */ +	struct timer_list	led_tim;	/* led off timer */ +	u8			led_rxrate;	/* current rx rate for LED */ +	u8			led_txrate;	/* current tx rate for LED */ + +	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 list_head	txbuf;		/* transmit buffer */ +	spinlock_t		txbuflock; +	unsigned int		txbuf_len;	/* buf count in txbuf list */ +	struct ath5k_txq	txqs[2];	/* beacon and tx */ + +	struct ath5k_txq	*txq;		/* beacon and tx*/ +	struct tasklet_struct	txtq;		/* tx intr tasklet */ + +	struct ath5k_buf	*bbuf;		/* beacon buffer */ +	unsigned int		bhalq,		/* SW q for outgoing beacons */ +				bmisscount,	/* missed beacon transmits */ +				bintval,	/* beacon interval */ +				bsent; + +	struct timer_list	calib_tim;	/* calibration timer */ +}; + +#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) + +#endif diff --git a/drivers/net/wireless/ath5k/debug.c b/drivers/net/wireless/ath5k/debug.c new file mode 100644 index 000000000000..4ba649e20269 --- /dev/null +++ b/drivers/net/wireless/ath5k/debug.c @@ -0,0 +1,469 @@ +/* + * 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. + */ + +#include "debug.h" +#include "base.h" + +static unsigned int ath5k_debug; +module_param_named(debug, ath5k_debug, uint, 0); + + +#if 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 { +	char *name; +	int addr; +}; + +#define REG_STRUCT_INIT(r) { #r, r } + +/* just a few random registers, might want to add more */ +static 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_CPCORN), +	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) ? ®s[*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) ? ®s[*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 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, ®ister_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: TSF */ + +static ssize_t read_file_tsf(struct file *file, char __user *user_buf, +				   size_t count, loff_t *ppos) +{ +	struct ath5k_softc *sc = file->private_data; +	char buf[100]; +	snprintf(buf, 100, "0x%016llx\n", ath5k_hw_get_tsf64(sc->ah)); +	return simple_read_from_buffer(user_buf, count, ppos, buf, 19); +} + +static ssize_t write_file_tsf(struct file *file, +				 const char __user *userbuf, +				 size_t count, loff_t *ppos) +{ +	struct ath5k_softc *sc = file->private_data; +	if (strncmp(userbuf, "reset", 5) == 0) { +		ath5k_hw_reset_tsf(sc->ah); +		printk(KERN_INFO "debugfs reset TSF\n"); +	} +	return count; +} + +static const struct file_operations fops_tsf = { +	.read = read_file_tsf, +	.write = write_file_tsf, +	.open = ath5k_debugfs_open, +	.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[1000]; +	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", 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; + +	if (strncmp(userbuf, "disable", 7) == 0) { +		AR5K_REG_DISABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE); +		printk(KERN_INFO "debugfs disable beacons\n"); +	} else if (strncmp(userbuf, "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, +}; + + +/* 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_u32("debug", +			0666, sc->debug.debugfs_phydir, &sc->debug.level); + +	sc->debug.debugfs_registers = debugfs_create_file("registers", 0444, +				sc->debug.debugfs_phydir, +				sc, &fops_registers); + +	sc->debug.debugfs_tsf = debugfs_create_file("tsf", 0666, +				sc->debug.debugfs_phydir, +				sc, &fops_tsf); + +	sc->debug.debugfs_beacon = debugfs_create_file("beacon", 0666, +				sc->debug.debugfs_phydir, +				sc, &fops_beacon); + +	sc->debug.debugfs_reset = debugfs_create_file("reset", 0222, +				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_tsf); +	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_modes(struct ath5k_softc *sc, struct ieee80211_hw_mode *modes) +{ +	unsigned int m, i; + +	if (likely(!(sc->debug.level & ATH5K_DEBUG_DUMPMODES))) +		return; + +	for (m = 0; m < NUM_DRIVER_MODES; m++) { +		printk(KERN_DEBUG "Mode %u: channels %d, rates %d\n", m, +				modes[m].num_channels, modes[m].num_rates); +		printk(KERN_DEBUG " channels:\n"); +		for (i = 0; i < modes[m].num_channels; i++) +			printk(KERN_DEBUG "  %3d %d %.4x %.4x\n", +					modes[m].channels[i].chan, +					modes[m].channels[i].freq, +					modes[m].channels[i].val, +					modes[m].channels[i].flag); +		printk(KERN_DEBUG " rates:\n"); +		for (i = 0; i < modes[m].num_rates; i++) +			printk(KERN_DEBUG "  %4d %.4x %.4x %.4x\n", +					modes[m].rates[i].rate, +					modes[m].rates[i].val, +					modes[m].rates[i].flags, +					modes[m].rates[i].val2); +	} +} + +static inline void +ath5k_debug_printrxbuf(struct ath5k_buf *bf, int done) +{ +	struct ath5k_desc *ds = bf->desc; + +	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, ds->ds_ctl0, ds->ds_ctl1, +		ds->ds_hw[0], ds->ds_hw[1], +		!done ? ' ' : (ds->ds_rxstat.rs_status == 0) ? '*' : '!'); +} + +void +ath5k_debug_printrxbuffs(struct ath5k_softc *sc, struct ath5k_hw *ah) +{ +	struct ath5k_desc *ds; +	struct ath5k_buf *bf; +	int status; + +	if (likely(!(sc->debug.level & +	    (ATH5K_DEBUG_RESET | ATH5K_DEBUG_FATAL)))) +		return; + +	printk(KERN_DEBUG "rx queue %x, link %p\n", +		ath5k_hw_get_rx_buf(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); +		if (!status || (sc->debug.level & ATH5K_DEBUG_FATAL)) +			ath5k_debug_printrxbuf(bf, status == 0); +	} +	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, int done) +{ +	struct ath5k_desc *ds = bf->desc; + +	if (likely(!(sc->debug.level & ATH5K_DEBUG_RESET))) +		return; + +	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, ds->ds_ctl0, ds->ds_ctl1, +		ds->ds_hw[0], ds->ds_hw[1], ds->ds_hw[2], ds->ds_hw[3], +		!done ? ' ' : (ds->ds_txstat.ts_status == 0) ? '*' : '!'); +} + +#endif /* if ATH5K_DEBUG */ diff --git a/drivers/net/wireless/ath5k/debug.h b/drivers/net/wireless/ath5k/debug.h new file mode 100644 index 000000000000..2b491cbc8c80 --- /dev/null +++ b/drivers/net/wireless/ath5k/debug.h @@ -0,0 +1,216 @@ +/* + * 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 + +/* set this to 1 for debugging output */ +#ifndef ATH5K_DEBUG +#define ATH5K_DEBUG	0 +#endif + +struct ath5k_softc; +struct ath5k_hw; +struct ieee80211_hw_mode; +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_tsf; +	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_BEACON_PROC: beacon ISR proc + * @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_DUMPMODES: dump modes + * @ATH5K_DEBUG_TRACE: trace function calls + * @ATH5K_DEBUG_FATAL: fatal errors + * @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_BEACON_PROC	= 0x00000020, +	ATH5K_DEBUG_CALIBRATE	= 0x00000100, +	ATH5K_DEBUG_TXPOWER	= 0x00000200, +	ATH5K_DEBUG_LED		= 0x00000400, +	ATH5K_DEBUG_DUMP_RX	= 0x00001000, +	ATH5K_DEBUG_DUMP_TX	= 0x00002000, +	ATH5K_DEBUG_DUMPMODES	= 0x00004000, +	ATH5K_DEBUG_TRACE	= 0x00010000, +	ATH5K_DEBUG_FATAL	= 0x80000000, +	ATH5K_DEBUG_ANY		= 0xffffffff +}; + +#if 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_modes(struct ath5k_softc *sc, +			struct ieee80211_hw_mode *modes); + +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, int done); + +#else /* no debugging */ + +#define ATH5K_TRACE(_sc) /* empty */ + +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_modes(struct ath5k_softc *sc, +			struct ieee80211_hw_mode *modes) {} + +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, int done) {} + +#endif /* if ATH5K_DEBUG */ + +#endif /* ifndef _ATH5K_DEBUG_H */ diff --git a/drivers/net/wireless/ath5k/hw.c b/drivers/net/wireless/ath5k/hw.c new file mode 100644 index 000000000000..5623d7dc738e --- /dev/null +++ b/drivers/net/wireless/ath5k/hw.c @@ -0,0 +1,4349 @@ + /* + * Copyright (c) 2004-2007 Reyk Floeter <reyk@openbsd.org> + * Copyright (c) 2006-2007 Nick Kossifidis <mickflemm@gmail.com> + * Copyright (c) 2007 Matthew W. S. Bell  <mentor@madwifi.org> + * Copyright (c) 2007 Luis Rodriguez <mcgrof@winlab.rutgers.edu> + * Copyright (c) 2007 Pavel Roskin <proski@gnu.org> + * Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +/* + * HW related functions for Atheros Wireless LAN devices. + */ + +#include <linux/pci.h> +#include <linux/delay.h> + +#include "reg.h" +#include "base.h" +#include "debug.h" + +/*Rate tables*/ +static const struct ath5k_rate_table ath5k_rt_11a = AR5K_RATES_11A; +static const struct ath5k_rate_table ath5k_rt_11b = AR5K_RATES_11B; +static const struct ath5k_rate_table ath5k_rt_11g = AR5K_RATES_11G; +static const struct ath5k_rate_table ath5k_rt_turbo = AR5K_RATES_TURBO; +static const struct ath5k_rate_table ath5k_rt_xr = AR5K_RATES_XR; + +/*Prototypes*/ +static int ath5k_hw_nic_reset(struct ath5k_hw *, u32); +static int ath5k_hw_nic_wakeup(struct ath5k_hw *, int, bool); +static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *, struct ath5k_desc *, +	unsigned int, unsigned int, enum ath5k_pkt_type, unsigned int, +	unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, +	unsigned int, unsigned int); +static bool ath5k_hw_setup_xr_tx_desc(struct ath5k_hw *, struct ath5k_desc *, +	unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, +	unsigned int); +static int ath5k_hw_proc_4word_tx_status(struct ath5k_hw *, struct ath5k_desc *); +static int ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *, struct ath5k_desc *, +	unsigned int, unsigned int, enum ath5k_pkt_type, unsigned int, +	unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, +	unsigned int, unsigned int); +static int ath5k_hw_proc_2word_tx_status(struct ath5k_hw *, struct ath5k_desc *); +static int ath5k_hw_proc_new_rx_status(struct ath5k_hw *, struct ath5k_desc *); +static int ath5k_hw_proc_old_rx_status(struct ath5k_hw *, struct ath5k_desc *); +static int ath5k_hw_get_capabilities(struct ath5k_hw *); + +static int ath5k_eeprom_init(struct ath5k_hw *); +static int ath5k_eeprom_read_mac(struct ath5k_hw *, u8 *); + +static int ath5k_hw_enable_pspoll(struct ath5k_hw *, u8 *, u16); +static int ath5k_hw_disable_pspoll(struct ath5k_hw *); + +/* + * Enable to overwrite the country code (use "00" for debug) + */ +#if 0 +#define COUNTRYCODE "00" +#endif + +/*******************\ +  General Functions +\*******************/ + +/* + * Functions used internaly + */ + +static inline unsigned int ath5k_hw_htoclock(unsigned int usec, bool turbo) +{ +	return turbo == true ? (usec * 80) : (usec * 40); +} + +static inline unsigned int ath5k_hw_clocktoh(unsigned int clock, bool turbo) +{ +	return turbo == true ? (clock / 80) : (clock / 40); +} + +/* + * Check if a register write has been completed + */ +int ath5k_hw_register_timeout(struct ath5k_hw *ah, u32 reg, u32 flag, u32 val, +		bool is_set) +{ +	int i; +	u32 data; + +	for (i = AR5K_TUNE_REGISTER_TIMEOUT; i > 0; i--) { +		data = ath5k_hw_reg_read(ah, reg); +		if ((is_set == true) && (data & flag)) +			break; +		else if ((data & flag) == val) +			break; +		udelay(15); +	} + +	return (i <= 0) ? -EAGAIN : 0; +} + + +/***************************************\ +	Attach/Detach Functions +\***************************************/ + +/* + * Check if the device is supported and initialize the needed structs + */ +struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version) +{ +	struct ath5k_hw *ah; +	u8 mac[ETH_ALEN]; +	int ret; +	u32 srev; + +	/*If we passed the test malloc a ath5k_hw struct*/ +	ah = kzalloc(sizeof(struct ath5k_hw), GFP_KERNEL); +	if (ah == NULL) { +		ret = -ENOMEM; +		ATH5K_ERR(sc, "out of memory\n"); +		goto err; +	} + +	ah->ah_sc = sc; +	ah->ah_iobase = sc->iobase; + +	/* +	 * HW information +	 */ + +	/* Get reg domain from eeprom */ +	ath5k_get_regdomain(ah); + +	ah->ah_op_mode = IEEE80211_IF_TYPE_STA; +	ah->ah_radar.r_enabled = AR5K_TUNE_RADAR_ALERT; +	ah->ah_turbo = false; +	ah->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER; +	ah->ah_imr = 0; +	ah->ah_atim_window = 0; +	ah->ah_aifs = AR5K_TUNE_AIFS; +	ah->ah_cw_min = AR5K_TUNE_CWMIN; +	ah->ah_limit_tx_retries = AR5K_INIT_TX_RETRY; +	ah->ah_software_retry = false; +	ah->ah_ant_diversity = AR5K_TUNE_ANT_DIVERSITY; + +	/* +	 * Set the mac revision based on the pci id +	 */ +	ah->ah_version = mac_version; + +	/*Fill the ath5k_hw struct with the needed functions*/ +	if (ah->ah_version == AR5K_AR5212) +		ah->ah_magic = AR5K_EEPROM_MAGIC_5212; +	else if (ah->ah_version == AR5K_AR5211) +		ah->ah_magic = AR5K_EEPROM_MAGIC_5211; + +	if (ah->ah_version == AR5K_AR5212) { +		ah->ah_setup_tx_desc = ath5k_hw_setup_4word_tx_desc; +		ah->ah_setup_xtx_desc = ath5k_hw_setup_xr_tx_desc; +		ah->ah_proc_tx_desc = ath5k_hw_proc_4word_tx_status; +	} else { +		ah->ah_setup_tx_desc = ath5k_hw_setup_2word_tx_desc; +		ah->ah_setup_xtx_desc = ath5k_hw_setup_xr_tx_desc; +		ah->ah_proc_tx_desc = ath5k_hw_proc_2word_tx_status; +	} + +	if (ah->ah_version == AR5K_AR5212) +		ah->ah_proc_rx_desc = ath5k_hw_proc_new_rx_status; +	else if (ah->ah_version <= AR5K_AR5211) +		ah->ah_proc_rx_desc = ath5k_hw_proc_old_rx_status; + +	/* Bring device out of sleep and reset it's units */ +	ret = ath5k_hw_nic_wakeup(ah, AR5K_INIT_MODE, true); +	if (ret) +		goto err_free; + +	/* Get MAC, PHY and RADIO revisions */ +	srev = ath5k_hw_reg_read(ah, AR5K_SREV); +	ah->ah_mac_srev = srev; +	ah->ah_mac_version = AR5K_REG_MS(srev, AR5K_SREV_VER); +	ah->ah_mac_revision = AR5K_REG_MS(srev, AR5K_SREV_REV); +	ah->ah_phy_revision = ath5k_hw_reg_read(ah, AR5K_PHY_CHIP_ID) & +			0xffffffff; +	ah->ah_radio_5ghz_revision = ath5k_hw_radio_revision(ah, +			CHANNEL_5GHZ); + +	if (ah->ah_version == AR5K_AR5210) +		ah->ah_radio_2ghz_revision = 0; +	else +		ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah, +				CHANNEL_2GHZ); + +	/* Return on unsuported chips (unsupported eeprom etc) */ +	if(srev >= AR5K_SREV_VER_AR5416){ +		ATH5K_ERR(sc, "Device not yet supported.\n"); +		ret = -ENODEV; +		goto err_free; +	} + +	/* Identify single chip solutions */ +	if((srev <= AR5K_SREV_VER_AR5414) && +	(srev >= AR5K_SREV_VER_AR2424)) { +		ah->ah_single_chip = true; +	} else { +		ah->ah_single_chip = false; +	} + +	/* Single chip radio */ +	if (ah->ah_radio_2ghz_revision == ah->ah_radio_5ghz_revision) +		ah->ah_radio_2ghz_revision = 0; + +	/* Identify the radio chip*/ +	if (ah->ah_version == AR5K_AR5210) { +		ah->ah_radio = AR5K_RF5110; +	} else if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_5112) { +		ah->ah_radio = AR5K_RF5111; +	} else if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_SC1) { +		ah->ah_radio = AR5K_RF5112; +	} else { +		ah->ah_radio = AR5K_RF5413; +	} + +	ah->ah_phy = AR5K_PHY(0); + +	/* +	 * Get card capabilities, values, ... +	 */ + +	ret = ath5k_eeprom_init(ah); +	if (ret) { +		ATH5K_ERR(sc, "unable to init EEPROM\n"); +		goto err_free; +	} + +	/* Get misc capabilities */ +	ret = ath5k_hw_get_capabilities(ah); +	if (ret) { +		ATH5K_ERR(sc, "unable to get device capabilities: 0x%04x\n", +			sc->pdev->device); +		goto err_free; +	} + +	/* Get MAC address */ +	ret = ath5k_eeprom_read_mac(ah, mac); +	if (ret) { +		ATH5K_ERR(sc, "unable to read address from EEPROM: 0x%04x\n", +			sc->pdev->device); +		goto err_free; +	} + +	ath5k_hw_set_lladdr(ah, mac); +	/* Set BSSID to bcast address: ff:ff:ff:ff:ff:ff for now */ +	memset(ah->ah_bssid, 0xff, ETH_ALEN); +	ath5k_hw_set_associd(ah, ah->ah_bssid, 0); +	ath5k_hw_set_opmode(ah); + +	ath5k_hw_set_rfgain_opt(ah); + +	return ah; +err_free: +	kfree(ah); +err: +	return ERR_PTR(ret); +} + +/* + * Bring up MAC + PHY Chips + */ +static int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial) +{ +	u32 turbo, mode, clock; +	int ret; + +	turbo = 0; +	mode = 0; +	clock = 0; + +	ATH5K_TRACE(ah->ah_sc); + +	/* Wakeup the device */ +	ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0); +	if (ret) { +		ATH5K_ERR(ah->ah_sc, "failed to wakeup the MAC Chip\n"); +		return ret; +	} + +	if (ah->ah_version != AR5K_AR5210) { +		/* +		 * Get channel mode flags +		 */ + +		if (ah->ah_radio >= AR5K_RF5112) { +			mode = AR5K_PHY_MODE_RAD_RF5112; +			clock = AR5K_PHY_PLL_RF5112; +		} else { +			mode = AR5K_PHY_MODE_RAD_RF5111;	/*Zero*/ +			clock = AR5K_PHY_PLL_RF5111;		/*Zero*/ +		} + +		if (flags & CHANNEL_2GHZ) { +			mode |= AR5K_PHY_MODE_FREQ_2GHZ; +			clock |= AR5K_PHY_PLL_44MHZ; + +			if (flags & CHANNEL_CCK) { +				mode |= AR5K_PHY_MODE_MOD_CCK; +			} else if (flags & CHANNEL_OFDM) { +				/* XXX Dynamic OFDM/CCK is not supported by the +				 * AR5211 so we set MOD_OFDM for plain g (no +				 * CCK headers) operation. We need to test +				 * this, 5211 might support ofdm-only g after +				 * all, there are also initial register values +				 * in the code for g mode (see initvals.c). */ +				if (ah->ah_version == AR5K_AR5211) +					mode |= AR5K_PHY_MODE_MOD_OFDM; +				else +					mode |= AR5K_PHY_MODE_MOD_DYN; +			} else { +				ATH5K_ERR(ah->ah_sc, +					"invalid radio modulation mode\n"); +				return -EINVAL; +			} +		} else if (flags & CHANNEL_5GHZ) { +			mode |= AR5K_PHY_MODE_FREQ_5GHZ; +			clock |= AR5K_PHY_PLL_40MHZ; + +			if (flags & CHANNEL_OFDM) +				mode |= AR5K_PHY_MODE_MOD_OFDM; +			else { +				ATH5K_ERR(ah->ah_sc, +					"invalid radio modulation mode\n"); +				return -EINVAL; +			} +		} else { +			ATH5K_ERR(ah->ah_sc, "invalid radio frequency mode\n"); +			return -EINVAL; +		} + +		if (flags & CHANNEL_TURBO) +			turbo = AR5K_PHY_TURBO_MODE | AR5K_PHY_TURBO_SHORT; +	} else { /* Reset the device */ + +		/* ...enable Atheros turbo mode if requested */ +		if (flags & CHANNEL_TURBO) +			ath5k_hw_reg_write(ah, AR5K_PHY_TURBO_MODE, +					AR5K_PHY_TURBO); +	} + +	/* ...reset chipset and PCI device */ +	if (ah->ah_single_chip == false && ath5k_hw_nic_reset(ah, +				AR5K_RESET_CTL_CHIP | AR5K_RESET_CTL_PCI)) { +		ATH5K_ERR(ah->ah_sc, "failed to reset the MAC Chip + PCI\n"); +		return -EIO; +	} + +	if (ah->ah_version == AR5K_AR5210) +		udelay(2300); + +	/* ...wakeup again!*/ +	ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0); +	if (ret) { +		ATH5K_ERR(ah->ah_sc, "failed to resume the MAC Chip\n"); +		return ret; +	} + +	/* ...final warm reset */ +	if (ath5k_hw_nic_reset(ah, 0)) { +		ATH5K_ERR(ah->ah_sc, "failed to warm reset the MAC Chip\n"); +		return -EIO; +	} + +	if (ah->ah_version != AR5K_AR5210) { +		/* ...set the PHY operating mode */ +		ath5k_hw_reg_write(ah, clock, AR5K_PHY_PLL); +		udelay(300); + +		ath5k_hw_reg_write(ah, mode, AR5K_PHY_MODE); +		ath5k_hw_reg_write(ah, turbo, AR5K_PHY_TURBO); +	} + +	return 0; +} + +/* + * Get the rate table for a specific operation mode + */ +const struct ath5k_rate_table *ath5k_hw_get_rate_table(struct ath5k_hw *ah, +		unsigned int mode) +{ +	ATH5K_TRACE(ah->ah_sc); + +	if (!test_bit(mode, ah->ah_capabilities.cap_mode)) +		return NULL; + +	/* Get rate tables */ +	switch (mode) { +	case MODE_IEEE80211A: +		return &ath5k_rt_11a; +	case MODE_ATHEROS_TURBO: +		return &ath5k_rt_turbo; +	case MODE_IEEE80211B: +		return &ath5k_rt_11b; +	case MODE_IEEE80211G: +		return &ath5k_rt_11g; +	case MODE_ATHEROS_TURBOG: +		return &ath5k_rt_xr; +	} + +	return NULL; +} + +/* + * Free the ath5k_hw struct + */ +void ath5k_hw_detach(struct ath5k_hw *ah) +{ +	ATH5K_TRACE(ah->ah_sc); + +	if (ah->ah_rf_banks != NULL) +		kfree(ah->ah_rf_banks); + +	/* assume interrupts are down */ +	kfree(ah); +} + +/****************************\ +  Reset function and helpers +\****************************/ + +/** + * ath5k_hw_write_ofdm_timings - set OFDM timings on AR5212 + * + * @ah: the &struct ath5k_hw + * @channel: the currently set channel upon reset + * + * Write the OFDM timings for the AR5212 upon reset. This is a helper for + * ath5k_hw_reset(). This seems to tune the PLL a specified frequency + * depending on the bandwidth of the channel. + * + */ +static inline int ath5k_hw_write_ofdm_timings(struct ath5k_hw *ah, +	struct ieee80211_channel *channel) +{ +	/* Get exponent and mantissa and set it */ +	u32 coef_scaled, coef_exp, coef_man, +		ds_coef_exp, ds_coef_man, clock; + +	if (!(ah->ah_version == AR5K_AR5212) || +		!(channel->val & CHANNEL_OFDM)) +		BUG(); + +	/* Seems there are two PLLs, one for baseband sampling and one +	 * for tuning. Tuning basebands are 40 MHz or 80MHz when in +	 * turbo. */ +	clock = channel->val & CHANNEL_TURBO ? 80 : 40; +	coef_scaled = ((5 * (clock << 24)) / 2) / +	channel->freq; + +	for (coef_exp = 31; coef_exp > 0; coef_exp--) +		if ((coef_scaled >> coef_exp) & 0x1) +			break; + +	if (!coef_exp) +		return -EINVAL; + +	coef_exp = 14 - (coef_exp - 24); +	coef_man = coef_scaled + +		(1 << (24 - coef_exp - 1)); +	ds_coef_man = coef_man >> (24 - coef_exp); +	ds_coef_exp = coef_exp - 16; + +	AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_3, +		AR5K_PHY_TIMING_3_DSC_MAN, ds_coef_man); +	AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_3, +		AR5K_PHY_TIMING_3_DSC_EXP, ds_coef_exp); + +	return 0; +} + +/** + * ath5k_hw_write_rate_duration - set rate duration during hw resets + * + * @ah: the &struct ath5k_hw + * @driver_mode: one of enum ieee80211_phymode or our one of our own + *     vendor modes + * + * Write the rate duration table for the current mode upon hw reset. This + * is a helper for ath5k_hw_reset(). It seems all this is doing is setting + * an ACK timeout for the hardware for the current mode for each rate. The + * rates which are capable of short preamble (802.11b rates 2Mbps, 5.5Mbps, + * and 11Mbps) have another register for the short preamble ACK timeout + * calculation. + * + */ +static inline void ath5k_hw_write_rate_duration(struct ath5k_hw *ah, +       unsigned int driver_mode) +{ +	struct ath5k_softc *sc = ah->ah_sc; +	const struct ath5k_rate_table *rt; +	unsigned int i; + +	/* Get rate table for the current operating mode */ +	rt = ath5k_hw_get_rate_table(ah, +		driver_mode); + +	/* Write rate duration table */ +	for (i = 0; i < rt->rate_count; i++) { +		const struct ath5k_rate *rate, *control_rate; +		u32 reg; +		u16 tx_time; + +		rate = &rt->rates[i]; +		control_rate = &rt->rates[rate->control_rate]; + +		/* Set ACK timeout */ +		reg = AR5K_RATE_DUR(rate->rate_code); + +		/* An ACK frame consists of 10 bytes. If you add the FCS, +		 * which ieee80211_generic_frame_duration() adds, +		 * its 14 bytes. Note we use the control rate and not the +		 * actual rate for this rate. See mac80211 tx.c +		 * ieee80211_duration() for a brief description of +		 * what rate we should choose to TX ACKs. */ +		tx_time = ieee80211_generic_frame_duration(sc->hw, +			sc->iface_id, 10, control_rate->rate_kbps/100); + +		ath5k_hw_reg_write(ah, tx_time, reg); + +		if (!HAS_SHPREAMBLE(i)) +			continue; + +		/* +		 * We're not distinguishing short preamble here, +		 * This is true, all we'll get is a longer value here +		 * which is not necessarilly bad. We could use +		 * export ieee80211_frame_duration() but that needs to be +		 * fixed first to be properly used by mac802111 drivers: +		 * +		 *  - remove erp stuff and let the routine figure ofdm +		 *    erp rates +		 *  - remove passing argument ieee80211_local as +		 *    drivers don't have access to it +		 *  - move drivers using ieee80211_generic_frame_duration() +		 *    to this +		 */ +		ath5k_hw_reg_write(ah, tx_time, +			reg + (AR5K_SET_SHORT_PREAMBLE << 2)); +	} +} + +/* + * Main reset function + */ +int ath5k_hw_reset(struct ath5k_hw *ah, enum ieee80211_if_types op_mode, +	struct ieee80211_channel *channel, bool change_channel) +{ +	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; +	u32 data, s_seq, s_ant, s_led[3]; +	unsigned int i, mode, freq, ee_mode, ant[2], driver_mode = -1; +	int ret; + +	ATH5K_TRACE(ah->ah_sc); + +	s_seq = 0; +	s_ant = 0; +	ee_mode = 0; +	freq = 0; +	mode = 0; + +	/* +	 * Save some registers before a reset +	 */ +	/*DCU/Antenna selection not available on 5210*/ +	if (ah->ah_version != AR5K_AR5210) { +		if (change_channel == true) { +			/* Seq number for queue 0 -do this for all queues ? */ +			s_seq = ath5k_hw_reg_read(ah, +					AR5K_QUEUE_DFS_SEQNUM(0)); +			/*Default antenna*/ +			s_ant = ath5k_hw_reg_read(ah, AR5K_DEFAULT_ANTENNA); +		} +	} + +	/*GPIOs*/ +	s_led[0] = ath5k_hw_reg_read(ah, AR5K_PCICFG) & AR5K_PCICFG_LEDSTATE; +	s_led[1] = ath5k_hw_reg_read(ah, AR5K_GPIOCR); +	s_led[2] = ath5k_hw_reg_read(ah, AR5K_GPIODO); + +	if (change_channel == true && ah->ah_rf_banks != NULL) +		ath5k_hw_get_rf_gain(ah); + + +	/*Wakeup the device*/ +	ret = ath5k_hw_nic_wakeup(ah, channel->val, false); +	if (ret) +		return ret; + +	/* +	 * Initialize operating mode +	 */ +	ah->ah_op_mode = op_mode; + +	/* +	 * 5111/5112 Settings +	 * 5210 only comes with RF5110 +	 */ +	if (ah->ah_version != AR5K_AR5210) { +		if (ah->ah_radio != AR5K_RF5111 && +			ah->ah_radio != AR5K_RF5112 && +			ah->ah_radio != AR5K_RF5413) { +			ATH5K_ERR(ah->ah_sc, +				"invalid phy radio: %u\n", ah->ah_radio); +			return -EINVAL; +		} + +		switch (channel->val & CHANNEL_MODES) { +		case CHANNEL_A: +			mode = AR5K_INI_VAL_11A; +			freq = AR5K_INI_RFGAIN_5GHZ; +			ee_mode = AR5K_EEPROM_MODE_11A; +			driver_mode = MODE_IEEE80211A; +			break; +		case CHANNEL_G: +			mode = AR5K_INI_VAL_11G; +			freq = AR5K_INI_RFGAIN_2GHZ; +			ee_mode = AR5K_EEPROM_MODE_11G; +			driver_mode = MODE_IEEE80211G; +			break; +		case CHANNEL_B: +			mode = AR5K_INI_VAL_11B; +			freq = AR5K_INI_RFGAIN_2GHZ; +			ee_mode = AR5K_EEPROM_MODE_11B; +			driver_mode = MODE_IEEE80211B; +			break; +		case CHANNEL_T: +			mode = AR5K_INI_VAL_11A_TURBO; +			freq = AR5K_INI_RFGAIN_5GHZ; +			ee_mode = AR5K_EEPROM_MODE_11A; +			driver_mode = MODE_ATHEROS_TURBO; +			break; +		/*Is this ok on 5211 too ?*/ +		case CHANNEL_TG: +			mode = AR5K_INI_VAL_11G_TURBO; +			freq = AR5K_INI_RFGAIN_2GHZ; +			ee_mode = AR5K_EEPROM_MODE_11G; +			driver_mode = MODE_ATHEROS_TURBOG; +			break; +		case CHANNEL_XR: +			if (ah->ah_version == AR5K_AR5211) { +				ATH5K_ERR(ah->ah_sc, +					"XR mode not available on 5211"); +				return -EINVAL; +			} +			mode = AR5K_INI_VAL_XR; +			freq = AR5K_INI_RFGAIN_5GHZ; +			ee_mode = AR5K_EEPROM_MODE_11A; +			driver_mode = MODE_IEEE80211A; +			break; +		default: +			ATH5K_ERR(ah->ah_sc, +				"invalid channel: %d\n", channel->freq); +			return -EINVAL; +		} + +		/* PHY access enable */ +		ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0)); + +	} + +	ret = ath5k_hw_write_initvals(ah, mode, change_channel); +	if (ret) +		return ret; + +	/* +	 * 5211/5212 Specific +	 */ +	if (ah->ah_version != AR5K_AR5210) { +		/* +		 * Write initial RF gain settings +		 * This should work for both 5111/5112 +		 */ +		ret = ath5k_hw_rfgain(ah, freq); +		if (ret) +			return ret; + +		mdelay(1); + +		/* +		 * Write some more initial register settings +		 */ +		if (ah->ah_version > AR5K_AR5211){ /* found on 5213+ */ +			ath5k_hw_reg_write(ah, 0x0002a002, AR5K_PHY(11)); + +			if (channel->val == CHANNEL_G) +				ath5k_hw_reg_write(ah, 0x00f80d80, AR5K_PHY(83)); /* 0x00fc0ec0 */ +			else +				ath5k_hw_reg_write(ah, 0x00000000, AR5K_PHY(83)); + +			ath5k_hw_reg_write(ah, 0x000001b5, 0xa228); /* 0x000009b5 */ +			ath5k_hw_reg_write(ah, 0x000009b5, 0xa228); +			ath5k_hw_reg_write(ah, 0x0000000f, 0x8060); +			ath5k_hw_reg_write(ah, 0x00000000, 0xa254); +			ath5k_hw_reg_write(ah, 0x0000000e, AR5K_PHY_SCAL); +		} + +		/* Fix for first revision of the RF5112 RF chipset */ +		if (ah->ah_radio >= AR5K_RF5112 && +				ah->ah_radio_5ghz_revision < +				AR5K_SREV_RAD_5112A) { +			ath5k_hw_reg_write(ah, AR5K_PHY_CCKTXCTL_WORLD, +					AR5K_PHY_CCKTXCTL); +			if (channel->val & CHANNEL_5GHZ) +				data = 0xffb81020; +			else +				data = 0xffb80d20; +			ath5k_hw_reg_write(ah, data, AR5K_PHY_FRAME_CTL); +		} + +		/* +		 * Set TX power (FIXME) +		 */ +		ret = ath5k_hw_txpower(ah, channel, AR5K_TUNE_DEFAULT_TXPOWER); +		if (ret) +			return ret; + +		/* Write rate duration table */ +		if (ah->ah_version == AR5K_AR5212) +			ath5k_hw_write_rate_duration(ah, driver_mode); + +		/* +		 * Write RF registers +		 * TODO:Does this work on 5211 (5111) ? +		 */ +		ret = ath5k_hw_rfregs(ah, channel, mode); +		if (ret) +			return ret; + +		/* +		 * Configure additional registers +		 */ + +		/* Write OFDM timings on 5212*/ +		if (ah->ah_version == AR5K_AR5212 && +			channel->val & CHANNEL_OFDM) { +			ret = ath5k_hw_write_ofdm_timings(ah, channel); +			if (ret) +				return ret; +		} + +		/*Enable/disable 802.11b mode on 5111 +		(enable 2111 frequency converter + CCK)*/ +		if (ah->ah_radio == AR5K_RF5111) { +			if (driver_mode == MODE_IEEE80211B) +				AR5K_REG_ENABLE_BITS(ah, AR5K_TXCFG, +				    AR5K_TXCFG_B_MODE); +			else +				AR5K_REG_DISABLE_BITS(ah, AR5K_TXCFG, +				    AR5K_TXCFG_B_MODE); +		} + +		/* +		 * Set channel and calibrate the PHY +		 */ +		ret = ath5k_hw_channel(ah, channel); +		if (ret) +			return ret; + +		/* Set antenna mode */ +		AR5K_REG_MASKED_BITS(ah, AR5K_PHY(0x44), +			ah->ah_antenna[ee_mode][0], 0xfffffc06); + +		/* +		 * In case a fixed antenna was set as default +		 * write the same settings on both AR5K_PHY_ANT_SWITCH_TABLE +		 * registers. +		 */ +		if (s_ant != 0){ +			if (s_ant == AR5K_ANT_FIXED_A) /* 1 - Main */ +				ant[0] = ant[1] = AR5K_ANT_FIXED_A; +			else	/* 2 - Aux */ +				ant[0] = ant[1] = AR5K_ANT_FIXED_B; +		} else { +			ant[0] = AR5K_ANT_FIXED_A; +			ant[1] = AR5K_ANT_FIXED_B; +		} + +		ath5k_hw_reg_write(ah, ah->ah_antenna[ee_mode][ant[0]], +			AR5K_PHY_ANT_SWITCH_TABLE_0); +		ath5k_hw_reg_write(ah, ah->ah_antenna[ee_mode][ant[1]], +			AR5K_PHY_ANT_SWITCH_TABLE_1); + +		/* Commit values from EEPROM */ +		if (ah->ah_radio == AR5K_RF5111) +			AR5K_REG_WRITE_BITS(ah, AR5K_PHY_FRAME_CTL, +			    AR5K_PHY_FRAME_CTL_TX_CLIP, ee->ee_tx_clip); + +		ath5k_hw_reg_write(ah, +			AR5K_PHY_NF_SVAL(ee->ee_noise_floor_thr[ee_mode]), +			AR5K_PHY(0x5a)); + +		AR5K_REG_MASKED_BITS(ah, AR5K_PHY(0x11), +			(ee->ee_switch_settling[ee_mode] << 7) & 0x3f80, +			0xffffc07f); +		AR5K_REG_MASKED_BITS(ah, AR5K_PHY(0x12), +			(ee->ee_ant_tx_rx[ee_mode] << 12) & 0x3f000, +			0xfffc0fff); +		AR5K_REG_MASKED_BITS(ah, AR5K_PHY(0x14), +			(ee->ee_adc_desired_size[ee_mode] & 0x00ff) | +			((ee->ee_pga_desired_size[ee_mode] << 8) & 0xff00), +			0xffff0000); + +		ath5k_hw_reg_write(ah, +			(ee->ee_tx_end2xpa_disable[ee_mode] << 24) | +			(ee->ee_tx_end2xpa_disable[ee_mode] << 16) | +			(ee->ee_tx_frm2xpa_enable[ee_mode] << 8) | +			(ee->ee_tx_frm2xpa_enable[ee_mode]), AR5K_PHY(0x0d)); + +		AR5K_REG_MASKED_BITS(ah, AR5K_PHY(0x0a), +			ee->ee_tx_end2xlna_enable[ee_mode] << 8, 0xffff00ff); +		AR5K_REG_MASKED_BITS(ah, AR5K_PHY(0x19), +			(ee->ee_thr_62[ee_mode] << 12) & 0x7f000, 0xfff80fff); +		AR5K_REG_MASKED_BITS(ah, AR5K_PHY(0x49), 4, 0xffffff01); + +		AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, +		    AR5K_PHY_IQ_CORR_ENABLE | +		    (ee->ee_i_cal[ee_mode] << AR5K_PHY_IQ_CORR_Q_I_COFF_S) | +		    ee->ee_q_cal[ee_mode]); + +		if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) +			AR5K_REG_WRITE_BITS(ah, AR5K_PHY_GAIN_2GHZ, +				AR5K_PHY_GAIN_2GHZ_MARGIN_TXRX, +				ee->ee_margin_tx_rx[ee_mode]); + +	} else { +		mdelay(1); +		/* Disable phy and wait */ +		ath5k_hw_reg_write(ah, AR5K_PHY_ACT_DISABLE, AR5K_PHY_ACT); +		mdelay(1); +	} + +	/* +	 * Restore saved values +	 */ +	/*DCU/Antenna selection not available on 5210*/ +	if (ah->ah_version != AR5K_AR5210) { +		ath5k_hw_reg_write(ah, s_seq, AR5K_QUEUE_DFS_SEQNUM(0)); +		ath5k_hw_reg_write(ah, s_ant, AR5K_DEFAULT_ANTENNA); +	} +	AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, s_led[0]); +	ath5k_hw_reg_write(ah, s_led[1], AR5K_GPIOCR); +	ath5k_hw_reg_write(ah, s_led[2], AR5K_GPIODO); + +	/* +	 * Misc +	 */ +	/* XXX: add ah->aid once mac80211 gives this to us */ +	ath5k_hw_set_associd(ah, ah->ah_bssid, 0); + +	ath5k_hw_set_opmode(ah); +	/*PISR/SISR Not available on 5210*/ +	if (ah->ah_version != AR5K_AR5210) { +		ath5k_hw_reg_write(ah, 0xffffffff, AR5K_PISR); +		/* If we later allow tuning for this, store into sc structure */ +		data = AR5K_TUNE_RSSI_THRES | +			AR5K_TUNE_BMISS_THRES << AR5K_RSSI_THR_BMISS_S; +		ath5k_hw_reg_write(ah, data, AR5K_RSSI_THR); +	} + +	/* +	 * Set Rx/Tx DMA Configuration +	 *(passing dma size not available on 5210) +	 */ +	if (ah->ah_version != AR5K_AR5210) { +		AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG, AR5K_TXCFG_SDMAMR, +				AR5K_DMASIZE_512B | AR5K_TXCFG_DMASIZE); +		AR5K_REG_WRITE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_SDMAMW, +				AR5K_DMASIZE_512B); +	} + +	/* +	 * Enable the PHY and wait until completion +	 */ +	ath5k_hw_reg_write(ah, AR5K_PHY_ACT_ENABLE, AR5K_PHY_ACT); + +	/* +	 * 5111/5112 Specific +	 */ +	if (ah->ah_version != AR5K_AR5210) { +		data = ath5k_hw_reg_read(ah, AR5K_PHY_RX_DELAY) & +			AR5K_PHY_RX_DELAY_M; +		data = (channel->val & CHANNEL_CCK) ? +			((data << 2) / 22) : (data / 10); + +		udelay(100 + data); +	} else { +		mdelay(1); +	} + +	/* +	 * Enable calibration and wait until completion +	 */ +	AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, +				AR5K_PHY_AGCCTL_CAL); + +	if (ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, +			AR5K_PHY_AGCCTL_CAL, 0, false)) { +		ATH5K_ERR(ah->ah_sc, "calibration timeout (%uMHz)\n", +			channel->freq); +		return -EAGAIN; +	} + +	ret = ath5k_hw_noise_floor_calibration(ah, channel->freq); +	if (ret) +		return ret; + +	ah->ah_calibration = false; + +	/* A and G modes can use QAM modulation which requires enabling +	 * I and Q calibration. Don't bother in B mode. */ +	if (!(driver_mode == MODE_IEEE80211B)) { +		ah->ah_calibration = true; +		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, +				AR5K_PHY_IQ_CAL_NUM_LOG_MAX, 15); +		AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, +				AR5K_PHY_IQ_RUN); +	} + +	/* +	 * Reset queues and start beacon timers at the end of the reset routine +	 */ +	for (i = 0; i < ah->ah_capabilities.cap_queues.q_tx_num; i++) { +		/*No QCU on 5210*/ +		if (ah->ah_version != AR5K_AR5210) +			AR5K_REG_WRITE_Q(ah, AR5K_QUEUE_QCUMASK(i), i); + +		ret = ath5k_hw_reset_tx_queue(ah, i); +		if (ret) { +			ATH5K_ERR(ah->ah_sc, +				"failed to reset TX queue #%d\n", i); +			return ret; +		} +	} + +	/* Pre-enable interrupts on 5211/5212*/ +	if (ah->ah_version != AR5K_AR5210) +		ath5k_hw_set_intr(ah, AR5K_INT_RX | AR5K_INT_TX | +				AR5K_INT_FATAL); + +	/* +	 * Set RF kill flags if supported by the device (read from the EEPROM) +	 * Disable gpio_intr for now since it results system hang. +	 * TODO: Handle this in ath5k_intr +	 */ +#if 0 +	if (AR5K_EEPROM_HDR_RFKILL(ah->ah_capabilities.cap_eeprom.ee_header)) { +		ath5k_hw_set_gpio_input(ah, 0); +		ah->ah_gpio[0] = ath5k_hw_get_gpio(ah, 0); +		if (ah->ah_gpio[0] == 0) +			ath5k_hw_set_gpio_intr(ah, 0, 1); +		else +			ath5k_hw_set_gpio_intr(ah, 0, 0); +	} +#endif + +	/* +	 * Set the 32MHz reference clock on 5212 phy clock sleep register +	 */ +	if (ah->ah_version == AR5K_AR5212) { +		ath5k_hw_reg_write(ah, AR5K_PHY_SCR_32MHZ, AR5K_PHY_SCR); +		ath5k_hw_reg_write(ah, AR5K_PHY_SLMT_32MHZ, AR5K_PHY_SLMT); +		ath5k_hw_reg_write(ah, AR5K_PHY_SCAL_32MHZ, AR5K_PHY_SCAL); +		ath5k_hw_reg_write(ah, AR5K_PHY_SCLOCK_32MHZ, AR5K_PHY_SCLOCK); +		ath5k_hw_reg_write(ah, AR5K_PHY_SDELAY_32MHZ, AR5K_PHY_SDELAY); +		ath5k_hw_reg_write(ah, ah->ah_radio == AR5K_RF5111 ? +			AR5K_PHY_SPENDING_RF5111 : AR5K_PHY_SPENDING_RF5112, +			AR5K_PHY_SPENDING); +	} + +	/* +	 * Disable beacons and reset the register +	 */ +	AR5K_REG_DISABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE | +			AR5K_BEACON_RESET_TSF); + +	return 0; +} + +/* + * Reset chipset + */ +static int ath5k_hw_nic_reset(struct ath5k_hw *ah, u32 val) +{ +	int ret; +	u32 mask = val ? val : ~0U; + +	ATH5K_TRACE(ah->ah_sc); + +	/* Read-and-clear RX Descriptor Pointer*/ +	ath5k_hw_reg_read(ah, AR5K_RXDP); + +	/* +	 * Reset the device and wait until success +	 */ +	ath5k_hw_reg_write(ah, val, AR5K_RESET_CTL); + +	/* Wait at least 128 PCI clocks */ +	udelay(15); + +	if (ah->ah_version == AR5K_AR5210) { +		val &= AR5K_RESET_CTL_CHIP; +		mask &= AR5K_RESET_CTL_CHIP; +	} else { +		val &= AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_BASEBAND; +		mask &= AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_BASEBAND; +	} + +	ret = ath5k_hw_register_timeout(ah, AR5K_RESET_CTL, mask, val, false); + +	/* +	 * Reset configuration register (for hw byte-swap). Note that this +	 * is only set for big endian. We do the necessary magic in +	 * AR5K_INIT_CFG. +	 */ +	if ((val & AR5K_RESET_CTL_PCU) == 0) +		ath5k_hw_reg_write(ah, AR5K_INIT_CFG, AR5K_CFG); + +	return ret; +} + +/* + * Power management functions + */ + +/* + * Sleep control + */ +int ath5k_hw_set_power(struct ath5k_hw *ah, enum ath5k_power_mode mode, +		bool set_chip, u16 sleep_duration) +{ +	unsigned int i; +	u32 staid; + +	ATH5K_TRACE(ah->ah_sc); +	staid = ath5k_hw_reg_read(ah, AR5K_STA_ID1); + +	switch (mode) { +	case AR5K_PM_AUTO: +		staid &= ~AR5K_STA_ID1_DEFAULT_ANTENNA; +		/* fallthrough */ +	case AR5K_PM_NETWORK_SLEEP: +		if (set_chip == true) +			ath5k_hw_reg_write(ah, +				AR5K_SLEEP_CTL_SLE | sleep_duration, +				AR5K_SLEEP_CTL); + +		staid |= AR5K_STA_ID1_PWR_SV; +		break; + +	case AR5K_PM_FULL_SLEEP: +		if (set_chip == true) +			ath5k_hw_reg_write(ah, AR5K_SLEEP_CTL_SLE_SLP, +				AR5K_SLEEP_CTL); + +		staid |= AR5K_STA_ID1_PWR_SV; +		break; + +	case AR5K_PM_AWAKE: +		if (set_chip == false) +			goto commit; + +		ath5k_hw_reg_write(ah, AR5K_SLEEP_CTL_SLE_WAKE, +				AR5K_SLEEP_CTL); + +		for (i = 5000; i > 0; i--) { +			/* Check if the chip did wake up */ +			if ((ath5k_hw_reg_read(ah, AR5K_PCICFG) & +					AR5K_PCICFG_SPWR_DN) == 0) +				break; + +			/* Wait a bit and retry */ +			udelay(200); +			ath5k_hw_reg_write(ah, AR5K_SLEEP_CTL_SLE_WAKE, +				AR5K_SLEEP_CTL); +		} + +		/* Fail if the chip didn't wake up */ +		if (i <= 0) +			return -EIO; + +		staid &= ~AR5K_STA_ID1_PWR_SV; +		break; + +	default: +		return -EINVAL; +	} + +commit: +	ah->ah_power_mode = mode; +	ath5k_hw_reg_write(ah, staid, AR5K_STA_ID1); + +	return 0; +} + +/***********************\ +  DMA Related Functions +\***********************/ + +/* + * Receive functions + */ + +/* + * Start DMA receive + */ +void ath5k_hw_start_rx(struct ath5k_hw *ah) +{ +	ATH5K_TRACE(ah->ah_sc); +	ath5k_hw_reg_write(ah, AR5K_CR_RXE, AR5K_CR); +} + +/* + * Stop DMA receive + */ +int ath5k_hw_stop_rx_dma(struct ath5k_hw *ah) +{ +	unsigned int i; + +	ATH5K_TRACE(ah->ah_sc); +	ath5k_hw_reg_write(ah, AR5K_CR_RXD, AR5K_CR); + +	/* +	 * It may take some time to disable the DMA receive unit +	 */ +	for (i = 2000; i > 0 && +			(ath5k_hw_reg_read(ah, AR5K_CR) & AR5K_CR_RXE) != 0; +			i--) +		udelay(10); + +	return i ? 0 : -EBUSY; +} + +/* + * Get the address of the RX Descriptor + */ +u32 ath5k_hw_get_rx_buf(struct ath5k_hw *ah) +{ +	return ath5k_hw_reg_read(ah, AR5K_RXDP); +} + +/* + * Set the address of the RX Descriptor + */ +void ath5k_hw_put_rx_buf(struct ath5k_hw *ah, u32 phys_addr) +{ +	ATH5K_TRACE(ah->ah_sc); + +	/*TODO:Shouldn't we check if RX is enabled first ?*/ +	ath5k_hw_reg_write(ah, phys_addr, AR5K_RXDP); +} + +/* + * Transmit functions + */ + +/* + * Start DMA transmit for a specific queue + * (see also QCU/DCU functions) + */ +int ath5k_hw_tx_start(struct ath5k_hw *ah, unsigned int queue) +{ +	u32 tx_queue; + +	ATH5K_TRACE(ah->ah_sc); +	AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); + +	/* Return if queue is declared inactive */ +	if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE) +		return -EIO; + +	if (ah->ah_version == AR5K_AR5210) { +		tx_queue = ath5k_hw_reg_read(ah, AR5K_CR); + +		/* +		 * Set the queue by type on 5210 +		 */ +		switch (ah->ah_txq[queue].tqi_type) { +		case AR5K_TX_QUEUE_DATA: +			tx_queue |= AR5K_CR_TXE0 & ~AR5K_CR_TXD0; +			break; +		case AR5K_TX_QUEUE_BEACON: +			tx_queue |= AR5K_CR_TXE1 & ~AR5K_CR_TXD1; +			ath5k_hw_reg_write(ah, AR5K_BCR_TQ1V | AR5K_BCR_BDMAE, +					AR5K_BSR); +			break; +		case AR5K_TX_QUEUE_CAB: +			tx_queue |= AR5K_CR_TXE1 & ~AR5K_CR_TXD1; +			ath5k_hw_reg_write(ah, AR5K_BCR_TQ1FV | AR5K_BCR_TQ1V | +				AR5K_BCR_BDMAE, AR5K_BSR); +			break; +		default: +			return -EINVAL; +		} +		/* Start queue */ +		ath5k_hw_reg_write(ah, tx_queue, AR5K_CR); +	} else { +		/* Return if queue is disabled */ +		if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXD, queue)) +			return -EIO; + +		/* Start queue */ +		AR5K_REG_WRITE_Q(ah, AR5K_QCU_TXE, queue); +	} + +	return 0; +} + +/* + * Stop DMA transmit for a specific queue + * (see also QCU/DCU functions) + */ +int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue) +{ +	unsigned int i = 100; +	u32 tx_queue, pending; + +	ATH5K_TRACE(ah->ah_sc); +	AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); + +	/* Return if queue is declared inactive */ +	if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE) +		return -EIO; + +	if (ah->ah_version == AR5K_AR5210) { +		tx_queue = ath5k_hw_reg_read(ah, AR5K_CR); + +		/* +		 * Set by queue type +		 */ +		switch (ah->ah_txq[queue].tqi_type) { +		case AR5K_TX_QUEUE_DATA: +			tx_queue |= AR5K_CR_TXD0 & ~AR5K_CR_TXE0; +			break; +		case AR5K_TX_QUEUE_BEACON: +		case AR5K_TX_QUEUE_CAB: +			/* XXX Fix me... */ +			tx_queue |= AR5K_CR_TXD1 & ~AR5K_CR_TXD1; +			ath5k_hw_reg_write(ah, 0, AR5K_BSR); +			break; +		default: +			return -EINVAL; +		} + +		/* Stop queue */ +		ath5k_hw_reg_write(ah, tx_queue, AR5K_CR); +	} else { +		/* +		 * Schedule TX disable and wait until queue is empty +		 */ +		AR5K_REG_WRITE_Q(ah, AR5K_QCU_TXD, queue); + +		/*Check for pending frames*/ +		do { +			pending = ath5k_hw_reg_read(ah, +				AR5K_QUEUE_STATUS(queue)) & +				AR5K_QCU_STS_FRMPENDCNT; +			udelay(100); +		} while (--i && pending); + +		/* Clear register */ +		ath5k_hw_reg_write(ah, 0, AR5K_QCU_TXD); +	} + +	/* TODO: Check for success else return error */ +	return 0; +} + +/* + * Get the address of the TX Descriptor for a specific queue + * (see also QCU/DCU functions) + */ +u32 ath5k_hw_get_tx_buf(struct ath5k_hw *ah, unsigned int queue) +{ +	u16 tx_reg; + +	ATH5K_TRACE(ah->ah_sc); +	AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); + +	/* +	 * Get the transmit queue descriptor pointer from the selected queue +	 */ +	/*5210 doesn't have QCU*/ +	if (ah->ah_version == AR5K_AR5210) { +		switch (ah->ah_txq[queue].tqi_type) { +		case AR5K_TX_QUEUE_DATA: +			tx_reg = AR5K_NOQCU_TXDP0; +			break; +		case AR5K_TX_QUEUE_BEACON: +		case AR5K_TX_QUEUE_CAB: +			tx_reg = AR5K_NOQCU_TXDP1; +			break; +		default: +			return 0xffffffff; +		} +	} else { +		tx_reg = AR5K_QUEUE_TXDP(queue); +	} + +	return ath5k_hw_reg_read(ah, tx_reg); +} + +/* + * Set the address of the TX Descriptor for a specific queue + * (see also QCU/DCU functions) + */ +int ath5k_hw_put_tx_buf(struct ath5k_hw *ah, unsigned int queue, u32 phys_addr) +{ +	u16 tx_reg; + +	ATH5K_TRACE(ah->ah_sc); +	AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); + +	/* +	 * Set the transmit queue descriptor pointer register by type +	 * on 5210 +	 */ +	if (ah->ah_version == AR5K_AR5210) { +		switch (ah->ah_txq[queue].tqi_type) { +		case AR5K_TX_QUEUE_DATA: +			tx_reg = AR5K_NOQCU_TXDP0; +			break; +		case AR5K_TX_QUEUE_BEACON: +		case AR5K_TX_QUEUE_CAB: +			tx_reg = AR5K_NOQCU_TXDP1; +			break; +		default: +			return -EINVAL; +		} +	} else { +		/* +		 * Set the transmit queue descriptor pointer for +		 * the selected queue on QCU for 5211+ +		 * (this won't work if the queue is still active) +		 */ +		if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue)) +			return -EIO; + +		tx_reg = AR5K_QUEUE_TXDP(queue); +	} + +	/* Set descriptor pointer */ +	ath5k_hw_reg_write(ah, phys_addr, tx_reg); + +	return 0; +} + +/* + * Update tx trigger level + */ +int ath5k_hw_update_tx_triglevel(struct ath5k_hw *ah, bool increase) +{ +	u32 trigger_level, imr; +	int ret = -EIO; + +	ATH5K_TRACE(ah->ah_sc); + +	/* +	 * Disable interrupts by setting the mask +	 */ +	imr = ath5k_hw_set_intr(ah, ah->ah_imr & ~AR5K_INT_GLOBAL); + +	/*TODO: Boundary check on trigger_level*/ +	trigger_level = AR5K_REG_MS(ath5k_hw_reg_read(ah, AR5K_TXCFG), +			AR5K_TXCFG_TXFULL); + +	if (increase == false) { +		if (--trigger_level < AR5K_TUNE_MIN_TX_FIFO_THRES) +			goto done; +	} else +		trigger_level += +			((AR5K_TUNE_MAX_TX_FIFO_THRES - trigger_level) / 2); + +	/* +	 * Update trigger level on success +	 */ +	if (ah->ah_version == AR5K_AR5210) +		ath5k_hw_reg_write(ah, trigger_level, AR5K_TRIG_LVL); +	else +		AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG, +				AR5K_TXCFG_TXFULL, trigger_level); + +	ret = 0; + +done: +	/* +	 * Restore interrupt mask +	 */ +	ath5k_hw_set_intr(ah, imr); + +	return ret; +} + +/* + * Interrupt handling + */ + +/* + * Check if we have pending interrupts + */ +bool ath5k_hw_is_intr_pending(struct ath5k_hw *ah) +{ +	ATH5K_TRACE(ah->ah_sc); +	return ath5k_hw_reg_read(ah, AR5K_INTPEND); +} + +/* + * Get interrupt mask (ISR) + */ +int ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) +{ +	u32 data; + +	ATH5K_TRACE(ah->ah_sc); + +	/* +	 * Read interrupt status from the Interrupt Status register +	 * on 5210 +	 */ +	if (ah->ah_version == AR5K_AR5210) { +		data = ath5k_hw_reg_read(ah, AR5K_ISR); +		if (unlikely(data == AR5K_INT_NOCARD)) { +			*interrupt_mask = data; +			return -ENODEV; +		} +	} else { +		/* +		 * Read interrupt status from the Read-And-Clear shadow register +		 * Note: PISR/SISR Not available on 5210 +		 */ +		data = ath5k_hw_reg_read(ah, AR5K_RAC_PISR); +	} + +	/* +	 * Get abstract interrupt mask (driver-compatible) +	 */ +	*interrupt_mask = (data & AR5K_INT_COMMON) & ah->ah_imr; + +	if (unlikely(data == AR5K_INT_NOCARD)) +		return -ENODEV; + +	if (data & (AR5K_ISR_RXOK | AR5K_ISR_RXERR)) +		*interrupt_mask |= AR5K_INT_RX; + +	if (data & (AR5K_ISR_TXOK | AR5K_ISR_TXERR +		| AR5K_ISR_TXDESC | AR5K_ISR_TXEOL)) +		*interrupt_mask |= AR5K_INT_TX; + +	if (ah->ah_version != AR5K_AR5210) { +		/*HIU = Host Interface Unit (PCI etc)*/ +		if (unlikely(data & (AR5K_ISR_HIUERR))) +			*interrupt_mask |= AR5K_INT_FATAL; + +		/*Beacon Not Ready*/ +		if (unlikely(data & (AR5K_ISR_BNR))) +			*interrupt_mask |= AR5K_INT_BNR; +	} + +	/* +	 * XXX: BMISS interrupts may occur after association. +	 * I found this on 5210 code but it needs testing. If this is +	 * true we should disable them before assoc and re-enable them +	 * after a successfull assoc + some jiffies. +	 */ +#if 0 +	interrupt_mask &= ~AR5K_INT_BMISS; +#endif + +	/* +	 * In case we didn't handle anything, +	 * print the register value. +	 */ +	if (unlikely(*interrupt_mask == 0 && net_ratelimit())) +		ATH5K_PRINTF("0x%08x\n", data); + +	return 0; +} + +/* + * Set interrupt mask + */ +enum ath5k_int ath5k_hw_set_intr(struct ath5k_hw *ah, enum ath5k_int new_mask) +{ +	enum ath5k_int old_mask, int_mask; + +	/* +	 * Disable card interrupts to prevent any race conditions +	 * (they will be re-enabled afterwards). +	 */ +	ath5k_hw_reg_write(ah, AR5K_IER_DISABLE, AR5K_IER); + +	old_mask = ah->ah_imr; + +	/* +	 * Add additional, chipset-dependent interrupt mask flags +	 * and write them to the IMR (interrupt mask register). +	 */ +	int_mask = new_mask & AR5K_INT_COMMON; + +	if (new_mask & AR5K_INT_RX) +		int_mask |= AR5K_IMR_RXOK | AR5K_IMR_RXERR | AR5K_IMR_RXORN | +			AR5K_IMR_RXDESC; + +	if (new_mask & AR5K_INT_TX) +		int_mask |= AR5K_IMR_TXOK | AR5K_IMR_TXERR | AR5K_IMR_TXDESC | +			AR5K_IMR_TXURN; + +	if (ah->ah_version != AR5K_AR5210) { +		if (new_mask & AR5K_INT_FATAL) { +			int_mask |= AR5K_IMR_HIUERR; +			AR5K_REG_ENABLE_BITS(ah, AR5K_SIMR2, AR5K_SIMR2_MCABT | +					AR5K_SIMR2_SSERR | AR5K_SIMR2_DPERR); +		} +	} + +	ath5k_hw_reg_write(ah, int_mask, AR5K_PIMR); + +	/* Store new interrupt mask */ +	ah->ah_imr = new_mask; + +	/* ..re-enable interrupts */ +	ath5k_hw_reg_write(ah, AR5K_IER_ENABLE, AR5K_IER); + +	return old_mask; +} + + +/*************************\ +  EEPROM access functions +\*************************/ + +/* + * Read from eeprom + */ +static int ath5k_hw_eeprom_read(struct ath5k_hw *ah, u32 offset, u16 *data) +{ +	u32 status, timeout; + +	ATH5K_TRACE(ah->ah_sc); +	/* +	 * Initialize EEPROM access +	 */ +	if (ah->ah_version == AR5K_AR5210) { +		AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_EEAE); +		(void)ath5k_hw_reg_read(ah, AR5K_EEPROM_BASE + (4 * offset)); +	} else { +		ath5k_hw_reg_write(ah, offset, AR5K_EEPROM_BASE); +		AR5K_REG_ENABLE_BITS(ah, AR5K_EEPROM_CMD, +				AR5K_EEPROM_CMD_READ); +	} + +	for (timeout = AR5K_TUNE_REGISTER_TIMEOUT; timeout > 0; timeout--) { +		status = ath5k_hw_reg_read(ah, AR5K_EEPROM_STATUS); +		if (status & AR5K_EEPROM_STAT_RDDONE) { +			if (status & AR5K_EEPROM_STAT_RDERR) +				return -EIO; +			*data = (u16)(ath5k_hw_reg_read(ah, AR5K_EEPROM_DATA) & +					0xffff); +			return 0; +		} +		udelay(15); +	} + +	return -ETIMEDOUT; +} + +/* + * Write to eeprom - currently disabled, use at your own risk + */ +static int ath5k_hw_eeprom_write(struct ath5k_hw *ah, u32 offset, u16 data) +{ +#if 0 +	u32 status, timeout; + +	ATH5K_TRACE(ah->ah_sc); + +	/* +	 * Initialize eeprom access +	 */ + +	if (ah->ah_version == AR5K_AR5210) { +		AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_EEAE); +	} else { +		AR5K_REG_ENABLE_BITS(ah, AR5K_EEPROM_CMD, +				AR5K_EEPROM_CMD_RESET); +	} + +	/* +	 * Write data to data register +	 */ + +	if (ah->ah_version == AR5K_AR5210) { +		ath5k_hw_reg_write(ah, data, AR5K_EEPROM_BASE + (4 * offset)); +	} else { +		ath5k_hw_reg_write(ah, offset, AR5K_EEPROM_BASE); +		ath5k_hw_reg_write(ah, data, AR5K_EEPROM_DATA); +		AR5K_REG_ENABLE_BITS(ah, AR5K_EEPROM_CMD, +				AR5K_EEPROM_CMD_WRITE); +	} + +	/* +	 * Check status +	 */ + +	for (timeout = AR5K_TUNE_REGISTER_TIMEOUT; timeout > 0; timeout--) { +		status = ath5k_hw_reg_read(ah, AR5K_EEPROM_STATUS); +		if (status & AR5K_EEPROM_STAT_WRDONE) { +			if (status & AR5K_EEPROM_STAT_WRERR) +				return EIO; +			return 0; +		} +		udelay(15); +	} +#endif +	ATH5K_ERR(ah->ah_sc, "EEPROM Write is disabled!"); +	return -EIO; +} + +/* + * Translate binary channel representation in EEPROM to frequency + */ +static u16 ath5k_eeprom_bin2freq(struct ath5k_hw *ah, u16 bin, unsigned int mode) +{ +	u16 val; + +	if (bin == AR5K_EEPROM_CHANNEL_DIS) +		return bin; + +	if (mode == AR5K_EEPROM_MODE_11A) { +		if (ah->ah_ee_version > AR5K_EEPROM_VERSION_3_2) +			val = (5 * bin) + 4800; +		else +			val = bin > 62 ? (10 * 62) + (5 * (bin - 62)) + 5100 : +				(bin * 10) + 5100; +	} else { +		if (ah->ah_ee_version > AR5K_EEPROM_VERSION_3_2) +			val = bin + 2300; +		else +			val = bin + 2400; +	} + +	return val; +} + +/* + * Read antenna infos from eeprom + */ +static int ath5k_eeprom_read_ants(struct ath5k_hw *ah, u32 *offset, +		unsigned int mode) +{ +	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; +	u32 o = *offset; +	u16 val; +	int ret, i = 0; + +	AR5K_EEPROM_READ(o++, val); +	ee->ee_switch_settling[mode]	= (val >> 8) & 0x7f; +	ee->ee_ant_tx_rx[mode]		= (val >> 2) & 0x3f; +	ee->ee_ant_control[mode][i]	= (val << 4) & 0x3f; + +	AR5K_EEPROM_READ(o++, val); +	ee->ee_ant_control[mode][i++]	|= (val >> 12) & 0xf; +	ee->ee_ant_control[mode][i++]	= (val >> 6) & 0x3f; +	ee->ee_ant_control[mode][i++]	= val & 0x3f; + +	AR5K_EEPROM_READ(o++, val); +	ee->ee_ant_control[mode][i++]	= (val >> 10) & 0x3f; +	ee->ee_ant_control[mode][i++]	= (val >> 4) & 0x3f; +	ee->ee_ant_control[mode][i]	= (val << 2) & 0x3f; + +	AR5K_EEPROM_READ(o++, val); +	ee->ee_ant_control[mode][i++]	|= (val >> 14) & 0x3; +	ee->ee_ant_control[mode][i++]	= (val >> 8) & 0x3f; +	ee->ee_ant_control[mode][i++]	= (val >> 2) & 0x3f; +	ee->ee_ant_control[mode][i]	= (val << 4) & 0x3f; + +	AR5K_EEPROM_READ(o++, val); +	ee->ee_ant_control[mode][i++]	|= (val >> 12) & 0xf; +	ee->ee_ant_control[mode][i++]	= (val >> 6) & 0x3f; +	ee->ee_ant_control[mode][i++]	= val & 0x3f; + +	/* Get antenna modes */ +	ah->ah_antenna[mode][0] = +	    (ee->ee_ant_control[mode][0] << 4) | 0x1; +	ah->ah_antenna[mode][AR5K_ANT_FIXED_A] = +	     ee->ee_ant_control[mode][1] 	| +	    (ee->ee_ant_control[mode][2] << 6) 	| +	    (ee->ee_ant_control[mode][3] << 12) | +	    (ee->ee_ant_control[mode][4] << 18) | +	    (ee->ee_ant_control[mode][5] << 24); +	ah->ah_antenna[mode][AR5K_ANT_FIXED_B] = +	     ee->ee_ant_control[mode][6] 	| +	    (ee->ee_ant_control[mode][7] << 6) 	| +	    (ee->ee_ant_control[mode][8] << 12) | +	    (ee->ee_ant_control[mode][9] << 18) | +	    (ee->ee_ant_control[mode][10] << 24); + +	/* return new offset */ +	*offset = o; + +	return 0; +} + +/* + * Read supported modes from eeprom + */ +static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset, +		unsigned int mode) +{ +	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; +	u32 o = *offset; +	u16 val; +	int ret; + +	AR5K_EEPROM_READ(o++, val); +	ee->ee_tx_end2xlna_enable[mode]	= (val >> 8) & 0xff; +	ee->ee_thr_62[mode]		= val & 0xff; + +	if (ah->ah_ee_version <= AR5K_EEPROM_VERSION_3_2) +		ee->ee_thr_62[mode] = mode == AR5K_EEPROM_MODE_11A ? 15 : 28; + +	AR5K_EEPROM_READ(o++, val); +	ee->ee_tx_end2xpa_disable[mode]	= (val >> 8) & 0xff; +	ee->ee_tx_frm2xpa_enable[mode]	= val & 0xff; + +	AR5K_EEPROM_READ(o++, val); +	ee->ee_pga_desired_size[mode]	= (val >> 8) & 0xff; + +	if ((val & 0xff) & 0x80) +		ee->ee_noise_floor_thr[mode] = -((((val & 0xff) ^ 0xff)) + 1); +	else +		ee->ee_noise_floor_thr[mode] = val & 0xff; + +	if (ah->ah_ee_version <= AR5K_EEPROM_VERSION_3_2) +		ee->ee_noise_floor_thr[mode] = +		    mode == AR5K_EEPROM_MODE_11A ? -54 : -1; + +	AR5K_EEPROM_READ(o++, val); +	ee->ee_xlna_gain[mode]		= (val >> 5) & 0xff; +	ee->ee_x_gain[mode]		= (val >> 1) & 0xf; +	ee->ee_xpd[mode]		= val & 0x1; + +	if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) +		ee->ee_fixed_bias[mode] = (val >> 13) & 0x1; + +	if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_3_3) { +		AR5K_EEPROM_READ(o++, val); +		ee->ee_false_detect[mode] = (val >> 6) & 0x7f; + +		if (mode == AR5K_EEPROM_MODE_11A) +			ee->ee_xr_power[mode] = val & 0x3f; +		else { +			ee->ee_ob[mode][0] = val & 0x7; +			ee->ee_db[mode][0] = (val >> 3) & 0x7; +		} +	} + +	if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_4) { +		ee->ee_i_gain[mode] = AR5K_EEPROM_I_GAIN; +		ee->ee_cck_ofdm_power_delta = AR5K_EEPROM_CCK_OFDM_DELTA; +	} else { +		ee->ee_i_gain[mode] = (val >> 13) & 0x7; + +		AR5K_EEPROM_READ(o++, val); +		ee->ee_i_gain[mode] |= (val << 3) & 0x38; + +		if (mode == AR5K_EEPROM_MODE_11G) +			ee->ee_cck_ofdm_power_delta = (val >> 3) & 0xff; +	} + +	if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0 && +			mode == AR5K_EEPROM_MODE_11A) { +		ee->ee_i_cal[mode] = (val >> 8) & 0x3f; +		ee->ee_q_cal[mode] = (val >> 3) & 0x1f; +	} + +	if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_6 && +	    mode == AR5K_EEPROM_MODE_11G) +		ee->ee_scaled_cck_delta = (val >> 11) & 0x1f; + +	/* return new offset */ +	*offset = o; + +	return 0; +} + +/* + * Initialize eeprom & capabilities structs + */ +static int ath5k_eeprom_init(struct ath5k_hw *ah) +{ +	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; +	unsigned int mode, i; +	int ret; +	u32 offset; +	u16 val; + +	/* Initial TX thermal adjustment values */ +	ee->ee_tx_clip = 4; +	ee->ee_pwd_84 = ee->ee_pwd_90 = 1; +	ee->ee_gain_select = 1; + +	/* +	 * Read values from EEPROM and store them in the capability structure +	 */ +	AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MAGIC, ee_magic); +	AR5K_EEPROM_READ_HDR(AR5K_EEPROM_PROTECT, ee_protect); +	AR5K_EEPROM_READ_HDR(AR5K_EEPROM_REG_DOMAIN, ee_regdomain); +	AR5K_EEPROM_READ_HDR(AR5K_EEPROM_VERSION, ee_version); +	AR5K_EEPROM_READ_HDR(AR5K_EEPROM_HDR, ee_header); + +	/* Return if we have an old EEPROM */ +	if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_0) +		return 0; + +#ifdef notyet +	/* +	 * Validate the checksum of the EEPROM date. There are some +	 * devices with invalid EEPROMs. +	 */ +	for (cksum = 0, offset = 0; offset < AR5K_EEPROM_INFO_MAX; offset++) { +		AR5K_EEPROM_READ(AR5K_EEPROM_INFO(offset), val); +		cksum ^= val; +	} +	if (cksum != AR5K_EEPROM_INFO_CKSUM) { +		ATH5K_ERR(ah->ah_sc, "Invalid EEPROM checksum 0x%04x\n", cksum); +		return -EIO; +	} +#endif + +	AR5K_EEPROM_READ_HDR(AR5K_EEPROM_ANT_GAIN(ah->ah_ee_version), +	    ee_ant_gain); + +	if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) { +		AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC0, ee_misc0); +		AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC1, ee_misc1); +	} + +	if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_3) { +		AR5K_EEPROM_READ(AR5K_EEPROM_OBDB0_2GHZ, val); +		ee->ee_ob[AR5K_EEPROM_MODE_11B][0] = val & 0x7; +		ee->ee_db[AR5K_EEPROM_MODE_11B][0] = (val >> 3) & 0x7; + +		AR5K_EEPROM_READ(AR5K_EEPROM_OBDB1_2GHZ, val); +		ee->ee_ob[AR5K_EEPROM_MODE_11G][0] = val & 0x7; +		ee->ee_db[AR5K_EEPROM_MODE_11G][0] = (val >> 3) & 0x7; +	} + +	/* +	 * Get conformance test limit values +	 */ +	offset = AR5K_EEPROM_CTL(ah->ah_ee_version); +	ee->ee_ctls = AR5K_EEPROM_N_CTLS(ah->ah_ee_version); + +	for (i = 0; i < ee->ee_ctls; i++) { +		AR5K_EEPROM_READ(offset++, val); +		ee->ee_ctl[i] = (val >> 8) & 0xff; +		ee->ee_ctl[i + 1] = val & 0xff; +	} + +	/* +	 * Get values for 802.11a (5GHz) +	 */ +	mode = AR5K_EEPROM_MODE_11A; + +	ee->ee_turbo_max_power[mode] = +			AR5K_EEPROM_HDR_T_5GHZ_DBM(ee->ee_header); + +	offset = AR5K_EEPROM_MODES_11A(ah->ah_ee_version); + +	ret = ath5k_eeprom_read_ants(ah, &offset, mode); +	if (ret) +		return ret; + +	AR5K_EEPROM_READ(offset++, val); +	ee->ee_adc_desired_size[mode]	= (s8)((val >> 8) & 0xff); +	ee->ee_ob[mode][3]		= (val >> 5) & 0x7; +	ee->ee_db[mode][3]		= (val >> 2) & 0x7; +	ee->ee_ob[mode][2]		= (val << 1) & 0x7; + +	AR5K_EEPROM_READ(offset++, val); +	ee->ee_ob[mode][2]		|= (val >> 15) & 0x1; +	ee->ee_db[mode][2]		= (val >> 12) & 0x7; +	ee->ee_ob[mode][1]		= (val >> 9) & 0x7; +	ee->ee_db[mode][1]		= (val >> 6) & 0x7; +	ee->ee_ob[mode][0]		= (val >> 3) & 0x7; +	ee->ee_db[mode][0]		= val & 0x7; + +	ret = ath5k_eeprom_read_modes(ah, &offset, mode); +	if (ret) +		return ret; + +	if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) { +		AR5K_EEPROM_READ(offset++, val); +		ee->ee_margin_tx_rx[mode] = val & 0x3f; +	} + +	/* +	 * Get values for 802.11b (2.4GHz) +	 */ +	mode = AR5K_EEPROM_MODE_11B; +	offset = AR5K_EEPROM_MODES_11B(ah->ah_ee_version); + +	ret = ath5k_eeprom_read_ants(ah, &offset, mode); +	if (ret) +		return ret; + +	AR5K_EEPROM_READ(offset++, val); +	ee->ee_adc_desired_size[mode]	= (s8)((val >> 8) & 0xff); +	ee->ee_ob[mode][1]		= (val >> 4) & 0x7; +	ee->ee_db[mode][1]		= val & 0x7; + +	ret = ath5k_eeprom_read_modes(ah, &offset, mode); +	if (ret) +		return ret; + +	if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) { +		AR5K_EEPROM_READ(offset++, val); +		ee->ee_cal_pier[mode][0] = +			ath5k_eeprom_bin2freq(ah, val & 0xff, mode); +		ee->ee_cal_pier[mode][1] = +			ath5k_eeprom_bin2freq(ah, (val >> 8) & 0xff, mode); + +		AR5K_EEPROM_READ(offset++, val); +		ee->ee_cal_pier[mode][2] = +			ath5k_eeprom_bin2freq(ah, val & 0xff, mode); +	} + +	if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) +		ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f; + +	/* +	 * Get values for 802.11g (2.4GHz) +	 */ +	mode = AR5K_EEPROM_MODE_11G; +	offset = AR5K_EEPROM_MODES_11G(ah->ah_ee_version); + +	ret = ath5k_eeprom_read_ants(ah, &offset, mode); +	if (ret) +		return ret; + +	AR5K_EEPROM_READ(offset++, val); +	ee->ee_adc_desired_size[mode]	= (s8)((val >> 8) & 0xff); +	ee->ee_ob[mode][1]		= (val >> 4) & 0x7; +	ee->ee_db[mode][1]		= val & 0x7; + +	ret = ath5k_eeprom_read_modes(ah, &offset, mode); +	if (ret) +		return ret; + +	if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) { +		AR5K_EEPROM_READ(offset++, val); +		ee->ee_cal_pier[mode][0] = +			ath5k_eeprom_bin2freq(ah, val & 0xff, mode); +		ee->ee_cal_pier[mode][1] = +			ath5k_eeprom_bin2freq(ah, (val >> 8) & 0xff, mode); + +		AR5K_EEPROM_READ(offset++, val); +		ee->ee_turbo_max_power[mode] = val & 0x7f; +		ee->ee_xr_power[mode] = (val >> 7) & 0x3f; + +		AR5K_EEPROM_READ(offset++, val); +		ee->ee_cal_pier[mode][2] = +			ath5k_eeprom_bin2freq(ah, val & 0xff, mode); + +		if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) +			ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f; + +		AR5K_EEPROM_READ(offset++, val); +		ee->ee_i_cal[mode] = (val >> 8) & 0x3f; +		ee->ee_q_cal[mode] = (val >> 3) & 0x1f; + +		if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_2) { +			AR5K_EEPROM_READ(offset++, val); +			ee->ee_cck_ofdm_gain_delta = val & 0xff; +		} +	} + +	/* +	 * Read 5GHz EEPROM channels +	 */ + +	return 0; +} + +/* + * Read the MAC address from eeprom + */ +static int ath5k_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac) +{ +	u8 mac_d[ETH_ALEN]; +	u32 total, offset; +	u16 data; +	int octet, ret; + +	memset(mac, 0, ETH_ALEN); +	memset(mac_d, 0, ETH_ALEN); + +	ret = ath5k_hw_eeprom_read(ah, 0x20, &data); +	if (ret) +		return ret; + +	for (offset = 0x1f, octet = 0, total = 0; offset >= 0x1d; offset--) { +		ret = ath5k_hw_eeprom_read(ah, offset, &data); +		if (ret) +			return ret; + +		total += data; +		mac_d[octet + 1] = data & 0xff; +		mac_d[octet] = data >> 8; +		octet += 2; +	} + +	memcpy(mac, mac_d, ETH_ALEN); + +	if (!total || total == 3 * 0xffff) +		return -EINVAL; + +	return 0; +} + +/* + * Read/Write regulatory domain + */ +static bool ath5k_eeprom_regulation_domain(struct ath5k_hw *ah, bool write, +	enum ath5k_regdom *regdomain) +{ +	u16 ee_regdomain; + +	/* Read current value */ +	if (write != true) { +		ee_regdomain = ah->ah_capabilities.cap_eeprom.ee_regdomain; +		*regdomain = ath5k_regdom_to_ieee(ee_regdomain); +		return true; +	} + +	ee_regdomain = ath5k_regdom_from_ieee(*regdomain); + +	/* Try to write a new value */ +	if (ah->ah_capabilities.cap_eeprom.ee_protect & +			AR5K_EEPROM_PROTECT_WR_128_191) +		return false; +	if (ath5k_hw_eeprom_write(ah, AR5K_EEPROM_REG_DOMAIN, ee_regdomain)!=0) +		return false; + +	ah->ah_capabilities.cap_eeprom.ee_regdomain = ee_regdomain; + +	return true; +} + +/* + * Use the above to write a new regulatory domain + */ +int ath5k_hw_set_regdomain(struct ath5k_hw *ah, u16 regdomain) +{ +	enum ath5k_regdom ieee_regdomain; + +	ieee_regdomain = ath5k_regdom_to_ieee(regdomain); + +	if (ath5k_eeprom_regulation_domain(ah, true, &ieee_regdomain) == true) +		return 0; + +	return -EIO; +} + +/* + * Fill the capabilities struct + */ +static int ath5k_hw_get_capabilities(struct ath5k_hw *ah) +{ +	u16 ee_header; + +	ATH5K_TRACE(ah->ah_sc); +	/* Capabilities stored in the EEPROM */ +	ee_header = ah->ah_capabilities.cap_eeprom.ee_header; + +	if (ah->ah_version == AR5K_AR5210) { +		/* +		 * Set radio capabilities +		 * (The AR5110 only supports the middle 5GHz band) +		 */ +		ah->ah_capabilities.cap_range.range_5ghz_min = 5120; +		ah->ah_capabilities.cap_range.range_5ghz_max = 5430; +		ah->ah_capabilities.cap_range.range_2ghz_min = 0; +		ah->ah_capabilities.cap_range.range_2ghz_max = 0; + +		/* Set supported modes */ +		__set_bit(MODE_IEEE80211A, ah->ah_capabilities.cap_mode); +		__set_bit(MODE_ATHEROS_TURBO, ah->ah_capabilities.cap_mode); +	} else { +		/* +		 * XXX The tranceiver supports frequencies from 4920 to 6100GHz +		 * XXX and from 2312 to 2732GHz. There are problems with the +		 * XXX current ieee80211 implementation because the IEEE +		 * XXX channel mapping does not support negative channel +		 * XXX numbers (2312MHz is channel -19). Of course, this +		 * XXX doesn't matter because these channels are out of range +		 * XXX but some regulation domains like MKK (Japan) will +		 * XXX support frequencies somewhere around 4.8GHz. +		 */ + +		/* +		 * Set radio capabilities +		 */ + +		if (AR5K_EEPROM_HDR_11A(ee_header)) { +			ah->ah_capabilities.cap_range.range_5ghz_min = 5005; /* 4920 */ +			ah->ah_capabilities.cap_range.range_5ghz_max = 6100; + +			/* Set supported modes */ +			__set_bit(MODE_IEEE80211A, +					ah->ah_capabilities.cap_mode); +			__set_bit(MODE_ATHEROS_TURBO, +					ah->ah_capabilities.cap_mode); +			if (ah->ah_version == AR5K_AR5212) +				__set_bit(MODE_ATHEROS_TURBOG, +						ah->ah_capabilities.cap_mode); +		} + +		/* Enable  802.11b if a 2GHz capable radio (2111/5112) is +		 * connected */ +		if (AR5K_EEPROM_HDR_11B(ee_header) || +				AR5K_EEPROM_HDR_11G(ee_header)) { +			ah->ah_capabilities.cap_range.range_2ghz_min = 2412; /* 2312 */ +			ah->ah_capabilities.cap_range.range_2ghz_max = 2732; + +			if (AR5K_EEPROM_HDR_11B(ee_header)) +				__set_bit(MODE_IEEE80211B, +						ah->ah_capabilities.cap_mode); + +			if (AR5K_EEPROM_HDR_11G(ee_header)) +				__set_bit(MODE_IEEE80211G, +						ah->ah_capabilities.cap_mode); +		} +	} + +	/* GPIO */ +	ah->ah_gpio_npins = AR5K_NUM_GPIO; + +	/* Set number of supported TX queues */ +	if (ah->ah_version == AR5K_AR5210) +		ah->ah_capabilities.cap_queues.q_tx_num = +			AR5K_NUM_TX_QUEUES_NOQCU; +	else +		ah->ah_capabilities.cap_queues.q_tx_num = AR5K_NUM_TX_QUEUES; + +	return 0; +} + +/*********************************\ +  Protocol Control Unit Functions +\*********************************/ + +/* + * Set Operation mode + */ +int ath5k_hw_set_opmode(struct ath5k_hw *ah) +{ +	u32 pcu_reg, beacon_reg, low_id, high_id; + +	pcu_reg = 0; +	beacon_reg = 0; + +	ATH5K_TRACE(ah->ah_sc); + +	switch (ah->ah_op_mode) { +	case IEEE80211_IF_TYPE_IBSS: +		pcu_reg |= AR5K_STA_ID1_ADHOC | AR5K_STA_ID1_DESC_ANTENNA | +			(ah->ah_version == AR5K_AR5210 ? +				AR5K_STA_ID1_NO_PSPOLL : 0); +		beacon_reg |= AR5K_BCR_ADHOC; +		break; + +	case IEEE80211_IF_TYPE_AP: +		pcu_reg |= AR5K_STA_ID1_AP | AR5K_STA_ID1_RTS_DEF_ANTENNA | +			(ah->ah_version == AR5K_AR5210 ? +				AR5K_STA_ID1_NO_PSPOLL : 0); +		beacon_reg |= AR5K_BCR_AP; +		break; + +	case IEEE80211_IF_TYPE_STA: +		pcu_reg |= AR5K_STA_ID1_DEFAULT_ANTENNA | +			(ah->ah_version == AR5K_AR5210 ? +				AR5K_STA_ID1_PWR_SV : 0); +	case IEEE80211_IF_TYPE_MNTR: +		pcu_reg |= AR5K_STA_ID1_DEFAULT_ANTENNA | +			(ah->ah_version == AR5K_AR5210 ? +				AR5K_STA_ID1_NO_PSPOLL : 0); +		break; + +	default: +		return -EINVAL; +	} + +	/* +	 * Set PCU registers +	 */ +	low_id = AR5K_LOW_ID(ah->ah_sta_id); +	high_id = AR5K_HIGH_ID(ah->ah_sta_id); +	ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0); +	ath5k_hw_reg_write(ah, pcu_reg | high_id, AR5K_STA_ID1); + +	/* +	 * Set Beacon Control Register on 5210 +	 */ +	if (ah->ah_version == AR5K_AR5210) +		ath5k_hw_reg_write(ah, beacon_reg, AR5K_BCR); + +	return 0; +} + +/* + * BSSID Functions + */ + +/* + * Get station id + */ +void ath5k_hw_get_lladdr(struct ath5k_hw *ah, u8 *mac) +{ +	ATH5K_TRACE(ah->ah_sc); +	memcpy(mac, ah->ah_sta_id, ETH_ALEN); +} + +/* + * Set station id + */ +int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac) +{ +	u32 low_id, high_id; + +	ATH5K_TRACE(ah->ah_sc); +	/* Set new station ID */ +	memcpy(ah->ah_sta_id, mac, ETH_ALEN); + +	low_id = AR5K_LOW_ID(mac); +	high_id = AR5K_HIGH_ID(mac); + +	ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0); +	ath5k_hw_reg_write(ah, high_id, AR5K_STA_ID1); + +	return 0; +} + +/* + * Set BSSID + */ +void ath5k_hw_set_associd(struct ath5k_hw *ah, const u8 *bssid, u16 assoc_id) +{ +	u32 low_id, high_id; +	u16 tim_offset = 0; + +	/* +	 * Set simple BSSID mask on 5212 +	 */ +	if (ah->ah_version == AR5K_AR5212) { +		ath5k_hw_reg_write(ah, 0xfffffff, AR5K_BSS_IDM0); +		ath5k_hw_reg_write(ah, 0xfffffff, AR5K_BSS_IDM1); +	} + +	/* +	 * Set BSSID which triggers the "SME Join" operation +	 */ +	low_id = AR5K_LOW_ID(bssid); +	high_id = AR5K_HIGH_ID(bssid); +	ath5k_hw_reg_write(ah, low_id, AR5K_BSS_ID0); +	ath5k_hw_reg_write(ah, high_id | ((assoc_id & 0x3fff) << +				AR5K_BSS_ID1_AID_S), AR5K_BSS_ID1); + +	if (assoc_id == 0) { +		ath5k_hw_disable_pspoll(ah); +		return; +	} + +	AR5K_REG_WRITE_BITS(ah, AR5K_BEACON, AR5K_BEACON_TIM, +			tim_offset ? tim_offset + 4 : 0); + +	ath5k_hw_enable_pspoll(ah, NULL, 0); +} +/** + * ath5k_hw_set_bssid_mask - set common bits we should listen to + * + * The bssid_mask is a utility used by AR5212 hardware to inform the hardware + * which bits of the interface's MAC address should be looked at when trying + * to decide which packets to ACK. In station mode every bit matters. In AP + * mode with a single BSS every bit matters as well. In AP mode with + * multiple BSSes not every bit matters. + * + * @ah: the &struct ath5k_hw + * @mask: the bssid_mask, a u8 array of size ETH_ALEN + * + * Note that this is a simple filter and *does* not filter out all + * relevant frames. Some non-relevant frames will get through, probability + * jocks are welcomed to compute. + * + * When handling multiple BSSes (or VAPs) you can get the BSSID mask by + * computing the set of: + * + *     ~ ( MAC XOR BSSID ) + * + * When you do this you are essentially computing the common bits. Later it + * is assumed the harware will "and" (&) the BSSID mask with the MAC address + * to obtain the relevant bits which should match on the destination frame. + * + * Simple example: on your card you have have two BSSes you have created with + * BSSID-01 and BSSID-02. Lets assume BSSID-01 will not use the MAC address. + * There is another BSSID-03 but you are not part of it. For simplicity's sake, + * assuming only 4 bits for a mac address and for BSSIDs you can then have: + * + *                  \ + * MAC:                0001 | + * BSSID-01:   0100 | --> Belongs to us + * BSSID-02:   1001 | + *                  / + * ------------------- + * BSSID-03:   0110  | --> External + * ------------------- + * + * Our bssid_mask would then be: + * + *             On loop iteration for BSSID-01: + *             ~(0001 ^ 0100)  -> ~(0101) + *                             ->   1010 + *             bssid_mask      =    1010 + * + *             On loop iteration for BSSID-02: + *             bssid_mask &= ~(0001   ^   1001) + *             bssid_mask =   (1010)  & ~(0001 ^ 1001) + *             bssid_mask =   (1010)  & ~(1001) + *             bssid_mask =   (1010)  &  (0110) + *             bssid_mask =   0010 + * + * A bssid_mask of 0010 means "only pay attention to the second least + * significant bit". This is because its the only bit common + * amongst the MAC and all BSSIDs we support. To findout what the real + * common bit is we can simply "&" the bssid_mask now with any BSSID we have + * or our MAC address (we assume the hardware uses the MAC address). + * + * Now, suppose there's an incoming frame for BSSID-03: + * + * IFRAME-01:  0110 + * + * An easy eye-inspeciton of this already should tell you that this frame + * will not pass our check. This is beacuse the bssid_mask tells the + * hardware to only look at the second least significant bit and the + * common bit amongst the MAC and BSSIDs is 0, this frame has the 2nd LSB + * as 1, which does not match 0. + * + * So with IFRAME-01 we *assume* the hardware will do: + * + *     allow = (IFRAME-01 & bssid_mask) == (bssid_mask & MAC) ? 1 : 0; + *  --> allow = (0110 & 0010) == (0010 & 0001) ? 1 : 0; + *  --> allow = (0010) == 0000 ? 1 : 0; + *  --> allow = 0 + * + *  Lets now test a frame that should work: + * + * IFRAME-02:  0001 (we should allow) + * + *     allow = (0001 & 1010) == 1010 + * + *     allow = (IFRAME-02 & bssid_mask) == (bssid_mask & MAC) ? 1 : 0; + *  --> allow = (0001 & 0010) ==  (0010 & 0001) ? 1 :0; + *  --> allow = (0010) == (0010) + *  --> allow = 1 + * + * Other examples: + * + * IFRAME-03:  0100 --> allowed + * IFRAME-04:  1001 --> allowed + * IFRAME-05:  1101 --> allowed but its not for us!!! + * + */ +int ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask) +{ +	u32 low_id, high_id; +	ATH5K_TRACE(ah->ah_sc); + +	if (ah->ah_version == AR5K_AR5212) { +		low_id = AR5K_LOW_ID(mask); +		high_id = AR5K_HIGH_ID(mask); + +		ath5k_hw_reg_write(ah, low_id, AR5K_BSS_IDM0); +		ath5k_hw_reg_write(ah, high_id, AR5K_BSS_IDM1); + +		return 0; +	} + +	return -EIO; +} + +/* + * Receive start/stop functions + */ + +/* + * Start receive on PCU + */ +void ath5k_hw_start_rx_pcu(struct ath5k_hw *ah) +{ +	ATH5K_TRACE(ah->ah_sc); +	AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW, AR5K_DIAG_SW_DIS_RX); +} + +/* + * Stop receive on PCU + */ +void ath5k_hw_stop_pcu_recv(struct ath5k_hw *ah) +{ +	ATH5K_TRACE(ah->ah_sc); +	AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW, AR5K_DIAG_SW_DIS_RX); +} + +/* + * RX Filter functions + */ + +/* + * Set multicast filter + */ +void ath5k_hw_set_mcast_filter(struct ath5k_hw *ah, u32 filter0, u32 filter1) +{ +	ATH5K_TRACE(ah->ah_sc); +	/* Set the multicat filter */ +	ath5k_hw_reg_write(ah, filter0, AR5K_MCAST_FILTER0); +	ath5k_hw_reg_write(ah, filter1, AR5K_MCAST_FILTER1); +} + +/* + * Set multicast filter by index + */ +int ath5k_hw_set_mcast_filterindex(struct ath5k_hw *ah, u32 index) +{ + +	ATH5K_TRACE(ah->ah_sc); +	if (index >= 64) +		return -EINVAL; +	else if (index >= 32) +		AR5K_REG_ENABLE_BITS(ah, AR5K_MCAST_FILTER1, +				(1 << (index - 32))); +	else +		AR5K_REG_ENABLE_BITS(ah, AR5K_MCAST_FILTER0, (1 << index)); + +	return 0; +} + +/* + * Clear Multicast filter by index + */ +int ath5k_hw_clear_mcast_filter_idx(struct ath5k_hw *ah, u32 index) +{ + +	ATH5K_TRACE(ah->ah_sc); +	if (index >= 64) +		return -EINVAL; +	else if (index >= 32) +		AR5K_REG_DISABLE_BITS(ah, AR5K_MCAST_FILTER1, +				(1 << (index - 32))); +	else +		AR5K_REG_DISABLE_BITS(ah, AR5K_MCAST_FILTER0, (1 << index)); + +	return 0; +} + +/* + * Get current rx filter + */ +u32 ath5k_hw_get_rx_filter(struct ath5k_hw *ah) +{ +	u32 data, filter = 0; + +	ATH5K_TRACE(ah->ah_sc); +	filter = ath5k_hw_reg_read(ah, AR5K_RX_FILTER); + +	/*Radar detection for 5212*/ +	if (ah->ah_version == AR5K_AR5212) { +		data = ath5k_hw_reg_read(ah, AR5K_PHY_ERR_FIL); + +		if (data & AR5K_PHY_ERR_FIL_RADAR) +			filter |= AR5K_RX_FILTER_RADARERR; +		if (data & (AR5K_PHY_ERR_FIL_OFDM | AR5K_PHY_ERR_FIL_CCK)) +			filter |= AR5K_RX_FILTER_PHYERR; +	} + +	return filter; +} + +/* + * Set rx filter + */ +void ath5k_hw_set_rx_filter(struct ath5k_hw *ah, u32 filter) +{ +	u32 data = 0; + +	ATH5K_TRACE(ah->ah_sc); + +	/* Set PHY error filter register on 5212*/ +	if (ah->ah_version == AR5K_AR5212) { +		if (filter & AR5K_RX_FILTER_RADARERR) +			data |= AR5K_PHY_ERR_FIL_RADAR; +		if (filter & AR5K_RX_FILTER_PHYERR) +			data |= AR5K_PHY_ERR_FIL_OFDM | AR5K_PHY_ERR_FIL_CCK; +	} + +	/* +	 * The AR5210 uses promiscous mode to detect radar activity +	 */ +	if (ah->ah_version == AR5K_AR5210 && +			(filter & AR5K_RX_FILTER_RADARERR)) { +		filter &= ~AR5K_RX_FILTER_RADARERR; +		filter |= AR5K_RX_FILTER_PROM; +	} + +	/*Zero length DMA*/ +	if (data) +		AR5K_REG_ENABLE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_ZLFDMA); +	else +		AR5K_REG_DISABLE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_ZLFDMA); + +	/*Write RX Filter register*/ +	ath5k_hw_reg_write(ah, filter & 0xff, AR5K_RX_FILTER); + +	/*Write PHY error filter register on 5212*/ +	if (ah->ah_version == AR5K_AR5212) +		ath5k_hw_reg_write(ah, data, AR5K_PHY_ERR_FIL); + +} + +/* + * Beacon related functions + */ + +/* + * Get a 32bit TSF + */ +u32 ath5k_hw_get_tsf32(struct ath5k_hw *ah) +{ +	ATH5K_TRACE(ah->ah_sc); +	return ath5k_hw_reg_read(ah, AR5K_TSF_L32); +} + +/* + * Get the full 64bit TSF + */ +u64 ath5k_hw_get_tsf64(struct ath5k_hw *ah) +{ +	u64 tsf = ath5k_hw_reg_read(ah, AR5K_TSF_U32); +	ATH5K_TRACE(ah->ah_sc); + +	return ath5k_hw_reg_read(ah, AR5K_TSF_L32) | (tsf << 32); +} + +/* + * Force a TSF reset + */ +void ath5k_hw_reset_tsf(struct ath5k_hw *ah) +{ +	ATH5K_TRACE(ah->ah_sc); +	AR5K_REG_ENABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_RESET_TSF); +} + +/* + * Initialize beacon timers + */ +void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval) +{ +	u32 timer1, timer2, timer3; + +	ATH5K_TRACE(ah->ah_sc); +	/* +	 * Set the additional timers by mode +	 */ +	switch (ah->ah_op_mode) { +	case IEEE80211_IF_TYPE_STA: +		if (ah->ah_version == AR5K_AR5210) { +			timer1 = 0xffffffff; +			timer2 = 0xffffffff; +		} else { +			timer1 = 0x0000ffff; +			timer2 = 0x0007ffff; +		} +		break; + +	default: +		timer1 = (next_beacon - AR5K_TUNE_DMA_BEACON_RESP) << +		    0x00000003; +		timer2 = (next_beacon - AR5K_TUNE_SW_BEACON_RESP) << +		    0x00000003; +	} + +	timer3 = next_beacon + (ah->ah_atim_window ? ah->ah_atim_window : 1); + +	/* +	 * Set the beacon register and enable all timers. +	 * (next beacon, DMA beacon, software beacon, ATIM window time) +	 */ +	ath5k_hw_reg_write(ah, next_beacon, AR5K_TIMER0); +	ath5k_hw_reg_write(ah, timer1, AR5K_TIMER1); +	ath5k_hw_reg_write(ah, timer2, AR5K_TIMER2); +	ath5k_hw_reg_write(ah, timer3, AR5K_TIMER3); + +	ath5k_hw_reg_write(ah, interval & (AR5K_BEACON_PERIOD | +			AR5K_BEACON_RESET_TSF | AR5K_BEACON_ENABLE), +		AR5K_BEACON); +} + +#if 0 +/* + * Set beacon timers + */ +int ath5k_hw_set_beacon_timers(struct ath5k_hw *ah, +		const struct ath5k_beacon_state *state) +{ +	u32 cfp_period, next_cfp, dtim, interval, next_beacon; + +	/* +	 * TODO: should be changed through *state +	 * review struct ath5k_beacon_state struct +	 * +	 * XXX: These are used for cfp period bellow, are they +	 * ok ? Is it O.K. for tsf here to be 0 or should we use +	 * get_tsf ? +	 */ +	u32 dtim_count = 0; /* XXX */ +	u32 cfp_count = 0; /* XXX */ +	u32 tsf = 0; /* XXX */ + +	ATH5K_TRACE(ah->ah_sc); +	/* Return on an invalid beacon state */ +	if (state->bs_interval < 1) +		return -EINVAL; + +	interval = state->bs_interval; +	dtim = state->bs_dtim_period; + +	/* +	 * PCF support? +	 */ +	if (state->bs_cfp_period > 0) { +		/* +		 * Enable PCF mode and set the CFP +		 * (Contention Free Period) and timer registers +		 */ +		cfp_period = state->bs_cfp_period * state->bs_dtim_period * +			state->bs_interval; +		next_cfp = (cfp_count * state->bs_dtim_period + dtim_count) * +			state->bs_interval; + +		AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1, +				AR5K_STA_ID1_DEFAULT_ANTENNA | +				AR5K_STA_ID1_PCF); +		ath5k_hw_reg_write(ah, cfp_period, AR5K_CFP_PERIOD); +		ath5k_hw_reg_write(ah, state->bs_cfp_max_duration, +				AR5K_CFP_DUR); +		ath5k_hw_reg_write(ah, (tsf + (next_cfp == 0 ? cfp_period : +						next_cfp)) << 3, AR5K_TIMER2); +	} else { +		/* Disable PCF mode */ +		AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, +				AR5K_STA_ID1_DEFAULT_ANTENNA | +				AR5K_STA_ID1_PCF); +	} + +	/* +	 * Enable the beacon timer register +	 */ +	ath5k_hw_reg_write(ah, state->bs_next_beacon, AR5K_TIMER0); + +	/* +	 * Start the beacon timers +	 */ +	ath5k_hw_reg_write(ah, (ath5k_hw_reg_read(ah, AR5K_BEACON) &~ +		(AR5K_BEACON_PERIOD | AR5K_BEACON_TIM)) | +		AR5K_REG_SM(state->bs_tim_offset ? state->bs_tim_offset + 4 : 0, +		AR5K_BEACON_TIM) | AR5K_REG_SM(state->bs_interval, +		AR5K_BEACON_PERIOD), AR5K_BEACON); + +	/* +	 * Write new beacon miss threshold, if it appears to be valid +	 * XXX: Figure out right values for min <= bs_bmiss_threshold <= max +	 * and return if its not in range. We can test this by reading value and +	 * setting value to a largest value and seeing which values register. +	 */ + +	AR5K_REG_WRITE_BITS(ah, AR5K_RSSI_THR, AR5K_RSSI_THR_BMISS, +			state->bs_bmiss_threshold); + +	/* +	 * Set sleep control register +	 * XXX: Didn't find this in 5210 code but since this register +	 * exists also in ar5k's 5210 headers i leave it as common code. +	 */ +	AR5K_REG_WRITE_BITS(ah, AR5K_SLEEP_CTL, AR5K_SLEEP_CTL_SLDUR, +			(state->bs_sleep_duration - 3) << 3); + +	/* +	 * Set enhanced sleep registers on 5212 +	 */ +	if (ah->ah_version == AR5K_AR5212) { +		if (state->bs_sleep_duration > state->bs_interval && +				roundup(state->bs_sleep_duration, interval) == +				state->bs_sleep_duration) +			interval = state->bs_sleep_duration; + +		if (state->bs_sleep_duration > dtim && (dtim == 0 || +				roundup(state->bs_sleep_duration, dtim) == +				state->bs_sleep_duration)) +			dtim = state->bs_sleep_duration; + +		if (interval > dtim) +			return -EINVAL; + +		next_beacon = interval == dtim ? state->bs_next_dtim : +			state->bs_next_beacon; + +		ath5k_hw_reg_write(ah, +			AR5K_REG_SM((state->bs_next_dtim - 3) << 3, +			AR5K_SLEEP0_NEXT_DTIM) | +			AR5K_REG_SM(10, AR5K_SLEEP0_CABTO) | +			AR5K_SLEEP0_ENH_SLEEP_EN | +			AR5K_SLEEP0_ASSUME_DTIM, AR5K_SLEEP0); + +		ath5k_hw_reg_write(ah, AR5K_REG_SM((next_beacon - 3) << 3, +			AR5K_SLEEP1_NEXT_TIM) | +			AR5K_REG_SM(10, AR5K_SLEEP1_BEACON_TO), AR5K_SLEEP1); + +		ath5k_hw_reg_write(ah, +			AR5K_REG_SM(interval, AR5K_SLEEP2_TIM_PER) | +			AR5K_REG_SM(dtim, AR5K_SLEEP2_DTIM_PER), AR5K_SLEEP2); +	} + +	return 0; +} + +/* + * Reset beacon timers + */ +void ath5k_hw_reset_beacon(struct ath5k_hw *ah) +{ +	ATH5K_TRACE(ah->ah_sc); +	/* +	 * Disable beacon timer +	 */ +	ath5k_hw_reg_write(ah, 0, AR5K_TIMER0); + +	/* +	 * Disable some beacon register values +	 */ +	AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, +			AR5K_STA_ID1_DEFAULT_ANTENNA | AR5K_STA_ID1_PCF); +	ath5k_hw_reg_write(ah, AR5K_BEACON_PERIOD, AR5K_BEACON); +} + +/* + * Wait for beacon queue to finish + */ +int ath5k_hw_beaconq_finish(struct ath5k_hw *ah, unsigned long phys_addr) +{ +	unsigned int i; +	int ret; + +	ATH5K_TRACE(ah->ah_sc); + +	/* 5210 doesn't have QCU*/ +	if (ah->ah_version == AR5K_AR5210) { +		/* +		 * Wait for beaconn queue to finish by checking +		 * Control Register and Beacon Status Register. +		 */ +		for (i = AR5K_TUNE_BEACON_INTERVAL / 2; i > 0; i--) { +			if (!(ath5k_hw_reg_read(ah, AR5K_BSR) & AR5K_BSR_TXQ1F) +					|| +			    !(ath5k_hw_reg_read(ah, AR5K_CR) & AR5K_BSR_TXQ1F)) +				break; +			udelay(10); +		} + +		/* Timeout... */ +		if (i <= 0) { +			/* +			 * Re-schedule the beacon queue +			 */ +			ath5k_hw_reg_write(ah, phys_addr, AR5K_NOQCU_TXDP1); +			ath5k_hw_reg_write(ah, AR5K_BCR_TQ1V | AR5K_BCR_BDMAE, +					AR5K_BCR); + +			return -EIO; +		} +		ret = 0; +	} else { +	/*5211/5212*/ +		ret = ath5k_hw_register_timeout(ah, +			AR5K_QUEUE_STATUS(AR5K_TX_QUEUE_ID_BEACON), +			AR5K_QCU_STS_FRMPENDCNT, 0, false); + +		if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, AR5K_TX_QUEUE_ID_BEACON)) +			return -EIO; +	} + +	return ret; +} +#endif + +/* + * Update mib counters (statistics) + */ +void ath5k_hw_update_mib_counters(struct ath5k_hw *ah, +		struct ath5k_mib_stats *statistics) +{ +	ATH5K_TRACE(ah->ah_sc); +	/* Read-And-Clear */ +	statistics->ackrcv_bad += ath5k_hw_reg_read(ah, AR5K_ACK_FAIL); +	statistics->rts_bad += ath5k_hw_reg_read(ah, AR5K_RTS_FAIL); +	statistics->rts_good += ath5k_hw_reg_read(ah, AR5K_RTS_OK); +	statistics->fcs_bad += ath5k_hw_reg_read(ah, AR5K_FCS_FAIL); +	statistics->beacons += ath5k_hw_reg_read(ah, AR5K_BEACON_CNT); + +	/* Reset profile count registers on 5212*/ +	if (ah->ah_version == AR5K_AR5212) { +		ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_TX); +		ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_RX); +		ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_RXCLR); +		ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_CYCLE); +	} +} + +/** ath5k_hw_set_ack_bitrate - set bitrate for ACKs + * + * @ah: the &struct ath5k_hw + * @high: determines if to use low bit rate or now + */ +void ath5k_hw_set_ack_bitrate_high(struct ath5k_hw *ah, bool high) +{ +	if (ah->ah_version != AR5K_AR5212) +		return; +	else { +		u32 val = AR5K_STA_ID1_BASE_RATE_11B | AR5K_STA_ID1_ACKCTS_6MB; +		if (high) +			AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1, val); +		else +			AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, val); +	} +} + + +/* + * ACK/CTS Timeouts + */ + +/* + * Set ACK timeout on PCU + */ +int ath5k_hw_set_ack_timeout(struct ath5k_hw *ah, unsigned int timeout) +{ +	ATH5K_TRACE(ah->ah_sc); +	if (ath5k_hw_clocktoh(AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_ACK), +			ah->ah_turbo) <= timeout) +		return -EINVAL; + +	AR5K_REG_WRITE_BITS(ah, AR5K_TIME_OUT, AR5K_TIME_OUT_ACK, +		ath5k_hw_htoclock(timeout, ah->ah_turbo)); + +	return 0; +} + +/* + * Read the ACK timeout from PCU + */ +unsigned int ath5k_hw_get_ack_timeout(struct ath5k_hw *ah) +{ +	ATH5K_TRACE(ah->ah_sc); + +	return ath5k_hw_clocktoh(AR5K_REG_MS(ath5k_hw_reg_read(ah, +			AR5K_TIME_OUT), AR5K_TIME_OUT_ACK), ah->ah_turbo); +} + +/* + * Set CTS timeout on PCU + */ +int ath5k_hw_set_cts_timeout(struct ath5k_hw *ah, unsigned int timeout) +{ +	ATH5K_TRACE(ah->ah_sc); +	if (ath5k_hw_clocktoh(AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_CTS), +			ah->ah_turbo) <= timeout) +		return -EINVAL; + +	AR5K_REG_WRITE_BITS(ah, AR5K_TIME_OUT, AR5K_TIME_OUT_CTS, +			ath5k_hw_htoclock(timeout, ah->ah_turbo)); + +	return 0; +} + +/* + * Read CTS timeout from PCU + */ +unsigned int ath5k_hw_get_cts_timeout(struct ath5k_hw *ah) +{ +	ATH5K_TRACE(ah->ah_sc); +	return ath5k_hw_clocktoh(AR5K_REG_MS(ath5k_hw_reg_read(ah, +			AR5K_TIME_OUT), AR5K_TIME_OUT_CTS), ah->ah_turbo); +} + +/* + * Key table (WEP) functions + */ + +int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry) +{ +	unsigned int i; + +	ATH5K_TRACE(ah->ah_sc); +	AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE); + +	for (i = 0; i < AR5K_KEYCACHE_SIZE; i++) +		ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_OFF(entry, i)); + +	/* Set NULL encryption on non-5210*/ +	if (ah->ah_version != AR5K_AR5210) +		ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL, +				AR5K_KEYTABLE_TYPE(entry)); + +	return 0; +} + +int ath5k_hw_is_key_valid(struct ath5k_hw *ah, u16 entry) +{ +	ATH5K_TRACE(ah->ah_sc); +	AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE); + +	/* Check the validation flag at the end of the entry */ +	return ath5k_hw_reg_read(ah, AR5K_KEYTABLE_MAC1(entry)) & +		AR5K_KEYTABLE_VALID; +} + +int ath5k_hw_set_key(struct ath5k_hw *ah, u16 entry, +		const struct ieee80211_key_conf *key, const u8 *mac) +{ +	unsigned int i; +	__le32 key_v[5] = {}; +	u32 keytype; + +	ATH5K_TRACE(ah->ah_sc); + +	/* key->keylen comes in from mac80211 in bytes */ + +	if (key->keylen > AR5K_KEYTABLE_SIZE / 8) +		return -EOPNOTSUPP; + +	switch (key->keylen) { +	/* WEP 40-bit   = 40-bit  entered key + 24 bit IV = 64-bit */ +	case 40 / 8: +		memcpy(&key_v[0], key->key, 5); +		keytype = AR5K_KEYTABLE_TYPE_40; +		break; + +	/* WEP 104-bit  = 104-bit entered key + 24-bit IV = 128-bit */ +	case 104 / 8: +		memcpy(&key_v[0], &key->key[0], 6); +		memcpy(&key_v[2], &key->key[6], 6); +		memcpy(&key_v[4], &key->key[12], 1); +		keytype = AR5K_KEYTABLE_TYPE_104; +		break; +	/* WEP 128-bit  = 128-bit entered key + 24 bit IV = 152-bit */ +	case 128 / 8: +		memcpy(&key_v[0], &key->key[0], 6); +		memcpy(&key_v[2], &key->key[6], 6); +		memcpy(&key_v[4], &key->key[12], 4); +		keytype = AR5K_KEYTABLE_TYPE_128; +		break; + +	default: +		return -EINVAL; /* shouldn't happen */ +	} + +	for (i = 0; i < ARRAY_SIZE(key_v); i++) +		ath5k_hw_reg_write(ah, le32_to_cpu(key_v[i]), +				AR5K_KEYTABLE_OFF(entry, i)); + +	ath5k_hw_reg_write(ah, keytype, AR5K_KEYTABLE_TYPE(entry)); + +	return ath5k_hw_set_key_lladdr(ah, entry, mac); +} + +int ath5k_hw_set_key_lladdr(struct ath5k_hw *ah, u16 entry, const u8 *mac) +{ +	u32 low_id, high_id; + +	ATH5K_TRACE(ah->ah_sc); +	 /* Invalid entry (key table overflow) */ +	AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE); + +	/* MAC may be NULL if it's a broadcast key. In this case no need to +	 * to compute AR5K_LOW_ID and AR5K_HIGH_ID as we already know it. */ +	if (unlikely(mac == NULL)) { +		low_id = 0xffffffff; +		high_id = 0xffff | AR5K_KEYTABLE_VALID; +	} else { +		low_id = AR5K_LOW_ID(mac); +		high_id = AR5K_HIGH_ID(mac) | AR5K_KEYTABLE_VALID; +	} + +	ath5k_hw_reg_write(ah, low_id, AR5K_KEYTABLE_MAC0(entry)); +	ath5k_hw_reg_write(ah, high_id, AR5K_KEYTABLE_MAC1(entry)); + +	return 0; +} + + +/********************************************\ +Queue Control Unit, DFS Control Unit Functions +\********************************************/ + +/* + * Initialize a transmit queue + */ +int ath5k_hw_setup_tx_queue(struct ath5k_hw *ah, enum ath5k_tx_queue queue_type, +		struct ath5k_txq_info *queue_info) +{ +	unsigned int queue; +	int ret; + +	ATH5K_TRACE(ah->ah_sc); + +	/* +	 * Get queue by type +	 */ +	/*5210 only has 2 queues*/ +	if (ah->ah_version == AR5K_AR5210) { +		switch (queue_type) { +		case AR5K_TX_QUEUE_DATA: +			queue = AR5K_TX_QUEUE_ID_NOQCU_DATA; +			break; +		case AR5K_TX_QUEUE_BEACON: +		case AR5K_TX_QUEUE_CAB: +			queue = AR5K_TX_QUEUE_ID_NOQCU_BEACON; +			break; +		default: +			return -EINVAL; +		} +	} else { +		switch (queue_type) { +		case AR5K_TX_QUEUE_DATA: +			for (queue = AR5K_TX_QUEUE_ID_DATA_MIN; +				ah->ah_txq[queue].tqi_type != +				AR5K_TX_QUEUE_INACTIVE; queue++) { + +				if (queue > AR5K_TX_QUEUE_ID_DATA_MAX) +					return -EINVAL; +			} +			break; +		case AR5K_TX_QUEUE_UAPSD: +			queue = AR5K_TX_QUEUE_ID_UAPSD; +			break; +		case AR5K_TX_QUEUE_BEACON: +			queue = AR5K_TX_QUEUE_ID_BEACON; +			break; +		case AR5K_TX_QUEUE_CAB: +			queue = AR5K_TX_QUEUE_ID_CAB; +			break; +		case AR5K_TX_QUEUE_XR_DATA: +			if (ah->ah_version != AR5K_AR5212) +				ATH5K_ERR(ah->ah_sc, +					"XR data queues only supported in" +					" 5212!\n"); +			queue = AR5K_TX_QUEUE_ID_XR_DATA; +			break; +		default: +			return -EINVAL; +		} +	} + +	/* +	 * Setup internal queue structure +	 */ +	memset(&ah->ah_txq[queue], 0, sizeof(struct ath5k_txq_info)); +	ah->ah_txq[queue].tqi_type = queue_type; + +	if (queue_info != NULL) { +		queue_info->tqi_type = queue_type; +		ret = ath5k_hw_setup_tx_queueprops(ah, queue, queue_info); +		if (ret) +			return ret; +	} +	/* +	 * We use ah_txq_status to hold a temp value for +	 * the Secondary interrupt mask registers on 5211+ +	 * check out ath5k_hw_reset_tx_queue +	 */ +	AR5K_Q_ENABLE_BITS(ah->ah_txq_status, queue); + +	return queue; +} + +/* + * Setup a transmit queue + */ +int ath5k_hw_setup_tx_queueprops(struct ath5k_hw *ah, int queue, +				const struct ath5k_txq_info *queue_info) +{ +	ATH5K_TRACE(ah->ah_sc); +	AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); + +	if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE) +		return -EIO; + +	memcpy(&ah->ah_txq[queue], queue_info, sizeof(struct ath5k_txq_info)); + +	/*XXX: Is this supported on 5210 ?*/ +	if ((queue_info->tqi_type == AR5K_TX_QUEUE_DATA && +			((queue_info->tqi_subtype == AR5K_WME_AC_VI) || +			(queue_info->tqi_subtype == AR5K_WME_AC_VO))) || +			queue_info->tqi_type == AR5K_TX_QUEUE_UAPSD) +		ah->ah_txq[queue].tqi_flags |= AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS; + +	return 0; +} + +/* + * Get properties for a specific transmit queue + */ +int ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue, +		struct ath5k_txq_info *queue_info) +{ +	ATH5K_TRACE(ah->ah_sc); +	memcpy(queue_info, &ah->ah_txq[queue], sizeof(struct ath5k_txq_info)); +	return 0; +} + +/* + * Set a transmit queue inactive + */ +void ath5k_hw_release_tx_queue(struct ath5k_hw *ah, unsigned int queue) +{ +	ATH5K_TRACE(ah->ah_sc); +	if (WARN_ON(queue >= ah->ah_capabilities.cap_queues.q_tx_num)) +		return; + +	/* This queue will be skipped in further operations */ +	ah->ah_txq[queue].tqi_type = AR5K_TX_QUEUE_INACTIVE; +	/*For SIMR setup*/ +	AR5K_Q_DISABLE_BITS(ah->ah_txq_status, queue); +} + +/* + * Set DFS params for a transmit queue + */ +int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) +{ +	u32 cw_min, cw_max, retry_lg, retry_sh; +	struct ath5k_txq_info *tq = &ah->ah_txq[queue]; + +	ATH5K_TRACE(ah->ah_sc); +	AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); + +	tq = &ah->ah_txq[queue]; + +	if (tq->tqi_type == AR5K_TX_QUEUE_INACTIVE) +		return 0; + +	if (ah->ah_version == AR5K_AR5210) { +		/* Only handle data queues, others will be ignored */ +		if (tq->tqi_type != AR5K_TX_QUEUE_DATA) +			return 0; + +		/* Set Slot time */ +		ath5k_hw_reg_write(ah, ah->ah_turbo == true ? +			AR5K_INIT_SLOT_TIME_TURBO : AR5K_INIT_SLOT_TIME, +			AR5K_SLOT_TIME); +		/* Set ACK_CTS timeout */ +		ath5k_hw_reg_write(ah, ah->ah_turbo == true ? +			AR5K_INIT_ACK_CTS_TIMEOUT_TURBO : +			AR5K_INIT_ACK_CTS_TIMEOUT, AR5K_SLOT_TIME); +		/* Set Transmit Latency */ +		ath5k_hw_reg_write(ah, ah->ah_turbo == true ? +			AR5K_INIT_TRANSMIT_LATENCY_TURBO : +			AR5K_INIT_TRANSMIT_LATENCY, AR5K_USEC_5210); +		/* Set IFS0 */ +		if (ah->ah_turbo == true) +			 ath5k_hw_reg_write(ah, ((AR5K_INIT_SIFS_TURBO + +				(ah->ah_aifs + tq->tqi_aifs) * +				AR5K_INIT_SLOT_TIME_TURBO) << +				AR5K_IFS0_DIFS_S) | AR5K_INIT_SIFS_TURBO, +				AR5K_IFS0); +		else +			ath5k_hw_reg_write(ah, ((AR5K_INIT_SIFS + +				(ah->ah_aifs + tq->tqi_aifs) * +				AR5K_INIT_SLOT_TIME) << AR5K_IFS0_DIFS_S) | +				AR5K_INIT_SIFS, AR5K_IFS0); + +		/* Set IFS1 */ +		ath5k_hw_reg_write(ah, ah->ah_turbo == true ? +			AR5K_INIT_PROTO_TIME_CNTRL_TURBO : +			AR5K_INIT_PROTO_TIME_CNTRL, AR5K_IFS1); +		/* Set PHY register 0x9844 (??) */ +		ath5k_hw_reg_write(ah, ah->ah_turbo == true ? +			(ath5k_hw_reg_read(ah, AR5K_PHY(17)) & ~0x7F) | 0x38 : +			(ath5k_hw_reg_read(ah, AR5K_PHY(17)) & ~0x7F) | 0x1C, +			AR5K_PHY(17)); +		/* Set Frame Control Register */ +		ath5k_hw_reg_write(ah, ah->ah_turbo == true ? +			(AR5K_PHY_FRAME_CTL_INI | AR5K_PHY_TURBO_MODE | +			AR5K_PHY_TURBO_SHORT | 0x2020) : +			(AR5K_PHY_FRAME_CTL_INI | 0x1020), +			AR5K_PHY_FRAME_CTL_5210); +	} + +	/* +	 * Calculate cwmin/max by channel mode +	 */ +	cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN; +	cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX; +	ah->ah_aifs = AR5K_TUNE_AIFS; +	/*XR is only supported on 5212*/ +	if (IS_CHAN_XR(ah->ah_current_channel) && +			ah->ah_version == AR5K_AR5212) { +		cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN_XR; +		cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX_XR; +		ah->ah_aifs = AR5K_TUNE_AIFS_XR; +	/*B mode is not supported on 5210*/ +	} else if (IS_CHAN_B(ah->ah_current_channel) && +			ah->ah_version != AR5K_AR5210) { +		cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN_11B; +		cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX_11B; +		ah->ah_aifs = AR5K_TUNE_AIFS_11B; +	} + +	cw_min = 1; +	while (cw_min < ah->ah_cw_min) +		cw_min = (cw_min << 1) | 1; + +	cw_min = tq->tqi_cw_min < 0 ? (cw_min >> (-tq->tqi_cw_min)) : +		((cw_min << tq->tqi_cw_min) + (1 << tq->tqi_cw_min) - 1); +	cw_max = tq->tqi_cw_max < 0 ? (cw_max >> (-tq->tqi_cw_max)) : +		((cw_max << tq->tqi_cw_max) + (1 << tq->tqi_cw_max) - 1); + +	/* +	 * Calculate and set retry limits +	 */ +	if (ah->ah_software_retry == true) { +		/* XXX Need to test this */ +		retry_lg = ah->ah_limit_tx_retries; +		retry_sh = retry_lg = retry_lg > AR5K_DCU_RETRY_LMT_SH_RETRY ? +			AR5K_DCU_RETRY_LMT_SH_RETRY : retry_lg; +	} else { +		retry_lg = AR5K_INIT_LG_RETRY; +		retry_sh = AR5K_INIT_SH_RETRY; +	} + +	/*No QCU/DCU [5210]*/ +	if (ah->ah_version == AR5K_AR5210) { +		ath5k_hw_reg_write(ah, +			(cw_min << AR5K_NODCU_RETRY_LMT_CW_MIN_S) +			| AR5K_REG_SM(AR5K_INIT_SLG_RETRY, +				AR5K_NODCU_RETRY_LMT_SLG_RETRY) +			| AR5K_REG_SM(AR5K_INIT_SSH_RETRY, +				AR5K_NODCU_RETRY_LMT_SSH_RETRY) +			| AR5K_REG_SM(retry_lg, AR5K_NODCU_RETRY_LMT_LG_RETRY) +			| AR5K_REG_SM(retry_sh, AR5K_NODCU_RETRY_LMT_SH_RETRY), +			AR5K_NODCU_RETRY_LMT); +	} else { +		/*QCU/DCU [5211+]*/ +		ath5k_hw_reg_write(ah, +			AR5K_REG_SM(AR5K_INIT_SLG_RETRY, +				AR5K_DCU_RETRY_LMT_SLG_RETRY) | +			AR5K_REG_SM(AR5K_INIT_SSH_RETRY, +				AR5K_DCU_RETRY_LMT_SSH_RETRY) | +			AR5K_REG_SM(retry_lg, AR5K_DCU_RETRY_LMT_LG_RETRY) | +			AR5K_REG_SM(retry_sh, AR5K_DCU_RETRY_LMT_SH_RETRY), +			AR5K_QUEUE_DFS_RETRY_LIMIT(queue)); + +	/*===Rest is also for QCU/DCU only [5211+]===*/ + +		/* +		 * Set initial content window (cw_min/cw_max) +		 * and arbitrated interframe space (aifs)... +		 */ +		ath5k_hw_reg_write(ah, +			AR5K_REG_SM(cw_min, AR5K_DCU_LCL_IFS_CW_MIN) | +			AR5K_REG_SM(cw_max, AR5K_DCU_LCL_IFS_CW_MAX) | +			AR5K_REG_SM(ah->ah_aifs + tq->tqi_aifs, +				AR5K_DCU_LCL_IFS_AIFS), +			AR5K_QUEUE_DFS_LOCAL_IFS(queue)); + +		/* +		 * Set misc registers +		 */ +		ath5k_hw_reg_write(ah, AR5K_QCU_MISC_DCU_EARLY, +			AR5K_QUEUE_MISC(queue)); + +		if (tq->tqi_cbr_period) { +			ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_cbr_period, +				AR5K_QCU_CBRCFG_INTVAL) | +				AR5K_REG_SM(tq->tqi_cbr_overflow_limit, +				AR5K_QCU_CBRCFG_ORN_THRES), +				AR5K_QUEUE_CBRCFG(queue)); +			AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), +				AR5K_QCU_MISC_FRSHED_CBR); +			if (tq->tqi_cbr_overflow_limit) +				AR5K_REG_ENABLE_BITS(ah, +					AR5K_QUEUE_MISC(queue), +					AR5K_QCU_MISC_CBR_THRES_ENABLE); +		} + +		if (tq->tqi_ready_time) +			ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_ready_time, +				AR5K_QCU_RDYTIMECFG_INTVAL) | +				AR5K_QCU_RDYTIMECFG_ENABLE, +				AR5K_QUEUE_RDYTIMECFG(queue)); + +		if (tq->tqi_burst_time) { +			ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_burst_time, +				AR5K_DCU_CHAN_TIME_DUR) | +				AR5K_DCU_CHAN_TIME_ENABLE, +				AR5K_QUEUE_DFS_CHANNEL_TIME(queue)); + +			if (tq->tqi_flags & AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE) +				AR5K_REG_ENABLE_BITS(ah, +					AR5K_QUEUE_MISC(queue), +					AR5K_QCU_MISC_TXE); +		} + +		if (tq->tqi_flags & AR5K_TXQ_FLAG_BACKOFF_DISABLE) +			ath5k_hw_reg_write(ah, AR5K_DCU_MISC_POST_FR_BKOFF_DIS, +				AR5K_QUEUE_DFS_MISC(queue)); + +		if (tq->tqi_flags & AR5K_TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE) +			ath5k_hw_reg_write(ah, AR5K_DCU_MISC_BACKOFF_FRAG, +				AR5K_QUEUE_DFS_MISC(queue)); + +		/* +		 * Set registers by queue type +		 */ +		switch (tq->tqi_type) { +		case AR5K_TX_QUEUE_BEACON: +			AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), +				AR5K_QCU_MISC_FRSHED_DBA_GT | +				AR5K_QCU_MISC_CBREXP_BCN | +				AR5K_QCU_MISC_BCN_ENABLE); + +			AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue), +				(AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL << +				AR5K_DCU_MISC_ARBLOCK_CTL_S) | +				AR5K_DCU_MISC_POST_FR_BKOFF_DIS | +				AR5K_DCU_MISC_BCN_ENABLE); + +			ath5k_hw_reg_write(ah, ((AR5K_TUNE_BEACON_INTERVAL - +				(AR5K_TUNE_SW_BEACON_RESP - +				AR5K_TUNE_DMA_BEACON_RESP) - +				AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF) * 1024) | +				AR5K_QCU_RDYTIMECFG_ENABLE, +				AR5K_QUEUE_RDYTIMECFG(queue)); +			break; + +		case AR5K_TX_QUEUE_CAB: +			AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), +				AR5K_QCU_MISC_FRSHED_DBA_GT | +				AR5K_QCU_MISC_CBREXP | +				AR5K_QCU_MISC_CBREXP_BCN); + +			AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue), +				(AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL << +				AR5K_DCU_MISC_ARBLOCK_CTL_S)); +			break; + +		case AR5K_TX_QUEUE_UAPSD: +			AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), +				AR5K_QCU_MISC_CBREXP); +			break; + +		case AR5K_TX_QUEUE_DATA: +		default: +			break; +		} + +		/* +		 * Enable interrupts for this tx queue +		 * in the secondary interrupt mask registers +		 */ +		if (tq->tqi_flags & AR5K_TXQ_FLAG_TXOKINT_ENABLE) +			AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txok, queue); + +		if (tq->tqi_flags & AR5K_TXQ_FLAG_TXERRINT_ENABLE) +			AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txerr, queue); + +		if (tq->tqi_flags & AR5K_TXQ_FLAG_TXURNINT_ENABLE) +			AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txurn, queue); + +		if (tq->tqi_flags & AR5K_TXQ_FLAG_TXDESCINT_ENABLE) +			AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txdesc, queue); + +		if (tq->tqi_flags & AR5K_TXQ_FLAG_TXEOLINT_ENABLE) +			AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txeol, queue); + + +		/* Update secondary interrupt mask registers */ +		ah->ah_txq_imr_txok &= ah->ah_txq_status; +		ah->ah_txq_imr_txerr &= ah->ah_txq_status; +		ah->ah_txq_imr_txurn &= ah->ah_txq_status; +		ah->ah_txq_imr_txdesc &= ah->ah_txq_status; +		ah->ah_txq_imr_txeol &= ah->ah_txq_status; + +		ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txok, +			AR5K_SIMR0_QCU_TXOK) | +			AR5K_REG_SM(ah->ah_txq_imr_txdesc, +			AR5K_SIMR0_QCU_TXDESC), AR5K_SIMR0); +		ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txerr, +			AR5K_SIMR1_QCU_TXERR) | +			AR5K_REG_SM(ah->ah_txq_imr_txeol, +			AR5K_SIMR1_QCU_TXEOL), AR5K_SIMR1); +		ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txurn, +			AR5K_SIMR2_QCU_TXURN), AR5K_SIMR2); +	} + +	return 0; +} + +/* + * Get number of pending frames + * for a specific queue [5211+] + */ +u32 ath5k_hw_num_tx_pending(struct ath5k_hw *ah, unsigned int queue) { +	ATH5K_TRACE(ah->ah_sc); +	AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); + +	/* Return if queue is declared inactive */ +	if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE) +		return false; + +	/* XXX: How about AR5K_CFG_TXCNT ? */ +	if (ah->ah_version == AR5K_AR5210) +		return false; + +	return AR5K_QUEUE_STATUS(queue) & AR5K_QCU_STS_FRMPENDCNT; +} + +/* + * Set slot time + */ +int ath5k_hw_set_slot_time(struct ath5k_hw *ah, unsigned int slot_time) +{ +	ATH5K_TRACE(ah->ah_sc); +	if (slot_time < AR5K_SLOT_TIME_9 || slot_time > AR5K_SLOT_TIME_MAX) +		return -EINVAL; + +	if (ah->ah_version == AR5K_AR5210) +		ath5k_hw_reg_write(ah, ath5k_hw_htoclock(slot_time, +				ah->ah_turbo), AR5K_SLOT_TIME); +	else +		ath5k_hw_reg_write(ah, slot_time, AR5K_DCU_GBL_IFS_SLOT); + +	return 0; +} + +/* + * Get slot time + */ +unsigned int ath5k_hw_get_slot_time(struct ath5k_hw *ah) +{ +	ATH5K_TRACE(ah->ah_sc); +	if (ah->ah_version == AR5K_AR5210) +		return ath5k_hw_clocktoh(ath5k_hw_reg_read(ah, +				AR5K_SLOT_TIME) & 0xffff, ah->ah_turbo); +	else +		return ath5k_hw_reg_read(ah, AR5K_DCU_GBL_IFS_SLOT) & 0xffff; +} + + +/******************************\ + Hardware Descriptor Functions +\******************************/ + +/* + * TX Descriptor + */ + +/* + * Initialize the 2-word tx descriptor on 5210/5211 + */ +static int +ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc, +	unsigned int pkt_len, unsigned int hdr_len, enum ath5k_pkt_type type, +	unsigned int tx_power, unsigned int tx_rate0, unsigned int tx_tries0, +	unsigned int key_index, unsigned int antenna_mode, unsigned int flags, +	unsigned int rtscts_rate, unsigned int rtscts_duration) +{ +	u32 frame_type; +	struct ath5k_hw_2w_tx_desc *tx_desc; +	unsigned int buff_len; + +	tx_desc = (struct ath5k_hw_2w_tx_desc *)&desc->ds_ctl0; + +	/* +	 * Validate input +	 * - Zero retries don't make sense. +	 * - A zero rate will put the HW into a mode where it continously sends +	 *   noise on the channel, so it is important to avoid this. +	 */ +	if (unlikely(tx_tries0 == 0)) { +		ATH5K_ERR(ah->ah_sc, "zero retries\n"); +		WARN_ON(1); +		return -EINVAL; +	} +	if (unlikely(tx_rate0 == 0)) { +		ATH5K_ERR(ah->ah_sc, "zero rate\n"); +		WARN_ON(1); +		return -EINVAL; +	} + +	/* Clear status descriptor */ +	memset(desc->ds_hw, 0, sizeof(struct ath5k_hw_tx_status)); + +	/* Initialize control descriptor */ +	tx_desc->tx_control_0 = 0; +	tx_desc->tx_control_1 = 0; + +	/* Setup control descriptor */ + +	/* Verify and set frame length */ +	if (pkt_len & ~AR5K_2W_TX_DESC_CTL0_FRAME_LEN) +		return -EINVAL; + +	tx_desc->tx_control_0 = pkt_len & AR5K_2W_TX_DESC_CTL0_FRAME_LEN; + +	/* Verify and set buffer length */ +	buff_len = pkt_len - FCS_LEN; + +	/* NB: beacon's BufLen must be a multiple of 4 bytes */ +	if(type == AR5K_PKT_TYPE_BEACON) +		buff_len = roundup(buff_len, 4); + +	if (buff_len & ~AR5K_2W_TX_DESC_CTL1_BUF_LEN) +		return -EINVAL; + +	tx_desc->tx_control_1 = buff_len & AR5K_2W_TX_DESC_CTL1_BUF_LEN; + +	/* +	 * Verify and set header length +	 * XXX: I only found that on 5210 code, does it work on 5211 ? +	 */ +	if (ah->ah_version == AR5K_AR5210) { +		if (hdr_len & ~AR5K_2W_TX_DESC_CTL0_HEADER_LEN) +			return -EINVAL; +		tx_desc->tx_control_0 |= +			AR5K_REG_SM(hdr_len, AR5K_2W_TX_DESC_CTL0_HEADER_LEN); +	} + +	/*Diferences between 5210-5211*/ +	if (ah->ah_version == AR5K_AR5210) { +		switch (type) { +		case AR5K_PKT_TYPE_BEACON: +		case AR5K_PKT_TYPE_PROBE_RESP: +			frame_type = AR5K_AR5210_TX_DESC_FRAME_TYPE_NO_DELAY; +		case AR5K_PKT_TYPE_PIFS: +			frame_type = AR5K_AR5210_TX_DESC_FRAME_TYPE_PIFS; +		default: +			frame_type = type /*<< 2 ?*/; +		} + +		tx_desc->tx_control_0 |= +			AR5K_REG_SM(frame_type, AR5K_2W_TX_DESC_CTL0_FRAME_TYPE) | +			AR5K_REG_SM(tx_rate0, AR5K_2W_TX_DESC_CTL0_XMIT_RATE); +	} else { +		tx_desc->tx_control_0 |= +			AR5K_REG_SM(tx_rate0, AR5K_2W_TX_DESC_CTL0_XMIT_RATE) | +			AR5K_REG_SM(antenna_mode, AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT); +		tx_desc->tx_control_1 |= +			AR5K_REG_SM(type, AR5K_2W_TX_DESC_CTL1_FRAME_TYPE); +	} +#define _TX_FLAGS(_c, _flag)						\ +	if (flags & AR5K_TXDESC_##_flag)				\ +		tx_desc->tx_control_##_c |=				\ +			AR5K_2W_TX_DESC_CTL##_c##_##_flag + +	_TX_FLAGS(0, CLRDMASK); +	_TX_FLAGS(0, VEOL); +	_TX_FLAGS(0, INTREQ); +	_TX_FLAGS(0, RTSENA); +	_TX_FLAGS(1, NOACK); + +#undef _TX_FLAGS + +	/* +	 * WEP crap +	 */ +	if (key_index != AR5K_TXKEYIX_INVALID) { +		tx_desc->tx_control_0 |= +			AR5K_2W_TX_DESC_CTL0_ENCRYPT_KEY_VALID; +		tx_desc->tx_control_1 |= +			AR5K_REG_SM(key_index, +			AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX); +	} + +	/* +	 * RTS/CTS Duration [5210 ?] +	 */ +	if ((ah->ah_version == AR5K_AR5210) && +			(flags & (AR5K_TXDESC_RTSENA | AR5K_TXDESC_CTSENA))) +		tx_desc->tx_control_1 |= rtscts_duration & +				AR5K_2W_TX_DESC_CTL1_RTS_DURATION; + +	return 0; +} + +/* + * Initialize the 4-word tx descriptor on 5212 + */ +static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah, +	struct ath5k_desc *desc, unsigned int pkt_len, unsigned int hdr_len, +	enum ath5k_pkt_type type, unsigned int tx_power, unsigned int tx_rate0, +	unsigned int tx_tries0, unsigned int key_index, +	unsigned int antenna_mode, unsigned int flags, unsigned int rtscts_rate, +	unsigned int rtscts_duration) +{ +	struct ath5k_hw_4w_tx_desc *tx_desc; +	struct ath5k_hw_tx_status *tx_status; +	unsigned int buff_len; + +	ATH5K_TRACE(ah->ah_sc); +	tx_desc = (struct ath5k_hw_4w_tx_desc *)&desc->ds_ctl0; +	tx_status = (struct ath5k_hw_tx_status *)&desc->ds_hw[2]; + +	/* +	 * Validate input +	 * - Zero retries don't make sense. +	 * - A zero rate will put the HW into a mode where it continously sends +	 *   noise on the channel, so it is important to avoid this. +	 */ +	if (unlikely(tx_tries0 == 0)) { +		ATH5K_ERR(ah->ah_sc, "zero retries\n"); +		WARN_ON(1); +		return -EINVAL; +	} +	if (unlikely(tx_rate0 == 0)) { +		ATH5K_ERR(ah->ah_sc, "zero rate\n"); +		WARN_ON(1); +		return -EINVAL; +	} + +	/* Clear status descriptor */ +	memset(tx_status, 0, sizeof(struct ath5k_hw_tx_status)); + +	/* Initialize control descriptor */ +	tx_desc->tx_control_0 = 0; +	tx_desc->tx_control_1 = 0; +	tx_desc->tx_control_2 = 0; +	tx_desc->tx_control_3 = 0; + +	/* Setup control descriptor */ + +	/* Verify and set frame length */ +	if (pkt_len & ~AR5K_4W_TX_DESC_CTL0_FRAME_LEN) +		return -EINVAL; + +	tx_desc->tx_control_0 = pkt_len & AR5K_4W_TX_DESC_CTL0_FRAME_LEN; + +	/* Verify and set buffer length */ +	buff_len = pkt_len - FCS_LEN; + +	/* NB: beacon's BufLen must be a multiple of 4 bytes */ +	if(type == AR5K_PKT_TYPE_BEACON) +		buff_len = roundup(buff_len, 4); + +	if (buff_len & ~AR5K_4W_TX_DESC_CTL1_BUF_LEN) +		return -EINVAL; + +	tx_desc->tx_control_1 = buff_len & AR5K_4W_TX_DESC_CTL1_BUF_LEN; + +	tx_desc->tx_control_0 |= +		AR5K_REG_SM(tx_power, AR5K_4W_TX_DESC_CTL0_XMIT_POWER) | +		AR5K_REG_SM(antenna_mode, AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT); +	tx_desc->tx_control_1 |= AR5K_REG_SM(type, +					AR5K_4W_TX_DESC_CTL1_FRAME_TYPE); +	tx_desc->tx_control_2 = AR5K_REG_SM(tx_tries0 + AR5K_TUNE_HWTXTRIES, +					AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0); +	tx_desc->tx_control_3 = tx_rate0 & AR5K_4W_TX_DESC_CTL3_XMIT_RATE0; + +#define _TX_FLAGS(_c, _flag)			\ +	if (flags & AR5K_TXDESC_##_flag)	\ +		tx_desc->tx_control_##_c |=	\ +			AR5K_4W_TX_DESC_CTL##_c##_##_flag + +	_TX_FLAGS(0, CLRDMASK); +	_TX_FLAGS(0, VEOL); +	_TX_FLAGS(0, INTREQ); +	_TX_FLAGS(0, RTSENA); +	_TX_FLAGS(0, CTSENA); +	_TX_FLAGS(1, NOACK); + +#undef _TX_FLAGS + +	/* +	 * WEP crap +	 */ +	if (key_index != AR5K_TXKEYIX_INVALID) { +		tx_desc->tx_control_0 |= AR5K_4W_TX_DESC_CTL0_ENCRYPT_KEY_VALID; +		tx_desc->tx_control_1 |= AR5K_REG_SM(key_index, +				AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX); +	} + +	/* +	 * RTS/CTS +	 */ +	if (flags & (AR5K_TXDESC_RTSENA | AR5K_TXDESC_CTSENA)) { +		if ((flags & AR5K_TXDESC_RTSENA) && +				(flags & AR5K_TXDESC_CTSENA)) +			return -EINVAL; +		tx_desc->tx_control_2 |= rtscts_duration & +				AR5K_4W_TX_DESC_CTL2_RTS_DURATION; +		tx_desc->tx_control_3 |= AR5K_REG_SM(rtscts_rate, +				AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE); +	} + +	return 0; +} + +/* + * Initialize a 4-word multirate tx descriptor on 5212 + */ +static bool +ath5k_hw_setup_xr_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc, +	unsigned int tx_rate1, u_int tx_tries1, u_int tx_rate2, u_int tx_tries2, +	unsigned int tx_rate3, u_int tx_tries3) +{ +	struct ath5k_hw_4w_tx_desc *tx_desc; + +	/* +	 * Rates can be 0 as long as the retry count is 0 too. +	 * A zero rate and nonzero retry count will put the HW into a mode where +	 * it continously sends noise on the channel, so it is important to +	 * avoid this. +	 */ +	if (unlikely((tx_rate1 == 0 && tx_tries1 != 0) || +		     (tx_rate2 == 0 && tx_tries2 != 0) || +		     (tx_rate3 == 0 && tx_tries3 != 0))) { +		ATH5K_ERR(ah->ah_sc, "zero rate\n"); +		WARN_ON(1); +		return -EINVAL; +	} + +	if (ah->ah_version == AR5K_AR5212) { +		tx_desc = (struct ath5k_hw_4w_tx_desc *)&desc->ds_ctl0; + +#define _XTX_TRIES(_n)							\ +	if (tx_tries##_n) {						\ +		tx_desc->tx_control_2 |=				\ +		    AR5K_REG_SM(tx_tries##_n,				\ +		    AR5K_4W_TX_DESC_CTL2_XMIT_TRIES##_n);		\ +		tx_desc->tx_control_3 |=				\ +		    AR5K_REG_SM(tx_rate##_n,				\ +		    AR5K_4W_TX_DESC_CTL3_XMIT_RATE##_n);		\ +	} + +		_XTX_TRIES(1); +		_XTX_TRIES(2); +		_XTX_TRIES(3); + +#undef _XTX_TRIES + +		return true; +	} + +	return false; +} + +/* + * Proccess the tx status descriptor on 5210/5211 + */ +static int ath5k_hw_proc_2word_tx_status(struct ath5k_hw *ah, +		struct ath5k_desc *desc) +{ +	struct ath5k_hw_tx_status *tx_status; +	struct ath5k_hw_2w_tx_desc *tx_desc; + +	tx_desc = (struct ath5k_hw_2w_tx_desc *)&desc->ds_ctl0; +	tx_status = (struct ath5k_hw_tx_status *)&desc->ds_hw[0]; + +	/* No frame has been send or error */ +	if (unlikely((tx_status->tx_status_1 & AR5K_DESC_TX_STATUS1_DONE) == 0)) +		return -EINPROGRESS; + +	/* +	 * Get descriptor status +	 */ +	desc->ds_us.tx.ts_tstamp = AR5K_REG_MS(tx_status->tx_status_0, +		AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP); +	desc->ds_us.tx.ts_shortretry = AR5K_REG_MS(tx_status->tx_status_0, +		AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT); +	desc->ds_us.tx.ts_longretry = AR5K_REG_MS(tx_status->tx_status_0, +		AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT); +	/*TODO: desc->ds_us.tx.ts_virtcol + test*/ +	desc->ds_us.tx.ts_seqnum = AR5K_REG_MS(tx_status->tx_status_1, +		AR5K_DESC_TX_STATUS1_SEQ_NUM); +	desc->ds_us.tx.ts_rssi = AR5K_REG_MS(tx_status->tx_status_1, +		AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH); +	desc->ds_us.tx.ts_antenna = 1; +	desc->ds_us.tx.ts_status = 0; +	desc->ds_us.tx.ts_rate = AR5K_REG_MS(tx_desc->tx_control_0, +		AR5K_2W_TX_DESC_CTL0_XMIT_RATE); + +	if ((tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK) == 0){ +		if (tx_status->tx_status_0 & +				AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES) +			desc->ds_us.tx.ts_status |= AR5K_TXERR_XRETRY; + +		if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN) +			desc->ds_us.tx.ts_status |= AR5K_TXERR_FIFO; + +		if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FILTERED) +			desc->ds_us.tx.ts_status |= AR5K_TXERR_FILT; +	} + +	return 0; +} + +/* + * Proccess a tx descriptor on 5212 + */ +static int ath5k_hw_proc_4word_tx_status(struct ath5k_hw *ah, +		struct ath5k_desc *desc) +{ +	struct ath5k_hw_tx_status *tx_status; +	struct ath5k_hw_4w_tx_desc *tx_desc; + +	ATH5K_TRACE(ah->ah_sc); +	tx_desc = (struct ath5k_hw_4w_tx_desc *)&desc->ds_ctl0; +	tx_status = (struct ath5k_hw_tx_status *)&desc->ds_hw[2]; + +	/* No frame has been send or error */ +	if (unlikely((tx_status->tx_status_1 & AR5K_DESC_TX_STATUS1_DONE) == 0)) +		return -EINPROGRESS; + +	/* +	 * Get descriptor status +	 */ +	desc->ds_us.tx.ts_tstamp = AR5K_REG_MS(tx_status->tx_status_0, +		AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP); +	desc->ds_us.tx.ts_shortretry = AR5K_REG_MS(tx_status->tx_status_0, +		AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT); +	desc->ds_us.tx.ts_longretry = AR5K_REG_MS(tx_status->tx_status_0, +		AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT); +	desc->ds_us.tx.ts_seqnum = AR5K_REG_MS(tx_status->tx_status_1, +		AR5K_DESC_TX_STATUS1_SEQ_NUM); +	desc->ds_us.tx.ts_rssi = AR5K_REG_MS(tx_status->tx_status_1, +		AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH); +	desc->ds_us.tx.ts_antenna = (tx_status->tx_status_1 & +		AR5K_DESC_TX_STATUS1_XMIT_ANTENNA) ? 2 : 1; +	desc->ds_us.tx.ts_status = 0; + +	switch (AR5K_REG_MS(tx_status->tx_status_1, +			AR5K_DESC_TX_STATUS1_FINAL_TS_INDEX)) { +	case 0: +		desc->ds_us.tx.ts_rate = tx_desc->tx_control_3 & +			AR5K_4W_TX_DESC_CTL3_XMIT_RATE0; +		break; +	case 1: +		desc->ds_us.tx.ts_rate = AR5K_REG_MS(tx_desc->tx_control_3, +			AR5K_4W_TX_DESC_CTL3_XMIT_RATE1); +		desc->ds_us.tx.ts_longretry +=AR5K_REG_MS(tx_desc->tx_control_2, +			AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1); +		break; +	case 2: +		desc->ds_us.tx.ts_rate = AR5K_REG_MS(tx_desc->tx_control_3, +			AR5K_4W_TX_DESC_CTL3_XMIT_RATE2); +		desc->ds_us.tx.ts_longretry +=AR5K_REG_MS(tx_desc->tx_control_2, +			AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2); +		break; +	case 3: +		desc->ds_us.tx.ts_rate = AR5K_REG_MS(tx_desc->tx_control_3, +			AR5K_4W_TX_DESC_CTL3_XMIT_RATE3); +		desc->ds_us.tx.ts_longretry +=AR5K_REG_MS(tx_desc->tx_control_2, +			AR5K_4W_TX_DESC_CTL2_XMIT_TRIES3); +		break; +	} + +	if ((tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK) == 0){ +		if (tx_status->tx_status_0 & +				AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES) +			desc->ds_us.tx.ts_status |= AR5K_TXERR_XRETRY; + +		if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN) +			desc->ds_us.tx.ts_status |= AR5K_TXERR_FIFO; + +		if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FILTERED) +			desc->ds_us.tx.ts_status |= AR5K_TXERR_FILT; +	} + +	return 0; +} + +/* + * RX Descriptor + */ + +/* + * Initialize an rx descriptor + */ +int ath5k_hw_setup_rx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc, +			u32 size, unsigned int flags) +{ +	struct ath5k_rx_desc *rx_desc; + +	ATH5K_TRACE(ah->ah_sc); +	rx_desc = (struct ath5k_rx_desc *)&desc->ds_ctl0; + +	/* +	 *Clear ds_hw +	 * If we don't clean the status descriptor, +	 * while scanning we get too many results, +	 * most of them virtual, after some secs +	 * of scanning system hangs. M.F. +	*/ +	memset(desc->ds_hw, 0, sizeof(desc->ds_hw)); + +	/*Initialize rx descriptor*/ +	rx_desc->rx_control_0 = 0; +	rx_desc->rx_control_1 = 0; + +	/* Setup descriptor */ +	rx_desc->rx_control_1 = size & AR5K_DESC_RX_CTL1_BUF_LEN; +	if (unlikely(rx_desc->rx_control_1 != size)) +		return -EINVAL; + +	if (flags & AR5K_RXDESC_INTREQ) +		rx_desc->rx_control_1 |= AR5K_DESC_RX_CTL1_INTREQ; + +	return 0; +} + +/* + * Proccess the rx status descriptor on 5210/5211 + */ +static int ath5k_hw_proc_old_rx_status(struct ath5k_hw *ah, +		struct ath5k_desc *desc) +{ +	struct ath5k_hw_old_rx_status *rx_status; + +	rx_status = (struct ath5k_hw_old_rx_status *)&desc->ds_hw[0]; + +	/* No frame received / not ready */ +	if (unlikely((rx_status->rx_status_1 & AR5K_OLD_RX_DESC_STATUS1_DONE) +				== 0)) +		return -EINPROGRESS; + +	/* +	 * Frame receive status +	 */ +	desc->ds_us.rx.rs_datalen = rx_status->rx_status_0 & +		AR5K_OLD_RX_DESC_STATUS0_DATA_LEN; +	desc->ds_us.rx.rs_rssi = AR5K_REG_MS(rx_status->rx_status_0, +		AR5K_OLD_RX_DESC_STATUS0_RECEIVE_SIGNAL); +	desc->ds_us.rx.rs_rate = AR5K_REG_MS(rx_status->rx_status_0, +		AR5K_OLD_RX_DESC_STATUS0_RECEIVE_RATE); +	desc->ds_us.rx.rs_antenna = rx_status->rx_status_0 & +		AR5K_OLD_RX_DESC_STATUS0_RECEIVE_ANTENNA; +	desc->ds_us.rx.rs_more = rx_status->rx_status_0 & +		AR5K_OLD_RX_DESC_STATUS0_MORE; +	desc->ds_us.rx.rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1, +		AR5K_OLD_RX_DESC_STATUS1_RECEIVE_TIMESTAMP); +	desc->ds_us.rx.rs_status = 0; + +	/* +	 * Key table status +	 */ +	if (rx_status->rx_status_1 & AR5K_OLD_RX_DESC_STATUS1_KEY_INDEX_VALID) +		desc->ds_us.rx.rs_keyix = AR5K_REG_MS(rx_status->rx_status_1, +			AR5K_OLD_RX_DESC_STATUS1_KEY_INDEX); +	else +		desc->ds_us.rx.rs_keyix = AR5K_RXKEYIX_INVALID; + +	/* +	 * Receive/descriptor errors +	 */ +	if ((rx_status->rx_status_1 & AR5K_OLD_RX_DESC_STATUS1_FRAME_RECEIVE_OK) +			== 0) { +		if (rx_status->rx_status_1 & AR5K_OLD_RX_DESC_STATUS1_CRC_ERROR) +			desc->ds_us.rx.rs_status |= AR5K_RXERR_CRC; + +		if (rx_status->rx_status_1 & +				AR5K_OLD_RX_DESC_STATUS1_FIFO_OVERRUN) +			desc->ds_us.rx.rs_status |= AR5K_RXERR_FIFO; + +		if (rx_status->rx_status_1 & +				AR5K_OLD_RX_DESC_STATUS1_PHY_ERROR) { +			desc->ds_us.rx.rs_status |= AR5K_RXERR_PHY; +			desc->ds_us.rx.rs_phyerr = +				AR5K_REG_MS(rx_status->rx_status_1, +					AR5K_OLD_RX_DESC_STATUS1_PHY_ERROR); +		} + +		if (rx_status->rx_status_1 & +				AR5K_OLD_RX_DESC_STATUS1_DECRYPT_CRC_ERROR) +			desc->ds_us.rx.rs_status |= AR5K_RXERR_DECRYPT; +	} + +	return 0; +} + +/* + * Proccess the rx status descriptor on 5212 + */ +static int ath5k_hw_proc_new_rx_status(struct ath5k_hw *ah, +		struct ath5k_desc *desc) +{ +	struct ath5k_hw_new_rx_status *rx_status; +	struct ath5k_hw_rx_error *rx_err; + +	ATH5K_TRACE(ah->ah_sc); +	rx_status = (struct ath5k_hw_new_rx_status *)&desc->ds_hw[0]; + +	/* Overlay on error */ +	rx_err = (struct ath5k_hw_rx_error *)&desc->ds_hw[0]; + +	/* No frame received / not ready */ +	if (unlikely((rx_status->rx_status_1 & AR5K_NEW_RX_DESC_STATUS1_DONE) +				== 0)) +		return -EINPROGRESS; + +	/* +	 * Frame receive status +	 */ +	desc->ds_us.rx.rs_datalen = rx_status->rx_status_0 & +		AR5K_NEW_RX_DESC_STATUS0_DATA_LEN; +	desc->ds_us.rx.rs_rssi = AR5K_REG_MS(rx_status->rx_status_0, +		AR5K_NEW_RX_DESC_STATUS0_RECEIVE_SIGNAL); +	desc->ds_us.rx.rs_rate = AR5K_REG_MS(rx_status->rx_status_0, +		AR5K_NEW_RX_DESC_STATUS0_RECEIVE_RATE); +	desc->ds_us.rx.rs_antenna = rx_status->rx_status_0 & +		AR5K_NEW_RX_DESC_STATUS0_RECEIVE_ANTENNA; +	desc->ds_us.rx.rs_more = rx_status->rx_status_0 & +		AR5K_NEW_RX_DESC_STATUS0_MORE; +	desc->ds_us.rx.rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1, +		AR5K_NEW_RX_DESC_STATUS1_RECEIVE_TIMESTAMP); +	desc->ds_us.rx.rs_status = 0; + +	/* +	 * Key table status +	 */ +	if (rx_status->rx_status_1 & AR5K_NEW_RX_DESC_STATUS1_KEY_INDEX_VALID) +		desc->ds_us.rx.rs_keyix = AR5K_REG_MS(rx_status->rx_status_1, +				AR5K_NEW_RX_DESC_STATUS1_KEY_INDEX); +	else +		desc->ds_us.rx.rs_keyix = AR5K_RXKEYIX_INVALID; + +	/* +	 * Receive/descriptor errors +	 */ +	if ((rx_status->rx_status_1 & +			AR5K_NEW_RX_DESC_STATUS1_FRAME_RECEIVE_OK) == 0) { +		if (rx_status->rx_status_1 & AR5K_NEW_RX_DESC_STATUS1_CRC_ERROR) +			desc->ds_us.rx.rs_status |= AR5K_RXERR_CRC; + +		if (rx_status->rx_status_1 & +				AR5K_NEW_RX_DESC_STATUS1_PHY_ERROR) { +			desc->ds_us.rx.rs_status |= AR5K_RXERR_PHY; +			desc->ds_us.rx.rs_phyerr = +				AR5K_REG_MS(rx_err->rx_error_1, +					AR5K_RX_DESC_ERROR1_PHY_ERROR_CODE); +		} + +		if (rx_status->rx_status_1 & +				AR5K_NEW_RX_DESC_STATUS1_DECRYPT_CRC_ERROR) +			desc->ds_us.rx.rs_status |= AR5K_RXERR_DECRYPT; + +		if (rx_status->rx_status_1 & AR5K_NEW_RX_DESC_STATUS1_MIC_ERROR) +			desc->ds_us.rx.rs_status |= AR5K_RXERR_MIC; +	} + +	return 0; +} + + +/****************\ +  GPIO Functions +\****************/ + +/* + * Set led state + */ +void ath5k_hw_set_ledstate(struct ath5k_hw *ah, unsigned int state) +{ +	u32 led; +	/*5210 has different led mode handling*/ +	u32 led_5210; + +	ATH5K_TRACE(ah->ah_sc); + +	/*Reset led status*/ +	if (ah->ah_version != AR5K_AR5210) +		AR5K_REG_DISABLE_BITS(ah, AR5K_PCICFG, +			AR5K_PCICFG_LEDMODE |  AR5K_PCICFG_LED); +	else +		AR5K_REG_DISABLE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_LED); + +	/* +	 * Some blinking values, define at your wish +	 */ +	switch (state) { +	case AR5K_LED_SCAN: +	case AR5K_LED_AUTH: +		led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_PEND; +		led_5210 = AR5K_PCICFG_LED_PEND | AR5K_PCICFG_LED_BCTL; +		break; + +	case AR5K_LED_INIT: +		led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_NONE; +		led_5210 = AR5K_PCICFG_LED_PEND; +		break; + +	case AR5K_LED_ASSOC: +	case AR5K_LED_RUN: +		led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_ASSOC; +		led_5210 = AR5K_PCICFG_LED_ASSOC; +		break; + +	default: +		led = AR5K_PCICFG_LEDMODE_PROM | AR5K_PCICFG_LED_NONE; +		led_5210 = AR5K_PCICFG_LED_PEND; +		break; +	} + +	/*Write new status to the register*/ +	if (ah->ah_version != AR5K_AR5210) +		AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, led); +	else +		AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, led_5210); +} + +/* + * Set GPIO outputs + */ +int ath5k_hw_set_gpio_output(struct ath5k_hw *ah, u32 gpio) +{ +	ATH5K_TRACE(ah->ah_sc); +	if (gpio > AR5K_NUM_GPIO) +		return -EINVAL; + +	ath5k_hw_reg_write(ah, (ath5k_hw_reg_read(ah, AR5K_GPIOCR) &~ +		AR5K_GPIOCR_OUT(gpio)) | AR5K_GPIOCR_OUT(gpio), AR5K_GPIOCR); + +	return 0; +} + +/* + * Set GPIO inputs + */ +int ath5k_hw_set_gpio_input(struct ath5k_hw *ah, u32 gpio) +{ +	ATH5K_TRACE(ah->ah_sc); +	if (gpio > AR5K_NUM_GPIO) +		return -EINVAL; + +	ath5k_hw_reg_write(ah, (ath5k_hw_reg_read(ah, AR5K_GPIOCR) &~ +		AR5K_GPIOCR_OUT(gpio)) | AR5K_GPIOCR_IN(gpio), AR5K_GPIOCR); + +	return 0; +} + +/* + * Get GPIO state + */ +u32 ath5k_hw_get_gpio(struct ath5k_hw *ah, u32 gpio) +{ +	ATH5K_TRACE(ah->ah_sc); +	if (gpio > AR5K_NUM_GPIO) +		return 0xffffffff; + +	/* GPIO input magic */ +	return ((ath5k_hw_reg_read(ah, AR5K_GPIODI) & AR5K_GPIODI_M) >> gpio) & +		0x1; +} + +/* + * Set GPIO state + */ +int ath5k_hw_set_gpio(struct ath5k_hw *ah, u32 gpio, u32 val) +{ +	u32 data; +	ATH5K_TRACE(ah->ah_sc); + +	if (gpio > AR5K_NUM_GPIO) +		return -EINVAL; + +	/* GPIO output magic */ +	data = ath5k_hw_reg_read(ah, AR5K_GPIODO); + +	data &= ~(1 << gpio); +	data |= (val & 1) << gpio; + +	ath5k_hw_reg_write(ah, data, AR5K_GPIODO); + +	return 0; +} + +/* + * Initialize the GPIO interrupt (RFKill switch) + */ +void ath5k_hw_set_gpio_intr(struct ath5k_hw *ah, unsigned int gpio, +		u32 interrupt_level) +{ +	u32 data; + +	ATH5K_TRACE(ah->ah_sc); +	if (gpio > AR5K_NUM_GPIO) +		return; + +	/* +	 * Set the GPIO interrupt +	 */ +	data = (ath5k_hw_reg_read(ah, AR5K_GPIOCR) & +		~(AR5K_GPIOCR_INT_SEL(gpio) | AR5K_GPIOCR_INT_SELH | +		AR5K_GPIOCR_INT_ENA | AR5K_GPIOCR_OUT(gpio))) | +		(AR5K_GPIOCR_INT_SEL(gpio) | AR5K_GPIOCR_INT_ENA); + +	ath5k_hw_reg_write(ah, interrupt_level ? data : +		(data | AR5K_GPIOCR_INT_SELH), AR5K_GPIOCR); + +	ah->ah_imr |= AR5K_IMR_GPIO; + +	/* Enable GPIO interrupts */ +	AR5K_REG_ENABLE_BITS(ah, AR5K_PIMR, AR5K_IMR_GPIO); +} + + +/*********************************\ + Regulatory Domain/Channels Setup +\*********************************/ + +u16 ath5k_get_regdomain(struct ath5k_hw *ah) +{ +	u16 regdomain; +	enum ath5k_regdom ieee_regdomain; +#ifdef COUNTRYCODE +	u16 code; +#endif + +	ath5k_eeprom_regulation_domain(ah, false, &ieee_regdomain); +	ah->ah_capabilities.cap_regdomain.reg_hw = ieee_regdomain; + +#ifdef COUNTRYCODE +	/* +	 * Get the regulation domain by country code. This will ignore +	 * the settings found in the EEPROM. +	 */ +	code = ieee80211_name2countrycode(COUNTRYCODE); +	ieee_regdomain = ieee80211_countrycode2regdomain(code); +#endif + +	regdomain = ath5k_regdom_from_ieee(ieee_regdomain); +	ah->ah_capabilities.cap_regdomain.reg_current = regdomain; + +	return regdomain; +} + + +/****************\ +  Misc functions +\****************/ + +int ath5k_hw_get_capability(struct ath5k_hw *ah, +		enum ath5k_capability_type cap_type, +		u32 capability, u32 *result) +{ +	ATH5K_TRACE(ah->ah_sc); + +	switch (cap_type) { +	case AR5K_CAP_NUM_TXQUEUES: +		if (result) { +			if (ah->ah_version == AR5K_AR5210) +				*result = AR5K_NUM_TX_QUEUES_NOQCU; +			else +				*result = AR5K_NUM_TX_QUEUES; +			goto yes; +		} +	case AR5K_CAP_VEOL: +		goto yes; +	case AR5K_CAP_COMPRESSION: +		if (ah->ah_version == AR5K_AR5212) +			goto yes; +		else +			goto no; +	case AR5K_CAP_BURST: +		goto yes; +	case AR5K_CAP_TPC: +		goto yes; +	case AR5K_CAP_BSSIDMASK: +		if (ah->ah_version == AR5K_AR5212) +			goto yes; +		else +			goto no; +	case AR5K_CAP_XR: +		if (ah->ah_version == AR5K_AR5212) +			goto yes; +		else +			goto no; +	default: +		goto no; +	} + +no: +	return -EINVAL; +yes: +	return 0; +} + +static int ath5k_hw_enable_pspoll(struct ath5k_hw *ah, u8 *bssid, +		u16 assoc_id) +{ +	ATH5K_TRACE(ah->ah_sc); + +	if (ah->ah_version == AR5K_AR5210) { +		AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, +			AR5K_STA_ID1_NO_PSPOLL | AR5K_STA_ID1_DEFAULT_ANTENNA); +		return 0; +	} + +	return -EIO; +} + +static int ath5k_hw_disable_pspoll(struct ath5k_hw *ah) +{ +	ATH5K_TRACE(ah->ah_sc); + +	if (ah->ah_version == AR5K_AR5210) { +		AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1, +			AR5K_STA_ID1_NO_PSPOLL | AR5K_STA_ID1_DEFAULT_ANTENNA); +		return 0; +	} + +	return -EIO; +} diff --git a/drivers/net/wireless/ath5k/hw.h b/drivers/net/wireless/ath5k/hw.h new file mode 100644 index 000000000000..d9a7c0973f53 --- /dev/null +++ b/drivers/net/wireless/ath5k/hw.h @@ -0,0 +1,588 @@ +/* + * Copyright (c) 2004-2007 Reyk Floeter <reyk@openbsd.org> + * Copyright (c) 2006-2007 Nick Kossifidis <mickflemm@gmail.com> + * Copyright (c) 2007 Matthew W. S. Bell  <mentor@madwifi.org> + * Copyright (c) 2007 Luis Rodriguez <mcgrof@winlab.rutgers.edu> + * + * 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. + */ + +#include <linux/delay.h> + +/* + * Gain settings + */ + +enum ath5k_rfgain { +	AR5K_RFGAIN_INACTIVE = 0, +	AR5K_RFGAIN_READ_REQUESTED, +	AR5K_RFGAIN_NEED_CHANGE, +}; + +#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 +#define AR5K_GAIN_PARAM_TX_CLIP			0 +#define AR5K_GAIN_PARAM_PD_90			1 +#define AR5K_GAIN_PARAM_PD_84			2 +#define AR5K_GAIN_PARAM_GAIN_SEL		3 +#define AR5K_GAIN_PARAM_MIX_ORN			0 +#define AR5K_GAIN_PARAM_PD_138			1 +#define AR5K_GAIN_PARAM_PD_137			2 +#define AR5K_GAIN_PARAM_PD_136			3 +#define AR5K_GAIN_PARAM_PD_132			4 +#define AR5K_GAIN_PARAM_PD_131			5 +#define AR5K_GAIN_PARAM_PD_130			6 +#define AR5K_GAIN_CHECK_ADJUST(_g) 		\ +	((_g)->g_current <= (_g)->g_low || (_g)->g_current >= (_g)->g_high) + +struct ath5k_gain_opt_step { +	s16				gos_param[AR5K_GAIN_CRN_MAX_FIX_BITS]; +	s32				gos_gain; +}; + +struct ath5k_gain { +	u32			g_step_idx; +	u32			g_current; +	u32			g_target; +	u32			g_low; +	u32			g_high; +	u32			g_f_corr; +	u32			g_active; +	const struct ath5k_gain_opt_step	*g_step; +}; + + +/* + * HW SPECIFIC STRUCTS + */ + +/* 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_MAX_CHAN		10 +#define AR5K_EEPROM_N_PCDAC		11 +#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_XPD_PER_CHANNEL	4 +#define AR5K_EEPROM_N_XPD0_POINTS	4 +#define AR5K_EEPROM_N_XPD3_POINTS	3 +#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 + +/* Struct to hold EEPROM calibration data */ +struct ath5k_eeprom_info { +	u16	ee_magic; +	u16	ee_protect; +	u16	ee_regdomain; +	u16	ee_version; +	u16	ee_header; +	u16	ee_ant_gain; +	u16	ee_misc0; +	u16	ee_misc1; +	u16	ee_cck_ofdm_gain_delta; +	u16	ee_cck_ofdm_power_delta; +	u16	ee_scaled_cck_delta; + +	/* Used for tx thermal adjustment (eeprom_init, rfregs) */ +	u16	ee_tx_clip; +	u16	ee_pwd_84; +	u16	ee_pwd_90; +	u16	ee_gain_select; + +	/* 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_ant_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]; + +	/* Unused */ +	u16	ee_false_detect[AR5K_EEPROM_N_MODES]; +	u16	ee_cal_pier[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_2GHZ_CHAN]; +	u16	ee_channel[AR5K_EEPROM_N_MODES][AR5K_EEPROM_MAX_CHAN]; /*empty*/ + +	/* Conformance test limits (Unused) */ +	u16	ee_ctls; +	u16	ee_ctl[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]; +}; + +/* + * Internal RX/TX descriptor structures + * (rX: reserved fields possibily used by future versions of the ar5k chipset) + */ + +struct ath5k_rx_desc { +	u32	rx_control_0; /* RX control word 0 */ + +#define AR5K_DESC_RX_CTL0			0x00000000 + +	u32	rx_control_1; /* RX control word 1 */ + +#define AR5K_DESC_RX_CTL1_BUF_LEN		0x00000fff +#define AR5K_DESC_RX_CTL1_INTREQ		0x00002000 +} __packed; + +/* + * 5210/5211 rx status descriptor + */ +struct ath5k_hw_old_rx_status { +	u32	rx_status_0; /* RX status word 0 */ + +#define AR5K_OLD_RX_DESC_STATUS0_DATA_LEN		0x00000fff +#define AR5K_OLD_RX_DESC_STATUS0_MORE			0x00001000 +#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_RATE		0x00078000 +#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_RATE_S		15 +#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_SIGNAL		0x07f80000 +#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_SIGNAL_S	19 +#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_ANTENNA	0x38000000 +#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_ANTENNA_S	27 + +	u32	rx_status_1; /* RX status word 1 */ + +#define AR5K_OLD_RX_DESC_STATUS1_DONE			0x00000001 +#define AR5K_OLD_RX_DESC_STATUS1_FRAME_RECEIVE_OK	0x00000002 +#define AR5K_OLD_RX_DESC_STATUS1_CRC_ERROR		0x00000004 +#define AR5K_OLD_RX_DESC_STATUS1_FIFO_OVERRUN		0x00000008 +#define AR5K_OLD_RX_DESC_STATUS1_DECRYPT_CRC_ERROR	0x00000010 +#define AR5K_OLD_RX_DESC_STATUS1_PHY_ERROR		0x000000e0 +#define AR5K_OLD_RX_DESC_STATUS1_PHY_ERROR_S		5 +#define AR5K_OLD_RX_DESC_STATUS1_KEY_INDEX_VALID	0x00000100 +#define AR5K_OLD_RX_DESC_STATUS1_KEY_INDEX		0x00007e00 +#define AR5K_OLD_RX_DESC_STATUS1_KEY_INDEX_S		9 +#define AR5K_OLD_RX_DESC_STATUS1_RECEIVE_TIMESTAMP	0x0fff8000 +#define AR5K_OLD_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S	15 +#define AR5K_OLD_RX_DESC_STATUS1_KEY_CACHE_MISS		0x10000000 +} __packed; + +/* + * 5212 rx status descriptor + */ +struct ath5k_hw_new_rx_status { +	u32	rx_status_0; /* RX status word 0 */ + +#define AR5K_NEW_RX_DESC_STATUS0_DATA_LEN		0x00000fff +#define AR5K_NEW_RX_DESC_STATUS0_MORE			0x00001000 +#define AR5K_NEW_RX_DESC_STATUS0_DECOMP_CRC_ERROR	0x00002000 +#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_RATE		0x000f8000 +#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_RATE_S		15 +#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_SIGNAL		0x0ff00000 +#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_SIGNAL_S	20 +#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_ANTENNA	0xf0000000 +#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_ANTENNA_S	28 + +	u32	rx_status_1; /* RX status word 1 */ + +#define AR5K_NEW_RX_DESC_STATUS1_DONE			0x00000001 +#define AR5K_NEW_RX_DESC_STATUS1_FRAME_RECEIVE_OK	0x00000002 +#define AR5K_NEW_RX_DESC_STATUS1_CRC_ERROR		0x00000004 +#define AR5K_NEW_RX_DESC_STATUS1_DECRYPT_CRC_ERROR	0x00000008 +#define AR5K_NEW_RX_DESC_STATUS1_PHY_ERROR		0x00000010 +#define AR5K_NEW_RX_DESC_STATUS1_MIC_ERROR		0x00000020 +#define AR5K_NEW_RX_DESC_STATUS1_KEY_INDEX_VALID	0x00000100 +#define AR5K_NEW_RX_DESC_STATUS1_KEY_INDEX		0x0000fe00 +#define AR5K_NEW_RX_DESC_STATUS1_KEY_INDEX_S		9 +#define AR5K_NEW_RX_DESC_STATUS1_RECEIVE_TIMESTAMP	0x7fff0000 +#define AR5K_NEW_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S	16 +#define AR5K_NEW_RX_DESC_STATUS1_KEY_CACHE_MISS		0x80000000 +} __packed; + +struct ath5k_hw_rx_error { +	u32	rx_error_0; /* RX error word 0 */ + +#define AR5K_RX_DESC_ERROR0			0x00000000 + +	u32	rx_error_1; /* RX error word 1 */ + +#define AR5K_RX_DESC_ERROR1_PHY_ERROR_CODE	0x0000ff00 +#define AR5K_RX_DESC_ERROR1_PHY_ERROR_CODE_S	8 +} __packed; + +#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 + +struct ath5k_hw_2w_tx_desc { +	u32	tx_control_0; /* TX control word 0 */ + +#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 + +	u32	tx_control_1; /* TX control word 1 */ + +#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 ?]*/ +} __packed; + +#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 4-word tx control descriptor + */ +struct ath5k_hw_4w_tx_desc { +	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 */ + +#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 + +	u32	tx_status_1; /* TX status word 1 */ + +#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 +} __packed; + + +/* + * AR5K REGISTER ACCESS + */ + +/*Swap RX/TX Descriptor for big endian archs*/ +#if defined(__BIG_ENDIAN) +#define AR5K_INIT_CFG	(		\ +	AR5K_CFG_SWTD | AR5K_CFG_SWRD	\ +) +#else +#define AR5K_INIT_CFG	0x00000000 +#endif + +/*#define AR5K_REG_READ(_reg)	ath5k_hw_reg_read(ah, _reg) + +#define AR5K_REG_WRITE(_reg, _val)	ath5k_hw_reg_write(ah, _val, _reg)*/ + +#define AR5K_REG_SM(_val, _flags)					\ +	(((_val) << _flags##_S) & (_flags)) + +#define AR5K_REG_MS(_val, _flags)					\ +	(((_val) & (_flags)) >> _flags##_S) + +/* Some registers can hold multiple values of interest. For this + * reason when we want to write to these registers we must first + * retrieve the values which we do not want to clear (lets call this + * old_data) and then set the register with this and our new_value: + * ( old_data | new_value) */ +#define AR5K_REG_WRITE_BITS(ah, _reg, _flags, _val)			\ +	ath5k_hw_reg_write(ah, (ath5k_hw_reg_read(ah, _reg) & ~(_flags)) | \ +	    (((_val) << _flags##_S) & (_flags)), _reg) + +#define AR5K_REG_MASKED_BITS(ah, _reg, _flags, _mask)			\ +	ath5k_hw_reg_write(ah, (ath5k_hw_reg_read(ah, _reg) &		\ +			(_mask)) | (_flags), _reg) + +#define AR5K_REG_ENABLE_BITS(ah, _reg, _flags)				\ +	ath5k_hw_reg_write(ah, ath5k_hw_reg_read(ah, _reg) | (_flags), _reg) + +#define AR5K_REG_DISABLE_BITS(ah, _reg, _flags)			\ +	ath5k_hw_reg_write(ah, ath5k_hw_reg_read(ah, _reg) & ~(_flags), _reg) + +#define AR5K_PHY_WRITE(ah, _reg, _val)					\ +	ath5k_hw_reg_write(ah, _val, (ah)->ah_phy + ((_reg) << 2)) + +#define AR5K_PHY_READ(ah, _reg)					\ +	ath5k_hw_reg_read(ah, (ah)->ah_phy + ((_reg) << 2)) + +#define AR5K_REG_WAIT(_i) do {						\ +	if (_i % 64)							\ +		udelay(1);						\ +} while (0) + +#define AR5K_EEPROM_READ(_o, _v) do {					\ +	if ((ret = ath5k_hw_eeprom_read(ah, (_o), &(_v))) != 0)	\ +		return (ret);						\ +} while (0) + +#define AR5K_EEPROM_READ_HDR(_o, _v)					\ +	AR5K_EEPROM_READ(_o, ah->ah_capabilities.cap_eeprom._v);	\ + +/* Read status of selected queue */ +#define AR5K_REG_READ_Q(ah, _reg, _queue)				\ +	(ath5k_hw_reg_read(ah, _reg) & (1 << _queue))			\ + +#define AR5K_REG_WRITE_Q(ah, _reg, _queue)				\ +	ath5k_hw_reg_write(ah, (1 << _queue), _reg) + +#define AR5K_Q_ENABLE_BITS(_reg, _queue) do {				\ +	_reg |= 1 << _queue;						\ +} while (0) + +#define AR5K_Q_DISABLE_BITS(_reg, _queue) do {				\ +	_reg &= ~(1 << _queue);						\ +} while (0) + +#define AR5K_LOW_ID(_a)(				\ +(_a)[0] | (_a)[1] << 8 | (_a)[2] << 16 | (_a)[3] << 24	\ +) + +#define AR5K_HIGH_ID(_a)	((_a)[4] | (_a)[5] << 8) + +/* + * Initial register values + */ + +/* + * Common initial register values + */ +#define AR5K_INIT_MODE				CHANNEL_B + +#define AR5K_INIT_TX_LATENCY			502 +#define AR5K_INIT_USEC				39 +#define AR5K_INIT_USEC_TURBO			79 +#define AR5K_INIT_USEC_32			31 +#define AR5K_INIT_CARR_SENSE_EN			1 +#define AR5K_INIT_PROG_IFS			920 +#define AR5K_INIT_PROG_IFS_TURBO		960 +#define AR5K_INIT_EIFS				3440 +#define AR5K_INIT_EIFS_TURBO			6880 +#define AR5K_INIT_SLOT_TIME			396 +#define AR5K_INIT_SLOT_TIME_TURBO		480 +#define AR5K_INIT_ACK_CTS_TIMEOUT		1024 +#define AR5K_INIT_ACK_CTS_TIMEOUT_TURBO		0x08000800 +#define AR5K_INIT_SIFS				560 +#define AR5K_INIT_SIFS_TURBO			480 +#define AR5K_INIT_SH_RETRY			10 +#define AR5K_INIT_LG_RETRY			AR5K_INIT_SH_RETRY +#define AR5K_INIT_SSH_RETRY			32 +#define AR5K_INIT_SLG_RETRY			AR5K_INIT_SSH_RETRY +#define AR5K_INIT_TX_RETRY			10 +#define AR5K_INIT_TOPS				8 +#define AR5K_INIT_RXNOFRM			8 +#define AR5K_INIT_RPGTO				0 +#define AR5K_INIT_TXNOFRM			0 +#define AR5K_INIT_BEACON_PERIOD			65535 +#define AR5K_INIT_TIM_OFFSET			0 +#define AR5K_INIT_BEACON_EN			0 +#define AR5K_INIT_RESET_TSF			0 + +#define AR5K_INIT_TRANSMIT_LATENCY		(			\ +	(AR5K_INIT_TX_LATENCY << 14) | (AR5K_INIT_USEC_32 << 7) |	\ +	(AR5K_INIT_USEC)						\ +) +#define AR5K_INIT_TRANSMIT_LATENCY_TURBO	(			\ +	(AR5K_INIT_TX_LATENCY << 14) | (AR5K_INIT_USEC_32 << 7) |	\ +	(AR5K_INIT_USEC_TURBO)						\ +) +#define AR5K_INIT_PROTO_TIME_CNTRL		(			\ +	(AR5K_INIT_CARR_SENSE_EN << 26) | (AR5K_INIT_EIFS << 12) |	\ +	(AR5K_INIT_PROG_IFS)						\ +) +#define AR5K_INIT_PROTO_TIME_CNTRL_TURBO	(			\ +	(AR5K_INIT_CARR_SENSE_EN << 26) | (AR5K_INIT_EIFS_TURBO << 12) | \ +	(AR5K_INIT_PROG_IFS_TURBO)					\ +) +#define AR5K_INIT_BEACON_CONTROL		(			\ +	(AR5K_INIT_RESET_TSF << 24) | (AR5K_INIT_BEACON_EN << 23) |	\ +	(AR5K_INIT_TIM_OFFSET << 16) | (AR5K_INIT_BEACON_PERIOD)	\ +) + +/* + * Non-common initial register values which have to be loaded into the + * card at boot time and after each reset. + */ + +/* Register dumps are done per operation mode */ +#define AR5K_INI_RFGAIN_5GHZ		0 +#define AR5K_INI_RFGAIN_2GHZ		1 + +#define AR5K_INI_VAL_11A		0 +#define AR5K_INI_VAL_11A_TURBO		1 +#define AR5K_INI_VAL_11B		2 +#define AR5K_INI_VAL_11G		3 +#define AR5K_INI_VAL_11G_TURBO		4 +#define AR5K_INI_VAL_XR			0 +#define AR5K_INI_VAL_MAX		5 + +#define AR5K_RF5111_INI_RF_MAX_BANKS	AR5K_MAX_RF_BANKS +#define AR5K_RF5112_INI_RF_MAX_BANKS	AR5K_MAX_RF_BANKS + +static inline u32 ath5k_hw_bitswap(u32 val, unsigned int bits) +{ +	u32 retval = 0, bit, i; + +	for (i = 0; i < bits; i++) { +		bit = (val >> i) & 1; +		retval = (retval << 1) | bit; +	} + +	return retval; +} diff --git a/drivers/net/wireless/ath5k/initvals.c b/drivers/net/wireless/ath5k/initvals.c new file mode 100644 index 000000000000..2c22f1d4ee64 --- /dev/null +++ b/drivers/net/wireless/ath5k/initvals.c @@ -0,0 +1,1347 @@ +/* + * Initial register settings functions + * + * Copyright (c) 2004, 2005, 2006, 2007 Reyk Floeter <reyk@openbsd.org> + * Copyright (c) 2006, 2007 Nick Kossifidis <mickflemm@gmail.com> + * Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "ath5k.h" +#include "base.h" +#include "reg.h" + +/* + * MAC/PHY REGISTERS + */ + + +/* + * Mode-independent initial register writes + */ + +struct ath5k_ini { +	u16	ini_register; +	u32	ini_value; + +	enum { +		AR5K_INI_WRITE = 0,	/* Default */ +		AR5K_INI_READ = 1,	/* Cleared on read */ +	} ini_mode; +}; + +/* + * Mode specific initial register values + */ + +struct ath5k_ini_mode { +	u16	mode_register; +	u32	mode_value[5]; +}; + +/* Initial register settings for AR5210 */ +static const struct ath5k_ini ar5210_ini[] = { +	/* PCU and MAC registers */ +	{ AR5K_NOQCU_TXDP0,	0 }, +	{ AR5K_NOQCU_TXDP1,	0 }, +	{ AR5K_RXDP,		0 }, +	{ AR5K_CR,		0 }, +	{ AR5K_ISR,		0, AR5K_INI_READ }, +	{ AR5K_IMR,		0 }, +	{ AR5K_IER,		AR5K_IER_DISABLE }, +	{ AR5K_BSR,		0, AR5K_INI_READ }, +	{ AR5K_TXCFG,		AR5K_DMASIZE_128B }, +	{ AR5K_RXCFG,		AR5K_DMASIZE_128B }, +	{ AR5K_CFG,		AR5K_INIT_CFG }, +	{ AR5K_TOPS,		AR5K_INIT_TOPS }, +	{ AR5K_RXNOFRM,		AR5K_INIT_RXNOFRM }, +	{ AR5K_RPGTO,		AR5K_INIT_RPGTO }, +	{ AR5K_TXNOFRM,		AR5K_INIT_TXNOFRM }, +	{ AR5K_SFR,		0 }, +	{ AR5K_MIBC,		0 }, +	{ AR5K_MISC,		0 }, +	{ AR5K_RX_FILTER_5210,	0 }, +	{ AR5K_MCAST_FILTER0_5210, 0 }, +	{ AR5K_MCAST_FILTER1_5210, 0 }, +	{ AR5K_TX_MASK0,	0 }, +	{ AR5K_TX_MASK1,	0 }, +	{ AR5K_CLR_TMASK,	0 }, +	{ AR5K_TRIG_LVL,	AR5K_TUNE_MIN_TX_FIFO_THRES }, +	{ AR5K_DIAG_SW_5210,	0 }, +	{ AR5K_RSSI_THR,	AR5K_TUNE_RSSI_THRES }, +	{ AR5K_TSF_L32_5210,	0 }, +	{ AR5K_TIMER0_5210,	0 }, +	{ AR5K_TIMER1_5210,	0xffffffff }, +	{ AR5K_TIMER2_5210,	0xffffffff }, +	{ AR5K_TIMER3_5210,	1 }, +	{ AR5K_CFP_DUR_5210,	0 }, +	{ AR5K_CFP_PERIOD_5210,	0 }, +	/* PHY registers */ +	{ AR5K_PHY(0),	0x00000047 }, +	{ AR5K_PHY_AGC,	0x00000000 }, +	{ AR5K_PHY(3),	0x09848ea6 }, +	{ AR5K_PHY(4),	0x3d32e000 }, +	{ AR5K_PHY(5),	0x0000076b }, +	{ AR5K_PHY_ACT,	AR5K_PHY_ACT_DISABLE }, +	{ AR5K_PHY(8),	0x02020200 }, +	{ AR5K_PHY(9),	0x00000e0e }, +	{ AR5K_PHY(10),	0x0a020201 }, +	{ AR5K_PHY(11),	0x00036ffc }, +	{ AR5K_PHY(12),	0x00000000 }, +	{ AR5K_PHY(13),	0x00000e0e }, +	{ AR5K_PHY(14),	0x00000007 }, +	{ AR5K_PHY(15),	0x00020100 }, +	{ AR5K_PHY(16),	0x89630000 }, +	{ AR5K_PHY(17),	0x1372169c }, +	{ AR5K_PHY(18),	0x0018b633 }, +	{ AR5K_PHY(19),	0x1284613c }, +	{ AR5K_PHY(20),	0x0de8b8e0 }, +	{ AR5K_PHY(21),	0x00074859 }, +	{ AR5K_PHY(22),	0x7e80beba }, +	{ AR5K_PHY(23),	0x313a665e }, +	{ AR5K_PHY_AGCCTL, 0x00001d08 }, +	{ AR5K_PHY(25),	0x0001ce00 }, +	{ AR5K_PHY(26),	0x409a4190 }, +	{ AR5K_PHY(28),	0x0000000f }, +	{ AR5K_PHY(29),	0x00000080 }, +	{ AR5K_PHY(30),	0x00000004 }, +	{ AR5K_PHY(31),	0x00000018 }, 	/* 0x987c */ +	{ AR5K_PHY(64),	0x00000000 }, 	/* 0x9900 */ +	{ AR5K_PHY(65),	0x00000000 }, +	{ AR5K_PHY(66),	0x00000000 }, +	{ AR5K_PHY(67),	0x00800000 }, +	{ AR5K_PHY(68),	0x00000003 }, +	/* BB gain table (64bytes) */ +	{ AR5K_BB_GAIN(0), 0x00000000 }, +	{ AR5K_BB_GAIN(1), 0x00000020 }, +	{ AR5K_BB_GAIN(2), 0x00000010 }, +	{ AR5K_BB_GAIN(3), 0x00000030 }, +	{ AR5K_BB_GAIN(4), 0x00000008 }, +	{ AR5K_BB_GAIN(5), 0x00000028 }, +	{ AR5K_BB_GAIN(6), 0x00000028 }, +	{ AR5K_BB_GAIN(7), 0x00000004 }, +	{ AR5K_BB_GAIN(8), 0x00000024 }, +	{ AR5K_BB_GAIN(9), 0x00000014 }, +	{ AR5K_BB_GAIN(10), 0x00000034 }, +	{ AR5K_BB_GAIN(11), 0x0000000c }, +	{ AR5K_BB_GAIN(12), 0x0000002c }, +	{ AR5K_BB_GAIN(13), 0x00000002 }, +	{ AR5K_BB_GAIN(14), 0x00000022 }, +	{ AR5K_BB_GAIN(15), 0x00000012 }, +	{ AR5K_BB_GAIN(16), 0x00000032 }, +	{ AR5K_BB_GAIN(17), 0x0000000a }, +	{ AR5K_BB_GAIN(18), 0x0000002a }, +	{ AR5K_BB_GAIN(19), 0x00000001 }, +	{ AR5K_BB_GAIN(20), 0x00000021 }, +	{ AR5K_BB_GAIN(21), 0x00000011 }, +	{ AR5K_BB_GAIN(22), 0x00000031 }, +	{ AR5K_BB_GAIN(23), 0x00000009 }, +	{ AR5K_BB_GAIN(24), 0x00000029 }, +	{ AR5K_BB_GAIN(25), 0x00000005 }, +	{ AR5K_BB_GAIN(26), 0x00000025 }, +	{ AR5K_BB_GAIN(27), 0x00000015 }, +	{ AR5K_BB_GAIN(28), 0x00000035 }, +	{ AR5K_BB_GAIN(29), 0x0000000d }, +	{ AR5K_BB_GAIN(30), 0x0000002d }, +	{ AR5K_BB_GAIN(31), 0x00000003 }, +	{ AR5K_BB_GAIN(32), 0x00000023 }, +	{ AR5K_BB_GAIN(33), 0x00000013 }, +	{ AR5K_BB_GAIN(34), 0x00000033 }, +	{ AR5K_BB_GAIN(35), 0x0000000b }, +	{ AR5K_BB_GAIN(36), 0x0000002b }, +	{ AR5K_BB_GAIN(37), 0x00000007 }, +	{ AR5K_BB_GAIN(38), 0x00000027 }, +	{ AR5K_BB_GAIN(39), 0x00000017 }, +	{ AR5K_BB_GAIN(40), 0x00000037 }, +	{ AR5K_BB_GAIN(41), 0x0000000f }, +	{ AR5K_BB_GAIN(42), 0x0000002f }, +	{ AR5K_BB_GAIN(43), 0x0000002f }, +	{ AR5K_BB_GAIN(44), 0x0000002f }, +	{ AR5K_BB_GAIN(45), 0x0000002f }, +	{ AR5K_BB_GAIN(46), 0x0000002f }, +	{ AR5K_BB_GAIN(47), 0x0000002f }, +	{ AR5K_BB_GAIN(48), 0x0000002f }, +	{ AR5K_BB_GAIN(49), 0x0000002f }, +	{ AR5K_BB_GAIN(50), 0x0000002f }, +	{ AR5K_BB_GAIN(51), 0x0000002f }, +	{ AR5K_BB_GAIN(52), 0x0000002f }, +	{ AR5K_BB_GAIN(53), 0x0000002f }, +	{ AR5K_BB_GAIN(54), 0x0000002f }, +	{ AR5K_BB_GAIN(55), 0x0000002f }, +	{ AR5K_BB_GAIN(56), 0x0000002f }, +	{ AR5K_BB_GAIN(57), 0x0000002f }, +	{ AR5K_BB_GAIN(58), 0x0000002f }, +	{ AR5K_BB_GAIN(59), 0x0000002f }, +	{ AR5K_BB_GAIN(60), 0x0000002f }, +	{ AR5K_BB_GAIN(61), 0x0000002f }, +	{ AR5K_BB_GAIN(62), 0x0000002f }, +	{ AR5K_BB_GAIN(63), 0x0000002f }, +	/* 5110 RF gain table (64btes) */ +	{ AR5K_RF_GAIN(0), 0x0000001d }, +	{ AR5K_RF_GAIN(1), 0x0000005d }, +	{ AR5K_RF_GAIN(2), 0x0000009d }, +	{ AR5K_RF_GAIN(3), 0x000000dd }, +	{ AR5K_RF_GAIN(4), 0x0000011d }, +	{ AR5K_RF_GAIN(5), 0x00000021 }, +	{ AR5K_RF_GAIN(6), 0x00000061 }, +	{ AR5K_RF_GAIN(7), 0x000000a1 }, +	{ AR5K_RF_GAIN(8), 0x000000e1 }, +	{ AR5K_RF_GAIN(9), 0x00000031 }, +	{ AR5K_RF_GAIN(10), 0x00000071 }, +	{ AR5K_RF_GAIN(11), 0x000000b1 }, +	{ AR5K_RF_GAIN(12), 0x0000001c }, +	{ AR5K_RF_GAIN(13), 0x0000005c }, +	{ AR5K_RF_GAIN(14), 0x00000029 }, +	{ AR5K_RF_GAIN(15), 0x00000069 }, +	{ AR5K_RF_GAIN(16), 0x000000a9 }, +	{ AR5K_RF_GAIN(17), 0x00000020 }, +	{ AR5K_RF_GAIN(18), 0x00000019 }, +	{ AR5K_RF_GAIN(19), 0x00000059 }, +	{ AR5K_RF_GAIN(20), 0x00000099 }, +	{ AR5K_RF_GAIN(21), 0x00000030 }, +	{ AR5K_RF_GAIN(22), 0x00000005 }, +	{ AR5K_RF_GAIN(23), 0x00000025 }, +	{ AR5K_RF_GAIN(24), 0x00000065 }, +	{ AR5K_RF_GAIN(25), 0x000000a5 }, +	{ AR5K_RF_GAIN(26), 0x00000028 }, +	{ AR5K_RF_GAIN(27), 0x00000068 }, +	{ AR5K_RF_GAIN(28), 0x0000001f }, +	{ AR5K_RF_GAIN(29), 0x0000001e }, +	{ AR5K_RF_GAIN(30), 0x00000018 }, +	{ AR5K_RF_GAIN(31), 0x00000058 }, +	{ AR5K_RF_GAIN(32), 0x00000098 }, +	{ AR5K_RF_GAIN(33), 0x00000003 }, +	{ AR5K_RF_GAIN(34), 0x00000004 }, +	{ AR5K_RF_GAIN(35), 0x00000044 }, +	{ AR5K_RF_GAIN(36), 0x00000084 }, +	{ AR5K_RF_GAIN(37), 0x00000013 }, +	{ AR5K_RF_GAIN(38), 0x00000012 }, +	{ AR5K_RF_GAIN(39), 0x00000052 }, +	{ AR5K_RF_GAIN(40), 0x00000092 }, +	{ AR5K_RF_GAIN(41), 0x000000d2 }, +	{ AR5K_RF_GAIN(42), 0x0000002b }, +	{ AR5K_RF_GAIN(43), 0x0000002a }, +	{ AR5K_RF_GAIN(44), 0x0000006a }, +	{ AR5K_RF_GAIN(45), 0x000000aa }, +	{ AR5K_RF_GAIN(46), 0x0000001b }, +	{ AR5K_RF_GAIN(47), 0x0000001a }, +	{ AR5K_RF_GAIN(48), 0x0000005a }, +	{ AR5K_RF_GAIN(49), 0x0000009a }, +	{ AR5K_RF_GAIN(50), 0x000000da }, +	{ AR5K_RF_GAIN(51), 0x00000006 }, +	{ AR5K_RF_GAIN(52), 0x00000006 }, +	{ AR5K_RF_GAIN(53), 0x00000006 }, +	{ AR5K_RF_GAIN(54), 0x00000006 }, +	{ AR5K_RF_GAIN(55), 0x00000006 }, +	{ AR5K_RF_GAIN(56), 0x00000006 }, +	{ AR5K_RF_GAIN(57), 0x00000006 }, +	{ AR5K_RF_GAIN(58), 0x00000006 }, +	{ AR5K_RF_GAIN(59), 0x00000006 }, +	{ AR5K_RF_GAIN(60), 0x00000006 }, +	{ AR5K_RF_GAIN(61), 0x00000006 }, +	{ AR5K_RF_GAIN(62), 0x00000006 }, +	{ AR5K_RF_GAIN(63), 0x00000006 }, +	/* PHY activation */ +	{ AR5K_PHY(53), 0x00000020 }, +	{ AR5K_PHY(51), 0x00000004 }, +	{ AR5K_PHY(50), 0x00060106 }, +	{ AR5K_PHY(39), 0x0000006d }, +	{ AR5K_PHY(48), 0x00000000 }, +	{ AR5K_PHY(52), 0x00000014 }, +	{ AR5K_PHY_ACT, AR5K_PHY_ACT_ENABLE }, +}; + +/* Initial register settings for AR5211 */ +static const struct ath5k_ini ar5211_ini[] = { +	{ AR5K_RXDP,		0x00000000 }, +	{ AR5K_RTSD0,		0x84849c9c }, +	{ AR5K_RTSD1,		0x7c7c7c7c }, +	{ AR5K_RXCFG,		0x00000005 }, +	{ AR5K_MIBC,		0x00000000 }, +	{ AR5K_TOPS,		0x00000008 }, +	{ AR5K_RXNOFRM,		0x00000008 }, +	{ AR5K_TXNOFRM,		0x00000010 }, +	{ AR5K_RPGTO,		0x00000000 }, +	{ AR5K_RFCNT,		0x0000001f }, +	{ AR5K_QUEUE_TXDP(0),	0x00000000 }, +	{ AR5K_QUEUE_TXDP(1),	0x00000000 }, +	{ AR5K_QUEUE_TXDP(2),	0x00000000 }, +	{ AR5K_QUEUE_TXDP(3),	0x00000000 }, +	{ AR5K_QUEUE_TXDP(4),	0x00000000 }, +	{ AR5K_QUEUE_TXDP(5),	0x00000000 }, +	{ AR5K_QUEUE_TXDP(6),	0x00000000 }, +	{ AR5K_QUEUE_TXDP(7),	0x00000000 }, +	{ AR5K_QUEUE_TXDP(8),	0x00000000 }, +	{ AR5K_QUEUE_TXDP(9),	0x00000000 }, +	{ AR5K_DCU_FP,		0x00000000 }, +	{ AR5K_STA_ID1,		0x00000000 }, +	{ AR5K_BSS_ID0,		0x00000000 }, +	{ AR5K_BSS_ID1,		0x00000000 }, +	{ AR5K_RSSI_THR,	0x00000000 }, +	{ AR5K_CFP_PERIOD_5211,	0x00000000 }, +	{ AR5K_TIMER0_5211,	0x00000030 }, +	{ AR5K_TIMER1_5211,	0x0007ffff }, +	{ AR5K_TIMER2_5211,	0x01ffffff }, +	{ AR5K_TIMER3_5211,	0x00000031 }, +	{ AR5K_CFP_DUR_5211,	0x00000000 }, +	{ AR5K_RX_FILTER_5211,	0x00000000 }, +	{ AR5K_MCAST_FILTER0_5211, 0x00000000 }, +	{ AR5K_MCAST_FILTER1_5211, 0x00000002 }, +	{ AR5K_DIAG_SW_5211,	0x00000000 }, +	{ AR5K_ADDAC_TEST,	0x00000000 }, +	{ AR5K_DEFAULT_ANTENNA,	0x00000000 }, +	/* PHY registers */ +	{ AR5K_PHY_AGC,	0x00000000 }, +	{ AR5K_PHY(3),	0x2d849093 }, +	{ AR5K_PHY(4),	0x7d32e000 }, +	{ AR5K_PHY(5),	0x00000f6b }, +	{ AR5K_PHY_ACT,	0x00000000 }, +	{ AR5K_PHY(11),	0x00026ffe }, +	{ AR5K_PHY(12),	0x00000000 }, +	{ AR5K_PHY(15),	0x00020100 }, +	{ AR5K_PHY(16),	0x206a017a }, +	{ AR5K_PHY(19),	0x1284613c }, +	{ AR5K_PHY(21),	0x00000859 }, +	{ AR5K_PHY(26),	0x409a4190 },	/* 0x9868 */ +	{ AR5K_PHY(27),	0x050cb081 }, +	{ AR5K_PHY(28),	0x0000000f }, +	{ AR5K_PHY(29),	0x00000080 }, +	{ AR5K_PHY(30),	0x0000000c }, +	{ AR5K_PHY(64),	0x00000000 }, +	{ AR5K_PHY(65),	0x00000000 }, +	{ AR5K_PHY(66),	0x00000000 }, +	{ AR5K_PHY(67),	0x00800000 }, +	{ AR5K_PHY(68),	0x00000001 }, +	{ AR5K_PHY(71),	0x0000092a }, +	{ AR5K_PHY_IQ,	0x00000000 }, +	{ AR5K_PHY(73),	0x00058a05 }, +	{ AR5K_PHY(74),	0x00000001 }, +	{ AR5K_PHY(75),	0x00000000 }, +	{ AR5K_PHY_PAPD_PROBE, 0x00000000 }, +	{ AR5K_PHY(77),	0x00000000 },	/* 0x9934 */ +	{ AR5K_PHY(78),	0x00000000 },	/* 0x9938 */ +	{ AR5K_PHY(79),	0x0000003f },	/* 0x993c */ +	{ AR5K_PHY(80),	0x00000004 }, +	{ AR5K_PHY(82),	0x00000000 }, +	{ AR5K_PHY(83),	0x00000000 }, +	{ AR5K_PHY(84),	0x00000000 }, +	{ AR5K_PHY_RADAR, 0x5d50f14c }, +	{ AR5K_PHY(86),	0x00000018 }, +	{ AR5K_PHY(87),	0x004b6a8e }, +	/* Initial Power table (32bytes) +	 * common on all cards/modes. +	 * Note: Table is rewritten during +	 * txpower setup later using calibration +	 * data etc. so next write is non-common +	{ AR5K_PHY_PCDAC_TXPOWER(1), 0x06ff05ff }, +	{ AR5K_PHY_PCDAC_TXPOWER(2), 0x07ff07ff }, +	{ AR5K_PHY_PCDAC_TXPOWER(3), 0x08ff08ff }, +	{ AR5K_PHY_PCDAC_TXPOWER(4), 0x09ff09ff }, +	{ AR5K_PHY_PCDAC_TXPOWER(5), 0x0aff0aff }, +	{ AR5K_PHY_PCDAC_TXPOWER(6), 0x0bff0bff }, +	{ AR5K_PHY_PCDAC_TXPOWER(7), 0x0cff0cff }, +	{ AR5K_PHY_PCDAC_TXPOWER(8), 0x0dff0dff }, +	{ AR5K_PHY_PCDAC_TXPOWER(9), 0x0fff0eff }, +	{ AR5K_PHY_PCDAC_TXPOWER(10), 0x12ff12ff }, +	{ AR5K_PHY_PCDAC_TXPOWER(11), 0x14ff13ff }, +	{ AR5K_PHY_PCDAC_TXPOWER(12), 0x16ff15ff }, +	{ AR5K_PHY_PCDAC_TXPOWER(13), 0x19ff17ff }, +	{ AR5K_PHY_PCDAC_TXPOWER(14), 0x1bff1aff }, +	{ AR5K_PHY_PCDAC_TXPOWER(15), 0x1eff1dff }, +	{ AR5K_PHY_PCDAC_TXPOWER(16), 0x23ff20ff }, +	{ AR5K_PHY_PCDAC_TXPOWER(17), 0x27ff25ff }, +	{ AR5K_PHY_PCDAC_TXPOWER(18), 0x2cff29ff }, +	{ AR5K_PHY_PCDAC_TXPOWER(19), 0x31ff2fff }, +	{ AR5K_PHY_PCDAC_TXPOWER(20), 0x37ff34ff }, +	{ AR5K_PHY_PCDAC_TXPOWER(21), 0x3aff3aff }, +	{ AR5K_PHY_PCDAC_TXPOWER(22), 0x3aff3aff }, +	{ AR5K_PHY_PCDAC_TXPOWER(23), 0x3aff3aff }, +	{ AR5K_PHY_PCDAC_TXPOWER(24), 0x3aff3aff }, +	{ AR5K_PHY_PCDAC_TXPOWER(25), 0x3aff3aff }, +	{ AR5K_PHY_PCDAC_TXPOWER(26), 0x3aff3aff }, +	{ AR5K_PHY_PCDAC_TXPOWER(27), 0x3aff3aff }, +	{ AR5K_PHY_PCDAC_TXPOWER(28), 0x3aff3aff }, +	{ AR5K_PHY_PCDAC_TXPOWER(29), 0x3aff3aff }, +	{ AR5K_PHY_PCDAC_TXPOWER(30), 0x3aff3aff }, +	{ AR5K_PHY_PCDAC_TXPOWER(31), 0x3aff3aff },*/ +	{ AR5K_PHY_CCKTXCTL, 0x00000000 }, +	{ AR5K_PHY(642), 0x503e4646 }, +	{ AR5K_PHY_GAIN_2GHZ, 0x6480416c }, +	{ AR5K_PHY(644), 0x0199a003 }, +	{ AR5K_PHY(645), 0x044cd610 }, +	{ AR5K_PHY(646), 0x13800040 }, +	{ AR5K_PHY(647), 0x1be00060 }, +	{ AR5K_PHY(648), 0x0c53800a }, +	{ AR5K_PHY(649), 0x0014df3b }, +	{ AR5K_PHY(650), 0x000001b5 }, +	{ AR5K_PHY(651), 0x00000020 }, +}; + +/* Initial mode-specific settings for AR5211 + * XXX: how about g / gTurbo ? RF5111 supports it, how about AR5211 ? + * Maybe 5211 supports OFDM-only g but we need to test it ! + */ +static const struct ath5k_ini_mode ar5211_ini_mode[] = { +	{ AR5K_TXCFG, +	/*	  a	      aTurbo	  b		*/ +		{ 0x00000015, 0x00000015, 0x0000001d } }, +	{ AR5K_QUEUE_DFS_LOCAL_IFS(0), +		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f } }, +	{ AR5K_QUEUE_DFS_LOCAL_IFS(1), +		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f } }, +	{ AR5K_QUEUE_DFS_LOCAL_IFS(2), +		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f } }, +	{ AR5K_QUEUE_DFS_LOCAL_IFS(3), +		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f } }, +	{ AR5K_QUEUE_DFS_LOCAL_IFS(4), +		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f } }, +	{ AR5K_QUEUE_DFS_LOCAL_IFS(5), +		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f } }, +	{ AR5K_QUEUE_DFS_LOCAL_IFS(6), +		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f } }, +	{ AR5K_QUEUE_DFS_LOCAL_IFS(7), +		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f } }, +	{ AR5K_QUEUE_DFS_LOCAL_IFS(8), +		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f } }, +	{ AR5K_QUEUE_DFS_LOCAL_IFS(9), +		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f } }, +	{ AR5K_DCU_GBL_IFS_SLOT, +		{ 0x00000168, 0x000001e0, 0x000001b8 } }, +	{ AR5K_DCU_GBL_IFS_SIFS, +		{ 0x00000230, 0x000001e0, 0x000000b0 } }, +	{ AR5K_DCU_GBL_IFS_EIFS, +		{ 0x00000d98, 0x00001180, 0x00001f48 } }, +	{ AR5K_DCU_GBL_IFS_MISC, +		{ 0x0000a0e0, 0x00014068, 0x00005880 } }, +	{ AR5K_TIME_OUT, +		{ 0x04000400, 0x08000800, 0x20003000 } }, +	{ AR5K_USEC_5211, +		{ 0x0e8d8fa7, 0x0e8d8fcf, 0x01608f95 } }, +	{ AR5K_PHY_TURBO, +		{ 0x00000000, 0x00000003, 0x00000000 } }, +	{ AR5K_PHY(8), +		{ 0x02020200, 0x02020200, 0x02010200 } }, +	{ AR5K_PHY(9), +		{ 0x00000e0e, 0x00000e0e, 0x00000707 } }, +	{ AR5K_PHY(10), +		{ 0x0a020001, 0x0a020001, 0x05010000 } }, +	{ AR5K_PHY(13), +		{ 0x00000e0e, 0x00000e0e, 0x00000e0e } }, +	{ AR5K_PHY(14), +		{ 0x00000007, 0x00000007, 0x0000000b } }, +	{ AR5K_PHY(17), +		{ 0x1372169c, 0x137216a5, 0x137216a8 } }, +	{ AR5K_PHY(18), +		{ 0x0018ba67, 0x0018ba67, 0x0018ba69 } }, +	{ AR5K_PHY(20), +		{ 0x0c28b4e0, 0x0c28b4e0, 0x0c28b4e0 } }, +	{ AR5K_PHY_SIG, +		{ 0x7e800d2e, 0x7e800d2e, 0x7ec00d2e } }, +	{ AR5K_PHY_AGCCOARSE, +		{ 0x31375d5e, 0x31375d5e, 0x313a5d5e } }, +	{ AR5K_PHY_AGCCTL, +		{ 0x0000bd10, 0x0000bd10, 0x0000bd38 } }, +	{ AR5K_PHY_NF, +		{ 0x0001ce00, 0x0001ce00, 0x0001ce00 } }, +	{ AR5K_PHY_RX_DELAY, +		{ 0x00002710, 0x00002710, 0x0000157c } }, +	{ AR5K_PHY(70), +		{ 0x00000190, 0x00000190, 0x00000084 } }, +	{ AR5K_PHY_FRAME_CTL_5211, +		{ 0x6fe01020, 0x6fe01020, 0x6fe00920 } }, +	{ AR5K_PHY_PCDAC_TXPOWER_BASE_5211, +		{ 0x05ff14ff, 0x05ff14ff, 0x05ff14ff } }, +	{ AR5K_RF_BUFFER_CONTROL_4, +		{ 0x00000010, 0x00000014, 0x00000010 } }, +}; + +/* Initial register settings for AR5212 */ +static const struct ath5k_ini ar5212_ini[] = { +	{ AR5K_RXDP,		0x00000000 }, +	{ AR5K_RXCFG,		0x00000005 }, +	{ AR5K_MIBC,		0x00000000 }, +	{ AR5K_TOPS,		0x00000008 }, +	{ AR5K_RXNOFRM,		0x00000008 }, +	{ AR5K_TXNOFRM,		0x00000010 }, +	{ AR5K_RPGTO,		0x00000000 }, +	{ AR5K_RFCNT,		0x0000001f }, +	{ AR5K_QUEUE_TXDP(0),	0x00000000 }, +	{ AR5K_QUEUE_TXDP(1),	0x00000000 }, +	{ AR5K_QUEUE_TXDP(2),	0x00000000 }, +	{ AR5K_QUEUE_TXDP(3),	0x00000000 }, +	{ AR5K_QUEUE_TXDP(4),	0x00000000 }, +	{ AR5K_QUEUE_TXDP(5),	0x00000000 }, +	{ AR5K_QUEUE_TXDP(6),	0x00000000 }, +	{ AR5K_QUEUE_TXDP(7),	0x00000000 }, +	{ AR5K_QUEUE_TXDP(8),	0x00000000 }, +	{ AR5K_QUEUE_TXDP(9),	0x00000000 }, +	{ AR5K_DCU_FP,		0x00000000 }, +	{ AR5K_DCU_TXP,		0x00000000 }, +	{ AR5K_DCU_TX_FILTER,	0x00000000 }, +	/* Unknown table */ +	{ 0x1078, 0x00000000 }, +	{ 0x10b8, 0x00000000 }, +	{ 0x10f8, 0x00000000 }, +	{ 0x1138, 0x00000000 }, +	{ 0x1178, 0x00000000 }, +	{ 0x11b8, 0x00000000 }, +	{ 0x11f8, 0x00000000 }, +	{ 0x1238, 0x00000000 }, +	{ 0x1278, 0x00000000 }, +	{ 0x12b8, 0x00000000 }, +	{ 0x12f8, 0x00000000 }, +	{ 0x1338, 0x00000000 }, +	{ 0x1378, 0x00000000 }, +	{ 0x13b8, 0x00000000 }, +	{ 0x13f8, 0x00000000 }, +	{ 0x1438, 0x00000000 }, +	{ 0x1478, 0x00000000 }, +	{ 0x14b8, 0x00000000 }, +	{ 0x14f8, 0x00000000 }, +	{ 0x1538, 0x00000000 }, +	{ 0x1578, 0x00000000 }, +	{ 0x15b8, 0x00000000 }, +	{ 0x15f8, 0x00000000 }, +	{ 0x1638, 0x00000000 }, +	{ 0x1678, 0x00000000 }, +	{ 0x16b8, 0x00000000 }, +	{ 0x16f8, 0x00000000 }, +	{ 0x1738, 0x00000000 }, +	{ 0x1778, 0x00000000 }, +	{ 0x17b8, 0x00000000 }, +	{ 0x17f8, 0x00000000 }, +	{ 0x103c, 0x00000000 }, +	{ 0x107c, 0x00000000 }, +	{ 0x10bc, 0x00000000 }, +	{ 0x10fc, 0x00000000 }, +	{ 0x113c, 0x00000000 }, +	{ 0x117c, 0x00000000 }, +	{ 0x11bc, 0x00000000 }, +	{ 0x11fc, 0x00000000 }, +	{ 0x123c, 0x00000000 }, +	{ 0x127c, 0x00000000 }, +	{ 0x12bc, 0x00000000 }, +	{ 0x12fc, 0x00000000 }, +	{ 0x133c, 0x00000000 }, +	{ 0x137c, 0x00000000 }, +	{ 0x13bc, 0x00000000 }, +	{ 0x13fc, 0x00000000 }, +	{ 0x143c, 0x00000000 }, +	{ 0x147c, 0x00000000 }, +	{ AR5K_DCU_TX_FILTER_CLR, 0x00000000 }, +	{ AR5K_DCU_TX_FILTER_SET, 0x00000000 }, +	{ AR5K_STA_ID1,		0x00000000 }, +	{ AR5K_BSS_ID0,		0x00000000 }, +	{ AR5K_BSS_ID1,		0x00000000 }, +	/*{ AR5K_RSSI_THR,	0x00000000 },*/	/* Found on SuperAG cards */ +	{ AR5K_BEACON_5211,	0x00000000 },	/* Found on SuperAG cards */ +	{ AR5K_CFP_PERIOD_5211, 0x00000000 },	/* Found on SuperAG cards */ +	{ AR5K_TIMER0_5211,	0x00000030 },	/* Found on SuperAG cards */ +	{ AR5K_TIMER1_5211,	0x0007ffff },	/* Found on SuperAG cards */ +	{ AR5K_TIMER2_5211,	0x01ffffff },	/* Found on SuperAG cards */ +	{ AR5K_TIMER3_5211,	0x00000031 },	/* Found on SuperAG cards */ +	{ AR5K_CFP_DUR_5211,	0x00000000 },	/* Found on SuperAG cards */ +	{ AR5K_RX_FILTER_5211,	0x00000000 }, +	{ AR5K_DIAG_SW_5211,	0x00000000 }, +	{ AR5K_ADDAC_TEST,	0x00000000 }, +	{ AR5K_DEFAULT_ANTENNA,	0x00000000 }, +	{ 0x8080, 0x00000000 }, +	/*{ 0x805c, 0xffffc7ff },*/ /* Old value */ +	{ 0x805c, 0x000fc78f }, +	{ AR5K_NAV_5211,	0x00000000 },	/* Not found on recent */ +	{ AR5K_RTS_OK_5211,	0x00000000 },	/* dumps but it makes  */ +	{ AR5K_RTS_FAIL_5211,	0x00000000 },	/* sense to reset counters */ +	{ AR5K_ACK_FAIL_5211,	0x00000000 },	/* since pcu registers */ +	{ AR5K_FCS_FAIL_5211,	0x00000000 },	/* are skiped during chan*/ +	{ AR5K_BEACON_CNT_5211, 0x00000000 },	/* change */ +	{ AR5K_XRMODE,		0x2a82301a }, +	{ AR5K_XRDELAY,		0x05dc01e0 }, +	{ AR5K_XRTIMEOUT,	0x1f402710 }, +	{ AR5K_XRCHIRP,		0x01f40000 }, +	{ AR5K_XRSTOMP,		0x00001e1c }, +	{ AR5K_SLEEP0,		0x0002aaaa },	/* Found on SuperAG cards */ +	{ AR5K_SLEEP1,		0x02005555 },	/* Found on SuperAG cards */ +	{ AR5K_SLEEP2,		0x00000000 },	/* Found on SuperAG cards */ +	{ AR5K_BSS_IDM0,	0xffffffff }, +	{ AR5K_BSS_IDM1,	0x0000ffff }, +	{ AR5K_TXPC,		0x00000000 }, +	{ AR5K_PROFCNT_TX,	0x00000000 }, +	{ AR5K_PROFCNT_RX,	0x00000000 }, +	{ AR5K_PROFCNT_RXCLR,	0x00000000 }, +	{ AR5K_PROFCNT_CYCLE,	0x00000000 }, +	{ 0x80fc, 0x00000088 }, +	{ AR5K_RATE_DUR(0),	0x00000000 }, +	{ AR5K_RATE_DUR(1),	0x0000008c }, +	{ AR5K_RATE_DUR(2),	0x000000e4 }, +	{ AR5K_RATE_DUR(3),	0x000002d5 }, +	{ AR5K_RATE_DUR(4),	0x00000000 }, +	{ AR5K_RATE_DUR(5),	0x00000000 }, +	{ AR5K_RATE_DUR(6),	0x000000a0 }, +	{ AR5K_RATE_DUR(7),	0x000001c9 }, +	{ AR5K_RATE_DUR(8),	0x0000002c }, +	{ AR5K_RATE_DUR(9),	0x0000002c }, +	{ AR5K_RATE_DUR(10),	0x00000030 }, +	{ AR5K_RATE_DUR(11),	0x0000003c }, +	{ AR5K_RATE_DUR(12),	0x0000002c }, +	{ AR5K_RATE_DUR(13),	0x0000002c }, +	{ AR5K_RATE_DUR(14),	0x00000030 }, +	{ AR5K_RATE_DUR(15),	0x0000003c }, +	{ AR5K_RATE_DUR(16),	0x00000000 }, +	{ AR5K_RATE_DUR(17),	0x00000000 }, +	{ AR5K_RATE_DUR(18),	0x00000000 }, +	{ AR5K_RATE_DUR(19),	0x00000000 }, +	{ AR5K_RATE_DUR(20),	0x00000000 }, +	{ AR5K_RATE_DUR(21),	0x00000000 }, +	{ AR5K_RATE_DUR(22),	0x00000000 }, +	{ AR5K_RATE_DUR(23),	0x00000000 }, +	{ AR5K_RATE_DUR(24),	0x000000d5 }, +	{ AR5K_RATE_DUR(25),	0x000000df }, +	{ AR5K_RATE_DUR(26),	0x00000102 }, +	{ AR5K_RATE_DUR(27),	0x0000013a }, +	{ AR5K_RATE_DUR(28),	0x00000075 }, +	{ AR5K_RATE_DUR(29),	0x0000007f }, +	{ AR5K_RATE_DUR(30),	0x000000a2 }, +	{ AR5K_RATE_DUR(31),	0x00000000 }, +	{ 0x8100, 0x00010002}, +	{ AR5K_TSF_PARM,	0x00000001 }, +	{ 0x8108, 0x000000c0 }, +	{ AR5K_PHY_ERR_FIL,	0x00000000 }, +	{ 0x8110, 0x00000168 }, +	{ 0x8114, 0x00000000 }, +	/* Some kind of table +	 * also notice ...03<-02<-01<-00) */ +	{ 0x87c0, 0x03020100 }, +	{ 0x87c4, 0x07060504 }, +	{ 0x87c8, 0x0b0a0908 }, +	{ 0x87cc, 0x0f0e0d0c }, +	{ 0x87d0, 0x13121110 }, +	{ 0x87d4, 0x17161514 }, +	{ 0x87d8, 0x1b1a1918 }, +	{ 0x87dc, 0x1f1e1d1c }, +	/* loop ? */ +	{ 0x87e0, 0x03020100 }, +	{ 0x87e4, 0x07060504 }, +	{ 0x87e8, 0x0b0a0908 }, +	{ 0x87ec, 0x0f0e0d0c }, +	{ 0x87f0, 0x13121110 }, +	{ 0x87f4, 0x17161514 }, +	{ 0x87f8, 0x1b1a1918 }, +	{ 0x87fc, 0x1f1e1d1c }, +	/* PHY registers */ +	/*{ AR5K_PHY_AGC, 0x00000000 },*/ +	{ AR5K_PHY(3),	0xad848e19 }, +	{ AR5K_PHY(4),	0x7d28e000 }, +	{ AR5K_PHY_TIMING_3, 0x9c0a9f6b }, +	{ AR5K_PHY_ACT,	0x00000000 }, +	/*{ AR5K_PHY(11), 0x00022ffe },*/ +	/*{ AR5K_PHY(15), 0x00020100 },*/ +	{ AR5K_PHY(16),	0x206a017a }, +	/*{ AR5K_PHY(19), 0x1284613c },*/ +	{ AR5K_PHY(21),	0x00000859 }, +	{ AR5K_PHY(64),	0x00000000 }, +	{ AR5K_PHY(65),	0x00000000 }, +	{ AR5K_PHY(66),	0x00000000 }, +	{ AR5K_PHY(67),	0x00800000 }, +	{ AR5K_PHY(68),	0x00000001 }, +	/*{ AR5K_PHY(71), 0x0000092a },*/ /* Old value */ +	{ AR5K_PHY(71),	0x00000c80 }, +	{ AR5K_PHY_IQ,	0x05100000 }, +	{ AR5K_PHY(74), 0x00000001 }, +	{ AR5K_PHY(75), 0x00000004 }, +	{ AR5K_PHY_TXPOWER_RATE1, 0x1e1f2022 }, +	{ AR5K_PHY_TXPOWER_RATE2, 0x0a0b0c0d }, +	{ AR5K_PHY_TXPOWER_RATE_MAX, 0x0000003f }, +	/*{ AR5K_PHY(80), 0x00000004 },*/ +	{ AR5K_PHY(82), 0x9280b212 }, +	{ AR5K_PHY_RADAR, 0x5d50e188 }, +	/*{ AR5K_PHY(86), 0x000000ff },*/ +	{ AR5K_PHY(87), 0x004b6a8e }, +	{ AR5K_PHY(90),	0x000003ce }, +	{ AR5K_PHY(92),	0x192fb515 }, +	/*{ AR5K_PHY(93), 0x00000000 },*/ +	{ AR5K_PHY(94),	0x00000001 }, +	{ AR5K_PHY(95),	0x00000000 }, +	/*{ AR5K_PHY(644), 0x0080a333 },*/ /* Old value */ +	/*{ AR5K_PHY(645), 0x00206c10 },*/ /* Old value */ +	{ AR5K_PHY(644), 0x00806333 }, +	{ AR5K_PHY(645), 0x00106c10 }, +	{ AR5K_PHY(646), 0x009c4060 }, +	/*{ AR5K_PHY(647), 0x1483800a },*/ /* Old value */ +	{ AR5K_PHY(647), 0x1483800a }, +	{ AR5K_PHY(648), 0x01831061 }, +	{ AR5K_PHY(649), 0x00000400 }, +	/*{ AR5K_PHY(650), 0x000001b5 },*/ +	{ AR5K_PHY(651), 0x00000000 }, +	{ AR5K_PHY_TXPOWER_RATE3, 0x20202020 }, +	{ AR5K_PHY_TXPOWER_RATE2, 0x20202020 }, +	/*{ AR5K_PHY(655), 0x13c889af },*/ +	{ AR5K_PHY(656), 0x38490a20 }, +	{ AR5K_PHY(657), 0x00007bb6 }, +	{ AR5K_PHY(658), 0x0fff3ffc }, +	/*{ AR5K_PHY_CCKTXCTL, 0x00000000 },*/ +}; + +/* Initial mode-specific settings for AR5212 (Written before ar5212_ini) */ +static const struct ath5k_ini_mode ar5212_ini_mode_start[] = { +	{ AR5K_PHY(640), +	/*	  a/XR	      aTurbo	  b	      g (DYN)	  gTurbo */ +		{ 0x00000008, 0x00000008, 0x0000000b, 0x0000000e, 0x0000000e } }, +	{ AR5K_PHY(0), +		{ 0x00000007, 0x00000007, 0x00000007, 0x00000007, 0x00000007 } }, +	{ AR5K_QUEUE_DFS_LOCAL_IFS(0), +		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, +	{ AR5K_QUEUE_DFS_LOCAL_IFS(1), +		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, +	{ AR5K_QUEUE_DFS_LOCAL_IFS(2), +		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, +	{ AR5K_QUEUE_DFS_LOCAL_IFS(3), +		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, +	{ AR5K_QUEUE_DFS_LOCAL_IFS(4), +		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, +	{ AR5K_QUEUE_DFS_LOCAL_IFS(5), +		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, +	{ AR5K_QUEUE_DFS_LOCAL_IFS(6), +		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, +	{ AR5K_QUEUE_DFS_LOCAL_IFS(7), +		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, +	{ AR5K_QUEUE_DFS_LOCAL_IFS(8), +		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, +	{ AR5K_QUEUE_DFS_LOCAL_IFS(9), +		{ 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, +	{ AR5K_DCU_GBL_IFS_SIFS, +		{ 0x00000230, 0x000001e0, 0x000000b0, 0x00000160, 0x000001e0 } }, +	{ AR5K_DCU_GBL_IFS_SLOT, +		{ 0x00000168, 0x000001e0, 0x000001b8, 0x0000018c, 0x000001e0 } }, +	{ AR5K_DCU_GBL_IFS_EIFS, +		{ 0x00000e60, 0x00001180, 0x00001f1c, 0x00003e38, 0x00001180 } }, +	{ AR5K_DCU_GBL_IFS_MISC, +		{ 0x0000a0e0, 0x00014068, 0x00005880, 0x0000b0e0, 0x00014068 } }, +	{ AR5K_TIME_OUT, +		{ 0x03e803e8, 0x06e006e0, 0x04200420, 0x08400840, 0x06e006e0 } }, +	{ AR5K_PHY_TURBO, +		{ 0x00000000, 0x00000003, 0x00000000, 0x00000000, 0x00000003 } }, +	{ AR5K_PHY(8), +		{ 0x02020200, 0x02020200, 0x02010200, 0x02020200, 0x02020200 } }, +	{ AR5K_PHY(9), +		{ 0x00000e0e, 0x00000e0e, 0x00000707, 0x00000e0e, 0x00000e0e } }, +	{ AR5K_PHY(17), +		{ 0x1372161c, 0x13721c25, 0x13721722, 0x137216a2, 0x13721c25 } }, +	{ AR5K_PHY_AGCCTL, +		{ 0x00009d10, 0x00009d10, 0x00009d18, 0x00009d18, 0x00009d18 } }, +	{ AR5K_PHY_NF, +		{ 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 } }, +	{ AR5K_PHY(26), +		{ 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190 } }, +	{ AR5K_PHY(70), +		{ 0x000001b8, 0x000001b8, 0x00000084, 0x00000108, 0x000001b8 } }, +	{ AR5K_PHY(73), +		{ 0x10058a05, 0x10058a05, 0x10058a05, 0x10058a05, 0x10058a05 } }, +	{ 0xa230, +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000108, 0x00000000 } }, +}; + +/* Initial mode-specific settings for AR5212 + RF5111 (Written after ar5212_ini) */ +/* New dump pending */ +static const struct ath5k_ini_mode ar5212_rf5111_ini_mode_end[] = { +	{ AR5K_PHY(640), /* This one differs from ar5212_ini_mode_start ! */ +	/*	  a/XR	      aTurbo	  b	      g (DYN)	  gTurbo */ +		{ 0x00000000, 0x00000000, 0x00000003, 0x00000006, 0x00000006 } }, +	{ AR5K_TXCFG, +		{ 0x00008015, 0x00008015, 0x00008015, 0x00008015, 0x00008015 } }, +	{ AR5K_USEC_5211, +		{ 0x128d8fa7, 0x09880fcf, 0x04e00f95, 0x12e00fab, 0x09880fcf } }, +	{ AR5K_PHY(10), +		{ 0x0a020001, 0x0a020001, 0x05010100, 0x0a020001, 0x0a020001 } }, +	{ AR5K_PHY(13), +		{ 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e } }, +	{ AR5K_PHY(14), +		{ 0x00000007, 0x00000007, 0x0000000b, 0x0000000b, 0x0000000b } }, +	{ AR5K_PHY(18), +		{ 0x0018da5a, 0x0018da5a, 0x0018ca69, 0x0018ca69, 0x0018ca69 } }, +	{ AR5K_PHY(20), +		{ 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0 } }, +	{ AR5K_PHY_SIG, +		{ 0x7e800d2e, 0x7e800d2e, 0x7ee84d2e, 0x7ee84d2e, 0x7e800d2e } }, +	{ AR5K_PHY_AGCCOARSE, +		{ 0x3137665e, 0x3137665e, 0x3137665e, 0x3137665e, 0x3137615e } }, +	{ AR5K_PHY(27), +		{ 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb080, 0x050cb080 } }, +	{ AR5K_PHY_RX_DELAY, +		{ 0x00002710, 0x00002710, 0x0000157c, 0x00002af8, 0x00002710 } }, +	{ AR5K_PHY_FRAME_CTL_5211, +		{ 0xf7b81020, 0xf7b81020, 0xf7b80d20, 0xf7b81020, 0xf7b81020 } }, +	{ AR5K_PHY_GAIN_2GHZ, +		{ 0x642c416a, 0x642c416a, 0x6440416a, 0x6440416a, 0x6440416a } }, +	{ 0xa21c, +		{ 0x1883800a, 0x1883800a, 0x1873800a, 0x1883800a, 0x1883800a } }, +	{ AR5K_DCU_FP, +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ AR5K_PHY_AGC, +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ AR5K_PHY(11), +		{ 0x00022ffe, 0x00022ffe, 0x00022ffe, 0x00022ffe, 0x00022ffe } }, +	{ AR5K_PHY(15), +		{ 0x00020100, 0x00020100, 0x00020100, 0x00020100, 0x00020100 } }, +	{ AR5K_PHY(19), +		{ 0x1284613c, 0x1284613c, 0x1284613c, 0x1284613c, 0x1284613c } }, +	{ AR5K_PHY_PAPD_PROBE, +		{ 0x00004883, 0x00004883, 0x00004883, 0x00004883, 0x00004883 } }, +	{ AR5K_PHY(80), +		{ 0x00000004, 0x00000004, 0x00000004, 0x00000004, 0x00000004 } }, +	{ AR5K_PHY(86), +		{ 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff } }, +	{ AR5K_PHY(93), +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ AR5K_PHY_SPENDING, +		{ 0x00000018, 0x00000018, 0x00000018, 0x00000018, 0x00000018 } }, +	{ AR5K_PHY_CCKTXCTL, +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ AR5K_PHY(642), +		{ 0xd03e6788, 0xd03e6788, 0xd03e6788, 0xd03e6788, 0xd03e6788 } }, +	{ 0xa23c, +		{ 0x13c889af, 0x13c889af, 0x13c889af, 0x13c889af, 0x13c889af } }, +}; + +/* Initial mode-specific settings for AR5212 + RF5112 (Written after ar5212_ini) */ +/* XXX: No dumps for turbog yet, but i found settings from old values so it should be ok */ +static const struct ath5k_ini_mode ar5212_rf5112_ini_mode_end[] = { +	{ AR5K_TXCFG, +	/*	  a/XR	      aTurbo	  b	      g (DYN)	  gTurbo */ +		{ 0x00008015, 0x00008015, 0x00008015, 0x00008015, 0x00008015 } }, +	{ AR5K_USEC_5211, +		{ 0x128d93a7, 0x098813cf, 0x04e01395, 0x12e013ab, 0x098813cf } }, +	{ AR5K_PHY(10), +		{ 0x0a020001, 0x0a020001, 0x05020100, 0x0a020001, 0x0a020001 } }, +	{ AR5K_PHY(13), +		{ 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e } }, +	{ AR5K_PHY(14), +		{ 0x00000007, 0x00000007, 0x0000000b, 0x0000000b, 0x0000000b } }, +	{ AR5K_PHY(18), +		{ 0x0018da6d, 0x0018da6d, 0x0018ca75, 0x0018ca75, 0x0018ca75 } }, +	{ AR5K_PHY(20), +		{ 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0 } }, +	{ AR5K_PHY_SIG, +		{ 0x7e800d2e, 0x7e800d2e, 0x7ee80d2e, 0x7ee80d2e, 0x7ee80d2e } }, +	{ AR5K_PHY_AGCCOARSE, +		{ 0x3137665e, 0x3137665e, 0x3137665e, 0x3137665e, 0x3137665e } }, +	{ AR5K_PHY(27), +		{ 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081 } }, +	{ AR5K_PHY_RX_DELAY, +		{ 0x000007d0, 0x000007d0, 0x0000044c, 0x00000898, 0x000007d0 } }, +	{ AR5K_PHY_FRAME_CTL_5211, +		{ 0xf7b81020, 0xf7b81020, 0xf7b80d10, 0xf7b81010, 0xf7b81010 } }, +	{ AR5K_PHY_CCKTXCTL, +		{ 0x00000000, 0x00000000, 0x00000008, 0x00000008, 0x00000008 } }, +	{ AR5K_PHY(642), +		{ 0xd6be6788, 0xd6be6788, 0xd03e6788, 0xd03e6788, 0xd03e6788 } }, +	{ AR5K_PHY_GAIN_2GHZ, +		{ 0x642c0140, 0x642c0140, 0x6442c160, 0x6442c160, 0x6442c160 } }, +	{ 0xa21c, +		{ 0x1883800a, 0x1883800a, 0x1873800a, 0x1883800a, 0x1883800a } }, +	{ AR5K_DCU_FP, +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ AR5K_PHY_AGC, +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ AR5K_PHY(11), +		{ 0x00022ffe, 0x00022ffe, 0x00022ffe, 0x00022ffe, 0x00022ffe } }, +	{ AR5K_PHY(15), +		{ 0x00020100, 0x00020100, 0x00020100, 0x00020100, 0x00020100 } }, +	{ AR5K_PHY(19), +		{ 0x1284613c, 0x1284613c, 0x1284613c, 0x1284613c, 0x1284613c } }, +	{ AR5K_PHY_PAPD_PROBE, +		{ 0x00004882, 0x00004882, 0x00004882, 0x00004882, 0x00004882 } }, +	{ AR5K_PHY(80), +		{ 0x00000004, 0x00000004, 0x00000004, 0x00000004, 0x00000004 } }, +	{ AR5K_PHY(86), +		{ 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff } }, +	{ AR5K_PHY(93), +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0xa228, +		{ 0x000001b5, 0x000001b5, 0x000001b5, 0x000001b5, 0x000001b5 } }, +	{ 0xa23c, +		{ 0x13c889af, 0x13c889af, 0x13c889af, 0x13c889af, 0x13c889af } }, +}; + +/* Initial mode-specific settings for RF5413/5414 (Written after ar5212_ini) */ +/* XXX: No dumps for turbog yet, so turbog is the same with g here with some + * minor tweaking based on dumps from other chips */ +static const struct ath5k_ini_mode rf5413_ini_mode_end[] = { +	{ AR5K_TXCFG, +	/*	  a/XR	      aTurbo	  b	      g		  gTurbo */ +		{ 0x00000015, 0x00000015, 0x00000015, 0x00000015, 0x00000015 } }, +	{ AR5K_USEC_5211, +		{ 0x128d93a7, 0x098813cf, 0x04e01395, 0x12e013ab, 0x098813cf } }, +	{ AR5K_PHY(10), +		{ 0x0a020001, 0x0a020001, 0x05020100, 0x0a020001, 0x0a020001 } }, +	{ AR5K_PHY(13), +		{ 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e } }, +	{ AR5K_PHY(14), +		{ 0x00000007, 0x00000007, 0x0000000b, 0x0000000b, 0x0000000b } }, +	{ AR5K_PHY(18), +		{ 0x0018fa61, 0x0018fa61, 0x001a1a63, 0x001a1a63, 0x001a1a63 } }, +	{ AR5K_PHY(20), +		{ 0x0c98b4e0, 0x0c98b4e0, 0x0c98b0da, 0x0c98b0da, 0x0c98b0da } }, +	{ AR5K_PHY_SIG, +		{ 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e } }, +	{ AR5K_PHY_AGCCOARSE, +		{ 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e } }, +	{ AR5K_PHY(27), +		{ 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081 } }, +	{ AR5K_PHY_RX_DELAY, +		{ 0x000007d0, 0x000007d0, 0x0000044c, 0x00000898, 0x000007d0 } }, +	{ AR5K_PHY_FRAME_CTL_5211, +		{ 0xf7b81000, 0xf7b81000, 0xf7b80d00, 0xf7b81000, 0xf7b81000 } }, +	{ AR5K_PHY_CCKTXCTL, +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ AR5K_PHY(642), +		{ 0xd6be6788, 0xd6be6788, 0xd03e6788, 0xd03e6788, 0xd03e6788 } }, +	{ AR5K_PHY_GAIN_2GHZ, +		{ 0x002ec1e0, 0x002ec1e0, 0x002ac120, 0x002ac120, 0x002ac120 } }, +	{ 0xa21c, +		{ 0x1883800a, 0x1883800a, 0x1863800a, 0x1883800a, 0x1883800a } }, +	{ 0xa300, +		{ 0x18010000, 0x18010000, 0x18010000, 0x18010000, 0x18010000 } }, +	{ 0xa304, +		{ 0x30032602, 0x30032602, 0x30032602, 0x30032602, 0x30032602 } }, +	{ 0xa308, +		{ 0x48073e06, 0x48073e06, 0x48073e06, 0x48073e06, 0x48073e06 } }, +	{ 0xa30c, +		{ 0x560b4c0a, 0x560b4c0a, 0x560b4c0a, 0x560b4c0a, 0x560b4c0a } }, +	{ 0xa310, +		{ 0x641a600f, 0x641a600f, 0x641a600f, 0x641a600f, 0x641a600f } }, +	{ 0xa314, +		{ 0x784f6e1b, 0x784f6e1b, 0x784f6e1b, 0x784f6e1b, 0x784f6e1b } }, +	{ 0xa318, +		{ 0x868f7c5a, 0x868f7c5a, 0x868f7c5a, 0x868f7c5a, 0x868f7c5a } }, +	{ 0xa31c, +		{ 0x90cf865b, 0x90cf865b, 0x8ecf865b, 0x8ecf865b, 0x8ecf865b } }, +	{ 0xa320, +		{ 0x9d4f970f, 0x9d4f970f, 0x9b4f970f, 0x9b4f970f, 0x9b4f970f } }, +	{ 0xa324, +		{ 0xa7cfa38f, 0xa7cfa38f, 0xa3cf9f8f, 0xa3cf9f8f, 0xa3cf9f8f } }, +	{ 0xa328, +		{ 0xb55faf1f, 0xb55faf1f, 0xb35faf1f, 0xb35faf1f, 0xb35faf1f } }, +	{ 0xa32c, +		{ 0xbddfb99f, 0xbddfb99f, 0xbbdfb99f, 0xbbdfb99f, 0xbbdfb99f } }, +	{ 0xa330, +		{ 0xcb7fc53f, 0xcb7fc53f, 0xcb7fc73f, 0xcb7fc73f, 0xcb7fc73f } }, +	{ 0xa334, +		{ 0xd5ffd1bf, 0xd5ffd1bf, 0xd3ffd1bf, 0xd3ffd1bf, 0xd3ffd1bf } }, +	{ AR5K_DCU_FP, +		{ 0x000003e0, 0x000003e0, 0x000003e0, 0x000003e0, 0x000003e0 } }, +	{ 0x4068, +		{ 0x00000010, 0x00000010, 0x00000010, 0x00000010, 0x00000010 } }, +	{ 0x8060, +		{ 0x0000000f, 0x0000000f, 0x0000000f, 0x0000000f, 0x0000000f } }, +	{ 0x809c, +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0x80a0, +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0x8118, +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0x811c, +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0x8120, +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0x8124, +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0x8128, +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0x812c, +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0x8130, +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0x8134, +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0x8138, +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0x813c, +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0x8140, +		{ 0x800003f9, 0x800003f9, 0x800003f9, 0x800003f9, 0x800003f9 } }, +	{ 0x8144, +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ AR5K_PHY_AGC, +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ AR5K_PHY(11), +		{ 0x0000a000, 0x0000a000, 0x0000a000, 0x0000a000, 0x0000a000 } }, +	{ AR5K_PHY(15), +		{ 0x00200400, 0x00200400, 0x00200400, 0x00200400, 0x00200400 } }, +	{ AR5K_PHY(19), +		{ 0x1284233c, 0x1284233c, 0x1284233c, 0x1284233c, 0x1284233c } }, +	{ AR5K_PHY_SCR, +		{ 0x0000001f, 0x0000001f, 0x0000001f, 0x0000001f, 0x0000001f } }, +	{ AR5K_PHY_SLMT, +		{ 0x00000080, 0x00000080, 0x00000080, 0x00000080, 0x00000080 } }, +	{ AR5K_PHY_SCAL, +		{ 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e } }, +	{ AR5K_PHY(86), +		{ 0x00081fff, 0x00081fff, 0x00081fff, 0x00081fff, 0x00081fff } }, +	{ AR5K_PHY(96), +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ AR5K_PHY(97), +		{ 0x02800000, 0x02800000, 0x02800000, 0x02800000, 0x02800000 } }, +	{ AR5K_PHY(104), +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ AR5K_PHY(120), +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ AR5K_PHY(121), +		{ 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa } }, +	{ AR5K_PHY(122), +		{ 0x3c466478, 0x3c466478, 0x3c466478, 0x3c466478, 0x3c466478 } }, +	{ AR5K_PHY(123), +		{ 0x000000aa, 0x000000aa, 0x000000aa, 0x000000aa, 0x000000aa } }, +	{ AR5K_PHY_SCLOCK, +		{ 0x0000000c, 0x0000000c, 0x0000000c, 0x0000000c, 0x0000000c } }, +	{ AR5K_PHY_SDELAY, +		{ 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff } }, +	{ AR5K_PHY_SPENDING, +		{ 0x00000014, 0x00000014, 0x00000014, 0x00000014, 0x00000014 } }, +	{ 0xa228, +		{ 0x000009b5, 0x000009b5, 0x000009b5, 0x000009b5, 0x000009b5 } }, +	{ 0xa23c, +		{ 0x93c889af, 0x93c889af, 0x93c889af, 0x93c889af, 0x93c889af } }, +	{ 0xa24c, +		{ 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001 } }, +	{ 0xa250, +		{ 0x0000a000, 0x0000a000, 0x0000a000, 0x0000a000, 0x0000a000 } }, +	{ 0xa254, +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0xa258, +		{ 0x0cc75380, 0x0cc75380, 0x0cc75380, 0x0cc75380, 0x0cc75380 } }, +	{ 0xa25c, +		{ 0x0f0f0f01, 0x0f0f0f01, 0x0f0f0f01, 0x0f0f0f01, 0x0f0f0f01 } }, +	{ 0xa260, +		{ 0x5f690f01, 0x5f690f01, 0x5f690f01, 0x5f690f01, 0x5f690f01 } }, +	{ 0xa264, +		{ 0x00418a11, 0x00418a11, 0x00418a11, 0x00418a11, 0x00418a11 } }, +	{ 0xa268, +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0xa26c, +		{ 0x0c30c16a, 0x0c30c16a, 0x0c30c16a, 0x0c30c16a, 0x0c30c16a } }, +	{ 0xa270, +		{ 0x00820820, 0x00820820, 0x00820820, 0x00820820, 0x00820820 } }, +	{ 0xa274, +		{ 0x081b7caa, 0x081b7caa, 0x081b7caa, 0x081b7caa, 0x081b7caa } }, +	{ 0xa278, +		{ 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce } }, +	{ 0xa27c, +		{ 0x051701ce, 0x051701ce, 0x051701ce, 0x051701ce, 0x051701ce } }, +	{ 0xa338, +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0xa33c, +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0xa340, +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0xa344, +		{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0xa348, +		{ 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff } }, +	{ 0xa34c, +		{ 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff } }, +	{ 0xa350, +		{ 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff } }, +	{ 0xa354, +		{ 0x0003ffff, 0x0003ffff, 0x0003ffff, 0x0003ffff, 0x0003ffff } }, +	{ 0xa358, +		{ 0x79a8aa1f, 0x79a8aa1f, 0x79a8aa1f, 0x79a8aa1f, 0x79a8aa1f } }, +	{ 0xa35c, +		{ 0x066c420f, 0x066c420f, 0x066c420f, 0x066c420f, 0x066c420f } }, +	{ 0xa360, +		{ 0x0f282207, 0x0f282207, 0x0f282207, 0x0f282207, 0x0f282207 } }, +	{ 0xa364, +		{ 0x17601685, 0x17601685, 0x17601685, 0x17601685, 0x17601685 } }, +	{ 0xa368, +		{ 0x1f801104, 0x1f801104, 0x1f801104, 0x1f801104, 0x1f801104 } }, +	{ 0xa36c, +		{ 0x37a00c03, 0x37a00c03, 0x37a00c03, 0x37a00c03, 0x37a00c03 } }, +	{ 0xa370, +		{ 0x3fc40883, 0x3fc40883, 0x3fc40883, 0x3fc40883, 0x3fc40883 } }, +	{ 0xa374, +		{ 0x57c00803, 0x57c00803, 0x57c00803, 0x57c00803, 0x57c00803 } }, +	{ 0xa378, +		{ 0x5fd80682, 0x5fd80682, 0x5fd80682, 0x5fd80682, 0x5fd80682 } }, +	{ 0xa37c, +		{ 0x7fe00482, 0x7fe00482, 0x7fe00482, 0x7fe00482, 0x7fe00482 } }, +	{ 0xa380, +		{ 0x7f3c7bba, 0x7f3c7bba, 0x7f3c7bba, 0x7f3c7bba, 0x7f3c7bba } }, +	{ 0xa384, +		{ 0xf3307ff0, 0xf3307ff0, 0xf3307ff0, 0xf3307ff0, 0xf3307ff0 } }, +}; + +/* + * Initial BaseBand Gain settings for RF5111/5112 (AR5210 comes with + * RF5110 only so initial BB Gain settings are included in AR5K_AR5210_INI) + */ + +/* RF5111 Initial BaseBand Gain settings */ +static const struct ath5k_ini rf5111_ini_bbgain[] = { +	{ AR5K_BB_GAIN(0), 0x00000000 }, +	{ AR5K_BB_GAIN(1), 0x00000020 }, +	{ AR5K_BB_GAIN(2), 0x00000010 }, +	{ AR5K_BB_GAIN(3), 0x00000030 }, +	{ AR5K_BB_GAIN(4), 0x00000008 }, +	{ AR5K_BB_GAIN(5), 0x00000028 }, +	{ AR5K_BB_GAIN(6), 0x00000004 }, +	{ AR5K_BB_GAIN(7), 0x00000024 }, +	{ AR5K_BB_GAIN(8), 0x00000014 }, +	{ AR5K_BB_GAIN(9), 0x00000034 }, +	{ AR5K_BB_GAIN(10), 0x0000000c }, +	{ AR5K_BB_GAIN(11), 0x0000002c }, +	{ AR5K_BB_GAIN(12), 0x00000002 }, +	{ AR5K_BB_GAIN(13), 0x00000022 }, +	{ AR5K_BB_GAIN(14), 0x00000012 }, +	{ AR5K_BB_GAIN(15), 0x00000032 }, +	{ AR5K_BB_GAIN(16), 0x0000000a }, +	{ AR5K_BB_GAIN(17), 0x0000002a }, +	{ AR5K_BB_GAIN(18), 0x00000006 }, +	{ AR5K_BB_GAIN(19), 0x00000026 }, +	{ AR5K_BB_GAIN(20), 0x00000016 }, +	{ AR5K_BB_GAIN(21), 0x00000036 }, +	{ AR5K_BB_GAIN(22), 0x0000000e }, +	{ AR5K_BB_GAIN(23), 0x0000002e }, +	{ AR5K_BB_GAIN(24), 0x00000001 }, +	{ AR5K_BB_GAIN(25), 0x00000021 }, +	{ AR5K_BB_GAIN(26), 0x00000011 }, +	{ AR5K_BB_GAIN(27), 0x00000031 }, +	{ AR5K_BB_GAIN(28), 0x00000009 }, +	{ AR5K_BB_GAIN(29), 0x00000029 }, +	{ AR5K_BB_GAIN(30), 0x00000005 }, +	{ AR5K_BB_GAIN(31), 0x00000025 }, +	{ AR5K_BB_GAIN(32), 0x00000015 }, +	{ AR5K_BB_GAIN(33), 0x00000035 }, +	{ AR5K_BB_GAIN(34), 0x0000000d }, +	{ AR5K_BB_GAIN(35), 0x0000002d }, +	{ AR5K_BB_GAIN(36), 0x00000003 }, +	{ AR5K_BB_GAIN(37), 0x00000023 }, +	{ AR5K_BB_GAIN(38), 0x00000013 }, +	{ AR5K_BB_GAIN(39), 0x00000033 }, +	{ AR5K_BB_GAIN(40), 0x0000000b }, +	{ AR5K_BB_GAIN(41), 0x0000002b }, +	{ AR5K_BB_GAIN(42), 0x0000002b }, +	{ AR5K_BB_GAIN(43), 0x0000002b }, +	{ AR5K_BB_GAIN(44), 0x0000002b }, +	{ AR5K_BB_GAIN(45), 0x0000002b }, +	{ AR5K_BB_GAIN(46), 0x0000002b }, +	{ AR5K_BB_GAIN(47), 0x0000002b }, +	{ AR5K_BB_GAIN(48), 0x0000002b }, +	{ AR5K_BB_GAIN(49), 0x0000002b }, +	{ AR5K_BB_GAIN(50), 0x0000002b }, +	{ AR5K_BB_GAIN(51), 0x0000002b }, +	{ AR5K_BB_GAIN(52), 0x0000002b }, +	{ AR5K_BB_GAIN(53), 0x0000002b }, +	{ AR5K_BB_GAIN(54), 0x0000002b }, +	{ AR5K_BB_GAIN(55), 0x0000002b }, +	{ AR5K_BB_GAIN(56), 0x0000002b }, +	{ AR5K_BB_GAIN(57), 0x0000002b }, +	{ AR5K_BB_GAIN(58), 0x0000002b }, +	{ AR5K_BB_GAIN(59), 0x0000002b }, +	{ AR5K_BB_GAIN(60), 0x0000002b }, +	{ AR5K_BB_GAIN(61), 0x0000002b }, +	{ AR5K_BB_GAIN(62), 0x00000002 }, +	{ AR5K_BB_GAIN(63), 0x00000016 }, +}; + +/* RF5112 Initial BaseBand Gain settings (Same for RF5413/5414) */ +static const struct ath5k_ini rf5112_ini_bbgain[] = { +	{ AR5K_BB_GAIN(0), 0x00000000 }, +	{ AR5K_BB_GAIN(1), 0x00000001 }, +	{ AR5K_BB_GAIN(2), 0x00000002 }, +	{ AR5K_BB_GAIN(3), 0x00000003 }, +	{ AR5K_BB_GAIN(4), 0x00000004 }, +	{ AR5K_BB_GAIN(5), 0x00000005 }, +	{ AR5K_BB_GAIN(6), 0x00000008 }, +	{ AR5K_BB_GAIN(7), 0x00000009 }, +	{ AR5K_BB_GAIN(8), 0x0000000a }, +	{ AR5K_BB_GAIN(9), 0x0000000b }, +	{ AR5K_BB_GAIN(10), 0x0000000c }, +	{ AR5K_BB_GAIN(11), 0x0000000d }, +	{ AR5K_BB_GAIN(12), 0x00000010 }, +	{ AR5K_BB_GAIN(13), 0x00000011 }, +	{ AR5K_BB_GAIN(14), 0x00000012 }, +	{ AR5K_BB_GAIN(15), 0x00000013 }, +	{ AR5K_BB_GAIN(16), 0x00000014 }, +	{ AR5K_BB_GAIN(17), 0x00000015 }, +	{ AR5K_BB_GAIN(18), 0x00000018 }, +	{ AR5K_BB_GAIN(19), 0x00000019 }, +	{ AR5K_BB_GAIN(20), 0x0000001a }, +	{ AR5K_BB_GAIN(21), 0x0000001b }, +	{ AR5K_BB_GAIN(22), 0x0000001c }, +	{ AR5K_BB_GAIN(23), 0x0000001d }, +	{ AR5K_BB_GAIN(24), 0x00000020 }, +	{ AR5K_BB_GAIN(25), 0x00000021 }, +	{ AR5K_BB_GAIN(26), 0x00000022 }, +	{ AR5K_BB_GAIN(27), 0x00000023 }, +	{ AR5K_BB_GAIN(28), 0x00000024 }, +	{ AR5K_BB_GAIN(29), 0x00000025 }, +	{ AR5K_BB_GAIN(30), 0x00000028 }, +	{ AR5K_BB_GAIN(31), 0x00000029 }, +	{ AR5K_BB_GAIN(32), 0x0000002a }, +	{ AR5K_BB_GAIN(33), 0x0000002b }, +	{ AR5K_BB_GAIN(34), 0x0000002c }, +	{ AR5K_BB_GAIN(35), 0x0000002d }, +	{ AR5K_BB_GAIN(36), 0x00000030 }, +	{ AR5K_BB_GAIN(37), 0x00000031 }, +	{ AR5K_BB_GAIN(38), 0x00000032 }, +	{ AR5K_BB_GAIN(39), 0x00000033 }, +	{ AR5K_BB_GAIN(40), 0x00000034 }, +	{ AR5K_BB_GAIN(41), 0x00000035 }, +	{ AR5K_BB_GAIN(42), 0x00000035 }, +	{ AR5K_BB_GAIN(43), 0x00000035 }, +	{ AR5K_BB_GAIN(44), 0x00000035 }, +	{ AR5K_BB_GAIN(45), 0x00000035 }, +	{ AR5K_BB_GAIN(46), 0x00000035 }, +	{ AR5K_BB_GAIN(47), 0x00000035 }, +	{ AR5K_BB_GAIN(48), 0x00000035 }, +	{ AR5K_BB_GAIN(49), 0x00000035 }, +	{ AR5K_BB_GAIN(50), 0x00000035 }, +	{ AR5K_BB_GAIN(51), 0x00000035 }, +	{ AR5K_BB_GAIN(52), 0x00000035 }, +	{ AR5K_BB_GAIN(53), 0x00000035 }, +	{ AR5K_BB_GAIN(54), 0x00000035 }, +	{ AR5K_BB_GAIN(55), 0x00000035 }, +	{ AR5K_BB_GAIN(56), 0x00000035 }, +	{ AR5K_BB_GAIN(57), 0x00000035 }, +	{ AR5K_BB_GAIN(58), 0x00000035 }, +	{ AR5K_BB_GAIN(59), 0x00000035 }, +	{ AR5K_BB_GAIN(60), 0x00000035 }, +	{ AR5K_BB_GAIN(61), 0x00000035 }, +	{ AR5K_BB_GAIN(62), 0x00000010 }, +	{ AR5K_BB_GAIN(63), 0x0000001a }, +}; + + +/* + * Write initial register dump + */ +static void ath5k_hw_ini_registers(struct ath5k_hw *ah, unsigned int size, +		const struct ath5k_ini *ini_regs, bool change_channel) +{ +	unsigned int i; + +	/* Write initial registers */ +	for (i = 0; i < size; i++) { +		/* On channel change there is +		 * no need to mess with PCU */ +		if (change_channel && +				ini_regs[i].ini_register >= AR5K_PCU_MIN && +				ini_regs[i].ini_register <= AR5K_PCU_MAX) +			continue; + +		switch (ini_regs[i].ini_mode) { +		case AR5K_INI_READ: +			/* Cleared on read */ +			ath5k_hw_reg_read(ah, ini_regs[i].ini_register); +			break; +		case AR5K_INI_WRITE: +		default: +			AR5K_REG_WAIT(i); +			ath5k_hw_reg_write(ah, ini_regs[i].ini_value, +					ini_regs[i].ini_register); +		} +	} +} + +static void ath5k_hw_ini_mode_registers(struct ath5k_hw *ah, +		unsigned int size, const struct ath5k_ini_mode *ini_mode, +		u8 mode) +{ +	unsigned int i; + +	for (i = 0; i < size; i++) { +		AR5K_REG_WAIT(i); +		ath5k_hw_reg_write(ah, ini_mode[i].mode_value[mode], +			(u32)ini_mode[i].mode_register); +	} + +} + +int ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool change_channel) +{ +	/* +	 * Write initial register settings +	 */ + +	/* For AR5212 and combatible */ +	if (ah->ah_version == AR5K_AR5212){ + +		/* First set of mode-specific settings */ +		ath5k_hw_ini_mode_registers(ah, +			ARRAY_SIZE(ar5212_ini_mode_start), +			ar5212_ini_mode_start, mode); + +		/* +		 * Write initial settings common for all modes +		 */ +		ath5k_hw_ini_registers(ah, ARRAY_SIZE(ar5212_ini), +					ar5212_ini, change_channel); + +		/* Second set of mode-specific settings */ +		if (ah->ah_radio == AR5K_RF5111){ +			ath5k_hw_ini_mode_registers(ah, +					ARRAY_SIZE(ar5212_rf5111_ini_mode_end), +					ar5212_rf5111_ini_mode_end, mode); +			/* Baseband gain table */ +			ath5k_hw_ini_registers(ah, +					ARRAY_SIZE(rf5111_ini_bbgain), +					rf5111_ini_bbgain, change_channel); +		} else if (ah->ah_radio == AR5K_RF5112){ +			ath5k_hw_ini_mode_registers(ah, +					ARRAY_SIZE(ar5212_rf5112_ini_mode_end), +					ar5212_rf5112_ini_mode_end, mode); +			/* Baseband gain table */ +			ath5k_hw_ini_registers(ah, +					ARRAY_SIZE(rf5112_ini_bbgain), +					rf5112_ini_bbgain, change_channel); +		} else if (ah->ah_radio == AR5K_RF5413){ +			ath5k_hw_ini_mode_registers(ah, +					ARRAY_SIZE(rf5413_ini_mode_end), +					rf5413_ini_mode_end, mode); +			/* Baseband gain table */ +			ath5k_hw_ini_registers(ah, +					ARRAY_SIZE(rf5112_ini_bbgain), +					rf5112_ini_bbgain, change_channel); +		} +	/* For AR5211 */ +	} else if (ah->ah_version == AR5K_AR5211) { + +		if(mode > 2){ /* AR5K_INI_VAL_11B */ +			ATH5K_ERR(ah->ah_sc,"unsupported channel mode: %d\n", mode); +			return -EINVAL; +		} + +		/* Mode-specific settings */ +		ath5k_hw_ini_mode_registers(ah, ARRAY_SIZE(ar5211_ini_mode), +				ar5211_ini_mode, mode); + +		/* +		 * Write initial settings common for all modes +		 */ +		ath5k_hw_ini_registers(ah, ARRAY_SIZE(ar5211_ini), +				ar5211_ini, change_channel); + +		/* AR5211 only comes with 5111 */ + +		/* Baseband gain table */ +		ath5k_hw_ini_registers(ah, ARRAY_SIZE(rf5111_ini_bbgain), +				rf5111_ini_bbgain, change_channel); +	/* For AR5210 (for mode settings check out ath5k_hw_reset_tx_queue) */ +	} else if (ah->ah_version == AR5K_AR5210) { +		ath5k_hw_ini_registers(ah, ARRAY_SIZE(ar5210_ini), +				ar5210_ini, change_channel); +	} + +	return 0; +} diff --git a/drivers/net/wireless/ath5k/phy.c b/drivers/net/wireless/ath5k/phy.c new file mode 100644 index 000000000000..b95941797141 --- /dev/null +++ b/drivers/net/wireless/ath5k/phy.c @@ -0,0 +1,2071 @@ +/* + * PHY functions + * + * Copyright (c) 2004, 2005, 2006, 2007 Reyk Floeter <reyk@openbsd.org> + * Copyright (c) 2006, 2007 Nick Kossifidis <mickflemm@gmail.com> + * Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include <linux/delay.h> + +#include "ath5k.h" +#include "reg.h" +#include "base.h" + +/* Struct to hold initial RF register values (RF Banks) */ +struct ath5k_ini_rf { +	u8	rf_bank;	/* check out ath5k_reg.h */ +	u16	rf_register;	/* register address */ +	u32	rf_value[5];	/* register value for different modes (above) */ +}; + +/* + * 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)] */ +}; + +struct ath5k_gain_opt { +	u32			go_default; +	u32			go_steps_count; +	const struct ath5k_gain_opt_step	go_step[AR5K_GAIN_STEP_COUNT]; +}; + +/* RF5111 mode-specific init registers */ +static const struct ath5k_ini_rf rfregs_5111[] = { +	{ 0, 0x989c, +	/*    mode a/XR   mode aTurbo mode b      mode g      mode gTurbo */ +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0, 0x989c, +	    { 0x00380000, 0x00380000, 0x00380000, 0x00380000, 0x00380000 } }, +	{ 0, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 0, 0x989c, +	    { 0x00000000, 0x00000000, 0x000000c0, 0x00000080, 0x00000080 } }, +	{ 0, 0x989c, +	    { 0x000400f9, 0x000400f9, 0x000400ff, 0x000400fd, 0x000400fd } }, +	{ 0, 0x98d4, +	    { 0x00000000, 0x00000000, 0x00000004, 0x00000004, 0x00000004 } }, +	{ 1, 0x98d4, +	    { 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020 } }, +	{ 2, 0x98d4, +	    { 0x00000010, 0x00000014, 0x00000010, 0x00000010, 0x00000014 } }, +	{ 3, 0x98d8, +	    { 0x00601068, 0x00601068, 0x00601068, 0x00601068, 0x00601068 } }, +	{ 6, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 6, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 6, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 6, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 6, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 6, 0x989c, +	    { 0x10000000, 0x10000000, 0x10000000, 0x10000000, 0x10000000 } }, +	{ 6, 0x989c, +	    { 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000 } }, +	{ 6, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 6, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 6, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 6, 0x989c, +	    { 0x00000000, 0x00000000, 0x0a000000, 0x00000000, 0x00000000 } }, +	{ 6, 0x989c, +	    { 0x003800c0, 0x00380080, 0x023800c0, 0x003800c0, 0x003800c0 } }, +	{ 6, 0x989c, +	    { 0x00020006, 0x00020006, 0x00000006, 0x00020006, 0x00020006 } }, +	{ 6, 0x989c, +	    { 0x00000089, 0x00000089, 0x00000089, 0x00000089, 0x00000089 } }, +	{ 6, 0x989c, +	    { 0x000000a0, 0x000000a0, 0x000000a0, 0x000000a0, 0x000000a0 } }, +	{ 6, 0x989c, +	    { 0x00040007, 0x00040007, 0x00040007, 0x00040007, 0x00040007 } }, +	{ 6, 0x98d4, +	    { 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a } }, +	{ 7, 0x989c, +	    { 0x00000040, 0x00000048, 0x00000040, 0x00000040, 0x00000040 } }, +	{ 7, 0x989c, +	    { 0x00000010, 0x00000010, 0x00000010, 0x00000010, 0x00000010 } }, +	{ 7, 0x989c, +	    { 0x00000008, 0x00000008, 0x00000008, 0x00000008, 0x00000008 } }, +	{ 7, 0x989c, +	    { 0x0000004f, 0x0000004f, 0x0000004f, 0x0000004f, 0x0000004f } }, +	{ 7, 0x989c, +	    { 0x000000f1, 0x000000f1, 0x00000061, 0x000000f1, 0x000000f1 } }, +	{ 7, 0x989c, +	    { 0x0000904f, 0x0000904f, 0x0000904c, 0x0000904f, 0x0000904f } }, +	{ 7, 0x989c, +	    { 0x0000125a, 0x0000125a, 0x0000129a, 0x0000125a, 0x0000125a } }, +	{ 7, 0x98cc, +	    { 0x0000000e, 0x0000000e, 0x0000000f, 0x0000000e, 0x0000000e } }, +}; + +/* 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 } }, +}; + +static const struct ath5k_gain_opt rfgain_opt_5111 = { +	4, +	9, +	{ +		{ { 4, 1, 1, 1 }, 6 }, +		{ { 4, 0, 1, 1 }, 4 }, +		{ { 3, 1, 1, 1 }, 3 }, +		{ { 4, 0, 0, 1 }, 1 }, +		{ { 4, 1, 1, 0 }, 0 }, +		{ { 4, 0, 1, 0 }, -2 }, +		{ { 3, 1, 1, 0 }, -3 }, +		{ { 4, 0, 0, 0 }, -4 }, +		{ { 2, 1, 1, 0 }, -6 } +	} +}; + +/* RF5112 mode-specific init registers */ +static const struct ath5k_ini_rf rfregs_5112[] = { +	{ 1, 0x98d4, +	/*    mode a/XR   mode aTurbo mode b      mode g      mode gTurbo */ +	    { 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020 } }, +	{ 2, 0x98d0, +	    { 0x03060408, 0x03070408, 0x03060408, 0x03060408, 0x03070408 } }, +	{ 3, 0x98dc, +	    { 0x00a0c0c0, 0x00a0c0c0, 0x00e0c0c0, 0x00e0c0c0, 0x00e0c0c0 } }, +	{ 6, 0x989c, +	    { 0x00a00000, 0x00a00000, 0x00a00000, 0x00a00000, 0x00a00000 } }, +	{ 6, 0x989c, +	    { 0x000a0000, 0x000a0000, 0x000a0000, 0x000a0000, 0x000a0000 } }, +	{ 6, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 6, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 6, 0x989c, +	    { 0x00660000, 0x00660000, 0x00660000, 0x00660000, 0x00660000 } }, +	{ 6, 0x989c, +	    { 0x00db0000, 0x00db0000, 0x00db0000, 0x00db0000, 0x00db0000 } }, +	{ 6, 0x989c, +	    { 0x00f10000, 0x00f10000, 0x00f10000, 0x00f10000, 0x00f10000 } }, +	{ 6, 0x989c, +	    { 0x00120000, 0x00120000, 0x00120000, 0x00120000, 0x00120000 } }, +	{ 6, 0x989c, +	    { 0x00120000, 0x00120000, 0x00120000, 0x00120000, 0x00120000 } }, +	{ 6, 0x989c, +	    { 0x00730000, 0x00730000, 0x00730000, 0x00730000, 0x00730000 } }, +	{ 6, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 6, 0x989c, +	    { 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000 } }, +	{ 6, 0x989c, +	    { 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000 } }, +	{ 6, 0x989c, +	    { 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000 } }, +	{ 6, 0x989c, +	    { 0x008b0000, 0x008b0000, 0x008b0000, 0x008b0000, 0x008b0000 } }, +	{ 6, 0x989c, +	    { 0x00600000, 0x00600000, 0x00600000, 0x00600000, 0x00600000 } }, +	{ 6, 0x989c, +	    { 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000 } }, +	{ 6, 0x989c, +	    { 0x00840000, 0x00840000, 0x00840000, 0x00840000, 0x00840000 } }, +	{ 6, 0x989c, +	    { 0x00640000, 0x00640000, 0x00640000, 0x00640000, 0x00640000 } }, +	{ 6, 0x989c, +	    { 0x00200000, 0x00200000, 0x00200000, 0x00200000, 0x00200000 } }, +	{ 6, 0x989c, +	    { 0x00240000, 0x00240000, 0x00240000, 0x00240000, 0x00240000 } }, +	{ 6, 0x989c, +	    { 0x00250000, 0x00250000, 0x00250000, 0x00250000, 0x00250000 } }, +	{ 6, 0x989c, +	    { 0x00110000, 0x00110000, 0x00110000, 0x00110000, 0x00110000 } }, +	{ 6, 0x989c, +	    { 0x00110000, 0x00110000, 0x00110000, 0x00110000, 0x00110000 } }, +	{ 6, 0x989c, +	    { 0x00510000, 0x00510000, 0x00510000, 0x00510000, 0x00510000 } }, +	{ 6, 0x989c, +	    { 0x1c040000, 0x1c040000, 0x1c040000, 0x1c040000, 0x1c040000 } }, +	{ 6, 0x989c, +	    { 0x000a0000, 0x000a0000, 0x000a0000, 0x000a0000, 0x000a0000 } }, +	{ 6, 0x989c, +	    { 0x00a10000, 0x00a10000, 0x00a10000, 0x00a10000, 0x00a10000 } }, +	{ 6, 0x989c, +	    { 0x00400000, 0x00400000, 0x00400000, 0x00400000, 0x00400000 } }, +	{ 6, 0x989c, +	    { 0x03090000, 0x03090000, 0x03090000, 0x03090000, 0x03090000 } }, +	{ 6, 0x989c, +	    { 0x06000000, 0x06000000, 0x06000000, 0x06000000, 0x06000000 } }, +	{ 6, 0x989c, +	    { 0x000000b0, 0x000000b0, 0x000000a8, 0x000000a8, 0x000000a8 } }, +	{ 6, 0x989c, +	    { 0x0000002e, 0x0000002e, 0x0000002e, 0x0000002e, 0x0000002e } }, +	{ 6, 0x989c, +	    { 0x006c4a41, 0x006c4a41, 0x006c4af1, 0x006c4a61, 0x006c4a61 } }, +	{ 6, 0x989c, +	    { 0x0050892a, 0x0050892a, 0x0050892b, 0x0050892b, 0x0050892b } }, +	{ 6, 0x989c, +	    { 0x00842400, 0x00842400, 0x00842400, 0x00842400, 0x00842400 } }, +	{ 6, 0x989c, +	    { 0x00c69200, 0x00c69200, 0x00c69200, 0x00c69200, 0x00c69200 } }, +	{ 6, 0x98d0, +	    { 0x0002000c, 0x0002000c, 0x0002000c, 0x0002000c, 0x0002000c } }, +	{ 7, 0x989c, +	    { 0x00000094, 0x00000094, 0x00000094, 0x00000094, 0x00000094 } }, +	{ 7, 0x989c, +	    { 0x00000091, 0x00000091, 0x00000091, 0x00000091, 0x00000091 } }, +	{ 7, 0x989c, +	    { 0x0000000a, 0x0000000a, 0x00000012, 0x00000012, 0x00000012 } }, +	{ 7, 0x989c, +	    { 0x00000080, 0x00000080, 0x00000080, 0x00000080, 0x00000080 } }, +	{ 7, 0x989c, +	    { 0x000000c1, 0x000000c1, 0x000000c1, 0x000000c1, 0x000000c1 } }, +	{ 7, 0x989c, +	    { 0x00000060, 0x00000060, 0x00000060, 0x00000060, 0x00000060 } }, +	{ 7, 0x989c, +	    { 0x000000f0, 0x000000f0, 0x000000f0, 0x000000f0, 0x000000f0 } }, +	{ 7, 0x989c, +	    { 0x00000022, 0x00000022, 0x00000022, 0x00000022, 0x00000022 } }, +	{ 7, 0x989c, +	    { 0x00000092, 0x00000092, 0x00000092, 0x00000092, 0x00000092 } }, +	{ 7, 0x989c, +	    { 0x000000d4, 0x000000d4, 0x000000d4, 0x000000d4, 0x000000d4 } }, +	{ 7, 0x989c, +	    { 0x000014cc, 0x000014cc, 0x000014cc, 0x000014cc, 0x000014cc } }, +	{ 7, 0x989c, +	    { 0x0000048c, 0x0000048c, 0x0000048c, 0x0000048c, 0x0000048c } }, +	{ 7, 0x98c4, +	    { 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000003 } }, +}; + +/* RF5112A mode-specific init registers */ +static const struct ath5k_ini_rf rfregs_5112a[] = { +	{ 1, 0x98d4, +	/*    mode a/XR   mode aTurbo mode b      mode g      mode gTurbo */ +	    { 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020 } }, +	{ 2, 0x98d0, +	    { 0x03060408, 0x03070408, 0x03060408, 0x03060408, 0x03070408 } }, +	{ 3, 0x98dc, +	    { 0x00a0c0c0, 0x00a0c0c0, 0x00e0c0c0, 0x00e0c0c0, 0x00e0c0c0 } }, +	{ 6, 0x989c, +	    { 0x0f000000, 0x0f000000, 0x0f000000, 0x0f000000, 0x0f000000 } }, +	{ 6, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 6, 0x989c, +	    { 0x00800000, 0x00800000, 0x00800000, 0x00800000, 0x00800000 } }, +	{ 6, 0x989c, +	    { 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000 } }, +	{ 6, 0x989c, +	    { 0x00010000, 0x00010000, 0x00010000, 0x00010000, 0x00010000 } }, +	{ 6, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 6, 0x989c, +	    { 0x00180000, 0x00180000, 0x00180000, 0x00180000, 0x00180000 } }, +	{ 6, 0x989c, +	    { 0x00600000, 0x00600000, 0x006e0000, 0x006e0000, 0x006e0000 } }, +	{ 6, 0x989c, +	    { 0x00c70000, 0x00c70000, 0x00c70000, 0x00c70000, 0x00c70000 } }, +	{ 6, 0x989c, +	    { 0x004b0000, 0x004b0000, 0x004b0000, 0x004b0000, 0x004b0000 } }, +	{ 6, 0x989c, +	    { 0x04480000, 0x04480000, 0x04480000, 0x04480000, 0x04480000 } }, +	{ 6, 0x989c, +	    { 0x00220000, 0x00220000, 0x00220000, 0x00220000, 0x00220000 } }, +	{ 6, 0x989c, +	    { 0x00e40000, 0x00e40000, 0x00e40000, 0x00e40000, 0x00e40000 } }, +	{ 6, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 6, 0x989c, +	    { 0x00fc0000, 0x00fc0000, 0x00fc0000, 0x00fc0000, 0x00fc0000 } }, +	{ 6, 0x989c, +	    { 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000 } }, +	{ 6, 0x989c, +	    { 0x043f0000, 0x043f0000, 0x043f0000, 0x043f0000, 0x043f0000 } }, +	{ 6, 0x989c, +	    { 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000 } }, +	{ 6, 0x989c, +	    { 0x00190000, 0x00190000, 0x00190000, 0x00190000, 0x00190000 } }, +	{ 6, 0x989c, +	    { 0x00240000, 0x00240000, 0x00240000, 0x00240000, 0x00240000 } }, +	{ 6, 0x989c, +	    { 0x00b40000, 0x00b40000, 0x00b40000, 0x00b40000, 0x00b40000 } }, +	{ 6, 0x989c, +	    { 0x00990000, 0x00990000, 0x00990000, 0x00990000, 0x00990000 } }, +	{ 6, 0x989c, +	    { 0x00500000, 0x00500000, 0x00500000, 0x00500000, 0x00500000 } }, +	{ 6, 0x989c, +	    { 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000 } }, +	{ 6, 0x989c, +	    { 0x00120000, 0x00120000, 0x00120000, 0x00120000, 0x00120000 } }, +	{ 6, 0x989c, +	    { 0xc0320000, 0xc0320000, 0xc0320000, 0xc0320000, 0xc0320000 } }, +	{ 6, 0x989c, +	    { 0x01740000, 0x01740000, 0x01740000, 0x01740000, 0x01740000 } }, +	{ 6, 0x989c, +	    { 0x00110000, 0x00110000, 0x00110000, 0x00110000, 0x00110000 } }, +	{ 6, 0x989c, +	    { 0x86280000, 0x86280000, 0x86280000, 0x86280000, 0x86280000 } }, +	{ 6, 0x989c, +	    { 0x31840000, 0x31840000, 0x31840000, 0x31840000, 0x31840000 } }, +	{ 6, 0x989c, +	    { 0x00020080, 0x00020080, 0x00020080, 0x00020080, 0x00020080 } }, +	{ 6, 0x989c, +	    { 0x00080009, 0x00080009, 0x00080009, 0x00080009, 0x00080009 } }, +	{ 6, 0x989c, +	    { 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000003 } }, +	{ 6, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 6, 0x989c, +	    { 0x000000b2, 0x000000b2, 0x000000b2, 0x000000b2, 0x000000b2 } }, +	{ 6, 0x989c, +	    { 0x00b02084, 0x00b02084, 0x00b02084, 0x00b02084, 0x00b02084 } }, +	{ 6, 0x989c, +	    { 0x004125a4, 0x004125a4, 0x004125a4, 0x004125a4, 0x004125a4 } }, +	{ 6, 0x989c, +	    { 0x00119220, 0x00119220, 0x00119220, 0x00119220, 0x00119220 } }, +	{ 6, 0x989c, +	    { 0x001a4800, 0x001a4800, 0x001a4800, 0x001a4800, 0x001a4800 } }, +	{ 6, 0x98d8, +	    { 0x000b0230, 0x000b0230, 0x000b0230, 0x000b0230, 0x000b0230 } }, +	{ 7, 0x989c, +	    { 0x00000094, 0x00000094, 0x00000094, 0x00000094, 0x00000094 } }, +	{ 7, 0x989c, +	    { 0x00000091, 0x00000091, 0x00000091, 0x00000091, 0x00000091 } }, +	{ 7, 0x989c, +	    { 0x00000012, 0x00000012, 0x00000012, 0x00000012, 0x00000012 } }, +	{ 7, 0x989c, +	    { 0x00000080, 0x00000080, 0x00000080, 0x00000080, 0x00000080 } }, +	{ 7, 0x989c, +	    { 0x000000d9, 0x000000d9, 0x000000d9, 0x000000d9, 0x000000d9 } }, +	{ 7, 0x989c, +	    { 0x00000060, 0x00000060, 0x00000060, 0x00000060, 0x00000060 } }, +	{ 7, 0x989c, +	    { 0x000000f0, 0x000000f0, 0x000000f0, 0x000000f0, 0x000000f0 } }, +	{ 7, 0x989c, +	    { 0x000000a2, 0x000000a2, 0x000000a2, 0x000000a2, 0x000000a2 } }, +	{ 7, 0x989c, +	    { 0x00000052, 0x00000052, 0x00000052, 0x00000052, 0x00000052 } }, +	{ 7, 0x989c, +	    { 0x000000d4, 0x000000d4, 0x000000d4, 0x000000d4, 0x000000d4 } }, +	{ 7, 0x989c, +	    { 0x000014cc, 0x000014cc, 0x000014cc, 0x000014cc, 0x000014cc } }, +	{ 7, 0x989c, +	    { 0x0000048c, 0x0000048c, 0x0000048c, 0x0000048c, 0x0000048c } }, +	{ 7, 0x98c4, +	    { 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000003 } }, +}; + + +static const struct ath5k_ini_rf rfregs_2112a[] = { +	{ 1, AR5K_RF_BUFFER_CONTROL_4, +	/*	   mode b	mode g	  mode gTurbo */ +		{ 0x00000020, 0x00000020, 0x00000020 } }, +	{ 2, AR5K_RF_BUFFER_CONTROL_3, +		{ 0x03060408, 0x03060408, 0x03070408 } }, +	{ 3, AR5K_RF_BUFFER_CONTROL_6, +		{ 0x00e020c0, 0x00e020c0, 0x00e020c0 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x0a000000, 0x0a000000, 0x0a000000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x00000000, 0x00000000, 0x00000000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x00800000, 0x00800000, 0x00800000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x002a0000, 0x002a0000, 0x002a0000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x00010000, 0x00010000, 0x00010000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x00000000, 0x00000000, 0x00000000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x00180000, 0x00180000, 0x00180000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x006e0000, 0x006e0000, 0x006e0000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x00c70000, 0x00c70000, 0x00c70000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x004b0000, 0x004b0000, 0x004b0000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x04480000, 0x04480000, 0x04480000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x002a0000, 0x002a0000, 0x002a0000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x00e40000, 0x00e40000, 0x00e40000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x00000000, 0x00000000, 0x00000000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x00fc0000, 0x00fc0000, 0x00fc0000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x00ff0000, 0x00ff0000, 0x00ff0000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x043f0000, 0x043f0000, 0x043f0000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x0c0c0000, 0x0c0c0000, 0x0c0c0000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x02190000, 0x02190000, 0x02190000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x00240000, 0x00240000, 0x00240000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x00b40000, 0x00b40000, 0x00b40000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x00990000, 0x00990000, 0x00990000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x00500000, 0x00500000, 0x00500000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x002a0000, 0x002a0000, 0x002a0000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x00120000, 0x00120000, 0x00120000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0xc0320000, 0xc0320000, 0xc0320000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x01740000, 0x01740000, 0x01740000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x00110000, 0x00110000, 0x00110000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x86280000, 0x86280000, 0x86280000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x31840000, 0x31840000, 0x31840000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x00f20080, 0x00f20080, 0x00f20080 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x00070019, 0x00070019, 0x00070019 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x00000000, 0x00000000, 0x00000000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x00000000, 0x00000000, 0x00000000 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x000000b2, 0x000000b2, 0x000000b2 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x00b02184, 0x00b02184, 0x00b02184 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x004125a4, 0x004125a4, 0x004125a4 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x00119220, 0x00119220, 0x00119220 } }, +	{ 6, AR5K_RF_BUFFER, +		{ 0x001a4800, 0x001a4800, 0x001a4800 } }, +	{ 6, AR5K_RF_BUFFER_CONTROL_5, +		{ 0x000b0230, 0x000b0230, 0x000b0230 } }, +	{ 7, AR5K_RF_BUFFER, +		{ 0x00000094, 0x00000094, 0x00000094 } }, +	{ 7, AR5K_RF_BUFFER, +		{ 0x00000091, 0x00000091, 0x00000091 } }, +	{ 7, AR5K_RF_BUFFER, +		{ 0x00000012, 0x00000012, 0x00000012 } }, +	{ 7, AR5K_RF_BUFFER, +		{ 0x00000080, 0x00000080, 0x00000080 } }, +	{ 7, AR5K_RF_BUFFER, +		{ 0x000000d9, 0x000000d9, 0x000000d9 } }, +	{ 7, AR5K_RF_BUFFER, +		{ 0x00000060, 0x00000060, 0x00000060 } }, +	{ 7, AR5K_RF_BUFFER, +		{ 0x000000f0, 0x000000f0, 0x000000f0 } }, +	{ 7, AR5K_RF_BUFFER, +		{ 0x000000a2, 0x000000a2, 0x000000a2 } }, +	{ 7, AR5K_RF_BUFFER, +		{ 0x00000052, 0x00000052, 0x00000052 } }, +	{ 7, AR5K_RF_BUFFER, +		{ 0x000000d4, 0x000000d4, 0x000000d4 } }, +	{ 7, AR5K_RF_BUFFER, +		{ 0x000014cc, 0x000014cc, 0x000014cc } }, +	{ 7, AR5K_RF_BUFFER, +		{ 0x0000048c, 0x0000048c, 0x0000048c } }, +	{ 7, AR5K_RF_BUFFER_CONTROL_1, +		{ 0x00000003, 0x00000003, 0x00000003 } }, +}; + +/* RF5413/5414 mode-specific init registers */ +static const struct ath5k_ini_rf rfregs_5413[] = { +	{ 1, 0x98d4, +	/*    mode a/XR   mode aTurbo mode b      mode g      mode gTurbo */ +	    { 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020 } }, +	{ 2, 0x98d0, +	    { 0x00000008, 0x00000008, 0x00000008, 0x00000008, 0x00000008 } }, +	{ 3, 0x98dc, +	    { 0x00a000c0, 0x00a000c0, 0x00e000c0, 0x00e000c0, 0x00e000c0 } }, +	{ 6, 0x989c, +	    { 0x33000000, 0x33000000, 0x33000000, 0x33000000, 0x33000000 } }, +	{ 6, 0x989c, +	    { 0x01000000, 0x01000000, 0x01000000, 0x01000000, 0x01000000 } }, +	{ 6, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 6, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 6, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 6, 0x989c, +	    { 0x1f000000, 0x1f000000, 0x1f000000, 0x1f000000, 0x1f000000 } }, +	{ 6, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 6, 0x989c, +	    { 0x00b80000, 0x00b80000, 0x00b80000, 0x00b80000, 0x00b80000 } }, +	{ 6, 0x989c, +	    { 0x00b70000, 0x00b70000, 0x00b70000, 0x00b70000, 0x00b70000 } }, +	{ 6, 0x989c, +	    { 0x00840000, 0x00840000, 0x00840000, 0x00840000, 0x00840000 } }, +	{ 6, 0x989c, +	    { 0x00980000, 0x00980000, 0x00980000, 0x00980000, 0x00980000 } }, +	{ 6, 0x989c, +	    { 0x00c00000, 0x00c00000, 0x00c00000, 0x00c00000, 0x00c00000 } }, +	{ 6, 0x989c, +	    { 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000 } }, +	{ 6, 0x989c, +	    { 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000 } }, +	{ 6, 0x989c, +	    { 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000 } }, +	{ 6, 0x989c, +	    { 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000 } }, +	{ 6, 0x989c, +	    { 0x00d70000, 0x00d70000, 0x00d70000, 0x00d70000, 0x00d70000 } }, +	{ 6, 0x989c, +	    { 0x00610000, 0x00610000, 0x00610000, 0x00610000, 0x00610000 } }, +	{ 6, 0x989c, +	    { 0x00fe0000, 0x00fe0000, 0x00fe0000, 0x00fe0000, 0x00fe0000 } }, +	{ 6, 0x989c, +	    { 0x00de0000, 0x00de0000, 0x00de0000, 0x00de0000, 0x00de0000 } }, +	{ 6, 0x989c, +	    { 0x007f0000, 0x007f0000, 0x007f0000, 0x007f0000, 0x007f0000 } }, +	{ 6, 0x989c, +	    { 0x043d0000, 0x043d0000, 0x043d0000, 0x043d0000, 0x043d0000 } }, +	{ 6, 0x989c, +	    { 0x00770000, 0x00770000, 0x00770000, 0x00770000, 0x00770000 } }, +	{ 6, 0x989c, +	    { 0x00440000, 0x00440000, 0x00440000, 0x00440000, 0x00440000 } }, +	{ 6, 0x989c, +	    { 0x00980000, 0x00980000, 0x00980000, 0x00980000, 0x00980000 } }, +	{ 6, 0x989c, +	    { 0x00100080, 0x00100080, 0x00100080, 0x00100080, 0x00100080 } }, +	{ 6, 0x989c, +	    { 0x0005c034, 0x0005c034, 0x0005c034, 0x0005c034, 0x0005c034 } }, +	{ 6, 0x989c, +	    { 0x003100f0, 0x003100f0, 0x003100f0, 0x003100f0, 0x003100f0 } }, +	{ 6, 0x989c, +	    { 0x000c011f, 0x000c011f, 0x000c011f, 0x000c011f, 0x000c011f } }, +	{ 6, 0x989c, +	    { 0x00510040, 0x00510040, 0x005100a0, 0x005100a0, 0x005100a0 } }, +	{ 6, 0x989c, +	    { 0x0050006a, 0x0050006a, 0x005000dd, 0x005000dd, 0x005000dd } }, +	{ 6, 0x989c, +	    { 0x00000001, 0x00000001, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 6, 0x989c, +	    { 0x00004044, 0x00004044, 0x00004044, 0x00004044, 0x00004044 } }, +	{ 6, 0x989c, +	    { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, +	{ 6, 0x989c, +	    { 0x000060c0, 0x000060c0, 0x000060c0, 0x000060c0, 0x000060c0 } }, +	{ 6, 0x989c, +	    { 0x00002c00, 0x00002c00, 0x00003600, 0x00003600, 0x00003600 } }, +	{ 6, 0x98c8, +	    { 0x00000403, 0x00000403, 0x00040403, 0x00040403, 0x00040403 } }, +	{ 7, 0x989c, +	    { 0x00006400, 0x00006400, 0x00006400, 0x00006400, 0x00006400 } }, +	{ 7, 0x989c, +	    { 0x00000800, 0x00000800, 0x00000800, 0x00000800, 0x00000800 } }, +	{ 7, 0x98cc, +	    { 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e } }, +}; + + +/* 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 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 } }, +}; + +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 }, +	} +}; + +/* + * Used to modify RF Banks before writing them to AR5K_RF_BUFFER + */ +static unsigned int ath5k_hw_rfregs_op(u32 *rf, u32 offset, u32 reg, u32 bits, +		u32 first, u32 col, bool set) +{ +	u32 mask, entry, last, data, shift, position; +	s32 left; +	int i; + +	data = 0; + +	if (rf == NULL) +		/* should not happen */ +		return 0; + +	if (!(col <= 3 && bits <= 32 && first + bits <= 319)) { +		ATH5K_PRINTF("invalid values at offset %u\n", offset); +		return 0; +	} + +	entry = ((first - 1) / 8) + offset; +	position = (first - 1) % 8; + +	if (set == true) +		data = ath5k_hw_bitswap(reg, bits); + +	for (i = shift = 0, left = bits; left > 0; position = 0, entry++, i++) { +		last = (position + left > 8) ? 8 : position + left; +		mask = (((1 << last) - 1) ^ ((1 << position) - 1)) << (col * 8); + +		if (set == true) { +			rf[entry] &= ~mask; +			rf[entry] |= ((data << position) << (col * 8)) & mask; +			data >>= (8 - position); +		} else { +			data = (((rf[entry] & mask) >> (col * 8)) >> position) +				<< shift; +			shift += last - position; +		} + +		left -= 8 - position; +	} + +	data = set == true ? 1 : ath5k_hw_bitswap(data, bits); + +	return data; +} + +static u32 ath5k_hw_rfregs_gainf_corr(struct ath5k_hw *ah) +{ +	u32 mix, step; +	u32 *rf; + +	if (ah->ah_rf_banks == NULL) +		return 0; + +	rf = ah->ah_rf_banks; +	ah->ah_gain.g_f_corr = 0; + +	if (ath5k_hw_rfregs_op(rf, ah->ah_offset[7], 0, 1, 36, 0, false) != 1) +		return 0; + +	step = ath5k_hw_rfregs_op(rf, ah->ah_offset[7], 0, 4, 32, 0, false); +	mix = ah->ah_gain.g_step->gos_param[0]; + +	switch (mix) { +	case 3: +		ah->ah_gain.g_f_corr = step * 2; +		break; +	case 2: +		ah->ah_gain.g_f_corr = (step - 5) * 2; +		break; +	case 1: +		ah->ah_gain.g_f_corr = step; +		break; +	default: +		ah->ah_gain.g_f_corr = 0; +		break; +	} + +	return ah->ah_gain.g_f_corr; +} + +static bool ath5k_hw_rfregs_gain_readback(struct ath5k_hw *ah) +{ +	u32 step, mix, level[4]; +	u32 *rf; + +	if (ah->ah_rf_banks == NULL) +		return false; + +	rf = ah->ah_rf_banks; + +	if (ah->ah_radio == AR5K_RF5111) { +		step = ath5k_hw_rfregs_op(rf, ah->ah_offset[7], 0, 6, 37, 0, +				false); +		level[0] = 0; +		level[1] = (step == 0x3f) ? 0x32 : step + 4; +		level[2] = (step != 0x3f) ? 0x40 : level[0]; +		level[3] = level[2] + 0x32; + +		ah->ah_gain.g_high = level[3] - +			(step == 0x3f ? AR5K_GAIN_DYN_ADJUST_HI_MARGIN : -5); +		ah->ah_gain.g_low = level[0] + +			(step == 0x3f ? AR5K_GAIN_DYN_ADJUST_LO_MARGIN : 0); +	} else { +		mix = ath5k_hw_rfregs_op(rf, ah->ah_offset[7], 0, 1, 36, 0, +				false); +		level[0] = level[2] = 0; + +		if (mix == 1) { +			level[1] = level[3] = 83; +		} else { +			level[1] = level[3] = 107; +			ah->ah_gain.g_high = 55; +		} +	} + +	return (ah->ah_gain.g_current >= level[0] && +			ah->ah_gain.g_current <= level[1]) || +		(ah->ah_gain.g_current >= level[2] && +			ah->ah_gain.g_current <= level[3]); +} + +static s32 ath5k_hw_rfregs_gain_adjust(struct ath5k_hw *ah) +{ +	const struct ath5k_gain_opt *go; +	int ret = 0; + +	switch (ah->ah_radio) { +	case AR5K_RF5111: +		go = &rfgain_opt_5111; +		break; +	case AR5K_RF5112: +	case AR5K_RF5413: /* ??? */ +		go = &rfgain_opt_5112; +		break; +	default: +		return 0; +	} + +	ah->ah_gain.g_step = &go->go_step[ah->ah_gain.g_step_idx]; + +	if (ah->ah_gain.g_current >= ah->ah_gain.g_high) { +		if (ah->ah_gain.g_step_idx == 0) +			return -1; +		for (ah->ah_gain.g_target = ah->ah_gain.g_current; +				ah->ah_gain.g_target >=  ah->ah_gain.g_high && +				ah->ah_gain.g_step_idx > 0; +				ah->ah_gain.g_step = +					&go->go_step[ah->ah_gain.g_step_idx]) +			ah->ah_gain.g_target -= 2 * +			    (go->go_step[--(ah->ah_gain.g_step_idx)].gos_gain - +			    ah->ah_gain.g_step->gos_gain); + +		ret = 1; +		goto done; +	} + +	if (ah->ah_gain.g_current <= ah->ah_gain.g_low) { +		if (ah->ah_gain.g_step_idx == (go->go_steps_count - 1)) +			return -2; +		for (ah->ah_gain.g_target = ah->ah_gain.g_current; +				ah->ah_gain.g_target <= ah->ah_gain.g_low && +				ah->ah_gain.g_step_idx < go->go_steps_count-1; +				ah->ah_gain.g_step = +					&go->go_step[ah->ah_gain.g_step_idx]) +			ah->ah_gain.g_target -= 2 * +			    (go->go_step[++ah->ah_gain.g_step_idx].gos_gain - +			    ah->ah_gain.g_step->gos_gain); + +		ret = 2; +		goto done; +	} + +done: +	ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE, +		"ret %d, gain step %u, current gain %u, target gain %u\n", +		ret, ah->ah_gain.g_step_idx, ah->ah_gain.g_current, +		ah->ah_gain.g_target); + +	return ret; +} + +/* + * Read EEPROM Calibration data, modify RF Banks and Initialize RF5111 + */ +static int ath5k_hw_rf5111_rfregs(struct ath5k_hw *ah, +		struct ieee80211_channel *channel, unsigned int mode) +{ +	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; +	u32 *rf; +	const unsigned int rf_size = ARRAY_SIZE(rfregs_5111); +	unsigned int i; +	int obdb = -1, bank = -1; +	u32 ee_mode; + +	AR5K_ASSERT_ENTRY(mode, AR5K_INI_VAL_MAX); + +	rf = ah->ah_rf_banks; + +	/* Copy values to modify them */ +	for (i = 0; i < rf_size; i++) { +		if (rfregs_5111[i].rf_bank >= AR5K_RF5111_INI_RF_MAX_BANKS) { +			ATH5K_ERR(ah->ah_sc, "invalid bank\n"); +			return -EINVAL; +		} + +		if (bank != rfregs_5111[i].rf_bank) { +			bank = rfregs_5111[i].rf_bank; +			ah->ah_offset[bank] = i; +		} + +		rf[i] = rfregs_5111[i].rf_value[mode]; +	} + +	/* Modify bank 0 */ +	if (channel->val & CHANNEL_2GHZ) { +		if (channel->val & CHANNEL_CCK) +			ee_mode = AR5K_EEPROM_MODE_11B; +		else +			ee_mode = AR5K_EEPROM_MODE_11G; +		obdb = 0; + +		if (!ath5k_hw_rfregs_op(rf, ah->ah_offset[0], +				ee->ee_ob[ee_mode][obdb], 3, 119, 0, true)) +			return -EINVAL; + +		if (!ath5k_hw_rfregs_op(rf, ah->ah_offset[0], +				ee->ee_ob[ee_mode][obdb], 3, 122, 0, true)) +			return -EINVAL; + +		obdb = 1; +	/* Modify bank 6 */ +	} else { +		/* For 11a, Turbo and XR */ +		ee_mode = AR5K_EEPROM_MODE_11A; +		obdb =	 channel->freq >= 5725 ? 3 : +			(channel->freq >= 5500 ? 2 : +			(channel->freq >= 5260 ? 1 : +			 (channel->freq > 4000 ? 0 : -1))); + +		if (!ath5k_hw_rfregs_op(rf, ah->ah_offset[6], +				ee->ee_pwd_84, 1, 51, 3, true)) +			return -EINVAL; + +		if (!ath5k_hw_rfregs_op(rf, ah->ah_offset[6], +				ee->ee_pwd_90, 1, 45, 3, true)) +			return -EINVAL; +	} + +	if (!ath5k_hw_rfregs_op(rf, ah->ah_offset[6], +			!ee->ee_xpd[ee_mode], 1, 95, 0, true)) +		return -EINVAL; + +	if (!ath5k_hw_rfregs_op(rf, ah->ah_offset[6], +			ee->ee_x_gain[ee_mode], 4, 96, 0, true)) +		return -EINVAL; + +	if (!ath5k_hw_rfregs_op(rf, ah->ah_offset[6], obdb >= 0 ? +			ee->ee_ob[ee_mode][obdb] : 0, 3, 104, 0, true)) +		return -EINVAL; + +	if (!ath5k_hw_rfregs_op(rf, ah->ah_offset[6], obdb >= 0 ? +			ee->ee_db[ee_mode][obdb] : 0, 3, 107, 0, true)) +		return -EINVAL; + +	/* Modify bank 7 */ +	if (!ath5k_hw_rfregs_op(rf, ah->ah_offset[7], +			ee->ee_i_gain[ee_mode], 6, 29, 0, true)) +		return -EINVAL; + +	if (!ath5k_hw_rfregs_op(rf, ah->ah_offset[7], +			ee->ee_xpd[ee_mode], 1, 4, 0, true)) +		return -EINVAL; + +	/* Write RF values */ +	for (i = 0; i < rf_size; i++) { +		AR5K_REG_WAIT(i); +		ath5k_hw_reg_write(ah, rf[i], rfregs_5111[i].rf_register); +	} + +	return 0; +} + +/* + * Read EEPROM Calibration data, modify RF Banks and Initialize RF5112 + */ +static int ath5k_hw_rf5112_rfregs(struct ath5k_hw *ah, +		struct ieee80211_channel *channel, unsigned int mode) +{ +	const struct ath5k_ini_rf *rf_ini; +	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; +	u32 *rf; +	unsigned int rf_size, i; +	int obdb = -1, bank = -1; +	u32 ee_mode; + +	AR5K_ASSERT_ENTRY(mode, AR5K_INI_VAL_MAX); + +	rf = ah->ah_rf_banks; + +	if (ah->ah_radio_5ghz_revision >= AR5K_SREV_RAD_2112A +		&& !test_bit(MODE_IEEE80211A, ah->ah_capabilities.cap_mode)){ +		rf_ini = rfregs_2112a; +		rf_size = ARRAY_SIZE(rfregs_5112a); +		if (mode < 2) { +			ATH5K_ERR(ah->ah_sc,"invalid channel mode: %i\n",mode); +			return -EINVAL; +		} +		mode = mode - 2; /*no a/turboa modes for 2112*/ +	} else if (ah->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A) { +		rf_ini = rfregs_5112a; +		rf_size = ARRAY_SIZE(rfregs_5112a); +	} else { +		rf_ini = rfregs_5112; +		rf_size = ARRAY_SIZE(rfregs_5112); +	} + +	/* Copy values to modify them */ +	for (i = 0; i < rf_size; i++) { +		if (rf_ini[i].rf_bank >= AR5K_RF5112_INI_RF_MAX_BANKS) { +			ATH5K_ERR(ah->ah_sc, "invalid bank\n"); +			return -EINVAL; +		} + +		if (bank != rf_ini[i].rf_bank) { +			bank = rf_ini[i].rf_bank; +			ah->ah_offset[bank] = i; +		} + +		rf[i] = rf_ini[i].rf_value[mode]; +	} + +	/* Modify bank 6 */ +	if (channel->val & CHANNEL_2GHZ) { +		if (channel->val & CHANNEL_OFDM) +			ee_mode = AR5K_EEPROM_MODE_11G; +		else +			ee_mode = AR5K_EEPROM_MODE_11B; +		obdb = 0; + +		if (!ath5k_hw_rfregs_op(rf, ah->ah_offset[6], +				ee->ee_ob[ee_mode][obdb], 3, 287, 0, true)) +			return -EINVAL; + +		if (!ath5k_hw_rfregs_op(rf, ah->ah_offset[6], +				ee->ee_ob[ee_mode][obdb], 3, 290, 0, true)) +			return -EINVAL; +	} else { +		/* For 11a, Turbo and XR */ +		ee_mode = AR5K_EEPROM_MODE_11A; +		obdb = channel->freq >= 5725 ? 3 : +		    (channel->freq >= 5500 ? 2 : +			(channel->freq >= 5260 ? 1 : +			    (channel->freq > 4000 ? 0 : -1))); + +		if (!ath5k_hw_rfregs_op(rf, ah->ah_offset[6], +				ee->ee_ob[ee_mode][obdb], 3, 279, 0, true)) +			return -EINVAL; + +		if (!ath5k_hw_rfregs_op(rf, ah->ah_offset[6], +				ee->ee_ob[ee_mode][obdb], 3, 282, 0, true)) +			return -EINVAL; +	} + +	ath5k_hw_rfregs_op(rf, ah->ah_offset[6], +	    ee->ee_x_gain[ee_mode], 2, 270, 0, true); +	ath5k_hw_rfregs_op(rf, ah->ah_offset[6], +	    ee->ee_x_gain[ee_mode], 2, 257, 0, true); + +	if (!ath5k_hw_rfregs_op(rf, ah->ah_offset[6], +			ee->ee_xpd[ee_mode], 1, 302, 0, true)) +		return -EINVAL; + +	/* Modify bank 7 */ +	if (!ath5k_hw_rfregs_op(rf, ah->ah_offset[7], +			ee->ee_i_gain[ee_mode], 6, 14, 0, true)) +		return -EINVAL; + +	/* Write RF values */ +	for (i = 0; i < rf_size; i++) +		ath5k_hw_reg_write(ah, rf[i], rf_ini[i].rf_register); + +	return 0; +} + +/* + * Initialize RF5413/5414 + */ +static int ath5k_hw_rf5413_rfregs(struct ath5k_hw *ah, +		struct ieee80211_channel *channel, unsigned int mode) +{ +	const struct ath5k_ini_rf *rf_ini; +	u32 *rf; +	unsigned int rf_size, i; +	int bank = -1; + +	AR5K_ASSERT_ENTRY(mode, AR5K_INI_VAL_MAX); + +	rf = ah->ah_rf_banks; + +	rf_ini = rfregs_5413; +	rf_size = ARRAY_SIZE(rfregs_5413); + +	/* Copy values to modify them */ +	for (i = 0; i < rf_size; i++) { +		if (rf_ini[i].rf_bank >= AR5K_RF5112_INI_RF_MAX_BANKS) { +			ATH5K_ERR(ah->ah_sc, "invalid bank\n"); +			return -EINVAL; +		} + +		if (bank != rf_ini[i].rf_bank) { +			bank = rf_ini[i].rf_bank; +			ah->ah_offset[bank] = i; +		} + +		rf[i] = rf_ini[i].rf_value[mode]; +	} + +	/* +	 * After compairing dumps from different cards +	 * we get the same RF_BUFFER settings (diff returns +	 * 0 lines). It seems that RF_BUFFER settings are static +	 * and are written unmodified (no EEPROM stuff +	 * is used because calibration data would be +	 * different between different cards and would result +	 * different RF_BUFFER settings) +	 */ + +	/* Write RF values */ +	for (i = 0; i < rf_size; i++) +		ath5k_hw_reg_write(ah, rf[i], rf_ini[i].rf_register); + +	return 0; +} + +/* + * Initialize RF + */ +int ath5k_hw_rfregs(struct ath5k_hw *ah, struct ieee80211_channel *channel, +		unsigned int mode) +{ +	int (*func)(struct ath5k_hw *, struct ieee80211_channel *, unsigned int); +	int ret; + +	switch (ah->ah_radio) { +	case AR5K_RF5111: +		ah->ah_rf_banks_size = sizeof(rfregs_5111); +		func = ath5k_hw_rf5111_rfregs; +		break; +	case AR5K_RF5112: +		if (ah->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A) +			ah->ah_rf_banks_size = sizeof(rfregs_5112a); +		else +			ah->ah_rf_banks_size = sizeof(rfregs_5112); +		func = ath5k_hw_rf5112_rfregs; +		break; +	case AR5K_RF5413: +		ah->ah_rf_banks_size = sizeof(rfregs_5413); +		func = ath5k_hw_rf5413_rfregs; +		break; +	default: +		return -EINVAL; +	} + +	if (ah->ah_rf_banks == NULL) { +		/* XXX do extra checks? */ +		ah->ah_rf_banks = kmalloc(ah->ah_rf_banks_size, GFP_KERNEL); +		if (ah->ah_rf_banks == NULL) { +			ATH5K_ERR(ah->ah_sc, "out of memory\n"); +			return -ENOMEM; +		} +	} + +	ret = func(ah, channel, mode); +	if (!ret) +		ah->ah_rf_gain = AR5K_RFGAIN_INACTIVE; + +	return ret; +} + +int ath5k_hw_rfgain(struct ath5k_hw *ah, unsigned int freq) +{ +	const struct ath5k_ini_rfgain *ath5k_rfg; +	unsigned int i, size; + +	switch (ah->ah_radio) { +	case AR5K_RF5111: +		ath5k_rfg = rfgain_5111; +		size = ARRAY_SIZE(rfgain_5111); +		break; +	case AR5K_RF5112: +		ath5k_rfg = rfgain_5112; +		size = ARRAY_SIZE(rfgain_5112); +		break; +	case AR5K_RF5413: +		ath5k_rfg = rfgain_5413; +		size = ARRAY_SIZE(rfgain_5413); +		break; +	default: +		return -EINVAL; +	} + +	switch (freq) { +	case AR5K_INI_RFGAIN_2GHZ: +	case AR5K_INI_RFGAIN_5GHZ: +		break; +	default: +		return -EINVAL; +	} + +	for (i = 0; i < size; i++) { +		AR5K_REG_WAIT(i); +		ath5k_hw_reg_write(ah, ath5k_rfg[i].rfg_value[freq], +			(u32)ath5k_rfg[i].rfg_register); +	} + +	return 0; +} + +enum ath5k_rfgain ath5k_hw_get_rf_gain(struct ath5k_hw *ah) +{ +	u32 data, type; + +	ATH5K_TRACE(ah->ah_sc); + +	if (ah->ah_rf_banks == NULL || !ah->ah_gain.g_active || +			ah->ah_version <= AR5K_AR5211) +		return AR5K_RFGAIN_INACTIVE; + +	if (ah->ah_rf_gain != AR5K_RFGAIN_READ_REQUESTED) +		goto done; + +	data = ath5k_hw_reg_read(ah, AR5K_PHY_PAPD_PROBE); + +	if (!(data & AR5K_PHY_PAPD_PROBE_TX_NEXT)) { +		ah->ah_gain.g_current = data >> AR5K_PHY_PAPD_PROBE_GAINF_S; +		type = AR5K_REG_MS(data, AR5K_PHY_PAPD_PROBE_TYPE); + +		if (type == AR5K_PHY_PAPD_PROBE_TYPE_CCK) +			ah->ah_gain.g_current += AR5K_GAIN_CCK_PROBE_CORR; + +		if (ah->ah_radio >= AR5K_RF5112) { +			ath5k_hw_rfregs_gainf_corr(ah); +			ah->ah_gain.g_current = +				ah->ah_gain.g_current>=ah->ah_gain.g_f_corr ? +				(ah->ah_gain.g_current-ah->ah_gain.g_f_corr) : +				0; +		} + +		if (ath5k_hw_rfregs_gain_readback(ah) && +				AR5K_GAIN_CHECK_ADJUST(&ah->ah_gain) && +				ath5k_hw_rfregs_gain_adjust(ah)) +			ah->ah_rf_gain = AR5K_RFGAIN_NEED_CHANGE; +	} + +done: +	return ah->ah_rf_gain; +} + +int ath5k_hw_set_rfgain_opt(struct ath5k_hw *ah) +{ +	/* Initialize the gain optimization values */ +	switch (ah->ah_radio) { +	case AR5K_RF5111: +		ah->ah_gain.g_step_idx = rfgain_opt_5111.go_default; +		ah->ah_gain.g_step = +		    &rfgain_opt_5111.go_step[ah->ah_gain.g_step_idx]; +		ah->ah_gain.g_low = 20; +		ah->ah_gain.g_high = 35; +		ah->ah_gain.g_active = 1; +		break; +	case AR5K_RF5112: +	case AR5K_RF5413: /* ??? */ +		ah->ah_gain.g_step_idx = rfgain_opt_5112.go_default; +		ah->ah_gain.g_step = +		    &rfgain_opt_5112.go_step[ah->ah_gain.g_step_idx]; +		ah->ah_gain.g_low = 20; +		ah->ah_gain.g_high = 85; +		ah->ah_gain.g_active = 1; +		break; +	default: +		return -EINVAL; +	} + +	return 0; +} + +/**************************\ +  PHY/RF channel functions +\**************************/ + +/* + * Check if a channel is supported + */ +bool ath5k_channel_ok(struct ath5k_hw *ah, u16 freq, unsigned int flags) +{ +	/* Check if the channel is in our supported range */ +	if (flags & CHANNEL_2GHZ) { +		if ((freq >= ah->ah_capabilities.cap_range.range_2ghz_min) && +		    (freq <= ah->ah_capabilities.cap_range.range_2ghz_max)) +			return true; +	} else if (flags & CHANNEL_5GHZ) +		if ((freq >= ah->ah_capabilities.cap_range.range_5ghz_min) && +		    (freq <= ah->ah_capabilities.cap_range.range_5ghz_max)) +			return true; + +	return false; +} + +/* + * Convertion needed for RF5110 + */ +static u32 ath5k_hw_rf5110_chan2athchan(struct ieee80211_channel *channel) +{ +	u32 athchan; + +	/* +	 * Convert IEEE channel/MHz to an internal channel value used +	 * by the AR5210 chipset. This has not been verified with +	 * newer chipsets like the AR5212A who have a completely +	 * different RF/PHY part. +	 */ +	athchan = (ath5k_hw_bitswap((channel->chan - 24) / 2, 5) << 1) | +		(1 << 6) | 0x1; + +	return athchan; +} + +/* + * Set channel on RF5110 + */ +static int ath5k_hw_rf5110_channel(struct ath5k_hw *ah, +		struct ieee80211_channel *channel) +{ +	u32 data; + +	/* +	 * Set the channel and wait +	 */ +	data = ath5k_hw_rf5110_chan2athchan(channel); +	ath5k_hw_reg_write(ah, data, AR5K_RF_BUFFER); +	ath5k_hw_reg_write(ah, 0, AR5K_RF_BUFFER_CONTROL_0); +	mdelay(1); + +	return 0; +} + +/* + * Convertion needed for 5111 + */ +static int ath5k_hw_rf5111_chan2athchan(unsigned int ieee, +		struct ath5k_athchan_2ghz *athchan) +{ +	int channel; + +	/* Cast this value to catch negative channel numbers (>= -19) */ +	channel = (int)ieee; + +	/* +	 * Map 2GHz IEEE channel to 5GHz Atheros channel +	 */ +	if (channel <= 13) { +		athchan->a2_athchan = 115 + channel; +		athchan->a2_flags = 0x46; +	} else if (channel == 14) { +		athchan->a2_athchan = 124; +		athchan->a2_flags = 0x44; +	} else if (channel >= 15 && channel <= 26) { +		athchan->a2_athchan = ((channel - 14) * 4) + 132; +		athchan->a2_flags = 0x46; +	} else +		return -EINVAL; + +	return 0; +} + +/* + * Set channel on 5111 + */ +static int ath5k_hw_rf5111_channel(struct ath5k_hw *ah, +		struct ieee80211_channel *channel) +{ +	struct ath5k_athchan_2ghz ath5k_channel_2ghz; +	unsigned int ath5k_channel = channel->chan; +	u32 data0, data1, clock; +	int ret; + +	/* +	 * Set the channel on the RF5111 radio +	 */ +	data0 = data1 = 0; + +	if (channel->val & CHANNEL_2GHZ) { +		/* Map 2GHz channel to 5GHz Atheros channel ID */ +		ret = ath5k_hw_rf5111_chan2athchan(channel->chan, +				&ath5k_channel_2ghz); +		if (ret) +			return ret; + +		ath5k_channel = ath5k_channel_2ghz.a2_athchan; +		data0 = ((ath5k_hw_bitswap(ath5k_channel_2ghz.a2_flags, 8) & 0xff) +		    << 5) | (1 << 4); +	} + +	if (ath5k_channel < 145 || !(ath5k_channel & 1)) { +		clock = 1; +		data1 = ((ath5k_hw_bitswap(ath5k_channel - 24, 8) & 0xff) << 2) | +			(clock << 1) | (1 << 10) | 1; +	} else { +		clock = 0; +		data1 = ((ath5k_hw_bitswap((ath5k_channel - 24) / 2, 8) & 0xff) +			<< 2) | (clock << 1) | (1 << 10) | 1; +	} + +	ath5k_hw_reg_write(ah, (data1 & 0xff) | ((data0 & 0xff) << 8), +			AR5K_RF_BUFFER); +	ath5k_hw_reg_write(ah, ((data1 >> 8) & 0xff) | (data0 & 0xff00), +			AR5K_RF_BUFFER_CONTROL_3); + +	return 0; +} + +/* + * Set channel on 5112 and newer + */ +static int ath5k_hw_rf5112_channel(struct ath5k_hw *ah, +		struct ieee80211_channel *channel) +{ +	u32 data, data0, data1, data2; +	u16 c; + +	data = data0 = data1 = data2 = 0; +	c = channel->freq; + +	/* +	 * Set the channel on the RF5112 or newer +	 */ +	if (c < 4800) { +		if (!((c - 2224) % 5)) { +			data0 = ((2 * (c - 704)) - 3040) / 10; +			data1 = 1; +		} else if (!((c - 2192) % 5)) { +			data0 = ((2 * (c - 672)) - 3040) / 10; +			data1 = 0; +		} else +			return -EINVAL; + +		data0 = ath5k_hw_bitswap((data0 << 2) & 0xff, 8); +	} else { +		if (!(c % 20) && c >= 5120) { +			data0 = ath5k_hw_bitswap(((c - 4800) / 20 << 2), 8); +			data2 = ath5k_hw_bitswap(3, 2); +		} else if (!(c % 10)) { +			data0 = ath5k_hw_bitswap(((c - 4800) / 10 << 1), 8); +			data2 = ath5k_hw_bitswap(2, 2); +		} else if (!(c % 5)) { +			data0 = ath5k_hw_bitswap((c - 4800) / 5, 8); +			data2 = ath5k_hw_bitswap(1, 2); +		} else +			return -EINVAL; +	} + +	data = (data0 << 4) | (data1 << 1) | (data2 << 2) | 0x1001; + +	ath5k_hw_reg_write(ah, data & 0xff, AR5K_RF_BUFFER); +	ath5k_hw_reg_write(ah, (data >> 8) & 0x7f, AR5K_RF_BUFFER_CONTROL_5); + +	return 0; +} + +/* + * Set a channel on the radio chip + */ +int ath5k_hw_channel(struct ath5k_hw *ah, struct ieee80211_channel *channel) +{ +	int ret; + +	/* +	 * Check bounds supported by the PHY +	 * (don't care about regulation restrictions at this point) +	 */ +	if ((channel->freq < ah->ah_capabilities.cap_range.range_2ghz_min || +	    channel->freq > ah->ah_capabilities.cap_range.range_2ghz_max) && +	    (channel->freq < ah->ah_capabilities.cap_range.range_5ghz_min || +	    channel->freq > ah->ah_capabilities.cap_range.range_5ghz_max)) { +		ATH5K_ERR(ah->ah_sc, +			"channel out of supported range (%u MHz)\n", +			channel->freq); +		return -EINVAL; +	} + +	/* +	 * Set the channel and wait +	 */ +	switch (ah->ah_radio) { +	case AR5K_RF5110: +		ret = ath5k_hw_rf5110_channel(ah, channel); +		break; +	case AR5K_RF5111: +		ret = ath5k_hw_rf5111_channel(ah, channel); +		break; +	default: +		ret = ath5k_hw_rf5112_channel(ah, channel); +		break; +	} + +	if (ret) +		return ret; + +	ah->ah_current_channel.freq = channel->freq; +	ah->ah_current_channel.val = channel->val; +	ah->ah_turbo = channel->val == CHANNEL_T ? true : false; + +	return 0; +} + +/*****************\ +  PHY calibration +\*****************/ + +/** + * ath5k_hw_noise_floor_calibration - perform PHY noise floor calibration + * + * @ah: struct ath5k_hw pointer we are operating on + * @freq: the channel frequency, just used for error logging + * + * This function performs a noise floor calibration of the PHY and waits for + * it to complete. Then the noise floor value is compared to some maximum + * noise floor we consider valid. + * + * Note that this is different from what the madwifi HAL does: it reads the + * noise floor and afterwards initiates the calibration. Since the noise floor + * calibration can take some time to finish, depending on the current channel + * use, that avoids the occasional timeout warnings we are seeing now. + * + * See the following link for an Atheros patent on noise floor calibration: + * http://patft.uspto.gov/netacgi/nph-Parser?Sect1=PTO1&Sect2=HITOFF&d=PALL \ + * &p=1&u=%2Fnetahtml%2FPTO%2Fsrchnum.htm&r=1&f=G&l=50&s1=7245893.PN.&OS=PN/7 + * + */ +int +ath5k_hw_noise_floor_calibration(struct ath5k_hw *ah, short freq) +{ +	int ret; +	unsigned int i; +	s32 noise_floor; + +	/* +	 * Enable noise floor calibration and wait until completion +	 */ +	AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, +				AR5K_PHY_AGCCTL_NF); + +	ret = ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, +			AR5K_PHY_AGCCTL_NF, 0, false); +	if (ret) { +		ATH5K_ERR(ah->ah_sc, +			"noise floor calibration timeout (%uMHz)\n", freq); +		return ret; +	} + +	/* Wait until the noise floor is calibrated and read the value */ +	for (i = 20; i > 0; i--) { +		mdelay(1); +		noise_floor = ath5k_hw_reg_read(ah, AR5K_PHY_NF); +		noise_floor = AR5K_PHY_NF_RVAL(noise_floor); +		if (noise_floor & AR5K_PHY_NF_ACTIVE) { +			noise_floor = AR5K_PHY_NF_AVAL(noise_floor); + +			if (noise_floor <= AR5K_TUNE_NOISE_FLOOR) +				break; +		} +	} + +	ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_CALIBRATE, +		"noise floor %d\n", noise_floor); + +	if (noise_floor > AR5K_TUNE_NOISE_FLOOR) { +		ATH5K_ERR(ah->ah_sc, +			"noise floor calibration failed (%uMHz)\n", freq); +		return -EIO; +	} + +	ah->ah_noise_floor = noise_floor; + +	return 0; +} + +/* + * Perform a PHY calibration on RF5110 + * -Fix BPSK/QAM Constellation (I/Q correction) + * -Calculate Noise Floor + */ +static int ath5k_hw_rf5110_calibrate(struct ath5k_hw *ah, +		struct ieee80211_channel *channel) +{ +	u32 phy_sig, phy_agc, phy_sat, beacon; +	int ret; + +	/* +	 * Disable beacons and RX/TX queues, wait +	 */ +	AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5210, +		AR5K_DIAG_SW_DIS_TX | AR5K_DIAG_SW_DIS_RX_5210); +	beacon = ath5k_hw_reg_read(ah, AR5K_BEACON_5210); +	ath5k_hw_reg_write(ah, beacon & ~AR5K_BEACON_ENABLE, AR5K_BEACON_5210); + +	udelay(2300); + +	/* +	 * Set the channel (with AGC turned off) +	 */ +	AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE); +	udelay(10); +	ret = ath5k_hw_channel(ah, channel); + +	/* +	 * Activate PHY and wait +	 */ +	ath5k_hw_reg_write(ah, AR5K_PHY_ACT_ENABLE, AR5K_PHY_ACT); +	mdelay(1); + +	AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE); + +	if (ret) +		return ret; + +	/* +	 * Calibrate the radio chip +	 */ + +	/* Remember normal state */ +	phy_sig = ath5k_hw_reg_read(ah, AR5K_PHY_SIG); +	phy_agc = ath5k_hw_reg_read(ah, AR5K_PHY_AGCCOARSE); +	phy_sat = ath5k_hw_reg_read(ah, AR5K_PHY_ADCSAT); + +	/* Update radio registers */ +	ath5k_hw_reg_write(ah, (phy_sig & ~(AR5K_PHY_SIG_FIRPWR)) | +		AR5K_REG_SM(-1, AR5K_PHY_SIG_FIRPWR), AR5K_PHY_SIG); + +	ath5k_hw_reg_write(ah, (phy_agc & ~(AR5K_PHY_AGCCOARSE_HI | +			AR5K_PHY_AGCCOARSE_LO)) | +		AR5K_REG_SM(-1, AR5K_PHY_AGCCOARSE_HI) | +		AR5K_REG_SM(-127, AR5K_PHY_AGCCOARSE_LO), AR5K_PHY_AGCCOARSE); + +	ath5k_hw_reg_write(ah, (phy_sat & ~(AR5K_PHY_ADCSAT_ICNT | +			AR5K_PHY_ADCSAT_THR)) | +		AR5K_REG_SM(2, AR5K_PHY_ADCSAT_ICNT) | +		AR5K_REG_SM(12, AR5K_PHY_ADCSAT_THR), AR5K_PHY_ADCSAT); + +	udelay(20); + +	AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE); +	udelay(10); +	ath5k_hw_reg_write(ah, AR5K_PHY_RFSTG_DISABLE, AR5K_PHY_RFSTG); +	AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE); + +	mdelay(1); + +	/* +	 * Enable calibration and wait until completion +	 */ +	AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_CAL); + +	ret = ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, +			AR5K_PHY_AGCCTL_CAL, 0, false); + +	/* Reset to normal state */ +	ath5k_hw_reg_write(ah, phy_sig, AR5K_PHY_SIG); +	ath5k_hw_reg_write(ah, phy_agc, AR5K_PHY_AGCCOARSE); +	ath5k_hw_reg_write(ah, phy_sat, AR5K_PHY_ADCSAT); + +	if (ret) { +		ATH5K_ERR(ah->ah_sc, "calibration timeout (%uMHz)\n", +				channel->freq); +		return ret; +	} + +	ret = ath5k_hw_noise_floor_calibration(ah, channel->freq); +	if (ret) +		return ret; + +	/* +	 * Re-enable RX/TX and beacons +	 */ +	AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW_5210, +		AR5K_DIAG_SW_DIS_TX | AR5K_DIAG_SW_DIS_RX_5210); +	ath5k_hw_reg_write(ah, beacon, AR5K_BEACON_5210); + +	return 0; +} + +/* + * Perform a PHY calibration on RF5111/5112 + */ +static int ath5k_hw_rf511x_calibrate(struct ath5k_hw *ah, +		struct ieee80211_channel *channel) +{ +	u32 i_pwr, q_pwr; +	s32 iq_corr, i_coff, i_coffd, q_coff, q_coffd; +	ATH5K_TRACE(ah->ah_sc); + +	if (ah->ah_calibration == false || +			ath5k_hw_reg_read(ah, AR5K_PHY_IQ) & AR5K_PHY_IQ_RUN) +		goto done; + +	ah->ah_calibration = false; + +	iq_corr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_CORR); +	i_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_I); +	q_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_Q); +	i_coffd = ((i_pwr >> 1) + (q_pwr >> 1)) >> 7; +	q_coffd = q_pwr >> 6; + +	if (i_coffd == 0 || q_coffd == 0) +		goto done; + +	i_coff = ((-iq_corr) / i_coffd) & 0x3f; +	q_coff = (((s32)i_pwr / q_coffd) - 64) & 0x1f; + +	/* Commit new IQ value */ +	AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_ENABLE | +		((u32)q_coff) | ((u32)i_coff << AR5K_PHY_IQ_CORR_Q_I_COFF_S)); + +done: +	ath5k_hw_noise_floor_calibration(ah, channel->freq); + +	/* Request RF gain */ +	if (channel->val & CHANNEL_5GHZ) { +		ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txpower.txp_max, +			AR5K_PHY_PAPD_PROBE_TXPOWER) | +			AR5K_PHY_PAPD_PROBE_TX_NEXT, AR5K_PHY_PAPD_PROBE); +		ah->ah_rf_gain = AR5K_RFGAIN_READ_REQUESTED; +	} + +	return 0; +} + +/* + * Perform a PHY calibration + */ +int ath5k_hw_phy_calibrate(struct ath5k_hw *ah, +		struct ieee80211_channel *channel) +{ +	int ret; + +	if (ah->ah_radio == AR5K_RF5110) +		ret = ath5k_hw_rf5110_calibrate(ah, channel); +	else +		ret = ath5k_hw_rf511x_calibrate(ah, channel); + +	return ret; +} + +int ath5k_hw_phy_disable(struct ath5k_hw *ah) +{ +	ATH5K_TRACE(ah->ah_sc); +	/*Just a try M.F.*/ +	ath5k_hw_reg_write(ah, AR5K_PHY_ACT_DISABLE, AR5K_PHY_ACT); + +	return 0; +} + +/********************\ +  Misc PHY functions +\********************/ + +/* + * Get the PHY Chip revision + */ +u16 ath5k_hw_radio_revision(struct ath5k_hw *ah, unsigned int chan) +{ +	unsigned int i; +	u32 srev; +	u16 ret; + +	ATH5K_TRACE(ah->ah_sc); + +	/* +	 * Set the radio chip access register +	 */ +	switch (chan) { +	case CHANNEL_2GHZ: +		ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_2GHZ, AR5K_PHY(0)); +		break; +	case CHANNEL_5GHZ: +		ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0)); +		break; +	default: +		return 0; +	} + +	mdelay(2); + +	/* ...wait until PHY is ready and read the selected radio revision */ +	ath5k_hw_reg_write(ah, 0x00001c16, AR5K_PHY(0x34)); + +	for (i = 0; i < 8; i++) +		ath5k_hw_reg_write(ah, 0x00010000, AR5K_PHY(0x20)); + +	if (ah->ah_version == AR5K_AR5210) { +		srev = ath5k_hw_reg_read(ah, AR5K_PHY(256) >> 28) & 0xf; +		ret = (u16)ath5k_hw_bitswap(srev, 4) + 1; +	} else { +		srev = (ath5k_hw_reg_read(ah, AR5K_PHY(0x100)) >> 24) & 0xff; +		ret = (u16)ath5k_hw_bitswap(((srev & 0xf0) >> 4) | +				((srev & 0x0f) << 4), 8); +	} + +	/* Reset to the 5GHz mode */ +	ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0)); + +	return ret; +} + +void /*TODO:Boundary check*/ +ath5k_hw_set_def_antenna(struct ath5k_hw *ah, unsigned int ant) +{ +	ATH5K_TRACE(ah->ah_sc); +	/*Just a try M.F.*/ +	if (ah->ah_version != AR5K_AR5210) +		ath5k_hw_reg_write(ah, ant, AR5K_DEFAULT_ANTENNA); +} + +unsigned int ath5k_hw_get_def_antenna(struct ath5k_hw *ah) +{ +	ATH5K_TRACE(ah->ah_sc); +	/*Just a try M.F.*/ +	if (ah->ah_version != AR5K_AR5210) +		return ath5k_hw_reg_read(ah, AR5K_DEFAULT_ANTENNA); + +	return false; /*XXX: What do we return for 5210 ?*/ +} + +/* + * TX power setup + */ + +/* + * Initialize the tx power table (not fully implemented) + */ +static void ath5k_txpower_table(struct ath5k_hw *ah, +		struct ieee80211_channel *channel, s16 max_power) +{ +	unsigned int i, min, max, n; +	u16 txpower, *rates; + +	rates = ah->ah_txpower.txp_rates; + +	txpower = AR5K_TUNE_DEFAULT_TXPOWER * 2; +	if (max_power > txpower) +		txpower = max_power > AR5K_TUNE_MAX_TXPOWER ? +		    AR5K_TUNE_MAX_TXPOWER : max_power; + +	for (i = 0; i < AR5K_MAX_RATES; i++) +		rates[i] = txpower; + +	/* XXX setup target powers by rate */ + +	ah->ah_txpower.txp_min = rates[7]; +	ah->ah_txpower.txp_max = rates[0]; +	ah->ah_txpower.txp_ofdm = rates[0]; + +	/* Calculate the power table */ +	n = ARRAY_SIZE(ah->ah_txpower.txp_pcdac); +	min = AR5K_EEPROM_PCDAC_START; +	max = AR5K_EEPROM_PCDAC_STOP; +	for (i = 0; i < n; i += AR5K_EEPROM_PCDAC_STEP) +		ah->ah_txpower.txp_pcdac[i] = +#ifdef notyet +		min + ((i * (max - min)) / n); +#else +		min; +#endif +} + +/* + * Set transmition power + */ +int /*O.K. - txpower_table is unimplemented so this doesn't work*/ +ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel, +		unsigned int txpower) +{ +	bool tpc = ah->ah_txpower.txp_tpc; +	unsigned int i; + +	ATH5K_TRACE(ah->ah_sc); +	if (txpower > AR5K_TUNE_MAX_TXPOWER) { +		ATH5K_ERR(ah->ah_sc, "invalid tx power: %u\n", txpower); +		return -EINVAL; +	} + +	/* Reset TX power values */ +	memset(&ah->ah_txpower, 0, sizeof(ah->ah_txpower)); +	ah->ah_txpower.txp_tpc = tpc; + +	/* Initialize TX power table */ +	ath5k_txpower_table(ah, channel, txpower); + +	/* +	 * Write TX power values +	 */ +	for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) { +		ath5k_hw_reg_write(ah, +			((((ah->ah_txpower.txp_pcdac[(i << 1) + 1] << 8) | 0xff) & 0xffff) << 16) | +			(((ah->ah_txpower.txp_pcdac[(i << 1)    ] << 8) | 0xff) & 0xffff), +			AR5K_PHY_PCDAC_TXPOWER(i)); +	} + +	ath5k_hw_reg_write(ah, AR5K_TXPOWER_OFDM(3, 24) | +		AR5K_TXPOWER_OFDM(2, 16) | AR5K_TXPOWER_OFDM(1, 8) | +		AR5K_TXPOWER_OFDM(0, 0), AR5K_PHY_TXPOWER_RATE1); + +	ath5k_hw_reg_write(ah, AR5K_TXPOWER_OFDM(7, 24) | +		AR5K_TXPOWER_OFDM(6, 16) | AR5K_TXPOWER_OFDM(5, 8) | +		AR5K_TXPOWER_OFDM(4, 0), AR5K_PHY_TXPOWER_RATE2); + +	ath5k_hw_reg_write(ah, AR5K_TXPOWER_CCK(10, 24) | +		AR5K_TXPOWER_CCK(9, 16) | AR5K_TXPOWER_CCK(15, 8) | +		AR5K_TXPOWER_CCK(8, 0), AR5K_PHY_TXPOWER_RATE3); + +	ath5k_hw_reg_write(ah, AR5K_TXPOWER_CCK(14, 24) | +		AR5K_TXPOWER_CCK(13, 16) | AR5K_TXPOWER_CCK(12, 8) | +		AR5K_TXPOWER_CCK(11, 0), AR5K_PHY_TXPOWER_RATE4); + +	if (ah->ah_txpower.txp_tpc == true) +		ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX_TPC_ENABLE | +			AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX); +	else +		ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX | +			AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX); + +	return 0; +} + +int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, unsigned int power) +{ +	/*Just a try M.F.*/ +	struct ieee80211_channel *channel = &ah->ah_current_channel; + +	ATH5K_TRACE(ah->ah_sc); +	ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_TXPOWER, +		"changing txpower to %d\n", power); + +	return ath5k_hw_txpower(ah, channel, power); +} diff --git a/drivers/net/wireless/ath5k/reg.h b/drivers/net/wireless/ath5k/reg.h new file mode 100644 index 000000000000..2f41c8398602 --- /dev/null +++ b/drivers/net/wireless/ath5k/reg.h @@ -0,0 +1,1987 @@ +/* + * Copyright (c) 2007 Nick Kossifidis <mickflemm@gmail.com> + * Copyright (c) 2004, 2005, 2006, 2007 Reyk Floeter <reyk@openbsd.org> + * Copyright (c) 2007 Michael Taylor <mike.taylor@apprion.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. + * + */ + +/* + * Register values for Atheros 5210/5211/5212 cards from OpenBSD's ar5k + * maintained by Reyk Floeter + * + * I tried to document those registers by looking at ar5k code, some + * 802.11 (802.11e mostly) papers and by reading various public available + * Atheros presentations and papers like these: + * + * 5210 - http://nova.stanford.edu/~bbaas/ps/isscc2002_slides.pdf + *        http://www.it.iitb.ac.in/~janak/wifire/01222734.pdf + * + * 5211 - http://www.hotchips.org/archives/hc14/3_Tue/16_mcfarland.pdf + */ + + + +/*====MAC DMA REGISTERS====*/ + +/* + * AR5210-Specific TXDP registers + * 5210 has only 2 transmit queues so no DCU/QCU, just + * 2 transmit descriptor pointers... + */ +#define AR5K_NOQCU_TXDP0	0x0000		/* Queue 0 - data */ +#define AR5K_NOQCU_TXDP1	0x0004		/* Queue 1 - beacons */ + +/* + * Mac Control Register + */ +#define	AR5K_CR		0x0008			/* Register Address */ +#define AR5K_CR_TXE0	0x00000001	/* TX Enable for queue 0 on 5210 */ +#define AR5K_CR_TXE1	0x00000002	/* TX Enable for queue 1 on 5210 */ +#define	AR5K_CR_RXE	0x00000004	/* RX Enable */ +#define AR5K_CR_TXD0	0x00000008	/* TX Disable for queue 0 on 5210 */ +#define AR5K_CR_TXD1	0x00000010	/* TX Disable for queue 1 on 5210 */ +#define	AR5K_CR_RXD	0x00000020	/* RX Disable */ +#define	AR5K_CR_SWI	0x00000040 + +/* + * RX Descriptor Pointer register + */ +#define	AR5K_RXDP	0x000c + +/* + * Configuration and status register + */ +#define	AR5K_CFG		0x0014			/* Register Address */ +#define	AR5K_CFG_SWTD		0x00000001	/* Byte-swap TX descriptor (for big endian archs) */ +#define	AR5K_CFG_SWTB		0x00000002	/* Byte-swap TX buffer (?) */ +#define	AR5K_CFG_SWRD		0x00000004	/* Byte-swap RX descriptor */ +#define	AR5K_CFG_SWRB		0x00000008	/* Byte-swap RX buffer (?) */ +#define	AR5K_CFG_SWRG		0x00000010	/* Byte-swap Register values (?) */ +#define AR5K_CFG_ADHOC		0x00000020 	/* [5211+] */ +#define AR5K_CFG_PHY_OK		0x00000100	/* [5211+] */ +#define AR5K_CFG_EEBS		0x00000200	/* EEPROM is busy */ +#define	AR5K_CFG_CLKGD		0x00000400	/* Clock gated (?) */ +#define AR5K_CFG_TXCNT		0x00007800	/* Tx frame count (?) [5210] */ +#define AR5K_CFG_TXCNT_S	11 +#define AR5K_CFG_TXFSTAT	0x00008000	/* Tx frame status (?) [5210] */ +#define AR5K_CFG_TXFSTRT	0x00010000	/* [5210] */ +#define	AR5K_CFG_PCI_THRES	0x00060000	/* [5211+] */ +#define	AR5K_CFG_PCI_THRES_S	17 + +/* + * Interrupt enable register + */ +#define AR5K_IER		0x0024		/* Register Address */ +#define AR5K_IER_DISABLE	0x00000000	/* Disable card interrupts */ +#define AR5K_IER_ENABLE		0x00000001	/* Enable card interrupts */ + + +/* + * 0x0028 is Beacon Control Register on 5210 + * and first RTS duration register on 5211 + */ + +/* + * Beacon control register [5210] + */ +#define AR5K_BCR		0x0028		/* Register Address */ +#define AR5K_BCR_AP		0x00000000	/* AP mode */ +#define AR5K_BCR_ADHOC		0x00000001	/* Ad-Hoc mode */ +#define AR5K_BCR_BDMAE		0x00000002	/* DMA enable */ +#define AR5K_BCR_TQ1FV		0x00000004	/* Use Queue1 for CAB traffic */ +#define AR5K_BCR_TQ1V		0x00000008	/* Use Queue1 for Beacon traffic */ +#define AR5K_BCR_BCGET		0x00000010 + +/* + * First RTS duration register [5211] + */ +#define AR5K_RTSD0		0x0028		/* Register Address */ +#define	AR5K_RTSD0_6		0x000000ff	/* 6Mb RTS duration mask (?) */ +#define	AR5K_RTSD0_6_S		0		/* 6Mb RTS duration shift (?) */ +#define	AR5K_RTSD0_9		0x0000ff00	/* 9Mb*/ +#define	AR5K_RTSD0_9_S		8 +#define	AR5K_RTSD0_12		0x00ff0000	/* 12Mb*/ +#define	AR5K_RTSD0_12_S		16 +#define	AR5K_RTSD0_18		0xff000000	/* 16Mb*/ +#define	AR5K_RTSD0_18_S		24 + + +/* + * 0x002c is Beacon Status Register on 5210 + * and second RTS duration register on 5211 + */ + +/* + * Beacon status register [5210] + * + * As i can see in ar5k_ar5210_tx_start Reyk uses some of the values of BCR + * for this register, so i guess TQ1V,TQ1FV and BDMAE have the same meaning + * here and SNP/SNAP means "snapshot" (so this register gets synced with BCR). + * So SNAPPEDBCRVALID sould also stand for "snapped BCR -values- valid", so i + * renamed it to SNAPSHOTSVALID to make more sense. I realy have no idea what + * else can it be. I also renamed SNPBCMD to SNPADHOC to match BCR. + */ +#define AR5K_BSR		0x002c			/* Register Address */ +#define AR5K_BSR_BDLYSW		0x00000001	/* SW Beacon delay (?) */ +#define AR5K_BSR_BDLYDMA	0x00000002	/* DMA Beacon delay (?) */ +#define AR5K_BSR_TXQ1F		0x00000004	/* Beacon queue (1) finished */ +#define AR5K_BSR_ATIMDLY	0x00000008	/* ATIM delay (?) */ +#define AR5K_BSR_SNPADHOC	0x00000100	/* Ad-hoc mode set (?) */ +#define AR5K_BSR_SNPBDMAE	0x00000200	/* Beacon DMA enabled (?) */ +#define AR5K_BSR_SNPTQ1FV	0x00000400	/* Queue1 is used for CAB traffic (?) */ +#define AR5K_BSR_SNPTQ1V	0x00000800	/* Queue1 is used for Beacon traffic (?) */ +#define AR5K_BSR_SNAPSHOTSVALID	0x00001000	/* BCR snapshots are valid (?) */ +#define AR5K_BSR_SWBA_CNT	0x00ff0000 + +/* + * Second RTS duration register [5211] + */ +#define AR5K_RTSD1		0x002c			/* Register Address */ +#define	AR5K_RTSD1_24		0x000000ff	/* 24Mb */ +#define	AR5K_RTSD1_24_S		0 +#define	AR5K_RTSD1_36		0x0000ff00	/* 36Mb */ +#define	AR5K_RTSD1_36_S		8 +#define	AR5K_RTSD1_48		0x00ff0000	/* 48Mb */ +#define	AR5K_RTSD1_48_S		16 +#define	AR5K_RTSD1_54		0xff000000	/* 54Mb */ +#define	AR5K_RTSD1_54_S		24 + + +/* + * Transmit configuration register + */ +#define AR5K_TXCFG		0x0030			/* Register Address */ +#define AR5K_TXCFG_SDMAMR	0x00000007	/* DMA size */ +#define AR5K_TXCFG_SDMAMR_S	0 +#define AR5K_TXCFG_B_MODE	0x00000008	/* Set b mode for 5111 (enable 2111) */ +#define AR5K_TXCFG_TXFSTP	0x00000008	/* TX DMA full Stop [5210] */ +#define AR5K_TXCFG_TXFULL	0x000003f0	/* TX Triger level mask */ +#define AR5K_TXCFG_TXFULL_S	4 +#define AR5K_TXCFG_TXFULL_0B	0x00000000 +#define AR5K_TXCFG_TXFULL_64B	0x00000010 +#define AR5K_TXCFG_TXFULL_128B	0x00000020 +#define AR5K_TXCFG_TXFULL_192B	0x00000030 +#define AR5K_TXCFG_TXFULL_256B	0x00000040 +#define AR5K_TXCFG_TXCONT_EN	0x00000080 +#define AR5K_TXCFG_DMASIZE	0x00000100	/* Flag for passing DMA size [5210] */ +#define AR5K_TXCFG_JUMBO_TXE	0x00000400	/* Enable jumbo frames transmition (?) [5211+] */ +#define AR5K_TXCFG_RTSRND	0x00001000	/* [5211+] */ +#define AR5K_TXCFG_FRMPAD_DIS	0x00002000	/* [5211+] */ +#define AR5K_TXCFG_RDY_DIS	0x00004000	/* [5211+] */ + +/* + * Receive configuration register + */ +#define AR5K_RXCFG		0x0034			/* Register Address */ +#define AR5K_RXCFG_SDMAMW	0x00000007	/* DMA size */ +#define AR5K_RXCFG_SDMAMW_S	0 +#define	AR5K_RXCFG_DEF_ANTENNA	0x00000008	/* Default antenna */ +#define AR5K_RXCFG_ZLFDMA	0x00000010	/* Zero-length DMA */ +#define AR5K_RXCFG_JUMBO_RXE	0x00000020	/* Enable jumbo frames reception (?) [5211+] */ +#define AR5K_RXCFG_JUMBO_WRAP	0x00000040	/* Wrap jumbo frames (?) [5211+] */ + +/* + * Receive jumbo descriptor last address register + * Only found in 5211 (?) + */ +#define AR5K_RXJLA		0x0038 + +/* + * MIB control register + */ +#define AR5K_MIBC		0x0040			/* Register Address */ +#define AR5K_MIBC_COW		0x00000001 +#define AR5K_MIBC_FMC		0x00000002	/* Freeze Mib Counters (?) */ +#define AR5K_MIBC_CMC		0x00000004	/* Clean Mib Counters (?) */ +#define AR5K_MIBC_MCS		0x00000008 + +/* + * Timeout prescale register + */ +#define AR5K_TOPS		0x0044 +#define	AR5K_TOPS_M		0x0000ffff	/* [5211+] (?) */ + +/* + * Receive timeout register (no frame received) + */ +#define AR5K_RXNOFRM		0x0048 +#define	AR5K_RXNOFRM_M		0x000003ff	/* [5211+] (?) */ + +/* + * Transmit timeout register (no frame sent) + */ +#define AR5K_TXNOFRM		0x004c +#define	AR5K_TXNOFRM_M		0x000003ff	/* [5211+] (?) */ +#define	AR5K_TXNOFRM_QCU	0x000ffc00	/* [5211+] (?) */ + +/* + * Receive frame gap timeout register + */ +#define AR5K_RPGTO		0x0050 +#define AR5K_RPGTO_M		0x000003ff	/* [5211+] (?) */ + +/* + * Receive frame count limit register + */ +#define AR5K_RFCNT		0x0054 +#define AR5K_RFCNT_M		0x0000001f	/* [5211+] (?) */ +#define AR5K_RFCNT_RFCL		0x0000000f	/* [5210] */ + +/* + * Misc settings register + */ +#define AR5K_MISC		0x0058			/* Register Address */ +#define	AR5K_MISC_DMA_OBS_M	0x000001e0 +#define	AR5K_MISC_DMA_OBS_S	5 +#define	AR5K_MISC_MISC_OBS_M	0x00000e00 +#define	AR5K_MISC_MISC_OBS_S	9 +#define	AR5K_MISC_MAC_OBS_LSB_M	0x00007000 +#define	AR5K_MISC_MAC_OBS_LSB_S	12 +#define	AR5K_MISC_MAC_OBS_MSB_M	0x00038000 +#define	AR5K_MISC_MAC_OBS_MSB_S	15 +#define AR5K_MISC_LED_DECAY	0x001c0000	/* [5210] */ +#define AR5K_MISC_LED_BLINK	0x00e00000	/* [5210] */ + +/* + * QCU/DCU clock gating register (5311) + */ +#define	AR5K_QCUDCU_CLKGT	0x005c			/* Register Address (?) */ +#define	AR5K_QCUDCU_CLKGT_QCU	0x0000ffff	/* Mask for QCU clock */ +#define	AR5K_QCUDCU_CLKGT_DCU	0x07ff0000	/* Mask for DCU clock */ + +/* + * Interrupt Status Registers + * + * For 5210 there is only one status register but for + * 5211/5212 we have one primary and 4 secondary registers. + * So we have AR5K_ISR for 5210 and AR5K_PISR /SISRx for 5211/5212. + * Most of these bits are common for all chipsets. + */ +#define AR5K_ISR		0x001c			/* Register Address [5210] */ +#define AR5K_PISR		0x0080			/* Register Address [5211+] */ +#define AR5K_ISR_RXOK		0x00000001	/* Frame successfuly recieved */ +#define AR5K_ISR_RXDESC		0x00000002	/* RX descriptor request */ +#define AR5K_ISR_RXERR		0x00000004	/* Receive error */ +#define AR5K_ISR_RXNOFRM	0x00000008	/* No frame received (receive timeout) */ +#define AR5K_ISR_RXEOL		0x00000010	/* Empty RX descriptor */ +#define AR5K_ISR_RXORN		0x00000020	/* Receive FIFO overrun */ +#define AR5K_ISR_TXOK		0x00000040	/* Frame successfuly transmited */ +#define AR5K_ISR_TXDESC		0x00000080	/* TX descriptor request */ +#define AR5K_ISR_TXERR		0x00000100	/* Transmit error */ +#define AR5K_ISR_TXNOFRM	0x00000200	/* No frame transmited (transmit timeout) */ +#define AR5K_ISR_TXEOL		0x00000400	/* Empty TX descriptor */ +#define AR5K_ISR_TXURN		0x00000800	/* Transmit FIFO underrun */ +#define AR5K_ISR_MIB		0x00001000	/* Update MIB counters */ +#define AR5K_ISR_SWI		0x00002000	/* Software interrupt (?) */ +#define AR5K_ISR_RXPHY		0x00004000	/* PHY error */ +#define AR5K_ISR_RXKCM		0x00008000 +#define AR5K_ISR_SWBA		0x00010000	/* Software beacon alert */ +#define AR5K_ISR_BRSSI		0x00020000 +#define AR5K_ISR_BMISS		0x00040000	/* Beacon missed */ +#define AR5K_ISR_HIUERR		0x00080000	/* Host Interface Unit error [5211+] */ +#define AR5K_ISR_BNR		0x00100000 	/* Beacon not ready [5211+] */ +#define AR5K_ISR_MCABT		0x00100000	/* [5210] */ +#define AR5K_ISR_RXCHIRP	0x00200000	/* [5212+] */ +#define AR5K_ISR_SSERR		0x00200000	/* [5210] */ +#define AR5K_ISR_DPERR		0x00400000	/* [5210] */ +#define AR5K_ISR_TIM		0x00800000	/* [5210] */ +#define AR5K_ISR_BCNMISC	0x00800000	/* [5212+] */ +#define AR5K_ISR_GPIO		0x01000000	/* GPIO (rf kill)*/ +#define AR5K_ISR_QCBRORN	0x02000000	/* CBR overrun (?)  [5211+] */ +#define AR5K_ISR_QCBRURN	0x04000000	/* CBR underrun (?) [5211+] */ +#define AR5K_ISR_QTRIG		0x08000000	/* [5211+] */ + +/* + * Secondary status registers [5211+] (0 - 4) + * + * I guess from the names that these give the status for each + * queue, that's why only masks are defined here, haven't got + * any info about them (couldn't find them anywhere in ar5k code). + */ +#define AR5K_SISR0		0x0084			/* Register Address [5211+] */ +#define AR5K_SISR0_QCU_TXOK	0x000003ff	/* Mask for QCU_TXOK */ +#define AR5K_SISR0_QCU_TXDESC	0x03ff0000	/* Mask for QCU_TXDESC */ + +#define AR5K_SISR1		0x0088			/* Register Address [5211+] */ +#define AR5K_SISR1_QCU_TXERR	0x000003ff	/* Mask for QCU_TXERR */ +#define AR5K_SISR1_QCU_TXEOL	0x03ff0000	/* Mask for QCU_TXEOL */ + +#define AR5K_SISR2		0x008c			/* Register Address [5211+] */ +#define AR5K_SISR2_QCU_TXURN	0x000003ff	/* Mask for QCU_TXURN */ +#define	AR5K_SISR2_MCABT	0x00100000 +#define	AR5K_SISR2_SSERR	0x00200000 +#define	AR5K_SISR2_DPERR	0x00400000 +#define	AR5K_SISR2_TIM		0x01000000	/* [5212+] */ +#define	AR5K_SISR2_CAB_END	0x02000000	/* [5212+] */ +#define	AR5K_SISR2_DTIM_SYNC	0x04000000	/* [5212+] */ +#define	AR5K_SISR2_BCN_TIMEOUT	0x08000000	/* [5212+] */ +#define	AR5K_SISR2_CAB_TIMEOUT	0x10000000	/* [5212+] */ +#define	AR5K_SISR2_DTIM		0x20000000	/* [5212+] */ + +#define AR5K_SISR3		0x0090			/* Register Address [5211+] */ +#define AR5K_SISR3_QCBRORN	0x000003ff	/* Mask for QCBRORN */ +#define AR5K_SISR3_QCBRURN	0x03ff0000	/* Mask for QCBRURN */ + +#define AR5K_SISR4		0x0094			/* Register Address [5211+] */ +#define AR5K_SISR4_QTRIG	0x000003ff	/* Mask for QTRIG */ + +/* + * Shadow read-and-clear interrupt status registers [5211+] + */ +#define AR5K_RAC_PISR		0x00c0		/* Read and clear PISR */ +#define AR5K_RAC_SISR0		0x00c4		/* Read and clear SISR0 */ +#define AR5K_RAC_SISR1		0x00c8		/* Read and clear SISR1 */ +#define AR5K_RAC_SISR2		0x00cc		/* Read and clear SISR2 */ +#define AR5K_RAC_SISR3		0x00d0		/* Read and clear SISR3 */ +#define AR5K_RAC_SISR4		0x00d4		/* Read and clear SISR4 */ + +/* + * Interrupt Mask Registers + * + * As whith ISRs 5210 has one IMR (AR5K_IMR) and 5211/5212 has one primary + * (AR5K_PIMR) and 4 secondary IMRs (AR5K_SIMRx). Note that ISR/IMR flags match. + */ +#define	AR5K_IMR		0x0020			/* Register Address [5210] */ +#define AR5K_PIMR		0x00a0			/* Register Address [5211+] */ +#define AR5K_IMR_RXOK		0x00000001	/* Frame successfuly recieved*/ +#define AR5K_IMR_RXDESC		0x00000002	/* RX descriptor request*/ +#define AR5K_IMR_RXERR		0x00000004	/* Receive error*/ +#define AR5K_IMR_RXNOFRM	0x00000008	/* No frame received (receive timeout)*/ +#define AR5K_IMR_RXEOL		0x00000010	/* Empty RX descriptor*/ +#define AR5K_IMR_RXORN		0x00000020	/* Receive FIFO overrun*/ +#define AR5K_IMR_TXOK		0x00000040	/* Frame successfuly transmited*/ +#define AR5K_IMR_TXDESC		0x00000080	/* TX descriptor request*/ +#define AR5K_IMR_TXERR		0x00000100	/* Transmit error*/ +#define AR5K_IMR_TXNOFRM	0x00000200	/* No frame transmited (transmit timeout)*/ +#define AR5K_IMR_TXEOL		0x00000400	/* Empty TX descriptor*/ +#define AR5K_IMR_TXURN		0x00000800	/* Transmit FIFO underrun*/ +#define AR5K_IMR_MIB		0x00001000	/* Update MIB counters*/ +#define AR5K_IMR_SWI		0x00002000 +#define AR5K_IMR_RXPHY		0x00004000	/* PHY error*/ +#define AR5K_IMR_RXKCM		0x00008000 +#define AR5K_IMR_SWBA		0x00010000	/* Software beacon alert*/ +#define AR5K_IMR_BRSSI		0x00020000 +#define AR5K_IMR_BMISS		0x00040000	/* Beacon missed*/ +#define AR5K_IMR_HIUERR		0x00080000	/* Host Interface Unit error [5211+] */ +#define AR5K_IMR_BNR		0x00100000 	/* Beacon not ready [5211+] */ +#define AR5K_IMR_MCABT		0x00100000	/* [5210] */ +#define AR5K_IMR_RXCHIRP	0x00200000	/* [5212+]*/ +#define AR5K_IMR_SSERR		0x00200000	/* [5210] */ +#define AR5K_IMR_DPERR		0x00400000	/* [5210] */ +#define AR5K_IMR_TIM		0x00800000	/* [5211+] */ +#define AR5K_IMR_BCNMISC	0x00800000	/* [5212+] */ +#define AR5K_IMR_GPIO		0x01000000	/* GPIO (rf kill)*/ +#define AR5K_IMR_QCBRORN	0x02000000	/* CBR overrun (?) [5211+] */ +#define AR5K_IMR_QCBRURN	0x04000000	/* CBR underrun (?) [5211+] */ +#define AR5K_IMR_QTRIG		0x08000000	/* [5211+] */ + +/* + * Secondary interrupt mask registers [5211+] (0 - 4) + */ +#define AR5K_SIMR0		0x00a4			/* Register Address [5211+] */ +#define AR5K_SIMR0_QCU_TXOK	0x000003ff	/* Mask for QCU_TXOK */ +#define AR5K_SIMR0_QCU_TXOK_S	0 +#define AR5K_SIMR0_QCU_TXDESC	0x03ff0000	/* Mask for QCU_TXDESC */ +#define AR5K_SIMR0_QCU_TXDESC_S	16 + +#define AR5K_SIMR1		0x00a8			/* Register Address [5211+] */ +#define AR5K_SIMR1_QCU_TXERR	0x000003ff	/* Mask for QCU_TXERR */ +#define AR5K_SIMR1_QCU_TXERR_S	0 +#define AR5K_SIMR1_QCU_TXEOL	0x03ff0000	/* Mask for QCU_TXEOL */ +#define AR5K_SIMR1_QCU_TXEOL_S	16 + +#define AR5K_SIMR2		0x00ac			/* Register Address [5211+] */ +#define AR5K_SIMR2_QCU_TXURN	0x000003ff	/* Mask for QCU_TXURN */ +#define AR5K_SIMR2_QCU_TXURN_S	0 +#define	AR5K_SIMR2_MCABT	0x00100000 +#define	AR5K_SIMR2_SSERR	0x00200000 +#define	AR5K_SIMR2_DPERR	0x00400000 +#define	AR5K_SIMR2_TIM		0x01000000	/* [5212+] */ +#define	AR5K_SIMR2_CAB_END	0x02000000	/* [5212+] */ +#define	AR5K_SIMR2_DTIM_SYNC	0x04000000	/* [5212+] */ +#define	AR5K_SIMR2_BCN_TIMEOUT	0x08000000	/* [5212+] */ +#define	AR5K_SIMR2_CAB_TIMEOUT	0x10000000	/* [5212+] */ +#define	AR5K_SIMR2_DTIM		0x20000000	/* [5212+] */ + +#define AR5K_SIMR3		0x00b0			/* Register Address [5211+] */ +#define AR5K_SIMR3_QCBRORN	0x000003ff	/* Mask for QCBRORN */ +#define AR5K_SIMR3_QCBRORN_S	0 +#define AR5K_SIMR3_QCBRURN	0x03ff0000	/* Mask for QCBRURN */ +#define AR5K_SIMR3_QCBRURN_S	16 + +#define AR5K_SIMR4		0x00b4			/* Register Address [5211+] */ +#define AR5K_SIMR4_QTRIG	0x000003ff	/* Mask for QTRIG */ +#define AR5K_SIMR4_QTRIG_S	0 + + +/* + * Decompression mask registers [5212+] + */ +#define AR5K_DCM_ADDR		0x0400		/*Decompression mask address (?)*/ +#define AR5K_DCM_DATA		0x0404		/*Decompression mask data (?)*/ + +/* + * Decompression configuration registers [5212+] + */ +#define AR5K_DCCFG		0x0420 + +/* + * Compression configuration registers [5212+] + */ +#define AR5K_CCFG		0x0600 +#define AR5K_CCFG_CUP		0x0604 + +/* + * Compression performance counter registers [5212+] + */ +#define AR5K_CPC0		0x0610		/* Compression performance counter 0 */ +#define AR5K_CPC1		0x0614		/* Compression performance counter 1*/ +#define AR5K_CPC2		0x0618		/* Compression performance counter 2 */ +#define AR5K_CPC3		0x061c		/* Compression performance counter 3 */ +#define AR5K_CPCORN		0x0620		/* Compression performance overrun (?) */ + + +/* + * Queue control unit (QCU) registers [5211+] + * + * Card has 12 TX Queues but i see that only 0-9 are used (?) + * both in binary HAL (see ah.h) and ar5k. Each queue has it's own + * TXDP at addresses 0x0800 - 0x082c, a CBR (Constant Bit Rate) + * configuration register (0x08c0 - 0x08ec), a ready time configuration + * register (0x0900 - 0x092c), a misc configuration register (0x09c0 - + * 0x09ec) and a status register (0x0a00 - 0x0a2c). We also have some + * global registers, QCU transmit enable/disable and "one shot arm (?)" + * set/clear, which contain status for all queues (we shift by 1 for each + * queue). To access these registers easily we define some macros here + * that are used inside HAL. For more infos check out *_tx_queue functs. + * + * TODO: Boundary checking on macros (here?) + */ + +/* + * Generic QCU Register access macros + */ +#define	AR5K_QUEUE_REG(_r, _q)		(((_q) << 2) + _r) +#define AR5K_QCU_GLOBAL_READ(_r, _q)	(AR5K_REG_READ(_r) & (1 << _q)) +#define AR5K_QCU_GLOBAL_WRITE(_r, _q)	AR5K_REG_WRITE(_r, (1 << _q)) + +/* + * QCU Transmit descriptor pointer registers + */ +#define AR5K_QCU_TXDP_BASE	0x0800		/* Register Address - Queue0 TXDP */ +#define AR5K_QUEUE_TXDP(_q)	AR5K_QUEUE_REG(AR5K_QCU_TXDP_BASE, _q) + +/* + * QCU Transmit enable register + */ +#define AR5K_QCU_TXE		0x0840 +#define AR5K_ENABLE_QUEUE(_q)	AR5K_QCU_GLOBAL_WRITE(AR5K_QCU_TXE, _q) +#define AR5K_QUEUE_ENABLED(_q)	AR5K_QCU_GLOBAL_READ(AR5K_QCU_TXE, _q) + +/* + * QCU Transmit disable register + */ +#define AR5K_QCU_TXD		0x0880 +#define AR5K_DISABLE_QUEUE(_q)	AR5K_QCU_GLOBAL_WRITE(AR5K_QCU_TXD, _q) +#define AR5K_QUEUE_DISABLED(_q)	AR5K_QCU_GLOBAL_READ(AR5K_QCU_TXD, _q) + +/* + * QCU Constant Bit Rate configuration registers + */ +#define	AR5K_QCU_CBRCFG_BASE		0x08c0	/* Register Address - Queue0 CBRCFG */ +#define	AR5K_QCU_CBRCFG_INTVAL		0x00ffffff	/* CBR Interval mask */ +#define AR5K_QCU_CBRCFG_INTVAL_S	0 +#define	AR5K_QCU_CBRCFG_ORN_THRES	0xff000000	/* CBR overrun threshold mask */ +#define AR5K_QCU_CBRCFG_ORN_THRES_S	24 +#define	AR5K_QUEUE_CBRCFG(_q)		AR5K_QUEUE_REG(AR5K_QCU_CBRCFG_BASE, _q) + +/* + * QCU Ready time configuration registers + */ +#define	AR5K_QCU_RDYTIMECFG_BASE	0x0900	/* Register Address - Queue0 RDYTIMECFG */ +#define	AR5K_QCU_RDYTIMECFG_INTVAL	0x00ffffff	/* Ready time interval mask */ +#define AR5K_QCU_RDYTIMECFG_INTVAL_S	0 +#define	AR5K_QCU_RDYTIMECFG_DURATION	0x00ffffff	/* Ready time duration mask */ +#define	AR5K_QCU_RDYTIMECFG_ENABLE	0x01000000	/* Ready time enable mask */ +#define AR5K_QUEUE_RDYTIMECFG(_q)	AR5K_QUEUE_REG(AR5K_QCU_RDYTIMECFG_BASE, _q) + +/* + * QCU one shot arm set registers + */ +#define	AR5K_QCU_ONESHOTARM_SET		0x0940	/* Register Address -QCU "one shot arm set (?)" */ +#define	AR5K_QCU_ONESHOTARM_SET_M	0x0000ffff + +/* + * QCU one shot arm clear registers + */ +#define	AR5K_QCU_ONESHOTARM_CLEAR	0x0980	/* Register Address -QCU "one shot arm clear (?)" */ +#define	AR5K_QCU_ONESHOTARM_CLEAR_M	0x0000ffff + +/* + * QCU misc registers + */ +#define AR5K_QCU_MISC_BASE		0x09c0			/* Register Address -Queue0 MISC */ +#define	AR5K_QCU_MISC_FRSHED_M		0x0000000f	/* Frame sheduling mask */ +#define	AR5K_QCU_MISC_FRSHED_ASAP	0		/* ASAP */ +#define	AR5K_QCU_MISC_FRSHED_CBR	1		/* Constant Bit Rate */ +#define	AR5K_QCU_MISC_FRSHED_DBA_GT	2		/* DMA Beacon alert gated (?) */ +#define	AR5K_QCU_MISC_FRSHED_TIM_GT	3		/* Time gated (?) */ +#define	AR5K_QCU_MISC_FRSHED_BCN_SENT_GT	4	/* Beacon sent gated (?) */ +#define	AR5K_QCU_MISC_ONESHOT_ENABLE	0x00000010	/* Oneshot enable */ +#define	AR5K_QCU_MISC_CBREXP		0x00000020	/* CBR expired (normal queue) */ +#define	AR5K_QCU_MISC_CBREXP_BCN	0x00000040	/* CBR expired (beacon queue) */ +#define	AR5K_QCU_MISC_BCN_ENABLE	0x00000080	/* Beacons enabled */ +#define	AR5K_QCU_MISC_CBR_THRES_ENABLE	0x00000100	/* CBR threshold enabled (?) */ +#define	AR5K_QCU_MISC_TXE		0x00000200	/* TXE reset when RDYTIME enalbed (?) */ +#define	AR5K_QCU_MISC_CBR		0x00000400	/* CBR threshold reset (?) */ +#define	AR5K_QCU_MISC_DCU_EARLY		0x00000800	/* DCU reset (?) */ +#define AR5K_QUEUE_MISC(_q)		AR5K_QUEUE_REG(AR5K_QCU_MISC_BASE, _q) + + +/* + * QCU status registers + */ +#define AR5K_QCU_STS_BASE	0x0a00			/* Register Address - Queue0 STS */ +#define	AR5K_QCU_STS_FRMPENDCNT	0x00000003	/* Frames pending counter */ +#define	AR5K_QCU_STS_CBREXPCNT	0x0000ff00	/* CBR expired counter (?) */ +#define	AR5K_QUEUE_STATUS(_q)	AR5K_QUEUE_REG(AR5K_QCU_STS_BASE, _q) + +/* + * QCU ready time shutdown register + */ +#define AR5K_QCU_RDYTIMESHDN	0x0a40 +#define AR5K_QCU_RDYTIMESHDN_M	0x000003ff + +/* + * QCU compression buffer base registers [5212+] + */ +#define AR5K_QCU_CBB_SELECT	0x0b00 +#define AR5K_QCU_CBB_ADDR	0x0b04 + +/* + * QCU compression buffer configuration register [5212+] + */ +#define AR5K_QCU_CBCFG		0x0b08 + + + +/* + * Distributed Coordination Function (DCF) control unit (DCU) + * registers [5211+] + * + * These registers control the various characteristics of each queue + * for 802.11e (WME) combatibility so they go together with + * QCU registers in pairs. For each queue we have a QCU mask register, + * (0x1000 - 0x102c), a local-IFS settings register (0x1040 - 0x106c), + * a retry limit register (0x1080 - 0x10ac), a channel time register + * (0x10c0 - 0x10ec), a misc-settings register (0x1100 - 0x112c) and + * a sequence number register (0x1140 - 0x116c). It seems that "global" + * registers here afect all queues (see use of DCU_GBL_IFS_SLOT in ar5k). + * We use the same macros here for easier register access. + * + */ + +/* + * DCU QCU mask registers + */ +#define AR5K_DCU_QCUMASK_BASE	0x1000		/* Register Address -Queue0 DCU_QCUMASK */ +#define AR5K_DCU_QCUMASK_M	0x000003ff +#define AR5K_QUEUE_QCUMASK(_q)	AR5K_QUEUE_REG(AR5K_DCU_QCUMASK_BASE, _q) + +/* + * DCU local Inter Frame Space settings register + */ +#define AR5K_DCU_LCL_IFS_BASE		0x1040			/* Register Address -Queue0 DCU_LCL_IFS */ +#define	AR5K_DCU_LCL_IFS_CW_MIN	        0x000003ff	/* Minimum Contention Window */ +#define	AR5K_DCU_LCL_IFS_CW_MIN_S	0 +#define	AR5K_DCU_LCL_IFS_CW_MAX	        0x000ffc00	/* Maximum Contention Window */ +#define	AR5K_DCU_LCL_IFS_CW_MAX_S	10 +#define	AR5K_DCU_LCL_IFS_AIFS		0x0ff00000	/* Arbitrated Interframe Space */ +#define	AR5K_DCU_LCL_IFS_AIFS_S		20 +#define	AR5K_QUEUE_DFS_LOCAL_IFS(_q)	AR5K_QUEUE_REG(AR5K_DCU_LCL_IFS_BASE, _q) + +/* + * DCU retry limit registers + */ +#define AR5K_DCU_RETRY_LMT_BASE		0x1080			/* Register Address -Queue0 DCU_RETRY_LMT */ +#define AR5K_DCU_RETRY_LMT_SH_RETRY	0x0000000f	/* Short retry limit mask */ +#define AR5K_DCU_RETRY_LMT_SH_RETRY_S	0 +#define AR5K_DCU_RETRY_LMT_LG_RETRY	0x000000f0	/* Long retry limit mask */ +#define AR5K_DCU_RETRY_LMT_LG_RETRY_S	4 +#define AR5K_DCU_RETRY_LMT_SSH_RETRY	0x00003f00	/* Station short retry limit mask (?) */ +#define AR5K_DCU_RETRY_LMT_SSH_RETRY_S	8 +#define AR5K_DCU_RETRY_LMT_SLG_RETRY	0x000fc000	/* Station long retry limit mask (?) */ +#define AR5K_DCU_RETRY_LMT_SLG_RETRY_S	14 +#define	AR5K_QUEUE_DFS_RETRY_LIMIT(_q)	AR5K_QUEUE_REG(AR5K_DCU_RETRY_LMT_BASE, _q) + +/* + * DCU channel time registers + */ +#define AR5K_DCU_CHAN_TIME_BASE		0x10c0			/* Register Address -Queue0 DCU_CHAN_TIME */ +#define	AR5K_DCU_CHAN_TIME_DUR		0x000fffff	/* Channel time duration */ +#define	AR5K_DCU_CHAN_TIME_DUR_S	0 +#define	AR5K_DCU_CHAN_TIME_ENABLE	0x00100000	/* Enable channel time */ +#define AR5K_QUEUE_DFS_CHANNEL_TIME(_q)	AR5K_QUEUE_REG(AR5K_DCU_CHAN_TIME_BASE, _q) + +/* + * DCU misc registers [5211+] + * + * For some of the registers i couldn't find in the code + * (only backoff stuff is there realy) i tried to match the + * names with 802.11e parameters etc, so i guess VIRTCOL here + * means Virtual Collision and HCFPOLL means Hybrid Coordination + * factor Poll (CF- Poll). Arbiter lockout control controls the + * behaviour on low priority queues when we have multiple queues + * with pending frames. Intra-frame lockout means we wait until + * the queue's current frame transmits (with post frame backoff and bursting) + * before we transmit anything else and global lockout means we + * wait for the whole queue to finish before higher priority queues + * can transmit (this is used on beacon and CAB queues). + * No lockout means there is no special handling. + */ +#define AR5K_DCU_MISC_BASE		0x1100			/* Register Address -Queue0 DCU_MISC */ +#define	AR5K_DCU_MISC_BACKOFF		0x000007ff	/* Mask for backoff setting (?) */ +#define AR5K_DCU_MISC_BACKOFF_FRAG	0x00000200	/* Enable backoff while bursting */ +#define	AR5K_DCU_MISC_HCFPOLL_ENABLE	0x00000800	/* CF - Poll (?) */ +#define	AR5K_DCU_MISC_BACKOFF_PERSIST	0x00001000	/* Persistent backoff (?) */ +#define	AR5K_DCU_MISC_FRMPRFTCH_ENABLE	0x00002000	/* Enable frame pre-fetch (?) */ +#define	AR5K_DCU_MISC_VIRTCOL		0x0000c000	/* Mask for Virtual Collision (?) */ +#define	AR5K_DCU_MISC_VIRTCOL_NORMAL	0 +#define	AR5K_DCU_MISC_VIRTCOL_MODIFIED	1 +#define	AR5K_DCU_MISC_VIRTCOL_IGNORE	2 +#define	AR5K_DCU_MISC_BCN_ENABLE	0x00010000	/* Beacon enable (?) */ +#define	AR5K_DCU_MISC_ARBLOCK_CTL	0x00060000	/* Arbiter lockout control mask */ +#define	AR5K_DCU_MISC_ARBLOCK_CTL_S	17 +#define	AR5K_DCU_MISC_ARBLOCK_CTL_NONE	0		/* No arbiter lockout */ +#define	AR5K_DCU_MISC_ARBLOCK_CTL_INTFRM	1	/* Intra-frame lockout */ +#define	AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL	2	/* Global lockout */ +#define	AR5K_DCU_MISC_ARBLOCK_IGNORE	0x00080000 +#define	AR5K_DCU_MISC_SEQ_NUM_INCR_DIS	0x00100000	/* Disable sequence number increment (?) */ +#define	AR5K_DCU_MISC_POST_FR_BKOFF_DIS	0x00200000	/* Disable post-frame backoff (?) */ +#define	AR5K_DCU_MISC_VIRT_COLL_POLICY	0x00400000	/* Virtual Collision policy (?) */ +#define	AR5K_DCU_MISC_BLOWN_IFS_POLICY	0x00800000 +#define	AR5K_DCU_MISC_SEQNUM_CTL	0x01000000	/* Sequence number control (?) */ +#define AR5K_QUEUE_DFS_MISC(_q)		AR5K_QUEUE_REG(AR5K_DCU_MISC_BASE, _q) + +/* + * DCU frame sequence number registers + */ +#define AR5K_DCU_SEQNUM_BASE	0x1140 +#define	AR5K_DCU_SEQNUM_M	0x00000fff +#define	AR5K_QUEUE_DFS_SEQNUM(_q)	AR5K_QUEUE_REG(AR5K_DCU_SEQNUM_BASE, _q) + +/* + * DCU global IFS SIFS registers + */ +#define AR5K_DCU_GBL_IFS_SIFS	0x1030 +#define AR5K_DCU_GBL_IFS_SIFS_M	0x0000ffff + +/* + * DCU global IFS slot interval registers + */ +#define AR5K_DCU_GBL_IFS_SLOT	0x1070 +#define AR5K_DCU_GBL_IFS_SLOT_M	0x0000ffff + +/* + * DCU global IFS EIFS registers + */ +#define AR5K_DCU_GBL_IFS_EIFS	0x10b0 +#define AR5K_DCU_GBL_IFS_EIFS_M	0x0000ffff + +/* + * DCU global IFS misc registers + */ +#define AR5K_DCU_GBL_IFS_MISC			0x10f0			/* Register Address */ +#define	AR5K_DCU_GBL_IFS_MISC_LFSR_SLICE	0x00000007 +#define	AR5K_DCU_GBL_IFS_MISC_TURBO_MODE	0x00000008	/* Turbo mode (?) */ +#define	AR5K_DCU_GBL_IFS_MISC_SIFS_DUR_USEC	0x000003f0	/* SIFS Duration mask (?) */ +#define	AR5K_DCU_GBL_IFS_MISC_USEC_DUR		0x000ffc00 +#define	AR5K_DCU_GBL_IFS_MISC_DCU_ARB_DELAY	0x00300000 + +/* + * DCU frame prefetch control register + */ +#define AR5K_DCU_FP		0x1230 + +/* + * DCU transmit pause control/status register + */ +#define AR5K_DCU_TXP		0x1270			/* Register Address */ +#define	AR5K_DCU_TXP_M		0x000003ff	/* Tx pause mask (?) */ +#define	AR5K_DCU_TXP_STATUS	0x00010000	/* Tx pause status (?) */ + +/* + * DCU transmit filter register + */ +#define AR5K_DCU_TX_FILTER	0x1038 + +/* + * DCU clear transmit filter register + */ +#define AR5K_DCU_TX_FILTER_CLR	0x143c + +/* + * DCU set transmit filter register + */ +#define AR5K_DCU_TX_FILTER_SET	0x147c + +/* + * Reset control register + * + * 4 and 8 are not used in 5211/5212 and + * 2 means "baseband reset" on 5211/5212. + */ +#define AR5K_RESET_CTL		0x4000			/* Register Address */ +#define AR5K_RESET_CTL_PCU	0x00000001	/* Protocol Control Unit reset */ +#define AR5K_RESET_CTL_DMA	0x00000002	/* DMA (Rx/Tx) reset [5210] */ +#define	AR5K_RESET_CTL_BASEBAND	0x00000002	/* Baseband reset [5211+] */ +#define AR5K_RESET_CTL_MAC	0x00000004	/* MAC reset (PCU+Baseband ?) [5210] */ +#define AR5K_RESET_CTL_PHY	0x00000008	/* PHY reset [5210] */ +#define AR5K_RESET_CTL_PCI	0x00000010	/* PCI Core reset (interrupts etc) */ +#define AR5K_RESET_CTL_CHIP	(AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_DMA |	\ +				AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_PHY) + +/* + * Sleep control register + */ +#define AR5K_SLEEP_CTL			0x4004			/* Register Address */ +#define AR5K_SLEEP_CTL_SLDUR		0x0000ffff	/* Sleep duration mask */ +#define AR5K_SLEEP_CTL_SLDUR_S		0 +#define AR5K_SLEEP_CTL_SLE		0x00030000	/* Sleep enable mask */ +#define AR5K_SLEEP_CTL_SLE_S		16 +#define AR5K_SLEEP_CTL_SLE_WAKE		0x00000000	/* Force chip awake */ +#define AR5K_SLEEP_CTL_SLE_SLP		0x00010000	/* Force chip sleep */ +#define AR5K_SLEEP_CTL_SLE_ALLOW	0x00020000 +#define AR5K_SLEEP_CTL_SLE_UNITS	0x00000008	/* [5211+] */ + +/* + * Interrupt pending register + */ +#define AR5K_INTPEND	0x4008 +#define AR5K_INTPEND_M	0x00000001 + +/* + * Sleep force register + */ +#define AR5K_SFR	0x400c +#define AR5K_SFR_M	0x00000001 + +/* + * PCI configuration register + */ +#define AR5K_PCICFG			0x4010			/* Register Address */ +#define AR5K_PCICFG_EEAE		0x00000001	/* Eeprom access enable [5210] */ +#define AR5K_PCICFG_CLKRUNEN		0x00000004	/* CLKRUN enable [5211+] */ +#define AR5K_PCICFG_EESIZE		0x00000018	/* Mask for EEPROM size [5211+] */ +#define AR5K_PCICFG_EESIZE_S		3 +#define AR5K_PCICFG_EESIZE_4K		0		/* 4K */ +#define AR5K_PCICFG_EESIZE_8K		1		/* 8K */ +#define AR5K_PCICFG_EESIZE_16K		2		/* 16K */ +#define AR5K_PCICFG_EESIZE_FAIL		3		/* Failed to get size (?) [5211+] */ +#define AR5K_PCICFG_LED			0x00000060	/* Led status [5211+] */ +#define AR5K_PCICFG_LED_NONE		0x00000000	/* Default [5211+] */ +#define AR5K_PCICFG_LED_PEND		0x00000020	/* Scan / Auth pending */ +#define AR5K_PCICFG_LED_ASSOC		0x00000040	/* Associated */ +#define	AR5K_PCICFG_BUS_SEL		0x00000380	/* Mask for "bus select" [5211+] (?) */ +#define	AR5K_PCICFG_CBEFIX_DIS		0x00000400	/* Disable CBE fix (?) */ +#define AR5K_PCICFG_SL_INTEN		0x00000800	/* Enable interrupts when asleep (?) */ +#define AR5K_PCICFG_LED_BCTL		0x00001000	/* Led blink (?) [5210] */ +#define AR5K_PCICFG_SL_INPEN		0x00002800	/* Sleep even whith pending interrupts (?) */ +#define AR5K_PCICFG_SPWR_DN		0x00010000	/* Mask for power status */ +#define AR5K_PCICFG_LEDMODE		0x000e0000	/* Ledmode [5211+] */ +#define AR5K_PCICFG_LEDMODE_PROP	0x00000000	/* Blink on standard traffic [5211+] */ +#define AR5K_PCICFG_LEDMODE_PROM	0x00020000	/* Default mode (blink on any traffic) [5211+] */ +#define AR5K_PCICFG_LEDMODE_PWR		0x00040000	/* Some other blinking mode  (?) [5211+] */ +#define AR5K_PCICFG_LEDMODE_RAND	0x00060000	/* Random blinking (?) [5211+] */ +#define AR5K_PCICFG_LEDBLINK		0x00700000 +#define AR5K_PCICFG_LEDBLINK_S		20 +#define AR5K_PCICFG_LEDSLOW		0x00800000	/* Slow led blink rate (?) [5211+] */ +#define AR5K_PCICFG_LEDSTATE				\ +	(AR5K_PCICFG_LED | AR5K_PCICFG_LEDMODE |	\ +	AR5K_PCICFG_LEDBLINK | AR5K_PCICFG_LEDSLOW) + +/* + * "General Purpose Input/Output" (GPIO) control register + * + * I'm not sure about this but after looking at the code + * for all chipsets here is what i got. + * + * We have 6 GPIOs (pins), each GPIO has 4 modes (2 bits) + * Mode 0 -> always input + * Mode 1 -> output when GPIODO for this GPIO is set to 0 + * Mode 2 -> output when GPIODO for this GPIO is set to 1 + * Mode 3 -> always output + * + * For more infos check out get_gpio/set_gpio and + * set_gpio_input/set_gpio_output functs. + * For more infos on gpio interrupt check out set_gpio_intr. + */ +#define AR5K_NUM_GPIO	6 + +#define AR5K_GPIOCR		0x4014				/* Register Address */ +#define AR5K_GPIOCR_INT_ENA	0x00008000		/* Enable GPIO interrupt */ +#define AR5K_GPIOCR_INT_SELL	0x00000000		/* Generate interrupt when pin is off (?) */ +#define AR5K_GPIOCR_INT_SELH	0x00010000		/* Generate interrupt when pin is on */ +#define AR5K_GPIOCR_IN(n)	(0 << ((n) * 2))	/* Mode 0 for pin n */ +#define AR5K_GPIOCR_OUT0(n)	(1 << ((n) * 2))	/* Mode 1 for pin n */ +#define AR5K_GPIOCR_OUT1(n)	(2 << ((n) * 2))	/* Mode 2 for pin n */ +#define AR5K_GPIOCR_OUT(n)	(3 << ((n) * 2))	/* Mode 3 for pin n */ +#define AR5K_GPIOCR_INT_SEL(n)	((n) << 12)		/* Interrupt for GPIO pin n */ + +/* + * "General Purpose Input/Output" (GPIO) data output register + */ +#define AR5K_GPIODO	0x4018 + +/* + * "General Purpose Input/Output" (GPIO) data input register + */ +#define AR5K_GPIODI	0x401c +#define AR5K_GPIODI_M	0x0000002f + + +/* + * Silicon revision register + */ +#define AR5K_SREV		0x4020			/* Register Address */ +#define AR5K_SREV_REV		0x0000000f	/* Mask for revision */ +#define AR5K_SREV_REV_S		0 +#define AR5K_SREV_VER		0x000000ff	/* Mask for version */ +#define AR5K_SREV_VER_S		4 + + + +/*====EEPROM REGISTERS====*/ + +/* + * EEPROM access registers + * + * Here we got a difference between 5210/5211-12 + * read data register for 5210 is at 0x6800 and + * status register is at 0x6c00. There is also + * no eeprom command register on 5210 and the + * offsets are different. + * + * To read eeprom data for a specific offset: + * 5210 - enable eeprom access (AR5K_PCICFG_EEAE) + *        read AR5K_EEPROM_BASE +(4 * offset) + *        check the eeprom status register + *        and read eeprom data register. + * + * 5211 - write offset to AR5K_EEPROM_BASE + * 5212   write AR5K_EEPROM_CMD_READ on AR5K_EEPROM_CMD + *        check the eeprom status register + *        and read eeprom data register. + * + * To write eeprom data for a specific offset: + * 5210 - enable eeprom access (AR5K_PCICFG_EEAE) + *        write data to AR5K_EEPROM_BASE +(4 * offset) + *        check the eeprom status register + * 5211 - write AR5K_EEPROM_CMD_RESET on AR5K_EEPROM_CMD + * 5212   write offset to AR5K_EEPROM_BASE + *        write data to data register + *	  write AR5K_EEPROM_CMD_WRITE on AR5K_EEPROM_CMD + *        check the eeprom status register + * + * For more infos check eeprom_* functs and the ar5k.c + * file posted in madwifi-devel mailing list. + * http://sourceforge.net/mailarchive/message.php?msg_id=8966525 + * + */ +#define AR5K_EEPROM_BASE	0x6000 + +/* + * Common ar5xxx EEPROM data offsets (set these on AR5K_EEPROM_BASE) + */ +#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_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 +#define AR5K_EEPROM_REG_DOMAIN		0x00bf	/* EEPROM regdom */ +#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 +#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 + +#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_T_5GHZ_DIS(_v)	(((_v) >> 15) & 0x1)	/* Disable turbo for 5Ghz (?) */ +#define AR5K_EEPROM_HDR_RFKILL(_v)	(((_v) >> 14) & 0x1)	/* Device has RFKill support */ + +#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 + +/* 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)	((int8_t)(((_v) >> 8) & 0xff)) +#define AR5K_EEPROM_ANT_GAIN_2GHZ(_v)	((int8_t)((_v) & 0xff)) + +/* 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 */ + +/* [3.1 - 3.3] */ +#define AR5K_EEPROM_OBDB0_2GHZ		0x00ec +#define AR5K_EEPROM_OBDB1_2GHZ		0x00ed + +/* Misc values available since EEPROM 4.0 */ +#define AR5K_EEPROM_MISC0		0x00c4 +#define AR5K_EEPROM_EARSTART(_v)	((_v) & 0xfff) +#define AR5K_EEPROM_EEMAP(_v)		(((_v) >> 14) & 0x3) +#define AR5K_EEPROM_MISC1		0x00c5 +#define AR5K_EEPROM_TARGET_PWRSTART(_v)	((_v) & 0xfff) +#define AR5K_EEPROM_HAS32KHZCRYSTAL(_v)	(((_v) >> 14) & 0x1) + +/* + * EEPROM data register + */ +#define AR5K_EEPROM_DATA_5211	0x6004 +#define AR5K_EEPROM_DATA_5210	0x6800 +#define	AR5K_EEPROM_DATA	(ah->ah_version == AR5K_AR5210 ? \ +				AR5K_EEPROM_DATA_5210 : AR5K_EEPROM_DATA_5211) + +/* + * EEPROM command register + */ +#define AR5K_EEPROM_CMD		0x6008			/* Register Addres */ +#define AR5K_EEPROM_CMD_READ	0x00000001	/* EEPROM read */ +#define AR5K_EEPROM_CMD_WRITE	0x00000002	/* EEPROM write */ +#define AR5K_EEPROM_CMD_RESET	0x00000004	/* EEPROM reset */ + +/* + * EEPROM status register + */ +#define AR5K_EEPROM_STAT_5210	0x6c00			/* Register Address [5210] */ +#define AR5K_EEPROM_STAT_5211	0x600c			/* Register Address [5211+] */ +#define	AR5K_EEPROM_STATUS	(ah->ah_version == AR5K_AR5210 ? \ +				AR5K_EEPROM_STAT_5210 : AR5K_EEPROM_STAT_5211) +#define AR5K_EEPROM_STAT_RDERR	0x00000001	/* EEPROM read failed */ +#define AR5K_EEPROM_STAT_RDDONE	0x00000002	/* EEPROM read successful */ +#define AR5K_EEPROM_STAT_WRERR	0x00000004	/* EEPROM write failed */ +#define AR5K_EEPROM_STAT_WRDONE	0x00000008	/* EEPROM write successful */ + +/* + * EEPROM config register (?) + */ +#define AR5K_EEPROM_CFG	0x6010 + + + +/* + * Protocol Control Unit (PCU) registers + */ +/* + * Used for checking initial register writes + * during channel reset (see reset func) + */ +#define AR5K_PCU_MIN	0x8000 +#define AR5K_PCU_MAX	0x8fff + +/* + * First station id register (MAC address in lower 32 bits) + */ +#define AR5K_STA_ID0	0x8000 + +/* + * Second station id register (MAC address in upper 16 bits) + */ +#define AR5K_STA_ID1			0x8004			/* Register Address */ +#define AR5K_STA_ID1_AP			0x00010000	/* Set AP mode */ +#define AR5K_STA_ID1_ADHOC		0x00020000	/* Set Ad-Hoc mode */ +#define AR5K_STA_ID1_PWR_SV		0x00040000	/* Power save reporting (?) */ +#define AR5K_STA_ID1_NO_KEYSRCH		0x00080000	/* No key search */ +#define AR5K_STA_ID1_NO_PSPOLL		0x00100000	/* No power save polling [5210] */ +#define AR5K_STA_ID1_PCF_5211		0x00100000	/* Enable PCF on [5211+] */ +#define AR5K_STA_ID1_PCF_5210		0x00200000	/* Enable PCF on [5210]*/ +#define	AR5K_STA_ID1_PCF		(ah->ah_version == AR5K_AR5210 ? \ +					AR5K_STA_ID1_PCF_5210 : AR5K_STA_ID1_PCF_5211) +#define AR5K_STA_ID1_DEFAULT_ANTENNA	0x00200000	/* Use default antenna */ +#define AR5K_STA_ID1_DESC_ANTENNA	0x00400000	/* Update antenna from descriptor */ +#define AR5K_STA_ID1_RTS_DEF_ANTENNA	0x00800000	/* Use default antenna for RTS (?) */ +#define AR5K_STA_ID1_ACKCTS_6MB		0x01000000	/* Use 6Mbit/s for ACK/CTS (?) */ +#define AR5K_STA_ID1_BASE_RATE_11B	0x02000000	/* Use 11b base rate (for ACK/CTS ?) [5211+] */ + +/* + * First BSSID register (MAC address, lower 32bits) + */ +#define AR5K_BSS_ID0	0x8008 + +/* + * Second BSSID register (MAC address in upper 16 bits) + * + * AID: Association ID + */ +#define AR5K_BSS_ID1		0x800c +#define AR5K_BSS_ID1_AID	0xffff0000 +#define AR5K_BSS_ID1_AID_S	16 + +/* + * Backoff slot time register + */ +#define AR5K_SLOT_TIME	0x8010 + +/* + * ACK/CTS timeout register + */ +#define AR5K_TIME_OUT		0x8014			/* Register Address */ +#define AR5K_TIME_OUT_ACK	0x00001fff	/* ACK timeout mask */ +#define AR5K_TIME_OUT_ACK_S	0 +#define AR5K_TIME_OUT_CTS	0x1fff0000	/* CTS timeout mask */ +#define AR5K_TIME_OUT_CTS_S	16 + +/* + * RSSI threshold register + */ +#define AR5K_RSSI_THR			0x8018		/* Register Address */ +#define AR5K_RSSI_THR_M			0x000000ff	/* Mask for RSSI threshold [5211+] */ +#define AR5K_RSSI_THR_BMISS_5210	0x00000700	/* Mask for Beacon Missed threshold [5210] */ +#define AR5K_RSSI_THR_BMISS_5210_S	8 +#define AR5K_RSSI_THR_BMISS_5211	0x0000ff00	/* Mask for Beacon Missed threshold [5211+] */ +#define AR5K_RSSI_THR_BMISS_5211_S	8 +#define	AR5K_RSSI_THR_BMISS		(ah->ah_version == AR5K_AR5210 ? \ +					AR5K_RSSI_THR_BMISS_5210 : AR5K_RSSI_THR_BMISS_5211) +#define	AR5K_RSSI_THR_BMISS_S		8 + +/* + * 5210 has more PCU registers because there is no QCU/DCU + * so queue parameters are set here, this way a lot common + * registers have different address for 5210. To make things + * easier we define a macro based on ah->ah_version for common + * registers with different addresses and common flags. + */ + +/* + * Retry limit register + * + * Retry limit register for 5210 (no QCU/DCU so it's done in PCU) + */ +#define AR5K_NODCU_RETRY_LMT		0x801c			/*Register Address */ +#define AR5K_NODCU_RETRY_LMT_SH_RETRY	0x0000000f	/* Short retry limit mask */ +#define AR5K_NODCU_RETRY_LMT_SH_RETRY_S	0 +#define AR5K_NODCU_RETRY_LMT_LG_RETRY	0x000000f0	/* Long retry mask */ +#define AR5K_NODCU_RETRY_LMT_LG_RETRY_S	4 +#define AR5K_NODCU_RETRY_LMT_SSH_RETRY	0x00003f00	/* Station short retry limit mask */ +#define AR5K_NODCU_RETRY_LMT_SSH_RETRY_S	8 +#define AR5K_NODCU_RETRY_LMT_SLG_RETRY	0x000fc000	/* Station long retry limit mask */ +#define AR5K_NODCU_RETRY_LMT_SLG_RETRY_S	14 +#define AR5K_NODCU_RETRY_LMT_CW_MIN	0x3ff00000	/* Minimum contention window mask */ +#define AR5K_NODCU_RETRY_LMT_CW_MIN_S	20 + +/* + * Transmit latency register + */ +#define AR5K_USEC_5210			0x8020			/* Register Address [5210] */ +#define AR5K_USEC_5211			0x801c			/* Register Address [5211+] */ +#define AR5K_USEC			(ah->ah_version == AR5K_AR5210 ? \ +					AR5K_USEC_5210 : AR5K_USEC_5211) +#define AR5K_USEC_1			0x0000007f +#define AR5K_USEC_1_S			0 +#define AR5K_USEC_32			0x00003f80 +#define AR5K_USEC_32_S			7 +#define AR5K_USEC_TX_LATENCY_5211	0x007fc000 +#define AR5K_USEC_TX_LATENCY_5211_S	14 +#define AR5K_USEC_RX_LATENCY_5211	0x1f800000 +#define AR5K_USEC_RX_LATENCY_5211_S	23 +#define AR5K_USEC_TX_LATENCY_5210	0x000fc000	/* also for 5311 */ +#define AR5K_USEC_TX_LATENCY_5210_S	14 +#define AR5K_USEC_RX_LATENCY_5210	0x03f00000	/* also for 5311 */ +#define AR5K_USEC_RX_LATENCY_5210_S	20 + +/* + * PCU beacon control register + */ +#define AR5K_BEACON_5210	0x8024 +#define AR5K_BEACON_5211	0x8020 +#define AR5K_BEACON		(ah->ah_version == AR5K_AR5210 ? \ +				AR5K_BEACON_5210 : AR5K_BEACON_5211) +#define AR5K_BEACON_PERIOD	0x0000ffff +#define AR5K_BEACON_PERIOD_S	0 +#define AR5K_BEACON_TIM		0x007f0000 +#define AR5K_BEACON_TIM_S	16 +#define AR5K_BEACON_ENABLE	0x00800000 +#define AR5K_BEACON_RESET_TSF	0x01000000 + +/* + * CFP period register + */ +#define AR5K_CFP_PERIOD_5210	0x8028 +#define AR5K_CFP_PERIOD_5211	0x8024 +#define AR5K_CFP_PERIOD		(ah->ah_version == AR5K_AR5210 ? \ +				AR5K_CFP_PERIOD_5210 : AR5K_CFP_PERIOD_5211) + +/* + * Next beacon time register + */ +#define AR5K_TIMER0_5210	0x802c +#define AR5K_TIMER0_5211	0x8028 +#define AR5K_TIMER0		(ah->ah_version == AR5K_AR5210 ? \ +				AR5K_TIMER0_5210 : AR5K_TIMER0_5211) + +/* + * Next DMA beacon alert register + */ +#define AR5K_TIMER1_5210	0x8030 +#define AR5K_TIMER1_5211	0x802c +#define AR5K_TIMER1		(ah->ah_version == AR5K_AR5210 ? \ +				AR5K_TIMER1_5210 : AR5K_TIMER1_5211) + +/* + * Next software beacon alert register + */ +#define AR5K_TIMER2_5210	0x8034 +#define AR5K_TIMER2_5211	0x8030 +#define AR5K_TIMER2		(ah->ah_version == AR5K_AR5210 ? \ +				AR5K_TIMER2_5210 : AR5K_TIMER2_5211) + +/* + * Next ATIM window time register + */ +#define AR5K_TIMER3_5210	0x8038 +#define AR5K_TIMER3_5211	0x8034 +#define AR5K_TIMER3		(ah->ah_version == AR5K_AR5210 ? \ +				AR5K_TIMER3_5210 : AR5K_TIMER3_5211) + + +/* + * 5210 First inter frame spacing register (IFS) + */ +#define AR5K_IFS0		0x8040 +#define AR5K_IFS0_SIFS		0x000007ff +#define AR5K_IFS0_SIFS_S	0 +#define AR5K_IFS0_DIFS		0x007ff800 +#define AR5K_IFS0_DIFS_S	11 + +/* + * 5210 Second inter frame spacing register (IFS) + */ +#define AR5K_IFS1		0x8044 +#define AR5K_IFS1_PIFS		0x00000fff +#define AR5K_IFS1_PIFS_S	0 +#define AR5K_IFS1_EIFS		0x03fff000 +#define AR5K_IFS1_EIFS_S	12 +#define AR5K_IFS1_CS_EN		0x04000000 + + +/* + * CFP duration register + */ +#define AR5K_CFP_DUR_5210	0x8048 +#define AR5K_CFP_DUR_5211	0x8038 +#define AR5K_CFP_DUR		(ah->ah_version == AR5K_AR5210 ? \ +				AR5K_CFP_DUR_5210 : AR5K_CFP_DUR_5211) + +/* + * Receive filter register + * TODO: Get these out of ar5xxx.h on ath5k + */ +#define AR5K_RX_FILTER_5210	0x804c			/* Register Address [5210] */ +#define AR5K_RX_FILTER_5211	0x803c			/* Register Address [5211+] */ +#define AR5K_RX_FILTER		(ah->ah_version == AR5K_AR5210 ? \ +				AR5K_RX_FILTER_5210 : AR5K_RX_FILTER_5211) +#define	AR5K_RX_FILTER_UCAST 	0x00000001	/* Don't filter unicast frames */ +#define	AR5K_RX_FILTER_MCAST 	0x00000002	/* Don't filter multicast frames */ +#define	AR5K_RX_FILTER_BCAST 	0x00000004	/* Don't filter broadcast frames */ +#define	AR5K_RX_FILTER_CONTROL 	0x00000008	/* Don't filter control frames */ +#define	AR5K_RX_FILTER_BEACON 	0x00000010	/* Don't filter beacon frames */ +#define	AR5K_RX_FILTER_PROM 	0x00000020	/* Set promiscuous mode */ +#define	AR5K_RX_FILTER_XRPOLL 	0x00000040	/* Don't filter XR poll frame [5212+] */ +#define	AR5K_RX_FILTER_PROBEREQ 0x00000080	/* Don't filter probe requests [5212+] */ +#define	AR5K_RX_FILTER_PHYERR_5212	0x00000100	/* Don't filter phy errors [5212+] */ +#define	AR5K_RX_FILTER_RADARERR_5212 	0x00000200	/* Don't filter phy radar errors [5212+] */ +#define AR5K_RX_FILTER_PHYERR_5211	0x00000040	/* [5211] */ +#define AR5K_RX_FILTER_RADARERR_5211	0x00000080	/* [5211] */ +#define AR5K_RX_FILTER_PHYERR  \ +	((ah->ah_version == AR5K_AR5211 ? \ +	AR5K_RX_FILTER_PHYERR_5211 : AR5K_RX_FILTER_PHYERR_5212)) +#define        AR5K_RX_FILTER_RADARERR \ +	((ah->ah_version == AR5K_AR5211 ? \ +	AR5K_RX_FILTER_RADARERR_5211 : AR5K_RX_FILTER_RADARERR_5212)) + +/* + * Multicast filter register (lower 32 bits) + */ +#define AR5K_MCAST_FILTER0_5210	0x8050 +#define AR5K_MCAST_FILTER0_5211	0x8040 +#define AR5K_MCAST_FILTER0	(ah->ah_version == AR5K_AR5210 ? \ +				AR5K_MCAST_FILTER0_5210 : AR5K_MCAST_FILTER0_5211) + +/* + * Multicast filter register (higher 16 bits) + */ +#define AR5K_MCAST_FILTER1_5210	0x8054 +#define AR5K_MCAST_FILTER1_5211	0x8044 +#define AR5K_MCAST_FILTER1	(ah->ah_version == AR5K_AR5210 ? \ +				AR5K_MCAST_FILTER1_5210 : AR5K_MCAST_FILTER1_5211) + + +/* + * Transmit mask register (lower 32 bits) [5210] + */ +#define AR5K_TX_MASK0	0x8058 + +/* + * Transmit mask register (higher 16 bits) [5210] + */ +#define AR5K_TX_MASK1	0x805c + +/* + * Clear transmit mask [5210] + */ +#define AR5K_CLR_TMASK	0x8060 + +/* + * Trigger level register (before transmission) [5210] + */ +#define AR5K_TRIG_LVL	0x8064 + + +/* + * PCU control register + * + * Only DIS_RX is used in the code, the rest i guess are + * for tweaking/diagnostics. + */ +#define AR5K_DIAG_SW_5210		0x8068			/* Register Address [5210] */ +#define AR5K_DIAG_SW_5211		0x8048			/* Register Address [5211+] */ +#define AR5K_DIAG_SW			(ah->ah_version == AR5K_AR5210 ? \ +					AR5K_DIAG_SW_5210 : AR5K_DIAG_SW_5211) +#define AR5K_DIAG_SW_DIS_WEP_ACK	0x00000001 +#define AR5K_DIAG_SW_DIS_ACK		0x00000002	/* Disable ACKs (?) */ +#define AR5K_DIAG_SW_DIS_CTS		0x00000004	/* Disable CTSs (?) */ +#define AR5K_DIAG_SW_DIS_ENC		0x00000008	/* Disable encryption (?) */ +#define AR5K_DIAG_SW_DIS_DEC		0x00000010	/* Disable decryption (?) */ +#define AR5K_DIAG_SW_DIS_TX		0x00000020	/* Disable transmit [5210] */ +#define AR5K_DIAG_SW_DIS_RX_5210	0x00000040	/* Disable recieve */ +#define AR5K_DIAG_SW_DIS_RX_5211	0x00000020 +#define	AR5K_DIAG_SW_DIS_RX		(ah->ah_version == AR5K_AR5210 ? \ +					AR5K_DIAG_SW_DIS_RX_5210 : AR5K_DIAG_SW_DIS_RX_5211) +#define AR5K_DIAG_SW_LOOP_BACK_5210	0x00000080	/* Loopback (i guess it goes with DIS_TX) [5210] */ +#define AR5K_DIAG_SW_LOOP_BACK_5211	0x00000040 +#define AR5K_DIAG_SW_LOOP_BACK		(ah->ah_version == AR5K_AR5210 ? \ +					AR5K_DIAG_SW_LOOP_BACK_5210 : AR5K_DIAG_SW_LOOP_BACK_5211) +#define AR5K_DIAG_SW_CORR_FCS_5210	0x00000100 +#define AR5K_DIAG_SW_CORR_FCS_5211	0x00000080 +#define AR5K_DIAG_SW_CORR_FCS		(ah->ah_version == AR5K_AR5210 ? \ +					AR5K_DIAG_SW_CORR_FCS_5210 : AR5K_DIAG_SW_CORR_FCS_5211) +#define AR5K_DIAG_SW_CHAN_INFO_5210	0x00000200 +#define AR5K_DIAG_SW_CHAN_INFO_5211	0x00000100 +#define AR5K_DIAG_SW_CHAN_INFO		(ah->ah_version == AR5K_AR5210 ? \ +					AR5K_DIAG_SW_CHAN_INFO_5210 : AR5K_DIAG_SW_CHAN_INFO_5211) +#define AR5K_DIAG_SW_EN_SCRAM_SEED_5211	0x00000200	/* Scrambler seed (?) */ +#define AR5K_DIAG_SW_EN_SCRAM_SEED_5210	0x00000400 +#define AR5K_DIAG_SW_EN_SCRAM_SEED	(ah->ah_version == AR5K_AR5210 ? \ +					AR5K_DIAG_SW_EN_SCRAM_SEED_5210 : AR5K_DIAG_SW_EN_SCRAM_SEED_5211) +#define AR5K_DIAG_SW_ECO_ENABLE		0x00000400	/* [5211+] */ +#define AR5K_DIAG_SW_SCVRAM_SEED	0x0003f800	/* [5210] */ +#define AR5K_DIAG_SW_SCRAM_SEED_M	0x0001fc00	/* Scrambler seed mask (?) */ +#define AR5K_DIAG_SW_SCRAM_SEED_S	10 +#define AR5K_DIAG_SW_DIS_SEQ_INC	0x00040000	/* Disable seqnum increment (?)[5210] */ +#define AR5K_DIAG_SW_FRAME_NV0_5210	0x00080000 +#define AR5K_DIAG_SW_FRAME_NV0_5211	0x00020000 +#define	AR5K_DIAG_SW_FRAME_NV0		(ah->ah_version == AR5K_AR5210 ? \ +					AR5K_DIAG_SW_FRAME_NV0_5210 : AR5K_DIAG_SW_FRAME_NV0_5211) +#define AR5K_DIAG_SW_OBSPT_M		0x000c0000 +#define AR5K_DIAG_SW_OBSPT_S		18 + +/* + * TSF (clock) register (lower 32 bits) + */ +#define AR5K_TSF_L32_5210	0x806c +#define AR5K_TSF_L32_5211	0x804c +#define	AR5K_TSF_L32		(ah->ah_version == AR5K_AR5210 ? \ +				AR5K_TSF_L32_5210 : AR5K_TSF_L32_5211) + +/* + * TSF (clock) register (higher 32 bits) + */ +#define AR5K_TSF_U32_5210	0x8070 +#define AR5K_TSF_U32_5211	0x8050 +#define	AR5K_TSF_U32		(ah->ah_version == AR5K_AR5210 ? \ +				AR5K_TSF_U32_5210 : AR5K_TSF_U32_5211) + +/* + * Last beacon timestamp register + */ +#define AR5K_LAST_TSTP	0x8080 + +/* + * ADDAC test register [5211+] + */ +#define AR5K_ADDAC_TEST	0x8054 +#define AR5K_ADDAC_TEST_TXCONT 0x00000001 + +/* + * Default antenna register [5211+] + */ +#define AR5K_DEFAULT_ANTENNA	0x8058 + + + +/* + * Retry count register [5210] + */ +#define AR5K_RETRY_CNT		0x8084			/* Register Address [5210] */ +#define AR5K_RETRY_CNT_SSH	0x0000003f	/* Station short retry count (?) */ +#define AR5K_RETRY_CNT_SLG	0x00000fc0	/* Station long retry count (?) */ + +/* + * Back-off status register [5210] + */ +#define AR5K_BACKOFF		0x8088			/* Register Address [5210] */ +#define AR5K_BACKOFF_CW		0x000003ff	/* Backoff Contention Window (?) */ +#define AR5K_BACKOFF_CNT	0x03ff0000	/* Backoff count (?) */ + + + +/* + * NAV register (current) + */ +#define AR5K_NAV_5210		0x808c +#define AR5K_NAV_5211		0x8084 +#define	AR5K_NAV		(ah->ah_version == AR5K_AR5210 ? \ +				AR5K_NAV_5210 : AR5K_NAV_5211) + +/* + * RTS success register + */ +#define AR5K_RTS_OK_5210	0x8090 +#define AR5K_RTS_OK_5211	0x8088 +#define	AR5K_RTS_OK		(ah->ah_version == AR5K_AR5210 ? \ +				AR5K_RTS_OK_5210 : AR5K_RTS_OK_5211) + +/* + * RTS failure register + */ +#define AR5K_RTS_FAIL_5210	0x8094 +#define AR5K_RTS_FAIL_5211	0x808c +#define	AR5K_RTS_FAIL		(ah->ah_version == AR5K_AR5210 ? \ +				AR5K_RTS_FAIL_5210 : AR5K_RTS_FAIL_5211) + +/* + * ACK failure register + */ +#define AR5K_ACK_FAIL_5210	0x8098 +#define AR5K_ACK_FAIL_5211	0x8090 +#define	AR5K_ACK_FAIL		(ah->ah_version == AR5K_AR5210 ? \ +				AR5K_ACK_FAIL_5210 : AR5K_ACK_FAIL_5211) + +/* + * FCS failure register + */ +#define AR5K_FCS_FAIL_5210	0x809c +#define AR5K_FCS_FAIL_5211	0x8094 +#define	AR5K_FCS_FAIL		(ah->ah_version == AR5K_AR5210 ? \ +				AR5K_FCS_FAIL_5210 : AR5K_FCS_FAIL_5211) + +/* + * Beacon count register + */ +#define AR5K_BEACON_CNT_5210	0x80a0 +#define AR5K_BEACON_CNT_5211	0x8098 +#define	AR5K_BEACON_CNT		(ah->ah_version == AR5K_AR5210 ? \ +				AR5K_BEACON_CNT_5210 : AR5K_BEACON_CNT_5211) + + +/*===5212 Specific PCU registers===*/ + +/* + * XR (eXtended Range) mode register + */ +#define AR5K_XRMODE			0x80c0 +#define	AR5K_XRMODE_POLL_TYPE_M		0x0000003f +#define	AR5K_XRMODE_POLL_TYPE_S		0 +#define	AR5K_XRMODE_POLL_SUBTYPE_M	0x0000003c +#define	AR5K_XRMODE_POLL_SUBTYPE_S	2 +#define	AR5K_XRMODE_POLL_WAIT_ALL	0x00000080 +#define	AR5K_XRMODE_SIFS_DELAY		0x000fff00 +#define	AR5K_XRMODE_FRAME_HOLD_M	0xfff00000 +#define	AR5K_XRMODE_FRAME_HOLD_S	20 + +/* + * XR delay register + */ +#define AR5K_XRDELAY			0x80c4 +#define AR5K_XRDELAY_SLOT_DELAY_M	0x0000ffff +#define AR5K_XRDELAY_SLOT_DELAY_S	0 +#define AR5K_XRDELAY_CHIRP_DELAY_M	0xffff0000 +#define AR5K_XRDELAY_CHIRP_DELAY_S	16 + +/* + * XR timeout register + */ +#define AR5K_XRTIMEOUT			0x80c8 +#define AR5K_XRTIMEOUT_CHIRP_M		0x0000ffff +#define AR5K_XRTIMEOUT_CHIRP_S		0 +#define AR5K_XRTIMEOUT_POLL_M		0xffff0000 +#define AR5K_XRTIMEOUT_POLL_S		16 + +/* + * XR chirp register + */ +#define AR5K_XRCHIRP			0x80cc +#define AR5K_XRCHIRP_SEND		0x00000001 +#define AR5K_XRCHIRP_GAP		0xffff0000 + +/* + * XR stomp register + */ +#define AR5K_XRSTOMP			0x80d0 +#define AR5K_XRSTOMP_TX			0x00000001 +#define AR5K_XRSTOMP_RX_ABORT		0x00000002 +#define AR5K_XRSTOMP_RSSI_THRES		0x0000ff00 + +/* + * First enhanced sleep register + */ +#define AR5K_SLEEP0			0x80d4 +#define AR5K_SLEEP0_NEXT_DTIM		0x0007ffff +#define AR5K_SLEEP0_NEXT_DTIM_S		0 +#define AR5K_SLEEP0_ASSUME_DTIM		0x00080000 +#define AR5K_SLEEP0_ENH_SLEEP_EN	0x00100000 +#define AR5K_SLEEP0_CABTO		0xff000000 +#define AR5K_SLEEP0_CABTO_S		24 + +/* + * Second enhanced sleep register + */ +#define AR5K_SLEEP1			0x80d8 +#define AR5K_SLEEP1_NEXT_TIM		0x0007ffff +#define AR5K_SLEEP1_NEXT_TIM_S		0 +#define AR5K_SLEEP1_BEACON_TO		0xff000000 +#define AR5K_SLEEP1_BEACON_TO_S		24 + +/* + * Third enhanced sleep register + */ +#define AR5K_SLEEP2			0x80dc +#define AR5K_SLEEP2_TIM_PER		0x0000ffff +#define AR5K_SLEEP2_TIM_PER_S		0 +#define AR5K_SLEEP2_DTIM_PER		0xffff0000 +#define AR5K_SLEEP2_DTIM_PER_S		16 + +/* + * BSSID mask registers + */ +#define AR5K_BSS_IDM0			0x80e0 +#define AR5K_BSS_IDM1			0x80e4 + +/* + * TX power control (TPC) register + */ +#define AR5K_TXPC			0x80e8 +#define AR5K_TXPC_ACK_M			0x0000003f +#define AR5K_TXPC_ACK_S			0 +#define AR5K_TXPC_CTS_M			0x00003f00 +#define AR5K_TXPC_CTS_S			8 +#define AR5K_TXPC_CHIRP_M		0x003f0000 +#define AR5K_TXPC_CHIRP_S		22 + +/* + * Profile count registers + */ +#define AR5K_PROFCNT_TX			0x80ec +#define AR5K_PROFCNT_RX			0x80f0 +#define AR5K_PROFCNT_RXCLR		0x80f4 +#define AR5K_PROFCNT_CYCLE		0x80f8 + +/* + * TSF parameter register + */ +#define AR5K_TSF_PARM			0x8104 +#define AR5K_TSF_PARM_INC_M		0x000000ff +#define AR5K_TSF_PARM_INC_S		0 + +/* + * PHY error filter register + */ +#define AR5K_PHY_ERR_FIL		0x810c +#define AR5K_PHY_ERR_FIL_RADAR		0x00000020 +#define AR5K_PHY_ERR_FIL_OFDM		0x00020000 +#define AR5K_PHY_ERR_FIL_CCK		0x02000000 + +/* + * Rate duration register + */ +#define AR5K_RATE_DUR_BASE		0x8700 +#define AR5K_RATE_DUR(_n)		(AR5K_RATE_DUR_BASE + ((_n) << 2)) + +/*===5212 end===*/ + +/* + * Key table (WEP) register + */ +#define AR5K_KEYTABLE_0_5210		0x9000 +#define AR5K_KEYTABLE_0_5211		0x8800 +#define AR5K_KEYTABLE_5210(_n)		(AR5K_KEYTABLE_0_5210 + ((_n) << 5)) +#define AR5K_KEYTABLE_5211(_n)		(AR5K_KEYTABLE_0_5211 + ((_n) << 5)) +#define	AR5K_KEYTABLE(_n)		(ah->ah_version == AR5K_AR5210 ? \ +					AR5K_KEYTABLE_5210(_n) : AR5K_KEYTABLE_5211(_n)) +#define AR5K_KEYTABLE_OFF(_n, x)	(AR5K_KEYTABLE(_n) + (x << 2)) +#define AR5K_KEYTABLE_TYPE(_n)		AR5K_KEYTABLE_OFF(_n, 5) +#define AR5K_KEYTABLE_TYPE_40		0x00000000 +#define AR5K_KEYTABLE_TYPE_104		0x00000001 +#define AR5K_KEYTABLE_TYPE_128		0x00000003 +#define AR5K_KEYTABLE_TYPE_TKIP		0x00000004	/* [5212+] */ +#define AR5K_KEYTABLE_TYPE_AES		0x00000005	/* [5211+] */ +#define AR5K_KEYTABLE_TYPE_CCM		0x00000006	/* [5212+] */ +#define AR5K_KEYTABLE_TYPE_NULL		0x00000007	/* [5211+] */ +#define AR5K_KEYTABLE_ANTENNA		0x00000008	/* [5212+] */ +#define AR5K_KEYTABLE_MAC0(_n)		AR5K_KEYTABLE_OFF(_n, 6) +#define AR5K_KEYTABLE_MAC1(_n)		AR5K_KEYTABLE_OFF(_n, 7) +#define AR5K_KEYTABLE_VALID		0x00008000 + +/* WEP 40-bit	= 40-bit  entered key + 24 bit IV = 64-bit + * WEP 104-bit	= 104-bit entered key + 24-bit IV = 128-bit + * WEP 128-bit	= 128-bit entered key + 24 bit IV = 152-bit + * + * Some vendors have introduced bigger WEP keys to address + * security vulnerabilities in WEP. This includes: + * + * WEP 232-bit = 232-bit entered key + 24 bit IV = 256-bit + * + * We can expand this if we find ar5k Atheros cards with a larger + * key table size. + */ +#define AR5K_KEYTABLE_SIZE_5210		64 +#define AR5K_KEYTABLE_SIZE_5211		128 +#define	AR5K_KEYTABLE_SIZE		(ah->ah_version == AR5K_AR5210 ? \ +					AR5K_KEYTABLE_SIZE_5210 : AR5K_KEYTABLE_SIZE_5211) + + +/*===PHY REGISTERS===*/ + +/* + * PHY register + */ +#define	AR5K_PHY_BASE			0x9800 +#define	AR5K_PHY(_n)			(AR5K_PHY_BASE + ((_n) << 2)) +#define AR5K_PHY_SHIFT_2GHZ		0x00004007 +#define AR5K_PHY_SHIFT_5GHZ		0x00000007 + +/* + * PHY frame control register [5110] /turbo mode register [5111+] + * + * There is another frame control register for [5111+] + * at address 0x9944 (see below) but the 2 first flags + * are common here between 5110 frame control register + * and [5111+] turbo mode register, so this also works as + * a "turbo mode register" for 5110. We treat this one as + * a frame control register for 5110 below. + */ +#define	AR5K_PHY_TURBO			0x9804 +#define	AR5K_PHY_TURBO_MODE		0x00000001 +#define	AR5K_PHY_TURBO_SHORT		0x00000002 + +/* + * PHY agility command register + */ +#define	AR5K_PHY_AGC			0x9808 +#define	AR5K_PHY_AGC_DISABLE		0x08000000 + +/* + * PHY timing register [5112+] + */ +#define	AR5K_PHY_TIMING_3		0x9814 +#define	AR5K_PHY_TIMING_3_DSC_MAN	0xfffe0000 +#define	AR5K_PHY_TIMING_3_DSC_MAN_S	17 +#define	AR5K_PHY_TIMING_3_DSC_EXP	0x0001e000 +#define	AR5K_PHY_TIMING_3_DSC_EXP_S	13 + +/* + * PHY chip revision register + */ +#define	AR5K_PHY_CHIP_ID		0x9818 + +/* + * PHY activation register + */ +#define	AR5K_PHY_ACT			0x981c +#define	AR5K_PHY_ACT_ENABLE		0x00000001 +#define	AR5K_PHY_ACT_DISABLE		0x00000002 + +/* + * PHY signal register + */ +#define	AR5K_PHY_SIG			0x9858 +#define	AR5K_PHY_SIG_FIRSTEP		0x0003f000 +#define	AR5K_PHY_SIG_FIRSTEP_S		12 +#define	AR5K_PHY_SIG_FIRPWR		0x03fc0000 +#define	AR5K_PHY_SIG_FIRPWR_S		18 + +/* + * PHY coarse agility control register + */ +#define	AR5K_PHY_AGCCOARSE		0x985c +#define	AR5K_PHY_AGCCOARSE_LO		0x00007f80 +#define	AR5K_PHY_AGCCOARSE_LO_S		7 +#define	AR5K_PHY_AGCCOARSE_HI		0x003f8000 +#define	AR5K_PHY_AGCCOARSE_HI_S		15 + +/* + * PHY agility control register + */ +#define	AR5K_PHY_AGCCTL			0x9860			/* Register address */ +#define	AR5K_PHY_AGCCTL_CAL		0x00000001	/* Enable PHY calibration */ +#define	AR5K_PHY_AGCCTL_NF		0x00000002	/* Enable Noise Floor calibration */ + +/* + * PHY noise floor status register + */ +#define AR5K_PHY_NF			0x9864 +#define AR5K_PHY_NF_M			0x000001ff +#define AR5K_PHY_NF_ACTIVE		0x00000100 +#define AR5K_PHY_NF_RVAL(_n)		(((_n) >> 19) & AR5K_PHY_NF_M) +#define AR5K_PHY_NF_AVAL(_n)		(-((_n) ^ AR5K_PHY_NF_M) + 1) +#define AR5K_PHY_NF_SVAL(_n)		(((_n) & AR5K_PHY_NF_M) | (1 << 9)) + +/* + * PHY ADC saturation register [5110] + */ +#define	AR5K_PHY_ADCSAT			0x9868 +#define	AR5K_PHY_ADCSAT_ICNT		0x0001f800 +#define	AR5K_PHY_ADCSAT_ICNT_S		11 +#define	AR5K_PHY_ADCSAT_THR		0x000007e0 +#define	AR5K_PHY_ADCSAT_THR_S		5 + +/* + * PHY sleep registers [5112+] + */ +#define AR5K_PHY_SCR			0x9870 +#define AR5K_PHY_SCR_32MHZ		0x0000001f +#define AR5K_PHY_SLMT			0x9874 +#define AR5K_PHY_SLMT_32MHZ		0x0000007f +#define AR5K_PHY_SCAL			0x9878 +#define AR5K_PHY_SCAL_32MHZ		0x0000000e + +/* + * PHY PLL (Phase Locked Loop) control register + */ +#define	AR5K_PHY_PLL			0x987c +#define	AR5K_PHY_PLL_20MHZ		0x13	/* For half rate (?) [5111+] */ +#define	AR5K_PHY_PLL_40MHZ_5211		0x18	/* For 802.11a */ +#define	AR5K_PHY_PLL_40MHZ_5212		0x000000aa +#define	AR5K_PHY_PLL_40MHZ		(ah->ah_version == AR5K_AR5211 ? \ +					AR5K_PHY_PLL_40MHZ_5211 : AR5K_PHY_PLL_40MHZ_5212) +#define	AR5K_PHY_PLL_44MHZ_5211		0x19	/* For 802.11b/g */ +#define	AR5K_PHY_PLL_44MHZ_5212		0x000000ab +#define	AR5K_PHY_PLL_44MHZ		(ah->ah_version == AR5K_AR5211 ? \ +					AR5K_PHY_PLL_44MHZ_5211 : AR5K_PHY_PLL_44MHZ_5212) +#define AR5K_PHY_PLL_RF5111		0x00000000 +#define AR5K_PHY_PLL_RF5112		0x00000040 + +/* + * RF Buffer register + * + * There are some special control registers on the RF chip + * that hold various operation settings related mostly to + * the analog parts (channel, gain adjustment etc). + * + * We don't write on those registers directly but + * we send a data packet on the buffer register and + * then write on another special register to notify hw + * to apply the settings. This is done so that control registers + * can be dynamicaly programmed during operation and the settings + * are applied faster on the hw. + * + * We sent such data packets during rf initialization and channel change + * through ath5k_hw_rf*_rfregs and ath5k_hw_rf*_channel functions. + * + * The data packets we send during initializadion are inside ath5k_ini_rf + * struct (see ath5k_hw.h) and each one is related to an "rf register bank". + * We use *rfregs functions to modify them  acording to current operation + * mode and eeprom values and pass them all together to the chip. + * + * It's obvious from the code that 0x989c is the buffer register but + * for the other special registers that we write to after sending each + * packet, i have no idea. So i'll name them BUFFER_CONTROL_X registers + * for now. It's interesting that they are also used for some other operations. + * + * Also check out hw.h and U.S. Patent 6677779 B1 (about buffer + * registers and control registers): + * + * http://www.google.com/patents?id=qNURAAAAEBAJ + */ + +#define AR5K_RF_BUFFER			0x989c +#define AR5K_RF_BUFFER_CONTROL_0	0x98c0	/* Channel on 5110 */ +#define AR5K_RF_BUFFER_CONTROL_1	0x98c4	/* Bank 7 on 5112 */ +#define AR5K_RF_BUFFER_CONTROL_2	0x98cc	/* Bank 7 on 5111 */ + +#define AR5K_RF_BUFFER_CONTROL_3	0x98d0	/* Bank 2 on 5112 */ +						/* Channel set on 5111 */ +						/* Used to read radio revision*/ + +#define AR5K_RF_BUFFER_CONTROL_4	0x98d4  /* RF Stage register on 5110 */ +						/* Bank 0,1,2,6 on 5111 */ +						/* Bank 1 on 5112 */ +						/* Used during activation on 5111 */ + +#define AR5K_RF_BUFFER_CONTROL_5	0x98d8	/* Bank 3 on 5111 */ +						/* Used during activation on 5111 */ +						/* Channel on 5112 */ +						/* Bank 6 on 5112 */ + +#define AR5K_RF_BUFFER_CONTROL_6	0x98dc	/* Bank 3 on 5112 */ + +/* + * PHY RF stage register [5210] + */ +#define AR5K_PHY_RFSTG			0x98d4 +#define AR5K_PHY_RFSTG_DISABLE		0x00000021 + +/* + * PHY receiver delay register [5111+] + */ +#define	AR5K_PHY_RX_DELAY		0x9914 +#define	AR5K_PHY_RX_DELAY_M		0x00003fff + +/* + * PHY timing I(nphase) Q(adrature) control register [5111+] + */ +#define	AR5K_PHY_IQ			0x9920		/* Register address */ +#define	AR5K_PHY_IQ_CORR_Q_Q_COFF	0x0000001f	/* Mask for q correction info */ +#define	AR5K_PHY_IQ_CORR_Q_I_COFF	0x000007e0	/* Mask for i correction info */ +#define	AR5K_PHY_IQ_CORR_Q_I_COFF_S	5 +#define	AR5K_PHY_IQ_CORR_ENABLE		0x00000800	/* Enable i/q correction */ +#define	AR5K_PHY_IQ_CAL_NUM_LOG_MAX	0x0000f000 +#define	AR5K_PHY_IQ_CAL_NUM_LOG_MAX_S	12 +#define	AR5K_PHY_IQ_RUN			0x00010000	/* Run i/q calibration */ + + +/* + * PHY PAPD probe register [5111+ (?)] + * Is this only present in 5212 ? + * Because it's always 0 in 5211 initialization code + */ +#define	AR5K_PHY_PAPD_PROBE		0x9930 +#define	AR5K_PHY_PAPD_PROBE_TXPOWER	0x00007e00 +#define	AR5K_PHY_PAPD_PROBE_TXPOWER_S	9 +#define	AR5K_PHY_PAPD_PROBE_TX_NEXT	0x00008000 +#define	AR5K_PHY_PAPD_PROBE_TYPE	0x01800000	/* [5112+] */ +#define	AR5K_PHY_PAPD_PROBE_TYPE_S	23 +#define	AR5K_PHY_PAPD_PROBE_TYPE_OFDM	0 +#define	AR5K_PHY_PAPD_PROBE_TYPE_XR	1 +#define	AR5K_PHY_PAPD_PROBE_TYPE_CCK	2 +#define	AR5K_PHY_PAPD_PROBE_GAINF	0xfe000000 +#define	AR5K_PHY_PAPD_PROBE_GAINF_S	25 +#define	AR5K_PHY_PAPD_PROBE_INI_5111	0x00004883	/* [5212+] */ +#define	AR5K_PHY_PAPD_PROBE_INI_5112	0x00004882	/* [5212+] */ + + +/* + * PHY TX rate power registers [5112+] + */ +#define	AR5K_PHY_TXPOWER_RATE1			0x9934 +#define	AR5K_PHY_TXPOWER_RATE2			0x9938 +#define	AR5K_PHY_TXPOWER_RATE_MAX		0x993c +#define	AR5K_PHY_TXPOWER_RATE_MAX_TPC_ENABLE	0x00000040 +#define	AR5K_PHY_TXPOWER_RATE3			0xa234 +#define	AR5K_PHY_TXPOWER_RATE4			0xa238 + +/* + * PHY frame control register [5111+] + */ +#define	AR5K_PHY_FRAME_CTL_5210		0x9804 +#define	AR5K_PHY_FRAME_CTL_5211		0x9944 +#define	AR5K_PHY_FRAME_CTL		(ah->ah_version == AR5K_AR5210 ? \ +					AR5K_PHY_FRAME_CTL_5210 : AR5K_PHY_FRAME_CTL_5211) +/*---[5111+]---*/ +#define	AR5K_PHY_FRAME_CTL_TX_CLIP	0x00000038 +#define	AR5K_PHY_FRAME_CTL_TX_CLIP_S	3 +/*---[5110/5111]---*/ +#define	AR5K_PHY_FRAME_CTL_TIMING_ERR	0x01000000 +#define	AR5K_PHY_FRAME_CTL_PARITY_ERR	0x02000000 +#define	AR5K_PHY_FRAME_CTL_ILLRATE_ERR	0x04000000	/* illegal rate */ +#define	AR5K_PHY_FRAME_CTL_ILLLEN_ERR	0x08000000	/* illegal length */ +#define	AR5K_PHY_FRAME_CTL_SERVICE_ERR	0x20000000 +#define	AR5K_PHY_FRAME_CTL_TXURN_ERR	0x40000000	/* tx underrun */ +#define AR5K_PHY_FRAME_CTL_INI		AR5K_PHY_FRAME_CTL_SERVICE_ERR | \ +			AR5K_PHY_FRAME_CTL_TXURN_ERR | \ +			AR5K_PHY_FRAME_CTL_ILLLEN_ERR | \ +			AR5K_PHY_FRAME_CTL_ILLRATE_ERR | \ +			AR5K_PHY_FRAME_CTL_PARITY_ERR | \ +			AR5K_PHY_FRAME_CTL_TIMING_ERR + +/* + * PHY radar detection register [5111+] + */ +#define	AR5K_PHY_RADAR			0x9954 + +/* Radar enable 			........ ........ ........ .......1 */ +#define	AR5K_PHY_RADAR_ENABLE		0x00000001 +#define	AR5K_PHY_RADAR_DISABLE          0x00000000 +#define	AR5K_PHY_RADAR_ENABLE_S		0 + +/* This is the value found on the card  .1.111.1 .1.1.... 111....1 1...1... +at power on. */ +#define	AR5K_PHY_RADAR_PWONDEF_AR5213	0x5d50e188 + +/* This is the value found on the card 	.1.1.111 ..11...1 .1...1.1 1...11.1 +after DFS is enabled */ +#define	AR5K_PHY_RADAR_ENABLED_AR5213	0x5731458d + +/* Finite Impulse Response (FIR) filter .1111111 ........ ........ ........ + * power out threshold. + * 7-bits, standard power range {0..127} in 1/2 dBm units. */ +#define AR5K_PHY_RADAR_FIRPWROUTTHR    	0x7f000000 +#define AR5K_PHY_RADAR_FIRPWROUTTHR_S	24 + +/* Radar RSSI/SNR threshold.		........ 111111.. ........ ........ + * 6-bits, dBm range {0..63} in dBm units. */ +#define AR5K_PHY_RADAR_RADARRSSITHR    	0x00fc0000 +#define AR5K_PHY_RADAR_RADARRSSITHR_S	18 + +/* Pulse height threshold 		........ ......11 1111.... ........ + * 6-bits, dBm range {0..63} in dBm units. */ +#define AR5K_PHY_RADAR_PULSEHEIGHTTHR   0x0003f000 +#define AR5K_PHY_RADAR_PULSEHEIGHTTHR_S	12 + +/* Pulse RSSI/SNR threshold		........ ........ ....1111 11...... + * 6-bits, dBm range {0..63} in dBm units. */ +#define AR5K_PHY_RADAR_PULSERSSITHR    	0x00000fc0 +#define AR5K_PHY_RADAR_PULSERSSITHR_S	6 + +/* Inband threshold  			........ ........ ........ ..11111. + * 5-bits, units unknown {0..31} (? MHz ?) */ +#define AR5K_PHY_RADAR_INBANDTHR    	0x0000003e +#define AR5K_PHY_RADAR_INBANDTHR_S	1 + +/* + * PHY antenna switch table registers [5110] + */ +#define AR5K_PHY_ANT_SWITCH_TABLE_0	0x9960 +#define AR5K_PHY_ANT_SWITCH_TABLE_1	0x9964 + +/* + * PHY clock sleep registers [5112+] + */ +#define AR5K_PHY_SCLOCK			0x99f0 +#define AR5K_PHY_SCLOCK_32MHZ		0x0000000c +#define AR5K_PHY_SDELAY			0x99f4 +#define AR5K_PHY_SDELAY_32MHZ		0x000000ff +#define AR5K_PHY_SPENDING		0x99f8 +#define AR5K_PHY_SPENDING_RF5111	0x00000018 +#define AR5K_PHY_SPENDING_RF5112	0x00000014 + +/* + * Misc PHY/radio registers [5110 - 5111] + */ +#define	AR5K_BB_GAIN_BASE		0x9b00 /* BaseBand Amplifier Gain table base address */ +#define AR5K_BB_GAIN(_n)		(AR5K_BB_GAIN_BASE + ((_n) << 2)) +#define	AR5K_RF_GAIN_BASE		0x9a00 /* RF Amplrifier Gain table base address */ +#define AR5K_RF_GAIN(_n)		(AR5K_RF_GAIN_BASE + ((_n) << 2)) + +/* + * PHY timing IQ calibration result register [5111+] + */ +#define	AR5K_PHY_IQRES_CAL_PWR_I	0x9c10 /* I (Inphase) power value */ +#define	AR5K_PHY_IQRES_CAL_PWR_Q	0x9c14 /* Q (Quadrature) power value */ +#define	AR5K_PHY_IQRES_CAL_CORR		0x9c18	/* I/Q Correlation */ + +/* + * PHY current RSSI register [5111+] + */ +#define	AR5K_PHY_CURRENT_RSSI		0x9c1c + +/* + * PHY PCDAC TX power table + */ +#define	AR5K_PHY_PCDAC_TXPOWER_BASE_5211	0xa180 +#define AR5K_PHY_PCDAC_TXPOWER_BASE_5413	0xa280 +#define AR5K_PHY_PCDAC_TXPOWER_BASE	(ah->ah_radio >= AR5K_RF5413 ? \ +					AR5K_PHY_PCDAC_TXPOWER_BASE_5413 :\ +					AR5K_PHY_PCDAC_TXPOWER_BASE_5211) +#define	AR5K_PHY_PCDAC_TXPOWER(_n)	(AR5K_PHY_PCDAC_TXPOWER_BASE + ((_n) << 2)) + +/* + * PHY mode register [5111+] + */ +#define	AR5K_PHY_MODE			0x0a200		/* Register address */ +#define	AR5K_PHY_MODE_MOD		0x00000001	/* PHY Modulation mask*/ +#define AR5K_PHY_MODE_MOD_OFDM		0 +#define AR5K_PHY_MODE_MOD_CCK		1 +#define AR5K_PHY_MODE_FREQ		0x00000002	/* Freq mode mask */ +#define	AR5K_PHY_MODE_FREQ_5GHZ		0 +#define	AR5K_PHY_MODE_FREQ_2GHZ		2 +#define AR5K_PHY_MODE_MOD_DYN		0x00000004	/* Dynamic OFDM/CCK mode mask [5112+] */ +#define AR5K_PHY_MODE_RAD		0x00000008	/* [5212+] */ +#define AR5K_PHY_MODE_RAD_RF5111	0 +#define AR5K_PHY_MODE_RAD_RF5112	8 +#define AR5K_PHY_MODE_XR		0x00000010	/* [5112+] */ + +/* + * PHY CCK transmit control register [5111+ (?)] + */ +#define AR5K_PHY_CCKTXCTL		0xa204 +#define AR5K_PHY_CCKTXCTL_WORLD		0x00000000 +#define AR5K_PHY_CCKTXCTL_JAPAN		0x00000010 + +/* + * PHY 2GHz gain register [5111+] + */ +#define	AR5K_PHY_GAIN_2GHZ		0xa20c +#define	AR5K_PHY_GAIN_2GHZ_MARGIN_TXRX	0x00fc0000 +#define	AR5K_PHY_GAIN_2GHZ_MARGIN_TXRX_S	18 +#define	AR5K_PHY_GAIN_2GHZ_INI_5111	0x6480416c diff --git a/drivers/net/wireless/ath5k/regdom.c b/drivers/net/wireless/ath5k/regdom.c new file mode 100644 index 000000000000..e851957dacfd --- /dev/null +++ b/drivers/net/wireless/ath5k/regdom.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2004, 2005 Reyk Floeter <reyk@vantronix.net> + * + * 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. + */ + +/* + * Basic regulation domain extensions for the IEEE 802.11 stack + */ + +#include <linux/kernel.h> +#include <linux/string.h> + +#include "regdom.h" + +static const struct ath5k_regdommap { +	enum ath5k_regdom dmn; +	enum ath5k_regdom dmn5; +	enum ath5k_regdom dmn2; +} r_map[] = { +	{ DMN_DEFAULT,		DMN_DEBUG,	DMN_DEBUG }, +	{ DMN_NULL_WORLD,	DMN_NULL,	DMN_WORLD }, +	{ DMN_NULL_ETSIB,	DMN_NULL,	DMN_ETSIB }, +	{ DMN_NULL_ETSIC,	DMN_NULL,	DMN_ETSIC }, +	{ DMN_FCC1_FCCA,	DMN_FCC1,	DMN_FCCA }, +	{ DMN_FCC1_WORLD,	DMN_FCC1,	DMN_WORLD }, +	{ DMN_FCC2_FCCA,	DMN_FCC2,	DMN_FCCA }, +	{ DMN_FCC2_WORLD,	DMN_FCC2,	DMN_WORLD }, +	{ DMN_FCC2_ETSIC,	DMN_FCC2,	DMN_ETSIC }, +	{ DMN_FRANCE_NULL,	DMN_ETSI3,	DMN_ETSI3 }, +	{ DMN_FCC3_FCCA,	DMN_FCC3,	DMN_WORLD }, +	{ DMN_ETSI1_WORLD,	DMN_ETSI1,	DMN_WORLD }, +	{ DMN_ETSI3_ETSIA,	DMN_ETSI3,	DMN_WORLD }, +	{ DMN_ETSI2_WORLD,	DMN_ETSI2,	DMN_WORLD }, +	{ DMN_ETSI3_WORLD,	DMN_ETSI3,	DMN_WORLD }, +	{ DMN_ETSI4_WORLD,	DMN_ETSI4,	DMN_WORLD }, +	{ DMN_ETSI4_ETSIC,	DMN_ETSI4,	DMN_ETSIC }, +	{ DMN_ETSI5_WORLD,	DMN_ETSI5,	DMN_WORLD }, +	{ DMN_ETSI6_WORLD,	DMN_ETSI6,	DMN_WORLD }, +	{ DMN_ETSI_NULL,	DMN_ETSI1,	DMN_ETSI1 }, +	{ DMN_MKK1_MKKA,	DMN_MKK1,	DMN_MKKA }, +	{ DMN_MKK1_MKKB,	DMN_MKK1,	DMN_MKKA }, +	{ DMN_APL4_WORLD,	DMN_APL4,	DMN_WORLD }, +	{ DMN_MKK2_MKKA,	DMN_MKK2,	DMN_MKKA }, +	{ DMN_APL_NULL,		DMN_APL1,	DMN_NULL }, +	{ DMN_APL2_WORLD,	DMN_APL2,	DMN_WORLD }, +	{ DMN_APL2_APLC,	DMN_APL2,	DMN_WORLD }, +	{ DMN_APL3_WORLD,	DMN_APL3,	DMN_WORLD }, +	{ DMN_MKK1_FCCA,	DMN_MKK1,	DMN_FCCA }, +	{ DMN_APL2_APLD,	DMN_APL2,	DMN_APLD }, +	{ DMN_MKK1_MKKA1,	DMN_MKK1,	DMN_MKKA }, +	{ DMN_MKK1_MKKA2,	DMN_MKK1,	DMN_MKKA }, +	{ DMN_APL1_WORLD,	DMN_APL1,	DMN_WORLD }, +	{ DMN_APL1_FCCA,	DMN_APL1,	DMN_FCCA }, +	{ DMN_APL1_APLA,	DMN_APL1,	DMN_WORLD }, +	{ DMN_APL1_ETSIC,	DMN_APL1,	DMN_ETSIC }, +	{ DMN_APL2_ETSIC,	DMN_APL2,	DMN_ETSIC }, +	{ DMN_APL5_WORLD,	DMN_APL5,	DMN_WORLD }, +	{ DMN_WOR0_WORLD,	DMN_WORLD,	DMN_WORLD }, +	{ DMN_WOR1_WORLD,	DMN_WORLD,	DMN_WORLD }, +	{ DMN_WOR2_WORLD,	DMN_WORLD,	DMN_WORLD }, +	{ DMN_WOR3_WORLD,	DMN_WORLD,	DMN_WORLD }, +	{ DMN_WOR4_WORLD,	DMN_WORLD,	DMN_WORLD }, +	{ DMN_WOR5_ETSIC,	DMN_WORLD,	DMN_WORLD }, +	{ DMN_WOR01_WORLD,	DMN_WORLD,	DMN_WORLD }, +	{ DMN_WOR02_WORLD,	DMN_WORLD,	DMN_WORLD }, +	{ DMN_EU1_WORLD,	DMN_ETSI1,	DMN_WORLD }, +	{ DMN_WOR9_WORLD,	DMN_WORLD,	DMN_WORLD }, +	{ DMN_WORA_WORLD,	DMN_WORLD,	DMN_WORLD }, +}; + +enum ath5k_regdom ath5k_regdom2flag(enum ath5k_regdom dmn, u16 mhz) +{ +	unsigned int i; + +	for (i = 0; i < ARRAY_SIZE(r_map); i++) { +		if (r_map[i].dmn == dmn) { +			if (mhz >= 2000 && mhz <= 3000) +				return r_map[i].dmn2; +			if (mhz >= IEEE80211_CHANNELS_5GHZ_MIN && +					mhz <= IEEE80211_CHANNELS_5GHZ_MAX) +				return r_map[i].dmn5; +		} +	} + +	return DMN_DEBUG; +} + +u16 ath5k_regdom_from_ieee(enum ath5k_regdom ieee) +{ +	u32 regdomain = (u32)ieee; + +	/* +	 * Use the default regulation domain if the value is empty +	 * or not supported by the net80211 regulation code. +	 */ +	if (ath5k_regdom2flag(regdomain, IEEE80211_CHANNELS_5GHZ_MIN) == +			DMN_DEBUG) +		return (u16)AR5K_TUNE_REGDOMAIN; + +	/* It is supported, just return the value */ +	return regdomain; +} + +enum ath5k_regdom ath5k_regdom_to_ieee(u16 regdomain) +{ +	enum ath5k_regdom ieee = (enum ath5k_regdom)regdomain; + +	return ieee; +} + diff --git a/drivers/net/wireless/ath5k/regdom.h b/drivers/net/wireless/ath5k/regdom.h new file mode 100644 index 000000000000..f7d3c66e594e --- /dev/null +++ b/drivers/net/wireless/ath5k/regdom.h @@ -0,0 +1,500 @@ +/* + * Copyright (c) 2004, 2005 Reyk Floeter <reyk@openbsd.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. + */ + +#ifndef _IEEE80211_REGDOMAIN_H_ +#define _IEEE80211_REGDOMAIN_H_ + +#include <linux/types.h> + +/* Default regulation domain if stored value EEPROM value is invalid */ +#define AR5K_TUNE_REGDOMAIN	DMN_FCC2_FCCA	/* Canada */ +#define AR5K_TUNE_CTRY		CTRY_DEFAULT + + +enum ath5k_regdom { +	DMN_DEFAULT		= 0x00, +	DMN_NULL_WORLD		= 0x03, +	DMN_NULL_ETSIB		= 0x07, +	DMN_NULL_ETSIC		= 0x08, +	DMN_FCC1_FCCA		= 0x10, +	DMN_FCC1_WORLD		= 0x11, +	DMN_FCC2_FCCA		= 0x20, +	DMN_FCC2_WORLD		= 0x21, +	DMN_FCC2_ETSIC		= 0x22, +	DMN_FRANCE_NULL		= 0x31, +	DMN_FCC3_FCCA		= 0x3A, +	DMN_ETSI1_WORLD		= 0x37, +	DMN_ETSI3_ETSIA		= 0x32, +	DMN_ETSI2_WORLD		= 0x35, +	DMN_ETSI3_WORLD		= 0x36, +	DMN_ETSI4_WORLD		= 0x30, +	DMN_ETSI4_ETSIC		= 0x38, +	DMN_ETSI5_WORLD		= 0x39, +	DMN_ETSI6_WORLD		= 0x34, +	DMN_ETSI_NULL		= 0x33, +	DMN_MKK1_MKKA		= 0x40, +	DMN_MKK1_MKKB		= 0x41, +	DMN_APL4_WORLD		= 0x42, +	DMN_MKK2_MKKA		= 0x43, +	DMN_APL_NULL		= 0x44, +	DMN_APL2_WORLD		= 0x45, +	DMN_APL2_APLC		= 0x46, +	DMN_APL3_WORLD		= 0x47, +	DMN_MKK1_FCCA		= 0x48, +	DMN_APL2_APLD		= 0x49, +	DMN_MKK1_MKKA1		= 0x4A, +	DMN_MKK1_MKKA2		= 0x4B, +	DMN_APL1_WORLD		= 0x52, +	DMN_APL1_FCCA		= 0x53, +	DMN_APL1_APLA		= 0x54, +	DMN_APL1_ETSIC		= 0x55, +	DMN_APL2_ETSIC		= 0x56, +	DMN_APL5_WORLD		= 0x58, +	DMN_WOR0_WORLD		= 0x60, +	DMN_WOR1_WORLD		= 0x61, +	DMN_WOR2_WORLD		= 0x62, +	DMN_WOR3_WORLD		= 0x63, +	DMN_WOR4_WORLD		= 0x64, +	DMN_WOR5_ETSIC		= 0x65, +	DMN_WOR01_WORLD		= 0x66, +	DMN_WOR02_WORLD		= 0x67, +	DMN_EU1_WORLD		= 0x68, +	DMN_WOR9_WORLD		= 0x69, +	DMN_WORA_WORLD		= 0x6A, + +	DMN_APL1		= 0xf0000001, +	DMN_APL2		= 0xf0000002, +	DMN_APL3		= 0xf0000004, +	DMN_APL4		= 0xf0000008, +	DMN_APL5		= 0xf0000010, +	DMN_ETSI1		= 0xf0000020, +	DMN_ETSI2		= 0xf0000040, +	DMN_ETSI3		= 0xf0000080, +	DMN_ETSI4		= 0xf0000100, +	DMN_ETSI5		= 0xf0000200, +	DMN_ETSI6		= 0xf0000400, +	DMN_ETSIA		= 0xf0000800, +	DMN_ETSIB		= 0xf0001000, +	DMN_ETSIC		= 0xf0002000, +	DMN_FCC1		= 0xf0004000, +	DMN_FCC2		= 0xf0008000, +	DMN_FCC3		= 0xf0010000, +	DMN_FCCA		= 0xf0020000, +	DMN_APLD		= 0xf0040000, +	DMN_MKK1		= 0xf0080000, +	DMN_MKK2		= 0xf0100000, +	DMN_MKKA		= 0xf0200000, +	DMN_NULL		= 0xf0400000, +	DMN_WORLD		= 0xf0800000, +	DMN_DEBUG               = 0xf1000000	/* used for debugging */ +}; + +#define IEEE80211_DMN(_d)	((_d) & ~0xf0000000) + +enum ath5k_countrycode { +	CTRY_DEFAULT            = 0,   /* Default domain (NA) */ +	CTRY_ALBANIA            = 8,   /* Albania */ +	CTRY_ALGERIA            = 12,  /* Algeria */ +	CTRY_ARGENTINA          = 32,  /* Argentina */ +	CTRY_ARMENIA            = 51,  /* Armenia */ +	CTRY_AUSTRALIA          = 36,  /* Australia */ +	CTRY_AUSTRIA            = 40,  /* Austria */ +	CTRY_AZERBAIJAN         = 31,  /* Azerbaijan */ +	CTRY_BAHRAIN            = 48,  /* Bahrain */ +	CTRY_BELARUS            = 112, /* Belarus */ +	CTRY_BELGIUM            = 56,  /* Belgium */ +	CTRY_BELIZE             = 84,  /* Belize */ +	CTRY_BOLIVIA            = 68,  /* Bolivia */ +	CTRY_BRAZIL             = 76,  /* Brazil */ +	CTRY_BRUNEI_DARUSSALAM  = 96,  /* Brunei Darussalam */ +	CTRY_BULGARIA           = 100, /* Bulgaria */ +	CTRY_CANADA             = 124, /* Canada */ +	CTRY_CHILE              = 152, /* Chile */ +	CTRY_CHINA              = 156, /* People's Republic of China */ +	CTRY_COLOMBIA           = 170, /* Colombia */ +	CTRY_COSTA_RICA         = 188, /* Costa Rica */ +	CTRY_CROATIA            = 191, /* Croatia */ +	CTRY_CYPRUS             = 196, /* Cyprus */ +	CTRY_CZECH              = 203, /* Czech Republic */ +	CTRY_DENMARK            = 208, /* Denmark */ +	CTRY_DOMINICAN_REPUBLIC = 214, /* Dominican Republic */ +	CTRY_ECUADOR            = 218, /* Ecuador */ +	CTRY_EGYPT              = 818, /* Egypt */ +	CTRY_EL_SALVADOR        = 222, /* El Salvador */ +	CTRY_ESTONIA            = 233, /* Estonia */ +	CTRY_FAEROE_ISLANDS     = 234, /* Faeroe Islands */ +	CTRY_FINLAND            = 246, /* Finland */ +	CTRY_FRANCE             = 250, /* France */ +	CTRY_FRANCE2            = 255, /* France2 */ +	CTRY_GEORGIA            = 268, /* Georgia */ +	CTRY_GERMANY            = 276, /* Germany */ +	CTRY_GREECE             = 300, /* Greece */ +	CTRY_GUATEMALA          = 320, /* Guatemala */ +	CTRY_HONDURAS           = 340, /* Honduras */ +	CTRY_HONG_KONG          = 344, /* Hong Kong S.A.R., P.R.C. */ +	CTRY_HUNGARY            = 348, /* Hungary */ +	CTRY_ICELAND            = 352, /* Iceland */ +	CTRY_INDIA              = 356, /* India */ +	CTRY_INDONESIA          = 360, /* Indonesia */ +	CTRY_IRAN               = 364, /* Iran */ +	CTRY_IRAQ               = 368, /* Iraq */ +	CTRY_IRELAND            = 372, /* Ireland */ +	CTRY_ISRAEL             = 376, /* Israel */ +	CTRY_ITALY              = 380, /* Italy */ +	CTRY_JAMAICA            = 388, /* Jamaica */ +	CTRY_JAPAN              = 392, /* Japan */ +	CTRY_JAPAN1             = 393, /* Japan (JP1) */ +	CTRY_JAPAN2             = 394, /* Japan (JP0) */ +	CTRY_JAPAN3             = 395, /* Japan (JP1-1) */ +	CTRY_JAPAN4             = 396, /* Japan (JE1) */ +	CTRY_JAPAN5             = 397, /* Japan (JE2) */ +	CTRY_JORDAN             = 400, /* Jordan */ +	CTRY_KAZAKHSTAN         = 398, /* Kazakhstan */ +	CTRY_KENYA              = 404, /* Kenya */ +	CTRY_KOREA_NORTH        = 408, /* North Korea */ +	CTRY_KOREA_ROC          = 410, /* South Korea */ +	CTRY_KOREA_ROC2         = 411, /* South Korea */ +	CTRY_KUWAIT             = 414, /* Kuwait */ +	CTRY_LATVIA             = 428, /* Latvia */ +	CTRY_LEBANON            = 422, /* Lebanon */ +	CTRY_LIBYA              = 434, /* Libya */ +	CTRY_LIECHTENSTEIN      = 438, /* Liechtenstein */ +	CTRY_LITHUANIA          = 440, /* Lithuania */ +	CTRY_LUXEMBOURG         = 442, /* Luxembourg */ +	CTRY_MACAU              = 446, /* Macau */ +	CTRY_MACEDONIA          = 807, /* Republic of Macedonia */ +	CTRY_MALAYSIA           = 458, /* Malaysia */ +	CTRY_MEXICO             = 484, /* Mexico */ +	CTRY_MONACO             = 492, /* Principality of Monaco */ +	CTRY_MOROCCO            = 504, /* Morocco */ +	CTRY_NETHERLANDS        = 528, /* Netherlands */ +	CTRY_NEW_ZEALAND        = 554, /* New Zealand */ +	CTRY_NICARAGUA          = 558, /* Nicaragua */ +	CTRY_NORWAY             = 578, /* Norway */ +	CTRY_OMAN               = 512, /* Oman */ +	CTRY_PAKISTAN           = 586, /* Islamic Republic of Pakistan */ +	CTRY_PANAMA             = 591, /* Panama */ +	CTRY_PARAGUAY           = 600, /* Paraguay */ +	CTRY_PERU               = 604, /* Peru */ +	CTRY_PHILIPPINES        = 608, /* Republic of the Philippines */ +	CTRY_POLAND             = 616, /* Poland */ +	CTRY_PORTUGAL           = 620, /* Portugal */ +	CTRY_PUERTO_RICO        = 630, /* Puerto Rico */ +	CTRY_QATAR              = 634, /* Qatar */ +	CTRY_ROMANIA            = 642, /* Romania */ +	CTRY_RUSSIA             = 643, /* Russia */ +	CTRY_SAUDI_ARABIA       = 682, /* Saudi Arabia */ +	CTRY_SINGAPORE          = 702, /* Singapore */ +	CTRY_SLOVAKIA           = 703, /* Slovak Republic */ +	CTRY_SLOVENIA           = 705, /* Slovenia */ +	CTRY_SOUTH_AFRICA       = 710, /* South Africa */ +	CTRY_SPAIN              = 724, /* Spain */ +	CTRY_SRI_LANKA          = 728, /* Sri Lanka */ +	CTRY_SWEDEN             = 752, /* Sweden */ +	CTRY_SWITZERLAND        = 756, /* Switzerland */ +	CTRY_SYRIA              = 760, /* Syria */ +	CTRY_TAIWAN             = 158, /* Taiwan */ +	CTRY_THAILAND           = 764, /* Thailand */ +	CTRY_TRINIDAD_Y_TOBAGO  = 780, /* Trinidad y Tobago */ +	CTRY_TUNISIA            = 788, /* Tunisia */ +	CTRY_TURKEY             = 792, /* Turkey */ +	CTRY_UAE                = 784, /* U.A.E. */ +	CTRY_UKRAINE            = 804, /* Ukraine */ +	CTRY_UNITED_KINGDOM     = 826, /* United Kingdom */ +	CTRY_UNITED_STATES      = 840, /* United States */ +	CTRY_URUGUAY            = 858, /* Uruguay */ +	CTRY_UZBEKISTAN         = 860, /* Uzbekistan */ +	CTRY_VENEZUELA          = 862, /* Venezuela */ +	CTRY_VIET_NAM           = 704, /* Viet Nam */ +	CTRY_YEMEN              = 887, /* Yemen */ +	CTRY_ZIMBABWE           = 716, /* Zimbabwe */ +}; + +#define IEEE80211_CHANNELS_2GHZ_MIN	2412	/* 2GHz channel 1 */ +#define IEEE80211_CHANNELS_2GHZ_MAX	2732	/* 2GHz channel 26 */ +#define IEEE80211_CHANNELS_5GHZ_MIN	5005	/* 5GHz channel 1 */ +#define IEEE80211_CHANNELS_5GHZ_MAX	6100	/* 5GHz channel 220 */ + +struct ath5k_regchannel { +	u16 chan; +	enum ath5k_regdom domain; +	u32 mode; +}; + +#define IEEE80211_CHANNELS_2GHZ {					\ +/*2412*/ {   1, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2417*/ {   2, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2422*/ {   3, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2427*/ {   4, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2432*/ {   5, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2437*/ {   6, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2442*/ {   7, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2447*/ {   8, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2452*/ {   9, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2457*/ {  10, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2462*/ {  11, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2467*/ {  12, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2472*/ {  13, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM },			\ +									\ +/*2432*/ {   5, DMN_ETSIB, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2437*/ {   6, DMN_ETSIB, CHANNEL_CCK|CHANNEL_OFDM|CHANNEL_TURBO },	\ +/*2442*/ {   7, DMN_ETSIB, CHANNEL_CCK|CHANNEL_OFDM },			\ +									\ +/*2412*/ {   1, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2417*/ {   2, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2422*/ {   3, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2427*/ {   4, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2432*/ {   5, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2437*/ {   6, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM|CHANNEL_TURBO },	\ +/*2442*/ {   7, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2447*/ {   8, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2452*/ {   9, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2457*/ {  10, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2462*/ {  11, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2467*/ {  12, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2472*/ {  13, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM },			\ +									\ +/*2412*/ {   1, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2417*/ {   2, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2422*/ {   3, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2427*/ {   4, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2432*/ {   5, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2437*/ {   6, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM|CHANNEL_TURBO },	\ +/*2442*/ {   7, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2447*/ {   8, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2452*/ {   9, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2457*/ {  10, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2462*/ {  11, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM },			\ +									\ +/*2412*/ {   1, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2417*/ {   2, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2422*/ {   3, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2427*/ {   4, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2432*/ {   5, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2437*/ {   6, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2442*/ {   7, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2447*/ {   8, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2452*/ {   9, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2457*/ {  10, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2462*/ {  11, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2467*/ {  12, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2472*/ {  13, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2484*/ {  14, DMN_MKKA, CHANNEL_CCK },				\ +									\ +/*2412*/ {   1, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2417*/ {   2, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2422*/ {   3, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2427*/ {   4, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2432*/ {   5, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2437*/ {   6, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM|CHANNEL_TURBO },	\ +/*2442*/ {   7, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2447*/ {   8, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2452*/ {   9, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2457*/ {  10, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2462*/ {  11, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2467*/ {  12, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM },			\ +/*2472*/ {  13, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM },			\ +} + +#define IEEE80211_CHANNELS_5GHZ {			\ +/*5745*/ { 149, DMN_APL1, CHANNEL_OFDM },		\ +/*5765*/ { 153, DMN_APL1, CHANNEL_OFDM },		\ +/*5785*/ { 157, DMN_APL1, CHANNEL_OFDM },		\ +/*5805*/ { 161, DMN_APL1, CHANNEL_OFDM },		\ +/*5825*/ { 165, DMN_APL1, CHANNEL_OFDM },		\ +							\ +/*5745*/ { 149, DMN_APL2, CHANNEL_OFDM },		\ +/*5765*/ { 153, DMN_APL2, CHANNEL_OFDM },		\ +/*5785*/ { 157, DMN_APL2, CHANNEL_OFDM },		\ +/*5805*/ { 161, DMN_APL2, CHANNEL_OFDM },		\ +							\ +/*5280*/ {  56, DMN_APL3, CHANNEL_OFDM },		\ +/*5300*/ {  60, DMN_APL3, CHANNEL_OFDM },		\ +/*5320*/ {  64, DMN_APL3, CHANNEL_OFDM },		\ +/*5745*/ { 149, DMN_APL3, CHANNEL_OFDM },		\ +/*5765*/ { 153, DMN_APL3, CHANNEL_OFDM },		\ +/*5785*/ { 157, DMN_APL3, CHANNEL_OFDM },		\ +/*5805*/ { 161, DMN_APL3, CHANNEL_OFDM },		\ +							\ +/*5180*/ {  36, DMN_APL4, CHANNEL_OFDM },		\ +/*5200*/ {  40, DMN_APL4, CHANNEL_OFDM },		\ +/*5220*/ {  44, DMN_APL4, CHANNEL_OFDM },		\ +/*5240*/ {  48, DMN_APL4, CHANNEL_OFDM },		\ +/*5745*/ { 149, DMN_APL4, CHANNEL_OFDM },		\ +/*5765*/ { 153, DMN_APL4, CHANNEL_OFDM },		\ +/*5785*/ { 157, DMN_APL4, CHANNEL_OFDM },		\ +/*5805*/ { 161, DMN_APL4, CHANNEL_OFDM },		\ +/*5825*/ { 165, DMN_APL4, CHANNEL_OFDM },		\ +							\ +/*5745*/ { 149, DMN_APL5, CHANNEL_OFDM },		\ +/*5765*/ { 153, DMN_APL5, CHANNEL_OFDM },		\ +/*5785*/ { 157, DMN_APL5, CHANNEL_OFDM },		\ +/*5805*/ { 161, DMN_APL5, CHANNEL_OFDM },		\ +/*5825*/ { 165, DMN_APL5, CHANNEL_OFDM },		\ +							\ +/*5180*/ {  36, DMN_ETSI1, CHANNEL_OFDM },		\ +/*5200*/ {  40, DMN_ETSI1, CHANNEL_OFDM },		\ +/*5220*/ {  44, DMN_ETSI1, CHANNEL_OFDM },		\ +/*5240*/ {  48, DMN_ETSI1, CHANNEL_OFDM },		\ +/*5260*/ {  52, DMN_ETSI1, CHANNEL_OFDM },		\ +/*5280*/ {  56, DMN_ETSI1, CHANNEL_OFDM },		\ +/*5300*/ {  60, DMN_ETSI1, CHANNEL_OFDM },		\ +/*5320*/ {  64, DMN_ETSI1, CHANNEL_OFDM },		\ +/*5500*/ { 100, DMN_ETSI1, CHANNEL_OFDM },		\ +/*5520*/ { 104, DMN_ETSI1, CHANNEL_OFDM },		\ +/*5540*/ { 108, DMN_ETSI1, CHANNEL_OFDM },		\ +/*5560*/ { 112, DMN_ETSI1, CHANNEL_OFDM },		\ +/*5580*/ { 116, DMN_ETSI1, CHANNEL_OFDM },		\ +/*5600*/ { 120, DMN_ETSI1, CHANNEL_OFDM },		\ +/*5620*/ { 124, DMN_ETSI1, CHANNEL_OFDM },		\ +/*5640*/ { 128, DMN_ETSI1, CHANNEL_OFDM },		\ +/*5660*/ { 132, DMN_ETSI1, CHANNEL_OFDM },		\ +/*5680*/ { 136, DMN_ETSI1, CHANNEL_OFDM },		\ +/*5700*/ { 140, DMN_ETSI1, CHANNEL_OFDM },		\ +							\ +/*5180*/ {  36, DMN_ETSI2, CHANNEL_OFDM },		\ +/*5200*/ {  40, DMN_ETSI2, CHANNEL_OFDM },		\ +/*5220*/ {  44, DMN_ETSI2, CHANNEL_OFDM },		\ +/*5240*/ {  48, DMN_ETSI2, CHANNEL_OFDM },		\ +							\ +/*5180*/ {  36, DMN_ETSI3, CHANNEL_OFDM },		\ +/*5200*/ {  40, DMN_ETSI3, CHANNEL_OFDM },		\ +/*5220*/ {  44, DMN_ETSI3, CHANNEL_OFDM },		\ +/*5240*/ {  48, DMN_ETSI3, CHANNEL_OFDM },		\ +/*5260*/ {  52, DMN_ETSI3, CHANNEL_OFDM },		\ +/*5280*/ {  56, DMN_ETSI3, CHANNEL_OFDM },		\ +/*5300*/ {  60, DMN_ETSI3, CHANNEL_OFDM },		\ +/*5320*/ {  64, DMN_ETSI3, CHANNEL_OFDM },		\ +							\ +/*5180*/ {  36, DMN_ETSI4, CHANNEL_OFDM },		\ +/*5200*/ {  40, DMN_ETSI4, CHANNEL_OFDM },		\ +/*5220*/ {  44, DMN_ETSI4, CHANNEL_OFDM },		\ +/*5240*/ {  48, DMN_ETSI4, CHANNEL_OFDM },		\ +/*5260*/ {  52, DMN_ETSI4, CHANNEL_OFDM },		\ +/*5280*/ {  56, DMN_ETSI4, CHANNEL_OFDM },		\ +/*5300*/ {  60, DMN_ETSI4, CHANNEL_OFDM },		\ +/*5320*/ {  64, DMN_ETSI4, CHANNEL_OFDM },		\ +							\ +/*5180*/ {  36, DMN_ETSI5, CHANNEL_OFDM },		\ +/*5200*/ {  40, DMN_ETSI5, CHANNEL_OFDM },		\ +/*5220*/ {  44, DMN_ETSI5, CHANNEL_OFDM },		\ +/*5240*/ {  48, DMN_ETSI5, CHANNEL_OFDM },		\ +							\ +/*5180*/ {  36, DMN_ETSI6, CHANNEL_OFDM },		\ +/*5200*/ {  40, DMN_ETSI6, CHANNEL_OFDM },		\ +/*5220*/ {  44, DMN_ETSI6, CHANNEL_OFDM },		\ +/*5240*/ {  48, DMN_ETSI6, CHANNEL_OFDM },		\ +/*5260*/ {  52, DMN_ETSI6, CHANNEL_OFDM },		\ +/*5280*/ {  56, DMN_ETSI6, CHANNEL_OFDM },		\ +/*5500*/ { 100, DMN_ETSI6, CHANNEL_OFDM },		\ +/*5520*/ { 104, DMN_ETSI6, CHANNEL_OFDM },		\ +/*5540*/ { 108, DMN_ETSI6, CHANNEL_OFDM },		\ +/*5560*/ { 112, DMN_ETSI6, CHANNEL_OFDM },		\ +/*5580*/ { 116, DMN_ETSI6, CHANNEL_OFDM },		\ +/*5600*/ { 120, DMN_ETSI6, CHANNEL_OFDM },		\ +/*5620*/ { 124, DMN_ETSI6, CHANNEL_OFDM },		\ +/*5640*/ { 128, DMN_ETSI6, CHANNEL_OFDM },		\ +/*5660*/ { 132, DMN_ETSI6, CHANNEL_OFDM },		\ +/*5680*/ { 136, DMN_ETSI6, CHANNEL_OFDM },		\ +/*5700*/ { 140, DMN_ETSI6, CHANNEL_OFDM },		\ +							\ +/*5180*/ {  36, DMN_FCC1, CHANNEL_OFDM },		\ +/*5200*/ {  40, DMN_FCC1, CHANNEL_OFDM },		\ +/*5210*/ {  42, DMN_FCC1, CHANNEL_OFDM|CHANNEL_TURBO },	\ +/*5220*/ {  44, DMN_FCC1, CHANNEL_OFDM },		\ +/*5240*/ {  48, DMN_FCC1, CHANNEL_OFDM },		\ +/*5250*/ {  50, DMN_FCC1, CHANNEL_OFDM|CHANNEL_TURBO },	\ +/*5260*/ {  52, DMN_FCC1, CHANNEL_OFDM },		\ +/*5280*/ {  56, DMN_FCC1, CHANNEL_OFDM },		\ +/*5290*/ {  58, DMN_FCC1, CHANNEL_OFDM|CHANNEL_TURBO },	\ +/*5300*/ {  60, DMN_FCC1, CHANNEL_OFDM },		\ +/*5320*/ {  64, DMN_FCC1, CHANNEL_OFDM },		\ +/*5745*/ { 149, DMN_FCC1, CHANNEL_OFDM },		\ +/*5760*/ { 152, DMN_FCC1, CHANNEL_OFDM|CHANNEL_TURBO },	\ +/*5765*/ { 153, DMN_FCC1, CHANNEL_OFDM },		\ +/*5785*/ { 157, DMN_FCC1, CHANNEL_OFDM },		\ +/*5800*/ { 160, DMN_FCC1, CHANNEL_OFDM|CHANNEL_TURBO },	\ +/*5805*/ { 161, DMN_FCC1, CHANNEL_OFDM },		\ +/*5825*/ { 165, DMN_FCC1, CHANNEL_OFDM },		\ +							\ +/*5180*/ {  36, DMN_FCC2, CHANNEL_OFDM },		\ +/*5200*/ {  40, DMN_FCC2, CHANNEL_OFDM },		\ +/*5220*/ {  44, DMN_FCC2, CHANNEL_OFDM },		\ +/*5240*/ {  48, DMN_FCC2, CHANNEL_OFDM },		\ +/*5260*/ {  52, DMN_FCC2, CHANNEL_OFDM },		\ +/*5280*/ {  56, DMN_FCC2, CHANNEL_OFDM },		\ +/*5300*/ {  60, DMN_FCC2, CHANNEL_OFDM },		\ +/*5320*/ {  64, DMN_FCC2, CHANNEL_OFDM },		\ +/*5745*/ { 149, DMN_FCC2, CHANNEL_OFDM },		\ +/*5765*/ { 153, DMN_FCC2, CHANNEL_OFDM },		\ +/*5785*/ { 157, DMN_FCC2, CHANNEL_OFDM },		\ +/*5805*/ { 161, DMN_FCC2, CHANNEL_OFDM },		\ +/*5825*/ { 165, DMN_FCC2, CHANNEL_OFDM },		\ +							\ +/*5180*/ {  36, DMN_FCC3, CHANNEL_OFDM },		\ +/*5200*/ {  40, DMN_FCC3, CHANNEL_OFDM },		\ +/*5210*/ {  42, DMN_FCC3, CHANNEL_OFDM|CHANNEL_TURBO },	\ +/*5220*/ {  44, DMN_FCC3, CHANNEL_OFDM },		\ +/*5240*/ {  48, DMN_FCC3, CHANNEL_OFDM },		\ +/*5250*/ {  50, DMN_FCC3, CHANNEL_OFDM|CHANNEL_TURBO },	\ +/*5260*/ {  52, DMN_FCC3, CHANNEL_OFDM },		\ +/*5280*/ {  56, DMN_FCC3, CHANNEL_OFDM },		\ +/*5290*/ {  58, DMN_FCC3, CHANNEL_OFDM|CHANNEL_TURBO },	\ +/*5300*/ {  60, DMN_FCC3, CHANNEL_OFDM },		\ +/*5320*/ {  64, DMN_FCC3, CHANNEL_OFDM },		\ +/*5500*/ { 100, DMN_FCC3, CHANNEL_OFDM },		\ +/*5520*/ { 104, DMN_FCC3, CHANNEL_OFDM },		\ +/*5540*/ { 108, DMN_FCC3, CHANNEL_OFDM },		\ +/*5560*/ { 112, DMN_FCC3, CHANNEL_OFDM },		\ +/*5580*/ { 116, DMN_FCC3, CHANNEL_OFDM },		\ +/*5600*/ { 120, DMN_FCC3, CHANNEL_OFDM },		\ +/*5620*/ { 124, DMN_FCC3, CHANNEL_OFDM },		\ +/*5640*/ { 128, DMN_FCC3, CHANNEL_OFDM },		\ +/*5660*/ { 132, DMN_FCC3, CHANNEL_OFDM },		\ +/*5680*/ { 136, DMN_FCC3, CHANNEL_OFDM },		\ +/*5700*/ { 140, DMN_FCC3, CHANNEL_OFDM },		\ +/*5745*/ { 149, DMN_FCC3, CHANNEL_OFDM },		\ +/*5760*/ { 152, DMN_FCC3, CHANNEL_OFDM|CHANNEL_TURBO },	\ +/*5765*/ { 153, DMN_FCC3, CHANNEL_OFDM },		\ +/*5785*/ { 157, DMN_FCC3, CHANNEL_OFDM },		\ +/*5800*/ { 160, DMN_FCC3, CHANNEL_OFDM|CHANNEL_TURBO },	\ +/*5805*/ { 161, DMN_FCC3, CHANNEL_OFDM },		\ +/*5825*/ { 165, DMN_FCC3, CHANNEL_OFDM },		\ +							\ +/*5170*/ {  34, DMN_MKK1, CHANNEL_OFDM },		\ +/*5190*/ {  38, DMN_MKK1, CHANNEL_OFDM },		\ +/*5210*/ {  42, DMN_MKK1, CHANNEL_OFDM },		\ +/*5230*/ {  46, DMN_MKK1, CHANNEL_OFDM },		\ +							\ +/*5040*/ {   8, DMN_MKK2, CHANNEL_OFDM },		\ +/*5060*/ {  12, DMN_MKK2, CHANNEL_OFDM },		\ +/*5080*/ {  16, DMN_MKK2, CHANNEL_OFDM },		\ +/*5170*/ {  34, DMN_MKK2, CHANNEL_OFDM },		\ +/*5190*/ {  38, DMN_MKK2, CHANNEL_OFDM },		\ +/*5210*/ {  42, DMN_MKK2, CHANNEL_OFDM },		\ +/*5230*/ {  46, DMN_MKK2, CHANNEL_OFDM },		\ +							\ +/*5180*/ {  36, DMN_WORLD, CHANNEL_OFDM },		\ +/*5200*/ {  40, DMN_WORLD, CHANNEL_OFDM },		\ +/*5220*/ {  44, DMN_WORLD, CHANNEL_OFDM },		\ +/*5240*/ {  48, DMN_WORLD, CHANNEL_OFDM },		\ +} + +enum ath5k_regdom ath5k_regdom2flag(enum ath5k_regdom, u16); +u16 ath5k_regdom_from_ieee(enum ath5k_regdom ieee); +enum ath5k_regdom ath5k_regdom_to_ieee(u16 regdomain); + +#endif  |