diff options
Diffstat (limited to 'drivers/bluetooth/btnxpuart.c')
-rw-r--r-- | drivers/bluetooth/btnxpuart.c | 137 |
1 files changed, 116 insertions, 21 deletions
diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index b34623a69b8a..73a4a325c867 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -18,6 +18,8 @@ #include <linux/string_helpers.h> #include <linux/gpio/consumer.h> #include <linux/of_irq.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> @@ -73,7 +75,8 @@ #define FW_AUTH_ENC 0xc0 #define HCI_NXP_PRI_BAUDRATE 115200 -#define HCI_NXP_SEC_BAUDRATE 3000000 +#define HCI_NXP_SEC_BAUDRATE_3M 3000000 +#define HCI_NXP_SEC_BAUDRATE_4M 4000000 #define MAX_FW_FILE_NAME_LEN 50 @@ -201,12 +204,14 @@ struct btnxpuart_dev { u32 new_baudrate; u32 current_baudrate; u32 fw_init_baudrate; + u32 secondary_baudrate; enum bootloader_param_change timeout_changed; enum bootloader_param_change baudrate_changed; bool helper_downloaded; struct ps_data psdata; struct btnxpuart_data *nxp_data; + struct reset_control *pdn; }; #define NXP_V1_FW_REQ_PKT 0xa5 @@ -365,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; @@ -467,7 +481,7 @@ static void ps_work_func(struct work_struct *work) static void ps_timeout_func(struct timer_list *t) { - struct ps_data *data = from_timer(data, t, ps_timer); + struct ps_data *data = timer_container_of(data, t, ps_timer); struct hci_dev *hdev = data->hdev; struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); @@ -533,6 +547,8 @@ static int ps_setup(struct hci_dev *hdev) ps_host_wakeup_irq_handler, IRQF_ONESHOT | IRQF_TRIGGER_FALLING, dev_name(&serdev->dev), nxpdev); + if (ret) + bt_dev_info(hdev, "error setting wakeup IRQ handler, ignoring\n"); disable_irq(psdata->irq_handler); device_init_wakeup(&serdev->dev, true); } @@ -593,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); @@ -642,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); @@ -800,7 +818,10 @@ static bool nxp_fw_change_baudrate(struct hci_dev *hdev, u16 req_len) nxpdev->fw_v3_offset_correction += req_len; } else if (req_len == sizeof(uart_config)) { uart_config.clkdiv.address = __cpu_to_le32(clkdivaddr); - uart_config.clkdiv.value = __cpu_to_le32(0x00c00000); + if (nxpdev->new_baudrate == HCI_NXP_SEC_BAUDRATE_4M) + uart_config.clkdiv.value = __cpu_to_le32(0x01000000); + else + uart_config.clkdiv.value = __cpu_to_le32(0x00c00000); uart_config.uartdiv.address = __cpu_to_le32(uartdivaddr); uart_config.uartdiv.value = __cpu_to_le32(1); uart_config.mcr.address = __cpu_to_le32(uartmcraddr); @@ -964,12 +985,13 @@ static int nxp_recv_fw_req_v1(struct hci_dev *hdev, struct sk_buff *skb) goto free_skb; } if (nxpdev->baudrate_changed != changed) { + nxpdev->new_baudrate = nxpdev->secondary_baudrate; if (nxp_fw_change_baudrate(hdev, len)) { nxpdev->baudrate_changed = changed; serdev_device_set_baudrate(nxpdev->serdev, - HCI_NXP_SEC_BAUDRATE); + nxpdev->secondary_baudrate); serdev_device_set_flow_control(nxpdev->serdev, true); - nxpdev->current_baudrate = HCI_NXP_SEC_BAUDRATE; + nxpdev->current_baudrate = nxpdev->secondary_baudrate; } goto free_skb; } @@ -990,7 +1012,7 @@ static int nxp_recv_fw_req_v1(struct hci_dev *hdev, struct sk_buff *skb) nxpdev->helper_downloaded = true; serdev_device_wait_until_sent(nxpdev->serdev, 0); serdev_device_set_baudrate(nxpdev->serdev, - HCI_NXP_SEC_BAUDRATE); + HCI_NXP_SEC_BAUDRATE_3M); serdev_device_set_flow_control(nxpdev->serdev, true); } else { clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); @@ -1214,12 +1236,13 @@ static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb) } if (nxpdev->baudrate_changed != changed) { + nxpdev->new_baudrate = nxpdev->secondary_baudrate; if (nxp_fw_change_baudrate(hdev, len)) { nxpdev->baudrate_changed = cmd_sent; serdev_device_set_baudrate(nxpdev->serdev, - HCI_NXP_SEC_BAUDRATE); + nxpdev->secondary_baudrate); serdev_device_set_flow_control(nxpdev->serdev, true); - nxpdev->current_baudrate = HCI_NXP_SEC_BAUDRATE; + nxpdev->current_baudrate = nxpdev->secondary_baudrate; } goto free_skb; } @@ -1263,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); @@ -1321,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 @@ -1363,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); } @@ -1417,6 +1440,10 @@ static int nxp_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) static int nxp_setup(struct hci_dev *hdev) { struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct serdev_device *serdev = nxpdev->serdev; + char device_string[30]; + char event_string[50]; + char *envp[] = {device_string, event_string, NULL}; int err = 0; if (nxp_check_boot_sign(nxpdev)) { @@ -1429,6 +1456,12 @@ static int nxp_setup(struct hci_dev *hdev) clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); } + snprintf(device_string, 30, "BTNXPUART_DEV=%s", dev_name(&serdev->dev)); + snprintf(event_string, 50, "BTNXPUART_STATE=FW_READY"); + bt_dev_dbg(hdev, "==== Send uevent: %s:%s ===", device_string, + event_string); + kobject_uevent_env(&serdev->dev.kobj, KOBJ_CHANGE, envp); + serdev_device_set_baudrate(nxpdev->serdev, nxpdev->fw_init_baudrate); nxpdev->current_baudrate = nxpdev->fw_init_baudrate; @@ -1445,8 +1478,8 @@ static int nxp_post_init(struct hci_dev *hdev) struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); struct ps_data *psdata = &nxpdev->psdata; - if (nxpdev->current_baudrate != HCI_NXP_SEC_BAUDRATE) { - nxpdev->new_baudrate = HCI_NXP_SEC_BAUDRATE; + if (nxpdev->current_baudrate != nxpdev->secondary_baudrate) { + nxpdev->new_baudrate = nxpdev->secondary_baudrate; nxp_set_baudrate_cmd(hdev, NULL); } if (psdata->cur_h2c_wakeupmode != psdata->h2c_wakeupmode) @@ -1477,7 +1510,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 */ @@ -1743,11 +1782,41 @@ static const struct serdev_device_ops btnxpuart_client_ops = { .write_wakeup = btnxpuart_write_wakeup, }; +static void nxp_coredump_notify(struct hci_dev *hdev, int state) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct serdev_device *serdev = nxpdev->serdev; + char device_string[30]; + char event_string[50]; + char *envp[] = {device_string, event_string, NULL}; + + snprintf(device_string, 30, "BTNXPUART_DEV=%s", dev_name(&serdev->dev)); + switch (state) { + case HCI_DEVCOREDUMP_ACTIVE: + snprintf(event_string, 50, "BTNXPUART_STATE=FW_DUMP_ACTIVE"); + break; + case HCI_DEVCOREDUMP_DONE: + snprintf(event_string, 50, "BTNXPUART_STATE=FW_DUMP_DONE"); + break; + case HCI_DEVCOREDUMP_TIMEOUT: + snprintf(event_string, 50, "BTNXPUART_STATE=FW_DUMP_TIMEOUT"); + break; + default: + snprintf(event_string, 50, "BTNXPUART_STATE=FW_DUMP_STATE_%d", + state); + break; + } + bt_dev_dbg(hdev, "==== Send uevent: %s:%s ===", device_string, + event_string); + kobject_uevent_env(&serdev->dev.kobj, KOBJ_CHANGE, envp); +} + static int nxp_serdev_probe(struct serdev_device *serdev) { struct hci_dev *hdev; struct btnxpuart_dev *nxpdev; bdaddr_t ba = {0}; + int err; nxpdev = devm_kzalloc(&serdev->dev, sizeof(*nxpdev), GFP_KERNEL); if (!nxpdev) @@ -1771,10 +1840,31 @@ static int nxp_serdev_probe(struct serdev_device *serdev) if (!nxpdev->fw_init_baudrate) nxpdev->fw_init_baudrate = FW_INIT_BAUDRATE; + device_property_read_u32(&nxpdev->serdev->dev, "max-speed", + &nxpdev->secondary_baudrate); + if (!nxpdev->secondary_baudrate || + (nxpdev->secondary_baudrate != HCI_NXP_SEC_BAUDRATE_3M && + nxpdev->secondary_baudrate != HCI_NXP_SEC_BAUDRATE_4M)) { + if (nxpdev->secondary_baudrate) + dev_err(&serdev->dev, + "Invalid max-speed. Using default 3000000."); + nxpdev->secondary_baudrate = HCI_NXP_SEC_BAUDRATE_3M; + } + set_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); crc8_populate_msb(crc8_table, POLYNOMIAL8); + nxpdev->pdn = devm_reset_control_get_optional_shared(&serdev->dev, NULL); + if (IS_ERR(nxpdev->pdn)) + return PTR_ERR(nxpdev->pdn); + + err = devm_regulator_get_enable(&serdev->dev, "vcc"); + if (err) { + dev_err(&serdev->dev, "Failed to enable vcc regulator\n"); + return err; + } + /* Initialize and register HCI device */ hdev = hci_alloc_dev(); if (!hdev) { @@ -1782,6 +1872,8 @@ static int nxp_serdev_probe(struct serdev_device *serdev) return -ENOMEM; } + reset_control_deassert(nxpdev->pdn); + nxpdev->hdev = hdev; hdev->bus = HCI_UART; @@ -1805,7 +1897,7 @@ static int nxp_serdev_probe(struct serdev_device *serdev) "local-bd-address", (u8 *)&ba, sizeof(ba)); if (bacmp(&ba, BDADDR_ANY)) - set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks); + hci_set_quirk(hdev, HCI_QUIRK_USE_BDADDR_PROPERTY); if (hci_register_dev(hdev) < 0) { dev_err(&serdev->dev, "Can't register HCI device\n"); @@ -1815,11 +1907,13 @@ static int nxp_serdev_probe(struct serdev_device *serdev) if (ps_setup(hdev)) goto probe_fail; - hci_devcd_register(hdev, nxp_coredump, nxp_coredump_hdr, NULL); + hci_devcd_register(hdev, nxp_coredump, nxp_coredump_hdr, + nxp_coredump_notify); return 0; probe_fail: + reset_control_assert(nxpdev->pdn); hci_free_dev(hdev); return -ENODEV; } @@ -1847,6 +1941,7 @@ static void nxp_serdev_remove(struct serdev_device *serdev) ps_cleanup(nxpdev); hci_unregister_dev(hdev); + reset_control_assert(nxpdev->pdn); hci_free_dev(hdev); } |