diff mbox series

[v1] Bluetooth: btintel: Set Per Platform Antenna Gain(PPAG)

Message ID 20230203093332.4736-1-kiran.k@intel.com
State Superseded
Headers show
Series [v1] Bluetooth: btintel: Set Per Platform Antenna Gain(PPAG) | expand

Commit Message

K, Kiran Feb. 3, 2023, 9:33 a.m. UTC
From: Seema Sreemantha <seema.sreemantha@intel.com>

Antenna gain is defined as the antenna’s ability to
increase the Tx power in a given direction. Intel
is certifying its products with fixed reference
antenna peak gain values (3/5dBi). The feature takes
into account the actual antenna gain, and increases
output power values, which results in a performance
improvement.

After firmware download is completed, driver reads from
ACPI table and configures PPAG as required. ACPI table
entry for PPAG is defined as below.

Name (PPAG, Package (0x02)
{
    0x00000001,
    Package (0x02)
    {
        0x00000012, /* Bluetooth Domain */
        0x00000001  /* 1 - Enable PPAG, 0 - Disable PPAG */
    }
})

btmon log:

< HCI Command: Vendor (0x3f|0x0219) plen 12
    00 00 00 00 00 00 00 00 00 00 00 00
> HCI Event: Command Complete (0x0e) plen 4
    Vendor (0x3f|0x0219) ncmd 1
    Status: Success (0x00)

Signed-off-by: Kiran K <kiran.k@intel.com>
Signed-off-by: Seema Sreemantha <seema.sreemantha@intel.com>
---
 drivers/bluetooth/btintel.c | 114 ++++++++++++++++++++++++++++++++++++
 drivers/bluetooth/btintel.h |  13 ++++
 2 files changed, 127 insertions(+)

Comments

bluez.test.bot@gmail.com Feb. 3, 2023, 9:55 a.m. UTC | #1
This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=718490

---Test result---

Test Summary:
CheckPatch                    PASS      1.11 seconds
GitLint                       PASS      0.29 seconds
SubjectPrefix                 PASS      0.09 seconds
BuildKernel                   PASS      29.78 seconds
CheckAllWarning               PASS      31.18 seconds
CheckSparse                   PASS      36.11 seconds
CheckSmatch                   PASS      95.28 seconds
BuildKernel32                 PASS      27.59 seconds
TestRunnerSetup               PASS      390.39 seconds
TestRunner_l2cap-tester       PASS      15.62 seconds
TestRunner_iso-tester         PASS      15.92 seconds
TestRunner_bnep-tester        PASS      5.16 seconds
TestRunner_mgmt-tester        PASS      104.89 seconds
TestRunner_rfcomm-tester      PASS      8.49 seconds
TestRunner_sco-tester         PASS      7.94 seconds
TestRunner_ioctl-tester       PASS      8.92 seconds
TestRunner_mesh-tester        PASS      6.60 seconds
TestRunner_smp-tester         PASS      7.67 seconds
TestRunner_userchan-tester    PASS      5.43 seconds
IncrementalBuild              PASS      25.51 seconds



---
Regards,
Linux Bluetooth
Luiz Augusto von Dentz Feb. 3, 2023, 9:08 p.m. UTC | #2
Hi Kiran, Seema,

On Fri, Feb 3, 2023 at 1:28 AM Kiran K <kiran.k@intel.com> wrote:
>
> From: Seema Sreemantha <seema.sreemantha@intel.com>
>
> Antenna gain is defined as the antenna’s ability to
> increase the Tx power in a given direction. Intel
> is certifying its products with fixed reference
> antenna peak gain values (3/5dBi). The feature takes
> into account the actual antenna gain, and increases
> output power values, which results in a performance
> improvement.
>
> After firmware download is completed, driver reads from
> ACPI table and configures PPAG as required. ACPI table
> entry for PPAG is defined as below.
>
> Name (PPAG, Package (0x02)
> {
>     0x00000001,
>     Package (0x02)
>     {
>         0x00000012, /* Bluetooth Domain */
>         0x00000001  /* 1 - Enable PPAG, 0 - Disable PPAG */
>     }
> })
>
> btmon log:
>
> < HCI Command: Vendor (0x3f|0x0219) plen 12
>     00 00 00 00 00 00 00 00 00 00 00 00
> > HCI Event: Command Complete (0x0e) plen 4
>     Vendor (0x3f|0x0219) ncmd 1
>     Status: Success (0x00)

It would have been better if you guys had added a decoder for it.

> Signed-off-by: Kiran K <kiran.k@intel.com>
> Signed-off-by: Seema Sreemantha <seema.sreemantha@intel.com>
> ---
>  drivers/bluetooth/btintel.c | 114 ++++++++++++++++++++++++++++++++++++
>  drivers/bluetooth/btintel.h |  13 ++++
>  2 files changed, 127 insertions(+)
>
> diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c
> index d4e2cb9a4eb4..4d6d0dc10caa 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;

You can probably just assign ppag = data above;

> +       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 = ((struct btintel_ppag *)data)->hdev;

