Commit | Line | Data |
---|---|---|
789221ec BZ |
1 | /** |
2 | * Marvell BT-over-SDIO driver: SDIO interface related functions. | |
3 | * | |
4 | * Copyright (C) 2009, Marvell International Ltd. | |
5 | * | |
6 | * This software file (the "File") is distributed by Marvell International | |
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | |
8 | * (the "License"). You may use, redistribute and/or modify this File in | |
9 | * accordance with the terms and conditions of the License, a copy of which | |
10 | * is available by writing to the Free Software Foundation, Inc., | |
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | |
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | |
13 | * | |
14 | * | |
15 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | |
16 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | |
17 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | |
18 | * this warranty disclaimer. | |
19 | **/ | |
20 | ||
21 | #include <linux/firmware.h> | |
22 | ||
23 | #include <linux/mmc/sdio_ids.h> | |
24 | #include <linux/mmc/sdio_func.h> | |
25 | ||
26 | #include <net/bluetooth/bluetooth.h> | |
27 | #include <net/bluetooth/hci_core.h> | |
28 | ||
29 | #include "btmrvl_drv.h" | |
30 | #include "btmrvl_sdio.h" | |
31 | ||
32 | #define VERSION "1.0" | |
33 | ||
789221ec BZ |
34 | /* The btmrvl_sdio_remove() callback function is called |
35 | * when user removes this module from kernel space or ejects | |
36 | * the card from the slot. The driver handles these 2 cases | |
37 | * differently. | |
38 | * If the user is removing the module, a MODULE_SHUTDOWN_REQ | |
39 | * command is sent to firmware and interrupt will be disabled. | |
40 | * If the card is removed, there is no need to send command | |
41 | * or disable interrupt. | |
42 | * | |
43 | * The variable 'user_rmmod' is used to distinguish these two | |
44 | * scenarios. This flag is initialized as FALSE in case the card | |
45 | * is removed, and will be set to TRUE for module removal when | |
46 | * module_exit function is called. | |
47 | */ | |
48 | static u8 user_rmmod; | |
49 | ||
dcf47f3b MH |
50 | static const struct btmrvl_sdio_device btmrvl_sdio_sd6888 = { |
51 | .helper = "sd8688_helper.bin", | |
52 | .firmware = "sd8688.bin", | |
789221ec BZ |
53 | }; |
54 | ||
dcf47f3b MH |
55 | static const struct sdio_device_id btmrvl_sdio_ids[] = { |
56 | /* Marvell SD8688 Bluetooth device */ | |
57 | { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9105), | |
58 | .driver_data = (unsigned long) &btmrvl_sdio_sd6888 }, | |
789221ec | 59 | |
dcf47f3b | 60 | { } /* Terminating entry */ |
789221ec BZ |
61 | }; |
62 | ||
dcf47f3b MH |
63 | MODULE_DEVICE_TABLE(sdio, btmrvl_sdio_ids); |
64 | ||
789221ec BZ |
65 | static int btmrvl_sdio_get_rx_unit(struct btmrvl_sdio_card *card) |
66 | { | |
67 | u8 reg; | |
68 | int ret; | |
69 | ||
70 | BT_DBG("Enter"); | |
71 | ||
72 | reg = sdio_readb(card->func, CARD_RX_UNIT_REG, &ret); | |
73 | if (!ret) | |
74 | card->rx_unit = reg; | |
75 | ||
76 | BT_DBG("Leave"); | |
77 | ||
78 | return ret; | |
79 | } | |
80 | ||
81 | static int btmrvl_sdio_read_fw_status(struct btmrvl_sdio_card *card, u16 *dat) | |
82 | { | |
83 | int ret; | |
84 | u8 fws0, fws1; | |
85 | ||
86 | BT_DBG("Enter"); | |
87 | ||
88 | *dat = 0; | |
89 | ||
90 | fws0 = sdio_readb(card->func, CARD_FW_STATUS0_REG, &ret); | |
91 | ||
92 | if (!ret) | |
93 | fws1 = sdio_readb(card->func, CARD_FW_STATUS1_REG, &ret); | |
94 | ||
95 | if (ret) { | |
96 | BT_DBG("Leave"); | |
97 | return -EIO; | |
98 | } | |
99 | ||
100 | *dat = (((u16) fws1) << 8) | fws0; | |
101 | ||
102 | BT_DBG("Leave"); | |
103 | ||
104 | return 0; | |
105 | } | |
106 | ||
107 | static int btmrvl_sdio_read_rx_len(struct btmrvl_sdio_card *card, u16 *dat) | |
108 | { | |
109 | int ret; | |
110 | u8 reg; | |
111 | ||
112 | BT_DBG("Enter"); | |
113 | ||
114 | reg = sdio_readb(card->func, CARD_RX_LEN_REG, &ret); | |
115 | if (!ret) | |
116 | *dat = (u16) reg << card->rx_unit; | |
117 | ||
118 | BT_DBG("Leave"); | |
119 | ||
120 | return ret; | |
121 | } | |
122 | ||
123 | static int btmrvl_sdio_enable_host_int_mask(struct btmrvl_sdio_card *card, | |
dcf47f3b | 124 | u8 mask) |
789221ec BZ |
125 | { |
126 | int ret; | |
127 | ||
128 | BT_DBG("Enter"); | |
129 | ||
130 | sdio_writeb(card->func, mask, HOST_INT_MASK_REG, &ret); | |
131 | if (ret) { | |
132 | BT_ERR("Unable to enable the host interrupt!"); | |
133 | ret = -EIO; | |
134 | } | |
135 | ||
136 | BT_DBG("Leave"); | |
137 | ||
138 | return ret; | |
139 | } | |
140 | ||
141 | static int btmrvl_sdio_disable_host_int_mask(struct btmrvl_sdio_card *card, | |
dcf47f3b | 142 | u8 mask) |
789221ec BZ |
143 | { |
144 | int ret; | |
145 | u8 host_int_mask; | |
146 | ||
147 | BT_DBG("Enter"); | |
148 | ||
149 | host_int_mask = sdio_readb(card->func, HOST_INT_MASK_REG, &ret); | |
150 | if (ret) { | |
151 | ret = -EIO; | |
152 | goto done; | |
153 | } | |
154 | ||
155 | host_int_mask &= ~mask; | |
156 | ||
157 | sdio_writeb(card->func, host_int_mask, HOST_INT_MASK_REG, &ret); | |
158 | if (ret < 0) { | |
159 | BT_ERR("Unable to disable the host interrupt!"); | |
160 | ret = -EIO; | |
161 | goto done; | |
162 | } | |
163 | ||
164 | ret = 0; | |
165 | ||
166 | done: | |
167 | BT_DBG("Leave"); | |
168 | ||
169 | return ret; | |
170 | } | |
171 | ||
172 | static int btmrvl_sdio_poll_card_status(struct btmrvl_sdio_card *card, u8 bits) | |
173 | { | |
174 | unsigned int tries; | |
175 | int ret; | |
176 | u8 status; | |
177 | ||
178 | BT_DBG("Enter"); | |
179 | ||
180 | for (tries = 0; tries < MAX_POLL_TRIES * 1000; tries++) { | |
181 | status = sdio_readb(card->func, CARD_STATUS_REG, &ret); | |
182 | if (ret) | |
183 | goto failed; | |
184 | if ((status & bits) == bits) | |
185 | goto done; | |
186 | ||
187 | udelay(1); | |
188 | } | |
189 | ||
190 | ret = -ETIMEDOUT; | |
191 | ||
192 | failed: | |
193 | BT_ERR("FAILED! ret=%d", ret); | |
194 | ||
195 | done: | |
196 | BT_DBG("Leave"); | |
197 | ||
198 | return ret; | |
199 | } | |
200 | ||
201 | static int btmrvl_sdio_verify_fw_download(struct btmrvl_sdio_card *card, | |
dcf47f3b | 202 | int pollnum) |
789221ec BZ |
203 | { |
204 | int ret = -ETIMEDOUT; | |
205 | u16 firmwarestat; | |
206 | unsigned int tries; | |
207 | ||
208 | BT_DBG("Enter"); | |
209 | ||
210 | /* Wait for firmware to become ready */ | |
211 | for (tries = 0; tries < pollnum; tries++) { | |
212 | if (btmrvl_sdio_read_fw_status(card, &firmwarestat) < 0) | |
213 | continue; | |
214 | ||
215 | if (firmwarestat == FIRMWARE_READY) { | |
216 | ret = 0; | |
217 | break; | |
218 | } else { | |
219 | msleep(10); | |
220 | } | |
221 | } | |
222 | ||
223 | BT_DBG("Leave"); | |
224 | ||
225 | return ret; | |
226 | } | |
227 | ||
228 | static int btmrvl_sdio_download_helper(struct btmrvl_sdio_card *card) | |
229 | { | |
230 | const struct firmware *fw_helper = NULL; | |
231 | const u8 *helper = NULL; | |
232 | int ret; | |
233 | void *tmphlprbuf = NULL; | |
234 | int tmphlprbufsz, hlprblknow, helperlen; | |
235 | u8 *helperbuf; | |
236 | u32 tx_len; | |
237 | ||
238 | BT_DBG("Enter"); | |
239 | ||
240 | ret = request_firmware(&fw_helper, card->helper, | |
dcf47f3b | 241 | &card->func->dev); |
789221ec BZ |
242 | if ((ret < 0) || !fw_helper) { |
243 | BT_ERR("request_firmware(helper) failed, error code = %d", | |
dcf47f3b | 244 | ret); |
789221ec BZ |
245 | ret = -ENOENT; |
246 | goto done; | |
247 | } | |
248 | ||
249 | helper = fw_helper->data; | |
250 | helperlen = fw_helper->size; | |
251 | ||
252 | BT_DBG("Downloading helper image (%d bytes), block size %d bytes", | |
dcf47f3b | 253 | helperlen, SDIO_BLOCK_SIZE); |
789221ec BZ |
254 | |
255 | tmphlprbufsz = ALIGN_SZ(BTM_UPLD_SIZE, BTSDIO_DMA_ALIGN); | |
256 | ||
257 | tmphlprbuf = kmalloc(tmphlprbufsz, GFP_KERNEL); | |
258 | if (!tmphlprbuf) { | |
259 | BT_ERR("Unable to allocate buffer for helper." | |
260 | " Terminating download"); | |
261 | ret = -ENOMEM; | |
262 | goto done; | |
263 | } | |
264 | ||
265 | memset(tmphlprbuf, 0, tmphlprbufsz); | |
266 | ||
267 | helperbuf = (u8 *) ALIGN_ADDR(tmphlprbuf, BTSDIO_DMA_ALIGN); | |
268 | ||
269 | /* Perform helper data transfer */ | |
270 | tx_len = (FIRMWARE_TRANSFER_NBLOCK * SDIO_BLOCK_SIZE) | |
271 | - SDIO_HEADER_LEN; | |
272 | hlprblknow = 0; | |
273 | ||
274 | do { | |
275 | ret = btmrvl_sdio_poll_card_status(card, | |
276 | CARD_IO_READY | DN_LD_CARD_RDY); | |
277 | if (ret < 0) { | |
278 | BT_ERR("Helper download poll status timeout @ %d", | |
279 | hlprblknow); | |
280 | goto done; | |
281 | } | |
282 | ||
283 | /* Check if there is more data? */ | |
284 | if (hlprblknow >= helperlen) | |
285 | break; | |
286 | ||
287 | if (helperlen - hlprblknow < tx_len) | |
288 | tx_len = helperlen - hlprblknow; | |
289 | ||
290 | /* Little-endian */ | |
291 | helperbuf[0] = ((tx_len & 0x000000ff) >> 0); | |
292 | helperbuf[1] = ((tx_len & 0x0000ff00) >> 8); | |
293 | helperbuf[2] = ((tx_len & 0x00ff0000) >> 16); | |
294 | helperbuf[3] = ((tx_len & 0xff000000) >> 24); | |
295 | ||
296 | memcpy(&helperbuf[SDIO_HEADER_LEN], &helper[hlprblknow], | |
297 | tx_len); | |
298 | ||
299 | /* Now send the data */ | |
dcf47f3b MH |
300 | ret = sdio_writesb(card->func, card->ioport, helperbuf, |
301 | FIRMWARE_TRANSFER_NBLOCK * SDIO_BLOCK_SIZE); | |
789221ec BZ |
302 | if (ret < 0) { |
303 | BT_ERR("IO error during helper download @ %d", | |
304 | hlprblknow); | |
305 | goto done; | |
306 | } | |
307 | ||
308 | hlprblknow += tx_len; | |
309 | } while (true); | |
310 | ||
311 | BT_DBG("Transferring helper image EOF block"); | |
312 | ||
313 | memset(helperbuf, 0x0, SDIO_BLOCK_SIZE); | |
314 | ||
315 | ret = sdio_writesb(card->func, card->ioport, helperbuf, | |
dcf47f3b | 316 | SDIO_BLOCK_SIZE); |
789221ec BZ |
317 | if (ret < 0) { |
318 | BT_ERR("IO error in writing helper image EOF block"); | |
319 | goto done; | |
320 | } | |
321 | ||
322 | ret = 0; | |
323 | ||
324 | done: | |
325 | kfree(tmphlprbuf); | |
326 | if (fw_helper) | |
327 | release_firmware(fw_helper); | |
328 | ||
329 | BT_DBG("Leave"); | |
330 | ||
331 | return ret; | |
332 | } | |
333 | ||
334 | static int btmrvl_sdio_download_fw_w_helper(struct btmrvl_sdio_card *card) | |
335 | { | |
336 | const struct firmware *fw_firmware = NULL; | |
337 | const u8 *firmware = NULL; | |
338 | int firmwarelen, tmpfwbufsz, ret; | |
339 | unsigned int tries, offset; | |
340 | u8 base0, base1; | |
341 | void *tmpfwbuf = NULL; | |
342 | u8 *fwbuf; | |
343 | u16 len; | |
344 | int txlen = 0, tx_blocks = 0, count = 0; | |
345 | ||
346 | BT_DBG("Enter"); | |
347 | ||
348 | ret = request_firmware(&fw_firmware, card->firmware, | |
dcf47f3b | 349 | &card->func->dev); |
789221ec BZ |
350 | if ((ret < 0) || !fw_firmware) { |
351 | BT_ERR("request_firmware(firmware) failed, error code = %d", | |
dcf47f3b | 352 | ret); |
789221ec BZ |
353 | ret = -ENOENT; |
354 | goto done; | |
355 | } | |
356 | ||
357 | firmware = fw_firmware->data; | |
358 | firmwarelen = fw_firmware->size; | |
359 | ||
360 | BT_DBG("Downloading FW image (%d bytes)", firmwarelen); | |
361 | ||
362 | tmpfwbufsz = ALIGN_SZ(BTM_UPLD_SIZE, BTSDIO_DMA_ALIGN); | |
363 | tmpfwbuf = kmalloc(tmpfwbufsz, GFP_KERNEL); | |
364 | if (!tmpfwbuf) { | |
365 | BT_ERR("Unable to allocate buffer for firmware." | |
366 | " Terminating download"); | |
367 | ret = -ENOMEM; | |
368 | goto done; | |
369 | } | |
370 | ||
371 | memset(tmpfwbuf, 0, tmpfwbufsz); | |
372 | ||
373 | /* Ensure aligned firmware buffer */ | |
374 | fwbuf = (u8 *) ALIGN_ADDR(tmpfwbuf, BTSDIO_DMA_ALIGN); | |
375 | ||
376 | /* Perform firmware data transfer */ | |
377 | offset = 0; | |
378 | do { | |
379 | ret = btmrvl_sdio_poll_card_status(card, | |
dcf47f3b | 380 | CARD_IO_READY | DN_LD_CARD_RDY); |
789221ec BZ |
381 | if (ret < 0) { |
382 | BT_ERR("FW download with helper poll status" | |
dcf47f3b | 383 | " timeout @ %d", offset); |
789221ec BZ |
384 | goto done; |
385 | } | |
386 | ||
387 | /* Check if there is more data ? */ | |
388 | if (offset >= firmwarelen) | |
389 | break; | |
390 | ||
391 | for (tries = 0; tries < MAX_POLL_TRIES; tries++) { | |
392 | base0 = sdio_readb(card->func, | |
393 | SQ_READ_BASE_ADDRESS_A0_REG, &ret); | |
394 | if (ret) { | |
395 | BT_ERR("BASE0 register read failed:" | |
396 | " base0 = 0x%04X(%d)." | |
397 | " Terminating download", | |
398 | base0, base0); | |
399 | ret = -EIO; | |
400 | goto done; | |
401 | } | |
402 | base1 = sdio_readb(card->func, | |
403 | SQ_READ_BASE_ADDRESS_A1_REG, &ret); | |
404 | if (ret) { | |
405 | BT_ERR("BASE1 register read failed:" | |
406 | " base1 = 0x%04X(%d)." | |
407 | " Terminating download", | |
408 | base1, base1); | |
409 | ret = -EIO; | |
410 | goto done; | |
411 | } | |
412 | ||
413 | len = (((u16) base1) << 8) | base0; | |
414 | if (len) | |
415 | break; | |
416 | ||
417 | udelay(10); | |
418 | } | |
419 | ||
420 | if (!len) | |
421 | break; | |
422 | else if (len > BTM_UPLD_SIZE) { | |
423 | BT_ERR("FW download failure @%d, invalid length %d", | |
dcf47f3b | 424 | offset, len); |
789221ec BZ |
425 | ret = -EINVAL; |
426 | goto done; | |
427 | } | |
428 | ||
429 | txlen = len; | |
430 | ||
431 | if (len & BIT(0)) { | |
432 | count++; | |
433 | if (count > MAX_WRITE_IOMEM_RETRY) { | |
434 | BT_ERR("FW download failure @%d, " | |
435 | "over max retry count", offset); | |
436 | ret = -EIO; | |
437 | goto done; | |
438 | } | |
439 | BT_ERR("FW CRC error indicated by the helper: " | |
440 | "len = 0x%04X, txlen = %d", len, txlen); | |
441 | len &= ~BIT(0); | |
442 | /* Set txlen to 0 so as to resend from same offset */ | |
443 | txlen = 0; | |
444 | } else { | |
445 | count = 0; | |
446 | ||
447 | /* Last block ? */ | |
448 | if (firmwarelen - offset < txlen) | |
449 | txlen = firmwarelen - offset; | |
450 | ||
451 | tx_blocks = | |
452 | (txlen + SDIO_BLOCK_SIZE - 1) / SDIO_BLOCK_SIZE; | |
453 | ||
454 | memcpy(fwbuf, &firmware[offset], txlen); | |
455 | } | |
456 | ||
457 | ret = sdio_writesb(card->func, card->ioport, fwbuf, | |
458 | tx_blocks * SDIO_BLOCK_SIZE); | |
459 | ||
460 | if (ret < 0) { | |
461 | BT_ERR("FW download, writesb(%d) failed @%d", | |
dcf47f3b | 462 | count, offset); |
789221ec | 463 | sdio_writeb(card->func, HOST_CMD53_FIN, CONFIG_REG, |
dcf47f3b | 464 | &ret); |
789221ec BZ |
465 | if (ret) |
466 | BT_ERR("writeb failed (CFG)"); | |
467 | } | |
468 | ||
469 | offset += txlen; | |
470 | } while (true); | |
471 | ||
472 | BT_DBG("FW download over, size %d bytes", offset); | |
473 | ||
474 | ret = 0; | |
475 | ||
476 | done: | |
477 | kfree(tmpfwbuf); | |
478 | ||
479 | if (fw_firmware) | |
480 | release_firmware(fw_firmware); | |
481 | ||
482 | BT_DBG("Leave"); | |
483 | ||
484 | return ret; | |
485 | } | |
486 | ||
487 | static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv) | |
488 | { | |
489 | u16 buf_len = 0; | |
490 | int ret, buf_block_len, blksz; | |
491 | struct sk_buff *skb = NULL; | |
492 | u32 type; | |
493 | u8 *payload = NULL; | |
494 | struct hci_dev *hdev = priv->btmrvl_dev.hcidev; | |
495 | struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; | |
496 | ||
497 | BT_DBG("Enter"); | |
498 | ||
499 | if (!card || !card->func) { | |
500 | BT_ERR("card or function is NULL!"); | |
501 | ret = -EINVAL; | |
502 | goto exit; | |
503 | } | |
504 | ||
505 | /* Read the length of data to be transferred */ | |
506 | ret = btmrvl_sdio_read_rx_len(card, &buf_len); | |
507 | if (ret < 0) { | |
508 | BT_ERR("read rx_len failed"); | |
509 | ret = -EIO; | |
510 | goto exit; | |
511 | } | |
512 | ||
513 | blksz = SDIO_BLOCK_SIZE; | |
514 | buf_block_len = (buf_len + blksz - 1) / blksz; | |
515 | ||
516 | if (buf_len <= SDIO_HEADER_LEN | |
dcf47f3b | 517 | || (buf_block_len * blksz) > ALLOC_BUF_SIZE) { |
789221ec BZ |
518 | BT_ERR("invalid packet length: %d", buf_len); |
519 | ret = -EINVAL; | |
520 | goto exit; | |
521 | } | |
522 | ||
523 | /* Allocate buffer */ | |
524 | skb = bt_skb_alloc(buf_block_len * blksz + BTSDIO_DMA_ALIGN, | |
dcf47f3b | 525 | GFP_ATOMIC); |
789221ec BZ |
526 | if (skb == NULL) { |
527 | BT_ERR("No free skb"); | |
528 | goto exit; | |
529 | } | |
530 | ||
531 | if ((u32) skb->data & (BTSDIO_DMA_ALIGN - 1)) { | |
532 | skb_put(skb, (u32) skb->data & (BTSDIO_DMA_ALIGN - 1)); | |
533 | skb_pull(skb, (u32) skb->data & (BTSDIO_DMA_ALIGN - 1)); | |
534 | } | |
535 | ||
536 | payload = skb->tail; | |
537 | ||
538 | ret = sdio_readsb(card->func, payload, card->ioport, | |
539 | buf_block_len * blksz); | |
540 | if (ret < 0) { | |
541 | BT_ERR("readsb failed: %d", ret); | |
542 | ret = -EIO; | |
543 | goto exit; | |
544 | } | |
545 | ||
546 | /* This is SDIO specific header length: byte[2][1][0], type: byte[3] | |
547 | * (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor) | |
548 | */ | |
549 | ||
550 | buf_len = payload[0]; | |
551 | buf_len |= (u16) payload[1] << 8; | |
552 | type = payload[3]; | |
553 | ||
554 | switch (type) { | |
555 | case HCI_ACLDATA_PKT: | |
556 | case HCI_SCODATA_PKT: | |
557 | case HCI_EVENT_PKT: | |
558 | bt_cb(skb)->pkt_type = type; | |
559 | skb->dev = (void *)hdev; | |
560 | skb_put(skb, buf_len); | |
561 | skb_pull(skb, SDIO_HEADER_LEN); | |
562 | ||
563 | if (type == HCI_EVENT_PKT) | |
564 | btmrvl_check_evtpkt(priv, skb); | |
565 | ||
566 | hci_recv_frame(skb); | |
567 | hdev->stat.byte_rx += buf_len; | |
568 | break; | |
569 | ||
570 | case MRVL_VENDOR_PKT: | |
571 | bt_cb(skb)->pkt_type = HCI_VENDOR_PKT; | |
572 | skb->dev = (void *)hdev; | |
573 | skb_put(skb, buf_len); | |
574 | skb_pull(skb, SDIO_HEADER_LEN); | |
575 | ||
576 | if (btmrvl_process_event(priv, skb)) | |
577 | hci_recv_frame(skb); | |
578 | ||
579 | hdev->stat.byte_rx += buf_len; | |
580 | break; | |
581 | ||
582 | default: | |
583 | BT_ERR("Unknow packet type:%d", type); | |
584 | print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, payload, | |
dcf47f3b | 585 | blksz * buf_block_len); |
789221ec BZ |
586 | |
587 | kfree_skb(skb); | |
588 | skb = NULL; | |
589 | break; | |
590 | } | |
591 | ||
592 | exit: | |
593 | if (ret) { | |
594 | hdev->stat.err_rx++; | |
595 | if (skb) | |
596 | kfree_skb(skb); | |
597 | } | |
598 | ||
599 | BT_DBG("Leave"); | |
600 | ||
601 | return ret; | |
602 | } | |
603 | ||
604 | static int btmrvl_sdio_get_int_status(struct btmrvl_private *priv, u8 * ireg) | |
605 | { | |
606 | int ret; | |
607 | u8 sdio_ireg = 0; | |
608 | struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; | |
609 | ||
610 | BT_DBG("Enter"); | |
611 | ||
612 | *ireg = 0; | |
613 | ||
614 | sdio_ireg = sdio_readb(card->func, HOST_INTSTATUS_REG, &ret); | |
615 | if (ret) { | |
616 | BT_ERR("sdio_readb: read int status register failed"); | |
617 | ret = -EIO; | |
618 | goto done; | |
619 | } | |
620 | ||
621 | if (sdio_ireg != 0) { | |
622 | /* | |
623 | * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS | |
624 | * Clear the interrupt status register and re-enable the | |
625 | * interrupt. | |
626 | */ | |
627 | BT_DBG("sdio_ireg = 0x%x", sdio_ireg); | |
628 | ||
629 | sdio_writeb(card->func, ~(sdio_ireg) & (DN_LD_HOST_INT_STATUS | | |
630 | UP_LD_HOST_INT_STATUS), | |
631 | HOST_INTSTATUS_REG, &ret); | |
632 | if (ret) { | |
633 | BT_ERR("sdio_writeb: clear int status register " | |
634 | "failed"); | |
635 | ret = -EIO; | |
636 | goto done; | |
637 | } | |
638 | } | |
639 | ||
640 | if (sdio_ireg & DN_LD_HOST_INT_STATUS) { | |
641 | if (priv->btmrvl_dev.tx_dnld_rdy) | |
642 | BT_DBG("tx_done already received: " | |
643 | " int_status=0x%x", sdio_ireg); | |
644 | else | |
645 | priv->btmrvl_dev.tx_dnld_rdy = true; | |
646 | } | |
647 | ||
648 | if (sdio_ireg & UP_LD_HOST_INT_STATUS) | |
649 | btmrvl_sdio_card_to_host(priv); | |
650 | ||
651 | *ireg = sdio_ireg; | |
652 | ||
653 | ret = 0; | |
654 | ||
655 | done: | |
656 | BT_DBG("Leave"); | |
657 | ||
658 | return ret; | |
659 | } | |
660 | ||
661 | static void btmrvl_sdio_interrupt(struct sdio_func *func) | |
662 | { | |
663 | struct btmrvl_private *priv; | |
664 | struct hci_dev *hcidev; | |
665 | struct btmrvl_sdio_card *card; | |
666 | u8 ireg = 0; | |
667 | ||
668 | BT_DBG("Enter"); | |
669 | ||
670 | card = sdio_get_drvdata(func); | |
671 | if (card && card->priv) { | |
672 | priv = card->priv; | |
673 | hcidev = priv->btmrvl_dev.hcidev; | |
674 | ||
675 | if (btmrvl_sdio_get_int_status(priv, &ireg)) | |
676 | BT_ERR("reading HOST_INT_STATUS_REG failed"); | |
677 | else | |
678 | BT_DBG("HOST_INT_STATUS_REG %#x", ireg); | |
679 | ||
680 | btmrvl_interrupt(priv); | |
681 | } | |
682 | ||
683 | BT_DBG("Leave"); | |
684 | } | |
685 | ||
686 | static int btmrvl_sdio_register_dev(struct btmrvl_sdio_card *card) | |
687 | { | |
789221ec | 688 | struct sdio_func *func; |
dcf47f3b MH |
689 | u8 reg; |
690 | int ret = 0; | |
789221ec BZ |
691 | |
692 | BT_DBG("Enter"); | |
693 | ||
694 | if (!card || !card->func) { | |
695 | BT_ERR("Error: card or function is NULL!"); | |
696 | ret = -EINVAL; | |
697 | goto failed; | |
698 | } | |
699 | ||
700 | func = card->func; | |
701 | ||
789221ec BZ |
702 | sdio_claim_host(func); |
703 | ||
704 | ret = sdio_enable_func(func); | |
705 | if (ret) { | |
706 | BT_ERR("sdio_enable_func() failed: ret=%d", ret); | |
707 | ret = -EIO; | |
708 | goto release_host; | |
709 | } | |
710 | ||
711 | ret = sdio_claim_irq(func, btmrvl_sdio_interrupt); | |
712 | if (ret) { | |
713 | BT_ERR("sdio_claim_irq failed: ret=%d", ret); | |
714 | ret = -EIO; | |
715 | goto disable_func; | |
716 | } | |
717 | ||
718 | ret = sdio_set_block_size(card->func, SDIO_BLOCK_SIZE); | |
719 | if (ret) { | |
720 | BT_ERR("cannot set SDIO block size"); | |
721 | ret = -EIO; | |
722 | goto release_irq; | |
723 | } | |
724 | ||
725 | reg = sdio_readb(func, IO_PORT_0_REG, &ret); | |
726 | if (ret < 0) { | |
727 | ret = -EIO; | |
728 | goto release_irq; | |
729 | } | |
730 | ||
731 | card->ioport = reg; | |
732 | ||
733 | reg = sdio_readb(func, IO_PORT_1_REG, &ret); | |
734 | if (ret < 0) { | |
735 | ret = -EIO; | |
736 | goto release_irq; | |
737 | } | |
738 | ||
739 | card->ioport |= (reg << 8); | |
740 | ||
741 | reg = sdio_readb(func, IO_PORT_2_REG, &ret); | |
742 | if (ret < 0) { | |
743 | ret = -EIO; | |
744 | goto release_irq; | |
745 | } | |
746 | ||
747 | card->ioport |= (reg << 16); | |
748 | ||
749 | BT_DBG("SDIO FUNC%d IO port: 0x%x", func->num, card->ioport); | |
750 | ||
751 | sdio_set_drvdata(func, card); | |
752 | ||
753 | sdio_release_host(func); | |
754 | ||
755 | BT_DBG("Leave"); | |
756 | return 0; | |
757 | ||
758 | release_irq: | |
759 | sdio_release_irq(func); | |
760 | ||
761 | disable_func: | |
762 | sdio_disable_func(func); | |
763 | ||
764 | release_host: | |
765 | sdio_release_host(func); | |
766 | ||
767 | failed: | |
768 | BT_DBG("Leave"); | |
769 | return ret; | |
770 | } | |
771 | ||
772 | static int btmrvl_sdio_unregister_dev(struct btmrvl_sdio_card *card) | |
773 | { | |
774 | BT_DBG("Enter"); | |
775 | ||
776 | if (card && card->func) { | |
777 | sdio_claim_host(card->func); | |
778 | sdio_release_irq(card->func); | |
779 | sdio_disable_func(card->func); | |
780 | sdio_release_host(card->func); | |
781 | sdio_set_drvdata(card->func, NULL); | |
782 | } | |
783 | ||
784 | BT_DBG("Leave"); | |
785 | ||
786 | return 0; | |
787 | } | |
788 | ||
789 | static int btmrvl_sdio_enable_host_int(struct btmrvl_sdio_card *card) | |
790 | { | |
791 | int ret; | |
792 | ||
793 | BT_DBG("Enter"); | |
794 | ||
795 | if (!card || !card->func) { | |
796 | BT_DBG("Leave"); | |
797 | return -EINVAL; | |
798 | } | |
799 | ||
800 | sdio_claim_host(card->func); | |
801 | ||
802 | ret = btmrvl_sdio_enable_host_int_mask(card, HIM_ENABLE); | |
803 | ||
804 | btmrvl_sdio_get_rx_unit(card); | |
805 | ||
806 | sdio_release_host(card->func); | |
807 | ||
808 | BT_DBG("Leave"); | |
809 | ||
810 | return ret; | |
811 | } | |
812 | ||
813 | static int btmrvl_sdio_disable_host_int(struct btmrvl_sdio_card *card) | |
814 | { | |
815 | int ret; | |
816 | ||
817 | BT_DBG("Enter"); | |
818 | ||
819 | if (!card || !card->func) { | |
820 | BT_DBG("Leave"); | |
821 | return -EINVAL; | |
822 | } | |
823 | ||
824 | sdio_claim_host(card->func); | |
825 | ||
826 | ret = btmrvl_sdio_disable_host_int_mask(card, HIM_DISABLE); | |
827 | ||
828 | sdio_release_host(card->func); | |
829 | ||
830 | BT_DBG("Leave"); | |
831 | ||
832 | return ret; | |
833 | } | |
834 | ||
835 | static int btmrvl_sdio_host_to_card(struct btmrvl_private *priv, | |
836 | u8 *payload, u16 nb) | |
837 | { | |
838 | struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; | |
839 | int ret = 0; | |
840 | int buf_block_len; | |
841 | int blksz; | |
842 | int i = 0; | |
843 | u8 *buf = NULL; | |
844 | void *tmpbuf = NULL; | |
845 | int tmpbufsz; | |
846 | ||
847 | BT_DBG("Enter"); | |
848 | ||
849 | if (!card || !card->func) { | |
850 | BT_ERR("card or function is NULL!"); | |
851 | BT_DBG("Leave"); | |
852 | return -EINVAL; | |
853 | } | |
854 | ||
855 | buf = payload; | |
856 | if ((u32) payload & (BTSDIO_DMA_ALIGN - 1)) { | |
857 | tmpbufsz = ALIGN_SZ(nb, BTSDIO_DMA_ALIGN); | |
858 | tmpbuf = kmalloc(tmpbufsz, GFP_KERNEL); | |
859 | memset(tmpbuf, 0, tmpbufsz); | |
860 | buf = (u8 *) ALIGN_ADDR(tmpbuf, BTSDIO_DMA_ALIGN); | |
861 | memcpy(buf, payload, nb); | |
862 | } | |
863 | ||
864 | blksz = SDIO_BLOCK_SIZE; | |
865 | buf_block_len = (nb + blksz - 1) / blksz; | |
866 | ||
867 | sdio_claim_host(card->func); | |
868 | ||
869 | do { | |
870 | /* Transfer data to card */ | |
871 | ret = sdio_writesb(card->func, card->ioport, buf, | |
872 | buf_block_len * blksz); | |
873 | if (ret < 0) { | |
874 | i++; | |
875 | BT_ERR("i=%d writesb failed: %d", i, ret); | |
876 | print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, | |
877 | payload, nb); | |
878 | ret = -EIO; | |
879 | if (i > MAX_WRITE_IOMEM_RETRY) | |
880 | goto exit; | |
881 | } | |
882 | } while (ret); | |
883 | ||
884 | priv->btmrvl_dev.tx_dnld_rdy = false; | |
885 | ||
886 | exit: | |
887 | sdio_release_host(card->func); | |
888 | ||
889 | BT_DBG("Leave"); | |
890 | ||
891 | return ret; | |
892 | } | |
893 | ||
894 | static int btmrvl_sdio_download_fw(struct btmrvl_sdio_card *card) | |
895 | { | |
896 | int ret = 0; | |
897 | ||
898 | BT_DBG("Enter"); | |
899 | ||
900 | if (!card || !card->func) { | |
901 | BT_ERR("card or function is NULL!"); | |
902 | BT_DBG("Leave"); | |
903 | return -EINVAL; | |
904 | } | |
905 | sdio_claim_host(card->func); | |
906 | ||
907 | if (!btmrvl_sdio_verify_fw_download(card, 1)) { | |
908 | BT_DBG("Firmware already downloaded!"); | |
909 | goto done; | |
910 | } | |
911 | ||
912 | ret = btmrvl_sdio_download_helper(card); | |
913 | if (ret) { | |
914 | BT_ERR("Failed to download helper!"); | |
915 | ret = -EIO; | |
916 | goto done; | |
917 | } | |
918 | ||
919 | if (btmrvl_sdio_download_fw_w_helper(card)) { | |
920 | BT_ERR("Failed to download firmware!"); | |
921 | ret = -EIO; | |
922 | goto done; | |
923 | } | |
924 | ||
925 | if (btmrvl_sdio_verify_fw_download(card, MAX_POLL_TRIES)) { | |
926 | BT_ERR("FW failed to be active in time!"); | |
927 | ret = -ETIMEDOUT; | |
928 | goto done; | |
929 | } | |
930 | ||
931 | done: | |
932 | sdio_release_host(card->func); | |
933 | ||
934 | BT_DBG("Leave"); | |
935 | ||
936 | return ret; | |
937 | } | |
938 | ||
939 | static int btmrvl_sdio_wakeup_fw(struct btmrvl_private *priv) | |
940 | { | |
941 | struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; | |
942 | int ret = 0; | |
943 | ||
944 | BT_DBG("Enter"); | |
945 | ||
946 | if (!card || !card->func) { | |
947 | BT_ERR("card or function is NULL!"); | |
948 | BT_DBG("Leave"); | |
949 | return -EINVAL; | |
950 | } | |
951 | ||
952 | sdio_claim_host(card->func); | |
953 | ||
954 | sdio_writeb(card->func, HOST_POWER_UP, CONFIG_REG, &ret); | |
955 | ||
956 | sdio_release_host(card->func); | |
957 | ||
958 | BT_DBG("wake up firmware"); | |
959 | ||
960 | BT_DBG("Leave"); | |
961 | ||
962 | return ret; | |
963 | } | |
964 | ||
965 | static int btmrvl_sdio_probe(struct sdio_func *func, | |
dcf47f3b | 966 | const struct sdio_device_id *id) |
789221ec BZ |
967 | { |
968 | int ret = 0; | |
969 | struct btmrvl_private *priv = NULL; | |
970 | struct btmrvl_sdio_card *card = NULL; | |
971 | ||
972 | BT_DBG("Enter"); | |
973 | ||
974 | BT_INFO("vendor=0x%x, device=0x%x, class=%d, fn=%d", | |
975 | id->vendor, id->device, id->class, func->num); | |
976 | ||
977 | card = kzalloc(sizeof(*card), GFP_KERNEL); | |
978 | if (!card) { | |
979 | ret = -ENOMEM; | |
980 | goto done; | |
981 | } | |
982 | ||
983 | card->func = func; | |
984 | ||
dcf47f3b MH |
985 | if (id->driver_data) { |
986 | struct btmrvl_sdio_device *data = (void *) id->driver_data; | |
987 | card->helper = data->helper; | |
988 | card->firmware = data->firmware; | |
989 | } | |
990 | ||
789221ec BZ |
991 | if (btmrvl_sdio_register_dev(card) < 0) { |
992 | BT_ERR("Failed to register BT device!"); | |
993 | ret = -ENODEV; | |
994 | goto free_card; | |
995 | } | |
996 | ||
997 | /* Disable the interrupts on the card */ | |
998 | btmrvl_sdio_disable_host_int(card); | |
999 | ||
1000 | if (btmrvl_sdio_download_fw(card)) { | |
1001 | BT_ERR("Downloading firmware failed!"); | |
1002 | ret = -ENODEV; | |
1003 | goto unreg_dev; | |
1004 | } | |
1005 | ||
1006 | msleep(100); | |
1007 | ||
1008 | btmrvl_sdio_enable_host_int(card); | |
1009 | ||
1010 | priv = btmrvl_add_card(card); | |
1011 | if (!priv) { | |
1012 | BT_ERR("Initializing card failed!"); | |
1013 | ret = -ENODEV; | |
1014 | goto disable_host_int; | |
1015 | } | |
1016 | ||
1017 | card->priv = priv; | |
1018 | ||
1019 | /* Initialize the interface specific function pointers */ | |
1020 | priv->hw_host_to_card = btmrvl_sdio_host_to_card; | |
1021 | priv->hw_wakeup_firmware = btmrvl_sdio_wakeup_fw; | |
1022 | ||
1023 | strncpy(priv->btmrvl_dev.name, "btmrvl_sdio0", | |
1024 | sizeof(priv->btmrvl_dev.name)); | |
1025 | ||
1026 | btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ); | |
1027 | ||
1028 | BT_DBG("Leave"); | |
1029 | ||
1030 | return 0; | |
1031 | ||
1032 | disable_host_int: | |
1033 | btmrvl_sdio_disable_host_int(card); | |
1034 | unreg_dev: | |
1035 | btmrvl_sdio_unregister_dev(card); | |
1036 | free_card: | |
1037 | kfree(card); | |
1038 | done: | |
1039 | BT_DBG("Leave"); | |
1040 | ||
1041 | return ret; | |
1042 | } | |
1043 | ||
1044 | static void btmrvl_sdio_remove(struct sdio_func *func) | |
1045 | { | |
1046 | struct btmrvl_sdio_card *card; | |
1047 | ||
1048 | BT_DBG("Enter"); | |
1049 | ||
1050 | if (func) { | |
1051 | card = sdio_get_drvdata(func); | |
1052 | if (card) { | |
1053 | /* Send SHUTDOWN command & disable interrupt | |
1054 | * if user removes the module. | |
1055 | */ | |
1056 | if (user_rmmod) { | |
1057 | btmrvl_send_module_cfg_cmd(card->priv, | |
1058 | MODULE_SHUTDOWN_REQ); | |
1059 | btmrvl_sdio_disable_host_int(card); | |
1060 | } | |
1061 | BT_DBG("unregester dev"); | |
1062 | btmrvl_sdio_unregister_dev(card); | |
1063 | btmrvl_remove_card(card->priv); | |
1064 | kfree(card); | |
1065 | } | |
1066 | } | |
1067 | ||
1068 | BT_DBG("Leave"); | |
1069 | } | |
1070 | ||
1071 | static struct sdio_driver bt_mrvl_sdio = { | |
1072 | .name = "btmrvl_sdio", | |
1073 | .id_table = btmrvl_sdio_ids, | |
1074 | .probe = btmrvl_sdio_probe, | |
1075 | .remove = btmrvl_sdio_remove, | |
1076 | }; | |
1077 | ||
1078 | static int btmrvl_sdio_init_module(void) | |
1079 | { | |
1080 | BT_DBG("Enter"); | |
1081 | ||
1082 | if (sdio_register_driver(&bt_mrvl_sdio) != 0) { | |
1083 | BT_ERR("SDIO Driver Registration Failed"); | |
1084 | BT_DBG("Leave"); | |
1085 | return -ENODEV; | |
1086 | } | |
1087 | ||
1088 | /* Clear the flag in case user removes the card. */ | |
1089 | user_rmmod = 0; | |
1090 | ||
1091 | BT_DBG("Leave"); | |
1092 | ||
1093 | return 0; | |
1094 | } | |
1095 | ||
1096 | static void btmrvl_sdio_exit_module(void) | |
1097 | { | |
1098 | BT_DBG("Enter"); | |
1099 | ||
1100 | /* Set the flag as user is removing this module. */ | |
1101 | user_rmmod = 1; | |
1102 | ||
1103 | sdio_unregister_driver(&bt_mrvl_sdio); | |
1104 | ||
1105 | BT_DBG("Leave"); | |
1106 | } | |
1107 | ||
1108 | module_init(btmrvl_sdio_init_module); | |
1109 | module_exit(btmrvl_sdio_exit_module); | |
1110 | ||
1111 | MODULE_AUTHOR("Marvell International Ltd."); | |
1112 | MODULE_DESCRIPTION("Marvell BT-over-SDIO Driver v" VERSION); | |
1113 | MODULE_VERSION(VERSION); | |
1114 | MODULE_LICENSE("GPL v2"); |