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