Then use hdev = ppga->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,56 @@ static int btintel_configure_offload(struct hci_dev *hdev)
>         return err;
>  }
>
> +static int 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 0;
> +       }
> +
> +       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)) {
> +               bt_dev_warn(hdev, "PPAG: ACPI Failure: %s", acpi_format_exception(status));

Shouldn't we consider it ok if the ACPI doesn't have an entry? I mean
it should be possible to plug a new controller in an old laptop that
possibly doesn't have this entry, right?

> +               return -1;
> +       }
> +
> +       if (ppag.domain != 0x12) {
> +               bt_dev_warn(hdev, "PPAG-BT Domain disabled");
> +               return -1;
> +       }
> +
> +       /* PPAG mode, BIT0 = 0 Disabled, BIT0 = 1 Enabled */
> +       if (!(ppag.mode & BIT(0))) {
> +               bt_dev_dbg(hdev, "PPAG disabled");
> +               return 0;
> +       }
> +
> +       ppag_cmd.mcc = 0;
> +       ppag_cmd.sel = 0; /* 0 - Operational, 1 - Disable, 2 - Testing mode */
> +       ppag_cmd.delta = 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 PTR_ERR(skb);
> +       }
> +       kfree_skb(skb);
> +       return 0;
> +}
> +
>  static int btintel_bootloader_setup_tlv(struct hci_dev *hdev,
>                                         struct intel_version_tlv *ver)
>  {
> @@ -2297,6 +2408,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);

Looks like you are not using the return above which I guess is fine
since this step can be considered optional but I'd document it as such
and probably make btintel_set_ppag return void as it shouldn't be
considered as an error if it fails.

> +
>         /* Read the Intel version information after loading the FW  */
>         err = btintel_read_version_tlv(hdev, &new_ver);
>         if (err)
> diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h
> index e0060e58573c..af508a0e0c42 100644
> --- a/drivers/bluetooth/btintel.h
> +++ b/drivers/bluetooth/btintel.h
> @@ -137,6 +137,19 @@ struct intel_offload_use_cases {
>         __u8    preset[8];
>  } __packed;
>
> +/* structure to store the PPAG data read from ACPI table */
> +struct btintel_ppag {
> +       u32     domain;
> +       u32     mode;
> +       struct hci_dev *hdev;
> +};
> +
> +struct btintel_loc_aware_reg {
> +       u32 mcc;
> +       u32 sel;
> +       s32 delta;
> +};

The above struct seems to be used as command data, in which case it
should be __packed, also they shall probably use le32 since there is
no guarantee we would be running in a system with the same byte order
as the controller.

> +
>  #define INTEL_HW_PLATFORM(cnvx_bt)     ((u8)(((cnvx_bt) & 0x0000ff00) >> 8))
>  #define INTEL_HW_VARIANT(cnvx_bt)      ((u8)(((cnvx_bt) & 0x003f0000) >> 16))
>  #define INTEL_CNVX_TOP_TYPE(cnvx_top)  ((cnvx_top) & 0x00000fff)
> --
> 2.17.1
>
K, Kiran Feb. 6, 2023, 6:09 a.m. UTC | #3
Hi Luiz,

> -----Original Message-----
> From: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
> Sent: Saturday, February 4, 2023 2:38 AM
> To: K, Kiran <kiran.k@intel.com>
> Cc: linux-bluetooth@vger.kernel.org; Srivatsa, Ravishankar
> <ravishankar.srivatsa@intel.com>; Singh, Lokendra
> <lokendra.singh@intel.com>; Tumkur Narayan, Chethan
> <chethan.tumkur.narayan@intel.com>; Sreemantha, Seema
> <seema.sreemantha@intel.com>
> Subject: Re: [PATCH v1] Bluetooth: btintel: Set Per Platform Antenna
> Gain(PPAG)
> 
> Hi Kiran, Seema,
> 
> On Fri, Feb 3, 2023 at 1:28 AM Kiran K <kiran.k@intel.com> wrote:
> >
> > From: Seema Sreemantha <seema.sreemantha@intel.com>
> >
> > Antenna gain is defined as the antenna’s ability to increase the Tx
> > power in a given direction. Intel is certifying its products with
> > fixed reference antenna peak gain values (3/5dBi). The feature takes
> > into account the actual antenna gain, and increases output power
> > values, which results in a performance improvement.
> >
> > After firmware download is completed, driver reads from ACPI table and
> > configures PPAG as required. ACPI table entry for PPAG is defined as
> > below.
> >
> > Name (PPAG, Package (0x02)
> > {
> >     0x00000001,
> >     Package (0x02)
> >     {
> >         0x00000012, /* Bluetooth Domain */
> >         0x00000001  /* 1 - Enable PPAG, 0 - Disable PPAG */
> >     }
> > })
> >
> > btmon log:
> >
> > < HCI Command: Vendor (0x3f|0x0219) plen 12
> >     00 00 00 00 00 00 00 00 00 00 00 00
> > > HCI Event: Command Complete (0x0e) plen 4
> >     Vendor (0x3f|0x0219) ncmd 1
> >     Status: Success (0x00)
> 
> It would have been better if you guys had added a decoder for it.

Ack. I will publish the patch for the same. 
> 
> > Signed-off-by: Kiran K <kiran.k@intel.com>
> > Signed-off-by: Seema Sreemantha <seema.sreemantha@intel.com>
> > ---
> >  drivers/bluetooth/btintel.c | 114
> > ++++++++++++++++++++++++++++++++++++
> >  drivers/bluetooth/btintel.h |  13 ++++
> >  2 files changed, 127 insertions(+)
> >
> > diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c
> > index d4e2cb9a4eb4..4d6d0dc10caa 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;
> 
> You can probably just assign ppag = data above;
> 
> > +       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 = ((struct btintel_ppag *)data)->hdev;
> 
> Then use hdev = ppga->hdev
> 
Ack.

> > +
> > +       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,56 @@ static int
> > btintel_configure_offload(struct hci_dev *hdev)
> >         return err;
> >  }
> >
> > +static int 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 0;
> > +       }
> > +
> > +       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)) {
> > +               bt_dev_warn(hdev, "PPAG: ACPI Failure: %s",
> > + acpi_format_exception(status));
> 
> Shouldn't we consider it ok if the ACPI doesn't have an entry? I mean it
> should be possible to plug a new controller in an old laptop that possibly
> doesn't have this entry, right?
> 
Ack. Older platform won't support this feature.
> > +               return -1;
> > +       }
> > +
> > +       if (ppag.domain != 0x12) {
> > +               bt_dev_warn(hdev, "PPAG-BT Domain disabled");
> > +               return -1;
> > +       }
> > +
> > +       /* PPAG mode, BIT0 = 0 Disabled, BIT0 = 1 Enabled */
> > +       if (!(ppag.mode & BIT(0))) {
> > +               bt_dev_dbg(hdev, "PPAG disabled");
> > +               return 0;
> > +       }
> > +
> > +       ppag_cmd.mcc = 0;
> > +       ppag_cmd.sel = 0; /* 0 - Operational, 1 - Disable, 2 - Testing mode */
> > +       ppag_cmd.delta = 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 PTR_ERR(skb);
> > +       }
> > +       kfree_skb(skb);
> > +       return 0;
> > +}
> > +
> >  static int btintel_bootloader_setup_tlv(struct hci_dev *hdev,
> >                                         struct intel_version_tlv *ver)
> > { @@ -2297,6 +2408,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);
> 
> Looks like you are not using the return above which I guess is fine since this
> step can be considered optional but I'd document it as such and probably
> make btintel_set_ppag return void as it shouldn't be considered as an error if
> it fails.
Ack. 
> 
> > +
....

Thanks,
Kiran
diff mbox series

Patch

diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c
index d4e2cb9a4eb4..4d6d0dc10caa 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;
+	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 = ((struct btintel_ppag *)data)->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,56 @@  static int btintel_configure_offload(struct hci_dev *hdev)
 	return err;
 }
 
+static int 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 0;
+	}
+
+	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)) {
+		bt_dev_warn(hdev, "PPAG: ACPI Failure: %s", acpi_format_exception(status));
+		return -1;
+	}
+
+	if (ppag.domain != 0x12) {
+		bt_dev_warn(hdev, "PPAG-BT Domain disabled");
+		return -1;
+	}
+
+	/* PPAG mode, BIT0 = 0 Disabled, BIT0 = 1 Enabled */
+	if (!(ppag.mode & BIT(0))) {
+		bt_dev_dbg(hdev, "PPAG disabled");
+		return 0;
+	}
+
+	ppag_cmd.mcc = 0;
+	ppag_cmd.sel = 0; /* 0 - Operational, 1 - Disable, 2 - Testing mode */
+	ppag_cmd.delta = 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 PTR_ERR(skb);
+	}
+	kfree_skb(skb);
+	return 0;
+}
+
 static int btintel_bootloader_setup_tlv(struct hci_dev *hdev,
 					struct intel_version_tlv *ver)
 {
@@ -2297,6 +2408,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)
diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h
index e0060e58573c..af508a0e0c42 100644
--- a/drivers/bluetooth/btintel.h
+++ b/drivers/bluetooth/btintel.h
@@ -137,6 +137,19 @@  struct intel_offload_use_cases {
 	__u8	preset[8];
 } __packed;
 
+/* structure to store the PPAG data read from ACPI table */
+struct btintel_ppag {
+	u32	domain;
+	u32     mode;
+	struct hci_dev *hdev;
+};
+
+struct btintel_loc_aware_reg {
+	u32 mcc;
+	u32 sel;
+	s32 delta;
+};
+
 #define INTEL_HW_PLATFORM(cnvx_bt)	((u8)(((cnvx_bt) & 0x0000ff00) >> 8))
 #define INTEL_HW_VARIANT(cnvx_bt)	((u8)(((cnvx_bt) & 0x003f0000) >> 16))
 #define INTEL_CNVX_TOP_TYPE(cnvx_top)	((cnvx_top) & 0x00000fff)