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