145051539SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
283e81961SBen Young Tae Kim /*
383e81961SBen Young Tae Kim * Bluetooth supports for Qualcomm Atheros chips
483e81961SBen Young Tae Kim *
583e81961SBen Young Tae Kim * Copyright (c) 2015 The Linux Foundation. All rights reserved.
683e81961SBen Young Tae Kim */
783e81961SBen Young Tae Kim #include <linux/module.h>
883e81961SBen Young Tae Kim #include <linux/firmware.h>
9b6459415SJakub Kicinski #include <linux/vmalloc.h>
1083e81961SBen Young Tae Kim
1183e81961SBen Young Tae Kim #include <net/bluetooth/bluetooth.h>
1283e81961SBen Young Tae Kim #include <net/bluetooth/hci_core.h>
1383e81961SBen Young Tae Kim
1483e81961SBen Young Tae Kim #include "btqca.h"
1583e81961SBen Young Tae Kim
1683e81961SBen Young Tae Kim #define VERSION "0.1"
1783e81961SBen Young Tae Kim
qca_read_soc_version(struct hci_dev * hdev,struct qca_btsoc_version * ver,enum qca_btsoc_type soc_type)18059924fdSVenkata Lakshmi Narayana Gubba int qca_read_soc_version(struct hci_dev *hdev, struct qca_btsoc_version *ver,
197d250a06SBalakrishna Godavarthi enum qca_btsoc_type soc_type)
2083e81961SBen Young Tae Kim {
2183e81961SBen Young Tae Kim struct sk_buff *skb;
2283e81961SBen Young Tae Kim struct edl_event_hdr *edl;
2383e81961SBen Young Tae Kim char cmd;
2483e81961SBen Young Tae Kim int err = 0;
257d250a06SBalakrishna Godavarthi u8 event_type = HCI_EV_VENDOR;
267d250a06SBalakrishna Godavarthi u8 rlen = sizeof(*edl) + sizeof(*ver);
277d250a06SBalakrishna Godavarthi u8 rtype = EDL_APP_VER_RES_EVT;
2883e81961SBen Young Tae Kim
29ba493d4fSBalakrishna Godavarthi bt_dev_dbg(hdev, "QCA Version Request");
3083e81961SBen Young Tae Kim
317d250a06SBalakrishna Godavarthi /* Unlike other SoC's sending version command response as payload to
327d250a06SBalakrishna Godavarthi * VSE event. WCN3991 sends version command response as a payload to
337d250a06SBalakrishna Godavarthi * command complete event.
347d250a06SBalakrishna Godavarthi */
35e5d6468fSRocky Liao if (soc_type >= QCA_WCN3991) {
367d250a06SBalakrishna Godavarthi event_type = 0;
377d250a06SBalakrishna Godavarthi rlen += 1;
387d250a06SBalakrishna Godavarthi rtype = EDL_PATCH_VER_REQ_CMD;
397d250a06SBalakrishna Godavarthi }
407d250a06SBalakrishna Godavarthi
4183e81961SBen Young Tae Kim cmd = EDL_PATCH_VER_REQ_CMD;
4283e81961SBen Young Tae Kim skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, EDL_PATCH_CMD_LEN,
437d250a06SBalakrishna Godavarthi &cmd, event_type, HCI_INIT_TIMEOUT);
4483e81961SBen Young Tae Kim if (IS_ERR(skb)) {
4583e81961SBen Young Tae Kim err = PTR_ERR(skb);
46ba493d4fSBalakrishna Godavarthi bt_dev_err(hdev, "Reading QCA version information failed (%d)",
4783e81961SBen Young Tae Kim err);
4883e81961SBen Young Tae Kim return err;
4983e81961SBen Young Tae Kim }
5083e81961SBen Young Tae Kim
517d250a06SBalakrishna Godavarthi if (skb->len != rlen) {
52ba493d4fSBalakrishna Godavarthi bt_dev_err(hdev, "QCA Version size mismatch len %d", skb->len);
5383e81961SBen Young Tae Kim err = -EILSEQ;
5483e81961SBen Young Tae Kim goto out;
5583e81961SBen Young Tae Kim }
5683e81961SBen Young Tae Kim
5783e81961SBen Young Tae Kim edl = (struct edl_event_hdr *)(skb->data);
580676cab4SColin Ian King if (!edl) {
59ba493d4fSBalakrishna Godavarthi bt_dev_err(hdev, "QCA TLV with no header");
6083e81961SBen Young Tae Kim err = -EILSEQ;
6183e81961SBen Young Tae Kim goto out;
6283e81961SBen Young Tae Kim }
6383e81961SBen Young Tae Kim
6483e81961SBen Young Tae Kim if (edl->cresp != EDL_CMD_REQ_RES_EVT ||
657d250a06SBalakrishna Godavarthi edl->rtype != rtype) {
66ba493d4fSBalakrishna Godavarthi bt_dev_err(hdev, "QCA Wrong packet received %d %d", edl->cresp,
67ba493d4fSBalakrishna Godavarthi edl->rtype);
6883e81961SBen Young Tae Kim err = -EIO;
6983e81961SBen Young Tae Kim goto out;
7083e81961SBen Young Tae Kim }
7183e81961SBen Young Tae Kim
72e5d6468fSRocky Liao if (soc_type >= QCA_WCN3991)
73059924fdSVenkata Lakshmi Narayana Gubba memcpy(ver, edl->data + 1, sizeof(*ver));
74059924fdSVenkata Lakshmi Narayana Gubba else
75059924fdSVenkata Lakshmi Narayana Gubba memcpy(ver, &edl->data, sizeof(*ver));
7683e81961SBen Young Tae Kim
774942857bSZijun Hu bt_dev_info(hdev, "QCA Product ID :0x%08x",
784942857bSZijun Hu le32_to_cpu(ver->product_id));
794942857bSZijun Hu bt_dev_info(hdev, "QCA SOC Version :0x%08x",
804942857bSZijun Hu le32_to_cpu(ver->soc_id));
814942857bSZijun Hu bt_dev_info(hdev, "QCA ROM Version :0x%08x",
824942857bSZijun Hu le16_to_cpu(ver->rom_ver));
834942857bSZijun Hu bt_dev_info(hdev, "QCA Patch Version:0x%08x",
844942857bSZijun Hu le16_to_cpu(ver->patch_ver));
8583e81961SBen Young Tae Kim
86059924fdSVenkata Lakshmi Narayana Gubba if (ver->soc_id == 0 || ver->rom_ver == 0)
87aadebac4SBalakrishna Godavarthi err = -EILSEQ;
8883e81961SBen Young Tae Kim
8983e81961SBen Young Tae Kim out:
9083e81961SBen Young Tae Kim kfree_skb(skb);
91aadebac4SBalakrishna Godavarthi if (err)
92aadebac4SBalakrishna Godavarthi bt_dev_err(hdev, "QCA Failed to get version (%d)", err);
9383e81961SBen Young Tae Kim
9483e81961SBen Young Tae Kim return err;
9583e81961SBen Young Tae Kim }
96ba493d4fSBalakrishna Godavarthi EXPORT_SYMBOL_GPL(qca_read_soc_version);
9783e81961SBen Young Tae Kim
qca_read_fw_build_info(struct hci_dev * hdev)98c0187b0bSVenkata Lakshmi Narayana Gubba static int qca_read_fw_build_info(struct hci_dev *hdev)
99c0187b0bSVenkata Lakshmi Narayana Gubba {
100c0187b0bSVenkata Lakshmi Narayana Gubba struct sk_buff *skb;
101c0187b0bSVenkata Lakshmi Narayana Gubba struct edl_event_hdr *edl;
1026b63e0efSJohan Hovold char *build_label;
1036b63e0efSJohan Hovold char cmd;
104c0187b0bSVenkata Lakshmi Narayana Gubba int build_lbl_len, err = 0;
105c0187b0bSVenkata Lakshmi Narayana Gubba
106c0187b0bSVenkata Lakshmi Narayana Gubba bt_dev_dbg(hdev, "QCA read fw build info");
107c0187b0bSVenkata Lakshmi Narayana Gubba
108c0187b0bSVenkata Lakshmi Narayana Gubba cmd = EDL_GET_BUILD_INFO_CMD;
109c0187b0bSVenkata Lakshmi Narayana Gubba skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, EDL_PATCH_CMD_LEN,
110c0187b0bSVenkata Lakshmi Narayana Gubba &cmd, 0, HCI_INIT_TIMEOUT);
111c0187b0bSVenkata Lakshmi Narayana Gubba if (IS_ERR(skb)) {
112c0187b0bSVenkata Lakshmi Narayana Gubba err = PTR_ERR(skb);
113c0187b0bSVenkata Lakshmi Narayana Gubba bt_dev_err(hdev, "Reading QCA fw build info failed (%d)",
114c0187b0bSVenkata Lakshmi Narayana Gubba err);
115c0187b0bSVenkata Lakshmi Narayana Gubba return err;
116c0187b0bSVenkata Lakshmi Narayana Gubba }
117c0187b0bSVenkata Lakshmi Narayana Gubba
1186b63e0efSJohan Hovold if (skb->len < sizeof(*edl)) {
1196b63e0efSJohan Hovold err = -EILSEQ;
1206b63e0efSJohan Hovold goto out;
1216b63e0efSJohan Hovold }
1226b63e0efSJohan Hovold
123c0187b0bSVenkata Lakshmi Narayana Gubba edl = (struct edl_event_hdr *)(skb->data);
124c0187b0bSVenkata Lakshmi Narayana Gubba if (!edl) {
125c0187b0bSVenkata Lakshmi Narayana Gubba bt_dev_err(hdev, "QCA read fw build info with no header");
126c0187b0bSVenkata Lakshmi Narayana Gubba err = -EILSEQ;
127c0187b0bSVenkata Lakshmi Narayana Gubba goto out;
128c0187b0bSVenkata Lakshmi Narayana Gubba }
129c0187b0bSVenkata Lakshmi Narayana Gubba
130c0187b0bSVenkata Lakshmi Narayana Gubba if (edl->cresp != EDL_CMD_REQ_RES_EVT ||
131c0187b0bSVenkata Lakshmi Narayana Gubba edl->rtype != EDL_GET_BUILD_INFO_CMD) {
132c0187b0bSVenkata Lakshmi Narayana Gubba bt_dev_err(hdev, "QCA Wrong packet received %d %d", edl->cresp,
133c0187b0bSVenkata Lakshmi Narayana Gubba edl->rtype);
134c0187b0bSVenkata Lakshmi Narayana Gubba err = -EIO;
135c0187b0bSVenkata Lakshmi Narayana Gubba goto out;
136c0187b0bSVenkata Lakshmi Narayana Gubba }
137c0187b0bSVenkata Lakshmi Narayana Gubba
1386b63e0efSJohan Hovold if (skb->len < sizeof(*edl) + 1) {
1396b63e0efSJohan Hovold err = -EILSEQ;
1406b63e0efSJohan Hovold goto out;
141c0187b0bSVenkata Lakshmi Narayana Gubba }
142c0187b0bSVenkata Lakshmi Narayana Gubba
1436b63e0efSJohan Hovold build_lbl_len = edl->data[0];
1446b63e0efSJohan Hovold
1456b63e0efSJohan Hovold if (skb->len < sizeof(*edl) + 1 + build_lbl_len) {
1466b63e0efSJohan Hovold err = -EILSEQ;
1476b63e0efSJohan Hovold goto out;
1486b63e0efSJohan Hovold }
1496b63e0efSJohan Hovold
1506b63e0efSJohan Hovold build_label = kstrndup(&edl->data[1], build_lbl_len, GFP_KERNEL);
151dfde465dSDan Carpenter if (!build_label) {
152dfde465dSDan Carpenter err = -ENOMEM;
1536b63e0efSJohan Hovold goto out;
154dfde465dSDan Carpenter }
1556b63e0efSJohan Hovold
156c0187b0bSVenkata Lakshmi Narayana Gubba hci_set_fw_info(hdev, "%s", build_label);
157c0187b0bSVenkata Lakshmi Narayana Gubba
1586b63e0efSJohan Hovold kfree(build_label);
159c0187b0bSVenkata Lakshmi Narayana Gubba out:
160c0187b0bSVenkata Lakshmi Narayana Gubba kfree_skb(skb);
161c0187b0bSVenkata Lakshmi Narayana Gubba return err;
162c0187b0bSVenkata Lakshmi Narayana Gubba }
163c0187b0bSVenkata Lakshmi Narayana Gubba
qca_send_patch_config_cmd(struct hci_dev * hdev)1644fac8a7aSSai Teja Aluvala static int qca_send_patch_config_cmd(struct hci_dev *hdev)
1654fac8a7aSSai Teja Aluvala {
1664fac8a7aSSai Teja Aluvala const u8 cmd[] = { EDL_PATCH_CONFIG_CMD, 0x01, 0, 0, 0 };
1674fac8a7aSSai Teja Aluvala struct sk_buff *skb;
1684fac8a7aSSai Teja Aluvala struct edl_event_hdr *edl;
1694fac8a7aSSai Teja Aluvala int err;
1704fac8a7aSSai Teja Aluvala
1714fac8a7aSSai Teja Aluvala bt_dev_dbg(hdev, "QCA Patch config");
1724fac8a7aSSai Teja Aluvala
1734fac8a7aSSai Teja Aluvala skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, sizeof(cmd),
174fb3e827bSZijun Hu cmd, 0, HCI_INIT_TIMEOUT);
1754fac8a7aSSai Teja Aluvala if (IS_ERR(skb)) {
1764fac8a7aSSai Teja Aluvala err = PTR_ERR(skb);
1774fac8a7aSSai Teja Aluvala bt_dev_err(hdev, "Sending QCA Patch config failed (%d)", err);
1784fac8a7aSSai Teja Aluvala return err;
1794fac8a7aSSai Teja Aluvala }
1804fac8a7aSSai Teja Aluvala
1814fac8a7aSSai Teja Aluvala if (skb->len != 2) {
1824fac8a7aSSai Teja Aluvala bt_dev_err(hdev, "QCA Patch config cmd size mismatch len %d", skb->len);
1834fac8a7aSSai Teja Aluvala err = -EILSEQ;
1844fac8a7aSSai Teja Aluvala goto out;
1854fac8a7aSSai Teja Aluvala }
1864fac8a7aSSai Teja Aluvala
1874fac8a7aSSai Teja Aluvala edl = (struct edl_event_hdr *)(skb->data);
1884fac8a7aSSai Teja Aluvala if (!edl) {
1894fac8a7aSSai Teja Aluvala bt_dev_err(hdev, "QCA Patch config with no header");
1904fac8a7aSSai Teja Aluvala err = -EILSEQ;
1914fac8a7aSSai Teja Aluvala goto out;
1924fac8a7aSSai Teja Aluvala }
1934fac8a7aSSai Teja Aluvala
1944fac8a7aSSai Teja Aluvala if (edl->cresp != EDL_PATCH_CONFIG_RES_EVT || edl->rtype != EDL_PATCH_CONFIG_CMD) {
1954fac8a7aSSai Teja Aluvala bt_dev_err(hdev, "QCA Wrong packet received %d %d", edl->cresp,
1964fac8a7aSSai Teja Aluvala edl->rtype);
1974fac8a7aSSai Teja Aluvala err = -EIO;
1984fac8a7aSSai Teja Aluvala goto out;
1994fac8a7aSSai Teja Aluvala }
2004fac8a7aSSai Teja Aluvala
2014fac8a7aSSai Teja Aluvala err = 0;
2024fac8a7aSSai Teja Aluvala
2034fac8a7aSSai Teja Aluvala out:
2044fac8a7aSSai Teja Aluvala kfree_skb(skb);
2054fac8a7aSSai Teja Aluvala return err;
2064fac8a7aSSai Teja Aluvala }
2074fac8a7aSSai Teja Aluvala
qca_send_reset(struct hci_dev * hdev)208ba493d4fSBalakrishna Godavarthi static int qca_send_reset(struct hci_dev *hdev)
20983e81961SBen Young Tae Kim {
21083e81961SBen Young Tae Kim struct sk_buff *skb;
21183e81961SBen Young Tae Kim int err;
21283e81961SBen Young Tae Kim
213ba493d4fSBalakrishna Godavarthi bt_dev_dbg(hdev, "QCA HCI_RESET");
21483e81961SBen Young Tae Kim
21583e81961SBen Young Tae Kim skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
21683e81961SBen Young Tae Kim if (IS_ERR(skb)) {
21783e81961SBen Young Tae Kim err = PTR_ERR(skb);
218ba493d4fSBalakrishna Godavarthi bt_dev_err(hdev, "QCA Reset failed (%d)", err);
21983e81961SBen Young Tae Kim return err;
22083e81961SBen Young Tae Kim }
22183e81961SBen Young Tae Kim
22283e81961SBen Young Tae Kim kfree_skb(skb);
22383e81961SBen Young Tae Kim
22483e81961SBen Young Tae Kim return 0;
22583e81961SBen Young Tae Kim }
22683e81961SBen Young Tae Kim
qca_read_fw_board_id(struct hci_dev * hdev,u16 * bid)227a381ee26STim Jiang static int qca_read_fw_board_id(struct hci_dev *hdev, u16 *bid)
228a381ee26STim Jiang {
229a381ee26STim Jiang u8 cmd;
230a381ee26STim Jiang struct sk_buff *skb;
231a381ee26STim Jiang struct edl_event_hdr *edl;
232a381ee26STim Jiang int err = 0;
233a381ee26STim Jiang
234a381ee26STim Jiang cmd = EDL_GET_BID_REQ_CMD;
235a381ee26STim Jiang skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, EDL_PATCH_CMD_LEN,
236a381ee26STim Jiang &cmd, 0, HCI_INIT_TIMEOUT);
237a381ee26STim Jiang if (IS_ERR(skb)) {
238a381ee26STim Jiang err = PTR_ERR(skb);
239a381ee26STim Jiang bt_dev_err(hdev, "Reading QCA board ID failed (%d)", err);
240a381ee26STim Jiang return err;
241a381ee26STim Jiang }
242a381ee26STim Jiang
243a381ee26STim Jiang edl = skb_pull_data(skb, sizeof(*edl));
244a381ee26STim Jiang if (!edl) {
245a381ee26STim Jiang bt_dev_err(hdev, "QCA read board ID with no header");
246a381ee26STim Jiang err = -EILSEQ;
247a381ee26STim Jiang goto out;
248a381ee26STim Jiang }
249a381ee26STim Jiang
250a381ee26STim Jiang if (edl->cresp != EDL_CMD_REQ_RES_EVT ||
251a381ee26STim Jiang edl->rtype != EDL_GET_BID_REQ_CMD) {
252a381ee26STim Jiang bt_dev_err(hdev, "QCA Wrong packet: %d %d", edl->cresp, edl->rtype);
253a381ee26STim Jiang err = -EIO;
254a381ee26STim Jiang goto out;
255a381ee26STim Jiang }
256a381ee26STim Jiang
257ba307abeSJohan Hovold if (skb->len < 3) {
258ba307abeSJohan Hovold err = -EILSEQ;
259ba307abeSJohan Hovold goto out;
260ba307abeSJohan Hovold }
261ba307abeSJohan Hovold
262a381ee26STim Jiang *bid = (edl->data[1] << 8) + edl->data[2];
263a381ee26STim Jiang bt_dev_dbg(hdev, "%s: bid = %x", __func__, *bid);
264a381ee26STim Jiang
265a381ee26STim Jiang out:
266a381ee26STim Jiang kfree_skb(skb);
267a381ee26STim Jiang return err;
268a381ee26STim Jiang }
269a381ee26STim Jiang
qca_send_pre_shutdown_cmd(struct hci_dev * hdev)270a2780889SHarish Bandi int qca_send_pre_shutdown_cmd(struct hci_dev *hdev)
271a2780889SHarish Bandi {
272a2780889SHarish Bandi struct sk_buff *skb;
273a2780889SHarish Bandi int err;
274a2780889SHarish Bandi
275a2780889SHarish Bandi bt_dev_dbg(hdev, "QCA pre shutdown cmd");
276a2780889SHarish Bandi
277010376abSHarish Bandi skb = __hci_cmd_sync_ev(hdev, QCA_PRE_SHUTDOWN_CMD, 0,
278010376abSHarish Bandi NULL, HCI_EV_CMD_COMPLETE, HCI_INIT_TIMEOUT);
279010376abSHarish Bandi
280a2780889SHarish Bandi if (IS_ERR(skb)) {
281a2780889SHarish Bandi err = PTR_ERR(skb);
282a2780889SHarish Bandi bt_dev_err(hdev, "QCA preshutdown_cmd failed (%d)", err);
283a2780889SHarish Bandi return err;
284a2780889SHarish Bandi }
285a2780889SHarish Bandi
286a2780889SHarish Bandi kfree_skb(skb);
287a2780889SHarish Bandi
288a2780889SHarish Bandi return 0;
289a2780889SHarish Bandi }
290a2780889SHarish Bandi EXPORT_SYMBOL_GPL(qca_send_pre_shutdown_cmd);
291a2780889SHarish Bandi
qca_filename_has_extension(const char * filename)292ae2d111cSCheng Jiang static bool qca_filename_has_extension(const char *filename)
293ae2d111cSCheng Jiang {
294ae2d111cSCheng Jiang const char *suffix = strrchr(filename, '.');
295ae2d111cSCheng Jiang
296ae2d111cSCheng Jiang /* File extensions require a dot, but not as the first or last character */
297ae2d111cSCheng Jiang if (!suffix || suffix == filename || *(suffix + 1) == '\0')
298ae2d111cSCheng Jiang return 0;
299ae2d111cSCheng Jiang
300ae2d111cSCheng Jiang /* Avoid matching directories with names that look like files with extensions */
301ae2d111cSCheng Jiang return !strchr(suffix, '/');
302ae2d111cSCheng Jiang }
303ae2d111cSCheng Jiang
qca_get_alt_nvm_file(char * filename,size_t max_size)304ae2d111cSCheng Jiang static bool qca_get_alt_nvm_file(char *filename, size_t max_size)
305ae2d111cSCheng Jiang {
306ae2d111cSCheng Jiang char fwname[64];
307ae2d111cSCheng Jiang const char *suffix;
308ae2d111cSCheng Jiang
309ae2d111cSCheng Jiang /* nvm file name has an extension, replace with .bin */
310ae2d111cSCheng Jiang if (qca_filename_has_extension(filename)) {
311ae2d111cSCheng Jiang suffix = strrchr(filename, '.');
312ae2d111cSCheng Jiang strscpy(fwname, filename, suffix - filename + 1);
313ae2d111cSCheng Jiang snprintf(fwname + (suffix - filename),
314ae2d111cSCheng Jiang sizeof(fwname) - (suffix - filename), ".bin");
315ae2d111cSCheng Jiang /* If nvm file is already the default one, return false to skip the retry. */
316ae2d111cSCheng Jiang if (strcmp(fwname, filename) == 0)
317ae2d111cSCheng Jiang return false;
318ae2d111cSCheng Jiang
319ae2d111cSCheng Jiang snprintf(filename, max_size, "%s", fwname);
320ae2d111cSCheng Jiang return true;
321ae2d111cSCheng Jiang }
322ae2d111cSCheng Jiang return false;
323ae2d111cSCheng Jiang }
324ae2d111cSCheng Jiang
qca_tlv_check_data(struct hci_dev * hdev,struct qca_fw_config * config,u8 * fw_data,size_t fw_size,enum qca_btsoc_type soc_type)325427281f9SJohan Hovold static int qca_tlv_check_data(struct hci_dev *hdev,
326ecf6b2d9SVenkata Lakshmi Narayana Gubba struct qca_fw_config *config,
327427281f9SJohan Hovold u8 *fw_data, size_t fw_size,
328427281f9SJohan Hovold enum qca_btsoc_type soc_type)
32983e81961SBen Young Tae Kim {
33083e81961SBen Young Tae Kim const u8 *data;
33183e81961SBen Young Tae Kim u32 type_len;
33283e81961SBen Young Tae Kim u16 tag_id, tag_len;
33383e81961SBen Young Tae Kim int idx, length;
33483e81961SBen Young Tae Kim struct tlv_type_hdr *tlv;
33583e81961SBen Young Tae Kim struct tlv_type_patch *tlv_patch;
33683e81961SBen Young Tae Kim struct tlv_type_nvm *tlv_nvm;
337b6388254SRocky Liao uint8_t nvm_baud_rate = config->user_baud_rate;
338c3a38d10SJohan Hovold u8 type;
33983e81961SBen Young Tae Kim
340e303d124SBalakrishna Godavarthi config->dnld_mode = QCA_SKIP_EVT_NONE;
341e303d124SBalakrishna Godavarthi config->dnld_type = QCA_SKIP_EVT_NONE;
3426e03126aSLoic Poulain
34383e81961SBen Young Tae Kim switch (config->type) {
344ecf6b2d9SVenkata Lakshmi Narayana Gubba case ELF_TYPE_PATCH:
345427281f9SJohan Hovold if (fw_size < 7)
346427281f9SJohan Hovold return -EINVAL;
347427281f9SJohan Hovold
348ecf6b2d9SVenkata Lakshmi Narayana Gubba config->dnld_mode = QCA_SKIP_EVT_VSE_CC;
349ecf6b2d9SVenkata Lakshmi Narayana Gubba config->dnld_type = QCA_SKIP_EVT_VSE_CC;
350ecf6b2d9SVenkata Lakshmi Narayana Gubba
351ecf6b2d9SVenkata Lakshmi Narayana Gubba bt_dev_dbg(hdev, "File Class : 0x%x", fw_data[4]);
352ecf6b2d9SVenkata Lakshmi Narayana Gubba bt_dev_dbg(hdev, "Data Encoding : 0x%x", fw_data[5]);
353ecf6b2d9SVenkata Lakshmi Narayana Gubba bt_dev_dbg(hdev, "File version : 0x%x", fw_data[6]);
354ecf6b2d9SVenkata Lakshmi Narayana Gubba break;
35583e81961SBen Young Tae Kim case TLV_TYPE_PATCH:
356427281f9SJohan Hovold if (fw_size < sizeof(struct tlv_type_hdr) + sizeof(struct tlv_type_patch))
357427281f9SJohan Hovold return -EINVAL;
358427281f9SJohan Hovold
359ecf6b2d9SVenkata Lakshmi Narayana Gubba tlv = (struct tlv_type_hdr *)fw_data;
360ecf6b2d9SVenkata Lakshmi Narayana Gubba type_len = le32_to_cpu(tlv->type_len);
36183e81961SBen Young Tae Kim tlv_patch = (struct tlv_type_patch *)tlv->data;
3626e03126aSLoic Poulain
3636e03126aSLoic Poulain /* For Rome version 1.1 to 3.1, all segment commands
3646e03126aSLoic Poulain * are acked by a vendor specific event (VSE).
3656e03126aSLoic Poulain * For Rome >= 3.2, the download mode field indicates
3666e03126aSLoic Poulain * if VSE is skipped by the controller.
3676e03126aSLoic Poulain * In case VSE is skipped, only the last segment is acked.
3686e03126aSLoic Poulain */
3696e03126aSLoic Poulain config->dnld_mode = tlv_patch->download_mode;
37032646db8SBalakrishna Godavarthi config->dnld_type = config->dnld_mode;
3716e03126aSLoic Poulain
372ecf6b2d9SVenkata Lakshmi Narayana Gubba BT_DBG("TLV Type\t\t : 0x%x", type_len & 0x000000ff);
3736e03126aSLoic Poulain BT_DBG("Total Length : %d bytes",
37483e81961SBen Young Tae Kim le32_to_cpu(tlv_patch->total_size));
3756e03126aSLoic Poulain BT_DBG("Patch Data Length : %d bytes",
37683e81961SBen Young Tae Kim le32_to_cpu(tlv_patch->data_length));
37783e81961SBen Young Tae Kim BT_DBG("Signing Format Version : 0x%x",
37883e81961SBen Young Tae Kim tlv_patch->format_version);
3796e03126aSLoic Poulain BT_DBG("Signature Algorithm : 0x%x",
38083e81961SBen Young Tae Kim tlv_patch->signature);
3816e03126aSLoic Poulain BT_DBG("Download mode : 0x%x",
3826e03126aSLoic Poulain tlv_patch->download_mode);
3836e03126aSLoic Poulain BT_DBG("Reserved : 0x%x",
3846e03126aSLoic Poulain tlv_patch->reserved1);
3856e03126aSLoic Poulain BT_DBG("Product ID : 0x%04x",
38683e81961SBen Young Tae Kim le16_to_cpu(tlv_patch->product_id));
3876e03126aSLoic Poulain BT_DBG("Rom Build Version : 0x%04x",
38883e81961SBen Young Tae Kim le16_to_cpu(tlv_patch->rom_build));
3896e03126aSLoic Poulain BT_DBG("Patch Version : 0x%04x",
39083e81961SBen Young Tae Kim le16_to_cpu(tlv_patch->patch_version));
3916e03126aSLoic Poulain BT_DBG("Reserved : 0x%x",
39283e81961SBen Young Tae Kim le16_to_cpu(tlv_patch->reserved2));
3936e03126aSLoic Poulain BT_DBG("Patch Entry Address : 0x%x",
39483e81961SBen Young Tae Kim le32_to_cpu(tlv_patch->entry));
39583e81961SBen Young Tae Kim break;
39683e81961SBen Young Tae Kim
39783e81961SBen Young Tae Kim case TLV_TYPE_NVM:
398427281f9SJohan Hovold if (fw_size < sizeof(struct tlv_type_hdr))
399427281f9SJohan Hovold return -EINVAL;
400427281f9SJohan Hovold
401ecf6b2d9SVenkata Lakshmi Narayana Gubba tlv = (struct tlv_type_hdr *)fw_data;
402ecf6b2d9SVenkata Lakshmi Narayana Gubba
403ecf6b2d9SVenkata Lakshmi Narayana Gubba type_len = le32_to_cpu(tlv->type_len);
404c3a38d10SJohan Hovold length = type_len >> 8;
405c3a38d10SJohan Hovold type = type_len & 0xff;
406ecf6b2d9SVenkata Lakshmi Narayana Gubba
407c3a38d10SJohan Hovold /* Some NVM files have more than one set of tags, only parse
408c3a38d10SJohan Hovold * the first set when it has type 2 for now. When there is
409c3a38d10SJohan Hovold * more than one set there is an enclosing header of type 4.
410c3a38d10SJohan Hovold */
411c3a38d10SJohan Hovold if (type == 4) {
412c3a38d10SJohan Hovold if (fw_size < 2 * sizeof(struct tlv_type_hdr))
413c3a38d10SJohan Hovold return -EINVAL;
414c3a38d10SJohan Hovold
415c3a38d10SJohan Hovold tlv++;
416c3a38d10SJohan Hovold
417c3a38d10SJohan Hovold type_len = le32_to_cpu(tlv->type_len);
418c3a38d10SJohan Hovold length = type_len >> 8;
419c3a38d10SJohan Hovold type = type_len & 0xff;
420c3a38d10SJohan Hovold }
421c3a38d10SJohan Hovold
422c3a38d10SJohan Hovold BT_DBG("TLV Type\t\t : 0x%x", type);
423ecf6b2d9SVenkata Lakshmi Narayana Gubba BT_DBG("Length\t\t : %d bytes", length);
424ecf6b2d9SVenkata Lakshmi Narayana Gubba
425c3a38d10SJohan Hovold if (type != 2)
426c3a38d10SJohan Hovold break;
427c3a38d10SJohan Hovold
428427281f9SJohan Hovold if (fw_size < length + (tlv->data - fw_data))
429427281f9SJohan Hovold return -EINVAL;
430427281f9SJohan Hovold
43183e81961SBen Young Tae Kim idx = 0;
43283e81961SBen Young Tae Kim data = tlv->data;
433427281f9SJohan Hovold while (idx < length - sizeof(struct tlv_type_nvm)) {
43483e81961SBen Young Tae Kim tlv_nvm = (struct tlv_type_nvm *)(data + idx);
43583e81961SBen Young Tae Kim
43683e81961SBen Young Tae Kim tag_id = le16_to_cpu(tlv_nvm->tag_id);
43783e81961SBen Young Tae Kim tag_len = le16_to_cpu(tlv_nvm->tag_len);
43883e81961SBen Young Tae Kim
439427281f9SJohan Hovold if (length < idx + sizeof(struct tlv_type_nvm) + tag_len)
440427281f9SJohan Hovold return -EINVAL;
441427281f9SJohan Hovold
44283e81961SBen Young Tae Kim /* Update NVM tags as needed */
44383e81961SBen Young Tae Kim switch (tag_id) {
4449d23305fSJohan Hovold case EDL_TAG_ID_BD_ADDR:
4459d23305fSJohan Hovold if (tag_len != sizeof(bdaddr_t))
4469d23305fSJohan Hovold return -EINVAL;
4479d23305fSJohan Hovold
4489d23305fSJohan Hovold memcpy(&config->bdaddr, tlv_nvm->data, sizeof(bdaddr_t));
4499d23305fSJohan Hovold
4509d23305fSJohan Hovold break;
4519d23305fSJohan Hovold
45283e81961SBen Young Tae Kim case EDL_TAG_ID_HCI:
453427281f9SJohan Hovold if (tag_len < 3)
454427281f9SJohan Hovold return -EINVAL;
455427281f9SJohan Hovold
45683e81961SBen Young Tae Kim /* HCI transport layer parameters
45783e81961SBen Young Tae Kim * enabling software inband sleep
45883e81961SBen Young Tae Kim * onto controller side.
45983e81961SBen Young Tae Kim */
46083e81961SBen Young Tae Kim tlv_nvm->data[0] |= 0x80;
46183e81961SBen Young Tae Kim
46283e81961SBen Young Tae Kim /* UART Baud Rate */
463e5d6468fSRocky Liao if (soc_type >= QCA_WCN3991)
464b6388254SRocky Liao tlv_nvm->data[1] = nvm_baud_rate;
465b6388254SRocky Liao else
466b6388254SRocky Liao tlv_nvm->data[2] = nvm_baud_rate;
46783e81961SBen Young Tae Kim
46883e81961SBen Young Tae Kim break;
46983e81961SBen Young Tae Kim
47083e81961SBen Young Tae Kim case EDL_TAG_ID_DEEP_SLEEP:
471427281f9SJohan Hovold if (tag_len < 1)
472427281f9SJohan Hovold return -EINVAL;
473427281f9SJohan Hovold
47483e81961SBen Young Tae Kim /* Sleep enable mask
47583e81961SBen Young Tae Kim * enabling deep sleep feature on controller.
47683e81961SBen Young Tae Kim */
47783e81961SBen Young Tae Kim tlv_nvm->data[0] |= 0x01;
47883e81961SBen Young Tae Kim
47983e81961SBen Young Tae Kim break;
48083e81961SBen Young Tae Kim }
48183e81961SBen Young Tae Kim
482427281f9SJohan Hovold idx += sizeof(struct tlv_type_nvm) + tag_len;
48383e81961SBen Young Tae Kim }
48483e81961SBen Young Tae Kim break;
48583e81961SBen Young Tae Kim
48683e81961SBen Young Tae Kim default:
48783e81961SBen Young Tae Kim BT_ERR("Unknown TLV type %d", config->type);
488427281f9SJohan Hovold return -EINVAL;
48983e81961SBen Young Tae Kim }
490427281f9SJohan Hovold
491427281f9SJohan Hovold return 0;
49283e81961SBen Young Tae Kim }
49383e81961SBen Young Tae Kim
qca_tlv_send_segment(struct hci_dev * hdev,int seg_size,const u8 * data,enum qca_tlv_dnld_mode mode,enum qca_btsoc_type soc_type)494ba493d4fSBalakrishna Godavarthi static int qca_tlv_send_segment(struct hci_dev *hdev, int seg_size,
4957d250a06SBalakrishna Godavarthi const u8 *data, enum qca_tlv_dnld_mode mode,
4967d250a06SBalakrishna Godavarthi enum qca_btsoc_type soc_type)
49783e81961SBen Young Tae Kim {
49883e81961SBen Young Tae Kim struct sk_buff *skb;
49983e81961SBen Young Tae Kim struct edl_event_hdr *edl;
50083e81961SBen Young Tae Kim struct tlv_seg_resp *tlv_resp;
50183e81961SBen Young Tae Kim u8 cmd[MAX_SIZE_PER_TLV_SEGMENT + 2];
50283e81961SBen Young Tae Kim int err = 0;
5037d250a06SBalakrishna Godavarthi u8 event_type = HCI_EV_VENDOR;
5047d250a06SBalakrishna Godavarthi u8 rlen = (sizeof(*edl) + sizeof(*tlv_resp));
5057d250a06SBalakrishna Godavarthi u8 rtype = EDL_TVL_DNLD_RES_EVT;
50683e81961SBen Young Tae Kim
50783e81961SBen Young Tae Kim cmd[0] = EDL_PATCH_TLV_REQ_CMD;
50883e81961SBen Young Tae Kim cmd[1] = seg_size;
50983e81961SBen Young Tae Kim memcpy(cmd + 2, data, seg_size);
51083e81961SBen Young Tae Kim
511e303d124SBalakrishna Godavarthi if (mode == QCA_SKIP_EVT_VSE_CC || mode == QCA_SKIP_EVT_VSE)
5126e03126aSLoic Poulain return __hci_cmd_send(hdev, EDL_PATCH_CMD_OPCODE, seg_size + 2,
5136e03126aSLoic Poulain cmd);
5146e03126aSLoic Poulain
5157d250a06SBalakrishna Godavarthi /* Unlike other SoC's sending version command response as payload to
5167d250a06SBalakrishna Godavarthi * VSE event. WCN3991 sends version command response as a payload to
5177d250a06SBalakrishna Godavarthi * command complete event.
5187d250a06SBalakrishna Godavarthi */
519e5d6468fSRocky Liao if (soc_type >= QCA_WCN3991) {
5207d250a06SBalakrishna Godavarthi event_type = 0;
5217d250a06SBalakrishna Godavarthi rlen = sizeof(*edl);
5227d250a06SBalakrishna Godavarthi rtype = EDL_PATCH_TLV_REQ_CMD;
5237d250a06SBalakrishna Godavarthi }
5247d250a06SBalakrishna Godavarthi
52583e81961SBen Young Tae Kim skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, seg_size + 2, cmd,
5267d250a06SBalakrishna Godavarthi event_type, HCI_INIT_TIMEOUT);
52783e81961SBen Young Tae Kim if (IS_ERR(skb)) {
52883e81961SBen Young Tae Kim err = PTR_ERR(skb);
529ba493d4fSBalakrishna Godavarthi bt_dev_err(hdev, "QCA Failed to send TLV segment (%d)", err);
53083e81961SBen Young Tae Kim return err;
53183e81961SBen Young Tae Kim }
53283e81961SBen Young Tae Kim
5337d250a06SBalakrishna Godavarthi if (skb->len != rlen) {
534ba493d4fSBalakrishna Godavarthi bt_dev_err(hdev, "QCA TLV response size mismatch");
53583e81961SBen Young Tae Kim err = -EILSEQ;
53683e81961SBen Young Tae Kim goto out;
53783e81961SBen Young Tae Kim }
53883e81961SBen Young Tae Kim
53983e81961SBen Young Tae Kim edl = (struct edl_event_hdr *)(skb->data);
5400676cab4SColin Ian King if (!edl) {
541ba493d4fSBalakrishna Godavarthi bt_dev_err(hdev, "TLV with no header");
54283e81961SBen Young Tae Kim err = -EILSEQ;
54383e81961SBen Young Tae Kim goto out;
54483e81961SBen Young Tae Kim }
54583e81961SBen Young Tae Kim
5467d250a06SBalakrishna Godavarthi if (edl->cresp != EDL_CMD_REQ_RES_EVT || edl->rtype != rtype) {
5477d250a06SBalakrishna Godavarthi bt_dev_err(hdev, "QCA TLV with error stat 0x%x rtype 0x%x",
5487d250a06SBalakrishna Godavarthi edl->cresp, edl->rtype);
5497d250a06SBalakrishna Godavarthi err = -EIO;
5507d250a06SBalakrishna Godavarthi }
55183e81961SBen Young Tae Kim
552e5d6468fSRocky Liao if (soc_type >= QCA_WCN3991)
5537d250a06SBalakrishna Godavarthi goto out;
5547d250a06SBalakrishna Godavarthi
5557d250a06SBalakrishna Godavarthi tlv_resp = (struct tlv_seg_resp *)(edl->data);
5567d250a06SBalakrishna Godavarthi if (tlv_resp->result) {
557ba493d4fSBalakrishna Godavarthi bt_dev_err(hdev, "QCA TLV with error stat 0x%x rtype 0x%x (0x%x)",
558ba493d4fSBalakrishna Godavarthi edl->cresp, edl->rtype, tlv_resp->result);
55983e81961SBen Young Tae Kim }
56083e81961SBen Young Tae Kim
56183e81961SBen Young Tae Kim out:
56283e81961SBen Young Tae Kim kfree_skb(skb);
56383e81961SBen Young Tae Kim
56483e81961SBen Young Tae Kim return err;
56583e81961SBen Young Tae Kim }
56683e81961SBen Young Tae Kim
qca_inject_cmd_complete_event(struct hci_dev * hdev)56732646db8SBalakrishna Godavarthi static int qca_inject_cmd_complete_event(struct hci_dev *hdev)
56832646db8SBalakrishna Godavarthi {
56932646db8SBalakrishna Godavarthi struct hci_event_hdr *hdr;
57032646db8SBalakrishna Godavarthi struct hci_ev_cmd_complete *evt;
57132646db8SBalakrishna Godavarthi struct sk_buff *skb;
57232646db8SBalakrishna Godavarthi
57332646db8SBalakrishna Godavarthi skb = bt_skb_alloc(sizeof(*hdr) + sizeof(*evt) + 1, GFP_KERNEL);
57432646db8SBalakrishna Godavarthi if (!skb)
57532646db8SBalakrishna Godavarthi return -ENOMEM;
57632646db8SBalakrishna Godavarthi
57732646db8SBalakrishna Godavarthi hdr = skb_put(skb, sizeof(*hdr));
57832646db8SBalakrishna Godavarthi hdr->evt = HCI_EV_CMD_COMPLETE;
57932646db8SBalakrishna Godavarthi hdr->plen = sizeof(*evt) + 1;
58032646db8SBalakrishna Godavarthi
58132646db8SBalakrishna Godavarthi evt = skb_put(skb, sizeof(*evt));
58232646db8SBalakrishna Godavarthi evt->ncmd = 1;
5832fde6afbSMatthias Kaehlcke evt->opcode = cpu_to_le16(QCA_HCI_CC_OPCODE);
58432646db8SBalakrishna Godavarthi
58532646db8SBalakrishna Godavarthi skb_put_u8(skb, QCA_HCI_CC_SUCCESS);
58632646db8SBalakrishna Godavarthi
58732646db8SBalakrishna Godavarthi hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
58832646db8SBalakrishna Godavarthi
58932646db8SBalakrishna Godavarthi return hci_recv_frame(hdev, skb);
59032646db8SBalakrishna Godavarthi }
59132646db8SBalakrishna Godavarthi
qca_download_firmware(struct hci_dev * hdev,struct qca_fw_config * config,enum qca_btsoc_type soc_type,u8 rom_ver)592ba493d4fSBalakrishna Godavarthi static int qca_download_firmware(struct hci_dev *hdev,
5937d250a06SBalakrishna Godavarthi struct qca_fw_config *config,
594ecf6b2d9SVenkata Lakshmi Narayana Gubba enum qca_btsoc_type soc_type,
595ecf6b2d9SVenkata Lakshmi Narayana Gubba u8 rom_ver)
59683e81961SBen Young Tae Kim {
59783e81961SBen Young Tae Kim const struct firmware *fw;
598b43ca511SConnor Abbott u8 *data;
5996e03126aSLoic Poulain const u8 *segment;
600b43ca511SConnor Abbott int ret, size, remain, i = 0;
60183e81961SBen Young Tae Kim
602ba493d4fSBalakrishna Godavarthi bt_dev_info(hdev, "QCA Downloading %s", config->fwname);
60383e81961SBen Young Tae Kim
60483e81961SBen Young Tae Kim ret = request_firmware(&fw, config->fwname, &hdev->dev);
60583e81961SBen Young Tae Kim if (ret) {
606ecf6b2d9SVenkata Lakshmi Narayana Gubba /* For WCN6750, if mbn file is not present then check for
607ecf6b2d9SVenkata Lakshmi Narayana Gubba * tlv file.
608ecf6b2d9SVenkata Lakshmi Narayana Gubba */
609ecf6b2d9SVenkata Lakshmi Narayana Gubba if (soc_type == QCA_WCN6750 && config->type == ELF_TYPE_PATCH) {
610ecf6b2d9SVenkata Lakshmi Narayana Gubba bt_dev_dbg(hdev, "QCA Failed to request file: %s (%d)",
611ecf6b2d9SVenkata Lakshmi Narayana Gubba config->fwname, ret);
612ecf6b2d9SVenkata Lakshmi Narayana Gubba config->type = TLV_TYPE_PATCH;
613ecf6b2d9SVenkata Lakshmi Narayana Gubba snprintf(config->fwname, sizeof(config->fwname),
614ecf6b2d9SVenkata Lakshmi Narayana Gubba "qca/msbtfw%02x.tlv", rom_ver);
615ecf6b2d9SVenkata Lakshmi Narayana Gubba bt_dev_info(hdev, "QCA Downloading %s", config->fwname);
616ecf6b2d9SVenkata Lakshmi Narayana Gubba ret = request_firmware(&fw, config->fwname, &hdev->dev);
617ecf6b2d9SVenkata Lakshmi Narayana Gubba if (ret) {
618ba493d4fSBalakrishna Godavarthi bt_dev_err(hdev, "QCA Failed to request file: %s (%d)",
61983e81961SBen Young Tae Kim config->fwname, ret);
62083e81961SBen Young Tae Kim return ret;
62183e81961SBen Young Tae Kim }
622ae2d111cSCheng Jiang }
623ae2d111cSCheng Jiang /* If the board-specific file is missing, try loading the default
624ae2d111cSCheng Jiang * one, unless that was attempted already.
625ae2d111cSCheng Jiang */
626ae2d111cSCheng Jiang else if (config->type == TLV_TYPE_NVM &&
627ae2d111cSCheng Jiang qca_get_alt_nvm_file(config->fwname, sizeof(config->fwname))) {
628ae2d111cSCheng Jiang bt_dev_info(hdev, "QCA Downloading %s", config->fwname);
629ae2d111cSCheng Jiang ret = request_firmware(&fw, config->fwname, &hdev->dev);
630ae2d111cSCheng Jiang if (ret) {
631ae2d111cSCheng Jiang bt_dev_err(hdev, "QCA Failed to request file: %s (%d)",
632ae2d111cSCheng Jiang config->fwname, ret);
633ae2d111cSCheng Jiang return ret;
634ae2d111cSCheng Jiang }
635ecf6b2d9SVenkata Lakshmi Narayana Gubba } else {
636ecf6b2d9SVenkata Lakshmi Narayana Gubba bt_dev_err(hdev, "QCA Failed to request file: %s (%d)",
637ecf6b2d9SVenkata Lakshmi Narayana Gubba config->fwname, ret);
638ecf6b2d9SVenkata Lakshmi Narayana Gubba return ret;
639ecf6b2d9SVenkata Lakshmi Narayana Gubba }
640ecf6b2d9SVenkata Lakshmi Narayana Gubba }
64183e81961SBen Young Tae Kim
642b43ca511SConnor Abbott size = fw->size;
643b43ca511SConnor Abbott data = vmalloc(fw->size);
644b43ca511SConnor Abbott if (!data) {
645b43ca511SConnor Abbott bt_dev_err(hdev, "QCA Failed to allocate memory for file: %s",
646b43ca511SConnor Abbott config->fwname);
647b43ca511SConnor Abbott release_firmware(fw);
648b43ca511SConnor Abbott return -ENOMEM;
649b43ca511SConnor Abbott }
65083e81961SBen Young Tae Kim
651b43ca511SConnor Abbott memcpy(data, fw->data, size);
652b43ca511SConnor Abbott release_firmware(fw);
653b43ca511SConnor Abbott
654427281f9SJohan Hovold ret = qca_tlv_check_data(hdev, config, data, size, soc_type);
655427281f9SJohan Hovold if (ret)
6567bcba557SJohan Hovold goto out;
657b43ca511SConnor Abbott
658b43ca511SConnor Abbott segment = data;
659b43ca511SConnor Abbott remain = size;
6606e03126aSLoic Poulain while (remain > 0) {
6616e03126aSLoic Poulain int segsize = min(MAX_SIZE_PER_TLV_SEGMENT, remain);
6626e03126aSLoic Poulain
6636e03126aSLoic Poulain bt_dev_dbg(hdev, "Send segment %d, size %d", i++, segsize);
6646e03126aSLoic Poulain
6656e03126aSLoic Poulain remain -= segsize;
6666e03126aSLoic Poulain /* The last segment is always acked regardless download mode */
6676e03126aSLoic Poulain if (!remain || segsize < MAX_SIZE_PER_TLV_SEGMENT)
668e303d124SBalakrishna Godavarthi config->dnld_mode = QCA_SKIP_EVT_NONE;
6696e03126aSLoic Poulain
670ba493d4fSBalakrishna Godavarthi ret = qca_tlv_send_segment(hdev, segsize, segment,
6717d250a06SBalakrishna Godavarthi config->dnld_mode, soc_type);
6726e03126aSLoic Poulain if (ret)
67332646db8SBalakrishna Godavarthi goto out;
6746e03126aSLoic Poulain
6756e03126aSLoic Poulain segment += segsize;
67683e81961SBen Young Tae Kim }
67783e81961SBen Young Tae Kim
67832646db8SBalakrishna Godavarthi /* Latest qualcomm chipsets are not sending a command complete event
67932646db8SBalakrishna Godavarthi * for every fw packet sent. They only respond with a vendor specific
68032646db8SBalakrishna Godavarthi * event for the last packet. This optimization in the chip will
68132646db8SBalakrishna Godavarthi * decrease the BT in initialization time. Here we will inject a command
68232646db8SBalakrishna Godavarthi * complete event to avoid a command timeout error message.
68332646db8SBalakrishna Godavarthi */
684e303d124SBalakrishna Godavarthi if (config->dnld_type == QCA_SKIP_EVT_VSE_CC ||
685e303d124SBalakrishna Godavarthi config->dnld_type == QCA_SKIP_EVT_VSE)
686c7c5ae29SClaire Chang ret = qca_inject_cmd_complete_event(hdev);
68732646db8SBalakrishna Godavarthi
68832646db8SBalakrishna Godavarthi out:
689b43ca511SConnor Abbott vfree(data);
69083e81961SBen Young Tae Kim
69183e81961SBen Young Tae Kim return ret;
69283e81961SBen Young Tae Kim }
69383e81961SBen Young Tae Kim
qca_disable_soc_logging(struct hci_dev * hdev)694590deccfSBalakrishna Godavarthi static int qca_disable_soc_logging(struct hci_dev *hdev)
695590deccfSBalakrishna Godavarthi {
696590deccfSBalakrishna Godavarthi struct sk_buff *skb;
697590deccfSBalakrishna Godavarthi u8 cmd[2];
698590deccfSBalakrishna Godavarthi int err;
699590deccfSBalakrishna Godavarthi
700590deccfSBalakrishna Godavarthi cmd[0] = QCA_DISABLE_LOGGING_SUB_OP;
701590deccfSBalakrishna Godavarthi cmd[1] = 0x00;
702590deccfSBalakrishna Godavarthi skb = __hci_cmd_sync_ev(hdev, QCA_DISABLE_LOGGING, sizeof(cmd), cmd,
703590deccfSBalakrishna Godavarthi HCI_EV_CMD_COMPLETE, HCI_INIT_TIMEOUT);
704590deccfSBalakrishna Godavarthi if (IS_ERR(skb)) {
705590deccfSBalakrishna Godavarthi err = PTR_ERR(skb);
706590deccfSBalakrishna Godavarthi bt_dev_err(hdev, "QCA Failed to disable soc logging(%d)", err);
707590deccfSBalakrishna Godavarthi return err;
708590deccfSBalakrishna Godavarthi }
709590deccfSBalakrishna Godavarthi
710590deccfSBalakrishna Godavarthi kfree_skb(skb);
711590deccfSBalakrishna Godavarthi
712590deccfSBalakrishna Godavarthi return 0;
713590deccfSBalakrishna Godavarthi }
714590deccfSBalakrishna Godavarthi
qca_set_bdaddr_rome(struct hci_dev * hdev,const bdaddr_t * bdaddr)71583e81961SBen Young Tae Kim int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr)
71683e81961SBen Young Tae Kim {
71783e81961SBen Young Tae Kim struct sk_buff *skb;
71883e81961SBen Young Tae Kim u8 cmd[9];
71983e81961SBen Young Tae Kim int err;
72083e81961SBen Young Tae Kim
72183e81961SBen Young Tae Kim cmd[0] = EDL_NVM_ACCESS_SET_REQ_CMD;
72283e81961SBen Young Tae Kim cmd[1] = 0x02; /* TAG ID */
72383e81961SBen Young Tae Kim cmd[2] = sizeof(bdaddr_t); /* size */
72483e81961SBen Young Tae Kim memcpy(cmd + 3, bdaddr, sizeof(bdaddr_t));
72583e81961SBen Young Tae Kim skb = __hci_cmd_sync_ev(hdev, EDL_NVM_ACCESS_OPCODE, sizeof(cmd), cmd,
726e4cc5a18SMarcel Holtmann HCI_EV_VENDOR, HCI_INIT_TIMEOUT);
72783e81961SBen Young Tae Kim if (IS_ERR(skb)) {
72883e81961SBen Young Tae Kim err = PTR_ERR(skb);
729ba493d4fSBalakrishna Godavarthi bt_dev_err(hdev, "QCA Change address command failed (%d)", err);
73083e81961SBen Young Tae Kim return err;
73183e81961SBen Young Tae Kim }
73283e81961SBen Young Tae Kim
73383e81961SBen Young Tae Kim kfree_skb(skb);
73483e81961SBen Young Tae Kim
73583e81961SBen Young Tae Kim return 0;
73683e81961SBen Young Tae Kim }
73783e81961SBen Young Tae Kim EXPORT_SYMBOL_GPL(qca_set_bdaddr_rome);
73883e81961SBen Young Tae Kim
qca_check_bdaddr(struct hci_dev * hdev,const struct qca_fw_config * config)7399d23305fSJohan Hovold static int qca_check_bdaddr(struct hci_dev *hdev, const struct qca_fw_config *config)
7403019a9d3SJohan Hovold {
7413019a9d3SJohan Hovold struct hci_rp_read_bd_addr *bda;
7423019a9d3SJohan Hovold struct sk_buff *skb;
7433019a9d3SJohan Hovold int err;
7443019a9d3SJohan Hovold
7453019a9d3SJohan Hovold if (bacmp(&hdev->public_addr, BDADDR_ANY))
7463019a9d3SJohan Hovold return 0;
7473019a9d3SJohan Hovold
7483019a9d3SJohan Hovold skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL,
7493019a9d3SJohan Hovold HCI_INIT_TIMEOUT);
7503019a9d3SJohan Hovold if (IS_ERR(skb)) {
7513019a9d3SJohan Hovold err = PTR_ERR(skb);
7523019a9d3SJohan Hovold bt_dev_err(hdev, "Failed to read device address (%d)", err);
7533019a9d3SJohan Hovold return err;
7543019a9d3SJohan Hovold }
7553019a9d3SJohan Hovold
7563019a9d3SJohan Hovold if (skb->len != sizeof(*bda)) {
7573019a9d3SJohan Hovold bt_dev_err(hdev, "Device address length mismatch");
7583019a9d3SJohan Hovold kfree_skb(skb);
7593019a9d3SJohan Hovold return -EIO;
7603019a9d3SJohan Hovold }
7613019a9d3SJohan Hovold
7623019a9d3SJohan Hovold bda = (struct hci_rp_read_bd_addr *)skb->data;
7639d23305fSJohan Hovold if (!bacmp(&bda->bdaddr, &config->bdaddr))
7643019a9d3SJohan Hovold set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks);
7653019a9d3SJohan Hovold
7663019a9d3SJohan Hovold kfree_skb(skb);
7673019a9d3SJohan Hovold
7683019a9d3SJohan Hovold return 0;
7693019a9d3SJohan Hovold }
7703019a9d3SJohan Hovold
qca_get_nvm_name_by_board(char * fwname,size_t max_size,const char * stem,enum qca_btsoc_type soc_type,struct qca_btsoc_version ver,u8 rom_ver,u16 bid)771ae2d111cSCheng Jiang static void qca_get_nvm_name_by_board(char *fwname, size_t max_size,
772ae2d111cSCheng Jiang const char *stem, enum qca_btsoc_type soc_type,
773a381ee26STim Jiang struct qca_btsoc_version ver, u8 rom_ver, u16 bid)
774a381ee26STim Jiang {
775a381ee26STim Jiang const char *variant;
776ae2d111cSCheng Jiang const char *prefix;
777a381ee26STim Jiang
778ae2d111cSCheng Jiang /* Set the default value to variant and prefix */
779ae2d111cSCheng Jiang variant = "";
780ae2d111cSCheng Jiang prefix = "b";
781ae2d111cSCheng Jiang
782ae2d111cSCheng Jiang if (soc_type == QCA_QCA2066)
783ae2d111cSCheng Jiang prefix = "";
784ae2d111cSCheng Jiang
785ae2d111cSCheng Jiang if (soc_type == QCA_WCN6855 || soc_type == QCA_QCA2066) {
786ae2d111cSCheng Jiang /* If the chip is manufactured by GlobalFoundries */
787a381ee26STim Jiang if ((le32_to_cpu(ver.soc_id) & QCA_HSP_GF_SOC_MASK) == QCA_HSP_GF_SOC_ID)
788a381ee26STim Jiang variant = "g";
789a381ee26STim Jiang }
790a381ee26STim Jiang
791ae2d111cSCheng Jiang if (rom_ver != 0) {
792ae2d111cSCheng Jiang if (bid == 0x0 || bid == 0xffff)
793ae2d111cSCheng Jiang snprintf(fwname, max_size, "qca/%s%02x%s.bin", stem, rom_ver, variant);
794e68d2b88SZijun Hu else
795ae2d111cSCheng Jiang snprintf(fwname, max_size, "qca/%s%02x%s.%s%02x", stem, rom_ver,
796ae2d111cSCheng Jiang variant, prefix, bid);
797ae2d111cSCheng Jiang } else {
798ae2d111cSCheng Jiang if (bid == 0x0 || bid == 0xffff)
799ae2d111cSCheng Jiang snprintf(fwname, max_size, "qca/%s%s.bin", stem, variant);
800ae2d111cSCheng Jiang else
801ae2d111cSCheng Jiang snprintf(fwname, max_size, "qca/%s%s.%s%02x", stem, variant, prefix, bid);
802ae2d111cSCheng Jiang }
803e68d2b88SZijun Hu }
804e68d2b88SZijun Hu
qca_uart_setup(struct hci_dev * hdev,uint8_t baudrate,enum qca_btsoc_type soc_type,struct qca_btsoc_version ver,const char * firmware_name)805aadebac4SBalakrishna Godavarthi int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
806059924fdSVenkata Lakshmi Narayana Gubba enum qca_btsoc_type soc_type, struct qca_btsoc_version ver,
80799c905c6SRocky Liao const char *firmware_name)
80883e81961SBen Young Tae Kim {
8099d23305fSJohan Hovold struct qca_fw_config config = {};
81083e81961SBen Young Tae Kim int err;
811523760b7SHarish Bandi u8 rom_ver = 0;
812059924fdSVenkata Lakshmi Narayana Gubba u32 soc_ver;
813a381ee26STim Jiang u16 boardid = 0;
81483e81961SBen Young Tae Kim
815ba493d4fSBalakrishna Godavarthi bt_dev_dbg(hdev, "QCA setup on UART");
81683e81961SBen Young Tae Kim
817059924fdSVenkata Lakshmi Narayana Gubba soc_ver = get_soc_ver(ver.soc_id, ver.rom_ver);
818059924fdSVenkata Lakshmi Narayana Gubba
819059924fdSVenkata Lakshmi Narayana Gubba bt_dev_info(hdev, "QCA controller version 0x%08x", soc_ver);
820059924fdSVenkata Lakshmi Narayana Gubba
82183e81961SBen Young Tae Kim config.user_baud_rate = baudrate;
82283e81961SBen Young Tae Kim
8234219d468SBalakrishna Godavarthi /* Firmware files to download are based on ROM version.
8244219d468SBalakrishna Godavarthi * ROM version is derived from last two bytes of soc_ver.
8254219d468SBalakrishna Godavarthi */
826f904feefSLuca Weiss if (soc_type == QCA_WCN3988)
827f904feefSLuca Weiss rom_ver = ((soc_ver & 0x00000f00) >> 0x05) | (soc_ver & 0x0000000f);
828f904feefSLuca Weiss else
82999fba8e3SVenkata Lakshmi Narayana Gubba rom_ver = ((soc_ver & 0x00000f00) >> 0x04) | (soc_ver & 0x0000000f);
83099fba8e3SVenkata Lakshmi Narayana Gubba
8314fac8a7aSSai Teja Aluvala if (soc_type == QCA_WCN6750)
8324fac8a7aSSai Teja Aluvala qca_send_patch_config_cmd(hdev);
8334fac8a7aSSai Teja Aluvala
83499fba8e3SVenkata Lakshmi Narayana Gubba /* Download rampatch file */
83599fba8e3SVenkata Lakshmi Narayana Gubba config.type = TLV_TYPE_PATCH;
836691d54d0SNeil Armstrong switch (soc_type) {
837691d54d0SNeil Armstrong case QCA_WCN3990:
838691d54d0SNeil Armstrong case QCA_WCN3991:
839691d54d0SNeil Armstrong case QCA_WCN3998:
8404219d468SBalakrishna Godavarthi snprintf(config.fwname, sizeof(config.fwname),
8414219d468SBalakrishna Godavarthi "qca/crbtfw%02x.tlv", rom_ver);
842691d54d0SNeil Armstrong break;
843691d54d0SNeil Armstrong case QCA_WCN3988:
844691d54d0SNeil Armstrong snprintf(config.fwname, sizeof(config.fwname),
845691d54d0SNeil Armstrong "qca/apbtfw%02x.tlv", rom_ver);
846691d54d0SNeil Armstrong break;
847a381ee26STim Jiang case QCA_QCA2066:
848a381ee26STim Jiang snprintf(config.fwname, sizeof(config.fwname),
849a381ee26STim Jiang "qca/hpbtfw%02x.tlv", rom_ver);
850a381ee26STim Jiang break;
851691d54d0SNeil Armstrong case QCA_QCA6390:
852e5d6468fSRocky Liao snprintf(config.fwname, sizeof(config.fwname),
853e5d6468fSRocky Liao "qca/htbtfw%02x.tlv", rom_ver);
854691d54d0SNeil Armstrong break;
855691d54d0SNeil Armstrong case QCA_WCN6750:
856ecf6b2d9SVenkata Lakshmi Narayana Gubba /* Choose mbn file by default.If mbn file is not found
857ecf6b2d9SVenkata Lakshmi Narayana Gubba * then choose tlv file
858ecf6b2d9SVenkata Lakshmi Narayana Gubba */
859ecf6b2d9SVenkata Lakshmi Narayana Gubba config.type = ELF_TYPE_PATCH;
860d8f97da1SVenkata Lakshmi Narayana Gubba snprintf(config.fwname, sizeof(config.fwname),
861ecf6b2d9SVenkata Lakshmi Narayana Gubba "qca/msbtfw%02x.mbn", rom_ver);
862691d54d0SNeil Armstrong break;
863691d54d0SNeil Armstrong case QCA_WCN6855:
864095327feSSteev Klimaszewski snprintf(config.fwname, sizeof(config.fwname),
865095327feSSteev Klimaszewski "qca/hpbtfw%02x.tlv", rom_ver);
866691d54d0SNeil Armstrong break;
867e0c1278aSNeil Armstrong case QCA_WCN7850:
868e0c1278aSNeil Armstrong snprintf(config.fwname, sizeof(config.fwname),
869e0c1278aSNeil Armstrong "qca/hmtbtfw%02x.tlv", rom_ver);
870e0c1278aSNeil Armstrong break;
871691d54d0SNeil Armstrong default:
8724219d468SBalakrishna Godavarthi snprintf(config.fwname, sizeof(config.fwname),
8734219d468SBalakrishna Godavarthi "qca/rampatch_%08x.bin", soc_ver);
8744219d468SBalakrishna Godavarthi }
8754219d468SBalakrishna Godavarthi
876ecf6b2d9SVenkata Lakshmi Narayana Gubba err = qca_download_firmware(hdev, &config, soc_type, rom_ver);
87783e81961SBen Young Tae Kim if (err < 0) {
878ba493d4fSBalakrishna Godavarthi bt_dev_err(hdev, "QCA Failed to download patch (%d)", err);
87983e81961SBen Young Tae Kim return err;
88083e81961SBen Young Tae Kim }
88183e81961SBen Young Tae Kim
8828059ba0bSMatthias Kaehlcke /* Give the controller some time to get ready to receive the NVM */
8838059ba0bSMatthias Kaehlcke msleep(10);
8848059ba0bSMatthias Kaehlcke
885e68d2b88SZijun Hu if (soc_type == QCA_QCA2066 || soc_type == QCA_WCN7850)
886a381ee26STim Jiang qca_read_fw_board_id(hdev, &boardid);
887a381ee26STim Jiang
88883e81961SBen Young Tae Kim /* Download NVM configuration */
88983e81961SBen Young Tae Kim config.type = TLV_TYPE_NVM;
890691d54d0SNeil Armstrong if (firmware_name) {
891ae2d111cSCheng Jiang /* The firmware name has an extension, use it directly */
892ae2d111cSCheng Jiang if (qca_filename_has_extension(firmware_name)) {
893ae2d111cSCheng Jiang snprintf(config.fwname, sizeof(config.fwname), "qca/%s", firmware_name);
894ae2d111cSCheng Jiang } else {
895ae2d111cSCheng Jiang qca_read_fw_board_id(hdev, &boardid);
896ae2d111cSCheng Jiang qca_get_nvm_name_by_board(config.fwname, sizeof(config.fwname),
897ae2d111cSCheng Jiang firmware_name, soc_type, ver, 0, boardid);
898ae2d111cSCheng Jiang }
899691d54d0SNeil Armstrong } else {
900691d54d0SNeil Armstrong switch (soc_type) {
901691d54d0SNeil Armstrong case QCA_WCN3990:
902691d54d0SNeil Armstrong case QCA_WCN3991:
903691d54d0SNeil Armstrong case QCA_WCN3998:
9048153b738SMin-Hua Chen if (le32_to_cpu(ver.soc_id) == QCA_WCN3991_SOC_ID) {
905059924fdSVenkata Lakshmi Narayana Gubba snprintf(config.fwname, sizeof(config.fwname),
906059924fdSVenkata Lakshmi Narayana Gubba "qca/crnv%02xu.bin", rom_ver);
907059924fdSVenkata Lakshmi Narayana Gubba } else {
9084219d468SBalakrishna Godavarthi snprintf(config.fwname, sizeof(config.fwname),
9094219d468SBalakrishna Godavarthi "qca/crnv%02x.bin", rom_ver);
910059924fdSVenkata Lakshmi Narayana Gubba }
911691d54d0SNeil Armstrong break;
912691d54d0SNeil Armstrong case QCA_WCN3988:
913691d54d0SNeil Armstrong snprintf(config.fwname, sizeof(config.fwname),
914691d54d0SNeil Armstrong "qca/apnv%02x.bin", rom_ver);
915691d54d0SNeil Armstrong break;
916a381ee26STim Jiang case QCA_QCA2066:
917ae2d111cSCheng Jiang qca_get_nvm_name_by_board(config.fwname,
918ae2d111cSCheng Jiang sizeof(config.fwname), "hpnv", soc_type, ver,
919ae2d111cSCheng Jiang rom_ver, boardid);
920a381ee26STim Jiang break;
921691d54d0SNeil Armstrong case QCA_QCA6390:
922e5d6468fSRocky Liao snprintf(config.fwname, sizeof(config.fwname),
923e5d6468fSRocky Liao "qca/htnv%02x.bin", rom_ver);
924691d54d0SNeil Armstrong break;
925691d54d0SNeil Armstrong case QCA_WCN6750:
926d8f97da1SVenkata Lakshmi Narayana Gubba snprintf(config.fwname, sizeof(config.fwname),
927d8f97da1SVenkata Lakshmi Narayana Gubba "qca/msnv%02x.bin", rom_ver);
928691d54d0SNeil Armstrong break;
929691d54d0SNeil Armstrong case QCA_WCN6855:
930*ab8b6bf2SZijun Hu qca_read_fw_board_id(hdev, &boardid);
931*ab8b6bf2SZijun Hu qca_get_nvm_name_by_board(config.fwname, sizeof(config.fwname),
932*ab8b6bf2SZijun Hu "hpnv", soc_type, ver, rom_ver, boardid);
933691d54d0SNeil Armstrong break;
934e0c1278aSNeil Armstrong case QCA_WCN7850:
935ae2d111cSCheng Jiang qca_get_nvm_name_by_board(config.fwname, sizeof(config.fwname),
936ae2d111cSCheng Jiang "hmtnv", soc_type, ver, rom_ver, boardid);
937e0c1278aSNeil Armstrong break;
938691d54d0SNeil Armstrong default:
9394219d468SBalakrishna Godavarthi snprintf(config.fwname, sizeof(config.fwname),
9404219d468SBalakrishna Godavarthi "qca/nvm_%08x.bin", soc_ver);
941691d54d0SNeil Armstrong }
942691d54d0SNeil Armstrong }
9434219d468SBalakrishna Godavarthi
944ecf6b2d9SVenkata Lakshmi Narayana Gubba err = qca_download_firmware(hdev, &config, soc_type, rom_ver);
94583e81961SBen Young Tae Kim if (err < 0) {
946ba493d4fSBalakrishna Godavarthi bt_dev_err(hdev, "QCA Failed to download NVM (%d)", err);
94783e81961SBen Young Tae Kim return err;
94883e81961SBen Young Tae Kim }
94983e81961SBen Young Tae Kim
950691d54d0SNeil Armstrong switch (soc_type) {
951691d54d0SNeil Armstrong case QCA_WCN3991:
952a381ee26STim Jiang case QCA_QCA2066:
953691d54d0SNeil Armstrong case QCA_QCA6390:
954691d54d0SNeil Armstrong case QCA_WCN6750:
955691d54d0SNeil Armstrong case QCA_WCN6855:
956e0c1278aSNeil Armstrong case QCA_WCN7850:
957590deccfSBalakrishna Godavarthi err = qca_disable_soc_logging(hdev);
958590deccfSBalakrishna Godavarthi if (err < 0)
959590deccfSBalakrishna Godavarthi return err;
960691d54d0SNeil Armstrong break;
961691d54d0SNeil Armstrong default:
962691d54d0SNeil Armstrong break;
963590deccfSBalakrishna Godavarthi }
964590deccfSBalakrishna Godavarthi
965d8f97da1SVenkata Lakshmi Narayana Gubba /* WCN399x and WCN6750 supports the Microsoft vendor extension with 0xFD70 as the
966eaf19b0cSMiao-chen Chou * VsMsftOpCode.
967eaf19b0cSMiao-chen Chou */
968eaf19b0cSMiao-chen Chou switch (soc_type) {
969691d54d0SNeil Armstrong case QCA_WCN3988:
970eaf19b0cSMiao-chen Chou case QCA_WCN3990:
971eaf19b0cSMiao-chen Chou case QCA_WCN3991:
972eaf19b0cSMiao-chen Chou case QCA_WCN3998:
973d8f97da1SVenkata Lakshmi Narayana Gubba case QCA_WCN6750:
974eaf19b0cSMiao-chen Chou hci_set_msft_opcode(hdev, 0xFD70);
975eaf19b0cSMiao-chen Chou break;
976eaf19b0cSMiao-chen Chou default:
977eaf19b0cSMiao-chen Chou break;
978eaf19b0cSMiao-chen Chou }
979eaf19b0cSMiao-chen Chou
98083e81961SBen Young Tae Kim /* Perform HCI reset */
981ba493d4fSBalakrishna Godavarthi err = qca_send_reset(hdev);
98283e81961SBen Young Tae Kim if (err < 0) {
983ba493d4fSBalakrishna Godavarthi bt_dev_err(hdev, "QCA Failed to run HCI_RESET (%d)", err);
98483e81961SBen Young Tae Kim return err;
98583e81961SBen Young Tae Kim }
98683e81961SBen Young Tae Kim
987095327feSSteev Klimaszewski switch (soc_type) {
988095327feSSteev Klimaszewski case QCA_WCN3991:
989095327feSSteev Klimaszewski case QCA_WCN6750:
990095327feSSteev Klimaszewski case QCA_WCN6855:
991e0c1278aSNeil Armstrong case QCA_WCN7850:
992c0187b0bSVenkata Lakshmi Narayana Gubba /* get fw build info */
993c0187b0bSVenkata Lakshmi Narayana Gubba err = qca_read_fw_build_info(hdev);
994c0187b0bSVenkata Lakshmi Narayana Gubba if (err < 0)
995c0187b0bSVenkata Lakshmi Narayana Gubba return err;
996095327feSSteev Klimaszewski break;
997095327feSSteev Klimaszewski default:
998095327feSSteev Klimaszewski break;
999c0187b0bSVenkata Lakshmi Narayana Gubba }
1000c0187b0bSVenkata Lakshmi Narayana Gubba
10019d23305fSJohan Hovold err = qca_check_bdaddr(hdev, &config);
10023019a9d3SJohan Hovold if (err)
10033019a9d3SJohan Hovold return err;
10043019a9d3SJohan Hovold
1005ba493d4fSBalakrishna Godavarthi bt_dev_info(hdev, "QCA setup on UART is completed");
100683e81961SBen Young Tae Kim
100783e81961SBen Young Tae Kim return 0;
100883e81961SBen Young Tae Kim }
1009ba493d4fSBalakrishna Godavarthi EXPORT_SYMBOL_GPL(qca_uart_setup);
101083e81961SBen Young Tae Kim
qca_set_bdaddr(struct hci_dev * hdev,const bdaddr_t * bdaddr)10115c0a1001SBalakrishna Godavarthi int qca_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
10125c0a1001SBalakrishna Godavarthi {
10131622e563SJohan Hovold bdaddr_t bdaddr_swapped;
10145c0a1001SBalakrishna Godavarthi struct sk_buff *skb;
10155c0a1001SBalakrishna Godavarthi int err;
10165c0a1001SBalakrishna Godavarthi
10171622e563SJohan Hovold baswap(&bdaddr_swapped, bdaddr);
10181622e563SJohan Hovold
10191622e563SJohan Hovold skb = __hci_cmd_sync_ev(hdev, EDL_WRITE_BD_ADDR_OPCODE, 6,
10201622e563SJohan Hovold &bdaddr_swapped, HCI_EV_VENDOR,
10211622e563SJohan Hovold HCI_INIT_TIMEOUT);
10225c0a1001SBalakrishna Godavarthi if (IS_ERR(skb)) {
10235c0a1001SBalakrishna Godavarthi err = PTR_ERR(skb);
10245c0a1001SBalakrishna Godavarthi bt_dev_err(hdev, "QCA Change address cmd failed (%d)", err);
10255c0a1001SBalakrishna Godavarthi return err;
10265c0a1001SBalakrishna Godavarthi }
10275c0a1001SBalakrishna Godavarthi
10285c0a1001SBalakrishna Godavarthi kfree_skb(skb);
10295c0a1001SBalakrishna Godavarthi
10305c0a1001SBalakrishna Godavarthi return 0;
10315c0a1001SBalakrishna Godavarthi }
10325c0a1001SBalakrishna Godavarthi EXPORT_SYMBOL_GPL(qca_set_bdaddr);
10335c0a1001SBalakrishna Godavarthi
1034523760b7SHarish Bandi
103583e81961SBen Young Tae Kim MODULE_AUTHOR("Ben Young Tae Kim <ytkim@qca.qualcomm.com>");
103683e81961SBen Young Tae Kim MODULE_DESCRIPTION("Bluetooth support for Qualcomm Atheros family ver " VERSION);
103783e81961SBen Young Tae Kim MODULE_VERSION(VERSION);
103883e81961SBen Young Tae Kim MODULE_LICENSE("GPL");
1039