Ejemplo n.º 1
0
/*
 * This is the master bt coex timer which runs for every
 * 45ms, bt traffic will be given priority during 55% of this
 * period while wlan gets remaining 45%
 */
static void ath_btcoex_period_timer(unsigned long data)
{
	struct ath_softc *sc = (struct ath_softc *) data;
	struct ath_hw *ah = sc->sc_ah;
	struct ath_btcoex *btcoex = &sc->btcoex;
	struct ath_mci_profile *mci = &btcoex->mci;
	u32 timer_period;
	bool is_btscan;
	unsigned long flags;

	spin_lock_irqsave(&sc->sc_pm_lock, flags);
	if (sc->sc_ah->power_mode == ATH9K_PM_NETWORK_SLEEP) {
		spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
		goto skip_hw_wakeup;
	}
	spin_unlock_irqrestore(&sc->sc_pm_lock, flags);

	ath9k_ps_wakeup(sc);
	if (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI))
		ath_detect_bt_priority(sc);
	is_btscan = test_bit(BT_OP_SCAN, &btcoex->op_flags);

	btcoex->bt_wait_time += btcoex->btcoex_period;
	if (btcoex->bt_wait_time > ATH_BTCOEX_RX_WAIT_TIME) {
		if (ar9003_mci_state(ah, MCI_STATE_NEED_FTP_STOMP) &&
		    (mci->num_pan || mci->num_other_acl))
			ah->btcoex_hw.mci.stomp_ftp =
				(sc->rx.num_pkts < ATH_BTCOEX_STOMP_FTP_THRESH);
		else
			ah->btcoex_hw.mci.stomp_ftp = false;
		btcoex->bt_wait_time = 0;
		sc->rx.num_pkts = 0;
	}

	spin_lock_bh(&btcoex->btcoex_lock);

	ath9k_hw_btcoex_bt_stomp(ah, is_btscan ? ATH_BTCOEX_STOMP_ALL :
			      btcoex->bt_stomp_type);

	ath9k_hw_btcoex_enable(ah);
	spin_unlock_bh(&btcoex->btcoex_lock);

	if (btcoex->btcoex_period != btcoex->btcoex_no_stomp) {
		if (btcoex->hw_timer_enabled)
			ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer);

		timer_period = is_btscan ? btcoex->btscan_no_stomp :
					   btcoex->btcoex_no_stomp;
		ath9k_gen_timer_start(ah, btcoex->no_stomp_timer, timer_period,
				      timer_period * 10);
		btcoex->hw_timer_enabled = true;
	}

	ath9k_ps_restore(sc);
