xref: /wlan-driver/qca-wifi-host-cmn/hif/src/sdio/hif_sdio_dev.c (revision 5113495b16420b49004c444715d2daae2066e7dc)
1 /*
2  * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved.
3  * Copyright (c) 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 #define ATH_MODULE_NAME hif
21 #include "a_debug.h"
22 
23 #include <qdf_types.h>
24 #include <qdf_status.h>
25 #include <qdf_timer.h>
26 #include <qdf_time.h>
27 #include <qdf_lock.h>
28 #include <qdf_mem.h>
29 #include <qdf_util.h>
30 #include <qdf_defer.h>
31 #include <qdf_atomic.h>
32 #include <qdf_nbuf.h>
33 #include <athdefs.h>
34 #include <qdf_net_types.h>
35 #include <a_types.h>
36 #include <athdefs.h>
37 #include <a_osapi.h>
38 #include <hif.h>
39 #include <htc_services.h>
40 #include "hif_sdio_internal.h"
41 #include "if_sdio.h"
42 #include "regtable_sdio.h"
43 
44 /**
45  * hif_dev_alloc_rx_buffer() - allocate rx buffer.
46  * @pdev: sdio device context
47  *
48  *
49  * Return: htc buffer pointer
50  */
hif_dev_alloc_rx_buffer(struct hif_sdio_device * pdev)51 HTC_PACKET *hif_dev_alloc_rx_buffer(struct hif_sdio_device *pdev)
52 {
53 	HTC_PACKET *packet;
54 	qdf_nbuf_t netbuf;
55 	uint32_t bufsize = 0, headsize = 0;
56 
57 	bufsize = HIF_SDIO_RX_BUFFER_SIZE + HIF_SDIO_RX_DATA_OFFSET;
58 	headsize = sizeof(HTC_PACKET);
59 	netbuf = qdf_nbuf_alloc(NULL, bufsize + headsize, 0, 4, false);
60 	if (!netbuf) {
61 		hif_err_rl("Allocate netbuf failed");
62 		return NULL;
63 	}
64 	packet = (HTC_PACKET *) qdf_nbuf_data(netbuf);
65 	qdf_nbuf_reserve(netbuf, headsize);
66 
67 	SET_HTC_PACKET_INFO_RX_REFILL(packet,
68 				      pdev,
69 				      qdf_nbuf_data(netbuf),
70 				      bufsize, ENDPOINT_0);
71 	SET_HTC_PACKET_NET_BUF_CONTEXT(packet, netbuf);
72 	return packet;
73 }
74 
75 /**
76  * hif_dev_create() - create hif device after probe.
77  * @hif_device: HIF context
78  * @callbacks: htc callbacks
79  * @target: HIF target
80  *
81  *
82  * Return: int
83  */
hif_dev_create(struct hif_sdio_dev * hif_device,struct hif_msg_callbacks * callbacks,void * target)84 struct hif_sdio_device *hif_dev_create(struct hif_sdio_dev *hif_device,
85 			struct hif_msg_callbacks *callbacks, void *target)
86 {
87 
88 	QDF_STATUS status;
89 	struct hif_sdio_device *pdev;
90 
91 	HIF_ENTER();
92 	pdev = qdf_mem_malloc(sizeof(struct hif_sdio_device));
93 	if (!pdev) {
94 		A_ASSERT(false);
95 		return NULL;
96 	}
97 
98 	qdf_spinlock_create(&pdev->Lock);
99 	qdf_spinlock_create(&pdev->TxLock);
100 	qdf_spinlock_create(&pdev->RxLock);
101 
102 	pdev->HIFDevice = hif_device;
103 	pdev->pTarget = target;
104 	status = hif_configure_device(NULL, hif_device,
105 				      HIF_DEVICE_SET_HTC_CONTEXT,
106 				      (void *)pdev, sizeof(pdev));
107 	if (status != QDF_STATUS_SUCCESS)
108 		hif_err("set context failed");
109 
110 	A_MEMCPY(&pdev->hif_callbacks, callbacks, sizeof(*callbacks));
111 
112 	HIF_EXIT();
113 	return pdev;
114 }
115 
116 /**
117  * hif_dev_destroy() - destroy hif device.
118  * @pdev: sdio device context
119  *
120  *
121  * Return: none
122  */
hif_dev_destroy(struct hif_sdio_device * pdev)123 void hif_dev_destroy(struct hif_sdio_device *pdev)
124 {
125 	QDF_STATUS status;
126 
127 	status = hif_configure_device(NULL, pdev->HIFDevice,
128 				      HIF_DEVICE_SET_HTC_CONTEXT,
129 				      (void *)NULL, 0);
130 	if (status != QDF_STATUS_SUCCESS)
131 		hif_err("set context failed");
132 
133 	qdf_mem_free(pdev);
134 }
135 
136 /**
137  * hif_dev_from_hif() - get sdio device from hif device.
138  * @hif_device: hif device context
139  *
140  *
141  * Return: hif sdio device context
142  */
hif_dev_from_hif(struct hif_sdio_dev * hif_device)143 struct hif_sdio_device *hif_dev_from_hif(struct hif_sdio_dev *hif_device)
144 {
145 	struct hif_sdio_device *pdev = NULL;
146 	QDF_STATUS status;
147 
148 	status = hif_configure_device(NULL, hif_device,
149 				      HIF_DEVICE_GET_HTC_CONTEXT,
150 				      (void **)&pdev,
151 				      sizeof(struct hif_sdio_device));
152 	if (status != QDF_STATUS_SUCCESS)
153 		hif_err("set context failed");
154 
155 	return pdev;
156 }
157 
158 /**
159  * hif_dev_disable_interrupts() - disable hif device interrupts.
160  * @pdev: sdio device context
161  *
162  *
163  * Return: int
164  */
hif_dev_disable_interrupts(struct hif_sdio_device * pdev)165 QDF_STATUS hif_dev_disable_interrupts(struct hif_sdio_device *pdev)
166 {
167 	QDF_STATUS status = QDF_STATUS_SUCCESS;
168 
169 	HIF_ENTER();
170 
171 	hif_dev_mask_interrupts(pdev);
172 
173 	/* To Do mask the host controller interrupts */
174 	hif_mask_interrupt(pdev->HIFDevice);
175 
176 	HIF_EXIT();
177 	return status;
178 }
179 
180 /**
181  * hif_dev_enable_interrupts() - enables hif device interrupts.
182  * @pdev: sdio device context
183  *
184  *
185  * Return: int
186  */
hif_dev_enable_interrupts(struct hif_sdio_device * pdev)187 QDF_STATUS hif_dev_enable_interrupts(struct hif_sdio_device *pdev)
188 {
189 	QDF_STATUS status;
190 
191 	HIF_ENTER();
192 
193 	/* for good measure, make sure interrupt are disabled
194 	 * before unmasking at the HIF layer.
195 	 * The rationale here is that between device insertion
196 	 * (where we clear the interrupts the first time)
197 	 * and when HTC is finally ready to handle interrupts,
198 	 * other software can perform target "soft" resets.
199 	 */
200 	status = hif_dev_disable_interrupts(pdev);
201 
202 	/* Unmask the host controller interrupts */
203 	hif_un_mask_interrupt(pdev->HIFDevice);
204 
205 	hif_dev_unmask_interrupts(pdev);
206 
207 	HIF_EXIT();
208 
209 	return status;
210 }
211 
212 /**
213  * hif_dev_setup() - set up sdio device.
214  * @pdev: sdio device context
215  *
216  *
217  * Return: int
218  */
hif_dev_setup(struct hif_sdio_device * pdev)219 QDF_STATUS hif_dev_setup(struct hif_sdio_device *pdev)
220 {
221 	QDF_STATUS status;
222 	struct htc_callbacks htc_cbs;
223 	struct hif_sdio_dev *hif_device = pdev->HIFDevice;
224 
225 	HIF_ENTER();
226 
227 	status = hif_dev_setup_device(pdev);
228 
229 	if (status != QDF_STATUS_SUCCESS) {
230 		hif_err("device specific setup failed");
231 		return QDF_STATUS_E_INVAL;
232 	}
233 
234 	pdev->BlockMask = pdev->BlockSize - 1;
235 	A_ASSERT((pdev->BlockSize & pdev->BlockMask) == 0);
236 
237 	/* assume we can process HIF interrupt events asynchronously */
238 	pdev->HifIRQProcessingMode = HIF_DEVICE_IRQ_ASYNC_SYNC;
239 
240 	/* see if the HIF layer overrides this assumption */
241 	hif_configure_device(NULL, hif_device,
242 			     HIF_DEVICE_GET_IRQ_PROC_MODE,
243 			     &pdev->HifIRQProcessingMode,
244 			     sizeof(pdev->HifIRQProcessingMode));
245 
246 	switch (pdev->HifIRQProcessingMode) {
247 	case HIF_DEVICE_IRQ_SYNC_ONLY:
248 		AR_DEBUG_PRINTF(ATH_DEBUG_WARN,
249 			("HIF Interrupt processing is SYNC ONLY\n"));
250 		/* see if HIF layer wants HTC to yield */
251 		hif_configure_device(NULL, hif_device,
252 				     HIF_DEVICE_GET_IRQ_YIELD_PARAMS,
253 				     &pdev->HifIRQYieldParams,
254 				     sizeof(pdev->HifIRQYieldParams));
255 
256 		if (pdev->HifIRQYieldParams.recv_packet_yield_count > 0) {
257 			AR_DEBUG_PRINTF(ATH_DEBUG_WARN,
258 				("HIF req of DSR yield per %d RECV packets\n",
259 				 pdev->HifIRQYieldParams.
260 				 recv_packet_yield_count));
261 			pdev->DSRCanYield = true;
262 		}
263 		break;
264 	case HIF_DEVICE_IRQ_ASYNC_SYNC:
265 		AR_DEBUG_PRINTF(ATH_DEBUG_TRC,
266 			("HIF Interrupt processing is ASYNC and SYNC\n"));
267 		break;
268 	default:
269 		A_ASSERT(false);
270 		break;
271 	}
272 
273 	pdev->HifMaskUmaskRecvEvent = NULL;
274 
275 	/* see if the HIF layer implements the mask/unmask recv
276 	 * events function
277 	 */
278 	hif_configure_device(NULL, hif_device,
279 			     HIF_DEVICE_GET_RECV_EVENT_MASK_UNMASK_FUNC,
280 			     &pdev->HifMaskUmaskRecvEvent,
281 			     sizeof(pdev->HifMaskUmaskRecvEvent));
282 
283 	status = hif_dev_disable_interrupts(pdev);
284 
285 	qdf_mem_zero(&htc_cbs, sizeof(struct htc_callbacks));
286 	/* the device layer handles these */
287 	htc_cbs.rw_compl_handler = hif_dev_rw_completion_handler;
288 	htc_cbs.dsr_handler = hif_dev_dsr_handler;
289 	htc_cbs.context = pdev;
290 	status = hif_attach_htc(pdev->HIFDevice, &htc_cbs);
291 
292 	HIF_EXIT();
293 	return status;
294 }
295