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

Commit bee008b7 authored by Wey-Yi Guy's avatar Wey-Yi Guy Committed by John W. Linville
Browse files

iwlwifi: add bt full concurrency support



Adding the bluetooth full concurrency support for WiFi/BT combo devices.

Driver should configure uCode to operate in "full concurrency" mode (via
LUT) if both conditions are met:
 - Antenna Coupling is more than 35dB
 - WiFi Channel Inhibition Request is hornored by BT Core

Currently, there is no antenna coupling information provided by uCode;
use module parameter to specified the antenna coupling in dB.

When in "full concurrency" mode, driver need to download different LUT
to uCode while sending bt configuration command; also, driver need to
configure the device operate in 1x1 while in full concurrency mode.

Signed-off-by: default avatarWey-Yi Guy <wey-yi.w.guy@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent bd6e2d57
Loading
Loading
Loading
Loading
+30 −4
Original line number Diff line number Diff line
@@ -201,6 +201,21 @@ static const __le32 iwl6000g2b_def_3w_lookup[12] = {
	cpu_to_le32(0xf0004000),
};

static const __le32 iwl6000g2b_concurrent_lookup[12] = {
	cpu_to_le32(0xaaaaaaaa),
	cpu_to_le32(0xaaaaaaaa),
	cpu_to_le32(0xaaaaaaaa),
	cpu_to_le32(0xaaaaaaaa),
	cpu_to_le32(0xaaaaaaaa),
	cpu_to_le32(0xaaaaaaaa),
	cpu_to_le32(0xaaaaaaaa),
	cpu_to_le32(0xaaaaaaaa),
	cpu_to_le32(0x00000000),
	cpu_to_le32(0x00000000),
	cpu_to_le32(0x00000000),
	cpu_to_le32(0x00000000),
};

static void iwl6000g2b_send_bt_config(struct iwl_priv *priv)
{
	struct iwl6000g2b_bt_cmd bt_cmd = {
@@ -233,11 +248,17 @@ static void iwl6000g2b_send_bt_config(struct iwl_priv *priv)
		bt_cmd.valid |= IWL6000G2B_BT_ALL_VALID_MSK;
	}

	if (priv->bt_full_concurrent)
		memcpy(bt_cmd.bt3_lookup_table, iwl6000g2b_concurrent_lookup,
			sizeof(iwl6000g2b_concurrent_lookup));
	else
		memcpy(bt_cmd.bt3_lookup_table, iwl6000g2b_def_3w_lookup,
			sizeof(iwl6000g2b_def_3w_lookup));

	IWL_DEBUG_INFO(priv, "BT coex %s\n",
		       bt_cmd.flags ? "active" : "disabled");
	IWL_DEBUG_INFO(priv, "BT coex %s in %s mode\n",
		       bt_cmd.flags ? "active" : "disabled",
		       priv->bt_full_concurrent ?
		       "full concurrency" : "3-wire");

	if (iwl_send_cmd_pdu(priv, REPLY_BT_CONFIG, sizeof(bt_cmd), &bt_cmd))
		IWL_ERR(priv, "failed to send BT Coex Config\n");
@@ -435,6 +456,7 @@ static void iwl6000g2b_bt_traffic_change_work(struct work_struct *work)
static void iwl6000g2b_bt_coex_profile_notif(struct iwl_priv *priv,
					     struct iwl_rx_mem_buffer *rxb)
{
	unsigned long flags;
	struct iwl_rx_packet *pkt = rxb_addr(rxb);
	struct iwl_bt_coex_profile_notif *coex = &pkt->u.bt_coex_profile_notif;
	struct iwl6000g2b_bt_sco_cmd sco_cmd = { .flags = 0 };
@@ -466,6 +488,10 @@ static void iwl6000g2b_bt_coex_profile_notif(struct iwl_priv *priv,
		iwl_send_cmd_pdu_async(priv, REPLY_BT_COEX_SCO,
				       sizeof(sco_cmd), &sco_cmd, NULL);
	}

	spin_lock_irqsave(&priv->lock, flags);
	priv->bt_ci_compliance = coex->bt_ci_compliance;
	spin_unlock_irqrestore(&priv->lock, flags);
}

void iwl6000g2b_rx_handler_setup(struct iwl_priv *priv)
+5 −1
Original line number Diff line number Diff line
@@ -914,6 +914,10 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv, void *stat_resp)
	 * To be safe, simply mask out any chains that we know
	 * are not on the device.
	 */
	if (priv->cfg->advanced_bt_coexist && priv->bt_full_concurrent) {
		/* operated as 1x1 in full concurrency mode */
		active_chains &= first_antenna(priv->hw_params.valid_rx_ant);
	} else
		active_chains &= priv->hw_params.valid_rx_ant;

	num_tx_chains = 0;
