1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Marvell BT-over-SDIO driver: SDIO interface related functions. |
4 | * |
5 | * Copyright (C) 2009, Marvell International Ltd. |
6 | **/ |
7 | |
8 | #include <linux/firmware.h> |
9 | #include <linux/slab.h> |
10 | #include <linux/suspend.h> |
11 | |
12 | #include <linux/mmc/sdio_ids.h> |
13 | #include <linux/mmc/sdio_func.h> |
14 | #include <linux/module.h> |
15 | #include <linux/devcoredump.h> |
16 | |
17 | #include <net/bluetooth/bluetooth.h> |
18 | #include <net/bluetooth/hci_core.h> |
19 | |
20 | #include "btmrvl_drv.h" |
21 | #include "btmrvl_sdio.h" |
22 | |
23 | #define VERSION "1.0" |
24 | |
25 | static struct memory_type_mapping mem_type_mapping_tbl[] = { |
26 | {"ITCM" , NULL, 0, 0xF0}, |
27 | {"DTCM" , NULL, 0, 0xF1}, |
28 | {"SQRAM" , NULL, 0, 0xF2}, |
29 | {"APU" , NULL, 0, 0xF3}, |
30 | {"CIU" , NULL, 0, 0xF4}, |
31 | {"ICU" , NULL, 0, 0xF5}, |
32 | {"MAC" , NULL, 0, 0xF6}, |
33 | {"EXT7" , NULL, 0, 0xF7}, |
34 | {"EXT8" , NULL, 0, 0xF8}, |
35 | {"EXT9" , NULL, 0, 0xF9}, |
36 | {"EXT10" , NULL, 0, 0xFA}, |
37 | {"EXT11" , NULL, 0, 0xFB}, |
38 | {"EXT12" , NULL, 0, 0xFC}, |
39 | {"EXT13" , NULL, 0, 0xFD}, |
40 | {"EXTLAST" , NULL, 0, 0xFE}, |
41 | }; |
42 | |
43 | static const struct of_device_id btmrvl_sdio_of_match_table[] __maybe_unused = { |
44 | { .compatible = "marvell,sd8897-bt" }, |
45 | { .compatible = "marvell,sd8997-bt" }, |
46 | { } |
47 | }; |
48 | |
49 | static irqreturn_t btmrvl_wake_irq_bt(int irq, void *priv) |
50 | { |
51 | struct btmrvl_sdio_card *card = priv; |
52 | struct device *dev = &card->func->dev; |
53 | struct btmrvl_plt_wake_cfg *cfg = card->plt_wake_cfg; |
54 | |
55 | dev_info(dev, "wake by bt\n" ); |
56 | cfg->wake_by_bt = true; |
57 | disable_irq_nosync(irq); |
58 | |
59 | pm_wakeup_event(dev, msec: 0); |
60 | pm_system_wakeup(); |
61 | |
62 | return IRQ_HANDLED; |
63 | } |
64 | |
65 | /* This function parses device tree node using mmc subnode devicetree API. |
66 | * The device node is saved in card->plt_of_node. |
67 | * If the device tree node exists and includes interrupts attributes, this |
68 | * function will request platform specific wakeup interrupt. |
69 | */ |
70 | static int btmrvl_sdio_probe_of(struct device *dev, |
71 | struct btmrvl_sdio_card *card) |
72 | { |
73 | struct btmrvl_plt_wake_cfg *cfg; |
74 | int ret; |
75 | |
76 | if (!dev->of_node || |
77 | !of_match_node(matches: btmrvl_sdio_of_match_table, node: dev->of_node)) { |
78 | dev_info(dev, "sdio device tree data not available\n" ); |
79 | return -1; |
80 | } |
81 | |
82 | card->plt_of_node = dev->of_node; |
83 | |
84 | card->plt_wake_cfg = devm_kzalloc(dev, size: sizeof(*card->plt_wake_cfg), |
85 | GFP_KERNEL); |
86 | cfg = card->plt_wake_cfg; |
87 | if (cfg && card->plt_of_node) { |
88 | cfg->irq_bt = irq_of_parse_and_map(node: card->plt_of_node, index: 0); |
89 | if (!cfg->irq_bt) { |
90 | dev_err(dev, "fail to parse irq_bt from device tree\n" ); |
91 | cfg->irq_bt = -1; |
92 | } else { |
93 | ret = devm_request_irq(dev, irq: cfg->irq_bt, |
94 | handler: btmrvl_wake_irq_bt, |
95 | irqflags: 0, devname: "bt_wake" , dev_id: card); |
96 | if (ret) { |
97 | dev_err(dev, |
98 | "Failed to request irq_bt %d (%d)\n" , |
99 | cfg->irq_bt, ret); |
100 | } |
101 | |
102 | /* Configure wakeup (enabled by default) */ |
103 | device_init_wakeup(dev, enable: true); |
104 | disable_irq(irq: cfg->irq_bt); |
105 | } |
106 | } |
107 | |
108 | return 0; |
109 | } |
110 | |
111 | /* The btmrvl_sdio_remove() callback function is called |
112 | * when user removes this module from kernel space or ejects |
113 | * the card from the slot. The driver handles these 2 cases |
114 | * differently. |
115 | * If the user is removing the module, a MODULE_SHUTDOWN_REQ |
116 | * command is sent to firmware and interrupt will be disabled. |
117 | * If the card is removed, there is no need to send command |
118 | * or disable interrupt. |
119 | * |
120 | * The variable 'user_rmmod' is used to distinguish these two |
121 | * scenarios. This flag is initialized as FALSE in case the card |
122 | * is removed, and will be set to TRUE for module removal when |
123 | * module_exit function is called. |
124 | */ |
125 | static u8 user_rmmod; |
126 | static u8 sdio_ireg; |
127 | |
128 | static const struct btmrvl_sdio_card_reg btmrvl_reg_8688 = { |
129 | .cfg = 0x03, |
130 | .host_int_mask = 0x04, |
131 | .host_intstatus = 0x05, |
132 | .card_status = 0x20, |
133 | .sq_read_base_addr_a0 = 0x10, |
134 | .sq_read_base_addr_a1 = 0x11, |
135 | .card_fw_status0 = 0x40, |
136 | .card_fw_status1 = 0x41, |
137 | .card_rx_len = 0x42, |
138 | .card_rx_unit = 0x43, |
139 | .io_port_0 = 0x00, |
140 | .io_port_1 = 0x01, |
141 | .io_port_2 = 0x02, |
142 | .int_read_to_clear = false, |
143 | }; |
144 | static const struct btmrvl_sdio_card_reg btmrvl_reg_87xx = { |
145 | .cfg = 0x00, |
146 | .host_int_mask = 0x02, |
147 | .host_intstatus = 0x03, |
148 | .card_status = 0x30, |
149 | .sq_read_base_addr_a0 = 0x40, |
150 | .sq_read_base_addr_a1 = 0x41, |
151 | .card_revision = 0x5c, |
152 | .card_fw_status0 = 0x60, |
153 | .card_fw_status1 = 0x61, |
154 | .card_rx_len = 0x62, |
155 | .card_rx_unit = 0x63, |
156 | .io_port_0 = 0x78, |
157 | .io_port_1 = 0x79, |
158 | .io_port_2 = 0x7a, |
159 | .int_read_to_clear = false, |
160 | }; |
161 | |
162 | static const struct btmrvl_sdio_card_reg btmrvl_reg_8887 = { |
163 | .cfg = 0x00, |
164 | .host_int_mask = 0x08, |
165 | .host_intstatus = 0x0C, |
166 | .card_status = 0x5C, |
167 | .sq_read_base_addr_a0 = 0x6C, |
168 | .sq_read_base_addr_a1 = 0x6D, |
169 | .card_revision = 0xC8, |
170 | .card_fw_status0 = 0x88, |
171 | .card_fw_status1 = 0x89, |
172 | .card_rx_len = 0x8A, |
173 | .card_rx_unit = 0x8B, |
174 | .io_port_0 = 0xE4, |
175 | .io_port_1 = 0xE5, |
176 | .io_port_2 = 0xE6, |
177 | .int_read_to_clear = true, |
178 | .host_int_rsr = 0x04, |
179 | .card_misc_cfg = 0xD8, |
180 | }; |
181 | |
182 | static const struct btmrvl_sdio_card_reg btmrvl_reg_8897 = { |
183 | .cfg = 0x00, |
184 | .host_int_mask = 0x02, |
185 | .host_intstatus = 0x03, |
186 | .card_status = 0x50, |
187 | .sq_read_base_addr_a0 = 0x60, |
188 | .sq_read_base_addr_a1 = 0x61, |
189 | .card_revision = 0xbc, |
190 | .card_fw_status0 = 0xc0, |
191 | .card_fw_status1 = 0xc1, |
192 | .card_rx_len = 0xc2, |
193 | .card_rx_unit = 0xc3, |
194 | .io_port_0 = 0xd8, |
195 | .io_port_1 = 0xd9, |
196 | .io_port_2 = 0xda, |
197 | .int_read_to_clear = true, |
198 | .host_int_rsr = 0x01, |
199 | .card_misc_cfg = 0xcc, |
200 | .fw_dump_ctrl = 0xe2, |
201 | .fw_dump_start = 0xe3, |
202 | .fw_dump_end = 0xea, |
203 | }; |
204 | |
205 | static const struct btmrvl_sdio_card_reg btmrvl_reg_89xx = { |
206 | .cfg = 0x00, |
207 | .host_int_mask = 0x08, |
208 | .host_intstatus = 0x0c, |
209 | .card_status = 0x5c, |
210 | .sq_read_base_addr_a0 = 0xf8, |
211 | .sq_read_base_addr_a1 = 0xf9, |
212 | .card_revision = 0xc8, |
213 | .card_fw_status0 = 0xe8, |
214 | .card_fw_status1 = 0xe9, |
215 | .card_rx_len = 0xea, |
216 | .card_rx_unit = 0xeb, |
217 | .io_port_0 = 0xe4, |
218 | .io_port_1 = 0xe5, |
219 | .io_port_2 = 0xe6, |
220 | .int_read_to_clear = true, |
221 | .host_int_rsr = 0x04, |
222 | .card_misc_cfg = 0xd8, |
223 | .fw_dump_ctrl = 0xf0, |
224 | .fw_dump_start = 0xf1, |
225 | .fw_dump_end = 0xf8, |
226 | }; |
227 | |
228 | static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = { |
229 | .helper = "mrvl/sd8688_helper.bin" , |
230 | .firmware = "mrvl/sd8688.bin" , |
231 | .reg = &btmrvl_reg_8688, |
232 | .support_pscan_win_report = false, |
233 | .sd_blksz_fw_dl = 64, |
234 | .supports_fw_dump = false, |
235 | }; |
236 | |
237 | static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = { |
238 | .helper = NULL, |
239 | .firmware = "mrvl/sd8787_uapsta.bin" , |
240 | .reg = &btmrvl_reg_87xx, |
241 | .support_pscan_win_report = false, |
242 | .sd_blksz_fw_dl = 256, |
243 | .supports_fw_dump = false, |
244 | }; |
245 | |
246 | static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = { |
247 | .helper = NULL, |
248 | .firmware = "mrvl/sd8797_uapsta.bin" , |
249 | .reg = &btmrvl_reg_87xx, |
250 | .support_pscan_win_report = false, |
251 | .sd_blksz_fw_dl = 256, |
252 | .supports_fw_dump = false, |
253 | }; |
254 | |
255 | static const struct btmrvl_sdio_device btmrvl_sdio_sd8887 = { |
256 | .helper = NULL, |
257 | .firmware = "mrvl/sd8887_uapsta.bin" , |
258 | .reg = &btmrvl_reg_8887, |
259 | .support_pscan_win_report = true, |
260 | .sd_blksz_fw_dl = 256, |
261 | .supports_fw_dump = false, |
262 | }; |
263 | |
264 | static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = { |
265 | .helper = NULL, |
266 | .firmware = "mrvl/sd8897_uapsta.bin" , |
267 | .reg = &btmrvl_reg_8897, |
268 | .support_pscan_win_report = true, |
269 | .sd_blksz_fw_dl = 256, |
270 | .supports_fw_dump = true, |
271 | }; |
272 | |
273 | static const struct btmrvl_sdio_device btmrvl_sdio_sd8977 = { |
274 | .helper = NULL, |
275 | .firmware = "mrvl/sdsd8977_combo_v2.bin" , |
276 | .reg = &btmrvl_reg_89xx, |
277 | .support_pscan_win_report = true, |
278 | .sd_blksz_fw_dl = 256, |
279 | .supports_fw_dump = true, |
280 | }; |
281 | |
282 | static const struct btmrvl_sdio_device btmrvl_sdio_sd8987 = { |
283 | .helper = NULL, |
284 | .firmware = "mrvl/sd8987_uapsta.bin" , |
285 | .reg = &btmrvl_reg_89xx, |
286 | .support_pscan_win_report = true, |
287 | .sd_blksz_fw_dl = 256, |
288 | .supports_fw_dump = true, |
289 | }; |
290 | |
291 | static const struct btmrvl_sdio_device btmrvl_sdio_sd8997 = { |
292 | .helper = NULL, |
293 | .firmware = "mrvl/sdsd8997_combo_v4.bin" , |
294 | .reg = &btmrvl_reg_89xx, |
295 | .support_pscan_win_report = true, |
296 | .sd_blksz_fw_dl = 256, |
297 | .supports_fw_dump = true, |
298 | }; |
299 | |
300 | static const struct sdio_device_id btmrvl_sdio_ids[] = { |
301 | /* Marvell SD8688 Bluetooth device */ |
302 | { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8688_BT), |
303 | .driver_data = (unsigned long)&btmrvl_sdio_sd8688 }, |
304 | /* Marvell SD8787 Bluetooth device */ |
305 | { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787_BT), |
306 | .driver_data = (unsigned long)&btmrvl_sdio_sd8787 }, |
307 | /* Marvell SD8787 Bluetooth AMP device */ |
308 | { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787_BT_AMP), |
309 | .driver_data = (unsigned long)&btmrvl_sdio_sd8787 }, |
310 | /* Marvell SD8797 Bluetooth device */ |
311 | { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797_BT), |
312 | .driver_data = (unsigned long)&btmrvl_sdio_sd8797 }, |
313 | /* Marvell SD8887 Bluetooth device */ |
314 | { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887_BT), |
315 | .driver_data = (unsigned long)&btmrvl_sdio_sd8887 }, |
316 | /* Marvell SD8897 Bluetooth device */ |
317 | { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8897_BT), |
318 | .driver_data = (unsigned long)&btmrvl_sdio_sd8897 }, |
319 | /* Marvell SD8977 Bluetooth device */ |
320 | { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8977_BT), |
321 | .driver_data = (unsigned long)&btmrvl_sdio_sd8977 }, |
322 | /* Marvell SD8987 Bluetooth device */ |
323 | { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8987_BT), |
324 | .driver_data = (unsigned long)&btmrvl_sdio_sd8987 }, |
325 | /* Marvell SD8997 Bluetooth device */ |
326 | { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8997_BT), |
327 | .driver_data = (unsigned long)&btmrvl_sdio_sd8997 }, |
328 | |
329 | { } /* Terminating entry */ |
330 | }; |
331 | |
332 | MODULE_DEVICE_TABLE(sdio, btmrvl_sdio_ids); |
333 | |
334 | static int btmrvl_sdio_get_rx_unit(struct btmrvl_sdio_card *card) |
335 | { |
336 | u8 reg; |
337 | int ret; |
338 | |
339 | reg = sdio_readb(func: card->func, addr: card->reg->card_rx_unit, err_ret: &ret); |
340 | if (!ret) |
341 | card->rx_unit = reg; |
342 | |
343 | return ret; |
344 | } |
345 | |
346 | static int btmrvl_sdio_read_fw_status(struct btmrvl_sdio_card *card, u16 *dat) |
347 | { |
348 | u8 fws0, fws1; |
349 | int ret; |
350 | |
351 | *dat = 0; |
352 | |
353 | fws0 = sdio_readb(func: card->func, addr: card->reg->card_fw_status0, err_ret: &ret); |
354 | if (ret) |
355 | return -EIO; |
356 | |
357 | fws1 = sdio_readb(func: card->func, addr: card->reg->card_fw_status1, err_ret: &ret); |
358 | if (ret) |
359 | return -EIO; |
360 | |
361 | *dat = (((u16) fws1) << 8) | fws0; |
362 | |
363 | return 0; |
364 | } |
365 | |
366 | static int btmrvl_sdio_read_rx_len(struct btmrvl_sdio_card *card, u16 *dat) |
367 | { |
368 | u8 reg; |
369 | int ret; |
370 | |
371 | reg = sdio_readb(func: card->func, addr: card->reg->card_rx_len, err_ret: &ret); |
372 | if (!ret) |
373 | *dat = (u16) reg << card->rx_unit; |
374 | |
375 | return ret; |
376 | } |
377 | |
378 | static int btmrvl_sdio_enable_host_int_mask(struct btmrvl_sdio_card *card, |
379 | u8 mask) |
380 | { |
381 | int ret; |
382 | |
383 | sdio_writeb(func: card->func, b: mask, addr: card->reg->host_int_mask, err_ret: &ret); |
384 | if (ret) { |
385 | BT_ERR("Unable to enable the host interrupt!" ); |
386 | ret = -EIO; |
387 | } |
388 | |
389 | return ret; |
390 | } |
391 | |
392 | static int btmrvl_sdio_disable_host_int_mask(struct btmrvl_sdio_card *card, |
393 | u8 mask) |
394 | { |
395 | u8 host_int_mask; |
396 | int ret; |
397 | |
398 | host_int_mask = sdio_readb(func: card->func, addr: card->reg->host_int_mask, err_ret: &ret); |
399 | if (ret) |
400 | return -EIO; |
401 | |
402 | host_int_mask &= ~mask; |
403 | |
404 | sdio_writeb(func: card->func, b: host_int_mask, addr: card->reg->host_int_mask, err_ret: &ret); |
405 | if (ret < 0) { |
406 | BT_ERR("Unable to disable the host interrupt!" ); |
407 | return -EIO; |
408 | } |
409 | |
410 | return 0; |
411 | } |
412 | |
413 | static int btmrvl_sdio_poll_card_status(struct btmrvl_sdio_card *card, u8 bits) |
414 | { |
415 | unsigned int tries; |
416 | u8 status; |
417 | int ret; |
418 | |
419 | for (tries = 0; tries < MAX_POLL_TRIES * 1000; tries++) { |
420 | status = sdio_readb(func: card->func, addr: card->reg->card_status, err_ret: &ret); |
421 | if (ret) |
422 | goto failed; |
423 | if ((status & bits) == bits) |
424 | return ret; |
425 | |
426 | udelay(1); |
427 | } |
428 | |
429 | ret = -ETIMEDOUT; |
430 | |
431 | failed: |
432 | BT_ERR("FAILED! ret=%d" , ret); |
433 | |
434 | return ret; |
435 | } |
436 | |
437 | static int btmrvl_sdio_verify_fw_download(struct btmrvl_sdio_card *card, |
438 | int pollnum) |
439 | { |
440 | u16 firmwarestat; |
441 | int tries, ret; |
442 | |
443 | /* Wait for firmware to become ready */ |
444 | for (tries = 0; tries < pollnum; tries++) { |
445 | sdio_claim_host(func: card->func); |
446 | ret = btmrvl_sdio_read_fw_status(card, dat: &firmwarestat); |
447 | sdio_release_host(func: card->func); |
448 | if (ret < 0) |
449 | continue; |
450 | |
451 | if (firmwarestat == FIRMWARE_READY) |
452 | return 0; |
453 | |
454 | msleep(msecs: 100); |
455 | } |
456 | |
457 | return -ETIMEDOUT; |
458 | } |
459 | |
460 | static int btmrvl_sdio_download_helper(struct btmrvl_sdio_card *card) |
461 | { |
462 | const struct firmware *fw_helper = NULL; |
463 | const u8 *helper = NULL; |
464 | int ret; |
465 | void *tmphlprbuf = NULL; |
466 | int tmphlprbufsz, hlprblknow, helperlen; |
467 | u8 *helperbuf; |
468 | u32 tx_len; |
469 | |
470 | ret = request_firmware(fw: &fw_helper, name: card->helper, |
471 | device: &card->func->dev); |
472 | if ((ret < 0) || !fw_helper) { |
473 | BT_ERR("request_firmware(helper) failed, error code = %d" , |
474 | ret); |
475 | ret = -ENOENT; |
476 | goto done; |
477 | } |
478 | |
479 | helper = fw_helper->data; |
480 | helperlen = fw_helper->size; |
481 | |
482 | BT_DBG("Downloading helper image (%d bytes), block size %d bytes" , |
483 | helperlen, SDIO_BLOCK_SIZE); |
484 | |
485 | tmphlprbufsz = ALIGN_SZ(BTM_UPLD_SIZE, BTSDIO_DMA_ALIGN); |
486 | |
487 | tmphlprbuf = kzalloc(size: tmphlprbufsz, GFP_KERNEL); |
488 | if (!tmphlprbuf) { |
489 | BT_ERR("Unable to allocate buffer for helper." |
490 | " Terminating download" ); |
491 | ret = -ENOMEM; |
492 | goto done; |
493 | } |
494 | |
495 | helperbuf = (u8 *) ALIGN_ADDR(tmphlprbuf, BTSDIO_DMA_ALIGN); |
496 | |
497 | /* Perform helper data transfer */ |
498 | tx_len = (FIRMWARE_TRANSFER_NBLOCK * SDIO_BLOCK_SIZE) |
499 | - SDIO_HEADER_LEN; |
500 | hlprblknow = 0; |
501 | |
502 | do { |
503 | ret = btmrvl_sdio_poll_card_status(card, |
504 | CARD_IO_READY | DN_LD_CARD_RDY); |
505 | if (ret < 0) { |
506 | BT_ERR("Helper download poll status timeout @ %d" , |
507 | hlprblknow); |
508 | goto done; |
509 | } |
510 | |
511 | /* Check if there is more data? */ |
512 | if (hlprblknow >= helperlen) |
513 | break; |
514 | |
515 | if (helperlen - hlprblknow < tx_len) |
516 | tx_len = helperlen - hlprblknow; |
517 | |
518 | /* Little-endian */ |
519 | helperbuf[0] = ((tx_len & 0x000000ff) >> 0); |
520 | helperbuf[1] = ((tx_len & 0x0000ff00) >> 8); |
521 | helperbuf[2] = ((tx_len & 0x00ff0000) >> 16); |
522 | helperbuf[3] = ((tx_len & 0xff000000) >> 24); |
523 | |
524 | memcpy(&helperbuf[SDIO_HEADER_LEN], &helper[hlprblknow], |
525 | tx_len); |
526 | |
527 | /* Now send the data */ |
528 | ret = sdio_writesb(func: card->func, addr: card->ioport, src: helperbuf, |
529 | FIRMWARE_TRANSFER_NBLOCK * SDIO_BLOCK_SIZE); |
530 | if (ret < 0) { |
531 | BT_ERR("IO error during helper download @ %d" , |
532 | hlprblknow); |
533 | goto done; |
534 | } |
535 | |
536 | hlprblknow += tx_len; |
537 | } while (true); |
538 | |
539 | BT_DBG("Transferring helper image EOF block" ); |
540 | |
541 | memset(helperbuf, 0x0, SDIO_BLOCK_SIZE); |
542 | |
543 | ret = sdio_writesb(func: card->func, addr: card->ioport, src: helperbuf, |
544 | SDIO_BLOCK_SIZE); |
545 | if (ret < 0) { |
546 | BT_ERR("IO error in writing helper image EOF block" ); |
547 | goto done; |
548 | } |
549 | |
550 | ret = 0; |
551 | |
552 | done: |
553 | kfree(objp: tmphlprbuf); |
554 | release_firmware(fw: fw_helper); |
555 | return ret; |
556 | } |
557 | |
558 | static int btmrvl_sdio_download_fw_w_helper(struct btmrvl_sdio_card *card) |
559 | { |
560 | const struct firmware *fw_firmware = NULL; |
561 | const u8 *firmware = NULL; |
562 | int firmwarelen, tmpfwbufsz, ret; |
563 | unsigned int tries, offset; |
564 | u8 base0, base1; |
565 | void *tmpfwbuf = NULL; |
566 | u8 *fwbuf; |
567 | u16 len, blksz_dl = card->sd_blksz_fw_dl; |
568 | int txlen = 0, tx_blocks = 0, count = 0; |
569 | |
570 | ret = request_firmware(fw: &fw_firmware, name: card->firmware, |
571 | device: &card->func->dev); |
572 | if ((ret < 0) || !fw_firmware) { |
573 | BT_ERR("request_firmware(firmware) failed, error code = %d" , |
574 | ret); |
575 | ret = -ENOENT; |
576 | goto done; |
577 | } |
578 | |
579 | firmware = fw_firmware->data; |
580 | firmwarelen = fw_firmware->size; |
581 | |
582 | BT_DBG("Downloading FW image (%d bytes)" , firmwarelen); |
583 | |
584 | tmpfwbufsz = ALIGN_SZ(BTM_UPLD_SIZE, BTSDIO_DMA_ALIGN); |
585 | tmpfwbuf = kzalloc(size: tmpfwbufsz, GFP_KERNEL); |
586 | if (!tmpfwbuf) { |
587 | BT_ERR("Unable to allocate buffer for firmware." |
588 | " Terminating download" ); |
589 | ret = -ENOMEM; |
590 | goto done; |
591 | } |
592 | |
593 | /* Ensure aligned firmware buffer */ |
594 | fwbuf = (u8 *) ALIGN_ADDR(tmpfwbuf, BTSDIO_DMA_ALIGN); |
595 | |
596 | /* Perform firmware data transfer */ |
597 | offset = 0; |
598 | do { |
599 | ret = btmrvl_sdio_poll_card_status(card, |
600 | CARD_IO_READY | DN_LD_CARD_RDY); |
601 | if (ret < 0) { |
602 | BT_ERR("FW download with helper poll status" |
603 | " timeout @ %d" , offset); |
604 | goto done; |
605 | } |
606 | |
607 | /* Check if there is more data ? */ |
608 | if (offset >= firmwarelen) |
609 | break; |
610 | |
611 | for (tries = 0; tries < MAX_POLL_TRIES; tries++) { |
612 | base0 = sdio_readb(func: card->func, |
613 | addr: card->reg->sq_read_base_addr_a0, err_ret: &ret); |
614 | if (ret) { |
615 | BT_ERR("BASE0 register read failed:" |
616 | " base0 = 0x%04X(%d)." |
617 | " Terminating download" , |
618 | base0, base0); |
619 | ret = -EIO; |
620 | goto done; |
621 | } |
622 | base1 = sdio_readb(func: card->func, |
623 | addr: card->reg->sq_read_base_addr_a1, err_ret: &ret); |
624 | if (ret) { |
625 | BT_ERR("BASE1 register read failed:" |
626 | " base1 = 0x%04X(%d)." |
627 | " Terminating download" , |
628 | base1, base1); |
629 | ret = -EIO; |
630 | goto done; |
631 | } |
632 | |
633 | len = (((u16) base1) << 8) | base0; |
634 | if (len) |
635 | break; |
636 | |
637 | udelay(10); |
638 | } |
639 | |
640 | if (!len) |
641 | break; |
642 | else if (len > BTM_UPLD_SIZE) { |
643 | BT_ERR("FW download failure @%d, invalid length %d" , |
644 | offset, len); |
645 | ret = -EINVAL; |
646 | goto done; |
647 | } |
648 | |
649 | txlen = len; |
650 | |
651 | if (len & BIT(0)) { |
652 | count++; |
653 | if (count > MAX_WRITE_IOMEM_RETRY) { |
654 | BT_ERR("FW download failure @%d, " |
655 | "over max retry count" , offset); |
656 | ret = -EIO; |
657 | goto done; |
658 | } |
659 | BT_ERR("FW CRC error indicated by the helper: " |
660 | "len = 0x%04X, txlen = %d" , len, txlen); |
661 | len &= ~BIT(0); |
662 | /* Set txlen to 0 so as to resend from same offset */ |
663 | txlen = 0; |
664 | } else { |
665 | count = 0; |
666 | |
667 | /* Last block ? */ |
668 | if (firmwarelen - offset < txlen) |
669 | txlen = firmwarelen - offset; |
670 | |
671 | tx_blocks = DIV_ROUND_UP(txlen, blksz_dl); |
672 | |
673 | memcpy(fwbuf, &firmware[offset], txlen); |
674 | } |
675 | |
676 | ret = sdio_writesb(func: card->func, addr: card->ioport, src: fwbuf, |
677 | count: tx_blocks * blksz_dl); |
678 | |
679 | if (ret < 0) { |
680 | BT_ERR("FW download, writesb(%d) failed @%d" , |
681 | count, offset); |
682 | sdio_writeb(func: card->func, HOST_CMD53_FIN, |
683 | addr: card->reg->cfg, err_ret: &ret); |
684 | if (ret) |
685 | BT_ERR("writeb failed (CFG)" ); |
686 | } |
687 | |
688 | offset += txlen; |
689 | } while (true); |
690 | |
691 | BT_INFO("FW download over, size %d bytes" , offset); |
692 | |
693 | ret = 0; |
694 | |
695 | done: |
696 | kfree(objp: tmpfwbuf); |
697 | release_firmware(fw: fw_firmware); |
698 | return ret; |
699 | } |
700 | |
701 | static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv) |
702 | { |
703 | u16 buf_len = 0; |
704 | int ret, num_blocks, blksz; |
705 | struct sk_buff *skb = NULL; |
706 | u32 type; |
707 | u8 *payload; |
708 | struct hci_dev *hdev = priv->btmrvl_dev.hcidev; |
709 | struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; |
710 | |
711 | if (!card || !card->func) { |
712 | BT_ERR("card or function is NULL!" ); |
713 | ret = -EINVAL; |
714 | goto exit; |
715 | } |
716 | |
717 | /* Read the length of data to be transferred */ |
718 | ret = btmrvl_sdio_read_rx_len(card, dat: &buf_len); |
719 | if (ret < 0) { |
720 | BT_ERR("read rx_len failed" ); |
721 | ret = -EIO; |
722 | goto exit; |
723 | } |
724 | |
725 | blksz = SDIO_BLOCK_SIZE; |
726 | num_blocks = DIV_ROUND_UP(buf_len, blksz); |
727 | |
728 | if (buf_len <= SDIO_HEADER_LEN |
729 | || (num_blocks * blksz) > ALLOC_BUF_SIZE) { |
730 | BT_ERR("invalid packet length: %d" , buf_len); |
731 | ret = -EINVAL; |
732 | goto exit; |
733 | } |
734 | |
735 | /* Allocate buffer */ |
736 | skb = bt_skb_alloc(len: num_blocks * blksz + BTSDIO_DMA_ALIGN, GFP_KERNEL); |
737 | if (!skb) { |
738 | BT_ERR("No free skb" ); |
739 | ret = -ENOMEM; |
740 | goto exit; |
741 | } |
742 | |
743 | if ((unsigned long) skb->data & (BTSDIO_DMA_ALIGN - 1)) { |
744 | skb_put(skb, len: (unsigned long) skb->data & |
745 | (BTSDIO_DMA_ALIGN - 1)); |
746 | skb_pull(skb, len: (unsigned long) skb->data & |
747 | (BTSDIO_DMA_ALIGN - 1)); |
748 | } |
749 | |
750 | payload = skb->data; |
751 | |
752 | ret = sdio_readsb(func: card->func, dst: payload, addr: card->ioport, |
753 | count: num_blocks * blksz); |
754 | if (ret < 0) { |
755 | BT_ERR("readsb failed: %d" , ret); |
756 | ret = -EIO; |
757 | goto exit; |
758 | } |
759 | |
760 | /* This is SDIO specific header length: byte[2][1][0], type: byte[3] |
761 | * (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor) |
762 | */ |
763 | |
764 | buf_len = payload[0]; |
765 | buf_len |= payload[1] << 8; |
766 | buf_len |= payload[2] << 16; |
767 | |
768 | if (buf_len > blksz * num_blocks) { |
769 | BT_ERR("Skip incorrect packet: hdrlen %d buffer %d" , |
770 | buf_len, blksz * num_blocks); |
771 | ret = -EIO; |
772 | goto exit; |
773 | } |
774 | |
775 | type = payload[3]; |
776 | |
777 | switch (type) { |
778 | case HCI_ACLDATA_PKT: |
779 | case HCI_SCODATA_PKT: |
780 | case HCI_EVENT_PKT: |
781 | hci_skb_pkt_type(skb) = type; |
782 | skb_put(skb, len: buf_len); |
783 | skb_pull(skb, SDIO_HEADER_LEN); |
784 | |
785 | if (type == HCI_EVENT_PKT) { |
786 | if (btmrvl_check_evtpkt(priv, skb)) |
787 | hci_recv_frame(hdev, skb); |
788 | } else { |
789 | hci_recv_frame(hdev, skb); |
790 | } |
791 | |
792 | hdev->stat.byte_rx += buf_len; |
793 | break; |
794 | |
795 | case MRVL_VENDOR_PKT: |
796 | hci_skb_pkt_type(skb) = HCI_VENDOR_PKT; |
797 | skb_put(skb, len: buf_len); |
798 | skb_pull(skb, SDIO_HEADER_LEN); |
799 | |
800 | if (btmrvl_process_event(priv, skb)) |
801 | hci_recv_frame(hdev, skb); |
802 | |
803 | hdev->stat.byte_rx += buf_len; |
804 | break; |
805 | |
806 | default: |
807 | BT_ERR("Unknown packet type:%d" , type); |
808 | BT_ERR("hex: %*ph" , blksz * num_blocks, payload); |
809 | |
810 | kfree_skb(skb); |
811 | skb = NULL; |
812 | break; |
813 | } |
814 | |
815 | exit: |
816 | if (ret) { |
817 | hdev->stat.err_rx++; |
818 | kfree_skb(skb); |
819 | } |
820 | |
821 | return ret; |
822 | } |
823 | |
824 | static int btmrvl_sdio_process_int_status(struct btmrvl_private *priv) |
825 | { |
826 | ulong flags; |
827 | u8 ireg; |
828 | struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; |
829 | |
830 | spin_lock_irqsave(&priv->driver_lock, flags); |
831 | ireg = sdio_ireg; |
832 | sdio_ireg = 0; |
833 | spin_unlock_irqrestore(lock: &priv->driver_lock, flags); |
834 | |
835 | sdio_claim_host(func: card->func); |
836 | if (ireg & DN_LD_HOST_INT_STATUS) { |
837 | if (priv->btmrvl_dev.tx_dnld_rdy) |
838 | BT_DBG("tx_done already received: " |
839 | " int_status=0x%x" , ireg); |
840 | else |
841 | priv->btmrvl_dev.tx_dnld_rdy = true; |
842 | } |
843 | |
844 | if (ireg & UP_LD_HOST_INT_STATUS) |
845 | btmrvl_sdio_card_to_host(priv); |
846 | |
847 | sdio_release_host(func: card->func); |
848 | |
849 | return 0; |
850 | } |
851 | |
852 | static int btmrvl_sdio_read_to_clear(struct btmrvl_sdio_card *card, u8 *ireg) |
853 | { |
854 | struct btmrvl_adapter *adapter = card->priv->adapter; |
855 | int ret; |
856 | |
857 | ret = sdio_readsb(func: card->func, dst: adapter->hw_regs, addr: 0, SDIO_BLOCK_SIZE); |
858 | if (ret) { |
859 | BT_ERR("sdio_readsb: read int hw_regs failed: %d" , ret); |
860 | return ret; |
861 | } |
862 | |
863 | *ireg = adapter->hw_regs[card->reg->host_intstatus]; |
864 | BT_DBG("hw_regs[%#x]=%#x" , card->reg->host_intstatus, *ireg); |
865 | |
866 | return 0; |
867 | } |
868 | |
869 | static int btmrvl_sdio_write_to_clear(struct btmrvl_sdio_card *card, u8 *ireg) |
870 | { |
871 | int ret; |
872 | |
873 | *ireg = sdio_readb(func: card->func, addr: card->reg->host_intstatus, err_ret: &ret); |
874 | if (ret) { |
875 | BT_ERR("sdio_readb: read int status failed: %d" , ret); |
876 | return ret; |
877 | } |
878 | |
879 | if (*ireg) { |
880 | /* |
881 | * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS |
882 | * Clear the interrupt status register and re-enable the |
883 | * interrupt. |
884 | */ |
885 | BT_DBG("int_status = 0x%x" , *ireg); |
886 | |
887 | sdio_writeb(func: card->func, b: ~(*ireg) & (DN_LD_HOST_INT_STATUS | |
888 | UP_LD_HOST_INT_STATUS), |
889 | addr: card->reg->host_intstatus, err_ret: &ret); |
890 | if (ret) { |
891 | BT_ERR("sdio_writeb: clear int status failed: %d" , ret); |
892 | return ret; |
893 | } |
894 | } |
895 | |
896 | return 0; |
897 | } |
898 | |
899 | static void btmrvl_sdio_interrupt(struct sdio_func *func) |
900 | { |
901 | struct btmrvl_private *priv; |
902 | struct btmrvl_sdio_card *card; |
903 | ulong flags; |
904 | u8 ireg = 0; |
905 | int ret; |
906 | |
907 | card = sdio_get_drvdata(func); |
908 | if (!card || !card->priv) { |
909 | BT_ERR("sbi_interrupt(%p) card or priv is NULL, card=%p" , |
910 | func, card); |
911 | return; |
912 | } |
913 | |
914 | priv = card->priv; |
915 | |
916 | if (priv->surprise_removed) |
917 | return; |
918 | |
919 | if (card->reg->int_read_to_clear) |
920 | ret = btmrvl_sdio_read_to_clear(card, ireg: &ireg); |
921 | else |
922 | ret = btmrvl_sdio_write_to_clear(card, ireg: &ireg); |
923 | |
924 | if (ret) |
925 | return; |
926 | |
927 | spin_lock_irqsave(&priv->driver_lock, flags); |
928 | sdio_ireg |= ireg; |
929 | spin_unlock_irqrestore(lock: &priv->driver_lock, flags); |
930 | |
931 | btmrvl_interrupt(priv); |
932 | } |
933 | |
934 | static int btmrvl_sdio_register_dev(struct btmrvl_sdio_card *card) |
935 | { |
936 | struct sdio_func *func; |
937 | u8 reg; |
938 | int ret; |
939 | |
940 | if (!card || !card->func) { |
941 | BT_ERR("Error: card or function is NULL!" ); |
942 | ret = -EINVAL; |
943 | goto failed; |
944 | } |
945 | |
946 | func = card->func; |
947 | |
948 | sdio_claim_host(func); |
949 | |
950 | ret = sdio_enable_func(func); |
951 | if (ret) { |
952 | BT_ERR("sdio_enable_func() failed: ret=%d" , ret); |
953 | ret = -EIO; |
954 | goto release_host; |
955 | } |
956 | |
957 | ret = sdio_claim_irq(func, handler: btmrvl_sdio_interrupt); |
958 | if (ret) { |
959 | BT_ERR("sdio_claim_irq failed: ret=%d" , ret); |
960 | ret = -EIO; |
961 | goto disable_func; |
962 | } |
963 | |
964 | ret = sdio_set_block_size(func: card->func, SDIO_BLOCK_SIZE); |
965 | if (ret) { |
966 | BT_ERR("cannot set SDIO block size" ); |
967 | ret = -EIO; |
968 | goto release_irq; |
969 | } |
970 | |
971 | reg = sdio_readb(func, addr: card->reg->io_port_0, err_ret: &ret); |
972 | if (ret < 0) { |
973 | ret = -EIO; |
974 | goto release_irq; |
975 | } |
976 | |
977 | card->ioport = reg; |
978 | |
979 | reg = sdio_readb(func, addr: card->reg->io_port_1, err_ret: &ret); |
980 | if (ret < 0) { |
981 | ret = -EIO; |
982 | goto release_irq; |
983 | } |
984 | |
985 | card->ioport |= (reg << 8); |
986 | |
987 | reg = sdio_readb(func, addr: card->reg->io_port_2, err_ret: &ret); |
988 | if (ret < 0) { |
989 | ret = -EIO; |
990 | goto release_irq; |
991 | } |
992 | |
993 | card->ioport |= (reg << 16); |
994 | |
995 | BT_DBG("SDIO FUNC%d IO port: 0x%x" , func->num, card->ioport); |
996 | |
997 | if (card->reg->int_read_to_clear) { |
998 | reg = sdio_readb(func, addr: card->reg->host_int_rsr, err_ret: &ret); |
999 | if (ret < 0) { |
1000 | ret = -EIO; |
1001 | goto release_irq; |
1002 | } |
1003 | sdio_writeb(func, b: reg | 0x3f, addr: card->reg->host_int_rsr, err_ret: &ret); |
1004 | if (ret < 0) { |
1005 | ret = -EIO; |
1006 | goto release_irq; |
1007 | } |
1008 | |
1009 | reg = sdio_readb(func, addr: card->reg->card_misc_cfg, err_ret: &ret); |
1010 | if (ret < 0) { |
1011 | ret = -EIO; |
1012 | goto release_irq; |
1013 | } |
1014 | sdio_writeb(func, b: reg | 0x10, addr: card->reg->card_misc_cfg, err_ret: &ret); |
1015 | if (ret < 0) { |
1016 | ret = -EIO; |
1017 | goto release_irq; |
1018 | } |
1019 | } |
1020 | |
1021 | sdio_set_drvdata(func, card); |
1022 | |
1023 | sdio_release_host(func); |
1024 | |
1025 | return 0; |
1026 | |
1027 | release_irq: |
1028 | sdio_release_irq(func); |
1029 | |
1030 | disable_func: |
1031 | sdio_disable_func(func); |
1032 | |
1033 | release_host: |
1034 | sdio_release_host(func); |
1035 | |
1036 | failed: |
1037 | return ret; |
1038 | } |
1039 | |
1040 | static int btmrvl_sdio_unregister_dev(struct btmrvl_sdio_card *card) |
1041 | { |
1042 | if (card && card->func) { |
1043 | sdio_claim_host(func: card->func); |
1044 | sdio_release_irq(func: card->func); |
1045 | sdio_disable_func(func: card->func); |
1046 | sdio_release_host(func: card->func); |
1047 | sdio_set_drvdata(card->func, NULL); |
1048 | } |
1049 | |
1050 | return 0; |
1051 | } |
1052 | |
1053 | static int btmrvl_sdio_enable_host_int(struct btmrvl_sdio_card *card) |
1054 | { |
1055 | int ret; |
1056 | |
1057 | if (!card || !card->func) |
1058 | return -EINVAL; |
1059 | |
1060 | sdio_claim_host(func: card->func); |
1061 | |
1062 | ret = btmrvl_sdio_enable_host_int_mask(card, HIM_ENABLE); |
1063 | |
1064 | btmrvl_sdio_get_rx_unit(card); |
1065 | |
1066 | sdio_release_host(func: card->func); |
1067 | |
1068 | return ret; |
1069 | } |
1070 | |
1071 | static int btmrvl_sdio_disable_host_int(struct btmrvl_sdio_card *card) |
1072 | { |
1073 | int ret; |
1074 | |
1075 | if (!card || !card->func) |
1076 | return -EINVAL; |
1077 | |
1078 | sdio_claim_host(func: card->func); |
1079 | |
1080 | ret = btmrvl_sdio_disable_host_int_mask(card, HIM_DISABLE); |
1081 | |
1082 | sdio_release_host(func: card->func); |
1083 | |
1084 | return ret; |
1085 | } |
1086 | |
1087 | static int btmrvl_sdio_host_to_card(struct btmrvl_private *priv, |
1088 | u8 *payload, u16 nb) |
1089 | { |
1090 | struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; |
1091 | int ret = 0; |
1092 | int blksz; |
1093 | int i = 0; |
1094 | u8 *buf = NULL; |
1095 | void *tmpbuf = NULL; |
1096 | int tmpbufsz; |
1097 | |
1098 | if (!card || !card->func) { |
1099 | BT_ERR("card or function is NULL!" ); |
1100 | return -EINVAL; |
1101 | } |
1102 | |
1103 | blksz = DIV_ROUND_UP(nb, SDIO_BLOCK_SIZE) * SDIO_BLOCK_SIZE; |
1104 | |
1105 | buf = payload; |
1106 | if ((unsigned long) payload & (BTSDIO_DMA_ALIGN - 1) || |
1107 | nb < blksz) { |
1108 | tmpbufsz = ALIGN_SZ(blksz, BTSDIO_DMA_ALIGN) + |
1109 | BTSDIO_DMA_ALIGN; |
1110 | tmpbuf = kzalloc(size: tmpbufsz, GFP_KERNEL); |
1111 | if (!tmpbuf) |
1112 | return -ENOMEM; |
1113 | buf = (u8 *) ALIGN_ADDR(tmpbuf, BTSDIO_DMA_ALIGN); |
1114 | memcpy(buf, payload, nb); |
1115 | } |
1116 | |
1117 | sdio_claim_host(func: card->func); |
1118 | |
1119 | do { |
1120 | /* Transfer data to card */ |
1121 | ret = sdio_writesb(func: card->func, addr: card->ioport, src: buf, |
1122 | count: blksz); |
1123 | if (ret < 0) { |
1124 | i++; |
1125 | BT_ERR("i=%d writesb failed: %d" , i, ret); |
1126 | BT_ERR("hex: %*ph" , nb, payload); |
1127 | ret = -EIO; |
1128 | if (i > MAX_WRITE_IOMEM_RETRY) |
1129 | goto exit; |
1130 | } |
1131 | } while (ret); |
1132 | |
1133 | priv->btmrvl_dev.tx_dnld_rdy = false; |
1134 | |
1135 | exit: |
1136 | sdio_release_host(func: card->func); |
1137 | kfree(objp: tmpbuf); |
1138 | |
1139 | return ret; |
1140 | } |
1141 | |
1142 | static int btmrvl_sdio_download_fw(struct btmrvl_sdio_card *card) |
1143 | { |
1144 | int ret; |
1145 | u8 fws0; |
1146 | int pollnum = MAX_POLL_TRIES; |
1147 | |
1148 | if (!card || !card->func) { |
1149 | BT_ERR("card or function is NULL!" ); |
1150 | return -EINVAL; |
1151 | } |
1152 | |
1153 | if (!btmrvl_sdio_verify_fw_download(card, pollnum: 1)) { |
1154 | BT_DBG("Firmware already downloaded!" ); |
1155 | return 0; |
1156 | } |
1157 | |
1158 | sdio_claim_host(func: card->func); |
1159 | |
1160 | /* Check if other function driver is downloading the firmware */ |
1161 | fws0 = sdio_readb(func: card->func, addr: card->reg->card_fw_status0, err_ret: &ret); |
1162 | if (ret) { |
1163 | BT_ERR("Failed to read FW downloading status!" ); |
1164 | ret = -EIO; |
1165 | goto done; |
1166 | } |
1167 | if (fws0) { |
1168 | BT_DBG("BT not the winner (%#x). Skip FW downloading" , fws0); |
1169 | |
1170 | /* Give other function more time to download the firmware */ |
1171 | pollnum *= 10; |
1172 | } else { |
1173 | if (card->helper) { |
1174 | ret = btmrvl_sdio_download_helper(card); |
1175 | if (ret) { |
1176 | BT_ERR("Failed to download helper!" ); |
1177 | ret = -EIO; |
1178 | goto done; |
1179 | } |
1180 | } |
1181 | |
1182 | if (btmrvl_sdio_download_fw_w_helper(card)) { |
1183 | BT_ERR("Failed to download firmware!" ); |
1184 | ret = -EIO; |
1185 | goto done; |
1186 | } |
1187 | } |
1188 | |
1189 | /* |
1190 | * winner or not, with this test the FW synchronizes when the |
1191 | * module can continue its initialization |
1192 | */ |
1193 | if (btmrvl_sdio_verify_fw_download(card, pollnum)) { |
1194 | BT_ERR("FW failed to be active in time!" ); |
1195 | ret = -ETIMEDOUT; |
1196 | goto done; |
1197 | } |
1198 | |
1199 | sdio_release_host(func: card->func); |
1200 | |
1201 | return 0; |
1202 | |
1203 | done: |
1204 | sdio_release_host(func: card->func); |
1205 | return ret; |
1206 | } |
1207 | |
1208 | static int btmrvl_sdio_wakeup_fw(struct btmrvl_private *priv) |
1209 | { |
1210 | struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; |
1211 | int ret = 0; |
1212 | |
1213 | if (!card || !card->func) { |
1214 | BT_ERR("card or function is NULL!" ); |
1215 | return -EINVAL; |
1216 | } |
1217 | |
1218 | sdio_claim_host(func: card->func); |
1219 | |
1220 | sdio_writeb(func: card->func, HOST_POWER_UP, addr: card->reg->cfg, err_ret: &ret); |
1221 | |
1222 | sdio_release_host(func: card->func); |
1223 | |
1224 | BT_DBG("wake up firmware" ); |
1225 | |
1226 | return ret; |
1227 | } |
1228 | |
1229 | static void btmrvl_sdio_dump_regs(struct btmrvl_private *priv) |
1230 | { |
1231 | struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; |
1232 | int ret = 0; |
1233 | unsigned int reg, reg_start, reg_end; |
1234 | char buf[256], *ptr; |
1235 | u8 loop, func, data; |
1236 | int MAX_LOOP = 2; |
1237 | |
1238 | btmrvl_sdio_wakeup_fw(priv); |
1239 | sdio_claim_host(func: card->func); |
1240 | |
1241 | for (loop = 0; loop < MAX_LOOP; loop++) { |
1242 | memset(buf, 0, sizeof(buf)); |
1243 | ptr = buf; |
1244 | |
1245 | if (loop == 0) { |
1246 | /* Read the registers of SDIO function0 */ |
1247 | func = loop; |
1248 | reg_start = 0; |
1249 | reg_end = 9; |
1250 | } else { |
1251 | func = 2; |
1252 | reg_start = 0; |
1253 | reg_end = 0x09; |
1254 | } |
1255 | |
1256 | ptr += sprintf(buf: ptr, fmt: "SDIO Func%d (%#x-%#x): " , |
1257 | func, reg_start, reg_end); |
1258 | for (reg = reg_start; reg <= reg_end; reg++) { |
1259 | if (func == 0) |
1260 | data = sdio_f0_readb(func: card->func, addr: reg, err_ret: &ret); |
1261 | else |
1262 | data = sdio_readb(func: card->func, addr: reg, err_ret: &ret); |
1263 | |
1264 | if (!ret) { |
1265 | ptr += sprintf(buf: ptr, fmt: "%02x " , data); |
1266 | } else { |
1267 | ptr += sprintf(buf: ptr, fmt: "ERR" ); |
1268 | break; |
1269 | } |
1270 | } |
1271 | |
1272 | BT_INFO("%s" , buf); |
1273 | } |
1274 | |
1275 | sdio_release_host(func: card->func); |
1276 | } |
1277 | |
1278 | /* This function read/write firmware */ |
1279 | static enum |
1280 | rdwr_status btmrvl_sdio_rdwr_firmware(struct btmrvl_private *priv, |
1281 | u8 doneflag) |
1282 | { |
1283 | struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; |
1284 | int ret, tries; |
1285 | u8 ctrl_data = 0; |
1286 | |
1287 | sdio_writeb(func: card->func, FW_DUMP_HOST_READY, addr: card->reg->fw_dump_ctrl, |
1288 | err_ret: &ret); |
1289 | |
1290 | if (ret) { |
1291 | BT_ERR("SDIO write err" ); |
1292 | return RDWR_STATUS_FAILURE; |
1293 | } |
1294 | |
1295 | for (tries = 0; tries < MAX_POLL_TRIES; tries++) { |
1296 | ctrl_data = sdio_readb(func: card->func, addr: card->reg->fw_dump_ctrl, |
1297 | err_ret: &ret); |
1298 | |
1299 | if (ret) { |
1300 | BT_ERR("SDIO read err" ); |
1301 | return RDWR_STATUS_FAILURE; |
1302 | } |
1303 | |
1304 | if (ctrl_data == FW_DUMP_DONE) |
1305 | break; |
1306 | if (doneflag && ctrl_data == doneflag) |
1307 | return RDWR_STATUS_DONE; |
1308 | if (ctrl_data != FW_DUMP_HOST_READY) { |
1309 | BT_INFO("The ctrl reg was changed, re-try again!" ); |
1310 | sdio_writeb(func: card->func, FW_DUMP_HOST_READY, |
1311 | addr: card->reg->fw_dump_ctrl, err_ret: &ret); |
1312 | if (ret) { |
1313 | BT_ERR("SDIO write err" ); |
1314 | return RDWR_STATUS_FAILURE; |
1315 | } |
1316 | } |
1317 | usleep_range(min: 100, max: 200); |
1318 | } |
1319 | |
1320 | if (ctrl_data == FW_DUMP_HOST_READY) { |
1321 | BT_ERR("Fail to pull ctrl_data" ); |
1322 | return RDWR_STATUS_FAILURE; |
1323 | } |
1324 | |
1325 | return RDWR_STATUS_SUCCESS; |
1326 | } |
1327 | |
1328 | /* This function dump sdio register and memory data */ |
1329 | static void btmrvl_sdio_coredump(struct device *dev) |
1330 | { |
1331 | struct sdio_func *func = dev_to_sdio_func(dev); |
1332 | struct btmrvl_sdio_card *card; |
1333 | struct btmrvl_private *priv; |
1334 | int ret = 0; |
1335 | unsigned int reg, reg_start, reg_end; |
1336 | enum rdwr_status stat; |
1337 | u8 *dbg_ptr, *end_ptr, *fw_dump_data, *fw_dump_ptr; |
1338 | u8 dump_num = 0, idx, i, read_reg, doneflag = 0; |
1339 | u32 memory_size, fw_dump_len = 0; |
1340 | int size = 0; |
1341 | |
1342 | card = sdio_get_drvdata(func); |
1343 | priv = card->priv; |
1344 | |
1345 | /* dump sdio register first */ |
1346 | btmrvl_sdio_dump_regs(priv); |
1347 | |
1348 | if (!card->supports_fw_dump) { |
1349 | BT_ERR("Firmware dump not supported for this card!" ); |
1350 | return; |
1351 | } |
1352 | |
1353 | for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) { |
1354 | struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; |
1355 | |
1356 | if (entry->mem_ptr) { |
1357 | vfree(addr: entry->mem_ptr); |
1358 | entry->mem_ptr = NULL; |
1359 | } |
1360 | entry->mem_size = 0; |
1361 | } |
1362 | |
1363 | btmrvl_sdio_wakeup_fw(priv); |
1364 | sdio_claim_host(func: card->func); |
1365 | |
1366 | BT_INFO("== btmrvl firmware dump start ==" ); |
1367 | |
1368 | stat = btmrvl_sdio_rdwr_firmware(priv, doneflag); |
1369 | if (stat == RDWR_STATUS_FAILURE) |
1370 | goto done; |
1371 | |
1372 | reg = card->reg->fw_dump_start; |
1373 | /* Read the number of the memories which will dump */ |
1374 | dump_num = sdio_readb(func: card->func, addr: reg, err_ret: &ret); |
1375 | |
1376 | if (ret) { |
1377 | BT_ERR("SDIO read memory length err" ); |
1378 | goto done; |
1379 | } |
1380 | |
1381 | /* Read the length of every memory which will dump */ |
1382 | for (idx = 0; idx < dump_num; idx++) { |
1383 | struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; |
1384 | |
1385 | stat = btmrvl_sdio_rdwr_firmware(priv, doneflag); |
1386 | if (stat == RDWR_STATUS_FAILURE) |
1387 | goto done; |
1388 | |
1389 | memory_size = 0; |
1390 | reg = card->reg->fw_dump_start; |
1391 | for (i = 0; i < 4; i++) { |
1392 | read_reg = sdio_readb(func: card->func, addr: reg, err_ret: &ret); |
1393 | if (ret) { |
1394 | BT_ERR("SDIO read err" ); |
1395 | goto done; |
1396 | } |
1397 | memory_size |= (read_reg << i*8); |
1398 | reg++; |
1399 | } |
1400 | |
1401 | if (memory_size == 0) { |
1402 | BT_INFO("Firmware dump finished!" ); |
1403 | sdio_writeb(func: card->func, FW_DUMP_READ_DONE, |
1404 | addr: card->reg->fw_dump_ctrl, err_ret: &ret); |
1405 | if (ret) { |
1406 | BT_ERR("SDIO Write MEMDUMP_FINISH ERR" ); |
1407 | goto done; |
1408 | } |
1409 | break; |
1410 | } |
1411 | |
1412 | BT_INFO("%s_SIZE=0x%x" , entry->mem_name, memory_size); |
1413 | entry->mem_ptr = vzalloc(size: memory_size + 1); |
1414 | entry->mem_size = memory_size; |
1415 | if (!entry->mem_ptr) { |
1416 | BT_ERR("Vzalloc %s failed" , entry->mem_name); |
1417 | goto done; |
1418 | } |
1419 | |
1420 | fw_dump_len += (strlen("========Start dump " ) + |
1421 | strlen(entry->mem_name) + |
1422 | strlen("========\n" ) + |
1423 | (memory_size + 1) + |
1424 | strlen("\n========End dump========\n" )); |
1425 | |
1426 | dbg_ptr = entry->mem_ptr; |
1427 | end_ptr = dbg_ptr + memory_size; |
1428 | |
1429 | doneflag = entry->done_flag; |
1430 | BT_INFO("Start %s output, please wait..." , |
1431 | entry->mem_name); |
1432 | |
1433 | do { |
1434 | stat = btmrvl_sdio_rdwr_firmware(priv, doneflag); |
1435 | if (stat == RDWR_STATUS_FAILURE) |
1436 | goto done; |
1437 | |
1438 | reg_start = card->reg->fw_dump_start; |
1439 | reg_end = card->reg->fw_dump_end; |
1440 | for (reg = reg_start; reg <= reg_end; reg++) { |
1441 | *dbg_ptr = sdio_readb(func: card->func, addr: reg, err_ret: &ret); |
1442 | if (ret) { |
1443 | BT_ERR("SDIO read err" ); |
1444 | goto done; |
1445 | } |
1446 | if (dbg_ptr < end_ptr) |
1447 | dbg_ptr++; |
1448 | else |
1449 | BT_ERR("Allocated buffer not enough" ); |
1450 | } |
1451 | |
1452 | if (stat == RDWR_STATUS_DONE) { |
1453 | BT_INFO("%s done: size=0x%tx" , |
1454 | entry->mem_name, |
1455 | dbg_ptr - entry->mem_ptr); |
1456 | break; |
1457 | } |
1458 | } while (1); |
1459 | } |
1460 | |
1461 | BT_INFO("== btmrvl firmware dump end ==" ); |
1462 | |
1463 | done: |
1464 | sdio_release_host(func: card->func); |
1465 | |
1466 | if (fw_dump_len == 0) |
1467 | return; |
1468 | |
1469 | fw_dump_data = vzalloc(size: fw_dump_len + 1); |
1470 | if (!fw_dump_data) { |
1471 | BT_ERR("Vzalloc fw_dump_data fail!" ); |
1472 | return; |
1473 | } |
1474 | fw_dump_ptr = fw_dump_data; |
1475 | |
1476 | /* Dump all the memory data into single file, a userspace script will |
1477 | * be used to split all the memory data to multiple files |
1478 | */ |
1479 | BT_INFO("== btmrvl firmware dump to /sys/class/devcoredump start" ); |
1480 | for (idx = 0; idx < dump_num; idx++) { |
1481 | struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; |
1482 | |
1483 | if (entry->mem_ptr) { |
1484 | size += scnprintf(buf: fw_dump_ptr + size, |
1485 | size: fw_dump_len + 1 - size, |
1486 | fmt: "========Start dump %s========\n" , |
1487 | entry->mem_name); |
1488 | |
1489 | memcpy(fw_dump_ptr + size, entry->mem_ptr, |
1490 | entry->mem_size); |
1491 | size += entry->mem_size; |
1492 | |
1493 | size += scnprintf(buf: fw_dump_ptr + size, |
1494 | size: fw_dump_len + 1 - size, |
1495 | fmt: "\n========End dump========\n" ); |
1496 | |
1497 | vfree(addr: mem_type_mapping_tbl[idx].mem_ptr); |
1498 | mem_type_mapping_tbl[idx].mem_ptr = NULL; |
1499 | } |
1500 | } |
1501 | |
1502 | /* fw_dump_data will be free in device coredump release function |
1503 | * after 5 min |
1504 | */ |
1505 | dev_coredumpv(dev: &card->func->dev, data: fw_dump_data, datalen: fw_dump_len, GFP_KERNEL); |
1506 | BT_INFO("== btmrvl firmware dump to /sys/class/devcoredump end" ); |
1507 | } |
1508 | |
1509 | static int btmrvl_sdio_probe(struct sdio_func *func, |
1510 | const struct sdio_device_id *id) |
1511 | { |
1512 | int ret = 0; |
1513 | struct btmrvl_private *priv = NULL; |
1514 | struct btmrvl_sdio_card *card = NULL; |
1515 | |
1516 | BT_INFO("vendor=0x%x, device=0x%x, class=%d, fn=%d" , |
1517 | id->vendor, id->device, id->class, func->num); |
1518 | |
1519 | card = devm_kzalloc(dev: &func->dev, size: sizeof(*card), GFP_KERNEL); |
1520 | if (!card) |
1521 | return -ENOMEM; |
1522 | |
1523 | card->func = func; |
1524 | |
1525 | if (id->driver_data) { |
1526 | struct btmrvl_sdio_device *data = (void *) id->driver_data; |
1527 | card->helper = data->helper; |
1528 | card->firmware = data->firmware; |
1529 | card->reg = data->reg; |
1530 | card->sd_blksz_fw_dl = data->sd_blksz_fw_dl; |
1531 | card->support_pscan_win_report = data->support_pscan_win_report; |
1532 | card->supports_fw_dump = data->supports_fw_dump; |
1533 | } |
1534 | |
1535 | if (btmrvl_sdio_register_dev(card) < 0) { |
1536 | BT_ERR("Failed to register BT device!" ); |
1537 | return -ENODEV; |
1538 | } |
1539 | |
1540 | /* Disable the interrupts on the card */ |
1541 | btmrvl_sdio_disable_host_int(card); |
1542 | |
1543 | if (btmrvl_sdio_download_fw(card)) { |
1544 | BT_ERR("Downloading firmware failed!" ); |
1545 | ret = -ENODEV; |
1546 | goto unreg_dev; |
1547 | } |
1548 | |
1549 | btmrvl_sdio_enable_host_int(card); |
1550 | |
1551 | /* Device tree node parsing and platform specific configuration*/ |
1552 | btmrvl_sdio_probe_of(dev: &func->dev, card); |
1553 | |
1554 | priv = btmrvl_add_card(card); |
1555 | if (!priv) { |
1556 | BT_ERR("Initializing card failed!" ); |
1557 | ret = -ENODEV; |
1558 | goto disable_host_int; |
1559 | } |
1560 | |
1561 | card->priv = priv; |
1562 | |
1563 | /* Initialize the interface specific function pointers */ |
1564 | priv->hw_host_to_card = btmrvl_sdio_host_to_card; |
1565 | priv->hw_wakeup_firmware = btmrvl_sdio_wakeup_fw; |
1566 | priv->hw_process_int_status = btmrvl_sdio_process_int_status; |
1567 | |
1568 | if (btmrvl_register_hdev(priv)) { |
1569 | BT_ERR("Register hdev failed!" ); |
1570 | ret = -ENODEV; |
1571 | goto disable_host_int; |
1572 | } |
1573 | |
1574 | return 0; |
1575 | |
1576 | disable_host_int: |
1577 | btmrvl_sdio_disable_host_int(card); |
1578 | unreg_dev: |
1579 | btmrvl_sdio_unregister_dev(card); |
1580 | return ret; |
1581 | } |
1582 | |
1583 | static void btmrvl_sdio_remove(struct sdio_func *func) |
1584 | { |
1585 | struct btmrvl_sdio_card *card; |
1586 | |
1587 | if (func) { |
1588 | card = sdio_get_drvdata(func); |
1589 | if (card) { |
1590 | /* Send SHUTDOWN command & disable interrupt |
1591 | * if user removes the module. |
1592 | */ |
1593 | if (user_rmmod) { |
1594 | btmrvl_send_module_cfg_cmd(priv: card->priv, |
1595 | MODULE_SHUTDOWN_REQ); |
1596 | btmrvl_sdio_disable_host_int(card); |
1597 | } |
1598 | |
1599 | BT_DBG("unregister dev" ); |
1600 | card->priv->surprise_removed = true; |
1601 | btmrvl_sdio_unregister_dev(card); |
1602 | btmrvl_remove_card(priv: card->priv); |
1603 | } |
1604 | } |
1605 | } |
1606 | |
1607 | static int btmrvl_sdio_suspend(struct device *dev) |
1608 | { |
1609 | struct sdio_func *func = dev_to_sdio_func(dev); |
1610 | struct btmrvl_sdio_card *card; |
1611 | struct btmrvl_private *priv; |
1612 | mmc_pm_flag_t pm_flags; |
1613 | struct hci_dev *hcidev; |
1614 | |
1615 | if (func) { |
1616 | pm_flags = sdio_get_host_pm_caps(func); |
1617 | BT_DBG("%s: suspend: PM flags = 0x%x" , sdio_func_id(func), |
1618 | pm_flags); |
1619 | if (!(pm_flags & MMC_PM_KEEP_POWER)) { |
1620 | BT_ERR("%s: cannot remain alive while suspended" , |
1621 | sdio_func_id(func)); |
1622 | return -ENOSYS; |
1623 | } |
1624 | card = sdio_get_drvdata(func); |
1625 | if (!card || !card->priv) { |
1626 | BT_ERR("card or priv structure is not valid" ); |
1627 | return 0; |
1628 | } |
1629 | } else { |
1630 | BT_ERR("sdio_func is not specified" ); |
1631 | return 0; |
1632 | } |
1633 | |
1634 | /* Enable platform specific wakeup interrupt */ |
1635 | if (card->plt_wake_cfg && card->plt_wake_cfg->irq_bt >= 0 && |
1636 | device_may_wakeup(dev)) { |
1637 | card->plt_wake_cfg->wake_by_bt = false; |
1638 | enable_irq(irq: card->plt_wake_cfg->irq_bt); |
1639 | enable_irq_wake(irq: card->plt_wake_cfg->irq_bt); |
1640 | } |
1641 | |
1642 | priv = card->priv; |
1643 | priv->adapter->is_suspending = true; |
1644 | hcidev = priv->btmrvl_dev.hcidev; |
1645 | BT_DBG("%s: SDIO suspend" , hcidev->name); |
1646 | hci_suspend_dev(hdev: hcidev); |
1647 | |
1648 | if (priv->adapter->hs_state != HS_ACTIVATED) { |
1649 | if (btmrvl_enable_hs(priv)) { |
1650 | BT_ERR("HS not activated, suspend failed!" ); |
1651 | /* Disable platform specific wakeup interrupt */ |
1652 | if (card->plt_wake_cfg && |
1653 | card->plt_wake_cfg->irq_bt >= 0 && |
1654 | device_may_wakeup(dev)) { |
1655 | disable_irq_wake(irq: card->plt_wake_cfg->irq_bt); |
1656 | disable_irq(irq: card->plt_wake_cfg->irq_bt); |
1657 | } |
1658 | |
1659 | priv->adapter->is_suspending = false; |
1660 | return -EBUSY; |
1661 | } |
1662 | } |
1663 | |
1664 | priv->adapter->is_suspending = false; |
1665 | priv->adapter->is_suspended = true; |
1666 | |
1667 | /* We will keep the power when hs enabled successfully */ |
1668 | if (priv->adapter->hs_state == HS_ACTIVATED) { |
1669 | BT_DBG("suspend with MMC_PM_KEEP_POWER" ); |
1670 | return sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); |
1671 | } |
1672 | |
1673 | BT_DBG("suspend without MMC_PM_KEEP_POWER" ); |
1674 | return 0; |
1675 | } |
1676 | |
1677 | static int btmrvl_sdio_resume(struct device *dev) |
1678 | { |
1679 | struct sdio_func *func = dev_to_sdio_func(dev); |
1680 | struct btmrvl_sdio_card *card; |
1681 | struct btmrvl_private *priv; |
1682 | mmc_pm_flag_t pm_flags; |
1683 | struct hci_dev *hcidev; |
1684 | |
1685 | if (func) { |
1686 | pm_flags = sdio_get_host_pm_caps(func); |
1687 | BT_DBG("%s: resume: PM flags = 0x%x" , sdio_func_id(func), |
1688 | pm_flags); |
1689 | card = sdio_get_drvdata(func); |
1690 | if (!card || !card->priv) { |
1691 | BT_ERR("card or priv structure is not valid" ); |
1692 | return 0; |
1693 | } |
1694 | } else { |
1695 | BT_ERR("sdio_func is not specified" ); |
1696 | return 0; |
1697 | } |
1698 | priv = card->priv; |
1699 | |
1700 | if (!priv->adapter->is_suspended) { |
1701 | BT_DBG("device already resumed" ); |
1702 | return 0; |
1703 | } |
1704 | |
1705 | priv->hw_wakeup_firmware(priv); |
1706 | priv->adapter->hs_state = HS_DEACTIVATED; |
1707 | hcidev = priv->btmrvl_dev.hcidev; |
1708 | BT_DBG("%s: HS DEACTIVATED in resume!" , hcidev->name); |
1709 | priv->adapter->is_suspended = false; |
1710 | BT_DBG("%s: SDIO resume" , hcidev->name); |
1711 | hci_resume_dev(hdev: hcidev); |
1712 | |
1713 | /* Disable platform specific wakeup interrupt */ |
1714 | if (card->plt_wake_cfg && card->plt_wake_cfg->irq_bt >= 0 && |
1715 | device_may_wakeup(dev)) { |
1716 | disable_irq_wake(irq: card->plt_wake_cfg->irq_bt); |
1717 | disable_irq(irq: card->plt_wake_cfg->irq_bt); |
1718 | if (card->plt_wake_cfg->wake_by_bt) |
1719 | /* Undo our disable, since interrupt handler already |
1720 | * did this. |
1721 | */ |
1722 | enable_irq(irq: card->plt_wake_cfg->irq_bt); |
1723 | } |
1724 | |
1725 | return 0; |
1726 | } |
1727 | |
1728 | static const struct dev_pm_ops btmrvl_sdio_pm_ops = { |
1729 | .suspend = btmrvl_sdio_suspend, |
1730 | .resume = btmrvl_sdio_resume, |
1731 | }; |
1732 | |
1733 | static struct sdio_driver bt_mrvl_sdio = { |
1734 | .name = "btmrvl_sdio" , |
1735 | .id_table = btmrvl_sdio_ids, |
1736 | .probe = btmrvl_sdio_probe, |
1737 | .remove = btmrvl_sdio_remove, |
1738 | .drv = { |
1739 | .owner = THIS_MODULE, |
1740 | .coredump = btmrvl_sdio_coredump, |
1741 | .pm = &btmrvl_sdio_pm_ops, |
1742 | } |
1743 | }; |
1744 | |
1745 | static int __init btmrvl_sdio_init_module(void) |
1746 | { |
1747 | if (sdio_register_driver(&bt_mrvl_sdio) != 0) { |
1748 | BT_ERR("SDIO Driver Registration Failed" ); |
1749 | return -ENODEV; |
1750 | } |
1751 | |
1752 | /* Clear the flag in case user removes the card. */ |
1753 | user_rmmod = 0; |
1754 | |
1755 | return 0; |
1756 | } |
1757 | |
1758 | static void __exit btmrvl_sdio_exit_module(void) |
1759 | { |
1760 | /* Set the flag as user is removing this module. */ |
1761 | user_rmmod = 1; |
1762 | |
1763 | sdio_unregister_driver(&bt_mrvl_sdio); |
1764 | } |
1765 | |
1766 | module_init(btmrvl_sdio_init_module); |
1767 | module_exit(btmrvl_sdio_exit_module); |
1768 | |
1769 | MODULE_AUTHOR("Marvell International Ltd." ); |
1770 | MODULE_DESCRIPTION("Marvell BT-over-SDIO driver ver " VERSION); |
1771 | MODULE_VERSION(VERSION); |
1772 | MODULE_LICENSE("GPL v2" ); |
1773 | MODULE_FIRMWARE("mrvl/sd8688_helper.bin" ); |
1774 | MODULE_FIRMWARE("mrvl/sd8688.bin" ); |
1775 | MODULE_FIRMWARE("mrvl/sd8787_uapsta.bin" ); |
1776 | MODULE_FIRMWARE("mrvl/sd8797_uapsta.bin" ); |
1777 | MODULE_FIRMWARE("mrvl/sd8887_uapsta.bin" ); |
1778 | MODULE_FIRMWARE("mrvl/sd8897_uapsta.bin" ); |
1779 | MODULE_FIRMWARE("mrvl/sdsd8977_combo_v2.bin" ); |
1780 | MODULE_FIRMWARE("mrvl/sd8987_uapsta.bin" ); |
1781 | MODULE_FIRMWARE("mrvl/sdsd8997_combo_v4.bin" ); |
1782 | |