diff options
| author | Dmitry Torokhov <[email protected]> | 2023-05-01 15:20:08 -0700 | 
|---|---|---|
| committer | Dmitry Torokhov <[email protected]> | 2023-05-01 15:20:08 -0700 | 
| commit | 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e (patch) | |
| tree | d57f3a63479a07b4e0cece029886e76e04feb984 /drivers/bluetooth/btintel.c | |
| parent | 5dc63e56a9cf8df0b59c234a505a1653f1bdf885 (diff) | |
| parent | 53bea86b5712c7491bb3dae12e271666df0a308c (diff) | |
Merge branch 'next' into for-linus
Prepare input updates for 6.4 merge window.
Diffstat (limited to 'drivers/bluetooth/btintel.c')
| -rw-r--r-- | drivers/bluetooth/btintel.c | 116 | 
1 files changed, 116 insertions, 0 deletions
diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index d4e2cb9a4eb4..bede8b005594 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -9,6 +9,7 @@  #include <linux/module.h>  #include <linux/firmware.h>  #include <linux/regmap.h> +#include <linux/acpi.h>  #include <asm/unaligned.h>  #include <net/bluetooth/bluetooth.h> @@ -24,6 +25,9 @@  #define ECDSA_OFFSET		644  #define ECDSA_HEADER_LEN	320 +#define BTINTEL_PPAG_NAME   "PPAG" +#define BTINTEL_PPAG_PREFIX "\\_SB_.PCI0.XHCI.RHUB" +  #define CMD_WRITE_BOOT_PARAMS	0xfc0e  struct cmd_write_boot_params {  	__le32 boot_addr; @@ -1278,6 +1282,63 @@ static int btintel_read_debug_features(struct hci_dev *hdev,  	return 0;  } +static acpi_status btintel_ppag_callback(acpi_handle handle, u32 lvl, void *data, +					 void **ret) +{ +	acpi_status status; +	size_t len; +	struct btintel_ppag *ppag = data; +	union acpi_object *p, *elements; +	struct acpi_buffer string = {ACPI_ALLOCATE_BUFFER, NULL}; +	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; +	struct hci_dev *hdev = ppag->hdev; + +	status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &string); +	if (ACPI_FAILURE(status)) { +		bt_dev_warn(hdev, "ACPI Failure: %s", acpi_format_exception(status)); +		return status; +	} + +	if (strncmp(BTINTEL_PPAG_PREFIX, string.pointer, +		    strlen(BTINTEL_PPAG_PREFIX))) { +		kfree(string.pointer); +		return AE_OK; +	} + +	len = strlen(string.pointer); +	if (strncmp((char *)string.pointer + len - 4, BTINTEL_PPAG_NAME, 4)) { +		kfree(string.pointer); +		return AE_OK; +	} +	kfree(string.pointer); + +	status = acpi_evaluate_object(handle, NULL, NULL, &buffer); +	if (ACPI_FAILURE(status)) { +		bt_dev_warn(hdev, "ACPI Failure: %s", acpi_format_exception(status)); +		return status; +	} + +	p = buffer.pointer; +	ppag = (struct btintel_ppag *)data; + +	if (p->type != ACPI_TYPE_PACKAGE || p->package.count != 2) { +		kfree(buffer.pointer); +		bt_dev_warn(hdev, "Invalid object type: %d or package count: %d", +			    p->type, p->package.count); +		return AE_ERROR; +	} + +	elements = p->package.elements; + +	/* PPAG table is located at element[1] */ +	p = &elements[1]; + +	ppag->domain = (u32)p->package.elements[0].integer.value; +	ppag->mode = (u32)p->package.elements[1].integer.value; +	kfree(buffer.pointer); +	return AE_CTRL_TERMINATE; +} +  static int btintel_set_debug_features(struct hci_dev *hdev,  			       const struct intel_debug_features *features)  { @@ -2251,6 +2312,58 @@ error:  	return err;  } +static void btintel_set_ppag(struct hci_dev *hdev, struct intel_version_tlv *ver) +{ +	acpi_status status; +	struct btintel_ppag ppag; +	struct sk_buff *skb; +	struct btintel_loc_aware_reg ppag_cmd; + +    /* PPAG is not supported if CRF is HrP2, Jfp2, JfP1 */ +	switch (ver->cnvr_top & 0xFFF) { +	case 0x504:     /* Hrp2 */ +	case 0x202:     /* Jfp2 */ +	case 0x201:     /* Jfp1 */ +		return; +	} + +	memset(&ppag, 0, sizeof(ppag)); + +	ppag.hdev = hdev; +	status = acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, +				     ACPI_UINT32_MAX, NULL, +				     btintel_ppag_callback, &ppag, NULL); + +	if (ACPI_FAILURE(status)) { +		/* Do not log warning message if ACPI entry is not found */ +		if (status == AE_NOT_FOUND) +			return; +		bt_dev_warn(hdev, "PPAG: ACPI Failure: %s", acpi_format_exception(status)); +		return; +	} + +	if (ppag.domain != 0x12) { +		bt_dev_warn(hdev, "PPAG-BT Domain disabled"); +		return; +	} + +	/* PPAG mode, BIT0 = 0 Disabled, BIT0 = 1 Enabled */ +	if (!(ppag.mode & BIT(0))) { +		bt_dev_dbg(hdev, "PPAG disabled"); +		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); +	if (IS_ERR(skb)) { +		bt_dev_warn(hdev, "Failed to send PPAG Enable (%ld)", PTR_ERR(skb)); +		return; +	} +	kfree_skb(skb); +} +  static int btintel_bootloader_setup_tlv(struct hci_dev *hdev,  					struct intel_version_tlv *ver)  { @@ -2297,6 +2410,9 @@ static int btintel_bootloader_setup_tlv(struct hci_dev *hdev,  	hci_dev_clear_flag(hdev, HCI_QUALITY_REPORT); +	/* Set PPAG feature */ +	btintel_set_ppag(hdev, ver); +  	/* Read the Intel version information after loading the FW  */  	err = btintel_read_version_tlv(hdev, &new_ver);  	if (err)  |