+11 −0
Original line number Diff line number Diff line
@@ -1333,6 +1333,12 @@ void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
	if (priv->cfg->scan_tx_antennas[band])
		scan_tx_antennas = priv->cfg->scan_tx_antennas[band];

	if (priv->cfg->advanced_bt_coexist && priv->bt_full_concurrent) {
		/* operated as 1x1 in full concurrency mode */
		scan_tx_antennas =
			first_antenna(priv->cfg->scan_tx_antennas[band]);
	}

	priv->scan_tx_ant[band] = iwl_toggle_tx_ant(priv, priv->scan_tx_ant[band],
						    scan_tx_antennas);
	rate_flags |= iwl_ant_idx_to_flags(priv->scan_tx_ant[band]);
@@ -1351,6 +1357,11 @@ void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)

		rx_ant = first_antenna(active_chains);
	}
	if (priv->cfg->advanced_bt_coexist && priv->bt_full_concurrent) {
		/* operated as 1x1 in full concurrency mode */
		rx_ant = first_antenna(rx_ant);
	}

	/* MIMO is not used here, but value is required */
	rx_chain |= priv->hw_params.valid_rx_ant << RXON_RX_CHAIN_VALID_POS;
	rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS;
+67 −3
Original line number Diff line number Diff line
@@ -758,6 +758,32 @@ static bool table_type_matches(struct iwl_scale_tbl_info *a,
		(a->is_SGI == b->is_SGI);
}

static void rs_bt_update_lq(struct iwl_priv *priv,
			       struct iwl_lq_sta *lq_sta)
{
	struct iwl_scale_tbl_info *tbl;
	bool full_concurrent;
	unsigned long flags;

	spin_lock_irqsave(&priv->lock, flags);
	if (priv->bt_ci_compliance && priv->bt_ant_couple_ok)
		full_concurrent = true;
	else
		full_concurrent = false;
	spin_unlock_irqrestore(&priv->lock, flags);

	if (priv->bt_full_concurrent != full_concurrent) {
		priv->bt_full_concurrent = full_concurrent;

		/* Update uCode's rate table. */
		tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
		rs_fill_link_cmd(priv, lq_sta, tbl->current_rate);
		iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC, false);

		queue_work(priv->workqueue, &priv->bt_full_concurrency);
	}
}

/*
 * mac80211 sends us Tx status
 */
@@ -940,6 +966,10 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
	/* See if there's a better rate or modulation mode to try. */
	if (sta && sta->supp_rates[sband->band])
		rs_rate_scale_perform(priv, skb, sta, lq_sta);

	/* Is there a need to switch between full concurrency and 3-wire? */
	if (priv->bt_ant_couple_ok)
		rs_bt_update_lq(priv, lq_sta);
}

