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