brintos

brintos / linux-shallow public Read only

0
0
Text · 12.4 KiB · 1e8495f Raw
483 lines · c
1// SPDX-License-Identifier: ISC2/*3 * Copyright (c) 2013 Broadcom Corporation4 */5#include <linux/slab.h>6#include <linux/netdevice.h>7#include <net/cfg80211.h>8 9#include <brcmu_wifi.h>10#include <brcmu_utils.h>11#include <defs.h>12#include "core.h"13#include "debug.h"14#include "fwil.h"15#include "fwil_types.h"16#include "btcoex.h"17#include "p2p.h"18#include "cfg80211.h"19 20/* T1 start SCO/eSCO priority suppression */21#define BRCMF_BTCOEX_OPPR_WIN_TIME   msecs_to_jiffies(2000)22 23/* BT registers values during DHCP */24#define BRCMF_BT_DHCP_REG50 0x802225#define BRCMF_BT_DHCP_REG51 026#define BRCMF_BT_DHCP_REG64 027#define BRCMF_BT_DHCP_REG65 028#define BRCMF_BT_DHCP_REG71 029#define BRCMF_BT_DHCP_REG66 0x271030#define BRCMF_BT_DHCP_REG41 0x3331#define BRCMF_BT_DHCP_REG68 0x19032 33/* number of samples for SCO detection */34#define BRCMF_BT_SCO_SAMPLES 1235 36/**37* enum brcmf_btcoex_state - BT coex DHCP state machine states38* @BRCMF_BT_DHCP_IDLE: DCHP is idle39* @BRCMF_BT_DHCP_START: DHCP started, wait before40*	boosting wifi priority41* @BRCMF_BT_DHCP_OPPR_WIN: graceful DHCP opportunity ended,42*	boost wifi priority43* @BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT: wifi priority boost end,44*	restore defaults45*/46enum brcmf_btcoex_state {47	BRCMF_BT_DHCP_IDLE,48	BRCMF_BT_DHCP_START,49	BRCMF_BT_DHCP_OPPR_WIN,50	BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT51};52 53/**54 * struct brcmf_btcoex_info - BT coex related information55 * @vif: interface for which request was done.56 * @timer: timer for DHCP state machine57 * @timeout: configured timeout.58 * @timer_on:  DHCP timer active59 * @dhcp_done: DHCP finished before T1/T2 timer expiration60 * @bt_state: DHCP state machine state61 * @work: DHCP state machine work62 * @cfg: driver private data for cfg80211 interface63 * @reg66: saved value of btc_params 6664 * @reg41: saved value of btc_params 4165 * @reg68: saved value of btc_params 6866 * @saved_regs_part1: flag indicating regs 66,41,6867 *	have been saved68 * @reg50: saved value of btc_params 5069 * @reg51: saved value of btc_params 5170 * @reg64: saved value of btc_params 6471 * @reg65: saved value of btc_params 6572 * @reg71: saved value of btc_params 7173 * @saved_regs_part2: flag indicating regs 50,51,64,65,7174 *	have been saved75 */76struct brcmf_btcoex_info {77	struct brcmf_cfg80211_vif *vif;78	struct timer_list timer;79	u16 timeout;80	bool timer_on;81	bool dhcp_done;82	enum brcmf_btcoex_state bt_state;83	struct work_struct work;84	struct brcmf_cfg80211_info *cfg;85	u32 reg66;86	u32 reg41;87	u32 reg68;88	bool saved_regs_part1;89	u32 reg50;90	u32 reg51;91	u32 reg64;92	u32 reg65;93	u32 reg71;94	bool saved_regs_part2;95};96 97/**98 * brcmf_btcoex_params_write() - write btc_params firmware variable99 * @ifp: interface100 * @addr: btc_params register number101 * @data: data to write102 */103static s32 brcmf_btcoex_params_write(struct brcmf_if *ifp, u32 addr, u32 data)104{105	struct {106		__le32 addr;107		__le32 data;108	} reg_write;109 110	reg_write.addr = cpu_to_le32(addr);111	reg_write.data = cpu_to_le32(data);112	return brcmf_fil_iovar_data_set(ifp, "btc_params",113					&reg_write, sizeof(reg_write));114}115 116/**117 * brcmf_btcoex_params_read() - read btc_params firmware variable118 * @ifp: interface119 * @addr: btc_params register number120 * @data: read data121 */122static s32 brcmf_btcoex_params_read(struct brcmf_if *ifp, u32 addr, u32 *data)123{124	*data = addr;125 126	return brcmf_fil_iovar_int_query(ifp, "btc_params", data);127}128 129/**130 * brcmf_btcoex_boost_wifi() - control BT SCO/eSCO parameters131 * @btci: BT coex info132 * @trump_sco:133 *	true - set SCO/eSCO parameters for compatibility134 *		during DHCP window135 *	false - restore saved parameter values136 *137 * Enhanced BT COEX settings for eSCO compatibility during DHCP window138 */139static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci,140				    bool trump_sco)141{142	struct brcmf_if *ifp = brcmf_get_ifp(btci->cfg->pub, 0);143 144	if (trump_sco && !btci->saved_regs_part2) {145		/* this should reduce eSCO agressive146		 * retransmit w/o breaking it147		 */148 149		/* save current */150		brcmf_dbg(INFO, "new SCO/eSCO coex algo {save & override}\n");151		brcmf_btcoex_params_read(ifp, 50, &btci->reg50);152		brcmf_btcoex_params_read(ifp, 51, &btci->reg51);153		brcmf_btcoex_params_read(ifp, 64, &btci->reg64);154		brcmf_btcoex_params_read(ifp, 65, &btci->reg65);155		brcmf_btcoex_params_read(ifp, 71, &btci->reg71);156 157		btci->saved_regs_part2 = true;158		brcmf_dbg(INFO,159			  "saved bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n",160			  btci->reg50, btci->reg51, btci->reg64,161			  btci->reg65, btci->reg71);162 163		/* pacify the eSco   */164		brcmf_btcoex_params_write(ifp, 50, BRCMF_BT_DHCP_REG50);165		brcmf_btcoex_params_write(ifp, 51, BRCMF_BT_DHCP_REG51);166		brcmf_btcoex_params_write(ifp, 64, BRCMF_BT_DHCP_REG64);167		brcmf_btcoex_params_write(ifp, 65, BRCMF_BT_DHCP_REG65);168		brcmf_btcoex_params_write(ifp, 71, BRCMF_BT_DHCP_REG71);169 170	} else if (btci->saved_regs_part2) {171		/* restore previously saved bt params */172		brcmf_dbg(INFO, "Do new SCO/eSCO coex algo {restore}\n");173		brcmf_btcoex_params_write(ifp, 50, btci->reg50);174		brcmf_btcoex_params_write(ifp, 51, btci->reg51);175		brcmf_btcoex_params_write(ifp, 64, btci->reg64);176		brcmf_btcoex_params_write(ifp, 65, btci->reg65);177		brcmf_btcoex_params_write(ifp, 71, btci->reg71);178 179		brcmf_dbg(INFO,180			  "restored bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n",181			  btci->reg50, btci->reg51, btci->reg64,182			  btci->reg65, btci->reg71);183 184		btci->saved_regs_part2 = false;185	} else {186		brcmf_dbg(INFO, "attempted to restore not saved BTCOEX params\n");187	}188}189 190/**191 * brcmf_btcoex_is_sco_active() - check if SCO/eSCO is active192 * @ifp: interface193 *194 * return: true if SCO/eSCO session is active195 */196static bool brcmf_btcoex_is_sco_active(struct brcmf_if *ifp)197{198	int ioc_res = 0;199	bool res = false;200	int sco_id_cnt = 0;201	u32 param27;202	int i;203 204	for (i = 0; i < BRCMF_BT_SCO_SAMPLES; i++) {205		ioc_res = brcmf_btcoex_params_read(ifp, 27, &param27);206 207		if (ioc_res < 0) {208			brcmf_err("ioc read btc params error\n");209			break;210		}211 212		brcmf_dbg(INFO, "sample[%d], btc_params 27:%x\n", i, param27);213 214		if ((param27 & 0x6) == 2) { /* count both sco & esco  */215			sco_id_cnt++;216		}217 218		if (sco_id_cnt > 2) {219			brcmf_dbg(INFO,220				  "sco/esco detected, pkt id_cnt:%d samples:%d\n",221				  sco_id_cnt, i);222			res = true;223			break;224		}225	}226	brcmf_dbg(TRACE, "exit: result=%d\n", res);227	return res;228}229 230/*231 * btcmf_btcoex_save_part1() - save first step parameters.232 */233static void btcmf_btcoex_save_part1(struct brcmf_btcoex_info *btci)234{235	struct brcmf_if *ifp = btci->vif->ifp;236 237	if (!btci->saved_regs_part1) {238		/* Retrieve and save original reg value */239		brcmf_btcoex_params_read(ifp, 66, &btci->reg66);240		brcmf_btcoex_params_read(ifp, 41, &btci->reg41);241		brcmf_btcoex_params_read(ifp, 68, &btci->reg68);242		btci->saved_regs_part1 = true;243		brcmf_dbg(INFO,244			  "saved btc_params regs (66,41,68) 0x%x 0x%x 0x%x\n",245			  btci->reg66, btci->reg41,246			  btci->reg68);247	}248}249 250/*251 * brcmf_btcoex_restore_part1() - restore first step parameters.252 */253static void brcmf_btcoex_restore_part1(struct brcmf_btcoex_info *btci)254{255	struct brcmf_if *ifp;256 257	if (btci->saved_regs_part1) {258		btci->saved_regs_part1 = false;259		ifp = btci->vif->ifp;260		brcmf_btcoex_params_write(ifp, 66, btci->reg66);261		brcmf_btcoex_params_write(ifp, 41, btci->reg41);262		brcmf_btcoex_params_write(ifp, 68, btci->reg68);263		brcmf_dbg(INFO,264			  "restored btc_params regs {66,41,68} 0x%x 0x%x 0x%x\n",265			  btci->reg66, btci->reg41,266			  btci->reg68);267	}268}269 270/*271 * brcmf_btcoex_timerfunc() - BT coex timer callback272 */273static void brcmf_btcoex_timerfunc(struct timer_list *t)274{275	struct brcmf_btcoex_info *bt_local = from_timer(bt_local, t, timer);276	brcmf_dbg(TRACE, "enter\n");277 278	bt_local->timer_on = false;279	schedule_work(&bt_local->work);280}281 282/**283 * brcmf_btcoex_handler() - BT coex state machine work handler284 * @work: work285 */286static void brcmf_btcoex_handler(struct work_struct *work)287{288	struct brcmf_btcoex_info *btci;289	btci = container_of(work, struct brcmf_btcoex_info, work);290	if (btci->timer_on) {291		btci->timer_on = false;292		del_timer_sync(&btci->timer);293	}294 295	switch (btci->bt_state) {296	case BRCMF_BT_DHCP_START:297		/* DHCP started provide OPPORTUNITY window298		   to get DHCP address299		*/300		brcmf_dbg(INFO, "DHCP started\n");301		btci->bt_state = BRCMF_BT_DHCP_OPPR_WIN;302		if (btci->timeout < BRCMF_BTCOEX_OPPR_WIN_TIME) {303			mod_timer(&btci->timer, btci->timer.expires);304		} else {305			btci->timeout -= BRCMF_BTCOEX_OPPR_WIN_TIME;306			mod_timer(&btci->timer,307				  jiffies + BRCMF_BTCOEX_OPPR_WIN_TIME);308		}309		btci->timer_on = true;310		break;311 312	case BRCMF_BT_DHCP_OPPR_WIN:313		if (btci->dhcp_done) {314			brcmf_dbg(INFO, "DHCP done before T1 expiration\n");315			goto idle;316		}317 318		/* DHCP is not over yet, start lowering BT priority */319		brcmf_dbg(INFO, "DHCP T1:%d expired\n",320			  jiffies_to_msecs(BRCMF_BTCOEX_OPPR_WIN_TIME));321		brcmf_btcoex_boost_wifi(btci, true);322 323		btci->bt_state = BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT;324		mod_timer(&btci->timer, jiffies + btci->timeout);325		btci->timer_on = true;326		break;327 328	case BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT:329		if (btci->dhcp_done)330			brcmf_dbg(INFO, "DHCP done before T2 expiration\n");331		else332			brcmf_dbg(INFO, "DHCP T2:%d expired\n",333				  BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT);334 335		goto idle;336 337	default:338		brcmf_err("invalid state=%d !!!\n", btci->bt_state);339		goto idle;340	}341 342	return;343 344idle:345	btci->bt_state = BRCMF_BT_DHCP_IDLE;346	btci->timer_on = false;347	brcmf_btcoex_boost_wifi(btci, false);348	cfg80211_crit_proto_stopped(&btci->vif->wdev, GFP_KERNEL);349	brcmf_btcoex_restore_part1(btci);350	btci->vif = NULL;351}352 353/**354 * brcmf_btcoex_attach() - initialize BT coex data355 * @cfg: driver private cfg80211 data356 *357 * return: 0 on success358 */359int brcmf_btcoex_attach(struct brcmf_cfg80211_info *cfg)360{361	struct brcmf_btcoex_info *btci;362	brcmf_dbg(TRACE, "enter\n");363 364	btci = kmalloc(sizeof(*btci), GFP_KERNEL);365	if (!btci)366		return -ENOMEM;367 368	btci->bt_state = BRCMF_BT_DHCP_IDLE;369 370	/* Set up timer for BT  */371	btci->timer_on = false;372	btci->timeout = BRCMF_BTCOEX_OPPR_WIN_TIME;373	timer_setup(&btci->timer, brcmf_btcoex_timerfunc, 0);374	btci->cfg = cfg;375	btci->saved_regs_part1 = false;376	btci->saved_regs_part2 = false;377 378	INIT_WORK(&btci->work, brcmf_btcoex_handler);379 380	cfg->btcoex = btci;381	return 0;382}383 384/**385 * brcmf_btcoex_detach - clean BT coex data386 * @cfg: driver private cfg80211 data387 */388void brcmf_btcoex_detach(struct brcmf_cfg80211_info *cfg)389{390	brcmf_dbg(TRACE, "enter\n");391 392	if (!cfg->btcoex)393		return;394 395	if (cfg->btcoex->timer_on) {396		cfg->btcoex->timer_on = false;397		timer_shutdown_sync(&cfg->btcoex->timer);398	}399 400	cancel_work_sync(&cfg->btcoex->work);401 402	brcmf_btcoex_boost_wifi(cfg->btcoex, false);403	brcmf_btcoex_restore_part1(cfg->btcoex);404 405	kfree(cfg->btcoex);406	cfg->btcoex = NULL;407}408 409static void brcmf_btcoex_dhcp_start(struct brcmf_btcoex_info *btci)410{411	struct brcmf_if *ifp = btci->vif->ifp;412 413	btcmf_btcoex_save_part1(btci);414	/* set new regs values */415	brcmf_btcoex_params_write(ifp, 66, BRCMF_BT_DHCP_REG66);416	brcmf_btcoex_params_write(ifp, 41, BRCMF_BT_DHCP_REG41);417	brcmf_btcoex_params_write(ifp, 68, BRCMF_BT_DHCP_REG68);418	btci->dhcp_done = false;419	btci->bt_state = BRCMF_BT_DHCP_START;420	schedule_work(&btci->work);421	brcmf_dbg(TRACE, "enable BT DHCP Timer\n");422}423 424static void brcmf_btcoex_dhcp_end(struct brcmf_btcoex_info *btci)425{426	/* Stop any bt timer because DHCP session is done */427	btci->dhcp_done = true;428	if (btci->timer_on) {429		brcmf_dbg(INFO, "disable BT DHCP Timer\n");430		btci->timer_on = false;431		del_timer_sync(&btci->timer);432 433		/* schedule worker if transition to IDLE is needed */434		if (btci->bt_state != BRCMF_BT_DHCP_IDLE) {435			brcmf_dbg(INFO, "bt_state:%d\n",436				  btci->bt_state);437			schedule_work(&btci->work);438		}439	} else {440		/* Restore original values */441		brcmf_btcoex_restore_part1(btci);442	}443}444 445/*446 * brcmf_btcoex_set_mode - set BT coex mode447 * @mode: Wifi-Bluetooth coexistence mode448 *449 * return: 0 on success450 */451int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif,452			  enum brcmf_btcoex_mode mode, u16 duration)453{454	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);455	struct brcmf_btcoex_info *btci = cfg->btcoex;456	struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);457 458	switch (mode) {459	case BRCMF_BTCOEX_DISABLED:460		brcmf_dbg(INFO, "DHCP session starts\n");461		if (btci->bt_state != BRCMF_BT_DHCP_IDLE)462			return -EBUSY;463		/* Start BT timer only for SCO connection */464		if (brcmf_btcoex_is_sco_active(ifp)) {465			btci->timeout = msecs_to_jiffies(duration);466			btci->vif = vif;467			brcmf_btcoex_dhcp_start(btci);468		}469		break;470 471	case BRCMF_BTCOEX_ENABLED:472		brcmf_dbg(INFO, "DHCP session ends\n");473		if (btci->bt_state != BRCMF_BT_DHCP_IDLE &&474		    vif == btci->vif) {475			brcmf_btcoex_dhcp_end(btci);476		}477		break;478	default:479		brcmf_dbg(INFO, "Unknown mode, ignored\n");480	}481	return 0;482}483