]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
Bluetooth: btintel_pcie: Add recovery mechanism
authorKiran K <kiran.k@intel.com>
Tue, 1 Oct 2024 10:44:51 +0000 (16:14 +0530)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Thu, 14 Nov 2024 20:30:53 +0000 (15:30 -0500)
This patch adds a recovery mechanism to recover controller if firmware
download fails due to unresponsive controller, command timeout, firmware
signature verification failure etc. Driver attmepts maximum of 5 times
before giving up.

Signed-off-by: Kiran K <kiran.k@intel.com>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
drivers/bluetooth/btintel.c
drivers/bluetooth/btintel_pcie.c
drivers/bluetooth/btintel_pcie.h

index c9df27f3b80ed55e42f5b49a34a242fb7d8ac8ac..c34ff4e1a8938ef6665365e81cc1c7b7576d195e 100644 (file)
@@ -1252,6 +1252,12 @@ static void btintel_reset_to_bootloader(struct hci_dev *hdev)
        struct intel_reset params;
        struct sk_buff *skb;
 
+       /* PCIe transport uses shared hardware reset mechanism for recovery
+        * which gets triggered in pcie *setup* function on error.
+        */
+       if (hdev->bus == HCI_PCI)
+               return;
+
        /* Send Intel Reset command. This will result in
         * re-enumeration of BT controller.
         *
@@ -1267,6 +1273,7 @@ static void btintel_reset_to_bootloader(struct hci_dev *hdev)
         * boot_param:    Boot address
         *
         */
+
        params.reset_type = 0x01;
        params.patch_enable = 0x01;
        params.ddc_reload = 0x01;
@@ -2796,6 +2803,13 @@ int btintel_bootloader_setup_tlv(struct hci_dev *hdev,
         */
        boot_param = 0x00000000;
 
+       /* In case of PCIe, this function might get called multiple times with
+        * same hdev instance if there is any error on firmware download.
+        * Need to clear stale bits of previous firmware download attempt.
+        */
+       for (int i = 0; i < __INTEL_NUM_FLAGS; i++)
+               btintel_clear_flag(hdev, i);
+
        btintel_set_flag(hdev, INTEL_BOOTLOADER);
 
        err = btintel_prepare_fw_download_tlv(hdev, ver, &boot_param);
index 16ee962d4086147b47d9dc292b0231b069448640..e4ae8c898dfdb79e2ab7ee5383bfb5c931117ef4 100644 (file)
@@ -75,24 +75,6 @@ static inline void ipc_print_urbd1(struct hci_dev *hdev, struct urbd1 *urbd1,
                   index, urbd1->frbd_tag, urbd1->status, urbd1->fixed);
 }
 
-static int btintel_pcie_poll_bit(struct btintel_pcie_data *data, u32 offset,
-                                u32 bits, u32 mask, int timeout_us)
-{
-       int t = 0;
-       u32 reg;
-
-       do {
-               reg = btintel_pcie_rd_reg32(data, offset);
-
-               if ((reg & mask) == (bits & mask))
-                       return t;
-               udelay(POLL_INTERVAL_US);
-               t += POLL_INTERVAL_US;
-       } while (t < timeout_us);
-
-       return -ETIMEDOUT;
-}
-
 static struct btintel_pcie_data *btintel_pcie_get_data(struct msix_entry *entry)
 {
        u8 queue = entry->entry;
@@ -248,10 +230,47 @@ static void btintel_pcie_reset_ia(struct btintel_pcie_data *data)
        memset(data->ia.cr_tia, 0, sizeof(u16) * BTINTEL_PCIE_NUM_QUEUES);
 }
 
