Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8badb50c authored by Oleksij Rempel's avatar Oleksij Rempel Committed by Kalle Valo
Browse files

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: default avatarOleksij Rempel <linux@rempel-privat.de>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent e480e134
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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;
+5 −0
Original line number Diff line number Diff line
@@ -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;
+136 −6
Original line number Diff line number Diff line
@@ -376,15 +376,137 @@ static void ath9k_regwrite_flush(void *hw_priv)
	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)
{
	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 = ath9k_regread(hw_priv, reg_offset);
		val = REG_READ(ah, reg_offset);
		val &= ~clr;
		val |= set;
	ath9k_regwrite(hw_priv, val, reg_offset);
	return val;
		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)
@@ -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;
}

+12 −0
Original line number Diff line number Diff line
@@ -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",\
+3 −0
Original line number Diff line number Diff line
@@ -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,
Loading