diff options
Diffstat (limited to 'drivers/bluetooth/btintel.c')
| -rw-r--r-- | drivers/bluetooth/btintel.c | 229 | 
1 files changed, 220 insertions, 9 deletions
diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index d9349ba48281..2462796a512a 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -10,6 +10,7 @@  #include <linux/firmware.h>  #include <linux/regmap.h>  #include <linux/acpi.h> +#include <acpi/acpi_bus.h>  #include <asm/unaligned.h>  #include <net/bluetooth/bluetooth.h> @@ -27,6 +28,11 @@  #define BTINTEL_PPAG_NAME   "PPAG" +enum { +	DSM_SET_WDISABLE2_DELAY = 1, +	DSM_SET_RESET_METHOD = 3, +}; +  /* structure to store the PPAG data read from ACPI table */  struct btintel_ppag {  	u32	domain; @@ -49,6 +55,10 @@ static struct {  	u32        fw_build_num;  } coredump_info; +static const guid_t btintel_guid_dsm = +	GUID_INIT(0xaa10f4e0, 0x81ac, 0x4233, +		  0xab, 0xf6, 0x3b, 0x2a, 0xc5, 0x0e, 0x28, 0xd9); +  int btintel_check_bdaddr(struct hci_dev *hdev)  {  	struct hci_rp_read_bd_addr *bda; @@ -470,6 +480,7 @@ static int btintel_version_info_tlv(struct hci_dev *hdev,  	case 0x18:	/* Slr */  	case 0x19:	/* Slr-F */  	case 0x1b:      /* Mgr */ +	case 0x1c:	/* Gale Peak (GaP) */  		break;  	default:  		bt_dev_err(hdev, "Unsupported Intel hardware variant (0x%x)", @@ -2390,7 +2401,7 @@ static void btintel_set_ppag(struct hci_dev *hdev, struct intel_version_tlv *ver  {  	struct btintel_ppag ppag;  	struct sk_buff *skb; -	struct btintel_loc_aware_reg ppag_cmd; +	struct hci_ppag_enable_cmd ppag_cmd;  	acpi_handle handle;  	/* PPAG is not supported if CRF is HrP2, Jfp2, JfP1 */ @@ -2398,6 +2409,8 @@ static void btintel_set_ppag(struct hci_dev *hdev, struct intel_version_tlv *ver  	case 0x504:     /* Hrp2 */  	case 0x202:     /* Jfp2 */  	case 0x201:     /* Jfp1 */ +		bt_dev_dbg(hdev, "PPAG not supported for Intel CNVr (0x%3x)", +			   ver->cnvr_top & 0xFFF);  		return;  	} @@ -2423,27 +2436,142 @@ static void btintel_set_ppag(struct hci_dev *hdev, struct intel_version_tlv *ver  	}  	if (ppag.domain != 0x12) { -		bt_dev_warn(hdev, "PPAG-BT: domain is not bluetooth"); +		bt_dev_dbg(hdev, "PPAG-BT: Bluetooth domain is disabled in ACPI firmware");  		return;  	} -	/* PPAG mode, BIT0 = 0 Disabled, BIT0 = 1 Enabled */ -	if (!(ppag.mode & BIT(0))) { -		bt_dev_dbg(hdev, "PPAG-BT: disabled"); +	/* PPAG mode +	 * BIT 0 : 0 Disabled in EU +	 *         1 Enabled in EU +	 * BIT 1 : 0 Disabled in China +	 *         1 Enabled in China +	 */ +	if ((ppag.mode & 0x01) != BIT(0) && (ppag.mode & 0x02) != BIT(1)) { +		bt_dev_dbg(hdev, "PPAG-BT: EU, China mode are disabled in CB/BIOS");  		return;  	} -	ppag_cmd.mcc = cpu_to_le32(0); -	ppag_cmd.sel = cpu_to_le32(0); /* 0 - Enable , 1 - Disable, 2 - Testing mode */ -	ppag_cmd.delta = cpu_to_le32(0); -	skb = __hci_cmd_sync(hdev, 0xfe19, sizeof(ppag_cmd), &ppag_cmd, HCI_CMD_TIMEOUT); +	ppag_cmd.ppag_enable_flags = cpu_to_le32(ppag.mode); + +	skb = __hci_cmd_sync(hdev, INTEL_OP_PPAG_CMD, sizeof(ppag_cmd), &ppag_cmd, HCI_CMD_TIMEOUT);  	if (IS_ERR(skb)) {  		bt_dev_warn(hdev, "Failed to send PPAG Enable (%ld)", PTR_ERR(skb));  		return;  	} +	bt_dev_info(hdev, "PPAG-BT: Enabled (Mode %d)", ppag.mode);  	kfree_skb(skb);  } +static int btintel_acpi_reset_method(struct hci_dev *hdev) +{ +	int ret = 0; +	acpi_status status; +	union acpi_object *p, *ref; +	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + +	status = acpi_evaluate_object(ACPI_HANDLE(GET_HCIDEV_DEV(hdev)), "_PRR", NULL, &buffer); +	if (ACPI_FAILURE(status)) { +		bt_dev_err(hdev, "Failed to run _PRR method"); +		ret = -ENODEV; +		return ret; +	} +	p = buffer.pointer; + +	if (p->package.count != 1 || p->type != ACPI_TYPE_PACKAGE) { +		bt_dev_err(hdev, "Invalid arguments"); +		ret = -EINVAL; +		goto exit_on_error; +	} + +	ref = &p->package.elements[0]; +	if (ref->type != ACPI_TYPE_LOCAL_REFERENCE) { +		bt_dev_err(hdev, "Invalid object type: 0x%x", ref->type); +		ret = -EINVAL; +		goto exit_on_error; +	} + +	status = acpi_evaluate_object(ref->reference.handle, "_RST", NULL, NULL); +	if (ACPI_FAILURE(status)) { +		bt_dev_err(hdev, "Failed to run_RST method"); +		ret = -ENODEV; +		goto exit_on_error; +	} + +exit_on_error: +	kfree(buffer.pointer); +	return ret; +} + +static void btintel_set_dsm_reset_method(struct hci_dev *hdev, +					 struct intel_version_tlv *ver_tlv) +{ +	struct btintel_data *data = hci_get_priv(hdev); +	acpi_handle handle = ACPI_HANDLE(GET_HCIDEV_DEV(hdev)); +	u8 reset_payload[4] = {0x01, 0x00, 0x01, 0x00}; +	union acpi_object *obj, argv4; +	enum { +		RESET_TYPE_WDISABLE2, +		RESET_TYPE_VSEC +	}; + +	handle = ACPI_HANDLE(GET_HCIDEV_DEV(hdev)); + +	if (!handle) { +		bt_dev_dbg(hdev, "No support for bluetooth device in ACPI firmware"); +		return; +	} + +	if (!acpi_has_method(handle, "_PRR")) { +		bt_dev_err(hdev, "No support for _PRR ACPI method"); +		return; +	} + +	switch (ver_tlv->cnvi_top & 0xfff) { +	case 0x910: /* GalePeak2 */ +		reset_payload[2] = RESET_TYPE_VSEC; +		break; +	default: +		/* WDISABLE2 is the default reset method */ +		reset_payload[2] = RESET_TYPE_WDISABLE2; + +		if (!acpi_check_dsm(handle, &btintel_guid_dsm, 0, +				    BIT(DSM_SET_WDISABLE2_DELAY))) { +			bt_dev_err(hdev, "No dsm support to set reset delay"); +			return; +		} +		argv4.integer.type = ACPI_TYPE_INTEGER; +		/* delay required to toggle BT power */ +		argv4.integer.value = 160; +		obj = acpi_evaluate_dsm(handle, &btintel_guid_dsm, 0, +					DSM_SET_WDISABLE2_DELAY, &argv4); +		if (!obj) { +			bt_dev_err(hdev, "Failed to call dsm to set reset delay"); +			return; +		} +		ACPI_FREE(obj); +	} + +	bt_dev_info(hdev, "DSM reset method type: 0x%02x", reset_payload[2]); + +	if (!acpi_check_dsm(handle, &btintel_guid_dsm, 0, +			    DSM_SET_RESET_METHOD)) { +		bt_dev_warn(hdev, "No support for dsm to set reset method"); +		return; +	} +	argv4.buffer.type = ACPI_TYPE_BUFFER; +	argv4.buffer.length = sizeof(reset_payload); +	argv4.buffer.pointer = reset_payload; + +	obj = acpi_evaluate_dsm(handle, &btintel_guid_dsm, 0, +				DSM_SET_RESET_METHOD, &argv4); +	if (!obj) { +		bt_dev_err(hdev, "Failed to call dsm to set reset method"); +		return; +	} +	ACPI_FREE(obj); +	data->acpi_reset_method = btintel_acpi_reset_method; +} +  static int btintel_bootloader_setup_tlv(struct hci_dev *hdev,  					struct intel_version_tlv *ver)  { @@ -2528,6 +2656,7 @@ static void btintel_set_msft_opcode(struct hci_dev *hdev, u8 hw_variant)  	case 0x18:  	case 0x19:  	case 0x1b: +	case 0x1c:  		hci_set_msft_opcode(hdev, 0xFC1E);  		break;  	default: @@ -2658,6 +2787,9 @@ static int btintel_setup_combined(struct hci_dev *hdev)  			set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED,  				&hdev->quirks); +			/* These variants don't seem to support LE Coded PHY */ +			set_bit(HCI_QUIRK_BROKEN_LE_CODED, &hdev->quirks); +  			/* Setup MSFT Extension support */  			btintel_set_msft_opcode(hdev, ver.hw_variant); @@ -2729,6 +2861,9 @@ static int btintel_setup_combined(struct hci_dev *hdev)  		 */  		set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks); +		/* These variants don't seem to support LE Coded PHY */ +		set_bit(HCI_QUIRK_BROKEN_LE_CODED, &hdev->quirks); +  		/* Set Valid LE States quirk */  		set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks); @@ -2742,6 +2877,7 @@ static int btintel_setup_combined(struct hci_dev *hdev)  	case 0x18:  	case 0x19:  	case 0x1b: +	case 0x1c:  		/* Display version information of TLV type */  		btintel_version_info_tlv(hdev, &ver_tlv); @@ -2757,6 +2893,7 @@ static int btintel_setup_combined(struct hci_dev *hdev)  		/* Setup MSFT Extension support */  		btintel_set_msft_opcode(hdev,  					INTEL_HW_VARIANT(ver_tlv.cnvi_bt)); +		btintel_set_dsm_reset_method(hdev, &ver_tlv);  		err = btintel_bootloader_setup_tlv(hdev, &ver_tlv);  		btintel_register_devcoredump_support(hdev); @@ -2824,6 +2961,80 @@ int btintel_configure_setup(struct hci_dev *hdev, const char *driver_name)  }  EXPORT_SYMBOL_GPL(btintel_configure_setup); +static int btintel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb) +{ +	struct intel_tlv *tlv = (void *)&skb->data[5]; + +	/* The first event is always an event type TLV */ +	if (tlv->type != INTEL_TLV_TYPE_ID) +		goto recv_frame; + +	switch (tlv->val[0]) { +	case INTEL_TLV_SYSTEM_EXCEPTION: +	case INTEL_TLV_FATAL_EXCEPTION: +	case INTEL_TLV_DEBUG_EXCEPTION: +	case INTEL_TLV_TEST_EXCEPTION: +		/* Generate devcoredump from exception */ +		if (!hci_devcd_init(hdev, skb->len)) { +			hci_devcd_append(hdev, skb); +			hci_devcd_complete(hdev); +		} else { +			bt_dev_err(hdev, "Failed to generate devcoredump"); +			kfree_skb(skb); +		} +		return 0; +	default: +		bt_dev_err(hdev, "Invalid exception type %02X", tlv->val[0]); +	} + +recv_frame: +	return hci_recv_frame(hdev, skb); +} + +int btintel_recv_event(struct hci_dev *hdev, struct sk_buff *skb) +{ +	struct hci_event_hdr *hdr = (void *)skb->data; +	const char diagnostics_hdr[] = { 0x87, 0x80, 0x03 }; + +	if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff && +	    hdr->plen > 0) { +		const void *ptr = skb->data + HCI_EVENT_HDR_SIZE + 1; +		unsigned int len = skb->len - HCI_EVENT_HDR_SIZE - 1; + +		if (btintel_test_flag(hdev, INTEL_BOOTLOADER)) { +			switch (skb->data[2]) { +			case 0x02: +				/* When switching to the operational firmware +				 * the device sends a vendor specific event +				 * indicating that the bootup completed. +				 */ +				btintel_bootup(hdev, ptr, len); +				break; +			case 0x06: +				/* When the firmware loading completes the +				 * device sends out a vendor specific event +				 * indicating the result of the firmware +				 * loading. +				 */ +				btintel_secure_send_result(hdev, ptr, len); +				break; +			} +		} + +		/* Handle all diagnostics events separately. May still call +		 * hci_recv_frame. +		 */ +		if (len >= sizeof(diagnostics_hdr) && +		    memcmp(&skb->data[2], diagnostics_hdr, +			   sizeof(diagnostics_hdr)) == 0) { +			return btintel_diagnostics(hdev, skb); +		} +	} + +	return hci_recv_frame(hdev, skb); +} +EXPORT_SYMBOL_GPL(btintel_recv_event); +  void btintel_bootup(struct hci_dev *hdev, const void *ptr, unsigned int len)  {  	const struct intel_bootup *evt = ptr;  | 
