|
|
@@ -0,0 +1,307 @@
|
|
|
+From: Oleksij Rempel <[email protected]>
|
|
|
+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 <[email protected]>
|
|
|
+Signed-off-by: Kalle Valo <[email protected]>
|
|
|
+---
|
|
|
+
|
|
|
+--- 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);
|