aboutsummaryrefslogtreecommitdiffstats
path: root/package/kernel/mac80211/patches/319-ath9k_htc-add-new-WMI_REG_RMW_CMDID-command.patch
diff options
context:
space:
mode:
Diffstat (limited to 'package/kernel/mac80211/patches/319-ath9k_htc-add-new-WMI_REG_RMW_CMDID-command.patch')
-rw-r--r--package/kernel/mac80211/patches/319-ath9k_htc-add-new-WMI_REG_RMW_CMDID-command.patch307
1 files changed, 307 insertions, 0 deletions
diff --git a/package/kernel/mac80211/patches/319-ath9k_htc-add-new-WMI_REG_RMW_CMDID-command.patch b/package/kernel/mac80211/patches/319-ath9k_htc-add-new-WMI_REG_RMW_CMDID-command.patch
new file mode 100644
index 0000000000..c3942cecf3
--- /dev/null
+++ b/package/kernel/mac80211/patches/319-ath9k_htc-add-new-WMI_REG_RMW_CMDID-command.patch
@@ -0,0 +1,307 @@
+From: Oleksij Rempel <linux@rempel-privat.de>
+Date: Sun, 22 Mar 2015 19:29:46 +0100
+Subject: [PATCH] ath9k_htc: add new WMI_REG_RMW_CMDID command
+
+Since usb bus add extra delay on each request, a command
+with read + write requests is too expensive. We can dramtically
+reduce usb load by moving this command to firmware.
+
+In my tests, this patch will reduce channel scan time
+for about 5-10 seconds.
+
+Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/ath/ath.h
++++ b/drivers/net/wireless/ath/ath.h
+@@ -131,6 +131,9 @@ struct ath_ops {
+ void (*enable_write_buffer)(void *);
+ void (*write_flush) (void *);
+ u32 (*rmw)(void *, u32 reg_offset, u32 set, u32 clr);
++ void (*enable_rmw_buffer)(void *);
++ void (*rmw_flush) (void *);
++
+ };
+
+ struct ath_common;
+--- a/drivers/net/wireless/ath/ath9k/htc.h
++++ b/drivers/net/wireless/ath/ath9k/htc.h
+@@ -444,6 +444,10 @@ static inline void ath9k_htc_stop_btcoex
+ #define OP_BT_SCAN BIT(4)
+ #define OP_TSF_RESET BIT(6)
+
++enum htc_op_flags {
++ HTC_FWFLAG_NO_RMW,
++};
++
+ struct ath9k_htc_priv {
+ struct device *dev;
+ struct ieee80211_hw *hw;
+@@ -482,6 +486,7 @@ struct ath9k_htc_priv {
+ bool reconfig_beacon;
+ unsigned int rxfilter;
+ unsigned long op_flags;
++ unsigned long fw_flags;
+
+ struct ath9k_hw_cal_data caldata;
+ struct ath_spec_scan_priv spec_priv;
+--- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c
++++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
+@@ -376,17 +376,139 @@ static void ath9k_regwrite_flush(void *h
+ mutex_unlock(&priv->wmi->multi_write_mutex);
+ }
+
+-static u32 ath9k_reg_rmw(void *hw_priv, u32 reg_offset, u32 set, u32 clr)
++static void ath9k_reg_rmw_buffer(void *hw_priv,
++ u32 reg_offset, u32 set, u32 clr)
++{
++ struct ath_hw *ah = (struct ath_hw *) hw_priv;
++ struct ath_common *common = ath9k_hw_common(ah);
++ struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
++ u32 rsp_status;
++ int r;
++
++ mutex_lock(&priv->wmi->multi_rmw_mutex);
++
++ /* Store the register/value */
++ priv->wmi->multi_rmw[priv->wmi->multi_rmw_idx].reg =
++ cpu_to_be32(reg_offset);
++ priv->wmi->multi_rmw[priv->wmi->multi_rmw_idx].set =
++ cpu_to_be32(set);
++ priv->wmi->multi_rmw[priv->wmi->multi_rmw_idx].clr =
++ cpu_to_be32(clr);
++
++ priv->wmi->multi_rmw_idx++;
++
++ /* If the buffer is full, send it out. */
++ if (priv->wmi->multi_rmw_idx == MAX_RMW_CMD_NUMBER) {
++ r = ath9k_wmi_cmd(priv->wmi, WMI_REG_RMW_CMDID,
++ (u8 *) &priv->wmi->multi_rmw,
++ sizeof(struct register_write) * priv->wmi->multi_rmw_idx,
++ (u8 *) &rsp_status, sizeof(rsp_status),
++ 100);
++ if (unlikely(r)) {
++ ath_dbg(common, WMI,
++ "REGISTER RMW FAILED, multi len: %d\n",
++ priv->wmi->multi_rmw_idx);
++ }
++ priv->wmi->multi_rmw_idx = 0;
++ }
++
++ mutex_unlock(&priv->wmi->multi_rmw_mutex);
++}
++
++static void ath9k_reg_rmw_flush(void *hw_priv)
+ {
+- u32 val;
++ struct ath_hw *ah = (struct ath_hw *) hw_priv;
++ struct ath_common *common = ath9k_hw_common(ah);
++ struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
++ u32 rsp_status;
++ int r;
++
++ if (test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags))
++ return;
++
++ atomic_dec(&priv->wmi->m_rmw_cnt);
++
++ mutex_lock(&priv->wmi->multi_rmw_mutex);
++
++ if (priv->wmi->multi_rmw_idx) {
++ r = ath9k_wmi_cmd(priv->wmi, WMI_REG_RMW_CMDID,
++ (u8 *) &priv->wmi->multi_rmw,
++ sizeof(struct register_rmw) * priv->wmi->multi_rmw_idx,
++ (u8 *) &rsp_status, sizeof(rsp_status),
++ 100);
++ if (unlikely(r)) {
++ ath_dbg(common, WMI,
++ "REGISTER RMW FAILED, multi len: %d\n",
++ priv->wmi->multi_rmw_idx);
++ }
++ priv->wmi->multi_rmw_idx = 0;
++ }
++
++ mutex_unlock(&priv->wmi->multi_rmw_mutex);
++}
++
++static void ath9k_enable_rmw_buffer(void *hw_priv)
++{
++ struct ath_hw *ah = (struct ath_hw *) hw_priv;
++ struct ath_common *common = ath9k_hw_common(ah);
++ struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
++
++ if (test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags))
++ return;
+
+- val = ath9k_regread(hw_priv, reg_offset);
+- val &= ~clr;
+- val |= set;
+- ath9k_regwrite(hw_priv, val, reg_offset);
++ atomic_inc(&priv->wmi->m_rmw_cnt);
++}
++
++static u32 ath9k_reg_rmw_single(void *hw_priv,
++ u32 reg_offset, u32 set, u32 clr)
++{
++ struct ath_hw *ah = (struct ath_hw *) hw_priv;
++ struct ath_common *common = ath9k_hw_common(ah);
++ struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
++ struct register_rmw buf, buf_ret;
++ int ret;
++ u32 val = 0;
++
++ buf.reg = cpu_to_be32(reg_offset);
++ buf.set = cpu_to_be32(set);
++ buf.clr = cpu_to_be32(clr);
++
++ ret = ath9k_wmi_cmd(priv->wmi, WMI_REG_RMW_CMDID,
++ (u8 *) &buf, sizeof(buf),
++ (u8 *) &buf_ret, sizeof(buf_ret),
++ 100);
++ if (unlikely(ret)) {
++ ath_dbg(common, WMI, "REGISTER RMW FAILED:(0x%04x, %d)\n",
++ reg_offset, ret);
++ }
+ return val;
+ }
+
++static u32 ath9k_reg_rmw(void *hw_priv, u32 reg_offset, u32 set, u32 clr)
++{
++ struct ath_hw *ah = (struct ath_hw *) hw_priv;
++ struct ath_common *common = ath9k_hw_common(ah);
++ struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
++
++ if (test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags)) {
++ u32 val;
++
++ val = REG_READ(ah, reg_offset);
++ val &= ~clr;
++ val |= set;
++ REG_WRITE(ah, reg_offset, val);
++
++ return 0;
++ }
++
++ if (atomic_read(&priv->wmi->m_rmw_cnt))
++ ath9k_reg_rmw_buffer(hw_priv, reg_offset, set, clr);
++ else
++ ath9k_reg_rmw_single(hw_priv, reg_offset, set, clr);
++
++ return 0;
++}
++
+ static void ath_usb_read_cachesize(struct ath_common *common, int *csz)
+ {
+ *csz = L1_CACHE_BYTES >> 2;
+@@ -501,6 +623,8 @@ static int ath9k_init_priv(struct ath9k_
+ ah->reg_ops.write = ath9k_regwrite;
+ ah->reg_ops.enable_write_buffer = ath9k_enable_regwrite_buffer;
+ ah->reg_ops.write_flush = ath9k_regwrite_flush;
++ ah->reg_ops.enable_rmw_buffer = ath9k_enable_rmw_buffer;
++ ah->reg_ops.rmw_flush = ath9k_reg_rmw_flush;
+ ah->reg_ops.rmw = ath9k_reg_rmw;
+ priv->ah = ah;
+
+@@ -686,6 +810,12 @@ static int ath9k_init_firmware_version(s
+ return -EINVAL;
+ }
+
++ if (priv->fw_version_major == 1 && priv->fw_version_minor < 4)
++ set_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags);
++
++ dev_info(priv->dev, "FW RMW support: %s\n",
++ test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags) ? "Off" : "On");
++
+ return 0;
+ }
+
+--- a/drivers/net/wireless/ath/ath9k/hw.h
++++ b/drivers/net/wireless/ath/ath9k/hw.h
+@@ -100,6 +100,18 @@
+ (_ah)->reg_ops.write_flush((_ah)); \
+ } while (0)
+
++#define ENABLE_REG_RMW_BUFFER(_ah) \
++ do { \
++ if ((_ah)->reg_ops.enable_rmw_buffer) \
++ (_ah)->reg_ops.enable_rmw_buffer((_ah)); \
++ } while (0)
++
++#define REG_RMW_BUFFER_FLUSH(_ah) \
++ do { \
++ if ((_ah)->reg_ops.rmw_flush) \
++ (_ah)->reg_ops.rmw_flush((_ah)); \
++ } while (0)
++
+ #define PR_EEP(_s, _val) \
+ do { \
+ len += scnprintf(buf + len, size - len, "%20s : %10d\n",\
+--- a/drivers/net/wireless/ath/ath9k/wmi.c
++++ b/drivers/net/wireless/ath/ath9k/wmi.c
+@@ -61,6 +61,8 @@ static const char *wmi_cmd_to_name(enum
+ return "WMI_REG_READ_CMDID";
+ case WMI_REG_WRITE_CMDID:
+ return "WMI_REG_WRITE_CMDID";
++ case WMI_REG_RMW_CMDID:
++ return "WMI_REG_RMW_CMDID";
+ case WMI_RC_STATE_CHANGE_CMDID:
+ return "WMI_RC_STATE_CHANGE_CMDID";
+ case WMI_RC_RATE_UPDATE_CMDID:
+@@ -101,6 +103,7 @@ struct wmi *ath9k_init_wmi(struct ath9k_
+ spin_lock_init(&wmi->event_lock);
+ mutex_init(&wmi->op_mutex);
+ mutex_init(&wmi->multi_write_mutex);
++ mutex_init(&wmi->multi_rmw_mutex);
+ init_completion(&wmi->cmd_wait);
+ INIT_LIST_HEAD(&wmi->pending_tx_events);
+ tasklet_init(&wmi->wmi_event_tasklet, ath9k_wmi_event_tasklet,
+--- a/drivers/net/wireless/ath/ath9k/wmi.h
++++ b/drivers/net/wireless/ath/ath9k/wmi.h
+@@ -112,6 +112,7 @@ enum wmi_cmd_id {
+ WMI_TX_STATS_CMDID,
+ WMI_RX_STATS_CMDID,
+ WMI_BITRATE_MASK_CMDID,
++ WMI_REG_RMW_CMDID,
+ };
+
+ enum wmi_event_id {
+@@ -125,12 +126,19 @@ enum wmi_event_id {
+ };
+
+ #define MAX_CMD_NUMBER 62
++#define MAX_RMW_CMD_NUMBER 15
+
+ struct register_write {
+ __be32 reg;
+ __be32 val;
+ };
+
++struct register_rmw {
++ __be32 reg;
++ __be32 set;
++ __be32 clr;
++} __packed;
++
+ struct ath9k_htc_tx_event {
+ int count;
+ struct __wmi_event_txstatus txs;
+@@ -156,10 +164,18 @@ struct wmi {
+
+ spinlock_t wmi_lock;
+
++ /* multi write section */
+ atomic_t mwrite_cnt;
+ struct register_write multi_write[MAX_CMD_NUMBER];
+ u32 multi_write_idx;
+ struct mutex multi_write_mutex;
++
++ /* multi rmw section */
++ atomic_t m_rmw_cnt;
++ struct register_rmw multi_rmw[MAX_RMW_CMD_NUMBER];
++ u32 multi_rmw_idx;
++ struct mutex multi_rmw_mutex;
++
+ };
+
+ struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv);