xref: /linux/drivers/net/wireless/rsi/rsi_91x_coex.c (revision 7a03124c1df503d612895ebcbb1fb8f6465399c9)
1*7a03124cSLee 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 {
552108df3cSPrameela Rani Garnepudi 	struct rsi_coex_ctrl_block *coex_cb =
562108df3cSPrameela Rani Garnepudi 		(struct rsi_coex_ctrl_block *)common->coex_cb;
572108df3cSPrameela Rani Garnepudi 	u32 timeout = EVENT_WAIT_FOREVER;
582108df3cSPrameela Rani Garnepudi 
592108df3cSPrameela Rani Garnepudi 	do {
602108df3cSPrameela Rani Garnepudi 		rsi_wait_event(&coex_cb->coex_tx_thread.event, timeout);
612108df3cSPrameela Rani Garnepudi 		rsi_reset_event(&coex_cb->coex_tx_thread.event);
622108df3cSPrameela Rani Garnepudi 
632108df3cSPrameela Rani Garnepudi 		rsi_coex_sched_tx_pkts(coex_cb);
642108df3cSPrameela Rani Garnepudi 	} while (atomic_read(&coex_cb->coex_tx_thread.thread_done) == 0);
652108df3cSPrameela Rani Garnepudi 
662108df3cSPrameela Rani Garnepudi 	complete_and_exit(&coex_cb->coex_tx_thread.completion, 0);
672108df3cSPrameela Rani Garnepudi }
682108df3cSPrameela Rani Garnepudi 
692108df3cSPrameela Rani Garnepudi int rsi_coex_recv_pkt(struct rsi_common *common, u8 *msg)
702108df3cSPrameela Rani Garnepudi {
712108df3cSPrameela Rani Garnepudi 	u8 msg_type = msg[RSI_RX_DESC_MSG_TYPE_OFFSET];
722108df3cSPrameela Rani Garnepudi 
732108df3cSPrameela Rani Garnepudi 	switch (msg_type) {
742108df3cSPrameela Rani Garnepudi 	case COMMON_CARD_READY_IND:
752108df3cSPrameela Rani Garnepudi 		rsi_dbg(INFO_ZONE, "common card ready received\n");
76d76f8513SSiva Rebbagondla 		common->hibernate_resume = false;
772108df3cSPrameela Rani Garnepudi 		rsi_handle_card_ready(common, msg);
782108df3cSPrameela Rani Garnepudi 		break;
792108df3cSPrameela Rani Garnepudi 	case SLEEP_NOTIFY_IND:
802108df3cSPrameela Rani Garnepudi 		rsi_dbg(INFO_ZONE, "sleep notify received\n");
812108df3cSPrameela Rani Garnepudi 		rsi_mgmt_pkt_recv(common, msg);
822108df3cSPrameela Rani Garnepudi 		break;
832108df3cSPrameela Rani Garnepudi 	}
842108df3cSPrameela Rani Garnepudi 
852108df3cSPrameela Rani Garnepudi 	return 0;
862108df3cSPrameela Rani Garnepudi }
872108df3cSPrameela Rani Garnepudi 
882108df3cSPrameela Rani Garnepudi static inline int rsi_map_coex_q(u8 hal_queue)
892108df3cSPrameela Rani Garnepudi {
902108df3cSPrameela Rani Garnepudi 	switch (hal_queue) {
912108df3cSPrameela Rani Garnepudi 	case RSI_COEX_Q:
922108df3cSPrameela Rani Garnepudi 		return RSI_COEX_Q_COMMON;
932108df3cSPrameela Rani Garnepudi 	case RSI_WLAN_Q:
942108df3cSPrameela Rani Garnepudi 		return RSI_COEX_Q_WLAN;
952108df3cSPrameela Rani Garnepudi 	case RSI_BT_Q:
962108df3cSPrameela Rani Garnepudi 		return RSI_COEX_Q_BT;
972108df3cSPrameela Rani Garnepudi 	}
982108df3cSPrameela Rani Garnepudi 	return RSI_COEX_Q_INVALID;
992108df3cSPrameela Rani Garnepudi }
1002108df3cSPrameela Rani Garnepudi 
1012108df3cSPrameela Rani Garnepudi int rsi_coex_send_pkt(void *priv, struct sk_buff *skb, u8 hal_queue)
1022108df3cSPrameela Rani Garnepudi {
1032108df3cSPrameela Rani Garnepudi 	struct rsi_common *common = (struct rsi_common *)priv;
1042108df3cSPrameela Rani Garnepudi 	struct rsi_coex_ctrl_block *coex_cb =
1052108df3cSPrameela Rani Garnepudi 		(struct rsi_coex_ctrl_block *)common->coex_cb;
1062108df3cSPrameela Rani Garnepudi 	struct skb_info *tx_params = NULL;
1072108df3cSPrameela Rani Garnepudi 	enum rsi_coex_queues coex_q;
1082108df3cSPrameela Rani Garnepudi 	int status;
1092108df3cSPrameela Rani Garnepudi 
1102108df3cSPrameela Rani Garnepudi 	coex_q = rsi_map_coex_q(hal_queue);
1112108df3cSPrameela Rani Garnepudi 	if (coex_q == RSI_COEX_Q_INVALID) {
1122108df3cSPrameela Rani Garnepudi 		rsi_dbg(ERR_ZONE, "Invalid coex queue\n");
1132108df3cSPrameela Rani Garnepudi 		return -EINVAL;
1142108df3cSPrameela Rani Garnepudi 	}
1152108df3cSPrameela Rani Garnepudi 	if (coex_q != RSI_COEX_Q_COMMON &&
1162108df3cSPrameela Rani Garnepudi 	    coex_q != RSI_COEX_Q_WLAN) {
1172108df3cSPrameela Rani Garnepudi 		skb_queue_tail(&coex_cb->coex_tx_qs[coex_q], skb);
1182108df3cSPrameela Rani Garnepudi 		rsi_set_event(&coex_cb->coex_tx_thread.event);
1192108df3cSPrameela Rani Garnepudi 		return 0;
1202108df3cSPrameela Rani Garnepudi 	}
1212108df3cSPrameela Rani Garnepudi 	if (common->iface_down) {
1222108df3cSPrameela Rani Garnepudi 		tx_params =
1232108df3cSPrameela Rani Garnepudi 			(struct skb_info *)&IEEE80211_SKB_CB(skb)->driver_data;
1242108df3cSPrameela Rani Garnepudi 
1252108df3cSPrameela Rani Garnepudi 		if (!(tx_params->flags & INTERNAL_MGMT_PKT)) {
1262108df3cSPrameela Rani Garnepudi 			rsi_indicate_tx_status(common->priv, skb, -EINVAL);
1272108df3cSPrameela Rani Garnepudi 			return 0;
1282108df3cSPrameela Rani Garnepudi 		}
1292108df3cSPrameela Rani Garnepudi 	}
1302108df3cSPrameela Rani Garnepudi 
1312108df3cSPrameela Rani Garnepudi 	/* Send packet to hal */
1322108df3cSPrameela Rani Garnepudi 	if (skb->priority == MGMT_SOFT_Q)
1332108df3cSPrameela Rani Garnepudi 		status = rsi_send_mgmt_pkt(common, skb);
1342108df3cSPrameela Rani Garnepudi 	else
1352108df3cSPrameela Rani Garnepudi 		status = rsi_send_data_pkt(common, skb);
1362108df3cSPrameela Rani Garnepudi 
1372108df3cSPrameela Rani Garnepudi 	return status;
1382108df3cSPrameela Rani Garnepudi }
1392108df3cSPrameela Rani Garnepudi 
1402108df3cSPrameela Rani Garnepudi int rsi_coex_attach(struct rsi_common *common)
1412108df3cSPrameela Rani Garnepudi {
1422108df3cSPrameela Rani Garnepudi 	struct rsi_coex_ctrl_block *coex_cb;
1432108df3cSPrameela Rani Garnepudi 	int cnt;
1442108df3cSPrameela Rani Garnepudi 
1452108df3cSPrameela Rani Garnepudi 	coex_cb = kzalloc(sizeof(*coex_cb), GFP_KERNEL);
1462108df3cSPrameela Rani Garnepudi 	if (!coex_cb)
1472108df3cSPrameela Rani Garnepudi 		return -ENOMEM;
1482108df3cSPrameela Rani Garnepudi 
1492108df3cSPrameela Rani Garnepudi 	common->coex_cb = (void *)coex_cb;
1502108df3cSPrameela Rani Garnepudi 	coex_cb->priv = common;
1512108df3cSPrameela Rani Garnepudi 
1522108df3cSPrameela Rani Garnepudi 	/* Initialize co-ex queues */
1532108df3cSPrameela Rani Garnepudi 	for (cnt = 0; cnt < NUM_COEX_TX_QUEUES; cnt++)
1542108df3cSPrameela Rani Garnepudi 		skb_queue_head_init(&coex_cb->coex_tx_qs[cnt]);
1552108df3cSPrameela Rani Garnepudi 	rsi_init_event(&coex_cb->coex_tx_thread.event);
1562108df3cSPrameela Rani Garnepudi 
1572108df3cSPrameela Rani Garnepudi 	/* Initialize co-ex thread */
1582108df3cSPrameela Rani Garnepudi 	if (rsi_create_kthread(common,
1592108df3cSPrameela Rani Garnepudi 			       &coex_cb->coex_tx_thread,
1602108df3cSPrameela Rani Garnepudi 			       rsi_coex_scheduler_thread,
1612108df3cSPrameela Rani Garnepudi 			       "Coex-Tx-Thread")) {
1622108df3cSPrameela Rani Garnepudi 		rsi_dbg(ERR_ZONE, "%s: Unable to init tx thrd\n", __func__);
1632108df3cSPrameela Rani Garnepudi 		return -EINVAL;
1642108df3cSPrameela Rani Garnepudi 	}
1652108df3cSPrameela Rani Garnepudi 	return 0;
1662108df3cSPrameela Rani Garnepudi }
1672108df3cSPrameela Rani Garnepudi 
1682108df3cSPrameela Rani Garnepudi void rsi_coex_detach(struct rsi_common *common)
1692108df3cSPrameela Rani Garnepudi {
1702108df3cSPrameela Rani Garnepudi 	struct rsi_coex_ctrl_block *coex_cb =
1712108df3cSPrameela Rani Garnepudi 		(struct rsi_coex_ctrl_block *)common->coex_cb;
1722108df3cSPrameela Rani Garnepudi 	int cnt;
1732108df3cSPrameela Rani Garnepudi 
1742108df3cSPrameela Rani Garnepudi 	rsi_kill_thread(&coex_cb->coex_tx_thread);
1752108df3cSPrameela Rani Garnepudi 
1762108df3cSPrameela Rani Garnepudi 	for (cnt = 0; cnt < NUM_COEX_TX_QUEUES; cnt++)
1772108df3cSPrameela Rani Garnepudi 		skb_queue_purge(&coex_cb->coex_tx_qs[cnt]);
1782108df3cSPrameela Rani Garnepudi 
1792108df3cSPrameela Rani Garnepudi 	kfree(coex_cb);
1802108df3cSPrameela Rani Garnepudi }
181