| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * |
|---|
| 3 | 4 | * Bluetooth support for Broadcom devices |
|---|
| 4 | 5 | * |
|---|
| 5 | 6 | * Copyright (C) 2015 Intel Corporation |
|---|
| 6 | | - * |
|---|
| 7 | | - * |
|---|
| 8 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 9 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 10 | | - * the Free Software Foundation; either version 2 of the License, or |
|---|
| 11 | | - * (at your option) any later version. |
|---|
| 12 | | - * |
|---|
| 13 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 14 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 15 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 16 | | - * GNU General Public License for more details. |
|---|
| 17 | | - * |
|---|
| 18 | | - * You should have received a copy of the GNU General Public License |
|---|
| 19 | | - * along with this program; if not, write to the Free Software |
|---|
| 20 | | - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|---|
| 21 | | - * |
|---|
| 22 | 7 | */ |
|---|
| 23 | 8 | |
|---|
| 24 | 9 | #include <linux/module.h> |
|---|
| .. | .. |
|---|
| 33 | 18 | #define VERSION "0.1" |
|---|
| 34 | 19 | |
|---|
| 35 | 20 | #define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00}}) |
|---|
| 21 | +#define BDADDR_BCM20702A1 (&(bdaddr_t) {{0x00, 0x00, 0xa0, 0x02, 0x70, 0x20}}) |
|---|
| 22 | +#define BDADDR_BCM2076B1 (&(bdaddr_t) {{0x79, 0x56, 0x00, 0xa0, 0x76, 0x20}}) |
|---|
| 23 | +#define BDADDR_BCM43430A0 (&(bdaddr_t) {{0xac, 0x1f, 0x12, 0xa0, 0x43, 0x43}}) |
|---|
| 36 | 24 | #define BDADDR_BCM4324B3 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb3, 0x24, 0x43}}) |
|---|
| 37 | 25 | #define BDADDR_BCM4330B1 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb1, 0x30, 0x43}}) |
|---|
| 26 | +#define BDADDR_BCM4334B0 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb0, 0x34, 0x43}}) |
|---|
| 27 | +#define BDADDR_BCM4345C5 (&(bdaddr_t) {{0xac, 0x1f, 0x00, 0xc5, 0x45, 0x43}}) |
|---|
| 28 | +#define BDADDR_BCM43341B (&(bdaddr_t) {{0xac, 0x1f, 0x00, 0x1b, 0x34, 0x43}}) |
|---|
| 29 | + |
|---|
| 30 | +#define BCM_FW_NAME_LEN 64 |
|---|
| 31 | +#define BCM_FW_NAME_COUNT_MAX 2 |
|---|
| 32 | +/* For kmalloc-ing the fw-name array instead of putting it on the stack */ |
|---|
| 33 | +typedef char bcm_fw_name[BCM_FW_NAME_LEN]; |
|---|
| 38 | 34 | |
|---|
| 39 | 35 | int btbcm_check_bdaddr(struct hci_dev *hdev) |
|---|
| 40 | 36 | { |
|---|
| .. | .. |
|---|
| 45 | 41 | HCI_INIT_TIMEOUT); |
|---|
| 46 | 42 | if (IS_ERR(skb)) { |
|---|
| 47 | 43 | int err = PTR_ERR(skb); |
|---|
| 44 | + |
|---|
| 48 | 45 | bt_dev_err(hdev, "BCM: Reading device address failed (%d)", err); |
|---|
| 49 | 46 | return err; |
|---|
| 50 | 47 | } |
|---|
| .. | .. |
|---|
| 64 | 61 | * The address 00:20:70:02:A0:00 indicates a BCM20702A0 controller |
|---|
| 65 | 62 | * with no configured address. |
|---|
| 66 | 63 | * |
|---|
| 64 | + * The address 20:70:02:A0:00:00 indicates a BCM20702A1 controller |
|---|
| 65 | + * with no configured address. |
|---|
| 66 | + * |
|---|
| 67 | + * The address 20:76:A0:00:56:79 indicates a BCM2076B1 controller |
|---|
| 68 | + * with no configured address. |
|---|
| 69 | + * |
|---|
| 67 | 70 | * The address 43:24:B3:00:00:00 indicates a BCM4324B3 controller |
|---|
| 68 | 71 | * with waiting for configuration state. |
|---|
| 69 | 72 | * |
|---|
| 70 | 73 | * The address 43:30:B1:00:00:00 indicates a BCM4330B1 controller |
|---|
| 71 | 74 | * with waiting for configuration state. |
|---|
| 75 | + * |
|---|
| 76 | + * The address 43:43:A0:12:1F:AC indicates a BCM43430A0 controller |
|---|
| 77 | + * with no configured address. |
|---|
| 72 | 78 | */ |
|---|
| 73 | 79 | if (!bacmp(&bda->bdaddr, BDADDR_BCM20702A0) || |
|---|
| 80 | + !bacmp(&bda->bdaddr, BDADDR_BCM20702A1) || |
|---|
| 81 | + !bacmp(&bda->bdaddr, BDADDR_BCM2076B1) || |
|---|
| 74 | 82 | !bacmp(&bda->bdaddr, BDADDR_BCM4324B3) || |
|---|
| 75 | | - !bacmp(&bda->bdaddr, BDADDR_BCM4330B1)) { |
|---|
| 83 | + !bacmp(&bda->bdaddr, BDADDR_BCM4330B1) || |
|---|
| 84 | + !bacmp(&bda->bdaddr, BDADDR_BCM4334B0) || |
|---|
| 85 | + !bacmp(&bda->bdaddr, BDADDR_BCM4345C5) || |
|---|
| 86 | + !bacmp(&bda->bdaddr, BDADDR_BCM43430A0) || |
|---|
| 87 | + !bacmp(&bda->bdaddr, BDADDR_BCM43341B)) { |
|---|
| 76 | 88 | bt_dev_info(hdev, "BCM: Using default device address (%pMR)", |
|---|
| 77 | 89 | &bda->bdaddr); |
|---|
| 78 | 90 | set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); |
|---|
| .. | .. |
|---|
| 100 | 112 | return 0; |
|---|
| 101 | 113 | } |
|---|
| 102 | 114 | EXPORT_SYMBOL_GPL(btbcm_set_bdaddr); |
|---|
| 115 | + |
|---|
| 116 | +int btbcm_read_pcm_int_params(struct hci_dev *hdev, |
|---|
| 117 | + struct bcm_set_pcm_int_params *params) |
|---|
| 118 | +{ |
|---|
| 119 | + struct sk_buff *skb; |
|---|
| 120 | + int err = 0; |
|---|
| 121 | + |
|---|
| 122 | + skb = __hci_cmd_sync(hdev, 0xfc1d, 0, NULL, HCI_INIT_TIMEOUT); |
|---|
| 123 | + if (IS_ERR(skb)) { |
|---|
| 124 | + err = PTR_ERR(skb); |
|---|
| 125 | + bt_dev_err(hdev, "BCM: Read PCM int params failed (%d)", err); |
|---|
| 126 | + return err; |
|---|
| 127 | + } |
|---|
| 128 | + |
|---|
| 129 | + if (skb->len != 6 || skb->data[0]) { |
|---|
| 130 | + bt_dev_err(hdev, "BCM: Read PCM int params length mismatch"); |
|---|
| 131 | + kfree_skb(skb); |
|---|
| 132 | + return -EIO; |
|---|
| 133 | + } |
|---|
| 134 | + |
|---|
| 135 | + if (params) |
|---|
| 136 | + memcpy(params, skb->data + 1, 5); |
|---|
| 137 | + |
|---|
| 138 | + kfree_skb(skb); |
|---|
| 139 | + |
|---|
| 140 | + return 0; |
|---|
| 141 | +} |
|---|
| 142 | +EXPORT_SYMBOL_GPL(btbcm_read_pcm_int_params); |
|---|
| 143 | + |
|---|
| 144 | +int btbcm_write_pcm_int_params(struct hci_dev *hdev, |
|---|
| 145 | + const struct bcm_set_pcm_int_params *params) |
|---|
| 146 | +{ |
|---|
| 147 | + struct sk_buff *skb; |
|---|
| 148 | + int err; |
|---|
| 149 | + |
|---|
| 150 | + skb = __hci_cmd_sync(hdev, 0xfc1c, 5, params, HCI_INIT_TIMEOUT); |
|---|
| 151 | + if (IS_ERR(skb)) { |
|---|
| 152 | + err = PTR_ERR(skb); |
|---|
| 153 | + bt_dev_err(hdev, "BCM: Write PCM int params failed (%d)", err); |
|---|
| 154 | + return err; |
|---|
| 155 | + } |
|---|
| 156 | + kfree_skb(skb); |
|---|
| 157 | + |
|---|
| 158 | + return 0; |
|---|
| 159 | +} |
|---|
| 160 | +EXPORT_SYMBOL_GPL(btbcm_write_pcm_int_params); |
|---|
| 103 | 161 | |
|---|
| 104 | 162 | int btbcm_patchram(struct hci_dev *hdev, const struct firmware *fw) |
|---|
| 105 | 163 | { |
|---|
| .. | .. |
|---|
| 171 | 229 | skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); |
|---|
| 172 | 230 | if (IS_ERR(skb)) { |
|---|
| 173 | 231 | int err = PTR_ERR(skb); |
|---|
| 232 | + |
|---|
| 174 | 233 | bt_dev_err(hdev, "BCM: Reset failed (%d)", err); |
|---|
| 175 | 234 | return err; |
|---|
| 176 | 235 | } |
|---|
| .. | .. |
|---|
| 304 | 363 | bt_dev_info(hdev, "BCM: features 0x%2.2x", skb->data[1]); |
|---|
| 305 | 364 | kfree_skb(skb); |
|---|
| 306 | 365 | |
|---|
| 366 | + return 0; |
|---|
| 367 | +} |
|---|
| 368 | + |
|---|
| 369 | +static int btbcm_print_local_name(struct hci_dev *hdev) |
|---|
| 370 | +{ |
|---|
| 371 | + struct sk_buff *skb; |
|---|
| 372 | + |
|---|
| 307 | 373 | /* Read Local Name */ |
|---|
| 308 | 374 | skb = btbcm_read_local_name(hdev); |
|---|
| 309 | 375 | if (IS_ERR(skb)) |
|---|
| .. | .. |
|---|
| 322 | 388 | |
|---|
| 323 | 389 | static const struct bcm_subver_table bcm_uart_subver_table[] = { |
|---|
| 324 | 390 | { 0x4103, "BCM4330B1" }, /* 002.001.003 */ |
|---|
| 391 | + { 0x410d, "BCM4334B0" }, /* 002.001.013 */ |
|---|
| 325 | 392 | { 0x410e, "BCM43341B0" }, /* 002.001.014 */ |
|---|
| 393 | + { 0x4204, "BCM2076B1" }, /* 002.002.004 */ |
|---|
| 326 | 394 | { 0x4406, "BCM4324B3" }, /* 002.004.006 */ |
|---|
| 327 | 395 | { 0x4606, "BCM4324B5" }, /* 002.006.006 */ |
|---|
| 328 | 396 | { 0x6109, "BCM4335C0" }, /* 003.001.009 */ |
|---|
| .. | .. |
|---|
| 330 | 398 | { 0x2122, "BCM4343A0" }, /* 001.001.034 */ |
|---|
| 331 | 399 | { 0x2209, "BCM43430A1" }, /* 001.002.009 */ |
|---|
| 332 | 400 | { 0x6119, "BCM4345C0" }, /* 003.001.025 */ |
|---|
| 401 | + { 0x6606, "BCM4345C5" }, /* 003.006.006 */ |
|---|
| 333 | 402 | { 0x230f, "BCM4356A2" }, /* 001.003.015 */ |
|---|
| 403 | + { 0x220e, "BCM20702A1" }, /* 001.002.014 */ |
|---|
| 404 | + { 0x420d, "BCM4349B1" }, /* 002.002.013 */ |
|---|
| 405 | + { 0x420e, "BCM4349B1" }, /* 002.002.014 */ |
|---|
| 406 | + { 0x4217, "BCM4329B1" }, /* 002.002.023 */ |
|---|
| 407 | + { 0x6106, "BCM4359C0" }, /* 003.001.006 */ |
|---|
| 408 | + { 0x4106, "BCM4335A0" }, /* 002.001.006 */ |
|---|
| 334 | 409 | { } |
|---|
| 335 | 410 | }; |
|---|
| 336 | 411 | |
|---|
| .. | .. |
|---|
| 341 | 416 | { 0x2118, "BCM20702A0" }, /* 001.001.024 */ |
|---|
| 342 | 417 | { 0x2126, "BCM4335A0" }, /* 001.001.038 */ |
|---|
| 343 | 418 | { 0x220e, "BCM20702A1" }, /* 001.002.014 */ |
|---|
| 344 | | - { 0x230f, "BCM4354A2" }, /* 001.003.015 */ |
|---|
| 419 | + { 0x230f, "BCM4356A2" }, /* 001.003.015 */ |
|---|
| 345 | 420 | { 0x4106, "BCM4335B0" }, /* 002.001.006 */ |
|---|
| 346 | 421 | { 0x410e, "BCM20702B0" }, /* 002.001.014 */ |
|---|
| 347 | 422 | { 0x6109, "BCM4335C0" }, /* 003.001.009 */ |
|---|
| 348 | 423 | { 0x610c, "BCM4354" }, /* 003.001.012 */ |
|---|
| 424 | + { 0x6607, "BCM4350C5" }, /* 003.006.007 */ |
|---|
| 349 | 425 | { } |
|---|
| 350 | 426 | }; |
|---|
| 351 | 427 | |
|---|
| 352 | | -int btbcm_initialize(struct hci_dev *hdev, char *fw_name, size_t len, |
|---|
| 353 | | - bool reinit) |
|---|
| 428 | +int btbcm_initialize(struct hci_dev *hdev, bool *fw_load_done) |
|---|
| 354 | 429 | { |
|---|
| 355 | 430 | u16 subver, rev, pid, vid; |
|---|
| 356 | | - const char *hw_name = "BCM"; |
|---|
| 357 | 431 | struct sk_buff *skb; |
|---|
| 358 | 432 | struct hci_rp_read_local_version *ver; |
|---|
| 359 | 433 | const struct bcm_subver_table *bcm_subver_table; |
|---|
| 434 | + const char *hw_name = NULL; |
|---|
| 435 | + char postfix[16] = ""; |
|---|
| 436 | + int fw_name_count = 0; |
|---|
| 437 | + bcm_fw_name *fw_name; |
|---|
| 438 | + const struct firmware *fw; |
|---|
| 360 | 439 | int i, err; |
|---|
| 361 | 440 | |
|---|
| 362 | 441 | /* Reset */ |
|---|
| .. | .. |
|---|
| 375 | 454 | kfree_skb(skb); |
|---|
| 376 | 455 | |
|---|
| 377 | 456 | /* Read controller information */ |
|---|
| 378 | | - if (!reinit) { |
|---|
| 457 | + if (!(*fw_load_done)) { |
|---|
| 379 | 458 | err = btbcm_read_info(hdev); |
|---|
| 380 | 459 | if (err) |
|---|
| 381 | 460 | return err; |
|---|
| 382 | 461 | } |
|---|
| 383 | | - |
|---|
| 384 | | - /* Upper nibble of rev should be between 0 and 3? */ |
|---|
| 385 | | - if (((rev & 0xf000) >> 12) > 3) |
|---|
| 386 | | - return 0; |
|---|
| 462 | + err = btbcm_print_local_name(hdev); |
|---|
| 463 | + if (err) |
|---|
| 464 | + return err; |
|---|
| 387 | 465 | |
|---|
| 388 | 466 | bcm_subver_table = (hdev->bus == HCI_USB) ? bcm_usb_subver_table : |
|---|
| 389 | 467 | bcm_uart_subver_table; |
|---|
| .. | .. |
|---|
| 395 | 473 | } |
|---|
| 396 | 474 | } |
|---|
| 397 | 475 | |
|---|
| 476 | + bt_dev_info(hdev, "%s (%3.3u.%3.3u.%3.3u) build %4.4u", |
|---|
| 477 | + hw_name ? hw_name : "BCM", (subver & 0xe000) >> 13, |
|---|
| 478 | + (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff); |
|---|
| 479 | + |
|---|
| 480 | + if (*fw_load_done) |
|---|
| 481 | + return 0; |
|---|
| 482 | + |
|---|
| 398 | 483 | if (hdev->bus == HCI_USB) { |
|---|
| 399 | 484 | /* Read USB Product Info */ |
|---|
| 400 | 485 | skb = btbcm_read_usb_product(hdev); |
|---|
| .. | .. |
|---|
| 405 | 490 | pid = get_unaligned_le16(skb->data + 3); |
|---|
| 406 | 491 | kfree_skb(skb); |
|---|
| 407 | 492 | |
|---|
| 408 | | - snprintf(fw_name, len, "brcm/%s-%4.4x-%4.4x.hcd", |
|---|
| 409 | | - hw_name, vid, pid); |
|---|
| 410 | | - } else { |
|---|
| 411 | | - snprintf(fw_name, len, "brcm/%s.hcd", hw_name); |
|---|
| 493 | + snprintf(postfix, sizeof(postfix), "-%4.4x-%4.4x", vid, pid); |
|---|
| 412 | 494 | } |
|---|
| 413 | 495 | |
|---|
| 414 | | - bt_dev_info(hdev, "%s (%3.3u.%3.3u.%3.3u) build %4.4u", |
|---|
| 415 | | - hw_name, (subver & 0xe000) >> 13, |
|---|
| 416 | | - (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff); |
|---|
| 496 | + fw_name = kmalloc(BCM_FW_NAME_COUNT_MAX * BCM_FW_NAME_LEN, GFP_KERNEL); |
|---|
| 497 | + if (!fw_name) |
|---|
| 498 | + return -ENOMEM; |
|---|
| 417 | 499 | |
|---|
| 500 | + if (hw_name) { |
|---|
| 501 | + snprintf(fw_name[fw_name_count], BCM_FW_NAME_LEN, |
|---|
| 502 | + "brcm/%s%s.hcd", hw_name, postfix); |
|---|
| 503 | + fw_name_count++; |
|---|
| 504 | + } |
|---|
| 505 | + |
|---|
| 506 | + snprintf(fw_name[fw_name_count], BCM_FW_NAME_LEN, |
|---|
| 507 | + "brcm/BCM%s.hcd", postfix); |
|---|
| 508 | + fw_name_count++; |
|---|
| 509 | + |
|---|
| 510 | + for (i = 0; i < fw_name_count; i++) { |
|---|
| 511 | + err = firmware_request_nowarn(&fw, fw_name[i], &hdev->dev); |
|---|
| 512 | + if (err == 0) { |
|---|
| 513 | + bt_dev_info(hdev, "%s '%s' Patch", |
|---|
| 514 | + hw_name ? hw_name : "BCM", fw_name[i]); |
|---|
| 515 | + *fw_load_done = true; |
|---|
| 516 | + break; |
|---|
| 517 | + } |
|---|
| 518 | + } |
|---|
| 519 | + |
|---|
| 520 | + if (*fw_load_done) { |
|---|
| 521 | + err = btbcm_patchram(hdev, fw); |
|---|
| 522 | + if (err) |
|---|
| 523 | + bt_dev_info(hdev, "BCM: Patch failed (%d)", err); |
|---|
| 524 | + |
|---|
| 525 | + release_firmware(fw); |
|---|
| 526 | + } else { |
|---|
| 527 | + bt_dev_err(hdev, "BCM: firmware Patch file not found, tried:"); |
|---|
| 528 | + for (i = 0; i < fw_name_count; i++) |
|---|
| 529 | + bt_dev_err(hdev, "BCM: '%s'", fw_name[i]); |
|---|
| 530 | + } |
|---|
| 531 | + |
|---|
| 532 | + kfree(fw_name); |
|---|
| 418 | 533 | return 0; |
|---|
| 419 | 534 | } |
|---|
| 420 | 535 | EXPORT_SYMBOL_GPL(btbcm_initialize); |
|---|
| 421 | 536 | |
|---|
| 422 | | -int btbcm_finalize(struct hci_dev *hdev) |
|---|
| 537 | +int btbcm_finalize(struct hci_dev *hdev, bool *fw_load_done) |
|---|
| 423 | 538 | { |
|---|
| 424 | | - char fw_name[64]; |
|---|
| 425 | 539 | int err; |
|---|
| 426 | 540 | |
|---|
| 427 | | - /* Re-initialize */ |
|---|
| 428 | | - err = btbcm_initialize(hdev, fw_name, sizeof(fw_name), true); |
|---|
| 429 | | - if (err) |
|---|
| 430 | | - return err; |
|---|
| 541 | + /* Re-initialize if necessary */ |
|---|
| 542 | + if (*fw_load_done) { |
|---|
| 543 | + err = btbcm_initialize(hdev, fw_load_done); |
|---|
| 544 | + if (err) |
|---|
| 545 | + return err; |
|---|
| 546 | + } |
|---|
| 431 | 547 | |
|---|
| 432 | 548 | btbcm_check_bdaddr(hdev); |
|---|
| 433 | 549 | |
|---|
| .. | .. |
|---|
| 439 | 555 | |
|---|
| 440 | 556 | int btbcm_setup_patchram(struct hci_dev *hdev) |
|---|
| 441 | 557 | { |
|---|
| 442 | | - char fw_name[64]; |
|---|
| 443 | | - const struct firmware *fw; |
|---|
| 444 | | - struct sk_buff *skb; |
|---|
| 558 | + bool fw_load_done = false; |
|---|
| 445 | 559 | int err; |
|---|
| 446 | 560 | |
|---|
| 447 | 561 | /* Initialize */ |
|---|
| 448 | | - err = btbcm_initialize(hdev, fw_name, sizeof(fw_name), false); |
|---|
| 562 | + err = btbcm_initialize(hdev, &fw_load_done); |
|---|
| 449 | 563 | if (err) |
|---|
| 450 | 564 | return err; |
|---|
| 451 | 565 | |
|---|
| 452 | | - err = request_firmware(&fw, fw_name, &hdev->dev); |
|---|
| 453 | | - if (err < 0) { |
|---|
| 454 | | - bt_dev_info(hdev, "BCM: Patch %s not found", fw_name); |
|---|
| 455 | | - goto done; |
|---|
| 456 | | - } |
|---|
| 457 | | - |
|---|
| 458 | | - btbcm_patchram(hdev, fw); |
|---|
| 459 | | - |
|---|
| 460 | | - release_firmware(fw); |
|---|
| 461 | | - |
|---|
| 462 | | - /* Re-initialize */ |
|---|
| 463 | | - err = btbcm_initialize(hdev, fw_name, sizeof(fw_name), true); |
|---|
| 464 | | - if (err) |
|---|
| 465 | | - return err; |
|---|
| 466 | | - |
|---|
| 467 | | - /* Read Local Name */ |
|---|
| 468 | | - skb = btbcm_read_local_name(hdev); |
|---|
| 469 | | - if (IS_ERR(skb)) |
|---|
| 470 | | - return PTR_ERR(skb); |
|---|
| 471 | | - |
|---|
| 472 | | - bt_dev_info(hdev, "%s", (char *)(skb->data + 1)); |
|---|
| 473 | | - kfree_skb(skb); |
|---|
| 474 | | - |
|---|
| 475 | | -done: |
|---|
| 476 | | - btbcm_check_bdaddr(hdev); |
|---|
| 477 | | - |
|---|
| 478 | | - set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); |
|---|
| 479 | | - |
|---|
| 480 | | - return 0; |
|---|
| 566 | + /* Re-initialize after loading Patch */ |
|---|
| 567 | + return btbcm_finalize(hdev, &fw_load_done); |
|---|
| 481 | 568 | } |
|---|
| 482 | 569 | EXPORT_SYMBOL_GPL(btbcm_setup_patchram); |
|---|
| 483 | 570 | |
|---|