diff options
author | Oleksij Rempel <linux@rempel-privat.de> | 2015-03-22 14:29:46 -0400 |
---|---|---|
committer | Kalle Valo <kvalo@codeaurora.org> | 2015-03-30 04:31:18 -0400 |
commit | 8badb50cfab6d433622dbfd5a90b6adf27333107 (patch) | |
tree | b8b6569c7390aaa1a8fb693c5e37a4935d0175bb /drivers/net/wireless/ath/ath9k | |
parent | e480e1344873b6a715d06a003e603d86a11a4033 (diff) |
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>
Diffstat (limited to 'drivers/net/wireless/ath/ath9k')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/htc.h | 5 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/htc_drv_init.c | 142 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/hw.h | 12 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/wmi.c | 3 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/wmi.h | 16 |
5 files changed, 172 insertions, 6 deletions
diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h index 300d3671d0ef..e82a0d4ce23f 100644 --- 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(struct ath9k_htc_priv *priv) | |||
444 | #define OP_BT_SCAN BIT(4) | 444 | #define OP_BT_SCAN BIT(4) |
445 | #define OP_TSF_RESET BIT(6) | 445 | #define OP_TSF_RESET BIT(6) |
446 | 446 | ||
447 | enum htc_op_flags { | ||
448 | HTC_FWFLAG_NO_RMW, | ||
449 | }; | ||
450 | |||
447 | struct ath9k_htc_priv { | 451 | struct ath9k_htc_priv { |
448 | struct device *dev; | 452 | struct device *dev; |
449 | struct ieee80211_hw *hw; | 453 | struct ieee80211_hw *hw; |
@@ -482,6 +486,7 @@ struct ath9k_htc_priv { | |||
482 | bool reconfig_beacon; | 486 | bool reconfig_beacon; |
483 | unsigned int rxfilter; | 487 | unsigned int rxfilter; |
484 | unsigned long op_flags; | 488 | unsigned long op_flags; |
489 | unsigned long fw_flags; | ||
485 | 490 | ||
486 | struct ath9k_hw_cal_data caldata; | 491 | struct ath9k_hw_cal_data caldata; |
487 | struct ath_spec_scan_priv spec_priv; | 492 | struct ath_spec_scan_priv spec_priv; |
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index fd229409f676..d7beefe60683 100644 --- 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 *hw_priv) | |||
376 | mutex_unlock(&priv->wmi->multi_write_mutex); | 376 | mutex_unlock(&priv->wmi->multi_write_mutex); |
377 | } | 377 | } |
378 | 378 | ||
379 | static u32 ath9k_reg_rmw(void *hw_priv, u32 reg_offset, u32 set, u32 clr) | 379 | static void ath9k_reg_rmw_buffer(void *hw_priv, |
380 | u32 reg_offset, u32 set, u32 clr) | ||
381 | { | ||
382 | struct ath_hw *ah = (struct ath_hw *) hw_priv; | ||
383 | struct ath_common *common = ath9k_hw_common(ah); | ||
384 | struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; | ||
385 | u32 rsp_status; | ||
386 | int r; | ||
387 | |||
388 | mutex_lock(&priv->wmi->multi_rmw_mutex); | ||
389 | |||
390 | /* Store the register/value */ | ||
391 | priv->wmi->multi_rmw[priv->wmi->multi_rmw_idx].reg = | ||
392 | cpu_to_be32(reg_offset); | ||
393 | priv->wmi->multi_rmw[priv->wmi->multi_rmw_idx].set = | ||
394 | cpu_to_be32(set); | ||
395 | priv->wmi->multi_rmw[priv->wmi->multi_rmw_idx].clr = | ||
396 | cpu_to_be32(clr); | ||
397 | |||
398 | priv->wmi->multi_rmw_idx++; | ||
399 | |||
400 | /* If the buffer is full, send it out. */ | ||
401 | if (priv->wmi->multi_rmw_idx == MAX_RMW_CMD_NUMBER) { | ||
402 | r = ath9k_wmi_cmd(priv->wmi, WMI_REG_RMW_CMDID, | ||
403 | (u8 *) &priv->wmi->multi_rmw, | ||
404 | sizeof(struct register_write) * priv->wmi->multi_rmw_idx, | ||
405 | (u8 *) &rsp_status, sizeof(rsp_status), | ||
406 | 100); | ||
407 | if (unlikely(r)) { | ||
408 | ath_dbg(common, WMI, | ||
409 | "REGISTER RMW FAILED, multi len: %d\n", | ||
410 | priv->wmi->multi_rmw_idx); | ||
411 | } | ||
412 | priv->wmi->multi_rmw_idx = 0; | ||
413 | } | ||
414 | |||
415 | mutex_unlock(&priv->wmi->multi_rmw_mutex); | ||
416 | } | ||
417 | |||
418 | static void ath9k_reg_rmw_flush(void *hw_priv) | ||
380 | { | 419 | { |
381 | u32 val; | 420 | struct ath_hw *ah = (struct ath_hw *) hw_priv; |
421 | struct ath_common *common = ath9k_hw_common(ah); | ||
422 | struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; | ||
423 | u32 rsp_status; | ||
424 | int r; | ||
425 | |||
426 | if (test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags)) | ||
427 | return; | ||
428 | |||
429 | atomic_dec(&priv->wmi->m_rmw_cnt); | ||
382 | 430 | ||
383 | val = ath9k_regread(hw_priv, reg_offset); | 431 | mutex_lock(&priv->wmi->multi_rmw_mutex); |
384 | val &= ~clr; | 432 | |
385 | val |= set; | 433 | if (priv->wmi->multi_rmw_idx) { |
386 | ath9k_regwrite(hw_priv, val, reg_offset); | 434 | r = ath9k_wmi_cmd(priv->wmi, WMI_REG_RMW_CMDID, |
435 | (u8 *) &priv->wmi->multi_rmw, | ||
436 | sizeof(struct register_rmw) * priv->wmi->multi_rmw_idx, | ||
437 | (u8 *) &rsp_status, sizeof(rsp_status), | ||
438 | 100); | ||
439 | if (unlikely(r)) { | ||
440 | ath_dbg(common, WMI, | ||
441 | "REGISTER RMW FAILED, multi len: %d\n", | ||
442 | priv->wmi->multi_rmw_idx); | ||
443 | } | ||
444 | priv->wmi->multi_rmw_idx = 0; | ||
445 | } | ||
446 | |||
447 | mutex_unlock(&priv->wmi->multi_rmw_mutex); | ||
448 | } | ||
449 | |||
450 | static void ath9k_enable_rmw_buffer(void *hw_priv) | ||
451 | { | ||
452 | struct ath_hw *ah = (struct ath_hw *) hw_priv; | ||
453 | struct ath_common *common = ath9k_hw_common(ah); | ||
454 | struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; | ||
455 | |||
456 | if (test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags)) | ||
457 | return; | ||
458 | |||
459 | atomic_inc(&priv->wmi->m_rmw_cnt); | ||
460 | } | ||
461 | |||
462 | static u32 ath9k_reg_rmw_single(void *hw_priv, | ||
463 | u32 reg_offset, u32 set, u32 clr) | ||
464 | { | ||
465 | struct ath_hw *ah = (struct ath_hw *) hw_priv; | ||
466 | struct ath_common *common = ath9k_hw_common(ah); | ||
467 | struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; | ||
468 | struct register_rmw buf, buf_ret; | ||
469 | int ret; | ||
470 | u32 val = 0; | ||
471 | |||
472 | buf.reg = cpu_to_be32(reg_offset); | ||
473 | buf.set = cpu_to_be32(set); | ||
474 | buf.clr = cpu_to_be32(clr); | ||
475 | |||
476 | ret = ath9k_wmi_cmd(priv->wmi, WMI_REG_RMW_CMDID, | ||
477 | (u8 *) &buf, sizeof(buf), | ||
478 | (u8 *) &buf_ret, sizeof(buf_ret), | ||
479 | 100); | ||
480 | if (unlikely(ret)) { | ||
481 | ath_dbg(common, WMI, "REGISTER RMW FAILED:(0x%04x, %d)\n", | ||
482 | reg_offset, ret); | ||
483 | } | ||
387 | return val; | 484 | return val; |
388 | } | 485 | } |
389 | 486 | ||
487 | static u32 ath9k_reg_rmw(void *hw_priv, u32 reg_offset, u32 set, u32 clr) | ||
488 | { | ||
489 | struct ath_hw *ah = (struct ath_hw *) hw_priv; | ||
490 | struct ath_common *common = ath9k_hw_common(ah); | ||
491 | struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; | ||
492 | |||
493 | if (test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags)) { | ||
494 | u32 val; | ||
495 | |||
496 | val = REG_READ(ah, reg_offset); | ||
497 | val &= ~clr; | ||
498 | val |= set; | ||
499 | REG_WRITE(ah, reg_offset, val); | ||
500 | |||
501 | return 0; | ||
502 | } | ||
503 | |||
504 | if (atomic_read(&priv->wmi->m_rmw_cnt)) | ||
505 | ath9k_reg_rmw_buffer(hw_priv, reg_offset, set, clr); | ||
506 | else | ||
507 | ath9k_reg_rmw_single(hw_priv, reg_offset, set, clr); | ||
508 | |||
509 | return 0; | ||
510 | } | ||
511 | |||
390 | static void ath_usb_read_cachesize(struct ath_common *common, int *csz) | 512 | static void ath_usb_read_cachesize(struct ath_common *common, int *csz) |
391 | { | 513 | { |
392 | *csz = L1_CACHE_BYTES >> 2; | 514 | *csz = L1_CACHE_BYTES >> 2; |
@@ -501,6 +623,8 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv, | |||
501 | ah->reg_ops.write = ath9k_regwrite; | 623 | ah->reg_ops.write = ath9k_regwrite; |
502 | ah->reg_ops.enable_write_buffer = ath9k_enable_regwrite_buffer; | 624 | ah->reg_ops.enable_write_buffer = ath9k_enable_regwrite_buffer; |
503 | ah->reg_ops.write_flush = ath9k_regwrite_flush; | 625 | ah->reg_ops.write_flush = ath9k_regwrite_flush; |
626 | ah->reg_ops.enable_rmw_buffer = ath9k_enable_rmw_buffer; | ||
627 | ah->reg_ops.rmw_flush = ath9k_reg_rmw_flush; | ||
504 | ah->reg_ops.rmw = ath9k_reg_rmw; | 628 | ah->reg_ops.rmw = ath9k_reg_rmw; |
505 | priv->ah = ah; | 629 | priv->ah = ah; |
506 | 630 | ||
@@ -686,6 +810,12 @@ static int ath9k_init_firmware_version(struct ath9k_htc_priv *priv) | |||
686 | return -EINVAL; | 810 | return -EINVAL; |
687 | } | 811 | } |
688 | 812 | ||
813 | if (priv->fw_version_major == 1 && priv->fw_version_minor < 4) | ||
814 | set_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags); | ||
815 | |||
816 | dev_info(priv->dev, "FW RMW support: %s\n", | ||
817 | test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags) ? "Off" : "On"); | ||
818 | |||
689 | return 0; | 819 | return 0; |
690 | } | 820 | } |
691 | 821 | ||
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index aaec9451de49..7b51d8e3696f 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h | |||
@@ -100,6 +100,18 @@ | |||
100 | (_ah)->reg_ops.write_flush((_ah)); \ | 100 | (_ah)->reg_ops.write_flush((_ah)); \ |
101 | } while (0) | 101 | } while (0) |
102 | 102 | ||
103 | #define ENABLE_REG_RMW_BUFFER(_ah) \ | ||
104 | do { \ | ||
105 | if ((_ah)->reg_ops.enable_rmw_buffer) \ | ||
106 | (_ah)->reg_ops.enable_rmw_buffer((_ah)); \ | ||
107 | } while (0) | ||
108 | |||
109 | #define REG_RMW_BUFFER_FLUSH(_ah) \ | ||
110 | do { \ | ||
111 | if ((_ah)->reg_ops.rmw_flush) \ | ||
112 | (_ah)->reg_ops.rmw_flush((_ah)); \ | ||
113 | } while (0) | ||
114 | |||
103 | #define PR_EEP(_s, _val) \ | 115 | #define PR_EEP(_s, _val) \ |
104 | do { \ | 116 | do { \ |
105 | len += scnprintf(buf + len, size - len, "%20s : %10d\n",\ | 117 | len += scnprintf(buf + len, size - len, "%20s : %10d\n",\ |
diff --git a/drivers/net/wireless/ath/ath9k/wmi.c b/drivers/net/wireless/ath/ath9k/wmi.c index 65c8894c5f81..67a2f8c88829 100644 --- 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 wmi_cmd_id wmi_cmd) | |||
61 | return "WMI_REG_READ_CMDID"; | 61 | return "WMI_REG_READ_CMDID"; |
62 | case WMI_REG_WRITE_CMDID: | 62 | case WMI_REG_WRITE_CMDID: |
63 | return "WMI_REG_WRITE_CMDID"; | 63 | return "WMI_REG_WRITE_CMDID"; |
64 | case WMI_REG_RMW_CMDID: | ||
65 | return "WMI_REG_RMW_CMDID"; | ||
64 | case WMI_RC_STATE_CHANGE_CMDID: | 66 | case WMI_RC_STATE_CHANGE_CMDID: |
65 | return "WMI_RC_STATE_CHANGE_CMDID"; | 67 | return "WMI_RC_STATE_CHANGE_CMDID"; |
66 | case WMI_RC_RATE_UPDATE_CMDID: | 68 | case WMI_RC_RATE_UPDATE_CMDID: |
@@ -101,6 +103,7 @@ struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv) | |||
101 | spin_lock_init(&wmi->event_lock); | 103 | spin_lock_init(&wmi->event_lock); |
102 | mutex_init(&wmi->op_mutex); | 104 | mutex_init(&wmi->op_mutex); |
103 | mutex_init(&wmi->multi_write_mutex); | 105 | mutex_init(&wmi->multi_write_mutex); |
106 | mutex_init(&wmi->multi_rmw_mutex); | ||
104 | init_completion(&wmi->cmd_wait); | 107 | init_completion(&wmi->cmd_wait); |
105 | INIT_LIST_HEAD(&wmi->pending_tx_events); | 108 | INIT_LIST_HEAD(&wmi->pending_tx_events); |
106 | tasklet_init(&wmi->wmi_event_tasklet, ath9k_wmi_event_tasklet, | 109 | tasklet_init(&wmi->wmi_event_tasklet, ath9k_wmi_event_tasklet, |
diff --git a/drivers/net/wireless/ath/ath9k/wmi.h b/drivers/net/wireless/ath/ath9k/wmi.h index 0db37f230018..aa84a335289a 100644 --- a/drivers/net/wireless/ath/ath9k/wmi.h +++ b/drivers/net/wireless/ath/ath9k/wmi.h | |||
@@ -112,6 +112,7 @@ enum wmi_cmd_id { | |||
112 | WMI_TX_STATS_CMDID, | 112 | WMI_TX_STATS_CMDID, |
113 | WMI_RX_STATS_CMDID, | 113 | WMI_RX_STATS_CMDID, |
114 | WMI_BITRATE_MASK_CMDID, | 114 | WMI_BITRATE_MASK_CMDID, |
115 | WMI_REG_RMW_CMDID, | ||
115 | }; | 116 | }; |
116 | 117 | ||
117 | enum wmi_event_id { | 118 | enum wmi_event_id { |
@@ -125,12 +126,19 @@ enum wmi_event_id { | |||
125 | }; | 126 | }; |
126 | 127 | ||
127 | #define MAX_CMD_NUMBER 62 | 128 | #define MAX_CMD_NUMBER 62 |
129 | #define MAX_RMW_CMD_NUMBER 15 | ||
128 | 130 | ||
129 | struct register_write { | 131 | struct register_write { |
130 | __be32 reg; | 132 | __be32 reg; |
131 | __be32 val; | 133 | __be32 val; |
132 | }; | 134 | }; |
133 | 135 | ||
136 | struct register_rmw { | ||
137 | __be32 reg; | ||
138 | __be32 set; | ||
139 | __be32 clr; | ||
140 | } __packed; | ||
141 | |||
134 | struct ath9k_htc_tx_event { | 142 | struct ath9k_htc_tx_event { |
135 | int count; | 143 | int count; |
136 | struct __wmi_event_txstatus txs; | 144 | struct __wmi_event_txstatus txs; |
@@ -156,10 +164,18 @@ struct wmi { | |||
156 | 164 | ||
157 | spinlock_t wmi_lock; | 165 | spinlock_t wmi_lock; |
158 | 166 | ||
167 | /* multi write section */ | ||
159 | atomic_t mwrite_cnt; | 168 | atomic_t mwrite_cnt; |
160 | struct register_write multi_write[MAX_CMD_NUMBER]; | 169 | struct register_write multi_write[MAX_CMD_NUMBER]; |
161 | u32 multi_write_idx; | 170 | u32 multi_write_idx; |
162 | struct mutex multi_write_mutex; | 171 | struct mutex multi_write_mutex; |
172 | |||
173 | /* multi rmw section */ | ||
174 | atomic_t m_rmw_cnt; | ||
175 | struct register_rmw multi_rmw[MAX_RMW_CMD_NUMBER]; | ||
176 | u32 multi_rmw_idx; | ||
177 | struct mutex multi_rmw_mutex; | ||
178 | |||
163 | }; | 179 | }; |
164 | 180 | ||
165 | struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv); | 181 | struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv); |