xref: /linux/drivers/net/wireless/morsemicro/mm81x/yaps.c (revision 91ec2035134982b98fab0609a9fd8480e8217dc1)
1b1906ceaSLachlan Hodges // SPDX-License-Identifier: GPL-2.0-only
2b1906ceaSLachlan Hodges /*
3b1906ceaSLachlan Hodges  * Copyright (c) 2017-2026 Morse Micro
4b1906ceaSLachlan Hodges  */
5b1906ceaSLachlan Hodges #include <linux/gpio.h>
6b1906ceaSLachlan Hodges #include <linux/random.h>
7b1906ceaSLachlan Hodges #include <linux/timer.h>
8b1906ceaSLachlan Hodges #include <linux/bitops.h>
9b1906ceaSLachlan Hodges #include <linux/slab.h>
10b1906ceaSLachlan Hodges #include "hif.h"
11b1906ceaSLachlan Hodges #include "ps.h"
12b1906ceaSLachlan Hodges #include "bus.h"
13b1906ceaSLachlan Hodges #include "command.h"
14b1906ceaSLachlan Hodges #include "skbq.h"
15b1906ceaSLachlan Hodges 
16b1906ceaSLachlan Hodges /* This is a fail safe timeout */
17b1906ceaSLachlan Hodges #define CHIP_FULL_RECOVERY_TIMEOUT_MS 30
18b1906ceaSLachlan Hodges 
19b1906ceaSLachlan Hodges /* Defined as the max number of MPDUs per AMPDU */
20b1906ceaSLachlan Hodges #define MAX_PKTS_PER_TX_TXN 16
21b1906ceaSLachlan Hodges #define MAX_PKTS_PER_RX_TXN 32
22b1906ceaSLachlan Hodges 
mm81x_yaps_alloc_pkt_buffers(struct mm81x_yaps * yaps)23b1906ceaSLachlan Hodges static int mm81x_yaps_alloc_pkt_buffers(struct mm81x_yaps *yaps)
24b1906ceaSLachlan Hodges {
25b1906ceaSLachlan Hodges 	yaps->hw.to_chip_pkts = kcalloc(MAX_PKTS_PER_TX_TXN,
26b1906ceaSLachlan Hodges 					sizeof(*yaps->hw.to_chip_pkts),
27b1906ceaSLachlan Hodges 					GFP_KERNEL);
28b1906ceaSLachlan Hodges 	if (!yaps->hw.to_chip_pkts)
29b1906ceaSLachlan Hodges 		return -ENOMEM;
30b1906ceaSLachlan Hodges 
31b1906ceaSLachlan Hodges 	yaps->hw.from_chip_pkts = kcalloc(MAX_PKTS_PER_RX_TXN,
32b1906ceaSLachlan Hodges 					  sizeof(*yaps->hw.from_chip_pkts),
33b1906ceaSLachlan Hodges 					  GFP_KERNEL);
34b1906ceaSLachlan Hodges 	if (!yaps->hw.from_chip_pkts) {
35b1906ceaSLachlan Hodges 		kfree(yaps->hw.to_chip_pkts);
36b1906ceaSLachlan Hodges 		yaps->hw.to_chip_pkts = NULL;
37b1906ceaSLachlan Hodges 		return -ENOMEM;
38b1906ceaSLachlan Hodges 	}
39b1906ceaSLachlan Hodges 
40b1906ceaSLachlan Hodges 	return 0;
41b1906ceaSLachlan Hodges }
42b1906ceaSLachlan Hodges 
mm81x_yaps_free_pkt_buffers(struct mm81x_yaps * yaps)43b1906ceaSLachlan Hodges static void mm81x_yaps_free_pkt_buffers(struct mm81x_yaps *yaps)
44b1906ceaSLachlan Hodges {
45b1906ceaSLachlan Hodges 	kfree(yaps->hw.from_chip_pkts);
46b1906ceaSLachlan Hodges 	yaps->hw.from_chip_pkts = NULL;
47b1906ceaSLachlan Hodges 	kfree(yaps->hw.to_chip_pkts);
48b1906ceaSLachlan Hodges 	yaps->hw.to_chip_pkts = NULL;
49b1906ceaSLachlan Hodges }
50b1906ceaSLachlan Hodges 
mm81x_yaps_write_pkts(struct mm81x_yaps * yaps,struct mm81x_yaps_pkt * pkts,int num_pkts,int * num_pkts_sent)51b1906ceaSLachlan Hodges static int mm81x_yaps_write_pkts(struct mm81x_yaps *yaps,
52b1906ceaSLachlan Hodges 				 struct mm81x_yaps_pkt *pkts, int num_pkts,
53b1906ceaSLachlan Hodges 				 int *num_pkts_sent)
54b1906ceaSLachlan Hodges {
55b1906ceaSLachlan Hodges 	return yaps->ops->write_pkts(yaps, pkts, num_pkts, num_pkts_sent);
56b1906ceaSLachlan Hodges }
57b1906ceaSLachlan Hodges 
mm81x_yaps_read_pkts(struct mm81x_yaps * yaps,struct mm81x_yaps_pkt * pkts,int num_pkts_max,int * num_pkts_received)58b1906ceaSLachlan Hodges static int mm81x_yaps_read_pkts(struct mm81x_yaps *yaps,
59b1906ceaSLachlan Hodges 				struct mm81x_yaps_pkt *pkts, int num_pkts_max,
60b1906ceaSLachlan Hodges 				int *num_pkts_received)
61b1906ceaSLachlan Hodges {
62b1906ceaSLachlan Hodges 	return yaps->ops->read_pkts(yaps, pkts, num_pkts_max,
63b1906ceaSLachlan Hodges 				    num_pkts_received);
64b1906ceaSLachlan Hodges }
65b1906ceaSLachlan Hodges 
mm81x_yaps_update_status(struct mm81x_yaps * yaps)66b1906ceaSLachlan Hodges static int mm81x_yaps_update_status(struct mm81x_yaps *yaps)
67b1906ceaSLachlan Hodges {
68b1906ceaSLachlan Hodges 	return yaps->ops->update_status(yaps);
69b1906ceaSLachlan Hodges }
70b1906ceaSLachlan Hodges 
71b1906ceaSLachlan Hodges /* Mappings between sk_buff, skbq and yaps */
mm81x_yaps_tc_q_from_aci(struct mm81x * mors,int aci)72b1906ceaSLachlan Hodges static struct mm81x_skbq *mm81x_yaps_tc_q_from_aci(struct mm81x *mors, int aci)
73b1906ceaSLachlan Hodges {
74b1906ceaSLachlan Hodges 	struct mm81x_yaps *yaps = &mors->hif.u.yaps;
75b1906ceaSLachlan Hodges 
76b1906ceaSLachlan Hodges 	if (aci >= ARRAY_SIZE(yaps->data_tx_qs))
77b1906ceaSLachlan Hodges 		return NULL;
78b1906ceaSLachlan Hodges 	return &yaps->data_tx_qs[aci];
79b1906ceaSLachlan Hodges }
80b1906ceaSLachlan Hodges 
mm81x_yaps_get_tx_qs(struct mm81x * mors,struct mm81x_skbq ** qs,int * num_qs)81b1906ceaSLachlan Hodges static void mm81x_yaps_get_tx_qs(struct mm81x *mors, struct mm81x_skbq **qs,
82b1906ceaSLachlan Hodges 				 int *num_qs)
83b1906ceaSLachlan Hodges {
84b1906ceaSLachlan Hodges 	*qs = mors->hif.u.yaps.data_tx_qs;
85b1906ceaSLachlan Hodges 	*num_qs = YAPS_TX_SKBQ_MAX;
86b1906ceaSLachlan Hodges }
87b1906ceaSLachlan Hodges 
mm81x_yaps_get_bcn_tc_q(struct mm81x * mors)88b1906ceaSLachlan Hodges static struct mm81x_skbq *mm81x_yaps_get_bcn_tc_q(struct mm81x *mors)
89b1906ceaSLachlan Hodges {
90b1906ceaSLachlan Hodges 	return &mors->hif.u.yaps.beacon_q;
91b1906ceaSLachlan Hodges }
92b1906ceaSLachlan Hodges 
mm81x_yaps_get_mgmt_tc_q(struct mm81x * mors)93b1906ceaSLachlan Hodges static struct mm81x_skbq *mm81x_yaps_get_mgmt_tc_q(struct mm81x *mors)
94b1906ceaSLachlan Hodges {
95b1906ceaSLachlan Hodges 	return &mors->hif.u.yaps.mgmt_q;
96b1906ceaSLachlan Hodges }
97b1906ceaSLachlan Hodges 
mm81x_yaps_get_tx_cmd_queue(struct mm81x * mors)98b1906ceaSLachlan Hodges static struct mm81x_skbq *mm81x_yaps_get_tx_cmd_queue(struct mm81x *mors)
99b1906ceaSLachlan Hodges {
100b1906ceaSLachlan Hodges 	return &mors->hif.u.yaps.cmd_q;
101b1906ceaSLachlan Hodges }
102b1906ceaSLachlan Hodges 
mm81x_yaps_irq_handler(struct mm81x * mors,u32 status)103b1906ceaSLachlan Hodges static int mm81x_yaps_irq_handler(struct mm81x *mors, u32 status)
104b1906ceaSLachlan Hodges {
105b1906ceaSLachlan Hodges 	if (status & BIT(MM81X_INT_YAPS_FC_PKT_WAITING_IRQN))
106b1906ceaSLachlan Hodges 		set_bit(MM81X_HIF_EVT_RX_PEND, &mors->hif.event_flags);
107b1906ceaSLachlan Hodges 
108b1906ceaSLachlan Hodges 	if (status & BIT(MM81X_INT_YAPS_FC_PACKET_FREED_UP_IRQN)) {
109b1906ceaSLachlan Hodges 		timer_delete_sync_try(&mors->hif.u.yaps.chip_queue_full.timer);
110b1906ceaSLachlan Hodges 		set_bit(MM81X_HIF_EVT_TX_PACKET_FREED_UP_PEND,
111b1906ceaSLachlan Hodges 			&mors->hif.event_flags);
112b1906ceaSLachlan Hodges 	}
113b1906ceaSLachlan Hodges 
114b1906ceaSLachlan Hodges 	queue_work(mors->chip_wq, &mors->hif_work);
115b1906ceaSLachlan Hodges 	return 0;
116b1906ceaSLachlan Hodges }
117b1906ceaSLachlan Hodges 
118b1906ceaSLachlan Hodges const struct mm81x_hif_ops mm81x_yaps_ops = {
119b1906ceaSLachlan Hodges 	.init = mm81x_yaps_init,
120b1906ceaSLachlan Hodges 	.flush_tx_data = mm81x_yaps_flush_tx_data,
121b1906ceaSLachlan Hodges 	.flush_cmds = mm81x_yaps_flush_cmds,
122b1906ceaSLachlan Hodges 	.get_tx_status_pending_count = mm81x_yaps_get_tx_status_pending_count,
123b1906ceaSLachlan Hodges 	.get_tx_buffered_count = mm81x_yaps_get_tx_buffered_count,
124b1906ceaSLachlan Hodges 	.finish = mm81x_yaps_finish,
125b1906ceaSLachlan Hodges 	.skbq_get_tx_qs = mm81x_yaps_get_tx_qs,
126b1906ceaSLachlan Hodges 	.get_tx_beacon_queue = mm81x_yaps_get_bcn_tc_q,
127b1906ceaSLachlan Hodges 	.get_tx_mgmt_queue = mm81x_yaps_get_mgmt_tc_q,
128b1906ceaSLachlan Hodges 	.get_tx_cmd_queue = mm81x_yaps_get_tx_cmd_queue,
129b1906ceaSLachlan Hodges 	.get_tx_data_queue = mm81x_yaps_tc_q_from_aci,
130b1906ceaSLachlan Hodges 	.handle_irq = mm81x_yaps_irq_handler
131b1906ceaSLachlan Hodges };
132b1906ceaSLachlan Hodges 
mm81x_yaps_read_pkt(struct mm81x_yaps * yaps,struct sk_buff * skb)133b1906ceaSLachlan Hodges static int mm81x_yaps_read_pkt(struct mm81x_yaps *yaps, struct sk_buff *skb)
134b1906ceaSLachlan Hodges {
135b1906ceaSLachlan Hodges 	struct mm81x *mors = yaps->mors;
136b1906ceaSLachlan Hodges 	struct sk_buff_head skbq;
137b1906ceaSLachlan Hodges 	struct mm81x_skbq *mq = NULL;
138b1906ceaSLachlan Hodges 	struct mm81x_skb_hdr *hdr;
139b1906ceaSLachlan Hodges 	int skb_bytes_remaining;
140b1906ceaSLachlan Hodges 	int skb_len;
141b1906ceaSLachlan Hodges 	int ret = 0;
142b1906ceaSLachlan Hodges 
143b1906ceaSLachlan Hodges 	if (!skb) {
144b1906ceaSLachlan Hodges 		ret = -EINVAL;
145b1906ceaSLachlan Hodges 		goto exit_return_page;
146b1906ceaSLachlan Hodges 	}
147b1906ceaSLachlan Hodges 
148b1906ceaSLachlan Hodges 	__skb_queue_head_init(&skbq);
149b1906ceaSLachlan Hodges 
150b1906ceaSLachlan Hodges 	hdr = (struct mm81x_skb_hdr *)skb->data;
151b1906ceaSLachlan Hodges 	if (hdr->sync != MM81X_SKB_HEADER_SYNC) {
152b1906ceaSLachlan Hodges 		dev_err(mors->dev, "sync value error [0xAA:%d], hdr.len %d",
153b1906ceaSLachlan Hodges 			hdr->sync, hdr->len);
154b1906ceaSLachlan Hodges 		ret = -EIO;
155b1906ceaSLachlan Hodges 		goto exit_return_page;
156b1906ceaSLachlan Hodges 	}
157b1906ceaSLachlan Hodges 
158b1906ceaSLachlan Hodges 	if (yaps->mors->hif.validate_skb_checksum &&
159b1906ceaSLachlan Hodges 	    !mm81x_skbq_validate_checksum(skb->data)) {
160b1906ceaSLachlan Hodges 		dev_dbg(yaps->mors->dev,
161b1906ceaSLachlan Hodges 			"SKB checksum is invalid hdr:[c:%02X s:%02X len:%d]",
162b1906ceaSLachlan Hodges 			hdr->channel, hdr->sync, hdr->len);
163b1906ceaSLachlan Hodges 
164b1906ceaSLachlan Hodges 		if (hdr->channel != MM81X_SKB_CHAN_TX_STATUS) {
165b1906ceaSLachlan Hodges 			ret = -EIO;
166b1906ceaSLachlan Hodges 			goto exit;
167b1906ceaSLachlan Hodges 		}
168b1906ceaSLachlan Hodges 	}
169b1906ceaSLachlan Hodges 
170b1906ceaSLachlan Hodges 	switch (hdr->channel) {
171b1906ceaSLachlan Hodges 	case MM81X_SKB_CHAN_DATA:
172b1906ceaSLachlan Hodges 	case MM81X_SKB_CHAN_NDP_FRAMES:
173b1906ceaSLachlan Hodges 	case MM81X_SKB_CHAN_TX_STATUS:
174b1906ceaSLachlan Hodges 	case MM81X_SKB_CHAN_DATA_NOACK:
175b1906ceaSLachlan Hodges 	case MM81X_SKB_CHAN_BEACON:
176b1906ceaSLachlan Hodges 	case MM81X_SKB_CHAN_MGMT:
177b1906ceaSLachlan Hodges 		mq = &yaps->data_rx_q;
178b1906ceaSLachlan Hodges 		break;
179b1906ceaSLachlan Hodges 	case MM81X_SKB_CHAN_COMMAND:
180b1906ceaSLachlan Hodges 		mq = &yaps->cmd_resp_q;
181b1906ceaSLachlan Hodges 		break;
182b1906ceaSLachlan Hodges 	default:
183b1906ceaSLachlan Hodges 		dev_err(mors->dev, "channel value error [%d]", hdr->channel);
184b1906ceaSLachlan Hodges 		ret = -EIO;
185b1906ceaSLachlan Hodges 		goto exit_return_page;
186b1906ceaSLachlan Hodges 	}
187b1906ceaSLachlan Hodges 
188b1906ceaSLachlan Hodges 	skb_len = sizeof(*hdr) + hdr->offset + le16_to_cpu(hdr->len);
189b1906ceaSLachlan Hodges 	skb_bytes_remaining = mm81x_skbq_space(mq);
190b1906ceaSLachlan Hodges 
191b1906ceaSLachlan Hodges 	if (skb_len > skb_bytes_remaining) {
192b1906ceaSLachlan Hodges 		dev_err(mors->dev,
193b1906ceaSLachlan Hodges 			"Page will not fit in SKBQ, dropping - len %d remain %d",
194b1906ceaSLachlan Hodges 			skb_len, skb_bytes_remaining);
195b1906ceaSLachlan Hodges 		ret = -ENOMEM;
196b1906ceaSLachlan Hodges 		/* Queue work to clear backlog */
197b1906ceaSLachlan Hodges 		queue_work(mors->net_wq, &mq->dispatch_work);
198b1906ceaSLachlan Hodges 		goto exit_return_page;
199b1906ceaSLachlan Hodges 	}
200b1906ceaSLachlan Hodges 
201b1906ceaSLachlan Hodges 	skb_trim(skb, skb_len);
202b1906ceaSLachlan Hodges 	__skb_queue_tail(&skbq, skb);
203b1906ceaSLachlan Hodges 
204b1906ceaSLachlan Hodges 	if (skb_queue_len(&skbq))
205b1906ceaSLachlan Hodges 		mm81x_skbq_enq(mq, &skbq);
206b1906ceaSLachlan Hodges 
207b1906ceaSLachlan Hodges 	/* push packets up in a different context */
208b1906ceaSLachlan Hodges 	queue_work(mors->net_wq, &mq->dispatch_work);
209b1906ceaSLachlan Hodges 
210b1906ceaSLachlan Hodges 	goto exit;
211b1906ceaSLachlan Hodges 
212b1906ceaSLachlan Hodges exit_return_page:
213b1906ceaSLachlan Hodges 	if (ret && mq) {
214b1906ceaSLachlan Hodges 		dev_err(mors->dev, "failed %d", ret);
215b1906ceaSLachlan Hodges 		mm81x_skbq_purge(mq, &skbq);
216b1906ceaSLachlan Hodges 		goto exit;
217b1906ceaSLachlan Hodges 	}
218b1906ceaSLachlan Hodges 
219b1906ceaSLachlan Hodges exit:
220b1906ceaSLachlan Hodges 	if (ret && skb)
221b1906ceaSLachlan Hodges 		dev_kfree_skb(skb);
222b1906ceaSLachlan Hodges 
223b1906ceaSLachlan Hodges 	return ret;
224b1906ceaSLachlan Hodges }
225b1906ceaSLachlan Hodges 
mm81x_yaps_tx(struct mm81x_yaps * yaps,struct mm81x_skbq * mq)226b1906ceaSLachlan Hodges static int mm81x_yaps_tx(struct mm81x_yaps *yaps, struct mm81x_skbq *mq)
227b1906ceaSLachlan Hodges {
228b1906ceaSLachlan Hodges 	int i;
229b1906ceaSLachlan Hodges 	int ret = 0;
230b1906ceaSLachlan Hodges 	int num_skbs = 0;
231b1906ceaSLachlan Hodges 	int tc_pkt_idx = 0;
232b1906ceaSLachlan Hodges 	int num_pkts_sent = 0;
233b1906ceaSLachlan Hodges 	struct sk_buff *skb;
234b1906ceaSLachlan Hodges 	struct sk_buff_head skbq_to_send;
235b1906ceaSLachlan Hodges 	struct sk_buff_head skbq_sent;
236b1906ceaSLachlan Hodges 	struct sk_buff_head skbq_failed;
237b1906ceaSLachlan Hodges 	struct sk_buff *pfirst, *pnext;
238b1906ceaSLachlan Hodges 	struct mm81x *mors = yaps->mors;
239b1906ceaSLachlan Hodges 	struct mm81x_skb_hdr *hdr;
240b1906ceaSLachlan Hodges 
241b1906ceaSLachlan Hodges 	/* Check there is something on the queue */
242b1906ceaSLachlan Hodges 	spin_lock_bh(&mq->lock);
243b1906ceaSLachlan Hodges 	skb = skb_peek(&mq->skbq);
244b1906ceaSLachlan Hodges 	spin_unlock_bh(&mq->lock);
245b1906ceaSLachlan Hodges 	if (!skb)
246b1906ceaSLachlan Hodges 		return 0;
247b1906ceaSLachlan Hodges 
248b1906ceaSLachlan Hodges 	__skb_queue_head_init(&skbq_to_send);
249b1906ceaSLachlan Hodges 	__skb_queue_head_init(&skbq_sent);
250b1906ceaSLachlan Hodges 	__skb_queue_head_init(&skbq_failed);
251b1906ceaSLachlan Hodges 
252b1906ceaSLachlan Hodges 	if (mq == &yaps->cmd_q)
253b1906ceaSLachlan Hodges 		/* Purge timed-out commands (this should not happen) */
254b1906ceaSLachlan Hodges 		mm81x_skbq_purge(mq, &mq->pending);
255b1906ceaSLachlan Hodges 	else if (mq == &yaps->mgmt_q && skb_queue_len(&mq->skbq) > 0)
256b1906ceaSLachlan Hodges 		/*
257b1906ceaSLachlan Hodges 		 * Purge old mgmt frames that have not been sent due to
258b1906ceaSLachlan Hodges 		 * congestion
259b1906ceaSLachlan Hodges 		 */
260b1906ceaSLachlan Hodges 		mm81x_skbq_purge_aged(mors, mq);
261b1906ceaSLachlan Hodges 
262b1906ceaSLachlan Hodges 	num_skbs =
263b1906ceaSLachlan Hodges 		mm81x_skbq_deq_num_skb(mq, &skbq_to_send, MAX_PKTS_PER_TX_TXN);
264b1906ceaSLachlan Hodges 
265b1906ceaSLachlan Hodges 	skb_queue_walk_safe(&skbq_to_send, pfirst, pnext) {
266b1906ceaSLachlan Hodges 		enum mm81x_yaps_to_chip_q tc_queue;
267b1906ceaSLachlan Hodges 
268b1906ceaSLachlan Hodges 		hdr = (struct mm81x_skb_hdr *)pfirst->data;
269b1906ceaSLachlan Hodges 		switch (hdr->channel) {
270b1906ceaSLachlan Hodges 		case MM81X_SKB_CHAN_COMMAND:
271b1906ceaSLachlan Hodges 			tc_queue = MM81X_YAPS_CMD_Q;
272b1906ceaSLachlan Hodges 			break;
273b1906ceaSLachlan Hodges 		case MM81X_SKB_CHAN_BEACON:
274b1906ceaSLachlan Hodges 			tc_queue = MM81X_YAPS_BEACON_Q;
275b1906ceaSLachlan Hodges 			break;
276b1906ceaSLachlan Hodges 		case MM81X_SKB_CHAN_MGMT:
277b1906ceaSLachlan Hodges 			tc_queue = MM81X_YAPS_MGMT_Q;
278b1906ceaSLachlan Hodges 			break;
279b1906ceaSLachlan Hodges 		default:
280b1906ceaSLachlan Hodges 			tc_queue = MM81X_YAPS_TX_Q;
281b1906ceaSLachlan Hodges 			break;
282b1906ceaSLachlan Hodges 		}
283b1906ceaSLachlan Hodges 		yaps->hw.to_chip_pkts[tc_pkt_idx].tc_queue = tc_queue;
284b1906ceaSLachlan Hodges 		yaps->hw.to_chip_pkts[tc_pkt_idx].skb = pfirst;
285b1906ceaSLachlan Hodges 		tc_pkt_idx++;
286b1906ceaSLachlan Hodges 	}
287b1906ceaSLachlan Hodges 
288b1906ceaSLachlan Hodges 	/* Send queued packets to chip */
289b1906ceaSLachlan Hodges 	ret = mm81x_yaps_update_status(yaps);
290b1906ceaSLachlan Hodges 	if (ret)
291b1906ceaSLachlan Hodges 		return ret;
292b1906ceaSLachlan Hodges 
293b1906ceaSLachlan Hodges 	ret = mm81x_yaps_write_pkts(yaps, yaps->hw.to_chip_pkts, tc_pkt_idx,
294b1906ceaSLachlan Hodges 				    &num_pkts_sent);
295b1906ceaSLachlan Hodges 
296b1906ceaSLachlan Hodges 	/* Move sent packets to done queue */
297b1906ceaSLachlan Hodges 	for (i = 0; i < num_pkts_sent; ++i) {
298b1906ceaSLachlan Hodges 		pfirst = __skb_dequeue(&skbq_to_send);
299b1906ceaSLachlan Hodges 		__skb_queue_tail(&skbq_sent, pfirst);
300b1906ceaSLachlan Hodges 	}
301b1906ceaSLachlan Hodges 
302b1906ceaSLachlan Hodges 	for (i = num_pkts_sent; i < num_skbs; ++i) {
303b1906ceaSLachlan Hodges 		pfirst = __skb_dequeue(&skbq_to_send);
304b1906ceaSLachlan Hodges 		__skb_queue_tail(&skbq_failed, pfirst);
305b1906ceaSLachlan Hodges 	}
306b1906ceaSLachlan Hodges 
307b1906ceaSLachlan Hodges 	if (skb_queue_len(&skbq_failed) > 0) {
308b1906ceaSLachlan Hodges 		mm81x_skbq_enq_prepend(mq, &skbq_failed);
309b1906ceaSLachlan Hodges 
310b1906ceaSLachlan Hodges 		/* queue full, can't requeue */
311b1906ceaSLachlan Hodges 		if (skb_queue_len(&skbq_failed) > 0) {
312b1906ceaSLachlan Hodges 			dev_warn(mors->dev,
313b1906ceaSLachlan Hodges 				 "can't requeue failed pkts, purging");
314b1906ceaSLachlan Hodges 			__skb_queue_purge(&skbq_failed);
315b1906ceaSLachlan Hodges 		}
316b1906ceaSLachlan Hodges 	}
317b1906ceaSLachlan Hodges 
318b1906ceaSLachlan Hodges 	if (skb_queue_len(&skbq_sent) > 0)
319b1906ceaSLachlan Hodges 		mm81x_skbq_tx_complete(mq, &skbq_sent);
320b1906ceaSLachlan Hodges 
321b1906ceaSLachlan Hodges 	return ret;
322b1906ceaSLachlan Hodges }
323b1906ceaSLachlan Hodges 
324b1906ceaSLachlan Hodges /* Returns true if there are TX data pages waiting to be sent */
mm81x_yaps_tx_data_handler(struct mm81x_yaps * yaps)325b1906ceaSLachlan Hodges static bool mm81x_yaps_tx_data_handler(struct mm81x_yaps *yaps)
326b1906ceaSLachlan Hodges {
327b1906ceaSLachlan Hodges 	s16 aci;
328b1906ceaSLachlan Hodges 	u32 count = 0;
329b1906ceaSLachlan Hodges 	struct mm81x *mors = yaps->mors;
330b1906ceaSLachlan Hodges 
331b1906ceaSLachlan Hodges 	for (aci = MM81X_ACI_VO; aci >= 0; aci--) {
332b1906ceaSLachlan Hodges 		struct mm81x_skbq *data_q = mm81x_yaps_tc_q_from_aci(mors, aci);
333b1906ceaSLachlan Hodges 
334b1906ceaSLachlan Hodges 		if (!mm81x_is_data_tx_allowed(mors))
335b1906ceaSLachlan Hodges 			break;
336b1906ceaSLachlan Hodges 
337b1906ceaSLachlan Hodges 		yaps->chip_queue_full.is_full = mm81x_yaps_tx(yaps, data_q);
338b1906ceaSLachlan Hodges 		count += mm81x_skbq_count(data_q);
339b1906ceaSLachlan Hodges 
340b1906ceaSLachlan Hodges 		if (yaps->chip_queue_full.is_full)
341b1906ceaSLachlan Hodges 			break;
342b1906ceaSLachlan Hodges 
343b1906ceaSLachlan Hodges 		if (aci == MM81X_ACI_BE)
344b1906ceaSLachlan Hodges 			break;
345b1906ceaSLachlan Hodges 	}
346b1906ceaSLachlan Hodges 
347b1906ceaSLachlan Hodges 	/*
348b1906ceaSLachlan Hodges 	 * Data has potentially been transmitted from the data SKBQs.
349b1906ceaSLachlan Hodges 	 * If the mac80211 TX data Qs were previously stopped, now would
350b1906ceaSLachlan Hodges 	 * be a good time to check if they can be started again.
351b1906ceaSLachlan Hodges 	 */
352b1906ceaSLachlan Hodges 	mm81x_skbq_may_wake_tx_queues(mors);
353b1906ceaSLachlan Hodges 
354b1906ceaSLachlan Hodges 	return (count > 0) && mm81x_is_data_tx_allowed(mors);
355b1906ceaSLachlan Hodges }
356b1906ceaSLachlan Hodges 
357b1906ceaSLachlan Hodges /* Returns true if there are commands waiting to be sent */
mm81x_yaps_tx_cmd_handler(struct mm81x_yaps * yaps)358b1906ceaSLachlan Hodges static bool mm81x_yaps_tx_cmd_handler(struct mm81x_yaps *yaps)
359b1906ceaSLachlan Hodges {
360b1906ceaSLachlan Hodges 	struct mm81x_skbq *cmd_q = &yaps->cmd_q;
361b1906ceaSLachlan Hodges 
362b1906ceaSLachlan Hodges 	mm81x_yaps_tx(yaps, cmd_q);
363b1906ceaSLachlan Hodges 
364b1906ceaSLachlan Hodges 	return mm81x_skbq_count(cmd_q) > 0;
365b1906ceaSLachlan Hodges }
366b1906ceaSLachlan Hodges 
mm81x_yaps_tx_beacon_handler(struct mm81x_yaps * yaps)367b1906ceaSLachlan Hodges static bool mm81x_yaps_tx_beacon_handler(struct mm81x_yaps *yaps)
368b1906ceaSLachlan Hodges {
369b1906ceaSLachlan Hodges 	struct mm81x_skbq *beacon_q = &yaps->beacon_q;
370b1906ceaSLachlan Hodges 
371b1906ceaSLachlan Hodges 	mm81x_yaps_tx(yaps, beacon_q);
372b1906ceaSLachlan Hodges 
373b1906ceaSLachlan Hodges 	return mm81x_skbq_count(beacon_q) > 0;
374b1906ceaSLachlan Hodges }
375b1906ceaSLachlan Hodges 
mm81x_yaps_tx_mgmt_handler(struct mm81x_yaps * yaps)376b1906ceaSLachlan Hodges static bool mm81x_yaps_tx_mgmt_handler(struct mm81x_yaps *yaps)
377b1906ceaSLachlan Hodges {
378b1906ceaSLachlan Hodges 	struct mm81x_skbq *mgmt_q = &yaps->mgmt_q;
379b1906ceaSLachlan Hodges 
380b1906ceaSLachlan Hodges 	mm81x_yaps_tx(yaps, mgmt_q);
381b1906ceaSLachlan Hodges 
382b1906ceaSLachlan Hodges 	return mm81x_skbq_count(mgmt_q) > 0;
383b1906ceaSLachlan Hodges }
384b1906ceaSLachlan Hodges 
385b1906ceaSLachlan Hodges /* Returns true if there are populated RX pages left in the device */
mm81x_yaps_rx_handler(struct mm81x_yaps * yaps)386b1906ceaSLachlan Hodges static bool mm81x_yaps_rx_handler(struct mm81x_yaps *yaps)
387b1906ceaSLachlan Hodges {
388b1906ceaSLachlan Hodges 	int ret = 0;
389b1906ceaSLachlan Hodges 	int i;
390b1906ceaSLachlan Hodges 	int num_pks_received;
391b1906ceaSLachlan Hodges 
392b1906ceaSLachlan Hodges 	ret = mm81x_yaps_update_status(yaps);
393b1906ceaSLachlan Hodges 	if (ret)
394b1906ceaSLachlan Hodges 		goto exit;
395b1906ceaSLachlan Hodges 
396b1906ceaSLachlan Hodges 	ret = mm81x_yaps_read_pkts(yaps, yaps->hw.from_chip_pkts,
397b1906ceaSLachlan Hodges 				   MAX_PKTS_PER_RX_TXN, &num_pks_received);
398b1906ceaSLachlan Hodges 	if (ret && ret != -EAGAIN) {
399b1906ceaSLachlan Hodges 		dev_err(yaps->mors->dev, "YAPS read_pkts fail: %d", ret);
400b1906ceaSLachlan Hodges 		goto exit;
401b1906ceaSLachlan Hodges 	}
402b1906ceaSLachlan Hodges 
403b1906ceaSLachlan Hodges 	for (i = 0; i < num_pks_received; ++i) {
404b1906ceaSLachlan Hodges 		mm81x_yaps_read_pkt(yaps, yaps->hw.from_chip_pkts[i].skb);
405b1906ceaSLachlan Hodges 		yaps->hw.from_chip_pkts[i].skb = NULL;
406b1906ceaSLachlan Hodges 	}
407b1906ceaSLachlan Hodges 
408b1906ceaSLachlan Hodges exit:
409b1906ceaSLachlan Hodges 	if (ret == -ENOMEM || ret == -EAGAIN)
410b1906ceaSLachlan Hodges 		return true;
411b1906ceaSLachlan Hodges 	else
412b1906ceaSLachlan Hodges 		return false;
413b1906ceaSLachlan Hodges }
414b1906ceaSLachlan Hodges 
mm81x_yaps_stale_tx_work(struct work_struct * work)415b1906ceaSLachlan Hodges void mm81x_yaps_stale_tx_work(struct work_struct *work)
416b1906ceaSLachlan Hodges {
417b1906ceaSLachlan Hodges 	int i;
418b1906ceaSLachlan Hodges 	int flushed = 0;
419b1906ceaSLachlan Hodges 	struct mm81x *mors = container_of(work, struct mm81x, tx_stale_work);
420b1906ceaSLachlan Hodges 	struct mm81x_yaps *yaps;
421b1906ceaSLachlan Hodges 
422b1906ceaSLachlan Hodges 	yaps = &mors->hif.u.yaps;
423b1906ceaSLachlan Hodges 	flushed += mm81x_skbq_check_for_stale_tx(mors, &yaps->beacon_q);
424b1906ceaSLachlan Hodges 	flushed += mm81x_skbq_check_for_stale_tx(mors, &yaps->mgmt_q);
425b1906ceaSLachlan Hodges 
426b1906ceaSLachlan Hodges 	for (i = 0; i < ARRAY_SIZE(yaps->data_tx_qs); i++)
427b1906ceaSLachlan Hodges 		flushed += mm81x_skbq_check_for_stale_tx(mors,
428b1906ceaSLachlan Hodges 							 &yaps->data_tx_qs[i]);
429b1906ceaSLachlan Hodges 
430b1906ceaSLachlan Hodges 	if (!flushed)
431b1906ceaSLachlan Hodges 		return;
432b1906ceaSLachlan Hodges 
433b1906ceaSLachlan Hodges 	dev_dbg(mors->dev, "Flushed %d stale TX SKBs", flushed);
434b1906ceaSLachlan Hodges 
435b1906ceaSLachlan Hodges 	if (mors->ps.enable && !mors->ps.suspended &&
436b1906ceaSLachlan Hodges 	    (mm81x_yaps_get_tx_buffered_count(mors) == 0)) {
437b1906ceaSLachlan Hodges 		/* Evaluate ps to check if it was gated on a stale tx status */
438b1906ceaSLachlan Hodges 		queue_delayed_work(mors->chip_wq, &mors->ps.delayed_eval_work,
439b1906ceaSLachlan Hodges 				   0);
440b1906ceaSLachlan Hodges 	}
441b1906ceaSLachlan Hodges }
442b1906ceaSLachlan Hodges 
mm81x_yaps_work(struct work_struct * work)443b1906ceaSLachlan Hodges void mm81x_yaps_work(struct work_struct *work)
444b1906ceaSLachlan Hodges {
445b1906ceaSLachlan Hodges 	struct mm81x *mors = container_of(work, struct mm81x, hif_work);
446b1906ceaSLachlan Hodges 	unsigned long *flags = &mors->hif.event_flags;
447b1906ceaSLachlan Hodges 	struct mm81x_yaps *yaps = &mors->hif.u.yaps;
448b1906ceaSLachlan Hodges 
449b1906ceaSLachlan Hodges 	if (test_bit(MM81X_STATE_CHIP_UNRESPONSIVE, &mors->state_flags))
450b1906ceaSLachlan Hodges 		return;
451b1906ceaSLachlan Hodges 
452b1906ceaSLachlan Hodges 	if (!*flags)
453b1906ceaSLachlan Hodges 		return;
454b1906ceaSLachlan Hodges 
455b1906ceaSLachlan Hodges 	/* Disable power save in case it is running */
456b1906ceaSLachlan Hodges 	mm81x_ps_disable(mors);
457b1906ceaSLachlan Hodges 	mm81x_claim_bus(mors);
458b1906ceaSLachlan Hodges 
459b1906ceaSLachlan Hodges 	/*
460b1906ceaSLachlan Hodges 	 * Handle any populated RX pages from chip first to
461b1906ceaSLachlan Hodges 	 * avoid dropping pkts due to full on-chip buffers.
462b1906ceaSLachlan Hodges 	 * Check if all pages were removed, set event flags if not.
463b1906ceaSLachlan Hodges 	 */
464b1906ceaSLachlan Hodges 	if (test_and_clear_bit(MM81X_HIF_EVT_RX_PEND, flags)) {
465b1906ceaSLachlan Hodges 		if (mm81x_yaps_rx_handler(yaps))
466b1906ceaSLachlan Hodges 			set_bit(MM81X_HIF_EVT_RX_PEND, flags);
467b1906ceaSLachlan Hodges 	}
468b1906ceaSLachlan Hodges 
469b1906ceaSLachlan Hodges 	/* TX any commands before considering data */
470b1906ceaSLachlan Hodges 	if (test_and_clear_bit(MM81X_HIF_EVT_TX_COMMAND_PEND, flags)) {
471b1906ceaSLachlan Hodges 		if (mm81x_yaps_tx_cmd_handler(yaps))
472b1906ceaSLachlan Hodges 			set_bit(MM81X_HIF_EVT_TX_COMMAND_PEND, flags);
473b1906ceaSLachlan Hodges 	}
474b1906ceaSLachlan Hodges 
475b1906ceaSLachlan Hodges 	/* TX beacons before considering mgmt/data */
476b1906ceaSLachlan Hodges 	if (test_and_clear_bit(MM81X_HIF_EVT_TX_BEACON_PEND, flags)) {
477b1906ceaSLachlan Hodges 		if (mm81x_yaps_tx_beacon_handler(yaps))
478b1906ceaSLachlan Hodges 			set_bit(MM81X_HIF_EVT_TX_BEACON_PEND, flags);
479b1906ceaSLachlan Hodges 	}
480b1906ceaSLachlan Hodges 
481b1906ceaSLachlan Hodges 	/* TX mgmt before considering data */
482b1906ceaSLachlan Hodges 	if (test_and_clear_bit(MM81X_HIF_EVT_TX_MGMT_PEND, flags)) {
483b1906ceaSLachlan Hodges 		if (mm81x_yaps_tx_mgmt_handler(yaps))
484b1906ceaSLachlan Hodges 			set_bit(MM81X_HIF_EVT_TX_MGMT_PEND, flags);
485b1906ceaSLachlan Hodges 	}
486b1906ceaSLachlan Hodges 
487b1906ceaSLachlan Hodges 	/* Pause TX data Qs */
488b1906ceaSLachlan Hodges 	if (test_and_clear_bit(MM81X_HIF_EVT_DATA_TRAFFIC_PAUSE_PEND, flags)) {
489b1906ceaSLachlan Hodges 		test_and_clear_bit(MM81X_HIF_EVT_DATA_TRAFFIC_RESUME_PEND,
490b1906ceaSLachlan Hodges 				   flags);
491b1906ceaSLachlan Hodges 		mm81x_skbq_data_traffic_pause(mors);
492b1906ceaSLachlan Hodges 	}
493b1906ceaSLachlan Hodges 
494b1906ceaSLachlan Hodges 	/* Resume TX data Qs  */
495b1906ceaSLachlan Hodges 	if (test_and_clear_bit(MM81X_HIF_EVT_DATA_TRAFFIC_RESUME_PEND, flags))
496b1906ceaSLachlan Hodges 		mm81x_skbq_data_traffic_resume(mors);
497b1906ceaSLachlan Hodges 
498b1906ceaSLachlan Hodges 	/* Handle chip queue status */
499b1906ceaSLachlan Hodges 	if (test_and_clear_bit(MM81X_HIF_EVT_TX_PACKET_FREED_UP_PEND, flags))
500b1906ceaSLachlan Hodges 		yaps->chip_queue_full.is_full = false;
501b1906ceaSLachlan Hodges 
502b1906ceaSLachlan Hodges 	/* Check to see if the queue is full or
503b1906ceaSLachlan Hodges 	 * long enough has past since the queue was full
504b1906ceaSLachlan Hodges 	 */
505b1906ceaSLachlan Hodges 	if (yaps->chip_queue_full.is_full &&
506b1906ceaSLachlan Hodges 	    time_before(jiffies, yaps->chip_queue_full.retry_expiry))
507b1906ceaSLachlan Hodges 		goto exit;
508b1906ceaSLachlan Hodges 
509b1906ceaSLachlan Hodges 	/* Finally TX any data */
510b1906ceaSLachlan Hodges 	if (test_and_clear_bit(MM81X_HIF_EVT_TX_DATA_PEND, flags)) {
511b1906ceaSLachlan Hodges 		if (mm81x_yaps_tx_data_handler(yaps))
512b1906ceaSLachlan Hodges 			set_bit(MM81X_HIF_EVT_TX_DATA_PEND, flags);
513b1906ceaSLachlan Hodges 
514b1906ceaSLachlan Hodges 		if (yaps->chip_queue_full.is_full) {
515b1906ceaSLachlan Hodges 			yaps->chip_queue_full.retry_expiry =
516b1906ceaSLachlan Hodges 				jiffies +
517b1906ceaSLachlan Hodges 				msecs_to_jiffies(CHIP_FULL_RECOVERY_TIMEOUT_MS);
518b1906ceaSLachlan Hodges 			mod_timer(&yaps->chip_queue_full.timer,
519b1906ceaSLachlan Hodges 				  yaps->chip_queue_full.retry_expiry);
520b1906ceaSLachlan Hodges 		}
521b1906ceaSLachlan Hodges 	}
522b1906ceaSLachlan Hodges 
523b1906ceaSLachlan Hodges exit:
524b1906ceaSLachlan Hodges 
525b1906ceaSLachlan Hodges 	/* Disable power save in case it is running */
526b1906ceaSLachlan Hodges 	mm81x_release_bus(mors);
527b1906ceaSLachlan Hodges 	mm81x_ps_enable(mors);
528b1906ceaSLachlan Hodges 
529b1906ceaSLachlan Hodges 	/* Don't requeue work if we are shutting down. */
530b1906ceaSLachlan Hodges 	if (yaps->finish)
531b1906ceaSLachlan Hodges 		return;
532b1906ceaSLachlan Hodges 	/*
533b1906ceaSLachlan Hodges 	 * Evaluate all events except MM81X_HIF_EVT_TX_DATA_PEND in case data
534b1906ceaSLachlan Hodges 	 * tx queue is full
535b1906ceaSLachlan Hodges 	 */
536b1906ceaSLachlan Hodges 	if ((*flags) & ~(1 << MM81X_HIF_EVT_TX_DATA_PEND))
537b1906ceaSLachlan Hodges 		queue_work(mors->chip_wq, &mors->hif_work);
538b1906ceaSLachlan Hodges 	/*
539b1906ceaSLachlan Hodges 	 * if data tx queue is not full and the work hasn't been queued let's
540b1906ceaSLachlan Hodges 	 * queue it
541b1906ceaSLachlan Hodges 	 */
542b1906ceaSLachlan Hodges 	else if (!yaps->chip_queue_full.is_full && *flags)
543b1906ceaSLachlan Hodges 		queue_work(mors->chip_wq, &mors->hif_work);
544b1906ceaSLachlan Hodges }
545b1906ceaSLachlan Hodges 
mm81x_yaps_get_tx_status_pending_count(struct mm81x * mors)546b1906ceaSLachlan Hodges int mm81x_yaps_get_tx_status_pending_count(struct mm81x *mors)
547b1906ceaSLachlan Hodges {
548b1906ceaSLachlan Hodges 	int i = 0;
549b1906ceaSLachlan Hodges 	int count = 0;
550b1906ceaSLachlan Hodges 	struct mm81x_yaps *yaps;
551b1906ceaSLachlan Hodges 
552b1906ceaSLachlan Hodges 	yaps = &mors->hif.u.yaps;
553b1906ceaSLachlan Hodges 	count += skb_queue_len(&yaps->beacon_q.pending);
554b1906ceaSLachlan Hodges 	count += skb_queue_len(&yaps->mgmt_q.pending);
555b1906ceaSLachlan Hodges 	count += skb_queue_len(&yaps->cmd_q.pending);
556b1906ceaSLachlan Hodges 
557b1906ceaSLachlan Hodges 	for (i = 0; i < ARRAY_SIZE(yaps->data_tx_qs); i++)
558b1906ceaSLachlan Hodges 		count += skb_queue_len(&yaps->data_tx_qs[i].pending);
559b1906ceaSLachlan Hodges 
560b1906ceaSLachlan Hodges 	return count;
561b1906ceaSLachlan Hodges }
562b1906ceaSLachlan Hodges 
mm81x_yaps_get_tx_buffered_count(struct mm81x * mors)563b1906ceaSLachlan Hodges int mm81x_yaps_get_tx_buffered_count(struct mm81x *mors)
564b1906ceaSLachlan Hodges {
565b1906ceaSLachlan Hodges 	int i = 0;
566b1906ceaSLachlan Hodges 	int count = 0;
567b1906ceaSLachlan Hodges 	struct mm81x_yaps *yaps;
568b1906ceaSLachlan Hodges 
569b1906ceaSLachlan Hodges 	yaps = &mors->hif.u.yaps;
570b1906ceaSLachlan Hodges 	count += skb_queue_len(&yaps->beacon_q.skbq) +
571b1906ceaSLachlan Hodges 		 skb_queue_len(&yaps->beacon_q.pending);
572b1906ceaSLachlan Hodges 	count += skb_queue_len(&yaps->mgmt_q.skbq) +
573b1906ceaSLachlan Hodges 		 skb_queue_len(&yaps->mgmt_q.pending);
574b1906ceaSLachlan Hodges 	count += skb_queue_len(&yaps->cmd_q.skbq) +
575b1906ceaSLachlan Hodges 		 skb_queue_len(&yaps->cmd_q.pending);
576b1906ceaSLachlan Hodges 
577b1906ceaSLachlan Hodges 	for (i = 0; i < ARRAY_SIZE(yaps->data_tx_qs); i++)
578b1906ceaSLachlan Hodges 		count += mm81x_skbq_count_tx_ready(&yaps->data_tx_qs[i]) +
579b1906ceaSLachlan Hodges 			 skb_queue_len(&yaps->data_tx_qs[i].pending);
580b1906ceaSLachlan Hodges 
581b1906ceaSLachlan Hodges 	return count;
582b1906ceaSLachlan Hodges }
583b1906ceaSLachlan Hodges 
mm81x_yaps_tx_q_full_timer(struct timer_list * t)584b1906ceaSLachlan Hodges static void mm81x_yaps_tx_q_full_timer(struct timer_list *t)
585b1906ceaSLachlan Hodges {
586b1906ceaSLachlan Hodges 	struct mm81x_yaps *yaps =
587b1906ceaSLachlan Hodges 		timer_container_of(yaps, t, chip_queue_full.timer);
588b1906ceaSLachlan Hodges 
589b1906ceaSLachlan Hodges 	queue_work(yaps->mors->chip_wq, &yaps->mors->hif_work);
590b1906ceaSLachlan Hodges }
591b1906ceaSLachlan Hodges 
mm81x_yaps_q_chip_full_timer_init(struct mm81x_yaps * yaps)592b1906ceaSLachlan Hodges static void mm81x_yaps_q_chip_full_timer_init(struct mm81x_yaps *yaps)
593b1906ceaSLachlan Hodges {
594b1906ceaSLachlan Hodges 	timer_setup(&yaps->chip_queue_full.timer, mm81x_yaps_tx_q_full_timer,
595b1906ceaSLachlan Hodges 		    0);
596b1906ceaSLachlan Hodges }
597b1906ceaSLachlan Hodges 
mm81x_yaps_q_chip_full_timer_finish(struct mm81x_yaps * yaps)598b1906ceaSLachlan Hodges static void mm81x_yaps_q_chip_full_timer_finish(struct mm81x_yaps *yaps)
599b1906ceaSLachlan Hodges {
600*333f7de5SLinmao Li 	timer_shutdown_sync(&yaps->chip_queue_full.timer);
601b1906ceaSLachlan Hodges }
602b1906ceaSLachlan Hodges 
mm81x_yaps_init(struct mm81x * mors)603b1906ceaSLachlan Hodges int mm81x_yaps_init(struct mm81x *mors)
604b1906ceaSLachlan Hodges {
605b1906ceaSLachlan Hodges 	int i, ret;
606b1906ceaSLachlan Hodges 	struct mm81x_yaps *yaps;
607b1906ceaSLachlan Hodges 
608b1906ceaSLachlan Hodges 	ret = mm81x_yaps_hw_init(mors);
609b1906ceaSLachlan Hodges 	if (ret) {
610b1906ceaSLachlan Hodges 		dev_err(mors->dev, "mm81x_yaps_hw_init failed %d", ret);
611b1906ceaSLachlan Hodges 		return ret;
612b1906ceaSLachlan Hodges 	}
613b1906ceaSLachlan Hodges 
614b1906ceaSLachlan Hodges 	yaps = &mors->hif.u.yaps;
615b1906ceaSLachlan Hodges 	yaps->mors = mors;
616b1906ceaSLachlan Hodges 
617b1906ceaSLachlan Hodges 	mm81x_claim_bus(mors);
618b1906ceaSLachlan Hodges 
619b1906ceaSLachlan Hodges 	ret = mm81x_yaps_alloc_pkt_buffers(yaps);
620b1906ceaSLachlan Hodges 	if (ret) {
621b1906ceaSLachlan Hodges 		dev_err(mors->dev, "Failed to allocate YAPS packet buffers: %d",
622b1906ceaSLachlan Hodges 			ret);
623b1906ceaSLachlan Hodges 		mm81x_yaps_hw_finish(mors);
624b1906ceaSLachlan Hodges 		mm81x_release_bus(mors);
625b1906ceaSLachlan Hodges 		return ret;
626b1906ceaSLachlan Hodges 	}
627b1906ceaSLachlan Hodges 
628b1906ceaSLachlan Hodges 	/* YAPS is bi-directional */
629b1906ceaSLachlan Hodges 	mm81x_skbq_init(mors, &yaps->data_rx_q,
630b1906ceaSLachlan Hodges 			MM81X_HIF_FLAGS_DATA | MM81X_HIF_FLAGS_DIR_TO_HOST);
631b1906ceaSLachlan Hodges 	mm81x_skbq_init(mors, &yaps->beacon_q,
632b1906ceaSLachlan Hodges 			MM81X_HIF_FLAGS_DATA | MM81X_HIF_FLAGS_DIR_TO_HOST);
633b1906ceaSLachlan Hodges 	mm81x_skbq_init(mors, &yaps->mgmt_q,
634b1906ceaSLachlan Hodges 			MM81X_HIF_FLAGS_DATA | MM81X_HIF_FLAGS_DIR_TO_HOST);
635b1906ceaSLachlan Hodges 
636b1906ceaSLachlan Hodges 	for (i = 0; i < ARRAY_SIZE(yaps->data_tx_qs); i++) {
637b1906ceaSLachlan Hodges 		mm81x_skbq_init(mors, &yaps->data_tx_qs[i],
638b1906ceaSLachlan Hodges 				MM81X_HIF_FLAGS_DATA |
639b1906ceaSLachlan Hodges 					MM81X_HIF_FLAGS_DIR_TO_CHIP);
640b1906ceaSLachlan Hodges 	}
641b1906ceaSLachlan Hodges 
642b1906ceaSLachlan Hodges 	mm81x_skbq_init(mors, &yaps->cmd_q,
643b1906ceaSLachlan Hodges 			MM81X_HIF_FLAGS_COMMAND | MM81X_HIF_FLAGS_DIR_TO_CHIP);
644b1906ceaSLachlan Hodges 	mm81x_skbq_init(mors, &yaps->cmd_resp_q,
645b1906ceaSLachlan Hodges 			MM81X_HIF_FLAGS_COMMAND | MM81X_HIF_FLAGS_DIR_TO_HOST);
646b1906ceaSLachlan Hodges 
647b1906ceaSLachlan Hodges 	mm81x_yaps_q_chip_full_timer_init(yaps);
648b1906ceaSLachlan Hodges 	INIT_WORK(&mors->hif_work, mm81x_yaps_work);
649b1906ceaSLachlan Hodges 	INIT_WORK(&mors->tx_stale_work, mm81x_yaps_stale_tx_work);
650b1906ceaSLachlan Hodges 	mm81x_release_bus(mors);
651b1906ceaSLachlan Hodges 	mm81x_hw_enable_stop_notifications(mors, true);
652b1906ceaSLachlan Hodges 	return 0;
653b1906ceaSLachlan Hodges }
654b1906ceaSLachlan Hodges 
mm81x_yaps_finish(struct mm81x * mors)655b1906ceaSLachlan Hodges void mm81x_yaps_finish(struct mm81x *mors)
656b1906ceaSLachlan Hodges {
657b1906ceaSLachlan Hodges 	int i;
658b1906ceaSLachlan Hodges 	struct mm81x_yaps *yaps;
659b1906ceaSLachlan Hodges 
660b1906ceaSLachlan Hodges 	mm81x_yaps_hw_enable_irqs(mors, false);
661b1906ceaSLachlan Hodges 
662b1906ceaSLachlan Hodges 	yaps = &mors->hif.u.yaps;
663b1906ceaSLachlan Hodges 	yaps->finish = true;
664b1906ceaSLachlan Hodges 
665b1906ceaSLachlan Hodges 	mm81x_skbq_finish(&yaps->data_rx_q);
666b1906ceaSLachlan Hodges 	mm81x_skbq_finish(&yaps->beacon_q);
667b1906ceaSLachlan Hodges 	mm81x_skbq_finish(&yaps->mgmt_q);
668b1906ceaSLachlan Hodges 
669b1906ceaSLachlan Hodges 	for (i = 0; i < ARRAY_SIZE(yaps->data_tx_qs); i++)
670b1906ceaSLachlan Hodges 		mm81x_skbq_finish(&yaps->data_tx_qs[i]);
671b1906ceaSLachlan Hodges 
672b1906ceaSLachlan Hodges 	mm81x_skbq_finish(&yaps->cmd_q);
673b1906ceaSLachlan Hodges 	mm81x_skbq_finish(&yaps->cmd_resp_q);
674b1906ceaSLachlan Hodges 
675b1906ceaSLachlan Hodges 	mm81x_yaps_q_chip_full_timer_finish(yaps);
676b1906ceaSLachlan Hodges 
677b1906ceaSLachlan Hodges 	cancel_work_sync(&mors->hif_work);
678b1906ceaSLachlan Hodges 	cancel_work_sync(&mors->tx_stale_work);
679b1906ceaSLachlan Hodges 
680b1906ceaSLachlan Hodges 	mm81x_yaps_free_pkt_buffers(yaps);
681b1906ceaSLachlan Hodges 	mm81x_yaps_hw_finish(mors);
682b1906ceaSLachlan Hodges }
683b1906ceaSLachlan Hodges 
mm81x_yaps_flush_tx_data(struct mm81x * mors)684b1906ceaSLachlan Hodges void mm81x_yaps_flush_tx_data(struct mm81x *mors)
685b1906ceaSLachlan Hodges {
686b1906ceaSLachlan Hodges 	int i;
687b1906ceaSLachlan Hodges 	struct mm81x_yaps *yaps = &mors->hif.u.yaps;
688b1906ceaSLachlan Hodges 
689b1906ceaSLachlan Hodges 	mm81x_skbq_tx_flush(&yaps->beacon_q);
690b1906ceaSLachlan Hodges 	mm81x_skbq_tx_flush(&yaps->mgmt_q);
691b1906ceaSLachlan Hodges 
692b1906ceaSLachlan Hodges 	for (i = 0; i < ARRAY_SIZE(yaps->data_tx_qs); i++)
693b1906ceaSLachlan Hodges 		mm81x_skbq_tx_flush(&yaps->data_tx_qs[i]);
694b1906ceaSLachlan Hodges }
695b1906ceaSLachlan Hodges 
mm81x_yaps_flush_cmds(struct mm81x * mors)696b1906ceaSLachlan Hodges void mm81x_yaps_flush_cmds(struct mm81x *mors)
697b1906ceaSLachlan Hodges {
698b1906ceaSLachlan Hodges 	struct mm81x_yaps *yaps = &mors->hif.u.yaps;
699b1906ceaSLachlan Hodges 
700b1906ceaSLachlan Hodges 	if (yaps->flags & MM81X_HIF_FLAGS_COMMAND) {
701b1906ceaSLachlan Hodges 		mm81x_skbq_finish(&yaps->cmd_q);
702b1906ceaSLachlan Hodges 		mm81x_skbq_finish(&yaps->cmd_resp_q);
703b1906ceaSLachlan Hodges 	}
704b1906ceaSLachlan Hodges }
705