162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Bluetooth support for Broadcom devices 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2015 Intel Corporation 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/efi.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/firmware.h> 1262306a36Sopenharmony_ci#include <linux/dmi.h> 1362306a36Sopenharmony_ci#include <linux/of.h> 1462306a36Sopenharmony_ci#include <asm/unaligned.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <net/bluetooth/bluetooth.h> 1762306a36Sopenharmony_ci#include <net/bluetooth/hci_core.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "btbcm.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define VERSION "0.1" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00}}) 2462306a36Sopenharmony_ci#define BDADDR_BCM20702A1 (&(bdaddr_t) {{0x00, 0x00, 0xa0, 0x02, 0x70, 0x20}}) 2562306a36Sopenharmony_ci#define BDADDR_BCM2076B1 (&(bdaddr_t) {{0x79, 0x56, 0x00, 0xa0, 0x76, 0x20}}) 2662306a36Sopenharmony_ci#define BDADDR_BCM43430A0 (&(bdaddr_t) {{0xac, 0x1f, 0x12, 0xa0, 0x43, 0x43}}) 2762306a36Sopenharmony_ci#define BDADDR_BCM43430A1 (&(bdaddr_t) {{0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa}}) 2862306a36Sopenharmony_ci#define BDADDR_BCM4324B3 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb3, 0x24, 0x43}}) 2962306a36Sopenharmony_ci#define BDADDR_BCM4330B1 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb1, 0x30, 0x43}}) 3062306a36Sopenharmony_ci#define BDADDR_BCM4334B0 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb0, 0x34, 0x43}}) 3162306a36Sopenharmony_ci#define BDADDR_BCM4345C5 (&(bdaddr_t) {{0xac, 0x1f, 0x00, 0xc5, 0x45, 0x43}}) 3262306a36Sopenharmony_ci#define BDADDR_BCM43341B (&(bdaddr_t) {{0xac, 0x1f, 0x00, 0x1b, 0x34, 0x43}}) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define BCM_FW_NAME_LEN 64 3562306a36Sopenharmony_ci#define BCM_FW_NAME_COUNT_MAX 4 3662306a36Sopenharmony_ci/* For kmalloc-ing the fw-name array instead of putting it on the stack */ 3762306a36Sopenharmony_citypedef char bcm_fw_name[BCM_FW_NAME_LEN]; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#ifdef CONFIG_EFI 4062306a36Sopenharmony_cistatic int btbcm_set_bdaddr_from_efi(struct hci_dev *hdev) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci efi_guid_t guid = EFI_GUID(0x74b00bd9, 0x805a, 0x4d61, 0xb5, 0x1f, 4362306a36Sopenharmony_ci 0x43, 0x26, 0x81, 0x23, 0xd1, 0x13); 4462306a36Sopenharmony_ci bdaddr_t efi_bdaddr, bdaddr; 4562306a36Sopenharmony_ci efi_status_t status; 4662306a36Sopenharmony_ci unsigned long len; 4762306a36Sopenharmony_ci int ret; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) 5062306a36Sopenharmony_ci return -EOPNOTSUPP; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci len = sizeof(efi_bdaddr); 5362306a36Sopenharmony_ci status = efi.get_variable(L"BDADDR", &guid, NULL, &len, &efi_bdaddr); 5462306a36Sopenharmony_ci if (status != EFI_SUCCESS) 5562306a36Sopenharmony_ci return -ENXIO; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (len != sizeof(efi_bdaddr)) 5862306a36Sopenharmony_ci return -EIO; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci baswap(&bdaddr, &efi_bdaddr); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci ret = btbcm_set_bdaddr(hdev, &bdaddr); 6362306a36Sopenharmony_ci if (ret) 6462306a36Sopenharmony_ci return ret; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci bt_dev_info(hdev, "BCM: Using EFI device address (%pMR)", &bdaddr); 6762306a36Sopenharmony_ci return 0; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci#else 7062306a36Sopenharmony_cistatic int btbcm_set_bdaddr_from_efi(struct hci_dev *hdev) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci return -EOPNOTSUPP; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci#endif 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ciint btbcm_check_bdaddr(struct hci_dev *hdev) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct hci_rp_read_bd_addr *bda; 7962306a36Sopenharmony_ci struct sk_buff *skb; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL, 8262306a36Sopenharmony_ci HCI_INIT_TIMEOUT); 8362306a36Sopenharmony_ci if (IS_ERR(skb)) { 8462306a36Sopenharmony_ci int err = PTR_ERR(skb); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci bt_dev_err(hdev, "BCM: Reading device address failed (%d)", err); 8762306a36Sopenharmony_ci return err; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (skb->len != sizeof(*bda)) { 9162306a36Sopenharmony_ci bt_dev_err(hdev, "BCM: Device address length mismatch"); 9262306a36Sopenharmony_ci kfree_skb(skb); 9362306a36Sopenharmony_ci return -EIO; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci bda = (struct hci_rp_read_bd_addr *)skb->data; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* Check if the address indicates a controller with either an 9962306a36Sopenharmony_ci * invalid or default address. In both cases the device needs 10062306a36Sopenharmony_ci * to be marked as not having a valid address. 10162306a36Sopenharmony_ci * 10262306a36Sopenharmony_ci * The address 00:20:70:02:A0:00 indicates a BCM20702A0 controller 10362306a36Sopenharmony_ci * with no configured address. 10462306a36Sopenharmony_ci * 10562306a36Sopenharmony_ci * The address 20:70:02:A0:00:00 indicates a BCM20702A1 controller 10662306a36Sopenharmony_ci * with no configured address. 10762306a36Sopenharmony_ci * 10862306a36Sopenharmony_ci * The address 20:76:A0:00:56:79 indicates a BCM2076B1 controller 10962306a36Sopenharmony_ci * with no configured address. 11062306a36Sopenharmony_ci * 11162306a36Sopenharmony_ci * The address 43:24:B3:00:00:00 indicates a BCM4324B3 controller 11262306a36Sopenharmony_ci * with waiting for configuration state. 11362306a36Sopenharmony_ci * 11462306a36Sopenharmony_ci * The address 43:30:B1:00:00:00 indicates a BCM4330B1 controller 11562306a36Sopenharmony_ci * with waiting for configuration state. 11662306a36Sopenharmony_ci * 11762306a36Sopenharmony_ci * The address 43:43:A0:12:1F:AC indicates a BCM43430A0 controller 11862306a36Sopenharmony_ci * with no configured address. 11962306a36Sopenharmony_ci * 12062306a36Sopenharmony_ci * The address AA:AA:AA:AA:AA:AA indicates a BCM43430A1 controller 12162306a36Sopenharmony_ci * with no configured address. 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_ci if (!bacmp(&bda->bdaddr, BDADDR_BCM20702A0) || 12462306a36Sopenharmony_ci !bacmp(&bda->bdaddr, BDADDR_BCM20702A1) || 12562306a36Sopenharmony_ci !bacmp(&bda->bdaddr, BDADDR_BCM2076B1) || 12662306a36Sopenharmony_ci !bacmp(&bda->bdaddr, BDADDR_BCM4324B3) || 12762306a36Sopenharmony_ci !bacmp(&bda->bdaddr, BDADDR_BCM4330B1) || 12862306a36Sopenharmony_ci !bacmp(&bda->bdaddr, BDADDR_BCM4334B0) || 12962306a36Sopenharmony_ci !bacmp(&bda->bdaddr, BDADDR_BCM4345C5) || 13062306a36Sopenharmony_ci !bacmp(&bda->bdaddr, BDADDR_BCM43430A0) || 13162306a36Sopenharmony_ci !bacmp(&bda->bdaddr, BDADDR_BCM43430A1) || 13262306a36Sopenharmony_ci !bacmp(&bda->bdaddr, BDADDR_BCM43341B)) { 13362306a36Sopenharmony_ci /* Try falling back to BDADDR EFI variable */ 13462306a36Sopenharmony_ci if (btbcm_set_bdaddr_from_efi(hdev) != 0) { 13562306a36Sopenharmony_ci bt_dev_info(hdev, "BCM: Using default device address (%pMR)", 13662306a36Sopenharmony_ci &bda->bdaddr); 13762306a36Sopenharmony_ci set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci kfree_skb(skb); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci return 0; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(btbcm_check_bdaddr); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ciint btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci struct sk_buff *skb; 15062306a36Sopenharmony_ci int err; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci skb = __hci_cmd_sync(hdev, 0xfc01, 6, bdaddr, HCI_INIT_TIMEOUT); 15362306a36Sopenharmony_ci if (IS_ERR(skb)) { 15462306a36Sopenharmony_ci err = PTR_ERR(skb); 15562306a36Sopenharmony_ci bt_dev_err(hdev, "BCM: Change address command failed (%d)", err); 15662306a36Sopenharmony_ci return err; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci kfree_skb(skb); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci return 0; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(btbcm_set_bdaddr); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ciint btbcm_read_pcm_int_params(struct hci_dev *hdev, 16562306a36Sopenharmony_ci struct bcm_set_pcm_int_params *params) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct sk_buff *skb; 16862306a36Sopenharmony_ci int err = 0; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci skb = __hci_cmd_sync(hdev, 0xfc1d, 0, NULL, HCI_INIT_TIMEOUT); 17162306a36Sopenharmony_ci if (IS_ERR(skb)) { 17262306a36Sopenharmony_ci err = PTR_ERR(skb); 17362306a36Sopenharmony_ci bt_dev_err(hdev, "BCM: Read PCM int params failed (%d)", err); 17462306a36Sopenharmony_ci return err; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (skb->len != 6 || skb->data[0]) { 17862306a36Sopenharmony_ci bt_dev_err(hdev, "BCM: Read PCM int params length mismatch"); 17962306a36Sopenharmony_ci kfree_skb(skb); 18062306a36Sopenharmony_ci return -EIO; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (params) 18462306a36Sopenharmony_ci memcpy(params, skb->data + 1, 5); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci kfree_skb(skb); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return 0; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(btbcm_read_pcm_int_params); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ciint btbcm_write_pcm_int_params(struct hci_dev *hdev, 19362306a36Sopenharmony_ci const struct bcm_set_pcm_int_params *params) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct sk_buff *skb; 19662306a36Sopenharmony_ci int err; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci skb = __hci_cmd_sync(hdev, 0xfc1c, 5, params, HCI_INIT_TIMEOUT); 19962306a36Sopenharmony_ci if (IS_ERR(skb)) { 20062306a36Sopenharmony_ci err = PTR_ERR(skb); 20162306a36Sopenharmony_ci bt_dev_err(hdev, "BCM: Write PCM int params failed (%d)", err); 20262306a36Sopenharmony_ci return err; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci kfree_skb(skb); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci return 0; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(btbcm_write_pcm_int_params); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ciint btbcm_patchram(struct hci_dev *hdev, const struct firmware *fw) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci const struct hci_command_hdr *cmd; 21362306a36Sopenharmony_ci const u8 *fw_ptr; 21462306a36Sopenharmony_ci size_t fw_size; 21562306a36Sopenharmony_ci struct sk_buff *skb; 21662306a36Sopenharmony_ci u16 opcode; 21762306a36Sopenharmony_ci int err = 0; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* Start Download */ 22062306a36Sopenharmony_ci skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT); 22162306a36Sopenharmony_ci if (IS_ERR(skb)) { 22262306a36Sopenharmony_ci err = PTR_ERR(skb); 22362306a36Sopenharmony_ci bt_dev_err(hdev, "BCM: Download Minidrv command failed (%d)", 22462306a36Sopenharmony_ci err); 22562306a36Sopenharmony_ci goto done; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci kfree_skb(skb); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* 50 msec delay after Download Minidrv completes */ 23062306a36Sopenharmony_ci msleep(50); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci fw_ptr = fw->data; 23362306a36Sopenharmony_ci fw_size = fw->size; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci while (fw_size >= sizeof(*cmd)) { 23662306a36Sopenharmony_ci const u8 *cmd_param; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci cmd = (struct hci_command_hdr *)fw_ptr; 23962306a36Sopenharmony_ci fw_ptr += sizeof(*cmd); 24062306a36Sopenharmony_ci fw_size -= sizeof(*cmd); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (fw_size < cmd->plen) { 24362306a36Sopenharmony_ci bt_dev_err(hdev, "BCM: Patch is corrupted"); 24462306a36Sopenharmony_ci err = -EINVAL; 24562306a36Sopenharmony_ci goto done; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci cmd_param = fw_ptr; 24962306a36Sopenharmony_ci fw_ptr += cmd->plen; 25062306a36Sopenharmony_ci fw_size -= cmd->plen; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci opcode = le16_to_cpu(cmd->opcode); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci skb = __hci_cmd_sync(hdev, opcode, cmd->plen, cmd_param, 25562306a36Sopenharmony_ci HCI_INIT_TIMEOUT); 25662306a36Sopenharmony_ci if (IS_ERR(skb)) { 25762306a36Sopenharmony_ci err = PTR_ERR(skb); 25862306a36Sopenharmony_ci bt_dev_err(hdev, "BCM: Patch command %04x failed (%d)", 25962306a36Sopenharmony_ci opcode, err); 26062306a36Sopenharmony_ci goto done; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci kfree_skb(skb); 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* 250 msec delay after Launch Ram completes */ 26662306a36Sopenharmony_ci msleep(250); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cidone: 26962306a36Sopenharmony_ci return err; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ciEXPORT_SYMBOL(btbcm_patchram); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic int btbcm_reset(struct hci_dev *hdev) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci struct sk_buff *skb; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); 27862306a36Sopenharmony_ci if (IS_ERR(skb)) { 27962306a36Sopenharmony_ci int err = PTR_ERR(skb); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci bt_dev_err(hdev, "BCM: Reset failed (%d)", err); 28262306a36Sopenharmony_ci return err; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci kfree_skb(skb); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* 100 msec delay for module to complete reset process */ 28762306a36Sopenharmony_ci msleep(100); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci return 0; 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic struct sk_buff *btbcm_read_local_name(struct hci_dev *hdev) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci struct sk_buff *skb; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL, 29762306a36Sopenharmony_ci HCI_INIT_TIMEOUT); 29862306a36Sopenharmony_ci if (IS_ERR(skb)) { 29962306a36Sopenharmony_ci bt_dev_err(hdev, "BCM: Reading local name failed (%ld)", 30062306a36Sopenharmony_ci PTR_ERR(skb)); 30162306a36Sopenharmony_ci return skb; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (skb->len != sizeof(struct hci_rp_read_local_name)) { 30562306a36Sopenharmony_ci bt_dev_err(hdev, "BCM: Local name length mismatch"); 30662306a36Sopenharmony_ci kfree_skb(skb); 30762306a36Sopenharmony_ci return ERR_PTR(-EIO); 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci return skb; 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic struct sk_buff *btbcm_read_local_version(struct hci_dev *hdev) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci struct sk_buff *skb; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL, 31862306a36Sopenharmony_ci HCI_INIT_TIMEOUT); 31962306a36Sopenharmony_ci if (IS_ERR(skb)) { 32062306a36Sopenharmony_ci bt_dev_err(hdev, "BCM: Reading local version info failed (%ld)", 32162306a36Sopenharmony_ci PTR_ERR(skb)); 32262306a36Sopenharmony_ci return skb; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (skb->len != sizeof(struct hci_rp_read_local_version)) { 32662306a36Sopenharmony_ci bt_dev_err(hdev, "BCM: Local version length mismatch"); 32762306a36Sopenharmony_ci kfree_skb(skb); 32862306a36Sopenharmony_ci return ERR_PTR(-EIO); 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci return skb; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic struct sk_buff *btbcm_read_verbose_config(struct hci_dev *hdev) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct sk_buff *skb; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci skb = __hci_cmd_sync(hdev, 0xfc79, 0, NULL, HCI_INIT_TIMEOUT); 33962306a36Sopenharmony_ci if (IS_ERR(skb)) { 34062306a36Sopenharmony_ci bt_dev_err(hdev, "BCM: Read verbose config info failed (%ld)", 34162306a36Sopenharmony_ci PTR_ERR(skb)); 34262306a36Sopenharmony_ci return skb; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (skb->len != 7) { 34662306a36Sopenharmony_ci bt_dev_err(hdev, "BCM: Verbose config length mismatch"); 34762306a36Sopenharmony_ci kfree_skb(skb); 34862306a36Sopenharmony_ci return ERR_PTR(-EIO); 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci return skb; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic struct sk_buff *btbcm_read_controller_features(struct hci_dev *hdev) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct sk_buff *skb; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci skb = __hci_cmd_sync(hdev, 0xfc6e, 0, NULL, HCI_INIT_TIMEOUT); 35962306a36Sopenharmony_ci if (IS_ERR(skb)) { 36062306a36Sopenharmony_ci bt_dev_err(hdev, "BCM: Read controller features failed (%ld)", 36162306a36Sopenharmony_ci PTR_ERR(skb)); 36262306a36Sopenharmony_ci return skb; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (skb->len != 9) { 36662306a36Sopenharmony_ci bt_dev_err(hdev, "BCM: Controller features length mismatch"); 36762306a36Sopenharmony_ci kfree_skb(skb); 36862306a36Sopenharmony_ci return ERR_PTR(-EIO); 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci return skb; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic struct sk_buff *btbcm_read_usb_product(struct hci_dev *hdev) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct sk_buff *skb; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci skb = __hci_cmd_sync(hdev, 0xfc5a, 0, NULL, HCI_INIT_TIMEOUT); 37962306a36Sopenharmony_ci if (IS_ERR(skb)) { 38062306a36Sopenharmony_ci bt_dev_err(hdev, "BCM: Read USB product info failed (%ld)", 38162306a36Sopenharmony_ci PTR_ERR(skb)); 38262306a36Sopenharmony_ci return skb; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (skb->len != 5) { 38662306a36Sopenharmony_ci bt_dev_err(hdev, "BCM: USB product length mismatch"); 38762306a36Sopenharmony_ci kfree_skb(skb); 38862306a36Sopenharmony_ci return ERR_PTR(-EIO); 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci return skb; 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic const struct dmi_system_id disable_broken_read_transmit_power[] = { 39562306a36Sopenharmony_ci { 39662306a36Sopenharmony_ci .matches = { 39762306a36Sopenharmony_ci DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."), 39862306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro16,1"), 39962306a36Sopenharmony_ci }, 40062306a36Sopenharmony_ci }, 40162306a36Sopenharmony_ci { 40262306a36Sopenharmony_ci .matches = { 40362306a36Sopenharmony_ci DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."), 40462306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro16,2"), 40562306a36Sopenharmony_ci }, 40662306a36Sopenharmony_ci }, 40762306a36Sopenharmony_ci { 40862306a36Sopenharmony_ci .matches = { 40962306a36Sopenharmony_ci DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."), 41062306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro16,4"), 41162306a36Sopenharmony_ci }, 41262306a36Sopenharmony_ci }, 41362306a36Sopenharmony_ci { 41462306a36Sopenharmony_ci .matches = { 41562306a36Sopenharmony_ci DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."), 41662306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir8,1"), 41762306a36Sopenharmony_ci }, 41862306a36Sopenharmony_ci }, 41962306a36Sopenharmony_ci { 42062306a36Sopenharmony_ci .matches = { 42162306a36Sopenharmony_ci DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."), 42262306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir8,2"), 42362306a36Sopenharmony_ci }, 42462306a36Sopenharmony_ci }, 42562306a36Sopenharmony_ci { 42662306a36Sopenharmony_ci .matches = { 42762306a36Sopenharmony_ci DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."), 42862306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "iMac20,1"), 42962306a36Sopenharmony_ci }, 43062306a36Sopenharmony_ci }, 43162306a36Sopenharmony_ci { 43262306a36Sopenharmony_ci .matches = { 43362306a36Sopenharmony_ci DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."), 43462306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "iMac20,2"), 43562306a36Sopenharmony_ci }, 43662306a36Sopenharmony_ci }, 43762306a36Sopenharmony_ci { } 43862306a36Sopenharmony_ci}; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic int btbcm_read_info(struct hci_dev *hdev) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci struct sk_buff *skb; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci /* Read Verbose Config Version Info */ 44562306a36Sopenharmony_ci skb = btbcm_read_verbose_config(hdev); 44662306a36Sopenharmony_ci if (IS_ERR(skb)) 44762306a36Sopenharmony_ci return PTR_ERR(skb); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci bt_dev_info(hdev, "BCM: chip id %u", skb->data[1]); 45062306a36Sopenharmony_ci kfree_skb(skb); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci return 0; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic int btbcm_print_controller_features(struct hci_dev *hdev) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct sk_buff *skb; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci /* Read Controller Features */ 46062306a36Sopenharmony_ci skb = btbcm_read_controller_features(hdev); 46162306a36Sopenharmony_ci if (IS_ERR(skb)) 46262306a36Sopenharmony_ci return PTR_ERR(skb); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci bt_dev_info(hdev, "BCM: features 0x%2.2x", skb->data[1]); 46562306a36Sopenharmony_ci kfree_skb(skb); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* Read DMI and disable broken Read LE Min/Max Tx Power */ 46862306a36Sopenharmony_ci if (dmi_first_match(disable_broken_read_transmit_power)) 46962306a36Sopenharmony_ci set_bit(HCI_QUIRK_BROKEN_READ_TRANSMIT_POWER, &hdev->quirks); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci return 0; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic int btbcm_print_local_name(struct hci_dev *hdev) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci struct sk_buff *skb; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci /* Read Local Name */ 47962306a36Sopenharmony_ci skb = btbcm_read_local_name(hdev); 48062306a36Sopenharmony_ci if (IS_ERR(skb)) 48162306a36Sopenharmony_ci return PTR_ERR(skb); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci bt_dev_info(hdev, "%s", (char *)(skb->data + 1)); 48462306a36Sopenharmony_ci kfree_skb(skb); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci return 0; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistruct bcm_subver_table { 49062306a36Sopenharmony_ci u16 subver; 49162306a36Sopenharmony_ci const char *name; 49262306a36Sopenharmony_ci}; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic const struct bcm_subver_table bcm_uart_subver_table[] = { 49562306a36Sopenharmony_ci { 0x1111, "BCM4362A2" }, /* 000.017.017 */ 49662306a36Sopenharmony_ci { 0x4103, "BCM4330B1" }, /* 002.001.003 */ 49762306a36Sopenharmony_ci { 0x410d, "BCM4334B0" }, /* 002.001.013 */ 49862306a36Sopenharmony_ci { 0x410e, "BCM43341B0" }, /* 002.001.014 */ 49962306a36Sopenharmony_ci { 0x4204, "BCM2076B1" }, /* 002.002.004 */ 50062306a36Sopenharmony_ci { 0x4406, "BCM4324B3" }, /* 002.004.006 */ 50162306a36Sopenharmony_ci { 0x4606, "BCM4324B5" }, /* 002.006.006 */ 50262306a36Sopenharmony_ci { 0x6109, "BCM4335C0" }, /* 003.001.009 */ 50362306a36Sopenharmony_ci { 0x610c, "BCM4354" }, /* 003.001.012 */ 50462306a36Sopenharmony_ci { 0x2122, "BCM4343A0" }, /* 001.001.034 */ 50562306a36Sopenharmony_ci { 0x2209, "BCM43430A1" }, /* 001.002.009 */ 50662306a36Sopenharmony_ci { 0x6119, "BCM4345C0" }, /* 003.001.025 */ 50762306a36Sopenharmony_ci { 0x6606, "BCM4345C5" }, /* 003.006.006 */ 50862306a36Sopenharmony_ci { 0x230f, "BCM4356A2" }, /* 001.003.015 */ 50962306a36Sopenharmony_ci { 0x220e, "BCM20702A1" }, /* 001.002.014 */ 51062306a36Sopenharmony_ci { 0x420d, "BCM4349B1" }, /* 002.002.013 */ 51162306a36Sopenharmony_ci { 0x420e, "BCM4349B1" }, /* 002.002.014 */ 51262306a36Sopenharmony_ci { 0x4217, "BCM4329B1" }, /* 002.002.023 */ 51362306a36Sopenharmony_ci { 0x6106, "BCM4359C0" }, /* 003.001.006 */ 51462306a36Sopenharmony_ci { 0x4106, "BCM4335A0" }, /* 002.001.006 */ 51562306a36Sopenharmony_ci { 0x410c, "BCM43430B0" }, /* 002.001.012 */ 51662306a36Sopenharmony_ci { 0x2119, "BCM4373A0" }, /* 001.001.025 */ 51762306a36Sopenharmony_ci { } 51862306a36Sopenharmony_ci}; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_cistatic const struct bcm_subver_table bcm_usb_subver_table[] = { 52162306a36Sopenharmony_ci { 0x2105, "BCM20703A1" }, /* 001.001.005 */ 52262306a36Sopenharmony_ci { 0x210b, "BCM43142A0" }, /* 001.001.011 */ 52362306a36Sopenharmony_ci { 0x2112, "BCM4314A0" }, /* 001.001.018 */ 52462306a36Sopenharmony_ci { 0x2118, "BCM20702A0" }, /* 001.001.024 */ 52562306a36Sopenharmony_ci { 0x2126, "BCM4335A0" }, /* 001.001.038 */ 52662306a36Sopenharmony_ci { 0x220e, "BCM20702A1" }, /* 001.002.014 */ 52762306a36Sopenharmony_ci { 0x230f, "BCM4356A2" }, /* 001.003.015 */ 52862306a36Sopenharmony_ci { 0x4106, "BCM4335B0" }, /* 002.001.006 */ 52962306a36Sopenharmony_ci { 0x410e, "BCM20702B0" }, /* 002.001.014 */ 53062306a36Sopenharmony_ci { 0x6109, "BCM4335C0" }, /* 003.001.009 */ 53162306a36Sopenharmony_ci { 0x610c, "BCM4354" }, /* 003.001.012 */ 53262306a36Sopenharmony_ci { 0x6607, "BCM4350C5" }, /* 003.006.007 */ 53362306a36Sopenharmony_ci { } 53462306a36Sopenharmony_ci}; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci/* 53762306a36Sopenharmony_ci * This currently only looks up the device tree board appendix, 53862306a36Sopenharmony_ci * but can be expanded to other mechanisms. 53962306a36Sopenharmony_ci */ 54062306a36Sopenharmony_cistatic const char *btbcm_get_board_name(struct device *dev) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci#ifdef CONFIG_OF 54362306a36Sopenharmony_ci struct device_node *root; 54462306a36Sopenharmony_ci char *board_type; 54562306a36Sopenharmony_ci const char *tmp; 54662306a36Sopenharmony_ci int len; 54762306a36Sopenharmony_ci int i; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci root = of_find_node_by_path("/"); 55062306a36Sopenharmony_ci if (!root) 55162306a36Sopenharmony_ci return NULL; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (of_property_read_string_index(root, "compatible", 0, &tmp)) 55462306a36Sopenharmony_ci return NULL; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci /* get rid of any '/' in the compatible string */ 55762306a36Sopenharmony_ci len = strlen(tmp) + 1; 55862306a36Sopenharmony_ci board_type = devm_kzalloc(dev, len, GFP_KERNEL); 55962306a36Sopenharmony_ci strscpy(board_type, tmp, len); 56062306a36Sopenharmony_ci for (i = 0; i < len; i++) { 56162306a36Sopenharmony_ci if (board_type[i] == '/') 56262306a36Sopenharmony_ci board_type[i] = '-'; 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci of_node_put(root); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci return board_type; 56762306a36Sopenharmony_ci#else 56862306a36Sopenharmony_ci return NULL; 56962306a36Sopenharmony_ci#endif 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ciint btbcm_initialize(struct hci_dev *hdev, bool *fw_load_done, bool use_autobaud_mode) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci u16 subver, rev, pid, vid; 57562306a36Sopenharmony_ci struct sk_buff *skb; 57662306a36Sopenharmony_ci struct hci_rp_read_local_version *ver; 57762306a36Sopenharmony_ci const struct bcm_subver_table *bcm_subver_table; 57862306a36Sopenharmony_ci const char *hw_name = NULL; 57962306a36Sopenharmony_ci const char *board_name; 58062306a36Sopenharmony_ci char postfix[16] = ""; 58162306a36Sopenharmony_ci int fw_name_count = 0; 58262306a36Sopenharmony_ci bcm_fw_name *fw_name; 58362306a36Sopenharmony_ci const struct firmware *fw; 58462306a36Sopenharmony_ci int i, err; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci board_name = btbcm_get_board_name(&hdev->dev); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci /* Reset */ 58962306a36Sopenharmony_ci err = btbcm_reset(hdev); 59062306a36Sopenharmony_ci if (err) 59162306a36Sopenharmony_ci return err; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci /* Read Local Version Info */ 59462306a36Sopenharmony_ci skb = btbcm_read_local_version(hdev); 59562306a36Sopenharmony_ci if (IS_ERR(skb)) 59662306a36Sopenharmony_ci return PTR_ERR(skb); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci ver = (struct hci_rp_read_local_version *)skb->data; 59962306a36Sopenharmony_ci rev = le16_to_cpu(ver->hci_rev); 60062306a36Sopenharmony_ci subver = le16_to_cpu(ver->lmp_subver); 60162306a36Sopenharmony_ci kfree_skb(skb); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci /* Read controller information */ 60462306a36Sopenharmony_ci if (!(*fw_load_done)) { 60562306a36Sopenharmony_ci err = btbcm_read_info(hdev); 60662306a36Sopenharmony_ci if (err) 60762306a36Sopenharmony_ci return err; 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci if (!use_autobaud_mode) { 61162306a36Sopenharmony_ci err = btbcm_print_controller_features(hdev); 61262306a36Sopenharmony_ci if (err) 61362306a36Sopenharmony_ci return err; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci err = btbcm_print_local_name(hdev); 61662306a36Sopenharmony_ci if (err) 61762306a36Sopenharmony_ci return err; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci bcm_subver_table = (hdev->bus == HCI_USB) ? bcm_usb_subver_table : 62162306a36Sopenharmony_ci bcm_uart_subver_table; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci for (i = 0; bcm_subver_table[i].name; i++) { 62462306a36Sopenharmony_ci if (subver == bcm_subver_table[i].subver) { 62562306a36Sopenharmony_ci hw_name = bcm_subver_table[i].name; 62662306a36Sopenharmony_ci break; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci bt_dev_info(hdev, "%s (%3.3u.%3.3u.%3.3u) build %4.4u", 63162306a36Sopenharmony_ci hw_name ? hw_name : "BCM", (subver & 0xe000) >> 13, 63262306a36Sopenharmony_ci (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci if (*fw_load_done) 63562306a36Sopenharmony_ci return 0; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci if (hdev->bus == HCI_USB) { 63862306a36Sopenharmony_ci /* Read USB Product Info */ 63962306a36Sopenharmony_ci skb = btbcm_read_usb_product(hdev); 64062306a36Sopenharmony_ci if (IS_ERR(skb)) 64162306a36Sopenharmony_ci return PTR_ERR(skb); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci vid = get_unaligned_le16(skb->data + 1); 64462306a36Sopenharmony_ci pid = get_unaligned_le16(skb->data + 3); 64562306a36Sopenharmony_ci kfree_skb(skb); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci snprintf(postfix, sizeof(postfix), "-%4.4x-%4.4x", vid, pid); 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci fw_name = kmalloc(BCM_FW_NAME_COUNT_MAX * BCM_FW_NAME_LEN, GFP_KERNEL); 65162306a36Sopenharmony_ci if (!fw_name) 65262306a36Sopenharmony_ci return -ENOMEM; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (hw_name) { 65562306a36Sopenharmony_ci if (board_name) { 65662306a36Sopenharmony_ci snprintf(fw_name[fw_name_count], BCM_FW_NAME_LEN, 65762306a36Sopenharmony_ci "brcm/%s%s.%s.hcd", hw_name, postfix, board_name); 65862306a36Sopenharmony_ci fw_name_count++; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci snprintf(fw_name[fw_name_count], BCM_FW_NAME_LEN, 66162306a36Sopenharmony_ci "brcm/%s%s.hcd", hw_name, postfix); 66262306a36Sopenharmony_ci fw_name_count++; 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci if (board_name) { 66662306a36Sopenharmony_ci snprintf(fw_name[fw_name_count], BCM_FW_NAME_LEN, 66762306a36Sopenharmony_ci "brcm/BCM%s.%s.hcd", postfix, board_name); 66862306a36Sopenharmony_ci fw_name_count++; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci snprintf(fw_name[fw_name_count], BCM_FW_NAME_LEN, 67162306a36Sopenharmony_ci "brcm/BCM%s.hcd", postfix); 67262306a36Sopenharmony_ci fw_name_count++; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci for (i = 0; i < fw_name_count; i++) { 67562306a36Sopenharmony_ci err = firmware_request_nowarn(&fw, fw_name[i], &hdev->dev); 67662306a36Sopenharmony_ci if (err == 0) { 67762306a36Sopenharmony_ci bt_dev_info(hdev, "%s '%s' Patch", 67862306a36Sopenharmony_ci hw_name ? hw_name : "BCM", fw_name[i]); 67962306a36Sopenharmony_ci *fw_load_done = true; 68062306a36Sopenharmony_ci break; 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci if (*fw_load_done) { 68562306a36Sopenharmony_ci err = btbcm_patchram(hdev, fw); 68662306a36Sopenharmony_ci if (err) 68762306a36Sopenharmony_ci bt_dev_info(hdev, "BCM: Patch failed (%d)", err); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci release_firmware(fw); 69062306a36Sopenharmony_ci } else { 69162306a36Sopenharmony_ci bt_dev_err(hdev, "BCM: firmware Patch file not found, tried:"); 69262306a36Sopenharmony_ci for (i = 0; i < fw_name_count; i++) 69362306a36Sopenharmony_ci bt_dev_err(hdev, "BCM: '%s'", fw_name[i]); 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci kfree(fw_name); 69762306a36Sopenharmony_ci return 0; 69862306a36Sopenharmony_ci} 69962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(btbcm_initialize); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ciint btbcm_finalize(struct hci_dev *hdev, bool *fw_load_done, bool use_autobaud_mode) 70262306a36Sopenharmony_ci{ 70362306a36Sopenharmony_ci int err; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci /* Re-initialize if necessary */ 70662306a36Sopenharmony_ci if (*fw_load_done) { 70762306a36Sopenharmony_ci err = btbcm_initialize(hdev, fw_load_done, use_autobaud_mode); 70862306a36Sopenharmony_ci if (err) 70962306a36Sopenharmony_ci return err; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci btbcm_check_bdaddr(hdev); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci return 0; 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(btbcm_finalize); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ciint btbcm_setup_patchram(struct hci_dev *hdev) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci bool fw_load_done = false; 72362306a36Sopenharmony_ci bool use_autobaud_mode = false; 72462306a36Sopenharmony_ci int err; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci /* Initialize */ 72762306a36Sopenharmony_ci err = btbcm_initialize(hdev, &fw_load_done, use_autobaud_mode); 72862306a36Sopenharmony_ci if (err) 72962306a36Sopenharmony_ci return err; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci /* Re-initialize after loading Patch */ 73262306a36Sopenharmony_ci return btbcm_finalize(hdev, &fw_load_done, use_autobaud_mode); 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(btbcm_setup_patchram); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ciint btbcm_setup_apple(struct hci_dev *hdev) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci struct sk_buff *skb; 73962306a36Sopenharmony_ci int err; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci /* Reset */ 74262306a36Sopenharmony_ci err = btbcm_reset(hdev); 74362306a36Sopenharmony_ci if (err) 74462306a36Sopenharmony_ci return err; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci /* Read Verbose Config Version Info */ 74762306a36Sopenharmony_ci skb = btbcm_read_verbose_config(hdev); 74862306a36Sopenharmony_ci if (!IS_ERR(skb)) { 74962306a36Sopenharmony_ci bt_dev_info(hdev, "BCM: chip id %u build %4.4u", 75062306a36Sopenharmony_ci skb->data[1], get_unaligned_le16(skb->data + 5)); 75162306a36Sopenharmony_ci kfree_skb(skb); 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci /* Read USB Product Info */ 75562306a36Sopenharmony_ci skb = btbcm_read_usb_product(hdev); 75662306a36Sopenharmony_ci if (!IS_ERR(skb)) { 75762306a36Sopenharmony_ci bt_dev_info(hdev, "BCM: product %4.4x:%4.4x", 75862306a36Sopenharmony_ci get_unaligned_le16(skb->data + 1), 75962306a36Sopenharmony_ci get_unaligned_le16(skb->data + 3)); 76062306a36Sopenharmony_ci kfree_skb(skb); 76162306a36Sopenharmony_ci } 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci /* Read Controller Features */ 76462306a36Sopenharmony_ci skb = btbcm_read_controller_features(hdev); 76562306a36Sopenharmony_ci if (!IS_ERR(skb)) { 76662306a36Sopenharmony_ci bt_dev_info(hdev, "BCM: features 0x%2.2x", skb->data[1]); 76762306a36Sopenharmony_ci kfree_skb(skb); 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci /* Read Local Name */ 77162306a36Sopenharmony_ci skb = btbcm_read_local_name(hdev); 77262306a36Sopenharmony_ci if (!IS_ERR(skb)) { 77362306a36Sopenharmony_ci bt_dev_info(hdev, "%s", (char *)(skb->data + 1)); 77462306a36Sopenharmony_ci kfree_skb(skb); 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci return 0; 78062306a36Sopenharmony_ci} 78162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(btbcm_setup_apple); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ciMODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); 78462306a36Sopenharmony_ciMODULE_DESCRIPTION("Bluetooth support for Broadcom devices ver " VERSION); 78562306a36Sopenharmony_ciMODULE_VERSION(VERSION); 78662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 787