Просмотр исходного кода

Merge remote-tracking branch 'wireless-next/master' into iwlwifi-next

Johannes Berg 13 лет назад
Родитель
Сommit
627ae3ddd6
100 измененных файлов с 6694 добавлено и 2702 удалено
  1. 19 11
      MAINTAINERS
  2. 3 1
      drivers/bcma/driver_chipcommon_pmu.c
  3. 4 2
      drivers/bcma/driver_pci.c
  4. 2 2
      drivers/bcma/sprom.c
  5. 3 1
      drivers/net/wireless/ath/ath9k/Makefile
  6. 1 1
      drivers/net/wireless/ath/ath9k/ahb.c
  7. 776 0
      drivers/net/wireless/ath/ath9k/antenna.c
  8. 8 12
      drivers/net/wireless/ath/ath9k/ar9003_calib.c
  9. 1 0
      drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
  10. 232 225
      drivers/net/wireless/ath/ath9k/ar9003_mci.c
  11. 1 9
      drivers/net/wireless/ath/ath9k/ar9003_mci.h
  12. 4 0
      drivers/net/wireless/ath/ath9k/ar9003_phy.c
  13. 20 12
      drivers/net/wireless/ath/ath9k/ar9003_phy.h
  14. 3 2
      drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h
  15. 34 15
      drivers/net/wireless/ath/ath9k/ath9k.h
  16. 13 10
      drivers/net/wireless/ath/ath9k/beacon.c
  17. 8 2
      drivers/net/wireless/ath/ath9k/btcoex.c
  18. 4 0
      drivers/net/wireless/ath/ath9k/btcoex.h
  19. 6 3
      drivers/net/wireless/ath/ath9k/debug.c
  20. 1 0
      drivers/net/wireless/ath/ath9k/debug.h
  21. 25 11
      drivers/net/wireless/ath/ath9k/gpio.c
  22. 90 122
      drivers/net/wireless/ath/ath9k/hw.c
  23. 9 1
      drivers/net/wireless/ath/ath9k/hw.h
  24. 10 8
      drivers/net/wireless/ath/ath9k/init.c
  25. 502 0
      drivers/net/wireless/ath/ath9k/link.c
  26. 73 485
      drivers/net/wireless/ath/ath9k/main.c
  27. 46 19
      drivers/net/wireless/ath/ath9k/mci.c
  28. 10 1
      drivers/net/wireless/ath/ath9k/mci.h
  29. 6 1
      drivers/net/wireless/ath/ath9k/pci.c
  30. 14 754
      drivers/net/wireless/ath/ath9k/recv.c
  31. 2 0
      drivers/net/wireless/ath/ath9k/reg.h
  32. 7 44
      drivers/net/wireless/ath/ath9k/xmit.c
  33. 4 0
      drivers/net/wireless/b43/b43.h
  34. 12 7
      drivers/net/wireless/b43/main.c
  35. 1 1
      drivers/net/wireless/b43legacy/main.c
  36. 2 2
      drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
  37. 1 2
      drivers/net/wireless/brcm80211/brcmsmac/aiutils.c
  38. 1 1
      drivers/net/wireless/brcm80211/brcmsmac/aiutils.h
  39. 5 15
      drivers/net/wireless/ipw2x00/ipw2100.c
  40. 12 1
      drivers/net/wireless/iwlegacy/4965-mac.c
  41. 3 0
      drivers/net/wireless/iwlwifi/dvm/mac80211.c
  42. 1 1
      drivers/net/wireless/iwlwifi/dvm/sta.c
  43. 9 1
      drivers/net/wireless/iwlwifi/iwl-drv.c
  44. 1 0
      drivers/net/wireless/iwlwifi/iwl-prph.h
  45. 21 2
      drivers/net/wireless/iwlwifi/pcie/6000.c
  46. 5 0
      drivers/net/wireless/iwlwifi/pcie/trans.c
  47. 28 11
      drivers/net/wireless/libertas/cfg.c
  48. 1 0
      drivers/net/wireless/libertas/dev.h
  49. 2 5
      drivers/net/wireless/libertas/mesh.c
  50. 22 0
      drivers/net/wireless/mac80211_hwsim.c
  51. 65 0
      drivers/net/wireless/mwifiex/init.c
  52. 2 2
      drivers/net/wireless/mwifiex/main.c
  53. 8 3
      drivers/net/wireless/mwifiex/main.h
  54. 44 15
      drivers/net/wireless/mwifiex/scan.c
  55. 4 5
      drivers/net/wireless/orinoco/cfg.c
  56. 6 0
      drivers/net/wireless/rt2x00/rt2800.h
  57. 21 1
      drivers/net/wireless/rt2x00/rt2800lib.c
  58. 1 2
      drivers/net/wireless/rt2x00/rt2x00.h
  59. 2 0
      drivers/net/wireless/rt2x00/rt2x00dev.c
  60. 12 3
      drivers/net/wireless/rt2x00/rt2x00mac.c
  61. 6 7
      drivers/net/wireless/rt2x00/rt2x00queue.c
  62. 1 1
      drivers/net/wireless/rtl818x/rtl8187/leds.c
  63. 1 0
      drivers/net/wireless/ti/Kconfig
  64. 1 0
      drivers/net/wireless/ti/Makefile
  65. 1 1
      drivers/net/wireless/ti/wl12xx/Makefile
  66. 237 0
      drivers/net/wireless/ti/wl12xx/acx.h
  67. 5 3
      drivers/net/wireless/ti/wl12xx/cmd.c
  68. 243 0
      drivers/net/wireless/ti/wl12xx/debugfs.c
  69. 28 0
      drivers/net/wireless/ti/wl12xx/debugfs.h
  70. 199 43
      drivers/net/wireless/ti/wl12xx/main.c
  71. 8 0
      drivers/net/wireless/ti/wl12xx/wl12xx.h
  72. 7 0
      drivers/net/wireless/ti/wl18xx/Kconfig
  73. 3 0
      drivers/net/wireless/ti/wl18xx/Makefile
  74. 111 0
      drivers/net/wireless/ti/wl18xx/acx.c
  75. 291 0
      drivers/net/wireless/ti/wl18xx/acx.h
  76. 92 0
      drivers/net/wireless/ti/wl18xx/conf.h
  77. 403 0
      drivers/net/wireless/ti/wl18xx/debugfs.c
  78. 28 0
      drivers/net/wireless/ti/wl18xx/debugfs.h
  79. 60 0
      drivers/net/wireless/ti/wl18xx/io.c
  80. 28 0
      drivers/net/wireless/ti/wl18xx/io.h
  81. 1463 0
      drivers/net/wireless/ti/wl18xx/main.c
  82. 191 0
      drivers/net/wireless/ti/wl18xx/reg.h
  83. 127 0
      drivers/net/wireless/ti/wl18xx/tx.c
  84. 46 0
      drivers/net/wireless/ti/wl18xx/tx.h
  85. 88 0
      drivers/net/wireless/ti/wl18xx/wl18xx.h
  86. 6 2
      drivers/net/wireless/ti/wlcore/acx.c
  87. 16 238
      drivers/net/wireless/ti/wlcore/acx.h
  88. 33 24
      drivers/net/wireless/ti/wlcore/boot.c
  89. 1 0
      drivers/net/wireless/ti/wlcore/boot.h
  90. 51 11
      drivers/net/wireless/ti/wlcore/cmd.c
  91. 11 2
      drivers/net/wireless/ti/wlcore/cmd.h
  92. 67 26
      drivers/net/wireless/ti/wlcore/conf.h
  93. 129 261
      drivers/net/wireless/ti/wlcore/debugfs.c
  94. 87 0
      drivers/net/wireless/ti/wlcore/debugfs.h
  95. 21 3
      drivers/net/wireless/ti/wlcore/event.c
  96. 78 0
      drivers/net/wireless/ti/wlcore/hw_ops.h
  97. 3 0
      drivers/net/wireless/ti/wlcore/init.c
  98. 267 237
      drivers/net/wireless/ti/wlcore/main.c
  99. 17 4
      drivers/net/wireless/ti/wlcore/ps.c
  100. 6 5
      drivers/net/wireless/ti/wlcore/rx.c

+ 19 - 11
MAINTAINERS

@@ -329,7 +329,7 @@ F:	drivers/hwmon/adm1029.c
 
 ADM8211 WIRELESS DRIVER
 L:	linux-wireless@vger.kernel.org
-W:	http://linuxwireless.org/
+W:	http://wireless.kernel.org/
 S:	Orphan
 F:	drivers/net/wireless/adm8211.*
 
@@ -1423,7 +1423,7 @@ B43 WIRELESS DRIVER
 M:	Stefano Brivio <stefano.brivio@polimi.it>
 L:	linux-wireless@vger.kernel.org
 L:	b43-dev@lists.infradead.org
-W:	http://linuxwireless.org/en/users/Drivers/b43
+W:	http://wireless.kernel.org/en/users/Drivers/b43
 S:	Maintained
 F:	drivers/net/wireless/b43/
 
@@ -1432,7 +1432,7 @@ M:	Larry Finger <Larry.Finger@lwfinger.net>
 M:	Stefano Brivio <stefano.brivio@polimi.it>
 L:	linux-wireless@vger.kernel.org
 L:	b43-dev@lists.infradead.org
-W:	http://linuxwireless.org/en/users/Drivers/b43
+W:	http://wireless.kernel.org/en/users/Drivers/b43
 S:	Maintained
 F:	drivers/net/wireless/b43legacy/
 
@@ -1800,6 +1800,9 @@ F:	include/linux/cfag12864b.h
 CFG80211 and NL80211
 M:	Johannes Berg <johannes@sipsolutions.net>
 L:	linux-wireless@vger.kernel.org
+W:	http://wireless.kernel.org/
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211.git
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next.git
 S:	Maintained
 F:	include/linux/nl80211.h
 F:	include/net/cfg80211.h
@@ -4339,8 +4342,9 @@ F:	arch/m68k/hp300/
 MAC80211
 M:	Johannes Berg <johannes@sipsolutions.net>
 L:	linux-wireless@vger.kernel.org
-W:	http://linuxwireless.org/
-T:	git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless.git
+W:	http://wireless.kernel.org/
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211.git
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next.git
 S:	Maintained
 F:	Documentation/networking/mac80211-injection.txt
 F:	include/net/mac80211.h
@@ -4350,8 +4354,9 @@ MAC80211 PID RATE CONTROL
 M:	Stefano Brivio <stefano.brivio@polimi.it>
 M:	Mattias Nissler <mattias.nissler@gmx.de>
 L:	linux-wireless@vger.kernel.org
-W:	http://linuxwireless.org/en/developers/Documentation/mac80211/RateControl/PID
-T:	git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless.git
+W:	http://wireless.kernel.org/en/developers/Documentation/mac80211/RateControl/PID
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211.git
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next.git
 S:	Maintained
 F:	net/mac80211/rc80211_pid*
 
@@ -5027,7 +5032,7 @@ F:	fs/ocfs2/
 
 ORINOCO DRIVER
 L:	linux-wireless@vger.kernel.org
-W:	http://linuxwireless.org/en/users/Drivers/orinoco
+W:	http://wireless.kernel.org/en/users/Drivers/orinoco
 W:	http://www.nongnu.org/orinoco/
 S:	Orphan
 F:	drivers/net/wireless/orinoco/
@@ -5695,6 +5700,9 @@ F:	include/linux/remoteproc.h
 RFKILL
 M:	Johannes Berg <johannes@sipsolutions.net>
 L:	linux-wireless@vger.kernel.org
+W:	http://wireless.kernel.org/
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211.git
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next.git
 S:	Maintained
 F:	Documentation/rfkill.txt
 F:	net/rfkill/
@@ -5729,7 +5737,7 @@ F:	net/rose/
 RTL8180 WIRELESS DRIVER
 M:	"John W. Linville" <linville@tuxdriver.com>
 L:	linux-wireless@vger.kernel.org
-W:	http://linuxwireless.org/
+W:	http://wireless.kernel.org/
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
 S:	Maintained
 F:	drivers/net/wireless/rtl818x/rtl8180/
@@ -5739,7 +5747,7 @@ M:	Herton Ronaldo Krzesinski <herton@canonical.com>
 M:	Hin-Tak Leung <htl10@users.sourceforge.net>
 M:	Larry Finger <Larry.Finger@lwfinger.net>
 L:	linux-wireless@vger.kernel.org
-W:	http://linuxwireless.org/
+W:	http://wireless.kernel.org/
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
 S:	Maintained
 F:	drivers/net/wireless/rtl818x/rtl8187/
@@ -5748,7 +5756,7 @@ RTL8192CE WIRELESS DRIVER
 M:	Larry Finger <Larry.Finger@lwfinger.net>
 M:	Chaoming Li <chaoming_li@realsil.com.cn>
 L:	linux-wireless@vger.kernel.org
-W:	http://linuxwireless.org/
+W:	http://wireless.kernel.org/
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
 S:	Maintained
 F:	drivers/net/wireless/rtlwifi/

+ 3 - 1
drivers/bcma/driver_chipcommon_pmu.c

@@ -139,7 +139,9 @@ void bcma_pmu_workarounds(struct bcma_drv_cc *cc)
 		bcma_chipco_chipctl_maskset(cc, 0, ~0, 0x7);
 		break;
 	case 0x4331:
-		/* BCM4331 workaround is SPROM-related, we put it in sprom.c */
+	case 43431:
+		/* Ext PA lines must be enabled for tx on BCM4331 */
+		bcma_chipco_bcm4331_ext_pa_lines_ctl(cc, true);
 		break;
 	case 43224:
 		if (bus->chipinfo.rev == 0) {

+ 4 - 2
drivers/bcma/driver_pci.c

@@ -232,17 +232,19 @@ void __devinit bcma_core_pci_init(struct bcma_drv_pci *pc)
 int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core,
 			  bool enable)
 {
-	struct pci_dev *pdev = pc->core->bus->host_pci;
+	struct pci_dev *pdev;
 	u32 coremask, tmp;
 	int err = 0;
 
-	if (core->bus->hosttype != BCMA_HOSTTYPE_PCI) {
+	if (!pc || core->bus->hosttype != BCMA_HOSTTYPE_PCI) {
 		/* This bcma device is not on a PCI host-bus. So the IRQs are
 		 * not routed through the PCI core.
 		 * So we must not enable routing through the PCI core. */
 		goto out;
 	}
 
+	pdev = pc->core->bus->host_pci;
+
 	err = pci_read_config_dword(pdev, BCMA_PCI_IRQMASK, &tmp);
 	if (err)
 		goto out;

+ 2 - 2
drivers/bcma/sprom.c

@@ -579,13 +579,13 @@ int bcma_sprom_get(struct bcma_bus *bus)
 	if (!sprom)
 		return -ENOMEM;
 
-	if (bus->chipinfo.id == 0x4331)
+	if (bus->chipinfo.id == 0x4331 || bus->chipinfo.id == 43431)
 		bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false);
 
 	pr_debug("SPROM offset 0x%x\n", offset);
 	bcma_sprom_read(bus, offset, sprom);
 
-	if (bus->chipinfo.id == 0x4331)
+	if (bus->chipinfo.id == 0x4331 || bus->chipinfo.id == 43431)
 		bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, true);
 
 	err = bcma_sprom_valid(sprom);

+ 3 - 1
drivers/net/wireless/ath/ath9k/Makefile

@@ -3,7 +3,9 @@ ath9k-y +=	beacon.o \
 		init.o \
 		main.o \
 		recv.o \
-		xmit.o
+		xmit.o \
+		link.o \
+		antenna.o
 
 ath9k-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += mci.o
 ath9k-$(CONFIG_ATH9K_RATE_CONTROL) += rc.o

+ 1 - 1
drivers/net/wireless/ath/ath9k/ahb.c

@@ -126,7 +126,7 @@ static int ath_ahb_probe(struct platform_device *pdev)
 	sc->irq = irq;
 
 	/* Will be cleared in ath9k_start() */
-	sc->sc_flags |= SC_OP_INVALID;
+	set_bit(SC_OP_INVALID, &sc->sc_flags);
 
 	ret = request_irq(irq, ath_isr, IRQF_SHARED, "ath9k", sc);
 	if (ret) {

+ 776 - 0
drivers/net/wireless/ath/ath9k/antenna.c

@@ -0,0 +1,776 @@
+/*
+ * Copyright (c) 2012 Qualcomm Atheros, 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 "ath9k.h"
+
+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 ath_ant_div_comb_alt_check(u8 div_group, int alt_ratio,
+					      int curr_main_set, int curr_alt_set,
+					      int alt_rssi_avg, int main_rssi_avg)
+{
+	bool result = false;
+	switch (div_group) {
+	case 0:
+		if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
+			result = true;
+		break;
+	case 1:
+	case 2:
+		if ((((curr_main_set == ATH_ANT_DIV_COMB_LNA2) &&
+		      (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) &&
+		      (alt_rssi_avg >= (main_rssi_avg - 5))) ||
+		     ((curr_main_set == ATH_ANT_DIV_COMB_LNA1) &&
+		      (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) &&
+		      (alt_rssi_avg >= (main_rssi_avg - 2)))) &&
+		    (alt_rssi_avg >= 4))
+			result = true;
+		else
+			result = false;
+		break;
+	}
+
+	return result;
+}
+
+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,
+					  struct ath_ant_comb *antcomb,
+					  int alt_ratio)
+{
+	if (ant_conf->div_group == 0) {
+		/* 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;
+		}
+	} else if (ant_conf->div_group == 1) {
+		/* 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 = 0x1;
+			ant_conf->main_gaintb = 0;
+			ant_conf->alt_gaintb = 0;
+			break;
+		case 0x02: /* A-B LNA1 */
+			ant_conf->fast_div_bias = 0x1;
+			ant_conf->main_gaintb = 0;
+			ant_conf->alt_gaintb = 0;
+			break;
+		case 0x03: /* A-B A+B */
+			ant_conf->fast_div_bias = 0x1;
+			ant_conf->main_gaintb = 0;
+			ant_conf->alt_gaintb = 0;
+			break;
+		case 0x10: /* LNA2 A-B */
+			if (!(antcomb->scan) &&
+			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+				ant_conf->fast_div_bias = 0x3f;
+			else
+				ant_conf->fast_div_bias = 0x1;
+			ant_conf->main_gaintb = 0;
+			ant_conf->alt_gaintb = 0;
+			break;
+		case 0x12: /* LNA2 LNA1 */
+			ant_conf->fast_div_bias = 0x1;
+			ant_conf->main_gaintb = 0;
+			ant_conf->alt_gaintb = 0;
+			break;
+		case 0x13: /* LNA2 A+B */
+			if (!(antcomb->scan) &&
+			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+				ant_conf->fast_div_bias = 0x3f;
+			else
+				ant_conf->fast_div_bias = 0x1;
+			ant_conf->main_gaintb = 0;
+			ant_conf->alt_gaintb = 0;
+			break;
+		case 0x20: /* LNA1 A-B */
+			if (!(antcomb->scan) &&
+			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+				ant_conf->fast_div_bias = 0x3f;
+			else
+				ant_conf->fast_div_bias = 0x1;
+			ant_conf->main_gaintb = 0;
+			ant_conf->alt_gaintb = 0;
+			break;
+		case 0x21: /* LNA1 LNA2 */
+			ant_conf->fast_div_bias = 0x1;
+			ant_conf->main_gaintb = 0;
+			ant_conf->alt_gaintb = 0;
+			break;
+		case 0x23: /* LNA1 A+B */
+			if (!(antcomb->scan) &&
+			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+				ant_conf->fast_div_bias = 0x3f;
+			else
+				ant_conf->fast_div_bias = 0x1;
+			ant_conf->main_gaintb = 0;
+			ant_conf->alt_gaintb = 0;
+			break;
+		case 0x30: /* A+B A-B */
+			ant_conf->fast_div_bias = 0x1;
+			ant_conf->main_gaintb = 0;
+			ant_conf->alt_gaintb = 0;
+			break;
+		case 0x31: /* A+B LNA2 */
+			ant_conf->fast_div_bias = 0x1;
+			ant_conf->main_gaintb = 0;
+			ant_conf->alt_gaintb = 0;
+			break;
+		case 0x32: /* A+B LNA1 */
+			ant_conf->fast_div_bias = 0x1;
+			ant_conf->main_gaintb = 0;
+			ant_conf->alt_gaintb = 0;
+			break;
+		default:
+			break;
+		}
+	} else if (ant_conf->div_group == 2) {
+		/* 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 = 0x1;
+			ant_conf->main_gaintb = 0;
+			ant_conf->alt_gaintb = 0;
+			break;
+		case 0x02: /* A-B LNA1 */
+			ant_conf->fast_div_bias = 0x1;
+			ant_conf->main_gaintb = 0;
+			ant_conf->alt_gaintb = 0;
+			break;
+		case 0x03: /* A-B A+B */
+			ant_conf->fast_div_bias = 0x1;
+			ant_conf->main_gaintb = 0;
+			ant_conf->alt_gaintb = 0;
+			break;
+		case 0x10: /* LNA2 A-B */
+			if (!(antcomb->scan) &&
+				(alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+				ant_conf->fast_div_bias = 0x1;
+			else
+				ant_conf->fast_div_bias = 0x2;
+			ant_conf->main_gaintb = 0;
+			ant_conf->alt_gaintb = 0;
+			break;
+		case 0x12: /* LNA2 LNA1 */
+			ant_conf->fast_div_bias = 0x1;
+			ant_conf->main_gaintb = 0;
+			ant_conf->alt_gaintb = 0;
+			break;
+		case 0x13: /* LNA2 A+B */
+			if (!(antcomb->scan) &&
+				(alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+				ant_conf->fast_div_bias = 0x1;
+			else
+				ant_conf->fast_div_bias = 0x2;
+			ant_conf->main_gaintb = 0;
+			ant_conf->alt_gaintb = 0;
+			break;
+		case 0x20: /* LNA1 A-B */
+			if (!(antcomb->scan) &&
+				(alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+				ant_conf->fast_div_bias = 0x1;
+			else
+				ant_conf->fast_div_bias = 0x2;
+			ant_conf->main_gaintb = 0;
+			ant_conf->alt_gaintb = 0;
+			break;
+		case 0x21: /* LNA1 LNA2 */
+			ant_conf->fast_div_bias = 0x1;
+			ant_conf->main_gaintb = 0;
+			ant_conf->alt_gaintb = 0;
+			break;
+		case 0x23: /* LNA1 A+B */
+			if (!(antcomb->scan) &&
+				(alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+				ant_conf->fast_div_bias = 0x1;
+			else
+				ant_conf->fast_div_bias = 0x2;
+			ant_conf->main_gaintb = 0;
+			ant_conf->alt_gaintb = 0;
+			break;
+		case 0x30: /* A+B A-B */
+			ant_conf->fast_div_bias = 0x1;
+			ant_conf->main_gaintb = 0;
+			ant_conf->alt_gaintb = 0;
+			break;
+		case 0x31: /* A+B LNA2 */
+			ant_conf->fast_div_bias = 0x1;
+			ant_conf->main_gaintb = 0;
+			ant_conf->alt_gaintb = 0;
+			break;
+		case 0x32: /* A+B LNA1 */
+			ant_conf->fast_div_bias = 0x1;
+			ant_conf->main_gaintb = 0;
+			ant_conf->alt_gaintb = 0;
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+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;
+	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 both main_rssi and  alt_rssi is positive */
+	if (main_rssi > 0 && 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;
+
+	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 (ath_ant_div_comb_alt_check(div_ant_conf.div_group,
+					alt_ratio, curr_main_set, curr_alt_set,
+					alt_rssi_avg, main_rssi_avg)) {
+			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 +
+				     div_ant_conf.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, antcomb, alt_ratio);
+	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;
+}
+
+void ath_ant_comb_update(struct ath_softc *sc)
+{
+	struct ath_hw *ah = sc->sc_ah;
+	struct ath_hw_antcomb_conf div_ant_conf;
+	u8 lna_conf;
+
+	ath9k_hw_antdiv_comb_conf_get(ah, &div_ant_conf);
+
+	if (sc->ant_rx == 1)
+		lna_conf = ATH_ANT_DIV_COMB_LNA1;
+	else
+		lna_conf = ATH_ANT_DIV_COMB_LNA2;
+
+	div_ant_conf.main_lna_conf = lna_conf;
+	div_ant_conf.alt_lna_conf = lna_conf;
+
+	ath9k_hw_antdiv_comb_conf_set(ah, &div_ant_conf);
+}

+ 8 - 12
drivers/net/wireless/ath/ath9k/ar9003_calib.c

@@ -653,7 +653,6 @@ static void ar9003_hw_detect_outlier(int *mp_coeff, int nmeasurement,
 }
 
 static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah,
-						 u8 num_chains,
 						 struct coeff *coeff,
 						 bool is_reusable)
 {
@@ -677,7 +676,9 @@ static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah,
 	}
 
 	/* Load the average of 2 passes */
-	for (i = 0; i < num_chains; i++) {
+	for (i = 0; i < AR9300_MAX_CHAINS; i++) {
+		if (!(ah->txchainmask & (1 << i)))
+			continue;
 		nmeasurement = REG_READ_FIELD(ah,
 				AR_PHY_TX_IQCAL_STATUS_B0,
 				AR_PHY_CALIBRATED_GAINS_0);
@@ -767,16 +768,13 @@ static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah, bool is_reusable)
 	};
 	struct coeff coeff;
 	s32 iq_res[6];
-	u8 num_chains = 0;
 	int i, im, j;
 	int nmeasurement;
 
 	for (i = 0; i < AR9300_MAX_CHAINS; i++) {
-		if (ah->txchainmask & (1 << i))
-			num_chains++;
-	}
+		if (!(ah->txchainmask & (1 << i)))
+			continue;
 
-	for (i = 0; i < num_chains; i++) {
 		nmeasurement = REG_READ_FIELD(ah,
 				AR_PHY_TX_IQCAL_STATUS_B0,
 				AR_PHY_CALIBRATED_GAINS_0);
@@ -839,8 +837,7 @@ static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah, bool is_reusable)
 				coeff.phs_coeff[i][im] -= 128;
 		}
 	}
-	ar9003_hw_tx_iqcal_load_avg_2_passes(ah, num_chains,
-					     &coeff, is_reusable);
+	ar9003_hw_tx_iqcal_load_avg_2_passes(ah, &coeff, is_reusable);
 
 	return;
 
@@ -901,7 +898,6 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah,
 	bool is_reusable = true, status = true;
 	bool run_rtt_cal = false, run_agc_cal;
 	bool rtt = !!(ah->caps.hw_caps & ATH9K_HW_CAP_RTT);
-	bool mci = !!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI);
 	u32 agc_ctrl = 0, agc_supp_cals = AR_PHY_AGC_CONTROL_OFFSET_CAL |
 					  AR_PHY_AGC_CONTROL_FLTR_CAL   |
 					  AR_PHY_AGC_CONTROL_PKDET_CAL;
@@ -970,7 +966,7 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah,
 	} else if (caldata && !caldata->done_txiqcal_once)
 		run_agc_cal = true;
 
-	if (mci && IS_CHAN_2GHZ(chan) && run_agc_cal)
+	if (ath9k_hw_mci_is_enabled(ah) && IS_CHAN_2GHZ(chan) && run_agc_cal)
 		ar9003_mci_init_cal_req(ah, &is_reusable);
 
 	if (!(IS_CHAN_HALF_RATE(chan) || IS_CHAN_QUARTER_RATE(chan))) {
@@ -993,7 +989,7 @@ skip_tx_iqcal:
 				       0, AH_WAIT_TIMEOUT);
 	}
 
-	if (mci && IS_CHAN_2GHZ(chan) && run_agc_cal)
+	if (ath9k_hw_mci_is_enabled(ah) && IS_CHAN_2GHZ(chan) && run_agc_cal)
 		ar9003_mci_init_cal_done(ah);
 
 	if (rtt && !run_rtt_cal) {

+ 1 - 0
drivers/net/wireless/ath/ath9k/ar9003_eeprom.c

@@ -3613,6 +3613,7 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz)
 		value = ar9003_switch_com_spdt_get(ah, is2ghz);
 		REG_RMW_FIELD(ah, AR_PHY_GLB_CONTROL,
 				AR_SWITCH_TABLE_COM_SPDT_ALL, value);
+		REG_SET_BIT(ah, AR_PHY_GLB_CONTROL, AR_BTCOEX_CTRL_SPDT_ENABLE);
 	}
 
 	value = ar9003_hw_ant_ctrl_common_2_get(ah, is2ghz);

+ 232 - 225
drivers/net/wireless/ath/ath9k/ar9003_mci.c

@@ -35,31 +35,30 @@ static int ar9003_mci_wait_for_interrupt(struct ath_hw *ah, u32 address,
 	struct ath_common *common = ath9k_hw_common(ah);
 
 	while (time_out) {
-		if (REG_READ(ah, address) & bit_position) {
-			REG_WRITE(ah, address, bit_position);
-
-			if (address == AR_MCI_INTERRUPT_RX_MSG_RAW) {
-				if (bit_position &
-				    AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE)
-					ar9003_mci_reset_req_wakeup(ah);
-
-				if (bit_position &
-				    (AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING |
-				     AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING))
-					REG_WRITE(ah, AR_MCI_INTERRUPT_RAW,
-					AR_MCI_INTERRUPT_REMOTE_SLEEP_UPDATE);
-
-				REG_WRITE(ah, AR_MCI_INTERRUPT_RAW,
-					  AR_MCI_INTERRUPT_RX_MSG);
-			}
-			break;
-		}
+		if (!(REG_READ(ah, address) & bit_position)) {
+			udelay(10);
+			time_out -= 10;
 
-		udelay(10);
-		time_out -= 10;
+			if (time_out < 0)
+				break;
+			else
+				continue;
+		}
+		REG_WRITE(ah, address, bit_position);
 
-		if (time_out < 0)
+		if (address != AR_MCI_INTERRUPT_RX_MSG_RAW)
 			break;
+
+		if (bit_position & AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE)
+			ar9003_mci_reset_req_wakeup(ah);
+
+		if (bit_position & (AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING |
+				    AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING))
+			REG_WRITE(ah, AR_MCI_INTERRUPT_RAW,
+				  AR_MCI_INTERRUPT_REMOTE_SLEEP_UPDATE);
+
+		REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, AR_MCI_INTERRUPT_RX_MSG);
+		break;
 	}
 
 	if (time_out <= 0) {
@@ -127,14 +126,13 @@ static void ar9003_mci_send_coex_version_query(struct ath_hw *ah,
 	struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;
 	u32 payload[4] = {0, 0, 0, 0};
 
-	if (!mci->bt_version_known &&
-	    (mci->bt_state != MCI_BT_SLEEP)) {
-		MCI_GPM_SET_TYPE_OPCODE(payload,
-					MCI_GPM_COEX_AGENT,
-					MCI_GPM_COEX_VERSION_QUERY);
-		ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16,
-					wait_done, true);
-	}
+	if (mci->bt_version_known ||
+	    (mci->bt_state == MCI_BT_SLEEP))
+		return;
+
+	MCI_GPM_SET_TYPE_OPCODE(payload, MCI_GPM_COEX_AGENT,
+				MCI_GPM_COEX_VERSION_QUERY);
+	ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, wait_done, true);
 }
 
 static void ar9003_mci_send_coex_version_response(struct ath_hw *ah,
@@ -158,15 +156,14 @@ static void ar9003_mci_send_coex_wlan_channels(struct ath_hw *ah,
 	struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;
 	u32 *payload = &mci->wlan_channels[0];
 
-	if ((mci->wlan_channels_update == true) &&
-	    (mci->bt_state != MCI_BT_SLEEP)) {
-		MCI_GPM_SET_TYPE_OPCODE(payload,
-					MCI_GPM_COEX_AGENT,
-					MCI_GPM_COEX_WLAN_CHANNELS);
-		ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16,
-					wait_done, true);
-		MCI_GPM_SET_TYPE_OPCODE(payload, 0xff, 0xff);
-	}
+	if (!mci->wlan_channels_update ||
+	    (mci->bt_state == MCI_BT_SLEEP))
+		return;
+
+	MCI_GPM_SET_TYPE_OPCODE(payload, MCI_GPM_COEX_AGENT,
+				MCI_GPM_COEX_WLAN_CHANNELS);
+	ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, wait_done, true);
+	MCI_GPM_SET_TYPE_OPCODE(payload, 0xff, 0xff);
 }
 
 static void ar9003_mci_send_coex_bt_status_query(struct ath_hw *ah,
@@ -174,29 +171,30 @@ static void ar9003_mci_send_coex_bt_status_query(struct ath_hw *ah,
 {
 	struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;
 	u32 payload[4] = {0, 0, 0, 0};
-	bool query_btinfo = !!(query_type & (MCI_GPM_COEX_QUERY_BT_ALL_INFO |
-					     MCI_GPM_COEX_QUERY_BT_TOPOLOGY));
-
-	if (mci->bt_state != MCI_BT_SLEEP) {
+	bool query_btinfo;
 
-		MCI_GPM_SET_TYPE_OPCODE(payload, MCI_GPM_COEX_AGENT,
-					MCI_GPM_COEX_STATUS_QUERY);
+	if (mci->bt_state == MCI_BT_SLEEP)
+		return;
 
-		*(((u8 *)payload) + MCI_GPM_COEX_B_BT_BITMAP) = query_type;
+	query_btinfo = !!(query_type & (MCI_GPM_COEX_QUERY_BT_ALL_INFO |
+					MCI_GPM_COEX_QUERY_BT_TOPOLOGY));
+	MCI_GPM_SET_TYPE_OPCODE(payload, MCI_GPM_COEX_AGENT,
+				MCI_GPM_COEX_STATUS_QUERY);
 
-		/*
-		 * If bt_status_query message is  not sent successfully,
-		 * then need_flush_btinfo should be set again.
-		 */
-		if (!ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16,
-					     wait_done, true)) {
-			if (query_btinfo)
-				mci->need_flush_btinfo = true;
-		}
+	*(((u8 *)payload) + MCI_GPM_COEX_B_BT_BITMAP) = query_type;
 
+	/*
+	 * If bt_status_query message is  not sent successfully,
+	 * then need_flush_btinfo should be set again.
+	 */
+	if (!ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16,
+				wait_done, true)) {
 		if (query_btinfo)
-			mci->query_bt = false;
+			mci->need_flush_btinfo = true;
 	}
+
+	if (query_btinfo)
+		mci->query_bt = false;
 }
 
 static void ar9003_mci_send_coex_halt_bt_gpm(struct ath_hw *ah, bool halt,
@@ -241,73 +239,73 @@ static void ar9003_mci_prep_interface(struct ath_hw *ah)
 	ar9003_mci_remote_reset(ah, true);
 	ar9003_mci_send_req_wake(ah, true);
 
-	if (ar9003_mci_wait_for_interrupt(ah, AR_MCI_INTERRUPT_RX_MSG_RAW,
-				  AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING, 500)) {
+	if (!ar9003_mci_wait_for_interrupt(ah, AR_MCI_INTERRUPT_RX_MSG_RAW,
+				  AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING, 500))
+		goto clear_redunt;
 
-		mci->bt_state = MCI_BT_AWAKE;
+	mci->bt_state = MCI_BT_AWAKE;
 
-		/*
-		 * we don't need to send more remote_reset at this moment.
-		 * If BT receive first remote_reset, then BT HW will
-		 * be cleaned up and will be able to receive req_wake
-		 * and BT HW will respond sys_waking.
-		 * In this case, WLAN will receive BT's HW sys_waking.
-		 * Otherwise, if BT SW missed initial remote_reset,
-		 * that remote_reset will still clean up BT MCI RX,
-		 * and the req_wake will wake BT up,
-		 * and BT SW will respond this req_wake with a remote_reset and
-		 * sys_waking. In this case, WLAN will receive BT's SW
-		 * sys_waking. In either case, BT's RX is cleaned up. So we
-		 * don't need to reply BT's remote_reset now, if any.
-		 * Similarly, if in any case, WLAN can receive BT's sys_waking,
-		 * that means WLAN's RX is also fine.
-		 */
-		ar9003_mci_send_sys_waking(ah, true);
-		udelay(10);
+	/*
+	 * we don't need to send more remote_reset at this moment.
+	 * If BT receive first remote_reset, then BT HW will
+	 * be cleaned up and will be able to receive req_wake
+	 * and BT HW will respond sys_waking.
+	 * In this case, WLAN will receive BT's HW sys_waking.
+	 * Otherwise, if BT SW missed initial remote_reset,
+	 * that remote_reset will still clean up BT MCI RX,
+	 * and the req_wake will wake BT up,
+	 * and BT SW will respond this req_wake with a remote_reset and
+	 * sys_waking. In this case, WLAN will receive BT's SW
+	 * sys_waking. In either case, BT's RX is cleaned up. So we
+	 * don't need to reply BT's remote_reset now, if any.
+	 * Similarly, if in any case, WLAN can receive BT's sys_waking,
+	 * that means WLAN's RX is also fine.
+	 */
+	ar9003_mci_send_sys_waking(ah, true);
+	udelay(10);
 
-		/*
-		 * Set BT priority interrupt value to be 0xff to
-		 * avoid having too many BT PRIORITY interrupts.
-		 */
-		REG_WRITE(ah, AR_MCI_BT_PRI0, 0xFFFFFFFF);
-		REG_WRITE(ah, AR_MCI_BT_PRI1, 0xFFFFFFFF);
-		REG_WRITE(ah, AR_MCI_BT_PRI2, 0xFFFFFFFF);
-		REG_WRITE(ah, AR_MCI_BT_PRI3, 0xFFFFFFFF);
-		REG_WRITE(ah, AR_MCI_BT_PRI, 0X000000FF);
+	/*
+	 * Set BT priority interrupt value to be 0xff to
+	 * avoid having too many BT PRIORITY interrupts.
+	 */
+	REG_WRITE(ah, AR_MCI_BT_PRI0, 0xFFFFFFFF);
+	REG_WRITE(ah, AR_MCI_BT_PRI1, 0xFFFFFFFF);
+	REG_WRITE(ah, AR_MCI_BT_PRI2, 0xFFFFFFFF);
+	REG_WRITE(ah, AR_MCI_BT_PRI3, 0xFFFFFFFF);
+	REG_WRITE(ah, AR_MCI_BT_PRI, 0X000000FF);
 
-		/*
-		 * A contention reset will be received after send out
-		 * sys_waking. Also BT priority interrupt bits will be set.
-		 * Clear those bits before the next step.
-		 */
+	/*
+	 * A contention reset will be received after send out
+	 * sys_waking. Also BT priority interrupt bits will be set.
+	 * Clear those bits before the next step.
+	 */
 
-		REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW,
-			  AR_MCI_INTERRUPT_RX_MSG_CONT_RST);
-		REG_WRITE(ah, AR_MCI_INTERRUPT_RAW,
-			  AR_MCI_INTERRUPT_BT_PRI);
+	REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW,
+		  AR_MCI_INTERRUPT_RX_MSG_CONT_RST);
+	REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, AR_MCI_INTERRUPT_BT_PRI);
 
-		if (mci->is_2g) {
-			ar9003_mci_send_lna_transfer(ah, true);
-			udelay(5);
-		}
+	if (mci->is_2g) {
+		ar9003_mci_send_lna_transfer(ah, true);
+		udelay(5);
+	}
 
-		if ((mci->is_2g && !mci->update_2g5g)) {
-			if (ar9003_mci_wait_for_interrupt(ah,
-					  AR_MCI_INTERRUPT_RX_MSG_RAW,
-					  AR_MCI_INTERRUPT_RX_MSG_LNA_INFO,
-					  mci_timeout))
-				ath_dbg(common, MCI,
-					"MCI WLAN has control over the LNA & BT obeys it\n");
-			else
-				ath_dbg(common, MCI,
-					"MCI BT didn't respond to LNA_TRANS\n");
-		}
+	if ((mci->is_2g && !mci->update_2g5g)) {
+		if (ar9003_mci_wait_for_interrupt(ah,
+					AR_MCI_INTERRUPT_RX_MSG_RAW,
+					AR_MCI_INTERRUPT_RX_MSG_LNA_INFO,
+					mci_timeout))
+			ath_dbg(common, MCI,
+				"MCI WLAN has control over the LNA & BT obeys it\n");
+		else
+			ath_dbg(common, MCI,
+				"MCI BT didn't respond to LNA_TRANS\n");
 	}
 
+clear_redunt:
 	/* Clear the extra redundant SYS_WAKING from BT */
 	if ((mci->bt_state == MCI_BT_AWAKE) &&
-		(REG_READ_FIELD(ah, AR_MCI_INTERRUPT_RX_MSG_RAW,
-				AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING)) &&
+	    (REG_READ_FIELD(ah, AR_MCI_INTERRUPT_RX_MSG_RAW,
+			    AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING)) &&
 	    (REG_READ_FIELD(ah, AR_MCI_INTERRUPT_RX_MSG_RAW,
 			    AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING) == 0)) {
 		REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW,
@@ -330,7 +328,6 @@ void ar9003_mci_set_full_sleep(struct ath_hw *ah)
 	}
 
 	mci->ready = false;
-	REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
 }
 
 static void ar9003_mci_disable_interrupt(struct ath_hw *ah)
@@ -615,9 +612,9 @@ static u32 ar9003_mci_wait_for_gpm(struct ath_hw *ah, u8 gpm_type,
 				}
 				break;
 			}
-		} else if ((recv_type == gpm_type) && (recv_opcode == gpm_opcode)) {
+		} else if ((recv_type == gpm_type) &&
+			   (recv_opcode == gpm_opcode))
 			break;
-		}
 
 		/*
 		 * check if it's cal_grant
@@ -731,38 +728,38 @@ int ar9003_mci_end_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 	if (!IS_CHAN_2GHZ(chan) || (mci_hw->bt_state != MCI_BT_SLEEP))
 		goto exit;
 
-	if (ar9003_mci_check_int(ah, AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET) ||
-	    ar9003_mci_check_int(ah, AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE)) {
+	if (!ar9003_mci_check_int(ah, AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET) &&
+	    !ar9003_mci_check_int(ah, AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE))
+		goto exit;
 
-		/*
-		 * BT is sleeping. Check if BT wakes up during
-		 * WLAN calibration. If BT wakes up during
-		 * WLAN calibration, need to go through all
-		 * message exchanges again and recal.
-		 */
-		REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW,
-			  AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET |
-			  AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE);
+	/*
+	 * BT is sleeping. Check if BT wakes up during
+	 * WLAN calibration. If BT wakes up during
+	 * WLAN calibration, need to go through all
+	 * message exchanges again and recal.
+	 */
+	REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW,
+		  (AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET |
+		   AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE));
 
-		ar9003_mci_remote_reset(ah, true);
-		ar9003_mci_send_sys_waking(ah, true);
-		udelay(1);
+	ar9003_mci_remote_reset(ah, true);
+	ar9003_mci_send_sys_waking(ah, true);
+	udelay(1);
 
-		if (IS_CHAN_2GHZ(chan))
-			ar9003_mci_send_lna_transfer(ah, true);
+	if (IS_CHAN_2GHZ(chan))
+		ar9003_mci_send_lna_transfer(ah, true);
 
-		mci_hw->bt_state = MCI_BT_AWAKE;
+	mci_hw->bt_state = MCI_BT_AWAKE;
 
-		if (caldata) {
-			caldata->done_txiqcal_once = false;
-			caldata->done_txclcal_once = false;
-			caldata->rtt_done = false;
-		}
+	if (caldata) {
+		caldata->done_txiqcal_once = false;
+		caldata->done_txclcal_once = false;
+		caldata->rtt_done = false;
+	}
 
-		if (!ath9k_hw_init_cal(ah, chan))
-			return -EIO;
+	if (!ath9k_hw_init_cal(ah, chan))
+		return -EIO;
 
-	}
 exit:
 	ar9003_mci_enable_interrupt(ah);
 	return 0;
@@ -798,29 +795,27 @@ static void ar9003_mci_osla_setup(struct ath_hw *ah, bool enable)
 	struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;
 	u32 thresh;
 
-	if (enable) {
-		REG_RMW_FIELD(ah, AR_MCI_SCHD_TABLE_2,
-			      AR_MCI_SCHD_TABLE_2_HW_BASED, 1);
-		REG_RMW_FIELD(ah, AR_MCI_SCHD_TABLE_2,
-			      AR_MCI_SCHD_TABLE_2_MEM_BASED, 1);
-
-		if (!(mci->config & ATH_MCI_CONFIG_DISABLE_AGGR_THRESH)) {
-			thresh = MS(mci->config, ATH_MCI_CONFIG_AGGR_THRESH);
-			REG_RMW_FIELD(ah, AR_BTCOEX_CTRL,
-				      AR_BTCOEX_CTRL_AGGR_THRESH, thresh);
-			REG_RMW_FIELD(ah, AR_BTCOEX_CTRL,
-				      AR_BTCOEX_CTRL_TIME_TO_NEXT_BT_THRESH_EN, 1);
-		} else {
-			REG_RMW_FIELD(ah, AR_BTCOEX_CTRL,
-				      AR_BTCOEX_CTRL_TIME_TO_NEXT_BT_THRESH_EN, 0);
-		}
-
-		REG_RMW_FIELD(ah, AR_BTCOEX_CTRL,
-			      AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN, 1);
-	} else {
+	if (!enable) {
 		REG_CLR_BIT(ah, AR_BTCOEX_CTRL,
 			    AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN);
+		return;
 	}
+	REG_RMW_FIELD(ah, AR_MCI_SCHD_TABLE_2, AR_MCI_SCHD_TABLE_2_HW_BASED, 1);
+	REG_RMW_FIELD(ah, AR_MCI_SCHD_TABLE_2,
+		      AR_MCI_SCHD_TABLE_2_MEM_BASED, 1);
+
+	if (!(mci->config & ATH_MCI_CONFIG_DISABLE_AGGR_THRESH)) {
+		thresh = MS(mci->config, ATH_MCI_CONFIG_AGGR_THRESH);
+		REG_RMW_FIELD(ah, AR_BTCOEX_CTRL,
+			      AR_BTCOEX_CTRL_AGGR_THRESH, thresh);
+		REG_RMW_FIELD(ah, AR_BTCOEX_CTRL,
+			      AR_BTCOEX_CTRL_TIME_TO_NEXT_BT_THRESH_EN, 1);
+	} else
+		REG_RMW_FIELD(ah, AR_BTCOEX_CTRL,
+			      AR_BTCOEX_CTRL_TIME_TO_NEXT_BT_THRESH_EN, 0);
+
+	REG_RMW_FIELD(ah, AR_BTCOEX_CTRL,
+		      AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN, 1);
 }
 
 void ar9003_mci_reset(struct ath_hw *ah, bool en_int, bool is_2g,
@@ -943,26 +938,27 @@ static void ar9003_mci_send_2g5g_status(struct ath_hw *ah, bool wait_done)
 	struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;
 	u32 new_flags, to_set, to_clear;
 
-	if (mci->update_2g5g && (mci->bt_state != MCI_BT_SLEEP)) {
-		if (mci->is_2g) {
-			new_flags = MCI_2G_FLAGS;
-			to_clear = MCI_2G_FLAGS_CLEAR_MASK;
-			to_set = MCI_2G_FLAGS_SET_MASK;
-		} else {
-			new_flags = MCI_5G_FLAGS;
-			to_clear = MCI_5G_FLAGS_CLEAR_MASK;
-			to_set = MCI_5G_FLAGS_SET_MASK;
-		}
+	if (!mci->update_2g5g || (mci->bt_state == MCI_BT_SLEEP))
+		return;
+
+	if (mci->is_2g) {
+		new_flags = MCI_2G_FLAGS;
+		to_clear = MCI_2G_FLAGS_CLEAR_MASK;
+		to_set = MCI_2G_FLAGS_SET_MASK;
+	} else {
+		new_flags = MCI_5G_FLAGS;
+		to_clear = MCI_5G_FLAGS_CLEAR_MASK;
+		to_set = MCI_5G_FLAGS_SET_MASK;
+	}
 
-		if (to_clear)
-			ar9003_mci_send_coex_bt_flags(ah, wait_done,
+	if (to_clear)
+		ar9003_mci_send_coex_bt_flags(ah, wait_done,
 					      MCI_GPM_COEX_BT_FLAGS_CLEAR,
 					      to_clear);
-		if (to_set)
-			ar9003_mci_send_coex_bt_flags(ah, wait_done,
+	if (to_set)
+		ar9003_mci_send_coex_bt_flags(ah, wait_done,
 					      MCI_GPM_COEX_BT_FLAGS_SET,
 					      to_set);
-	}
 }
 
 static void ar9003_mci_queue_unsent_gpm(struct ath_hw *ah, u8 header,
@@ -1018,34 +1014,34 @@ void ar9003_mci_2g5g_switch(struct ath_hw *ah, bool wait_done)
 {
 	struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;
 
-	if (mci->update_2g5g) {
-		if (mci->is_2g) {
-			ar9003_mci_send_2g5g_status(ah, true);
-			ar9003_mci_send_lna_transfer(ah, true);
-			udelay(5);
+	if (!mci->update_2g5g)
+		return;
 
-			REG_CLR_BIT(ah, AR_MCI_TX_CTRL,
-				    AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE);
-			REG_CLR_BIT(ah, AR_PHY_GLB_CONTROL,
-				    AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL);
+	if (mci->is_2g) {
+		ar9003_mci_send_2g5g_status(ah, true);
+		ar9003_mci_send_lna_transfer(ah, true);
+		udelay(5);
 
-			if (!(mci->config & ATH_MCI_CONFIG_DISABLE_OSLA)) {
-				REG_SET_BIT(ah, AR_BTCOEX_CTRL,
-					    AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN);
-			}
-		} else {
-			ar9003_mci_send_lna_take(ah, true);
-			udelay(5);
-
-			REG_SET_BIT(ah, AR_MCI_TX_CTRL,
-				    AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE);
-			REG_SET_BIT(ah, AR_PHY_GLB_CONTROL,
-				    AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL);
-			REG_CLR_BIT(ah, AR_BTCOEX_CTRL,
+		REG_CLR_BIT(ah, AR_MCI_TX_CTRL,
+			    AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE);
+		REG_CLR_BIT(ah, AR_PHY_GLB_CONTROL,
+			    AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL);
+
+		if (!(mci->config & ATH_MCI_CONFIG_DISABLE_OSLA))
+			REG_SET_BIT(ah, AR_BTCOEX_CTRL,
 				    AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN);
+	} else {
+		ar9003_mci_send_lna_take(ah, true);
+		udelay(5);
 
-			ar9003_mci_send_2g5g_status(ah, true);
-		}
+		REG_SET_BIT(ah, AR_MCI_TX_CTRL,
+			    AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE);
+		REG_SET_BIT(ah, AR_PHY_GLB_CONTROL,
+			    AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL);
+		REG_CLR_BIT(ah, AR_BTCOEX_CTRL,
+			    AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN);
+
+		ar9003_mci_send_2g5g_status(ah, true);
 	}
 }
 
@@ -1132,7 +1128,7 @@ void ar9003_mci_init_cal_req(struct ath_hw *ah, bool *is_reusable)
 	if (ar9003_mci_wait_for_gpm(ah, MCI_GPM_BT_CAL_GRANT, 0, 50000)) {
 		ath_dbg(common, MCI, "MCI BT_CAL_GRANT received\n");
 	} else {
-		is_reusable = false;
+		*is_reusable = false;
 		ath_dbg(common, MCI, "MCI BT_CAL_GRANT not received\n");
 	}
 }
@@ -1259,12 +1255,12 @@ u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type, u32 *p_data)
 			}
 			if (p_data)
 				*p_data = more_gpm;
-			}
+		}
 
-			if (value != MCI_GPM_INVALID)
-				value <<= 4;
+		if (value != MCI_GPM_INVALID)
+			value <<= 4;
 
-			break;
+		break;
 	case MCI_STATE_LAST_SCHD_MSG_OFFSET:
 		value = MS(REG_READ(ah, AR_MCI_RX_STATUS),
 				    AR_MCI_RX_LAST_SCHD_MSG_INDEX);
@@ -1359,24 +1355,22 @@ u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type, u32 *p_data)
 		ar9003_mci_send_coex_bt_status_query(ah, true, query_type);
 		break;
 	case MCI_STATE_NEED_FLUSH_BT_INFO:
-			/*
-			 * btcoex_hw.mci.unhalt_bt_gpm means whether it's
-			 * needed to send UNHALT message. It's set whenever
-			 * there's a request to send HALT message.
-			 * mci_halted_bt_gpm means whether HALT message is sent
-			 * out successfully.
-			 *
-			 * Checking (mci_unhalt_bt_gpm == false) instead of
-			 * checking (ah->mci_halted_bt_gpm == false) will make
-			 * sure currently is in UNHALT-ed mode and BT can
-			 * respond to status query.
-			 */
-			value = (!mci->unhalt_bt_gpm &&
-				 mci->need_flush_btinfo) ? 1 : 0;
-			if (p_data)
-				mci->need_flush_btinfo =
-					(*p_data != 0) ? true : false;
-			break;
+		/*
+		 * btcoex_hw.mci.unhalt_bt_gpm means whether it's
+		 * needed to send UNHALT message. It's set whenever
+		 * there's a request to send HALT message.
+		 * mci_halted_bt_gpm means whether HALT message is sent
+		 * out successfully.
+		 *
+		 * Checking (mci_unhalt_bt_gpm == false) instead of
+		 * checking (ah->mci_halted_bt_gpm == false) will make
+		 * sure currently is in UNHALT-ed mode and BT can
+		 * respond to status query.
+		 */
+		value = (!mci->unhalt_bt_gpm && mci->need_flush_btinfo) ? 1 : 0;
+		if (p_data)
+			mci->need_flush_btinfo = (*p_data != 0) ? true : false;
+		break;
 	case MCI_STATE_RECOVER_RX:
 		ar9003_mci_prep_interface(ah);
 		mci->query_bt = true;
@@ -1387,9 +1381,6 @@ u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type, u32 *p_data)
 	case MCI_STATE_NEED_FTP_STOMP:
 		value = !(mci->config & ATH_MCI_CONFIG_DISABLE_FTP_STOMP);
 		break;
-	case MCI_STATE_NEED_TUNING:
-		value = !(mci->config & ATH_MCI_CONFIG_DISABLE_TUNING);
-		break;
 	default:
 		break;
 	}
@@ -1397,3 +1388,19 @@ u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type, u32 *p_data)
 	return value;
 }
 EXPORT_SYMBOL(ar9003_mci_state);
+
+void ar9003_mci_bt_gain_ctrl(struct ath_hw *ah)
+{
+	struct ath_common *common = ath9k_hw_common(ah);
+	struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;
+
+	ath_dbg(common, MCI, "Give LNA and SPDT control to BT\n");
+
+	REG_SET_BIT(ah, AR_PHY_GLB_CONTROL, AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL);
+	mci->is_2g = false;
+	mci->update_2g5g = true;
+	ar9003_mci_send_2g5g_status(ah, true);
+
+	/* Force another 2g5g update at next scanning */
+	mci->update_2g5g = true;
+}

+ 1 - 9
drivers/net/wireless/ath/ath9k/ar9003_mci.h

@@ -212,7 +212,6 @@ enum mci_state_type {
 	MCI_STATE_SET_CONCUR_TX_PRI,
 	MCI_STATE_RECOVER_RX,
 	MCI_STATE_NEED_FTP_STOMP,
-	MCI_STATE_NEED_TUNING,
 	MCI_STATE_DEBUG,
 	MCI_STATE_MAX
 };
@@ -266,6 +265,7 @@ void ar9003_mci_setup(struct ath_hw *ah, u32 gpm_addr, void *gpm_buf,
 void ar9003_mci_cleanup(struct ath_hw *ah);
 void ar9003_mci_get_interrupt(struct ath_hw *ah, u32 *raw_intr,
 			      u32 *rx_msg_intr);
+void ar9003_mci_bt_gain_ctrl(struct ath_hw *ah);
 
 /*
  * These functions are used by ath9k_hw.
@@ -273,10 +273,6 @@ void ar9003_mci_get_interrupt(struct ath_hw *ah, u32 *raw_intr,
 
 #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
 
-static inline bool ar9003_mci_is_ready(struct ath_hw *ah)
-{
-	return ah->btcoex_hw.mci.ready;
-}
 void ar9003_mci_stop_bt(struct ath_hw *ah, bool save_fullsleep);
 void ar9003_mci_init_cal_req(struct ath_hw *ah, bool *is_reusable);
 void ar9003_mci_init_cal_done(struct ath_hw *ah);
@@ -292,10 +288,6 @@ void ar9003_mci_get_isr(struct ath_hw *ah, enum ath9k_int *masked);
 
 #else
 
-static inline bool ar9003_mci_is_ready(struct ath_hw *ah)
-{
-	return false;
-}
 static inline void ar9003_mci_stop_bt(struct ath_hw *ah, bool save_fullsleep)
 {
 }

+ 4 - 0
drivers/net/wireless/ath/ath9k/ar9003_phy.c

@@ -676,6 +676,10 @@ static int ar9003_hw_process_ini(struct ath_hw *ah,
 	if (chan->channel == 2484)
 		ar9003_hw_prog_ini(ah, &ah->ini_japan2484, 1);
 
+	if (AR_SREV_9462(ah))
+		REG_WRITE(ah, AR_GLB_SWREG_DISCONT_MODE,
+			  AR_GLB_SWREG_DISCONT_EN_BT_WLAN);
+
 	ah->modes_index = modesIndex;
 	ar9003_hw_override_ini(ah);
 	ar9003_hw_set_channel_regs(ah, chan);

+ 20 - 12
drivers/net/wireless/ath/ath9k/ar9003_phy.h

@@ -820,18 +820,26 @@
 #define AR_PHY_CHAN_INFO_MEMORY_CAPTURE_MASK         0x0001
 #define AR_PHY_RX_DELAY_DELAY   0x00003FFF
 #define AR_PHY_CCK_TX_CTRL_JAPAN    0x00000010
-#define AR_PHY_SPECTRAL_SCAN_ENABLE         0x00000001
-#define AR_PHY_SPECTRAL_SCAN_ENABLE_S       0
-#define AR_PHY_SPECTRAL_SCAN_ACTIVE         0x00000002
-#define AR_PHY_SPECTRAL_SCAN_ACTIVE_S       1
-#define AR_PHY_SPECTRAL_SCAN_FFT_PERIOD     0x000000F0
-#define AR_PHY_SPECTRAL_SCAN_FFT_PERIOD_S   4
-#define AR_PHY_SPECTRAL_SCAN_PERIOD         0x0000FF00
-#define AR_PHY_SPECTRAL_SCAN_PERIOD_S       8
-#define AR_PHY_SPECTRAL_SCAN_COUNT          0x00FF0000
-#define AR_PHY_SPECTRAL_SCAN_COUNT_S        16
-#define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT   0x01000000
-#define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_S 24
+
+#define AR_PHY_SPECTRAL_SCAN_ENABLE           0x00000001
+#define AR_PHY_SPECTRAL_SCAN_ENABLE_S         0
+#define AR_PHY_SPECTRAL_SCAN_ACTIVE           0x00000002
+#define AR_PHY_SPECTRAL_SCAN_ACTIVE_S         1
+#define AR_PHY_SPECTRAL_SCAN_FFT_PERIOD       0x000000F0
+#define AR_PHY_SPECTRAL_SCAN_FFT_PERIOD_S     4
+#define AR_PHY_SPECTRAL_SCAN_PERIOD           0x0000FF00
+#define AR_PHY_SPECTRAL_SCAN_PERIOD_S         8
+#define AR_PHY_SPECTRAL_SCAN_COUNT            0x0FFF0000
+#define AR_PHY_SPECTRAL_SCAN_COUNT_S          16
+#define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT     0x10000000
+#define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_S   28
+#define AR_PHY_SPECTRAL_SCAN_PRIORITY         0x20000000
+#define AR_PHY_SPECTRAL_SCAN_PRIORITY_S       29
+#define AR_PHY_SPECTRAL_SCAN_USE_ERR5         0x40000000
+#define AR_PHY_SPECTRAL_SCAN_USE_ERR5_S       30
+#define AR_PHY_SPECTRAL_SCAN_COMPRESSED_RPT   0x80000000
+#define AR_PHY_SPECTRAL_SCAN_COMPRESSED_RPT_S 31
+
 #define AR_PHY_CHANNEL_STATUS_RX_CLEAR      0x00000004
 #define AR_PHY_RTT_CTRL_ENA_RADIO_RETENTION     0x00000001
 #define AR_PHY_RTT_CTRL_ENA_RADIO_RETENTION_S   0

+ 3 - 2
drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h

@@ -958,7 +958,7 @@ static const u32 ar9462_2p0_radio_core[][2] = {
 	{0x0001604c, 0x2699e04f},
 	{0x00016050, 0x6db6db6c},
 	{0x00016058, 0x6c200000},
-	{0x00016080, 0x00040000},
+	{0x00016080, 0x000c0000},
 	{0x00016084, 0x9a68048c},
 	{0x00016088, 0x54214514},
 	{0x0001608c, 0x1203040b},
@@ -981,7 +981,7 @@ static const u32 ar9462_2p0_radio_core[][2] = {
 	{0x00016144, 0x02084080},
 	{0x00016148, 0x000080c0},
 	{0x00016280, 0x050a0001},
-	{0x00016284, 0x3d841400},
+	{0x00016284, 0x3d841418},
 	{0x00016288, 0x00000000},
 	{0x0001628c, 0xe3000000},
 	{0x00016290, 0xa1005080},
@@ -1007,6 +1007,7 @@ static const u32 ar9462_2p0_radio_core[][2] = {
 
 static const u32 ar9462_2p0_soc_preamble[][2] = {
 	/* Addr      allmodes  */
+	{0x000040a4 ,0x00a0c1c9},
 	{0x00007020, 0x00000000},
 	{0x00007034, 0x00000002},
 	{0x00007038, 0x000004c2},

+ 34 - 15
drivers/net/wireless/ath/ath9k/ath9k.h

@@ -307,6 +307,7 @@ struct ath_rx {
 	u8 defant;
 	u8 rxotherant;
 	u32 *rxlink;
+	u32 num_pkts;
 	unsigned int rxfilter;
 	spinlock_t rxbuflock;
 	struct list_head rxbuf;
@@ -325,6 +326,9 @@ int ath_rx_init(struct ath_softc *sc, int nbufs);
 void ath_rx_cleanup(struct ath_softc *sc);
 int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp);
 struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype);
+void ath_txq_lock(struct ath_softc *sc, struct ath_txq *txq);
+void ath_txq_unlock(struct ath_softc *sc, struct ath_txq *txq);
+void ath_txq_unlock_complete(struct ath_softc *sc, struct ath_txq *txq);
 void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq);
 bool ath_drain_all_txq(struct ath_softc *sc, bool retry_tx);
 void ath_draintxq(struct ath_softc *sc,
@@ -414,9 +418,9 @@ int ath_beaconq_config(struct ath_softc *sc);
 void ath_set_beacon(struct ath_softc *sc);
 void ath9k_set_beaconing_status(struct ath_softc *sc, bool status);
 
-/*******/
-/* ANI */
-/*******/
+/*******************/
+/* Link Monitoring */
+/*******************/
 
 #define ATH_STA_SHORT_CALINTERVAL 1000    /* 1 second */
 #define ATH_AP_SHORT_CALINTERVAL  100     /* 100 ms */
@@ -427,7 +431,9 @@ void ath9k_set_beaconing_status(struct ath_softc *sc, bool status);
 #define ATH_RESTART_CALINTERVAL   1200000 /* 20 minutes */
 
 #define ATH_PAPRD_TIMEOUT	100 /* msecs */
+#define ATH_PLL_WORK_INTERVAL   100
 
+void ath_tx_complete_poll_work(struct work_struct *work);
 void ath_reset_work(struct work_struct *work);
 void ath_hw_check(struct work_struct *work);
 void ath_hw_pll_work(struct work_struct *work);
@@ -436,22 +442,31 @@ void ath_start_rx_poll(struct ath_softc *sc, u8 nbeacon);
 void ath_paprd_calibrate(struct work_struct *work);
 void ath_ani_calibrate(unsigned long data);
 void ath_start_ani(struct ath_common *common);
+int ath_update_survey_stats(struct ath_softc *sc);
+void ath_update_survey_nf(struct ath_softc *sc, int channel);
 
 /**********/
 /* BTCOEX */
 /**********/
 
+enum bt_op_flags {
+	BT_OP_PRIORITY_DETECTED,
+	BT_OP_SCAN,
+};
+
 struct ath_btcoex {
 	bool hw_timer_enabled;
 	spinlock_t btcoex_lock;
 	struct timer_list period_timer; /* Timer for BT period */
 	u32 bt_priority_cnt;
 	unsigned long bt_priority_time;
+	unsigned long op_flags;
 	int bt_stomp_type; /* Types of BT stomping */
 	u32 btcoex_no_stomp; /* in usec */
 	u32 btcoex_period; /* in usec */
 	u32 btscan_no_stomp; /* in usec */
 	u32 duty_cycle;
+	u32 bt_wait_time;
 	struct ath_gen_timer *no_stomp_timer; /* Timer for no BT stomping */
 	struct ath_mci_profile mci;
 };
@@ -513,8 +528,10 @@ static inline void ath_deinit_leds(struct ath_softc *sc)
 }
 #endif
 
-
+/*******************************/
 /* Antenna diversity/combining */
+/*******************************/
+
 #define ATH_ANT_RX_CURRENT_SHIFT 4
 #define ATH_ANT_RX_MAIN_SHIFT 2
 #define ATH_ANT_RX_MASK 0x3
@@ -567,6 +584,9 @@ struct ath_ant_comb {
 	unsigned long scan_start_time;
 };
 
+void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs);
+void ath_ant_comb_update(struct ath_softc *sc);
+
 /********************/
 /* Main driver core */
 /********************/
@@ -584,15 +604,15 @@ struct ath_ant_comb {
 #define ATH_TXPOWER_MAX         100     /* .5 dBm units */
 #define ATH_RATE_DUMMY_MARKER   0
 
-#define SC_OP_INVALID                BIT(0)
-#define SC_OP_BEACONS                BIT(1)
-#define SC_OP_OFFCHANNEL             BIT(2)
-#define SC_OP_RXFLUSH                BIT(3)
-#define SC_OP_TSF_RESET              BIT(4)
-#define SC_OP_BT_PRIORITY_DETECTED   BIT(5)
-#define SC_OP_BT_SCAN                BIT(6)
-#define SC_OP_ANI_RUN                BIT(7)
-#define SC_OP_PRIM_STA_VIF           BIT(8)
+enum sc_op_flags {
+	SC_OP_INVALID,
+	SC_OP_BEACONS,
+	SC_OP_RXFLUSH,
+	SC_OP_TSF_RESET,
+	SC_OP_ANI_RUN,
+	SC_OP_PRIM_STA_VIF,
+	SC_OP_HW_RESET,
+};
 
 /* Powersave flags */
 #define PS_WAIT_FOR_BEACON        BIT(0)
@@ -638,9 +658,9 @@ struct ath_softc {
 	struct completion paprd_complete;
 
 	unsigned int hw_busy_count;
+	unsigned long sc_flags;
 
 	u32 intrstatus;
-	u32 sc_flags; /* SC_OP_* */
 	u16 ps_flags; /* PS_* */
 	u16 curtxpow;
 	bool ps_enabled;
@@ -736,5 +756,4 @@ void ath9k_calculate_iter_data(struct ieee80211_hw *hw,
 			       struct ieee80211_vif *vif,
 			       struct ath9k_vif_iter_data *iter_data);
 
-
 #endif /* ATH9K_H */

+ 13 - 10
drivers/net/wireless/ath/ath9k/beacon.c

@@ -48,7 +48,10 @@ int ath_beaconq_config(struct ath_softc *sc)
 		txq = sc->tx.txq_map[WME_AC_BE];
 		ath9k_hw_get_txq_props(ah, txq->axq_qnum, &qi_be);
 		qi.tqi_aifs = qi_be.tqi_aifs;
-		qi.tqi_cwmin = 4*qi_be.tqi_cwmin;
+		if (ah->slottime == ATH9K_SLOT_TIME_20)
+			qi.tqi_cwmin = 2*qi_be.tqi_cwmin;
+		else
+			qi.tqi_cwmin = 4*qi_be.tqi_cwmin;
 		qi.tqi_cwmax = qi_be.tqi_cwmax;
 	}
 
@@ -387,7 +390,7 @@ void ath_beacon_tasklet(unsigned long data)
 		} else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) {
 			ath_dbg(common, BSTUCK, "beacon is officially stuck\n");
 			sc->beacon.bmisscnt = 0;
-			sc->sc_flags |= SC_OP_TSF_RESET;
+			set_bit(SC_OP_TSF_RESET, &sc->sc_flags);
 			ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
 		}
 
@@ -477,16 +480,16 @@ static void ath9k_beacon_init(struct ath_softc *sc,
 			      u32 next_beacon,
 			      u32 beacon_period)
 {
-	if (sc->sc_flags & SC_OP_TSF_RESET) {
+	if (test_bit(SC_OP_TSF_RESET, &sc->sc_flags)) {
 		ath9k_ps_wakeup(sc);
 		ath9k_hw_reset_tsf(sc->sc_ah);
 	}
 
 	ath9k_hw_beaconinit(sc->sc_ah, next_beacon, beacon_period);
 
-	if (sc->sc_flags & SC_OP_TSF_RESET) {
+	if (test_bit(SC_OP_TSF_RESET, &sc->sc_flags)) {
 		ath9k_ps_restore(sc);
-		sc->sc_flags &= ~SC_OP_TSF_RESET;
+		clear_bit(SC_OP_TSF_RESET, &sc->sc_flags);
 	}
 }
 
@@ -516,7 +519,7 @@ static void ath_beacon_config_ap(struct ath_softc *sc,
 	/* Set the computed AP beacon timers */
 
 	ath9k_hw_disable_interrupts(ah);
-	sc->sc_flags |= SC_OP_TSF_RESET;
+	set_bit(SC_OP_TSF_RESET, &sc->sc_flags);
 	ath9k_beacon_init(sc, nexttbtt, intval);
 	sc->beacon.bmisscnt = 0;
 	ath9k_hw_set_interrupts(ah);
@@ -659,7 +662,7 @@ static void ath_beacon_config_adhoc(struct ath_softc *sc,
 	u32 tsf, intval, nexttbtt;
 
 	ath9k_reset_beacon_status(sc);
-	if (!(sc->sc_flags & SC_OP_BEACONS))
+	if (!test_bit(SC_OP_BEACONS, &sc->sc_flags))
 		ath9k_hw_settsf64(ah, sc->beacon.bc_tstamp);
 
 	intval = TU_TO_USEC(conf->beacon_interval);
@@ -724,7 +727,7 @@ static bool ath9k_allow_beacon_config(struct ath_softc *sc,
 	 */
 	if ((sc->sc_ah->opmode == NL80211_IFTYPE_STATION) &&
 	    (vif->type == NL80211_IFTYPE_STATION) &&
-	    (sc->sc_flags & SC_OP_BEACONS) &&
+	    test_bit(SC_OP_BEACONS, &sc->sc_flags) &&
 	    !avp->primary_sta_vif) {
 		ath_dbg(common, CONFIG,
 			"Beacon already configured for a station interface\n");
@@ -810,7 +813,7 @@ void ath_set_beacon(struct ath_softc *sc)
 		return;
 	}
 
-	sc->sc_flags |= SC_OP_BEACONS;
+	set_bit(SC_OP_BEACONS, &sc->sc_flags);
 }
 
 void ath9k_set_beaconing_status(struct ath_softc *sc, bool status)
@@ -818,7 +821,7 @@ void ath9k_set_beaconing_status(struct ath_softc *sc, bool status)
 	struct ath_hw *ah = sc->sc_ah;
 
 	if (!ath_has_valid_bslot(sc)) {
-		sc->sc_flags &= ~SC_OP_BEACONS;
+		clear_bit(SC_OP_BEACONS, &sc->sc_flags);
 		return;
 	}
 

+ 8 - 2
drivers/net/wireless/ath/ath9k/btcoex.c

@@ -336,10 +336,16 @@ static void ar9003_btcoex_bt_stomp(struct ath_hw *ah,
 			 enum ath_stomp_type stomp_type)
 {
 	struct ath_btcoex_hw *btcoex = &ah->btcoex_hw;
-	const u32 *weight = AR_SREV_9462(ah) ? ar9003_wlan_weights[stomp_type] :
-					       ar9462_wlan_weights[stomp_type];
+	const u32 *weight = ar9003_wlan_weights[stomp_type];
 	int i;
 
+	if (AR_SREV_9462(ah)) {
+		if ((stomp_type == ATH_BTCOEX_STOMP_LOW) &&
+		    btcoex->mci.stomp_ftp)
+			stomp_type = ATH_BTCOEX_STOMP_LOW_FTP;
+		weight = ar9462_wlan_weights[stomp_type];
+	}
+
 	for (i = 0; i < AR9300_NUM_WLAN_WEIGHTS; i++) {
 		btcoex->bt_weight[i] = AR9300_BT_WGHT;
 		btcoex->wlan_weight[i] = weight[i];

+ 4 - 0
drivers/net/wireless/ath/ath9k/btcoex.h

@@ -36,6 +36,9 @@
 #define ATH_BT_CNT_THRESHOLD	       3
 #define ATH_BT_CNT_SCAN_THRESHOLD      15
 
+#define ATH_BTCOEX_RX_WAIT_TIME       100
+#define ATH_BTCOEX_STOMP_FTP_THRESH   5
+
 #define AR9300_NUM_BT_WEIGHTS   4
 #define AR9300_NUM_WLAN_WEIGHTS 4
 /* Defines the BT AR_BT_COEX_WGHT used */
@@ -80,6 +83,7 @@ struct ath9k_hw_mci {
 	u8 bt_ver_major;
 	u8 bt_ver_minor;
 	u8 bt_state;
+	u8 stomp_ftp;
 };
 
 struct ath_btcoex_hw {

+ 6 - 3
drivers/net/wireless/ath/ath9k/debug.c

@@ -205,10 +205,10 @@ static ssize_t write_file_disable_ani(struct file *file,
 	common->disable_ani = !!disable_ani;
 
 	if (disable_ani) {
-		sc->sc_flags &= ~SC_OP_ANI_RUN;
+		clear_bit(SC_OP_ANI_RUN, &sc->sc_flags);
 		del_timer_sync(&common->ani.timer);
 	} else {
-		sc->sc_flags |= SC_OP_ANI_RUN;
+		set_bit(SC_OP_ANI_RUN, &sc->sc_flags);
 		ath_start_ani(common);
 	}
 
@@ -374,6 +374,8 @@ void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status)
 		sc->debug.stats.istats.dtim++;
 	if (status & ATH9K_INT_TSFOOR)
 		sc->debug.stats.istats.tsfoor++;
+	if (status & ATH9K_INT_MCI)
+		sc->debug.stats.istats.mci++;
 }
 
 static ssize_t read_file_interrupt(struct file *file, char __user *user_buf,
@@ -418,6 +420,7 @@ static ssize_t read_file_interrupt(struct file *file, char __user *user_buf,
 	PR_IS("DTIMSYNC", dtimsync);
 	PR_IS("DTIM", dtim);
 	PR_IS("TSFOOR", tsfoor);
+	PR_IS("MCI", mci);
 	PR_IS("TOTAL", total);
 
 	len += snprintf(buf + len, mxlen - len,
@@ -1318,7 +1321,7 @@ static int open_file_bb_mac_samps(struct inode *inode, struct file *file)
 	u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
 	u8 nread;
 
-	if (sc->sc_flags & SC_OP_INVALID)
+	if (test_bit(SC_OP_INVALID, &sc->sc_flags))
 		return -EAGAIN;
 
 	buf = vmalloc(size);

+ 1 - 0
drivers/net/wireless/ath/ath9k/debug.h

@@ -86,6 +86,7 @@ struct ath_interrupt_stats {
 	u32 dtim;
 	u32 bb_watchdog;
 	u32 tsfoor;
+	u32 mci;
 
 	/* Sync-cause stats */
 	u32 sync_cause_all;

+ 25 - 11
drivers/net/wireless/ath/ath9k/gpio.c

@@ -132,17 +132,18 @@ static void ath_detect_bt_priority(struct ath_softc *sc)
 
 	if (time_after(jiffies, btcoex->bt_priority_time +
 			msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) {
-		sc->sc_flags &= ~(SC_OP_BT_PRIORITY_DETECTED | SC_OP_BT_SCAN);
+		clear_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags);
+		clear_bit(BT_OP_SCAN, &btcoex->op_flags);
 		/* Detect if colocated bt started scanning */
 		if (btcoex->bt_priority_cnt >= ATH_BT_CNT_SCAN_THRESHOLD) {
 			ath_dbg(ath9k_hw_common(sc->sc_ah), BTCOEX,
 				"BT scan detected\n");
-			sc->sc_flags |= (SC_OP_BT_SCAN |
-					 SC_OP_BT_PRIORITY_DETECTED);
+			set_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags);
+			set_bit(BT_OP_SCAN, &btcoex->op_flags);
 		} else if (btcoex->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) {
 			ath_dbg(ath9k_hw_common(sc->sc_ah), BTCOEX,
 				"BT priority traffic detected\n");
-			sc->sc_flags |= SC_OP_BT_PRIORITY_DETECTED;
+			set_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags);
 		}
 
 		btcoex->bt_priority_cnt = 0;
@@ -190,13 +191,26 @@ static void ath_btcoex_period_timer(unsigned long data)
 	struct ath_softc *sc = (struct ath_softc *) data;
 	struct ath_hw *ah = sc->sc_ah;
 	struct ath_btcoex *btcoex = &sc->btcoex;
+	struct ath_mci_profile *mci = &btcoex->mci;
 	u32 timer_period;
 	bool is_btscan;
 
 	ath9k_ps_wakeup(sc);
 	if (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI))
 		ath_detect_bt_priority(sc);
-	is_btscan = sc->sc_flags & SC_OP_BT_SCAN;
+	is_btscan = test_bit(BT_OP_SCAN, &btcoex->op_flags);
+
+	btcoex->bt_wait_time += btcoex->btcoex_period;
+	if (btcoex->bt_wait_time > ATH_BTCOEX_RX_WAIT_TIME) {
+		if (ar9003_mci_state(ah, MCI_STATE_NEED_FTP_STOMP, NULL) &&
+		    (mci->num_pan || mci->num_other_acl))
+			ah->btcoex_hw.mci.stomp_ftp =
+				(sc->rx.num_pkts < ATH_BTCOEX_STOMP_FTP_THRESH);
+		else
+			ah->btcoex_hw.mci.stomp_ftp = false;
+		btcoex->bt_wait_time = 0;
+		sc->rx.num_pkts = 0;
+	}
 
 	spin_lock_bh(&btcoex->btcoex_lock);
 
@@ -219,8 +233,7 @@ static void ath_btcoex_period_timer(unsigned long data)
 
 	ath9k_ps_restore(sc);
 	timer_period = btcoex->btcoex_period / 1000;
-	mod_timer(&btcoex->period_timer, jiffies +
-				  msecs_to_jiffies(timer_period));
+	mod_timer(&btcoex->period_timer, jiffies + msecs_to_jiffies(timer_period));
 }
 
 /*
@@ -233,14 +246,14 @@ static void ath_btcoex_no_stomp_timer(void *arg)
 	struct ath_hw *ah = sc->sc_ah;
 	struct ath_btcoex *btcoex = &sc->btcoex;
 	struct ath_common *common = ath9k_hw_common(ah);
-	bool is_btscan = sc->sc_flags & SC_OP_BT_SCAN;
 
 	ath_dbg(common, BTCOEX, "no stomp timer running\n");
 
 	ath9k_ps_wakeup(sc);
 	spin_lock_bh(&btcoex->btcoex_lock);
 
-	if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW || is_btscan)
+	if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW ||
+	    test_bit(BT_OP_SCAN, &btcoex->op_flags))
 		ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_NONE);
 	 else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL)
 		ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_LOW);
@@ -292,7 +305,7 @@ void ath9k_btcoex_timer_resume(struct ath_softc *sc)
 
 	btcoex->bt_priority_cnt = 0;
 	btcoex->bt_priority_time = jiffies;
-	sc->sc_flags &= ~(SC_OP_BT_PRIORITY_DETECTED | SC_OP_BT_SCAN);
+	btcoex->op_flags &= ~(BT_OP_PRIORITY_DETECTED | BT_OP_SCAN);
 
 	mod_timer(&btcoex->period_timer, jiffies);
 }
@@ -316,12 +329,13 @@ void ath9k_btcoex_timer_pause(struct ath_softc *sc)
 
 u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen)
 {
+	struct ath_btcoex *btcoex = &sc->btcoex;
 	struct ath_mci_profile *mci = &sc->btcoex.mci;
 	u16 aggr_limit = 0;
 
 	if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_MCI) && mci->aggr_limit)
 		aggr_limit = (max_4ms_framelen * mci->aggr_limit) >> 4;
-	else if (sc->sc_flags & SC_OP_BT_PRIORITY_DETECTED)
+	else if (test_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags))
 		aggr_limit = min((max_4ms_framelen * 3) / 8,
 				 (u32)ATH_AMPDU_LIMIT_MAX);
 

+ 90 - 122
drivers/net/wireless/ath/ath9k/hw.c

@@ -390,14 +390,6 @@ static void ath9k_hw_disablepcie(struct ath_hw *ah)
 	REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000);
 }
 
-static void ath9k_hw_aspm_init(struct ath_hw *ah)
-{
-	struct ath_common *common = ath9k_hw_common(ah);
-
-	if (common->bus_ops->aspm_init)
-		common->bus_ops->aspm_init(common);
-}
-
 /* This should work for all families including legacy */
 static bool ath9k_hw_chip_test(struct ath_hw *ah)
 {
@@ -693,9 +685,6 @@ static int __ath9k_hw_init(struct ath_hw *ah)
 	if (r)
 		return r;
 
-	if (ah->is_pciexpress)
-		ath9k_hw_aspm_init(ah);
-
 	r = ath9k_hw_init_macaddr(ah);
 	if (r) {
 		ath_err(common, "Failed to initialize MAC address\n");
@@ -1443,9 +1432,6 @@ static bool ath9k_hw_set_reset_reg(struct ath_hw *ah, u32 type)
 		break;
 	}
 
-	if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI)
-		REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
-
 	return ret;
 }
 
@@ -1721,7 +1707,7 @@ static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan)
 	ath9k_hw_loadnf(ah, ah->curchan);
 	ath9k_hw_start_nfcal(ah, true);
 
-	if ((ah->caps.hw_caps & ATH9K_HW_CAP_MCI) && ar9003_mci_is_ready(ah))
+	if (ath9k_hw_mci_is_enabled(ah))
 		ar9003_mci_2g5g_switch(ah, true);
 
 	if (AR_SREV_9271(ah))
@@ -1742,10 +1728,9 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 	u64 tsf = 0;
 	int i, r;
 	bool start_mci_reset = false;
-	bool mci = !!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI);
 	bool save_fullsleep = ah->chip_fullsleep;
 
-	if (mci) {
+	if (ath9k_hw_mci_is_enabled(ah)) {
 		start_mci_reset = ar9003_mci_start_reset(ah, chan);
 		if (start_mci_reset)
 			return 0;
@@ -1774,7 +1759,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 			return r;
 	}
 
-	if (mci)
+	if (ath9k_hw_mci_is_enabled(ah))
 		ar9003_mci_stop_bt(ah, save_fullsleep);
 
 	saveDefAntenna = REG_READ(ah, AR_DEF_ANTENNA);
@@ -1832,7 +1817,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 	if (r)
 		return r;
 
-	if (mci)
+	if (ath9k_hw_mci_is_enabled(ah))
 		ar9003_mci_reset(ah, false, IS_CHAN_2GHZ(chan), save_fullsleep);
 
 	/*
@@ -1951,7 +1936,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 	ath9k_hw_loadnf(ah, chan);
 	ath9k_hw_start_nfcal(ah, true);
 
-	if (mci && ar9003_mci_end_reset(ah, chan, caldata))
+	if (ath9k_hw_mci_is_enabled(ah) && ar9003_mci_end_reset(ah, chan, caldata))
 		return -EIO;
 
 	ENABLE_REGWRITE_BUFFER(ah);
@@ -1996,7 +1981,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 	if (ath9k_hw_btcoex_is_enabled(ah))
 		ath9k_hw_btcoex_enable(ah);
 
-	if (mci)
+	if (ath9k_hw_mci_is_enabled(ah))
 		ar9003_mci_check_bt(ah);
 
 	if (AR_SREV_9300_20_OR_LATER(ah)) {
@@ -2019,39 +2004,35 @@ EXPORT_SYMBOL(ath9k_hw_reset);
  * Notify Power Mgt is disabled in self-generated frames.
  * If requested, force chip to sleep.
  */
-static void ath9k_set_power_sleep(struct ath_hw *ah, int setChip)
+static void ath9k_set_power_sleep(struct ath_hw *ah)
 {
 	REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
-	if (setChip) {
-		if (AR_SREV_9462(ah)) {
-			REG_WRITE(ah, AR_TIMER_MODE,
-				  REG_READ(ah, AR_TIMER_MODE) & 0xFFFFFF00);
-			REG_WRITE(ah, AR_NDP2_TIMER_MODE, REG_READ(ah,
-				  AR_NDP2_TIMER_MODE) & 0xFFFFFF00);
-			REG_WRITE(ah, AR_SLP32_INC,
-				  REG_READ(ah, AR_SLP32_INC) & 0xFFF00000);
-			/* xxx Required for WLAN only case ? */
-			REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN, 0);
-			udelay(100);
-		}
 
-		/*
-		 * Clear the RTC force wake bit to allow the
-		 * mac to go to sleep.
-		 */
-		REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN);
+	if (AR_SREV_9462(ah)) {
+		REG_CLR_BIT(ah, AR_TIMER_MODE, 0xff);
+		REG_CLR_BIT(ah, AR_NDP2_TIMER_MODE, 0xff);
+		REG_CLR_BIT(ah, AR_SLP32_INC, 0xfffff);
+		/* xxx Required for WLAN only case ? */
+		REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN, 0);
+		udelay(100);
+	}
 
-		if (AR_SREV_9462(ah))
-			udelay(100);
+	/*
+	 * Clear the RTC force wake bit to allow the
+	 * mac to go to sleep.
+	 */
+	REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN);
+
+	if (ath9k_hw_mci_is_enabled(ah))
+		udelay(100);
 
-		if (!AR_SREV_9100(ah) && !AR_SREV_9300_20_OR_LATER(ah))
-			REG_WRITE(ah, AR_RC, AR_RC_AHB | AR_RC_HOSTIF);
+	if (!AR_SREV_9100(ah) && !AR_SREV_9300_20_OR_LATER(ah))
+		REG_WRITE(ah, AR_RC, AR_RC_AHB | AR_RC_HOSTIF);
 
-		/* Shutdown chip. Active low */
-		if (!AR_SREV_5416(ah) && !AR_SREV_9271(ah)) {
-			REG_CLR_BIT(ah, AR_RTC_RESET, AR_RTC_RESET_EN);
-			udelay(2);
-		}
+	/* Shutdown chip. Active low */
+	if (!AR_SREV_5416(ah) && !AR_SREV_9271(ah)) {
+		REG_CLR_BIT(ah, AR_RTC_RESET, AR_RTC_RESET_EN);
+		udelay(2);
 	}
 
 	/* Clear Bit 14 of AR_WA after putting chip into Full Sleep mode. */
@@ -2064,44 +2045,38 @@ static void ath9k_set_power_sleep(struct ath_hw *ah, int setChip)
  * frames. If request, set power mode of chip to
  * auto/normal.  Duration in units of 128us (1/8 TU).
  */
-static void ath9k_set_power_network_sleep(struct ath_hw *ah, int setChip)
+static void ath9k_set_power_network_sleep(struct ath_hw *ah)
 {
-	u32 val;
+	struct ath9k_hw_capabilities *pCap = &ah->caps;
 
 	REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
-	if (setChip) {
-		struct ath9k_hw_capabilities *pCap = &ah->caps;
 
-		if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) {
-			/* Set WakeOnInterrupt bit; clear ForceWake bit */
-			REG_WRITE(ah, AR_RTC_FORCE_WAKE,
-				  AR_RTC_FORCE_WAKE_ON_INT);
-		} else {
+	if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) {
+		/* Set WakeOnInterrupt bit; clear ForceWake bit */
+		REG_WRITE(ah, AR_RTC_FORCE_WAKE,
+			  AR_RTC_FORCE_WAKE_ON_INT);
+	} else {
 
-			/* When chip goes into network sleep, it could be waken
-			 * up by MCI_INT interrupt caused by BT's HW messages
-			 * (LNA_xxx, CONT_xxx) which chould be in a very fast
-			 * rate (~100us). This will cause chip to leave and
-			 * re-enter network sleep mode frequently, which in
-			 * consequence will have WLAN MCI HW to generate lots of
-			 * SYS_WAKING and SYS_SLEEPING messages which will make
-			 * BT CPU to busy to process.
-			 */
-			if (AR_SREV_9462(ah)) {
-				val = REG_READ(ah, AR_MCI_INTERRUPT_RX_MSG_EN) &
-					~AR_MCI_INTERRUPT_RX_HW_MSG_MASK;
-				REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN, val);
-			}
-			/*
-			 * Clear the RTC force wake bit to allow the
-			 * mac to go to sleep.
-			 */
-			REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE,
-				    AR_RTC_FORCE_WAKE_EN);
-
-			if (AR_SREV_9462(ah))
-				udelay(30);
-		}
+		/* When chip goes into network sleep, it could be waken
+		 * up by MCI_INT interrupt caused by BT's HW messages
+		 * (LNA_xxx, CONT_xxx) which chould be in a very fast
+		 * rate (~100us). This will cause chip to leave and
+		 * re-enter network sleep mode frequently, which in
+		 * consequence will have WLAN MCI HW to generate lots of
+		 * SYS_WAKING and SYS_SLEEPING messages which will make
+		 * BT CPU to busy to process.
+		 */
+		if (ath9k_hw_mci_is_enabled(ah))
+			REG_CLR_BIT(ah, AR_MCI_INTERRUPT_RX_MSG_EN,
+				    AR_MCI_INTERRUPT_RX_HW_MSG_MASK);
+		/*
+		 * Clear the RTC force wake bit to allow the
+		 * mac to go to sleep.
+		 */
+		REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN);
+
+		if (ath9k_hw_mci_is_enabled(ah))
+			udelay(30);
 	}
 
 	/* Clear Bit 14 of AR_WA after putting chip into Net Sleep mode. */
@@ -2109,7 +2084,7 @@ static void ath9k_set_power_network_sleep(struct ath_hw *ah, int setChip)
 		REG_WRITE(ah, AR_WA, ah->WARegVal & ~AR_WA_D3_L1_DISABLE);
 }
 
-static bool ath9k_hw_set_power_awake(struct ath_hw *ah, int setChip)
+static bool ath9k_hw_set_power_awake(struct ath_hw *ah)
 {
 	u32 val;
 	int i;
@@ -2120,37 +2095,35 @@ static bool ath9k_hw_set_power_awake(struct ath_hw *ah, int setChip)
 		udelay(10);
 	}
 
-	if (setChip) {
-		if ((REG_READ(ah, AR_RTC_STATUS) &
-		     AR_RTC_STATUS_M) == AR_RTC_STATUS_SHUTDOWN) {
-			if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) {
-				return false;
-			}
-			if (!AR_SREV_9300_20_OR_LATER(ah))
-				ath9k_hw_init_pll(ah, NULL);
+	if ((REG_READ(ah, AR_RTC_STATUS) &
+	     AR_RTC_STATUS_M) == AR_RTC_STATUS_SHUTDOWN) {
+		if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) {
+			return false;
 		}
-		if (AR_SREV_9100(ah))
-			REG_SET_BIT(ah, AR_RTC_RESET,
-				    AR_RTC_RESET_EN);
+		if (!AR_SREV_9300_20_OR_LATER(ah))
+			ath9k_hw_init_pll(ah, NULL);
+	}
+	if (AR_SREV_9100(ah))
+		REG_SET_BIT(ah, AR_RTC_RESET,
+			    AR_RTC_RESET_EN);
 
+	REG_SET_BIT(ah, AR_RTC_FORCE_WAKE,
+		    AR_RTC_FORCE_WAKE_EN);
+	udelay(50);
+
+	for (i = POWER_UP_TIME / 50; i > 0; i--) {
+		val = REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M;
+		if (val == AR_RTC_STATUS_ON)
+			break;
+		udelay(50);
 		REG_SET_BIT(ah, AR_RTC_FORCE_WAKE,
 			    AR_RTC_FORCE_WAKE_EN);
-		udelay(50);
-
-		for (i = POWER_UP_TIME / 50; i > 0; i--) {
-			val = REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M;
-			if (val == AR_RTC_STATUS_ON)
-				break;
-			udelay(50);
-			REG_SET_BIT(ah, AR_RTC_FORCE_WAKE,
-				    AR_RTC_FORCE_WAKE_EN);
-		}
-		if (i == 0) {
-			ath_err(ath9k_hw_common(ah),
-				"Failed to wakeup in %uus\n",
-				POWER_UP_TIME / 20);
-			return false;
-		}
+	}
+	if (i == 0) {
+		ath_err(ath9k_hw_common(ah),
+			"Failed to wakeup in %uus\n",
+			POWER_UP_TIME / 20);
+		return false;
 	}
 
 	REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
@@ -2161,7 +2134,7 @@ static bool ath9k_hw_set_power_awake(struct ath_hw *ah, int setChip)
 bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode)
 {
 	struct ath_common *common = ath9k_hw_common(ah);
-	int status = true, setChip = true;
+	int status = true;
 	static const char *modes[] = {
 		"AWAKE",
 		"FULL-SLEEP",
@@ -2177,25 +2150,17 @@ bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode)
 
 	switch (mode) {
 	case ATH9K_PM_AWAKE:
-		status = ath9k_hw_set_power_awake(ah, setChip);
-
-		if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI)
-			REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
-
+		status = ath9k_hw_set_power_awake(ah);
 		break;
 	case ATH9K_PM_FULL_SLEEP:
-		if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI)
+		if (ath9k_hw_mci_is_enabled(ah))
 			ar9003_mci_set_full_sleep(ah);
 
-		ath9k_set_power_sleep(ah, setChip);
+		ath9k_set_power_sleep(ah);
 		ah->chip_fullsleep = true;
 		break;
 	case ATH9K_PM_NETWORK_SLEEP:
-
-		if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI)
-			REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
-
-		ath9k_set_power_network_sleep(ah, setChip);
+		ath9k_set_power_network_sleep(ah);
 		break;
 	default:
 		ath_err(common, "Unknown power mode %u\n", mode);
@@ -2765,6 +2730,9 @@ EXPORT_SYMBOL(ath9k_hw_setrxfilter);
 
 bool ath9k_hw_phy_disable(struct ath_hw *ah)
 {
+	if (ath9k_hw_mci_is_enabled(ah))
+		ar9003_mci_bt_gain_ctrl(ah);
+
 	if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_WARM))
 		return false;
 

+ 9 - 1
drivers/net/wireless/ath/ath9k/hw.h

@@ -824,7 +824,6 @@ struct ath_hw {
 	struct ar5416IniArray ini_japan2484;
 	struct ar5416IniArray iniModes_9271_ANI_reg;
 	struct ar5416IniArray ini_radio_post_sys2ant;
-	struct ar5416IniArray ini_BTCOEX_MAX_TXPWR;
 
 	struct ar5416IniArray iniMac[ATH_INI_NUM_SPLIT];
 	struct ar5416IniArray iniBB[ATH_INI_NUM_SPLIT];
@@ -1037,6 +1036,11 @@ static inline bool ath9k_hw_btcoex_is_enabled(struct ath_hw *ah)
 {
 	return ah->btcoex_hw.enabled;
 }
+static inline bool ath9k_hw_mci_is_enabled(struct ath_hw *ah)
+{
+	return ah->btcoex_hw.enabled && (ah->caps.hw_caps & ATH9K_HW_CAP_MCI);
+
+}
 void ath9k_hw_btcoex_enable(struct ath_hw *ah);
 static inline enum ath_btcoex_scheme
 ath9k_hw_get_btcoex_scheme(struct ath_hw *ah)
@@ -1048,6 +1052,10 @@ static inline bool ath9k_hw_btcoex_is_enabled(struct ath_hw *ah)
 {
 	return false;
 }
+static inline bool ath9k_hw_mci_is_enabled(struct ath_hw *ah)
+{
+	return false;
+}
 static inline void ath9k_hw_btcoex_enable(struct ath_hw *ah)
 {
 }

+ 10 - 8
drivers/net/wireless/ath/ath9k/init.c

@@ -489,6 +489,7 @@ static void ath9k_init_misc(struct ath_softc *sc)
 
 	setup_timer(&common->ani.timer, ath_ani_calibrate, (unsigned long)sc);
 
+	sc->last_rssi = ATH_RSSI_DUMMY_MARKER;
 	sc->config.txpowlimit = ATH_TXPOWER_MAX;
 	memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
 	sc->beacon.slottime = ATH9K_SLOT_TIME_9;
@@ -560,6 +561,12 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
 	tasklet_init(&sc->bcon_tasklet, ath_beacon_tasklet,
 		     (unsigned long)sc);
 
+	INIT_WORK(&sc->hw_reset_work, ath_reset_work);
+	INIT_WORK(&sc->hw_check_work, ath_hw_check);
+	INIT_WORK(&sc->paprd_work, ath_paprd_calibrate);
+	INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work);
+	setup_timer(&sc->rx_poll_timer, ath_rx_poll, (unsigned long)sc);
+
 	/*
 	 * Cache line size is used to size and align various
 	 * structures used to communicate with the hardware.
@@ -590,6 +597,9 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
 	ath9k_cmn_init_crypto(sc->sc_ah);
 	ath9k_init_misc(sc);
 
+	if (common->bus_ops->aspm_init)
+		common->bus_ops->aspm_init(common);
+
 	return 0;
 
 err_btcoex:
@@ -782,11 +792,6 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc,
 		ARRAY_SIZE(ath9k_tpt_blink));
 #endif
 
-	INIT_WORK(&sc->hw_reset_work, ath_reset_work);
-	INIT_WORK(&sc->hw_check_work, ath_hw_check);
-	INIT_WORK(&sc->paprd_work, ath_paprd_calibrate);
-	INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work);
-
 	/* Register with mac80211 */
 	error = ieee80211_register_hw(hw);
 	if (error)
@@ -805,9 +810,6 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc,
 			goto error_world;
 	}
 
-	setup_timer(&sc->rx_poll_timer, ath_rx_poll, (unsigned long)sc);
-	sc->last_rssi = ATH_RSSI_DUMMY_MARKER;
-
 	ath_init_leds(sc);
 	ath_start_rfkill_poll(sc);
 

+ 502 - 0
drivers/net/wireless/ath/ath9k/link.c

@@ -0,0 +1,502 @@
+/*
+ * Copyright (c) 2012 Qualcomm Atheros, 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 "ath9k.h"
+
+/*
+ * TX polling - checks if the TX engine is stuck somewhere
+ * and issues a chip reset if so.
+ */
+void ath_tx_complete_poll_work(struct work_struct *work)
+{
+	struct ath_softc *sc = container_of(work, struct ath_softc,
+					    tx_complete_work.work);
+	struct ath_txq *txq;
+	int i;
+	bool needreset = false;
+#ifdef CONFIG_ATH9K_DEBUGFS
+	sc->tx_complete_poll_work_seen++;
+#endif
+
+	for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
+		if (ATH_TXQ_SETUP(sc, i)) {
+			txq = &sc->tx.txq[i];
+			ath_txq_lock(sc, txq);
+			if (txq->axq_depth) {
+				if (txq->axq_tx_inprogress) {
+					needreset = true;
+					ath_txq_unlock(sc, txq);
+					break;
+				} else {
+					txq->axq_tx_inprogress = true;
+				}
+			}
+			ath_txq_unlock_complete(sc, txq);
+		}
+
+	if (needreset) {
+		ath_dbg(ath9k_hw_common(sc->sc_ah), RESET,
+			"tx hung, resetting the chip\n");
+		RESET_STAT_INC(sc, RESET_TYPE_TX_HANG);
+		ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
+		return;
+	}
+
+	ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work,
+				     msecs_to_jiffies(ATH_TX_COMPLETE_POLL_INT));
+}
+
+/*
+ * Checks if the BB/MAC is hung.
+ */
+void ath_hw_check(struct work_struct *work)
+{
+	struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work);
+	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+	unsigned long flags;
+	int busy;
+	u8 is_alive, nbeacon = 1;
+
+	ath9k_ps_wakeup(sc);
+	is_alive = ath9k_hw_check_alive(sc->sc_ah);
+
+	if (is_alive && !AR_SREV_9300(sc->sc_ah))
+		goto out;
+	else if (!is_alive && AR_SREV_9300(sc->sc_ah)) {
+		ath_dbg(common, RESET,
+			"DCU stuck is detected. Schedule chip reset\n");
+		RESET_STAT_INC(sc, RESET_TYPE_MAC_HANG);
+		goto sched_reset;
+	}
+
+	spin_lock_irqsave(&common->cc_lock, flags);
+	busy = ath_update_survey_stats(sc);
+	spin_unlock_irqrestore(&common->cc_lock, flags);
+
+	ath_dbg(common, RESET, "Possible baseband hang, busy=%d (try %d)\n",
+		busy, sc->hw_busy_count + 1);
+	if (busy >= 99) {
+		if (++sc->hw_busy_count >= 3) {
+			RESET_STAT_INC(sc, RESET_TYPE_BB_HANG);
+			goto sched_reset;
+		}
+	} else if (busy >= 0) {
+		sc->hw_busy_count = 0;
+		nbeacon = 3;
+	}
+
+	ath_start_rx_poll(sc, nbeacon);
+	goto out;
+
+sched_reset:
+	ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
+out:
+	ath9k_ps_restore(sc);
+}
+
+/*
+ * PLL-WAR for AR9485/AR9340
+ */
+static bool ath_hw_pll_rx_hang_check(struct ath_softc *sc, u32 pll_sqsum)
+{
+	static int count;
+	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+
+	if (pll_sqsum >= 0x40000) {
+		count++;
+		if (count == 3) {
+			ath_dbg(common, RESET, "PLL WAR, resetting the chip\n");
+			RESET_STAT_INC(sc, RESET_TYPE_PLL_HANG);
+			ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
+			count = 0;
+			return true;
+		}
+	} else {
+		count = 0;
+	}
+
+	return false;
+}
+
+void ath_hw_pll_work(struct work_struct *work)
+{
+	u32 pll_sqsum;
+	struct ath_softc *sc = container_of(work, struct ath_softc,
+					    hw_pll_work.work);
+
+	ath9k_ps_wakeup(sc);
+	pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah);
+	ath9k_ps_restore(sc);
+	if (ath_hw_pll_rx_hang_check(sc, pll_sqsum))
+		return;
+
+	ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work,
+				     msecs_to_jiffies(ATH_PLL_WORK_INTERVAL));
+}
+
+/*
+ * RX Polling - monitors baseband hangs.
+ */
+void ath_start_rx_poll(struct ath_softc *sc, u8 nbeacon)
+{
+	if (!AR_SREV_9300(sc->sc_ah))
+		return;
+
+	if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags))
+		return;
+
+	mod_timer(&sc->rx_poll_timer, jiffies + msecs_to_jiffies
+		  (nbeacon * sc->cur_beacon_conf.beacon_interval));
+}
+
+void ath_rx_poll(unsigned long data)
+{
+	struct ath_softc *sc = (struct ath_softc *)data;
+
+	ieee80211_queue_work(sc->hw, &sc->hw_check_work);
+}
+
+/*
+ * PA Pre-distortion.
+ */
+static void ath_paprd_activate(struct ath_softc *sc)
+{
+	struct ath_hw *ah = sc->sc_ah;
+	struct ath9k_hw_cal_data *caldata = ah->caldata;
+	int chain;
+
+	if (!caldata || !caldata->paprd_done)
+		return;
+
+	ath9k_ps_wakeup(sc);
+	ar9003_paprd_enable(ah, false);
+	for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
+		if (!(ah->txchainmask & BIT(chain)))
+			continue;
+
+		ar9003_paprd_populate_single_table(ah, caldata, chain);
+	}
+
+	ar9003_paprd_enable(ah, true);
+	ath9k_ps_restore(sc);
+}
+
+static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int chain)
+{
+	struct ieee80211_hw *hw = sc->hw;
+	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
+	struct ath_hw *ah = sc->sc_ah;
+	struct ath_common *common = ath9k_hw_common(ah);
+	struct ath_tx_control txctl;
+	int time_left;
+
+	memset(&txctl, 0, sizeof(txctl));
+	txctl.txq = sc->tx.txq_map[WME_AC_BE];
+
+	memset(tx_info, 0, sizeof(*tx_info));
+	tx_info->band = hw->conf.channel->band;
+	tx_info->flags |= IEEE80211_TX_CTL_NO_ACK;
+	tx_info->control.rates[0].idx = 0;
+	tx_info->control.rates[0].count = 1;
+	tx_info->control.rates[0].flags = IEEE80211_TX_RC_MCS;
+	tx_info->control.rates[1].idx = -1;
+
+	init_completion(&sc->paprd_complete);
+	txctl.paprd = BIT(chain);
+
+	if (ath_tx_start(hw, skb, &txctl) != 0) {
+		ath_dbg(common, CALIBRATE, "PAPRD TX failed\n");
+		dev_kfree_skb_any(skb);
+		return false;
+	}
+
+	time_left = wait_for_completion_timeout(&sc->paprd_complete,
+			msecs_to_jiffies(ATH_PAPRD_TIMEOUT));
+
+	if (!time_left)
+		ath_dbg(common, CALIBRATE,
+			"Timeout waiting for paprd training on TX chain %d\n",
+			chain);
+
+	return !!time_left;
+}
+
+void ath_paprd_calibrate(struct work_struct *work)
+{
+	struct ath_softc *sc = container_of(work, struct ath_softc, paprd_work);
+	struct ieee80211_hw *hw = sc->hw;
+	struct ath_hw *ah = sc->sc_ah;
+	struct ieee80211_hdr *hdr;
+	struct sk_buff *skb = NULL;
+	struct ath9k_hw_cal_data *caldata = ah->caldata;
+	struct ath_common *common = ath9k_hw_common(ah);
+	int ftype;
+	int chain_ok = 0;
+	int chain;
+	int len = 1800;
+
+	if (!caldata)
+		return;
+
+	ath9k_ps_wakeup(sc);
+
+	if (ar9003_paprd_init_table(ah) < 0)
+		goto fail_paprd;
+
+	skb = alloc_skb(len, GFP_KERNEL);
+	if (!skb)
+		goto fail_paprd;
+
+	skb_put(skb, len);
+	memset(skb->data, 0, len);
+	hdr = (struct ieee80211_hdr *)skb->data;
+	ftype = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC;
+	hdr->frame_control = cpu_to_le16(ftype);
+	hdr->duration_id = cpu_to_le16(10);
+	memcpy(hdr->addr1, hw->wiphy->perm_addr, ETH_ALEN);
+	memcpy(hdr->addr2, hw->wiphy->perm_addr, ETH_ALEN);
+	memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN);
+
+	for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
+		if (!(ah->txchainmask & BIT(chain)))
+			continue;
+
+		chain_ok = 0;
+
+		ath_dbg(common, CALIBRATE,
+			"Sending PAPRD frame for thermal measurement on chain %d\n",
+			chain);
+		if (!ath_paprd_send_frame(sc, skb, chain))
+			goto fail_paprd;
+
+		ar9003_paprd_setup_gain_table(ah, chain);
+
+		ath_dbg(common, CALIBRATE,
+			"Sending PAPRD training frame on chain %d\n", chain);
+		if (!ath_paprd_send_frame(sc, skb, chain))
+			goto fail_paprd;
+
+		if (!ar9003_paprd_is_done(ah)) {
+			ath_dbg(common, CALIBRATE,
+				"PAPRD not yet done on chain %d\n", chain);
+			break;
+		}
+
+		if (ar9003_paprd_create_curve(ah, caldata, chain)) {
+			ath_dbg(common, CALIBRATE,
+				"PAPRD create curve failed on chain %d\n",
+				chain);
+			break;
+		}
+
+		chain_ok = 1;
+	}
+	kfree_skb(skb);
+
+	if (chain_ok) {
+		caldata->paprd_done = true;
+		ath_paprd_activate(sc);
+	}
+
+fail_paprd:
+	ath9k_ps_restore(sc);
+}
+
+/*
+ *  ANI performs periodic noise floor calibration
+ *  that is used to adjust and optimize the chip performance.  This
+ *  takes environmental changes (location, temperature) into account.
+ *  When the task is complete, it reschedules itself depending on the
+ *  appropriate interval that was calculated.
+ */
+void ath_ani_calibrate(unsigned long data)
+{
+	struct ath_softc *sc = (struct ath_softc *)data;
+	struct ath_hw *ah = sc->sc_ah;
+	struct ath_common *common = ath9k_hw_common(ah);
+	bool longcal = false;
+	bool shortcal = false;
+	bool aniflag = false;
+	unsigned int timestamp = jiffies_to_msecs(jiffies);
+	u32 cal_interval, short_cal_interval, long_cal_interval;
+	unsigned long flags;
+
+	if (ah->caldata && ah->caldata->nfcal_interference)
+		long_cal_interval = ATH_LONG_CALINTERVAL_INT;
+	else
+		long_cal_interval = ATH_LONG_CALINTERVAL;
+
+	short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ?
+		ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL;
+
+	/* Only calibrate if awake */
+	if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE)
+		goto set_timer;
+
+	ath9k_ps_wakeup(sc);
+
+	/* Long calibration runs independently of short calibration. */
+	if ((timestamp - common->ani.longcal_timer) >= long_cal_interval) {
+		longcal = true;
+		common->ani.longcal_timer = timestamp;
+	}
+
+	/* Short calibration applies only while caldone is false */
+	if (!common->ani.caldone) {
+		if ((timestamp - common->ani.shortcal_timer) >= short_cal_interval) {
+			shortcal = true;
+			common->ani.shortcal_timer = timestamp;
+			common->ani.resetcal_timer = timestamp;
+		}
+	} else {
+		if ((timestamp - common->ani.resetcal_timer) >=
+		    ATH_RESTART_CALINTERVAL) {
+			common->ani.caldone = ath9k_hw_reset_calvalid(ah);
+			if (common->ani.caldone)
+				common->ani.resetcal_timer = timestamp;
+		}
+	}
+
+	/* Verify whether we must check ANI */
+	if (sc->sc_ah->config.enable_ani
+	    && (timestamp - common->ani.checkani_timer) >=
+	    ah->config.ani_poll_interval) {
+		aniflag = true;
+		common->ani.checkani_timer = timestamp;
+	}
+
+	/* Call ANI routine if necessary */
+	if (aniflag) {
+		spin_lock_irqsave(&common->cc_lock, flags);
+		ath9k_hw_ani_monitor(ah, ah->curchan);
+		ath_update_survey_stats(sc);
+		spin_unlock_irqrestore(&common->cc_lock, flags);
+	}
+
+	/* Perform calibration if necessary */
+	if (longcal || shortcal) {
+		common->ani.caldone =
+			ath9k_hw_calibrate(ah, ah->curchan,
+					   ah->rxchainmask, longcal);
+	}
+
+	ath_dbg(common, ANI,
+		"Calibration @%lu finished: %s %s %s, caldone: %s\n",
+		jiffies,
+		longcal ? "long" : "", shortcal ? "short" : "",
+		aniflag ? "ani" : "", common->ani.caldone ? "true" : "false");
+
+	ath9k_ps_restore(sc);
+
+set_timer:
+	/*
+	* Set timer interval based on previous results.
+	* The interval must be the shortest necessary to satisfy ANI,
+	* short calibration and long calibration.
+	*/
+	ath9k_debug_samp_bb_mac(sc);
+	cal_interval = ATH_LONG_CALINTERVAL;
+	if (sc->sc_ah->config.enable_ani)
+		cal_interval = min(cal_interval,
+				   (u32)ah->config.ani_poll_interval);
+	if (!common->ani.caldone)
+		cal_interval = min(cal_interval, (u32)short_cal_interval);
+
+	mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval));
+	if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_PAPRD) && ah->caldata) {
+		if (!ah->caldata->paprd_done)
+			ieee80211_queue_work(sc->hw, &sc->paprd_work);
+		else if (!ah->paprd_table_write_done)
+			ath_paprd_activate(sc);
+	}
+}
+
+void ath_start_ani(struct ath_common *common)
+{
+	struct ath_hw *ah = common->ah;
+	unsigned long timestamp = jiffies_to_msecs(jiffies);
+	struct ath_softc *sc = (struct ath_softc *) common->priv;
+
+	if (!test_bit(SC_OP_ANI_RUN, &sc->sc_flags))
+		return;
+
+	if (sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)
+		return;
+
+	common->ani.longcal_timer = timestamp;
+	common->ani.shortcal_timer = timestamp;
+	common->ani.checkani_timer = timestamp;
+
+	mod_timer(&common->ani.timer,
+		  jiffies + msecs_to_jiffies((u32)ah->config.ani_poll_interval));
+}
+
+void ath_update_survey_nf(struct ath_softc *sc, int channel)
+{
+	struct ath_hw *ah = sc->sc_ah;
+	struct ath9k_channel *chan = &ah->channels[channel];
+	struct survey_info *survey = &sc->survey[channel];
+
+	if (chan->noisefloor) {
+		survey->filled |= SURVEY_INFO_NOISE_DBM;
+		survey->noise = ath9k_hw_getchan_noise(ah, chan);
+	}
+}
+
+/*
+ * Updates the survey statistics and returns the busy time since last
+ * update in %, if the measurement duration was long enough for the
+ * result to be useful, -1 otherwise.
+ */
+int ath_update_survey_stats(struct ath_softc *sc)
+{
+	struct ath_hw *ah = sc->sc_ah;
+	struct ath_common *common = ath9k_hw_common(ah);
+	int pos = ah->curchan - &ah->channels[0];
+	struct survey_info *survey = &sc->survey[pos];
+	struct ath_cycle_counters *cc = &common->cc_survey;
+	unsigned int div = common->clockrate * 1000;
+	int ret = 0;
+
+	if (!ah->curchan)
+		return -1;
+
+	if (ah->power_mode == ATH9K_PM_AWAKE)
+		ath_hw_cycle_counters_update(common);
+
+	if (cc->cycles > 0) {
+		survey->filled |= SURVEY_INFO_CHANNEL_TIME |
+			SURVEY_INFO_CHANNEL_TIME_BUSY |
+			SURVEY_INFO_CHANNEL_TIME_RX |
+			SURVEY_INFO_CHANNEL_TIME_TX;
+		survey->channel_time += cc->cycles / div;
+		survey->channel_time_busy += cc->rx_busy / div;
+		survey->channel_time_rx += cc->rx_frame / div;
+		survey->channel_time_tx += cc->tx_frame / div;
+	}
+
+	if (cc->cycles < div)
+		return -1;
+
+	if (cc->cycles > 0)
+		ret = cc->rx_busy * 100 / cc->cycles;
+
+	memset(cc, 0, sizeof(*cc));
+
+	ath_update_survey_nf(sc, pos);
+
+	return ret;
+}

+ 73 - 485
drivers/net/wireless/ath/ath9k/main.c

@@ -101,6 +101,7 @@ void ath9k_ps_wakeup(struct ath_softc *sc)
 		spin_lock(&common->cc_lock);
 		ath_hw_cycle_counters_update(common);
 		memset(&common->cc_survey, 0, sizeof(common->cc_survey));
+		memset(&common->cc_ani, 0, sizeof(common->cc_ani));
 		spin_unlock(&common->cc_lock);
 	}
 
@@ -143,84 +144,6 @@ void ath9k_ps_restore(struct ath_softc *sc)
 	spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
 }
 
-void ath_start_ani(struct ath_common *common)
-{
-	struct ath_hw *ah = common->ah;
-	unsigned long timestamp = jiffies_to_msecs(jiffies);
-	struct ath_softc *sc = (struct ath_softc *) common->priv;
-
-	if (!(sc->sc_flags & SC_OP_ANI_RUN))
-		return;
-
-	if (sc->sc_flags & SC_OP_OFFCHANNEL)
-		return;
-
-	common->ani.longcal_timer = timestamp;
-	common->ani.shortcal_timer = timestamp;
-	common->ani.checkani_timer = timestamp;
-
-	mod_timer(&common->ani.timer,
-		  jiffies +
-			msecs_to_jiffies((u32)ah->config.ani_poll_interval));
-}
-
-static void ath_update_survey_nf(struct ath_softc *sc, int channel)
-{
-	struct ath_hw *ah = sc->sc_ah;
-	struct ath9k_channel *chan = &ah->channels[channel];
-	struct survey_info *survey = &sc->survey[channel];
-
-	if (chan->noisefloor) {
-		survey->filled |= SURVEY_INFO_NOISE_DBM;
-		survey->noise = ath9k_hw_getchan_noise(ah, chan);
-	}
-}
-
-/*
- * Updates the survey statistics and returns the busy time since last
- * update in %, if the measurement duration was long enough for the
- * result to be useful, -1 otherwise.
- */
-static int ath_update_survey_stats(struct ath_softc *sc)
-{
-	struct ath_hw *ah = sc->sc_ah;
-	struct ath_common *common = ath9k_hw_common(ah);
-	int pos = ah->curchan - &ah->channels[0];
-	struct survey_info *survey = &sc->survey[pos];
-	struct ath_cycle_counters *cc = &common->cc_survey;
-	unsigned int div = common->clockrate * 1000;
-	int ret = 0;
-
-	if (!ah->curchan)
-		return -1;
-
-	if (ah->power_mode == ATH9K_PM_AWAKE)
-		ath_hw_cycle_counters_update(common);
-
-	if (cc->cycles > 0) {
-		survey->filled |= SURVEY_INFO_CHANNEL_TIME |
-			SURVEY_INFO_CHANNEL_TIME_BUSY |
-			SURVEY_INFO_CHANNEL_TIME_RX |
-			SURVEY_INFO_CHANNEL_TIME_TX;
-		survey->channel_time += cc->cycles / div;
-		survey->channel_time_busy += cc->rx_busy / div;
-		survey->channel_time_rx += cc->rx_frame / div;
-		survey->channel_time_tx += cc->tx_frame / div;
-	}
-
-	if (cc->cycles < div)
-		return -1;
-
-	if (cc->cycles > 0)
-		ret = cc->rx_busy * 100 / cc->cycles;
-
-	memset(cc, 0, sizeof(*cc));
-
-	ath_update_survey_nf(sc, pos);
-
-	return ret;
-}
-
 static void __ath_cancel_work(struct ath_softc *sc)
 {
 	cancel_work_sync(&sc->paprd_work);
@@ -235,6 +158,22 @@ static void ath_cancel_work(struct ath_softc *sc)
 	cancel_work_sync(&sc->hw_reset_work);
 }
 
+static void ath_restart_work(struct ath_softc *sc)
+{
+	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+
+	ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
+
+	if (AR_SREV_9485(sc->sc_ah) || AR_SREV_9340(sc->sc_ah))
+		ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work,
+				     msecs_to_jiffies(ATH_PLL_WORK_INTERVAL));
+
+	ath_start_rx_poll(sc, 3);
+
+	if (!common->disable_ani)
+		ath_start_ani(common);
+}
+
 static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush)
 {
 	struct ath_hw *ah = sc->sc_ah;
@@ -271,6 +210,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
 {
 	struct ath_hw *ah = sc->sc_ah;
 	struct ath_common *common = ath9k_hw_common(ah);
+	unsigned long flags;
 
 	if (ath_startrecv(sc) != 0) {
 		ath_err(common, "Unable to restart recv logic\n");
@@ -279,36 +219,30 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
 
 	ath9k_cmn_update_txpow(ah, sc->curtxpow,
 			       sc->config.txpowlimit, &sc->curtxpow);
+
+	clear_bit(SC_OP_HW_RESET, &sc->sc_flags);
 	ath9k_hw_set_interrupts(ah);
 	ath9k_hw_enable_interrupts(ah);
 
-	if (!(sc->sc_flags & (SC_OP_OFFCHANNEL)) && start) {
-		if (sc->sc_flags & SC_OP_BEACONS)
-			ath_set_beacon(sc);
+	if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) && start) {
+		if (!test_bit(SC_OP_BEACONS, &sc->sc_flags))
+			goto work;
 
-		ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
-		ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/2);
-		ath_start_rx_poll(sc, 3);
-		if (!common->disable_ani)
-			ath_start_ani(common);
-	}
-
-	if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx != 3) {
-		struct ath_hw_antcomb_conf div_ant_conf;
-		u8 lna_conf;
-
-		ath9k_hw_antdiv_comb_conf_get(ah, &div_ant_conf);
+		ath_set_beacon(sc);
 
-		if (sc->ant_rx == 1)
-			lna_conf = ATH_ANT_DIV_COMB_LNA1;
-		else
-			lna_conf = ATH_ANT_DIV_COMB_LNA2;
-		div_ant_conf.main_lna_conf = lna_conf;
-		div_ant_conf.alt_lna_conf = lna_conf;
-
-		ath9k_hw_antdiv_comb_conf_set(ah, &div_ant_conf);
+		if (ah->opmode == NL80211_IFTYPE_STATION &&
+		    test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) {
+			spin_lock_irqsave(&sc->sc_pm_lock, flags);
+			sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON;
+			spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
+		}
+	work:
+		ath_restart_work(sc);
 	}
 
+	if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx != 3)
+		ath_ant_comb_update(sc);
+
 	ieee80211_wake_queues(sc->hw);
 
 	return true;
@@ -328,7 +262,7 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan,
 
 	spin_lock_bh(&sc->sc_pcu_lock);
 
-	if (!(sc->sc_flags & SC_OP_OFFCHANNEL)) {
+	if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) {
 		fastcc = false;
 		caldata = &sc->caldata;
 	}
@@ -371,7 +305,7 @@ static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
 {
 	int r;
 
-	if (sc->sc_flags & SC_OP_INVALID)
+	if (test_bit(SC_OP_INVALID, &sc->sc_flags))
 		return -EIO;
 
 	r = ath_reset_internal(sc, hchan, false);
@@ -379,258 +313,6 @@ static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
 	return r;
 }
 
-static void ath_paprd_activate(struct ath_softc *sc)
-{
-	struct ath_hw *ah = sc->sc_ah;
-	struct ath9k_hw_cal_data *caldata = ah->caldata;
-	int chain;
-
-	if (!caldata || !caldata->paprd_done)
-		return;
-
-	ath9k_ps_wakeup(sc);
-	ar9003_paprd_enable(ah, false);
-	for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
-		if (!(ah->txchainmask & BIT(chain)))
-			continue;
-
-		ar9003_paprd_populate_single_table(ah, caldata, chain);
-	}
-
-	ar9003_paprd_enable(ah, true);
-	ath9k_ps_restore(sc);
-}
-
-static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int chain)
-{
-	struct ieee80211_hw *hw = sc->hw;
-	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
-	struct ath_hw *ah = sc->sc_ah;
-	struct ath_common *common = ath9k_hw_common(ah);
-	struct ath_tx_control txctl;
-	int time_left;
-
-	memset(&txctl, 0, sizeof(txctl));
-	txctl.txq = sc->tx.txq_map[WME_AC_BE];
-
-	memset(tx_info, 0, sizeof(*tx_info));
-	tx_info->band = hw->conf.channel->band;
-	tx_info->flags |= IEEE80211_TX_CTL_NO_ACK;
-	tx_info->control.rates[0].idx = 0;
-	tx_info->control.rates[0].count = 1;
-	tx_info->control.rates[0].flags = IEEE80211_TX_RC_MCS;
-	tx_info->control.rates[1].idx = -1;
-
-	init_completion(&sc->paprd_complete);
-	txctl.paprd = BIT(chain);
-
-	if (ath_tx_start(hw, skb, &txctl) != 0) {
-		ath_dbg(common, CALIBRATE, "PAPRD TX failed\n");
-		dev_kfree_skb_any(skb);
-		return false;
-	}
-
-	time_left = wait_for_completion_timeout(&sc->paprd_complete,
-			msecs_to_jiffies(ATH_PAPRD_TIMEOUT));
-
-	if (!time_left)
-		ath_dbg(common, CALIBRATE,
-			"Timeout waiting for paprd training on TX chain %d\n",
-			chain);
-
-	return !!time_left;
-}
-
-void ath_paprd_calibrate(struct work_struct *work)
-{
-	struct ath_softc *sc = container_of(work, struct ath_softc, paprd_work);
-	struct ieee80211_hw *hw = sc->hw;
-	struct ath_hw *ah = sc->sc_ah;
-	struct ieee80211_hdr *hdr;
-	struct sk_buff *skb = NULL;
-	struct ath9k_hw_cal_data *caldata = ah->caldata;
-	struct ath_common *common = ath9k_hw_common(ah);
-	int ftype;
-	int chain_ok = 0;
-	int chain;
-	int len = 1800;
-
-	if (!caldata)
-		return;
-
-	ath9k_ps_wakeup(sc);
-
-	if (ar9003_paprd_init_table(ah) < 0)
-		goto fail_paprd;
-
-	skb = alloc_skb(len, GFP_KERNEL);
-	if (!skb)
-		goto fail_paprd;
-
-	skb_put(skb, len);
-	memset(skb->data, 0, len);
-	hdr = (struct ieee80211_hdr *)skb->data;
-	ftype = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC;
-	hdr->frame_control = cpu_to_le16(ftype);
-	hdr->duration_id = cpu_to_le16(10);
-	memcpy(hdr->addr1, hw->wiphy->perm_addr, ETH_ALEN);
-	memcpy(hdr->addr2, hw->wiphy->perm_addr, ETH_ALEN);
-	memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN);
-
-	for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
-		if (!(ah->txchainmask & BIT(chain)))
-			continue;
-
-		chain_ok = 0;
-
-		ath_dbg(common, CALIBRATE,
-			"Sending PAPRD frame for thermal measurement on chain %d\n",
-			chain);
-		if (!ath_paprd_send_frame(sc, skb, chain))
-			goto fail_paprd;
-
-		ar9003_paprd_setup_gain_table(ah, chain);
-
-		ath_dbg(common, CALIBRATE,
-			"Sending PAPRD training frame on chain %d\n", chain);
-		if (!ath_paprd_send_frame(sc, skb, chain))
-			goto fail_paprd;
-
-		if (!ar9003_paprd_is_done(ah)) {
-			ath_dbg(common, CALIBRATE,
-				"PAPRD not yet done on chain %d\n", chain);
-			break;
-		}
-
-		if (ar9003_paprd_create_curve(ah, caldata, chain)) {
-			ath_dbg(common, CALIBRATE,
-				"PAPRD create curve failed on chain %d\n",
-								   chain);
-			break;
-		}
-
-		chain_ok = 1;
-	}
-	kfree_skb(skb);
-
-	if (chain_ok) {
-		caldata->paprd_done = true;
-		ath_paprd_activate(sc);
-	}
-
-fail_paprd:
-	ath9k_ps_restore(sc);
-}
-
-/*
- *  This routine performs the periodic noise floor calibration function
- *  that is used to adjust and optimize the chip performance.  This
- *  takes environmental changes (location, temperature) into account.
- *  When the task is complete, it reschedules itself depending on the
- *  appropriate interval that was calculated.
- */
-void ath_ani_calibrate(unsigned long data)
-{
-	struct ath_softc *sc = (struct ath_softc *)data;
-	struct ath_hw *ah = sc->sc_ah;
-	struct ath_common *common = ath9k_hw_common(ah);
-	bool longcal = false;
-	bool shortcal = false;
-	bool aniflag = false;
-	unsigned int timestamp = jiffies_to_msecs(jiffies);
-	u32 cal_interval, short_cal_interval, long_cal_interval;
-	unsigned long flags;
-
-	if (ah->caldata && ah->caldata->nfcal_interference)
-		long_cal_interval = ATH_LONG_CALINTERVAL_INT;
-	else
-		long_cal_interval = ATH_LONG_CALINTERVAL;
-
-	short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ?
-		ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL;
-
-	/* Only calibrate if awake */
-	if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE)
-		goto set_timer;
-
-	ath9k_ps_wakeup(sc);
-
-	/* Long calibration runs independently of short calibration. */
-	if ((timestamp - common->ani.longcal_timer) >= long_cal_interval) {
-		longcal = true;
-		common->ani.longcal_timer = timestamp;
-	}
-
-	/* Short calibration applies only while caldone is false */
-	if (!common->ani.caldone) {
-		if ((timestamp - common->ani.shortcal_timer) >= short_cal_interval) {
-			shortcal = true;
-			common->ani.shortcal_timer = timestamp;
-			common->ani.resetcal_timer = timestamp;
-		}
-	} else {
-		if ((timestamp - common->ani.resetcal_timer) >=
-		    ATH_RESTART_CALINTERVAL) {
-			common->ani.caldone = ath9k_hw_reset_calvalid(ah);
-			if (common->ani.caldone)
-				common->ani.resetcal_timer = timestamp;
-		}
-	}
-
-	/* Verify whether we must check ANI */
-	if (sc->sc_ah->config.enable_ani
-	    && (timestamp - common->ani.checkani_timer) >=
-	    ah->config.ani_poll_interval) {
-		aniflag = true;
-		common->ani.checkani_timer = timestamp;
-	}
-
-	/* Call ANI routine if necessary */
-	if (aniflag) {
-		spin_lock_irqsave(&common->cc_lock, flags);
-		ath9k_hw_ani_monitor(ah, ah->curchan);
-		ath_update_survey_stats(sc);
-		spin_unlock_irqrestore(&common->cc_lock, flags);
-	}
-
-	/* Perform calibration if necessary */
-	if (longcal || shortcal) {
-		common->ani.caldone =
-			ath9k_hw_calibrate(ah, ah->curchan,
-						ah->rxchainmask, longcal);
-	}
-
-	ath_dbg(common, ANI,
-		"Calibration @%lu finished: %s %s %s, caldone: %s\n",
-		jiffies,
-		longcal ? "long" : "", shortcal ? "short" : "",
-		aniflag ? "ani" : "", common->ani.caldone ? "true" : "false");
-
-	ath9k_ps_restore(sc);
-
-set_timer:
-	/*
-	* Set timer interval based on previous results.
-	* The interval must be the shortest necessary to satisfy ANI,
-	* short calibration and long calibration.
-	*/
-	ath9k_debug_samp_bb_mac(sc);
-	cal_interval = ATH_LONG_CALINTERVAL;
-	if (sc->sc_ah->config.enable_ani)
-		cal_interval = min(cal_interval,
-				   (u32)ah->config.ani_poll_interval);
-	if (!common->ani.caldone)
-		cal_interval = min(cal_interval, (u32)short_cal_interval);
-
-	mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval));
-	if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_PAPRD) && ah->caldata) {
-		if (!ah->caldata->paprd_done)
-			ieee80211_queue_work(sc->hw, &sc->paprd_work);
-		else if (!ah->paprd_table_write_done)
-			ath_paprd_activate(sc);
-	}
-}
-
 static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta,
 			    struct ieee80211_vif *vif)
 {
@@ -668,13 +350,12 @@ static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta)
 		ath_tx_node_cleanup(sc, an);
 }
 
-
 void ath9k_tasklet(unsigned long data)
 {
 	struct ath_softc *sc = (struct ath_softc *)data;
 	struct ath_hw *ah = sc->sc_ah;
 	struct ath_common *common = ath9k_hw_common(ah);
-
+	unsigned long flags;
 	u32 status = sc->intrstatus;
 	u32 rxmask;
 
@@ -693,10 +374,12 @@ void ath9k_tasklet(unsigned long data)
 
 		RESET_STAT_INC(sc, type);
 #endif
+		set_bit(SC_OP_HW_RESET, &sc->sc_flags);
 		ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
 		goto out;
 	}
 
+	spin_lock_irqsave(&sc->sc_pm_lock, flags);
 	if ((status & ATH9K_INT_TSFOOR) && sc->ps_enabled) {
 		/*
 		 * TSF sync does not look correct; remain awake to sync with
@@ -705,6 +388,7 @@ void ath9k_tasklet(unsigned long data)
 		ath_dbg(common, PS, "TSFOOR - Sync with next Beacon\n");
 		sc->ps_flags |= PS_WAIT_FOR_BEACON | PS_BEACON_SYNC;
 	}
+	spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
 
 	if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
 		rxmask = (ATH9K_INT_RXHP | ATH9K_INT_RXLP | ATH9K_INT_RXEOL |
@@ -766,15 +450,17 @@ irqreturn_t ath_isr(int irq, void *dev)
 	 * touch anything. Note this can happen early
 	 * on if the IRQ is shared.
 	 */
-	if (sc->sc_flags & SC_OP_INVALID)
+	if (test_bit(SC_OP_INVALID, &sc->sc_flags))
 		return IRQ_NONE;
 
-
 	/* shared irq, not for us */
 
 	if (!ath9k_hw_intrpend(ah))
 		return IRQ_NONE;
 
+	if(test_bit(SC_OP_HW_RESET, &sc->sc_flags))
+		return IRQ_HANDLED;
+
 	/*
 	 * Figure out the reason(s) for the interrupt.  Note
 	 * that the hal returns a pseudo-ISR that may include
@@ -852,8 +538,10 @@ irqreturn_t ath_isr(int irq, void *dev)
 			/* Clear RxAbort bit so that we can
 			 * receive frames */
 			ath9k_setpower(sc, ATH9K_PM_AWAKE);
+			spin_lock(&sc->sc_pm_lock);
 			ath9k_hw_setrxabort(sc->sc_ah, 0);
 			sc->ps_flags |= PS_WAIT_FOR_BEACON;
+			spin_unlock(&sc->sc_pm_lock);
 		}
 
 chip_reset:
@@ -902,87 +590,6 @@ void ath_reset_work(struct work_struct *work)
 	ath_reset(sc, true);
 }
 
-void ath_hw_check(struct work_struct *work)
-{
-	struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work);
-	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-	unsigned long flags;
-	int busy;
-	u8 is_alive, nbeacon = 1;
-
-	ath9k_ps_wakeup(sc);
-	is_alive = ath9k_hw_check_alive(sc->sc_ah);
-
-	if (is_alive && !AR_SREV_9300(sc->sc_ah))
-		goto out;
-	else if (!is_alive && AR_SREV_9300(sc->sc_ah)) {
-		ath_dbg(common, RESET,
-			"DCU stuck is detected. Schedule chip reset\n");
-		RESET_STAT_INC(sc, RESET_TYPE_MAC_HANG);
-		goto sched_reset;
-	}
-
-	spin_lock_irqsave(&common->cc_lock, flags);
-	busy = ath_update_survey_stats(sc);
-	spin_unlock_irqrestore(&common->cc_lock, flags);
-
-	ath_dbg(common, RESET, "Possible baseband hang, busy=%d (try %d)\n",
-		busy, sc->hw_busy_count + 1);
-	if (busy >= 99) {
-		if (++sc->hw_busy_count >= 3) {
-			RESET_STAT_INC(sc, RESET_TYPE_BB_HANG);
-			goto sched_reset;
-		}
-	} else if (busy >= 0) {
-		sc->hw_busy_count = 0;
-		nbeacon = 3;
-	}
-
-	ath_start_rx_poll(sc, nbeacon);
-	goto out;
-
-sched_reset:
-	ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
-out:
-	ath9k_ps_restore(sc);
-}
-
-static void ath_hw_pll_rx_hang_check(struct ath_softc *sc, u32 pll_sqsum)
-{
-	static int count;
-	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-
-	if (pll_sqsum >= 0x40000) {
-		count++;
-		if (count == 3) {
-			/* Rx is hung for more than 500ms. Reset it */
-			ath_dbg(common, RESET, "Possible RX hang, resetting\n");
-			RESET_STAT_INC(sc, RESET_TYPE_PLL_HANG);
-			ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
-			count = 0;
-		}
-	} else
-		count = 0;
-}
-
-void ath_hw_pll_work(struct work_struct *work)
-{
-	struct ath_softc *sc = container_of(work, struct ath_softc,
-					    hw_pll_work.work);
-	u32 pll_sqsum;
-
-	if (AR_SREV_9485(sc->sc_ah)) {
-
-		ath9k_ps_wakeup(sc);
-		pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah);
-		ath9k_ps_restore(sc);
-
-		ath_hw_pll_rx_hang_check(sc, pll_sqsum);
-
-		ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/5);
-	}
-}
-
 /**********************/
 /* mac80211 callbacks */
 /**********************/
@@ -1045,10 +652,9 @@ static int ath9k_start(struct ieee80211_hw *hw)
 	if (ah->caps.hw_caps & ATH9K_HW_CAP_HT)
 		ah->imask |= ATH9K_INT_CST;
 
-	if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI)
-		ah->imask |= ATH9K_INT_MCI;
+	ath_mci_enable(sc);
 
-	sc->sc_flags &= ~SC_OP_INVALID;
+	clear_bit(SC_OP_INVALID, &sc->sc_flags);
 	sc->sc_ah->is_monitoring = false;
 
 	if (!ath_complete_reset(sc, false)) {
@@ -1090,6 +696,7 @@ static void ath9k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
 	struct ath_tx_control txctl;
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+	unsigned long flags;
 
 	if (sc->ps_enabled) {
 		/*
@@ -1112,6 +719,7 @@ static void ath9k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
 		 * completed and if needed, also for RX of buffered frames.
 		 */
 		ath9k_ps_wakeup(sc);
+		spin_lock_irqsave(&sc->sc_pm_lock, flags);
 		if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP))
 			ath9k_hw_setrxabort(sc->sc_ah, 0);
 		if (ieee80211_is_pspoll(hdr->frame_control)) {
@@ -1127,6 +735,7 @@ static void ath9k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
 		 * the ps_flags bit is cleared. We are just dropping
 		 * the ps_usecount here.
 		 */
+		spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
 		ath9k_ps_restore(sc);
 	}
 
@@ -1167,7 +776,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
 	ath_cancel_work(sc);
 	del_timer_sync(&sc->rx_poll_timer);
 
-	if (sc->sc_flags & SC_OP_INVALID) {
+	if (test_bit(SC_OP_INVALID, &sc->sc_flags)) {
 		ath_dbg(common, ANY, "Device not present\n");
 		mutex_unlock(&sc->mutex);
 		return;
@@ -1224,7 +833,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
 
 	ath9k_ps_restore(sc);
 
-	sc->sc_flags |= SC_OP_INVALID;
+	set_bit(SC_OP_INVALID, &sc->sc_flags);
 	sc->ps_idle = prev_idle;
 
 	mutex_unlock(&sc->mutex);
@@ -1328,11 +937,11 @@ static void ath9k_calculate_summary_state(struct ieee80211_hw *hw,
 	/* Set op-mode & TSF */
 	if (iter_data.naps > 0) {
 		ath9k_hw_set_tsfadjust(ah, 1);
-		sc->sc_flags |= SC_OP_TSF_RESET;
+		set_bit(SC_OP_TSF_RESET, &sc->sc_flags);
 		ah->opmode = NL80211_IFTYPE_AP;
 	} else {
 		ath9k_hw_set_tsfadjust(ah, 0);
-		sc->sc_flags &= ~SC_OP_TSF_RESET;
+		clear_bit(SC_OP_TSF_RESET, &sc->sc_flags);
 
 		if (iter_data.nmeshes)
 			ah->opmode = NL80211_IFTYPE_MESH_POINT;
@@ -1363,12 +972,12 @@ static void ath9k_calculate_summary_state(struct ieee80211_hw *hw,
 		sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
 
 		if (!common->disable_ani) {
-			sc->sc_flags |= SC_OP_ANI_RUN;
+			set_bit(SC_OP_ANI_RUN, &sc->sc_flags);
 			ath_start_ani(common);
 		}
 
 	} else {
-		sc->sc_flags &= ~SC_OP_ANI_RUN;
+		clear_bit(SC_OP_ANI_RUN, &sc->sc_flags);
 		del_timer_sync(&common->ani.timer);
 	}
 }
@@ -1389,25 +998,6 @@ static void ath9k_do_vif_add_setup(struct ieee80211_hw *hw,
 	}
 }
 
-void ath_start_rx_poll(struct ath_softc *sc, u8 nbeacon)
-{
-	if (!AR_SREV_9300(sc->sc_ah))
-		return;
-
-	if (!(sc->sc_flags & SC_OP_PRIM_STA_VIF))
-		return;
-
-	mod_timer(&sc->rx_poll_timer, jiffies + msecs_to_jiffies
-			(nbeacon * sc->cur_beacon_conf.beacon_interval));
-}
-
-void ath_rx_poll(unsigned long data)
-{
-	struct ath_softc *sc = (struct ath_softc *)data;
-
-	ieee80211_queue_work(sc->hw, &sc->hw_check_work);
-}
-
 static int ath9k_add_interface(struct ieee80211_hw *hw,
 			       struct ieee80211_vif *vif)
 {
@@ -1627,11 +1217,6 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
 		if (ah->curchan)
 			old_pos = ah->curchan - &ah->channels[0];
 
-		if (hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)
-			sc->sc_flags |= SC_OP_OFFCHANNEL;
-		else
-			sc->sc_flags &= ~SC_OP_OFFCHANNEL;
-
 		ath_dbg(common, CONFIG, "Set channel: %d MHz type: %d\n",
 			curchan->center_freq, conf->channel_type);
 
@@ -1911,16 +1496,16 @@ static void ath9k_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
 	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
 	struct ath_vif *avp = (void *)vif->drv_priv;
-
+	unsigned long flags;
 	/*
 	 * Skip iteration if primary station vif's bss info
 	 * was not changed
 	 */
-	if (sc->sc_flags & SC_OP_PRIM_STA_VIF)
+	if (test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags))
 		return;
 
 	if (bss_conf->assoc) {
-		sc->sc_flags |= SC_OP_PRIM_STA_VIF;
+		set_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags);
 		avp->primary_sta_vif = true;
 		memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
 		common->curaid = bss_conf->aid;
@@ -1933,7 +1518,10 @@ static void ath9k_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
 		 * on the receipt of the first Beacon frame (i.e.,
 		 * after time sync with the AP).
 		 */
+		spin_lock_irqsave(&sc->sc_pm_lock, flags);
 		sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON;
+		spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
+
 		/* Reset rssi stats */
 		sc->last_rssi = ATH_RSSI_DUMMY_MARKER;
 		sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
@@ -1941,7 +1529,7 @@ static void ath9k_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
 		ath_start_rx_poll(sc, 3);
 
 		if (!common->disable_ani) {
-			sc->sc_flags |= SC_OP_ANI_RUN;
+			set_bit(SC_OP_ANI_RUN, &sc->sc_flags);
 			ath_start_ani(common);
 		}
 
@@ -1961,7 +1549,8 @@ static void ath9k_config_bss(struct ath_softc *sc, struct ieee80211_vif *vif)
 	if (avp->primary_sta_vif && !bss_conf->assoc) {
 		ath_dbg(common, CONFIG, "Bss Info DISASSOC %d, bssid %pM\n",
 			common->curaid, common->curbssid);
-		sc->sc_flags &= ~(SC_OP_PRIM_STA_VIF | SC_OP_BEACONS);
+		clear_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags);
+		clear_bit(SC_OP_BEACONS, &sc->sc_flags);
 		avp->primary_sta_vif = false;
 		memset(common->curbssid, 0, ETH_ALEN);
 		common->curaid = 0;
@@ -1974,10 +1563,9 @@ static void ath9k_config_bss(struct ath_softc *sc, struct ieee80211_vif *vif)
 	 * None of station vifs are associated.
 	 * Clear bssid & aid
 	 */
-	if (!(sc->sc_flags & SC_OP_PRIM_STA_VIF)) {
+	if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) {
 		ath9k_hw_write_associd(sc->sc_ah);
-		/* Stop ANI */
-		sc->sc_flags &= ~SC_OP_ANI_RUN;
+		clear_bit(SC_OP_ANI_RUN, &sc->sc_flags);
 		del_timer_sync(&common->ani.timer);
 		del_timer_sync(&sc->rx_poll_timer);
 		memset(&sc->caldata, 0, sizeof(sc->caldata));
@@ -2015,12 +1603,12 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
 			sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
 
 			if (!common->disable_ani) {
-				sc->sc_flags |= SC_OP_ANI_RUN;
+				set_bit(SC_OP_ANI_RUN, &sc->sc_flags);
 				ath_start_ani(common);
 			}
 
 		} else {
-			sc->sc_flags &= ~SC_OP_ANI_RUN;
+			clear_bit(SC_OP_ANI_RUN, &sc->sc_flags);
 			del_timer_sync(&common->ani.timer);
 			del_timer_sync(&sc->rx_poll_timer);
 		}
@@ -2032,7 +1620,7 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
 	 */
 	if ((changed & BSS_CHANGED_BEACON_INT) &&
 	    (vif->type == NL80211_IFTYPE_AP))
-		sc->sc_flags |= SC_OP_TSF_RESET;
+		set_bit(SC_OP_TSF_RESET, &sc->sc_flags);
 
 	/* Configure beaconing (AP, IBSS, MESH) */
 	if (ath9k_uses_beacons(vif->type) &&
@@ -2224,7 +1812,7 @@ static void ath9k_flush(struct ieee80211_hw *hw, bool drop)
 		return;
 	}
 
-	if (sc->sc_flags & SC_OP_INVALID) {
+	if (test_bit(SC_OP_INVALID, &sc->sc_flags)) {
 		ath_dbg(common, ANY, "Device not present\n");
 		mutex_unlock(&sc->mutex);
 		return;

+ 46 - 19
drivers/net/wireless/ath/ath9k/mci.c

@@ -116,42 +116,58 @@ static void ath_mci_update_scheme(struct ath_softc *sc)
 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
 	struct ath_btcoex *btcoex = &sc->btcoex;
 	struct ath_mci_profile *mci = &btcoex->mci;
+	struct ath9k_hw_mci *mci_hw = &sc->sc_ah->btcoex_hw.mci;
 	struct ath_mci_profile_info *info;
 	u32 num_profile = NUM_PROF(mci);
 
+	if (mci_hw->config & ATH_MCI_CONFIG_DISABLE_TUNING)
+		goto skip_tuning;
+
 	if (num_profile == 1) {
 		info = list_first_entry(&mci->info,
 					struct ath_mci_profile_info,
 					list);
-		if (mci->num_sco && info->T == 12) {
-			mci->aggr_limit = 8;
+		if (mci->num_sco) {
+			if (info->T == 12)
+				mci->aggr_limit = 8;
+			else if (info->T == 6) {
+				mci->aggr_limit = 6;
+				btcoex->duty_cycle = 30;
+			}
 			ath_dbg(common, MCI,
-				"Single SCO, aggregation limit 2 ms\n");
-		} else if ((info->type == MCI_GPM_COEX_PROFILE_BNEP) &&
-			   !info->master) {
-			btcoex->btcoex_period = 60;
+				"Single SCO, aggregation limit %d 1/4 ms\n",
+				mci->aggr_limit);
+		} else if (mci->num_pan || mci->num_other_acl) {
+			/*
+			 * For single PAN/FTP profile, allocate 35% for BT
+			 * to improve WLAN throughput.
+			 */
+			btcoex->duty_cycle = 35;
+			btcoex->btcoex_period = 53;
 			ath_dbg(common, MCI,
-				"Single slave PAN/FTP, bt period 60 ms\n");
-		} else if ((info->type == MCI_GPM_COEX_PROFILE_HID) &&
-			 (info->T > 0 && info->T < 50) &&
-			 (info->A > 1 || info->W > 1)) {
+				"Single PAN/FTP bt period %d ms dutycycle %d\n",
+				btcoex->duty_cycle, btcoex->btcoex_period);
+		} else if (mci->num_hid) {
 			btcoex->duty_cycle = 30;
-			mci->aggr_limit = 8;
+			mci->aggr_limit = 6;
 			ath_dbg(common, MCI,
 				"Multiple attempt/timeout single HID "
-				"aggregation limit 2 ms dutycycle 30%%\n");
+				"aggregation limit 1.5 ms dutycycle 30%%\n");
 		}
-	} else if ((num_profile == 2) && (mci->num_hid == 2)) {
-		btcoex->duty_cycle = 30;
-		mci->aggr_limit = 8;
-		ath_dbg(common, MCI,
-			"Two HIDs aggregation limit 2 ms dutycycle 30%%\n");
-	} else if (num_profile > 3) {
+	} else if (num_profile == 2) {
+		if (mci->num_hid == 2)
+			btcoex->duty_cycle = 30;
 		mci->aggr_limit = 6;
 		ath_dbg(common, MCI,
-			"Three or more profiles aggregation limit 1.5 ms\n");
+			"Two BT profiles aggr limit 1.5 ms dutycycle %d%%\n",
+			btcoex->duty_cycle);
+	} else if (num_profile >= 3) {
+		mci->aggr_limit = 4;
+		ath_dbg(common, MCI,
+			"Three or more profiles aggregation limit 1 ms\n");
 	}
 
+skip_tuning:
 	if (IS_CHAN_2GHZ(sc->sc_ah->curchan)) {
 		if (IS_CHAN_HT(sc->sc_ah->curchan))
 			ath_mci_adjust_aggr_limit(btcoex);
@@ -538,3 +554,14 @@ void ath_mci_intr(struct ath_softc *sc)
 		mci_int &= ~(AR_MCI_INTERRUPT_RX_INVALID_HDR |
 			     AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT);
 }
+
+void ath_mci_enable(struct ath_softc *sc)
+{
+	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+
+	if (!common->btcoex_enabled)
+		return;
+
+	if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_MCI)
+		sc->sc_ah->imask |= ATH9K_INT_MCI;
+}

+ 10 - 1
drivers/net/wireless/ath/ath9k/mci.h

@@ -130,4 +130,13 @@ void ath_mci_flush_profile(struct ath_mci_profile *mci);
 int ath_mci_setup(struct ath_softc *sc);
 void ath_mci_cleanup(struct ath_softc *sc);
 void ath_mci_intr(struct ath_softc *sc);
-#endif
+
+#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
+void ath_mci_enable(struct ath_softc *sc);
+#else
+static inline void ath_mci_enable(struct ath_softc *sc)
+{
+}
+#endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */
+
+#endif /* MCI_H*/

+ 6 - 1
drivers/net/wireless/ath/ath9k/pci.c

@@ -115,6 +115,9 @@ static void ath_pci_aspm_init(struct ath_common *common)
 	int pos;
 	u8 aspm;
 
+	if (!ah->is_pciexpress)
+		return;
+
 	pos = pci_pcie_cap(pdev);
 	if (!pos)
 		return;
@@ -138,6 +141,7 @@ static void ath_pci_aspm_init(struct ath_common *common)
 		aspm &= ~(PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1);
 		pci_write_config_byte(parent, pos + PCI_EXP_LNKCTL, aspm);
 
+		ath_info(common, "Disabling ASPM since BTCOEX is enabled\n");
 		return;
 	}
 
@@ -147,6 +151,7 @@ static void ath_pci_aspm_init(struct ath_common *common)
 		ah->aspm_enabled = true;
 		/* Initialize PCIe PM and SERDES registers. */
 		ath9k_hw_configpcipowersave(ah, false);
+		ath_info(common, "ASPM enabled: 0x%x\n", aspm);
 	}
 }
 
@@ -246,7 +251,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	sc->mem = mem;
 
 	/* Will be cleared in ath9k_start() */
-	sc->sc_flags |= SC_OP_INVALID;
+	set_bit(SC_OP_INVALID, &sc->sc_flags);
 
 	ret = request_irq(pdev->irq, ath_isr, IRQF_SHARED, "ath9k", sc);
 	if (ret) {

+ 14 - 754
drivers/net/wireless/ath/ath9k/recv.c

@@ -20,43 +20,6 @@
 
 #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 ath_ant_div_comb_alt_check(u8 div_group, int alt_ratio,
-					int curr_main_set, int curr_alt_set,
-					int alt_rssi_avg, int main_rssi_avg)
-{
-	bool result = false;
-	switch (div_group) {
-	case 0:
-		if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
-			result = true;
-		break;
-	case 1:
-	case 2:
-		if ((((curr_main_set == ATH_ANT_DIV_COMB_LNA2) &&
-			(curr_alt_set == ATH_ANT_DIV_COMB_LNA1) &&
-				(alt_rssi_avg >= (main_rssi_avg - 5))) ||
-			((curr_main_set == ATH_ANT_DIV_COMB_LNA1) &&
-			(curr_alt_set == ATH_ANT_DIV_COMB_LNA2) &&
-				(alt_rssi_avg >= (main_rssi_avg - 2)))) &&
-							(alt_rssi_avg >= 4))
-			result = true;
-		else
-			result = false;
-		break;
-	}
-
-	return result;
-}
-
 static inline bool ath9k_check_auto_sleep(struct ath_softc *sc)
 {
 	return sc->ps_enabled &&
@@ -303,7 +266,7 @@ static void ath_edma_start_recv(struct ath_softc *sc)
 
 	ath_opmode_init(sc);
 
-	ath9k_hw_startpcureceive(sc->sc_ah, (sc->sc_flags & SC_OP_OFFCHANNEL));
+	ath9k_hw_startpcureceive(sc->sc_ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL));
 
 	spin_unlock_bh(&sc->rx.rxbuflock);
 }
@@ -322,8 +285,8 @@ int ath_rx_init(struct ath_softc *sc, int nbufs)
 	int error = 0;
 
 	spin_lock_init(&sc->sc_pcu_lock);
-	sc->sc_flags &= ~SC_OP_RXFLUSH;
 	spin_lock_init(&sc->rx.rxbuflock);
+	clear_bit(SC_OP_RXFLUSH, &sc->sc_flags);
 
 	common->rx_bufsize = IEEE80211_MAX_MPDU_LEN / 2 +
 			     sc->sc_ah->caps.rx_status_len;
@@ -500,7 +463,7 @@ int ath_startrecv(struct ath_softc *sc)
 
 start_recv:
 	ath_opmode_init(sc);
-	ath9k_hw_startpcureceive(ah, (sc->sc_flags & SC_OP_OFFCHANNEL));
+	ath9k_hw_startpcureceive(ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL));
 
 	spin_unlock_bh(&sc->rx.rxbuflock);
 
@@ -535,11 +498,11 @@ bool ath_stoprecv(struct ath_softc *sc)
 
 void ath_flushrecv(struct ath_softc *sc)
 {
-	sc->sc_flags |= SC_OP_RXFLUSH;
+	set_bit(SC_OP_RXFLUSH, &sc->sc_flags);
 	if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
 		ath_rx_tasklet(sc, 1, true);
 	ath_rx_tasklet(sc, 1, false);
-	sc->sc_flags &= ~SC_OP_RXFLUSH;
+	clear_bit(SC_OP_RXFLUSH, &sc->sc_flags);
 }
 
 static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb)
@@ -624,13 +587,13 @@ static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb, bool mybeacon)
 
 	/* Process Beacon and CAB receive in PS state */
 	if (((sc->ps_flags & PS_WAIT_FOR_BEACON) || ath9k_check_auto_sleep(sc))
-	    && mybeacon)
+	    && mybeacon) {
 		ath_rx_ps_beacon(sc, skb);
-	else if ((sc->ps_flags & PS_WAIT_FOR_CAB) &&
-		 (ieee80211_is_data(hdr->frame_control) ||
-		  ieee80211_is_action(hdr->frame_control)) &&
-		 is_multicast_ether_addr(hdr->addr1) &&
-		 !ieee80211_has_moredata(hdr->frame_control)) {
+	} else if ((sc->ps_flags & PS_WAIT_FOR_CAB) &&
+		   (ieee80211_is_data(hdr->frame_control) ||
+		    ieee80211_is_action(hdr->frame_control)) &&
+		   is_multicast_ether_addr(hdr->addr1) &&
+		   !ieee80211_has_moredata(hdr->frame_control)) {
 		/*
 		 * No more broadcast/multicast frames to be received at this
 		 * point.
@@ -1067,709 +1030,6 @@ static void ath9k_rx_skb_postprocess(struct ath_common *common,
 		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,
-		struct ath_ant_comb *antcomb, int alt_ratio)
-{
-	if (ant_conf->div_group == 0) {
-		/* 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;
-		}
-	} else if (ant_conf->div_group == 1) {
-		/* 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 = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
-			break;
-		case 0x02: /* A-B LNA1 */
-			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
-			break;
-		case 0x03: /* A-B A+B */
-			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
-			break;
-		case 0x10: /* LNA2 A-B */
-			if (!(antcomb->scan) &&
-			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
-				ant_conf->fast_div_bias = 0x3f;
-			else
-				ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
-			break;
-		case 0x12: /* LNA2 LNA1 */
-			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
-			break;
-		case 0x13: /* LNA2 A+B */
-			if (!(antcomb->scan) &&
-			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
-				ant_conf->fast_div_bias = 0x3f;
-			else
-				ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
-			break;
-		case 0x20: /* LNA1 A-B */
-			if (!(antcomb->scan) &&
-			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
-				ant_conf->fast_div_bias = 0x3f;
-			else
-				ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
-			break;
-		case 0x21: /* LNA1 LNA2 */
-			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
-			break;
-		case 0x23: /* LNA1 A+B */
-			if (!(antcomb->scan) &&
-			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
-				ant_conf->fast_div_bias = 0x3f;
-			else
-				ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
-			break;
-		case 0x30: /* A+B A-B */
-			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
-			break;
-		case 0x31: /* A+B LNA2 */
-			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
-			break;
-		case 0x32: /* A+B LNA1 */
-			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
-			break;
-		default:
-			break;
-		}
-	} else if (ant_conf->div_group == 2) {
-		/* 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 = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
-			break;
-		case 0x02: /* A-B LNA1 */
-			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
-			break;
-		case 0x03: /* A-B A+B */
-			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
-			break;
-		case 0x10: /* LNA2 A-B */
-			if (!(antcomb->scan) &&
-				(alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
-				ant_conf->fast_div_bias = 0x1;
-			else
-				ant_conf->fast_div_bias = 0x2;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
-			break;
-		case 0x12: /* LNA2 LNA1 */
-			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
-			break;
-		case 0x13: /* LNA2 A+B */
-			if (!(antcomb->scan) &&
-				(alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
-				ant_conf->fast_div_bias = 0x1;
-			else
-				ant_conf->fast_div_bias = 0x2;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
-			break;
-		case 0x20: /* LNA1 A-B */
-			if (!(antcomb->scan) &&
-				(alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
-				ant_conf->fast_div_bias = 0x1;
-			else
-				ant_conf->fast_div_bias = 0x2;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
-			break;
-		case 0x21: /* LNA1 LNA2 */
-			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
-			break;
-		case 0x23: /* LNA1 A+B */
-			if (!(antcomb->scan) &&
-				(alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
-				ant_conf->fast_div_bias = 0x1;
-			else
-				ant_conf->fast_div_bias = 0x2;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
-			break;
-		case 0x30: /* A+B A-B */
-			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
-			break;
-		case 0x31: /* A+B LNA2 */
-			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
-			break;
-		case 0x32: /* A+B LNA1 */
-			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
-			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;
-	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 both main_rssi and  alt_rssi is positive */
-	if (main_rssi > 0 && 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;
-
-	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 (ath_ant_div_comb_alt_check(div_ant_conf.div_group,
-					alt_ratio, curr_main_set, curr_alt_set,
-					alt_rssi_avg, main_rssi_avg)) {
-			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 +
-						div_ant_conf.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, antcomb, alt_ratio);
-	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;
@@ -1803,7 +1063,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
 
 	do {
 		/* If handling rx interrupt and flush is in progress => exit */
-		if ((sc->sc_flags & SC_OP_RXFLUSH) && (flush == 0))
+		if (test_bit(SC_OP_RXFLUSH, &sc->sc_flags) && (flush == 0))
 			break;
 
 		memset(&rs, 0, sizeof(rs));
@@ -1841,13 +1101,14 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
 		else
 			rs.is_mybeacon = false;
 
+		sc->rx.num_pkts++;
 		ath_debug_stat_rx(sc, &rs);
 
 		/*
 		 * If we're asked to flush receive queue, directly
 		 * chain it back at the queue without processing it.
 		 */
-		if (sc->sc_flags & SC_OP_RXFLUSH) {
+		if (test_bit(SC_OP_RXFLUSH, &sc->sc_flags)) {
 			RX_STAT_INC(rx_drop_rxflush);
 			goto requeue_drop_frag;
 		}
@@ -1968,7 +1229,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
 			skb_trim(skb, skb->len - 8);
 
 		spin_lock_irqsave(&sc->sc_pm_lock, flags);
-
 		if ((sc->ps_flags & (PS_WAIT_FOR_BEACON |
 				     PS_WAIT_FOR_CAB |
 				     PS_WAIT_FOR_PSPOLL_DATA)) ||

+ 2 - 0
drivers/net/wireless/ath/ath9k/reg.h

@@ -2211,5 +2211,7 @@ enum {
 #define AR_BTCOEX_CTRL3_CONT_INFO_TIMEOUT	0x00000fff
 #define AR_BTCOEX_CTRL3_CONT_INFO_TIMEOUT_S	0
 
+#define AR_GLB_SWREG_DISCONT_MODE         0x2002c
+#define AR_GLB_SWREG_DISCONT_EN_BT_WLAN   0x3
 
 #endif

+ 7 - 44
drivers/net/wireless/ath/ath9k/xmit.c

@@ -105,19 +105,19 @@ static int ath_max_4ms_framelen[4][32] = {
 /* Aggregation logic */
 /*********************/
 
-static void ath_txq_lock(struct ath_softc *sc, struct ath_txq *txq)
+void ath_txq_lock(struct ath_softc *sc, struct ath_txq *txq)
 	__acquires(&txq->axq_lock)
 {
 	spin_lock_bh(&txq->axq_lock);
 }
 
-static void ath_txq_unlock(struct ath_softc *sc, struct ath_txq *txq)
+void ath_txq_unlock(struct ath_softc *sc, struct ath_txq *txq)
 	__releases(&txq->axq_lock)
 {
 	spin_unlock_bh(&txq->axq_lock);
 }
 
-static void ath_txq_unlock_complete(struct ath_softc *sc, struct ath_txq *txq)
+void ath_txq_unlock_complete(struct ath_softc *sc, struct ath_txq *txq)
 	__releases(&txq->axq_lock)
 {
 	struct sk_buff_head q;
@@ -1536,7 +1536,7 @@ bool ath_drain_all_txq(struct ath_softc *sc, bool retry_tx)
 	int i;
 	u32 npend = 0;
 
-	if (sc->sc_flags & SC_OP_INVALID)
+	if (test_bit(SC_OP_INVALID, &sc->sc_flags))
 		return true;
 
 	ath9k_hw_abort_tx_dma(ah);
@@ -1994,6 +1994,7 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
 	struct ieee80211_hdr * hdr = (struct ieee80211_hdr *)skb->data;
 	int q, padpos, padsize;
+	unsigned long flags;
 
 	ath_dbg(common, XMIT, "TX complete: skb: %p\n", skb);
 
@@ -2012,6 +2013,7 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
 		skb_pull(skb, padsize);
 	}
 
+	spin_lock_irqsave(&sc->sc_pm_lock, flags);
 	if ((sc->ps_flags & PS_WAIT_FOR_TX_ACK) && !txq->axq_depth) {
 		sc->ps_flags &= ~PS_WAIT_FOR_TX_ACK;
 		ath_dbg(common, PS,
@@ -2021,6 +2023,7 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
 					PS_WAIT_FOR_PSPOLL_DATA |
 					PS_WAIT_FOR_TX_ACK));
 	}
+	spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
 
 	q = skb_get_queue_mapping(skb);
 	if (txq == sc->tx.txq_map[q]) {
@@ -2231,46 +2234,6 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
 	ath_txq_unlock_complete(sc, txq);
 }
 
-static void ath_tx_complete_poll_work(struct work_struct *work)
-{
-	struct ath_softc *sc = container_of(work, struct ath_softc,
-			tx_complete_work.work);
-	struct ath_txq *txq;
-	int i;
-	bool needreset = false;
-#ifdef CONFIG_ATH9K_DEBUGFS
-	sc->tx_complete_poll_work_seen++;
-#endif
-
-	for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
-		if (ATH_TXQ_SETUP(sc, i)) {
-			txq = &sc->tx.txq[i];
-			ath_txq_lock(sc, txq);
-			if (txq->axq_depth) {
-				if (txq->axq_tx_inprogress) {
-					needreset = true;
-					ath_txq_unlock(sc, txq);
-					break;
-				} else {
-					txq->axq_tx_inprogress = true;
-				}
-			}
-			ath_txq_unlock_complete(sc, txq);
-		}
-
-	if (needreset) {
-		ath_dbg(ath9k_hw_common(sc->sc_ah), RESET,
-			"tx hung, resetting the chip\n");
-		RESET_STAT_INC(sc, RESET_TYPE_TX_HANG);
-		ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
-	}
-
-	ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work,
-			msecs_to_jiffies(ATH_TX_COMPLETE_POLL_INT));
-}
-
-
-
 void ath_tx_tasklet(struct ath_softc *sc)
 {
 	struct ath_hw *ah = sc->sc_ah;

+ 4 - 0
drivers/net/wireless/b43/b43.h

@@ -877,6 +877,10 @@ struct b43_wl {
 	 * from the mac80211 subsystem. */
 	u16 mac80211_initially_registered_queues;
 
+	/* Set this if we call ieee80211_register_hw() and check if we call
+	 * ieee80211_unregister_hw(). */
+	bool hw_registred;
+
 	/* We can only have one operating interface (802.11 core)
 	 * at a time. General information about this interface follows.
 	 */

+ 12 - 7
drivers/net/wireless/b43/main.c

@@ -2437,6 +2437,7 @@ start_ieee80211:
 	err = ieee80211_register_hw(wl->hw);
 	if (err)
 		goto err_one_core_detach;
+	wl->hw_registred = true;
 	b43_leds_register(wl->current_dev);
 	goto out;
 
@@ -5299,6 +5300,7 @@ static struct b43_wl *b43_wireless_init(struct b43_bus_dev *dev)
 
 	hw->queues = modparam_qos ? B43_QOS_QUEUE_NUM : 1;
 	wl->mac80211_initially_registered_queues = hw->queues;
+	wl->hw_registred = false;
 	hw->max_rates = 2;
 	SET_IEEE80211_DEV(hw, dev->dev);
 	if (is_valid_ether_addr(sprom->et1mac))
@@ -5370,12 +5372,15 @@ static void b43_bcma_remove(struct bcma_device *core)
 	 * as the ieee80211 unreg will destroy the workqueue. */
 	cancel_work_sync(&wldev->restart_work);
 
-	/* Restore the queues count before unregistering, because firmware detect
-	 * might have modified it. Restoring is important, so the networking
-	 * stack can properly free resources. */
-	wl->hw->queues = wl->mac80211_initially_registered_queues;
-	b43_leds_stop(wldev);
-	ieee80211_unregister_hw(wl->hw);
+	B43_WARN_ON(!wl);
+	if (wl->current_dev == wldev && wl->hw_registred) {
+		/* Restore the queues count before unregistering, because firmware detect
+		 * might have modified it. Restoring is important, so the networking
+		 * stack can properly free resources. */
+		wl->hw->queues = wl->mac80211_initially_registered_queues;
+		b43_leds_stop(wldev);
+		ieee80211_unregister_hw(wl->hw);
+	}
 
 	b43_one_core_detach(wldev->dev);
 
@@ -5446,7 +5451,7 @@ static void b43_ssb_remove(struct ssb_device *sdev)
 	cancel_work_sync(&wldev->restart_work);
 
 	B43_WARN_ON(!wl);
-	if (wl->current_dev == wldev) {
+	if (wl->current_dev == wldev && wl->hw_registred) {
 		/* Restore the queues count before unregistering, because firmware detect
 		 * might have modified it. Restoring is important, so the networking
 		 * stack can properly free resources. */

+ 1 - 1
drivers/net/wireless/b43legacy/main.c

@@ -1508,7 +1508,7 @@ static void b43legacy_release_firmware(struct b43legacy_wldev *dev)
 
 static void b43legacy_print_fw_helptext(struct b43legacy_wl *wl)
 {
-	b43legacyerr(wl, "You must go to http://linuxwireless.org/en/users/"
+	b43legacyerr(wl, "You must go to http://wireless.kernel.org/en/users/"
 		     "Drivers/b43#devicefirmware "
 		     "and download the correct firmware (version 3).\n");
 }

+ 2 - 2
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c

@@ -89,9 +89,9 @@ int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev)
 	data |= 1 << SDIO_FUNC_1 | 1 << SDIO_FUNC_2 | 1;
 	brcmf_sdio_regwb(sdiodev, SDIO_CCCR_IENx, data, &ret);
 
-	/* redirect, configure ane enable io for interrupt signal */
+	/* redirect, configure and enable io for interrupt signal */
 	data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE;
-	if (sdiodev->irq_flags | IRQF_TRIGGER_HIGH)
+	if (sdiodev->irq_flags & IRQF_TRIGGER_HIGH)
 		data |= SDIO_SEPINT_ACT_HI;
 	brcmf_sdio_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, data, &ret);
 

+ 1 - 2
drivers/net/wireless/brcm80211/brcmsmac/aiutils.c

@@ -631,9 +631,8 @@ uint ai_cc_reg(struct si_pub *sih, uint regoff, u32 mask, u32 val)
 	cc = sii->icbus->drv_cc.core;
 
 	/* mask and set */
-	if (mask || val) {
+	if (mask || val)
 		bcma_maskset32(cc, regoff, ~mask, val);
-	}
 
 	/* readback */
 	w = bcma_read32(cc, regoff);

+ 1 - 1
drivers/net/wireless/brcm80211/brcmsmac/aiutils.h

@@ -193,7 +193,7 @@ extern void ai_detach(struct si_pub *sih);
 extern uint ai_cc_reg(struct si_pub *sih, uint regoff, u32 mask, u32 val);
 extern void ai_clkctl_init(struct si_pub *sih);
 extern u16 ai_clkctl_fast_pwrup_delay(struct si_pub *sih);
-extern bool ai_clkctl_cc(struct si_pub *sih, uint mode);
+extern bool ai_clkctl_cc(struct si_pub *sih, enum bcma_clkmode mode);
 extern bool ai_deviceremoved(struct si_pub *sih);
 
 extern void ai_pci_down(struct si_pub *sih);

+ 5 - 15
drivers/net/wireless/ipw2x00/ipw2100.c

@@ -1903,14 +1903,6 @@ static void ipw2100_down(struct ipw2100_priv *priv)
 	netif_stop_queue(priv->net_dev);
 }
 
-/* Called by register_netdev() */
-static int ipw2100_net_init(struct net_device *dev)
-{
-	struct ipw2100_priv *priv = libipw_priv(dev);
-
-	return ipw2100_up(priv, 1);
-}
-
 static int ipw2100_wdev_init(struct net_device *dev)
 {
 	struct ipw2100_priv *priv = libipw_priv(dev);
@@ -6087,7 +6079,6 @@ static const struct net_device_ops ipw2100_netdev_ops = {
 	.ndo_stop		= ipw2100_close,
 	.ndo_start_xmit		= libipw_xmit,
 	.ndo_change_mtu		= libipw_change_mtu,
-	.ndo_init		= ipw2100_net_init,
 	.ndo_tx_timeout		= ipw2100_tx_timeout,
 	.ndo_set_mac_address	= ipw2100_set_address,
 	.ndo_validate_addr	= eth_validate_addr,
@@ -6329,6 +6320,10 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev,
 	printk(KERN_INFO DRV_NAME
 	       ": Detected Intel PRO/Wireless 2100 Network Connection\n");
 
+	err = ipw2100_up(priv, 1);
+	if (err)
+		goto fail;
+
 	err = ipw2100_wdev_init(dev);
 	if (err)
 		goto fail;
@@ -6338,12 +6333,7 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev,
 	 * network device we would call ipw2100_up.  This introduced a race
 	 * condition with newer hotplug configurations (network was coming
 	 * up and making calls before the device was initialized).
-	 *
-	 * If we called ipw2100_up before we registered the device, then the
-	 * device name wasn't registered.  So, we instead use the net_dev->init
-	 * member to call a function that then just turns and calls ipw2100_up.
-	 * net_dev->init is called after name allocation but before the
-	 * notifier chain is called */
+	 */
 	err = register_netdev(dev);
 	if (err) {
 		printk(KERN_WARNING DRV_NAME

+ 12 - 1
drivers/net/wireless/iwlegacy/4965-mac.c

@@ -5724,7 +5724,8 @@ il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length)
 	    BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC);
 
 	hw->wiphy->flags |=
-	    WIPHY_FLAG_CUSTOM_REGULATORY | WIPHY_FLAG_DISABLE_BEACON_HINTS;
+	    WIPHY_FLAG_CUSTOM_REGULATORY | WIPHY_FLAG_DISABLE_BEACON_HINTS |
+	    WIPHY_FLAG_IBSS_RSN;
 
 	/*
 	 * For now, disable PS by default because it affects
@@ -5873,6 +5874,16 @@ il4965_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 		return -EOPNOTSUPP;
 	}
 
+	/*
+	 * To support IBSS RSN, don't program group keys in IBSS, the
+	 * hardware will then not attempt to decrypt the frames.
+	 */
+	if (vif->type == NL80211_IFTYPE_ADHOC &&
+	    !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
+		D_MAC80211("leave - ad-hoc group key\n");
+		return -EOPNOTSUPP;
+	}
+
 	sta_id = il_sta_id_or_broadcast(il, sta);
 	if (sta_id == IL_INVALID_STATION)
 		return -EINVAL;

+ 3 - 0
drivers/net/wireless/iwlwifi/dvm/mac80211.c

@@ -201,6 +201,7 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv,
 			    WIPHY_FLAG_DISABLE_BEACON_HINTS |
 			    WIPHY_FLAG_IBSS_RSN;
 
+#ifdef CONFIG_PM_SLEEP
 	if (priv->fw->img[IWL_UCODE_WOWLAN].sec[0].len &&
 	    priv->trans->ops->wowlan_suspend &&
 	    device_can_wakeup(priv->trans->dev)) {
@@ -219,6 +220,7 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv,
 		hw->wiphy->wowlan.pattern_max_len =
 					IWLAGN_WOWLAN_MAX_PATTERN_LEN;
 	}
+#endif
 
 	if (iwlwifi_mod_params.power_save)
 		hw->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
@@ -251,6 +253,7 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv,
 	ret = ieee80211_register_hw(priv->hw);
 	if (ret) {
 		IWL_ERR(priv, "Failed to register hw (error %d)\n", ret);
+		iwl_leds_exit(priv);
 		return ret;
 	}
 	priv->mac80211_registered = 1;

+ 1 - 1
drivers/net/wireless/iwlwifi/dvm/sta.c

@@ -1251,7 +1251,7 @@ int iwl_remove_dynamic_key(struct iwl_priv *priv,
 		key_flags |= STA_KEY_MULTICAST_MSK;
 
 	sta_cmd.key.key_flags = key_flags;
-	sta_cmd.key.key_offset = WEP_INVALID_OFFSET;
+	sta_cmd.key.key_offset = keyconf->hw_key_idx;
 	sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK;
 	sta_cmd.mode = STA_CONTROL_MODIFY_MSK;
 

+ 9 - 1
drivers/net/wireless/iwlwifi/iwl-drv.c

@@ -899,7 +899,6 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
 
 	/* We have our copies now, allow OS release its copies */
 	release_firmware(ucode_raw);
-	complete(&drv->request_firmware_complete);
 
 	mutex_lock(&iwlwifi_opmode_table_mtx);
 	op = &iwlwifi_opmode_table[DVM_OP_MODE];
@@ -910,11 +909,20 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
 	if (op->ops) {
 		const struct iwl_op_mode_ops *ops = op->ops;
 		drv->op_mode = ops->start(drv->trans, drv->cfg, &drv->fw);
+
+		if (!drv->op_mode)
+			goto out_unbind;
 	} else {
 		request_module_nowait("%s", op->name);
 	}
 	mutex_unlock(&iwlwifi_opmode_table_mtx);
 
+	/*
+	 * Complete the firmware request last so that
+	 * a driver unbind (stop) doesn't run while we
+	 * are doing the start() above.
+	 */
+	complete(&drv->request_firmware_complete);
 	return;
 
  try_again:

+ 1 - 0
drivers/net/wireless/iwlwifi/iwl-prph.h

@@ -224,6 +224,7 @@
 #define SCD_TXFACT		(SCD_BASE + 0x10)
 #define SCD_ACTIVE		(SCD_BASE + 0x14)
 #define SCD_QUEUECHAIN_SEL	(SCD_BASE + 0xe8)
+#define SCD_CHAINEXT_EN		(SCD_BASE + 0x244)
 #define SCD_AGGR_SEL		(SCD_BASE + 0x248)
 #define SCD_INTERRUPT_MASK	(SCD_BASE + 0x108)
 

+ 21 - 2
drivers/net/wireless/iwlwifi/pcie/6000.c

@@ -35,17 +35,20 @@
 #define IWL6000_UCODE_API_MAX 6
 #define IWL6050_UCODE_API_MAX 5
 #define IWL6000G2_UCODE_API_MAX 6
+#define IWL6035_UCODE_API_MAX 6
 
 /* Oldest version we won't warn about */
 #define IWL6000_UCODE_API_OK 4
 #define IWL6000G2_UCODE_API_OK 5
 #define IWL6050_UCODE_API_OK 5
 #define IWL6000G2B_UCODE_API_OK 6
+#define IWL6035_UCODE_API_OK 6
 
 /* Lowest firmware API version supported */
 #define IWL6000_UCODE_API_MIN 4
 #define IWL6050_UCODE_API_MIN 4
-#define IWL6000G2_UCODE_API_MIN 4
+#define IWL6000G2_UCODE_API_MIN 5
+#define IWL6035_UCODE_API_MIN 6
 
 /* EEPROM versions */
 #define EEPROM_6000_TX_POWER_VERSION	(4)
@@ -243,9 +246,25 @@ const struct iwl_cfg iwl6030_2bg_cfg = {
 	IWL_DEVICE_6030,
 };
 
+#define IWL_DEVICE_6035						\
+	.fw_name_pre = IWL6030_FW_PRE,				\
+	.ucode_api_max = IWL6035_UCODE_API_MAX,			\
+	.ucode_api_ok = IWL6035_UCODE_API_OK,			\
+	.ucode_api_min = IWL6035_UCODE_API_MIN,			\
+	.device_family = IWL_DEVICE_FAMILY_6030,		\
+	.max_inst_size = IWL60_RTC_INST_SIZE,			\
+	.max_data_size = IWL60_RTC_DATA_SIZE,			\
+	.eeprom_ver = EEPROM_6030_EEPROM_VERSION,		\
+	.eeprom_calib_ver = EEPROM_6030_TX_POWER_VERSION,	\
+	.base_params = &iwl6000_g2_base_params,			\
+	.bt_params = &iwl6000_bt_params,			\
+	.need_temp_offset_calib = true,				\
+	.led_mode = IWL_LED_RF_STATE,				\
+	.adv_pm = true
+
 const struct iwl_cfg iwl6035_2agn_cfg = {
 	.name = "Intel(R) Centrino(R) Advanced-N 6235 AGN",
-	IWL_DEVICE_6030,
+	IWL_DEVICE_6035,
 	.ht_params = &iwl6000_ht_params,
 };
 

+ 5 - 0
drivers/net/wireless/iwlwifi/pcie/trans.c

@@ -1083,6 +1083,11 @@ static void iwl_tx_start(struct iwl_trans *trans)
 	iwl_write_prph(trans, SCD_DRAM_BASE_ADDR,
 		       trans_pcie->scd_bc_tbls.dma >> 10);
 
+	/* The chain extension of the SCD doesn't work well. This feature is
+	 * enabled by default by the HW, so we need to disable it manually.
+	 */
+	iwl_write_prph(trans, SCD_CHAINEXT_EN, 0);
+
 	for (i = 0; i < trans_pcie->n_q_to_fifo; i++) {
 		int fifo = trans_pcie->setup_q_to_fifo[i];
 

+ 28 - 11
drivers/net/wireless/libertas/cfg.c

@@ -435,24 +435,40 @@ static int lbs_add_wpa_tlv(u8 *tlv, const u8 *ie, u8 ie_len)
  * Set Channel
  */
 
-static int lbs_cfg_set_channel(struct wiphy *wiphy,
-	struct net_device *netdev,
-	struct ieee80211_channel *channel,
-	enum nl80211_channel_type channel_type)
+static int lbs_cfg_set_monitor_channel(struct wiphy *wiphy,
+				       struct ieee80211_channel *channel,
+				       enum nl80211_channel_type channel_type)
 {
 	struct lbs_private *priv = wiphy_priv(wiphy);
 	int ret = -ENOTSUPP;
 
-	lbs_deb_enter_args(LBS_DEB_CFG80211, "iface %s freq %d, type %d",
-			   netdev_name(netdev), channel->center_freq, channel_type);
+	lbs_deb_enter_args(LBS_DEB_CFG80211, "freq %d, type %d",
+			   channel->center_freq, channel_type);
 
 	if (channel_type != NL80211_CHAN_NO_HT)
 		goto out;
 
-	if (netdev == priv->mesh_dev)
-		ret = lbs_mesh_set_channel(priv, channel->hw_value);
-	else
-		ret = lbs_set_channel(priv, channel->hw_value);
+	ret = lbs_set_channel(priv, channel->hw_value);
+
+ out:
+	lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+	return ret;
+}
+
+static int lbs_cfg_set_mesh_channel(struct wiphy *wiphy,
+				    struct net_device *netdev,
+				    struct ieee80211_channel *channel)
+{
+	struct lbs_private *priv = wiphy_priv(wiphy);
+	int ret = -ENOTSUPP;
+
+	lbs_deb_enter_args(LBS_DEB_CFG80211, "iface %s freq %d",
+			   netdev_name(netdev), channel->center_freq);
+
+	if (netdev != priv->mesh_dev)
+		goto out;
+
+	ret = lbs_mesh_set_channel(priv, channel->hw_value);
 
  out:
 	lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
@@ -2029,7 +2045,8 @@ static int lbs_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
  */
 
 static struct cfg80211_ops lbs_cfg80211_ops = {
-	.set_channel = lbs_cfg_set_channel,
+	.set_monitor_channel = lbs_cfg_set_monitor_channel,
+	.libertas_set_mesh_channel = lbs_cfg_set_mesh_channel,
 	.scan = lbs_cfg_scan,
 	.connect = lbs_cfg_connect,
 	.disconnect = lbs_cfg_disconnect,

+ 1 - 0
drivers/net/wireless/libertas/dev.h

@@ -58,6 +58,7 @@ struct lbs_private {
 	uint16_t mesh_tlv;
 	u8 mesh_ssid[IEEE80211_MAX_SSID_LEN + 1];
 	u8 mesh_ssid_len;
+	u8 mesh_channel;
 #endif
 
 	/* Debugfs */

+ 2 - 5
drivers/net/wireless/libertas/mesh.c

@@ -131,16 +131,13 @@ static int lbs_mesh_config(struct lbs_private *priv, uint16_t action,
 
 int lbs_mesh_set_channel(struct lbs_private *priv, u8 channel)
 {
+	priv->mesh_channel = channel;
 	return lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, channel);
 }
 
 static uint16_t lbs_mesh_get_channel(struct lbs_private *priv)
 {
-	struct wireless_dev *mesh_wdev = priv->mesh_dev->ieee80211_ptr;
-	if (mesh_wdev->channel)
-		return mesh_wdev->channel->hw_value;
-	else
-		return 1;
+	return priv->mesh_channel ?: 1;
 }
 
 /***************************************************************************

+ 22 - 0
drivers/net/wireless/mac80211_hwsim.c

@@ -1555,6 +1555,7 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
 			hdr = (struct ieee80211_hdr *) skb->data;
 			mac80211_hwsim_monitor_ack(data2->hw, hdr->addr2);
 		}
+		txi->flags |= IEEE80211_TX_STAT_ACK;
 	}
 	ieee80211_tx_status_irqsafe(data2->hw, skb);
 	return 0;
@@ -1721,6 +1722,24 @@ static void hwsim_exit_netlink(void)
 		       "unregister family %i\n", ret);
 }
 
+static const struct ieee80211_iface_limit hwsim_if_limits[] = {
+	{ .max = 1, .types = BIT(NL80211_IFTYPE_ADHOC) },
+	{ .max = 2048,  .types = BIT(NL80211_IFTYPE_STATION) |
+				 BIT(NL80211_IFTYPE_P2P_CLIENT) |
+#ifdef CONFIG_MAC80211_MESH
+				 BIT(NL80211_IFTYPE_MESH_POINT) |
+#endif
+				 BIT(NL80211_IFTYPE_AP) |
+				 BIT(NL80211_IFTYPE_P2P_GO) },
+};
+
+static const struct ieee80211_iface_combination hwsim_if_comb = {
+	.limits = hwsim_if_limits,
+	.n_limits = ARRAY_SIZE(hwsim_if_limits),
+	.max_interfaces = 2048,
+	.num_different_channels = 1,
+};
+
 static int __init init_mac80211_hwsim(void)
 {
 	int i, err = 0;
@@ -1782,6 +1801,9 @@ static int __init init_mac80211_hwsim(void)
 		hw->wiphy->n_addresses = 2;
 		hw->wiphy->addresses = data->addresses;
 
+		hw->wiphy->iface_combinations = &hwsim_if_comb;
+		hw->wiphy->n_iface_combinations = 1;
+
 		if (fake_hw_scan) {
 			hw->wiphy->max_scan_ssids = 255;
 			hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;

+ 65 - 0
drivers/net/wireless/mwifiex/init.c

@@ -57,6 +57,68 @@ static int mwifiex_add_bss_prio_tbl(struct mwifiex_private *priv)
 	return 0;
 }
 
+static void scan_delay_timer_fn(unsigned long data)
+{
+	struct mwifiex_private *priv = (struct mwifiex_private *)data;
+	struct mwifiex_adapter *adapter = priv->adapter;
+	struct cmd_ctrl_node *cmd_node, *tmp_node;
+	unsigned long flags;
+
+	if (!mwifiex_wmm_lists_empty(adapter)) {
+		if (adapter->scan_delay_cnt == MWIFIEX_MAX_SCAN_DELAY_CNT) {
+			/*
+			 * Abort scan operation by cancelling all pending scan
+			 * command
+			 */
+			spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
+			list_for_each_entry_safe(cmd_node, tmp_node,
+						 &adapter->scan_pending_q,
+						 list) {
+				list_del(&cmd_node->list);
+				cmd_node->wait_q_enabled = false;
+				mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
+			}
+			spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
+					       flags);
+
+			spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
+			adapter->scan_processing = false;
+			spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock,
+					       flags);
+
+			if (priv->user_scan_cfg) {
+				dev_dbg(priv->adapter->dev,
+					"info: %s: scan aborted\n", __func__);
+				cfg80211_scan_done(priv->scan_request, 1);
+				priv->scan_request = NULL;
+				kfree(priv->user_scan_cfg);
+				priv->user_scan_cfg = NULL;
+			}
+		} else {
+			/*
+			 * Tx data queue is still not empty, delay scan
+			 * operation further by 20msec.
+			 */
+			mod_timer(&priv->scan_delay_timer, jiffies +
+				  msecs_to_jiffies(MWIFIEX_SCAN_DELAY_MSEC));
+			adapter->scan_delay_cnt++;
+		}
+	} else {
+		/*
+		 * Tx data queue is empty. Get scan command from scan_pending_q
+		 * and put to cmd_pending_q to resume scan operation
+		 */
+		adapter->scan_delay_cnt = 0;
+		spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
+		cmd_node = list_first_entry(&adapter->scan_pending_q,
+					    struct cmd_ctrl_node, list);
+		list_del(&cmd_node->list);
+		spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags);
+
+		mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true);
+	}
+}
+
 /*
  * This function initializes the private structure and sets default
  * values to the members.
@@ -136,6 +198,9 @@ static int mwifiex_init_priv(struct mwifiex_private *priv)
 
 	priv->scan_block = false;
 
+	setup_timer(&priv->scan_delay_timer, scan_delay_timer_fn,
+		    (unsigned long)priv);
+
 	return mwifiex_add_bss_prio_tbl(priv);
 }
 

+ 2 - 2
drivers/net/wireless/mwifiex/main.c

@@ -244,8 +244,8 @@ process_start:
 			}
 		}
 
-		if (!adapter->scan_processing && !adapter->data_sent &&
-		    !mwifiex_wmm_lists_empty(adapter)) {
+		if ((!adapter->scan_processing || adapter->scan_delay_cnt) &&
+		    !adapter->data_sent && !mwifiex_wmm_lists_empty(adapter)) {
 			mwifiex_wmm_process_tx(adapter);
 			if (adapter->hs_activated) {
 				adapter->is_hs_configured = false;

+ 8 - 3
drivers/net/wireless/mwifiex/main.h

@@ -79,14 +79,17 @@ enum {
 
 #define SCAN_BEACON_ENTRY_PAD			6
 
-#define MWIFIEX_PASSIVE_SCAN_CHAN_TIME	200
-#define MWIFIEX_ACTIVE_SCAN_CHAN_TIME	200
-#define MWIFIEX_SPECIFIC_SCAN_CHAN_TIME	110
+#define MWIFIEX_PASSIVE_SCAN_CHAN_TIME	110
+#define MWIFIEX_ACTIVE_SCAN_CHAN_TIME	30
+#define MWIFIEX_SPECIFIC_SCAN_CHAN_TIME	30
 
 #define SCAN_RSSI(RSSI)					(0x100 - ((u8)(RSSI)))
 
 #define MWIFIEX_MAX_TOTAL_SCAN_TIME	(MWIFIEX_TIMER_10S - MWIFIEX_TIMER_1S)
 
+#define MWIFIEX_MAX_SCAN_DELAY_CNT			50
+#define MWIFIEX_SCAN_DELAY_MSEC				20
+
 #define RSN_GTK_OUI_OFFSET				2
 
 #define MWIFIEX_OUI_NOT_PRESENT			0
@@ -482,6 +485,7 @@ struct mwifiex_private {
 	u16 proberesp_idx;
 	u16 assocresp_idx;
 	u16 rsn_idx;
+	struct timer_list scan_delay_timer;
 };
 
 enum mwifiex_ba_status {
@@ -686,6 +690,7 @@ struct mwifiex_adapter {
 	struct completion fw_load;
 	u8 country_code[IEEE80211_COUNTRY_STRING_LEN];
 	u16 max_mgmt_ie_index;
+	u8 scan_delay_cnt;
 };
 
 int mwifiex_init_lock_list(struct mwifiex_adapter *adapter);

+ 44 - 15
drivers/net/wireless/mwifiex/scan.c

@@ -28,7 +28,10 @@
 /* The maximum number of channels the firmware can scan per command */
 #define MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN   14
 
-#define MWIFIEX_CHANNELS_PER_SCAN_CMD            4
+#define MWIFIEX_DEF_CHANNELS_PER_SCAN_CMD	4
+#define MWIFIEX_LIMIT_1_CHANNEL_PER_SCAN_CMD	15
+#define MWIFIEX_LIMIT_2_CHANNELS_PER_SCAN_CMD	27
+#define MWIFIEX_LIMIT_3_CHANNELS_PER_SCAN_CMD	35
 
 /* Memory needed to store a max sized Channel List TLV for a firmware scan */
 #define CHAN_TLV_MAX_SIZE  (sizeof(struct mwifiex_ie_types_header)         \
@@ -471,7 +474,7 @@ mwifiex_is_network_compatible(struct mwifiex_private *priv,
  * This routine is used for any scan that is not provided with a
  * specific channel list to scan.
  */
-static void
+static int
 mwifiex_scan_create_channel_list(struct mwifiex_private *priv,
 				 const struct mwifiex_user_scan_cfg
 							*user_scan_in,
@@ -528,6 +531,7 @@ mwifiex_scan_create_channel_list(struct mwifiex_private *priv,
 		}
 
 	}
+	return chan_idx;
 }
 
 /*
@@ -727,6 +731,7 @@ mwifiex_config_scan(struct mwifiex_private *priv,
 	u32 num_probes;
 	u32 ssid_len;
 	u32 chan_idx;
+	u32 chan_num;
 	u32 scan_type;
 	u16 scan_dur;
 	u8 channel;
@@ -850,7 +855,7 @@ mwifiex_config_scan(struct mwifiex_private *priv,
 	if (*filtered_scan)
 		*max_chan_per_scan = MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN;
 	else
-		*max_chan_per_scan = MWIFIEX_CHANNELS_PER_SCAN_CMD;
+		*max_chan_per_scan = MWIFIEX_DEF_CHANNELS_PER_SCAN_CMD;
 
 	/* If the input config or adapter has the number of Probes set,
 	   add tlv */
@@ -962,13 +967,28 @@ mwifiex_config_scan(struct mwifiex_private *priv,
 			dev_dbg(adapter->dev,
 				"info: Scan: Scanning current channel only\n");
 		}
-
+		chan_num = chan_idx;
 	} else {
 		dev_dbg(adapter->dev,
 			"info: Scan: Creating full region channel list\n");
-		mwifiex_scan_create_channel_list(priv, user_scan_in,
-						 scan_chan_list,
-						 *filtered_scan);
+		chan_num = mwifiex_scan_create_channel_list(priv, user_scan_in,
+							    scan_chan_list,
+							    *filtered_scan);
+	}
+
+	/*
+	 * In associated state we will reduce the number of channels scanned per
+	 * scan command to avoid any traffic delay/loss. This number is decided
+	 * based on total number of channels to be scanned due to constraints
+	 * of command buffers.
+	 */
+	if (priv->media_connected) {
+		if (chan_num < MWIFIEX_LIMIT_1_CHANNEL_PER_SCAN_CMD)
+			*max_chan_per_scan = 1;
+		else if (chan_num < MWIFIEX_LIMIT_2_CHANNELS_PER_SCAN_CMD)
+			*max_chan_per_scan = 2;
+		else if (chan_num < MWIFIEX_LIMIT_3_CHANNELS_PER_SCAN_CMD)
+			*max_chan_per_scan = 3;
 	}
 }
 
@@ -1772,14 +1792,23 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
 			priv->user_scan_cfg = NULL;
 		}
 	} else {
-		/* Get scan command from scan_pending_q and put to
-		   cmd_pending_q */
-		cmd_node = list_first_entry(&adapter->scan_pending_q,
-					    struct cmd_ctrl_node, list);
-		list_del(&cmd_node->list);
-		spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags);
-
-		mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true);
+		if (!mwifiex_wmm_lists_empty(adapter)) {
+			spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
+					       flags);
+			adapter->scan_delay_cnt = 1;
+			mod_timer(&priv->scan_delay_timer, jiffies +
+				  msecs_to_jiffies(MWIFIEX_SCAN_DELAY_MSEC));
+		} else {
+			/* Get scan command from scan_pending_q and put to
+			   cmd_pending_q */
+			cmd_node = list_first_entry(&adapter->scan_pending_q,
+						    struct cmd_ctrl_node, list);
+			list_del(&cmd_node->list);
+			spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
+					       flags);
+			mwifiex_insert_cmd_to_pending_q(adapter, cmd_node,
+							true);
+		}
 	}
 
 done:

+ 4 - 5
drivers/net/wireless/orinoco/cfg.c

@@ -160,10 +160,9 @@ static int orinoco_scan(struct wiphy *wiphy, struct net_device *dev,
 	return err;
 }
 
-static int orinoco_set_channel(struct wiphy *wiphy,
-			struct net_device *netdev,
-			struct ieee80211_channel *chan,
-			enum nl80211_channel_type channel_type)
+static int orinoco_set_monitor_channel(struct wiphy *wiphy,
+				       struct ieee80211_channel *chan,
+				       enum nl80211_channel_type channel_type)
 {
 	struct orinoco_private *priv = wiphy_priv(wiphy);
 	int err = 0;
@@ -286,7 +285,7 @@ static int orinoco_set_wiphy_params(struct wiphy *wiphy, u32 changed)
 
 const struct cfg80211_ops orinoco_cfg_ops = {
 	.change_virtual_intf = orinoco_change_vif,
-	.set_channel = orinoco_set_channel,
+	.set_monitor_channel = orinoco_set_monitor_channel,
 	.scan = orinoco_scan,
 	.set_wiphy_params = orinoco_set_wiphy_params,
 };

+ 6 - 0
drivers/net/wireless/rt2x00/rt2800.h

@@ -72,6 +72,7 @@
 #define RF5370				0x5370
 #define RF5372				0x5372
 #define RF5390				0x5390
+#define RF5392				0x5392
 
 /*
  * Chipset revisions.
@@ -1945,6 +1946,11 @@ struct mac_iveiv_entry {
  */
 #define RFCSR49_TX			FIELD8(0x3f)
 
+/*
+ * RFCSR 50:
+ */
+#define RFCSR50_TX			FIELD8(0x3f)
+
 /*
  * RF registers
  */

+ 21 - 1
drivers/net/wireless/rt2x00/rt2800lib.c

@@ -1958,7 +1958,22 @@ static void rt2800_config_channel_rf53xx(struct rt2x00_dev *rt2x00dev,
 		rt2x00_set_field8(&rfcsr, RFCSR49_TX, info->default_power1);
 	rt2800_rfcsr_write(rt2x00dev, 49, rfcsr);
 
+	if (rt2x00_rt(rt2x00dev, RT5392)) {
+		rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr);
+		if (info->default_power1 > RT5390_POWER_BOUND)
+			rt2x00_set_field8(&rfcsr, RFCSR50_TX,
+					  RT5390_POWER_BOUND);
+		else
+			rt2x00_set_field8(&rfcsr, RFCSR50_TX,
+					  info->default_power2);
+		rt2800_rfcsr_write(rt2x00dev, 50, rfcsr);
+	}
+
 	rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr);
+	if (rt2x00_rt(rt2x00dev, RT5392)) {
+		rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1);
+		rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1);
+	}
 	rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1);
 	rt2x00_set_field8(&rfcsr, RFCSR1_PLL_PD, 1);
 	rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1);
@@ -2064,6 +2079,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
 	case RF5370:
 	case RF5372:
 	case RF5390:
+	case RF5392:
 		rt2800_config_channel_rf53xx(rt2x00dev, conf, rf, info);
 		break;
 	default:
@@ -2554,6 +2570,7 @@ void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev)
 	case RF5370:
 	case RF5372:
 	case RF5390:
+	case RF5392:
 		rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr);
 		rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 1);
 		rt2800_rfcsr_write(rt2x00dev, 3, rfcsr);
@@ -4269,6 +4286,7 @@ int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev)
 	case RF5370:
 	case RF5372:
 	case RF5390:
+	case RF5392:
 		break;
 	default:
 		ERROR(rt2x00dev, "Invalid RF chipset 0x%04x detected.\n",
@@ -4583,7 +4601,8 @@ int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
 		   rt2x00_rf(rt2x00dev, RF5360) ||
 		   rt2x00_rf(rt2x00dev, RF5370) ||
 		   rt2x00_rf(rt2x00dev, RF5372) ||
-		   rt2x00_rf(rt2x00dev, RF5390)) {
+		   rt2x00_rf(rt2x00dev, RF5390) ||
+		   rt2x00_rf(rt2x00dev, RF5392)) {
 		spec->num_channels = 14;
 		spec->channels = rf_vals_3x;
 	} else if (rt2x00_rf(rt2x00dev, RF3052)) {
@@ -4670,6 +4689,7 @@ int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
 	case RF5370:
 	case RF5372:
 	case RF5390:
+	case RF5392:
 		__set_bit(CAPABILITY_VCO_RECALIBRATION, &rt2x00dev->cap_flags);
 		break;
 	}

+ 1 - 2
drivers/net/wireless/rt2x00/rt2x00.h

@@ -396,8 +396,7 @@ struct rt2x00_intf {
 	 * for hardware which doesn't support hardware
 	 * sequence counting.
 	 */
-	spinlock_t seqlock;
-	u16 seqno;
+	atomic_t seqno;
 };
 
 static inline struct rt2x00_intf* vif_to_intf(struct ieee80211_vif *vif)

+ 2 - 0
drivers/net/wireless/rt2x00/rt2x00dev.c

@@ -1161,6 +1161,8 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
 		    BIT(NL80211_IFTYPE_MESH_POINT) |
 		    BIT(NL80211_IFTYPE_WDS);
 
+	rt2x00dev->hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
+
 	/*
 	 * Initialize work.
 	 */

+ 12 - 3
drivers/net/wireless/rt2x00/rt2x00mac.c

@@ -277,7 +277,6 @@ int rt2x00mac_add_interface(struct ieee80211_hw *hw,
 	else
 		rt2x00dev->intf_sta_count++;
 
-	spin_lock_init(&intf->seqlock);
 	mutex_init(&intf->beacon_skb_mutex);
 	intf->beacon = entry;
 
@@ -507,9 +506,19 @@ int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 
 	if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
 		return 0;
-	else if (!test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags))
+
+	if (!test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags))
+		return -EOPNOTSUPP;
+
+	/*
+	 * To support IBSS RSN, don't program group keys in IBSS, the
+	 * hardware will then not attempt to decrypt the frames.
+	 */
+	if (vif->type == NL80211_IFTYPE_ADHOC &&
+	    !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
 		return -EOPNOTSUPP;
-	else if (key->keylen > 32)
+
+	if (key->keylen > 32)
 		return -ENOSPC;
 
 	memset(&crypto, 0, sizeof(crypto));

+ 6 - 7
drivers/net/wireless/rt2x00/rt2x00queue.c

@@ -207,6 +207,7 @@ static void rt2x00queue_create_tx_descriptor_seq(struct rt2x00_dev *rt2x00dev,
 	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 	struct rt2x00_intf *intf = vif_to_intf(tx_info->control.vif);
+	u16 seqno;
 
 	if (!(tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ))
 		return;
@@ -238,15 +239,13 @@ static void rt2x00queue_create_tx_descriptor_seq(struct rt2x00_dev *rt2x00dev,
 	 * sequence counting per-frame, since those will override the
 	 * sequence counter given by mac80211.
 	 */
-	spin_lock(&intf->seqlock);
-
 	if (test_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags))
-		intf->seqno += 0x10;
-	hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
-	hdr->seq_ctrl |= cpu_to_le16(intf->seqno);
-
-	spin_unlock(&intf->seqlock);
+		seqno = atomic_add_return(0x10, &intf->seqno);
+	else
+		seqno = atomic_read(&intf->seqno);
 
+	hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
+	hdr->seq_ctrl |= cpu_to_le16(seqno);
 }
 
 static void rt2x00queue_create_tx_descriptor_plcp(struct rt2x00_dev *rt2x00dev,

+ 1 - 1
drivers/net/wireless/rtl818x/rtl8187/leds.c

@@ -117,7 +117,7 @@ static void rtl8187_led_brightness_set(struct led_classdev *led_dev,
 			radio_on = true;
 		} else if (radio_on) {
 			radio_on = false;
-			cancel_delayed_work_sync(&priv->led_on);
+			cancel_delayed_work(&priv->led_on);
 			ieee80211_queue_delayed_work(hw, &priv->led_off, 0);
 		}
 	} else if (radio_on) {

+ 1 - 0
drivers/net/wireless/ti/Kconfig

@@ -8,6 +8,7 @@ menuconfig WL_TI
 if WL_TI
 source "drivers/net/wireless/ti/wl1251/Kconfig"
 source "drivers/net/wireless/ti/wl12xx/Kconfig"
+source "drivers/net/wireless/ti/wl18xx/Kconfig"
 
 # keep last for automatic dependencies
 source "drivers/net/wireless/ti/wlcore/Kconfig"

+ 1 - 0
drivers/net/wireless/ti/Makefile

@@ -2,3 +2,4 @@ obj-$(CONFIG_WLCORE)			+= wlcore/
 obj-$(CONFIG_WL12XX)			+= wl12xx/
 obj-$(CONFIG_WL12XX_PLATFORM_DATA)	+= wlcore/
 obj-$(CONFIG_WL1251)			+= wl1251/
+obj-$(CONFIG_WL18XX)			+= wl18xx/

+ 1 - 1
drivers/net/wireless/ti/wl12xx/Makefile

@@ -1,3 +1,3 @@
-wl12xx-objs	= main.o cmd.o acx.o
+wl12xx-objs	= main.o cmd.o acx.o debugfs.o
 
 obj-$(CONFIG_WL12XX)		+= wl12xx.o

+ 237 - 0
drivers/net/wireless/ti/wl12xx/acx.h

@@ -24,6 +24,21 @@
 #define __WL12XX_ACX_H__
 
 #include "../wlcore/wlcore.h"
+#include "../wlcore/acx.h"
+
+#define WL12XX_ACX_ALL_EVENTS_VECTOR	(WL1271_ACX_INTR_WATCHDOG      | \
+					WL1271_ACX_INTR_INIT_COMPLETE | \
+					WL1271_ACX_INTR_EVENT_A       | \
+					WL1271_ACX_INTR_EVENT_B       | \
+					WL1271_ACX_INTR_CMD_COMPLETE  | \
+					WL1271_ACX_INTR_HW_AVAILABLE  | \
+					WL1271_ACX_INTR_DATA)
+
+#define WL12XX_INTR_MASK		(WL1271_ACX_INTR_WATCHDOG      | \
+					WL1271_ACX_INTR_EVENT_A       | \
+					WL1271_ACX_INTR_EVENT_B       | \
+					WL1271_ACX_INTR_HW_AVAILABLE  | \
+					WL1271_ACX_INTR_DATA)
 
 struct wl1271_acx_host_config_bitmap {
 	struct acx_header header;
@@ -31,6 +46,228 @@ struct wl1271_acx_host_config_bitmap {
 	__le32 host_cfg_bitmap;
 } __packed;
 
+struct wl12xx_acx_tx_statistics {
+	__le32 internal_desc_overflow;
+}  __packed;
+
+struct wl12xx_acx_rx_statistics {
+	__le32 out_of_mem;
+	__le32 hdr_overflow;
+	__le32 hw_stuck;
+	__le32 dropped;
+	__le32 fcs_err;
+	__le32 xfr_hint_trig;
+	__le32 path_reset;
+	__le32 reset_counter;
+} __packed;
+
+struct wl12xx_acx_dma_statistics {
+	__le32 rx_requested;
+	__le32 rx_errors;
+	__le32 tx_requested;
+	__le32 tx_errors;
+}  __packed;
+
+struct wl12xx_acx_isr_statistics {
+	/* host command complete */
+	__le32 cmd_cmplt;
+
+	/* fiqisr() */
+	__le32 fiqs;
+
+	/* (INT_STS_ND & INT_TRIG_RX_HEADER) */
+	__le32 rx_headers;
+
+	/* (INT_STS_ND & INT_TRIG_RX_CMPLT) */
+	__le32 rx_completes;
+
+	/* (INT_STS_ND & INT_TRIG_NO_RX_BUF) */
+	__le32 rx_mem_overflow;
+
+	/* (INT_STS_ND & INT_TRIG_S_RX_RDY) */
+	__le32 rx_rdys;
+
+	/* irqisr() */
+	__le32 irqs;
+
+	/* (INT_STS_ND & INT_TRIG_TX_PROC) */
+	__le32 tx_procs;
+
+	/* (INT_STS_ND & INT_TRIG_DECRYPT_DONE) */
+	__le32 decrypt_done;
+
+	/* (INT_STS_ND & INT_TRIG_DMA0) */
+	__le32 dma0_done;
+
+	/* (INT_STS_ND & INT_TRIG_DMA1) */
+	__le32 dma1_done;
+
+	/* (INT_STS_ND & INT_TRIG_TX_EXC_CMPLT) */
+	__le32 tx_exch_complete;
+
+	/* (INT_STS_ND & INT_TRIG_COMMAND) */
+	__le32 commands;
+
+	/* (INT_STS_ND & INT_TRIG_RX_PROC) */
+	__le32 rx_procs;
+
+	/* (INT_STS_ND & INT_TRIG_PM_802) */
+	__le32 hw_pm_mode_changes;
+
+	/* (INT_STS_ND & INT_TRIG_ACKNOWLEDGE) */
+	__le32 host_acknowledges;
+
+	/* (INT_STS_ND & INT_TRIG_PM_PCI) */
+	__le32 pci_pm;
+
+	/* (INT_STS_ND & INT_TRIG_ACM_WAKEUP) */
+	__le32 wakeups;
+
+	/* (INT_STS_ND & INT_TRIG_LOW_RSSI) */
+	__le32 low_rssi;
+} __packed;
+
+struct wl12xx_acx_wep_statistics {
+	/* WEP address keys configured */
+	__le32 addr_key_count;
+
+	/* default keys configured */
+	__le32 default_key_count;
+
+	__le32 reserved;
+
+	/* number of times that WEP key not found on lookup */
+	__le32 key_not_found;
+
+	/* number of times that WEP key decryption failed */
+	__le32 decrypt_fail;
+
+	/* WEP packets decrypted */
+	__le32 packets;
+
+	/* WEP decrypt interrupts */
+	__le32 interrupt;
+} __packed;
+
+#define ACX_MISSED_BEACONS_SPREAD 10
+
+struct wl12xx_acx_pwr_statistics {
+	/* the amount of enters into power save mode (both PD & ELP) */
+	__le32 ps_enter;
+
+	/* the amount of enters into ELP mode */
+	__le32 elp_enter;
+
+	/* the amount of missing beacon interrupts to the host */
+	__le32 missing_bcns;
+
+	/* the amount of wake on host-access times */
+	__le32 wake_on_host;
+
+	/* the amount of wake on timer-expire */
+	__le32 wake_on_timer_exp;
+
+	/* the number of packets that were transmitted with PS bit set */
+	__le32 tx_with_ps;
+
+	/* the number of packets that were transmitted with PS bit clear */
+	__le32 tx_without_ps;
+
+	/* the number of received beacons */
+	__le32 rcvd_beacons;
+
+	/* the number of entering into PowerOn (power save off) */
+	__le32 power_save_off;
+
+	/* the number of entries into power save mode */
+	__le16 enable_ps;
+
+	/*
+	 * the number of exits from power save, not including failed PS
+	 * transitions
+	 */
+	__le16 disable_ps;
+
+	/*
+	 * the number of times the TSF counter was adjusted because
+	 * of drift
+	 */
+	__le32 fix_tsf_ps;
+
+	/* Gives statistics about the spread continuous missed beacons.
+	 * The 16 LSB are dedicated for the PS mode.
+	 * The 16 MSB are dedicated for the PS mode.
+	 * cont_miss_bcns_spread[0] - single missed beacon.
+	 * cont_miss_bcns_spread[1] - two continuous missed beacons.
+	 * cont_miss_bcns_spread[2] - three continuous missed beacons.
+	 * ...
+	 * cont_miss_bcns_spread[9] - ten and more continuous missed beacons.
+	*/
+	__le32 cont_miss_bcns_spread[ACX_MISSED_BEACONS_SPREAD];
+
+	/* the number of beacons in awake mode */
+	__le32 rcvd_awake_beacons;
+} __packed;
+
+struct wl12xx_acx_mic_statistics {
+	__le32 rx_pkts;
+	__le32 calc_failure;
+} __packed;
+
+struct wl12xx_acx_aes_statistics {
+	__le32 encrypt_fail;
+	__le32 decrypt_fail;
+	__le32 encrypt_packets;
+	__le32 decrypt_packets;
+	__le32 encrypt_interrupt;
+	__le32 decrypt_interrupt;
+} __packed;
+
+struct wl12xx_acx_event_statistics {
+	__le32 heart_beat;
+	__le32 calibration;
+	__le32 rx_mismatch;
+	__le32 rx_mem_empty;
+	__le32 rx_pool;
+	__le32 oom_late;
+	__le32 phy_transmit_error;
+	__le32 tx_stuck;
+} __packed;
+
+struct wl12xx_acx_ps_statistics {
+	__le32 pspoll_timeouts;
+	__le32 upsd_timeouts;
+	__le32 upsd_max_sptime;
+	__le32 upsd_max_apturn;
+	__le32 pspoll_max_apturn;
+	__le32 pspoll_utilization;
+	__le32 upsd_utilization;
+} __packed;
+
+struct wl12xx_acx_rxpipe_statistics {
+	__le32 rx_prep_beacon_drop;
+	__le32 descr_host_int_trig_rx_data;
+	__le32 beacon_buffer_thres_host_int_trig_rx_data;
+	__le32 missed_beacon_host_int_trig_rx_data;
+	__le32 tx_xfr_host_int_trig_rx_data;
+} __packed;
+
+struct wl12xx_acx_statistics {
+	struct acx_header header;
+
+	struct wl12xx_acx_tx_statistics tx;
+	struct wl12xx_acx_rx_statistics rx;
+	struct wl12xx_acx_dma_statistics dma;
+	struct wl12xx_acx_isr_statistics isr;
+	struct wl12xx_acx_wep_statistics wep;
+	struct wl12xx_acx_pwr_statistics pwr;
+	struct wl12xx_acx_aes_statistics aes;
+	struct wl12xx_acx_mic_statistics mic;
+	struct wl12xx_acx_event_statistics event;
+	struct wl12xx_acx_ps_statistics ps;
+	struct wl12xx_acx_rxpipe_statistics rxpipe;
+} __packed;
+
 int wl1271_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap);
 
 #endif /* __WL12XX_ACX_H__ */

+ 5 - 3
drivers/net/wireless/ti/wl12xx/cmd.c

@@ -65,6 +65,7 @@ int wl1271_cmd_general_parms(struct wl1271 *wl)
 	struct wl1271_general_parms_cmd *gen_parms;
 	struct wl1271_ini_general_params *gp =
 		&((struct wl1271_nvs_file *)wl->nvs)->general_params;
+	struct wl12xx_priv *priv = wl->priv;
 	bool answer = false;
 	int ret;
 
@@ -88,7 +89,7 @@ int wl1271_cmd_general_parms(struct wl1271 *wl)
 		answer = true;
 
 	/* Override the REF CLK from the NVS with the one from platform data */
-	gen_parms->general_params.ref_clock = wl->ref_clock;
+	gen_parms->general_params.ref_clock = priv->ref_clock;
 
 	ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), answer);
 	if (ret < 0) {
@@ -118,6 +119,7 @@ int wl128x_cmd_general_parms(struct wl1271 *wl)
 	struct wl128x_general_parms_cmd *gen_parms;
 	struct wl128x_ini_general_params *gp =
 		&((struct wl128x_nvs_file *)wl->nvs)->general_params;
+	struct wl12xx_priv *priv = wl->priv;
 	bool answer = false;
 	int ret;
 
@@ -141,8 +143,8 @@ int wl128x_cmd_general_parms(struct wl1271 *wl)
 		answer = true;
 
 	/* Replace REF and TCXO CLKs with the ones from platform data */
-	gen_parms->general_params.ref_clock = wl->ref_clock;
-	gen_parms->general_params.tcxo_ref_clock = wl->tcxo_clock;
+	gen_parms->general_params.ref_clock = priv->ref_clock;
+	gen_parms->general_params.tcxo_ref_clock = priv->tcxo_clock;
 
 	ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), answer);
 	if (ret < 0) {

+ 243 - 0
drivers/net/wireless/ti/wl12xx/debugfs.c

@@ -0,0 +1,243 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Copyright (C) 2011-2012 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "../wlcore/debugfs.h"
+#include "../wlcore/wlcore.h"
+
+#include "wl12xx.h"
+#include "acx.h"
+#include "debugfs.h"
+
+#define WL12XX_DEBUGFS_FWSTATS_FILE(a, b, c) \
+	DEBUGFS_FWSTATS_FILE(a, b, c, wl12xx_acx_statistics)
+
+WL12XX_DEBUGFS_FWSTATS_FILE(tx, internal_desc_overflow, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(rx, out_of_mem, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rx, hdr_overflow, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rx, hw_stuck, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rx, dropped, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rx, fcs_err, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rx, xfr_hint_trig, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rx, path_reset, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rx, reset_counter, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(dma, rx_requested, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(dma, rx_errors, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(dma, tx_requested, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(dma, tx_errors, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, cmd_cmplt, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, fiqs, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, rx_headers, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, rx_mem_overflow, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, rx_rdys, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, irqs, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, tx_procs, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, decrypt_done, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, dma0_done, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, dma1_done, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, tx_exch_complete, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, commands, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, rx_procs, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, hw_pm_mode_changes, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, host_acknowledges, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, pci_pm, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, wakeups, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, low_rssi, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(wep, addr_key_count, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(wep, default_key_count, "%u");
+/* skipping wep.reserved */
+WL12XX_DEBUGFS_FWSTATS_FILE(wep, key_not_found, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(wep, decrypt_fail, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(wep, packets, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(wep, interrupt, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, ps_enter, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, elp_enter, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, missing_bcns, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, wake_on_host, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, wake_on_timer_exp, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, tx_with_ps, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, tx_without_ps, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_beacons, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, power_save_off, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, enable_ps, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, disable_ps, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, fix_tsf_ps, "%u");
+/* skipping cont_miss_bcns_spread for now */
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_awake_beacons, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(mic, rx_pkts, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(mic, calc_failure, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(aes, encrypt_fail, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(aes, decrypt_fail, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(aes, encrypt_packets, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(aes, decrypt_packets, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(aes, encrypt_interrupt, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(aes, decrypt_interrupt, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(event, heart_beat, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(event, calibration, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(event, rx_mismatch, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(event, rx_mem_empty, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(event, rx_pool, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(event, oom_late, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(event, phy_transmit_error, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(event, tx_stuck, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(ps, pspoll_timeouts, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(ps, upsd_timeouts, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(ps, upsd_max_sptime, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(ps, upsd_max_apturn, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(ps, pspoll_max_apturn, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(ps, pspoll_utilization, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(ps, upsd_utilization, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, rx_prep_beacon_drop, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, descr_host_int_trig_rx_data, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, beacon_buffer_thres_host_int_trig_rx_data,
+			    "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, missed_beacon_host_int_trig_rx_data, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, tx_xfr_host_int_trig_rx_data, "%u");
+
+int wl12xx_debugfs_add_files(struct wl1271 *wl,
+			     struct dentry *rootdir)
+{
+	int ret = 0;
+	struct dentry *entry, *stats, *moddir;
+
+	moddir = debugfs_create_dir(KBUILD_MODNAME, rootdir);
+	if (!moddir || IS_ERR(moddir)) {
+		entry = moddir;
+		goto err;
+	}
+
+	stats = debugfs_create_dir("fw_stats", moddir);
+	if (!stats || IS_ERR(stats)) {
+		entry = stats;
+		goto err;
+	}
+
+	DEBUGFS_FWSTATS_ADD(tx, internal_desc_overflow);
+
+	DEBUGFS_FWSTATS_ADD(rx, out_of_mem);
+	DEBUGFS_FWSTATS_ADD(rx, hdr_overflow);
+	DEBUGFS_FWSTATS_ADD(rx, hw_stuck);
+	DEBUGFS_FWSTATS_ADD(rx, dropped);
+	DEBUGFS_FWSTATS_ADD(rx, fcs_err);
+	DEBUGFS_FWSTATS_ADD(rx, xfr_hint_trig);
+	DEBUGFS_FWSTATS_ADD(rx, path_reset);
+	DEBUGFS_FWSTATS_ADD(rx, reset_counter);
+
+	DEBUGFS_FWSTATS_ADD(dma, rx_requested);
+	DEBUGFS_FWSTATS_ADD(dma, rx_errors);
+	DEBUGFS_FWSTATS_ADD(dma, tx_requested);
+	DEBUGFS_FWSTATS_ADD(dma, tx_errors);
+
+	DEBUGFS_FWSTATS_ADD(isr, cmd_cmplt);
+	DEBUGFS_FWSTATS_ADD(isr, fiqs);
+	DEBUGFS_FWSTATS_ADD(isr, rx_headers);
+	DEBUGFS_FWSTATS_ADD(isr, rx_mem_overflow);
+	DEBUGFS_FWSTATS_ADD(isr, rx_rdys);
+	DEBUGFS_FWSTATS_ADD(isr, irqs);
+	DEBUGFS_FWSTATS_ADD(isr, tx_procs);
+	DEBUGFS_FWSTATS_ADD(isr, decrypt_done);
+	DEBUGFS_FWSTATS_ADD(isr, dma0_done);
+	DEBUGFS_FWSTATS_ADD(isr, dma1_done);
+	DEBUGFS_FWSTATS_ADD(isr, tx_exch_complete);
+	DEBUGFS_FWSTATS_ADD(isr, commands);
+	DEBUGFS_FWSTATS_ADD(isr, rx_procs);
+	DEBUGFS_FWSTATS_ADD(isr, hw_pm_mode_changes);
+	DEBUGFS_FWSTATS_ADD(isr, host_acknowledges);
+	DEBUGFS_FWSTATS_ADD(isr, pci_pm);
+	DEBUGFS_FWSTATS_ADD(isr, wakeups);
+	DEBUGFS_FWSTATS_ADD(isr, low_rssi);
+
+	DEBUGFS_FWSTATS_ADD(wep, addr_key_count);
+	DEBUGFS_FWSTATS_ADD(wep, default_key_count);
+	/* skipping wep.reserved */
+	DEBUGFS_FWSTATS_ADD(wep, key_not_found);
+	DEBUGFS_FWSTATS_ADD(wep, decrypt_fail);
+	DEBUGFS_FWSTATS_ADD(wep, packets);
+	DEBUGFS_FWSTATS_ADD(wep, interrupt);
+
+	DEBUGFS_FWSTATS_ADD(pwr, ps_enter);
+	DEBUGFS_FWSTATS_ADD(pwr, elp_enter);
+	DEBUGFS_FWSTATS_ADD(pwr, missing_bcns);
+	DEBUGFS_FWSTATS_ADD(pwr, wake_on_host);
+	DEBUGFS_FWSTATS_ADD(pwr, wake_on_timer_exp);
+	DEBUGFS_FWSTATS_ADD(pwr, tx_with_ps);
+	DEBUGFS_FWSTATS_ADD(pwr, tx_without_ps);
+	DEBUGFS_FWSTATS_ADD(pwr, rcvd_beacons);
+	DEBUGFS_FWSTATS_ADD(pwr, power_save_off);
+	DEBUGFS_FWSTATS_ADD(pwr, enable_ps);
+	DEBUGFS_FWSTATS_ADD(pwr, disable_ps);
+	DEBUGFS_FWSTATS_ADD(pwr, fix_tsf_ps);
+	/* skipping cont_miss_bcns_spread for now */
+	DEBUGFS_FWSTATS_ADD(pwr, rcvd_awake_beacons);
+
+	DEBUGFS_FWSTATS_ADD(mic, rx_pkts);
+	DEBUGFS_FWSTATS_ADD(mic, calc_failure);
+
+	DEBUGFS_FWSTATS_ADD(aes, encrypt_fail);
+	DEBUGFS_FWSTATS_ADD(aes, decrypt_fail);
+	DEBUGFS_FWSTATS_ADD(aes, encrypt_packets);
+	DEBUGFS_FWSTATS_ADD(aes, decrypt_packets);
+	DEBUGFS_FWSTATS_ADD(aes, encrypt_interrupt);
+	DEBUGFS_FWSTATS_ADD(aes, decrypt_interrupt);
+
+	DEBUGFS_FWSTATS_ADD(event, heart_beat);
+	DEBUGFS_FWSTATS_ADD(event, calibration);
+	DEBUGFS_FWSTATS_ADD(event, rx_mismatch);
+	DEBUGFS_FWSTATS_ADD(event, rx_mem_empty);
+	DEBUGFS_FWSTATS_ADD(event, rx_pool);
+	DEBUGFS_FWSTATS_ADD(event, oom_late);
+	DEBUGFS_FWSTATS_ADD(event, phy_transmit_error);
+	DEBUGFS_FWSTATS_ADD(event, tx_stuck);
+
+	DEBUGFS_FWSTATS_ADD(ps, pspoll_timeouts);
+	DEBUGFS_FWSTATS_ADD(ps, upsd_timeouts);
+	DEBUGFS_FWSTATS_ADD(ps, upsd_max_sptime);
+	DEBUGFS_FWSTATS_ADD(ps, upsd_max_apturn);
+	DEBUGFS_FWSTATS_ADD(ps, pspoll_max_apturn);
+	DEBUGFS_FWSTATS_ADD(ps, pspoll_utilization);
+	DEBUGFS_FWSTATS_ADD(ps, upsd_utilization);
+
+	DEBUGFS_FWSTATS_ADD(rxpipe, rx_prep_beacon_drop);
+	DEBUGFS_FWSTATS_ADD(rxpipe, descr_host_int_trig_rx_data);
+	DEBUGFS_FWSTATS_ADD(rxpipe, beacon_buffer_thres_host_int_trig_rx_data);
+	DEBUGFS_FWSTATS_ADD(rxpipe, missed_beacon_host_int_trig_rx_data);
+	DEBUGFS_FWSTATS_ADD(rxpipe, tx_xfr_host_int_trig_rx_data);
+
+	return 0;
+
+err:
+	if (IS_ERR(entry))
+		ret = PTR_ERR(entry);
+	else
+		ret = -ENOMEM;
+
+	return ret;
+}

+ 28 - 0
drivers/net/wireless/ti/wl12xx/debugfs.h

@@ -0,0 +1,28 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL12XX_DEBUGFS_H__
+#define __WL12XX_DEBUGFS_H__
+
+int wl12xx_debugfs_add_files(struct wl1271 *wl,
+			     struct dentry *rootdir);
+
+#endif /* __WL12XX_DEBUGFS_H__ */

+ 199 - 43
drivers/net/wireless/ti/wl12xx/main.c

@@ -39,6 +39,10 @@
 #include "reg.h"
 #include "cmd.h"
 #include "acx.h"
+#include "debugfs.h"
+
+static char *fref_param;
+static char *tcxo_param;
 
 static struct wlcore_conf wl12xx_conf = {
 	.sg = {
@@ -212,7 +216,7 @@ static struct wlcore_conf wl12xx_conf = {
 		.suspend_wake_up_event       = CONF_WAKE_UP_EVENT_N_DTIM,
 		.suspend_listen_interval     = 3,
 		.bcn_filt_mode               = CONF_BCN_FILT_MODE_ENABLED,
-		.bcn_filt_ie_count           = 2,
+		.bcn_filt_ie_count           = 3,
 		.bcn_filt_ie = {
 			[0] = {
 				.ie          = WLAN_EID_CHANNEL_SWITCH,
@@ -222,9 +226,13 @@ static struct wlcore_conf wl12xx_conf = {
 				.ie          = WLAN_EID_HT_OPERATION,
 				.rule        = CONF_BCN_RULE_PASS_ON_CHANGE,
 			},
+			[2] = {
+				.ie	     = WLAN_EID_ERP_INFO,
+				.rule	     = CONF_BCN_RULE_PASS_ON_CHANGE,
+			},
 		},
-		.synch_fail_thold            = 10,
-		.bss_lose_timeout            = 100,
+		.synch_fail_thold            = 12,
+		.bss_lose_timeout            = 400,
 		.beacon_rx_timeout           = 10000,
 		.broadcast_timeout           = 20000,
 		.rx_broadcast_in_ps          = 1,
@@ -234,7 +242,7 @@ static struct wlcore_conf wl12xx_conf = {
 		.psm_entry_retries           = 8,
 		.psm_exit_retries            = 16,
 		.psm_entry_nullfunc_retries  = 3,
-		.dynamic_ps_timeout          = 40,
+		.dynamic_ps_timeout          = 200,
 		.forced_ps                   = false,
 		.keep_alive_interval         = 55000,
 		.max_listen_interval         = 20,
@@ -245,7 +253,7 @@ static struct wlcore_conf wl12xx_conf = {
 	},
 	.pm_config = {
 		.host_clk_settling_time = 5000,
-		.host_fast_wakeup_support = false
+		.host_fast_wakeup_support = CONF_FAST_WAKEUP_DISABLE,
 	},
 	.roam_trigger = {
 		.trigger_pacing               = 1,
@@ -305,8 +313,8 @@ static struct wlcore_conf wl12xx_conf = {
 		.swallow_period               = 5,
 		.n_divider_fref_set_1         = 0xff,       /* default */
 		.n_divider_fref_set_2         = 12,
-		.m_divider_fref_set_1         = 148,
-		.m_divider_fref_set_2         = 0xffff,     /* default */
+		.m_divider_fref_set_1         = 0xffff,
+		.m_divider_fref_set_2         = 148,	    /* default */
 		.coex_pll_stabilization_time  = 0xffffffff, /* default */
 		.ldo_stabilization_time       = 0xffff,     /* default */
 		.fm_disturbed_band_margin     = 0xff,       /* default */
@@ -593,7 +601,7 @@ static void wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len)
 {
 	if (wl->chip.id != CHIP_ID_1283_PG20) {
 		struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map;
-		struct wl1271_rx_mem_pool_addr rx_mem_addr;
+		struct wl127x_rx_mem_pool_addr rx_mem_addr;
 
 		/*
 		 * Choose the block we want to read
@@ -621,10 +629,8 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
 		wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
 			       wl->chip.id);
 
-		/* clear the alignment quirk, since we don't support it */
-		wl->quirks &= ~WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN;
-
-		wl->quirks |= WLCORE_QUIRK_LEGACY_NVS;
+		wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |
+			      WLCORE_QUIRK_TKIP_HEADER_SPACE;
 		wl->sr_fw_name = WL127X_FW_NAME_SINGLE;
 		wl->mr_fw_name = WL127X_FW_NAME_MULTI;
 		memcpy(&wl->conf.mem, &wl12xx_default_priv_conf.mem_wl127x,
@@ -639,10 +645,8 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
 		wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
 			     wl->chip.id);
 
-		/* clear the alignment quirk, since we don't support it */
-		wl->quirks &= ~WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN;
-
-		wl->quirks |= WLCORE_QUIRK_LEGACY_NVS;
+		wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |
+			      WLCORE_QUIRK_TKIP_HEADER_SPACE;
 		wl->plt_fw_name = WL127X_PLT_FW_NAME;
 		wl->sr_fw_name = WL127X_FW_NAME_SINGLE;
 		wl->mr_fw_name = WL127X_FW_NAME_MULTI;
@@ -660,6 +664,11 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
 		wl->plt_fw_name = WL128X_PLT_FW_NAME;
 		wl->sr_fw_name = WL128X_FW_NAME_SINGLE;
 		wl->mr_fw_name = WL128X_FW_NAME_MULTI;
+
+		/* wl128x requires TX blocksize alignment */
+		wl->quirks |= WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN |
+			      WLCORE_QUIRK_TKIP_HEADER_SPACE;
+
 		break;
 	case CHIP_ID_1283_PG10:
 	default:
@@ -773,6 +782,7 @@ static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk)
 	u16 spare_reg;
 	u16 pll_config;
 	u8 input_freq;
+	struct wl12xx_priv *priv = wl->priv;
 
 	/* Mask bits [3:1] in the sys_clk_cfg register */
 	spare_reg = wl12xx_top_reg_read(wl, WL_SPARE_REG);
@@ -782,8 +792,8 @@ static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk)
 	wl12xx_top_reg_write(wl, WL_SPARE_REG, spare_reg);
 
 	/* Handle special cases of the TCXO clock */
-	if (wl->tcxo_clock == WL12XX_TCXOCLOCK_16_8 ||
-	    wl->tcxo_clock == WL12XX_TCXOCLOCK_33_6)
+	if (priv->tcxo_clock == WL12XX_TCXOCLOCK_16_8 ||
+	    priv->tcxo_clock == WL12XX_TCXOCLOCK_33_6)
 		return wl128x_manually_configure_mcs_pll(wl);
 
 	/* Set the input frequency according to the selected clock source */
@@ -808,11 +818,12 @@ static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk)
  */
 static int wl128x_boot_clk(struct wl1271 *wl, int *selected_clock)
 {
+	struct wl12xx_priv *priv = wl->priv;
 	u16 sys_clk_cfg;
 
 	/* For XTAL-only modes, FREF will be used after switching from TCXO */
-	if (wl->ref_clock == WL12XX_REFCLOCK_26_XTAL ||
-	    wl->ref_clock == WL12XX_REFCLOCK_38_XTAL) {
+	if (priv->ref_clock == WL12XX_REFCLOCK_26_XTAL ||
+	    priv->ref_clock == WL12XX_REFCLOCK_38_XTAL) {
 		if (!wl128x_switch_tcxo_to_fref(wl))
 			return -EINVAL;
 		goto fref_clk;
@@ -826,8 +837,8 @@ static int wl128x_boot_clk(struct wl1271 *wl, int *selected_clock)
 		goto fref_clk;
 
 	/* If TCXO is either 32.736MHz or 16.368MHz, switch to FREF */
-	if (wl->tcxo_clock == WL12XX_TCXOCLOCK_16_368 ||
-	    wl->tcxo_clock == WL12XX_TCXOCLOCK_32_736) {
+	if (priv->tcxo_clock == WL12XX_TCXOCLOCK_16_368 ||
+	    priv->tcxo_clock == WL12XX_TCXOCLOCK_32_736) {
 		if (!wl128x_switch_tcxo_to_fref(wl))
 			return -EINVAL;
 		goto fref_clk;
@@ -836,14 +847,14 @@ static int wl128x_boot_clk(struct wl1271 *wl, int *selected_clock)
 	/* TCXO clock is selected */
 	if (!wl128x_is_tcxo_valid(wl))
 		return -EINVAL;
-	*selected_clock = wl->tcxo_clock;
+	*selected_clock = priv->tcxo_clock;
 	goto config_mcs_pll;
 
 fref_clk:
 	/* FREF clock is selected */
 	if (!wl128x_is_fref_valid(wl))
 		return -EINVAL;
-	*selected_clock = wl->ref_clock;
+	*selected_clock = priv->ref_clock;
 
 config_mcs_pll:
 	return wl128x_configure_mcs_pll(wl, *selected_clock);
@@ -851,25 +862,27 @@ config_mcs_pll:
 
 static int wl127x_boot_clk(struct wl1271 *wl)
 {
+	struct wl12xx_priv *priv = wl->priv;
 	u32 pause;
 	u32 clk;
 
 	if (WL127X_PG_GET_MAJOR(wl->hw_pg_ver) < 3)
 		wl->quirks |= WLCORE_QUIRK_END_OF_TRANSACTION;
 
-	if (wl->ref_clock == CONF_REF_CLK_19_2_E ||
-	    wl->ref_clock == CONF_REF_CLK_38_4_E ||
-	    wl->ref_clock == CONF_REF_CLK_38_4_M_XTAL)
+	if (priv->ref_clock == CONF_REF_CLK_19_2_E ||
+	    priv->ref_clock == CONF_REF_CLK_38_4_E ||
+	    priv->ref_clock == CONF_REF_CLK_38_4_M_XTAL)
 		/* ref clk: 19.2/38.4/38.4-XTAL */
 		clk = 0x3;
-	else if (wl->ref_clock == CONF_REF_CLK_26_E ||
-		 wl->ref_clock == CONF_REF_CLK_52_E)
+	else if (priv->ref_clock == CONF_REF_CLK_26_E ||
+		 priv->ref_clock == CONF_REF_CLK_26_M_XTAL ||
+		 priv->ref_clock == CONF_REF_CLK_52_E)
 		/* ref clk: 26/52 */
 		clk = 0x5;
 	else
 		return -EINVAL;
 
-	if (wl->ref_clock != CONF_REF_CLK_19_2_E) {
+	if (priv->ref_clock != CONF_REF_CLK_19_2_E) {
 		u16 val;
 		/* Set clock type (open drain) */
 		val = wl12xx_top_reg_read(wl, OCP_REG_CLK_TYPE);
@@ -939,6 +952,7 @@ static int wl1271_boot_soft_reset(struct wl1271 *wl)
 
 static int wl12xx_pre_boot(struct wl1271 *wl)
 {
+	struct wl12xx_priv *priv = wl->priv;
 	int ret = 0;
 	u32 clk;
 	int selected_clock = -1;
@@ -970,7 +984,7 @@ static int wl12xx_pre_boot(struct wl1271 *wl)
 	if (wl->chip.id == CHIP_ID_1283_PG20)
 		clk |= ((selected_clock & 0x3) << 1) << 4;
 	else
-		clk |= (wl->ref_clock << 1) << 4;
+		clk |= (priv->ref_clock << 1) << 4;
 
 	wl1271_write32(wl, WL12XX_DRPW_SCRATCH_START, clk);
 
@@ -989,7 +1003,7 @@ out:
 
 static void wl12xx_pre_upload(struct wl1271 *wl)
 {
-	u32 tmp;
+	u32 tmp, polarity;
 
 	/* write firmware's last address (ie. it's length) to
 	 * ACX_EEPROMLESS_IND_REG */
@@ -1009,23 +1023,23 @@ static void wl12xx_pre_upload(struct wl1271 *wl)
 
 	if (wl->chip.id == CHIP_ID_1283_PG20)
 		wl12xx_top_reg_write(wl, SDIO_IO_DS, HCI_IO_DS_6MA);
-}
-
-static void wl12xx_enable_interrupts(struct wl1271 *wl)
-{
-	u32 polarity;
 
+	/* polarity must be set before the firmware is loaded */
 	polarity = wl12xx_top_reg_read(wl, OCP_REG_POLARITY);
 
 	/* We use HIGH polarity, so unset the LOW bit */
 	polarity &= ~POLARITY_LOW;
 	wl12xx_top_reg_write(wl, OCP_REG_POLARITY, polarity);
 
-	wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_ALL_EVENTS_VECTOR);
+}
+
+static void wl12xx_enable_interrupts(struct wl1271 *wl)
+{
+	wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL12XX_ACX_ALL_EVENTS_VECTOR);
 
 	wlcore_enable_interrupts(wl);
 	wlcore_write_reg(wl, REG_INTERRUPT_MASK,
-			 WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK));
+			 WL1271_ACX_INTR_ALL & ~(WL12XX_INTR_MASK));
 
 	wl1271_write32(wl, WL12XX_HI_CFG, HI_CFG_DEF_VAL);
 }
@@ -1149,7 +1163,8 @@ static u32 wl12xx_get_rx_packet_len(struct wl1271 *wl, void *rx_data,
 
 static void wl12xx_tx_delayed_compl(struct wl1271 *wl)
 {
-	if (wl->fw_status->tx_results_counter == (wl->tx_results_count & 0xff))
+	if (wl->fw_status_1->tx_results_counter ==
+	    (wl->tx_results_count & 0xff))
 		return;
 
 	wl1271_tx_complete(wl);
@@ -1288,10 +1303,90 @@ static void wl12xx_get_mac(struct wl1271 *wl)
 		wl12xx_get_fuse_mac(wl);
 }
 
+static void wl12xx_set_tx_desc_csum(struct wl1271 *wl,
+				    struct wl1271_tx_hw_descr *desc,
+				    struct sk_buff *skb)
+{
+	desc->wl12xx_reserved = 0;
+}
+
+static int wl12xx_plt_init(struct wl1271 *wl)
+{
+	int ret;
+
+	ret = wl->ops->boot(wl);
+	if (ret < 0)
+		goto out;
+
+	ret = wl->ops->hw_init(wl);
+	if (ret < 0)
+		goto out_irq_disable;
+
+	ret = wl1271_acx_init_mem_config(wl);
+	if (ret < 0)
+		goto out_irq_disable;
+
+	ret = wl12xx_acx_mem_cfg(wl);
+	if (ret < 0)
+		goto out_free_memmap;
+
+	/* Enable data path */
+	ret = wl1271_cmd_data_path(wl, 1);
+	if (ret < 0)
+		goto out_free_memmap;
+
+	/* Configure for CAM power saving (ie. always active) */
+	ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
+	if (ret < 0)
+		goto out_free_memmap;
+
+	/* configure PM */
+	ret = wl1271_acx_pm_config(wl);
+	if (ret < 0)
+		goto out_free_memmap;
+
+	goto out;
+
+out_free_memmap:
+	kfree(wl->target_mem_map);
+	wl->target_mem_map = NULL;
+
+out_irq_disable:
+	mutex_unlock(&wl->mutex);
+	/* Unlocking the mutex in the middle of handling is
+	   inherently unsafe. In this case we deem it safe to do,
+	   because we need to let any possibly pending IRQ out of
+	   the system (and while we are WL1271_STATE_OFF the IRQ
+	   work function will not do anything.) Also, any other
+	   possible concurrent operations will fail due to the
+	   current state, hence the wl1271 struct should be safe. */
+	wlcore_disable_interrupts(wl);
+	mutex_lock(&wl->mutex);
+out:
+	return ret;
+}
+
+static int wl12xx_get_spare_blocks(struct wl1271 *wl, bool is_gem)
+{
+	if (is_gem)
+		return WL12XX_TX_HW_BLOCK_GEM_SPARE;
+
+	return WL12XX_TX_HW_BLOCK_SPARE_DEFAULT;
+}
+
+static int wl12xx_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
+			  struct ieee80211_vif *vif,
+			  struct ieee80211_sta *sta,
+			  struct ieee80211_key_conf *key_conf)
+{
+	return wlcore_set_key(wl, cmd, vif, sta, key_conf);
+}
+
 static struct wlcore_ops wl12xx_ops = {
 	.identify_chip		= wl12xx_identify_chip,
 	.identify_fw		= wl12xx_identify_fw,
 	.boot			= wl12xx_boot,
+	.plt_init		= wl12xx_plt_init,
 	.trigger_cmd		= wl12xx_trigger_cmd,
 	.ack_event		= wl12xx_ack_event,
 	.calc_tx_blocks		= wl12xx_calc_tx_blocks,
@@ -1306,6 +1401,13 @@ static struct wlcore_ops wl12xx_ops = {
 	.sta_get_ap_rate_mask	= wl12xx_sta_get_ap_rate_mask,
 	.get_pg_ver		= wl12xx_get_pg_ver,
 	.get_mac		= wl12xx_get_mac,
+	.set_tx_desc_csum	= wl12xx_set_tx_desc_csum,
+	.set_rx_csum		= NULL,
+	.ap_get_mimo_wide_rate_mask = NULL,
+	.debugfs_init		= wl12xx_debugfs_add_files,
+	.get_spare_blocks	= wl12xx_get_spare_blocks,
+	.set_key		= wl12xx_set_key,
+	.pre_pkt_send		= NULL,
 };
 
 static struct ieee80211_sta_ht_cap wl12xx_ht_cap = {
@@ -1323,6 +1425,7 @@ static struct ieee80211_sta_ht_cap wl12xx_ht_cap = {
 
 static int __devinit wl12xx_probe(struct platform_device *pdev)
 {
+	struct wl12xx_platform_data *pdata = pdev->dev.platform_data;
 	struct wl1271 *wl;
 	struct ieee80211_hw *hw;
 	struct wl12xx_priv *priv;
@@ -1334,19 +1437,65 @@ static int __devinit wl12xx_probe(struct platform_device *pdev)
 	}
 
 	wl = hw->priv;
+	priv = wl->priv;
 	wl->ops = &wl12xx_ops;
 	wl->ptable = wl12xx_ptable;
 	wl->rtable = wl12xx_rtable;
 	wl->num_tx_desc = 16;
-	wl->normal_tx_spare = WL12XX_TX_HW_BLOCK_SPARE_DEFAULT;
-	wl->gem_tx_spare = WL12XX_TX_HW_BLOCK_GEM_SPARE;
+	wl->num_rx_desc = 8;
 	wl->band_rate_to_idx = wl12xx_band_rate_to_idx;
 	wl->hw_tx_rate_tbl_size = WL12XX_CONF_HW_RXTX_RATE_MAX;
 	wl->hw_min_ht_rate = WL12XX_CONF_HW_RXTX_RATE_MCS0;
 	wl->fw_status_priv_len = 0;
-	memcpy(&wl->ht_cap, &wl12xx_ht_cap, sizeof(wl12xx_ht_cap));
+	wl->stats.fw_stats_len = sizeof(struct wl12xx_acx_statistics);
+	memcpy(&wl->ht_cap[IEEE80211_BAND_2GHZ], &wl12xx_ht_cap,
+	       sizeof(wl12xx_ht_cap));
+	memcpy(&wl->ht_cap[IEEE80211_BAND_5GHZ], &wl12xx_ht_cap,
+	       sizeof(wl12xx_ht_cap));
 	wl12xx_conf_init(wl);
 
+	if (!fref_param) {
+		priv->ref_clock = pdata->board_ref_clock;
+	} else {
+		if (!strcmp(fref_param, "19.2"))
+			priv->ref_clock = WL12XX_REFCLOCK_19;
+		else if (!strcmp(fref_param, "26"))
+			priv->ref_clock = WL12XX_REFCLOCK_26;
+		else if (!strcmp(fref_param, "26x"))
+			priv->ref_clock = WL12XX_REFCLOCK_26_XTAL;
+		else if (!strcmp(fref_param, "38.4"))
+			priv->ref_clock = WL12XX_REFCLOCK_38;
+		else if (!strcmp(fref_param, "38.4x"))
+			priv->ref_clock = WL12XX_REFCLOCK_38_XTAL;
+		else if (!strcmp(fref_param, "52"))
+			priv->ref_clock = WL12XX_REFCLOCK_52;
+		else
+			wl1271_error("Invalid fref parameter %s", fref_param);
+	}
+
+	if (!tcxo_param) {
+		priv->tcxo_clock = pdata->board_tcxo_clock;
+	} else {
+		if (!strcmp(tcxo_param, "19.2"))
+			priv->tcxo_clock = WL12XX_TCXOCLOCK_19_2;
+		else if (!strcmp(tcxo_param, "26"))
+			priv->tcxo_clock = WL12XX_TCXOCLOCK_26;
+		else if (!strcmp(tcxo_param, "38.4"))
+			priv->tcxo_clock = WL12XX_TCXOCLOCK_38_4;
+		else if (!strcmp(tcxo_param, "52"))
+			priv->tcxo_clock = WL12XX_TCXOCLOCK_52;
+		else if (!strcmp(tcxo_param, "16.368"))
+			priv->tcxo_clock = WL12XX_TCXOCLOCK_16_368;
+		else if (!strcmp(tcxo_param, "32.736"))
+			priv->tcxo_clock = WL12XX_TCXOCLOCK_32_736;
+		else if (!strcmp(tcxo_param, "16.8"))
+			priv->tcxo_clock = WL12XX_TCXOCLOCK_16_8;
+		else if (!strcmp(tcxo_param, "33.6"))
+			priv->tcxo_clock = WL12XX_TCXOCLOCK_33_6;
+		else
+			wl1271_error("Invalid tcxo parameter %s", tcxo_param);
+	}
+
 	return wlcore_probe(wl, pdev);
 }
 
@@ -1378,6 +1527,13 @@ static void __exit wl12xx_exit(void)
 }
 module_exit(wl12xx_exit);
 
+module_param_named(fref, fref_param, charp, 0);
+MODULE_PARM_DESC(fref, "FREF clock: 19.2, 26, 26x, 38.4, 38.4x, 52");
+
+module_param_named(tcxo, tcxo_param, charp, 0);
+MODULE_PARM_DESC(tcxo,
+		 "TCXO clock: 19.2, 26, 38.4, 52, 16.368, 32.736, 16.8, 33.6");
+
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
 MODULE_FIRMWARE(WL127X_FW_NAME_SINGLE);

+ 8 - 0
drivers/net/wireless/ti/wl12xx/wl12xx.h

@@ -24,8 +24,16 @@
 
 #include "conf.h"
 
+struct wl127x_rx_mem_pool_addr {
+	u32 addr;
+	u32 addr_extra;
+};
+
 struct wl12xx_priv {
 	struct wl12xx_priv_conf conf;
+
+	int ref_clock;
+	int tcxo_clock;
 };
 
 #endif /* __WL12XX_PRIV_H__ */

+ 7 - 0
drivers/net/wireless/ti/wl18xx/Kconfig

@@ -0,0 +1,7 @@
+config WL18XX
+	tristate "TI wl18xx support"
+	depends on MAC80211
+	select WLCORE
+	---help---
+	  This module adds support for wireless adapters based on TI
+	  WiLink 8 chipsets.

+ 3 - 0
drivers/net/wireless/ti/wl18xx/Makefile

@@ -0,0 +1,3 @@
+wl18xx-objs	= main.o acx.o tx.o io.o debugfs.o
+
+obj-$(CONFIG_WL18XX)		+= wl18xx.o

+ 111 - 0
drivers/net/wireless/ti/wl18xx/acx.c

@@ -0,0 +1,111 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2011 Texas Instruments Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "../wlcore/cmd.h"
+#include "../wlcore/debug.h"
+#include "../wlcore/acx.h"
+
+#include "acx.h"
+
+int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap,
+				  u32 sdio_blk_size, u32 extra_mem_blks,
+				  u32 len_field_size)
+{
+	struct wl18xx_acx_host_config_bitmap *bitmap_conf;
+	int ret;
+
+	wl1271_debug(DEBUG_ACX, "acx cfg bitmap %d blk %d spare %d field %d",
+		     host_cfg_bitmap, sdio_blk_size, extra_mem_blks,
+		     len_field_size);
+
+	bitmap_conf = kzalloc(sizeof(*bitmap_conf), GFP_KERNEL);
+	if (!bitmap_conf) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	bitmap_conf->host_cfg_bitmap = cpu_to_le32(host_cfg_bitmap);
+	bitmap_conf->host_sdio_block_size = cpu_to_le32(sdio_blk_size);
+	bitmap_conf->extra_mem_blocks = cpu_to_le32(extra_mem_blks);
+	bitmap_conf->length_field_size = cpu_to_le32(len_field_size);
+
+	ret = wl1271_cmd_configure(wl, ACX_HOST_IF_CFG_BITMAP,
+				   bitmap_conf, sizeof(*bitmap_conf));
+	if (ret < 0) {
+		wl1271_warning("wl1271 bitmap config opt failed: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(bitmap_conf);
+
+	return ret;
+}
+
+int wl18xx_acx_set_checksum_state(struct wl1271 *wl)
+{
+	struct wl18xx_acx_checksum_state *acx;
+	int ret;
+
+	wl1271_debug(DEBUG_ACX, "acx checksum state");
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	acx->checksum_state = CHECKSUM_OFFLOAD_ENABLED;
+
+	ret = wl1271_cmd_configure(wl, ACX_CHECKSUM_CONFIG, acx, sizeof(*acx));
+	if (ret < 0) {
+		wl1271_warning("failed to set Tx checksum state: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}
+
+int wl18xx_acx_clear_statistics(struct wl1271 *wl)
+{
+	struct wl18xx_acx_clear_statistics *acx;
+	int ret = 0;
+
+	wl1271_debug(DEBUG_ACX, "acx clear statistics");
+
+	acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+	if (!acx) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ret = wl1271_cmd_configure(wl, ACX_CLEAR_STATISTICS, acx, sizeof(*acx));
+	if (ret < 0) {
+		wl1271_warning("failed to clear firmware statistics: %d", ret);
+		goto out;
+	}
+
+out:
+	kfree(acx);
+	return ret;
+}

+ 291 - 0
drivers/net/wireless/ti/wl18xx/acx.h

@@ -0,0 +1,291 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2011 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL18XX_ACX_H__
+#define __WL18XX_ACX_H__
+
+#include "../wlcore/wlcore.h"
+#include "../wlcore/acx.h"
+
+enum {
+	ACX_CLEAR_STATISTICS		 = 0x0047,
+};
+
+/* numbers of bits the length field takes (add 1 for the actual number) */
+#define WL18XX_HOST_IF_LEN_SIZE_FIELD 15
+
+#define WL18XX_ACX_EVENTS_VECTOR_PG1	(WL1271_ACX_INTR_WATCHDOG      | \
+					WL1271_ACX_INTR_INIT_COMPLETE | \
+					WL1271_ACX_INTR_EVENT_A       | \
+					WL1271_ACX_INTR_EVENT_B       | \
+					WL1271_ACX_INTR_CMD_COMPLETE  | \
+					WL1271_ACX_INTR_HW_AVAILABLE  | \
+					WL1271_ACX_INTR_DATA)
+
+#define WL18XX_ACX_EVENTS_VECTOR_PG2	(WL18XX_ACX_EVENTS_VECTOR_PG1 | \
+					WL1271_ACX_SW_INTR_WATCHDOG)
+
+#define WL18XX_INTR_MASK_PG1		(WL1271_ACX_INTR_WATCHDOG      | \
+					WL1271_ACX_INTR_EVENT_A       | \
+					WL1271_ACX_INTR_EVENT_B       | \
+					WL1271_ACX_INTR_HW_AVAILABLE  | \
+					WL1271_ACX_INTR_DATA)
+
+#define WL18XX_INTR_MASK_PG2		(WL18XX_INTR_MASK_PG1         | \
+					WL1271_ACX_SW_INTR_WATCHDOG)
+
+struct wl18xx_acx_host_config_bitmap {
+	struct acx_header header;
+
+	__le32 host_cfg_bitmap;
+
+	__le32 host_sdio_block_size;
+
+	/* extra mem blocks per frame in TX. */
+	__le32 extra_mem_blocks;
+
+	/*
+	 * number of bits of the length field in the first TX word
+	 * (up to 15 - for using the entire 16 bits).
+	 */
+	__le32 length_field_size;
+
+} __packed;
+
+enum {
+	CHECKSUM_OFFLOAD_DISABLED = 0,
+	CHECKSUM_OFFLOAD_ENABLED  = 1,
+	CHECKSUM_OFFLOAD_FAKE_RX  = 2,
+	CHECKSUM_OFFLOAD_INVALID  = 0xFF
+};
+
+struct wl18xx_acx_checksum_state {
+	struct acx_header header;
+
+	 /* enum acx_checksum_state */
+	u8 checksum_state;
+	u8 pad[3];
+} __packed;
+
+
+struct wl18xx_acx_error_stats {
+	u32 error_frame;
+	u32 error_null_Frame_tx_start;
+	u32 error_numll_frame_cts_start;
+	u32 error_bar_retry;
+	u32 error_frame_cts_nul_flid;
+} __packed;
+
+struct wl18xx_acx_debug_stats {
+	u32 debug1;
+	u32 debug2;
+	u32 debug3;
+	u32 debug4;
+	u32 debug5;
+	u32 debug6;
+} __packed;
+
+struct wl18xx_acx_ring_stats {
+	u32 prepared_descs;
+	u32 tx_cmplt;
+} __packed;
+
+struct wl18xx_acx_tx_stats {
+	u32 tx_prepared_descs;
+	u32 tx_cmplt;
+	u32 tx_template_prepared;
+	u32 tx_data_prepared;
+	u32 tx_template_programmed;
+	u32 tx_data_programmed;
+	u32 tx_burst_programmed;
+	u32 tx_starts;
+	u32 tx_imm_resp;
+	u32 tx_start_templates;
+	u32 tx_start_int_templates;
+	u32 tx_start_fw_gen;
+	u32 tx_start_data;
+	u32 tx_start_null_frame;
+	u32 tx_exch;
+	u32 tx_retry_template;
+	u32 tx_retry_data;
+	u32 tx_exch_pending;
+	u32 tx_exch_expiry;
+	u32 tx_done_template;
+	u32 tx_done_data;
+	u32 tx_done_int_template;
+	u32 tx_frame_checksum;
+	u32 tx_checksum_result;
+	u32 frag_called;
+	u32 frag_mpdu_alloc_failed;
+	u32 frag_init_called;
+	u32 frag_in_process_called;
+	u32 frag_tkip_called;
+	u32 frag_key_not_found;
+	u32 frag_need_fragmentation;
+	u32 frag_bad_mblk_num;
+	u32 frag_failed;
+	u32 frag_cache_hit;
+	u32 frag_cache_miss;
+} __packed;
+
+struct wl18xx_acx_rx_stats {
+	u32 rx_beacon_early_term;
+	u32 rx_out_of_mpdu_nodes;
+	u32 rx_hdr_overflow;
+	u32 rx_dropped_frame;
+	u32 rx_done_stage;
+	u32 rx_done;
+	u32 rx_defrag;
+	u32 rx_defrag_end;
+	u32 rx_cmplt;
+	u32 rx_pre_complt;
+	u32 rx_cmplt_task;
+	u32 rx_phy_hdr;
+	u32 rx_timeout;
+	u32 rx_timeout_wa;
+	u32 rx_wa_density_dropped_frame;
+	u32 rx_wa_ba_not_expected;
+	u32 rx_frame_checksum;
+	u32 rx_checksum_result;
+	u32 defrag_called;
+	u32 defrag_init_called;
+	u32 defrag_in_process_called;
+	u32 defrag_tkip_called;
+	u32 defrag_need_defrag;
+	u32 defrag_decrypt_failed;
+	u32 decrypt_key_not_found;
+	u32 defrag_need_decrypt;
+	u32 rx_tkip_replays;
+} __packed;
+
+struct wl18xx_acx_isr_stats {
+	u32 irqs;
+} __packed;
+
+#define PWR_STAT_MAX_CONT_MISSED_BCNS_SPREAD 10
+
+struct wl18xx_acx_pwr_stats {
+	u32 missing_bcns_cnt;
+	u32 rcvd_bcns_cnt;
+	u32 connection_out_of_sync;
+	u32 cont_miss_bcns_spread[PWR_STAT_MAX_CONT_MISSED_BCNS_SPREAD];
+	u32 rcvd_awake_bcns_cnt;
+} __packed;
+
+struct wl18xx_acx_event_stats {
+	u32 calibration;
+	u32 rx_mismatch;
+	u32 rx_mem_empty;
+} __packed;
+
+struct wl18xx_acx_ps_poll_stats {
+	u32 ps_poll_timeouts;
+	u32 upsd_timeouts;
+	u32 upsd_max_ap_turn;
+	u32 ps_poll_max_ap_turn;
+	u32 ps_poll_utilization;
+	u32 upsd_utilization;
+} __packed;
+
+struct wl18xx_acx_rx_filter_stats {
+	u32 beacon_filter;
+	u32 arp_filter;
+	u32 mc_filter;
+	u32 dup_filter;
+	u32 data_filter;
+	u32 ibss_filter;
+	u32 protection_filter;
+	u32 accum_arp_pend_requests;
+	u32 max_arp_queue_dep;
+} __packed;
+
+struct wl18xx_acx_rx_rate_stats {
+	u32 rx_frames_per_rates[50];
+} __packed;
+
+#define AGGR_STATS_TX_AGG	16
+#define AGGR_STATS_TX_RATE	16
+#define AGGR_STATS_RX_SIZE_LEN	16
+
+struct wl18xx_acx_aggr_stats {
+	u32 tx_agg_vs_rate[AGGR_STATS_TX_AGG * AGGR_STATS_TX_RATE];
+	u32 rx_size[AGGR_STATS_RX_SIZE_LEN];
+} __packed;
+
+#define PIPE_STATS_HW_FIFO	11
+
+struct wl18xx_acx_pipeline_stats {
+	u32 hs_tx_stat_fifo_int;
+	u32 hs_rx_stat_fifo_int;
+	u32 tcp_tx_stat_fifo_int;
+	u32 tcp_rx_stat_fifo_int;
+	u32 enc_tx_stat_fifo_int;
+	u32 enc_rx_stat_fifo_int;
+	u32 rx_complete_stat_fifo_int;
+	u32 pre_proc_swi;
+	u32 post_proc_swi;
+	u32 sec_frag_swi;
+	u32 pre_to_defrag_swi;
+	u32 defrag_to_csum_swi;
+	u32 csum_to_rx_xfer_swi;
+	u32 dec_packet_in;
+	u32 dec_packet_in_fifo_full;
+	u32 dec_packet_out;
+	u32 cs_rx_packet_in;
+	u32 cs_rx_packet_out;
+	u16 pipeline_fifo_full[PIPE_STATS_HW_FIFO];
+} __packed;
+
+struct wl18xx_acx_mem_stats {
+	u32 rx_free_mem_blks;
+	u32 tx_free_mem_blks;
+	u32 fwlog_free_mem_blks;
+	u32 fw_gen_free_mem_blks;
+} __packed;
+
+struct wl18xx_acx_statistics {
+	struct acx_header header;
+
+	struct wl18xx_acx_error_stats		error;
+	struct wl18xx_acx_debug_stats		debug;
+	struct wl18xx_acx_tx_stats		tx;
+	struct wl18xx_acx_rx_stats		rx;
+	struct wl18xx_acx_isr_stats		isr;
+	struct wl18xx_acx_pwr_stats		pwr;
+	struct wl18xx_acx_ps_poll_stats		ps_poll;
+	struct wl18xx_acx_rx_filter_stats	rx_filter;
+	struct wl18xx_acx_rx_rate_stats		rx_rate;
+	struct wl18xx_acx_aggr_stats		aggr_size;
+	struct wl18xx_acx_pipeline_stats	pipeline;
+	struct wl18xx_acx_mem_stats		mem;
+} __packed;
+
+struct wl18xx_acx_clear_statistics {
+	struct acx_header header;
+};
+
+int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap,
+				  u32 sdio_blk_size, u32 extra_mem_blks,
+				  u32 len_field_size);
+int wl18xx_acx_set_checksum_state(struct wl1271 *wl);
+int wl18xx_acx_clear_statistics(struct wl1271 *wl);
+
+#endif /* __WL18XX_ACX_H__ */

+ 92 - 0
drivers/net/wireless/ti/wl18xx/conf.h

@@ -0,0 +1,92 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2011 Texas Instruments Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL18XX_CONF_H__
+#define __WL18XX_CONF_H__
+
+#define WL18XX_CONF_MAGIC	0x10e100ca
+#define WL18XX_CONF_VERSION	(WLCORE_CONF_VERSION | 0x0002)
+#define WL18XX_CONF_MASK	0x0000ffff
+#define WL18XX_CONF_SIZE	(WLCORE_CONF_SIZE + \
+				 sizeof(struct wl18xx_priv_conf))
+
+#define NUM_OF_CHANNELS_11_ABG 150
+#define NUM_OF_CHANNELS_11_P 7
+#define WL18XX_NUM_OF_SUB_BANDS 9
+#define SRF_TABLE_LEN 16
+#define PIN_MUXING_SIZE 2
+
+struct wl18xx_mac_and_phy_params {
+	u8 phy_standalone;
+	u8 rdl;
+	u8 enable_clpc;
+	u8 enable_tx_low_pwr_on_siso_rdl;
+	u8 auto_detect;
+	u8 dedicated_fem;
+
+	u8 low_band_component;
+
+	/* Bit 0: One Hot, Bit 1: Control Enable, Bit 2: 1.8V, Bit 3: 3V */
+	u8 low_band_component_type;
+
+	u8 high_band_component;
+
+	/* Bit 0: One Hot, Bit 1: Control Enable, Bit 2: 1.8V, Bit 3: 3V */
+	u8 high_band_component_type;
+	u8 number_of_assembled_ant2_4;
+	u8 number_of_assembled_ant5;
+	u8 pin_muxing_platform_options[PIN_MUXING_SIZE];
+	u8 external_pa_dc2dc;
+	u8 tcxo_ldo_voltage;
+	u8 xtal_itrim_val;
+	u8 srf_state;
+	u8 srf1[SRF_TABLE_LEN];
+	u8 srf2[SRF_TABLE_LEN];
+	u8 srf3[SRF_TABLE_LEN];
+	u8 io_configuration;
+	u8 sdio_configuration;
+	u8 settings;
+	u8 rx_profile;
+	u8 per_chan_pwr_limit_arr_11abg[NUM_OF_CHANNELS_11_ABG];
+	u8 pwr_limit_reference_11_abg;
+	u8 per_chan_pwr_limit_arr_11p[NUM_OF_CHANNELS_11_P];
+	u8 pwr_limit_reference_11p;
+	u8 per_sub_band_tx_trace_loss[WL18XX_NUM_OF_SUB_BANDS];
+	u8 per_sub_band_rx_trace_loss[WL18XX_NUM_OF_SUB_BANDS];
+	u8 primary_clock_setting_time;
+	u8 clock_valid_on_wake_up;
+	u8 secondary_clock_setting_time;
+	u8 board_type;
+	/* enable point saturation */
+	u8 psat;
+	/* low/medium/high Tx power in dBm */
+	s8 low_power_val;
+	s8 med_power_val;
+	s8 high_power_val;
+	u8 padding[1];
+} __packed;
+
+struct wl18xx_priv_conf {
+	/* this structure is copied wholesale to FW */
+	struct wl18xx_mac_and_phy_params phy;
+} __packed;
+
+#endif /* __WL18XX_CONF_H__ */

+ 403 - 0
drivers/net/wireless/ti/wl18xx/debugfs.c

@@ -0,0 +1,403 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Copyright (C) 2011-2012 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "../wlcore/debugfs.h"
+#include "../wlcore/wlcore.h"
+
+#include "wl18xx.h"
+#include "acx.h"
+#include "debugfs.h"
+
+#define WL18XX_DEBUGFS_FWSTATS_FILE(a, b, c) \
+	DEBUGFS_FWSTATS_FILE(a, b, c, wl18xx_acx_statistics)
+#define WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(a, b, c) \
+	DEBUGFS_FWSTATS_FILE_ARRAY(a, b, c, wl18xx_acx_statistics)
+
+
+WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug1, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug2, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug3, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug4, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug5, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug6, "%u");
+
+WL18XX_DEBUGFS_FWSTATS_FILE(error, error_frame, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(error, error_null_Frame_tx_start, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(error, error_numll_frame_cts_start, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(error, error_bar_retry, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(error, error_frame_cts_nul_flid, "%u");
+
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_prepared_descs, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_cmplt, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_template_prepared, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_data_prepared, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_template_programmed, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_data_programmed, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_burst_programmed, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_starts, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_imm_resp, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_templates, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_int_templates, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_fw_gen, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_data, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_null_frame, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_exch, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_retry_template, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_retry_data, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_exch_pending, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_exch_expiry, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_done_template, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_done_data, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_done_int_template, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_frame_checksum, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_checksum_result, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_called, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_mpdu_alloc_failed, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_init_called, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_in_process_called, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_tkip_called, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_key_not_found, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_need_fragmentation, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_bad_mblk_num, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_failed, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_cache_hit, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_cache_miss, "%u");
+
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_beacon_early_term, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_out_of_mpdu_nodes, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_hdr_overflow, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_dropped_frame, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_done, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_defrag, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_defrag_end, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_cmplt, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_pre_complt, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_cmplt_task, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_phy_hdr, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_timeout, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_timeout_wa, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_wa_density_dropped_frame, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_wa_ba_not_expected, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_frame_checksum, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_checksum_result, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_called, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_init_called, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_in_process_called, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_tkip_called, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_need_defrag, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_decrypt_failed, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, decrypt_key_not_found, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_need_decrypt, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_tkip_replays, "%u");
+
+WL18XX_DEBUGFS_FWSTATS_FILE(isr, irqs, "%u");
+
+WL18XX_DEBUGFS_FWSTATS_FILE(pwr, missing_bcns_cnt, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_bcns_cnt, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pwr, connection_out_of_sync, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(pwr, cont_miss_bcns_spread,
+				  PWR_STAT_MAX_CONT_MISSED_BCNS_SPREAD);
+WL18XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_awake_bcns_cnt, "%u");
+
+
+WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, ps_poll_timeouts, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, upsd_timeouts, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, upsd_max_ap_turn, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, ps_poll_max_ap_turn, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, ps_poll_utilization, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, upsd_utilization, "%u");
+
+WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, beacon_filter, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, arp_filter, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, mc_filter, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, dup_filter, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, data_filter, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, ibss_filter, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, protection_filter, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, accum_arp_pend_requests, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, max_arp_queue_dep, "%u");
+
+WL18XX_DEBUGFS_FWSTATS_FILE(rx_rate, rx_frames_per_rates, "%u");
+
+WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(aggr_size, tx_agg_vs_rate,
+				  AGGR_STATS_TX_AGG*AGGR_STATS_TX_RATE);
+WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(aggr_size, rx_size,
+				  AGGR_STATS_RX_SIZE_LEN);
+
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, hs_tx_stat_fifo_int, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, tcp_tx_stat_fifo_int, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, tcp_rx_stat_fifo_int, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, enc_tx_stat_fifo_int, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, enc_rx_stat_fifo_int, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, rx_complete_stat_fifo_int, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, pre_proc_swi, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, post_proc_swi, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, sec_frag_swi, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, pre_to_defrag_swi, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, defrag_to_csum_swi, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, csum_to_rx_xfer_swi, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, dec_packet_in, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, dec_packet_in_fifo_full, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, dec_packet_out, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, cs_rx_packet_in, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, cs_rx_packet_out, "%u");
+
+WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(pipeline, pipeline_fifo_full,
+				  PIPE_STATS_HW_FIFO);
+
+WL18XX_DEBUGFS_FWSTATS_FILE(mem, rx_free_mem_blks, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(mem, tx_free_mem_blks, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(mem, fwlog_free_mem_blks, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(mem, fw_gen_free_mem_blks, "%u");
+
+static ssize_t conf_read(struct file *file, char __user *user_buf,
+			 size_t count, loff_t *ppos)
+{
+	struct wl1271 *wl = file->private_data;
+	struct wl18xx_priv *priv = wl->priv;
+	struct wlcore_conf_header header;
+	char *buf, *pos;
+	size_t len;
+	int ret;
+
+	len = WL18XX_CONF_SIZE;
+	buf = kmalloc(len, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	header.magic	= cpu_to_le32(WL18XX_CONF_MAGIC);
+	header.version	= cpu_to_le32(WL18XX_CONF_VERSION);
+	header.checksum	= 0;
+
+	mutex_lock(&wl->mutex);
+
+	pos = buf;
+	memcpy(pos, &header, sizeof(header));
+	pos += sizeof(header);
+	memcpy(pos, &wl->conf, sizeof(wl->conf));
+	pos += sizeof(wl->conf);
+	memcpy(pos, &priv->conf, sizeof(priv->conf));
+
+	mutex_unlock(&wl->mutex);
+
+	ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+	kfree(buf);
+	return ret;
+}
+
+static const struct file_operations conf_ops = {
+	.read = conf_read,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t clear_fw_stats_write(struct file *file,
+			      const char __user *user_buf,
+			      size_t count, loff_t *ppos)
+{
+	struct wl1271 *wl = file->private_data;
+	int ret;
+
+	mutex_lock(&wl->mutex);
+
+	if (wl->state == WL1271_STATE_OFF)
+		goto out;
+
+	ret = wl18xx_acx_clear_statistics(wl);
+	if (ret < 0) {
+		count = ret;
+		goto out;
+	}
+out:
+	mutex_unlock(&wl->mutex);
+	return count;
+}
+
+static const struct file_operations clear_fw_stats_ops = {
+	.write = clear_fw_stats_write,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
+int wl18xx_debugfs_add_files(struct wl1271 *wl,
+			     struct dentry *rootdir)
+{
+	int ret = 0;
+	struct dentry *entry, *stats, *moddir;
+
+	moddir = debugfs_create_dir(KBUILD_MODNAME, rootdir);
+	if (!moddir || IS_ERR(moddir)) {
+		entry = moddir;
+		goto err;
+	}
+
+	stats = debugfs_create_dir("fw_stats", moddir);
+	if (!stats || IS_ERR(stats)) {
+		entry = stats;
+		goto err;
+	}
+
+	DEBUGFS_ADD(clear_fw_stats, stats);
+
+	DEBUGFS_FWSTATS_ADD(debug, debug1);
+	DEBUGFS_FWSTATS_ADD(debug, debug2);
+	DEBUGFS_FWSTATS_ADD(debug, debug3);
+	DEBUGFS_FWSTATS_ADD(debug, debug4);
+	DEBUGFS_FWSTATS_ADD(debug, debug5);
+	DEBUGFS_FWSTATS_ADD(debug, debug6);
+
+	DEBUGFS_FWSTATS_ADD(error, error_frame);
+	DEBUGFS_FWSTATS_ADD(error, error_null_Frame_tx_start);
+	DEBUGFS_FWSTATS_ADD(error, error_numll_frame_cts_start);
+	DEBUGFS_FWSTATS_ADD(error, error_bar_retry);
+	DEBUGFS_FWSTATS_ADD(error, error_frame_cts_nul_flid);
+
+	DEBUGFS_FWSTATS_ADD(tx, tx_prepared_descs);
+	DEBUGFS_FWSTATS_ADD(tx, tx_cmplt);
+	DEBUGFS_FWSTATS_ADD(tx, tx_template_prepared);
+	DEBUGFS_FWSTATS_ADD(tx, tx_data_prepared);
+	DEBUGFS_FWSTATS_ADD(tx, tx_template_programmed);
+	DEBUGFS_FWSTATS_ADD(tx, tx_data_programmed);
+	DEBUGFS_FWSTATS_ADD(tx, tx_burst_programmed);
+	DEBUGFS_FWSTATS_ADD(tx, tx_starts);
+	DEBUGFS_FWSTATS_ADD(tx, tx_imm_resp);
+	DEBUGFS_FWSTATS_ADD(tx, tx_start_templates);
+	DEBUGFS_FWSTATS_ADD(tx, tx_start_int_templates);
+	DEBUGFS_FWSTATS_ADD(tx, tx_start_fw_gen);
+	DEBUGFS_FWSTATS_ADD(tx, tx_start_data);
+	DEBUGFS_FWSTATS_ADD(tx, tx_start_null_frame);
+	DEBUGFS_FWSTATS_ADD(tx, tx_exch);
+	DEBUGFS_FWSTATS_ADD(tx, tx_retry_template);
+	DEBUGFS_FWSTATS_ADD(tx, tx_retry_data);
+	DEBUGFS_FWSTATS_ADD(tx, tx_exch_pending);
+	DEBUGFS_FWSTATS_ADD(tx, tx_exch_expiry);
+	DEBUGFS_FWSTATS_ADD(tx, tx_done_template);
+	DEBUGFS_FWSTATS_ADD(tx, tx_done_data);
+	DEBUGFS_FWSTATS_ADD(tx, tx_done_int_template);
+	DEBUGFS_FWSTATS_ADD(tx, tx_frame_checksum);
+	DEBUGFS_FWSTATS_ADD(tx, tx_checksum_result);
+	DEBUGFS_FWSTATS_ADD(tx, frag_called);
+	DEBUGFS_FWSTATS_ADD(tx, frag_mpdu_alloc_failed);
+	DEBUGFS_FWSTATS_ADD(tx, frag_init_called);
+	DEBUGFS_FWSTATS_ADD(tx, frag_in_process_called);
+	DEBUGFS_FWSTATS_ADD(tx, frag_tkip_called);
+	DEBUGFS_FWSTATS_ADD(tx, frag_key_not_found);
+	DEBUGFS_FWSTATS_ADD(tx, frag_need_fragmentation);
+	DEBUGFS_FWSTATS_ADD(tx, frag_bad_mblk_num);
+	DEBUGFS_FWSTATS_ADD(tx, frag_failed);
+	DEBUGFS_FWSTATS_ADD(tx, frag_cache_hit);
+	DEBUGFS_FWSTATS_ADD(tx, frag_cache_miss);
+
+	DEBUGFS_FWSTATS_ADD(rx, rx_beacon_early_term);
+	DEBUGFS_FWSTATS_ADD(rx, rx_out_of_mpdu_nodes);
+	DEBUGFS_FWSTATS_ADD(rx, rx_hdr_overflow);
+	DEBUGFS_FWSTATS_ADD(rx, rx_dropped_frame);
+	DEBUGFS_FWSTATS_ADD(rx, rx_done);
+	DEBUGFS_FWSTATS_ADD(rx, rx_defrag);
+	DEBUGFS_FWSTATS_ADD(rx, rx_defrag_end);
+	DEBUGFS_FWSTATS_ADD(rx, rx_cmplt);
+	DEBUGFS_FWSTATS_ADD(rx, rx_pre_complt);
+	DEBUGFS_FWSTATS_ADD(rx, rx_cmplt_task);
+	DEBUGFS_FWSTATS_ADD(rx, rx_phy_hdr);
+	DEBUGFS_FWSTATS_ADD(rx, rx_timeout);
+	DEBUGFS_FWSTATS_ADD(rx, rx_timeout_wa);
+	DEBUGFS_FWSTATS_ADD(rx, rx_wa_density_dropped_frame);
+	DEBUGFS_FWSTATS_ADD(rx, rx_wa_ba_not_expected);
+	DEBUGFS_FWSTATS_ADD(rx, rx_frame_checksum);
+	DEBUGFS_FWSTATS_ADD(rx, rx_checksum_result);
+	DEBUGFS_FWSTATS_ADD(rx, defrag_called);
+	DEBUGFS_FWSTATS_ADD(rx, defrag_init_called);
+	DEBUGFS_FWSTATS_ADD(rx, defrag_in_process_called);
+	DEBUGFS_FWSTATS_ADD(rx, defrag_tkip_called);
+	DEBUGFS_FWSTATS_ADD(rx, defrag_need_defrag);
+	DEBUGFS_FWSTATS_ADD(rx, defrag_decrypt_failed);
+	DEBUGFS_FWSTATS_ADD(rx, decrypt_key_not_found);
+	DEBUGFS_FWSTATS_ADD(rx, defrag_need_decrypt);
+	DEBUGFS_FWSTATS_ADD(rx, rx_tkip_replays);
+
+	DEBUGFS_FWSTATS_ADD(isr, irqs);
+
+	DEBUGFS_FWSTATS_ADD(pwr, missing_bcns_cnt);
+	DEBUGFS_FWSTATS_ADD(pwr, rcvd_bcns_cnt);
+	DEBUGFS_FWSTATS_ADD(pwr, connection_out_of_sync);
+	DEBUGFS_FWSTATS_ADD(pwr, cont_miss_bcns_spread);
+	DEBUGFS_FWSTATS_ADD(pwr, rcvd_awake_bcns_cnt);
+
+	DEBUGFS_FWSTATS_ADD(ps_poll, ps_poll_timeouts);
+	DEBUGFS_FWSTATS_ADD(ps_poll, upsd_timeouts);
+	DEBUGFS_FWSTATS_ADD(ps_poll, upsd_max_ap_turn);
+	DEBUGFS_FWSTATS_ADD(ps_poll, ps_poll_max_ap_turn);
+	DEBUGFS_FWSTATS_ADD(ps_poll, ps_poll_utilization);
+	DEBUGFS_FWSTATS_ADD(ps_poll, upsd_utilization);
+
+	DEBUGFS_FWSTATS_ADD(rx_filter, beacon_filter);
+	DEBUGFS_FWSTATS_ADD(rx_filter, arp_filter);
+	DEBUGFS_FWSTATS_ADD(rx_filter, mc_filter);
+	DEBUGFS_FWSTATS_ADD(rx_filter, dup_filter);
+	DEBUGFS_FWSTATS_ADD(rx_filter, data_filter);
+	DEBUGFS_FWSTATS_ADD(rx_filter, ibss_filter);
+	DEBUGFS_FWSTATS_ADD(rx_filter, protection_filter);
+	DEBUGFS_FWSTATS_ADD(rx_filter, accum_arp_pend_requests);
+	DEBUGFS_FWSTATS_ADD(rx_filter, max_arp_queue_dep);
+
+	DEBUGFS_FWSTATS_ADD(rx_rate, rx_frames_per_rates);
+
+	DEBUGFS_FWSTATS_ADD(aggr_size, tx_agg_vs_rate);
+	DEBUGFS_FWSTATS_ADD(aggr_size, rx_size);
+
+	DEBUGFS_FWSTATS_ADD(pipeline, hs_tx_stat_fifo_int);
+	DEBUGFS_FWSTATS_ADD(pipeline, tcp_tx_stat_fifo_int);
+	DEBUGFS_FWSTATS_ADD(pipeline, tcp_rx_stat_fifo_int);
+	DEBUGFS_FWSTATS_ADD(pipeline, enc_tx_stat_fifo_int);
+	DEBUGFS_FWSTATS_ADD(pipeline, enc_rx_stat_fifo_int);
+	DEBUGFS_FWSTATS_ADD(pipeline, rx_complete_stat_fifo_int);
+	DEBUGFS_FWSTATS_ADD(pipeline, pre_proc_swi);
+	DEBUGFS_FWSTATS_ADD(pipeline, post_proc_swi);
+	DEBUGFS_FWSTATS_ADD(pipeline, sec_frag_swi);
+	DEBUGFS_FWSTATS_ADD(pipeline, pre_to_defrag_swi);
+	DEBUGFS_FWSTATS_ADD(pipeline, defrag_to_csum_swi);
+	DEBUGFS_FWSTATS_ADD(pipeline, csum_to_rx_xfer_swi);
+	DEBUGFS_FWSTATS_ADD(pipeline, dec_packet_in);
+	DEBUGFS_FWSTATS_ADD(pipeline, dec_packet_in_fifo_full);
+	DEBUGFS_FWSTATS_ADD(pipeline, dec_packet_out);
+	DEBUGFS_FWSTATS_ADD(pipeline, cs_rx_packet_in);
+	DEBUGFS_FWSTATS_ADD(pipeline, cs_rx_packet_out);
+	DEBUGFS_FWSTATS_ADD(pipeline, pipeline_fifo_full);
+
+	DEBUGFS_FWSTATS_ADD(mem, rx_free_mem_blks);
+	DEBUGFS_FWSTATS_ADD(mem, tx_free_mem_blks);
+	DEBUGFS_FWSTATS_ADD(mem, fwlog_free_mem_blks);
+	DEBUGFS_FWSTATS_ADD(mem, fw_gen_free_mem_blks);
+
+	DEBUGFS_ADD(conf, moddir);
+
+	return 0;
+
+err:
+	if (IS_ERR(entry))
+		ret = PTR_ERR(entry);
+	else
+		ret = -ENOMEM;
+
+	return ret;
+}

+ 28 - 0
drivers/net/wireless/ti/wl18xx/debugfs.h

@@ -0,0 +1,28 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL18XX_DEBUGFS_H__
+#define __WL18XX_DEBUGFS_H__
+
+int wl18xx_debugfs_add_files(struct wl1271 *wl,
+			     struct dentry *rootdir);
+
+#endif /* __WL18XX_DEBUGFS_H__ */

+ 60 - 0
drivers/net/wireless/ti/wl18xx/io.c

@@ -0,0 +1,60 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "../wlcore/wlcore.h"
+#include "../wlcore/io.h"
+
+#include "io.h"
+
+void wl18xx_top_reg_write(struct wl1271 *wl, int addr, u16 val)
+{
+	u32 tmp;
+
+	if (WARN_ON(addr % 2))
+		return;
+
+	if ((addr % 4) == 0) {
+		tmp = wl1271_read32(wl, addr);
+		tmp = (tmp & 0xffff0000) | val;
+		wl1271_write32(wl, addr, tmp);
+	} else {
+		tmp = wl1271_read32(wl, addr - 2);
+		tmp = (tmp & 0xffff) | (val << 16);
+		wl1271_write32(wl, addr - 2, tmp);
+	}
+}
+
+u16 wl18xx_top_reg_read(struct wl1271 *wl, int addr)
+{
+	u32 val;
+
+	if (WARN_ON(addr % 2))
+		return 0;
+
+	if ((addr % 4) == 0) {
+		/* address is 4-bytes aligned */
+		val = wl1271_read32(wl, addr);
+		return val & 0xffff;
+	} else {
+		val = wl1271_read32(wl, addr - 2);
+		return (val & 0xffff0000) >> 16;
+	}
+}

+ 28 - 0
drivers/net/wireless/ti/wl18xx/io.h

@@ -0,0 +1,28 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL18XX_IO_H__
+#define __WL18XX_IO_H__
+
+void wl18xx_top_reg_write(struct wl1271 *wl, int addr, u16 val);
+u16 wl18xx_top_reg_read(struct wl1271 *wl, int addr);
+
+#endif /* __WL18XX_IO_H__ */

+ 1463 - 0
drivers/net/wireless/ti/wl18xx/main.c

@@ -0,0 +1,1463 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/ip.h>
+#include <linux/firmware.h>
+
+#include "../wlcore/wlcore.h"
+#include "../wlcore/debug.h"
+#include "../wlcore/io.h"
+#include "../wlcore/acx.h"
+#include "../wlcore/tx.h"
+#include "../wlcore/rx.h"
+#include "../wlcore/io.h"
+#include "../wlcore/boot.h"
+
+#include "reg.h"
+#include "conf.h"
+#include "acx.h"
+#include "tx.h"
+#include "wl18xx.h"
+#include "io.h"
+#include "debugfs.h"
+
+#define WL18XX_RX_CHECKSUM_MASK      0x40
+
+static char *ht_mode_param = "wide";
+static char *board_type_param = "hdk";
+static bool checksum_param = false;
+static bool enable_11a_param = true;
+
+/* phy paramters */
+static int dc2dc_param = -1;
+static int n_antennas_2_param = -1;
+static int n_antennas_5_param = -1;
+static int low_band_component_param = -1;
+static int low_band_component_type_param = -1;
+static int high_band_component_param = -1;
+static int high_band_component_type_param = -1;
+static int pwr_limit_reference_11_abg_param = -1;
+
+static const u8 wl18xx_rate_to_idx_2ghz[] = {
+	/* MCS rates are used only with 11n */
+	15,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS15 */
+	14,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS14 */
+	13,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS13 */
+	12,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS12 */
+	11,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS11 */
+	10,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS10 */
+	9,                             /* WL18XX_CONF_HW_RXTX_RATE_MCS9 */
+	8,                             /* WL18XX_CONF_HW_RXTX_RATE_MCS8 */
+	7,                             /* WL18XX_CONF_HW_RXTX_RATE_MCS7 */
+	6,                             /* WL18XX_CONF_HW_RXTX_RATE_MCS6 */
+	5,                             /* WL18XX_CONF_HW_RXTX_RATE_MCS5 */
+	4,                             /* WL18XX_CONF_HW_RXTX_RATE_MCS4 */
+	3,                             /* WL18XX_CONF_HW_RXTX_RATE_MCS3 */
+	2,                             /* WL18XX_CONF_HW_RXTX_RATE_MCS2 */
+	1,                             /* WL18XX_CONF_HW_RXTX_RATE_MCS1 */
+	0,                             /* WL18XX_CONF_HW_RXTX_RATE_MCS0 */
+
+	11,                            /* WL18XX_CONF_HW_RXTX_RATE_54   */
+	10,                            /* WL18XX_CONF_HW_RXTX_RATE_48   */
+	9,                             /* WL18XX_CONF_HW_RXTX_RATE_36   */
+	8,                             /* WL18XX_CONF_HW_RXTX_RATE_24   */
+
+	/* TI-specific rate */
+	CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL18XX_CONF_HW_RXTX_RATE_22   */
+
+	7,                             /* WL18XX_CONF_HW_RXTX_RATE_18   */
+	6,                             /* WL18XX_CONF_HW_RXTX_RATE_12   */
+	3,                             /* WL18XX_CONF_HW_RXTX_RATE_11   */
+	5,                             /* WL18XX_CONF_HW_RXTX_RATE_9    */
+	4,                             /* WL18XX_CONF_HW_RXTX_RATE_6    */
+	2,                             /* WL18XX_CONF_HW_RXTX_RATE_5_5  */
+	1,                             /* WL18XX_CONF_HW_RXTX_RATE_2    */
+	0                              /* WL18XX_CONF_HW_RXTX_RATE_1    */
+};
+
+static const u8 wl18xx_rate_to_idx_5ghz[] = {
+	/* MCS rates are used only with 11n */
+	15,                           /* WL18XX_CONF_HW_RXTX_RATE_MCS15 */
+	14,                           /* WL18XX_CONF_HW_RXTX_RATE_MCS14 */
+	13,                           /* WL18XX_CONF_HW_RXTX_RATE_MCS13 */
+	12,                           /* WL18XX_CONF_HW_RXTX_RATE_MCS12 */
+	11,                           /* WL18XX_CONF_HW_RXTX_RATE_MCS11 */
+	10,                           /* WL18XX_CONF_HW_RXTX_RATE_MCS10 */
+	9,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS9 */
+	8,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS8 */
+	7,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS7 */
+	6,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS6 */
+	5,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS5 */
+	4,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS4 */
+	3,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS3 */
+	2,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS2 */
+	1,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS1 */
+	0,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS0 */
+
+	7,                             /* WL18XX_CONF_HW_RXTX_RATE_54   */
+	6,                             /* WL18XX_CONF_HW_RXTX_RATE_48   */
+	5,                             /* WL18XX_CONF_HW_RXTX_RATE_36   */
+	4,                             /* WL18XX_CONF_HW_RXTX_RATE_24   */
+
+	/* TI-specific rate */
+	CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL18XX_CONF_HW_RXTX_RATE_22   */
+
+	3,                             /* WL18XX_CONF_HW_RXTX_RATE_18   */
+	2,                             /* WL18XX_CONF_HW_RXTX_RATE_12   */
+	CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL18XX_CONF_HW_RXTX_RATE_11   */
+	1,                             /* WL18XX_CONF_HW_RXTX_RATE_9    */
+	0,                             /* WL18XX_CONF_HW_RXTX_RATE_6    */
+	CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL18XX_CONF_HW_RXTX_RATE_5_5  */
+	CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL18XX_CONF_HW_RXTX_RATE_2    */
+	CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL18XX_CONF_HW_RXTX_RATE_1    */
+};
+
+static const u8 *wl18xx_band_rate_to_idx[] = {
+	[IEEE80211_BAND_2GHZ] = wl18xx_rate_to_idx_2ghz,
+	[IEEE80211_BAND_5GHZ] = wl18xx_rate_to_idx_5ghz
+};
+
+enum wl18xx_hw_rates {
+	WL18XX_CONF_HW_RXTX_RATE_MCS15 = 0,
+	WL18XX_CONF_HW_RXTX_RATE_MCS14,
+	WL18XX_CONF_HW_RXTX_RATE_MCS13,
+	WL18XX_CONF_HW_RXTX_RATE_MCS12,
+	WL18XX_CONF_HW_RXTX_RATE_MCS11,
+	WL18XX_CONF_HW_RXTX_RATE_MCS10,
+	WL18XX_CONF_HW_RXTX_RATE_MCS9,
+	WL18XX_CONF_HW_RXTX_RATE_MCS8,
+	WL18XX_CONF_HW_RXTX_RATE_MCS7,
+	WL18XX_CONF_HW_RXTX_RATE_MCS6,
+	WL18XX_CONF_HW_RXTX_RATE_MCS5,
+	WL18XX_CONF_HW_RXTX_RATE_MCS4,
+	WL18XX_CONF_HW_RXTX_RATE_MCS3,
+	WL18XX_CONF_HW_RXTX_RATE_MCS2,
+	WL18XX_CONF_HW_RXTX_RATE_MCS1,
+	WL18XX_CONF_HW_RXTX_RATE_MCS0,
+	WL18XX_CONF_HW_RXTX_RATE_54,
+	WL18XX_CONF_HW_RXTX_RATE_48,
+	WL18XX_CONF_HW_RXTX_RATE_36,
+	WL18XX_CONF_HW_RXTX_RATE_24,
+	WL18XX_CONF_HW_RXTX_RATE_22,
+	WL18XX_CONF_HW_RXTX_RATE_18,
+	WL18XX_CONF_HW_RXTX_RATE_12,
+	WL18XX_CONF_HW_RXTX_RATE_11,
+	WL18XX_CONF_HW_RXTX_RATE_9,
+	WL18XX_CONF_HW_RXTX_RATE_6,
+	WL18XX_CONF_HW_RXTX_RATE_5_5,
+	WL18XX_CONF_HW_RXTX_RATE_2,
+	WL18XX_CONF_HW_RXTX_RATE_1,
+	WL18XX_CONF_HW_RXTX_RATE_MAX,
+};
+
+static struct wlcore_conf wl18xx_conf = {
+	.sg = {
+		.params = {
+			[CONF_SG_ACL_BT_MASTER_MIN_BR] = 10,
+			[CONF_SG_ACL_BT_MASTER_MAX_BR] = 180,
+			[CONF_SG_ACL_BT_SLAVE_MIN_BR] = 10,
+			[CONF_SG_ACL_BT_SLAVE_MAX_BR] = 180,
+			[CONF_SG_ACL_BT_MASTER_MIN_EDR] = 10,
+			[CONF_SG_ACL_BT_MASTER_MAX_EDR] = 80,
+			[CONF_SG_ACL_BT_SLAVE_MIN_EDR] = 10,
+			[CONF_SG_ACL_BT_SLAVE_MAX_EDR] = 80,
+			[CONF_SG_ACL_WLAN_PS_MASTER_BR] = 8,
+			[CONF_SG_ACL_WLAN_PS_SLAVE_BR] = 8,
+			[CONF_SG_ACL_WLAN_PS_MASTER_EDR] = 20,
+			[CONF_SG_ACL_WLAN_PS_SLAVE_EDR] = 20,
+			[CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_BR] = 20,
+			[CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_BR] = 35,
+			[CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_BR] = 16,
+			[CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_BR] = 35,
+			[CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_EDR] = 32,
+			[CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_EDR] = 50,
+			[CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_EDR] = 28,
+			[CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_EDR] = 50,
+			[CONF_SG_ACL_ACTIVE_SCAN_WLAN_BR] = 10,
+			[CONF_SG_ACL_ACTIVE_SCAN_WLAN_EDR] = 20,
+			[CONF_SG_ACL_PASSIVE_SCAN_BT_BR] = 75,
+			[CONF_SG_ACL_PASSIVE_SCAN_WLAN_BR] = 15,
+			[CONF_SG_ACL_PASSIVE_SCAN_BT_EDR] = 27,
+			[CONF_SG_ACL_PASSIVE_SCAN_WLAN_EDR] = 17,
+			/* active scan params */
+			[CONF_SG_AUTO_SCAN_PROBE_REQ] = 170,
+			[CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50,
+			[CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100,
+			/* passive scan params */
+			[CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_BR] = 800,
+			[CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_EDR] = 200,
+			[CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200,
+			/* passive scan in dual antenna params */
+			[CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN] = 0,
+			[CONF_SG_BCN_HV3_COLLISION_THRESH_IN_PASSIVE_SCAN] = 0,
+			[CONF_SG_TX_RX_PROTECTION_BWIDTH_IN_PASSIVE_SCAN] = 0,
+			/* general params */
+			[CONF_SG_STA_FORCE_PS_IN_BT_SCO] = 1,
+			[CONF_SG_ANTENNA_CONFIGURATION] = 0,
+			[CONF_SG_BEACON_MISS_PERCENT] = 60,
+			[CONF_SG_DHCP_TIME] = 5000,
+			[CONF_SG_RXT] = 1200,
+			[CONF_SG_TXT] = 1000,
+			[CONF_SG_ADAPTIVE_RXT_TXT] = 1,
+			[CONF_SG_GENERAL_USAGE_BIT_MAP] = 3,
+			[CONF_SG_HV3_MAX_SERVED] = 6,
+			[CONF_SG_PS_POLL_TIMEOUT] = 10,
+			[CONF_SG_UPSD_TIMEOUT] = 10,
+			[CONF_SG_CONSECUTIVE_CTS_THRESHOLD] = 2,
+			[CONF_SG_STA_RX_WINDOW_AFTER_DTIM] = 5,
+			[CONF_SG_STA_CONNECTION_PROTECTION_TIME] = 30,
+			/* AP params */
+			[CONF_AP_BEACON_MISS_TX] = 3,
+			[CONF_AP_RX_WINDOW_AFTER_BEACON] = 10,
+			[CONF_AP_BEACON_WINDOW_INTERVAL] = 2,
+			[CONF_AP_CONNECTION_PROTECTION_TIME] = 0,
+			[CONF_AP_BT_ACL_VAL_BT_SERVE_TIME] = 25,
+			[CONF_AP_BT_ACL_VAL_WL_SERVE_TIME] = 25,
+			/* CTS Diluting params */
+			[CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH] = 0,
+			[CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER] = 0,
+		},
+		.state = CONF_SG_PROTECTIVE,
+	},
+	.rx = {
+		.rx_msdu_life_time           = 512000,
+		.packet_detection_threshold  = 0,
+		.ps_poll_timeout             = 15,
+		.upsd_timeout                = 15,
+		.rts_threshold               = IEEE80211_MAX_RTS_THRESHOLD,
+		.rx_cca_threshold            = 0,
+		.irq_blk_threshold           = 0xFFFF,
+		.irq_pkt_threshold           = 0,
+		.irq_timeout                 = 600,
+		.queue_type                  = CONF_RX_QUEUE_TYPE_LOW_PRIORITY,
+	},
+	.tx = {
+		.tx_energy_detection         = 0,
+		.sta_rc_conf                 = {
+			.enabled_rates       = 0,
+			.short_retry_limit   = 10,
+			.long_retry_limit    = 10,
+			.aflags              = 0,
+		},
+		.ac_conf_count               = 4,
+		.ac_conf                     = {
+			[CONF_TX_AC_BE] = {
+				.ac          = CONF_TX_AC_BE,
+				.cw_min      = 15,
+				.cw_max      = 63,
+				.aifsn       = 3,
+				.tx_op_limit = 0,
+			},
+			[CONF_TX_AC_BK] = {
+				.ac          = CONF_TX_AC_BK,
+				.cw_min      = 15,
+				.cw_max      = 63,
+				.aifsn       = 7,
+				.tx_op_limit = 0,
+			},
+			[CONF_TX_AC_VI] = {
+				.ac          = CONF_TX_AC_VI,
+				.cw_min      = 15,
+				.cw_max      = 63,
+				.aifsn       = CONF_TX_AIFS_PIFS,
+				.tx_op_limit = 3008,
+			},
+			[CONF_TX_AC_VO] = {
+				.ac          = CONF_TX_AC_VO,
+				.cw_min      = 15,
+				.cw_max      = 63,
+				.aifsn       = CONF_TX_AIFS_PIFS,
+				.tx_op_limit = 1504,
+			},
+		},
+		.max_tx_retries = 100,
+		.ap_aging_period = 300,
+		.tid_conf_count = 4,
+		.tid_conf = {
+			[CONF_TX_AC_BE] = {
+				.queue_id    = CONF_TX_AC_BE,
+				.channel_type = CONF_CHANNEL_TYPE_EDCF,
+				.tsid        = CONF_TX_AC_BE,
+				.ps_scheme   = CONF_PS_SCHEME_LEGACY,
+				.ack_policy  = CONF_ACK_POLICY_LEGACY,
+				.apsd_conf   = {0, 0},
+			},
+			[CONF_TX_AC_BK] = {
+				.queue_id    = CONF_TX_AC_BK,
+				.channel_type = CONF_CHANNEL_TYPE_EDCF,
+				.tsid        = CONF_TX_AC_BK,
+				.ps_scheme   = CONF_PS_SCHEME_LEGACY,
+				.ack_policy  = CONF_ACK_POLICY_LEGACY,
+				.apsd_conf   = {0, 0},
+			},
+			[CONF_TX_AC_VI] = {
+				.queue_id    = CONF_TX_AC_VI,
+				.channel_type = CONF_CHANNEL_TYPE_EDCF,
+				.tsid        = CONF_TX_AC_VI,
+				.ps_scheme   = CONF_PS_SCHEME_LEGACY,
+				.ack_policy  = CONF_ACK_POLICY_LEGACY,
+				.apsd_conf   = {0, 0},
+			},
+			[CONF_TX_AC_VO] = {
+				.queue_id    = CONF_TX_AC_VO,
+				.channel_type = CONF_CHANNEL_TYPE_EDCF,
+				.tsid        = CONF_TX_AC_VO,
+				.ps_scheme   = CONF_PS_SCHEME_LEGACY,
+				.ack_policy  = CONF_ACK_POLICY_LEGACY,
+				.apsd_conf   = {0, 0},
+			},
+		},
+		.frag_threshold              = IEEE80211_MAX_FRAG_THRESHOLD,
+		.tx_compl_timeout            = 350,
+		.tx_compl_threshold          = 10,
+		.basic_rate                  = CONF_HW_BIT_RATE_1MBPS,
+		.basic_rate_5                = CONF_HW_BIT_RATE_6MBPS,
+		.tmpl_short_retry_limit      = 10,
+		.tmpl_long_retry_limit       = 10,
+		.tx_watchdog_timeout         = 5000,
+	},
+	.conn = {
+		.wake_up_event               = CONF_WAKE_UP_EVENT_DTIM,
+		.listen_interval             = 1,
+		.suspend_wake_up_event       = CONF_WAKE_UP_EVENT_N_DTIM,
+		.suspend_listen_interval     = 3,
+		.bcn_filt_mode               = CONF_BCN_FILT_MODE_ENABLED,
+		.bcn_filt_ie_count           = 3,
+		.bcn_filt_ie = {
+			[0] = {
+				.ie          = WLAN_EID_CHANNEL_SWITCH,
+				.rule        = CONF_BCN_RULE_PASS_ON_APPEARANCE,
+			},
+			[1] = {
+				.ie          = WLAN_EID_HT_OPERATION,
+				.rule        = CONF_BCN_RULE_PASS_ON_CHANGE,
+			},
+			[2] = {
+				.ie	     = WLAN_EID_ERP_INFO,
+				.rule	     = CONF_BCN_RULE_PASS_ON_CHANGE,
+			},
+		},
+		.synch_fail_thold            = 12,
+		.bss_lose_timeout            = 400,
+		.beacon_rx_timeout           = 10000,
+		.broadcast_timeout           = 20000,
+		.rx_broadcast_in_ps          = 1,
+		.ps_poll_threshold           = 10,
+		.bet_enable                  = CONF_BET_MODE_ENABLE,
+		.bet_max_consecutive         = 50,
+		.psm_entry_retries           = 8,
+		.psm_exit_retries            = 16,
+		.psm_entry_nullfunc_retries  = 3,
+		.dynamic_ps_timeout          = 200,
+		.forced_ps                   = false,
+		.keep_alive_interval         = 55000,
+		.max_listen_interval         = 20,
+	},
+	.itrim = {
+		.enable = false,
+		.timeout = 50000,
+	},
+	.pm_config = {
+		.host_clk_settling_time = 5000,
+		.host_fast_wakeup_support = CONF_FAST_WAKEUP_DISABLE,
+	},
+	.roam_trigger = {
+		.trigger_pacing               = 1,
+		.avg_weight_rssi_beacon       = 20,
+		.avg_weight_rssi_data         = 10,
+		.avg_weight_snr_beacon        = 20,
+		.avg_weight_snr_data          = 10,
+	},
+	.scan = {
+		.min_dwell_time_active        = 7500,
+		.max_dwell_time_active        = 30000,
+		.min_dwell_time_passive       = 100000,
+		.max_dwell_time_passive       = 100000,
+		.num_probe_reqs               = 2,
+		.split_scan_timeout           = 50000,
+	},
+	.sched_scan = {
+		/*
+		 * Values are in TU/1000 but since sched scan FW command
+		 * params are in TUs rounding up may occur.
+		 */
+		.base_dwell_time		= 7500,
+		.max_dwell_time_delta		= 22500,
+		/* based on 250bits per probe @1Mbps */
+		.dwell_time_delta_per_probe	= 2000,
+		/* based on 250bits per probe @6Mbps (plus a bit more) */
+		.dwell_time_delta_per_probe_5	= 350,
+		.dwell_time_passive		= 100000,
+		.dwell_time_dfs			= 150000,
+		.num_probe_reqs			= 2,
+		.rssi_threshold			= -90,
+		.snr_threshold			= 0,
+	},
+	.ht = {
+		.rx_ba_win_size = 10,
+		.tx_ba_win_size = 64,
+		.inactivity_timeout = 10000,
+		.tx_ba_tid_bitmap = CONF_TX_BA_ENABLED_TID_BITMAP,
+	},
+	.mem = {
+		.num_stations                 = 1,
+		.ssid_profiles                = 1,
+		.rx_block_num                 = 40,
+		.tx_min_block_num             = 40,
+		.dynamic_memory               = 1,
+		.min_req_tx_blocks            = 45,
+		.min_req_rx_blocks            = 22,
+		.tx_min                       = 27,
+	},
+	.fm_coex = {
+		.enable                       = true,
+		.swallow_period               = 5,
+		.n_divider_fref_set_1         = 0xff,       /* default */
+		.n_divider_fref_set_2         = 12,
+		.m_divider_fref_set_1         = 0xffff,
+		.m_divider_fref_set_2         = 148,        /* default */
+		.coex_pll_stabilization_time  = 0xffffffff, /* default */
+		.ldo_stabilization_time       = 0xffff,     /* default */
+		.fm_disturbed_band_margin     = 0xff,       /* default */
+		.swallow_clk_diff             = 0xff,       /* default */
+	},
+	.rx_streaming = {
+		.duration                      = 150,
+		.queues                        = 0x1,
+		.interval                      = 20,
+		.always                        = 0,
+	},
+	.fwlog = {
+		.mode                         = WL12XX_FWLOG_ON_DEMAND,
+		.mem_blocks                   = 2,
+		.severity                     = 0,
+		.timestamp                    = WL12XX_FWLOG_TIMESTAMP_DISABLED,
+		.output                       = WL12XX_FWLOG_OUTPUT_HOST,
+		.threshold                    = 0,
+	},
+	.rate = {
+		.rate_retry_score = 32000,
+		.per_add = 8192,
+		.per_th1 = 2048,
+		.per_th2 = 4096,
+		.max_per = 8100,
+		.inverse_curiosity_factor = 5,
+		.tx_fail_low_th = 4,
+		.tx_fail_high_th = 10,
+		.per_alpha_shift = 4,
+		.per_add_shift = 13,
+		.per_beta1_shift = 10,
+		.per_beta2_shift = 8,
+		.rate_check_up = 2,
+		.rate_check_down = 12,
+		.rate_retry_policy = {
+			0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00,
+		},
+	},
+	.hangover = {
+		.recover_time               = 0,
+		.hangover_period            = 20,
+		.dynamic_mode               = 1,
+		.early_termination_mode     = 1,
+		.max_period                 = 20,
+		.min_period                 = 1,
+		.increase_delta             = 1,
+		.decrease_delta             = 2,
+		.quiet_time                 = 4,
+		.increase_time              = 1,
+		.window_size                = 16,
+	},
+};
+
+static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
+	.phy = {
+		.phy_standalone			= 0x00,
+		.primary_clock_setting_time	= 0x05,
+		.clock_valid_on_wake_up		= 0x00,
+		.secondary_clock_setting_time	= 0x05,
+		.rdl				= 0x01,
+		.auto_detect			= 0x00,
+		.dedicated_fem			= FEM_NONE,
+		.low_band_component		= COMPONENT_2_WAY_SWITCH,
+		.low_band_component_type	= 0x05,
+		.high_band_component		= COMPONENT_2_WAY_SWITCH,
+		.high_band_component_type	= 0x09,
+		.tcxo_ldo_voltage		= 0x00,
+		.xtal_itrim_val			= 0x04,
+		.srf_state			= 0x00,
+		.io_configuration		= 0x01,
+		.sdio_configuration		= 0x00,
+		.settings			= 0x00,
+		.enable_clpc			= 0x00,
+		.enable_tx_low_pwr_on_siso_rdl	= 0x00,
+		.rx_profile			= 0x00,
+		.pwr_limit_reference_11_abg	= 0xc8,
+		.psat				= 0,
+		.low_power_val			= 0x00,
+		.med_power_val			= 0x0a,
+		.high_power_val			= 0x1e,
+		.external_pa_dc2dc		= 0,
+		.number_of_assembled_ant2_4	= 1,
+		.number_of_assembled_ant5	= 1,
+	},
+};
+
+static const struct wlcore_partition_set wl18xx_ptable[PART_TABLE_LEN] = {
+	[PART_TOP_PRCM_ELP_SOC] = {
+		.mem  = { .start = 0x00A02000, .size  = 0x00010000 },
+		.reg  = { .start = 0x00807000, .size  = 0x00005000 },
+		.mem2 = { .start = 0x00800000, .size  = 0x0000B000 },
+		.mem3 = { .start = 0x00000000, .size  = 0x00000000 },
+	},
+	[PART_DOWN] = {
+		.mem  = { .start = 0x00000000, .size  = 0x00014000 },
+		.reg  = { .start = 0x00810000, .size  = 0x0000BFFF },
+		.mem2 = { .start = 0x00000000, .size  = 0x00000000 },
+		.mem3 = { .start = 0x00000000, .size  = 0x00000000 },
+	},
+	[PART_BOOT] = {
+		.mem  = { .start = 0x00700000, .size = 0x0000030c },
+		.reg  = { .start = 0x00802000, .size = 0x00014578 },
+		.mem2 = { .start = 0x00B00404, .size = 0x00001000 },
+		.mem3 = { .start = 0x00C00000, .size = 0x00000400 },
+	},
+	[PART_WORK] = {
+		.mem  = { .start = 0x00800000, .size  = 0x000050FC },
+		.reg  = { .start = 0x00B00404, .size  = 0x00001000 },
+		.mem2 = { .start = 0x00C00000, .size  = 0x00000400 },
+		.mem3 = { .start = 0x00000000, .size  = 0x00000000 },
+	},
+	[PART_PHY_INIT] = {
+		.mem  = { .start = 0x80926000,
+			  .size = sizeof(struct wl18xx_mac_and_phy_params) },
+		.reg  = { .start = 0x00000000, .size = 0x00000000 },
+		.mem2 = { .start = 0x00000000, .size = 0x00000000 },
+		.mem3 = { .start = 0x00000000, .size = 0x00000000 },
+	},
+};
+
+static const int wl18xx_rtable[REG_TABLE_LEN] = {
+	[REG_ECPU_CONTROL]		= WL18XX_REG_ECPU_CONTROL,
+	[REG_INTERRUPT_NO_CLEAR]	= WL18XX_REG_INTERRUPT_NO_CLEAR,
+	[REG_INTERRUPT_ACK]		= WL18XX_REG_INTERRUPT_ACK,
+	[REG_COMMAND_MAILBOX_PTR]	= WL18XX_REG_COMMAND_MAILBOX_PTR,
+	[REG_EVENT_MAILBOX_PTR]		= WL18XX_REG_EVENT_MAILBOX_PTR,
+	[REG_INTERRUPT_TRIG]		= WL18XX_REG_INTERRUPT_TRIG_H,
+	[REG_INTERRUPT_MASK]		= WL18XX_REG_INTERRUPT_MASK,
+	[REG_PC_ON_RECOVERY]		= WL18XX_SCR_PAD4,
+	[REG_CHIP_ID_B]			= WL18XX_REG_CHIP_ID_B,
+	[REG_CMD_MBOX_ADDRESS]		= WL18XX_CMD_MBOX_ADDRESS,
+
+	/* data access memory addresses, used with partition translation */
+	[REG_SLV_MEM_DATA]		= WL18XX_SLV_MEM_DATA,
+	[REG_SLV_REG_DATA]		= WL18XX_SLV_REG_DATA,
+
+	/* raw data access memory addresses */
+	[REG_RAW_FW_STATUS_ADDR]	= WL18XX_FW_STATUS_ADDR,
+};
+
+static const struct wl18xx_clk_cfg wl18xx_clk_table[NUM_CLOCK_CONFIGS] = {
+	[CLOCK_CONFIG_16_2_M]	= { 7,  104,  801, 4,  true },
+	[CLOCK_CONFIG_16_368_M]	= { 9,  132, 3751, 4,  true },
+	[CLOCK_CONFIG_16_8_M]	= { 7,  100,    0, 0, false },
+	[CLOCK_CONFIG_19_2_M]	= { 8,  100,    0, 0, false },
+	[CLOCK_CONFIG_26_M]	= { 13, 120,    0, 0, false },
+	[CLOCK_CONFIG_32_736_M]	= { 9,  132, 3751, 4,  true },
+	[CLOCK_CONFIG_33_6_M]	= { 7,  100,    0, 0, false },
+	[CLOCK_CONFIG_38_468_M]	= { 8,  100,    0, 0, false },
+	[CLOCK_CONFIG_52_M]	= { 13, 120,    0, 0, false },
+};
+
+/* TODO: maybe move to a new header file? */
+#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw.bin"
+
+static int wl18xx_identify_chip(struct wl1271 *wl)
+{
+	int ret = 0;
+
+	switch (wl->chip.id) {
+	case CHIP_ID_185x_PG20:
+		wl1271_debug(DEBUG_BOOT, "chip id 0x%x (185x PG20)",
+				 wl->chip.id);
+		wl->sr_fw_name = WL18XX_FW_NAME;
+		/* wl18xx uses the same firmware for PLT */
+		wl->plt_fw_name = WL18XX_FW_NAME;
+		wl->quirks |= WLCORE_QUIRK_NO_ELP |
+			      WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN |
+			      WLCORE_QUIRK_TX_PAD_LAST_FRAME;
+
+		break;
+	case CHIP_ID_185x_PG10:
+		wl1271_debug(DEBUG_BOOT, "chip id 0x%x (185x PG10)",
+			     wl->chip.id);
+		wl->sr_fw_name = WL18XX_FW_NAME;
+		/* wl18xx uses the same firmware for PLT */
+		wl->plt_fw_name = WL18XX_FW_NAME;
+		wl->quirks |= WLCORE_QUIRK_NO_ELP |
+			WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED |
+			WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN |
+			WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN;
+
+		/* PG 1.0 has some problems with MCS_13, so disable it */
+		wl->ht_cap[IEEE80211_BAND_2GHZ].mcs.rx_mask[1] &= ~BIT(5);
+
+		break;
+	default:
+		wl1271_warning("unsupported chip id: 0x%x", wl->chip.id);
+		ret = -ENODEV;
+		goto out;
+	}
+
+out:
+	return ret;
+}
+
+static void wl18xx_set_clk(struct wl1271 *wl)
+{
+	u32 clk_freq;
+
+	wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]);
+
+	/* TODO: PG2: apparently we need to read the clk type */
+
+	clk_freq = wl18xx_top_reg_read(wl, PRIMARY_CLK_DETECT);
+	wl1271_debug(DEBUG_BOOT, "clock freq %d (%d, %d, %d, %d, %s)", clk_freq,
+		     wl18xx_clk_table[clk_freq].n, wl18xx_clk_table[clk_freq].m,
+		     wl18xx_clk_table[clk_freq].p, wl18xx_clk_table[clk_freq].q,
+		     wl18xx_clk_table[clk_freq].swallow ? "swallow" : "spit");
+
+	wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_N, wl18xx_clk_table[clk_freq].n);
+	wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_M, wl18xx_clk_table[clk_freq].m);
+
+	if (wl18xx_clk_table[clk_freq].swallow) {
+		/* first the 16 lower bits */
+		wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_Q_FACTOR_CFG_1,
+				     wl18xx_clk_table[clk_freq].q &
+				     PLLSH_WCS_PLL_Q_FACTOR_CFG_1_MASK);
+		/* then the 16 higher bits, masked out */
+		wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_Q_FACTOR_CFG_2,
+				     (wl18xx_clk_table[clk_freq].q >> 16) &
+				     PLLSH_WCS_PLL_Q_FACTOR_CFG_2_MASK);
+
+		/* first the 16 lower bits */
+		wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_P_FACTOR_CFG_1,
+				     wl18xx_clk_table[clk_freq].p &
+				     PLLSH_WCS_PLL_P_FACTOR_CFG_1_MASK);
+		/* then the 16 higher bits, masked out */
+		wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_P_FACTOR_CFG_2,
+				     (wl18xx_clk_table[clk_freq].p >> 16) &
+				     PLLSH_WCS_PLL_P_FACTOR_CFG_2_MASK);
+	} else {
+		wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_SWALLOW_EN,
+				     PLLSH_WCS_PLL_SWALLOW_EN_VAL2);
+	}
+}
+
+static void wl18xx_boot_soft_reset(struct wl1271 *wl)
+{
+	/* disable Rx/Tx */
+	wl1271_write32(wl, WL18XX_ENABLE, 0x0);
+
+	/* disable auto calibration on start*/
+	wl1271_write32(wl, WL18XX_SPARE_A2, 0xffff);
+}
+
+static int wl18xx_pre_boot(struct wl1271 *wl)
+{
+	wl18xx_set_clk(wl);
+
+	/* Continue the ELP wake up sequence */
+	wl1271_write32(wl, WL18XX_WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL);
+	udelay(500);
+
+	wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
+
+	/* Disable interrupts */
+	wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL);
+
+	wl18xx_boot_soft_reset(wl);
+
+	return 0;
+}
+
+static void wl18xx_pre_upload(struct wl1271 *wl)
+{
+	u32 tmp;
+
+	wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
+
+	/* TODO: check if this is all needed */
+	wl1271_write32(wl, WL18XX_EEPROMLESS_IND, WL18XX_EEPROMLESS_IND);
+
+	tmp = wlcore_read_reg(wl, REG_CHIP_ID_B);
+
+	wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp);
+
+	tmp = wl1271_read32(wl, WL18XX_SCR_PAD2);
+}
+
+static void wl18xx_set_mac_and_phy(struct wl1271 *wl)
+{
+	struct wl18xx_priv *priv = wl->priv;
+	size_t len;
+
+	/* the parameters struct is smaller for PG1 */
+	if (wl->chip.id == CHIP_ID_185x_PG10)
+		len = offsetof(struct wl18xx_mac_and_phy_params, psat) + 1;
+	else
+		len = sizeof(struct wl18xx_mac_and_phy_params);
+
+	wlcore_set_partition(wl, &wl->ptable[PART_PHY_INIT]);
+	wl1271_write(wl, WL18XX_PHY_INIT_MEM_ADDR, (u8 *)&priv->conf.phy, len,
+		     false);
+}
+
+static void wl18xx_enable_interrupts(struct wl1271 *wl)
+{
+	u32 event_mask, intr_mask;
+
+	if (wl->chip.id == CHIP_ID_185x_PG10) {
+		event_mask = WL18XX_ACX_EVENTS_VECTOR_PG1;
+		intr_mask = WL18XX_INTR_MASK_PG1;
+	} else {
+		event_mask = WL18XX_ACX_EVENTS_VECTOR_PG2;
+		intr_mask = WL18XX_INTR_MASK_PG2;
+	}
+
+	wlcore_write_reg(wl, REG_INTERRUPT_MASK, event_mask);
+
+	wlcore_enable_interrupts(wl);
+	wlcore_write_reg(wl, REG_INTERRUPT_MASK,
+			 WL1271_ACX_INTR_ALL & ~intr_mask);
+}
+
+static int wl18xx_boot(struct wl1271 *wl)
+{
+	int ret;
+
+	ret = wl18xx_pre_boot(wl);
+	if (ret < 0)
+		goto out;
+
+	wl18xx_pre_upload(wl);
+
+	ret = wlcore_boot_upload_firmware(wl);
+	if (ret < 0)
+		goto out;
+
+	wl18xx_set_mac_and_phy(wl);
+
+	ret = wlcore_boot_run_firmware(wl);
+	if (ret < 0)
+		goto out;
+
+	wl18xx_enable_interrupts(wl);
+
+out:
+	return ret;
+}
+
+static void wl18xx_trigger_cmd(struct wl1271 *wl, int cmd_box_addr,
+			       void *buf, size_t len)
+{
+	struct wl18xx_priv *priv = wl->priv;
+
+	memcpy(priv->cmd_buf, buf, len);
+	memset(priv->cmd_buf + len, 0, WL18XX_CMD_MAX_SIZE - len);
+
+	wl1271_write(wl, cmd_box_addr, priv->cmd_buf, WL18XX_CMD_MAX_SIZE,
+		     false);
+}
+
+static void wl18xx_ack_event(struct wl1271 *wl)
+{
+	wlcore_write_reg(wl, REG_INTERRUPT_TRIG, WL18XX_INTR_TRIG_EVENT_ACK);
+}
+
+static u32 wl18xx_calc_tx_blocks(struct wl1271 *wl, u32 len, u32 spare_blks)
+{
+	u32 blk_size = WL18XX_TX_HW_BLOCK_SIZE;
+	return (len + blk_size - 1) / blk_size + spare_blks;
+}
+
+static void
+wl18xx_set_tx_desc_blocks(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc,
+			  u32 blks, u32 spare_blks)
+{
+	desc->wl18xx_mem.total_mem_blocks = blks;
+}
+
+static void
+wl18xx_set_tx_desc_data_len(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc,
+			    struct sk_buff *skb)
+{
+	desc->length = cpu_to_le16(skb->len);
+
+	/* if only the last frame is to be padded, we unset this bit on Tx */
+	if (wl->quirks & WLCORE_QUIRK_TX_PAD_LAST_FRAME)
+		desc->wl18xx_mem.ctrl = WL18XX_TX_CTRL_NOT_PADDED;
+	else
+		desc->wl18xx_mem.ctrl = 0;
+
+	wl1271_debug(DEBUG_TX, "tx_fill_hdr: hlid: %d "
+		     "len: %d life: %d mem: %d", desc->hlid,
+		     le16_to_cpu(desc->length),
+		     le16_to_cpu(desc->life_time),
+		     desc->wl18xx_mem.total_mem_blocks);
+}
+
+static enum wl_rx_buf_align
+wl18xx_get_rx_buf_align(struct wl1271 *wl, u32 rx_desc)
+{
+	if (rx_desc & RX_BUF_PADDED_PAYLOAD)
+		return WLCORE_RX_BUF_PADDED;
+
+	return WLCORE_RX_BUF_ALIGNED;
+}
+
+static u32 wl18xx_get_rx_packet_len(struct wl1271 *wl, void *rx_data,
+				    u32 data_len)
+{
+	struct wl1271_rx_descriptor *desc = rx_data;
+
+	/* invalid packet */
+	if (data_len < sizeof(*desc))
+		return 0;
+
+	return data_len - sizeof(*desc);
+}
+
+static void wl18xx_tx_immediate_completion(struct wl1271 *wl)
+{
+	wl18xx_tx_immediate_complete(wl);
+}
+
+static int wl18xx_set_host_cfg_bitmap(struct wl1271 *wl, u32 extra_mem_blk)
+{
+	int ret;
+	u32 sdio_align_size = 0;
+	u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE |
+			      HOST_IF_CFG_ADD_RX_ALIGNMENT;
+
+	/* Enable Tx SDIO padding */
+	if (wl->quirks & WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN) {
+		host_cfg_bitmap |= HOST_IF_CFG_TX_PAD_TO_SDIO_BLK;
+		sdio_align_size = WL12XX_BUS_BLOCK_SIZE;
+	}
+
+	/* Enable Rx SDIO padding */
+	if (wl->quirks & WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN) {
+		host_cfg_bitmap |= HOST_IF_CFG_RX_PAD_TO_SDIO_BLK;
+		sdio_align_size = WL12XX_BUS_BLOCK_SIZE;
+	}
+
+	ret = wl18xx_acx_host_if_cfg_bitmap(wl, host_cfg_bitmap,
+					    sdio_align_size, extra_mem_blk,
+					    WL18XX_HOST_IF_LEN_SIZE_FIELD);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int wl18xx_hw_init(struct wl1271 *wl)
+{
+	int ret;
+	struct wl18xx_priv *priv = wl->priv;
+
+	/* (re)init private structures. Relevant on recovery as well. */
+	priv->last_fw_rls_idx = 0;
+	priv->extra_spare_vif_count = 0;
+
+	/* set the default amount of spare blocks in the bitmap */
+	ret = wl18xx_set_host_cfg_bitmap(wl, WL18XX_TX_HW_BLOCK_SPARE);
+	if (ret < 0)
+		return ret;
+
+	if (checksum_param) {
+		ret = wl18xx_acx_set_checksum_state(wl);
+		if (ret != 0)
+			return ret;
+	}
+
+	return ret;
+}
+
+static void wl18xx_set_tx_desc_csum(struct wl1271 *wl,
+				    struct wl1271_tx_hw_descr *desc,
+				    struct sk_buff *skb)
+{
+	u32 ip_hdr_offset;
+	struct iphdr *ip_hdr;
+
+	if (!checksum_param) {
+		desc->wl18xx_checksum_data = 0;
+		return;
+	}
+
+	if (skb->ip_summed != CHECKSUM_PARTIAL) {
+		desc->wl18xx_checksum_data = 0;
+		return;
+	}
+
+	ip_hdr_offset = skb_network_header(skb) - skb_mac_header(skb);
+	if (WARN_ON(ip_hdr_offset >= (1<<7))) {
+		desc->wl18xx_checksum_data = 0;
+		return;
+	}
+
+	desc->wl18xx_checksum_data = ip_hdr_offset << 1;
+
+	/* FW is interested only in the LSB of the protocol  TCP=0 UDP=1 */
+	ip_hdr = (void *)skb_network_header(skb);
+	desc->wl18xx_checksum_data |= (ip_hdr->protocol & 0x01);
+}
+
+static void wl18xx_set_rx_csum(struct wl1271 *wl,
+			       struct wl1271_rx_descriptor *desc,
+			       struct sk_buff *skb)
+{
+	if (desc->status & WL18XX_RX_CHECKSUM_MASK)
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+}
+
+/*
+ * TODO: instead of having these two functions to get the rate mask,
+ * we should modify the wlvif->rate_set instead
+ */
+static u32 wl18xx_sta_get_ap_rate_mask(struct wl1271 *wl,
+				       struct wl12xx_vif *wlvif)
+{
+	u32 hw_rate_set = wlvif->rate_set;
+
+	if (wlvif->channel_type == NL80211_CHAN_HT40MINUS ||
+	    wlvif->channel_type == NL80211_CHAN_HT40PLUS) {
+		wl1271_debug(DEBUG_ACX, "using wide channel rate mask");
+		hw_rate_set |= CONF_TX_RATE_USE_WIDE_CHAN;
+
+		/* we don't support MIMO in wide-channel mode */
+		hw_rate_set &= ~CONF_TX_MIMO_RATES;
+	}
+
+	return hw_rate_set;
+}
+
+static u32 wl18xx_ap_get_mimo_wide_rate_mask(struct wl1271 *wl,
+					     struct wl12xx_vif *wlvif)
+{
+	if ((wlvif->channel_type == NL80211_CHAN_HT40MINUS ||
+	     wlvif->channel_type == NL80211_CHAN_HT40PLUS) &&
+	    !strcmp(ht_mode_param, "wide")) {
+		wl1271_debug(DEBUG_ACX, "using wide channel rate mask");
+		return CONF_TX_RATE_USE_WIDE_CHAN;
+	} else if (!strcmp(ht_mode_param, "mimo")) {
+		wl1271_debug(DEBUG_ACX, "using MIMO rate mask");
+
+		/*
+		 * PG 1.0 has some problems with MCS_13, so disable it
+		 *
+		 * TODO: instead of hacking this in here, we should
+		 * make it more general and change a bit in the
+		 * wlvif->rate_set instead.
+		 */
+		if (wl->chip.id == CHIP_ID_185x_PG10)
+			return CONF_TX_MIMO_RATES & ~CONF_HW_BIT_RATE_MCS_13;
+
+		return CONF_TX_MIMO_RATES;
+	} else {
+		return 0;
+	}
+}
+
+static s8 wl18xx_get_pg_ver(struct wl1271 *wl)
+{
+	u32 fuse;
+
+	wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]);
+
+	fuse = wl1271_read32(wl, WL18XX_REG_FUSE_DATA_1_3);
+	fuse = (fuse & WL18XX_PG_VER_MASK) >> WL18XX_PG_VER_OFFSET;
+
+	wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
+
+	return (s8)fuse;
+}
+
+#define WL18XX_CONF_FILE_NAME "ti-connectivity/wl18xx-conf.bin"
+static int wl18xx_conf_init(struct wl1271 *wl, struct device *dev)
+{
+	struct wl18xx_priv *priv = wl->priv;
+	struct wlcore_conf_file *conf_file;
+	const struct firmware *fw;
+	int ret;
+
+	ret = request_firmware(&fw, WL18XX_CONF_FILE_NAME, dev);
+	if (ret < 0) {
+		wl1271_error("could not get configuration binary %s: %d",
+			     WL18XX_CONF_FILE_NAME, ret);
+		goto out_fallback;
+	}
+
+	if (fw->size != WL18XX_CONF_SIZE) {
+		wl1271_error("configuration binary file size is wrong, "
+			     "expected %ld got %zd",
+			     WL18XX_CONF_SIZE, fw->size);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	conf_file = (struct wlcore_conf_file *) fw->data;
+
+	if (conf_file->header.magic != cpu_to_le32(WL18XX_CONF_MAGIC)) {
+		wl1271_error("configuration binary file magic number mismatch, "
+			     "expected 0x%0x got 0x%0x", WL18XX_CONF_MAGIC,
+			     conf_file->header.magic);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (conf_file->header.version != cpu_to_le32(WL18XX_CONF_VERSION)) {
+		wl1271_error("configuration binary file version not supported, "
+			     "expected 0x%08x got 0x%08x",
+			     WL18XX_CONF_VERSION, conf_file->header.version);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	memcpy(&wl->conf, &conf_file->core, sizeof(wl18xx_conf));
+	memcpy(&priv->conf, &conf_file->priv, sizeof(priv->conf));
+
+	goto out;
+
+out_fallback:
+	wl1271_warning("falling back to default config");
+
+	/* apply driver default configuration */
+	memcpy(&wl->conf, &wl18xx_conf, sizeof(wl18xx_conf));
+	/* apply default private configuration */
+	memcpy(&priv->conf, &wl18xx_default_priv_conf, sizeof(priv->conf));
+
+	/* For now we just fallback */
+	return 0;
+
+out:
+	release_firmware(fw);
+	return ret;
+}
+
+static int wl18xx_plt_init(struct wl1271 *wl)
+{
+	wl1271_write32(wl, WL18XX_SCR_PAD8, WL18XX_SCR_PAD8_PLT);
+
+	return wl->ops->boot(wl);
+}
+
+static void wl18xx_get_mac(struct wl1271 *wl)
+{
+	u32 mac1, mac2;
+
+	wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]);
+
+	mac1 = wl1271_read32(wl, WL18XX_REG_FUSE_BD_ADDR_1);
+	mac2 = wl1271_read32(wl, WL18XX_REG_FUSE_BD_ADDR_2);
+
+	/* these are the two parts of the BD_ADDR */
+	wl->fuse_oui_addr = ((mac2 & 0xffff) << 8) +
+		((mac1 & 0xff000000) >> 24);
+	wl->fuse_nic_addr = (mac1 & 0xffffff);
+
+	wlcore_set_partition(wl, &wl->ptable[PART_DOWN]);
+}
+
+static int wl18xx_handle_static_data(struct wl1271 *wl,
+				     struct wl1271_static_data *static_data)
+{
+	struct wl18xx_static_data_priv *static_data_priv =
+		(struct wl18xx_static_data_priv *) static_data->priv;
+
+	wl1271_info("PHY firmware version: %s", static_data_priv->phy_version);
+
+	return 0;
+}
+
+static int wl18xx_get_spare_blocks(struct wl1271 *wl, bool is_gem)
+{
+	struct wl18xx_priv *priv = wl->priv;
+
+	/* If we have VIFs requiring extra spare, indulge them */
+	if (priv->extra_spare_vif_count)
+		return WL18XX_TX_HW_EXTRA_BLOCK_SPARE;
+
+	return WL18XX_TX_HW_BLOCK_SPARE;
+}
+
+static int wl18xx_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
+			  struct ieee80211_vif *vif,
+			  struct ieee80211_sta *sta,
+			  struct ieee80211_key_conf *key_conf)
+{
+	struct wl18xx_priv *priv = wl->priv;
+	bool change_spare = false;
+	int ret;
+
+	/*
+	 * when adding the first or removing the last GEM/TKIP interface,
+	 * we have to adjust the number of spare blocks.
+	 */
+	change_spare = (key_conf->cipher == WL1271_CIPHER_SUITE_GEM ||
+		key_conf->cipher == WLAN_CIPHER_SUITE_TKIP) &&
+		((priv->extra_spare_vif_count == 0 && cmd == SET_KEY) ||
+		 (priv->extra_spare_vif_count == 1 && cmd == DISABLE_KEY));
+
+	/* no need to change spare - just regular set_key */
+	if (!change_spare)
+		return wlcore_set_key(wl, cmd, vif, sta, key_conf);
+
+	/*
+	 * stop the queues and flush to ensure the next packets are
+	 * in sync with FW spare block accounting
+	 */
+	wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_SPARE_BLK);
+	wl1271_tx_flush(wl);
+
+	ret = wlcore_set_key(wl, cmd, vif, sta, key_conf);
+	if (ret < 0)
+		goto out;
+
+	/* key is now set, change the spare blocks */
+	if (cmd == SET_KEY) {
+		ret = wl18xx_set_host_cfg_bitmap(wl,
+					WL18XX_TX_HW_EXTRA_BLOCK_SPARE);
+		if (ret < 0)
+			goto out;
+
+		priv->extra_spare_vif_count++;
+	} else {
+		ret = wl18xx_set_host_cfg_bitmap(wl,
+					WL18XX_TX_HW_BLOCK_SPARE);
+		if (ret < 0)
+			goto out;
+
+		priv->extra_spare_vif_count--;
+	}
+
+out:
+	wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_SPARE_BLK);
+	return ret;
+}
+
+static u32 wl18xx_pre_pkt_send(struct wl1271 *wl,
+			       u32 buf_offset, u32 last_len)
+{
+	if (wl->quirks & WLCORE_QUIRK_TX_PAD_LAST_FRAME) {
+		struct wl1271_tx_hw_descr *last_desc;
+
+		/* get the last TX HW descriptor written to the aggr buf */
+		last_desc = (struct wl1271_tx_hw_descr *)(wl->aggr_buf +
+							buf_offset - last_len);
+
+		/* the last frame is padded up to an SDIO block */
+		last_desc->wl18xx_mem.ctrl &= ~WL18XX_TX_CTRL_NOT_PADDED;
+		return ALIGN(buf_offset, WL12XX_BUS_BLOCK_SIZE);
+	}
+
+	/* no modifications */
+	return buf_offset;
+}
+
+static struct wlcore_ops wl18xx_ops = {
+	.identify_chip	= wl18xx_identify_chip,
+	.boot		= wl18xx_boot,
+	.plt_init	= wl18xx_plt_init,
+	.trigger_cmd	= wl18xx_trigger_cmd,
+	.ack_event	= wl18xx_ack_event,
+	.calc_tx_blocks = wl18xx_calc_tx_blocks,
+	.set_tx_desc_blocks = wl18xx_set_tx_desc_blocks,
+	.set_tx_desc_data_len = wl18xx_set_tx_desc_data_len,
+	.get_rx_buf_align = wl18xx_get_rx_buf_align,
+	.get_rx_packet_len = wl18xx_get_rx_packet_len,
+	.tx_immediate_compl = wl18xx_tx_immediate_completion,
+	.tx_delayed_compl = NULL,
+	.hw_init	= wl18xx_hw_init,
+	.set_tx_desc_csum = wl18xx_set_tx_desc_csum,
+	.get_pg_ver	= wl18xx_get_pg_ver,
+	.set_rx_csum = wl18xx_set_rx_csum,
+	.sta_get_ap_rate_mask = wl18xx_sta_get_ap_rate_mask,
+	.ap_get_mimo_wide_rate_mask = wl18xx_ap_get_mimo_wide_rate_mask,
+	.get_mac	= wl18xx_get_mac,
+	.debugfs_init	= wl18xx_debugfs_add_files,
+	.handle_static_data	= wl18xx_handle_static_data,
+	.get_spare_blocks = wl18xx_get_spare_blocks,
+	.set_key	= wl18xx_set_key,
+	.pre_pkt_send	= wl18xx_pre_pkt_send,
+};
+
+/* HT cap appropriate for wide channels */
+static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap = {
+	.cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 |
+	       IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_DSSSCCK40,
+	.ht_supported = true,
+	.ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
+	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
+	.mcs = {
+		.rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
+		.rx_highest = cpu_to_le16(150),
+		.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+		},
+};
+
+/* HT cap appropriate for SISO 20 */
+static struct ieee80211_sta_ht_cap wl18xx_siso20_ht_cap = {
+	.cap = IEEE80211_HT_CAP_SGI_20,
+	.ht_supported = true,
+	.ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
+	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
+	.mcs = {
+		.rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
+		.rx_highest = cpu_to_le16(72),
+		.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+		},
+};
+
+/* HT cap appropriate for MIMO rates in 20mhz channel */
+static struct ieee80211_sta_ht_cap wl18xx_mimo_ht_cap_2ghz = {
+	.cap = IEEE80211_HT_CAP_SGI_20,
+	.ht_supported = true,
+	.ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
+	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
+	.mcs = {
+		.rx_mask = { 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, },
+		.rx_highest = cpu_to_le16(144),
+		.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+		},
+};
+
+static struct ieee80211_sta_ht_cap wl18xx_mimo_ht_cap_5ghz = {
+	.cap = IEEE80211_HT_CAP_SGI_20,
+	.ht_supported = true,
+	.ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
+	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
+	.mcs = {
+		.rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
+		.rx_highest = cpu_to_le16(72),
+		.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+		},
+};
+
+static int __devinit wl18xx_probe(struct platform_device *pdev)
+{
+	struct wl1271 *wl;
+	struct ieee80211_hw *hw;
+	struct wl18xx_priv *priv;
+	int ret;
+
+	hw = wlcore_alloc_hw(sizeof(*priv));
+	if (IS_ERR(hw)) {
+		wl1271_error("can't allocate hw");
+		ret = PTR_ERR(hw);
+		goto out;
+	}
+
+	wl = hw->priv;
+	priv = wl->priv;
+	wl->ops = &wl18xx_ops;
+	wl->ptable = wl18xx_ptable;
+	wl->rtable = wl18xx_rtable;
+	wl->num_tx_desc = 32;
+	wl->num_rx_desc = 16;
+	wl->band_rate_to_idx = wl18xx_band_rate_to_idx;
+	wl->hw_tx_rate_tbl_size = WL18XX_CONF_HW_RXTX_RATE_MAX;
+	wl->hw_min_ht_rate = WL18XX_CONF_HW_RXTX_RATE_MCS0;
+	wl->fw_status_priv_len = sizeof(struct wl18xx_fw_status_priv);
+	wl->stats.fw_stats_len = sizeof(struct wl18xx_acx_statistics);
+	wl->static_data_priv_len = sizeof(struct wl18xx_static_data_priv);
+
+	if (!strcmp(ht_mode_param, "wide")) {
+		memcpy(&wl->ht_cap[IEEE80211_BAND_2GHZ],
+		       &wl18xx_siso40_ht_cap,
+		       sizeof(wl18xx_siso40_ht_cap));
+		memcpy(&wl->ht_cap[IEEE80211_BAND_5GHZ],
+		       &wl18xx_siso40_ht_cap,
+		       sizeof(wl18xx_siso40_ht_cap));
+	} else if (!strcmp(ht_mode_param, "mimo")) {
+		memcpy(&wl->ht_cap[IEEE80211_BAND_2GHZ],
+		       &wl18xx_mimo_ht_cap_2ghz,
+		       sizeof(wl18xx_mimo_ht_cap_2ghz));
+		memcpy(&wl->ht_cap[IEEE80211_BAND_5GHZ],
+		       &wl18xx_mimo_ht_cap_5ghz,
+		       sizeof(wl18xx_mimo_ht_cap_5ghz));
+	} else if (!strcmp(ht_mode_param, "siso20")) {
+		memcpy(&wl->ht_cap[IEEE80211_BAND_2GHZ],
+		       &wl18xx_siso20_ht_cap,
+		       sizeof(wl18xx_siso20_ht_cap));
+		memcpy(&wl->ht_cap[IEEE80211_BAND_5GHZ],
+		       &wl18xx_siso20_ht_cap,
+		       sizeof(wl18xx_siso20_ht_cap));
+	} else {
+		wl1271_error("invalid ht_mode '%s'", ht_mode_param);
+		ret = -EINVAL;
+		goto out_free;
+	}
+
+	ret = wl18xx_conf_init(wl, &pdev->dev);
+	if (ret < 0)
+		goto out_free;
+
+	if (!strcmp(board_type_param, "fpga")) {
+		priv->conf.phy.board_type = BOARD_TYPE_FPGA_18XX;
+	} else if (!strcmp(board_type_param, "hdk")) {
+		priv->conf.phy.board_type = BOARD_TYPE_HDK_18XX;
+		/* HACK! Just for now we hardcode HDK to 0x06 */
+		priv->conf.phy.low_band_component_type = 0x06;
+	} else if (!strcmp(board_type_param, "dvp")) {
+		priv->conf.phy.board_type = BOARD_TYPE_DVP_18XX;
+	} else if (!strcmp(board_type_param, "evb")) {
+		priv->conf.phy.board_type = BOARD_TYPE_EVB_18XX;
+	} else if (!strcmp(board_type_param, "com8")) {
+		priv->conf.phy.board_type = BOARD_TYPE_COM8_18XX;
+		/* HACK! Just for now we hardcode COM8 to 0x06 */
+		priv->conf.phy.low_band_component_type = 0x06;
+	} else {
+		wl1271_error("invalid board type '%s'", board_type_param);
+		ret = -EINVAL;
+		goto out_free;
+	}
+
+	/* If the module param is set, update it in conf */
+	if (low_band_component_param != -1)
+		priv->conf.phy.low_band_component = low_band_component_param;
+	if (low_band_component_type_param != -1)
+		priv->conf.phy.low_band_component_type =
+			low_band_component_type_param;
+	if (high_band_component_param != -1)
+		priv->conf.phy.high_band_component = high_band_component_param;
+	if (high_band_component_type_param != -1)
+		priv->conf.phy.high_band_component_type =
+			high_band_component_type_param;
+	if (pwr_limit_reference_11_abg_param != -1)
+		priv->conf.phy.pwr_limit_reference_11_abg =
+			pwr_limit_reference_11_abg_param;
+	if (n_antennas_2_param != -1)
+		priv->conf.phy.number_of_assembled_ant2_4 = n_antennas_2_param;
+	if (n_antennas_5_param != -1)
+		priv->conf.phy.number_of_assembled_ant5 = n_antennas_5_param;
+	if (dc2dc_param != -1)
+		priv->conf.phy.external_pa_dc2dc = dc2dc_param;
+
+	if (!checksum_param) {
+		wl18xx_ops.set_rx_csum = NULL;
+		wl18xx_ops.init_vif = NULL;
+	}
+
+	wl->enable_11a = enable_11a_param;
+
+	return wlcore_probe(wl, pdev);
+
+out_free:
+	wlcore_free_hw(wl);
+out:
+	return ret;
+}
+
+static const struct platform_device_id wl18xx_id_table[] __devinitconst = {
+	{ "wl18xx", 0 },
+	{  } /* Terminating Entry */
+};
+MODULE_DEVICE_TABLE(platform, wl18xx_id_table);
+
+static struct platform_driver wl18xx_driver = {
+	.probe		= wl18xx_probe,
+	.remove		= __devexit_p(wlcore_remove),
+	.id_table	= wl18xx_id_table,
+	.driver = {
+		.name	= "wl18xx_driver",
+		.owner	= THIS_MODULE,
+	}
+};
+
+static int __init wl18xx_init(void)
+{
+	return platform_driver_register(&wl18xx_driver);
+}
+module_init(wl18xx_init);
+
+static void __exit wl18xx_exit(void)
+{
+	platform_driver_unregister(&wl18xx_driver);
+}
+module_exit(wl18xx_exit);
+
+module_param_named(ht_mode, ht_mode_param, charp, S_IRUSR);
+MODULE_PARM_DESC(ht_mode, "Force HT mode: wide (default), mimo or siso20");
+
+module_param_named(board_type, board_type_param, charp, S_IRUSR);
+MODULE_PARM_DESC(board_type, "Board type: fpga, hdk (default), evb, com8 or "
+		 "dvp");
+
+module_param_named(checksum, checksum_param, bool, S_IRUSR);
+MODULE_PARM_DESC(checksum, "Enable TCP checksum: boolean (defaults to false)");
+
+module_param_named(enable_11a, enable_11a_param, bool, S_IRUSR);
+MODULE_PARM_DESC(enable_11a, "Enable 11a (5GHz): boolean (defaults to true)");
+
+module_param_named(dc2dc, dc2dc_param, int, S_IRUSR);
+MODULE_PARM_DESC(dc2dc, "External DC2DC: u8 (defaults to 0)");
+
+module_param_named(n_antennas_2, n_antennas_2_param, int, S_IRUSR);
+MODULE_PARM_DESC(n_antennas_2,
+		 "Number of installed 2.4GHz antennas: 1 (default) or 2");
+
+module_param_named(n_antennas_5, n_antennas_5_param, int, S_IRUSR);
+MODULE_PARM_DESC(n_antennas_5,
+		 "Number of installed 5GHz antennas: 1 (default) or 2");
+
+module_param_named(low_band_component, low_band_component_param, int,
+		   S_IRUSR);
+MODULE_PARM_DESC(low_band_component, "Low band component: u8 "
+		 "(default is 0x01)");
+
+module_param_named(low_band_component_type, low_band_component_type_param,
+		   int, S_IRUSR);
+MODULE_PARM_DESC(low_band_component_type, "Low band component type: u8 "
+		 "(default is 0x05 or 0x06 depending on the board_type)");
+
+module_param_named(high_band_component, high_band_component_param, int,
+		   S_IRUSR);
+MODULE_PARM_DESC(high_band_component, "High band component: u8, "
+		 "(default is 0x01)");
+
+module_param_named(high_band_component_type, high_band_component_type_param,
+		   int, S_IRUSR);
+MODULE_PARM_DESC(high_band_component_type, "High band component type: u8 "
+		 "(default is 0x09)");
+
+module_param_named(pwr_limit_reference_11_abg,
+		   pwr_limit_reference_11_abg_param, int, S_IRUSR);
+MODULE_PARM_DESC(pwr_limit_reference_11_abg, "Power limit reference: u8 "
+		 "(default is 0xc8)");
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
+MODULE_FIRMWARE(WL18XX_FW_NAME);

+ 191 - 0
drivers/net/wireless/ti/wl18xx/reg.h

@@ -0,0 +1,191 @@
+/*
+ * This file is part of wlcore
+ *
+ * Copyright (C) 2011 Texas Instruments Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __REG_H__
+#define __REG_H__
+
+#define WL18XX_REGISTERS_BASE      0x00800000
+#define WL18XX_CODE_BASE           0x00000000
+#define WL18XX_DATA_BASE           0x00400000
+#define WL18XX_DOUBLE_BUFFER_BASE  0x00600000
+#define WL18XX_MCU_KEY_SEARCH_BASE 0x00700000
+#define WL18XX_PHY_BASE            0x00900000
+#define WL18XX_TOP_OCP_BASE        0x00A00000
+#define WL18XX_PACKET_RAM_BASE     0x00B00000
+#define WL18XX_HOST_BASE           0x00C00000
+
+#define WL18XX_REGISTERS_DOWN_SIZE 0x0000B000
+
+#define WL18XX_REG_BOOT_PART_START 0x00802000
+#define WL18XX_REG_BOOT_PART_SIZE  0x00014578
+
+#define WL18XX_PHY_INIT_MEM_ADDR   0x80926000
+
+#define WL18XX_SDIO_WSPI_BASE		(WL18XX_REGISTERS_BASE)
+#define WL18XX_REG_CONFIG_BASE		(WL18XX_REGISTERS_BASE + 0x02000)
+#define WL18XX_WGCM_REGS_BASE		(WL18XX_REGISTERS_BASE + 0x03000)
+#define WL18XX_ENC_BASE			(WL18XX_REGISTERS_BASE + 0x04000)
+#define WL18XX_INTERRUPT_BASE		(WL18XX_REGISTERS_BASE + 0x05000)
+#define WL18XX_UART_BASE		(WL18XX_REGISTERS_BASE + 0x06000)
+#define WL18XX_WELP_BASE		(WL18XX_REGISTERS_BASE + 0x07000)
+#define WL18XX_TCP_CKSM_BASE		(WL18XX_REGISTERS_BASE + 0x08000)
+#define WL18XX_FIFO_BASE		(WL18XX_REGISTERS_BASE + 0x09000)
+#define WL18XX_OCP_BRIDGE_BASE		(WL18XX_REGISTERS_BASE + 0x0A000)
+#define WL18XX_PMAC_RX_BASE		(WL18XX_REGISTERS_BASE + 0x14800)
+#define WL18XX_PMAC_ACM_BASE		(WL18XX_REGISTERS_BASE + 0x14C00)
+#define WL18XX_PMAC_TX_BASE		(WL18XX_REGISTERS_BASE + 0x15000)
+#define WL18XX_PMAC_CSR_BASE		(WL18XX_REGISTERS_BASE + 0x15400)
+
+#define WL18XX_REG_ECPU_CONTROL		(WL18XX_REGISTERS_BASE + 0x02004)
+#define WL18XX_REG_INTERRUPT_NO_CLEAR	(WL18XX_REGISTERS_BASE + 0x050E8)
+#define WL18XX_REG_INTERRUPT_ACK	(WL18XX_REGISTERS_BASE + 0x050F0)
+#define WL18XX_REG_INTERRUPT_TRIG	(WL18XX_REGISTERS_BASE + 0x5074)
+#define WL18XX_REG_INTERRUPT_TRIG_H	(WL18XX_REGISTERS_BASE + 0x5078)
+#define WL18XX_REG_INTERRUPT_MASK	(WL18XX_REGISTERS_BASE + 0x0050DC)
+
+#define WL18XX_REG_CHIP_ID_B		(WL18XX_REGISTERS_BASE + 0x01542C)
+
+#define WL18XX_SLV_MEM_DATA		(WL18XX_HOST_BASE + 0x0018)
+#define WL18XX_SLV_REG_DATA		(WL18XX_HOST_BASE + 0x0008)
+
+/* Scratch Pad registers*/
+#define WL18XX_SCR_PAD0			(WL18XX_REGISTERS_BASE + 0x0154EC)
+#define WL18XX_SCR_PAD1			(WL18XX_REGISTERS_BASE + 0x0154F0)
+#define WL18XX_SCR_PAD2			(WL18XX_REGISTERS_BASE + 0x0154F4)
+#define WL18XX_SCR_PAD3			(WL18XX_REGISTERS_BASE + 0x0154F8)
+#define WL18XX_SCR_PAD4			(WL18XX_REGISTERS_BASE + 0x0154FC)
+#define WL18XX_SCR_PAD4_SET		(WL18XX_REGISTERS_BASE + 0x015504)
+#define WL18XX_SCR_PAD4_CLR		(WL18XX_REGISTERS_BASE + 0x015500)
+#define WL18XX_SCR_PAD5			(WL18XX_REGISTERS_BASE + 0x015508)
+#define WL18XX_SCR_PAD5_SET		(WL18XX_REGISTERS_BASE + 0x015510)
+#define WL18XX_SCR_PAD5_CLR		(WL18XX_REGISTERS_BASE + 0x01550C)
+#define WL18XX_SCR_PAD6			(WL18XX_REGISTERS_BASE + 0x015514)
+#define WL18XX_SCR_PAD7			(WL18XX_REGISTERS_BASE + 0x015518)
+#define WL18XX_SCR_PAD8			(WL18XX_REGISTERS_BASE + 0x01551C)
+#define WL18XX_SCR_PAD9			(WL18XX_REGISTERS_BASE + 0x015520)
+
+/* Spare registers*/
+#define WL18XX_SPARE_A1			(WL18XX_REGISTERS_BASE + 0x002194)
+#define WL18XX_SPARE_A2			(WL18XX_REGISTERS_BASE + 0x002198)
+#define WL18XX_SPARE_A3			(WL18XX_REGISTERS_BASE + 0x00219C)
+#define WL18XX_SPARE_A4			(WL18XX_REGISTERS_BASE + 0x0021A0)
+#define WL18XX_SPARE_A5			(WL18XX_REGISTERS_BASE + 0x0021A4)
+#define WL18XX_SPARE_A6			(WL18XX_REGISTERS_BASE + 0x0021A8)
+#define WL18XX_SPARE_A7			(WL18XX_REGISTERS_BASE + 0x0021AC)
+#define WL18XX_SPARE_A8			(WL18XX_REGISTERS_BASE + 0x0021B0)
+#define WL18XX_SPARE_B1			(WL18XX_REGISTERS_BASE + 0x015524)
+#define WL18XX_SPARE_B2			(WL18XX_REGISTERS_BASE + 0x015528)
+#define WL18XX_SPARE_B3			(WL18XX_REGISTERS_BASE + 0x01552C)
+#define WL18XX_SPARE_B4			(WL18XX_REGISTERS_BASE + 0x015530)
+#define WL18XX_SPARE_B5			(WL18XX_REGISTERS_BASE + 0x015534)
+#define WL18XX_SPARE_B6			(WL18XX_REGISTERS_BASE + 0x015538)
+#define WL18XX_SPARE_B7			(WL18XX_REGISTERS_BASE + 0x01553C)
+#define WL18XX_SPARE_B8			(WL18XX_REGISTERS_BASE + 0x015540)
+
+#define WL18XX_REG_COMMAND_MAILBOX_PTR	(WL18XX_SCR_PAD0)
+#define WL18XX_REG_EVENT_MAILBOX_PTR	(WL18XX_SCR_PAD1)
+#define WL18XX_EEPROMLESS_IND		(WL18XX_SCR_PAD4)
+
+#define WL18XX_WELP_ARM_COMMAND		(WL18XX_REGISTERS_BASE + 0x7100)
+#define WL18XX_ENABLE			(WL18XX_REGISTERS_BASE + 0x01543C)
+
+/* PRCM registers */
+#define PLATFORM_DETECTION		0xA0E3E0
+#define OCS_EN				0xA02080
+#define PRIMARY_CLK_DETECT		0xA020A6
+#define PLLSH_WCS_PLL_N			0xA02362
+#define PLLSH_WCS_PLL_M			0xA02360
+#define PLLSH_WCS_PLL_Q_FACTOR_CFG_1	0xA02364
+#define PLLSH_WCS_PLL_Q_FACTOR_CFG_2	0xA02366
+#define PLLSH_WCS_PLL_P_FACTOR_CFG_1	0xA02368
+#define PLLSH_WCS_PLL_P_FACTOR_CFG_2	0xA0236A
+#define PLLSH_WCS_PLL_SWALLOW_EN	0xA0236C
+#define PLLSH_WL_PLL_EN			0xA02392
+
+#define PLLSH_WCS_PLL_Q_FACTOR_CFG_1_MASK	0xFFFF
+#define PLLSH_WCS_PLL_Q_FACTOR_CFG_2_MASK	0x007F
+#define PLLSH_WCS_PLL_P_FACTOR_CFG_1_MASK	0xFFFF
+#define PLLSH_WCS_PLL_P_FACTOR_CFG_2_MASK	0x000F
+
+#define PLLSH_WCS_PLL_SWALLOW_EN_VAL1	0x1
+#define PLLSH_WCS_PLL_SWALLOW_EN_VAL2	0x12
+
+#define WL18XX_REG_FUSE_DATA_1_3	0xA0260C
+#define WL18XX_PG_VER_MASK		0x70
+#define WL18XX_PG_VER_OFFSET		4
+
+#define WL18XX_REG_FUSE_BD_ADDR_1	0xA02602
+#define WL18XX_REG_FUSE_BD_ADDR_2	0xA02606
+
+#define WL18XX_CMD_MBOX_ADDRESS		0xB007B4
+
+#define WL18XX_FW_STATUS_ADDR		0x50F8
+
+#define CHIP_ID_185x_PG10              (0x06030101)
+#define CHIP_ID_185x_PG20              (0x06030111)
+
+/*
+ * Host Command Interrupt. Setting this bit masks
+ * the interrupt that the host issues to inform
+ * the FW that it has sent a command
+ * to the Wlan hardware Command Mailbox.
+ */
+#define WL18XX_INTR_TRIG_CMD       BIT(28)
+
+/*
+ * Host Event Acknowlegde Interrupt. The host
+ * sets this bit to acknowledge that it received
+ * the unsolicited information from the event
+ * mailbox.
+ */
+#define WL18XX_INTR_TRIG_EVENT_ACK BIT(29)
+
+/*
+ * To boot the firmware in PLT mode we need to write this value in
+ * SCR_PAD8 before starting.
+ */
+#define WL18XX_SCR_PAD8_PLT	0xBABABEBE
+
+enum {
+	COMPONENT_NO_SWITCH	= 0x0,
+	COMPONENT_2_WAY_SWITCH	= 0x1,
+	COMPONENT_3_WAY_SWITCH	= 0x2,
+	COMPONENT_MATCHING	= 0x3,
+};
+
+enum {
+	FEM_NONE	= 0x0,
+	FEM_VENDOR_1	= 0x1,
+	FEM_VENDOR_2	= 0x2,
+	FEM_VENDOR_3	= 0x3,
+};
+
+enum {
+	BOARD_TYPE_EVB_18XX     = 0,
+	BOARD_TYPE_DVP_18XX     = 1,
+	BOARD_TYPE_HDK_18XX     = 2,
+	BOARD_TYPE_FPGA_18XX    = 3,
+	BOARD_TYPE_COM8_18XX    = 4,
+
+	NUM_BOARD_TYPES,
+};
+
+#endif /* __REG_H__ */

+ 127 - 0
drivers/net/wireless/ti/wl18xx/tx.c

@@ -0,0 +1,127 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2011 Texas Instruments Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "../wlcore/wlcore.h"
+#include "../wlcore/cmd.h"
+#include "../wlcore/debug.h"
+#include "../wlcore/acx.h"
+#include "../wlcore/tx.h"
+
+#include "wl18xx.h"
+#include "tx.h"
+
+static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte)
+{
+	struct ieee80211_tx_info *info;
+	struct sk_buff *skb;
+	int id = tx_stat_byte & WL18XX_TX_STATUS_DESC_ID_MASK;
+	bool tx_success;
+
+	/* check for id legality */
+	if (unlikely(id >= wl->num_tx_desc || wl->tx_frames[id] == NULL)) {
+		wl1271_warning("illegal id in tx completion: %d", id);
+		return;
+	}
+
+	/* a zero bit indicates Tx success */
+	tx_success = !(tx_stat_byte & BIT(WL18XX_TX_STATUS_STAT_BIT_IDX));
+
+
+	skb = wl->tx_frames[id];
+	info = IEEE80211_SKB_CB(skb);
+
+	if (wl12xx_is_dummy_packet(wl, skb)) {
+		wl1271_free_tx_id(wl, id);
+		return;
+	}
+
+	/* update the TX status info */
+	if (tx_success && !(info->flags & IEEE80211_TX_CTL_NO_ACK))
+		info->flags |= IEEE80211_TX_STAT_ACK;
+
+	/* no real data about Tx completion */
+	info->status.rates[0].idx = -1;
+	info->status.rates[0].count = 0;
+	info->status.rates[0].flags = 0;
+	info->status.ack_signal = -1;
+
+	if (!tx_success)
+		wl->stats.retry_count++;
+
+	/*
+	 * TODO: update sequence number for encryption? seems to be
+	 * unsupported for now. needed for recovery with encryption.
+	 */
+
+	/* remove private header from packet */
+	skb_pull(skb, sizeof(struct wl1271_tx_hw_descr));
+
+	/* remove TKIP header space if present */
+	if ((wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) &&
+	    info->control.hw_key &&
+	    info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) {
+		int hdrlen = ieee80211_get_hdrlen_from_skb(skb);
+		memmove(skb->data + WL1271_EXTRA_SPACE_TKIP, skb->data, hdrlen);
+		skb_pull(skb, WL1271_EXTRA_SPACE_TKIP);
+	}
+
+	wl1271_debug(DEBUG_TX, "tx status id %u skb 0x%p success %d",
+		     id, skb, tx_success);
+
+	/* return the packet to the stack */
+	skb_queue_tail(&wl->deferred_tx_queue, skb);
+	queue_work(wl->freezable_wq, &wl->netstack_work);
+	wl1271_free_tx_id(wl, id);
+}
+
+void wl18xx_tx_immediate_complete(struct wl1271 *wl)
+{
+	struct wl18xx_fw_status_priv *status_priv =
+		(struct wl18xx_fw_status_priv *)wl->fw_status_2->priv;
+	struct wl18xx_priv *priv = wl->priv;
+	u8 i;
+
+	/* nothing to do here */
+	if (priv->last_fw_rls_idx == status_priv->fw_release_idx)
+		return;
+
+	/* freed Tx descriptors */
+	wl1271_debug(DEBUG_TX, "last released desc = %d, current idx = %d",
+		     priv->last_fw_rls_idx, status_priv->fw_release_idx);
+
+	if (status_priv->fw_release_idx >= WL18XX_FW_MAX_TX_STATUS_DESC) {
+		wl1271_error("invalid desc release index %d",
+			     status_priv->fw_release_idx);
+		WARN_ON(1);
+		return;
+	}
+
+	for (i = priv->last_fw_rls_idx;
+	     i != status_priv->fw_release_idx;
+	     i = (i + 1) % WL18XX_FW_MAX_TX_STATUS_DESC) {
+		wl18xx_tx_complete_packet(wl,
+			status_priv->released_tx_desc[i]);
+
+		wl->tx_results_count++;
+	}
+
+	priv->last_fw_rls_idx = status_priv->fw_release_idx;
+}

+ 46 - 0
drivers/net/wireless/ti/wl18xx/tx.h

@@ -0,0 +1,46 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2011 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL18XX_TX_H__
+#define __WL18XX_TX_H__
+
+#include "../wlcore/wlcore.h"
+
+#define WL18XX_TX_HW_BLOCK_SPARE        1
+/* for special cases - namely, TKIP and GEM */
+#define WL18XX_TX_HW_EXTRA_BLOCK_SPARE  2
+#define WL18XX_TX_HW_BLOCK_SIZE         268
+
+#define WL18XX_TX_STATUS_DESC_ID_MASK    0x7F
+#define WL18XX_TX_STATUS_STAT_BIT_IDX    7
+
+/* Indicates this TX HW frame is not padded to SDIO block size */
+#define WL18XX_TX_CTRL_NOT_PADDED	BIT(7)
+
+/*
+ * The FW uses a special bit to indicate a wide channel should be used in
+ * the rate policy.
+ */
+#define CONF_TX_RATE_USE_WIDE_CHAN BIT(31)
+
+void wl18xx_tx_immediate_complete(struct wl1271 *wl);
+
+#endif /* __WL12XX_TX_H__ */

+ 88 - 0
drivers/net/wireless/ti/wl18xx/wl18xx.h

@@ -0,0 +1,88 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2011 Texas Instruments Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL18XX_PRIV_H__
+#define __WL18XX_PRIV_H__
+
+#include "conf.h"
+
+#define WL18XX_CMD_MAX_SIZE          740
+
+struct wl18xx_priv {
+	/* buffer for sending commands to FW */
+	u8 cmd_buf[WL18XX_CMD_MAX_SIZE];
+
+	struct wl18xx_priv_conf conf;
+
+	/* Index of last released Tx desc in FW */
+	u8 last_fw_rls_idx;
+
+	/* number of VIFs requiring extra spare mem-blocks */
+	int extra_spare_vif_count;
+};
+
+#define WL18XX_FW_MAX_TX_STATUS_DESC 33
+
+struct wl18xx_fw_status_priv {
+	/*
+	 * Index in released_tx_desc for first byte that holds
+	 * released tx host desc
+	 */
+	u8 fw_release_idx;
+
+	/*
+	 * Array of host Tx descriptors, where fw_release_idx
+	 * indicated the first released idx.
+	 */
+	u8 released_tx_desc[WL18XX_FW_MAX_TX_STATUS_DESC];
+
+	u8 padding[2];
+};
+
+#define WL18XX_PHY_VERSION_MAX_LEN 20
+
+struct wl18xx_static_data_priv {
+	char phy_version[WL18XX_PHY_VERSION_MAX_LEN];
+};
+
+struct wl18xx_clk_cfg {
+	u32 n;
+	u32 m;
+	u32 p;
+	u32 q;
+	bool swallow;
+};
+
+enum {
+	CLOCK_CONFIG_16_2_M	= 1,
+	CLOCK_CONFIG_16_368_M,
+	CLOCK_CONFIG_16_8_M,
+	CLOCK_CONFIG_19_2_M,
+	CLOCK_CONFIG_26_M,
+	CLOCK_CONFIG_32_736_M,
+	CLOCK_CONFIG_33_6_M,
+	CLOCK_CONFIG_38_468_M,
+	CLOCK_CONFIG_52_M,
+
+	NUM_CLOCK_CONFIGS,
+};
+
+#endif /* __WL18XX_PRIV_H__ */

+ 6 - 2
drivers/net/wireless/ti/wlcore/acx.c

@@ -86,6 +86,7 @@ out:
 	kfree(auth);
 	return ret;
 }
+EXPORT_SYMBOL_GPL(wl1271_acx_sleep_auth);
 
 int wl1271_acx_tx_power(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 			int power)
@@ -708,14 +709,14 @@ out:
 	return ret;
 }
 
-int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats)
+int wl1271_acx_statistics(struct wl1271 *wl, void *stats)
 {
 	int ret;
 
 	wl1271_debug(DEBUG_ACX, "acx statistics");
 
 	ret = wl1271_cmd_interrogate(wl, ACX_STATISTICS, stats,
-				     sizeof(*stats));
+				     wl->stats.fw_stats_len);
 	if (ret < 0) {
 		wl1271_warning("acx statistics failed: %d", ret);
 		return -ENOMEM;
@@ -997,6 +998,7 @@ out:
 	kfree(mem_conf);
 	return ret;
 }
+EXPORT_SYMBOL_GPL(wl12xx_acx_mem_cfg);
 
 int wl1271_acx_init_mem_config(struct wl1271 *wl)
 {
@@ -1027,6 +1029,7 @@ int wl1271_acx_init_mem_config(struct wl1271 *wl)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(wl1271_acx_init_mem_config);
 
 int wl1271_acx_init_rx_interrupt(struct wl1271 *wl)
 {
@@ -1150,6 +1153,7 @@ out:
 	kfree(acx);
 	return ret;
 }
+EXPORT_SYMBOL_GPL(wl1271_acx_pm_config);
 
 int wl1271_acx_keep_alive_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 			       bool enable)

+ 16 - 238
drivers/net/wireless/ti/wlcore/acx.h

@@ -51,21 +51,18 @@
 #define WL1271_ACX_INTR_TRACE_A            BIT(7)
 /* Trace message on MBOX #B */
 #define WL1271_ACX_INTR_TRACE_B            BIT(8)
+/* SW FW Initiated interrupt Watchdog timer expiration */
+#define WL1271_ACX_SW_INTR_WATCHDOG        BIT(9)
 
-#define WL1271_ACX_INTR_ALL		   0xFFFFFFFF
-#define WL1271_ACX_ALL_EVENTS_VECTOR       (WL1271_ACX_INTR_WATCHDOG      | \
-					    WL1271_ACX_INTR_INIT_COMPLETE | \
-					    WL1271_ACX_INTR_EVENT_A       | \
-					    WL1271_ACX_INTR_EVENT_B       | \
-					    WL1271_ACX_INTR_CMD_COMPLETE  | \
-					    WL1271_ACX_INTR_HW_AVAILABLE  | \
-					    WL1271_ACX_INTR_DATA)
-
-#define WL1271_INTR_MASK                   (WL1271_ACX_INTR_WATCHDOG     | \
-					    WL1271_ACX_INTR_EVENT_A      | \
-					    WL1271_ACX_INTR_EVENT_B      | \
-					    WL1271_ACX_INTR_HW_AVAILABLE | \
-					    WL1271_ACX_INTR_DATA)
+#define WL1271_ACX_INTR_ALL             0xFFFFFFFF
+
+/* all possible interrupts - only appropriate ones will be masked in */
+#define WLCORE_ALL_INTR_MASK		(WL1271_ACX_INTR_WATCHDOG     | \
+					WL1271_ACX_INTR_EVENT_A       | \
+					WL1271_ACX_INTR_EVENT_B       | \
+					WL1271_ACX_INTR_HW_AVAILABLE  | \
+					WL1271_ACX_INTR_DATA          | \
+					WL1271_ACX_SW_INTR_WATCHDOG)
 
 /* Target's information element */
 struct acx_header {
@@ -417,228 +414,6 @@ struct acx_ctsprotect {
 	u8 padding[2];
 } __packed;
 
-struct acx_tx_statistics {
-	__le32 internal_desc_overflow;
-}  __packed;
-
-struct acx_rx_statistics {
-	__le32 out_of_mem;
-	__le32 hdr_overflow;
-	__le32 hw_stuck;
-	__le32 dropped;
-	__le32 fcs_err;
-	__le32 xfr_hint_trig;
-	__le32 path_reset;
-	__le32 reset_counter;
-} __packed;
-
-struct acx_dma_statistics {
-	__le32 rx_requested;
-	__le32 rx_errors;
-	__le32 tx_requested;
-	__le32 tx_errors;
-}  __packed;
-
-struct acx_isr_statistics {
-	/* host command complete */
-	__le32 cmd_cmplt;
-
-	/* fiqisr() */
-	__le32 fiqs;
-
-	/* (INT_STS_ND & INT_TRIG_RX_HEADER) */
-	__le32 rx_headers;
-
-	/* (INT_STS_ND & INT_TRIG_RX_CMPLT) */
-	__le32 rx_completes;
-
-	/* (INT_STS_ND & INT_TRIG_NO_RX_BUF) */
-	__le32 rx_mem_overflow;
-
-	/* (INT_STS_ND & INT_TRIG_S_RX_RDY) */
-	__le32 rx_rdys;
-
-	/* irqisr() */
-	__le32 irqs;
-
-	/* (INT_STS_ND & INT_TRIG_TX_PROC) */
-	__le32 tx_procs;
-
-	/* (INT_STS_ND & INT_TRIG_DECRYPT_DONE) */
-	__le32 decrypt_done;
-
-	/* (INT_STS_ND & INT_TRIG_DMA0) */
-	__le32 dma0_done;
-
-	/* (INT_STS_ND & INT_TRIG_DMA1) */
-	__le32 dma1_done;
-
-	/* (INT_STS_ND & INT_TRIG_TX_EXC_CMPLT) */
-	__le32 tx_exch_complete;
-
-	/* (INT_STS_ND & INT_TRIG_COMMAND) */
-	__le32 commands;
-
-	/* (INT_STS_ND & INT_TRIG_RX_PROC) */
-	__le32 rx_procs;
-
-	/* (INT_STS_ND & INT_TRIG_PM_802) */
-	__le32 hw_pm_mode_changes;
-
-	/* (INT_STS_ND & INT_TRIG_ACKNOWLEDGE) */
-	__le32 host_acknowledges;
-
-	/* (INT_STS_ND & INT_TRIG_PM_PCI) */
-	__le32 pci_pm;
-
-	/* (INT_STS_ND & INT_TRIG_ACM_WAKEUP) */
-	__le32 wakeups;
-
-	/* (INT_STS_ND & INT_TRIG_LOW_RSSI) */
-	__le32 low_rssi;
-} __packed;
-
-struct acx_wep_statistics {
-	/* WEP address keys configured */
-	__le32 addr_key_count;
-
-	/* default keys configured */
-	__le32 default_key_count;
-
-	__le32 reserved;
-
-	/* number of times that WEP key not found on lookup */
-	__le32 key_not_found;
-
-	/* number of times that WEP key decryption failed */
-	__le32 decrypt_fail;
-
-	/* WEP packets decrypted */
-	__le32 packets;
-
-	/* WEP decrypt interrupts */
-	__le32 interrupt;
-} __packed;
-
-#define ACX_MISSED_BEACONS_SPREAD 10
-
-struct acx_pwr_statistics {
-	/* the amount of enters into power save mode (both PD & ELP) */
-	__le32 ps_enter;
-
-	/* the amount of enters into ELP mode */
-	__le32 elp_enter;
-
-	/* the amount of missing beacon interrupts to the host */
-	__le32 missing_bcns;
-
-	/* the amount of wake on host-access times */
-	__le32 wake_on_host;
-
-	/* the amount of wake on timer-expire */
-	__le32 wake_on_timer_exp;
-
-	/* the number of packets that were transmitted with PS bit set */
-	__le32 tx_with_ps;
-
-	/* the number of packets that were transmitted with PS bit clear */
-	__le32 tx_without_ps;
-
-	/* the number of received beacons */
-	__le32 rcvd_beacons;
-
-	/* the number of entering into PowerOn (power save off) */
-	__le32 power_save_off;
-
-	/* the number of entries into power save mode */
-	__le16 enable_ps;
-
-	/*
-	 * the number of exits from power save, not including failed PS
-	 * transitions
-	 */
-	__le16 disable_ps;
-
-	/*
-	 * the number of times the TSF counter was adjusted because
-	 * of drift
-	 */
-	__le32 fix_tsf_ps;
-
-	/* Gives statistics about the spread continuous missed beacons.
-	 * The 16 LSB are dedicated for the PS mode.
-	 * The 16 MSB are dedicated for the PS mode.
-	 * cont_miss_bcns_spread[0] - single missed beacon.
-	 * cont_miss_bcns_spread[1] - two continuous missed beacons.
-	 * cont_miss_bcns_spread[2] - three continuous missed beacons.
-	 * ...
-	 * cont_miss_bcns_spread[9] - ten and more continuous missed beacons.
-	*/
-	__le32 cont_miss_bcns_spread[ACX_MISSED_BEACONS_SPREAD];
-
-	/* the number of beacons in awake mode */
-	__le32 rcvd_awake_beacons;
-} __packed;
-
-struct acx_mic_statistics {
-	__le32 rx_pkts;
-	__le32 calc_failure;
-} __packed;
-
-struct acx_aes_statistics {
-	__le32 encrypt_fail;
-	__le32 decrypt_fail;
-	__le32 encrypt_packets;
-	__le32 decrypt_packets;
-	__le32 encrypt_interrupt;
-	__le32 decrypt_interrupt;
-} __packed;
-
-struct acx_event_statistics {
-	__le32 heart_beat;
-	__le32 calibration;
-	__le32 rx_mismatch;
-	__le32 rx_mem_empty;
-	__le32 rx_pool;
-	__le32 oom_late;
-	__le32 phy_transmit_error;
-	__le32 tx_stuck;
-} __packed;
-
-struct acx_ps_statistics {
-	__le32 pspoll_timeouts;
-	__le32 upsd_timeouts;
-	__le32 upsd_max_sptime;
-	__le32 upsd_max_apturn;
-	__le32 pspoll_max_apturn;
-	__le32 pspoll_utilization;
-	__le32 upsd_utilization;
-} __packed;
-
-struct acx_rxpipe_statistics {
-	__le32 rx_prep_beacon_drop;
-	__le32 descr_host_int_trig_rx_data;
-	__le32 beacon_buffer_thres_host_int_trig_rx_data;
-	__le32 missed_beacon_host_int_trig_rx_data;
-	__le32 tx_xfr_host_int_trig_rx_data;
-} __packed;
-
-struct acx_statistics {
-	struct acx_header header;
-
-	struct acx_tx_statistics tx;
-	struct acx_rx_statistics rx;
-	struct acx_dma_statistics dma;
-	struct acx_isr_statistics isr;
-	struct acx_wep_statistics wep;
-	struct acx_pwr_statistics pwr;
-	struct acx_aes_statistics aes;
-	struct acx_mic_statistics mic;
-	struct acx_event_statistics event;
-	struct acx_ps_statistics ps;
-	struct acx_rxpipe_statistics rxpipe;
-} __packed;
-
 struct acx_rate_class {
 	__le32 enabled_rates;
 	u8 short_retry_limit;
@@ -828,6 +603,8 @@ struct wl1271_acx_keep_alive_config {
 #define HOST_IF_CFG_RX_FIFO_ENABLE     BIT(0)
 #define HOST_IF_CFG_TX_EXTRA_BLKS_SWAP BIT(1)
 #define HOST_IF_CFG_TX_PAD_TO_SDIO_BLK BIT(3)
+#define HOST_IF_CFG_RX_PAD_TO_SDIO_BLK BIT(4)
+#define HOST_IF_CFG_ADD_RX_ALIGNMENT   BIT(6)
 
 enum {
 	WL1271_ACX_TRIG_TYPE_LEVEL = 0,
@@ -946,7 +723,7 @@ struct wl1271_acx_ht_information {
 	u8 padding[2];
 } __packed;
 
-#define RX_BA_MAX_SESSIONS 2
+#define RX_BA_MAX_SESSIONS 3
 
 struct wl1271_acx_ba_initiator_policy {
 	struct acx_header header;
@@ -1243,6 +1020,7 @@ enum {
 	ACX_CONFIG_HANGOVER              = 0x0042,
 	ACX_FEATURE_CFG                  = 0x0043,
 	ACX_PROTECTION_CFG               = 0x0044,
+	ACX_CHECKSUM_CONFIG              = 0x0045,
 };
 
 
@@ -1281,7 +1059,7 @@ int wl1271_acx_set_preamble(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 			    enum acx_preamble_type preamble);
 int wl1271_acx_cts_protect(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 			   enum acx_ctsprotect_type ctsprotect);
-int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats);
+int wl1271_acx_statistics(struct wl1271 *wl, void *stats);
 int wl1271_acx_sta_rate_policies(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 int wl1271_acx_ap_rate_policy(struct wl1271 *wl, struct conf_tx_rate_class *c,
 		      u8 idx);

+ 33 - 24
drivers/net/wireless/ti/wlcore/boot.c

@@ -45,10 +45,17 @@ static void wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag)
 	wlcore_write_reg(wl, REG_ECPU_CONTROL, cpu_ctrl);
 }
 
-static int wlcore_parse_fw_ver(struct wl1271 *wl)
+static int wlcore_boot_parse_fw_ver(struct wl1271 *wl,
+				    struct wl1271_static_data *static_data)
 {
 	int ret;
 
+	strncpy(wl->chip.fw_ver_str, static_data->fw_version,
+		sizeof(wl->chip.fw_ver_str));
+
+	/* make sure the string is NULL-terminated */
+	wl->chip.fw_ver_str[sizeof(wl->chip.fw_ver_str) - 1] = '\0';
+
 	ret = sscanf(wl->chip.fw_ver_str + 4, "%u.%u.%u.%u.%u",
 		     &wl->chip.fw_ver[0], &wl->chip.fw_ver[1],
 		     &wl->chip.fw_ver[2], &wl->chip.fw_ver[3],
@@ -57,43 +64,43 @@ static int wlcore_parse_fw_ver(struct wl1271 *wl)
 	if (ret != 5) {
 		wl1271_warning("fw version incorrect value");
 		memset(wl->chip.fw_ver, 0, sizeof(wl->chip.fw_ver));
-		return -EINVAL;
+		ret = -EINVAL;
+		goto out;
 	}
 
 	ret = wlcore_identify_fw(wl);
 	if (ret < 0)
-		return ret;
-
-	return 0;
+		goto out;
+out:
+	return ret;
 }
 
-static int wlcore_boot_fw_version(struct wl1271 *wl)
+static int wlcore_boot_static_data(struct wl1271 *wl)
 {
 	struct wl1271_static_data *static_data;
+	size_t len = sizeof(*static_data) + wl->static_data_priv_len;
 	int ret;
 
-	static_data = kmalloc(sizeof(*static_data), GFP_KERNEL | GFP_DMA);
+	static_data = kmalloc(len, GFP_KERNEL);
 	if (!static_data) {
-		wl1271_error("Couldn't allocate memory for static data!");
-		return -ENOMEM;
+		ret = -ENOMEM;
+		goto out;
 	}
 
-	wl1271_read(wl, wl->cmd_box_addr, static_data, sizeof(*static_data),
-		    false);
-
-	strncpy(wl->chip.fw_ver_str, static_data->fw_version,
-		sizeof(wl->chip.fw_ver_str));
-
-	kfree(static_data);
+	wl1271_read(wl, wl->cmd_box_addr, static_data, len, false);
 
-	/* make sure the string is NULL-terminated */
-	wl->chip.fw_ver_str[sizeof(wl->chip.fw_ver_str) - 1] = '\0';
+	ret = wlcore_boot_parse_fw_ver(wl, static_data);
+	if (ret < 0)
+		goto out_free;
 
-	ret = wlcore_parse_fw_ver(wl);
+	ret = wlcore_handle_static_data(wl, static_data);
 	if (ret < 0)
-		return ret;
+		goto out_free;
 
-	return 0;
+out_free:
+	kfree(static_data);
+out:
+	return ret;
 }
 
 static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
@@ -204,8 +211,10 @@ int wlcore_boot_upload_nvs(struct wl1271 *wl)
 	u32 dest_addr, val;
 	u8 *nvs_ptr, *nvs_aligned;
 
-	if (wl->nvs == NULL)
+	if (wl->nvs == NULL) {
+		wl1271_error("NVS file is needed during boot");
 		return -ENODEV;
+	}
 
 	if (wl->quirks & WLCORE_QUIRK_LEGACY_NVS) {
 		struct wl1271_nvs_file *nvs =
@@ -400,9 +409,9 @@ int wlcore_boot_run_firmware(struct wl1271 *wl)
 	wl1271_debug(DEBUG_MAILBOX, "MBOX ptrs: 0x%x 0x%x",
 		     wl->mbox_ptr[0], wl->mbox_ptr[1]);
 
-	ret = wlcore_boot_fw_version(wl);
+	ret = wlcore_boot_static_data(wl);
 	if (ret < 0) {
-		wl1271_error("couldn't boot firmware");
+		wl1271_error("error getting static data");
 		return ret;
 	}
 

+ 1 - 0
drivers/net/wireless/ti/wlcore/boot.h

@@ -40,6 +40,7 @@ struct wl1271_static_data {
 	u8 fw_version[WL1271_FW_VERSION_MAX_LEN];
 	u32 hw_version;
 	u8 tx_power_table[WL1271_NO_SUBBANDS][WL1271_NO_POWER_LEVELS];
+	u8 priv[0];
 };
 
 /* number of times we try to read the INIT interrupt */

+ 51 - 11
drivers/net/wireless/ti/wlcore/cmd.c

@@ -36,6 +36,7 @@
 #include "cmd.h"
 #include "event.h"
 #include "tx.h"
+#include "hw_ops.h"
 
 #define WL1271_CMD_FAST_POLL_COUNT       50
 
@@ -291,6 +292,23 @@ static int wl12xx_get_new_session_id(struct wl1271 *wl,
 	return wlvif->session_counter;
 }
 
+static u8 wlcore_get_native_channel_type(u8 nl_channel_type)
+{
+	switch (nl_channel_type) {
+	case NL80211_CHAN_NO_HT:
+		return WLCORE_CHAN_NO_HT;
+	case NL80211_CHAN_HT20:
+		return WLCORE_CHAN_HT20;
+	case NL80211_CHAN_HT40MINUS:
+		return WLCORE_CHAN_HT40MINUS;
+	case NL80211_CHAN_HT40PLUS:
+		return WLCORE_CHAN_HT40PLUS;
+	default:
+		WARN_ON(1);
+		return WLCORE_CHAN_NO_HT;
+	}
+}
+
 static int wl12xx_cmd_role_start_dev(struct wl1271 *wl,
 				     struct wl12xx_vif *wlvif)
 {
@@ -407,6 +425,7 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 	memcpy(cmd->sta.ssid, wlvif->ssid, wlvif->ssid_len);
 	memcpy(cmd->sta.bssid, vif->bss_conf.bssid, ETH_ALEN);
 	cmd->sta.local_rates = cpu_to_le32(wlvif->rate_set);
+	cmd->channel_type = wlcore_get_native_channel_type(wlvif->channel_type);
 
 	if (wlvif->sta.hlid == WL12XX_INVALID_LINK_ID) {
 		ret = wl12xx_allocate_link(wl, wlvif, &wlvif->sta.hlid);
@@ -482,6 +501,7 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 	struct wl12xx_cmd_role_start *cmd;
 	struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
 	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
+	u32 supported_rates;
 	int ret;
 
 	wl1271_debug(DEBUG_CMD, "cmd role start ap %d", wlvif->role_id);
@@ -519,6 +539,7 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 	/* FIXME: Change when adding DFS */
 	cmd->ap.reset_tsf = 1;  /* By default reset AP TSF */
 	cmd->channel = wlvif->channel;
+	cmd->channel_type = wlcore_get_native_channel_type(wlvif->channel_type);
 
 	if (!bss_conf->hidden_ssid) {
 		/* take the SSID from the beacon for backward compatibility */
@@ -531,7 +552,13 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 		memcpy(cmd->ap.ssid, bss_conf->ssid, bss_conf->ssid_len);
 	}
 
-	cmd->ap.local_rates = cpu_to_le32(0xffffffff);
+	supported_rates = CONF_TX_AP_ENABLED_RATES | CONF_TX_MCS_RATES |
+		wlcore_hw_ap_get_mimo_wide_rate_mask(wl, wlvif);
+
+	wl1271_debug(DEBUG_CMD, "cmd role start ap with supported_rates 0x%08x",
+		     supported_rates);
+
+	cmd->ap.local_rates = cpu_to_le32(supported_rates);
 
 	switch (wlvif->band) {
 	case IEEE80211_BAND_2GHZ:
@@ -797,6 +824,7 @@ out:
 	kfree(cmd);
 	return ret;
 }
+EXPORT_SYMBOL_GPL(wl1271_cmd_data_path);
 
 int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 		       u8 ps_mode, u16 auto_ps_timeout)
@@ -1018,7 +1046,7 @@ out:
 
 int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
-	int ret, extra;
+	int ret, extra = 0;
 	u16 fc;
 	struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
 	struct sk_buff *skb;
@@ -1057,7 +1085,8 @@ int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 	/* encryption space */
 	switch (wlvif->encryption_type) {
 	case KEY_TKIP:
-		extra = WL1271_EXTRA_SPACE_TKIP;
+		if (wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE)
+			extra = WL1271_EXTRA_SPACE_TKIP;
 		break;
 	case KEY_AES:
 		extra = WL1271_EXTRA_SPACE_AES;
@@ -1346,13 +1375,18 @@ int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 
 	for (i = 0; i < NUM_ACCESS_CATEGORIES_COPY; i++)
 		if (sta->wme && (sta->uapsd_queues & BIT(i)))
-			cmd->psd_type[i] = WL1271_PSD_UPSD_TRIGGER;
+			cmd->psd_type[NUM_ACCESS_CATEGORIES_COPY-1-i] =
+					WL1271_PSD_UPSD_TRIGGER;
 		else
-			cmd->psd_type[i] = WL1271_PSD_LEGACY;
+			cmd->psd_type[NUM_ACCESS_CATEGORIES_COPY-1-i] =
+					WL1271_PSD_LEGACY;
+
 
 	sta_rates = sta->supp_rates[wlvif->band];
 	if (sta->ht_cap.ht_supported)
-		sta_rates |= sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET;
+		sta_rates |=
+			(sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET) |
+			(sta->ht_cap.mcs.rx_mask[1] << HW_MIMO_RATES_OFFSET);
 
 	cmd->supported_rates =
 		cpu_to_le32(wl1271_tx_enabled_rates_get(wl, sta_rates,
@@ -1573,19 +1607,25 @@ out:
 int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id)
 {
 	int ret = 0;
+	bool is_first_roc;
 
 	if (WARN_ON(test_bit(role_id, wl->roc_map)))
 		return 0;
 
+	is_first_roc = (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) >=
+			WL12XX_MAX_ROLES);
+
 	ret = wl12xx_cmd_roc(wl, wlvif, role_id);
 	if (ret < 0)
 		goto out;
 
-	ret = wl1271_cmd_wait_for_event(wl,
-					REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID);
-	if (ret < 0) {
-		wl1271_error("cmd roc event completion error");
-		goto out;
+	if (is_first_roc) {
+		ret = wl1271_cmd_wait_for_event(wl,
+					   REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID);
+		if (ret < 0) {
+			wl1271_error("cmd roc event completion error");
+			goto out;
+		}
 	}
 
 	__set_bit(role_id, wl->roc_map);

+ 11 - 2
drivers/net/wireless/ti/wlcore/cmd.h

@@ -192,7 +192,7 @@ enum cmd_templ {
 #define WL1271_COMMAND_TIMEOUT     2000
 #define WL1271_CMD_TEMPL_DFLT_SIZE 252
 #define WL1271_CMD_TEMPL_MAX_SIZE  512
-#define WL1271_EVENT_TIMEOUT       750
+#define WL1271_EVENT_TIMEOUT       1000
 
 struct wl1271_cmd_header {
 	__le16 id;
@@ -266,13 +266,22 @@ enum wlcore_band {
 	WLCORE_BAND_MAX_RADIO		= 0x7F,
 };
 
+enum wlcore_channel_type {
+	WLCORE_CHAN_NO_HT,
+	WLCORE_CHAN_HT20,
+	WLCORE_CHAN_HT40MINUS,
+	WLCORE_CHAN_HT40PLUS
+};
+
 struct wl12xx_cmd_role_start {
 	struct wl1271_cmd_header header;
 
 	u8 role_id;
 	u8 band;
 	u8 channel;
-	u8 padding;
+
+	/* enum wlcore_channel_type */
+	u8 channel_type;
 
 	union {
 		struct {

+ 67 - 26
drivers/net/wireless/ti/wlcore/conf.h

@@ -45,7 +45,15 @@ enum {
 	CONF_HW_BIT_RATE_MCS_4   = BIT(17),
 	CONF_HW_BIT_RATE_MCS_5   = BIT(18),
 	CONF_HW_BIT_RATE_MCS_6   = BIT(19),
-	CONF_HW_BIT_RATE_MCS_7   = BIT(20)
+	CONF_HW_BIT_RATE_MCS_7   = BIT(20),
+	CONF_HW_BIT_RATE_MCS_8   = BIT(21),
+	CONF_HW_BIT_RATE_MCS_9   = BIT(22),
+	CONF_HW_BIT_RATE_MCS_10  = BIT(23),
+	CONF_HW_BIT_RATE_MCS_11  = BIT(24),
+	CONF_HW_BIT_RATE_MCS_12  = BIT(25),
+	CONF_HW_BIT_RATE_MCS_13  = BIT(26),
+	CONF_HW_BIT_RATE_MCS_14  = BIT(27),
+	CONF_HW_BIT_RATE_MCS_15  = BIT(28),
 };
 
 enum {
@@ -310,7 +318,7 @@ enum {
 struct conf_sg_settings {
 	u32 params[CONF_SG_PARAMS_MAX];
 	u8 state;
-};
+} __packed;
 
 enum conf_rx_queue_type {
 	CONF_RX_QUEUE_TYPE_LOW_PRIORITY,  /* All except the high priority */
@@ -394,7 +402,7 @@ struct conf_rx_settings {
 	 * Range: RX_QUEUE_TYPE_RX_LOW_PRIORITY, RX_QUEUE_TYPE_RX_HIGH_PRIORITY,
 	 */
 	u8 queue_type;
-};
+} __packed;
 
 #define CONF_TX_MAX_RATE_CLASSES       10
 
@@ -435,6 +443,12 @@ struct conf_rx_settings {
 	CONF_HW_BIT_RATE_MCS_5 | CONF_HW_BIT_RATE_MCS_6 |        \
 	CONF_HW_BIT_RATE_MCS_7)
 
+#define CONF_TX_MIMO_RATES (CONF_HW_BIT_RATE_MCS_8 |             \
+	CONF_HW_BIT_RATE_MCS_9 | CONF_HW_BIT_RATE_MCS_10 |       \
+	CONF_HW_BIT_RATE_MCS_11 | CONF_HW_BIT_RATE_MCS_12 |      \
+	CONF_HW_BIT_RATE_MCS_13 | CONF_HW_BIT_RATE_MCS_14 |      \
+	CONF_HW_BIT_RATE_MCS_15)
+
 /*
  * Default rates for management traffic when operating in AP mode. This
  * should be configured according to the basic rate set of the AP
@@ -487,7 +501,7 @@ struct conf_tx_rate_class {
 	 *               the policy (0 - long preamble, 1 - short preamble.
 	 */
 	u8 aflags;
-};
+} __packed;
 
 #define CONF_TX_MAX_AC_COUNT 4
 
@@ -504,7 +518,7 @@ enum conf_tx_ac {
 	CONF_TX_AC_VI = 2,         /* video */
 	CONF_TX_AC_VO = 3,         /* voice */
 	CONF_TX_AC_CTS2SELF = 4,   /* fictitious AC, follows AC_VO */
-	CONF_TX_AC_ANY_TID = 0x1f
+	CONF_TX_AC_ANY_TID = 0xff
 };
 
 struct conf_tx_ac_category {
@@ -544,7 +558,7 @@ struct conf_tx_ac_category {
 	 * Range: u16
 	 */
 	u16 tx_op_limit;
-};
+} __packed;
 
 #define CONF_TX_MAX_TID_COUNT 8
 
@@ -578,7 +592,7 @@ struct conf_tx_tid {
 	u8 ps_scheme;
 	u8 ack_policy;
 	u32 apsd_conf[2];
-};
+} __packed;
 
 struct conf_tx_settings {
 	/*
@@ -664,7 +678,7 @@ struct conf_tx_settings {
 
 	/* Time in ms for Tx watchdog timer to expire */
 	u32 tx_watchdog_timeout;
-};
+} __packed;
 
 enum {
 	CONF_WAKE_UP_EVENT_BEACON    = 0x01, /* Wake on every Beacon*/
@@ -711,7 +725,7 @@ struct conf_bcn_filt_rule {
 	 * Version for the vendor specifie IE (221)
 	 */
 	u8 version[CONF_BCN_IE_VER_LEN];
-};
+} __packed;
 
 #define CONF_MAX_RSSI_SNR_TRIGGERS 8
 
@@ -762,7 +776,7 @@ struct conf_sig_weights {
 	 * Range: u8
 	 */
 	u8 snr_pkt_avg_weight;
-};
+} __packed;
 
 enum conf_bcn_filt_mode {
 	CONF_BCN_FILT_MODE_DISABLED = 0,
@@ -810,7 +824,7 @@ struct conf_conn_settings {
 	 *
 	 * Range: CONF_BCN_FILT_MODE_*
 	 */
-	enum conf_bcn_filt_mode bcn_filt_mode;
+	u8 bcn_filt_mode;
 
 	/*
 	 * Configure Beacon filter pass-thru rules.
@@ -937,7 +951,7 @@ struct conf_conn_settings {
 	 * Range: u16
 	 */
 	u8 max_listen_interval;
-};
+} __packed;
 
 enum {
 	CONF_REF_CLK_19_2_E,
@@ -965,6 +979,11 @@ struct conf_itrim_settings {
 
 	/* moderation timeout in microsecs from the last TX */
 	u32 timeout;
+} __packed;
+
+enum conf_fast_wakeup {
+	CONF_FAST_WAKEUP_ENABLE,
+	CONF_FAST_WAKEUP_DISABLE,
 };
 
 struct conf_pm_config_settings {
@@ -978,10 +997,10 @@ struct conf_pm_config_settings {
 	/*
 	 * Host fast wakeup support
 	 *
-	 * Range: true, false
+	 * Range: enum conf_fast_wakeup
 	 */
-	bool host_fast_wakeup_support;
-};
+	u8 host_fast_wakeup_support;
+} __packed;
 
 struct conf_roam_trigger_settings {
 	/*
@@ -1018,7 +1037,7 @@ struct conf_roam_trigger_settings {
 	 * Range: 0 - 255
 	 */
 	u8 avg_weight_snr_data;
-};
+} __packed;
 
 struct conf_scan_settings {
 	/*
@@ -1064,7 +1083,7 @@ struct conf_scan_settings {
 	 * Range: u32 Microsecs
 	 */
 	u32 split_scan_timeout;
-};
+} __packed;
 
 struct conf_sched_scan_settings {
 	/*
@@ -1102,7 +1121,7 @@ struct conf_sched_scan_settings {
 
 	/* SNR threshold to be used for filtering */
 	s8 snr_threshold;
-};
+} __packed;
 
 struct conf_ht_setting {
 	u8 rx_ba_win_size;
@@ -1111,7 +1130,7 @@ struct conf_ht_setting {
 
 	/* bitmap of enabled TIDs for TX BA sessions */
 	u8 tx_ba_tid_bitmap;
-};
+} __packed;
 
 struct conf_memory_settings {
 	/* Number of stations supported in IBSS mode */
@@ -1151,7 +1170,7 @@ struct conf_memory_settings {
 	 * Range: 0-120
 	 */
 	u8 tx_min;
-};
+} __packed;
 
 struct conf_fm_coex {
 	u8 enable;
@@ -1164,7 +1183,7 @@ struct conf_fm_coex {
 	u16 ldo_stabilization_time;
 	u8 fm_disturbed_band_margin;
 	u8 swallow_clk_diff;
-};
+} __packed;
 
 struct conf_rx_streaming_settings {
 	/*
@@ -1193,7 +1212,7 @@ struct conf_rx_streaming_settings {
 	 * enable rx streaming also when there is no coex activity
 	 */
 	u8 always;
-};
+} __packed;
 
 struct conf_fwlog {
 	/* Continuous or on-demand */
@@ -1217,7 +1236,7 @@ struct conf_fwlog {
 
 	/* Regulates the frequency of log messages */
 	u8 threshold;
-};
+} __packed;
 
 #define ACX_RATE_MGMT_NUM_OF_RATES 13
 struct conf_rate_policy_settings {
@@ -1236,7 +1255,7 @@ struct conf_rate_policy_settings {
 	u8 rate_check_up;
 	u8 rate_check_down;
 	u8 rate_retry_policy[ACX_RATE_MGMT_NUM_OF_RATES];
-};
+} __packed;
 
 struct conf_hangover_settings {
 	u32 recover_time;
@@ -1250,7 +1269,23 @@ struct conf_hangover_settings {
 	u8 quiet_time;
 	u8 increase_time;
 	u8 window_size;
-};
+} __packed;
+
+/*
+ * The conf version consists of 4 bytes.  The two MSB are the wlcore
+ * version, the two LSB are the lower driver's private conf
+ * version.
+ */
+#define WLCORE_CONF_VERSION	(0x0001 << 16)
+#define WLCORE_CONF_MASK	0xffff0000
+#define WLCORE_CONF_SIZE	(sizeof(struct wlcore_conf_header) +	\
+				 sizeof(struct wlcore_conf))
+
+struct wlcore_conf_header {
+	__le32 magic;
+	__le32 version;
+	__le32 checksum;
+} __packed;
 
 struct wlcore_conf {
 	struct conf_sg_settings sg;
@@ -1269,6 +1304,12 @@ struct wlcore_conf {
 	struct conf_fwlog fwlog;
 	struct conf_rate_policy_settings rate;
 	struct conf_hangover_settings hangover;
-};
+} __packed;
+
+struct wlcore_conf_file {
+	struct wlcore_conf_header header;
+	struct wlcore_conf core;
+	u8 priv[0];
+} __packed;
 
 #endif

+ 129 - 261
drivers/net/wireless/ti/wlcore/debugfs.c

@@ -25,6 +25,7 @@
 
 #include <linux/skbuff.h>
 #include <linux/slab.h>
+#include <linux/module.h>
 
 #include "wlcore.h"
 #include "debug.h"
@@ -32,14 +33,14 @@
 #include "ps.h"
 #include "io.h"
 #include "tx.h"
+#include "hw_ops.h"
 
 /* ms */
 #define WL1271_DEBUGFS_STATS_LIFETIME 1000
 
 /* debugfs macros idea from mac80211 */
-#define DEBUGFS_FORMAT_BUFFER_SIZE 100
-static int wl1271_format_buffer(char __user *userbuf, size_t count,
-				    loff_t *ppos, char *fmt, ...)
+int wl1271_format_buffer(char __user *userbuf, size_t count,
+			 loff_t *ppos, char *fmt, ...)
 {
 	va_list args;
 	char buf[DEBUGFS_FORMAT_BUFFER_SIZE];
@@ -51,59 +52,9 @@ static int wl1271_format_buffer(char __user *userbuf, size_t count,
 
 	return simple_read_from_buffer(userbuf, count, ppos, buf, res);
 }
+EXPORT_SYMBOL_GPL(wl1271_format_buffer);
 
-#define DEBUGFS_READONLY_FILE(name, fmt, value...)			\
-static ssize_t name## _read(struct file *file, char __user *userbuf,	\
-			    size_t count, loff_t *ppos)			\
-{									\
-	struct wl1271 *wl = file->private_data;				\
-	return wl1271_format_buffer(userbuf, count, ppos,		\
-				    fmt "\n", ##value);			\
-}									\
-									\
-static const struct file_operations name## _ops = {			\
-	.read = name## _read,						\
-	.open = simple_open,						\
-	.llseek	= generic_file_llseek,					\
-};
-
-#define DEBUGFS_ADD(name, parent)					\
-	entry = debugfs_create_file(#name, 0400, parent,		\
-				    wl, &name## _ops);			\
-	if (!entry || IS_ERR(entry))					\
-		goto err;						\
-
-#define DEBUGFS_ADD_PREFIX(prefix, name, parent)			\
-	do {								\
-		entry = debugfs_create_file(#name, 0400, parent,	\
-				    wl, &prefix## _## name## _ops);	\
-		if (!entry || IS_ERR(entry))				\
-			goto err;					\
-	} while (0);
-
-#define DEBUGFS_FWSTATS_FILE(sub, name, fmt)				\
-static ssize_t sub## _ ##name## _read(struct file *file,		\
-				      char __user *userbuf,		\
-				      size_t count, loff_t *ppos)	\
-{									\
-	struct wl1271 *wl = file->private_data;				\
-									\
-	wl1271_debugfs_update_stats(wl);				\
-									\
-	return wl1271_format_buffer(userbuf, count, ppos, fmt "\n",	\
-				    wl->stats.fw_stats->sub.name);	\
-}									\
-									\
-static const struct file_operations sub## _ ##name## _ops = {		\
-	.read = sub## _ ##name## _read,					\
-	.open = simple_open,						\
-	.llseek	= generic_file_llseek,					\
-};
-
-#define DEBUGFS_FWSTATS_ADD(sub, name)				\
-	DEBUGFS_ADD(sub## _ ##name, stats)
-
-static void wl1271_debugfs_update_stats(struct wl1271 *wl)
+void wl1271_debugfs_update_stats(struct wl1271 *wl)
 {
 	int ret;
 
@@ -125,97 +76,7 @@ static void wl1271_debugfs_update_stats(struct wl1271 *wl)
 out:
 	mutex_unlock(&wl->mutex);
 }
-
-DEBUGFS_FWSTATS_FILE(tx, internal_desc_overflow, "%u");
-
-DEBUGFS_FWSTATS_FILE(rx, out_of_mem, "%u");
-DEBUGFS_FWSTATS_FILE(rx, hdr_overflow, "%u");
-DEBUGFS_FWSTATS_FILE(rx, hw_stuck, "%u");
-DEBUGFS_FWSTATS_FILE(rx, dropped, "%u");
-DEBUGFS_FWSTATS_FILE(rx, fcs_err, "%u");
-DEBUGFS_FWSTATS_FILE(rx, xfr_hint_trig, "%u");
-DEBUGFS_FWSTATS_FILE(rx, path_reset, "%u");
-DEBUGFS_FWSTATS_FILE(rx, reset_counter, "%u");
-
-DEBUGFS_FWSTATS_FILE(dma, rx_requested, "%u");
-DEBUGFS_FWSTATS_FILE(dma, rx_errors, "%u");
-DEBUGFS_FWSTATS_FILE(dma, tx_requested, "%u");
-DEBUGFS_FWSTATS_FILE(dma, tx_errors, "%u");
-
-DEBUGFS_FWSTATS_FILE(isr, cmd_cmplt, "%u");
-DEBUGFS_FWSTATS_FILE(isr, fiqs, "%u");
-DEBUGFS_FWSTATS_FILE(isr, rx_headers, "%u");
-DEBUGFS_FWSTATS_FILE(isr, rx_mem_overflow, "%u");
-DEBUGFS_FWSTATS_FILE(isr, rx_rdys, "%u");
-DEBUGFS_FWSTATS_FILE(isr, irqs, "%u");
-DEBUGFS_FWSTATS_FILE(isr, tx_procs, "%u");
-DEBUGFS_FWSTATS_FILE(isr, decrypt_done, "%u");
-DEBUGFS_FWSTATS_FILE(isr, dma0_done, "%u");
-DEBUGFS_FWSTATS_FILE(isr, dma1_done, "%u");
-DEBUGFS_FWSTATS_FILE(isr, tx_exch_complete, "%u");
-DEBUGFS_FWSTATS_FILE(isr, commands, "%u");
-DEBUGFS_FWSTATS_FILE(isr, rx_procs, "%u");
-DEBUGFS_FWSTATS_FILE(isr, hw_pm_mode_changes, "%u");
-DEBUGFS_FWSTATS_FILE(isr, host_acknowledges, "%u");
-DEBUGFS_FWSTATS_FILE(isr, pci_pm, "%u");
-DEBUGFS_FWSTATS_FILE(isr, wakeups, "%u");
-DEBUGFS_FWSTATS_FILE(isr, low_rssi, "%u");
-
-DEBUGFS_FWSTATS_FILE(wep, addr_key_count, "%u");
-DEBUGFS_FWSTATS_FILE(wep, default_key_count, "%u");
-/* skipping wep.reserved */
-DEBUGFS_FWSTATS_FILE(wep, key_not_found, "%u");
-DEBUGFS_FWSTATS_FILE(wep, decrypt_fail, "%u");
-DEBUGFS_FWSTATS_FILE(wep, packets, "%u");
-DEBUGFS_FWSTATS_FILE(wep, interrupt, "%u");
-
-DEBUGFS_FWSTATS_FILE(pwr, ps_enter, "%u");
-DEBUGFS_FWSTATS_FILE(pwr, elp_enter, "%u");
-DEBUGFS_FWSTATS_FILE(pwr, missing_bcns, "%u");
-DEBUGFS_FWSTATS_FILE(pwr, wake_on_host, "%u");
-DEBUGFS_FWSTATS_FILE(pwr, wake_on_timer_exp, "%u");
-DEBUGFS_FWSTATS_FILE(pwr, tx_with_ps, "%u");
-DEBUGFS_FWSTATS_FILE(pwr, tx_without_ps, "%u");
-DEBUGFS_FWSTATS_FILE(pwr, rcvd_beacons, "%u");
-DEBUGFS_FWSTATS_FILE(pwr, power_save_off, "%u");
-DEBUGFS_FWSTATS_FILE(pwr, enable_ps, "%u");
-DEBUGFS_FWSTATS_FILE(pwr, disable_ps, "%u");
-DEBUGFS_FWSTATS_FILE(pwr, fix_tsf_ps, "%u");
-/* skipping cont_miss_bcns_spread for now */
-DEBUGFS_FWSTATS_FILE(pwr, rcvd_awake_beacons, "%u");
-
-DEBUGFS_FWSTATS_FILE(mic, rx_pkts, "%u");
-DEBUGFS_FWSTATS_FILE(mic, calc_failure, "%u");
-
-DEBUGFS_FWSTATS_FILE(aes, encrypt_fail, "%u");
-DEBUGFS_FWSTATS_FILE(aes, decrypt_fail, "%u");
-DEBUGFS_FWSTATS_FILE(aes, encrypt_packets, "%u");
-DEBUGFS_FWSTATS_FILE(aes, decrypt_packets, "%u");
-DEBUGFS_FWSTATS_FILE(aes, encrypt_interrupt, "%u");
-DEBUGFS_FWSTATS_FILE(aes, decrypt_interrupt, "%u");
-
-DEBUGFS_FWSTATS_FILE(event, heart_beat, "%u");
-DEBUGFS_FWSTATS_FILE(event, calibration, "%u");
-DEBUGFS_FWSTATS_FILE(event, rx_mismatch, "%u");
-DEBUGFS_FWSTATS_FILE(event, rx_mem_empty, "%u");
-DEBUGFS_FWSTATS_FILE(event, rx_pool, "%u");
-DEBUGFS_FWSTATS_FILE(event, oom_late, "%u");
-DEBUGFS_FWSTATS_FILE(event, phy_transmit_error, "%u");
-DEBUGFS_FWSTATS_FILE(event, tx_stuck, "%u");
-
-DEBUGFS_FWSTATS_FILE(ps, pspoll_timeouts, "%u");
-DEBUGFS_FWSTATS_FILE(ps, upsd_timeouts, "%u");
-DEBUGFS_FWSTATS_FILE(ps, upsd_max_sptime, "%u");
-DEBUGFS_FWSTATS_FILE(ps, upsd_max_apturn, "%u");
-DEBUGFS_FWSTATS_FILE(ps, pspoll_max_apturn, "%u");
-DEBUGFS_FWSTATS_FILE(ps, pspoll_utilization, "%u");
-DEBUGFS_FWSTATS_FILE(ps, upsd_utilization, "%u");
-
-DEBUGFS_FWSTATS_FILE(rxpipe, rx_prep_beacon_drop, "%u");
-DEBUGFS_FWSTATS_FILE(rxpipe, descr_host_int_trig_rx_data, "%u");
-DEBUGFS_FWSTATS_FILE(rxpipe, beacon_buffer_thres_host_int_trig_rx_data, "%u");
-DEBUGFS_FWSTATS_FILE(rxpipe, missed_beacon_host_int_trig_rx_data, "%u");
-DEBUGFS_FWSTATS_FILE(rxpipe, tx_xfr_host_int_trig_rx_data, "%u");
+EXPORT_SYMBOL_GPL(wl1271_debugfs_update_stats);
 
 DEBUGFS_READONLY_FILE(retry_count, "%u", wl->stats.retry_count);
 DEBUGFS_READONLY_FILE(excessive_retries, "%u",
@@ -241,6 +102,89 @@ static const struct file_operations tx_queue_len_ops = {
 	.llseek = default_llseek,
 };
 
+static void chip_op_handler(struct wl1271 *wl, unsigned long value,
+			    void *arg)
+{
+	int ret;
+	int (*chip_op) (struct wl1271 *wl);
+
+	if (!arg) {
+		wl1271_warning("debugfs chip_op_handler with no callback");
+		return;
+	}
+
+	ret = wl1271_ps_elp_wakeup(wl);
+	if (ret < 0)
+		return;
+
+	chip_op = arg;
+	chip_op(wl);
+
+	wl1271_ps_elp_sleep(wl);
+}
+
+
+static inline void no_write_handler(struct wl1271 *wl,
+				    unsigned long value,
+				    unsigned long param)
+{
+}
+
+#define WL12XX_CONF_DEBUGFS(param, conf_sub_struct,			\
+			    min_val, max_val, write_handler_locked,	\
+			    write_handler_arg)				\
+	static ssize_t param##_read(struct file *file,			\
+				      char __user *user_buf,		\
+				      size_t count, loff_t *ppos)	\
+	{								\
+	struct wl1271 *wl = file->private_data;				\
+	return wl1271_format_buffer(user_buf, count,			\
+				    ppos, "%d\n",			\
+				    wl->conf.conf_sub_struct.param);	\
+	}								\
+									\
+	static ssize_t param##_write(struct file *file,			\
+				     const char __user *user_buf,	\
+				     size_t count, loff_t *ppos)	\
+	{								\
+	struct wl1271 *wl = file->private_data;				\
+	unsigned long value;						\
+	int ret;							\
+									\
+	ret = kstrtoul_from_user(user_buf, count, 10, &value);		\
+	if (ret < 0) {							\
+		wl1271_warning("illegal value for " #param);		\
+		return -EINVAL;						\
+	}								\
+									\
+	if (value < min_val || value > max_val) {			\
+		wl1271_warning(#param " is not in valid range");	\
+		return -ERANGE;						\
+	}								\
+									\
+	mutex_lock(&wl->mutex);						\
+	wl->conf.conf_sub_struct.param = value;				\
+									\
+	write_handler_locked(wl, value, write_handler_arg);		\
+									\
+	mutex_unlock(&wl->mutex);					\
+	return count;							\
+	}								\
+									\
+	static const struct file_operations param##_ops = {		\
+		.read = param##_read,					\
+		.write = param##_write,					\
+		.open = simple_open,					\
+		.llseek = default_llseek,				\
+	};
+
+WL12XX_CONF_DEBUGFS(irq_pkt_threshold, rx, 0, 65535,
+		    chip_op_handler, wl1271_acx_init_rx_interrupt)
+WL12XX_CONF_DEBUGFS(irq_blk_threshold, rx, 0, 65535,
+		    chip_op_handler, wl1271_acx_init_rx_interrupt)
+WL12XX_CONF_DEBUGFS(irq_timeout, rx, 0, 100,
+		    chip_op_handler, wl1271_acx_init_rx_interrupt)
+
 static ssize_t gpio_power_read(struct file *file, char __user *user_buf,
 			  size_t count, loff_t *ppos)
 {
@@ -535,8 +479,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf,
 	DRIVER_STATE_PRINT_LHEX(ap_ps_map);
 	DRIVER_STATE_PRINT_HEX(quirks);
 	DRIVER_STATE_PRINT_HEX(irq);
-	DRIVER_STATE_PRINT_HEX(ref_clock);
-	DRIVER_STATE_PRINT_HEX(tcxo_clock);
+	/* TODO: ref_clock and tcxo_clock were moved to wl12xx priv */
 	DRIVER_STATE_PRINT_HEX(hw_pg_ver);
 	DRIVER_STATE_PRINT_HEX(platform_quirks);
 	DRIVER_STATE_PRINT_HEX(chip.id);
@@ -647,7 +590,6 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf,
 		VIF_STATE_PRINT_INT(last_rssi_event);
 		VIF_STATE_PRINT_INT(ba_support);
 		VIF_STATE_PRINT_INT(ba_allowed);
-		VIF_STATE_PRINT_INT(is_gem);
 		VIF_STATE_PRINT_LLHEX(tx_security_seq);
 		VIF_STATE_PRINT_INT(tx_security_last_seq_lsb);
 	}
@@ -1002,108 +944,30 @@ static const struct file_operations beacon_filtering_ops = {
 	.llseek = default_llseek,
 };
 
-static int wl1271_debugfs_add_files(struct wl1271 *wl,
-				     struct dentry *rootdir)
+static ssize_t fw_stats_raw_read(struct file *file,
+				 char __user *userbuf,
+				 size_t count, loff_t *ppos)
 {
-	int ret = 0;
-	struct dentry *entry, *stats, *streaming;
+	struct wl1271 *wl = file->private_data;
 
-	stats = debugfs_create_dir("fw-statistics", rootdir);
-	if (!stats || IS_ERR(stats)) {
-		entry = stats;
-		goto err;
-	}
+	wl1271_debugfs_update_stats(wl);
 
-	DEBUGFS_FWSTATS_ADD(tx, internal_desc_overflow);
-
-	DEBUGFS_FWSTATS_ADD(rx, out_of_mem);
-	DEBUGFS_FWSTATS_ADD(rx, hdr_overflow);
-	DEBUGFS_FWSTATS_ADD(rx, hw_stuck);
-	DEBUGFS_FWSTATS_ADD(rx, dropped);
-	DEBUGFS_FWSTATS_ADD(rx, fcs_err);
-	DEBUGFS_FWSTATS_ADD(rx, xfr_hint_trig);
-	DEBUGFS_FWSTATS_ADD(rx, path_reset);
-	DEBUGFS_FWSTATS_ADD(rx, reset_counter);
-
-	DEBUGFS_FWSTATS_ADD(dma, rx_requested);
-	DEBUGFS_FWSTATS_ADD(dma, rx_errors);
-	DEBUGFS_FWSTATS_ADD(dma, tx_requested);
-	DEBUGFS_FWSTATS_ADD(dma, tx_errors);
-
-	DEBUGFS_FWSTATS_ADD(isr, cmd_cmplt);
-	DEBUGFS_FWSTATS_ADD(isr, fiqs);
-	DEBUGFS_FWSTATS_ADD(isr, rx_headers);
-	DEBUGFS_FWSTATS_ADD(isr, rx_mem_overflow);
-	DEBUGFS_FWSTATS_ADD(isr, rx_rdys);
-	DEBUGFS_FWSTATS_ADD(isr, irqs);
-	DEBUGFS_FWSTATS_ADD(isr, tx_procs);
-	DEBUGFS_FWSTATS_ADD(isr, decrypt_done);
-	DEBUGFS_FWSTATS_ADD(isr, dma0_done);
-	DEBUGFS_FWSTATS_ADD(isr, dma1_done);
-	DEBUGFS_FWSTATS_ADD(isr, tx_exch_complete);
-	DEBUGFS_FWSTATS_ADD(isr, commands);
-	DEBUGFS_FWSTATS_ADD(isr, rx_procs);
-	DEBUGFS_FWSTATS_ADD(isr, hw_pm_mode_changes);
-	DEBUGFS_FWSTATS_ADD(isr, host_acknowledges);
-	DEBUGFS_FWSTATS_ADD(isr, pci_pm);
-	DEBUGFS_FWSTATS_ADD(isr, wakeups);
-	DEBUGFS_FWSTATS_ADD(isr, low_rssi);
-
-	DEBUGFS_FWSTATS_ADD(wep, addr_key_count);
-	DEBUGFS_FWSTATS_ADD(wep, default_key_count);
-	/* skipping wep.reserved */
-	DEBUGFS_FWSTATS_ADD(wep, key_not_found);
-	DEBUGFS_FWSTATS_ADD(wep, decrypt_fail);
-	DEBUGFS_FWSTATS_ADD(wep, packets);
-	DEBUGFS_FWSTATS_ADD(wep, interrupt);
-
-	DEBUGFS_FWSTATS_ADD(pwr, ps_enter);
-	DEBUGFS_FWSTATS_ADD(pwr, elp_enter);
-	DEBUGFS_FWSTATS_ADD(pwr, missing_bcns);
-	DEBUGFS_FWSTATS_ADD(pwr, wake_on_host);
-	DEBUGFS_FWSTATS_ADD(pwr, wake_on_timer_exp);
-	DEBUGFS_FWSTATS_ADD(pwr, tx_with_ps);
-	DEBUGFS_FWSTATS_ADD(pwr, tx_without_ps);
-	DEBUGFS_FWSTATS_ADD(pwr, rcvd_beacons);
-	DEBUGFS_FWSTATS_ADD(pwr, power_save_off);
-	DEBUGFS_FWSTATS_ADD(pwr, enable_ps);
-	DEBUGFS_FWSTATS_ADD(pwr, disable_ps);
-	DEBUGFS_FWSTATS_ADD(pwr, fix_tsf_ps);
-	/* skipping cont_miss_bcns_spread for now */
-	DEBUGFS_FWSTATS_ADD(pwr, rcvd_awake_beacons);
-
-	DEBUGFS_FWSTATS_ADD(mic, rx_pkts);
-	DEBUGFS_FWSTATS_ADD(mic, calc_failure);
-
-	DEBUGFS_FWSTATS_ADD(aes, encrypt_fail);
-	DEBUGFS_FWSTATS_ADD(aes, decrypt_fail);
-	DEBUGFS_FWSTATS_ADD(aes, encrypt_packets);
-	DEBUGFS_FWSTATS_ADD(aes, decrypt_packets);
-	DEBUGFS_FWSTATS_ADD(aes, encrypt_interrupt);
-	DEBUGFS_FWSTATS_ADD(aes, decrypt_interrupt);
-
-	DEBUGFS_FWSTATS_ADD(event, heart_beat);
-	DEBUGFS_FWSTATS_ADD(event, calibration);
-	DEBUGFS_FWSTATS_ADD(event, rx_mismatch);
-	DEBUGFS_FWSTATS_ADD(event, rx_mem_empty);
-	DEBUGFS_FWSTATS_ADD(event, rx_pool);
-	DEBUGFS_FWSTATS_ADD(event, oom_late);
-	DEBUGFS_FWSTATS_ADD(event, phy_transmit_error);
-	DEBUGFS_FWSTATS_ADD(event, tx_stuck);
-
-	DEBUGFS_FWSTATS_ADD(ps, pspoll_timeouts);
-	DEBUGFS_FWSTATS_ADD(ps, upsd_timeouts);
-	DEBUGFS_FWSTATS_ADD(ps, upsd_max_sptime);
-	DEBUGFS_FWSTATS_ADD(ps, upsd_max_apturn);
-	DEBUGFS_FWSTATS_ADD(ps, pspoll_max_apturn);
-	DEBUGFS_FWSTATS_ADD(ps, pspoll_utilization);
-	DEBUGFS_FWSTATS_ADD(ps, upsd_utilization);
-
-	DEBUGFS_FWSTATS_ADD(rxpipe, rx_prep_beacon_drop);
-	DEBUGFS_FWSTATS_ADD(rxpipe, descr_host_int_trig_rx_data);
-	DEBUGFS_FWSTATS_ADD(rxpipe, beacon_buffer_thres_host_int_trig_rx_data);
-	DEBUGFS_FWSTATS_ADD(rxpipe, missed_beacon_host_int_trig_rx_data);
-	DEBUGFS_FWSTATS_ADD(rxpipe, tx_xfr_host_int_trig_rx_data);
+	return simple_read_from_buffer(userbuf, count, ppos,
+				       wl->stats.fw_stats,
+				       wl->stats.fw_stats_len);
+}
+
+static const struct file_operations fw_stats_raw_ops = {
+	.read = fw_stats_raw_read,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
+static int wl1271_debugfs_add_files(struct wl1271 *wl,
+				    struct dentry *rootdir)
+{
+	int ret = 0;
+	struct dentry *entry, *streaming;
 
 	DEBUGFS_ADD(tx_queue_len, rootdir);
 	DEBUGFS_ADD(retry_count, rootdir);
@@ -1120,6 +984,10 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl,
 	DEBUGFS_ADD(dynamic_ps_timeout, rootdir);
 	DEBUGFS_ADD(forced_ps, rootdir);
 	DEBUGFS_ADD(split_scan_timeout, rootdir);
+	DEBUGFS_ADD(irq_pkt_threshold, rootdir);
+	DEBUGFS_ADD(irq_blk_threshold, rootdir);
+	DEBUGFS_ADD(irq_timeout, rootdir);
+	DEBUGFS_ADD(fw_stats_raw, rootdir);
 
 	streaming = debugfs_create_dir("rx_streaming", rootdir);
 	if (!streaming || IS_ERR(streaming))
@@ -1145,7 +1013,7 @@ void wl1271_debugfs_reset(struct wl1271 *wl)
 	if (!wl->stats.fw_stats)
 		return;
 
-	memset(wl->stats.fw_stats, 0, sizeof(*wl->stats.fw_stats));
+	memset(wl->stats.fw_stats, 0, wl->stats.fw_stats_len);
 	wl->stats.retry_count = 0;
 	wl->stats.excessive_retries = 0;
 }
@@ -1160,34 +1028,34 @@ int wl1271_debugfs_init(struct wl1271 *wl)
 
 	if (IS_ERR(rootdir)) {
 		ret = PTR_ERR(rootdir);
-		goto err;
+		goto out;
 	}
 
-	wl->stats.fw_stats = kzalloc(sizeof(*wl->stats.fw_stats),
-				      GFP_KERNEL);
-
+	wl->stats.fw_stats = kzalloc(wl->stats.fw_stats_len, GFP_KERNEL);
 	if (!wl->stats.fw_stats) {
 		ret = -ENOMEM;
-		goto err_fw;
+		goto out_remove;
 	}
 
 	wl->stats.fw_stats_update = jiffies;
 
 	ret = wl1271_debugfs_add_files(wl, rootdir);
+	if (ret < 0)
+		goto out_exit;
 
+	ret = wlcore_debugfs_init(wl, rootdir);
 	if (ret < 0)
-		goto err_file;
+		goto out_exit;
 
-	return 0;
+	goto out;
 
-err_file:
-	kfree(wl->stats.fw_stats);
-	wl->stats.fw_stats = NULL;
+out_exit:
+	wl1271_debugfs_exit(wl);
 
-err_fw:
+out_remove:
 	debugfs_remove_recursive(rootdir);
 
-err:
+out:
 	return ret;
 }
 

+ 87 - 0
drivers/net/wireless/ti/wlcore/debugfs.h

@@ -26,8 +26,95 @@
 
 #include "wlcore.h"
 
+int wl1271_format_buffer(char __user *userbuf, size_t count,
+			 loff_t *ppos, char *fmt, ...);
+
 int wl1271_debugfs_init(struct wl1271 *wl);
 void wl1271_debugfs_exit(struct wl1271 *wl);
 void wl1271_debugfs_reset(struct wl1271 *wl);
+void wl1271_debugfs_update_stats(struct wl1271 *wl);
+
+#define DEBUGFS_FORMAT_BUFFER_SIZE 256
+
+#define DEBUGFS_READONLY_FILE(name, fmt, value...)			\
+static ssize_t name## _read(struct file *file, char __user *userbuf,	\
+			    size_t count, loff_t *ppos)			\
+{									\
+	struct wl1271 *wl = file->private_data;				\
+	return wl1271_format_buffer(userbuf, count, ppos,		\
+				    fmt "\n", ##value);			\
+}									\
+									\
+static const struct file_operations name## _ops = {			\
+	.read = name## _read,						\
+	.open = simple_open,						\
+	.llseek	= generic_file_llseek,					\
+};
+
+#define DEBUGFS_ADD(name, parent)					\
+	do {								\
+		entry = debugfs_create_file(#name, 0400, parent,	\
+					    wl, &name## _ops);		\
+		if (!entry || IS_ERR(entry))				\
+			goto err;					\
+	} while (0);
+
+
+#define DEBUGFS_ADD_PREFIX(prefix, name, parent)			\
+	do {								\
+		entry = debugfs_create_file(#name, 0400, parent,	\
+				    wl, &prefix## _## name## _ops);	\
+		if (!entry || IS_ERR(entry))				\
+			goto err;					\
+	} while (0);
+
+#define DEBUGFS_FWSTATS_FILE(sub, name, fmt, struct_type)		\
+static ssize_t sub## _ ##name## _read(struct file *file,		\
+				      char __user *userbuf,		\
+				      size_t count, loff_t *ppos)	\
+{									\
+	struct wl1271 *wl = file->private_data;				\
+	struct struct_type *stats = wl->stats.fw_stats;			\
+									\
+	wl1271_debugfs_update_stats(wl);				\
+									\
+	return wl1271_format_buffer(userbuf, count, ppos, fmt "\n",	\
+				    stats->sub.name);			\
+}									\
+									\
+static const struct file_operations sub## _ ##name## _ops = {		\
+	.read = sub## _ ##name## _read,					\
+	.open = simple_open,						\
+	.llseek	= generic_file_llseek,					\
+};
+
+#define DEBUGFS_FWSTATS_FILE_ARRAY(sub, name, len, struct_type)		\
+static ssize_t sub## _ ##name## _read(struct file *file,		\
+				      char __user *userbuf,		\
+				      size_t count, loff_t *ppos)	\
+{									\
+	struct wl1271 *wl = file->private_data;				\
+	struct struct_type *stats = wl->stats.fw_stats;			\
+	char buf[DEBUGFS_FORMAT_BUFFER_SIZE] = "";			\
+	int res, i;							\
+									\
+	wl1271_debugfs_update_stats(wl);				\
+									\
+	for (i = 0; i < len; i++)					\
+		res = snprintf(buf, sizeof(buf), "%s[%d] = %d\n",	\
+			       buf, i, stats->sub.name[i]);		\
+									\
+	return wl1271_format_buffer(userbuf, count, ppos, "%s", buf);	\
+}									\
+									\
+static const struct file_operations sub## _ ##name## _ops = {		\
+	.read = sub## _ ##name## _read,					\
+	.open = simple_open,						\
+	.llseek	= generic_file_llseek,					\
+};
+
+#define DEBUGFS_FWSTATS_ADD(sub, name)					\
+	DEBUGFS_ADD(sub## _ ##name, stats)
+
 
 #endif /* WL1271_DEBUGFS_H */

+ 21 - 3
drivers/net/wireless/ti/wlcore/event.c

@@ -148,15 +148,33 @@ static int wl1271_event_process(struct wl1271 *wl)
 		int delay = wl->conf.conn.synch_fail_thold *
 					wl->conf.conn.bss_lose_timeout;
 		wl1271_info("Beacon loss detected.");
-		cancel_delayed_work_sync(&wl->connection_loss_work);
+
+		/*
+		 * if the work is already queued, it should take place. We
+		 * don't want to delay the connection loss indication
+		 * any more.
+		 */
 		ieee80211_queue_delayed_work(wl->hw, &wl->connection_loss_work,
-		      msecs_to_jiffies(delay));
+					     msecs_to_jiffies(delay));
+
+		wl12xx_for_each_wlvif_sta(wl, wlvif) {
+			vif = wl12xx_wlvif_to_vif(wlvif);
+
+			ieee80211_cqm_rssi_notify(
+					vif,
+					NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
+					GFP_KERNEL);
+		}
 	}
 
 	if (vector & REGAINED_BSS_EVENT_ID) {
 		/* TODO: check for multi-role */
 		wl1271_info("Beacon regained.");
-		cancel_delayed_work_sync(&wl->connection_loss_work);
+		cancel_delayed_work(&wl->connection_loss_work);
+
+		/* sanity check - we can't lose and gain the beacon together */
+		WARN(vector & BSS_LOSE_EVENT_ID,
+		     "Concurrent beacon loss and gain from FW");
 	}
 
 	if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) {

+ 78 - 0
drivers/net/wireless/ti/wlcore/hw_ops.h

@@ -119,4 +119,82 @@ static inline int wlcore_identify_fw(struct wl1271 *wl)
 	return 0;
 }
 
+static inline void
+wlcore_hw_set_tx_desc_csum(struct wl1271 *wl,
+			   struct wl1271_tx_hw_descr *desc,
+			   struct sk_buff *skb)
+{
+	if (!wl->ops->set_tx_desc_csum)
+		BUG_ON(1);
+
+	wl->ops->set_tx_desc_csum(wl, desc, skb);
+}
+
+static inline void
+wlcore_hw_set_rx_csum(struct wl1271 *wl,
+		      struct wl1271_rx_descriptor *desc,
+		      struct sk_buff *skb)
+{
+	if (wl->ops->set_rx_csum)
+		wl->ops->set_rx_csum(wl, desc, skb);
+}
+
+static inline u32
+wlcore_hw_ap_get_mimo_wide_rate_mask(struct wl1271 *wl,
+				     struct wl12xx_vif *wlvif)
+{
+	if (wl->ops->ap_get_mimo_wide_rate_mask)
+		return wl->ops->ap_get_mimo_wide_rate_mask(wl, wlvif);
+
+	return 0;
+}
+
+static inline int
+wlcore_debugfs_init(struct wl1271 *wl, struct dentry *rootdir)
+{
+	if (wl->ops->debugfs_init)
+		return wl->ops->debugfs_init(wl, rootdir);
+
+	return 0;
+}
+
+static inline int
+wlcore_handle_static_data(struct wl1271 *wl, void *static_data)
+{
+	if (wl->ops->handle_static_data)
+		return wl->ops->handle_static_data(wl, static_data);
+
+	return 0;
+}
+
+static inline int
+wlcore_hw_get_spare_blocks(struct wl1271 *wl, bool is_gem)
+{
+	if (!wl->ops->get_spare_blocks)
+		BUG_ON(1);
+
+	return wl->ops->get_spare_blocks(wl, is_gem);
+}
+
+static inline int
+wlcore_hw_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
+		  struct ieee80211_vif *vif,
+		  struct ieee80211_sta *sta,
+		  struct ieee80211_key_conf *key_conf)
+{
+	if (!wl->ops->set_key)
+		BUG_ON(1);
+
+	return wl->ops->set_key(wl, cmd, vif, sta, key_conf);
+}
+
+static inline u32
+wlcore_hw_pre_pkt_send(struct wl1271 *wl, u32 buf_offset, u32 last_len)
+{
+	if (wl->ops->pre_pkt_send)
+		return wl->ops->pre_pkt_send(wl, buf_offset, last_len);
+
+	return buf_offset;
+}
+
 #endif

+ 3 - 0
drivers/net/wireless/ti/wlcore/init.c

@@ -460,6 +460,9 @@ int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 	/* unconditionally enable HT rates */
 	supported_rates |= CONF_TX_MCS_RATES;
 
+	/* get extra MIMO or wide-chan rates where the HW supports it */
+	supported_rates |= wlcore_hw_ap_get_mimo_wide_rate_mask(wl, wlvif);
+
 	/* configure unicast TX rate classes */
 	for (i = 0; i < wl->conf.tx.ac_conf_count; i++) {
 		rc.enabled_rates = supported_rates;

+ 267 - 237
drivers/net/wireless/ti/wlcore/main.c

@@ -320,46 +320,6 @@ static void wlcore_adjust_conf(struct wl1271 *wl)
 	}
 }
 
-static int wl1271_plt_init(struct wl1271 *wl)
-{
-	int ret;
-
-	ret = wl->ops->hw_init(wl);
-	if (ret < 0)
-		return ret;
-
-	ret = wl1271_acx_init_mem_config(wl);
-	if (ret < 0)
-		return ret;
-
-	ret = wl12xx_acx_mem_cfg(wl);
-	if (ret < 0)
-		goto out_free_memmap;
-
-	/* Enable data path */
-	ret = wl1271_cmd_data_path(wl, 1);
-	if (ret < 0)
-		goto out_free_memmap;
-
-	/* Configure for CAM power saving (ie. always active) */
-	ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
-	if (ret < 0)
-		goto out_free_memmap;
-
-	/* configure PM */
-	ret = wl1271_acx_pm_config(wl);
-	if (ret < 0)
-		goto out_free_memmap;
-
-	return 0;
-
- out_free_memmap:
-	kfree(wl->target_mem_map);
-	wl->target_mem_map = NULL;
-
-	return ret;
-}
-
 static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl,
 					struct wl12xx_vif *wlvif,
 					u8 hlid, u8 tx_pkts)
@@ -387,7 +347,7 @@ static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl,
 
 static void wl12xx_irq_update_links_status(struct wl1271 *wl,
 					   struct wl12xx_vif *wlvif,
-					   struct wl_fw_status *status)
+					   struct wl_fw_status_2 *status)
 {
 	struct wl1271_link *lnk;
 	u32 cur_fw_ps_map;
@@ -419,7 +379,8 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl,
 }
 
 static void wl12xx_fw_status(struct wl1271 *wl,
-			     struct wl_fw_status *status)
+			     struct wl_fw_status_1 *status_1,
+			     struct wl_fw_status_2 *status_2)
 {
 	struct wl12xx_vif *wlvif;
 	struct timespec ts;
@@ -428,37 +389,38 @@ static void wl12xx_fw_status(struct wl1271 *wl,
 	int i;
 	size_t status_len;
 
-	status_len = sizeof(*status) + wl->fw_status_priv_len;
+	status_len = WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
+		sizeof(*status_2) + wl->fw_status_priv_len;
 
-	wlcore_raw_read_data(wl, REG_RAW_FW_STATUS_ADDR, status,
+	wlcore_raw_read_data(wl, REG_RAW_FW_STATUS_ADDR, status_1,
 			     status_len, false);
 
 	wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, "
 		     "drv_rx_counter = %d, tx_results_counter = %d)",
-		     status->intr,
-		     status->fw_rx_counter,
-		     status->drv_rx_counter,
-		     status->tx_results_counter);
+		     status_1->intr,
+		     status_1->fw_rx_counter,
+		     status_1->drv_rx_counter,
+		     status_1->tx_results_counter);
 
 	for (i = 0; i < NUM_TX_QUEUES; i++) {
 		/* prevent wrap-around in freed-packets counter */
 		wl->tx_allocated_pkts[i] -=
-				(status->counters.tx_released_pkts[i] -
+				(status_2->counters.tx_released_pkts[i] -
 				wl->tx_pkts_freed[i]) & 0xff;
 
-		wl->tx_pkts_freed[i] = status->counters.tx_released_pkts[i];
+		wl->tx_pkts_freed[i] = status_2->counters.tx_released_pkts[i];
 	}
 
 	/* prevent wrap-around in total blocks counter */
 	if (likely(wl->tx_blocks_freed <=
-		   le32_to_cpu(status->total_released_blks)))
-		freed_blocks = le32_to_cpu(status->total_released_blks) -
+		   le32_to_cpu(status_2->total_released_blks)))
+		freed_blocks = le32_to_cpu(status_2->total_released_blks) -
 			       wl->tx_blocks_freed;
 	else
 		freed_blocks = 0x100000000LL - wl->tx_blocks_freed +
-			       le32_to_cpu(status->total_released_blks);
+			       le32_to_cpu(status_2->total_released_blks);
 
-	wl->tx_blocks_freed = le32_to_cpu(status->total_released_blks);
+	wl->tx_blocks_freed = le32_to_cpu(status_2->total_released_blks);
 
 	wl->tx_allocated_blocks -= freed_blocks;
 
@@ -474,7 +436,7 @@ static void wl12xx_fw_status(struct wl1271 *wl,
 			cancel_delayed_work(&wl->tx_watchdog_work);
 	}
 
-	avail = le32_to_cpu(status->tx_total) - wl->tx_allocated_blocks;
+	avail = le32_to_cpu(status_2->tx_total) - wl->tx_allocated_blocks;
 
 	/*
 	 * The FW might change the total number of TX memblocks before
@@ -493,13 +455,13 @@ static void wl12xx_fw_status(struct wl1271 *wl,
 
 	/* for AP update num of allocated TX blocks per link and ps status */
 	wl12xx_for_each_wlvif_ap(wl, wlvif) {
-		wl12xx_irq_update_links_status(wl, wlvif, status);
+		wl12xx_irq_update_links_status(wl, wlvif, status_2);
 	}
 
 	/* update the host-chipset time offset */
 	getnstimeofday(&ts);
 	wl->time_offset = (timespec_to_ns(&ts) >> 10) -
-		(s64)le32_to_cpu(status->fw_localtime);
+		(s64)le32_to_cpu(status_2->fw_localtime);
 }
 
 static void wl1271_flush_deferred_work(struct wl1271 *wl)
@@ -568,20 +530,30 @@ static irqreturn_t wl1271_irq(int irq, void *cookie)
 		clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
 		smp_mb__after_clear_bit();
 
-		wl12xx_fw_status(wl, wl->fw_status);
+		wl12xx_fw_status(wl, wl->fw_status_1, wl->fw_status_2);
 
 		wlcore_hw_tx_immediate_compl(wl);
 
-		intr = le32_to_cpu(wl->fw_status->intr);
-		intr &= WL1271_INTR_MASK;
+		intr = le32_to_cpu(wl->fw_status_1->intr);
+		intr &= WLCORE_ALL_INTR_MASK;
 		if (!intr) {
 			done = true;
 			continue;
 		}
 
 		if (unlikely(intr & WL1271_ACX_INTR_WATCHDOG)) {
-			wl1271_error("watchdog interrupt received! "
+			wl1271_error("HW watchdog interrupt received! starting recovery.");
+			wl->watchdog_recovery = true;
+			wl12xx_queue_recovery_work(wl);
+
+			/* restarting the chip. ignore any other interrupt. */
+			goto out;
+		}
+
+		if (unlikely(intr & WL1271_ACX_SW_INTR_WATCHDOG)) {
+			wl1271_error("SW watchdog interrupt received! "
 				     "starting recovery.");
+			wl->watchdog_recovery = true;
 			wl12xx_queue_recovery_work(wl);
 
 			/* restarting the chip. ignore any other interrupt. */
@@ -591,7 +563,7 @@ static irqreturn_t wl1271_irq(int irq, void *cookie)
 		if (likely(intr & WL1271_ACX_INTR_DATA)) {
 			wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
 
-			wl12xx_rx(wl, wl->fw_status);
+			wl12xx_rx(wl, wl->fw_status_1);
 
 			/* Check if any tx blocks were freed */
 			spin_lock_irqsave(&wl->wl_lock, flags);
@@ -743,7 +715,7 @@ out:
 	return ret;
 }
 
-static int wl1271_fetch_nvs(struct wl1271 *wl)
+static void wl1271_fetch_nvs(struct wl1271 *wl)
 {
 	const struct firmware *fw;
 	int ret;
@@ -751,16 +723,15 @@ static int wl1271_fetch_nvs(struct wl1271 *wl)
 	ret = request_firmware(&fw, WL12XX_NVS_NAME, wl->dev);
 
 	if (ret < 0) {
-		wl1271_error("could not get nvs file %s: %d", WL12XX_NVS_NAME,
-			     ret);
-		return ret;
+		wl1271_debug(DEBUG_BOOT, "could not get nvs file %s: %d",
+			     WL12XX_NVS_NAME, ret);
+		return;
 	}
 
 	wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL);
 
 	if (!wl->nvs) {
 		wl1271_error("could not allocate memory for the nvs file");
-		ret = -ENOMEM;
 		goto out;
 	}
 
@@ -768,8 +739,6 @@ static int wl1271_fetch_nvs(struct wl1271 *wl)
 
 out:
 	release_firmware(fw);
-
-	return ret;
 }
 
 void wl12xx_queue_recovery_work(struct wl1271 *wl)
@@ -820,14 +789,16 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
 
 	/*
 	 * Make sure the chip is awake and the logger isn't active.
-	 * This might fail if the firmware hanged.
+	 * Do not send a stop fwlog command if the fw is hanged.
 	 */
-	if (!wl1271_ps_elp_wakeup(wl))
+	if (!wl1271_ps_elp_wakeup(wl) && !wl->watchdog_recovery)
 		wl12xx_cmd_stop_fwlog(wl);
+	else
+		goto out;
 
 	/* Read the first memory block address */
-	wl12xx_fw_status(wl, wl->fw_status);
-	first_addr = le32_to_cpu(wl->fw_status->log_start_addr);
+	wl12xx_fw_status(wl, wl->fw_status_1, wl->fw_status_2);
+	first_addr = le32_to_cpu(wl->fw_status_2->log_start_addr);
 	if (!first_addr)
 		goto out;
 
@@ -872,9 +843,14 @@ static void wl1271_recovery_work(struct work_struct *work)
 
 	wl12xx_read_fwlog_panic(wl);
 
-	wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x",
+	/* change partitions momentarily so we can read the FW pc */
+	wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
+	wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x "
+		    "hint_sts: 0x%08x",
 		    wl->chip.fw_ver_str,
-		    wlcore_read_reg(wl, REG_PC_ON_RECOVERY));
+		    wlcore_read_reg(wl, REG_PC_ON_RECOVERY),
+		    wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR));
+	wlcore_set_partition(wl, &wl->ptable[PART_WORK]);
 
 	BUG_ON(bug_on_recovery &&
 	       !test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags));
@@ -885,8 +861,6 @@ static void wl1271_recovery_work(struct work_struct *work)
 		goto out_unlock;
 	}
 
-	BUG_ON(bug_on_recovery);
-
 	/*
 	 * Advance security sequence number to overcome potential progress
 	 * in the firmware during recovery. This doens't hurt if the network is
@@ -900,7 +874,7 @@ static void wl1271_recovery_work(struct work_struct *work)
 	}
 
 	/* Prevent spurious TX during FW restart */
-	ieee80211_stop_queues(wl->hw);
+	wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART);
 
 	if (wl->sched_scanning) {
 		ieee80211_sched_scan_stopped(wl->hw);
@@ -914,6 +888,7 @@ static void wl1271_recovery_work(struct work_struct *work)
 		vif = wl12xx_wlvif_to_vif(wlvif);
 		__wl1271_op_remove_interface(wl, vif, false);
 	}
+        wl->watchdog_recovery = false;
 	mutex_unlock(&wl->mutex);
 	wl1271_op_stop(wl->hw);
 
@@ -925,9 +900,10 @@ static void wl1271_recovery_work(struct work_struct *work)
 	 * Its safe to enable TX now - the queues are stopped after a request
 	 * to restart the HW.
 	 */
-	ieee80211_wake_queues(wl->hw);
+	wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART);
 	return;
 out_unlock:
+        wl->watchdog_recovery = false;
 	mutex_unlock(&wl->mutex);
 }
 
@@ -938,13 +914,19 @@ static void wl1271_fw_wakeup(struct wl1271 *wl)
 
 static int wl1271_setup(struct wl1271 *wl)
 {
-	wl->fw_status = kmalloc(sizeof(*wl->fw_status), GFP_KERNEL);
-	if (!wl->fw_status)
+	wl->fw_status_1 = kmalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
+				  sizeof(*wl->fw_status_2) +
+				  wl->fw_status_priv_len, GFP_KERNEL);
+	if (!wl->fw_status_1)
 		return -ENOMEM;
 
+	wl->fw_status_2 = (struct wl_fw_status_2 *)
+				(((u8 *) wl->fw_status_1) +
+				WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc));
+
 	wl->tx_res_if = kmalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
 	if (!wl->tx_res_if) {
-		kfree(wl->fw_status);
+		kfree(wl->fw_status_1);
 		return -ENOMEM;
 	}
 
@@ -987,13 +969,12 @@ static int wl12xx_chip_wakeup(struct wl1271 *wl, bool plt)
 	 * simplify the code and since the performance impact is
 	 * negligible, we use the same block size for all different
 	 * chip types.
+	 *
+	 * Check if the bus supports blocksize alignment and, if it
+	 * doesn't, make sure we don't have the quirk.
 	 */
-	if (wl1271_set_block_size(wl))
-		wl->quirks |= WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN;
-
-	ret = wl->ops->identify_chip(wl);
-	if (ret < 0)
-		goto out;
+	if (!wl1271_set_block_size(wl))
+		wl->quirks &= ~WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN;
 
 	/* TODO: make sure the lower driver has set things up correctly */
 
@@ -1005,13 +986,6 @@ static int wl12xx_chip_wakeup(struct wl1271 *wl, bool plt)
 	if (ret < 0)
 		goto out;
 
-	/* No NVS from netlink, try to get it from the filesystem */
-	if (wl->nvs == NULL) {
-		ret = wl1271_fetch_nvs(wl);
-		if (ret < 0)
-			goto out;
-	}
-
 out:
 	return ret;
 }
@@ -1039,14 +1013,10 @@ int wl1271_plt_start(struct wl1271 *wl)
 		if (ret < 0)
 			goto power_off;
 
-		ret = wl->ops->boot(wl);
+		ret = wl->ops->plt_init(wl);
 		if (ret < 0)
 			goto power_off;
 
-		ret = wl1271_plt_init(wl);
-		if (ret < 0)
-			goto irq_disable;
-
 		wl->plt = true;
 		wl->state = WL1271_STATE_ON;
 		wl1271_notice("firmware booted in PLT mode (%s)",
@@ -1059,19 +1029,6 @@ int wl1271_plt_start(struct wl1271 *wl)
 
 		goto out;
 
-irq_disable:
-		mutex_unlock(&wl->mutex);
-		/* Unlocking the mutex in the middle of handling is
-		   inherently unsafe. In this case we deem it safe to do,
-		   because we need to let any possibly pending IRQ out of
-		   the system (and while we are WL1271_STATE_OFF the IRQ
-		   work function will not do anything.) Also, any other
-		   possible concurrent operations will fail due to the
-		   current state, hence the wl1271 struct should be safe. */
-		wlcore_disable_interrupts(wl);
-		wl1271_flush_deferred_work(wl);
-		cancel_work_sync(&wl->netstack_work);
-		mutex_lock(&wl->mutex);
 power_off:
 		wl1271_power_off(wl);
 	}
@@ -1154,9 +1111,16 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
 
 	spin_lock_irqsave(&wl->wl_lock, flags);
 
-	/* queue the packet */
+	/*
+	 * drop the packet if the link is invalid or the queue is stopped
+	 * for any reason but watermark. Watermark is a "soft"-stop so we
+	 * allow these packets through.
+	 */
 	if (hlid == WL12XX_INVALID_LINK_ID ||
-	    (wlvif && !test_bit(hlid, wlvif->links_map))) {
+	    (wlvif && !test_bit(hlid, wlvif->links_map)) ||
+	     (wlcore_is_queue_stopped(wl, q) &&
+	      !wlcore_is_queue_stopped_by_reason(wl, q,
+			WLCORE_QUEUE_STOP_REASON_WATERMARK))) {
 		wl1271_debug(DEBUG_TX, "DROP skb hlid %d q %d", hlid, q);
 		ieee80211_free_txskb(hw, skb);
 		goto out;
@@ -1174,8 +1138,8 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
 	 */
 	if (wl->tx_queue_count[q] >= WL1271_TX_QUEUE_HIGH_WATERMARK) {
 		wl1271_debug(DEBUG_TX, "op_tx: stopping queues for q %d", q);
-		ieee80211_stop_queue(wl->hw, mapping);
-		set_bit(q, &wl->stopped_queues_map);
+		wlcore_stop_queue_locked(wl, q,
+					 WLCORE_QUEUE_STOP_REASON_WATERMARK);
 	}
 
 	/*
@@ -1758,7 +1722,7 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
 	cancel_delayed_work_sync(&wl->connection_loss_work);
 
 	/* let's notify MAC80211 about the remaining pending TX frames */
-	wl12xx_tx_reset(wl, true);
+	wl12xx_tx_reset(wl);
 	mutex_lock(&wl->mutex);
 
 	wl1271_power_off(wl);
@@ -1767,6 +1731,7 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
 
 	wl->rx_counter = 0;
 	wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
+	wl->channel_type = NL80211_CHAN_NO_HT;
 	wl->tx_blocks_available = 0;
 	wl->tx_allocated_blocks = 0;
 	wl->tx_results_count = 0;
@@ -1799,8 +1764,9 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
 
 	wl1271_debugfs_reset(wl);
 
-	kfree(wl->fw_status);
-	wl->fw_status = NULL;
+	kfree(wl->fw_status_1);
+	wl->fw_status_1 = NULL;
+	wl->fw_status_2 = NULL;
 	kfree(wl->tx_res_if);
 	wl->tx_res_if = NULL;
 	kfree(wl->target_mem_map);
@@ -1894,6 +1860,9 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
 		wl12xx_allocate_rate_policy(wl, &wlvif->sta.basic_rate_idx);
 		wl12xx_allocate_rate_policy(wl, &wlvif->sta.ap_rate_idx);
 		wl12xx_allocate_rate_policy(wl, &wlvif->sta.p2p_rate_idx);
+		wlvif->basic_rate_set = CONF_TX_RATE_MASK_BASIC;
+		wlvif->basic_rate = CONF_TX_RATE_MASK_BASIC;
+		wlvif->rate_set = CONF_TX_RATE_MASK_BASIC;
 	} else {
 		/* init ap data */
 		wlvif->ap.bcast_hlid = WL12XX_INVALID_LINK_ID;
@@ -1903,13 +1872,19 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
 		for (i = 0; i < CONF_TX_MAX_AC_COUNT; i++)
 			wl12xx_allocate_rate_policy(wl,
 						&wlvif->ap.ucast_rate_idx[i]);
+		wlvif->basic_rate_set = CONF_TX_AP_ENABLED_RATES;
+		/*
+		 * TODO: check if basic_rate shouldn't be
+		 * wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
+		 * instead (the same thing for STA above).
+		*/
+		wlvif->basic_rate = CONF_TX_AP_ENABLED_RATES;
+		/* TODO: this seems to be used only for STA, check it */
+		wlvif->rate_set = CONF_TX_AP_ENABLED_RATES;
 	}
 
 	wlvif->bitrate_masks[IEEE80211_BAND_2GHZ] = wl->conf.tx.basic_rate;
 	wlvif->bitrate_masks[IEEE80211_BAND_5GHZ] = wl->conf.tx.basic_rate_5;
-	wlvif->basic_rate_set = CONF_TX_RATE_MASK_BASIC;
-	wlvif->basic_rate = CONF_TX_RATE_MASK_BASIC;
-	wlvif->rate_set = CONF_TX_RATE_MASK_BASIC;
 	wlvif->beacon_int = WL1271_DEFAULT_BEACON_INT;
 
 	/*
@@ -1919,6 +1894,7 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
 	wlvif->band = wl->band;
 	wlvif->channel = wl->channel;
 	wlvif->power_level = wl->power_level;
+	wlvif->channel_type = wl->channel_type;
 
 	INIT_WORK(&wlvif->rx_streaming_enable_work,
 		  wl1271_rx_streaming_enable_work);
@@ -2444,7 +2420,7 @@ static int wl1271_sta_handle_idle(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 	} else {
 		/* The current firmware only supports sched_scan in idle */
 		if (wl->sched_scanning) {
-			wl1271_scan_sched_scan_stop(wl);
+			wl1271_scan_sched_scan_stop(wl, wlvif);
 			ieee80211_sched_scan_stopped(wl->hw);
 		}
 
@@ -2469,13 +2445,20 @@ static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 	/* if the channel changes while joined, join again */
 	if (changed & IEEE80211_CONF_CHANGE_CHANNEL &&
 	    ((wlvif->band != conf->channel->band) ||
-	     (wlvif->channel != channel))) {
+	     (wlvif->channel != channel) ||
+	     (wlvif->channel_type != conf->channel_type))) {
 		/* send all pending packets */
 		wl1271_tx_work_locked(wl);
 		wlvif->band = conf->channel->band;
 		wlvif->channel = channel;
+		wlvif->channel_type = conf->channel_type;
 
-		if (!is_ap) {
+		if (is_ap) {
+			ret = wl1271_init_ap_rates(wl, wlvif);
+			if (ret < 0)
+				wl1271_error("AP rate policy change failed %d",
+					     ret);
+		} else {
 			/*
 			 * FIXME: the mac80211 should really provide a fixed
 			 * rate to use here. for now, just use the smallest
@@ -2583,8 +2566,9 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
 	 * frames, such as the deauth. To make sure those frames reach the air,
 	 * wait here until the TX queue is fully flushed.
 	 */
-	if ((changed & IEEE80211_CONF_CHANGE_IDLE) &&
-	    (conf->flags & IEEE80211_CONF_IDLE))
+	if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) ||
+	    ((changed & IEEE80211_CONF_CHANGE_IDLE) &&
+	     (conf->flags & IEEE80211_CONF_IDLE)))
 		wl1271_tx_flush(wl);
 
 	mutex_lock(&wl->mutex);
@@ -2593,6 +2577,7 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
 	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
 		wl->band = conf->channel->band;
 		wl->channel = channel;
+		wl->channel_type = conf->channel_type;
 	}
 
 	if (changed & IEEE80211_CONF_CHANGE_POWER)
@@ -2825,17 +2810,6 @@ static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 	int ret;
 	bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
 
-	/*
-	 * A role set to GEM cipher requires different Tx settings (namely
-	 * spare blocks). Note when we are in this mode so the HW can adjust.
-	 */
-	if (key_type == KEY_GEM) {
-		if (action == KEY_ADD_OR_REPLACE)
-			wlvif->is_gem = true;
-		else if (action == KEY_REMOVE)
-			wlvif->is_gem = false;
-	}
-
 	if (is_ap) {
 		struct wl1271_station *wl_sta;
 		u8 hlid;
@@ -2913,12 +2887,21 @@ static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 	return 0;
 }
 
-static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+static int wlcore_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_conf)
 {
 	struct wl1271 *wl = hw->priv;
+
+	return wlcore_hw_set_key(wl, cmd, vif, sta, key_conf);
+}
+
+int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
+		   struct ieee80211_vif *vif,
+		   struct ieee80211_sta *sta,
+		   struct ieee80211_key_conf *key_conf)
+{
 	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
 	int ret;
 	u32 tx_seq_32 = 0;
@@ -3029,6 +3012,7 @@ out_unlock:
 
 	return ret;
 }
+EXPORT_SYMBOL_GPL(wlcore_set_key);
 
 static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
 			     struct ieee80211_vif *vif,
@@ -3167,6 +3151,7 @@ static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw,
 				      struct ieee80211_vif *vif)
 {
 	struct wl1271 *wl = hw->priv;
+	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
 	int ret;
 
 	wl1271_debug(DEBUG_MAC80211, "wl1271_op_sched_scan_stop");
@@ -3180,7 +3165,7 @@ static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw,
 	if (ret < 0)
 		goto out;
 
-	wl1271_scan_sched_scan_stop(wl);
+	wl1271_scan_sched_scan_stop(wl, wlvif);
 
 	wl1271_ps_elp_sleep(wl);
 out:
@@ -3316,8 +3301,15 @@ static int wl1271_ap_set_probe_resp_tmpl(struct wl1271 *wl, u32 rates,
 				      skb->data,
 				      skb->len, 0,
 				      rates);
-
 	dev_kfree_skb(skb);
+
+	if (ret < 0)
+		goto out;
+
+	wl1271_debug(DEBUG_AP, "probe response updated");
+	set_bit(WLVIF_FLAG_AP_PROBE_RESP_SET, &wlvif->flags);
+
+out:
 	return ret;
 }
 
@@ -3422,6 +3414,87 @@ out:
 	return ret;
 }
 
+static int wlcore_set_beacon_template(struct wl1271 *wl,
+				      struct ieee80211_vif *vif,
+				      bool is_ap)
+{
+	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+	struct ieee80211_hdr *hdr;
+	u32 min_rate;
+	int ret;
+	int ieoffset = offsetof(struct ieee80211_mgmt,
+				u.beacon.variable);
+	struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif);
+	u16 tmpl_id;
+
+	if (!beacon) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	wl1271_debug(DEBUG_MASTER, "beacon updated");
+
+	ret = wl1271_ssid_set(vif, beacon, ieoffset);
+	if (ret < 0) {
+		dev_kfree_skb(beacon);
+		goto out;
+	}
+	min_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
+	tmpl_id = is_ap ? CMD_TEMPL_AP_BEACON :
+		CMD_TEMPL_BEACON;
+	ret = wl1271_cmd_template_set(wl, wlvif->role_id, tmpl_id,
+				      beacon->data,
+				      beacon->len, 0,
+				      min_rate);
+	if (ret < 0) {
+		dev_kfree_skb(beacon);
+		goto out;
+	}
+
+	/*
+	 * In case we already have a probe-resp beacon set explicitly
+	 * by usermode, don't use the beacon data.
+	 */
+	if (test_bit(WLVIF_FLAG_AP_PROBE_RESP_SET, &wlvif->flags))
+		goto end_bcn;
+
+	/* remove TIM ie from probe response */
+	wl12xx_remove_ie(beacon, WLAN_EID_TIM, ieoffset);
+
+	/*
+	 * remove p2p ie from probe response.
+	 * the fw reponds to probe requests that don't include
+	 * the p2p ie. probe requests with p2p ie will be passed,
+	 * and will be responded by the supplicant (the spec
+	 * forbids including the p2p ie when responding to probe
+	 * requests that didn't include it).
+	 */
+	wl12xx_remove_vendor_ie(beacon, WLAN_OUI_WFA,
+				WLAN_OUI_TYPE_WFA_P2P, ieoffset);
+
+	hdr = (struct ieee80211_hdr *) beacon->data;
+	hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+					 IEEE80211_STYPE_PROBE_RESP);
+	if (is_ap)
+		ret = wl1271_ap_set_probe_resp_tmpl_legacy(wl, vif,
+							   beacon->data,
+							   beacon->len,
+							   min_rate);
+	else
+		ret = wl1271_cmd_template_set(wl, wlvif->role_id,
+					      CMD_TEMPL_PROBE_RESPONSE,
+					      beacon->data,
+					      beacon->len, 0,
+					      min_rate);
+end_bcn:
+	dev_kfree_skb(beacon);
+	if (ret < 0)
+		goto out;
+
+out:
+	return ret;
+}
+
 static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
 					  struct ieee80211_vif *vif,
 					  struct ieee80211_bss_conf *bss_conf,
@@ -3440,81 +3513,12 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
 
 	if ((changed & BSS_CHANGED_AP_PROBE_RESP) && is_ap) {
 		u32 rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
-		if (!wl1271_ap_set_probe_resp_tmpl(wl, rate, vif)) {
-			wl1271_debug(DEBUG_AP, "probe response updated");
-			set_bit(WLVIF_FLAG_AP_PROBE_RESP_SET, &wlvif->flags);
-		}
+
+		wl1271_ap_set_probe_resp_tmpl(wl, rate, vif);
 	}
 
 	if ((changed & BSS_CHANGED_BEACON)) {
-		struct ieee80211_hdr *hdr;
-		u32 min_rate;
-		int ieoffset = offsetof(struct ieee80211_mgmt,
-					u.beacon.variable);
-		struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif);
-		u16 tmpl_id;
-
-		if (!beacon) {
-			ret = -EINVAL;
-			goto out;
-		}
-
-		wl1271_debug(DEBUG_MASTER, "beacon updated");
-
-		ret = wl1271_ssid_set(vif, beacon, ieoffset);
-		if (ret < 0) {
-			dev_kfree_skb(beacon);
-			goto out;
-		}
-		min_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
-		tmpl_id = is_ap ? CMD_TEMPL_AP_BEACON :
-				  CMD_TEMPL_BEACON;
-		ret = wl1271_cmd_template_set(wl, wlvif->role_id, tmpl_id,
-					      beacon->data,
-					      beacon->len, 0,
-					      min_rate);
-		if (ret < 0) {
-			dev_kfree_skb(beacon);
-			goto out;
-		}
-
-		/*
-		 * In case we already have a probe-resp beacon set explicitly
-		 * by usermode, don't use the beacon data.
-		 */
-		if (test_bit(WLVIF_FLAG_AP_PROBE_RESP_SET, &wlvif->flags))
-			goto end_bcn;
-
-		/* remove TIM ie from probe response */
-		wl12xx_remove_ie(beacon, WLAN_EID_TIM, ieoffset);
-
-		/*
-		 * remove p2p ie from probe response.
-		 * the fw reponds to probe requests that don't include
-		 * the p2p ie. probe requests with p2p ie will be passed,
-		 * and will be responded by the supplicant (the spec
-		 * forbids including the p2p ie when responding to probe
-		 * requests that didn't include it).
-		 */
-		wl12xx_remove_vendor_ie(beacon, WLAN_OUI_WFA,
-					WLAN_OUI_TYPE_WFA_P2P, ieoffset);
-
-		hdr = (struct ieee80211_hdr *) beacon->data;
-		hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
-						 IEEE80211_STYPE_PROBE_RESP);
-		if (is_ap)
-			ret = wl1271_ap_set_probe_resp_tmpl_legacy(wl, vif,
-						beacon->data,
-						beacon->len,
-						min_rate);
-		else
-			ret = wl1271_cmd_template_set(wl, wlvif->role_id,
-						CMD_TEMPL_PROBE_RESPONSE,
-						beacon->data,
-						beacon->len, 0,
-						min_rate);
-end_bcn:
-		dev_kfree_skb(beacon);
+		ret = wlcore_set_beacon_template(wl, vif, is_ap);
 		if (ret < 0)
 			goto out;
 	}
@@ -3551,6 +3555,14 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
 		ret = wl1271_ap_init_templates(wl, vif);
 		if (ret < 0)
 			goto out;
+
+		ret = wl1271_ap_set_probe_resp_tmpl(wl, wlvif->basic_rate, vif);
+		if (ret < 0)
+			goto out;
+
+		ret = wlcore_set_beacon_template(wl, vif, true);
+		if (ret < 0)
+			goto out;
 	}
 
 	ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf, changed);
@@ -3691,7 +3703,8 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
 		sta_rate_set = sta->supp_rates[wl->hw->conf.channel->band];
 		if (sta->ht_cap.ht_supported)
 			sta_rate_set |=
-			    (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET);
+			  (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET) |
+			  (sta->ht_cap.mcs.rx_mask[1] << HW_MIMO_RATES_OFFSET);
 		sta_ht_cap = sta->ht_cap;
 		sta_exists = true;
 
@@ -3704,13 +3717,11 @@ sta_not_found:
 			u32 rates;
 			int ieoffset;
 			wlvif->aid = bss_conf->aid;
+			wlvif->channel_type = bss_conf->channel_type;
 			wlvif->beacon_int = bss_conf->beacon_int;
 			do_join = true;
 			set_assoc = true;
 
-			/* Cancel connection_loss_work */
-			cancel_delayed_work_sync(&wl->connection_loss_work);
-
 			/*
 			 * use basic rates from AP, and determine lowest rate
 			 * to use with control frames.
@@ -3960,6 +3971,17 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
 	wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed 0x%x",
 		     (int)changed);
 
+	/*
+	 * make sure to cancel pending disconnections if our association
+	 * state changed
+	 */
+	if (!is_ap && (changed & BSS_CHANGED_ASSOC))
+		cancel_delayed_work_sync(&wl->connection_loss_work);
+
+	if (is_ap && (changed & BSS_CHANGED_BEACON_ENABLED) &&
+	    !bss_conf->enable_beacon)
+		wl1271_tx_flush(wl);
+
 	mutex_lock(&wl->mutex);
 
 	if (unlikely(wl->state == WL1271_STATE_OFF))
@@ -4636,7 +4658,7 @@ static const struct ieee80211_ops wl1271_ops = {
 	.prepare_multicast = wl1271_op_prepare_multicast,
 	.configure_filter = wl1271_op_configure_filter,
 	.tx = wl1271_op_tx,
-	.set_key = wl1271_op_set_key,
+	.set_key = wlcore_op_set_key,
 	.hw_scan = wl1271_op_hw_scan,
 	.cancel_hw_scan = wl1271_op_cancel_hw_scan,
 	.sched_scan_start = wl1271_op_sched_scan_start,
@@ -4905,14 +4927,8 @@ static int wl1271_register_hw(struct wl1271 *wl)
 	if (wl->mac80211_registered)
 		return 0;
 
-	ret = wl12xx_get_hw_info(wl);
-	if (ret < 0) {
-		wl1271_error("couldn't get hw info");
-		goto out;
-	}
-
-	ret = wl1271_fetch_nvs(wl);
-	if (ret == 0) {
+	wl1271_fetch_nvs(wl);
+	if (wl->nvs != NULL) {
 		/* NOTE: The wl->nvs->nvs element must be first, in
 		 * order to simplify the casting, we assume it is at
 		 * the beginning of the wl->nvs structure.
@@ -4970,9 +4986,11 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
 		WL1271_CIPHER_SUITE_GEM,
 	};
 
-	/* The tx descriptor buffer and the TKIP space. */
-	wl->hw->extra_tx_headroom = WL1271_EXTRA_SPACE_TKIP +
-		sizeof(struct wl1271_tx_hw_descr);
+	/* The tx descriptor buffer */
+	wl->hw->extra_tx_headroom = sizeof(struct wl1271_tx_hw_descr);
+
+	if (wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE)
+		wl->hw->extra_tx_headroom += WL1271_EXTRA_SPACE_TKIP;
 
 	/* unit us */
 	/* FIXME: find a proper value */
@@ -5025,12 +5043,14 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
 	 */
 	memcpy(&wl->bands[IEEE80211_BAND_2GHZ], &wl1271_band_2ghz,
 	       sizeof(wl1271_band_2ghz));
-	memcpy(&wl->bands[IEEE80211_BAND_2GHZ].ht_cap, &wl->ht_cap,
-	       sizeof(wl->ht_cap));
+	memcpy(&wl->bands[IEEE80211_BAND_2GHZ].ht_cap,
+	       &wl->ht_cap[IEEE80211_BAND_2GHZ],
+	       sizeof(*wl->ht_cap));
 	memcpy(&wl->bands[IEEE80211_BAND_5GHZ], &wl1271_band_5ghz,
 	       sizeof(wl1271_band_5ghz));
-	memcpy(&wl->bands[IEEE80211_BAND_5GHZ].ht_cap, &wl->ht_cap,
-	       sizeof(wl->ht_cap));
+	memcpy(&wl->bands[IEEE80211_BAND_5GHZ].ht_cap,
+	       &wl->ht_cap[IEEE80211_BAND_5GHZ],
+	       sizeof(*wl->ht_cap));
 
 	wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
 		&wl->bands[IEEE80211_BAND_2GHZ];
@@ -5117,6 +5137,7 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size)
 	wl->rx_counter = 0;
 	wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
 	wl->band = IEEE80211_BAND_2GHZ;
+	wl->channel_type = NL80211_CHAN_NO_HT;
 	wl->flags = 0;
 	wl->sg_enabled = true;
 	wl->hw_pg_ver = -1;
@@ -5142,6 +5163,7 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size)
 	wl->state = WL1271_STATE_OFF;
 	wl->fw_type = WL12XX_FW_TYPE_NONE;
 	mutex_init(&wl->mutex);
+	mutex_init(&wl->flush_mutex);
 
 	order = get_order(WL1271_AGGR_BUFFER_SIZE);
 	wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order);
@@ -5222,7 +5244,7 @@ int wlcore_free_hw(struct wl1271 *wl)
 	kfree(wl->nvs);
 	wl->nvs = NULL;
 
-	kfree(wl->fw_status);
+	kfree(wl->fw_status_1);
 	kfree(wl->tx_res_if);
 	destroy_workqueue(wl->freezable_wq);
 
@@ -5279,8 +5301,6 @@ int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev)
 	wlcore_adjust_conf(wl);
 
 	wl->irq = platform_get_irq(pdev, 0);
-	wl->ref_clock = pdata->board_ref_clock;
-	wl->tcxo_clock = pdata->board_tcxo_clock;
 	wl->platform_quirks = pdata->platform_quirks;
 	wl->set_power = pdata->set_power;
 	wl->dev = &pdev->dev;
@@ -5316,6 +5336,16 @@ int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev)
 	}
 	disable_irq(wl->irq);
 
+	ret = wl12xx_get_hw_info(wl);
+	if (ret < 0) {
+		wl1271_error("couldn't get hw info");
+		goto out;
+	}
+
+	ret = wl->ops->identify_chip(wl);
+	if (ret < 0)
+		goto out;
+
 	ret = wl1271_init_ieee80211(wl);
 	if (ret)
 		goto out_irq;

+ 17 - 4
drivers/net/wireless/ti/wlcore/ps.c

@@ -28,6 +28,8 @@
 
 #define WL1271_WAKEUP_TIMEOUT 500
 
+#define ELP_ENTRY_DELAY  5
+
 void wl1271_elp_work(struct work_struct *work)
 {
 	struct delayed_work *dwork;
@@ -72,6 +74,7 @@ out:
 void wl1271_ps_elp_sleep(struct wl1271 *wl)
 {
 	struct wl12xx_vif *wlvif;
+	u32 timeout;
 
 	if (wl->quirks & WLCORE_QUIRK_NO_ELP)
 		return;
@@ -89,8 +92,13 @@ void wl1271_ps_elp_sleep(struct wl1271 *wl)
 			return;
 	}
 
+	if (wl->conf.conn.forced_ps)
+		timeout = ELP_ENTRY_DELAY;
+	else
+		timeout = wl->conf.conn.dynamic_ps_timeout;
+
 	ieee80211_queue_delayed_work(wl->hw, &wl->elp_work,
-		msecs_to_jiffies(wl->conf.conn.dynamic_ps_timeout));
+				     msecs_to_jiffies(timeout));
 }
 
 int wl1271_ps_elp_wakeup(struct wl1271 *wl)
@@ -185,8 +193,12 @@ int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 
 		set_bit(WLVIF_FLAG_IN_PS, &wlvif->flags);
 
-		/* enable beacon early termination. Not relevant for 5GHz */
-		if (wlvif->band == IEEE80211_BAND_2GHZ) {
+		/*
+		 * enable beacon early termination.
+		 * Not relevant for 5GHz and for high rates.
+		 */
+		if ((wlvif->band == IEEE80211_BAND_2GHZ) &&
+		    (wlvif->basic_rate < CONF_HW_BIT_RATE_9MBPS)) {
 			ret = wl1271_acx_bet_enable(wl, wlvif, true);
 			if (ret < 0)
 				return ret;
@@ -196,7 +208,8 @@ int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 		wl1271_debug(DEBUG_PSM, "leaving psm");
 
 		/* disable beacon early termination */
-		if (wlvif->band == IEEE80211_BAND_2GHZ) {
+		if ((wlvif->band == IEEE80211_BAND_2GHZ) &&
+		    (wlvif->basic_rate < CONF_HW_BIT_RATE_9MBPS)) {
 			ret = wl1271_acx_bet_enable(wl, wlvif, false);
 			if (ret < 0)
 				return ret;

+ 6 - 5
drivers/net/wireless/ti/wlcore/rx.c

@@ -186,6 +186,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
 		is_data = 1;
 
 	wl1271_rx_status(wl, desc, IEEE80211_SKB_RXCB(skb), beacon);
+	wlcore_hw_set_rx_csum(wl, desc, skb);
 
 	seq_num = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4;
 	wl1271_debug(DEBUG_RX, "rx skb 0x%p: %d B %s seq %d hlid %d", skb,
@@ -199,12 +200,12 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
 	return is_data;
 }
 
-void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status *status)
+void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status_1 *status)
 {
 	unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0};
 	u32 buf_size;
-	u32 fw_rx_counter  = status->fw_rx_counter & NUM_RX_PKT_DESC_MOD_MASK;
-	u32 drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK;
+	u32 fw_rx_counter = status->fw_rx_counter % wl->num_rx_desc;
+	u32 drv_rx_counter = wl->rx_counter % wl->num_rx_desc;
 	u32 rx_counter;
 	u32 pkt_len, align_pkt_len;
 	u32 pkt_offset, des;
@@ -223,7 +224,7 @@ void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status *status)
 				break;
 			buf_size += align_pkt_len;
 			rx_counter++;
-			rx_counter &= NUM_RX_PKT_DESC_MOD_MASK;
+			rx_counter %= wl->num_rx_desc;
 		}
 
 		if (buf_size == 0) {
@@ -263,7 +264,7 @@ void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status *status)
 
 			wl->rx_counter++;
 			drv_rx_counter++;
-			drv_rx_counter &= NUM_RX_PKT_DESC_MOD_MASK;
+			drv_rx_counter %= wl->num_rx_desc;
 			pkt_offset += wlcore_rx_get_align_buf_size(wl, pkt_len);
 		}
 	}

Некоторые файлы не были показаны из-за большого количества измененных файлов