1 /* 2 * Copyright (c) 2013-2017 The Linux Foundation. All rights reserved. 3 * 4 * Previously licensed under the ISC license by Qualcomm Atheros, Inc. 5 * 6 * 7 * Permission to use, copy, modify, and/or distribute this software for 8 * any purpose with or without fee is hereby granted, provided that the 9 * above copyright notice and this permission notice appear in all 10 * copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 14 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 15 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 16 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 17 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 18 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 19 * PERFORMANCE OF THIS SOFTWARE. 20 */ 21 22 /* 23 * This file was originally distributed by Qualcomm Atheros, Inc. 24 * under proprietary terms before Copyright ownership was assigned 25 * to the Linux Foundation. 26 */ 27 28 #include <linux/usb.h> 29 #include <linux/usb/hcd.h> 30 #include "if_usb.h" 31 #include "hif_usb_internal.h" 32 #include "bmi_msg.h" /* TARGET_TYPE_ */ 33 #include "regtable_usb.h" 34 #include "ol_fw.h" 35 #include "hif_debug.h" 36 #include "epping_main.h" 37 #include "hif_main.h" 38 #include "qwlan_version.h" 39 40 #define DELAY_FOR_TARGET_READY 200 /* 200ms */ 41 42 /* Save memory addresses where we save FW ram dump, and then we could obtain 43 * them by symbol table. 44 */ 45 uint32_t fw_stack_addr; 46 void *fw_ram_seg_addr[FW_RAM_SEG_CNT]; 47 48 49 50 static int hif_usb_unload_dev_num = -1; 51 struct hif_usb_softc *g_usb_sc; 52 53 /** 54 * hif_usb_diag_write_cold_reset() - reset SOC by sending a diag command 55 * @scn: pointer to ol_softc structure 56 * 57 * Return: QDF_STATUS_SUCCESS if success else an appropriate QDF_STATUS error 58 */ 59 static inline QDF_STATUS 60 hif_usb_diag_write_cold_reset(struct hif_softc *scn) 61 { 62 struct hif_opaque_softc *hif_hdl = GET_HIF_OPAQUE_HDL(scn); 63 64 65 HIF_DBG("%s: resetting SOC", __func__); 66 67 return hif_diag_write_access(hif_hdl, 68 (ROME_USB_SOC_RESET_CONTROL_COLD_RST_LSB | 69 ROME_USB_RTC_SOC_BASE_ADDRESS), 70 SOC_RESET_CONTROL_COLD_RST_SET(1)); 71 } 72 73 /** 74 * hif_usb_procfs_init() - create init procfs 75 * @scn: pointer to hif_usb_softc structure 76 * 77 * Return: int 0 if success else an appropriate error number 78 */ 79 static int 80 hif_usb_procfs_init(struct hif_softc *scn) 81 { 82 int ret = 0; 83 84 HIF_ENTER(); 85 86 if (athdiag_procfs_init(scn) != 0) { 87 HIF_ERROR("athdiag_procfs_init failed"); 88 ret = A_ERROR; 89 } 90 91 scn->athdiag_procfs_inited = true; 92 93 HIF_EXIT(); 94 return ret; 95 } 96 97 /** 98 * hif_nointrs(): disable IRQ 99 * @scn: pointer to struct hif_softc 100 * 101 * This function stops interrupt(s) 102 * 103 * Return: none 104 */ 105 void hif_usb_nointrs(struct hif_softc *scn) 106 { 107 108 } 109 110 /** 111 * hif_usb_reboot() - called at reboot time to reset WLAN SOC 112 * @nb: pointer to notifier_block registered during register_reboot_notifier 113 * @val: code indicating reboot reason 114 * @v: unused pointer 115 * 116 * Return: int 0 if success else an appropriate error number 117 */ 118 static int hif_usb_reboot(struct notifier_block *nb, unsigned long val, 119 void *v) 120 { 121 struct hif_usb_softc *sc; 122 123 HIF_ENTER(); 124 sc = container_of(nb, struct hif_usb_softc, reboot_notifier); 125 /* do cold reset */ 126 hif_usb_diag_write_cold_reset(HIF_GET_SOFTC(sc)); 127 HIF_EXIT(); 128 return NOTIFY_DONE; 129 } 130 131 /** 132 * hif_usb_disable_lpm() - Disable lpm feature of usb2.0 133 * @udev: pointer to usb_device for which LPM is to be disabled 134 * 135 * LPM needs to be disabled to avoid usb2.0 probe timeout 136 * 137 * Return: int 0 if success else an appropriate error number 138 */ 139 static int hif_usb_disable_lpm(struct usb_device *udev) 140 { 141 struct usb_hcd *hcd; 142 int ret = -EPERM; 143 144 HIF_ENTER(); 145 146 if (!udev || !udev->bus) { 147 HIF_ERROR("Invalid input parameters"); 148 goto exit; 149 } 150 151 hcd = bus_to_hcd(udev->bus); 152 if (udev->usb2_hw_lpm_enabled) { 153 if (hcd->driver->set_usb2_hw_lpm) { 154 ret = hcd->driver->set_usb2_hw_lpm(hcd, udev, false); 155 if (!ret) { 156 udev->usb2_hw_lpm_enabled = false; 157 udev->usb2_hw_lpm_capable = false; 158 HIF_TRACE("%s: LPM is disabled", __func__); 159 } else { 160 HIF_TRACE("%s: Fail to disable LPM", 161 __func__); 162 } 163 } else { 164 HIF_TRACE("%s: hcd doesn't support LPM", 165 __func__); 166 } 167 } else { 168 HIF_TRACE("%s: LPM isn't enabled", __func__); 169 } 170 exit: 171 HIF_EXIT(); 172 return ret; 173 } 174 175 /** 176 * hif_usb_enable_bus() - enable usb bus 177 * @ol_sc: hif_softc struct 178 * @dev: device pointer 179 * @bdev: bus dev pointer 180 * @bid: bus id pointer 181 * @type: enum hif_enable_type such as HIF_ENABLE_TYPE_PROBE 182 * 183 * Return: QDF_STATUS_SUCCESS on success and error QDF status on failure 184 */ 185 QDF_STATUS hif_usb_enable_bus(struct hif_softc *scn, 186 struct device *dev, void *bdev, 187 const struct hif_bus_id *bid, 188 enum hif_enable_type type) 189 190 { 191 struct usb_interface *interface = (struct usb_interface *)bdev; 192 struct usb_device_id *id = (struct usb_device_id *)bid; 193 int ret = 0; 194 struct hif_usb_softc *sc; 195 struct usb_device *usbdev = interface_to_usbdev(interface); 196 int vendor_id, product_id; 197 198 usb_get_dev(usbdev); 199 200 if (!scn) { 201 HIF_ERROR("%s: hif_ctx is NULL", __func__); 202 goto err_usb; 203 } 204 205 sc = HIF_GET_USB_SOFTC(scn); 206 207 HIF_INFO("%s hif_softc %pK usbdev %pK interface %pK\n", 208 __func__, 209 scn, 210 usbdev, 211 interface); 212 213 vendor_id = qdf_le16_to_cpu(usbdev->descriptor.idVendor); 214 product_id = qdf_le16_to_cpu(usbdev->descriptor.idProduct); 215 216 HIF_ERROR("%s: con_mode = 0x%x, vendor_id = 0x%x product_id = 0x%x", 217 __func__, hif_get_conparam(scn), vendor_id, product_id); 218 219 sc->pdev = (void *)usbdev; 220 sc->dev = &usbdev->dev; 221 sc->devid = id->idProduct; 222 223 if ((usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 224 USB_REQ_SET_CONFIGURATION, 0, 1, 0, NULL, 0, 225 HZ)) < 0) { 226 HIF_ERROR("%s[%d]", __func__, __LINE__); 227 goto err_usb; 228 } 229 230 usb_set_interface(usbdev, 0, 0); 231 /* disable lpm to avoid usb2.0 probe timeout */ 232 hif_usb_disable_lpm(usbdev); 233 234 /* params need to be added - TODO 235 * scn->enableuartprint = 1; 236 * scn->enablefwlog = 0; 237 * scn->max_no_of_peers = 1; 238 */ 239 240 sc->interface = interface; 241 sc->reboot_notifier.notifier_call = hif_usb_reboot; 242 register_reboot_notifier(&sc->reboot_notifier); 243 244 if (hif_usb_device_init(sc) != QDF_STATUS_SUCCESS) { 245 HIF_ERROR("ath: %s: hif_usb_device_init failed", __func__); 246 goto err_reset; 247 } 248 249 if (hif_usb_procfs_init(scn)) 250 goto err_reset; 251 252 hif_usb_unload_dev_num = usbdev->devnum; 253 g_usb_sc = sc; 254 HIF_EXIT(); 255 return 0; 256 257 err_reset: 258 hif_usb_diag_write_cold_reset(scn); 259 g_usb_sc = NULL; 260 hif_usb_unload_dev_num = -1; 261 unregister_reboot_notifier(&sc->reboot_notifier); 262 err_usb: 263 ret = QDF_STATUS_E_FAILURE; 264 usb_put_dev(usbdev); 265 return ret; 266 } 267 268 269 /** 270 * hif_usb_close(): close bus, delete hif_sc 271 * @ol_sc: soft_sc struct 272 * 273 * Return: none 274 */ 275 void hif_usb_close(struct hif_softc *scn) 276 { 277 g_usb_sc = NULL; 278 } 279 280 /** 281 * hif_usb_disable_bus(): This function disables usb bus 282 * @hif_ctx: pointer to struct hif_softc 283 * 284 * Return: none 285 */ 286 void hif_usb_disable_bus(struct hif_softc *hif_ctx) 287 { 288 struct hif_usb_softc *sc = HIF_GET_USB_SOFTC(hif_ctx); 289 struct usb_interface *interface = sc->interface; 290 struct usb_device *udev = interface_to_usbdev(interface); 291 292 HIF_TRACE("%s: trying to remove hif_usb!", __func__); 293 294 /* disable lpm to avoid following cold reset will 295 * cause xHCI U1/U2 timeout 296 */ 297 usb_disable_lpm(udev); 298 299 /* wait for disable lpm */ 300 set_current_state(TASK_INTERRUPTIBLE); 301 schedule_timeout(msecs_to_jiffies(DELAY_FOR_TARGET_READY)); 302 set_current_state(TASK_RUNNING); 303 304 /* do cold reset */ 305 hif_usb_diag_write_cold_reset(hif_ctx); 306 307 if (g_usb_sc->suspend_state) 308 hif_bus_resume(GET_HIF_OPAQUE_HDL(hif_ctx)); 309 310 unregister_reboot_notifier(&sc->reboot_notifier); 311 usb_put_dev(interface_to_usbdev(interface)); 312 313 hif_usb_device_deinit(sc); 314 315 HIF_TRACE("%s hif_usb removed !!!!!!", __func__); 316 } 317 318 /** 319 * hif_usb_bus_suspend() - suspend the bus 320 * @hif_ctx: hif_ctx 321 * 322 * This function suspends the bus, but usb doesn't need to suspend. 323 * Therefore just remove all the pending urb transactions 324 * 325 * Return: 0 for success and non-zero for failure 326 */ 327 int hif_usb_bus_suspend(struct hif_softc *hif_ctx) 328 { 329 struct hif_usb_softc *sc = HIF_GET_USB_SOFTC(hif_ctx); 330 HIF_DEVICE_USB *device = HIF_GET_USB_DEVICE(hif_ctx); 331 332 HIF_ENTER(); 333 sc->suspend_state = 1; 334 usb_hif_flush_all(device); 335 HIF_EXIT(); 336 return 0; 337 } 338 339 /** 340 * hif_usb_bus_resume() - hif resume API 341 * @hif_ctx: struct hif_opaque_softc 342 * 343 * This function resumes the bus. but usb doesn't need to resume. 344 * Post recv urbs for RX data pipe 345 * 346 * Return: 0 for success and non-zero for failure 347 */ 348 int hif_usb_bus_resume(struct hif_softc *hif_ctx) 349 { 350 struct hif_usb_softc *sc = HIF_GET_USB_SOFTC(hif_ctx); 351 HIF_DEVICE_USB *device = HIF_GET_USB_DEVICE(hif_ctx); 352 353 HIF_ENTER(); 354 sc->suspend_state = 0; 355 usb_hif_start_recv_pipes(device); 356 357 HIF_EXIT(); 358 return 0; 359 } 360 361 /** 362 * hif_usb_bus_reset_resume() - resume the bus after reset 363 * @scn: struct hif_opaque_softc 364 * 365 * This function is called to tell the driver that USB device has been resumed 366 * and it has also been reset. The driver should redo any necessary 367 * initialization. This function resets WLAN SOC. 368 * 369 * Return: int 0 for success, non zero for failure 370 */ 371 int hif_usb_bus_reset_resume(struct hif_softc *hif_ctx) 372 { 373 int ret = 0; 374 375 HIF_ENTER(); 376 if (hif_usb_diag_write_cold_reset(hif_ctx) != QDF_STATUS_SUCCESS) 377 ret = 1; 378 379 HIF_EXIT(); 380 return ret; 381 } 382 383 /** 384 * hif_usb_open()- initialization routine for usb bus 385 * @ol_sc: ol_sc 386 * @bus_type: bus type 387 * 388 * Return: QDF_STATUS_SUCCESS on success and error QDF status on failure 389 */ 390 QDF_STATUS hif_usb_open(struct hif_softc *hif_ctx, 391 enum qdf_bus_type bus_type) 392 { 393 hif_ctx->bus_type = bus_type; 394 return QDF_STATUS_SUCCESS; 395 } 396 397 /** 398 * hif_usb_disable_isr(): disable isr 399 * @hif_ctx: struct hif_softc 400 * 401 * Return: void 402 */ 403 void hif_usb_disable_isr(struct hif_softc *hif_ctx) 404 { 405 /* TODO */ 406 } 407 408 /** 409 * hif_usb_reg_tbl_attach()- attach hif, target register tables 410 * @scn: pointer to ol_softc structure 411 * 412 * Attach host and target register tables based on target_type, target_version 413 * 414 * Return: none 415 */ 416 void hif_usb_reg_tbl_attach(struct hif_softc *scn) 417 { 418 u_int32_t hif_type, target_type; 419 int32_t ret = 0; 420 uint32_t chip_id; 421 QDF_STATUS rv; 422 struct hif_target_info *tgt_info = &scn->target_info; 423 struct hif_opaque_softc *hif_hdl = GET_HIF_OPAQUE_HDL(scn); 424 425 if (scn->hostdef == NULL && scn->targetdef == NULL) { 426 switch (tgt_info->target_type) { 427 case TARGET_TYPE_AR6320: 428 switch (tgt_info->target_version) { 429 case AR6320_REV1_VERSION: 430 case AR6320_REV1_1_VERSION: 431 case AR6320_REV1_3_VERSION: 432 hif_type = HIF_TYPE_AR6320; 433 target_type = TARGET_TYPE_AR6320; 434 break; 435 case AR6320_REV2_1_VERSION: 436 case AR6320_REV3_VERSION: 437 case QCA9377_REV1_1_VERSION: 438 case QCA9379_REV1_VERSION: 439 hif_type = HIF_TYPE_AR6320V2; 440 target_type = TARGET_TYPE_AR6320V2; 441 break; 442 default: 443 ret = -1; 444 break; 445 } 446 break; 447 default: 448 ret = -1; 449 break; 450 } 451 452 if (ret) 453 return; 454 455 /* assign target register table if we find 456 * corresponding type 457 */ 458 hif_register_tbl_attach(scn, hif_type); 459 target_register_tbl_attach(scn, target_type); 460 /* read the chip revision*/ 461 rv = hif_diag_read_access(hif_hdl, 462 (CHIP_ID_ADDRESS | 463 RTC_SOC_BASE_ADDRESS), 464 &chip_id); 465 if (rv != QDF_STATUS_SUCCESS) { 466 HIF_ERROR("%s: get chip id val (%d)", __func__, 467 rv); 468 } 469 tgt_info->target_revision = 470 CHIP_ID_REVISION_GET(chip_id); 471 } 472 } 473 474 /** 475 * hif_usb_get_hw_info()- attach register table for USB 476 * @hif_ctx: pointer to hif_softc structure 477 478 * This function is used to attach the host and target register tables. 479 * Ideally, we should not attach register tables as a part of this function. 480 * There is scope of cleanup to move register table attach during 481 * initialization for USB bus. 482 * 483 * The reason we are doing register table attach for USB here is that, it relies 484 * on target_info->target_type and target_info->target_version, 485 * which get populated during bmi_firmware_download. "hif_get_fw_info" is the 486 * only initialization related call into HIF there after. 487 * 488 * To fix this, we can move the "get target info, functionality currently in 489 * bmi_firmware_download into hif initialization functions. This change will 490 * affect all buses. Can be taken up as a part of convergence. 491 * 492 * Return: none 493 */ 494 void hif_usb_get_hw_info(struct hif_softc *hif_ctx) 495 { 496 hif_usb_reg_tbl_attach(hif_ctx); 497 } 498 499 500 /** 501 * hif_bus_configure() - configure the bus 502 * @scn: pointer to the hif context. 503 * 504 * return: 0 for success. nonzero for failure. 505 */ 506 int hif_usb_bus_configure(struct hif_softc *scn) 507 { 508 return 0; 509 } 510 511 /** 512 * hif_usb_irq_enable() - hif_usb_irq_enable 513 * @scn: hif_softc 514 * @ce_id: ce_id 515 * 516 * Return: void 517 */ 518 void hif_usb_irq_enable(struct hif_softc *scn, int ce_id) 519 { 520 } 521 522 /** 523 * hif_usb_irq_disable() - hif_usb_irq_disable 524 * @scn: hif_softc 525 * @ce_id: ce_id 526 * 527 * Return: void 528 */ 529 void hif_usb_irq_disable(struct hif_softc *scn, int ce_id) 530 { 531 } 532 533 /** 534 * hif_usb_shutdown_bus_device() - This function shuts down the device 535 * @scn: hif opaque pointer 536 * 537 * Return: void 538 */ 539 void hif_usb_shutdown_bus_device(struct hif_softc *scn) 540 { 541 } 542 543 /** 544 * hif_trigger_dump() - trigger various dump cmd 545 * @scn: struct hif_opaque_softc 546 * @cmd_id: dump command id 547 * @start: start/stop dump 548 * 549 * Return: None 550 */ 551 void hif_trigger_dump(struct hif_opaque_softc *scn, uint8_t cmd_id, bool start) 552 { 553 } 554 555 /** 556 * hif_wlan_disable() - call the platform driver to disable wlan 557 * @scn: scn 558 * 559 * Return: void 560 */ 561 void hif_wlan_disable(struct hif_softc *scn) 562 { 563 } 564 565 /** 566 * hif_fw_assert_ramdump_pattern() - handle firmware assert with ramdump pattern 567 * @sc: pointer to hif_usb_softc structure 568 * 569 * Return: void 570 */ 571 572 void hif_fw_assert_ramdump_pattern(struct hif_usb_softc *sc) 573 { 574 uint32_t *reg, pattern, i = 0; 575 uint32_t len; 576 uint8_t *data; 577 uint8_t *ram_ptr = NULL; 578 char *fw_ram_seg_name[FW_RAM_SEG_CNT] = {"DRAM", "IRAM", "AXI"}; 579 size_t fw_ram_reg_size[FW_RAM_SEG_CNT] = { 580 FW_RAMDUMP_DRAMSIZE, 581 FW_RAMDUMP_IRAMSIZE, 582 FW_RAMDUMP_AXISIZE }; 583 584 data = sc->fw_data; 585 len = sc->fw_data_len; 586 pattern = *((A_UINT32 *) data); 587 588 qdf_assert(sc->ramdump_index < FW_RAM_SEG_CNT); 589 i = sc->ramdump_index; 590 reg = (uint32_t *) (data + 4); 591 if (sc->fw_ram_dumping == 0) { 592 sc->fw_ram_dumping = 1; 593 HIF_ERROR("Firmware %s dump:\n", fw_ram_seg_name[i]); 594 sc->ramdump[i] = 595 qdf_mem_malloc(sizeof(struct fw_ramdump) + 596 fw_ram_reg_size[i]); 597 if (!sc->ramdump[i]) { 598 pr_err("Fail to allocate memory for ram dump"); 599 QDF_BUG(0); 600 } 601 (sc->ramdump[i])->mem = (uint8_t *) (sc->ramdump[i] + 1); 602 fw_ram_seg_addr[i] = (sc->ramdump[i])->mem; 603 HIF_ERROR("FW %s start addr = %#08x\n", 604 fw_ram_seg_name[i], *reg); 605 HIF_ERROR("Memory addr for %s = %pK\n", 606 fw_ram_seg_name[i], 607 (sc->ramdump[i])->mem); 608 (sc->ramdump[i])->start_addr = *reg; 609 (sc->ramdump[i])->length = 0; 610 } 611 reg++; 612 ram_ptr = (sc->ramdump[i])->mem + (sc->ramdump[i])->length; 613 (sc->ramdump[i])->length += (len - 8); 614 if (sc->ramdump[i]->length <= fw_ram_reg_size[i]) { 615 qdf_mem_copy(ram_ptr, (A_UINT8 *) reg, len - 8); 616 } else { 617 HIF_ERROR("memory copy overlap\n"); 618 QDF_BUG(0); 619 } 620 621 if (pattern == FW_RAMDUMP_END_PATTERN) { 622 HIF_ERROR("%s memory size = %d\n", fw_ram_seg_name[i], 623 (sc->ramdump[i])->length); 624 if (i == (FW_RAM_SEG_CNT - 1)) 625 QDF_BUG(0); 626 627 sc->ramdump_index++; 628 sc->fw_ram_dumping = 0; 629 } 630 } 631 632 /** 633 * hif_usb_ramdump_handler(): dump bus debug registers 634 * @scn: struct hif_opaque_softc 635 * 636 * This function is to receive information of firmware crash dump, and 637 * save it in host memory. It consists of 5 parts: registers, call stack, 638 * DRAM dump, IRAM dump, and AXI dump, and they are reported to host in order. 639 * 640 * registers: wrapped in a USB packet by starting as FW_ASSERT_PATTERN and 641 * 60 registers. 642 * call stack: wrapped in multiple USB packets, and each of them starts as 643 * FW_REG_PATTERN and contains multiple double-words. The tail 644 * of the last packet is FW_REG_END_PATTERN. 645 * DRAM dump: wrapped in multiple USB pakcets, and each of them start as 646 * FW_RAMDUMP_PATTERN and contains multiple double-wors. The tail 647 * of the last packet is FW_RAMDUMP_END_PATTERN; 648 * IRAM dump and AXI dump are with the same format as DRAM dump. 649 * 650 * Return: 0 for success or error code 651 */ 652 653 void hif_usb_ramdump_handler(struct hif_opaque_softc *scn) 654 { 655 uint32_t *reg, pattern, i, start_addr = 0; 656 uint32_t len; 657 uint8_t *data; 658 uint8_t str_buf[128]; 659 uint32_t remaining; 660 struct hif_usb_softc *sc = HIF_GET_USB_SOFTC(scn); 661 struct hif_softc *hif_ctx = HIF_GET_SOFTC(scn); 662 struct hif_target_info *tgt_info = &hif_ctx->target_info; 663 664 data = sc->fw_data; 665 len = sc->fw_data_len; 666 pattern = *((A_UINT32 *) data); 667 668 if (pattern == FW_ASSERT_PATTERN) { 669 HIF_ERROR("Firmware crash detected...\n"); 670 HIF_ERROR("Host SW version: %s\n", QWLAN_VERSIONSTR); 671 HIF_ERROR("target_type: %d.target_version %d. target_revision%d.", 672 tgt_info->target_type, 673 tgt_info->target_version, 674 tgt_info->target_revision); 675 676 reg = (uint32_t *) (data + 4); 677 print_hex_dump(KERN_DEBUG, " ", DUMP_PREFIX_OFFSET, 16, 4, reg, 678 min_t(A_UINT32, len - 4, FW_REG_DUMP_CNT * 4), 679 false); 680 sc->fw_ram_dumping = 0; 681 682 } else if (pattern == FW_REG_PATTERN) { 683 reg = (uint32_t *) (data + 4); 684 start_addr = *reg++; 685 if (sc->fw_ram_dumping == 0) { 686 pr_err("Firmware stack dump:"); 687 sc->fw_ram_dumping = 1; 688 fw_stack_addr = start_addr; 689 } 690 remaining = len - 8; 691 /* len is in byte, but it's printed in double-word. */ 692 for (i = 0; i < (len - 8); i += 16) { 693 if ((*reg == FW_REG_END_PATTERN) && (i == len - 12)) { 694 sc->fw_ram_dumping = 0; 695 pr_err("Stack start address = %#08x\n", 696 fw_stack_addr); 697 break; 698 } 699 hex_dump_to_buffer(reg, remaining, 16, 4, str_buf, 700 sizeof(str_buf), false); 701 pr_err("%#08x: %s\n", start_addr + i, str_buf); 702 remaining -= 16; 703 reg += 4; 704 } 705 } else if ((!sc->enable_self_recovery) && 706 ((pattern & FW_RAMDUMP_PATTERN_MASK) == 707 FW_RAMDUMP_PATTERN)) { 708 hif_fw_assert_ramdump_pattern(sc); 709 } 710 } 711 712 #ifndef QCA_WIFI_3_0 713 /** 714 * hif_check_fw_reg(): hif_check_fw_reg 715 * @scn: scn 716 * @state: 717 * 718 * Return: int 719 */ 720 int hif_check_fw_reg(struct hif_opaque_softc *scn) 721 { 722 return 0; 723 } 724 #endif 725 726