123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395 |
- From: Matthias Schiffer <mschiffer@universe-factory.net>
- Date: Thu, 3 Sep 2015 23:50:51 +0200
- Subject: ath9k: add HSR tuner support for UniFi Outdoor Plus
- Patch-by: Stefan Rompf <stefan@loplof.de>
- diff --git a/package/kernel/mac80211/patches/931-ubnt-uap-plus-hsr.patch b/package/kernel/mac80211/patches/931-ubnt-uap-plus-hsr.patch
- new file mode 100644
- index 0000000..8e09fee
- --- /dev/null
- +++ b/package/kernel/mac80211/patches/931-ubnt-uap-plus-hsr.patch
- @@ -0,0 +1,349 @@
- +--- 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) {
- ++ hsr_enable(ah, chandef->width, chan->center_freq);
- ++ 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,223 @@
- ++/*
- ++ *
- ++ * 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 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 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, "hsr_write_byte: write byte %d return value is %d %c\n",
- ++ value, rval, rval > 32 ? rval : '-');
- ++
- ++ return rval & 0xff;
- ++}
- ++
- ++static int hsr_write_a_chain(struct ath_hw* ah, char* chain, int items) {
- ++ int i = 0;
- ++ int status = 0;
- ++
- ++ // a preamble
- ++ hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0);
- ++ status = 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 = hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0);
- ++ }
- ++ if ( loop >= 42) {
- ++ ATH_DBG_WARN(1, "hsr_write_a_chain: can't clear an output buffer after a 42 cycles.\n");
- ++ return -1;
- ++ }
- ++ }
- ++
- ++ for ( i =0; (i < items) && ( 0 != chain[i]); ++i) {
- ++ hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, (u32)chain[i]);
- ++ }
- ++
- ++ hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0);
- ++ mdelay(HSR_DELAY_FINAL / 1000);
- ++
- ++ // reply
- ++ memset(chain, 0, items);
- ++
- ++ hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0);
- ++ udelay(HSR_DELAY_TRAILING);
- ++
- ++ for ( i = 0; i < (items - 1); ++i) {
- ++ u32 ret;
- ++ if ( 0 != (ret = hsr_write_byte(ah, HSR_DELAY_PRE_WRITE, 0))) {
- ++ chain[i] = (char)ret;
- ++ } else {
- ++ break;
- ++ }
- ++ udelay(HSR_DELAY_TRAILING);
- ++ }
- ++
- ++ return (1 < i) ? simple_strtol(chain + 1, NULL, 10) : 0;
- ++}
- ++
- ++int hsr_disable(struct ath_hw* ah) {
- ++ char cmd[10] = {'b', '4', '0', 0, 0, 0, 0, 0, 0, 0};
- ++ int ret;
- ++
- ++ ret = hsr_write_a_chain(ah, cmd, sizeof(cmd));
- ++ if ( (ret > 0) && (*cmd == 'B')) {
- ++ return 0;
- ++ }
- ++
- ++ return -1;
- ++}
- ++
- ++int 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'; // 98
- ++ snprintf(cmd + 1, 3, "%02d", bw);
- ++
- ++ ret = hsr_write_a_chain(ah, cmd, sizeof(cmd));
- ++ if ( (*cmd != 'B') || (ret != bw)) {
- ++ ATH_DBG_WARN(1, "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'; // 120
- ++ ret = hsr_write_a_chain(ah, cmd, sizeof(cmd));
- ++ if ( *cmd != 'X') {
- ++ ATH_DBG_WARN(1, "hsr_enable: failed 'x' command -> reply (%d, %d) \n", *cmd, ret);
- ++ return -1;
- ++ }
- ++
- ++ memset(cmd, 0, sizeof(cmd));
- ++ *cmd = 'm'; // 109
- ++ ret = hsr_write_a_chain(ah, cmd, sizeof(cmd));
- ++ if ( *cmd != 'M') {
- ++ ATH_DBG_WARN(1, "hsr_enable: failed 'm' command -> reply (%d, %d) \n", *cmd, ret);
- ++ return -1;
- ++ }
- ++
- ++ memset(cmd, 0, sizeof(cmd));
- ++ *cmd = 'f'; // 102
- ++ snprintf(cmd + 1, 6, "%05d", fq);
- ++ ret = hsr_write_a_chain(ah, cmd, sizeof(cmd));
- ++ if ( (*cmd != 'F') && (ret != fq)) {
- ++ ATH_DBG_WARN(1, "hsr_enable: failed set frequency -> reply (%d, %d) \n", *cmd, ret);
- ++ return -1;
- ++ }
- ++
- ++ return 0;
- ++}
- ++
- ++int hsr_status(struct ath_hw* ah) {
- ++ char cmd[10] = {'s', 0, 0, 0, 0, 0, 0, 0, 0, 0}; // 115
- ++ int ret;
- ++
- ++ ret = hsr_write_a_chain(ah, cmd, sizeof(cmd));
- ++ if ( (*cmd != 'S')) {
- ++ ATH_DBG_WARN(1, "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,33 @@
- ++/*
- ++ * 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_
- ++
- ++void hsr_init(struct ath_hw* ah);
- ++int hsr_disable(struct ath_hw* ah);
- ++int hsr_enable(struct ath_hw* ah, int bw, int fq);
- ++int hsr_status(struct ath_hw* ah);
- ++
- ++#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;
- +@@ -727,6 +730,11 @@ static int ath9k_start(struct ieee80211_
- + ath9k_hw_set_gpio(ah, ah->led_pin,
- + (ah->config.led_active_high) ? 1 : 0);
- +
- ++ if (pdata && pdata->ubnt_hsr) {
- ++ hsr_init(ah);
- ++ 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
- +@@ -6,7 +6,8 @@ ath9k-y += beacon.o \
- + xmit.o \
- + link.o \
- + antenna.o \
- +- channel.o
- ++ channel.o \
- ++ hsr.o
- +
- + ath9k-$(CPTCFG_ATH9K_BTCOEX_SUPPORT) += mci.o
- + ath9k-$(CPTCFG_ATH9K_PCI) += pci.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 */
- diff --git a/target/linux/ar71xx/patches-3.18/608-MIPS-ath79-ubnt-xm-add-more-boards.patch b/target/linux/ar71xx/patches-3.18/608-MIPS-ath79-ubnt-xm-add-more-boards.patch
- index 7803513..d865ed2 100644
- --- a/target/linux/ar71xx/patches-3.18/608-MIPS-ath79-ubnt-xm-add-more-boards.patch
- +++ b/target/linux/ar71xx/patches-3.18/608-MIPS-ath79-ubnt-xm-add-more-boards.patch
- @@ -254,6 +254,7 @@
- + ath79_register_eth(0);
- + ath79_register_eth(1);
- +
- ++ ap9x_pci_get_wmac_data(0)->ubnt_hsr = true;
- + ap91_pci_init(ee, NULL);
- +
- + ath79_register_leds_gpio(-1, ARRAY_SIZE(ubnt_unifi_outdoor_plus_leds_gpio),
- diff --git a/target/linux/generic/patches-3.18/150-ath9k_ubnt_hsr_filter.patch b/target/linux/generic/patches-3.18/150-ath9k_ubnt_hsr_filter.patch
- new file mode 100644
- index 0000000..b8844f1
- --- /dev/null
- +++ b/target/linux/generic/patches-3.18/150-ath9k_ubnt_hsr_filter.patch
- @@ -0,0 +1,16 @@
- +Flag that this platform is an Ubiquiti UniFi Outdoor Plus
- +containing a RF filter in ath9k's receive path.
- +
- +Signed-off-by: Stefan Rompf <stefan@loplof.de>
- +
- +--- 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 */
|