xref: /linux/drivers/net/wireless/rsi/rsi_91x_coex.c (revision 5f48e91624b35fc85d087978feaea70a8c35be56)
17a03124cSLee Jones /*
22108df3cSPrameela Rani Garnepudi  * Copyright (c) 2018 Redpine Signals Inc.
32108df3cSPrameela Rani Garnepudi  *
42108df3cSPrameela Rani Garnepudi  * Permission to use, copy, modify, and/or distribute this software for any
52108df3cSPrameela Rani Garnepudi  * purpose with or without fee is hereby granted, provided that the above
62108df3cSPrameela Rani Garnepudi  * copyright notice and this permission notice appear in all copies.
72108df3cSPrameela Rani Garnepudi  *
82108df3cSPrameela Rani Garnepudi  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
92108df3cSPrameela Rani Garnepudi  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
102108df3cSPrameela Rani Garnepudi  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
112108df3cSPrameela Rani Garnepudi  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
122108df3cSPrameela Rani Garnepudi  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
132108df3cSPrameela Rani Garnepudi  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
142108df3cSPrameela Rani Garnepudi  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
152108df3cSPrameela Rani Garnepudi  */
162108df3cSPrameela Rani Garnepudi 
172108df3cSPrameela Rani Garnepudi #include "rsi_main.h"
182108df3cSPrameela Rani Garnepudi #include "rsi_coex.h"
192108df3cSPrameela Rani Garnepudi #include "rsi_mgmt.h"
202108df3cSPrameela Rani Garnepudi #include "rsi_hal.h"
212108df3cSPrameela Rani Garnepudi 
222108df3cSPrameela Rani Garnepudi static enum rsi_coex_queues rsi_coex_determine_coex_q
232108df3cSPrameela Rani Garnepudi 			(struct rsi_coex_ctrl_block *coex_cb)
242108df3cSPrameela Rani Garnepudi {
252108df3cSPrameela Rani Garnepudi 	enum rsi_coex_queues q_num = RSI_COEX_Q_INVALID;
262108df3cSPrameela Rani Garnepudi 
272108df3cSPrameela Rani Garnepudi 	if (skb_queue_len(&coex_cb->coex_tx_qs[RSI_COEX_Q_COMMON]) > 0)
282108df3cSPrameela Rani Garnepudi 		q_num = RSI_COEX_Q_COMMON;
292108df3cSPrameela Rani Garnepudi 	if (skb_queue_len(&coex_cb->coex_tx_qs[RSI_COEX_Q_BT]) > 0)
302108df3cSPrameela Rani Garnepudi 		q_num = RSI_COEX_Q_BT;
312108df3cSPrameela Rani Garnepudi 	if (skb_queue_len(&coex_cb->coex_tx_qs[RSI_COEX_Q_WLAN]) > 0)
322108df3cSPrameela Rani Garnepudi 		q_num = RSI_COEX_Q_WLAN;
332108df3cSPrameela Rani Garnepudi 
342108df3cSPrameela Rani Garnepudi 	return q_num;
352108df3cSPrameela Rani Garnepudi }
362108df3cSPrameela Rani Garnepudi 
372108df3cSPrameela Rani Garnepudi static void rsi_coex_sched_tx_pkts(struct rsi_coex_ctrl_block *coex_cb)
382108df3cSPrameela Rani Garnepudi {
392108df3cSPrameela Rani Garnepudi 	enum rsi_coex_queues coex_q = RSI_COEX_Q_INVALID;
402108df3cSPrameela Rani Garnepudi 	struct sk_buff *skb;
412108df3cSPrameela Rani Garnepudi 
422108df3cSPrameela Rani Garnepudi 	do {
432108df3cSPrameela Rani Garnepudi 		coex_q = rsi_coex_determine_coex_q(coex_cb);
442108df3cSPrameela Rani Garnepudi 		rsi_dbg(INFO_ZONE, "queue = %d\n", coex_q);
452108df3cSPrameela Rani Garnepudi 
46716b840cSSiva Rebbagondla 		if (coex_q == RSI_COEX_Q_BT) {
472108df3cSPrameela Rani Garnepudi 			skb = skb_dequeue(&coex_cb->coex_tx_qs[RSI_COEX_Q_BT]);
48716b840cSSiva Rebbagondla 			rsi_send_bt_pkt(coex_cb->priv, skb);
49716b840cSSiva Rebbagondla 		}
502108df3cSPrameela Rani Garnepudi 	} while (coex_q != RSI_COEX_Q_INVALID);
512108df3cSPrameela Rani Garnepudi }
522108df3cSPrameela Rani Garnepudi 
532108df3cSPrameela Rani Garnepudi static void rsi_coex_scheduler_thread(struct rsi_common *common)
542108df3cSPrameela Rani Garnepudi {
55*5f48e916SWu Yunchuan 	struct rsi_coex_ctrl_block *coex_cb = common->coex_cb;
562108df3cSPrameela Rani Garnepudi 	u32 timeout = EVENT_WAIT_FOREVER;
572108df3cSPrameela Rani Garnepudi 
582108df3cSPrameela Rani Garnepudi 	do {
592108df3cSPrameela Rani Garnepudi 		rsi_wait_event(&coex_cb->coex_tx_thread.event, timeout);
602108df3cSPrameela Rani Garnepudi 		rsi_reset_event(&coex_cb->coex_tx_thread.event);
612108df3cSPrameela Rani Garnepudi 
622108df3cSPrameela Rani Garnepudi 		rsi_coex_sched_tx_pkts(coex_cb);
632108df3cSPrameela Rani Garnepudi 	} while (atomic_read(&coex_cb->coex_tx_thread.thread_done) == 0);
642108df3cSPrameela Rani Garnepudi 
65cead1855SEric W. Biederman 	kthread_complete_and_exit(&coex_cb->coex_tx_thread.completion, 0);
662108df3cSPrameela Rani Garnepudi }
672108df3cSPrameela Rani Garnepudi 
682108df3cSPrameela Rani Garnepudi int rsi_coex_recv_pkt(struct rsi_common *common, u8 *msg)
692108df3cSPrameela Rani Garnepudi {
702108df3cSPrameela Rani Garnepudi 	u8 msg_type = msg[RSI_RX_DESC_MSG_TYPE_OFFSET];
712108df3cSPrameela Rani Garnepudi 
722108df3cSPrameela Rani Garnepudi 	switch (msg_type) {
732108df3cSPrameela Rani Garnepudi 	case COMMON_CARD_READY_IND:
742108df3cSPrameela Rani Garnepudi 		rsi_dbg(INFO_ZONE, "common card ready received\n");
75d76f8513SSiva Rebbagondla 		common->hibernate_resume = false;
762108df3cSPrameela Rani Garnepudi 		rsi_handle_card_ready(common, msg);
772108df3cSPrameela Rani Garnepudi 		break;
782108df3cSPrameela Rani Garnepudi 	case SLEEP_NOTIFY_IND:
792108df3cSPrameela Rani Garnepudi 		rsi_dbg(INFO_ZONE, "sleep notify received\n");
802108df3cSPrameela Rani Garnepudi 		rsi_mgmt_pkt_recv(common, msg);
812108df3cSPrameela Rani Garnepudi 		break;
822108df3cSPrameela Rani Garnepudi 	}
832108df3cSPrameela Rani Garnepudi 
842108df3cSPrameela Rani Garnepudi 	return 0;
852108df3cSPrameela Rani Garnepudi }
862108df3cSPrameela Rani Garnepudi 
872108df3cSPrameela Rani Garnepudi static inline int rsi_map_coex_q(u8 hal_queue)
882108df3cSPrameela Rani Garnepudi {
892108df3cSPrameela Rani Garnepudi 	switch (hal_queue) {
902108df3cSPrameela Rani Garnepudi 	case RSI_COEX_Q:
912108df3cSPrameela Rani Garnepudi 		return RSI_COEX_Q_COMMON;
922108df3cSPrameela Rani Garnepudi 	case RSI_WLAN_Q:
932108df3cSPrameela Rani Garnepudi 		return RSI_COEX_Q_WLAN;
942108df3cSPrameela Rani Garnepudi 	case RSI_BT_Q:
952108df3cSPrameela Rani Garnepudi 		return RSI_COEX_Q_BT;
962108df3cSPrameela Rani Garnepudi 	}
972108df3cSPrameela Rani Garnepudi 	return RSI_COEX_Q_INVALID;
982108df3cSPrameela Rani Garnepudi }
992108df3cSPrameela Rani Garnepudi 
1002108df3cSPrameela Rani Garnepudi int rsi_coex_send_pkt(void *priv, struct sk_buff *skb, u8 hal_queue)
1012108df3cSPrameela Rani Garnepudi {
102*5f48e916SWu Yunchuan 	struct rsi_common *common = priv;
103*5f48e916SWu Yunchuan 	struct rsi_coex_ctrl_block *coex_cb = common->coex_cb;
1042108df3cSPrameela Rani Garnepudi 	struct skb_info *tx_params = NULL;
1052108df3cSPrameela Rani Garnepudi 	enum rsi_coex_queues coex_q;
1062108df3cSPrameela Rani Garnepudi 	int status;
1072108df3cSPrameela Rani Garnepudi 
1082108df3cSPrameela Rani Garnepudi 	coex_q = rsi_map_coex_q(hal_queue);
1092108df3cSPrameela Rani Garnepudi 	if (coex_q == RSI_COEX_Q_INVALID) {
1102108df3cSPrameela Rani Garnepudi 		rsi_dbg(ERR_ZONE, "Invalid coex queue\n");
1112108df3cSPrameela Rani Garnepudi 		return -EINVAL;
1122108df3cSPrameela Rani Garnepudi 	}
1132108df3cSPrameela Rani Garnepudi 	if (coex_q != RSI_COEX_Q_COMMON &&
1142108df3cSPrameela Rani Garnepudi 	    coex_q != RSI_COEX_Q_WLAN) {
1152108df3cSPrameela Rani Garnepudi 		skb_queue_tail(&coex_cb->coex_tx_qs[coex_q], skb);
1162108df3cSPrameela Rani Garnepudi 		rsi_set_event(&coex_cb->coex_tx_thread.event);
1172108df3cSPrameela Rani Garnepudi 		return 0;
1182108df3cSPrameela Rani Garnepudi 	}
1192108df3cSPrameela Rani Garnepudi 	if (common->iface_down) {
1202108df3cSPrameela Rani Garnepudi 		tx_params =
1212108df3cSPrameela Rani Garnepudi 			(struct skb_info *)&IEEE80211_SKB_CB(skb)->driver_data;
1222108df3cSPrameela Rani Garnepudi 
1232108df3cSPrameela Rani Garnepudi 		if (!(tx_params->flags & INTERNAL_MGMT_PKT)) {
1242108df3cSPrameela Rani Garnepudi 			rsi_indicate_tx_status(common->priv, skb, -EINVAL);
1252108df3cSPrameela Rani Garnepudi 			return 0;
1262108df3cSPrameela Rani Garnepudi 		}
1272108df3cSPrameela Rani Garnepudi 	}
1282108df3cSPrameela Rani Garnepudi 
1292108df3cSPrameela Rani Garnepudi 	/* Send packet to hal */
1302108df3cSPrameela Rani Garnepudi 	if (skb->priority == MGMT_SOFT_Q)
1312108df3cSPrameela Rani Garnepudi 		status = rsi_send_mgmt_pkt(common, skb);
1322108df3cSPrameela Rani Garnepudi 	else
1332108df3cSPrameela Rani Garnepudi 		status = rsi_send_data_pkt(common, skb);
1342108df3cSPrameela Rani Garnepudi 
1352108df3cSPrameela Rani Garnepudi 	return status;
1362108df3cSPrameela Rani Garnepudi }
1372108df3cSPrameela Rani Garnepudi 
1382108df3cSPrameela Rani Garnepudi int rsi_coex_attach(struct rsi_common *common)
1392108df3cSPrameela Rani Garnepudi {
1402108df3cSPrameela Rani Garnepudi 	struct rsi_coex_ctrl_block *coex_cb;
1412108df3cSPrameela Rani Garnepudi 	int cnt;
1422108df3cSPrameela Rani Garnepudi 
1432108df3cSPrameela Rani Garnepudi 	coex_cb = kzalloc(sizeof(*coex_cb), GFP_KERNEL);
1442108df3cSPrameela Rani Garnepudi 	if (!coex_cb)
1452108df3cSPrameela Rani Garnepudi 		return -ENOMEM;
1462108df3cSPrameela Rani Garnepudi 
1472108df3cSPrameela Rani Garnepudi 	common->coex_cb = (void *)coex_cb;
1482108df3cSPrameela Rani Garnepudi 	coex_cb->priv = common;
1492108df3cSPrameela Rani Garnepudi 
1502108df3cSPrameela Rani Garnepudi 	/* Initialize co-ex queues */
1512108df3cSPrameela Rani Garnepudi 	for (cnt = 0; cnt < NUM_COEX_TX_QUEUES; cnt++)
1522108df3cSPrameela Rani Garnepudi 		skb_queue_head_init(&coex_cb->coex_tx_qs[cnt]);
1532108df3cSPrameela Rani Garnepudi 	rsi_init_event(&coex_cb->coex_tx_thread.event);
1542108df3cSPrameela Rani Garnepudi 
1552108df3cSPrameela Rani Garnepudi 	/* Initialize co-ex thread */
1562108df3cSPrameela Rani Garnepudi 	if (rsi_create_kthread(common,
1572108df3cSPrameela Rani Garnepudi 			       &coex_cb->coex_tx_thread,
1582108df3cSPrameela Rani Garnepudi 			       rsi_coex_scheduler_thread,
1592108df3cSPrameela Rani Garnepudi 			       "Coex-Tx-Thread")) {
1602108df3cSPrameela Rani Garnepudi 		rsi_dbg(ERR_ZONE, "%s: Unable to init tx thrd\n", __func__);
161956fb851SYuan Can 		kfree(coex_cb);
1622108df3cSPrameela Rani Garnepudi 		return -EINVAL;
1632108df3cSPrameela Rani Garnepudi 	}
1642108df3cSPrameela Rani Garnepudi 	return 0;
1652108df3cSPrameela Rani Garnepudi }
1662108df3cSPrameela Rani Garnepudi 
1672108df3cSPrameela Rani Garnepudi void rsi_coex_detach(struct rsi_common *common)
1682108df3cSPrameela Rani Garnepudi {
169*5f48e916SWu Yunchuan 	struct rsi_coex_ctrl_block *coex_cb = common->coex_cb;
1702108df3cSPrameela Rani Garnepudi 	int cnt;
1712108df3cSPrameela Rani Garnepudi 
1722108df3cSPrameela Rani Garnepudi 	rsi_kill_thread(&coex_cb->coex_tx_thread);
1732108df3cSPrameela Rani Garnepudi 
1742108df3cSPrameela Rani Garnepudi 	for (cnt = 0; cnt < NUM_COEX_TX_QUEUES; cnt++)
1752108df3cSPrameela Rani Garnepudi 		skb_queue_purge(&coex_cb->coex_tx_qs[cnt]);
1762108df3cSPrameela Rani Garnepudi 
1772108df3cSPrameela Rani Garnepudi 	kfree(coex_cb);
1782108df3cSPrameela Rani Garnepudi }
179