diff options
author | Ben Young Tae Kim <ytkim@qca.qualcomm.com> | 2015-08-10 17:24:12 -0400 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2015-08-10 17:52:20 -0400 |
commit | 83e81961ff7ef75f97756f316caea5aa6bcc19cc (patch) | |
tree | dcc4bad3f44bcda3eaf3c1b2d6e94be057f5512b /drivers/bluetooth/btqca.c | |
parent | fa1422207756833096b54356a539e3a7d7edec4f (diff) |
Bluetooth: btqca: Introduce generic QCA ROME support
This is for supporting BT for QCA ROME with vendor specific
HCI commands and initialization on the chip. This will have
USB/UART implementation both, but for now, adding UART vendor
specific commands to patch downloading and set Bluetooth device
address using vendor specific command.
Signed-off-by: Ben Young Tae Kim <ytkim@qca.qualcomm.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'drivers/bluetooth/btqca.c')
-rw-r--r-- | drivers/bluetooth/btqca.c | 392 |
1 files changed, 392 insertions, 0 deletions
diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c new file mode 100644 index 000000000000..4a6208168850 --- /dev/null +++ b/drivers/bluetooth/btqca.c | |||
@@ -0,0 +1,392 @@ | |||
1 | /* | ||
2 | * Bluetooth supports for Qualcomm Atheros chips | ||
3 | * | ||
4 | * Copyright (c) 2015 The Linux Foundation. All rights reserved. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 | ||
8 | * as published by the Free Software Foundation | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | * | ||
19 | */ | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/firmware.h> | ||
22 | |||
23 | #include <net/bluetooth/bluetooth.h> | ||
24 | #include <net/bluetooth/hci_core.h> | ||
25 | |||
26 | #include "btqca.h" | ||
27 | |||
28 | #define VERSION "0.1" | ||
29 | |||
30 | static int rome_patch_ver_req(struct hci_dev *hdev, u32 *rome_version) | ||
31 | { | ||
32 | struct sk_buff *skb; | ||
33 | struct edl_event_hdr *edl; | ||
34 | struct rome_version *ver; | ||
35 | char cmd; | ||
36 | int err = 0; | ||
37 | |||
38 | BT_DBG("%s: ROME Patch Version Request", hdev->name); | ||
39 | |||
40 | cmd = EDL_PATCH_VER_REQ_CMD; | ||
41 | skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, EDL_PATCH_CMD_LEN, | ||
42 | &cmd, HCI_VENDOR_PKT, HCI_INIT_TIMEOUT); | ||
43 | if (IS_ERR(skb)) { | ||
44 | err = PTR_ERR(skb); | ||
45 | BT_ERR("%s: Failed to read version of ROME (%d)", hdev->name, | ||
46 | err); | ||
47 | return err; | ||
48 | } | ||
49 | |||
50 | if (skb->len != sizeof(*edl) + sizeof(*ver)) { | ||
51 | BT_ERR("%s: Version size mismatch len %d", hdev->name, | ||
52 | skb->len); | ||
53 | err = -EILSEQ; | ||
54 | goto out; | ||
55 | } | ||
56 | |||
57 | edl = (struct edl_event_hdr *)(skb->data); | ||
58 | if (!edl || !edl->data) { | ||
59 | BT_ERR("%s: TLV with no header or no data", hdev->name); | ||
60 | err = -EILSEQ; | ||
61 | goto out; | ||
62 | } | ||
63 | |||
64 | if (edl->cresp != EDL_CMD_REQ_RES_EVT || | ||
65 | edl->rtype != EDL_APP_VER_RES_EVT) { | ||
66 | BT_ERR("%s: Wrong packet received %d %d", hdev->name, | ||
67 | edl->cresp, edl->rtype); | ||
68 | err = -EIO; | ||
69 | goto out; | ||
70 | } | ||
71 | |||
72 | ver = (struct rome_version *)(edl->data); | ||
73 | |||
74 | BT_DBG("%s: Product:0x%08x", hdev->name, le32_to_cpu(ver->product_id)); | ||
75 | BT_DBG("%s: Patch :0x%08x", hdev->name, le16_to_cpu(ver->patch_ver)); | ||
76 | BT_DBG("%s: ROM :0x%08x", hdev->name, le16_to_cpu(ver->rome_ver)); | ||
77 | BT_DBG("%s: SOC :0x%08x", hdev->name, le32_to_cpu(ver->soc_id)); | ||
78 | |||
79 | /* ROME chipset version can be decided by patch and SoC | ||
80 | * version, combination with upper 2 bytes from SoC | ||
81 | * and lower 2 bytes from patch will be used. | ||
82 | */ | ||
83 | *rome_version = (le32_to_cpu(ver->soc_id) << 16) | | ||
84 | (le16_to_cpu(ver->rome_ver) & 0x0000ffff); | ||
85 | |||
86 | out: | ||
87 | kfree_skb(skb); | ||
88 | |||
89 | return err; | ||
90 | } | ||
91 | |||
92 | static int rome_reset(struct hci_dev *hdev) | ||
93 | { | ||
94 | struct sk_buff *skb; | ||
95 | int err; | ||
96 | |||
97 | BT_DBG("%s: ROME HCI_RESET", hdev->name); | ||
98 | |||
99 | skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); | ||
100 | if (IS_ERR(skb)) { | ||
101 | err = PTR_ERR(skb); | ||
102 | BT_ERR("%s: Reset failed (%d)", hdev->name, err); | ||
103 | return err; | ||
104 | } | ||
105 | |||
106 | kfree_skb(skb); | ||
107 | |||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | static void rome_tlv_check_data(struct rome_config *config, | ||
112 | const struct firmware *fw) | ||
113 | { | ||
114 | const u8 *data; | ||
115 | u32 type_len; | ||
116 | u16 tag_id, tag_len; | ||
117 | int idx, length; | ||
118 | struct tlv_type_hdr *tlv; | ||
119 | struct tlv_type_patch *tlv_patch; | ||
120 | struct tlv_type_nvm *tlv_nvm; | ||
121 | |||
122 | tlv = (struct tlv_type_hdr *)fw->data; | ||
123 | |||
124 | type_len = le32_to_cpu(tlv->type_len); | ||
125 | length = (type_len >> 8) & 0x00ffffff; | ||
126 | |||
127 | BT_DBG("TLV Type\t\t : 0x%x", type_len & 0x000000ff); | ||
128 | BT_DBG("Length\t\t : %d bytes", length); | ||
129 | |||
130 | switch (config->type) { | ||
131 | case TLV_TYPE_PATCH: | ||
132 | tlv_patch = (struct tlv_type_patch *)tlv->data; | ||
133 | BT_DBG("Total Length\t\t : %d bytes", | ||
134 | le32_to_cpu(tlv_patch->total_size)); | ||
135 | BT_DBG("Patch Data Length\t : %d bytes", | ||
136 | le32_to_cpu(tlv_patch->data_length)); | ||
137 | BT_DBG("Signing Format Version : 0x%x", | ||
138 | tlv_patch->format_version); | ||
139 | BT_DBG("Signature Algorithm\t : 0x%x", | ||
140 | tlv_patch->signature); | ||
141 | BT_DBG("Reserved\t\t : 0x%x", | ||
142 | le16_to_cpu(tlv_patch->reserved1)); | ||
143 | BT_DBG("Product ID\t\t : 0x%04x", | ||
144 | le16_to_cpu(tlv_patch->product_id)); | ||
145 | BT_DBG("Rom Build Version\t : 0x%04x", | ||
146 | le16_to_cpu(tlv_patch->rom_build)); | ||
147 | BT_DBG("Patch Version\t\t : 0x%04x", | ||
148 | le16_to_cpu(tlv_patch->patch_version)); | ||
149 | BT_DBG("Reserved\t\t : 0x%x", | ||
150 | le16_to_cpu(tlv_patch->reserved2)); | ||
151 | BT_DBG("Patch Entry Address\t : 0x%x", | ||
152 | le32_to_cpu(tlv_patch->entry)); | ||
153 | break; | ||
154 | |||
155 | case TLV_TYPE_NVM: | ||
156 | idx = 0; | ||
157 | data = tlv->data; | ||
158 | while (idx < length) { | ||
159 | tlv_nvm = (struct tlv_type_nvm *)(data + idx); | ||
160 | |||
161 | tag_id = le16_to_cpu(tlv_nvm->tag_id); | ||
162 | tag_len = le16_to_cpu(tlv_nvm->tag_len); | ||
163 | |||
164 | /* Update NVM tags as needed */ | ||
165 | switch (tag_id) { | ||
166 | case EDL_TAG_ID_HCI: | ||
167 | /* HCI transport layer parameters | ||
168 | * enabling software inband sleep | ||
169 | * onto controller side. | ||
170 | */ | ||
171 | tlv_nvm->data[0] |= 0x80; | ||
172 | |||
173 | /* UART Baud Rate */ | ||
174 | tlv_nvm->data[2] = config->user_baud_rate; | ||
175 | |||
176 | break; | ||
177 | |||
178 | case EDL_TAG_ID_DEEP_SLEEP: | ||
179 | /* Sleep enable mask | ||
180 | * enabling deep sleep feature on controller. | ||
181 | */ | ||
182 | tlv_nvm->data[0] |= 0x01; | ||
183 | |||
184 | break; | ||
185 | } | ||
186 | |||
187 | idx += (sizeof(u16) + sizeof(u16) + 8 + tag_len); | ||
188 | } | ||
189 | break; | ||
190 | |||
191 | default: | ||
192 | BT_ERR("Unknown TLV type %d", config->type); | ||
193 | break; | ||
194 | } | ||
195 | } | ||
196 | |||
197 | static int rome_tlv_send_segment(struct hci_dev *hdev, int idx, int seg_size, | ||
198 | const u8 *data) | ||
199 | { | ||
200 | struct sk_buff *skb; | ||
201 | struct edl_event_hdr *edl; | ||
202 | struct tlv_seg_resp *tlv_resp; | ||
203 | u8 cmd[MAX_SIZE_PER_TLV_SEGMENT + 2]; | ||
204 | int err = 0; | ||
205 | |||
206 | BT_DBG("%s: Download segment #%d size %d", hdev->name, idx, seg_size); | ||
207 | |||
208 | cmd[0] = EDL_PATCH_TLV_REQ_CMD; | ||
209 | cmd[1] = seg_size; | ||
210 | memcpy(cmd + 2, data, seg_size); | ||
211 | |||
212 | skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, seg_size + 2, cmd, | ||
213 | HCI_VENDOR_PKT, HCI_INIT_TIMEOUT); | ||
214 | if (IS_ERR(skb)) { | ||
215 | err = PTR_ERR(skb); | ||
216 | BT_ERR("%s: Failed to send TLV segment (%d)", hdev->name, err); | ||
217 | return err; | ||
218 | } | ||
219 | |||
220 | if (skb->len != sizeof(*edl) + sizeof(*tlv_resp)) { | ||
221 | BT_ERR("%s: TLV response size mismatch", hdev->name); | ||
222 | err = -EILSEQ; | ||
223 | goto out; | ||
224 | } | ||
225 | |||
226 | edl = (struct edl_event_hdr *)(skb->data); | ||
227 | if (!edl || !edl->data) { | ||
228 | BT_ERR("%s: TLV with no header or no data", hdev->name); | ||
229 | err = -EILSEQ; | ||
230 | goto out; | ||
231 | } | ||
232 | |||
233 | tlv_resp = (struct tlv_seg_resp *)(edl->data); | ||
234 | |||
235 | if (edl->cresp != EDL_CMD_REQ_RES_EVT || | ||
236 | edl->rtype != EDL_TVL_DNLD_RES_EVT || tlv_resp->result != 0x00) { | ||
237 | BT_ERR("%s: TLV with error stat 0x%x rtype 0x%x (0x%x)", | ||
238 | hdev->name, edl->cresp, edl->rtype, tlv_resp->result); | ||
239 | err = -EIO; | ||
240 | } | ||
241 | |||
242 | out: | ||
243 | kfree_skb(skb); | ||
244 | |||
245 | return err; | ||
246 | } | ||
247 | |||
248 | static int rome_tlv_download_request(struct hci_dev *hdev, | ||
249 | const struct firmware *fw) | ||
250 | { | ||
251 | const u8 *buffer, *data; | ||
252 | int total_segment, remain_size; | ||
253 | int ret, i; | ||
254 | |||
255 | if (!fw || !fw->data) | ||
256 | return -EINVAL; | ||
257 | |||
258 | total_segment = fw->size / MAX_SIZE_PER_TLV_SEGMENT; | ||
259 | remain_size = fw->size % MAX_SIZE_PER_TLV_SEGMENT; | ||
260 | |||
261 | BT_DBG("%s: Total segment num %d remain size %d total size %zu", | ||
262 | hdev->name, total_segment, remain_size, fw->size); | ||
263 | |||
264 | data = fw->data; | ||
265 | for (i = 0; i < total_segment; i++) { | ||
266 | buffer = data + i * MAX_SIZE_PER_TLV_SEGMENT; | ||
267 | ret = rome_tlv_send_segment(hdev, i, MAX_SIZE_PER_TLV_SEGMENT, | ||
268 | buffer); | ||
269 | if (ret < 0) | ||
270 | return -EIO; | ||
271 | } | ||
272 | |||
273 | if (remain_size) { | ||
274 | buffer = data + total_segment * MAX_SIZE_PER_TLV_SEGMENT; | ||
275 | ret = rome_tlv_send_segment(hdev, total_segment, remain_size, | ||
276 | buffer); | ||
277 | if (ret < 0) | ||
278 | return -EIO; | ||
279 | } | ||
280 | |||
281 | return 0; | ||
282 | } | ||
283 | |||
284 | static int rome_download_firmware(struct hci_dev *hdev, | ||
285 | struct rome_config *config) | ||
286 | { | ||
287 | const struct firmware *fw; | ||
288 | int ret; | ||
289 | |||
290 | BT_INFO("%s: ROME Downloading %s", hdev->name, config->fwname); | ||
291 | |||
292 | ret = request_firmware(&fw, config->fwname, &hdev->dev); | ||
293 | if (ret) { | ||
294 | BT_ERR("%s: Failed to request file: %s (%d)", hdev->name, | ||
295 | config->fwname, ret); | ||
296 | return ret; | ||
297 | } | ||
298 | |||
299 | rome_tlv_check_data(config, fw); | ||
300 | |||
301 | ret = rome_tlv_download_request(hdev, fw); | ||
302 | if (ret) { | ||
303 | BT_ERR("%s: Failed to download file: %s (%d)", hdev->name, | ||
304 | config->fwname, ret); | ||
305 | } | ||
306 | |||
307 | release_firmware(fw); | ||
308 | |||
309 | return ret; | ||
310 | } | ||
311 | |||
312 | int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr) | ||
313 | { | ||
314 | struct sk_buff *skb; | ||
315 | u8 cmd[9]; | ||
316 | int err; | ||
317 | |||
318 | cmd[0] = EDL_NVM_ACCESS_SET_REQ_CMD; | ||
319 | cmd[1] = 0x02; /* TAG ID */ | ||
320 | cmd[2] = sizeof(bdaddr_t); /* size */ | ||
321 | memcpy(cmd + 3, bdaddr, sizeof(bdaddr_t)); | ||
322 | skb = __hci_cmd_sync_ev(hdev, EDL_NVM_ACCESS_OPCODE, sizeof(cmd), cmd, | ||
323 | HCI_VENDOR_PKT, HCI_INIT_TIMEOUT); | ||
324 | if (IS_ERR(skb)) { | ||
325 | err = PTR_ERR(skb); | ||
326 | BT_ERR("%s: Change address command failed (%d)", | ||
327 | hdev->name, err); | ||
328 | return err; | ||
329 | } | ||
330 | |||
331 | kfree_skb(skb); | ||
332 | |||
333 | return 0; | ||
334 | } | ||
335 | EXPORT_SYMBOL_GPL(qca_set_bdaddr_rome); | ||
336 | |||
337 | int qca_uart_setup_rome(struct hci_dev *hdev, uint8_t baudrate) | ||
338 | { | ||
339 | u32 rome_ver = 0; | ||
340 | struct rome_config config; | ||
341 | int err; | ||
342 | |||
343 | BT_DBG("%s: ROME setup on UART", hdev->name); | ||
344 | |||
345 | config.user_baud_rate = baudrate; | ||
346 | |||
347 | /* Get ROME version information */ | ||
348 | err = rome_patch_ver_req(hdev, &rome_ver); | ||
349 | if (err < 0 || rome_ver == 0) { | ||
350 | BT_ERR("%s: Failed to get version 0x%x", hdev->name, err); | ||
351 | return err; | ||
352 | } | ||
353 | |||
354 | BT_INFO("%s: ROME controller version 0x%08x", hdev->name, rome_ver); | ||
355 | |||
356 | /* Download rampatch file */ | ||
357 | config.type = TLV_TYPE_PATCH; | ||
358 | snprintf(config.fwname, sizeof(config.fwname), "qca/rampatch_%08x.bin", | ||
359 | rome_ver); | ||
360 | err = rome_download_firmware(hdev, &config); | ||
361 | if (err < 0) { | ||
362 | BT_ERR("%s: Failed to download patch (%d)", hdev->name, err); | ||
363 | return err; | ||
364 | } | ||
365 | |||
366 | /* Download NVM configuration */ | ||
367 | config.type = TLV_TYPE_NVM; | ||
368 | snprintf(config.fwname, sizeof(config.fwname), "qca/nvm_%08x.bin", | ||
369 | rome_ver); | ||
370 | err = rome_download_firmware(hdev, &config); | ||
371 | if (err < 0) { | ||
372 | BT_ERR("%s: Failed to download NVM (%d)", hdev->name, err); | ||
373 | return err; | ||
374 | } | ||
375 | |||
376 | /* Perform HCI reset */ | ||
377 | err = rome_reset(hdev); | ||
378 | if (err < 0) { | ||
379 | BT_ERR("%s: Failed to run HCI_RESET (%d)", hdev->name, err); | ||
380 | return err; | ||
381 | } | ||
382 | |||
383 | BT_INFO("%s: ROME setup on UART is completed", hdev->name); | ||
384 | |||
385 | return 0; | ||
386 | } | ||
387 | EXPORT_SYMBOL_GPL(qca_uart_setup_rome); | ||
388 | |||
389 | MODULE_AUTHOR("Ben Young Tae Kim <ytkim@qca.qualcomm.com>"); | ||
390 | MODULE_DESCRIPTION("Bluetooth support for Qualcomm Atheros family ver " VERSION); | ||
391 | MODULE_VERSION(VERSION); | ||
392 | MODULE_LICENSE("GPL"); | ||