xref: /wlan-driver/qca-wifi-host-cmn/hif/src/sdio/native_sdio/src/hif_scatter.c (revision 5113495b16420b49004c444715d2daae2066e7dc)
1 /*
2  * Copyright (c) 2013-2019 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 #include <linux/mmc/card.h>
21 #include <linux/mmc/host.h>
22 #include <linux/mmc/sdio_func.h>
23 #include <linux/mmc/sdio_ids.h>
24 #include <linux/mmc/sdio.h>
25 #include <linux/kthread.h>
26 #include "hif_internal.h"
27 #include <qdf_mem.h>
28 #include "dl_list.h"
29 #define ATH_MODULE_NAME hif
30 #include "a_debug.h"
31 #include <transfer/transfer.h>
32 
33 #ifdef HIF_LINUX_MMC_SCATTER_SUPPORT
34 
35 #define _CMD53_ARG_READ          0
36 #define _CMD53_ARG_WRITE         1
37 #define _CMD53_ARG_BLOCK_BASIS   1
38 #define _CMD53_ARG_FIXED_ADDRESS 0
39 #define _CMD53_ARG_INCR_ADDRESS  1
40 
41 #define SDIO_SET_CMD53_ARG(arg, rw, func, mode, opcode, address, bytes_blocks) \
42 		((arg) = (((rw) & 1) << 31) | \
43 		((func & 0x7) << 28) | \
44 		(((mode) & 1) << 27) | \
45 		(((opcode) & 1) << 26) | \
46 		(((address) & 0x1FFFF) << 9) | \
47 		((bytes_blocks) & 0x1FF))
48 
49 /**
50  * free_scatter_req() - free scattered request.
51  * @device: hif device context
52  * @pReq: scatter list node
53  *
54  * Return: none
55  */
free_scatter_req(struct hif_sdio_dev * device,struct _HIF_SCATTER_REQ * pReq)56 static void free_scatter_req(struct hif_sdio_dev *device,
57 		struct _HIF_SCATTER_REQ *pReq)
58 {
59 	qdf_spin_lock_irqsave(&device->lock);
60 
61 	dl_list_insert_tail(&device->scatter_req_head, &pReq->list_link);
62 
63 	qdf_spin_unlock_irqrestore(&device->lock);
64 }
65 
66 /**
67  * alloc_scatter_req() - allocate scattered request.
68  * @device: hif device context
69  *
70  *
71  * Return: pointer to allocated scatter list node
72  */
alloc_scatter_req(struct hif_sdio_dev * device)73 static struct _HIF_SCATTER_REQ *alloc_scatter_req(struct hif_sdio_dev *device)
74 {
75 	DL_LIST *item;
76 
77 	qdf_spin_lock_irqsave(&device->lock);
78 
79 	item = dl_list_remove_item_from_head(&device->scatter_req_head);
80 
81 	qdf_spin_unlock_irqrestore(&device->lock);
82 
83 	if (item)
84 		return A_CONTAINING_STRUCT(item,
85 			struct _HIF_SCATTER_REQ, list_link);
86 
87 	return NULL;
88 }
89 
90 /**
91  * do_hif_read_write_scatter() - rd/wr scattered operation.
92  * @device: hif device context
93  * @busrequest: rd/wr bus request
94  *
95  * called by async task to perform the operation synchronously
96  * using direct MMC APIs
97  * Return: int
98  */
do_hif_read_write_scatter(struct hif_sdio_dev * device,struct bus_request * busrequest)99 QDF_STATUS do_hif_read_write_scatter(struct hif_sdio_dev *device,
100 		struct bus_request *busrequest)
101 {
102 	int i;
103 	uint8_t rw;
104 	uint8_t opcode;
105 	struct mmc_request mmcreq;
106 	struct mmc_command cmd;
107 	struct mmc_data data;
108 	struct HIF_SCATTER_REQ_PRIV *req_priv;
109 	struct _HIF_SCATTER_REQ *req;
110 	QDF_STATUS status = QDF_STATUS_SUCCESS;
111 	struct scatterlist *sg;
112 
113 	HIF_ENTER();
114 
115 	req_priv = busrequest->scatter_req;
116 
117 	A_ASSERT(req_priv);
118 	if (!req_priv) {
119 		return QDF_STATUS_E_FAILURE;
120 	}
121 
122 	req = req_priv->hif_scatter_req;
123 
124 	memset(&mmcreq, 0, sizeof(struct mmc_request));
125 	memset(&cmd, 0, sizeof(struct mmc_command));
126 	memset(&data, 0, sizeof(struct mmc_data));
127 
128 	data.blksz = HIF_BLOCK_SIZE;
129 	data.blocks = req->total_length / HIF_BLOCK_SIZE;
130 
131 	AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER,
132 			("HIF-SCATTER: (%s) Address: 0x%X, (BlockLen: %d, BlockCount: %d), (tot:%d,sg:%d)\n",
133 			 (req->request & HIF_SDIO_WRITE) ? "WRITE" : "READ",
134 			 req->address, data.blksz, data.blocks,
135 			 req->total_length, req->valid_scatter_entries));
136 
137 	if (req->request & HIF_SDIO_WRITE) {
138 		rw = _CMD53_ARG_WRITE;
139 		data.flags = MMC_DATA_WRITE;
140 	} else {
141 		rw = _CMD53_ARG_READ;
142 		data.flags = MMC_DATA_READ;
143 	}
144 
145 	if (req->request & HIF_FIXED_ADDRESS)
146 		opcode = _CMD53_ARG_FIXED_ADDRESS;
147 	else
148 		opcode = _CMD53_ARG_INCR_ADDRESS;
149 
150 	/* fill SG entries */
151 	sg = req_priv->sgentries;
152 	sg_init_table(sg, req->valid_scatter_entries);
153 
154 	/* assemble SG list */
155 	for (i = 0; i < req->valid_scatter_entries; i++, sg++) {
156 		/* setup each sg entry */
157 		if ((unsigned long)req->scatter_list[i].buffer & 0x3) {
158 			/* note some scatter engines can handle unaligned
159 			 * buffers, print this as informational only
160 			 */
161 			AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER,
162 				("HIF: (%s) Scatter Buf is unaligned 0x%lx\n",
163 				 req->
164 				 request & HIF_SDIO_WRITE ? "WRITE" : "READ",
165 				 (unsigned long)req->scatter_list[i].
166 				 buffer));
167 		}
168 
169 		AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER,
170 				("  %d:  Addr:0x%lX, Len:%d\n", i,
171 				 (unsigned long)req->scatter_list[i].buffer,
172 				 req->scatter_list[i].length));
173 
174 		sg_set_buf(sg, req->scatter_list[i].buffer,
175 			   req->scatter_list[i].length);
176 	}
177 	/* set scatter-gather table for request */
178 	data.sg = req_priv->sgentries;
179 	data.sg_len = req->valid_scatter_entries;
180 	/* set command argument */
181 	SDIO_SET_CMD53_ARG(cmd.arg,
182 			   rw,
183 			   device->func->num,
184 			   _CMD53_ARG_BLOCK_BASIS,
185 			   opcode, req->address, data.blocks);
186 
187 	cmd.opcode = SD_IO_RW_EXTENDED;
188 	cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
189 
190 	mmcreq.cmd = &cmd;
191 	mmcreq.data = &data;
192 
193 	mmc_set_data_timeout(&data, device->func->card);
194 	/* synchronous call to process request */
195 	mmc_wait_for_req(device->func->card->host, &mmcreq);
196 
197 	if (cmd.error) {
198 		status = QDF_STATUS_E_FAILURE;
199 		AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
200 				("HIF-SCATTER: cmd error: %d\n", cmd.error));
201 	}
202 
203 	if (data.error) {
204 		status = QDF_STATUS_E_FAILURE;
205 		AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
206 				("HIF-SCATTER: data error: %d\n", data.error));
207 	}
208 
209 	if (QDF_IS_STATUS_ERROR(status)) {
210 		AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
211 			("HIF-SCATTER: FAILED!!! (%s) Address: 0x%X, Block mode (BlockLen: %d, BlockCount: %d)\n",
212 			 (req->request & HIF_SDIO_WRITE) ? "WRITE" : "READ",
213 			 req->address, data.blksz, data.blocks));
214 	}
215 
216 	/* set completion status, fail or success */
217 	req->completion_status = status;
218 
219 	if (req->request & HIF_ASYNCHRONOUS) {
220 		AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER,
221 				("HIF-SCATTER: async_task completion routine req: 0x%lX (%d)\n",
222 				 (unsigned long)busrequest, status));
223 		/* complete the request */
224 		A_ASSERT(req->completion_routine);
225 		if (req->completion_routine) {
226 			req->completion_routine(req);
227 		}
228 	} else {
229 		AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER,
230 			("HIF-SCATTER async_task upping busreq : 0x%lX (%d)\n",
231 			 (unsigned long)busrequest, status));
232 		/* signal wait */
233 		up(&busrequest->sem_req);
234 	}
235 	HIF_EXIT();
236 
237 	return status;
238 }
239 
240 /**
241  * hif_read_write_scatter() - callback to issue a read-write
242  * scatter request.
243  * @device: hif device context
244  * @req: rd/wr scatter request
245  *
246  * Return: QDF_STATUS
247  */
hif_read_write_scatter(struct hif_sdio_dev * device,struct _HIF_SCATTER_REQ * req)248 static QDF_STATUS hif_read_write_scatter(struct hif_sdio_dev *device,
249 				   struct _HIF_SCATTER_REQ *req)
250 {
251 	QDF_STATUS status = QDF_STATUS_E_INVAL;
252 	uint32_t request = req->request;
253 	struct HIF_SCATTER_REQ_PRIV *req_priv =
254 		(struct HIF_SCATTER_REQ_PRIV *) req->hif_private[0];
255 
256 	do {
257 
258 		A_ASSERT(req_priv);
259 		if (!req_priv) {
260 			break;
261 		}
262 
263 		AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER,
264 			("HIF-SCATTER: total len: %d Scatter Entries: %d\n",
265 				 req->total_length,
266 				 req->valid_scatter_entries));
267 
268 		if (!(request & HIF_EXTENDED_IO)) {
269 			AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
270 				("HIF-SCATTER: Invalid command type: 0x%08x\n",
271 					 request));
272 			break;
273 		}
274 
275 		if (!(request & (HIF_SYNCHRONOUS | HIF_ASYNCHRONOUS))) {
276 			AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
277 				("HIF-SCATTER: Invalid mode: 0x%08x\n",
278 					 request));
279 			break;
280 		}
281 
282 		if (!(request & HIF_BLOCK_BASIS)) {
283 			AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
284 				("HIF-SCATTER: Invalid data mode: 0x%08x\n",
285 					 request));
286 			break;
287 		}
288 
289 		if (req->total_length > MAX_SCATTER_REQ_TRANSFER_SIZE) {
290 			AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
291 				("HIF-SCATTER: Invalid length: %d\n",
292 					 req->total_length));
293 			break;
294 		}
295 
296 		if (req->total_length == 0) {
297 			A_ASSERT(false);
298 			break;
299 		}
300 
301 		/* add bus request to the async list for the async
302 		 * I/O thread to process
303 		 */
304 		add_to_async_list(device, req_priv->busrequest);
305 
306 		if (request & HIF_SYNCHRONOUS) {
307 			AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER,
308 				("HIF-SCATTER: queued sync req: 0x%lX\n",
309 					 (unsigned long)req_priv->busrequest));
310 			/* signal thread and wait */
311 			up(&device->sem_async);
312 			if (down_interruptible(&req_priv->busrequest->sem_req)
313 			    != 0) {
314 				AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
315 					("HIF-SCATTER: interrupted!\n"));
316 				/* interrupted, exit */
317 				status = QDF_STATUS_E_FAILURE;
318 				break;
319 			}
320 			status = req->completion_status;
321 		} else {
322 			AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER,
323 				("HIF-SCATTER: queued async req: 0x%lX\n",
324 					 (unsigned long)req_priv->busrequest));
325 			/* wake thread, it will process and then take
326 			 * care of the async callback
327 			 */
328 			up(&device->sem_async);
329 			status = QDF_STATUS_SUCCESS;
330 		}
331 
332 	} while (false);
333 
334 	if (QDF_IS_STATUS_ERROR(status) && (request & HIF_ASYNCHRONOUS)) {
335 		req->completion_status = status;
336 		req->completion_routine(req);
337 		status = QDF_STATUS_SUCCESS;
338 	}
339 
340 	return status;
341 }
342 
343 /**
344  * setup_hif_scatter_support() - setup of HIF scatter resources
345  * scatter request.
346  * @device: hif device context
347  * @info: scatter info
348  *
349  * Return: int
350  */
setup_hif_scatter_support(struct hif_sdio_dev * device,struct HIF_DEVICE_SCATTER_SUPPORT_INFO * info)351 QDF_STATUS setup_hif_scatter_support(struct hif_sdio_dev *device,
352 			   struct HIF_DEVICE_SCATTER_SUPPORT_INFO *info)
353 {
354 	QDF_STATUS status = QDF_STATUS_E_FAILURE;
355 	int i;
356 	struct HIF_SCATTER_REQ_PRIV *req_priv;
357 	struct bus_request *busrequest;
358 
359 	if (device->func->card->host->max_segs <
360 	    MAX_SCATTER_ENTRIES_PER_REQ) {
361 		AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
362 				("host only supports scatter of : %d entries, need: %d\n",
363 				 device->func->card->host->max_segs,
364 				 MAX_SCATTER_ENTRIES_PER_REQ));
365 		status = QDF_STATUS_E_NOSUPPORT;
366 		goto end;
367 	}
368 
369 	AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
370 			("max scatter req : %d entries: %d\n",
371 			 MAX_SCATTER_REQUESTS,
372 			 MAX_SCATTER_ENTRIES_PER_REQ));
373 
374 	for (i = 0; i < MAX_SCATTER_REQUESTS; i++) {
375 		/* allocate the private request blob */
376 		req_priv =
377 			(struct HIF_SCATTER_REQ_PRIV *)
378 			qdf_mem_malloc(sizeof(
379 					struct HIF_SCATTER_REQ_PRIV));
380 		if (!req_priv)
381 			goto end;
382 		/* save the device instance */
383 		req_priv->device = device;
384 		/* allocate the scatter request */
385 		req_priv->hif_scatter_req =
386 			(struct _HIF_SCATTER_REQ *)
387 			qdf_mem_malloc(sizeof(struct _HIF_SCATTER_REQ) +
388 				       (MAX_SCATTER_ENTRIES_PER_REQ -
389 			       1) * (sizeof(struct _HIF_SCATTER_ITEM)));
390 
391 		if (!req_priv->hif_scatter_req) {
392 			qdf_mem_free(req_priv);
393 			goto end;
394 		}
395 		/* back pointer to the private struct */
396 		req_priv->hif_scatter_req->hif_private[0] = req_priv;
397 		/* allocate a bus request for this scatter request */
398 		busrequest = hif_allocate_bus_request(device);
399 		if (!busrequest) {
400 			qdf_mem_free(req_priv->hif_scatter_req);
401 			qdf_mem_free(req_priv);
402 			goto end;
403 		}
404 		/* assign the scatter request to this bus request */
405 		busrequest->scatter_req = req_priv;
406 		/* point back to the request */
407 		req_priv->busrequest = busrequest;
408 		/* req_priv it to the scatter pool */
409 		free_scatter_req(device, req_priv->hif_scatter_req);
410 	}
411 
412 	if (i != MAX_SCATTER_REQUESTS) {
413 		status = QDF_STATUS_E_NOMEM;
414 		AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
415 				("failed to alloc scatter resources !\n"));
416 		goto end;
417 	}
418 
419 	/* set scatter function pointers */
420 	info->allocate_req_func = alloc_scatter_req;
421 	info->free_req_func = free_scatter_req;
422 	info->read_write_scatter_func = hif_read_write_scatter;
423 	info->max_scatter_entries = MAX_SCATTER_ENTRIES_PER_REQ;
424 	info->max_tx_size_per_scatter_req =
425 		MAX_SCATTER_REQ_TRANSFER_SIZE;
426 
427 	status = QDF_STATUS_SUCCESS;
428 
429 end:
430 	if (QDF_IS_STATUS_ERROR(status))
431 		cleanup_hif_scatter_resources(device);
432 
433 	return status;
434 }
435 
436 /**
437  * cleanup_hif_scatter_resources() - cleanup HIF scatter resources
438  * scatter request.
439  * @device: hif device context
440  *
441  *
442  * Return: none
443  */
cleanup_hif_scatter_resources(struct hif_sdio_dev * device)444 void cleanup_hif_scatter_resources(struct hif_sdio_dev *device)
445 {
446 	struct HIF_SCATTER_REQ_PRIV *req_priv;
447 	struct _HIF_SCATTER_REQ *req;
448 
449 	/* empty the free list */
450 
451 	while (true) {
452 		req = alloc_scatter_req(device);
453 
454 		if (!req)
455 			break;
456 
457 		req_priv = (struct HIF_SCATTER_REQ_PRIV *)req->hif_private[0];
458 		A_ASSERT(req_priv);
459 		if (!req_priv) {
460 			continue;
461 		}
462 
463 		if (req_priv->busrequest) {
464 			req_priv->busrequest->scatter_req = NULL;
465 			/* free bus request */
466 			hif_free_bus_request(device, req_priv->busrequest);
467 			req_priv->busrequest = NULL;
468 		}
469 
470 		if (req_priv->hif_scatter_req) {
471 			qdf_mem_free(req_priv->hif_scatter_req);
472 			req_priv->hif_scatter_req = NULL;
473 		}
474 
475 		qdf_mem_free(req_priv);
476 	}
477 }
478 
479 #endif /* HIF_LINUX_MMC_SCATTER_SUPPORT */
480