From 634fd53a63be4798da356dbc7251046b713d992a Mon Sep 17 00:00:00 2001 From: Neeraj Sanjay Kale Date: Mon, 14 Jul 2025 13:00:15 +0530 Subject: [PATCH] Bluetooth: btnxpuart: Correct the Independent Reset handling after FW dump MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This adds proper handling for the independent reset command sent by the driver after FW dump is complete. In normal scenario, the independent reset vendor command gives a success response before jumping to bootcode. However, when FW goes in a bad state, and sends out FW dump packets, the independent reset command does not get any response from the controller. [ 159.807732] Bluetooth: hci0: ==== Start FW dump === [ 180.759060] Bluetooth: hci0: ==== FW dump complete === [ 182.779208] Bluetooth: hci0: command 0xfcfc tx timeout [ 183.364974] Bluetooth: hci0: ChipID: 7601, Version: 0 [ 183.368490] Bluetooth: hci0: Request Firmware: nxp/uartspi_n61x_v1.bin.se [ 184.679977] Bluetooth: hci0: FW Download Complete: 417064 bytes [ 187.963102] Bluetooth: hci0: Opcode 0x0c03 failed: -110 As a fix for such scenario, the independent reset vendor command is sent using the __hci_cmd_send() API, which does not expect any response for vendor commands. __hci_cmd_send is non blocking, so before the tx_work is scheduled, it sometimes gets canceled and 3F|FC command is never sent. Adding a small delay after __hci_cmd_send allows the command to be sent to the controller. Signed-off-by: Neeraj Sanjay Kale Tested-by: Jean-Yves Salaün Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btnxpuart.c | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index 52e5cc3eb451..a2a0a3937d17 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -370,17 +370,26 @@ static u8 crc8_table[CRC8_TABLE_SIZE]; static struct sk_buff *nxp_drv_send_cmd(struct hci_dev *hdev, u16 opcode, u32 plen, - void *param) + void *param, + bool resp) { struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); struct ps_data *psdata = &nxpdev->psdata; - struct sk_buff *skb; + struct sk_buff *skb = NULL; /* set flag to prevent nxp_enqueue from parsing values from this command and * calling hci_cmd_sync_queue() again. */ psdata->driver_sent_cmd = true; - skb = __hci_cmd_sync(hdev, opcode, plen, param, HCI_CMD_TIMEOUT); + if (resp) { + skb = __hci_cmd_sync(hdev, opcode, plen, param, HCI_CMD_TIMEOUT); + } else { + __hci_cmd_send(hdev, opcode, plen, param); + /* Allow command to be sent before tx_work is cancelled + * by btnxpuart_flush() + */ + msleep(20); + } psdata->driver_sent_cmd = false; return skb; @@ -600,7 +609,8 @@ static int send_ps_cmd(struct hci_dev *hdev, void *data) pcmd.ps_cmd = BT_PS_DISABLE; pcmd.c2h_ps_interval = __cpu_to_le16(psdata->c2h_ps_interval); - skb = nxp_drv_send_cmd(hdev, HCI_NXP_AUTO_SLEEP_MODE, sizeof(pcmd), &pcmd); + skb = nxp_drv_send_cmd(hdev, HCI_NXP_AUTO_SLEEP_MODE, sizeof(pcmd), + &pcmd, true); if (IS_ERR(skb)) { bt_dev_err(hdev, "Setting Power Save mode failed (%ld)", PTR_ERR(skb)); return PTR_ERR(skb); @@ -649,7 +659,8 @@ static int send_wakeup_method_cmd(struct hci_dev *hdev, void *data) break; } - skb = nxp_drv_send_cmd(hdev, HCI_NXP_WAKEUP_METHOD, sizeof(pcmd), &pcmd); + skb = nxp_drv_send_cmd(hdev, HCI_NXP_WAKEUP_METHOD, sizeof(pcmd), + &pcmd, true); if (IS_ERR(skb)) { bt_dev_err(hdev, "Setting wake-up method failed (%ld)", PTR_ERR(skb)); return PTR_ERR(skb); @@ -1275,7 +1286,8 @@ static int nxp_set_baudrate_cmd(struct hci_dev *hdev, void *data) if (!psdata) return 0; - skb = nxp_drv_send_cmd(hdev, HCI_NXP_SET_OPER_SPEED, 4, (u8 *)&new_baudrate); + skb = nxp_drv_send_cmd(hdev, HCI_NXP_SET_OPER_SPEED, 4, + (u8 *)&new_baudrate, true); if (IS_ERR(skb)) { bt_dev_err(hdev, "Setting baudrate failed (%ld)", PTR_ERR(skb)); return PTR_ERR(skb); @@ -1333,7 +1345,7 @@ static void nxp_coredump(struct hci_dev *hdev) struct sk_buff *skb; u8 pcmd = 2; - skb = nxp_drv_send_cmd(hdev, HCI_NXP_TRIGGER_DUMP, 1, &pcmd); + skb = nxp_drv_send_cmd(hdev, HCI_NXP_TRIGGER_DUMP, 1, &pcmd, true); if (IS_ERR(skb)) bt_dev_err(hdev, "Failed to trigger FW Dump. (%ld)", PTR_ERR(skb)); else @@ -1375,7 +1387,6 @@ static int nxp_process_fw_dump(struct hci_dev *hdev, struct sk_buff *skb) if (buf_len == 0) { bt_dev_warn(hdev, "==== FW dump complete ==="); - clear_bit(BTNXPUART_FW_DUMP_IN_PROGRESS, &nxpdev->tx_state); hci_devcd_complete(hdev); nxp_set_ind_reset(hdev, NULL); } @@ -1489,7 +1500,13 @@ static int nxp_shutdown(struct hci_dev *hdev) u8 pcmd = 0; if (ind_reset_in_progress(nxpdev)) { - skb = nxp_drv_send_cmd(hdev, HCI_NXP_IND_RESET, 1, &pcmd); + if (test_and_clear_bit(BTNXPUART_FW_DUMP_IN_PROGRESS, + &nxpdev->tx_state)) + skb = nxp_drv_send_cmd(hdev, HCI_NXP_IND_RESET, 1, + &pcmd, false); + else + skb = nxp_drv_send_cmd(hdev, HCI_NXP_IND_RESET, 1, + &pcmd, true); serdev_device_set_flow_control(nxpdev->serdev, false); set_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); /* HCI_NXP_IND_RESET command may not returns any response */ -- 2.25.1