From da83d20b26d410ed64ffe41bee5308ff524b33d4 Mon Sep 17 00:00:00 2001
From: Shayne Chen <shayne.chen@mediatek.com>
Date: Thu, 16 Mar 2023 12:36:11 +0800
Subject: [PATCH 04/70] sync backport patches/ath9k

---
 drivers/net/wireless/ath/ath.h              |   1 +
 drivers/net/wireless/ath/ath9k/Kconfig      |  13 +
 drivers/net/wireless/ath/ath9k/Makefile     |   1 +
 drivers/net/wireless/ath/ath9k/ahb.c        | 260 +++++++++++++-
 drivers/net/wireless/ath/ath9k/ani.h        |   2 +-
 drivers/net/wireless/ath/ath9k/ar5008_phy.c |  72 ++--
 drivers/net/wireless/ath/ath9k/ar9002_phy.h |  11 +
 drivers/net/wireless/ath/ath9k/ar9003_phy.c |  95 ++---
 drivers/net/wireless/ath/ath9k/ath9k.h      |  34 +-
 drivers/net/wireless/ath/ath9k/channel.c    |   7 +
 drivers/net/wireless/ath/ath9k/common.c     |  21 +-
 drivers/net/wireless/ath/ath9k/debug.c      | 202 +++++++++++
 drivers/net/wireless/ath/ath9k/gpio.c       | 365 ++++++++++++++++++--
 drivers/net/wireless/ath/ath9k/hsr.c        | 247 +++++++++++++
 drivers/net/wireless/ath/ath9k/hsr.h        |  48 +++
 drivers/net/wireless/ath/ath9k/hw-ops.h     |   6 +
 drivers/net/wireless/ath/ath9k/hw.c         | 156 ++++++---
 drivers/net/wireless/ath/ath9k/hw.h         |  12 +
 drivers/net/wireless/ath/ath9k/init.c       |  47 ++-
 drivers/net/wireless/ath/ath9k/mac.c        |   9 +-
 drivers/net/wireless/ath/ath9k/main.c       |  12 +
 drivers/net/wireless/ath/ath9k/pci.c        |   1 +
 drivers/net/wireless/ath/ath9k/phy.h        |   3 +
 include/linux/ath9k_platform.h              |   7 +
 local-symbols                               |   1 +
 25 files changed, 1415 insertions(+), 218 deletions(-)
 create mode 100644 drivers/net/wireless/ath/ath9k/hsr.c
 create mode 100644 drivers/net/wireless/ath/ath9k/hsr.h

diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h
index cfe535a..bb30fd7 100644
--- a/drivers/net/wireless/ath/ath.h
+++ b/drivers/net/wireless/ath/ath.h
@@ -149,6 +149,7 @@ struct ath_common {
 	int debug_mask;
 	enum ath_device_state state;
 	unsigned long op_flags;
+	u32 chan_bw;
 
 	struct ath_ani ani;
 
diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig
index eed0f3a..d2ea086 100644
--- a/drivers/net/wireless/ath/ath9k/Kconfig
+++ b/drivers/net/wireless/ath/ath9k/Kconfig
@@ -58,6 +58,19 @@ config ATH9K_AHB
 	  Say Y, if you have a SoC with a compatible built-in
 	  wireless MAC. Say N if unsure.
 
+config ATH9K_UBNTHSR
+	bool "Ubiquiti UniFi Outdoor Plus HSR support"
+	depends on ATH9K
+	---help---
+	  This options enables code to control the HSR RF
+	  filter in the receive path of the Ubiquiti UniFi
+	  Outdoor Plus access point.
+
+	  Say Y if you want to use the access point. The
+	  code will only be used if the device is detected,
+	  so it does not harm other setup other than occupying
+	  a bit of memory.
+
 config ATH9K_DEBUGFS
 	bool "Atheros ath9k debugging"
 	depends on ATH9K && DEBUG_FS
diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile
index 847c8a8..6427bc6 100644
--- a/drivers/net/wireless/ath/ath9k/Makefile
+++ b/drivers/net/wireless/ath/ath9k/Makefile
@@ -17,6 +17,7 @@ ath9k-$(CPTCFG_ATH9K_DFS_CERTIFIED) += dfs.o
 ath9k-$(CPTCFG_ATH9K_TX99) += tx99.o
 ath9k-$(CPTCFG_ATH9K_WOW) += wow.o
 ath9k-$(CPTCFG_ATH9K_HWRNG) += rng.o
+ath9k-$(CPTCFG_ATH9K_UBNTHSR) += hsr.o
 
 ath9k-$(CPTCFG_ATH9K_DEBUGFS) += debug.o
 
diff --git a/drivers/net/wireless/ath/ath9k/ahb.c b/drivers/net/wireless/ath/ath9k/ahb.c
index 9cd12b2..d6c381b 100644
--- a/drivers/net/wireless/ath/ath9k/ahb.c
+++ b/drivers/net/wireless/ath/ath9k/ahb.c
@@ -20,7 +20,15 @@
 #include <linux/platform_device.h>
 #include <linux/module.h>
 #include <linux/mod_devicetable.h>
+#include <linux/of_device.h>
 #include "ath9k.h"
+#include <linux/ath9k_platform.h>
+
+#ifdef CONFIG_OF
+#include <asm/mach-ath79/ath79.h>
+#include <asm/mach-ath79/ar71xx_regs.h>
+#include <linux/mtd/mtd.h>
+#endif
 
 static const struct platform_device_id ath9k_platform_id_table[] = {
 	{
@@ -69,6 +77,236 @@ static const struct ath_bus_ops ath_ahb_bus_ops  = {
 	.eeprom_read = ath_ahb_eeprom_read,
 };
 
+#ifdef CONFIG_OF
+
+#define QCA955X_DDR_CTL_CONFIG          0x108
+#define QCA955X_DDR_CTL_CONFIG_ACT_WMAC BIT(23)
+
+static int of_get_wifi_cal(struct device_node *np, struct ath9k_platform_data *pdata)
+{
+#ifdef CONFIG_MTD
+	struct device_node *mtd_np = NULL;
+	size_t retlen;
+	int size, ret;
+	struct mtd_info *mtd;
+	const char *part;
+	const __be32 *list;
+	phandle phandle;
+
+	list = of_get_property(np, "mtd-cal-data", &size);
+	if (!list)
+		return 0;
+
+	if (size != (2 * sizeof(*list)))
+		return 1;
+
+	phandle = be32_to_cpup(list++);
+	if (phandle)
+		mtd_np = of_find_node_by_phandle(phandle);
+
+	if (!mtd_np)
+		return 1;
+
+	part = of_get_property(mtd_np, "label", NULL);
+	if (!part)
+		part = mtd_np->name;
+
+	mtd = get_mtd_device_nm(part);
+	if (IS_ERR(mtd))
+		return 1;
+
+	ret = mtd_read(mtd, be32_to_cpup(list), sizeof(pdata->eeprom_data),
+			&retlen, (u8*)pdata->eeprom_data);
+	put_mtd_device(mtd);
+
+#endif
+	return 0;
+}
+
+static int ar913x_wmac_reset(void)
+{
+	ath79_device_reset_set(AR913X_RESET_AMBA2WMAC);
+	mdelay(10);
+
+	ath79_device_reset_clear(AR913X_RESET_AMBA2WMAC);
+	mdelay(10);
+
+	return 0;
+}
+
+static int ar933x_wmac_reset(void)
+{
+	int retries = 20;
+
+	ath79_device_reset_set(AR933X_RESET_WMAC);
+	ath79_device_reset_clear(AR933X_RESET_WMAC);
+
+	while (1) {
+		u32 bootstrap;
+
+		bootstrap = ath79_reset_rr(AR933X_RESET_REG_BOOTSTRAP);
+		if ((bootstrap & AR933X_BOOTSTRAP_EEPBUSY) == 0)
+			return 0;
+
+		if (retries-- == 0)
+			break;
+
+		udelay(10000);
+	}
+
+	pr_err("ar933x: WMAC reset timed out");
+	return -ETIMEDOUT;
+}
+
+static int qca955x_wmac_reset(void)
+{
+	int i;
+
+	/* Try to wait for WMAC DDR activity to stop */
+	for (i = 0; i < 10; i++) {
+		if (!(__raw_readl(ath79_ddr_base + QCA955X_DDR_CTL_CONFIG) &
+		    QCA955X_DDR_CTL_CONFIG_ACT_WMAC))
+			break;
+
+		udelay(10);
+	}
+
+	ath79_device_reset_set(QCA955X_RESET_RTC);
+	udelay(10);
+	ath79_device_reset_clear(QCA955X_RESET_RTC);
+	udelay(10);
+
+	return 0;
+}
+
+enum {
+	AR913X_WMAC = 0,
+	AR933X_WMAC,
+	AR934X_WMAC,
+	QCA953X_WMAC,
+	QCA955X_WMAC,
+	QCA956X_WMAC,
+};
+
+static int ar9330_get_soc_revision(void)
+{
+	if (ath79_soc_rev == 1)
+		return ath79_soc_rev;
+
+	return 0;
+}
+
+static int ath79_get_soc_revision(void)
+{
+	return ath79_soc_rev;
+}
+
+static const struct of_ath_ahb_data {
+	u16 dev_id;
+	u32 bootstrap_reg;
+	u32 bootstrap_ref;
+
+	int (*soc_revision)(void);
+	int (*wmac_reset)(void);
+} of_ath_ahb_data[] = {
+	[AR913X_WMAC] = {
+		.dev_id = AR5416_AR9100_DEVID,
+		.wmac_reset = ar913x_wmac_reset,
+
+	},
+	[AR933X_WMAC] = {
+		.dev_id = AR9300_DEVID_AR9330,
+		.bootstrap_reg = AR933X_RESET_REG_BOOTSTRAP,
+		.bootstrap_ref = AR933X_BOOTSTRAP_REF_CLK_40,
+		.soc_revision = ar9330_get_soc_revision,
+		.wmac_reset = ar933x_wmac_reset,
+	},
+	[AR934X_WMAC] = {
+		.dev_id = AR9300_DEVID_AR9340,
+		.bootstrap_reg = AR934X_RESET_REG_BOOTSTRAP,
+		.bootstrap_ref = AR934X_BOOTSTRAP_REF_CLK_40,
+		.soc_revision = ath79_get_soc_revision,
+	},
+	[QCA953X_WMAC] = {
+		.dev_id = AR9300_DEVID_AR953X,
+		.bootstrap_reg = QCA953X_RESET_REG_BOOTSTRAP,
+		.bootstrap_ref = QCA953X_BOOTSTRAP_REF_CLK_40,
+		.soc_revision = ath79_get_soc_revision,
+	},
+	[QCA955X_WMAC] = {
+		.dev_id = AR9300_DEVID_QCA955X,
+		.bootstrap_reg = QCA955X_RESET_REG_BOOTSTRAP,
+		.bootstrap_ref = QCA955X_BOOTSTRAP_REF_CLK_40,
+		.wmac_reset = qca955x_wmac_reset,
+	},
+	[QCA956X_WMAC] = {
+		.dev_id = AR9300_DEVID_QCA956X,
+		.bootstrap_reg = QCA956X_RESET_REG_BOOTSTRAP,
+		.bootstrap_ref = QCA956X_BOOTSTRAP_REF_CLK_40,
+		.soc_revision = ath79_get_soc_revision,
+	},
+};
+
+const struct of_device_id of_ath_ahb_match[] = {
+	{ .compatible = "qca,ar9130-wmac", .data = &of_ath_ahb_data[AR913X_WMAC] },
+	{ .compatible = "qca,ar9330-wmac", .data = &of_ath_ahb_data[AR933X_WMAC] },
+	{ .compatible = "qca,ar9340-wmac", .data = &of_ath_ahb_data[AR934X_WMAC] },
+	{ .compatible = "qca,qca9530-wmac", .data = &of_ath_ahb_data[QCA953X_WMAC] },
+	{ .compatible = "qca,qca9550-wmac", .data = &of_ath_ahb_data[QCA955X_WMAC] },
+	{ .compatible = "qca,qca9560-wmac", .data = &of_ath_ahb_data[QCA956X_WMAC] },
+	{},
+};
+MODULE_DEVICE_TABLE(of, of_ath_ahb_match);
+
+static int of_ath_ahb_probe(struct platform_device *pdev)
+{
+	struct ath9k_platform_data *pdata;
+	const struct of_device_id *match;
+	const struct of_ath_ahb_data *data;
+	u8 led_pin;
+
+	match = of_match_device(of_ath_ahb_match, &pdev->dev);
+	data = (const struct of_ath_ahb_data *)match->data;
+
+	pdata = dev_get_platdata(&pdev->dev);
+
+	if (!of_property_read_u8(pdev->dev.of_node, "qca,led-pin", &led_pin))
+		pdata->led_pin = led_pin;
+	else
+		pdata->led_pin = -1;
+
+	if (of_property_read_bool(pdev->dev.of_node, "qca,tx-gain-buffalo"))
+		pdata->tx_gain_buffalo = true;
+
+	if (data->wmac_reset) {
+		data->wmac_reset();
+		pdata->external_reset = data->wmac_reset;
+	}
+
+	if (data->dev_id == AR9300_DEVID_AR953X) {
+		/*
+		 * QCA953x only supports 25MHz refclk.
+		 * Some vendors have an invalid bootstrap option
+		 * set, which would break the WMAC here.
+		 */
+		pdata->is_clk_25mhz = true;
+	} else if (data->bootstrap_reg && data->bootstrap_ref) {
+		u32 t = ath79_reset_rr(data->bootstrap_reg);
+		if (t & data->bootstrap_ref)
+			pdata->is_clk_25mhz = false;
+		else
+			pdata->is_clk_25mhz = true;
+	}
+
+	pdata->get_mac_revision = data->soc_revision;
+
+	if (of_get_wifi_cal(pdev->dev.of_node, pdata))
+		dev_err(&pdev->dev, "failed to load calibration data from mtd device\n");
+
+	return data->dev_id;
+}
+#endif
+
 static int ath_ahb_probe(struct platform_device *pdev)
 {
 	void __iomem *mem;
@@ -80,6 +318,17 @@ static int ath_ahb_probe(struct platform_device *pdev)
 	int ret = 0;
 	struct ath_hw *ah;
 	char hw_name[64];
+	u16 dev_id;
+
+	if (id)
+		dev_id = id->driver_data;
+
+#ifdef CONFIG_OF
+	if (pdev->dev.of_node)
+		pdev->dev.platform_data = devm_kzalloc(&pdev->dev,
+					sizeof(struct ath9k_platform_data),
+					GFP_KERNEL);
+#endif
 
 	if (!dev_get_platdata(&pdev->dev)) {
 		dev_err(&pdev->dev, "no platform data specified\n");
@@ -118,13 +367,16 @@ static int ath_ahb_probe(struct platform_device *pdev)
 	sc->mem = mem;
 	sc->irq = irq;
 
+#ifdef CONFIG_OF
+	dev_id = of_ath_ahb_probe(pdev);
+#endif
 	ret = request_irq(irq, ath_isr, IRQF_SHARED, "ath9k", sc);
 	if (ret) {
 		dev_err(&pdev->dev, "request_irq failed\n");
 		goto err_free_hw;
 	}
 
-	ret = ath9k_init_device(id->driver_data, sc, &ath_ahb_bus_ops);
+	ret = ath9k_init_device(dev_id, sc, &ath_ahb_bus_ops);
 	if (ret) {
 		dev_err(&pdev->dev, "failed to initialize device\n");
 		goto err_irq;
@@ -155,6 +407,9 @@ static int ath_ahb_remove(struct platform_device *pdev)
 		free_irq(sc->irq, sc);
 		ieee80211_free_hw(sc->hw);
 	}
+#ifdef CONFIG_OF
+	pdev->dev.platform_data = NULL;
+#endif
 
 	return 0;
 }
@@ -164,6 +419,9 @@ static struct platform_driver ath_ahb_driver = {
 	.remove     = ath_ahb_remove,
 	.driver		= {
 		.name	= "ath9k",
+#ifdef CONFIG_OF
+		.of_match_table = of_ath_ahb_match,
+#endif
 	},
 	.id_table    = ath9k_platform_id_table,
 };
diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h
index c40965b..f66f7ed 100644
--- a/drivers/net/wireless/ath/ath9k/ani.h
+++ b/drivers/net/wireless/ath/ath9k/ani.h
@@ -42,7 +42,7 @@
 #define ATH9K_ANI_PERIOD                  300
 
 /* in ms */
-#define ATH9K_ANI_POLLINTERVAL            1000
+#define ATH9K_ANI_POLLINTERVAL            300
 
 #define ATH9K_SIG_FIRSTEP_SETTING_MIN     0
 #define ATH9K_SIG_FIRSTEP_SETTING_MAX     20
diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
index 7a45f5f..3f0ca1d 100644
--- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
@@ -969,55 +969,6 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah,
 		 * on == 0 means more noise imm
 		 */
 		u32 on = param ? 1 : 0;
-		/*
-		 * make register setting for default
-		 * (weak sig detect ON) come from INI file
-		 */
-		int m1ThreshLow = on ?
-			aniState->iniDef.m1ThreshLow : m1ThreshLow_off;
-		int m2ThreshLow = on ?
-			aniState->iniDef.m2ThreshLow : m2ThreshLow_off;
-		int m1Thresh = on ?
-			aniState->iniDef.m1Thresh : m1Thresh_off;
-		int m2Thresh = on ?
-			aniState->iniDef.m2Thresh : m2Thresh_off;
-		int m2CountThr = on ?
-			aniState->iniDef.m2CountThr : m2CountThr_off;
-		int m2CountThrLow = on ?
-			aniState->iniDef.m2CountThrLow : m2CountThrLow_off;
-		int m1ThreshLowExt = on ?
-			aniState->iniDef.m1ThreshLowExt : m1ThreshLowExt_off;
-		int m2ThreshLowExt = on ?
-			aniState->iniDef.m2ThreshLowExt : m2ThreshLowExt_off;
-		int m1ThreshExt = on ?
-			aniState->iniDef.m1ThreshExt : m1ThreshExt_off;
-		int m2ThreshExt = on ?
-			aniState->iniDef.m2ThreshExt : m2ThreshExt_off;
-
-		REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
-			      AR_PHY_SFCORR_LOW_M1_THRESH_LOW,
-			      m1ThreshLow);
-		REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
-			      AR_PHY_SFCORR_LOW_M2_THRESH_LOW,
-			      m2ThreshLow);
-		REG_RMW_FIELD(ah, AR_PHY_SFCORR,
-			      AR_PHY_SFCORR_M1_THRESH, m1Thresh);
-		REG_RMW_FIELD(ah, AR_PHY_SFCORR,
-			      AR_PHY_SFCORR_M2_THRESH, m2Thresh);
-		REG_RMW_FIELD(ah, AR_PHY_SFCORR,
-			      AR_PHY_SFCORR_M2COUNT_THR, m2CountThr);
-		REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
-			      AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW,
-			      m2CountThrLow);
-
-		REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
-			      AR_PHY_SFCORR_EXT_M1_THRESH_LOW, m1ThreshLowExt);
-		REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
-			      AR_PHY_SFCORR_EXT_M2_THRESH_LOW, m2ThreshLowExt);
-		REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
-			      AR_PHY_SFCORR_EXT_M1_THRESH, m1ThreshExt);
-		REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
-			      AR_PHY_SFCORR_EXT_M2_THRESH, m2ThreshExt);
 
 		if (on)
 			REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
