1 /*
2 * Copyright (c) 2015-2021 The Linux Foundation. All rights reserved.
3 * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for
6 * any purpose with or without fee is hereby granted, provided that the
7 * above copyright notice and this permission notice appear in all
8 * copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 /**
21 * DOC: if_snoc.c
22 *
23 * c file for snoc specific implementations.
24 */
25
26 #include "hif.h"
27 #include "hif_main.h"
28 #include "hif_debug.h"
29 #include "hif_io32.h"
30 #include "ce_main.h"
31 #include "ce_tasklet.h"
32 #include "ce_api.h"
33 #include "ce_internal.h"
34 #include "snoc_api.h"
35 #include "pld_common.h"
36 #include "qdf_util.h"
37 #ifdef IPA_OFFLOAD
38 #include <uapi/linux/msm_ipa.h>
39 #endif
40 #include "target_type.h"
41
42 /**
43 * hif_snoc_disable_isr(): disable isr
44 * @scn: struct hif_softc
45 *
46 * This function disables isr and kills tasklets
47 *
48 * Return: void
49 */
hif_snoc_disable_isr(struct hif_softc * scn)50 void hif_snoc_disable_isr(struct hif_softc *scn)
51 {
52 hif_exec_kill(&scn->osc);
53 hif_nointrs(scn);
54 ce_tasklet_kill(scn);
55 qdf_atomic_set(&scn->active_tasklet_cnt, 0);
56 qdf_atomic_set(&scn->active_grp_tasklet_cnt, 0);
57 }
58
59 /**
60 * hif_snoc_dump_registers(): dump bus debug registers
61 * @hif_ctx: struct hif_opaque_softc
62 *
63 * This function dumps hif bus debug registers
64 *
65 * Return: 0 for success or error code
66 */
hif_snoc_dump_registers(struct hif_softc * hif_ctx)67 int hif_snoc_dump_registers(struct hif_softc *hif_ctx)
68 {
69 int status;
70 struct hif_softc *scn = HIF_GET_SOFTC(hif_ctx);
71
72 status = hif_dump_ce_registers(scn);
73 if (status)
74 hif_err("Dump CE Registers Failed");
75
76 return 0;
77 }
78
hif_snoc_display_stats(struct hif_softc * hif_ctx)79 void hif_snoc_display_stats(struct hif_softc *hif_ctx)
80 {
81 if (!hif_ctx) {
82 hif_err("hif_ctx null");
83 return;
84 }
85 hif_display_ce_stats(hif_ctx);
86 }
87
hif_snoc_clear_stats(struct hif_softc * hif_ctx)88 void hif_snoc_clear_stats(struct hif_softc *hif_ctx)
89 {
90 struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(hif_ctx);
91
92 if (!hif_state) {
93 hif_err("hif_ctx null");
94 return;
95 }
96 hif_clear_ce_stats(hif_state);
97 }
98
99 /**
100 * hif_snoc_close(): hif_bus_close
101 * @scn: pointer to the hif context.
102 *
103 * Return: n/a
104 */
hif_snoc_close(struct hif_softc * scn)105 void hif_snoc_close(struct hif_softc *scn)
106 {
107 hif_ce_close(scn);
108 }
109
110 /**
111 * hif_snoc_open(): hif_bus_open
112 * @hif_ctx: hif context
113 * @bus_type: bus type
114 *
115 * Return: QDF_STATUS
116 */
hif_snoc_open(struct hif_softc * hif_ctx,enum qdf_bus_type bus_type)117 QDF_STATUS hif_snoc_open(struct hif_softc *hif_ctx, enum qdf_bus_type bus_type)
118 {
119 return hif_ce_open(hif_ctx);
120 }
121
122 /**
123 * hif_snoc_get_soc_info() - populates scn with hw info
124 * @scn: pointer to the hif context.
125 *
126 * fills in the virtual and physical base address as well as
127 * soc version info.
128 *
129 * Return: QDF_STATUS_SUCCESS or QDF_STATUS_E_FAILURE
130 */
hif_snoc_get_soc_info(struct hif_softc * scn)131 static QDF_STATUS hif_snoc_get_soc_info(struct hif_softc *scn)
132 {
133 int ret;
134 struct pld_soc_info soc_info;
135
136 qdf_mem_zero(&soc_info, sizeof(soc_info));
137
138 ret = pld_get_soc_info(scn->qdf_dev->dev, &soc_info);
139 if (ret < 0) {
140 hif_err("pld_get_soc_info error = %d", ret);
141 return QDF_STATUS_E_FAILURE;
142 }
143
144 scn->mem = soc_info.v_addr;
145 scn->mem_pa = soc_info.p_addr;
146
147 scn->target_info.soc_version = soc_info.soc_id;
148 scn->target_info.target_version = soc_info.soc_id;
149 scn->target_info.target_revision = 0;
150 return QDF_STATUS_SUCCESS;
151 }
152
153 /**
154 * hif_snoc_bus_configure() - configure the snoc bus
155 * @scn: pointer to the hif context.
156 *
157 * return: 0 for success. nonzero for failure.
158 */
hif_snoc_bus_configure(struct hif_softc * scn)159 int hif_snoc_bus_configure(struct hif_softc *scn)
160 {
161 int ret;
162 uint8_t wake_ce_id;
163
164 ret = hif_snoc_get_soc_info(scn);
165 if (ret)
166 return ret;
167
168 hif_ce_prepare_config(scn);
169
170 ret = hif_wlan_enable(scn);
171 if (ret) {
172 hif_err("hif_wlan_enable error = %d", ret);
173 return ret;
174 }
175
176 ret = hif_config_ce(scn);
177 if (ret)
178 goto wlan_disable;
179
180 ret = hif_get_wake_ce_id(scn, &wake_ce_id);
181 if (ret)
182 goto unconfig_ce;
183
184 scn->wake_irq = pld_get_irq(scn->qdf_dev->dev, wake_ce_id);
185 scn->wake_irq_type = HIF_PM_CE_WAKE;
186
187 hif_info("expecting wake from ce %d, irq %d",
188 wake_ce_id, scn->wake_irq);
189
190 return 0;
191
192 unconfig_ce:
193 hif_unconfig_ce(scn);
194
195 wlan_disable:
196 hif_wlan_disable(scn);
197
198 return ret;
199 }
200
201 /**
202 * hif_snoc_get_target_type(): Get the target type
203 *
204 * This function is used to query the target type.
205 *
206 * @ol_sc: hif_softc struct pointer
207 * @dev: device pointer
208 * @bdev: bus dev pointer
209 * @bid: bus id pointer
210 * @hif_type: HIF type such as HIF_TYPE_QCA6180
211 * @target_type: target type such as TARGET_TYPE_QCA6180
212 *
213 * Return: 0 for success
214 */
hif_snoc_get_target_type(struct hif_softc * ol_sc,struct device * dev,void * bdev,const struct hif_bus_id * bid,uint32_t * hif_type,uint32_t * target_type)215 static inline int hif_snoc_get_target_type(struct hif_softc *ol_sc,
216 struct device *dev, void *bdev, const struct hif_bus_id *bid,
217 uint32_t *hif_type, uint32_t *target_type)
218 {
219 /* TODO: need to use HW version. Hard code for now */
220 #ifdef QCA_WIFI_3_0_ADRASTEA
221 *hif_type = HIF_TYPE_ADRASTEA;
222 *target_type = TARGET_TYPE_ADRASTEA;
223 #else
224 *hif_type = 0;
225 *target_type = 0;
226 #endif
227 return 0;
228 }
229
230 #ifdef IPA_OFFLOAD
hif_set_dma_coherent_mask(qdf_device_t osdev)231 static int hif_set_dma_coherent_mask(qdf_device_t osdev)
232 {
233 uint8_t addr_bits;
234
235 if (false == hif_get_ipa_present())
236 return qdf_set_dma_coherent_mask(osdev->dev,
237 DMA_COHERENT_MASK_DEFAULT);
238
239 if (hif_get_ipa_hw_type() < IPA_HW_v3_0)
240 addr_bits = DMA_COHERENT_MASK_BELOW_IPA_VER_3;
241 else
242 addr_bits = DMA_COHERENT_MASK_DEFAULT;
243
244 return qdf_set_dma_coherent_mask(osdev->dev, addr_bits);
245 }
246 #else
hif_set_dma_coherent_mask(qdf_device_t osdev)247 static int hif_set_dma_coherent_mask(qdf_device_t osdev)
248 {
249 return qdf_set_dma_coherent_mask(osdev->dev,
250 DMA_COHERENT_MASK_DEFAULT);
251 }
252 #endif
253
254 /**
255 * hif_snoc_enable_bus(): hif_enable_bus
256 * @ol_sc: HIF context
257 * @dev: dev
258 * @bdev: bus dev
259 * @bid: bus id
260 * @type: bus type
261 *
262 * Return: QDF_STATUS
263 */
hif_snoc_enable_bus(struct hif_softc * ol_sc,struct device * dev,void * bdev,const struct hif_bus_id * bid,enum hif_enable_type type)264 QDF_STATUS hif_snoc_enable_bus(struct hif_softc *ol_sc,
265 struct device *dev, void *bdev,
266 const struct hif_bus_id *bid,
267 enum hif_enable_type type)
268 {
269 int ret;
270 int hif_type;
271 int target_type;
272
273 if (!ol_sc) {
274 hif_err("hif_ctx is NULL");
275 return QDF_STATUS_E_NOMEM;
276 }
277
278 ret = hif_set_dma_coherent_mask(ol_sc->qdf_dev);
279 if (ret) {
280 hif_err("Failed to set dma mask error = %d", ret);
281 return qdf_status_from_os_return(ret);
282 }
283
284 ret = qdf_device_init_wakeup(ol_sc->qdf_dev, true);
285 if (ret == -EEXIST)
286 hif_warn("device_init_wakeup already done");
287 else if (ret) {
288 hif_err("device_init_wakeup: err= %d", ret);
289 return qdf_status_from_os_return(ret);
290 }
291
292 ret = hif_snoc_get_target_type(ol_sc, dev, bdev, bid,
293 &hif_type, &target_type);
294 if (ret < 0) {
295 hif_err("Invalid device id/revision_id");
296 return QDF_STATUS_E_FAILURE;
297 }
298
299 ol_sc->target_info.target_type = target_type;
300
301 hif_register_tbl_attach(ol_sc, hif_type);
302 hif_target_register_tbl_attach(ol_sc, target_type);
303
304 /* the bus should remain on during suspend for snoc */
305 hif_vote_link_up(GET_HIF_OPAQUE_HDL(ol_sc));
306
307 hif_debug("X - hif_type = 0x%x, target_type = 0x%x",
308 hif_type, target_type);
309
310 return QDF_STATUS_SUCCESS;
311 }
312
313 /**
314 * hif_snoc_disable_bus(): hif_disable_bus
315 * @scn: HIF context
316 *
317 * This function disables the bus
318 *
319 * Return: none
320 */
hif_snoc_disable_bus(struct hif_softc * scn)321 void hif_snoc_disable_bus(struct hif_softc *scn)
322 {
323 int ret;
324
325 hif_vote_link_down(GET_HIF_OPAQUE_HDL(scn));
326
327 ret = qdf_device_init_wakeup(scn->qdf_dev, false);
328 if (ret)
329 hif_err("device_init_wakeup: err %d", ret);
330 }
331
332 /**
333 * hif_snoc_nointrs(): disable IRQ
334 * @scn: struct hif_softc
335 *
336 * This function stops interrupt(s)
337 *
338 * Return: none
339 */
hif_snoc_nointrs(struct hif_softc * scn)340 void hif_snoc_nointrs(struct hif_softc *scn)
341 {
342 struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn);
343
344 scn->free_irq_done = true;
345 ce_unregister_irq(hif_state, CE_ALL_BITMAP);
346 }
347
348 /**
349 * hif_snoc_irq_enable() - enable copy engine IRQ
350 * @scn: struct hif_softc
351 * @ce_id: ce_id
352 *
353 * Return: N/A
354 */
hif_snoc_irq_enable(struct hif_softc * scn,int ce_id)355 void hif_snoc_irq_enable(struct hif_softc *scn,
356 int ce_id)
357 {
358 ce_enable_irq_in_individual_register(scn, ce_id);
359 }
360
361 /**
362 * hif_snoc_irq_disable() - disable copy engine IRQ
363 * @scn: struct hif_softc
364 * @ce_id: ce_id
365 *
366 * Return: N/A
367 */
hif_snoc_irq_disable(struct hif_softc * scn,int ce_id)368 void hif_snoc_irq_disable(struct hif_softc *scn, int ce_id)
369 {
370 ce_disable_irq_in_individual_register(scn, ce_id);
371 }
372
373 /**
374 * hif_snoc_setup_wakeup_sources() - enable/disable irq wake on correct irqs
375 * @scn: hif context
376 * @enable: true to enable
377 *
378 * Firmware will send a wakeup request to the HTC_CTRL_RSVD_SVC when waking up
379 * the host driver. Ensure that the copy complete interrupt from this copy
380 * engine can wake up the apps processor.
381 *
382 * Return: 0 for success
383 */
384 static
hif_snoc_setup_wakeup_sources(struct hif_softc * scn,bool enable)385 QDF_STATUS hif_snoc_setup_wakeup_sources(struct hif_softc *scn, bool enable)
386 {
387 int ret;
388
389 if (enable)
390 ret = enable_irq_wake(scn->wake_irq);
391 else
392 ret = disable_irq_wake(scn->wake_irq);
393
394 if (ret) {
395 hif_err("Fail to setup wake IRQ!");
396 return QDF_STATUS_E_RESOURCES;
397 }
398
399 return QDF_STATUS_SUCCESS;
400 }
401
402 /**
403 * hif_snoc_bus_suspend() - prepare to suspend the bus
404 * @scn: hif context
405 *
406 * Setup wakeup interrupt configuration.
407 * Disable CE interrupts (wakeup interrupt will still wake apps)
408 * Drain tasklets. - make sure that we don't suspend while processing
409 * the wakeup message.
410 *
411 * Return: 0 on success.
412 */
hif_snoc_bus_suspend(struct hif_softc * scn)413 int hif_snoc_bus_suspend(struct hif_softc *scn)
414 {
415 if (hif_snoc_setup_wakeup_sources(scn, true) != QDF_STATUS_SUCCESS)
416 return -EFAULT;
417 return 0;
418 }
419
420 /**
421 * hif_snoc_bus_resume() - snoc bus resume function
422 * @scn: hif context
423 *
424 * Clear wakeup interrupt configuration.
425 * Re-enable ce interrupts
426 *
427 * Return: 0 on success
428 */
hif_snoc_bus_resume(struct hif_softc * scn)429 int hif_snoc_bus_resume(struct hif_softc *scn)
430 {
431 if (hif_snoc_setup_wakeup_sources(scn, false) != QDF_STATUS_SUCCESS)
432 QDF_BUG(0);
433
434 return 0;
435 }
436
437 /**
438 * hif_snoc_bus_suspend_noirq() - ensure there are no pending transactions
439 * @scn: hif context
440 *
441 * Ensure that if we received the wakeup message before the irq
442 * was disabled that the message is processed before suspending.
443 *
444 * Return: -EBUSY if we fail to flush the tasklets.
445 */
hif_snoc_bus_suspend_noirq(struct hif_softc * scn)446 int hif_snoc_bus_suspend_noirq(struct hif_softc *scn)
447 {
448 if (hif_drain_tasklets(scn) != 0)
449 return -EBUSY;
450 return 0;
451 }
452
hif_snoc_map_ce_to_irq(struct hif_softc * scn,int ce_id)453 int hif_snoc_map_ce_to_irq(struct hif_softc *scn, int ce_id)
454 {
455 return pld_get_irq(scn->qdf_dev->dev, ce_id);
456 }
457
458 /**
459 * hif_is_target_register_access_allowed(): Check target register access allow
460 * @scn: HIF Context
461 *
462 * This function help to check whether target register access is allowed or not
463 *
464 * Return: true if target access is allowed else false
465 */
hif_is_target_register_access_allowed(struct hif_softc * scn)466 bool hif_is_target_register_access_allowed(struct hif_softc *scn)
467 {
468 if (hif_is_recovery_in_progress(scn))
469 return hif_is_target_ready(scn);
470 else
471 return true;
472 }
473
474 /**
475 * hif_snoc_needs_bmi() - return true if the soc needs bmi through the driver
476 * @scn: hif context
477 *
478 * Return: true if soc needs driver bmi otherwise false
479 */
hif_snoc_needs_bmi(struct hif_softc * scn)480 bool hif_snoc_needs_bmi(struct hif_softc *scn)
481 {
482 return false;
483 }
484
485 #ifdef FEATURE_ENABLE_CE_DP_IRQ_AFFINE
hif_snoc_ce_dp_irq_set_affinity_hint(struct hif_softc * scn)486 static void hif_snoc_ce_dp_irq_set_affinity_hint(struct hif_softc *scn)
487 {
488 int ret, irq;
489 unsigned int cpus;
490 struct CE_state *ce_state;
491 int ce_id;
492 qdf_cpu_mask ce_cpu_mask, updated_mask;
493 int perf_cpu_cluster = hif_get_perf_cluster_bitmap();
494 int package_id;
495
496 qdf_cpumask_clear(&ce_cpu_mask);
497
498 qdf_for_each_online_cpu(cpus) {
499 package_id = qdf_topology_physical_package_id(cpus);
500 if (package_id >= 0 && BIT(package_id) & perf_cpu_cluster)
501 qdf_cpumask_set_cpu(cpus, &ce_cpu_mask);
502 }
503
504 if (qdf_cpumask_empty(&ce_cpu_mask)) {
505 hif_err_rl("Empty cpu_mask, unable to set CE DP IRQ affinity");
506 return;
507 }
508
509 for (ce_id = 0; ce_id < scn->ce_count; ce_id++) {
510 ce_state = scn->ce_id_to_state[ce_id];
511 if (!ce_state || !ce_state->htt_rx_data)
512 continue;
513
514 qdf_cpumask_copy(&updated_mask, &ce_cpu_mask);
515 irq = pld_get_irq(scn->qdf_dev->dev, ce_id);
516 ret = hif_affinity_mgr_set_ce_irq_affinity(scn, irq, ce_id,
517 &updated_mask);
518 if (ret)
519 hif_err_rl("Set affinity %*pbl fails for CE IRQ %d",
520 qdf_cpumask_pr_args(&updated_mask), irq);
521 else
522 hif_debug_rl("Set affinity %*pbl for CE IRQ: %d",
523 qdf_cpumask_pr_args(&updated_mask), irq);
524 }
525 }
526
hif_snoc_configure_irq_affinity(struct hif_softc * scn)527 void hif_snoc_configure_irq_affinity(struct hif_softc *scn)
528 {
529 if (scn->hif_config.enable_ce_dp_irq_affine)
530 hif_snoc_ce_dp_irq_set_affinity_hint(scn);
531 }
532 #endif
533