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: Add support for HCI coredump feature

This adds support for Bluetooth Coredump feature to BTNXPUART driver to
collect FW dumps on demand, or in case FW goes in a bad state.

To trigger manual FW dump, following command can be used:
echo 1 > /sys/class/bluetooth/hci0/device/coredump

Once FW dump is complete, it can be written to a file:
cat /sys/class/bluetooth/hci0/devcoredump/data > fw_dump

While FW dump is in progress, any HCI command will return -EBUSY.

After FW dump is complete, driver will give HCI_NXP_IND_RESET command
which soft-resets the chip, allowing FW re-download.

Signed-off-by: Neeraj Sanjay Kale <neeraj.sanjaykale@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
998e447f 6fca6781

+132 -15
+132 -15
drivers/bluetooth/btnxpuart.c
··· 31 31 #define BTNXPUART_SERDEV_OPEN 4 32 32 #define BTNXPUART_IR_IN_PROGRESS 5 33 33 #define BTNXPUART_FW_DOWNLOAD_ABORT 6 34 + #define BTNXPUART_FW_DUMP_IN_PROGRESS 7 34 35 35 36 /* NXP HW err codes */ 36 37 #define BTNXPUART_IR_HW_ERR 0xb0 ··· 107 106 #define HCI_NXP_SET_OPER_SPEED 0xfc09 108 107 /* Bluetooth vendor command: Independent Reset */ 109 108 #define HCI_NXP_IND_RESET 0xfcfc 109 + /* Bluetooth vendor command: Trigger FW dump */ 110 + #define HCI_NXP_TRIGGER_DUMP 0xfe91 110 111 111 112 /* Bluetooth Power State : Vendor cmd params */ 112 113 #define BT_PS_ENABLE 0x02 ··· 311 308 union nxp_v3_rx_timeout_nak_u { 312 309 struct nxp_v3_rx_timeout_nak pkt; 313 310 u8 buf[6]; 311 + }; 312 + 313 + /* FW dump */ 314 + #define NXP_FW_DUMP_SIZE (1024 * 1000) 315 + 316 + struct nxp_fw_dump_hdr { 317 + __le16 seq_num; 318 + __le16 reserved; 319 + __le16 buf_type; 320 + __le16 buf_len; 314 321 }; 315 322 316 323 static u8 crc8_table[CRC8_TABLE_SIZE]; ··· 787 774 return test_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); 788 775 } 789 776 777 + static bool ind_reset_in_progress(struct btnxpuart_dev *nxpdev) 778 + { 779 + return test_bit(BTNXPUART_IR_IN_PROGRESS, &nxpdev->tx_state); 780 + } 781 + 782 + static bool fw_dump_in_progress(struct btnxpuart_dev *nxpdev) 783 + { 784 + return test_bit(BTNXPUART_FW_DUMP_IN_PROGRESS, &nxpdev->tx_state); 785 + } 786 + 790 787 static bool process_boot_signature(struct btnxpuart_dev *nxpdev) 791 788 { 792 789 if (test_bit(BTNXPUART_CHECK_BOOT_SIGNATURE, &nxpdev->tx_state)) { ··· 1198 1175 static int nxp_check_boot_sign(struct btnxpuart_dev *nxpdev) 1199 1176 { 1200 1177 serdev_device_set_baudrate(nxpdev->serdev, HCI_NXP_PRI_BAUDRATE); 1201 - if (test_bit(BTNXPUART_IR_IN_PROGRESS, &nxpdev->tx_state)) 1178 + if (ind_reset_in_progress(nxpdev)) 1202 1179 serdev_device_set_flow_control(nxpdev->serdev, false); 1203 1180 else 1204 1181 serdev_device_set_flow_control(nxpdev->serdev, true); ··· 1225 1202 1226 1203 /* Inject Hardware Error to upper stack */ 1227 1204 return hci_recv_frame(hdev, skb); 1205 + } 1206 + 1207 + /* Firmware dump */ 1208 + static void nxp_coredump(struct hci_dev *hdev) 1209 + { 1210 + struct sk_buff *skb; 1211 + u8 pcmd = 2; 1212 + 1213 + skb = nxp_drv_send_cmd(hdev, HCI_NXP_TRIGGER_DUMP, 1, &pcmd); 1214 + if (!IS_ERR(skb)) 1215 + kfree_skb(skb); 1216 + } 1217 + 1218 + static void nxp_coredump_hdr(struct hci_dev *hdev, struct sk_buff *skb) 1219 + { 1220 + /* Nothing to be added in FW dump header */ 1221 + } 1222 + 1223 + static int nxp_process_fw_dump(struct hci_dev *hdev, struct sk_buff *skb) 1224 + { 1225 + struct hci_acl_hdr *acl_hdr = (struct hci_acl_hdr *)skb_pull_data(skb, 1226 + sizeof(*acl_hdr)); 1227 + struct nxp_fw_dump_hdr *fw_dump_hdr = (struct nxp_fw_dump_hdr *)skb->data; 1228 + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); 1229 + __u16 seq_num = __le16_to_cpu(fw_dump_hdr->seq_num); 1230 + __u16 buf_len = __le16_to_cpu(fw_dump_hdr->buf_len); 1231 + int err; 1232 + 1233 + if (seq_num == 0x0001) { 1234 + if (test_and_set_bit(BTNXPUART_FW_DUMP_IN_PROGRESS, &nxpdev->tx_state)) { 1235 + bt_dev_err(hdev, "FW dump already in progress"); 1236 + goto free_skb; 1237 + } 1238 + bt_dev_warn(hdev, "==== Start FW dump ==="); 1239 + err = hci_devcd_init(hdev, NXP_FW_DUMP_SIZE); 1240 + if (err < 0) 1241 + goto free_skb; 1242 + 1243 + schedule_delayed_work(&hdev->dump.dump_timeout, 1244 + msecs_to_jiffies(20000)); 1245 + } 1246 + 1247 + err = hci_devcd_append(hdev, skb_clone(skb, GFP_ATOMIC)); 1248 + if (err < 0) 1249 + goto free_skb; 1250 + 1251 + if (buf_len == 0) { 1252 + bt_dev_warn(hdev, "==== FW dump complete ==="); 1253 + clear_bit(BTNXPUART_FW_DUMP_IN_PROGRESS, &nxpdev->tx_state); 1254 + hci_devcd_complete(hdev); 1255 + nxp_set_ind_reset(hdev, NULL); 1256 + } 1257 + 1258 + free_skb: 1259 + kfree_skb(skb); 1260 + return 0; 1261 + } 1262 + 1263 + static int nxp_recv_acl_pkt(struct hci_dev *hdev, struct sk_buff *skb) 1264 + { 1265 + __u16 handle = __le16_to_cpu(hci_acl_hdr(skb)->handle); 1266 + 1267 + /* FW dump chunks are ACL packets with conn handle 0xfff */ 1268 + if ((handle & 0x0FFF) == 0xFFF) 1269 + return nxp_process_fw_dump(hdev, skb); 1270 + else 1271 + return hci_recv_frame(hdev, skb); 1228 1272 } 1229 1273 1230 1274 /* NXP protocol */ ··· 1355 1265 { 1356 1266 struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); 1357 1267 struct sk_buff *skb; 1358 - u8 *status; 1359 1268 u8 pcmd = 0; 1360 1269 1361 - if (test_bit(BTNXPUART_IR_IN_PROGRESS, &nxpdev->tx_state)) { 1270 + if (ind_reset_in_progress(nxpdev)) { 1362 1271 skb = nxp_drv_send_cmd(hdev, HCI_NXP_IND_RESET, 1, &pcmd); 1363 - if (IS_ERR(skb)) 1364 - return PTR_ERR(skb); 1365 - 1366 - status = skb_pull_data(skb, 1); 1367 - if (status) { 1368 - serdev_device_set_flow_control(nxpdev->serdev, false); 1369 - set_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); 1370 - } 1371 - kfree_skb(skb); 1272 + serdev_device_set_flow_control(nxpdev->serdev, false); 1273 + set_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); 1274 + /* HCI_NXP_IND_RESET command may not returns any response */ 1275 + if (!IS_ERR(skb)) 1276 + kfree_skb(skb); 1372 1277 } else if (nxpdev->current_baudrate != nxpdev->fw_init_baudrate) { 1373 1278 nxpdev->new_baudrate = nxpdev->fw_init_baudrate; 1374 1279 nxp_set_baudrate_cmd(hdev, NULL); ··· 1381 1296 return true; 1382 1297 1383 1298 return false; 1299 + } 1300 + 1301 + static void nxp_reset(struct hci_dev *hdev) 1302 + { 1303 + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); 1304 + 1305 + if (!ind_reset_in_progress(nxpdev) && !fw_dump_in_progress(nxpdev)) { 1306 + bt_dev_dbg(hdev, "CMD Timeout detected. Resetting."); 1307 + nxp_set_ind_reset(hdev, NULL); 1308 + } 1384 1309 } 1385 1310 1386 1311 static int btnxpuart_queue_skb(struct hci_dev *hdev, struct sk_buff *skb) ··· 1412 1317 struct psmode_cmd_payload ps_parm; 1413 1318 struct wakeup_cmd_payload wakeup_parm; 1414 1319 __le32 baudrate_parm; 1320 + 1321 + if (fw_dump_in_progress(nxpdev)) 1322 + return -EBUSY; 1415 1323 1416 1324 /* if vendor commands are received from user space (e.g. hcitool), update 1417 1325 * driver flags accordingly and ask driver to re-send the command to FW. ··· 1584 1486 } 1585 1487 1586 1488 static const struct h4_recv_pkt nxp_recv_pkts[] = { 1587 - { H4_RECV_ACL, .recv = hci_recv_frame }, 1489 + { H4_RECV_ACL, .recv = nxp_recv_acl_pkt }, 1588 1490 { H4_RECV_SCO, .recv = hci_recv_frame }, 1589 1491 { H4_RECV_EVENT, .recv = hci_recv_frame }, 1590 1492 { H4_RECV_ISO, .recv = hci_recv_frame }, ··· 1606 1508 if (IS_ERR(nxpdev->rx_skb)) { 1607 1509 int err = PTR_ERR(nxpdev->rx_skb); 1608 1510 /* Safe to ignore out-of-sync bootloader signatures */ 1609 - if (!is_fw_downloading(nxpdev)) 1511 + if (!is_fw_downloading(nxpdev) && 1512 + !ind_reset_in_progress(nxpdev)) 1610 1513 bt_dev_err(nxpdev->hdev, "Frame reassembly failed (%d)", err); 1611 1514 return count; 1612 1515 } 1613 - if (!is_fw_downloading(nxpdev)) 1516 + if (!is_fw_downloading(nxpdev) && 1517 + !ind_reset_in_progress(nxpdev)) 1614 1518 nxpdev->hdev->stat.byte_rx += count; 1615 1519 return count; 1616 1520 } ··· 1680 1580 hdev->hw_error = nxp_hw_err; 1681 1581 hdev->shutdown = nxp_shutdown; 1682 1582 hdev->wakeup = nxp_wakeup; 1583 + hdev->reset = nxp_reset; 1683 1584 SET_HCIDEV_DEV(hdev, &serdev->dev); 1684 1585 1685 1586 if (hci_register_dev(hdev) < 0) { ··· 1690 1589 1691 1590 if (ps_setup(hdev)) 1692 1591 goto probe_fail; 1592 + 1593 + hci_devcd_register(hdev, nxp_coredump, nxp_coredump_hdr, NULL); 1693 1594 1694 1595 return 0; 1695 1596 ··· 1744 1641 } 1745 1642 #endif 1746 1643 1644 + #ifdef CONFIG_DEV_COREDUMP 1645 + static void nxp_serdev_coredump(struct device *dev) 1646 + { 1647 + struct btnxpuart_dev *nxpdev = dev_get_drvdata(dev); 1648 + struct hci_dev *hdev = nxpdev->hdev; 1649 + 1650 + if (hdev->dump.coredump) 1651 + hdev->dump.coredump(hdev); 1652 + } 1653 + #endif 1654 + 1747 1655 static struct btnxpuart_data w8987_data __maybe_unused = { 1748 1656 .helper_fw_name = NULL, 1749 1657 .fw_name = FIRMWARE_W8987, ··· 1785 1671 .name = "btnxpuart", 1786 1672 .of_match_table = of_match_ptr(nxpuart_of_match_table), 1787 1673 .pm = &nxp_pm_ops, 1674 + #ifdef CONFIG_DEV_COREDUMP 1675 + .coredump = nxp_serdev_coredump, 1676 + #endif 1788 1677 }, 1789 1678 }; 1790 1679