@@ -1340,9 +1291,30 @@ void ar5008_hw_init_rate_txpower(struct ath_hw *ah, int16_t *rate_array,
 	}
 }
 
+static void ar5008_hw_get_adc_entropy(struct ath_hw *ah, u8 *buf, size_t len)
+{
+	int i, j;
+
+	REG_RMW_FIELD(ah, AR_PHY_TEST, AR_PHY_TEST_BBB_OBS_SEL, 1);
+	REG_CLR_BIT(ah, AR_PHY_TEST, AR_PHY_TEST_RX_OBS_SEL_BIT5);
+	REG_RMW_FIELD(ah, AR_PHY_TEST2, AR_PHY_TEST2_RX_OBS_SEL, 0);
+
+	memset(buf, 0, len);
+	for (i = 0; i < len; i++) {
+		for (j = 0; j < 4; j++) {
+			u32 regval = REG_READ(ah, AR_PHY_TST_ADC);
+
+			buf[i] <<= 2;
+			buf[i] |= (regval & 1) | ((regval & BIT(9)) >> 8);
+			udelay(1);
+		}
+	}
+}
+
 int ar5008_hw_attach_phy_ops(struct ath_hw *ah)
 {
 	struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
+	struct ath_hw_ops *ops = ath9k_hw_ops(ah);
 	static const u32 ar5416_cca_regs[6] = {
 		AR_PHY_CCA,
 		AR_PHY_CH1_CCA,
@@ -1357,6 +1329,8 @@ int ar5008_hw_attach_phy_ops(struct ath_hw *ah)
 	if (ret)
 	    return ret;
 
+	ops->get_adc_entropy = ar5008_hw_get_adc_entropy;
+
 	priv_ops->rf_set_freq = ar5008_hw_set_channel;
 	priv_ops->spur_mitigate_freq = ar5008_hw_spur_mitigate;
 
diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.h b/drivers/net/wireless/ath/ath9k/ar9002_phy.h
index 2b58245..d20a936 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_phy.h
+++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.h
@@ -20,6 +20,12 @@
 #define PHY_AGC_CLR             0x10000000
 #define RFSILENT_BB             0x00002000
 
+#define AR_PHY_TEST_BBB_OBS_SEL       0x780000
+#define AR_PHY_TEST_BBB_OBS_SEL_S     19
+
+#define AR_PHY_TEST_RX_OBS_SEL_BIT5_S 23
+#define AR_PHY_TEST_RX_OBS_SEL_BIT5   (1 << AR_PHY_TEST_RX_OBS_SEL_BIT5_S)
+
 #define AR_PHY_TURBO                0x9804
 #define AR_PHY_FC_TURBO_MODE        0x00000001
 #define AR_PHY_FC_TURBO_SHORT       0x00000002
@@ -36,6 +42,9 @@
 
 #define AR_PHY_TEST2			0x9808
 
+#define AR_PHY_TEST2_RX_OBS_SEL        0x3C00
+#define AR_PHY_TEST2_RX_OBS_SEL_S      10
+
 #define AR_PHY_TIMING2           0x9810
 #define AR_PHY_TIMING3           0x9814
 #define AR_PHY_TIMING3_DSC_MAN   0xFFFE0000
@@ -393,6 +402,8 @@
 #define AR_PHY_RFBUS_GRANT       0x9C20
 #define AR_PHY_RFBUS_GRANT_EN    0x00000001
 
+#define AR_PHY_TST_ADC      0x9C24
+
 #define AR_PHY_CHAN_INFO_GAIN_DIFF             0x9CF4
 #define AR_PHY_CHAN_INFO_GAIN_DIFF_UPPER_LIMIT 320
 
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
index acd98a8..ccc551f 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
@@ -42,20 +42,6 @@ static const int cycpwrThr1_table[] =
 /* level:  0   1   2   3   4   5   6   7   8  */
 	{ -6, -4, -2,  0,  2,  4,  6,  8 };     /* lvl 0-7, default 3 */
 
-/*
- * register values to turn OFDM weak signal detection OFF
- */
-static const int m1ThreshLow_off = 127;
-static const int m2ThreshLow_off = 127;
-static const int m1Thresh_off = 127;
-static const int m2Thresh_off = 127;
-static const int m2CountThr_off =  31;
-static const int m2CountThrLow_off =  63;
-static const int m1ThreshLowExt_off = 127;
-static const int m2ThreshLowExt_off = 127;
-static const int m1ThreshExt_off = 127;
-static const int m2ThreshExt_off = 127;
-
 static const u8 ofdm2pwr[] = {
 	ALL_TARGET_LEGACY_6_24,
 	ALL_TARGET_LEGACY_6_24,
@@ -1068,11 +1054,6 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah,
 	struct ath_common *common = ath9k_hw_common(ah);
 	struct ath9k_channel *chan = ah->curchan;
 	struct ar5416AniState *aniState = &ah->ani;
-	int m1ThreshLow, m2ThreshLow;
-	int m1Thresh, m2Thresh;
-	int m2CountThr, m2CountThrLow;
-	int m1ThreshLowExt, m2ThreshLowExt;
-	int m1ThreshExt, m2ThreshExt;
 	s32 value, value2;
 
 	switch (cmd & ah->ani_function) {
@@ -1086,61 +1067,6 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah,
 		 */
 		u32 on = param ? 1 : 0;
 
-		if (AR_SREV_9462(ah) || AR_SREV_9565(ah))
-			goto skip_ws_det;
-
-		m1ThreshLow = on ?
-			aniState->iniDef.m1ThreshLow : m1ThreshLow_off;
-		m2ThreshLow = on ?
-			aniState->iniDef.m2ThreshLow : m2ThreshLow_off;
-		m1Thresh = on ?
-			aniState->iniDef.m1Thresh : m1Thresh_off;
-		m2Thresh = on ?
-			aniState->iniDef.m2Thresh : m2Thresh_off;
-		m2CountThr = on ?
-			aniState->iniDef.m2CountThr : m2CountThr_off;
-		m2CountThrLow = on ?
-			aniState->iniDef.m2CountThrLow : m2CountThrLow_off;
-		m1ThreshLowExt = on ?
-			aniState->iniDef.m1ThreshLowExt : m1ThreshLowExt_off;
-		m2ThreshLowExt = on ?
-			aniState->iniDef.m2ThreshLowExt : m2ThreshLowExt_off;
-		m1ThreshExt = on ?
-			aniState->iniDef.m1ThreshExt : m1ThreshExt_off;
-		m2ThreshExt = on ?
-			aniState->iniDef.m2ThreshExt : m2ThreshExt_off;
-
-		REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
-			      AR_PHY_SFCORR_LOW_M1_THRESH_LOW,
-			      m1ThreshLow);
-		REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
-			      AR_PHY_SFCORR_LOW_M2_THRESH_LOW,
-			      m2ThreshLow);
-		REG_RMW_FIELD(ah, AR_PHY_SFCORR,
-			      AR_PHY_SFCORR_M1_THRESH,
-			      m1Thresh);
-		REG_RMW_FIELD(ah, AR_PHY_SFCORR,
-			      AR_PHY_SFCORR_M2_THRESH,
-			      m2Thresh);
-		REG_RMW_FIELD(ah, AR_PHY_SFCORR,
-			      AR_PHY_SFCORR_M2COUNT_THR,
-			      m2CountThr);
-		REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
-			      AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW,
-			      m2CountThrLow);
-		REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
-			      AR_PHY_SFCORR_EXT_M1_THRESH_LOW,
-			      m1ThreshLowExt);
-		REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
-			      AR_PHY_SFCORR_EXT_M2_THRESH_LOW,
-			      m2ThreshLowExt);
-		REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
-			      AR_PHY_SFCORR_EXT_M1_THRESH,
-			      m1ThreshExt);
-		REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
-			      AR_PHY_SFCORR_EXT_M2_THRESH,
-			      m2ThreshExt);
-skip_ws_det:
 		if (on)
 			REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
 				    AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