/*
@@ -1325,6 +1355,15 @@ static int rs_move_legacy_other(struct iwl_priv *priv,
	else if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE &&
		   tbl->action > IWL_LEGACY_SWITCH_SISO)
		tbl->action = IWL_LEGACY_SWITCH_SISO;

	/* configure as 1x1 if bt full concurrency */
	if (priv->bt_full_concurrent) {
		if (!iwl_ht_enabled(priv))
			tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
		else if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2)
			tbl->action = IWL_LEGACY_SWITCH_SISO;
	}

	start_action = tbl->action;
	for (; ;) {
		lq_sta->action_counter++;
@@ -1484,6 +1523,12 @@ static int rs_move_siso_to_other(struct iwl_priv *priv,
		/* stay in SISO */
		tbl->action = IWL_SISO_SWITCH_ANTENNA1;
	}

	/* configure as 1x1 if bt full concurrency */
	if (priv->bt_full_concurrent &&
	    tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2)
		tbl->action = IWL_SISO_SWITCH_ANTENNA1;

	start_action = tbl->action;
	for (;;) {
		lq_sta->action_counter++;
@@ -1645,6 +1690,13 @@ static int rs_move_mimo2_to_other(struct iwl_priv *priv,
		/* switch in SISO */
		tbl->action = IWL_MIMO2_SWITCH_SISO_A;
	}

	/* configure as 1x1 if bt full concurrency */
	if (priv->bt_full_concurrent &&
	    (tbl->action < IWL_MIMO2_SWITCH_SISO_A ||
	     tbl->action > IWL_MIMO2_SWITCH_SISO_C))
		tbl->action = IWL_MIMO2_SWITCH_SISO_A;

	start_action = tbl->action;
	for (;;) {
		lq_sta->action_counter++;
@@ -1810,6 +1862,13 @@ static int rs_move_mimo3_to_other(struct iwl_priv *priv,
		/* switch in SISO */
		tbl->action = IWL_MIMO3_SWITCH_SISO_A;
	}

	/* configure as 1x1 if bt full concurrency */
	if (priv->bt_full_concurrent &&
	    (tbl->action < IWL_MIMO3_SWITCH_SISO_A ||
	     tbl->action > IWL_MIMO3_SWITCH_SISO_C))
		tbl->action = IWL_MIMO3_SWITCH_SISO_A;

	start_action = tbl->action;
	for (;;) {
		lq_sta->action_counter++;
@@ -2741,7 +2800,8 @@ static void rs_fill_link_cmd(struct iwl_priv *priv,
	/* Fill 1st table entry (index 0) */
	lq_cmd->rs_table[index].rate_n_flags = cpu_to_le32(new_rate);

	if (num_of_ant(tbl_type.ant_type) == 1) {
	if (num_of_ant(tbl_type.ant_type) == 1 ||
	    (priv && priv->bt_full_concurrent)) {
		lq_cmd->general_params.single_stream_ant_msk =
						tbl_type.ant_type;
	} else if (num_of_ant(tbl_type.ant_type) == 2) {
@@ -2752,8 +2812,12 @@ static void rs_fill_link_cmd(struct iwl_priv *priv,
	index++;
	repeat_rate--;

	if (priv)
	if (priv) {
		if (priv->bt_full_concurrent)
			valid_tx_ant = ANT_A;
		else
			valid_tx_ant = priv->hw_params.valid_tx_ant;
	}

	/* Fill rest of rate table */
	while (index < LINK_QUAL_MAX_RETRY_NUM) {
+6 −1
Original line number Diff line number Diff line
@@ -461,6 +461,11 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv,
		rate_flags |= RATE_MCS_CCK_MSK;

	/* Set up antennas */
	 if (priv->cfg->advanced_bt_coexist && priv->bt_full_concurrent) {
		/* operated as 1x1 in full concurrency mode */
		priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant,
				first_antenna(priv->hw_params.valid_tx_ant));
	} else
		priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant,
					      priv->hw_params.valid_tx_ant);
	rate_flags |= iwl_ant_idx_to_flags(priv->mgmt_tx_ant);
Loading