1daeccac2SArend van Spriel // SPDX-License-Identifier: ISC
205491d2cSKalle Valo /*
305491d2cSKalle Valo * Copyright (c) 2013 Broadcom Corporation
405491d2cSKalle Valo */
505491d2cSKalle Valo #include <linux/slab.h>
605491d2cSKalle Valo #include <linux/netdevice.h>
705491d2cSKalle Valo #include <net/cfg80211.h>
805491d2cSKalle Valo
905491d2cSKalle Valo #include <brcmu_wifi.h>
1005491d2cSKalle Valo #include <brcmu_utils.h>
1105491d2cSKalle Valo #include <defs.h>
1205491d2cSKalle Valo #include "core.h"
1305491d2cSKalle Valo #include "debug.h"
1405491d2cSKalle Valo #include "fwil.h"
1505491d2cSKalle Valo #include "fwil_types.h"
1605491d2cSKalle Valo #include "btcoex.h"
1705491d2cSKalle Valo #include "p2p.h"
1805491d2cSKalle Valo #include "cfg80211.h"
1905491d2cSKalle Valo
2005491d2cSKalle Valo /* T1 start SCO/eSCO priority suppression */
215ce96c08SArend van Spriel #define BRCMF_BTCOEX_OPPR_WIN_TIME msecs_to_jiffies(2000)
2205491d2cSKalle Valo
2305491d2cSKalle Valo /* BT registers values during DHCP */
2405491d2cSKalle Valo #define BRCMF_BT_DHCP_REG50 0x8022
2505491d2cSKalle Valo #define BRCMF_BT_DHCP_REG51 0
2605491d2cSKalle Valo #define BRCMF_BT_DHCP_REG64 0
2705491d2cSKalle Valo #define BRCMF_BT_DHCP_REG65 0
2805491d2cSKalle Valo #define BRCMF_BT_DHCP_REG71 0
2905491d2cSKalle Valo #define BRCMF_BT_DHCP_REG66 0x2710
3005491d2cSKalle Valo #define BRCMF_BT_DHCP_REG41 0x33
3105491d2cSKalle Valo #define BRCMF_BT_DHCP_REG68 0x190
3205491d2cSKalle Valo
3305491d2cSKalle Valo /* number of samples for SCO detection */
3405491d2cSKalle Valo #define BRCMF_BT_SCO_SAMPLES 12
3505491d2cSKalle Valo
3605491d2cSKalle Valo /**
3705491d2cSKalle Valo * enum brcmf_btcoex_state - BT coex DHCP state machine states
3805491d2cSKalle Valo * @BRCMF_BT_DHCP_IDLE: DCHP is idle
3905491d2cSKalle Valo * @BRCMF_BT_DHCP_START: DHCP started, wait before
4005491d2cSKalle Valo * boosting wifi priority
4105491d2cSKalle Valo * @BRCMF_BT_DHCP_OPPR_WIN: graceful DHCP opportunity ended,
4205491d2cSKalle Valo * boost wifi priority
4305491d2cSKalle Valo * @BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT: wifi priority boost end,
4405491d2cSKalle Valo * restore defaults
4505491d2cSKalle Valo */
4605491d2cSKalle Valo enum brcmf_btcoex_state {
4705491d2cSKalle Valo BRCMF_BT_DHCP_IDLE,
4805491d2cSKalle Valo BRCMF_BT_DHCP_START,
4905491d2cSKalle Valo BRCMF_BT_DHCP_OPPR_WIN,
5005491d2cSKalle Valo BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT
5105491d2cSKalle Valo };
5205491d2cSKalle Valo
5305491d2cSKalle Valo /**
5405491d2cSKalle Valo * struct brcmf_btcoex_info - BT coex related information
5505491d2cSKalle Valo * @vif: interface for which request was done.
5605491d2cSKalle Valo * @timer: timer for DHCP state machine
5705491d2cSKalle Valo * @timeout: configured timeout.
5805491d2cSKalle Valo * @timer_on: DHCP timer active
5905491d2cSKalle Valo * @dhcp_done: DHCP finished before T1/T2 timer expiration
6005491d2cSKalle Valo * @bt_state: DHCP state machine state
6105491d2cSKalle Valo * @work: DHCP state machine work
6205491d2cSKalle Valo * @cfg: driver private data for cfg80211 interface
6305491d2cSKalle Valo * @reg66: saved value of btc_params 66
6405491d2cSKalle Valo * @reg41: saved value of btc_params 41
6505491d2cSKalle Valo * @reg68: saved value of btc_params 68
6605491d2cSKalle Valo * @saved_regs_part1: flag indicating regs 66,41,68
6705491d2cSKalle Valo * have been saved
689d16c385SLee Jones * @reg50: saved value of btc_params 50
6905491d2cSKalle Valo * @reg51: saved value of btc_params 51
7005491d2cSKalle Valo * @reg64: saved value of btc_params 64
7105491d2cSKalle Valo * @reg65: saved value of btc_params 65
7205491d2cSKalle Valo * @reg71: saved value of btc_params 71
739d16c385SLee Jones * @saved_regs_part2: flag indicating regs 50,51,64,65,71
7405491d2cSKalle Valo * have been saved
7505491d2cSKalle Valo */
7605491d2cSKalle Valo struct brcmf_btcoex_info {
7705491d2cSKalle Valo struct brcmf_cfg80211_vif *vif;
7805491d2cSKalle Valo struct timer_list timer;
7905491d2cSKalle Valo u16 timeout;
8005491d2cSKalle Valo bool timer_on;
8105491d2cSKalle Valo bool dhcp_done;
8205491d2cSKalle Valo enum brcmf_btcoex_state bt_state;
8305491d2cSKalle Valo struct work_struct work;
8405491d2cSKalle Valo struct brcmf_cfg80211_info *cfg;
8505491d2cSKalle Valo u32 reg66;
8605491d2cSKalle Valo u32 reg41;
8705491d2cSKalle Valo u32 reg68;
8805491d2cSKalle Valo bool saved_regs_part1;
8905491d2cSKalle Valo u32 reg50;
9005491d2cSKalle Valo u32 reg51;
9105491d2cSKalle Valo u32 reg64;
9205491d2cSKalle Valo u32 reg65;
9305491d2cSKalle Valo u32 reg71;
9405491d2cSKalle Valo bool saved_regs_part2;
9505491d2cSKalle Valo };
9605491d2cSKalle Valo
9705491d2cSKalle Valo /**
9805491d2cSKalle Valo * brcmf_btcoex_params_write() - write btc_params firmware variable
9905491d2cSKalle Valo * @ifp: interface
10005491d2cSKalle Valo * @addr: btc_params register number
10105491d2cSKalle Valo * @data: data to write
10205491d2cSKalle Valo */
brcmf_btcoex_params_write(struct brcmf_if * ifp,u32 addr,u32 data)10305491d2cSKalle Valo static s32 brcmf_btcoex_params_write(struct brcmf_if *ifp, u32 addr, u32 data)
10405491d2cSKalle Valo {
10505491d2cSKalle Valo struct {
10605491d2cSKalle Valo __le32 addr;
10705491d2cSKalle Valo __le32 data;
10805491d2cSKalle Valo } reg_write;
10905491d2cSKalle Valo
11005491d2cSKalle Valo reg_write.addr = cpu_to_le32(addr);
11105491d2cSKalle Valo reg_write.data = cpu_to_le32(data);
11205491d2cSKalle Valo return brcmf_fil_iovar_data_set(ifp, "btc_params",
11305491d2cSKalle Valo ®_write, sizeof(reg_write));
11405491d2cSKalle Valo }
11505491d2cSKalle Valo
11605491d2cSKalle Valo /**
11705491d2cSKalle Valo * brcmf_btcoex_params_read() - read btc_params firmware variable
11805491d2cSKalle Valo * @ifp: interface
11905491d2cSKalle Valo * @addr: btc_params register number
12005491d2cSKalle Valo * @data: read data
12105491d2cSKalle Valo */
brcmf_btcoex_params_read(struct brcmf_if * ifp,u32 addr,u32 * data)12205491d2cSKalle Valo static s32 brcmf_btcoex_params_read(struct brcmf_if *ifp, u32 addr, u32 *data)
12305491d2cSKalle Valo {
12405491d2cSKalle Valo *data = addr;
12505491d2cSKalle Valo
126*fb1862ceSArend van Spriel return brcmf_fil_iovar_int_query(ifp, "btc_params", data);
12705491d2cSKalle Valo }
12805491d2cSKalle Valo
12905491d2cSKalle Valo /**
13005491d2cSKalle Valo * brcmf_btcoex_boost_wifi() - control BT SCO/eSCO parameters
13105491d2cSKalle Valo * @btci: BT coex info
13205491d2cSKalle Valo * @trump_sco:
13305491d2cSKalle Valo * true - set SCO/eSCO parameters for compatibility
13405491d2cSKalle Valo * during DHCP window
13505491d2cSKalle Valo * false - restore saved parameter values
13605491d2cSKalle Valo *
13705491d2cSKalle Valo * Enhanced BT COEX settings for eSCO compatibility during DHCP window
13805491d2cSKalle Valo */
brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info * btci,bool trump_sco)13905491d2cSKalle Valo static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci,
14005491d2cSKalle Valo bool trump_sco)
14105491d2cSKalle Valo {
14205491d2cSKalle Valo struct brcmf_if *ifp = brcmf_get_ifp(btci->cfg->pub, 0);
14305491d2cSKalle Valo
14405491d2cSKalle Valo if (trump_sco && !btci->saved_regs_part2) {
14505491d2cSKalle Valo /* this should reduce eSCO agressive
14605491d2cSKalle Valo * retransmit w/o breaking it
14705491d2cSKalle Valo */
14805491d2cSKalle Valo
14905491d2cSKalle Valo /* save current */
15005491d2cSKalle Valo brcmf_dbg(INFO, "new SCO/eSCO coex algo {save & override}\n");
15105491d2cSKalle Valo brcmf_btcoex_params_read(ifp, 50, &btci->reg50);
15205491d2cSKalle Valo brcmf_btcoex_params_read(ifp, 51, &btci->reg51);
15305491d2cSKalle Valo brcmf_btcoex_params_read(ifp, 64, &btci->reg64);
15405491d2cSKalle Valo brcmf_btcoex_params_read(ifp, 65, &btci->reg65);
15505491d2cSKalle Valo brcmf_btcoex_params_read(ifp, 71, &btci->reg71);
15605491d2cSKalle Valo
15705491d2cSKalle Valo btci->saved_regs_part2 = true;
15805491d2cSKalle Valo brcmf_dbg(INFO,
15905491d2cSKalle Valo "saved bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
16005491d2cSKalle Valo btci->reg50, btci->reg51, btci->reg64,
16105491d2cSKalle Valo btci->reg65, btci->reg71);
16205491d2cSKalle Valo
16305491d2cSKalle Valo /* pacify the eSco */
16405491d2cSKalle Valo brcmf_btcoex_params_write(ifp, 50, BRCMF_BT_DHCP_REG50);
16505491d2cSKalle Valo brcmf_btcoex_params_write(ifp, 51, BRCMF_BT_DHCP_REG51);
16605491d2cSKalle Valo brcmf_btcoex_params_write(ifp, 64, BRCMF_BT_DHCP_REG64);
16705491d2cSKalle Valo brcmf_btcoex_params_write(ifp, 65, BRCMF_BT_DHCP_REG65);
16805491d2cSKalle Valo brcmf_btcoex_params_write(ifp, 71, BRCMF_BT_DHCP_REG71);
16905491d2cSKalle Valo
17005491d2cSKalle Valo } else if (btci->saved_regs_part2) {
17105491d2cSKalle Valo /* restore previously saved bt params */
17205491d2cSKalle Valo brcmf_dbg(INFO, "Do new SCO/eSCO coex algo {restore}\n");
17305491d2cSKalle Valo brcmf_btcoex_params_write(ifp, 50, btci->reg50);
17405491d2cSKalle Valo brcmf_btcoex_params_write(ifp, 51, btci->reg51);
17505491d2cSKalle Valo brcmf_btcoex_params_write(ifp, 64, btci->reg64);
17605491d2cSKalle Valo brcmf_btcoex_params_write(ifp, 65, btci->reg65);
17705491d2cSKalle Valo brcmf_btcoex_params_write(ifp, 71, btci->reg71);
17805491d2cSKalle Valo
17905491d2cSKalle Valo brcmf_dbg(INFO,
18005491d2cSKalle Valo "restored bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
18105491d2cSKalle Valo btci->reg50, btci->reg51, btci->reg64,
18205491d2cSKalle Valo btci->reg65, btci->reg71);
18305491d2cSKalle Valo
18405491d2cSKalle Valo btci->saved_regs_part2 = false;
18505491d2cSKalle Valo } else {
18605491d2cSKalle Valo brcmf_dbg(INFO, "attempted to restore not saved BTCOEX params\n");
18705491d2cSKalle Valo }
18805491d2cSKalle Valo }
18905491d2cSKalle Valo
19005491d2cSKalle Valo /**
19105491d2cSKalle Valo * brcmf_btcoex_is_sco_active() - check if SCO/eSCO is active
19205491d2cSKalle Valo * @ifp: interface
19305491d2cSKalle Valo *
19405491d2cSKalle Valo * return: true if SCO/eSCO session is active
19505491d2cSKalle Valo */
brcmf_btcoex_is_sco_active(struct brcmf_if * ifp)19605491d2cSKalle Valo static bool brcmf_btcoex_is_sco_active(struct brcmf_if *ifp)
19705491d2cSKalle Valo {
19805491d2cSKalle Valo int ioc_res = 0;
19905491d2cSKalle Valo bool res = false;
20005491d2cSKalle Valo int sco_id_cnt = 0;
20105491d2cSKalle Valo u32 param27;
20205491d2cSKalle Valo int i;
20305491d2cSKalle Valo
20405491d2cSKalle Valo for (i = 0; i < BRCMF_BT_SCO_SAMPLES; i++) {
20505491d2cSKalle Valo ioc_res = brcmf_btcoex_params_read(ifp, 27, ¶m27);
20605491d2cSKalle Valo
20705491d2cSKalle Valo if (ioc_res < 0) {
20805491d2cSKalle Valo brcmf_err("ioc read btc params error\n");
20905491d2cSKalle Valo break;
21005491d2cSKalle Valo }
21105491d2cSKalle Valo
21205491d2cSKalle Valo brcmf_dbg(INFO, "sample[%d], btc_params 27:%x\n", i, param27);
21305491d2cSKalle Valo
21405491d2cSKalle Valo if ((param27 & 0x6) == 2) { /* count both sco & esco */
21505491d2cSKalle Valo sco_id_cnt++;
21605491d2cSKalle Valo }
21705491d2cSKalle Valo
21805491d2cSKalle Valo if (sco_id_cnt > 2) {
21905491d2cSKalle Valo brcmf_dbg(INFO,
22005491d2cSKalle Valo "sco/esco detected, pkt id_cnt:%d samples:%d\n",
22105491d2cSKalle Valo sco_id_cnt, i);
22205491d2cSKalle Valo res = true;
22305491d2cSKalle Valo break;
22405491d2cSKalle Valo }
22505491d2cSKalle Valo }
22605491d2cSKalle Valo brcmf_dbg(TRACE, "exit: result=%d\n", res);
22705491d2cSKalle Valo return res;
22805491d2cSKalle Valo }
22905491d2cSKalle Valo
2309d16c385SLee Jones /*
23105491d2cSKalle Valo * btcmf_btcoex_save_part1() - save first step parameters.
23205491d2cSKalle Valo */
btcmf_btcoex_save_part1(struct brcmf_btcoex_info * btci)23305491d2cSKalle Valo static void btcmf_btcoex_save_part1(struct brcmf_btcoex_info *btci)
23405491d2cSKalle Valo {
23505491d2cSKalle Valo struct brcmf_if *ifp = btci->vif->ifp;
23605491d2cSKalle Valo
23705491d2cSKalle Valo if (!btci->saved_regs_part1) {
23805491d2cSKalle Valo /* Retrieve and save original reg value */
23905491d2cSKalle Valo brcmf_btcoex_params_read(ifp, 66, &btci->reg66);
24005491d2cSKalle Valo brcmf_btcoex_params_read(ifp, 41, &btci->reg41);
24105491d2cSKalle Valo brcmf_btcoex_params_read(ifp, 68, &btci->reg68);
24205491d2cSKalle Valo btci->saved_regs_part1 = true;
24305491d2cSKalle Valo brcmf_dbg(INFO,
24405491d2cSKalle Valo "saved btc_params regs (66,41,68) 0x%x 0x%x 0x%x\n",
24505491d2cSKalle Valo btci->reg66, btci->reg41,
24605491d2cSKalle Valo btci->reg68);
24705491d2cSKalle Valo }
24805491d2cSKalle Valo }
24905491d2cSKalle Valo
2509d16c385SLee Jones /*
25105491d2cSKalle Valo * brcmf_btcoex_restore_part1() - restore first step parameters.
25205491d2cSKalle Valo */
brcmf_btcoex_restore_part1(struct brcmf_btcoex_info * btci)25305491d2cSKalle Valo static void brcmf_btcoex_restore_part1(struct brcmf_btcoex_info *btci)
25405491d2cSKalle Valo {
25505491d2cSKalle Valo struct brcmf_if *ifp;
25605491d2cSKalle Valo
25705491d2cSKalle Valo if (btci->saved_regs_part1) {
25805491d2cSKalle Valo btci->saved_regs_part1 = false;
25905491d2cSKalle Valo ifp = btci->vif->ifp;
26005491d2cSKalle Valo brcmf_btcoex_params_write(ifp, 66, btci->reg66);
26105491d2cSKalle Valo brcmf_btcoex_params_write(ifp, 41, btci->reg41);
26205491d2cSKalle Valo brcmf_btcoex_params_write(ifp, 68, btci->reg68);
26305491d2cSKalle Valo brcmf_dbg(INFO,
26405491d2cSKalle Valo "restored btc_params regs {66,41,68} 0x%x 0x%x 0x%x\n",
26505491d2cSKalle Valo btci->reg66, btci->reg41,
26605491d2cSKalle Valo btci->reg68);
26705491d2cSKalle Valo }
26805491d2cSKalle Valo }
26905491d2cSKalle Valo
2709d16c385SLee Jones /*
27105491d2cSKalle Valo * brcmf_btcoex_timerfunc() - BT coex timer callback
27205491d2cSKalle Valo */
brcmf_btcoex_timerfunc(struct timer_list * t)273e99e88a9SKees Cook static void brcmf_btcoex_timerfunc(struct timer_list *t)
27405491d2cSKalle Valo {
275e99e88a9SKees Cook struct brcmf_btcoex_info *bt_local = from_timer(bt_local, t, timer);
27605491d2cSKalle Valo brcmf_dbg(TRACE, "enter\n");
27705491d2cSKalle Valo
27805491d2cSKalle Valo bt_local->timer_on = false;
27905491d2cSKalle Valo schedule_work(&bt_local->work);
28005491d2cSKalle Valo }
28105491d2cSKalle Valo
28205491d2cSKalle Valo /**
28305491d2cSKalle Valo * brcmf_btcoex_handler() - BT coex state machine work handler
28405491d2cSKalle Valo * @work: work
28505491d2cSKalle Valo */
brcmf_btcoex_handler(struct work_struct * work)28605491d2cSKalle Valo static void brcmf_btcoex_handler(struct work_struct *work)
28705491d2cSKalle Valo {
28805491d2cSKalle Valo struct brcmf_btcoex_info *btci;
28905491d2cSKalle Valo btci = container_of(work, struct brcmf_btcoex_info, work);
29005491d2cSKalle Valo if (btci->timer_on) {
29105491d2cSKalle Valo btci->timer_on = false;
29205491d2cSKalle Valo del_timer_sync(&btci->timer);
29305491d2cSKalle Valo }
29405491d2cSKalle Valo
29505491d2cSKalle Valo switch (btci->bt_state) {
29605491d2cSKalle Valo case BRCMF_BT_DHCP_START:
29705491d2cSKalle Valo /* DHCP started provide OPPORTUNITY window
29805491d2cSKalle Valo to get DHCP address
29905491d2cSKalle Valo */
30005491d2cSKalle Valo brcmf_dbg(INFO, "DHCP started\n");
30105491d2cSKalle Valo btci->bt_state = BRCMF_BT_DHCP_OPPR_WIN;
30205491d2cSKalle Valo if (btci->timeout < BRCMF_BTCOEX_OPPR_WIN_TIME) {
30305491d2cSKalle Valo mod_timer(&btci->timer, btci->timer.expires);
30405491d2cSKalle Valo } else {
30505491d2cSKalle Valo btci->timeout -= BRCMF_BTCOEX_OPPR_WIN_TIME;
30605491d2cSKalle Valo mod_timer(&btci->timer,
3075ce96c08SArend van Spriel jiffies + BRCMF_BTCOEX_OPPR_WIN_TIME);
30805491d2cSKalle Valo }
30905491d2cSKalle Valo btci->timer_on = true;
31005491d2cSKalle Valo break;
31105491d2cSKalle Valo
31205491d2cSKalle Valo case BRCMF_BT_DHCP_OPPR_WIN:
31305491d2cSKalle Valo if (btci->dhcp_done) {
31405491d2cSKalle Valo brcmf_dbg(INFO, "DHCP done before T1 expiration\n");
31505491d2cSKalle Valo goto idle;
31605491d2cSKalle Valo }
31705491d2cSKalle Valo
31805491d2cSKalle Valo /* DHCP is not over yet, start lowering BT priority */
31905491d2cSKalle Valo brcmf_dbg(INFO, "DHCP T1:%d expired\n",
3205ce96c08SArend van Spriel jiffies_to_msecs(BRCMF_BTCOEX_OPPR_WIN_TIME));
32105491d2cSKalle Valo brcmf_btcoex_boost_wifi(btci, true);
32205491d2cSKalle Valo
32305491d2cSKalle Valo btci->bt_state = BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT;
3245ce96c08SArend van Spriel mod_timer(&btci->timer, jiffies + btci->timeout);
32505491d2cSKalle Valo btci->timer_on = true;
32605491d2cSKalle Valo break;
32705491d2cSKalle Valo
32805491d2cSKalle Valo case BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT:
32905491d2cSKalle Valo if (btci->dhcp_done)
33005491d2cSKalle Valo brcmf_dbg(INFO, "DHCP done before T2 expiration\n");
33105491d2cSKalle Valo else
33205491d2cSKalle Valo brcmf_dbg(INFO, "DHCP T2:%d expired\n",
33305491d2cSKalle Valo BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT);
33405491d2cSKalle Valo
33505491d2cSKalle Valo goto idle;
33605491d2cSKalle Valo
33705491d2cSKalle Valo default:
33805491d2cSKalle Valo brcmf_err("invalid state=%d !!!\n", btci->bt_state);
33905491d2cSKalle Valo goto idle;
34005491d2cSKalle Valo }
34105491d2cSKalle Valo
34205491d2cSKalle Valo return;
34305491d2cSKalle Valo
34405491d2cSKalle Valo idle:
34505491d2cSKalle Valo btci->bt_state = BRCMF_BT_DHCP_IDLE;
34605491d2cSKalle Valo btci->timer_on = false;
34705491d2cSKalle Valo brcmf_btcoex_boost_wifi(btci, false);
34805491d2cSKalle Valo cfg80211_crit_proto_stopped(&btci->vif->wdev, GFP_KERNEL);
34905491d2cSKalle Valo brcmf_btcoex_restore_part1(btci);
35005491d2cSKalle Valo btci->vif = NULL;
35105491d2cSKalle Valo }
35205491d2cSKalle Valo
35305491d2cSKalle Valo /**
35405491d2cSKalle Valo * brcmf_btcoex_attach() - initialize BT coex data
35505491d2cSKalle Valo * @cfg: driver private cfg80211 data
35605491d2cSKalle Valo *
35705491d2cSKalle Valo * return: 0 on success
35805491d2cSKalle Valo */
brcmf_btcoex_attach(struct brcmf_cfg80211_info * cfg)35905491d2cSKalle Valo int brcmf_btcoex_attach(struct brcmf_cfg80211_info *cfg)
36005491d2cSKalle Valo {
36105491d2cSKalle Valo struct brcmf_btcoex_info *btci = NULL;
36205491d2cSKalle Valo brcmf_dbg(TRACE, "enter\n");
36305491d2cSKalle Valo
36405491d2cSKalle Valo btci = kmalloc(sizeof(struct brcmf_btcoex_info), GFP_KERNEL);
36505491d2cSKalle Valo if (!btci)
36605491d2cSKalle Valo return -ENOMEM;
36705491d2cSKalle Valo
36805491d2cSKalle Valo btci->bt_state = BRCMF_BT_DHCP_IDLE;
36905491d2cSKalle Valo
37005491d2cSKalle Valo /* Set up timer for BT */
37105491d2cSKalle Valo btci->timer_on = false;
37205491d2cSKalle Valo btci->timeout = BRCMF_BTCOEX_OPPR_WIN_TIME;
373e99e88a9SKees Cook timer_setup(&btci->timer, brcmf_btcoex_timerfunc, 0);
37405491d2cSKalle Valo btci->cfg = cfg;
37505491d2cSKalle Valo btci->saved_regs_part1 = false;
37605491d2cSKalle Valo btci->saved_regs_part2 = false;
37705491d2cSKalle Valo
37805491d2cSKalle Valo INIT_WORK(&btci->work, brcmf_btcoex_handler);
37905491d2cSKalle Valo
38005491d2cSKalle Valo cfg->btcoex = btci;
38105491d2cSKalle Valo return 0;
38205491d2cSKalle Valo }
38305491d2cSKalle Valo
38405491d2cSKalle Valo /**
38505491d2cSKalle Valo * brcmf_btcoex_detach - clean BT coex data
38605491d2cSKalle Valo * @cfg: driver private cfg80211 data
38705491d2cSKalle Valo */
brcmf_btcoex_detach(struct brcmf_cfg80211_info * cfg)38805491d2cSKalle Valo void brcmf_btcoex_detach(struct brcmf_cfg80211_info *cfg)
38905491d2cSKalle Valo {
39005491d2cSKalle Valo brcmf_dbg(TRACE, "enter\n");
39105491d2cSKalle Valo
39205491d2cSKalle Valo if (!cfg->btcoex)
39305491d2cSKalle Valo return;
39405491d2cSKalle Valo
39505491d2cSKalle Valo if (cfg->btcoex->timer_on) {
39605491d2cSKalle Valo cfg->btcoex->timer_on = false;
397292a089dSSteven Rostedt (Google) timer_shutdown_sync(&cfg->btcoex->timer);
39805491d2cSKalle Valo }
39905491d2cSKalle Valo
40005491d2cSKalle Valo cancel_work_sync(&cfg->btcoex->work);
40105491d2cSKalle Valo
40205491d2cSKalle Valo brcmf_btcoex_boost_wifi(cfg->btcoex, false);
40305491d2cSKalle Valo brcmf_btcoex_restore_part1(cfg->btcoex);
40405491d2cSKalle Valo
40505491d2cSKalle Valo kfree(cfg->btcoex);
40605491d2cSKalle Valo cfg->btcoex = NULL;
40705491d2cSKalle Valo }
40805491d2cSKalle Valo
brcmf_btcoex_dhcp_start(struct brcmf_btcoex_info * btci)40905491d2cSKalle Valo static void brcmf_btcoex_dhcp_start(struct brcmf_btcoex_info *btci)
41005491d2cSKalle Valo {
41105491d2cSKalle Valo struct brcmf_if *ifp = btci->vif->ifp;
41205491d2cSKalle Valo
41305491d2cSKalle Valo btcmf_btcoex_save_part1(btci);
41405491d2cSKalle Valo /* set new regs values */
41505491d2cSKalle Valo brcmf_btcoex_params_write(ifp, 66, BRCMF_BT_DHCP_REG66);
41605491d2cSKalle Valo brcmf_btcoex_params_write(ifp, 41, BRCMF_BT_DHCP_REG41);
41705491d2cSKalle Valo brcmf_btcoex_params_write(ifp, 68, BRCMF_BT_DHCP_REG68);
41805491d2cSKalle Valo btci->dhcp_done = false;
41905491d2cSKalle Valo btci->bt_state = BRCMF_BT_DHCP_START;
42005491d2cSKalle Valo schedule_work(&btci->work);
42105491d2cSKalle Valo brcmf_dbg(TRACE, "enable BT DHCP Timer\n");
42205491d2cSKalle Valo }
42305491d2cSKalle Valo
brcmf_btcoex_dhcp_end(struct brcmf_btcoex_info * btci)42405491d2cSKalle Valo static void brcmf_btcoex_dhcp_end(struct brcmf_btcoex_info *btci)
42505491d2cSKalle Valo {
42605491d2cSKalle Valo /* Stop any bt timer because DHCP session is done */
42705491d2cSKalle Valo btci->dhcp_done = true;
42805491d2cSKalle Valo if (btci->timer_on) {
42905491d2cSKalle Valo brcmf_dbg(INFO, "disable BT DHCP Timer\n");
43005491d2cSKalle Valo btci->timer_on = false;
43105491d2cSKalle Valo del_timer_sync(&btci->timer);
43205491d2cSKalle Valo
43305491d2cSKalle Valo /* schedule worker if transition to IDLE is needed */
43405491d2cSKalle Valo if (btci->bt_state != BRCMF_BT_DHCP_IDLE) {
43505491d2cSKalle Valo brcmf_dbg(INFO, "bt_state:%d\n",
43605491d2cSKalle Valo btci->bt_state);
43705491d2cSKalle Valo schedule_work(&btci->work);
43805491d2cSKalle Valo }
43905491d2cSKalle Valo } else {
44005491d2cSKalle Valo /* Restore original values */
44105491d2cSKalle Valo brcmf_btcoex_restore_part1(btci);
44205491d2cSKalle Valo }
44305491d2cSKalle Valo }
44405491d2cSKalle Valo
4459d16c385SLee Jones /*
44605491d2cSKalle Valo * brcmf_btcoex_set_mode - set BT coex mode
44705491d2cSKalle Valo * @mode: Wifi-Bluetooth coexistence mode
44805491d2cSKalle Valo *
44905491d2cSKalle Valo * return: 0 on success
45005491d2cSKalle Valo */
brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif * vif,enum brcmf_btcoex_mode mode,u16 duration)45105491d2cSKalle Valo int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif,
45205491d2cSKalle Valo enum brcmf_btcoex_mode mode, u16 duration)
45305491d2cSKalle Valo {
454856d5a01SArend Van Spriel struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
45505491d2cSKalle Valo struct brcmf_btcoex_info *btci = cfg->btcoex;
45605491d2cSKalle Valo struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);
45705491d2cSKalle Valo
45805491d2cSKalle Valo switch (mode) {
45905491d2cSKalle Valo case BRCMF_BTCOEX_DISABLED:
46005491d2cSKalle Valo brcmf_dbg(INFO, "DHCP session starts\n");
46105491d2cSKalle Valo if (btci->bt_state != BRCMF_BT_DHCP_IDLE)
46205491d2cSKalle Valo return -EBUSY;
46305491d2cSKalle Valo /* Start BT timer only for SCO connection */
46405491d2cSKalle Valo if (brcmf_btcoex_is_sco_active(ifp)) {
4655ce96c08SArend van Spriel btci->timeout = msecs_to_jiffies(duration);
46605491d2cSKalle Valo btci->vif = vif;
46705491d2cSKalle Valo brcmf_btcoex_dhcp_start(btci);
46805491d2cSKalle Valo }
46905491d2cSKalle Valo break;
47005491d2cSKalle Valo
47105491d2cSKalle Valo case BRCMF_BTCOEX_ENABLED:
47205491d2cSKalle Valo brcmf_dbg(INFO, "DHCP session ends\n");
47305491d2cSKalle Valo if (btci->bt_state != BRCMF_BT_DHCP_IDLE &&
47405491d2cSKalle Valo vif == btci->vif) {
47505491d2cSKalle Valo brcmf_btcoex_dhcp_end(btci);
47605491d2cSKalle Valo }
47705491d2cSKalle Valo break;
47805491d2cSKalle Valo default:
47905491d2cSKalle Valo brcmf_dbg(INFO, "Unknown mode, ignored\n");
48005491d2cSKalle Valo }
48105491d2cSKalle Valo return 0;
48205491d2cSKalle Valo }
483