Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Bluetooth: btintel_pcie: Suspend/Resume: Controller doorbell interrupt handling

Due to a hardware bug during suspend/resume, the controller may miss a
doorbell interrupt. To address this, a retry mechanism has been added to
inform the controller before reporting a failure.

Test case:
- run suspend and resume cycles.

Signed-off-by: Ravindra <ravindra@intel.com>
Signed-off-by: Kiran K <kiran.k@intel.com>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

authored by

Ravindra and committed by
Luiz Augusto von Dentz
88c6216a 1fb0d830

+75 -44
+73 -44
drivers/bluetooth/btintel_pcie.c
··· 2524 2524 } 2525 2525 #endif 2526 2526 2527 + static int btintel_pcie_set_dxstate(struct btintel_pcie_data *data, u32 dxstate) 2528 + { 2529 + int retry = 0, status; 2530 + u32 dx_intr_timeout_ms = 200; 2531 + 2532 + do { 2533 + data->gp0_received = false; 2534 + 2535 + btintel_pcie_wr_sleep_cntrl(data, dxstate); 2536 + 2537 + status = wait_event_timeout(data->gp0_wait_q, data->gp0_received, 2538 + msecs_to_jiffies(dx_intr_timeout_ms)); 2539 + 2540 + if (status) 2541 + return 0; 2542 + 2543 + bt_dev_warn(data->hdev, 2544 + "Timeout (%u ms) on alive interrupt for D%d entry, retry count %d", 2545 + dx_intr_timeout_ms, dxstate, retry); 2546 + 2547 + /* clear gp0 cause */ 2548 + btintel_pcie_clr_reg_bits(data, 2549 + BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES, 2550 + BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0); 2551 + 2552 + /* A hardware bug may cause the alive interrupt to be missed. 2553 + * Check if the controller reached the expected state and retry 2554 + * the operation only if it hasn't. 2555 + */ 2556 + if (dxstate == BTINTEL_PCIE_STATE_D0) { 2557 + if (btintel_pcie_in_d0(data)) 2558 + return 0; 2559 + } else { 2560 + if (btintel_pcie_in_d3(data)) 2561 + return 0; 2562 + } 2563 + 2564 + } while (++retry < BTINTEL_PCIE_DX_TRANSITION_MAX_RETRIES); 2565 + 2566 + return -EBUSY; 2567 + } 2568 + 2527 2569 static int btintel_pcie_suspend_late(struct device *dev, pm_message_t mesg) 2528 2570 { 2529 2571 struct pci_dev *pdev = to_pci_dev(dev); ··· 2581 2539 2582 2540 data->pm_sx_event = mesg.event; 2583 2541 2584 - data->gp0_received = false; 2585 - 2586 2542 start = ktime_get(); 2587 2543 2588 2544 /* Refer: 6.4.11.7 -> Platform power management */ 2589 - btintel_pcie_wr_sleep_cntrl(data, dxstate); 2590 - err = wait_event_timeout(data->gp0_wait_q, data->gp0_received, 2591 - msecs_to_jiffies(BTINTEL_DEFAULT_INTR_TIMEOUT_MS)); 2592 - if (err == 0) { 2593 - bt_dev_err(data->hdev, 2594 - "Timeout (%u ms) on alive interrupt for D3 entry", 2595 - BTINTEL_DEFAULT_INTR_TIMEOUT_MS); 2596 - return -EBUSY; 2597 - } 2545 + err = btintel_pcie_set_dxstate(data, dxstate); 2546 + 2547 + if (err) 2548 + return err; 2598 2549 2599 2550 bt_dev_dbg(data->hdev, 2600 2551 "device entered into d3 state from d0 in %lld us", 2601 2552 ktime_to_us(ktime_get() - start)); 2602 - 2603 - return 0; 2553 + return err; 2604 2554 } 2605 2555 2606 2556 static int btintel_pcie_suspend(struct device *dev) ··· 2637 2603 } 2638 2604 2639 2605 /* Refer: 6.4.11.7 -> Platform power management */ 2640 - btintel_pcie_wr_sleep_cntrl(data, BTINTEL_PCIE_STATE_D0); 2641 - err = wait_event_timeout(data->gp0_wait_q, data->gp0_received, 2642 - msecs_to_jiffies(BTINTEL_DEFAULT_INTR_TIMEOUT_MS)); 2606 + err = btintel_pcie_set_dxstate(data, BTINTEL_PCIE_STATE_D0); 2607 + 2643 2608 if (err == 0) { 2644 - bt_dev_err(data->hdev, 2645 - "Timeout (%u ms) on alive interrupt for D0 entry", 2646 - BTINTEL_DEFAULT_INTR_TIMEOUT_MS); 2647 - 2648 - /* Trigger function level reset if the controller is in error 2649 - * state during resume() to bring back the controller to 2650 - * operational mode 2651 - */ 2652 - 2653 - data->boot_stage_cache = btintel_pcie_rd_reg32(data, 2654 - BTINTEL_PCIE_CSR_BOOT_STAGE_REG); 2655 - if (btintel_pcie_in_error(data) || 2656 - btintel_pcie_in_device_halt(data)) { 2657 - bt_dev_err(data->hdev, "Controller in error state for D0 entry"); 2658 - if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, 2659 - &data->flags)) { 2660 - data->dmp_hdr.trigger_reason = 2661 - BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT; 2662 - queue_work(data->workqueue, &data->rx_work); 2663 - } 2664 - set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags); 2665 - btintel_pcie_reset(data->hdev); 2666 - } 2667 - return -EBUSY; 2609 + bt_dev_dbg(data->hdev, 2610 + "device entered into d0 state from d3 in %lld us", 2611 + ktime_to_us(ktime_get() - start)); 2612 + return err; 2668 2613 } 2669 2614 2670 - bt_dev_dbg(data->hdev, 2671 - "device entered into d0 state from d3 in %lld us", 2672 - ktime_to_us(ktime_get() - start)); 2673 - return 0; 2615 + /* Trigger function level reset if the controller is in error 2616 + * state during resume() to bring back the controller to 2617 + * operational mode 2618 + */ 2619 + 2620 + data->boot_stage_cache = btintel_pcie_rd_reg32(data, 2621 + BTINTEL_PCIE_CSR_BOOT_STAGE_REG); 2622 + if (btintel_pcie_in_error(data) || 2623 + btintel_pcie_in_device_halt(data)) { 2624 + bt_dev_err(data->hdev, "Controller in error state for D0 entry"); 2625 + if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, 2626 + &data->flags)) { 2627 + data->dmp_hdr.trigger_reason = 2628 + BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT; 2629 + queue_work(data->workqueue, &data->rx_work); 2630 + } 2631 + set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags); 2632 + btintel_pcie_reset(data->hdev); 2633 + } 2634 + return err; 2674 2635 } 2675 2636 2676 2637 static const struct dev_pm_ops btintel_pcie_pm_ops = {
+2
drivers/bluetooth/btintel_pcie.h
··· 158 158 /* Default interrupt timeout in msec */ 159 159 #define BTINTEL_DEFAULT_INTR_TIMEOUT_MS 3000 160 160 161 + #define BTINTEL_PCIE_DX_TRANSITION_MAX_RETRIES 3 162 + 161 163 /* The number of descriptors in TX queues */ 162 164 #define BTINTEL_PCIE_TX_DESCS_COUNT 32 163 165