diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/mci.c')
| -rw-r--r-- | drivers/net/wireless/ath/ath9k/mci.c | 246 | 
1 files changed, 137 insertions, 109 deletions
diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index 29fe52d6997..fb536e7e661 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -20,7 +20,7 @@  #include "ath9k.h"  #include "mci.h" -static const u8 ath_mci_duty_cycle[] = { 0, 50, 60, 70, 80, 85, 90, 95, 98 }; +static const u8 ath_mci_duty_cycle[] = { 55, 50, 60, 70, 80, 85, 90, 95, 98 };  static struct ath_mci_profile_info*  ath_mci_find_profile(struct ath_mci_profile *mci, @@ -28,11 +28,14 @@ ath_mci_find_profile(struct ath_mci_profile *mci,  {  	struct ath_mci_profile_info *entry; +	if (list_empty(&mci->info)) +		return NULL; +  	list_for_each_entry(entry, &mci->info, list) {  		if (entry->conn_handle == info->conn_handle) -			break; +			return entry;  	} -	return entry; +	return NULL;  }  static bool ath_mci_add_profile(struct ath_common *common, @@ -49,31 +52,21 @@ static bool ath_mci_add_profile(struct ath_common *common,  	    (info->type != MCI_GPM_COEX_PROFILE_VOICE))  		return false; -	entry = ath_mci_find_profile(mci, info); +	entry = kzalloc(sizeof(*entry), GFP_ATOMIC); +	if (!entry) +		return false; -	if (entry) { -		memcpy(entry, info, 10); -	} else { -		entry = kzalloc(sizeof(*entry), GFP_KERNEL); -		if (!entry) -			return false; - -		memcpy(entry, info, 10); -		INC_PROF(mci, info); -		list_add_tail(&info->list, &mci->info); -	} +	memcpy(entry, info, 10); +	INC_PROF(mci, info); +	list_add_tail(&entry->list, &mci->info);  	return true;  }  static void ath_mci_del_profile(struct ath_common *common,  				struct ath_mci_profile *mci, -				struct ath_mci_profile_info *info) +				struct ath_mci_profile_info *entry)  { -	struct ath_mci_profile_info *entry; - -	entry = ath_mci_find_profile(mci, info); -  	if (!entry)  		return; @@ -86,12 +79,16 @@ void ath_mci_flush_profile(struct ath_mci_profile *mci)  {  	struct ath_mci_profile_info *info, *tinfo; +	mci->aggr_limit = 0; + +	if (list_empty(&mci->info)) +		return; +  	list_for_each_entry_safe(info, tinfo, &mci->info, list) {  		list_del(&info->list);  		DEC_PROF(mci, info);  		kfree(info);  	} -	mci->aggr_limit = 0;  }  static void ath_mci_adjust_aggr_limit(struct ath_btcoex *btcoex) @@ -116,42 +113,60 @@ static void ath_mci_update_scheme(struct ath_softc *sc)  	struct ath_common *common = ath9k_hw_common(sc->sc_ah);  	struct ath_btcoex *btcoex = &sc->btcoex;  	struct ath_mci_profile *mci = &btcoex->mci; +	struct ath9k_hw_mci *mci_hw = &sc->sc_ah->btcoex_hw.mci;  	struct ath_mci_profile_info *info;  	u32 num_profile = NUM_PROF(mci); +	if (mci_hw->config & ATH_MCI_CONFIG_DISABLE_TUNING) +		goto skip_tuning; + +	btcoex->duty_cycle = ath_mci_duty_cycle[num_profile]; +  	if (num_profile == 1) {  		info = list_first_entry(&mci->info,  					struct ath_mci_profile_info,  					list); -		if (mci->num_sco && info->T == 12) { -			mci->aggr_limit = 8; +		if (mci->num_sco) { +			if (info->T == 12) +				mci->aggr_limit = 8; +			else if (info->T == 6) { +				mci->aggr_limit = 6; +				btcoex->duty_cycle = 30; +			}  			ath_dbg(common, MCI, -				"Single SCO, aggregation limit 2 ms\n"); -		} else if ((info->type == MCI_GPM_COEX_PROFILE_BNEP) && -			   !info->master) { -			btcoex->btcoex_period = 60; +				"Single SCO, aggregation limit %d 1/4 ms\n", +				mci->aggr_limit); +		} else if (mci->num_pan || mci->num_other_acl) { +			/* +			 * For single PAN/FTP profile, allocate 35% for BT +			 * to improve WLAN throughput. +			 */ +			btcoex->duty_cycle = 35; +			btcoex->btcoex_period = 53;  			ath_dbg(common, MCI, -				"Single slave PAN/FTP, bt period 60 ms\n"); -		} else if ((info->type == MCI_GPM_COEX_PROFILE_HID) && -			 (info->T > 0 && info->T < 50) && -			 (info->A > 1 || info->W > 1)) { +				"Single PAN/FTP bt period %d ms dutycycle %d\n", +				btcoex->duty_cycle, btcoex->btcoex_period); +		} else if (mci->num_hid) {  			btcoex->duty_cycle = 30; -			mci->aggr_limit = 8; +			mci->aggr_limit = 6;  			ath_dbg(common, MCI,  				"Multiple attempt/timeout single HID " -				"aggregation limit 2 ms dutycycle 30%%\n"); +				"aggregation limit 1.5 ms dutycycle 30%%\n");  		} -	} else if ((num_profile == 2) && (mci->num_hid == 2)) { -		btcoex->duty_cycle = 30; -		mci->aggr_limit = 8; -		ath_dbg(common, MCI, -			"Two HIDs aggregation limit 2 ms dutycycle 30%%\n"); -	} else if (num_profile > 3) { +	} else if (num_profile == 2) { +		if (mci->num_hid == 2) +			btcoex->duty_cycle = 30;  		mci->aggr_limit = 6;  		ath_dbg(common, MCI, -			"Three or more profiles aggregation limit 1.5 ms\n"); +			"Two BT profiles aggr limit 1.5 ms dutycycle %d%%\n", +			btcoex->duty_cycle); +	} else if (num_profile >= 3) { +		mci->aggr_limit = 4; +		ath_dbg(common, MCI, +			"Three or more profiles aggregation limit 1 ms\n");  	} +skip_tuning:  	if (IS_CHAN_2GHZ(sc->sc_ah->curchan)) {  		if (IS_CHAN_HT(sc->sc_ah->curchan))  			ath_mci_adjust_aggr_limit(btcoex); @@ -159,18 +174,17 @@ static void ath_mci_update_scheme(struct ath_softc *sc)  			btcoex->btcoex_period >>= 1;  	} -	ath9k_hw_btcoex_disable(sc->sc_ah);  	ath9k_btcoex_timer_pause(sc); +	ath9k_hw_btcoex_disable(sc->sc_ah);  	if (IS_CHAN_5GHZ(sc->sc_ah->curchan))  		return; -	btcoex->duty_cycle += (mci->num_bdr ? ATH_MCI_MAX_DUTY_CYCLE : 0); +	btcoex->duty_cycle += (mci->num_bdr ? ATH_MCI_BDR_DUTY_CYCLE : 0);  	if (btcoex->duty_cycle > ATH_MCI_MAX_DUTY_CYCLE)  		btcoex->duty_cycle = ATH_MCI_MAX_DUTY_CYCLE; -	btcoex->btcoex_period *= 1000; -	btcoex->btcoex_no_stomp =  btcoex->btcoex_period * +	btcoex->btcoex_no_stomp =  btcoex->btcoex_period * 1000 *  		(100 - btcoex->duty_cycle) / 100;  	ath9k_hw_btcoex_enable(sc->sc_ah); @@ -181,20 +195,16 @@ static void ath_mci_cal_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload)  {  	struct ath_hw *ah = sc->sc_ah;  	struct ath_common *common = ath9k_hw_common(ah); +	struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci;  	u32 payload[4] = {0, 0, 0, 0};  	switch (opcode) {  	case MCI_GPM_BT_CAL_REQ: -		if (ar9003_mci_state(ah, MCI_STATE_BT, NULL) == MCI_BT_AWAKE) { -			ar9003_mci_state(ah, MCI_STATE_SET_BT_CAL_START, NULL); -			ieee80211_queue_work(sc->hw, &sc->hw_reset_work); -		} else { -			ath_dbg(common, MCI, "MCI State mismatch: %d\n", -				ar9003_mci_state(ah, MCI_STATE_BT, NULL)); +		if (mci_hw->bt_state == MCI_BT_AWAKE) { +			ar9003_mci_state(ah, MCI_STATE_SET_BT_CAL_START); +			ath9k_queue_reset(sc, RESET_TYPE_MCI);  		} -		break; -	case MCI_GPM_BT_CAL_DONE: -		ar9003_mci_state(ah, MCI_STATE_BT, NULL); +		ath_dbg(common, MCI, "MCI State : %d\n", mci_hw->bt_state);  		break;  	case MCI_GPM_BT_CAL_GRANT:  		MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_DONE); @@ -207,32 +217,55 @@ static void ath_mci_cal_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload)  	}  } +static void ath9k_mci_work(struct work_struct *work) +{ +	struct ath_softc *sc = container_of(work, struct ath_softc, mci_work); + +	ath_mci_update_scheme(sc); +} +  static void ath_mci_process_profile(struct ath_softc *sc,  				    struct ath_mci_profile_info *info)  {  	struct ath_common *common = ath9k_hw_common(sc->sc_ah);  	struct ath_btcoex *btcoex = &sc->btcoex;  	struct ath_mci_profile *mci = &btcoex->mci; +	struct ath_mci_profile_info *entry = NULL; + +	entry = ath_mci_find_profile(mci, info); +	if (entry) { +		/* +		 * Two MCI interrupts are generated while connecting to +		 * headset and A2DP profile, but only one MCI interrupt +		 * is generated with last added profile type while disconnecting +		 * both profiles. +		 * So while adding second profile type decrement +		 * the first one. +		 */ +		if (entry->type != info->type) { +			DEC_PROF(mci, entry); +			INC_PROF(mci, info); +		} +		memcpy(entry, info, 10); +	}  	if (info->start) { -		if (!ath_mci_add_profile(common, mci, info)) +		if (!entry && !ath_mci_add_profile(common, mci, info))  			return;  	} else -		ath_mci_del_profile(common, mci, info); +		ath_mci_del_profile(common, mci, entry);  	btcoex->btcoex_period = ATH_MCI_DEF_BT_PERIOD;  	mci->aggr_limit = mci->num_sco ? 6 : 0; -	if (NUM_PROF(mci)) { +	btcoex->duty_cycle = ath_mci_duty_cycle[NUM_PROF(mci)]; +	if (NUM_PROF(mci))  		btcoex->bt_stomp_type = ATH_BTCOEX_STOMP_LOW; -		btcoex->duty_cycle = ath_mci_duty_cycle[NUM_PROF(mci)]; -	} else { +	else  		btcoex->bt_stomp_type = mci->num_mgmt ? ATH_BTCOEX_STOMP_ALL :  							ATH_BTCOEX_STOMP_LOW; -		btcoex->duty_cycle = ATH_BTCOEX_DEF_DUTY_CYCLE; -	} -	ath_mci_update_scheme(sc); +	ieee80211_queue_work(sc->hw, &sc->mci_work);  }  static void ath_mci_process_status(struct ath_softc *sc, @@ -247,8 +280,6 @@ static void ath_mci_process_status(struct ath_softc *sc,  	if (status->is_link)  		return; -	memset(&info, 0, sizeof(struct ath_mci_profile_info)); -  	info.conn_handle = status->conn_handle;  	if (ath_mci_find_profile(mci, &info))  		return; @@ -268,7 +299,7 @@ static void ath_mci_process_status(struct ath_softc *sc,  	} while (++i < ATH_MCI_MAX_PROFILE);  	if (old_num_mgmt != mci->num_mgmt) -		ath_mci_update_scheme(sc); +		ieee80211_queue_work(sc->hw, &sc->mci_work);  }  static void ath_mci_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload) @@ -277,25 +308,20 @@ static void ath_mci_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload)  	struct ath_mci_profile_info profile_info;  	struct ath_mci_profile_status profile_status;  	struct ath_common *common = ath9k_hw_common(sc->sc_ah); -	u32 version; -	u8 major; -	u8 minor; +	u8 major, minor;  	u32 seq_num;  	switch (opcode) {  	case MCI_GPM_COEX_VERSION_QUERY: -		version = ar9003_mci_state(ah, MCI_STATE_SEND_WLAN_COEX_VERSION, -					   NULL); +		ar9003_mci_state(ah, MCI_STATE_SEND_WLAN_COEX_VERSION);  		break;  	case MCI_GPM_COEX_VERSION_RESPONSE:  		major = *(rx_payload + MCI_GPM_COEX_B_MAJOR_VERSION);  		minor = *(rx_payload + MCI_GPM_COEX_B_MINOR_VERSION); -		version = (major << 8) + minor; -		version = ar9003_mci_state(ah, MCI_STATE_SET_BT_COEX_VERSION, -					   &version); +		ar9003_mci_set_bt_version(ah, major, minor);  		break;  	case MCI_GPM_COEX_STATUS_QUERY: -		ar9003_mci_state(ah, MCI_STATE_SEND_WLAN_CHANNELS, NULL); +		ar9003_mci_send_wlan_channels(ah);  		break;  	case MCI_GPM_COEX_BT_PROFILE_INFO:  		memcpy(&profile_info, @@ -322,7 +348,7 @@ static void ath_mci_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload)  		seq_num = *((u32 *)(rx_payload + 12));  		ath_dbg(common, MCI, -			"BT_Status_Update: is_link=%d, linkId=%d, state=%d, SEQ=%d\n", +			"BT_Status_Update: is_link=%d, linkId=%d, state=%d, SEQ=%u\n",  			profile_status.is_link, profile_status.conn_handle,  			profile_status.is_critical, seq_num); @@ -362,6 +388,7 @@ int ath_mci_setup(struct ath_softc *sc)  			 mci->gpm_buf.bf_addr, (mci->gpm_buf.bf_len >> 4),  			 mci->sched_buf.bf_paddr); +	INIT_WORK(&sc->mci_work, ath9k_mci_work);  	ath_dbg(common, MCI, "MCI Initialized\n");  	return 0; @@ -389,6 +416,7 @@ void ath_mci_intr(struct ath_softc *sc)  	struct ath_mci_coex *mci = &sc->mci_coex;  	struct ath_hw *ah = sc->sc_ah;  	struct ath_common *common = ath9k_hw_common(ah); +	struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci;  	u32 mci_int, mci_int_rxmsg;  	u32 offset, subtype, opcode;  	u32 *pgpm; @@ -397,8 +425,8 @@ void ath_mci_intr(struct ath_softc *sc)  	ar9003_mci_get_interrupt(sc->sc_ah, &mci_int, &mci_int_rxmsg); -	if (ar9003_mci_state(ah, MCI_STATE_ENABLE, NULL) == 0) { -		ar9003_mci_state(ah, MCI_STATE_INIT_GPM_OFFSET, NULL); +	if (ar9003_mci_state(ah, MCI_STATE_ENABLE) == 0) { +		ar9003_mci_get_next_gpm_offset(ah, true, NULL);  		return;  	} @@ -417,46 +445,41 @@ void ath_mci_intr(struct ath_softc *sc)  					NULL, 0, true, false);  		mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE; -		ar9003_mci_state(ah, MCI_STATE_RESET_REQ_WAKE, NULL); +		ar9003_mci_state(ah, MCI_STATE_RESET_REQ_WAKE);  		/*  		 * always do this for recovery and 2G/5G toggling and LNA_TRANS  		 */ -		ar9003_mci_state(ah, MCI_STATE_SET_BT_AWAKE, NULL); +		ar9003_mci_state(ah, MCI_STATE_SET_BT_AWAKE);  	}  	if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING) {  		mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING; -		if (ar9003_mci_state(ah, MCI_STATE_BT, NULL) == MCI_BT_SLEEP) { -			if (ar9003_mci_state(ah, MCI_STATE_REMOTE_SLEEP, NULL) != -			    MCI_BT_SLEEP) -				ar9003_mci_state(ah, MCI_STATE_SET_BT_AWAKE, -						 NULL); -		} +		if ((mci_hw->bt_state == MCI_BT_SLEEP) && +		    (ar9003_mci_state(ah, MCI_STATE_REMOTE_SLEEP) != +		     MCI_BT_SLEEP)) +			ar9003_mci_state(ah, MCI_STATE_SET_BT_AWAKE);  	}  	if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING) {  		mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING; -		if (ar9003_mci_state(ah, MCI_STATE_BT, NULL) == MCI_BT_AWAKE) { -			if (ar9003_mci_state(ah, MCI_STATE_REMOTE_SLEEP, NULL) != -			    MCI_BT_AWAKE) -				ar9003_mci_state(ah, MCI_STATE_SET_BT_SLEEP, -						 NULL); -		} +		if ((mci_hw->bt_state == MCI_BT_AWAKE) && +		    (ar9003_mci_state(ah, MCI_STATE_REMOTE_SLEEP) != +		     MCI_BT_AWAKE)) +			mci_hw->bt_state = MCI_BT_SLEEP;  	}  	if ((mci_int & AR_MCI_INTERRUPT_RX_INVALID_HDR) ||  	    (mci_int & AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT)) { -		ar9003_mci_state(ah, MCI_STATE_RECOVER_RX, NULL); +		ar9003_mci_state(ah, MCI_STATE_RECOVER_RX);  		skip_gpm = true;  	}  	if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO) {  		mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO; -		offset = ar9003_mci_state(ah, MCI_STATE_LAST_SCHD_MSG_OFFSET, -					  NULL); +		offset = ar9003_mci_state(ah, MCI_STATE_LAST_SCHD_MSG_OFFSET);  	}  	if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_GPM) { @@ -465,8 +488,8 @@ void ath_mci_intr(struct ath_softc *sc)  		while (more_data == MCI_GPM_MORE) {  			pgpm = mci->gpm_buf.bf_addr; -			offset = ar9003_mci_state(ah, MCI_STATE_NEXT_GPM_OFFSET, -						  &more_data); +			offset = ar9003_mci_get_next_gpm_offset(ah, false, +								&more_data);  			if (offset == MCI_GPM_INVALID)  				break; @@ -507,23 +530,17 @@ void ath_mci_intr(struct ath_softc *sc)  			mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_LNA_INFO;  		if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_CONT_INFO) { -			int value_dbm = ar9003_mci_state(ah, -						 MCI_STATE_CONT_RSSI_POWER, NULL); +			int value_dbm = MS(mci_hw->cont_status, +					   AR_MCI_CONT_RSSI_POWER);  			mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_CONT_INFO; -			if (ar9003_mci_state(ah, MCI_STATE_CONT_TXRX, NULL)) -				ath_dbg(common, MCI, -					"MCI CONT_INFO: (tx) pri = %d, pwr = %d dBm\n", -					ar9003_mci_state(ah, -						 MCI_STATE_CONT_PRIORITY, NULL), -					value_dbm); -			else -				ath_dbg(common, MCI, -					"MCI CONT_INFO: (rx) pri = %d,pwr = %d dBm\n", -					ar9003_mci_state(ah, -						 MCI_STATE_CONT_PRIORITY, NULL), -					value_dbm); +			ath_dbg(common, MCI, +				"MCI CONT_INFO: (%s) pri = %d pwr = %d dBm\n", +				MS(mci_hw->cont_status, AR_MCI_CONT_TXRX) ? +				"tx" : "rx", +				MS(mci_hw->cont_status, AR_MCI_CONT_PRIORITY), +				value_dbm);  		}  		if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_CONT_NACK) @@ -538,3 +555,14 @@ void ath_mci_intr(struct ath_softc *sc)  		mci_int &= ~(AR_MCI_INTERRUPT_RX_INVALID_HDR |  			     AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT);  } + +void ath_mci_enable(struct ath_softc *sc) +{ +	struct ath_common *common = ath9k_hw_common(sc->sc_ah); + +	if (!common->btcoex_enabled) +		return; + +	if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_MCI) +		sc->sc_ah->imask |= ATH9K_INT_MCI; +}  |