diff mbox

[OpenWrt-Devel,v4,2/2] Add HSR tuner to ath9k

Message ID 201506162316.46858.stefan@loplof.de
State Changes Requested
Delegated to: Felix Fietkau
Headers show

Commit Message

Stefan Rompf June 16, 2015, 9:16 p.m. UTC
Add tuner for the HSR filter of the UniFi Outdoor Plus access point. Usage of
the tuner is controlled at runtime by ath9k_platform_data. The code can be
enabled or disabled by a compile time option.

Signed-off-by: Stefan Rompf <stefan@loplof.de>
---
diff mbox

Patch

Index: package/kernel/mac80211/Makefile
===================================================================
--- package/kernel/mac80211/Makefile	(Revision 46000)
+++ package/kernel/mac80211/Makefile	(Arbeitskopie)
@@ -587,6 +587,11 @@ 
 		bool "Support chips used in PC OEM cards"
 		depends on PACKAGE_kmod-ath9k
 	
+	config ATH9K_UBNTHSR
+		bool "Support for Ubiquiti UniFi Outdoor Plus access point"
+		depends on PACKAGE_kmod-ath9k
+		default y
+	
 endef
 
 define KernelPackage/ath9k-htc
@@ -1558,6 +1563,7 @@ 
 config-$(CONFIG_PCI) += ATH9K_PCI
 config-$(CONFIG_ATH_USER_REGD) += ATH_USER_REGD
 config-$(CONFIG_ATH9K_SUPPORT_PCOEM) += ATH9K_PCOEM
+config-$(CONFIG_ATH9K_UBNTHSR) += ATH9K_UBNTHSR
 
 config-$(call config_package,ath9k-htc) += ATH9K_HTC
 config-$(call config_package,ath10k) += ATH10K ATH10K_PCI
Index: package/kernel/mac80211/patches/930-ubnt-uap-plus-hsr.patch
===================================================================
--- package/kernel/mac80211/patches/930-ubnt-uap-plus-hsr.patch	(Revision 0)
+++ package/kernel/mac80211/patches/930-ubnt-uap-plus-hsr.patch	(Arbeitskopie)
@@ -0,0 +1,373 @@ 
+--- compat-wireless-2015-03-09/drivers/net/wireless/ath/ath9k.orig/channel.c	2015-06-16 09:28:34.000000000 +0200
++++ compat-wireless-2015-03-09/drivers/net/wireless/ath/ath9k/channel.c	2015-06-16 09:49:53.000000000 +0200
+@@ -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 @@
+ 	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);
+--- compat-wireless-2015-03-09/drivers/net/wireless/ath/ath9k.orig/hsr.c	1970-01-01 01:00:00.000000000 +0100
++++ compat-wireless-2015-03-09/drivers/net/wireless/ath/ath9k/hsr.c	2015-06-16 09:41:24.000000000 +0200
+@@ -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("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("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("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("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("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("hsr_status: returned %d,%d \n", *cmd, ret);
++		return -1;
++	}
++
++	return 0;
++}
++
+--- compat-wireless-2015-03-09/drivers/net/wireless/ath/ath9k.orig/hsr.h	1970-01-01 01:00:00.000000000 +0100
++++ compat-wireless-2015-03-09/drivers/net/wireless/ath/ath9k/hsr.h	2015-06-16 09:17:52.000000000 +0200
+@@ -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_ */
+--- compat-wireless-2015-03-09/drivers/net/wireless/ath/ath9k.orig/Kconfig	2015-03-10 04:37:16.000000000 +0100
++++ compat-wireless-2015-03-09/drivers/net/wireless/ath/ath9k/Kconfig	2015-06-16 09:13:53.000000000 +0200
+@@ -59,6 +59,20 @@
+ 	  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
++	default y
++	---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
+--- compat-wireless-2015-03-09/drivers/net/wireless/ath/ath9k.orig/main.c	2015-06-16 08:45:50.000000000 +0200
++++ compat-wireless-2015-03-09/drivers/net/wireless/ath/ath9k/main.c	2015-06-16 09:49:43.000000000 +0200
+@@ -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 @@
+ 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 @@
+ 				  (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.
+--- compat-wireless-2015-03-09/drivers/net/wireless/ath/ath9k.orig/Makefile	2015-03-10 04:37:16.000000000 +0100
++++ compat-wireless-2015-03-09/drivers/net/wireless/ath/ath9k/Makefile	2015-06-16 09:14:59.000000000 +0200
+@@ -15,6 +15,7 @@
+ ath9k-$(CPTCFG_ATH9K_DFS_CERTIFIED) += dfs.o
+ ath9k-$(CPTCFG_ATH9K_TX99) += tx99.o
+ ath9k-$(CPTCFG_ATH9K_WOW) += wow.o
++ath9k-$(CPTCFG_ATH9K_UBNTHSR) += hsr.o
+ 
+ ath9k-$(CPTCFG_ATH9K_DEBUGFS) += debug.o
+ 
+--- compat-wireless-2015-03-09/.local-symbols.orig	2015-06-16 14:54:43.000000000 +0200
++++ compat-wireless-2015-03-09/.local-symbols	2015-06-16 14:54:45.000000000 +0200
+@@ -115,6 +115,7 @@
+ ATH9K_RFKILL=
+ ATH9K_CHANNEL_CONTEXT=
+ ATH9K_PCOEM=
++ATH9K_UBNTHSR=
+ ATH9K_HTC=
+ ATH9K_HTC_DEBUGFS=
+ CARL9170=