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