@@ -1918,6 +1844,26 @@ void ar9003_hw_init_rate_txpower(struct ath_hw *ah, u8 *rate_array,
 	}
 }
 
+static void ar9003_hw_get_adc_entropy(struct ath_hw *ah, u8 *buf, size_t len)
+{
+	int i, j;
+
+	REG_RMW_FIELD(ah, AR_PHY_TEST, AR_PHY_TEST_BBB_OBS_SEL, 1);
+	REG_CLR_BIT(ah, AR_PHY_TEST, AR_PHY_TEST_RX_OBS_SEL_BIT5);
+	REG_RMW_FIELD(ah, AR_PHY_TEST_CTL_STATUS, AR_PHY_TEST_CTL_RX_OBS_SEL, 0);
+
+	memset(buf, 0, len);
+	for (i = 0; i < len; i++) {
+		for (j = 0; j < 4; j++) {
+			u32 regval = REG_READ(ah, AR_PHY_TST_ADC);
+
+			buf[i] <<= 2;
+			buf[i] |= (regval & 1) | ((regval & BIT(10)) >> 9);
+			udelay(1);
+		}
+	}
+}
+
 void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
 {
 	struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
@@ -1954,6 +1900,7 @@ void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
 	priv_ops->set_radar_params = ar9003_hw_set_radar_params;
 	priv_ops->fast_chan_change = ar9003_hw_fast_chan_change;
 
+	ops->get_adc_entropy = ar9003_hw_get_adc_entropy;
 	ops->antdiv_comb_conf_get = ar9003_hw_antdiv_comb_conf_get;
 	ops->antdiv_comb_conf_set = ar9003_hw_antdiv_comb_conf_set;
 	ops->spectral_scan_config = ar9003_hw_spectral_scan_config;
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index eb86ba0..28242ed 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -25,6 +25,8 @@
 #include <linux/completion.h>
 #include <linux/time.h>
 #include <linux/hw_random.h>
+#include <linux/gpio/driver.h>
+#include <linux/reset.h>
 
 #include "common.h"
 #include "debug.h"
@@ -89,7 +91,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
 		(_l) &= ((_sz) - 1);		\
 	} while (0)
 
-#define ATH_RXBUF               512
+#define ATH_RXBUF               256
 #define ATH_TXBUF               512
 #define ATH_TXBUF_RESERVE       5
 #define ATH_TXMAXTRY            13
@@ -844,6 +846,9 @@ static inline int ath9k_dump_btcoex(struct ath_softc *sc, u8 *buf, u32 size)
 #ifdef CPTCFG_MAC80211_LEDS
 void ath_init_leds(struct ath_softc *sc);
 void ath_deinit_leds(struct ath_softc *sc);
+int ath_create_gpio_led(struct ath_softc *sc, int gpio, const char *name,
+			const char *trigger, bool active_low);
+
 #else
 static inline void ath_init_leds(struct ath_softc *sc)
 {
@@ -980,6 +985,21 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs);
 
 #define ATH9K_NUM_CHANCTX  2 /* supports 2 operating channels */
 
