diff options
Diffstat (limited to 'drivers/bluetooth/btintel.c')
| -rw-r--r-- | drivers/bluetooth/btintel.c | 124 | 
1 files changed, 124 insertions, 0 deletions
| diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index 7d5e4de64e3c..1ccbb5157515 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -12,6 +12,7 @@  #include <linux/acpi.h>  #include <acpi/acpi_bus.h>  #include <asm/unaligned.h> +#include <linux/efi.h>  #include <net/bluetooth/bluetooth.h>  #include <net/bluetooth/hci_core.h> @@ -26,6 +27,8 @@  #define ECDSA_OFFSET		644  #define ECDSA_HEADER_LEN	320 +#define BTINTEL_EFI_DSBR	L"UefiCnvCommonDSBR" +  enum {  	DSM_SET_WDISABLE2_DELAY = 1,  	DSM_SET_RESET_METHOD = 3, @@ -2616,6 +2619,120 @@ static u8 btintel_classify_pkt_type(struct hci_dev *hdev, struct sk_buff *skb)  	return hci_skb_pkt_type(skb);  } +/* + * UefiCnvCommonDSBR UEFI variable provides information from the OEM platforms + * if they have replaced the BRI (Bluetooth Radio Interface) resistor to + * overcome the potential STEP errors on their designs. Based on the + * configauration, bluetooth firmware shall adjust the BRI response line drive + * strength. The below structure represents DSBR data. + * struct { + *	u8 header; + *	u32 dsbr; + * } __packed; + * + * header - defines revision number of the structure + * dsbr - defines drive strength BRI response + *	bit0 + *		0 - instructs bluetooth firmware to use default values + *		1 - instructs bluetooth firmware to override default values + *	bit3:1 + *		Reserved + *	bit7:4 + *		DSBR override values (only if bit0 is set. Default value is 0xF + *	bit31:7 + *		Reserved + * Expected values for dsbr field: + *	1. 0xF1 - indicates that the resistor on board is 33 Ohm + *	2. 0x00 or 0xB1 - indicates that the resistor on board is 10 Ohm + *	3. Non existing UEFI variable or invalid (none of the above) - indicates + *	   that the resistor on board is 10 Ohm + * Even if uefi variable is not present, driver shall send 0xfc0a command to + * firmware to use default values. + * + */ +static int btintel_uefi_get_dsbr(u32 *dsbr_var) +{ +	struct btintel_dsbr { +		u8 header; +		u32 dsbr; +	} __packed data; + +	efi_status_t status; +	unsigned long data_size = 0; +	efi_guid_t guid = EFI_GUID(0xe65d8884, 0xd4af, 0x4b20, 0x8d, 0x03, +				   0x77, 0x2e, 0xcc, 0x3d, 0xa5, 0x31); + +	if (!IS_ENABLED(CONFIG_EFI)) +		return -EOPNOTSUPP; + +	if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) +		return -EOPNOTSUPP; + +	status = efi.get_variable(BTINTEL_EFI_DSBR, &guid, NULL, &data_size, +				  NULL); + +	if (status != EFI_BUFFER_TOO_SMALL || !data_size) +		return -EIO; + +	status = efi.get_variable(BTINTEL_EFI_DSBR, &guid, NULL, &data_size, +				  &data); + +	if (status != EFI_SUCCESS) +		return -ENXIO; + +	*dsbr_var = data.dsbr; +	return 0; +} + +static int btintel_set_dsbr(struct hci_dev *hdev, struct intel_version_tlv *ver) +{ +	struct btintel_dsbr_cmd { +		u8 enable; +		u8 dsbr; +	} __packed; + +	struct btintel_dsbr_cmd cmd; +	struct sk_buff *skb; +	u8 status; +	u32 dsbr; +	bool apply_dsbr; +	int err; + +	/* DSBR command needs to be sent for BlazarI + B0 step product after +	 * downloading IML image. +	 */ +	apply_dsbr = (ver->img_type == BTINTEL_IMG_IML && +		((ver->cnvi_top & 0xfff) == BTINTEL_CNVI_BLAZARI) && +		INTEL_CNVX_TOP_STEP(ver->cnvi_top) == 0x01); + +	if (!apply_dsbr) +		return 0; + +	dsbr = 0; +	err = btintel_uefi_get_dsbr(&dsbr); +	if (err < 0) +		bt_dev_dbg(hdev, "Error reading efi: %ls  (%d)", +			   BTINTEL_EFI_DSBR, err); + +	cmd.enable = dsbr & BIT(0); +	cmd.dsbr = dsbr >> 4 & 0xF; + +	bt_dev_info(hdev, "dsbr: enable: 0x%2.2x value: 0x%2.2x", cmd.enable, +		    cmd.dsbr); + +	skb = __hci_cmd_sync(hdev, 0xfc0a, sizeof(cmd), &cmd,  HCI_CMD_TIMEOUT); +	if (IS_ERR(skb)) +		return -bt_to_errno(PTR_ERR(skb)); + +	status = skb->data[0]; +	kfree_skb(skb); + +	if (status) +		return -bt_to_errno(status); + +	return 0; +} +  int btintel_bootloader_setup_tlv(struct hci_dev *hdev,  				 struct intel_version_tlv *ver)  { @@ -2650,6 +2767,13 @@ int btintel_bootloader_setup_tlv(struct hci_dev *hdev,  	if (err)  		return err; +	/* set drive strength of BRI response */ +	err = btintel_set_dsbr(hdev, ver); +	if (err) { +		bt_dev_err(hdev, "Failed to send dsbr command (%d)", err); +		return err; +	} +  	/* If image type returned is BTINTEL_IMG_IML, then controller supports  	 * intermediate loader image  	 */ | 
