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