xref: /wlan-dirver/qca-wifi-host-cmn/hif/src/snoc/if_snoc.c (revision 2c92bf0c9c4019a53f6a3346987ef86213a388ed)
1 /*
2  * Copyright (c) 2015-2021 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022 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_disable_isr(): disable isr
44  *
45  * This function disables isr and kills tasklets
46  *
47  * @hif_ctx: struct hif_softc
48  *
49  * Return: void
50  */
51 void hif_snoc_disable_isr(struct hif_softc *scn)
52 {
53 	hif_exec_kill(&scn->osc);
54 	hif_nointrs(scn);
55 	ce_tasklet_kill(scn);
56 	qdf_atomic_set(&scn->active_tasklet_cnt, 0);
57 	qdf_atomic_set(&scn->active_grp_tasklet_cnt, 0);
58 }
59 
60 /**
61  * hif_dump_registers(): dump bus debug registers
62  * @hif_ctx: struct hif_opaque_softc
63  *
64  * This function dumps hif bus debug registers
65  *
66  * Return: 0 for success or error code
67  */
68 int hif_snoc_dump_registers(struct hif_softc *hif_ctx)
69 {
70 	int status;
71 	struct hif_softc *scn = HIF_GET_SOFTC(hif_ctx);
72 
73 	status = hif_dump_ce_registers(scn);
74 	if (status)
75 		hif_err("Dump CE Registers Failed");
76 
77 	return 0;
78 }
79 
80 void hif_snoc_display_stats(struct hif_softc *hif_ctx)
81 {
82 	if (!hif_ctx) {
83 		hif_err("hif_ctx null");
84 		return;
85 	}
86 	hif_display_ce_stats(hif_ctx);
87 }
88 
89 void hif_snoc_clear_stats(struct hif_softc *hif_ctx)
90 {
91 	struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(hif_ctx);
92 
93 	if (!hif_state) {
94 		hif_err("hif_ctx null");
95 		return;
96 	}
97 	hif_clear_ce_stats(hif_state);
98 }
99 
100 /**
101  * hif_snoc_close(): hif_bus_close
102  *
103  * Return: n/a
104  */
105 void hif_snoc_close(struct hif_softc *scn)
106 {
107 	hif_ce_close(scn);
108 }
109 
110 /**
111  * hif_bus_open(): hif_bus_open
112  * @hif_ctx: hif context
113  * @bus_type: bus type
114  *
115  * Return: n/a
116  */
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  *
125  * fills in the virtual and physical base address as well as
126  * soc version info.
127  *
128  * return 0 or QDF_STATUS_E_FAILURE
129  */
130 static QDF_STATUS hif_snoc_get_soc_info(struct hif_softc *scn)
131 {
132 	int ret;
133 	struct pld_soc_info soc_info;
134 
135 	qdf_mem_zero(&soc_info, sizeof(soc_info));
136 
137 	ret = pld_get_soc_info(scn->qdf_dev->dev, &soc_info);
138 	if (ret < 0) {
139 		hif_err("pld_get_soc_info error = %d", ret);
140 		return QDF_STATUS_E_FAILURE;
141 	}
142 
143 	scn->mem = soc_info.v_addr;
144 	scn->mem_pa = soc_info.p_addr;
145 
146 	scn->target_info.soc_version = soc_info.soc_id;
147 	scn->target_info.target_version = soc_info.soc_id;
148 	scn->target_info.target_revision = 0;
149 	return QDF_STATUS_SUCCESS;
150 }
151 
152 /**
153  * hif_bus_configure() - configure the snoc bus
154  * @scn: pointer to the hif context.
155  *
156  * return: 0 for success. nonzero for failure.
157  */
158 int hif_snoc_bus_configure(struct hif_softc *scn)
159 {
160 	int ret;
161 	uint8_t wake_ce_id;
162 
163 	ret = hif_snoc_get_soc_info(scn);
164 	if (ret)
165 		return ret;
166 
167 	hif_ce_prepare_config(scn);
168 
169 	ret = hif_wlan_enable(scn);
170 	if (ret) {
171 		hif_err("hif_wlan_enable error = %d", ret);
172 		return ret;
173 	}
174 
175 	ret = hif_config_ce(scn);
176 	if (ret)
177 		goto wlan_disable;
178 
179 	ret = hif_get_wake_ce_id(scn, &wake_ce_id);
180 	if (ret)
181 		goto unconfig_ce;
182 
183 	scn->wake_irq = pld_get_irq(scn->qdf_dev->dev, wake_ce_id);
184 	scn->wake_irq_type = HIF_PM_CE_WAKE;
185 
186 	hif_info("expecting wake from ce %d, irq %d",
187 		 wake_ce_id, scn->wake_irq);
188 
189 	return 0;
190 
191 unconfig_ce:
192 	hif_unconfig_ce(scn);
193 
194 wlan_disable:
195 	hif_wlan_disable(scn);
196 
197 	return ret;
198 }
199 
200 /**
201  * hif_snoc_get_target_type(): Get the target type
202  *
203  * This function is used to query the target type.
204  *
205  * @ol_sc: hif_softc struct pointer
206  * @dev: device pointer
207  * @bdev: bus dev pointer
208  * @bid: bus id pointer
209  * @hif_type: HIF type such as HIF_TYPE_QCA6180
210  * @target_type: target type such as TARGET_TYPE_QCA6180
211  *
212  * Return: 0 for success
213  */
214 static inline int hif_snoc_get_target_type(struct hif_softc *ol_sc,
215 	struct device *dev, void *bdev, const struct hif_bus_id *bid,
216 	uint32_t *hif_type, uint32_t *target_type)
217 {
218 	/* TODO: need to use HW version. Hard code for now */
219 #ifdef QCA_WIFI_3_0_ADRASTEA
220 	*hif_type = HIF_TYPE_ADRASTEA;
221 	*target_type = TARGET_TYPE_ADRASTEA;
222 #else
223 	*hif_type = 0;
224 	*target_type = 0;
225 #endif
226 	return 0;
227 }
228 
229 #ifdef IPA_OFFLOAD
230 static int hif_set_dma_coherent_mask(qdf_device_t osdev)
231 {
232 	uint8_t addr_bits;
233 
234 	if (false == hif_get_ipa_present())
235 		return qdf_set_dma_coherent_mask(osdev->dev,
236 					DMA_COHERENT_MASK_DEFAULT);
237 
238 	if (hif_get_ipa_hw_type() < IPA_HW_v3_0)
239 		addr_bits = DMA_COHERENT_MASK_BELOW_IPA_VER_3;
240 	else
241 		addr_bits = DMA_COHERENT_MASK_DEFAULT;
242 
243 	return qdf_set_dma_coherent_mask(osdev->dev, addr_bits);
244 }
245 #else
246 static int hif_set_dma_coherent_mask(qdf_device_t osdev)
247 {
248 	return qdf_set_dma_coherent_mask(osdev->dev,
249 					DMA_COHERENT_MASK_DEFAULT);
250 }
251 #endif
252 
253 /**
254  * hif_enable_bus(): hif_enable_bus
255  * @dev: dev
256  * @bdev: bus dev
257  * @bid: bus id
258  * @type: bus type
259  *
260  * Return: QDF_STATUS
261  */
262 QDF_STATUS hif_snoc_enable_bus(struct hif_softc *ol_sc,
263 			  struct device *dev, void *bdev,
264 			  const struct hif_bus_id *bid,
265 			  enum hif_enable_type type)
266 {
267 	int ret;
268 	int hif_type;
269 	int target_type;
270 
271 	if (!ol_sc) {
272 		hif_err("hif_ctx is NULL");
273 		return QDF_STATUS_E_NOMEM;
274 	}
275 
276 	ret = hif_set_dma_coherent_mask(ol_sc->qdf_dev);
277 	if (ret) {
278 		hif_err("Failed to set dma mask error = %d", ret);
279 		return qdf_status_from_os_return(ret);
280 	}
281 
282 	ret = qdf_device_init_wakeup(ol_sc->qdf_dev, true);
283 	if (ret == -EEXIST)
284 		hif_warn("device_init_wakeup already done");
285 	else if (ret) {
286 		hif_err("device_init_wakeup: err= %d", ret);
287 		return qdf_status_from_os_return(ret);
288 	}
289 
290 	ret = hif_snoc_get_target_type(ol_sc, dev, bdev, bid,
291 			&hif_type, &target_type);
292 	if (ret < 0) {
293 		hif_err("Invalid device id/revision_id");
294 		return QDF_STATUS_E_FAILURE;
295 	}
296 
297 	ol_sc->target_info.target_type = target_type;
298 
299 	hif_register_tbl_attach(ol_sc, hif_type);
300 	hif_target_register_tbl_attach(ol_sc, target_type);
301 
302 	/* the bus should remain on during suspend for snoc */
303 	hif_vote_link_up(GET_HIF_OPAQUE_HDL(ol_sc));
304 
305 	hif_debug("X - hif_type = 0x%x, target_type = 0x%x",
306 		  hif_type, target_type);
307 
308 	return QDF_STATUS_SUCCESS;
309 }
310 
311 /**
312  * hif_disable_bus(): hif_disable_bus
313  *
314  * This function disables the bus
315  *
316  * @bdev: bus dev
317  *
318  * Return: none
319  */
320 void hif_snoc_disable_bus(struct hif_softc *scn)
321 {
322 	int ret;
323 
324 	hif_vote_link_down(GET_HIF_OPAQUE_HDL(scn));
325 
326 	ret = qdf_device_init_wakeup(scn->qdf_dev, false);
327 	if (ret)
328 		hif_err("device_init_wakeup: err %d", ret);
329 }
330 
331 /**
332  * hif_nointrs(): disable IRQ
333  *
334  * This function stops interrupt(s)
335  *
336  * @scn: struct hif_softc
337  *
338  * Return: none
339  */
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  * ce_irq_enable() - enable copy engine IRQ
350  * @scn: struct hif_softc
351  * @ce_id: ce_id
352  *
353  * Return: N/A
354  */
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  * ce_irq_disable() - disable copy engine IRQ
363  * @scn: struct hif_softc
364  * @ce_id: ce_id
365  *
366  * Return: N/A
367  */
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  * @hif_softc: hif context
376  *
377  * Firmware will send a wakeup request to the HTC_CTRL_RSVD_SVC when waking up
378  * the host driver. Ensure that the copy complete interrupt from this copy
379  * engine can wake up the apps processor.
380  *
381  * Return: 0 for success
382  */
383 static
384 QDF_STATUS hif_snoc_setup_wakeup_sources(struct hif_softc *scn, bool enable)
385 {
386 	int ret;
387 
388 	if (enable)
389 		ret = enable_irq_wake(scn->wake_irq);
390 	else
391 		ret = disable_irq_wake(scn->wake_irq);
392 
393 	if (ret) {
394 		hif_err("Fail to setup wake IRQ!");
395 		return QDF_STATUS_E_RESOURCES;
396 	}
397 
398 	return QDF_STATUS_SUCCESS;
399 }
400 
401 /**
402  * hif_snoc_bus_suspend() - prepare to suspend the bus
403  * @scn: hif context
404  *
405  * Setup wakeup interrupt configuration.
406  * Disable CE interrupts (wakeup interrupt will still wake apps)
407  * Drain tasklets. - make sure that we don't suspend while processing
408  * the wakeup message.
409  *
410  * Return: 0 on success.
411  */
412 int hif_snoc_bus_suspend(struct hif_softc *scn)
413 {
414 	if (hif_snoc_setup_wakeup_sources(scn, true) != QDF_STATUS_SUCCESS)
415 		return -EFAULT;
416 	return 0;
417 }
418 
419 /**
420  * hif_snoc_bus_resume() - snoc bus resume function
421  * @scn: hif context
422  *
423  * Clear wakeup interrupt configuration.
424  * Re-enable ce interrupts
425  *
426  * Return: 0 on success
427  */
428 int hif_snoc_bus_resume(struct hif_softc *scn)
429 {
430 	if (hif_snoc_setup_wakeup_sources(scn, false) != QDF_STATUS_SUCCESS)
431 		QDF_BUG(0);
432 
433 	return 0;
434 }
435 
436 /**
437  * hif_snoc_bus_suspend_noirq() - ensure there are no pending transactions
438  * @scn: hif context
439  *
440  * Ensure that if we received the wakeup message before the irq
441  * was disabled that the message is processed before suspending.
442  *
443  * Return: -EBUSY if we fail to flush the tasklets.
444  */
445 int hif_snoc_bus_suspend_noirq(struct hif_softc *scn)
446 {
447 	if (hif_drain_tasklets(scn) != 0)
448 		return -EBUSY;
449 	return 0;
450 }
451 
452 int hif_snoc_map_ce_to_irq(struct hif_softc *scn, int ce_id)
453 {
454 	return pld_get_irq(scn->qdf_dev->dev, ce_id);
455 }
456 
457 /**
458  * hif_is_target_register_access_allowed(): Check target register access allow
459  * @scn: HIF Context
460  *
461  * This function help to check whether target register access is allowed or not
462  *
463  * Return: true if target access is allowed else false
464  */
465 bool hif_is_target_register_access_allowed(struct hif_softc *scn)
466 {
467 	if (hif_is_recovery_in_progress(scn))
468 		return hif_is_target_ready(scn);
469 	else
470 		return true;
471 }
472 
473 /**
474  * hif_snoc_needs_bmi() - return true if the soc needs bmi through the driver
475  * @scn: hif context
476  *
477  * Return: true if soc needs driver bmi otherwise false
478  */
479 bool hif_snoc_needs_bmi(struct hif_softc *scn)
480 {
481 	return false;
482 }
483