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

Configure Feed

Select the types of activity you want to include in your feed.

Bluetooth: btnxpuart: Correct the Independent Reset handling after FW dump

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 <neeraj.sanjaykale@nxp.com>
Tested-by: Jean-Yves Salaün <jean-yves.salaun@nxp.com>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

authored by

Neeraj Sanjay Kale and committed by
Luiz Augusto von Dentz
634fd53a ef568ae0

+26 -9
+26 -9
drivers/bluetooth/btnxpuart.c
··· 370 370 371 371 static struct sk_buff *nxp_drv_send_cmd(struct hci_dev *hdev, u16 opcode, 372 372 u32 plen, 373 - void *param) 373 + void *param, 374 + bool resp) 374 375 { 375 376 struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); 376 377 struct ps_data *psdata = &nxpdev->psdata; 377 - struct sk_buff *skb; 378 + struct sk_buff *skb = NULL; 378 379 379 380 /* set flag to prevent nxp_enqueue from parsing values from this command and 380 381 * calling hci_cmd_sync_queue() again. 381 382 */ 382 383 psdata->driver_sent_cmd = true; 383 - skb = __hci_cmd_sync(hdev, opcode, plen, param, HCI_CMD_TIMEOUT); 384 + if (resp) { 385 + skb = __hci_cmd_sync(hdev, opcode, plen, param, HCI_CMD_TIMEOUT); 386 + } else { 387 + __hci_cmd_send(hdev, opcode, plen, param); 388 + /* Allow command to be sent before tx_work is cancelled 389 + * by btnxpuart_flush() 390 + */ 391 + msleep(20); 392 + } 384 393 psdata->driver_sent_cmd = false; 385 394 386 395 return skb; ··· 609 600 pcmd.ps_cmd = BT_PS_DISABLE; 610 601 pcmd.c2h_ps_interval = __cpu_to_le16(psdata->c2h_ps_interval); 611 602 612 - skb = nxp_drv_send_cmd(hdev, HCI_NXP_AUTO_SLEEP_MODE, sizeof(pcmd), &pcmd); 603 + skb = nxp_drv_send_cmd(hdev, HCI_NXP_AUTO_SLEEP_MODE, sizeof(pcmd), 604 + &pcmd, true); 613 605 if (IS_ERR(skb)) { 614 606 bt_dev_err(hdev, "Setting Power Save mode failed (%ld)", PTR_ERR(skb)); 615 607 return PTR_ERR(skb); ··· 659 649 break; 660 650 } 661 651 662 - skb = nxp_drv_send_cmd(hdev, HCI_NXP_WAKEUP_METHOD, sizeof(pcmd), &pcmd); 652 + skb = nxp_drv_send_cmd(hdev, HCI_NXP_WAKEUP_METHOD, sizeof(pcmd), 653 + &pcmd, true); 663 654 if (IS_ERR(skb)) { 664 655 bt_dev_err(hdev, "Setting wake-up method failed (%ld)", PTR_ERR(skb)); 665 656 return PTR_ERR(skb); ··· 1286 1275 if (!psdata) 1287 1276 return 0; 1288 1277 1289 - skb = nxp_drv_send_cmd(hdev, HCI_NXP_SET_OPER_SPEED, 4, (u8 *)&new_baudrate); 1278 + skb = nxp_drv_send_cmd(hdev, HCI_NXP_SET_OPER_SPEED, 4, 1279 + (u8 *)&new_baudrate, true); 1290 1280 if (IS_ERR(skb)) { 1291 1281 bt_dev_err(hdev, "Setting baudrate failed (%ld)", PTR_ERR(skb)); 1292 1282 return PTR_ERR(skb); ··· 1345 1333 struct sk_buff *skb; 1346 1334 u8 pcmd = 2; 1347 1335 1348 - skb = nxp_drv_send_cmd(hdev, HCI_NXP_TRIGGER_DUMP, 1, &pcmd); 1336 + skb = nxp_drv_send_cmd(hdev, HCI_NXP_TRIGGER_DUMP, 1, &pcmd, true); 1349 1337 if (IS_ERR(skb)) 1350 1338 bt_dev_err(hdev, "Failed to trigger FW Dump. (%ld)", PTR_ERR(skb)); 1351 1339 else ··· 1387 1375 1388 1376 if (buf_len == 0) { 1389 1377 bt_dev_warn(hdev, "==== FW dump complete ==="); 1390 - clear_bit(BTNXPUART_FW_DUMP_IN_PROGRESS, &nxpdev->tx_state); 1391 1378 hci_devcd_complete(hdev); 1392 1379 nxp_set_ind_reset(hdev, NULL); 1393 1380 } ··· 1500 1489 u8 pcmd = 0; 1501 1490 1502 1491 if (ind_reset_in_progress(nxpdev)) { 1503 - skb = nxp_drv_send_cmd(hdev, HCI_NXP_IND_RESET, 1, &pcmd); 1492 + if (test_and_clear_bit(BTNXPUART_FW_DUMP_IN_PROGRESS, 1493 + &nxpdev->tx_state)) 1494 + skb = nxp_drv_send_cmd(hdev, HCI_NXP_IND_RESET, 1, 1495 + &pcmd, false); 1496 + else 1497 + skb = nxp_drv_send_cmd(hdev, HCI_NXP_IND_RESET, 1, 1498 + &pcmd, true); 1504 1499 serdev_device_set_flow_control(nxpdev->serdev, false); 1505 1500 set_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); 1506 1501 /* HCI_NXP_IND_RESET command may not returns any response */