1 | // SPDX-License-Identifier: ISC |
2 | /* |
3 | * Copyright (c) 2018 The Linux Foundation. All rights reserved. |
4 | */ |
5 | |
6 | #include <linux/bits.h> |
7 | #include <linux/clk.h> |
8 | #include <linux/kernel.h> |
9 | #include <linux/module.h> |
10 | #include <linux/of.h> |
11 | #include <linux/of_device.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/property.h> |
14 | #include <linux/regulator/consumer.h> |
15 | #include <linux/remoteproc/qcom_rproc.h> |
16 | #include <linux/of_address.h> |
17 | #include <linux/iommu.h> |
18 | |
19 | #include "ce.h" |
20 | #include "coredump.h" |
21 | #include "debug.h" |
22 | #include "hif.h" |
23 | #include "htc.h" |
24 | #include "snoc.h" |
25 | |
26 | #define ATH10K_SNOC_RX_POST_RETRY_MS 50 |
27 | #define CE_POLL_PIPE 4 |
28 | #define ATH10K_SNOC_WAKE_IRQ 2 |
29 | |
30 | static char *const ce_name[] = { |
31 | "WLAN_CE_0" , |
32 | "WLAN_CE_1" , |
33 | "WLAN_CE_2" , |
34 | "WLAN_CE_3" , |
35 | "WLAN_CE_4" , |
36 | "WLAN_CE_5" , |
37 | "WLAN_CE_6" , |
38 | "WLAN_CE_7" , |
39 | "WLAN_CE_8" , |
40 | "WLAN_CE_9" , |
41 | "WLAN_CE_10" , |
42 | "WLAN_CE_11" , |
43 | }; |
44 | |
45 | static const char * const ath10k_regulators[] = { |
46 | "vdd-0.8-cx-mx" , |
47 | "vdd-1.8-xo" , |
48 | "vdd-1.3-rfa" , |
49 | "vdd-3.3-ch0" , |
50 | "vdd-3.3-ch1" , |
51 | }; |
52 | |
53 | static const char * const ath10k_clocks[] = { |
54 | "cxo_ref_clk_pin" , "qdss" , |
55 | }; |
56 | |
57 | static void ath10k_snoc_htc_tx_cb(struct ath10k_ce_pipe *ce_state); |
58 | static void ath10k_snoc_htt_tx_cb(struct ath10k_ce_pipe *ce_state); |
59 | static void ath10k_snoc_htc_rx_cb(struct ath10k_ce_pipe *ce_state); |
60 | static void ath10k_snoc_htt_rx_cb(struct ath10k_ce_pipe *ce_state); |
61 | static void ath10k_snoc_htt_htc_rx_cb(struct ath10k_ce_pipe *ce_state); |
62 | static void ath10k_snoc_pktlog_rx_cb(struct ath10k_ce_pipe *ce_state); |
63 | |
64 | static const struct ath10k_snoc_drv_priv drv_priv = { |
65 | .hw_rev = ATH10K_HW_WCN3990, |
66 | .dma_mask = DMA_BIT_MASK(35), |
67 | .msa_size = 0x100000, |
68 | }; |
69 | |
70 | #define WCN3990_SRC_WR_IDX_OFFSET 0x3C |
71 | #define WCN3990_DST_WR_IDX_OFFSET 0x40 |
72 | |
73 | static struct ath10k_shadow_reg_cfg target_shadow_reg_cfg_map[] = { |
74 | { |
75 | .ce_id = __cpu_to_le16(0), |
76 | .reg_offset = __cpu_to_le16(WCN3990_SRC_WR_IDX_OFFSET), |
77 | }, |
78 | |
79 | { |
80 | .ce_id = __cpu_to_le16(3), |
81 | .reg_offset = __cpu_to_le16(WCN3990_SRC_WR_IDX_OFFSET), |
82 | }, |
83 | |
84 | { |
85 | .ce_id = __cpu_to_le16(4), |
86 | .reg_offset = __cpu_to_le16(WCN3990_SRC_WR_IDX_OFFSET), |
87 | }, |
88 | |
89 | { |
90 | .ce_id = __cpu_to_le16(5), |
91 | .reg_offset = __cpu_to_le16(WCN3990_SRC_WR_IDX_OFFSET), |
92 | }, |
93 | |
94 | { |
95 | .ce_id = __cpu_to_le16(7), |
96 | .reg_offset = __cpu_to_le16(WCN3990_SRC_WR_IDX_OFFSET), |
97 | }, |
98 | |
99 | { |
100 | .ce_id = __cpu_to_le16(1), |
101 | .reg_offset = __cpu_to_le16(WCN3990_DST_WR_IDX_OFFSET), |
102 | }, |
103 | |
104 | { |
105 | .ce_id = __cpu_to_le16(2), |
106 | .reg_offset = __cpu_to_le16(WCN3990_DST_WR_IDX_OFFSET), |
107 | }, |
108 | |
109 | { |
110 | .ce_id = __cpu_to_le16(7), |
111 | .reg_offset = __cpu_to_le16(WCN3990_DST_WR_IDX_OFFSET), |
112 | }, |
113 | |
114 | { |
115 | .ce_id = __cpu_to_le16(8), |
116 | .reg_offset = __cpu_to_le16(WCN3990_DST_WR_IDX_OFFSET), |
117 | }, |
118 | |
119 | { |
120 | .ce_id = __cpu_to_le16(9), |
121 | .reg_offset = __cpu_to_le16(WCN3990_DST_WR_IDX_OFFSET), |
122 | }, |
123 | |
124 | { |
125 | .ce_id = __cpu_to_le16(10), |
126 | .reg_offset = __cpu_to_le16(WCN3990_DST_WR_IDX_OFFSET), |
127 | }, |
128 | |
129 | { |
130 | .ce_id = __cpu_to_le16(11), |
131 | .reg_offset = __cpu_to_le16(WCN3990_DST_WR_IDX_OFFSET), |
132 | }, |
133 | }; |
134 | |
135 | static struct ce_attr host_ce_config_wlan[] = { |
136 | /* CE0: host->target HTC control streams */ |
137 | { |
138 | .flags = CE_ATTR_FLAGS, |
139 | .src_nentries = 16, |
140 | .src_sz_max = 2048, |
141 | .dest_nentries = 0, |
142 | .send_cb = ath10k_snoc_htc_tx_cb, |
143 | }, |
144 | |
145 | /* CE1: target->host HTT + HTC control */ |
146 | { |
147 | .flags = CE_ATTR_FLAGS, |
148 | .src_nentries = 0, |
149 | .src_sz_max = 2048, |
150 | .dest_nentries = 512, |
151 | .recv_cb = ath10k_snoc_htt_htc_rx_cb, |
152 | }, |
153 | |
154 | /* CE2: target->host WMI */ |
155 | { |
156 | .flags = CE_ATTR_FLAGS, |
157 | .src_nentries = 0, |
158 | .src_sz_max = 2048, |
159 | .dest_nentries = 64, |
160 | .recv_cb = ath10k_snoc_htc_rx_cb, |
161 | }, |
162 | |
163 | /* CE3: host->target WMI */ |
164 | { |
165 | .flags = CE_ATTR_FLAGS, |
166 | .src_nentries = 32, |
167 | .src_sz_max = 2048, |
168 | .dest_nentries = 0, |
169 | .send_cb = ath10k_snoc_htc_tx_cb, |
170 | }, |
171 | |
172 | /* CE4: host->target HTT */ |
173 | { |
174 | .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, |
175 | .src_nentries = 2048, |
176 | .src_sz_max = 256, |
177 | .dest_nentries = 0, |
178 | .send_cb = ath10k_snoc_htt_tx_cb, |
179 | }, |
180 | |
181 | /* CE5: target->host HTT (ipa_uc->target ) */ |
182 | { |
183 | .flags = CE_ATTR_FLAGS, |
184 | .src_nentries = 0, |
185 | .src_sz_max = 512, |
186 | .dest_nentries = 512, |
187 | .recv_cb = ath10k_snoc_htt_rx_cb, |
188 | }, |
189 | |
190 | /* CE6: target autonomous hif_memcpy */ |
191 | { |
192 | .flags = CE_ATTR_FLAGS, |
193 | .src_nentries = 0, |
194 | .src_sz_max = 0, |
195 | .dest_nentries = 0, |
196 | }, |
197 | |
198 | /* CE7: ce_diag, the Diagnostic Window */ |
199 | { |
200 | .flags = CE_ATTR_FLAGS, |
201 | .src_nentries = 2, |
202 | .src_sz_max = 2048, |
203 | .dest_nentries = 2, |
204 | }, |
205 | |
206 | /* CE8: Target to uMC */ |
207 | { |
208 | .flags = CE_ATTR_FLAGS, |
209 | .src_nentries = 0, |
210 | .src_sz_max = 2048, |
211 | .dest_nentries = 128, |
212 | }, |
213 | |
214 | /* CE9 target->host HTT */ |
215 | { |
216 | .flags = CE_ATTR_FLAGS, |
217 | .src_nentries = 0, |
218 | .src_sz_max = 2048, |
219 | .dest_nentries = 512, |
220 | .recv_cb = ath10k_snoc_htt_htc_rx_cb, |
221 | }, |
222 | |
223 | /* CE10: target->host HTT */ |
224 | { |
225 | .flags = CE_ATTR_FLAGS, |
226 | .src_nentries = 0, |
227 | .src_sz_max = 2048, |
228 | .dest_nentries = 512, |
229 | .recv_cb = ath10k_snoc_htt_htc_rx_cb, |
230 | }, |
231 | |
232 | /* CE11: target -> host PKTLOG */ |
233 | { |
234 | .flags = CE_ATTR_FLAGS, |
235 | .src_nentries = 0, |
236 | .src_sz_max = 2048, |
237 | .dest_nentries = 512, |
238 | .recv_cb = ath10k_snoc_pktlog_rx_cb, |
239 | }, |
240 | }; |
241 | |
242 | static struct ce_pipe_config target_ce_config_wlan[] = { |
243 | /* CE0: host->target HTC control and raw streams */ |
244 | { |
245 | .pipenum = __cpu_to_le32(0), |
246 | .pipedir = __cpu_to_le32(PIPEDIR_OUT), |
247 | .nentries = __cpu_to_le32(32), |
248 | .nbytes_max = __cpu_to_le32(2048), |
249 | .flags = __cpu_to_le32(CE_ATTR_FLAGS), |
250 | .reserved = __cpu_to_le32(0), |
251 | }, |
252 | |
253 | /* CE1: target->host HTT + HTC control */ |
254 | { |
255 | .pipenum = __cpu_to_le32(1), |
256 | .pipedir = __cpu_to_le32(PIPEDIR_IN), |
257 | .nentries = __cpu_to_le32(32), |
258 | .nbytes_max = __cpu_to_le32(2048), |
259 | .flags = __cpu_to_le32(CE_ATTR_FLAGS), |
260 | .reserved = __cpu_to_le32(0), |
261 | }, |
262 | |
263 | /* CE2: target->host WMI */ |
264 | { |
265 | .pipenum = __cpu_to_le32(2), |
266 | .pipedir = __cpu_to_le32(PIPEDIR_IN), |
267 | .nentries = __cpu_to_le32(64), |
268 | .nbytes_max = __cpu_to_le32(2048), |
269 | .flags = __cpu_to_le32(CE_ATTR_FLAGS), |
270 | .reserved = __cpu_to_le32(0), |
271 | }, |
272 | |
273 | /* CE3: host->target WMI */ |
274 | { |
275 | .pipenum = __cpu_to_le32(3), |
276 | .pipedir = __cpu_to_le32(PIPEDIR_OUT), |
277 | .nentries = __cpu_to_le32(32), |
278 | .nbytes_max = __cpu_to_le32(2048), |
279 | .flags = __cpu_to_le32(CE_ATTR_FLAGS), |
280 | .reserved = __cpu_to_le32(0), |
281 | }, |
282 | |
283 | /* CE4: host->target HTT */ |
284 | { |
285 | .pipenum = __cpu_to_le32(4), |
286 | .pipedir = __cpu_to_le32(PIPEDIR_OUT), |
287 | .nentries = __cpu_to_le32(256), |
288 | .nbytes_max = __cpu_to_le32(256), |
289 | .flags = __cpu_to_le32(CE_ATTR_FLAGS | CE_ATTR_DIS_INTR), |
290 | .reserved = __cpu_to_le32(0), |
291 | }, |
292 | |
293 | /* CE5: target->host HTT (HIF->HTT) */ |
294 | { |
295 | .pipenum = __cpu_to_le32(5), |
296 | .pipedir = __cpu_to_le32(PIPEDIR_OUT), |
297 | .nentries = __cpu_to_le32(1024), |
298 | .nbytes_max = __cpu_to_le32(64), |
299 | .flags = __cpu_to_le32(CE_ATTR_FLAGS | CE_ATTR_DIS_INTR), |
300 | .reserved = __cpu_to_le32(0), |
301 | }, |
302 | |
303 | /* CE6: Reserved for target autonomous hif_memcpy */ |
304 | { |
305 | .pipenum = __cpu_to_le32(6), |
306 | .pipedir = __cpu_to_le32(PIPEDIR_INOUT), |
307 | .nentries = __cpu_to_le32(32), |
308 | .nbytes_max = __cpu_to_le32(16384), |
309 | .flags = __cpu_to_le32(CE_ATTR_FLAGS), |
310 | .reserved = __cpu_to_le32(0), |
311 | }, |
312 | |
313 | /* CE7 used only by Host */ |
314 | { |
315 | .pipenum = __cpu_to_le32(7), |
316 | .pipedir = __cpu_to_le32(4), |
317 | .nentries = __cpu_to_le32(0), |
318 | .nbytes_max = __cpu_to_le32(0), |
319 | .flags = __cpu_to_le32(CE_ATTR_FLAGS | CE_ATTR_DIS_INTR), |
320 | .reserved = __cpu_to_le32(0), |
321 | }, |
322 | |
323 | /* CE8 Target to uMC */ |
324 | { |
325 | .pipenum = __cpu_to_le32(8), |
326 | .pipedir = __cpu_to_le32(PIPEDIR_IN), |
327 | .nentries = __cpu_to_le32(32), |
328 | .nbytes_max = __cpu_to_le32(2048), |
329 | .flags = __cpu_to_le32(0), |
330 | .reserved = __cpu_to_le32(0), |
331 | }, |
332 | |
333 | /* CE9 target->host HTT */ |
334 | { |
335 | .pipenum = __cpu_to_le32(9), |
336 | .pipedir = __cpu_to_le32(PIPEDIR_IN), |
337 | .nentries = __cpu_to_le32(32), |
338 | .nbytes_max = __cpu_to_le32(2048), |
339 | .flags = __cpu_to_le32(CE_ATTR_FLAGS), |
340 | .reserved = __cpu_to_le32(0), |
341 | }, |
342 | |
343 | /* CE10 target->host HTT */ |
344 | { |
345 | .pipenum = __cpu_to_le32(10), |
346 | .pipedir = __cpu_to_le32(PIPEDIR_IN), |
347 | .nentries = __cpu_to_le32(32), |
348 | .nbytes_max = __cpu_to_le32(2048), |
349 | .flags = __cpu_to_le32(CE_ATTR_FLAGS), |
350 | .reserved = __cpu_to_le32(0), |
351 | }, |
352 | |
353 | /* CE11 target autonomous qcache memcpy */ |
354 | { |
355 | .pipenum = __cpu_to_le32(11), |
356 | .pipedir = __cpu_to_le32(PIPEDIR_IN), |
357 | .nentries = __cpu_to_le32(32), |
358 | .nbytes_max = __cpu_to_le32(2048), |
359 | .flags = __cpu_to_le32(CE_ATTR_FLAGS), |
360 | .reserved = __cpu_to_le32(0), |
361 | }, |
362 | }; |
363 | |
364 | static struct ce_service_to_pipe target_service_to_ce_map_wlan[] = { |
365 | { |
366 | __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_VO), |
367 | __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ |
368 | __cpu_to_le32(3), |
369 | }, |
370 | { |
371 | __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_VO), |
372 | __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ |
373 | __cpu_to_le32(2), |
374 | }, |
375 | { |
376 | __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_BK), |
377 | __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ |
378 | __cpu_to_le32(3), |
379 | }, |
380 | { |
381 | __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_BK), |
382 | __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ |
383 | __cpu_to_le32(2), |
384 | }, |
385 | { |
386 | __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_BE), |
387 | __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ |
388 | __cpu_to_le32(3), |
389 | }, |
390 | { |
391 | __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_BE), |
392 | __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ |
393 | __cpu_to_le32(2), |
394 | }, |
395 | { |
396 | __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_VI), |
397 | __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ |
398 | __cpu_to_le32(3), |
399 | }, |
400 | { |
401 | __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_VI), |
402 | __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ |
403 | __cpu_to_le32(2), |
404 | }, |
405 | { |
406 | __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_CONTROL), |
407 | __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ |
408 | __cpu_to_le32(3), |
409 | }, |
410 | { |
411 | __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_CONTROL), |
412 | __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ |
413 | __cpu_to_le32(2), |
414 | }, |
415 | { |
416 | __cpu_to_le32(ATH10K_HTC_SVC_ID_RSVD_CTRL), |
417 | __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ |
418 | __cpu_to_le32(0), |
419 | }, |
420 | { |
421 | __cpu_to_le32(ATH10K_HTC_SVC_ID_RSVD_CTRL), |
422 | __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ |
423 | __cpu_to_le32(2), |
424 | }, |
425 | { /* not used */ |
426 | __cpu_to_le32(ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS), |
427 | __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ |
428 | __cpu_to_le32(0), |
429 | }, |
430 | { /* not used */ |
431 | __cpu_to_le32(ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS), |
432 | __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ |
433 | __cpu_to_le32(2), |
434 | }, |
435 | { |
436 | __cpu_to_le32(ATH10K_HTC_SVC_ID_HTT_DATA_MSG), |
437 | __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ |
438 | __cpu_to_le32(4), |
439 | }, |
440 | { |
441 | __cpu_to_le32(ATH10K_HTC_SVC_ID_HTT_DATA_MSG), |
442 | __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ |
443 | __cpu_to_le32(1), |
444 | }, |
445 | { /* not used */ |
446 | __cpu_to_le32(ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS), |
447 | __cpu_to_le32(PIPEDIR_OUT), |
448 | __cpu_to_le32(5), |
449 | }, |
450 | { /* in = DL = target -> host */ |
451 | __cpu_to_le32(ATH10K_HTC_SVC_ID_HTT_DATA2_MSG), |
452 | __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ |
453 | __cpu_to_le32(9), |
454 | }, |
455 | { /* in = DL = target -> host */ |
456 | __cpu_to_le32(ATH10K_HTC_SVC_ID_HTT_DATA3_MSG), |
457 | __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ |
458 | __cpu_to_le32(10), |
459 | }, |
460 | { /* in = DL = target -> host pktlog */ |
461 | __cpu_to_le32(ATH10K_HTC_SVC_ID_HTT_LOG_MSG), |
462 | __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ |
463 | __cpu_to_le32(11), |
464 | }, |
465 | /* (Additions here) */ |
466 | |
467 | { /* must be last */ |
468 | __cpu_to_le32(0), |
469 | __cpu_to_le32(0), |
470 | __cpu_to_le32(0), |
471 | }, |
472 | }; |
473 | |
474 | static void ath10k_snoc_write32(struct ath10k *ar, u32 offset, u32 value) |
475 | { |
476 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
477 | |
478 | iowrite32(value, ar_snoc->mem + offset); |
479 | } |
480 | |
481 | static u32 ath10k_snoc_read32(struct ath10k *ar, u32 offset) |
482 | { |
483 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
484 | u32 val; |
485 | |
486 | val = ioread32(ar_snoc->mem + offset); |
487 | |
488 | return val; |
489 | } |
490 | |
491 | static int __ath10k_snoc_rx_post_buf(struct ath10k_snoc_pipe *pipe) |
492 | { |
493 | struct ath10k_ce_pipe *ce_pipe = pipe->ce_hdl; |
494 | struct ath10k *ar = pipe->hif_ce_state; |
495 | struct ath10k_ce *ce = ath10k_ce_priv(ar); |
496 | struct sk_buff *skb; |
497 | dma_addr_t paddr; |
498 | int ret; |
499 | |
500 | skb = dev_alloc_skb(length: pipe->buf_sz); |
501 | if (!skb) |
502 | return -ENOMEM; |
503 | |
504 | WARN_ONCE((unsigned long)skb->data & 3, "unaligned skb" ); |
505 | |
506 | paddr = dma_map_single(ar->dev, skb->data, |
507 | skb->len + skb_tailroom(skb), |
508 | DMA_FROM_DEVICE); |
509 | if (unlikely(dma_mapping_error(ar->dev, paddr))) { |
510 | ath10k_warn(ar, fmt: "failed to dma map snoc rx buf\n" ); |
511 | dev_kfree_skb_any(skb); |
512 | return -EIO; |
513 | } |
514 | |
515 | ATH10K_SKB_RXCB(skb)->paddr = paddr; |
516 | |
517 | spin_lock_bh(lock: &ce->ce_lock); |
518 | ret = ce_pipe->ops->ce_rx_post_buf(ce_pipe, skb, paddr); |
519 | spin_unlock_bh(lock: &ce->ce_lock); |
520 | if (ret) { |
521 | dma_unmap_single(ar->dev, paddr, skb->len + skb_tailroom(skb), |
522 | DMA_FROM_DEVICE); |
523 | dev_kfree_skb_any(skb); |
524 | return ret; |
525 | } |
526 | |
527 | return 0; |
528 | } |
529 | |
530 | static void ath10k_snoc_rx_post_pipe(struct ath10k_snoc_pipe *pipe) |
531 | { |
532 | struct ath10k *ar = pipe->hif_ce_state; |
533 | struct ath10k_ce *ce = ath10k_ce_priv(ar); |
534 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
535 | struct ath10k_ce_pipe *ce_pipe = pipe->ce_hdl; |
536 | int ret, num; |
537 | |
538 | if (pipe->buf_sz == 0) |
539 | return; |
540 | |
541 | if (!ce_pipe->dest_ring) |
542 | return; |
543 | |
544 | spin_lock_bh(lock: &ce->ce_lock); |
545 | num = __ath10k_ce_rx_num_free_bufs(pipe: ce_pipe); |
546 | spin_unlock_bh(lock: &ce->ce_lock); |
547 | while (num--) { |
548 | ret = __ath10k_snoc_rx_post_buf(pipe); |
549 | if (ret) { |
550 | if (ret == -ENOSPC) |
551 | break; |
552 | ath10k_warn(ar, fmt: "failed to post rx buf: %d\n" , ret); |
553 | mod_timer(timer: &ar_snoc->rx_post_retry, expires: jiffies + |
554 | ATH10K_SNOC_RX_POST_RETRY_MS); |
555 | break; |
556 | } |
557 | } |
558 | } |
559 | |
560 | static void ath10k_snoc_rx_post(struct ath10k *ar) |
561 | { |
562 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
563 | int i; |
564 | |
565 | for (i = 0; i < CE_COUNT; i++) |
566 | ath10k_snoc_rx_post_pipe(pipe: &ar_snoc->pipe_info[i]); |
567 | } |
568 | |
569 | static void ath10k_snoc_process_rx_cb(struct ath10k_ce_pipe *ce_state, |
570 | void (*callback)(struct ath10k *ar, |
571 | struct sk_buff *skb)) |
572 | { |
573 | struct ath10k *ar = ce_state->ar; |
574 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
575 | struct ath10k_snoc_pipe *pipe_info = &ar_snoc->pipe_info[ce_state->id]; |
576 | struct sk_buff *skb; |
577 | struct sk_buff_head list; |
578 | void *transfer_context; |
579 | unsigned int nbytes, max_nbytes; |
580 | |
581 | __skb_queue_head_init(list: &list); |
582 | while (ath10k_ce_completed_recv_next(ce_state, per_transfer_contextp: &transfer_context, |
583 | nbytesp: &nbytes) == 0) { |
584 | skb = transfer_context; |
585 | max_nbytes = skb->len + skb_tailroom(skb); |
586 | dma_unmap_single(ar->dev, ATH10K_SKB_RXCB(skb)->paddr, |
587 | max_nbytes, DMA_FROM_DEVICE); |
588 | |
589 | if (unlikely(max_nbytes < nbytes)) { |
590 | ath10k_warn(ar, fmt: "rxed more than expected (nbytes %d, max %d)\n" , |
591 | nbytes, max_nbytes); |
592 | dev_kfree_skb_any(skb); |
593 | continue; |
594 | } |
595 | |
596 | skb_put(skb, len: nbytes); |
597 | __skb_queue_tail(list: &list, newsk: skb); |
598 | } |
599 | |
600 | while ((skb = __skb_dequeue(list: &list))) { |
601 | ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc rx ce pipe %d len %d\n" , |
602 | ce_state->id, skb->len); |
603 | |
604 | callback(ar, skb); |
605 | } |
606 | |
607 | ath10k_snoc_rx_post_pipe(pipe: pipe_info); |
608 | } |
609 | |
610 | static void ath10k_snoc_htc_rx_cb(struct ath10k_ce_pipe *ce_state) |
611 | { |
612 | ath10k_snoc_process_rx_cb(ce_state, callback: ath10k_htc_rx_completion_handler); |
613 | } |
614 | |
615 | static void ath10k_snoc_htt_htc_rx_cb(struct ath10k_ce_pipe *ce_state) |
616 | { |
617 | /* CE4 polling needs to be done whenever CE pipe which transports |
618 | * HTT Rx (target->host) is processed. |
619 | */ |
620 | ath10k_ce_per_engine_service(ar: ce_state->ar, CE_POLL_PIPE); |
621 | |
622 | ath10k_snoc_process_rx_cb(ce_state, callback: ath10k_htc_rx_completion_handler); |
623 | } |
624 | |
625 | /* Called by lower (CE) layer when data is received from the Target. |
626 | * WCN3990 firmware uses separate CE(CE11) to transfer pktlog data. |
627 | */ |
628 | static void ath10k_snoc_pktlog_rx_cb(struct ath10k_ce_pipe *ce_state) |
629 | { |
630 | ath10k_snoc_process_rx_cb(ce_state, callback: ath10k_htc_rx_completion_handler); |
631 | } |
632 | |
633 | static void ath10k_snoc_htt_rx_deliver(struct ath10k *ar, struct sk_buff *skb) |
634 | { |
635 | skb_pull(skb, len: sizeof(struct ath10k_htc_hdr)); |
636 | ath10k_htt_t2h_msg_handler(ar, skb); |
637 | } |
638 | |
639 | static void ath10k_snoc_htt_rx_cb(struct ath10k_ce_pipe *ce_state) |
640 | { |
641 | ath10k_ce_per_engine_service(ar: ce_state->ar, CE_POLL_PIPE); |
642 | ath10k_snoc_process_rx_cb(ce_state, callback: ath10k_snoc_htt_rx_deliver); |
643 | } |
644 | |
645 | static void ath10k_snoc_rx_replenish_retry(struct timer_list *t) |
646 | { |
647 | struct ath10k_snoc *ar_snoc = from_timer(ar_snoc, t, rx_post_retry); |
648 | struct ath10k *ar = ar_snoc->ar; |
649 | |
650 | ath10k_snoc_rx_post(ar); |
651 | } |
652 | |
653 | static void ath10k_snoc_htc_tx_cb(struct ath10k_ce_pipe *ce_state) |
654 | { |
655 | struct ath10k *ar = ce_state->ar; |
656 | struct sk_buff_head list; |
657 | struct sk_buff *skb; |
658 | |
659 | __skb_queue_head_init(list: &list); |
660 | while (ath10k_ce_completed_send_next(ce_state, per_transfer_contextp: (void **)&skb) == 0) { |
661 | if (!skb) |
662 | continue; |
663 | |
664 | __skb_queue_tail(list: &list, newsk: skb); |
665 | } |
666 | |
667 | while ((skb = __skb_dequeue(list: &list))) |
668 | ath10k_htc_tx_completion_handler(ar, skb); |
669 | } |
670 | |
671 | static void ath10k_snoc_htt_tx_cb(struct ath10k_ce_pipe *ce_state) |
672 | { |
673 | struct ath10k *ar = ce_state->ar; |
674 | struct sk_buff *skb; |
675 | |
676 | while (ath10k_ce_completed_send_next(ce_state, per_transfer_contextp: (void **)&skb) == 0) { |
677 | if (!skb) |
678 | continue; |
679 | |
680 | dma_unmap_single(ar->dev, ATH10K_SKB_CB(skb)->paddr, |
681 | skb->len, DMA_TO_DEVICE); |
682 | ath10k_htt_hif_tx_complete(ar, skb); |
683 | } |
684 | } |
685 | |
686 | static int ath10k_snoc_hif_tx_sg(struct ath10k *ar, u8 pipe_id, |
687 | struct ath10k_hif_sg_item *items, int n_items) |
688 | { |
689 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
690 | struct ath10k_ce *ce = ath10k_ce_priv(ar); |
691 | struct ath10k_snoc_pipe *snoc_pipe; |
692 | struct ath10k_ce_pipe *ce_pipe; |
693 | int err, i = 0; |
694 | |
695 | snoc_pipe = &ar_snoc->pipe_info[pipe_id]; |
696 | ce_pipe = snoc_pipe->ce_hdl; |
697 | spin_lock_bh(lock: &ce->ce_lock); |
698 | |
699 | for (i = 0; i < n_items - 1; i++) { |
700 | ath10k_dbg(ar, ATH10K_DBG_SNOC, |
701 | "snoc tx item %d paddr %pad len %d n_items %d\n" , |
702 | i, &items[i].paddr, items[i].len, n_items); |
703 | |
704 | err = ath10k_ce_send_nolock(ce_state: ce_pipe, |
705 | per_transfer_context: items[i].transfer_context, |
706 | buffer: items[i].paddr, |
707 | nbytes: items[i].len, |
708 | transfer_id: items[i].transfer_id, |
709 | CE_SEND_FLAG_GATHER); |
710 | if (err) |
711 | goto err; |
712 | } |
713 | |
714 | ath10k_dbg(ar, ATH10K_DBG_SNOC, |
715 | "snoc tx item %d paddr %pad len %d n_items %d\n" , |
716 | i, &items[i].paddr, items[i].len, n_items); |
717 | |
718 | err = ath10k_ce_send_nolock(ce_state: ce_pipe, |
719 | per_transfer_context: items[i].transfer_context, |
720 | buffer: items[i].paddr, |
721 | nbytes: items[i].len, |
722 | transfer_id: items[i].transfer_id, |
723 | flags: 0); |
724 | if (err) |
725 | goto err; |
726 | |
727 | spin_unlock_bh(lock: &ce->ce_lock); |
728 | |
729 | return 0; |
730 | |
731 | err: |
732 | for (; i > 0; i--) |
733 | __ath10k_ce_send_revert(pipe: ce_pipe); |
734 | |
735 | spin_unlock_bh(lock: &ce->ce_lock); |
736 | return err; |
737 | } |
738 | |
739 | static int ath10k_snoc_hif_get_target_info(struct ath10k *ar, |
740 | struct bmi_target_info *target_info) |
741 | { |
742 | target_info->version = ATH10K_HW_WCN3990; |
743 | target_info->type = ATH10K_HW_WCN3990; |
744 | |
745 | return 0; |
746 | } |
747 | |
748 | static u16 ath10k_snoc_hif_get_free_queue_number(struct ath10k *ar, u8 pipe) |
749 | { |
750 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
751 | |
752 | ath10k_dbg(ar, ATH10K_DBG_SNOC, "hif get free queue number\n" ); |
753 | |
754 | return ath10k_ce_num_free_src_entries(pipe: ar_snoc->pipe_info[pipe].ce_hdl); |
755 | } |
756 | |
757 | static void ath10k_snoc_hif_send_complete_check(struct ath10k *ar, u8 pipe, |
758 | int force) |
759 | { |
760 | int resources; |
761 | |
762 | ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc hif send complete check\n" ); |
763 | |
764 | if (!force) { |
765 | resources = ath10k_snoc_hif_get_free_queue_number(ar, pipe); |
766 | |
767 | if (resources > (host_ce_config_wlan[pipe].src_nentries >> 1)) |
768 | return; |
769 | } |
770 | ath10k_ce_per_engine_service(ar, ce_id: pipe); |
771 | } |
772 | |
773 | static int ath10k_snoc_hif_map_service_to_pipe(struct ath10k *ar, |
774 | u16 service_id, |
775 | u8 *ul_pipe, u8 *dl_pipe) |
776 | { |
777 | const struct ce_service_to_pipe *entry; |
778 | bool ul_set = false, dl_set = false; |
779 | int i; |
780 | |
781 | ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc hif map service\n" ); |
782 | |
783 | for (i = 0; i < ARRAY_SIZE(target_service_to_ce_map_wlan); i++) { |
784 | entry = &target_service_to_ce_map_wlan[i]; |
785 | |
786 | if (__le32_to_cpu(entry->service_id) != service_id) |
787 | continue; |
788 | |
789 | switch (__le32_to_cpu(entry->pipedir)) { |
790 | case PIPEDIR_NONE: |
791 | break; |
792 | case PIPEDIR_IN: |
793 | WARN_ON(dl_set); |
794 | *dl_pipe = __le32_to_cpu(entry->pipenum); |
795 | dl_set = true; |
796 | break; |
797 | case PIPEDIR_OUT: |
798 | WARN_ON(ul_set); |
799 | *ul_pipe = __le32_to_cpu(entry->pipenum); |
800 | ul_set = true; |
801 | break; |
802 | case PIPEDIR_INOUT: |
803 | WARN_ON(dl_set); |
804 | WARN_ON(ul_set); |
805 | *dl_pipe = __le32_to_cpu(entry->pipenum); |
806 | *ul_pipe = __le32_to_cpu(entry->pipenum); |
807 | dl_set = true; |
808 | ul_set = true; |
809 | break; |
810 | } |
811 | } |
812 | |
813 | if (!ul_set || !dl_set) |
814 | return -ENOENT; |
815 | |
816 | return 0; |
817 | } |
818 | |
819 | static void ath10k_snoc_hif_get_default_pipe(struct ath10k *ar, |
820 | u8 *ul_pipe, u8 *dl_pipe) |
821 | { |
822 | ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc hif get default pipe\n" ); |
823 | |
824 | (void)ath10k_snoc_hif_map_service_to_pipe(ar, |
825 | service_id: ATH10K_HTC_SVC_ID_RSVD_CTRL, |
826 | ul_pipe, dl_pipe); |
827 | } |
828 | |
829 | static inline void ath10k_snoc_irq_disable(struct ath10k *ar) |
830 | { |
831 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
832 | int id; |
833 | |
834 | for (id = 0; id < CE_COUNT_MAX; id++) |
835 | disable_irq(irq: ar_snoc->ce_irqs[id].irq_line); |
836 | } |
837 | |
838 | static inline void ath10k_snoc_irq_enable(struct ath10k *ar) |
839 | { |
840 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
841 | int id; |
842 | |
843 | for (id = 0; id < CE_COUNT_MAX; id++) |
844 | enable_irq(irq: ar_snoc->ce_irqs[id].irq_line); |
845 | } |
846 | |
847 | static void ath10k_snoc_rx_pipe_cleanup(struct ath10k_snoc_pipe *snoc_pipe) |
848 | { |
849 | struct ath10k_ce_pipe *ce_pipe; |
850 | struct ath10k_ce_ring *ce_ring; |
851 | struct sk_buff *skb; |
852 | struct ath10k *ar; |
853 | int i; |
854 | |
855 | ar = snoc_pipe->hif_ce_state; |
856 | ce_pipe = snoc_pipe->ce_hdl; |
857 | ce_ring = ce_pipe->dest_ring; |
858 | |
859 | if (!ce_ring) |
860 | return; |
861 | |
862 | if (!snoc_pipe->buf_sz) |
863 | return; |
864 | |
865 | for (i = 0; i < ce_ring->nentries; i++) { |
866 | skb = ce_ring->per_transfer_context[i]; |
867 | if (!skb) |
868 | continue; |
869 | |
870 | ce_ring->per_transfer_context[i] = NULL; |
871 | |
872 | dma_unmap_single(ar->dev, ATH10K_SKB_RXCB(skb)->paddr, |
873 | skb->len + skb_tailroom(skb), |
874 | DMA_FROM_DEVICE); |
875 | dev_kfree_skb_any(skb); |
876 | } |
877 | } |
878 | |
879 | static void ath10k_snoc_tx_pipe_cleanup(struct ath10k_snoc_pipe *snoc_pipe) |
880 | { |
881 | struct ath10k_ce_pipe *ce_pipe; |
882 | struct ath10k_ce_ring *ce_ring; |
883 | struct sk_buff *skb; |
884 | struct ath10k *ar; |
885 | int i; |
886 | |
887 | ar = snoc_pipe->hif_ce_state; |
888 | ce_pipe = snoc_pipe->ce_hdl; |
889 | ce_ring = ce_pipe->src_ring; |
890 | |
891 | if (!ce_ring) |
892 | return; |
893 | |
894 | if (!snoc_pipe->buf_sz) |
895 | return; |
896 | |
897 | for (i = 0; i < ce_ring->nentries; i++) { |
898 | skb = ce_ring->per_transfer_context[i]; |
899 | if (!skb) |
900 | continue; |
901 | |
902 | ce_ring->per_transfer_context[i] = NULL; |
903 | |
904 | ath10k_htc_tx_completion_handler(ar, skb); |
905 | } |
906 | } |
907 | |
908 | static void ath10k_snoc_buffer_cleanup(struct ath10k *ar) |
909 | { |
910 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
911 | struct ath10k_snoc_pipe *pipe_info; |
912 | int pipe_num; |
913 | |
914 | del_timer_sync(timer: &ar_snoc->rx_post_retry); |
915 | for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) { |
916 | pipe_info = &ar_snoc->pipe_info[pipe_num]; |
917 | ath10k_snoc_rx_pipe_cleanup(snoc_pipe: pipe_info); |
918 | ath10k_snoc_tx_pipe_cleanup(snoc_pipe: pipe_info); |
919 | } |
920 | } |
921 | |
922 | static void ath10k_snoc_hif_stop(struct ath10k *ar) |
923 | { |
924 | if (!test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) |
925 | ath10k_snoc_irq_disable(ar); |
926 | |
927 | ath10k_core_napi_sync_disable(ar); |
928 | ath10k_snoc_buffer_cleanup(ar); |
929 | ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif stop\n" ); |
930 | } |
931 | |
932 | static int ath10k_snoc_hif_start(struct ath10k *ar) |
933 | { |
934 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
935 | |
936 | bitmap_clear(map: ar_snoc->pending_ce_irqs, start: 0, CE_COUNT_MAX); |
937 | |
938 | dev_set_threaded(dev: &ar->napi_dev, threaded: true); |
939 | ath10k_core_napi_enable(ar); |
940 | ath10k_snoc_irq_enable(ar); |
941 | ath10k_snoc_rx_post(ar); |
942 | |
943 | clear_bit(nr: ATH10K_SNOC_FLAG_RECOVERY, addr: &ar_snoc->flags); |
944 | |
945 | ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif start\n" ); |
946 | |
947 | return 0; |
948 | } |
949 | |
950 | static int ath10k_snoc_init_pipes(struct ath10k *ar) |
951 | { |
952 | int i, ret; |
953 | |
954 | for (i = 0; i < CE_COUNT; i++) { |
955 | ret = ath10k_ce_init_pipe(ar, ce_id: i, attr: &host_ce_config_wlan[i]); |
956 | if (ret) { |
957 | ath10k_err(ar, fmt: "failed to initialize copy engine pipe %d: %d\n" , |
958 | i, ret); |
959 | return ret; |
960 | } |
961 | } |
962 | |
963 | return 0; |
964 | } |
965 | |
966 | static int ath10k_snoc_wlan_enable(struct ath10k *ar, |
967 | enum ath10k_firmware_mode fw_mode) |
968 | { |
969 | struct ath10k_tgt_pipe_cfg tgt_cfg[CE_COUNT_MAX]; |
970 | struct ath10k_qmi_wlan_enable_cfg cfg; |
971 | enum wlfw_driver_mode_enum_v01 mode; |
972 | int pipe_num; |
973 | |
974 | for (pipe_num = 0; pipe_num < CE_COUNT_MAX; pipe_num++) { |
975 | tgt_cfg[pipe_num].pipe_num = |
976 | target_ce_config_wlan[pipe_num].pipenum; |
977 | tgt_cfg[pipe_num].pipe_dir = |
978 | target_ce_config_wlan[pipe_num].pipedir; |
979 | tgt_cfg[pipe_num].nentries = |
980 | target_ce_config_wlan[pipe_num].nentries; |
981 | tgt_cfg[pipe_num].nbytes_max = |
982 | target_ce_config_wlan[pipe_num].nbytes_max; |
983 | tgt_cfg[pipe_num].flags = |
984 | target_ce_config_wlan[pipe_num].flags; |
985 | tgt_cfg[pipe_num].reserved = 0; |
986 | } |
987 | |
988 | cfg.num_ce_tgt_cfg = sizeof(target_ce_config_wlan) / |
989 | sizeof(struct ath10k_tgt_pipe_cfg); |
990 | cfg.ce_tgt_cfg = (struct ath10k_tgt_pipe_cfg *) |
991 | &tgt_cfg; |
992 | cfg.num_ce_svc_pipe_cfg = sizeof(target_service_to_ce_map_wlan) / |
993 | sizeof(struct ath10k_svc_pipe_cfg); |
994 | cfg.ce_svc_cfg = (struct ath10k_svc_pipe_cfg *) |
995 | &target_service_to_ce_map_wlan; |
996 | cfg.num_shadow_reg_cfg = ARRAY_SIZE(target_shadow_reg_cfg_map); |
997 | cfg.shadow_reg_cfg = (struct ath10k_shadow_reg_cfg *) |
998 | &target_shadow_reg_cfg_map; |
999 | |
1000 | switch (fw_mode) { |
1001 | case ATH10K_FIRMWARE_MODE_NORMAL: |
1002 | mode = QMI_WLFW_MISSION_V01; |
1003 | break; |
1004 | case ATH10K_FIRMWARE_MODE_UTF: |
1005 | mode = QMI_WLFW_FTM_V01; |
1006 | break; |
1007 | default: |
1008 | ath10k_err(ar, fmt: "invalid firmware mode %d\n" , fw_mode); |
1009 | return -EINVAL; |
1010 | } |
1011 | |
1012 | return ath10k_qmi_wlan_enable(ar, config: &cfg, mode, |
1013 | NULL); |
1014 | } |
1015 | |
1016 | static int ath10k_hw_power_on(struct ath10k *ar) |
1017 | { |
1018 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
1019 | int ret; |
1020 | |
1021 | ath10k_dbg(ar, ATH10K_DBG_SNOC, "soc power on\n" ); |
1022 | |
1023 | ret = regulator_bulk_enable(num_consumers: ar_snoc->num_vregs, consumers: ar_snoc->vregs); |
1024 | if (ret) |
1025 | return ret; |
1026 | |
1027 | ret = clk_bulk_prepare_enable(num_clks: ar_snoc->num_clks, clks: ar_snoc->clks); |
1028 | if (ret) |
1029 | goto vreg_off; |
1030 | |
1031 | return ret; |
1032 | |
1033 | vreg_off: |
1034 | regulator_bulk_disable(num_consumers: ar_snoc->num_vregs, consumers: ar_snoc->vregs); |
1035 | return ret; |
1036 | } |
1037 | |
1038 | static int ath10k_hw_power_off(struct ath10k *ar) |
1039 | { |
1040 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
1041 | |
1042 | ath10k_dbg(ar, ATH10K_DBG_SNOC, "soc power off\n" ); |
1043 | |
1044 | clk_bulk_disable_unprepare(num_clks: ar_snoc->num_clks, clks: ar_snoc->clks); |
1045 | |
1046 | return regulator_bulk_disable(num_consumers: ar_snoc->num_vregs, consumers: ar_snoc->vregs); |
1047 | } |
1048 | |
1049 | static void ath10k_snoc_wlan_disable(struct ath10k *ar) |
1050 | { |
1051 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
1052 | |
1053 | /* If both ATH10K_FLAG_CRASH_FLUSH and ATH10K_SNOC_FLAG_RECOVERY |
1054 | * flags are not set, it means that the driver has restarted |
1055 | * due to a crash inject via debugfs. In this case, the driver |
1056 | * needs to restart the firmware and hence send qmi wlan disable, |
1057 | * during the driver restart sequence. |
1058 | */ |
1059 | if (!test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags) || |
1060 | !test_bit(ATH10K_SNOC_FLAG_RECOVERY, &ar_snoc->flags)) |
1061 | ath10k_qmi_wlan_disable(ar); |
1062 | } |
1063 | |
1064 | static void ath10k_snoc_hif_power_down(struct ath10k *ar) |
1065 | { |
1066 | ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif power down\n" ); |
1067 | |
1068 | ath10k_snoc_wlan_disable(ar); |
1069 | ath10k_ce_free_rri(ar); |
1070 | ath10k_hw_power_off(ar); |
1071 | } |
1072 | |
1073 | static int ath10k_snoc_hif_power_up(struct ath10k *ar, |
1074 | enum ath10k_firmware_mode fw_mode) |
1075 | { |
1076 | int ret; |
1077 | |
1078 | ath10k_dbg(ar, ATH10K_DBG_SNOC, "%s:WCN3990 driver state = %d\n" , |
1079 | __func__, ar->state); |
1080 | |
1081 | ret = ath10k_hw_power_on(ar); |
1082 | if (ret) { |
1083 | ath10k_err(ar, fmt: "failed to power on device: %d\n" , ret); |
1084 | return ret; |
1085 | } |
1086 | |
1087 | ret = ath10k_snoc_wlan_enable(ar, fw_mode); |
1088 | if (ret) { |
1089 | ath10k_err(ar, fmt: "failed to enable wcn3990: %d\n" , ret); |
1090 | goto err_hw_power_off; |
1091 | } |
1092 | |
1093 | ath10k_ce_alloc_rri(ar); |
1094 | |
1095 | ret = ath10k_snoc_init_pipes(ar); |
1096 | if (ret) { |
1097 | ath10k_err(ar, fmt: "failed to initialize CE: %d\n" , ret); |
1098 | goto err_free_rri; |
1099 | } |
1100 | |
1101 | ath10k_ce_enable_interrupts(ar); |
1102 | |
1103 | return 0; |
1104 | |
1105 | err_free_rri: |
1106 | ath10k_ce_free_rri(ar); |
1107 | ath10k_snoc_wlan_disable(ar); |
1108 | |
1109 | err_hw_power_off: |
1110 | ath10k_hw_power_off(ar); |
1111 | |
1112 | return ret; |
1113 | } |
1114 | |
1115 | static int ath10k_snoc_hif_set_target_log_mode(struct ath10k *ar, |
1116 | u8 fw_log_mode) |
1117 | { |
1118 | u8 fw_dbg_mode; |
1119 | |
1120 | if (fw_log_mode) |
1121 | fw_dbg_mode = ATH10K_ENABLE_FW_LOG_CE; |
1122 | else |
1123 | fw_dbg_mode = ATH10K_ENABLE_FW_LOG_DIAG; |
1124 | |
1125 | return ath10k_qmi_set_fw_log_mode(ar, fw_log_mode: fw_dbg_mode); |
1126 | } |
1127 | |
1128 | #ifdef CONFIG_PM |
1129 | static int ath10k_snoc_hif_suspend(struct ath10k *ar) |
1130 | { |
1131 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
1132 | int ret; |
1133 | |
1134 | if (!device_may_wakeup(dev: ar->dev)) |
1135 | return -EPERM; |
1136 | |
1137 | ret = enable_irq_wake(irq: ar_snoc->ce_irqs[ATH10K_SNOC_WAKE_IRQ].irq_line); |
1138 | if (ret) { |
1139 | ath10k_err(ar, fmt: "failed to enable wakeup irq :%d\n" , ret); |
1140 | return ret; |
1141 | } |
1142 | |
1143 | ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc device suspended\n" ); |
1144 | |
1145 | return ret; |
1146 | } |
1147 | |
1148 | static int ath10k_snoc_hif_resume(struct ath10k *ar) |
1149 | { |
1150 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
1151 | int ret; |
1152 | |
1153 | if (!device_may_wakeup(dev: ar->dev)) |
1154 | return -EPERM; |
1155 | |
1156 | ret = disable_irq_wake(irq: ar_snoc->ce_irqs[ATH10K_SNOC_WAKE_IRQ].irq_line); |
1157 | if (ret) { |
1158 | ath10k_err(ar, fmt: "failed to disable wakeup irq: %d\n" , ret); |
1159 | return ret; |
1160 | } |
1161 | |
1162 | ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc device resumed\n" ); |
1163 | |
1164 | return ret; |
1165 | } |
1166 | #endif |
1167 | |
1168 | static const struct ath10k_hif_ops ath10k_snoc_hif_ops = { |
1169 | .read32 = ath10k_snoc_read32, |
1170 | .write32 = ath10k_snoc_write32, |
1171 | .start = ath10k_snoc_hif_start, |
1172 | .stop = ath10k_snoc_hif_stop, |
1173 | .map_service_to_pipe = ath10k_snoc_hif_map_service_to_pipe, |
1174 | .get_default_pipe = ath10k_snoc_hif_get_default_pipe, |
1175 | .power_up = ath10k_snoc_hif_power_up, |
1176 | .power_down = ath10k_snoc_hif_power_down, |
1177 | .tx_sg = ath10k_snoc_hif_tx_sg, |
1178 | .send_complete_check = ath10k_snoc_hif_send_complete_check, |
1179 | .get_free_queue_number = ath10k_snoc_hif_get_free_queue_number, |
1180 | .get_target_info = ath10k_snoc_hif_get_target_info, |
1181 | .set_target_log_mode = ath10k_snoc_hif_set_target_log_mode, |
1182 | |
1183 | #ifdef CONFIG_PM |
1184 | .suspend = ath10k_snoc_hif_suspend, |
1185 | .resume = ath10k_snoc_hif_resume, |
1186 | #endif |
1187 | }; |
1188 | |
1189 | static const struct ath10k_bus_ops ath10k_snoc_bus_ops = { |
1190 | .read32 = ath10k_snoc_read32, |
1191 | .write32 = ath10k_snoc_write32, |
1192 | }; |
1193 | |
1194 | static int ath10k_snoc_get_ce_id_from_irq(struct ath10k *ar, int irq) |
1195 | { |
1196 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
1197 | int i; |
1198 | |
1199 | for (i = 0; i < CE_COUNT_MAX; i++) { |
1200 | if (ar_snoc->ce_irqs[i].irq_line == irq) |
1201 | return i; |
1202 | } |
1203 | ath10k_err(ar, fmt: "No matching CE id for irq %d\n" , irq); |
1204 | |
1205 | return -EINVAL; |
1206 | } |
1207 | |
1208 | static irqreturn_t ath10k_snoc_per_engine_handler(int irq, void *arg) |
1209 | { |
1210 | struct ath10k *ar = arg; |
1211 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
1212 | int ce_id = ath10k_snoc_get_ce_id_from_irq(ar, irq); |
1213 | |
1214 | if (ce_id < 0 || ce_id >= ARRAY_SIZE(ar_snoc->pipe_info)) { |
1215 | ath10k_warn(ar, fmt: "unexpected/invalid irq %d ce_id %d\n" , irq, |
1216 | ce_id); |
1217 | return IRQ_HANDLED; |
1218 | } |
1219 | |
1220 | ath10k_ce_disable_interrupt(ar, ce_id); |
1221 | set_bit(nr: ce_id, addr: ar_snoc->pending_ce_irqs); |
1222 | |
1223 | napi_schedule(n: &ar->napi); |
1224 | |
1225 | return IRQ_HANDLED; |
1226 | } |
1227 | |
1228 | static int ath10k_snoc_napi_poll(struct napi_struct *ctx, int budget) |
1229 | { |
1230 | struct ath10k *ar = container_of(ctx, struct ath10k, napi); |
1231 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
1232 | int done = 0; |
1233 | int ce_id; |
1234 | |
1235 | if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) { |
1236 | napi_complete(n: ctx); |
1237 | return done; |
1238 | } |
1239 | |
1240 | for (ce_id = 0; ce_id < CE_COUNT; ce_id++) |
1241 | if (test_and_clear_bit(nr: ce_id, addr: ar_snoc->pending_ce_irqs)) { |
1242 | ath10k_ce_per_engine_service(ar, ce_id); |
1243 | ath10k_ce_enable_interrupt(ar, ce_id); |
1244 | } |
1245 | |
1246 | done = ath10k_htt_txrx_compl_task(ar, budget); |
1247 | |
1248 | if (done < budget) |
1249 | napi_complete(n: ctx); |
1250 | |
1251 | return done; |
1252 | } |
1253 | |
1254 | static void ath10k_snoc_init_napi(struct ath10k *ar) |
1255 | { |
1256 | netif_napi_add(dev: &ar->napi_dev, napi: &ar->napi, poll: ath10k_snoc_napi_poll); |
1257 | } |
1258 | |
1259 | static int ath10k_snoc_request_irq(struct ath10k *ar) |
1260 | { |
1261 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
1262 | int ret, id; |
1263 | |
1264 | for (id = 0; id < CE_COUNT_MAX; id++) { |
1265 | ret = request_irq(irq: ar_snoc->ce_irqs[id].irq_line, |
1266 | handler: ath10k_snoc_per_engine_handler, |
1267 | IRQF_NO_AUTOEN, name: ce_name[id], dev: ar); |
1268 | if (ret) { |
1269 | ath10k_err(ar, |
1270 | fmt: "failed to register IRQ handler for CE %d: %d\n" , |
1271 | id, ret); |
1272 | goto err_irq; |
1273 | } |
1274 | } |
1275 | |
1276 | return 0; |
1277 | |
1278 | err_irq: |
1279 | for (id -= 1; id >= 0; id--) |
1280 | free_irq(ar_snoc->ce_irqs[id].irq_line, ar); |
1281 | |
1282 | return ret; |
1283 | } |
1284 | |
1285 | static void ath10k_snoc_free_irq(struct ath10k *ar) |
1286 | { |
1287 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
1288 | int id; |
1289 | |
1290 | for (id = 0; id < CE_COUNT_MAX; id++) |
1291 | free_irq(ar_snoc->ce_irqs[id].irq_line, ar); |
1292 | } |
1293 | |
1294 | static int ath10k_snoc_resource_init(struct ath10k *ar) |
1295 | { |
1296 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
1297 | struct platform_device *pdev; |
1298 | struct resource *res; |
1299 | int i, ret = 0; |
1300 | |
1301 | pdev = ar_snoc->dev; |
1302 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "membase" ); |
1303 | if (!res) { |
1304 | ath10k_err(ar, fmt: "Memory base not found in DT\n" ); |
1305 | return -EINVAL; |
1306 | } |
1307 | |
1308 | ar_snoc->mem_pa = res->start; |
1309 | ar_snoc->mem = devm_ioremap(dev: &pdev->dev, offset: ar_snoc->mem_pa, |
1310 | size: resource_size(res)); |
1311 | if (!ar_snoc->mem) { |
1312 | ath10k_err(ar, fmt: "Memory base ioremap failed with physical address %pa\n" , |
1313 | &ar_snoc->mem_pa); |
1314 | return -EINVAL; |
1315 | } |
1316 | |
1317 | for (i = 0; i < CE_COUNT; i++) { |
1318 | ret = platform_get_irq(ar_snoc->dev, i); |
1319 | if (ret < 0) |
1320 | return ret; |
1321 | ar_snoc->ce_irqs[i].irq_line = ret; |
1322 | } |
1323 | |
1324 | ret = device_property_read_u32(dev: &pdev->dev, propname: "qcom,xo-cal-data" , |
1325 | val: &ar_snoc->xo_cal_data); |
1326 | ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc xo-cal-data return %d\n" , ret); |
1327 | if (ret == 0) { |
1328 | ar_snoc->xo_cal_supported = true; |
1329 | ath10k_dbg(ar, ATH10K_DBG_SNOC, "xo cal data %x\n" , |
1330 | ar_snoc->xo_cal_data); |
1331 | } |
1332 | |
1333 | return 0; |
1334 | } |
1335 | |
1336 | static void ath10k_snoc_quirks_init(struct ath10k *ar) |
1337 | { |
1338 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
1339 | struct device *dev = &ar_snoc->dev->dev; |
1340 | |
1341 | if (of_property_read_bool(np: dev->of_node, propname: "qcom,snoc-host-cap-8bit-quirk" )) |
1342 | set_bit(nr: ATH10K_SNOC_FLAG_8BIT_HOST_CAP_QUIRK, addr: &ar_snoc->flags); |
1343 | } |
1344 | |
1345 | int ath10k_snoc_fw_indication(struct ath10k *ar, u64 type) |
1346 | { |
1347 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
1348 | struct ath10k_bus_params bus_params = {}; |
1349 | int ret; |
1350 | |
1351 | if (test_bit(ATH10K_SNOC_FLAG_UNREGISTERING, &ar_snoc->flags)) |
1352 | return 0; |
1353 | |
1354 | switch (type) { |
1355 | case ATH10K_QMI_EVENT_FW_READY_IND: |
1356 | if (test_bit(ATH10K_SNOC_FLAG_REGISTERED, &ar_snoc->flags)) { |
1357 | ath10k_core_start_recovery(ar); |
1358 | break; |
1359 | } |
1360 | |
1361 | bus_params.dev_type = ATH10K_DEV_TYPE_LL; |
1362 | bus_params.chip_id = ar_snoc->target_info.soc_version; |
1363 | ret = ath10k_core_register(ar, bus_params: &bus_params); |
1364 | if (ret) { |
1365 | ath10k_err(ar, fmt: "Failed to register driver core: %d\n" , |
1366 | ret); |
1367 | return ret; |
1368 | } |
1369 | set_bit(nr: ATH10K_SNOC_FLAG_REGISTERED, addr: &ar_snoc->flags); |
1370 | break; |
1371 | case ATH10K_QMI_EVENT_FW_DOWN_IND: |
1372 | set_bit(nr: ATH10K_SNOC_FLAG_RECOVERY, addr: &ar_snoc->flags); |
1373 | set_bit(nr: ATH10K_FLAG_CRASH_FLUSH, addr: &ar->dev_flags); |
1374 | break; |
1375 | default: |
1376 | ath10k_err(ar, fmt: "invalid fw indication: %llx\n" , type); |
1377 | return -EINVAL; |
1378 | } |
1379 | |
1380 | return 0; |
1381 | } |
1382 | |
1383 | static int ath10k_snoc_setup_resource(struct ath10k *ar) |
1384 | { |
1385 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
1386 | struct ath10k_ce *ce = ath10k_ce_priv(ar); |
1387 | struct ath10k_snoc_pipe *pipe; |
1388 | int i, ret; |
1389 | |
1390 | timer_setup(&ar_snoc->rx_post_retry, ath10k_snoc_rx_replenish_retry, 0); |
1391 | spin_lock_init(&ce->ce_lock); |
1392 | for (i = 0; i < CE_COUNT; i++) { |
1393 | pipe = &ar_snoc->pipe_info[i]; |
1394 | pipe->ce_hdl = &ce->ce_states[i]; |
1395 | pipe->pipe_num = i; |
1396 | pipe->hif_ce_state = ar; |
1397 | |
1398 | ret = ath10k_ce_alloc_pipe(ar, ce_id: i, attr: &host_ce_config_wlan[i]); |
1399 | if (ret) { |
1400 | ath10k_err(ar, fmt: "failed to allocate copy engine pipe %d: %d\n" , |
1401 | i, ret); |
1402 | return ret; |
1403 | } |
1404 | |
1405 | pipe->buf_sz = host_ce_config_wlan[i].src_sz_max; |
1406 | } |
1407 | ath10k_snoc_init_napi(ar); |
1408 | |
1409 | return 0; |
1410 | } |
1411 | |
1412 | static void ath10k_snoc_release_resource(struct ath10k *ar) |
1413 | { |
1414 | int i; |
1415 | |
1416 | netif_napi_del(napi: &ar->napi); |
1417 | for (i = 0; i < CE_COUNT; i++) |
1418 | ath10k_ce_free_pipe(ar, ce_id: i); |
1419 | } |
1420 | |
1421 | static void ath10k_msa_dump_memory(struct ath10k *ar, |
1422 | struct ath10k_fw_crash_data *crash_data) |
1423 | { |
1424 | const struct ath10k_hw_mem_layout *mem_layout; |
1425 | const struct ath10k_mem_region *current_region; |
1426 | struct ath10k_dump_ram_data_hdr *hdr; |
1427 | size_t buf_len; |
1428 | u8 *buf; |
1429 | |
1430 | if (!crash_data || !crash_data->ramdump_buf) |
1431 | return; |
1432 | |
1433 | mem_layout = ath10k_coredump_get_mem_layout(ar); |
1434 | if (!mem_layout) |
1435 | return; |
1436 | |
1437 | current_region = &mem_layout->region_table.regions[0]; |
1438 | |
1439 | buf = crash_data->ramdump_buf; |
1440 | buf_len = crash_data->ramdump_buf_len; |
1441 | memset(buf, 0, buf_len); |
1442 | |
1443 | /* Reserve space for the header. */ |
1444 | hdr = (void *)buf; |
1445 | buf += sizeof(*hdr); |
1446 | buf_len -= sizeof(*hdr); |
1447 | |
1448 | hdr->region_type = cpu_to_le32(current_region->type); |
1449 | hdr->start = cpu_to_le32((unsigned long)ar->msa.vaddr); |
1450 | hdr->length = cpu_to_le32(ar->msa.mem_size); |
1451 | |
1452 | if (current_region->len < ar->msa.mem_size) { |
1453 | memcpy(buf, ar->msa.vaddr, current_region->len); |
1454 | ath10k_warn(ar, fmt: "msa dump length is less than msa size %x, %x\n" , |
1455 | current_region->len, ar->msa.mem_size); |
1456 | } else { |
1457 | memcpy(buf, ar->msa.vaddr, ar->msa.mem_size); |
1458 | } |
1459 | } |
1460 | |
1461 | void ath10k_snoc_fw_crashed_dump(struct ath10k *ar) |
1462 | { |
1463 | struct ath10k_fw_crash_data *crash_data; |
1464 | char guid[UUID_STRING_LEN + 1]; |
1465 | |
1466 | mutex_lock(&ar->dump_mutex); |
1467 | |
1468 | spin_lock_bh(lock: &ar->data_lock); |
1469 | ar->stats.fw_crash_counter++; |
1470 | spin_unlock_bh(lock: &ar->data_lock); |
1471 | |
1472 | crash_data = ath10k_coredump_new(ar); |
1473 | |
1474 | if (crash_data) |
1475 | scnprintf(buf: guid, size: sizeof(guid), fmt: "%pUl" , &crash_data->guid); |
1476 | else |
1477 | scnprintf(buf: guid, size: sizeof(guid), fmt: "n/a" ); |
1478 | |
1479 | ath10k_err(ar, fmt: "firmware crashed! (guid %s)\n" , guid); |
1480 | ath10k_print_driver_info(ar); |
1481 | ath10k_msa_dump_memory(ar, crash_data); |
1482 | mutex_unlock(lock: &ar->dump_mutex); |
1483 | } |
1484 | |
1485 | static int ath10k_snoc_modem_notify(struct notifier_block *nb, unsigned long action, |
1486 | void *data) |
1487 | { |
1488 | struct ath10k_snoc *ar_snoc = container_of(nb, struct ath10k_snoc, nb); |
1489 | struct ath10k *ar = ar_snoc->ar; |
1490 | struct qcom_ssr_notify_data *notify_data = data; |
1491 | |
1492 | switch (action) { |
1493 | case QCOM_SSR_BEFORE_POWERUP: |
1494 | ath10k_dbg(ar, ATH10K_DBG_SNOC, "received modem starting event\n" ); |
1495 | clear_bit(nr: ATH10K_SNOC_FLAG_MODEM_STOPPED, addr: &ar_snoc->flags); |
1496 | break; |
1497 | |
1498 | case QCOM_SSR_AFTER_POWERUP: |
1499 | ath10k_dbg(ar, ATH10K_DBG_SNOC, "received modem running event\n" ); |
1500 | break; |
1501 | |
1502 | case QCOM_SSR_BEFORE_SHUTDOWN: |
1503 | ath10k_dbg(ar, ATH10K_DBG_SNOC, "received modem %s event\n" , |
1504 | notify_data->crashed ? "crashed" : "stopping" ); |
1505 | if (!notify_data->crashed) |
1506 | set_bit(nr: ATH10K_SNOC_FLAG_MODEM_STOPPED, addr: &ar_snoc->flags); |
1507 | else |
1508 | clear_bit(nr: ATH10K_SNOC_FLAG_MODEM_STOPPED, addr: &ar_snoc->flags); |
1509 | break; |
1510 | |
1511 | case QCOM_SSR_AFTER_SHUTDOWN: |
1512 | ath10k_dbg(ar, ATH10K_DBG_SNOC, "received modem offline event\n" ); |
1513 | break; |
1514 | |
1515 | default: |
1516 | ath10k_err(ar, fmt: "received unrecognized event %lu\n" , action); |
1517 | break; |
1518 | } |
1519 | |
1520 | return NOTIFY_OK; |
1521 | } |
1522 | |
1523 | static int ath10k_modem_init(struct ath10k *ar) |
1524 | { |
1525 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
1526 | void *notifier; |
1527 | int ret; |
1528 | |
1529 | ar_snoc->nb.notifier_call = ath10k_snoc_modem_notify; |
1530 | |
1531 | notifier = qcom_register_ssr_notifier(name: "mpss" , nb: &ar_snoc->nb); |
1532 | if (IS_ERR(ptr: notifier)) { |
1533 | ret = PTR_ERR(ptr: notifier); |
1534 | ath10k_err(ar, fmt: "failed to initialize modem notifier: %d\n" , ret); |
1535 | return ret; |
1536 | } |
1537 | |
1538 | ar_snoc->notifier = notifier; |
1539 | |
1540 | return 0; |
1541 | } |
1542 | |
1543 | static void ath10k_modem_deinit(struct ath10k *ar) |
1544 | { |
1545 | int ret; |
1546 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
1547 | |
1548 | ret = qcom_unregister_ssr_notifier(notify: ar_snoc->notifier, nb: &ar_snoc->nb); |
1549 | if (ret) |
1550 | ath10k_err(ar, fmt: "error %d unregistering notifier\n" , ret); |
1551 | } |
1552 | |
1553 | static int ath10k_setup_msa_resources(struct ath10k *ar, u32 msa_size) |
1554 | { |
1555 | struct device *dev = ar->dev; |
1556 | struct device_node *node; |
1557 | struct resource r; |
1558 | int ret; |
1559 | |
1560 | node = of_parse_phandle(np: dev->of_node, phandle_name: "memory-region" , index: 0); |
1561 | if (node) { |
1562 | ret = of_address_to_resource(dev: node, index: 0, r: &r); |
1563 | of_node_put(node); |
1564 | if (ret) { |
1565 | dev_err(dev, "failed to resolve msa fixed region\n" ); |
1566 | return ret; |
1567 | } |
1568 | |
1569 | ar->msa.paddr = r.start; |
1570 | ar->msa.mem_size = resource_size(res: &r); |
1571 | ar->msa.vaddr = devm_memremap(dev, offset: ar->msa.paddr, |
1572 | size: ar->msa.mem_size, |
1573 | flags: MEMREMAP_WT); |
1574 | if (IS_ERR(ptr: ar->msa.vaddr)) { |
1575 | dev_err(dev, "failed to map memory region: %pa\n" , |
1576 | &r.start); |
1577 | return PTR_ERR(ptr: ar->msa.vaddr); |
1578 | } |
1579 | } else { |
1580 | ar->msa.vaddr = dmam_alloc_coherent(dev, size: msa_size, |
1581 | dma_handle: &ar->msa.paddr, |
1582 | GFP_KERNEL); |
1583 | if (!ar->msa.vaddr) { |
1584 | ath10k_err(ar, fmt: "failed to allocate dma memory for msa region\n" ); |
1585 | return -ENOMEM; |
1586 | } |
1587 | ar->msa.mem_size = msa_size; |
1588 | } |
1589 | |
1590 | ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi msa.paddr: %pad , msa.vaddr: 0x%p\n" , |
1591 | &ar->msa.paddr, |
1592 | ar->msa.vaddr); |
1593 | |
1594 | return 0; |
1595 | } |
1596 | |
1597 | static int ath10k_fw_init(struct ath10k *ar) |
1598 | { |
1599 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
1600 | struct device *host_dev = &ar_snoc->dev->dev; |
1601 | struct platform_device_info info; |
1602 | struct iommu_domain *iommu_dom; |
1603 | struct platform_device *pdev; |
1604 | struct device_node *node; |
1605 | int ret; |
1606 | |
1607 | node = of_get_child_by_name(node: host_dev->of_node, name: "wifi-firmware" ); |
1608 | if (!node) { |
1609 | ar_snoc->use_tz = true; |
1610 | return 0; |
1611 | } |
1612 | |
1613 | memset(&info, 0, sizeof(info)); |
1614 | info.fwnode = &node->fwnode; |
1615 | info.parent = host_dev; |
1616 | info.name = node->name; |
1617 | info.dma_mask = DMA_BIT_MASK(32); |
1618 | |
1619 | pdev = platform_device_register_full(pdevinfo: &info); |
1620 | if (IS_ERR(ptr: pdev)) { |
1621 | of_node_put(node); |
1622 | return PTR_ERR(ptr: pdev); |
1623 | } |
1624 | |
1625 | pdev->dev.of_node = node; |
1626 | |
1627 | ret = of_dma_configure(dev: &pdev->dev, np: node, force_dma: true); |
1628 | if (ret) { |
1629 | ath10k_err(ar, fmt: "dma configure fail: %d\n" , ret); |
1630 | goto err_unregister; |
1631 | } |
1632 | |
1633 | ar_snoc->fw.dev = &pdev->dev; |
1634 | |
1635 | iommu_dom = iommu_domain_alloc(bus: &platform_bus_type); |
1636 | if (!iommu_dom) { |
1637 | ath10k_err(ar, fmt: "failed to allocate iommu domain\n" ); |
1638 | ret = -ENOMEM; |
1639 | goto err_unregister; |
1640 | } |
1641 | |
1642 | ret = iommu_attach_device(domain: iommu_dom, dev: ar_snoc->fw.dev); |
1643 | if (ret) { |
1644 | ath10k_err(ar, fmt: "could not attach device: %d\n" , ret); |
1645 | goto err_iommu_free; |
1646 | } |
1647 | |
1648 | ar_snoc->fw.iommu_domain = iommu_dom; |
1649 | ar_snoc->fw.fw_start_addr = ar->msa.paddr; |
1650 | |
1651 | ret = iommu_map(domain: iommu_dom, iova: ar_snoc->fw.fw_start_addr, |
1652 | paddr: ar->msa.paddr, size: ar->msa.mem_size, |
1653 | IOMMU_READ | IOMMU_WRITE, GFP_KERNEL); |
1654 | if (ret) { |
1655 | ath10k_err(ar, fmt: "failed to map firmware region: %d\n" , ret); |
1656 | goto err_iommu_detach; |
1657 | } |
1658 | |
1659 | of_node_put(node); |
1660 | |
1661 | return 0; |
1662 | |
1663 | err_iommu_detach: |
1664 | iommu_detach_device(domain: iommu_dom, dev: ar_snoc->fw.dev); |
1665 | |
1666 | err_iommu_free: |
1667 | iommu_domain_free(domain: iommu_dom); |
1668 | |
1669 | err_unregister: |
1670 | platform_device_unregister(pdev); |
1671 | of_node_put(node); |
1672 | |
1673 | return ret; |
1674 | } |
1675 | |
1676 | static int ath10k_fw_deinit(struct ath10k *ar) |
1677 | { |
1678 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
1679 | const size_t mapped_size = ar_snoc->fw.mapped_mem_size; |
1680 | struct iommu_domain *iommu; |
1681 | size_t unmapped_size; |
1682 | |
1683 | if (ar_snoc->use_tz) |
1684 | return 0; |
1685 | |
1686 | iommu = ar_snoc->fw.iommu_domain; |
1687 | |
1688 | unmapped_size = iommu_unmap(domain: iommu, iova: ar_snoc->fw.fw_start_addr, |
1689 | size: mapped_size); |
1690 | if (unmapped_size != mapped_size) |
1691 | ath10k_err(ar, fmt: "failed to unmap firmware: %zu\n" , |
1692 | unmapped_size); |
1693 | |
1694 | iommu_detach_device(domain: iommu, dev: ar_snoc->fw.dev); |
1695 | iommu_domain_free(domain: iommu); |
1696 | |
1697 | platform_device_unregister(to_platform_device(ar_snoc->fw.dev)); |
1698 | |
1699 | return 0; |
1700 | } |
1701 | |
1702 | static const struct of_device_id ath10k_snoc_dt_match[] = { |
1703 | { .compatible = "qcom,wcn3990-wifi" , |
1704 | .data = &drv_priv, |
1705 | }, |
1706 | { } |
1707 | }; |
1708 | MODULE_DEVICE_TABLE(of, ath10k_snoc_dt_match); |
1709 | |
1710 | static int ath10k_snoc_probe(struct platform_device *pdev) |
1711 | { |
1712 | const struct ath10k_snoc_drv_priv *drv_data; |
1713 | struct ath10k_snoc *ar_snoc; |
1714 | struct device *dev; |
1715 | struct ath10k *ar; |
1716 | u32 msa_size; |
1717 | int ret; |
1718 | u32 i; |
1719 | |
1720 | dev = &pdev->dev; |
1721 | drv_data = device_get_match_data(dev); |
1722 | if (!drv_data) { |
1723 | dev_err(dev, "failed to find matching device tree id\n" ); |
1724 | return -EINVAL; |
1725 | } |
1726 | |
1727 | ret = dma_set_mask_and_coherent(dev, mask: drv_data->dma_mask); |
1728 | if (ret) { |
1729 | dev_err(dev, "failed to set dma mask: %d\n" , ret); |
1730 | return ret; |
1731 | } |
1732 | |
1733 | ar = ath10k_core_create(priv_size: sizeof(*ar_snoc), dev, bus: ATH10K_BUS_SNOC, |
1734 | hw_rev: drv_data->hw_rev, hif_ops: &ath10k_snoc_hif_ops); |
1735 | if (!ar) { |
1736 | dev_err(dev, "failed to allocate core\n" ); |
1737 | return -ENOMEM; |
1738 | } |
1739 | |
1740 | ar_snoc = ath10k_snoc_priv(ar); |
1741 | ar_snoc->dev = pdev; |
1742 | platform_set_drvdata(pdev, data: ar); |
1743 | ar_snoc->ar = ar; |
1744 | ar_snoc->ce.bus_ops = &ath10k_snoc_bus_ops; |
1745 | ar->ce_priv = &ar_snoc->ce; |
1746 | msa_size = drv_data->msa_size; |
1747 | |
1748 | ath10k_snoc_quirks_init(ar); |
1749 | |
1750 | ret = ath10k_snoc_resource_init(ar); |
1751 | if (ret) { |
1752 | ath10k_warn(ar, fmt: "failed to initialize resource: %d\n" , ret); |
1753 | goto err_core_destroy; |
1754 | } |
1755 | |
1756 | ret = ath10k_snoc_setup_resource(ar); |
1757 | if (ret) { |
1758 | ath10k_warn(ar, fmt: "failed to setup resource: %d\n" , ret); |
1759 | goto err_core_destroy; |
1760 | } |
1761 | ret = ath10k_snoc_request_irq(ar); |
1762 | if (ret) { |
1763 | ath10k_warn(ar, fmt: "failed to request irqs: %d\n" , ret); |
1764 | goto err_release_resource; |
1765 | } |
1766 | |
1767 | ar_snoc->num_vregs = ARRAY_SIZE(ath10k_regulators); |
1768 | ar_snoc->vregs = devm_kcalloc(dev: &pdev->dev, n: ar_snoc->num_vregs, |
1769 | size: sizeof(*ar_snoc->vregs), GFP_KERNEL); |
1770 | if (!ar_snoc->vregs) { |
1771 | ret = -ENOMEM; |
1772 | goto err_free_irq; |
1773 | } |
1774 | for (i = 0; i < ar_snoc->num_vregs; i++) |
1775 | ar_snoc->vregs[i].supply = ath10k_regulators[i]; |
1776 | |
1777 | ret = devm_regulator_bulk_get(dev: &pdev->dev, num_consumers: ar_snoc->num_vregs, |
1778 | consumers: ar_snoc->vregs); |
1779 | if (ret < 0) |
1780 | goto err_free_irq; |
1781 | |
1782 | ar_snoc->num_clks = ARRAY_SIZE(ath10k_clocks); |
1783 | ar_snoc->clks = devm_kcalloc(dev: &pdev->dev, n: ar_snoc->num_clks, |
1784 | size: sizeof(*ar_snoc->clks), GFP_KERNEL); |
1785 | if (!ar_snoc->clks) { |
1786 | ret = -ENOMEM; |
1787 | goto err_free_irq; |
1788 | } |
1789 | |
1790 | for (i = 0; i < ar_snoc->num_clks; i++) |
1791 | ar_snoc->clks[i].id = ath10k_clocks[i]; |
1792 | |
1793 | ret = devm_clk_bulk_get_optional(dev: &pdev->dev, num_clks: ar_snoc->num_clks, |
1794 | clks: ar_snoc->clks); |
1795 | if (ret) |
1796 | goto err_free_irq; |
1797 | |
1798 | ret = ath10k_setup_msa_resources(ar, msa_size); |
1799 | if (ret) { |
1800 | ath10k_warn(ar, fmt: "failed to setup msa resources: %d\n" , ret); |
1801 | goto err_free_irq; |
1802 | } |
1803 | |
1804 | ret = ath10k_fw_init(ar); |
1805 | if (ret) { |
1806 | ath10k_err(ar, fmt: "failed to initialize firmware: %d\n" , ret); |
1807 | goto err_free_irq; |
1808 | } |
1809 | |
1810 | ret = ath10k_qmi_init(ar, msa_size); |
1811 | if (ret) { |
1812 | ath10k_warn(ar, fmt: "failed to register wlfw qmi client: %d\n" , ret); |
1813 | goto err_fw_deinit; |
1814 | } |
1815 | |
1816 | ret = ath10k_modem_init(ar); |
1817 | if (ret) |
1818 | goto err_qmi_deinit; |
1819 | |
1820 | ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc probe\n" ); |
1821 | |
1822 | return 0; |
1823 | |
1824 | err_qmi_deinit: |
1825 | ath10k_qmi_deinit(ar); |
1826 | |
1827 | err_fw_deinit: |
1828 | ath10k_fw_deinit(ar); |
1829 | |
1830 | err_free_irq: |
1831 | ath10k_snoc_free_irq(ar); |
1832 | |
1833 | err_release_resource: |
1834 | ath10k_snoc_release_resource(ar); |
1835 | |
1836 | err_core_destroy: |
1837 | ath10k_core_destroy(ar); |
1838 | |
1839 | return ret; |
1840 | } |
1841 | |
1842 | static int ath10k_snoc_free_resources(struct ath10k *ar) |
1843 | { |
1844 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
1845 | |
1846 | ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc free resources\n" ); |
1847 | |
1848 | set_bit(nr: ATH10K_SNOC_FLAG_UNREGISTERING, addr: &ar_snoc->flags); |
1849 | |
1850 | ath10k_core_unregister(ar); |
1851 | ath10k_fw_deinit(ar); |
1852 | ath10k_snoc_free_irq(ar); |
1853 | ath10k_snoc_release_resource(ar); |
1854 | ath10k_modem_deinit(ar); |
1855 | ath10k_qmi_deinit(ar); |
1856 | ath10k_core_destroy(ar); |
1857 | |
1858 | return 0; |
1859 | } |
1860 | |
1861 | static void ath10k_snoc_remove(struct platform_device *pdev) |
1862 | { |
1863 | struct ath10k *ar = platform_get_drvdata(pdev); |
1864 | struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); |
1865 | |
1866 | ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc remove\n" ); |
1867 | |
1868 | reinit_completion(x: &ar->driver_recovery); |
1869 | |
1870 | if (test_bit(ATH10K_SNOC_FLAG_RECOVERY, &ar_snoc->flags)) |
1871 | wait_for_completion_timeout(x: &ar->driver_recovery, timeout: 3 * HZ); |
1872 | |
1873 | ath10k_snoc_free_resources(ar); |
1874 | } |
1875 | |
1876 | static void ath10k_snoc_shutdown(struct platform_device *pdev) |
1877 | { |
1878 | struct ath10k *ar = platform_get_drvdata(pdev); |
1879 | |
1880 | ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc shutdown\n" ); |
1881 | ath10k_snoc_free_resources(ar); |
1882 | } |
1883 | |
1884 | static struct platform_driver ath10k_snoc_driver = { |
1885 | .probe = ath10k_snoc_probe, |
1886 | .remove_new = ath10k_snoc_remove, |
1887 | .shutdown = ath10k_snoc_shutdown, |
1888 | .driver = { |
1889 | .name = "ath10k_snoc" , |
1890 | .of_match_table = ath10k_snoc_dt_match, |
1891 | }, |
1892 | }; |
1893 | module_platform_driver(ath10k_snoc_driver); |
1894 | |
1895 | MODULE_AUTHOR("Qualcomm" ); |
1896 | MODULE_LICENSE("Dual BSD/GPL" ); |
1897 | MODULE_DESCRIPTION("Driver support for Atheros WCN3990 SNOC devices" ); |
1898 | |