+struct ath_led {
+	struct list_head list;
+	struct ath_softc *sc;
+	const struct gpio_led *gpio;
+	struct led_classdev cdev;
+};
+
+#ifdef CONFIG_GPIOLIB
+struct ath9k_gpio_chip {
+	struct ath_softc *sc;
+	char label[32];
+	struct gpio_chip gchip;
+};
+#endif
+
 struct ath_softc {
 	struct ieee80211_hw *hw;
 	struct device *dev;
@@ -993,6 +1013,9 @@ struct ath_softc {
 	struct ath_hw *sc_ah;
 	void __iomem *mem;
 	int irq;
+#ifdef CONFIG_OF
+	struct reset_control *reset;
+#endif
 	spinlock_t sc_serial_rw;
 	spinlock_t sc_pm_lock;
 	spinlock_t sc_pcu_lock;
@@ -1033,9 +1056,12 @@ struct ath_softc {
 	spinlock_t chan_lock;
 
 #ifdef CPTCFG_MAC80211_LEDS
-	bool led_registered;
-	char led_name[32];
-	struct led_classdev led_cdev;
+	const char *led_default_trigger;
+	struct list_head leds;
+#ifdef CONFIG_GPIOLIB
+	struct ath9k_gpio_chip *gpiochip;
+	struct platform_device *btnpdev;	/* gpio-keys-polled */
+#endif
 #endif
 
 #ifdef CPTCFG_ATH9K_DEBUGFS
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
index 86c59bd..49cf036 100644
--- a/drivers/net/wireless/ath/ath9k/channel.c
+++ b/drivers/net/wireless/ath/ath9k/channel.c
@@ -15,6 +15,7 @@
  */
 
 #include "ath9k.h"
+#include "hsr.h"
 
 /* Set/change channels.  If the channel is really being changed, it's done
  * by reseting the chip.  To accomplish this we must first cleanup any pending
@@ -22,6 +23,7 @@
  */
 static int ath_set_channel(struct ath_softc *sc)
 {
+	struct device_node *np = sc->dev->of_node;
 	struct ath_hw *ah = sc->sc_ah;
 	struct ath_common *common = ath9k_hw_common(ah);
 	struct ieee80211_hw *hw = sc->hw;
@@ -42,6 +44,11 @@ static int ath_set_channel(struct ath_softc *sc)
 	ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n",
 		chan->center_freq, chandef->width);
 
+	if (of_property_read_bool(np, "ubnt,hsr")) {
+		ath9k_hsr_enable(ah, chandef->width, chan->center_freq);
+		ath9k_hsr_status(ah);
+	}
+
 	/* update survey stats for the old channel before switching */
 	spin_lock_irqsave(&common->cc_lock, flags);
 	ath_update_survey_stats(sc);
diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c
index 099f3d4..86d4a50 100644
--- a/drivers/net/wireless/ath/ath9k/common.c
+++ b/drivers/net/wireless/ath/ath9k/common.c
@@ -297,11 +297,13 @@ EXPORT_SYMBOL(ath9k_cmn_get_hw_crypto_keytype);
 /*
  * Update internal channel flags.
  */
-static void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan,
+static void ath9k_cmn_update_ichannel(struct ath_common *common,
+				      struct ath9k_channel *ichan,
 				      struct cfg80211_chan_def *chandef)
 {
 	struct ieee80211_channel *chan = chandef->chan;
 	u16 flags = 0;
+	int width;
 
 	ichan->channel = chan->center_freq;
 	ichan->chan = chan;
@@ -309,7 +311,19 @@ static void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan,
 	if (chan->band == NL80211_BAND_5GHZ)
 		flags |= CHANNEL_5GHZ;
 
-	switch (chandef->width) {
+	switch (common->chan_bw) {
+	case 5:
+		width = NL80211_CHAN_WIDTH_5;
+		break;
+	case 10:
+		width = NL80211_CHAN_WIDTH_10;
+		break;
+	default:
+		width = chandef->width;
+		break;
+	}
+
+	switch (width) {
 	case NL80211_CHAN_WIDTH_5:
 		flags |= CHANNEL_QUARTER;
 		break;
@@ -342,10 +356,11 @@ struct ath9k_channel *ath9k_cmn_get_channel(struct ieee80211_hw *hw,
 					    struct cfg80211_chan_def *chandef)
 {
 	struct ieee80211_channel *curchan = chandef->chan;
+	struct ath_common *common = ath9k_hw_common(ah);
 	struct ath9k_channel *channel;
 
 	channel = &ah->channels[curchan->hw_value];
-	ath9k_cmn_update_ichannel(channel, chandef);
+	ath9k_cmn_update_ichannel(common, channel, chandef);
 
 	return channel;
 }
diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index fb87075..62996df 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -1413,6 +1413,198 @@ void ath9k_deinit_debug(struct ath_softc *sc)
 	ath9k_cmn_spectral_deinit_debug(&sc->spec_priv);
 }
 
+static ssize_t read_file_eeprom(struct file *file, char __user *user_buf,
+			     size_t count, loff_t *ppos)
+{
+	struct ath_softc *sc = file->private_data;
+	struct ath_hw *ah = sc->sc_ah;
+	struct ath_common *common = ath9k_hw_common(ah);
+	int bytes = 0;
+	int pos = *ppos;
+	int size = 4096;
+	u16 val;
+	int i;
+
+	if (AR_SREV_9300_20_OR_LATER(ah))
+		size = 16384;
+
+	if (*ppos < 0)
+		return -EINVAL;
+
+	if (count > size - *ppos)
+		count = size - *ppos;
+
+	for (i = *ppos / 2; count > 0; count -= bytes, *ppos += bytes, i++) {
+		void *from = &val;
+
+		if (!common->bus_ops->eeprom_read(common, i, &val))
+			val = 0xffff;
+
+		if (*ppos % 2) {
+			from++;
+			bytes = 1;
+		} else if (count == 1) {
+			bytes = 1;
+		} else {
+			bytes = 2;
+		}
+		copy_to_user(user_buf, from, bytes);
+		user_buf += bytes;
+	}
+	return *ppos - pos;
+}
+
+static const struct file_operations fops_eeprom = {
+	.read = read_file_eeprom,
+	.open = simple_open,
+	.owner = THIS_MODULE
+};
+
+
+static ssize_t read_file_chan_bw(struct file *file, char __user *user_buf,
+			     size_t count, loff_t *ppos)
+{
+	struct ath_softc *sc = file->private_data;
+	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+	char buf[32];
+	unsigned int len;
+
+	len = sprintf(buf, "0x%08x\n", common->chan_bw);
+	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t write_file_chan_bw(struct file *file, const char __user *user_buf,
+			     size_t count, loff_t *ppos)
+{
+	struct ath_softc *sc = file->private_data;
+	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+	unsigned long chan_bw;
+	char buf[32];
+	ssize_t len;
+
+	len = min(count, sizeof(buf) - 1);
+	if (copy_from_user(buf, user_buf, len))
+		return -EFAULT;
+
+	buf[len] = '\0';
+	if (kstrtoul(buf, 0, &chan_bw))
+		return -EINVAL;
+
+	common->chan_bw = chan_bw;
+	if (!test_bit(ATH_OP_INVALID, &common->op_flags))
+		ath9k_ops.config(sc->hw, IEEE80211_CONF_CHANGE_CHANNEL);
+
+	return count;
+}
+
+static const struct file_operations fops_chanbw = {
+	.read = read_file_chan_bw,
+	.write = write_file_chan_bw,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
+#ifdef CONFIG_MAC80211_LEDS
+
+static ssize_t write_file_gpio_led(struct file *file, const char __user *ubuf,
+				   size_t count, loff_t *ppos)
+{
+	struct ath_softc *sc = file->private_data;
+	char buf[32], *str, *name, *c;
+	ssize_t len;
+	unsigned int gpio;
+	bool active_low = false;
+
+	len = min(count, sizeof(buf) - 1);
+	if (copy_from_user(buf, ubuf, len))
+		return -EFAULT;
+
+	buf[len] = '\0';
+	name = strchr(buf, ',');
+	if (!name)
+		return -EINVAL;
+
+	*(name++) = 0;
+	if (!*name)
+		return -EINVAL;
+
+	c = strchr(name, '\n');
+	if (c)
+		*c = 0;
+
+	str = buf;
+	if (*str == '!') {
+		str++;
+		active_low = true;
+	}
+
+	if (kstrtouint(str, 0, &gpio) < 0)
+		return -EINVAL;
+
+	if (gpio >= sc->sc_ah->caps.num_gpio_pins)
+		return -EINVAL;
+
+	if (ath_create_gpio_led(sc, gpio, name, NULL, active_low) < 0)
+		return -EINVAL;
+
+	return count;
+}
+
+static const struct file_operations fops_gpio_led = {
+	.write = write_file_gpio_led,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
+#endif
+
+
+static ssize_t read_file_diag(struct file *file, char __user *user_buf,
+			     size_t count, loff_t *ppos)
+{
+	struct ath_softc *sc = file->private_data;
+	struct ath_hw *ah = sc->sc_ah;
+	char buf[32];
+	unsigned int len;
+
+	len = sprintf(buf, "0x%08lx\n", ah->diag);
+	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t write_file_diag(struct file *file, const char __user *user_buf,
+			     size_t count, loff_t *ppos)
+{
+	struct ath_softc *sc = file->private_data;
+	struct ath_hw *ah = sc->sc_ah;
+	unsigned long diag;
+	char buf[32];
+	ssize_t len;
+
+	len = min(count, sizeof(buf) - 1);
+	if (copy_from_user(buf, user_buf, len))
+		return -EFAULT;
+
+	buf[len] = '\0';
+	if (kstrtoul(buf, 0, &diag))
+		return -EINVAL;
+
+	ah->diag = diag;
+	ath9k_hw_update_diag(ah);
+
+	return count;
+}
+
+static const struct file_operations fops_diag = {
+	.read = read_file_diag,
+	.write = write_file_diag,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
+
 int ath9k_init_debug(struct ath_hw *ah)
 {
 	struct ath_common *common = ath9k_hw_common(ah);
@@ -1432,6 +1624,16 @@ int ath9k_init_debug(struct ath_hw *ah)
 	ath9k_tx99_init_debug(sc);
 	ath9k_cmn_spectral_init_debug(&sc->spec_priv, sc->debug.debugfs_phy);
 
+	debugfs_create_file("eeprom", S_IRUSR, sc->debug.debugfs_phy, sc,
+			    &fops_eeprom);
+	debugfs_create_file("chanbw", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
+			    sc, &fops_chanbw);
+#ifdef CONFIG_MAC80211_LEDS
+	debugfs_create_file("gpio_led", S_IWUSR,
+			   sc->debug.debugfs_phy, sc, &fops_gpio_led);
+#endif
+	debugfs_create_file("diag", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
+			    sc, &fops_diag);
 	debugfs_create_devm_seqfile(sc->dev, "dma", sc->debug.debugfs_phy,
 				    read_file_dma);
 	debugfs_create_devm_seqfile(sc->dev, "interrupt", sc->debug.debugfs_phy,
diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c
index a8101c9..37a796b 100644
--- a/drivers/net/wireless/ath/ath9k/gpio.c
+++ b/drivers/net/wireless/ath/ath9k/gpio.c
@@ -15,13 +15,211 @@
  */
 
 #include "ath9k.h"
+#include <linux/ath9k_platform.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/gpio_keys.h>
+
+#ifdef CPTCFG_MAC80211_LEDS
+
+#ifdef CONFIG_GPIOLIB
+
+/***************/
+/*  GPIO Chip  */
+/***************/
+
+/* gpio_chip handler : set GPIO to input */
+static int ath9k_gpio_pin_cfg_input(struct gpio_chip *chip, unsigned offset)
+{
+	struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
+						  gchip);
+
+	ath9k_hw_gpio_request_in(gc->sc->sc_ah, offset, "ath9k-gpio");
+
+	return 0;
+}
+
+/* gpio_chip handler : set GPIO to output */
+static int ath9k_gpio_pin_cfg_output(struct gpio_chip *chip, unsigned offset,
+				     int value)
+{
+	struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
+						  gchip);
+
+	ath9k_hw_gpio_request_out(gc->sc->sc_ah, offset, "ath9k-gpio",
+				  AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
+	ath9k_hw_set_gpio(gc->sc->sc_ah, offset, value);
+
+	return 0;
+}
+
+/* gpio_chip handler : query GPIO direction (0=out, 1=in) */
+static int ath9k_gpio_pin_get_dir(struct gpio_chip *chip, unsigned offset)
+{
+	struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
+						  gchip);
+	struct ath_hw *ah = gc->sc->sc_ah;
+
+	return !((REG_READ(ah, AR_GPIO_OE_OUT) >> (offset * 2)) & 3);
+}
+
+/* gpio_chip handler : get GPIO pin value */
+static int ath9k_gpio_pin_get(struct gpio_chip *chip, unsigned offset)
+{
+	struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
+						  gchip);
+
+	return ath9k_hw_gpio_get(gc->sc->sc_ah, offset);
+}
+
+/* gpio_chip handler : set GPIO pin to value */
+static void ath9k_gpio_pin_set(struct gpio_chip *chip, unsigned offset,
+			       int value)
+{
+	struct ath9k_gpio_chip *gc = container_of(chip, struct ath9k_gpio_chip,
+						  gchip);
+
+	ath9k_hw_set_gpio(gc->sc->sc_ah, offset, value);
+}
+
+/* register GPIO chip */
+static void ath9k_register_gpio_chip(struct ath_softc *sc)
+{
+	struct ath9k_gpio_chip *gc;
+	struct ath_hw *ah = sc->sc_ah;
+
+	gc = kzalloc(sizeof(struct ath9k_gpio_chip), GFP_KERNEL);
+	if (!gc)
+		return;
+
+	gc->sc = sc;
+	snprintf(gc->label, sizeof(gc->label), "ath9k-%s",
+		 wiphy_name(sc->hw->wiphy));
+#ifdef CONFIG_OF
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,5,0)
+	gc->gchip.parent = sc->dev;
+#else
+	gc->gchip.dev = sc->dev;
+#endif
+#endif
+	gc->gchip.label = gc->label;
+	gc->gchip.base = -1;	/* determine base automatically */
+	gc->gchip.ngpio = ah->caps.num_gpio_pins;
+	gc->gchip.direction_input = ath9k_gpio_pin_cfg_input;
+	gc->gchip.direction_output = ath9k_gpio_pin_cfg_output;
+	gc->gchip.get_direction = ath9k_gpio_pin_get_dir;
+	gc->gchip.get = ath9k_gpio_pin_get;
+	gc->gchip.set = ath9k_gpio_pin_set;
+
+	if (gpiochip_add(&gc->gchip)) {
+		kfree(gc);
+		return;
+	}
+
+#ifdef CONFIG_OF
+	gc->gchip.owner = NULL;
+#endif
+	sc->gpiochip = gc;
+}
+
+/* remove GPIO chip */
+static void ath9k_unregister_gpio_chip(struct ath_softc *sc)
+{
+	struct ath9k_gpio_chip *gc = sc->gpiochip;
+
+	if (!gc)
+		return;
+
+	gpiochip_remove(&gc->gchip);
+	kfree(gc);
+	sc->gpiochip = NULL;
+}
+
+/******************/
+/*  GPIO Buttons  */
+/******************/
+
+/* add GPIO buttons */
+static void ath9k_init_buttons(struct ath_softc *sc)
+{
+	struct ath9k_platform_data *pdata = sc->dev->platform_data;
+	struct platform_device *pdev;
+	struct gpio_keys_platform_data gkpdata;
+	struct gpio_keys_button *bt;
+	int i;
+
+	if (!sc->gpiochip)
+		return;
+
+	if (!pdata || !pdata->btns || !pdata->num_btns)
+		return;
+
+	bt = devm_kmemdup(sc->dev, pdata->btns,
+			  pdata->num_btns * sizeof(struct gpio_keys_button),
+			  GFP_KERNEL);
+	if (!bt)
+		return;
+
+	for (i = 0; i < pdata->num_btns; i++) {
+		if (pdata->btns[i].gpio == sc->sc_ah->led_pin)
+				sc->sc_ah->led_pin = -1;
+
+		ath9k_hw_gpio_request_in(sc->sc_ah, pdata->btns[i].gpio,
+					 "ath9k-gpio");
+		bt[i].gpio = sc->gpiochip->gchip.base + pdata->btns[i].gpio;
+	}
+
+	memset(&gkpdata, 0, sizeof(struct gpio_keys_platform_data));
+	gkpdata.buttons = bt;
+	gkpdata.nbuttons = pdata->num_btns;
+	gkpdata.poll_interval = pdata->btn_poll_interval;
+
+	pdev = platform_device_register_data(sc->dev, "gpio-keys-polled",
+					     PLATFORM_DEVID_AUTO, &gkpdata,
+					     sizeof(gkpdata));
+	if (!IS_ERR_OR_NULL(pdev))
+		sc->btnpdev = pdev;
+	else {
+		sc->btnpdev = NULL;
+		devm_kfree(sc->dev, bt);
+	}
+}
+
+/* remove GPIO buttons */
+static void ath9k_deinit_buttons(struct ath_softc *sc)
+{
+	if (!sc->gpiochip || !sc->btnpdev)
+		return;
+
+	platform_device_unregister(sc->btnpdev);
+
+	sc->btnpdev = NULL;
+}
+
+#else /* CONFIG_GPIOLIB */
+
+static inline void ath9k_register_gpio_chip(struct ath_softc *sc)
+{
+}
+
+static inline void ath9k_unregister_gpio_chip(struct ath_softc *sc)
+{
+}
+
+static inline void ath9k_init_buttons(struct ath_softc *sc)
+{
+}
+
+static inline void ath9k_deinit_buttons(struct ath_softc *sc)
+{
+}
+
+#endif /* CONFIG_GPIOLIB */
 
 /********************************/
 /*	 LED functions		*/
 /********************************/
 
-#ifdef CPTCFG_MAC80211_LEDS
-
 static void ath_fill_led_pin(struct ath_softc *sc)
 {
 	struct ath_hw *ah = sc->sc_ah;
@@ -39,62 +237,171 @@ static void ath_fill_led_pin(struct ath_softc *sc)
 		else
 			ah->led_pin = ATH_LED_PIN_DEF;
 	}
+}
+
+static void ath_led_brightness(struct led_classdev *led_cdev,
+			       enum led_brightness brightness)
+{
+	struct ath_led *led = container_of(led_cdev, struct ath_led, cdev);
+	struct ath_softc *sc = led->sc;
+
+	ath9k_ps_wakeup(sc);
+	ath9k_hw_set_gpio(sc->sc_ah, led->gpio->gpio,
+			  (brightness != LED_OFF) ^ led->gpio->active_low);
+	ath9k_ps_restore(sc);
+}
+
+static int ath_add_led(struct ath_softc *sc, struct ath_led *led)
+{
+	const struct gpio_led *gpio = led->gpio;
+	int ret;
+
+	led->cdev.name = gpio->name;
+	led->cdev.default_trigger = gpio->default_trigger;
+	led->cdev.brightness_set = ath_led_brightness;
+
+	ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &led->cdev);
+	if (ret < 0)
+		return ret;
+
+	led->sc = sc;
+	list_add(&led->list, &sc->leds);
 
 	/* Configure gpio for output */
-	ath9k_hw_gpio_request_out(ah, ah->led_pin, "ath9k-led",
+	ath9k_hw_gpio_request_out(sc->sc_ah, gpio->gpio, gpio->name,
 				  AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
 
-	/* LED off, active low */
-	ath9k_hw_set_gpio(ah, ah->led_pin, ah->config.led_active_high ? 0 : 1);
+	/* Set default LED state */
+	if (gpio->default_state == LEDS_GPIO_DEFSTATE_ON)
+		ath9k_hw_set_gpio(sc->sc_ah, gpio->gpio, !gpio->active_low);
+	else
+		ath9k_hw_set_gpio(sc->sc_ah, gpio->gpio, gpio->active_low);
+
+#ifdef CONFIG_GPIOLIB
+	/* If there is GPIO chip configured, reserve LED pin */
+	if (sc->gpiochip)
+		gpio_request(sc->gpiochip->gchip.base + gpio->gpio, gpio->name);
+#endif
+
+	return 0;
 }
 
-static void ath_led_brightness(struct led_classdev *led_cdev,
-			       enum led_brightness brightness)
+int ath_create_gpio_led(struct ath_softc *sc, int gpio_num, const char *name,
+			const char *trigger, bool active_low)
 {
-	struct ath_softc *sc = container_of(led_cdev, struct ath_softc, led_cdev);
-	u32 val = (brightness == LED_OFF);
+	struct ath_led *led;
+	struct gpio_led *gpio;
+	char *_name;
+	int ret;
+
+	led = kzalloc(sizeof(*led) + sizeof(*gpio) + strlen(name) + 1,
+		      GFP_KERNEL);
+	if (!led)
+		return -ENOMEM;
 
-	if (sc->sc_ah->config.led_active_high)
-		val = !val;
+	led->gpio = gpio = (struct gpio_led *) (led + 1);
+	_name = (char *) (led->gpio + 1);
 
-	ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, val);
+	strcpy(_name, name);
+	gpio->name = _name;
+	gpio->gpio = gpio_num;
+	gpio->active_low = active_low;
+	gpio->default_trigger = trigger;
+
+	ret = ath_add_led(sc, led);
+	if (unlikely(ret < 0))
+		kfree(led);
+
+	return ret;
 }
 
-void ath_deinit_leds(struct ath_softc *sc)
+static int ath_create_platform_led(struct ath_softc *sc,
+				   const struct gpio_led *gpio)
 {
-	if (!sc->led_registered)
-		return;
+	struct ath_led *led;
+	int ret;
 
-	ath_led_brightness(&sc->led_cdev, LED_OFF);
-	led_classdev_unregister(&sc->led_cdev);
+	led = kzalloc(sizeof(*led), GFP_KERNEL);
+	if (!led)
+		return -ENOMEM;
 
-	ath9k_hw_gpio_free(sc->sc_ah, sc->sc_ah->led_pin);
+	led->gpio = gpio;
+	ret = ath_add_led(sc, led);
+	if (ret < 0)
+		kfree(led);
+
+	return ret;
+}
+
+void ath_deinit_leds(struct ath_softc *sc)
+{
+	struct ath_led *led;
+
+	ath9k_deinit_buttons(sc);
+	while (!list_empty(&sc->leds)) {
+		led = list_first_entry(&sc->leds, struct ath_led, list);
+#ifdef CONFIG_GPIOLIB
+		/* If there is GPIO chip configured, free LED pin */
+		if (sc->gpiochip)
+			gpio_free(sc->gpiochip->gchip.base + led->gpio->gpio);
+#endif
+		list_del(&led->list);
+		ath_led_brightness(&led->cdev, LED_OFF);
+		led_classdev_unregister(&led->cdev);
+		ath9k_hw_gpio_free(sc->sc_ah, led->gpio->gpio);
+		kfree(led);
+	}
+	ath9k_unregister_gpio_chip(sc);
 }
 
 void ath_init_leds(struct ath_softc *sc)
 {
-	int ret;
+	struct ath9k_platform_data *pdata = sc->dev->platform_data;
+	struct device_node *np = sc->dev->of_node;
+	char led_name[32];
+	const char *trigger;
+	int i;
+
+	INIT_LIST_HEAD(&sc->leds);
 
 	if (AR_SREV_9100(sc->sc_ah))
 		return;
 
+	if (!np)
+		ath9k_register_gpio_chip(sc);
+
+	/* setup gpio controller only if requested and skip the led_pin setup */
+	if (of_property_read_bool(np, "gpio-controller")) {
+		ath9k_register_gpio_chip(sc);
+		return;
+	}
+
 	ath_fill_led_pin(sc);
+	ath9k_init_buttons(sc);
 
-	if (!ath9k_led_blink)
-		sc->led_cdev.default_trigger =
-			ieee80211_get_radio_led_name(sc->hw);
+	if (pdata && pdata->leds && pdata->num_leds)
+		for (i = 0; i < pdata->num_leds; i++) {
+			if (pdata->leds[i].gpio == sc->sc_ah->led_pin)
+				sc->sc_ah->led_pin = -1;
 
-	snprintf(sc->led_name, sizeof(sc->led_name),
-		"ath9k-%s", wiphy_name(sc->hw->wiphy));
-	sc->led_cdev.name = sc->led_name;
-	sc->led_cdev.brightness_set = ath_led_brightness;
+			ath_create_platform_led(sc, &pdata->leds[i]);
+		}
 
-	ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &sc->led_cdev);
-	if (ret < 0)
+	if (sc->sc_ah->led_pin < 0)
 		return;
 
-	sc->led_registered = true;
+	snprintf(led_name, sizeof(led_name), "ath9k-%s",
+		 wiphy_name(sc->hw->wiphy));
+
+	if (ath9k_led_blink)
+		trigger = sc->led_default_trigger;
+	else
+		trigger = ieee80211_get_radio_led_name(sc->hw);
+
+	ath_create_gpio_led(sc, sc->sc_ah->led_pin, led_name, trigger,
+			   !sc->sc_ah->config.led_active_high);
 }
+
 #endif
 
 /*******************/
diff --git a/drivers/net/wireless/ath/ath9k/hsr.c b/drivers/net/wireless/ath/ath9k/hsr.c
new file mode 100644
index 0000000..7d12d91
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/hsr.c
@@ -0,0 +1,247 @@
+/*
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015 Kirill Berezin
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/bitops.h>
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <asm/unaligned.h>
+
+#include "hw.h"
+#include "ath9k.h"
+
+#define HSR_GPIO_CSN 8
+#define HSR_GPIO_CLK 6
+#define HSR_GPIO_DOUT 7
+#define HSR_GPIO_DIN 5
+
+/* delays are in useconds */
+#define HSR_DELAY_HALF_TICK 100
+#define HSR_DELAY_PRE_WRITE 75
+#define HSR_DELAY_FINAL 20000
+#define HSR_DELAY_TRAILING 200
+
+void ath9k_hsr_init(struct ath_hw *ah)
+{
+	ath9k_hw_gpio_request_in(ah, HSR_GPIO_DIN, NULL);
+	ath9k_hw_gpio_request_out(ah, HSR_GPIO_CSN, NULL,
+				  AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
+	ath9k_hw_gpio_request_out(ah, HSR_GPIO_CLK, NULL,
+				  AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
+	ath9k_hw_gpio_request_out(ah, HSR_GPIO_DOUT, NULL,
+				  AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
+
+	ath9k_hw_set_gpio(ah, HSR_GPIO_CSN, 1);
+	ath9k_hw_set_gpio(ah, HSR_GPIO_CLK, 0);
+	ath9k_hw_set_gpio(ah, HSR_GPIO_DOUT, 0);
+
+	udelay(HSR_DELAY_TRAILING);
+}
+
+static u32 ath9k_hsr_write_byte(struct ath_hw *ah, int delay, u32 value)
+{
+	struct ath_common *common = ath9k_hw_common(ah);
+	int i;
+	u32 rval = 0;
+
+	udelay(delay);
+
+	ath9k_hw_set_gpio(ah, HSR_GPIO_CLK, 0);
+	udelay(HSR_DELAY_HALF_TICK);
+
+	ath9k_hw_set_gpio(ah, HSR_GPIO_CSN, 0);
+	udelay(HSR_DELAY_HALF_TICK);
+
+	for (i = 0; i < 8; ++i) {
+		rval = rval << 1;
+
+		/* pattern is left to right, that is 7-th bit runs first */
+		ath9k_hw_set_gpio(ah, HSR_GPIO_DOUT, (value >> (7 - i)) & 0x1);
+		udelay(HSR_DELAY_HALF_TICK);
+
+		ath9k_hw_set_gpio(ah, HSR_GPIO_CLK, 1);
+		udelay(HSR_DELAY_HALF_TICK);
+
+		rval |= ath9k_hw_gpio_get(ah, HSR_GPIO_DIN);
+
+		ath9k_hw_set_gpio(ah, HSR_GPIO_CLK, 0);
+		udelay(HSR_DELAY_HALF_TICK);
+	}
+
+	ath9k_hw_set_gpio(ah, HSR_GPIO_CSN, 1);
+	udelay(HSR_DELAY_HALF_TICK);
+
+	ath_dbg(common, CONFIG, "ath9k_hsr_write_byte: write byte %d return value is %d %c\n",
+		value, rval, rval > 32 ? rval : '-');
+
+	return rval & 0xff;
+}
+
+static int ath9k_hsr_write_a_chain(struct ath_hw *ah, char *chain, int items)
+{
+	int status = 0;
+	int i = 0;
+	int err;
+
+	/* a preamble */
+	ath9k_hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0);
+	status = ath9k_hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0);
+
+	/* clear HSR's reply buffer */
+	if (status) {
+		int loop = 0;
+
+		for (loop = 0; (loop < 42) && status; ++loop)
+			status = ath9k_hsr_write_byte(ah, HSR_DELAY_PRE_WRITE,
+						      0);
+
+		if (loop >= 42) {
+			ATH_DBG_WARN(1,
+				     "ath9k_hsr_write_a_chain: can't clear an output buffer after a 42 cycles.\n");
+			return -1;
+		}
+	}
+
+	for (i = 0; (i < items) && (chain[i] != 0); ++i)
+		ath9k_hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, (u32)chain[i]);
+
+	ath9k_hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0);
+	mdelay(HSR_DELAY_FINAL / 1000);
+
+	/* reply */
+	memset(chain, 0, items);
+
+	ath9k_hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0);
+	udelay(HSR_DELAY_TRAILING);
+
+	for (i = 0; i < (items - 1); ++i) {
+		u32 ret;
+
+		ret = ath9k_hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0);
+		if (ret != 0)
+			chain[i] = (char)ret;
+		else
+			break;
+
+		udelay(HSR_DELAY_TRAILING);
+	}
+
+	if (i <= 1)
+		return 0;
+
+	err = kstrtoint(chain + 1, 10, &i);
+	if (err)
+		return err;
+
+	return i;
+}
+
+int ath9k_hsr_disable(struct ath_hw *ah)
+{
+	char cmd[10] = {'b', '4', '0', 0, 0, 0, 0, 0, 0, 0};
+	int ret;
+
+	ret = ath9k_hsr_write_a_chain(ah, cmd, sizeof(cmd));
+	if ((ret > 0) && (*cmd == 'B'))
+		return 0;
+
+	return -1;
+}
+
+int ath9k_hsr_enable(struct ath_hw *ah, int bw, int fq)
+{
+	char cmd[10];
+	int ret;
+
+	/* Bandwidth argument is 0 sometimes. Assume default 802.11bgn
+	 * 20MHz on invalid values
+	 */
+	if ((bw != 5) && (bw != 10) && (bw != 20) && (bw != 40))
+		bw = 20;
+
+	memset(cmd, 0, sizeof(cmd));
+	*cmd = 'b';
+	snprintf(cmd + 1, 3, "%02d", bw);
+
+	ret = ath9k_hsr_write_a_chain(ah, cmd, sizeof(cmd));
+	if ((*cmd != 'B') || (ret != bw)) {
+		ATH_DBG_WARN(1,
+			     "ath9k_hsr_enable: failed changing bandwidth -> set (%d,%d) reply (%d, %d)\n",
+			     'b', bw, *cmd, ret);
+		return -1;
+	}
+
+	memset(cmd, 0, sizeof(cmd));
+	*cmd = 'x';
+	ret = ath9k_hsr_write_a_chain(ah, cmd, sizeof(cmd));
+	if (*cmd != 'X') {
+		ATH_DBG_WARN(1,
+			     "ath9k_hsr_enable: failed 'x' command -> reply (%d, %d)\n",
+			     *cmd, ret);
+		return -1;
+	}
+
+	memset(cmd, 0, sizeof(cmd));
+	*cmd = 'm';
+	ret = ath9k_hsr_write_a_chain(ah, cmd, sizeof(cmd));
+	if (*cmd != 'M') {
+		ATH_DBG_WARN(1,
+			     "ath9k_hsr_enable: failed 'm' command -> reply (%d, %d)\n",
+			     *cmd, ret);
+		return  -1;
+	}
+
+	memset(cmd, 0, sizeof(cmd));
+	*cmd = 'f';
+	snprintf(cmd + 1, 6, "%05d", fq);
+	ret = ath9k_hsr_write_a_chain(ah, cmd, sizeof(cmd));
+	if ((*cmd != 'F') && (ret != fq)) {
+		ATH_DBG_WARN(1,
+			     "ath9k_hsr_enable: failed set frequency -> reply (%d, %d)\n",
+			     *cmd, ret);
+		return -1;
+	}
+
+	return 0;
+}
+
+int ath9k_hsr_status(struct ath_hw *ah)
+{
+	char cmd[10] = {'s', 0, 0, 0, 0, 0, 0, 0, 0, 0};
+	int ret;
+
+	ret = ath9k_hsr_write_a_chain(ah, cmd, sizeof(cmd));
+	if (*cmd != 'S') {
+		ATH_DBG_WARN(1, "ath9k_hsr_status: returned %d,%d\n", *cmd,
+			     ret);
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/drivers/net/wireless/ath/ath9k/hsr.h b/drivers/net/wireless/ath/ath9k/hsr.h
new file mode 100644
index 0000000..78af444
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/hsr.h
@@ -0,0 +1,48 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015 Kirill Berezin
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef HSR_H
+#define HSR_H
+
+#ifdef CPTCFG_ATH9K_UBNTHSR
+
+void ath9k_hsr_init(struct ath_hw *ah);
+int ath9k_hsr_disable(struct ath_hw *ah);
+int ath9k_hsr_enable(struct ath_hw *ah, int bw, int fq);
+int ath9k_hsr_status(struct ath_hw *ah);
+
+#else
+static inline void ath9k_hsr_init(struct ath_hw *ah) {}
+
+static inline int ath9k_hsr_enable(struct ath_hw *ah, int bw, int fq)
+{
+	return 0;
+}
+
+static inline int ath9k_hsr_disable(struct ath_hw *ah) { return 0; }
+static inline int ath9k_hsr_status(struct ath_hw *ah) { return 0; }
+
+#endif
+
+#endif /* HSR_H */
diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h
index 174d716..605abe1 100644
--- a/drivers/net/wireless/ath/ath9k/hw-ops.h
+++ b/drivers/net/wireless/ath/ath9k/hw-ops.h
@@ -100,6 +100,12 @@ static inline void ath9k_hw_tx99_set_txpower(struct ath_hw *ah, u8 power)
 		ath9k_hw_ops(ah)->tx99_set_txpower(ah, power);
 }
 
+static inline void ath9k_hw_get_adc_entropy(struct ath_hw *ah,
+		u8 *buf, size_t len)
+{
+	ath9k_hw_ops(ah)->get_adc_entropy(ah, buf, len);
+}
+
 #ifdef CPTCFG_ATH9K_BTCOEX_SUPPORT
 
 static inline void ath9k_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable)
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index fafe1f0..1dfb3e9 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -247,6 +247,19 @@ void ath9k_hw_get_channel_centers(struct ath_hw *ah,
 		centers->synth_center + (extoff * HT40_CHANNEL_CENTER_SHIFT);
 }
 
+static inline void ath9k_hw_disable_pll_lock_detect(struct ath_hw *ah)
+{
+	/* On AR9330 and AR9340 devices, some PHY registers must be
+	 * tuned to gain better stability/performance. These registers
+	 * might be changed while doing wlan reset so the registers must
+	 * be reprogrammed after each reset.
+	 */
+	REG_CLR_BIT(ah, AR_PHY_USB_CTRL1, BIT(20));
+	REG_RMW(ah, AR_PHY_USB_CTRL2,
+		(1 << 21) | (0xf << 22),
+		(1 << 21) | (0x3 << 22));
+}
+
 /******************/
 /* Chip Revisions */
 /******************/
@@ -402,13 +415,8 @@ static void ath9k_hw_init_config(struct ath_hw *ah)
 
 	ah->config.rx_intr_mitigation = true;
 
-	if (AR_SREV_9300_20_OR_LATER(ah)) {
-		ah->config.rimt_last = 500;
-		ah->config.rimt_first = 2000;
-	} else {
-		ah->config.rimt_last = 250;
-		ah->config.rimt_first = 700;
-	}
+	ah->config.rimt_last = 250;
+	ah->config.rimt_first = 500;
 
 	if (AR_SREV_9462(ah) || AR_SREV_9565(ah))
 		ah->config.pll_pwrsave = 7;
@@ -667,6 +675,7 @@ int ath9k_hw_init(struct ath_hw *ah)
 
 	/* These are all the AR5008/AR9001/AR9002/AR9003 hardware family of chipsets */
 	switch (ah->hw_version.devid) {
+	case AR9300_DEVID_INVALID:
 	case AR5416_DEVID_PCI:
 	case AR5416_DEVID_PCIE:
 	case AR5416_AR9100_DEVID:
@@ -1311,39 +1320,56 @@ void ath9k_hw_get_delta_slope_vals(struct ath_hw *ah, u32 coef_scaled,
 	*coef_exponent = coef_exp - 16;
 }
 
-/* AR9330 WAR:
- * call external reset function to reset WMAC if:
- * - doing a cold reset
- * - we have pending frames in the TX queues.
- */
-static bool ath9k_hw_ar9330_reset_war(struct ath_hw *ah, int type)
+static bool ath9k_hw_need_external_reset(struct ath_hw *ah, int type)
 {
-	int i, npend = 0;
-
-	for (i = 0; i < AR_NUM_QCU; i++) {
-		npend = ath9k_hw_numtxpending(ah, i);
-		if (npend)
-			break;
-	}
+	int i;
 
-	if (ah->external_reset &&
-	    (npend || type == ATH9K_RESET_COLD)) {
-		int reset_err = 0;
+	if (type == ATH9K_RESET_COLD)
+		return true;
 
-		ath_dbg(ath9k_hw_common(ah), RESET,
-			"reset MAC via external reset\n");
+	if (AR_SREV_9550(ah))
+		return true;
 
-		reset_err = ah->external_reset();
-		if (reset_err) {
-			ath_err(ath9k_hw_common(ah),
-				"External reset failed, err=%d\n",
-				reset_err);
-			return false;
+	/* AR9330 WAR:
+	 * call external reset function to reset WMAC if:
+	 * - doing a cold reset
+	 * - we have pending frames in the TX queues.
+	 */
+	if (AR_SREV_9330(ah)) {
+		for (i = 0; i < AR_NUM_QCU; i++) {
+			if (ath9k_hw_numtxpending(ah, i))
+				return true;
 		}
+ 	}
+
+	return false;
+}
 
-		REG_WRITE(ah, AR_RTC_RESET(ah), 1);
+static bool ath9k_hw_external_reset(struct ath_hw *ah, int type)
+{
+	int err;
+
+	if (!ah->external_reset || !ath9k_hw_need_external_reset(ah, type))
+		return true;
+
+	ath_dbg(ath9k_hw_common(ah), RESET,
+		"reset MAC via external reset\n");
+
+	err = ah->external_reset();
+	if (err) {
+		ath_err(ath9k_hw_common(ah),
+			"External reset failed, err=%d\n", err);
+		return false;
 	}
 
+	if (AR_SREV_9550(ah)) {
+		REG_WRITE(ah, AR_RTC_RESET, 0);
+		udelay(10);
+	}
+
+	REG_WRITE(ah, AR_RTC_RESET, 1);
+	udelay(10);
+
 	return true;
 }
 
@@ -1396,24 +1422,24 @@ static bool ath9k_hw_set_reset(struct ath_hw *ah, int type)
 			rst_flags |= AR_RTC_RC_MAC_COLD;
 	}
 
-	if (AR_SREV_9330(ah)) {
-		if (!ath9k_hw_ar9330_reset_war(ah, type))
-			return false;
-	}
-
 	if (ath9k_hw_mci_is_enabled(ah))
 		ar9003_mci_check_gpm_offset(ah);
 
 	/* DMA HALT added to resolve ar9300 and ar9580 bus error during
-	 * RTC_RC reg read
+	 * RTC_RC reg read. Also needed for AR9550 external reset
 	 */
-	if (AR_SREV_9300(ah) || AR_SREV_9580(ah)) {
+	if (AR_SREV_9300(ah) || AR_SREV_9580(ah) || AR_SREV_9550(ah)) {
 		REG_SET_BIT(ah, AR_CFG, AR_CFG_HALT_REQ);
 		ath9k_hw_wait(ah, AR_CFG, AR_CFG_HALT_ACK, AR_CFG_HALT_ACK,
 			      20 * AH_WAIT_TIMEOUT);
-		REG_CLR_BIT(ah, AR_CFG, AR_CFG_HALT_REQ);
 	}
 
+	if (!AR_SREV_9100(ah))
+		ath9k_hw_external_reset(ah, type);
+
+	if (AR_SREV_9300(ah) || AR_SREV_9580(ah))
+		REG_CLR_BIT(ah, AR_CFG, AR_CFG_HALT_REQ);
+
 	REG_WRITE(ah, AR_RTC_RC(ah), rst_flags);
 
 	REGWRITE_BUFFER_FLUSH(ah);
@@ -1434,8 +1460,15 @@ static bool ath9k_hw_set_reset(struct ath_hw *ah, int type)
 	if (!AR_SREV_9100(ah))
 		REG_WRITE(ah, AR_RC, 0);
 
-	if (AR_SREV_9100(ah))
+	if (AR_SREV_9100(ah)) {
+		/* Reset the AHB-WMAC interface */
+		if (ah->external_reset)
+			ah->external_reset();
 		udelay(50);
+	}
+
+	if (AR_SREV_9330(ah) || AR_SREV_9340(ah))
+		ath9k_hw_disable_pll_lock_detect(ah);
 
 	return true;
 }
@@ -1536,6 +1569,9 @@ static bool ath9k_hw_chip_reset(struct ath_hw *ah,
 		ar9003_hw_internal_regulator_apply(ah);
 	ath9k_hw_init_pll(ah, chan);
 
+	if (AR_SREV_9330(ah) || AR_SREV_9340(ah))
+		ath9k_hw_disable_pll_lock_detect(ah);
+
 	return true;
 }
 
@@ -1842,8 +1878,14 @@ static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan)
 	if (AR_SREV_9271(ah))
 		ar9002_hw_load_ani_reg(ah, chan);
 
+	if (AR_SREV_9330(ah) || AR_SREV_9340(ah))
+		ath9k_hw_disable_pll_lock_detect(ah);
+
 	return 0;
 fail:
+	if (AR_SREV_9330(ah) || AR_SREV_9340(ah))
+		ath9k_hw_disable_pll_lock_detect(ah);
+
 	return -EINVAL;
 }
 
@@ -1864,6 +1906,20 @@ u32 ath9k_hw_get_tsf_offset(struct timespec64 *last, struct timespec64 *cur)
 }
 EXPORT_SYMBOL(ath9k_hw_get_tsf_offset);
 
+void ath9k_hw_update_diag(struct ath_hw *ah)
+{
+	if (test_bit(ATH_DIAG_DISABLE_RX, &ah->diag))
+		REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS);
+	else
+		REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS);
+
+	if (test_bit(ATH_DIAG_DISABLE_TX, &ah->diag))
+		REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_LOOP_BACK);
+	else
+		REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_LOOP_BACK);
+}
+EXPORT_SYMBOL(ath9k_hw_update_diag);
+
 int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 		   struct ath9k_hw_cal_data *caldata, bool fastcc)
 {
@@ -2072,6 +2128,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 		ar9003_hw_disable_phy_restart(ah);
 
 	ath9k_hw_apply_gpio_override(ah);
+	ath9k_hw_update_diag(ah);
 
 	if (AR_SREV_9565(ah) && common->bt_ant_diversity)
 		REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV, AR_BTCOEX_WL_LNADIV_FORCE_ON);
