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>
This commit is contained in:
Oleksij Rempel 2015-03-22 19:29:46 +01:00 committed by Kalle Valo
parent e480e13448
commit 8badb50cfa
6 changed files with 175 additions and 6 deletions

View File

@ -131,6 +131,9 @@ struct ath_ops {
void (*enable_write_buffer)(void *); void (*enable_write_buffer)(void *);
void (*write_flush) (void *); void (*write_flush) (void *);
u32 (*rmw)(void *, u32 reg_offset, u32 set, u32 clr); u32 (*rmw)(void *, u32 reg_offset, u32 set, u32 clr);
void (*enable_rmw_buffer)(void *);
void (*rmw_flush) (void *);
}; };
struct ath_common; struct ath_common;

View 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_BT_SCAN BIT(4)
#define OP_TSF_RESET BIT(6) #define OP_TSF_RESET BIT(6)
enum htc_op_flags {
HTC_FWFLAG_NO_RMW,
};
struct ath9k_htc_priv { struct ath9k_htc_priv {
struct device *dev; struct device *dev;
struct ieee80211_hw *hw; struct ieee80211_hw *hw;
@ -482,6 +486,7 @@ struct ath9k_htc_priv {
bool reconfig_beacon; bool reconfig_beacon;
unsigned int rxfilter; unsigned int rxfilter;
unsigned long op_flags; unsigned long op_flags;
unsigned long fw_flags;
struct ath9k_hw_cal_data caldata; struct ath9k_hw_cal_data caldata;
struct ath_spec_scan_priv spec_priv; struct ath_spec_scan_priv spec_priv;

View File

@ -376,15 +376,137 @@ static void ath9k_regwrite_flush(void *hw_priv)
mutex_unlock(&priv->wmi->multi_write_mutex); mutex_unlock(&priv->wmi->multi_write_mutex);
} }
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)
{
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;
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) static u32 ath9k_reg_rmw(void *hw_priv, u32 reg_offset, u32 set, u32 clr)
{ {
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;
val = ath9k_regread(hw_priv, reg_offset); if (test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags)) {
val &= ~clr; u32 val;
val |= set;
ath9k_regwrite(hw_priv, val, reg_offset); val = REG_READ(ah, reg_offset);
return val; 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) static void ath_usb_read_cachesize(struct ath_common *common, int *csz)
@ -501,6 +623,8 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv,
ah->reg_ops.write = ath9k_regwrite; ah->reg_ops.write = ath9k_regwrite;
ah->reg_ops.enable_write_buffer = ath9k_enable_regwrite_buffer; ah->reg_ops.enable_write_buffer = ath9k_enable_regwrite_buffer;
ah->reg_ops.write_flush = ath9k_regwrite_flush; 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; ah->reg_ops.rmw = ath9k_reg_rmw;
priv->ah = ah; priv->ah = ah;
@ -686,6 +810,12 @@ static int ath9k_init_firmware_version(struct ath9k_htc_priv *priv)
return -EINVAL; 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; return 0;
} }

View File

@ -100,6 +100,18 @@
(_ah)->reg_ops.write_flush((_ah)); \ (_ah)->reg_ops.write_flush((_ah)); \
} while (0) } 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) \ #define PR_EEP(_s, _val) \
do { \ do { \
len += scnprintf(buf + len, size - len, "%20s : %10d\n",\ len += scnprintf(buf + len, size - len, "%20s : %10d\n",\

View File

@ -61,6 +61,8 @@ static const char *wmi_cmd_to_name(enum wmi_cmd_id wmi_cmd)
return "WMI_REG_READ_CMDID"; return "WMI_REG_READ_CMDID";
case WMI_REG_WRITE_CMDID: case WMI_REG_WRITE_CMDID:
return "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: case WMI_RC_STATE_CHANGE_CMDID:
return "WMI_RC_STATE_CHANGE_CMDID"; return "WMI_RC_STATE_CHANGE_CMDID";
case WMI_RC_RATE_UPDATE_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); spin_lock_init(&wmi->event_lock);
mutex_init(&wmi->op_mutex); mutex_init(&wmi->op_mutex);
mutex_init(&wmi->multi_write_mutex); mutex_init(&wmi->multi_write_mutex);
mutex_init(&wmi->multi_rmw_mutex);
init_completion(&wmi->cmd_wait); init_completion(&wmi->cmd_wait);
INIT_LIST_HEAD(&wmi->pending_tx_events); INIT_LIST_HEAD(&wmi->pending_tx_events);
tasklet_init(&wmi->wmi_event_tasklet, ath9k_wmi_event_tasklet, tasklet_init(&wmi->wmi_event_tasklet, ath9k_wmi_event_tasklet,

View File

@ -112,6 +112,7 @@ enum wmi_cmd_id {
WMI_TX_STATS_CMDID, WMI_TX_STATS_CMDID,
WMI_RX_STATS_CMDID, WMI_RX_STATS_CMDID,
WMI_BITRATE_MASK_CMDID, WMI_BITRATE_MASK_CMDID,
WMI_REG_RMW_CMDID,
}; };
enum wmi_event_id { enum wmi_event_id {
@ -125,12 +126,19 @@ enum wmi_event_id {
}; };
#define MAX_CMD_NUMBER 62 #define MAX_CMD_NUMBER 62
#define MAX_RMW_CMD_NUMBER 15
struct register_write { struct register_write {
__be32 reg; __be32 reg;
__be32 val; __be32 val;
}; };
struct register_rmw {
__be32 reg;
__be32 set;
__be32 clr;
} __packed;
struct ath9k_htc_tx_event { struct ath9k_htc_tx_event {
int count; int count;
struct __wmi_event_txstatus txs; struct __wmi_event_txstatus txs;
@ -156,10 +164,18 @@ struct wmi {
spinlock_t wmi_lock; spinlock_t wmi_lock;
/* multi write section */
atomic_t mwrite_cnt; atomic_t mwrite_cnt;
struct register_write multi_write[MAX_CMD_NUMBER]; struct register_write multi_write[MAX_CMD_NUMBER];
u32 multi_write_idx; u32 multi_write_idx;
struct mutex multi_write_mutex; 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); struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv);