1 From: Oleksij Rempel <linux@rempel-privat.de>
2 Date: Sun, 22 Mar 2015 19:29:46 +0100
3 Subject: [PATCH] ath9k_htc: add new WMI_REG_RMW_CMDID command
5 Since usb bus add extra delay on each request, a command
6 with read + write requests is too expensive. We can dramtically
7 reduce usb load by moving this command to firmware.
9 In my tests, this patch will reduce channel scan time
10 for about 5-10 seconds.
12 Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
13 Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
16 --- a/drivers/net/wireless/ath/ath.h
17 +++ b/drivers/net/wireless/ath/ath.h
18 @@ -131,6 +131,9 @@ struct ath_ops {
19 void (*enable_write_buffer)(void *);
20 void (*write_flush) (void *);
21 u32 (*rmw)(void *, u32 reg_offset, u32 set, u32 clr);
22 + void (*enable_rmw_buffer)(void *);
23 + void (*rmw_flush) (void *);
28 --- a/drivers/net/wireless/ath/ath9k/htc.h
29 +++ b/drivers/net/wireless/ath/ath9k/htc.h
30 @@ -444,6 +444,10 @@ static inline void ath9k_htc_stop_btcoex
31 #define OP_BT_SCAN BIT(4)
32 #define OP_TSF_RESET BIT(6)
38 struct ath9k_htc_priv {
40 struct ieee80211_hw *hw;
41 @@ -482,6 +486,7 @@ struct ath9k_htc_priv {
43 unsigned int rxfilter;
44 unsigned long op_flags;
45 + unsigned long fw_flags;
47 struct ath9k_hw_cal_data caldata;
48 struct ath_spec_scan_priv spec_priv;
49 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c
50 +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
51 @@ -376,17 +376,139 @@ static void ath9k_regwrite_flush(void *h
52 mutex_unlock(&priv->wmi->multi_write_mutex);
55 -static u32 ath9k_reg_rmw(void *hw_priv, u32 reg_offset, u32 set, u32 clr)
56 +static void ath9k_reg_rmw_buffer(void *hw_priv,
57 + u32 reg_offset, u32 set, u32 clr)
59 + struct ath_hw *ah = (struct ath_hw *) hw_priv;
60 + struct ath_common *common = ath9k_hw_common(ah);
61 + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
65 + mutex_lock(&priv->wmi->multi_rmw_mutex);
67 + /* Store the register/value */
68 + priv->wmi->multi_rmw[priv->wmi->multi_rmw_idx].reg =
69 + cpu_to_be32(reg_offset);
70 + priv->wmi->multi_rmw[priv->wmi->multi_rmw_idx].set =
72 + priv->wmi->multi_rmw[priv->wmi->multi_rmw_idx].clr =
75 + priv->wmi->multi_rmw_idx++;
77 + /* If the buffer is full, send it out. */
78 + if (priv->wmi->multi_rmw_idx == MAX_RMW_CMD_NUMBER) {
79 + r = ath9k_wmi_cmd(priv->wmi, WMI_REG_RMW_CMDID,
80 + (u8 *) &priv->wmi->multi_rmw,
81 + sizeof(struct register_write) * priv->wmi->multi_rmw_idx,
82 + (u8 *) &rsp_status, sizeof(rsp_status),
85 + ath_dbg(common, WMI,
86 + "REGISTER RMW FAILED, multi len: %d\n",
87 + priv->wmi->multi_rmw_idx);
89 + priv->wmi->multi_rmw_idx = 0;
92 + mutex_unlock(&priv->wmi->multi_rmw_mutex);
95 +static void ath9k_reg_rmw_flush(void *hw_priv)
98 + struct ath_hw *ah = (struct ath_hw *) hw_priv;
99 + struct ath_common *common = ath9k_hw_common(ah);
100 + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
104 + if (test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags))
107 + atomic_dec(&priv->wmi->m_rmw_cnt);
109 + mutex_lock(&priv->wmi->multi_rmw_mutex);
111 + if (priv->wmi->multi_rmw_idx) {
112 + r = ath9k_wmi_cmd(priv->wmi, WMI_REG_RMW_CMDID,
113 + (u8 *) &priv->wmi->multi_rmw,
114 + sizeof(struct register_rmw) * priv->wmi->multi_rmw_idx,
115 + (u8 *) &rsp_status, sizeof(rsp_status),
118 + ath_dbg(common, WMI,
119 + "REGISTER RMW FAILED, multi len: %d\n",
120 + priv->wmi->multi_rmw_idx);
122 + priv->wmi->multi_rmw_idx = 0;
125 + mutex_unlock(&priv->wmi->multi_rmw_mutex);
128 +static void ath9k_enable_rmw_buffer(void *hw_priv)
130 + struct ath_hw *ah = (struct ath_hw *) hw_priv;
131 + struct ath_common *common = ath9k_hw_common(ah);
132 + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
134 + if (test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags))
137 - val = ath9k_regread(hw_priv, reg_offset);
140 - ath9k_regwrite(hw_priv, val, reg_offset);
141 + atomic_inc(&priv->wmi->m_rmw_cnt);
144 +static u32 ath9k_reg_rmw_single(void *hw_priv,
145 + u32 reg_offset, u32 set, u32 clr)
147 + struct ath_hw *ah = (struct ath_hw *) hw_priv;
148 + struct ath_common *common = ath9k_hw_common(ah);
149 + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
150 + struct register_rmw buf, buf_ret;
154 + buf.reg = cpu_to_be32(reg_offset);
155 + buf.set = cpu_to_be32(set);
156 + buf.clr = cpu_to_be32(clr);
158 + ret = ath9k_wmi_cmd(priv->wmi, WMI_REG_RMW_CMDID,
159 + (u8 *) &buf, sizeof(buf),
160 + (u8 *) &buf_ret, sizeof(buf_ret),
162 + if (unlikely(ret)) {
163 + ath_dbg(common, WMI, "REGISTER RMW FAILED:(0x%04x, %d)\n",
169 +static u32 ath9k_reg_rmw(void *hw_priv, u32 reg_offset, u32 set, u32 clr)
171 + struct ath_hw *ah = (struct ath_hw *) hw_priv;
172 + struct ath_common *common = ath9k_hw_common(ah);
173 + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
175 + if (test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags)) {
178 + val = REG_READ(ah, reg_offset);
181 + REG_WRITE(ah, reg_offset, val);
186 + if (atomic_read(&priv->wmi->m_rmw_cnt))
187 + ath9k_reg_rmw_buffer(hw_priv, reg_offset, set, clr);
189 + ath9k_reg_rmw_single(hw_priv, reg_offset, set, clr);
194 static void ath_usb_read_cachesize(struct ath_common *common, int *csz)
196 *csz = L1_CACHE_BYTES >> 2;
197 @@ -501,6 +623,8 @@ static int ath9k_init_priv(struct ath9k_
198 ah->reg_ops.write = ath9k_regwrite;
199 ah->reg_ops.enable_write_buffer = ath9k_enable_regwrite_buffer;
200 ah->reg_ops.write_flush = ath9k_regwrite_flush;
201 + ah->reg_ops.enable_rmw_buffer = ath9k_enable_rmw_buffer;
202 + ah->reg_ops.rmw_flush = ath9k_reg_rmw_flush;
203 ah->reg_ops.rmw = ath9k_reg_rmw;
206 @@ -686,6 +810,12 @@ static int ath9k_init_firmware_version(s
210 + if (priv->fw_version_major == 1 && priv->fw_version_minor < 4)
211 + set_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags);
213 + dev_info(priv->dev, "FW RMW support: %s\n",
214 + test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags) ? "Off" : "On");
219 --- a/drivers/net/wireless/ath/ath9k/hw.h
220 +++ b/drivers/net/wireless/ath/ath9k/hw.h
222 (_ah)->reg_ops.write_flush((_ah)); \
225 +#define ENABLE_REG_RMW_BUFFER(_ah) \
227 + if ((_ah)->reg_ops.enable_rmw_buffer) \
228 + (_ah)->reg_ops.enable_rmw_buffer((_ah)); \
231 +#define REG_RMW_BUFFER_FLUSH(_ah) \
233 + if ((_ah)->reg_ops.rmw_flush) \
234 + (_ah)->reg_ops.rmw_flush((_ah)); \
237 #define PR_EEP(_s, _val) \
239 len += scnprintf(buf + len, size - len, "%20s : %10d\n",\
240 --- a/drivers/net/wireless/ath/ath9k/wmi.c
241 +++ b/drivers/net/wireless/ath/ath9k/wmi.c
242 @@ -61,6 +61,8 @@ static const char *wmi_cmd_to_name(enum
243 return "WMI_REG_READ_CMDID";
244 case WMI_REG_WRITE_CMDID:
245 return "WMI_REG_WRITE_CMDID";
246 + case WMI_REG_RMW_CMDID:
247 + return "WMI_REG_RMW_CMDID";
248 case WMI_RC_STATE_CHANGE_CMDID:
249 return "WMI_RC_STATE_CHANGE_CMDID";
250 case WMI_RC_RATE_UPDATE_CMDID:
251 @@ -101,6 +103,7 @@ struct wmi *ath9k_init_wmi(struct ath9k_
252 spin_lock_init(&wmi->event_lock);
253 mutex_init(&wmi->op_mutex);
254 mutex_init(&wmi->multi_write_mutex);
255 + mutex_init(&wmi->multi_rmw_mutex);
256 init_completion(&wmi->cmd_wait);
257 INIT_LIST_HEAD(&wmi->pending_tx_events);
258 tasklet_init(&wmi->wmi_event_tasklet, ath9k_wmi_event_tasklet,
259 --- a/drivers/net/wireless/ath/ath9k/wmi.h
260 +++ b/drivers/net/wireless/ath/ath9k/wmi.h
261 @@ -112,6 +112,7 @@ enum wmi_cmd_id {
264 WMI_BITRATE_MASK_CMDID,
269 @@ -125,12 +126,19 @@ enum wmi_event_id {
272 #define MAX_CMD_NUMBER 62
273 +#define MAX_RMW_CMD_NUMBER 15
275 struct register_write {
280 +struct register_rmw {
286 struct ath9k_htc_tx_event {
288 struct __wmi_event_txstatus txs;
289 @@ -156,10 +164,18 @@ struct wmi {
293 + /* multi write section */
295 struct register_write multi_write[MAX_CMD_NUMBER];
297 struct mutex multi_write_mutex;
299 + /* multi rmw section */
300 + atomic_t m_rmw_cnt;
301 + struct register_rmw multi_rmw[MAX_RMW_CMD_NUMBER];
303 + struct mutex multi_rmw_mutex;
307 struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv);