From e3d89ee8c4ecf67892bd38cfe17116b9cf9a9495 Mon Sep 17 00:00:00 2001
From: Matthias Schiffer <mschiffer@universe-factory.net>
Date: Thu, 3 Sep 2015 23:51:19 +0200
Subject: [PATCH] ath9k: add HSR tuner support for UniFi Outdoor Plus

---
 ...tuner-support-for-UniFi-Outdoor-Plus.patch | 399 ++++++++++++++++++
 1 file changed, 399 insertions(+)
 create mode 100644 patches/openwrt/0024-ath9k-add-HSR-tuner-support-for-UniFi-Outdoor-Plus.patch

diff --git a/patches/openwrt/0024-ath9k-add-HSR-tuner-support-for-UniFi-Outdoor-Plus.patch b/patches/openwrt/0024-ath9k-add-HSR-tuner-support-for-UniFi-Outdoor-Plus.patch
new file mode 100644
index 000000000..98a85aaff
--- /dev/null
+++ b/patches/openwrt/0024-ath9k-add-HSR-tuner-support-for-UniFi-Outdoor-Plus.patch
@@ -0,0 +1,399 @@
+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..4cd6faf
+--- /dev/null
++++ b/package/kernel/mac80211/patches/931-ubnt-uap-plus-hsr.patch
+@@ -0,0 +1,353 @@
++--- 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,220 @@
+++/*
+++ *
+++ * 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_cfg_gpio_input(ah, HSR_GPIO_DIN);
+++	ath9k_hw_cfg_output(ah, HSR_GPIO_CSN, AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
+++	ath9k_hw_cfg_output(ah, HSR_GPIO_CLK, AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
+++	ath9k_hw_cfg_output(ah, HSR_GPIO_DOUT, 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,40 @@
+++/*
+++ * 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 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);
+++#else
+++static inline void hsr_init(struct ath_hw* ah) {}
+++static inline int hsr_disable(struct ath_hw* ah) { return 0; }
+++static inline int hsr_enable(struct ath_hw* ah, int bw, int fq) { return 0; }
+++static inline int 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_
++ 				  (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
++@@ -44,6 +44,8 @@ struct ath9k_platform_data {
++ 
++ 	int num_leds;
++ 	const struct gpio_led *leds;
+++
+++	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..a0ebbc7
+--- /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
++@@ -44,6 +44,8 @@ struct ath9k_platform_data {
++ 
++ 	int num_leds;
++ 	const struct gpio_led *leds;
+++
+++	bool ubnt_hsr;
++ };
++ 
++ #endif /* _LINUX_ATH9K_PLATFORM_H */
-- 
GitLab