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