123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 |
- --- a/drivers/net/wireless/ath/ath9k/channel.c
- +++ b/drivers/net/wireless/ath/ath9k/channel.c
- @@ -15,6 +15,8 @@
- */
-
- #include "ath9k.h"
- +#include <linux/ath9k_platform.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 +24,7 @@
- */
- static int ath_set_channel(struct ath_softc *sc)
- {
- + struct ath9k_platform_data *pdata = sc->dev->platform_data;
- struct ath_hw *ah = sc->sc_ah;
- struct ath_common *common = ath9k_hw_common(ah);
- struct ieee80211_hw *hw = sc->hw;
- @@ -41,6 +44,11 @@ static int ath_set_channel(struct ath_so
- ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n",
- chan->center_freq, chandef->width);
-
- + if (pdata && pdata->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_bh(&common->cc_lock);
- ath_update_survey_stats(sc);
- --- /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;
- +}
- --- /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 */
- --- a/drivers/net/wireless/ath/ath9k/main.c
- +++ b/drivers/net/wireless/ath/ath9k/main.c
- @@ -16,8 +16,10 @@
-
- #include <linux/nl80211.h>
- #include <linux/delay.h>
- +#include <linux/ath9k_platform.h>
- #include "ath9k.h"
- #include "btcoex.h"
- +#include "hsr.h"
-
- u8 ath9k_parse_mpdudensity(u8 mpdudensity)
- {
- @@ -652,6 +654,7 @@ void ath_reset_work(struct work_struct *
- static int ath9k_start(struct ieee80211_hw *hw)
- {
- struct ath_softc *sc = hw->priv;
- + struct ath9k_platform_data *pdata = sc->dev->platform_data;
- struct ath_hw *ah = sc->sc_ah;
- struct ath_common *common = ath9k_hw_common(ah);
- struct ieee80211_channel *curchan = sc->cur_chan->chandef.chan;
- @@ -730,6 +733,11 @@ static int ath9k_start(struct ieee80211_
- AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
- }
-
- + if (pdata && pdata->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.
- --- a/drivers/net/wireless/ath/ath9k/Makefile
- +++ b/drivers/net/wireless/ath/ath9k/Makefile
- @@ -16,6 +16,7 @@ ath9k-$(CPTCFG_ATH9K_DFS_CERTIFIED) += d
- 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
-
- --- a/include/linux/ath9k_platform.h
- +++ b/include/linux/ath9k_platform.h
- @@ -54,6 +54,8 @@ struct ath9k_platform_data {
- unsigned num_btns;
- const struct gpio_keys_button *btns;
- unsigned btn_poll_interval;
- +
- + bool ubnt_hsr;
- };
-
- #endif /* _LINUX_ATH9K_PLATFORM_H */
- --- a/.local-symbols
- +++ b/.local-symbols
- @@ -153,6 +153,7 @@ ATH9K_WOW=
- ATH9K_RFKILL=
- ATH9K_CHANNEL_CONTEXT=
- ATH9K_PCOEM=
- +ATH9K_UBNTHSR=
- ATH9K_HTC=
- ATH9K_HTC_DEBUGFS=
- ATH9K_HWRNG=
- --- a/drivers/net/wireless/ath/ath9k/Kconfig
- +++ b/drivers/net/wireless/ath/ath9k/Kconfig
- @@ -59,6 +59,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
|