@@ -2082,6 +2139,9 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 		ath9k_hw_set_radar_params(ah);
 	}
 
+	if (AR_SREV_9330(ah) || AR_SREV_9340(ah))
+		ath9k_hw_disable_pll_lock_detect(ah);
+
 	return 0;
 }
 EXPORT_SYMBOL(ath9k_hw_reset);
@@ -2956,7 +3016,8 @@ void ath9k_hw_apply_txpower(struct ath_hw *ah, struct ath9k_channel *chan,
 {
 	struct ath_regulatory *reg = ath9k_hw_regulatory(ah);
 	struct ieee80211_channel *channel;
-	int chan_pwr, new_pwr;
+	int chan_pwr, new_pwr, max_gain;
+	int ant_gain, ant_reduction = 0;
 	u16 ctl = NO_CTL;
 
 	if (!chan)
@@ -2968,9 +3029,18 @@ void ath9k_hw_apply_txpower(struct ath_hw *ah, struct ath9k_channel *chan,
 	channel = chan->chan;
 	chan_pwr = min_t(int, channel->max_power * 2, MAX_COMBINED_POWER);
 	new_pwr = min_t(int, chan_pwr, reg->power_limit);
+	max_gain = chan_pwr - new_pwr + channel->max_antenna_gain * 2;
+
+	ant_gain = get_antenna_gain(ah, chan);
+	if (ant_gain > max_gain)
+		ant_reduction = ant_gain - max_gain;
+
+	/* FCC allows maximum antenna gain of 6 dBi */
+	if (reg->region == NL80211_DFS_FCC)
+		ant_reduction = max_t(int, ant_reduction - 12, 0);
 
 	ah->eep_ops->set_txpower(ah, chan, ctl,
-				 get_antenna_gain(ah, chan), new_pwr, test);
+				 ant_reduction, new_pwr, test);
 }
 
 void ath9k_hw_set_txpowerlimit(struct ath_hw *ah, u32 limit, bool test)
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index 6243626..8d756dc 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -36,6 +36,7 @@
 
 #define ATHEROS_VENDOR_ID	0x168c
 
+#define AR9300_DEVID_INVALID	0xabcd
 #define AR5416_DEVID_PCI	0x0023
 #define AR5416_DEVID_PCIE	0x0024
 #define AR9160_DEVID_PCI	0x0027
@@ -521,6 +522,12 @@ enum {
 	ATH9K_RESET_COLD,
 };
 
+enum {
+	ATH_DIAG_DISABLE_RX,
+	ATH_DIAG_DISABLE_TX,
+	ATH_DIAG_TRIGGER_ERROR,
+};
+
 struct ath9k_hw_version {
 	u32 magic;
 	u16 devid;
@@ -716,6 +723,7 @@ struct ath_spec_scan {
  * @config_pci_powersave:
  * @calibrate: periodic calibration for NF, ANI, IQ, ADC gain, ADC-DC
  *
+ * @get_adc_entropy: get entropy from the raw ADC I/Q output
  * @spectral_scan_config: set parameters for spectral scan and enable/disable it
  * @spectral_scan_trigger: trigger a spectral scan run
  * @spectral_scan_wait: wait for a spectral scan run to finish
@@ -738,6 +746,7 @@ struct ath_hw_ops {
 			struct ath_hw_antcomb_conf *antconf);
 	void (*antdiv_comb_conf_set)(struct ath_hw *ah,
 			struct ath_hw_antcomb_conf *antconf);
+	void (*get_adc_entropy)(struct ath_hw *ah, u8 *buf, size_t len);
 	void (*spectral_scan_config)(struct ath_hw *ah,
 				     struct ath_spec_scan *param);
 	void (*spectral_scan_trigger)(struct ath_hw *ah);
@@ -809,6 +818,8 @@ struct ath_hw {
 	u32 ah_flags;
 	s16 nf_override;
 
+	unsigned long diag;
+
 	bool reset_power_on;
 	bool htc_reset_init;
 
@@ -1078,6 +1089,7 @@ void ath9k_hw_check_nav(struct ath_hw *ah);
 bool ath9k_hw_check_alive(struct ath_hw *ah);
 
 bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode);
+void ath9k_hw_update_diag(struct ath_hw *ah);
 
 /* Generic hw timer primitives */
 struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah,
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index f67f1f1..165d920 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -48,7 +48,7 @@ int ath9k_modparam_nohwcrypt;
 module_param_named(nohwcrypt, ath9k_modparam_nohwcrypt, int, 0444);
 MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption");
 
-int ath9k_led_blink;
+int ath9k_led_blink = 1;
 module_param_named(blink, ath9k_led_blink, int, 0444);
 MODULE_PARM_DESC(blink, "Enable LED blink on activity");
 
@@ -696,6 +696,12 @@ static int ath9k_of_init(struct ath_softc *sc)
 	return 0;
 }
 
+static void ath9k_of_gpio_mask(struct ath_softc *sc)
+{
+	of_property_read_u32(sc->dev->of_node, "qca,gpio-mask",
+			     &sc->sc_ah->caps.gpio_mask);
+}
+
 static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
 			    const struct ath_bus_ops *bus_ops)
 {
@@ -803,6 +809,9 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
 	if (ret)
 		goto err_hw;
 
+	/* GPIO mask quirk */
+	ath9k_of_gpio_mask(sc);
+
 	ret = ath9k_init_queues(sc);
 	if (ret)
 		goto err_queues;
@@ -870,7 +879,8 @@ static void ath9k_init_txpower_limits(struct ath_softc *sc)
 	if (ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
 		ath9k_init_band_txpower(sc, NL80211_BAND_5GHZ);
 
-	ah->curchan = curchan;
+	if (curchan)
+		ah->curchan = curchan;
 }
 
 static const struct ieee80211_iface_limit if_limits[] = {
@@ -882,6 +892,7 @@ static const struct ieee80211_iface_limit if_limits[] = {
 				 BIT(NL80211_IFTYPE_AP) },
 	{ .max = 1,	.types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
 				 BIT(NL80211_IFTYPE_P2P_GO) },
+	{ .max = 1,	.types = BIT(NL80211_IFTYPE_ADHOC) },
 };
 
 #ifdef CPTCFG_ATH9K_CHANNEL_CONTEXT
@@ -962,6 +973,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
 	ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
 	ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
 	ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS);
+	ieee80211_hw_set(hw, MFP_CAPABLE);
 
 	if (ath9k_ps_enable)
 		ieee80211_hw_set(hw, SUPPORTS_PS);
@@ -974,9 +986,6 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
 				IEEE80211_RADIOTAP_MCS_HAVE_STBC;
 	}
 
