xref: /linux/drivers/net/wireless/morsemicro/mm81x/yaps_hw.c (revision 91ec2035134982b98fab0609a9fd8480e8217dc1)
1*b1906ceaSLachlan Hodges // SPDX-License-Identifier: GPL-2.0-only
2*b1906ceaSLachlan Hodges /*
3*b1906ceaSLachlan Hodges  * Copyright (c) 2017-2026 Morse Micro
4*b1906ceaSLachlan Hodges  */
5*b1906ceaSLachlan Hodges #include "yaps_hw.h"
6*b1906ceaSLachlan Hodges #include "bus.h"
7*b1906ceaSLachlan Hodges #include "hif.h"
8*b1906ceaSLachlan Hodges #include "yaps.h"
9*b1906ceaSLachlan Hodges 
10*b1906ceaSLachlan Hodges #define YAPS_HW_WINDOW_SIZE_BYTES 32768
11*b1906ceaSLachlan Hodges #define YAPS_MAX_PKT_SIZE_BYTES 16128
12*b1906ceaSLachlan Hodges #define YAPS_METADATA_PAGE_COUNT 1
13*b1906ceaSLachlan Hodges 
14*b1906ceaSLachlan Hodges #define YAPS_PHANDLE_CORRUPTION_WAR_EXTRA_PAGE 1
15*b1906ceaSLachlan Hodges 
16*b1906ceaSLachlan Hodges #define YAPS_PAGE_SIZE 256
17*b1906ceaSLachlan Hodges 
18*b1906ceaSLachlan Hodges /* Calculate padding required for yaps transaction */
19*b1906ceaSLachlan Hodges #define YAPS_CALC_PADDING(_bytes) ((_bytes) & 0x3 ? (4 - ((_bytes) & 0x3)) : 0)
20*b1906ceaSLachlan Hodges 
21*b1906ceaSLachlan Hodges #define YAPS_RESERVED_PAGE_SIZE 256
22*b1906ceaSLachlan Hodges 
23*b1906ceaSLachlan Hodges /*
24*b1906ceaSLachlan Hodges  * Yaps data stream delimiter is a 32 bit word with the following fields:
25*b1906ceaSLachlan Hodges  *
26*b1906ceaSLachlan Hodges  * pkt_size (14 bits) - Packet size not including delimiter or padding
27*b1906ceaSLachlan Hodges  * pool_id  (3  bits) - Pool that pages should be allocated from.
28*b1906ceaSLachlan Hodges  * padding  (2  bits) - Padding required to bring packet to word (4 byte)
29*b1906ceaSLachlan Hodges  * irq      (1  bit ) - Raise a PKT_IRQ on the YDS this is sent to
30*b1906ceaSLachlan Hodges  * reserved (5  bits) - Reserved, must write as 0
31*b1906ceaSLachlan Hodges  * crc      (7  bits) - YAPS CRC
32*b1906ceaSLachlan Hodges  */
33*b1906ceaSLachlan Hodges 
34*b1906ceaSLachlan Hodges /* Packet size not including delimiter or padding */
35*b1906ceaSLachlan Hodges #define YAPS_DELIM_GET_PKT_SIZE(_delim) \
36*b1906ceaSLachlan Hodges 	(((_delim) & 0x3FFF) - YAPS_RESERVED_PAGE_SIZE)
37*b1906ceaSLachlan Hodges #define YAPS_DELIM_SET_PKT_SIZE(_pkt_size) \
38*b1906ceaSLachlan Hodges 	(((_pkt_size) & 0x3FFF) + YAPS_RESERVED_PAGE_SIZE)
39*b1906ceaSLachlan Hodges #define YAPS_DELIM_GET_PHANDLE_SIZE(_delim) (((_delim) & 0x3FFF))
40*b1906ceaSLachlan Hodges 
41*b1906ceaSLachlan Hodges /* Pool that pages should be allocated from. */
42*b1906ceaSLachlan Hodges #define YAPS_DELIM_SET_POOL_ID(_pool_id) (((_pool_id) & 0x7) << 14)
43*b1906ceaSLachlan Hodges 
44*b1906ceaSLachlan Hodges /* Padding required to bring packet to word (4 byte) boundary */
45*b1906ceaSLachlan Hodges #define YAPS_DELIM_GET_PADDING(_delim) (((_delim) >> 17) & 0x3)
46*b1906ceaSLachlan Hodges #define YAPS_DELIM_SET_PADDING(_padding) (((_padding) & 0x3) << 17)
47*b1906ceaSLachlan Hodges 
48*b1906ceaSLachlan Hodges /* Raise a PKT_IRQ on the YDS this is sent to */
49*b1906ceaSLachlan Hodges #define YAPS_DELIM_SET_IRQ(_irq) (((_irq) & 0x1) << 19)
50*b1906ceaSLachlan Hodges 
51*b1906ceaSLachlan Hodges /* YAPS CRC */
52*b1906ceaSLachlan Hodges #define YAPS_DELIM_GET_CRC(_delim) (((_delim) >> 25) & 0x7F)
53*b1906ceaSLachlan Hodges #define YAPS_DELIM_SET_CRC(_crc) (((_crc) & 0x7F) << 25)
54*b1906ceaSLachlan Hodges 
55*b1906ceaSLachlan Hodges struct mm81x_yaps_status_regs {
56*b1906ceaSLachlan Hodges 	/* Allocation pools */
57*b1906ceaSLachlan Hodges 	u32 tc_tx_pool_num_pages;
58*b1906ceaSLachlan Hodges 	u32 tc_cmd_pool_num_pages;
59*b1906ceaSLachlan Hodges 	u32 tc_beacon_pool_num_pages;
60*b1906ceaSLachlan Hodges 	u32 tc_mgmt_pool_num_pages;
61*b1906ceaSLachlan Hodges 	u32 fc_rx_pool_num_pages;
62*b1906ceaSLachlan Hodges 	u32 fc_resp_pool_num_pages;
63*b1906ceaSLachlan Hodges 	u32 fc_tx_sts_pool_num_pages;
64*b1906ceaSLachlan Hodges 	u32 fc_aux_pool_num_pages;
65*b1906ceaSLachlan Hodges 	u32 tc_tx_num_pkts;
66*b1906ceaSLachlan Hodges 	u32 tc_cmd_num_pkts;
67*b1906ceaSLachlan Hodges 	u32 tc_beacon_num_pkts;
68*b1906ceaSLachlan Hodges 	u32 tc_mgmt_num_pkts;
69*b1906ceaSLachlan Hodges 	u32 fc_num_pkts;
70*b1906ceaSLachlan Hodges 	u32 fc_done_num_pkts;
71*b1906ceaSLachlan Hodges 	u32 fc_rx_bytes_in_queue;
72*b1906ceaSLachlan Hodges 	u32 tc_delim_crc_fail_detected;
73*b1906ceaSLachlan Hodges 	u32 fc_host_ysl_status;
74*b1906ceaSLachlan Hodges 	u32 lock;
75*b1906ceaSLachlan Hodges } __packed __aligned(8);
76*b1906ceaSLachlan Hodges 
77*b1906ceaSLachlan Hodges struct mm81x_yaps_hw_status_regs {
78*b1906ceaSLachlan Hodges 	__le32 tc_tx_pool_num_pages;
79*b1906ceaSLachlan Hodges 	__le32 tc_cmd_pool_num_pages;
80*b1906ceaSLachlan Hodges 	__le32 tc_beacon_pool_num_pages;
81*b1906ceaSLachlan Hodges 	__le32 tc_mgmt_pool_num_pages;
82*b1906ceaSLachlan Hodges 	__le32 fc_rx_pool_num_pages;
83*b1906ceaSLachlan Hodges 	__le32 fc_resp_pool_num_pages;
84*b1906ceaSLachlan Hodges 	__le32 fc_tx_sts_pool_num_pages;
85*b1906ceaSLachlan Hodges 	__le32 fc_aux_pool_num_pages;
86*b1906ceaSLachlan Hodges 	__le32 tc_tx_num_pkts;
87*b1906ceaSLachlan Hodges 	__le32 tc_cmd_num_pkts;
88*b1906ceaSLachlan Hodges 	__le32 tc_beacon_num_pkts;
89*b1906ceaSLachlan Hodges 	__le32 tc_mgmt_num_pkts;
90*b1906ceaSLachlan Hodges 	__le32 fc_num_pkts;
91*b1906ceaSLachlan Hodges 	__le32 fc_done_num_pkts;
92*b1906ceaSLachlan Hodges 	__le32 fc_rx_bytes_in_queue;
93*b1906ceaSLachlan Hodges 	__le32 tc_delim_crc_fail_detected;
94*b1906ceaSLachlan Hodges 	__le32 fc_host_ysl_status;
95*b1906ceaSLachlan Hodges 	__le32 lock;
96*b1906ceaSLachlan Hodges } __packed __aligned(8);
97*b1906ceaSLachlan Hodges 
98*b1906ceaSLachlan Hodges struct mm81x_yaps_hw_aux_data {
99*b1906ceaSLachlan Hodges 	unsigned long access_lock;
100*b1906ceaSLachlan Hodges 
101*b1906ceaSLachlan Hodges 	u32 yds_addr;
102*b1906ceaSLachlan Hodges 	u32 ysl_addr;
103*b1906ceaSLachlan Hodges 	u32 status_regs_addr;
104*b1906ceaSLachlan Hodges 
105*b1906ceaSLachlan Hodges 	/* Alloc pool sizes */
106*b1906ceaSLachlan Hodges 	u16 tc_tx_pool_size;
107*b1906ceaSLachlan Hodges 	u16 tc_cmd_pool_size;
108*b1906ceaSLachlan Hodges 	u8 tc_beacon_pool_size;
109*b1906ceaSLachlan Hodges 	u8 tc_mgmt_pool_size;
110*b1906ceaSLachlan Hodges 	u8 fc_rx_pool_size;
111*b1906ceaSLachlan Hodges 	u8 fc_resp_pool_size;
112*b1906ceaSLachlan Hodges 	u8 fc_tx_sts_pool_size;
113*b1906ceaSLachlan Hodges 	u8 fc_aux_pool_size;
114*b1906ceaSLachlan Hodges 
115*b1906ceaSLachlan Hodges 	/* To chip/from chip queue sizes */
116*b1906ceaSLachlan Hodges 	u8 tc_tx_q_size;
117*b1906ceaSLachlan Hodges 	u8 tc_cmd_q_size;
118*b1906ceaSLachlan Hodges 	u8 tc_beacon_q_size;
119*b1906ceaSLachlan Hodges 	u8 tc_mgmt_q_size;
120*b1906ceaSLachlan Hodges 	u8 fc_q_size;
121*b1906ceaSLachlan Hodges 	u8 fc_done_q_size;
122*b1906ceaSLachlan Hodges 
123*b1906ceaSLachlan Hodges 	u16 reserved_yaps_page_size;
124*b1906ceaSLachlan Hodges 
125*b1906ceaSLachlan Hodges 	/* Buffers to/from chip to support large contiguous reads/writes */
126*b1906ceaSLachlan Hodges 	char *to_chip_buffer;
127*b1906ceaSLachlan Hodges 	char *from_chip_buffer;
128*b1906ceaSLachlan Hodges 
129*b1906ceaSLachlan Hodges 	/* status registers in host endian */
130*b1906ceaSLachlan Hodges 	struct mm81x_yaps_status_regs status_regs;
131*b1906ceaSLachlan Hodges 
132*b1906ceaSLachlan Hodges 	/* DMA target buffer in firmware endian */
133*b1906ceaSLachlan Hodges 	struct mm81x_yaps_hw_status_regs hw_status_regs;
134*b1906ceaSLachlan Hodges };
135*b1906ceaSLachlan Hodges 
mm81x_yaps_hw_lock(struct mm81x_yaps * yaps)136*b1906ceaSLachlan Hodges static int mm81x_yaps_hw_lock(struct mm81x_yaps *yaps)
137*b1906ceaSLachlan Hodges {
138*b1906ceaSLachlan Hodges 	if (test_and_set_bit_lock(0, &yaps->aux_data->access_lock))
139*b1906ceaSLachlan Hodges 		return -1;
140*b1906ceaSLachlan Hodges 	return 0;
141*b1906ceaSLachlan Hodges }
142*b1906ceaSLachlan Hodges 
mm81x_yaps_hw_unlock(struct mm81x_yaps * yaps)143*b1906ceaSLachlan Hodges static void mm81x_yaps_hw_unlock(struct mm81x_yaps *yaps)
144*b1906ceaSLachlan Hodges {
145*b1906ceaSLachlan Hodges 	clear_bit_unlock(0, &yaps->aux_data->access_lock);
146*b1906ceaSLachlan Hodges }
147*b1906ceaSLachlan Hodges 
148*b1906ceaSLachlan Hodges static void
mm81x_yaps_hw_fill_aux_data_from_hw_tbl(struct mm81x_yaps_hw_aux_data * a,struct mm81x_yaps_hw_table * t)149*b1906ceaSLachlan Hodges mm81x_yaps_hw_fill_aux_data_from_hw_tbl(struct mm81x_yaps_hw_aux_data *a,
150*b1906ceaSLachlan Hodges 					struct mm81x_yaps_hw_table *t)
151*b1906ceaSLachlan Hodges {
152*b1906ceaSLachlan Hodges 	a->ysl_addr = __le32_to_cpu(t->ysl_addr);
153*b1906ceaSLachlan Hodges 	a->yds_addr = __le32_to_cpu(t->yds_addr);
154*b1906ceaSLachlan Hodges 	a->status_regs_addr = __le32_to_cpu(t->status_regs_addr);
155*b1906ceaSLachlan Hodges 	a->tc_tx_pool_size = __le16_to_cpu(t->tc_tx_pool_size);
156*b1906ceaSLachlan Hodges 	a->fc_rx_pool_size = __le16_to_cpu(t->fc_rx_pool_size);
157*b1906ceaSLachlan Hodges 	a->tc_cmd_pool_size = t->tc_cmd_pool_size;
158*b1906ceaSLachlan Hodges 	a->tc_beacon_pool_size = t->tc_beacon_pool_size;
159*b1906ceaSLachlan Hodges 	a->tc_mgmt_pool_size = t->tc_mgmt_pool_size;
160*b1906ceaSLachlan Hodges 	a->fc_resp_pool_size = t->fc_resp_pool_size;
161*b1906ceaSLachlan Hodges 	a->fc_tx_sts_pool_size = t->fc_tx_sts_pool_size;
162*b1906ceaSLachlan Hodges 	a->fc_aux_pool_size = t->fc_aux_pool_size;
163*b1906ceaSLachlan Hodges 	a->tc_tx_q_size = t->tc_tx_q_size;
164*b1906ceaSLachlan Hodges 	a->tc_cmd_q_size = t->tc_cmd_q_size;
165*b1906ceaSLachlan Hodges 	a->tc_beacon_q_size = t->tc_beacon_q_size;
166*b1906ceaSLachlan Hodges 	a->tc_mgmt_q_size = t->tc_mgmt_q_size;
167*b1906ceaSLachlan Hodges 	a->fc_q_size = t->fc_q_size;
168*b1906ceaSLachlan Hodges 	a->fc_done_q_size = t->fc_done_q_size;
169*b1906ceaSLachlan Hodges 	a->reserved_yaps_page_size = le16_to_cpu(t->yaps_reserved_page_size);
170*b1906ceaSLachlan Hodges }
171*b1906ceaSLachlan Hodges 
mm81x_yaps_hw_crc(u32 word)172*b1906ceaSLachlan Hodges static u8 mm81x_yaps_hw_crc(u32 word)
173*b1906ceaSLachlan Hodges {
174*b1906ceaSLachlan Hodges 	u8 crc = 0;
175*b1906ceaSLachlan Hodges 	u8 byte;
176*b1906ceaSLachlan Hodges 	int i;
177*b1906ceaSLachlan Hodges 
178*b1906ceaSLachlan Hodges 	/* Mask to look at only non-CRC bits */
179*b1906ceaSLachlan Hodges 	word &= 0x1ffffff;
180*b1906ceaSLachlan Hodges 
181*b1906ceaSLachlan Hodges 	for (i = 0; i < 4; i++) {
182*b1906ceaSLachlan Hodges 		byte = (word >> 24) & 0xff;
183*b1906ceaSLachlan Hodges 		crc = crc7_be(crc, &byte, 1);
184*b1906ceaSLachlan Hodges 		word <<= 8;
185*b1906ceaSLachlan Hodges 	}
186*b1906ceaSLachlan Hodges 
187*b1906ceaSLachlan Hodges 	return crc >> 1;
188*b1906ceaSLachlan Hodges }
189*b1906ceaSLachlan Hodges 
mm81x_write_pkts_h_build_delim(struct mm81x_yaps * yaps,unsigned int size,u8 pool_id,bool irq)190*b1906ceaSLachlan Hodges static u32 mm81x_write_pkts_h_build_delim(struct mm81x_yaps *yaps,
191*b1906ceaSLachlan Hodges 					  unsigned int size, u8 pool_id,
192*b1906ceaSLachlan Hodges 					  bool irq)
193*b1906ceaSLachlan Hodges {
194*b1906ceaSLachlan Hodges 	u32 delim = 0;
195*b1906ceaSLachlan Hodges 
196*b1906ceaSLachlan Hodges 	delim |= YAPS_DELIM_SET_PKT_SIZE(size);
197*b1906ceaSLachlan Hodges 	delim |= YAPS_DELIM_SET_PADDING(YAPS_CALC_PADDING(size));
198*b1906ceaSLachlan Hodges 	delim |= YAPS_DELIM_SET_POOL_ID(pool_id);
199*b1906ceaSLachlan Hodges 	delim |= YAPS_DELIM_SET_IRQ(irq);
200*b1906ceaSLachlan Hodges 	delim |= YAPS_DELIM_SET_CRC(mm81x_yaps_hw_crc(delim));
201*b1906ceaSLachlan Hodges 	return delim;
202*b1906ceaSLachlan Hodges }
203*b1906ceaSLachlan Hodges 
mm81x_yaps_hw_enable_irqs(struct mm81x * mors,bool enable)204*b1906ceaSLachlan Hodges void mm81x_yaps_hw_enable_irqs(struct mm81x *mors, bool enable)
205*b1906ceaSLachlan Hodges {
206*b1906ceaSLachlan Hodges 	mm81x_hw_irq_enable(mors, MM81X_INT_YAPS_FC_PKT_WAITING_IRQN, enable);
207*b1906ceaSLachlan Hodges 	mm81x_hw_irq_enable(mors, MM81X_INT_YAPS_FC_PACKET_FREED_UP_IRQN,
208*b1906ceaSLachlan Hodges 			    enable);
209*b1906ceaSLachlan Hodges }
210*b1906ceaSLachlan Hodges 
mm81x_yaps_hw_read_table(struct mm81x * mors,struct mm81x_yaps_hw_table * tbl_ptr)211*b1906ceaSLachlan Hodges void mm81x_yaps_hw_read_table(struct mm81x *mors,
212*b1906ceaSLachlan Hodges 			      struct mm81x_yaps_hw_table *tbl_ptr)
213*b1906ceaSLachlan Hodges {
214*b1906ceaSLachlan Hodges 	mm81x_yaps_hw_fill_aux_data_from_hw_tbl(mors->hif.u.yaps.aux_data,
215*b1906ceaSLachlan Hodges 						tbl_ptr);
216*b1906ceaSLachlan Hodges 	mm81x_yaps_hw_enable_irqs(mors, true);
217*b1906ceaSLachlan Hodges }
218*b1906ceaSLachlan Hodges 
mm81x_write_pkts_h_pages_required(struct mm81x_yaps * yaps,unsigned int size_bytes)219*b1906ceaSLachlan Hodges static unsigned int mm81x_write_pkts_h_pages_required(struct mm81x_yaps *yaps,
220*b1906ceaSLachlan Hodges 						      unsigned int size_bytes)
221*b1906ceaSLachlan Hodges {
222*b1906ceaSLachlan Hodges 	/* Always account for the first metadata page */
223*b1906ceaSLachlan Hodges 	return DIV_ROUND_UP(size_bytes +
224*b1906ceaSLachlan Hodges 				    yaps->aux_data->reserved_yaps_page_size,
225*b1906ceaSLachlan Hodges 			    YAPS_PAGE_SIZE) +
226*b1906ceaSLachlan Hodges 	       YAPS_METADATA_PAGE_COUNT +
227*b1906ceaSLachlan Hodges 	       YAPS_PHANDLE_CORRUPTION_WAR_EXTRA_PAGE;
228*b1906ceaSLachlan Hodges }
229*b1906ceaSLachlan Hodges 
230*b1906ceaSLachlan Hodges /*
231*b1906ceaSLachlan Hodges  * Checks if a single pkt will fit in the chip using the pool/alloc holding
232*b1906ceaSLachlan Hodges  * information from the last status register read.
233*b1906ceaSLachlan Hodges  */
mm81x_write_pkts_h_will_fit(struct mm81x_yaps * yaps,struct mm81x_yaps_pkt * pkt,bool update)234*b1906ceaSLachlan Hodges static bool mm81x_write_pkts_h_will_fit(struct mm81x_yaps *yaps,
235*b1906ceaSLachlan Hodges 					struct mm81x_yaps_pkt *pkt, bool update)
236*b1906ceaSLachlan Hodges {
237*b1906ceaSLachlan Hodges 	bool will_fit = true;
238*b1906ceaSLachlan Hodges 	const int pages_required =
239*b1906ceaSLachlan Hodges 		mm81x_write_pkts_h_pages_required(yaps, pkt->skb->len);
240*b1906ceaSLachlan Hodges 	int *pool_pages_avail = NULL;
241*b1906ceaSLachlan Hodges 	int *pkts_in_queue = NULL;
242*b1906ceaSLachlan Hodges 	int queue_pkts_avail = 0;
243*b1906ceaSLachlan Hodges 
244*b1906ceaSLachlan Hodges 	switch (pkt->tc_queue) {
245*b1906ceaSLachlan Hodges 	case MM81X_YAPS_TX_Q:
246*b1906ceaSLachlan Hodges 		pool_pages_avail =
247*b1906ceaSLachlan Hodges 			&yaps->aux_data->status_regs.tc_tx_pool_num_pages;
248*b1906ceaSLachlan Hodges 		pkts_in_queue = &yaps->aux_data->status_regs.tc_tx_num_pkts;
249*b1906ceaSLachlan Hodges 		queue_pkts_avail =
250*b1906ceaSLachlan Hodges 			yaps->aux_data->tc_tx_q_size - *pkts_in_queue;
251*b1906ceaSLachlan Hodges 		break;
252*b1906ceaSLachlan Hodges 	case MM81X_YAPS_CMD_Q:
253*b1906ceaSLachlan Hodges 		pool_pages_avail =
254*b1906ceaSLachlan Hodges 			&yaps->aux_data->status_regs.tc_cmd_pool_num_pages;
255*b1906ceaSLachlan Hodges 		pkts_in_queue = &yaps->aux_data->status_regs.tc_cmd_num_pkts;
256*b1906ceaSLachlan Hodges 		queue_pkts_avail =
257*b1906ceaSLachlan Hodges 			yaps->aux_data->tc_cmd_q_size - *pkts_in_queue;
258*b1906ceaSLachlan Hodges 		break;
259*b1906ceaSLachlan Hodges 	case MM81X_YAPS_BEACON_Q:
260*b1906ceaSLachlan Hodges 		pool_pages_avail =
261*b1906ceaSLachlan Hodges 			&yaps->aux_data->status_regs.tc_beacon_pool_num_pages;
262*b1906ceaSLachlan Hodges 		pkts_in_queue = &yaps->aux_data->status_regs.tc_beacon_num_pkts;
263*b1906ceaSLachlan Hodges 		queue_pkts_avail =
264*b1906ceaSLachlan Hodges 			yaps->aux_data->tc_beacon_q_size - *pkts_in_queue;
265*b1906ceaSLachlan Hodges 		break;
266*b1906ceaSLachlan Hodges 	case MM81X_YAPS_MGMT_Q:
267*b1906ceaSLachlan Hodges 		pool_pages_avail =
268*b1906ceaSLachlan Hodges 			&yaps->aux_data->status_regs.tc_mgmt_pool_num_pages;
269*b1906ceaSLachlan Hodges 		pkts_in_queue = &yaps->aux_data->status_regs.tc_mgmt_num_pkts;
270*b1906ceaSLachlan Hodges 		queue_pkts_avail =
271*b1906ceaSLachlan Hodges 			yaps->aux_data->tc_mgmt_q_size - *pkts_in_queue;
272*b1906ceaSLachlan Hodges 		break;
273*b1906ceaSLachlan Hodges 	default:
274*b1906ceaSLachlan Hodges 		dev_err(yaps->mors->dev, "yaps invalid tc queue");
275*b1906ceaSLachlan Hodges 		return false;
276*b1906ceaSLachlan Hodges 	}
277*b1906ceaSLachlan Hodges 
278*b1906ceaSLachlan Hodges 	WARN_ON(queue_pkts_avail < 0);
279*b1906ceaSLachlan Hodges 
280*b1906ceaSLachlan Hodges 	if (pages_required > *pool_pages_avail)
281*b1906ceaSLachlan Hodges 		will_fit = false;
282*b1906ceaSLachlan Hodges 
283*b1906ceaSLachlan Hodges 	if (queue_pkts_avail == 0)
284*b1906ceaSLachlan Hodges 		will_fit = false;
285*b1906ceaSLachlan Hodges 
286*b1906ceaSLachlan Hodges 	if (will_fit && update) {
287*b1906ceaSLachlan Hodges 		*pool_pages_avail -= pages_required;
288*b1906ceaSLachlan Hodges 		*pkts_in_queue += 1;
289*b1906ceaSLachlan Hodges 	}
290*b1906ceaSLachlan Hodges 
291*b1906ceaSLachlan Hodges 	return will_fit;
292*b1906ceaSLachlan Hodges }
293*b1906ceaSLachlan Hodges 
mm81x_write_pkts_h_err_check(struct mm81x_yaps * yaps,struct mm81x_yaps_pkt * pkt)294*b1906ceaSLachlan Hodges static int mm81x_write_pkts_h_err_check(struct mm81x_yaps *yaps,
295*b1906ceaSLachlan Hodges 					struct mm81x_yaps_pkt *pkt)
296*b1906ceaSLachlan Hodges {
297*b1906ceaSLachlan Hodges 	if (pkt->skb->len + yaps->aux_data->reserved_yaps_page_size >
298*b1906ceaSLachlan Hodges 	    YAPS_MAX_PKT_SIZE_BYTES)
299*b1906ceaSLachlan Hodges 		return -EMSGSIZE;
300*b1906ceaSLachlan Hodges 	if (pkt->tc_queue >= MM81X_YAPS_NUM_TC_Q)
301*b1906ceaSLachlan Hodges 		return -EINVAL;
302*b1906ceaSLachlan Hodges 	if (!mm81x_write_pkts_h_will_fit(yaps, pkt, true))
303*b1906ceaSLachlan Hodges 		return -EAGAIN;
304*b1906ceaSLachlan Hodges 
305*b1906ceaSLachlan Hodges 	return 0;
306*b1906ceaSLachlan Hodges }
307*b1906ceaSLachlan Hodges 
mm81x_yaps_hw_write_pkts(struct mm81x_yaps * yaps,struct mm81x_yaps_pkt * pkts,int num_pkts,int * num_pkts_sent)308*b1906ceaSLachlan Hodges static int mm81x_yaps_hw_write_pkts(struct mm81x_yaps *yaps,
309*b1906ceaSLachlan Hodges 				    struct mm81x_yaps_pkt *pkts, int num_pkts,
310*b1906ceaSLachlan Hodges 				    int *num_pkts_sent)
311*b1906ceaSLachlan Hodges {
312*b1906ceaSLachlan Hodges 	int ret = 0;
313*b1906ceaSLachlan Hodges 	int i;
314*b1906ceaSLachlan Hodges 	u32 delim = 0;
315*b1906ceaSLachlan Hodges 	int tx_len;
316*b1906ceaSLachlan Hodges 	int batch_txn_len = 0;
317*b1906ceaSLachlan Hodges 	int pkts_pending = 0;
318*b1906ceaSLachlan Hodges 	bool delim_irq = false;
319*b1906ceaSLachlan Hodges 	char *to_chip_buffer_aligned =
320*b1906ceaSLachlan Hodges 		PTR_ALIGN(yaps->aux_data->to_chip_buffer,
321*b1906ceaSLachlan Hodges 			  mm81x_bus_get_alignment(yaps->mors));
322*b1906ceaSLachlan Hodges 	char *write_buf = to_chip_buffer_aligned;
323*b1906ceaSLachlan Hodges 
324*b1906ceaSLachlan Hodges 	ret = mm81x_yaps_hw_lock(yaps);
325*b1906ceaSLachlan Hodges 	if (ret) {
326*b1906ceaSLachlan Hodges 		dev_dbg(yaps->mors->dev, "yaps lock failed %d", ret);
327*b1906ceaSLachlan Hodges 		return ret;
328*b1906ceaSLachlan Hodges 	}
329*b1906ceaSLachlan Hodges 
330*b1906ceaSLachlan Hodges 	*num_pkts_sent = 0;
331*b1906ceaSLachlan Hodges 
332*b1906ceaSLachlan Hodges 	/* Check packet conditions */
333*b1906ceaSLachlan Hodges 	ret = mm81x_write_pkts_h_err_check(yaps, &pkts[0]);
334*b1906ceaSLachlan Hodges 	if (ret)
335*b1906ceaSLachlan Hodges 		goto exit;
336*b1906ceaSLachlan Hodges 
337*b1906ceaSLachlan Hodges 	/* Batch packets into larger transactions */
338*b1906ceaSLachlan Hodges 	for (i = 0; i < num_pkts; ++i) {
339*b1906ceaSLachlan Hodges 		u32 pkt_size =
340*b1906ceaSLachlan Hodges 			pkts[i].skb->len + YAPS_CALC_PADDING(pkts[i].skb->len);
341*b1906ceaSLachlan Hodges 		tx_len = pkt_size + sizeof(delim);
342*b1906ceaSLachlan Hodges 
343*b1906ceaSLachlan Hodges 		/*
344*b1906ceaSLachlan Hodges 		 * Send when we have reached window size, don't split pkt over
345*b1906ceaSLachlan Hodges 		 * boundary
346*b1906ceaSLachlan Hodges 		 */
347*b1906ceaSLachlan Hodges 		if ((batch_txn_len + tx_len) > YAPS_HW_WINDOW_SIZE_BYTES) {
348*b1906ceaSLachlan Hodges 			ret = mm81x_dm_write(yaps->mors,
349*b1906ceaSLachlan Hodges 					     yaps->aux_data->yds_addr,
350*b1906ceaSLachlan Hodges 					     to_chip_buffer_aligned,
351*b1906ceaSLachlan Hodges 					     batch_txn_len);
352*b1906ceaSLachlan Hodges 
353*b1906ceaSLachlan Hodges 			batch_txn_len = 0;
354*b1906ceaSLachlan Hodges 			if (ret)
355*b1906ceaSLachlan Hodges 				goto exit;
356*b1906ceaSLachlan Hodges 			write_buf = to_chip_buffer_aligned;
357*b1906ceaSLachlan Hodges 			*num_pkts_sent += pkts_pending;
358*b1906ceaSLachlan Hodges 			pkts_pending = 0;
359*b1906ceaSLachlan Hodges 		}
360*b1906ceaSLachlan Hodges 
361*b1906ceaSLachlan Hodges 		if ((i + 1) == num_pkts) {
362*b1906ceaSLachlan Hodges 			/* The last packet in the queue has IRQ set */
363*b1906ceaSLachlan Hodges 			delim_irq = true;
364*b1906ceaSLachlan Hodges 		} else {
365*b1906ceaSLachlan Hodges 			/*
366*b1906ceaSLachlan Hodges 			 * Since this is not the last packet, we can check for
367*b1906ceaSLachlan Hodges 			 * the next one. In case of errors in the next packet
368*b1906ceaSLachlan Hodges 			 * set the IRQ
369*b1906ceaSLachlan Hodges 			 */
370*b1906ceaSLachlan Hodges 			ret = mm81x_write_pkts_h_err_check(yaps, &pkts[i + 1]);
371*b1906ceaSLachlan Hodges 			if (ret)
372*b1906ceaSLachlan Hodges 				delim_irq = true;
373*b1906ceaSLachlan Hodges 		}
374*b1906ceaSLachlan Hodges 
375*b1906ceaSLachlan Hodges 		/* Build stream header*/
376*b1906ceaSLachlan Hodges 		delim = mm81x_write_pkts_h_build_delim(
377*b1906ceaSLachlan Hodges 			yaps, pkt_size, pkts[i].tc_queue, delim_irq);
378*b1906ceaSLachlan Hodges 		*((__le32 *)write_buf) = cpu_to_le32(delim);
379*b1906ceaSLachlan Hodges 		memcpy(write_buf + sizeof(delim), pkts[i].skb->data,
380*b1906ceaSLachlan Hodges 		       pkts[i].skb->len);
381*b1906ceaSLachlan Hodges 
382*b1906ceaSLachlan Hodges 		write_buf += tx_len;
383*b1906ceaSLachlan Hodges 		batch_txn_len += tx_len;
384*b1906ceaSLachlan Hodges 		pkts_pending++;
385*b1906ceaSLachlan Hodges 
386*b1906ceaSLachlan Hodges 		if (ret)
387*b1906ceaSLachlan Hodges 			goto exit;
388*b1906ceaSLachlan Hodges 	}
389*b1906ceaSLachlan Hodges 
390*b1906ceaSLachlan Hodges exit:
391*b1906ceaSLachlan Hodges 	if (batch_txn_len > 0) {
392*b1906ceaSLachlan Hodges 		ret = mm81x_dm_write(yaps->mors, yaps->aux_data->yds_addr,
393*b1906ceaSLachlan Hodges 				     to_chip_buffer_aligned, batch_txn_len);
394*b1906ceaSLachlan Hodges 		*num_pkts_sent += pkts_pending;
395*b1906ceaSLachlan Hodges 	}
396*b1906ceaSLachlan Hodges 
397*b1906ceaSLachlan Hodges 	mm81x_yaps_hw_unlock(yaps);
398*b1906ceaSLachlan Hodges 	return ret;
399*b1906ceaSLachlan Hodges }
400*b1906ceaSLachlan Hodges 
mm81x_read_pkts_h_is_valid_delim(u32 delim)401*b1906ceaSLachlan Hodges static bool mm81x_read_pkts_h_is_valid_delim(u32 delim)
402*b1906ceaSLachlan Hodges {
403*b1906ceaSLachlan Hodges 	u8 calc_crc = mm81x_yaps_hw_crc(delim);
404*b1906ceaSLachlan Hodges 	int pkt_size = YAPS_DELIM_GET_PHANDLE_SIZE(delim);
405*b1906ceaSLachlan Hodges 	int padding = YAPS_DELIM_GET_PADDING(delim);
406*b1906ceaSLachlan Hodges 
407*b1906ceaSLachlan Hodges 	if (calc_crc != YAPS_DELIM_GET_CRC(delim))
408*b1906ceaSLachlan Hodges 		return false;
409*b1906ceaSLachlan Hodges 
410*b1906ceaSLachlan Hodges 	if (pkt_size == 0)
411*b1906ceaSLachlan Hodges 		return false;
412*b1906ceaSLachlan Hodges 
413*b1906ceaSLachlan Hodges 	if ((pkt_size + padding) > YAPS_MAX_PKT_SIZE_BYTES)
414*b1906ceaSLachlan Hodges 		return false;
415*b1906ceaSLachlan Hodges 
416*b1906ceaSLachlan Hodges 	/* Pkt length + padding should not require more padding */
417*b1906ceaSLachlan Hodges 	if (YAPS_CALC_PADDING(pkt_size) != padding)
418*b1906ceaSLachlan Hodges 		return false;
419*b1906ceaSLachlan Hodges 
420*b1906ceaSLachlan Hodges 	return true;
421*b1906ceaSLachlan Hodges }
422*b1906ceaSLachlan Hodges 
mm81x_read_pkts_h_bytes_remaining(struct mm81x_yaps * yaps)423*b1906ceaSLachlan Hodges static int mm81x_read_pkts_h_bytes_remaining(struct mm81x_yaps *yaps)
424*b1906ceaSLachlan Hodges {
425*b1906ceaSLachlan Hodges 	u32 bytes_in_queue = yaps->aux_data->status_regs.fc_rx_bytes_in_queue;
426*b1906ceaSLachlan Hodges 	u32 delim_overhead =
427*b1906ceaSLachlan Hodges 		yaps->aux_data->status_regs.fc_num_pkts * sizeof(u32);
428*b1906ceaSLachlan Hodges 	u32 reserved_bytes = yaps->aux_data->status_regs.fc_num_pkts *
429*b1906ceaSLachlan Hodges 			     yaps->aux_data->reserved_yaps_page_size;
430*b1906ceaSLachlan Hodges 
431*b1906ceaSLachlan Hodges 	if (WARN_ON(bytes_in_queue > INT_MAX) ||
432*b1906ceaSLachlan Hodges 	    WARN_ON(delim_overhead > INT_MAX) ||
433*b1906ceaSLachlan Hodges 	    WARN_ON(reserved_bytes > INT_MAX))
434*b1906ceaSLachlan Hodges 		return -EIO;
435*b1906ceaSLachlan Hodges 
436*b1906ceaSLachlan Hodges 	return (int)bytes_in_queue;
437*b1906ceaSLachlan Hodges }
438*b1906ceaSLachlan Hodges 
mm81x_yaps_hw_read_pkts(struct mm81x_yaps * yaps,struct mm81x_yaps_pkt * pkts,int num_pkts_max,int * num_pkts_received)439*b1906ceaSLachlan Hodges static int mm81x_yaps_hw_read_pkts(struct mm81x_yaps *yaps,
440*b1906ceaSLachlan Hodges 				   struct mm81x_yaps_pkt *pkts,
441*b1906ceaSLachlan Hodges 				   int num_pkts_max, int *num_pkts_received)
442*b1906ceaSLachlan Hodges {
443*b1906ceaSLachlan Hodges 	int ret;
444*b1906ceaSLachlan Hodges 	int i = 0;
445*b1906ceaSLachlan Hodges 	char *from_chip_buffer_aligned =
446*b1906ceaSLachlan Hodges 		PTR_ALIGN(yaps->aux_data->from_chip_buffer,
447*b1906ceaSLachlan Hodges 			  mm81x_bus_get_alignment(yaps->mors));
448*b1906ceaSLachlan Hodges 	char *read_ptr = from_chip_buffer_aligned;
449*b1906ceaSLachlan Hodges 	int bytes_remaining = mm81x_read_pkts_h_bytes_remaining(yaps);
450*b1906ceaSLachlan Hodges 	bool again = false;
451*b1906ceaSLachlan Hodges 
452*b1906ceaSLachlan Hodges 	*num_pkts_received = 0;
453*b1906ceaSLachlan Hodges 
454*b1906ceaSLachlan Hodges 	if (num_pkts_max == 0 || bytes_remaining == 0)
455*b1906ceaSLachlan Hodges 		return 0;
456*b1906ceaSLachlan Hodges 	if (bytes_remaining < 0)
457*b1906ceaSLachlan Hodges 		return bytes_remaining;
458*b1906ceaSLachlan Hodges 
459*b1906ceaSLachlan Hodges 	if (bytes_remaining > YAPS_HW_WINDOW_SIZE_BYTES) {
460*b1906ceaSLachlan Hodges 		bytes_remaining = YAPS_HW_WINDOW_SIZE_BYTES;
461*b1906ceaSLachlan Hodges 		again = true;
462*b1906ceaSLachlan Hodges 	}
463*b1906ceaSLachlan Hodges 
464*b1906ceaSLachlan Hodges 	/*
465*b1906ceaSLachlan Hodges 	 * This is more coarse-grained than it needs to be - once the data
466*b1906ceaSLachlan Hodges 	 * is read into a local buffer the lock can be released, however
467*b1906ceaSLachlan Hodges 	 * access to from_chip_buffer will need to be protected with its
468*b1906ceaSLachlan Hodges 	 * own lock
469*b1906ceaSLachlan Hodges 	 */
470*b1906ceaSLachlan Hodges 	ret = mm81x_yaps_hw_lock(yaps);
471*b1906ceaSLachlan Hodges 	if (ret) {
472*b1906ceaSLachlan Hodges 		dev_dbg(yaps->mors->dev, "yaps lock failed %d", ret);
473*b1906ceaSLachlan Hodges 		return ret;
474*b1906ceaSLachlan Hodges 	}
475*b1906ceaSLachlan Hodges 
476*b1906ceaSLachlan Hodges 	/* Read all available packets to the buffer */
477*b1906ceaSLachlan Hodges 	ret = mm81x_dm_read(yaps->mors, yaps->aux_data->ysl_addr,
478*b1906ceaSLachlan Hodges 			    from_chip_buffer_aligned, bytes_remaining);
479*b1906ceaSLachlan Hodges 
480*b1906ceaSLachlan Hodges 	if (ret)
481*b1906ceaSLachlan Hodges 		goto exit;
482*b1906ceaSLachlan Hodges 
483*b1906ceaSLachlan Hodges 	/* Split serialised packets from buffer */
484*b1906ceaSLachlan Hodges 	while (i < num_pkts_max && bytes_remaining > 0) {
485*b1906ceaSLachlan Hodges 		u32 delim;
486*b1906ceaSLachlan Hodges 		int total_len;
487*b1906ceaSLachlan Hodges 		int pkt_size;
488*b1906ceaSLachlan Hodges 
489*b1906ceaSLachlan Hodges 		delim = le32_to_cpu(*((__le32 *)read_ptr));
490*b1906ceaSLachlan Hodges 		read_ptr += sizeof(delim);
491*b1906ceaSLachlan Hodges 		bytes_remaining -= sizeof(delim);
492*b1906ceaSLachlan Hodges 
493*b1906ceaSLachlan Hodges 		/* End of stream */
494*b1906ceaSLachlan Hodges 		if (!delim)
495*b1906ceaSLachlan Hodges 			break;
496*b1906ceaSLachlan Hodges 
497*b1906ceaSLachlan Hodges 		if (!mm81x_read_pkts_h_is_valid_delim(delim)) {
498*b1906ceaSLachlan Hodges 			/*
499*b1906ceaSLachlan Hodges 			 * This will start a hunt for a valid delimiter. Given
500*b1906ceaSLachlan Hodges 			 * the CRC is only 7 bit it's possible to find an
501*b1906ceaSLachlan Hodges 			 * invalid block with a valid delimiter, leading to
502*b1906ceaSLachlan Hodges 			 * desynchronisation.
503*b1906ceaSLachlan Hodges 			 */
504*b1906ceaSLachlan Hodges 			dev_warn(yaps->mors->dev, "yaps invalid delim");
505*b1906ceaSLachlan Hodges 			break;
506*b1906ceaSLachlan Hodges 		}
507*b1906ceaSLachlan Hodges 
508*b1906ceaSLachlan Hodges 		/* Total length in chip */
509*b1906ceaSLachlan Hodges 		pkt_size = YAPS_DELIM_GET_PKT_SIZE(delim);
510*b1906ceaSLachlan Hodges 		total_len = pkt_size + YAPS_DELIM_GET_PADDING(delim);
511*b1906ceaSLachlan Hodges 
512*b1906ceaSLachlan Hodges 		if (pkts[i].skb)
513*b1906ceaSLachlan Hodges 			dev_err(yaps->mors->dev, "yaps packet leak");
514*b1906ceaSLachlan Hodges 
515*b1906ceaSLachlan Hodges 		/* SKB doesn't want padding */
516*b1906ceaSLachlan Hodges 		pkts[i].skb = dev_alloc_skb(pkt_size);
517*b1906ceaSLachlan Hodges 		if (!pkts[i].skb) {
518*b1906ceaSLachlan Hodges 			ret = -ENOMEM;
519*b1906ceaSLachlan Hodges 			dev_err(yaps->mors->dev, "yaps no mem for skb");
520*b1906ceaSLachlan Hodges 			goto exit;
521*b1906ceaSLachlan Hodges 		}
522*b1906ceaSLachlan Hodges 		skb_put(pkts[i].skb, pkt_size);
523*b1906ceaSLachlan Hodges 
524*b1906ceaSLachlan Hodges 		if (total_len <= bytes_remaining) {
525*b1906ceaSLachlan Hodges 			memcpy(pkts[i].skb->data, read_ptr, pkt_size);
526*b1906ceaSLachlan Hodges 			read_ptr += total_len;
527*b1906ceaSLachlan Hodges 			bytes_remaining -= total_len;
528*b1906ceaSLachlan Hodges 		} else {
529*b1906ceaSLachlan Hodges 			const int read_overhang_len =
530*b1906ceaSLachlan Hodges 				total_len - bytes_remaining;
531*b1906ceaSLachlan Hodges 			const int pkt_overhang_len = pkt_size - bytes_remaining;
532*b1906ceaSLachlan Hodges 
533*b1906ceaSLachlan Hodges 			memcpy(pkts[i].skb->data, read_ptr, bytes_remaining);
534*b1906ceaSLachlan Hodges 			read_ptr = from_chip_buffer_aligned;
535*b1906ceaSLachlan Hodges 
536*b1906ceaSLachlan Hodges 			ret = mm81x_dm_read(
537*b1906ceaSLachlan Hodges 				yaps->mors,
538*b1906ceaSLachlan Hodges 				/* Offset by 4 to avoid retry logic */
539*b1906ceaSLachlan Hodges 				yaps->aux_data->ysl_addr + 4, read_ptr,
540*b1906ceaSLachlan Hodges 				read_overhang_len);
541*b1906ceaSLachlan Hodges 
542*b1906ceaSLachlan Hodges 			if (ret)
543*b1906ceaSLachlan Hodges 				goto exit;
544*b1906ceaSLachlan Hodges 
545*b1906ceaSLachlan Hodges 			memcpy(pkts[i].skb->data + bytes_remaining, read_ptr,
546*b1906ceaSLachlan Hodges 			       pkt_overhang_len);
547*b1906ceaSLachlan Hodges 			read_ptr += read_overhang_len;
548*b1906ceaSLachlan Hodges 			bytes_remaining = 0;
549*b1906ceaSLachlan Hodges 		}
550*b1906ceaSLachlan Hodges 
551*b1906ceaSLachlan Hodges 		*num_pkts_received += 1;
552*b1906ceaSLachlan Hodges 		i++;
553*b1906ceaSLachlan Hodges 	}
554*b1906ceaSLachlan Hodges 
555*b1906ceaSLachlan Hodges 	if (again)
556*b1906ceaSLachlan Hodges 		ret = -EAGAIN;
557*b1906ceaSLachlan Hodges 
558*b1906ceaSLachlan Hodges exit:
559*b1906ceaSLachlan Hodges 	mm81x_yaps_hw_unlock(yaps);
560*b1906ceaSLachlan Hodges 	return ret;
561*b1906ceaSLachlan Hodges }
562*b1906ceaSLachlan Hodges 
mm81x_yaps_hw_update_status(struct mm81x_yaps * yaps)563*b1906ceaSLachlan Hodges static int mm81x_yaps_hw_update_status(struct mm81x_yaps *yaps)
564*b1906ceaSLachlan Hodges {
565*b1906ceaSLachlan Hodges 	int ret;
566*b1906ceaSLachlan Hodges 	int tc_total_pkt_count;
567*b1906ceaSLachlan Hodges 	unsigned long reg_read_timeout;
568*b1906ceaSLachlan Hodges 	struct mm81x_yaps_status_regs *r = &yaps->aux_data->status_regs;
569*b1906ceaSLachlan Hodges 	struct mm81x_yaps_hw_status_regs *hw_r = &yaps->aux_data->hw_status_regs;
570*b1906ceaSLachlan Hodges 
571*b1906ceaSLachlan Hodges 	ret = mm81x_yaps_hw_lock(yaps);
572*b1906ceaSLachlan Hodges 	if (ret) {
573*b1906ceaSLachlan Hodges 		dev_dbg(yaps->mors->dev, "yaps lock failed %d", ret);
574*b1906ceaSLachlan Hodges 		return ret;
575*b1906ceaSLachlan Hodges 	}
576*b1906ceaSLachlan Hodges 
577*b1906ceaSLachlan Hodges 	reg_read_timeout = jiffies + msecs_to_jiffies(100);
578*b1906ceaSLachlan Hodges 	do {
579*b1906ceaSLachlan Hodges 		if (time_after(jiffies, reg_read_timeout)) {
580*b1906ceaSLachlan Hodges 			dev_err(yaps->mors->dev,
581*b1906ceaSLachlan Hodges 				"timed out reading status registers: %d", ret);
582*b1906ceaSLachlan Hodges 			ret = -ETIMEDOUT;
583*b1906ceaSLachlan Hodges 			break;
584*b1906ceaSLachlan Hodges 		}
585*b1906ceaSLachlan Hodges 
586*b1906ceaSLachlan Hodges 		ret = mm81x_dm_read(yaps->mors,
587*b1906ceaSLachlan Hodges 				    yaps->aux_data->status_regs_addr,
588*b1906ceaSLachlan Hodges 				    (u8 *)hw_r, sizeof(*hw_r));
589*b1906ceaSLachlan Hodges 	} while (!ret && le32_to_cpu(hw_r->lock));
590*b1906ceaSLachlan Hodges 
591*b1906ceaSLachlan Hodges 	if (ret) {
592*b1906ceaSLachlan Hodges 		if (ret != -ENODEV) {
593*b1906ceaSLachlan Hodges 			dev_err(yaps->mors->dev,
594*b1906ceaSLachlan Hodges 				"error reading yaps status registers: %d", ret);
595*b1906ceaSLachlan Hodges 		}
596*b1906ceaSLachlan Hodges 		goto exit_unlock;
597*b1906ceaSLachlan Hodges 	}
598*b1906ceaSLachlan Hodges 
599*b1906ceaSLachlan Hodges 	r->tc_tx_pool_num_pages = le32_to_cpu(hw_r->tc_tx_pool_num_pages);
600*b1906ceaSLachlan Hodges 	r->tc_cmd_pool_num_pages = le32_to_cpu(hw_r->tc_cmd_pool_num_pages);
601*b1906ceaSLachlan Hodges 	r->tc_beacon_pool_num_pages = le32_to_cpu(hw_r->tc_beacon_pool_num_pages);
602*b1906ceaSLachlan Hodges 	r->tc_mgmt_pool_num_pages = le32_to_cpu(hw_r->tc_mgmt_pool_num_pages);
603*b1906ceaSLachlan Hodges 	r->fc_rx_pool_num_pages = le32_to_cpu(hw_r->fc_rx_pool_num_pages);
604*b1906ceaSLachlan Hodges 	r->fc_resp_pool_num_pages = le32_to_cpu(hw_r->fc_resp_pool_num_pages);
605*b1906ceaSLachlan Hodges 	r->fc_tx_sts_pool_num_pages = le32_to_cpu(hw_r->fc_tx_sts_pool_num_pages);
606*b1906ceaSLachlan Hodges 	r->fc_aux_pool_num_pages = le32_to_cpu(hw_r->fc_aux_pool_num_pages);
607*b1906ceaSLachlan Hodges 	r->tc_tx_num_pkts = le32_to_cpu(hw_r->tc_tx_num_pkts);
608*b1906ceaSLachlan Hodges 	r->tc_cmd_num_pkts = le32_to_cpu(hw_r->tc_cmd_num_pkts);
609*b1906ceaSLachlan Hodges 	r->tc_beacon_num_pkts = le32_to_cpu(hw_r->tc_beacon_num_pkts);
610*b1906ceaSLachlan Hodges 	r->tc_mgmt_num_pkts = le32_to_cpu(hw_r->tc_mgmt_num_pkts);
611*b1906ceaSLachlan Hodges 	r->fc_num_pkts = le32_to_cpu(hw_r->fc_num_pkts);
612*b1906ceaSLachlan Hodges 	r->fc_done_num_pkts = le32_to_cpu(hw_r->fc_done_num_pkts);
613*b1906ceaSLachlan Hodges 	r->fc_rx_bytes_in_queue = le32_to_cpu(hw_r->fc_rx_bytes_in_queue);
614*b1906ceaSLachlan Hodges 	r->tc_delim_crc_fail_detected = le32_to_cpu(hw_r->tc_delim_crc_fail_detected);
615*b1906ceaSLachlan Hodges 	r->lock = le32_to_cpu(hw_r->lock);
616*b1906ceaSLachlan Hodges 	r->fc_host_ysl_status = le32_to_cpu(hw_r->fc_host_ysl_status);
617*b1906ceaSLachlan Hodges 
618*b1906ceaSLachlan Hodges 	tc_total_pkt_count = r->tc_tx_num_pkts + r->tc_cmd_num_pkts +
619*b1906ceaSLachlan Hodges 			     r->tc_beacon_num_pkts + r->tc_mgmt_num_pkts;
620*b1906ceaSLachlan Hodges 
621*b1906ceaSLachlan Hodges 	if (r->tc_delim_crc_fail_detected) {
622*b1906ceaSLachlan Hodges 		/*
623*b1906ceaSLachlan Hodges 		 * Host and chip have become desynchronised. This can happen if
624*b1906ceaSLachlan Hodges 		 * the chip crashes during a YAPS transaction. We cannot
625*b1906ceaSLachlan Hodges 		 * recover from this.
626*b1906ceaSLachlan Hodges 		 */
627*b1906ceaSLachlan Hodges 		dev_err(yaps->mors->dev,
628*b1906ceaSLachlan Hodges 			"to-chip yaps delimiter CRC fail, pkt_count=%d",
629*b1906ceaSLachlan Hodges 			tc_total_pkt_count);
630*b1906ceaSLachlan Hodges 		ret = -EIO;
631*b1906ceaSLachlan Hodges 	}
632*b1906ceaSLachlan Hodges 
633*b1906ceaSLachlan Hodges 	if (mm81x_read_pkts_h_bytes_remaining(yaps))
634*b1906ceaSLachlan Hodges 		set_bit(MM81X_HIF_EVT_RX_PEND, &yaps->mors->hif.event_flags);
635*b1906ceaSLachlan Hodges 
636*b1906ceaSLachlan Hodges exit_unlock:
637*b1906ceaSLachlan Hodges 	mm81x_yaps_hw_unlock(yaps);
638*b1906ceaSLachlan Hodges 	return ret;
639*b1906ceaSLachlan Hodges }
640*b1906ceaSLachlan Hodges 
641*b1906ceaSLachlan Hodges static const struct mm81x_yaps_ops mm81x_yaps_hw_ops = {
642*b1906ceaSLachlan Hodges 	.write_pkts = mm81x_yaps_hw_write_pkts,
643*b1906ceaSLachlan Hodges 	.read_pkts = mm81x_yaps_hw_read_pkts,
644*b1906ceaSLachlan Hodges 	.update_status = mm81x_yaps_hw_update_status,
645*b1906ceaSLachlan Hodges };
646*b1906ceaSLachlan Hodges 
mm81x_yaps_hw_init(struct mm81x * mors)647*b1906ceaSLachlan Hodges int mm81x_yaps_hw_init(struct mm81x *mors)
648*b1906ceaSLachlan Hodges {
649*b1906ceaSLachlan Hodges 	int ret = 0;
650*b1906ceaSLachlan Hodges 	struct mm81x_yaps *yaps = NULL;
651*b1906ceaSLachlan Hodges 	int aux_data_len = sizeof(struct mm81x_yaps_hw_aux_data);
652*b1906ceaSLachlan Hodges 	int alignment = mm81x_bus_get_alignment(mors);
653*b1906ceaSLachlan Hodges 
654*b1906ceaSLachlan Hodges 	yaps = &mors->hif.u.yaps;
655*b1906ceaSLachlan Hodges 	yaps->aux_data = kzalloc(aux_data_len, GFP_KERNEL);
656*b1906ceaSLachlan Hodges 	if (!yaps->aux_data) {
657*b1906ceaSLachlan Hodges 		ret = -ENOMEM;
658*b1906ceaSLachlan Hodges 		goto err_exit;
659*b1906ceaSLachlan Hodges 	}
660*b1906ceaSLachlan Hodges 
661*b1906ceaSLachlan Hodges 	yaps->aux_data->to_chip_buffer =
662*b1906ceaSLachlan Hodges 		kzalloc(YAPS_HW_WINDOW_SIZE_BYTES + alignment - 1, GFP_KERNEL);
663*b1906ceaSLachlan Hodges 	if (!yaps->aux_data->to_chip_buffer) {
664*b1906ceaSLachlan Hodges 		ret = -ENOMEM;
665*b1906ceaSLachlan Hodges 		goto err_exit;
666*b1906ceaSLachlan Hodges 	}
667*b1906ceaSLachlan Hodges 
668*b1906ceaSLachlan Hodges 	yaps->aux_data->from_chip_buffer =
669*b1906ceaSLachlan Hodges 		kzalloc(YAPS_HW_WINDOW_SIZE_BYTES + alignment - 1, GFP_KERNEL);
670*b1906ceaSLachlan Hodges 	if (!yaps->aux_data->from_chip_buffer) {
671*b1906ceaSLachlan Hodges 		ret = -ENOMEM;
672*b1906ceaSLachlan Hodges 		goto err_exit;
673*b1906ceaSLachlan Hodges 	}
674*b1906ceaSLachlan Hodges 
675*b1906ceaSLachlan Hodges 	if (!IS_ALIGNED((uintptr_t)&yaps->aux_data->status_regs, alignment)) {
676*b1906ceaSLachlan Hodges 		dev_warn(mors->dev,
677*b1906ceaSLachlan Hodges 			 "Status registers are not aligned to %d bytes",
678*b1906ceaSLachlan Hodges 			 alignment);
679*b1906ceaSLachlan Hodges 	}
680*b1906ceaSLachlan Hodges 
681*b1906ceaSLachlan Hodges 	yaps->ops = &mm81x_yaps_hw_ops;
682*b1906ceaSLachlan Hodges 	return ret;
683*b1906ceaSLachlan Hodges 
684*b1906ceaSLachlan Hodges err_exit:
685*b1906ceaSLachlan Hodges 	mm81x_yaps_hw_finish(mors);
686*b1906ceaSLachlan Hodges 	return ret;
687*b1906ceaSLachlan Hodges }
688*b1906ceaSLachlan Hodges 
mm81x_yaps_hw_finish(struct mm81x * mors)689*b1906ceaSLachlan Hodges void mm81x_yaps_hw_finish(struct mm81x *mors)
690*b1906ceaSLachlan Hodges {
691*b1906ceaSLachlan Hodges 	struct mm81x_yaps *yaps;
692*b1906ceaSLachlan Hodges 
693*b1906ceaSLachlan Hodges 	yaps = &mors->hif.u.yaps;
694*b1906ceaSLachlan Hodges 	if (yaps->aux_data) {
695*b1906ceaSLachlan Hodges 		kfree(yaps->aux_data->from_chip_buffer);
696*b1906ceaSLachlan Hodges 		yaps->aux_data->from_chip_buffer = NULL;
697*b1906ceaSLachlan Hodges 		kfree(yaps->aux_data->to_chip_buffer);
698*b1906ceaSLachlan Hodges 		yaps->aux_data->to_chip_buffer = NULL;
699*b1906ceaSLachlan Hodges 		kfree(yaps->aux_data);
700*b1906ceaSLachlan Hodges 		yaps->aux_data = NULL;
701*b1906ceaSLachlan Hodges 	}
702*b1906ceaSLachlan Hodges }
703