|
9 | 9 | #include <linux/module.h>
|
10 | 10 | #include <linux/firmware.h>
|
11 | 11 | #include <linux/regmap.h>
|
| 12 | +#include <linux/acpi.h> |
12 | 13 | #include <asm/unaligned.h>
|
13 | 14 |
|
14 | 15 | #include <net/bluetooth/bluetooth.h>
|
|
24 | 25 | #define ECDSA_OFFSET 644
|
25 | 26 | #define ECDSA_HEADER_LEN 320
|
26 | 27 |
|
| 28 | +#define BTINTEL_PPAG_NAME "PPAG" |
| 29 | +#define BTINTEL_PPAG_PREFIX "\\_SB_.PCI0.XHCI.RHUB" |
| 30 | + |
27 | 31 | #define CMD_WRITE_BOOT_PARAMS 0xfc0e
|
28 | 32 | struct cmd_write_boot_params {
|
29 | 33 | __le32 boot_addr;
|
@@ -1278,6 +1282,63 @@ static int btintel_read_debug_features(struct hci_dev *hdev,
|
1278 | 1282 | return 0;
|
1279 | 1283 | }
|
1280 | 1284 |
|
| 1285 | +static acpi_status btintel_ppag_callback(acpi_handle handle, u32 lvl, void *data, |
| 1286 | + void **ret) |
| 1287 | +{ |
| 1288 | + acpi_status status; |
| 1289 | + size_t len; |
| 1290 | + struct btintel_ppag *ppag = data; |
| 1291 | + union acpi_object *p, *elements; |
| 1292 | + struct acpi_buffer string = {ACPI_ALLOCATE_BUFFER, NULL}; |
| 1293 | + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; |
| 1294 | + struct hci_dev *hdev = ppag->hdev; |
| 1295 | + |
| 1296 | + status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &string); |
| 1297 | + if (ACPI_FAILURE(status)) { |
| 1298 | + bt_dev_warn(hdev, "ACPI Failure: %s", acpi_format_exception(status)); |
| 1299 | + return status; |
| 1300 | + } |
| 1301 | + |
| 1302 | + if (strncmp(BTINTEL_PPAG_PREFIX, string.pointer, |
| 1303 | + strlen(BTINTEL_PPAG_PREFIX))) { |
| 1304 | + kfree(string.pointer); |
| 1305 | + return AE_OK; |
| 1306 | + } |
| 1307 | + |
| 1308 | + len = strlen(string.pointer); |
| 1309 | + if (strncmp((char *)string.pointer + len - 4, BTINTEL_PPAG_NAME, 4)) { |
| 1310 | + kfree(string.pointer); |
| 1311 | + return AE_OK; |
| 1312 | + } |
| 1313 | + kfree(string.pointer); |
| 1314 | + |
| 1315 | + status = acpi_evaluate_object(handle, NULL, NULL, &buffer); |
| 1316 | + if (ACPI_FAILURE(status)) { |
| 1317 | + bt_dev_warn(hdev, "ACPI Failure: %s", acpi_format_exception(status)); |
| 1318 | + return status; |
| 1319 | + } |
| 1320 | + |
| 1321 | + p = buffer.pointer; |
| 1322 | + ppag = (struct btintel_ppag *)data; |
| 1323 | + |
| 1324 | + if (p->type != ACPI_TYPE_PACKAGE || p->package.count != 2) { |
| 1325 | + kfree(buffer.pointer); |
| 1326 | + bt_dev_warn(hdev, "Invalid object type: %d or package count: %d", |
| 1327 | + p->type, p->package.count); |
| 1328 | + return AE_ERROR; |
| 1329 | + } |
| 1330 | + |
| 1331 | + elements = p->package.elements; |
| 1332 | + |
| 1333 | + /* PPAG table is located at element[1] */ |
| 1334 | + p = &elements[1]; |
| 1335 | + |
| 1336 | + ppag->domain = (u32)p->package.elements[0].integer.value; |
| 1337 | + ppag->mode = (u32)p->package.elements[1].integer.value; |
| 1338 | + kfree(buffer.pointer); |
| 1339 | + return AE_CTRL_TERMINATE; |
| 1340 | +} |
| 1341 | + |
1281 | 1342 | static int btintel_set_debug_features(struct hci_dev *hdev,
|
1282 | 1343 | const struct intel_debug_features *features)
|
1283 | 1344 | {
|
@@ -2251,6 +2312,58 @@ static int btintel_configure_offload(struct hci_dev *hdev)
|
2251 | 2312 | return err;
|
2252 | 2313 | }
|
2253 | 2314 |
|
| 2315 | +static void btintel_set_ppag(struct hci_dev *hdev, struct intel_version_tlv *ver) |
| 2316 | +{ |
| 2317 | + acpi_status status; |
| 2318 | + struct btintel_ppag ppag; |
| 2319 | + struct sk_buff *skb; |
| 2320 | + struct btintel_loc_aware_reg ppag_cmd; |
| 2321 | + |
| 2322 | + /* PPAG is not supported if CRF is HrP2, Jfp2, JfP1 */ |
| 2323 | + switch (ver->cnvr_top & 0xFFF) { |
| 2324 | + case 0x504: /* Hrp2 */ |
| 2325 | + case 0x202: /* Jfp2 */ |
| 2326 | + case 0x201: /* Jfp1 */ |
| 2327 | + return; |
| 2328 | + } |
| 2329 | + |
| 2330 | + memset(&ppag, 0, sizeof(ppag)); |
| 2331 | + |
| 2332 | + ppag.hdev = hdev; |
| 2333 | + status = acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, |
| 2334 | + ACPI_UINT32_MAX, NULL, |
| 2335 | + btintel_ppag_callback, &ppag, NULL); |
| 2336 | + |
| 2337 | + if (ACPI_FAILURE(status)) { |
| 2338 | + /* Do not log warning message if ACPI entry is not found */ |
| 2339 | + if (status == AE_NOT_FOUND) |
| 2340 | + return; |
| 2341 | + bt_dev_warn(hdev, "PPAG: ACPI Failure: %s", acpi_format_exception(status)); |
| 2342 | + return; |
| 2343 | + } |
| 2344 | + |
| 2345 | + if (ppag.domain != 0x12) { |
| 2346 | + bt_dev_warn(hdev, "PPAG-BT Domain disabled"); |
| 2347 | + return; |
| 2348 | + } |
| 2349 | + |
| 2350 | + /* PPAG mode, BIT0 = 0 Disabled, BIT0 = 1 Enabled */ |
| 2351 | + if (!(ppag.mode & BIT(0))) { |
| 2352 | + bt_dev_dbg(hdev, "PPAG disabled"); |
| 2353 | + return; |
| 2354 | + } |
| 2355 | + |
| 2356 | + ppag_cmd.mcc = cpu_to_le32(0); |
| 2357 | + ppag_cmd.sel = cpu_to_le32(0); /* 0 - Enable , 1 - Disable, 2 - Testing mode */ |
| 2358 | + ppag_cmd.delta = cpu_to_le32(0); |
| 2359 | + skb = __hci_cmd_sync(hdev, 0xfe19, sizeof(ppag_cmd), &ppag_cmd, HCI_CMD_TIMEOUT); |
| 2360 | + if (IS_ERR(skb)) { |
| 2361 | + bt_dev_warn(hdev, "Failed to send PPAG Enable (%ld)", PTR_ERR(skb)); |
| 2362 | + return; |
| 2363 | + } |
| 2364 | + kfree_skb(skb); |
| 2365 | +} |
| 2366 | + |
2254 | 2367 | static int btintel_bootloader_setup_tlv(struct hci_dev *hdev,
|
2255 | 2368 | struct intel_version_tlv *ver)
|
2256 | 2369 | {
|
@@ -2297,6 +2410,9 @@ static int btintel_bootloader_setup_tlv(struct hci_dev *hdev,
|
2297 | 2410 |
|
2298 | 2411 | hci_dev_clear_flag(hdev, HCI_QUALITY_REPORT);
|
2299 | 2412 |
|
| 2413 | + /* Set PPAG feature */ |
| 2414 | + btintel_set_ppag(hdev, ver); |
| 2415 | + |
2300 | 2416 | /* Read the Intel version information after loading the FW */
|
2301 | 2417 | err = btintel_read_version_tlv(hdev, &new_ver);
|
2302 | 2418 | if (err)
|
|
0 commit comments