-	if (AR_SREV_9160_10_OR_LATER(sc->sc_ah) || ath9k_modparam_nohwcrypt)
-		ieee80211_hw_set(hw, MFP_CAPABLE);
-
 	hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR |
 			       NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE |
 			       NL80211_FEATURE_P2P_GO_CTWIN;
@@ -1049,6 +1058,18 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
 	wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0);
 }
 
+static void ath_get_initial_entropy(struct ath_softc *sc)
+{
+	struct ath_hw *ah = sc->sc_ah;
+	char buf[256];
+
+	/* reuse last channel initialized by the tx power test */
+	ath9k_hw_reset(ah, ah->curchan, NULL, false);
+
+	ath9k_hw_get_adc_entropy(ah, buf, sizeof(buf));
+	add_device_randomness(buf, sizeof(buf));
+}
+
 int ath9k_init_device(u16 devid, struct ath_softc *sc,
 		    const struct ath_bus_ops *bus_ops)
 {
@@ -1089,13 +1110,15 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc,
 
 #ifdef CPTCFG_MAC80211_LEDS
 	/* must be initialized before ieee80211_register_hw */
-	sc->led_cdev.default_trigger = ieee80211_create_tpt_led_trigger(sc->hw,
+	sc->led_default_trigger = ieee80211_create_tpt_led_trigger(sc->hw,
 		IEEE80211_TPT_LEDTRIG_FL_RADIO, ath9k_tpt_blink,
 		ARRAY_SIZE(ath9k_tpt_blink));
 #endif
 
 	wiphy_read_of_freq_limits(hw->wiphy);
 
+	ath_get_initial_entropy(sc);
+
 	/* Register with mac80211 */
 	error = ieee80211_register_hw(hw);
 	if (error)
@@ -1179,25 +1202,25 @@ static int __init ath9k_init(void)
 {
 	int error;
 
-	error = ath_pci_init();
+	error = ath_ahb_init();
 	if (error < 0) {
-		pr_err("No PCI devices found, driver not installed\n");
 		error = -ENODEV;
 		goto err_out;
 	}
 
-	error = ath_ahb_init();
+	error = ath_pci_init();
 	if (error < 0) {
+		pr_err("No PCI devices found, driver not installed\n");
 		error = -ENODEV;
-		goto err_pci_exit;
+		goto err_ahb_exit;
 	}
 
 	dmi_check_system(ath9k_quirks);
 
 	return 0;
 
- err_pci_exit:
-	ath_pci_exit();
+ err_ahb_exit:
+	ath_ahb_exit();
  err_out:
 	return error;
 }
diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c
index b070403..d8d14a5 100644
--- a/drivers/net/wireless/ath/ath9k/mac.c
+++ b/drivers/net/wireless/ath/ath9k/mac.c
@@ -678,13 +678,18 @@ void ath9k_hw_startpcureceive(struct ath_hw *ah, bool is_scanning)
 
 	ath9k_ani_reset(ah, is_scanning);
 
-	REG_CLR_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT));
+	REG_CLR_BIT(ah, AR_DIAG_SW,
+		    AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT | AR_DIAG_FORCE_RX_CLEAR);
 }
 EXPORT_SYMBOL(ath9k_hw_startpcureceive);
 
 void ath9k_hw_abortpcurecv(struct ath_hw *ah)
 {
-	REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_ABORT | AR_DIAG_RX_DIS);
+	u32 reg = AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT;
+
+	if (!IS_ENABLED(CPTCFG_ATH9K_TX99))
+		reg |= AR_DIAG_FORCE_RX_CLEAR;
+	REG_SET_BIT(ah, AR_DIAG_SW, reg);
 
 	ath9k_hw_disable_mib_counters(ah);
 }
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index ed87a37..0647f33 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -18,6 +18,7 @@
 #include <linux/delay.h>
 #include "ath9k.h"
 #include "btcoex.h"
+#include "hsr.h"
 
 static void ath9k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 			u32 queues, bool drop);
@@ -538,6 +539,11 @@ irqreturn_t ath_isr(int irq, void *dev)
 		return IRQ_HANDLED;
 	}
 
+	if (test_bit(ATH_DIAG_TRIGGER_ERROR, &ah->diag)) {
+		status |= ATH9K_INT_FATAL;
+		clear_bit(ATH_DIAG_TRIGGER_ERROR, &ah->diag);
+	}
+
 	/*
 	 * If there are no status bits set, then this interrupt was not
 	 * for me (should have been caught above).
@@ -654,6 +660,7 @@ void ath_reset_work(struct work_struct *work)
 static int ath9k_start(struct ieee80211_hw *hw)
 {
 	struct ath_softc *sc = hw->priv;
+	struct device_node *np = sc->dev->of_node;
 	struct ath_hw *ah = sc->sc_ah;
 	struct ath_common *common = ath9k_hw_common(ah);
 	struct ieee80211_channel *curchan = sc->cur_chan->chandef.chan;
@@ -732,6 +739,11 @@ static int ath9k_start(struct ieee80211_hw *hw)
 					  AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
 	}
 
+	if (of_property_read_bool(np, "ubnt,hsr")) {
+		ath9k_hsr_init(ah);
+		ath9k_hsr_disable(ah);
+	}
+
 	/*
 	 * Reset key cache to sane defaults (all entries cleared) instead of
 	 * semi-random values after suspend/resume.
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index 1adbac8..339aec6 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -774,6 +774,7 @@ static const struct pci_device_id ath_pci_id_table[] = {
 	  .driver_data = ATH9K_PCI_BT_ANT_DIV },
 #endif
 
+	{ PCI_VDEVICE(ATHEROS, 0xabcd) }, /* PCI-E  internal chip default ID */
 	{ 0 }
 };
 
diff --git a/drivers/net/wireless/ath/ath9k/phy.h b/drivers/net/wireless/ath/ath9k/phy.h
index 4a1b992..af667a3 100644
--- a/drivers/net/wireless/ath/ath9k/phy.h
+++ b/drivers/net/wireless/ath/ath9k/phy.h
@@ -48,6 +48,9 @@
 #define AR_PHY_PLL_CONTROL 0x16180
 #define AR_PHY_PLL_MODE 0x16184
 
+#define AR_PHY_USB_CTRL1	0x16c84
+#define AR_PHY_USB_CTRL2	0x16c88
+
 enum ath9k_ant_div_comb_lna_conf {
 	ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2,
 	ATH_ANT_DIV_COMB_LNA2,
diff --git a/include/linux/ath9k_platform.h b/include/linux/ath9k_platform.h
index 76860a4..e210108 100644
--- a/include/linux/ath9k_platform.h
+++ b/include/linux/ath9k_platform.h
@@ -46,6 +46,13 @@ struct ath9k_platform_data {
 	int (*external_reset)(void);
 
 	bool use_eeprom;
+
+	int num_leds;
+	const struct gpio_led *leds;
+
+	unsigned num_btns;
+	const struct gpio_keys_button *btns;
+	unsigned btn_poll_interval;
 };
 
 #endif /* _LINUX_ATH9K_PLATFORM_H */
diff --git a/local-symbols b/local-symbols
index c750661..33e4225 100644
--- a/local-symbols
+++ b/local-symbols
@@ -131,6 +131,7 @@ ATH9K_WOW=
 ATH9K_RFKILL=
 ATH9K_CHANNEL_CONTEXT=
 ATH9K_PCOEM=
+ATH9K_UBNTHSR=
 ATH9K_PCI_NO_EEPROM=
 ATH9K_HTC=
 ATH9K_HTC_DEBUGFS=
-- 
2.39.2

