* [PATCH v1] Bluetooth: btintel: Add UEFI BRDS and BPMO for PeP
@ 2026-10-30 11:50 Catherine L
2026-10-30 16:43 ` [v1] " bluez.test.bot
0 siblings, 1 reply; 2+ messages in thread
From: Catherine L @ 2026-10-30 11:50 UTC (permalink / raw)
To: linux-bluetooth; +Cc: kiran.k, chethan.tumkur.narayan, Catherine L
DCR-3475 adds per-modulation SAR offsets for PeP platforms. PeP
platforms expose BRDS and BPMO data through UEFI variables instead of
the legacy ACPI BRDS path.
Add UEFI-based SAR initialization for supported CNVi/CNVr combinations:
- SCP2, SCP2_C0, or SCP2F CNVi with PeP CNVr 0xA30
- Exclude WHP2 CNVr 0xA10, even when UEFI variables are present
- Read UefiCnvBtBRDS and write SarBrdsLimit2G4 DDC 0x0311
- Read UefiCnvBtBPMO and write BPMO DDC 0x0359, 0x035A, and 0x035B
- Preserve fixed UEFI structures, including header and mode fields
- Handle unsupported and oversized future UEFI revisions gracefully
- Send HCI_Intel_SarInitializeState_CMD (0xFE25) after SAR DDC writes
BRDS and BPMO are independent. Missing or disabled BIOS data is
non-fatal, and firmware proceeds with default SAR values. The existing
ACPI BRDS/SAR path, including mutual SAR and its 0xFE25 completion
command, remains unchanged. No ACPI BPMO path is added.
Initial UEFI validation passed 6/6 scenarios on a PEP STC card using
FW 255-255.255. Compatibility validation confirmed that platforms
without UEFI variables remain operational.
[RELEASEINFO]
program=all
ticket=jira:BT-479365
component=ibtpci
hw=all
sdl=na
test=notrelevant
justification=valid: UEFI BRDS/BPMO handling implemented for PeP CNVr 0xA30
Signed-off-by: Catherine L <catherine.l@intel.com>
---
drivers/bluetooth/btintel.c | 266 ++++++++++++++++++++++++++++++++++--
drivers/bluetooth/btintel.h | 29 ++++
2 files changed, 284 insertions(+), 11 deletions(-)
diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c
index b9bb6980bada..017681c7e3ce 100644
--- a/drivers/bluetooth/btintel.c
+++ b/drivers/bluetooth/btintel.c
@@ -28,8 +28,14 @@
#define ECDSA_OFFSET 644
#define ECDSA_HEADER_LEN 320
+#define BTINTEL_EFI_BPMO L"UefiCnvBtBPMO"
+#define BTINTEL_EFI_BRDS L"UefiCnvBtBRDS"
#define BTINTEL_EFI_DSBR L"UefiCnvCommonDSBR"
+static efi_guid_t btintel_efi_sar_guid =
+ EFI_GUID(0x42780dd5, 0x9a7d, 0x404c,
+ 0x80, 0xe4, 0x7f, 0x70, 0x94, 0x36, 0x03, 0x94);
+
enum {
DSM_SET_WDISABLE2_DELAY = 1,
DSM_SET_RESET_METHOD = 3,
@@ -3092,6 +3098,7 @@ static union acpi_object *btintel_acpi_get_bt_pkg(union acpi_object *buffer)
}
return ERR_PTR(-ENOENT);
}
+#endif /* CONFIG_ACPI */
static int btintel_send_sar_ddc(struct hci_dev *hdev, struct btintel_cp_ddc_write *data, u8 len)
{
@@ -3107,6 +3114,7 @@ static int btintel_send_sar_ddc(struct hci_dev *hdev, struct btintel_cp_ddc_writ
return 0;
}
+#ifdef CONFIG_ACPI
static int btintel_send_edr(struct hci_dev *hdev, struct btintel_cp_ddc_write *cmd,
int id, struct btintel_sar_inc_pwr *sar)
{
@@ -3162,7 +3170,153 @@ static int btintel_send_edr3(struct hci_dev *hdev, struct btintel_cp_ddc_write *
cmd->data[0] = sar->edr3;
return btintel_send_sar_ddc(hdev, cmd, 4);
}
+#endif /* CONFIG_ACPI */
+
+/* btintel_send_sar_complete - send HCI_Intel_SarInitializeState_CMD (0xFE25)
+ *
+ * Sent once after all SAR DDC writes are complete.
+ */
+static int btintel_send_sar_complete(struct hci_dev *hdev)
+{
+ struct sk_buff *skb;
+ bool enable = true;
+
+ skb = __hci_cmd_sync(hdev, 0xfe25, 1, &enable, HCI_CMD_TIMEOUT);
+ if (IS_ERR(skb)) {
+ bt_dev_err(hdev, "BT_SAR: failed to send SAR initialization complete (%ld)",
+ PTR_ERR(skb));
+ return PTR_ERR(skb);
+ }
+ kfree_skb(skb);
+ return 0;
+}
+
+/* btintel_write_bpmo_ddc - write BPMO DDC constants to firmware
+ *
+ * Writes DDC_ID_SAR_BPMO_ENABLE (1 byte), DDC_ID_SAR_BPMO_OFFSET_A (16 bytes)
+ * and DDC_ID_SAR_BPMO_OFFSET_B (16 bytes) via HCI WriteDdcConst (0xfc8b).
+ * Values are passed through unchanged from the BIOS UEFI variable; no
+ * arithmetic is performed by the driver (HLD DCR-3475 §5.1 step 2).
+ */
+static int btintel_write_bpmo_ddc(struct hci_dev *hdev,
+ const struct btintel_bpmo *bpmo)
+{
+ u8 buffer[sizeof(struct btintel_cp_ddc_write) + BTINTEL_BPMO_NUM_MODULATIONS];
+ struct btintel_cp_ddc_write *cmd = (void *)buffer;
+ int ret;
+
+ /* Enable flag (1 byte) — SarBpmoEnable (0x0359) */
+ cmd->len = 3;
+ cmd->id = cpu_to_le16(BTINTEL_DDC_ID_SAR_BPMO_ENABLE);
+ cmd->data[0] = !!(le32_to_cpu(bpmo->mode) & BIT(0));
+ ret = btintel_send_sar_ddc(hdev, cmd, 4);
+ if (ret) {
+ bt_dev_err(hdev, "BT_BPMO: failed to write DDC enable (0x%04x): %d",
+ BTINTEL_DDC_ID_SAR_BPMO_ENABLE, ret);
+ return ret;
+ }
+ /* Chain A per-modulation offsets (16 bytes) — SarBpmoChainA (0x035A) */
+ cmd->len = 2 + BTINTEL_BPMO_NUM_MODULATIONS;
+ cmd->id = cpu_to_le16(BTINTEL_DDC_ID_SAR_BPMO_CHAIN_A);
+ memcpy(cmd->data, bpmo->chain_a, BTINTEL_BPMO_NUM_MODULATIONS);
+ ret = btintel_send_sar_ddc(hdev, cmd,
+ sizeof(*cmd) + BTINTEL_BPMO_NUM_MODULATIONS);
+ if (ret) {
+ bt_dev_err(hdev, "BT_BPMO: failed to write DDC chain A (0x%04x): %d",
+ BTINTEL_DDC_ID_SAR_BPMO_CHAIN_A, ret);
+ return ret;
+ }
+ /* Chain B per-modulation offsets (16 bytes) — SarBpmoChainB (0x035B) */
+ cmd->len = 2 + BTINTEL_BPMO_NUM_MODULATIONS;
+ cmd->id = cpu_to_le16(BTINTEL_DDC_ID_SAR_BPMO_CHAIN_B);
+ memcpy(cmd->data, bpmo->chain_b, BTINTEL_BPMO_NUM_MODULATIONS);
+ ret = btintel_send_sar_ddc(hdev, cmd,
+ sizeof(*cmd) + BTINTEL_BPMO_NUM_MODULATIONS);
+ if (ret) {
+ bt_dev_err(hdev, "BT_BPMO: failed to write DDC chain B (0x%04x): %d",
+ BTINTEL_DDC_ID_SAR_BPMO_CHAIN_B, ret);
+ return ret;
+ }
+ return 0;
+}
+
+/* btintel_uefi_get_bpmo - read BT per-modulation SAR offsets from UEFI variable
+ *
+ * Reads UefiCnvBtBPMO (CNV_UEFI_VAR_BPMO, Revision 0) from the UEFI runtime
+ * variable store for the PeP/SCP2 path.
+ *
+ * Returns 0 on success; @bpmo is filled.
+ * Returns -ENOENT if the variable is absent.
+ * Returns -EOPNOTSUPP if EFI runtime is unavailable or revision unsupported.
+ * Returns -EINVAL/-EIO on structural or read errors.
+ *
+ * Layout confirmed by BIOS team (Kobi), total 37 bytes:
+ * u8 header/revision (offset 0)
+ * u32 mode (offset 1, no padding — struct is __packed)
+ * u8 chain_a[16] (offset 5)
+ * u8 chain_b[16] (offset 21)
+ */
+static int btintel_uefi_get_bpmo(struct hci_dev *hdev, struct btintel_bpmo *bpmo)
+{
+ unsigned long data_size = sizeof(*bpmo);
+ efi_status_t status;
+
+ 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_BPMO, &btintel_efi_sar_guid, NULL,
+ &data_size, bpmo);
+ if (status == EFI_NOT_FOUND)
+ return -ENOENT;
+ /* larger variable = future revision; treat as unsupported rather than error */
+ if (status == EFI_BUFFER_TOO_SMALL)
+ return -EOPNOTSUPP;
+ if (status != EFI_SUCCESS) {
+ bt_dev_warn(hdev, "BT_BPMO: UEFI variable read error (0x%lx)",
+ status);
+ return -EIO;
+ }
+
+ if (data_size < sizeof(*bpmo)) {
+ bt_dev_warn(hdev, "BT_BPMO: UEFI variable too small (%lu < %zu)",
+ data_size, sizeof(*bpmo));
+ return -EINVAL;
+ }
+
+ if (bpmo->header != 0) {
+ bt_dev_dbg(hdev, "BT_BPMO: unsupported UEFI revision %u",
+ bpmo->header);
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+/* btintel_set_bpmo - apply UEFI BT per-modulation SAR offsets to firmware */
+static int btintel_set_bpmo(struct hci_dev *hdev)
+{
+ struct btintel_bpmo bpmo;
+ int ret;
+
+ ret = btintel_uefi_get_bpmo(hdev, &bpmo);
+ if (ret == 0) {
+ if (!(le32_to_cpu(bpmo.mode) & BIT(0)))
+ return 0;
+ ret = btintel_write_bpmo_ddc(hdev, &bpmo);
+ if (ret)
+ bt_dev_err(hdev, "BT_BPMO: DDC write failed (%d)", ret);
+ return ret;
+ }
+
+ if (ret != -ENOENT && ret != -EOPNOTSUPP)
+ bt_dev_warn(hdev, "BT_BPMO: UEFI read failed (%d)", ret);
+ return -ENOENT;
+}
+
+#ifdef CONFIG_ACPI
static int btintel_set_legacy_sar(struct hci_dev *hdev, struct btintel_sar_inc_pwr *sar)
{
struct btintel_cp_ddc_write *cmd;
@@ -3202,9 +3356,7 @@ static int btintel_set_legacy_sar(struct hci_dev *hdev, struct btintel_sar_inc_p
static int btintel_set_mutual_sar(struct hci_dev *hdev, struct btintel_sar_inc_pwr *sar)
{
struct btintel_cp_ddc_write *cmd;
- struct sk_buff *skb;
u8 buffer[64];
- bool enable;
int ret;
cmd = (void *)buffer;
@@ -3249,15 +3401,7 @@ static int btintel_set_mutual_sar(struct hci_dev *hdev, struct btintel_sar_inc_p
if (ret)
return ret;
- enable = true;
- skb = __hci_cmd_sync(hdev, 0xfe25, 1, &enable, HCI_CMD_TIMEOUT);
- if (IS_ERR(skb)) {
- bt_dev_warn(hdev, "Failed to send Intel SAR Enable (%ld)", PTR_ERR(skb));
- return PTR_ERR(skb);
- }
-
- kfree_skb(skb);
- return 0;
+ return btintel_send_sar_complete(hdev);
}
/* btintel_send_sar_rev2_band - send DDC command for one Rev2 sub-band
@@ -3544,9 +3688,109 @@ static int btintel_acpi_set_sar(struct hci_dev *hdev, struct intel_version_tlv *
}
#endif /* CONFIG_ACPI */
+/* btintel_uefi_get_brds - read SAR limits from UEFI variable UefiCnvBtBRDS
+ *
+ * Primary SAR data source that expose BRDS
+ * via UEFI instead of ACPI. Uses the same GUID as UefiCnvBtBPMO.
+ *
+ * Writes BRDS DDC only — does NOT call btintel_set_bpmo() or send 0xFE25.
+ * BPMO and 0xFE25 are handled independently by the caller.
+ *
+ * CNV_UEFI_VAR_BRDS revision 2 layout (confirmed by BIOS team, 19 bytes):
+ * u8 header - CNV_UEFI_VAR_HEADER (0x02 = revision 2)
+ * u32 mode - bit 0: SAR enabled (0=disabled, 1=enabled)
+ * u32 inc_power_mode - 0=enabled, 1=disabled
+ * u8 chain_a[5] - BT_SAR_POWER_LIMITS chain A [2G4,5G2,5G8,6G1,6G3]
+ * u8 chain_b[5] - BT_SAR_POWER_LIMITS chain B [2G4,5G2,5G8,6G1,6G3]
+ */
+static int btintel_uefi_get_brds(struct hci_dev *hdev)
+{
+ struct {
+ u8 header; /* CNV_UEFI_VAR_HEADER: 0x02 = revision 2 */
+ __le32 mode; /* bit 0: 1=SAR enabled, 0=disabled */
+ __le32 inc_power_mode; /* 0=enabled, 1=disabled */
+ u8 chain_a[5]; /* sub-band limits chain A: [2G4,5G2,5G8,6G1,6G3] */
+ u8 chain_b[5]; /* sub-band limits chain B: [2G4,5G2,5G8,6G1,6G3] */
+ } __packed var;
+ unsigned long data_size = sizeof(var);
+ efi_status_t status;
+
+ 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_BRDS, &btintel_efi_sar_guid, NULL,
+ &data_size, &var);
+ if (status == EFI_NOT_FOUND)
+ return -ENOENT;
+ /* larger variable = future revision; treat as unsupported rather than error */
+ if (status == EFI_BUFFER_TOO_SMALL)
+ return -EOPNOTSUPP;
+ if (status != EFI_SUCCESS) {
+ bt_dev_warn(hdev, "BT_SAR: UEFI BRDS read error (0x%lx)", status);
+ return -EIO;
+ }
+
+ if (data_size < sizeof(var)) {
+ bt_dev_warn(hdev, "BT_SAR: UEFI BRDS too small (%lu < %zu)",
+ data_size, sizeof(var));
+ return -EINVAL;
+ }
+
+ if (var.header != 0x02) {
+ bt_dev_warn(hdev, "BT_SAR: UEFI BRDS unsupported revision 0x%02x", var.header);
+ return -EOPNOTSUPP;
+ }
+
+ if (!(le32_to_cpu(var.mode) & BIT(0))) {
+ bt_dev_dbg(hdev, "BT_SAR: UEFI BRDS SAR not enabled in BIOS");
+ return -EOPNOTSUPP;
+ }
+
+ /* BT operates at 2.4 GHz only — write SarBrdsLimit2G4 (0x0311) with chain A/B pair. */
+ {
+ u8 buf[sizeof(struct btintel_cp_ddc_write) + 2];
+ struct btintel_cp_ddc_write *cmd = (void *)buf;
+ int ret;
+
+ cmd->len = 4; /* 2 (id) + 2 (data) */
+ cmd->id = cpu_to_le16(BTINTEL_DDC_ID_SAR_BRDS_LIMIT_2G4);
+ cmd->data[0] = var.chain_a[0]; /* subBand2G4 Chain A */
+ cmd->data[1] = var.chain_b[0]; /* subBand2G4 Chain B */
+
+ ret = btintel_send_sar_ddc(hdev, cmd, sizeof(*cmd) + 2);
+ if (ret) {
+ bt_dev_warn(hdev, "BT_SAR: BRDS DDC write failed (0x%04x): %d",
+ BTINTEL_DDC_ID_SAR_BRDS_LIMIT_2G4, ret);
+ return ret;
+ }
+ }
+ return 0;
+}
+
static int btintel_set_specific_absorption_rate(struct hci_dev *hdev,
struct intel_version_tlv *ver)
{
+ u16 cnvi = ver->cnvi_top & 0xfff;
+ u16 cnvr = ver->cnvr_top & 0xfff;
+ int ret;
+
+ /* SCP2/SCP2F with PeP CNVr: UEFI path, independent of CONFIG_ACPI */
+ if ((cnvi == BTINTEL_CNVI_SCP2 || cnvi == BTINTEL_CNVI_SCP2_C0 ||
+ cnvi == BTINTEL_CNVI_SCP2F) && cnvr == BTINTEL_CNVR_PEP) {
+ ret = btintel_uefi_get_brds(hdev);
+ if (ret && ret != -ENOENT && ret != -EOPNOTSUPP)
+ bt_dev_warn(hdev, "BT_SAR: BRDS DDC failed (%d)", ret);
+
+ ret = btintel_set_bpmo(hdev);
+ if (ret && ret != -ENOENT)
+ bt_dev_warn(hdev, "BT_BPMO: failed (%d)", ret);
+
+ return btintel_send_sar_complete(hdev);
+ }
+
#ifdef CONFIG_ACPI
return btintel_acpi_set_sar(hdev, ver);
#endif
diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h
index d5e125ec5168..338a4beed5cc 100644
--- a/drivers/bluetooth/btintel.h
+++ b/drivers/bluetooth/btintel.h
@@ -71,6 +71,7 @@ struct intel_tlv {
/* CNVR */
#define BTINTEL_CNVR_FMP2 0x910
#define BTINTEL_CNVR_WHP2 0xA10 /* Whale Peak2 - Panther Lake */
+#define BTINTEL_CNVR_PEP 0xA30 /* PeP2 / PeP STC */
#define BTINTEL_IMG_BOOTLOADER 0x01 /* Bootloader image */
#define BTINTEL_IMG_IML 0x02 /* Intermediate image */
@@ -222,6 +223,34 @@ struct btintel_sar_inc_pwr {
u8 le_lr;
};
+/* DCR-3475: BT SAR Per Modulation Offset (BPMO)
+ *
+ * BPMO_NUM_MODULATIONS = 16 (indices 0-12 active, 13-15 reserved).
+ */
+#define BTINTEL_BPMO_NUM_MODULATIONS 16
+
+/* BPMO UEFI variable (Revision 0) layout:
+ * [0] Header/revision (0 = Revision 0)
+ * [1..4] Mode (0 = disabled, 1 = enabled, little-endian u32)
+ * [5..20] Chain A per-modulation offsets (u8, format xxxxx.yyy)
+ * [21..36] Chain B per-modulation offsets (u8, format xxxxx.yyy)
+ * Index-to-modulation mapping (sc_sar.h scSar_bpmoModulation_en):
+ * 0=BR, 1=EDR2M, 2=EDR3M, 3=LE1M, 4=LE2M, 5=CS, 6=LRS2, 7=LRS8,
+ * 8=HDT2, 9=HDT3, 10=HDT4, 11=HDT6, 12=HDT7P5, 13-15=reserved
+ */
+struct btintel_bpmo {
+ u8 header;
+ __le32 mode;
+ u8 chain_a[BTINTEL_BPMO_NUM_MODULATIONS];
+ u8 chain_b[BTINTEL_BPMO_NUM_MODULATIONS];
+} __packed;
+
+#define BTINTEL_DDC_ID_SAR_BRDS_LIMIT_2G4 0x0311
+
+#define BTINTEL_DDC_ID_SAR_BPMO_ENABLE 0x0359
+#define BTINTEL_DDC_ID_SAR_BPMO_CHAIN_A 0x035A
+#define BTINTEL_DDC_ID_SAR_BPMO_CHAIN_B 0x035B
+
/* Bluetooth SAR feature (BRDS), Revision 2 - per-chain sub-band power limits */
struct btintel_sar_band_limits {
u8 subband_2g4;
--
2.43.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
* RE: [v1] Bluetooth: btintel: Add UEFI BRDS and BPMO for PeP
2026-10-30 11:50 [PATCH v1] Bluetooth: btintel: Add UEFI BRDS and BPMO for PeP Catherine L
@ 2026-10-30 16:43 ` bluez.test.bot
0 siblings, 0 replies; 2+ messages in thread
From: bluez.test.bot @ 2026-10-30 16:43 UTC (permalink / raw)
To: linux-bluetooth, catherine.l
[-- Attachment #1: Type: text/plain, Size: 1020 bytes --]
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/series/1197122/
---Test result---
Test Summary:
CheckPatch PASS 1.06 seconds
VerifyFixes PASS 0.07 seconds
VerifySignedoff PASS 0.07 seconds
GitLint PASS 0.23 seconds
SubjectPrefix PASS 0.06 seconds
BuildKernel PASS 39.89 seconds
CheckAllWarning PASS 36.52 seconds
CheckSparse PASS 39.57 seconds
BuildKernel32 PASS 32.24 seconds
CheckKernelLLVM PASS 37.42 seconds
TestRunnerSetup PASS 753.35 seconds
IncrementalBuild PASS 30.05 seconds
https://github.com/bluez/bluetooth-next/pull/1029
---
Regards,
Linux Bluetooth
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-10-30 16:43 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-30 11:50 [PATCH v1] Bluetooth: btintel: Add UEFI BRDS and BPMO for PeP Catherine L
2026-10-30 16:43 ` [v1] " bluez.test.bot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.