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: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
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 = {
bt_cmd.valid |= IWL6000G2B_BT_ALL_VALID_MSK;
}
- memcpy(bt_cmd.bt3_lookup_table, iwl6000g2b_def_3w_lookup,
- sizeof(iwl6000g2b_def_3w_lookup));
+ 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");
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 };
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)
* To be safe, simply mask out any chains that we know
* are not on the device.
*/
- active_chains &= priv->hw_params.valid_rx_ant;
+ 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;
for (i = 0; i < NUM_RX_CHAINS; i++) {
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]);
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;
(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
*/
/* 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);
}
/*
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++;
/* 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++;
/* 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++;
/* 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++;
/* 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) {
index++;
repeat_rate--;
- if (priv)
- valid_tx_ant = priv->hw_params.valid_tx_ant;
+ 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) {
rate_flags |= RATE_MCS_CCK_MSK;
/* Set up antennas */
- priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant,
+ 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);
MODULE_LICENSE("GPL");
MODULE_ALIAS("iwl4965");
+static int iwlagn_ant_coupling;
+
/**
* iwl_commit_rxon - commit staging_rxon to hardware
*
iwl_send_beacon_cmd(priv);
}
+static void iwl_bg_bt_full_concurrency(struct work_struct *work)
+{
+ struct iwl_priv *priv =
+ container_of(work, struct iwl_priv, bt_full_concurrency);
+
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ return;
+
+ /* dont send host command if rf-kill is on */
+ if (!iwl_is_ready_rf(priv))
+ return;
+
+ IWL_DEBUG_INFO(priv, "BT coex in %s mode\n",
+ priv->bt_full_concurrent ?
+ "full concurrency" : "3-wire");
+
+ /*
+ * LQ & RXON updated cmds must be sent before BT Config cmd
+ * to avoid 3-wire collisions
+ */
+ if (priv->cfg->ops->hcmd->set_rxon_chain)
+ priv->cfg->ops->hcmd->set_rxon_chain(priv);
+ iwlcore_commit_rxon(priv);
+
+ priv->cfg->ops->hcmd->send_bt_config(priv);
+}
+
/**
* iwl_bg_statistics_periodic - Timer callback to queue statistics
*
/* reset BT coex data */
priv->bt_traffic_load = 0;
priv->bt_sco_active = false;
+ priv->bt_full_concurrent = false;
+ priv->bt_ci_compliance = 0;
/* Unblock any waiting calls */
wake_up_interruptible_all(&priv->wait_command_queue);
return;
if (test_and_clear_bit(STATUS_FW_ERROR, &priv->status)) {
- bool bt_sco;
+ bool bt_sco, bt_full_concurrent;
+ u8 bt_ci_compliance;
u8 bt_load;
mutex_lock(&priv->mutex);
* command.
*/
bt_sco = priv->bt_sco_active;
+ bt_full_concurrent = priv->bt_full_concurrent;
+ bt_ci_compliance = priv->bt_ci_compliance;
bt_load = priv->bt_traffic_load;
__iwl_down(priv);
priv->bt_sco_active = bt_sco;
+ priv->bt_full_concurrent = bt_full_concurrent;
+ priv->bt_ci_compliance = bt_ci_compliance;
priv->bt_traffic_load = bt_load;
mutex_unlock(&priv->mutex);
INIT_WORK(&priv->beacon_update, iwl_bg_beacon_update);
INIT_WORK(&priv->run_time_calib_work, iwl_bg_run_time_calib_work);
INIT_WORK(&priv->tx_flush, iwl_bg_tx_flush);
+ INIT_WORK(&priv->bt_full_concurrency, iwl_bg_bt_full_concurrency);
INIT_DELAYED_WORK(&priv->init_alive_start, iwl_bg_init_alive_start);
INIT_DELAYED_WORK(&priv->alive_start, iwl_bg_alive_start);
cancel_delayed_work(&priv->alive_start);
cancel_work_sync(&priv->run_time_calib_work);
cancel_work_sync(&priv->beacon_update);
+ cancel_work_sync(&priv->bt_full_concurrency);
del_timer_sync(&priv->statistics_periodic);
del_timer_sync(&priv->ucode_trace);
}
priv->pci_dev = pdev;
priv->inta_mask = CSR_INI_SET_MASK;
+ /* is antenna coupling more than 35dB ? */
+ priv->bt_ant_couple_ok =
+ (iwlagn_ant_coupling > IWL_BT_ANTENNA_COUPLING_THRESHOLD) ?
+ true : false;
+
if (iwl_alloc_traffic_mem(priv))
IWL_ERR(priv, "Not enough memory to generate traffic log\n");
S_IRUGO);
MODULE_PARM_DESC(ucode_alternative,
"specify ucode alternative to use from ucode file");
+
+module_param_named(antenna_coupling, iwlagn_ant_coupling, int, S_IRUGO);
+MODULE_PARM_DESC(antenna_coupling,
+ "specify antenna coupling in dB (defualt: 0 dB)");
*/
static int iwl_get_active_rx_chain_count(struct iwl_priv *priv)
{
+ if (priv->cfg->advanced_bt_coexist && priv->bt_full_concurrent) {
+ /* operated as 1x1 in full concurrency mode */
+ return IWL_NUM_RX_CHAINS_SINGLE;
+ }
/* # of Rx chains to use when expecting MIMO. */
if (is_single_rx_stream(priv))
return IWL_NUM_RX_CHAINS_SINGLE;
* Before first association, we assume all antennas are connected.
* Just after first association, iwl_chain_noise_calibration()
* checks which antennas actually *are* connected. */
- if (priv->chain_noise_data.active_chains)
+ if (priv->chain_noise_data.active_chains)
active_chains = priv->chain_noise_data.active_chains;
else
active_chains = priv->hw_params.valid_rx_ant;
+ if (priv->cfg->advanced_bt_coexist && priv->bt_full_concurrent) {
+ /* operated as 1x1 in full concurrency mode */
+ active_chains = first_antenna(active_chains);
+ }
+
rx_chain = active_chains << RXON_RX_CHAIN_VALID_POS;
/* How many receivers should we use? */
}
extern bool bt_coex_active;
+extern bool bt_siso_mode;
#endif /* __iwl_core_h__ */
#define IWL_LONG_MONITORING_PERIOD (5000)
#define IWL_ONE_HUNDRED_MSECS (100)
+/* BT Antenna Coupling Threshold (dB) */
+#define IWL_BT_ANTENNA_COUPLING_THRESHOLD (35)
+
enum iwl_reset {
IWL_RF_RESET = 0,
IWL_FW_RESET,
u8 bt_traffic_load, notif_bt_traffic_load;
bool bt_sco_active;
+ bool bt_full_concurrent;
+ bool bt_ant_couple_ok;
+ u8 bt_ci_compliance;
struct work_struct bt_traffic_change_work;
struct iwl_hw_params hw_params;
struct work_struct ct_exit;
struct work_struct start_internal_scan;
struct work_struct tx_flush;
+ struct work_struct bt_full_concurrency;
struct tasklet_struct irq_tasklet;