diff options
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.patch | 307 |
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); |