-static void btintel_pcie_reset_bt(struct btintel_pcie_data *data)
+static int btintel_pcie_reset_bt(struct btintel_pcie_data *data)
 {
-       btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG,
-                             BTINTEL_PCIE_CSR_FUNC_CTRL_SW_RESET);
+       u32 reg;
+       int retry = 3;
+
+       reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
+
+       reg &= ~(BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_ENA |
+                       BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_INIT |
+                       BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_INIT);
+       reg |= BTINTEL_PCIE_CSR_FUNC_CTRL_BUS_MASTER_DISCON;
+
+       btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg);
+
+       do {
+               reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
+               if (reg & BTINTEL_PCIE_CSR_FUNC_CTRL_BUS_MASTER_STS)
+                       break;
+               usleep_range(10000, 12000);
+
+       } while (--retry > 0);
+       usleep_range(10000, 12000);
+
+       reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
+
+       reg &= ~(BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_ENA |
+                       BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_INIT |
+                       BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_INIT);
+       reg |= BTINTEL_PCIE_CSR_FUNC_CTRL_SW_RESET;
+       btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg);
+       usleep_range(10000, 12000);
+
+       reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
+       bt_dev_dbg(data->hdev, "csr register after reset: 0x%8.8x", reg);
+
+       reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_BOOT_STAGE_REG);
+
+       /* If shared hardware reset is success then boot stage register shall be
+        * set to 0
+        */
+       return reg == 0 ? 0 : -ENODEV;
 }
 
 /* This function enables BT function by setting BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_INIT bit in
@@ -263,6 +282,7 @@ static void btintel_pcie_reset_bt(struct btintel_pcie_data *data)
 static int btintel_pcie_enable_bt(struct btintel_pcie_data *data)
 {
        int err;
+       u32 reg;
 
        data->gp0_received = false;
 
@@ -278,22 +298,17 @@ static int btintel_pcie_enable_bt(struct btintel_pcie_data *data)
        data->boot_stage_cache = 0x0;
 
        /* Set MAC_INIT bit to start primary bootloader */
-       btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
+       reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
+       reg &= ~(BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_INIT |
+                       BTINTEL_PCIE_CSR_FUNC_CTRL_BUS_MASTER_DISCON |
+                       BTINTEL_PCIE_CSR_FUNC_CTRL_SW_RESET);
+       reg |= (BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_ENA |
+                       BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_INIT);
 
-       btintel_pcie_set_reg_bits(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG,
-                                 BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_INIT);
-
-       /* Wait until MAC_ACCESS is granted */
-       err = btintel_pcie_poll_bit(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG,
-                                   BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_STS,
-                                   BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_STS,
-                                   BTINTEL_DEFAULT_MAC_ACCESS_TIMEOUT_US);
-       if (err < 0)
-               return -ENODEV;
+       btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg);
 
        /* MAC is ready. Enable BT FUNC */
        btintel_pcie_set_reg_bits(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG,
-                                 BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_ENA |
                                  BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_INIT);
 
        btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG);
@@ -1376,7 +1391,7 @@ static void btintel_pcie_release_hdev(struct btintel_pcie_data *data)
        data->hdev = NULL;
 }
 
-static int btintel_pcie_setup(struct hci_dev *hdev)
+static int btintel_pcie_setup_internal(struct hci_dev *hdev)
 {
        const u8 param[1] = { 0xFF };
        struct intel_version_tlv ver_tlv;
@@ -1467,6 +1482,32 @@ exit_error:
        return err;
 }
 
+static int btintel_pcie_setup(struct hci_dev *hdev)
+{
+       int err, fw_dl_retry = 0;
+       struct btintel_pcie_data *data = hci_get_drvdata(hdev);
+
+       while ((err = btintel_pcie_setup_internal(hdev)) && fw_dl_retry++ < 1) {
+               bt_dev_err(hdev, "Firmware download retry count: %d",
+                          fw_dl_retry);
+               err = btintel_pcie_reset_bt(data);
+               if (err) {
+                       bt_dev_err(hdev, "Failed to do shr reset: %d", err);
+                       break;
+               }
+               usleep_range(10000, 12000);
+               btintel_pcie_reset_ia(data);
+               btintel_pcie_config_msix(data);
+               err = btintel_pcie_enable_bt(data);
+               if (err) {
+                       bt_dev_err(hdev, "Failed to enable hardware: %d", err);
+                       break;
+               }
+               btintel_pcie_start_rx(data);
+       }
+       return err;
+}
+
 static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data)
 {
        int err;
index 8b7824ad005a2a72b88f71e88d23e0b279299f31..f9aada0543c48921fdf89eb29537814a7f27ea8e 100644 (file)
@@ -23,6 +23,8 @@
 #define BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_INIT            (BIT(6))
 #define BTINTEL_PCIE_CSR_FUNC_CTRL_FUNC_INIT           (BIT(7))
 #define BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_STS      (BIT(20))
+#define BTINTEL_PCIE_CSR_FUNC_CTRL_BUS_MASTER_STS      (BIT(28))
+#define BTINTEL_PCIE_CSR_FUNC_CTRL_BUS_MASTER_DISCON   (BIT(29))
 #define BTINTEL_PCIE_CSR_FUNC_CTRL_SW_RESET            (BIT(31))
 
 /* Value for BTINTEL_PCIE_CSR_BOOT_STAGE register */