xref: /openbmc/linux/drivers/bluetooth/btbcm.c (revision 47e90f6b)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
24fba30f0SMarcel Holtmann /*
34fba30f0SMarcel Holtmann  *
44fba30f0SMarcel Holtmann  *  Bluetooth support for Broadcom devices
54fba30f0SMarcel Holtmann  *
64fba30f0SMarcel Holtmann  *  Copyright (C) 2015  Intel Corporation
74fba30f0SMarcel Holtmann  */
84fba30f0SMarcel Holtmann 
90d218c36SHans de Goede #include <linux/efi.h>
104fba30f0SMarcel Holtmann #include <linux/module.h>
111c8ba6d0SMarcel Holtmann #include <linux/firmware.h>
12801b4c02SAditya Garg #include <linux/dmi.h>
1363fac334SLinus Walleij #include <linux/of.h>
141c8ba6d0SMarcel Holtmann #include <asm/unaligned.h>
154fba30f0SMarcel Holtmann 
164fba30f0SMarcel Holtmann #include <net/bluetooth/bluetooth.h>
174fba30f0SMarcel Holtmann #include <net/bluetooth/hci_core.h>
184fba30f0SMarcel Holtmann 
194fba30f0SMarcel Holtmann #include "btbcm.h"
204fba30f0SMarcel Holtmann 
214fba30f0SMarcel Holtmann #define VERSION "0.1"
224fba30f0SMarcel Holtmann 
234fba30f0SMarcel Holtmann #define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00}})
2492ffe0dbSMaxime Ripard #define BDADDR_BCM20702A1 (&(bdaddr_t) {{0x00, 0x00, 0xa0, 0x02, 0x70, 0x20}})
25300926b1SStephan Gerhold #define BDADDR_BCM2076B1 (&(bdaddr_t) {{0x79, 0x56, 0x00, 0xa0, 0x76, 0x20}})
260697607aSChen-Yu Tsai #define BDADDR_BCM43430A0 (&(bdaddr_t) {{0xac, 0x1f, 0x12, 0xa0, 0x43, 0x43}})
27*47e90f6bSMans Rullgard #define BDADDR_BCM43430A1 (&(bdaddr_t) {{0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa}})
28a8f3b941SFrederic Danis #define BDADDR_BCM4324B3 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb3, 0x24, 0x43}})
294a546ec3SFrederic Danis #define BDADDR_BCM4330B1 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb1, 0x30, 0x43}})
30b8dc6476SStephan Gerhold #define BDADDR_BCM4334B0 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb0, 0x34, 0x43}})
3152c8c7a7SOndrej Jirman #define BDADDR_BCM4345C5 (&(bdaddr_t) {{0xac, 0x1f, 0x00, 0xc5, 0x45, 0x43}})
3250357261SFerry Toth #define BDADDR_BCM43341B (&(bdaddr_t) {{0xac, 0x1f, 0x00, 0x1b, 0x34, 0x43}})
334fba30f0SMarcel Holtmann 
340287c5d8SHans de Goede #define BCM_FW_NAME_LEN			64
3563fac334SLinus Walleij #define BCM_FW_NAME_COUNT_MAX		4
3674530a63SHans de Goede /* For kmalloc-ing the fw-name array instead of putting it on the stack */
3774530a63SHans de Goede typedef char bcm_fw_name[BCM_FW_NAME_LEN];
380287c5d8SHans de Goede 
390d218c36SHans de Goede #ifdef CONFIG_EFI
btbcm_set_bdaddr_from_efi(struct hci_dev * hdev)400d218c36SHans de Goede static int btbcm_set_bdaddr_from_efi(struct hci_dev *hdev)
410d218c36SHans de Goede {
420d218c36SHans de Goede 	efi_guid_t guid = EFI_GUID(0x74b00bd9, 0x805a, 0x4d61, 0xb5, 0x1f,
430d218c36SHans de Goede 				   0x43, 0x26, 0x81, 0x23, 0xd1, 0x13);
440d218c36SHans de Goede 	bdaddr_t efi_bdaddr, bdaddr;
450d218c36SHans de Goede 	efi_status_t status;
460d218c36SHans de Goede 	unsigned long len;
470d218c36SHans de Goede 	int ret;
480d218c36SHans de Goede 
490d218c36SHans de Goede 	if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
500d218c36SHans de Goede 		return -EOPNOTSUPP;
510d218c36SHans de Goede 
520d218c36SHans de Goede 	len = sizeof(efi_bdaddr);
530d218c36SHans de Goede 	status = efi.get_variable(L"BDADDR", &guid, NULL, &len, &efi_bdaddr);
540d218c36SHans de Goede 	if (status != EFI_SUCCESS)
550d218c36SHans de Goede 		return -ENXIO;
560d218c36SHans de Goede 
570d218c36SHans de Goede 	if (len != sizeof(efi_bdaddr))
580d218c36SHans de Goede 		return -EIO;
590d218c36SHans de Goede 
600d218c36SHans de Goede 	baswap(&bdaddr, &efi_bdaddr);
610d218c36SHans de Goede 
620d218c36SHans de Goede 	ret = btbcm_set_bdaddr(hdev, &bdaddr);
630d218c36SHans de Goede 	if (ret)
640d218c36SHans de Goede 		return ret;
650d218c36SHans de Goede 
660d218c36SHans de Goede 	bt_dev_info(hdev, "BCM: Using EFI device address (%pMR)", &bdaddr);
670d218c36SHans de Goede 	return 0;
680d218c36SHans de Goede }
690d218c36SHans de Goede #else
btbcm_set_bdaddr_from_efi(struct hci_dev * hdev)700d218c36SHans de Goede static int btbcm_set_bdaddr_from_efi(struct hci_dev *hdev)
710d218c36SHans de Goede {
720d218c36SHans de Goede 	return -EOPNOTSUPP;
730d218c36SHans de Goede }
740d218c36SHans de Goede #endif
750d218c36SHans de Goede 
btbcm_check_bdaddr(struct hci_dev * hdev)764fba30f0SMarcel Holtmann int btbcm_check_bdaddr(struct hci_dev *hdev)
774fba30f0SMarcel Holtmann {
784fba30f0SMarcel Holtmann 	struct hci_rp_read_bd_addr *bda;
794fba30f0SMarcel Holtmann 	struct sk_buff *skb;
804fba30f0SMarcel Holtmann 
814fba30f0SMarcel Holtmann 	skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL,
824fba30f0SMarcel Holtmann 			     HCI_INIT_TIMEOUT);
834fba30f0SMarcel Holtmann 	if (IS_ERR(skb)) {
844fba30f0SMarcel Holtmann 		int err = PTR_ERR(skb);
85dde8010bSChangqi Du 
862064ee33SMarcel Holtmann 		bt_dev_err(hdev, "BCM: Reading device address failed (%d)", err);
874fba30f0SMarcel Holtmann 		return err;
884fba30f0SMarcel Holtmann 	}
894fba30f0SMarcel Holtmann 
904fba30f0SMarcel Holtmann 	if (skb->len != sizeof(*bda)) {
912064ee33SMarcel Holtmann 		bt_dev_err(hdev, "BCM: Device address length mismatch");
924fba30f0SMarcel Holtmann 		kfree_skb(skb);
934fba30f0SMarcel Holtmann 		return -EIO;
944fba30f0SMarcel Holtmann 	}
954fba30f0SMarcel Holtmann 
964fba30f0SMarcel Holtmann 	bda = (struct hci_rp_read_bd_addr *)skb->data;
974fba30f0SMarcel Holtmann 
98a8f3b941SFrederic Danis 	/* Check if the address indicates a controller with either an
99a8f3b941SFrederic Danis 	 * invalid or default address. In both cases the device needs
100a8f3b941SFrederic Danis 	 * to be marked as not having a valid address.
101a8f3b941SFrederic Danis 	 *
102a8f3b941SFrederic Danis 	 * The address 00:20:70:02:A0:00 indicates a BCM20702A0 controller
1034fba30f0SMarcel Holtmann 	 * with no configured address.
104a8f3b941SFrederic Danis 	 *
10592ffe0dbSMaxime Ripard 	 * The address 20:70:02:A0:00:00 indicates a BCM20702A1 controller
10692ffe0dbSMaxime Ripard 	 * with no configured address.
10792ffe0dbSMaxime Ripard 	 *
108300926b1SStephan Gerhold 	 * The address 20:76:A0:00:56:79 indicates a BCM2076B1 controller
109300926b1SStephan Gerhold 	 * with no configured address.
110300926b1SStephan Gerhold 	 *
111a8f3b941SFrederic Danis 	 * The address 43:24:B3:00:00:00 indicates a BCM4324B3 controller
112a8f3b941SFrederic Danis 	 * with waiting for configuration state.
1134a546ec3SFrederic Danis 	 *
1144a546ec3SFrederic Danis 	 * The address 43:30:B1:00:00:00 indicates a BCM4330B1 controller
1154a546ec3SFrederic Danis 	 * with waiting for configuration state.
1160697607aSChen-Yu Tsai 	 *
1170697607aSChen-Yu Tsai 	 * The address 43:43:A0:12:1F:AC indicates a BCM43430A0 controller
1180697607aSChen-Yu Tsai 	 * with no configured address.
119*47e90f6bSMans Rullgard 	 *
120*47e90f6bSMans Rullgard 	 * The address AA:AA:AA:AA:AA:AA indicates a BCM43430A1 controller
121*47e90f6bSMans Rullgard 	 * with no configured address.
1224fba30f0SMarcel Holtmann 	 */
123a8f3b941SFrederic Danis 	if (!bacmp(&bda->bdaddr, BDADDR_BCM20702A0) ||
12492ffe0dbSMaxime Ripard 	    !bacmp(&bda->bdaddr, BDADDR_BCM20702A1) ||
125300926b1SStephan Gerhold 	    !bacmp(&bda->bdaddr, BDADDR_BCM2076B1) ||
1264a546ec3SFrederic Danis 	    !bacmp(&bda->bdaddr, BDADDR_BCM4324B3) ||
1270697607aSChen-Yu Tsai 	    !bacmp(&bda->bdaddr, BDADDR_BCM4330B1) ||
128b8dc6476SStephan Gerhold 	    !bacmp(&bda->bdaddr, BDADDR_BCM4334B0) ||
12952c8c7a7SOndrej Jirman 	    !bacmp(&bda->bdaddr, BDADDR_BCM4345C5) ||
13050357261SFerry Toth 	    !bacmp(&bda->bdaddr, BDADDR_BCM43430A0) ||
131*47e90f6bSMans Rullgard 	    !bacmp(&bda->bdaddr, BDADDR_BCM43430A1) ||
13250357261SFerry Toth 	    !bacmp(&bda->bdaddr, BDADDR_BCM43341B)) {
1330d218c36SHans de Goede 		/* Try falling back to BDADDR EFI variable */
1340d218c36SHans de Goede 		if (btbcm_set_bdaddr_from_efi(hdev) != 0) {
1352064ee33SMarcel Holtmann 			bt_dev_info(hdev, "BCM: Using default device address (%pMR)",
1362064ee33SMarcel Holtmann 				    &bda->bdaddr);
1374fba30f0SMarcel Holtmann 			set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
1384fba30f0SMarcel Holtmann 		}
1390d218c36SHans de Goede 	}
1404fba30f0SMarcel Holtmann 
1414fba30f0SMarcel Holtmann 	kfree_skb(skb);
1424fba30f0SMarcel Holtmann 
1434fba30f0SMarcel Holtmann 	return 0;
1444fba30f0SMarcel Holtmann }
1454fba30f0SMarcel Holtmann EXPORT_SYMBOL_GPL(btbcm_check_bdaddr);
1464fba30f0SMarcel Holtmann 
btbcm_set_bdaddr(struct hci_dev * hdev,const bdaddr_t * bdaddr)1474fba30f0SMarcel Holtmann int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
1484fba30f0SMarcel Holtmann {
1494fba30f0SMarcel Holtmann 	struct sk_buff *skb;
1504fba30f0SMarcel Holtmann 	int err;
1514fba30f0SMarcel Holtmann 
1524fba30f0SMarcel Holtmann 	skb = __hci_cmd_sync(hdev, 0xfc01, 6, bdaddr, HCI_INIT_TIMEOUT);
1534fba30f0SMarcel Holtmann 	if (IS_ERR(skb)) {
1544fba30f0SMarcel Holtmann 		err = PTR_ERR(skb);
1552064ee33SMarcel Holtmann 		bt_dev_err(hdev, "BCM: Change address command failed (%d)", err);
1564fba30f0SMarcel Holtmann 		return err;
1574fba30f0SMarcel Holtmann 	}
1584fba30f0SMarcel Holtmann 	kfree_skb(skb);
1594fba30f0SMarcel Holtmann 
1604fba30f0SMarcel Holtmann 	return 0;
1614fba30f0SMarcel Holtmann }
1624fba30f0SMarcel Holtmann EXPORT_SYMBOL_GPL(btbcm_set_bdaddr);
1634fba30f0SMarcel Holtmann 
btbcm_read_pcm_int_params(struct hci_dev * hdev,struct bcm_set_pcm_int_params * params)16452837990SAbhishek Pandit-Subedi int btbcm_read_pcm_int_params(struct hci_dev *hdev,
16552837990SAbhishek Pandit-Subedi 			      struct bcm_set_pcm_int_params *params)
16652837990SAbhishek Pandit-Subedi {
16752837990SAbhishek Pandit-Subedi 	struct sk_buff *skb;
16852837990SAbhishek Pandit-Subedi 	int err = 0;
16952837990SAbhishek Pandit-Subedi 
17052837990SAbhishek Pandit-Subedi 	skb = __hci_cmd_sync(hdev, 0xfc1d, 0, NULL, HCI_INIT_TIMEOUT);
17152837990SAbhishek Pandit-Subedi 	if (IS_ERR(skb)) {
17252837990SAbhishek Pandit-Subedi 		err = PTR_ERR(skb);
17352837990SAbhishek Pandit-Subedi 		bt_dev_err(hdev, "BCM: Read PCM int params failed (%d)", err);
17452837990SAbhishek Pandit-Subedi 		return err;
17552837990SAbhishek Pandit-Subedi 	}
17652837990SAbhishek Pandit-Subedi 
17752837990SAbhishek Pandit-Subedi 	if (skb->len != 6 || skb->data[0]) {
17852837990SAbhishek Pandit-Subedi 		bt_dev_err(hdev, "BCM: Read PCM int params length mismatch");
17952837990SAbhishek Pandit-Subedi 		kfree_skb(skb);
18052837990SAbhishek Pandit-Subedi 		return -EIO;
18152837990SAbhishek Pandit-Subedi 	}
18252837990SAbhishek Pandit-Subedi 
18352837990SAbhishek Pandit-Subedi 	if (params)
18452837990SAbhishek Pandit-Subedi 		memcpy(params, skb->data + 1, 5);
18552837990SAbhishek Pandit-Subedi 
18652837990SAbhishek Pandit-Subedi 	kfree_skb(skb);
18752837990SAbhishek Pandit-Subedi 
18852837990SAbhishek Pandit-Subedi 	return 0;
18952837990SAbhishek Pandit-Subedi }
19052837990SAbhishek Pandit-Subedi EXPORT_SYMBOL_GPL(btbcm_read_pcm_int_params);
19152837990SAbhishek Pandit-Subedi 
btbcm_write_pcm_int_params(struct hci_dev * hdev,const struct bcm_set_pcm_int_params * params)19252837990SAbhishek Pandit-Subedi int btbcm_write_pcm_int_params(struct hci_dev *hdev,
19352837990SAbhishek Pandit-Subedi 			       const struct bcm_set_pcm_int_params *params)
19452837990SAbhishek Pandit-Subedi {
19552837990SAbhishek Pandit-Subedi 	struct sk_buff *skb;
19652837990SAbhishek Pandit-Subedi 	int err;
19752837990SAbhishek Pandit-Subedi 
19852837990SAbhishek Pandit-Subedi 	skb = __hci_cmd_sync(hdev, 0xfc1c, 5, params, HCI_INIT_TIMEOUT);
19952837990SAbhishek Pandit-Subedi 	if (IS_ERR(skb)) {
20052837990SAbhishek Pandit-Subedi 		err = PTR_ERR(skb);
20152837990SAbhishek Pandit-Subedi 		bt_dev_err(hdev, "BCM: Write PCM int params failed (%d)", err);
20252837990SAbhishek Pandit-Subedi 		return err;
20352837990SAbhishek Pandit-Subedi 	}
20452837990SAbhishek Pandit-Subedi 	kfree_skb(skb);
20552837990SAbhishek Pandit-Subedi 
20652837990SAbhishek Pandit-Subedi 	return 0;
20752837990SAbhishek Pandit-Subedi }
20852837990SAbhishek Pandit-Subedi EXPORT_SYMBOL_GPL(btbcm_write_pcm_int_params);
20952837990SAbhishek Pandit-Subedi 
btbcm_patchram(struct hci_dev * hdev,const struct firmware * fw)21018aeb444SFrederic Danis int btbcm_patchram(struct hci_dev *hdev, const struct firmware *fw)
21150862ee5SMarcel Holtmann {
21250862ee5SMarcel Holtmann 	const struct hci_command_hdr *cmd;
21350862ee5SMarcel Holtmann 	const u8 *fw_ptr;
21450862ee5SMarcel Holtmann 	size_t fw_size;
21550862ee5SMarcel Holtmann 	struct sk_buff *skb;
21650862ee5SMarcel Holtmann 	u16 opcode;
21718aeb444SFrederic Danis 	int err = 0;
21850862ee5SMarcel Holtmann 
21950862ee5SMarcel Holtmann 	/* Start Download */
22050862ee5SMarcel Holtmann 	skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT);
22150862ee5SMarcel Holtmann 	if (IS_ERR(skb)) {
22250862ee5SMarcel Holtmann 		err = PTR_ERR(skb);
2232064ee33SMarcel Holtmann 		bt_dev_err(hdev, "BCM: Download Minidrv command failed (%d)",
2242064ee33SMarcel Holtmann 			   err);
22550862ee5SMarcel Holtmann 		goto done;
22650862ee5SMarcel Holtmann 	}
22750862ee5SMarcel Holtmann 	kfree_skb(skb);
22850862ee5SMarcel Holtmann 
22950862ee5SMarcel Holtmann 	/* 50 msec delay after Download Minidrv completes */
23050862ee5SMarcel Holtmann 	msleep(50);
23150862ee5SMarcel Holtmann 
23250862ee5SMarcel Holtmann 	fw_ptr = fw->data;
23350862ee5SMarcel Holtmann 	fw_size = fw->size;
23450862ee5SMarcel Holtmann 
23550862ee5SMarcel Holtmann 	while (fw_size >= sizeof(*cmd)) {
23650862ee5SMarcel Holtmann 		const u8 *cmd_param;
23750862ee5SMarcel Holtmann 
23850862ee5SMarcel Holtmann 		cmd = (struct hci_command_hdr *)fw_ptr;
23950862ee5SMarcel Holtmann 		fw_ptr += sizeof(*cmd);
24050862ee5SMarcel Holtmann 		fw_size -= sizeof(*cmd);
24150862ee5SMarcel Holtmann 
24250862ee5SMarcel Holtmann 		if (fw_size < cmd->plen) {
2432064ee33SMarcel Holtmann 			bt_dev_err(hdev, "BCM: Patch is corrupted");
24450862ee5SMarcel Holtmann 			err = -EINVAL;
24550862ee5SMarcel Holtmann 			goto done;
24650862ee5SMarcel Holtmann 		}
24750862ee5SMarcel Holtmann 
24850862ee5SMarcel Holtmann 		cmd_param = fw_ptr;
24950862ee5SMarcel Holtmann 		fw_ptr += cmd->plen;
25050862ee5SMarcel Holtmann 		fw_size -= cmd->plen;
25150862ee5SMarcel Holtmann 
25250862ee5SMarcel Holtmann 		opcode = le16_to_cpu(cmd->opcode);
25350862ee5SMarcel Holtmann 
25450862ee5SMarcel Holtmann 		skb = __hci_cmd_sync(hdev, opcode, cmd->plen, cmd_param,
25550862ee5SMarcel Holtmann 				     HCI_INIT_TIMEOUT);
25650862ee5SMarcel Holtmann 		if (IS_ERR(skb)) {
25750862ee5SMarcel Holtmann 			err = PTR_ERR(skb);
2582064ee33SMarcel Holtmann 			bt_dev_err(hdev, "BCM: Patch command %04x failed (%d)",
2592064ee33SMarcel Holtmann 				   opcode, err);
26050862ee5SMarcel Holtmann 			goto done;
26150862ee5SMarcel Holtmann 		}
26250862ee5SMarcel Holtmann 		kfree_skb(skb);
26350862ee5SMarcel Holtmann 	}
26450862ee5SMarcel Holtmann 
26550862ee5SMarcel Holtmann 	/* 250 msec delay after Launch Ram completes */
26650862ee5SMarcel Holtmann 	msleep(250);
26750862ee5SMarcel Holtmann 
26850862ee5SMarcel Holtmann done:
26950862ee5SMarcel Holtmann 	return err;
27050862ee5SMarcel Holtmann }
27150862ee5SMarcel Holtmann EXPORT_SYMBOL(btbcm_patchram);
27250862ee5SMarcel Holtmann 
btbcm_reset(struct hci_dev * hdev)2731c8ba6d0SMarcel Holtmann static int btbcm_reset(struct hci_dev *hdev)
2741c8ba6d0SMarcel Holtmann {
2751c8ba6d0SMarcel Holtmann 	struct sk_buff *skb;
2761c8ba6d0SMarcel Holtmann 
2771c8ba6d0SMarcel Holtmann 	skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
2781c8ba6d0SMarcel Holtmann 	if (IS_ERR(skb)) {
2791c8ba6d0SMarcel Holtmann 		int err = PTR_ERR(skb);
280dde8010bSChangqi Du 
2812064ee33SMarcel Holtmann 		bt_dev_err(hdev, "BCM: Reset failed (%d)", err);
2821c8ba6d0SMarcel Holtmann 		return err;
2831c8ba6d0SMarcel Holtmann 	}
2841c8ba6d0SMarcel Holtmann 	kfree_skb(skb);
2851c8ba6d0SMarcel Holtmann 
2863af3a594SWen-chien Jesse Sung 	/* 100 msec delay for module to complete reset process */
2873af3a594SWen-chien Jesse Sung 	msleep(100);
2883af3a594SWen-chien Jesse Sung 
2891c8ba6d0SMarcel Holtmann 	return 0;
2901c8ba6d0SMarcel Holtmann }
2911c8ba6d0SMarcel Holtmann 
btbcm_read_local_name(struct hci_dev * hdev)2929bc63ca0SMarcel Holtmann static struct sk_buff *btbcm_read_local_name(struct hci_dev *hdev)
2939bc63ca0SMarcel Holtmann {
2949bc63ca0SMarcel Holtmann 	struct sk_buff *skb;
2959bc63ca0SMarcel Holtmann 
2969bc63ca0SMarcel Holtmann 	skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL,
2979bc63ca0SMarcel Holtmann 			     HCI_INIT_TIMEOUT);
2989bc63ca0SMarcel Holtmann 	if (IS_ERR(skb)) {
2992064ee33SMarcel Holtmann 		bt_dev_err(hdev, "BCM: Reading local name failed (%ld)",
3002064ee33SMarcel Holtmann 			   PTR_ERR(skb));
3019bc63ca0SMarcel Holtmann 		return skb;
3029bc63ca0SMarcel Holtmann 	}
3039bc63ca0SMarcel Holtmann 
3049bc63ca0SMarcel Holtmann 	if (skb->len != sizeof(struct hci_rp_read_local_name)) {
3052064ee33SMarcel Holtmann 		bt_dev_err(hdev, "BCM: Local name length mismatch");
3069bc63ca0SMarcel Holtmann 		kfree_skb(skb);
3079bc63ca0SMarcel Holtmann 		return ERR_PTR(-EIO);
3089bc63ca0SMarcel Holtmann 	}
3099bc63ca0SMarcel Holtmann 
3109bc63ca0SMarcel Holtmann 	return skb;
3119bc63ca0SMarcel Holtmann }
3129bc63ca0SMarcel Holtmann 
btbcm_read_local_version(struct hci_dev * hdev)3131c8ba6d0SMarcel Holtmann static struct sk_buff *btbcm_read_local_version(struct hci_dev *hdev)
3141c8ba6d0SMarcel Holtmann {
3151c8ba6d0SMarcel Holtmann 	struct sk_buff *skb;
3161c8ba6d0SMarcel Holtmann 
3171c8ba6d0SMarcel Holtmann 	skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
3181c8ba6d0SMarcel Holtmann 			     HCI_INIT_TIMEOUT);
3191c8ba6d0SMarcel Holtmann 	if (IS_ERR(skb)) {
3202064ee33SMarcel Holtmann 		bt_dev_err(hdev, "BCM: Reading local version info failed (%ld)",
3212064ee33SMarcel Holtmann 			   PTR_ERR(skb));
3221c8ba6d0SMarcel Holtmann 		return skb;
3231c8ba6d0SMarcel Holtmann 	}
3241c8ba6d0SMarcel Holtmann 
3251c8ba6d0SMarcel Holtmann 	if (skb->len != sizeof(struct hci_rp_read_local_version)) {
3262064ee33SMarcel Holtmann 		bt_dev_err(hdev, "BCM: Local version length mismatch");
3271c8ba6d0SMarcel Holtmann 		kfree_skb(skb);
3281c8ba6d0SMarcel Holtmann 		return ERR_PTR(-EIO);
3291c8ba6d0SMarcel Holtmann 	}
3301c8ba6d0SMarcel Holtmann 
3311c8ba6d0SMarcel Holtmann 	return skb;
3321c8ba6d0SMarcel Holtmann }
3331c8ba6d0SMarcel Holtmann 
btbcm_read_verbose_config(struct hci_dev * hdev)3341c8ba6d0SMarcel Holtmann static struct sk_buff *btbcm_read_verbose_config(struct hci_dev *hdev)
3351c8ba6d0SMarcel Holtmann {
3361c8ba6d0SMarcel Holtmann 	struct sk_buff *skb;
3371c8ba6d0SMarcel Holtmann 
3381c8ba6d0SMarcel Holtmann 	skb = __hci_cmd_sync(hdev, 0xfc79, 0, NULL, HCI_INIT_TIMEOUT);
3391c8ba6d0SMarcel Holtmann 	if (IS_ERR(skb)) {
3402064ee33SMarcel Holtmann 		bt_dev_err(hdev, "BCM: Read verbose config info failed (%ld)",
3412064ee33SMarcel Holtmann 			   PTR_ERR(skb));
3421c8ba6d0SMarcel Holtmann 		return skb;
3431c8ba6d0SMarcel Holtmann 	}
3441c8ba6d0SMarcel Holtmann 
3451c8ba6d0SMarcel Holtmann 	if (skb->len != 7) {
3462064ee33SMarcel Holtmann 		bt_dev_err(hdev, "BCM: Verbose config length mismatch");
3471c8ba6d0SMarcel Holtmann 		kfree_skb(skb);
3481c8ba6d0SMarcel Holtmann 		return ERR_PTR(-EIO);
3491c8ba6d0SMarcel Holtmann 	}
3501c8ba6d0SMarcel Holtmann 
3511c8ba6d0SMarcel Holtmann 	return skb;
3521c8ba6d0SMarcel Holtmann }
3531c8ba6d0SMarcel Holtmann 
btbcm_read_controller_features(struct hci_dev * hdev)3544284ecbeSMarcel Holtmann static struct sk_buff *btbcm_read_controller_features(struct hci_dev *hdev)
3554284ecbeSMarcel Holtmann {
3564284ecbeSMarcel Holtmann 	struct sk_buff *skb;
3574284ecbeSMarcel Holtmann 
3584284ecbeSMarcel Holtmann 	skb = __hci_cmd_sync(hdev, 0xfc6e, 0, NULL, HCI_INIT_TIMEOUT);
3594284ecbeSMarcel Holtmann 	if (IS_ERR(skb)) {
3602064ee33SMarcel Holtmann 		bt_dev_err(hdev, "BCM: Read controller features failed (%ld)",
3612064ee33SMarcel Holtmann 			   PTR_ERR(skb));
3624284ecbeSMarcel Holtmann 		return skb;
3634284ecbeSMarcel Holtmann 	}
3644284ecbeSMarcel Holtmann 
3654284ecbeSMarcel Holtmann 	if (skb->len != 9) {
3662064ee33SMarcel Holtmann 		bt_dev_err(hdev, "BCM: Controller features length mismatch");
3674284ecbeSMarcel Holtmann 		kfree_skb(skb);
3684284ecbeSMarcel Holtmann 		return ERR_PTR(-EIO);
3694284ecbeSMarcel Holtmann 	}
3704284ecbeSMarcel Holtmann 
3714284ecbeSMarcel Holtmann 	return skb;
3724284ecbeSMarcel Holtmann }
3734284ecbeSMarcel Holtmann 
btbcm_read_usb_product(struct hci_dev * hdev)3741c8ba6d0SMarcel Holtmann static struct sk_buff *btbcm_read_usb_product(struct hci_dev *hdev)
3751c8ba6d0SMarcel Holtmann {
3761c8ba6d0SMarcel Holtmann 	struct sk_buff *skb;
3771c8ba6d0SMarcel Holtmann 
3781c8ba6d0SMarcel Holtmann 	skb = __hci_cmd_sync(hdev, 0xfc5a, 0, NULL, HCI_INIT_TIMEOUT);
3791c8ba6d0SMarcel Holtmann 	if (IS_ERR(skb)) {
3802064ee33SMarcel Holtmann 		bt_dev_err(hdev, "BCM: Read USB product info failed (%ld)",
3812064ee33SMarcel Holtmann 			   PTR_ERR(skb));
3821c8ba6d0SMarcel Holtmann 		return skb;
3831c8ba6d0SMarcel Holtmann 	}
3841c8ba6d0SMarcel Holtmann 
3851c8ba6d0SMarcel Holtmann 	if (skb->len != 5) {
3862064ee33SMarcel Holtmann 		bt_dev_err(hdev, "BCM: USB product length mismatch");
3871c8ba6d0SMarcel Holtmann 		kfree_skb(skb);
3881c8ba6d0SMarcel Holtmann 		return ERR_PTR(-EIO);
3891c8ba6d0SMarcel Holtmann 	}
3901c8ba6d0SMarcel Holtmann 
3911c8ba6d0SMarcel Holtmann 	return skb;
3921c8ba6d0SMarcel Holtmann }
3931c8ba6d0SMarcel Holtmann 
394801b4c02SAditya Garg static const struct dmi_system_id disable_broken_read_transmit_power[] = {
395801b4c02SAditya Garg 	{
396801b4c02SAditya Garg 		 .matches = {
397801b4c02SAditya Garg 			DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
398801b4c02SAditya Garg 			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro16,1"),
399801b4c02SAditya Garg 		},
400801b4c02SAditya Garg 	},
401801b4c02SAditya Garg 	{
402801b4c02SAditya Garg 		 .matches = {
403801b4c02SAditya Garg 			DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
404801b4c02SAditya Garg 			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro16,2"),
405801b4c02SAditya Garg 		},
406801b4c02SAditya Garg 	},
407801b4c02SAditya Garg 	{
408801b4c02SAditya Garg 		 .matches = {
409801b4c02SAditya Garg 			DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
410801b4c02SAditya Garg 			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro16,4"),
411801b4c02SAditya Garg 		},
412801b4c02SAditya Garg 	},
413801b4c02SAditya Garg 	{
414801b4c02SAditya Garg 		 .matches = {
415801b4c02SAditya Garg 			DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
4163318ae23SAditya Garg 			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir8,1"),
4173318ae23SAditya Garg 		},
4183318ae23SAditya Garg 	},
4193318ae23SAditya Garg 	{
4203318ae23SAditya Garg 		 .matches = {
4213318ae23SAditya Garg 			DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
4223318ae23SAditya Garg 			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir8,2"),
4233318ae23SAditya Garg 		},
4243318ae23SAditya Garg 	},
4253318ae23SAditya Garg 	{
4263318ae23SAditya Garg 		 .matches = {
4273318ae23SAditya Garg 			DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
428801b4c02SAditya Garg 			DMI_MATCH(DMI_PRODUCT_NAME, "iMac20,1"),
429801b4c02SAditya Garg 		},
430801b4c02SAditya Garg 	},
431801b4c02SAditya Garg 	{
432801b4c02SAditya Garg 		 .matches = {
433801b4c02SAditya Garg 			DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
434801b4c02SAditya Garg 			DMI_MATCH(DMI_PRODUCT_NAME, "iMac20,2"),
435801b4c02SAditya Garg 		},
436801b4c02SAditya Garg 	},
437801b4c02SAditya Garg 	{ }
438801b4c02SAditya Garg };
439801b4c02SAditya Garg 
btbcm_read_info(struct hci_dev * hdev)440e76dc1ddSMarcel Holtmann static int btbcm_read_info(struct hci_dev *hdev)
441e76dc1ddSMarcel Holtmann {
442e76dc1ddSMarcel Holtmann 	struct sk_buff *skb;
443e76dc1ddSMarcel Holtmann 
444e76dc1ddSMarcel Holtmann 	/* Read Verbose Config Version Info */
445e76dc1ddSMarcel Holtmann 	skb = btbcm_read_verbose_config(hdev);
446e76dc1ddSMarcel Holtmann 	if (IS_ERR(skb))
447e76dc1ddSMarcel Holtmann 		return PTR_ERR(skb);
448e76dc1ddSMarcel Holtmann 
4492064ee33SMarcel Holtmann 	bt_dev_info(hdev, "BCM: chip id %u", skb->data[1]);
450e76dc1ddSMarcel Holtmann 	kfree_skb(skb);
451e76dc1ddSMarcel Holtmann 
452af35e28fSHakan Jansson 	return 0;
453af35e28fSHakan Jansson }
454af35e28fSHakan Jansson 
btbcm_print_controller_features(struct hci_dev * hdev)455af35e28fSHakan Jansson static int btbcm_print_controller_features(struct hci_dev *hdev)
456af35e28fSHakan Jansson {
457af35e28fSHakan Jansson 	struct sk_buff *skb;
458af35e28fSHakan Jansson 
459e76dc1ddSMarcel Holtmann 	/* Read Controller Features */
460e76dc1ddSMarcel Holtmann 	skb = btbcm_read_controller_features(hdev);
461e76dc1ddSMarcel Holtmann 	if (IS_ERR(skb))
462e76dc1ddSMarcel Holtmann 		return PTR_ERR(skb);
463e76dc1ddSMarcel Holtmann 
4642064ee33SMarcel Holtmann 	bt_dev_info(hdev, "BCM: features 0x%2.2x", skb->data[1]);
465e76dc1ddSMarcel Holtmann 	kfree_skb(skb);
466e76dc1ddSMarcel Holtmann 
467801b4c02SAditya Garg 	/* Read DMI and disable broken Read LE Min/Max Tx Power */
468801b4c02SAditya Garg 	if (dmi_first_match(disable_broken_read_transmit_power))
469801b4c02SAditya Garg 		set_bit(HCI_QUIRK_BROKEN_READ_TRANSMIT_POWER, &hdev->quirks);
470801b4c02SAditya Garg 
4712fcdd562SHans de Goede 	return 0;
4722fcdd562SHans de Goede }
4732fcdd562SHans de Goede 
btbcm_print_local_name(struct hci_dev * hdev)4742fcdd562SHans de Goede static int btbcm_print_local_name(struct hci_dev *hdev)
4752fcdd562SHans de Goede {
4762fcdd562SHans de Goede 	struct sk_buff *skb;
4772fcdd562SHans de Goede 
478e76dc1ddSMarcel Holtmann 	/* Read Local Name */
479e76dc1ddSMarcel Holtmann 	skb = btbcm_read_local_name(hdev);
480e76dc1ddSMarcel Holtmann 	if (IS_ERR(skb))
481e76dc1ddSMarcel Holtmann 		return PTR_ERR(skb);
482e76dc1ddSMarcel Holtmann 
4832064ee33SMarcel Holtmann 	bt_dev_info(hdev, "%s", (char *)(skb->data + 1));
484e76dc1ddSMarcel Holtmann 	kfree_skb(skb);
485e76dc1ddSMarcel Holtmann 
486e76dc1ddSMarcel Holtmann 	return 0;
487e76dc1ddSMarcel Holtmann }
488e76dc1ddSMarcel Holtmann 
489f7328381SHans de Goede struct bcm_subver_table {
4901c8ba6d0SMarcel Holtmann 	u16 subver;
4911c8ba6d0SMarcel Holtmann 	const char *name;
492f7328381SHans de Goede };
493f7328381SHans de Goede 
494f7328381SHans de Goede static const struct bcm_subver_table bcm_uart_subver_table[] = {
495192aa65aSAngus Ainslie 	{ 0x1111, "BCM4362A2"	},	/* 000.017.017 */
4964a546ec3SFrederic Danis 	{ 0x4103, "BCM4330B1"	},	/* 002.001.003 */
497b8dc6476SStephan Gerhold 	{ 0x410d, "BCM4334B0"	},	/* 002.001.013 */
4989a0bb57dSMarcel Holtmann 	{ 0x410e, "BCM43341B0"	},	/* 002.001.014 */
499039287aaSStephan Gerhold 	{ 0x4204, "BCM2076B1"	},	/* 002.002.004 */
500a8f3b941SFrederic Danis 	{ 0x4406, "BCM4324B3"	},	/* 002.004.006 */
501c03ee9afSHans de Goede 	{ 0x4606, "BCM4324B5"	},	/* 002.006.006 */
502a357ea09SChristian Hewitt 	{ 0x6109, "BCM4335C0"	},	/* 003.001.009 */
50340db5f0eSIlya Faenson 	{ 0x610c, "BCM4354"	},	/* 003.001.012 */
504d456f678SJörg Krause 	{ 0x2122, "BCM4343A0"	},	/* 001.001.034 */
505feb16722SIan Molton 	{ 0x2209, "BCM43430A1"  },	/* 001.002.009 */
50618a39b9aSIan W MORRISON 	{ 0x6119, "BCM4345C0"	},	/* 003.001.025 */
50752c8c7a7SOndrej Jirman 	{ 0x6606, "BCM4345C5"	},	/* 003.006.006 */
508b133e0c4SHans de Goede 	{ 0x230f, "BCM4356A2"	},	/* 001.003.015 */
50992ffe0dbSMaxime Ripard 	{ 0x220e, "BCM20702A1"  },	/* 001.002.014 */
5104f17c2b6SAhmad Fatoum 	{ 0x420d, "BCM4349B1"	},	/* 002.002.013 */
5114f17c2b6SAhmad Fatoum 	{ 0x420e, "BCM4349B1"	},	/* 002.002.014 */
512e3ca60d0SPaweł Chmiel 	{ 0x4217, "BCM4329B1"   },	/* 002.002.023 */
513f4d297eeSNeil Armstrong 	{ 0x6106, "BCM4359C0"	},	/* 003.001.006 */
5141199ab4cSMohammad Rasim 	{ 0x4106, "BCM4335A0"	},	/* 002.001.006 */
51527f4d1f2SMikhail Rudenko 	{ 0x410c, "BCM43430B0"	},	/* 002.001.012 */
5160d37ddfcSTim Harvey 	{ 0x2119, "BCM4373A0"	},	/* 001.001.025 */
5179a0bb57dSMarcel Holtmann 	{ }
5189a0bb57dSMarcel Holtmann };
5199a0bb57dSMarcel Holtmann 
520fec0de23SHans de Goede static const struct bcm_subver_table bcm_usb_subver_table[] = {
521c03ee9afSHans de Goede 	{ 0x2105, "BCM20703A1"	},	/* 001.001.005 */
522fec0de23SHans de Goede 	{ 0x210b, "BCM43142A0"	},	/* 001.001.011 */
523fec0de23SHans de Goede 	{ 0x2112, "BCM4314A0"	},	/* 001.001.018 */
524fec0de23SHans de Goede 	{ 0x2118, "BCM20702A0"	},	/* 001.001.024 */
525fec0de23SHans de Goede 	{ 0x2126, "BCM4335A0"	},	/* 001.001.038 */
526fec0de23SHans de Goede 	{ 0x220e, "BCM20702A1"	},	/* 001.002.014 */
527bf0ddd10SAzamat H. Hackimov 	{ 0x230f, "BCM4356A2"	},	/* 001.003.015 */
528fec0de23SHans de Goede 	{ 0x4106, "BCM4335B0"	},	/* 002.001.006 */
529fec0de23SHans de Goede 	{ 0x410e, "BCM20702B0"	},	/* 002.001.014 */
530fec0de23SHans de Goede 	{ 0x6109, "BCM4335C0"	},	/* 003.001.009 */
531fec0de23SHans de Goede 	{ 0x610c, "BCM4354"	},	/* 003.001.012 */
532bf0ddd10SAzamat H. Hackimov 	{ 0x6607, "BCM4350C5"	},	/* 003.006.007 */
533fec0de23SHans de Goede 	{ }
534fec0de23SHans de Goede };
535fec0de23SHans de Goede 
53663fac334SLinus Walleij /*
53763fac334SLinus Walleij  * This currently only looks up the device tree board appendix,
53863fac334SLinus Walleij  * but can be expanded to other mechanisms.
53963fac334SLinus Walleij  */
btbcm_get_board_name(struct device * dev)54063fac334SLinus Walleij static const char *btbcm_get_board_name(struct device *dev)
54163fac334SLinus Walleij {
54263fac334SLinus Walleij #ifdef CONFIG_OF
54363fac334SLinus Walleij 	struct device_node *root;
54463fac334SLinus Walleij 	char *board_type;
54563fac334SLinus Walleij 	const char *tmp;
54663fac334SLinus Walleij 	int len;
54763fac334SLinus Walleij 	int i;
54863fac334SLinus Walleij 
54963fac334SLinus Walleij 	root = of_find_node_by_path("/");
55063fac334SLinus Walleij 	if (!root)
55163fac334SLinus Walleij 		return NULL;
55263fac334SLinus Walleij 
55363fac334SLinus Walleij 	if (of_property_read_string_index(root, "compatible", 0, &tmp))
55463fac334SLinus Walleij 		return NULL;
55563fac334SLinus Walleij 
55663fac334SLinus Walleij 	/* get rid of any '/' in the compatible string */
55763fac334SLinus Walleij 	len = strlen(tmp) + 1;
55863fac334SLinus Walleij 	board_type = devm_kzalloc(dev, len, GFP_KERNEL);
55963fac334SLinus Walleij 	strscpy(board_type, tmp, len);
560b76abe46SSasha Finkelstein 	for (i = 0; i < len; i++) {
56163fac334SLinus Walleij 		if (board_type[i] == '/')
56263fac334SLinus Walleij 			board_type[i] = '-';
56363fac334SLinus Walleij 	}
56463fac334SLinus Walleij 	of_node_put(root);
56563fac334SLinus Walleij 
56663fac334SLinus Walleij 	return board_type;
56763fac334SLinus Walleij #else
56863fac334SLinus Walleij 	return NULL;
56963fac334SLinus Walleij #endif
57063fac334SLinus Walleij }
57163fac334SLinus Walleij 
btbcm_initialize(struct hci_dev * hdev,bool * fw_load_done,bool use_autobaud_mode)572af35e28fSHakan Jansson int btbcm_initialize(struct hci_dev *hdev, bool *fw_load_done, bool use_autobaud_mode)
57375e167e6SFrederic Danis {
574fec0de23SHans de Goede 	u16 subver, rev, pid, vid;
57575e167e6SFrederic Danis 	struct sk_buff *skb;
57675e167e6SFrederic Danis 	struct hci_rp_read_local_version *ver;
577fec0de23SHans de Goede 	const struct bcm_subver_table *bcm_subver_table;
57874530a63SHans de Goede 	const char *hw_name = NULL;
57963fac334SLinus Walleij 	const char *board_name;
58074530a63SHans de Goede 	char postfix[16] = "";
58174530a63SHans de Goede 	int fw_name_count = 0;
58274530a63SHans de Goede 	bcm_fw_name *fw_name;
5830287c5d8SHans de Goede 	const struct firmware *fw;
58475e167e6SFrederic Danis 	int i, err;
58575e167e6SFrederic Danis 
58663fac334SLinus Walleij 	board_name = btbcm_get_board_name(&hdev->dev);
58763fac334SLinus Walleij 
58875e167e6SFrederic Danis 	/* Reset */
58975e167e6SFrederic Danis 	err = btbcm_reset(hdev);
59075e167e6SFrederic Danis 	if (err)
59175e167e6SFrederic Danis 		return err;
59275e167e6SFrederic Danis 
59375e167e6SFrederic Danis 	/* Read Local Version Info */
59475e167e6SFrederic Danis 	skb = btbcm_read_local_version(hdev);
59575e167e6SFrederic Danis 	if (IS_ERR(skb))
59675e167e6SFrederic Danis 		return PTR_ERR(skb);
59775e167e6SFrederic Danis 
59875e167e6SFrederic Danis 	ver = (struct hci_rp_read_local_version *)skb->data;
59975e167e6SFrederic Danis 	rev = le16_to_cpu(ver->hci_rev);
60075e167e6SFrederic Danis 	subver = le16_to_cpu(ver->lmp_subver);
60175e167e6SFrederic Danis 	kfree_skb(skb);
60275e167e6SFrederic Danis 
603e76dc1ddSMarcel Holtmann 	/* Read controller information */
6040287c5d8SHans de Goede 	if (!(*fw_load_done)) {
605e76dc1ddSMarcel Holtmann 		err = btbcm_read_info(hdev);
606e76dc1ddSMarcel Holtmann 		if (err)
607e76dc1ddSMarcel Holtmann 			return err;
60822ac1916SHans de Goede 	}
609af35e28fSHakan Jansson 
610af35e28fSHakan Jansson 	if (!use_autobaud_mode) {
611af35e28fSHakan Jansson 		err = btbcm_print_controller_features(hdev);
612af35e28fSHakan Jansson 		if (err)
613af35e28fSHakan Jansson 			return err;
614af35e28fSHakan Jansson 
6152fcdd562SHans de Goede 		err = btbcm_print_local_name(hdev);
6162fcdd562SHans de Goede 		if (err)
6172fcdd562SHans de Goede 			return err;
618af35e28fSHakan Jansson 	}
61975e167e6SFrederic Danis 
620fec0de23SHans de Goede 	bcm_subver_table = (hdev->bus == HCI_USB) ? bcm_usb_subver_table :
621fec0de23SHans de Goede 						    bcm_uart_subver_table;
622fec0de23SHans de Goede 
623fec0de23SHans de Goede 	for (i = 0; bcm_subver_table[i].name; i++) {
624fec0de23SHans de Goede 		if (subver == bcm_subver_table[i].subver) {
625fec0de23SHans de Goede 			hw_name = bcm_subver_table[i].name;
62675e167e6SFrederic Danis 			break;
62775e167e6SFrederic Danis 		}
62875e167e6SFrederic Danis 	}
62975e167e6SFrederic Danis 
630f53b975cSHans de Goede 	bt_dev_info(hdev, "%s (%3.3u.%3.3u.%3.3u) build %4.4u",
63174530a63SHans de Goede 		    hw_name ? hw_name : "BCM", (subver & 0xe000) >> 13,
632f53b975cSHans de Goede 		    (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
633f53b975cSHans de Goede 
634f53b975cSHans de Goede 	if (*fw_load_done)
635f53b975cSHans de Goede 		return 0;
636f53b975cSHans de Goede 
637fec0de23SHans de Goede 	if (hdev->bus == HCI_USB) {
638fec0de23SHans de Goede 		/* Read USB Product Info */
639fec0de23SHans de Goede 		skb = btbcm_read_usb_product(hdev);
640fec0de23SHans de Goede 		if (IS_ERR(skb))
641fec0de23SHans de Goede 			return PTR_ERR(skb);
642fec0de23SHans de Goede 
643fec0de23SHans de Goede 		vid = get_unaligned_le16(skb->data + 1);
644fec0de23SHans de Goede 		pid = get_unaligned_le16(skb->data + 3);
645fec0de23SHans de Goede 		kfree_skb(skb);
646fec0de23SHans de Goede 
64774530a63SHans de Goede 		snprintf(postfix, sizeof(postfix), "-%4.4x-%4.4x", vid, pid);
64875e167e6SFrederic Danis 	}
64975e167e6SFrederic Danis 
65074530a63SHans de Goede 	fw_name = kmalloc(BCM_FW_NAME_COUNT_MAX * BCM_FW_NAME_LEN, GFP_KERNEL);
65174530a63SHans de Goede 	if (!fw_name)
65274530a63SHans de Goede 		return -ENOMEM;
65374530a63SHans de Goede 
65474530a63SHans de Goede 	if (hw_name) {
65563fac334SLinus Walleij 		if (board_name) {
65663fac334SLinus Walleij 			snprintf(fw_name[fw_name_count], BCM_FW_NAME_LEN,
65763fac334SLinus Walleij 				 "brcm/%s%s.%s.hcd", hw_name, postfix, board_name);
65863fac334SLinus Walleij 			fw_name_count++;
65963fac334SLinus Walleij 		}
66074530a63SHans de Goede 		snprintf(fw_name[fw_name_count], BCM_FW_NAME_LEN,
66174530a63SHans de Goede 			 "brcm/%s%s.hcd", hw_name, postfix);
66274530a63SHans de Goede 		fw_name_count++;
6630287c5d8SHans de Goede 	}
6640287c5d8SHans de Goede 
66563fac334SLinus Walleij 	if (board_name) {
66663fac334SLinus Walleij 		snprintf(fw_name[fw_name_count], BCM_FW_NAME_LEN,
66763fac334SLinus Walleij 			 "brcm/BCM%s.%s.hcd", postfix, board_name);
66863fac334SLinus Walleij 		fw_name_count++;
66963fac334SLinus Walleij 	}
67074530a63SHans de Goede 	snprintf(fw_name[fw_name_count], BCM_FW_NAME_LEN,
67174530a63SHans de Goede 		 "brcm/BCM%s.hcd", postfix);
67274530a63SHans de Goede 	fw_name_count++;
67374530a63SHans de Goede 
67474530a63SHans de Goede 	for (i = 0; i < fw_name_count; i++) {
67574530a63SHans de Goede 		err = firmware_request_nowarn(&fw, fw_name[i], &hdev->dev);
67674530a63SHans de Goede 		if (err == 0) {
67774530a63SHans de Goede 			bt_dev_info(hdev, "%s '%s' Patch",
67874530a63SHans de Goede 				    hw_name ? hw_name : "BCM", fw_name[i]);
67974530a63SHans de Goede 			*fw_load_done = true;
68074530a63SHans de Goede 			break;
68174530a63SHans de Goede 		}
68274530a63SHans de Goede 	}
68374530a63SHans de Goede 
68474530a63SHans de Goede 	if (*fw_load_done) {
6850287c5d8SHans de Goede 		err = btbcm_patchram(hdev, fw);
6860287c5d8SHans de Goede 		if (err)
6870287c5d8SHans de Goede 			bt_dev_info(hdev, "BCM: Patch failed (%d)", err);
6880287c5d8SHans de Goede 
6890287c5d8SHans de Goede 		release_firmware(fw);
69074530a63SHans de Goede 	} else {
69174530a63SHans de Goede 		bt_dev_err(hdev, "BCM: firmware Patch file not found, tried:");
69274530a63SHans de Goede 		for (i = 0; i < fw_name_count; i++)
69374530a63SHans de Goede 			bt_dev_err(hdev, "BCM: '%s'", fw_name[i]);
69474530a63SHans de Goede 	}
69574530a63SHans de Goede 
69674530a63SHans de Goede 	kfree(fw_name);
69775e167e6SFrederic Danis 	return 0;
69875e167e6SFrederic Danis }
69975e167e6SFrederic Danis EXPORT_SYMBOL_GPL(btbcm_initialize);
70075e167e6SFrederic Danis 
btbcm_finalize(struct hci_dev * hdev,bool * fw_load_done,bool use_autobaud_mode)701af35e28fSHakan Jansson int btbcm_finalize(struct hci_dev *hdev, bool *fw_load_done, bool use_autobaud_mode)
70275e167e6SFrederic Danis {
70375e167e6SFrederic Danis 	int err;
70475e167e6SFrederic Danis 
7050383f16aSHans de Goede 	/* Re-initialize if necessary */
7060383f16aSHans de Goede 	if (*fw_load_done) {
707af35e28fSHakan Jansson 		err = btbcm_initialize(hdev, fw_load_done, use_autobaud_mode);
70875e167e6SFrederic Danis 		if (err)
70975e167e6SFrederic Danis 			return err;
7100383f16aSHans de Goede 	}
71175e167e6SFrederic Danis 
71275e167e6SFrederic Danis 	btbcm_check_bdaddr(hdev);
71375e167e6SFrederic Danis 
71475e167e6SFrederic Danis 	set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
71575e167e6SFrederic Danis 
71675e167e6SFrederic Danis 	return 0;
71775e167e6SFrederic Danis }
71875e167e6SFrederic Danis EXPORT_SYMBOL_GPL(btbcm_finalize);
71975e167e6SFrederic Danis 
btbcm_setup_patchram(struct hci_dev * hdev)7201c8ba6d0SMarcel Holtmann int btbcm_setup_patchram(struct hci_dev *hdev)
7211c8ba6d0SMarcel Holtmann {
7220287c5d8SHans de Goede 	bool fw_load_done = false;
723af35e28fSHakan Jansson 	bool use_autobaud_mode = false;
72459ce5699SHans de Goede 	int err;
7251c8ba6d0SMarcel Holtmann 
72659ce5699SHans de Goede 	/* Initialize */
727af35e28fSHakan Jansson 	err = btbcm_initialize(hdev, &fw_load_done, use_autobaud_mode);
7281c8ba6d0SMarcel Holtmann 	if (err)
7291c8ba6d0SMarcel Holtmann 		return err;
7301c8ba6d0SMarcel Holtmann 
7310287c5d8SHans de Goede 	/* Re-initialize after loading Patch */
732af35e28fSHakan Jansson 	return btbcm_finalize(hdev, &fw_load_done, use_autobaud_mode);
7331c8ba6d0SMarcel Holtmann }
7341c8ba6d0SMarcel Holtmann EXPORT_SYMBOL_GPL(btbcm_setup_patchram);
7351c8ba6d0SMarcel Holtmann 
btbcm_setup_apple(struct hci_dev * hdev)7361c8ba6d0SMarcel Holtmann int btbcm_setup_apple(struct hci_dev *hdev)
7371c8ba6d0SMarcel Holtmann {
7381c8ba6d0SMarcel Holtmann 	struct sk_buff *skb;
739b224d3ffSMarcel Holtmann 	int err;
740b224d3ffSMarcel Holtmann 
741b224d3ffSMarcel Holtmann 	/* Reset */
742b224d3ffSMarcel Holtmann 	err = btbcm_reset(hdev);
743b224d3ffSMarcel Holtmann 	if (err)
744b224d3ffSMarcel Holtmann 		return err;
7451c8ba6d0SMarcel Holtmann 
7461c8ba6d0SMarcel Holtmann 	/* Read Verbose Config Version Info */
7471c8ba6d0SMarcel Holtmann 	skb = btbcm_read_verbose_config(hdev);
7487bee8b08SChris Mason 	if (!IS_ERR(skb)) {
7492064ee33SMarcel Holtmann 		bt_dev_info(hdev, "BCM: chip id %u build %4.4u",
750b224d3ffSMarcel Holtmann 			    skb->data[1], get_unaligned_le16(skb->data + 5));
7511c8ba6d0SMarcel Holtmann 		kfree_skb(skb);
7527bee8b08SChris Mason 	}
7531c8ba6d0SMarcel Holtmann 
75434cea41eSMarcel Holtmann 	/* Read USB Product Info */
75534cea41eSMarcel Holtmann 	skb = btbcm_read_usb_product(hdev);
75634cea41eSMarcel Holtmann 	if (!IS_ERR(skb)) {
7572064ee33SMarcel Holtmann 		bt_dev_info(hdev, "BCM: product %4.4x:%4.4x",
75834cea41eSMarcel Holtmann 			    get_unaligned_le16(skb->data + 1),
75934cea41eSMarcel Holtmann 			    get_unaligned_le16(skb->data + 3));
76034cea41eSMarcel Holtmann 		kfree_skb(skb);
76134cea41eSMarcel Holtmann 	}
76234cea41eSMarcel Holtmann 
7634284ecbeSMarcel Holtmann 	/* Read Controller Features */
7644284ecbeSMarcel Holtmann 	skb = btbcm_read_controller_features(hdev);
7654284ecbeSMarcel Holtmann 	if (!IS_ERR(skb)) {
7662064ee33SMarcel Holtmann 		bt_dev_info(hdev, "BCM: features 0x%2.2x", skb->data[1]);
7674284ecbeSMarcel Holtmann 		kfree_skb(skb);
7684284ecbeSMarcel Holtmann 	}
7694284ecbeSMarcel Holtmann 
7709bc63ca0SMarcel Holtmann 	/* Read Local Name */
7719bc63ca0SMarcel Holtmann 	skb = btbcm_read_local_name(hdev);
7729bc63ca0SMarcel Holtmann 	if (!IS_ERR(skb)) {
7732064ee33SMarcel Holtmann 		bt_dev_info(hdev, "%s", (char *)(skb->data + 1));
7749bc63ca0SMarcel Holtmann 		kfree_skb(skb);
7759bc63ca0SMarcel Holtmann 	}
7769bc63ca0SMarcel Holtmann 
777941521e2SMarcel Holtmann 	set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
778941521e2SMarcel Holtmann 
7791c8ba6d0SMarcel Holtmann 	return 0;
7801c8ba6d0SMarcel Holtmann }
7811c8ba6d0SMarcel Holtmann EXPORT_SYMBOL_GPL(btbcm_setup_apple);
7821c8ba6d0SMarcel Holtmann 
7834fba30f0SMarcel Holtmann MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
7844fba30f0SMarcel Holtmann MODULE_DESCRIPTION("Bluetooth support for Broadcom devices ver " VERSION);
7854fba30f0SMarcel Holtmann MODULE_VERSION(VERSION);
7864fba30f0SMarcel Holtmann MODULE_LICENSE("GPL");
787