skip_hw_wakeup:
	timer_period = btcoex->btcoex_period;
	mod_timer(&btcoex->period_timer, jiffies + msecs_to_jiffies(timer_period));
}
Ejemplo n.º 2
0
void ar9003_mci_set_full_sleep(struct ath_hw *ah)
{
	struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;

	if (ar9003_mci_state(ah, MCI_STATE_ENABLE) &&
	    (mci->bt_state != MCI_BT_SLEEP) &&
	    !mci->halted_bt_gpm) {
		ar9003_mci_send_coex_halt_bt_gpm(ah, true, true);
	}

	mci->ready = false;
}
Ejemplo n.º 3
0
static void ath_mci_ftp_adjust(struct ath_softc *sc)
{
	struct ath_btcoex *btcoex = &sc->btcoex;
	struct ath_mci_profile *mci = &btcoex->mci;
	struct ath_hw *ah = sc->sc_ah;

	if (btcoex->bt_wait_time > ATH_BTCOEX_RX_WAIT_TIME) {
		if (ar9003_mci_state(ah, MCI_STATE_NEED_FTP_STOMP) &&
		    (mci->num_pan || mci->num_other_acl))
			ah->btcoex_hw.mci.stomp_ftp =
				(sc->rx.num_pkts < ATH_BTCOEX_STOMP_FTP_THRESH);
		else
			ah->btcoex_hw.mci.stomp_ftp = false;
		btcoex->bt_wait_time = 0;
		sc->rx.num_pkts = 0;
	}
}
Ejemplo n.º 4
0
static void ar9003_mci_sync_bt_state(struct ath_hw *ah)
{
	struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;
	u32 cur_bt_state;

	cur_bt_state = ar9003_mci_state(ah, MCI_STATE_REMOTE_SLEEP);

	if (mci->bt_state != cur_bt_state)
		mci->bt_state = cur_bt_state;

	if (mci->bt_state != MCI_BT_SLEEP) {

		ar9003_mci_send_coex_version_query(ah, true);
		ar9003_mci_send_coex_wlan_channels(ah, true);

		if (mci->unhalt_bt_gpm == true)
			ar9003_mci_send_coex_halt_bt_gpm(ah, false, true);
	}
}
Ejemplo n.º 5
0
int ar9003_mci_reset(struct ath_hw *ah, bool en_int, bool is_2g,
		     bool is_full_sleep)
{
	struct ath_common *common = ath9k_hw_common(ah);
	struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;
	u32 regval, i;

	ath_dbg(common, MCI, "MCI Reset (full_sleep = %d, is_2g = %d)\n",
		is_full_sleep, is_2g);

	if (REG_READ(ah, AR_BTCOEX_CTRL) == 0xdeadbeef) {
		ath_err(common, "BTCOEX control register is dead\n");
		return -EINVAL;
	}

	/* Program MCI DMA related registers */
	REG_WRITE(ah, AR_MCI_GPM_0, mci->gpm_addr);
	REG_WRITE(ah, AR_MCI_GPM_1, mci->gpm_len);
	REG_WRITE(ah, AR_MCI_SCHD_TABLE_0, mci->sched_addr);

	/*
	* To avoid MCI state machine be affected by incoming remote MCI msgs,
	* MCI mode will be enabled later, right before reset the MCI TX and RX.
	*/
	if (AR_SREV_9565(ah)) {
		u8 ant = MS(mci->config, ATH_MCI_CONFIG_ANT_ARCH);

		if (ant == ATH_MCI_ANT_ARCH_1_ANT_PA_LNA_SHARED)
			ar9003_mci_set_btcoex_ctrl_9565_1ANT(ah);
		else
			ar9003_mci_set_btcoex_ctrl_9565_2ANT(ah);
	} else {
		ar9003_mci_set_btcoex_ctrl_9462(ah);
	}

	if (is_2g && !(mci->config & ATH_MCI_CONFIG_DISABLE_OSLA))
		ar9003_mci_osla_setup(ah, true);
	else
		ar9003_mci_osla_setup(ah, false);

	REG_SET_BIT(ah, AR_PHY_GLB_CONTROL,
		    AR_BTCOEX_CTRL_SPDT_ENABLE);
	REG_RMW_FIELD(ah, AR_BTCOEX_CTRL3,
		      AR_BTCOEX_CTRL3_CONT_INFO_TIMEOUT, 20);

	REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, AR_BTCOEX_CTRL2_RX_DEWEIGHT, 0);
	REG_RMW_FIELD(ah, AR_PCU_MISC, AR_PCU_BT_ANT_PREVENT_RX, 0);

	/* Set the time out to 3.125ms (5 BT slots) */
	REG_RMW_FIELD(ah, AR_BTCOEX_WL_LNA, AR_BTCOEX_WL_LNA_TIMEOUT, 0x3D090);

	/* concurrent tx priority */
	if (mci->config & ATH_MCI_CONFIG_CONCUR_TX) {
		REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2,
			      AR_BTCOEX_CTRL2_DESC_BASED_TXPWR_ENABLE, 0);
		REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2,
			      AR_BTCOEX_CTRL2_TXPWR_THRESH, 0x7f);
		REG_RMW_FIELD(ah, AR_BTCOEX_CTRL,
			      AR_BTCOEX_CTRL_REDUCE_TXPWR, 0);
		for (i = 0; i < 8; i++)
			REG_WRITE(ah, AR_BTCOEX_MAX_TXPWR(i), 0x7f7f7f7f);
	}

	regval = MS(mci->config, ATH_MCI_CONFIG_CLK_DIV);
	REG_RMW_FIELD(ah, AR_MCI_TX_CTRL, AR_MCI_TX_CTRL_CLK_DIV, regval);
	REG_SET_BIT(ah, AR_BTCOEX_CTRL, AR_BTCOEX_CTRL_MCI_MODE_EN);

	/* Resetting the Rx and Tx paths of MCI */
	regval = REG_READ(ah, AR_MCI_COMMAND2);
	regval |= SM(1, AR_MCI_COMMAND2_RESET_TX);
	REG_WRITE(ah, AR_MCI_COMMAND2, regval);

	udelay(1);

	regval &= ~SM(1, AR_MCI_COMMAND2_RESET_TX);
	REG_WRITE(ah, AR_MCI_COMMAND2, regval);

	if (is_full_sleep) {
		ar9003_mci_mute_bt(ah);
		udelay(100);
	}

	/* Check pending GPM msg before MCI Reset Rx */
	ar9003_mci_check_gpm_offset(ah);

	regval |= SM(1, AR_MCI_COMMAND2_RESET_RX);
	REG_WRITE(ah, AR_MCI_COMMAND2, regval);
	udelay(1);
	regval &= ~SM(1, AR_MCI_COMMAND2_RESET_RX);
	REG_WRITE(ah, AR_MCI_COMMAND2, regval);

	/* Init GPM offset after MCI Reset Rx */
	ar9003_mci_state(ah, MCI_STATE_INIT_GPM_OFFSET);

	REG_WRITE(ah, AR_MCI_MSG_ATTRIBUTES_TABLE,
		  (SM(0xe801, AR_MCI_MSG_ATTRIBUTES_TABLE_INVALID_HDR) |
		   SM(0x0000, AR_MCI_MSG_ATTRIBUTES_TABLE_CHECKSUM)));

	if (MCI_ANT_ARCH_PA_LNA_SHARED(mci))
		REG_CLR_BIT(ah, AR_MCI_TX_CTRL,
			    AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE);
	else
		REG_SET_BIT(ah, AR_MCI_TX_CTRL,
			    AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE);

	ar9003_mci_observation_set_up(ah);

	mci->ready = true;
	ar9003_mci_prep_interface(ah);
	ar9003_mci_stat_setup(ah);

	if (en_int)
		ar9003_mci_enable_interrupt(ah);

	if (ath9k_hw_is_aic_enabled(ah))
		ar9003_aic_start_normal(ah);

	return 0;
}
void ar9003_mci_reset(struct ath_hw *ah, bool en_int, bool is_2g,
		      bool is_full_sleep)
{
	struct ath_common *common = ath9k_hw_common(ah);
	struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;
	u32 regval;

	ath_dbg(common, MCI, "MCI Reset (full_sleep = %d, is_2g = %d)\n",
		is_full_sleep, is_2g);

	if (!mci->gpm_addr && !mci->sched_addr) {
		ath_dbg(common, MCI,
			"MCI GPM and schedule buffers are not allocated\n");
		return;
	}

	if (REG_READ(ah, AR_BTCOEX_CTRL) == 0xdeadbeef) {
		ath_dbg(common, MCI, "BTCOEX control register is dead\n");
		return;
	}

	
	REG_WRITE(ah, AR_MCI_GPM_0, mci->gpm_addr);
	REG_WRITE(ah, AR_MCI_GPM_1, mci->gpm_len);
	REG_WRITE(ah, AR_MCI_SCHD_TABLE_0, mci->sched_addr);


	regval = SM(1, AR_BTCOEX_CTRL_AR9462_MODE) |
		 SM(1, AR_BTCOEX_CTRL_WBTIMER_EN) |
		 SM(1, AR_BTCOEX_CTRL_PA_SHARED) |
		 SM(1, AR_BTCOEX_CTRL_LNA_SHARED) |
		 SM(2, AR_BTCOEX_CTRL_NUM_ANTENNAS) |
		 SM(3, AR_BTCOEX_CTRL_RX_CHAIN_MASK) |
		 SM(0, AR_BTCOEX_CTRL_1_CHAIN_ACK) |
		 SM(0, AR_BTCOEX_CTRL_1_CHAIN_BCN) |
		 SM(0, AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN);

	REG_WRITE(ah, AR_BTCOEX_CTRL, regval);

	if (is_2g && !(mci->config & ATH_MCI_CONFIG_DISABLE_OSLA))
		ar9003_mci_osla_setup(ah, true);
	else
		ar9003_mci_osla_setup(ah, false);

	REG_SET_BIT(ah, AR_PHY_GLB_CONTROL,
		    AR_BTCOEX_CTRL_SPDT_ENABLE);
	REG_RMW_FIELD(ah, AR_BTCOEX_CTRL3,
		      AR_BTCOEX_CTRL3_CONT_INFO_TIMEOUT, 20);

	REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, AR_BTCOEX_CTRL2_RX_DEWEIGHT, 1);
	REG_RMW_FIELD(ah, AR_PCU_MISC, AR_PCU_BT_ANT_PREVENT_RX, 0);

	regval = MS(mci->config, ATH_MCI_CONFIG_CLK_DIV);
	REG_RMW_FIELD(ah, AR_MCI_TX_CTRL, AR_MCI_TX_CTRL_CLK_DIV, regval);
	REG_SET_BIT(ah, AR_BTCOEX_CTRL, AR_BTCOEX_CTRL_MCI_MODE_EN);

	
	regval = REG_READ(ah, AR_MCI_COMMAND2);
	regval |= SM(1, AR_MCI_COMMAND2_RESET_TX);
	REG_WRITE(ah, AR_MCI_COMMAND2, regval);

	udelay(1);

	regval &= ~SM(1, AR_MCI_COMMAND2_RESET_TX);
	REG_WRITE(ah, AR_MCI_COMMAND2, regval);

	if (is_full_sleep) {
		ar9003_mci_mute_bt(ah);
		udelay(100);
	}

	regval |= SM(1, AR_MCI_COMMAND2_RESET_RX);
	REG_WRITE(ah, AR_MCI_COMMAND2, regval);
	udelay(1);
	regval &= ~SM(1, AR_MCI_COMMAND2_RESET_RX);
	REG_WRITE(ah, AR_MCI_COMMAND2, regval);

	ar9003_mci_state(ah, MCI_STATE_INIT_GPM_OFFSET, NULL);

	REG_WRITE(ah, AR_MCI_MSG_ATTRIBUTES_TABLE,
		  (SM(0xe801, AR_MCI_MSG_ATTRIBUTES_TABLE_INVALID_HDR) |
		   SM(0x0000, AR_MCI_MSG_ATTRIBUTES_TABLE_CHECKSUM)));

	REG_CLR_BIT(ah, AR_MCI_TX_CTRL,
		    AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE);

	ar9003_mci_observation_set_up(ah);

	mci->ready = true;
	ar9003_mci_prep_interface(ah);

	if (en_int)
		ar9003_mci_enable_interrupt(ah);
}
static u32 ar9003_mci_wait_for_gpm(struct ath_hw *ah, u8 gpm_type,
				   u8 gpm_opcode, int time_out)
{
	struct ath_common *common = ath9k_hw_common(ah);
	struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;
	u32 *p_gpm = NULL, mismatch = 0, more_data;
	u32 offset;
	u8 recv_type = 0, recv_opcode = 0;
	bool b_is_bt_cal_done = (gpm_type == MCI_GPM_BT_CAL_DONE);

	more_data = time_out ? MCI_GPM_NOMORE : MCI_GPM_MORE;

	while (time_out > 0) {
		if (p_gpm) {
			MCI_GPM_RECYCLE(p_gpm);
			p_gpm = NULL;
		}

		if (more_data != MCI_GPM_MORE)
			time_out = ar9003_mci_wait_for_interrupt(ah,
					AR_MCI_INTERRUPT_RX_MSG_RAW,
					AR_MCI_INTERRUPT_RX_MSG_GPM,
					time_out);

		if (!time_out)
			break;

		offset = ar9003_mci_state(ah, MCI_STATE_NEXT_GPM_OFFSET,
					  &more_data);

		if (offset == MCI_GPM_INVALID)
			continue;

		p_gpm = (u32 *) (mci->gpm_buf + offset);
		recv_type = MCI_GPM_TYPE(p_gpm);
		recv_opcode = MCI_GPM_OPCODE(p_gpm);

		if (MCI_GPM_IS_CAL_TYPE(recv_type)) {
			if (recv_type == gpm_type) {
				if ((gpm_type == MCI_GPM_BT_CAL_DONE) &&
				    !b_is_bt_cal_done) {
					gpm_type = MCI_GPM_BT_CAL_GRANT;
					continue;
				}
				break;
			}
		} else if ((recv_type == gpm_type) && (recv_opcode == gpm_opcode)) {
			break;
		}


		if ((gpm_type == MCI_GPM_BT_CAL_GRANT) &&
		    (recv_type == MCI_GPM_BT_CAL_REQ)) {

			u32 payload[4] = {0, 0, 0, 0};

			gpm_type = MCI_GPM_BT_CAL_DONE;
			MCI_GPM_SET_CAL_TYPE(payload,
					     MCI_GPM_WLAN_CAL_GRANT);
			ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16,
						false, false);
			continue;
		} else {
			ath_dbg(common, MCI, "MCI GPM subtype not match 0x%x\n",
				*(p_gpm + 1));
			mismatch++;
			ar9003_mci_process_gpm_extra(ah, recv_type,
						     recv_opcode, p_gpm);
		}
	}

	if (p_gpm) {
		MCI_GPM_RECYCLE(p_gpm);
		p_gpm = NULL;
	}

	if (time_out <= 0)
		time_out = 0;

	while (more_data == MCI_GPM_MORE) {
		offset = ar9003_mci_state(ah, MCI_STATE_NEXT_GPM_OFFSET,
					  &more_data);
		if (offset == MCI_GPM_INVALID)
			break;

		p_gpm = (u32 *) (mci->gpm_buf + offset);
		recv_type = MCI_GPM_TYPE(p_gpm);
		recv_opcode = MCI_GPM_OPCODE(p_gpm);

		if (!MCI_GPM_IS_CAL_TYPE(recv_type))
			ar9003_mci_process_gpm_extra(ah, recv_type,
						     recv_opcode, p_gpm);

		MCI_GPM_RECYCLE(p_gpm);
	}

	return time_out;
}