summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/bluetooth/btnxpuart.c67
1 files changed, 59 insertions, 8 deletions
diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c
index 5f641466d695..32e22044b6b2 100644
--- a/drivers/bluetooth/btnxpuart.c
+++ b/drivers/bluetooth/btnxpuart.c
@@ -34,6 +34,7 @@
#define FIRMWARE_W9098 "nxp/uartuart9098_bt_v1.bin"
#define FIRMWARE_IW416 "nxp/uartiw416_bt_v0.bin"
#define FIRMWARE_IW612 "nxp/uartspi_n61x_v1.bin.se"
+#define FIRMWARE_HELPER "nxp/helper_uart_3000000.bin"
#define CHIP_ID_W9098 0x5c03
#define CHIP_ID_IW416 0x7201
@@ -123,7 +124,7 @@ struct psmode_cmd_payload {
} __packed;
struct btnxpuart_data {
- bool fw_download_3M_baudrate;
+ const char *helper_fw_name;
const char *fw_name;
};
@@ -150,6 +151,7 @@ struct btnxpuart_dev {
u32 fw_init_baudrate;
bool timeout_changed;
bool baudrate_changed;
+ bool helper_downloaded;
struct ps_data psdata;
struct btnxpuart_data *nxp_data;
@@ -168,6 +170,13 @@ struct btnxpuart_dev {
#define HDR_LEN 16
+#define NXP_RECV_CHIP_VER_V1 \
+ .type = NXP_V1_CHIP_VER_PKT, \
+ .hlen = 4, \
+ .loff = 0, \
+ .lsize = 0, \
+ .maxlen = 4
+
#define NXP_RECV_FW_REQ_V1 \
.type = NXP_V1_FW_REQ_PKT, \
.hlen = 4, \
@@ -194,6 +203,11 @@ struct v1_data_req {
__le16 len_comp;
} __packed;
+struct v1_start_ind {
+ __le16 chip_id;
+ __le16 chip_id_comp;
+} __packed;
+
struct v3_data_req {
__le16 len;
__le32 offset;
@@ -518,6 +532,7 @@ static int nxp_download_firmware(struct hci_dev *hdev)
nxpdev->fw_v3_offset_correction = 0;
nxpdev->baudrate_changed = false;
nxpdev->timeout_changed = false;
+ nxpdev->helper_downloaded = false;
serdev_device_set_baudrate(nxpdev->serdev, HCI_NXP_PRI_BAUDRATE);
serdev_device_set_flow_control(nxpdev->serdev, 0);
@@ -664,6 +679,29 @@ static int nxp_request_firmware(struct hci_dev *hdev, const char *fw_name)
}
/* for legacy chipsets with V1 bootloader */
+static int nxp_recv_chip_ver_v1(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ struct v1_start_ind *req;
+
+ req = (struct v1_start_ind *)skb_pull_data(skb, sizeof(struct v1_start_ind));
+ if (!req)
+ goto free_skb;
+
+ if ((req->chip_id ^ req->chip_id_comp) == 0xffff) {
+ nxpdev->fw_dnld_v1_offset = 0;
+ nxpdev->fw_v1_sent_bytes = 0;
+ nxpdev->fw_v1_expected_len = HDR_LEN;
+ release_firmware(nxpdev->fw);
+ memset(nxpdev->fw_name, 0, sizeof(nxpdev->fw_name));
+ nxp_send_ack(NXP_ACK_V1, hdev);
+ }
+
+free_skb:
+ kfree_skb(skb);
+ return 0;
+}
+
static int nxp_recv_fw_req_v1(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
@@ -685,7 +723,7 @@ static int nxp_recv_fw_req_v1(struct hci_dev *hdev, struct sk_buff *skb)
}
nxp_send_ack(NXP_ACK_V1, hdev);
- if (nxp_data->fw_download_3M_baudrate) {
+ if (!nxp_data->helper_fw_name) {
if (!nxpdev->timeout_changed) {
nxpdev->timeout_changed = nxp_fw_change_timeout(hdev, req->len);
goto free_skb;
@@ -702,14 +740,26 @@ static int nxp_recv_fw_req_v1(struct hci_dev *hdev, struct sk_buff *skb)
}
}
- if (nxp_request_firmware(hdev, nxp_data->fw_name))
- goto free_skb;
+ if (!nxp_data->helper_fw_name || nxpdev->helper_downloaded) {
+ if (nxp_request_firmware(hdev, nxp_data->fw_name))
+ goto free_skb;
+ } else if (nxp_data->helper_fw_name && !nxpdev->helper_downloaded) {
+ if (nxp_request_firmware(hdev, nxp_data->helper_fw_name))
+ goto free_skb;
+ }
requested_len = req->len;
if (requested_len == 0) {
bt_dev_dbg(hdev, "FW Downloaded Successfully: %zu bytes", nxpdev->fw->size);
- clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state);
- wake_up_interruptible(&nxpdev->fw_dnld_done_wait_q);
+ if (nxp_data->helper_fw_name && !nxpdev->helper_downloaded) {
+ nxpdev->helper_downloaded = true;
+ serdev_device_wait_until_sent(nxpdev->serdev, 0);
+ serdev_device_set_baudrate(nxpdev->serdev, HCI_NXP_SEC_BAUDRATE);
+ serdev_device_set_flow_control(nxpdev->serdev, 1);
+ } else {
+ clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state);
+ wake_up_interruptible(&nxpdev->fw_dnld_done_wait_q);
+ }
goto free_skb;
}
if (requested_len & 0x01) {
@@ -1142,6 +1192,7 @@ static const struct h4_recv_pkt nxp_recv_pkts[] = {
{ H4_RECV_ACL, .recv = hci_recv_frame },
{ H4_RECV_SCO, .recv = hci_recv_frame },
{ H4_RECV_EVENT, .recv = hci_recv_frame },
+ { NXP_RECV_CHIP_VER_V1, .recv = nxp_recv_chip_ver_v1 },
{ NXP_RECV_FW_REQ_V1, .recv = nxp_recv_fw_req_v1 },
{ NXP_RECV_CHIP_VER_V3, .recv = nxp_recv_chip_ver_v3 },
{ NXP_RECV_FW_REQ_V3, .recv = nxp_recv_fw_req_v3 },
@@ -1265,12 +1316,12 @@ static void nxp_serdev_remove(struct serdev_device *serdev)
}
static struct btnxpuart_data w8987_data = {
- .fw_download_3M_baudrate = true,
+ .helper_fw_name = NULL,
.fw_name = FIRMWARE_W8987,
};
static struct btnxpuart_data w8997_data = {
- .fw_download_3M_baudrate = false,
+ .helper_fw_name = FIRMWARE_HELPER,
.fw_name = FIRMWARE_W8997,
};