Merge branch 'for-davem' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
diff --git a/MAINTAINERS b/MAINTAINERS
index 102352e..017bf49 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1120,6 +1120,13 @@
 S:	Maintained
 F:	drivers/net/wireless/ath/ar9170/
 
+CARL9170 LINUX COMMUNITY WIRELESS DRIVER
+M:	Christian Lamparter <chunkeey@googlemail.com>
+L:	linux-wireless@vger.kernel.org
+W:	http://wireless.kernel.org/en/users/Drivers/carl9170
+S:	Maintained
+F:	drivers/net/wireless/ath/carl9170/
+
 ATK0110 HWMON DRIVER
 M:	Luca Tettamanti <kronos.it@gmail.com>
 L:	lm-sensors@lm-sensors.org
@@ -6427,7 +6434,7 @@
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
 S:	Maintained
 F:	drivers/net/wireless/wl12xx/wl1271*
-F:	include/linux/spi/wl12xx.h
+F:	include/linux/wl12xx.h
 
 WL3501 WIRELESS PCMCIA CARD DRIVER
 M:	Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
diff --git a/arch/arm/mach-omap2/board-omap3pandora.c b/arch/arm/mach-omap2/board-omap3pandora.c
index dd3af2b..7ea1eb4 100644
--- a/arch/arm/mach-omap2/board-omap3pandora.c
+++ b/arch/arm/mach-omap2/board-omap3pandora.c
@@ -25,7 +25,7 @@
 #include <linux/spi/ads7846.h>
 #include <linux/regulator/machine.h>
 #include <linux/i2c/twl.h>
-#include <linux/spi/wl12xx.h>
+#include <linux/wl12xx.h>
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/nand.h>
 #include <linux/leds.h>
diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c
index 9a5eb87..ce28a85 100644
--- a/arch/arm/mach-omap2/board-rx51-peripherals.c
+++ b/arch/arm/mach-omap2/board-rx51-peripherals.c
@@ -14,7 +14,7 @@
 #include <linux/input.h>
 #include <linux/input/matrix_keypad.h>
 #include <linux/spi/spi.h>
-#include <linux/spi/wl12xx.h>
+#include <linux/wl12xx.h>
 #include <linux/i2c.h>
 #include <linux/i2c/twl.h>
 #include <linux/clk.h>
diff --git a/arch/arm/mach-omap2/board-zoom-peripherals.c b/arch/arm/mach-omap2/board-zoom-peripherals.c
index 6b39849..6aa0728 100644
--- a/arch/arm/mach-omap2/board-zoom-peripherals.c
+++ b/arch/arm/mach-omap2/board-zoom-peripherals.c
@@ -16,6 +16,8 @@
 #include <linux/gpio.h>
 #include <linux/i2c/twl.h>
 #include <linux/regulator/machine.h>
+#include <linux/regulator/fixed.h>
+#include <linux/wl12xx.h>
 
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
@@ -27,6 +29,9 @@
 #include "mux.h"
 #include "hsmmc.h"
 
+#define OMAP_ZOOM_WLAN_PMENA_GPIO	(101)
+#define OMAP_ZOOM_WLAN_IRQ_GPIO		(162)
+
 /* Zoom2 has Qwerty keyboard*/
 static int board_keymap[] = {
 	KEY(0, 0, KEY_E),
@@ -106,6 +111,11 @@
 	.supply		= "vmmc",
 };
 
+static struct regulator_consumer_supply zoom_vmmc3_supply = {
+	.supply		= "vmmc",
+	.dev_name	= "mmci-omap-hs.2",
+};
+
 /* VMMC1 for OMAP VDD_MMC1 (i/o) and MMC1 card */
 static struct regulator_init_data zoom_vmmc1 = {
 	.constraints = {
@@ -151,6 +161,38 @@
 	.consumer_supplies      = &zoom_vsim_supply,
 };
 
+static struct regulator_init_data zoom_vmmc3 = {
+	.constraints = {
+		.valid_ops_mask	= REGULATOR_CHANGE_STATUS,
+	},
+	.num_consumer_supplies	= 1,
+	.consumer_supplies = &zoom_vmmc3_supply,
+};
+
+static struct fixed_voltage_config zoom_vwlan = {
+	.supply_name		= "vwl1271",
+	.microvolts		= 1800000, /* 1.8V */
+	.gpio			= OMAP_ZOOM_WLAN_PMENA_GPIO,
+	.startup_delay		= 70000, /* 70msec */
+	.enable_high		= 1,
+	.enabled_at_boot	= 0,
+	.init_data		= &zoom_vmmc3,
+};
+
+static struct platform_device omap_vwlan_device = {
+	.name		= "reg-fixed-voltage",
+	.id		= 1,
+	.dev = {
+		.platform_data	= &zoom_vwlan,
+	},
+};
+
+struct wl12xx_platform_data omap_zoom_wlan_data __initdata = {
+	.irq = OMAP_GPIO_IRQ(OMAP_ZOOM_WLAN_IRQ_GPIO),
+	/* ZOOM ref clock is 26 MHz */
+	.board_ref_clock = 1,
+};
+
 static struct omap2_hsmmc_info mmc[] __initdata = {
 	{
 		.name		= "external",
@@ -168,6 +210,14 @@
 		.nonremovable	= true,
 		.power_saving	= true,
 	},
+	{
+		.name		= "wl1271",
+		.mmc		= 3,
+		.wires		= 4,
+		.gpio_wp	= -EINVAL,
+		.gpio_cd	= -EINVAL,
+		.nonremovable	= true,
+	},
 	{}      /* Terminator */
 };
 
@@ -279,7 +329,11 @@
 
 void __init zoom_peripherals_init(void)
 {
+	if (wl12xx_set_platform_data(&omap_zoom_wlan_data))
+		pr_err("error setting wl12xx data\n");
+
 	omap_i2c_init();
+	platform_device_register(&omap_vwlan_device);
 	usb_musb_init(&musb_board_data);
 	enable_board_wakeup_source();
 }
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 5d4ce4d..85af697 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -50,5 +50,7 @@
 obj-$(CONFIG_MAC80211_HWSIM)	+= mac80211_hwsim.o
 
 obj-$(CONFIG_WL12XX)	+= wl12xx/
+# small builtin driver bit
+obj-$(CONFIG_WL12XX_PLATFORM_DATA)	+= wl12xx/wl12xx_platform_data.o
 
 obj-$(CONFIG_IWM)	+= iwmc3200wifi/
diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c
index 7d26506..924ed09 100644
--- a/drivers/net/wireless/airo.c
+++ b/drivers/net/wireless/airo.c
@@ -105,7 +105,7 @@
    of statistics in the /proc filesystem */
 
 #define IGNLABEL(comment) NULL
-static char *statsLabels[] = {
+static const char *statsLabels[] = {
 	"RxOverrun",
 	IGNLABEL("RxPlcpCrcErr"),
 	IGNLABEL("RxPlcpFormatErr"),
@@ -932,7 +932,7 @@
 	unsigned char __user *data;	// d-data
 } aironet_ioctl;
 
-static char swversion[] = "2.1";
+static const char swversion[] = "2.1";
 #endif /* CISCO_EXT */
 
 #define NUM_MODULES       2
@@ -1374,7 +1374,7 @@
 	return SUCCESS;
 }
 
-static char micsnap[] = {0xAA,0xAA,0x03,0x00,0x40,0x96,0x00,0x02};
+static const u8 micsnap[] = {0xAA,0xAA,0x03,0x00,0x40,0x96,0x00,0x02};
 
 /*===========================================================================
  * Description: Mic a packet
@@ -5023,7 +5023,7 @@
 	airo_config_commit(dev, NULL, NULL, NULL);
 }
 
-static char *get_rmode(__le16 mode)
+static const char *get_rmode(__le16 mode)
 {
         switch(mode & RXMODE_MASK) {
         case RXMODE_RFMON:  return "rfmon";
diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig
index 0a75be0..92c2162 100644
--- a/drivers/net/wireless/ath/Kconfig
+++ b/drivers/net/wireless/ath/Kconfig
@@ -25,5 +25,6 @@
 source "drivers/net/wireless/ath/ath5k/Kconfig"
 source "drivers/net/wireless/ath/ath9k/Kconfig"
 source "drivers/net/wireless/ath/ar9170/Kconfig"
+source "drivers/net/wireless/ath/carl9170/Kconfig"
 
 endif
diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile
index 8113a50..6d711ec 100644
--- a/drivers/net/wireless/ath/Makefile
+++ b/drivers/net/wireless/ath/Makefile
@@ -1,11 +1,13 @@
 obj-$(CONFIG_ATH5K)		+= ath5k/
 obj-$(CONFIG_ATH9K_HW)		+= ath9k/
 obj-$(CONFIG_AR9170_USB)        += ar9170/
+obj-$(CONFIG_CARL9170)		+= carl9170/
 
 obj-$(CONFIG_ATH_COMMON)	+= ath.o
 
 ath-objs :=	main.o \
 		regd.o \
-		hw.o
+		hw.o \
+		key.o
 
 ath-$(CONFIG_ATH_DEBUG) += debug.o
diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h
index a706202..dd236c3 100644
--- a/drivers/net/wireless/ath/ath.h
+++ b/drivers/net/wireless/ath/ath.h
@@ -71,6 +71,32 @@
 	struct reg_dmn_pair_mapping *regpair;
 };
 
+enum ath_crypt_caps {
+	ATH_CRYPT_CAP_CIPHER_AESCCM		= BIT(0),
+	ATH_CRYPT_CAP_MIC_COMBINED		= BIT(1),
+};
+
+struct ath_keyval {
+	u8 kv_type;
+	u8 kv_pad;
+	u16 kv_len;
+	u8 kv_val[16]; /* TK */
+	u8 kv_mic[8]; /* Michael MIC key */
+	u8 kv_txmic[8]; /* Michael MIC TX key (used only if the hardware
+			 * supports both MIC keys in the same key cache entry;
+			 * in that case, kv_mic is the RX key) */
+};
+
+enum ath_cipher {
+	ATH_CIPHER_WEP = 0,
+	ATH_CIPHER_AES_OCB = 1,
+	ATH_CIPHER_AES_CCM = 2,
+	ATH_CIPHER_CKIP = 3,
+	ATH_CIPHER_TKIP = 4,
+	ATH_CIPHER_CLR = 5,
+	ATH_CIPHER_MIC = 127
+};
+
 /**
  * struct ath_ops - Register read/write operations
  *
@@ -120,7 +146,7 @@
 	u32 keymax;
 	DECLARE_BITMAP(keymap, ATH_KEYMAX);
 	DECLARE_BITMAP(tkip_keymap, ATH_KEYMAX);
-	u8 splitmic;
+	enum ath_crypt_caps crypt_caps;
 
 	struct ath_regulatory regulatory;
 	const struct ath_ops *ops;
@@ -132,5 +158,11 @@
 				gfp_t gfp_mask);
 
 void ath_hw_setbssidmask(struct ath_common *common);
+void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf *key);
+int ath_key_config(struct ath_common *common,
+			  struct ieee80211_vif *vif,
+			  struct ieee80211_sta *sta,
+			  struct ieee80211_key_conf *key);
+bool ath_hw_keyreset(struct ath_common *common, u16 entry);
 
 #endif /* ATH_H */
diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h
index f399c4d..b96bb98 100644
--- a/drivers/net/wireless/ath/ath5k/ath5k.h
+++ b/drivers/net/wireless/ath/ath5k/ath5k.h
@@ -206,6 +206,8 @@
 #define ATH5K_TUNE_CALIBRATION_INTERVAL_ANI	1000	/* 1 sec */
 #define ATH5K_TUNE_CALIBRATION_INTERVAL_NF	60000	/* 60 sec */
 
+#define ATH5K_TX_COMPLETE_POLL_INT		3000	/* 3 sec */
+
 #define AR5K_INIT_CARR_SENSE_EN			1
 
 /*Swap RX/TX Descriptor for big endian archs*/
@@ -256,8 +258,6 @@
 	(AR5K_INIT_PROG_IFS_TURBO)					\
 )
 
-/* token to use for aifs, cwmin, cwmax in MadWiFi */
-#define	AR5K_TXQ_USEDEFAULT	((u32) -1)
 
 /* GENERIC CHIPSET DEFINITIONS */
 
@@ -528,9 +528,9 @@
 	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 */
+	u8	tqi_aifs;	/* Arbitrated Interframe Space */
+	u16	tqi_cw_min;	/* Minimum Contention Window */
+	u16	tqi_cw_max;	/* Maximum Contention Window */
 	u32	tqi_cbr_period; /* Constant bit rate period */
 	u32	tqi_cbr_overflow_limit;
 	u32	tqi_burst_time;
@@ -1028,8 +1028,6 @@
 	bool			ah_turbo;
 	bool			ah_calibration;
 	bool			ah_single_chip;
-	bool			ah_aes_support;
-	bool			ah_combined_mic;
 
 	enum ath5k_version	ah_version;
 	enum ath5k_radio	ah_radio;
@@ -1044,9 +1042,6 @@
 #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;
 	u32			ah_limit_tx_retries;
 	u8			ah_coverage_class;
 
@@ -1207,11 +1202,6 @@
 unsigned int ath5k_hw_htoclock(struct ath5k_hw *ah, unsigned int usec);
 unsigned int ath5k_hw_clocktoh(struct ath5k_hw *ah, unsigned int clock);
 unsigned int ath5k_hw_get_clockrate(struct ath5k_hw *ah);
-/* Key table (WEP) functions */
-int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry);
-int ath5k_hw_set_key(struct ath5k_hw *ah, u16 entry,
-		     const struct ieee80211_key_conf *key, const u8 *mac);
-int ath5k_hw_set_key_lladdr(struct ath5k_hw *ah, u16 entry, const u8 *mac);
 
 /* Queue Control Unit, DFS Control Unit Functions */
 int ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue,
diff --git a/drivers/net/wireless/ath/ath5k/attach.c b/drivers/net/wireless/ath/ath5k/attach.c
index aabad4f..6e02de3 100644
--- a/drivers/net/wireless/ath/ath5k/attach.c
+++ b/drivers/net/wireless/ath/ath5k/attach.c
@@ -119,8 +119,6 @@
 	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_mode = AR5K_ANTMODE_DEFAULT;
@@ -314,12 +312,16 @@
 	}
 
 	/* Crypto settings */
-	ah->ah_aes_support = srev >= AR5K_SREV_AR5212_V4 &&
-		(ee->ee_version >= AR5K_EEPROM_VERSION_5_0 &&
-		 !AR5K_EEPROM_AES_DIS(ee->ee_misc5));
+	common->keymax = (sc->ah->ah_version == AR5K_AR5210 ?
+			  AR5K_KEYTABLE_SIZE_5210 : AR5K_KEYTABLE_SIZE_5211);
+
+	if (srev >= AR5K_SREV_AR5212_V4 &&
+	    (ee->ee_version >= AR5K_EEPROM_VERSION_5_0 &&
+	    !AR5K_EEPROM_AES_DIS(ee->ee_misc5)))
+		common->crypt_caps |= ATH_CRYPT_CAP_CIPHER_AESCCM;
 
 	if (srev >= AR5K_SREV_AR2414) {
-		ah->ah_combined_mic = true;
+		common->crypt_caps |= ATH_CRYPT_CAP_MIC_COMBINED;
 		AR5K_REG_ENABLE_BITS(ah, AR5K_MISC_MODE,
 			AR5K_MISC_MODE_COMBINED_MIC);
 	}
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index 116ac66..95072db 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -70,11 +70,6 @@
 module_param_named(all_channels, modparam_all_channels, bool, S_IRUGO);
 MODULE_PARM_DESC(all_channels, "Expose all channels the device can use.");
 
-
-/******************\
-* Internal defines *
-\******************/
-
 /* Module info */
 MODULE_AUTHOR("Jiri Slaby");
 MODULE_AUTHOR("Nick Kossifidis");
@@ -83,6 +78,10 @@
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_VERSION("0.6.0 (EXPERIMENTAL)");
 
+static int ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan);
+static int ath5k_beacon_update(struct ieee80211_hw *hw,
+		struct ieee80211_vif *vif);
+static void ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf);
 
 /* Known PCI ids */
 static DEFINE_PCI_DEVICE_TABLE(ath5k_pci_id_table) = {
@@ -190,129 +189,6 @@
 	/* XR missing */
 };
 
-/*
- * 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_SLEEP
-static int		ath5k_pci_suspend(struct device *dev);
-static int		ath5k_pci_resume(struct device *dev);
-
-static SIMPLE_DEV_PM_OPS(ath5k_pm_ops, ath5k_pci_suspend, ath5k_pci_resume);
-#define ATH5K_PM_OPS	(&ath5k_pm_ops)
-#else
-#define ATH5K_PM_OPS	NULL
-#endif /* CONFIG_PM_SLEEP */
-
-static struct pci_driver ath5k_pci_driver = {
-	.name		= KBUILD_MODNAME,
-	.id_table	= ath5k_pci_id_table,
-	.probe		= ath5k_pci_probe,
-	.remove		= __devexit_p(ath5k_pci_remove),
-	.driver.pm	= ATH5K_PM_OPS,
-};
-
-
-
-/*
- * Prototypes - MAC 802.11 stack related functions
- */
-static int ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb);
-static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
-		struct ath5k_txq *txq);
-static int ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan);
-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_vif *vif);
-static void ath5k_remove_interface(struct ieee80211_hw *hw,
-		struct ieee80211_vif *vif);
-static int ath5k_config(struct ieee80211_hw *hw, u32 changed);
-static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw,
-				   struct netdev_hw_addr_list *mc_list);
-static void ath5k_configure_filter(struct ieee80211_hw *hw,
-		unsigned int changed_flags,
-		unsigned int *new_flags,
-		u64 multicast);
-static int ath5k_set_key(struct ieee80211_hw *hw,
-		enum set_key_cmd cmd,
-		struct ieee80211_vif *vif, struct ieee80211_sta *sta,
-		struct ieee80211_key_conf *key);
-static int ath5k_get_stats(struct ieee80211_hw *hw,
-		struct ieee80211_low_level_stats *stats);
-static int ath5k_get_survey(struct ieee80211_hw *hw,
-		int idx, struct survey_info *survey);
-static u64 ath5k_get_tsf(struct ieee80211_hw *hw);
-static void ath5k_set_tsf(struct ieee80211_hw *hw, u64 tsf);
-static void ath5k_reset_tsf(struct ieee80211_hw *hw);
-static int ath5k_beacon_update(struct ieee80211_hw *hw,
-		struct ieee80211_vif *vif);
-static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
-		struct ieee80211_vif *vif,
-		struct ieee80211_bss_conf *bss_conf,
-		u32 changes);
-static void ath5k_sw_scan_start(struct ieee80211_hw *hw);
-static void ath5k_sw_scan_complete(struct ieee80211_hw *hw);
-static void ath5k_set_coverage_class(struct ieee80211_hw *hw,
-		u8 coverage_class);
-
-static const 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,
-	.prepare_multicast = ath5k_prepare_multicast,
-	.configure_filter = ath5k_configure_filter,
-	.set_key 	= ath5k_set_key,
-	.get_stats 	= ath5k_get_stats,
-	.get_survey	= ath5k_get_survey,
-	.conf_tx 	= NULL,
-	.get_tsf 	= ath5k_get_tsf,
-	.set_tsf 	= ath5k_set_tsf,
-	.reset_tsf 	= ath5k_reset_tsf,
-	.bss_info_changed = ath5k_bss_info_changed,
-	.sw_scan_start	= ath5k_sw_scan_start,
-	.sw_scan_complete = ath5k_sw_scan_complete,
-	.set_coverage_class = ath5k_set_coverage_class,
-};
-
-/*
- * 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_channels(struct ath5k_hw *ah,
-				struct ieee80211_channel *channels,
-				unsigned int mode,
-				unsigned int max);
-static int 	ath5k_setup_bands(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 ath5k_txq *txq, int padsize);
-
 static inline void ath5k_txbuf_free_skb(struct ath5k_softc *sc,
 				struct ath5k_buf *bf)
 {
@@ -345,35 +221,6 @@
 }
 
 
-/* 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 sk_buff *skb,
-					struct ath5k_rx_status *rs);
-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);
-static void 	ath5k_beacon_send(struct ath5k_softc *sc);
-static void 	ath5k_beacon_config(struct ath5k_softc *sc);
-static void	ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf);
-static void	ath5k_tasklet_beacon(unsigned long data);
-static void	ath5k_tasklet_ani(unsigned long data);
-
 static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp)
 {
 	u64 tsf = ath5k_hw_get_tsf64(ah);
@@ -384,50 +231,6 @@
 	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_reset_work(struct work_struct *work);
-
-static void 	ath5k_tasklet_calibrate(unsigned long data);
-
-/*
- * Module init/exit functions
- */
-static int __init
-init_ath5k_pci(void)
-{
-	int ret;
-
-	ath5k_debug_init();
-
-	ret = pci_register_driver(&ath5k_pci_driver);
-	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_driver);
-
-	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)
 {
@@ -466,299 +269,6 @@
 	.write = ath5k_iowrite32,
 };
 
-static int __devinit
-ath5k_pci_probe(struct pci_dev *pdev,
-		const struct pci_device_id *id)
-{
-	void __iomem *mem;
-	struct ath5k_softc *sc;
-	struct ath_common *common;
-	struct ieee80211_hw *hw;
-	int ret;
-	u8 csz;
-
-	/*
-	 * L0s needs to be disabled on all ath5k cards.
-	 *
-	 * For distributions shipping with CONFIG_PCIEASPM (this will be enabled
-	 * by default in the future in 2.6.36) this will also mean both L1 and
-	 * L0s will be disabled when a pre 1.1 PCIe device is detected. We do
-	 * know L1 works correctly even for all ath5k pre 1.1 PCIe devices
-	 * though but cannot currently undue the effect of a blacklist, for
-	 * details you can read pcie_aspm_sanity_check() and see how it adjusts
-	 * the device link capability.
-	 *
-	 * It may be possible in the future to implement some PCI API to allow
-	 * drivers to override blacklists for pre 1.1 PCIe but for now it is
-	 * best to accept that both L0s and L1 will be disabled completely for
-	 * distributions shipping with CONFIG_PCIEASPM rather than having this
-	 * issue present. Motivation for adding this new API will be to help
-	 * with power consumption for some of these devices.
-	 */
-	pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S);
-
-	ret = pci_enable_device(pdev);
-	if (ret) {
-		dev_err(&pdev->dev, "can't enable device\n");
-		goto err;
-	}
-
-	/* XXX 32-bit addressing only */
-	ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
-	if (ret) {
-		dev_err(&pdev->dev, "32-bit DMA not available\n");
-		goto err_dis;
-	}
-
-	/*
-	 * Cache line size is used to size and align various
-	 * structures used to communicate with the hardware.
-	 */
-	pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &csz);
-	if (csz == 0) {
-		/*
-		 * Linux 2.4.18 (at least) writes the cache line size
-		 * register as a 16-bit wide register which is wrong.
-		 * We must have this setup properly for rx buffer
-		 * DMA to work so force a reasonable value here if it
-		 * comes up zero.
-		 */
-		csz = L1_CACHE_BYTES >> 2;
-		pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, csz);
-	}
-	/*
-	 * The default setting of latency timer yields poor results,
-	 * set it to the value used by other systems.  It may be worth
-	 * tweaking this setting more.
-	 */
-	pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8);
-
-	/* Enable bus mastering */
-	pci_set_master(pdev);
-
-	/*
-	 * Disable the RETRY_TIMEOUT register (0x41) to keep
-	 * PCI Tx retries from interfering with C3 CPU state.
-	 */
-	pci_write_config_byte(pdev, 0x41, 0);
-
-	ret = pci_request_region(pdev, 0, "ath5k");
-	if (ret) {
-		dev_err(&pdev->dev, "cannot reserve PCI memory region\n");
-		goto err_dis;
-	}
-
-	mem = pci_iomap(pdev, 0, 0);
-	if (!mem) {
-		dev_err(&pdev->dev, "cannot remap PCI memory region\n") ;
-		ret = -EIO;
-		goto err_reg;
-	}
-
-	/*
-	 * Allocate hw (mac80211 main struct)
-	 * and hw->priv (driver private data)
-	 */
-	hw = ieee80211_alloc_hw(sizeof(*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 |
-		    IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
-		    IEEE80211_HW_SIGNAL_DBM;
-
-	hw->wiphy->interface_modes =
-		BIT(NL80211_IFTYPE_AP) |
-		BIT(NL80211_IFTYPE_STATION) |
-		BIT(NL80211_IFTYPE_ADHOC) |
-		BIT(NL80211_IFTYPE_MESH_POINT);
-
-	hw->extra_tx_headroom = 2;
-	hw->channel_change_time = 5000;
-	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->opmode = NL80211_IFTYPE_STATION;
-	sc->bintval = 1000;
-	mutex_init(&sc->lock);
-	spin_lock_init(&sc->rxbuflock);
-	spin_lock_init(&sc->txbuflock);
-	spin_lock_init(&sc->block);
-
-	/* Set private data */
-	pci_set_drvdata(pdev, sc);
-
-	/* 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;
-	}
-
-	/* If we passed the test, malloc an ath5k_hw struct */
-	sc->ah = kzalloc(sizeof(struct ath5k_hw), GFP_KERNEL);
-	if (!sc->ah) {
-		ret = -ENOMEM;
-		ATH5K_ERR(sc, "out of memory\n");
-		goto err_irq;
-	}
-
-	sc->ah->ah_sc = sc;
-	sc->ah->ah_iobase = sc->iobase;
-	common = ath5k_hw_common(sc->ah);
-	common->ops = &ath5k_common_ops;
-	common->ah = sc->ah;
-	common->hw = hw;
-	common->cachelsz = csz << 2; /* convert to bytes */
-
-	/* Initialize device */
-	ret = ath5k_hw_attach(sc);
-	if (ret) {
-		goto err_free_ah;
-	}
-
-	/* set up multi-rate retry capabilities */
-	if (sc->ah->ah_version == AR5K_AR5212) {
-		hw->max_rates = 4;
-		hw->max_rate_tries = 11;
-	}
-
-	/* 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_MAC, 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(AR5K_MODE_11A,
-				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(AR5K_MODE_11B,
-				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_free_ah:
-	kfree(sc->ah);
-err_irq:
-	free_irq(pdev->irq, sc);
-err_free:
-	ieee80211_free_hw(hw);
-err_map:
-	pci_iounmap(pdev, mem);
-err_reg:
-	pci_release_region(pdev, 0);
-err_dis:
-	pci_disable_device(pdev);
-err:
-	return ret;
-}
-
-static void __devexit
-ath5k_pci_remove(struct pci_dev *pdev)
-{
-	struct ath5k_softc *sc = pci_get_drvdata(pdev);
-
-	ath5k_debug_finish_device(sc);
-	ath5k_detach(pdev, sc->hw);
-	ath5k_hw_detach(sc->ah);
-	kfree(sc->ah);
-	free_irq(pdev->irq, sc);
-	pci_iounmap(pdev, sc->iobase);
-	pci_release_region(pdev, 0);
-	pci_disable_device(pdev);
-	ieee80211_free_hw(sc->hw);
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int ath5k_pci_suspend(struct device *dev)
-{
-	struct ath5k_softc *sc = pci_get_drvdata(to_pci_dev(dev));
-
-	ath5k_led_off(sc);
-	return 0;
-}
-
-static int ath5k_pci_resume(struct device *dev)
-{
-	struct pci_dev *pdev = to_pci_dev(dev);
-	struct ath5k_softc *sc = pci_get_drvdata(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_led_enable(sc);
-	return 0;
-}
-#endif /* CONFIG_PM_SLEEP */
-
-
 /***********************\
 * Driver Initialization *
 \***********************/
@@ -772,170 +282,6 @@
 	return ath_reg_notifier_apply(wiphy, request, regulatory);
 }
 
-static int
-ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
-{
-	struct ath5k_softc *sc = hw->priv;
-	struct ath5k_hw *ah = sc->ah;
-	struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah);
-	u8 mac[ETH_ALEN] = {};
-	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.  MACs that don't have support will
-	 * return false w/o doing anything.  MACs that do
-	 * support it will return true w/o doing anything.
-	 */
-	ret = ath5k_hw_setup_mrr_tx_desc(ah, NULL, 0, 0, 0, 0, 0, 0);
-
-	if (ret < 0)
-		goto err;
-	if (ret > 0)
-		__set_bit(ATH_STAT_MRRETRY, sc->status);
-
-	/*
-	 * 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_setup_bands(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(AR5K_MODE_11A, ah->ah_modes))
-		ath5k_setcurmode(sc, AR5K_MODE_11A);
-	else
-		ath5k_setcurmode(sc, AR5K_MODE_11B);
-
-	/*
-	 * 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 resetting
-	 * 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->cabq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_CAB, 0);
-	if (IS_ERR(sc->cabq)) {
-		ATH5K_ERR(sc, "can't setup cab queue\n");
-		ret = PTR_ERR(sc->cabq);
-		goto err_bhal;
-	}
-
-	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_queues;
-	}
-
-	tasklet_init(&sc->rxtq, ath5k_tasklet_rx, (unsigned long)sc);
-	tasklet_init(&sc->txtq, ath5k_tasklet_tx, (unsigned long)sc);
-	tasklet_init(&sc->calib, ath5k_tasklet_calibrate, (unsigned long)sc);
-	tasklet_init(&sc->beacontq, ath5k_tasklet_beacon, (unsigned long)sc);
-	tasklet_init(&sc->ani_tasklet, ath5k_tasklet_ani, (unsigned long)sc);
-
-	INIT_WORK(&sc->reset_work, ath5k_reset_work);
-
-	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_queues;
-	}
-
-	SET_IEEE80211_PERM_ADDR(hw, mac);
-	/* All MAC address bits matter for ACKs */
-	memcpy(sc->bssidmask, ath_bcast_mac, ETH_ALEN);
-	ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask);
-
-	regulatory->current_rd = ah->ah_capabilities.cap_eeprom.ee_regdomain;
-	ret = ath_regd_init(regulatory, hw->wiphy, ath5k_reg_notifier);
-	if (ret) {
-		ATH5K_ERR(sc, "can't initialize regulatory system\n");
-		goto err_queues;
-	}
-
-	ret = ieee80211_register_hw(hw);
-	if (ret) {
-		ATH5K_ERR(sc, "can't register ieee80211 hw\n");
-		goto err_queues;
-	}
-
-	if (!ath_is_world_regd(regulatory))
-		regulatory_hint(hw->wiphy, regulatory->alpha2);
-
-	ath5k_init_leds(sc);
-
-	ath5k_sysfs_register(sc);
-
-	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
-	 *   ensure 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);
-	ath5k_unregister_leds(sc);
-
-	ath5k_sysfs_unregister(sc);
-	/*
-	 * 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 *
 \********************/
@@ -1391,6 +737,7 @@
 
 	spin_lock_bh(&txq->lock);
 	list_add_tail(&bf->list, &txq->q);
+	txq->txq_len++;
 	if (txq->link == NULL) /* is this first packet? */
 		ath5k_hw_set_txdp(ah, txq->qnum, bf->daddr);
 	else /* no, so only link it */
@@ -1494,9 +841,6 @@
 }
 
 
-
-
-
 /**************\
 * Queues setup *
 \**************/
@@ -1509,9 +853,11 @@
 	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
+		/* XXX: default values not correct for B and XR channels,
+		 * but who cares? */
+		.tqi_aifs = AR5K_TUNE_AIFS,
+		.tqi_cw_min = AR5K_TUNE_CWMIN,
+		.tqi_cw_max = AR5K_TUNE_CWMAX
 	};
 	int qnum;
 
@@ -1550,6 +896,9 @@
 		INIT_LIST_HEAD(&txq->q);
 		spin_lock_init(&txq->lock);
 		txq->setup = true;
+		txq->txq_len = 0;
+		txq->txq_poll_mark = false;
+		txq->txq_stuck = 0;
 	}
 	return &sc->txqs[qnum];
 }
@@ -1558,9 +907,11 @@
 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,
+		/* XXX: default values not correct for B and XR channels,
+		 * but who cares? */
+		.tqi_aifs = AR5K_TUNE_AIFS,
+		.tqi_cw_min = AR5K_TUNE_CWMIN,
+		.tqi_cw_max = AR5K_TUNE_CWMAX,
 		/* NB: for dynamic turbo, don't enable any other interrupts */
 		.tqi_flags = AR5K_TXQ_FLAG_TXDESCINT_ENABLE
 	};
@@ -1594,7 +945,7 @@
 		 */
 		qi.tqi_aifs = 0;
 		qi.tqi_cw_min = 0;
-		qi.tqi_cw_max = 2 * ah->ah_cw_min;
+		qi.tqi_cw_max = 2 * AR5K_TUNE_CWMIN;
 	}
 
 	ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
@@ -1644,9 +995,11 @@
 		spin_lock_bh(&sc->txbuflock);
 		list_move_tail(&bf->list, &sc->txbuf);
 		sc->txbuf_len++;
+		txq->txq_len--;
 		spin_unlock_bh(&sc->txbuflock);
 	}
 	txq->link = NULL;
+	txq->txq_poll_mark = false;
 	spin_unlock_bh(&txq->lock);
 }
 
@@ -1696,8 +1049,6 @@
 }
 
 
-
-
 /*************\
 * RX Handling *
 \*************/
@@ -2121,6 +1472,117 @@
 * TX Handling *
 \*************/
 
+static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
+			  struct ath5k_txq *txq)
+{
+	struct ath5k_softc *sc = hw->priv;
+	struct ath5k_buf *bf;
+	unsigned long flags;
+	int padsize;
+
+	ath5k_debug_dump_skb(sc, skb, "TX  ", 1);
+
+	/*
+	 * The hardware expects the header padded to 4 byte boundaries.
+	 * If this is not the case, we add the padding after the header.
+	 */
+	padsize = ath5k_add_padding(skb);
+	if (padsize < 0) {
+		ATH5K_ERR(sc, "tx hdrlen not %%4: not enough"
+			  " headroom to pad");
+		goto drop_packet;
+	}
+
+	if (txq->txq_len >= ATH5K_TXQ_LEN_MAX)
+		ieee80211_stop_queue(hw, txq->qnum);
+
+	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_queues(hw);
+		goto drop_packet;
+	}
+	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, txq, padsize)) {
+		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);
+		goto drop_packet;
+	}
+	return NETDEV_TX_OK;
+
+drop_packet:
+	dev_kfree_skb_any(skb);
+	return NETDEV_TX_OK;
+}
+
+static void
+ath5k_tx_frame_completed(struct ath5k_softc *sc, struct sk_buff *skb,
+			 struct ath5k_tx_status *ts)
+{
+	struct ieee80211_tx_info *info;
+	int i;
+
+	sc->stats.tx_all_count++;
+	info = IEEE80211_SKB_CB(skb);
+
+	ieee80211_tx_info_clear_status(info);
+	for (i = 0; i < 4; i++) {
+		struct ieee80211_tx_rate *r =
+			&info->status.rates[i];
+
+		if (ts->ts_rate[i]) {
+			r->idx = ath5k_hw_to_driver_rix(sc, ts->ts_rate[i]);
+			r->count = ts->ts_retry[i];
+		} else {
+			r->idx = -1;
+			r->count = 0;
+		}
+	}
+
+	/* count the successful attempt as well */
+	info->status.rates[ts->ts_final_idx].count++;
+
+	if (unlikely(ts->ts_status)) {
+		sc->stats.ack_fail++;
+		if (ts->ts_status & AR5K_TXERR_FILT) {
+			info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
+			sc->stats.txerr_filt++;
+		}
+		if (ts->ts_status & AR5K_TXERR_XRETRY)
+			sc->stats.txerr_retry++;
+		if (ts->ts_status & AR5K_TXERR_FIFO)
+			sc->stats.txerr_fifo++;
+	} else {
+		info->flags |= IEEE80211_TX_STAT_ACK;
+		info->status.ack_signal = ts->ts_rssi;
+	}
+
+	/*
+	* Remove MAC header padding before giving the frame
+	* back to mac80211.
+	*/
+	ath5k_remove_padding(skb);
+
+	if (ts->ts_antenna > 0 && ts->ts_antenna < 5)
+		sc->stats.antenna_tx[ts->ts_antenna]++;
+	else
+		sc->stats.antenna_tx[0]++; /* invalid */
+
+	ieee80211_tx_status(sc->hw, skb);
+}
+
 static void
 ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
 {
@@ -2128,96 +1590,51 @@
 	struct ath5k_buf *bf, *bf0;
 	struct ath5k_desc *ds;
 	struct sk_buff *skb;
-	struct ieee80211_tx_info *info;
-	int i, ret;
+	int ret;
 
 	spin_lock(&txq->lock);
 	list_for_each_entry_safe(bf, bf0, &txq->q, list) {
-		ds = bf->desc;
+
+		txq->txq_poll_mark = false;
+
+		/* skb might already have been processed last time. */
+		if (bf->skb != NULL) {
+			ds = bf->desc;
+
+			ret = sc->ah->ah_proc_tx_desc(sc->ah, ds, &ts);
+			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);
+			ath5k_tx_frame_completed(sc, skb, &ts);
+		}
 
 		/*
 		 * It's possible that the hardware can say the buffer is
 		 * completed when it hasn't yet loaded the ds_link from
-		 * host memory and moved on.  If there are more TX
-		 * descriptors in the queue, wait for TXDP to change
-		 * before processing this one.
+		 * host memory and moved on.
+		 * Always keep the last descriptor to avoid HW races...
 		 */
-		if (ath5k_hw_get_txdp(sc->ah, txq->qnum) == bf->daddr &&
-		    !list_is_last(&bf->list, &txq->q))
-			break;
-
-		ret = sc->ah->ah_proc_tx_desc(sc->ah, ds, &ts);
-		if (unlikely(ret == -EINPROGRESS))
-			break;
-		else if (unlikely(ret)) {
-			ATH5K_ERR(sc, "error %d while processing queue %u\n",
-				ret, txq->qnum);
-			break;
+		if (ath5k_hw_get_txdp(sc->ah, txq->qnum) != bf->daddr) {
+			spin_lock(&sc->txbuflock);
+			list_move_tail(&bf->list, &sc->txbuf);
+			sc->txbuf_len++;
+			txq->txq_len--;
+			spin_unlock(&sc->txbuflock);
 		}
-
-		sc->stats.tx_all_count++;
-		skb = bf->skb;
-		info = IEEE80211_SKB_CB(skb);
-		bf->skb = NULL;
-
-		pci_unmap_single(sc->pdev, bf->skbaddr, skb->len,
-				PCI_DMA_TODEVICE);
-
-		ieee80211_tx_info_clear_status(info);
-		for (i = 0; i < 4; i++) {
-			struct ieee80211_tx_rate *r =
-				&info->status.rates[i];
-
-			if (ts.ts_rate[i]) {
-				r->idx = ath5k_hw_to_driver_rix(sc, ts.ts_rate[i]);
-				r->count = ts.ts_retry[i];
-			} else {
-				r->idx = -1;
-				r->count = 0;
-			}
-		}
-
-		/* count the successful attempt as well */
-		info->status.rates[ts.ts_final_idx].count++;
-
-		if (unlikely(ts.ts_status)) {
-			sc->stats.ack_fail++;
-			if (ts.ts_status & AR5K_TXERR_FILT) {
-				info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
-				sc->stats.txerr_filt++;
-			}
-			if (ts.ts_status & AR5K_TXERR_XRETRY)
-				sc->stats.txerr_retry++;
-			if (ts.ts_status & AR5K_TXERR_FIFO)
-				sc->stats.txerr_fifo++;
-		} else {
-			info->flags |= IEEE80211_TX_STAT_ACK;
-			info->status.ack_signal = ts.ts_rssi;
-		}
-
-		/*
-		 * Remove MAC header padding before giving the frame
-		 * back to mac80211.
-		 */
-		ath5k_remove_padding(skb);
-
-		if (ts.ts_antenna > 0 && ts.ts_antenna < 5)
-			sc->stats.antenna_tx[ts.ts_antenna]++;
-		else
-			sc->stats.antenna_tx[0]++; /* invalid */
-
-		ieee80211_tx_status(sc->hw, skb);
-
-		spin_lock(&sc->txbuflock);
-		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);
+	if (txq->txq_len < ATH5K_TXQ_LEN_LOW)
+		ieee80211_wake_queue(sc->hw, txq->qnum);
 }
 
 static void
@@ -2313,6 +1730,43 @@
 }
 
 /*
+ * Updates the beacon that is sent by ath5k_beacon_send.  For adhoc,
+ * this is called only once at config_bss time, for AP we do it every
+ * SWBA interrupt so that the TIM will reflect buffered frames.
+ *
+ * Called with the beacon lock.
+ */
+static int
+ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+	int ret;
+	struct ath5k_softc *sc = hw->priv;
+	struct sk_buff *skb;
+
+	if (WARN_ON(!vif)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	skb = ieee80211_beacon_get(hw, vif);
+
+	if (!skb) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ath5k_debug_dump_skb(sc, skb, "BC  ", 1);
+
+	ath5k_txbuf_free_skb(sc, sc->bbuf);
+	sc->bbuf->skb = skb;
+	ret = ath5k_beacon_setup(sc, sc->bbuf);
+	if (ret)
+		sc->bbuf->skb = NULL;
+out:
+	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.
@@ -2389,7 +1843,6 @@
 	sc->bsent++;
 }
 
-
 /**
  * ath5k_beacon_update_timers - update beacon timers
  *
@@ -2491,7 +1944,6 @@
 		intval & AR5K_BEACON_RESET_TSF ? "AR5K_BEACON_RESET_TSF" : "");
 }
 
-
 /**
  * ath5k_beacon_config - Configure the beacon queues and interrupts
  *
@@ -2570,155 +2022,6 @@
 * Interrupt handling *
 \********************/
 
-static int
-ath5k_init(struct ath5k_softc *sc)
-{
-	struct ath5k_hw *ah = sc->ah;
-	int ret, i;
-
-	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.channel;
-	sc->curband = &sc->sbands[sc->curchan->band];
-	sc->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL |
-		AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL |
-		AR5K_INT_FATAL | AR5K_INT_GLOBAL | AR5K_INT_MIB;
-
-	ret = ath5k_reset(sc, NULL);
-	if (ret)
-		goto done;
-
-	ath5k_rfkill_hw_start(ah);
-
-	/*
-	 * Reset the key cache since some parts do not reset the
-	 * contents on initial power up or resume from suspend.
-	 */
-	for (i = 0; i < AR5K_KEYTABLE_SIZE; i++)
-		ath5k_hw_reset_key(ah, i);
-
-	ath5k_hw_set_ack_bitrate_high(ah, true);
-	ret = 0;
-done:
-	mmiowb();
-	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)) {
-		ath5k_led_off(sc);
-		ath5k_hw_set_imr(ah, 0);
-		synchronize_irq(sc->pdev->irq);
-	}
-	ath5k_txq_cleanup(sc);
-	if (!test_bit(ATH_STAT_INVALID, sc->status)) {
-		ath5k_rx_stop(sc);
-		ath5k_hw_phy_disable(ah);
-	}
-
-	return 0;
-}
-
-static void stop_tasklets(struct ath5k_softc *sc)
-{
-	tasklet_kill(&sc->rxtq);
-	tasklet_kill(&sc->txtq);
-	tasklet_kill(&sc->calib);
-	tasklet_kill(&sc->beacontq);
-	tasklet_kill(&sc->ani_tasklet);
-}
-
-/*
- * 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)) {
-		/*
-		 * Don't set the card in full sleep mode!
-		 *
-		 * a) When the device 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
-		 * (madwifi sources mentioned srev >= 0x78) that go to
-		 * sleep more quickly.
-		 *
-		 * b) On older chips full sleep results a weird behaviour
-		 * during wakeup. I tested various cards with srev < 0x78
-		 * and they don't wake up after module reload, a second
-		 * module reload is needed to bring the card up again.
-		 *
-		 * Until we figure out what's going on don't enable
-		 * full chip reset on any chip (this is what Legacy HAL
-		 * and Sam's HAL do anyway). Instead Perform a full reset
-		 * on the device (same as initial state after attach) and
-		 * leave it idle (keep MAC/BB on warm reset) */
-		ret = ath5k_hw_on_hold(sc->ah);
-
-		ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
-				"putting device to sleep\n");
-	}
-	ath5k_txbuf_free_skb(sc, sc->bbuf);
-
-	mmiowb();
-	mutex_unlock(&sc->lock);
-
-	stop_tasklets(sc);
-
-	ath5k_rfkill_hw_stop(sc->ah);
-
-	return ret;
-}
-
 static void
 ath5k_intr_calibration_poll(struct ath5k_hw *ah)
 {
@@ -2855,14 +2158,13 @@
 				sc->curchan->center_freq));
 
 	/* Noise floor calibration interrupts rx/tx path while I/Q calibration
-	 * doesn't. We stop the queues so that calibration doesn't interfere
-	 * with TX and don't run it as often */
+	 * doesn't.
+	 * TODO: We should stop TX here, so that it doesn't interfere.
+	 * Note that stopping the queues is not enough to stop TX! */
 	if (time_is_before_eq_jiffies(ah->ah_cal_next_nf)) {
 		ah->ah_cal_next_nf = jiffies +
 			msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_NF);
-		ieee80211_stop_queues(sc->hw);
 		ath5k_hw_update_noise_floor(ah);
-		ieee80211_wake_queues(sc->hw);
 	}
 
 	ah->ah_cal_mask &= ~AR5K_CALIBRATION_FULL;
@@ -2881,68 +2183,205 @@
 }
 
 
-/********************\
-* Mac80211 functions *
-\********************/
-
-static int
-ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+static void
+ath5k_tx_complete_poll_work(struct work_struct *work)
 {
-	struct ath5k_softc *sc = hw->priv;
+	struct ath5k_softc *sc = container_of(work, struct ath5k_softc,
+			tx_complete_work.work);
+	struct ath5k_txq *txq;
+	int i;
+	bool needreset = false;
 
-	return ath5k_tx_queue(hw, skb, sc->txq);
+	for (i = 0; i < ARRAY_SIZE(sc->txqs); i++) {
+		if (sc->txqs[i].setup) {
+			txq = &sc->txqs[i];
+			spin_lock_bh(&txq->lock);
+			if (txq->txq_len > 1) {
+				if (txq->txq_poll_mark) {
+					ATH5K_DBG(sc, ATH5K_DEBUG_XMIT,
+						  "TX queue stuck %d\n",
+						  txq->qnum);
+					needreset = true;
+					txq->txq_stuck++;
+					spin_unlock_bh(&txq->lock);
+					break;
+				} else {
+					txq->txq_poll_mark = true;
+				}
+			}
+			spin_unlock_bh(&txq->lock);
+		}
+	}
+
+	if (needreset) {
+		ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
+			  "TX queues stuck, resetting\n");
+		ath5k_reset(sc, sc->curchan);
+	}
+
+	ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work,
+		msecs_to_jiffies(ATH5K_TX_COMPLETE_POLL_INT));
 }
 
-static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
-			  struct ath5k_txq *txq)
-{
-	struct ath5k_softc *sc = hw->priv;
-	struct ath5k_buf *bf;
-	unsigned long flags;
-	int padsize;
 
-	ath5k_debug_dump_skb(sc, skb, "TX  ", 1);
+/*************************\
+* Initialization routines *
+\*************************/
+
+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));
 
 	/*
-	 * The hardware expects the header padded to 4 byte boundaries.
-	 * If this is not the case, we add the padding after the header.
+	 * 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).
 	 */
-	padsize = ath5k_add_padding(skb);
-	if (padsize < 0) {
-		ATH5K_ERR(sc, "tx hdrlen not %%4: not enough"
-			  " headroom to pad");
-		goto drop_packet;
+	ieee80211_stop_queues(sc->hw);
+
+	if (!test_bit(ATH_STAT_INVALID, sc->status)) {
+		ath5k_led_off(sc);
+		ath5k_hw_set_imr(ah, 0);
+		synchronize_irq(sc->pdev->irq);
+	}
+	ath5k_txq_cleanup(sc);
+	if (!test_bit(ATH_STAT_INVALID, sc->status)) {
+		ath5k_rx_stop(sc);
+		ath5k_hw_phy_disable(ah);
 	}
 
-	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, skb_get_queue_mapping(skb));
-		goto drop_packet;
+	return 0;
+}
+
+static int
+ath5k_init(struct ath5k_softc *sc)
+{
+	struct ath5k_hw *ah = sc->ah;
+	struct ath_common *common = ath5k_hw_common(ah);
+	int ret, i;
+
+	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.channel;
+	sc->curband = &sc->sbands[sc->curchan->band];
+	sc->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL |
+		AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL |
+		AR5K_INT_FATAL | AR5K_INT_GLOBAL | AR5K_INT_MIB;
+
+	ret = ath5k_reset(sc, NULL);
+	if (ret)
+		goto done;
+
+	ath5k_rfkill_hw_start(ah);
+
+	/*
+	 * Reset the key cache since some parts do not reset the
+	 * contents on initial power up or resume from suspend.
+	 */
+	for (i = 0; i < common->keymax; i++)
+		ath_hw_keyreset(common, (u16) i);
+
+	ath5k_hw_set_ack_bitrate_high(ah, true);
+	ret = 0;
+done:
+	mmiowb();
+	mutex_unlock(&sc->lock);
+
+	ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work,
+			msecs_to_jiffies(ATH5K_TX_COMPLETE_POLL_INT));
+
+	return ret;
+}
+
+static void stop_tasklets(struct ath5k_softc *sc)
+{
+	tasklet_kill(&sc->rxtq);
+	tasklet_kill(&sc->txtq);
+	tasklet_kill(&sc->calib);
+	tasklet_kill(&sc->beacontq);
+	tasklet_kill(&sc->ani_tasklet);
+}
+
+/*
+ * 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)) {
+		/*
+		 * Don't set the card in full sleep mode!
+		 *
+		 * a) When the device 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
+		 * (madwifi sources mentioned srev >= 0x78) that go to
+		 * sleep more quickly.
+		 *
+		 * b) On older chips full sleep results a weird behaviour
+		 * during wakeup. I tested various cards with srev < 0x78
+		 * and they don't wake up after module reload, a second
+		 * module reload is needed to bring the card up again.
+		 *
+		 * Until we figure out what's going on don't enable
+		 * full chip reset on any chip (this is what Legacy HAL
+		 * and Sam's HAL do anyway). Instead Perform a full reset
+		 * on the device (same as initial state after attach) and
+		 * leave it idle (keep MAC/BB on warm reset) */
+		ret = ath5k_hw_on_hold(sc->ah);
+
+		ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
+				"putting device to sleep\n");
 	}
-	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);
+	ath5k_txbuf_free_skb(sc, sc->bbuf);
 
-	bf->skb = skb;
+	mmiowb();
+	mutex_unlock(&sc->lock);
 
-	if (ath5k_txbuf_setup(sc, bf, txq, padsize)) {
-		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);
-		goto drop_packet;
-	}
-	return NETDEV_TX_OK;
+	stop_tasklets(sc);
 
-drop_packet:
-	dev_kfree_skb_any(skb);
-	return NETDEV_TX_OK;
+	cancel_delayed_work_sync(&sc->tx_complete_work);
+
+	ath5k_rfkill_hw_stop(sc->ah);
+
+	return ret;
 }
 
 /*
@@ -3019,6 +2458,208 @@
 	mutex_unlock(&sc->lock);
 }
 
+static int
+ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
+{
+	struct ath5k_softc *sc = hw->priv;
+	struct ath5k_hw *ah = sc->ah;
+	struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah);
+	struct ath5k_txq *txq;
+	u8 mac[ETH_ALEN] = {};
+	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.  MACs that don't have support will
+	 * return false w/o doing anything.  MACs that do
+	 * support it will return true w/o doing anything.
+	 */
+	ret = ath5k_hw_setup_mrr_tx_desc(ah, NULL, 0, 0, 0, 0, 0, 0);
+
+	if (ret < 0)
+		goto err;
+	if (ret > 0)
+		__set_bit(ATH_STAT_MRRETRY, sc->status);
+
+	/*
+	 * 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_setup_bands(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(AR5K_MODE_11A, ah->ah_modes))
+		ath5k_setcurmode(sc, AR5K_MODE_11A);
+	else
+		ath5k_setcurmode(sc, AR5K_MODE_11B);
+
+	/*
+	 * 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 resetting
+	 * 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->cabq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_CAB, 0);
+	if (IS_ERR(sc->cabq)) {
+		ATH5K_ERR(sc, "can't setup cab queue\n");
+		ret = PTR_ERR(sc->cabq);
+		goto err_bhal;
+	}
+
+	/* This order matches mac80211's queue priority, so we can
+	 * directly use the mac80211 queue number without any mapping */
+	txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_VO);
+	if (IS_ERR(txq)) {
+		ATH5K_ERR(sc, "can't setup xmit queue\n");
+		ret = PTR_ERR(txq);
+		goto err_queues;
+	}
+	txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_VI);
+	if (IS_ERR(txq)) {
+		ATH5K_ERR(sc, "can't setup xmit queue\n");
+		ret = PTR_ERR(txq);
+		goto err_queues;
+	}
+	txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BE);
+	if (IS_ERR(txq)) {
+		ATH5K_ERR(sc, "can't setup xmit queue\n");
+		ret = PTR_ERR(txq);
+		goto err_queues;
+	}
+	txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BK);
+	if (IS_ERR(txq)) {
+		ATH5K_ERR(sc, "can't setup xmit queue\n");
+		ret = PTR_ERR(txq);
+		goto err_queues;
+	}
+	hw->queues = 4;
+
+	tasklet_init(&sc->rxtq, ath5k_tasklet_rx, (unsigned long)sc);
+	tasklet_init(&sc->txtq, ath5k_tasklet_tx, (unsigned long)sc);
+	tasklet_init(&sc->calib, ath5k_tasklet_calibrate, (unsigned long)sc);
+	tasklet_init(&sc->beacontq, ath5k_tasklet_beacon, (unsigned long)sc);
+	tasklet_init(&sc->ani_tasklet, ath5k_tasklet_ani, (unsigned long)sc);
+
+	INIT_WORK(&sc->reset_work, ath5k_reset_work);
+	INIT_DELAYED_WORK(&sc->tx_complete_work, ath5k_tx_complete_poll_work);
+
+	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_queues;
+	}
+
+	SET_IEEE80211_PERM_ADDR(hw, mac);
+	/* All MAC address bits matter for ACKs */
+	memcpy(sc->bssidmask, ath_bcast_mac, ETH_ALEN);
+	ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask);
+
+	regulatory->current_rd = ah->ah_capabilities.cap_eeprom.ee_regdomain;
+	ret = ath_regd_init(regulatory, hw->wiphy, ath5k_reg_notifier);
+	if (ret) {
+		ATH5K_ERR(sc, "can't initialize regulatory system\n");
+		goto err_queues;
+	}
+
+	ret = ieee80211_register_hw(hw);
+	if (ret) {
+		ATH5K_ERR(sc, "can't register ieee80211 hw\n");
+		goto err_queues;
+	}
+
+	if (!ath_is_world_regd(regulatory))
+		regulatory_hint(hw->wiphy, regulatory->alpha2);
+
+	ath5k_init_leds(sc);
+
+	ath5k_sysfs_register(sc);
+
+	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
+	 *   ensure 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);
+	ath5k_unregister_leds(sc);
+
+	ath5k_sysfs_unregister(sc);
+	/*
+	 * 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.
+	 */
+}
+
+/********************\
+* Mac80211 functions *
+\********************/
+
+static int
+ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+	struct ath5k_softc *sc = hw->priv;
+	u16 qnum = skb_get_queue_mapping(skb);
+
+	if (WARN_ON(qnum >= sc->ah->ah_capabilities.cap_queues.q_tx_num)) {
+		dev_kfree_skb_any(skb);
+		return 0;
+	}
+
+	return ath5k_tx_queue(hw, skb, &sc->txqs[qnum]);
+}
+
 static int ath5k_start(struct ieee80211_hw *hw)
 {
 	return ath5k_init(hw->priv);
@@ -3291,18 +2932,14 @@
 	if (modparam_nohwcrypt)
 		return -EOPNOTSUPP;
 
-	if (sc->opmode == NL80211_IFTYPE_AP)
-		return -EOPNOTSUPP;
-
 	switch (key->cipher) {
 	case WLAN_CIPHER_SUITE_WEP40:
 	case WLAN_CIPHER_SUITE_WEP104:
 	case WLAN_CIPHER_SUITE_TKIP:
 		break;
 	case WLAN_CIPHER_SUITE_CCMP:
-		if (sc->ah->ah_aes_support)
+		if (common->crypt_caps & ATH_CRYPT_CAP_CIPHER_AESCCM)
 			break;
-
 		return -EOPNOTSUPP;
 	default:
 		WARN_ON(1);
@@ -3313,27 +2950,25 @@
 
 	switch (cmd) {
 	case SET_KEY:
-		ret = ath5k_hw_set_key(sc->ah, key->keyidx, key,
-				       sta ? sta->addr : NULL);
-		if (ret) {
-			ATH5K_ERR(sc, "can't set the key\n");
-			goto unlock;
+		ret = ath_key_config(common, vif, sta, key);
+		if (ret >= 0) {
+			key->hw_key_idx = ret;
+			/* push IV and Michael MIC generation to stack */
+			key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+			if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
+				key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+			if (key->cipher == WLAN_CIPHER_SUITE_CCMP)
+				key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
+			ret = 0;
 		}
-		__set_bit(key->keyidx, common->keymap);
-		key->hw_key_idx = key->keyidx;
-		key->flags |= (IEEE80211_KEY_FLAG_GENERATE_IV |
-			       IEEE80211_KEY_FLAG_GENERATE_MMIC);
 		break;
 	case DISABLE_KEY:
-		ath5k_hw_reset_key(sc->ah, key->keyidx);
-		__clear_bit(key->keyidx, common->keymap);
+		ath_key_delete(common, key);
 		break;
 	default:
 		ret = -EINVAL;
-		goto unlock;
 	}
 
-unlock:
 	mmiowb();
 	mutex_unlock(&sc->lock);
 	return ret;
@@ -3403,43 +3038,6 @@
 		ath5k_hw_reset_tsf(sc->ah);
 }
 
-/*
- * Updates the beacon that is sent by ath5k_beacon_send.  For adhoc,
- * this is called only once at config_bss time, for AP we do it every
- * SWBA interrupt so that the TIM will reflect buffered frames.
- *
- * Called with the beacon lock.
- */
-static int
-ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
-{
-	int ret;
-	struct ath5k_softc *sc = hw->priv;
-	struct sk_buff *skb;
-
-	if (WARN_ON(!vif)) {
-		ret = -EINVAL;
-		goto out;
-	}
-
-	skb = ieee80211_beacon_get(hw, vif);
-
-	if (!skb) {
-		ret = -ENOMEM;
-		goto out;
-	}
-
-	ath5k_debug_dump_skb(sc, skb, "BC  ", 1);
-
-	ath5k_txbuf_free_skb(sc, sc->bbuf);
-	sc->bbuf->skb = skb;
-	ret = ath5k_beacon_setup(sc, sc->bbuf);
-	if (ret)
-		sc->bbuf->skb = NULL;
-out:
-	return ret;
-}
-
 static void
 set_beacon_filter(struct ieee80211_hw *hw, bool enable)
 {
@@ -3545,3 +3143,402 @@
 	ath5k_hw_set_coverage_class(sc->ah, coverage_class);
 	mutex_unlock(&sc->lock);
 }
+
+static int ath5k_conf_tx(struct ieee80211_hw *hw, u16 queue,
+			 const struct ieee80211_tx_queue_params *params)
+{
+	struct ath5k_softc *sc = hw->priv;
+	struct ath5k_hw *ah = sc->ah;
+	struct ath5k_txq_info qi;
+	int ret = 0;
+
+	if (queue >= ah->ah_capabilities.cap_queues.q_tx_num)
+		return 0;
+
+	mutex_lock(&sc->lock);
+
+	ath5k_hw_get_tx_queueprops(ah, queue, &qi);
+
+	qi.tqi_aifs = params->aifs;
+	qi.tqi_cw_min = params->cw_min;
+	qi.tqi_cw_max = params->cw_max;
+	qi.tqi_burst_time = params->txop;
+
+	ATH5K_DBG(sc, ATH5K_DEBUG_ANY,
+		  "Configure tx [queue %d],  "
+		  "aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
+		  queue, params->aifs, params->cw_min,
+		  params->cw_max, params->txop);
+
+	if (ath5k_hw_set_tx_queueprops(ah, queue, &qi)) {
+		ATH5K_ERR(sc,
+			  "Unable to update hardware queue %u!\n", queue);
+		ret = -EIO;
+	} else
+		ath5k_hw_reset_tx_queue(ah, queue);
+
+	mutex_unlock(&sc->lock);
+
+	return ret;
+}
+
+static const 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,
+	.prepare_multicast = ath5k_prepare_multicast,
+	.configure_filter = ath5k_configure_filter,
+	.set_key 	= ath5k_set_key,
+	.get_stats 	= ath5k_get_stats,
+	.get_survey	= ath5k_get_survey,
+	.conf_tx	= ath5k_conf_tx,
+	.get_tsf 	= ath5k_get_tsf,
+	.set_tsf 	= ath5k_set_tsf,
+	.reset_tsf 	= ath5k_reset_tsf,
+	.bss_info_changed = ath5k_bss_info_changed,
+	.sw_scan_start	= ath5k_sw_scan_start,
+	.sw_scan_complete = ath5k_sw_scan_complete,
+	.set_coverage_class = ath5k_set_coverage_class,
+};
+
+/********************\
+* PCI Initialization *
+\********************/
+
+static int __devinit
+ath5k_pci_probe(struct pci_dev *pdev,
+		const struct pci_device_id *id)
+{
+	void __iomem *mem;
+	struct ath5k_softc *sc;
+	struct ath_common *common;
+	struct ieee80211_hw *hw;
+	int ret;
+	u8 csz;
+
+	/*
+	 * L0s needs to be disabled on all ath5k cards.
+	 *
+	 * For distributions shipping with CONFIG_PCIEASPM (this will be enabled
+	 * by default in the future in 2.6.36) this will also mean both L1 and
+	 * L0s will be disabled when a pre 1.1 PCIe device is detected. We do
+	 * know L1 works correctly even for all ath5k pre 1.1 PCIe devices
+	 * though but cannot currently undue the effect of a blacklist, for
+	 * details you can read pcie_aspm_sanity_check() and see how it adjusts
+	 * the device link capability.
+	 *
+	 * It may be possible in the future to implement some PCI API to allow
+	 * drivers to override blacklists for pre 1.1 PCIe but for now it is
+	 * best to accept that both L0s and L1 will be disabled completely for
+	 * distributions shipping with CONFIG_PCIEASPM rather than having this
+	 * issue present. Motivation for adding this new API will be to help
+	 * with power consumption for some of these devices.
+	 */
+	pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S);
+
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "can't enable device\n");
+		goto err;
+	}
+
+	/* XXX 32-bit addressing only */
+	ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+	if (ret) {
+		dev_err(&pdev->dev, "32-bit DMA not available\n");
+		goto err_dis;
+	}
+
+	/*
+	 * Cache line size is used to size and align various
+	 * structures used to communicate with the hardware.
+	 */
+	pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &csz);
+	if (csz == 0) {
+		/*
+		 * Linux 2.4.18 (at least) writes the cache line size
+		 * register as a 16-bit wide register which is wrong.
+		 * We must have this setup properly for rx buffer
+		 * DMA to work so force a reasonable value here if it
+		 * comes up zero.
+		 */
+		csz = L1_CACHE_BYTES >> 2;
+		pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, csz);
+	}
+	/*
+	 * The default setting of latency timer yields poor results,
+	 * set it to the value used by other systems.  It may be worth
+	 * tweaking this setting more.
+	 */
+	pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8);
+
+	/* Enable bus mastering */
+	pci_set_master(pdev);
+
+	/*
+	 * Disable the RETRY_TIMEOUT register (0x41) to keep
+	 * PCI Tx retries from interfering with C3 CPU state.
+	 */
+	pci_write_config_byte(pdev, 0x41, 0);
+
+	ret = pci_request_region(pdev, 0, "ath5k");
+	if (ret) {
+		dev_err(&pdev->dev, "cannot reserve PCI memory region\n");
+		goto err_dis;
+	}
+
+	mem = pci_iomap(pdev, 0, 0);
+	if (!mem) {
+		dev_err(&pdev->dev, "cannot remap PCI memory region\n") ;
+		ret = -EIO;
+		goto err_reg;
+	}
+
+	/*
+	 * Allocate hw (mac80211 main struct)
+	 * and hw->priv (driver private data)
+	 */
+	hw = ieee80211_alloc_hw(sizeof(*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 |
+		    IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
+		    IEEE80211_HW_SIGNAL_DBM;
+
+	hw->wiphy->interface_modes =
+		BIT(NL80211_IFTYPE_AP) |
+		BIT(NL80211_IFTYPE_STATION) |
+		BIT(NL80211_IFTYPE_ADHOC) |
+		BIT(NL80211_IFTYPE_MESH_POINT);
+
+	hw->extra_tx_headroom = 2;
+	hw->channel_change_time = 5000;
+	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->opmode = NL80211_IFTYPE_STATION;
+	sc->bintval = 1000;
+	mutex_init(&sc->lock);
+	spin_lock_init(&sc->rxbuflock);
+	spin_lock_init(&sc->txbuflock);
+	spin_lock_init(&sc->block);
+
+	/* Set private data */
+	pci_set_drvdata(pdev, sc);
+
+	/* 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;
+	}
+
+	/* If we passed the test, malloc an ath5k_hw struct */
+	sc->ah = kzalloc(sizeof(struct ath5k_hw), GFP_KERNEL);
+	if (!sc->ah) {
+		ret = -ENOMEM;
+		ATH5K_ERR(sc, "out of memory\n");
+		goto err_irq;
+	}
+
+	sc->ah->ah_sc = sc;
+	sc->ah->ah_iobase = sc->iobase;
+	common = ath5k_hw_common(sc->ah);
+	common->ops = &ath5k_common_ops;
+	common->ah = sc->ah;
+	common->hw = hw;
+	common->cachelsz = csz << 2; /* convert to bytes */
+
+	/* Initialize device */
+	ret = ath5k_hw_attach(sc);
+	if (ret) {
+		goto err_free_ah;
+	}
+
+	/* set up multi-rate retry capabilities */
+	if (sc->ah->ah_version == AR5K_AR5212) {
+		hw->max_rates = 4;
+		hw->max_rate_tries = 11;
+	}
+
+	/* 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_MAC, 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(AR5K_MODE_11A,
+				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(AR5K_MODE_11B,
+				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_free_ah:
+	kfree(sc->ah);
+err_irq:
+	free_irq(pdev->irq, sc);
+err_free:
+	ieee80211_free_hw(hw);
+err_map:
+	pci_iounmap(pdev, mem);
+err_reg:
+	pci_release_region(pdev, 0);
+err_dis:
+	pci_disable_device(pdev);
+err:
+	return ret;
+}
+
+static void __devexit
+ath5k_pci_remove(struct pci_dev *pdev)
+{
+	struct ath5k_softc *sc = pci_get_drvdata(pdev);
+
+	ath5k_debug_finish_device(sc);
+	ath5k_detach(pdev, sc->hw);
+	ath5k_hw_detach(sc->ah);
+	kfree(sc->ah);
+	free_irq(pdev->irq, sc);
+	pci_iounmap(pdev, sc->iobase);
+	pci_release_region(pdev, 0);
+	pci_disable_device(pdev);
+	ieee80211_free_hw(sc->hw);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ath5k_pci_suspend(struct device *dev)
+{
+	struct ath5k_softc *sc = pci_get_drvdata(to_pci_dev(dev));
+
+	ath5k_led_off(sc);
+	return 0;
+}
+
+static int ath5k_pci_resume(struct device *dev)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct ath5k_softc *sc = pci_get_drvdata(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_led_enable(sc);
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(ath5k_pm_ops, ath5k_pci_suspend, ath5k_pci_resume);
+#define ATH5K_PM_OPS	(&ath5k_pm_ops)
+#else
+#define ATH5K_PM_OPS	NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static struct pci_driver ath5k_pci_driver = {
+	.name		= KBUILD_MODNAME,
+	.id_table	= ath5k_pci_id_table,
+	.probe		= ath5k_pci_probe,
+	.remove		= __devexit_p(ath5k_pci_remove),
+	.driver.pm	= ATH5K_PM_OPS,
+};
+
+/*
+ * Module init/exit functions
+ */
+static int __init
+init_ath5k_pci(void)
+{
+	int ret;
+
+	ath5k_debug_init();
+
+	ret = pci_register_driver(&ath5k_pci_driver);
+	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_driver);
+
+	ath5k_debug_finish();
+}
+
+module_init(init_ath5k_pci);
+module_exit(exit_ath5k_pci);
diff --git a/drivers/net/wireless/ath/ath5k/base.h b/drivers/net/wireless/ath/ath5k/base.h
index dc1241f..7f9d0d3 100644
--- a/drivers/net/wireless/ath/ath5k/base.h
+++ b/drivers/net/wireless/ath/ath5k/base.h
@@ -60,6 +60,9 @@
 #define	ATH_TXBUF	200		/* number of TX buffers */
 #define ATH_BCBUF	1		/* number of beacon buffers */
 
+#define ATH5K_TXQ_LEN_MAX	(ATH_TXBUF / 4)		/* bufs per queue */
+#define ATH5K_TXQ_LEN_LOW	(ATH5K_TXQ_LEN_MAX / 2)	/* low mark */
+
 struct ath5k_buf {
 	struct list_head	list;
 	struct ath5k_desc	*desc;	/* virtual addr of desc */
@@ -83,6 +86,9 @@
 	struct list_head	q;	/* transmit queue */
 	spinlock_t		lock;	/* lock on q and link */
 	bool			setup;
+	int			txq_len; /* number of queued buffers */
+	bool			txq_poll_mark;
+	unsigned int		txq_stuck;	/* informational counter */
 };
 
 #define ATH5K_LED_MAX_NAME_LEN 31
@@ -204,7 +210,6 @@
 	spinlock_t		txbuflock;
 	unsigned int		txbuf_len;	/* buf count in txbuf list */
 	struct ath5k_txq	txqs[AR5K_NUM_TX_QUEUES];	/* tx queues */
-	struct ath5k_txq	*txq;		/* main tx queue */
 	struct tasklet_struct	txtq;		/* tx intr tasklet */
 	struct ath5k_led	tx_led;		/* tx led */
 
@@ -230,6 +235,8 @@
 
 	struct ath5k_ani_state	ani_state;
 	struct tasklet_struct	ani_tasklet;	/* ANI calibration */
+
+	struct delayed_work	tx_complete_work;
 };
 
 #define ath5k_hw_hasbssidmask(_ah) \
diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c
index 1b7c6d7..6583a82 100644
--- a/drivers/net/wireless/ath/ath5k/debug.c
+++ b/drivers/net/wireless/ath/ath5k/debug.c
@@ -763,7 +763,7 @@
 
 	struct ath5k_txq *txq;
 	struct ath5k_buf *bf, *bf0;
-	int i, n = 0;
+	int i, n;
 
 	len += snprintf(buf+len, sizeof(buf)-len,
 			"available txbuffers: %d\n", sc->txbuf_len);
@@ -777,9 +777,16 @@
 		if (!txq->setup)
 			continue;
 
+		n = 0;
+		spin_lock_bh(&txq->lock);
 		list_for_each_entry_safe(bf, bf0, &txq->q, list)
 			n++;
-		len += snprintf(buf+len, sizeof(buf)-len, "  len: %d\n", n);
+		spin_unlock_bh(&txq->lock);
+
+		len += snprintf(buf+len, sizeof(buf)-len,
+				"  len: %d bufs: %d\n", txq->txq_len, n);
+		len += snprintf(buf+len, sizeof(buf)-len,
+				"  stuck: %d\n", txq->txq_stuck);
 	}
 
 	if (len > sizeof(buf))
diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c
index bb2e215..6a891c4 100644
--- a/drivers/net/wireless/ath/ath5k/pcu.c
+++ b/drivers/net/wireless/ath/ath5k/pcu.c
@@ -640,197 +640,6 @@
 
 }
 
-
-/*********************\
-* Key table functions *
-\*********************/
-
-/*
- * Reset a key entry on the table
- */
-int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry)
-{
-	unsigned int i, type;
-	u16 micentry = entry + AR5K_KEYTABLE_MIC_OFFSET;
-
-	AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE);
-
-	type = ath5k_hw_reg_read(ah, AR5K_KEYTABLE_TYPE(entry));
-
-	for (i = 0; i < AR5K_KEYCACHE_SIZE; i++)
-		ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_OFF(entry, i));
-
-	/* Reset associated MIC entry if TKIP
-	 * is enabled located at offset (entry + 64) */
-	if (type == AR5K_KEYTABLE_TYPE_TKIP) {
-		AR5K_ASSERT_ENTRY(micentry, AR5K_KEYTABLE_SIZE);
-		for (i = 0; i < AR5K_KEYCACHE_SIZE / 2 ; i++)
-			ath5k_hw_reg_write(ah, 0,
-				AR5K_KEYTABLE_OFF(micentry, i));
-	}
-
-	/*
-	 * Set NULL encryption on AR5212+
-	 *
-	 * Note: AR5K_KEYTABLE_TYPE -> AR5K_KEYTABLE_OFF(entry, 5)
-	 *       AR5K_KEYTABLE_TYPE_NULL -> 0x00000007
-	 *
-	 * Note2: Windows driver (ndiswrapper) sets this to
-	 *        0x00000714 instead of 0x00000007
-	 */
-	if (ah->ah_version >= AR5K_AR5211) {
-		ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL,
-				AR5K_KEYTABLE_TYPE(entry));
-
-		if (type == AR5K_KEYTABLE_TYPE_TKIP) {
-			ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL,
-				AR5K_KEYTABLE_TYPE(micentry));
-		}
-	}
-
-	return 0;
-}
-
-static
-int ath5k_keycache_type(const struct ieee80211_key_conf *key)
-{
-	switch (key->cipher) {
-	case WLAN_CIPHER_SUITE_TKIP:
-		return AR5K_KEYTABLE_TYPE_TKIP;
-	case WLAN_CIPHER_SUITE_CCMP:
-		return AR5K_KEYTABLE_TYPE_CCM;
-	case WLAN_CIPHER_SUITE_WEP40:
-		return AR5K_KEYTABLE_TYPE_40;
-	case WLAN_CIPHER_SUITE_WEP104:
-		return AR5K_KEYTABLE_TYPE_104;
-	default:
-		return -EINVAL;
-	}
-}
-
-/*
- * Set a key entry on the table
- */
-int ath5k_hw_set_key(struct ath5k_hw *ah, u16 entry,
-		const struct ieee80211_key_conf *key, const u8 *mac)
-{
-	unsigned int i;
-	int keylen;
-	__le32 key_v[5] = {};
-	__le32 key0 = 0, key1 = 0;
-	__le32 *rxmic, *txmic;
-	int keytype;
-	u16 micentry = entry + AR5K_KEYTABLE_MIC_OFFSET;
-	bool is_tkip;
-	const u8 *key_ptr;
-
-	is_tkip = (key->cipher == WLAN_CIPHER_SUITE_TKIP);
-
-	/*
-	 * key->keylen comes in from mac80211 in bytes.
-	 * TKIP is 128 bit + 128 bit mic
-	 */
-	keylen = (is_tkip) ? (128 / 8) : key->keylen;
-
-	if (entry > AR5K_KEYTABLE_SIZE ||
-		(is_tkip && micentry > AR5K_KEYTABLE_SIZE))
-		return -EOPNOTSUPP;
-
-	if (unlikely(keylen > 16))
-		return -EOPNOTSUPP;
-
-	keytype = ath5k_keycache_type(key);
-	if (keytype < 0)
-		return keytype;
-
-	/*
-	 * each key block is 6 bytes wide, written as pairs of
-	 * alternating 32 and 16 bit le values.
-	 */
-	key_ptr = key->key;
-	for (i = 0; keylen >= 6; keylen -= 6) {
-		memcpy(&key_v[i], key_ptr, 6);
-		i += 2;
-		key_ptr += 6;
-	}
-	if (keylen)
-		memcpy(&key_v[i], key_ptr, keylen);
-
-	/* intentionally corrupt key until mic is installed */
-	if (is_tkip) {
-		key0 = key_v[0] = ~key_v[0];
-		key1 = key_v[1] = ~key_v[1];
-	}
-
-	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));
-
-	if (is_tkip) {
-		/* Install rx/tx MIC */
-		rxmic = (__le32 *) &key->key[16];
-		txmic = (__le32 *) &key->key[24];
-
-		if (ah->ah_combined_mic) {
-			key_v[0] = rxmic[0];
-			key_v[1] = cpu_to_le32(le32_to_cpu(txmic[0]) >> 16);
-			key_v[2] = rxmic[1];
-			key_v[3] = cpu_to_le32(le32_to_cpu(txmic[0]) & 0xffff);
-			key_v[4] = txmic[1];
-		} else {
-			key_v[0] = rxmic[0];
-			key_v[1] = 0;
-			key_v[2] = rxmic[1];
-			key_v[3] = 0;
-			key_v[4] = 0;
-		}
-		for (i = 0; i < ARRAY_SIZE(key_v); i++)
-			ath5k_hw_reg_write(ah, le32_to_cpu(key_v[i]),
-				AR5K_KEYTABLE_OFF(micentry, i));
-
-		ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL,
-			AR5K_KEYTABLE_TYPE(micentry));
-		ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_MAC0(micentry));
-		ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_MAC1(micentry));
-
-		/* restore first 2 words of key */
-		ath5k_hw_reg_write(ah, le32_to_cpu(~key0),
-			AR5K_KEYTABLE_OFF(entry, 0));
-		ath5k_hw_reg_write(ah, le32_to_cpu(~key1),
-			AR5K_KEYTABLE_OFF(entry, 1));
-	}
-
-	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;
-
-	 /* 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 get_unaligned_le32 and get_unaligned_le16 as we
-	 * already know it.
-	 */
-	if (!mac) {
-		low_id = 0xffffffff;
-		high_id = 0xffff | AR5K_KEYTABLE_VALID;
-	} else {
-		low_id = get_unaligned_le32(mac);
-		high_id = get_unaligned_le16(mac + 4) | 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;
-}
-
 /**
  * ath5k_hw_set_coverage_class - Set IEEE 802.11 coverage class
  *
diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c
index 984ba92..4932bf2 100644
--- a/drivers/net/wireless/ath/ath5k/phy.c
+++ b/drivers/net/wireless/ath/ath5k/phy.c
@@ -1377,7 +1377,7 @@
 
 	/* protect against divide by 0 and loss of sign bits */
 	if (i_coffd == 0 || q_coffd < 2)
-		return -1;
+		return 0;
 
 	i_coff = (-iq_corr) / i_coffd;
 	i_coff = clamp(i_coff, -32, 31); /* signed 6 bit */
diff --git a/drivers/net/wireless/ath/ath5k/qcu.c b/drivers/net/wireless/ath/ath5k/qcu.c
index 4186ff4..84c717d 100644
--- a/drivers/net/wireless/ath/ath5k/qcu.c
+++ b/drivers/net/wireless/ath/ath5k/qcu.c
@@ -36,24 +36,58 @@
 }
 
 /*
+ * Make sure cw is a power of 2 minus 1 and smaller than 1024
+ */
+static u16 ath5k_cw_validate(u16 cw_req)
+{
+	u32 cw = 1;
+	cw_req = min(cw_req, (u16)1023);
+
+	while (cw < cw_req)
+		cw = (cw << 1) | 1;
+
+	return cw;
+}
+
+/*
  * Set properties for a transmit queue
  */
 int ath5k_hw_set_tx_queueprops(struct ath5k_hw *ah, int queue,
-				const struct ath5k_txq_info *queue_info)
+				const struct ath5k_txq_info *qinfo)
 {
+	struct ath5k_txq_info *qi;
+
 	AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
 
-	if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE)
+	qi = &ah->ah_txq[queue];
+
+	if (qi->tqi_type == AR5K_TX_QUEUE_INACTIVE)
 		return -EIO;
 
-	memcpy(&ah->ah_txq[queue], queue_info, sizeof(struct ath5k_txq_info));
+	/* copy and validate values */
+	qi->tqi_type = qinfo->tqi_type;
+	qi->tqi_subtype = qinfo->tqi_subtype;
+	qi->tqi_flags = qinfo->tqi_flags;
+	/*
+	 * According to the docs: Although the AIFS field is 8 bit wide,
+	 * the maximum supported value is 0xFC. Setting it higher than that
+	 * will cause the DCU to hang.
+	 */
+	qi->tqi_aifs = min(qinfo->tqi_aifs, (u8)0xFC);
+	qi->tqi_cw_min = ath5k_cw_validate(qinfo->tqi_cw_min);
+	qi->tqi_cw_max = ath5k_cw_validate(qinfo->tqi_cw_max);
+	qi->tqi_cbr_period = qinfo->tqi_cbr_period;
+	qi->tqi_cbr_overflow_limit = qinfo->tqi_cbr_overflow_limit;
+	qi->tqi_burst_time = qinfo->tqi_burst_time;
+	qi->tqi_ready_time = qinfo->tqi_ready_time;
 
 	/*XXX: Is this supported on 5210 ?*/
-	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;
+	/*XXX: Is this correct for AR5K_WME_AC_VI,VO ???*/
+	if ((qinfo->tqi_type == AR5K_TX_QUEUE_DATA &&
+		((qinfo->tqi_subtype == AR5K_WME_AC_VI) ||
+		 (qinfo->tqi_subtype == AR5K_WME_AC_VO))) ||
+	     qinfo->tqi_type == AR5K_TX_QUEUE_UAPSD)
+		qi->tqi_flags |= AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS;
 
 	return 0;
 }
@@ -186,7 +220,7 @@
  */
 int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue)
 {
-	u32 cw_min, cw_max, retry_lg, retry_sh;
+	u32 retry_lg, retry_sh;
 	struct ath5k_txq_info *tq = &ah->ah_txq[queue];
 
 	AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
@@ -217,14 +251,13 @@
 		/* Set IFS0 */
 		if (ah->ah_turbo) {
 			 ath5k_hw_reg_write(ah, ((AR5K_INIT_SIFS_TURBO +
-				(ah->ah_aifs + tq->tqi_aifs) *
-				AR5K_INIT_SLOT_TIME_TURBO) <<
+				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) |
+				tq->tqi_aifs * AR5K_INIT_SLOT_TIME) <<
+				AR5K_IFS0_DIFS_S) |
 				AR5K_INIT_SIFS, AR5K_IFS0);
 		}
 
@@ -248,35 +281,6 @@
 	}
 
 	/*
-	 * 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) {
@@ -292,7 +296,7 @@
 	/*No QCU/DCU [5210]*/
 	if (ah->ah_version == AR5K_AR5210) {
 		ath5k_hw_reg_write(ah,
-			(cw_min << AR5K_NODCU_RETRY_LMT_CW_MIN_S)
+			(tq->tqi_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,
@@ -314,14 +318,13 @@
 	/*===Rest is also for QCU/DCU only [5211+]===*/
 
 		/*
-		 * Set initial content window (cw_min/cw_max)
+		 * Set contention 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_REG_SM(tq->tqi_cw_min, AR5K_DCU_LCL_IFS_CW_MIN) |
+			AR5K_REG_SM(tq->tqi_cw_max, AR5K_DCU_LCL_IFS_CW_MAX) |
+			AR5K_REG_SM(tq->tqi_aifs, AR5K_DCU_LCL_IFS_AIFS),
 			AR5K_QUEUE_DFS_LOCAL_IFS(queue));
 
 		/*
diff --git a/drivers/net/wireless/ath/ath5k/reg.h b/drivers/net/wireless/ath/ath5k/reg.h
index 05ef587..67d6308 100644
--- a/drivers/net/wireless/ath/ath5k/reg.h
+++ b/drivers/net/wireless/ath/ath5k/reg.h
@@ -1822,50 +1822,8 @@
 
 /*===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
-
-/* If key type is TKIP and MIC is enabled
- * MIC key goes in offset entry + 64 */
-#define	AR5K_KEYTABLE_MIC_OFFSET	64
-
-/* 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===*/
 
diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig
index 35f23bd..ad57a6d 100644
--- a/drivers/net/wireless/ath/ath9k/Kconfig
+++ b/drivers/net/wireless/ath/ath9k/Kconfig
@@ -32,6 +32,14 @@
 
 	  Also required for changing debug message flags at run time.
 
+config ATH9K_RATE_CONTROL
+	bool "Atheros ath9k rate control"
+	depends on ATH9K
+	default y
+	---help---
+	  Say Y, if you want to use the ath9k specific rate control
+	  module instead of minstrel_ht.
+
 config ATH9K_HTC
        tristate "Atheros HTC based wireless cards support"
        depends on USB && MAC80211
diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile
index 4555e99..aca0162 100644
--- a/drivers/net/wireless/ath/ath9k/Makefile
+++ b/drivers/net/wireless/ath/ath9k/Makefile
@@ -5,8 +5,8 @@
 		recv.o \
 		xmit.o \
 		virtual.o \
-		rc.o
 
+ath9k-$(CONFIG_ATH9K_RATE_CONTROL) += rc.o
 ath9k-$(CONFIG_PCI) += pci.o
 ath9k-$(CONFIG_ATHEROS_AR71XX) += ahb.o
 ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o
diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c
index cc648b6..0496f96 100644
--- a/drivers/net/wireless/ath/ath9k/ani.c
+++ b/drivers/net/wireless/ath/ath9k/ani.c
@@ -14,6 +14,7 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <linux/kernel.h>
 #include "hw.h"
 #include "hw-ops.h"
 
@@ -48,7 +49,7 @@
 	{  7,  8,  0  }  /* lvl 9 */
 };
 #define ATH9K_ANI_OFDM_NUM_LEVEL \
-	(sizeof(ofdm_level_table)/sizeof(ofdm_level_table[0]))
+	ARRAY_SIZE(ofdm_level_table)
 #define ATH9K_ANI_OFDM_MAX_LEVEL \
 	(ATH9K_ANI_OFDM_NUM_LEVEL-1)
 #define ATH9K_ANI_OFDM_DEF_LEVEL \
@@ -94,7 +95,7 @@
 };
 
 #define ATH9K_ANI_CCK_NUM_LEVEL \
-	(sizeof(cck_level_table)/sizeof(cck_level_table[0]))
+	ARRAY_SIZE(cck_level_table)
 #define ATH9K_ANI_CCK_MAX_LEVEL \
 	(ATH9K_ANI_CCK_NUM_LEVEL-1)
 #define ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI \
diff --git a/drivers/net/wireless/ath/ath9k/ar9002_hw.c b/drivers/net/wireless/ath/ath9k/ar9002_hw.c
index 303c63d..94392da 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c
@@ -580,3 +580,53 @@
 	else
 		ath9k_hw_attach_ani_ops_old(ah);
 }
+
+void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan)
+{
+	u32 modesIndex;
+	int i;
+
+	switch (chan->chanmode) {
+	case CHANNEL_A:
+	case CHANNEL_A_HT20:
+		modesIndex = 1;
+		break;
+	case CHANNEL_A_HT40PLUS:
+	case CHANNEL_A_HT40MINUS:
+		modesIndex = 2;
+		break;
+	case CHANNEL_G:
+	case CHANNEL_G_HT20:
+	case CHANNEL_B:
+		modesIndex = 4;
+		break;
+	case CHANNEL_G_HT40PLUS:
+	case CHANNEL_G_HT40MINUS:
+		modesIndex = 3;
+		break;
+
+	default:
+		return;
+	}
+
+	ENABLE_REGWRITE_BUFFER(ah);
+
+	for (i = 0; i < ah->iniModes_9271_ANI_reg.ia_rows; i++) {
+		u32 reg = INI_RA(&ah->iniModes_9271_ANI_reg, i, 0);
+		u32 val = INI_RA(&ah->iniModes_9271_ANI_reg, i, modesIndex);
+		u32 val_orig;
+
+		if (reg == AR_PHY_CCK_DETECT) {
+			val_orig = REG_READ(ah, reg);
+			val &= AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK;
+			val_orig &= ~AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK;
+
+			REG_WRITE(ah, reg, val|val_orig);
+		} else
+			REG_WRITE(ah, reg, val);
+	}
+
+	REGWRITE_BUFFER_FLUSH(ah);
+	DISABLE_REGWRITE_BUFFER(ah);
+
+}
diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.c b/drivers/net/wireless/ath/ath9k/ar9002_phy.c
index adbf031..cd56c86 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c
@@ -530,3 +530,38 @@
 
 	ar9002_hw_set_nf_limits(ah);
 }
+
+void ath9k_hw_antdiv_comb_conf_get(struct ath_hw *ah,
+				   struct ath_hw_antcomb_conf *antconf)
+{
+	u32 regval;
+
+	regval = REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL);
+	antconf->main_lna_conf = (regval & AR_PHY_9285_ANT_DIV_MAIN_LNACONF) >>
+				  AR_PHY_9285_ANT_DIV_MAIN_LNACONF_S;
+	antconf->alt_lna_conf = (regval & AR_PHY_9285_ANT_DIV_ALT_LNACONF) >>
+				 AR_PHY_9285_ANT_DIV_ALT_LNACONF_S;
+	antconf->fast_div_bias = (regval & AR_PHY_9285_FAST_DIV_BIAS) >>
+				  AR_PHY_9285_FAST_DIV_BIAS_S;
+}
+EXPORT_SYMBOL(ath9k_hw_antdiv_comb_conf_get);
+
+void ath9k_hw_antdiv_comb_conf_set(struct ath_hw *ah,
+				   struct ath_hw_antcomb_conf *antconf)
+{
+	u32 regval;
+
+	regval = REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL);
+	regval &= ~(AR_PHY_9285_ANT_DIV_MAIN_LNACONF |
+		    AR_PHY_9285_ANT_DIV_ALT_LNACONF |
+		    AR_PHY_9285_FAST_DIV_BIAS);
+	regval |= ((antconf->main_lna_conf << AR_PHY_9285_ANT_DIV_MAIN_LNACONF_S)
+		   & AR_PHY_9285_ANT_DIV_MAIN_LNACONF);
+	regval |= ((antconf->alt_lna_conf << AR_PHY_9285_ANT_DIV_ALT_LNACONF_S)
+		   & AR_PHY_9285_ANT_DIV_ALT_LNACONF);
+	regval |= ((antconf->fast_div_bias << AR_PHY_9285_FAST_DIV_BIAS_S)
+		   & AR_PHY_9285_FAST_DIV_BIAS);
+
+	REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regval);
+}
+EXPORT_SYMBOL(ath9k_hw_antdiv_comb_conf_set);
diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.h b/drivers/net/wireless/ath/ath9k/ar9002_phy.h
index c5151a4..37663db 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_phy.h
+++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.h
@@ -302,6 +302,8 @@
 #define AR_PHY_NEW_ADC_DC_OFFSET_CORR_ENABLE 0x80000000
 
 #define AR_PHY_MULTICHAIN_GAIN_CTL          0x99ac
+#define AR_PHY_9285_FAST_DIV_BIAS	    0x00007E00
+#define AR_PHY_9285_FAST_DIV_BIAS_S	    9
 #define AR_PHY_9285_ANT_DIV_CTL_ALL         0x7f000000
 #define AR_PHY_9285_ANT_DIV_CTL             0x01000000
 #define AR_PHY_9285_ANT_DIV_CTL_S           24
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
index 057fb69..c418235 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
@@ -968,7 +968,7 @@
 }
 
 static u8 ath9k_hw_ar9300_get_num_ant_config(struct ath_hw *ah,
-					     enum ieee80211_band freq_band)
+					     enum ath9k_hal_freq_band freq_band)
 {
 	return 1;
 }
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c
index a462da2..3b424ca 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c
@@ -616,7 +616,8 @@
 			rxs->rs_status |= ATH9K_RXERR_DECRYPT;
 		} else if (rxsp->status11 & AR_MichaelErr) {
 			rxs->rs_status |= ATH9K_RXERR_MIC;
-		}
+		} else if (rxsp->status11 & AR_KeyMiss)
+			rxs->rs_status |= ATH9K_RXERR_DECRYPT;
 	}
 
 	return 0;
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index f0197a6..9f8e542 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -254,7 +254,7 @@
 	struct list_head buf_q;
 	struct ath_node *an;
 	struct ath_atx_ac *ac;
-	struct ath_buf *tx_buf[ATH_TID_MAX_BUFS];
+	unsigned long tx_buf[BITS_TO_LONGS(ATH_TID_MAX_BUFS)];
 	u16 seq_start;
 	u16 seq_next;
 	u16 baw_size;
@@ -345,9 +345,8 @@
 void ath_tx_tasklet(struct ath_softc *sc);
 void ath_tx_edma_tasklet(struct ath_softc *sc);
 void ath_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb);
-bool ath_tx_aggr_check(struct ath_softc *sc, struct ath_node *an, u8 tidno);
-void ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
-		       u16 tid, u16 *ssn);
+int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
+		      u16 tid, u16 *ssn);
 void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid);
 void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid);
 void ath9k_enable_ps(struct ath_softc *sc);
@@ -481,6 +480,60 @@
 void ath_init_leds(struct ath_softc *sc);
 void ath_deinit_leds(struct ath_softc *sc);
 
+/* Antenna diversity/combining */
+#define ATH_ANT_RX_CURRENT_SHIFT 4
+#define ATH_ANT_RX_MAIN_SHIFT 2
+#define ATH_ANT_RX_MASK 0x3
+
+#define ATH_ANT_DIV_COMB_SHORT_SCAN_INTR 50
+#define ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT 0x100
+#define ATH_ANT_DIV_COMB_MAX_PKTCOUNT 0x200
+#define ATH_ANT_DIV_COMB_INIT_COUNT 95
+#define ATH_ANT_DIV_COMB_MAX_COUNT 100
+#define ATH_ANT_DIV_COMB_ALT_ANT_RATIO 30
+#define ATH_ANT_DIV_COMB_ALT_ANT_RATIO2 20
+
+#define ATH_ANT_DIV_COMB_LNA1_LNA2_DELTA -3
+#define ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA -1
+#define ATH_ANT_DIV_COMB_LNA1_DELTA_HI -4
+#define ATH_ANT_DIV_COMB_LNA1_DELTA_MID -2
+#define ATH_ANT_DIV_COMB_LNA1_DELTA_LOW 2
+
+enum ath9k_ant_div_comb_lna_conf {
+	ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2,
+	ATH_ANT_DIV_COMB_LNA2,
+	ATH_ANT_DIV_COMB_LNA1,
+	ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2,
+};
+
+struct ath_ant_comb {
+	u16 count;
+	u16 total_pkt_count;
+	bool scan;
+	bool scan_not_start;
+	int main_total_rssi;
+	int alt_total_rssi;
+	int alt_recv_cnt;
+	int main_recv_cnt;
+	int rssi_lna1;
+	int rssi_lna2;
+	int rssi_add;
+	int rssi_sub;
+	int rssi_first;
+	int rssi_second;
+	int rssi_third;
+	bool alt_good;
+	int quick_scan_cnt;
+	int main_conf;
+	enum ath9k_ant_div_comb_lna_conf first_quick_scan_conf;
+	enum ath9k_ant_div_comb_lna_conf second_quick_scan_conf;
+	int first_bias;
+	int second_bias;
+	bool first_ratio;
+	bool second_ratio;
+	unsigned long scan_start_time;
+};
+
 /********************/
 /* Main driver core */
 /********************/
@@ -509,7 +562,6 @@
 #define SC_OP_RXFLUSH                BIT(7)
 #define SC_OP_LED_ASSOCIATED         BIT(8)
 #define SC_OP_LED_ON                 BIT(9)
-#define SC_OP_SCANNING               BIT(10)
 #define SC_OP_TSF_RESET              BIT(11)
 #define SC_OP_BT_PRIORITY_DETECTED   BIT(12)
 #define SC_OP_BT_SCAN		     BIT(13)
@@ -597,6 +649,8 @@
 	struct ath_btcoex btcoex;
 
 	struct ath_descdma txsdma;
+
+	struct ath_ant_comb ant_comb;
 };
 
 struct ath_wiphy {
@@ -663,7 +717,7 @@
 void ath9k_ps_wakeup(struct ath_softc *sc);
 void ath9k_ps_restore(struct ath_softc *sc);
 
-void ath9k_set_bssid_mask(struct ieee80211_hw *hw);
+void ath9k_set_bssid_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
 int ath9k_wiphy_add(struct ath_softc *sc);
 int ath9k_wiphy_del(struct ath_wiphy *aphy);
 void ath9k_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb);
diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c
index 2dab64b..f43a2d9 100644
--- a/drivers/net/wireless/ath/ath9k/common.c
+++ b/drivers/net/wireless/ath/ath9k/common.c
@@ -148,276 +148,6 @@
 }
 EXPORT_SYMBOL(ath9k_cmn_get_curchannel);
 
-static int ath_setkey_tkip(struct ath_common *common, u16 keyix, const u8 *key,
-			   struct ath9k_keyval *hk, const u8 *addr,
-			   bool authenticator)
-{
-	struct ath_hw *ah = common->ah;
-	const u8 *key_rxmic;
-	const u8 *key_txmic;
-
-	key_txmic = key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY;
-	key_rxmic = key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY;
-
-	if (addr == NULL) {
-		/*
-		 * Group key installation - only two key cache entries are used
-		 * regardless of splitmic capability since group key is only
-		 * used either for TX or RX.
-		 */
-		if (authenticator) {
-			memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic));
-			memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_mic));
-		} else {
-			memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
-			memcpy(hk->kv_txmic, key_rxmic, sizeof(hk->kv_mic));
-		}
-		return ath9k_hw_set_keycache_entry(ah, keyix, hk, addr);
-	}
-	if (!common->splitmic) {
-		/* TX and RX keys share the same key cache entry. */
-		memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
-		memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_txmic));
-		return ath9k_hw_set_keycache_entry(ah, keyix, hk, addr);
-	}
-
-	/* Separate key cache entries for TX and RX */
-
-	/* TX key goes at first index, RX key at +32. */
-	memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic));
-	if (!ath9k_hw_set_keycache_entry(ah, keyix, hk, NULL)) {
-		/* TX MIC entry failed. No need to proceed further */
-		ath_print(common, ATH_DBG_FATAL,
-			  "Setting TX MIC Key Failed\n");
-		return 0;
-	}
-
-	memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
-	/* XXX delete tx key on failure? */
-	return ath9k_hw_set_keycache_entry(ah, keyix + 32, hk, addr);
-}
-
-static int ath_reserve_key_cache_slot_tkip(struct ath_common *common)
-{
-	int i;
-
-	for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) {
-		if (test_bit(i, common->keymap) ||
-		    test_bit(i + 64, common->keymap))
-			continue; /* At least one part of TKIP key allocated */
-		if (common->splitmic &&
-		    (test_bit(i + 32, common->keymap) ||
-		     test_bit(i + 64 + 32, common->keymap)))
-			continue; /* At least one part of TKIP key allocated */
-
-		/* Found a free slot for a TKIP key */
-		return i;
-	}
-	return -1;
-}
-
-static int ath_reserve_key_cache_slot(struct ath_common *common,
-				      u32 cipher)
-{
-	int i;
-
-	if (cipher == WLAN_CIPHER_SUITE_TKIP)
-		return ath_reserve_key_cache_slot_tkip(common);
-
-	/* First, try to find slots that would not be available for TKIP. */
-	if (common->splitmic) {
-		for (i = IEEE80211_WEP_NKID; i < common->keymax / 4; i++) {
-			if (!test_bit(i, common->keymap) &&
-			    (test_bit(i + 32, common->keymap) ||
-			     test_bit(i + 64, common->keymap) ||
-			     test_bit(i + 64 + 32, common->keymap)))
-				return i;
-			if (!test_bit(i + 32, common->keymap) &&
-			    (test_bit(i, common->keymap) ||
-			     test_bit(i + 64, common->keymap) ||
-			     test_bit(i + 64 + 32, common->keymap)))
-				return i + 32;
-			if (!test_bit(i + 64, common->keymap) &&
-			    (test_bit(i , common->keymap) ||
-			     test_bit(i + 32, common->keymap) ||
-			     test_bit(i + 64 + 32, common->keymap)))
-				return i + 64;
-			if (!test_bit(i + 64 + 32, common->keymap) &&
-			    (test_bit(i, common->keymap) ||
-			     test_bit(i + 32, common->keymap) ||
-			     test_bit(i + 64, common->keymap)))
-				return i + 64 + 32;
-		}
-	} else {
-		for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) {
-			if (!test_bit(i, common->keymap) &&
-			    test_bit(i + 64, common->keymap))
-				return i;
-			if (test_bit(i, common->keymap) &&
-			    !test_bit(i + 64, common->keymap))
-				return i + 64;
-		}
-	}
-
-	/* No partially used TKIP slots, pick any available slot */
-	for (i = IEEE80211_WEP_NKID; i < common->keymax; i++) {
-		/* Do not allow slots that could be needed for TKIP group keys
-		 * to be used. This limitation could be removed if we know that
-		 * TKIP will not be used. */
-		if (i >= 64 && i < 64 + IEEE80211_WEP_NKID)
-			continue;
-		if (common->splitmic) {
-			if (i >= 32 && i < 32 + IEEE80211_WEP_NKID)
-				continue;
-			if (i >= 64 + 32 && i < 64 + 32 + IEEE80211_WEP_NKID)
-				continue;
-		}
-
-		if (!test_bit(i, common->keymap))
-			return i; /* Found a free slot for a key */
-	}
-
-	/* No free slot found */
-	return -1;
-}
-
-/*
- * Configure encryption in the HW.
- */
-int ath9k_cmn_key_config(struct ath_common *common,
-			 struct ieee80211_vif *vif,
-			 struct ieee80211_sta *sta,
-			 struct ieee80211_key_conf *key)
-{
-	struct ath_hw *ah = common->ah;
-	struct ath9k_keyval hk;
-	const u8 *mac = NULL;
-	u8 gmac[ETH_ALEN];
-	int ret = 0;
-	int idx;
-
-	memset(&hk, 0, sizeof(hk));
-
-	switch (key->cipher) {
-	case WLAN_CIPHER_SUITE_WEP40:
-	case WLAN_CIPHER_SUITE_WEP104:
-		hk.kv_type = ATH9K_CIPHER_WEP;
-		break;
-	case WLAN_CIPHER_SUITE_TKIP:
-		hk.kv_type = ATH9K_CIPHER_TKIP;
-		break;
-	case WLAN_CIPHER_SUITE_CCMP:
-		hk.kv_type = ATH9K_CIPHER_AES_CCM;
-		break;
-	default:
-		return -EOPNOTSUPP;
-	}
-
-	hk.kv_len = key->keylen;
-	memcpy(hk.kv_val, key->key, key->keylen);
-
-	if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
-		switch (vif->type) {
-		case NL80211_IFTYPE_AP:
-			memcpy(gmac, vif->addr, ETH_ALEN);
-			gmac[0] |= 0x01;
-			mac = gmac;
-			idx = ath_reserve_key_cache_slot(common, key->cipher);
-			break;
-		case NL80211_IFTYPE_ADHOC:
-			if (!sta) {
-				idx = key->keyidx;
-				break;
-			}
-			memcpy(gmac, sta->addr, ETH_ALEN);
-			gmac[0] |= 0x01;
-			mac = gmac;
-			idx = ath_reserve_key_cache_slot(common, key->cipher);
-			break;
-		default:
-			idx = key->keyidx;
-			break;
-		}
-	} else if (key->keyidx) {
-		if (WARN_ON(!sta))
-			return -EOPNOTSUPP;
-		mac = sta->addr;
-
-		if (vif->type != NL80211_IFTYPE_AP) {
-			/* Only keyidx 0 should be used with unicast key, but
-			 * allow this for client mode for now. */
-			idx = key->keyidx;
-		} else
-			return -EIO;
-	} else {
-		if (WARN_ON(!sta))
-			return -EOPNOTSUPP;
-		mac = sta->addr;
-
-		idx = ath_reserve_key_cache_slot(common, key->cipher);
-	}
-
-	if (idx < 0)
-		return -ENOSPC; /* no free key cache entries */
-
-	if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
-		ret = ath_setkey_tkip(common, idx, key->key, &hk, mac,
-				      vif->type == NL80211_IFTYPE_AP);
-	else
-		ret = ath9k_hw_set_keycache_entry(ah, idx, &hk, mac);
-
-	if (!ret)
-		return -EIO;
-
-	set_bit(idx, common->keymap);
-	if (key->cipher == WLAN_CIPHER_SUITE_TKIP) {
-		set_bit(idx + 64, common->keymap);
-		set_bit(idx, common->tkip_keymap);
-		set_bit(idx + 64, common->tkip_keymap);
-		if (common->splitmic) {
-			set_bit(idx + 32, common->keymap);
-			set_bit(idx + 64 + 32, common->keymap);
-			set_bit(idx + 32, common->tkip_keymap);
-			set_bit(idx + 64 + 32, common->tkip_keymap);
-		}
-	}
-
-	return idx;
-}
-EXPORT_SYMBOL(ath9k_cmn_key_config);
-
-/*
- * Delete Key.
- */
-void ath9k_cmn_key_delete(struct ath_common *common,
-			  struct ieee80211_key_conf *key)
-{
-	struct ath_hw *ah = common->ah;
-
-	ath9k_hw_keyreset(ah, key->hw_key_idx);
-	if (key->hw_key_idx < IEEE80211_WEP_NKID)
-		return;
-
-	clear_bit(key->hw_key_idx, common->keymap);
-	if (key->cipher != WLAN_CIPHER_SUITE_TKIP)
-		return;
-
-	clear_bit(key->hw_key_idx + 64, common->keymap);
-
-	clear_bit(key->hw_key_idx, common->tkip_keymap);
-	clear_bit(key->hw_key_idx + 64, common->tkip_keymap);
-
-	if (common->splitmic) {
-		ath9k_hw_keyreset(ah, key->hw_key_idx + 32);
-		clear_bit(key->hw_key_idx + 32, common->keymap);
-		clear_bit(key->hw_key_idx + 64 + 32, common->keymap);
-
-		clear_bit(key->hw_key_idx + 32, common->tkip_keymap);
-		clear_bit(key->hw_key_idx + 64 + 32, common->tkip_keymap);
-	}
-}
-EXPORT_SYMBOL(ath9k_cmn_key_delete);
-
 int ath9k_cmn_count_streams(unsigned int chainmask, int max)
 {
 	int streams = 0;
diff --git a/drivers/net/wireless/ath/ath9k/common.h b/drivers/net/wireless/ath/ath9k/common.h
index 4aa4e7d..fea3b33 100644
--- a/drivers/net/wireless/ath/ath9k/common.h
+++ b/drivers/net/wireless/ath/ath9k/common.h
@@ -66,12 +66,6 @@
 			       struct ath9k_channel *ichan);
 struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw,
 					       struct ath_hw *ah);
-int ath9k_cmn_key_config(struct ath_common *common,
-			 struct ieee80211_vif *vif,
-			 struct ieee80211_sta *sta,
-			 struct ieee80211_key_conf *key);
-void ath9k_cmn_key_delete(struct ath_common *common,
-			  struct ieee80211_key_conf *key);
 int ath9k_cmn_count_streams(unsigned int chainmask, int max);
 void ath9k_cmn_btcoex_bt_stomp(struct ath_common *common,
 				  enum ath_stomp_type stomp_type);
diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index 54aae93..d65a896 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -492,12 +492,55 @@
 	unsigned int len = 0;
 	int i;
 	u8 addr[ETH_ALEN];
+	u32 tmp;
 
 	len += snprintf(buf + len, sizeof(buf) - len,
 			"primary: %s (%s chan=%d ht=%d)\n",
 			wiphy_name(sc->pri_wiphy->hw->wiphy),
 			ath_wiphy_state_str(sc->pri_wiphy->state),
 			sc->pri_wiphy->chan_idx, sc->pri_wiphy->chan_is_ht);
+
+	put_unaligned_le32(REG_READ_D(sc->sc_ah, AR_STA_ID0), addr);
+	put_unaligned_le16(REG_READ_D(sc->sc_ah, AR_STA_ID1) & 0xffff, addr + 4);
+	len += snprintf(buf + len, sizeof(buf) - len,
+			"addr: %pM\n", addr);
+	put_unaligned_le32(REG_READ_D(sc->sc_ah, AR_BSSMSKL), addr);
+	put_unaligned_le16(REG_READ_D(sc->sc_ah, AR_BSSMSKU) & 0xffff, addr + 4);
+	len += snprintf(buf + len, sizeof(buf) - len,
+			"addrmask: %pM\n", addr);
+	tmp = ath9k_hw_getrxfilter(sc->sc_ah);
+	len += snprintf(buf + len, sizeof(buf) - len,
+			"rfilt: 0x%x", tmp);
+	if (tmp & ATH9K_RX_FILTER_UCAST)
+		len += snprintf(buf + len, sizeof(buf) - len, " UCAST");
+	if (tmp & ATH9K_RX_FILTER_MCAST)
+		len += snprintf(buf + len, sizeof(buf) - len, " MCAST");
+	if (tmp & ATH9K_RX_FILTER_BCAST)
+		len += snprintf(buf + len, sizeof(buf) - len, " BCAST");
+	if (tmp & ATH9K_RX_FILTER_CONTROL)
+		len += snprintf(buf + len, sizeof(buf) - len, " CONTROL");
+	if (tmp & ATH9K_RX_FILTER_BEACON)
+		len += snprintf(buf + len, sizeof(buf) - len, " BEACON");
+	if (tmp & ATH9K_RX_FILTER_PROM)
+		len += snprintf(buf + len, sizeof(buf) - len, " PROM");
+	if (tmp & ATH9K_RX_FILTER_PROBEREQ)
+		len += snprintf(buf + len, sizeof(buf) - len, " PROBEREQ");
+	if (tmp & ATH9K_RX_FILTER_PHYERR)
+		len += snprintf(buf + len, sizeof(buf) - len, " PHYERR");
+	if (tmp & ATH9K_RX_FILTER_MYBEACON)
+		len += snprintf(buf + len, sizeof(buf) - len, " MYBEACON");
+	if (tmp & ATH9K_RX_FILTER_COMP_BAR)
+		len += snprintf(buf + len, sizeof(buf) - len, " COMP_BAR");
+	if (tmp & ATH9K_RX_FILTER_PSPOLL)
+		len += snprintf(buf + len, sizeof(buf) - len, " PSPOLL");
+	if (tmp & ATH9K_RX_FILTER_PHYRADAR)
+		len += snprintf(buf + len, sizeof(buf) - len, " PHYRADAR");
+	if (tmp & ATH9K_RX_FILTER_MCAST_BCAST_ALL)
+		len += snprintf(buf + len, sizeof(buf) - len, " MCAST_BCAST_ALL\n");
+	else
+		len += snprintf(buf + len, sizeof(buf) - len, "\n");
+
+	/* Put variable-length stuff down here, and check for overflows. */
 	for (i = 0; i < sc->num_sec_wiphy; i++) {
 		struct ath_wiphy *aphy = sc->sec_wiphy[i];
 		if (aphy == NULL)
@@ -508,16 +551,6 @@
 				ath_wiphy_state_str(aphy->state),
 				aphy->chan_idx, aphy->chan_is_ht);
 	}
-
-	put_unaligned_le32(REG_READ_D(sc->sc_ah, AR_STA_ID0), addr);
-	put_unaligned_le16(REG_READ_D(sc->sc_ah, AR_STA_ID1) & 0xffff, addr + 4);
-	len += snprintf(buf + len, sizeof(buf) - len,
-			"addr: %pM\n", addr);
-	put_unaligned_le32(REG_READ_D(sc->sc_ah, AR_BSSMSKL), addr);
-	put_unaligned_le16(REG_READ_D(sc->sc_ah, AR_BSSMSKU) & 0xffff, addr + 4);
-	len += snprintf(buf + len, sizeof(buf) - len,
-			"addrmask: %pM\n", addr);
-
 	if (len > sizeof(buf))
 		len = sizeof(buf);
 
diff --git a/drivers/net/wireless/ath/ath9k/eeprom.h b/drivers/net/wireless/ath/ath9k/eeprom.h
index 0b09db0..3030564 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom.h
+++ b/drivers/net/wireless/ath/ath9k/eeprom.h
@@ -266,6 +266,8 @@
 	EEP_INTERNAL_REGULATOR,
 	EEP_SWREG,
 	EEP_PAPRD,
+	EEP_MODAL_VER,
+	EEP_ANT_DIV_CTL1,
 };
 
 enum ar5416_rates {
@@ -670,7 +672,8 @@
 	bool (*fill_eeprom)(struct ath_hw *hw);
 	int (*get_eeprom_ver)(struct ath_hw *hw);
 	int (*get_eeprom_rev)(struct ath_hw *hw);
-	u8 (*get_num_ant_config)(struct ath_hw *hw, enum ieee80211_band band);
+	u8 (*get_num_ant_config)(struct ath_hw *hw,
+				 enum ath9k_hal_freq_band band);
 	u32 (*get_eeprom_antenna_cfg)(struct ath_hw *hw,
 				      struct ath9k_channel *chan);
 	void (*set_board_values)(struct ath_hw *hw, struct ath9k_channel *chan);
diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c
index 9cccd12..ead8b0d 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c
@@ -213,6 +213,10 @@
 		return 0;
 	case EEP_PWR_TABLE_OFFSET:
 		return AR5416_PWR_TABLE_OFFSET_DB;
+	case EEP_MODAL_VER:
+		return pModal->version;
+	case EEP_ANT_DIV_CTL1:
+		return pModal->antdiv_ctl1;
 	default:
 		return 0;
 	}
@@ -1157,7 +1161,7 @@
 }
 
 static u8 ath9k_hw_4k_get_num_ant_config(struct ath_hw *ah,
-					 enum ieee80211_band freq_band)
+					 enum ath9k_hal_freq_band freq_band)
 {
 	return 1;
 }
diff --git a/drivers/net/wireless/ath/ath9k/eeprom_9287.c b/drivers/net/wireless/ath/ath9k/eeprom_9287.c
index dff2da7..e618651 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom_9287.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c
@@ -1126,7 +1126,7 @@
 }
 
 static u8 ath9k_hw_ar9287_get_num_ant_config(struct ath_hw *ah,
-					     enum ieee80211_band freq_band)
+					     enum ath9k_hal_freq_band freq_band)
 {
 	return 1;
 }
diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c
index afa2b73..23f480d 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom_def.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c
@@ -1418,11 +1418,11 @@
 }
 
 static u8 ath9k_hw_def_get_num_ant_config(struct ath_hw *ah,
-					  enum ieee80211_band freq_band)
+					  enum ath9k_hal_freq_band freq_band)
 {
 	struct ar5416_eeprom_def *eep = &ah->eeprom.def;
 	struct modal_eep_header *pModal =
-		&(eep->modalHeader[ATH9K_HAL_FREQ_BAND_2GHZ == freq_band]);
+		&(eep->modalHeader[freq_band]);
 	struct base_eep_header *pBase = &eep->baseEepHeader;
 	u8 num_ant_config;
 
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c
index 495f189..728d904 100644
--- a/drivers/net/wireless/ath/ath9k/hif_usb.c
+++ b/drivers/net/wireless/ath/ath9k/hif_usb.c
@@ -92,10 +92,10 @@
 	cmd->skb = skb;
 	cmd->hif_dev = hif_dev;
 
-	usb_fill_int_urb(urb, hif_dev->udev,
-			 usb_sndintpipe(hif_dev->udev, USB_REG_OUT_PIPE),
+	usb_fill_bulk_urb(urb, hif_dev->udev,
+			 usb_sndbulkpipe(hif_dev->udev, USB_REG_OUT_PIPE),
 			 skb->data, skb->len,
-			 hif_usb_regout_cb, cmd, 1);
+			 hif_usb_regout_cb, cmd);
 
 	usb_anchor_urb(urb, &hif_dev->regout_submitted);
 	ret = usb_submit_urb(urb, GFP_KERNEL);
@@ -541,7 +541,8 @@
 		}
 
 		usb_fill_int_urb(urb, hif_dev->udev,
-				 usb_rcvintpipe(hif_dev->udev, USB_REG_IN_PIPE),
+				 usb_rcvbulkpipe(hif_dev->udev,
+						 USB_REG_IN_PIPE),
 				 nskb->data, MAX_REG_IN_BUF_SIZE,
 				 ath9k_hif_usb_reg_in_cb, nskb, 1);
 
@@ -720,7 +721,8 @@
 		goto err;
 
 	usb_fill_int_urb(hif_dev->reg_in_urb, hif_dev->udev,
-			 usb_rcvintpipe(hif_dev->udev, USB_REG_IN_PIPE),
+			 usb_rcvbulkpipe(hif_dev->udev,
+					 USB_REG_IN_PIPE),
 			 skb->data, MAX_REG_IN_BUF_SIZE,
 			 ath9k_hif_usb_reg_in_cb, skb, 1);
 
@@ -822,7 +824,9 @@
 
 static int ath9k_hif_usb_dev_init(struct hif_device_usb *hif_dev)
 {
-	int ret;
+	int ret, idx;
+	struct usb_host_interface *alt = &hif_dev->interface->altsetting[0];
+	struct usb_endpoint_descriptor *endp;
 
 	/* Request firmware */
 	ret = request_firmware(&hif_dev->firmware, hif_dev->fw_name,
@@ -850,6 +854,22 @@
 		goto err_fw_download;
 	}
 
+	/* On downloading the firmware to the target, the USB descriptor of EP4
+	 * is 'patched' to change the type of the endpoint to Bulk. This will
+	 * bring down CPU usage during the scan period.
+	 */
+	for (idx = 0; idx < alt->desc.bNumEndpoints; idx++) {
+		endp = &alt->endpoint[idx].desc;
+		if (((endp->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK)
+				== 0x04) &&
+		    ((endp->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+				== USB_ENDPOINT_XFER_INT)) {
+			endp->bmAttributes &= ~USB_ENDPOINT_XFERTYPE_MASK;
+			endp->bmAttributes |= USB_ENDPOINT_XFER_BULK;
+			endp->bInterval = 0;
+		}
+	}
+
 	return 0;
 
 err_fw_download:
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
index 695e2b0..33850c9 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
@@ -566,7 +566,7 @@
 	 * reset the contents on initial power up.
 	 */
 	for (i = 0; i < common->keymax; i++)
-		ath9k_hw_keyreset(priv->ah, (u16) i);
+		ath_hw_keyreset(common, (u16) i);
 }
 
 static void ath9k_init_channels_rates(struct ath9k_htc_priv *priv)
@@ -601,8 +601,7 @@
 	common->tx_chainmask = priv->ah->caps.tx_chainmask;
 	common->rx_chainmask = priv->ah->caps.rx_chainmask;
 
-	if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
-		memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
+	memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
 
 	priv->ah->opmode = NL80211_IFTYPE_STATION;
 }
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
index f467207..5124d04 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
@@ -137,8 +137,6 @@
 	if (priv->op_flags & OP_FULL_RESET)
 		fastcc = false;
 
-	/* Fiddle around with fastcc later on, for now just use full reset */
-	fastcc = false;
 	ath9k_htc_ps_wakeup(priv);
 	htc_stop(priv->htc);
 	WMI_CMD(WMI_DISABLE_INTR_CMDID);
@@ -146,9 +144,10 @@
 	WMI_CMD(WMI_STOP_RECV_CMDID);
 
 	ath_print(common, ATH_DBG_CONFIG,
-		  "(%u MHz) -> (%u MHz), HT: %d, HT40: %d\n",
+		  "(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n",
 		  priv->ah->curchan->channel,
-		  channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf));
+		  channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf),
+		  fastcc);
 
 	caldata = &priv->caldata[channel->hw_value];
 	ret = ath9k_hw_reset(ah, hchan, caldata, fastcc);
@@ -1591,7 +1590,7 @@
 
 	switch (cmd) {
 	case SET_KEY:
-		ret = ath9k_cmn_key_config(common, vif, sta, key);
+		ret = ath_key_config(common, vif, sta, key);
 		if (ret >= 0) {
 			key->hw_key_idx = ret;
 			/* push IV and Michael MIC generation to stack */
@@ -1605,7 +1604,7 @@
 		}
 		break;
 	case DISABLE_KEY:
-		ath9k_cmn_key_delete(common, key);
+		ath_key_delete(common, key);
 		break;
 	default:
 		ret = -EINVAL;
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
index 2a6e45a..c99600a 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
@@ -415,8 +415,7 @@
 	ath9k_hw_setrxfilter(ah, rfilt);
 
 	/* configure bssid mask */
-	if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
-		ath_hw_setbssidmask(common);
+	ath_hw_setbssidmask(common);
 
 	/* configure operational mode */
 	ath9k_hw_setopmode(ah);
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 3384ca1..0b2ff98 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -1258,11 +1258,13 @@
 	    (chan->channel != ah->curchan->channel) &&
 	    ((chan->channelFlags & CHANNEL_ALL) ==
 	     (ah->curchan->channelFlags & CHANNEL_ALL)) &&
-	    !AR_SREV_9280(ah)) {
+	    (!AR_SREV_9280(ah) || AR_DEVID_7010(ah))) {
 
 		if (ath9k_hw_channel_change(ah, chan)) {
 			ath9k_hw_loadnf(ah, ah->curchan);
 			ath9k_hw_start_nfcal(ah, true);
+			if (AR_SREV_9271(ah))
+				ar9002_hw_load_ani_reg(ah, chan);
 			return 0;
 		}
 	}
@@ -1474,283 +1476,6 @@
 }
 EXPORT_SYMBOL(ath9k_hw_reset);
 
-/************************/
-/* Key Cache Management */
-/************************/
-
-bool ath9k_hw_keyreset(struct ath_hw *ah, u16 entry)
-{
-	u32 keyType;
-
-	if (entry >= ah->caps.keycache_size) {
-		ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL,
-			  "keychache entry %u out of range\n", entry);
-		return false;
-	}
-
-	keyType = REG_READ(ah, AR_KEYTABLE_TYPE(entry));
-
-	REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), 0);
-	REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), 0);
-	REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), 0);
-	REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), 0);
-	REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), 0);
-	REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), AR_KEYTABLE_TYPE_CLR);
-	REG_WRITE(ah, AR_KEYTABLE_MAC0(entry), 0);
-	REG_WRITE(ah, AR_KEYTABLE_MAC1(entry), 0);
-
-	if (keyType == AR_KEYTABLE_TYPE_TKIP && ATH9K_IS_MIC_ENABLED(ah)) {
-		u16 micentry = entry + 64;
-
-		REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), 0);
-		REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), 0);
-		REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), 0);
-		REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), 0);
-
-	}
-
-	return true;
-}
-EXPORT_SYMBOL(ath9k_hw_keyreset);
-
-static bool ath9k_hw_keysetmac(struct ath_hw *ah, u16 entry, const u8 *mac)
-{
-	u32 macHi, macLo;
-	u32 unicast_flag = AR_KEYTABLE_VALID;
-
-	if (entry >= ah->caps.keycache_size) {
-		ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL,
-			  "keychache entry %u out of range\n", entry);
-		return false;
-	}
-
-	if (mac != NULL) {
-		/*
-		 * AR_KEYTABLE_VALID indicates that the address is a unicast
-		 * address, which must match the transmitter address for
-		 * decrypting frames.
-		 * Not setting this bit allows the hardware to use the key
-		 * for multicast frame decryption.
-		 */
-		if (mac[0] & 0x01)
-			unicast_flag = 0;
-
-		macHi = (mac[5] << 8) | mac[4];
-		macLo = (mac[3] << 24) |
-			(mac[2] << 16) |
-			(mac[1] << 8) |
-			mac[0];
-		macLo >>= 1;
-		macLo |= (macHi & 1) << 31;
-		macHi >>= 1;
-	} else {
-		macLo = macHi = 0;
-	}
-	REG_WRITE(ah, AR_KEYTABLE_MAC0(entry), macLo);
-	REG_WRITE(ah, AR_KEYTABLE_MAC1(entry), macHi | unicast_flag);
-
-	return true;
-}
-
-bool ath9k_hw_set_keycache_entry(struct ath_hw *ah, u16 entry,
-				 const struct ath9k_keyval *k,
-				 const u8 *mac)
-{
-	const struct ath9k_hw_capabilities *pCap = &ah->caps;
-	struct ath_common *common = ath9k_hw_common(ah);
-	u32 key0, key1, key2, key3, key4;
-	u32 keyType;
-
-	if (entry >= pCap->keycache_size) {
-		ath_print(common, ATH_DBG_FATAL,
-			  "keycache entry %u out of range\n", entry);
-		return false;
-	}
-
-	switch (k->kv_type) {
-	case ATH9K_CIPHER_AES_OCB:
-		keyType = AR_KEYTABLE_TYPE_AES;
-		break;
-	case ATH9K_CIPHER_AES_CCM:
-		if (!(pCap->hw_caps & ATH9K_HW_CAP_CIPHER_AESCCM)) {
-			ath_print(common, ATH_DBG_ANY,
-				  "AES-CCM not supported by mac rev 0x%x\n",
-				  ah->hw_version.macRev);
-			return false;
-		}
-		keyType = AR_KEYTABLE_TYPE_CCM;
-		break;
-	case ATH9K_CIPHER_TKIP:
-		keyType = AR_KEYTABLE_TYPE_TKIP;
-		if (ATH9K_IS_MIC_ENABLED(ah)
-		    && entry + 64 >= pCap->keycache_size) {
-			ath_print(common, ATH_DBG_ANY,
-				  "entry %u inappropriate for TKIP\n", entry);
-			return false;
-		}
-		break;
-	case ATH9K_CIPHER_WEP:
-		if (k->kv_len < WLAN_KEY_LEN_WEP40) {
-			ath_print(common, ATH_DBG_ANY,
-				  "WEP key length %u too small\n", k->kv_len);
-			return false;
-		}
-		if (k->kv_len <= WLAN_KEY_LEN_WEP40)
-			keyType = AR_KEYTABLE_TYPE_40;
-		else if (k->kv_len <= WLAN_KEY_LEN_WEP104)
-			keyType = AR_KEYTABLE_TYPE_104;
-		else
-			keyType = AR_KEYTABLE_TYPE_128;
-		break;
-	case ATH9K_CIPHER_CLR:
-		keyType = AR_KEYTABLE_TYPE_CLR;
-		break;
-	default:
-		ath_print(common, ATH_DBG_FATAL,
-			  "cipher %u not supported\n", k->kv_type);
-		return false;
-	}
-
-	key0 = get_unaligned_le32(k->kv_val + 0);
-	key1 = get_unaligned_le16(k->kv_val + 4);
-	key2 = get_unaligned_le32(k->kv_val + 6);
-	key3 = get_unaligned_le16(k->kv_val + 10);
-	key4 = get_unaligned_le32(k->kv_val + 12);
-	if (k->kv_len <= WLAN_KEY_LEN_WEP104)
-		key4 &= 0xff;
-
-	/*
-	 * Note: Key cache registers access special memory area that requires
-	 * two 32-bit writes to actually update the values in the internal
-	 * memory. Consequently, the exact order and pairs used here must be
-	 * maintained.
-	 */
-
-	if (keyType == AR_KEYTABLE_TYPE_TKIP && ATH9K_IS_MIC_ENABLED(ah)) {
-		u16 micentry = entry + 64;
-
-		/*
-		 * Write inverted key[47:0] first to avoid Michael MIC errors
-		 * on frames that could be sent or received at the same time.
-		 * The correct key will be written in the end once everything
-		 * else is ready.
-		 */
-		REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), ~key0);
-		REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), ~key1);
-
-		/* Write key[95:48] */
-		REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), key2);
-		REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), key3);
-
-		/* Write key[127:96] and key type */
-		REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4);
-		REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), keyType);
-
-		/* Write MAC address for the entry */
-		(void) ath9k_hw_keysetmac(ah, entry, mac);
-
-		if (ah->misc_mode & AR_PCU_MIC_NEW_LOC_ENA) {
-			/*
-			 * TKIP uses two key cache entries:
-			 * Michael MIC TX/RX keys in the same key cache entry
-			 * (idx = main index + 64):
-			 * key0 [31:0] = RX key [31:0]
-			 * key1 [15:0] = TX key [31:16]
-			 * key1 [31:16] = reserved
-			 * key2 [31:0] = RX key [63:32]
-			 * key3 [15:0] = TX key [15:0]
-			 * key3 [31:16] = reserved
-			 * key4 [31:0] = TX key [63:32]
-			 */
-			u32 mic0, mic1, mic2, mic3, mic4;
-
-			mic0 = get_unaligned_le32(k->kv_mic + 0);
-			mic2 = get_unaligned_le32(k->kv_mic + 4);
-			mic1 = get_unaligned_le16(k->kv_txmic + 2) & 0xffff;
-			mic3 = get_unaligned_le16(k->kv_txmic + 0) & 0xffff;
-			mic4 = get_unaligned_le32(k->kv_txmic + 4);
-
-			/* Write RX[31:0] and TX[31:16] */
-			REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), mic0);
-			REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), mic1);
-
-			/* Write RX[63:32] and TX[15:0] */
-			REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), mic2);
-			REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), mic3);
-
-			/* Write TX[63:32] and keyType(reserved) */
-			REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), mic4);
-			REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry),
-				  AR_KEYTABLE_TYPE_CLR);
-
-		} else {
-			/*
-			 * TKIP uses four key cache entries (two for group
-			 * keys):
-			 * Michael MIC TX/RX keys are in different key cache
-			 * entries (idx = main index + 64 for TX and
-			 * main index + 32 + 96 for RX):
-			 * key0 [31:0] = TX/RX MIC key [31:0]
-			 * key1 [31:0] = reserved
-			 * key2 [31:0] = TX/RX MIC key [63:32]
-			 * key3 [31:0] = reserved
-			 * key4 [31:0] = reserved
-			 *
-			 * Upper layer code will call this function separately
-			 * for TX and RX keys when these registers offsets are
-			 * used.
-			 */
-			u32 mic0, mic2;
-
-			mic0 = get_unaligned_le32(k->kv_mic + 0);
-			mic2 = get_unaligned_le32(k->kv_mic + 4);
-
-			/* Write MIC key[31:0] */
-			REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), mic0);
-			REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), 0);
-
-			/* Write MIC key[63:32] */
-			REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), mic2);
-			REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), 0);
-
-			/* Write TX[63:32] and keyType(reserved) */
-			REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), 0);
-			REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry),
-				  AR_KEYTABLE_TYPE_CLR);
-		}
-
-		/* MAC address registers are reserved for the MIC entry */
-		REG_WRITE(ah, AR_KEYTABLE_MAC0(micentry), 0);
-		REG_WRITE(ah, AR_KEYTABLE_MAC1(micentry), 0);
-
-		/*
-		 * Write the correct (un-inverted) key[47:0] last to enable
-		 * TKIP now that all other registers are set with correct
-		 * values.
-		 */
-		REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), key0);
-		REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), key1);
-	} else {
-		/* Write key[47:0] */
-		REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), key0);
-		REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), key1);
-
-		/* Write key[95:48] */
-		REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), key2);
-		REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), key3);
-
-		/* Write key[127:96] and key type */
-		REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4);
-		REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), keyType);
-
-		/* Write MAC address for the entry */
-		(void) ath9k_hw_keysetmac(ah, entry, mac);
-	}
-
-	return true;
-}
-EXPORT_SYMBOL(ath9k_hw_set_keycache_entry);
-
 /******************************/
 /* Power Management (Chipset) */
 /******************************/
@@ -2056,6 +1781,7 @@
 	struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw;
 
 	u16 capField = 0, eeval;
+	u8 ant_div_ctl1;
 
 	eeval = ah->eep_ops->get_eeprom(ah, EEP_REG_0);
 	regulatory->current_rd = eeval;
@@ -2140,24 +1866,13 @@
 	pCap->low_5ghz_chan = 4920;
 	pCap->high_5ghz_chan = 6100;
 
-	pCap->hw_caps &= ~ATH9K_HW_CAP_CIPHER_CKIP;
-	pCap->hw_caps |= ATH9K_HW_CAP_CIPHER_TKIP;
-	pCap->hw_caps |= ATH9K_HW_CAP_CIPHER_AESCCM;
-
-	pCap->hw_caps &= ~ATH9K_HW_CAP_MIC_CKIP;
-	pCap->hw_caps |= ATH9K_HW_CAP_MIC_TKIP;
-	pCap->hw_caps |= ATH9K_HW_CAP_MIC_AESCCM;
+	common->crypt_caps |= ATH_CRYPT_CAP_CIPHER_AESCCM;
 
 	if (ah->config.ht_enable)
 		pCap->hw_caps |= ATH9K_HW_CAP_HT;
 	else
 		pCap->hw_caps &= ~ATH9K_HW_CAP_HT;
 
-	pCap->hw_caps |= ATH9K_HW_CAP_GTT;
-	pCap->hw_caps |= ATH9K_HW_CAP_VEOL;
-	pCap->hw_caps |= ATH9K_HW_CAP_BSSIDMASK;
-	pCap->hw_caps &= ~ATH9K_HW_CAP_MCAST_KEYSEARCH;
-
 	if (capField & AR_EEPROM_EEPCAP_MAXQCU)
 		pCap->total_queues =
 			MS(capField, AR_EEPROM_EEPCAP_MAXQCU);
@@ -2170,8 +1885,6 @@
 	else
 		pCap->keycache_size = AR_KEYTABLE_SIZE;
 
-	pCap->hw_caps |= ATH9K_HW_CAP_FASTCC;
-
 	if (AR_SREV_9285(ah) || AR_SREV_9271(ah))
 		pCap->tx_triglevel_max = MAX_TX_FIFO_THRESHOLD >> 1;
 	else
@@ -2280,6 +1993,14 @@
 	if (AR_SREV_9287_10_OR_LATER(ah) || AR_SREV_9271(ah))
 		pCap->hw_caps |= ATH9K_HW_CAP_SGI_20;
 
+	if (AR_SREV_9285(ah))
+		if (ah->eep_ops->get_eeprom(ah, EEP_MODAL_VER) >= 3) {
+			ant_div_ctl1 =
+				ah->eep_ops->get_eeprom(ah, EEP_ANT_DIV_CTL1);
+			if ((ant_div_ctl1 & 0x1) && ((ant_div_ctl1 >> 3) & 0x1))
+				pCap->hw_caps |= ATH9K_HW_CAP_ANT_DIV_COMB;
+		}
+
 	return 0;
 }
 
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index 1601dd4..df47f79 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -181,29 +181,19 @@
 };
 
 enum ath9k_hw_caps {
-	ATH9K_HW_CAP_MIC_AESCCM                 = BIT(0),
-	ATH9K_HW_CAP_MIC_CKIP                   = BIT(1),
-	ATH9K_HW_CAP_MIC_TKIP                   = BIT(2),
-	ATH9K_HW_CAP_CIPHER_AESCCM              = BIT(3),
-	ATH9K_HW_CAP_CIPHER_CKIP                = BIT(4),
-	ATH9K_HW_CAP_CIPHER_TKIP                = BIT(5),
-	ATH9K_HW_CAP_VEOL                       = BIT(6),
-	ATH9K_HW_CAP_BSSIDMASK                  = BIT(7),
-	ATH9K_HW_CAP_MCAST_KEYSEARCH            = BIT(8),
-	ATH9K_HW_CAP_HT                         = BIT(9),
-	ATH9K_HW_CAP_GTT                        = BIT(10),
-	ATH9K_HW_CAP_FASTCC                     = BIT(11),
-	ATH9K_HW_CAP_RFSILENT                   = BIT(12),
-	ATH9K_HW_CAP_CST                        = BIT(13),
-	ATH9K_HW_CAP_ENHANCEDPM                 = BIT(14),
-	ATH9K_HW_CAP_AUTOSLEEP                  = BIT(15),
-	ATH9K_HW_CAP_4KB_SPLITTRANS             = BIT(16),
-	ATH9K_HW_CAP_EDMA			= BIT(17),
-	ATH9K_HW_CAP_RAC_SUPPORTED		= BIT(18),
-	ATH9K_HW_CAP_LDPC			= BIT(19),
-	ATH9K_HW_CAP_FASTCLOCK			= BIT(20),
-	ATH9K_HW_CAP_SGI_20			= BIT(21),
-	ATH9K_HW_CAP_PAPRD			= BIT(22),
+	ATH9K_HW_CAP_HT                         = BIT(0),
+	ATH9K_HW_CAP_RFSILENT                   = BIT(1),
+	ATH9K_HW_CAP_CST                        = BIT(2),
+	ATH9K_HW_CAP_ENHANCEDPM                 = BIT(3),
+	ATH9K_HW_CAP_AUTOSLEEP                  = BIT(4),
+	ATH9K_HW_CAP_4KB_SPLITTRANS             = BIT(5),
+	ATH9K_HW_CAP_EDMA			= BIT(6),
+	ATH9K_HW_CAP_RAC_SUPPORTED		= BIT(7),
+	ATH9K_HW_CAP_LDPC			= BIT(8),
+	ATH9K_HW_CAP_FASTCLOCK			= BIT(9),
+	ATH9K_HW_CAP_SGI_20			= BIT(10),
+	ATH9K_HW_CAP_PAPRD			= BIT(11),
+	ATH9K_HW_CAP_ANT_DIV_COMB		= BIT(12),
 };
 
 struct ath9k_hw_capabilities {
@@ -495,6 +485,12 @@
 	} timer_mask;
 };
 
+struct ath_hw_antcomb_conf {
+	u8 main_lna_conf;
+	u8 alt_lna_conf;
+	u8 fast_div_bias;
+};
+
 /**
  * struct ath_hw_private_ops - callbacks used internally by hardware code
  *
@@ -874,12 +870,6 @@
 int ath9k_hw_fill_cap_info(struct ath_hw *ah);
 u32 ath9k_regd_get_ctl(struct ath_regulatory *reg, struct ath9k_channel *chan);
 
-/* Key Cache Management */
-bool ath9k_hw_keyreset(struct ath_hw *ah, u16 entry);
-bool ath9k_hw_set_keycache_entry(struct ath_hw *ah, u16 entry,
-				 const struct ath9k_keyval *k,
-				 const u8 *mac);
-
 /* GPIO / RFKILL / Antennae */
 void ath9k_hw_cfg_gpio_input(struct ath_hw *ah, u32 gpio);
 u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio);
@@ -888,6 +878,10 @@
 void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val);
 u32 ath9k_hw_getdefantenna(struct ath_hw *ah);
 void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna);
+void ath9k_hw_antdiv_comb_conf_get(struct ath_hw *ah,
+				   struct ath_hw_antcomb_conf *antconf);
+void ath9k_hw_antdiv_comb_conf_set(struct ath_hw *ah,
+				   struct ath_hw_antcomb_conf *antconf);
 
 /* General Operation */
 bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout);
@@ -985,6 +979,7 @@
 void ar9002_hw_attach_ops(struct ath_hw *ah);
 void ar9003_hw_attach_ops(struct ath_hw *ah);
 
+void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan);
 /*
  * ANI work can be shared between all families but a next
  * generation implementation of ANI will be used only for AR9003 only
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 3dbff8d..573899e 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -381,7 +381,7 @@
 	 * reset the contents on initial power up.
 	 */
 	for (i = 0; i < common->keymax; i++)
-		ath9k_hw_keyreset(sc->sc_ah, (u16) i);
+		ath_hw_keyreset(common, (u16) i);
 
 	/*
 	 * Check whether the separate key cache entries
@@ -389,8 +389,8 @@
 	 * With split mic keys the number of stations is limited
 	 * to 27 otherwise 59.
 	 */
-	if (!(sc->sc_ah->misc_mode & AR_PCU_MIC_NEW_LOC_ENA))
-		common->splitmic = 1;
+	if (sc->sc_ah->misc_mode & AR_PCU_MIC_NEW_LOC_ENA)
+		common->crypt_caps |= ATH_CRYPT_CAP_MIC_COMBINED;
 }
 
 static int ath9k_init_btcoex(struct ath_softc *sc)
@@ -522,8 +522,7 @@
 	ath9k_hw_set_diversity(sc->sc_ah, true);
 	sc->rx.defant = ath9k_hw_getdefantenna(sc->sc_ah);
 
-	if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
-		memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
+	memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
 
 	sc->beacon.slottime = ATH9K_SLOT_TIME_9;
 
@@ -531,6 +530,9 @@
 		sc->beacon.bslot[i] = NULL;
 		sc->beacon.bslot_aphy[i] = NULL;
 	}
+
+	if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB)
+		sc->ant_comb.count = ATH_ANT_DIV_COMB_INIT_COUNT;
 }
 
 static int ath9k_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid,
@@ -641,7 +643,8 @@
 		BIT(NL80211_IFTYPE_ADHOC) |
 		BIT(NL80211_IFTYPE_MESH_POINT);
 
-	hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+	if (AR_SREV_5416(sc->sc_ah))
+		hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
 
 	hw->queues = 4;
 	hw->max_rates = 4;
@@ -651,7 +654,9 @@
 	hw->sta_data_size = sizeof(struct ath_node);
 	hw->vif_data_size = sizeof(struct ath_vif);
 
+#ifdef CONFIG_ATH9K_RATE_CONTROL
 	hw->rate_control_algorithm = "ath9k_rate_control";
+#endif
 
 	if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes))
 		hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c
index 0b7d125..3efda8a 100644
--- a/drivers/net/wireless/ath/ath9k/mac.c
+++ b/drivers/net/wireless/ath/ath9k/mac.c
@@ -714,6 +714,8 @@
 		else if ((ads.ds_rxstatus8 & AR_MichaelErr) &&
 		         rs->rs_keyix != ATH9K_RXKEYIX_INVALID)
 			rs->rs_status |= ATH9K_RXERR_MIC;
+		else if (ads.ds_rxstatus8 & AR_KeyMiss)
+			rs->rs_status |= ATH9K_RXERR_DECRYPT;
 	}
 
 	return 0;
diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h
index 2633896..7c1a34d 100644
--- a/drivers/net/wireless/ath/ath9k/mac.h
+++ b/drivers/net/wireless/ath/ath9k/mac.h
@@ -660,17 +660,6 @@
 	u32 RateFlags;
 };
 
-struct ath9k_keyval {
-	u8 kv_type;
-	u8 kv_pad;
-	u16 kv_len;
-	u8 kv_val[16]; /* TK */
-	u8 kv_mic[8]; /* Michael MIC key */
-	u8 kv_txmic[8]; /* Michael MIC TX key (used only if the hardware
-			 * supports both MIC keys in the same key cache entry;
-			 * in that case, kv_mic is the RX key) */
-};
-
 enum ath9k_key_type {
 	ATH9K_KEY_TYPE_CLEAR,
 	ATH9K_KEY_TYPE_WEP,
@@ -678,16 +667,6 @@
 	ATH9K_KEY_TYPE_TKIP,
 };
 
-enum ath9k_cipher {
-	ATH9K_CIPHER_WEP = 0,
-	ATH9K_CIPHER_AES_OCB = 1,
-	ATH9K_CIPHER_AES_CCM = 2,
-	ATH9K_CIPHER_CKIP = 3,
-	ATH9K_CIPHER_TKIP = 4,
-	ATH9K_CIPHER_CLR = 5,
-	ATH9K_CIPHER_MIC = 127
-};
-
 struct ath_hw;
 struct ath9k_channel;
 
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 1165f90..8b327bc 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -255,10 +255,10 @@
 	ath_update_txpow(sc);
 	ath9k_hw_set_interrupts(ah, ah->imask);
 
-	if (!(sc->sc_flags & (SC_OP_OFFCHANNEL | SC_OP_SCANNING))) {
-		ath_start_ani(common);
-		ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
+	if (!(sc->sc_flags & (SC_OP_OFFCHANNEL))) {
 		ath_beacon_config(sc, NULL);
+		ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
+		ath_start_ani(common);
 	}
 
  ps_restore:
@@ -957,7 +957,7 @@
 
 	ath_update_txpow(sc);
 
-	if (sc->sc_flags & SC_OP_BEACONS)
+	if ((sc->sc_flags & SC_OP_BEACONS) || !(sc->sc_flags & (SC_OP_OFFCHANNEL)))
 		ath_beacon_config(sc, NULL);	/* restart beacons */
 
 	ath9k_hw_set_interrupts(ah, ah->imask);
@@ -1156,8 +1156,7 @@
 	else
 		ah->imask |= ATH9K_INT_RX;
 
-	if (ah->caps.hw_caps & ATH9K_HW_CAP_GTT)
-		ah->imask |= ATH9K_INT_GTT;
+	ah->imask |= ATH9K_INT_GTT;
 
 	if (ah->caps.hw_caps & ATH9K_HW_CAP_HT)
 		ah->imask |= ATH9K_INT_CST;
@@ -1379,12 +1378,6 @@
 
 	mutex_lock(&sc->mutex);
 
-	if (!(ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) &&
-	    sc->nvifs > 0) {
-		ret = -ENOBUFS;
-		goto out;
-	}
-
 	switch (vif->type) {
 	case NL80211_IFTYPE_STATION:
 		ic_opmode = NL80211_IFTYPE_STATION;
@@ -1414,8 +1407,7 @@
 
 	sc->nvifs++;
 
-	if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
-		ath9k_set_bssid_mask(hw);
+	ath9k_set_bssid_mask(hw, vif);
 
 	if (sc->nvifs > 1)
 		goto out; /* skip global settings for secondary vif */
@@ -1562,6 +1554,8 @@
 	 * IEEE80211_CONF_CHANGE_PS is only passed by mac80211 for STA mode.
 	 */
 	if (changed & IEEE80211_CONF_CHANGE_PS) {
+		unsigned long flags;
+		spin_lock_irqsave(&sc->sc_pm_lock, flags);
 		if (conf->flags & IEEE80211_CONF_PS) {
 			sc->ps_flags |= PS_ENABLED;
 			/*
@@ -1576,7 +1570,7 @@
 			sc->ps_enabled = false;
 			sc->ps_flags &= ~(PS_ENABLED |
 					  PS_NULLFUNC_COMPLETED);
-			ath9k_setpower(sc, ATH9K_PM_AWAKE);
+			ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE);
 			if (!(ah->caps.hw_caps &
 			      ATH9K_HW_CAP_AUTOSLEEP)) {
 				ath9k_hw_setrxabort(sc->sc_ah, 0);
@@ -1591,6 +1585,7 @@
 				}
 			}
 		}
+		spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
 	}
 
 	if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
@@ -1777,7 +1772,7 @@
 
 	switch (cmd) {
 	case SET_KEY:
-		ret = ath9k_cmn_key_config(common, vif, sta, key);
+		ret = ath_key_config(common, vif, sta, key);
 		if (ret >= 0) {
 			key->hw_key_idx = ret;
 			/* push IV and Michael MIC generation to stack */
@@ -1791,7 +1786,7 @@
 		}
 		break;
 	case DISABLE_KEY:
-		ath9k_cmn_key_delete(common, key);
+		ath_key_delete(common, key);
 		break;
 	default:
 		ret = -EINVAL;
@@ -1975,8 +1970,9 @@
 		break;
 	case IEEE80211_AMPDU_TX_START:
 		ath9k_ps_wakeup(sc);
-		ath_tx_aggr_start(sc, sta, tid, ssn);
-		ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+		ret = ath_tx_aggr_start(sc, sta, tid, ssn);
+		if (!ret)
+			ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
 		ath9k_ps_restore(sc);
 		break;
 	case IEEE80211_AMPDU_TX_STOP:
@@ -2039,7 +2035,6 @@
 
 	aphy->state = ATH_WIPHY_SCAN;
 	ath9k_wiphy_pause_all_forced(sc, aphy);
-	sc->sc_flags |= SC_OP_SCANNING;
 	mutex_unlock(&sc->mutex);
 }
 
@@ -2054,7 +2049,6 @@
 
 	mutex_lock(&sc->mutex);
 	aphy->state = ATH_WIPHY_ACTIVE;
-	sc->sc_flags &= ~SC_OP_SCANNING;
 	mutex_unlock(&sc->mutex);
 }
 
diff --git a/drivers/net/wireless/ath/ath9k/phy.h b/drivers/net/wireless/ath/ath9k/phy.h
index e724c2c..17969af 100644
--- a/drivers/net/wireless/ath/ath9k/phy.h
+++ b/drivers/net/wireless/ath/ath9k/phy.h
@@ -45,9 +45,6 @@
 		}							\
 	} while (0)
 
-#define ATH9K_IS_MIC_ENABLED(ah)					\
-	((ah)->sta_id1_defaults & AR_STA_ID1_CRPT_MIC_ENABLE)
-
 #define ANTSWAP_AB 0x0001
 #define REDUCE_CHAIN_0 0x00000050
 #define REDUCE_CHAIN_1 0x00000051
diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c
index e49be73..ce1cd6d 100644
--- a/drivers/net/wireless/ath/ath9k/rc.c
+++ b/drivers/net/wireless/ath/ath9k/rc.c
@@ -1320,6 +1320,22 @@
 	return caps;
 }
 
+static bool ath_tx_aggr_check(struct ath_softc *sc, struct ath_node *an,
+			      u8 tidno)
+{
+	struct ath_atx_tid *txtid;
+
+	if (!(sc->sc_flags & SC_OP_TXAGGR))
+		return false;
+
+	txtid = ATH_AN_2_TID(an, tidno);
+
+	if (!(txtid->state & (AGGR_ADDBA_COMPLETE | AGGR_ADDBA_PROGRESS)))
+			return true;
+	return false;
+}
+
+
 /***********************************/
 /* mac80211 Rate Control callbacks */
 /***********************************/
diff --git a/drivers/net/wireless/ath/ath9k/rc.h b/drivers/net/wireless/ath/ath9k/rc.h
index dc10826..268072f 100644
--- a/drivers/net/wireless/ath/ath9k/rc.h
+++ b/drivers/net/wireless/ath/ath9k/rc.h
@@ -224,7 +224,18 @@
 	ATH9K_IFT_UNPAUSE
 };
 
+#ifdef CONFIG_ATH9K_RATE_CONTROL
 int ath_rate_control_register(void);
 void ath_rate_control_unregister(void);
+#else
+static inline int ath_rate_control_register(void)
+{
+	return 0;
+}
+
+static inline void ath_rate_control_unregister(void)
+{
+}
+#endif
 
 #endif /* RC_H */
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index 534a91b..c5e7af4 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -19,6 +19,15 @@
 
 #define SKB_CB_ATHBUF(__skb)	(*((struct ath_buf **)__skb->cb))
 
+static inline bool ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta,
+					       int mindelta, int main_rssi_avg,
+					       int alt_rssi_avg, int pkt_count)
+{
+	return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
+		(alt_rssi_avg > main_rssi_avg + maxdelta)) ||
+		(alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
+}
+
 static inline bool ath9k_check_auto_sleep(struct ath_softc *sc)
 {
 	return sc->ps_enabled &&
@@ -110,8 +119,7 @@
 	ath9k_hw_setrxfilter(ah, rfilt);
 
 	/* configure bssid mask */
-	if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
-		ath_hw_setbssidmask(common);
+	ath_hw_setbssidmask(common);
 
 	/* configure operational mode */
 	ath9k_hw_setopmode(ah);
@@ -292,7 +300,7 @@
 
 	ath_opmode_init(sc);
 
-	ath9k_hw_startpcureceive(sc->sc_ah, (sc->sc_flags & SC_OP_SCANNING));
+	ath9k_hw_startpcureceive(sc->sc_ah, (sc->sc_flags & SC_OP_OFFCHANNEL));
 }
 
 static void ath_edma_stop_recv(struct ath_softc *sc)
@@ -440,6 +448,7 @@
 		rfilt |= ATH9K_RX_FILTER_CONTROL;
 
 	if ((sc->sc_ah->opmode == NL80211_IFTYPE_STATION) &&
+	    (sc->nvifs <= 1) &&
 	    !(sc->rx.rxfilter & FIF_BCN_PRBRESP_PROMISC))
 		rfilt |= ATH9K_RX_FILTER_MYBEACON;
 	else
@@ -454,9 +463,8 @@
 	if (conf_is_ht(&sc->hw->conf))
 		rfilt |= ATH9K_RX_FILTER_COMP_BAR;
 
-	if (sc->sec_wiphy || (sc->rx.rxfilter & FIF_OTHER_BSS)) {
-		/* TODO: only needed if more than one BSSID is in use in
-		 * station/adhoc mode */
+	if (sc->sec_wiphy || (sc->nvifs > 1) ||
+	    (sc->rx.rxfilter & FIF_OTHER_BSS)) {
 		/* The following may also be needed for other older chips */
 		if (sc->sc_ah->hw_version.macVersion == AR_SREV_VERSION_9160)
 			rfilt |= ATH9K_RX_FILTER_PROM;
@@ -498,7 +506,7 @@
 start_recv:
 	spin_unlock_bh(&sc->rx.rxbuflock);
 	ath_opmode_init(sc);
-	ath9k_hw_startpcureceive(ah, (sc->sc_flags & SC_OP_SCANNING));
+	ath9k_hw_startpcureceive(ah, (sc->sc_flags & SC_OP_OFFCHANNEL));
 
 	return 0;
 }
@@ -631,7 +639,7 @@
 		 * No more broadcast/multicast frames to be received at this
 		 * point.
 		 */
-		sc->ps_flags &= ~PS_WAIT_FOR_CAB;
+		sc->ps_flags &= ~(PS_WAIT_FOR_CAB | PS_WAIT_FOR_BEACON);
 		ath_print(common, ATH_DBG_PS,
 			  "All PS CAB frames received, back to sleep\n");
 	} else if ((sc->ps_flags & PS_WAIT_FOR_PSPOLL_DATA) &&
@@ -1076,6 +1084,539 @@
 		rxs->flag &= ~RX_FLAG_DECRYPTED;
 }
 
+static void ath_lnaconf_alt_good_scan(struct ath_ant_comb *antcomb,
+				      struct ath_hw_antcomb_conf ant_conf,
+				      int main_rssi_avg)
+{
+	antcomb->quick_scan_cnt = 0;
+
+	if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
+		antcomb->rssi_lna2 = main_rssi_avg;
+	else if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA1)
+		antcomb->rssi_lna1 = main_rssi_avg;
+
+	switch ((ant_conf.main_lna_conf << 4) | ant_conf.alt_lna_conf) {
+	case (0x10): /* LNA2 A-B */
+		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+		antcomb->first_quick_scan_conf =
+			ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+		antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1;
+		break;
+	case (0x20): /* LNA1 A-B */
+		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+		antcomb->first_quick_scan_conf =
+			ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+		antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2;
+		break;
+	case (0x21): /* LNA1 LNA2 */
+		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA2;
+		antcomb->first_quick_scan_conf =
+			ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+		antcomb->second_quick_scan_conf =
+			ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+		break;
+	case (0x12): /* LNA2 LNA1 */
+		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1;
+		antcomb->first_quick_scan_conf =
+			ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+		antcomb->second_quick_scan_conf =
+			ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+		break;
+	case (0x13): /* LNA2 A+B */
+		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+		antcomb->first_quick_scan_conf =
+			ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+		antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1;
+		break;
+	case (0x23): /* LNA1 A+B */
+		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+		antcomb->first_quick_scan_conf =
+			ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+		antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2;
+		break;
+	default:
+		break;
+	}
+}
+
+static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb,
+				struct ath_hw_antcomb_conf *div_ant_conf,
+				int main_rssi_avg, int alt_rssi_avg,
+				int alt_ratio)
+{
+	/* alt_good */
+	switch (antcomb->quick_scan_cnt) {
+	case 0:
+		/* set alt to main, and alt to first conf */
+		div_ant_conf->main_lna_conf = antcomb->main_conf;
+		div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
+		break;
+	case 1:
+		/* set alt to main, and alt to first conf */
+		div_ant_conf->main_lna_conf = antcomb->main_conf;
+		div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
+		antcomb->rssi_first = main_rssi_avg;
+		antcomb->rssi_second = alt_rssi_avg;
+
+		if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
+			/* main is LNA1 */
+			if (ath_is_alt_ant_ratio_better(alt_ratio,
+						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
+						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
+						main_rssi_avg, alt_rssi_avg,
+						antcomb->total_pkt_count))
+				antcomb->first_ratio = true;
+			else
+				antcomb->first_ratio = false;
+		} else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
+			if (ath_is_alt_ant_ratio_better(alt_ratio,
+						ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
+						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
+						main_rssi_avg, alt_rssi_avg,
+						antcomb->total_pkt_count))
+				antcomb->first_ratio = true;
+			else
+				antcomb->first_ratio = false;
+		} else {
+			if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
+			    (alt_rssi_avg > main_rssi_avg +
+			    ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
+			    (alt_rssi_avg > main_rssi_avg)) &&
+			    (antcomb->total_pkt_count > 50))
+				antcomb->first_ratio = true;
+			else
+				antcomb->first_ratio = false;
+		}
+		break;
+	case 2:
+		antcomb->alt_good = false;
+		antcomb->scan_not_start = false;
+		antcomb->scan = false;
+		antcomb->rssi_first = main_rssi_avg;
+		antcomb->rssi_third = alt_rssi_avg;
+
+		if (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1)
+			antcomb->rssi_lna1 = alt_rssi_avg;
+		else if (antcomb->second_quick_scan_conf ==
+			 ATH_ANT_DIV_COMB_LNA2)
+			antcomb->rssi_lna2 = alt_rssi_avg;
+		else if (antcomb->second_quick_scan_conf ==
+			 ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
+			if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2)
+				antcomb->rssi_lna2 = main_rssi_avg;
+			else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1)
+				antcomb->rssi_lna1 = main_rssi_avg;
+		}
+
+		if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
+		    ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
+			div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+		else
+			div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+
+		if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
+			if (ath_is_alt_ant_ratio_better(alt_ratio,
+						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
+						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
+						main_rssi_avg, alt_rssi_avg,
+						antcomb->total_pkt_count))
+				antcomb->second_ratio = true;
+			else
+				antcomb->second_ratio = false;
+		} else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
+			if (ath_is_alt_ant_ratio_better(alt_ratio,
+						ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
+						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
+						main_rssi_avg, alt_rssi_avg,
+						antcomb->total_pkt_count))
+				antcomb->second_ratio = true;
+			else
+				antcomb->second_ratio = false;
+		} else {
+			if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
+			    (alt_rssi_avg > main_rssi_avg +
+			    ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
+			    (alt_rssi_avg > main_rssi_avg)) &&
+			    (antcomb->total_pkt_count > 50))
+				antcomb->second_ratio = true;
+			else
+				antcomb->second_ratio = false;
+		}
+
+		/* set alt to the conf with maximun ratio */
+		if (antcomb->first_ratio && antcomb->second_ratio) {
+			if (antcomb->rssi_second > antcomb->rssi_third) {
+				/* first alt*/
+				if ((antcomb->first_quick_scan_conf ==
+				    ATH_ANT_DIV_COMB_LNA1) ||
+				    (antcomb->first_quick_scan_conf ==
+				    ATH_ANT_DIV_COMB_LNA2))
+					/* Set alt LNA1 or LNA2*/
+					if (div_ant_conf->main_lna_conf ==
+					    ATH_ANT_DIV_COMB_LNA2)
+						div_ant_conf->alt_lna_conf =
+							ATH_ANT_DIV_COMB_LNA1;
+					else
+						div_ant_conf->alt_lna_conf =
+							ATH_ANT_DIV_COMB_LNA2;
+				else
+					/* Set alt to A+B or A-B */
+					div_ant_conf->alt_lna_conf =
+						antcomb->first_quick_scan_conf;
+			} else if ((antcomb->second_quick_scan_conf ==
+				   ATH_ANT_DIV_COMB_LNA1) ||
+				   (antcomb->second_quick_scan_conf ==
+				   ATH_ANT_DIV_COMB_LNA2)) {
+				/* Set alt LNA1 or LNA2 */
+				if (div_ant_conf->main_lna_conf ==
+				    ATH_ANT_DIV_COMB_LNA2)
+					div_ant_conf->alt_lna_conf =
+						ATH_ANT_DIV_COMB_LNA1;
+				else
+					div_ant_conf->alt_lna_conf =
+						ATH_ANT_DIV_COMB_LNA2;
+			} else {
+				/* Set alt to A+B or A-B */
+				div_ant_conf->alt_lna_conf =
+					antcomb->second_quick_scan_conf;
+			}
+		} else if (antcomb->first_ratio) {
+			/* first alt */
+			if ((antcomb->first_quick_scan_conf ==
+			    ATH_ANT_DIV_COMB_LNA1) ||
+			    (antcomb->first_quick_scan_conf ==
+			    ATH_ANT_DIV_COMB_LNA2))
+					/* Set alt LNA1 or LNA2 */
+				if (div_ant_conf->main_lna_conf ==
+				    ATH_ANT_DIV_COMB_LNA2)
+					div_ant_conf->alt_lna_conf =
+							ATH_ANT_DIV_COMB_LNA1;
+				else
+					div_ant_conf->alt_lna_conf =
+							ATH_ANT_DIV_COMB_LNA2;
+			else
+				/* Set alt to A+B or A-B */
+				div_ant_conf->alt_lna_conf =
+						antcomb->first_quick_scan_conf;
+		} else if (antcomb->second_ratio) {
+				/* second alt */
+			if ((antcomb->second_quick_scan_conf ==
+			    ATH_ANT_DIV_COMB_LNA1) ||
+			    (antcomb->second_quick_scan_conf ==
+			    ATH_ANT_DIV_COMB_LNA2))
+				/* Set alt LNA1 or LNA2 */
+				if (div_ant_conf->main_lna_conf ==
+				    ATH_ANT_DIV_COMB_LNA2)
+					div_ant_conf->alt_lna_conf =
+						ATH_ANT_DIV_COMB_LNA1;
+				else
+					div_ant_conf->alt_lna_conf =
+						ATH_ANT_DIV_COMB_LNA2;
+			else
+				/* Set alt to A+B or A-B */
+				div_ant_conf->alt_lna_conf =
+						antcomb->second_quick_scan_conf;
+		} else {
+			/* main is largest */
+			if ((antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) ||
+			    (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2))
+				/* Set alt LNA1 or LNA2 */
+				if (div_ant_conf->main_lna_conf ==
+				    ATH_ANT_DIV_COMB_LNA2)
+					div_ant_conf->alt_lna_conf =
+							ATH_ANT_DIV_COMB_LNA1;
+				else
+					div_ant_conf->alt_lna_conf =
+							ATH_ANT_DIV_COMB_LNA2;
+			else
+				/* Set alt to A+B or A-B */
+				div_ant_conf->alt_lna_conf = antcomb->main_conf;
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf)
+{
+	/* Adjust the fast_div_bias based on main and alt lna conf */
+	switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) {
+	case (0x01): /* A-B LNA2 */
+		ant_conf->fast_div_bias = 0x3b;
+		break;
+	case (0x02): /* A-B LNA1 */
+		ant_conf->fast_div_bias = 0x3d;
+		break;
+	case (0x03): /* A-B A+B */
+		ant_conf->fast_div_bias = 0x1;
+		break;
+	case (0x10): /* LNA2 A-B */
+		ant_conf->fast_div_bias = 0x7;
+		break;
+	case (0x12): /* LNA2 LNA1 */
+		ant_conf->fast_div_bias = 0x2;
+		break;
+	case (0x13): /* LNA2 A+B */
+		ant_conf->fast_div_bias = 0x7;
+		break;
+	case (0x20): /* LNA1 A-B */
+		ant_conf->fast_div_bias = 0x6;
+		break;
+	case (0x21): /* LNA1 LNA2 */
+		ant_conf->fast_div_bias = 0x0;
+		break;
+	case (0x23): /* LNA1 A+B */
+		ant_conf->fast_div_bias = 0x6;
+		break;
+	case (0x30): /* A+B A-B */
+		ant_conf->fast_div_bias = 0x1;
+		break;
+	case (0x31): /* A+B LNA2 */
+		ant_conf->fast_div_bias = 0x3b;
+		break;
+	case (0x32): /* A+B LNA1 */
+		ant_conf->fast_div_bias = 0x3d;
+		break;
+	default:
+		break;
+	}
+}
+
+/* Antenna diversity and combining */
+static void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs)
+{
+	struct ath_hw_antcomb_conf div_ant_conf;
+	struct ath_ant_comb *antcomb = &sc->ant_comb;
+	int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
+	int curr_main_set, curr_bias;
+	int main_rssi = rs->rs_rssi_ctl0;
+	int alt_rssi = rs->rs_rssi_ctl1;
+	int rx_ant_conf,  main_ant_conf;
+	bool short_scan = false;
+
+	rx_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_CURRENT_SHIFT) &
+		       ATH_ANT_RX_MASK;
+	main_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_MAIN_SHIFT) &
+			 ATH_ANT_RX_MASK;
+
+	/* Record packet only when alt_rssi is positive */
+	if (alt_rssi > 0) {
+		antcomb->total_pkt_count++;
+		antcomb->main_total_rssi += main_rssi;
+		antcomb->alt_total_rssi  += alt_rssi;
+		if (main_ant_conf == rx_ant_conf)
+			antcomb->main_recv_cnt++;
+		else
+			antcomb->alt_recv_cnt++;
+	}
+
+	/* Short scan check */
+	if (antcomb->scan && antcomb->alt_good) {
+		if (time_after(jiffies, antcomb->scan_start_time +
+		    msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
+			short_scan = true;
+		else
+			if (antcomb->total_pkt_count ==
+			    ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
+				alt_ratio = ((antcomb->alt_recv_cnt * 100) /
+					    antcomb->total_pkt_count);
+				if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
+					short_scan = true;
+			}
+	}
+
+	if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
+	    rs->rs_moreaggr) && !short_scan)
+		return;
+
+	if (antcomb->total_pkt_count) {
+		alt_ratio = ((antcomb->alt_recv_cnt * 100) /
+			     antcomb->total_pkt_count);
+		main_rssi_avg = (antcomb->main_total_rssi /
+				 antcomb->total_pkt_count);
+		alt_rssi_avg = (antcomb->alt_total_rssi /
+				 antcomb->total_pkt_count);
+	}
+
+
+	ath9k_hw_antdiv_comb_conf_get(sc->sc_ah, &div_ant_conf);
+	curr_alt_set = div_ant_conf.alt_lna_conf;
+	curr_main_set = div_ant_conf.main_lna_conf;
+	curr_bias = div_ant_conf.fast_div_bias;
+
+	antcomb->count++;
+
+	if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
+		if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
+			ath_lnaconf_alt_good_scan(antcomb, div_ant_conf,
+						  main_rssi_avg);
+			antcomb->alt_good = true;
+		} else {
+			antcomb->alt_good = false;
+		}
+
+		antcomb->count = 0;
+		antcomb->scan = true;
+		antcomb->scan_not_start = true;
+	}
+
+	if (!antcomb->scan) {
+		if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
+			if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) {
+				/* Switch main and alt LNA */
+				div_ant_conf.main_lna_conf =
+						ATH_ANT_DIV_COMB_LNA2;
+				div_ant_conf.alt_lna_conf  =
+						ATH_ANT_DIV_COMB_LNA1;
+			} else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) {
+				div_ant_conf.main_lna_conf =
+						ATH_ANT_DIV_COMB_LNA1;
+				div_ant_conf.alt_lna_conf  =
+						ATH_ANT_DIV_COMB_LNA2;
+			}
+
+			goto div_comb_done;
+		} else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) &&
+			   (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) {
+			/* Set alt to another LNA */
+			if (curr_main_set == ATH_ANT_DIV_COMB_LNA2)
+				div_ant_conf.alt_lna_conf =
+						ATH_ANT_DIV_COMB_LNA1;
+			else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1)
+				div_ant_conf.alt_lna_conf =
+						ATH_ANT_DIV_COMB_LNA2;
+
+			goto div_comb_done;
+		}
+
+		if ((alt_rssi_avg < (main_rssi_avg +
+		    ATH_ANT_DIV_COMB_LNA1_LNA2_DELTA)))
+			goto div_comb_done;
+	}
+
+	if (!antcomb->scan_not_start) {
+		switch (curr_alt_set) {
+		case ATH_ANT_DIV_COMB_LNA2:
+			antcomb->rssi_lna2 = alt_rssi_avg;
+			antcomb->rssi_lna1 = main_rssi_avg;
+			antcomb->scan = true;
+			/* set to A+B */
+			div_ant_conf.main_lna_conf =
+				ATH_ANT_DIV_COMB_LNA1;
+			div_ant_conf.alt_lna_conf  =
+				ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+			break;
+		case ATH_ANT_DIV_COMB_LNA1:
+			antcomb->rssi_lna1 = alt_rssi_avg;
+			antcomb->rssi_lna2 = main_rssi_avg;
+			antcomb->scan = true;
+			/* set to A+B */
+			div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+			div_ant_conf.alt_lna_conf  =
+				ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+			break;
+		case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2:
+			antcomb->rssi_add = alt_rssi_avg;
+			antcomb->scan = true;
+			/* set to A-B */
+			div_ant_conf.alt_lna_conf =
+				ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+			break;
+		case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2:
+			antcomb->rssi_sub = alt_rssi_avg;
+			antcomb->scan = false;
+			if (antcomb->rssi_lna2 >
+			    (antcomb->rssi_lna1 +
+			    ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
+				/* use LNA2 as main LNA */
+				if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
+				    (antcomb->rssi_add > antcomb->rssi_sub)) {
+					/* set to A+B */
+					div_ant_conf.main_lna_conf =
+						ATH_ANT_DIV_COMB_LNA2;
+					div_ant_conf.alt_lna_conf  =
+						ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+				} else if (antcomb->rssi_sub >
+					   antcomb->rssi_lna1) {
+					/* set to A-B */
+					div_ant_conf.main_lna_conf =
+						ATH_ANT_DIV_COMB_LNA2;
+					div_ant_conf.alt_lna_conf =
+						ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+				} else {
+					/* set to LNA1 */
+					div_ant_conf.main_lna_conf =
+						ATH_ANT_DIV_COMB_LNA2;
+					div_ant_conf.alt_lna_conf =
+						ATH_ANT_DIV_COMB_LNA1;
+				}
+			} else {
+				/* use LNA1 as main LNA */
+				if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
+				    (antcomb->rssi_add > antcomb->rssi_sub)) {
+					/* set to A+B */
+					div_ant_conf.main_lna_conf =
+						ATH_ANT_DIV_COMB_LNA1;
+					div_ant_conf.alt_lna_conf  =
+						ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+				} else if (antcomb->rssi_sub >
+					   antcomb->rssi_lna1) {
+					/* set to A-B */
+					div_ant_conf.main_lna_conf =
+						ATH_ANT_DIV_COMB_LNA1;
+					div_ant_conf.alt_lna_conf =
+						ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+				} else {
+					/* set to LNA2 */
+					div_ant_conf.main_lna_conf =
+						ATH_ANT_DIV_COMB_LNA1;
+					div_ant_conf.alt_lna_conf =
+						ATH_ANT_DIV_COMB_LNA2;
+				}
+			}
+			break;
+		default:
+			break;
+		}
+	} else {
+		if (!antcomb->alt_good) {
+			antcomb->scan_not_start = false;
+			/* Set alt to another LNA */
+			if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) {
+				div_ant_conf.main_lna_conf =
+						ATH_ANT_DIV_COMB_LNA2;
+				div_ant_conf.alt_lna_conf =
+						ATH_ANT_DIV_COMB_LNA1;
+			} else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) {
+				div_ant_conf.main_lna_conf =
+						ATH_ANT_DIV_COMB_LNA1;
+				div_ant_conf.alt_lna_conf =
+						ATH_ANT_DIV_COMB_LNA2;
+			}
+			goto div_comb_done;
+		}
+	}
+
+	ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
+					   main_rssi_avg, alt_rssi_avg,
+					   alt_ratio);
+
+	antcomb->quick_scan_cnt++;
+
+div_comb_done:
+	ath_ant_div_conf_fast_divbias(&div_ant_conf);
+
+	ath9k_hw_antdiv_comb_conf_set(sc->sc_ah, &div_ant_conf);
+
+	antcomb->scan_start_time = jiffies;
+	antcomb->total_pkt_count = 0;
+	antcomb->main_total_rssi = 0;
+	antcomb->alt_total_rssi = 0;
+	antcomb->main_recv_cnt = 0;
+	antcomb->alt_recv_cnt = 0;
+}
+
 int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
 {
 	struct ath_buf *bf;
@@ -1099,6 +1640,7 @@
 	u8 rx_status_len = ah->caps.rx_status_len;
 	u64 tsf = 0;
 	u32 tsf_lower = 0;
+	unsigned long flags;
 
 	if (edma)
 		dma_type = DMA_BIDIRECTIONAL;
@@ -1207,11 +1749,16 @@
 			sc->rx.rxotherant = 0;
 		}
 
+		spin_lock_irqsave(&sc->sc_pm_lock, flags);
 		if (unlikely(ath9k_check_auto_sleep(sc) ||
 			     (sc->ps_flags & (PS_WAIT_FOR_BEACON |
 					      PS_WAIT_FOR_CAB |
 					      PS_WAIT_FOR_PSPOLL_DATA))))
 			ath_rx_ps(sc, skb);
+		spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
+
+		if (ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB)
+			ath_ant_comb_scan(sc, &rs);
 
 		ath_rx_send_to_mac80211(hw, sc, skb, rxs);
 
diff --git a/drivers/net/wireless/ath/ath9k/virtual.c b/drivers/net/wireless/ath/ath9k/virtual.c
index fd20241..ec7cf5e 100644
--- a/drivers/net/wireless/ath/ath9k/virtual.c
+++ b/drivers/net/wireless/ath/ath9k/virtual.c
@@ -19,45 +19,36 @@
 #include "ath9k.h"
 
 struct ath9k_vif_iter_data {
-	int count;
-	u8 *addr;
+	const u8 *hw_macaddr;
+	u8 mask[ETH_ALEN];
 };
 
 static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
 {
 	struct ath9k_vif_iter_data *iter_data = data;
-	u8 *nbuf;
+	int i;
 
-	nbuf = krealloc(iter_data->addr, (iter_data->count + 1) * ETH_ALEN,
-			GFP_ATOMIC);
-	if (nbuf == NULL)
-		return;
-
-	memcpy(nbuf + iter_data->count * ETH_ALEN, mac, ETH_ALEN);
-	iter_data->addr = nbuf;
-	iter_data->count++;
+	for (i = 0; i < ETH_ALEN; i++)
+		iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]);
 }
 
-void ath9k_set_bssid_mask(struct ieee80211_hw *hw)
+void ath9k_set_bssid_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 {
 	struct ath_wiphy *aphy = hw->priv;
 	struct ath_softc *sc = aphy->sc;
 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
 	struct ath9k_vif_iter_data iter_data;
-	int i, j;
-	u8 mask[ETH_ALEN];
+	int i;
 
 	/*
-	 * Add primary MAC address even if it is not in active use since it
-	 * will be configured to the hardware as the starting point and the
-	 * BSSID mask will need to be changed if another address is active.
+	 * Use the hardware MAC address as reference, the hardware uses it
+	 * together with the BSSID mask when matching addresses.
 	 */
-	iter_data.addr = kmalloc(ETH_ALEN, GFP_ATOMIC);
-	if (iter_data.addr) {
-		memcpy(iter_data.addr, common->macaddr, ETH_ALEN);
-		iter_data.count = 1;
-	} else
-		iter_data.count = 0;
+	iter_data.hw_macaddr = common->macaddr;
+	memset(&iter_data.mask, 0xff, ETH_ALEN);
+
+	if (vif)
+		ath9k_vif_iter(&iter_data, vif->addr, vif);
 
 	/* Get list of all active MAC addresses */
 	spin_lock_bh(&sc->wiphy_lock);
@@ -71,31 +62,7 @@
 	}
 	spin_unlock_bh(&sc->wiphy_lock);
 
-	/* Generate an address mask to cover all active addresses */
-	memset(mask, 0, ETH_ALEN);
-	for (i = 0; i < iter_data.count; i++) {
-		u8 *a1 = iter_data.addr + i * ETH_ALEN;
-		for (j = i + 1; j < iter_data.count; j++) {
-			u8 *a2 = iter_data.addr + j * ETH_ALEN;
-			mask[0] |= a1[0] ^ a2[0];
-			mask[1] |= a1[1] ^ a2[1];
-			mask[2] |= a1[2] ^ a2[2];
-			mask[3] |= a1[3] ^ a2[3];
-			mask[4] |= a1[4] ^ a2[4];
-			mask[5] |= a1[5] ^ a2[5];
-		}
-	}
-
-	kfree(iter_data.addr);
-
-	/* Invert the mask and configure hardware */
-	common->bssidmask[0] = ~mask[0];
-	common->bssidmask[1] = ~mask[1];
-	common->bssidmask[2] = ~mask[2];
-	common->bssidmask[3] = ~mask[3];
-	common->bssidmask[4] = ~mask[4];
-	common->bssidmask[5] = ~mask[5];
-
+	memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
 	ath_hw_setbssidmask(common);
 }
 
diff --git a/drivers/net/wireless/ath/ath9k/wmi.c b/drivers/net/wireless/ath/ath9k/wmi.c
index 45fe9ca..93a8bda 100644
--- a/drivers/net/wireless/ath/ath9k/wmi.c
+++ b/drivers/net/wireless/ath/ath9k/wmi.c
@@ -124,55 +124,11 @@
 {
 	struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data;
 	struct ath_common *common = ath9k_hw_common(priv->ah);
-	struct wmi_cmd_hdr *hdr;
-	struct wmi_swba *swba_hdr;
-	enum wmi_event_id event;
-	struct sk_buff *skb;
-	void *wmi_event;
-	unsigned long flags;
-#ifdef CONFIG_ATH9K_HTC_DEBUGFS
-	__be32 txrate;
-#endif
 
-	spin_lock_irqsave(&priv->wmi->wmi_lock, flags);
-	skb = priv->wmi->wmi_skb;
-	spin_unlock_irqrestore(&priv->wmi->wmi_lock, flags);
+	ath_print(common, ATH_DBG_WMI, "SWBA Event received\n");
 
-	hdr = (struct wmi_cmd_hdr *) skb->data;
-	event = be16_to_cpu(hdr->command_id);
-	wmi_event = skb_pull(skb, sizeof(struct wmi_cmd_hdr));
+	ath9k_htc_swba(priv, priv->wmi->beacon_pending);
 
-	ath_print(common, ATH_DBG_WMI,
-		  "WMI Event: 0x%x\n", event);
-
-	switch (event) {
-	case WMI_TGT_RDY_EVENTID:
-		break;
-	case WMI_SWBA_EVENTID:
-		swba_hdr = (struct wmi_swba *) wmi_event;
-		ath9k_htc_swba(priv, swba_hdr->beacon_pending);
-		break;
-	case WMI_FATAL_EVENTID:
-		break;
-	case WMI_TXTO_EVENTID:
-		break;
-	case WMI_BMISS_EVENTID:
-		break;
-	case WMI_WLAN_TXCOMP_EVENTID:
-		break;
-	case WMI_DELBA_EVENTID:
-		break;
-	case WMI_TXRATE_EVENTID:
-#ifdef CONFIG_ATH9K_HTC_DEBUGFS
-		txrate = ((struct wmi_event_txrate *)wmi_event)->txrate;
-		priv->debug.txrate = be32_to_cpu(txrate);
-#endif
-		break;
-	default:
-		break;
-	}
-
-	kfree_skb(skb);
 }
 
 static void ath9k_wmi_rsp_callback(struct wmi *wmi, struct sk_buff *skb)
@@ -191,6 +147,10 @@
 	struct wmi *wmi = (struct wmi *) priv;
 	struct wmi_cmd_hdr *hdr;
 	u16 cmd_id;
+	void *wmi_event;
+#ifdef CONFIG_ATH9K_HTC_DEBUGFS
+	__be32 txrate;
+#endif
 
 	if (unlikely(wmi->stopped))
 		goto free_skb;
@@ -199,10 +159,22 @@
 	cmd_id = be16_to_cpu(hdr->command_id);
 
 	if (cmd_id & 0x1000) {
-		spin_lock(&wmi->wmi_lock);
-		wmi->wmi_skb = skb;
-		spin_unlock(&wmi->wmi_lock);
-		tasklet_schedule(&wmi->drv_priv->wmi_tasklet);
+		wmi_event = skb_pull(skb, sizeof(struct wmi_cmd_hdr));
+		switch (cmd_id) {
+		case WMI_SWBA_EVENTID:
+			wmi->beacon_pending = *(u8 *)wmi_event;
+			tasklet_schedule(&wmi->drv_priv->wmi_tasklet);
+			break;
+		case WMI_TXRATE_EVENTID:
+#ifdef CONFIG_ATH9K_HTC_DEBUGFS
+			txrate = ((struct wmi_event_txrate *)wmi_event)->txrate;
+			wmi->drv_priv->debug.txrate = be32_to_cpu(txrate);
+#endif
+			break;
+		default:
+			break;
+		}
+		kfree_skb(skb);
 		return;
 	}
 
diff --git a/drivers/net/wireless/ath/ath9k/wmi.h b/drivers/net/wireless/ath/ath9k/wmi.h
index a0bf857..ac61074a 100644
--- a/drivers/net/wireless/ath/ath9k/wmi.h
+++ b/drivers/net/wireless/ath/ath9k/wmi.h
@@ -31,10 +31,6 @@
 	__be16 seq_no;
 } __packed;
 
-struct wmi_swba {
-	u8 beacon_pending;
-} __packed;
-
 enum wmi_cmd_id {
 	WMI_ECHO_CMDID = 0x0001,
 	WMI_ACCESS_MEMORY_CMDID,
@@ -104,7 +100,7 @@
 	u32 cmd_rsp_len;
 	bool stopped;
 
-	struct sk_buff *wmi_skb;
+	u8 beacon_pending;
 	spinlock_t wmi_lock;
 
 	atomic_t mwrite_cnt;
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 457f076..85a7323 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -61,6 +61,8 @@
 			      struct ath_tx_status *ts, int txok);
 static void ath_tx_rc_status(struct ath_buf *bf, struct ath_tx_status *ts,
 			     int nbad, int txok, bool update_rc);
+static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid,
+			      int seqno);
 
 enum {
 	MCS_HT20,
@@ -143,18 +145,23 @@
 	struct ath_txq *txq = &sc->tx.txq[tid->ac->qnum];
 	struct ath_buf *bf;
 	struct list_head bf_head;
+	struct ath_tx_status ts;
+
 	INIT_LIST_HEAD(&bf_head);
 
-	WARN_ON(!tid->paused);
-
+	memset(&ts, 0, sizeof(ts));
 	spin_lock_bh(&txq->axq_lock);
-	tid->paused = false;
 
 	while (!list_empty(&tid->buf_q)) {
 		bf = list_first_entry(&tid->buf_q, struct ath_buf, list);
-		BUG_ON(bf_isretried(bf));
 		list_move_tail(&bf->list, &bf_head);
-		ath_tx_send_ht_normal(sc, txq, tid, &bf_head);
+
+		if (bf_isretried(bf)) {
+			ath_tx_update_baw(sc, tid, bf->bf_seqno);
+			ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0, 0);
+		} else {
+			ath_tx_send_ht_normal(sc, txq, tid, &bf_head);
+		}
 	}
 
 	spin_unlock_bh(&txq->axq_lock);
@@ -168,9 +175,9 @@
 	index  = ATH_BA_INDEX(tid->seq_start, seqno);
 	cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
 
-	tid->tx_buf[cindex] = NULL;
+	__clear_bit(cindex, tid->tx_buf);
 
-	while (tid->baw_head != tid->baw_tail && !tid->tx_buf[tid->baw_head]) {
+	while (tid->baw_head != tid->baw_tail && !test_bit(tid->baw_head, tid->tx_buf)) {
 		INCR(tid->seq_start, IEEE80211_SEQ_MAX);
 		INCR(tid->baw_head, ATH_TID_MAX_BUFS);
 	}
@@ -186,9 +193,7 @@
 
 	index  = ATH_BA_INDEX(tid->seq_start, bf->bf_seqno);
 	cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
-
-	BUG_ON(tid->tx_buf[cindex] != NULL);
-	tid->tx_buf[cindex] = bf;
+	__set_bit(cindex, tid->tx_buf);
 
 	if (index >= ((tid->baw_tail - tid->baw_head) &
 		(ATH_TID_MAX_BUFS - 1))) {
@@ -431,7 +436,7 @@
 			list_move_tail(&bf->list, &bf_head);
 		}
 
-		if (!txpending) {
+		if (!txpending || (tid->state & AGGR_CLEANUP)) {
 			/*
 			 * complete the acked-ones/xretried ones; update
 			 * block-ack window
@@ -510,15 +515,12 @@
 	}
 
 	if (tid->state & AGGR_CLEANUP) {
+		ath_tx_flush_tid(sc, tid);
+
 		if (tid->baw_head == tid->baw_tail) {
 			tid->state &= ~AGGR_ADDBA_COMPLETE;
 			tid->state &= ~AGGR_CLEANUP;
-
-			/* send buffered frames as singles */
-			ath_tx_flush_tid(sc, tid);
 		}
-		rcu_read_unlock();
-		return;
 	}
 
 	rcu_read_unlock();
@@ -785,17 +787,23 @@
 		 status != ATH_AGGR_BAW_CLOSED);
 }
 
-void ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
-		       u16 tid, u16 *ssn)
+int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
+		      u16 tid, u16 *ssn)
 {
 	struct ath_atx_tid *txtid;
 	struct ath_node *an;
 
 	an = (struct ath_node *)sta->drv_priv;
 	txtid = ATH_AN_2_TID(an, tid);
+
+	if (txtid->state & (AGGR_CLEANUP | AGGR_ADDBA_COMPLETE))
+		return -EAGAIN;
+
 	txtid->state |= AGGR_ADDBA_PROGRESS;
 	txtid->paused = true;
 	*ssn = txtid->seq_start;
+
+	return 0;
 }
 
 void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
@@ -803,12 +811,6 @@
 	struct ath_node *an = (struct ath_node *)sta->drv_priv;
 	struct ath_atx_tid *txtid = ATH_AN_2_TID(an, tid);
 	struct ath_txq *txq = &sc->tx.txq[txtid->ac->qnum];
-	struct ath_tx_status ts;
-	struct ath_buf *bf;
-	struct list_head bf_head;
-
-	memset(&ts, 0, sizeof(ts));
-	INIT_LIST_HEAD(&bf_head);
 
 	if (txtid->state & AGGR_CLEANUP)
 		return;
@@ -818,31 +820,22 @@
 		return;
 	}
 
-	/* drop all software retried frames and mark this TID */
 	spin_lock_bh(&txq->axq_lock);
 	txtid->paused = true;
-	while (!list_empty(&txtid->buf_q)) {
-		bf = list_first_entry(&txtid->buf_q, struct ath_buf, list);
-		if (!bf_isretried(bf)) {
-			/*
-			 * NB: it's based on the assumption that
-			 * software retried frame will always stay
-			 * at the head of software queue.
-			 */
-			break;
-		}
-		list_move_tail(&bf->list, &bf_head);
-		ath_tx_update_baw(sc, txtid, bf->bf_seqno);
-		ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0, 0);
-	}
+
+	/*
+	 * If frames are still being transmitted for this TID, they will be
+	 * cleaned up during tx completion. To prevent race conditions, this
+	 * TID can only be reused after all in-progress subframes have been
+	 * completed.
+	 */
+	if (txtid->baw_head != txtid->baw_tail)
+		txtid->state |= AGGR_CLEANUP;
+	else
+		txtid->state &= ~AGGR_ADDBA_COMPLETE;
 	spin_unlock_bh(&txq->axq_lock);
 
-	if (txtid->baw_head != txtid->baw_tail) {
-		txtid->state |= AGGR_CLEANUP;
-	} else {
-		txtid->state &= ~AGGR_ADDBA_COMPLETE;
-		ath_tx_flush_tid(sc, txtid);
-	}
+	ath_tx_flush_tid(sc, txtid);
 }
 
 void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
@@ -862,20 +855,6 @@
 	}
 }
 
-bool ath_tx_aggr_check(struct ath_softc *sc, struct ath_node *an, u8 tidno)
-{
-	struct ath_atx_tid *txtid;
-
-	if (!(sc->sc_flags & SC_OP_TXAGGR))
-		return false;
-
-	txtid = ATH_AN_2_TID(an, tidno);
-
-	if (!(txtid->state & (AGGR_ADDBA_COMPLETE | AGGR_ADDBA_PROGRESS)))
-			return true;
-	return false;
-}
-
 /********************/
 /* Queue Management */
 /********************/
diff --git a/drivers/net/wireless/ath/carl9170/Kconfig b/drivers/net/wireless/ath/carl9170/Kconfig
new file mode 100644
index 0000000..c5d3a3f
--- /dev/null
+++ b/drivers/net/wireless/ath/carl9170/Kconfig
@@ -0,0 +1,41 @@
+config CARL9170
+	tristate "Linux Community AR9170 802.11n USB support"
+	depends on USB && MAC80211 && EXPERIMENTAL
+	select FW_LOADER
+	select CRC32
+	help
+	  This is another driver for the Atheros "otus" 802.11n USB devices.
+
+	  This driver provides more features than the original,
+	  but it needs a special firmware (carl9170-1.fw) to do that.
+
+	  The firmware can be downloaded from our wiki here:
+	  http://wireless.kernel.org/en/users/Drivers/carl9170
+
+	  If you choose to build a module, it'll be called carl9170.
+
+config CARL9170_LEDS
+	bool "SoftLED Support"
+	depends on CARL9170
+	select MAC80211_LEDS
+	select LEDS_CLASS
+	select NEW_LEDS
+	default y
+	help
+	  This option is necessary, if you want your device' LEDs to blink
+
+	  Say Y, unless you need the LEDs for firmware debugging.
+
+config CARL9170_DEBUGFS
+	bool "DebugFS Support"
+	depends on CARL9170 && DEBUG_FS && MAC80211_DEBUGFS
+	default n
+	help
+	  Export several driver and device internals to user space.
+
+	  Say N.
+
+config CARL9170_WPC
+	bool
+	depends on CARL9170 && (INPUT = y || INPUT = CARL9170)
+	default y
diff --git a/drivers/net/wireless/ath/carl9170/Makefile b/drivers/net/wireless/ath/carl9170/Makefile
new file mode 100644
index 0000000..f64ed76
--- /dev/null
+++ b/drivers/net/wireless/ath/carl9170/Makefile
@@ -0,0 +1,4 @@
+carl9170-objs := main.o usb.o cmd.o mac.o phy.o led.o fw.o tx.o rx.o
+carl9170-$(CONFIG_CARL9170_DEBUGFS) += debug.o
+
+obj-$(CONFIG_CARL9170) += carl9170.o
diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h
new file mode 100644
index 0000000..d7c5482
--- /dev/null
+++ b/drivers/net/wireless/ath/carl9170/carl9170.h
@@ -0,0 +1,627 @@
+/*
+ * Atheros CARL9170 driver
+ *
+ * Driver specific definitions
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * This program is free software; you can redistribute it 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 program 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or distribute this software for any
+ *    purpose with or without fee is hereby granted, provided that the above
+ *    copyright notice and this permission notice appear in all copies.
+ *
+ *    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef __CARL9170_H
+#define __CARL9170_H
+
+#include <linux/kernel.h>
+#include <linux/firmware.h>
+#include <linux/completion.h>
+#include <linux/spinlock.h>
+#include <net/cfg80211.h>
+#include <net/mac80211.h>
+#include <linux/usb.h>
+#ifdef CONFIG_CARL9170_LEDS
+#include <linux/leds.h>
+#endif /* CONFIG_CARL170_LEDS */
+#ifdef CONFIG_CARL9170_WPC
+#include <linux/input.h>
+#endif /* CONFIG_CARL9170_WPC */
+#include "eeprom.h"
+#include "wlan.h"
+#include "hw.h"
+#include "fwdesc.h"
+#include "fwcmd.h"
+#include "../regd.h"
+
+#ifdef CONFIG_CARL9170_DEBUGFS
+#include "debug.h"
+#endif /* CONFIG_CARL9170_DEBUGFS */
+
+#define CARL9170FW_NAME	"carl9170-1.fw"
+
+#define PAYLOAD_MAX	(CARL9170_MAX_CMD_LEN / 4 - 1)
+
+enum carl9170_rf_init_mode {
+	CARL9170_RFI_NONE,
+	CARL9170_RFI_WARM,
+	CARL9170_RFI_COLD,
+};
+
+#define CARL9170_MAX_RX_BUFFER_SIZE		8192
+
+enum carl9170_device_state {
+	CARL9170_UNKNOWN_STATE,
+	CARL9170_STOPPED,
+	CARL9170_IDLE,
+	CARL9170_STARTED,
+};
+
+#define CARL9170_NUM_TID		16
+#define WME_BA_BMP_SIZE			64
+#define CARL9170_TX_USER_RATE_TRIES	3
+
+#define WME_AC_BE   2
+#define WME_AC_BK   3
+#define WME_AC_VI   1
+#define WME_AC_VO   0
+
+#define TID_TO_WME_AC(_tid)				\
+	((((_tid) == 0) || ((_tid) == 3)) ? WME_AC_BE :	\
+	 (((_tid) == 1) || ((_tid) == 2)) ? WME_AC_BK :	\
+	 (((_tid) == 4) || ((_tid) == 5)) ? WME_AC_VI :	\
+	 WME_AC_VO)
+
+#define SEQ_DIFF(_start, _seq) \
+	(((_start) - (_seq)) & 0x0fff)
+#define SEQ_PREV(_seq) \
+	(((_seq) - 1) & 0x0fff)
+#define SEQ_NEXT(_seq) \
+	(((_seq) + 1) & 0x0fff)
+#define BAW_WITHIN(_start, _bawsz, _seqno) \
+	((((_seqno) - (_start)) & 0xfff) < (_bawsz))
+
+enum carl9170_tid_state {
+	CARL9170_TID_STATE_INVALID,
+	CARL9170_TID_STATE_KILLED,
+	CARL9170_TID_STATE_SHUTDOWN,
+	CARL9170_TID_STATE_SUSPEND,
+	CARL9170_TID_STATE_PROGRESS,
+	CARL9170_TID_STATE_IDLE,
+	CARL9170_TID_STATE_XMIT,
+};
+
+#define CARL9170_BAW_BITS (2 * WME_BA_BMP_SIZE)
+#define CARL9170_BAW_SIZE (BITS_TO_LONGS(CARL9170_BAW_BITS))
+#define CARL9170_BAW_LEN (DIV_ROUND_UP(CARL9170_BAW_BITS, BITS_PER_BYTE))
+
+struct carl9170_sta_tid {
+	/* must be the first entry! */
+	struct list_head list;
+
+	/* temporary list for RCU unlink procedure */
+	struct list_head tmp_list;
+
+	/* lock for the following data structures */
+	spinlock_t lock;
+
+	unsigned int counter;
+	enum carl9170_tid_state state;
+	u8 tid;		/* TID number ( 0 - 15 ) */
+	u16 max;	/* max. AMPDU size */
+
+	u16 snx;	/* awaiting _next_ frame */
+	u16 hsn;	/* highest _queued_ sequence */
+	u16 bsn;	/* base of the tx/agg bitmap */
+	unsigned long bitmap[CARL9170_BAW_SIZE];
+
+	/* Preaggregation reorder queue */
+	struct sk_buff_head queue;
+};
+
+#define CARL9170_QUEUE_TIMEOUT		256
+#define CARL9170_BUMP_QUEUE		1000
+#define CARL9170_TX_TIMEOUT		2500
+#define CARL9170_JANITOR_DELAY		128
+#define CARL9170_QUEUE_STUCK_TIMEOUT	5500
+
+#define CARL9170_NUM_TX_AGG_MAX		30
+
+/*
+ * Tradeoff between stability/latency and speed.
+ *
+ * AR9170_TXQ_DEPTH is devised by dividing the amount of available
+ * tx buffers with the size of a full ethernet frame + overhead.
+ *
+ * Naturally: The higher the limit, the faster the device CAN send.
+ * However, even a slight over-commitment at the wrong time and the
+ * hardware is doomed to send all already-queued frames at suboptimal
+ * rates. This in turn leads to an enourmous amount of unsuccessful
+ * retries => Latency goes up, whereas the throughput goes down. CRASH!
+ */
+#define CARL9170_NUM_TX_LIMIT_HARD	((AR9170_TXQ_DEPTH * 3) / 2)
+#define CARL9170_NUM_TX_LIMIT_SOFT	(AR9170_TXQ_DEPTH)
+
+struct carl9170_tx_queue_stats {
+	unsigned int count;
+	unsigned int limit;
+	unsigned int len;
+};
+
+struct carl9170_vif {
+	unsigned int id;
+	struct ieee80211_vif *vif;
+};
+
+struct carl9170_vif_info {
+	struct list_head list;
+	bool active;
+	unsigned int id;
+	struct sk_buff *beacon;
+	bool enable_beacon;
+};
+
+#define AR9170_NUM_RX_URBS	16
+#define AR9170_NUM_RX_URBS_MUL	2
+#define AR9170_NUM_TX_URBS	8
+#define AR9170_NUM_RX_URBS_POOL (AR9170_NUM_RX_URBS_MUL * AR9170_NUM_RX_URBS)
+
+enum carl9170_device_features {
+	CARL9170_WPS_BUTTON		= BIT(0),
+	CARL9170_ONE_LED		= BIT(1),
+};
+
+#ifdef CONFIG_CARL9170_LEDS
+struct ar9170;
+
+struct carl9170_led {
+	struct ar9170 *ar;
+	struct led_classdev l;
+	char name[32];
+	unsigned int toggled;
+	bool last_state;
+	bool registered;
+};
+#endif /* CONFIG_CARL9170_LEDS */
+
+enum carl9170_restart_reasons {
+	CARL9170_RR_NO_REASON = 0,
+	CARL9170_RR_FATAL_FIRMWARE_ERROR,
+	CARL9170_RR_TOO_MANY_FIRMWARE_ERRORS,
+	CARL9170_RR_WATCHDOG,
+	CARL9170_RR_STUCK_TX,
+	CARL9170_RR_SLOW_SYSTEM,
+	CARL9170_RR_COMMAND_TIMEOUT,
+	CARL9170_RR_TOO_MANY_PHY_ERRORS,
+	CARL9170_RR_LOST_RSP,
+	CARL9170_RR_INVALID_RSP,
+	CARL9170_RR_USER_REQUEST,
+
+	__CARL9170_RR_LAST,
+};
+
+enum carl9170_erp_modes {
+	CARL9170_ERP_INVALID,
+	CARL9170_ERP_AUTO,
+	CARL9170_ERP_MAC80211,
+	CARL9170_ERP_OFF,
+	CARL9170_ERP_CTS,
+	CARL9170_ERP_RTS,
+	__CARL9170_ERP_NUM,
+};
+
+struct ar9170 {
+	struct ath_common common;
+	struct ieee80211_hw *hw;
+	struct mutex mutex;
+	enum carl9170_device_state state;
+	spinlock_t state_lock;
+	enum carl9170_restart_reasons last_reason;
+	bool registered;
+
+	/* USB */
+	struct usb_device *udev;
+	struct usb_interface *intf;
+	struct usb_anchor rx_anch;
+	struct usb_anchor rx_work;
+	struct usb_anchor rx_pool;
+	struct usb_anchor tx_wait;
+	struct usb_anchor tx_anch;
+	struct usb_anchor tx_cmd;
+	struct usb_anchor tx_err;
+	struct tasklet_struct usb_tasklet;
+	atomic_t tx_cmd_urbs;
+	atomic_t tx_anch_urbs;
+	atomic_t rx_anch_urbs;
+	atomic_t rx_work_urbs;
+	atomic_t rx_pool_urbs;
+	kernel_ulong_t features;
+
+	/* firmware settings */
+	struct completion fw_load_wait;
+	struct completion fw_boot_wait;
+	struct {
+		const struct carl9170fw_desc_head *desc;
+		const struct firmware *fw;
+		unsigned int offset;
+		unsigned int address;
+		unsigned int cmd_bufs;
+		unsigned int api_version;
+		unsigned int vif_num;
+		unsigned int err_counter;
+		unsigned int bug_counter;
+		u32 beacon_addr;
+		unsigned int beacon_max_len;
+		bool rx_stream;
+		bool tx_stream;
+		unsigned int mem_blocks;
+		unsigned int mem_block_size;
+		unsigned int rx_size;
+	} fw;
+
+	/* reset / stuck frames/queue detection */
+	struct work_struct restart_work;
+	unsigned int restart_counter;
+	unsigned long queue_stop_timeout[__AR9170_NUM_TXQ];
+	unsigned long max_queue_stop_timeout[__AR9170_NUM_TXQ];
+	bool needs_full_reset;
+	atomic_t pending_restarts;
+
+	/* interface mode settings */
+	struct list_head vif_list;
+	unsigned long vif_bitmap;
+	unsigned int vifs;
+	struct carl9170_vif vif_priv[AR9170_MAX_VIRTUAL_MAC];
+
+	/* beaconing */
+	spinlock_t beacon_lock;
+	unsigned int global_pretbtt;
+	unsigned int global_beacon_int;
+	struct carl9170_vif_info *beacon_iter;
+	unsigned int beacon_enabled;
+
+	/* cryptographic engine */
+	u64 usedkeys;
+	bool rx_software_decryption;
+	bool disable_offload;
+
+	/* filter settings */
+	u64 cur_mc_hash;
+	u32 cur_filter;
+	unsigned int filter_state;
+	bool sniffer_enabled;
+
+	/* MAC */
+	enum carl9170_erp_modes erp_mode;
+
+	/* PHY */
+	struct ieee80211_channel *channel;
+	int noise[4];
+	unsigned int chan_fail;
+	unsigned int total_chan_fail;
+	u8 heavy_clip;
+	u8 ht_settings;
+
+	/* power calibration data */
+	u8 power_5G_leg[4];
+	u8 power_2G_cck[4];
+	u8 power_2G_ofdm[4];
+	u8 power_5G_ht20[8];
+	u8 power_5G_ht40[8];
+	u8 power_2G_ht20[8];
+	u8 power_2G_ht40[8];
+
+#ifdef CONFIG_CARL9170_LEDS
+	/* LED */
+	struct delayed_work led_work;
+	struct carl9170_led leds[AR9170_NUM_LEDS];
+#endif /* CONFIG_CARL9170_LEDS */
+
+	/* qos queue settings */
+	spinlock_t tx_stats_lock;
+	struct carl9170_tx_queue_stats tx_stats[__AR9170_NUM_TXQ];
+	struct ieee80211_tx_queue_params edcf[5];
+	struct completion tx_flush;
+
+	/* CMD */
+	int cmd_seq;
+	int readlen;
+	u8 *readbuf;
+	spinlock_t cmd_lock;
+	struct completion cmd_wait;
+	union {
+		__le32 cmd_buf[PAYLOAD_MAX + 1];
+		struct carl9170_cmd cmd;
+		struct carl9170_rsp rsp;
+	};
+
+	/* statistics */
+	unsigned int tx_dropped;
+	unsigned int tx_ack_failures;
+	unsigned int tx_fcs_errors;
+	unsigned int tx_ampdu_timeout;
+	unsigned int rx_dropped;
+
+	/* EEPROM */
+	struct ar9170_eeprom eeprom;
+
+	/* tx queuing */
+	struct sk_buff_head tx_pending[__AR9170_NUM_TXQ];
+	struct sk_buff_head tx_status[__AR9170_NUM_TXQ];
+	struct delayed_work tx_janitor;
+	unsigned long tx_janitor_last_run;
+	bool tx_schedule;
+
+	/* tx ampdu */
+	struct work_struct ampdu_work;
+	spinlock_t tx_ampdu_list_lock;
+	struct carl9170_sta_tid *tx_ampdu_iter;
+	struct list_head tx_ampdu_list;
+	atomic_t tx_ampdu_upload;
+	atomic_t tx_ampdu_scheduler;
+	atomic_t tx_total_pending;
+	atomic_t tx_total_queued;
+	unsigned int tx_ampdu_list_len;
+	int current_density;
+	int current_factor;
+	bool tx_ampdu_schedule;
+
+	/* internal memory management */
+	spinlock_t mem_lock;
+	unsigned long *mem_bitmap;
+	atomic_t mem_free_blocks;
+	atomic_t mem_allocs;
+
+	/* rxstream mpdu merge */
+	struct ar9170_rx_head rx_plcp;
+	bool rx_has_plcp;
+	struct sk_buff *rx_failover;
+	int rx_failover_missing;
+
+#ifdef CONFIG_CARL9170_WPC
+	struct {
+		bool pbc_state;
+		struct input_dev *pbc;
+		char name[32];
+		char phys[32];
+	} wps;
+#endif /* CONFIG_CARL9170_WPC */
+
+#ifdef CONFIG_CARL9170_DEBUGFS
+	struct carl9170_debug debug;
+	struct dentry *debug_dir;
+#endif /* CONFIG_CARL9170_DEBUGFS */
+
+	/* PSM */
+	struct work_struct ps_work;
+	struct {
+		unsigned int dtim_counter;
+		unsigned long last_beacon;
+		unsigned long last_action;
+		unsigned long last_slept;
+		unsigned int sleep_ms;
+		unsigned int off_override;
+		bool state;
+	} ps;
+};
+
+enum carl9170_ps_off_override_reasons {
+	PS_OFF_VIF	= BIT(0),
+	PS_OFF_BCN	= BIT(1),
+	PS_OFF_5GHZ	= BIT(2),
+};
+
+struct carl9170_ba_stats {
+	u8 ampdu_len;
+	u8 ampdu_ack_len;
+	bool clear;
+};
+
+struct carl9170_sta_info {
+	bool ht_sta;
+	unsigned int ampdu_max_len;
+	struct carl9170_sta_tid *agg[CARL9170_NUM_TID];
+	struct carl9170_ba_stats stats[CARL9170_NUM_TID];
+};
+
+struct carl9170_tx_info {
+	unsigned long timeout;
+	struct ar9170 *ar;
+	struct kref ref;
+};
+
+#define CHK_DEV_STATE(a, s)	(((struct ar9170 *)a)->state >= (s))
+#define IS_INITIALIZED(a)	(CHK_DEV_STATE(a, CARL9170_STOPPED))
+#define IS_ACCEPTING_CMD(a)	(CHK_DEV_STATE(a, CARL9170_IDLE))
+#define IS_STARTED(a)		(CHK_DEV_STATE(a, CARL9170_STARTED))
+
+static inline void __carl9170_set_state(struct ar9170 *ar,
+	enum carl9170_device_state newstate)
+{
+	ar->state = newstate;
+}
+
+static inline void carl9170_set_state(struct ar9170 *ar,
+	enum carl9170_device_state newstate)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&ar->state_lock, flags);
+	__carl9170_set_state(ar, newstate);
+	spin_unlock_irqrestore(&ar->state_lock, flags);
+}
+
+static inline void carl9170_set_state_when(struct ar9170 *ar,
+	enum carl9170_device_state min, enum carl9170_device_state newstate)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&ar->state_lock, flags);
+	if (CHK_DEV_STATE(ar, min))
+		__carl9170_set_state(ar, newstate);
+	spin_unlock_irqrestore(&ar->state_lock, flags);
+}
+
+/* exported interface */
+void *carl9170_alloc(size_t priv_size);
+int carl9170_register(struct ar9170 *ar);
+void carl9170_unregister(struct ar9170 *ar);
+void carl9170_free(struct ar9170 *ar);
+void carl9170_restart(struct ar9170 *ar, const enum carl9170_restart_reasons r);
+void carl9170_ps_check(struct ar9170 *ar);
+
+/* USB back-end */
+int carl9170_usb_open(struct ar9170 *ar);
+void carl9170_usb_stop(struct ar9170 *ar);
+void carl9170_usb_tx(struct ar9170 *ar, struct sk_buff *skb);
+void carl9170_usb_handle_tx_err(struct ar9170 *ar);
+int carl9170_exec_cmd(struct ar9170 *ar, const enum carl9170_cmd_oids,
+		      u32 plen, void *payload, u32 rlen, void *resp);
+int __carl9170_exec_cmd(struct ar9170 *ar, struct carl9170_cmd *cmd,
+			const bool free_buf);
+int carl9170_usb_restart(struct ar9170 *ar);
+void carl9170_usb_reset(struct ar9170 *ar);
+
+/* MAC */
+int carl9170_init_mac(struct ar9170 *ar);
+int carl9170_set_qos(struct ar9170 *ar);
+int carl9170_update_multicast(struct ar9170 *ar, const u64 mc_hast);
+int carl9170_mod_virtual_mac(struct ar9170 *ar, const unsigned int id,
+			     const u8 *mac);
+int carl9170_set_operating_mode(struct ar9170 *ar);
+int carl9170_set_beacon_timers(struct ar9170 *ar);
+int carl9170_set_dyn_sifs_ack(struct ar9170 *ar);
+int carl9170_set_rts_cts_rate(struct ar9170 *ar);
+int carl9170_set_ampdu_settings(struct ar9170 *ar);
+int carl9170_set_slot_time(struct ar9170 *ar);
+int carl9170_set_mac_rates(struct ar9170 *ar);
+int carl9170_set_hwretry_limit(struct ar9170 *ar, const u32 max_retry);
+int carl9170_update_beacon(struct ar9170 *ar, const bool submit);
+int carl9170_upload_key(struct ar9170 *ar, const u8 id, const u8 *mac,
+	const u8 ktype, const u8 keyidx, const u8 *keydata, const int keylen);
+int carl9170_disable_key(struct ar9170 *ar, const u8 id);
+
+/* RX */
+void carl9170_rx(struct ar9170 *ar, void *buf, unsigned int len);
+void carl9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len);
+
+/* TX */
+int carl9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb);
+void carl9170_tx_janitor(struct work_struct *work);
+void carl9170_tx_process_status(struct ar9170 *ar,
+				const struct carl9170_rsp *cmd);
+void carl9170_tx_status(struct ar9170 *ar, struct sk_buff *skb,
+			const bool success);
+void carl9170_tx_callback(struct ar9170 *ar, struct sk_buff *skb);
+void carl9170_tx_drop(struct ar9170 *ar, struct sk_buff *skb);
+void carl9170_tx_scheduler(struct ar9170 *ar);
+void carl9170_tx_get_skb(struct sk_buff *skb);
+int carl9170_tx_put_skb(struct sk_buff *skb);
+
+/* LEDs */
+#ifdef CONFIG_CARL9170_LEDS
+int carl9170_led_register(struct ar9170 *ar);
+void carl9170_led_unregister(struct ar9170 *ar);
+#endif /* CONFIG_CARL9170_LEDS */
+int carl9170_led_init(struct ar9170 *ar);
+int carl9170_led_set_state(struct ar9170 *ar, const u32 led_state);
+
+/* PHY / RF */
+int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
+	enum nl80211_channel_type bw, enum carl9170_rf_init_mode rfi);
+int carl9170_get_noisefloor(struct ar9170 *ar);
+
+/* FW */
+int carl9170_parse_firmware(struct ar9170 *ar);
+int carl9170_fw_fix_eeprom(struct ar9170 *ar);
+
+extern struct ieee80211_rate __carl9170_ratetable[];
+extern int modparam_noht;
+
+static inline struct ar9170 *carl9170_get_priv(struct carl9170_vif *carl_vif)
+{
+	return container_of(carl_vif, struct ar9170,
+			    vif_priv[carl_vif->id]);
+}
+
+static inline struct ieee80211_hdr *carl9170_get_hdr(struct sk_buff *skb)
+{
+	return (void *)((struct _carl9170_tx_superframe *)
+		skb->data)->frame_data;
+}
+
+static inline u16 get_seq_h(struct ieee80211_hdr *hdr)
+{
+	return le16_to_cpu(hdr->seq_ctrl) >> 4;
+}
+
+static inline u16 carl9170_get_seq(struct sk_buff *skb)
+{
+	return get_seq_h(carl9170_get_hdr(skb));
+}
+
+static inline u16 get_tid_h(struct ieee80211_hdr *hdr)
+{
+	return (ieee80211_get_qos_ctl(hdr))[0] & IEEE80211_QOS_CTL_TID_MASK;
+}
+
+static inline u16 carl9170_get_tid(struct sk_buff *skb)
+{
+	return get_tid_h(carl9170_get_hdr(skb));
+}
+
+static inline struct ieee80211_vif *
+carl9170_get_vif(struct carl9170_vif_info *priv)
+{
+	return container_of((void *)priv, struct ieee80211_vif, drv_priv);
+}
+
+/* Protected by ar->mutex or RCU */
+static inline struct ieee80211_vif *carl9170_get_main_vif(struct ar9170 *ar)
+{
+	struct carl9170_vif_info *cvif;
+
+	list_for_each_entry_rcu(cvif, &ar->vif_list, list) {
+		if (cvif->active)
+			return carl9170_get_vif(cvif);
+	}
+
+	return NULL;
+}
+
+static inline bool is_main_vif(struct ar9170 *ar, struct ieee80211_vif *vif)
+{
+	bool ret;
+
+	rcu_read_lock();
+	ret = (carl9170_get_main_vif(ar) == vif);
+	rcu_read_unlock();
+	return ret;
+}
+
+#endif /* __CARL9170_H */
diff --git a/drivers/net/wireless/ath/carl9170/cmd.c b/drivers/net/wireless/ath/carl9170/cmd.c
new file mode 100644
index 0000000..c21f336
--- /dev/null
+++ b/drivers/net/wireless/ath/carl9170/cmd.c
@@ -0,0 +1,188 @@
+/*
+ * Atheros CARL9170 driver
+ *
+ * Basic HW register/memory/command access functions
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This program is free software; you can redistribute it 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 program 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or distribute this software for any
+ *    purpose with or without fee is hereby granted, provided that the above
+ *    copyright notice and this permission notice appear in all copies.
+ *
+ *    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "carl9170.h"
+#include "cmd.h"
+
+int carl9170_write_reg(struct ar9170 *ar, const u32 reg, const u32 val)
+{
+	__le32 buf[2] = {
+		cpu_to_le32(reg),
+		cpu_to_le32(val),
+	};
+	int err;
+
+	err = carl9170_exec_cmd(ar, CARL9170_CMD_WREG, sizeof(buf),
+				(u8 *) buf, 0, NULL);
+	if (err) {
+		if (net_ratelimit()) {
+			wiphy_err(ar->hw->wiphy, "writing reg %#x "
+				"(val %#x) failed (%d)\n", reg, val, err);
+		}
+	}
+	return err;
+}
+
+int carl9170_read_mreg(struct ar9170 *ar, const int nregs,
+		       const u32 *regs, u32 *out)
+{
+	int i, err;
+	__le32 *offs, *res;
+
+	/* abuse "out" for the register offsets, must be same length */
+	offs = (__le32 *)out;
+	for (i = 0; i < nregs; i++)
+		offs[i] = cpu_to_le32(regs[i]);
+
+	/* also use the same buffer for the input */
+	res = (__le32 *)out;
+
+	err = carl9170_exec_cmd(ar, CARL9170_CMD_RREG,
+				4 * nregs, (u8 *)offs,
+				4 * nregs, (u8 *)res);
+	if (err) {
+		if (net_ratelimit()) {
+			wiphy_err(ar->hw->wiphy, "reading regs failed (%d)\n",
+				  err);
+		}
+		return err;
+	}
+
+	/* convert result to cpu endian */
+	for (i = 0; i < nregs; i++)
+		out[i] = le32_to_cpu(res[i]);
+
+	return 0;
+}
+
+int carl9170_read_reg(struct ar9170 *ar, u32 reg, u32 *val)
+{
+	return carl9170_read_mreg(ar, 1, &reg, val);
+}
+
+int carl9170_echo_test(struct ar9170 *ar, const u32 v)
+{
+	u32 echores;
+	int err;
+
+	err = carl9170_exec_cmd(ar, CARL9170_CMD_ECHO,
+				4, (u8 *)&v,
+				4, (u8 *)&echores);
+	if (err)
+		return err;
+
+	if (v != echores) {
+		wiphy_info(ar->hw->wiphy, "wrong echo %x != %x", v, echores);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+struct carl9170_cmd *carl9170_cmd_buf(struct ar9170 *ar,
+	const enum carl9170_cmd_oids cmd, const unsigned int len)
+{
+	struct carl9170_cmd *tmp;
+
+	tmp = kzalloc(sizeof(struct carl9170_cmd_head) + len, GFP_ATOMIC);
+	if (tmp) {
+		tmp->hdr.cmd = cmd;
+		tmp->hdr.len = len;
+	}
+
+	return tmp;
+}
+
+int carl9170_reboot(struct ar9170 *ar)
+{
+	struct carl9170_cmd *cmd;
+	int err;
+
+	cmd = carl9170_cmd_buf(ar, CARL9170_CMD_REBOOT_ASYNC, 0);
+	if (!cmd)
+		return -ENOMEM;
+
+	err = __carl9170_exec_cmd(ar, (struct carl9170_cmd *)cmd, true);
+	return err;
+}
+
+int carl9170_mac_reset(struct ar9170 *ar)
+{
+	return carl9170_exec_cmd(ar, CARL9170_CMD_SWRST,
+				 0, NULL, 0, NULL);
+}
+
+int carl9170_bcn_ctrl(struct ar9170 *ar, const unsigned int vif_id,
+		       const u32 mode, const u32 addr, const u32 len)
+{
+	struct carl9170_cmd *cmd;
+
+	cmd = carl9170_cmd_buf(ar, CARL9170_CMD_BCN_CTRL_ASYNC,
+			       sizeof(struct carl9170_bcn_ctrl_cmd));
+	if (!cmd)
+		return -ENOMEM;
+
+	cmd->bcn_ctrl.vif_id = cpu_to_le32(vif_id);
+	cmd->bcn_ctrl.mode = cpu_to_le32(mode);
+	cmd->bcn_ctrl.bcn_addr = cpu_to_le32(addr);
+	cmd->bcn_ctrl.bcn_len = cpu_to_le32(len);
+
+	return __carl9170_exec_cmd(ar, cmd, true);
+}
+
+int carl9170_powersave(struct ar9170 *ar, const bool ps)
+{
+	struct carl9170_cmd *cmd;
+	u32 state;
+
+	cmd = carl9170_cmd_buf(ar, CARL9170_CMD_PSM_ASYNC,
+			       sizeof(struct carl9170_psm));
+	if (!cmd)
+		return -ENOMEM;
+
+	if (ps) {
+		/* Sleep until next TBTT */
+		state = CARL9170_PSM_SLEEP | 1;
+	} else {
+		/* wake up immediately */
+		state = 1;
+	}
+
+	cmd->psm.state = cpu_to_le32(state);
+	return __carl9170_exec_cmd(ar, cmd, true);
+}
diff --git a/drivers/net/wireless/ath/carl9170/cmd.h b/drivers/net/wireless/ath/carl9170/cmd.h
new file mode 100644
index 0000000..0fc83d2
--- /dev/null
+++ b/drivers/net/wireless/ath/carl9170/cmd.h
@@ -0,0 +1,158 @@
+/*
+ * Atheros CARL9170 driver
+ *
+ * Basic HW register/memory/command access functions
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2010, Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * This program is free software; you can redistribute it 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 program 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or distribute this software for any
+ *    purpose with or without fee is hereby granted, provided that the above
+ *    copyright notice and this permission notice appear in all copies.
+ *
+ *    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef __CMD_H
+#define __CMD_H
+
+#include "carl9170.h"
+
+/* basic HW access */
+int carl9170_write_reg(struct ar9170 *ar, const u32 reg, const u32 val);
+int carl9170_read_reg(struct ar9170 *ar, const u32 reg, u32 *val);
+int carl9170_read_mreg(struct ar9170 *ar, const int nregs,
+		       const u32 *regs, u32 *out);
+int carl9170_echo_test(struct ar9170 *ar, u32 v);
+int carl9170_reboot(struct ar9170 *ar);
+int carl9170_mac_reset(struct ar9170 *ar);
+int carl9170_powersave(struct ar9170 *ar, const bool power_on);
+int carl9170_bcn_ctrl(struct ar9170 *ar, const unsigned int vif_id,
+		       const u32 mode, const u32 addr, const u32 len);
+
+static inline int carl9170_flush_cab(struct ar9170 *ar,
+				     const unsigned int vif_id)
+{
+	return carl9170_bcn_ctrl(ar, vif_id, CARL9170_BCN_CTRL_DRAIN, 0, 0);
+}
+
+struct carl9170_cmd *carl9170_cmd_buf(struct ar9170 *ar,
+	const enum carl9170_cmd_oids cmd, const unsigned int len);
+
+/*
+ * Macros to facilitate writing multiple registers in a single
+ * write-combining USB command. Note that when the first group
+ * fails the whole thing will fail without any others attempted,
+ * but you won't know which write in the group failed.
+ */
+#define carl9170_regwrite_begin(ar)					\
+do {									\
+	int __nreg = 0, __err = 0;					\
+	struct ar9170 *__ar = ar;
+
+#define carl9170_regwrite(r, v) do {					\
+	__ar->cmd_buf[2 * __nreg + 1] = cpu_to_le32(r);			\
+	__ar->cmd_buf[2 * __nreg + 2] = cpu_to_le32(v);			\
+	__nreg++;							\
+	if ((__nreg >= PAYLOAD_MAX/2)) {				\
+		if (IS_ACCEPTING_CMD(__ar))				\
+			__err = carl9170_exec_cmd(__ar,			\
+				CARL9170_CMD_WREG, 8 * __nreg,		\
+				(u8 *) &__ar->cmd_buf[1], 0, NULL);	\
+		else							\
+			goto __regwrite_out;				\
+									\
+		__nreg = 0;						\
+		if (__err)						\
+			goto __regwrite_out;				\
+	}								\
+} while (0)
+
+#define carl9170_regwrite_finish()					\
+__regwrite_out :							\
+	if (__err == 0 && __nreg) {					\
+		if (IS_ACCEPTING_CMD(__ar))				\
+			__err = carl9170_exec_cmd(__ar,			\
+				CARL9170_CMD_WREG, 8 * __nreg,		\
+				(u8 *) &__ar->cmd_buf[1], 0, NULL);	\
+		__nreg = 0;						\
+	}
+
+#define carl9170_regwrite_result()					\
+	__err;								\
+} while (0);
+
+
+#define carl9170_async_get_buf()					\
+do {									\
+	__cmd = carl9170_cmd_buf(__carl, CARL9170_CMD_WREG_ASYNC,	\
+				 CARL9170_MAX_CMD_PAYLOAD_LEN);		\
+	if (__cmd == NULL) {						\
+		__err = -ENOMEM;					\
+		goto __async_regwrite_out;				\
+	}								\
+} while (0);
+
+#define carl9170_async_regwrite_begin(carl)				\
+do {									\
+	int __nreg = 0, __err = 0;					\
+	struct ar9170 *__carl = carl;					\
+	struct carl9170_cmd *__cmd;					\
+	carl9170_async_get_buf();					\
+
+#define carl9170_async_regwrite(r, v) do {				\
+	__cmd->wreg.regs[__nreg].addr = cpu_to_le32(r);			\
+	__cmd->wreg.regs[__nreg].val = cpu_to_le32(v);			\
+	__nreg++;							\
+	if ((__nreg >= PAYLOAD_MAX/2)) {				\
+		if (IS_ACCEPTING_CMD(__carl)) {				\
+			__cmd->hdr.len = 8 * __nreg;			\
+			__err = __carl9170_exec_cmd(__carl, __cmd, true);\
+			__cmd = NULL;					\
+			carl9170_async_get_buf();			\
+		} else {						\
+			goto __async_regwrite_out;			\
+		}							\
+		__nreg = 0;						\
+		if (__err)						\
+			goto __async_regwrite_out;			\
+	}								\
+} while (0)
+
+#define carl9170_async_regwrite_finish()				\
+__async_regwrite_out :							\
+	if (__err == 0 && __nreg) {					\
+		__cmd->hdr.len = 8 * __nreg;				\
+		if (IS_ACCEPTING_CMD(__carl))				\
+			__err = __carl9170_exec_cmd(__carl, __cmd, true);\
+		__nreg = 0;						\
+	}
+
+#define carl9170_async_regwrite_result()				\
+	__err;								\
+} while (0);
+
+#endif /* __CMD_H */
diff --git a/drivers/net/wireless/ath/carl9170/debug.c b/drivers/net/wireless/ath/carl9170/debug.c
new file mode 100644
index 0000000..19b4836
--- /dev/null
+++ b/drivers/net/wireless/ath/carl9170/debug.c
@@ -0,0 +1,906 @@
+/*
+ * Atheros CARL9170 driver
+ *
+ * debug(fs) probing
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * This program is free software; you can redistribute it 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 program 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2008-2009 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or distribute this software for any
+ *    purpose with or without fee is hereby granted, provided that the above
+ *    copyright notice and this permission notice appear in all copies.
+ *
+ *    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/vmalloc.h>
+#include "carl9170.h"
+#include "cmd.h"
+
+#define ADD(buf, off, max, fmt, args...)				\
+	off += snprintf(&buf[off], max - off, fmt, ##args);
+
+static int carl9170_debugfs_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+struct carl9170_debugfs_fops {
+	unsigned int read_bufsize;
+	mode_t attr;
+	char *(*read)(struct ar9170 *ar, char *buf, size_t bufsize,
+		      ssize_t *len);
+	ssize_t (*write)(struct ar9170 *aru, const char *buf, size_t size);
+	const struct file_operations fops;
+
+	enum carl9170_device_state req_dev_state;
+};
+
+static ssize_t carl9170_debugfs_read(struct file *file, char __user *userbuf,
+				     size_t count, loff_t *ppos)
+{
+	struct carl9170_debugfs_fops *dfops;
+	struct ar9170 *ar;
+	char *buf = NULL, *res_buf = NULL;
+	ssize_t ret = 0;
+	int err = 0;
+
+	if (!count)
+		return 0;
+
+	ar = file->private_data;
+
+	if (!ar)
+		return -ENODEV;
+	dfops = container_of(file->f_op, struct carl9170_debugfs_fops, fops);
+
+	if (!dfops->read)
+		return -ENOSYS;
+
+	if (dfops->read_bufsize) {
+		buf = vmalloc(dfops->read_bufsize);
+		if (!buf)
+			return -ENOMEM;
+	}
+
+	mutex_lock(&ar->mutex);
+	if (!CHK_DEV_STATE(ar, dfops->req_dev_state)) {
+		err = -ENODEV;
+		res_buf = buf;
+		goto out_free;
+	}
+
+	res_buf = dfops->read(ar, buf, dfops->read_bufsize, &ret);
+
+	if (ret > 0)
+		err = simple_read_from_buffer(userbuf, count, ppos,
+					      res_buf, ret);
+	else
+		err = ret;
+
+	WARN_ON_ONCE(dfops->read_bufsize && (res_buf != buf));
+
+out_free:
+	vfree(res_buf);
+	mutex_unlock(&ar->mutex);
+	return err;
+}
+
+static ssize_t carl9170_debugfs_write(struct file *file,
+	const char __user *userbuf, size_t count, loff_t *ppos)
+{
+	struct carl9170_debugfs_fops *dfops;
+	struct ar9170 *ar;
+	char *buf = NULL;
+	int err = 0;
+
+	if (!count)
+		return 0;
+
+	if (count > PAGE_SIZE)
+		return -E2BIG;
+
+	ar = file->private_data;
+
+	if (!ar)
+		return -ENODEV;
+	dfops = container_of(file->f_op, struct carl9170_debugfs_fops, fops);
+
+	if (!dfops->write)
+		return -ENOSYS;
+
+	buf = vmalloc(count);
+	if (!buf)
+		return -ENOMEM;
+
+	if (copy_from_user(buf, userbuf, count)) {
+		err = -EFAULT;
+		goto out_free;
+	}
+
+	if (mutex_trylock(&ar->mutex) == 0) {
+		err = -EAGAIN;
+		goto out_free;
+	}
+
+	if (!CHK_DEV_STATE(ar, dfops->req_dev_state)) {
+		err = -ENODEV;
+		goto out_unlock;
+	}
+
+	err = dfops->write(ar, buf, count);
+	if (err)
+		goto out_unlock;
+
+out_unlock:
+	mutex_unlock(&ar->mutex);
+
+out_free:
+	vfree(buf);
+	return err;
+}
+
+#define __DEBUGFS_DECLARE_FILE(name, _read, _write, _read_bufsize,	\
+			       _attr, _dstate)				\
+static const struct carl9170_debugfs_fops carl_debugfs_##name ##_ops = {\
+	.read_bufsize = _read_bufsize,					\
+	.read = _read,							\
+	.write = _write,						\
+	.attr = _attr,							\
+	.req_dev_state = _dstate,					\
+	.fops = {							\
+		.open	= carl9170_debugfs_open,			\
+		.read	= carl9170_debugfs_read,			\
+		.write	= carl9170_debugfs_write,			\
+		.owner	= THIS_MODULE					\
+	},								\
+}
+
+#define DEBUGFS_DECLARE_FILE(name, _read, _write, _read_bufsize, _attr)	\
+	__DEBUGFS_DECLARE_FILE(name, _read, _write, _read_bufsize,	\
+			       _attr, CARL9170_STARTED)			\
+
+#define DEBUGFS_DECLARE_RO_FILE(name, _read_bufsize)			\
+	DEBUGFS_DECLARE_FILE(name, carl9170_debugfs_##name ##_read,	\
+			     NULL, _read_bufsize, S_IRUSR)
+
+#define DEBUGFS_DECLARE_WO_FILE(name)					\
+	DEBUGFS_DECLARE_FILE(name, NULL, carl9170_debugfs_##name ##_write,\
+			     0, S_IWUSR)
+
+#define DEBUGFS_DECLARE_RW_FILE(name, _read_bufsize)			\
+	DEBUGFS_DECLARE_FILE(name, carl9170_debugfs_##name ##_read,	\
+			     carl9170_debugfs_##name ##_write,		\
+			     _read_bufsize, S_IRUSR | S_IWUSR)
+
+#define __DEBUGFS_DECLARE_RW_FILE(name, _read_bufsize, _dstate)		\
+	__DEBUGFS_DECLARE_FILE(name, carl9170_debugfs_##name ##_read,	\
+			     carl9170_debugfs_##name ##_write,		\
+			     _read_bufsize, S_IRUSR | S_IWUSR, _dstate)
+
+#define DEBUGFS_READONLY_FILE(name, _read_bufsize, fmt, value...)	\
+static char *carl9170_debugfs_ ##name ## _read(struct ar9170 *ar,	\
+					     char *buf, size_t buf_size,\
+					     ssize_t *len)		\
+{									\
+	ADD(buf, *len, buf_size, fmt "\n", ##value);			\
+	return buf;							\
+}									\
+DEBUGFS_DECLARE_RO_FILE(name, _read_bufsize)
+
+static char *carl9170_debugfs_mem_usage_read(struct ar9170 *ar, char *buf,
+					     size_t bufsize, ssize_t *len)
+{
+	ADD(buf, *len, bufsize, "jar: [");
+
+	spin_lock_bh(&ar->mem_lock);
+
+	*len += bitmap_scnprintf(&buf[*len], bufsize - *len,
+				  ar->mem_bitmap, ar->fw.mem_blocks);
+
+	ADD(buf, *len, bufsize, "]\n");
+
+	ADD(buf, *len, bufsize, "cookies: used:%3d / total:%3d, allocs:%d\n",
+	    bitmap_weight(ar->mem_bitmap, ar->fw.mem_blocks),
+	    ar->fw.mem_blocks, atomic_read(&ar->mem_allocs));
+
+	ADD(buf, *len, bufsize, "memory: free:%3d (%3d KiB) / total:%3d KiB)\n",
+	    atomic_read(&ar->mem_free_blocks),
+	    (atomic_read(&ar->mem_free_blocks) * ar->fw.mem_block_size) / 1024,
+	    (ar->fw.mem_blocks * ar->fw.mem_block_size) / 1024);
+
+	spin_unlock_bh(&ar->mem_lock);
+
+	return buf;
+}
+DEBUGFS_DECLARE_RO_FILE(mem_usage, 512);
+
+static char *carl9170_debugfs_qos_stat_read(struct ar9170 *ar, char *buf,
+					    size_t bufsize, ssize_t *len)
+{
+	ADD(buf, *len, bufsize, "%s QoS AC\n", modparam_noht ? "Hardware" :
+	    "Software");
+
+	ADD(buf, *len, bufsize, "[     VO            VI       "
+				 "     BE            BK      ]\n");
+
+	spin_lock_bh(&ar->tx_stats_lock);
+	ADD(buf, *len, bufsize, "[length/limit  length/limit  "
+				 "length/limit  length/limit ]\n"
+				"[   %3d/%3d       %3d/%3d    "
+				 "   %3d/%3d       %3d/%3d   ]\n\n",
+	    ar->tx_stats[0].len, ar->tx_stats[0].limit,
+	    ar->tx_stats[1].len, ar->tx_stats[1].limit,
+	    ar->tx_stats[2].len, ar->tx_stats[2].limit,
+	    ar->tx_stats[3].len, ar->tx_stats[3].limit);
+
+	ADD(buf, *len, bufsize, "[    total         total     "
+				 "    total         total    ]\n"
+				"[%10d    %10d    %10d    %10d   ]\n\n",
+	    ar->tx_stats[0].count, ar->tx_stats[1].count,
+	    ar->tx_stats[2].count, ar->tx_stats[3].count);
+
+	spin_unlock_bh(&ar->tx_stats_lock);
+
+	ADD(buf, *len, bufsize, "[  pend/waittx   pend/waittx "
+				 "  pend/waittx   pend/waittx]\n"
+				"[   %3d/%3d       %3d/%3d    "
+				 "   %3d/%3d       %3d/%3d   ]\n\n",
+	    skb_queue_len(&ar->tx_pending[0]),
+	    skb_queue_len(&ar->tx_status[0]),
+	    skb_queue_len(&ar->tx_pending[1]),
+	    skb_queue_len(&ar->tx_status[1]),
+	    skb_queue_len(&ar->tx_pending[2]),
+	    skb_queue_len(&ar->tx_status[2]),
+	    skb_queue_len(&ar->tx_pending[3]),
+	    skb_queue_len(&ar->tx_status[3]));
+
+	return buf;
+}
+DEBUGFS_DECLARE_RO_FILE(qos_stat, 512);
+
+static void carl9170_debugfs_format_frame(struct ar9170 *ar,
+	struct sk_buff *skb, const char *prefix, char *buf,
+	ssize_t *off, ssize_t bufsize)
+{
+	struct _carl9170_tx_superframe *txc = (void *) skb->data;
+	struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb);
+	struct carl9170_tx_info *arinfo = (void *) txinfo->rate_driver_data;
+	struct ieee80211_hdr *hdr = (void *) txc->frame_data;
+
+	ADD(buf, *off, bufsize, "%s %p, c:%2x, DA:%pM, sq:%4d, mc:%.4x, "
+	    "pc:%.8x, to:%d ms\n", prefix, skb, txc->s.cookie,
+	    ieee80211_get_DA(hdr), get_seq_h(hdr),
+	    le16_to_cpu(txc->f.mac_control), le32_to_cpu(txc->f.phy_control),
+	    jiffies_to_msecs(jiffies - arinfo->timeout));
+}
+
+
+static char *carl9170_debugfs_ampdu_state_read(struct ar9170 *ar, char *buf,
+					       size_t bufsize, ssize_t *len)
+{
+	struct carl9170_sta_tid *iter;
+	struct sk_buff *skb;
+	int cnt = 0, fc;
+	int offset;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(iter, &ar->tx_ampdu_list, list) {
+
+		spin_lock_bh(&iter->lock);
+		ADD(buf, *len, bufsize, "Entry: #%2d TID:%1d, BSN:%4d, "
+		    "SNX:%4d, HSN:%4d, BAW:%2d, state:%1d, toggles:%d\n",
+		    cnt, iter->tid, iter->bsn, iter->snx, iter->hsn,
+		    iter->max, iter->state, iter->counter);
+
+		ADD(buf, *len, bufsize, "\tWindow:  [");
+
+		*len += bitmap_scnprintf(&buf[*len], bufsize - *len,
+			iter->bitmap, CARL9170_BAW_BITS);
+
+#define BM_STR_OFF(offset)					\
+	((CARL9170_BAW_BITS - (offset) - 1) / 4 +		\
+	 (CARL9170_BAW_BITS - (offset) - 1) / 32 + 1)
+
+		ADD(buf, *len, bufsize, ",W]\n");
+
+		offset = BM_STR_OFF(0);
+		ADD(buf, *len, bufsize, "\tBase Seq: %*s\n", offset, "T");
+
+		offset = BM_STR_OFF(SEQ_DIFF(iter->snx, iter->bsn));
+		ADD(buf, *len, bufsize, "\tNext Seq: %*s\n", offset, "W");
+
+		offset = BM_STR_OFF(((int)iter->hsn - (int)iter->bsn) %
+				     CARL9170_BAW_BITS);
+		ADD(buf, *len, bufsize, "\tLast Seq: %*s\n", offset, "N");
+
+		ADD(buf, *len, bufsize, "\tPre-Aggregation reorder buffer: "
+		    " currently queued:%d\n", skb_queue_len(&iter->queue));
+
+		fc = 0;
+		skb_queue_walk(&iter->queue, skb) {
+			char prefix[32];
+
+			snprintf(prefix, sizeof(prefix), "\t\t%3d :", fc);
+			carl9170_debugfs_format_frame(ar, skb, prefix, buf,
+						      len, bufsize);
+
+			fc++;
+		}
+		spin_unlock_bh(&iter->lock);
+		cnt++;
+	}
+	rcu_read_unlock();
+
+	return buf;
+}
+DEBUGFS_DECLARE_RO_FILE(ampdu_state, 8000);
+
+static void carl9170_debugfs_queue_dump(struct ar9170 *ar, char *buf,
+	ssize_t *len, size_t bufsize, struct sk_buff_head *queue)
+{
+	struct sk_buff *skb;
+	char prefix[16];
+	int fc = 0;
+
+	spin_lock_bh(&queue->lock);
+	skb_queue_walk(queue, skb) {
+		snprintf(prefix, sizeof(prefix), "%3d :", fc);
+		carl9170_debugfs_format_frame(ar, skb, prefix, buf,
+					      len, bufsize);
+		fc++;
+	}
+	spin_unlock_bh(&queue->lock);
+}
+
+#define DEBUGFS_QUEUE_DUMP(q, qi)					\
+static char *carl9170_debugfs_##q ##_##qi ##_read(struct ar9170 *ar,	\
+	char *buf, size_t bufsize, ssize_t *len)			\
+{									\
+	carl9170_debugfs_queue_dump(ar, buf, len, bufsize, &ar->q[qi]);	\
+	return buf;							\
+}									\
+DEBUGFS_DECLARE_RO_FILE(q##_##qi, 8000);
+
+static char *carl9170_debugfs_sta_psm_read(struct ar9170 *ar, char *buf,
+					   size_t bufsize, ssize_t *len)
+{
+	ADD(buf, *len, bufsize, "psm state: %s\n", (ar->ps.off_override ?
+	    "FORCE CAM" : (ar->ps.state ? "PSM" : "CAM")));
+
+	ADD(buf, *len, bufsize, "sleep duration: %d ms.\n", ar->ps.sleep_ms);
+	ADD(buf, *len, bufsize, "last power-state transition: %d ms ago.\n",
+	    jiffies_to_msecs(jiffies - ar->ps.last_action));
+	ADD(buf, *len, bufsize, "last CAM->PSM transition: %d ms ago.\n",
+	    jiffies_to_msecs(jiffies - ar->ps.last_slept));
+
+	return buf;
+}
+DEBUGFS_DECLARE_RO_FILE(sta_psm, 160);
+
+static char *carl9170_debugfs_tx_stuck_read(struct ar9170 *ar, char *buf,
+					    size_t bufsize, ssize_t *len)
+{
+	int i;
+
+	for (i = 0; i < ar->hw->queues; i++) {
+		ADD(buf, *len, bufsize, "TX queue [%d]: %10d max:%10d ms.\n",
+		    i, ieee80211_queue_stopped(ar->hw, i) ?
+		    jiffies_to_msecs(jiffies - ar->queue_stop_timeout[i]) : 0,
+		    jiffies_to_msecs(ar->max_queue_stop_timeout[i]));
+
+		ar->max_queue_stop_timeout[i] = 0;
+	}
+
+	return buf;
+}
+DEBUGFS_DECLARE_RO_FILE(tx_stuck, 180);
+
+static char *carl9170_debugfs_phy_noise_read(struct ar9170 *ar, char *buf,
+					     size_t bufsize, ssize_t *len)
+{
+	int err;
+
+	err = carl9170_get_noisefloor(ar);
+	if (err) {
+		*len = err;
+		return buf;
+	}
+
+	ADD(buf, *len, bufsize, "Chain 0: %10d dBm, ext. chan.:%10d dBm\n",
+	    ar->noise[0], ar->noise[2]);
+	ADD(buf, *len, bufsize, "Chain 2: %10d dBm, ext. chan.:%10d dBm\n",
+	    ar->noise[1], ar->noise[3]);
+
+	return buf;
+}
+DEBUGFS_DECLARE_RO_FILE(phy_noise, 180);
+
+static char *carl9170_debugfs_vif_dump_read(struct ar9170 *ar, char *buf,
+					    size_t bufsize, ssize_t *len)
+{
+	struct carl9170_vif_info *iter;
+	int i = 0;
+
+	ADD(buf, *len, bufsize, "registered VIFs:%d \\ %d\n",
+	    ar->vifs, ar->fw.vif_num);
+
+	ADD(buf, *len, bufsize, "VIF bitmap: [");
+
+	*len += bitmap_scnprintf(&buf[*len], bufsize - *len,
+				 &ar->vif_bitmap, ar->fw.vif_num);
+
+	ADD(buf, *len, bufsize, "]\n");
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(iter, &ar->vif_list, list) {
+		struct ieee80211_vif *vif = carl9170_get_vif(iter);
+		ADD(buf, *len, bufsize, "\t%d = [%s VIF, id:%d, type:%x "
+		    " mac:%pM %s]\n", i, (carl9170_get_main_vif(ar) == vif ?
+		    "Master" : " Slave"), iter->id, vif->type, vif->addr,
+		    iter->enable_beacon ? "beaconing " : "");
+		i++;
+	}
+	rcu_read_unlock();
+
+	return buf;
+}
+DEBUGFS_DECLARE_RO_FILE(vif_dump, 8000);
+
+#define UPDATE_COUNTER(ar, name)	({				\
+	u32 __tmp[ARRAY_SIZE(name##_regs)];				\
+	unsigned int __i, __err = -ENODEV;				\
+									\
+	for (__i = 0; __i < ARRAY_SIZE(name##_regs); __i++) {		\
+		__tmp[__i] = name##_regs[__i].reg;			\
+		ar->debug.stats.name##_counter[__i] = 0;		\
+	}								\
+									\
+	if (IS_STARTED(ar))						\
+		__err = carl9170_read_mreg(ar, ARRAY_SIZE(name##_regs),	\
+			__tmp, ar->debug.stats.name##_counter);		\
+	(__err); })
+
+#define TALLY_SUM_UP(ar, name)	do {					\
+	unsigned int __i;						\
+									\
+	for (__i = 0; __i < ARRAY_SIZE(name##_regs); __i++) {		\
+		ar->debug.stats.name##_sum[__i] +=			\
+			ar->debug.stats.name##_counter[__i];		\
+	}								\
+} while (0)
+
+#define DEBUGFS_HW_TALLY_FILE(name, f)					\
+static char *carl9170_debugfs_##name ## _read(struct ar9170 *ar,	\
+	 char *dum, size_t bufsize, ssize_t *ret)			\
+{									\
+	char *buf;							\
+	int i, max_len, err;						\
+									\
+	max_len = ARRAY_SIZE(name##_regs) * 80;				\
+	buf = vmalloc(max_len);						\
+	if (!buf)							\
+		return NULL;						\
+									\
+	err = UPDATE_COUNTER(ar, name);					\
+	if (err) {							\
+		*ret = err;						\
+		return buf;						\
+	}								\
+									\
+	TALLY_SUM_UP(ar, name);						\
+									\
+	for (i = 0; i < ARRAY_SIZE(name##_regs); i++) {			\
+		ADD(buf, *ret, max_len, "%22s = %" f "[+%" f "]\n",	\
+		    name##_regs[i].nreg, ar->debug.stats.name ##_sum[i],\
+		    ar->debug.stats.name ##_counter[i]);		\
+	}								\
+									\
+	return buf;							\
+}									\
+DEBUGFS_DECLARE_RO_FILE(name, 0);
+
+#define DEBUGFS_HW_REG_FILE(name, f)					\
+static char *carl9170_debugfs_##name ## _read(struct ar9170 *ar,	\
+	char *dum, size_t bufsize, ssize_t *ret)			\
+{									\
+	char *buf;							\
+	int i, max_len, err;						\
+									\
+	max_len = ARRAY_SIZE(name##_regs) * 80;				\
+	buf = vmalloc(max_len);						\
+	if (!buf)							\
+		return NULL;						\
+									\
+	err = UPDATE_COUNTER(ar, name);					\
+	if (err) {							\
+		*ret = err;						\
+		return buf;						\
+	}								\
+									\
+	for (i = 0; i < ARRAY_SIZE(name##_regs); i++) {			\
+		ADD(buf, *ret, max_len, "%22s = %" f "\n",		\
+		    name##_regs[i].nreg,				\
+		    ar->debug.stats.name##_counter[i]);			\
+	}								\
+									\
+	return buf;							\
+}									\
+DEBUGFS_DECLARE_RO_FILE(name, 0);
+
+static ssize_t carl9170_debugfs_hw_ioread32_write(struct ar9170 *ar,
+	const char *buf, size_t count)
+{
+	int err = 0, i, n = 0, max_len = 32, res;
+	unsigned int reg, tmp;
+
+	if (!count)
+		return 0;
+
+	if (count > max_len)
+		return -E2BIG;
+
+	res = sscanf(buf, "0x%X %d", &reg, &n);
+	if (res < 1) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	if (res == 1)
+		n = 1;
+
+	if (n > 15) {
+		err = -EMSGSIZE;
+		goto out;
+	}
+
+	if ((reg >= 0x280000) || ((reg + (n << 2)) >= 0x280000)) {
+		err = -EADDRNOTAVAIL;
+		goto out;
+	}
+
+	if (reg & 3) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	for (i = 0; i < n; i++) {
+		err = carl9170_read_reg(ar, reg + (i << 2), &tmp);
+		if (err)
+			goto out;
+
+		ar->debug.ring[ar->debug.ring_tail].reg = reg + (i << 2);
+		ar->debug.ring[ar->debug.ring_tail].value = tmp;
+		ar->debug.ring_tail++;
+		ar->debug.ring_tail %= CARL9170_DEBUG_RING_SIZE;
+	}
+
+out:
+	return err ? err : count;
+}
+
+static char *carl9170_debugfs_hw_ioread32_read(struct ar9170 *ar, char *buf,
+					       size_t bufsize, ssize_t *ret)
+{
+	int i = 0;
+
+	while (ar->debug.ring_head != ar->debug.ring_tail) {
+		ADD(buf, *ret, bufsize, "%.8x = %.8x\n",
+		    ar->debug.ring[ar->debug.ring_head].reg,
+		    ar->debug.ring[ar->debug.ring_head].value);
+
+		ar->debug.ring_head++;
+		ar->debug.ring_head %= CARL9170_DEBUG_RING_SIZE;
+
+		if (i++ == 64)
+			break;
+	}
+	ar->debug.ring_head = ar->debug.ring_tail;
+	return buf;
+}
+DEBUGFS_DECLARE_RW_FILE(hw_ioread32, CARL9170_DEBUG_RING_SIZE * 40);
+
+static ssize_t carl9170_debugfs_bug_write(struct ar9170 *ar, const char *buf,
+					  size_t count)
+{
+	int err;
+
+	if (count < 1)
+		return -EINVAL;
+
+	switch (buf[0]) {
+	case 'F':
+		ar->needs_full_reset = true;
+		break;
+
+	case 'R':
+		if (!IS_STARTED(ar)) {
+			err = -EAGAIN;
+			goto out;
+		}
+
+		ar->needs_full_reset = false;
+		break;
+
+	case 'M':
+		err = carl9170_mac_reset(ar);
+		if (err < 0)
+			count = err;
+
+		goto out;
+
+	case 'P':
+		err = carl9170_set_channel(ar, ar->hw->conf.channel,
+			ar->hw->conf.channel_type, CARL9170_RFI_COLD);
+		if (err < 0)
+			count = err;
+
+		goto out;
+
+	default:
+		return -EINVAL;
+	}
+
+	carl9170_restart(ar, CARL9170_RR_USER_REQUEST);
+
+out:
+	return count;
+}
+
+static char *carl9170_debugfs_bug_read(struct ar9170 *ar, char *buf,
+				       size_t bufsize, ssize_t *ret)
+{
+	ADD(buf, *ret, bufsize, "[P]hy reinit, [R]estart, [F]ull usb reset, "
+	    "[M]ac reset\n");
+	ADD(buf, *ret, bufsize, "firmware restarts:%d, last reason:%d\n",
+		ar->restart_counter, ar->last_reason);
+	ADD(buf, *ret, bufsize, "phy reinit errors:%d (%d)\n",
+		ar->total_chan_fail, ar->chan_fail);
+	ADD(buf, *ret, bufsize, "reported firmware errors:%d\n",
+		ar->fw.err_counter);
+	ADD(buf, *ret, bufsize, "reported firmware BUGs:%d\n",
+		ar->fw.bug_counter);
+	ADD(buf, *ret, bufsize, "pending restart requests:%d\n",
+		atomic_read(&ar->pending_restarts));
+	return buf;
+}
+__DEBUGFS_DECLARE_RW_FILE(bug, 400, CARL9170_STOPPED);
+
+static const char *erp_modes[] = {
+	[CARL9170_ERP_INVALID] = "INVALID",
+	[CARL9170_ERP_AUTO] = "Automatic",
+	[CARL9170_ERP_MAC80211] = "Set by MAC80211",
+	[CARL9170_ERP_OFF] = "Force Off",
+	[CARL9170_ERP_RTS] = "Force RTS",
+	[CARL9170_ERP_CTS] = "Force CTS"
+};
+
+static char *carl9170_debugfs_erp_read(struct ar9170 *ar, char *buf,
+				       size_t bufsize, ssize_t *ret)
+{
+	ADD(buf, *ret, bufsize, "ERP Setting: (%d) -> %s\n", ar->erp_mode,
+	    erp_modes[ar->erp_mode]);
+	return buf;
+}
+
+static ssize_t carl9170_debugfs_erp_write(struct ar9170 *ar, const char *buf,
+					  size_t count)
+{
+	int res, val;
+
+	if (count < 1)
+		return -EINVAL;
+
+	res = sscanf(buf, "%d", &val);
+	if (res != 1)
+		return -EINVAL;
+
+	if (!((val > CARL9170_ERP_INVALID) &&
+	      (val < __CARL9170_ERP_NUM)))
+		return -EINVAL;
+
+	ar->erp_mode = val;
+	return count;
+}
+
+DEBUGFS_DECLARE_RW_FILE(erp, 80);
+
+static ssize_t carl9170_debugfs_hw_iowrite32_write(struct ar9170 *ar,
+	const char *buf, size_t count)
+{
+	int err = 0, max_len = 22, res;
+	u32 reg, val;
+
+	if (!count)
+		return 0;
+
+	if (count > max_len)
+		return -E2BIG;
+
+	res = sscanf(buf, "0x%X 0x%X", &reg, &val);
+	if (res != 2) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	if (reg <= 0x100000 || reg >= 0x280000) {
+		err = -EADDRNOTAVAIL;
+		goto out;
+	}
+
+	if (reg & 3) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	err = carl9170_write_reg(ar, reg, val);
+	if (err)
+		goto out;
+
+out:
+	return err ? err : count;
+}
+DEBUGFS_DECLARE_WO_FILE(hw_iowrite32);
+
+DEBUGFS_HW_TALLY_FILE(hw_tx_tally, "u");
+DEBUGFS_HW_TALLY_FILE(hw_rx_tally, "u");
+DEBUGFS_HW_TALLY_FILE(hw_phy_errors, "u");
+DEBUGFS_HW_REG_FILE(hw_wlan_queue, ".8x");
+DEBUGFS_HW_REG_FILE(hw_pta_queue, ".8x");
+DEBUGFS_HW_REG_FILE(hw_ampdu_info, ".8x");
+DEBUGFS_QUEUE_DUMP(tx_status, 0);
+DEBUGFS_QUEUE_DUMP(tx_status, 1);
+DEBUGFS_QUEUE_DUMP(tx_status, 2);
+DEBUGFS_QUEUE_DUMP(tx_status, 3);
+DEBUGFS_QUEUE_DUMP(tx_pending, 0);
+DEBUGFS_QUEUE_DUMP(tx_pending, 1);
+DEBUGFS_QUEUE_DUMP(tx_pending, 2);
+DEBUGFS_QUEUE_DUMP(tx_pending, 3);
+DEBUGFS_READONLY_FILE(usb_tx_anch_urbs, 20, "%d",
+		      atomic_read(&ar->tx_anch_urbs));
+DEBUGFS_READONLY_FILE(usb_rx_anch_urbs, 20, "%d",
+		      atomic_read(&ar->rx_anch_urbs));
+DEBUGFS_READONLY_FILE(usb_rx_work_urbs, 20, "%d",
+		      atomic_read(&ar->rx_work_urbs));
+DEBUGFS_READONLY_FILE(usb_rx_pool_urbs, 20, "%d",
+		      atomic_read(&ar->rx_pool_urbs));
+
+DEBUGFS_READONLY_FILE(tx_total_queued, 20, "%d",
+		      atomic_read(&ar->tx_total_queued));
+DEBUGFS_READONLY_FILE(tx_ampdu_scheduler, 20, "%d",
+		      atomic_read(&ar->tx_ampdu_scheduler));
+DEBUGFS_READONLY_FILE(tx_ampdu_timeout, 20, "%d",
+		      ar->tx_ampdu_timeout);
+
+DEBUGFS_READONLY_FILE(tx_total_pending, 20, "%d",
+		      atomic_read(&ar->tx_total_pending));
+
+DEBUGFS_READONLY_FILE(tx_ampdu_list_len, 20, "%d",
+		      ar->tx_ampdu_list_len);
+
+DEBUGFS_READONLY_FILE(tx_ampdu_upload, 20, "%d",
+		      atomic_read(&ar->tx_ampdu_upload));
+
+DEBUGFS_READONLY_FILE(tx_janitor_last_run, 64, "last run:%d ms ago",
+	jiffies_to_msecs(jiffies - ar->tx_janitor_last_run));
+
+DEBUGFS_READONLY_FILE(tx_dropped, 20, "%d", ar->tx_dropped);
+
+DEBUGFS_READONLY_FILE(rx_dropped, 20, "%d", ar->rx_dropped);
+
+DEBUGFS_READONLY_FILE(sniffer_enabled, 20, "%d", ar->sniffer_enabled);
+DEBUGFS_READONLY_FILE(rx_software_decryption, 20, "%d",
+		      ar->rx_software_decryption);
+DEBUGFS_READONLY_FILE(ampdu_factor, 20, "%d",
+		      ar->current_factor);
+DEBUGFS_READONLY_FILE(ampdu_density, 20, "%d",
+		      ar->current_density);
+
+DEBUGFS_READONLY_FILE(beacon_int, 20, "%d TU", ar->global_beacon_int);
+DEBUGFS_READONLY_FILE(pretbtt, 20, "%d TU", ar->global_pretbtt);
+
+void carl9170_debugfs_register(struct ar9170 *ar)
+{
+	ar->debug_dir = debugfs_create_dir(KBUILD_MODNAME,
+		ar->hw->wiphy->debugfsdir);
+
+#define DEBUGFS_ADD(name)						\
+	debugfs_create_file(#name, carl_debugfs_##name ##_ops.attr,	\
+			    ar->debug_dir, ar,				\
+			    &carl_debugfs_##name ## _ops.fops);
+
+	DEBUGFS_ADD(usb_tx_anch_urbs);
+	DEBUGFS_ADD(usb_rx_pool_urbs);
+	DEBUGFS_ADD(usb_rx_anch_urbs);
+	DEBUGFS_ADD(usb_rx_work_urbs);
+
+	DEBUGFS_ADD(tx_total_queued);
+	DEBUGFS_ADD(tx_total_pending);
+	DEBUGFS_ADD(tx_dropped);
+	DEBUGFS_ADD(tx_stuck);
+	DEBUGFS_ADD(tx_ampdu_upload);
+	DEBUGFS_ADD(tx_ampdu_scheduler);
+	DEBUGFS_ADD(tx_ampdu_list_len);
+
+	DEBUGFS_ADD(rx_dropped);
+	DEBUGFS_ADD(sniffer_enabled);
+	DEBUGFS_ADD(rx_software_decryption);
+
+	DEBUGFS_ADD(mem_usage);
+	DEBUGFS_ADD(qos_stat);
+	DEBUGFS_ADD(sta_psm);
+	DEBUGFS_ADD(ampdu_state);
+
+	DEBUGFS_ADD(hw_tx_tally);
+	DEBUGFS_ADD(hw_rx_tally);
+	DEBUGFS_ADD(hw_phy_errors);
+	DEBUGFS_ADD(phy_noise);
+
+	DEBUGFS_ADD(hw_wlan_queue);
+	DEBUGFS_ADD(hw_pta_queue);
+	DEBUGFS_ADD(hw_ampdu_info);
+
+	DEBUGFS_ADD(ampdu_density);
+	DEBUGFS_ADD(ampdu_factor);
+
+	DEBUGFS_ADD(tx_ampdu_timeout);
+
+	DEBUGFS_ADD(tx_janitor_last_run);
+
+	DEBUGFS_ADD(tx_status_0);
+	DEBUGFS_ADD(tx_status_1);
+	DEBUGFS_ADD(tx_status_2);
+	DEBUGFS_ADD(tx_status_3);
+
+	DEBUGFS_ADD(tx_pending_0);
+	DEBUGFS_ADD(tx_pending_1);
+	DEBUGFS_ADD(tx_pending_2);
+	DEBUGFS_ADD(tx_pending_3);
+
+	DEBUGFS_ADD(hw_ioread32);
+	DEBUGFS_ADD(hw_iowrite32);
+	DEBUGFS_ADD(bug);
+
+	DEBUGFS_ADD(erp);
+
+	DEBUGFS_ADD(vif_dump);
+
+	DEBUGFS_ADD(beacon_int);
+	DEBUGFS_ADD(pretbtt);
+
+#undef DEBUGFS_ADD
+}
+
+void carl9170_debugfs_unregister(struct ar9170 *ar)
+{
+	debugfs_remove_recursive(ar->debug_dir);
+}
diff --git a/drivers/net/wireless/ath/carl9170/debug.h b/drivers/net/wireless/ath/carl9170/debug.h
new file mode 100644
index 0000000..ea4b975
--- /dev/null
+++ b/drivers/net/wireless/ath/carl9170/debug.h
@@ -0,0 +1,134 @@
+/*
+ * Atheros CARL9170 driver
+ *
+ * debug header
+ *
+ * Copyright 2010, Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * This program is free software; you can redistribute it 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 program 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or distribute this software for any
+ *    purpose with or without fee is hereby granted, provided that the above
+ *    copyright notice and this permission notice appear in all copies.
+ *
+ *    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef __DEBUG_H
+#define __DEBUG_H
+
+#include "eeprom.h"
+#include "wlan.h"
+#include "hw.h"
+#include "fwdesc.h"
+#include "fwcmd.h"
+#include "../regd.h"
+
+struct hw_stat_reg_entry {
+	u32 reg;
+	char nreg[32];
+};
+
+#define	STAT_MAC_REG(reg)	\
+	{ (AR9170_MAC_REG_##reg), #reg }
+
+#define	STAT_PTA_REG(reg)	\
+	{ (AR9170_PTA_REG_##reg), #reg }
+
+#define	STAT_USB_REG(reg)	\
+	{ (AR9170_USB_REG_##reg), #reg }
+
+static const struct hw_stat_reg_entry hw_rx_tally_regs[] = {
+	STAT_MAC_REG(RX_CRC32),		STAT_MAC_REG(RX_CRC16),
+	STAT_MAC_REG(RX_TIMEOUT_COUNT),	STAT_MAC_REG(RX_ERR_DECRYPTION_UNI),
+	STAT_MAC_REG(RX_ERR_DECRYPTION_MUL), STAT_MAC_REG(RX_MPDU),
+	STAT_MAC_REG(RX_DROPPED_MPDU),	STAT_MAC_REG(RX_DEL_MPDU),
+};
+
+static const struct hw_stat_reg_entry hw_phy_errors_regs[] = {
+	STAT_MAC_REG(RX_PHY_MISC_ERROR), STAT_MAC_REG(RX_PHY_XR_ERROR),
+	STAT_MAC_REG(RX_PHY_OFDM_ERROR), STAT_MAC_REG(RX_PHY_CCK_ERROR),
+	STAT_MAC_REG(RX_PHY_HT_ERROR), STAT_MAC_REG(RX_PHY_TOTAL),
+};
+
+static const struct hw_stat_reg_entry hw_tx_tally_regs[] = {
+	STAT_MAC_REG(TX_TOTAL),		STAT_MAC_REG(TX_UNDERRUN),
+	STAT_MAC_REG(TX_RETRY),
+};
+
+static const struct hw_stat_reg_entry hw_wlan_queue_regs[] = {
+	STAT_MAC_REG(DMA_STATUS),	STAT_MAC_REG(DMA_TRIGGER),
+	STAT_MAC_REG(DMA_TXQ0_ADDR),	STAT_MAC_REG(DMA_TXQ0_CURR_ADDR),
+	STAT_MAC_REG(DMA_TXQ1_ADDR),	STAT_MAC_REG(DMA_TXQ1_CURR_ADDR),
+	STAT_MAC_REG(DMA_TXQ2_ADDR),	STAT_MAC_REG(DMA_TXQ2_CURR_ADDR),
+	STAT_MAC_REG(DMA_TXQ3_ADDR),	STAT_MAC_REG(DMA_TXQ3_CURR_ADDR),
+	STAT_MAC_REG(DMA_RXQ_ADDR),	STAT_MAC_REG(DMA_RXQ_CURR_ADDR),
+};
+
+static const struct hw_stat_reg_entry hw_ampdu_info_regs[] = {
+	STAT_MAC_REG(AMPDU_DENSITY),	STAT_MAC_REG(AMPDU_FACTOR),
+};
+
+static const struct hw_stat_reg_entry hw_pta_queue_regs[] = {
+	STAT_PTA_REG(DN_CURR_ADDRH),	STAT_PTA_REG(DN_CURR_ADDRL),
+	STAT_PTA_REG(UP_CURR_ADDRH),	STAT_PTA_REG(UP_CURR_ADDRL),
+	STAT_PTA_REG(DMA_STATUS),	STAT_PTA_REG(DMA_MODE_CTRL),
+};
+
+#define	DEFINE_TALLY(name)					\
+	u32 name##_sum[ARRAY_SIZE(name##_regs)],		\
+	    name##_counter[ARRAY_SIZE(name##_regs)]		\
+
+#define	DEFINE_STAT(name)					\
+	u32 name##_counter[ARRAY_SIZE(name##_regs)]		\
+
+struct ath_stats {
+	DEFINE_TALLY(hw_tx_tally);
+	DEFINE_TALLY(hw_rx_tally);
+	DEFINE_TALLY(hw_phy_errors);
+	DEFINE_STAT(hw_wlan_queue);
+	DEFINE_STAT(hw_pta_queue);
+	DEFINE_STAT(hw_ampdu_info);
+};
+
+struct carl9170_debug_mem_rbe {
+	u32 reg;
+	u32 value;
+};
+
+#define	CARL9170_DEBUG_RING_SIZE			64
+
+struct carl9170_debug {
+	struct ath_stats stats;
+	struct carl9170_debug_mem_rbe ring[CARL9170_DEBUG_RING_SIZE];
+	struct mutex ring_lock;
+	unsigned int ring_head, ring_tail;
+	struct delayed_work update_tally;
+};
+
+struct ar9170;
+
+void carl9170_debugfs_register(struct ar9170 *ar);
+void carl9170_debugfs_unregister(struct ar9170 *ar);
+#endif /* __DEBUG_H */
diff --git a/drivers/net/wireless/ath/carl9170/eeprom.h b/drivers/net/wireless/ath/carl9170/eeprom.h
new file mode 100644
index 0000000..7cff40a
--- /dev/null
+++ b/drivers/net/wireless/ath/carl9170/eeprom.h
@@ -0,0 +1,216 @@
+/*
+ * Shared Atheros AR9170 Header
+ *
+ * EEPROM layout
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This program is free software; you can redistribute it 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 program 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or distribute this software for any
+ *    purpose with or without fee is hereby granted, provided that the above
+ *    copyright notice and this permission notice appear in all copies.
+ *
+ *    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef __CARL9170_SHARED_EEPROM_H
+#define __CARL9170_SHARED_EEPROM_H
+
+#define AR9170_EEPROM_START		0x1600
+
+#define AR5416_MAX_CHAINS		2
+#define AR5416_MODAL_SPURS		5
+
+struct ar9170_eeprom_modal {
+	__le32	antCtrlChain[AR5416_MAX_CHAINS];
+	__le32	antCtrlCommon;
+	s8	antennaGainCh[AR5416_MAX_CHAINS];
+	u8	switchSettling;
+	u8	txRxAttenCh[AR5416_MAX_CHAINS];
+	u8	rxTxMarginCh[AR5416_MAX_CHAINS];
+	s8	adcDesiredSize;
+	s8	pgaDesiredSize;
+	u8	xlnaGainCh[AR5416_MAX_CHAINS];
+	u8	txEndToXpaOff;
+	u8	txEndToRxOn;
+	u8	txFrameToXpaOn;
+	u8	thresh62;
+	s8	noiseFloorThreshCh[AR5416_MAX_CHAINS];
+	u8	xpdGain;
+	u8	xpd;
+	s8	iqCalICh[AR5416_MAX_CHAINS];
+	s8	iqCalQCh[AR5416_MAX_CHAINS];
+	u8	pdGainOverlap;
+	u8	ob;
+	u8	db;
+	u8	xpaBiasLvl;
+	u8	pwrDecreaseFor2Chain;
+	u8	pwrDecreaseFor3Chain;
+	u8	txFrameToDataStart;
+	u8	txFrameToPaOn;
+	u8	ht40PowerIncForPdadc;
+	u8	bswAtten[AR5416_MAX_CHAINS];
+	u8	bswMargin[AR5416_MAX_CHAINS];
+	u8	swSettleHt40;
+	u8	reserved[22];
+	struct spur_channel {
+		__le16 spurChan;
+		u8	spurRangeLow;
+		u8	spurRangeHigh;
+	} __packed spur_channels[AR5416_MODAL_SPURS];
+} __packed;
+
+#define AR5416_NUM_PD_GAINS		4
+#define AR5416_PD_GAIN_ICEPTS		5
+
+struct ar9170_calibration_data_per_freq {
+	u8	pwr_pdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS];
+	u8	vpd_pdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS];
+} __packed;
+
+#define AR5416_NUM_5G_CAL_PIERS		8
+#define AR5416_NUM_2G_CAL_PIERS		4
+
+#define AR5416_NUM_5G_TARGET_PWRS	8
+#define AR5416_NUM_2G_CCK_TARGET_PWRS	3
+#define AR5416_NUM_2G_OFDM_TARGET_PWRS	4
+#define AR5416_MAX_NUM_TGT_PWRS		8
+
+struct ar9170_calibration_target_power_legacy {
+	u8	freq;
+	u8	power[4];
+} __packed;
+
+struct ar9170_calibration_target_power_ht {
+	u8	freq;
+	u8	power[8];
+} __packed;
+
+#define AR5416_NUM_CTLS			24
+
+struct ar9170_calctl_edges {
+	u8	channel;
+#define AR9170_CALCTL_EDGE_FLAGS	0xC0
+	u8	power_flags;
+} __packed;
+
+#define AR5416_NUM_BAND_EDGES		8
+
+struct ar9170_calctl_data {
+	struct ar9170_calctl_edges
+		control_edges[AR5416_MAX_CHAINS][AR5416_NUM_BAND_EDGES];
+} __packed;
+
+struct ar9170_eeprom {
+	__le16	length;
+	__le16	checksum;
+	__le16	version;
+	u8	operating_flags;
+#define AR9170_OPFLAG_5GHZ		1
+#define AR9170_OPFLAG_2GHZ		2
+	u8	misc;
+	__le16	reg_domain[2];
+	u8	mac_address[6];
+	u8	rx_mask;
+	u8	tx_mask;
+	__le16	rf_silent;
+	__le16	bluetooth_options;
+	__le16	device_capabilities;
+	__le32	build_number;
+	u8	deviceType;
+	u8	reserved[33];
+
+	u8	customer_data[64];
+
+	struct ar9170_eeprom_modal
+		modal_header[2];
+
+	u8	cal_freq_pier_5G[AR5416_NUM_5G_CAL_PIERS];
+	u8	cal_freq_pier_2G[AR5416_NUM_2G_CAL_PIERS];
+
+	struct ar9170_calibration_data_per_freq
+		cal_pier_data_5G[AR5416_MAX_CHAINS][AR5416_NUM_5G_CAL_PIERS],
+		cal_pier_data_2G[AR5416_MAX_CHAINS][AR5416_NUM_2G_CAL_PIERS];
+
+	/* power calibration data */
+	struct ar9170_calibration_target_power_legacy
+		cal_tgt_pwr_5G[AR5416_NUM_5G_TARGET_PWRS];
+	struct ar9170_calibration_target_power_ht
+		cal_tgt_pwr_5G_ht20[AR5416_NUM_5G_TARGET_PWRS],
+		cal_tgt_pwr_5G_ht40[AR5416_NUM_5G_TARGET_PWRS];
+
+	struct ar9170_calibration_target_power_legacy
+		cal_tgt_pwr_2G_cck[AR5416_NUM_2G_CCK_TARGET_PWRS],
+		cal_tgt_pwr_2G_ofdm[AR5416_NUM_2G_OFDM_TARGET_PWRS];
+	struct ar9170_calibration_target_power_ht
+		cal_tgt_pwr_2G_ht20[AR5416_NUM_2G_OFDM_TARGET_PWRS],
+		cal_tgt_pwr_2G_ht40[AR5416_NUM_2G_OFDM_TARGET_PWRS];
+
+	/* conformance testing limits */
+	u8	ctl_index[AR5416_NUM_CTLS];
+	struct ar9170_calctl_data
+		ctl_data[AR5416_NUM_CTLS];
+
+	u8	pad;
+	__le16	subsystem_id;
+} __packed;
+
+#define AR9170_LED_MODE_POWER_ON		0x0001
+#define AR9170_LED_MODE_RESERVED		0x0002
+#define AR9170_LED_MODE_DISABLE_STATE		0x0004
+#define AR9170_LED_MODE_OFF_IN_PSM		0x0008
+
+/* AR9170_LED_MODE BIT is set */
+#define AR9170_LED_MODE_FREQUENCY_S		4
+#define AR9170_LED_MODE_FREQUENCY		0x0030
+#define AR9170_LED_MODE_FREQUENCY_1HZ		0x0000
+#define AR9170_LED_MODE_FREQUENCY_0_5HZ		0x0010
+#define AR9170_LED_MODE_FREQUENCY_0_25HZ	0x0020
+#define AR9170_LED_MODE_FREQUENCY_0_125HZ	0x0030
+
+/* AR9170_LED_MODE BIT is not set */
+#define AR9170_LED_MODE_CONN_STATE_S		4
+#define AR9170_LED_MODE_CONN_STATE		0x0030
+#define AR9170_LED_MODE_CONN_STATE_FORCE_OFF	0x0000
+#define AR9170_LED_MODE_CONN_STATE_FORCE_ON	0x0010
+/* Idle off / Active on */
+#define AR9170_LED_MODE_CONN_STATE_IOFF_AON	0x0020
+/* Idle on / Active off */
+#define AR9170_LED_MODE_CONN_STATE_ION_AOFF	0x0010
+
+#define AR9170_LED_MODE_MODE			0x0040
+#define AR9170_LED_MODE_RESERVED2		0x0080
+
+#define AR9170_LED_MODE_TON_SCAN_S		8
+#define AR9170_LED_MODE_TON_SCAN		0x0f00
+
+#define AR9170_LED_MODE_TOFF_SCAN_S		12
+#define AR9170_LED_MODE_TOFF_SCAN		0xf000
+
+struct ar9170_led_mode {
+	__le16 led;
+};
+
+#endif /* __CARL9170_SHARED_EEPROM_H */
diff --git a/drivers/net/wireless/ath/carl9170/fw.c b/drivers/net/wireless/ath/carl9170/fw.c
new file mode 100644
index 0000000..3661546
--- /dev/null
+++ b/drivers/net/wireless/ath/carl9170/fw.c
@@ -0,0 +1,395 @@
+/*
+ * Atheros CARL9170 driver
+ *
+ * firmware parser
+ *
+ * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * This program is free software; you can redistribute it 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 program 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ */
+
+#include <linux/kernel.h>
+#include <linux/firmware.h>
+#include <linux/crc32.h>
+#include "carl9170.h"
+#include "fwcmd.h"
+#include "version.h"
+
+#define MAKE_STR(symbol) #symbol
+#define TO_STR(symbol) MAKE_STR(symbol)
+#define CARL9170FW_API_VER_STR TO_STR(CARL9170FW_API_MAX_VER)
+MODULE_VERSION(CARL9170FW_API_VER_STR ":" CARL9170FW_VERSION_GIT);
+
+static const u8 otus_magic[4] = { OTUS_MAGIC };
+
+static const void *carl9170_fw_find_desc(struct ar9170 *ar, const u8 descid[4],
+	const unsigned int len, const u8 compatible_revision)
+{
+	const struct carl9170fw_desc_head *iter;
+
+	carl9170fw_for_each_hdr(iter, ar->fw.desc) {
+		if (carl9170fw_desc_cmp(iter, descid, len,
+					compatible_revision))
+			return (void *)iter;
+	}
+
+	/* needed to find the LAST desc */
+	if (carl9170fw_desc_cmp(iter, descid, len,
+				compatible_revision))
+		return (void *)iter;
+
+	return NULL;
+}
+
+static int carl9170_fw_verify_descs(struct ar9170 *ar,
+	const struct carl9170fw_desc_head *head, unsigned int max_len)
+{
+	const struct carl9170fw_desc_head *pos;
+	unsigned long pos_addr, end_addr;
+	unsigned int pos_length;
+
+	if (max_len < sizeof(*pos))
+		return -ENODATA;
+
+	max_len = min_t(unsigned int, CARL9170FW_DESC_MAX_LENGTH, max_len);
+
+	pos = head;
+	pos_addr = (unsigned long) pos;
+	end_addr = pos_addr + max_len;
+
+	while (pos_addr < end_addr) {
+		if (pos_addr + sizeof(*head) > end_addr)
+			return -E2BIG;
+
+		pos_length = le16_to_cpu(pos->length);
+
+		if (pos_length < sizeof(*head))
+			return -EBADMSG;
+
+		if (pos_length > max_len)
+			return -EOVERFLOW;
+
+		if (pos_addr + pos_length > end_addr)
+			return -EMSGSIZE;
+
+		if (carl9170fw_desc_cmp(pos, LAST_MAGIC,
+					CARL9170FW_LAST_DESC_SIZE,
+					CARL9170FW_LAST_DESC_CUR_VER))
+			return 0;
+
+		pos_addr += pos_length;
+		pos = (void *)pos_addr;
+		max_len -= pos_length;
+	}
+	return -EINVAL;
+}
+
+static void carl9170_fw_info(struct ar9170 *ar)
+{
+	const struct carl9170fw_motd_desc *motd_desc;
+	unsigned int str_ver_len;
+	u32 fw_date;
+
+	dev_info(&ar->udev->dev, "driver   API: %s 2%03d-%02d-%02d [%d-%d]\n",
+		CARL9170FW_VERSION_GIT, CARL9170FW_VERSION_YEAR,
+		CARL9170FW_VERSION_MONTH, CARL9170FW_VERSION_DAY,
+		CARL9170FW_API_MIN_VER, CARL9170FW_API_MAX_VER);
+
+	motd_desc = carl9170_fw_find_desc(ar, MOTD_MAGIC,
+		sizeof(*motd_desc), CARL9170FW_MOTD_DESC_CUR_VER);
+
+	if (motd_desc) {
+		str_ver_len = strnlen(motd_desc->release,
+			CARL9170FW_MOTD_RELEASE_LEN);
+
+		fw_date = le32_to_cpu(motd_desc->fw_year_month_day);
+
+		dev_info(&ar->udev->dev, "firmware API: %.*s 2%03d-%02d-%02d\n",
+			 str_ver_len, motd_desc->release,
+			 CARL9170FW_GET_YEAR(fw_date),
+			 CARL9170FW_GET_MONTH(fw_date),
+			 CARL9170FW_GET_DAY(fw_date));
+
+		strlcpy(ar->hw->wiphy->fw_version, motd_desc->release,
+			sizeof(ar->hw->wiphy->fw_version));
+	}
+}
+
+static bool valid_dma_addr(const u32 address)
+{
+	if (address >= AR9170_SRAM_OFFSET &&
+	    address < (AR9170_SRAM_OFFSET + AR9170_SRAM_SIZE))
+		return true;
+
+	return false;
+}
+
+static bool valid_cpu_addr(const u32 address)
+{
+	if (valid_dma_addr(address) || (address >= AR9170_PRAM_OFFSET &&
+	    address < (AR9170_PRAM_OFFSET + AR9170_PRAM_SIZE)))
+		return true;
+
+	return false;
+}
+
+static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len)
+{
+	const struct carl9170fw_otus_desc *otus_desc;
+	const struct carl9170fw_chk_desc *chk_desc;
+	const struct carl9170fw_last_desc *last_desc;
+
+	last_desc = carl9170_fw_find_desc(ar, LAST_MAGIC,
+		sizeof(*last_desc), CARL9170FW_LAST_DESC_CUR_VER);
+	if (!last_desc)
+		return -EINVAL;
+
+	otus_desc = carl9170_fw_find_desc(ar, OTUS_MAGIC,
+		sizeof(*otus_desc), CARL9170FW_OTUS_DESC_CUR_VER);
+	if (!otus_desc) {
+		dev_err(&ar->udev->dev, "failed to find compatible firmware "
+			"descriptor.\n");
+		return -ENODATA;
+	}
+
+	chk_desc = carl9170_fw_find_desc(ar, CHK_MAGIC,
+		sizeof(*chk_desc), CARL9170FW_CHK_DESC_CUR_VER);
+
+	if (chk_desc) {
+		unsigned long fin, diff;
+		unsigned int dsc_len;
+		u32 crc32;
+
+		dsc_len = min_t(unsigned int, len,
+			(unsigned long)chk_desc - (unsigned long)otus_desc);
+
+		fin = (unsigned long) last_desc + sizeof(*last_desc);
+		diff = fin - (unsigned long) otus_desc;
+
+		if (diff < len)
+			len -= diff;
+
+		if (len < 256)
+			return -EIO;
+
+		crc32 = crc32_le(~0, data, len);
+		if (cpu_to_le32(crc32) != chk_desc->fw_crc32) {
+			dev_err(&ar->udev->dev, "fw checksum test failed.\n");
+			return -ENOEXEC;
+		}
+
+		crc32 = crc32_le(crc32, (void *)otus_desc, dsc_len);
+		if (cpu_to_le32(crc32) != chk_desc->hdr_crc32) {
+			dev_err(&ar->udev->dev, "descriptor check failed.\n");
+			return -EINVAL;
+		}
+	} else {
+		dev_warn(&ar->udev->dev, "Unprotected firmware image.\n");
+	}
+
+#define SUPP(feat)						\
+	(carl9170fw_supports(otus_desc->feature_set, feat))
+
+	if (!SUPP(CARL9170FW_DUMMY_FEATURE)) {
+		dev_err(&ar->udev->dev, "invalid firmware descriptor "
+			"format detected.\n");
+		return -EINVAL;
+	}
+
+	ar->fw.api_version = otus_desc->api_ver;
+
+	if (ar->fw.api_version < CARL9170FW_API_MIN_VER ||
+	    ar->fw.api_version > CARL9170FW_API_MAX_VER) {
+		dev_err(&ar->udev->dev, "unsupported firmware api version.\n");
+		return -EINVAL;
+	}
+
+	if (!SUPP(CARL9170FW_COMMAND_PHY) || SUPP(CARL9170FW_UNUSABLE) ||
+	    !SUPP(CARL9170FW_HANDLE_BACK_REQ)) {
+		dev_err(&ar->udev->dev, "firmware does support "
+			"mandatory features.\n");
+		return -ECANCELED;
+	}
+
+	if (ilog2(le32_to_cpu(otus_desc->feature_set)) >=
+		__CARL9170FW_FEATURE_NUM) {
+		dev_warn(&ar->udev->dev, "driver does not support all "
+			 "firmware features.\n");
+	}
+
+	if (!SUPP(CARL9170FW_COMMAND_CAM)) {
+		dev_info(&ar->udev->dev, "crypto offloading is disabled "
+			 "by firmware.\n");
+		ar->disable_offload = true;
+	}
+
+	if (SUPP(CARL9170FW_PSM))
+		ar->hw->flags |= IEEE80211_HW_SUPPORTS_PS;
+
+	if (!SUPP(CARL9170FW_USB_INIT_FIRMWARE)) {
+		dev_err(&ar->udev->dev, "firmware does not provide "
+			"mandatory interfaces.\n");
+		return -EINVAL;
+	}
+
+	if (SUPP(CARL9170FW_MINIBOOT))
+		ar->fw.offset = le16_to_cpu(otus_desc->miniboot_size);
+	else
+		ar->fw.offset = 0;
+
+	if (SUPP(CARL9170FW_USB_DOWN_STREAM)) {
+		ar->hw->extra_tx_headroom += sizeof(struct ar9170_stream);
+		ar->fw.tx_stream = true;
+	}
+
+	if (SUPP(CARL9170FW_USB_UP_STREAM))
+		ar->fw.rx_stream = true;
+
+	ar->fw.vif_num = otus_desc->vif_num;
+	ar->fw.cmd_bufs = otus_desc->cmd_bufs;
+	ar->fw.address = le32_to_cpu(otus_desc->fw_address);
+	ar->fw.rx_size = le16_to_cpu(otus_desc->rx_max_frame_len);
+	ar->fw.mem_blocks = min_t(unsigned int, otus_desc->tx_descs, 0xfe);
+	atomic_set(&ar->mem_free_blocks, ar->fw.mem_blocks);
+	ar->fw.mem_block_size = le16_to_cpu(otus_desc->tx_frag_len);
+
+	if (ar->fw.vif_num >= AR9170_MAX_VIRTUAL_MAC || !ar->fw.vif_num ||
+	    ar->fw.mem_blocks < 16 || !ar->fw.cmd_bufs ||
+	    ar->fw.mem_block_size < 64 || ar->fw.mem_block_size > 512 ||
+	    ar->fw.rx_size > 32768 || ar->fw.rx_size < 4096 ||
+	    !valid_cpu_addr(ar->fw.address)) {
+		dev_err(&ar->udev->dev, "firmware shows obvious signs of "
+			"malicious tampering.\n");
+		return -EINVAL;
+	}
+
+	ar->fw.beacon_addr = le32_to_cpu(otus_desc->bcn_addr);
+	ar->fw.beacon_max_len = le16_to_cpu(otus_desc->bcn_len);
+
+	if (valid_dma_addr(ar->fw.beacon_addr) && ar->fw.beacon_max_len >=
+	    AR9170_MAC_BCN_LENGTH_MAX) {
+		ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC);
+
+		if (SUPP(CARL9170FW_WLANTX_CAB)) {
+			ar->hw->wiphy->interface_modes |=
+				BIT(NL80211_IFTYPE_AP);
+		}
+	}
+
+#undef SUPPORTED
+	return 0;
+}
+
+static struct carl9170fw_desc_head *
+carl9170_find_fw_desc(struct ar9170 *ar, const __u8 *fw_data, const size_t len)
+
+{
+	int scan = 0, found = 0;
+
+	if (!carl9170fw_size_check(len)) {
+		dev_err(&ar->udev->dev, "firmware size is out of bound.\n");
+		return NULL;
+	}
+
+	while (scan < len - sizeof(struct carl9170fw_desc_head)) {
+		if (fw_data[scan++] == otus_magic[found])
+			found++;
+		else
+			found = 0;
+
+		if (scan >= len)
+			break;
+
+		if (found == sizeof(otus_magic))
+			break;
+	}
+
+	if (found != sizeof(otus_magic))
+		return NULL;
+
+	return (void *)&fw_data[scan - found];
+}
+
+int carl9170_fw_fix_eeprom(struct ar9170 *ar)
+{
+	const struct carl9170fw_fix_desc *fix_desc = NULL;
+	unsigned int i, n, off;
+	u32 *data = (void *)&ar->eeprom;
+
+	fix_desc = carl9170_fw_find_desc(ar, FIX_MAGIC,
+		sizeof(*fix_desc), CARL9170FW_FIX_DESC_CUR_VER);
+
+	if (!fix_desc)
+		return 0;
+
+	n = (le16_to_cpu(fix_desc->head.length) - sizeof(*fix_desc)) /
+	    sizeof(struct carl9170fw_fix_entry);
+
+	for (i = 0; i < n; i++) {
+		off = le32_to_cpu(fix_desc->data[i].address) -
+		      AR9170_EEPROM_START;
+
+		if (off >= sizeof(struct ar9170_eeprom) || (off & 3)) {
+			dev_err(&ar->udev->dev, "Skip invalid entry %d\n", i);
+			continue;
+		}
+
+		data[off / sizeof(*data)] &=
+			le32_to_cpu(fix_desc->data[i].mask);
+		data[off / sizeof(*data)] |=
+			le32_to_cpu(fix_desc->data[i].value);
+	}
+
+	return 0;
+}
+
+int carl9170_parse_firmware(struct ar9170 *ar)
+{
+	const struct carl9170fw_desc_head *fw_desc = NULL;
+	const struct firmware *fw = ar->fw.fw;
+	unsigned long header_offset = 0;
+	int err;
+
+	if (WARN_ON(!fw))
+		return -EINVAL;
+
+	fw_desc = carl9170_find_fw_desc(ar, fw->data, fw->size);
+
+	if (!fw_desc) {
+		dev_err(&ar->udev->dev, "unsupported firmware.\n");
+		return -ENODATA;
+	}
+
+	header_offset = (unsigned long)fw_desc - (unsigned long)fw->data;
+
+	err = carl9170_fw_verify_descs(ar, fw_desc, fw->size - header_offset);
+	if (err) {
+		dev_err(&ar->udev->dev, "damaged firmware (%d).\n", err);
+		return err;
+	}
+
+	ar->fw.desc = fw_desc;
+
+	carl9170_fw_info(ar);
+
+	err = carl9170_fw(ar, fw->data, fw->size);
+	if (err) {
+		dev_err(&ar->udev->dev, "failed to parse firmware (%d).\n",
+			err);
+		return err;
+	}
+
+	return 0;
+}
diff --git a/drivers/net/wireless/ath/carl9170/fwcmd.h b/drivers/net/wireless/ath/carl9170/fwcmd.h
new file mode 100644
index 0000000..d4a4e1d
--- /dev/null
+++ b/drivers/net/wireless/ath/carl9170/fwcmd.h
@@ -0,0 +1,268 @@
+/*
+ * Shared Atheros AR9170 Header
+ *
+ * Firmware command interface definitions
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * This program is free software; you can redistribute it 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.
+ *
+ * This program 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or distribute this software for any
+ *    purpose with or without fee is hereby granted, provided that the above
+ *    copyright notice and this permission notice appear in all copies.
+ *
+ *    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __CARL9170_SHARED_FWCMD_H
+#define __CARL9170_SHARED_FWCMD_H
+
+#define	CARL9170_MAX_CMD_LEN		64
+#define	CARL9170_MAX_CMD_PAYLOAD_LEN	60
+
+#define CARL9170FW_API_MIN_VER		1
+#define CARL9170FW_API_MAX_VER		1
+
+enum carl9170_cmd_oids {
+	CARL9170_CMD_RREG		= 0x00,
+	CARL9170_CMD_WREG		= 0x01,
+	CARL9170_CMD_ECHO		= 0x02,
+	CARL9170_CMD_SWRST		= 0x03,
+	CARL9170_CMD_REBOOT		= 0x04,
+	CARL9170_CMD_BCN_CTRL		= 0x05,
+	CARL9170_CMD_READ_TSF		= 0x06,
+
+	/* CAM */
+	CARL9170_CMD_EKEY		= 0x10,
+	CARL9170_CMD_DKEY		= 0x11,
+
+	/* RF / PHY */
+	CARL9170_CMD_FREQUENCY		= 0x20,
+	CARL9170_CMD_RF_INIT		= 0x21,
+	CARL9170_CMD_SYNTH		= 0x22,
+	CARL9170_CMD_FREQ_START		= 0x23,
+	CARL9170_CMD_PSM		= 0x24,
+
+	/* Asychronous command flag */
+	CARL9170_CMD_ASYNC_FLAG		= 0x40,
+	CARL9170_CMD_WREG_ASYNC		= (CARL9170_CMD_WREG |
+					   CARL9170_CMD_ASYNC_FLAG),
+	CARL9170_CMD_REBOOT_ASYNC	= (CARL9170_CMD_REBOOT |
+					   CARL9170_CMD_ASYNC_FLAG),
+	CARL9170_CMD_BCN_CTRL_ASYNC	= (CARL9170_CMD_BCN_CTRL |
+					   CARL9170_CMD_ASYNC_FLAG),
+	CARL9170_CMD_PSM_ASYNC		= (CARL9170_CMD_PSM |
+					   CARL9170_CMD_ASYNC_FLAG),
+
+	/* responses and traps */
+	CARL9170_RSP_FLAG		= 0xc0,
+	CARL9170_RSP_PRETBTT		= 0xc0,
+	CARL9170_RSP_TXCOMP		= 0xc1,
+	CARL9170_RSP_BEACON_CONFIG	= 0xc2,
+	CARL9170_RSP_ATIM		= 0xc3,
+	CARL9170_RSP_WATCHDOG		= 0xc6,
+	CARL9170_RSP_TEXT		= 0xca,
+	CARL9170_RSP_HEXDUMP		= 0xcc,
+	CARL9170_RSP_RADAR		= 0xcd,
+	CARL9170_RSP_GPIO		= 0xce,
+	CARL9170_RSP_BOOT		= 0xcf,
+};
+
+struct carl9170_set_key_cmd {
+	__le16		user;
+	__le16		keyId;
+	__le16		type;
+	u8		macAddr[6];
+	u32		key[4];
+} __packed;
+#define CARL9170_SET_KEY_CMD_SIZE		28
+
+struct carl9170_disable_key_cmd {
+	__le16		user;
+	__le16		padding;
+} __packed;
+#define CARL9170_DISABLE_KEY_CMD_SIZE		4
+
+struct carl9170_u32_list {
+	u32	vals[0];
+} __packed;
+
+struct carl9170_reg_list {
+	__le32		regs[0];
+} __packed;
+
+struct carl9170_write_reg {
+	struct {
+		__le32		addr;
+		__le32		val;
+	} regs[0] __packed;
+} __packed;
+
+#define	CARL9170FW_PHY_HT_ENABLE		0x4
+#define	CARL9170FW_PHY_HT_DYN2040		0x8
+#define	CARL9170FW_PHY_HT_EXT_CHAN_OFF		0x3
+#define	CARL9170FW_PHY_HT_EXT_CHAN_OFF_S	2
+
+struct carl9170_rf_init {
+	__le32		freq;
+	u8		ht_settings;
+	u8		padding2[3];
+	__le32		delta_slope_coeff_exp;
+	__le32		delta_slope_coeff_man;
+	__le32		delta_slope_coeff_exp_shgi;
+	__le32		delta_slope_coeff_man_shgi;
+	__le32		finiteLoopCount;
+} __packed;
+#define CARL9170_RF_INIT_SIZE		28
+
+struct carl9170_rf_init_result {
+	__le32		ret;		/* AR9170_PHY_REG_AGC_CONTROL */
+} __packed;
+#define	CARL9170_RF_INIT_RESULT_SIZE	4
+
+#define	CARL9170_PSM_SLEEP		0x1000
+#define	CARL9170_PSM_SOFTWARE		0
+#define	CARL9170_PSM_WAKE		0 /* internally used. */
+#define	CARL9170_PSM_COUNTER		0xfff
+#define	CARL9170_PSM_COUNTER_S		0
+
+struct carl9170_psm {
+	__le32		state;
+} __packed;
+#define CARL9170_PSM_SIZE		4
+
+struct carl9170_bcn_ctrl_cmd {
+	__le32		vif_id;
+	__le32		mode;
+	__le32		bcn_addr;
+	__le32		bcn_len;
+} __packed;
+#define CARL9170_BCN_CTRL_CMD_SIZE	16
+
+#define CARL9170_BCN_CTRL_DRAIN	0
+#define CARL9170_BCN_CTRL_CAB_TRIGGER	1
+
+struct carl9170_cmd_head {
+	union {
+		struct {
+			u8	len;
+			u8	cmd;
+			u8	seq;
+			u8	ext;
+		} __packed;
+
+		u32 hdr_data;
+	} __packed;
+} __packed;
+
+struct carl9170_cmd {
+	struct carl9170_cmd_head hdr;
+	union {
+		struct carl9170_set_key_cmd	setkey;
+		struct carl9170_disable_key_cmd	disablekey;
+		struct carl9170_u32_list	echo;
+		struct carl9170_reg_list	rreg;
+		struct carl9170_write_reg	wreg;
+		struct carl9170_rf_init		rf_init;
+		struct carl9170_psm		psm;
+		struct carl9170_bcn_ctrl_cmd	bcn_ctrl;
+		u8 data[CARL9170_MAX_CMD_PAYLOAD_LEN];
+	} __packed;
+} __packed;
+
+#define	CARL9170_TX_STATUS_QUEUE	3
+#define	CARL9170_TX_STATUS_QUEUE_S	0
+#define	CARL9170_TX_STATUS_RIX_S	2
+#define	CARL9170_TX_STATUS_RIX		(3 << CARL9170_TX_STATUS_RIX_S)
+#define	CARL9170_TX_STATUS_TRIES_S	4
+#define	CARL9170_TX_STATUS_TRIES	(7 << CARL9170_TX_STATUS_TRIES_S)
+#define	CARL9170_TX_STATUS_SUCCESS	0x80
+
+/*
+ * NOTE:
+ * Both structs [carl9170_tx_status and _carl9170_tx_status]
+ * need to be "bit for bit" in sync.
+ */
+struct carl9170_tx_status {
+	/*
+	 * Beware of compiler bugs in all gcc pre 4.4!
+	 */
+
+	u8 cookie;
+	u8 queue:2;
+	u8 rix:2;
+	u8 tries:3;
+	u8 success:1;
+} __packed;
+struct _carl9170_tx_status {
+	/*
+	 * This version should be immune to all alignment bugs.
+	 */
+
+	u8 cookie;
+	u8 info;
+} __packed;
+#define CARL9170_TX_STATUS_SIZE		2
+
+#define	CARL9170_RSP_TX_STATUS_NUM	(CARL9170_MAX_CMD_PAYLOAD_LEN /	\
+					 sizeof(struct _carl9170_tx_status))
+
+#define	CARL9170_TX_MAX_RATE_TRIES	7
+
+#define	CARL9170_TX_MAX_RATES		4
+#define	CARL9170_TX_MAX_RETRY_RATES	(CARL9170_TX_MAX_RATES - 1)
+#define	CARL9170_ERR_MAGIC		"ERR:"
+#define	CARL9170_BUG_MAGIC		"BUG:"
+
+struct carl9170_gpio {
+	__le32 gpio;
+} __packed;
+#define CARL9170_GPIO_SIZE		4
+
+struct carl9170_tsf_rsp {
+	union {
+		__le32 tsf[2];
+		__le64 tsf_64;
+	} __packed;
+} __packed;
+#define CARL9170_TSF_RSP_SIZE		8
+
+struct carl9170_rsp {
+	struct carl9170_cmd_head hdr;
+
+	union {
+		struct carl9170_rf_init_result	rf_init_res;
+		struct carl9170_u32_list	rreg_res;
+		struct carl9170_u32_list	echo;
+		struct carl9170_tx_status	tx_status[0];
+		struct _carl9170_tx_status	_tx_status[0];
+		struct carl9170_gpio		gpio;
+		struct carl9170_tsf_rsp		tsf;
+		struct carl9170_psm		psm;
+		u8 data[CARL9170_MAX_CMD_PAYLOAD_LEN];
+	} __packed;
+} __packed;
+
+#endif /* __CARL9170_SHARED_FWCMD_H */
diff --git a/drivers/net/wireless/ath/carl9170/fwdesc.h b/drivers/net/wireless/ath/carl9170/fwdesc.h
new file mode 100644
index 0000000..7cd8117
--- /dev/null
+++ b/drivers/net/wireless/ath/carl9170/fwdesc.h
@@ -0,0 +1,237 @@
+/*
+ * Shared CARL9170 Header
+ *
+ * Firmware descriptor format
+ *
+ * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * This program is free software; you can redistribute it 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.
+ *
+ * This program 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ */
+
+#ifndef __CARL9170_SHARED_FWDESC_H
+#define __CARL9170_SHARED_FWDESC_H
+
+/* NOTE: Don't mess with the order of the flags! */
+enum carl9170fw_feature_list {
+	/* Always set */
+	CARL9170FW_DUMMY_FEATURE,
+
+	/*
+	 * Indicates that this image has special boot block which prevents
+	 * legacy drivers to drive the firmware.
+	 */
+	CARL9170FW_MINIBOOT,
+
+	/* usb registers are initialized by the firmware */
+	CARL9170FW_USB_INIT_FIRMWARE,
+
+	/* command traps & notifications are send through EP2 */
+	CARL9170FW_USB_RESP_EP2,
+
+	/* usb download (app -> fw) stream */
+	CARL9170FW_USB_DOWN_STREAM,
+
+	/* usb upload (fw -> app) stream */
+	CARL9170FW_USB_UP_STREAM,
+
+	/* unusable - reserved to flag non-functional debug firmwares */
+	CARL9170FW_UNUSABLE,
+
+	/* AR9170_CMD_RF_INIT, AR9170_CMD_FREQ_START, AR9170_CMD_FREQUENCY */
+	CARL9170FW_COMMAND_PHY,
+
+	/* AR9170_CMD_EKEY, AR9170_CMD_DKEY */
+	CARL9170FW_COMMAND_CAM,
+
+	/* Firmware has a software Content After Beacon Queueing mechanism */
+	CARL9170FW_WLANTX_CAB,
+
+	/* The firmware is capable of responding to incoming BAR frames */
+	CARL9170FW_HANDLE_BACK_REQ,
+
+	/* GPIO Interrupt | CARL9170_RSP_GPIO */
+	CARL9170FW_GPIO_INTERRUPT,
+
+	/* Firmware PSM support | CARL9170_CMD_PSM */
+	CARL9170FW_PSM,
+
+	/* KEEP LAST */
+	__CARL9170FW_FEATURE_NUM
+};
+
+#define OTUS_MAGIC	"OTAR"
+#define MOTD_MAGIC	"MOTD"
+#define FIX_MAGIC	"FIX\0"
+#define DBG_MAGIC	"DBG\0"
+#define CHK_MAGIC	"CHK\0"
+#define LAST_MAGIC	"LAST"
+
+#define CARL9170FW_SET_DAY(d) (((d) - 1) % 31)
+#define CARL9170FW_SET_MONTH(m) ((((m) - 1) % 12) * 31)
+#define CARL9170FW_SET_YEAR(y) (((y) - 10) * 372)
+
+#define CARL9170FW_GET_DAY(d) (((d) % 31) + 1)
+#define CARL9170FW_GET_MONTH(m) ((((m) / 31) % 12) + 1)
+#define CARL9170FW_GET_YEAR(y) ((y) / 372 + 10)
+
+struct carl9170fw_desc_head {
+	u8	magic[4];
+	__le16 length;
+	u8 min_ver;
+	u8 cur_ver;
+} __packed;
+#define CARL9170FW_DESC_HEAD_SIZE			\
+	(sizeof(struct carl9170fw_desc_head))
+
+#define CARL9170FW_OTUS_DESC_MIN_VER		6
+#define CARL9170FW_OTUS_DESC_CUR_VER		6
+struct carl9170fw_otus_desc {
+	struct carl9170fw_desc_head head;
+	__le32 feature_set;
+	__le32 fw_address;
+	__le32 bcn_addr;
+	__le16 bcn_len;
+	__le16 miniboot_size;
+	__le16 tx_frag_len;
+	__le16 rx_max_frame_len;
+	u8 tx_descs;
+	u8 cmd_bufs;
+	u8 api_ver;
+	u8 vif_num;
+} __packed;
+#define CARL9170FW_OTUS_DESC_SIZE			\
+	(sizeof(struct carl9170fw_otus_desc))
+
+#define CARL9170FW_MOTD_STRING_LEN			24
+#define CARL9170FW_MOTD_RELEASE_LEN			20
+#define CARL9170FW_MOTD_DESC_MIN_VER			1
+#define CARL9170FW_MOTD_DESC_CUR_VER			2
+struct carl9170fw_motd_desc {
+	struct carl9170fw_desc_head head;
+	__le32 fw_year_month_day;
+	char desc[CARL9170FW_MOTD_STRING_LEN];
+	char release[CARL9170FW_MOTD_RELEASE_LEN];
+} __packed;
+#define CARL9170FW_MOTD_DESC_SIZE			\
+	(sizeof(struct carl9170fw_motd_desc))
+
+#define CARL9170FW_FIX_DESC_MIN_VER			1
+#define CARL9170FW_FIX_DESC_CUR_VER			2
+struct carl9170fw_fix_entry {
+	__le32 address;
+	__le32 mask;
+	__le32 value;
+} __packed;
+
+struct carl9170fw_fix_desc {
+	struct carl9170fw_desc_head head;
+	struct carl9170fw_fix_entry data[0];
+} __packed;
+#define CARL9170FW_FIX_DESC_SIZE			\
+	(sizeof(struct carl9170fw_fix_desc))
+
+#define CARL9170FW_DBG_DESC_MIN_VER			1
+#define CARL9170FW_DBG_DESC_CUR_VER			2
+struct carl9170fw_dbg_desc {
+	struct carl9170fw_desc_head head;
+
+	__le32 bogoclock_addr;
+	__le32 counter_addr;
+	__le32 rx_total_addr;
+	__le32 rx_overrun_addr;
+
+	/* Put your debugging definitions here */
+} __packed;
+#define CARL9170FW_DBG_DESC_SIZE			\
+	(sizeof(struct carl9170fw_dbg_desc))
+
+#define CARL9170FW_CHK_DESC_MIN_VER			1
+#define CARL9170FW_CHK_DESC_CUR_VER			2
+struct carl9170fw_chk_desc {
+	struct carl9170fw_desc_head head;
+	__le32 fw_crc32;
+	__le32 hdr_crc32;
+} __packed;
+#define CARL9170FW_CHK_DESC_SIZE			\
+	(sizeof(struct carl9170fw_chk_desc))
+
+#define CARL9170FW_LAST_DESC_MIN_VER			1
+#define CARL9170FW_LAST_DESC_CUR_VER			2
+struct carl9170fw_last_desc {
+	struct carl9170fw_desc_head head;
+} __packed;
+#define CARL9170FW_LAST_DESC_SIZE			\
+	(sizeof(struct carl9170fw_fix_desc))
+
+#define CARL9170FW_DESC_MAX_LENGTH			8192
+
+#define CARL9170FW_FILL_DESC(_magic, _length, _min_ver, _cur_ver)	\
+	.head = {							\
+		.magic = _magic,					\
+		.length = cpu_to_le16(_length),				\
+		.min_ver = _min_ver,					\
+		.cur_ver = _cur_ver,					\
+	}
+
+static inline void carl9170fw_fill_desc(struct carl9170fw_desc_head *head,
+					 u8 magic[4], __le16 length,
+					 u8 min_ver, u8 cur_ver)
+{
+	head->magic[0] = magic[0];
+	head->magic[1] = magic[1];
+	head->magic[2] = magic[2];
+	head->magic[3] = magic[3];
+
+	head->length = length;
+	head->min_ver = min_ver;
+	head->cur_ver = cur_ver;
+}
+
+#define carl9170fw_for_each_hdr(desc, fw_desc)				\
+	for (desc = fw_desc;						\
+	     memcmp(desc->magic, LAST_MAGIC, 4) &&			\
+	     le16_to_cpu(desc->length) >= CARL9170FW_DESC_HEAD_SIZE &&	\
+	     le16_to_cpu(desc->length) < CARL9170FW_DESC_MAX_LENGTH;	\
+	     desc = (void *)((unsigned long)desc + le16_to_cpu(desc->length)))
+
+#define CHECK_HDR_VERSION(head, _min_ver)				\
+	(((head)->cur_ver < _min_ver) || ((head)->min_ver > _min_ver))	\
+
+static inline bool carl9170fw_supports(__le32 list, u8 feature)
+{
+	return le32_to_cpu(list) & BIT(feature);
+}
+
+static inline bool carl9170fw_desc_cmp(const struct carl9170fw_desc_head *head,
+				       const u8 descid[4], u16 min_len,
+				       u8 compatible_revision)
+{
+	if (descid[0] == head->magic[0] && descid[1] == head->magic[1] &&
+	    descid[2] == head->magic[2] && descid[3] == head->magic[3] &&
+	    !CHECK_HDR_VERSION(head, compatible_revision) &&
+	    (le16_to_cpu(head->length) >= min_len))
+		return true;
+
+	return false;
+}
+
+#define CARL9170FW_MIN_SIZE	32
+#define CARL9170FW_MAX_SIZE	16384
+
+static inline bool carl9170fw_size_check(unsigned int len)
+{
+	return (len <= CARL9170FW_MAX_SIZE && len >= CARL9170FW_MIN_SIZE);
+}
+
+#endif /* __CARL9170_SHARED_FWDESC_H */
diff --git a/drivers/net/wireless/ath/carl9170/hw.h b/drivers/net/wireless/ath/carl9170/hw.h
new file mode 100644
index 0000000..b1292ac
--- /dev/null
+++ b/drivers/net/wireless/ath/carl9170/hw.h
@@ -0,0 +1,736 @@
+/*
+ * Shared Atheros AR9170 Header
+ *
+ * Register map, hardware-specific definitions
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * This program is free software; you can redistribute it 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.
+ *
+ * This program 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or distribute this software for any
+ *    purpose with or without fee is hereby granted, provided that the above
+ *    copyright notice and this permission notice appear in all copies.
+ *
+ *    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __CARL9170_SHARED_HW_H
+#define __CARL9170_SHARED_HW_H
+
+/* High Speed UART */
+#define	AR9170_UART_REG_BASE			0x1c0000
+
+/* Definitions of interrupt registers */
+#define	AR9170_UART_REG_RX_BUFFER		(AR9170_UART_REG_BASE + 0x000)
+#define	AR9170_UART_REG_TX_HOLDING		(AR9170_UART_REG_BASE + 0x004)
+#define	AR9170_UART_REG_FIFO_CONTROL		(AR9170_UART_REG_BASE + 0x010)
+#define		AR9170_UART_FIFO_CTRL_RESET_RX_FIFO	0x02
+#define		AR9170_UART_FIFO_CTRL_RESET_TX_FIFO	0x04
+
+#define	AR9170_UART_REG_LINE_CONTROL		(AR9170_UART_REG_BASE + 0x014)
+#define	AR9170_UART_REG_MODEM_CONTROL		(AR9170_UART_REG_BASE + 0x018)
+#define		AR9170_UART_MODEM_CTRL_DTR_BIT		0x01
+#define		AR9170_UART_MODEM_CTRL_RTS_BIT		0x02
+#define		AR9170_UART_MODEM_CTRL_INTERNAL_LOOP_BACK	0x10
+#define		AR9170_UART_MODEM_CTRL_AUTO_RTS		0x20
+#define		AR9170_UART_MODEM_CTRL_AUTO_CTR		0x40
+
+#define	AR9170_UART_REG_LINE_STATUS		(AR9170_UART_REG_BASE + 0x01c)
+#define		AR9170_UART_LINE_STS_RX_DATA_READY	0x01
+#define		AR9170_UART_LINE_STS_RX_BUFFER_OVERRUN	0x02
+#define		AR9170_UART_LINE_STS_RX_BREAK_IND	0x10
+#define		AR9170_UART_LINE_STS_TX_FIFO_NEAR_EMPTY	0x20
+#define		AR9170_UART_LINE_STS_TRANSMITTER_EMPTY	0x40
+
+#define	AR9170_UART_REG_MODEM_STATUS		(AR9170_UART_REG_BASE + 0x020)
+#define		AR9170_UART_MODEM_STS_CTS_CHANGE	0x01
+#define		AR9170_UART_MODEM_STS_DSR_CHANGE	0x02
+#define		AR9170_UART_MODEM_STS_DCD_CHANGE	0x08
+#define		AR9170_UART_MODEM_STS_CTS_COMPL		0x10
+#define		AR9170_UART_MODEM_STS_DSR_COMPL		0x20
+#define		AR9170_UART_MODEM_STS_DCD_COMPL		0x80
+
+#define	AR9170_UART_REG_SCRATCH			(AR9170_UART_REG_BASE + 0x024)
+#define	AR9170_UART_REG_DIVISOR_LSB		(AR9170_UART_REG_BASE + 0x028)
+#define	AR9170_UART_REG_DIVISOR_MSB		(AR9170_UART_REG_BASE + 0x02c)
+#define	AR9170_UART_REG_WORD_RX_BUFFER		(AR9170_UART_REG_BASE + 0x034)
+#define	AR9170_UART_REG_WORD_TX_HOLDING		(AR9170_UART_REG_BASE + 0x038)
+#define	AR9170_UART_REG_FIFO_COUNT		(AR9170_UART_REG_BASE + 0x03c)
+#define	AR9170_UART_REG_REMAINDER		(AR9170_UART_REG_BASE + 0x04c)
+
+/* Timer */
+#define	AR9170_TIMER_REG_BASE			0x1c1000
+
+#define	AR9170_TIMER_REG_WATCH_DOG		(AR9170_TIMER_REG_BASE + 0x000)
+#define	AR9170_TIMER_REG_TIMER0			(AR9170_TIMER_REG_BASE + 0x010)
+#define	AR9170_TIMER_REG_TIMER1			(AR9170_TIMER_REG_BASE + 0x014)
+#define	AR9170_TIMER_REG_TIMER2			(AR9170_TIMER_REG_BASE + 0x018)
+#define	AR9170_TIMER_REG_TIMER3			(AR9170_TIMER_REG_BASE + 0x01c)
+#define	AR9170_TIMER_REG_TIMER4			(AR9170_TIMER_REG_BASE + 0x020)
+#define	AR9170_TIMER_REG_CONTROL		(AR9170_TIMER_REG_BASE + 0x024)
+#define		AR9170_TIMER_CTRL_DISABLE_CLOCK		0x100
+
+#define	AR9170_TIMER_REG_INTERRUPT		(AR9170_TIMER_REG_BASE + 0x028)
+#define		AR9170_TIMER_INT_TIMER0			0x001
+#define		AR9170_TIMER_INT_TIMER1			0x002
+#define		AR9170_TIMER_INT_TIMER2			0x004
+#define		AR9170_TIMER_INT_TIMER3			0x008
+#define		AR9170_TIMER_INT_TIMER4			0x010
+#define		AR9170_TIMER_INT_TICK_TIMER		0x100
+
+#define	AR9170_TIMER_REG_TICK_TIMER		(AR9170_TIMER_REG_BASE + 0x030)
+#define	AR9170_TIMER_REG_CLOCK_LOW		(AR9170_TIMER_REG_BASE + 0x040)
+#define	AR9170_TIMER_REG_CLOCK_HIGH		(AR9170_TIMER_REG_BASE + 0x044)
+
+#define	AR9170_MAC_REG_BASE			0x1c3000
+
+#define	AR9170_MAC_REG_POWER_STATE_CTRL		(AR9170_MAC_REG_BASE + 0x500)
+#define		AR9170_MAC_POWER_STATE_CTRL_RESET	0x20
+
+#define	AR9170_MAC_REG_MAC_POWER_STATE_CTRL	(AR9170_MAC_REG_BASE + 0x50c)
+
+#define	AR9170_MAC_REG_INT_CTRL			(AR9170_MAC_REG_BASE + 0x510)
+#define		AR9170_MAC_INT_TXC			BIT(0)
+#define		AR9170_MAC_INT_RXC			BIT(1)
+#define		AR9170_MAC_INT_RETRY_FAIL		BIT(2)
+#define		AR9170_MAC_INT_WAKEUP			BIT(3)
+#define		AR9170_MAC_INT_ATIM			BIT(4)
+#define		AR9170_MAC_INT_DTIM			BIT(5)
+#define		AR9170_MAC_INT_CFG_BCN			BIT(6)
+#define		AR9170_MAC_INT_ABORT			BIT(7)
+#define		AR9170_MAC_INT_QOS			BIT(8)
+#define		AR9170_MAC_INT_MIMO_PS			BIT(9)
+#define		AR9170_MAC_INT_KEY_GEN			BIT(10)
+#define		AR9170_MAC_INT_DECRY_NOUSER		BIT(11)
+#define		AR9170_MAC_INT_RADAR			BIT(12)
+#define		AR9170_MAC_INT_QUIET_FRAME		BIT(13)
+#define		AR9170_MAC_INT_PRETBTT			BIT(14)
+
+#define	AR9170_MAC_REG_TSF_L			(AR9170_MAC_REG_BASE + 0x514)
+#define	AR9170_MAC_REG_TSF_H			(AR9170_MAC_REG_BASE + 0x518)
+
+#define	AR9170_MAC_REG_ATIM_WINDOW		(AR9170_MAC_REG_BASE + 0x51c)
+#define		AR9170_MAC_ATIM_PERIOD_S		0
+#define		AR9170_MAC_ATIM_PERIOD			0x0000ffff
+
+#define	AR9170_MAC_REG_BCN_PERIOD		(AR9170_MAC_REG_BASE + 0x520)
+#define		AR9170_MAC_BCN_PERIOD_S			0
+#define		AR9170_MAC_BCN_PERIOD			0x0000ffff
+#define		AR9170_MAC_BCN_DTIM_S			16
+#define		AR9170_MAC_BCN_DTIM			0x00ff0000
+#define		AR9170_MAC_BCN_AP_MODE			BIT(24)
+#define		AR9170_MAC_BCN_IBSS_MODE		BIT(25)
+#define		AR9170_MAC_BCN_PWR_MGT			BIT(26)
+#define		AR9170_MAC_BCN_STA_PS			BIT(27)
+
+#define	AR9170_MAC_REG_PRETBTT			(AR9170_MAC_REG_BASE + 0x524)
+#define		AR9170_MAC_PRETBTT_S			0
+#define		AR9170_MAC_PRETBTT			0x0000ffff
+#define		AR9170_MAC_PRETBTT2_S			16
+#define		AR9170_MAC_PRETBTT2			0xffff0000
+
+#define	AR9170_MAC_REG_MAC_ADDR_L		(AR9170_MAC_REG_BASE + 0x610)
+#define	AR9170_MAC_REG_MAC_ADDR_H		(AR9170_MAC_REG_BASE + 0x614)
+#define	AR9170_MAC_REG_BSSID_L			(AR9170_MAC_REG_BASE + 0x618)
+#define	AR9170_MAC_REG_BSSID_H			(AR9170_MAC_REG_BASE + 0x61c)
+
+#define	AR9170_MAC_REG_GROUP_HASH_TBL_L		(AR9170_MAC_REG_BASE + 0x624)
+#define	AR9170_MAC_REG_GROUP_HASH_TBL_H		(AR9170_MAC_REG_BASE + 0x628)
+
+#define	AR9170_MAC_REG_RX_TIMEOUT		(AR9170_MAC_REG_BASE + 0x62c)
+
+#define	AR9170_MAC_REG_BASIC_RATE		(AR9170_MAC_REG_BASE + 0x630)
+#define	AR9170_MAC_REG_MANDATORY_RATE		(AR9170_MAC_REG_BASE + 0x634)
+#define	AR9170_MAC_REG_RTS_CTS_RATE		(AR9170_MAC_REG_BASE + 0x638)
+#define	AR9170_MAC_REG_BACKOFF_PROTECT		(AR9170_MAC_REG_BASE + 0x63c)
+#define	AR9170_MAC_REG_RX_THRESHOLD		(AR9170_MAC_REG_BASE + 0x640)
+#define	AR9170_MAC_REG_AFTER_PNP		(AR9170_MAC_REG_BASE + 0x648)
+#define	AR9170_MAC_REG_RX_PE_DELAY		(AR9170_MAC_REG_BASE + 0x64c)
+
+#define	AR9170_MAC_REG_DYNAMIC_SIFS_ACK		(AR9170_MAC_REG_BASE + 0x658)
+#define	AR9170_MAC_REG_SNIFFER			(AR9170_MAC_REG_BASE + 0x674)
+#define		AR9170_MAC_SNIFFER_ENABLE_PROMISC	BIT(0)
+#define		AR9170_MAC_SNIFFER_DEFAULTS		0x02000000
+#define	AR9170_MAC_REG_ENCRYPTION		(AR9170_MAC_REG_BASE + 0x678)
+#define		AR9170_MAC_ENCRYPTION_RX_SOFTWARE	BIT(3)
+#define		AR9170_MAC_ENCRYPTION_DEFAULTS		0x70
+
+#define	AR9170_MAC_REG_MISC_680			(AR9170_MAC_REG_BASE + 0x680)
+#define	AR9170_MAC_REG_MISC_684			(AR9170_MAC_REG_BASE + 0x684)
+#define	AR9170_MAC_REG_TX_UNDERRUN		(AR9170_MAC_REG_BASE + 0x688)
+
+#define	AR9170_MAC_REG_FRAMETYPE_FILTER		(AR9170_MAC_REG_BASE + 0x68c)
+#define		AR9170_MAC_FTF_ASSOC_REQ		BIT(0)
+#define		AR9170_MAC_FTF_ASSOC_RESP		BIT(1)
+#define		AR9170_MAC_FTF_REASSOC_REQ		BIT(2)
+#define		AR9170_MAC_FTF_REASSOC_RESP		BIT(3)
+#define		AR9170_MAC_FTF_PRB_REQ			BIT(4)
+#define		AR9170_MAC_FTF_PRB_RESP			BIT(5)
+#define		AR9170_MAC_FTF_BIT6			BIT(6)
+#define		AR9170_MAC_FTF_BIT7			BIT(7)
+#define		AR9170_MAC_FTF_BEACON			BIT(8)
+#define		AR9170_MAC_FTF_ATIM			BIT(9)
+#define		AR9170_MAC_FTF_DEASSOC			BIT(10)
+#define		AR9170_MAC_FTF_AUTH			BIT(11)
+#define		AR9170_MAC_FTF_DEAUTH			BIT(12)
+#define		AR9170_MAC_FTF_BIT13			BIT(13)
+#define		AR9170_MAC_FTF_BIT14			BIT(14)
+#define		AR9170_MAC_FTF_BIT15			BIT(15)
+#define		AR9170_MAC_FTF_BAR			BIT(24)
+#define		AR9170_MAC_FTF_BA			BIT(25)
+#define		AR9170_MAC_FTF_PSPOLL			BIT(26)
+#define		AR9170_MAC_FTF_RTS			BIT(27)
+#define		AR9170_MAC_FTF_CTS			BIT(28)
+#define		AR9170_MAC_FTF_ACK			BIT(29)
+#define		AR9170_MAC_FTF_CFE			BIT(30)
+#define		AR9170_MAC_FTF_CFE_ACK			BIT(31)
+#define		AR9170_MAC_FTF_DEFAULTS			0x0500ffff
+#define		AR9170_MAC_FTF_MONITOR			0xff00ffff
+
+#define	AR9170_MAC_REG_ACK_EXTENSION		(AR9170_MAC_REG_BASE + 0x690)
+#define	AR9170_MAC_REG_ACK_TPC			(AR9170_MAC_REG_BASE + 0x694)
+#define	AR9170_MAC_REG_EIFS_AND_SIFS		(AR9170_MAC_REG_BASE + 0x698)
+#define	AR9170_MAC_REG_RX_TIMEOUT_COUNT		(AR9170_MAC_REG_BASE + 0x69c)
+#define	AR9170_MAC_REG_RX_TOTAL			(AR9170_MAC_REG_BASE + 0x6a0)
+#define	AR9170_MAC_REG_RX_CRC32			(AR9170_MAC_REG_BASE + 0x6a4)
+#define	AR9170_MAC_REG_RX_CRC16			(AR9170_MAC_REG_BASE + 0x6a8)
+#define	AR9170_MAC_REG_RX_ERR_DECRYPTION_UNI	(AR9170_MAC_REG_BASE + 0x6ac)
+#define	AR9170_MAC_REG_RX_OVERRUN		(AR9170_MAC_REG_BASE + 0x6b0)
+#define	AR9170_MAC_REG_RX_ERR_DECRYPTION_MUL	(AR9170_MAC_REG_BASE + 0x6bc)
+#define AR9170_MAC_REG_TX_BLOCKACKS		(AR9170_MAC_REG_BASE + 0x6c0)
+#define AR9170_MAC_REG_NAV_COUNT		(AR9170_MAC_REG_BASE + 0x6c4)
+#define AR9170_MAC_REG_BACKOFF_STATUS		(AR9170_MAC_REG_BASE + 0x6c8)
+#define	AR9170_MAC_REG_TX_RETRY			(AR9170_MAC_REG_BASE + 0x6cc)
+
+#define AR9170_MAC_REG_TX_COMPLETE		(AR9170_MAC_REG_BASE + 0x6d4)
+
+#define	AR9170_MAC_REG_CHANNEL_BUSY		(AR9170_MAC_REG_BASE + 0x6e8)
+#define	AR9170_MAC_REG_EXT_BUSY			(AR9170_MAC_REG_BASE + 0x6ec)
+
+#define	AR9170_MAC_REG_SLOT_TIME		(AR9170_MAC_REG_BASE + 0x6f0)
+#define	AR9170_MAC_REG_TX_TOTAL			(AR9170_MAC_REG_BASE + 0x6f4)
+#define AR9170_MAC_REG_ACK_FC			(AR9170_MAC_REG_BASE + 0x6f8)
+
+#define	AR9170_MAC_REG_CAM_MODE			(AR9170_MAC_REG_BASE + 0x700)
+#define		AR9170_MAC_CAM_IBSS			0xe0
+#define		AR9170_MAC_CAM_AP			0xa1
+#define		AR9170_MAC_CAM_STA			0x2
+#define		AR9170_MAC_CAM_AP_WDS			0x3
+#define		AR9170_MAC_CAM_DEFAULTS			(0xf << 24)
+#define		AR9170_MAC_CAM_HOST_PENDING		0x80000000
+
+#define	AR9170_MAC_REG_CAM_ROLL_CALL_TBL_L	(AR9170_MAC_REG_BASE + 0x704)
+#define	AR9170_MAC_REG_CAM_ROLL_CALL_TBL_H	(AR9170_MAC_REG_BASE + 0x708)
+
+#define	AR9170_MAC_REG_CAM_ADDR			(AR9170_MAC_REG_BASE + 0x70c)
+#define		AR9170_MAC_CAM_ADDR_WRITE		0x80000000
+#define	AR9170_MAC_REG_CAM_DATA0		(AR9170_MAC_REG_BASE + 0x720)
+#define	AR9170_MAC_REG_CAM_DATA1		(AR9170_MAC_REG_BASE + 0x724)
+#define	AR9170_MAC_REG_CAM_DATA2		(AR9170_MAC_REG_BASE + 0x728)
+#define	AR9170_MAC_REG_CAM_DATA3		(AR9170_MAC_REG_BASE + 0x72c)
+
+#define	AR9170_MAC_REG_CAM_DBG0			(AR9170_MAC_REG_BASE + 0x730)
+#define	AR9170_MAC_REG_CAM_DBG1			(AR9170_MAC_REG_BASE + 0x734)
+#define	AR9170_MAC_REG_CAM_DBG2			(AR9170_MAC_REG_BASE + 0x738)
+#define	AR9170_MAC_REG_CAM_STATE		(AR9170_MAC_REG_BASE + 0x73c)
+#define		AR9170_MAC_CAM_STATE_READ_PENDING	0x40000000
+#define		AR9170_MAC_CAM_STATE_WRITE_PENDING	0x80000000
+
+#define	AR9170_MAC_REG_CAM_TXKEY		(AR9170_MAC_REG_BASE + 0x740)
+#define	AR9170_MAC_REG_CAM_RXKEY		(AR9170_MAC_REG_BASE + 0x750)
+
+#define	AR9170_MAC_REG_CAM_TX_ENC_TYPE		(AR9170_MAC_REG_BASE + 0x760)
+#define	AR9170_MAC_REG_CAM_RX_ENC_TYPE		(AR9170_MAC_REG_BASE + 0x770)
+#define	AR9170_MAC_REG_CAM_TX_SERACH_HIT	(AR9170_MAC_REG_BASE + 0x780)
+#define	AR9170_MAC_REG_CAM_RX_SERACH_HIT	(AR9170_MAC_REG_BASE + 0x790)
+
+#define	AR9170_MAC_REG_AC0_CW			(AR9170_MAC_REG_BASE + 0xb00)
+#define	AR9170_MAC_REG_AC1_CW			(AR9170_MAC_REG_BASE + 0xb04)
+#define	AR9170_MAC_REG_AC2_CW			(AR9170_MAC_REG_BASE + 0xb08)
+#define	AR9170_MAC_REG_AC3_CW			(AR9170_MAC_REG_BASE + 0xb0c)
+#define	AR9170_MAC_REG_AC4_CW			(AR9170_MAC_REG_BASE + 0xb10)
+#define	AR9170_MAC_REG_AC2_AC1_AC0_AIFS		(AR9170_MAC_REG_BASE + 0xb14)
+#define	AR9170_MAC_REG_AC4_AC3_AC2_AIFS		(AR9170_MAC_REG_BASE + 0xb18)
+#define AR9170_MAC_REG_TXOP_ACK_EXTENSION	(AR9170_MAC_REG_BASE + 0xb1c)
+#define AR9170_MAC_REG_TXOP_ACK_INTERVAL	(AR9170_MAC_REG_BASE + 0xb20)
+#define AR9170_MAC_REG_CONTENTION_POINT		(AR9170_MAC_REG_BASE + 0xb24)
+#define	AR9170_MAC_REG_RETRY_MAX		(AR9170_MAC_REG_BASE + 0xb28)
+#define AR9170_MAC_REG_TID_CFACK_CFEND_RATE	(AR9170_MAC_REG_BASE + 0xb2c)
+#define	AR9170_MAC_REG_TXOP_NOT_ENOUGH_IND	(AR9170_MAC_REG_BASE + 0xb30)
+#define AR9170_MAC_REG_TKIP_TSC			(AR9170_MAC_REG_BASE + 0xb34)
+#define AR9170_MAC_REG_TXOP_DURATION		(AR9170_MAC_REG_BASE + 0xb38)
+#define AR9170_MAC_REG_TX_QOS_THRESHOLD		(AR9170_MAC_REG_BASE + 0xb3c)
+#define	AR9170_MAC_REG_QOS_PRIORITY_VIRTUAL_CCA	(AR9170_MAC_REG_BASE + 0xb40)
+#define		AR9170_MAC_VIRTUAL_CCA_Q0		BIT(15)
+#define		AR9170_MAC_VIRTUAL_CCA_Q1		BIT(16)
+#define		AR9170_MAC_VIRTUAL_CCA_Q2		BIT(17)
+#define		AR9170_MAC_VIRTUAL_CCA_Q3		BIT(18)
+#define		AR9170_MAC_VIRTUAL_CCA_Q4		BIT(19)
+#define		AR9170_MAC_VIRTUAL_CCA_ALL		(0xf8000)
+
+#define	AR9170_MAC_REG_AC1_AC0_TXOP		(AR9170_MAC_REG_BASE + 0xb44)
+#define	AR9170_MAC_REG_AC3_AC2_TXOP		(AR9170_MAC_REG_BASE + 0xb48)
+
+#define	AR9170_MAC_REG_AMPDU_COUNT		(AR9170_MAC_REG_BASE + 0xb88)
+#define	AR9170_MAC_REG_MPDU_COUNT		(AR9170_MAC_REG_BASE + 0xb8c)
+
+#define	AR9170_MAC_REG_AMPDU_FACTOR		(AR9170_MAC_REG_BASE + 0xb9c)
+#define		AR9170_MAC_AMPDU_FACTOR			0x7f0000
+#define		AR9170_MAC_AMPDU_FACTOR_S		16
+#define	AR9170_MAC_REG_AMPDU_DENSITY		(AR9170_MAC_REG_BASE + 0xba0)
+#define		AR9170_MAC_AMPDU_DENSITY		0x7
+#define		AR9170_MAC_AMPDU_DENSITY_S		0
+
+#define	AR9170_MAC_REG_FCS_SELECT		(AR9170_MAC_REG_BASE + 0xbb0)
+#define		AR9170_MAC_FCS_SWFCS			0x1
+#define		AR9170_MAC_FCS_FIFO_PROT		0x4
+
+#define	AR9170_MAC_REG_RTS_CTS_TPC		(AR9170_MAC_REG_BASE + 0xbb4)
+#define AR9170_MAC_REG_CFEND_QOSNULL_TPC	(AR9170_MAC_REG_BASE + 0xbb8)
+
+#define	AR9170_MAC_REG_ACK_TABLE		(AR9170_MAC_REG_BASE + 0xc00)
+#define AR9170_MAC_REG_RX_CONTROL		(AR9170_MAC_REG_BASE + 0xc40)
+#define		AR9170_MAC_RX_CTRL_DEAGG		0x1
+#define		AR9170_MAC_RX_CTRL_SHORT_FILTER		0x2
+#define		AR9170_MAC_RX_CTRL_SA_DA_SEARCH		0x20
+#define		AR9170_MAC_RX_CTRL_PASS_TO_HOST		BIT(28)
+#define		AR9170_MAC_RX_CTRL_ACK_IN_SNIFFER	BIT(30)
+
+#define AR9170_MAC_REG_RX_CONTROL_1		(AR9170_MAC_REG_BASE + 0xc44)
+
+#define	AR9170_MAC_REG_AMPDU_RX_THRESH		(AR9170_MAC_REG_BASE + 0xc50)
+
+#define	AR9170_MAC_REG_RX_MPDU			(AR9170_MAC_REG_BASE + 0xca0)
+#define	AR9170_MAC_REG_RX_DROPPED_MPDU		(AR9170_MAC_REG_BASE + 0xca4)
+#define	AR9170_MAC_REG_RX_DEL_MPDU		(AR9170_MAC_REG_BASE + 0xca8)
+#define	AR9170_MAC_REG_RX_PHY_MISC_ERROR	(AR9170_MAC_REG_BASE + 0xcac)
+#define	AR9170_MAC_REG_RX_PHY_XR_ERROR		(AR9170_MAC_REG_BASE + 0xcb0)
+#define	AR9170_MAC_REG_RX_PHY_OFDM_ERROR	(AR9170_MAC_REG_BASE + 0xcb4)
+#define	AR9170_MAC_REG_RX_PHY_CCK_ERROR		(AR9170_MAC_REG_BASE + 0xcb8)
+#define	AR9170_MAC_REG_RX_PHY_HT_ERROR		(AR9170_MAC_REG_BASE + 0xcbc)
+#define	AR9170_MAC_REG_RX_PHY_TOTAL		(AR9170_MAC_REG_BASE + 0xcc0)
+
+#define	AR9170_MAC_REG_DMA_TXQ_ADDR		(AR9170_MAC_REG_BASE + 0xd00)
+#define	AR9170_MAC_REG_DMA_TXQ_CURR_ADDR	(AR9170_MAC_REG_BASE + 0xd04)
+#define	AR9170_MAC_REG_DMA_TXQ0_ADDR		(AR9170_MAC_REG_BASE + 0xd00)
+#define	AR9170_MAC_REG_DMA_TXQ0_CURR_ADDR	(AR9170_MAC_REG_BASE + 0xd04)
+#define	AR9170_MAC_REG_DMA_TXQ1_ADDR		(AR9170_MAC_REG_BASE + 0xd08)
+#define	AR9170_MAC_REG_DMA_TXQ1_CURR_ADDR	(AR9170_MAC_REG_BASE + 0xd0c)
+#define	AR9170_MAC_REG_DMA_TXQ2_ADDR		(AR9170_MAC_REG_BASE + 0xd10)
+#define	AR9170_MAC_REG_DMA_TXQ2_CURR_ADDR	(AR9170_MAC_REG_BASE + 0xd14)
+#define	AR9170_MAC_REG_DMA_TXQ3_ADDR		(AR9170_MAC_REG_BASE + 0xd18)
+#define	AR9170_MAC_REG_DMA_TXQ3_CURR_ADDR	(AR9170_MAC_REG_BASE + 0xd1c)
+#define	AR9170_MAC_REG_DMA_TXQ4_ADDR		(AR9170_MAC_REG_BASE + 0xd20)
+#define	AR9170_MAC_REG_DMA_TXQ4_CURR_ADDR	(AR9170_MAC_REG_BASE + 0xd24)
+#define	AR9170_MAC_REG_DMA_RXQ_ADDR		(AR9170_MAC_REG_BASE + 0xd28)
+#define	AR9170_MAC_REG_DMA_RXQ_CURR_ADDR	(AR9170_MAC_REG_BASE + 0xd2c)
+
+#define	AR9170_MAC_REG_DMA_TRIGGER		(AR9170_MAC_REG_BASE + 0xd30)
+#define		AR9170_DMA_TRIGGER_TXQ0			BIT(0)
+#define		AR9170_DMA_TRIGGER_TXQ1			BIT(1)
+#define		AR9170_DMA_TRIGGER_TXQ2			BIT(2)
+#define		AR9170_DMA_TRIGGER_TXQ3			BIT(3)
+#define		AR9170_DMA_TRIGGER_TXQ4			BIT(4)
+#define		AR9170_DMA_TRIGGER_RXQ			BIT(8)
+
+#define AR9170_MAC_REG_DMA_WLAN_STATUS		(AR9170_MAC_REG_BASE + 0xd38)
+#define	AR9170_MAC_REG_DMA_STATUS		(AR9170_MAC_REG_BASE + 0xd3c)
+
+#define	AR9170_MAC_REG_TXRX_MPI			(AR9170_MAC_REG_BASE + 0xd7c)
+#define		AR9170_MAC_TXRX_MPI_TX_MPI_MASK		0x0000000f
+#define		AR9170_MAC_TXRX_MPI_TX_TO_MASK		0x0000fff0
+#define		AR9170_MAC_TXRX_MPI_RX_MPI_MASK		0x000f0000
+#define		AR9170_MAC_TXRX_MPI_RX_TO_MASK		0xfff00000
+
+#define	AR9170_MAC_REG_BCN_ADDR			(AR9170_MAC_REG_BASE + 0xd84)
+#define	AR9170_MAC_REG_BCN_LENGTH		(AR9170_MAC_REG_BASE + 0xd88)
+#define		AR9170_MAC_BCN_LENGTH_MAX		256
+
+#define AR9170_MAC_REG_BCN_STATUS		(AR9170_MAC_REG_BASE + 0xd8c)
+
+#define	AR9170_MAC_REG_BCN_PLCP			(AR9170_MAC_REG_BASE + 0xd90)
+#define	AR9170_MAC_REG_BCN_CTRL			(AR9170_MAC_REG_BASE + 0xd94)
+#define		AR9170_BCN_CTRL_READY			0x01
+#define		AR9170_BCN_CTRL_LOCK			0x02
+
+#define AR9170_MAC_REG_BCN_CURR_ADDR		(AR9170_MAC_REG_BASE + 0xd98)
+#define	AR9170_MAC_REG_BCN_COUNT		(AR9170_MAC_REG_BASE + 0xd9c)
+
+
+#define	AR9170_MAC_REG_BCN_HT1			(AR9170_MAC_REG_BASE + 0xda0)
+#define	AR9170_MAC_REG_BCN_HT2			(AR9170_MAC_REG_BASE + 0xda4)
+
+#define	AR9170_MAC_REG_DMA_TXQX_ADDR_CURR	(AR9170_MAC_REG_BASE + 0xdc0)
+
+/* Random number generator */
+#define	AR9170_RAND_REG_BASE			0x1d0000
+
+#define	AR9170_RAND_REG_NUM			(AR9170_RAND_REG_BASE + 0x000)
+#define	AR9170_RAND_REG_MODE			(AR9170_RAND_REG_BASE + 0x004)
+#define		AR9170_RAND_MODE_MANUAL			0x000
+#define		AR9170_RAND_MODE_FREE			0x001
+
+/* GPIO */
+#define	AR9170_GPIO_REG_BASE			0x1d0100
+#define	AR9170_GPIO_REG_PORT_TYPE		(AR9170_GPIO_REG_BASE + 0x000)
+#define	AR9170_GPIO_REG_PORT_DATA		(AR9170_GPIO_REG_BASE + 0x004)
+#define		AR9170_GPIO_PORT_LED_0			1
+#define		AR9170_GPIO_PORT_LED_1			2
+/* WPS Button GPIO for TP-Link TL-WN821N */
+#define		AR9170_GPIO_PORT_WPS_BUTTON_PRESSED	4
+
+/* Memory Controller */
+#define	AR9170_MC_REG_BASE			0x1d1000
+
+#define	AR9170_MC_REG_FLASH_WAIT_STATE		(AR9170_MC_REG_BASE + 0x000)
+#define	AR9170_MC_REG_SEEPROM_WP0		(AR9170_MC_REG_BASE + 0x400)
+#define	AR9170_MC_REG_SEEPROM_WP1		(AR9170_MC_REG_BASE + 0x404)
+#define	AR9170_MC_REG_SEEPROM_WP2		(AR9170_MC_REG_BASE + 0x408)
+
+/* Interrupt Controller */
+#define	AR9170_MAX_INT_SRC			9
+#define	AR9170_INT_REG_BASE			0x1d2000
+
+#define	AR9170_INT_REG_FLAG			(AR9170_INT_REG_BASE + 0x000)
+#define	AR9170_INT_REG_FIQ_MASK			(AR9170_INT_REG_BASE + 0x004)
+#define	AR9170_INT_REG_IRQ_MASK			(AR9170_INT_REG_BASE + 0x008)
+/* INT_REG_FLAG, INT_REG_FIQ_MASK and INT_REG_IRQ_MASK */
+#define		AR9170_INT_FLAG_WLAN			0x001
+#define		AR9170_INT_FLAG_PTAB_BIT		0x002
+#define		AR9170_INT_FLAG_SE_BIT			0x004
+#define		AR9170_INT_FLAG_UART_BIT		0x008
+#define		AR9170_INT_FLAG_TIMER_BIT		0x010
+#define		AR9170_INT_FLAG_EXT_BIT			0x020
+#define		AR9170_INT_FLAG_SW_BIT			0x040
+#define		AR9170_INT_FLAG_USB_BIT			0x080
+#define		AR9170_INT_FLAG_ETHERNET_BIT		0x100
+
+#define	AR9170_INT_REG_PRIORITY1		(AR9170_INT_REG_BASE + 0x00c)
+#define	AR9170_INT_REG_PRIORITY2		(AR9170_INT_REG_BASE + 0x010)
+#define	AR9170_INT_REG_PRIORITY3		(AR9170_INT_REG_BASE + 0x014)
+#define	AR9170_INT_REG_EXT_INT_CONTROL		(AR9170_INT_REG_BASE + 0x018)
+#define	AR9170_INT_REG_SW_INT_CONTROL		(AR9170_INT_REG_BASE + 0x01c)
+#define		AR9170_INT_SW_INT_ENABLE		0x1
+
+#define	AR9170_INT_REG_FIQ_ENCODE		(AR9170_INT_REG_BASE + 0x020)
+#define	AR9170_INT_INT_IRQ_ENCODE		(AR9170_INT_REG_BASE + 0x024)
+
+/* Power Management */
+#define	AR9170_PWR_REG_BASE			0x1d4000
+
+#define AR9170_PWR_REG_POWER_STATE		(AR9170_PWR_REG_BASE + 0x000)
+
+#define	AR9170_PWR_REG_RESET			(AR9170_PWR_REG_BASE + 0x004)
+#define		AR9170_PWR_RESET_COMMIT_RESET_MASK	BIT(0)
+#define		AR9170_PWR_RESET_WLAN_MASK		BIT(1)
+#define		AR9170_PWR_RESET_DMA_MASK		BIT(2)
+#define		AR9170_PWR_RESET_BRIDGE_MASK		BIT(3)
+#define		AR9170_PWR_RESET_AHB_MASK		BIT(9)
+#define		AR9170_PWR_RESET_BB_WARM_RESET		BIT(10)
+#define		AR9170_PWR_RESET_BB_COLD_RESET		BIT(11)
+#define		AR9170_PWR_RESET_ADDA_CLK_COLD_RESET	BIT(12)
+#define		AR9170_PWR_RESET_PLL			BIT(13)
+#define		AR9170_PWR_RESET_USB_PLL		BIT(14)
+
+#define	AR9170_PWR_REG_CLOCK_SEL		(AR9170_PWR_REG_BASE + 0x008)
+#define		AR9170_PWR_CLK_AHB_40MHZ		0
+#define		AR9170_PWR_CLK_AHB_20_22MHZ		1
+#define		AR9170_PWR_CLK_AHB_40_44MHZ		2
+#define		AR9170_PWR_CLK_AHB_80_88MHZ		3
+#define		AR9170_PWR_CLK_DAC_160_INV_DLY		0x70
+
+#define	AR9170_PWR_REG_CHIP_REVISION		(AR9170_PWR_REG_BASE + 0x010)
+#define AR9170_PWR_REG_PLL_ADDAC		(AR9170_PWR_REG_BASE + 0x014)
+#define	AR9170_PWR_REG_WATCH_DOG_MAGIC		(AR9170_PWR_REG_BASE + 0x020)
+
+/* Faraday USB Controller */
+#define	AR9170_USB_REG_BASE			0x1e1000
+
+#define	AR9170_USB_REG_MAIN_CTRL		(AR9170_USB_REG_BASE + 0x000)
+#define		AR9170_USB_MAIN_CTRL_REMOTE_WAKEUP	BIT(0)
+#define		AR9170_USB_MAIN_CTRL_ENABLE_GLOBAL_INT	BIT(2)
+#define		AR9170_USB_MAIN_CTRL_HIGHSPEED		BIT(6)
+
+#define	AR9170_USB_REG_DEVICE_ADDRESS		(AR9170_USB_REG_BASE + 0x001)
+#define		AR9170_USB_DEVICE_ADDRESS_CONFIGURE	BIT(7)
+
+#define	AR9170_USB_REG_TEST			(AR9170_USB_REG_BASE + 0x002)
+#define	AR9170_USB_REG_PHY_TEST_SELECT		(AR9170_USB_REG_BASE + 0x008)
+#define	AR9170_USB_REG_CX_CONFIG_STATUS		(AR9170_USB_REG_BASE + 0x00b)
+#define	AR9170_USB_REG_EP0_DATA			(AR9170_USB_REG_BASE + 0x00c)
+#define	AR9170_USB_REG_EP0_DATA1		(AR9170_USB_REG_BASE + 0x00c)
+#define	AR9170_USB_REG_EP0_DATA2		(AR9170_USB_REG_BASE + 0x00d)
+
+#define	AR9170_USB_REG_INTR_MASK_BYTE_0		(AR9170_USB_REG_BASE + 0x011)
+#define	AR9170_USB_REG_INTR_MASK_BYTE_1		(AR9170_USB_REG_BASE + 0x012)
+#define	AR9170_USB_REG_INTR_MASK_BYTE_2		(AR9170_USB_REG_BASE + 0x013)
+#define	AR9170_USB_REG_INTR_MASK_BYTE_3		(AR9170_USB_REG_BASE + 0x014)
+#define	AR9170_USB_REG_INTR_MASK_BYTE_4		(AR9170_USB_REG_BASE + 0x015)
+#define		AR9170_USB_INTR_DISABLE_OUT_INT		(BIT(7) | BIT(6))
+
+#define	AR9170_USB_REG_INTR_MASK_BYTE_5		(AR9170_USB_REG_BASE + 0x016)
+#define	AR9170_USB_REG_INTR_MASK_BYTE_6		(AR9170_USB_REG_BASE + 0x017)
+#define		AR9170_USB_INTR_DISABLE_IN_INT		BIT(6)
+
+#define	AR9170_USB_REG_INTR_MASK_BYTE_7		(AR9170_USB_REG_BASE + 0x018)
+
+#define	AR9170_USB_REG_INTR_GROUP		(AR9170_USB_REG_BASE + 0x020)
+
+#define	AR9170_USB_REG_INTR_SOURCE_0		(AR9170_USB_REG_BASE + 0x021)
+#define	AR9170_USB_REG_INTR_SOURCE_1		(AR9170_USB_REG_BASE + 0x022)
+#define	AR9170_USB_REG_INTR_SOURCE_2		(AR9170_USB_REG_BASE + 0x023)
+#define	AR9170_USB_REG_INTR_SOURCE_3		(AR9170_USB_REG_BASE + 0x024)
+#define	AR9170_USB_REG_INTR_SOURCE_4		(AR9170_USB_REG_BASE + 0x025)
+#define	AR9170_USB_REG_INTR_SOURCE_5		(AR9170_USB_REG_BASE + 0x026)
+#define	AR9170_USB_REG_INTR_SOURCE_6		(AR9170_USB_REG_BASE + 0x027)
+#define	AR9170_USB_REG_INTR_SOURCE_7		(AR9170_USB_REG_BASE + 0x028)
+
+#define	AR9170_USB_REG_EP_MAP			(AR9170_USB_REG_BASE + 0x030)
+#define	AR9170_USB_REG_EP1_MAP			(AR9170_USB_REG_BASE + 0x030)
+#define	AR9170_USB_REG_EP2_MAP			(AR9170_USB_REG_BASE + 0x031)
+#define	AR9170_USB_REG_EP3_MAP			(AR9170_USB_REG_BASE + 0x032)
+#define	AR9170_USB_REG_EP4_MAP			(AR9170_USB_REG_BASE + 0x033)
+#define	AR9170_USB_REG_EP5_MAP			(AR9170_USB_REG_BASE + 0x034)
+#define	AR9170_USB_REG_EP6_MAP			(AR9170_USB_REG_BASE + 0x035)
+#define	AR9170_USB_REG_EP7_MAP			(AR9170_USB_REG_BASE + 0x036)
+#define	AR9170_USB_REG_EP8_MAP			(AR9170_USB_REG_BASE + 0x037)
+#define	AR9170_USB_REG_EP9_MAP			(AR9170_USB_REG_BASE + 0x038)
+#define	AR9170_USB_REG_EP10_MAP			(AR9170_USB_REG_BASE + 0x039)
+
+#define	AR9170_USB_REG_EP_IN_MAX_SIZE_HIGH	(AR9170_USB_REG_BASE + 0x03f)
+#define		AR9170_USB_EP_IN_TOGGLE			0x10
+
+#define	AR9170_USB_REG_EP_IN_MAX_SIZE_LOW	(AR9170_USB_REG_BASE + 0x03e)
+
+#define	AR9170_USB_REG_EP_OUT_MAX_SIZE_HIGH	(AR9170_USB_REG_BASE + 0x05f)
+#define		AR9170_USB_EP_OUT_TOGGLE		0x10
+
+#define	AR9170_USB_REG_EP_OUT_MAX_SIZE_LOW	(AR9170_USB_REG_BASE + 0x05e)
+
+#define	AR9170_USB_REG_EP3_BYTE_COUNT_HIGH	(AR9170_USB_REG_BASE + 0x0ae)
+#define	AR9170_USB_REG_EP3_BYTE_COUNT_LOW	(AR9170_USB_REG_BASE + 0x0be)
+#define	AR9170_USB_REG_EP4_BYTE_COUNT_HIGH	(AR9170_USB_REG_BASE + 0x0af)
+#define	AR9170_USB_REG_EP4_BYTE_COUNT_LOW	(AR9170_USB_REG_BASE + 0x0bf)
+
+#define	AR9170_USB_REG_FIFO_MAP			(AR9170_USB_REG_BASE + 0x080)
+#define	AR9170_USB_REG_FIFO0_MAP		(AR9170_USB_REG_BASE + 0x080)
+#define	AR9170_USB_REG_FIFO1_MAP		(AR9170_USB_REG_BASE + 0x081)
+#define	AR9170_USB_REG_FIFO2_MAP		(AR9170_USB_REG_BASE + 0x082)
+#define	AR9170_USB_REG_FIFO3_MAP		(AR9170_USB_REG_BASE + 0x083)
+#define	AR9170_USB_REG_FIFO4_MAP		(AR9170_USB_REG_BASE + 0x084)
+#define	AR9170_USB_REG_FIFO5_MAP		(AR9170_USB_REG_BASE + 0x085)
+#define	AR9170_USB_REG_FIFO6_MAP		(AR9170_USB_REG_BASE + 0x086)
+#define	AR9170_USB_REG_FIFO7_MAP		(AR9170_USB_REG_BASE + 0x087)
+#define	AR9170_USB_REG_FIFO8_MAP		(AR9170_USB_REG_BASE + 0x088)
+#define	AR9170_USB_REG_FIFO9_MAP		(AR9170_USB_REG_BASE + 0x089)
+
+#define	AR9170_USB_REG_FIFO_CONFIG		(AR9170_USB_REG_BASE + 0x090)
+#define	AR9170_USB_REG_FIFO0_CONFIG		(AR9170_USB_REG_BASE + 0x090)
+#define	AR9170_USB_REG_FIFO1_CONFIG		(AR9170_USB_REG_BASE + 0x091)
+#define	AR9170_USB_REG_FIFO2_CONFIG		(AR9170_USB_REG_BASE + 0x092)
+#define	AR9170_USB_REG_FIFO3_CONFIG		(AR9170_USB_REG_BASE + 0x093)
+#define	AR9170_USB_REG_FIFO4_CONFIG		(AR9170_USB_REG_BASE + 0x094)
+#define	AR9170_USB_REG_FIFO5_CONFIG		(AR9170_USB_REG_BASE + 0x095)
+#define	AR9170_USB_REG_FIFO6_CONFIG		(AR9170_USB_REG_BASE + 0x096)
+#define	AR9170_USB_REG_FIFO7_CONFIG		(AR9170_USB_REG_BASE + 0x097)
+#define	AR9170_USB_REG_FIFO8_CONFIG		(AR9170_USB_REG_BASE + 0x098)
+#define	AR9170_USB_REG_FIFO9_CONFIG		(AR9170_USB_REG_BASE + 0x099)
+
+#define	AR9170_USB_REG_EP3_DATA			(AR9170_USB_REG_BASE + 0x0f8)
+#define	AR9170_USB_REG_EP4_DATA			(AR9170_USB_REG_BASE + 0x0fc)
+
+#define	AR9170_USB_REG_FIFO_SIZE		(AR9170_USB_REG_BASE + 0x100)
+#define	AR9170_USB_REG_DMA_CTL			(AR9170_USB_REG_BASE + 0x108)
+#define		AR9170_USB_DMA_CTL_ENABLE_TO_DEVICE	BIT(0)
+#define		AR9170_USB_DMA_CTL_ENABLE_FROM_DEVICE	BIT(1)
+#define		AR9170_USB_DMA_CTL_HIGH_SPEED		BIT(2)
+#define		AR9170_USB_DMA_CTL_UP_PACKET_MODE	BIT(3)
+#define		AR9170_USB_DMA_CTL_UP_STREAM_S		4
+#define		AR9170_USB_DMA_CTL_UP_STREAM		(BIT(4) | BIT(5))
+#define		AR9170_USB_DMA_CTL_UP_STREAM_4K		(0)
+#define		AR9170_USB_DMA_CTL_UP_STREAM_8K		BIT(4)
+#define		AR9170_USB_DMA_CTL_UP_STREAM_16K	BIT(5)
+#define		AR9170_USB_DMA_CTL_UP_STREAM_32K	(BIT(4) | BIT(5))
+#define		AR9170_USB_DMA_CTL_DOWN_STREAM		BIT(6)
+
+#define	AR9170_USB_REG_DMA_STATUS		(AR9170_USB_REG_BASE + 0x10c)
+#define		AR9170_USB_DMA_STATUS_UP_IDLE		BIT(8)
+#define		AR9170_USB_DMA_STATUS_DN_IDLE		BIT(16)
+
+#define	AR9170_USB_REG_MAX_AGG_UPLOAD		(AR9170_USB_REG_BASE + 0x110)
+#define	AR9170_USB_REG_UPLOAD_TIME_CTL		(AR9170_USB_REG_BASE + 0x114)
+#define	AR9170_USB_REG_CBUS_CTRL		(AR9170_USB_REG_BASE + 0x1f0)
+#define		AR9170_USB_CBUS_CTRL_BUFFER_END		(BIT(1))
+
+/* PCI/USB to AHB Bridge */
+#define	AR9170_PTA_REG_BASE			0x1e2000
+
+#define	AR9170_PTA_REG_CMD			(AR9170_PTA_REG_BASE + 0x000)
+#define	AR9170_PTA_REG_PARAM1			(AR9170_PTA_REG_BASE + 0x004)
+#define	AR9170_PTA_REG_PARAM2			(AR9170_PTA_REG_BASE + 0x008)
+#define	AR9170_PTA_REG_PARAM3			(AR9170_PTA_REG_BASE + 0x00c)
+#define	AR9170_PTA_REG_RSP			(AR9170_PTA_REG_BASE + 0x010)
+#define	AR9170_PTA_REG_STATUS1			(AR9170_PTA_REG_BASE + 0x014)
+#define	AR9170_PTA_REG_STATUS2			(AR9170_PTA_REG_BASE + 0x018)
+#define	AR9170_PTA_REG_STATUS3			(AR9170_PTA_REG_BASE + 0x01c)
+#define	AR9170_PTA_REG_AHB_INT_FLAG		(AR9170_PTA_REG_BASE + 0x020)
+#define	AR9170_PTA_REG_AHB_INT_MASK		(AR9170_PTA_REG_BASE + 0x024)
+#define	AR9170_PTA_REG_AHB_INT_ACK		(AR9170_PTA_REG_BASE + 0x028)
+#define	AR9170_PTA_REG_AHB_SCRATCH1		(AR9170_PTA_REG_BASE + 0x030)
+#define	AR9170_PTA_REG_AHB_SCRATCH2		(AR9170_PTA_REG_BASE + 0x034)
+#define	AR9170_PTA_REG_AHB_SCRATCH3		(AR9170_PTA_REG_BASE + 0x038)
+#define	AR9170_PTA_REG_AHB_SCRATCH4		(AR9170_PTA_REG_BASE + 0x03c)
+
+#define	AR9170_PTA_REG_SHARE_MEM_CTRL		(AR9170_PTA_REG_BASE + 0x124)
+
+/*
+ * PCI to AHB Bridge
+ */
+
+#define	AR9170_PTA_REG_INT_FLAG			(AR9170_PTA_REG_BASE + 0x100)
+#define		AR9170_PTA_INT_FLAG_DN			0x01
+#define		AR9170_PTA_INT_FLAG_UP			0x02
+#define		AR9170_PTA_INT_FLAG_CMD			0x04
+
+#define	AR9170_PTA_REG_INT_MASK			(AR9170_PTA_REG_BASE + 0x104)
+#define	AR9170_PTA_REG_DN_DMA_ADDRL		(AR9170_PTA_REG_BASE + 0x108)
+#define	AR9170_PTA_REG_DN_DMA_ADDRH		(AR9170_PTA_REG_BASE + 0x10c)
+#define	AR9170_PTA_REG_UP_DMA_ADDRL		(AR9170_PTA_REG_BASE + 0x110)
+#define	AR9170_PTA_REG_UP_DMA_ADDRH		(AR9170_PTA_REG_BASE + 0x114)
+#define	AR9170_PTA_REG_DN_PEND_TIME		(AR9170_PTA_REG_BASE + 0x118)
+#define	AR9170_PTA_REG_UP_PEND_TIME		(AR9170_PTA_REG_BASE + 0x11c)
+#define	AR9170_PTA_REG_CONTROL			(AR9170_PTA_REG_BASE + 0x120)
+#define		AR9170_PTA_CTRL_4_BEAT_BURST		0x00
+#define		AR9170_PTA_CTRL_8_BEAT_BURST		0x01
+#define		AR9170_PTA_CTRL_16_BEAT_BURST		0x02
+#define		AR9170_PTA_CTRL_LOOPBACK_MODE		0x10
+
+#define	AR9170_PTA_REG_MEM_CTRL			(AR9170_PTA_REG_BASE + 0x124)
+#define	AR9170_PTA_REG_MEM_ADDR			(AR9170_PTA_REG_BASE + 0x128)
+#define	AR9170_PTA_REG_DN_DMA_TRIGGER		(AR9170_PTA_REG_BASE + 0x12c)
+#define	AR9170_PTA_REG_UP_DMA_TRIGGER		(AR9170_PTA_REG_BASE + 0x130)
+#define	AR9170_PTA_REG_DMA_STATUS		(AR9170_PTA_REG_BASE + 0x134)
+#define	AR9170_PTA_REG_DN_CURR_ADDRL		(AR9170_PTA_REG_BASE + 0x138)
+#define	AR9170_PTA_REG_DN_CURR_ADDRH		(AR9170_PTA_REG_BASE + 0x13c)
+#define	AR9170_PTA_REG_UP_CURR_ADDRL		(AR9170_PTA_REG_BASE + 0x140)
+#define	AR9170_PTA_REG_UP_CURR_ADDRH		(AR9170_PTA_REG_BASE + 0x144)
+#define	AR9170_PTA_REG_DMA_MODE_CTRL		(AR9170_PTA_REG_BASE + 0x148)
+#define		AR9170_PTA_DMA_MODE_CTRL_RESET		BIT(0)
+#define		AR9170_PTA_DMA_MODE_CTRL_DISABLE_USB	BIT(1)
+
+/* Protocol Controller Module */
+#define	AR9170_MAC_REG_PC_REG_BASE		(AR9170_MAC_REG_BASE + 0xe00)
+
+
+#define	AR9170_NUM_LEDS				2
+
+/* CAM */
+#define	AR9170_CAM_MAX_USER			64
+#define	AR9170_CAM_MAX_KEY_LENGTH		16
+
+#define AR9170_SRAM_OFFSET		0x100000
+#define AR9170_SRAM_SIZE		0x18000
+
+#define AR9170_PRAM_OFFSET		0x200000
+#define AR9170_PRAM_SIZE		0x8000
+
+enum cpu_clock {
+	AHB_STATIC_40MHZ = 0,
+	AHB_GMODE_22MHZ = 1,
+	AHB_AMODE_20MHZ = 1,
+	AHB_GMODE_44MHZ = 2,
+	AHB_AMODE_40MHZ = 2,
+	AHB_GMODE_88MHZ = 3,
+	AHB_AMODE_80MHZ = 3
+};
+
+/* USB endpoints */
+enum ar9170_usb_ep {
+	/*
+	 * Control EP is always EP 0 (USB SPEC)
+	 *
+	 * The weird thing is: the original firmware has a few
+	 * comments that suggest that the actual EP numbers
+	 * are in the 1 to 10 range?!
+	 */
+	AR9170_USB_EP_CTRL		= 0,
+
+	AR9170_USB_EP_TX,
+	AR9170_USB_EP_RX,
+	AR9170_USB_EP_IRQ,
+	AR9170_USB_EP_CMD,
+	AR9170_USB_NUM_EXTRA_EP		= 4,
+
+	__AR9170_USB_NUM_EP,
+
+	__AR9170_USB_NUM_MAX_EP		= 10
+};
+
+enum ar9170_usb_fifo {
+	__AR9170_USB_NUM_MAX_FIFO	= 10
+};
+
+enum ar9170_tx_queues {
+	AR9170_TXQ0	= 0,
+	AR9170_TXQ1,
+	AR9170_TXQ2,
+	AR9170_TXQ3,
+	AR9170_TXQ_SPECIAL,
+
+	/* keep last */
+	__AR9170_NUM_TX_QUEUES = 5
+};
+
+#define	AR9170_TX_STREAM_TAG		0x697e
+#define	AR9170_RX_STREAM_TAG		0x4e00
+#define	AR9170_RX_STREAM_MAX_SIZE	0xffff
+
+struct ar9170_stream {
+	__le16 length;
+	__le16 tag;
+
+	u8 payload[0];
+};
+
+#define AR9170_MAX_ACKTABLE_ENTRIES			8
+#define AR9170_MAX_VIRTUAL_MAC				7
+
+#define	AR9170_USB_EP_CTRL_MAX				64
+#define	AR9170_USB_EP_TX_MAX				512
+#define	AR9170_USB_EP_RX_MAX				512
+#define	AR9170_USB_EP_IRQ_MAX				64
+#define	AR9170_USB_EP_CMD_MAX				64
+
+/* Trigger PRETBTT interrupt 6 Kus earlier */
+#define CARL9170_PRETBTT_KUS				6
+
+#define	AR5416_MAX_RATE_POWER				63
+
+#define SET_VAL(reg, value, newvalue)					\
+	(value = ((value) & ~reg) | (((newvalue) << reg##_S) & reg))
+
+#define MOD_VAL(reg, value, newvalue)					\
+	(((value) & ~reg) | (((newvalue) << reg##_S) & reg))
+#endif	/* __CARL9170_SHARED_HW_H */
diff --git a/drivers/net/wireless/ath/carl9170/led.c b/drivers/net/wireless/ath/carl9170/led.c
new file mode 100644
index 0000000..4bb2cbd
--- /dev/null
+++ b/drivers/net/wireless/ath/carl9170/led.c
@@ -0,0 +1,190 @@
+/*
+ * Atheros CARL9170 driver
+ *
+ * LED handling
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010, Christian Lamparer <chunkeey@googlemail.com>
+ *
+ * This program is free software; you can redistribute it 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 program 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or distribute this software for any
+ *    purpose with or without fee is hereby granted, provided that the above
+ *    copyright notice and this permission notice appear in all copies.
+ *
+ *    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "carl9170.h"
+#include "cmd.h"
+
+int carl9170_led_set_state(struct ar9170 *ar, const u32 led_state)
+{
+	return carl9170_write_reg(ar, AR9170_GPIO_REG_PORT_DATA, led_state);
+}
+
+int carl9170_led_init(struct ar9170 *ar)
+{
+	int err;
+
+	/* disable LEDs */
+	/* GPIO [0/1 mode: output, 2/3: input] */
+	err = carl9170_write_reg(ar, AR9170_GPIO_REG_PORT_TYPE, 3);
+	if (err)
+		goto out;
+
+	/* GPIO 0/1 value: off */
+	err = carl9170_led_set_state(ar, 0);
+
+out:
+	return err;
+}
+
+#ifdef CONFIG_CARL9170_LEDS
+static void carl9170_led_update(struct work_struct *work)
+{
+	struct ar9170 *ar = container_of(work, struct ar9170, led_work.work);
+	int i, tmp = 300, blink_delay = 1000;
+	u32 led_val = 0;
+	bool rerun = false;
+
+	if (!IS_ACCEPTING_CMD(ar))
+		return;
+
+	mutex_lock(&ar->mutex);
+	for (i = 0; i < AR9170_NUM_LEDS; i++) {
+		if (ar->leds[i].registered) {
+			if (ar->leds[i].last_state ||
+			    ar->leds[i].toggled) {
+
+				if (ar->leds[i].toggled)
+					tmp = 70 + 200 / (ar->leds[i].toggled);
+
+				if (tmp < blink_delay)
+					blink_delay = tmp;
+
+				led_val |= 1 << i;
+				ar->leds[i].toggled = 0;
+				rerun = true;
+			}
+		}
+	}
+
+	carl9170_led_set_state(ar, led_val);
+	mutex_unlock(&ar->mutex);
+
+	if (!rerun)
+		return;
+
+	ieee80211_queue_delayed_work(ar->hw,
+				     &ar->led_work,
+				     msecs_to_jiffies(blink_delay));
+}
+
+static void carl9170_led_set_brightness(struct led_classdev *led,
+					enum led_brightness brightness)
+{
+	struct carl9170_led *arl = container_of(led, struct carl9170_led, l);
+	struct ar9170 *ar = arl->ar;
+
+	if (!arl->registered)
+		return;
+
+	if (arl->last_state != !!brightness) {
+		arl->toggled++;
+		arl->last_state = !!brightness;
+	}
+
+	if (likely(IS_ACCEPTING_CMD(ar) && arl->toggled))
+		ieee80211_queue_delayed_work(ar->hw, &ar->led_work, HZ/10);
+}
+
+static int carl9170_led_register_led(struct ar9170 *ar, int i, char *name,
+				     char *trigger)
+{
+	int err;
+
+	snprintf(ar->leds[i].name, sizeof(ar->leds[i].name),
+		 "carl9170-%s::%s", wiphy_name(ar->hw->wiphy), name);
+
+	ar->leds[i].ar = ar;
+	ar->leds[i].l.name = ar->leds[i].name;
+	ar->leds[i].l.brightness_set = carl9170_led_set_brightness;
+	ar->leds[i].l.brightness = 0;
+	ar->leds[i].l.default_trigger = trigger;
+
+	err = led_classdev_register(wiphy_dev(ar->hw->wiphy),
+				    &ar->leds[i].l);
+	if (err) {
+		wiphy_err(ar->hw->wiphy, "failed to register %s LED (%d).\n",
+			ar->leds[i].name, err);
+	} else {
+		ar->leds[i].registered = true;
+	}
+
+	return err;
+}
+
+void carl9170_led_unregister(struct ar9170 *ar)
+{
+	int i;
+
+	for (i = 0; i < AR9170_NUM_LEDS; i++)
+		if (ar->leds[i].registered) {
+			led_classdev_unregister(&ar->leds[i].l);
+			ar->leds[i].registered = false;
+			ar->leds[i].toggled = 0;
+		}
+
+	cancel_delayed_work_sync(&ar->led_work);
+}
+
+int carl9170_led_register(struct ar9170 *ar)
+{
+	int err;
+
+	INIT_DELAYED_WORK(&ar->led_work, carl9170_led_update);
+
+	err = carl9170_led_register_led(ar, 0, "tx",
+					ieee80211_get_tx_led_name(ar->hw));
+	if (err)
+		goto fail;
+
+	if (ar->features & CARL9170_ONE_LED)
+		return 0;
+
+	err = carl9170_led_register_led(ar, 1, "assoc",
+					ieee80211_get_assoc_led_name(ar->hw));
+	if (err)
+		goto fail;
+
+	return 0;
+
+fail:
+	carl9170_led_unregister(ar);
+	return err;
+}
+
+#endif /* CONFIG_CARL9170_LEDS */
diff --git a/drivers/net/wireless/ath/carl9170/mac.c b/drivers/net/wireless/ath/carl9170/mac.c
new file mode 100644
index 0000000..2305bc2
--- /dev/null
+++ b/drivers/net/wireless/ath/carl9170/mac.c
@@ -0,0 +1,604 @@
+/*
+ * Atheros CARL9170 driver
+ *
+ * MAC programming
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This program is free software; you can redistribute it 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 program 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or distribute this software for any
+ *    purpose with or without fee is hereby granted, provided that the above
+ *    copyright notice and this permission notice appear in all copies.
+ *
+ *    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <asm/unaligned.h>
+
+#include "carl9170.h"
+#include "cmd.h"
+
+int carl9170_set_dyn_sifs_ack(struct ar9170 *ar)
+{
+	u32 val;
+
+	if (conf_is_ht40(&ar->hw->conf))
+		val = 0x010a;
+	else {
+		if (ar->hw->conf.channel->band == IEEE80211_BAND_2GHZ)
+			val = 0x105;
+		else
+			val = 0x104;
+	}
+
+	return carl9170_write_reg(ar, AR9170_MAC_REG_DYNAMIC_SIFS_ACK, val);
+}
+
+int carl9170_set_rts_cts_rate(struct ar9170 *ar)
+{
+	u32 rts_rate, cts_rate;
+
+	if (conf_is_ht(&ar->hw->conf)) {
+		/* 12 mbit OFDM */
+		rts_rate = 0x1da;
+		cts_rate = 0x10a;
+	} else {
+		if (ar->hw->conf.channel->band == IEEE80211_BAND_2GHZ) {
+			/* 11 mbit CCK */
+			rts_rate = 033;
+			cts_rate = 003;
+		} else {
+			/* 6 mbit OFDM */
+			rts_rate = 0x1bb;
+			cts_rate = 0x10b;
+		}
+	}
+
+	return carl9170_write_reg(ar, AR9170_MAC_REG_RTS_CTS_RATE,
+				  rts_rate | (cts_rate) << 16);
+}
+
+int carl9170_set_slot_time(struct ar9170 *ar)
+{
+	struct ieee80211_vif *vif;
+	u32 slottime = 20;
+
+	rcu_read_lock();
+	vif = carl9170_get_main_vif(ar);
+	if (!vif) {
+		rcu_read_unlock();
+		return 0;
+	}
+
+	if ((ar->hw->conf.channel->band == IEEE80211_BAND_5GHZ) ||
+	    vif->bss_conf.use_short_slot)
+		slottime = 9;
+
+	rcu_read_unlock();
+
+	return carl9170_write_reg(ar, AR9170_MAC_REG_SLOT_TIME,
+				  slottime << 10);
+}
+
+int carl9170_set_mac_rates(struct ar9170 *ar)
+{
+	struct ieee80211_vif *vif;
+	u32 basic, mandatory;
+
+	rcu_read_lock();
+	vif = carl9170_get_main_vif(ar);
+
+	if (!vif) {
+		rcu_read_unlock();
+		return 0;
+	}
+
+	basic = (vif->bss_conf.basic_rates & 0xf);
+	basic |= (vif->bss_conf.basic_rates & 0xff0) << 4;
+	rcu_read_unlock();
+
+	if (ar->hw->conf.channel->band == IEEE80211_BAND_5GHZ)
+		mandatory = 0xff00; /* OFDM 6/9/12/18/24/36/48/54 */
+	else
+		mandatory = 0xff0f; /* OFDM (6/9../54) + CCK (1/2/5.5/11) */
+
+	carl9170_regwrite_begin(ar);
+	carl9170_regwrite(AR9170_MAC_REG_BASIC_RATE, basic);
+	carl9170_regwrite(AR9170_MAC_REG_MANDATORY_RATE, mandatory);
+	carl9170_regwrite_finish();
+
+	return carl9170_regwrite_result();
+}
+
+int carl9170_set_qos(struct ar9170 *ar)
+{
+	carl9170_regwrite_begin(ar);
+
+	carl9170_regwrite(AR9170_MAC_REG_AC0_CW, ar->edcf[0].cw_min |
+			  (ar->edcf[0].cw_max << 16));
+	carl9170_regwrite(AR9170_MAC_REG_AC1_CW, ar->edcf[1].cw_min |
+			  (ar->edcf[1].cw_max << 16));
+	carl9170_regwrite(AR9170_MAC_REG_AC2_CW, ar->edcf[2].cw_min |
+			  (ar->edcf[2].cw_max << 16));
+	carl9170_regwrite(AR9170_MAC_REG_AC3_CW, ar->edcf[3].cw_min |
+			  (ar->edcf[3].cw_max << 16));
+	carl9170_regwrite(AR9170_MAC_REG_AC4_CW, ar->edcf[4].cw_min |
+			  (ar->edcf[4].cw_max << 16));
+
+	carl9170_regwrite(AR9170_MAC_REG_AC2_AC1_AC0_AIFS,
+			  ((ar->edcf[0].aifs * 9 + 10)) |
+			  ((ar->edcf[1].aifs * 9 + 10) << 12) |
+			  ((ar->edcf[2].aifs * 9 + 10) << 24));
+	carl9170_regwrite(AR9170_MAC_REG_AC4_AC3_AC2_AIFS,
+			  ((ar->edcf[2].aifs * 9 + 10) >> 8) |
+			  ((ar->edcf[3].aifs * 9 + 10) << 4) |
+			  ((ar->edcf[4].aifs * 9 + 10) << 16));
+
+	carl9170_regwrite(AR9170_MAC_REG_AC1_AC0_TXOP,
+			  ar->edcf[0].txop | ar->edcf[1].txop << 16);
+	carl9170_regwrite(AR9170_MAC_REG_AC3_AC2_TXOP,
+			  ar->edcf[2].txop | ar->edcf[3].txop << 16 |
+			  ar->edcf[4].txop << 24);
+
+	carl9170_regwrite_finish();
+
+	return carl9170_regwrite_result();
+}
+
+int carl9170_init_mac(struct ar9170 *ar)
+{
+	carl9170_regwrite_begin(ar);
+
+	/* switch MAC to OTUS interface */
+	carl9170_regwrite(0x1c3600, 0x3);
+
+	carl9170_regwrite(AR9170_MAC_REG_ACK_EXTENSION, 0x40);
+
+	carl9170_regwrite(AR9170_MAC_REG_RETRY_MAX, 0x0);
+
+	carl9170_regwrite(AR9170_MAC_REG_FRAMETYPE_FILTER,
+			  AR9170_MAC_FTF_MONITOR);
+
+	/* enable MMIC */
+	carl9170_regwrite(AR9170_MAC_REG_SNIFFER,
+			AR9170_MAC_SNIFFER_DEFAULTS);
+
+	carl9170_regwrite(AR9170_MAC_REG_RX_THRESHOLD, 0xc1f80);
+
+	carl9170_regwrite(AR9170_MAC_REG_RX_PE_DELAY, 0x70);
+	carl9170_regwrite(AR9170_MAC_REG_EIFS_AND_SIFS, 0xa144000);
+	carl9170_regwrite(AR9170_MAC_REG_SLOT_TIME, 9 << 10);
+
+	/* CF-END & CF-ACK rate => 24M OFDM */
+	carl9170_regwrite(AR9170_MAC_REG_TID_CFACK_CFEND_RATE, 0x59900000);
+
+	/* NAV protects ACK only (in TXOP) */
+	carl9170_regwrite(AR9170_MAC_REG_TXOP_DURATION, 0x201);
+
+	/* Set Beacon PHY CTRL's TPC to 0x7, TA1=1 */
+	/* OTUS set AM to 0x1 */
+	carl9170_regwrite(AR9170_MAC_REG_BCN_HT1, 0x8000170);
+
+	carl9170_regwrite(AR9170_MAC_REG_BACKOFF_PROTECT, 0x105);
+
+	/* Aggregation MAX number and timeout */
+	carl9170_regwrite(AR9170_MAC_REG_AMPDU_FACTOR, 0xa);
+	carl9170_regwrite(AR9170_MAC_REG_AMPDU_DENSITY, 0x140a00);
+
+	carl9170_regwrite(AR9170_MAC_REG_FRAMETYPE_FILTER,
+			  AR9170_MAC_FTF_DEFAULTS);
+
+	carl9170_regwrite(AR9170_MAC_REG_RX_CONTROL,
+			  AR9170_MAC_RX_CTRL_DEAGG |
+			  AR9170_MAC_RX_CTRL_SHORT_FILTER);
+
+	/* rate sets */
+	carl9170_regwrite(AR9170_MAC_REG_BASIC_RATE, 0x150f);
+	carl9170_regwrite(AR9170_MAC_REG_MANDATORY_RATE, 0x150f);
+	carl9170_regwrite(AR9170_MAC_REG_RTS_CTS_RATE, 0x0030033);
+
+	/* MIMO response control */
+	carl9170_regwrite(AR9170_MAC_REG_ACK_TPC, 0x4003c1e);
+
+	carl9170_regwrite(AR9170_MAC_REG_AMPDU_RX_THRESH, 0xffff);
+
+	/* set PHY register read timeout (??) */
+	carl9170_regwrite(AR9170_MAC_REG_MISC_680, 0xf00008);
+
+	/* Disable Rx TimeOut, workaround for BB. */
+	carl9170_regwrite(AR9170_MAC_REG_RX_TIMEOUT, 0x0);
+
+	/* Set WLAN DMA interrupt mode: generate int per packet */
+	carl9170_regwrite(AR9170_MAC_REG_TXRX_MPI, 0x110011);
+
+	carl9170_regwrite(AR9170_MAC_REG_FCS_SELECT,
+			AR9170_MAC_FCS_FIFO_PROT);
+
+	/* Disables the CF_END frame, undocumented register */
+	carl9170_regwrite(AR9170_MAC_REG_TXOP_NOT_ENOUGH_IND,
+			0x141e0f48);
+
+	/* reset group hash table */
+	carl9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_L, 0xffffffff);
+	carl9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_H, 0xffffffff);
+
+	/* disable PRETBTT interrupt */
+	carl9170_regwrite(AR9170_MAC_REG_PRETBTT, 0x0);
+	carl9170_regwrite(AR9170_MAC_REG_BCN_PERIOD, 0x0);
+
+	carl9170_regwrite_finish();
+
+	return carl9170_regwrite_result();
+}
+
+static int carl9170_set_mac_reg(struct ar9170 *ar,
+				const u32 reg, const u8 *mac)
+{
+	static const u8 zero[ETH_ALEN] = { 0 };
+
+	if (!mac)
+		mac = zero;
+
+	carl9170_regwrite_begin(ar);
+
+	carl9170_regwrite(reg, get_unaligned_le32(mac));
+	carl9170_regwrite(reg + 4, get_unaligned_le16(mac + 4));
+
+	carl9170_regwrite_finish();
+
+	return carl9170_regwrite_result();
+}
+
+int carl9170_mod_virtual_mac(struct ar9170 *ar, const unsigned int id,
+			     const u8 *mac)
+{
+	if (WARN_ON(id >= ar->fw.vif_num))
+		return -EINVAL;
+
+	return carl9170_set_mac_reg(ar,
+		AR9170_MAC_REG_ACK_TABLE + (id - 1) * 8, mac);
+}
+
+int carl9170_update_multicast(struct ar9170 *ar, const u64 mc_hash)
+{
+	int err;
+
+	carl9170_regwrite_begin(ar);
+	carl9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_H, mc_hash >> 32);
+	carl9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_L, mc_hash);
+	carl9170_regwrite_finish();
+	err = carl9170_regwrite_result();
+	if (err)
+		return err;
+
+	ar->cur_mc_hash = mc_hash;
+	return 0;
+}
+
+int carl9170_set_operating_mode(struct ar9170 *ar)
+{
+	struct ieee80211_vif *vif;
+	struct ath_common *common = &ar->common;
+	u8 *mac_addr, *bssid;
+	u32 cam_mode = AR9170_MAC_CAM_DEFAULTS;
+	u32 enc_mode = AR9170_MAC_ENCRYPTION_DEFAULTS;
+	u32 rx_ctrl = AR9170_MAC_RX_CTRL_DEAGG |
+		      AR9170_MAC_RX_CTRL_SHORT_FILTER;
+	u32 sniffer = AR9170_MAC_SNIFFER_DEFAULTS;
+	int err = 0;
+
+	rcu_read_lock();
+	vif = carl9170_get_main_vif(ar);
+
+	if (vif) {
+		mac_addr = common->macaddr;
+		bssid = common->curbssid;
+
+		switch (vif->type) {
+		case NL80211_IFTYPE_MESH_POINT:
+		case NL80211_IFTYPE_ADHOC:
+			cam_mode |= AR9170_MAC_CAM_IBSS;
+			break;
+		case NL80211_IFTYPE_AP:
+			cam_mode |= AR9170_MAC_CAM_AP;
+
+			/* iwlagn 802.11n STA Workaround */
+			rx_ctrl |= AR9170_MAC_RX_CTRL_PASS_TO_HOST;
+			break;
+		case NL80211_IFTYPE_WDS:
+			cam_mode |= AR9170_MAC_CAM_AP_WDS;
+			rx_ctrl |= AR9170_MAC_RX_CTRL_PASS_TO_HOST;
+			break;
+		case NL80211_IFTYPE_STATION:
+			cam_mode |= AR9170_MAC_CAM_STA;
+			rx_ctrl |= AR9170_MAC_RX_CTRL_PASS_TO_HOST;
+			break;
+		default:
+			WARN(1, "Unsupported operation mode %x\n", vif->type);
+			err = -EOPNOTSUPP;
+			break;
+		}
+	} else {
+		mac_addr = NULL;
+		bssid = NULL;
+	}
+	rcu_read_unlock();
+
+	if (err)
+		return err;
+
+	if (ar->rx_software_decryption)
+		enc_mode |= AR9170_MAC_ENCRYPTION_RX_SOFTWARE;
+
+	if (ar->sniffer_enabled) {
+		rx_ctrl |= AR9170_MAC_RX_CTRL_ACK_IN_SNIFFER;
+		sniffer |= AR9170_MAC_SNIFFER_ENABLE_PROMISC;
+		enc_mode |= AR9170_MAC_ENCRYPTION_RX_SOFTWARE;
+	}
+
+	err = carl9170_set_mac_reg(ar, AR9170_MAC_REG_MAC_ADDR_L, mac_addr);
+	if (err)
+		return err;
+
+	err = carl9170_set_mac_reg(ar, AR9170_MAC_REG_BSSID_L, bssid);
+	if (err)
+		return err;
+
+	carl9170_regwrite_begin(ar);
+	carl9170_regwrite(AR9170_MAC_REG_SNIFFER, sniffer);
+	carl9170_regwrite(AR9170_MAC_REG_CAM_MODE, cam_mode);
+	carl9170_regwrite(AR9170_MAC_REG_ENCRYPTION, enc_mode);
+	carl9170_regwrite(AR9170_MAC_REG_RX_CONTROL, rx_ctrl);
+	carl9170_regwrite_finish();
+
+	return carl9170_regwrite_result();
+}
+
+int carl9170_set_hwretry_limit(struct ar9170 *ar, const unsigned int max_retry)
+{
+	u32 tmp = min_t(u32, 0x33333, max_retry * 0x11111);
+
+	return carl9170_write_reg(ar, AR9170_MAC_REG_RETRY_MAX, tmp);
+}
+
+int carl9170_set_beacon_timers(struct ar9170 *ar)
+{
+	struct ieee80211_vif *vif;
+	u32 v = 0;
+	u32 pretbtt = 0;
+
+	rcu_read_lock();
+	vif = carl9170_get_main_vif(ar);
+
+	if (vif) {
+		struct carl9170_vif_info *mvif;
+		mvif = (void *) vif->drv_priv;
+
+		if (mvif->enable_beacon && !WARN_ON(!ar->beacon_enabled)) {
+			ar->global_beacon_int = vif->bss_conf.beacon_int /
+						ar->beacon_enabled;
+
+			SET_VAL(AR9170_MAC_BCN_DTIM, v,
+				vif->bss_conf.dtim_period);
+
+			switch (vif->type) {
+			case NL80211_IFTYPE_MESH_POINT:
+			case NL80211_IFTYPE_ADHOC:
+				v |= AR9170_MAC_BCN_IBSS_MODE;
+				break;
+			case NL80211_IFTYPE_AP:
+				v |= AR9170_MAC_BCN_AP_MODE;
+				break;
+			default:
+				WARN_ON_ONCE(1);
+				break;
+			}
+		} else if (vif->type == NL80211_IFTYPE_STATION) {
+			ar->global_beacon_int = vif->bss_conf.beacon_int;
+
+			SET_VAL(AR9170_MAC_BCN_DTIM, v,
+				ar->hw->conf.ps_dtim_period);
+
+			v |= AR9170_MAC_BCN_STA_PS |
+			     AR9170_MAC_BCN_PWR_MGT;
+		}
+
+		if (ar->global_beacon_int) {
+			if (ar->global_beacon_int < 15) {
+				rcu_read_unlock();
+				return -ERANGE;
+			}
+
+			ar->global_pretbtt = ar->global_beacon_int -
+					CARL9170_PRETBTT_KUS;
+		} else {
+			ar->global_pretbtt = 0;
+		}
+	} else {
+		ar->global_beacon_int = 0;
+		ar->global_pretbtt = 0;
+	}
+
+	rcu_read_unlock();
+
+	SET_VAL(AR9170_MAC_BCN_PERIOD, v, ar->global_beacon_int);
+	SET_VAL(AR9170_MAC_PRETBTT, pretbtt, ar->global_pretbtt);
+	SET_VAL(AR9170_MAC_PRETBTT2, pretbtt, ar->global_pretbtt);
+
+	carl9170_regwrite_begin(ar);
+	carl9170_regwrite(AR9170_MAC_REG_PRETBTT, pretbtt);
+	carl9170_regwrite(AR9170_MAC_REG_BCN_PERIOD, v);
+	carl9170_regwrite_finish();
+	return carl9170_regwrite_result();
+}
+
+int carl9170_update_beacon(struct ar9170 *ar, const bool submit)
+{
+	struct sk_buff *skb;
+	struct carl9170_vif_info *cvif;
+	__le32 *data, *old = NULL;
+	u32 word, off, addr, len;
+	int i = 0, err = 0;
+
+	rcu_read_lock();
+	cvif = rcu_dereference(ar->beacon_iter);
+retry:
+	if (ar->vifs == 0 || !cvif)
+		goto out_unlock;
+
+	list_for_each_entry_continue_rcu(cvif, &ar->vif_list, list) {
+		if (cvif->active && cvif->enable_beacon)
+			goto found;
+	}
+
+	if (!ar->beacon_enabled || i++)
+		goto out_unlock;
+
+	goto retry;
+
+found:
+	rcu_assign_pointer(ar->beacon_iter, cvif);
+
+	skb = ieee80211_beacon_get_tim(ar->hw, carl9170_get_vif(cvif),
+		NULL, NULL);
+
+	if (!skb) {
+		err = -ENOMEM;
+		goto out_unlock;
+	}
+
+	spin_lock_bh(&ar->beacon_lock);
+	data = (__le32 *)skb->data;
+	if (cvif->beacon)
+		old = (__le32 *)cvif->beacon->data;
+
+	off = cvif->id * AR9170_MAC_BCN_LENGTH_MAX;
+	addr = ar->fw.beacon_addr + off;
+	len = roundup(skb->len + FCS_LEN, 4);
+
+	if ((off + len) > ar->fw.beacon_max_len) {
+		if (net_ratelimit()) {
+			wiphy_err(ar->hw->wiphy, "beacon does not "
+				  "fit into device memory!\n");
+		}
+
+		spin_unlock_bh(&ar->beacon_lock);
+		dev_kfree_skb_any(skb);
+		err = -EINVAL;
+		goto out_unlock;
+	}
+
+	if (len > AR9170_MAC_BCN_LENGTH_MAX) {
+		if (net_ratelimit()) {
+			wiphy_err(ar->hw->wiphy, "no support for beacons "
+				"bigger than %d (yours:%d).\n",
+				 AR9170_MAC_BCN_LENGTH_MAX, len);
+		}
+
+		spin_unlock_bh(&ar->beacon_lock);
+		dev_kfree_skb_any(skb);
+		err = -EMSGSIZE;
+		goto out_unlock;
+	}
+
+	carl9170_async_regwrite_begin(ar);
+
+	/* XXX: use skb->cb info */
+	if (ar->hw->conf.channel->band == IEEE80211_BAND_2GHZ) {
+		carl9170_async_regwrite(AR9170_MAC_REG_BCN_PLCP,
+				((skb->len + FCS_LEN) << (3 + 16)) + 0x0400);
+	} else {
+		carl9170_async_regwrite(AR9170_MAC_REG_BCN_PLCP,
+				((skb->len + FCS_LEN) << 16) + 0x001b);
+	}
+
+	for (i = 0; i < DIV_ROUND_UP(skb->len, 4); i++) {
+		/*
+		 * XXX: This accesses beyond skb data for up
+		 *	to the last 3 bytes!!
+		 */
+
+		if (old && (data[i] == old[i]))
+			continue;
+
+		word = le32_to_cpu(data[i]);
+		carl9170_async_regwrite(addr + 4 * i, word);
+	}
+	carl9170_async_regwrite_finish();
+
+	dev_kfree_skb_any(cvif->beacon);
+	cvif->beacon = NULL;
+
+	err = carl9170_async_regwrite_result();
+	if (!err)
+		cvif->beacon = skb;
+	spin_unlock_bh(&ar->beacon_lock);
+	if (err)
+		goto out_unlock;
+
+	if (submit) {
+		err = carl9170_bcn_ctrl(ar, cvif->id,
+					CARL9170_BCN_CTRL_CAB_TRIGGER,
+					addr, skb->len + FCS_LEN);
+
+		if (err)
+			goto out_unlock;
+	}
+out_unlock:
+	rcu_read_unlock();
+	return err;
+}
+
+int carl9170_upload_key(struct ar9170 *ar, const u8 id, const u8 *mac,
+			const u8 ktype, const u8 keyidx, const u8 *keydata,
+			const int keylen)
+{
+	struct carl9170_set_key_cmd key = { };
+	static const u8 bcast[ETH_ALEN] = {
+		0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+	mac = mac ? : bcast;
+
+	key.user = cpu_to_le16(id);
+	key.keyId = cpu_to_le16(keyidx);
+	key.type = cpu_to_le16(ktype);
+	memcpy(&key.macAddr, mac, ETH_ALEN);
+	if (keydata)
+		memcpy(&key.key, keydata, keylen);
+
+	return carl9170_exec_cmd(ar, CARL9170_CMD_EKEY,
+		sizeof(key), (u8 *)&key, 0, NULL);
+}
+
+int carl9170_disable_key(struct ar9170 *ar, const u8 id)
+{
+	struct carl9170_disable_key_cmd key = { };
+
+	key.user = cpu_to_le16(id);
+
+	return carl9170_exec_cmd(ar, CARL9170_CMD_DKEY,
+		sizeof(key), (u8 *)&key, 0, NULL);
+}
diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c
new file mode 100644
index 0000000..43de9dfa
--- /dev/null
+++ b/drivers/net/wireless/ath/carl9170/main.c
@@ -0,0 +1,1855 @@
+/*
+ * Atheros CARL9170 driver
+ *
+ * mac80211 interaction code
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * This program is free software; you can redistribute it 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 program 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or distribute this software for any
+ *    purpose with or without fee is hereby granted, provided that the above
+ *    copyright notice and this permission notice appear in all copies.
+ *
+ *    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/etherdevice.h>
+#include <linux/random.h>
+#include <net/mac80211.h>
+#include <net/cfg80211.h>
+#include "hw.h"
+#include "carl9170.h"
+#include "cmd.h"
+
+static int modparam_nohwcrypt;
+module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
+MODULE_PARM_DESC(nohwcrypt, "Disable hardware crypto offload.");
+
+int modparam_noht;
+module_param_named(noht, modparam_noht, int, S_IRUGO);
+MODULE_PARM_DESC(noht, "Disable MPDU aggregation.");
+
+#define RATE(_bitrate, _hw_rate, _txpidx, _flags) {	\
+	.bitrate	= (_bitrate),			\
+	.flags		= (_flags),			\
+	.hw_value	= (_hw_rate) | (_txpidx) << 4,	\
+}
+
+struct ieee80211_rate __carl9170_ratetable[] = {
+	RATE(10, 0, 0, 0),
+	RATE(20, 1, 1, IEEE80211_RATE_SHORT_PREAMBLE),
+	RATE(55, 2, 2, IEEE80211_RATE_SHORT_PREAMBLE),
+	RATE(110, 3, 3, IEEE80211_RATE_SHORT_PREAMBLE),
+	RATE(60, 0xb, 0, 0),
+	RATE(90, 0xf, 0, 0),
+	RATE(120, 0xa, 0, 0),
+	RATE(180, 0xe, 0, 0),
+	RATE(240, 0x9, 0, 0),
+	RATE(360, 0xd, 1, 0),
+	RATE(480, 0x8, 2, 0),
+	RATE(540, 0xc, 3, 0),
+};
+#undef RATE
+
+#define carl9170_g_ratetable	(__carl9170_ratetable + 0)
+#define carl9170_g_ratetable_size	12
+#define carl9170_a_ratetable	(__carl9170_ratetable + 4)
+#define carl9170_a_ratetable_size	8
+
+/*
+ * NB: The hw_value is used as an index into the carl9170_phy_freq_params
+ *     array in phy.c so that we don't have to do frequency lookups!
+ */
+#define CHAN(_freq, _idx) {		\
+	.center_freq	= (_freq),	\
+	.hw_value	= (_idx),	\
+	.max_power	= 18, /* XXX */	\
+}
+
+static struct ieee80211_channel carl9170_2ghz_chantable[] = {
+	CHAN(2412,  0),
+	CHAN(2417,  1),
+	CHAN(2422,  2),
+	CHAN(2427,  3),
+	CHAN(2432,  4),
+	CHAN(2437,  5),
+	CHAN(2442,  6),
+	CHAN(2447,  7),
+	CHAN(2452,  8),
+	CHAN(2457,  9),
+	CHAN(2462, 10),
+	CHAN(2467, 11),
+	CHAN(2472, 12),
+	CHAN(2484, 13),
+};
+
+static struct ieee80211_channel carl9170_5ghz_chantable[] = {
+	CHAN(4920, 14),
+	CHAN(4940, 15),
+	CHAN(4960, 16),
+	CHAN(4980, 17),
+	CHAN(5040, 18),
+	CHAN(5060, 19),
+	CHAN(5080, 20),
+	CHAN(5180, 21),
+	CHAN(5200, 22),
+	CHAN(5220, 23),
+	CHAN(5240, 24),
+	CHAN(5260, 25),
+	CHAN(5280, 26),
+	CHAN(5300, 27),
+	CHAN(5320, 28),
+	CHAN(5500, 29),
+	CHAN(5520, 30),
+	CHAN(5540, 31),
+	CHAN(5560, 32),
+	CHAN(5580, 33),
+	CHAN(5600, 34),
+	CHAN(5620, 35),
+	CHAN(5640, 36),
+	CHAN(5660, 37),
+	CHAN(5680, 38),
+	CHAN(5700, 39),
+	CHAN(5745, 40),
+	CHAN(5765, 41),
+	CHAN(5785, 42),
+	CHAN(5805, 43),
+	CHAN(5825, 44),
+	CHAN(5170, 45),
+	CHAN(5190, 46),
+	CHAN(5210, 47),
+	CHAN(5230, 48),
+};
+#undef CHAN
+
+#define CARL9170_HT_CAP							\
+{									\
+	.ht_supported	= true,						\
+	.cap		= IEEE80211_HT_CAP_MAX_AMSDU |			\
+			  IEEE80211_HT_CAP_SUP_WIDTH_20_40 |		\
+			  IEEE80211_HT_CAP_SGI_40 |			\
+			  IEEE80211_HT_CAP_DSSSCCK40 |			\
+			  IEEE80211_HT_CAP_SM_PS,			\
+	.ampdu_factor	= IEEE80211_HT_MAX_AMPDU_64K,			\
+	.ampdu_density	= IEEE80211_HT_MPDU_DENSITY_8,			\
+	.mcs		= {						\
+		.rx_mask = { 0xff, 0xff, 0, 0, 0x1, 0, 0, 0, 0, 0, },	\
+		.rx_highest = cpu_to_le16(300),				\
+		.tx_params = IEEE80211_HT_MCS_TX_DEFINED,		\
+	},								\
+}
+
+static struct ieee80211_supported_band carl9170_band_2GHz = {
+	.channels	= carl9170_2ghz_chantable,
+	.n_channels	= ARRAY_SIZE(carl9170_2ghz_chantable),
+	.bitrates	= carl9170_g_ratetable,
+	.n_bitrates	= carl9170_g_ratetable_size,
+	.ht_cap		= CARL9170_HT_CAP,
+};
+
+static struct ieee80211_supported_band carl9170_band_5GHz = {
+	.channels	= carl9170_5ghz_chantable,
+	.n_channels	= ARRAY_SIZE(carl9170_5ghz_chantable),
+	.bitrates	= carl9170_a_ratetable,
+	.n_bitrates	= carl9170_a_ratetable_size,
+	.ht_cap		= CARL9170_HT_CAP,
+};
+
+static void carl9170_ampdu_gc(struct ar9170 *ar)
+{
+	struct carl9170_sta_tid *tid_info;
+	LIST_HEAD(tid_gc);
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(tid_info, &ar->tx_ampdu_list, list) {
+		spin_lock_bh(&ar->tx_ampdu_list_lock);
+		if (tid_info->state == CARL9170_TID_STATE_SHUTDOWN) {
+			tid_info->state = CARL9170_TID_STATE_KILLED;
+			list_del_rcu(&tid_info->list);
+			ar->tx_ampdu_list_len--;
+			list_add_tail(&tid_info->tmp_list, &tid_gc);
+		}
+		spin_unlock_bh(&ar->tx_ampdu_list_lock);
+
+	}
+	rcu_assign_pointer(ar->tx_ampdu_iter, tid_info);
+	rcu_read_unlock();
+
+	synchronize_rcu();
+
+	while (!list_empty(&tid_gc)) {
+		struct sk_buff *skb;
+		tid_info = list_first_entry(&tid_gc, struct carl9170_sta_tid,
+					    tmp_list);
+
+		while ((skb = __skb_dequeue(&tid_info->queue)))
+			carl9170_tx_status(ar, skb, false);
+
+		list_del_init(&tid_info->tmp_list);
+		kfree(tid_info);
+	}
+}
+
+static void carl9170_flush(struct ar9170 *ar, bool drop_queued)
+{
+	if (drop_queued) {
+		int i;
+
+		/*
+		 * We can only drop frames which have not been uploaded
+		 * to the device yet.
+		 */
+
+		for (i = 0; i < ar->hw->queues; i++) {
+			struct sk_buff *skb;
+
+			while ((skb = skb_dequeue(&ar->tx_pending[i])))
+				carl9170_tx_status(ar, skb, false);
+		}
+	}
+
+	/* Wait for all other outstanding frames to timeout. */
+	if (atomic_read(&ar->tx_total_queued))
+		WARN_ON(wait_for_completion_timeout(&ar->tx_flush, HZ) == 0);
+}
+
+static void carl9170_flush_ba(struct ar9170 *ar)
+{
+	struct sk_buff_head free;
+	struct carl9170_sta_tid *tid_info;
+	struct sk_buff *skb;
+
+	__skb_queue_head_init(&free);
+
+	rcu_read_lock();
+	spin_lock_bh(&ar->tx_ampdu_list_lock);
+	list_for_each_entry_rcu(tid_info, &ar->tx_ampdu_list, list) {
+		if (tid_info->state > CARL9170_TID_STATE_SUSPEND) {
+			tid_info->state = CARL9170_TID_STATE_SUSPEND;
+
+			spin_lock(&tid_info->lock);
+			while ((skb = __skb_dequeue(&tid_info->queue)))
+				__skb_queue_tail(&free, skb);
+			spin_unlock(&tid_info->lock);
+		}
+	}
+	spin_unlock_bh(&ar->tx_ampdu_list_lock);
+	rcu_read_unlock();
+
+	while ((skb = __skb_dequeue(&free)))
+		carl9170_tx_status(ar, skb, false);
+}
+
+static void carl9170_zap_queues(struct ar9170 *ar)
+{
+	struct carl9170_vif_info *cvif;
+	unsigned int i;
+
+	carl9170_ampdu_gc(ar);
+
+	carl9170_flush_ba(ar);
+	carl9170_flush(ar, true);
+
+	for (i = 0; i < ar->hw->queues; i++) {
+		spin_lock_bh(&ar->tx_status[i].lock);
+		while (!skb_queue_empty(&ar->tx_status[i])) {
+			struct sk_buff *skb;
+
+			skb = skb_peek(&ar->tx_status[i]);
+			carl9170_tx_get_skb(skb);
+			spin_unlock_bh(&ar->tx_status[i].lock);
+			carl9170_tx_drop(ar, skb);
+			spin_lock_bh(&ar->tx_status[i].lock);
+			carl9170_tx_put_skb(skb);
+		}
+		spin_unlock_bh(&ar->tx_status[i].lock);
+	}
+
+	BUILD_BUG_ON(CARL9170_NUM_TX_LIMIT_SOFT < 1);
+	BUILD_BUG_ON(CARL9170_NUM_TX_LIMIT_HARD < CARL9170_NUM_TX_LIMIT_SOFT);
+	BUILD_BUG_ON(CARL9170_NUM_TX_LIMIT_HARD >= CARL9170_BAW_BITS);
+
+	/* reinitialize queues statistics */
+	memset(&ar->tx_stats, 0, sizeof(ar->tx_stats));
+	for (i = 0; i < ar->hw->queues; i++)
+		ar->tx_stats[i].limit = CARL9170_NUM_TX_LIMIT_HARD;
+
+	for (i = 0; i < DIV_ROUND_UP(ar->fw.mem_blocks, BITS_PER_LONG); i++)
+		ar->mem_bitmap[i] = 0;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(cvif, &ar->vif_list, list) {
+		spin_lock_bh(&ar->beacon_lock);
+		dev_kfree_skb_any(cvif->beacon);
+		cvif->beacon = NULL;
+		spin_unlock_bh(&ar->beacon_lock);
+	}
+	rcu_read_unlock();
+
+	atomic_set(&ar->tx_ampdu_upload, 0);
+	atomic_set(&ar->tx_ampdu_scheduler, 0);
+	atomic_set(&ar->tx_total_pending, 0);
+	atomic_set(&ar->tx_total_queued, 0);
+	atomic_set(&ar->mem_free_blocks, ar->fw.mem_blocks);
+}
+
+#define CARL9170_FILL_QUEUE(queue, ai_fs, cwmin, cwmax, _txop)		\
+do {									\
+	queue.aifs = ai_fs;						\
+	queue.cw_min = cwmin;						\
+	queue.cw_max = cwmax;						\
+	queue.txop = _txop;						\
+} while (0)
+
+static int carl9170_op_start(struct ieee80211_hw *hw)
+{
+	struct ar9170 *ar = hw->priv;
+	int err, i;
+
+	mutex_lock(&ar->mutex);
+
+	carl9170_zap_queues(ar);
+
+	/* reset QoS defaults */
+	CARL9170_FILL_QUEUE(ar->edcf[0], 3, 15, 1023,  0); /* BEST EFFORT */
+	CARL9170_FILL_QUEUE(ar->edcf[1], 2, 7,    15, 94); /* VIDEO */
+	CARL9170_FILL_QUEUE(ar->edcf[2], 2, 3,     7, 47); /* VOICE */
+	CARL9170_FILL_QUEUE(ar->edcf[3], 7, 15, 1023,  0); /* BACKGROUND */
+	CARL9170_FILL_QUEUE(ar->edcf[4], 2, 3,     7,  0); /* SPECIAL */
+
+	ar->current_factor = ar->current_density = -1;
+	/* "The first key is unique." */
+	ar->usedkeys = 1;
+	ar->filter_state = 0;
+	ar->ps.last_action = jiffies;
+	ar->ps.last_slept = jiffies;
+	ar->erp_mode = CARL9170_ERP_AUTO;
+	ar->rx_software_decryption = false;
+	ar->disable_offload = false;
+
+	for (i = 0; i < ar->hw->queues; i++) {
+		ar->queue_stop_timeout[i] = jiffies;
+		ar->max_queue_stop_timeout[i] = 0;
+	}
+
+	atomic_set(&ar->mem_allocs, 0);
+
+	err = carl9170_usb_open(ar);
+	if (err)
+		goto out;
+
+	err = carl9170_init_mac(ar);
+	if (err)
+		goto out;
+
+	err = carl9170_set_qos(ar);
+	if (err)
+		goto out;
+
+	err = carl9170_write_reg(ar, AR9170_MAC_REG_DMA_TRIGGER,
+				 AR9170_DMA_TRIGGER_RXQ);
+	if (err)
+		goto out;
+
+	/* Clear key-cache */
+	for (i = 0; i < AR9170_CAM_MAX_USER + 4; i++) {
+		err = carl9170_upload_key(ar, i, NULL, AR9170_ENC_ALG_NONE,
+					  0, NULL, 0);
+		if (err)
+			goto out;
+
+		err = carl9170_upload_key(ar, i, NULL, AR9170_ENC_ALG_NONE,
+					  1, NULL, 0);
+		if (err)
+			goto out;
+
+		if (i < AR9170_CAM_MAX_USER) {
+			err = carl9170_disable_key(ar, i);
+			if (err)
+				goto out;
+		}
+	}
+
+	carl9170_set_state_when(ar, CARL9170_IDLE, CARL9170_STARTED);
+
+	ieee80211_wake_queues(ar->hw);
+	err = 0;
+
+out:
+	mutex_unlock(&ar->mutex);
+	return err;
+}
+
+static void carl9170_cancel_worker(struct ar9170 *ar)
+{
+	cancel_delayed_work_sync(&ar->tx_janitor);
+#ifdef CONFIG_CARL9170_LEDS
+	cancel_delayed_work_sync(&ar->led_work);
+#endif /* CONFIG_CARL9170_LEDS */
+	cancel_work_sync(&ar->ps_work);
+	cancel_work_sync(&ar->ampdu_work);
+}
+
+static void carl9170_op_stop(struct ieee80211_hw *hw)
+{
+	struct ar9170 *ar = hw->priv;
+
+	carl9170_set_state_when(ar, CARL9170_STARTED, CARL9170_IDLE);
+
+	ieee80211_stop_queues(ar->hw);
+
+	mutex_lock(&ar->mutex);
+	if (IS_ACCEPTING_CMD(ar)) {
+		rcu_assign_pointer(ar->beacon_iter, NULL);
+
+		carl9170_led_set_state(ar, 0);
+
+		/* stop DMA */
+		carl9170_write_reg(ar, AR9170_MAC_REG_DMA_TRIGGER, 0);
+		carl9170_usb_stop(ar);
+	}
+
+	carl9170_zap_queues(ar);
+	mutex_unlock(&ar->mutex);
+
+	carl9170_cancel_worker(ar);
+}
+
+static void carl9170_restart_work(struct work_struct *work)
+{
+	struct ar9170 *ar = container_of(work, struct ar9170,
+					 restart_work);
+	int err;
+
+	ar->usedkeys = 0;
+	ar->filter_state = 0;
+	carl9170_cancel_worker(ar);
+
+	mutex_lock(&ar->mutex);
+	err = carl9170_usb_restart(ar);
+	if (net_ratelimit()) {
+		if (err) {
+			dev_err(&ar->udev->dev, "Failed to restart device "
+				" (%d).\n", err);
+		 } else {
+			dev_info(&ar->udev->dev, "device restarted "
+				 "successfully.\n");
+		}
+	}
+
+	carl9170_zap_queues(ar);
+	mutex_unlock(&ar->mutex);
+	if (!err) {
+		ar->restart_counter++;
+		atomic_set(&ar->pending_restarts, 0);
+
+		ieee80211_restart_hw(ar->hw);
+	} else {
+		/*
+		 * The reset was unsuccessful and the device seems to
+		 * be dead. But there's still one option: a low-level
+		 * usb subsystem reset...
+		 */
+
+		carl9170_usb_reset(ar);
+	}
+}
+
+void carl9170_restart(struct ar9170 *ar, const enum carl9170_restart_reasons r)
+{
+	carl9170_set_state_when(ar, CARL9170_STARTED, CARL9170_IDLE);
+
+	/*
+	 * Sometimes, an error can trigger several different reset events.
+	 * By ignoring these *surplus* reset events, the device won't be
+	 * killed again, right after it has recovered.
+	 */
+	if (atomic_inc_return(&ar->pending_restarts) > 1) {
+		dev_dbg(&ar->udev->dev, "ignoring restart (%d)\n", r);
+		return;
+	}
+
+	ieee80211_stop_queues(ar->hw);
+
+	dev_err(&ar->udev->dev, "restart device (%d)\n", r);
+
+	if (!WARN_ON(r == CARL9170_RR_NO_REASON) ||
+	    !WARN_ON(r >= __CARL9170_RR_LAST))
+		ar->last_reason = r;
+
+	if (!ar->registered)
+		return;
+
+	if (IS_ACCEPTING_CMD(ar) && !ar->needs_full_reset)
+		ieee80211_queue_work(ar->hw, &ar->restart_work);
+	else
+		carl9170_usb_reset(ar);
+
+	/*
+	 * At this point, the device instance might have vanished/disabled.
+	 * So, don't put any code which access the ar9170 struct
+	 * without proper protection.
+	 */
+}
+
+static int carl9170_init_interface(struct ar9170 *ar,
+				   struct ieee80211_vif *vif)
+{
+	struct ath_common *common = &ar->common;
+	int err;
+
+	if (!vif) {
+		WARN_ON_ONCE(IS_STARTED(ar));
+		return 0;
+	}
+
+	memcpy(common->macaddr, vif->addr, ETH_ALEN);
+
+	if (modparam_nohwcrypt ||
+	    ((vif->type != NL80211_IFTYPE_STATION) &&
+	     (vif->type != NL80211_IFTYPE_AP))) {
+		ar->rx_software_decryption = true;
+		ar->disable_offload = true;
+	}
+
+	err = carl9170_set_operating_mode(ar);
+	return err;
+}
+
+static int carl9170_op_add_interface(struct ieee80211_hw *hw,
+				     struct ieee80211_vif *vif)
+{
+	struct carl9170_vif_info *vif_priv = (void *) vif->drv_priv;
+	struct ieee80211_vif *main_vif;
+	struct ar9170 *ar = hw->priv;
+	int vif_id = -1, err = 0;
+
+	mutex_lock(&ar->mutex);
+	rcu_read_lock();
+	if (vif_priv->active) {
+		/*
+		 * Skip the interface structure initialization,
+		 * if the vif survived the _restart call.
+		 */
+		vif_id = vif_priv->id;
+		vif_priv->enable_beacon = false;
+
+		spin_lock_bh(&ar->beacon_lock);
+		dev_kfree_skb_any(vif_priv->beacon);
+		vif_priv->beacon = NULL;
+		spin_unlock_bh(&ar->beacon_lock);
+
+		goto init;
+	}
+
+	main_vif = carl9170_get_main_vif(ar);
+
+	if (main_vif) {
+		switch (main_vif->type) {
+		case NL80211_IFTYPE_STATION:
+			if (vif->type == NL80211_IFTYPE_STATION)
+				break;
+
+			err = -EBUSY;
+			rcu_read_unlock();
+
+			goto unlock;
+
+		case NL80211_IFTYPE_AP:
+			if ((vif->type == NL80211_IFTYPE_STATION) ||
+			    (vif->type == NL80211_IFTYPE_WDS) ||
+			    (vif->type == NL80211_IFTYPE_AP))
+				break;
+
+			err = -EBUSY;
+			rcu_read_unlock();
+			goto unlock;
+
+		default:
+			rcu_read_unlock();
+			goto unlock;
+		}
+	}
+
+	vif_id = bitmap_find_free_region(&ar->vif_bitmap, ar->fw.vif_num, 0);
+
+	if (vif_id < 0) {
+		rcu_read_unlock();
+
+		err = -ENOSPC;
+		goto unlock;
+	}
+
+	BUG_ON(ar->vif_priv[vif_id].id != vif_id);
+
+	vif_priv->active = true;
+	vif_priv->id = vif_id;
+	vif_priv->enable_beacon = false;
+	ar->vifs++;
+	list_add_tail_rcu(&vif_priv->list, &ar->vif_list);
+	rcu_assign_pointer(ar->vif_priv[vif_id].vif, vif);
+
+init:
+	if (carl9170_get_main_vif(ar) == vif) {
+		rcu_assign_pointer(ar->beacon_iter, vif_priv);
+		rcu_read_unlock();
+
+		err = carl9170_init_interface(ar, vif);
+		if (err)
+			goto unlock;
+	} else {
+		err = carl9170_mod_virtual_mac(ar, vif_id, vif->addr);
+		rcu_read_unlock();
+
+		if (err)
+			goto unlock;
+	}
+
+unlock:
+	if (err && (vif_id != -1)) {
+		vif_priv->active = false;
+		bitmap_release_region(&ar->vif_bitmap, vif_id, 0);
+		ar->vifs--;
+		rcu_assign_pointer(ar->vif_priv[vif_id].vif, NULL);
+		list_del_rcu(&vif_priv->list);
+		mutex_unlock(&ar->mutex);
+		synchronize_rcu();
+	} else {
+		if (ar->vifs > 1)
+			ar->ps.off_override |= PS_OFF_VIF;
+
+		mutex_unlock(&ar->mutex);
+	}
+
+	return err;
+}
+
+static void carl9170_op_remove_interface(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif)
+{
+	struct carl9170_vif_info *vif_priv = (void *) vif->drv_priv;
+	struct ieee80211_vif *main_vif;
+	struct ar9170 *ar = hw->priv;
+	unsigned int id;
+
+	mutex_lock(&ar->mutex);
+
+	if (WARN_ON_ONCE(!vif_priv->active))
+		goto unlock;
+
+	ar->vifs--;
+
+	rcu_read_lock();
+	main_vif = carl9170_get_main_vif(ar);
+
+	id = vif_priv->id;
+
+	vif_priv->active = false;
+	WARN_ON(vif_priv->enable_beacon);
+	vif_priv->enable_beacon = false;
+	list_del_rcu(&vif_priv->list);
+	rcu_assign_pointer(ar->vif_priv[id].vif, NULL);
+
+	if (vif == main_vif) {
+		rcu_read_unlock();
+
+		if (ar->vifs) {
+			WARN_ON(carl9170_init_interface(ar,
+					carl9170_get_main_vif(ar)));
+		} else {
+			carl9170_set_operating_mode(ar);
+		}
+	} else {
+		rcu_read_unlock();
+
+		WARN_ON(carl9170_mod_virtual_mac(ar, id, NULL));
+	}
+
+	carl9170_update_beacon(ar, false);
+	carl9170_flush_cab(ar, id);
+
+	spin_lock_bh(&ar->beacon_lock);
+	dev_kfree_skb_any(vif_priv->beacon);
+	vif_priv->beacon = NULL;
+	spin_unlock_bh(&ar->beacon_lock);
+
+	bitmap_release_region(&ar->vif_bitmap, id, 0);
+
+	carl9170_set_beacon_timers(ar);
+
+	if (ar->vifs == 1)
+		ar->ps.off_override &= ~PS_OFF_VIF;
+
+unlock:
+	mutex_unlock(&ar->mutex);
+
+	synchronize_rcu();
+}
+
+void carl9170_ps_check(struct ar9170 *ar)
+{
+	ieee80211_queue_work(ar->hw, &ar->ps_work);
+}
+
+/* caller must hold ar->mutex */
+static int carl9170_ps_update(struct ar9170 *ar)
+{
+	bool ps = false;
+	int err = 0;
+
+	if (!ar->ps.off_override)
+		ps = (ar->hw->conf.flags & IEEE80211_CONF_PS);
+
+	if (ps != ar->ps.state) {
+		err = carl9170_powersave(ar, ps);
+		if (err)
+			return err;
+
+		if (ar->ps.state && !ps) {
+			ar->ps.sleep_ms = jiffies_to_msecs(jiffies -
+				ar->ps.last_action);
+		}
+
+		if (ps)
+			ar->ps.last_slept = jiffies;
+
+		ar->ps.last_action = jiffies;
+		ar->ps.state = ps;
+	}
+
+	return 0;
+}
+
+static void carl9170_ps_work(struct work_struct *work)
+{
+	struct ar9170 *ar = container_of(work, struct ar9170,
+					 ps_work);
+	mutex_lock(&ar->mutex);
+	if (IS_STARTED(ar))
+		WARN_ON_ONCE(carl9170_ps_update(ar) != 0);
+	mutex_unlock(&ar->mutex);
+}
+
+
+static int carl9170_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+	struct ar9170 *ar = hw->priv;
+	int err = 0;
+
+	mutex_lock(&ar->mutex);
+	if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) {
+		/* TODO */
+		err = 0;
+	}
+
+	if (changed & IEEE80211_CONF_CHANGE_PS) {
+		err = carl9170_ps_update(ar);
+		if (err)
+			goto out;
+	}
+
+	if (changed & IEEE80211_CONF_CHANGE_POWER) {
+		/* TODO */
+		err = 0;
+	}
+
+	if (changed & IEEE80211_CONF_CHANGE_SMPS) {
+		/* TODO */
+		err = 0;
+	}
+
+	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+		/* adjust slot time for 5 GHz */
+		err = carl9170_set_slot_time(ar);
+		if (err)
+			goto out;
+
+		err = carl9170_set_channel(ar, hw->conf.channel,
+			hw->conf.channel_type, CARL9170_RFI_NONE);
+		if (err)
+			goto out;
+
+		err = carl9170_set_dyn_sifs_ack(ar);
+		if (err)
+			goto out;
+
+		err = carl9170_set_rts_cts_rate(ar);
+		if (err)
+			goto out;
+	}
+
+out:
+	mutex_unlock(&ar->mutex);
+	return err;
+}
+
+static u64 carl9170_op_prepare_multicast(struct ieee80211_hw *hw,
+					 struct netdev_hw_addr_list *mc_list)
+{
+	struct netdev_hw_addr *ha;
+	u64 mchash;
+
+	/* always get broadcast frames */
+	mchash = 1ULL << (0xff >> 2);
+
+	netdev_hw_addr_list_for_each(ha, mc_list)
+		mchash |= 1ULL << (ha->addr[5] >> 2);
+
+	return mchash;
+}
+
+static void carl9170_op_configure_filter(struct ieee80211_hw *hw,
+					 unsigned int changed_flags,
+					 unsigned int *new_flags,
+					 u64 multicast)
+{
+	struct ar9170 *ar = hw->priv;
+
+	/* mask supported flags */
+	*new_flags &= FIF_ALLMULTI | FIF_FCSFAIL | FIF_PLCPFAIL |
+		      FIF_OTHER_BSS | FIF_PROMISC_IN_BSS;
+
+	if (!IS_ACCEPTING_CMD(ar))
+		return;
+
+	mutex_lock(&ar->mutex);
+
+	ar->filter_state = *new_flags;
+	/*
+	 * We can support more by setting the sniffer bit and
+	 * then checking the error flags, later.
+	 */
+
+	if (changed_flags & FIF_ALLMULTI && *new_flags & FIF_ALLMULTI)
+		multicast = ~0ULL;
+
+	if (multicast != ar->cur_mc_hash)
+		WARN_ON(carl9170_update_multicast(ar, multicast));
+
+	if (changed_flags & (FIF_OTHER_BSS | FIF_PROMISC_IN_BSS)) {
+		ar->sniffer_enabled = !!(*new_flags &
+			(FIF_OTHER_BSS | FIF_PROMISC_IN_BSS));
+
+		WARN_ON(carl9170_set_operating_mode(ar));
+	}
+
+	mutex_unlock(&ar->mutex);
+}
+
+
+static void carl9170_op_bss_info_changed(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif,
+					 struct ieee80211_bss_conf *bss_conf,
+					 u32 changed)
+{
+	struct ar9170 *ar = hw->priv;
+	struct ath_common *common = &ar->common;
+	int err = 0;
+	struct carl9170_vif_info *vif_priv;
+	struct ieee80211_vif *main_vif;
+
+	mutex_lock(&ar->mutex);
+	vif_priv = (void *) vif->drv_priv;
+	main_vif = carl9170_get_main_vif(ar);
+	if (WARN_ON(!main_vif))
+		goto out;
+
+	if (changed & BSS_CHANGED_BEACON_ENABLED) {
+		struct carl9170_vif_info *iter;
+		int i = 0;
+
+		vif_priv->enable_beacon = bss_conf->enable_beacon;
+		rcu_read_lock();
+		list_for_each_entry_rcu(iter, &ar->vif_list, list) {
+			if (iter->active && iter->enable_beacon)
+				i++;
+
+		}
+		rcu_read_unlock();
+
+		ar->beacon_enabled = i;
+	}
+
+	if (changed & BSS_CHANGED_BEACON) {
+		err = carl9170_update_beacon(ar, false);
+		if (err)
+			goto out;
+	}
+
+	if (changed & (BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_BEACON |
+		       BSS_CHANGED_BEACON_INT)) {
+
+		if (main_vif != vif) {
+			bss_conf->beacon_int = main_vif->bss_conf.beacon_int;
+			bss_conf->dtim_period = main_vif->bss_conf.dtim_period;
+		}
+
+		/*
+		 * Therefore a hard limit for the broadcast traffic should
+		 * prevent false alarms.
+		 */
+		if (vif->type != NL80211_IFTYPE_STATION &&
+		    (bss_conf->beacon_int * bss_conf->dtim_period >=
+		     (CARL9170_QUEUE_STUCK_TIMEOUT / 2))) {
+			err = -EINVAL;
+			goto out;
+		}
+
+		err = carl9170_set_beacon_timers(ar);
+		if (err)
+			goto out;
+	}
+
+	if (changed & BSS_CHANGED_HT) {
+		/* TODO */
+		err = 0;
+		if (err)
+			goto out;
+	}
+
+	if (main_vif != vif)
+		goto out;
+
+	/*
+	 * The following settings can only be changed by the
+	 * master interface.
+	 */
+
+	if (changed & BSS_CHANGED_BSSID) {
+		memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
+		err = carl9170_set_operating_mode(ar);
+		if (err)
+			goto out;
+	}
+
+	if (changed & BSS_CHANGED_ASSOC) {
+		ar->common.curaid = bss_conf->aid;
+		err = carl9170_set_beacon_timers(ar);
+		if (err)
+			goto out;
+	}
+
+	if (changed & BSS_CHANGED_ERP_SLOT) {
+		err = carl9170_set_slot_time(ar);
+		if (err)
+			goto out;
+	}
+
+	if (changed & BSS_CHANGED_BASIC_RATES) {
+		err = carl9170_set_mac_rates(ar);
+		if (err)
+			goto out;
+	}
+
+out:
+	WARN_ON_ONCE(err && IS_STARTED(ar));
+	mutex_unlock(&ar->mutex);
+}
+
+static u64 carl9170_op_get_tsf(struct ieee80211_hw *hw)
+{
+	struct ar9170 *ar = hw->priv;
+	struct carl9170_tsf_rsp tsf;
+	int err;
+
+	mutex_lock(&ar->mutex);
+	err = carl9170_exec_cmd(ar, CARL9170_CMD_READ_TSF,
+				0, NULL, sizeof(tsf), &tsf);
+	mutex_unlock(&ar->mutex);
+	if (WARN_ON(err))
+		return 0;
+
+	return le64_to_cpu(tsf.tsf_64);
+}
+
+static int carl9170_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+			       struct ieee80211_vif *vif,
+			       struct ieee80211_sta *sta,
+			       struct ieee80211_key_conf *key)
+{
+	struct ar9170 *ar = hw->priv;
+	int err = 0, i;
+	u8 ktype;
+
+	if (ar->disable_offload || !vif)
+		return -EOPNOTSUPP;
+
+	/*
+	 * We have to fall back to software encryption, whenever
+	 * the user choose to participates in an IBSS or is connected
+	 * to more than one network.
+	 *
+	 * This is very unfortunate, because some machines cannot handle
+	 * the high througput speed in 802.11n networks.
+	 */
+
+	if (!is_main_vif(ar, vif))
+		goto err_softw;
+
+	/*
+	 * While the hardware supports *catch-all* key, for offloading
+	 * group-key en-/de-cryption. The way of how the hardware
+	 * decides which keyId maps to which key, remains a mystery...
+	 */
+	if ((vif->type != NL80211_IFTYPE_STATION &&
+	     vif->type != NL80211_IFTYPE_ADHOC) &&
+	    !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
+		return -EOPNOTSUPP;
+
+	switch (key->cipher) {
+	case WLAN_CIPHER_SUITE_WEP40:
+		ktype = AR9170_ENC_ALG_WEP64;
+		break;
+	case WLAN_CIPHER_SUITE_WEP104:
+		ktype = AR9170_ENC_ALG_WEP128;
+		break;
+	case WLAN_CIPHER_SUITE_TKIP:
+		ktype = AR9170_ENC_ALG_TKIP;
+		break;
+	case WLAN_CIPHER_SUITE_CCMP:
+		ktype = AR9170_ENC_ALG_AESCCMP;
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	mutex_lock(&ar->mutex);
+	if (cmd == SET_KEY) {
+		if (!IS_STARTED(ar)) {
+			err = -EOPNOTSUPP;
+			goto out;
+		}
+
+		if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
+			sta = NULL;
+
+			i = 64 + key->keyidx;
+		} else {
+			for (i = 0; i < 64; i++)
+				if (!(ar->usedkeys & BIT(i)))
+					break;
+			if (i == 64)
+				goto err_softw;
+		}
+
+		key->hw_key_idx = i;
+
+		err = carl9170_upload_key(ar, i, sta ? sta->addr : NULL,
+					  ktype, 0, key->key,
+					  min_t(u8, 16, key->keylen));
+		if (err)
+			goto out;
+
+		if (key->cipher == WLAN_CIPHER_SUITE_TKIP) {
+			err = carl9170_upload_key(ar, i, sta ? sta->addr :
+						  NULL, ktype, 1,
+						  key->key + 16, 16);
+			if (err)
+				goto out;
+
+			/*
+			 * hardware is not capable generating MMIC
+			 * of fragmented frames!
+			 */
+			key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+		}
+
+		if (i < 64)
+			ar->usedkeys |= BIT(i);
+
+		key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+	} else {
+		if (!IS_STARTED(ar)) {
+			/* The device is gone... together with the key ;-) */
+			err = 0;
+			goto out;
+		}
+
+		if (key->hw_key_idx < 64) {
+			ar->usedkeys &= ~BIT(key->hw_key_idx);
+		} else {
+			err = carl9170_upload_key(ar, key->hw_key_idx, NULL,
+						  AR9170_ENC_ALG_NONE, 0,
+						  NULL, 0);
+			if (err)
+				goto out;
+
+			if (key->cipher == WLAN_CIPHER_SUITE_TKIP) {
+				err = carl9170_upload_key(ar, key->hw_key_idx,
+							  NULL,
+							  AR9170_ENC_ALG_NONE,
+							  1, NULL, 0);
+				if (err)
+					goto out;
+			}
+
+		}
+
+		err = carl9170_disable_key(ar, key->hw_key_idx);
+		if (err)
+			goto out;
+	}
+
+out:
+	mutex_unlock(&ar->mutex);
+	return err;
+
+err_softw:
+	if (!ar->rx_software_decryption) {
+		ar->rx_software_decryption = true;
+		carl9170_set_operating_mode(ar);
+	}
+	mutex_unlock(&ar->mutex);
+	return -ENOSPC;
+}
+
+static int carl9170_op_sta_add(struct ieee80211_hw *hw,
+			       struct ieee80211_vif *vif,
+			       struct ieee80211_sta *sta)
+{
+	struct carl9170_sta_info *sta_info = (void *) sta->drv_priv;
+	unsigned int i;
+
+	if (sta->ht_cap.ht_supported) {
+		if (sta->ht_cap.ampdu_density > 6) {
+			/*
+			 * HW does support 16us AMPDU density.
+			 * No HT-Xmit for station.
+			 */
+
+			return 0;
+		}
+
+		for (i = 0; i < CARL9170_NUM_TID; i++)
+			rcu_assign_pointer(sta_info->agg[i], NULL);
+
+		sta_info->ampdu_max_len = 1 << (3 + sta->ht_cap.ampdu_factor);
+		sta_info->ht_sta = true;
+	}
+
+	return 0;
+}
+
+static int carl9170_op_sta_remove(struct ieee80211_hw *hw,
+				struct ieee80211_vif *vif,
+				struct ieee80211_sta *sta)
+{
+	struct ar9170 *ar = hw->priv;
+	struct carl9170_sta_info *sta_info = (void *) sta->drv_priv;
+	unsigned int i;
+	bool cleanup = false;
+
+	if (sta->ht_cap.ht_supported) {
+
+		sta_info->ht_sta = false;
+
+		rcu_read_lock();
+		for (i = 0; i < CARL9170_NUM_TID; i++) {
+			struct carl9170_sta_tid *tid_info;
+
+			tid_info = rcu_dereference(sta_info->agg[i]);
+			rcu_assign_pointer(sta_info->agg[i], NULL);
+
+			if (!tid_info)
+				continue;
+
+			spin_lock_bh(&ar->tx_ampdu_list_lock);
+			if (tid_info->state > CARL9170_TID_STATE_SHUTDOWN)
+				tid_info->state = CARL9170_TID_STATE_SHUTDOWN;
+			spin_unlock_bh(&ar->tx_ampdu_list_lock);
+			cleanup = true;
+		}
+		rcu_read_unlock();
+
+		if (cleanup)
+			carl9170_ampdu_gc(ar);
+	}
+
+	return 0;
+}
+
+static int carl9170_op_conf_tx(struct ieee80211_hw *hw, u16 queue,
+			       const struct ieee80211_tx_queue_params *param)
+{
+	struct ar9170 *ar = hw->priv;
+	int ret;
+
+	mutex_lock(&ar->mutex);
+	if (queue < ar->hw->queues) {
+		memcpy(&ar->edcf[ar9170_qmap[queue]], param, sizeof(*param));
+		ret = carl9170_set_qos(ar);
+	} else {
+		ret = -EINVAL;
+	}
+
+	mutex_unlock(&ar->mutex);
+	return ret;
+}
+
+static void carl9170_ampdu_work(struct work_struct *work)
+{
+	struct ar9170 *ar = container_of(work, struct ar9170,
+					 ampdu_work);
+
+	if (!IS_STARTED(ar))
+		return;
+
+	mutex_lock(&ar->mutex);
+	carl9170_ampdu_gc(ar);
+	mutex_unlock(&ar->mutex);
+}
+
+static int carl9170_op_ampdu_action(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif,
+				    enum ieee80211_ampdu_mlme_action action,
+				    struct ieee80211_sta *sta,
+				    u16 tid, u16 *ssn)
+{
+	struct ar9170 *ar = hw->priv;
+	struct carl9170_sta_info *sta_info = (void *) sta->drv_priv;
+	struct carl9170_sta_tid *tid_info;
+
+	if (modparam_noht)
+		return -EOPNOTSUPP;
+
+	switch (action) {
+	case IEEE80211_AMPDU_TX_START:
+		if (WARN_ON_ONCE(!sta_info->ht_sta))
+			return -EOPNOTSUPP;
+
+		rcu_read_lock();
+		if (rcu_dereference(sta_info->agg[tid])) {
+			rcu_read_unlock();
+			return -EBUSY;
+		}
+
+		tid_info = kzalloc(sizeof(struct carl9170_sta_tid),
+				   GFP_ATOMIC);
+		if (!tid_info) {
+			rcu_read_unlock();
+			return -ENOMEM;
+		}
+
+		tid_info->hsn = tid_info->bsn = tid_info->snx = (*ssn);
+		tid_info->state = CARL9170_TID_STATE_PROGRESS;
+		tid_info->tid = tid;
+		tid_info->max = sta_info->ampdu_max_len;
+
+		INIT_LIST_HEAD(&tid_info->list);
+		INIT_LIST_HEAD(&tid_info->tmp_list);
+		skb_queue_head_init(&tid_info->queue);
+		spin_lock_init(&tid_info->lock);
+
+		spin_lock_bh(&ar->tx_ampdu_list_lock);
+		ar->tx_ampdu_list_len++;
+		list_add_tail_rcu(&tid_info->list, &ar->tx_ampdu_list);
+		rcu_assign_pointer(sta_info->agg[tid], tid_info);
+		spin_unlock_bh(&ar->tx_ampdu_list_lock);
+		rcu_read_unlock();
+
+		ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+		break;
+
+	case IEEE80211_AMPDU_TX_STOP:
+		rcu_read_lock();
+		tid_info = rcu_dereference(sta_info->agg[tid]);
+		if (tid_info) {
+			spin_lock_bh(&ar->tx_ampdu_list_lock);
+			if (tid_info->state > CARL9170_TID_STATE_SHUTDOWN)
+				tid_info->state = CARL9170_TID_STATE_SHUTDOWN;
+			spin_unlock_bh(&ar->tx_ampdu_list_lock);
+		}
+
+		rcu_assign_pointer(sta_info->agg[tid], NULL);
+		rcu_read_unlock();
+
+		ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+		ieee80211_queue_work(ar->hw, &ar->ampdu_work);
+		break;
+
+	case IEEE80211_AMPDU_TX_OPERATIONAL:
+		rcu_read_lock();
+		tid_info = rcu_dereference(sta_info->agg[tid]);
+
+		sta_info->stats[tid].clear = true;
+
+		if (tid_info) {
+			bitmap_zero(tid_info->bitmap, CARL9170_BAW_SIZE);
+			tid_info->state = CARL9170_TID_STATE_IDLE;
+		}
+		rcu_read_unlock();
+
+		if (WARN_ON_ONCE(!tid_info))
+			return -EFAULT;
+
+		break;
+
+	case IEEE80211_AMPDU_RX_START:
+	case IEEE80211_AMPDU_RX_STOP:
+		/* Handled by hardware */
+		break;
+
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_CARL9170_WPC
+static int carl9170_register_wps_button(struct ar9170 *ar)
+{
+	struct input_dev *input;
+	int err;
+
+	if (!(ar->features & CARL9170_WPS_BUTTON))
+		return 0;
+
+	input = input_allocate_device();
+	if (!input)
+		return -ENOMEM;
+
+	snprintf(ar->wps.name, sizeof(ar->wps.name), "%s WPS Button",
+		 wiphy_name(ar->hw->wiphy));
+
+	snprintf(ar->wps.phys, sizeof(ar->wps.phys),
+		 "ieee80211/%s/input0", wiphy_name(ar->hw->wiphy));
+
+	input->name = ar->wps.name;
+	input->phys = ar->wps.phys;
+	input->id.bustype = BUS_USB;
+	input->dev.parent = &ar->hw->wiphy->dev;
+
+	input_set_capability(input, EV_KEY, KEY_WPS_BUTTON);
+
+	err = input_register_device(input);
+	if (err) {
+		input_free_device(input);
+		return err;
+	}
+
+	ar->wps.pbc = input;
+	return 0;
+}
+#endif /* CONFIG_CARL9170_WPC */
+
+static int carl9170_op_get_survey(struct ieee80211_hw *hw, int idx,
+				struct survey_info *survey)
+{
+	struct ar9170 *ar = hw->priv;
+	int err;
+
+	if (idx != 0)
+		return -ENOENT;
+
+	mutex_lock(&ar->mutex);
+	err = carl9170_get_noisefloor(ar);
+	mutex_unlock(&ar->mutex);
+	if (err)
+		return err;
+
+	survey->channel = ar->channel;
+	survey->filled = SURVEY_INFO_NOISE_DBM;
+	survey->noise = ar->noise[0];
+	return 0;
+}
+
+static void carl9170_op_flush(struct ieee80211_hw *hw, bool drop)
+{
+	struct ar9170 *ar = hw->priv;
+	unsigned int vid;
+
+	mutex_lock(&ar->mutex);
+	for_each_set_bit(vid, &ar->vif_bitmap, ar->fw.vif_num)
+		carl9170_flush_cab(ar, vid);
+
+	carl9170_flush(ar, drop);
+	mutex_unlock(&ar->mutex);
+}
+
+static int carl9170_op_get_stats(struct ieee80211_hw *hw,
+				 struct ieee80211_low_level_stats *stats)
+{
+	struct ar9170 *ar = hw->priv;
+
+	memset(stats, 0, sizeof(*stats));
+	stats->dot11ACKFailureCount = ar->tx_ack_failures;
+	stats->dot11FCSErrorCount = ar->tx_fcs_errors;
+	return 0;
+}
+
+static void carl9170_op_sta_notify(struct ieee80211_hw *hw,
+				   struct ieee80211_vif *vif,
+				   enum sta_notify_cmd cmd,
+				   struct ieee80211_sta *sta)
+{
+	struct ar9170 *ar = hw->priv;
+	struct carl9170_sta_info *sta_info = (void *) sta->drv_priv;
+	struct sk_buff *skb, *tmp;
+	struct sk_buff_head free;
+	int i;
+
+	switch (cmd) {
+	case STA_NOTIFY_SLEEP:
+		/*
+		 * Since the peer is no longer listening, we have to return
+		 * as many SKBs as possible back to the mac80211 stack.
+		 * It will deal with the retry procedure, once the peer
+		 * has become available again.
+		 *
+		 * NB: Ideally, the driver should return the all frames in
+		 * the correct, ascending order. However, I think that this
+		 * functionality should be implemented in the stack and not
+		 * here...
+		 */
+
+		__skb_queue_head_init(&free);
+
+		if (sta->ht_cap.ht_supported) {
+			rcu_read_lock();
+			for (i = 0; i < CARL9170_NUM_TID; i++) {
+				struct carl9170_sta_tid *tid_info;
+
+				tid_info = rcu_dereference(sta_info->agg[i]);
+
+				if (!tid_info)
+					continue;
+
+				spin_lock_bh(&ar->tx_ampdu_list_lock);
+				if (tid_info->state >
+				    CARL9170_TID_STATE_SUSPEND)
+					tid_info->state =
+						CARL9170_TID_STATE_SUSPEND;
+				spin_unlock_bh(&ar->tx_ampdu_list_lock);
+
+				spin_lock_bh(&tid_info->lock);
+				while ((skb = __skb_dequeue(&tid_info->queue)))
+					__skb_queue_tail(&free, skb);
+				spin_unlock_bh(&tid_info->lock);
+
+				ieee80211_stop_tx_ba_session(sta,
+					tid_info->tid);
+			}
+			rcu_read_unlock();
+		}
+
+		for (i = 0; i < ar->hw->queues; i++) {
+			spin_lock_bh(&ar->tx_pending[i].lock);
+			skb_queue_walk_safe(&ar->tx_pending[i], skb, tmp) {
+				struct _carl9170_tx_superframe *super;
+				struct ieee80211_hdr *hdr;
+
+				super = (void *) skb->data;
+				hdr = (void *) super->frame_data;
+
+				if (compare_ether_addr(hdr->addr1, sta->addr))
+					continue;
+
+				__skb_unlink(skb, &ar->tx_pending[i]);
+				carl9170_tx_status(ar, skb, false);
+			}
+			spin_unlock_bh(&ar->tx_pending[i].lock);
+		}
+
+		while ((skb = __skb_dequeue(&free)))
+			carl9170_tx_status(ar, skb, false);
+
+		break;
+
+	case STA_NOTIFY_AWAKE:
+		if (!sta->ht_cap.ht_supported)
+			return;
+
+		rcu_read_lock();
+		for (i = 0; i < CARL9170_NUM_TID; i++) {
+			struct carl9170_sta_tid *tid_info;
+
+			tid_info = rcu_dereference(sta_info->agg[i]);
+
+			if (!tid_info)
+				continue;
+
+			if ((tid_info->state == CARL9170_TID_STATE_SUSPEND))
+				tid_info->state = CARL9170_TID_STATE_IDLE;
+		}
+		rcu_read_unlock();
+		break;
+	}
+}
+
+static const struct ieee80211_ops carl9170_ops = {
+	.start			= carl9170_op_start,
+	.stop			= carl9170_op_stop,
+	.tx			= carl9170_op_tx,
+	.flush			= carl9170_op_flush,
+	.add_interface		= carl9170_op_add_interface,
+	.remove_interface	= carl9170_op_remove_interface,
+	.config			= carl9170_op_config,
+	.prepare_multicast	= carl9170_op_prepare_multicast,
+	.configure_filter	= carl9170_op_configure_filter,
+	.conf_tx		= carl9170_op_conf_tx,
+	.bss_info_changed	= carl9170_op_bss_info_changed,
+	.get_tsf		= carl9170_op_get_tsf,
+	.set_key		= carl9170_op_set_key,
+	.sta_add		= carl9170_op_sta_add,
+	.sta_remove		= carl9170_op_sta_remove,
+	.sta_notify		= carl9170_op_sta_notify,
+	.get_survey		= carl9170_op_get_survey,
+	.get_stats		= carl9170_op_get_stats,
+	.ampdu_action		= carl9170_op_ampdu_action,
+};
+
+void *carl9170_alloc(size_t priv_size)
+{
+	struct ieee80211_hw *hw;
+	struct ar9170 *ar;
+	struct sk_buff *skb;
+	int i;
+
+	/*
+	 * this buffer is used for rx stream reconstruction.
+	 * Under heavy load this device (or the transport layer?)
+	 * tends to split the streams into separate rx descriptors.
+	 */
+
+	skb = __dev_alloc_skb(AR9170_RX_STREAM_MAX_SIZE, GFP_KERNEL);
+	if (!skb)
+		goto err_nomem;
+
+	hw = ieee80211_alloc_hw(priv_size, &carl9170_ops);
+	if (!hw)
+		goto err_nomem;
+
+	ar = hw->priv;
+	ar->hw = hw;
+	ar->rx_failover = skb;
+
+	memset(&ar->rx_plcp, 0, sizeof(struct ar9170_rx_head));
+	ar->rx_has_plcp = false;
+
+	/*
+	 * Here's a hidden pitfall!
+	 *
+	 * All 4 AC queues work perfectly well under _legacy_ operation.
+	 * However as soon as aggregation is enabled, the traffic flow
+	 * gets very bumpy. Therefore we have to _switch_ to a
+	 * software AC with a single HW queue.
+	 */
+	hw->queues = __AR9170_NUM_TXQ;
+
+	mutex_init(&ar->mutex);
+	spin_lock_init(&ar->beacon_lock);
+	spin_lock_init(&ar->cmd_lock);
+	spin_lock_init(&ar->tx_stats_lock);
+	spin_lock_init(&ar->tx_ampdu_list_lock);
+	spin_lock_init(&ar->mem_lock);
+	spin_lock_init(&ar->state_lock);
+	atomic_set(&ar->pending_restarts, 0);
+	ar->vifs = 0;
+	for (i = 0; i < ar->hw->queues; i++) {
+		skb_queue_head_init(&ar->tx_status[i]);
+		skb_queue_head_init(&ar->tx_pending[i]);
+	}
+	INIT_WORK(&ar->ps_work, carl9170_ps_work);
+	INIT_WORK(&ar->restart_work, carl9170_restart_work);
+	INIT_WORK(&ar->ampdu_work, carl9170_ampdu_work);
+	INIT_DELAYED_WORK(&ar->tx_janitor, carl9170_tx_janitor);
+	INIT_LIST_HEAD(&ar->tx_ampdu_list);
+	rcu_assign_pointer(ar->tx_ampdu_iter,
+			   (struct carl9170_sta_tid *) &ar->tx_ampdu_list);
+
+	bitmap_zero(&ar->vif_bitmap, ar->fw.vif_num);
+	INIT_LIST_HEAD(&ar->vif_list);
+	init_completion(&ar->tx_flush);
+
+	/*
+	 * Note:
+	 * IBSS/ADHOC and AP mode are only enabled, if the firmware
+	 * supports these modes. The code which will add the
+	 * additional interface_modes is in fw.c.
+	 */
+	hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+
+	hw->flags |= IEEE80211_HW_RX_INCLUDES_FCS |
+		     IEEE80211_HW_REPORTS_TX_ACK_STATUS |
+		     IEEE80211_HW_SUPPORTS_PS |
+		     IEEE80211_HW_PS_NULLFUNC_STACK |
+		     IEEE80211_HW_SIGNAL_DBM;
+
+	if (!modparam_noht) {
+		/*
+		 * see the comment above, why we allow the user
+		 * to disable HT by a module parameter.
+		 */
+		hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
+	}
+
+	hw->extra_tx_headroom = sizeof(struct _carl9170_tx_superframe);
+	hw->sta_data_size = sizeof(struct carl9170_sta_info);
+	hw->vif_data_size = sizeof(struct carl9170_vif_info);
+
+	hw->max_rates = CARL9170_TX_MAX_RATES;
+	hw->max_rate_tries = CARL9170_TX_USER_RATE_TRIES;
+
+	for (i = 0; i < ARRAY_SIZE(ar->noise); i++)
+		ar->noise[i] = -95; /* ATH_DEFAULT_NOISE_FLOOR */
+
+	hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+	return ar;
+
+err_nomem:
+	kfree_skb(skb);
+	return ERR_PTR(-ENOMEM);
+}
+
+static int carl9170_read_eeprom(struct ar9170 *ar)
+{
+#define RW	8	/* number of words to read at once */
+#define RB	(sizeof(u32) * RW)
+	u8 *eeprom = (void *)&ar->eeprom;
+	__le32 offsets[RW];
+	int i, j, err;
+
+	BUILD_BUG_ON(sizeof(ar->eeprom) & 3);
+
+	BUILD_BUG_ON(RB > CARL9170_MAX_CMD_LEN - 4);
+#ifndef __CHECKER__
+	/* don't want to handle trailing remains */
+	BUILD_BUG_ON(sizeof(ar->eeprom) % RB);
+#endif
+
+	for (i = 0; i < sizeof(ar->eeprom)/RB; i++) {
+		for (j = 0; j < RW; j++)
+			offsets[j] = cpu_to_le32(AR9170_EEPROM_START +
+						 RB * i + 4 * j);
+
+		err = carl9170_exec_cmd(ar, CARL9170_CMD_RREG,
+					RB, (u8 *) &offsets,
+					RB, eeprom + RB * i);
+		if (err)
+			return err;
+	}
+
+#undef RW
+#undef RB
+	return 0;
+}
+
+static int carl9170_parse_eeprom(struct ar9170 *ar)
+{
+	struct ath_regulatory *regulatory = &ar->common.regulatory;
+	unsigned int rx_streams, tx_streams, tx_params = 0;
+	int bands = 0;
+
+	if (ar->eeprom.length == cpu_to_le16(0xffff))
+		return -ENODATA;
+
+	rx_streams = hweight8(ar->eeprom.rx_mask);
+	tx_streams = hweight8(ar->eeprom.tx_mask);
+
+	if (rx_streams != tx_streams) {
+		tx_params = IEEE80211_HT_MCS_TX_RX_DIFF;
+
+		WARN_ON(!(tx_streams >= 1 && tx_streams <=
+			IEEE80211_HT_MCS_TX_MAX_STREAMS));
+
+		tx_params = (tx_streams - 1) <<
+			    IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT;
+
+		carl9170_band_2GHz.ht_cap.mcs.tx_params |= tx_params;
+		carl9170_band_5GHz.ht_cap.mcs.tx_params |= tx_params;
+	}
+
+	if (ar->eeprom.operating_flags & AR9170_OPFLAG_2GHZ) {
+		ar->hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
+			&carl9170_band_2GHz;
+		bands++;
+	}
+	if (ar->eeprom.operating_flags & AR9170_OPFLAG_5GHZ) {
+		ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
+			&carl9170_band_5GHz;
+		bands++;
+	}
+
+	/*
+	 * I measured this, a bandswitch takes roughly
+	 * 135 ms and a frequency switch about 80.
+	 *
+	 * FIXME: measure these values again once EEPROM settings
+	 *	  are used, that will influence them!
+	 */
+	if (bands == 2)
+		ar->hw->channel_change_time = 135 * 1000;
+	else
+		ar->hw->channel_change_time = 80 * 1000;
+
+	regulatory->current_rd = le16_to_cpu(ar->eeprom.reg_domain[0]);
+	regulatory->current_rd_ext = le16_to_cpu(ar->eeprom.reg_domain[1]);
+
+	/* second part of wiphy init */
+	SET_IEEE80211_PERM_ADDR(ar->hw, ar->eeprom.mac_address);
+
+	return bands ? 0 : -EINVAL;
+}
+
+static int carl9170_reg_notifier(struct wiphy *wiphy,
+				 struct regulatory_request *request)
+{
+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+	struct ar9170 *ar = hw->priv;
+
+	return ath_reg_notifier_apply(wiphy, request, &ar->common.regulatory);
+}
+
+int carl9170_register(struct ar9170 *ar)
+{
+	struct ath_regulatory *regulatory = &ar->common.regulatory;
+	int err = 0, i;
+
+	if (WARN_ON(ar->mem_bitmap))
+		return -EINVAL;
+
+	ar->mem_bitmap = kzalloc(roundup(ar->fw.mem_blocks, BITS_PER_LONG) *
+				 sizeof(unsigned long), GFP_KERNEL);
+
+	if (!ar->mem_bitmap)
+		return -ENOMEM;
+
+	/* try to read EEPROM, init MAC addr */
+	err = carl9170_read_eeprom(ar);
+	if (err)
+		return err;
+
+	err = carl9170_fw_fix_eeprom(ar);
+	if (err)
+		return err;
+
+	err = carl9170_parse_eeprom(ar);
+	if (err)
+		return err;
+
+	err = ath_regd_init(regulatory, ar->hw->wiphy,
+			    carl9170_reg_notifier);
+	if (err)
+		return err;
+
+	if (modparam_noht) {
+		carl9170_band_2GHz.ht_cap.ht_supported = false;
+		carl9170_band_5GHz.ht_cap.ht_supported = false;
+	}
+
+	for (i = 0; i < ar->fw.vif_num; i++) {
+		ar->vif_priv[i].id = i;
+		ar->vif_priv[i].vif = NULL;
+	}
+
+	err = ieee80211_register_hw(ar->hw);
+	if (err)
+		return err;
+
+	/* mac80211 interface is now registered */
+	ar->registered = true;
+
+	if (!ath_is_world_regd(regulatory))
+		regulatory_hint(ar->hw->wiphy, regulatory->alpha2);
+
+#ifdef CONFIG_CARL9170_DEBUGFS
+	carl9170_debugfs_register(ar);
+#endif /* CONFIG_CARL9170_DEBUGFS */
+
+	err = carl9170_led_init(ar);
+	if (err)
+		goto err_unreg;
+
+#ifdef CONFIG_CARL9170_LEDS
+	err = carl9170_led_register(ar);
+	if (err)
+		goto err_unreg;
+#endif /* CONFIG_CAR9L170_LEDS */
+
+#ifdef CONFIG_CARL9170_WPC
+	err = carl9170_register_wps_button(ar);
+	if (err)
+		goto err_unreg;
+#endif /* CONFIG_CARL9170_WPC */
+
+	dev_info(&ar->udev->dev, "Atheros AR9170 is registered as '%s'\n",
+		 wiphy_name(ar->hw->wiphy));
+
+	return 0;
+
+err_unreg:
+	carl9170_unregister(ar);
+	return err;
+}
+
+void carl9170_unregister(struct ar9170 *ar)
+{
+	if (!ar->registered)
+		return;
+
+	ar->registered = false;
+
+#ifdef CONFIG_CARL9170_LEDS
+	carl9170_led_unregister(ar);
+#endif /* CONFIG_CARL9170_LEDS */
+
+#ifdef CONFIG_CARL9170_DEBUGFS
+	carl9170_debugfs_unregister(ar);
+#endif /* CONFIG_CARL9170_DEBUGFS */
+
+#ifdef CONFIG_CARL9170_WPC
+	if (ar->wps.pbc) {
+		input_unregister_device(ar->wps.pbc);
+		ar->wps.pbc = NULL;
+	}
+#endif /* CONFIG_CARL9170_WPC */
+
+	carl9170_cancel_worker(ar);
+	cancel_work_sync(&ar->restart_work);
+
+	ieee80211_unregister_hw(ar->hw);
+}
+
+void carl9170_free(struct ar9170 *ar)
+{
+	WARN_ON(ar->registered);
+	WARN_ON(IS_INITIALIZED(ar));
+
+	kfree_skb(ar->rx_failover);
+	ar->rx_failover = NULL;
+
+	kfree(ar->mem_bitmap);
+	ar->mem_bitmap = NULL;
+
+	mutex_destroy(&ar->mutex);
+
+	ieee80211_free_hw(ar->hw);
+}
diff --git a/drivers/net/wireless/ath/carl9170/phy.c b/drivers/net/wireless/ath/carl9170/phy.c
new file mode 100644
index 0000000..89deca3
--- /dev/null
+++ b/drivers/net/wireless/ath/carl9170/phy.c
@@ -0,0 +1,1810 @@
+/*
+ * Atheros CARL9170 driver
+ *
+ * PHY and RF code
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This program is free software; you can redistribute it 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 program 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or distribute this software for any
+ *    purpose with or without fee is hereby granted, provided that the above
+ *    copyright notice and this permission notice appear in all copies.
+ *
+ *    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/bitrev.h>
+#include "carl9170.h"
+#include "cmd.h"
+#include "phy.h"
+
+static int carl9170_init_power_cal(struct ar9170 *ar)
+{
+	carl9170_regwrite_begin(ar);
+
+	carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE_MAX, 0x7f);
+	carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE1, 0x3f3f3f3f);
+	carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE2, 0x3f3f3f3f);
+	carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE3, 0x3f3f3f3f);
+	carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE4, 0x3f3f3f3f);
+	carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE5, 0x3f3f3f3f);
+	carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE6, 0x3f3f3f3f);
+	carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE7, 0x3f3f3f3f);
+	carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE8, 0x3f3f3f3f);
+	carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE9, 0x3f3f3f3f);
+
+	carl9170_regwrite_finish();
+	return carl9170_regwrite_result();
+}
+
+struct carl9170_phy_init {
+	u32 reg, _5ghz_20, _5ghz_40, _2ghz_40, _2ghz_20;
+};
+
+static struct carl9170_phy_init ar5416_phy_init[] = {
+	{ 0x1c5800, 0x00000007, 0x00000007, 0x00000007, 0x00000007, },
+	{ 0x1c5804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, },
+	{ 0x1c5808, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c580c, 0xad848e19, 0xad848e19, 0xad848e19, 0xad848e19, },
+	{ 0x1c5810, 0x7d14e000, 0x7d14e000, 0x7d14e000, 0x7d14e000, },
+	{ 0x1c5814, 0x9c0a9f6b, 0x9c0a9f6b, 0x9c0a9f6b, 0x9c0a9f6b, },
+	{ 0x1c5818, 0x00000090, 0x00000090, 0x00000090, 0x00000090, },
+	{ 0x1c581c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, },
+	{ 0x1c5824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, },
+	{ 0x1c5828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, },
+	{ 0x1c582c, 0x0000a000, 0x0000a000, 0x0000a000, 0x0000a000, },
+	{ 0x1c5830, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, },
+	{ 0x1c5838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, },
+	{ 0x1c583c, 0x00200400, 0x00200400, 0x00200400, 0x00200400, },
+	{ 0x1c5840, 0x206a002e, 0x206a002e, 0x206a002e, 0x206a002e, },
+	{ 0x1c5844, 0x1372161e, 0x13721c1e, 0x13721c24, 0x137216a4, },
+	{ 0x1c5848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, },
+	{ 0x1c584c, 0x1284233c, 0x1284233c, 0x1284233c, 0x1284233c, },
+	{ 0x1c5850, 0x6c48b4e4, 0x6d48b4e4, 0x6d48b0e4, 0x6c48b0e4, },
+	{ 0x1c5854, 0x00000859, 0x00000859, 0x00000859, 0x00000859, },
+	{ 0x1c5858, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, },
+	{ 0x1c585c, 0x31395c5e, 0x3139605e, 0x3139605e, 0x31395c5e, },
+	{ 0x1c5860, 0x0004dd10, 0x0004dd10, 0x0004dd20, 0x0004dd20, },
+	{ 0x1c5864, 0x0001c600, 0x0001c600, 0x0001c600, 0x0001c600, },
+	{ 0x1c5868, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190, },
+	{ 0x1c586c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, },
+	{ 0x1c5900, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5904, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5908, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c590c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5914, 0x000007d0, 0x000007d0, 0x00000898, 0x00000898, },
+	{ 0x1c5918, 0x00000118, 0x00000230, 0x00000268, 0x00000134, },
+	{ 0x1c591c, 0x10000fff, 0x10000fff, 0x10000fff, 0x10000fff, },
+	{ 0x1c5920, 0x0510081c, 0x0510081c, 0x0510001c, 0x0510001c, },
+	{ 0x1c5924, 0xd0058a15, 0xd0058a15, 0xd0058a15, 0xd0058a15, },
+	{ 0x1c5928, 0x00000001, 0x00000001, 0x00000001, 0x00000001, },
+	{ 0x1c592c, 0x00000004, 0x00000004, 0x00000004, 0x00000004, },
+	{ 0x1c5934, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+	{ 0x1c5938, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+	{ 0x1c593c, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, },
+	{ 0x1c5944, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020, },
+	{ 0x1c5948, 0x9280b212, 0x9280b212, 0x9280b212, 0x9280b212, },
+	{ 0x1c594c, 0x00020028, 0x00020028, 0x00020028, 0x00020028, },
+	{ 0x1c5954, 0x5d50e188, 0x5d50e188, 0x5d50e188, 0x5d50e188, },
+	{ 0x1c5958, 0x00081fff, 0x00081fff, 0x00081fff, 0x00081fff, },
+	{ 0x1c5960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, },
+	{ 0x1c5964, 0x00001120, 0x00001120, 0x00001120, 0x00001120, },
+	{ 0x1c5970, 0x190fb515, 0x190fb515, 0x190fb515, 0x190fb515, },
+	{ 0x1c5974, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5978, 0x00000001, 0x00000001, 0x00000001, 0x00000001, },
+	{ 0x1c597c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5980, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5984, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5988, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c598c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5990, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5994, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5998, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c599c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c59a0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c59a4, 0x00000007, 0x00000007, 0x00000007, 0x00000007, },
+	{ 0x1c59a8, 0x001fff00, 0x001fff00, 0x001fff00, 0x001fff00, },
+	{ 0x1c59ac, 0x006f00c4, 0x006f00c4, 0x006f00c4, 0x006f00c4, },
+	{ 0x1c59b0, 0x03051000, 0x03051000, 0x03051000, 0x03051000, },
+	{ 0x1c59b4, 0x00000820, 0x00000820, 0x00000820, 0x00000820, },
+	{ 0x1c59bc, 0x00181400, 0x00181400, 0x00181400, 0x00181400, },
+	{ 0x1c59c0, 0x038919be, 0x038919be, 0x038919be, 0x038919be, },
+	{ 0x1c59c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, },
+	{ 0x1c59c8, 0x6af6532c, 0x6af6532c, 0x6af6532c, 0x6af6532c, },
+	{ 0x1c59cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, },
+	{ 0x1c59d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, },
+	{ 0x1c59d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c59d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c59dc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c59e0, 0x00000200, 0x00000200, 0x00000200, 0x00000200, },
+	{ 0x1c59e4, 0x64646464, 0x64646464, 0x64646464, 0x64646464, },
+	{ 0x1c59e8, 0x3c787878, 0x3c787878, 0x3c787878, 0x3c787878, },
+	{ 0x1c59ec, 0x000000aa, 0x000000aa, 0x000000aa, 0x000000aa, },
+	{ 0x1c59f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c59fc, 0x00001042, 0x00001042, 0x00001042, 0x00001042, },
+	{ 0x1c5a00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5a04, 0x00000040, 0x00000040, 0x00000040, 0x00000040, },
+	{ 0x1c5a08, 0x00000080, 0x00000080, 0x00000080, 0x00000080, },
+	{ 0x1c5a0c, 0x000001a1, 0x000001a1, 0x00000141, 0x00000141, },
+	{ 0x1c5a10, 0x000001e1, 0x000001e1, 0x00000181, 0x00000181, },
+	{ 0x1c5a14, 0x00000021, 0x00000021, 0x000001c1, 0x000001c1, },
+	{ 0x1c5a18, 0x00000061, 0x00000061, 0x00000001, 0x00000001, },
+	{ 0x1c5a1c, 0x00000168, 0x00000168, 0x00000041, 0x00000041, },
+	{ 0x1c5a20, 0x000001a8, 0x000001a8, 0x000001a8, 0x000001a8, },
+	{ 0x1c5a24, 0x000001e8, 0x000001e8, 0x000001e8, 0x000001e8, },
+	{ 0x1c5a28, 0x00000028, 0x00000028, 0x00000028, 0x00000028, },
+	{ 0x1c5a2c, 0x00000068, 0x00000068, 0x00000068, 0x00000068, },
+	{ 0x1c5a30, 0x00000189, 0x00000189, 0x000000a8, 0x000000a8, },
+	{ 0x1c5a34, 0x000001c9, 0x000001c9, 0x00000169, 0x00000169, },
+	{ 0x1c5a38, 0x00000009, 0x00000009, 0x000001a9, 0x000001a9, },
+	{ 0x1c5a3c, 0x00000049, 0x00000049, 0x000001e9, 0x000001e9, },
+	{ 0x1c5a40, 0x00000089, 0x00000089, 0x00000029, 0x00000029, },
+	{ 0x1c5a44, 0x00000170, 0x00000170, 0x00000069, 0x00000069, },
+	{ 0x1c5a48, 0x000001b0, 0x000001b0, 0x00000190, 0x00000190, },
+	{ 0x1c5a4c, 0x000001f0, 0x000001f0, 0x000001d0, 0x000001d0, },
+	{ 0x1c5a50, 0x00000030, 0x00000030, 0x00000010, 0x00000010, },
+	{ 0x1c5a54, 0x00000070, 0x00000070, 0x00000050, 0x00000050, },
+	{ 0x1c5a58, 0x00000191, 0x00000191, 0x00000090, 0x00000090, },
+	{ 0x1c5a5c, 0x000001d1, 0x000001d1, 0x00000151, 0x00000151, },
+	{ 0x1c5a60, 0x00000011, 0x00000011, 0x00000191, 0x00000191, },
+	{ 0x1c5a64, 0x00000051, 0x00000051, 0x000001d1, 0x000001d1, },
+	{ 0x1c5a68, 0x00000091, 0x00000091, 0x00000011, 0x00000011, },
+	{ 0x1c5a6c, 0x000001b8, 0x000001b8, 0x00000051, 0x00000051, },
+	{ 0x1c5a70, 0x000001f8, 0x000001f8, 0x00000198, 0x00000198, },
+	{ 0x1c5a74, 0x00000038, 0x00000038, 0x000001d8, 0x000001d8, },
+	{ 0x1c5a78, 0x00000078, 0x00000078, 0x00000018, 0x00000018, },
+	{ 0x1c5a7c, 0x00000199, 0x00000199, 0x00000058, 0x00000058, },
+	{ 0x1c5a80, 0x000001d9, 0x000001d9, 0x00000098, 0x00000098, },
+	{ 0x1c5a84, 0x00000019, 0x00000019, 0x00000159, 0x00000159, },
+	{ 0x1c5a88, 0x00000059, 0x00000059, 0x00000199, 0x00000199, },
+	{ 0x1c5a8c, 0x00000099, 0x00000099, 0x000001d9, 0x000001d9, },
+	{ 0x1c5a90, 0x000000d9, 0x000000d9, 0x00000019, 0x00000019, },
+	{ 0x1c5a94, 0x000000f9, 0x000000f9, 0x00000059, 0x00000059, },
+	{ 0x1c5a98, 0x000000f9, 0x000000f9, 0x00000099, 0x00000099, },
+	{ 0x1c5a9c, 0x000000f9, 0x000000f9, 0x000000d9, 0x000000d9, },
+	{ 0x1c5aa0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+	{ 0x1c5aa4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+	{ 0x1c5aa8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+	{ 0x1c5aac, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+	{ 0x1c5ab0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+	{ 0x1c5ab4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+	{ 0x1c5ab8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+	{ 0x1c5abc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+	{ 0x1c5ac0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+	{ 0x1c5ac4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+	{ 0x1c5ac8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+	{ 0x1c5acc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+	{ 0x1c5ad0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+	{ 0x1c5ad4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+	{ 0x1c5ad8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+	{ 0x1c5adc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+	{ 0x1c5ae0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+	{ 0x1c5ae4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+	{ 0x1c5ae8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+	{ 0x1c5aec, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+	{ 0x1c5af0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+	{ 0x1c5af4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+	{ 0x1c5af8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+	{ 0x1c5afc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+	{ 0x1c5b00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5b04, 0x00000001, 0x00000001, 0x00000001, 0x00000001, },
+	{ 0x1c5b08, 0x00000002, 0x00000002, 0x00000002, 0x00000002, },
+	{ 0x1c5b0c, 0x00000003, 0x00000003, 0x00000003, 0x00000003, },
+	{ 0x1c5b10, 0x00000004, 0x00000004, 0x00000004, 0x00000004, },
+	{ 0x1c5b14, 0x00000005, 0x00000005, 0x00000005, 0x00000005, },
+	{ 0x1c5b18, 0x00000008, 0x00000008, 0x00000008, 0x00000008, },
+	{ 0x1c5b1c, 0x00000009, 0x00000009, 0x00000009, 0x00000009, },
+	{ 0x1c5b20, 0x0000000a, 0x0000000a, 0x0000000a, 0x0000000a, },
+	{ 0x1c5b24, 0x0000000b, 0x0000000b, 0x0000000b, 0x0000000b, },
+	{ 0x1c5b28, 0x0000000c, 0x0000000c, 0x0000000c, 0x0000000c, },
+	{ 0x1c5b2c, 0x0000000d, 0x0000000d, 0x0000000d, 0x0000000d, },
+	{ 0x1c5b30, 0x00000010, 0x00000010, 0x00000010, 0x00000010, },
+	{ 0x1c5b34, 0x00000011, 0x00000011, 0x00000011, 0x00000011, },
+	{ 0x1c5b38, 0x00000012, 0x00000012, 0x00000012, 0x00000012, },
+	{ 0x1c5b3c, 0x00000013, 0x00000013, 0x00000013, 0x00000013, },
+	{ 0x1c5b40, 0x00000014, 0x00000014, 0x00000014, 0x00000014, },
+	{ 0x1c5b44, 0x00000015, 0x00000015, 0x00000015, 0x00000015, },
+	{ 0x1c5b48, 0x00000018, 0x00000018, 0x00000018, 0x00000018, },
+	{ 0x1c5b4c, 0x00000019, 0x00000019, 0x00000019, 0x00000019, },
+	{ 0x1c5b50, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a, },
+	{ 0x1c5b54, 0x0000001b, 0x0000001b, 0x0000001b, 0x0000001b, },
+	{ 0x1c5b58, 0x0000001c, 0x0000001c, 0x0000001c, 0x0000001c, },
+	{ 0x1c5b5c, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, },
+	{ 0x1c5b60, 0x00000020, 0x00000020, 0x00000020, 0x00000020, },
+	{ 0x1c5b64, 0x00000021, 0x00000021, 0x00000021, 0x00000021, },
+	{ 0x1c5b68, 0x00000022, 0x00000022, 0x00000022, 0x00000022, },
+	{ 0x1c5b6c, 0x00000023, 0x00000023, 0x00000023, 0x00000023, },
+	{ 0x1c5b70, 0x00000024, 0x00000024, 0x00000024, 0x00000024, },
+	{ 0x1c5b74, 0x00000025, 0x00000025, 0x00000025, 0x00000025, },
+	{ 0x1c5b78, 0x00000028, 0x00000028, 0x00000028, 0x00000028, },
+	{ 0x1c5b7c, 0x00000029, 0x00000029, 0x00000029, 0x00000029, },
+	{ 0x1c5b80, 0x0000002a, 0x0000002a, 0x0000002a, 0x0000002a, },
+	{ 0x1c5b84, 0x0000002b, 0x0000002b, 0x0000002b, 0x0000002b, },
+	{ 0x1c5b88, 0x0000002c, 0x0000002c, 0x0000002c, 0x0000002c, },
+	{ 0x1c5b8c, 0x0000002d, 0x0000002d, 0x0000002d, 0x0000002d, },
+	{ 0x1c5b90, 0x00000030, 0x00000030, 0x00000030, 0x00000030, },
+	{ 0x1c5b94, 0x00000031, 0x00000031, 0x00000031, 0x00000031, },
+	{ 0x1c5b98, 0x00000032, 0x00000032, 0x00000032, 0x00000032, },
+	{ 0x1c5b9c, 0x00000033, 0x00000033, 0x00000033, 0x00000033, },
+	{ 0x1c5ba0, 0x00000034, 0x00000034, 0x00000034, 0x00000034, },
+	{ 0x1c5ba4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+	{ 0x1c5ba8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+	{ 0x1c5bac, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+	{ 0x1c5bb0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+	{ 0x1c5bb4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+	{ 0x1c5bb8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+	{ 0x1c5bbc, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+	{ 0x1c5bc0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+	{ 0x1c5bc4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+	{ 0x1c5bc8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+	{ 0x1c5bcc, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+	{ 0x1c5bd0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+	{ 0x1c5bd4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+	{ 0x1c5bd8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+	{ 0x1c5bdc, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+	{ 0x1c5be0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+	{ 0x1c5be4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+	{ 0x1c5be8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+	{ 0x1c5bec, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+	{ 0x1c5bf0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+	{ 0x1c5bf4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+	{ 0x1c5bf8, 0x00000010, 0x00000010, 0x00000010, 0x00000010, },
+	{ 0x1c5bfc, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a, },
+	{ 0x1c5c00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5c0c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5c10, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5c14, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5c18, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5c1c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5c20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5c24, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5c28, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5c2c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5c30, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5c34, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5c38, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5c3c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5cf0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5cf4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5cf8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c5cfc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c6200, 0x00000008, 0x00000008, 0x0000000e, 0x0000000e, },
+	{ 0x1c6204, 0x00000440, 0x00000440, 0x00000440, 0x00000440, },
+	{ 0x1c6208, 0xd6be4788, 0xd6be4788, 0xd03e4788, 0xd03e4788, },
+	{ 0x1c620c, 0x012e8160, 0x012e8160, 0x012a8160, 0x012a8160, },
+	{ 0x1c6210, 0x40806333, 0x40806333, 0x40806333, 0x40806333, },
+	{ 0x1c6214, 0x00106c10, 0x00106c10, 0x00106c10, 0x00106c10, },
+	{ 0x1c6218, 0x009c4060, 0x009c4060, 0x009c4060, 0x009c4060, },
+	{ 0x1c621c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, },
+	{ 0x1c6220, 0x018830c6, 0x018830c6, 0x018830c6, 0x018830c6, },
+	{ 0x1c6224, 0x00000400, 0x00000400, 0x00000400, 0x00000400, },
+	{ 0x1c6228, 0x000009b5, 0x000009b5, 0x000009b5, 0x000009b5, },
+	{ 0x1c622c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c6230, 0x00000108, 0x00000210, 0x00000210, 0x00000108, },
+	{ 0x1c6234, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+	{ 0x1c6238, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+	{ 0x1c623c, 0x13c889af, 0x13c889af, 0x13c889af, 0x13c889af, },
+	{ 0x1c6240, 0x38490a20, 0x38490a20, 0x38490a20, 0x38490a20, },
+	{ 0x1c6244, 0x00007bb6, 0x00007bb6, 0x00007bb6, 0x00007bb6, },
+	{ 0x1c6248, 0x0fff3ffc, 0x0fff3ffc, 0x0fff3ffc, 0x0fff3ffc, },
+	{ 0x1c624c, 0x00000001, 0x00000001, 0x00000001, 0x00000001, },
+	{ 0x1c6250, 0x0000a000, 0x0000a000, 0x0000a000, 0x0000a000, },
+	{ 0x1c6254, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c6258, 0x0cc75380, 0x0cc75380, 0x0cc75380, 0x0cc75380, },
+	{ 0x1c625c, 0x0f0f0f01, 0x0f0f0f01, 0x0f0f0f01, 0x0f0f0f01, },
+	{ 0x1c6260, 0xdfa91f01, 0xdfa91f01, 0xdfa91f01, 0xdfa91f01, },
+	{ 0x1c6264, 0x00418a11, 0x00418a11, 0x00418a11, 0x00418a11, },
+	{ 0x1c6268, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c626c, 0x09249126, 0x09249126, 0x09249126, 0x09249126, },
+	{ 0x1c6274, 0x0a1a9caa, 0x0a1a9caa, 0x0a1a7caa, 0x0a1a7caa, },
+	{ 0x1c6278, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, },
+	{ 0x1c627c, 0x051701ce, 0x051701ce, 0x051701ce, 0x051701ce, },
+	{ 0x1c6300, 0x18010000, 0x18010000, 0x18010000, 0x18010000, },
+	{ 0x1c6304, 0x30032602, 0x30032602, 0x2e032402, 0x2e032402, },
+	{ 0x1c6308, 0x48073e06, 0x48073e06, 0x4a0a3c06, 0x4a0a3c06, },
+	{ 0x1c630c, 0x560b4c0a, 0x560b4c0a, 0x621a540b, 0x621a540b, },
+	{ 0x1c6310, 0x641a600f, 0x641a600f, 0x764f6c1b, 0x764f6c1b, },
+	{ 0x1c6314, 0x7a4f6e1b, 0x7a4f6e1b, 0x845b7a5a, 0x845b7a5a, },
+	{ 0x1c6318, 0x8c5b7e5a, 0x8c5b7e5a, 0x950f8ccf, 0x950f8ccf, },
+	{ 0x1c631c, 0x9d0f96cf, 0x9d0f96cf, 0xa5cf9b4f, 0xa5cf9b4f, },
+	{ 0x1c6320, 0xb51fa69f, 0xb51fa69f, 0xbddfaf1f, 0xbddfaf1f, },
+	{ 0x1c6324, 0xcb3fbd07, 0xcb3fbcbf, 0xd1ffc93f, 0xd1ffc93f, },
+	{ 0x1c6328, 0x0000d7bf, 0x0000d7bf, 0x00000000, 0x00000000, },
+	{ 0x1c632c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c6330, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c6334, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c6338, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c633c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c6340, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c6344, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c6348, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, },
+	{ 0x1c634c, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, },
+	{ 0x1c6350, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, },
+	{ 0x1c6354, 0x0003ffff, 0x0003ffff, 0x0003ffff, 0x0003ffff, },
+	{ 0x1c6358, 0x79a8aa1f, 0x79a8aa1f, 0x79a8aa1f, 0x79a8aa1f, },
+	{ 0x1c6388, 0x08000000, 0x08000000, 0x08000000, 0x08000000, },
+	{ 0x1c638c, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+	{ 0x1c6390, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+	{ 0x1c6394, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, },
+	{ 0x1c6398, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce, },
+	{ 0x1c639c, 0x00000007, 0x00000007, 0x00000007, 0x00000007, },
+	{ 0x1c63a0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c63a4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c63a8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c63ac, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c63b0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c63b4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c63b8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c63bc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c63c0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c63c4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c63c8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c63cc, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+	{ 0x1c63d0, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+	{ 0x1c63d4, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+	{ 0x1c63d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+	{ 0x1c63dc, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, },
+	{ 0x1c63e0, 0x000000c0, 0x000000c0, 0x000000c0, 0x000000c0, },
+	{ 0x1c6848, 0x00180a65, 0x00180a65, 0x00180a68, 0x00180a68, },
+	{ 0x1c6920, 0x0510001c, 0x0510001c, 0x0510001c, 0x0510001c, },
+	{ 0x1c6960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, },
+	{ 0x1c720c, 0x012e8160, 0x012e8160, 0x012a8160, 0x012a8160, },
+	{ 0x1c726c, 0x09249126, 0x09249126, 0x09249126, 0x09249126, },
+	{ 0x1c7848, 0x00180a65, 0x00180a65, 0x00180a68, 0x00180a68, },
+	{ 0x1c7920, 0x0510001c, 0x0510001c, 0x0510001c, 0x0510001c, },
+	{ 0x1c7960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, },
+	{ 0x1c820c, 0x012e8160, 0x012e8160, 0x012a8160, 0x012a8160, },
+	{ 0x1c826c, 0x09249126, 0x09249126, 0x09249126, 0x09249126, },
+/*	{ 0x1c8864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, }, */
+	{ 0x1c8864, 0x0001c600, 0x0001c600, 0x0001c600, 0x0001c600, },
+	{ 0x1c895c, 0x004b6a8e, 0x004b6a8e, 0x004b6a8e, 0x004b6a8e, },
+	{ 0x1c8968, 0x000003ce, 0x000003ce, 0x000003ce, 0x000003ce, },
+	{ 0x1c89bc, 0x00181400, 0x00181400, 0x00181400, 0x00181400, },
+	{ 0x1c9270, 0x00820820, 0x00820820, 0x00820820, 0x00820820, },
+	{ 0x1c935c, 0x066c420f, 0x066c420f, 0x066c420f, 0x066c420f, },
+	{ 0x1c9360, 0x0f282207, 0x0f282207, 0x0f282207, 0x0f282207, },
+	{ 0x1c9364, 0x17601685, 0x17601685, 0x17601685, 0x17601685, },
+	{ 0x1c9368, 0x1f801104, 0x1f801104, 0x1f801104, 0x1f801104, },
+	{ 0x1c936c, 0x37a00c03, 0x37a00c03, 0x37a00c03, 0x37a00c03, },
+	{ 0x1c9370, 0x3fc40883, 0x3fc40883, 0x3fc40883, 0x3fc40883, },
+	{ 0x1c9374, 0x57c00803, 0x57c00803, 0x57c00803, 0x57c00803, },
+	{ 0x1c9378, 0x5fd80682, 0x5fd80682, 0x5fd80682, 0x5fd80682, },
+	{ 0x1c937c, 0x7fe00482, 0x7fe00482, 0x7fe00482, 0x7fe00482, },
+	{ 0x1c9380, 0x7f3c7bba, 0x7f3c7bba, 0x7f3c7bba, 0x7f3c7bba, },
+	{ 0x1c9384, 0xf3307ff0, 0xf3307ff0, 0xf3307ff0, 0xf3307ff0, }
+};
+
+/*
+ * look up a certain register in ar5416_phy_init[] and return the init. value
+ * for the band and bandwidth given. Return 0 if register address not found.
+ */
+static u32 carl9170_def_val(u32 reg, bool is_2ghz, bool is_40mhz)
+{
+	unsigned int i;
+	for (i = 0; i < ARRAY_SIZE(ar5416_phy_init); i++) {
+		if (ar5416_phy_init[i].reg != reg)
+			continue;
+
+		if (is_2ghz) {
+			if (is_40mhz)
+				return ar5416_phy_init[i]._2ghz_40;
+			else
+				return ar5416_phy_init[i]._2ghz_20;
+		} else {
+			if (is_40mhz)
+				return ar5416_phy_init[i]._5ghz_40;
+			else
+				return ar5416_phy_init[i]._5ghz_20;
+		}
+	}
+	return 0;
+}
+
+/*
+ * initialize some phy regs from eeprom values in modal_header[]
+ * acc. to band and bandwith
+ */
+static int carl9170_init_phy_from_eeprom(struct ar9170 *ar,
+				bool is_2ghz, bool is_40mhz)
+{
+	static const u8 xpd2pd[16] = {
+		0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x6, 0x2,
+		0x2, 0x3, 0x7, 0x2, 0xb, 0x2, 0x2, 0x2
+	};
+	/* pointer to the modal_header acc. to band */
+	struct ar9170_eeprom_modal *m = &ar->eeprom.modal_header[is_2ghz];
+	u32 val;
+
+	carl9170_regwrite_begin(ar);
+
+	/* ant common control (index 0) */
+	carl9170_regwrite(AR9170_PHY_REG_SWITCH_COM,
+		le32_to_cpu(m->antCtrlCommon));
+
+	/* ant control chain 0 (index 1) */
+	carl9170_regwrite(AR9170_PHY_REG_SWITCH_CHAIN_0,
+		le32_to_cpu(m->antCtrlChain[0]));
+
+	/* ant control chain 2 (index 2) */
+	carl9170_regwrite(AR9170_PHY_REG_SWITCH_CHAIN_2,
+		le32_to_cpu(m->antCtrlChain[1]));
+
+	/* SwSettle (index 3) */
+	if (!is_40mhz) {
+		val = carl9170_def_val(AR9170_PHY_REG_SETTLING,
+				     is_2ghz, is_40mhz);
+		SET_VAL(AR9170_PHY_SETTLING_SWITCH, val, m->switchSettling);
+		carl9170_regwrite(AR9170_PHY_REG_SETTLING, val);
+	}
+
+	/* adcDesired, pdaDesired (index 4) */
+	val = carl9170_def_val(AR9170_PHY_REG_DESIRED_SZ, is_2ghz, is_40mhz);
+	SET_VAL(AR9170_PHY_DESIRED_SZ_PGA, val, m->pgaDesiredSize);
+	SET_VAL(AR9170_PHY_DESIRED_SZ_ADC, val, m->adcDesiredSize);
+	carl9170_regwrite(AR9170_PHY_REG_DESIRED_SZ, val);
+
+	/* TxEndToXpaOff, TxFrameToXpaOn (index 5) */
+	val = carl9170_def_val(AR9170_PHY_REG_RF_CTL4, is_2ghz, is_40mhz);
+	SET_VAL(AR9170_PHY_RF_CTL4_TX_END_XPAB_OFF, val, m->txEndToXpaOff);
+	SET_VAL(AR9170_PHY_RF_CTL4_TX_END_XPAA_OFF, val, m->txEndToXpaOff);
+	SET_VAL(AR9170_PHY_RF_CTL4_FRAME_XPAB_ON, val, m->txFrameToXpaOn);
+	SET_VAL(AR9170_PHY_RF_CTL4_FRAME_XPAA_ON, val, m->txFrameToXpaOn);
+	carl9170_regwrite(AR9170_PHY_REG_RF_CTL4, val);
+
+	/* TxEndToRxOn (index 6) */
+	val = carl9170_def_val(AR9170_PHY_REG_RF_CTL3, is_2ghz, is_40mhz);
+	SET_VAL(AR9170_PHY_RF_CTL3_TX_END_TO_A2_RX_ON, val, m->txEndToRxOn);
+	carl9170_regwrite(AR9170_PHY_REG_RF_CTL3, val);
+
+	/* thresh62 (index 7) */
+	val = carl9170_def_val(0x1c8864, is_2ghz, is_40mhz);
+	val = (val & ~0x7f000) | (m->thresh62 << 12);
+	carl9170_regwrite(0x1c8864, val);
+
+	/* tx/rx attenuation chain 0 (index 8) */
+	val = carl9170_def_val(AR9170_PHY_REG_RXGAIN, is_2ghz, is_40mhz);
+	SET_VAL(AR9170_PHY_RXGAIN_TXRX_ATTEN, val, m->txRxAttenCh[0]);
+	carl9170_regwrite(AR9170_PHY_REG_RXGAIN, val);
+
+	/* tx/rx attenuation chain 2 (index 9) */
+	val = carl9170_def_val(AR9170_PHY_REG_RXGAIN_CHAIN_2,
+			       is_2ghz, is_40mhz);
+	SET_VAL(AR9170_PHY_RXGAIN_TXRX_ATTEN, val, m->txRxAttenCh[1]);
+	carl9170_regwrite(AR9170_PHY_REG_RXGAIN_CHAIN_2, val);
+
+	/* tx/rx margin chain 0 (index 10) */
+	val = carl9170_def_val(AR9170_PHY_REG_GAIN_2GHZ, is_2ghz, is_40mhz);
+	SET_VAL(AR9170_PHY_GAIN_2GHZ_RXTX_MARGIN, val, m->rxTxMarginCh[0]);
+	/* bsw margin chain 0 for 5GHz only */
+	if (!is_2ghz)
+		SET_VAL(AR9170_PHY_GAIN_2GHZ_BSW_MARGIN, val, m->bswMargin[0]);
+	carl9170_regwrite(AR9170_PHY_REG_GAIN_2GHZ, val);
+
+	/* tx/rx margin chain 2 (index 11) */
+	val = carl9170_def_val(AR9170_PHY_REG_GAIN_2GHZ_CHAIN_2,
+			       is_2ghz, is_40mhz);
+	SET_VAL(AR9170_PHY_GAIN_2GHZ_RXTX_MARGIN, val, m->rxTxMarginCh[1]);
+	carl9170_regwrite(AR9170_PHY_REG_GAIN_2GHZ_CHAIN_2, val);
+
+	/* iqCall, iqCallq chain 0 (index 12) */
+	val = carl9170_def_val(AR9170_PHY_REG_TIMING_CTRL4(0),
+			       is_2ghz, is_40mhz);
+	SET_VAL(AR9170_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF, val, m->iqCalICh[0]);
+	SET_VAL(AR9170_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF, val, m->iqCalQCh[0]);
+	carl9170_regwrite(AR9170_PHY_REG_TIMING_CTRL4(0), val);
+
+	/* iqCall, iqCallq chain 2 (index 13) */
+	val = carl9170_def_val(AR9170_PHY_REG_TIMING_CTRL4(2),
+			       is_2ghz, is_40mhz);
+	SET_VAL(AR9170_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF, val, m->iqCalICh[1]);
+	SET_VAL(AR9170_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF, val, m->iqCalQCh[1]);
+	carl9170_regwrite(AR9170_PHY_REG_TIMING_CTRL4(2), val);
+
+	/* xpd gain mask (index 14) */
+	val = carl9170_def_val(AR9170_PHY_REG_TPCRG1, is_2ghz, is_40mhz);
+	SET_VAL(AR9170_PHY_TPCRG1_PD_GAIN_1, val,
+		xpd2pd[m->xpdGain & 0xf] & 3);
+	SET_VAL(AR9170_PHY_TPCRG1_PD_GAIN_2, val,
+		xpd2pd[m->xpdGain & 0xf] >> 2);
+	carl9170_regwrite(AR9170_PHY_REG_TPCRG1, val);
+
+	carl9170_regwrite(AR9170_PHY_REG_RX_CHAINMASK, ar->eeprom.rx_mask);
+	carl9170_regwrite(AR9170_PHY_REG_CAL_CHAINMASK, ar->eeprom.rx_mask);
+
+	carl9170_regwrite_finish();
+	return carl9170_regwrite_result();
+}
+
+static int carl9170_init_phy(struct ar9170 *ar, enum ieee80211_band band)
+{
+	int i, err;
+	u32 val;
+	bool is_2ghz = band == IEEE80211_BAND_2GHZ;
+	bool is_40mhz = conf_is_ht40(&ar->hw->conf);
+
+	carl9170_regwrite_begin(ar);
+
+	for (i = 0; i < ARRAY_SIZE(ar5416_phy_init); i++) {
+		if (is_40mhz) {
+			if (is_2ghz)
+				val = ar5416_phy_init[i]._2ghz_40;
+			else
+				val = ar5416_phy_init[i]._5ghz_40;
+		} else {
+			if (is_2ghz)
+				val = ar5416_phy_init[i]._2ghz_20;
+			else
+				val = ar5416_phy_init[i]._5ghz_20;
+		}
+
+		carl9170_regwrite(ar5416_phy_init[i].reg, val);
+	}
+
+	carl9170_regwrite_finish();
+	err = carl9170_regwrite_result();
+	if (err)
+		return err;
+
+	err = carl9170_init_phy_from_eeprom(ar, is_2ghz, is_40mhz);
+	if (err)
+		return err;
+
+	err = carl9170_init_power_cal(ar);
+	if (err)
+		return err;
+
+	/* XXX: remove magic! */
+	if (is_2ghz)
+		err = carl9170_write_reg(ar, AR9170_PWR_REG_PLL_ADDAC, 0x5163);
+	else
+		err = carl9170_write_reg(ar, AR9170_PWR_REG_PLL_ADDAC, 0x5143);
+
+	return err;
+}
+
+struct carl9170_rf_initvals {
+	u32 reg, _5ghz, _2ghz;
+};
+
+static struct carl9170_rf_initvals carl9170_rf_initval[] = {
+	/* bank 0 */
+	{ 0x1c58b0, 0x1e5795e5, 0x1e5795e5},
+	{ 0x1c58e0, 0x02008020, 0x02008020},
+	/* bank 1 */
+	{ 0x1c58b0, 0x02108421, 0x02108421},
+	{ 0x1c58ec, 0x00000008, 0x00000008},
+	/* bank 2 */
+	{ 0x1c58b0, 0x0e73ff17, 0x0e73ff17},
+	{ 0x1c58e0, 0x00000420, 0x00000420},
+	/* bank 3 */
+	{ 0x1c58f0, 0x01400018, 0x01c00018},
+	/* bank 4 */
+	{ 0x1c58b0, 0x000001a1, 0x000001a1},
+	{ 0x1c58e8, 0x00000001, 0x00000001},
+	/* bank 5 */
+	{ 0x1c58b0, 0x00000013, 0x00000013},
+	{ 0x1c58e4, 0x00000002, 0x00000002},
+	/* bank 6 */
+	{ 0x1c58b0, 0x00000000, 0x00000000},
+	{ 0x1c58b0, 0x00000000, 0x00000000},
+	{ 0x1c58b0, 0x00000000, 0x00000000},
+	{ 0x1c58b0, 0x00000000, 0x00000000},
+	{ 0x1c58b0, 0x00000000, 0x00000000},
+	{ 0x1c58b0, 0x00004000, 0x00004000},
+	{ 0x1c58b0, 0x00006c00, 0x00006c00},
+	{ 0x1c58b0, 0x00002c00, 0x00002c00},
+	{ 0x1c58b0, 0x00004800, 0x00004800},
+	{ 0x1c58b0, 0x00004000, 0x00004000},
+	{ 0x1c58b0, 0x00006000, 0x00006000},
+	{ 0x1c58b0, 0x00001000, 0x00001000},
+	{ 0x1c58b0, 0x00004000, 0x00004000},
+	{ 0x1c58b0, 0x00007c00, 0x00007c00},
+	{ 0x1c58b0, 0x00007c00, 0x00007c00},
+	{ 0x1c58b0, 0x00007c00, 0x00007c00},
+	{ 0x1c58b0, 0x00007c00, 0x00007c00},
+	{ 0x1c58b0, 0x00007c00, 0x00007c00},
+	{ 0x1c58b0, 0x00087c00, 0x00087c00},
+	{ 0x1c58b0, 0x00007c00, 0x00007c00},
+	{ 0x1c58b0, 0x00005400, 0x00005400},
+	{ 0x1c58b0, 0x00000c00, 0x00000c00},
+	{ 0x1c58b0, 0x00001800, 0x00001800},
+	{ 0x1c58b0, 0x00007c00, 0x00007c00},
+	{ 0x1c58b0, 0x00006c00, 0x00006c00},
+	{ 0x1c58b0, 0x00006c00, 0x00006c00},
+	{ 0x1c58b0, 0x00007c00, 0x00007c00},
+	{ 0x1c58b0, 0x00002c00, 0x00002c00},
+	{ 0x1c58b0, 0x00003c00, 0x00003c00},
+	{ 0x1c58b0, 0x00003800, 0x00003800},
+	{ 0x1c58b0, 0x00001c00, 0x00001c00},
+	{ 0x1c58b0, 0x00000800, 0x00000800},
+	{ 0x1c58b0, 0x00000408, 0x00000408},
+	{ 0x1c58b0, 0x00004c15, 0x00004c15},
+	{ 0x1c58b0, 0x00004188, 0x00004188},
+	{ 0x1c58b0, 0x0000201e, 0x0000201e},
+	{ 0x1c58b0, 0x00010408, 0x00010408},
+	{ 0x1c58b0, 0x00000801, 0x00000801},
+	{ 0x1c58b0, 0x00000c08, 0x00000c08},
+	{ 0x1c58b0, 0x0000181e, 0x0000181e},
+	{ 0x1c58b0, 0x00001016, 0x00001016},
+	{ 0x1c58b0, 0x00002800, 0x00002800},
+	{ 0x1c58b0, 0x00004010, 0x00004010},
+	{ 0x1c58b0, 0x0000081c, 0x0000081c},
+	{ 0x1c58b0, 0x00000115, 0x00000115},
+	{ 0x1c58b0, 0x00000015, 0x00000015},
+	{ 0x1c58b0, 0x00000066, 0x00000066},
+	{ 0x1c58b0, 0x0000001c, 0x0000001c},
+	{ 0x1c58b0, 0x00000000, 0x00000000},
+	{ 0x1c58b0, 0x00000004, 0x00000004},
+	{ 0x1c58b0, 0x00000015, 0x00000015},
+	{ 0x1c58b0, 0x0000001f, 0x0000001f},
+	{ 0x1c58e0, 0x00000000, 0x00000400},
+	/* bank 7 */
+	{ 0x1c58b0, 0x000000a0, 0x000000a0},
+	{ 0x1c58b0, 0x00000000, 0x00000000},
+	{ 0x1c58b0, 0x00000040, 0x00000040},
+	{ 0x1c58f0, 0x0000001c, 0x0000001c},
+};
+
+static int carl9170_init_rf_banks_0_7(struct ar9170 *ar, bool band5ghz)
+{
+	int err, i;
+
+	carl9170_regwrite_begin(ar);
+
+	for (i = 0; i < ARRAY_SIZE(carl9170_rf_initval); i++)
+		carl9170_regwrite(carl9170_rf_initval[i].reg,
+				  band5ghz ? carl9170_rf_initval[i]._5ghz
+					   : carl9170_rf_initval[i]._2ghz);
+
+	carl9170_regwrite_finish();
+	err = carl9170_regwrite_result();
+	if (err)
+		wiphy_err(ar->hw->wiphy, "rf init failed\n");
+
+	return err;
+}
+
+struct carl9170_phy_freq_params {
+	u8 coeff_exp;
+	u16 coeff_man;
+	u8 coeff_exp_shgi;
+	u16 coeff_man_shgi;
+};
+
+enum carl9170_bw {
+	CARL9170_BW_20,
+	CARL9170_BW_40_BELOW,
+	CARL9170_BW_40_ABOVE,
+
+	__CARL9170_NUM_BW,
+};
+
+struct carl9170_phy_freq_entry {
+	u16 freq;
+	struct carl9170_phy_freq_params params[__CARL9170_NUM_BW];
+};
+
+/* NB: must be in sync with channel tables in main! */
+static const struct carl9170_phy_freq_entry carl9170_phy_freq_params[] = {
+/*
+ *	freq,
+ *		20MHz,
+ *		40MHz (below),
+ *		40Mhz (above),
+ */
+	{ 2412, {
+		{ 3, 21737, 3, 19563, },
+		{ 3, 21827, 3, 19644, },
+		{ 3, 21647, 3, 19482, },
+	} },
+	{ 2417, {
+		{ 3, 21692, 3, 19523, },
+		{ 3, 21782, 3, 19604, },
+		{ 3, 21602, 3, 19442, },
+	} },
+	{ 2422, {
+		{ 3, 21647, 3, 19482, },
+		{ 3, 21737, 3, 19563, },
+		{ 3, 21558, 3, 19402, },
+	} },
+	{ 2427, {
+		{ 3, 21602, 3, 19442, },
+		{ 3, 21692, 3, 19523, },
+		{ 3, 21514, 3, 19362, },
+	} },
+	{ 2432, {
+		{ 3, 21558, 3, 19402, },
+		{ 3, 21647, 3, 19482, },
+		{ 3, 21470, 3, 19323, },
+	} },
+	{ 2437, {
+		{ 3, 21514, 3, 19362, },
+		{ 3, 21602, 3, 19442, },
+		{ 3, 21426, 3, 19283, },
+	} },
+	{ 2442, {
+		{ 3, 21470, 3, 19323, },
+		{ 3, 21558, 3, 19402, },
+		{ 3, 21382, 3, 19244, },
+	} },
+	{ 2447, {
+		{ 3, 21426, 3, 19283, },
+		{ 3, 21514, 3, 19362, },
+		{ 3, 21339, 3, 19205, },
+	} },
+	{ 2452, {
+		{ 3, 21382, 3, 19244, },
+		{ 3, 21470, 3, 19323, },
+		{ 3, 21295, 3, 19166, },
+	} },
+	{ 2457, {
+		{ 3, 21339, 3, 19205, },
+		{ 3, 21426, 3, 19283, },
+		{ 3, 21252, 3, 19127, },
+	} },
+	{ 2462, {
+		{ 3, 21295, 3, 19166, },
+		{ 3, 21382, 3, 19244, },
+		{ 3, 21209, 3, 19088, },
+	} },
+	{ 2467, {
+		{ 3, 21252, 3, 19127, },
+		{ 3, 21339, 3, 19205, },
+		{ 3, 21166, 3, 19050, },
+	} },
+	{ 2472, {
+		{ 3, 21209, 3, 19088, },
+		{ 3, 21295, 3, 19166, },
+		{ 3, 21124, 3, 19011, },
+	} },
+	{ 2484, {
+		{ 3, 21107, 3, 18996, },
+		{ 3, 21192, 3, 19073, },
+		{ 3, 21022, 3, 18920, },
+	} },
+	{ 4920, {
+		{ 4, 21313, 4, 19181, },
+		{ 4, 21356, 4, 19220, },
+		{ 4, 21269, 4, 19142, },
+	} },
+	{ 4940, {
+		{ 4, 21226, 4, 19104, },
+		{ 4, 21269, 4, 19142, },
+		{ 4, 21183, 4, 19065, },
+	} },
+	{ 4960, {
+		{ 4, 21141, 4, 19027, },
+		{ 4, 21183, 4, 19065, },
+		{ 4, 21098, 4, 18988, },
+	} },
+	{ 4980, {
+		{ 4, 21056, 4, 18950, },
+		{ 4, 21098, 4, 18988, },
+		{ 4, 21014, 4, 18912, },
+	} },
+	{ 5040, {
+		{ 4, 20805, 4, 18725, },
+		{ 4, 20846, 4, 18762, },
+		{ 4, 20764, 4, 18687, },
+	} },
+	{ 5060, {
+		{ 4, 20723, 4, 18651, },
+		{ 4, 20764, 4, 18687, },
+		{ 4, 20682, 4, 18614, },
+	} },
+	{ 5080, {
+		{ 4, 20641, 4, 18577, },
+		{ 4, 20682, 4, 18614, },
+		{ 4, 20601, 4, 18541, },
+	} },
+	{ 5180, {
+		{ 4, 20243, 4, 18219, },
+		{ 4, 20282, 4, 18254, },
+		{ 4, 20204, 4, 18183, },
+	} },
+	{ 5200, {
+		{ 4, 20165, 4, 18148, },
+		{ 4, 20204, 4, 18183, },
+		{ 4, 20126, 4, 18114, },
+	} },
+	{ 5220, {
+		{ 4, 20088, 4, 18079, },
+		{ 4, 20126, 4, 18114, },
+		{ 4, 20049, 4, 18044, },
+	} },
+	{ 5240, {
+		{ 4, 20011, 4, 18010, },
+		{ 4, 20049, 4, 18044, },
+		{ 4, 19973, 4, 17976, },
+	} },
+	{ 5260, {
+		{ 4, 19935, 4, 17941, },
+		{ 4, 19973, 4, 17976, },
+		{ 4, 19897, 4, 17907, },
+	} },
+	{ 5280, {
+		{ 4, 19859, 4, 17873, },
+		{ 4, 19897, 4, 17907, },
+		{ 4, 19822, 4, 17840, },
+	} },
+	{ 5300, {
+		{ 4, 19784, 4, 17806, },
+		{ 4, 19822, 4, 17840, },
+		{ 4, 19747, 4, 17772, },
+	} },
+	{ 5320, {
+		{ 4, 19710, 4, 17739, },
+		{ 4, 19747, 4, 17772, },
+		{ 4, 19673, 4, 17706, },
+	} },
+	{ 5500, {
+		{ 4, 19065, 4, 17159, },
+		{ 4, 19100, 4, 17190, },
+		{ 4, 19030, 4, 17127, },
+	} },
+	{ 5520, {
+		{ 4, 18996, 4, 17096, },
+		{ 4, 19030, 4, 17127, },
+		{ 4, 18962, 4, 17065, },
+	} },
+	{ 5540, {
+		{ 4, 18927, 4, 17035, },
+		{ 4, 18962, 4, 17065, },
+		{ 4, 18893, 4, 17004, },
+	} },
+	{ 5560, {
+		{ 4, 18859, 4, 16973, },
+		{ 4, 18893, 4, 17004, },
+		{ 4, 18825, 4, 16943, },
+	} },
+	{ 5580, {
+		{ 4, 18792, 4, 16913, },
+		{ 4, 18825, 4, 16943, },
+		{ 4, 18758, 4, 16882, },
+	} },
+	{ 5600, {
+		{ 4, 18725, 4, 16852, },
+		{ 4, 18758, 4, 16882, },
+		{ 4, 18691, 4, 16822, },
+	} },
+	{ 5620, {
+		{ 4, 18658, 4, 16792, },
+		{ 4, 18691, 4, 16822, },
+		{ 4, 18625, 4, 16762, },
+	} },
+	{ 5640, {
+		{ 4, 18592, 4, 16733, },
+		{ 4, 18625, 4, 16762, },
+		{ 4, 18559, 4, 16703, },
+	} },
+	{ 5660, {
+		{ 4, 18526, 4, 16673, },
+		{ 4, 18559, 4, 16703, },
+		{ 4, 18493, 4, 16644, },
+	} },
+	{ 5680, {
+		{ 4, 18461, 4, 16615, },
+		{ 4, 18493, 4, 16644, },
+		{ 4, 18428, 4, 16586, },
+	} },
+	{ 5700, {
+		{ 4, 18396, 4, 16556, },
+		{ 4, 18428, 4, 16586, },
+		{ 4, 18364, 4, 16527, },
+	} },
+	{ 5745, {
+		{ 4, 18252, 4, 16427, },
+		{ 4, 18284, 4, 16455, },
+		{ 4, 18220, 4, 16398, },
+	} },
+	{ 5765, {
+		{ 4, 18189, 5, 32740, },
+		{ 4, 18220, 4, 16398, },
+		{ 4, 18157, 5, 32683, },
+	} },
+	{ 5785, {
+		{ 4, 18126, 5, 32626, },
+		{ 4, 18157, 5, 32683, },
+		{ 4, 18094, 5, 32570, },
+	} },
+	{ 5805, {
+		{ 4, 18063, 5, 32514, },
+		{ 4, 18094, 5, 32570, },
+		{ 4, 18032, 5, 32458, },
+	} },
+	{ 5825, {
+		{ 4, 18001, 5, 32402, },
+		{ 4, 18032, 5, 32458, },
+		{ 4, 17970, 5, 32347, },
+	} },
+	{ 5170, {
+		{ 4, 20282, 4, 18254, },
+		{ 4, 20321, 4, 18289, },
+		{ 4, 20243, 4, 18219, },
+	} },
+	{ 5190, {
+		{ 4, 20204, 4, 18183, },
+		{ 4, 20243, 4, 18219, },
+		{ 4, 20165, 4, 18148, },
+	} },
+	{ 5210, {
+		{ 4, 20126, 4, 18114, },
+		{ 4, 20165, 4, 18148, },
+		{ 4, 20088, 4, 18079, },
+	} },
+	{ 5230, {
+		{ 4, 20049, 4, 18044, },
+		{ 4, 20088, 4, 18079, },
+		{ 4, 20011, 4, 18010, },
+	} },
+};
+
+static int carl9170_init_rf_bank4_pwr(struct ar9170 *ar, bool band5ghz,
+				      u32 freq, enum carl9170_bw bw)
+{
+	int err;
+	u32 d0, d1, td0, td1, fd0, fd1;
+	u8 chansel;
+	u8 refsel0 = 1, refsel1 = 0;
+	u8 lf_synth = 0;
+
+	switch (bw) {
+	case CARL9170_BW_40_ABOVE:
+		freq += 10;
+		break;
+	case CARL9170_BW_40_BELOW:
+		freq -= 10;
+		break;
+	case CARL9170_BW_20:
+		break;
+	default:
+		BUG();
+		return -ENOSYS;
+	}
+
+	if (band5ghz) {
+		if (freq % 10) {
+			chansel = (freq - 4800) / 5;
+		} else {
+			chansel = ((freq - 4800) / 10) * 2;
+			refsel0 = 0;
+			refsel1 = 1;
+		}
+		chansel = byte_rev_table[chansel];
+	} else {
+		if (freq == 2484) {
+			chansel = 10 + (freq - 2274) / 5;
+			lf_synth = 1;
+		} else
+			chansel = 16 + (freq - 2272) / 5;
+		chansel *= 4;
+		chansel = byte_rev_table[chansel];
+	}
+
+	d1 =	chansel;
+	d0 =	0x21 |
+		refsel0 << 3 |
+		refsel1 << 2 |
+		lf_synth << 1;
+	td0 =	d0 & 0x1f;
+	td1 =	d1 & 0x1f;
+	fd0 =	td1 << 5 | td0;
+
+	td0 =	(d0 >> 5) & 0x7;
+	td1 =	(d1 >> 5) & 0x7;
+	fd1 =	td1 << 5 | td0;
+
+	carl9170_regwrite_begin(ar);
+
+	carl9170_regwrite(0x1c58b0, fd0);
+	carl9170_regwrite(0x1c58e8, fd1);
+
+	carl9170_regwrite_finish();
+	err = carl9170_regwrite_result();
+	if (err)
+		return err;
+
+	msleep(20);
+
+	return 0;
+}
+
+static const struct carl9170_phy_freq_params *
+carl9170_get_hw_dyn_params(struct ieee80211_channel *channel,
+			   enum carl9170_bw bw)
+{
+	unsigned int chanidx = 0;
+	u16 freq = 2412;
+
+	if (channel) {
+		chanidx = channel->hw_value;
+		freq = channel->center_freq;
+	}
+
+	BUG_ON(chanidx >= ARRAY_SIZE(carl9170_phy_freq_params));
+
+	BUILD_BUG_ON(__CARL9170_NUM_BW != 3);
+
+	WARN_ON(carl9170_phy_freq_params[chanidx].freq != freq);
+
+	return &carl9170_phy_freq_params[chanidx].params[bw];
+}
+
+static int carl9170_find_freq_idx(int nfreqs, u8 *freqs, u8 f)
+{
+	int idx = nfreqs - 2;
+
+	while (idx >= 0) {
+		if (f >= freqs[idx])
+			return idx;
+		idx--;
+	}
+
+	return 0;
+}
+
+static s32 carl9170_interpolate_s32(s32 x, s32 x1, s32 y1, s32 x2, s32 y2)
+{
+	/* nothing to interpolate, it's horizontal */
+	if (y2 == y1)
+		return y1;
+
+	/* check if we hit one of the edges */
+	if (x == x1)
+		return y1;
+	if (x == x2)
+		return y2;
+
+	/* x1 == x2 is bad, hopefully == x */
+	if (x2 == x1)
+		return y1;
+
+	return y1 + (((y2 - y1) * (x - x1)) / (x2 - x1));
+}
+
+static u8 carl9170_interpolate_u8(u8 x, u8 x1, u8 y1, u8 x2, u8 y2)
+{
+#define SHIFT		8
+	s32 y;
+
+	y = carl9170_interpolate_s32(x << SHIFT, x1 << SHIFT,
+		y1 << SHIFT, x2 << SHIFT, y2 << SHIFT);
+
+	/*
+	 * XXX: unwrap this expression
+	 *	Isn't it just DIV_ROUND_UP(y, 1<<SHIFT)?
+	 *	Can we rely on the compiler to optimise away the div?
+	 */
+	return (y >> SHIFT) + ((y & (1<<(SHIFT-1))) >> (SHIFT - 1));
+#undef SHIFT
+}
+
+static u8 carl9170_interpolate_val(u8 x, u8 *x_array, u8 *y_array)
+{
+	int i;
+
+	for (i = 0; i < 3; i++) {
+		if (x <= x_array[i + 1])
+			break;
+	}
+
+	return carl9170_interpolate_u8(x, x_array[i], y_array[i],
+		x_array[i + 1], y_array[i + 1]);
+}
+
+static int carl9170_set_freq_cal_data(struct ar9170 *ar,
+	struct ieee80211_channel *channel)
+{
+	u8 *cal_freq_pier;
+	u8 vpds[2][AR5416_PD_GAIN_ICEPTS];
+	u8 pwrs[2][AR5416_PD_GAIN_ICEPTS];
+	int chain, idx, i;
+	u32 phy_data = 0;
+	u8 f, tmp;
+
+	switch (channel->band) {
+	case IEEE80211_BAND_2GHZ:
+		f = channel->center_freq - 2300;
+		cal_freq_pier = ar->eeprom.cal_freq_pier_2G;
+		i = AR5416_NUM_2G_CAL_PIERS - 1;
+		break;
+
+	case IEEE80211_BAND_5GHZ:
+		f = (channel->center_freq - 4800) / 5;
+		cal_freq_pier = ar->eeprom.cal_freq_pier_5G;
+		i = AR5416_NUM_5G_CAL_PIERS - 1;
+		break;
+
+	default:
+		return -EINVAL;
+		break;
+	}
+
+	for (; i >= 0; i--) {
+		if (cal_freq_pier[i] != 0xff)
+			break;
+	}
+	if (i < 0)
+		return -EINVAL;
+
+	idx = carl9170_find_freq_idx(i, cal_freq_pier, f);
+
+	carl9170_regwrite_begin(ar);
+
+	for (chain = 0; chain < AR5416_MAX_CHAINS; chain++) {
+		for (i = 0; i < AR5416_PD_GAIN_ICEPTS; i++) {
+			struct ar9170_calibration_data_per_freq *cal_pier_data;
+			int j;
+
+			switch (channel->band) {
+			case IEEE80211_BAND_2GHZ:
+				cal_pier_data = &ar->eeprom.
+					cal_pier_data_2G[chain][idx];
+				break;
+
+			case IEEE80211_BAND_5GHZ:
+				cal_pier_data = &ar->eeprom.
+					cal_pier_data_5G[chain][idx];
+				break;
+
+			default:
+				return -EINVAL;
+			}
+
+			for (j = 0; j < 2; j++) {
+				vpds[j][i] = carl9170_interpolate_u8(f,
+					cal_freq_pier[idx],
+					cal_pier_data->vpd_pdg[j][i],
+					cal_freq_pier[idx + 1],
+					cal_pier_data[1].vpd_pdg[j][i]);
+
+				pwrs[j][i] = carl9170_interpolate_u8(f,
+					cal_freq_pier[idx],
+					cal_pier_data->pwr_pdg[j][i],
+					cal_freq_pier[idx + 1],
+					cal_pier_data[1].pwr_pdg[j][i]) / 2;
+			}
+		}
+
+		for (i = 0; i < 76; i++) {
+			if (i < 25) {
+				tmp = carl9170_interpolate_val(i, &pwrs[0][0],
+							       &vpds[0][0]);
+			} else {
+				tmp = carl9170_interpolate_val(i - 12,
+							       &pwrs[1][0],
+							       &vpds[1][0]);
+			}
+
+			phy_data |= tmp << ((i & 3) << 3);
+			if ((i & 3) == 3) {
+				carl9170_regwrite(0x1c6280 + chain * 0x1000 +
+						  (i & ~3), phy_data);
+				phy_data = 0;
+			}
+		}
+
+		for (i = 19; i < 32; i++)
+			carl9170_regwrite(0x1c6280 + chain * 0x1000 + (i << 2),
+					  0x0);
+	}
+
+	carl9170_regwrite_finish();
+	return carl9170_regwrite_result();
+}
+
+static u8 carl9170_get_max_edge_power(struct ar9170 *ar,
+	u32 freq, struct ar9170_calctl_edges edges[])
+{
+	int i;
+	u8 rc = AR5416_MAX_RATE_POWER;
+	u8 f;
+	if (freq < 3000)
+		f = freq - 2300;
+	else
+		f = (freq - 4800) / 5;
+
+	for (i = 0; i < AR5416_NUM_BAND_EDGES; i++) {
+		if (edges[i].channel == 0xff)
+			break;
+		if (f == edges[i].channel) {
+			/* exact freq match */
+			rc = edges[i].power_flags & ~AR9170_CALCTL_EDGE_FLAGS;
+			break;
+		}
+		if (i > 0 && f < edges[i].channel) {
+			if (f > edges[i - 1].channel &&
+			    edges[i - 1].power_flags &
+			    AR9170_CALCTL_EDGE_FLAGS) {
+				/* lower channel has the inband flag set */
+				rc = edges[i - 1].power_flags &
+					~AR9170_CALCTL_EDGE_FLAGS;
+			}
+			break;
+		}
+	}
+
+	if (i == AR5416_NUM_BAND_EDGES) {
+		if (f > edges[i - 1].channel &&
+		    edges[i - 1].power_flags & AR9170_CALCTL_EDGE_FLAGS) {
+			/* lower channel has the inband flag set */
+			rc = edges[i - 1].power_flags &
+				~AR9170_CALCTL_EDGE_FLAGS;
+		}
+	}
+	return rc;
+}
+
+static u8 carl9170_get_heavy_clip(struct ar9170 *ar, u32 freq,
+	enum carl9170_bw bw, struct ar9170_calctl_edges edges[])
+{
+	u8 f;
+	int i;
+	u8 rc = 0;
+
+	if (freq < 3000)
+		f = freq - 2300;
+	else
+		f = (freq - 4800) / 5;
+
+	if (bw == CARL9170_BW_40_BELOW || bw == CARL9170_BW_40_ABOVE)
+		rc |= 0xf0;
+
+	for (i = 0; i < AR5416_NUM_BAND_EDGES; i++) {
+		if (edges[i].channel == 0xff)
+			break;
+		if (f == edges[i].channel) {
+			if (!(edges[i].power_flags & AR9170_CALCTL_EDGE_FLAGS))
+				rc |= 0x0f;
+			break;
+		}
+	}
+
+	return rc;
+}
+
+/*
+ * calculate the conformance test limits and the heavy clip parameter
+ * and apply them to ar->power* (derived from otus hal/hpmain.c, line 3706)
+ */
+static void carl9170_calc_ctl(struct ar9170 *ar, u32 freq, enum carl9170_bw bw)
+{
+	u8 ctl_grp; /* CTL group */
+	u8 ctl_idx; /* CTL index */
+	int i, j;
+	struct ctl_modes {
+		u8 ctl_mode;
+		u8 max_power;
+		u8 *pwr_cal_data;
+		int pwr_cal_len;
+	} *modes;
+
+	/*
+	 * order is relevant in the mode_list_*: we fall back to the
+	 * lower indices if any mode is missed in the EEPROM.
+	 */
+	struct ctl_modes mode_list_2ghz[] = {
+		{ CTL_11B, 0, ar->power_2G_cck, 4 },
+		{ CTL_11G, 0, ar->power_2G_ofdm, 4 },
+		{ CTL_2GHT20, 0, ar->power_2G_ht20, 8 },
+		{ CTL_2GHT40, 0, ar->power_2G_ht40, 8 },
+	};
+	struct ctl_modes mode_list_5ghz[] = {
+		{ CTL_11A, 0, ar->power_5G_leg, 4 },
+		{ CTL_5GHT20, 0, ar->power_5G_ht20, 8 },
+		{ CTL_5GHT40, 0, ar->power_5G_ht40, 8 },
+	};
+	int nr_modes;
+
+#define EDGES(c, n) (ar->eeprom.ctl_data[c].control_edges[n])
+
+	ar->heavy_clip = 0;
+
+	/*
+	 * TODO: investigate the differences between OTUS'
+	 * hpreg.c::zfHpGetRegulatoryDomain() and
+	 * ath/regd.c::ath_regd_get_band_ctl() -
+	 * e.g. for FCC3_WORLD the OTUS procedure
+	 * always returns CTL_FCC, while the one in ath/ delivers
+	 * CTL_ETSI for 2GHz and CTL_FCC for 5GHz.
+	 */
+	ctl_grp = ath_regd_get_band_ctl(&ar->common.regulatory,
+					ar->hw->conf.channel->band);
+
+	/* ctl group not found - either invalid band (NO_CTL) or ww roaming */
+	if (ctl_grp == NO_CTL || ctl_grp == SD_NO_CTL)
+		ctl_grp = CTL_FCC;
+
+	if (ctl_grp != CTL_FCC)
+		/* skip CTL and heavy clip for CTL_MKK and CTL_ETSI */
+		return;
+
+	if (ar->hw->conf.channel->band == IEEE80211_BAND_2GHZ) {
+		modes = mode_list_2ghz;
+		nr_modes = ARRAY_SIZE(mode_list_2ghz);
+	} else {
+		modes = mode_list_5ghz;
+		nr_modes = ARRAY_SIZE(mode_list_5ghz);
+	}
+
+	for (i = 0; i < nr_modes; i++) {
+		u8 c = ctl_grp | modes[i].ctl_mode;
+		for (ctl_idx = 0; ctl_idx < AR5416_NUM_CTLS; ctl_idx++)
+			if (c == ar->eeprom.ctl_index[ctl_idx])
+				break;
+		if (ctl_idx < AR5416_NUM_CTLS) {
+			int f_off = 0;
+
+			/*
+			 * determine heavy clip parameter
+			 * from the 11G edges array
+			 */
+			if (modes[i].ctl_mode == CTL_11G) {
+				ar->heavy_clip =
+					carl9170_get_heavy_clip(ar,
+						freq, bw, EDGES(ctl_idx, 1));
+			}
+
+			/* adjust freq for 40MHz */
+			if (modes[i].ctl_mode == CTL_2GHT40 ||
+			    modes[i].ctl_mode == CTL_5GHT40) {
+				if (bw == CARL9170_BW_40_BELOW)
+					f_off = -10;
+				else
+					f_off = 10;
+			}
+
+			modes[i].max_power =
+				carl9170_get_max_edge_power(ar,
+					freq+f_off, EDGES(ctl_idx, 1));
+
+			/*
+			 * TODO: check if the regulatory max. power is
+			 * controlled by cfg80211 for DFS.
+			 * (hpmain applies it to max_power itself for DFS freq)
+			 */
+
+		} else {
+			/*
+			 * Workaround in otus driver, hpmain.c, line 3906:
+			 * if no data for 5GHT20 are found, take the
+			 * legacy 5G value. We extend this here to fallback
+			 * from any other HT* or 11G, too.
+			 */
+			int k = i;
+
+			modes[i].max_power = AR5416_MAX_RATE_POWER;
+			while (k-- > 0) {
+				if (modes[k].max_power !=
+				    AR5416_MAX_RATE_POWER) {
+					modes[i].max_power = modes[k].max_power;
+					break;
+				}
+			}
+		}
+
+		/* apply max power to pwr_cal_data (ar->power_*) */
+		for (j = 0; j < modes[i].pwr_cal_len; j++) {
+			modes[i].pwr_cal_data[j] = min(modes[i].pwr_cal_data[j],
+						       modes[i].max_power);
+		}
+	}
+
+	if (ar->heavy_clip & 0xf0) {
+		ar->power_2G_ht40[0]--;
+		ar->power_2G_ht40[1]--;
+		ar->power_2G_ht40[2]--;
+	}
+	if (ar->heavy_clip & 0xf) {
+		ar->power_2G_ht20[0]++;
+		ar->power_2G_ht20[1]++;
+		ar->power_2G_ht20[2]++;
+	}
+
+#undef EDGES
+}
+
+static int carl9170_set_power_cal(struct ar9170 *ar, u32 freq,
+				  enum carl9170_bw bw)
+{
+	struct ar9170_calibration_target_power_legacy *ctpl;
+	struct ar9170_calibration_target_power_ht *ctph;
+	u8 *ctpres;
+	int ntargets;
+	int idx, i, n;
+	u8 ackpower, ackchains, f;
+	u8 pwr_freqs[AR5416_MAX_NUM_TGT_PWRS];
+
+	if (freq < 3000)
+		f = freq - 2300;
+	else
+		f = (freq - 4800)/5;
+
+	/*
+	 * cycle through the various modes
+	 *
+	 * legacy modes first: 5G, 2G CCK, 2G OFDM
+	 */
+	for (i = 0; i < 3; i++) {
+		switch (i) {
+		case 0: /* 5 GHz legacy */
+			ctpl = &ar->eeprom.cal_tgt_pwr_5G[0];
+			ntargets = AR5416_NUM_5G_TARGET_PWRS;
+			ctpres = ar->power_5G_leg;
+			break;
+		case 1: /* 2.4 GHz CCK */
+			ctpl = &ar->eeprom.cal_tgt_pwr_2G_cck[0];
+			ntargets = AR5416_NUM_2G_CCK_TARGET_PWRS;
+			ctpres = ar->power_2G_cck;
+			break;
+		case 2: /* 2.4 GHz OFDM */
+			ctpl = &ar->eeprom.cal_tgt_pwr_2G_ofdm[0];
+			ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS;
+			ctpres = ar->power_2G_ofdm;
+			break;
+		default:
+			BUG();
+		}
+
+		for (n = 0; n < ntargets; n++) {
+			if (ctpl[n].freq == 0xff)
+				break;
+			pwr_freqs[n] = ctpl[n].freq;
+		}
+		ntargets = n;
+		idx = carl9170_find_freq_idx(ntargets, pwr_freqs, f);
+		for (n = 0; n < 4; n++)
+			ctpres[n] = carl9170_interpolate_u8(f,
+				ctpl[idx + 0].freq, ctpl[idx + 0].power[n],
+				ctpl[idx + 1].freq, ctpl[idx + 1].power[n]);
+	}
+
+	/* HT modes now: 5G HT20, 5G HT40, 2G CCK, 2G OFDM, 2G HT20, 2G HT40 */
+	for (i = 0; i < 4; i++) {
+		switch (i) {
+		case 0: /* 5 GHz HT 20 */
+			ctph = &ar->eeprom.cal_tgt_pwr_5G_ht20[0];
+			ntargets = AR5416_NUM_5G_TARGET_PWRS;
+			ctpres = ar->power_5G_ht20;
+			break;
+		case 1: /* 5 GHz HT 40 */
+			ctph = &ar->eeprom.cal_tgt_pwr_5G_ht40[0];
+			ntargets = AR5416_NUM_5G_TARGET_PWRS;
+			ctpres = ar->power_5G_ht40;
+			break;
+		case 2: /* 2.4 GHz HT 20 */
+			ctph = &ar->eeprom.cal_tgt_pwr_2G_ht20[0];
+			ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS;
+			ctpres = ar->power_2G_ht20;
+			break;
+		case 3: /* 2.4 GHz HT 40 */
+			ctph = &ar->eeprom.cal_tgt_pwr_2G_ht40[0];
+			ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS;
+			ctpres = ar->power_2G_ht40;
+			break;
+		default:
+			BUG();
+		}
+
+		for (n = 0; n < ntargets; n++) {
+			if (ctph[n].freq == 0xff)
+				break;
+			pwr_freqs[n] = ctph[n].freq;
+		}
+		ntargets = n;
+		idx = carl9170_find_freq_idx(ntargets, pwr_freqs, f);
+		for (n = 0; n < 8; n++)
+			ctpres[n] = carl9170_interpolate_u8(f,
+				ctph[idx + 0].freq, ctph[idx + 0].power[n],
+				ctph[idx + 1].freq, ctph[idx + 1].power[n]);
+	}
+
+	/* calc. conformance test limits and apply to ar->power*[] */
+	carl9170_calc_ctl(ar, freq, bw);
+
+	/* set ACK/CTS TX power */
+	carl9170_regwrite_begin(ar);
+
+	if (ar->eeprom.tx_mask != 1)
+		ackchains = AR9170_TX_PHY_TXCHAIN_2;
+	else
+		ackchains = AR9170_TX_PHY_TXCHAIN_1;
+
+	if (freq < 3000)
+		ackpower = ar->power_2G_ofdm[0] & 0x3f;
+	else
+		ackpower = ar->power_5G_leg[0] & 0x3f;
+
+	carl9170_regwrite(AR9170_MAC_REG_ACK_TPC,
+			  0x3c1e | ackpower << 20 | ackchains << 26);
+	carl9170_regwrite(AR9170_MAC_REG_RTS_CTS_TPC,
+			  ackpower << 5 | ackchains << 11 |
+			  ackpower << 21 | ackchains << 27);
+
+	carl9170_regwrite(AR9170_MAC_REG_CFEND_QOSNULL_TPC,
+			  ackpower << 5 | ackchains << 11 |
+			  ackpower << 21 | ackchains << 27);
+
+	carl9170_regwrite_finish();
+	return carl9170_regwrite_result();
+}
+
+/* TODO: replace this with sign_extend32(noise, 8) */
+static int carl9170_calc_noise_dbm(u32 raw_noise)
+{
+	if (raw_noise & 0x100)
+		return ~0x1ff | raw_noise;
+	else
+		return raw_noise;
+}
+
+int carl9170_get_noisefloor(struct ar9170 *ar)
+{
+	static const u32 phy_regs[] = {
+		AR9170_PHY_REG_CCA, AR9170_PHY_REG_CH2_CCA,
+		AR9170_PHY_REG_EXT_CCA, AR9170_PHY_REG_CH2_EXT_CCA };
+	u32 phy_res[ARRAY_SIZE(phy_regs)];
+	int err, i;
+
+	BUILD_BUG_ON(ARRAY_SIZE(phy_regs) != ARRAY_SIZE(ar->noise));
+
+	err = carl9170_read_mreg(ar, ARRAY_SIZE(phy_regs), phy_regs, phy_res);
+	if (err)
+		return err;
+
+	for (i = 0; i < 2; i++) {
+		ar->noise[i] = carl9170_calc_noise_dbm(
+			(phy_res[i] >> 19) & 0x1ff);
+
+		ar->noise[i + 2] = carl9170_calc_noise_dbm(
+			(phy_res[i + 2] >> 23) & 0x1ff);
+	}
+
+	return 0;
+}
+
+static enum carl9170_bw nl80211_to_carl(enum nl80211_channel_type type)
+{
+	switch (type) {
+	case NL80211_CHAN_NO_HT:
+	case NL80211_CHAN_HT20:
+		return CARL9170_BW_20;
+	case NL80211_CHAN_HT40MINUS:
+		return CARL9170_BW_40_BELOW;
+	case NL80211_CHAN_HT40PLUS:
+		return CARL9170_BW_40_ABOVE;
+	default:
+		BUG();
+	}
+}
+
+int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
+			 enum nl80211_channel_type _bw,
+			 enum carl9170_rf_init_mode rfi)
+{
+	const struct carl9170_phy_freq_params *freqpar;
+	struct carl9170_rf_init_result rf_res;
+	struct carl9170_rf_init rf;
+	u32 cmd, tmp, offs = 0, new_ht = 0;
+	int err;
+	enum carl9170_bw bw;
+	bool warm_reset;
+	struct ieee80211_channel *old_channel = NULL;
+
+	bw = nl80211_to_carl(_bw);
+
+	if (conf_is_ht(&ar->hw->conf))
+		new_ht |= CARL9170FW_PHY_HT_ENABLE;
+
+	if (conf_is_ht40(&ar->hw->conf))
+		new_ht |= CARL9170FW_PHY_HT_DYN2040;
+
+	/* may be NULL at first setup */
+	if (ar->channel) {
+		old_channel = ar->channel;
+		warm_reset = (old_channel->band != channel->band) ||
+			     (old_channel->center_freq ==
+			      channel->center_freq) ||
+			     (ar->ht_settings != new_ht);
+
+		ar->channel = NULL;
+	} else {
+		warm_reset = true;
+	}
+
+	/* HW workaround */
+	if (!ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] &&
+	    channel->center_freq <= 2417)
+		warm_reset = true;
+
+	if (rfi != CARL9170_RFI_NONE || warm_reset) {
+		u32 val;
+
+		if (rfi == CARL9170_RFI_COLD)
+			val = AR9170_PWR_RESET_BB_COLD_RESET;
+		else
+			val = AR9170_PWR_RESET_BB_WARM_RESET;
+
+		/* warm/cold reset BB/ADDA */
+		err = carl9170_write_reg(ar, AR9170_PWR_REG_RESET, val);
+		if (err)
+			return err;
+
+		err = carl9170_write_reg(ar, AR9170_PWR_REG_RESET, 0x0);
+		if (err)
+			return err;
+
+		err = carl9170_init_phy(ar, channel->band);
+		if (err)
+			return err;
+
+		err = carl9170_init_rf_banks_0_7(ar,
+			channel->band == IEEE80211_BAND_5GHZ);
+		if (err)
+			return err;
+
+		cmd = CARL9170_CMD_RF_INIT;
+
+		msleep(100);
+
+		err = carl9170_echo_test(ar, 0xaabbccdd);
+		if (err)
+			return err;
+	} else {
+		cmd = CARL9170_CMD_FREQUENCY;
+	}
+
+	err = carl9170_exec_cmd(ar, CARL9170_CMD_FREQ_START, 0, NULL, 0, NULL);
+	if (err)
+		return err;
+
+	err = carl9170_write_reg(ar, AR9170_PHY_REG_HEAVY_CLIP_ENABLE,
+				 0x200);
+
+	err = carl9170_init_rf_bank4_pwr(ar,
+		channel->band == IEEE80211_BAND_5GHZ,
+		channel->center_freq, bw);
+	if (err)
+		return err;
+
+	tmp = AR9170_PHY_TURBO_FC_SINGLE_HT_LTF1 |
+	      AR9170_PHY_TURBO_FC_HT_EN;
+
+	switch (bw) {
+	case CARL9170_BW_20:
+		break;
+	case CARL9170_BW_40_BELOW:
+		tmp |= AR9170_PHY_TURBO_FC_DYN2040_EN |
+		       AR9170_PHY_TURBO_FC_SHORT_GI_40;
+		offs = 3;
+		break;
+	case CARL9170_BW_40_ABOVE:
+		tmp |= AR9170_PHY_TURBO_FC_DYN2040_EN |
+		       AR9170_PHY_TURBO_FC_SHORT_GI_40 |
+		       AR9170_PHY_TURBO_FC_DYN2040_PRI_CH;
+		offs = 1;
+		break;
+	default:
+		BUG();
+		return -ENOSYS;
+	}
+
+	if (ar->eeprom.tx_mask != 1)
+		tmp |= AR9170_PHY_TURBO_FC_WALSH;
+
+	err = carl9170_write_reg(ar, AR9170_PHY_REG_TURBO, tmp);
+	if (err)
+		return err;
+
+	err = carl9170_set_freq_cal_data(ar, channel);
+	if (err)
+		return err;
+
+	err = carl9170_set_power_cal(ar, channel->center_freq, bw);
+	if (err)
+		return err;
+
+	freqpar = carl9170_get_hw_dyn_params(channel, bw);
+
+	rf.ht_settings = new_ht;
+	if (conf_is_ht40(&ar->hw->conf))
+		SET_VAL(CARL9170FW_PHY_HT_EXT_CHAN_OFF, rf.ht_settings, offs);
+
+	rf.freq = cpu_to_le32(channel->center_freq * 1000);
+	rf.delta_slope_coeff_exp = cpu_to_le32(freqpar->coeff_exp);
+	rf.delta_slope_coeff_man = cpu_to_le32(freqpar->coeff_man);
+	rf.delta_slope_coeff_exp_shgi = cpu_to_le32(freqpar->coeff_exp_shgi);
+	rf.delta_slope_coeff_man_shgi = cpu_to_le32(freqpar->coeff_man_shgi);
+
+	if (rfi != CARL9170_RFI_NONE)
+		rf.finiteLoopCount = cpu_to_le32(2000);
+	else
+		rf.finiteLoopCount = cpu_to_le32(1000);
+
+	err = carl9170_exec_cmd(ar, cmd, sizeof(rf), &rf,
+				sizeof(rf_res), &rf_res);
+	if (err)
+		return err;
+
+	err = le32_to_cpu(rf_res.ret);
+	if (err != 0) {
+		ar->chan_fail++;
+		ar->total_chan_fail++;
+
+		wiphy_err(ar->hw->wiphy, "channel change: %d -> %d "
+			  "failed (%d).\n", old_channel ?
+			  old_channel->center_freq : -1, channel->center_freq,
+			  err);
+
+		if ((rfi == CARL9170_RFI_COLD) || (ar->chan_fail > 3)) {
+			/*
+			 * We have tried very hard to change to _another_
+			 * channel and we've failed to do so!
+			 * Chances are that the PHY/RF is no longer
+			 * operable (due to corruptions/fatal events/bugs?)
+			 * and we need to reset at a higher level.
+			 */
+			carl9170_restart(ar, CARL9170_RR_TOO_MANY_PHY_ERRORS);
+			return 0;
+		}
+
+		err = carl9170_set_channel(ar, channel, _bw,
+					   CARL9170_RFI_COLD);
+		if (err)
+			return err;
+	} else {
+		ar->chan_fail = 0;
+	}
+
+	err = carl9170_get_noisefloor(ar);
+	if (err)
+		return err;
+
+	if (ar->heavy_clip) {
+		err = carl9170_write_reg(ar, AR9170_PHY_REG_HEAVY_CLIP_ENABLE,
+					 0x200 | ar->heavy_clip);
+		if (err) {
+			if (net_ratelimit()) {
+				wiphy_err(ar->hw->wiphy, "failed to set "
+				       "heavy clip\n");
+			}
+
+			return err;
+		}
+	}
+
+	/* FIXME: PSM does not work in 5GHz Band */
+	if (channel->band == IEEE80211_BAND_5GHZ)
+		ar->ps.off_override |= PS_OFF_5GHZ;
+	else
+		ar->ps.off_override &= ~PS_OFF_5GHZ;
+
+	ar->channel = channel;
+	ar->ht_settings = new_ht;
+	return 0;
+}
diff --git a/drivers/net/wireless/ath/carl9170/phy.h b/drivers/net/wireless/ath/carl9170/phy.h
new file mode 100644
index 0000000..53c18d3
--- /dev/null
+++ b/drivers/net/wireless/ath/carl9170/phy.h
@@ -0,0 +1,567 @@
+/*
+ * Shared Atheros AR9170 Header
+ *
+ * PHY register map
+ *
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __CARL9170_SHARED_PHY_H
+#define __CARL9170_SHARED_PHY_H
+
+#define	AR9170_PHY_REG_BASE			(0x1bc000 + 0x9800)
+#define	AR9170_PHY_REG(_n)			(AR9170_PHY_REG_BASE + \
+						 ((_n) << 2))
+
+#define	AR9170_PHY_REG_TEST			(AR9170_PHY_REG_BASE + 0x0000)
+#define		AR9170_PHY_TEST_AGC_CLR			0x10000000
+#define		AR9170_PHY_TEST_RFSILENT_BB		0x00002000
+
+#define	AR9170_PHY_REG_TURBO			(AR9170_PHY_REG_BASE + 0x0004)
+#define		AR9170_PHY_TURBO_FC_TURBO_MODE		0x00000001
+#define		AR9170_PHY_TURBO_FC_TURBO_SHORT		0x00000002
+#define		AR9170_PHY_TURBO_FC_DYN2040_EN		0x00000004
+#define		AR9170_PHY_TURBO_FC_DYN2040_PRI_ONLY	0x00000008
+#define		AR9170_PHY_TURBO_FC_DYN2040_PRI_CH	0x00000010
+/* For 25 MHz channel spacing -- not used but supported by hw */
+#define		AR9170_PHY_TURBO_FC_DYN2040_EXT_CH	0x00000020
+#define		AR9170_PHY_TURBO_FC_HT_EN		0x00000040
+#define		AR9170_PHY_TURBO_FC_SHORT_GI_40		0x00000080
+#define		AR9170_PHY_TURBO_FC_WALSH		0x00000100
+#define		AR9170_PHY_TURBO_FC_SINGLE_HT_LTF1	0x00000200
+#define		AR9170_PHY_TURBO_FC_ENABLE_DAC_FIFO	0x00000800
+
+#define	AR9170_PHY_REG_TEST2			(AR9170_PHY_REG_BASE + 0x0008)
+
+#define	AR9170_PHY_REG_TIMING2			(AR9170_PHY_REG_BASE + 0x0010)
+#define		AR9170_PHY_TIMING2_USE_FORCE		0x00001000
+#define		AR9170_PHY_TIMING2_FORCE		0x00000fff
+#define		AR9170_PHY_TIMING2_FORCE_S			 0
+
+#define	AR9170_PHY_REG_TIMING3			(AR9170_PHY_REG_BASE + 0x0014)
+#define		AR9170_PHY_TIMING3_DSC_EXP		0x0001e000
+#define		AR9170_PHY_TIMING3_DSC_EXP_S		13
+#define		AR9170_PHY_TIMING3_DSC_MAN		0xfffe0000
+#define		AR9170_PHY_TIMING3_DSC_MAN_S		17
+
+#define	AR9170_PHY_REG_CHIP_ID			(AR9170_PHY_REG_BASE + 0x0018)
+#define		AR9170_PHY_CHIP_ID_REV_0		0x80
+#define		AR9170_PHY_CHIP_ID_REV_1		0x81
+#define		AR9170_PHY_CHIP_ID_9160_REV_0		0xb0
+
+#define	AR9170_PHY_REG_ACTIVE			(AR9170_PHY_REG_BASE + 0x001c)
+#define		AR9170_PHY_ACTIVE_EN			0x00000001
+#define		AR9170_PHY_ACTIVE_DIS			0x00000000
+
+#define	AR9170_PHY_REG_RF_CTL2			(AR9170_PHY_REG_BASE + 0x0024)
+#define		AR9170_PHY_RF_CTL2_TX_END_DATA_START	0x000000ff
+#define		AR9170_PHY_RF_CTL2_TX_END_DATA_START_S	0
+#define		AR9170_PHY_RF_CTL2_TX_END_PA_ON		0x0000ff00
+#define		AR9170_PHY_RF_CTL2_TX_END_PA_ON_S	8
+
+#define	AR9170_PHY_REG_RF_CTL3                  (AR9170_PHY_REG_BASE + 0x0028)
+#define		AR9170_PHY_RF_CTL3_TX_END_TO_A2_RX_ON	0x00ff0000
+#define		AR9170_PHY_RF_CTL3_TX_END_TO_A2_RX_ON_S	16
+
+#define	AR9170_PHY_REG_ADC_CTL			(AR9170_PHY_REG_BASE + 0x002c)
+#define		AR9170_PHY_ADC_CTL_OFF_INBUFGAIN	0x00000003
+#define		AR9170_PHY_ADC_CTL_OFF_INBUFGAIN_S	0
+#define		AR9170_PHY_ADC_CTL_OFF_PWDDAC		0x00002000
+#define		AR9170_PHY_ADC_CTL_OFF_PWDBANDGAP	0x00004000
+#define		AR9170_PHY_ADC_CTL_OFF_PWDADC		0x00008000
+#define		AR9170_PHY_ADC_CTL_ON_INBUFGAIN		0x00030000
+#define		AR9170_PHY_ADC_CTL_ON_INBUFGAIN_S	16
+
+#define	AR9170_PHY_REG_ADC_SERIAL_CTL		(AR9170_PHY_REG_BASE + 0x0030)
+#define		AR9170_PHY_ADC_SCTL_SEL_INTERNAL_ADDAC	0x00000000
+#define		AR9170_PHY_ADC_SCTL_SEL_EXTERNAL_RADIO	0x00000001
+
+#define	AR9170_PHY_REG_RF_CTL4			(AR9170_PHY_REG_BASE + 0x0034)
+#define		AR9170_PHY_RF_CTL4_TX_END_XPAB_OFF	0xff000000
+#define		AR9170_PHY_RF_CTL4_TX_END_XPAB_OFF_S	24
+#define		AR9170_PHY_RF_CTL4_TX_END_XPAA_OFF	0x00ff0000
+#define		AR9170_PHY_RF_CTL4_TX_END_XPAA_OFF_S	16
+#define		AR9170_PHY_RF_CTL4_FRAME_XPAB_ON	0x0000ff00
+#define		AR9170_PHY_RF_CTL4_FRAME_XPAB_ON_S	8
+#define		AR9170_PHY_RF_CTL4_FRAME_XPAA_ON	0x000000ff
+#define		AR9170_PHY_RF_CTL4_FRAME_XPAA_ON_S	0
+
+#define	AR9170_PHY_REG_TSTDAC_CONST		(AR9170_PHY_REG_BASE + 0x003c)
+
+#define	AR9170_PHY_REG_SETTLING			(AR9170_PHY_REG_BASE + 0x0044)
+#define		AR9170_PHY_SETTLING_SWITCH		0x00003f80
+#define		AR9170_PHY_SETTLING_SWITCH_S		7
+
+#define	AR9170_PHY_REG_RXGAIN			(AR9170_PHY_REG_BASE + 0x0048)
+#define	AR9170_PHY_REG_RXGAIN_CHAIN_2		(AR9170_PHY_REG_BASE + 0x2048)
+#define		AR9170_PHY_RXGAIN_TXRX_ATTEN		0x0003f000
+#define		AR9170_PHY_RXGAIN_TXRX_ATTEN_S		12
+#define		AR9170_PHY_RXGAIN_TXRX_RF_MAX		0x007c0000
+#define		AR9170_PHY_RXGAIN_TXRX_RF_MAX_S		18
+
+#define	AR9170_PHY_REG_DESIRED_SZ		(AR9170_PHY_REG_BASE + 0x0050)
+#define		AR9170_PHY_DESIRED_SZ_ADC		0x000000ff
+#define		AR9170_PHY_DESIRED_SZ_ADC_S		0
+#define		AR9170_PHY_DESIRED_SZ_PGA		0x0000ff00
+#define		AR9170_PHY_DESIRED_SZ_PGA_S		8
+#define		AR9170_PHY_DESIRED_SZ_TOT_DES		0x0ff00000
+#define		AR9170_PHY_DESIRED_SZ_TOT_DES_S		20
+
+#define	AR9170_PHY_REG_FIND_SIG			(AR9170_PHY_REG_BASE + 0x0058)
+#define		AR9170_PHY_FIND_SIG_FIRSTEP		0x0003f000
+#define		AR9170_PHY_FIND_SIG_FIRSTEP_S		12
+#define		AR9170_PHY_FIND_SIG_FIRPWR		0x03fc0000
+#define		AR9170_PHY_FIND_SIG_FIRPWR_S		18
+
+#define	AR9170_PHY_REG_AGC_CTL1			(AR9170_PHY_REG_BASE + 0x005c)
+#define		AR9170_PHY_AGC_CTL1_COARSE_LOW		0x00007f80
+#define		AR9170_PHY_AGC_CTL1_COARSE_LOW_S	7
+#define		AR9170_PHY_AGC_CTL1_COARSE_HIGH		0x003f8000
+#define		AR9170_PHY_AGC_CTL1_COARSE_HIGH_S	15
+
+#define	AR9170_PHY_REG_AGC_CONTROL		(AR9170_PHY_REG_BASE + 0x0060)
+#define		AR9170_PHY_AGC_CONTROL_CAL		0x00000001
+#define		AR9170_PHY_AGC_CONTROL_NF		0x00000002
+#define		AR9170_PHY_AGC_CONTROL_ENABLE_NF	0x00008000
+#define		AR9170_PHY_AGC_CONTROL_FLTR_CAL		0x00010000
+#define		AR9170_PHY_AGC_CONTROL_NO_UPDATE_NF	0x00020000
+
+#define	AR9170_PHY_REG_CCA			(AR9170_PHY_REG_BASE + 0x0064)
+#define		AR9170_PHY_CCA_MINCCA_PWR		0x0ff80000
+#define		AR9170_PHY_CCA_MINCCA_PWR_S		19
+#define		AR9170_PHY_CCA_THRESH62			0x0007f000
+#define		AR9170_PHY_CCA_THRESH62_S		12
+
+#define	AR9170_PHY_REG_SFCORR			(AR9170_PHY_REG_BASE + 0x0068)
+#define		AR9170_PHY_SFCORR_M2COUNT_THR		0x0000001f
+#define		AR9170_PHY_SFCORR_M2COUNT_THR_S		0
+#define		AR9170_PHY_SFCORR_M1_THRESH		0x00fe0000
+#define		AR9170_PHY_SFCORR_M1_THRESH_S		17
+#define		AR9170_PHY_SFCORR_M2_THRESH		0x7f000000
+#define		AR9170_PHY_SFCORR_M2_THRESH_S		24
+
+#define	AR9170_PHY_REG_SFCORR_LOW		(AR9170_PHY_REG_BASE + 0x006c)
+#define		AR9170_PHY_SFCORR_LOW_USE_SELF_CORR_LOW	0x00000001
+#define		AR9170_PHY_SFCORR_LOW_M2COUNT_THR_LOW	0x00003f00
+#define		AR9170_PHY_SFCORR_LOW_M2COUNT_THR_LOW_S	8
+#define		AR9170_PHY_SFCORR_LOW_M1_THRESH_LOW	0x001fc000
+#define		AR9170_PHY_SFCORR_LOW_M1_THRESH_LOW_S	14
+#define		AR9170_PHY_SFCORR_LOW_M2_THRESH_LOW	0x0fe00000
+#define		AR9170_PHY_SFCORR_LOW_M2_THRESH_LOW_S	21
+
+#define	AR9170_PHY_REG_SLEEP_CTR_CONTROL	(AR9170_PHY_REG_BASE + 0x0070)
+#define	AR9170_PHY_REG_SLEEP_CTR_LIMIT		(AR9170_PHY_REG_BASE + 0x0074)
+#define	AR9170_PHY_REG_SLEEP_SCAL		(AR9170_PHY_REG_BASE + 0x0078)
+
+#define	AR9170_PHY_REG_PLL_CTL			(AR9170_PHY_REG_BASE + 0x007c)
+#define		AR9170_PHY_PLL_CTL_40			0xaa
+#define		AR9170_PHY_PLL_CTL_40_5413		0x04
+#define		AR9170_PHY_PLL_CTL_44			0xab
+#define		AR9170_PHY_PLL_CTL_44_2133		0xeb
+#define		AR9170_PHY_PLL_CTL_40_2133		0xea
+
+#define	AR9170_PHY_REG_BIN_MASK_1		(AR9170_PHY_REG_BASE + 0x0100)
+#define	AR9170_PHY_REG_BIN_MASK_2		(AR9170_PHY_REG_BASE + 0x0104)
+#define	AR9170_PHY_REG_BIN_MASK_3		(AR9170_PHY_REG_BASE + 0x0108)
+#define	AR9170_PHY_REG_MASK_CTL			(AR9170_PHY_REG_BASE + 0x010c)
+
+/* analogue power on time (100ns) */
+#define	AR9170_PHY_REG_RX_DELAY			(AR9170_PHY_REG_BASE + 0x0114)
+#define	AR9170_PHY_REG_SEARCH_START_DELAY	(AR9170_PHY_REG_BASE + 0x0118)
+#define		AR9170_PHY_RX_DELAY_DELAY		0x00003fff
+
+#define	AR9170_PHY_REG_TIMING_CTRL4(_i)		(AR9170_PHY_REG_BASE + \
+						(0x0120 + ((_i) << 12)))
+#define		AR9170_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF		0x01f
+#define		AR9170_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF_S	0
+#define		AR9170_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF		0x7e0
+#define		AR9170_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF_S	5
+#define		AR9170_PHY_TIMING_CTRL4_IQCORR_ENABLE		0x800
+#define		AR9170_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX	0xf000
+#define		AR9170_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX_S	12
+#define		AR9170_PHY_TIMING_CTRL4_DO_IQCAL		0x10000
+#define		AR9170_PHY_TIMING_CTRL4_ENABLE_SPUR_RSSI	0x80000000
+#define		AR9170_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER	0x40000000
+#define		AR9170_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK	0x20000000
+#define		AR9170_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK	0x10000000
+
+#define	AR9170_PHY_REG_TIMING5			(AR9170_PHY_REG_BASE + 0x0124)
+#define		AR9170_PHY_TIMING5_CYCPWR_THR1		0x000000fe
+#define		AR9170_PHY_TIMING5_CYCPWR_THR1_S	1
+
+#define	AR9170_PHY_REG_POWER_TX_RATE1		(AR9170_PHY_REG_BASE + 0x0134)
+#define	AR9170_PHY_REG_POWER_TX_RATE2		(AR9170_PHY_REG_BASE + 0x0138)
+#define	AR9170_PHY_REG_POWER_TX_RATE_MAX	(AR9170_PHY_REG_BASE + 0x013c)
+#define		AR9170_PHY_POWER_TX_RATE_MAX_TPC_ENABLE	0x00000040
+
+#define	AR9170_PHY_REG_FRAME_CTL		(AR9170_PHY_REG_BASE + 0x0144)
+#define		AR9170_PHY_FRAME_CTL_TX_CLIP		0x00000038
+#define		AR9170_PHY_FRAME_CTL_TX_CLIP_S		3
+
+#define	AR9170_PHY_REG_SPUR_REG			(AR9170_PHY_REG_BASE + 0x014c)
+#define		AR9170_PHY_SPUR_REG_MASK_RATE_CNTL	(0xff << 18)
+#define		AR9170_PHY_SPUR_REG_MASK_RATE_CNTL_S	18
+#define		AR9170_PHY_SPUR_REG_ENABLE_MASK_PPM	0x20000
+#define		AR9170_PHY_SPUR_REG_MASK_RATE_SELECT	(0xff << 9)
+#define		AR9170_PHY_SPUR_REG_MASK_RATE_SELECT_S	9
+#define		AR9170_PHY_SPUR_REG_ENABLE_VIT_SPUR_RSSI	0x100
+#define		AR9170_PHY_SPUR_REG_SPUR_RSSI_THRESH	0x7f
+#define		AR9170_PHY_SPUR_REG_SPUR_RSSI_THRESH_S	0
+
+#define	AR9170_PHY_REG_RADAR_EXT		(AR9170_PHY_REG_BASE + 0x0140)
+#define		AR9170_PHY_RADAR_EXT_ENA		0x00004000
+
+#define	AR9170_PHY_REG_RADAR_0			(AR9170_PHY_REG_BASE + 0x0154)
+#define		AR9170_PHY_RADAR_0_ENA			0x00000001
+#define		AR9170_PHY_RADAR_0_FFT_ENA		0x80000000
+/* inband pulse threshold */
+#define		AR9170_PHY_RADAR_0_INBAND		0x0000003e
+#define		AR9170_PHY_RADAR_0_INBAND_S		1
+/* pulse RSSI threshold */
+#define		AR9170_PHY_RADAR_0_PRSSI		0x00000fc0
+#define		AR9170_PHY_RADAR_0_PRSSI_S		6
+/* pulse height threshold */
+#define		AR9170_PHY_RADAR_0_HEIGHT		0x0003f000
+#define		AR9170_PHY_RADAR_0_HEIGHT_S		12
+/* radar RSSI threshold */
+#define		AR9170_PHY_RADAR_0_RRSSI		0x00fc0000
+#define		AR9170_PHY_RADAR_0_RRSSI_S		18
+/* radar firepower threshold */
+#define		AR9170_PHY_RADAR_0_FIRPWR		0x7f000000
+#define		AR9170_PHY_RADAR_0_FIRPWR_S		24
+
+#define	AR9170_PHY_REG_RADAR_1			(AR9170_PHY_REG_BASE + 0x0158)
+#define		AR9170_PHY_RADAR_1_RELPWR_ENA		0x00800000
+#define		AR9170_PHY_RADAR_1_USE_FIR128		0x00400000
+#define		AR9170_PHY_RADAR_1_RELPWR_THRESH	0x003f0000
+#define		AR9170_PHY_RADAR_1_RELPWR_THRESH_S	16
+#define		AR9170_PHY_RADAR_1_BLOCK_CHECK		0x00008000
+#define		AR9170_PHY_RADAR_1_MAX_RRSSI		0x00004000
+#define		AR9170_PHY_RADAR_1_RELSTEP_CHECK	0x00002000
+#define		AR9170_PHY_RADAR_1_RELSTEP_THRESH	0x00001f00
+#define		AR9170_PHY_RADAR_1_RELSTEP_THRESH_S	8
+#define		AR9170_PHY_RADAR_1_MAXLEN		0x000000ff
+#define		AR9170_PHY_RADAR_1_MAXLEN_S		0
+
+#define	AR9170_PHY_REG_SWITCH_CHAIN_0		(AR9170_PHY_REG_BASE + 0x0160)
+#define	AR9170_PHY_REG_SWITCH_CHAIN_2		(AR9170_PHY_REG_BASE + 0x2160)
+
+#define	AR9170_PHY_REG_SWITCH_COM		(AR9170_PHY_REG_BASE + 0x0164)
+
+#define	AR9170_PHY_REG_CCA_THRESHOLD		(AR9170_PHY_REG_BASE + 0x0168)
+
+#define	AR9170_PHY_REG_SIGMA_DELTA		(AR9170_PHY_REG_BASE + 0x016c)
+#define		AR9170_PHY_SIGMA_DELTA_ADC_SEL		0x00000003
+#define		AR9170_PHY_SIGMA_DELTA_ADC_SEL_S	0
+#define		AR9170_PHY_SIGMA_DELTA_FILT2		0x000000f8
+#define		AR9170_PHY_SIGMA_DELTA_FILT2_S		3
+#define		AR9170_PHY_SIGMA_DELTA_FILT1		0x00001f00
+#define		AR9170_PHY_SIGMA_DELTA_FILT1_S		8
+#define		AR9170_PHY_SIGMA_DELTA_ADC_CLIP		0x01ffe000
+#define		AR9170_PHY_SIGMA_DELTA_ADC_CLIP_S	13
+
+#define	AR9170_PHY_REG_RESTART			(AR9170_PHY_REG_BASE + 0x0170)
+#define		AR9170_PHY_RESTART_DIV_GC		0x001c0000
+#define		AR9170_PHY_RESTART_DIV_GC_S		18
+
+#define	AR9170_PHY_REG_RFBUS_REQ		(AR9170_PHY_REG_BASE + 0x017c)
+#define		AR9170_PHY_RFBUS_REQ_EN			0x00000001
+
+#define	AR9170_PHY_REG_TIMING7			(AR9170_PHY_REG_BASE + 0x0180)
+#define	AR9170_PHY_REG_TIMING8			(AR9170_PHY_REG_BASE + 0x0184)
+#define		AR9170_PHY_TIMING8_PILOT_MASK_2		0x000fffff
+#define		AR9170_PHY_TIMING8_PILOT_MASK_2_S	0
+
+#define	AR9170_PHY_REG_BIN_MASK2_1		(AR9170_PHY_REG_BASE + 0x0188)
+#define	AR9170_PHY_REG_BIN_MASK2_2		(AR9170_PHY_REG_BASE + 0x018c)
+#define	AR9170_PHY_REG_BIN_MASK2_3		(AR9170_PHY_REG_BASE + 0x0190)
+#define	AR9170_PHY_REG_BIN_MASK2_4		(AR9170_PHY_REG_BASE + 0x0194)
+#define		AR9170_PHY_BIN_MASK2_4_MASK_4		0x00003fff
+#define		AR9170_PHY_BIN_MASK2_4_MASK_4_S		0
+
+#define	AR9170_PHY_REG_TIMING9			(AR9170_PHY_REG_BASE + 0x0198)
+#define	AR9170_PHY_REG_TIMING10			(AR9170_PHY_REG_BASE + 0x019c)
+#define		AR9170_PHY_TIMING10_PILOT_MASK_2	0x000fffff
+#define		AR9170_PHY_TIMING10_PILOT_MASK_2_S	0
+
+#define	AR9170_PHY_REG_TIMING11			(AR9170_PHY_REG_BASE + 0x01a0)
+#define		AR9170_PHY_TIMING11_SPUR_DELTA_PHASE	0x000fffff
+#define		AR9170_PHY_TIMING11_SPUR_DELTA_PHASE_S	0
+#define		AR9170_PHY_TIMING11_SPUR_FREQ_SD	0x3ff00000
+#define		AR9170_PHY_TIMING11_SPUR_FREQ_SD_S	20
+#define		AR9170_PHY_TIMING11_USE_SPUR_IN_AGC	0x40000000
+#define		AR9170_PHY_TIMING11_USE_SPUR_IN_SELFCOR	0x80000000
+
+#define	AR9170_PHY_REG_RX_CHAINMASK		(AR9170_PHY_REG_BASE + 0x01a4)
+#define	AR9170_PHY_REG_NEW_ADC_DC_GAIN_CORR(_i)	(AR9170_PHY_REG_BASE + \
+						 0x01b4 + ((_i) << 12))
+#define		AR9170_PHY_NEW_ADC_GAIN_CORR_ENABLE		0x40000000
+#define		AR9170_PHY_NEW_ADC_DC_OFFSET_CORR_ENABLE	0x80000000
+
+#define	AR9170_PHY_REG_MULTICHAIN_GAIN_CTL	(AR9170_PHY_REG_BASE + 0x01ac)
+#define		AR9170_PHY_9285_ANT_DIV_CTL_ALL		0x7f000000
+#define		AR9170_PHY_9285_ANT_DIV_CTL		0x01000000
+#define		AR9170_PHY_9285_ANT_DIV_CTL_S		24
+#define		AR9170_PHY_9285_ANT_DIV_ALT_LNACONF	0x06000000
+#define		AR9170_PHY_9285_ANT_DIV_ALT_LNACONF_S	25
+#define		AR9170_PHY_9285_ANT_DIV_MAIN_LNACONF	0x18000000
+#define		AR9170_PHY_9285_ANT_DIV_MAIN_LNACONF_S	27
+#define		AR9170_PHY_9285_ANT_DIV_ALT_GAINTB	0x20000000
+#define		AR9170_PHY_9285_ANT_DIV_ALT_GAINTB_S	29
+#define		AR9170_PHY_9285_ANT_DIV_MAIN_GAINTB	0x40000000
+#define		AR9170_PHY_9285_ANT_DIV_MAIN_GAINTB_S	30
+#define		AR9170_PHY_9285_ANT_DIV_LNA1		2
+#define		AR9170_PHY_9285_ANT_DIV_LNA2		1
+#define		AR9170_PHY_9285_ANT_DIV_LNA1_PLUS_LNA2	3
+#define		AR9170_PHY_9285_ANT_DIV_LNA1_MINUS_LNA2	0
+#define		AR9170_PHY_9285_ANT_DIV_GAINTB_0	0
+#define		AR9170_PHY_9285_ANT_DIV_GAINTB_1	1
+
+#define	AR9170_PHY_REG_EXT_CCA0			(AR9170_PHY_REG_BASE + 0x01b8)
+#define		AR9170_PHY_REG_EXT_CCA0_THRESH62	0x000000ff
+#define		AR9170_PHY_REG_EXT_CCA0_THRESH62_S	0
+
+#define	AR9170_PHY_REG_EXT_CCA			(AR9170_PHY_REG_BASE + 0x01bc)
+#define		AR9170_PHY_EXT_CCA_CYCPWR_THR1		0x0000fe00
+#define		AR9170_PHY_EXT_CCA_CYCPWR_THR1_S	9
+#define		AR9170_PHY_EXT_CCA_THRESH62		0x007f0000
+#define		AR9170_PHY_EXT_CCA_THRESH62_S		16
+#define		AR9170_PHY_EXT_MINCCA_PWR		0xff800000
+#define		AR9170_PHY_EXT_MINCCA_PWR_S		23
+
+#define	AR9170_PHY_REG_SFCORR_EXT		(AR9170_PHY_REG_BASE + 0x01c0)
+#define		AR9170_PHY_SFCORR_EXT_M1_THRESH		0x0000007f
+#define		AR9170_PHY_SFCORR_EXT_M1_THRESH_S	0
+#define		AR9170_PHY_SFCORR_EXT_M2_THRESH		0x00003f80
+#define		AR9170_PHY_SFCORR_EXT_M2_THRESH_S	7
+#define		AR9170_PHY_SFCORR_EXT_M1_THRESH_LOW	0x001fc000
+#define		AR9170_PHY_SFCORR_EXT_M1_THRESH_LOW_S	14
+#define		AR9170_PHY_SFCORR_EXT_M2_THRESH_LOW	0x0fe00000
+#define		AR9170_PHY_SFCORR_EXT_M2_THRESH_LOW_S	21
+#define		AR9170_PHY_SFCORR_SPUR_SUBCHNL_SD_S	28
+
+#define	AR9170_PHY_REG_HALFGI			(AR9170_PHY_REG_BASE + 0x01d0)
+#define		AR9170_PHY_HALFGI_DSC_MAN		0x0007fff0
+#define		AR9170_PHY_HALFGI_DSC_MAN_S		4
+#define		AR9170_PHY_HALFGI_DSC_EXP		0x0000000f
+#define		AR9170_PHY_HALFGI_DSC_EXP_S		0
+
+#define	AR9170_PHY_REG_CHANNEL_MASK_01_30	(AR9170_PHY_REG_BASE + 0x01d4)
+#define	AR9170_PHY_REG_CHANNEL_MASK_31_60	(AR9170_PHY_REG_BASE + 0x01d8)
+
+#define	AR9170_PHY_REG_CHAN_INFO_MEMORY		(AR9170_PHY_REG_BASE + 0x01dc)
+#define		AR9170_PHY_CHAN_INFO_MEMORY_CAPTURE_MASK	0x0001
+
+#define	AR9170_PHY_REG_HEAVY_CLIP_ENABLE	(AR9170_PHY_REG_BASE + 0x01e0)
+#define	AR9170_PHY_REG_HEAVY_CLIP_FACTOR_RIFS	(AR9170_PHY_REG_BASE + 0x01ec)
+#define		AR9170_PHY_RIFS_INIT_DELAY		0x03ff0000
+
+#define	AR9170_PHY_REG_CALMODE			(AR9170_PHY_REG_BASE + 0x01f0)
+#define		AR9170_PHY_CALMODE_IQ			0x00000000
+#define		AR9170_PHY_CALMODE_ADC_GAIN		0x00000001
+#define		AR9170_PHY_CALMODE_ADC_DC_PER		0x00000002
+#define		AR9170_PHY_CALMODE_ADC_DC_INIT		0x00000003
+
+#define	AR9170_PHY_REG_REFCLKDLY		(AR9170_PHY_REG_BASE + 0x01f4)
+#define	AR9170_PHY_REG_REFCLKPD			(AR9170_PHY_REG_BASE + 0x01f8)
+
+
+#define	AR9170_PHY_REG_CAL_MEAS_0(_i)		(AR9170_PHY_REG_BASE + \
+						 0x0410 + ((_i) << 12))
+#define	AR9170_PHY_REG_CAL_MEAS_1(_i)		(AR9170_PHY_REG_BASE + \
+						 0x0414 \ + ((_i) << 12))
+#define	AR9170_PHY_REG_CAL_MEAS_2(_i)		(AR9170_PHY_REG_BASE + \
+						 0x0418 + ((_i) << 12))
+#define	AR9170_PHY_REG_CAL_MEAS_3(_i)		(AR9170_PHY_REG_BASE + \
+						 0x041c + ((_i) << 12))
+
+#define	AR9170_PHY_REG_CURRENT_RSSI		(AR9170_PHY_REG_BASE + 0x041c)
+
+#define	AR9170_PHY_REG_RFBUS_GRANT		(AR9170_PHY_REG_BASE + 0x0420)
+#define		AR9170_PHY_RFBUS_GRANT_EN		0x00000001
+
+#define	AR9170_PHY_REG_CHAN_INFO_GAIN_DIFF	(AR9170_PHY_REG_BASE + 0x04f4)
+#define		AR9170_PHY_CHAN_INFO_GAIN_DIFF_UPPER_LIMIT	320
+
+#define	AR9170_PHY_REG_CHAN_INFO_GAIN		(AR9170_PHY_REG_BASE + 0x04fc)
+
+#define	AR9170_PHY_REG_MODE			(AR9170_PHY_REG_BASE + 0x0a00)
+#define		AR9170_PHY_MODE_ASYNCFIFO		0x80
+#define		AR9170_PHY_MODE_AR2133			0x08
+#define		AR9170_PHY_MODE_AR5111			0x00
+#define		AR9170_PHY_MODE_AR5112			0x08
+#define		AR9170_PHY_MODE_DYNAMIC			0x04
+#define		AR9170_PHY_MODE_RF2GHZ			0x02
+#define		AR9170_PHY_MODE_RF5GHZ			0x00
+#define		AR9170_PHY_MODE_CCK			0x01
+#define		AR9170_PHY_MODE_OFDM			0x00
+#define		AR9170_PHY_MODE_DYN_CCK_DISABLE		0x100
+
+#define	AR9170_PHY_REG_CCK_TX_CTRL		(AR9170_PHY_REG_BASE + 0x0a04)
+#define		AR9170_PHY_CCK_TX_CTRL_JAPAN			0x00000010
+#define		AR9170_PHY_CCK_TX_CTRL_TX_DAC_SCALE_CCK         0x0000000c
+#define		AR9170_PHY_CCK_TX_CTRL_TX_DAC_SCALE_CCK_S       2
+
+#define	AR9170_PHY_REG_CCK_DETECT		(AR9170_PHY_REG_BASE + 0x0a08)
+#define		AR9170_PHY_CCK_DETECT_WEAK_SIG_THR_CCK		0x0000003f
+#define		AR9170_PHY_CCK_DETECT_WEAK_SIG_THR_CCK_S	0
+/* [12:6] settling time for antenna switch */
+#define		AR9170_PHY_CCK_DETECT_ANT_SWITCH_TIME		0x00001fc0
+#define		AR9170_PHY_CCK_DETECT_ANT_SWITCH_TIME_S		6
+#define		AR9170_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV	0x2000
+#define		AR9170_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV_S	13
+
+#define	AR9170_PHY_REG_GAIN_2GHZ_CHAIN_2	(AR9170_PHY_REG_BASE + 0x2a0c)
+#define	AR9170_PHY_REG_GAIN_2GHZ		(AR9170_PHY_REG_BASE + 0x0a0c)
+#define		AR9170_PHY_GAIN_2GHZ_RXTX_MARGIN	0x00fc0000
+#define		AR9170_PHY_GAIN_2GHZ_RXTX_MARGIN_S	18
+#define		AR9170_PHY_GAIN_2GHZ_BSW_MARGIN		0x00003c00
+#define		AR9170_PHY_GAIN_2GHZ_BSW_MARGIN_S	10
+#define		AR9170_PHY_GAIN_2GHZ_BSW_ATTEN		0x0000001f
+#define		AR9170_PHY_GAIN_2GHZ_BSW_ATTEN_S	0
+#define		AR9170_PHY_GAIN_2GHZ_XATTEN2_MARGIN	0x003e0000
+#define		AR9170_PHY_GAIN_2GHZ_XATTEN2_MARGIN_S	17
+#define		AR9170_PHY_GAIN_2GHZ_XATTEN1_MARGIN	0x0001f000
+#define		AR9170_PHY_GAIN_2GHZ_XATTEN1_MARGIN_S	12
+#define		AR9170_PHY_GAIN_2GHZ_XATTEN2_DB		0x00000fc0
+#define		AR9170_PHY_GAIN_2GHZ_XATTEN2_DB_S	6
+#define		AR9170_PHY_GAIN_2GHZ_XATTEN1_DB		0x0000003f
+#define		AR9170_PHY_GAIN_2GHZ_XATTEN1_DB_S	0
+
+#define	AR9170_PHY_REG_CCK_RXCTRL4		(AR9170_PHY_REG_BASE + 0x0a1c)
+#define		AR9170_PHY_CCK_RXCTRL4_FREQ_EST_SHORT	0x01f80000
+#define		AR9170_PHY_CCK_RXCTRL4_FREQ_EST_SHORT_S	19
+
+#define	AR9170_PHY_REG_DAG_CTRLCCK		(AR9170_PHY_REG_BASE + 0x0a28)
+#define		AR9170_REG_DAG_CTRLCCK_EN_RSSI_THR	0x00000200
+#define		AR9170_REG_DAG_CTRLCCK_RSSI_THR		0x0001fc00
+#define		AR9170_REG_DAG_CTRLCCK_RSSI_THR_S	10
+
+#define	AR9170_PHY_REG_FORCE_CLKEN_CCK		(AR9170_PHY_REG_BASE + 0x0a2c)
+#define		AR9170_FORCE_CLKEN_CCK_MRC_MUX		0x00000040
+
+#define	AR9170_PHY_REG_POWER_TX_RATE3		(AR9170_PHY_REG_BASE + 0x0a34)
+#define	AR9170_PHY_REG_POWER_TX_RATE4		(AR9170_PHY_REG_BASE + 0x0a38)
+
+#define	AR9170_PHY_REG_SCRM_SEQ_XR		(AR9170_PHY_REG_BASE + 0x0a3c)
+#define	AR9170_PHY_REG_HEADER_DETECT_XR		(AR9170_PHY_REG_BASE + 0x0a40)
+#define	AR9170_PHY_REG_CHIRP_DETECTED_XR	(AR9170_PHY_REG_BASE + 0x0a44)
+#define	AR9170_PHY_REG_BLUETOOTH		(AR9170_PHY_REG_BASE + 0x0a54)
+
+#define	AR9170_PHY_REG_TPCRG1			(AR9170_PHY_REG_BASE + 0x0a58)
+#define		AR9170_PHY_TPCRG1_NUM_PD_GAIN		0x0000c000
+#define		AR9170_PHY_TPCRG1_NUM_PD_GAIN_S		14
+#define		AR9170_PHY_TPCRG1_PD_GAIN_1		0x00030000
+#define		AR9170_PHY_TPCRG1_PD_GAIN_1_S		16
+#define		AR9170_PHY_TPCRG1_PD_GAIN_2		0x000c0000
+#define		AR9170_PHY_TPCRG1_PD_GAIN_2_S		18
+#define		AR9170_PHY_TPCRG1_PD_GAIN_3		0x00300000
+#define		AR9170_PHY_TPCRG1_PD_GAIN_3_S		20
+#define		AR9170_PHY_TPCRG1_PD_CAL_ENABLE		0x00400000
+#define		AR9170_PHY_TPCRG1_PD_CAL_ENABLE_S	22
+
+#define	AR9170_PHY_REG_TX_PWRCTRL4		(AR9170_PHY_REG_BASE + 0x0a64)
+#define		AR9170_PHY_TX_PWRCTRL_PD_AVG_VALID	0x00000001
+#define		AR9170_PHY_TX_PWRCTRL_PD_AVG_VALID_S	0
+#define		AR9170_PHY_TX_PWRCTRL_PD_AVG_OUT	0x000001fe
+#define		AR9170_PHY_TX_PWRCTRL_PD_AVG_OUT_S	1
+
+#define	AR9170_PHY_REG_ANALOG_SWAP		(AR9170_PHY_REG_BASE + 0x0a68)
+#define		AR9170_PHY_ANALOG_SWAP_AB		0x0001
+#define		AR9170_PHY_ANALOG_SWAP_ALT_CHAIN	0x00000040
+
+#define	AR9170_PHY_REG_TPCRG5			(AR9170_PHY_REG_BASE + 0x0a6c)
+#define		AR9170_PHY_TPCRG5_PD_GAIN_OVERLAP	0x0000000f
+#define		AR9170_PHY_TPCRG5_PD_GAIN_OVERLAP_S	0
+#define		AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_1	0x000003f0
+#define		AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_1_S	4
+#define		AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_2	0x0000fc00
+#define		AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_2_S	10
+#define		AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_3    0x003f0000
+#define		AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_3_S  16
+#define		AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_4    0x0fc00000
+#define		AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_4_S  22
+
+#define	AR9170_PHY_REG_TX_PWRCTRL6_0		(AR9170_PHY_REG_BASE + 0x0a70)
+#define	AR9170_PHY_REG_TX_PWRCTRL6_1		(AR9170_PHY_REG_BASE + 0x1a70)
+#define		AR9170_PHY_TX_PWRCTRL_ERR_EST_MODE	0x03000000
+#define		AR9170_PHY_TX_PWRCTRL_ERR_EST_MODE_S	24
+
+#define	AR9170_PHY_REG_TX_PWRCTRL7		(AR9170_PHY_REG_BASE + 0x0a74)
+#define		AR9170_PHY_TX_PWRCTRL_INIT_TX_GAIN	0x01f80000
+#define		AR9170_PHY_TX_PWRCTRL_INIT_TX_GAIN_S	19
+
+#define	AR9170_PHY_REG_TX_PWRCTRL9		(AR9170_PHY_REG_BASE + 0x0a7c)
+#define		AR9170_PHY_TX_DESIRED_SCALE_CCK		0x00007c00
+#define		AR9170_PHY_TX_DESIRED_SCALE_CCK_S	10
+#define		AR9170_PHY_TX_PWRCTRL9_RES_DC_REMOVAL	0x80000000
+#define		AR9170_PHY_TX_PWRCTRL9_RES_DC_REMOVAL_S	31
+
+#define	AR9170_PHY_REG_TX_GAIN_TBL1		(AR9170_PHY_REG_BASE + 0x0b00)
+#define		AR9170_PHY_TX_GAIN			0x0007f000
+#define		AR9170_PHY_TX_GAIN_S			12
+
+/* Carrier leak calibration control, do it after AGC calibration */
+#define	AR9170_PHY_REG_CL_CAL_CTL		(AR9170_PHY_REG_BASE + 0x0b58)
+#define		AR9170_PHY_CL_CAL_ENABLE		0x00000002
+#define		AR9170_PHY_CL_CAL_PARALLEL_CAL_ENABLE	0x00000001
+
+#define	AR9170_PHY_REG_POWER_TX_RATE5		(AR9170_PHY_REG_BASE + 0x0b8c)
+#define	AR9170_PHY_REG_POWER_TX_RATE6		(AR9170_PHY_REG_BASE + 0x0b90)
+
+#define	AR9170_PHY_REG_CH0_TX_PWRCTRL11		(AR9170_PHY_REG_BASE + 0x0b98)
+#define	AR9170_PHY_REG_CH1_TX_PWRCTRL11		(AR9170_PHY_REG_BASE + 0x1b98)
+#define		AR9170_PHY_TX_CHX_PWRCTRL_OLPC_TEMP_COMP	0x0000fc00
+#define		AR9170_PHY_TX_CHX_PWRCTRL_OLPC_TEMP_COMP_S	10
+
+#define	AR9170_PHY_REG_CAL_CHAINMASK		(AR9170_PHY_REG_BASE + 0x0b9c)
+#define	AR9170_PHY_REG_VIT_MASK2_M_46_61	(AR9170_PHY_REG_BASE + 0x0ba0)
+#define	AR9170_PHY_REG_MASK2_M_31_45		(AR9170_PHY_REG_BASE + 0x0ba4)
+#define	AR9170_PHY_REG_MASK2_M_16_30		(AR9170_PHY_REG_BASE + 0x0ba8)
+#define	AR9170_PHY_REG_MASK2_M_00_15		(AR9170_PHY_REG_BASE + 0x0bac)
+#define	AR9170_PHY_REG_PILOT_MASK_01_30		(AR9170_PHY_REG_BASE + 0x0bb0)
+#define	AR9170_PHY_REG_PILOT_MASK_31_60		(AR9170_PHY_REG_BASE + 0x0bb4)
+#define	AR9170_PHY_REG_MASK2_P_15_01		(AR9170_PHY_REG_BASE + 0x0bb8)
+#define	AR9170_PHY_REG_MASK2_P_30_16		(AR9170_PHY_REG_BASE + 0x0bbc)
+#define	AR9170_PHY_REG_MASK2_P_45_31		(AR9170_PHY_REG_BASE + 0x0bc0)
+#define	AR9170_PHY_REG_MASK2_P_61_45		(AR9170_PHY_REG_BASE + 0x0bc4)
+#define	AR9170_PHY_REG_POWER_TX_SUB		(AR9170_PHY_REG_BASE + 0x0bc8)
+#define	AR9170_PHY_REG_POWER_TX_RATE7		(AR9170_PHY_REG_BASE + 0x0bcc)
+#define	AR9170_PHY_REG_POWER_TX_RATE8		(AR9170_PHY_REG_BASE + 0x0bd0)
+#define	AR9170_PHY_REG_POWER_TX_RATE9		(AR9170_PHY_REG_BASE + 0x0bd4)
+#define	AR9170_PHY_REG_XPA_CFG			(AR9170_PHY_REG_BASE + 0x0bd8)
+#define		AR9170_PHY_FORCE_XPA_CFG		0x000000001
+#define		AR9170_PHY_FORCE_XPA_CFG_S		0
+
+#define	AR9170_PHY_REG_CH1_CCA			(AR9170_PHY_REG_BASE + 0x1064)
+#define		AR9170_PHY_CH1_MINCCA_PWR		0x0ff80000
+#define		AR9170_PHY_CH1_MINCCA_PWR_S		19
+
+#define	AR9170_PHY_REG_CH2_CCA			(AR9170_PHY_REG_BASE + 0x2064)
+#define		AR9170_PHY_CH2_MINCCA_PWR		0x0ff80000
+#define		AR9170_PHY_CH2_MINCCA_PWR_S		19
+
+#define	AR9170_PHY_REG_CH1_EXT_CCA		(AR9170_PHY_REG_BASE + 0x11bc)
+#define		AR9170_PHY_CH1_EXT_MINCCA_PWR		0xff800000
+#define		AR9170_PHY_CH1_EXT_MINCCA_PWR_S		23
+
+#define	AR9170_PHY_REG_CH2_EXT_CCA		(AR9170_PHY_REG_BASE + 0x21bc)
+#define		AR9170_PHY_CH2_EXT_MINCCA_PWR		0xff800000
+#define		AR9170_PHY_CH2_EXT_MINCCA_PWR_S		23
+
+#define	REDUCE_CHAIN_0 0x00000050
+#define	REDUCE_CHAIN_1 0x00000051
+
+#endif	/* __CARL9170_SHARED_PHY_H */
diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c
new file mode 100644
index 0000000..671dbc4
--- /dev/null
+++ b/drivers/net/wireless/ath/carl9170/rx.c
@@ -0,0 +1,909 @@
+/*
+ * Atheros CARL9170 driver
+ *
+ * 802.11 & command trap routines
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * This program is free software; you can redistribute it 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 program 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or distribute this software for any
+ *    purpose with or without fee is hereby granted, provided that the above
+ *    copyright notice and this permission notice appear in all copies.
+ *
+ *    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/etherdevice.h>
+#include <linux/crc32.h>
+#include <net/mac80211.h>
+#include "carl9170.h"
+#include "hw.h"
+#include "cmd.h"
+
+static void carl9170_dbg_message(struct ar9170 *ar, const char *buf, u32 len)
+{
+	bool restart = false;
+	enum carl9170_restart_reasons reason = CARL9170_RR_NO_REASON;
+
+	if (len > 3) {
+		if (memcmp(buf, CARL9170_ERR_MAGIC, 3) == 0) {
+			ar->fw.err_counter++;
+			if (ar->fw.err_counter > 3) {
+				restart = true;
+				reason = CARL9170_RR_TOO_MANY_FIRMWARE_ERRORS;
+			}
+		}
+
+		if (memcmp(buf, CARL9170_BUG_MAGIC, 3) == 0) {
+			ar->fw.bug_counter++;
+			restart = true;
+			reason = CARL9170_RR_FATAL_FIRMWARE_ERROR;
+		}
+	}
+
+	wiphy_info(ar->hw->wiphy, "FW: %.*s\n", len, buf);
+
+	if (restart)
+		carl9170_restart(ar, reason);
+}
+
+static void carl9170_handle_ps(struct ar9170 *ar, struct carl9170_rsp *rsp)
+{
+	u32 ps;
+	bool new_ps;
+
+	ps = le32_to_cpu(rsp->psm.state);
+
+	new_ps = (ps & CARL9170_PSM_COUNTER) != CARL9170_PSM_WAKE;
+	if (ar->ps.state != new_ps) {
+		if (!new_ps) {
+			ar->ps.sleep_ms = jiffies_to_msecs(jiffies -
+				ar->ps.last_action);
+		}
+
+		ar->ps.last_action = jiffies;
+
+		ar->ps.state = new_ps;
+	}
+}
+
+static int carl9170_check_sequence(struct ar9170 *ar, unsigned int seq)
+{
+	if (ar->cmd_seq < -1)
+		return 0;
+
+	/*
+	 * Initialize Counter
+	 */
+	if (ar->cmd_seq < 0)
+		ar->cmd_seq = seq;
+
+	/*
+	 * The sequence is strictly monotonic increasing and it never skips!
+	 *
+	 * Therefore we can safely assume that whenever we received an
+	 * unexpected sequence we have lost some valuable data.
+	 */
+	if (seq != ar->cmd_seq) {
+		int count;
+
+		count = (seq - ar->cmd_seq) % ar->fw.cmd_bufs;
+
+		wiphy_err(ar->hw->wiphy, "lost %d command responses/traps! "
+			  "w:%d g:%d\n", count, ar->cmd_seq, seq);
+
+		carl9170_restart(ar, CARL9170_RR_LOST_RSP);
+		return -EIO;
+	}
+
+	ar->cmd_seq = (ar->cmd_seq + 1) % ar->fw.cmd_bufs;
+	return 0;
+}
+
+static void carl9170_cmd_callback(struct ar9170 *ar, u32 len, void *buffer)
+{
+	/*
+	 * Some commands may have a variable response length
+	 * and we cannot predict the correct length in advance.
+	 * So we only check if we provided enough space for the data.
+	 */
+	if (unlikely(ar->readlen != (len - 4))) {
+		dev_warn(&ar->udev->dev, "received invalid command response:"
+			 "got %d, instead of %d\n", len - 4, ar->readlen);
+		print_hex_dump_bytes("carl9170 cmd:", DUMP_PREFIX_OFFSET,
+			ar->cmd_buf, (ar->cmd.hdr.len + 4) & 0x3f);
+		print_hex_dump_bytes("carl9170 rsp:", DUMP_PREFIX_OFFSET,
+			buffer, len);
+		/*
+		 * Do not complete. The command times out,
+		 * and we get a stack trace from there.
+		 */
+		carl9170_restart(ar, CARL9170_RR_INVALID_RSP);
+	}
+
+	spin_lock(&ar->cmd_lock);
+	if (ar->readbuf) {
+		if (len >= 4)
+			memcpy(ar->readbuf, buffer + 4, len - 4);
+
+		ar->readbuf = NULL;
+	}
+	complete(&ar->cmd_wait);
+	spin_unlock(&ar->cmd_lock);
+}
+
+void carl9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len)
+{
+	struct carl9170_rsp *cmd = (void *) buf;
+	struct ieee80211_vif *vif;
+
+	if (carl9170_check_sequence(ar, cmd->hdr.seq))
+		return;
+
+	if ((cmd->hdr.cmd & CARL9170_RSP_FLAG) != CARL9170_RSP_FLAG) {
+		if (!(cmd->hdr.cmd & CARL9170_CMD_ASYNC_FLAG))
+			carl9170_cmd_callback(ar, len, buf);
+
+		return;
+	}
+
+	if (unlikely(cmd->hdr.len != (len - 4))) {
+		if (net_ratelimit()) {
+			wiphy_err(ar->hw->wiphy, "FW: received over-/under"
+				"sized event %x (%d, but should be %d).\n",
+			       cmd->hdr.cmd, cmd->hdr.len, len - 4);
+
+			print_hex_dump_bytes("dump:", DUMP_PREFIX_NONE,
+					     buf, len);
+		}
+
+		return;
+	}
+
+	/* hardware event handlers */
+	switch (cmd->hdr.cmd) {
+	case CARL9170_RSP_PRETBTT:
+		/* pre-TBTT event */
+		rcu_read_lock();
+		vif = carl9170_get_main_vif(ar);
+
+		if (!vif) {
+			rcu_read_unlock();
+			break;
+		}
+
+		switch (vif->type) {
+		case NL80211_IFTYPE_STATION:
+			carl9170_handle_ps(ar, cmd);
+			break;
+
+		case NL80211_IFTYPE_AP:
+		case NL80211_IFTYPE_ADHOC:
+			carl9170_update_beacon(ar, true);
+			break;
+
+		default:
+			break;
+		}
+		rcu_read_unlock();
+
+		break;
+
+
+	case CARL9170_RSP_TXCOMP:
+		/* TX status notification */
+		carl9170_tx_process_status(ar, cmd);
+		break;
+
+	case CARL9170_RSP_BEACON_CONFIG:
+		/*
+		 * (IBSS) beacon send notification
+		 * bytes: 04 c2 XX YY B4 B3 B2 B1
+		 *
+		 * XX always 80
+		 * YY always 00
+		 * B1-B4 "should" be the number of send out beacons.
+		 */
+		break;
+
+	case CARL9170_RSP_ATIM:
+		/* End of Atim Window */
+		break;
+
+	case CARL9170_RSP_WATCHDOG:
+		/* Watchdog Interrupt */
+		carl9170_restart(ar, CARL9170_RR_WATCHDOG);
+		break;
+
+	case CARL9170_RSP_TEXT:
+		/* firmware debug */
+		carl9170_dbg_message(ar, (char *)buf + 4, len - 4);
+		break;
+
+	case CARL9170_RSP_HEXDUMP:
+		wiphy_dbg(ar->hw->wiphy, "FW: HD %d\n", len - 4);
+		print_hex_dump_bytes("FW:", DUMP_PREFIX_NONE,
+				     (char *)buf + 4, len - 4);
+		break;
+
+	case CARL9170_RSP_RADAR:
+		if (!net_ratelimit())
+			break;
+
+		wiphy_info(ar->hw->wiphy, "FW: RADAR! Please report this "
+		       "incident to linux-wireless@vger.kernel.org !\n");
+		break;
+
+	case CARL9170_RSP_GPIO:
+#ifdef CONFIG_CARL9170_WPC
+		if (ar->wps.pbc) {
+			bool state = !!(cmd->gpio.gpio & cpu_to_le32(
+				AR9170_GPIO_PORT_WPS_BUTTON_PRESSED));
+
+			if (state != ar->wps.pbc_state) {
+				ar->wps.pbc_state = state;
+				input_report_key(ar->wps.pbc, KEY_WPS_BUTTON,
+						 state);
+				input_sync(ar->wps.pbc);
+			}
+		}
+#endif /* CONFIG_CARL9170_WPC */
+		break;
+
+	case CARL9170_RSP_BOOT:
+		complete(&ar->fw_boot_wait);
+		break;
+
+	default:
+		wiphy_err(ar->hw->wiphy, "FW: received unhandled event %x\n",
+			cmd->hdr.cmd);
+		print_hex_dump_bytes("dump:", DUMP_PREFIX_NONE, buf, len);
+		break;
+	}
+}
+
+static int carl9170_rx_mac_status(struct ar9170 *ar,
+	struct ar9170_rx_head *head, struct ar9170_rx_macstatus *mac,
+	struct ieee80211_rx_status *status)
+{
+	struct ieee80211_channel *chan;
+	u8 error, decrypt;
+
+	BUILD_BUG_ON(sizeof(struct ar9170_rx_head) != 12);
+	BUILD_BUG_ON(sizeof(struct ar9170_rx_macstatus) != 4);
+
+	error = mac->error;
+
+	if (error & AR9170_RX_ERROR_WRONG_RA) {
+		if (!ar->sniffer_enabled)
+			return -EINVAL;
+	}
+
+	if (error & AR9170_RX_ERROR_PLCP) {
+		if (!(ar->filter_state & FIF_PLCPFAIL))
+			return -EINVAL;
+
+		status->flag |= RX_FLAG_FAILED_PLCP_CRC;
+	}
+
+	if (error & AR9170_RX_ERROR_FCS) {
+		ar->tx_fcs_errors++;
+
+		if (!(ar->filter_state & FIF_FCSFAIL))
+			return -EINVAL;
+
+		status->flag |= RX_FLAG_FAILED_FCS_CRC;
+	}
+
+	decrypt = ar9170_get_decrypt_type(mac);
+	if (!(decrypt & AR9170_RX_ENC_SOFTWARE) &&
+	    decrypt != AR9170_ENC_ALG_NONE) {
+		if ((decrypt == AR9170_ENC_ALG_TKIP) &&
+		    (error & AR9170_RX_ERROR_MMIC))
+			status->flag |= RX_FLAG_MMIC_ERROR;
+
+		status->flag |= RX_FLAG_DECRYPTED;
+	}
+
+	if (error & AR9170_RX_ERROR_DECRYPT && !ar->sniffer_enabled)
+		return -ENODATA;
+
+	error &= ~(AR9170_RX_ERROR_MMIC |
+		   AR9170_RX_ERROR_FCS |
+		   AR9170_RX_ERROR_WRONG_RA |
+		   AR9170_RX_ERROR_DECRYPT |
+		   AR9170_RX_ERROR_PLCP);
+
+	/* drop any other error frames */
+	if (unlikely(error)) {
+		/* TODO: update netdevice's RX dropped/errors statistics */
+
+		if (net_ratelimit())
+			wiphy_dbg(ar->hw->wiphy, "received frame with "
+			       "suspicious error code (%#x).\n", error);
+
+		return -EINVAL;
+	}
+
+	chan = ar->channel;
+	if (chan) {
+		status->band = chan->band;
+		status->freq = chan->center_freq;
+	}
+
+	switch (mac->status & AR9170_RX_STATUS_MODULATION) {
+	case AR9170_RX_STATUS_MODULATION_CCK:
+		if (mac->status & AR9170_RX_STATUS_SHORT_PREAMBLE)
+			status->flag |= RX_FLAG_SHORTPRE;
+		switch (head->plcp[0]) {
+		case AR9170_RX_PHY_RATE_CCK_1M:
+			status->rate_idx = 0;
+			break;
+		case AR9170_RX_PHY_RATE_CCK_2M:
+			status->rate_idx = 1;
+			break;
+		case AR9170_RX_PHY_RATE_CCK_5M:
+			status->rate_idx = 2;
+			break;
+		case AR9170_RX_PHY_RATE_CCK_11M:
+			status->rate_idx = 3;
+			break;
+		default:
+			if (net_ratelimit()) {
+				wiphy_err(ar->hw->wiphy, "invalid plcp cck "
+				       "rate (%x).\n", head->plcp[0]);
+			}
+
+			return -EINVAL;
+		}
+		break;
+
+	case AR9170_RX_STATUS_MODULATION_DUPOFDM:
+	case AR9170_RX_STATUS_MODULATION_OFDM:
+		switch (head->plcp[0] & 0xf) {
+		case AR9170_TXRX_PHY_RATE_OFDM_6M:
+			status->rate_idx = 0;
+			break;
+		case AR9170_TXRX_PHY_RATE_OFDM_9M:
+			status->rate_idx = 1;
+			break;
+		case AR9170_TXRX_PHY_RATE_OFDM_12M:
+			status->rate_idx = 2;
+			break;
+		case AR9170_TXRX_PHY_RATE_OFDM_18M:
+			status->rate_idx = 3;
+			break;
+		case AR9170_TXRX_PHY_RATE_OFDM_24M:
+			status->rate_idx = 4;
+			break;
+		case AR9170_TXRX_PHY_RATE_OFDM_36M:
+			status->rate_idx = 5;
+			break;
+		case AR9170_TXRX_PHY_RATE_OFDM_48M:
+			status->rate_idx = 6;
+			break;
+		case AR9170_TXRX_PHY_RATE_OFDM_54M:
+			status->rate_idx = 7;
+			break;
+		default:
+			if (net_ratelimit()) {
+				wiphy_err(ar->hw->wiphy, "invalid plcp ofdm "
+					"rate (%x).\n", head->plcp[0]);
+			}
+
+			return -EINVAL;
+		}
+		if (status->band == IEEE80211_BAND_2GHZ)
+			status->rate_idx += 4;
+		break;
+
+	case AR9170_RX_STATUS_MODULATION_HT:
+		if (head->plcp[3] & 0x80)
+			status->flag |= RX_FLAG_40MHZ;
+		if (head->plcp[6] & 0x80)
+			status->flag |= RX_FLAG_SHORT_GI;
+
+		status->rate_idx = clamp(0, 75, head->plcp[3] & 0x7f);
+		status->flag |= RX_FLAG_HT;
+		break;
+
+	default:
+		BUG();
+		return -ENOSYS;
+	}
+
+	return 0;
+}
+
+static void carl9170_rx_phy_status(struct ar9170 *ar,
+	struct ar9170_rx_phystatus *phy, struct ieee80211_rx_status *status)
+{
+	int i;
+
+	BUILD_BUG_ON(sizeof(struct ar9170_rx_phystatus) != 20);
+
+	for (i = 0; i < 3; i++)
+		if (phy->rssi[i] != 0x80)
+			status->antenna |= BIT(i);
+
+	/* post-process RSSI */
+	for (i = 0; i < 7; i++)
+		if (phy->rssi[i] & 0x80)
+			phy->rssi[i] = ((phy->rssi[i] & 0x7f) + 1) & 0x7f;
+
+	/* TODO: we could do something with phy_errors */
+	status->signal = ar->noise[0] + phy->rssi_combined;
+}
+
+static struct sk_buff *carl9170_rx_copy_data(u8 *buf, int len)
+{
+	struct sk_buff *skb;
+	int reserved = 0;
+	struct ieee80211_hdr *hdr = (void *) buf;
+
+	if (ieee80211_is_data_qos(hdr->frame_control)) {
+		u8 *qc = ieee80211_get_qos_ctl(hdr);
+		reserved += NET_IP_ALIGN;
+
+		if (*qc & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT)
+			reserved += NET_IP_ALIGN;
+	}
+
+	if (ieee80211_has_a4(hdr->frame_control))
+		reserved += NET_IP_ALIGN;
+
+	reserved = 32 + (reserved & NET_IP_ALIGN);
+
+	skb = dev_alloc_skb(len + reserved);
+	if (likely(skb)) {
+		skb_reserve(skb, reserved);
+		memcpy(skb_put(skb, len), buf, len);
+	}
+
+	return skb;
+}
+
+static u8 *carl9170_find_ie(u8 *data, unsigned int len, u8 ie)
+{
+	struct ieee80211_mgmt *mgmt = (void *)data;
+	u8 *pos, *end;
+
+	pos = (u8 *)mgmt->u.beacon.variable;
+	end = data + len;
+	while (pos < end) {
+		if (pos + 2 + pos[1] > end)
+			return NULL;
+
+		if (pos[0] == ie)
+			return pos;
+
+		pos += 2 + pos[1];
+	}
+	return NULL;
+}
+
+/*
+ * NOTE:
+ *
+ * The firmware is in charge of waking up the device just before
+ * the AP is expected to transmit the next beacon.
+ *
+ * This leaves the driver with the important task of deciding when
+ * to set the PHY back to bed again.
+ */
+static void carl9170_ps_beacon(struct ar9170 *ar, void *data, unsigned int len)
+{
+	struct ieee80211_hdr *hdr = (void *) data;
+	struct ieee80211_tim_ie *tim_ie;
+	u8 *tim;
+	u8 tim_len;
+	bool cam;
+
+	if (likely(!(ar->hw->conf.flags & IEEE80211_CONF_PS)))
+		return;
+
+	/* check if this really is a beacon */
+	if (!ieee80211_is_beacon(hdr->frame_control))
+		return;
+
+	/* min. beacon length + FCS_LEN */
+	if (len <= 40 + FCS_LEN)
+		return;
+
+	/* and only beacons from the associated BSSID, please */
+	if (compare_ether_addr(hdr->addr3, ar->common.curbssid) ||
+	    !ar->common.curaid)
+		return;
+
+	ar->ps.last_beacon = jiffies;
+
+	tim = carl9170_find_ie(data, len - FCS_LEN, WLAN_EID_TIM);
+	if (!tim)
+		return;
+
+	if (tim[1] < sizeof(*tim_ie))
+		return;
+
+	tim_len = tim[1];
+	tim_ie = (struct ieee80211_tim_ie *) &tim[2];
+
+	if (!WARN_ON_ONCE(!ar->hw->conf.ps_dtim_period))
+		ar->ps.dtim_counter = (tim_ie->dtim_count - 1) %
+			ar->hw->conf.ps_dtim_period;
+
+	/* Check whenever the PHY can be turned off again. */
+
+	/* 1. What about buffered unicast traffic for our AID? */
+	cam = ieee80211_check_tim(tim_ie, tim_len, ar->common.curaid);
+
+	/* 2. Maybe the AP wants to send multicast/broadcast data? */
+	cam = !!(tim_ie->bitmap_ctrl & 0x01);
+
+	if (!cam) {
+		/* back to low-power land. */
+		ar->ps.off_override &= ~PS_OFF_BCN;
+		carl9170_ps_check(ar);
+	} else {
+		/* force CAM */
+		ar->ps.off_override |= PS_OFF_BCN;
+	}
+}
+
+/*
+ * If the frame alignment is right (or the kernel has
+ * CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS), and there
+ * is only a single MPDU in the USB frame, then we could
+ * submit to mac80211 the SKB directly. However, since
+ * there may be multiple packets in one SKB in stream
+ * mode, and we need to observe the proper ordering,
+ * this is non-trivial.
+ */
+
+static void carl9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len)
+{
+	struct ar9170_rx_head *head;
+	struct ar9170_rx_macstatus *mac;
+	struct ar9170_rx_phystatus *phy = NULL;
+	struct ieee80211_rx_status status;
+	struct sk_buff *skb;
+	int mpdu_len;
+
+	if (!IS_STARTED(ar))
+		return;
+
+	if (unlikely(len < sizeof(*mac))) {
+		ar->rx_dropped++;
+		return;
+	}
+
+	mpdu_len = len - sizeof(*mac);
+
+	mac = (void *)(buf + mpdu_len);
+	if (unlikely(mac->error & AR9170_RX_ERROR_FATAL)) {
+		ar->rx_dropped++;
+		return;
+	}
+
+	switch (mac->status & AR9170_RX_STATUS_MPDU) {
+	case AR9170_RX_STATUS_MPDU_FIRST:
+		/* Aggregated MPDUs start with an PLCP header */
+		if (likely(mpdu_len >= sizeof(struct ar9170_rx_head))) {
+			head = (void *) buf;
+
+			/*
+			 * The PLCP header needs to be cached for the
+			 * following MIDDLE + LAST A-MPDU packets.
+			 *
+			 * So, if you are wondering why all frames seem
+			 * to share a common RX status information,
+			 * then you have the answer right here...
+			 */
+			memcpy(&ar->rx_plcp, (void *) buf,
+			       sizeof(struct ar9170_rx_head));
+
+			mpdu_len -= sizeof(struct ar9170_rx_head);
+			buf += sizeof(struct ar9170_rx_head);
+
+			ar->rx_has_plcp = true;
+		} else {
+			if (net_ratelimit()) {
+				wiphy_err(ar->hw->wiphy, "plcp info "
+					"is clipped.\n");
+			}
+
+			ar->rx_dropped++;
+			return;
+		}
+		break;
+
+	case AR9170_RX_STATUS_MPDU_LAST:
+		/*
+		 * The last frame of an A-MPDU has an extra tail
+		 * which does contain the phy status of the whole
+		 * aggregate.
+		 */
+
+		if (likely(mpdu_len >= sizeof(struct ar9170_rx_phystatus))) {
+			mpdu_len -= sizeof(struct ar9170_rx_phystatus);
+			phy = (void *)(buf + mpdu_len);
+		} else {
+			if (net_ratelimit()) {
+				wiphy_err(ar->hw->wiphy, "frame tail "
+					"is clipped.\n");
+			}
+
+			ar->rx_dropped++;
+			return;
+		}
+
+	case AR9170_RX_STATUS_MPDU_MIDDLE:
+		/*  These are just data + mac status */
+		if (unlikely(!ar->rx_has_plcp)) {
+			if (!net_ratelimit())
+				return;
+
+			wiphy_err(ar->hw->wiphy, "rx stream does not start "
+					"with a first_mpdu frame tag.\n");
+
+			ar->rx_dropped++;
+			return;
+		}
+
+		head = &ar->rx_plcp;
+		break;
+
+	case AR9170_RX_STATUS_MPDU_SINGLE:
+		/* single mpdu has both: plcp (head) and phy status (tail) */
+		head = (void *) buf;
+
+		mpdu_len -= sizeof(struct ar9170_rx_head);
+		mpdu_len -= sizeof(struct ar9170_rx_phystatus);
+
+		buf += sizeof(struct ar9170_rx_head);
+		phy = (void *)(buf + mpdu_len);
+		break;
+
+	default:
+		BUG_ON(1);
+		break;
+	}
+
+	/* FC + DU + RA + FCS */
+	if (unlikely(mpdu_len < (2 + 2 + 6 + FCS_LEN))) {
+		ar->rx_dropped++;
+		return;
+	}
+
+	memset(&status, 0, sizeof(status));
+	if (unlikely(carl9170_rx_mac_status(ar, head, mac, &status))) {
+		ar->rx_dropped++;
+		return;
+	}
+
+	if (phy)
+		carl9170_rx_phy_status(ar, phy, &status);
+
+	carl9170_ps_beacon(ar, buf, mpdu_len);
+
+	skb = carl9170_rx_copy_data(buf, mpdu_len);
+	if (likely(skb)) {
+		memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status));
+		ieee80211_rx(ar->hw, skb);
+	} else {
+		ar->rx_dropped++;
+	}
+}
+
+static void carl9170_rx_untie_cmds(struct ar9170 *ar, const u8 *respbuf,
+				   const unsigned int resplen)
+{
+	struct carl9170_rsp *cmd;
+	int i = 0;
+
+	while (i < resplen) {
+		cmd = (void *) &respbuf[i];
+
+		i += cmd->hdr.len + 4;
+		if (unlikely(i > resplen))
+			break;
+
+		carl9170_handle_command_response(ar, cmd, cmd->hdr.len + 4);
+	}
+
+	if (unlikely(i != resplen)) {
+		if (!net_ratelimit())
+			return;
+
+		wiphy_err(ar->hw->wiphy, "malformed firmware trap:\n");
+		print_hex_dump_bytes("rxcmd:", DUMP_PREFIX_OFFSET,
+				     respbuf, resplen);
+	}
+}
+
+static void __carl9170_rx(struct ar9170 *ar, u8 *buf, unsigned int len)
+{
+	unsigned int i = 0;
+
+	/* weird thing, but this is the same in the original driver */
+	while (len > 2 && i < 12 && buf[0] == 0xff && buf[1] == 0xff) {
+		i += 2;
+		len -= 2;
+		buf += 2;
+	}
+
+	if (unlikely(len < 4))
+		return;
+
+	/* found the 6 * 0xffff marker? */
+	if (i == 12)
+		carl9170_rx_untie_cmds(ar, buf, len);
+	else
+		carl9170_handle_mpdu(ar, buf, len);
+}
+
+static void carl9170_rx_stream(struct ar9170 *ar, void *buf, unsigned int len)
+{
+	unsigned int tlen, wlen = 0, clen = 0;
+	struct ar9170_stream *rx_stream;
+	u8 *tbuf;
+
+	tbuf = buf;
+	tlen = len;
+
+	while (tlen >= 4) {
+		rx_stream = (void *) tbuf;
+		clen = le16_to_cpu(rx_stream->length);
+		wlen = ALIGN(clen, 4);
+
+		/* check if this is stream has a valid tag.*/
+		if (rx_stream->tag != cpu_to_le16(AR9170_RX_STREAM_TAG)) {
+			/*
+			 * TODO: handle the highly unlikely event that the
+			 * corrupted stream has the TAG at the right position.
+			 */
+
+			/* check if the frame can be repaired. */
+			if (!ar->rx_failover_missing) {
+
+				/* this is not "short read". */
+				if (net_ratelimit()) {
+					wiphy_err(ar->hw->wiphy,
+						"missing tag!\n");
+				}
+
+				__carl9170_rx(ar, tbuf, tlen);
+				return;
+			}
+
+			if (ar->rx_failover_missing > tlen) {
+				if (net_ratelimit()) {
+					wiphy_err(ar->hw->wiphy,
+						"possible multi "
+						"stream corruption!\n");
+					goto err_telluser;
+				} else {
+					goto err_silent;
+				}
+			}
+
+			memcpy(skb_put(ar->rx_failover, tlen), tbuf, tlen);
+			ar->rx_failover_missing -= tlen;
+
+			if (ar->rx_failover_missing <= 0) {
+				/*
+				 * nested carl9170_rx_stream call!
+				 *
+				 * termination is guranteed, even when the
+				 * combined frame also have an element with
+				 * a bad tag.
+				 */
+
+				ar->rx_failover_missing = 0;
+				carl9170_rx_stream(ar, ar->rx_failover->data,
+						   ar->rx_failover->len);
+
+				skb_reset_tail_pointer(ar->rx_failover);
+				skb_trim(ar->rx_failover, 0);
+			}
+
+			return;
+		}
+
+		/* check if stream is clipped */
+		if (wlen > tlen - 4) {
+			if (ar->rx_failover_missing) {
+				/* TODO: handle double stream corruption. */
+				if (net_ratelimit()) {
+					wiphy_err(ar->hw->wiphy, "double rx "
+						"stream corruption!\n");
+					goto err_telluser;
+				} else {
+					goto err_silent;
+				}
+			}
+
+			/*
+			 * save incomplete data set.
+			 * the firmware will resend the missing bits when
+			 * the rx - descriptor comes round again.
+			 */
+
+			memcpy(skb_put(ar->rx_failover, tlen), tbuf, tlen);
+			ar->rx_failover_missing = clen - tlen;
+			return;
+		}
+		__carl9170_rx(ar, rx_stream->payload, clen);
+
+		tbuf += wlen + 4;
+		tlen -= wlen + 4;
+	}
+
+	if (tlen) {
+		if (net_ratelimit()) {
+			wiphy_err(ar->hw->wiphy, "%d bytes of unprocessed "
+				"data left in rx stream!\n", tlen);
+		}
+
+		goto err_telluser;
+	}
+
+	return;
+
+err_telluser:
+	wiphy_err(ar->hw->wiphy, "damaged RX stream data [want:%d, "
+		"data:%d, rx:%d, pending:%d ]\n", clen, wlen, tlen,
+		ar->rx_failover_missing);
+
+	if (ar->rx_failover_missing)
+		print_hex_dump_bytes("rxbuf:", DUMP_PREFIX_OFFSET,
+				     ar->rx_failover->data,
+				     ar->rx_failover->len);
+
+	print_hex_dump_bytes("stream:", DUMP_PREFIX_OFFSET,
+			     buf, len);
+
+	wiphy_err(ar->hw->wiphy, "please check your hardware and cables, if "
+		"you see this message frequently.\n");
+
+err_silent:
+	if (ar->rx_failover_missing) {
+		skb_reset_tail_pointer(ar->rx_failover);
+		skb_trim(ar->rx_failover, 0);
+		ar->rx_failover_missing = 0;
+	}
+}
+
+void carl9170_rx(struct ar9170 *ar, void *buf, unsigned int len)
+{
+	if (ar->fw.rx_stream)
+		carl9170_rx_stream(ar, buf, len);
+	else
+		__carl9170_rx(ar, buf, len);
+}
diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c
new file mode 100644
index 0000000..e0d2374
--- /dev/null
+++ b/drivers/net/wireless/ath/carl9170/tx.c
@@ -0,0 +1,1373 @@
+/*
+ * Atheros CARL9170 driver
+ *
+ * 802.11 xmit & status routines
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * This program is free software; you can redistribute it 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 program 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or distribute this software for any
+ *    purpose with or without fee is hereby granted, provided that the above
+ *    copyright notice and this permission notice appear in all copies.
+ *
+ *    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/etherdevice.h>
+#include <net/mac80211.h>
+#include "carl9170.h"
+#include "hw.h"
+#include "cmd.h"
+
+static inline unsigned int __carl9170_get_queue(struct ar9170 *ar,
+						unsigned int queue)
+{
+	if (unlikely(modparam_noht)) {
+		return queue;
+	} else {
+		/*
+		 * This is just another workaround, until
+		 * someone figures out how to get QoS and
+		 * AMPDU to play nicely together.
+		 */
+
+		return 2;		/* AC_BE */
+	}
+}
+
+static inline unsigned int carl9170_get_queue(struct ar9170 *ar,
+					      struct sk_buff *skb)
+{
+	return __carl9170_get_queue(ar, skb_get_queue_mapping(skb));
+}
+
+static bool is_mem_full(struct ar9170 *ar)
+{
+	return (DIV_ROUND_UP(IEEE80211_MAX_FRAME_LEN, ar->fw.mem_block_size) >
+		atomic_read(&ar->mem_free_blocks));
+}
+
+static void carl9170_tx_accounting(struct ar9170 *ar, struct sk_buff *skb)
+{
+	int queue, i;
+	bool mem_full;
+
+	atomic_inc(&ar->tx_total_queued);
+
+	queue = skb_get_queue_mapping(skb);
+	spin_lock_bh(&ar->tx_stats_lock);
+
+	/*
+	 * The driver has to accept the frame, regardless if the queue is
+	 * full to the brim, or not. We have to do the queuing internally,
+	 * since mac80211 assumes that a driver which can operate with
+	 * aggregated frames does not reject frames for this reason.
+	 */
+	ar->tx_stats[queue].len++;
+	ar->tx_stats[queue].count++;
+
+	mem_full = is_mem_full(ar);
+	for (i = 0; i < ar->hw->queues; i++) {
+		if (mem_full || ar->tx_stats[i].len >= ar->tx_stats[i].limit) {
+			ieee80211_stop_queue(ar->hw, i);
+			ar->queue_stop_timeout[i] = jiffies;
+		}
+	}
+
+	spin_unlock_bh(&ar->tx_stats_lock);
+}
+
+static void carl9170_tx_accounting_free(struct ar9170 *ar, struct sk_buff *skb)
+{
+	struct ieee80211_tx_info *txinfo;
+	int queue;
+
+	txinfo = IEEE80211_SKB_CB(skb);
+	queue = skb_get_queue_mapping(skb);
+
+	spin_lock_bh(&ar->tx_stats_lock);
+
+	ar->tx_stats[queue].len--;
+
+	if (!is_mem_full(ar)) {
+		unsigned int i;
+		for (i = 0; i < ar->hw->queues; i++) {
+			if (ar->tx_stats[i].len >= CARL9170_NUM_TX_LIMIT_SOFT)
+				continue;
+
+			if (ieee80211_queue_stopped(ar->hw, i)) {
+				unsigned long tmp;
+
+				tmp = jiffies - ar->queue_stop_timeout[i];
+				if (tmp > ar->max_queue_stop_timeout[i])
+					ar->max_queue_stop_timeout[i] = tmp;
+			}
+
+			ieee80211_wake_queue(ar->hw, i);
+		}
+	}
+
+	spin_unlock_bh(&ar->tx_stats_lock);
+	if (atomic_dec_and_test(&ar->tx_total_queued))
+		complete(&ar->tx_flush);
+}
+
+static int carl9170_alloc_dev_space(struct ar9170 *ar, struct sk_buff *skb)
+{
+	struct _carl9170_tx_superframe *super = (void *) skb->data;
+	unsigned int chunks;
+	int cookie = -1;
+
+	atomic_inc(&ar->mem_allocs);
+
+	chunks = DIV_ROUND_UP(skb->len, ar->fw.mem_block_size);
+	if (unlikely(atomic_sub_return(chunks, &ar->mem_free_blocks) < 0)) {
+		atomic_add(chunks, &ar->mem_free_blocks);
+		return -ENOSPC;
+	}
+
+	spin_lock_bh(&ar->mem_lock);
+	cookie = bitmap_find_free_region(ar->mem_bitmap, ar->fw.mem_blocks, 0);
+	spin_unlock_bh(&ar->mem_lock);
+
+	if (unlikely(cookie < 0)) {
+		atomic_add(chunks, &ar->mem_free_blocks);
+		return -ENOSPC;
+	}
+
+	super = (void *) skb->data;
+
+	/*
+	 * Cookie #0 serves two special purposes:
+	 *  1. The firmware might use it generate BlockACK frames
+	 *     in responds of an incoming BlockAckReqs.
+	 *
+	 *  2. Prevent double-free bugs.
+	 */
+	super->s.cookie = (u8) cookie + 1;
+	return 0;
+}
+
+static void carl9170_release_dev_space(struct ar9170 *ar, struct sk_buff *skb)
+{
+	struct _carl9170_tx_superframe *super = (void *) skb->data;
+	int cookie;
+
+	/* make a local copy of the cookie */
+	cookie = super->s.cookie;
+	/* invalidate cookie */
+	super->s.cookie = 0;
+
+	/*
+	 * Do a out-of-bounds check on the cookie:
+	 *
+	 *  * cookie "0" is reserved and won't be assigned to any
+	 *    out-going frame. Internally however, it is used to
+	 *    mark no longer/un-accounted frames and serves as a
+	 *    cheap way of preventing frames from being freed
+	 *    twice by _accident_. NB: There is a tiny race...
+	 *
+	 *  * obviously, cookie number is limited by the amount
+	 *    of available memory blocks, so the number can
+	 *    never execeed the mem_blocks count.
+	 */
+	if (unlikely(WARN_ON_ONCE(cookie == 0) ||
+	    WARN_ON_ONCE(cookie > ar->fw.mem_blocks)))
+		return;
+
+	atomic_add(DIV_ROUND_UP(skb->len, ar->fw.mem_block_size),
+		   &ar->mem_free_blocks);
+
+	spin_lock_bh(&ar->mem_lock);
+	bitmap_release_region(ar->mem_bitmap, cookie - 1, 0);
+	spin_unlock_bh(&ar->mem_lock);
+}
+
+/* Called from any context */
+static void carl9170_tx_release(struct kref *ref)
+{
+	struct ar9170 *ar;
+	struct carl9170_tx_info *arinfo;
+	struct ieee80211_tx_info *txinfo;
+	struct sk_buff *skb;
+
+	arinfo = container_of(ref, struct carl9170_tx_info, ref);
+	txinfo = container_of((void *) arinfo, struct ieee80211_tx_info,
+			      rate_driver_data);
+	skb = container_of((void *) txinfo, struct sk_buff, cb);
+
+	ar = arinfo->ar;
+	if (WARN_ON_ONCE(!ar))
+		return;
+
+	BUILD_BUG_ON(
+	    offsetof(struct ieee80211_tx_info, status.ampdu_ack_len) != 23);
+
+	memset(&txinfo->status.ampdu_ack_len, 0,
+	       sizeof(struct ieee80211_tx_info) -
+	       offsetof(struct ieee80211_tx_info, status.ampdu_ack_len));
+
+	if (atomic_read(&ar->tx_total_queued))
+		ar->tx_schedule = true;
+
+	if (txinfo->flags & IEEE80211_TX_CTL_AMPDU) {
+		if (!atomic_read(&ar->tx_ampdu_upload))
+			ar->tx_ampdu_schedule = true;
+
+		if (txinfo->flags & IEEE80211_TX_STAT_AMPDU) {
+			txinfo->status.ampdu_len = txinfo->pad[0];
+			txinfo->status.ampdu_ack_len = txinfo->pad[1];
+			txinfo->pad[0] = txinfo->pad[1] = 0;
+		} else if (txinfo->flags & IEEE80211_TX_STAT_ACK) {
+			/*
+			 * drop redundant tx_status reports:
+			 *
+			 * 1. ampdu_ack_len of the final tx_status does
+			 *    include the feedback of this particular frame.
+			 *
+			 * 2. tx_status_irqsafe only queues up to 128
+			 *    tx feedback reports and discards the rest.
+			 *
+			 * 3. minstrel_ht is picky, it only accepts
+			 *    reports of frames with the TX_STATUS_AMPDU flag.
+			 */
+
+			dev_kfree_skb_any(skb);
+			return;
+		} else {
+			/*
+			 * Frame has failed, but we want to keep it in
+			 * case it was lost due to a power-state
+			 * transition.
+			 */
+		}
+	}
+
+	skb_pull(skb, sizeof(struct _carl9170_tx_superframe));
+	ieee80211_tx_status_irqsafe(ar->hw, skb);
+}
+
+void carl9170_tx_get_skb(struct sk_buff *skb)
+{
+	struct carl9170_tx_info *arinfo = (void *)
+		(IEEE80211_SKB_CB(skb))->rate_driver_data;
+	kref_get(&arinfo->ref);
+}
+
+int carl9170_tx_put_skb(struct sk_buff *skb)
+{
+	struct carl9170_tx_info *arinfo = (void *)
+		(IEEE80211_SKB_CB(skb))->rate_driver_data;
+
+	return kref_put(&arinfo->ref, carl9170_tx_release);
+}
+
+/* Caller must hold the tid_info->lock & rcu_read_lock */
+static void carl9170_tx_shift_bm(struct ar9170 *ar,
+	struct carl9170_sta_tid *tid_info, u16 seq)
+{
+	u16 off;
+
+	off = SEQ_DIFF(seq, tid_info->bsn);
+
+	if (WARN_ON_ONCE(off >= CARL9170_BAW_BITS))
+		return;
+
+	/*
+	 * Sanity check. For each MPDU we set the bit in bitmap and
+	 * clear it once we received the tx_status.
+	 * But if the bit is already cleared then we've been bitten
+	 * by a bug.
+	 */
+	WARN_ON_ONCE(!test_and_clear_bit(off, tid_info->bitmap));
+
+	off = SEQ_DIFF(tid_info->snx, tid_info->bsn);
+	if (WARN_ON_ONCE(off >= CARL9170_BAW_BITS))
+		return;
+
+	if (!bitmap_empty(tid_info->bitmap, off))
+		off = find_first_bit(tid_info->bitmap, off);
+
+	tid_info->bsn += off;
+	tid_info->bsn &= 0x0fff;
+
+	bitmap_shift_right(tid_info->bitmap, tid_info->bitmap,
+			   off, CARL9170_BAW_BITS);
+}
+
+static void carl9170_tx_status_process_ampdu(struct ar9170 *ar,
+	struct sk_buff *skb, struct ieee80211_tx_info *txinfo)
+{
+	struct _carl9170_tx_superframe *super = (void *) skb->data;
+	struct ieee80211_hdr *hdr = (void *) super->frame_data;
+	struct ieee80211_tx_info *tx_info;
+	struct carl9170_tx_info *ar_info;
+	struct carl9170_sta_info *sta_info;
+	struct ieee80211_sta *sta;
+	struct carl9170_sta_tid *tid_info;
+	struct ieee80211_vif *vif;
+	unsigned int vif_id;
+	u8 tid;
+
+	if (!(txinfo->flags & IEEE80211_TX_CTL_AMPDU) ||
+	    txinfo->flags & IEEE80211_TX_CTL_INJECTED)
+		return;
+
+	tx_info = IEEE80211_SKB_CB(skb);
+	ar_info = (void *) tx_info->rate_driver_data;
+
+	vif_id = (super->s.misc & CARL9170_TX_SUPER_MISC_VIF_ID) >>
+		 CARL9170_TX_SUPER_MISC_VIF_ID_S;
+
+	if (WARN_ON_ONCE(vif_id >= AR9170_MAX_VIRTUAL_MAC))
+		return;
+
+	rcu_read_lock();
+	vif = rcu_dereference(ar->vif_priv[vif_id].vif);
+	if (unlikely(!vif))
+		goto out_rcu;
+
+	/*
+	 * Normally we should use wrappers like ieee80211_get_DA to get
+	 * the correct peer ieee80211_sta.
+	 *
+	 * But there is a problem with indirect traffic (broadcasts, or
+	 * data which is designated for other stations) in station mode.
+	 * The frame will be directed to the AP for distribution and not
+	 * to the actual destination.
+	 */
+	sta = ieee80211_find_sta(vif, hdr->addr1);
+	if (unlikely(!sta))
+		goto out_rcu;
+
+	tid = get_tid_h(hdr);
+
+	sta_info = (void *) sta->drv_priv;
+	tid_info = rcu_dereference(sta_info->agg[tid]);
+	if (!tid_info)
+		goto out_rcu;
+
+	spin_lock_bh(&tid_info->lock);
+	if (likely(tid_info->state >= CARL9170_TID_STATE_IDLE))
+		carl9170_tx_shift_bm(ar, tid_info, get_seq_h(hdr));
+
+	if (sta_info->stats[tid].clear) {
+		sta_info->stats[tid].clear = false;
+		sta_info->stats[tid].ampdu_len = 0;
+		sta_info->stats[tid].ampdu_ack_len = 0;
+	}
+
+	sta_info->stats[tid].ampdu_len++;
+	if (txinfo->status.rates[0].count == 1)
+		sta_info->stats[tid].ampdu_ack_len++;
+
+	if (super->f.mac_control & cpu_to_le16(AR9170_TX_MAC_IMM_BA)) {
+		txinfo->pad[0] = sta_info->stats[tid].ampdu_len;
+		txinfo->pad[1] = sta_info->stats[tid].ampdu_ack_len;
+		txinfo->flags |= IEEE80211_TX_STAT_AMPDU;
+		sta_info->stats[tid].clear = true;
+	}
+	spin_unlock_bh(&tid_info->lock);
+
+out_rcu:
+	rcu_read_unlock();
+}
+
+void carl9170_tx_status(struct ar9170 *ar, struct sk_buff *skb,
+			const bool success)
+{
+	struct ieee80211_tx_info *txinfo;
+
+	carl9170_tx_accounting_free(ar, skb);
+
+	txinfo = IEEE80211_SKB_CB(skb);
+
+	if (success)
+		txinfo->flags |= IEEE80211_TX_STAT_ACK;
+	else
+		ar->tx_ack_failures++;
+
+	if (txinfo->flags & IEEE80211_TX_CTL_AMPDU)
+		carl9170_tx_status_process_ampdu(ar, skb, txinfo);
+
+	carl9170_tx_put_skb(skb);
+}
+
+/* This function may be called form any context */
+void carl9170_tx_callback(struct ar9170 *ar, struct sk_buff *skb)
+{
+	struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb);
+
+	atomic_dec(&ar->tx_total_pending);
+
+	if (txinfo->flags & IEEE80211_TX_CTL_AMPDU)
+		atomic_dec(&ar->tx_ampdu_upload);
+
+	if (carl9170_tx_put_skb(skb))
+		tasklet_hi_schedule(&ar->usb_tasklet);
+}
+
+static struct sk_buff *carl9170_get_queued_skb(struct ar9170 *ar, u8 cookie,
+					       struct sk_buff_head *queue)
+{
+	struct sk_buff *skb;
+
+	spin_lock_bh(&queue->lock);
+	skb_queue_walk(queue, skb) {
+		struct _carl9170_tx_superframe *txc = (void *) skb->data;
+
+		if (txc->s.cookie != cookie)
+			continue;
+
+		__skb_unlink(skb, queue);
+		spin_unlock_bh(&queue->lock);
+
+		carl9170_release_dev_space(ar, skb);
+		return skb;
+	}
+	spin_unlock_bh(&queue->lock);
+
+	return NULL;
+}
+
+static void carl9170_tx_fill_rateinfo(struct ar9170 *ar, unsigned int rix,
+	unsigned int tries, struct ieee80211_tx_info *txinfo)
+{
+	unsigned int i;
+
+	for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+		if (txinfo->status.rates[i].idx < 0)
+			break;
+
+		if (i == rix) {
+			txinfo->status.rates[i].count = tries;
+			i++;
+			break;
+		}
+	}
+
+	for (; i < IEEE80211_TX_MAX_RATES; i++) {
+		txinfo->status.rates[i].idx = -1;
+		txinfo->status.rates[i].count = 0;
+	}
+}
+
+static void carl9170_check_queue_stop_timeout(struct ar9170 *ar)
+{
+	int i;
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *txinfo;
+	struct carl9170_tx_info *arinfo;
+	bool restart = false;
+
+	for (i = 0; i < ar->hw->queues; i++) {
+		spin_lock_bh(&ar->tx_status[i].lock);
+
+		skb = skb_peek(&ar->tx_status[i]);
+
+		if (!skb)
+			goto next;
+
+		txinfo = IEEE80211_SKB_CB(skb);
+		arinfo = (void *) txinfo->rate_driver_data;
+
+		if (time_is_before_jiffies(arinfo->timeout +
+		    msecs_to_jiffies(CARL9170_QUEUE_STUCK_TIMEOUT)) == true)
+			restart = true;
+
+next:
+		spin_unlock_bh(&ar->tx_status[i].lock);
+	}
+
+	if (restart) {
+		/*
+		 * At least one queue has been stuck for long enough.
+		 * Give the device a kick and hope it gets back to
+		 * work.
+		 *
+		 * possible reasons may include:
+		 *  - frames got lost/corrupted (bad connection to the device)
+		 *  - stalled rx processing/usb controller hiccups
+		 *  - firmware errors/bugs
+		 *  - every bug you can think of.
+		 *  - all bugs you can't...
+		 *  - ...
+		 */
+		carl9170_restart(ar, CARL9170_RR_STUCK_TX);
+	}
+}
+
+void carl9170_tx_janitor(struct work_struct *work)
+{
+	struct ar9170 *ar = container_of(work, struct ar9170,
+					 tx_janitor.work);
+	if (!IS_STARTED(ar))
+		return;
+
+	ar->tx_janitor_last_run = jiffies;
+
+	carl9170_check_queue_stop_timeout(ar);
+
+	if (!atomic_read(&ar->tx_total_queued))
+		return;
+
+	ieee80211_queue_delayed_work(ar->hw, &ar->tx_janitor,
+		msecs_to_jiffies(CARL9170_TX_TIMEOUT));
+}
+
+static void __carl9170_tx_process_status(struct ar9170 *ar,
+	const uint8_t cookie, const uint8_t info)
+{
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *txinfo;
+	struct carl9170_tx_info *arinfo;
+	unsigned int r, t, q;
+	bool success = true;
+
+	q = ar9170_qmap[info & CARL9170_TX_STATUS_QUEUE];
+
+	skb = carl9170_get_queued_skb(ar, cookie, &ar->tx_status[q]);
+	if (!skb) {
+		/*
+		 * We have lost the race to another thread.
+		 */
+
+		return ;
+	}
+
+	txinfo = IEEE80211_SKB_CB(skb);
+	arinfo = (void *) txinfo->rate_driver_data;
+
+	if (!(info & CARL9170_TX_STATUS_SUCCESS))
+		success = false;
+
+	r = (info & CARL9170_TX_STATUS_RIX) >> CARL9170_TX_STATUS_RIX_S;
+	t = (info & CARL9170_TX_STATUS_TRIES) >> CARL9170_TX_STATUS_TRIES_S;
+
+	carl9170_tx_fill_rateinfo(ar, r, t, txinfo);
+	carl9170_tx_status(ar, skb, success);
+}
+
+void carl9170_tx_process_status(struct ar9170 *ar,
+				const struct carl9170_rsp *cmd)
+{
+	unsigned int i;
+
+	for (i = 0;  i < cmd->hdr.ext; i++) {
+		if (WARN_ON(i > ((cmd->hdr.len / 2) + 1))) {
+			print_hex_dump_bytes("UU:", DUMP_PREFIX_NONE,
+					     (void *) cmd, cmd->hdr.len + 4);
+			break;
+		}
+
+		__carl9170_tx_process_status(ar, cmd->_tx_status[i].cookie,
+					     cmd->_tx_status[i].info);
+	}
+}
+
+static __le32 carl9170_tx_physet(struct ar9170 *ar,
+	struct ieee80211_tx_info *info, struct ieee80211_tx_rate *txrate)
+{
+	struct ieee80211_rate *rate = NULL;
+	u32 power, chains;
+	__le32 tmp;
+
+	tmp = cpu_to_le32(0);
+
+	if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+		tmp |= cpu_to_le32(AR9170_TX_PHY_BW_40MHZ <<
+			AR9170_TX_PHY_BW_S);
+	/* this works because 40 MHz is 2 and dup is 3 */
+	if (txrate->flags & IEEE80211_TX_RC_DUP_DATA)
+		tmp |= cpu_to_le32(AR9170_TX_PHY_BW_40MHZ_DUP <<
+			AR9170_TX_PHY_BW_S);
+
+	if (txrate->flags & IEEE80211_TX_RC_SHORT_GI)
+		tmp |= cpu_to_le32(AR9170_TX_PHY_SHORT_GI);
+
+	if (txrate->flags & IEEE80211_TX_RC_MCS) {
+		u32 r = txrate->idx;
+		u8 *txpower;
+
+		/* heavy clip control */
+		tmp |= cpu_to_le32((r & 0x7) <<
+			AR9170_TX_PHY_TX_HEAVY_CLIP_S);
+
+		if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) {
+			if (info->band == IEEE80211_BAND_5GHZ)
+				txpower = ar->power_5G_ht40;
+			else
+				txpower = ar->power_2G_ht40;
+		} else {
+			if (info->band == IEEE80211_BAND_5GHZ)
+				txpower = ar->power_5G_ht20;
+			else
+				txpower = ar->power_2G_ht20;
+		}
+
+		power = txpower[r & 7];
+
+		/* +1 dBm for HT40 */
+		if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+			power += 2;
+
+		r <<= AR9170_TX_PHY_MCS_S;
+		BUG_ON(r & ~AR9170_TX_PHY_MCS);
+
+		tmp |= cpu_to_le32(r & AR9170_TX_PHY_MCS);
+		tmp |= cpu_to_le32(AR9170_TX_PHY_MOD_HT);
+
+		/*
+		 * green field preamble does not work.
+		 *
+		 * if (txrate->flags & IEEE80211_TX_RC_GREEN_FIELD)
+		 * tmp |= cpu_to_le32(AR9170_TX_PHY_GREENFIELD);
+		 */
+	} else {
+		u8 *txpower;
+		u32 mod;
+		u32 phyrate;
+		u8 idx = txrate->idx;
+
+		if (info->band != IEEE80211_BAND_2GHZ) {
+			idx += 4;
+			txpower = ar->power_5G_leg;
+			mod = AR9170_TX_PHY_MOD_OFDM;
+		} else {
+			if (idx < 4) {
+				txpower = ar->power_2G_cck;
+				mod = AR9170_TX_PHY_MOD_CCK;
+			} else {
+				mod = AR9170_TX_PHY_MOD_OFDM;
+				txpower = ar->power_2G_ofdm;
+			}
+		}
+
+		rate = &__carl9170_ratetable[idx];
+
+		phyrate = rate->hw_value & 0xF;
+		power = txpower[(rate->hw_value & 0x30) >> 4];
+		phyrate <<= AR9170_TX_PHY_MCS_S;
+
+		tmp |= cpu_to_le32(mod);
+		tmp |= cpu_to_le32(phyrate);
+
+		/*
+		 * short preamble seems to be broken too.
+		 *
+		 * if (txrate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)
+		 *	tmp |= cpu_to_le32(AR9170_TX_PHY_SHORT_PREAMBLE);
+		 */
+	}
+	power <<= AR9170_TX_PHY_TX_PWR_S;
+	power &= AR9170_TX_PHY_TX_PWR;
+	tmp |= cpu_to_le32(power);
+
+	/* set TX chains */
+	if (ar->eeprom.tx_mask == 1) {
+		chains = AR9170_TX_PHY_TXCHAIN_1;
+	} else {
+		chains = AR9170_TX_PHY_TXCHAIN_2;
+
+		/* >= 36M legacy OFDM - use only one chain */
+		if (rate && rate->bitrate >= 360 &&
+		    !(txrate->flags & IEEE80211_TX_RC_MCS))
+			chains = AR9170_TX_PHY_TXCHAIN_1;
+	}
+	tmp |= cpu_to_le32(chains << AR9170_TX_PHY_TXCHAIN_S);
+
+	return tmp;
+}
+
+static bool carl9170_tx_rts_check(struct ar9170 *ar,
+				  struct ieee80211_tx_rate *rate,
+				  bool ampdu, bool multi)
+{
+	switch (ar->erp_mode) {
+	case CARL9170_ERP_AUTO:
+		if (ampdu)
+			break;
+
+	case CARL9170_ERP_MAC80211:
+		if (!(rate->flags & IEEE80211_TX_RC_USE_RTS_CTS))
+			break;
+
+	case CARL9170_ERP_RTS:
+		if (likely(!multi))
+			return true;
+
+	default:
+		break;
+	}
+
+	return false;
+}
+
+static bool carl9170_tx_cts_check(struct ar9170 *ar,
+				  struct ieee80211_tx_rate *rate)
+{
+	switch (ar->erp_mode) {
+	case CARL9170_ERP_AUTO:
+	case CARL9170_ERP_MAC80211:
+		if (!(rate->flags & IEEE80211_TX_RC_USE_CTS_PROTECT))
+			break;
+
+	case CARL9170_ERP_CTS:
+		return true;
+
+	default:
+		break;
+	}
+
+	return false;
+}
+
+static int carl9170_tx_prepare(struct ar9170 *ar, struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr;
+	struct _carl9170_tx_superframe *txc;
+	struct carl9170_vif_info *cvif;
+	struct ieee80211_tx_info *info;
+	struct ieee80211_tx_rate *txrate;
+	struct ieee80211_sta *sta;
+	struct carl9170_tx_info *arinfo;
+	unsigned int hw_queue;
+	int i;
+	u16 keytype = 0;
+	u16 len, icv = 0;
+	bool ampdu, no_ack;
+
+	BUILD_BUG_ON(sizeof(*arinfo) > sizeof(info->rate_driver_data));
+	BUILD_BUG_ON(sizeof(struct _carl9170_tx_superdesc) !=
+		     CARL9170_TX_SUPERDESC_LEN);
+
+	BUILD_BUG_ON(sizeof(struct _ar9170_tx_hwdesc) !=
+		     AR9170_TX_HWDESC_LEN);
+
+	BUILD_BUG_ON(IEEE80211_TX_MAX_RATES < CARL9170_TX_MAX_RATES);
+
+	hw_queue = ar9170_qmap[carl9170_get_queue(ar, skb)];
+
+	hdr = (void *)skb->data;
+	info = IEEE80211_SKB_CB(skb);
+	len = skb->len;
+
+	/*
+	 * Note: If the frame was sent through a monitor interface,
+	 * the ieee80211_vif pointer can be NULL.
+	 */
+	if (likely(info->control.vif))
+		cvif = (void *) info->control.vif->drv_priv;
+	else
+		cvif = NULL;
+
+	sta = info->control.sta;
+
+	txc = (void *)skb_push(skb, sizeof(*txc));
+	memset(txc, 0, sizeof(*txc));
+
+	ampdu = !!(info->flags & IEEE80211_TX_CTL_AMPDU);
+	no_ack = !!(info->flags & IEEE80211_TX_CTL_NO_ACK);
+
+	if (info->control.hw_key) {
+		icv = info->control.hw_key->icv_len;
+
+		switch (info->control.hw_key->cipher) {
+		case WLAN_CIPHER_SUITE_WEP40:
+		case WLAN_CIPHER_SUITE_WEP104:
+		case WLAN_CIPHER_SUITE_TKIP:
+			keytype = AR9170_TX_MAC_ENCR_RC4;
+			break;
+		case WLAN_CIPHER_SUITE_CCMP:
+			keytype = AR9170_TX_MAC_ENCR_AES;
+			break;
+		default:
+			WARN_ON(1);
+			goto err_out;
+		}
+	}
+
+	BUILD_BUG_ON(AR9170_MAX_VIRTUAL_MAC >
+		((CARL9170_TX_SUPER_MISC_VIF_ID >>
+		 CARL9170_TX_SUPER_MISC_VIF_ID_S) + 1));
+
+	txc->s.len = cpu_to_le16(len + sizeof(*txc));
+	txc->f.length = cpu_to_le16(len + icv + 4);
+	SET_VAL(CARL9170_TX_SUPER_MISC_VIF_ID, txc->s.misc,
+		cvif ? cvif->id : 0);
+
+	txc->f.mac_control = cpu_to_le16(AR9170_TX_MAC_HW_DURATION |
+					 AR9170_TX_MAC_BACKOFF);
+
+	SET_VAL(CARL9170_TX_SUPER_MISC_QUEUE, txc->s.misc, hw_queue);
+
+	txc->f.mac_control |= cpu_to_le16(hw_queue << AR9170_TX_MAC_QOS_S);
+	txc->f.mac_control |= cpu_to_le16(keytype);
+	txc->f.phy_control = cpu_to_le32(0);
+
+	if (no_ack)
+		txc->f.mac_control |= cpu_to_le16(AR9170_TX_MAC_NO_ACK);
+
+	if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM)
+		txc->s.misc |= CARL9170_TX_SUPER_MISC_CAB;
+
+	txrate = &info->control.rates[0];
+	if (carl9170_tx_rts_check(ar, txrate, ampdu, no_ack))
+		txc->f.mac_control |= cpu_to_le16(AR9170_TX_MAC_PROT_RTS);
+	else if (carl9170_tx_cts_check(ar, txrate))
+		txc->f.mac_control |= cpu_to_le16(AR9170_TX_MAC_PROT_CTS);
+
+	SET_VAL(CARL9170_TX_SUPER_RI_TRIES, txc->s.ri[0], txrate->count);
+	txc->f.phy_control |= carl9170_tx_physet(ar, info, txrate);
+
+	if (info->flags & IEEE80211_TX_CTL_AMPDU) {
+		for (i = 1; i < CARL9170_TX_MAX_RATES; i++) {
+			txrate = &info->control.rates[i];
+			if (txrate->idx >= 0)
+				continue;
+
+			txrate->idx = 0;
+			txrate->count = ar->hw->max_rate_tries;
+		}
+	}
+
+	/*
+	 * NOTE: For the first rate, the ERP & AMPDU flags are directly
+	 * taken from mac_control. For all fallback rate, the firmware
+	 * updates the mac_control flags from the rate info field.
+	 */
+	for (i = 1; i < CARL9170_TX_MAX_RATES; i++) {
+		txrate = &info->control.rates[i];
+		if (txrate->idx < 0)
+			break;
+
+		SET_VAL(CARL9170_TX_SUPER_RI_TRIES, txc->s.ri[i],
+			txrate->count);
+
+		if (carl9170_tx_rts_check(ar, txrate, ampdu, no_ack))
+			txc->s.ri[i] |= (AR9170_TX_MAC_PROT_RTS <<
+				CARL9170_TX_SUPER_RI_ERP_PROT_S);
+		else if (carl9170_tx_cts_check(ar, txrate))
+			txc->s.ri[i] |= (AR9170_TX_MAC_PROT_CTS <<
+				CARL9170_TX_SUPER_RI_ERP_PROT_S);
+
+		/*
+		 * unaggregated fallback, in case aggregation
+		 * proves to be unsuccessful and unreliable.
+		 */
+		if (ampdu && i < 3)
+			txc->s.ri[i] |= CARL9170_TX_SUPER_RI_AMPDU;
+
+		txc->s.rr[i - 1] = carl9170_tx_physet(ar, info, txrate);
+	}
+
+	if (ieee80211_is_probe_resp(hdr->frame_control))
+		txc->s.misc |= CARL9170_TX_SUPER_MISC_FILL_IN_TSF;
+
+	if (ampdu) {
+		unsigned int density, factor;
+
+		if (unlikely(!sta || !cvif))
+			goto err_out;
+
+		density = info->control.sta->ht_cap.ampdu_density;
+		factor = info->control.sta->ht_cap.ampdu_factor;
+
+		if (density) {
+			/*
+			 * Watch out!
+			 *
+			 * Otus uses slightly different density values than
+			 * those from the 802.11n spec.
+			 */
+
+			density = max_t(unsigned int, density + 1, 7u);
+		}
+
+		factor = min_t(unsigned int, 1u, factor);
+
+		SET_VAL(CARL9170_TX_SUPER_AMPDU_DENSITY,
+			txc->s.ampdu_settings, density);
+
+		SET_VAL(CARL9170_TX_SUPER_AMPDU_FACTOR,
+			txc->s.ampdu_settings, factor);
+
+		if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS) {
+			txc->f.mac_control |= cpu_to_le16(AR9170_TX_MAC_AGGR);
+		} else {
+			/*
+			 * Not sure if it's even possible to aggregate
+			 * non-ht rates with this HW.
+			 */
+			WARN_ON_ONCE(1);
+		}
+	}
+
+	arinfo = (void *)info->rate_driver_data;
+	arinfo->timeout = jiffies;
+	arinfo->ar = ar;
+	kref_init(&arinfo->ref);
+	return 0;
+
+err_out:
+	skb_pull(skb, sizeof(*txc));
+	return -EINVAL;
+}
+
+static void carl9170_set_immba(struct ar9170 *ar, struct sk_buff *skb)
+{
+	struct _carl9170_tx_superframe *super;
+
+	super = (void *) skb->data;
+	super->f.mac_control |= cpu_to_le16(AR9170_TX_MAC_IMM_BA);
+}
+
+static void carl9170_set_ampdu_params(struct ar9170 *ar, struct sk_buff *skb)
+{
+	struct _carl9170_tx_superframe *super;
+	int tmp;
+
+	super = (void *) skb->data;
+
+	tmp = (super->s.ampdu_settings & CARL9170_TX_SUPER_AMPDU_DENSITY) <<
+		CARL9170_TX_SUPER_AMPDU_DENSITY_S;
+
+	/*
+	 * If you haven't noticed carl9170_tx_prepare has already filled
+	 * in all ampdu spacing & factor parameters.
+	 * Now it's the time to check whenever the settings have to be
+	 * updated by the firmware, or if everything is still the same.
+	 *
+	 * There's no sane way to handle different density values with
+	 * this hardware, so we may as well just do the compare in the
+	 * driver.
+	 */
+
+	if (tmp != ar->current_density) {
+		ar->current_density = tmp;
+		super->s.ampdu_settings |=
+			CARL9170_TX_SUPER_AMPDU_COMMIT_DENSITY;
+	}
+
+	tmp = (super->s.ampdu_settings & CARL9170_TX_SUPER_AMPDU_FACTOR) <<
+		CARL9170_TX_SUPER_AMPDU_FACTOR_S;
+
+	if (tmp != ar->current_factor) {
+		ar->current_factor = tmp;
+		super->s.ampdu_settings |=
+			CARL9170_TX_SUPER_AMPDU_COMMIT_FACTOR;
+	}
+}
+
+static bool carl9170_tx_rate_check(struct ar9170 *ar, struct sk_buff *_dest,
+				   struct sk_buff *_src)
+{
+	struct _carl9170_tx_superframe *dest, *src;
+
+	dest = (void *) _dest->data;
+	src = (void *) _src->data;
+
+	/*
+	 * The mac80211 rate control algorithm expects that all MPDUs in
+	 * an AMPDU share the same tx vectors.
+	 * This is not really obvious right now, because the hardware
+	 * does the AMPDU setup according to its own rulebook.
+	 * Our nicely assembled, strictly monotonic increasing mpdu
+	 * chains will be broken up, mashed back together...
+	 */
+
+	return (dest->f.phy_control == src->f.phy_control);
+}
+
+static void carl9170_tx_ampdu(struct ar9170 *ar)
+{
+	struct sk_buff_head agg;
+	struct carl9170_sta_tid *tid_info;
+	struct sk_buff *skb, *first;
+	unsigned int i = 0, done_ampdus = 0;
+	u16 seq, queue, tmpssn;
+
+	atomic_inc(&ar->tx_ampdu_scheduler);
+	ar->tx_ampdu_schedule = false;
+
+	if (atomic_read(&ar->tx_ampdu_upload))
+		return;
+
+	if (!ar->tx_ampdu_list_len)
+		return;
+
+	__skb_queue_head_init(&agg);
+
+	rcu_read_lock();
+	tid_info = rcu_dereference(ar->tx_ampdu_iter);
+	if (WARN_ON_ONCE(!tid_info)) {
+		rcu_read_unlock();
+		return;
+	}
+
+retry:
+	list_for_each_entry_continue_rcu(tid_info, &ar->tx_ampdu_list, list) {
+		i++;
+
+		if (tid_info->state < CARL9170_TID_STATE_PROGRESS)
+			continue;
+
+		queue = TID_TO_WME_AC(tid_info->tid);
+
+		spin_lock_bh(&tid_info->lock);
+		if (tid_info->state != CARL9170_TID_STATE_XMIT) {
+			first = skb_peek(&tid_info->queue);
+			if (first) {
+				struct ieee80211_tx_info *txinfo;
+				struct carl9170_tx_info *arinfo;
+
+				txinfo = IEEE80211_SKB_CB(first);
+				arinfo = (void *) txinfo->rate_driver_data;
+
+				if (time_is_after_jiffies(arinfo->timeout +
+				    msecs_to_jiffies(CARL9170_QUEUE_TIMEOUT))
+				    == true)
+					goto processed;
+
+				/*
+				 * We've been waiting for the frame which
+				 * matches "snx" (start sequence of the
+				 * next aggregate) for some time now.
+				 *
+				 * But it never arrived. Therefore
+				 * jump to the next available frame
+				 * and kick-start the transmission.
+				 *
+				 * Note: This might induce odd latency
+				 * spikes because the receiver will be
+				 * waiting for the lost frame too.
+				 */
+				ar->tx_ampdu_timeout++;
+
+				tid_info->snx = carl9170_get_seq(first);
+				tid_info->state = CARL9170_TID_STATE_XMIT;
+			} else {
+				goto processed;
+			}
+		}
+
+		tid_info->counter++;
+		first = skb_peek(&tid_info->queue);
+		tmpssn = carl9170_get_seq(first);
+		seq = tid_info->snx;
+
+		if (unlikely(tmpssn != seq)) {
+			tid_info->state = CARL9170_TID_STATE_IDLE;
+
+			goto processed;
+		}
+
+		while ((skb = skb_peek(&tid_info->queue))) {
+			/* strict 0, 1, ..., n - 1, n frame sequence order */
+			if (unlikely(carl9170_get_seq(skb) != seq))
+				break;
+
+			/* don't upload more than AMPDU FACTOR allows. */
+			if (unlikely(SEQ_DIFF(tid_info->snx, tid_info->bsn) >=
+			    (tid_info->max - 1)))
+				break;
+
+			if (!carl9170_tx_rate_check(ar, skb, first))
+				break;
+
+			atomic_inc(&ar->tx_ampdu_upload);
+			tid_info->snx = seq = SEQ_NEXT(seq);
+			__skb_unlink(skb, &tid_info->queue);
+
+			__skb_queue_tail(&agg, skb);
+
+			if (skb_queue_len(&agg) >= CARL9170_NUM_TX_AGG_MAX)
+				break;
+		}
+
+		if (skb_queue_empty(&tid_info->queue) ||
+		    carl9170_get_seq(skb_peek(&tid_info->queue)) !=
+		    tid_info->snx) {
+			/*
+			 * stop TID, if A-MPDU frames are still missing,
+			 * or whenever the queue is empty.
+			 */
+
+			tid_info->state = CARL9170_TID_STATE_IDLE;
+		}
+		done_ampdus++;
+
+processed:
+		spin_unlock_bh(&tid_info->lock);
+
+		if (skb_queue_empty(&agg))
+			continue;
+
+		/* apply ampdu spacing & factor settings */
+		carl9170_set_ampdu_params(ar, skb_peek(&agg));
+
+		/* set aggregation push bit */
+		carl9170_set_immba(ar, skb_peek_tail(&agg));
+
+		spin_lock_bh(&ar->tx_pending[queue].lock);
+		skb_queue_splice_tail_init(&agg, &ar->tx_pending[queue]);
+		spin_unlock_bh(&ar->tx_pending[queue].lock);
+		ar->tx_schedule = true;
+	}
+	if ((done_ampdus++ == 0) && (i++ == 0))
+		goto retry;
+
+	rcu_assign_pointer(ar->tx_ampdu_iter, tid_info);
+	rcu_read_unlock();
+}
+
+static struct sk_buff *carl9170_tx_pick_skb(struct ar9170 *ar,
+					    struct sk_buff_head *queue)
+{
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *info;
+	struct carl9170_tx_info *arinfo;
+
+	BUILD_BUG_ON(sizeof(*arinfo) > sizeof(info->rate_driver_data));
+
+	spin_lock_bh(&queue->lock);
+	skb = skb_peek(queue);
+	if (unlikely(!skb))
+		goto err_unlock;
+
+	if (carl9170_alloc_dev_space(ar, skb))
+		goto err_unlock;
+
+	__skb_unlink(skb, queue);
+	spin_unlock_bh(&queue->lock);
+
+	info = IEEE80211_SKB_CB(skb);
+	arinfo = (void *) info->rate_driver_data;
+
+	arinfo->timeout = jiffies;
+
+	/*
+	 * increase ref count to "2".
+	 * Ref counting is the easiest way to solve the race between
+	 * the the urb's completion routine: carl9170_tx_callback and
+	 * wlan tx status functions: carl9170_tx_status/janitor.
+	 */
+	carl9170_tx_get_skb(skb);
+
+	return skb;
+
+err_unlock:
+	spin_unlock_bh(&queue->lock);
+	return NULL;
+}
+
+void carl9170_tx_drop(struct ar9170 *ar, struct sk_buff *skb)
+{
+	struct _carl9170_tx_superframe *super;
+	uint8_t q = 0;
+
+	ar->tx_dropped++;
+
+	super = (void *)skb->data;
+	SET_VAL(CARL9170_TX_SUPER_MISC_QUEUE, q,
+		ar9170_qmap[carl9170_get_queue(ar, skb)]);
+	__carl9170_tx_process_status(ar, super->s.cookie, q);
+}
+
+static void carl9170_tx(struct ar9170 *ar)
+{
+	struct sk_buff *skb;
+	unsigned int i, q;
+	bool schedule_garbagecollector = false;
+
+	ar->tx_schedule = false;
+
+	if (unlikely(!IS_STARTED(ar)))
+		return;
+
+	carl9170_usb_handle_tx_err(ar);
+
+	for (i = 0; i < ar->hw->queues; i++) {
+		while (!skb_queue_empty(&ar->tx_pending[i])) {
+			skb = carl9170_tx_pick_skb(ar, &ar->tx_pending[i]);
+			if (unlikely(!skb))
+				break;
+
+			atomic_inc(&ar->tx_total_pending);
+
+			q = __carl9170_get_queue(ar, i);
+			/*
+			 * NB: tx_status[i] vs. tx_status[q],
+			 * TODO: Move into pick_skb or alloc_dev_space.
+			 */
+			skb_queue_tail(&ar->tx_status[q], skb);
+
+			carl9170_usb_tx(ar, skb);
+			schedule_garbagecollector = true;
+		}
+	}
+
+	if (!schedule_garbagecollector)
+		return;
+
+	ieee80211_queue_delayed_work(ar->hw, &ar->tx_janitor,
+		msecs_to_jiffies(CARL9170_TX_TIMEOUT));
+}
+
+static bool carl9170_tx_ampdu_queue(struct ar9170 *ar,
+	struct ieee80211_sta *sta, struct sk_buff *skb)
+{
+	struct carl9170_sta_info *sta_info;
+	struct carl9170_sta_tid *agg;
+	struct sk_buff *iter;
+	unsigned int max;
+	u16 tid, seq, qseq, off;
+	bool run = false;
+
+	tid = carl9170_get_tid(skb);
+	seq = carl9170_get_seq(skb);
+	sta_info = (void *) sta->drv_priv;
+
+	rcu_read_lock();
+	agg = rcu_dereference(sta_info->agg[tid]);
+	max = sta_info->ampdu_max_len;
+
+	if (!agg)
+		goto err_unlock_rcu;
+
+	spin_lock_bh(&agg->lock);
+	if (unlikely(agg->state < CARL9170_TID_STATE_IDLE))
+		goto err_unlock;
+
+	/* check if sequence is within the BA window */
+	if (unlikely(!BAW_WITHIN(agg->bsn, CARL9170_BAW_BITS, seq)))
+		goto err_unlock;
+
+	if (WARN_ON_ONCE(!BAW_WITHIN(agg->snx, CARL9170_BAW_BITS, seq)))
+		goto err_unlock;
+
+	off = SEQ_DIFF(seq, agg->bsn);
+	if (WARN_ON_ONCE(test_and_set_bit(off, agg->bitmap)))
+		goto err_unlock;
+
+	if (likely(BAW_WITHIN(agg->hsn, CARL9170_BAW_BITS, seq))) {
+		__skb_queue_tail(&agg->queue, skb);
+		agg->hsn = seq;
+		goto queued;
+	}
+
+	skb_queue_reverse_walk(&agg->queue, iter) {
+		qseq = carl9170_get_seq(iter);
+
+		if (BAW_WITHIN(qseq, CARL9170_BAW_BITS, seq)) {
+			__skb_queue_after(&agg->queue, iter, skb);
+			goto queued;
+		}
+	}
+
+	__skb_queue_head(&agg->queue, skb);
+queued:
+
+	if (unlikely(agg->state != CARL9170_TID_STATE_XMIT)) {
+		if (agg->snx == carl9170_get_seq(skb_peek(&agg->queue))) {
+			agg->state = CARL9170_TID_STATE_XMIT;
+			run = true;
+		}
+	}
+
+	spin_unlock_bh(&agg->lock);
+	rcu_read_unlock();
+
+	return run;
+
+err_unlock:
+	spin_unlock_bh(&agg->lock);
+
+err_unlock_rcu:
+	rcu_read_unlock();
+	carl9170_tx_status(ar, skb, false);
+	ar->tx_dropped++;
+	return false;
+}
+
+int carl9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+	struct ar9170 *ar = hw->priv;
+	struct ieee80211_tx_info *info;
+	struct ieee80211_sta *sta;
+	bool run;
+
+	if (unlikely(!IS_STARTED(ar)))
+		goto err_free;
+
+	info = IEEE80211_SKB_CB(skb);
+	sta = info->control.sta;
+
+	if (unlikely(carl9170_tx_prepare(ar, skb)))
+		goto err_free;
+
+	carl9170_tx_accounting(ar, skb);
+	/*
+	 * from now on, one has to use carl9170_tx_status to free
+	 * all ressouces which are associated with the frame.
+	 */
+
+	if (info->flags & IEEE80211_TX_CTL_AMPDU) {
+		if (WARN_ON_ONCE(!sta))
+			goto err_free;
+
+		run = carl9170_tx_ampdu_queue(ar, sta, skb);
+		if (run)
+			carl9170_tx_ampdu(ar);
+
+	} else {
+		unsigned int queue = skb_get_queue_mapping(skb);
+
+		skb_queue_tail(&ar->tx_pending[queue], skb);
+	}
+
+	carl9170_tx(ar);
+	return NETDEV_TX_OK;
+
+err_free:
+	ar->tx_dropped++;
+	dev_kfree_skb_any(skb);
+	return NETDEV_TX_OK;
+}
+
+void carl9170_tx_scheduler(struct ar9170 *ar)
+{
+
+	if (ar->tx_ampdu_schedule)
+		carl9170_tx_ampdu(ar);
+
+	if (ar->tx_schedule)
+		carl9170_tx(ar);
+}
diff --git a/drivers/net/wireless/ath/carl9170/usb.c b/drivers/net/wireless/ath/carl9170/usb.c
new file mode 100644
index 0000000..eb789a9
--- /dev/null
+++ b/drivers/net/wireless/ath/carl9170/usb.c
@@ -0,0 +1,1138 @@
+/*
+ * Atheros CARL9170 driver
+ *
+ * USB - frontend
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * This program is free software; you can redistribute it 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 program 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or distribute this software for any
+ *    purpose with or without fee is hereby granted, provided that the above
+ *    copyright notice and this permission notice appear in all copies.
+ *
+ *    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/firmware.h>
+#include <linux/etherdevice.h>
+#include <linux/device.h>
+#include <net/mac80211.h>
+#include "carl9170.h"
+#include "cmd.h"
+#include "hw.h"
+#include "fwcmd.h"
+
+MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
+MODULE_AUTHOR("Christian Lamparter <chunkeey@googlemail.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Atheros AR9170 802.11n USB wireless");
+MODULE_FIRMWARE(CARL9170FW_NAME);
+MODULE_ALIAS("ar9170usb");
+MODULE_ALIAS("arusb_lnx");
+
+/*
+ * Note:
+ *
+ * Always update our wiki's device list (located at:
+ * http://wireless.kernel.org/en/users/Drivers/ar9170/devices ),
+ * whenever you add a new device.
+ */
+static struct usb_device_id carl9170_usb_ids[] = {
+	/* Atheros 9170 */
+	{ USB_DEVICE(0x0cf3, 0x9170) },
+	/* Atheros TG121N */
+	{ USB_DEVICE(0x0cf3, 0x1001) },
+	/* TP-Link TL-WN821N v2 */
+	{ USB_DEVICE(0x0cf3, 0x1002), .driver_info = CARL9170_WPS_BUTTON |
+		 CARL9170_ONE_LED },
+	/* 3Com Dual Band 802.11n USB Adapter */
+	{ USB_DEVICE(0x0cf3, 0x1010) },
+	/* H3C Dual Band 802.11n USB Adapter */
+	{ USB_DEVICE(0x0cf3, 0x1011) },
+	/* Cace Airpcap NX */
+	{ USB_DEVICE(0xcace, 0x0300) },
+	/* D-Link DWA 160 A1 */
+	{ USB_DEVICE(0x07d1, 0x3c10) },
+	/* D-Link DWA 160 A2 */
+	{ USB_DEVICE(0x07d1, 0x3a09) },
+	/* Netgear WNA1000 */
+	{ USB_DEVICE(0x0846, 0x9040) },
+	/* Netgear WNDA3100 */
+	{ USB_DEVICE(0x0846, 0x9010) },
+	/* Netgear WN111 v2 */
+	{ USB_DEVICE(0x0846, 0x9001), .driver_info = CARL9170_ONE_LED },
+	/* Zydas ZD1221 */
+	{ USB_DEVICE(0x0ace, 0x1221) },
+	/* Proxim ORiNOCO 802.11n USB */
+	{ USB_DEVICE(0x1435, 0x0804) },
+	/* WNC Generic 11n USB Dongle */
+	{ USB_DEVICE(0x1435, 0x0326) },
+	/* ZyXEL NWD271N */
+	{ USB_DEVICE(0x0586, 0x3417) },
+	/* Z-Com UB81 BG */
+	{ USB_DEVICE(0x0cde, 0x0023) },
+	/* Z-Com UB82 ABG */
+	{ USB_DEVICE(0x0cde, 0x0026) },
+	/* Sphairon Homelink 1202 */
+	{ USB_DEVICE(0x0cde, 0x0027) },
+	/* Arcadyan WN7512 */
+	{ USB_DEVICE(0x083a, 0xf522) },
+	/* Planex GWUS300 */
+	{ USB_DEVICE(0x2019, 0x5304) },
+	/* IO-Data WNGDNUS2 */
+	{ USB_DEVICE(0x04bb, 0x093f) },
+	/* NEC WL300NU-G */
+	{ USB_DEVICE(0x0409, 0x0249) },
+	/* AVM FRITZ!WLAN USB Stick N */
+	{ USB_DEVICE(0x057c, 0x8401) },
+	/* AVM FRITZ!WLAN USB Stick N 2.4 */
+	{ USB_DEVICE(0x057c, 0x8402) },
+	/* Qwest/Actiontec 802AIN Wireless N USB Network Adapter */
+	{ USB_DEVICE(0x1668, 0x1200) },
+
+	/* terminate */
+	{}
+};
+MODULE_DEVICE_TABLE(usb, carl9170_usb_ids);
+
+static void carl9170_usb_submit_data_urb(struct ar9170 *ar)
+{
+	struct urb *urb;
+	int err;
+
+	if (atomic_inc_return(&ar->tx_anch_urbs) > AR9170_NUM_TX_URBS)
+		goto err_acc;
+
+	urb = usb_get_from_anchor(&ar->tx_wait);
+	if (!urb)
+		goto err_acc;
+
+	usb_anchor_urb(urb, &ar->tx_anch);
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (unlikely(err)) {
+		if (net_ratelimit()) {
+			dev_err(&ar->udev->dev, "tx submit failed (%d)\n",
+				urb->status);
+		}
+
+		usb_unanchor_urb(urb);
+		usb_anchor_urb(urb, &ar->tx_err);
+	}
+
+	usb_free_urb(urb);
+
+	if (likely(err == 0))
+		return;
+
+err_acc:
+	atomic_dec(&ar->tx_anch_urbs);
+}
+
+static void carl9170_usb_tx_data_complete(struct urb *urb)
+{
+	struct ar9170 *ar = (struct ar9170 *)
+	      usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
+
+	if (WARN_ON_ONCE(!ar)) {
+		dev_kfree_skb_irq(urb->context);
+		return;
+	}
+
+	atomic_dec(&ar->tx_anch_urbs);
+
+	switch (urb->status) {
+	/* everything is fine */
+	case 0:
+		carl9170_tx_callback(ar, (void *)urb->context);
+		break;
+
+	/* disconnect */
+	case -ENOENT:
+	case -ECONNRESET:
+	case -ENODEV:
+	case -ESHUTDOWN:
+		/*
+		 * Defer the frame clean-up to the tasklet worker.
+		 * This is necessary, because carl9170_tx_drop
+		 * does not work in an irqsave context.
+		 */
+		usb_anchor_urb(urb, &ar->tx_err);
+		return;
+
+	/* a random transmission error has occurred? */
+	default:
+		if (net_ratelimit()) {
+			dev_err(&ar->udev->dev, "tx failed (%d)\n",
+				urb->status);
+		}
+
+		usb_anchor_urb(urb, &ar->tx_err);
+		break;
+	}
+
+	if (likely(IS_STARTED(ar)))
+		carl9170_usb_submit_data_urb(ar);
+}
+
+static int carl9170_usb_submit_cmd_urb(struct ar9170 *ar)
+{
+	struct urb *urb;
+	int err;
+
+	if (atomic_inc_return(&ar->tx_cmd_urbs) != 1) {
+		atomic_dec(&ar->tx_cmd_urbs);
+		return 0;
+	}
+
+	urb = usb_get_from_anchor(&ar->tx_cmd);
+	if (!urb) {
+		atomic_dec(&ar->tx_cmd_urbs);
+		return 0;
+	}
+
+	usb_anchor_urb(urb, &ar->tx_anch);
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (unlikely(err)) {
+		usb_unanchor_urb(urb);
+		atomic_dec(&ar->tx_cmd_urbs);
+	}
+	usb_free_urb(urb);
+
+	return err;
+}
+
+static void carl9170_usb_cmd_complete(struct urb *urb)
+{
+	struct ar9170 *ar = urb->context;
+	int err = 0;
+
+	if (WARN_ON_ONCE(!ar))
+		return;
+
+	atomic_dec(&ar->tx_cmd_urbs);
+
+	switch (urb->status) {
+	/* everything is fine */
+	case 0:
+		break;
+
+	/* disconnect */
+	case -ENOENT:
+	case -ECONNRESET:
+	case -ENODEV:
+	case -ESHUTDOWN:
+		return;
+
+	default:
+		err = urb->status;
+		break;
+	}
+
+	if (!IS_INITIALIZED(ar))
+		return;
+
+	if (err)
+		dev_err(&ar->udev->dev, "submit cmd cb failed (%d).\n", err);
+
+	err = carl9170_usb_submit_cmd_urb(ar);
+	if (err)
+		dev_err(&ar->udev->dev, "submit cmd failed (%d).\n", err);
+}
+
+static void carl9170_usb_rx_irq_complete(struct urb *urb)
+{
+	struct ar9170 *ar = urb->context;
+
+	if (WARN_ON_ONCE(!ar))
+		return;
+
+	switch (urb->status) {
+	/* everything is fine */
+	case 0:
+		break;
+
+	/* disconnect */
+	case -ENOENT:
+	case -ECONNRESET:
+	case -ENODEV:
+	case -ESHUTDOWN:
+		return;
+
+	default:
+		goto resubmit;
+	}
+
+	carl9170_handle_command_response(ar, urb->transfer_buffer,
+					 urb->actual_length);
+
+resubmit:
+	usb_anchor_urb(urb, &ar->rx_anch);
+	if (unlikely(usb_submit_urb(urb, GFP_ATOMIC)))
+		usb_unanchor_urb(urb);
+}
+
+static int carl9170_usb_submit_rx_urb(struct ar9170 *ar, gfp_t gfp)
+{
+	struct urb *urb;
+	int err = 0, runs = 0;
+
+	while ((atomic_read(&ar->rx_anch_urbs) < AR9170_NUM_RX_URBS) &&
+		(runs++ < AR9170_NUM_RX_URBS)) {
+		err = -ENOSPC;
+		urb = usb_get_from_anchor(&ar->rx_pool);
+		if (urb) {
+			usb_anchor_urb(urb, &ar->rx_anch);
+			err = usb_submit_urb(urb, gfp);
+			if (unlikely(err)) {
+				usb_unanchor_urb(urb);
+				usb_anchor_urb(urb, &ar->rx_pool);
+			} else {
+				atomic_dec(&ar->rx_pool_urbs);
+				atomic_inc(&ar->rx_anch_urbs);
+			}
+			usb_free_urb(urb);
+		}
+	}
+
+	return err;
+}
+
+static void carl9170_usb_rx_work(struct ar9170 *ar)
+{
+	struct urb *urb;
+	int i;
+
+	for (i = 0; i < AR9170_NUM_RX_URBS_POOL; i++) {
+		urb = usb_get_from_anchor(&ar->rx_work);
+		if (!urb)
+			break;
+
+		atomic_dec(&ar->rx_work_urbs);
+		if (IS_INITIALIZED(ar)) {
+			carl9170_rx(ar, urb->transfer_buffer,
+				    urb->actual_length);
+		}
+
+		usb_anchor_urb(urb, &ar->rx_pool);
+		atomic_inc(&ar->rx_pool_urbs);
+
+		usb_free_urb(urb);
+
+		carl9170_usb_submit_rx_urb(ar, GFP_ATOMIC);
+	}
+}
+
+void carl9170_usb_handle_tx_err(struct ar9170 *ar)
+{
+	struct urb *urb;
+
+	while ((urb = usb_get_from_anchor(&ar->tx_err))) {
+		struct sk_buff *skb = (void *)urb->context;
+
+		carl9170_tx_drop(ar, skb);
+		carl9170_tx_callback(ar, skb);
+		usb_free_urb(urb);
+	}
+}
+
+static void carl9170_usb_tasklet(unsigned long data)
+{
+	struct ar9170 *ar = (struct ar9170 *) data;
+
+	if (!IS_INITIALIZED(ar))
+		return;
+
+	carl9170_usb_rx_work(ar);
+
+	/*
+	 * Strictly speaking: The tx scheduler is not part of the USB system.
+	 * But the rx worker returns frames back to the mac80211-stack and
+	 * this is the _perfect_ place to generate the next transmissions.
+	 */
+	if (IS_STARTED(ar))
+		carl9170_tx_scheduler(ar);
+}
+
+static void carl9170_usb_rx_complete(struct urb *urb)
+{
+	struct ar9170 *ar = (struct ar9170 *)urb->context;
+	int err;
+
+	if (WARN_ON_ONCE(!ar))
+		return;
+
+	atomic_dec(&ar->rx_anch_urbs);
+
+	switch (urb->status) {
+	case 0:
+		/* rx path */
+		usb_anchor_urb(urb, &ar->rx_work);
+		atomic_inc(&ar->rx_work_urbs);
+		break;
+
+	case -ENOENT:
+	case -ECONNRESET:
+	case -ENODEV:
+	case -ESHUTDOWN:
+		/* handle disconnect events*/
+		return;
+
+	default:
+		/* handle all other errors */
+		usb_anchor_urb(urb, &ar->rx_pool);
+		atomic_inc(&ar->rx_pool_urbs);
+		break;
+	}
+
+	err = carl9170_usb_submit_rx_urb(ar, GFP_ATOMIC);
+	if (unlikely(err)) {
+		/*
+		 * usb_submit_rx_urb reported a problem.
+		 * In case this is due to a rx buffer shortage,
+		 * elevate the tasklet worker priority to
+		 * the highest available level.
+		 */
+		tasklet_hi_schedule(&ar->usb_tasklet);
+
+		if (atomic_read(&ar->rx_anch_urbs) == 0) {
+			/*
+			 * The system is too slow to cope with
+			 * the enormous workload. We have simply
+			 * run out of active rx urbs and this
+			 * unfortunatly leads to an unpredictable
+			 * device.
+			 */
+
+			carl9170_restart(ar, CARL9170_RR_SLOW_SYSTEM);
+		}
+	} else {
+		/*
+		 * Using anything less than _high_ priority absolutely
+		 * kills the rx performance my UP-System...
+		 */
+		tasklet_hi_schedule(&ar->usb_tasklet);
+	}
+}
+
+static struct urb *carl9170_usb_alloc_rx_urb(struct ar9170 *ar, gfp_t gfp)
+{
+	struct urb *urb;
+	void *buf;
+
+	buf = kmalloc(ar->fw.rx_size, gfp);
+	if (!buf)
+		return NULL;
+
+	urb = usb_alloc_urb(0, gfp);
+	if (!urb) {
+		kfree(buf);
+		return NULL;
+	}
+
+	usb_fill_bulk_urb(urb, ar->udev, usb_rcvbulkpipe(ar->udev,
+			  AR9170_USB_EP_RX), buf, ar->fw.rx_size,
+			  carl9170_usb_rx_complete, ar);
+
+	urb->transfer_flags |= URB_FREE_BUFFER;
+
+	return urb;
+}
+
+static int carl9170_usb_send_rx_irq_urb(struct ar9170 *ar)
+{
+	struct urb *urb = NULL;
+	void *ibuf;
+	int err = -ENOMEM;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		goto out;
+
+	ibuf = kmalloc(AR9170_USB_EP_CTRL_MAX, GFP_KERNEL);
+	if (!ibuf)
+		goto out;
+
+	usb_fill_int_urb(urb, ar->udev, usb_rcvintpipe(ar->udev,
+			 AR9170_USB_EP_IRQ), ibuf, AR9170_USB_EP_CTRL_MAX,
+			 carl9170_usb_rx_irq_complete, ar, 1);
+
+	urb->transfer_flags |= URB_FREE_BUFFER;
+
+	usb_anchor_urb(urb, &ar->rx_anch);
+	err = usb_submit_urb(urb, GFP_KERNEL);
+	if (err)
+		usb_unanchor_urb(urb);
+
+out:
+	usb_free_urb(urb);
+	return err;
+}
+
+static int carl9170_usb_init_rx_bulk_urbs(struct ar9170 *ar)
+{
+	struct urb *urb;
+	int i, err = -EINVAL;
+
+	/*
+	 * The driver actively maintains a second shadow
+	 * pool for inactive, but fully-prepared rx urbs.
+	 *
+	 * The pool should help the driver to master huge
+	 * workload spikes without running the risk of
+	 * undersupplying the hardware or wasting time by
+	 * processing rx data (streams) inside the urb
+	 * completion (hardirq context).
+	 */
+	for (i = 0; i < AR9170_NUM_RX_URBS_POOL; i++) {
+		urb = carl9170_usb_alloc_rx_urb(ar, GFP_KERNEL);
+		if (!urb) {
+			err = -ENOMEM;
+			goto err_out;
+		}
+
+		usb_anchor_urb(urb, &ar->rx_pool);
+		atomic_inc(&ar->rx_pool_urbs);
+		usb_free_urb(urb);
+	}
+
+	err = carl9170_usb_submit_rx_urb(ar, GFP_KERNEL);
+	if (err)
+		goto err_out;
+
+	/* the device now waiting for the firmware. */
+	carl9170_set_state_when(ar, CARL9170_STOPPED, CARL9170_IDLE);
+	return 0;
+
+err_out:
+
+	usb_scuttle_anchored_urbs(&ar->rx_pool);
+	usb_scuttle_anchored_urbs(&ar->rx_work);
+	usb_kill_anchored_urbs(&ar->rx_anch);
+	return err;
+}
+
+static int carl9170_usb_flush(struct ar9170 *ar)
+{
+	struct urb *urb;
+	int ret, err = 0;
+
+	while ((urb = usb_get_from_anchor(&ar->tx_wait))) {
+		struct sk_buff *skb = (void *)urb->context;
+		carl9170_tx_drop(ar, skb);
+		carl9170_tx_callback(ar, skb);
+		usb_free_urb(urb);
+	}
+
+	ret = usb_wait_anchor_empty_timeout(&ar->tx_cmd, HZ);
+	if (ret == 0)
+		err = -ETIMEDOUT;
+
+	/* lets wait a while until the tx - queues are dried out */
+	ret = usb_wait_anchor_empty_timeout(&ar->tx_anch, HZ);
+	if (ret == 0)
+		err = -ETIMEDOUT;
+
+	usb_kill_anchored_urbs(&ar->tx_anch);
+	carl9170_usb_handle_tx_err(ar);
+
+	return err;
+}
+
+static void carl9170_usb_cancel_urbs(struct ar9170 *ar)
+{
+	int err;
+
+	carl9170_set_state(ar, CARL9170_UNKNOWN_STATE);
+
+	err = carl9170_usb_flush(ar);
+	if (err)
+		dev_err(&ar->udev->dev, "stuck tx urbs!\n");
+
+	usb_poison_anchored_urbs(&ar->tx_anch);
+	carl9170_usb_handle_tx_err(ar);
+	usb_poison_anchored_urbs(&ar->rx_anch);
+
+	tasklet_kill(&ar->usb_tasklet);
+
+	usb_scuttle_anchored_urbs(&ar->rx_work);
+	usb_scuttle_anchored_urbs(&ar->rx_pool);
+	usb_scuttle_anchored_urbs(&ar->tx_cmd);
+}
+
+int __carl9170_exec_cmd(struct ar9170 *ar, struct carl9170_cmd *cmd,
+			const bool free_buf)
+{
+	struct urb *urb;
+
+	if (!IS_INITIALIZED(ar))
+		return -EPERM;
+
+	if (WARN_ON(cmd->hdr.len > CARL9170_MAX_CMD_LEN - 4))
+		return -EINVAL;
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb)
+		return -ENOMEM;
+
+	usb_fill_int_urb(urb, ar->udev, usb_sndintpipe(ar->udev,
+		AR9170_USB_EP_CMD), cmd, cmd->hdr.len + 4,
+		carl9170_usb_cmd_complete, ar, 1);
+
+	urb->transfer_flags |= URB_ZERO_PACKET;
+
+	if (free_buf)
+		urb->transfer_flags |= URB_FREE_BUFFER;
+
+	usb_anchor_urb(urb, &ar->tx_cmd);
+	usb_free_urb(urb);
+
+	return carl9170_usb_submit_cmd_urb(ar);
+}
+
+int carl9170_exec_cmd(struct ar9170 *ar, const enum carl9170_cmd_oids cmd,
+	unsigned int plen, void *payload, unsigned int outlen, void *out)
+{
+	int err = -ENOMEM;
+
+	if (!IS_ACCEPTING_CMD(ar))
+		return -EIO;
+
+	if (!(cmd & CARL9170_CMD_ASYNC_FLAG))
+		might_sleep();
+
+	ar->cmd.hdr.len = plen;
+	ar->cmd.hdr.cmd = cmd;
+	/* writing multiple regs fills this buffer already */
+	if (plen && payload != (u8 *)(ar->cmd.data))
+		memcpy(ar->cmd.data, payload, plen);
+
+	spin_lock_bh(&ar->cmd_lock);
+	ar->readbuf = (u8 *)out;
+	ar->readlen = outlen;
+	spin_unlock_bh(&ar->cmd_lock);
+
+	err = __carl9170_exec_cmd(ar, &ar->cmd, false);
+
+	if (!(cmd & CARL9170_CMD_ASYNC_FLAG)) {
+		err = wait_for_completion_timeout(&ar->cmd_wait, HZ);
+		if (err == 0) {
+			err = -ETIMEDOUT;
+			goto err_unbuf;
+		}
+
+		if (ar->readlen != outlen) {
+			err = -EMSGSIZE;
+			goto err_unbuf;
+		}
+	}
+
+	return 0;
+
+err_unbuf:
+	/* Maybe the device was removed in the moment we were waiting? */
+	if (IS_STARTED(ar)) {
+		dev_err(&ar->udev->dev, "no command feedback "
+			"received (%d).\n", err);
+
+		/* provide some maybe useful debug information */
+		print_hex_dump_bytes("carl9170 cmd: ", DUMP_PREFIX_NONE,
+				     &ar->cmd, plen + 4);
+
+		carl9170_restart(ar, CARL9170_RR_COMMAND_TIMEOUT);
+	}
+
+	/* invalidate to avoid completing the next command prematurely */
+	spin_lock_bh(&ar->cmd_lock);
+	ar->readbuf = NULL;
+	ar->readlen = 0;
+	spin_unlock_bh(&ar->cmd_lock);
+
+	return err;
+}
+
+void carl9170_usb_tx(struct ar9170 *ar, struct sk_buff *skb)
+{
+	struct urb *urb;
+	struct ar9170_stream *tx_stream;
+	void *data;
+	unsigned int len;
+
+	if (!IS_STARTED(ar))
+		goto err_drop;
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb)
+		goto err_drop;
+
+	if (ar->fw.tx_stream) {
+		tx_stream = (void *) (skb->data - sizeof(*tx_stream));
+
+		len = skb->len + sizeof(*tx_stream);
+		tx_stream->length = cpu_to_le16(len);
+		tx_stream->tag = cpu_to_le16(AR9170_TX_STREAM_TAG);
+		data = tx_stream;
+	} else {
+		data = skb->data;
+		len = skb->len;
+	}
+
+	usb_fill_bulk_urb(urb, ar->udev, usb_sndbulkpipe(ar->udev,
+		AR9170_USB_EP_TX), data, len,
+		carl9170_usb_tx_data_complete, skb);
+
+	urb->transfer_flags |= URB_ZERO_PACKET;
+
+	usb_anchor_urb(urb, &ar->tx_wait);
+
+	usb_free_urb(urb);
+
+	carl9170_usb_submit_data_urb(ar);
+	return;
+
+err_drop:
+	carl9170_tx_drop(ar, skb);
+	carl9170_tx_callback(ar, skb);
+}
+
+static void carl9170_release_firmware(struct ar9170 *ar)
+{
+	if (ar->fw.fw) {
+		release_firmware(ar->fw.fw);
+		memset(&ar->fw, 0, sizeof(ar->fw));
+	}
+}
+
+void carl9170_usb_stop(struct ar9170 *ar)
+{
+	int ret;
+
+	carl9170_set_state_when(ar, CARL9170_IDLE, CARL9170_STOPPED);
+
+	ret = carl9170_usb_flush(ar);
+	if (ret)
+		dev_err(&ar->udev->dev, "kill pending tx urbs.\n");
+
+	usb_poison_anchored_urbs(&ar->tx_anch);
+	carl9170_usb_handle_tx_err(ar);
+
+	/* kill any pending command */
+	spin_lock_bh(&ar->cmd_lock);
+	ar->readlen = 0;
+	spin_unlock_bh(&ar->cmd_lock);
+	complete_all(&ar->cmd_wait);
+
+	/* This is required to prevent an early completion on _start */
+	INIT_COMPLETION(ar->cmd_wait);
+
+	/*
+	 * Note:
+	 * So far we freed all tx urbs, but we won't dare to touch any rx urbs.
+	 * Else we would end up with a unresponsive device...
+	 */
+}
+
+int carl9170_usb_open(struct ar9170 *ar)
+{
+	usb_unpoison_anchored_urbs(&ar->tx_anch);
+
+	carl9170_set_state_when(ar, CARL9170_STOPPED, CARL9170_IDLE);
+	return 0;
+}
+
+static int carl9170_usb_load_firmware(struct ar9170 *ar)
+{
+	const u8 *data;
+	u8 *buf;
+	unsigned int transfer;
+	size_t len;
+	u32 addr;
+	int err = 0;
+
+	buf = kmalloc(4096, GFP_KERNEL);
+	if (!buf) {
+		err = -ENOMEM;
+		goto err_out;
+	}
+
+	data = ar->fw.fw->data;
+	len = ar->fw.fw->size;
+	addr = ar->fw.address;
+
+	/* this removes the miniboot image */
+	data += ar->fw.offset;
+	len -= ar->fw.offset;
+
+	while (len) {
+		transfer = min_t(unsigned int, len, 4096u);
+		memcpy(buf, data, transfer);
+
+		err = usb_control_msg(ar->udev, usb_sndctrlpipe(ar->udev, 0),
+				      0x30 /* FW DL */, 0x40 | USB_DIR_OUT,
+				      addr >> 8, 0, buf, transfer, 100);
+
+		if (err < 0) {
+			kfree(buf);
+			goto err_out;
+		}
+
+		len -= transfer;
+		data += transfer;
+		addr += transfer;
+	}
+	kfree(buf);
+
+	err = usb_control_msg(ar->udev, usb_sndctrlpipe(ar->udev, 0),
+			      0x31 /* FW DL COMPLETE */,
+			      0x40 | USB_DIR_OUT, 0, 0, NULL, 0, 200);
+
+	if (wait_for_completion_timeout(&ar->fw_boot_wait, HZ) == 0) {
+		err = -ETIMEDOUT;
+		goto err_out;
+	}
+
+	err = carl9170_echo_test(ar, 0x4a110123);
+	if (err)
+		goto err_out;
+
+	/* firmware restarts cmd counter */
+	ar->cmd_seq = -1;
+
+	return 0;
+
+err_out:
+	dev_err(&ar->udev->dev, "firmware upload failed (%d).\n", err);
+	return err;
+}
+
+int carl9170_usb_restart(struct ar9170 *ar)
+{
+	int err = 0;
+
+	if (ar->intf->condition != USB_INTERFACE_BOUND)
+		return 0;
+
+	/* Disable command response sequence counter. */
+	ar->cmd_seq = -2;
+
+	err = carl9170_reboot(ar);
+
+	carl9170_usb_stop(ar);
+
+	if (err)
+		goto err_out;
+
+	tasklet_schedule(&ar->usb_tasklet);
+
+	/* The reboot procedure can take quite a while to complete. */
+	msleep(1100);
+
+	err = carl9170_usb_open(ar);
+	if (err)
+		goto err_out;
+
+	err = carl9170_usb_load_firmware(ar);
+	if (err)
+		goto err_out;
+
+	return 0;
+
+err_out:
+	carl9170_usb_cancel_urbs(ar);
+	return err;
+}
+
+void carl9170_usb_reset(struct ar9170 *ar)
+{
+	/*
+	 * This is the last resort to get the device going again
+	 * without any *user replugging action*.
+	 *
+	 * But there is a catch: usb_reset really is like a physical
+	 * *reconnect*. The mac80211 state will be lost in the process.
+	 * Therefore a userspace application, which is monitoring
+	 * the link must step in.
+	 */
+	carl9170_usb_cancel_urbs(ar);
+
+	carl9170_usb_stop(ar);
+
+	usb_queue_reset_device(ar->intf);
+}
+
+static int carl9170_usb_init_device(struct ar9170 *ar)
+{
+	int err;
+
+	err = carl9170_usb_send_rx_irq_urb(ar);
+	if (err)
+		goto err_out;
+
+	err = carl9170_usb_init_rx_bulk_urbs(ar);
+	if (err)
+		goto err_unrx;
+
+	mutex_lock(&ar->mutex);
+	err = carl9170_usb_load_firmware(ar);
+	mutex_unlock(&ar->mutex);
+	if (err)
+		goto err_unrx;
+
+	return 0;
+
+err_unrx:
+	carl9170_usb_cancel_urbs(ar);
+
+err_out:
+	return err;
+}
+
+static void carl9170_usb_firmware_failed(struct ar9170 *ar)
+{
+	struct device *parent = ar->udev->dev.parent;
+	struct usb_device *udev;
+
+	/*
+	 * Store a copy of the usb_device pointer locally.
+	 * This is because device_release_driver initiates
+	 * carl9170_usb_disconnect, which in turn frees our
+	 * driver context (ar).
+	 */
+	udev = ar->udev;
+
+	complete(&ar->fw_load_wait);
+
+	/* unbind anything failed */
+	if (parent)
+		device_lock(parent);
+
+	device_release_driver(&udev->dev);
+	if (parent)
+		device_unlock(parent);
+
+	usb_put_dev(udev);
+}
+
+static void carl9170_usb_firmware_finish(struct ar9170 *ar)
+{
+	int err;
+
+	err = carl9170_parse_firmware(ar);
+	if (err)
+		goto err_freefw;
+
+	err = carl9170_usb_init_device(ar);
+	if (err)
+		goto err_freefw;
+
+	err = carl9170_usb_open(ar);
+	if (err)
+		goto err_unrx;
+
+	err = carl9170_register(ar);
+
+	carl9170_usb_stop(ar);
+	if (err)
+		goto err_unrx;
+
+	complete(&ar->fw_load_wait);
+	usb_put_dev(ar->udev);
+	return;
+
+err_unrx:
+	carl9170_usb_cancel_urbs(ar);
+
+err_freefw:
+	carl9170_release_firmware(ar);
+	carl9170_usb_firmware_failed(ar);
+}
+
+static void carl9170_usb_firmware_step2(const struct firmware *fw,
+					void *context)
+{
+	struct ar9170 *ar = context;
+
+	if (fw) {
+		ar->fw.fw = fw;
+		carl9170_usb_firmware_finish(ar);
+		return;
+	}
+
+	dev_err(&ar->udev->dev, "firmware not found.\n");
+	carl9170_usb_firmware_failed(ar);
+}
+
+static int carl9170_usb_probe(struct usb_interface *intf,
+			      const struct usb_device_id *id)
+{
+	struct ar9170 *ar;
+	struct usb_device *udev;
+	int err;
+
+	err = usb_reset_device(interface_to_usbdev(intf));
+	if (err)
+		return err;
+
+	ar = carl9170_alloc(sizeof(*ar));
+	if (IS_ERR(ar))
+		return PTR_ERR(ar);
+
+	udev = interface_to_usbdev(intf);
+	usb_get_dev(udev);
+	ar->udev = udev;
+	ar->intf = intf;
+	ar->features = id->driver_info;
+
+	usb_set_intfdata(intf, ar);
+	SET_IEEE80211_DEV(ar->hw, &intf->dev);
+
+	init_usb_anchor(&ar->rx_anch);
+	init_usb_anchor(&ar->rx_pool);
+	init_usb_anchor(&ar->rx_work);
+	init_usb_anchor(&ar->tx_wait);
+	init_usb_anchor(&ar->tx_anch);
+	init_usb_anchor(&ar->tx_cmd);
+	init_usb_anchor(&ar->tx_err);
+	init_completion(&ar->cmd_wait);
+	init_completion(&ar->fw_boot_wait);
+	init_completion(&ar->fw_load_wait);
+	tasklet_init(&ar->usb_tasklet, carl9170_usb_tasklet,
+		     (unsigned long)ar);
+
+	atomic_set(&ar->tx_cmd_urbs, 0);
+	atomic_set(&ar->tx_anch_urbs, 0);
+	atomic_set(&ar->rx_work_urbs, 0);
+	atomic_set(&ar->rx_anch_urbs, 0);
+	atomic_set(&ar->rx_pool_urbs, 0);
+	ar->cmd_seq = -2;
+
+	usb_get_dev(ar->udev);
+
+	carl9170_set_state(ar, CARL9170_STOPPED);
+
+	return request_firmware_nowait(THIS_MODULE, 1, CARL9170FW_NAME,
+		&ar->udev->dev, GFP_KERNEL, ar, carl9170_usb_firmware_step2);
+}
+
+static void carl9170_usb_disconnect(struct usb_interface *intf)
+{
+	struct ar9170 *ar = usb_get_intfdata(intf);
+	struct usb_device *udev;
+
+	if (WARN_ON(!ar))
+		return;
+
+	udev = ar->udev;
+	wait_for_completion(&ar->fw_load_wait);
+
+	if (IS_INITIALIZED(ar)) {
+		carl9170_reboot(ar);
+		carl9170_usb_stop(ar);
+	}
+
+	carl9170_usb_cancel_urbs(ar);
+	carl9170_unregister(ar);
+
+	usb_set_intfdata(intf, NULL);
+
+	carl9170_release_firmware(ar);
+	carl9170_free(ar);
+	usb_put_dev(udev);
+}
+
+#ifdef CONFIG_PM
+static int carl9170_usb_suspend(struct usb_interface *intf,
+				pm_message_t message)
+{
+	struct ar9170 *ar = usb_get_intfdata(intf);
+
+	if (!ar)
+		return -ENODEV;
+
+	carl9170_usb_cancel_urbs(ar);
+
+	/*
+	 * firmware automatically reboots for usb suspend.
+	 */
+
+	return 0;
+}
+
+static int carl9170_usb_resume(struct usb_interface *intf)
+{
+	struct ar9170 *ar = usb_get_intfdata(intf);
+	int err;
+
+	if (!ar)
+		return -ENODEV;
+
+	usb_unpoison_anchored_urbs(&ar->rx_anch);
+
+	err = carl9170_usb_init_device(ar);
+	if (err)
+		goto err_unrx;
+
+	err = carl9170_usb_open(ar);
+	if (err)
+		goto err_unrx;
+
+	return 0;
+
+err_unrx:
+	carl9170_usb_cancel_urbs(ar);
+
+	return err;
+}
+#endif /* CONFIG_PM */
+
+static struct usb_driver carl9170_driver = {
+	.name = KBUILD_MODNAME,
+	.probe = carl9170_usb_probe,
+	.disconnect = carl9170_usb_disconnect,
+	.id_table = carl9170_usb_ids,
+	.soft_unbind = 1,
+#ifdef CONFIG_PM
+	.suspend = carl9170_usb_suspend,
+	.resume = carl9170_usb_resume,
+#endif /* CONFIG_PM */
+};
+
+static int __init carl9170_usb_init(void)
+{
+	return usb_register(&carl9170_driver);
+}
+
+static void __exit carl9170_usb_exit(void)
+{
+	usb_deregister(&carl9170_driver);
+}
+
+module_init(carl9170_usb_init);
+module_exit(carl9170_usb_exit);
diff --git a/drivers/net/wireless/ath/carl9170/version.h b/drivers/net/wireless/ath/carl9170/version.h
new file mode 100644
index 0000000..0e917f8
--- /dev/null
+++ b/drivers/net/wireless/ath/carl9170/version.h
@@ -0,0 +1,7 @@
+#ifndef __CARL9170_SHARED_VERSION_H
+#define __CARL9170_SHARED_VERSION_H
+#define CARL9170FW_VERSION_YEAR 10
+#define CARL9170FW_VERSION_MONTH 8
+#define CARL9170FW_VERSION_DAY 30
+#define CARL9170FW_VERSION_GIT "1.8.8.1"
+#endif /* __CARL9170_SHARED_VERSION_H */
diff --git a/drivers/net/wireless/ath/carl9170/wlan.h b/drivers/net/wireless/ath/carl9170/wlan.h
new file mode 100644
index 0000000..48ead22
--- /dev/null
+++ b/drivers/net/wireless/ath/carl9170/wlan.h
@@ -0,0 +1,412 @@
+/*
+ * Shared Atheros AR9170 Header
+ *
+ * RX/TX meta descriptor format
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * This program is free software; you can redistribute it 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.
+ *
+ * This program 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or distribute this software for any
+ *    purpose with or without fee is hereby granted, provided that the above
+ *    copyright notice and this permission notice appear in all copies.
+ *
+ *    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __CARL9170_SHARED_WLAN_H
+#define __CARL9170_SHARED_WLAN_H
+
+#include "fwcmd.h"
+
+#define	AR9170_RX_PHY_RATE_CCK_1M		0x0a
+#define	AR9170_RX_PHY_RATE_CCK_2M		0x14
+#define	AR9170_RX_PHY_RATE_CCK_5M		0x37
+#define	AR9170_RX_PHY_RATE_CCK_11M		0x6e
+
+#define	AR9170_ENC_ALG_NONE			0x0
+#define	AR9170_ENC_ALG_WEP64			0x1
+#define	AR9170_ENC_ALG_TKIP			0x2
+#define	AR9170_ENC_ALG_AESCCMP			0x4
+#define	AR9170_ENC_ALG_WEP128			0x5
+#define	AR9170_ENC_ALG_WEP256			0x6
+#define	AR9170_ENC_ALG_CENC			0x7
+
+#define	AR9170_RX_ENC_SOFTWARE			0x8
+
+#define	AR9170_RX_STATUS_MODULATION		0x03
+#define	AR9170_RX_STATUS_MODULATION_S		0
+#define	AR9170_RX_STATUS_MODULATION_CCK		0x00
+#define	AR9170_RX_STATUS_MODULATION_OFDM	0x01
+#define	AR9170_RX_STATUS_MODULATION_HT		0x02
+#define	AR9170_RX_STATUS_MODULATION_DUPOFDM	0x03
+
+/* depends on modulation */
+#define	AR9170_RX_STATUS_SHORT_PREAMBLE		0x08
+#define	AR9170_RX_STATUS_GREENFIELD		0x08
+
+#define	AR9170_RX_STATUS_MPDU			0x30
+#define	AR9170_RX_STATUS_MPDU_S			4
+#define	AR9170_RX_STATUS_MPDU_SINGLE		0x00
+#define	AR9170_RX_STATUS_MPDU_FIRST		0x20
+#define	AR9170_RX_STATUS_MPDU_MIDDLE		0x30
+#define	AR9170_RX_STATUS_MPDU_LAST		0x10
+
+#define	AR9170_RX_ERROR_RXTO			0x01
+#define	AR9170_RX_ERROR_OVERRUN			0x02
+#define	AR9170_RX_ERROR_DECRYPT			0x04
+#define	AR9170_RX_ERROR_FCS			0x08
+#define	AR9170_RX_ERROR_WRONG_RA		0x10
+#define	AR9170_RX_ERROR_PLCP			0x20
+#define	AR9170_RX_ERROR_MMIC			0x40
+#define	AR9170_RX_ERROR_FATAL			0x80
+
+/* these are either-or */
+#define	AR9170_TX_MAC_PROT_RTS			0x0001
+#define	AR9170_TX_MAC_PROT_CTS			0x0002
+#define	AR9170_TX_MAC_PROT			0x0003
+
+#define	AR9170_TX_MAC_NO_ACK			0x0004
+/* if unset, MAC will only do SIFS space before frame */
+#define	AR9170_TX_MAC_BACKOFF			0x0008
+#define	AR9170_TX_MAC_BURST			0x0010
+#define	AR9170_TX_MAC_AGGR			0x0020
+
+/* encryption is a two-bit field */
+#define	AR9170_TX_MAC_ENCR_NONE			0x0000
+#define	AR9170_TX_MAC_ENCR_RC4			0x0040
+#define	AR9170_TX_MAC_ENCR_CENC			0x0080
+#define	AR9170_TX_MAC_ENCR_AES			0x00c0
+
+#define	AR9170_TX_MAC_MMIC			0x0100
+#define	AR9170_TX_MAC_HW_DURATION		0x0200
+#define	AR9170_TX_MAC_QOS_S			10
+#define	AR9170_TX_MAC_QOS			0x0c00
+#define	AR9170_TX_MAC_DISABLE_TXOP		0x1000
+#define	AR9170_TX_MAC_TXOP_RIFS			0x2000
+#define	AR9170_TX_MAC_IMM_BA			0x4000
+
+/* either-or */
+#define	AR9170_TX_PHY_MOD_CCK			0x00000000
+#define	AR9170_TX_PHY_MOD_OFDM			0x00000001
+#define	AR9170_TX_PHY_MOD_HT			0x00000002
+
+/* depends on modulation */
+#define	AR9170_TX_PHY_SHORT_PREAMBLE		0x00000004
+#define	AR9170_TX_PHY_GREENFIELD		0x00000004
+
+#define	AR9170_TX_PHY_BW_S			3
+#define	AR9170_TX_PHY_BW			(3 << AR9170_TX_PHY_BW_SHIFT)
+#define	AR9170_TX_PHY_BW_20MHZ			0
+#define	AR9170_TX_PHY_BW_40MHZ			2
+#define	AR9170_TX_PHY_BW_40MHZ_DUP		3
+
+#define	AR9170_TX_PHY_TX_HEAVY_CLIP_S		6
+#define	AR9170_TX_PHY_TX_HEAVY_CLIP		(7 << \
+						 AR9170_TX_PHY_TX_HEAVY_CLIP_S)
+
+#define	AR9170_TX_PHY_TX_PWR_S			9
+#define	AR9170_TX_PHY_TX_PWR			(0x3f << \
+						 AR9170_TX_PHY_TX_PWR_S)
+
+#define	AR9170_TX_PHY_TXCHAIN_S			15
+#define	AR9170_TX_PHY_TXCHAIN			(7 << \
+						 AR9170_TX_PHY_TXCHAIN_S)
+#define	AR9170_TX_PHY_TXCHAIN_1			1
+/* use for cck, ofdm 6/9/12/18/24 and HT if capable */
+#define	AR9170_TX_PHY_TXCHAIN_2			5
+
+#define	AR9170_TX_PHY_MCS_S			18
+#define	AR9170_TX_PHY_MCS			(0x7f << \
+						 AR9170_TX_PHY_MCS_S)
+
+#define	AR9170_TX_PHY_RATE_CCK_1M		0x0
+#define	AR9170_TX_PHY_RATE_CCK_2M		0x1
+#define	AR9170_TX_PHY_RATE_CCK_5M		0x2
+#define	AR9170_TX_PHY_RATE_CCK_11M		0x3
+
+/* same as AR9170_RX_PHY_RATE */
+#define	AR9170_TXRX_PHY_RATE_OFDM_6M		0xb
+#define	AR9170_TXRX_PHY_RATE_OFDM_9M		0xf
+#define	AR9170_TXRX_PHY_RATE_OFDM_12M		0xa
+#define	AR9170_TXRX_PHY_RATE_OFDM_18M		0xe
+#define	AR9170_TXRX_PHY_RATE_OFDM_24M		0x9
+#define	AR9170_TXRX_PHY_RATE_OFDM_36M		0xd
+#define	AR9170_TXRX_PHY_RATE_OFDM_48M		0x8
+#define	AR9170_TXRX_PHY_RATE_OFDM_54M		0xc
+
+#define	AR9170_TXRX_PHY_RATE_HT_MCS0		0x0
+#define	AR9170_TXRX_PHY_RATE_HT_MCS1		0x1
+#define	AR9170_TXRX_PHY_RATE_HT_MCS2		0x2
+#define	AR9170_TXRX_PHY_RATE_HT_MCS3		0x3
+#define	AR9170_TXRX_PHY_RATE_HT_MCS4		0x4
+#define	AR9170_TXRX_PHY_RATE_HT_MCS5		0x5
+#define	AR9170_TXRX_PHY_RATE_HT_MCS6		0x6
+#define	AR9170_TXRX_PHY_RATE_HT_MCS7		0x7
+#define	AR9170_TXRX_PHY_RATE_HT_MCS8		0x8
+#define	AR9170_TXRX_PHY_RATE_HT_MCS9		0x9
+#define	AR9170_TXRX_PHY_RATE_HT_MCS10		0xa
+#define	AR9170_TXRX_PHY_RATE_HT_MCS11		0xb
+#define	AR9170_TXRX_PHY_RATE_HT_MCS12		0xc
+#define	AR9170_TXRX_PHY_RATE_HT_MCS13		0xd
+#define	AR9170_TXRX_PHY_RATE_HT_MCS14		0xe
+#define	AR9170_TXRX_PHY_RATE_HT_MCS15		0xf
+
+#define	AR9170_TX_PHY_SHORT_GI			0x80000000
+
+#ifdef __CARL9170FW__
+struct ar9170_tx_hw_mac_control {
+	union {
+		struct {
+			/*
+			 * Beware of compiler bugs in all gcc pre 4.4!
+			 */
+
+			u8 erp_prot:2;
+			u8 no_ack:1;
+			u8 backoff:1;
+			u8 burst:1;
+			u8 ampdu:1;
+
+			u8 enc_mode:2;
+
+			u8 hw_mmic:1;
+			u8 hw_duration:1;
+
+			u8 qos_queue:2;
+
+			u8 disable_txop:1;
+			u8 txop_rifs:1;
+
+			u8 ba_end:1;
+			u8 probe:1;
+		} __packed;
+
+		__le16 set;
+	} __packed;
+} __packed;
+
+struct ar9170_tx_hw_phy_control {
+	union {
+		struct {
+			/*
+			 * Beware of compiler bugs in all gcc pre 4.4!
+			 */
+
+			u8 modulation:2;
+			u8 preamble:1;
+			u8 bandwidth:2;
+			u8:1;
+			u8 heavy_clip:3;
+			u8 tx_power:6;
+			u8 chains:3;
+			u8 mcs:7;
+			u8:6;
+			u8 short_gi:1;
+		} __packed;
+
+		__le32 set;
+	} __packed;
+} __packed;
+
+struct ar9170_tx_rate_info {
+	u8 tries:3;
+	u8 erp_prot:2;
+	u8 ampdu:1;
+	u8 free:2; /* free for use (e.g.:RIFS/TXOP/AMPDU) */
+} __packed;
+
+struct carl9170_tx_superdesc {
+	__le16 len;
+	u8 rix;
+	u8 cnt;
+	u8 cookie;
+	u8 ampdu_density:3;
+	u8 ampdu_factor:2;
+	u8 ampdu_commit_density:1;
+	u8 ampdu_commit_factor:1;
+	u8 ampdu_unused_bit:1;
+	u8 queue:2;
+	u8 reserved:1;
+	u8 vif_id:3;
+	u8 fill_in_tsf:1;
+	u8 cab:1;
+	u8 padding2;
+	struct ar9170_tx_rate_info ri[CARL9170_TX_MAX_RATES];
+	struct ar9170_tx_hw_phy_control rr[CARL9170_TX_MAX_RETRY_RATES];
+} __packed;
+
+struct ar9170_tx_hwdesc {
+	__le16 length;
+	struct ar9170_tx_hw_mac_control mac;
+	struct ar9170_tx_hw_phy_control phy;
+} __packed;
+
+struct ar9170_tx_frame {
+	struct ar9170_tx_hwdesc hdr;
+
+	union {
+		struct ieee80211_hdr i3e;
+		u8 payload[0];
+	} data;
+} __packed;
+
+struct carl9170_tx_superframe {
+	struct carl9170_tx_superdesc s;
+	struct ar9170_tx_frame f;
+} __packed;
+
+#endif /* __CARL9170FW__ */
+
+struct _ar9170_tx_hwdesc {
+	__le16 length;
+	__le16 mac_control;
+	__le32 phy_control;
+} __packed;
+
+#define	CARL9170_TX_SUPER_AMPDU_DENSITY_S		0
+#define	CARL9170_TX_SUPER_AMPDU_DENSITY			0x7
+#define	CARL9170_TX_SUPER_AMPDU_FACTOR			0x18
+#define	CARL9170_TX_SUPER_AMPDU_FACTOR_S		3
+#define	CARL9170_TX_SUPER_AMPDU_COMMIT_DENSITY		0x20
+#define	CARL9170_TX_SUPER_AMPDU_COMMIT_DENSITY_S	5
+#define	CARL9170_TX_SUPER_AMPDU_COMMIT_FACTOR		0x40
+#define	CARL9170_TX_SUPER_AMPDU_COMMIT_FACTOR_S		6
+
+#define CARL9170_TX_SUPER_MISC_QUEUE			0x3
+#define CARL9170_TX_SUPER_MISC_QUEUE_S			0
+#define	CARL9170_TX_SUPER_MISC_VIF_ID			0x38
+#define	CARL9170_TX_SUPER_MISC_VIF_ID_S			3
+#define	CARL9170_TX_SUPER_MISC_FILL_IN_TSF		0x40
+#define	CARL9170_TX_SUPER_MISC_CAB			0x80
+
+#define CARL9170_TX_SUPER_RI_TRIES			0x7
+#define CARL9170_TX_SUPER_RI_TRIES_S			0
+#define CARL9170_TX_SUPER_RI_ERP_PROT			0x18
+#define CARL9170_TX_SUPER_RI_ERP_PROT_S			3
+#define CARL9170_TX_SUPER_RI_AMPDU			0x20
+#define CARL9170_TX_SUPER_RI_AMPDU_S			5
+
+struct _carl9170_tx_superdesc {
+	__le16 len;
+	u8 rix;
+	u8 cnt;
+	u8 cookie;
+	u8 ampdu_settings;
+	u8 misc;
+	u8 padding;
+	u8 ri[CARL9170_TX_MAX_RATES];
+	__le32 rr[CARL9170_TX_MAX_RETRY_RATES];
+} __packed;
+
+struct _carl9170_tx_superframe {
+	struct _carl9170_tx_superdesc s;
+	struct _ar9170_tx_hwdesc f;
+	u8 frame_data[0];
+} __packed;
+
+#define	CARL9170_TX_SUPERDESC_LEN		24
+#define	AR9170_TX_HWDESC_LEN			8
+#define	AR9170_TX_SUPERFRAME_LEN		(CARL9170_TX_HWDESC_LEN + \
+						 AR9170_TX_SUPERDESC_LEN)
+
+struct ar9170_rx_head {
+	u8 plcp[12];
+} __packed;
+
+struct ar9170_rx_phystatus {
+	union {
+		struct {
+			u8 rssi_ant0, rssi_ant1, rssi_ant2,
+				rssi_ant0x, rssi_ant1x, rssi_ant2x,
+				rssi_combined;
+		} __packed;
+		u8 rssi[7];
+	} __packed;
+
+	u8 evm_stream0[6], evm_stream1[6];
+	u8 phy_err;
+} __packed;
+
+struct ar9170_rx_macstatus {
+	u8 SAidx, DAidx;
+	u8 error;
+	u8 status;
+} __packed;
+
+struct ar9170_rx_frame_single {
+	struct ar9170_rx_head phy_head;
+	struct ieee80211_hdr i3e;
+	struct ar9170_rx_phystatus phy_tail;
+	struct ar9170_rx_macstatus macstatus;
+} __packed;
+
+struct ar9170_rx_frame_head {
+	struct ar9170_rx_head phy_head;
+	struct ieee80211_hdr i3e;
+	struct ar9170_rx_macstatus macstatus;
+} __packed;
+
+struct ar9170_rx_frame_middle {
+	struct ieee80211_hdr i3e;
+	struct ar9170_rx_macstatus macstatus;
+} __packed;
+
+struct ar9170_rx_frame_tail {
+	struct ieee80211_hdr i3e;
+	struct ar9170_rx_phystatus phy_tail;
+	struct ar9170_rx_macstatus macstatus;
+} __packed;
+
+struct ar9170_rx_frame {
+	union {
+		struct ar9170_rx_frame_single single;
+		struct ar9170_rx_frame_head head;
+		struct ar9170_rx_frame_middle middle;
+		struct ar9170_rx_frame_tail tail;
+	} __packed;
+} __packed;
+
+static inline u8 ar9170_get_decrypt_type(struct ar9170_rx_macstatus *t)
+{
+	return (t->SAidx & 0xc0) >> 4 |
+	       (t->DAidx & 0xc0) >> 6;
+}
+
+enum ar9170_txq {
+	AR9170_TXQ_BE,
+
+	AR9170_TXQ_VI,
+	AR9170_TXQ_VO,
+	AR9170_TXQ_BK,
+
+	__AR9170_NUM_TXQ,
+};
+
+static const u8 ar9170_qmap[__AR9170_NUM_TXQ] = { 2, 1, 0, 3 };
+
+#define	AR9170_TXQ_DEPTH			32
+
+#endif /* __CARL9170_SHARED_WLAN_H */
diff --git a/drivers/net/wireless/ath/key.c b/drivers/net/wireless/ath/key.c
new file mode 100644
index 0000000..bd21a4d
--- /dev/null
+++ b/drivers/net/wireless/ath/key.c
@@ -0,0 +1,568 @@
+/*
+ * Copyright (c) 2009 Atheros Communications Inc.
+ * Copyright (c) 2010 Bruno Randolf <br1@einfach.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <asm/unaligned.h>
+#include <net/mac80211.h>
+
+#include "ath.h"
+#include "reg.h"
+#include "debug.h"
+
+#define REG_READ			(common->ops->read)
+#define REG_WRITE(_ah, _reg, _val)	(common->ops->write)(_ah, _val, _reg)
+
+#define IEEE80211_WEP_NKID      4       /* number of key ids */
+
+/************************/
+/* Key Cache Management */
+/************************/
+
+bool ath_hw_keyreset(struct ath_common *common, u16 entry)
+{
+	u32 keyType;
+	void *ah = common->ah;
+
+	if (entry >= common->keymax) {
+		ath_print(common, ATH_DBG_FATAL,
+			  "keychache entry %u out of range\n", entry);
+		return false;
+	}
+
+	keyType = REG_READ(ah, AR_KEYTABLE_TYPE(entry));
+
+	REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), 0);
+	REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), 0);
+	REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), 0);
+	REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), 0);
+	REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), 0);
+	REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), AR_KEYTABLE_TYPE_CLR);
+	REG_WRITE(ah, AR_KEYTABLE_MAC0(entry), 0);
+	REG_WRITE(ah, AR_KEYTABLE_MAC1(entry), 0);
+
+	if (keyType == AR_KEYTABLE_TYPE_TKIP) {
+		u16 micentry = entry + 64;
+
+		REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), 0);
+		REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), 0);
+		REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), 0);
+		REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), 0);
+
+	}
+
+	return true;
+}
+EXPORT_SYMBOL(ath_hw_keyreset);
+
+bool ath_hw_keysetmac(struct ath_common *common, u16 entry, const u8 *mac)
+{
+	u32 macHi, macLo;
+	u32 unicast_flag = AR_KEYTABLE_VALID;
+	void *ah = common->ah;
+
+	if (entry >= common->keymax) {
+		ath_print(common, ATH_DBG_FATAL,
+			  "keychache entry %u out of range\n", entry);
+		return false;
+	}
+
+	if (mac != NULL) {
+		/*
+		 * AR_KEYTABLE_VALID indicates that the address is a unicast
+		 * address, which must match the transmitter address for
+		 * decrypting frames.
+		 * Not setting this bit allows the hardware to use the key
+		 * for multicast frame decryption.
+		 */
+		if (mac[0] & 0x01)
+			unicast_flag = 0;
+
+		macHi = (mac[5] << 8) | mac[4];
+		macLo = (mac[3] << 24) |
+			(mac[2] << 16) |
+			(mac[1] << 8) |
+			mac[0];
+		macLo >>= 1;
+		macLo |= (macHi & 1) << 31;
+		macHi >>= 1;
+	} else {
+		macLo = macHi = 0;
+	}
+	REG_WRITE(ah, AR_KEYTABLE_MAC0(entry), macLo);
+	REG_WRITE(ah, AR_KEYTABLE_MAC1(entry), macHi | unicast_flag);
+
+	return true;
+}
+
+bool ath_hw_set_keycache_entry(struct ath_common *common, u16 entry,
+				 const struct ath_keyval *k,
+				 const u8 *mac)
+{
+	void *ah = common->ah;
+	u32 key0, key1, key2, key3, key4;
+	u32 keyType;
+
+	if (entry >= common->keymax) {
+		ath_print(common, ATH_DBG_FATAL,
+			  "keycache entry %u out of range\n", entry);
+		return false;
+	}
+
+	switch (k->kv_type) {
+	case ATH_CIPHER_AES_OCB:
+		keyType = AR_KEYTABLE_TYPE_AES;
+		break;
+	case ATH_CIPHER_AES_CCM:
+		if (!(common->crypt_caps & ATH_CRYPT_CAP_CIPHER_AESCCM)) {
+			ath_print(common, ATH_DBG_ANY,
+				  "AES-CCM not supported by this mac rev\n");
+			return false;
+		}
+		keyType = AR_KEYTABLE_TYPE_CCM;
+		break;
+	case ATH_CIPHER_TKIP:
+		keyType = AR_KEYTABLE_TYPE_TKIP;
+		if (entry + 64 >= common->keymax) {
+			ath_print(common, ATH_DBG_ANY,
+				  "entry %u inappropriate for TKIP\n", entry);
+			return false;
+		}
+		break;
+	case ATH_CIPHER_WEP:
+		if (k->kv_len < WLAN_KEY_LEN_WEP40) {
+			ath_print(common, ATH_DBG_ANY,
+				  "WEP key length %u too small\n", k->kv_len);
+			return false;
+		}
+		if (k->kv_len <= WLAN_KEY_LEN_WEP40)
+			keyType = AR_KEYTABLE_TYPE_40;
+		else if (k->kv_len <= WLAN_KEY_LEN_WEP104)
+			keyType = AR_KEYTABLE_TYPE_104;
+		else
+			keyType = AR_KEYTABLE_TYPE_128;
+		break;
+	case ATH_CIPHER_CLR:
+		keyType = AR_KEYTABLE_TYPE_CLR;
+		break;
+	default:
+		ath_print(common, ATH_DBG_FATAL,
+			  "cipher %u not supported\n", k->kv_type);
+		return false;
+	}
+
+	key0 = get_unaligned_le32(k->kv_val + 0);
+	key1 = get_unaligned_le16(k->kv_val + 4);
+	key2 = get_unaligned_le32(k->kv_val + 6);
+	key3 = get_unaligned_le16(k->kv_val + 10);
+	key4 = get_unaligned_le32(k->kv_val + 12);
+	if (k->kv_len <= WLAN_KEY_LEN_WEP104)
+		key4 &= 0xff;
+
+	/*
+	 * Note: Key cache registers access special memory area that requires
+	 * two 32-bit writes to actually update the values in the internal
+	 * memory. Consequently, the exact order and pairs used here must be
+	 * maintained.
+	 */
+
+	if (keyType == AR_KEYTABLE_TYPE_TKIP) {
+		u16 micentry = entry + 64;
+
+		/*
+		 * Write inverted key[47:0] first to avoid Michael MIC errors
+		 * on frames that could be sent or received at the same time.
+		 * The correct key will be written in the end once everything
+		 * else is ready.
+		 */
+		REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), ~key0);
+		REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), ~key1);
+
+		/* Write key[95:48] */
+		REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), key2);
+		REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), key3);
+
+		/* Write key[127:96] and key type */
+		REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4);
+		REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), keyType);
+
+		/* Write MAC address for the entry */
+		(void) ath_hw_keysetmac(common, entry, mac);
+
+		if (common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED) {
+			/*
+			 * TKIP uses two key cache entries:
+			 * Michael MIC TX/RX keys in the same key cache entry
+			 * (idx = main index + 64):
+			 * key0 [31:0] = RX key [31:0]
+			 * key1 [15:0] = TX key [31:16]
+			 * key1 [31:16] = reserved
+			 * key2 [31:0] = RX key [63:32]
+			 * key3 [15:0] = TX key [15:0]
+			 * key3 [31:16] = reserved
+			 * key4 [31:0] = TX key [63:32]
+			 */
+			u32 mic0, mic1, mic2, mic3, mic4;
+
+			mic0 = get_unaligned_le32(k->kv_mic + 0);
+			mic2 = get_unaligned_le32(k->kv_mic + 4);
+			mic1 = get_unaligned_le16(k->kv_txmic + 2) & 0xffff;
+			mic3 = get_unaligned_le16(k->kv_txmic + 0) & 0xffff;
+			mic4 = get_unaligned_le32(k->kv_txmic + 4);
+
+			/* Write RX[31:0] and TX[31:16] */
+			REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), mic0);
+			REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), mic1);
+
+			/* Write RX[63:32] and TX[15:0] */
+			REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), mic2);
+			REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), mic3);
+
+			/* Write TX[63:32] and keyType(reserved) */
+			REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), mic4);
+			REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry),
+				  AR_KEYTABLE_TYPE_CLR);
+
+		} else {
+			/*
+			 * TKIP uses four key cache entries (two for group
+			 * keys):
+			 * Michael MIC TX/RX keys are in different key cache
+			 * entries (idx = main index + 64 for TX and
+			 * main index + 32 + 96 for RX):
+			 * key0 [31:0] = TX/RX MIC key [31:0]
+			 * key1 [31:0] = reserved
+			 * key2 [31:0] = TX/RX MIC key [63:32]
+			 * key3 [31:0] = reserved
+			 * key4 [31:0] = reserved
+			 *
+			 * Upper layer code will call this function separately
+			 * for TX and RX keys when these registers offsets are
+			 * used.
+			 */
+			u32 mic0, mic2;
+
+			mic0 = get_unaligned_le32(k->kv_mic + 0);
+			mic2 = get_unaligned_le32(k->kv_mic + 4);
+
+			/* Write MIC key[31:0] */
+			REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), mic0);
+			REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), 0);
+
+			/* Write MIC key[63:32] */
+			REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), mic2);
+			REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), 0);
+
+			/* Write TX[63:32] and keyType(reserved) */
+			REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), 0);
+			REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry),
+				  AR_KEYTABLE_TYPE_CLR);
+		}
+
+		/* MAC address registers are reserved for the MIC entry */
+		REG_WRITE(ah, AR_KEYTABLE_MAC0(micentry), 0);
+		REG_WRITE(ah, AR_KEYTABLE_MAC1(micentry), 0);
+
+		/*
+		 * Write the correct (un-inverted) key[47:0] last to enable
+		 * TKIP now that all other registers are set with correct
+		 * values.
+		 */
+		REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), key0);
+		REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), key1);
+	} else {
+		/* Write key[47:0] */
+		REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), key0);
+		REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), key1);
+
+		/* Write key[95:48] */
+		REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), key2);
+		REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), key3);
+
+		/* Write key[127:96] and key type */
+		REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4);
+		REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), keyType);
+
+		/* Write MAC address for the entry */
+		(void) ath_hw_keysetmac(common, entry, mac);
+	}
+
+	return true;
+}
+
+static int ath_setkey_tkip(struct ath_common *common, u16 keyix, const u8 *key,
+			   struct ath_keyval *hk, const u8 *addr,
+			   bool authenticator)
+{
+	const u8 *key_rxmic;
+	const u8 *key_txmic;
+
+	key_txmic = key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY;
+	key_rxmic = key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY;
+
+	if (addr == NULL) {
+		/*
+		 * Group key installation - only two key cache entries are used
+		 * regardless of splitmic capability since group key is only
+		 * used either for TX or RX.
+		 */
+		if (authenticator) {
+			memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic));
+			memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_mic));
+		} else {
+			memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
+			memcpy(hk->kv_txmic, key_rxmic, sizeof(hk->kv_mic));
+		}
+		return ath_hw_set_keycache_entry(common, keyix, hk, addr);
+	}
+	if (common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED) {
+		/* TX and RX keys share the same key cache entry. */
+		memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
+		memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_txmic));
+		return ath_hw_set_keycache_entry(common, keyix, hk, addr);
+	}
+
+	/* Separate key cache entries for TX and RX */
+
+	/* TX key goes at first index, RX key at +32. */
+	memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic));
+	if (!ath_hw_set_keycache_entry(common, keyix, hk, NULL)) {
+		/* TX MIC entry failed. No need to proceed further */
+		ath_print(common, ATH_DBG_FATAL,
+			  "Setting TX MIC Key Failed\n");
+		return 0;
+	}
+
+	memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
+	/* XXX delete tx key on failure? */
+	return ath_hw_set_keycache_entry(common, keyix + 32, hk, addr);
+}
+
+static int ath_reserve_key_cache_slot_tkip(struct ath_common *common)
+{
+	int i;
+
+	for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) {
+		if (test_bit(i, common->keymap) ||
+		    test_bit(i + 64, common->keymap))
+			continue; /* At least one part of TKIP key allocated */
+		if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED) &&
+		    (test_bit(i + 32, common->keymap) ||
+		     test_bit(i + 64 + 32, common->keymap)))
+			continue; /* At least one part of TKIP key allocated */
+
+		/* Found a free slot for a TKIP key */
+		return i;
+	}
+	return -1;
+}
+
+static int ath_reserve_key_cache_slot(struct ath_common *common,
+				      u32 cipher)
+{
+	int i;
+
+	if (cipher == WLAN_CIPHER_SUITE_TKIP)
+		return ath_reserve_key_cache_slot_tkip(common);
+
+	/* First, try to find slots that would not be available for TKIP. */
+	if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) {
+		for (i = IEEE80211_WEP_NKID; i < common->keymax / 4; i++) {
+			if (!test_bit(i, common->keymap) &&
+			    (test_bit(i + 32, common->keymap) ||
+			     test_bit(i + 64, common->keymap) ||
+			     test_bit(i + 64 + 32, common->keymap)))
+				return i;
+			if (!test_bit(i + 32, common->keymap) &&
+			    (test_bit(i, common->keymap) ||
+			     test_bit(i + 64, common->keymap) ||
+			     test_bit(i + 64 + 32, common->keymap)))
+				return i + 32;
+			if (!test_bit(i + 64, common->keymap) &&
+			    (test_bit(i , common->keymap) ||
+			     test_bit(i + 32, common->keymap) ||
+			     test_bit(i + 64 + 32, common->keymap)))
+				return i + 64;
+			if (!test_bit(i + 64 + 32, common->keymap) &&
+			    (test_bit(i, common->keymap) ||
+			     test_bit(i + 32, common->keymap) ||
+			     test_bit(i + 64, common->keymap)))
+				return i + 64 + 32;
+		}
+	} else {
+		for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) {
+			if (!test_bit(i, common->keymap) &&
+			    test_bit(i + 64, common->keymap))
+				return i;
+			if (test_bit(i, common->keymap) &&
+			    !test_bit(i + 64, common->keymap))
+				return i + 64;
+		}
+	}
+
+	/* No partially used TKIP slots, pick any available slot */
+	for (i = IEEE80211_WEP_NKID; i < common->keymax; i++) {
+		/* Do not allow slots that could be needed for TKIP group keys
+		 * to be used. This limitation could be removed if we know that
+		 * TKIP will not be used. */
+		if (i >= 64 && i < 64 + IEEE80211_WEP_NKID)
+			continue;
+		if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) {
+			if (i >= 32 && i < 32 + IEEE80211_WEP_NKID)
+				continue;
+			if (i >= 64 + 32 && i < 64 + 32 + IEEE80211_WEP_NKID)
+				continue;
+		}
+
+		if (!test_bit(i, common->keymap))
+			return i; /* Found a free slot for a key */
+	}
+
+	/* No free slot found */
+	return -1;
+}
+
+/*
+ * Configure encryption in the HW.
+ */
+int ath_key_config(struct ath_common *common,
+			  struct ieee80211_vif *vif,
+			  struct ieee80211_sta *sta,
+			  struct ieee80211_key_conf *key)
+{
+	struct ath_keyval hk;
+	const u8 *mac = NULL;
+	u8 gmac[ETH_ALEN];
+	int ret = 0;
+	int idx;
+
+	memset(&hk, 0, sizeof(hk));
+
+	switch (key->cipher) {
+	case WLAN_CIPHER_SUITE_WEP40:
+	case WLAN_CIPHER_SUITE_WEP104:
+		hk.kv_type = ATH_CIPHER_WEP;
+		break;
+	case WLAN_CIPHER_SUITE_TKIP:
+		hk.kv_type = ATH_CIPHER_TKIP;
+		break;
+	case WLAN_CIPHER_SUITE_CCMP:
+		hk.kv_type = ATH_CIPHER_AES_CCM;
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	hk.kv_len = key->keylen;
+	memcpy(hk.kv_val, key->key, key->keylen);
+
+	if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
+		switch (vif->type) {
+		case NL80211_IFTYPE_AP:
+			memcpy(gmac, vif->addr, ETH_ALEN);
+			gmac[0] |= 0x01;
+			mac = gmac;
+			idx = ath_reserve_key_cache_slot(common, key->cipher);
+			break;
+		case NL80211_IFTYPE_ADHOC:
+			if (!sta) {
+				idx = key->keyidx;
+				break;
+			}
+			memcpy(gmac, sta->addr, ETH_ALEN);
+			gmac[0] |= 0x01;
+			mac = gmac;
+			idx = ath_reserve_key_cache_slot(common, key->cipher);
+			break;
+		default:
+			idx = key->keyidx;
+			break;
+		}
+	} else if (key->keyidx) {
+		if (WARN_ON(!sta))
+			return -EOPNOTSUPP;
+		mac = sta->addr;
+
+		if (vif->type != NL80211_IFTYPE_AP) {
+			/* Only keyidx 0 should be used with unicast key, but
+			 * allow this for client mode for now. */
+			idx = key->keyidx;
+		} else
+			return -EIO;
+	} else {
+		if (WARN_ON(!sta))
+			return -EOPNOTSUPP;
+		mac = sta->addr;
+
+		idx = ath_reserve_key_cache_slot(common, key->cipher);
+	}
+
+	if (idx < 0)
+		return -ENOSPC; /* no free key cache entries */
+
+	if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
+		ret = ath_setkey_tkip(common, idx, key->key, &hk, mac,
+				      vif->type == NL80211_IFTYPE_AP);
+	else
+		ret = ath_hw_set_keycache_entry(common, idx, &hk, mac);
+
+	if (!ret)
+		return -EIO;
+
+	set_bit(idx, common->keymap);
+	if (key->cipher == WLAN_CIPHER_SUITE_TKIP) {
+		set_bit(idx + 64, common->keymap);
+		set_bit(idx, common->tkip_keymap);
+		set_bit(idx + 64, common->tkip_keymap);
+		if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) {
+			set_bit(idx + 32, common->keymap);
+			set_bit(idx + 64 + 32, common->keymap);
+			set_bit(idx + 32, common->tkip_keymap);
+			set_bit(idx + 64 + 32, common->tkip_keymap);
+		}
+	}
+
+	return idx;
+}
+EXPORT_SYMBOL(ath_key_config);
+
+/*
+ * Delete Key.
+ */
+void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf *key)
+{
+	ath_hw_keyreset(common, key->hw_key_idx);
+	if (key->hw_key_idx < IEEE80211_WEP_NKID)
+		return;
+
+	clear_bit(key->hw_key_idx, common->keymap);
+	if (key->cipher != WLAN_CIPHER_SUITE_TKIP)
+		return;
+
+	clear_bit(key->hw_key_idx + 64, common->keymap);
+
+	clear_bit(key->hw_key_idx, common->tkip_keymap);
+	clear_bit(key->hw_key_idx + 64, common->tkip_keymap);
+
+	if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) {
+		ath_hw_keyreset(common, key->hw_key_idx + 32);
+		clear_bit(key->hw_key_idx + 32, common->keymap);
+		clear_bit(key->hw_key_idx + 64 + 32, common->keymap);
+
+		clear_bit(key->hw_key_idx + 32, common->tkip_keymap);
+		clear_bit(key->hw_key_idx + 64 + 32, common->tkip_keymap);
+	}
+}
+EXPORT_SYMBOL(ath_key_delete);
diff --git a/drivers/net/wireless/ath/reg.h b/drivers/net/wireless/ath/reg.h
index dfe1fbe..e798ef4 100644
--- a/drivers/net/wireless/ath/reg.h
+++ b/drivers/net/wireless/ath/reg.h
@@ -24,4 +24,27 @@
 #define AR_BSSMSKL		0x80e0
 #define AR_BSSMSKU		0x80e4
 
+#define AR_KEYTABLE_0           0x8800
+#define AR_KEYTABLE(_n)         (AR_KEYTABLE_0 + ((_n)*32))
+#define AR_KEY_CACHE_SIZE       128
+#define AR_RSVD_KEYTABLE_ENTRIES 4
+#define AR_KEY_TYPE             0x00000007
+#define AR_KEYTABLE_TYPE_40     0x00000000
+#define AR_KEYTABLE_TYPE_104    0x00000001
+#define AR_KEYTABLE_TYPE_128    0x00000003
+#define AR_KEYTABLE_TYPE_TKIP   0x00000004
+#define AR_KEYTABLE_TYPE_AES    0x00000005
+#define AR_KEYTABLE_TYPE_CCM    0x00000006
+#define AR_KEYTABLE_TYPE_CLR    0x00000007
+#define AR_KEYTABLE_ANT         0x00000008
+#define AR_KEYTABLE_VALID       0x00008000
+#define AR_KEYTABLE_KEY0(_n)    (AR_KEYTABLE(_n) + 0)
+#define AR_KEYTABLE_KEY1(_n)    (AR_KEYTABLE(_n) + 4)
+#define AR_KEYTABLE_KEY2(_n)    (AR_KEYTABLE(_n) + 8)
+#define AR_KEYTABLE_KEY3(_n)    (AR_KEYTABLE(_n) + 12)
+#define AR_KEYTABLE_KEY4(_n)    (AR_KEYTABLE(_n) + 16)
+#define AR_KEYTABLE_TYPE(_n)    (AR_KEYTABLE(_n) + 20)
+#define AR_KEYTABLE_MAC0(_n)    (AR_KEYTABLE(_n) + 24)
+#define AR_KEYTABLE_MAC1(_n)    (AR_KEYTABLE(_n) + 28)
+
 #endif /* ATH_REGISTERS_H */
diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c
index 674fb93..56ef4ed 100644
--- a/drivers/net/wireless/iwlwifi/iwl-1000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-1000.c
@@ -223,6 +223,7 @@
 		.tx_stats_read = iwl_ucode_tx_stats_read,
 		.general_stats_read = iwl_ucode_general_stats_read,
 		.bt_stats_read = iwl_ucode_bt_stats_read,
+		.reply_tx_error = iwl_reply_tx_error_read,
 	},
 	.recover_from_tx_stall = iwl_bg_monitor_recover,
 	.check_plcp_health = iwl_good_plcp_health,
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.h b/drivers/net/wireless/iwlwifi/iwl-3945.h
index bb2aeeb..98509c5 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945.h
+++ b/drivers/net/wireless/iwlwifi/iwl-3945.h
@@ -295,7 +295,7 @@
 extern int iwl3945_rs_next_rate(struct iwl_priv *priv, int rate);
 
 /* scanning */
-void iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif);
+int iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif);
 
 /* Requires full declaration of iwl_priv before including */
 #include "iwl-io.h"
diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c
index 1d6a46d..943a9c7 100644
--- a/drivers/net/wireless/iwlwifi/iwl-4965.c
+++ b/drivers/net/wireless/iwlwifi/iwl-4965.c
@@ -2289,6 +2289,7 @@
 		.tx_stats_read = iwl_ucode_tx_stats_read,
 		.general_stats_read = iwl_ucode_general_stats_read,
 		.bt_stats_read = iwl_ucode_bt_stats_read,
+		.reply_tx_error = iwl_reply_tx_error_read,
 	},
 	.recover_from_tx_stall = iwl_bg_monitor_recover,
 	.check_plcp_health = iwl_good_plcp_health,
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c
index 1dbb124..21b4b23 100644
--- a/drivers/net/wireless/iwlwifi/iwl-5000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-5000.c
@@ -404,6 +404,7 @@
 		.tx_stats_read = iwl_ucode_tx_stats_read,
 		.general_stats_read = iwl_ucode_general_stats_read,
 		.bt_stats_read = iwl_ucode_bt_stats_read,
+		.reply_tx_error = iwl_reply_tx_error_read,
 	},
 	.recover_from_tx_stall = iwl_bg_monitor_recover,
 	.check_plcp_health = iwl_good_plcp_health,
@@ -474,6 +475,8 @@
 		.rx_stats_read = iwl_ucode_rx_stats_read,
 		.tx_stats_read = iwl_ucode_tx_stats_read,
 		.general_stats_read = iwl_ucode_general_stats_read,
+		.bt_stats_read = iwl_ucode_bt_stats_read,
+		.reply_tx_error = iwl_reply_tx_error_read,
 	},
 	.recover_from_tx_stall = iwl_bg_monitor_recover,
 	.check_plcp_health = iwl_good_plcp_health,
diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c
index 2fdba08..9f43f27 100644
--- a/drivers/net/wireless/iwlwifi/iwl-6000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-6000.c
@@ -329,6 +329,7 @@
 		.tx_stats_read = iwl_ucode_tx_stats_read,
 		.general_stats_read = iwl_ucode_general_stats_read,
 		.bt_stats_read = iwl_ucode_bt_stats_read,
+		.reply_tx_error = iwl_reply_tx_error_read,
 	},
 	.recover_from_tx_stall = iwl_bg_monitor_recover,
 	.check_plcp_health = iwl_good_plcp_health,
@@ -404,6 +405,7 @@
 		.tx_stats_read = iwl_ucode_tx_stats_read,
 		.general_stats_read = iwl_ucode_general_stats_read,
 		.bt_stats_read = iwl_ucode_bt_stats_read,
+		.reply_tx_error = iwl_reply_tx_error_read,
 	},
 	.recover_from_tx_stall = iwl_bg_monitor_recover,
 	.check_plcp_health = iwl_good_plcp_health,
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-agn-debugfs.c
index d706b8a..5391b46 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-debugfs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-debugfs.c
@@ -25,9 +25,15 @@
 *  Intel Linux Wireless <ilw@linux.intel.com>
 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
 *****************************************************************************/
-
+#include "iwl-agn.h"
 #include "iwl-agn-debugfs.h"
 
+static const char *fmt_value = "  %-30s %10u\n";
+static const char *fmt_hex   = "  %-30s       0x%02X\n";
+static const char *fmt_table = "  %-30s %10u  %10u  %10u  %10u\n";
+static const char *fmt_header =
+	"%-32s    current  cumulative       delta         max\n";
+
 static int iwl_statistics_flag(struct iwl_priv *priv, char *buf, int bufsz)
 {
 	int p = 0;
@@ -121,436 +127,380 @@
 	}
 
 	pos += iwl_statistics_flag(priv, buf, bufsz);
-	pos += scnprintf(buf + pos, bufsz - pos, "%-32s     current"
-			 "acumulative       delta         max\n",
-			 "Statistics_Rx - OFDM:");
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "ina_cnt:", le32_to_cpu(ofdm->ina_cnt),
+			 fmt_header, "Statistics_Rx - OFDM:");
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 fmt_table, "ina_cnt:",
+			 le32_to_cpu(ofdm->ina_cnt),
 			 accum_ofdm->ina_cnt,
 			 delta_ofdm->ina_cnt, max_ofdm->ina_cnt);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "fina_cnt:",
+			 fmt_table, "fina_cnt:",
 			 le32_to_cpu(ofdm->fina_cnt), accum_ofdm->fina_cnt,
 			 delta_ofdm->fina_cnt, max_ofdm->fina_cnt);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "plcp_err:",
+			 fmt_table, "plcp_err:",
 			 le32_to_cpu(ofdm->plcp_err), accum_ofdm->plcp_err,
 			 delta_ofdm->plcp_err, max_ofdm->plcp_err);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n", "crc32_err:",
+			 fmt_table, "crc32_err:",
 			 le32_to_cpu(ofdm->crc32_err), accum_ofdm->crc32_err,
 			 delta_ofdm->crc32_err, max_ofdm->crc32_err);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n", "overrun_err:",
+			 fmt_table, "overrun_err:",
 			 le32_to_cpu(ofdm->overrun_err),
 			 accum_ofdm->overrun_err, delta_ofdm->overrun_err,
 			 max_ofdm->overrun_err);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "early_overrun_err:",
+			 fmt_table, "early_overrun_err:",
 			 le32_to_cpu(ofdm->early_overrun_err),
 			 accum_ofdm->early_overrun_err,
 			 delta_ofdm->early_overrun_err,
 			 max_ofdm->early_overrun_err);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "crc32_good:", le32_to_cpu(ofdm->crc32_good),
+			 fmt_table, "crc32_good:",
+			 le32_to_cpu(ofdm->crc32_good),
 			 accum_ofdm->crc32_good, delta_ofdm->crc32_good,
 			 max_ofdm->crc32_good);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n", "false_alarm_cnt:",
+			 fmt_table, "false_alarm_cnt:",
 			 le32_to_cpu(ofdm->false_alarm_cnt),
 			 accum_ofdm->false_alarm_cnt,
 			 delta_ofdm->false_alarm_cnt,
 			 max_ofdm->false_alarm_cnt);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "fina_sync_err_cnt:",
+			 fmt_table, "fina_sync_err_cnt:",
 			 le32_to_cpu(ofdm->fina_sync_err_cnt),
 			 accum_ofdm->fina_sync_err_cnt,
 			 delta_ofdm->fina_sync_err_cnt,
 			 max_ofdm->fina_sync_err_cnt);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n", "sfd_timeout:",
+			 fmt_table, "sfd_timeout:",
 			 le32_to_cpu(ofdm->sfd_timeout),
 			 accum_ofdm->sfd_timeout, delta_ofdm->sfd_timeout,
 			 max_ofdm->sfd_timeout);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n", "fina_timeout:",
+			 fmt_table, "fina_timeout:",
 			 le32_to_cpu(ofdm->fina_timeout),
 			 accum_ofdm->fina_timeout, delta_ofdm->fina_timeout,
 			 max_ofdm->fina_timeout);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "unresponded_rts:",
+			 fmt_table, "unresponded_rts:",
 			 le32_to_cpu(ofdm->unresponded_rts),
 			 accum_ofdm->unresponded_rts,
 			 delta_ofdm->unresponded_rts,
 			 max_ofdm->unresponded_rts);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "rxe_frame_lmt_ovrun:",
+			 fmt_table, "rxe_frame_lmt_ovrun:",
 			 le32_to_cpu(ofdm->rxe_frame_limit_overrun),
 			 accum_ofdm->rxe_frame_limit_overrun,
 			 delta_ofdm->rxe_frame_limit_overrun,
 			 max_ofdm->rxe_frame_limit_overrun);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n", "sent_ack_cnt:",
+			 fmt_table, "sent_ack_cnt:",
 			 le32_to_cpu(ofdm->sent_ack_cnt),
 			 accum_ofdm->sent_ack_cnt, delta_ofdm->sent_ack_cnt,
 			 max_ofdm->sent_ack_cnt);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n", "sent_cts_cnt:",
+			 fmt_table, "sent_cts_cnt:",
 			 le32_to_cpu(ofdm->sent_cts_cnt),
 			 accum_ofdm->sent_cts_cnt, delta_ofdm->sent_cts_cnt,
 			 max_ofdm->sent_cts_cnt);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "sent_ba_rsp_cnt:",
+			 fmt_table, "sent_ba_rsp_cnt:",
 			 le32_to_cpu(ofdm->sent_ba_rsp_cnt),
 			 accum_ofdm->sent_ba_rsp_cnt,
 			 delta_ofdm->sent_ba_rsp_cnt,
 			 max_ofdm->sent_ba_rsp_cnt);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n", "dsp_self_kill:",
+			 fmt_table, "dsp_self_kill:",
 			 le32_to_cpu(ofdm->dsp_self_kill),
 			 accum_ofdm->dsp_self_kill,
 			 delta_ofdm->dsp_self_kill,
 			 max_ofdm->dsp_self_kill);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "mh_format_err:",
+			 fmt_table, "mh_format_err:",
 			 le32_to_cpu(ofdm->mh_format_err),
 			 accum_ofdm->mh_format_err,
 			 delta_ofdm->mh_format_err,
 			 max_ofdm->mh_format_err);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "re_acq_main_rssi_sum:",
+			 fmt_table, "re_acq_main_rssi_sum:",
 			 le32_to_cpu(ofdm->re_acq_main_rssi_sum),
 			 accum_ofdm->re_acq_main_rssi_sum,
 			 delta_ofdm->re_acq_main_rssi_sum,
 			 max_ofdm->re_acq_main_rssi_sum);
 
-	pos += scnprintf(buf + pos, bufsz - pos, "%-32s     current"
-			 "acumulative       delta         max\n",
-			 "Statistics_Rx - CCK:");
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "ina_cnt:",
+			 fmt_header, "Statistics_Rx - CCK:");
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 fmt_table, "ina_cnt:",
 			 le32_to_cpu(cck->ina_cnt), accum_cck->ina_cnt,
 			 delta_cck->ina_cnt, max_cck->ina_cnt);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "fina_cnt:",
+			 fmt_table, "fina_cnt:",
 			 le32_to_cpu(cck->fina_cnt), accum_cck->fina_cnt,
 			 delta_cck->fina_cnt, max_cck->fina_cnt);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "plcp_err:",
+			 fmt_table, "plcp_err:",
 			 le32_to_cpu(cck->plcp_err), accum_cck->plcp_err,
 			 delta_cck->plcp_err, max_cck->plcp_err);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "crc32_err:",
+			 fmt_table, "crc32_err:",
 			 le32_to_cpu(cck->crc32_err), accum_cck->crc32_err,
 			 delta_cck->crc32_err, max_cck->crc32_err);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "overrun_err:",
+			 fmt_table, "overrun_err:",
 			 le32_to_cpu(cck->overrun_err),
 			 accum_cck->overrun_err, delta_cck->overrun_err,
 			 max_cck->overrun_err);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "early_overrun_err:",
+			 fmt_table, "early_overrun_err:",
 			 le32_to_cpu(cck->early_overrun_err),
 			 accum_cck->early_overrun_err,
 			 delta_cck->early_overrun_err,
 			 max_cck->early_overrun_err);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "crc32_good:",
+			 fmt_table, "crc32_good:",
 			 le32_to_cpu(cck->crc32_good), accum_cck->crc32_good,
 			 delta_cck->crc32_good, max_cck->crc32_good);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "false_alarm_cnt:",
+			 fmt_table, "false_alarm_cnt:",
 			 le32_to_cpu(cck->false_alarm_cnt),
 			 accum_cck->false_alarm_cnt,
 			 delta_cck->false_alarm_cnt, max_cck->false_alarm_cnt);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "fina_sync_err_cnt:",
+			 fmt_table, "fina_sync_err_cnt:",
 			 le32_to_cpu(cck->fina_sync_err_cnt),
 			 accum_cck->fina_sync_err_cnt,
 			 delta_cck->fina_sync_err_cnt,
 			 max_cck->fina_sync_err_cnt);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "sfd_timeout:",
+			 fmt_table, "sfd_timeout:",
 			 le32_to_cpu(cck->sfd_timeout),
 			 accum_cck->sfd_timeout, delta_cck->sfd_timeout,
 			 max_cck->sfd_timeout);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n", "fina_timeout:",
+			 fmt_table, "fina_timeout:",
 			 le32_to_cpu(cck->fina_timeout),
 			 accum_cck->fina_timeout, delta_cck->fina_timeout,
 			 max_cck->fina_timeout);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "unresponded_rts:",
+			 fmt_table, "unresponded_rts:",
 			 le32_to_cpu(cck->unresponded_rts),
 			 accum_cck->unresponded_rts, delta_cck->unresponded_rts,
 			 max_cck->unresponded_rts);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "rxe_frame_lmt_ovrun:",
+			 fmt_table, "rxe_frame_lmt_ovrun:",
 			 le32_to_cpu(cck->rxe_frame_limit_overrun),
 			 accum_cck->rxe_frame_limit_overrun,
 			 delta_cck->rxe_frame_limit_overrun,
 			 max_cck->rxe_frame_limit_overrun);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n", "sent_ack_cnt:",
+			 fmt_table, "sent_ack_cnt:",
 			 le32_to_cpu(cck->sent_ack_cnt),
 			 accum_cck->sent_ack_cnt, delta_cck->sent_ack_cnt,
 			 max_cck->sent_ack_cnt);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n", "sent_cts_cnt:",
+			 fmt_table, "sent_cts_cnt:",
 			 le32_to_cpu(cck->sent_cts_cnt),
 			 accum_cck->sent_cts_cnt, delta_cck->sent_cts_cnt,
 			 max_cck->sent_cts_cnt);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n", "sent_ba_rsp_cnt:",
+			 fmt_table, "sent_ba_rsp_cnt:",
 			 le32_to_cpu(cck->sent_ba_rsp_cnt),
 			 accum_cck->sent_ba_rsp_cnt,
 			 delta_cck->sent_ba_rsp_cnt,
 			 max_cck->sent_ba_rsp_cnt);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n", "dsp_self_kill:",
+			 fmt_table, "dsp_self_kill:",
 			 le32_to_cpu(cck->dsp_self_kill),
 			 accum_cck->dsp_self_kill, delta_cck->dsp_self_kill,
 			 max_cck->dsp_self_kill);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n", "mh_format_err:",
+			 fmt_table, "mh_format_err:",
 			 le32_to_cpu(cck->mh_format_err),
 			 accum_cck->mh_format_err, delta_cck->mh_format_err,
 			 max_cck->mh_format_err);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "re_acq_main_rssi_sum:",
+			 fmt_table, "re_acq_main_rssi_sum:",
 			 le32_to_cpu(cck->re_acq_main_rssi_sum),
 			 accum_cck->re_acq_main_rssi_sum,
 			 delta_cck->re_acq_main_rssi_sum,
 			 max_cck->re_acq_main_rssi_sum);
 
-	pos += scnprintf(buf + pos, bufsz - pos, "%-32s     current"
-			 "acumulative       delta         max\n",
-			 "Statistics_Rx - GENERAL:");
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n", "bogus_cts:",
+			 fmt_header, "Statistics_Rx - GENERAL:");
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 fmt_table, "bogus_cts:",
 			 le32_to_cpu(general->bogus_cts),
 			 accum_general->bogus_cts, delta_general->bogus_cts,
 			 max_general->bogus_cts);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n", "bogus_ack:",
+			 fmt_table, "bogus_ack:",
 			 le32_to_cpu(general->bogus_ack),
 			 accum_general->bogus_ack, delta_general->bogus_ack,
 			 max_general->bogus_ack);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "non_bssid_frames:",
+			 fmt_table, "non_bssid_frames:",
 			 le32_to_cpu(general->non_bssid_frames),
 			 accum_general->non_bssid_frames,
 			 delta_general->non_bssid_frames,
 			 max_general->non_bssid_frames);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "filtered_frames:",
+			 fmt_table, "filtered_frames:",
 			 le32_to_cpu(general->filtered_frames),
 			 accum_general->filtered_frames,
 			 delta_general->filtered_frames,
 			 max_general->filtered_frames);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "non_channel_beacons:",
+			 fmt_table, "non_channel_beacons:",
 			 le32_to_cpu(general->non_channel_beacons),
 			 accum_general->non_channel_beacons,
 			 delta_general->non_channel_beacons,
 			 max_general->non_channel_beacons);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "channel_beacons:",
+			 fmt_table, "channel_beacons:",
 			 le32_to_cpu(general->channel_beacons),
 			 accum_general->channel_beacons,
 			 delta_general->channel_beacons,
 			 max_general->channel_beacons);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "num_missed_bcon:",
+			 fmt_table, "num_missed_bcon:",
 			 le32_to_cpu(general->num_missed_bcon),
 			 accum_general->num_missed_bcon,
 			 delta_general->num_missed_bcon,
 			 max_general->num_missed_bcon);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "adc_rx_saturation_time:",
+			 fmt_table, "adc_rx_saturation_time:",
 			 le32_to_cpu(general->adc_rx_saturation_time),
 			 accum_general->adc_rx_saturation_time,
 			 delta_general->adc_rx_saturation_time,
 			 max_general->adc_rx_saturation_time);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "ina_detect_search_tm:",
+			 fmt_table, "ina_detect_search_tm:",
 			 le32_to_cpu(general->ina_detection_search_time),
 			 accum_general->ina_detection_search_time,
 			 delta_general->ina_detection_search_time,
 			 max_general->ina_detection_search_time);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "beacon_silence_rssi_a:",
+			 fmt_table, "beacon_silence_rssi_a:",
 			 le32_to_cpu(general->beacon_silence_rssi_a),
 			 accum_general->beacon_silence_rssi_a,
 			 delta_general->beacon_silence_rssi_a,
 			 max_general->beacon_silence_rssi_a);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "beacon_silence_rssi_b:",
+			 fmt_table, "beacon_silence_rssi_b:",
 			 le32_to_cpu(general->beacon_silence_rssi_b),
 			 accum_general->beacon_silence_rssi_b,
 			 delta_general->beacon_silence_rssi_b,
 			 max_general->beacon_silence_rssi_b);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "beacon_silence_rssi_c:",
+			 fmt_table, "beacon_silence_rssi_c:",
 			 le32_to_cpu(general->beacon_silence_rssi_c),
 			 accum_general->beacon_silence_rssi_c,
 			 delta_general->beacon_silence_rssi_c,
 			 max_general->beacon_silence_rssi_c);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "interference_data_flag:",
+			 fmt_table, "interference_data_flag:",
 			 le32_to_cpu(general->interference_data_flag),
 			 accum_general->interference_data_flag,
 			 delta_general->interference_data_flag,
 			 max_general->interference_data_flag);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "channel_load:",
+			 fmt_table, "channel_load:",
 			 le32_to_cpu(general->channel_load),
 			 accum_general->channel_load,
 			 delta_general->channel_load,
 			 max_general->channel_load);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "dsp_false_alarms:",
+			 fmt_table, "dsp_false_alarms:",
 			 le32_to_cpu(general->dsp_false_alarms),
 			 accum_general->dsp_false_alarms,
 			 delta_general->dsp_false_alarms,
 			 max_general->dsp_false_alarms);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "beacon_rssi_a:",
+			 fmt_table, "beacon_rssi_a:",
 			 le32_to_cpu(general->beacon_rssi_a),
 			 accum_general->beacon_rssi_a,
 			 delta_general->beacon_rssi_a,
 			 max_general->beacon_rssi_a);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "beacon_rssi_b:",
+			 fmt_table, "beacon_rssi_b:",
 			 le32_to_cpu(general->beacon_rssi_b),
 			 accum_general->beacon_rssi_b,
 			 delta_general->beacon_rssi_b,
 			 max_general->beacon_rssi_b);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "beacon_rssi_c:",
+			 fmt_table, "beacon_rssi_c:",
 			 le32_to_cpu(general->beacon_rssi_c),
 			 accum_general->beacon_rssi_c,
 			 delta_general->beacon_rssi_c,
 			 max_general->beacon_rssi_c);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "beacon_energy_a:",
+			 fmt_table, "beacon_energy_a:",
 			 le32_to_cpu(general->beacon_energy_a),
 			 accum_general->beacon_energy_a,
 			 delta_general->beacon_energy_a,
 			 max_general->beacon_energy_a);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "beacon_energy_b:",
+			 fmt_table, "beacon_energy_b:",
 			 le32_to_cpu(general->beacon_energy_b),
 			 accum_general->beacon_energy_b,
 			 delta_general->beacon_energy_b,
 			 max_general->beacon_energy_b);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "beacon_energy_c:",
+			 fmt_table, "beacon_energy_c:",
 			 le32_to_cpu(general->beacon_energy_c),
 			 accum_general->beacon_energy_c,
 			 delta_general->beacon_energy_c,
 			 max_general->beacon_energy_c);
 
-	pos += scnprintf(buf + pos, bufsz - pos, "Statistics_Rx - OFDM_HT:\n");
-	pos += scnprintf(buf + pos, bufsz - pos, "%-32s     current"
-			 "acumulative       delta         max\n",
-			 "Statistics_Rx - OFDM_HT:");
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "plcp_err:",
+			 fmt_header, "Statistics_Rx - OFDM_HT:");
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 fmt_table, "plcp_err:",
 			 le32_to_cpu(ht->plcp_err), accum_ht->plcp_err,
 			 delta_ht->plcp_err, max_ht->plcp_err);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "overrun_err:",
+			 fmt_table, "overrun_err:",
 			 le32_to_cpu(ht->overrun_err), accum_ht->overrun_err,
 			 delta_ht->overrun_err, max_ht->overrun_err);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "early_overrun_err:",
+			 fmt_table, "early_overrun_err:",
 			 le32_to_cpu(ht->early_overrun_err),
 			 accum_ht->early_overrun_err,
 			 delta_ht->early_overrun_err,
 			 max_ht->early_overrun_err);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "crc32_good:",
+			 fmt_table, "crc32_good:",
 			 le32_to_cpu(ht->crc32_good), accum_ht->crc32_good,
 			 delta_ht->crc32_good, max_ht->crc32_good);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "crc32_err:",
+			 fmt_table, "crc32_err:",
 			 le32_to_cpu(ht->crc32_err), accum_ht->crc32_err,
 			 delta_ht->crc32_err, max_ht->crc32_err);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "mh_format_err:",
+			 fmt_table, "mh_format_err:",
 			 le32_to_cpu(ht->mh_format_err),
 			 accum_ht->mh_format_err,
 			 delta_ht->mh_format_err, max_ht->mh_format_err);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "agg_crc32_good:",
+			 fmt_table, "agg_crc32_good:",
 			 le32_to_cpu(ht->agg_crc32_good),
 			 accum_ht->agg_crc32_good,
 			 delta_ht->agg_crc32_good, max_ht->agg_crc32_good);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "agg_mpdu_cnt:",
+			 fmt_table, "agg_mpdu_cnt:",
 			 le32_to_cpu(ht->agg_mpdu_cnt),
 			 accum_ht->agg_mpdu_cnt,
 			 delta_ht->agg_mpdu_cnt, max_ht->agg_mpdu_cnt);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "agg_cnt:",
+			 fmt_table, "agg_cnt:",
 			 le32_to_cpu(ht->agg_cnt), accum_ht->agg_cnt,
 			 delta_ht->agg_cnt, max_ht->agg_cnt);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "unsupport_mcs:",
+			 fmt_table, "unsupport_mcs:",
 			 le32_to_cpu(ht->unsupport_mcs),
 			 accum_ht->unsupport_mcs,
 			 delta_ht->unsupport_mcs, max_ht->unsupport_mcs);
@@ -597,166 +547,141 @@
 	}
 
 	pos += iwl_statistics_flag(priv, buf, bufsz);
-	pos += scnprintf(buf + pos, bufsz - pos,  "%-32s     current"
-			 "acumulative       delta         max\n",
-			 "Statistics_Tx:");
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "preamble:",
+			 fmt_header, "Statistics_Tx:");
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 fmt_table, "preamble:",
 			 le32_to_cpu(tx->preamble_cnt),
 			 accum_tx->preamble_cnt,
 			 delta_tx->preamble_cnt, max_tx->preamble_cnt);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "rx_detected_cnt:",
+			 fmt_table, "rx_detected_cnt:",
 			 le32_to_cpu(tx->rx_detected_cnt),
 			 accum_tx->rx_detected_cnt,
 			 delta_tx->rx_detected_cnt, max_tx->rx_detected_cnt);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "bt_prio_defer_cnt:",
+			 fmt_table, "bt_prio_defer_cnt:",
 			 le32_to_cpu(tx->bt_prio_defer_cnt),
 			 accum_tx->bt_prio_defer_cnt,
 			 delta_tx->bt_prio_defer_cnt,
 			 max_tx->bt_prio_defer_cnt);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "bt_prio_kill_cnt:",
+			 fmt_table, "bt_prio_kill_cnt:",
 			 le32_to_cpu(tx->bt_prio_kill_cnt),
 			 accum_tx->bt_prio_kill_cnt,
 			 delta_tx->bt_prio_kill_cnt,
 			 max_tx->bt_prio_kill_cnt);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "few_bytes_cnt:",
+			 fmt_table, "few_bytes_cnt:",
 			 le32_to_cpu(tx->few_bytes_cnt),
 			 accum_tx->few_bytes_cnt,
 			 delta_tx->few_bytes_cnt, max_tx->few_bytes_cnt);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "cts_timeout:",
+			 fmt_table, "cts_timeout:",
 			 le32_to_cpu(tx->cts_timeout), accum_tx->cts_timeout,
 			 delta_tx->cts_timeout, max_tx->cts_timeout);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "ack_timeout:",
+			 fmt_table, "ack_timeout:",
 			 le32_to_cpu(tx->ack_timeout),
 			 accum_tx->ack_timeout,
 			 delta_tx->ack_timeout, max_tx->ack_timeout);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "expected_ack_cnt:",
+			 fmt_table, "expected_ack_cnt:",
 			 le32_to_cpu(tx->expected_ack_cnt),
 			 accum_tx->expected_ack_cnt,
 			 delta_tx->expected_ack_cnt,
 			 max_tx->expected_ack_cnt);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "actual_ack_cnt:",
+			 fmt_table, "actual_ack_cnt:",
 			 le32_to_cpu(tx->actual_ack_cnt),
 			 accum_tx->actual_ack_cnt,
 			 delta_tx->actual_ack_cnt,
 			 max_tx->actual_ack_cnt);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "dump_msdu_cnt:",
+			 fmt_table, "dump_msdu_cnt:",
 			 le32_to_cpu(tx->dump_msdu_cnt),
 			 accum_tx->dump_msdu_cnt,
 			 delta_tx->dump_msdu_cnt,
 			 max_tx->dump_msdu_cnt);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "abort_nxt_frame_mismatch:",
+			 fmt_table, "abort_nxt_frame_mismatch:",
 			 le32_to_cpu(tx->burst_abort_next_frame_mismatch_cnt),
 			 accum_tx->burst_abort_next_frame_mismatch_cnt,
 			 delta_tx->burst_abort_next_frame_mismatch_cnt,
 			 max_tx->burst_abort_next_frame_mismatch_cnt);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "abort_missing_nxt_frame:",
+			 fmt_table, "abort_missing_nxt_frame:",
 			 le32_to_cpu(tx->burst_abort_missing_next_frame_cnt),
 			 accum_tx->burst_abort_missing_next_frame_cnt,
 			 delta_tx->burst_abort_missing_next_frame_cnt,
 			 max_tx->burst_abort_missing_next_frame_cnt);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "cts_timeout_collision:",
+			 fmt_table, "cts_timeout_collision:",
 			 le32_to_cpu(tx->cts_timeout_collision),
 			 accum_tx->cts_timeout_collision,
 			 delta_tx->cts_timeout_collision,
 			 max_tx->cts_timeout_collision);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "ack_ba_timeout_collision:",
+			 fmt_table, "ack_ba_timeout_collision:",
 			 le32_to_cpu(tx->ack_or_ba_timeout_collision),
 			 accum_tx->ack_or_ba_timeout_collision,
 			 delta_tx->ack_or_ba_timeout_collision,
 			 max_tx->ack_or_ba_timeout_collision);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "agg ba_timeout:",
+			 fmt_table, "agg ba_timeout:",
 			 le32_to_cpu(tx->agg.ba_timeout),
 			 accum_tx->agg.ba_timeout,
 			 delta_tx->agg.ba_timeout,
 			 max_tx->agg.ba_timeout);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "agg ba_resched_frames:",
+			 fmt_table, "agg ba_resched_frames:",
 			 le32_to_cpu(tx->agg.ba_reschedule_frames),
 			 accum_tx->agg.ba_reschedule_frames,
 			 delta_tx->agg.ba_reschedule_frames,
 			 max_tx->agg.ba_reschedule_frames);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "agg scd_query_agg_frame:",
+			 fmt_table, "agg scd_query_agg_frame:",
 			 le32_to_cpu(tx->agg.scd_query_agg_frame_cnt),
 			 accum_tx->agg.scd_query_agg_frame_cnt,
 			 delta_tx->agg.scd_query_agg_frame_cnt,
 			 max_tx->agg.scd_query_agg_frame_cnt);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "agg scd_query_no_agg:",
+			 fmt_table, "agg scd_query_no_agg:",
 			 le32_to_cpu(tx->agg.scd_query_no_agg),
 			 accum_tx->agg.scd_query_no_agg,
 			 delta_tx->agg.scd_query_no_agg,
 			 max_tx->agg.scd_query_no_agg);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "agg scd_query_agg:",
+			 fmt_table, "agg scd_query_agg:",
 			 le32_to_cpu(tx->agg.scd_query_agg),
 			 accum_tx->agg.scd_query_agg,
 			 delta_tx->agg.scd_query_agg,
 			 max_tx->agg.scd_query_agg);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "agg scd_query_mismatch:",
+			 fmt_table, "agg scd_query_mismatch:",
 			 le32_to_cpu(tx->agg.scd_query_mismatch),
 			 accum_tx->agg.scd_query_mismatch,
 			 delta_tx->agg.scd_query_mismatch,
 			 max_tx->agg.scd_query_mismatch);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "agg frame_not_ready:",
+			 fmt_table, "agg frame_not_ready:",
 			 le32_to_cpu(tx->agg.frame_not_ready),
 			 accum_tx->agg.frame_not_ready,
 			 delta_tx->agg.frame_not_ready,
 			 max_tx->agg.frame_not_ready);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "agg underrun:",
+			 fmt_table, "agg underrun:",
 			 le32_to_cpu(tx->agg.underrun),
 			 accum_tx->agg.underrun,
 			 delta_tx->agg.underrun, max_tx->agg.underrun);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "agg bt_prio_kill:",
+			 fmt_table, "agg bt_prio_kill:",
 			 le32_to_cpu(tx->agg.bt_prio_kill),
 			 accum_tx->agg.bt_prio_kill,
 			 delta_tx->agg.bt_prio_kill,
 			 max_tx->agg.bt_prio_kill);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "agg rx_ba_rsp_cnt:",
+			 fmt_table, "agg rx_ba_rsp_cnt:",
 			 le32_to_cpu(tx->agg.rx_ba_rsp_cnt),
 			 accum_tx->agg.rx_ba_rsp_cnt,
 			 delta_tx->agg.rx_ba_rsp_cnt,
@@ -767,15 +692,15 @@
 			"tx power: (1/2 dB step)\n");
 		if ((priv->cfg->valid_tx_ant & ANT_A) && tx->tx_power.ant_a)
 			pos += scnprintf(buf + pos, bufsz - pos,
-					"\tantenna A: 0x%X\n",
+					fmt_hex, "antenna A:",
 					tx->tx_power.ant_a);
 		if ((priv->cfg->valid_tx_ant & ANT_B) && tx->tx_power.ant_b)
 			pos += scnprintf(buf + pos, bufsz - pos,
-					"\tantenna B: 0x%X\n",
+					fmt_hex, "antenna B:",
 					tx->tx_power.ant_b);
 		if ((priv->cfg->valid_tx_ant & ANT_C) && tx->tx_power.ant_c)
 			pos += scnprintf(buf + pos, bufsz - pos,
-					"\tantenna C: 0x%X\n",
+					fmt_hex, "antenna C:",
 					tx->tx_power.ant_c);
 	}
 	ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
@@ -838,84 +763,72 @@
 	}
 
 	pos += iwl_statistics_flag(priv, buf, bufsz);
-	pos += scnprintf(buf + pos, bufsz - pos, "%-32s     current"
-			 "acumulative       delta         max\n",
-			 "Statistics_General:");
-	pos += scnprintf(buf + pos, bufsz - pos, "  %-30s %10u\n",
-			 "temperature:",
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 fmt_header, "Statistics_General:");
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 fmt_value, "temperature:",
 			 le32_to_cpu(general->temperature));
-	pos += scnprintf(buf + pos, bufsz - pos, "  %-30s %10u\n",
-			 "temperature_m:",
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 fmt_value, "temperature_m:",
 			 le32_to_cpu(general->temperature_m));
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "burst_check:",
+			 fmt_value, "ttl_timestamp:",
+			 le32_to_cpu(general->ttl_timestamp));
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 fmt_table, "burst_check:",
 			 le32_to_cpu(dbg->burst_check),
 			 accum_dbg->burst_check,
 			 delta_dbg->burst_check, max_dbg->burst_check);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "burst_count:",
+			 fmt_table, "burst_count:",
 			 le32_to_cpu(dbg->burst_count),
 			 accum_dbg->burst_count,
 			 delta_dbg->burst_count, max_dbg->burst_count);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "wait_for_silence_timeout_count:",
+			 fmt_table, "wait_for_silence_timeout_count:",
 			 le32_to_cpu(dbg->wait_for_silence_timeout_cnt),
 			 accum_dbg->wait_for_silence_timeout_cnt,
 			 delta_dbg->wait_for_silence_timeout_cnt,
 			 max_dbg->wait_for_silence_timeout_cnt);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "sleep_time:",
+			 fmt_table, "sleep_time:",
 			 le32_to_cpu(general->sleep_time),
 			 accum_general->sleep_time,
 			 delta_general->sleep_time, max_general->sleep_time);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "slots_out:",
+			 fmt_table, "slots_out:",
 			 le32_to_cpu(general->slots_out),
 			 accum_general->slots_out,
 			 delta_general->slots_out, max_general->slots_out);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "slots_idle:",
+			 fmt_table, "slots_idle:",
 			 le32_to_cpu(general->slots_idle),
 			 accum_general->slots_idle,
 			 delta_general->slots_idle, max_general->slots_idle);
-	pos += scnprintf(buf + pos, bufsz - pos, "ttl_timestamp:\t\t\t%u\n",
-			 le32_to_cpu(general->ttl_timestamp));
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "tx_on_a:",
+			 fmt_table, "tx_on_a:",
 			 le32_to_cpu(div->tx_on_a), accum_div->tx_on_a,
 			 delta_div->tx_on_a, max_div->tx_on_a);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "tx_on_b:",
+			 fmt_table, "tx_on_b:",
 			 le32_to_cpu(div->tx_on_b), accum_div->tx_on_b,
 			 delta_div->tx_on_b, max_div->tx_on_b);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "exec_time:",
+			 fmt_table, "exec_time:",
 			 le32_to_cpu(div->exec_time), accum_div->exec_time,
 			 delta_div->exec_time, max_div->exec_time);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "probe_time:",
+			 fmt_table, "probe_time:",
 			 le32_to_cpu(div->probe_time), accum_div->probe_time,
 			 delta_div->probe_time, max_div->probe_time);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "rx_enable_counter:",
+			 fmt_table, "rx_enable_counter:",
 			 le32_to_cpu(general->rx_enable_counter),
 			 accum_general->rx_enable_counter,
 			 delta_general->rx_enable_counter,
 			 max_general->rx_enable_counter);
 	pos += scnprintf(buf + pos, bufsz - pos,
-			 "  %-30s %10u  %10u  %10u  %10u\n",
-			 "num_of_sos_states:",
+			 fmt_table, "num_of_sos_states:",
 			 le32_to_cpu(general->num_of_sos_states),
 			 accum_general->num_of_sos_states,
 			 delta_general->num_of_sos_states,
@@ -1011,3 +924,147 @@
 	kfree(buf);
 	return ret;
 }
+
+ssize_t iwl_reply_tx_error_read(struct file *file,
+				char __user *user_buf,
+				size_t count, loff_t *ppos)
+{
+	struct iwl_priv *priv = (struct iwl_priv *)file->private_data;
+	int pos = 0;
+	char *buf;
+	int bufsz = (sizeof(struct reply_tx_error_statistics) * 24) +
+		(sizeof(struct reply_agg_tx_error_statistics) * 24) + 200;
+	ssize_t ret;
+
+	if (!iwl_is_alive(priv))
+		return -EAGAIN;
+
+	buf = kzalloc(bufsz, GFP_KERNEL);
+	if (!buf) {
+		IWL_ERR(priv, "Can not allocate Buffer\n");
+		return -ENOMEM;
+	}
+
+	pos += scnprintf(buf + pos, bufsz - pos, "Statistics_TX_Error:\n");
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t\t%u\n",
+			 iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_DELAY),
+			 priv->_agn.reply_tx_stats.pp_delay);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+			 iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_FEW_BYTES),
+			 priv->_agn.reply_tx_stats.pp_few_bytes);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+			 iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_BT_PRIO),
+			 priv->_agn.reply_tx_stats.pp_bt_prio);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+			 iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_QUIET_PERIOD),
+			 priv->_agn.reply_tx_stats.pp_quiet_period);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+			 iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_CALC_TTAK),
+			 priv->_agn.reply_tx_stats.pp_calc_ttak);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
+			 iwl_get_tx_fail_reason(
+				TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY),
+			 priv->_agn.reply_tx_stats.int_crossed_retry);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+			 iwl_get_tx_fail_reason(TX_STATUS_FAIL_SHORT_LIMIT),
+			 priv->_agn.reply_tx_stats.short_limit);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+			 iwl_get_tx_fail_reason(TX_STATUS_FAIL_LONG_LIMIT),
+			 priv->_agn.reply_tx_stats.long_limit);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+			 iwl_get_tx_fail_reason(TX_STATUS_FAIL_FIFO_UNDERRUN),
+			 priv->_agn.reply_tx_stats.fifo_underrun);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+			 iwl_get_tx_fail_reason(TX_STATUS_FAIL_DRAIN_FLOW),
+			 priv->_agn.reply_tx_stats.drain_flow);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+			 iwl_get_tx_fail_reason(TX_STATUS_FAIL_RFKILL_FLUSH),
+			 priv->_agn.reply_tx_stats.rfkill_flush);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+			 iwl_get_tx_fail_reason(TX_STATUS_FAIL_LIFE_EXPIRE),
+			 priv->_agn.reply_tx_stats.life_expire);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+			 iwl_get_tx_fail_reason(TX_STATUS_FAIL_DEST_PS),
+			 priv->_agn.reply_tx_stats.dest_ps);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+			 iwl_get_tx_fail_reason(TX_STATUS_FAIL_HOST_ABORTED),
+			 priv->_agn.reply_tx_stats.host_abort);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+			 iwl_get_tx_fail_reason(TX_STATUS_FAIL_BT_RETRY),
+			 priv->_agn.reply_tx_stats.pp_delay);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+			 iwl_get_tx_fail_reason(TX_STATUS_FAIL_STA_INVALID),
+			 priv->_agn.reply_tx_stats.sta_invalid);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+			 iwl_get_tx_fail_reason(TX_STATUS_FAIL_FRAG_DROPPED),
+			 priv->_agn.reply_tx_stats.frag_drop);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+			 iwl_get_tx_fail_reason(TX_STATUS_FAIL_TID_DISABLE),
+			 priv->_agn.reply_tx_stats.tid_disable);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+			 iwl_get_tx_fail_reason(TX_STATUS_FAIL_FIFO_FLUSHED),
+			 priv->_agn.reply_tx_stats.fifo_flush);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
+			 iwl_get_tx_fail_reason(
+				TX_STATUS_FAIL_INSUFFICIENT_CF_POLL),
+			 priv->_agn.reply_tx_stats.insuff_cf_poll);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+			 iwl_get_tx_fail_reason(TX_STATUS_FAIL_PASSIVE_NO_RX),
+			 priv->_agn.reply_tx_stats.fail_hw_drop);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
+			 iwl_get_tx_fail_reason(
+				TX_STATUS_FAIL_NO_BEACON_ON_RADAR),
+			 priv->_agn.reply_tx_stats.sta_color_mismatch);
+	pos += scnprintf(buf + pos, bufsz - pos, "UNKNOWN:\t\t\t%u\n",
+			 priv->_agn.reply_tx_stats.unknown);
+
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "\nStatistics_Agg_TX_Error:\n");
+
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+			 iwl_get_agg_tx_fail_reason(AGG_TX_STATE_UNDERRUN_MSK),
+			 priv->_agn.reply_agg_tx_stats.underrun);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+			 iwl_get_agg_tx_fail_reason(AGG_TX_STATE_BT_PRIO_MSK),
+			 priv->_agn.reply_agg_tx_stats.bt_prio);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+			 iwl_get_agg_tx_fail_reason(AGG_TX_STATE_FEW_BYTES_MSK),
+			 priv->_agn.reply_agg_tx_stats.few_bytes);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+			 iwl_get_agg_tx_fail_reason(AGG_TX_STATE_ABORT_MSK),
+			 priv->_agn.reply_agg_tx_stats.abort);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
+			 iwl_get_agg_tx_fail_reason(
+				AGG_TX_STATE_LAST_SENT_TTL_MSK),
+			 priv->_agn.reply_agg_tx_stats.last_sent_ttl);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
+			 iwl_get_agg_tx_fail_reason(
+				AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK),
+			 priv->_agn.reply_agg_tx_stats.last_sent_try);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
+			 iwl_get_agg_tx_fail_reason(
+				AGG_TX_STATE_LAST_SENT_BT_KILL_MSK),
+			 priv->_agn.reply_agg_tx_stats.last_sent_bt_kill);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+			 iwl_get_agg_tx_fail_reason(AGG_TX_STATE_SCD_QUERY_MSK),
+			 priv->_agn.reply_agg_tx_stats.scd_query);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
+			 iwl_get_agg_tx_fail_reason(
+				AGG_TX_STATE_TEST_BAD_CRC32_MSK),
+			 priv->_agn.reply_agg_tx_stats.bad_crc32);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+			 iwl_get_agg_tx_fail_reason(AGG_TX_STATE_RESPONSE_MSK),
+			 priv->_agn.reply_agg_tx_stats.response);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+			 iwl_get_agg_tx_fail_reason(AGG_TX_STATE_DUMP_TX_MSK),
+			 priv->_agn.reply_agg_tx_stats.dump_tx);
+	pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+			 iwl_get_agg_tx_fail_reason(AGG_TX_STATE_DELAY_TX_MSK),
+			 priv->_agn.reply_agg_tx_stats.delay_tx);
+	pos += scnprintf(buf + pos, bufsz - pos, "UNKNOWN:\t\t\t%u\n",
+			 priv->_agn.reply_agg_tx_stats.unknown);
+
+	ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+	kfree(buf);
+	return ret;
+}
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-debugfs.h b/drivers/net/wireless/iwlwifi/iwl-agn-debugfs.h
index bbdce59..f2573b5 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-debugfs.h
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-debugfs.h
@@ -39,6 +39,8 @@
 				     size_t count, loff_t *ppos);
 ssize_t iwl_ucode_bt_stats_read(struct file *file, char __user *user_buf,
 				size_t count, loff_t *ppos);
+ssize_t iwl_reply_tx_error_read(struct file *file, char __user *user_buf,
+				size_t count, loff_t *ppos);
 #else
 static ssize_t iwl_ucode_rx_stats_read(struct file *file, char __user *user_buf,
 				       size_t count, loff_t *ppos)
@@ -60,4 +62,9 @@
 {
 	return 0;
 }
+static ssize_t iwl_reply_tx_error_read(struct file *file, char __user *user_buf,
+				       size_t count, loff_t *ppos)
+{
+	return 0;
+}
 #endif
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-hcmd.c b/drivers/net/wireless/iwlwifi/iwl-agn-hcmd.c
index 6fb52ab..d86902b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-hcmd.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-hcmd.c
@@ -287,6 +287,15 @@
 	ctx_bss = &priv->contexts[IWL_RXON_CTX_BSS];
 	ctx_pan = &priv->contexts[IWL_RXON_CTX_PAN];
 
+	/*
+	 * If the PAN context is inactive, then we don't need
+	 * to update the PAN parameters, the last thing we'll
+	 * have done before it goes inactive is making the PAN
+	 * parameters be WLAN-only.
+	 */
+	if (!ctx_pan->is_active)
+		return 0;
+
 	memset(&cmd, 0, sizeof(cmd));
 
 	/* only 2 slots are currently allowed */
@@ -312,7 +321,7 @@
 			bcnint = max_t(int, bcnint,
 				       ctx_bss->vif->bss_conf.beacon_int);
 		if (!bcnint)
-			bcnint = 100;
+			bcnint = DEFAULT_BEACON_INTERVAL;
 		slot0 = bcnint / 2;
 		slot1 = bcnint - slot0;
 
@@ -330,7 +339,12 @@
 		slot0 = 0;
 		slot1 = max_t(int, 1, ctx_pan->vif->bss_conf.dtim_period) *
 					ctx_pan->vif->bss_conf.beacon_int;
-		slot1 = max_t(int, 100, slot1);
+		slot1 = max_t(int, DEFAULT_BEACON_INTERVAL, slot1);
+
+		if (test_bit(STATUS_SCAN_HW, &priv->status)) {
+			slot0 = slot1 * 3 - 20;
+			slot1 = 20;
+		}
 	}
 
 	cmd.slots[0].width = cpu_to_le16(slot0);
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
index a8f2adf..299fd9d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
@@ -46,6 +46,181 @@
 			    tx_resp->frame_count) & MAX_SN;
 }
 
+static void iwlagn_count_tx_err_status(struct iwl_priv *priv, u16 status)
+{
+	status &= TX_STATUS_MSK;
+
+	switch (status) {
+	case TX_STATUS_POSTPONE_DELAY:
+		priv->_agn.reply_tx_stats.pp_delay++;
+		break;
+	case TX_STATUS_POSTPONE_FEW_BYTES:
+		priv->_agn.reply_tx_stats.pp_few_bytes++;
+		break;
+	case TX_STATUS_POSTPONE_BT_PRIO:
+		priv->_agn.reply_tx_stats.pp_bt_prio++;
+		break;
+	case TX_STATUS_POSTPONE_QUIET_PERIOD:
+		priv->_agn.reply_tx_stats.pp_quiet_period++;
+		break;
+	case TX_STATUS_POSTPONE_CALC_TTAK:
+		priv->_agn.reply_tx_stats.pp_calc_ttak++;
+		break;
+	case TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY:
+		priv->_agn.reply_tx_stats.int_crossed_retry++;
+		break;
+	case TX_STATUS_FAIL_SHORT_LIMIT:
+		priv->_agn.reply_tx_stats.short_limit++;
+		break;
+	case TX_STATUS_FAIL_LONG_LIMIT:
+		priv->_agn.reply_tx_stats.long_limit++;
+		break;
+	case TX_STATUS_FAIL_FIFO_UNDERRUN:
+		priv->_agn.reply_tx_stats.fifo_underrun++;
+		break;
+	case TX_STATUS_FAIL_DRAIN_FLOW:
+		priv->_agn.reply_tx_stats.drain_flow++;
+		break;
+	case TX_STATUS_FAIL_RFKILL_FLUSH:
+		priv->_agn.reply_tx_stats.rfkill_flush++;
+		break;
+	case TX_STATUS_FAIL_LIFE_EXPIRE:
+		priv->_agn.reply_tx_stats.life_expire++;
+		break;
+	case TX_STATUS_FAIL_DEST_PS:
+		priv->_agn.reply_tx_stats.dest_ps++;
+		break;
+	case TX_STATUS_FAIL_HOST_ABORTED:
+		priv->_agn.reply_tx_stats.host_abort++;
+		break;
+	case TX_STATUS_FAIL_BT_RETRY:
+		priv->_agn.reply_tx_stats.bt_retry++;
+		break;
+	case TX_STATUS_FAIL_STA_INVALID:
+		priv->_agn.reply_tx_stats.sta_invalid++;
+		break;
+	case TX_STATUS_FAIL_FRAG_DROPPED:
+		priv->_agn.reply_tx_stats.frag_drop++;
+		break;
+	case TX_STATUS_FAIL_TID_DISABLE:
+		priv->_agn.reply_tx_stats.tid_disable++;
+		break;
+	case TX_STATUS_FAIL_FIFO_FLUSHED:
+		priv->_agn.reply_tx_stats.fifo_flush++;
+		break;
+	case TX_STATUS_FAIL_INSUFFICIENT_CF_POLL:
+		priv->_agn.reply_tx_stats.insuff_cf_poll++;
+		break;
+	case TX_STATUS_FAIL_PASSIVE_NO_RX:
+		priv->_agn.reply_tx_stats.fail_hw_drop++;
+		break;
+	case TX_STATUS_FAIL_NO_BEACON_ON_RADAR:
+		priv->_agn.reply_tx_stats.sta_color_mismatch++;
+		break;
+	default:
+		priv->_agn.reply_tx_stats.unknown++;
+		break;
+	}
+}
+
+static void iwlagn_count_agg_tx_err_status(struct iwl_priv *priv, u16 status)
+{
+	status &= AGG_TX_STATUS_MSK;
+
+	switch (status) {
+	case AGG_TX_STATE_UNDERRUN_MSK:
+		priv->_agn.reply_agg_tx_stats.underrun++;
+		break;
+	case AGG_TX_STATE_BT_PRIO_MSK:
+		priv->_agn.reply_agg_tx_stats.bt_prio++;
+		break;
+	case AGG_TX_STATE_FEW_BYTES_MSK:
+		priv->_agn.reply_agg_tx_stats.few_bytes++;
+		break;
+	case AGG_TX_STATE_ABORT_MSK:
+		priv->_agn.reply_agg_tx_stats.abort++;
+		break;
+	case AGG_TX_STATE_LAST_SENT_TTL_MSK:
+		priv->_agn.reply_agg_tx_stats.last_sent_ttl++;
+		break;
+	case AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK:
+		priv->_agn.reply_agg_tx_stats.last_sent_try++;
+		break;
+	case AGG_TX_STATE_LAST_SENT_BT_KILL_MSK:
+		priv->_agn.reply_agg_tx_stats.last_sent_bt_kill++;
+		break;
+	case AGG_TX_STATE_SCD_QUERY_MSK:
+		priv->_agn.reply_agg_tx_stats.scd_query++;
+		break;
+	case AGG_TX_STATE_TEST_BAD_CRC32_MSK:
+		priv->_agn.reply_agg_tx_stats.bad_crc32++;
+		break;
+	case AGG_TX_STATE_RESPONSE_MSK:
+		priv->_agn.reply_agg_tx_stats.response++;
+		break;
+	case AGG_TX_STATE_DUMP_TX_MSK:
+		priv->_agn.reply_agg_tx_stats.dump_tx++;
+		break;
+	case AGG_TX_STATE_DELAY_TX_MSK:
+		priv->_agn.reply_agg_tx_stats.delay_tx++;
+		break;
+	default:
+		priv->_agn.reply_agg_tx_stats.unknown++;
+		break;
+	}
+}
+
+static void iwlagn_set_tx_status(struct iwl_priv *priv,
+				 struct ieee80211_tx_info *info,
+				 struct iwl5000_tx_resp *tx_resp,
+				 int txq_id, bool is_agg)
+{
+	u16  status = le16_to_cpu(tx_resp->status.status);
+
+	info->status.rates[0].count = tx_resp->failure_frame + 1;
+	if (is_agg)
+		info->flags &= ~IEEE80211_TX_CTL_AMPDU;
+	info->flags |= iwl_tx_status_to_mac80211(status);
+	iwlagn_hwrate_to_tx_control(priv, le32_to_cpu(tx_resp->rate_n_flags),
+				    info);
+	if (!iwl_is_tx_success(status))
+		iwlagn_count_tx_err_status(priv, status);
+
+	IWL_DEBUG_TX_REPLY(priv, "TXQ %d status %s (0x%08x) rate_n_flags "
+			   "0x%x retries %d\n",
+			   txq_id,
+			   iwl_get_tx_fail_reason(status), status,
+			   le32_to_cpu(tx_resp->rate_n_flags),
+			   tx_resp->failure_frame);
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+#define AGG_TX_STATE_FAIL(x) case AGG_TX_STATE_ ## x: return #x
+
+const char *iwl_get_agg_tx_fail_reason(u16 status)
+{
+	status &= AGG_TX_STATUS_MSK;
+	switch (status) {
+	case AGG_TX_STATE_TRANSMITTED:
+		return "SUCCESS";
+		AGG_TX_STATE_FAIL(UNDERRUN_MSK);
+		AGG_TX_STATE_FAIL(BT_PRIO_MSK);
+		AGG_TX_STATE_FAIL(FEW_BYTES_MSK);
+		AGG_TX_STATE_FAIL(ABORT_MSK);
+		AGG_TX_STATE_FAIL(LAST_SENT_TTL_MSK);
+		AGG_TX_STATE_FAIL(LAST_SENT_TRY_CNT_MSK);
+		AGG_TX_STATE_FAIL(LAST_SENT_BT_KILL_MSK);
+		AGG_TX_STATE_FAIL(SCD_QUERY_MSK);
+		AGG_TX_STATE_FAIL(TEST_BAD_CRC32_MSK);
+		AGG_TX_STATE_FAIL(RESPONSE_MSK);
+		AGG_TX_STATE_FAIL(DUMP_TX_MSK);
+		AGG_TX_STATE_FAIL(DELAY_TX_MSK);
+	}
+
+	return "UNKNOWN";
+}
+#endif /* CONFIG_IWLWIFI_DEBUG */
+
 static int iwlagn_tx_status_reply_tx(struct iwl_priv *priv,
 				      struct iwl_ht_agg *agg,
 				      struct iwl5000_tx_resp *tx_resp,
@@ -53,9 +228,7 @@
 {
 	u16 status;
 	struct agg_tx_status *frame_status = &tx_resp->status;
-	struct ieee80211_tx_info *info = NULL;
 	struct ieee80211_hdr *hdr = NULL;
-	u32 rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags);
 	int i, sh, idx;
 	u16 seq;
 
@@ -64,31 +237,20 @@
 
 	agg->frame_count = tx_resp->frame_count;
 	agg->start_idx = start_idx;
-	agg->rate_n_flags = rate_n_flags;
+	agg->rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags);
 	agg->bitmap = 0;
 
 	/* # frames attempted by Tx command */
 	if (agg->frame_count == 1) {
 		/* Only one frame was attempted; no block-ack will arrive */
-		status = le16_to_cpu(frame_status[0].status);
 		idx = start_idx;
 
-		/* FIXME: code repetition */
 		IWL_DEBUG_TX_REPLY(priv, "FrameCnt = %d, StartIdx=%d idx=%d\n",
 				   agg->frame_count, agg->start_idx, idx);
-
-		info = IEEE80211_SKB_CB(priv->txq[txq_id].txb[idx].skb);
-		info->status.rates[0].count = tx_resp->failure_frame + 1;
-		info->flags &= ~IEEE80211_TX_CTL_AMPDU;
-		info->flags |= iwl_tx_status_to_mac80211(status);
-		iwlagn_hwrate_to_tx_control(priv, rate_n_flags, info);
-
-		/* FIXME: code repetition end */
-
-		IWL_DEBUG_TX_REPLY(priv, "1 Frame 0x%x failure :%d\n",
-				    status & 0xff, tx_resp->failure_frame);
-		IWL_DEBUG_TX_REPLY(priv, "Rate Info rate_n_flags=%x\n", rate_n_flags);
-
+		iwlagn_set_tx_status(priv,
+				     IEEE80211_SKB_CB(
+					priv->txq[txq_id].txb[idx].skb),
+				     tx_resp, txq_id, true);
 		agg->wait_for_ba = 0;
 	} else {
 		/* Two or more frames were attempted; expect block-ack */
@@ -109,12 +271,20 @@
 			idx = SEQ_TO_INDEX(seq);
 			txq_id = SEQ_TO_QUEUE(seq);
 
+			if (status & AGG_TX_STATUS_MSK)
+				iwlagn_count_agg_tx_err_status(priv, status);
+
 			if (status & (AGG_TX_STATE_FEW_BYTES_MSK |
 				      AGG_TX_STATE_ABORT_MSK))
 				continue;
 
 			IWL_DEBUG_TX_REPLY(priv, "FrameCnt = %d, txq_id=%d idx=%d\n",
 					   agg->frame_count, txq_id, idx);
+			IWL_DEBUG_TX_REPLY(priv, "status %s (0x%08x), "
+					   "try-count (0x%08x)\n",
+					   iwl_get_agg_tx_fail_reason(status),
+					   status & AGG_TX_STATUS_MSK,
+					   status & AGG_TX_TRY_MSK);
 
 			hdr = iwl_tx_queue_get_hdr(priv, txq_id, idx);
 			if (!hdr) {
@@ -281,20 +451,7 @@
 		}
 	} else {
 		BUG_ON(txq_id != txq->swq_id);
-
-		info->status.rates[0].count = tx_resp->failure_frame + 1;
-		info->flags |= iwl_tx_status_to_mac80211(status);
-		iwlagn_hwrate_to_tx_control(priv,
-					le32_to_cpu(tx_resp->rate_n_flags),
-					info);
-
-		IWL_DEBUG_TX_REPLY(priv, "TXQ %d status %s (0x%08x) rate_n_flags "
-				   "0x%x retries %d\n",
-				   txq_id,
-				   iwl_get_tx_fail_reason(status), status,
-				   le32_to_cpu(tx_resp->rate_n_flags),
-				   tx_resp->failure_frame);
-
+		iwlagn_set_tx_status(priv, info, tx_resp, txq_id, false);
 		freed = iwlagn_tx_queue_reclaim(priv, txq_id, index);
 		iwl_free_tfds_in_queue(priv, sta_id, tid, freed);
 
@@ -1154,7 +1311,7 @@
 	return added;
 }
 
-void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
+int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
 {
 	struct iwl_host_cmd cmd = {
 		.id = REPLY_SCAN_CMD,
@@ -1162,7 +1319,6 @@
 		.flags = CMD_SIZE_HUGE,
 	};
 	struct iwl_scan_cmd *scan;
-	struct ieee80211_conf *conf = NULL;
 	struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
 	u32 rate_flags = 0;
 	u16 cmd_len;
@@ -1175,59 +1331,20 @@
 	int  chan_mod;
 	u8 active_chains;
 	u8 scan_tx_antennas = priv->hw_params.valid_tx_ant;
+	int ret;
+
+	lockdep_assert_held(&priv->mutex);
 
 	if (vif)
 		ctx = iwl_rxon_ctx_from_vif(vif);
 
-	conf = ieee80211_get_hw_conf(priv->hw);
-
-	cancel_delayed_work(&priv->scan_check);
-
-	if (!iwl_is_ready(priv)) {
-		IWL_WARN(priv, "request scan called when driver not ready.\n");
-		goto done;
-	}
-
-	/* Make sure the scan wasn't canceled before this queued work
-	 * was given the chance to run... */
-	if (!test_bit(STATUS_SCANNING, &priv->status))
-		goto done;
-
-	/* This should never be called or scheduled if there is currently
-	 * a scan active in the hardware. */
-	if (test_bit(STATUS_SCAN_HW, &priv->status)) {
-		IWL_DEBUG_INFO(priv, "Multiple concurrent scan requests in parallel. "
-			       "Ignoring second request.\n");
-		goto done;
-	}
-
-	if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
-		IWL_DEBUG_SCAN(priv, "Aborting scan due to device shutdown\n");
-		goto done;
-	}
-
-	if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
-		IWL_DEBUG_HC(priv, "Scan request while abort pending.  Queuing.\n");
-		goto done;
-	}
-
-	if (iwl_is_rfkill(priv)) {
-		IWL_DEBUG_HC(priv, "Aborting scan due to RF Kill activation\n");
-		goto done;
-	}
-
-	if (!test_bit(STATUS_READY, &priv->status)) {
-		IWL_DEBUG_HC(priv, "Scan request while uninitialized.  Queuing.\n");
-		goto done;
-	}
-
 	if (!priv->scan_cmd) {
 		priv->scan_cmd = kmalloc(sizeof(struct iwl_scan_cmd) +
 					 IWL_MAX_SCAN_SIZE, GFP_KERNEL);
 		if (!priv->scan_cmd) {
 			IWL_DEBUG_SCAN(priv,
 				       "fail to allocate memory for scan\n");
-			goto done;
+			return -ENOMEM;
 		}
 	}
 	scan = priv->scan_cmd;
@@ -1334,8 +1451,8 @@
 						IWL_GOOD_CRC_TH_NEVER;
 		break;
 	default:
-		IWL_WARN(priv, "Invalid scan band count\n");
-		goto done;
+		IWL_WARN(priv, "Invalid scan band\n");
+		return -EIO;
 	}
 
 	band = priv->scan_band;
@@ -1415,7 +1532,7 @@
 	}
 	if (scan->channel_count == 0) {
 		IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count);
-		goto done;
+		return -EIO;
 	}
 
 	cmd.len += le16_to_cpu(scan->tx_cmd.len) +
@@ -1423,30 +1540,21 @@
 	cmd.data = scan;
 	scan->len = cpu_to_le16(cmd.len);
 
+	if (priv->cfg->ops->hcmd->set_pan_params) {
+		ret = priv->cfg->ops->hcmd->set_pan_params(priv);
+		if (ret)
+			return ret;
+	}
+
 	set_bit(STATUS_SCAN_HW, &priv->status);
+	ret = iwl_send_cmd_sync(priv, &cmd);
+	if (ret) {
+		clear_bit(STATUS_SCAN_HW, &priv->status);
+		if (priv->cfg->ops->hcmd->set_pan_params)
+			priv->cfg->ops->hcmd->set_pan_params(priv);
+	}
 
-	if (priv->cfg->ops->hcmd->set_pan_params &&
-	    priv->cfg->ops->hcmd->set_pan_params(priv))
-		goto done;
-
-	if (iwl_send_cmd_sync(priv, &cmd))
-		goto done;
-
-	queue_delayed_work(priv->workqueue, &priv->scan_check,
-			   IWL_SCAN_CHECK_WATCHDOG);
-
-	return;
-
- done:
-	/* Cannot perform scan. Make sure we clear scanning
-	* bits from status so next scan request can be performed.
-	* If we don't clear scanning status bit here all next scan
-	* will fail
-	*/
-	clear_bit(STATUS_SCAN_HW, &priv->status);
-	clear_bit(STATUS_SCANNING, &priv->status);
-	/* inform mac80211 scan aborted */
-	queue_work(priv->workqueue, &priv->scan_completed);
+	return ret;
 }
 
 int iwlagn_manage_ibss_station(struct iwl_priv *priv,
@@ -1673,6 +1781,8 @@
 	bt_cmd.kill_ack_mask = priv->kill_ack_mask;
 	bt_cmd.kill_cts_mask = priv->kill_cts_mask;
 	bt_cmd.valid = priv->bt_valid;
+	bt_cmd.tx_prio_boost = 0;
+	bt_cmd.rx_prio_boost = 0;
 
 	/*
 	 * Configure BT coex mode to "no coexistence" when the
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.h b/drivers/net/wireless/iwlwifi/iwl-agn-rs.h
index 3970ab1..357cdb2 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.h
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.h
@@ -453,15 +453,6 @@
 }
 
 
-static inline u8 iwl_get_prev_ieee_rate(u8 rate_index)
-{
-	u8 rate = iwl_rates[rate_index].prev_ieee;
-
-	if (rate == IWL_RATE_INVALID)
-		rate = rate_index;
-	return rate;
-}
-
 static inline u8 iwl3945_get_prev_ieee_rate(u8 rate_index)
 {
 	u8 rate = iwl3945_rates[rate_index].prev_ieee;
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-ucode.c b/drivers/net/wireless/iwlwifi/iwl-agn-ucode.c
index a7961bf..8bfb049 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-ucode.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-ucode.c
@@ -307,6 +307,17 @@
 		goto restart;
 	}
 
+	if (priv->cfg->advanced_bt_coexist) {
+		/*
+		 * Tell uCode we are ready to perform calibration
+		 * need to perform this before any calibration
+		 * no need to close the envlope since we are going
+		 * to load the runtime uCode later.
+		 */
+		iwlagn_send_bt_env(priv, IWL_BT_COEX_ENV_OPEN,
+			BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
+
+	}
 	iwlagn_send_calib_cfg(priv);
 	return;
 
@@ -364,7 +375,7 @@
 	0, 0, 0, 0, 0, 0, 0
 };
 
-static void iwlagn_send_prio_tbl(struct iwl_priv *priv)
+void iwlagn_send_prio_tbl(struct iwl_priv *priv)
 {
 	struct iwl_bt_coex_prio_table_cmd prio_tbl_cmd;
 
@@ -375,7 +386,7 @@
 		IWL_ERR(priv, "failed to send BT prio tbl command\n");
 }
 
-static void iwlagn_send_bt_env(struct iwl_priv *priv, u8 action, u8 type)
+void iwlagn_send_bt_env(struct iwl_priv *priv, u8 action, u8 type)
 {
 	struct iwl_bt_coex_prot_env_cmd env_cmd;
 
@@ -482,25 +493,6 @@
 
 	spin_unlock_irqrestore(&priv->lock, flags);
 
-	if (priv->cfg->advanced_bt_coexist) {
-		/* Configure Bluetooth device coexistence support */
-		/* need to perform this before any calibration */
-		priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK;
-		priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT;
-		priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT;
-		priv->cfg->ops->hcmd->send_bt_config(priv);
-		priv->bt_valid = IWLAGN_BT_VALID_ENABLE_FLAGS;
-
-		if (bt_coex_active && priv->iw_mode != NL80211_IFTYPE_ADHOC) {
-			iwlagn_send_prio_tbl(priv);
-			iwlagn_send_bt_env(priv, IWL_BT_COEX_ENV_OPEN,
-				BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
-			iwlagn_send_bt_env(priv, IWL_BT_COEX_ENV_CLOSE,
-				BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
-		}
-
-	}
-
 	iwlagn_send_wimax_coex(priv);
 
 	iwlagn_set_Xtal_calib(priv);
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index ad0e67f..646864a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -110,6 +110,9 @@
 	if (!iwl_is_alive(priv))
 		return -EBUSY;
 
+	if (!ctx->is_active)
+		return 0;
+
 	/* always get timestamp with Rx frame */
 	ctx->staging.flags |= RXON_FLG_TSF2HOST_MSK;
 
@@ -223,9 +226,8 @@
 			return ret;
 		}
 	}
-
-	priv->start_calib = 0;
 	if (new_assoc) {
+		priv->start_calib = 0;
 		/* Apply the new configuration
 		 * RXON assoc doesn't clear the station table in uCode,
 		 */
@@ -369,7 +371,7 @@
 
 	if (!priv->beacon_ctx) {
 		IWL_ERR(priv, "trying to build beacon w/o beacon context!\n");
-		return -EINVAL;
+		return 0;
 	}
 
 	/* Initialize memory */
@@ -1278,7 +1280,6 @@
 		IWL_ERR(priv, "Microcode SW error detected. "
 			" Restarting 0x%X.\n", inta);
 		priv->isr_stats.sw++;
-		priv->isr_stats.sw_err = inta;
 		iwl_irq_handle_error(priv);
 		handled |= CSR_INT_BIT_SW_ERR;
 	}
@@ -1459,7 +1460,6 @@
 		IWL_ERR(priv, "Microcode SW error detected. "
 			" Restarting 0x%X.\n", inta);
 		priv->isr_stats.sw++;
-		priv->isr_stats.sw_err = inta;
 		iwl_irq_handle_error(priv);
 		handled |= CSR_INT_BIT_SW_ERR;
 	}
@@ -2467,6 +2467,7 @@
 	}
 
 	desc = iwl_read_targ_mem(priv, base + 1 * sizeof(u32));
+	priv->isr_stats.err_code = desc;
 	pc = iwl_read_targ_mem(priv, base + 2 * sizeof(u32));
 	blink1 = iwl_read_targ_mem(priv, base + 3 * sizeof(u32));
 	blink2 = iwl_read_targ_mem(priv, base + 4 * sizeof(u32));
@@ -2813,6 +2814,22 @@
 	if (iwl_is_rfkill(priv))
 		return;
 
+	if (priv->cfg->advanced_bt_coexist) {
+		/* Configure Bluetooth device coexistence support */
+		priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK;
+		priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT;
+		priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT;
+		priv->cfg->ops->hcmd->send_bt_config(priv);
+		priv->bt_valid = IWLAGN_BT_VALID_ENABLE_FLAGS;
+		if (bt_coex_active && priv->iw_mode != NL80211_IFTYPE_ADHOC)
+			iwlagn_send_prio_tbl(priv);
+
+		/* FIXME: w/a to force change uCode BT state machine */
+		iwlagn_send_bt_env(priv, IWL_BT_COEX_ENV_OPEN,
+			BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
+		iwlagn_send_bt_env(priv, IWL_BT_COEX_ENV_CLOSE,
+			BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
+	}
 	ieee80211_wake_queues(priv->hw);
 
 	priv->active_rate = IWL_RATES_MASK;
@@ -2875,8 +2892,9 @@
 
 	IWL_DEBUG_INFO(priv, DRV_NAME " is going down\n");
 
-	if (!exit_pending)
-		set_bit(STATUS_EXIT_PENDING, &priv->status);
+	iwl_scan_cancel_timeout(priv, 200);
+
+	exit_pending = test_and_set_bit(STATUS_EXIT_PENDING, &priv->status);
 
 	/* Stop TX queues watchdog. We need to have STATUS_EXIT_PENDING bit set
 	 * to prevent rearm timer */
@@ -3486,15 +3504,6 @@
 
 	priv->is_open = 0;
 
-	if (iwl_is_ready_rf(priv) || test_bit(STATUS_SCAN_HW, &priv->status)) {
-		/* stop mac, cancel any scan request and clear
-		 * RXON_FILTER_ASSOC_MSK BIT
-		 */
-		mutex_lock(&priv->mutex);
-		iwl_scan_cancel_timeout(priv, 100);
-		mutex_unlock(&priv->mutex);
-	}
-
 	iwl_down(priv);
 
 	flush_workqueue(priv->workqueue);
@@ -4062,13 +4071,15 @@
 		priv->cfg->ops->lib->cancel_deferred_work(priv);
 
 	cancel_delayed_work_sync(&priv->init_alive_start);
-	cancel_delayed_work(&priv->scan_check);
-	cancel_work_sync(&priv->start_internal_scan);
 	cancel_delayed_work(&priv->alive_start);
 	cancel_work_sync(&priv->run_time_calib_work);
 	cancel_work_sync(&priv->beacon_update);
+
+	iwl_cancel_scan_deferred_work(priv);
+
 	cancel_work_sync(&priv->bt_full_concurrency);
 	cancel_work_sync(&priv->bt_runtime_config);
+
 	del_timer_sync(&priv->statistics_periodic);
 	del_timer_sync(&priv->ucode_trace);
 }
@@ -4286,6 +4297,8 @@
 	for (i = 0; i < NUM_IWL_RXON_CTX; i++)
 		priv->contexts[i].ctxid = i;
 
+	priv->contexts[IWL_RXON_CTX_BSS].always_active = true;
+	priv->contexts[IWL_RXON_CTX_BSS].is_active = true;
 	priv->contexts[IWL_RXON_CTX_BSS].rxon_cmd = REPLY_RXON;
 	priv->contexts[IWL_RXON_CTX_BSS].rxon_timing_cmd = REPLY_RXON_TIMING;
 	priv->contexts[IWL_RXON_CTX_BSS].rxon_assoc_cmd = REPLY_RXON_ASSOC;
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.h b/drivers/net/wireless/iwlwifi/iwl-agn.h
index 7c542a8..a372184 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.h
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.h
@@ -134,6 +134,8 @@
 void iwlagn_init_alive_start(struct iwl_priv *priv);
 int iwlagn_alive_notify(struct iwl_priv *priv);
 int iwl_verify_ucode(struct iwl_priv *priv);
+void iwlagn_send_bt_env(struct iwl_priv *priv, u8 action, u8 type);
+void iwlagn_send_prio_tbl(struct iwl_priv *priv);
 
 /* lib */
 void iwl_check_abort_status(struct iwl_priv *priv,
@@ -217,7 +219,7 @@
 			  struct iwl_rx_mem_buffer *rxb);
 
 /* scan */
-void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif);
+int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif);
 
 /* station mgmt */
 int iwlagn_manage_ibss_station(struct iwl_priv *priv,
@@ -236,4 +238,9 @@
 void iwlagn_bt_setup_deferred_work(struct iwl_priv *priv);
 void iwlagn_bt_cancel_deferred_work(struct iwl_priv *priv);
 
+#ifdef CONFIG_IWLWIFI_DEBUG
+const char *iwl_get_agg_tx_fail_reason(u16 status);
+#else
+static inline const char *iwl_get_agg_tx_fail_reason(u16 status) { return ""; }
+#endif
 #endif /* __iwl_agn_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h
index 3e4ba31..27e250c 100644
--- a/drivers/net/wireless/iwlwifi/iwl-commands.h
+++ b/drivers/net/wireless/iwlwifi/iwl-commands.h
@@ -1820,13 +1820,8 @@
 	TX_STATUS_FAIL_TID_DISABLE = 0x8d,
 	TX_STATUS_FAIL_FIFO_FLUSHED = 0x8e,
 	TX_STATUS_FAIL_INSUFFICIENT_CF_POLL = 0x8f,
-	/* uCode drop due to FW drop request */
-	TX_STATUS_FAIL_FW_DROP = 0x90,
-	/*
-	 * uCode drop due to station color mismatch
-	 * between tx command and station table
-	 */
-	TX_STATUS_FAIL_STA_COLOR_MISMATCH_DROP = 0x91,
+	TX_STATUS_FAIL_PASSIVE_NO_RX = 0x90,
+	TX_STATUS_FAIL_NO_BEACON_ON_RADAR = 0x91,
 };
 
 #define	TX_PACKET_MODE_REGULAR		0x0000
@@ -1868,6 +1863,9 @@
 	AGG_TX_STATE_DELAY_TX_MSK = 0x400
 };
 
+#define AGG_TX_STATUS_MSK	0x00000fff	/* bits 0:11 */
+#define AGG_TX_TRY_MSK		0x0000f000	/* bits 12:15 */
+
 #define AGG_TX_STATE_LAST_SENT_MSK  (AGG_TX_STATE_LAST_SENT_TTL_MSK | \
 				     AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK | \
 				     AGG_TX_STATE_LAST_SENT_BT_KILL_MSK)
@@ -2488,7 +2486,12 @@
 	__le16 bt4_decision_time; /* unused */
 	__le16 valid;
 	u8 prio_boost;
-	u8 reserved[3];
+	/*
+	 * set IWLAGN_BT_VALID_BOOST to "1" in "valid" bitmask
+	 * if configure the following patterns
+	 */
+	u8 tx_prio_boost;	/* SW boost of WiFi tx priority */
+	__le16 rx_prio_boost;	/* SW boost of WiFi rx priority */
 };
 
 #define IWLAGN_BT_SCO_ACTIVE	cpu_to_le32(BIT(0))
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c
index 87a2e40..393f02d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.c
+++ b/drivers/net/wireless/iwlwifi/iwl-core.c
@@ -196,6 +196,9 @@
 	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
 		return;
 
+	if (!ctx->is_active)
+		return;
+
 	ctx->qos_data.def_qos_parm.qos_flags = 0;
 
 	if (ctx->qos_data.qos_active)
@@ -488,8 +491,29 @@
 
 static u16 iwl_adjust_beacon_interval(u16 beacon_val, u16 max_beacon_val)
 {
-	u16 new_val = 0;
-	u16 beacon_factor = 0;
+	u16 new_val;
+	u16 beacon_factor;
+
+	/*
+	 * If mac80211 hasn't given us a beacon interval, program
+	 * the default into the device (not checking this here
+	 * would cause the adjustment below to return the maximum
+	 * value, which may break PAN.)
+	 */
+	if (!beacon_val)
+		return DEFAULT_BEACON_INTERVAL;
+
+	/*
+	 * If the beacon interval we obtained from the peer
+	 * is too large, we'll have to wake up more often
+	 * (and in IBSS case, we'll beacon too much)
+	 *
+	 * For example, if max_beacon_val is 4096, and the
+	 * requested beacon interval is 7000, we'll have to
+	 * use 3500 to be able to wake up on the beacons.
+	 *
+	 * This could badly influence beacon detection stats.
+	 */
 
 	beacon_factor = (beacon_val + max_beacon_val) / max_beacon_val;
 	new_val = beacon_val / beacon_factor;
@@ -526,10 +550,22 @@
 	ctx->timing.atim_window = 0;
 
 	if (ctx->ctxid == IWL_RXON_CTX_PAN &&
-	    (!ctx->vif || ctx->vif->type != NL80211_IFTYPE_STATION)) {
+	    (!ctx->vif || ctx->vif->type != NL80211_IFTYPE_STATION) &&
+	    iwl_is_associated(priv, IWL_RXON_CTX_BSS) &&
+	    priv->contexts[IWL_RXON_CTX_BSS].vif &&
+	    priv->contexts[IWL_RXON_CTX_BSS].vif->bss_conf.beacon_int) {
 		ctx->timing.beacon_interval =
 			priv->contexts[IWL_RXON_CTX_BSS].timing.beacon_interval;
 		beacon_int = le16_to_cpu(ctx->timing.beacon_interval);
+	} else if (ctx->ctxid == IWL_RXON_CTX_BSS &&
+		   iwl_is_associated(priv, IWL_RXON_CTX_PAN) &&
+		   priv->contexts[IWL_RXON_CTX_PAN].vif &&
+		   priv->contexts[IWL_RXON_CTX_PAN].vif->bss_conf.beacon_int &&
+		   (!iwl_is_associated_ctx(ctx) || !ctx->vif ||
+		    !ctx->vif->bss_conf.beacon_int)) {
+		ctx->timing.beacon_interval =
+			priv->contexts[IWL_RXON_CTX_PAN].timing.beacon_interval;
+		beacon_int = le16_to_cpu(ctx->timing.beacon_interval);
 	} else {
 		beacon_int = iwl_adjust_beacon_interval(beacon_int,
 				priv->hw_params.max_beacon_itrvl * TIME_UNIT);
@@ -1797,9 +1833,8 @@
 		priv->ibss_beacon = ieee80211_beacon_get(hw, vif);
 	}
 
-	if (changes & BSS_CHANGED_BEACON_INT) {
-		/* TODO: in AP mode, do something to make this take effect */
-	}
+	if (changes & BSS_CHANGED_BEACON_INT && vif->type == NL80211_IFTYPE_AP)
+		iwl_send_rxon_timing(priv, ctx);
 
 	if (changes & BSS_CHANGED_BSSID) {
 		IWL_DEBUG_MAC80211(priv, "BSSID %pM\n", bss_conf->bssid);
@@ -2009,9 +2044,14 @@
 	 */
 	priv->iw_mode = vif->type;
 
+	ctx->is_active = true;
+
 	err = iwl_set_mode(priv, vif);
-	if (err)
+	if (err) {
+		if (!ctx->always_active)
+			ctx->is_active = false;
 		goto out_err;
+	}
 
 	if (priv->cfg->advanced_bt_coexist &&
 	    vif->type == NL80211_IFTYPE_ADHOC) {
@@ -2041,7 +2081,6 @@
 {
 	struct iwl_priv *priv = hw->priv;
 	struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
-	bool scan_completed = false;
 
 	IWL_DEBUG_MAC80211(priv, "enter\n");
 
@@ -2050,14 +2089,14 @@
 	WARN_ON(ctx->vif != vif);
 	ctx->vif = NULL;
 
-	iwl_scan_cancel_timeout(priv, 100);
+	if (priv->scan_vif == vif) {
+		iwl_scan_cancel_timeout(priv, 200);
+		iwl_force_scan_end(priv);
+	}
 	iwl_set_mode(priv, vif);
 
-	if (priv->scan_vif == vif) {
-		scan_completed = true;
-		priv->scan_vif = NULL;
-		priv->scan_request = NULL;
-	}
+	if (!ctx->always_active)
+		ctx->is_active = false;
 
 	/*
 	 * When removing the IBSS interface, overwrite the
@@ -2072,9 +2111,6 @@
 	memset(priv->bssid, 0, ETH_ALEN);
 	mutex_unlock(&priv->mutex);
 
-	if (scan_completed)
-		ieee80211_scan_completed(priv->hw, true);
-
 	IWL_DEBUG_MAC80211(priv, "leave\n");
 
 }
@@ -2255,6 +2291,7 @@
 
 	spin_unlock_irqrestore(&priv->lock, flags);
 
+	iwl_scan_cancel_timeout(priv, 100);
 	if (!iwl_is_ready_rf(priv)) {
 		IWL_DEBUG_MAC80211(priv, "leave - not ready\n");
 		mutex_unlock(&priv->mutex);
@@ -2264,7 +2301,6 @@
 	/* we are restarting association process
 	 * clear RXON_FILTER_ASSOC_MSK bit
 	 */
-	iwl_scan_cancel_timeout(priv, 100);
 	ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
 	iwlcore_commit_rxon(priv, ctx);
 
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h
index f7b57ed..f0302bf 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.h
+++ b/drivers/net/wireless/iwlwifi/iwl-core.h
@@ -111,7 +111,7 @@
 				  __le16 fc, __le32 *tx_flags);
 	int  (*calc_rssi)(struct iwl_priv *priv,
 			  struct iwl_rx_phy_res *rx_resp);
-	void (*request_scan)(struct iwl_priv *priv, struct ieee80211_vif *vif);
+	int (*request_scan)(struct iwl_priv *priv, struct ieee80211_vif *vif);
 };
 
 struct iwl_apm_ops {
@@ -130,6 +130,8 @@
 				      size_t count, loff_t *ppos);
 	ssize_t (*bt_stats_read)(struct file *file, char __user *user_buf,
 				 size_t count, loff_t *ppos);
+	ssize_t (*reply_tx_error)(struct file *file, char __user *user_buf,
+				 size_t count, loff_t *ppos);
 };
 
 struct iwl_temp_ops {
@@ -553,6 +555,7 @@
 void iwl_init_scan_params(struct iwl_priv *priv);
 int iwl_scan_cancel(struct iwl_priv *priv);
 int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms);
+void iwl_force_scan_end(struct iwl_priv *priv);
 int iwl_mac_hw_scan(struct ieee80211_hw *hw,
 		    struct ieee80211_vif *vif,
 		    struct cfg80211_scan_request *req);
@@ -568,6 +571,7 @@
 			       enum ieee80211_band band,
 			       struct ieee80211_vif *vif);
 void iwl_setup_scan_deferred_work(struct iwl_priv *priv);
+void iwl_cancel_scan_deferred_work(struct iwl_priv *priv);
 
 /* For faster active scanning, scan will move to the next channel if fewer than
  * PLCP_QUIET_THRESH packets are heard on this channel within
diff --git a/drivers/net/wireless/iwlwifi/iwl-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-debugfs.c
index 0ee8f51..265ad01 100644
--- a/drivers/net/wireless/iwlwifi/iwl-debugfs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-debugfs.c
@@ -575,10 +575,10 @@
 		priv->isr_stats.hw);
 	pos += scnprintf(buf + pos, bufsz - pos, "SW Error:\t\t\t %u\n",
 		priv->isr_stats.sw);
-	if (priv->isr_stats.sw > 0) {
+	if (priv->isr_stats.sw || priv->isr_stats.hw) {
 		pos += scnprintf(buf + pos, bufsz - pos,
 			"\tLast Restarting Code:  0x%X\n",
-			priv->isr_stats.sw_err);
+			priv->isr_stats.err_code);
 	}
 #ifdef CONFIG_IWLWIFI_DEBUG
 	pos += scnprintf(buf + pos, bufsz - pos, "Frame transmitted:\t\t %u\n",
@@ -1604,6 +1604,56 @@
 	return ret;
 }
 
+static ssize_t iwl_dbgfs_protection_mode_read(struct file *file,
+					char __user *user_buf,
+					size_t count, loff_t *ppos)
+{
+	struct iwl_priv *priv = (struct iwl_priv *)file->private_data;
+
+	int pos = 0;
+	char buf[40];
+	const size_t bufsz = sizeof(buf);
+
+	pos += scnprintf(buf + pos, bufsz - pos, "use %s for aggregation\n",
+			 (priv->cfg->use_rts_for_aggregation) ? "rts/cts" :
+			 "cts-to-self");
+	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_protection_mode_write(struct file *file,
+					const char __user *user_buf,
+					size_t count, loff_t *ppos) {
+
+	struct iwl_priv *priv = file->private_data;
+	char buf[8];
+	int buf_size;
+	int rts;
+
+	memset(buf, 0, sizeof(buf));
+	buf_size = min(count, sizeof(buf) -  1);
+	if (copy_from_user(buf, user_buf, buf_size))
+		return -EFAULT;
+	if (sscanf(buf, "%d", &rts) != 1)
+		return -EINVAL;
+	if (rts)
+		priv->cfg->use_rts_for_aggregation = true;
+	else
+		priv->cfg->use_rts_for_aggregation = false;
+	return count;
+}
+
+static ssize_t iwl_dbgfs_reply_tx_error_read(struct file *file,
+					char __user *user_buf,
+					size_t count, loff_t *ppos)
+{
+	struct iwl_priv *priv = file->private_data;
+
+	if (priv->cfg->ops->lib->debugfs_ops.reply_tx_error)
+		return priv->cfg->ops->lib->debugfs_ops.reply_tx_error(
+			file, user_buf, count, ppos);
+	else
+		return -ENODATA;
+}
 DEBUGFS_READ_FILE_OPS(rx_statistics);
 DEBUGFS_READ_FILE_OPS(tx_statistics);
 DEBUGFS_READ_WRITE_FILE_OPS(traffic_log);
@@ -1629,6 +1679,8 @@
 DEBUGFS_READ_FILE_OPS(ucode_bt_stats);
 DEBUGFS_WRITE_FILE_OPS(monitor_period);
 DEBUGFS_READ_FILE_OPS(bt_traffic);
+DEBUGFS_READ_WRITE_FILE_OPS(protection_mode);
+DEBUGFS_READ_FILE_OPS(reply_tx_error);
 
 /*
  * Create the debugfs files and directories
@@ -1689,6 +1741,7 @@
 	DEBUGFS_ADD_FILE(ucode_general_stats, dir_debug, S_IRUSR);
 	if (priv->cfg->ops->lib->dev_txfifo_flush)
 		DEBUGFS_ADD_FILE(txfifo_flush, dir_debug, S_IWUSR);
+	DEBUGFS_ADD_FILE(protection_mode, dir_debug, S_IWUSR | S_IRUSR);
 
 	if (priv->cfg->sensitivity_calib_by_driver)
 		DEBUGFS_ADD_FILE(sensitivity, dir_debug, S_IRUSR);
@@ -1698,6 +1751,7 @@
 		DEBUGFS_ADD_FILE(ucode_tracing, dir_debug, S_IWUSR | S_IRUSR);
 	if (priv->cfg->bt_statistics)
 		DEBUGFS_ADD_FILE(ucode_bt_stats, dir_debug, S_IRUSR);
+	DEBUGFS_ADD_FILE(reply_tx_error, dir_debug, S_IRUSR);
 	DEBUGFS_ADD_FILE(rxon_flags, dir_debug, S_IWUSR);
 	DEBUGFS_ADD_FILE(rxon_filter_flags, dir_debug, S_IWUSR);
 	DEBUGFS_ADD_FILE(monitor_period, dir_debug, S_IWUSR);
diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h
index 4dd38b7..74d25bc 100644
--- a/drivers/net/wireless/iwlwifi/iwl-dev.h
+++ b/drivers/net/wireless/iwlwifi/iwl-dev.h
@@ -945,7 +945,7 @@
 struct isr_statistics {
 	u32 hw;
 	u32 sw;
-	u32 sw_err;
+	u32 err_code;
 	u32 sch;
 	u32 alive;
 	u32 rfkill;
@@ -957,6 +957,50 @@
 	u32 unhandled;
 };
 
+/* reply_tx_statistics (for _agn devices) */
+struct reply_tx_error_statistics {
+	u32 pp_delay;
+	u32 pp_few_bytes;
+	u32 pp_bt_prio;
+	u32 pp_quiet_period;
+	u32 pp_calc_ttak;
+	u32 int_crossed_retry;
+	u32 short_limit;
+	u32 long_limit;
+	u32 fifo_underrun;
+	u32 drain_flow;
+	u32 rfkill_flush;
+	u32 life_expire;
+	u32 dest_ps;
+	u32 host_abort;
+	u32 bt_retry;
+	u32 sta_invalid;
+	u32 frag_drop;
+	u32 tid_disable;
+	u32 fifo_flush;
+	u32 insuff_cf_poll;
+	u32 fail_hw_drop;
+	u32 sta_color_mismatch;
+	u32 unknown;
+};
+
+/* reply_agg_tx_statistics (for _agn devices) */
+struct reply_agg_tx_error_statistics {
+	u32 underrun;
+	u32 bt_prio;
+	u32 few_bytes;
+	u32 abort;
+	u32 last_sent_ttl;
+	u32 last_sent_try;
+	u32 last_sent_bt_kill;
+	u32 scd_query;
+	u32 bad_crc32;
+	u32 response;
+	u32 dump_tx;
+	u32 delay_tx;
+	u32 unknown;
+};
+
 #ifdef CONFIG_IWLWIFI_DEBUGFS
 /* management statistics */
 enum iwl_mgmt_stats {
@@ -1116,6 +1160,13 @@
 	const u8 *ac_to_queue;
 	u8 mcast_queue;
 
+	/*
+	 * We could use the vif to indicate active, but we
+	 * also need it to be active during disabling when
+	 * we already removed the vif for type setting.
+	 */
+	bool always_active, is_active;
+
 	enum iwl_rxon_context_id ctxid;
 
 	u32 interface_modes, exclusive_interface_modes;
@@ -1408,6 +1459,9 @@
 
 			struct iwl_notif_statistics statistics;
 			struct iwl_bt_notif_statistics statistics_bt;
+			/* counts reply_tx error */
+			struct reply_tx_error_statistics reply_tx_stats;
+			struct reply_agg_tx_error_statistics reply_agg_tx_stats;
 #ifdef CONFIG_IWLWIFI_DEBUGFS
 			struct iwl_notif_statistics accum_statistics;
 			struct iwl_notif_statistics delta_statistics;
diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c
index 7727f09..c54c200 100644
--- a/drivers/net/wireless/iwlwifi/iwl-scan.c
+++ b/drivers/net/wireless/iwlwifi/iwl-scan.c
@@ -54,82 +54,28 @@
 #define IWL_PASSIVE_DWELL_BASE      (100)
 #define IWL_CHANNEL_TUNE_TIME       5
 
-
-
-/**
- * iwl_scan_cancel - Cancel any currently executing HW scan
- *
- * NOTE: priv->mutex is not required before calling this function
- */
-int iwl_scan_cancel(struct iwl_priv *priv)
-{
-	if (!test_bit(STATUS_SCAN_HW, &priv->status)) {
-		clear_bit(STATUS_SCANNING, &priv->status);
-		return 0;
-	}
-
-	if (test_bit(STATUS_SCANNING, &priv->status)) {
-		if (!test_and_set_bit(STATUS_SCAN_ABORTING, &priv->status)) {
-			IWL_DEBUG_SCAN(priv, "Queuing scan abort.\n");
-			queue_work(priv->workqueue, &priv->abort_scan);
-
-		} else
-			IWL_DEBUG_SCAN(priv, "Scan abort already in progress.\n");
-
-		return test_bit(STATUS_SCANNING, &priv->status);
-	}
-
-	return 0;
-}
-EXPORT_SYMBOL(iwl_scan_cancel);
-/**
- * iwl_scan_cancel_timeout - Cancel any currently executing HW scan
- * @ms: amount of time to wait (in milliseconds) for scan to abort
- *
- * NOTE: priv->mutex must be held before calling this function
- */
-int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms)
-{
-	unsigned long now = jiffies;
-	int ret;
-
-	ret = iwl_scan_cancel(priv);
-	if (ret && ms) {
-		mutex_unlock(&priv->mutex);
-		while (!time_after(jiffies, now + msecs_to_jiffies(ms)) &&
-				test_bit(STATUS_SCANNING, &priv->status))
-			msleep(1);
-		mutex_lock(&priv->mutex);
-
-		return test_bit(STATUS_SCANNING, &priv->status);
-	}
-
-	return ret;
-}
-EXPORT_SYMBOL(iwl_scan_cancel_timeout);
-
 static int iwl_send_scan_abort(struct iwl_priv *priv)
 {
-	int ret = 0;
+	int ret;
 	struct iwl_rx_packet *pkt;
 	struct iwl_host_cmd cmd = {
 		.id = REPLY_SCAN_ABORT_CMD,
 		.flags = CMD_WANT_SKB,
 	};
 
-	/* If there isn't a scan actively going on in the hardware
-	 * then we are in between scan bands and not actually
-	 * actively scanning, so don't send the abort command */
-	if (!test_bit(STATUS_SCAN_HW, &priv->status)) {
-		clear_bit(STATUS_SCAN_ABORTING, &priv->status);
-		return 0;
-	}
+	/* Exit instantly with error when device is not ready
+	 * to receive scan abort command or it does not perform
+	 * hardware scan currently */
+	if (!test_bit(STATUS_READY, &priv->status) ||
+	    !test_bit(STATUS_GEO_CONFIGURED, &priv->status) ||
+	    !test_bit(STATUS_SCAN_HW, &priv->status) ||
+	    test_bit(STATUS_FW_ERROR, &priv->status) ||
+	    test_bit(STATUS_EXIT_PENDING, &priv->status))
+		return -EIO;
 
 	ret = iwl_send_cmd_sync(priv, &cmd);
-	if (ret) {
-		clear_bit(STATUS_SCAN_ABORTING, &priv->status);
+	if (ret)
 		return ret;
-	}
 
 	pkt = (struct iwl_rx_packet *)cmd.reply_page;
 	if (pkt->u.status != CAN_ABORT_STATUS) {
@@ -139,16 +85,104 @@
 		 * can occur if we send the scan abort before we
 		 * the microcode has notified us that a scan is
 		 * completed. */
-		IWL_DEBUG_INFO(priv, "SCAN_ABORT returned %d.\n", pkt->u.status);
-		clear_bit(STATUS_SCAN_ABORTING, &priv->status);
-		clear_bit(STATUS_SCAN_HW, &priv->status);
+		IWL_DEBUG_SCAN(priv, "SCAN_ABORT ret %d.\n", pkt->u.status);
+		ret = -EIO;
 	}
 
 	iwl_free_pages(priv, cmd.reply_page);
-
 	return ret;
 }
 
+static void iwl_complete_scan(struct iwl_priv *priv, bool aborted)
+{
+	/* check if scan was requested from mac80211 */
+	if (priv->scan_request) {
+		IWL_DEBUG_SCAN(priv, "Complete scan in mac80211\n");
+		ieee80211_scan_completed(priv->hw, aborted);
+	}
+
+	priv->is_internal_short_scan = false;
+	priv->scan_vif = NULL;
+	priv->scan_request = NULL;
+}
+
+void iwl_force_scan_end(struct iwl_priv *priv)
+{
+	lockdep_assert_held(&priv->mutex);
+
+	if (!test_bit(STATUS_SCANNING, &priv->status)) {
+		IWL_DEBUG_SCAN(priv, "Forcing scan end while not scanning\n");
+		return;
+	}
+
+	IWL_DEBUG_SCAN(priv, "Forcing scan end\n");
+	clear_bit(STATUS_SCANNING, &priv->status);
+	clear_bit(STATUS_SCAN_HW, &priv->status);
+	clear_bit(STATUS_SCAN_ABORTING, &priv->status);
+	iwl_complete_scan(priv, true);
+}
+EXPORT_SYMBOL(iwl_force_scan_end);
+
+static void iwl_do_scan_abort(struct iwl_priv *priv)
+{
+	int ret;
+
+	lockdep_assert_held(&priv->mutex);
+
+	if (!test_bit(STATUS_SCANNING, &priv->status)) {
+		IWL_DEBUG_SCAN(priv, "Not performing scan to abort\n");
+		return;
+	}
+
+	if (test_and_set_bit(STATUS_SCAN_ABORTING, &priv->status)) {
+		IWL_DEBUG_SCAN(priv, "Scan abort in progress\n");
+		return;
+	}
+
+	ret = iwl_send_scan_abort(priv);
+	if (ret) {
+		IWL_DEBUG_SCAN(priv, "Send scan abort failed %d\n", ret);
+		iwl_force_scan_end(priv);
+	} else
+		IWL_DEBUG_SCAN(priv, "Sucessfully send scan abort\n");
+}
+
+/**
+ * iwl_scan_cancel - Cancel any currently executing HW scan
+ */
+int iwl_scan_cancel(struct iwl_priv *priv)
+{
+	IWL_DEBUG_SCAN(priv, "Queuing abort scan\n");
+	queue_work(priv->workqueue, &priv->abort_scan);
+	return 0;
+}
+EXPORT_SYMBOL(iwl_scan_cancel);
+
+/**
+ * iwl_scan_cancel_timeout - Cancel any currently executing HW scan
+ * @ms: amount of time to wait (in milliseconds) for scan to abort
+ *
+ */
+int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(ms);
+
+	lockdep_assert_held(&priv->mutex);
+
+	IWL_DEBUG_SCAN(priv, "Scan cancel timeout\n");
+
+	iwl_do_scan_abort(priv);
+
+	while (time_before_eq(jiffies, timeout)) {
+		if (!test_bit(STATUS_SCAN_HW, &priv->status))
+			break;
+		msleep(20);
+	}
+
+	return test_bit(STATUS_SCAN_HW, &priv->status);
+}
+EXPORT_SYMBOL(iwl_scan_cancel_timeout);
+
 /* Service response to REPLY_SCAN_CMD (0x80) */
 static void iwl_rx_reply_scan(struct iwl_priv *priv,
 			      struct iwl_rx_mem_buffer *rxb)
@@ -158,7 +192,7 @@
 	struct iwl_scanreq_notification *notif =
 	    (struct iwl_scanreq_notification *)pkt->u.raw;
 
-	IWL_DEBUG_RX(priv, "Scan request status = 0x%x\n", notif->status);
+	IWL_DEBUG_SCAN(priv, "Scan request status = 0x%x\n", notif->status);
 #endif
 }
 
@@ -217,26 +251,16 @@
 	/* The HW is no longer scanning */
 	clear_bit(STATUS_SCAN_HW, &priv->status);
 
-	IWL_DEBUG_INFO(priv, "Scan on %sGHz took %dms\n",
+	IWL_DEBUG_SCAN(priv, "Scan on %sGHz took %dms\n",
 		       (priv->scan_band == IEEE80211_BAND_2GHZ) ? "2.4" : "5.2",
 		       jiffies_to_msecs(elapsed_jiffies
 					(priv->scan_start, jiffies)));
 
-	/*
-	 * If a request to abort was given, or the scan did not succeed
-	 * then we reset the scan state machine and terminate,
-	 * re-queuing another scan if one has been requested
-	 */
-	if (test_and_clear_bit(STATUS_SCAN_ABORTING, &priv->status))
-		IWL_DEBUG_INFO(priv, "Aborted scan completed.\n");
-
-	IWL_DEBUG_INFO(priv, "Setting scan to off\n");
-
-	clear_bit(STATUS_SCANNING, &priv->status);
+	queue_work(priv->workqueue, &priv->scan_completed);
 
 	if (priv->iw_mode != NL80211_IFTYPE_ADHOC &&
-	    priv->cfg->advanced_bt_coexist && priv->bt_status !=
-	    scan_notif->bt_status) {
+	    priv->cfg->advanced_bt_coexist &&
+	    priv->bt_status != scan_notif->bt_status) {
 		if (scan_notif->bt_status) {
 			/* BT on */
 			if (!priv->bt_ch_announce)
@@ -254,7 +278,6 @@
 		priv->bt_status = scan_notif->bt_status;
 		queue_work(priv->workqueue, &priv->bt_traffic_change_work);
 	}
-	queue_work(priv->workqueue, &priv->scan_completed);
 }
 
 void iwl_setup_rx_scan_handlers(struct iwl_priv *priv)
@@ -324,19 +347,53 @@
 }
 EXPORT_SYMBOL(iwl_init_scan_params);
 
-static int iwl_scan_initiate(struct iwl_priv *priv, struct ieee80211_vif *vif)
+static int __must_check iwl_scan_initiate(struct iwl_priv *priv,
+					  struct ieee80211_vif *vif,
+					  bool internal,
+					  enum ieee80211_band band)
 {
-	lockdep_assert_held(&priv->mutex);
+	int ret;
 
-	IWL_DEBUG_INFO(priv, "Starting scan...\n");
-	set_bit(STATUS_SCANNING, &priv->status);
-	priv->is_internal_short_scan = false;
-	priv->scan_start = jiffies;
+	lockdep_assert_held(&priv->mutex);
 
 	if (WARN_ON(!priv->cfg->ops->utils->request_scan))
 		return -EOPNOTSUPP;
 
-	priv->cfg->ops->utils->request_scan(priv, vif);
+	cancel_delayed_work(&priv->scan_check);
+
+	if (!iwl_is_ready_rf(priv)) {
+		IWL_WARN(priv, "Request scan called when driver not ready.\n");
+		return -EIO;
+	}
+
+	if (test_bit(STATUS_SCAN_HW, &priv->status)) {
+		IWL_DEBUG_SCAN(priv,
+			"Multiple concurrent scan requests in parallel.\n");
+		return -EBUSY;
+	}
+
+	if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
+		IWL_DEBUG_SCAN(priv, "Scan request while abort pending.\n");
+		return -EBUSY;
+	}
+
+	IWL_DEBUG_SCAN(priv, "Starting %sscan...\n",
+			internal ? "internal short " : "");
+
+	set_bit(STATUS_SCANNING, &priv->status);
+	priv->is_internal_short_scan = internal;
+	priv->scan_start = jiffies;
+	priv->scan_band = band;
+
+	ret = priv->cfg->ops->utils->request_scan(priv, vif);
+	if (ret) {
+		clear_bit(STATUS_SCANNING, &priv->status);
+		priv->is_internal_short_scan = false;
+		return ret;
+	}
+
+	queue_delayed_work(priv->workqueue, &priv->scan_check,
+			   IWL_SCAN_CHECK_WATCHDOG);
 
 	return 0;
 }
@@ -355,12 +412,6 @@
 
 	mutex_lock(&priv->mutex);
 
-	if (!iwl_is_ready_rf(priv)) {
-		ret = -EIO;
-		IWL_DEBUG_MAC80211(priv, "leave - not ready or exit pending\n");
-		goto out_unlock;
-	}
-
 	if (test_bit(STATUS_SCANNING, &priv->status) &&
 	    !priv->is_internal_short_scan) {
 		IWL_DEBUG_SCAN(priv, "Scan already in progress.\n");
@@ -368,14 +419,7 @@
 		goto out_unlock;
 	}
 
-	if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
-		IWL_DEBUG_SCAN(priv, "Scan request while abort pending\n");
-		ret = -EAGAIN;
-		goto out_unlock;
-	}
-
 	/* mac80211 will only ask for one band at a time */
-	priv->scan_band = req->channels[0]->band;
 	priv->scan_request = req;
 	priv->scan_vif = vif;
 
@@ -383,10 +427,12 @@
 	 * If an internal scan is in progress, just set
 	 * up the scan_request as per above.
 	 */
-	if (priv->is_internal_short_scan)
+	if (priv->is_internal_short_scan) {
+		IWL_DEBUG_SCAN(priv, "SCAN request during internal scan\n");
 		ret = 0;
-	else
-		ret = iwl_scan_initiate(priv, vif);
+	} else
+		ret = iwl_scan_initiate(priv, vif, false,
+					req->channels[0]->band);
 
 	IWL_DEBUG_MAC80211(priv, "leave\n");
 
@@ -411,6 +457,8 @@
 	struct iwl_priv *priv =
 		container_of(work, struct iwl_priv, start_internal_scan);
 
+	IWL_DEBUG_SCAN(priv, "Start internal scan\n");
+
 	mutex_lock(&priv->mutex);
 
 	if (priv->is_internal_short_scan == true) {
@@ -418,31 +466,13 @@
 		goto unlock;
 	}
 
-	if (!iwl_is_ready_rf(priv)) {
-		IWL_DEBUG_SCAN(priv, "not ready or exit pending\n");
-		goto unlock;
-	}
-
 	if (test_bit(STATUS_SCANNING, &priv->status)) {
 		IWL_DEBUG_SCAN(priv, "Scan already in progress.\n");
 		goto unlock;
 	}
 
-	if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
-		IWL_DEBUG_SCAN(priv, "Scan request while abort pending\n");
-		goto unlock;
-	}
-
-	priv->scan_band = priv->band;
-
-	IWL_DEBUG_SCAN(priv, "Start internal short scan...\n");
-	set_bit(STATUS_SCANNING, &priv->status);
-	priv->is_internal_short_scan = true;
-
-	if (WARN_ON(!priv->cfg->ops->utils->request_scan))
-		goto unlock;
-
-	priv->cfg->ops->utils->request_scan(priv, NULL);
+	if (iwl_scan_initiate(priv, NULL, true, priv->band))
+		IWL_DEBUG_SCAN(priv, "failed to start internal short scan\n");
  unlock:
 	mutex_unlock(&priv->mutex);
 }
@@ -452,18 +482,13 @@
 	struct iwl_priv *priv =
 	    container_of(data, struct iwl_priv, scan_check.work);
 
-	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
-		return;
+	IWL_DEBUG_SCAN(priv, "Scan check work\n");
 
+	/* Since we are here firmware does not finish scan and
+	 * most likely is in bad shape, so we don't bother to
+	 * send abort command, just force scan complete to mac80211 */
 	mutex_lock(&priv->mutex);
-	if (test_bit(STATUS_SCANNING, &priv->status) &&
-	    !test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
-		IWL_DEBUG_SCAN(priv, "Scan completion watchdog (%dms)\n",
-			       jiffies_to_msecs(IWL_SCAN_CHECK_WATCHDOG));
-
-		if (!test_bit(STATUS_EXIT_PENDING, &priv->status))
-			iwl_send_scan_abort(priv);
-	}
+	iwl_force_scan_end(priv);
 	mutex_unlock(&priv->mutex);
 }
 
@@ -519,15 +544,12 @@
 {
 	struct iwl_priv *priv = container_of(work, struct iwl_priv, abort_scan);
 
-	if (!test_bit(STATUS_READY, &priv->status) ||
-	    !test_bit(STATUS_GEO_CONFIGURED, &priv->status))
-		return;
+	IWL_DEBUG_SCAN(priv, "Abort scan work\n");
 
-	cancel_delayed_work(&priv->scan_check);
-
+	/* We keep scan_check work queued in case when firmware will not
+	 * report back scan completed notification */
 	mutex_lock(&priv->mutex);
-	if (test_bit(STATUS_SCAN_ABORTING, &priv->status))
-		iwl_send_scan_abort(priv);
+	iwl_scan_cancel_timeout(priv, 200);
 	mutex_unlock(&priv->mutex);
 }
 
@@ -535,30 +557,52 @@
 {
 	struct iwl_priv *priv =
 	    container_of(work, struct iwl_priv, scan_completed);
-	bool internal = false;
-	bool scan_completed = false;
+	bool aborted;
 	struct iwl_rxon_context *ctx;
 
-	IWL_DEBUG_SCAN(priv, "SCAN complete scan\n");
+	IWL_DEBUG_SCAN(priv, "Completed %sscan.\n",
+		       priv->is_internal_short_scan ? "internal short " : "");
 
 	cancel_delayed_work(&priv->scan_check);
 
 	mutex_lock(&priv->mutex);
-	if (priv->is_internal_short_scan) {
-		priv->is_internal_short_scan = false;
-		IWL_DEBUG_SCAN(priv, "internal short scan completed\n");
-		internal = true;
-	} else if (priv->scan_request) {
-		scan_completed = true;
-		priv->scan_request = NULL;
-		priv->scan_vif = NULL;
+
+	aborted = test_and_clear_bit(STATUS_SCAN_ABORTING, &priv->status);
+	if (aborted)
+		IWL_DEBUG_SCAN(priv, "Aborted scan completed.\n");
+
+	if (!test_and_clear_bit(STATUS_SCANNING, &priv->status)) {
+		IWL_DEBUG_SCAN(priv, "Scan already completed.\n");
+		goto out_settings;
 	}
 
-	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
-		goto out;
+	if (priv->is_internal_short_scan && !aborted) {
+		int err;
 
-	if (internal && priv->scan_request)
-		iwl_scan_initiate(priv, priv->scan_vif);
+		/* Check if mac80211 requested scan during our internal scan */
+		if (priv->scan_request == NULL)
+			goto out_complete;
+
+		/* If so request a new scan */
+		err = iwl_scan_initiate(priv, priv->scan_vif, false,
+					priv->scan_request->channels[0]->band);
+		if (err) {
+			IWL_DEBUG_SCAN(priv,
+				"failed to initiate pending scan: %d\n", err);
+			aborted = true;
+			goto out_complete;
+		}
+
+		goto out;
+	}
+
+out_complete:
+	iwl_complete_scan(priv, aborted);
+
+out_settings:
+	/* Can we still talk to firmware ? */
+	if (!iwl_is_ready_rf(priv))
+		goto out;
 
 	/* Since setting the TXPOWER may have been deferred while
 	 * performing the scan, fire one off */
@@ -571,19 +615,11 @@
 	for_each_context(priv, ctx)
 		iwlcore_commit_rxon(priv, ctx);
 
- out:
 	if (priv->cfg->ops->hcmd->set_pan_params)
 		priv->cfg->ops->hcmd->set_pan_params(priv);
 
+ out:
 	mutex_unlock(&priv->mutex);
-
-	/*
-	 * Do not hold mutex here since this will cause mac80211 to call
-	 * into driver again into functions that will attempt to take
-	 * mutex.
-	 */
-	if (scan_completed)
-		ieee80211_scan_completed(priv->hw, false);
 }
 
 void iwl_setup_scan_deferred_work(struct iwl_priv *priv)
@@ -595,3 +631,16 @@
 }
 EXPORT_SYMBOL(iwl_setup_scan_deferred_work);
 
+void iwl_cancel_scan_deferred_work(struct iwl_priv *priv)
+{
+	cancel_work_sync(&priv->start_internal_scan);
+	cancel_work_sync(&priv->abort_scan);
+	cancel_work_sync(&priv->scan_completed);
+
+	if (cancel_delayed_work_sync(&priv->scan_check)) {
+		mutex_lock(&priv->mutex);
+		iwl_force_scan_end(priv);
+		mutex_unlock(&priv->mutex);
+	}
+}
+EXPORT_SYMBOL(iwl_cancel_scan_deferred_work);
diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c
index ccd0902..6edd034 100644
--- a/drivers/net/wireless/iwlwifi/iwl-sta.c
+++ b/drivers/net/wireless/iwlwifi/iwl-sta.c
@@ -386,7 +386,8 @@
 {
 	int i, r;
 	struct iwl_link_quality_cmd *link_cmd;
-	u32 rate_flags;
+	u32 rate_flags = 0;
+	__le32 rate_n_flags;
 
 	link_cmd = kzalloc(sizeof(struct iwl_link_quality_cmd), GFP_KERNEL);
 	if (!link_cmd) {
@@ -400,18 +401,14 @@
 	else
 		r = IWL_RATE_1M_INDEX;
 
-	for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
-		rate_flags = 0;
-		if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE)
-			rate_flags |= RATE_MCS_CCK_MSK;
+	if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE)
+		rate_flags |= RATE_MCS_CCK_MSK;
 
-		rate_flags |= first_antenna(priv->hw_params.valid_tx_ant) <<
+	rate_flags |= first_antenna(priv->hw_params.valid_tx_ant) <<
 				RATE_MCS_ANT_POS;
-
-		link_cmd->rs_table[i].rate_n_flags =
-			iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags);
-		r = iwl_get_prev_ieee_rate(r);
-	}
+	rate_n_flags = iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags);
+	for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++)
+		link_cmd->rs_table[i].rate_n_flags = rate_n_flags;
 
 	link_cmd->general_params.single_stream_ant_msk =
 				first_antenna(priv->hw_params.valid_tx_ant);
diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c
index 347d3dc..3290b15 100644
--- a/drivers/net/wireless/iwlwifi/iwl-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-tx.c
@@ -666,8 +666,8 @@
 		TX_STATUS_FAIL(TID_DISABLE);
 		TX_STATUS_FAIL(FIFO_FLUSHED);
 		TX_STATUS_FAIL(INSUFFICIENT_CF_POLL);
-		TX_STATUS_FAIL(FW_DROP);
-		TX_STATUS_FAIL(STA_COLOR_MISMATCH_DROP);
+		TX_STATUS_FAIL(PASSIVE_NO_RX);
+		TX_STATUS_FAIL(NO_BEACON_ON_RADAR);
 	}
 
 	return "UNKNOWN";
diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c
index 68e624a..1167771 100644
--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
@@ -1730,7 +1730,6 @@
 		IWL_ERR(priv, "Microcode SW error detected. "
 			"Restarting 0x%X.\n", inta);
 		priv->isr_stats.sw++;
-		priv->isr_stats.sw_err = inta;
 		iwl_irq_handle_error(priv);
 		handled |= CSR_INT_BIT_SW_ERR;
 	}
@@ -2568,15 +2567,13 @@
 static void __iwl3945_down(struct iwl_priv *priv)
 {
 	unsigned long flags;
-	int exit_pending = test_bit(STATUS_EXIT_PENDING, &priv->status);
-	struct ieee80211_conf *conf = NULL;
+	int exit_pending;
 
 	IWL_DEBUG_INFO(priv, DRV_NAME " is going down\n");
 
-	conf = ieee80211_get_hw_conf(priv->hw);
+	iwl_scan_cancel_timeout(priv, 200);
 
-	if (!exit_pending)
-		set_bit(STATUS_EXIT_PENDING, &priv->status);
+	exit_pending = test_and_set_bit(STATUS_EXIT_PENDING, &priv->status);
 
 	/* Stop TX queues watchdog. We need to have STATUS_EXIT_PENDING bit set
 	 * to prevent rearm timer */
@@ -2820,7 +2817,7 @@
 
 }
 
-void iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
+int iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
 {
 	struct iwl_host_cmd cmd = {
 		.id = REPLY_SCAN_CMD,
@@ -2828,61 +2825,19 @@
 		.flags = CMD_SIZE_HUGE,
 	};
 	struct iwl3945_scan_cmd *scan;
-	struct ieee80211_conf *conf = NULL;
 	u8 n_probes = 0;
 	enum ieee80211_band band;
 	bool is_active = false;
+	int ret;
 
-	conf = ieee80211_get_hw_conf(priv->hw);
-
-	cancel_delayed_work(&priv->scan_check);
-
-	if (!iwl_is_ready(priv)) {
-		IWL_WARN(priv, "request scan called when driver not ready.\n");
-		goto done;
-	}
-
-	/* Make sure the scan wasn't canceled before this queued work
-	 * was given the chance to run... */
-	if (!test_bit(STATUS_SCANNING, &priv->status))
-		goto done;
-
-	/* This should never be called or scheduled if there is currently
-	 * a scan active in the hardware. */
-	if (test_bit(STATUS_SCAN_HW, &priv->status)) {
-		IWL_DEBUG_INFO(priv, "Multiple concurrent scan requests  "
-				"Ignoring second request.\n");
-		goto done;
-	}
-
-	if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
-		IWL_DEBUG_SCAN(priv, "Aborting scan due to device shutdown\n");
-		goto done;
-	}
-
-	if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
-		IWL_DEBUG_HC(priv,
-			"Scan request while abort pending. Queuing.\n");
-		goto done;
-	}
-
-	if (iwl_is_rfkill(priv)) {
-		IWL_DEBUG_HC(priv, "Aborting scan due to RF Kill activation\n");
-		goto done;
-	}
-
-	if (!test_bit(STATUS_READY, &priv->status)) {
-		IWL_DEBUG_HC(priv,
-			"Scan request while uninitialized. Queuing.\n");
-		goto done;
-	}
+	lockdep_assert_held(&priv->mutex);
 
 	if (!priv->scan_cmd) {
 		priv->scan_cmd = kmalloc(sizeof(struct iwl3945_scan_cmd) +
 					 IWL_MAX_SCAN_SIZE, GFP_KERNEL);
 		if (!priv->scan_cmd) {
 			IWL_DEBUG_SCAN(priv, "Fail to allocate scan memory\n");
-			goto done;
+			return -ENOMEM;
 		}
 	}
 	scan = priv->scan_cmd;
@@ -2977,7 +2932,7 @@
 		break;
 	default:
 		IWL_WARN(priv, "Invalid scan band\n");
-		goto done;
+		return -EIO;
 	}
 
 	if (!priv->is_internal_short_scan) {
@@ -3012,7 +2967,7 @@
 
 	if (scan->channel_count == 0) {
 		IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count);
-		goto done;
+		return -EIO;
 	}
 
 	cmd.len += le16_to_cpu(scan->tx_cmd.len) +
@@ -3021,25 +2976,10 @@
 	scan->len = cpu_to_le16(cmd.len);
 
 	set_bit(STATUS_SCAN_HW, &priv->status);
-	if (iwl_send_cmd_sync(priv, &cmd))
-		goto done;
-
-	queue_delayed_work(priv->workqueue, &priv->scan_check,
-			   IWL_SCAN_CHECK_WATCHDOG);
-
-	return;
-
- done:
-	/* can not perform scan make sure we clear scanning
-	 * bits from status so next scan request can be performed.
-	 * if we dont clear scanning status bit here all next scan
-	 * will fail
-	*/
-	clear_bit(STATUS_SCAN_HW, &priv->status);
-	clear_bit(STATUS_SCANNING, &priv->status);
-
-	/* inform mac80211 scan aborted */
-	queue_work(priv->workqueue, &priv->scan_completed);
+	ret = iwl_send_cmd_sync(priv, &cmd);
+	if (ret)
+		clear_bit(STATUS_SCAN_HW, &priv->status);
+	return ret;
 }
 
 static void iwl3945_bg_restart(struct work_struct *data)
@@ -3233,15 +3173,6 @@
 
 	priv->is_open = 0;
 
-	if (iwl_is_ready_rf(priv)) {
-		/* stop mac, cancel any scan request and clear
-		 * RXON_FILTER_ASSOC_MSK BIT
-		 */
-		mutex_lock(&priv->mutex);
-		iwl_scan_cancel_timeout(priv, 100);
-		mutex_unlock(&priv->mutex);
-	}
-
 	iwl3945_down(priv);
 
 	flush_workqueue(priv->workqueue);
@@ -3831,10 +3762,10 @@
 	iwl3945_hw_cancel_deferred_work(priv);
 
 	cancel_delayed_work_sync(&priv->init_alive_start);
-	cancel_delayed_work(&priv->scan_check);
 	cancel_delayed_work(&priv->alive_start);
-	cancel_work_sync(&priv->start_internal_scan);
 	cancel_work_sync(&priv->beacon_update);
+
+	iwl_cancel_scan_deferred_work(priv);
 }
 
 static struct attribute *iwl3945_sysfs_entries[] = {
diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c
index 317f086..1bbdb14 100644
--- a/drivers/net/wireless/libertas/cfg.c
+++ b/drivers/net/wireless/libertas/cfg.c
@@ -481,7 +481,6 @@
 	struct cmd_ds_802_11_scan_rsp *scanresp = (void *)resp;
 	int bsssize;
 	const u8 *pos;
-	u16 nr_sets;
 	const u8 *tsfdesc;
 	int tsfsize;
 	int i;
@@ -490,12 +489,11 @@
 	lbs_deb_enter(LBS_DEB_CFG80211);
 
 	bsssize = get_unaligned_le16(&scanresp->bssdescriptsize);
-	nr_sets = le16_to_cpu(scanresp->nr_sets);
 
 	lbs_deb_scan("scan response: %d BSSs (%d bytes); resp size %d bytes\n",
-			nr_sets, bsssize, le16_to_cpu(resp->size));
+			scanresp->nr_sets, bsssize, le16_to_cpu(resp->size));
 
-	if (nr_sets == 0) {
+	if (scanresp->nr_sets == 0) {
 		ret = 0;
 		goto done;
 	}
diff --git a/drivers/net/wireless/libertas/mesh.c b/drivers/net/wireless/libertas/mesh.c
index 194762a..acf3bf6 100644
--- a/drivers/net/wireless/libertas/mesh.c
+++ b/drivers/net/wireless/libertas/mesh.c
@@ -574,7 +574,7 @@
 	memset(&cmd, 0, sizeof(cmd));
 	cmd.hdr.size = cpu_to_le16(sizeof(cmd));
 	cmd.action = cpu_to_le16(CMD_ACT_BT_ACCESS_SET_INVERT);
-	cmd.id = !!inverted;
+	cmd.id = cpu_to_le32(!!inverted);
 
 	ret = lbs_cmd_with_response(priv, CMD_BT_ACCESS, &cmd);
 
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 92b486d..7eaaa3b 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -595,7 +595,8 @@
 					struct ieee80211_vif *vif)
 {
 	wiphy_debug(hw->wiphy, "%s (type=%d mac_addr=%pM)\n",
-		    __func__, vif->type, vif->addr);
+		    __func__, ieee80211_vif_type_p2p(vif),
+		    vif->addr);
 	hwsim_set_magic(vif);
 	return 0;
 }
@@ -603,11 +604,14 @@
 
 static int mac80211_hwsim_change_interface(struct ieee80211_hw *hw,
 					   struct ieee80211_vif *vif,
-					   enum nl80211_iftype newtype)
+					   enum nl80211_iftype newtype,
+					   bool newp2p)
 {
+	newtype = ieee80211_iftype_p2p(newtype, newp2p);
 	wiphy_debug(hw->wiphy,
 		    "%s (old type=%d, new type=%d, mac_addr=%pM)\n",
-		    __func__, vif->type, newtype, vif->addr);
+		    __func__, ieee80211_vif_type_p2p(vif),
+		    newtype, vif->addr);
 	hwsim_check_magic(vif);
 
 	return 0;
@@ -617,7 +621,8 @@
 	struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 {
 	wiphy_debug(hw->wiphy, "%s (type=%d mac_addr=%pM)\n",
-		    __func__, vif->type, vif->addr);
+		    __func__, ieee80211_vif_type_p2p(vif),
+		    vif->addr);
 	hwsim_check_magic(vif);
 	hwsim_clear_magic(vif);
 }
@@ -1310,6 +1315,8 @@
 		hw->wiphy->interface_modes =
 			BIT(NL80211_IFTYPE_STATION) |
 			BIT(NL80211_IFTYPE_AP) |
+			BIT(NL80211_IFTYPE_P2P_CLIENT) |
+			BIT(NL80211_IFTYPE_P2P_GO) |
 			BIT(NL80211_IFTYPE_ADHOC) |
 			BIT(NL80211_IFTYPE_MESH_POINT);
 
diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c
index 156e57d..18d24b7 100644
--- a/drivers/net/wireless/p54/p54spi.c
+++ b/drivers/net/wireless/p54/p54spi.c
@@ -202,6 +202,8 @@
 		dev_info(&priv->spi->dev, "loading default eeprom...\n");
 		ret = p54_parse_eeprom(dev, (void *) p54spi_eeprom,
 				       sizeof(p54spi_eeprom));
+#else
+		dev_err(&priv->spi->dev, "Failed to request user eeprom\n");
 #endif /* CONFIG_P54_SPI_DEFAULT_EEPROM */
 	} else {
 		dev_info(&priv->spi->dev, "loading user eeprom...\n");
diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c
index d91a831..5ca624a 100644
--- a/drivers/net/wireless/ray_cs.c
+++ b/drivers/net/wireless/ray_cs.c
@@ -194,7 +194,7 @@
 module_param(phy_addr, charp, 0);
 module_param(ray_mem_speed, int, 0);
 
-static UCHAR b5_default_startup_parms[] = {
+static const UCHAR b5_default_startup_parms[] = {
 	0, 0,			/* Adhoc station */
 	'L', 'I', 'N', 'U', 'X', 0, 0, 0,	/* 32 char ESSID */
 	0, 0, 0, 0, 0, 0, 0, 0,
@@ -229,7 +229,7 @@
 	2, 0, 0, 0, 0, 0, 0, 0	/* basic rate set */
 };
 
-static UCHAR b4_default_startup_parms[] = {
+static const UCHAR b4_default_startup_parms[] = {
 	0, 0,			/* Adhoc station */
 	'L', 'I', 'N', 'U', 'X', 0, 0, 0,	/* 32 char ESSID */
 	0, 0, 0, 0, 0, 0, 0, 0,
@@ -261,9 +261,9 @@
 };
 
 /*===========================================================================*/
-static unsigned char eth2_llc[] = { 0xaa, 0xaa, 3, 0, 0, 0 };
+static const u8 eth2_llc[] = { 0xaa, 0xaa, 3, 0, 0, 0 };
 
-static char hop_pattern_length[] = { 1,
+static const char hop_pattern_length[] = { 1,
 	USA_HOP_MOD, EUROPE_HOP_MOD,
 	JAPAN_HOP_MOD, KOREA_HOP_MOD,
 	SPAIN_HOP_MOD, FRANCE_HOP_MOD,
@@ -271,7 +271,7 @@
 	JAPAN_TEST_HOP_MOD
 };
 
-static char rcsid[] =
+static const char rcsid[] =
     "Raylink/WebGear wireless LAN - Corey <Thomas corey@world.std.com>";
 
 static const struct net_device_ops ray_netdev_ops = {
@@ -2575,7 +2575,7 @@
 #ifdef CONFIG_PROC_FS
 #define MAXDATA (PAGE_SIZE - 80)
 
-static char *card_status[] = {
+static const char *card_status[] = {
 	"Card inserted - uninitialized",	/* 0 */
 	"Card not downloaded",			/* 1 */
 	"Waiting for download parameters",	/* 2 */
@@ -2592,8 +2592,8 @@
 	"Association failed"			/* 16 */
 };
 
-static char *nettype[] = { "Adhoc", "Infra " };
-static char *framing[] = { "Encapsulation", "Translation" }
+static const char *nettype[] = { "Adhoc", "Infra " };
+static const char *framing[] = { "Encapsulation", "Translation" }
 
 ;
 /*===========================================================================*/
diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c
index 103c711..d49e830 100644
--- a/drivers/net/wireless/rt2x00/rt2400pci.c
+++ b/drivers/net/wireless/rt2x00/rt2400pci.c
@@ -321,7 +321,8 @@
 }
 
 static void rt2400pci_config_erp(struct rt2x00_dev *rt2x00dev,
-				 struct rt2x00lib_erp *erp)
+				 struct rt2x00lib_erp *erp,
+				 u32 changed)
 {
 	int preamble_mask;
 	u32 reg;
@@ -329,59 +330,72 @@
 	/*
 	 * When short preamble is enabled, we should set bit 0x08
 	 */
-	preamble_mask = erp->short_preamble << 3;
+	if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+		preamble_mask = erp->short_preamble << 3;
 
-	rt2x00pci_register_read(rt2x00dev, TXCSR1, &reg);
-	rt2x00_set_field32(&reg, TXCSR1_ACK_TIMEOUT, 0x1ff);
-	rt2x00_set_field32(&reg, TXCSR1_ACK_CONSUME_TIME, 0x13a);
-	rt2x00_set_field32(&reg, TXCSR1_TSF_OFFSET, IEEE80211_HEADER);
-	rt2x00_set_field32(&reg, TXCSR1_AUTORESPONDER, 1);
-	rt2x00pci_register_write(rt2x00dev, TXCSR1, reg);
+		rt2x00pci_register_read(rt2x00dev, TXCSR1, &reg);
+		rt2x00_set_field32(&reg, TXCSR1_ACK_TIMEOUT, 0x1ff);
+		rt2x00_set_field32(&reg, TXCSR1_ACK_CONSUME_TIME, 0x13a);
+		rt2x00_set_field32(&reg, TXCSR1_TSF_OFFSET, IEEE80211_HEADER);
+		rt2x00_set_field32(&reg, TXCSR1_AUTORESPONDER, 1);
+		rt2x00pci_register_write(rt2x00dev, TXCSR1, reg);
 
-	rt2x00pci_register_read(rt2x00dev, ARCSR2, &reg);
-	rt2x00_set_field32(&reg, ARCSR2_SIGNAL, 0x00);
-	rt2x00_set_field32(&reg, ARCSR2_SERVICE, 0x04);
-	rt2x00_set_field32(&reg, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 10));
-	rt2x00pci_register_write(rt2x00dev, ARCSR2, reg);
+		rt2x00pci_register_read(rt2x00dev, ARCSR2, &reg);
+		rt2x00_set_field32(&reg, ARCSR2_SIGNAL, 0x00);
+		rt2x00_set_field32(&reg, ARCSR2_SERVICE, 0x04);
+		rt2x00_set_field32(&reg, ARCSR2_LENGTH,
+				   GET_DURATION(ACK_SIZE, 10));
+		rt2x00pci_register_write(rt2x00dev, ARCSR2, reg);
 
-	rt2x00pci_register_read(rt2x00dev, ARCSR3, &reg);
-	rt2x00_set_field32(&reg, ARCSR3_SIGNAL, 0x01 | preamble_mask);
-	rt2x00_set_field32(&reg, ARCSR3_SERVICE, 0x04);
-	rt2x00_set_field32(&reg, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 20));
-	rt2x00pci_register_write(rt2x00dev, ARCSR3, reg);
+		rt2x00pci_register_read(rt2x00dev, ARCSR3, &reg);
+		rt2x00_set_field32(&reg, ARCSR3_SIGNAL, 0x01 | preamble_mask);
+		rt2x00_set_field32(&reg, ARCSR3_SERVICE, 0x04);
+		rt2x00_set_field32(&reg, ARCSR2_LENGTH,
+				   GET_DURATION(ACK_SIZE, 20));
+		rt2x00pci_register_write(rt2x00dev, ARCSR3, reg);
 
-	rt2x00pci_register_read(rt2x00dev, ARCSR4, &reg);
-	rt2x00_set_field32(&reg, ARCSR4_SIGNAL, 0x02 | preamble_mask);
-	rt2x00_set_field32(&reg, ARCSR4_SERVICE, 0x04);
-	rt2x00_set_field32(&reg, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 55));
-	rt2x00pci_register_write(rt2x00dev, ARCSR4, reg);
+		rt2x00pci_register_read(rt2x00dev, ARCSR4, &reg);
+		rt2x00_set_field32(&reg, ARCSR4_SIGNAL, 0x02 | preamble_mask);
+		rt2x00_set_field32(&reg, ARCSR4_SERVICE, 0x04);
+		rt2x00_set_field32(&reg, ARCSR2_LENGTH,
+				   GET_DURATION(ACK_SIZE, 55));
+		rt2x00pci_register_write(rt2x00dev, ARCSR4, reg);
 
-	rt2x00pci_register_read(rt2x00dev, ARCSR5, &reg);
-	rt2x00_set_field32(&reg, ARCSR5_SIGNAL, 0x03 | preamble_mask);
-	rt2x00_set_field32(&reg, ARCSR5_SERVICE, 0x84);
-	rt2x00_set_field32(&reg, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 110));
-	rt2x00pci_register_write(rt2x00dev, ARCSR5, reg);
+		rt2x00pci_register_read(rt2x00dev, ARCSR5, &reg);
+		rt2x00_set_field32(&reg, ARCSR5_SIGNAL, 0x03 | preamble_mask);
+		rt2x00_set_field32(&reg, ARCSR5_SERVICE, 0x84);
+		rt2x00_set_field32(&reg, ARCSR2_LENGTH,
+				   GET_DURATION(ACK_SIZE, 110));
+		rt2x00pci_register_write(rt2x00dev, ARCSR5, reg);
+	}
 
-	rt2x00pci_register_write(rt2x00dev, ARCSR1, erp->basic_rates);
+	if (changed & BSS_CHANGED_BASIC_RATES)
+		rt2x00pci_register_write(rt2x00dev, ARCSR1, erp->basic_rates);
 
-	rt2x00pci_register_read(rt2x00dev, CSR11, &reg);
-	rt2x00_set_field32(&reg, CSR11_SLOT_TIME, erp->slot_time);
-	rt2x00pci_register_write(rt2x00dev, CSR11, reg);
+	if (changed & BSS_CHANGED_ERP_SLOT) {
+		rt2x00pci_register_read(rt2x00dev, CSR11, &reg);
+		rt2x00_set_field32(&reg, CSR11_SLOT_TIME, erp->slot_time);
+		rt2x00pci_register_write(rt2x00dev, CSR11, reg);
 
-	rt2x00pci_register_read(rt2x00dev, CSR12, &reg);
-	rt2x00_set_field32(&reg, CSR12_BEACON_INTERVAL, erp->beacon_int * 16);
-	rt2x00_set_field32(&reg, CSR12_CFP_MAX_DURATION, erp->beacon_int * 16);
-	rt2x00pci_register_write(rt2x00dev, CSR12, reg);
+		rt2x00pci_register_read(rt2x00dev, CSR18, &reg);
+		rt2x00_set_field32(&reg, CSR18_SIFS, erp->sifs);
+		rt2x00_set_field32(&reg, CSR18_PIFS, erp->pifs);
+		rt2x00pci_register_write(rt2x00dev, CSR18, reg);
 
-	rt2x00pci_register_read(rt2x00dev, CSR18, &reg);
-	rt2x00_set_field32(&reg, CSR18_SIFS, erp->sifs);
-	rt2x00_set_field32(&reg, CSR18_PIFS, erp->pifs);
-	rt2x00pci_register_write(rt2x00dev, CSR18, reg);
+		rt2x00pci_register_read(rt2x00dev, CSR19, &reg);
+		rt2x00_set_field32(&reg, CSR19_DIFS, erp->difs);
+		rt2x00_set_field32(&reg, CSR19_EIFS, erp->eifs);
+		rt2x00pci_register_write(rt2x00dev, CSR19, reg);
+	}
 
-	rt2x00pci_register_read(rt2x00dev, CSR19, &reg);
-	rt2x00_set_field32(&reg, CSR19_DIFS, erp->difs);
-	rt2x00_set_field32(&reg, CSR19_EIFS, erp->eifs);
-	rt2x00pci_register_write(rt2x00dev, CSR19, reg);
+	if (changed & BSS_CHANGED_BEACON_INT) {
+		rt2x00pci_register_read(rt2x00dev, CSR12, &reg);
+		rt2x00_set_field32(&reg, CSR12_BEACON_INTERVAL,
+				   erp->beacon_int * 16);
+		rt2x00_set_field32(&reg, CSR12_CFP_MAX_DURATION,
+				   erp->beacon_int * 16);
+		rt2x00pci_register_write(rt2x00dev, CSR12, reg);
+	}
 }
 
 static void rt2400pci_config_ant(struct rt2x00_dev *rt2x00dev,
diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c
index ab05071..2214c32 100644
--- a/drivers/net/wireless/rt2x00/rt2500pci.c
+++ b/drivers/net/wireless/rt2x00/rt2500pci.c
@@ -327,7 +327,8 @@
 }
 
 static void rt2500pci_config_erp(struct rt2x00_dev *rt2x00dev,
-				 struct rt2x00lib_erp *erp)
+				 struct rt2x00lib_erp *erp,
+				 u32 changed)
 {
 	int preamble_mask;
 	u32 reg;
@@ -335,59 +336,73 @@
 	/*
 	 * When short preamble is enabled, we should set bit 0x08
 	 */
-	preamble_mask = erp->short_preamble << 3;
+	if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+		preamble_mask = erp->short_preamble << 3;
 
-	rt2x00pci_register_read(rt2x00dev, TXCSR1, &reg);
-	rt2x00_set_field32(&reg, TXCSR1_ACK_TIMEOUT, 0x162);
-	rt2x00_set_field32(&reg, TXCSR1_ACK_CONSUME_TIME, 0xa2);
-	rt2x00_set_field32(&reg, TXCSR1_TSF_OFFSET, IEEE80211_HEADER);
-	rt2x00_set_field32(&reg, TXCSR1_AUTORESPONDER, 1);
-	rt2x00pci_register_write(rt2x00dev, TXCSR1, reg);
+		rt2x00pci_register_read(rt2x00dev, TXCSR1, &reg);
+		rt2x00_set_field32(&reg, TXCSR1_ACK_TIMEOUT, 0x162);
+		rt2x00_set_field32(&reg, TXCSR1_ACK_CONSUME_TIME, 0xa2);
+		rt2x00_set_field32(&reg, TXCSR1_TSF_OFFSET, IEEE80211_HEADER);
+		rt2x00_set_field32(&reg, TXCSR1_AUTORESPONDER, 1);
+		rt2x00pci_register_write(rt2x00dev, TXCSR1, reg);
 
-	rt2x00pci_register_read(rt2x00dev, ARCSR2, &reg);
-	rt2x00_set_field32(&reg, ARCSR2_SIGNAL, 0x00);
-	rt2x00_set_field32(&reg, ARCSR2_SERVICE, 0x04);
-	rt2x00_set_field32(&reg, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 10));
-	rt2x00pci_register_write(rt2x00dev, ARCSR2, reg);
+		rt2x00pci_register_read(rt2x00dev, ARCSR2, &reg);
+		rt2x00_set_field32(&reg, ARCSR2_SIGNAL, 0x00);
+		rt2x00_set_field32(&reg, ARCSR2_SERVICE, 0x04);
+		rt2x00_set_field32(&reg, ARCSR2_LENGTH,
+				   GET_DURATION(ACK_SIZE, 10));
+		rt2x00pci_register_write(rt2x00dev, ARCSR2, reg);
 
-	rt2x00pci_register_read(rt2x00dev, ARCSR3, &reg);
-	rt2x00_set_field32(&reg, ARCSR3_SIGNAL, 0x01 | preamble_mask);
-	rt2x00_set_field32(&reg, ARCSR3_SERVICE, 0x04);
-	rt2x00_set_field32(&reg, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 20));
-	rt2x00pci_register_write(rt2x00dev, ARCSR3, reg);
+		rt2x00pci_register_read(rt2x00dev, ARCSR3, &reg);
+		rt2x00_set_field32(&reg, ARCSR3_SIGNAL, 0x01 | preamble_mask);
+		rt2x00_set_field32(&reg, ARCSR3_SERVICE, 0x04);
+		rt2x00_set_field32(&reg, ARCSR2_LENGTH,
+				   GET_DURATION(ACK_SIZE, 20));
+		rt2x00pci_register_write(rt2x00dev, ARCSR3, reg);
 
-	rt2x00pci_register_read(rt2x00dev, ARCSR4, &reg);
-	rt2x00_set_field32(&reg, ARCSR4_SIGNAL, 0x02 | preamble_mask);
-	rt2x00_set_field32(&reg, ARCSR4_SERVICE, 0x04);
-	rt2x00_set_field32(&reg, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 55));
-	rt2x00pci_register_write(rt2x00dev, ARCSR4, reg);
+		rt2x00pci_register_read(rt2x00dev, ARCSR4, &reg);
+		rt2x00_set_field32(&reg, ARCSR4_SIGNAL, 0x02 | preamble_mask);
+		rt2x00_set_field32(&reg, ARCSR4_SERVICE, 0x04);
+		rt2x00_set_field32(&reg, ARCSR2_LENGTH,
+				   GET_DURATION(ACK_SIZE, 55));
+		rt2x00pci_register_write(rt2x00dev, ARCSR4, reg);
 
-	rt2x00pci_register_read(rt2x00dev, ARCSR5, &reg);
-	rt2x00_set_field32(&reg, ARCSR5_SIGNAL, 0x03 | preamble_mask);
-	rt2x00_set_field32(&reg, ARCSR5_SERVICE, 0x84);
-	rt2x00_set_field32(&reg, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 110));
-	rt2x00pci_register_write(rt2x00dev, ARCSR5, reg);
+		rt2x00pci_register_read(rt2x00dev, ARCSR5, &reg);
+		rt2x00_set_field32(&reg, ARCSR5_SIGNAL, 0x03 | preamble_mask);
+		rt2x00_set_field32(&reg, ARCSR5_SERVICE, 0x84);
+		rt2x00_set_field32(&reg, ARCSR2_LENGTH,
+				   GET_DURATION(ACK_SIZE, 110));
+		rt2x00pci_register_write(rt2x00dev, ARCSR5, reg);
+	}
 
-	rt2x00pci_register_write(rt2x00dev, ARCSR1, erp->basic_rates);
+	if (changed & BSS_CHANGED_BASIC_RATES)
+		rt2x00pci_register_write(rt2x00dev, ARCSR1, erp->basic_rates);
 
-	rt2x00pci_register_read(rt2x00dev, CSR11, &reg);
-	rt2x00_set_field32(&reg, CSR11_SLOT_TIME, erp->slot_time);
-	rt2x00pci_register_write(rt2x00dev, CSR11, reg);
+	if (changed & BSS_CHANGED_ERP_SLOT) {
+		rt2x00pci_register_read(rt2x00dev, CSR11, &reg);
+		rt2x00_set_field32(&reg, CSR11_SLOT_TIME, erp->slot_time);
+		rt2x00pci_register_write(rt2x00dev, CSR11, reg);
 
-	rt2x00pci_register_read(rt2x00dev, CSR12, &reg);
-	rt2x00_set_field32(&reg, CSR12_BEACON_INTERVAL, erp->beacon_int * 16);
-	rt2x00_set_field32(&reg, CSR12_CFP_MAX_DURATION, erp->beacon_int * 16);
-	rt2x00pci_register_write(rt2x00dev, CSR12, reg);
+		rt2x00pci_register_read(rt2x00dev, CSR18, &reg);
+		rt2x00_set_field32(&reg, CSR18_SIFS, erp->sifs);
+		rt2x00_set_field32(&reg, CSR18_PIFS, erp->pifs);
+		rt2x00pci_register_write(rt2x00dev, CSR18, reg);
 
-	rt2x00pci_register_read(rt2x00dev, CSR18, &reg);
-	rt2x00_set_field32(&reg, CSR18_SIFS, erp->sifs);
-	rt2x00_set_field32(&reg, CSR18_PIFS, erp->pifs);
-	rt2x00pci_register_write(rt2x00dev, CSR18, reg);
+		rt2x00pci_register_read(rt2x00dev, CSR19, &reg);
+		rt2x00_set_field32(&reg, CSR19_DIFS, erp->difs);
+		rt2x00_set_field32(&reg, CSR19_EIFS, erp->eifs);
+		rt2x00pci_register_write(rt2x00dev, CSR19, reg);
+	}
 
-	rt2x00pci_register_read(rt2x00dev, CSR19, &reg);
-	rt2x00_set_field32(&reg, CSR19_DIFS, erp->difs);
-	rt2x00_set_field32(&reg, CSR19_EIFS, erp->eifs);
-	rt2x00pci_register_write(rt2x00dev, CSR19, reg);
+	if (changed & BSS_CHANGED_BEACON_INT) {
+		rt2x00pci_register_read(rt2x00dev, CSR12, &reg);
+		rt2x00_set_field32(&reg, CSR12_BEACON_INTERVAL,
+				   erp->beacon_int * 16);
+		rt2x00_set_field32(&reg, CSR12_CFP_MAX_DURATION,
+				   erp->beacon_int * 16);
+		rt2x00pci_register_write(rt2x00dev, CSR12, reg);
+	}
+
 }
 
 static void rt2500pci_config_ant(struct rt2x00_dev *rt2x00dev,
diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c
index db64df4..6e94356 100644
--- a/drivers/net/wireless/rt2x00/rt2500usb.c
+++ b/drivers/net/wireless/rt2x00/rt2500usb.c
@@ -494,24 +494,34 @@
 }
 
 static void rt2500usb_config_erp(struct rt2x00_dev *rt2x00dev,
-				 struct rt2x00lib_erp *erp)
+				 struct rt2x00lib_erp *erp,
+				 u32 changed)
 {
 	u16 reg;
 
-	rt2500usb_register_read(rt2x00dev, TXRX_CSR10, &reg);
-	rt2x00_set_field16(&reg, TXRX_CSR10_AUTORESPOND_PREAMBLE,
-			   !!erp->short_preamble);
-	rt2500usb_register_write(rt2x00dev, TXRX_CSR10, reg);
+	if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+		rt2500usb_register_read(rt2x00dev, TXRX_CSR10, &reg);
+		rt2x00_set_field16(&reg, TXRX_CSR10_AUTORESPOND_PREAMBLE,
+				   !!erp->short_preamble);
+		rt2500usb_register_write(rt2x00dev, TXRX_CSR10, reg);
+	}
 
-	rt2500usb_register_write(rt2x00dev, TXRX_CSR11, erp->basic_rates);
+	if (changed & BSS_CHANGED_BASIC_RATES)
+		rt2500usb_register_write(rt2x00dev, TXRX_CSR11,
+					 erp->basic_rates);
 
-	rt2500usb_register_read(rt2x00dev, TXRX_CSR18, &reg);
-	rt2x00_set_field16(&reg, TXRX_CSR18_INTERVAL, erp->beacon_int * 4);
-	rt2500usb_register_write(rt2x00dev, TXRX_CSR18, reg);
+	if (changed & BSS_CHANGED_BEACON_INT) {
+		rt2500usb_register_read(rt2x00dev, TXRX_CSR18, &reg);
+		rt2x00_set_field16(&reg, TXRX_CSR18_INTERVAL,
+				   erp->beacon_int * 4);
+		rt2500usb_register_write(rt2x00dev, TXRX_CSR18, reg);
+	}
 
-	rt2500usb_register_write(rt2x00dev, MAC_CSR10, erp->slot_time);
-	rt2500usb_register_write(rt2x00dev, MAC_CSR11, erp->sifs);
-	rt2500usb_register_write(rt2x00dev, MAC_CSR12, erp->eifs);
+	if (changed & BSS_CHANGED_ERP_SLOT) {
+		rt2500usb_register_write(rt2x00dev, MAC_CSR10, erp->slot_time);
+		rt2500usb_register_write(rt2x00dev, MAC_CSR11, erp->sifs);
+		rt2500usb_register_write(rt2x00dev, MAC_CSR12, erp->eifs);
+	}
 }
 
 static void rt2500usb_config_ant(struct rt2x00_dev *rt2x00dev,
diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/rt2x00/rt2800.h
index 70a5cb8..2edc774 100644
--- a/drivers/net/wireless/rt2x00/rt2800.h
+++ b/drivers/net/wireless/rt2x00/rt2800.h
@@ -639,6 +639,18 @@
 #define LED_CFG_LED_POLAR		FIELD32(0x40000000)
 
 /*
+ * AMPDU_BA_WINSIZE: Force BlockAck window size
+ * FORCE_WINSIZE_ENABLE:
+ *   0: Disable forcing of BlockAck window size
+ *   1: Enable forcing of BlockAck window size, overwrites values BlockAck
+ *      window size values in the TXWI
+ * FORCE_WINSIZE: BlockAck window size
+ */
+#define AMPDU_BA_WINSIZE		0x1040
+#define AMPDU_BA_WINSIZE_FORCE_WINSIZE_ENABLE FIELD32(0x00000020)
+#define AMPDU_BA_WINSIZE_FORCE_WINSIZE	FIELD32(0x0000001f)
+
+/*
  * XIFS_TIME_CFG: MAC timing
  * CCKM_SIFS_TIME: unit 1us. Applied after CCK RX/TX
  * OFDM_SIFS_TIME: unit 1us. Applied after OFDM RX/TX
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index 27a6e22..3bb6749 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -1159,38 +1159,50 @@
 }
 EXPORT_SYMBOL_GPL(rt2800_config_intf);
 
-void rt2800_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp)
+void rt2800_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp,
+		       u32 changed)
 {
 	u32 reg;
 
-	rt2800_register_read(rt2x00dev, AUTO_RSP_CFG, &reg);
-	rt2x00_set_field32(&reg, AUTO_RSP_CFG_BAC_ACK_POLICY,
-			   !!erp->short_preamble);
-	rt2x00_set_field32(&reg, AUTO_RSP_CFG_AR_PREAMBLE,
-			   !!erp->short_preamble);
-	rt2800_register_write(rt2x00dev, AUTO_RSP_CFG, reg);
+	if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+		rt2800_register_read(rt2x00dev, AUTO_RSP_CFG, &reg);
+		rt2x00_set_field32(&reg, AUTO_RSP_CFG_BAC_ACK_POLICY,
+				   !!erp->short_preamble);
+		rt2x00_set_field32(&reg, AUTO_RSP_CFG_AR_PREAMBLE,
+				   !!erp->short_preamble);
+		rt2800_register_write(rt2x00dev, AUTO_RSP_CFG, reg);
+	}
 
-	rt2800_register_read(rt2x00dev, OFDM_PROT_CFG, &reg);
-	rt2x00_set_field32(&reg, OFDM_PROT_CFG_PROTECT_CTRL,
-			   erp->cts_protection ? 2 : 0);
-	rt2800_register_write(rt2x00dev, OFDM_PROT_CFG, reg);
+	if (changed & BSS_CHANGED_ERP_CTS_PROT) {
+		rt2800_register_read(rt2x00dev, OFDM_PROT_CFG, &reg);
+		rt2x00_set_field32(&reg, OFDM_PROT_CFG_PROTECT_CTRL,
+				   erp->cts_protection ? 2 : 0);
+		rt2800_register_write(rt2x00dev, OFDM_PROT_CFG, reg);
+	}
 
-	rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE,
-				 erp->basic_rates);
-	rt2800_register_write(rt2x00dev, HT_BASIC_RATE, 0x00008003);
+	if (changed & BSS_CHANGED_BASIC_RATES) {
+		rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE,
+					 erp->basic_rates);
+		rt2800_register_write(rt2x00dev, HT_BASIC_RATE, 0x00008003);
+	}
 
-	rt2800_register_read(rt2x00dev, BKOFF_SLOT_CFG, &reg);
-	rt2x00_set_field32(&reg, BKOFF_SLOT_CFG_SLOT_TIME, erp->slot_time);
-	rt2800_register_write(rt2x00dev, BKOFF_SLOT_CFG, reg);
+	if (changed & BSS_CHANGED_ERP_SLOT) {
+		rt2800_register_read(rt2x00dev, BKOFF_SLOT_CFG, &reg);
+		rt2x00_set_field32(&reg, BKOFF_SLOT_CFG_SLOT_TIME,
+				   erp->slot_time);
+		rt2800_register_write(rt2x00dev, BKOFF_SLOT_CFG, reg);
 
-	rt2800_register_read(rt2x00dev, XIFS_TIME_CFG, &reg);
-	rt2x00_set_field32(&reg, XIFS_TIME_CFG_EIFS, erp->eifs);
-	rt2800_register_write(rt2x00dev, XIFS_TIME_CFG, reg);
+		rt2800_register_read(rt2x00dev, XIFS_TIME_CFG, &reg);
+		rt2x00_set_field32(&reg, XIFS_TIME_CFG_EIFS, erp->eifs);
+		rt2800_register_write(rt2x00dev, XIFS_TIME_CFG, reg);
+	}
 
-	rt2800_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
-	rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_INTERVAL,
-			   erp->beacon_int * 16);
-	rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
+	if (changed & BSS_CHANGED_BEACON_INT) {
+		rt2800_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
+		rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_INTERVAL,
+				   erp->beacon_int * 16);
+		rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
+	}
 }
 EXPORT_SYMBOL_GPL(rt2800_config_erp);
 
@@ -2053,6 +2065,14 @@
 	rt2800_register_write(rt2x00dev, LG_FBK_CFG1, reg);
 
 	/*
+	 * Do not force the BA window size, we use the TXWI to set it
+	 */
+	rt2800_register_read(rt2x00dev, AMPDU_BA_WINSIZE, &reg);
+	rt2x00_set_field32(&reg, AMPDU_BA_WINSIZE_FORCE_WINSIZE_ENABLE, 0);
+	rt2x00_set_field32(&reg, AMPDU_BA_WINSIZE_FORCE_WINSIZE, 0);
+	rt2800_register_write(rt2x00dev, AMPDU_BA_WINSIZE, reg);
+
+	/*
 	 * We must clear the error counters.
 	 * These registers are cleared on read,
 	 * so we may pass a useless variable to store the value.
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.h b/drivers/net/wireless/rt2x00/rt2800lib.h
index 986229c..600c5eb 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.h
+++ b/drivers/net/wireless/rt2x00/rt2800lib.h
@@ -169,7 +169,8 @@
 			  const unsigned int filter_flags);
 void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf,
 			struct rt2x00intf_conf *conf, const unsigned int flags);
-void rt2800_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp);
+void rt2800_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp,
+		       u32 changed);
 void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant);
 void rt2800_config(struct rt2x00_dev *rt2x00dev,
 		   struct rt2x00lib_conf *libconf,
diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c
index 2bcb150..005ee15 100644
--- a/drivers/net/wireless/rt2x00/rt2800pci.c
+++ b/drivers/net/wireless/rt2x00/rt2800pci.c
@@ -342,24 +342,24 @@
 	}
 
 	rt2800_register_read(rt2x00dev, INT_MASK_CSR, &reg);
-	rt2x00_set_field32(&reg, INT_MASK_CSR_RXDELAYINT, mask);
-	rt2x00_set_field32(&reg, INT_MASK_CSR_TXDELAYINT, mask);
+	rt2x00_set_field32(&reg, INT_MASK_CSR_RXDELAYINT, 0);
+	rt2x00_set_field32(&reg, INT_MASK_CSR_TXDELAYINT, 0);
 	rt2x00_set_field32(&reg, INT_MASK_CSR_RX_DONE, mask);
-	rt2x00_set_field32(&reg, INT_MASK_CSR_AC0_DMA_DONE, mask);
-	rt2x00_set_field32(&reg, INT_MASK_CSR_AC1_DMA_DONE, mask);
-	rt2x00_set_field32(&reg, INT_MASK_CSR_AC2_DMA_DONE, mask);
-	rt2x00_set_field32(&reg, INT_MASK_CSR_AC3_DMA_DONE, mask);
-	rt2x00_set_field32(&reg, INT_MASK_CSR_HCCA_DMA_DONE, mask);
-	rt2x00_set_field32(&reg, INT_MASK_CSR_MGMT_DMA_DONE, mask);
-	rt2x00_set_field32(&reg, INT_MASK_CSR_MCU_COMMAND, mask);
-	rt2x00_set_field32(&reg, INT_MASK_CSR_RXTX_COHERENT, mask);
+	rt2x00_set_field32(&reg, INT_MASK_CSR_AC0_DMA_DONE, 0);
+	rt2x00_set_field32(&reg, INT_MASK_CSR_AC1_DMA_DONE, 0);
+	rt2x00_set_field32(&reg, INT_MASK_CSR_AC2_DMA_DONE, 0);
+	rt2x00_set_field32(&reg, INT_MASK_CSR_AC3_DMA_DONE, 0);
+	rt2x00_set_field32(&reg, INT_MASK_CSR_HCCA_DMA_DONE, 0);
+	rt2x00_set_field32(&reg, INT_MASK_CSR_MGMT_DMA_DONE, 0);
+	rt2x00_set_field32(&reg, INT_MASK_CSR_MCU_COMMAND, 0);
+	rt2x00_set_field32(&reg, INT_MASK_CSR_RXTX_COHERENT, 0);
 	rt2x00_set_field32(&reg, INT_MASK_CSR_TBTT, mask);
 	rt2x00_set_field32(&reg, INT_MASK_CSR_PRE_TBTT, mask);
 	rt2x00_set_field32(&reg, INT_MASK_CSR_TX_FIFO_STATUS, mask);
 	rt2x00_set_field32(&reg, INT_MASK_CSR_AUTO_WAKEUP, mask);
-	rt2x00_set_field32(&reg, INT_MASK_CSR_GPTIMER, mask);
-	rt2x00_set_field32(&reg, INT_MASK_CSR_RX_COHERENT, mask);
-	rt2x00_set_field32(&reg, INT_MASK_CSR_TX_COHERENT, mask);
+	rt2x00_set_field32(&reg, INT_MASK_CSR_GPTIMER, 0);
+	rt2x00_set_field32(&reg, INT_MASK_CSR_RX_COHERENT, 0);
+	rt2x00_set_field32(&reg, INT_MASK_CSR_TX_COHERENT, 0);
 	rt2800_register_write(rt2x00dev, INT_MASK_CSR, reg);
 }
 
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h
index 0ae942c..7832a59 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/rt2x00/rt2x00.h
@@ -596,7 +596,8 @@
 #define CONFIG_UPDATE_BSSID		( 1 << 3 )
 
 	void (*config_erp) (struct rt2x00_dev *rt2x00dev,
-			    struct rt2x00lib_erp *erp);
+			    struct rt2x00lib_erp *erp,
+			    u32 changed);
 	void (*config_ant) (struct rt2x00_dev *rt2x00dev,
 			    struct antenna_setup *ant);
 	void (*config) (struct rt2x00_dev *rt2x00dev,
diff --git a/drivers/net/wireless/rt2x00/rt2x00config.c b/drivers/net/wireless/rt2x00/rt2x00config.c
index 34f34fa..4c7ff76 100644
--- a/drivers/net/wireless/rt2x00/rt2x00config.c
+++ b/drivers/net/wireless/rt2x00/rt2x00config.c
@@ -81,7 +81,8 @@
 
 void rt2x00lib_config_erp(struct rt2x00_dev *rt2x00dev,
 			  struct rt2x00_intf *intf,
-			  struct ieee80211_bss_conf *bss_conf)
+			  struct ieee80211_bss_conf *bss_conf,
+			  u32 changed)
 {
 	struct rt2x00lib_erp erp;
 
@@ -102,7 +103,7 @@
 	/* Update global beacon interval time, this is needed for PS support */
 	rt2x00dev->beacon_int = bss_conf->beacon_int;
 
-	rt2x00dev->ops->lib->config_erp(rt2x00dev, &erp);
+	rt2x00dev->ops->lib->config_erp(rt2x00dev, &erp, changed);
 }
 
 static inline
@@ -129,12 +130,12 @@
 	 */
 	if (!(ant->flags & ANTENNA_RX_DIVERSITY))
 		config.rx = rt2x00lib_config_antenna_check(config.rx, def->rx);
-	else
+	else if(config.rx == ANTENNA_SW_DIVERSITY)
 		config.rx = active->rx;
 
 	if (!(ant->flags & ANTENNA_TX_DIVERSITY))
 		config.tx = rt2x00lib_config_antenna_check(config.tx, def->tx);
-	else
+	else if (config.tx == ANTENNA_SW_DIVERSITY)
 		config.tx = active->tx;
 
 	/*
diff --git a/drivers/net/wireless/rt2x00/rt2x00lib.h b/drivers/net/wireless/rt2x00/rt2x00lib.h
index dc5c657..70c85ac 100644
--- a/drivers/net/wireless/rt2x00/rt2x00lib.h
+++ b/drivers/net/wireless/rt2x00/rt2x00lib.h
@@ -86,7 +86,8 @@
 			   const u8 *mac, const u8 *bssid);
 void rt2x00lib_config_erp(struct rt2x00_dev *rt2x00dev,
 			  struct rt2x00_intf *intf,
-			  struct ieee80211_bss_conf *conf);
+			  struct ieee80211_bss_conf *conf,
+			  u32 changed);
 void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev,
 			      struct antenna_setup ant);
 void rt2x00lib_config(struct rt2x00_dev *rt2x00dev,
diff --git a/drivers/net/wireless/rt2x00/rt2x00link.c b/drivers/net/wireless/rt2x00/rt2x00link.c
index 666cef3..4d534e9 100644
--- a/drivers/net/wireless/rt2x00/rt2x00link.c
+++ b/drivers/net/wireless/rt2x00/rt2x00link.c
@@ -188,7 +188,6 @@
 static bool rt2x00lib_antenna_diversity(struct rt2x00_dev *rt2x00dev)
 {
 	struct link_ant *ant = &rt2x00dev->link.ant;
-	unsigned int flags = ant->flags;
 
 	/*
 	 * Determine if software diversity is enabled for
@@ -196,13 +195,13 @@
 	 * Always perform this check since within the link
 	 * tuner interval the configuration might have changed.
 	 */
-	flags &= ~ANTENNA_RX_DIVERSITY;
-	flags &= ~ANTENNA_TX_DIVERSITY;
+	ant->flags &= ~ANTENNA_RX_DIVERSITY;
+	ant->flags &= ~ANTENNA_TX_DIVERSITY;
 
 	if (rt2x00dev->default_ant.rx == ANTENNA_SW_DIVERSITY)
-		flags |= ANTENNA_RX_DIVERSITY;
+		ant->flags |= ANTENNA_RX_DIVERSITY;
 	if (rt2x00dev->default_ant.tx == ANTENNA_SW_DIVERSITY)
-		flags |= ANTENNA_TX_DIVERSITY;
+		ant->flags |= ANTENNA_TX_DIVERSITY;
 
 	if (!(ant->flags & ANTENNA_RX_DIVERSITY) &&
 	    !(ant->flags & ANTENNA_TX_DIVERSITY)) {
@@ -210,9 +209,6 @@
 		return true;
 	}
 
-	/* Update flags */
-	ant->flags = flags;
-
 	/*
 	 * If we have only sampled the data over the last period
 	 * we should now harvest the data. Otherwise just evaluate
diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c
index 235e037..7862a84 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mac.c
+++ b/drivers/net/wireless/rt2x00/rt2x00mac.c
@@ -669,8 +669,10 @@
 	 * When the erp information has changed, we should perform
 	 * additional configuration steps. For all other changes we are done.
 	 */
-	if (changes & ~(BSS_CHANGED_ASSOC | BSS_CHANGED_HT))
-		rt2x00lib_config_erp(rt2x00dev, intf, bss_conf);
+	if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_ERP_PREAMBLE |
+		       BSS_CHANGED_ERP_SLOT | BSS_CHANGED_BASIC_RATES |
+		       BSS_CHANGED_BEACON_INT))
+		rt2x00lib_config_erp(rt2x00dev, intf, bss_conf, changes);
 }
 EXPORT_SYMBOL_GPL(rt2x00mac_bss_info_changed);
 
diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c
index 3a77599..97b3935 100644
--- a/drivers/net/wireless/rt2x00/rt61pci.c
+++ b/drivers/net/wireless/rt2x00/rt61pci.c
@@ -594,7 +594,8 @@
 }
 
 static void rt61pci_config_erp(struct rt2x00_dev *rt2x00dev,
-			       struct rt2x00lib_erp *erp)
+			       struct rt2x00lib_erp *erp,
+			       u32 changed)
 {
 	u32 reg;
 
@@ -603,28 +604,36 @@
 	rt2x00_set_field32(&reg, TXRX_CSR0_TSF_OFFSET, IEEE80211_HEADER);
 	rt2x00pci_register_write(rt2x00dev, TXRX_CSR0, reg);
 
-	rt2x00pci_register_read(rt2x00dev, TXRX_CSR4, &reg);
-	rt2x00_set_field32(&reg, TXRX_CSR4_AUTORESPOND_ENABLE, 1);
-	rt2x00_set_field32(&reg, TXRX_CSR4_AUTORESPOND_PREAMBLE,
-			   !!erp->short_preamble);
-	rt2x00pci_register_write(rt2x00dev, TXRX_CSR4, reg);
+	if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+		rt2x00pci_register_read(rt2x00dev, TXRX_CSR4, &reg);
+		rt2x00_set_field32(&reg, TXRX_CSR4_AUTORESPOND_ENABLE, 1);
+		rt2x00_set_field32(&reg, TXRX_CSR4_AUTORESPOND_PREAMBLE,
+				   !!erp->short_preamble);
+		rt2x00pci_register_write(rt2x00dev, TXRX_CSR4, reg);
+	}
 
-	rt2x00pci_register_write(rt2x00dev, TXRX_CSR5, erp->basic_rates);
+	if (changed & BSS_CHANGED_BASIC_RATES)
+		rt2x00pci_register_write(rt2x00dev, TXRX_CSR5,
+					 erp->basic_rates);
 
-	rt2x00pci_register_read(rt2x00dev, TXRX_CSR9, &reg);
-	rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_INTERVAL,
-			   erp->beacon_int * 16);
-	rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, reg);
+	if (changed & BSS_CHANGED_BEACON_INT) {
+		rt2x00pci_register_read(rt2x00dev, TXRX_CSR9, &reg);
+		rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_INTERVAL,
+				   erp->beacon_int * 16);
+		rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, reg);
+	}
 
-	rt2x00pci_register_read(rt2x00dev, MAC_CSR9, &reg);
-	rt2x00_set_field32(&reg, MAC_CSR9_SLOT_TIME, erp->slot_time);
-	rt2x00pci_register_write(rt2x00dev, MAC_CSR9, reg);
+	if (changed & BSS_CHANGED_ERP_SLOT) {
+		rt2x00pci_register_read(rt2x00dev, MAC_CSR9, &reg);
+		rt2x00_set_field32(&reg, MAC_CSR9_SLOT_TIME, erp->slot_time);
+		rt2x00pci_register_write(rt2x00dev, MAC_CSR9, reg);
 
-	rt2x00pci_register_read(rt2x00dev, MAC_CSR8, &reg);
-	rt2x00_set_field32(&reg, MAC_CSR8_SIFS, erp->sifs);
-	rt2x00_set_field32(&reg, MAC_CSR8_SIFS_AFTER_RX_OFDM, 3);
-	rt2x00_set_field32(&reg, MAC_CSR8_EIFS, erp->eifs);
-	rt2x00pci_register_write(rt2x00dev, MAC_CSR8, reg);
+		rt2x00pci_register_read(rt2x00dev, MAC_CSR8, &reg);
+		rt2x00_set_field32(&reg, MAC_CSR8_SIFS, erp->sifs);
+		rt2x00_set_field32(&reg, MAC_CSR8_SIFS_AFTER_RX_OFDM, 3);
+		rt2x00_set_field32(&reg, MAC_CSR8_EIFS, erp->eifs);
+		rt2x00pci_register_write(rt2x00dev, MAC_CSR8, reg);
+	}
 }
 
 static void rt61pci_config_antenna_5x(struct rt2x00_dev *rt2x00dev,
@@ -1645,6 +1654,7 @@
 	rt2x00pci_register_read(rt2x00dev, INT_MASK_CSR, &reg);
 	rt2x00_set_field32(&reg, INT_MASK_CSR_TXDONE, mask);
 	rt2x00_set_field32(&reg, INT_MASK_CSR_RXDONE, mask);
+	rt2x00_set_field32(&reg, INT_MASK_CSR_BEACON_DONE, mask);
 	rt2x00_set_field32(&reg, INT_MASK_CSR_ENABLE_MITIGATION, mask);
 	rt2x00_set_field32(&reg, INT_MASK_CSR_MITIGATION_PERIOD, 0xff);
 	rt2x00pci_register_write(rt2x00dev, INT_MASK_CSR, reg);
@@ -1658,6 +1668,7 @@
 	rt2x00_set_field32(&reg, MCU_INT_MASK_CSR_5, mask);
 	rt2x00_set_field32(&reg, MCU_INT_MASK_CSR_6, mask);
 	rt2x00_set_field32(&reg, MCU_INT_MASK_CSR_7, mask);
+	rt2x00_set_field32(&reg, MCU_INT_MASK_CSR_TWAKEUP, mask);
 	rt2x00pci_register_write(rt2x00dev, MCU_INT_MASK_CSR, reg);
 }
 
@@ -2106,7 +2117,7 @@
 				"TX status report missed for entry %d\n",
 				entry_done->entry_idx);
 
-			rt2x00lib_txdone_noinfo(entry, TXDONE_UNKNOWN);
+			rt2x00lib_txdone_noinfo(entry_done, TXDONE_UNKNOWN);
 			entry_done = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
 		}
 
diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c
index 87fb220..e22f01c 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.c
+++ b/drivers/net/wireless/rt2x00/rt73usb.c
@@ -545,7 +545,8 @@
 }
 
 static void rt73usb_config_erp(struct rt2x00_dev *rt2x00dev,
-			       struct rt2x00lib_erp *erp)
+			       struct rt2x00lib_erp *erp,
+			       u32 changed)
 {
 	u32 reg;
 
@@ -554,28 +555,36 @@
 	rt2x00_set_field32(&reg, TXRX_CSR0_TSF_OFFSET, IEEE80211_HEADER);
 	rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg);
 
-	rt2x00usb_register_read(rt2x00dev, TXRX_CSR4, &reg);
-	rt2x00_set_field32(&reg, TXRX_CSR4_AUTORESPOND_ENABLE, 1);
-	rt2x00_set_field32(&reg, TXRX_CSR4_AUTORESPOND_PREAMBLE,
-			   !!erp->short_preamble);
-	rt2x00usb_register_write(rt2x00dev, TXRX_CSR4, reg);
+	if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+		rt2x00usb_register_read(rt2x00dev, TXRX_CSR4, &reg);
+		rt2x00_set_field32(&reg, TXRX_CSR4_AUTORESPOND_ENABLE, 1);
+		rt2x00_set_field32(&reg, TXRX_CSR4_AUTORESPOND_PREAMBLE,
+				   !!erp->short_preamble);
+		rt2x00usb_register_write(rt2x00dev, TXRX_CSR4, reg);
+	}
 
-	rt2x00usb_register_write(rt2x00dev, TXRX_CSR5, erp->basic_rates);
+	if (changed & BSS_CHANGED_BASIC_RATES)
+		rt2x00usb_register_write(rt2x00dev, TXRX_CSR5,
+					 erp->basic_rates);
 
-	rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, &reg);
-	rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_INTERVAL,
-			   erp->beacon_int * 16);
-	rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg);
+	if (changed & BSS_CHANGED_BEACON_INT) {
+		rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, &reg);
+		rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_INTERVAL,
+				   erp->beacon_int * 16);
+		rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg);
+	}
 
-	rt2x00usb_register_read(rt2x00dev, MAC_CSR9, &reg);
-	rt2x00_set_field32(&reg, MAC_CSR9_SLOT_TIME, erp->slot_time);
-	rt2x00usb_register_write(rt2x00dev, MAC_CSR9, reg);
+	if (changed & BSS_CHANGED_ERP_SLOT) {
+		rt2x00usb_register_read(rt2x00dev, MAC_CSR9, &reg);
+		rt2x00_set_field32(&reg, MAC_CSR9_SLOT_TIME, erp->slot_time);
+		rt2x00usb_register_write(rt2x00dev, MAC_CSR9, reg);
 
-	rt2x00usb_register_read(rt2x00dev, MAC_CSR8, &reg);
-	rt2x00_set_field32(&reg, MAC_CSR8_SIFS, erp->sifs);
-	rt2x00_set_field32(&reg, MAC_CSR8_SIFS_AFTER_RX_OFDM, 3);
-	rt2x00_set_field32(&reg, MAC_CSR8_EIFS, erp->eifs);
-	rt2x00usb_register_write(rt2x00dev, MAC_CSR8, reg);
+		rt2x00usb_register_read(rt2x00dev, MAC_CSR8, &reg);
+		rt2x00_set_field32(&reg, MAC_CSR8_SIFS, erp->sifs);
+		rt2x00_set_field32(&reg, MAC_CSR8_SIFS_AFTER_RX_OFDM, 3);
+		rt2x00_set_field32(&reg, MAC_CSR8_EIFS, erp->eifs);
+		rt2x00usb_register_write(rt2x00dev, MAC_CSR8, reg);
+	}
 }
 
 static void rt73usb_config_antenna_5x(struct rt2x00_dev *rt2x00dev,
diff --git a/drivers/net/wireless/wl12xx/Kconfig b/drivers/net/wireless/wl12xx/Kconfig
index 2f98058..4a8bb25 100644
--- a/drivers/net/wireless/wl12xx/Kconfig
+++ b/drivers/net/wireless/wl12xx/Kconfig
@@ -74,4 +74,7 @@
 	  If you choose to build a module, it'll be called
 	  wl1271_sdio. Say N if unsure.
 
-
+config WL12XX_PLATFORM_DATA
+	bool
+	depends on WL1271_SDIO != n
+	default y
diff --git a/drivers/net/wireless/wl12xx/wl1251_sdio.c b/drivers/net/wireless/wl12xx/wl1251_sdio.c
index c0b68b0..74ba9ce 100644
--- a/drivers/net/wireless/wl12xx/wl1251_sdio.c
+++ b/drivers/net/wireless/wl12xx/wl1251_sdio.c
@@ -24,7 +24,7 @@
 #include <linux/mmc/sdio_func.h>
 #include <linux/mmc/sdio_ids.h>
 #include <linux/platform_device.h>
-#include <linux/spi/wl12xx.h>
+#include <linux/wl12xx.h>
 #include <linux/irq.h>
 
 #include "wl1251.h"
diff --git a/drivers/net/wireless/wl12xx/wl1251_spi.c b/drivers/net/wireless/wl12xx/wl1251_spi.c
index 334ded9..320de79 100644
--- a/drivers/net/wireless/wl12xx/wl1251_spi.c
+++ b/drivers/net/wireless/wl12xx/wl1251_spi.c
@@ -24,7 +24,7 @@
 #include <linux/slab.h>
 #include <linux/crc7.h>
 #include <linux/spi/spi.h>
-#include <linux/spi/wl12xx.h>
+#include <linux/wl12xx.h>
 
 #include "wl1251.h"
 #include "wl1251_reg.h"
diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h
index dd3cee6..4134f44 100644
--- a/drivers/net/wireless/wl12xx/wl1271.h
+++ b/drivers/net/wireless/wl12xx/wl1271.h
@@ -313,7 +313,7 @@
 		     bool fixed);
 	void (*reset)(struct wl1271 *wl);
 	void (*init)(struct wl1271 *wl);
-	void (*power)(struct wl1271 *wl, bool enable);
+	int (*power)(struct wl1271 *wl, bool enable);
 	struct device* (*dev)(struct wl1271 *wl);
 	void (*enable_irq)(struct wl1271 *wl);
 	void (*disable_irq)(struct wl1271 *wl);
@@ -330,6 +330,7 @@
 
 	void (*set_power)(bool enable);
 	int irq;
+	int ref_clock;
 
 	spinlock_t wl_lock;
 
diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.c b/drivers/net/wireless/wl12xx/wl1271_boot.c
index f36430b..fc21db8 100644
--- a/drivers/net/wireless/wl12xx/wl1271_boot.c
+++ b/drivers/net/wireless/wl12xx/wl1271_boot.c
@@ -457,17 +457,20 @@
 {
 	int ret = 0;
 	u32 tmp, clk, pause;
+	int ref_clock = wl->ref_clock;
 
 	wl1271_boot_hw_version(wl);
 
-	if (REF_CLOCK == 0 || REF_CLOCK == 2 || REF_CLOCK == 4)
+	if (ref_clock == 0 || ref_clock == 2 || ref_clock == 4)
 		/* ref clk: 19.2/38.4/38.4-XTAL */
 		clk = 0x3;
-	else if (REF_CLOCK == 1 || REF_CLOCK == 3)
+	else if (ref_clock == 1 || ref_clock == 3)
 		/* ref clk: 26/52 */
 		clk = 0x5;
+	else
+		return -EINVAL;
 
-	if (REF_CLOCK != 0) {
+	if (ref_clock != 0) {
 		u16 val;
 		/* Set clock type (open drain) */
 		val = wl1271_top_reg_read(wl, OCP_REG_CLK_TYPE);
@@ -516,7 +519,7 @@
 	wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk);
 
 	/* 2 */
-	clk |= (REF_CLOCK << 1) << 4;
+	clk |= (ref_clock << 1) << 4;
 	wl1271_write32(wl, DRPW_SCRATCH_START, clk);
 
 	wl1271_set_partition(wl, &part_table[PART_WORK]);
diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.h b/drivers/net/wireless/wl12xx/wl1271_boot.h
index f829699..f73b0b1 100644
--- a/drivers/net/wireless/wl12xx/wl1271_boot.h
+++ b/drivers/net/wireless/wl12xx/wl1271_boot.h
@@ -46,7 +46,6 @@
 /* delay between retries */
 #define INIT_LOOP_DELAY 50
 
-#define REF_CLOCK            2
 #define WU_COUNTER_PAUSE_VAL 0x3FF
 #define WELP_ARM_COMMAND_VAL 0x4
 
diff --git a/drivers/net/wireless/wl12xx/wl1271_io.h b/drivers/net/wireless/wl12xx/wl1271_io.h
index bc806c7..c1f92e6 100644
--- a/drivers/net/wireless/wl12xx/wl1271_io.h
+++ b/drivers/net/wireless/wl12xx/wl1271_io.h
@@ -144,10 +144,13 @@
 	clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
 }
 
-static inline void wl1271_power_on(struct wl1271 *wl)
+static inline int wl1271_power_on(struct wl1271 *wl)
 {
-	wl->if_ops->power(wl, true);
-	set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
+	int ret = wl->if_ops->power(wl, true);
+	if (ret == 0)
+		set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
+
+	return ret;
 }
 
 
diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
index 8e55cf8..776cd7c4 100644
--- a/drivers/net/wireless/wl12xx/wl1271_main.c
+++ b/drivers/net/wireless/wl12xx/wl1271_main.c
@@ -621,7 +621,9 @@
 	int ret = 0;
 
 	msleep(WL1271_PRE_POWER_ON_SLEEP);
-	wl1271_power_on(wl);
+	ret = wl1271_power_on(wl);
+	if (ret < 0)
+		goto out;
 	msleep(WL1271_POWER_ON_SLEEP);
 	wl1271_io_reset(wl);
 	wl1271_io_init(wl);
@@ -1632,7 +1634,7 @@
 	if (ret < 0)
 		goto out;
 
-	if ((changed && BSS_CHANGED_BEACON_INT) &&
+	if ((changed & BSS_CHANGED_BEACON_INT) &&
 	    (wl->bss_type == BSS_TYPE_IBSS)) {
 		wl1271_debug(DEBUG_ADHOC, "ad-hoc beacon interval updated: %d",
 			bss_conf->beacon_int);
@@ -1641,7 +1643,7 @@
 		do_join = true;
 	}
 
-	if ((changed && BSS_CHANGED_BEACON) &&
+	if ((changed & BSS_CHANGED_BEACON) &&
 	    (wl->bss_type == BSS_TYPE_IBSS)) {
 		struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
 
diff --git a/drivers/net/wireless/wl12xx/wl1271_rx.c b/drivers/net/wireless/wl12xx/wl1271_rx.c
index 019aa79..94da5dd 100644
--- a/drivers/net/wireless/wl12xx/wl1271_rx.c
+++ b/drivers/net/wireless/wl12xx/wl1271_rx.c
@@ -76,7 +76,6 @@
 
 static void wl1271_rx_handle_data(struct wl1271 *wl, u32 length)
 {
-	struct ieee80211_rx_status rx_status;
 	struct wl1271_rx_descriptor *desc;
 	struct sk_buff *skb;
 	u16 *fc;
@@ -109,14 +108,13 @@
 	if ((*fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON)
 		beacon = 1;
 
-	wl1271_rx_status(wl, desc, &rx_status, beacon);
+	wl1271_rx_status(wl, desc, IEEE80211_SKB_RXCB(skb), beacon);
 
 	wl1271_debug(DEBUG_RX, "rx skb 0x%p: %d B %s", skb, skb->len,
 		     beacon ? "beacon" : "");
 
 	skb_trim(skb, skb->len - desc->pad_len);
 
-	memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
 	ieee80211_rx_ni(wl->hw, skb);
 }
 
diff --git a/drivers/net/wireless/wl12xx/wl1271_sdio.c b/drivers/net/wireless/wl12xx/wl1271_sdio.c
index 7059b5c..f2f0466 100644
--- a/drivers/net/wireless/wl12xx/wl1271_sdio.c
+++ b/drivers/net/wireless/wl12xx/wl1271_sdio.c
@@ -29,14 +29,12 @@
 #include <linux/mmc/sdio_ids.h>
 #include <linux/mmc/card.h>
 #include <linux/gpio.h>
+#include <linux/wl12xx.h>
 
 #include "wl1271.h"
 #include "wl12xx_80211.h"
 #include "wl1271_io.h"
 
-
-#define RX71_WL1271_IRQ_GPIO		42
-
 #ifndef SDIO_VENDOR_ID_TI
 #define SDIO_VENDOR_ID_TI		0x0097
 #endif
@@ -107,6 +105,8 @@
 	int ret;
 	struct sdio_func *func = wl_to_func(wl);
 
+	sdio_claim_host(func);
+
 	if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) {
 		((u8 *)buf)[0] = sdio_f0_readb(func, addr, &ret);
 		wl1271_debug(DEBUG_SDIO, "sdio read 52 addr 0x%x, byte 0x%02x",
@@ -122,9 +122,10 @@
 		wl1271_dump_ascii(DEBUG_SDIO, "data: ", buf, len);
 	}
 
+	sdio_release_host(func);
+
 	if (ret)
 		wl1271_error("sdio read failed (%d)", ret);
-
 }
 
 static void wl1271_sdio_raw_write(struct wl1271 *wl, int addr, void *buf,
@@ -133,6 +134,8 @@
 	int ret;
 	struct sdio_func *func = wl_to_func(wl);
 
+	sdio_claim_host(func);
+
 	if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) {
 		sdio_f0_writeb(func, ((u8 *)buf)[0], addr, &ret);
 		wl1271_debug(DEBUG_SDIO, "sdio write 52 addr 0x%x, byte 0x%02x",
@@ -147,26 +150,45 @@
 		else
 			ret = sdio_memcpy_toio(func, addr, buf, len);
 	}
+
+	sdio_release_host(func);
+
 	if (ret)
 		wl1271_error("sdio write failed (%d)", ret);
-
 }
 
-static void wl1271_sdio_set_power(struct wl1271 *wl, bool enable)
+static int wl1271_sdio_power_on(struct wl1271 *wl)
 {
 	struct sdio_func *func = wl_to_func(wl);
 
+	sdio_claim_host(func);
+	sdio_enable_func(func);
+	sdio_release_host(func);
+
+	return 0;
+}
+
+static int wl1271_sdio_power_off(struct wl1271 *wl)
+{
+	struct sdio_func *func = wl_to_func(wl);
+
+	sdio_claim_host(func);
+	sdio_disable_func(func);
+	sdio_release_host(func);
+
+	return 0;
+}
+
+static int wl1271_sdio_set_power(struct wl1271 *wl, bool enable)
+{
 	/* Let the SDIO stack handle wlan_enable control, so we
 	 * keep host claimed while wlan is in use to keep wl1271
 	 * alive.
 	 */
-	if (enable) {
-		sdio_claim_host(func);
-		sdio_enable_func(func);
-	} else {
-		sdio_disable_func(func);
-		sdio_release_host(func);
-	}
+	if (enable)
+		return wl1271_sdio_power_on(wl);
+	else
+		return wl1271_sdio_power_off(wl);
 }
 
 static struct wl1271_if_operations sdio_ops = {
@@ -184,6 +206,7 @@
 				  const struct sdio_device_id *id)
 {
 	struct ieee80211_hw *hw;
+	const struct wl12xx_platform_data *wlan_data;
 	struct wl1271 *wl;
 	int ret;
 
@@ -203,13 +226,16 @@
 	/* Grab access to FN0 for ELP reg. */
 	func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
 
-	wl->irq = gpio_to_irq(RX71_WL1271_IRQ_GPIO);
-	if (wl->irq < 0) {
-		ret = wl->irq;
-		wl1271_error("could not get irq!");
+	wlan_data = wl12xx_get_platform_data();
+	if (IS_ERR(wlan_data)) {
+		ret = PTR_ERR(wlan_data);
+		wl1271_error("missing wlan platform data: %d", ret);
 		goto out_free;
 	}
 
+	wl->irq = wlan_data->irq;
+	wl->ref_clock = wlan_data->board_ref_clock;
+
 	ret = request_irq(wl->irq, wl1271_irq, 0, DRIVER_NAME, wl);
 	if (ret < 0) {
 		wl1271_error("request_irq() failed: %d", ret);
diff --git a/drivers/net/wireless/wl12xx/wl1271_spi.c b/drivers/net/wireless/wl12xx/wl1271_spi.c
index 4cb99c5..ced0a9e 100644
--- a/drivers/net/wireless/wl12xx/wl1271_spi.c
+++ b/drivers/net/wireless/wl12xx/wl1271_spi.c
@@ -25,7 +25,7 @@
 #include <linux/module.h>
 #include <linux/crc7.h>
 #include <linux/spi/spi.h>
-#include <linux/spi/wl12xx.h>
+#include <linux/wl12xx.h>
 #include <linux/slab.h>
 
 #include "wl1271.h"
@@ -312,10 +312,12 @@
 	return IRQ_HANDLED;
 }
 
-static void wl1271_spi_set_power(struct wl1271 *wl, bool enable)
+static int wl1271_spi_set_power(struct wl1271 *wl, bool enable)
 {
 	if (wl->set_power)
 		wl->set_power(enable);
+
+	return 0;
 }
 
 static struct wl1271_if_operations spi_ops = {
@@ -370,6 +372,8 @@
 		goto out_free;
 	}
 
+	wl->ref_clock = pdata->board_ref_clock;
+
 	wl->irq = spi->irq;
 	if (wl->irq < 0) {
 		wl1271_error("irq missing in platform data");
diff --git a/drivers/net/wireless/wl12xx/wl12xx_platform_data.c b/drivers/net/wireless/wl12xx/wl12xx_platform_data.c
new file mode 100644
index 0000000..973b110
--- /dev/null
+++ b/drivers/net/wireless/wl12xx/wl12xx_platform_data.c
@@ -0,0 +1,28 @@
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/wl12xx.h>
+
+static const struct wl12xx_platform_data *platform_data;
+
+int __init wl12xx_set_platform_data(const struct wl12xx_platform_data *data)
+{
+	if (platform_data)
+		return -EBUSY;
+	if (!data)
+		return -EINVAL;
+
+	platform_data = kmemdup(data, sizeof(*data), GFP_KERNEL);
+	if (!platform_data)
+		return -ENOMEM;
+
+	return 0;
+}
+
+const struct wl12xx_platform_data *wl12xx_get_platform_data(void)
+{
+	if (!platform_data)
+		return ERR_PTR(-ENODEV);
+
+	return platform_data;
+}
+EXPORT_SYMBOL(wl12xx_get_platform_data);
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index 31603e8..f0518b0 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -1020,6 +1020,8 @@
  * @NL80211_IFTYPE_WDS: wireless distribution interface
  * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames
  * @NL80211_IFTYPE_MESH_POINT: mesh point
+ * @NL80211_IFTYPE_P2P_CLIENT: P2P client
+ * @NL80211_IFTYPE_P2P_GO: P2P group owner
  * @NL80211_IFTYPE_MAX: highest interface type number currently defined
  * @NUM_NL80211_IFTYPES: number of defined interface types
  *
@@ -1036,6 +1038,8 @@
 	NL80211_IFTYPE_WDS,
 	NL80211_IFTYPE_MONITOR,
 	NL80211_IFTYPE_MESH_POINT,
+	NL80211_IFTYPE_P2P_CLIENT,
+	NL80211_IFTYPE_P2P_GO,
 
 	/* keep last */
 	NUM_NL80211_IFTYPES,
diff --git a/include/linux/spi/wl12xx.h b/include/linux/wl12xx.h
similarity index 82%
rename from include/linux/spi/wl12xx.h
rename to include/linux/wl12xx.h
index a20bccf0..95deae3 100644
--- a/include/linux/spi/wl12xx.h
+++ b/include/linux/wl12xx.h
@@ -21,14 +21,18 @@
  *
  */
 
-#ifndef _LINUX_SPI_WL12XX_H
-#define _LINUX_SPI_WL12XX_H
+#ifndef _LINUX_WL12XX_H
+#define _LINUX_WL12XX_H
 
 struct wl12xx_platform_data {
 	void (*set_power)(bool enable);
 	/* SDIO only: IRQ number if WLAN_IRQ line is used, 0 for SDIO IRQs */
 	int irq;
 	bool use_eeprom;
+	int board_ref_clock;
 };
 
+int wl12xx_set_platform_data(const struct wl12xx_platform_data *data);
+const struct wl12xx_platform_data *wl12xx_get_platform_data(void);
+
 #endif
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 4c8c727..a0613ff 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -2558,49 +2558,36 @@
 /* wiphy_printk helpers, similar to dev_printk */
 
 #define wiphy_printk(level, wiphy, format, args...)		\
-	printk(level "%s: " format, wiphy_name(wiphy), ##args)
+	dev_printk(level, &(wiphy)->dev, format, ##args)
 #define wiphy_emerg(wiphy, format, args...)			\
-	wiphy_printk(KERN_EMERG, wiphy, format, ##args)
+	dev_emerg(&(wiphy)->dev, format, ##args)
 #define wiphy_alert(wiphy, format, args...)			\
-	wiphy_printk(KERN_ALERT, wiphy, format, ##args)
+	dev_alert(&(wiphy)->dev, format, ##args)
 #define wiphy_crit(wiphy, format, args...)			\
-	wiphy_printk(KERN_CRIT, wiphy, format, ##args)
+	dev_crit(&(wiphy)->dev, format, ##args)
 #define wiphy_err(wiphy, format, args...)			\
-	wiphy_printk(KERN_ERR, wiphy, format, ##args)
+	dev_err(&(wiphy)->dev, format, ##args)
 #define wiphy_warn(wiphy, format, args...)			\
-	wiphy_printk(KERN_WARNING, wiphy, format, ##args)
+	dev_warn(&(wiphy)->dev, format, ##args)
 #define wiphy_notice(wiphy, format, args...)			\
-	wiphy_printk(KERN_NOTICE, wiphy, format, ##args)
+	dev_notice(&(wiphy)->dev, format, ##args)
 #define wiphy_info(wiphy, format, args...)			\
-	wiphy_printk(KERN_INFO, wiphy, format, ##args)
+	dev_info(&(wiphy)->dev, format, ##args)
 
-int wiphy_debug(const struct wiphy *wiphy, const char *format, ...)
-	__attribute__ ((format (printf, 2, 3)));
-
-#if defined(DEBUG)
-#define wiphy_dbg(wiphy, format, args...)			\
+#define wiphy_debug(wiphy, format, args...)			\
 	wiphy_printk(KERN_DEBUG, wiphy, format, ##args)
-#elif defined(CONFIG_DYNAMIC_DEBUG)
+
 #define wiphy_dbg(wiphy, format, args...)			\
-	dynamic_pr_debug("%s: " format,	wiphy_name(wiphy), ##args)
-#else
-#define wiphy_dbg(wiphy, format, args...)				\
-({									\
-	if (0)								\
-		wiphy_printk(KERN_DEBUG, wiphy, format, ##args);	\
-	0;								\
-})
-#endif
+	dev_dbg(&(wiphy)->dev, format, ##args)
 
 #if defined(VERBOSE_DEBUG)
 #define wiphy_vdbg	wiphy_dbg
 #else
-
 #define wiphy_vdbg(wiphy, format, args...)				\
 ({									\
 	if (0)								\
 		wiphy_printk(KERN_DEBUG, wiphy, format, ##args);	\
-		0;							\
+	0;								\
 })
 #endif
 
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index f91fc33..12a49f0 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -769,6 +769,8 @@
  * @bss_conf: BSS configuration for this interface, either our own
  *	or the BSS we're associated to
  * @addr: address of this interface
+ * @p2p: indicates whether this AP or STA interface is a p2p
+ *	interface, i.e. a GO or p2p-sta respectively
  * @drv_priv: data area for driver use, will always be aligned to
  *	sizeof(void *).
  */
@@ -776,6 +778,7 @@
 	enum nl80211_iftype type;
 	struct ieee80211_bss_conf bss_conf;
 	u8 addr[ETH_ALEN];
+	bool p2p;
 	/* must be last */
 	u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *))));
 };
@@ -1701,7 +1704,7 @@
 			     struct ieee80211_vif *vif);
 	int (*change_interface)(struct ieee80211_hw *hw,
 				struct ieee80211_vif *vif,
-				enum nl80211_iftype new_type);
+				enum nl80211_iftype new_type, bool p2p);
 	void (*remove_interface)(struct ieee80211_hw *hw,
 				 struct ieee80211_vif *vif);
 	int (*config)(struct ieee80211_hw *hw, u32 changed);
@@ -2293,6 +2296,7 @@
  * This function allows the iterator function to sleep, when the iterator
  * function is atomic @ieee80211_iterate_active_interfaces_atomic can
  * be used.
+ * Does not iterate over a new interface during add_interface()
  *
  * @hw: the hardware struct of which the interfaces should be iterated over
  * @iterator: the iterator function to call
@@ -2310,6 +2314,7 @@
  * hardware that are currently active and calls the callback for them.
  * This function requires the iterator callback function to be atomic,
  * if that is not desired, use @ieee80211_iterate_active_interfaces instead.
+ * Does not iterate over a new interface during add_interface()
  *
  * @hw: the hardware struct of which the interfaces should be iterated over
  * @iterator: the iterator function to call, cannot sleep
@@ -2719,4 +2724,26 @@
 	return conf->channel_type != NL80211_CHAN_NO_HT;
 }
 
+static inline enum nl80211_iftype
+ieee80211_iftype_p2p(enum nl80211_iftype type, bool p2p)
+{
+	if (p2p) {
+		switch (type) {
+		case NL80211_IFTYPE_STATION:
+			return NL80211_IFTYPE_P2P_CLIENT;
+		case NL80211_IFTYPE_AP:
+			return NL80211_IFTYPE_P2P_GO;
+		default:
+			break;
+		}
+	}
+	return type;
+}
+
+static inline enum nl80211_iftype
+ieee80211_vif_type_p2p(struct ieee80211_vif *vif)
+{
+	return ieee80211_iftype_p2p(vif->type, vif->p2p);
+}
+
 #endif /* MAC80211_H */
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 5de1ca3..c981604 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -577,6 +577,7 @@
 				 struct sta_info *sta,
 				 struct station_parameters *params)
 {
+	unsigned long flags;
 	u32 rates;
 	int i, j;
 	struct ieee80211_supported_band *sband;
@@ -585,7 +586,7 @@
 
 	sband = local->hw.wiphy->bands[local->oper_channel->band];
 
-	spin_lock_bh(&sta->lock);
+	spin_lock_irqsave(&sta->flaglock, flags);
 	mask = params->sta_flags_mask;
 	set = params->sta_flags_set;
 
@@ -612,7 +613,7 @@
 		if (set & BIT(NL80211_STA_FLAG_MFP))
 			sta->flags |= WLAN_STA_MFP;
 	}
-	spin_unlock_bh(&sta->lock);
+	spin_unlock_irqrestore(&sta->flaglock, flags);
 
 	/*
 	 * cfg80211 validates this (1-2007) and allows setting the AID
@@ -1150,15 +1151,26 @@
 			  struct net_device *dev,
 			  struct cfg80211_scan_request *req)
 {
-	struct ieee80211_sub_if_data *sdata;
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
-	if (sdata->vif.type != NL80211_IFTYPE_STATION &&
-	    sdata->vif.type != NL80211_IFTYPE_ADHOC &&
-	    sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
-	    (sdata->vif.type != NL80211_IFTYPE_AP || sdata->u.ap.beacon))
+	switch (ieee80211_vif_type_p2p(&sdata->vif)) {
+	case NL80211_IFTYPE_STATION:
+	case NL80211_IFTYPE_ADHOC:
+	case NL80211_IFTYPE_MESH_POINT:
+	case NL80211_IFTYPE_P2P_CLIENT:
+		break;
+	case NL80211_IFTYPE_P2P_GO:
+		if (sdata->local->ops->hw_scan)
+			break;
+		/* FIXME: implement NoA while scanning in software */
 		return -EOPNOTSUPP;
+	case NL80211_IFTYPE_AP:
+		if (sdata->u.ap.beacon)
+			return -EOPNOTSUPP;
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
 
 	return ieee80211_request_scan(sdata, req);
 }
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 32be11e..5b24740 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -11,7 +11,7 @@
 {
 	struct ieee80211_sub_if_data *sdata;
 
-	WARN_ON(!mutex_is_locked(&local->iflist_mtx));
+	lockdep_assert_held(&local->iflist_mtx);
 
 	list_for_each_entry(sdata, &local->interfaces, list) {
 		if (sdata == ignore)
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 6064b7b..1698382 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -56,14 +56,14 @@
 
 static inline int drv_change_interface(struct ieee80211_local *local,
 				       struct ieee80211_sub_if_data *sdata,
-				       enum nl80211_iftype type)
+				       enum nl80211_iftype type, bool p2p)
 {
 	int ret;
 
 	might_sleep();
 
-	trace_drv_change_interface(local, sdata, type);
-	ret = local->ops->change_interface(&local->hw, &sdata->vif, type);
+	trace_drv_change_interface(local, sdata, type, p2p);
+	ret = local->ops->change_interface(&local->hw, &sdata->vif, type, p2p);
 	trace_drv_return_int(local, ret);
 	return ret;
 }
diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h
index f6f3d89..6831fb1 100644
--- a/net/mac80211/driver-trace.h
+++ b/net/mac80211/driver-trace.h
@@ -25,12 +25,14 @@
 #define STA_PR_FMT	" sta:%pM"
 #define STA_PR_ARG	__entry->sta_addr
 
-#define VIF_ENTRY	__field(enum nl80211_iftype, vif_type) __field(void *, sdata) \
+#define VIF_ENTRY	__field(enum nl80211_iftype, vif_type) __field(void *, sdata)	\
+			__field(bool, p2p)						\
 			__string(vif_name, sdata->dev ? sdata->dev->name : "<nodev>")
-#define VIF_ASSIGN	__entry->vif_type = sdata->vif.type; __entry->sdata = sdata; \
+#define VIF_ASSIGN	__entry->vif_type = sdata->vif.type; __entry->sdata = sdata;	\
+			__entry->p2p = sdata->vif.p2p;					\
 			__assign_str(vif_name, sdata->dev ? sdata->dev->name : "<nodev>")
-#define VIF_PR_FMT	" vif:%s(%d)"
-#define VIF_PR_ARG	__get_str(vif_name), __entry->vif_type
+#define VIF_PR_FMT	" vif:%s(%d%s)"
+#define VIF_PR_ARG	__get_str(vif_name), __entry->vif_type, __entry->p2p ? "/p2p" : ""
 
 /*
  * Tracing for driver callbacks.
@@ -139,25 +141,28 @@
 TRACE_EVENT(drv_change_interface,
 	TP_PROTO(struct ieee80211_local *local,
 		 struct ieee80211_sub_if_data *sdata,
-		 enum nl80211_iftype type),
+		 enum nl80211_iftype type, bool p2p),
 
-	TP_ARGS(local, sdata, type),
+	TP_ARGS(local, sdata, type, p2p),
 
 	TP_STRUCT__entry(
 		LOCAL_ENTRY
 		VIF_ENTRY
 		__field(u32, new_type)
+		__field(bool, new_p2p)
 	),
 
 	TP_fast_assign(
 		LOCAL_ASSIGN;
 		VIF_ASSIGN;
 		__entry->new_type = type;
+		__entry->new_p2p = p2p;
 	),
 
 	TP_printk(
-		LOCAL_PR_FMT  VIF_PR_FMT " new type:%d",
-		LOCAL_PR_ARG, VIF_PR_ARG, __entry->new_type
+		LOCAL_PR_FMT  VIF_PR_FMT " new type:%d%s",
+		LOCAL_PR_ARG, VIF_PR_ARG, __entry->new_type,
+		__entry->new_p2p ? "/p2p" : ""
 	)
 );
 
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 4e635e2..9346a6b 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1041,6 +1041,8 @@
 void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata);
 void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
 				  struct sk_buff *skb);
+void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata);
+void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata);
 
 /* IBSS code */
 void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local);
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index c1cc200..6678573 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -188,6 +188,8 @@
 		break;
 	case NL80211_IFTYPE_UNSPECIFIED:
 	case NUM_NL80211_IFTYPES:
+	case NL80211_IFTYPE_P2P_CLIENT:
+	case NL80211_IFTYPE_P2P_GO:
 		/* cannot happen */
 		WARN_ON(1);
 		break;
@@ -280,6 +282,8 @@
 			netif_carrier_on(dev);
 	}
 
+	set_bit(SDATA_STATE_RUNNING, &sdata->state);
+
 	if (sdata->vif.type == NL80211_IFTYPE_WDS) {
 		/* Create STA entry for the WDS peer */
 		sta = sta_info_alloc(sdata, sdata->u.wds.remote_addr,
@@ -331,8 +335,6 @@
 
 	netif_tx_start_all_queues(dev);
 
-	set_bit(SDATA_STATE_RUNNING, &sdata->state);
-
 	return 0;
  err_del_interface:
 	drv_remove_interface(local, &sdata->vif);
@@ -343,6 +345,7 @@
 	sdata->bss = NULL;
 	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
 		list_del(&sdata->u.vlan.list);
+	clear_bit(SDATA_STATE_RUNNING, &sdata->state);
 	return res;
 }
 
@@ -843,6 +846,7 @@
 
 	/* and set some type-dependent values */
 	sdata->vif.type = type;
+	sdata->vif.p2p = false;
 	sdata->dev->netdev_ops = &ieee80211_dataif_ops;
 	sdata->wdev.iftype = type;
 
@@ -856,10 +860,20 @@
 	INIT_WORK(&sdata->work, ieee80211_iface_work);
 
 	switch (type) {
+	case NL80211_IFTYPE_P2P_GO:
+		type = NL80211_IFTYPE_AP;
+		sdata->vif.type = type;
+		sdata->vif.p2p = true;
+		/* fall through */
 	case NL80211_IFTYPE_AP:
 		skb_queue_head_init(&sdata->u.ap.ps_bc_buf);
 		INIT_LIST_HEAD(&sdata->u.ap.vlans);
 		break;
+	case NL80211_IFTYPE_P2P_CLIENT:
+		type = NL80211_IFTYPE_STATION;
+		sdata->vif.type = type;
+		sdata->vif.p2p = true;
+		/* fall through */
 	case NL80211_IFTYPE_STATION:
 		ieee80211_sta_setup_sdata(sdata);
 		break;
@@ -893,6 +907,8 @@
 {
 	struct ieee80211_local *local = sdata->local;
 	int ret, err;
+	enum nl80211_iftype internal_type = type;
+	bool p2p = false;
 
 	ASSERT_RTNL();
 
@@ -925,11 +941,19 @@
 		 * code isn't prepared to handle).
 		 */
 		break;
+	case NL80211_IFTYPE_P2P_CLIENT:
+		p2p = true;
+		internal_type = NL80211_IFTYPE_STATION;
+		break;
+	case NL80211_IFTYPE_P2P_GO:
+		p2p = true;
+		internal_type = NL80211_IFTYPE_AP;
+		break;
 	default:
 		return -EBUSY;
 	}
 
-	ret = ieee80211_check_concurrent_iface(sdata, type);
+	ret = ieee80211_check_concurrent_iface(sdata, internal_type);
 	if (ret)
 		return ret;
 
@@ -937,7 +961,7 @@
 
 	ieee80211_teardown_sdata(sdata->dev);
 
-	ret = drv_change_interface(local, sdata, type);
+	ret = drv_change_interface(local, sdata, internal_type, p2p);
 	if (ret)
 		type = sdata->vif.type;
 
@@ -956,7 +980,7 @@
 
 	ASSERT_RTNL();
 
-	if (type == sdata->vif.type)
+	if (type == ieee80211_vif_type_p2p(&sdata->vif))
 		return 0;
 
 	/* Setting ad-hoc mode on non-IBSS channel is not supported. */
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 3570f8c..6a63d1a 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -49,7 +49,7 @@
 
 static void assert_key_lock(struct ieee80211_local *local)
 {
-	WARN_ON(!mutex_is_locked(&local->key_mtx));
+	lockdep_assert_held(&local->key_mtx);
 }
 
 static struct ieee80211_sta *get_sta_for_key(struct ieee80211_key *key)
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index b8cf282..fda97bb 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -305,6 +305,9 @@
 
 	trace_api_restart_hw(local);
 
+	/* wait for scan work complete */
+	flush_workqueue(local->workqueue);
+
 	WARN(test_bit(SCAN_HW_SCANNING, &local->scanning),
 		"%s called with hardware scan in progress\n", __func__);
 
@@ -456,6 +459,21 @@
 			BIT(IEEE80211_STYPE_DEAUTH >> 4) |
 			BIT(IEEE80211_STYPE_ACTION >> 4),
 	},
+	[NL80211_IFTYPE_P2P_CLIENT] = {
+		.tx = 0xffff,
+		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+			BIT(IEEE80211_STYPE_PROBE_REQ >> 4),
+	},
+	[NL80211_IFTYPE_P2P_GO] = {
+		.tx = 0xffff,
+		.rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+			BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+			BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+			BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+			BIT(IEEE80211_STYPE_AUTH >> 4) |
+			BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+			BIT(IEEE80211_STYPE_ACTION >> 4),
+	},
 };
 
 struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 0cb822c..8b733cf 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -92,7 +92,7 @@
 /* utils */
 static inline void ASSERT_MGD_MTX(struct ieee80211_if_managed *ifmgd)
 {
-	WARN_ON(!mutex_is_locked(&ifmgd->mtx));
+	lockdep_assert_held(&ifmgd->mtx);
 }
 
 /*
@@ -115,7 +115,7 @@
 		mod_timer(&ifmgd->timer, timeout);
 }
 
-static void mod_beacon_timer(struct ieee80211_sub_if_data *sdata)
+void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata)
 {
 	if (sdata->local->hw.flags & IEEE80211_HW_BEACON_FILTER)
 		return;
@@ -124,6 +124,19 @@
 		  round_jiffies_up(jiffies + IEEE80211_BEACON_LOSS_TIME));
 }
 
+void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata)
+{
+	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
+	if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
+		return;
+
+	mod_timer(&sdata->u.mgd.conn_mon_timer,
+		  round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME));
+
+	ifmgd->probe_send_count = 0;
+}
+
 static int ecw2cw(int ecw)
 {
 	return (1 << ecw) - 1;
@@ -1018,21 +1031,26 @@
 	if (is_multicast_ether_addr(hdr->addr1))
 		return;
 
-	if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
-		return;
-
-	mod_timer(&sdata->u.mgd.conn_mon_timer,
-		  round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME));
+	ieee80211_sta_reset_conn_monitor(sdata);
 }
 
 static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
 {
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	const u8 *ssid;
+	u8 *dst = ifmgd->associated->bssid;
+	u8 unicast_limit = max(1, IEEE80211_MAX_PROBE_TRIES - 3);
+
+	/*
+	 * Try sending broadcast probe requests for the last three
+	 * probe requests after the first ones failed since some
+	 * buggy APs only support broadcast probe requests.
+	 */
+	if (ifmgd->probe_send_count >= unicast_limit)
+		dst = NULL;
 
 	ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
-	ieee80211_send_probe_req(sdata, ifmgd->associated->bssid,
-				 ssid + 2, ssid[1], NULL, 0);
+	ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid[1], NULL, 0);
 
 	ifmgd->probe_send_count++;
 	ifmgd->probe_timeout = jiffies + IEEE80211_PROBE_WAIT;
@@ -1381,7 +1399,7 @@
 	 * Also start the timer that will detect beacon loss.
 	 */
 	ieee80211_sta_rx_notify(sdata, (struct ieee80211_hdr *)mgmt);
-	mod_beacon_timer(sdata);
+	ieee80211_sta_reset_beacon_monitor(sdata);
 
 	return true;
 }
@@ -1484,7 +1502,7 @@
 		 * we have or will be receiving any beacons or data, so let's
 		 * schedule the timers again, just in case.
 		 */
-		mod_beacon_timer(sdata);
+		ieee80211_sta_reset_beacon_monitor(sdata);
 
 		mod_timer(&ifmgd->conn_mon_timer,
 			  round_jiffies_up(jiffies +
@@ -1610,7 +1628,7 @@
 	 * Push the beacon loss detection into the future since
 	 * we are processing a beacon from the AP just now.
 	 */
-	mod_beacon_timer(sdata);
+	ieee80211_sta_reset_beacon_monitor(sdata);
 
 	ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);
 	ncrc = ieee802_11_parse_elems_crc(mgmt->u.beacon.variable,
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index eeacaa5..4b56409 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -22,12 +22,16 @@
 static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata)
 {
 	struct ieee80211_local *local = sdata->local;
+	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 
 	local->offchannel_ps_enabled = false;
 
 	/* FIXME: what to do when local->pspolling is true? */
 
 	del_timer_sync(&local->dynamic_ps_timer);
+	del_timer_sync(&ifmgd->bcn_mon_timer);
+	del_timer_sync(&ifmgd->conn_mon_timer);
+
 	cancel_work_sync(&local->dynamic_ps_enable_work);
 
 	if (local->hw.conf.flags & IEEE80211_CONF_PS) {
@@ -85,6 +89,9 @@
 		mod_timer(&local->dynamic_ps_timer, jiffies +
 			  msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
 	}
+
+	ieee80211_sta_reset_beacon_monitor(sdata);
+	ieee80211_sta_reset_conn_monitor(sdata);
 }
 
 void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local)
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index ac205a3..c0368152 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2588,9 +2588,7 @@
 		if (compare_ether_addr(sdata->u.wds.remote_addr, hdr->addr2))
 			return 0;
 		break;
-	case NL80211_IFTYPE_MONITOR:
-	case NL80211_IFTYPE_UNSPECIFIED:
-	case NUM_NL80211_IFTYPES:
+	default:
 		/* should never get here */
 		WARN_ON(1);
 		break;
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 687077e..44e10a9 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -125,7 +125,7 @@
 				    lockdep_is_held(&local->sta_mtx));
 	while (sta) {
 		if ((sta->sdata == sdata ||
-		     sta->sdata->bss == sdata->bss) &&
+		     (sta->sdata->bss && sta->sdata->bss == sdata->bss)) &&
 		    memcmp(sta->sta.addr, addr, ETH_ALEN) == 0)
 			break;
 		sta = rcu_dereference_check(sta->hnext,
@@ -280,7 +280,7 @@
 	unsigned long flags;
 	int err = 0;
 
-	WARN_ON(!mutex_is_locked(&local->sta_mtx));
+	lockdep_assert_held(&local->sta_mtx);
 
 	/* notify driver */
 	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index ccf3737..e1733dc 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1609,6 +1609,7 @@
 		return;
 	}
 
+	hdr = (struct ieee80211_hdr *) skb->data;
 	info->control.vif = &sdata->vif;
 
 	if (ieee80211_vif_is_mesh(&sdata->vif) &&
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index bd40b11..737f426 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -474,16 +474,10 @@
 
 	list_for_each_entry(sdata, &local->interfaces, list) {
 		switch (sdata->vif.type) {
-		case NUM_NL80211_IFTYPES:
-		case NL80211_IFTYPE_UNSPECIFIED:
 		case NL80211_IFTYPE_MONITOR:
 		case NL80211_IFTYPE_AP_VLAN:
 			continue;
-		case NL80211_IFTYPE_AP:
-		case NL80211_IFTYPE_STATION:
-		case NL80211_IFTYPE_ADHOC:
-		case NL80211_IFTYPE_WDS:
-		case NL80211_IFTYPE_MESH_POINT:
+		default:
 			break;
 		}
 		if (ieee80211_sdata_running(sdata))
@@ -508,16 +502,10 @@
 
 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
 		switch (sdata->vif.type) {
-		case NUM_NL80211_IFTYPES:
-		case NL80211_IFTYPE_UNSPECIFIED:
 		case NL80211_IFTYPE_MONITOR:
 		case NL80211_IFTYPE_AP_VLAN:
 			continue;
-		case NL80211_IFTYPE_AP:
-		case NL80211_IFTYPE_STATION:
-		case NL80211_IFTYPE_ADHOC:
-		case NL80211_IFTYPE_WDS:
-		case NL80211_IFTYPE_MESH_POINT:
+		default:
 			break;
 		}
 		if (ieee80211_sdata_running(sdata))
@@ -1193,6 +1181,8 @@
 			break;
 		case NL80211_IFTYPE_UNSPECIFIED:
 		case NUM_NL80211_IFTYPES:
+		case NL80211_IFTYPE_P2P_CLIENT:
+		case NL80211_IFTYPE_P2P_GO:
 			WARN_ON(1);
 			break;
 		}
@@ -1296,9 +1286,9 @@
 	int count = 0;
 
 	if (forsdata)
-		WARN_ON(!mutex_is_locked(&forsdata->u.mgd.mtx));
+		lockdep_assert_held(&forsdata->u.mgd.mtx);
 
-	WARN_ON(!mutex_is_locked(&local->iflist_mtx));
+	lockdep_assert_held(&local->iflist_mtx);
 
 	/*
 	 * This function could be improved to handle multiple
diff --git a/net/wireless/core.c b/net/wireless/core.c
index d587ad2..9c21ebf 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -729,6 +729,7 @@
 			dev->ethtool_ops = &cfg80211_ethtool_ops;
 
 		if ((wdev->iftype == NL80211_IFTYPE_STATION ||
+		     wdev->iftype == NL80211_IFTYPE_P2P_CLIENT ||
 		     wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr)
 			dev->priv_flags |= IFF_DONT_BRIDGE;
 		break;
@@ -737,6 +738,7 @@
 		case NL80211_IFTYPE_ADHOC:
 			cfg80211_leave_ibss(rdev, dev, true);
 			break;
+		case NL80211_IFTYPE_P2P_CLIENT:
 		case NL80211_IFTYPE_STATION:
 			wdev_lock(wdev);
 #ifdef CONFIG_CFG80211_WEXT
@@ -915,52 +917,3 @@
 	destroy_workqueue(cfg80211_wq);
 }
 module_exit(cfg80211_exit);
-
-static int ___wiphy_printk(const char *level, const struct wiphy *wiphy,
-			   struct va_format *vaf)
-{
-	if (!wiphy)
-		return printk("%s(NULL wiphy *): %pV", level, vaf);
-
-	return printk("%s%s: %pV", level, wiphy_name(wiphy), vaf);
-}
-
-int __wiphy_printk(const char *level, const struct wiphy *wiphy,
-		   const char *fmt, ...)
-{
-	struct va_format vaf;
-	va_list args;
-	int r;
-
-	va_start(args, fmt);
-
-	vaf.fmt = fmt;
-	vaf.va = &args;
-
-	r = ___wiphy_printk(level, wiphy, &vaf);
-	va_end(args);
-
-	return r;
-}
-EXPORT_SYMBOL(__wiphy_printk);
-
-#define define_wiphy_printk_level(func, kern_level)		\
-int func(const struct wiphy *wiphy, const char *fmt, ...)	\
-{								\
-	struct va_format vaf;					\
-	va_list args;						\
-	int r;							\
-								\
-	va_start(args, fmt);					\
-								\
-	vaf.fmt = fmt;						\
-	vaf.va = &args;						\
-								\
-	r = ___wiphy_printk(kern_level, wiphy, &vaf);		\
-	va_end(args);						\
-								\
-	return r;						\
-}								\
-EXPORT_SYMBOL(func);
-
-define_wiphy_printk_level(wiphy_debug, KERN_DEBUG);
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 58ab2c7..37580e0 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -95,7 +95,10 @@
 extern struct list_head cfg80211_rdev_list;
 extern int cfg80211_rdev_list_generation;
 
-#define assert_cfg80211_lock() WARN_ON(!mutex_is_locked(&cfg80211_mutex))
+static inline void assert_cfg80211_lock(void)
+{
+	lockdep_assert_held(&cfg80211_mutex);
+}
 
 /*
  * You can use this to mark a wiphy_idx as not having an associated wiphy.
@@ -202,8 +205,8 @@
 	mutex_unlock(&wdev->mtx);
 }
 
-#define ASSERT_RDEV_LOCK(rdev) WARN_ON(!mutex_is_locked(&(rdev)->mtx));
-#define ASSERT_WDEV_LOCK(wdev) WARN_ON(!mutex_is_locked(&(wdev)->mtx));
+#define ASSERT_RDEV_LOCK(rdev) lockdep_assert_held(&(rdev)->mtx)
+#define ASSERT_WDEV_LOCK(wdev) lockdep_assert_held(&(wdev)->mtx)
 
 enum cfg80211_event_type {
 	EVENT_CONNECT_RESULT,
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 8515b1e..46f3711 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -882,7 +882,8 @@
 		if (!wdev->current_bss ||
 		    memcmp(wdev->current_bss->pub.bssid, mgmt->bssid,
 			   ETH_ALEN) != 0 ||
-		    (wdev->iftype == NL80211_IFTYPE_STATION &&
+		    ((wdev->iftype == NL80211_IFTYPE_STATION ||
+		      wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) &&
 		     memcmp(wdev->current_bss->pub.bssid, mgmt->da,
 			    ETH_ALEN) != 0)) {
 			wdev_unlock(wdev);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 85a23de..f15b1af 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -410,12 +410,14 @@
 	switch (wdev->iftype) {
 	case NL80211_IFTYPE_AP:
 	case NL80211_IFTYPE_AP_VLAN:
+	case NL80211_IFTYPE_P2P_GO:
 		break;
 	case NL80211_IFTYPE_ADHOC:
 		if (!wdev->current_bss)
 			return -ENOLINK;
 		break;
 	case NL80211_IFTYPE_STATION:
+	case NL80211_IFTYPE_P2P_CLIENT:
 		if (wdev->sme_state != CFG80211_SME_CONNECTED)
 			return -ENOLINK;
 		break;
@@ -766,7 +768,8 @@
 		wdev->iftype == NL80211_IFTYPE_AP ||
 		wdev->iftype == NL80211_IFTYPE_WDS ||
 		wdev->iftype == NL80211_IFTYPE_MESH_POINT ||
-		wdev->iftype == NL80211_IFTYPE_MONITOR;
+		wdev->iftype == NL80211_IFTYPE_MONITOR ||
+		wdev->iftype == NL80211_IFTYPE_P2P_GO;
 }
 
 static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
@@ -833,7 +836,7 @@
 	struct cfg80211_registered_device *rdev;
 	struct net_device *netdev = NULL;
 	struct wireless_dev *wdev;
-	int result, rem_txq_params = 0;
+	int result = 0, rem_txq_params = 0;
 	struct nlattr *nl_txq_params;
 	u32 changed;
 	u8 retry_short = 0, retry_long = 0;
@@ -1693,7 +1696,8 @@
 	if (err)
 		goto unlock_rtnl;
 
-	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
+	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
 		err = -EOPNOTSUPP;
 		goto out;
 	}
@@ -1785,7 +1789,8 @@
 		goto out;
 	}
 
-	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
+	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
 		err = -EOPNOTSUPP;
 		goto out;
 	}
@@ -2128,10 +2133,12 @@
 	switch (dev->ieee80211_ptr->iftype) {
 	case NL80211_IFTYPE_AP:
 	case NL80211_IFTYPE_AP_VLAN:
+	case NL80211_IFTYPE_P2P_GO:
 		/* disallow mesh-specific things */
 		if (params.plink_action)
 			err = -EINVAL;
 		break;
+	case NL80211_IFTYPE_P2P_CLIENT:
 	case NL80211_IFTYPE_STATION:
 		/* disallow everything but AUTHORIZED flag */
 		if (params.plink_action)
@@ -2233,7 +2240,8 @@
 		goto out_rtnl;
 
 	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
-	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN) {
+	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
+	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
 		err = -EINVAL;
 		goto out;
 	}
@@ -2286,7 +2294,8 @@
 
 	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
 	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
-	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
+	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
+	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
 		err = -EINVAL;
 		goto out;
 	}
@@ -2660,7 +2669,8 @@
 		goto out;
 	}
 
-	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
+	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
 		err = -EOPNOTSUPP;
 		goto out;
 	}
@@ -3363,6 +3373,7 @@
 	}
 
 	switch (wdev->iftype) {
+	case NL80211_IFTYPE_P2P_CLIENT:
 	case NL80211_IFTYPE_STATION:
 		if (intbss == wdev->current_bss)
 			NLA_PUT_U32(msg, NL80211_BSS_STATUS,
@@ -3649,7 +3660,8 @@
 		goto out;
 	}
 
-	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
+	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) {
 		err = -EOPNOTSUPP;
 		goto out;
 	}
@@ -3804,7 +3816,8 @@
 		goto out;
 	}
 
-	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
+	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) {
 		err = -EOPNOTSUPP;
 		goto out;
 	}
@@ -3888,7 +3901,8 @@
 		goto out;
 	}
 
-	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
+	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) {
 		err = -EOPNOTSUPP;
 		goto out;
 	}
@@ -3954,7 +3968,8 @@
 		goto out;
 	}
 
-	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
+	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) {
 		err = -EOPNOTSUPP;
 		goto out;
 	}
@@ -4332,7 +4347,8 @@
 	if (err)
 		goto unlock_rtnl;
 
-	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
+	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) {
 		err = -EOPNOTSUPP;
 		goto out;
 	}
@@ -4408,7 +4424,8 @@
 	if (err)
 		goto unlock_rtnl;
 
-	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
+	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) {
 		err = -EOPNOTSUPP;
 		goto out;
 	}
@@ -4496,7 +4513,8 @@
 	pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]);
 	pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
 
-	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
+	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) {
 		err = -EOPNOTSUPP;
 		goto out;
 	}
@@ -4541,7 +4559,8 @@
 	if (err)
 		goto out_rtnl;
 
-	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
+	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) {
 		err = -EOPNOTSUPP;
 		goto out;
 	}
@@ -4823,7 +4842,8 @@
 		goto unlock_rtnl;
 
 	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
-	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
+	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
+	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) {
 		err = -EOPNOTSUPP;
 		goto out;
 	}
@@ -4875,7 +4895,8 @@
 	}
 
 	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
-	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
+	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
+	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) {
 		err = -EOPNOTSUPP;
 		goto out;
 	}
@@ -5093,7 +5114,8 @@
 		goto unlock_rdev;
 	}
 
-	if (wdev->iftype != NL80211_IFTYPE_STATION) {
+	if (wdev->iftype != NL80211_IFTYPE_STATION &&
+	    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) {
 		err = -EOPNOTSUPP;
 		goto unlock_rdev;
 	}
diff --git a/net/wireless/radiotap.c b/net/wireless/radiotap.c
index 1332c44..c774bc0 100644
--- a/net/wireless/radiotap.c
+++ b/net/wireless/radiotap.c
@@ -14,6 +14,7 @@
  * See COPYING for more details.
  */
 
+#include <linux/kernel.h>
 #include <net/cfg80211.h>
 #include <net/ieee80211_radiotap.h>
 #include <asm/unaligned.h>
@@ -45,7 +46,7 @@
 };
 
 static const struct ieee80211_radiotap_namespace radiotap_ns = {
-	.n_bits = sizeof(rtap_namespace_sizes) / sizeof(rtap_namespace_sizes[0]),
+	.n_bits = ARRAY_SIZE(rtap_namespace_sizes),
 	.align_size = rtap_namespace_sizes,
 };
 
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index b0d9a08..d14bbf9 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -74,7 +74,11 @@
  *     - last_request
  */
 static DEFINE_MUTEX(reg_mutex);
-#define assert_reg_lock() WARN_ON(!mutex_is_locked(&reg_mutex))
+
+static inline void assert_reg_lock(void)
+{
+	lockdep_assert_held(&reg_mutex);
+}
 
 /* Used to queue up regulatory hints */
 static LIST_HEAD(reg_requests_list);
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index a8c2d6b..f161b98 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -411,7 +411,8 @@
 
 	ASSERT_WDEV_LOCK(wdev);
 
-	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
+	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
+		    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
 		return;
 
 	if (wdev->sme_state != CFG80211_SME_CONNECTING)
@@ -548,7 +549,8 @@
 
 	ASSERT_WDEV_LOCK(wdev);
 
-	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
+	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
+		    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
 		return;
 
 	if (wdev->sme_state != CFG80211_SME_CONNECTED)
@@ -644,7 +646,8 @@
 
 	ASSERT_WDEV_LOCK(wdev);
 
-	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
+	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
+		    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
 		return;
 
 	if (wdev->sme_state != CFG80211_SME_CONNECTED)
diff --git a/net/wireless/util.c b/net/wireless/util.c
index bca32eb..fb5448f 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -326,7 +326,8 @@
 		cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
 	case cpu_to_le16(IEEE80211_FCTL_TODS):
 		if (unlikely(iftype != NL80211_IFTYPE_AP &&
-			     iftype != NL80211_IFTYPE_AP_VLAN))
+			     iftype != NL80211_IFTYPE_AP_VLAN &&
+			     iftype != NL80211_IFTYPE_P2P_GO))
 			return -1;
 		break;
 	case cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS):
@@ -354,7 +355,8 @@
 		break;
 	case cpu_to_le16(IEEE80211_FCTL_FROMDS):
 		if ((iftype != NL80211_IFTYPE_STATION &&
-		    iftype != NL80211_IFTYPE_MESH_POINT) ||
+		     iftype != NL80211_IFTYPE_P2P_CLIENT &&
+		     iftype != NL80211_IFTYPE_MESH_POINT) ||
 		    (is_multicast_ether_addr(dst) &&
 		     !compare_ether_addr(src, addr)))
 			return -1;
@@ -431,6 +433,7 @@
 	switch (iftype) {
 	case NL80211_IFTYPE_AP:
 	case NL80211_IFTYPE_AP_VLAN:
+	case NL80211_IFTYPE_P2P_GO:
 		fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
 		/* DA BSSID SA */
 		memcpy(hdr.addr1, skb->data, ETH_ALEN);
@@ -439,6 +442,7 @@
 		hdrlen = 24;
 		break;
 	case NL80211_IFTYPE_STATION:
+	case NL80211_IFTYPE_P2P_CLIENT:
 		fc |= cpu_to_le16(IEEE80211_FCTL_TODS);
 		/* BSSID SA DA */
 		memcpy(hdr.addr1, bssid, ETH_ALEN);
@@ -778,7 +782,9 @@
 
 	/* if it's part of a bridge, reject changing type to station/ibss */
 	if ((dev->priv_flags & IFF_BRIDGE_PORT) &&
-	    (ntype == NL80211_IFTYPE_ADHOC || ntype == NL80211_IFTYPE_STATION))
+	    (ntype == NL80211_IFTYPE_ADHOC ||
+	     ntype == NL80211_IFTYPE_STATION ||
+	     ntype == NL80211_IFTYPE_P2P_CLIENT))
 		return -EBUSY;
 
 	if (ntype != otype) {
@@ -789,6 +795,7 @@
 			cfg80211_leave_ibss(rdev, dev, false);
 			break;
 		case NL80211_IFTYPE_STATION:
+		case NL80211_IFTYPE_P2P_CLIENT:
 			cfg80211_disconnect(rdev, dev,
 					    WLAN_REASON_DEAUTH_LEAVING, true);
 			break;
@@ -817,9 +824,11 @@
 			if (dev->ieee80211_ptr->use_4addr)
 				break;
 			/* fall through */
+		case NL80211_IFTYPE_P2P_CLIENT:
 		case NL80211_IFTYPE_ADHOC:
 			dev->priv_flags |= IFF_DONT_BRIDGE;
 			break;
+		case NL80211_IFTYPE_P2P_GO:
 		case NL80211_IFTYPE_AP:
 		case NL80211_IFTYPE_AP_VLAN:
 		case NL80211_IFTYPE_WDS: