| From 58736b0bd1ae576889c23c0639aa36f29ab6a74f Mon Sep 17 00:00:00 2001 |
| From: Shayne Chen <shayne.chen@mediatek.com> |
| Date: Tue, 12 Mar 2024 11:26:51 +0800 |
| Subject: [PATCH 04/61] sync backports patches/ath9k |
| |
| --- |
| drivers/net/wireless/ath/ath.h | 2 + |
| 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-debug.c | 107 +++++ |
| drivers/net/wireless/ath/ath9k/common-debug.h | 4 + |
| drivers/net/wireless/ath/ath9k/common.c | 21 +- |
| drivers/net/wireless/ath/ath9k/debug.c | 107 +++++ |
| drivers/net/wireless/ath/ath9k/gpio.c | 365 ++++++++++++++++-- |
| drivers/net/wireless/ath/ath9k/hsr.c | 247 ++++++++++++ |
| drivers/net/wireless/ath/ath9k/hsr.h | 48 +++ |
| .../net/wireless/ath/ath9k/htc_drv_debug.c | 2 + |
| drivers/net/wireless/ath/ath9k/htc_drv_init.c | 7 +- |
| drivers/net/wireless/ath/ath9k/hw-ops.h | 6 + |
| drivers/net/wireless/ath/ath9k/hw.c | 154 ++++++-- |
| drivers/net/wireless/ath/ath9k/hw.h | 12 + |
| drivers/net/wireless/ath/ath9k/init.c | 48 ++- |
| 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 + |
| 29 files changed, 1438 insertions(+), 220 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 888c5e2..f7e2e89 100644 |
| --- a/drivers/net/wireless/ath/ath.h |
| +++ b/drivers/net/wireless/ath/ath.h |
| @@ -153,6 +153,7 @@ struct ath_common { |
| int debug_mask; |
| enum ath_device_state state; |
| unsigned long op_flags; |
| + u32 chan_bw; |
| |
| struct ath_ani ani; |
| |
| @@ -181,6 +182,7 @@ struct ath_common { |
| const struct ath_ops *ops; |
| const struct ath_bus_ops *bus_ops; |
| const struct ath_ps_ops *ps_ops; |
| + const struct ieee80211_ops *ieee_ops; |
| |
| bool btcoex_enabled; |
| bool disable_ani; |
| diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig |
| index b97598f..d3e72a8 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 && MAC80211_DEBUGFS |
| 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 1a6697b..f9a76e7 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,17 +367,23 @@ 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; |
| } |
| +#ifdef CONFIG_OF |
| + pdev->dev.platform_data = NULL; |
| +#endif |
| |
| ah = sc->sc_ah; |
| ath9k_hw_name(ah, hw_name, sizeof(hw_name)); |
| @@ -162,6 +417,9 @@ static struct platform_driver ath_ahb_driver = { |
| .remove_new = 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 f715149..0246ad0 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, |
| @@ -1065,11 +1051,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) { |
| @@ -1083,61 +1064,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); |
| @@ -1915,6 +1841,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(ah), AR_PHY_TEST_BBB_OBS_SEL, 1); |
| + REG_CLR_BIT(ah, AR_PHY_TEST(ah), AR_PHY_TEST_RX_OBS_SEL_BIT5); |
| + REG_RMW_FIELD(ah, AR_PHY_TEST_CTL_STATUS(ah), 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); |
| @@ -1951,6 +1897,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 97f4710..94efbdb 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" |
| @@ -90,7 +92,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 |
| @@ -845,6 +847,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) |
| { |
| @@ -981,6 +986,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; |
| @@ -994,6 +1014,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; |
| @@ -1034,9 +1057,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-debug.c b/drivers/net/wireless/ath/ath9k/common-debug.c |
| index 7aefb79..944bbe0 100644 |
| --- a/drivers/net/wireless/ath/ath9k/common-debug.c |
| +++ b/drivers/net/wireless/ath/ath9k/common-debug.c |
| @@ -260,3 +260,110 @@ void ath9k_cmn_debug_phy_err(struct dentry *debugfs_phy, |
| &fops_phy_err); |
| } |
| EXPORT_SYMBOL(ath9k_cmn_debug_phy_err); |
| + |
| +static ssize_t read_file_eeprom(struct file *file, char __user *user_buf, |
| + size_t count, loff_t *ppos) |
| +{ |
| + struct ath_hw *ah = file->private_data; |
| + 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; |
| + } |
| + if (copy_to_user(user_buf, from, bytes)) |
| + return -EFAULT; |
| + user_buf += bytes; |
| + } |
| + return *ppos - pos; |
| +} |
| + |
| +static const struct file_operations fops_eeprom = { |
| + .read = read_file_eeprom, |
| + .open = simple_open, |
| + .owner = THIS_MODULE |
| +}; |
| + |
| +void ath9k_cmn_debug_eeprom(struct dentry *debugfs_phy, |
| + struct ath_hw *ah) |
| +{ |
| + debugfs_create_file("eeprom", S_IRUSR, debugfs_phy, ah, |
| + &fops_eeprom); |
| +} |
| +EXPORT_SYMBOL(ath9k_cmn_debug_eeprom); |
| + |
| +static ssize_t read_file_chan_bw(struct file *file, char __user *user_buf, |
| + size_t count, loff_t *ppos) |
| +{ |
| + struct ath_hw *ah = file->private_data; |
| + struct ath_common *common = ath9k_hw_common(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_hw *ah = file->private_data; |
| + struct ath_common *common = ath9k_hw_common(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)) |
| + common->ieee_ops->config(ah->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, |
| +}; |
| + |
| +void ath9k_cmn_debug_chanbw(struct dentry *debugfs_phy, |
| + struct ath_hw *ah) |
| +{ |
| + debugfs_create_file("chanbw", S_IRUSR | S_IWUSR, debugfs_phy, ah, |
| + &fops_chanbw); |
| +} |
| +EXPORT_SYMBOL(ath9k_cmn_debug_chanbw); |
| diff --git a/drivers/net/wireless/ath/ath9k/common-debug.h b/drivers/net/wireless/ath/ath9k/common-debug.h |
| index 54f4c42..7a8b7ed 100644 |
| --- a/drivers/net/wireless/ath/ath9k/common-debug.h |
| +++ b/drivers/net/wireless/ath/ath9k/common-debug.h |
| @@ -69,6 +69,10 @@ void ath9k_cmn_debug_modal_eeprom(struct dentry *debugfs_phy, |
| struct ath_hw *ah); |
| void ath9k_cmn_debug_base_eeprom(struct dentry *debugfs_phy, |
| struct ath_hw *ah); |
| +void ath9k_cmn_debug_eeprom(struct dentry *debugfs_phy, |
| + struct ath_hw *ah); |
| +void ath9k_cmn_debug_chanbw(struct dentry *debugfs_phy, |
| + struct ath_hw *ah); |
| void ath9k_cmn_debug_stat_rx(struct ath_rx_stats *rxstats, |
| struct ath_rx_status *rs); |
| void ath9k_cmn_debug_recv(struct dentry *debugfs_phy, |
| 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 87ffcb4..f081d1d 100644 |
| --- a/drivers/net/wireless/ath/ath9k/debug.c |
| +++ b/drivers/net/wireless/ath/ath9k/debug.c |
| @@ -123,6 +123,61 @@ static const struct file_operations fops_debug = { |
| |
| #define DMA_BUF_LEN 1024 |
| |
| +#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_ani(struct file *file, char __user *user_buf, |
| size_t count, loff_t *ppos) |
| @@ -1373,6 +1428,50 @@ void ath9k_deinit_debug(struct ath_softc *sc) |
| ath9k_cmn_spectral_deinit_debug(&sc->spec_priv); |
| } |
| |
| +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); |
| @@ -1392,6 +1491,12 @@ 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); |
| |
| +#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, |
| @@ -1431,6 +1536,8 @@ int ath9k_init_debug(struct ath_hw *ah) |
| |
| ath9k_cmn_debug_base_eeprom(sc->debug.debugfs_phy, sc->sc_ah); |
| ath9k_cmn_debug_modal_eeprom(sc->debug.debugfs_phy, sc->sc_ah); |
| + ath9k_cmn_debug_eeprom(sc->debug.debugfs_phy, sc->sc_ah); |
| + ath9k_cmn_debug_chanbw(sc->debug.debugfs_phy, sc->sc_ah); |
| |
| debugfs_create_u32("gpio_mask", 0600, |
| sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask); |
| diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c |
| index a8101c9..e1449d7 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(ah)) >> (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/htc_drv_debug.c b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c |
| index f7c6d9b..5c015ac 100644 |
| --- a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c |
| +++ b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c |
| @@ -514,6 +514,8 @@ int ath9k_htc_init_debug(struct ath_hw *ah) |
| |
| ath9k_cmn_debug_base_eeprom(priv->debug.debugfs_phy, priv->ah); |
| ath9k_cmn_debug_modal_eeprom(priv->debug.debugfs_phy, priv->ah); |
| + ath9k_cmn_debug_eeprom(priv->debug.debugfs_phy, priv->ah); |
| + ath9k_cmn_debug_chanbw(priv->debug.debugfs_phy, priv->ah); |
| |
| return 0; |
| } |
| diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c |
| index fa02d9a..53e49b6 100644 |
| --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c |
| +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c |
| @@ -631,6 +631,7 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv, |
| priv->ah = ah; |
| |
| common = ath9k_hw_common(ah); |
| + common->ieee_ops = &ath9k_htc_ops; |
| common->ops = &ah->reg_ops; |
| common->ps_ops = &ath9k_htc_ps_ops; |
| common->bus_ops = &ath9k_usb_bus_ops; |
| @@ -746,9 +747,9 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, |
| |
| hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN | |
| WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | |
| - WIPHY_FLAG_HAS_CHANNEL_SWITCH; |
| - |
| - hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; |
| + WIPHY_FLAG_HAS_CHANNEL_SWITCH | |
| + WIPHY_FLAG_SUPPORTS_5_10_MHZ | |
| + WIPHY_FLAG_SUPPORTS_TDLS; |
| |
| hw->queues = 4; |
| hw->max_listen_interval = 1; |
| 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..f53964b 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; |
| + int i; |
| |
| - for (i = 0; i < AR_NUM_QCU; i++) { |
| - npend = ath9k_hw_numtxpending(ah, i); |
| - if (npend) |
| - break; |
| + if (type == ATH9K_RESET_COLD) |
| + return true; |
| + |
| + if (AR_SREV_9550(ah)) |
| + return true; |
| + |
| + /* 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; |
| + } |
| } |
| |
| - if (ah->external_reset && |
| - (npend || type == ATH9K_RESET_COLD)) { |
| - int reset_err = 0; |
| + return false; |
| +} |
| |
| - ath_dbg(ath9k_hw_common(ah), RESET, |
| - "reset MAC via external reset\n"); |
| +static bool ath9k_hw_external_reset(struct ath_hw *ah, int type) |
| +{ |
| + int err; |
| |
| - reset_err = ah->external_reset(); |
| - if (reset_err) { |
| - ath_err(ath9k_hw_common(ah), |
| - "External reset failed, err=%d\n", |
| - reset_err); |
| - return false; |
| - } |
| + if (!ah->external_reset || !ath9k_hw_need_external_reset(ah, type)) |
| + return true; |
| |
| - REG_WRITE(ah, AR_RTC_RESET(ah), 1); |
| + 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(ah), 0); |
| + udelay(10); |
| + } |
| + |
| + REG_WRITE(ah, AR_RTC_RESET(ah), 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 2458387..2f5266c 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) |
| { |
| @@ -733,6 +739,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, |
| if (!ath9k_is_chanctx_enabled()) |
| sc->cur_chan->hw_queue_base = 0; |
| |
| + common->ieee_ops = &ath9k_ops; |
| common->ops = &ah->reg_ops; |
| common->bus_ops = bus_ops; |
| common->ps_ops = &ath9k_ps_ops; |
| @@ -803,6 +810,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 +880,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 +893,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 +974,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 +987,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 +1059,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 +1111,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 +1203,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 6d120df..2807d36 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 6421e49..20c7095 100644 |
| --- a/drivers/net/wireless/ath/ath9k/pci.c |
| +++ b/drivers/net/wireless/ath/ath9k/pci.c |
| @@ -772,6 +772,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 b200b00..b5745bf 100644 |
| --- a/local-symbols |
| +++ b/local-symbols |
| @@ -128,6 +128,7 @@ ATH9K_WOW= |
| ATH9K_RFKILL= |
| ATH9K_CHANNEL_CONTEXT= |
| ATH9K_PCOEM= |
| +ATH9K_UBNTHSR= |
| ATH9K_PCI_NO_EEPROM= |
| ATH9K_HTC= |
| ATH9K_HTC_DEBUGFS= |
| -- |
| 2.39.2 |
| |