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