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