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