xref: /wlan-dirver/qca-wifi-host-cmn/hif/src/ath_procfs.c (revision 2888b71da71bce103343119fa1b31f4a0cee07c8)
1 /*
2  * Copyright (c) 2013-2014, 2016-2021 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2021-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 #if defined(CONFIG_ATH_PROCFS_DIAG_SUPPORT)
21 #include <linux/module.h>       /* Specifically, a module */
22 #include <linux/kernel.h>       /* We're doing kernel work */
23 #include <linux/version.h>      /* We're doing kernel work */
24 #include <linux/proc_fs.h>      /* Necessary because we use the proc fs */
25 #include <linux/uaccess.h>        /* for copy_from_user */
26 #include "hif.h"
27 #include "hif_main.h"
28 #if defined(HIF_USB)
29 #include "if_usb.h"
30 #endif
31 #if defined(HIF_SDIO)
32 #include "if_sdio.h"
33 #endif
34 #include "hif_debug.h"
35 #include "pld_common.h"
36 #include "target_type.h"
37 
38 #define PROCFS_NAME             "athdiagpfs"
39 #ifdef MULTI_IF_NAME
40 #define PROCFS_DIR              "cld" MULTI_IF_NAME
41 #else
42 #define PROCFS_DIR              "cld"
43 #endif
44 
45 /**
46  * Get op_type, mem_type and offset fields from pos of procfs
47  * It will reuse pos, which is long long type
48  *
49  * op_type:     4 bits
50  * memtype:     8 bits
51  * reserve1:    20 bits
52  * offset:      32 bits
53  */
54 #define OP_TYPE_LEGACY                  0
55 #define OP_TYPE_EXT_QMI                 1
56 #define OP_TYPE_EXT_DIRECT              2
57 
58 #define ATH_DIAG_EXT_OP_TYPE_BITS        4
59 #define ATH_DIAG_EXT_OP_TYPE_INDEX       60
60 #define ATH_DIAG_EXT_MEM_TYPE_BITS       8
61 #define ATH_DIAG_EXT_MEM_TYPE_INDEX      52
62 #define ATH_DIAG_EXT_OFFSET_BITS         32
63 #define ATH_DIAG_EXT_OFFSET_INDEX        0
64 
65 /**
66  * This structure hold information about the /proc file
67  *
68  */
69 static struct proc_dir_entry *proc_file, *proc_dir;
70 
71 static void *get_hif_hdl_from_file(struct file *file)
72 {
73 	struct hif_opaque_softc *scn;
74 
75 	scn = (struct hif_opaque_softc *)PDE_DATA(file_inode(file));
76 	return (void *)scn;
77 }
78 
79 static ssize_t ath_procfs_diag_read_legacy(struct file *file,
80 					   char __user *buf,
81 					   size_t count, loff_t *pos)
82 {
83 	hif_handle_t hif_hdl;
84 	int rv;
85 	uint8_t *read_buffer = NULL;
86 	struct hif_softc *scn;
87 	uint32_t offset = 0, memtype = 0;
88 	struct hif_target_info *tgt_info;
89 
90 	hif_hdl = get_hif_hdl_from_file(file);
91 	scn = HIF_GET_SOFTC(hif_hdl);
92 
93 	read_buffer = qdf_mem_malloc(count);
94 	if (!read_buffer)
95 		return -ENOMEM;
96 
97 	hif_debug("rd buff 0x%pK cnt %zu offset 0x%x buf 0x%pK",
98 		 read_buffer, count, (int)*pos, buf);
99 
100 	tgt_info = hif_get_target_info_handle(GET_HIF_OPAQUE_HDL(hif_hdl));
101 	if ((scn->bus_type == QDF_BUS_TYPE_SNOC) ||
102 	    (scn->bus_type ==  QDF_BUS_TYPE_PCI &&
103 	    ((tgt_info->target_type == TARGET_TYPE_QCA6290) ||
104 	     (tgt_info->target_type == TARGET_TYPE_QCA6390) ||
105 	     (tgt_info->target_type == TARGET_TYPE_QCA6490) ||
106 	     (tgt_info->target_type == TARGET_TYPE_QCA8074) ||
107 	     (tgt_info->target_type == TARGET_TYPE_QCA8074V2) ||
108 	     (tgt_info->target_type == TARGET_TYPE_QCA9574) ||
109 	     (tgt_info->target_type == TARGET_TYPE_QCN9000) ||
110 	     (tgt_info->target_type == TARGET_TYPE_QCN9224) ||
111 	     (tgt_info->target_type == TARGET_TYPE_QCN6122) ||
112 	     (tgt_info->target_type == TARGET_TYPE_QCA5018) ||
113 	     (tgt_info->target_type == TARGET_TYPE_QCA5332) ||
114 	     (tgt_info->target_type == TARGET_TYPE_QCA6018) ||
115 	     (tgt_info->target_type == TARGET_TYPE_QCN7605) ||
116 	     (tgt_info->target_type == TARGET_TYPE_KIWI) ||
117 	     (tgt_info->target_type == TARGET_TYPE_MANGO))) ||
118 	    (scn->bus_type ==  QDF_BUS_TYPE_IPCI &&
119 	     (tgt_info->target_type == TARGET_TYPE_QCA6750)) ||
120 	    ((scn->bus_type ==  QDF_BUS_TYPE_USB) &&
121 	     (tgt_info->target_type == TARGET_TYPE_QCN7605))) {
122 		memtype = ((uint32_t)(*pos) & 0xff000000) >> 24;
123 		offset = (uint32_t)(*pos) & 0xffffff;
124 		hif_debug("offset 0x%x memtype 0x%x, datalen %zu",
125 			 offset, memtype, count);
126 		rv = pld_athdiag_read(scn->qdf_dev->dev,
127 				      offset, memtype, count,
128 				      (uint8_t *)read_buffer);
129 		goto out;
130 	}
131 
132 	if ((count == 4) && ((((uint32_t) (*pos)) & 3) == 0)) {
133 		/* reading a word? */
134 		rv = hif_diag_read_access(hif_hdl, (uint32_t)(*pos),
135 					  (uint32_t *)read_buffer);
136 	} else {
137 		rv = hif_diag_read_mem(hif_hdl, (uint32_t)(*pos),
138 				       (uint8_t *)read_buffer, count);
139 	}
140 
141 out:
142 	if (rv) {
143 		qdf_mem_free(read_buffer);
144 		return -EIO;
145 	}
146 
147 	if (copy_to_user(buf, read_buffer, count)) {
148 		qdf_mem_free(read_buffer);
149 		hif_err("copy_to_user error in /proc/%s", PROCFS_NAME);
150 		return -EFAULT;
151 	}
152 	qdf_mem_free(read_buffer);
153 	return count;
154 }
155 
156 static ssize_t ath_procfs_diag_write_legacy(struct file *file,
157 					    const char __user *buf,
158 					    size_t count, loff_t *pos)
159 {
160 	hif_handle_t hif_hdl;
161 	int rv;
162 	uint8_t *write_buffer = NULL;
163 	struct hif_softc *scn;
164 	uint32_t offset = 0, memtype = 0;
165 	struct hif_target_info *tgt_info;
166 
167 	hif_hdl = get_hif_hdl_from_file(file);
168 	scn = HIF_GET_SOFTC(hif_hdl);
169 
170 	write_buffer = qdf_mem_malloc(count);
171 	if (!write_buffer)
172 		return -ENOMEM;
173 
174 	if (copy_from_user(write_buffer, buf, count)) {
175 		qdf_mem_free(write_buffer);
176 		hif_err("copy_to_user error in /proc/%s", PROCFS_NAME);
177 		return -EFAULT;
178 	}
179 
180 	hif_debug("wr buff 0x%pK buf 0x%pK cnt %zu offset 0x%x value 0x%x",
181 		 write_buffer, buf, count,
182 		 (int)*pos, *((uint32_t *) write_buffer));
183 
184 	tgt_info = hif_get_target_info_handle(GET_HIF_OPAQUE_HDL(hif_hdl));
185 	if ((scn->bus_type == QDF_BUS_TYPE_SNOC) ||
186 	    ((scn->bus_type ==  QDF_BUS_TYPE_PCI) &&
187 	     ((tgt_info->target_type == TARGET_TYPE_QCA6290) ||
188 	      (tgt_info->target_type == TARGET_TYPE_QCA6390) ||
189 	      (tgt_info->target_type == TARGET_TYPE_QCA6490) ||
190 	      (tgt_info->target_type == TARGET_TYPE_QCA8074) ||
191 	      (tgt_info->target_type == TARGET_TYPE_QCA8074V2) ||
192 	      (tgt_info->target_type == TARGET_TYPE_QCA9574) ||
193 	      (tgt_info->target_type == TARGET_TYPE_QCN9000) ||
194 	      (tgt_info->target_type == TARGET_TYPE_QCN9224) ||
195 	      (tgt_info->target_type == TARGET_TYPE_QCN6122) ||
196 	      (tgt_info->target_type == TARGET_TYPE_QCA5018) ||
197 	      (tgt_info->target_type == TARGET_TYPE_QCA5332) ||
198 	      (tgt_info->target_type == TARGET_TYPE_QCA6018) ||
199 	      (tgt_info->target_type == TARGET_TYPE_QCN7605) ||
200 	      (tgt_info->target_type == TARGET_TYPE_KIWI) ||
201 	      (tgt_info->target_type == TARGET_TYPE_MANGO))) ||
202 	    (scn->bus_type ==  QDF_BUS_TYPE_IPCI &&
203 	     (tgt_info->target_type == TARGET_TYPE_QCA6750)) ||
204 	    ((scn->bus_type ==  QDF_BUS_TYPE_USB) &&
205 	     (tgt_info->target_type == TARGET_TYPE_QCN7605))) {
206 		memtype = ((uint32_t)(*pos) & 0xff000000) >> 24;
207 		offset = (uint32_t)(*pos) & 0xffffff;
208 		hif_debug("offset 0x%x memtype 0x%x, datalen %zu",
209 			 offset, memtype, count);
210 		rv = pld_athdiag_write(scn->qdf_dev->dev,
211 				      offset, memtype, count,
212 				      (uint8_t *)write_buffer);
213 		goto out;
214 	}
215 
216 	if ((count == 4) && ((((uint32_t) (*pos)) & 3) == 0)) {
217 		/* reading a word? */
218 		uint32_t value = *((uint32_t *)write_buffer);
219 
220 		rv = hif_diag_write_access(hif_hdl, (uint32_t)(*pos), value);
221 	} else {
222 		rv = hif_diag_write_mem(hif_hdl, (uint32_t)(*pos),
223 					(uint8_t *)write_buffer, count);
224 	}
225 
226 out:
227 
228 	qdf_mem_free(write_buffer);
229 	if (rv == 0)
230 		return count;
231 	else
232 		return -EIO;
233 }
234 
235 #ifdef ATH_DIAG_EXT_DIRECT
236 /* Used to dump register or SRAM from target directly */
237 static int ath_procfs_direct_read(struct hif_softc *scn, uint32_t offset,
238 				  uint8_t *buf, size_t count)
239 {
240 	size_t remaining = count;
241 	uint32_t *p_val = (uint32_t *)buf;
242 	uint32_t val;
243 	uint8_t *buf_d, *buf_s;
244 
245 	if (!scn->bus_ops.hif_reg_read32)
246 		return -EIO;
247 
248 	while (remaining >= 4) {
249 		*p_val++ = scn->bus_ops.hif_reg_read32(scn,
250 						       offset);
251 		offset += 4;
252 		remaining -= 4;
253 	}
254 
255 	if (remaining) {
256 		val = scn->bus_ops.hif_reg_read32(scn,
257 						  offset);
258 		buf_d = (uint8_t *)p_val;
259 		buf_s = (uint8_t *)&val;
260 		while (remaining) {
261 			*buf_d++ = *buf_s++;
262 			remaining--;
263 		}
264 	}
265 
266 	return 0;
267 }
268 
269 /* Used to write register or SRAM to target directly */
270 static int ath_procfs_direct_write(struct hif_softc *scn, uint32_t offset,
271 				   uint8_t *buf, size_t count)
272 {
273 	size_t remaining = count;
274 	uint32_t *p_val = (uint32_t *)buf;
275 	uint32_t val;
276 	uint8_t *buf_d, *buf_s;
277 
278 	if (!scn->bus_ops.hif_reg_write32 || !scn->bus_ops.hif_reg_read32)
279 		return -EIO;
280 
281 	while (remaining >= 4) {
282 		scn->bus_ops.hif_reg_write32(scn,
283 					     offset,
284 					     *p_val++);
285 		offset += 4;
286 		remaining -= 4;
287 	}
288 
289 	if (remaining) {
290 		val = scn->bus_ops.hif_reg_read32(scn,
291 						  offset);
292 		buf_s = (uint8_t *)p_val;
293 		buf_d = (uint8_t *)&val;
294 		while (remaining) {
295 			*buf_d++ = *buf_s++;
296 			remaining--;
297 		}
298 		scn->bus_ops.hif_reg_write32(scn,
299 					     offset,
300 					     val);
301 	}
302 
303 	return 0;
304 }
305 #else
306 static int ath_procfs_direct_read(struct hif_softc *scn, uint32_t offset,
307 				  uint8_t *buf, size_t count)
308 {
309 	return -EIO;
310 }
311 
312 /* Used to write register or SRAM to target directly */
313 static int ath_procfs_direct_write(struct hif_softc *scn, uint32_t offset,
314 				   uint8_t *buf, size_t count)
315 {
316 	return -EIO;
317 }
318 
319 #endif
320 
321 static ssize_t ath_procfs_diag_read_ext(struct file *file, char __user *buf,
322 					size_t count,
323 					uint32_t op_type,
324 					uint32_t memtype,
325 					uint32_t offset)
326 {
327 	hif_handle_t hif_hdl = get_hif_hdl_from_file(file);
328 	int rv = -EINVAL;
329 	uint8_t *read_buffer;
330 	struct hif_softc *scn;
331 	struct hif_target_info *tgt_info;
332 
333 	if (!hif_hdl)
334 		return -EINVAL;
335 
336 	read_buffer = qdf_mem_malloc(count);
337 	if (!read_buffer)
338 		return -ENOMEM;
339 
340 	scn = HIF_GET_SOFTC(hif_hdl);
341 	tgt_info = hif_get_target_info_handle(GET_HIF_OPAQUE_HDL(hif_hdl));
342 	switch (scn->bus_type) {
343 	case QDF_BUS_TYPE_PCI:
344 		switch (tgt_info->target_type) {
345 		case TARGET_TYPE_QCA6390:
346 		case TARGET_TYPE_QCA6490:
347 		case TARGET_TYPE_KIWI:
348 		case TARGET_TYPE_MANGO:
349 			if (op_type == OP_TYPE_EXT_DIRECT)
350 				rv = ath_procfs_direct_read(scn,
351 							    offset,
352 							    read_buffer,
353 							    count);
354 			else
355 				rv = pld_athdiag_read(scn->qdf_dev->dev,
356 						      offset,
357 						      memtype,
358 						      count,
359 						      read_buffer);
360 			break;
361 		default:
362 			hif_err("Unrecognized target type %d",
363 				tgt_info->target_type);
364 		}
365 		break;
366 	default:
367 		hif_err("Unrecognized bus type %d", scn->bus_type);
368 		break;
369 	}
370 
371 	if (rv) {
372 		hif_err("fail to read from target %d", rv);
373 	} else {
374 		rv = count;
375 		if (copy_to_user(buf, read_buffer, count)) {
376 			hif_err("copy_to_user error in /proc/%s",
377 				PROCFS_NAME);
378 			rv = -EFAULT;
379 		}
380 	}
381 
382 	qdf_mem_free(read_buffer);
383 
384 	return rv;
385 }
386 
387 static ssize_t ath_procfs_diag_write_ext(struct file *file,
388 					 const char __user *buf,
389 					 size_t count,
390 					 uint32_t op_type,
391 					 uint32_t memtype,
392 					 uint32_t offset)
393 {
394 	hif_handle_t hif_hdl = get_hif_hdl_from_file(file);
395 	int rv = -EINVAL;
396 	uint8_t *write_buffer;
397 	struct hif_softc *scn;
398 	struct hif_target_info *tgt_info;
399 
400 	if (!hif_hdl)
401 		return -EINVAL;
402 
403 	scn = HIF_GET_SOFTC(hif_hdl);
404 
405 	write_buffer = qdf_mem_malloc(count);
406 	if (!write_buffer)
407 		return -ENOMEM;
408 
409 	if (copy_from_user(write_buffer, buf, count)) {
410 		qdf_mem_free(write_buffer);
411 		hif_err("copy_to_user error in /proc/%s",
412 			PROCFS_NAME);
413 		return -EFAULT;
414 	}
415 
416 	tgt_info = hif_get_target_info_handle(GET_HIF_OPAQUE_HDL(hif_hdl));
417 
418 	switch (scn->bus_type) {
419 	case QDF_BUS_TYPE_PCI:
420 		switch (tgt_info->target_type) {
421 		case TARGET_TYPE_QCA6390:
422 		case TARGET_TYPE_QCA6490:
423 		case TARGET_TYPE_KIWI:
424 		case TARGET_TYPE_MANGO:
425 			if (op_type == OP_TYPE_EXT_DIRECT)
426 				rv = ath_procfs_direct_write(scn,
427 							     offset,
428 							     write_buffer,
429 							     count);
430 			else
431 				rv = pld_athdiag_write(scn->qdf_dev->dev,
432 						       offset,
433 						       memtype,
434 						       count,
435 						       write_buffer);
436 			break;
437 		default:
438 			hif_err("Unrecognized target type %d",
439 				tgt_info->target_type);
440 		}
441 		break;
442 	default:
443 		hif_err("Unrecognized bus type %d", scn->bus_type);
444 		break;
445 	}
446 
447 	qdf_mem_free(write_buffer);
448 
449 	return (rv == 0) ? count : -EIO;
450 }
451 
452 static void get_fields_from_pos(loff_t pos,
453 				uint32_t *op_type,
454 				uint32_t *memtype,
455 				uint32_t *offset)
456 {
457 	*op_type = QDF_GET_BITS64(pos, ATH_DIAG_EXT_OP_TYPE_INDEX,
458 				  ATH_DIAG_EXT_OP_TYPE_BITS);
459 	*memtype = QDF_GET_BITS64(pos, ATH_DIAG_EXT_MEM_TYPE_INDEX,
460 				  ATH_DIAG_EXT_MEM_TYPE_BITS);
461 	*offset = QDF_GET_BITS64(pos, ATH_DIAG_EXT_OFFSET_INDEX,
462 				 ATH_DIAG_EXT_OFFSET_BITS);
463 }
464 
465 static ssize_t ath_procfs_diag_read(struct file *file, char __user *buf,
466 				    size_t count, loff_t *pos)
467 {
468 	hif_handle_t hif_hdl = get_hif_hdl_from_file(file);
469 	int rv = -EINVAL;
470 	struct hif_softc *scn;
471 	uint32_t offset, memtype;
472 	uint32_t op_type;
473 
474 	if (!hif_hdl)
475 		return -EINVAL;
476 
477 	get_fields_from_pos(*pos, &op_type, &memtype, &offset);
478 
479 	scn = HIF_GET_SOFTC(hif_hdl);
480 	if (scn->bus_ops.hif_addr_in_boundary(scn, offset))
481 		return -EINVAL;
482 
483 	if (offset & 0x3)
484 		return -EINVAL;
485 
486 	hif_info("rd cnt %zu offset 0x%x op_type %d type %d pos %llx",
487 		 count, offset, op_type, memtype, *pos);
488 
489 	switch (op_type) {
490 	case OP_TYPE_LEGACY:
491 		rv = ath_procfs_diag_read_legacy(file, buf, count, pos);
492 		break;
493 	case OP_TYPE_EXT_QMI:
494 	case OP_TYPE_EXT_DIRECT:
495 		rv = ath_procfs_diag_read_ext(file, buf, count, op_type,
496 					      memtype, offset);
497 		break;
498 	default:
499 		hif_err("Unrecognized op type %d", op_type);
500 		break;
501 	}
502 
503 	return rv;
504 }
505 
506 static ssize_t ath_procfs_diag_write(struct file *file,
507 				     const char __user *buf,
508 				     size_t count, loff_t *pos)
509 {
510 	hif_handle_t hif_hdl = get_hif_hdl_from_file(file);
511 	int rv = -EINVAL;
512 	struct hif_softc *scn;
513 	uint32_t offset, memtype;
514 	uint32_t op_type;
515 
516 	if (!hif_hdl)
517 		return -EINVAL;
518 
519 	get_fields_from_pos(*pos, &op_type, &memtype, &offset);
520 
521 	scn = HIF_GET_SOFTC(hif_hdl);
522 	if (scn->bus_ops.hif_addr_in_boundary(scn, offset))
523 		return -EINVAL;
524 
525 	if (offset & 0x3)
526 		return -EINVAL;
527 
528 	hif_info("wr cnt %zu offset 0x%x op_type %d mem_type %d",
529 		 count, offset, op_type, memtype);
530 
531 	switch (op_type) {
532 	case OP_TYPE_LEGACY:
533 		rv = ath_procfs_diag_write_legacy(file, buf, count, pos);
534 		break;
535 	case OP_TYPE_EXT_QMI:
536 	case OP_TYPE_EXT_DIRECT:
537 		rv = ath_procfs_diag_write_ext(file, buf, count, op_type,
538 					       memtype, offset);
539 		break;
540 	default:
541 		hif_err("Unrecognized op type %d", op_type);
542 		break;
543 	}
544 
545 	return rv;
546 }
547 
548 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0))
549 static const struct proc_ops athdiag_fops = {
550 	.proc_read = ath_procfs_diag_read,
551 	.proc_write = ath_procfs_diag_write,
552 	.proc_lseek = default_llseek,
553 };
554 #else
555 static const struct file_operations athdiag_fops = {
556 	.read = ath_procfs_diag_read,
557 	.write = ath_procfs_diag_write,
558 };
559 #endif
560 
561 /*
562  * This function is called when the module is loaded
563  *
564  */
565 int athdiag_procfs_init(void *scn)
566 {
567 	proc_dir = proc_mkdir(PROCFS_DIR, NULL);
568 	if (!proc_dir) {
569 		remove_proc_entry(PROCFS_DIR, NULL);
570 		hif_err("Could not initialize /proc/%s", PROCFS_DIR);
571 		return -ENOMEM;
572 	}
573 
574 	proc_file = proc_create_data(PROCFS_NAME, 0600, proc_dir,
575 				     &athdiag_fops, (void *)scn);
576 	if (!proc_file) {
577 		remove_proc_entry(PROCFS_NAME, proc_dir);
578 		hif_err("Could not initialize /proc/%s", PROCFS_NAME);
579 		return -ENOMEM;
580 	}
581 
582 	hif_debug("/proc/%s/%s created", PROCFS_DIR, PROCFS_NAME);
583 	return 0;
584 }
585 
586 /*
587  * This function is called when the module is unloaded
588  *
589  */
590 void athdiag_procfs_remove(void)
591 {
592 	if (proc_dir) {
593 		remove_proc_entry(PROCFS_NAME, proc_dir);
594 		hif_debug("/proc/%s/%s removed", PROCFS_DIR, PROCFS_NAME);
595 		remove_proc_entry(PROCFS_DIR, NULL);
596 		hif_debug("/proc/%s removed", PROCFS_DIR);
597 		proc_dir = NULL;
598 	}
599 }
600 #else
601 int athdiag_procfs_init(void *scn)
602 {
603 	return 0;
604 }
605 void athdiag_procfs_remove(void) {}
606 #endif
607