Skip to content

Commit

Permalink
Bluetooth: btintel_pcie: Add handshake between driver and firmware
Browse files Browse the repository at this point in the history
The following handshake mechanism needs be followed after firmware
download is completed to bring the firmware to running state.

After firmware fragments of Operational image are downloaded and
secure sends result of the image succeeds,

1. Driver sends HCI Intel reset with boot option #1 to switch FW image.
2. FW sends Alive GP[0] MSIx
3. Driver enables data path (doorbell 0x460 for RBDs, etc...)
4. Driver gets Bootup event from firmware
5. Driver performs D0 entry to device (WRITE to IPC_Sleep_Control =0x0)
6. FW sends Alive GP[0] MSIx
7. Device host interface is fully set for BT protocol stack operation.
8. Driver may optionally get debug event with ID 0x97 which can be dropped

For Intermediate loadger image, all the above steps are applicable
expcept #5 and #6.

On HCI_OP_RESET, firmware raises alive interrupt. Driver needs to wait
for it before passing control over to bluetooth stack.

Co-developed-by: Devegowda Chandrashekar <chandrashekar.devegowda@intel.com>
Signed-off-by: Devegowda Chandrashekar <chandrashekar.devegowda@intel.com>
Signed-off-by: Kiran K <kiran.k@intel.com>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
(cherry picked from commit 05c200c8f0295c9c91beeb3ee0552331c1f8adbe)
  • Loading branch information
kirankrishnappa-intel authored and opsiff committed Dec 10, 2024
1 parent 2895ae8 commit cd8b4dd
Show file tree
Hide file tree
Showing 4 changed files with 329 additions and 12 deletions.
56 changes: 52 additions & 4 deletions drivers/bluetooth/btintel.c
Original file line number Diff line number Diff line change
Expand Up @@ -1841,6 +1841,37 @@ static int btintel_boot_wait(struct hci_dev *hdev, ktime_t calltime, int msec)
return 0;
}

static int btintel_boot_wait_d0(struct hci_dev *hdev, ktime_t calltime,
int msec)
{
ktime_t delta, rettime;
unsigned long long duration;
int err;

bt_dev_info(hdev, "Waiting for device transition to d0");

err = btintel_wait_on_flag_timeout(hdev, INTEL_WAIT_FOR_D0,
TASK_INTERRUPTIBLE,
msecs_to_jiffies(msec));
if (err == -EINTR) {
bt_dev_err(hdev, "Device d0 move interrupted");
return -EINTR;
}

if (err) {
bt_dev_err(hdev, "Device d0 move timeout");
return -ETIMEDOUT;
}

rettime = ktime_get();
delta = ktime_sub(rettime, calltime);
duration = (unsigned long long)ktime_to_ns(delta) >> 10;

bt_dev_info(hdev, "Device moved to D0 in %llu usecs", duration);

return 0;
}

static int btintel_boot(struct hci_dev *hdev, u32 boot_addr)
{
ktime_t calltime;
Expand All @@ -1849,6 +1880,7 @@ static int btintel_boot(struct hci_dev *hdev, u32 boot_addr)
calltime = ktime_get();

btintel_set_flag(hdev, INTEL_BOOTING);
btintel_set_flag(hdev, INTEL_WAIT_FOR_D0);

err = btintel_send_intel_reset(hdev, boot_addr);
if (err) {
Expand All @@ -1861,13 +1893,28 @@ static int btintel_boot(struct hci_dev *hdev, u32 boot_addr)
* is done by the operational firmware sending bootup notification.
*
* Booting into operational firmware should not take longer than
* 1 second. However if that happens, then just fail the setup
* 5 second. However if that happens, then just fail the setup
* since something went wrong.
*/
err = btintel_boot_wait(hdev, calltime, 1000);
if (err == -ETIMEDOUT)
err = btintel_boot_wait(hdev, calltime, 5000);
if (err == -ETIMEDOUT) {
btintel_reset_to_bootloader(hdev);
goto exit_error;
}

if (hdev->bus == HCI_PCI) {
/* In case of PCIe, after receiving bootup event, driver performs
* D0 entry by writing 0 to sleep control register (check
* btintel_pcie_recv_event())
* Firmware acks with alive interrupt indicating host is full ready to
* perform BT operation. Lets wait here till INTEL_WAIT_FOR_D0
* bit is cleared.
*/
calltime = ktime_get();
err = btintel_boot_wait_d0(hdev, calltime, 2000);
}

exit_error:
return err;
}

Expand Down Expand Up @@ -3274,7 +3321,7 @@ int btintel_configure_setup(struct hci_dev *hdev, const char *driver_name)
}
EXPORT_SYMBOL_GPL(btintel_configure_setup);

static int btintel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb)
int btintel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb)
{
struct intel_tlv *tlv = (void *)&skb->data[5];

Expand Down Expand Up @@ -3302,6 +3349,7 @@ static int btintel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb)
recv_frame:
return hci_recv_frame(hdev, skb);
}
EXPORT_SYMBOL_GPL(btintel_diagnostics);

int btintel_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
{
Expand Down
7 changes: 7 additions & 0 deletions drivers/bluetooth/btintel.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ enum {
INTEL_ROM_LEGACY,
INTEL_ROM_LEGACY_NO_WBS_SUPPORT,
INTEL_ACPI_RESET_ACTIVE,
INTEL_WAIT_FOR_D0,

__INTEL_NUM_FLAGS,
};
Expand Down Expand Up @@ -249,6 +250,7 @@ int btintel_bootloader_setup_tlv(struct hci_dev *hdev,
int btintel_shutdown_combined(struct hci_dev *hdev);
void btintel_hw_error(struct hci_dev *hdev, u8 code);
void btintel_print_fseq_info(struct hci_dev *hdev);
int btintel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb);
#else

static inline int btintel_check_bdaddr(struct hci_dev *hdev)
Expand Down Expand Up @@ -382,4 +384,9 @@ static inline void btintel_hw_error(struct hci_dev *hdev, u8 code)
static inline void btintel_print_fseq_info(struct hci_dev *hdev)
{
}

static inline int btintel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb)
{
return -EOPNOTSUPP;
}
#endif
Loading

0 comments on commit cd8b4dd

Please sign in to comment.