ath9k_htc: add new WMI_REG_RMW_CMDID command
authorOleksij Rempel <linux@rempel-privat.de>
Sun, 22 Mar 2015 18:29:46 +0000 (19:29 +0100)
committerKalle Valo <kvalo@codeaurora.org>
Mon, 30 Mar 2015 08:31:18 +0000 (11:31 +0300)
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>
drivers/net/wireless/ath/ath.h
drivers/net/wireless/ath/ath9k/htc.h
drivers/net/wireless/ath/ath9k/htc_drv_init.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/wmi.c
drivers/net/wireless/ath/ath9k/wmi.h

index 1eebe2e..7e94810 100644 (file)
@@ -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;
index 300d367..e82a0d4 100644 (file)
@@ -444,6 +444,10 @@ static inline void ath9k_htc_stop_btcoex(struct ath9k_htc_priv *priv)
 #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;
index fd22940..d7beefe 100644 (file)
@@ -376,17 +376,139 @@ static void ath9k_regwrite_flush(void *hw_priv)
        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);
 
-       val = ath9k_regread(hw_priv, reg_offset);
-       val &= ~clr;
-       val |= set;
-       ath9k_regwrite(hw_priv, val, reg_offset);
+       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;
+
+       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_htc_priv *priv,
        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(struct ath9k_htc_priv *priv)
                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;
 }
 
index aaec945..7b51d8e 100644 (file)
                        (_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",\
index 65c8894..67a2f8c 100644 (file)
@@ -61,6 +61,8 @@ static const char *wmi_cmd_to_name(enum wmi_cmd_id wmi_cmd)
                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_htc_priv *priv)
        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,
index 0db37f2..aa84a33 100644 (file)
@@ -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);