* acpi-apei: arm64: acpi: Make apei_claim_sea() synchronise with APEI's irq work ACPI: APEI: Kick the memory_failure() queue for synchronous errors mm/memory-failure: Add memory_failure_queue_kick() * acpi-pmic: ACPI / PMIC: Add i2c address for thermal control * acpi-video: ACPI: video: Use native backlight on Acer TravelMate 5735Z * acpi-dptf: ACPI: DPTF: Add battery participant driver ACPI: DPTF: Additional sysfs attributes for power participant driver
Unified · Split
| @@ -27,10 +27,12 @@ KernelVersion: v4.10 · 26 unmodified lines | |||
| 27 | Contact: linux-acpi@vger.kernel.org | 27 | Contact: linux-acpi@vger.kernel.org |
| 28 | Description: | 28 | Description: |
| 29 | (RO) Display the platform power source | 29 | (RO) Display the platform power source |
| 30 | 0x00 = DC | 30 | bits[3:0] Current power source |
| 31 | 0x01 = AC | 31 | 0x00 = DC |
| 32 | 0x02 = USB | 32 | 0x01 = AC |
| 33 | 0x03 = Wireless Charger | 33 | 0x02 = USB |
| 34 | 0x03 = Wireless Charger | ||
| 35 | bits[7:4] Power source sequence number | ||
| 34 | 36 | ||
| 35 | What: /sys/bus/platform/devices/INT3407:00/dptf_power/battery_steady_power | 37 | What: /sys/bus/platform/devices/INT3407:00/dptf_power/battery_steady_power |
| 36 | Date: Jul, 2016 | 38 | Date: Jul, 2016 |
| @@ -38,3 +40,55 @@ KernelVersion: v4.10 · 1 unmodified line | |||
| 38 | Contact: linux-acpi@vger.kernel.org | 40 | Contact: linux-acpi@vger.kernel.org |
| 39 | Description: | 41 | Description: |
| 40 | (RO) The maximum sustained power for battery in milliwatts. | 42 | (RO) The maximum sustained power for battery in milliwatts. |
| 43 | |||
| 44 | What: /sys/bus/platform/devices/INT3407:00/dptf_power/rest_of_platform_power_mw | ||
| 45 | Date: June, 2020 | ||
| 46 | KernelVersion: v5.8 | ||
| 47 | Contact: linux-acpi@vger.kernel.org | ||
| 48 | Description: | ||
| 49 | (RO) Shows the rest (outside of SoC) of worst-case platform power. | ||
| 50 | |||
| 51 | What: /sys/bus/platform/devices/INT3407:00/dptf_power/prochot_confirm | ||
| 52 | Date: June, 2020 | ||
| 53 | KernelVersion: v5.8 | ||
| 54 | Contact: linux-acpi@vger.kernel.org | ||
| 55 | Description: | ||
| 56 | (WO) Confirm embedded controller about a prochot notification. | ||
| 57 | |||
| 58 | What: /sys/bus/platform/devices/INT3532:00/dptf_battery/max_platform_power_mw | ||
| 59 | Date: June, 2020 | ||
| 60 | KernelVersion: v5.8 | ||
| 61 | Contact: linux-acpi@vger.kernel.org | ||
| 62 | Description: | ||
| 63 | (RO) The maximum platform power that can be supported by the battery in milli watts. | ||
| 64 | |||
| 65 | What: /sys/bus/platform/devices/INT3532:00/dptf_battery/max_steady_state_power_mw | ||
| 66 | Date: June, 2020 | ||
| 67 | KernelVersion: v5.8 | ||
| 68 | Contact: linux-acpi@vger.kernel.org | ||
| 69 | Description: | ||
| 70 | (RO) The maximum sustained power for battery in milli watts. | ||
| 71 | |||
| 72 | What: /sys/bus/platform/devices/INT3532:00/dptf_battery/high_freq_impedance_mohm | ||
| 73 | Date: June, 2020 | ||
| 74 | KernelVersion: v5.8 | ||
| 75 | Contact: linux-acpi@vger.kernel.org | ||
| 76 | Description: | ||
| 77 | (RO) The high frequency impedance value that can be obtained from battery | ||
| 78 | fuel gauge in milli Ohms. | ||
| 79 | |||
| 80 | What: /sys/bus/platform/devices/INT3532:00/dptf_battery/no_load_voltage_mv | ||
| 81 | Date: June, 2020 | ||
| 82 | KernelVersion: v5.8 | ||
| 83 | Contact: linux-acpi@vger.kernel.org | ||
| 84 | Description: | ||
| 85 | (RO) The no-load voltage that can be obtained from battery fuel gauge in | ||
| 86 | milli volts. | ||
| 87 | |||
| 88 | What: /sys/bus/platform/devices/INT3532:00/dptf_battery/current_discharge_capbility_ma | ||
| 89 | Date: June, 2020 | ||
| 90 | KernelVersion: v5.8 | ||
| 91 | Contact: linux-acpi@vger.kernel.org | ||
| 92 | Description: | ||
| 93 | (RO) The battery discharge current capability obtained from battery fuel gauge in | ||
| 94 | milli Amps. | ||
| @@ -19,6 +19,7 @@ · 18 unmodified lines | |||
| 19 | #include <linux/init.h> | 19 | #include <linux/init.h> |
| 20 | #include <linux/irq.h> | 20 | #include <linux/irq.h> |
| 21 | #include <linux/irqdomain.h> | 21 | #include <linux/irqdomain.h> |
| 22 | #include <linux/irq_work.h> | ||
| 22 | #include <linux/memblock.h> | 23 | #include <linux/memblock.h> |
| 23 | #include <linux/of_fdt.h> | 24 | #include <linux/of_fdt.h> |
| 24 | #include <linux/smp.h> | 25 | #include <linux/smp.h> |
| @@ -269,6 +270,7 @@ pgprot_t __acpi_get_mem_attribute(phys_addr_t addr) · 244 unmodified lines | |||
| 269 | int apei_claim_sea(struct pt_regs *regs) | 270 | int apei_claim_sea(struct pt_regs *regs) |
| 270 | { | 271 | { |
| 271 | int err = -ENOENT; | 272 | int err = -ENOENT; |
| 273 | bool return_to_irqs_enabled; | ||
| 272 | unsigned long current_flags; | 274 | unsigned long current_flags; |
| 273 | 275 | ||
| 274 | if (!IS_ENABLED(CONFIG_ACPI_APEI_GHES)) | 276 | if (!IS_ENABLED(CONFIG_ACPI_APEI_GHES)) |
| @@ -276,6 +278,12 @@ int apei_claim_sea(struct pt_regs *regs) · 1 unmodified line | |||
| 276 | 278 | ||
| 277 | current_flags = local_daif_save_flags(); | 279 | current_flags = local_daif_save_flags(); |
| 278 | 280 | ||
| 281 | /* current_flags isn't useful here as daif doesn't tell us about pNMI */ | ||
| 282 | return_to_irqs_enabled = !irqs_disabled_flags(arch_local_save_flags()); | ||
| 283 | |||
| 284 | if (regs) | ||
| 285 | return_to_irqs_enabled = interrupts_enabled(regs); | ||
| 286 | |||
| 279 | /* | 287 | /* |
| 280 | * SEA can interrupt SError, mask it and describe this as an NMI so | 288 | * SEA can interrupt SError, mask it and describe this as an NMI so |
| 281 | * that APEI defers the handling. | 289 | * that APEI defers the handling. |
| @@ -284,6 +292,23 @@ int apei_claim_sea(struct pt_regs *regs) · 2 unmodified lines | |||
| 284 | nmi_enter(); | 292 | nmi_enter(); |
| 285 | err = ghes_notify_sea(); | 293 | err = ghes_notify_sea(); |
| 286 | nmi_exit(); | 294 | nmi_exit(); |
| 295 | |||
| 296 | /* | ||
| 297 | * APEI NMI-like notifications are deferred to irq_work. Unless | ||
| 298 | * we interrupted irqs-masked code, we can do that now. | ||
| 299 | */ | ||
| 300 | if (!err) { | ||
| 301 | if (return_to_irqs_enabled) { | ||
| 302 | local_daif_restore(DAIF_PROCCTX_NOIRQ); | ||
| 303 | __irq_enter(); | ||
| 304 | irq_work_run(); | ||
| 305 | __irq_exit(); | ||
| 306 | } else { | ||
| 307 | pr_warn_ratelimited("APEI work queued but not completed"); | ||
| 308 | err = -EINPROGRESS; | ||
| 309 | } | ||
| 310 | } | ||
| 311 | |||
| 287 | local_daif_restore(current_flags); | 312 | local_daif_restore(current_flags); |
| 288 | 313 | ||
| 289 | return err; | 314 | return err; |
| @@ -635,11 +635,13 @@ static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs) · 634 unmodified lines | |||
| 635 | 635 | ||
| 636 | inf = esr_to_fault_info(esr); | 636 | inf = esr_to_fault_info(esr); |
| 637 | 637 | ||
| 638 | /* | 638 | if (user_mode(regs) && apei_claim_sea(regs) == 0) { |
| 639 | * Return value ignored as we rely on signal merging. | 639 | /* |
| 640 | * Future patches will make this more robust. | 640 | * APEI claimed this as a firmware-first notification. |
| 641 | */ | 641 | * Some processing deferred to task_work before ret_to_user(). |
| 642 | apei_claim_sea(regs); | 642 | */ |
| 643 | return 0; | ||
| 644 | } | ||
| 643 | 645 | ||
| 644 | if (esr & ESR_ELx_FnV) | 646 | if (esr & ESR_ELx_FnV) |
| 645 | siaddr = NULL; | 647 | siaddr = NULL; |
| @@ -40,6 +40,7 @@ · 39 unmodified lines | |||
| 40 | #include <linux/sched/clock.h> | 40 | #include <linux/sched/clock.h> |
| 41 | #include <linux/uuid.h> | 41 | #include <linux/uuid.h> |
| 42 | #include <linux/ras.h> | 42 | #include <linux/ras.h> |
| 43 | #include <linux/task_work.h> | ||
| 43 | 44 | ||
| 44 | #include <acpi/actbl1.h> | 45 | #include <acpi/actbl1.h> |
| 45 | #include <acpi/ghes.h> | 46 | #include <acpi/ghes.h> |
| @@ -414,23 +415,46 @@ static void ghes_clear_estatus(struct ghes *ghes, · 368 unmodified lines | |||
| 414 | ghes_ack_error(ghes->generic_v2); | 415 | ghes_ack_error(ghes->generic_v2); |
| 415 | } | 416 | } |
| 416 | 417 | ||
| 417 | static void ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata, int sev) | 418 | /* |
| 419 | * Called as task_work before returning to user-space. | ||
| 420 | * Ensure any queued work has been done before we return to the context that | ||
| 421 | * triggered the notification. | ||
| 422 | */ | ||
| 423 | static void ghes_kick_task_work(struct callback_head *head) | ||
| 424 | { | ||
| 425 | struct acpi_hest_generic_status *estatus; | ||
| 426 | struct ghes_estatus_node *estatus_node; | ||
| 427 | u32 node_len; | ||
| 428 | |||
| 429 | estatus_node = container_of(head, struct ghes_estatus_node, task_work); | ||
| 430 | if (IS_ENABLED(CONFIG_ACPI_APEI_MEMORY_FAILURE)) | ||
| 431 | memory_failure_queue_kick(estatus_node->task_work_cpu); | ||
| 432 | |||
| 433 | estatus = GHES_ESTATUS_FROM_NODE(estatus_node); | ||
| 434 | node_len = GHES_ESTATUS_NODE_LEN(cper_estatus_len(estatus)); | ||
| 435 | gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node, node_len); | ||
| 436 | } | ||
| 437 | |||
| 438 | static bool ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata, | ||
| 439 | int sev) | ||
| 418 | { | 440 | { |
| 419 | #ifdef CONFIG_ACPI_APEI_MEMORY_FAILURE | ||
| 420 | unsigned long pfn; | 441 | unsigned long pfn; |
| 421 | int flags = -1; | 442 | int flags = -1; |
| 422 | int sec_sev = ghes_severity(gdata->error_severity); | 443 | int sec_sev = ghes_severity(gdata->error_severity); |
| 423 | struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata); | 444 | struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata); |
| 424 | 445 | ||
| 446 | if (!IS_ENABLED(CONFIG_ACPI_APEI_MEMORY_FAILURE)) | ||
| 447 | return false; | ||
| 448 | |||
| 425 | if (!(mem_err->validation_bits & CPER_MEM_VALID_PA)) | 449 | if (!(mem_err->validation_bits & CPER_MEM_VALID_PA)) |
| 426 | return; | 450 | return false; |
| 427 | 451 | ||
| 428 | pfn = mem_err->physical_addr >> PAGE_SHIFT; | 452 | pfn = mem_err->physical_addr >> PAGE_SHIFT; |
| 429 | if (!pfn_valid(pfn)) { | 453 | if (!pfn_valid(pfn)) { |
| 430 | pr_warn_ratelimited(FW_WARN GHES_PFX | 454 | pr_warn_ratelimited(FW_WARN GHES_PFX |
| 431 | "Invalid address in generic error data: %#llx\n", | 455 | "Invalid address in generic error data: %#llx\n", |
| 432 | mem_err->physical_addr); | 456 | mem_err->physical_addr); |
| 433 | return; | 457 | return false; |
| 434 | } | 458 | } |
| 435 | 459 | ||
| 436 | /* iff following two events can be handled properly by now */ | 460 | /* iff following two events can be handled properly by now */ |
| @@ -440,9 +464,12 @@ static void ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata, int · 3 unmodified lines | |||
| 440 | if (sev == GHES_SEV_RECOVERABLE && sec_sev == GHES_SEV_RECOVERABLE) | 464 | if (sev == GHES_SEV_RECOVERABLE && sec_sev == GHES_SEV_RECOVERABLE) |
| 441 | flags = 0; | 465 | flags = 0; |
| 442 | 466 | ||
| 443 | if (flags != -1) | 467 | if (flags != -1) { |
| 444 | memory_failure_queue(pfn, flags); | 468 | memory_failure_queue(pfn, flags); |
| 445 | #endif | 469 | return true; |
| 470 | } | ||
| 471 | |||
| 472 | return false; | ||
| 446 | } | 473 | } |
| 447 | 474 | ||
| 448 | /* | 475 | /* |
| @@ -490,7 +517,7 @@ static void ghes_handle_aer(struct acpi_hest_generic_data *gdata) · 41 unmodified lines | |||
| 490 | #endif | 517 | #endif |
| 491 | } | 518 | } |
| 492 | 519 | ||
| 493 | static void ghes_do_proc(struct ghes *ghes, | 520 | static bool ghes_do_proc(struct ghes *ghes, |
| 494 | const struct acpi_hest_generic_status *estatus) | 521 | const struct acpi_hest_generic_status *estatus) |
| 495 | { | 522 | { |
| 496 | int sev, sec_sev; | 523 | int sev, sec_sev; |
| @@ -498,6 +525,7 @@ static void ghes_do_proc(struct ghes *ghes, · 1 unmodified line | |||
| 498 | guid_t *sec_type; | 525 | guid_t *sec_type; |
| 499 | const guid_t *fru_id = &guid_null; | 526 | const guid_t *fru_id = &guid_null; |
| 500 | char *fru_text = ""; | 527 | char *fru_text = ""; |
| 528 | bool queued = false; | ||
| 501 | 529 | ||
| 502 | sev = ghes_severity(estatus->error_severity); | 530 | sev = ghes_severity(estatus->error_severity); |
| 503 | apei_estatus_for_each_section(estatus, gdata) { | 531 | apei_estatus_for_each_section(estatus, gdata) { |
| @@ -515,7 +543,7 @@ static void ghes_do_proc(struct ghes *ghes, · 11 unmodified lines | |||
| 515 | ghes_edac_report_mem_error(sev, mem_err); | 543 | ghes_edac_report_mem_error(sev, mem_err); |
| 516 | 544 | ||
| 517 | arch_apei_report_mem_error(sev, mem_err); | 545 | arch_apei_report_mem_error(sev, mem_err); |
| 518 | ghes_handle_memory_failure(gdata, sev); | 546 | queued = ghes_handle_memory_failure(gdata, sev); |
| 519 | } | 547 | } |
| 520 | else if (guid_equal(sec_type, &CPER_SEC_PCIE)) { | 548 | else if (guid_equal(sec_type, &CPER_SEC_PCIE)) { |
| 521 | ghes_handle_aer(gdata); | 549 | ghes_handle_aer(gdata); |
| @@ -532,6 +560,8 @@ static void ghes_do_proc(struct ghes *ghes, · 10 unmodified lines | |||
| 532 | gdata->error_data_length); | 560 | gdata->error_data_length); |
| 533 | } | 561 | } |
| 534 | } | 562 | } |
| 563 | |||
| 564 | return queued; | ||
| 535 | } | 565 | } |
| 536 | 566 | ||
| 537 | static void __ghes_print_estatus(const char *pfx, | 567 | static void __ghes_print_estatus(const char *pfx, |
| @@ -827,7 +857,9 @@ static void ghes_proc_in_irq(struct irq_work *irq_work) · 289 unmodified lines | |||
| 827 | struct ghes_estatus_node *estatus_node; | 857 | struct ghes_estatus_node *estatus_node; |
| 828 | struct acpi_hest_generic *generic; | 858 | struct acpi_hest_generic *generic; |
| 829 | struct acpi_hest_generic_status *estatus; | 859 | struct acpi_hest_generic_status *estatus; |
| 860 | bool task_work_pending; | ||
| 830 | u32 len, node_len; | 861 | u32 len, node_len; |
| 862 | int ret; | ||
| 831 | 863 | ||
| 832 | llnode = llist_del_all(&ghes_estatus_llist); | 864 | llnode = llist_del_all(&ghes_estatus_llist); |
| 833 | /* | 865 | /* |
| @@ -842,14 +874,26 @@ static void ghes_proc_in_irq(struct irq_work *irq_work) · 8 unmodified lines | |||
| 842 | estatus = GHES_ESTATUS_FROM_NODE(estatus_node); | 874 | estatus = GHES_ESTATUS_FROM_NODE(estatus_node); |
| 843 | len = cper_estatus_len(estatus); | 875 | len = cper_estatus_len(estatus); |
| 844 | node_len = GHES_ESTATUS_NODE_LEN(len); | 876 | node_len = GHES_ESTATUS_NODE_LEN(len); |
| 845 | ghes_do_proc(estatus_node->ghes, estatus); | 877 | task_work_pending = ghes_do_proc(estatus_node->ghes, estatus); |
| 846 | if (!ghes_estatus_cached(estatus)) { | 878 | if (!ghes_estatus_cached(estatus)) { |
| 847 | generic = estatus_node->generic; | 879 | generic = estatus_node->generic; |
| 848 | if (ghes_print_estatus(NULL, generic, estatus)) | 880 | if (ghes_print_estatus(NULL, generic, estatus)) |
| 849 | ghes_estatus_cache_add(generic, estatus); | 881 | ghes_estatus_cache_add(generic, estatus); |
| 850 | } | 882 | } |
| 851 | gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node, | 883 | |
| 852 | node_len); | 884 | if (task_work_pending && current->mm != &init_mm) { |
| 885 | estatus_node->task_work.func = ghes_kick_task_work; | ||
| 886 | estatus_node->task_work_cpu = smp_processor_id(); | ||
| 887 | ret = task_work_add(current, &estatus_node->task_work, | ||
| 888 | true); | ||
| 889 | if (ret) | ||
| 890 | estatus_node->task_work.func = NULL; | ||
| 891 | } | ||
| 892 | |||
| 893 | if (!estatus_node->task_work.func) | ||
| 894 | gen_pool_free(ghes_estatus_pool, | ||
| 895 | (unsigned long)estatus_node, node_len); | ||
| 896 | |||
| 853 | llnode = next; | 897 | llnode = next; |
| 854 | } | 898 | } |
| 855 | } | 899 | } |
| @@ -909,6 +953,7 @@ static int ghes_in_nmi_queue_one_entry(struct ghes *ghes, · 53 unmodified lines | |||
| 909 | 953 | ||
| 910 | estatus_node->ghes = ghes; | 954 | estatus_node->ghes = ghes; |
| 911 | estatus_node->generic = ghes->generic; | 955 | estatus_node->generic = ghes->generic; |
| 956 | estatus_node->task_work.func = NULL; | ||
| 912 | estatus = GHES_ESTATUS_FROM_NODE(estatus_node); | 957 | estatus = GHES_ESTATUS_FROM_NODE(estatus_node); |
| 913 | 958 | ||
| 914 | if (__ghes_read_estatus(estatus, buf_paddr, fixmap_idx, len)) { | 959 | if (__ghes_read_estatus(estatus, buf_paddr, fixmap_idx, len)) { |
| @@ -10,12 +10,19 @@ · 9 unmodified lines | |||
| 10 | #include <linux/platform_device.h> | 10 | #include <linux/platform_device.h> |
| 11 | 11 | ||
| 12 | /* | 12 | /* |
| 13 | * Presentation of attributes which are defined for INT3407. They are: | 13 | * Presentation of attributes which are defined for INT3407 and INT3532. |
| 14 | * They are: | ||
| 14 | * PMAX : Maximum platform powe | 15 | * PMAX : Maximum platform powe |
| 15 | * PSRC : Platform power source | 16 | * PSRC : Platform power source |
| 16 | * ARTG : Adapter rating | 17 | * ARTG : Adapter rating |
| 17 | * CTYP : Charger type | 18 | * CTYP : Charger type |
| 18 | * PBSS : Battery steady power | 19 | * PBSS : Battery steady power |
| 20 | * PROP : Rest of worst case platform Power | ||
| 21 | * PBSS : Power Battery Steady State | ||
| 22 | * PBSS : Power Battery Steady State | ||
| 23 | * RBHF : High Frequency Impedance | ||
| 24 | * VBNL : Instantaneous No-Load Voltage | ||
| 25 | * CMPP : Current Discharge Capability | ||
| 19 | */ | 26 | */ |
| 20 | #define DPTF_POWER_SHOW(name, object) \ | 27 | #define DPTF_POWER_SHOW(name, object) \ |
| 21 | static ssize_t name##_show(struct device *dev,\ | 28 | static ssize_t name##_show(struct device *dev,\ |
| @@ -39,12 +46,42 @@ DPTF_POWER_SHOW(platform_power_source, PSRC) · 17 unmodified lines | |||
| 39 | DPTF_POWER_SHOW(adapter_rating_mw, ARTG) | 46 | DPTF_POWER_SHOW(adapter_rating_mw, ARTG) |
| 40 | DPTF_POWER_SHOW(battery_steady_power_mw, PBSS) | 47 | DPTF_POWER_SHOW(battery_steady_power_mw, PBSS) |
| 41 | DPTF_POWER_SHOW(charger_type, CTYP) | 48 | DPTF_POWER_SHOW(charger_type, CTYP) |
| 49 | DPTF_POWER_SHOW(rest_of_platform_power_mw, PROP) | ||
| 50 | DPTF_POWER_SHOW(max_steady_state_power_mw, PBSS) | ||
| 51 | DPTF_POWER_SHOW(high_freq_impedance_mohm, RBHF) | ||
| 52 | DPTF_POWER_SHOW(no_load_voltage_mv, VBNL) | ||
| 53 | DPTF_POWER_SHOW(current_discharge_capbility_ma, CMPP); | ||
| 42 | 54 | ||
| 43 | static DEVICE_ATTR_RO(max_platform_power_mw); | 55 | static DEVICE_ATTR_RO(max_platform_power_mw); |
| 44 | static DEVICE_ATTR_RO(platform_power_source); | 56 | static DEVICE_ATTR_RO(platform_power_source); |
| 45 | static DEVICE_ATTR_RO(adapter_rating_mw); | 57 | static DEVICE_ATTR_RO(adapter_rating_mw); |
| 46 | static DEVICE_ATTR_RO(battery_steady_power_mw); | 58 | static DEVICE_ATTR_RO(battery_steady_power_mw); |
| 47 | static DEVICE_ATTR_RO(charger_type); | 59 | static DEVICE_ATTR_RO(charger_type); |
| 60 | static DEVICE_ATTR_RO(rest_of_platform_power_mw); | ||
| 61 | static DEVICE_ATTR_RO(max_steady_state_power_mw); | ||
| 62 | static DEVICE_ATTR_RO(high_freq_impedance_mohm); | ||
| 63 | static DEVICE_ATTR_RO(no_load_voltage_mv); | ||
| 64 | static DEVICE_ATTR_RO(current_discharge_capbility_ma); | ||
| 65 | |||
| 66 | static ssize_t prochot_confirm_store(struct device *dev, | ||
| 67 | struct device_attribute *attr, | ||
| 68 | const char *buf, size_t count) | ||
| 69 | { | ||
| 70 | struct acpi_device *acpi_dev = dev_get_drvdata(dev); | ||
| 71 | acpi_status status; | ||
| 72 | int seq_no; | ||
| 73 | |||
| 74 | if (kstrtouint(buf, 0, &seq_no) < 0) | ||
| 75 | return -EINVAL; | ||
| 76 | |||
| 77 | status = acpi_execute_simple_method(acpi_dev->handle, "PBOK", seq_no); | ||
| 78 | if (ACPI_SUCCESS(status)) | ||
| 79 | return count; | ||
| 80 | |||
| 81 | return -EINVAL; | ||
| 82 | } | ||
| 83 | |||
| 84 | static DEVICE_ATTR_WO(prochot_confirm); | ||
| 48 | 85 | ||
| 49 | static struct attribute *dptf_power_attrs[] = { | 86 | static struct attribute *dptf_power_attrs[] = { |
| 50 | &dev_attr_max_platform_power_mw.attr, | 87 | &dev_attr_max_platform_power_mw.attr, |
| @@ -52,6 +89,8 @@ static struct attribute *dptf_power_attrs[] = { · 1 unmodified line | |||
| 52 | &dev_attr_adapter_rating_mw.attr, | 89 | &dev_attr_adapter_rating_mw.attr, |
| 53 | &dev_attr_battery_steady_power_mw.attr, | 90 | &dev_attr_battery_steady_power_mw.attr, |
| 54 | &dev_attr_charger_type.attr, | 91 | &dev_attr_charger_type.attr, |
| 92 | &dev_attr_rest_of_platform_power_mw.attr, | ||
| 93 | &dev_attr_prochot_confirm.attr, | ||
| 55 | NULL | 94 | NULL |
| 56 | }; | 95 | }; |
| 57 | 96 | ||
| @@ -60,10 +99,79 @@ static const struct attribute_group dptf_power_attribute_group = { · 2 unmodified lines | |||
| 60 | .name = "dptf_power" | 99 | .name = "dptf_power" |
| 61 | }; | 100 | }; |
| 62 | 101 | ||
| 102 | static struct attribute *dptf_battery_attrs[] = { | ||
| 103 | &dev_attr_max_platform_power_mw.attr, | ||
| 104 | &dev_attr_max_steady_state_power_mw.attr, | ||
| 105 | &dev_attr_high_freq_impedance_mohm.attr, | ||
| 106 | &dev_attr_no_load_voltage_mv.attr, | ||
| 107 | &dev_attr_current_discharge_capbility_ma.attr, | ||
| 108 | NULL | ||
| 109 | }; | ||
| 110 | |||
| 111 | static const struct attribute_group dptf_battery_attribute_group = { | ||
| 112 | .attrs = dptf_battery_attrs, | ||
| 113 | .name = "dptf_battery" | ||
| 114 | }; | ||
| 115 | |||
| 116 | #define MAX_POWER_CHANGED 0x80 | ||
| 117 | #define POWER_STATE_CHANGED 0x81 | ||
| 118 | #define STEADY_STATE_POWER_CHANGED 0x83 | ||
| 119 | #define POWER_PROP_CHANGE_EVENT 0x84 | ||
| 120 | #define IMPEDANCED_CHNGED 0x85 | ||
| 121 | #define VOLTAGE_CURRENT_CHANGED 0x86 | ||
| 122 | |||
| 123 | static long long dptf_participant_type(acpi_handle handle) | ||
| 124 | { | ||
| 125 | unsigned long long ptype; | ||
| 126 | acpi_status status; | ||
| 127 | |||
| 128 | status = acpi_evaluate_integer(handle, "PTYP", NULL, &ptype); | ||
| 129 | if (ACPI_FAILURE(status)) | ||
| 130 | return -ENODEV; | ||
| 131 | |||
| 132 | return ptype; | ||
| 133 | } | ||
| 134 | |||
| 135 | static void dptf_power_notify(acpi_handle handle, u32 event, void *data) | ||
| 136 | { | ||
| 137 | struct platform_device *pdev = data; | ||
| 138 | char *attr; | ||
| 139 | |||
| 140 | switch (event) { | ||
| 141 | case POWER_STATE_CHANGED: | ||
| 142 | attr = "platform_power_source"; | ||
| 143 | break; | ||
| 144 | case POWER_PROP_CHANGE_EVENT: | ||
| 145 | attr = "rest_of_platform_power_mw"; | ||
| 146 | break; | ||
| 147 | case MAX_POWER_CHANGED: | ||
| 148 | attr = "max_platform_power_mw"; | ||
| 149 | break; | ||
| 150 | case STEADY_STATE_POWER_CHANGED: | ||
| 151 | attr = "max_steady_state_power_mw"; | ||
| 152 | break; | ||
| 153 | case VOLTAGE_CURRENT_CHANGED: | ||
| 154 | attr = "no_load_voltage_mv"; | ||
| 155 | break; | ||
| 156 | default: | ||
| 157 | dev_err(&pdev->dev, "Unsupported event [0x%x]\n", event); | ||
| 158 | return; | ||
| 159 | } | ||
| 160 | |||
| 161 | /* | ||
| 162 | * Notify that an attribute is changed, so that user space can read | ||
| 163 | * again. | ||
| 164 | */ | ||
| 165 | if (dptf_participant_type(handle) == 0x0CULL) | ||
| 166 | sysfs_notify(&pdev->dev.kobj, "dptf_battery", attr); | ||
| 167 | else | ||
| 168 | sysfs_notify(&pdev->dev.kobj, "dptf_power", attr); | ||
| 169 | } | ||
| 170 | |||
| 63 | static int dptf_power_add(struct platform_device *pdev) | 171 | static int dptf_power_add(struct platform_device *pdev) |
| 64 | { | 172 | { |
| 173 | const struct attribute_group *attr_group; | ||
| 65 | struct acpi_device *acpi_dev; | 174 | struct acpi_device *acpi_dev; |
| 66 | acpi_status status; | ||
| 67 | unsigned long long ptype; | 175 | unsigned long long ptype; |
| 68 | int result; | 176 | int result; |
| 69 | 177 | ||
| @@ -71,17 +179,29 @@ static int dptf_power_add(struct platform_device *pdev) · 1 unmodified line | |||
| 71 | if (!acpi_dev) | 179 | if (!acpi_dev) |
| 72 | return -ENODEV; | 180 | return -ENODEV; |
| 73 | 181 | ||
| 74 | status = acpi_evaluate_integer(acpi_dev->handle, "PTYP", NULL, &ptype); | 182 | ptype = dptf_participant_type(acpi_dev->handle); |
| 75 | if (ACPI_FAILURE(status)) | 183 | if (ptype == 0x11) |
| 184 | attr_group = &dptf_power_attribute_group; | ||
| 185 | else if (ptype == 0x0C) | ||
| 186 | attr_group = &dptf_battery_attribute_group; | ||
| 187 | else | ||
| 76 | return -ENODEV; | 188 | return -ENODEV; |
| 77 | 189 | ||
| 78 | if (ptype != 0x11) | 190 | result = acpi_install_notify_handler(acpi_dev->handle, |
| 79 | return -ENODEV; | 191 | ACPI_DEVICE_NOTIFY, |
| 192 | dptf_power_notify, | ||
| 193 | (void *)pdev); | ||
| 194 | if (result) | ||
| 195 | return result; | ||
| 80 | 196 | ||
| 81 | result = sysfs_create_group(&pdev->dev.kobj, | 197 | result = sysfs_create_group(&pdev->dev.kobj, |
| 82 | &dptf_power_attribute_group); | 198 | attr_group); |
| 83 | if (result) | 199 | if (result) { |
| 200 | acpi_remove_notify_handler(acpi_dev->handle, | ||
| 201 | ACPI_DEVICE_NOTIFY, | ||
| 202 | dptf_power_notify); | ||
| 84 | return result; | 203 | return result; |
| 204 | } | ||
| 85 | 205 | ||
| 86 | platform_set_drvdata(pdev, acpi_dev); | 206 | platform_set_drvdata(pdev, acpi_dev); |
| 87 | 207 | ||
| @@ -90,14 +210,23 @@ static int dptf_power_add(struct platform_device *pdev) · 2 unmodified lines | |||
| 90 | 210 | ||
| 91 | static int dptf_power_remove(struct platform_device *pdev) | 211 | static int dptf_power_remove(struct platform_device *pdev) |
| 92 | { | 212 | { |
| 213 | struct acpi_device *acpi_dev = platform_get_drvdata(pdev); | ||
| 214 | |||
| 215 | acpi_remove_notify_handler(acpi_dev->handle, | ||
| 216 | ACPI_DEVICE_NOTIFY, | ||
| 217 | dptf_power_notify); | ||
| 93 | 218 | ||
| 94 | sysfs_remove_group(&pdev->dev.kobj, &dptf_power_attribute_group); | 219 | if (dptf_participant_type(acpi_dev->handle) == 0x0CULL) |
| 220 | sysfs_remove_group(&pdev->dev.kobj, &dptf_battery_attribute_group); | ||
| 221 | else | ||
| 222 | sysfs_remove_group(&pdev->dev.kobj, &dptf_power_attribute_group); | ||
| 95 | 223 | ||
| 96 | return 0; | 224 | return 0; |
| 97 | } | 225 | } |
| 98 | 226 | ||
| 99 | static const struct acpi_device_id int3407_device_ids[] = { | 227 | static const struct acpi_device_id int3407_device_ids[] = { |
| 100 | {"INT3407", 0}, | 228 | {"INT3407", 0}, |
| 229 | {"INT3532", 0}, | ||
| 101 | {"INTC1047", 0}, | 230 | {"INTC1047", 0}, |
| 102 | {"", 0}, | 231 | {"", 0}, |
| 103 | }; | 232 | }; |
| @@ -102,6 +102,7 @@ static struct intel_pmic_opregion_data chtdc_ti_pmic_opregion_data = { · 101 unmodified lines | |||
| 102 | .power_table_count = ARRAY_SIZE(chtdc_ti_power_table), | 102 | .power_table_count = ARRAY_SIZE(chtdc_ti_power_table), |
| 103 | .thermal_table = chtdc_ti_thermal_table, | 103 | .thermal_table = chtdc_ti_thermal_table, |
| 104 | .thermal_table_count = ARRAY_SIZE(chtdc_ti_thermal_table), | 104 | .thermal_table_count = ARRAY_SIZE(chtdc_ti_thermal_table), |
| 105 | .pmic_i2c_address = 0x5e, | ||
| 105 | }; | 106 | }; |
| 106 | 107 | ||
| 107 | static int chtdc_ti_pmic_opregion_probe(struct platform_device *pdev) | 108 | static int chtdc_ti_pmic_opregion_probe(struct platform_device *pdev) |
| @@ -361,6 +361,16 @@ static const struct dmi_system_id video_detect_dmi_table[] = { · 360 unmodified lines | |||
| 361 | DMI_MATCH(DMI_BOARD_NAME, "JV50"), | 361 | DMI_MATCH(DMI_BOARD_NAME, "JV50"), |
| 362 | }, | 362 | }, |
| 363 | }, | 363 | }, |
| 364 | { | ||
| 365 | /* https://bugzilla.kernel.org/show_bug.cgi?id=207835 */ | ||
| 366 | .callback = video_detect_force_native, | ||
| 367 | .ident = "Acer TravelMate 5735Z", | ||
| 368 | .matches = { | ||
| 369 | DMI_MATCH(DMI_SYS_VENDOR, "Acer"), | ||
| 370 | DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 5735Z"), | ||
| 371 | DMI_MATCH(DMI_BOARD_NAME, "BA51_MV"), | ||
| 372 | }, | ||
| 373 | }, | ||
| 364 | 374 | ||
| 365 | /* | 375 | /* |
| 366 | * Desktops which falsely report a backlight and which our heuristics | 376 | * Desktops which falsely report a backlight and which our heuristics |
| @@ -33,6 +33,9 @@ struct ghes_estatus_node { · 32 unmodified lines | |||
| 33 | struct llist_node llnode; | 33 | struct llist_node llnode; |
| 34 | struct acpi_hest_generic *generic; | 34 | struct acpi_hest_generic *generic; |
| 35 | struct ghes *ghes; | 35 | struct ghes *ghes; |
| 36 | |||
| 37 | int task_work_cpu; | ||
| 38 | struct callback_head task_work; | ||
| 36 | }; | 39 | }; |
| 37 | 40 | ||
| 38 | struct ghes_estatus_cache { | 41 | struct ghes_estatus_cache { |
| @@ -3012,6 +3012,7 @@ enum mf_flags { · 3011 unmodified lines | |||
| 3012 | }; | 3012 | }; |
| 3013 | extern int memory_failure(unsigned long pfn, int flags); | 3013 | extern int memory_failure(unsigned long pfn, int flags); |
| 3014 | extern void memory_failure_queue(unsigned long pfn, int flags); | 3014 | extern void memory_failure_queue(unsigned long pfn, int flags); |
| 3015 | extern void memory_failure_queue_kick(int cpu); | ||
| 3015 | extern int unpoison_memory(unsigned long pfn); | 3016 | extern int unpoison_memory(unsigned long pfn); |
| 3016 | extern int get_hwpoison_page(struct page *page); | 3017 | extern int get_hwpoison_page(struct page *page); |
| 3017 | #define put_hwpoison_page(page) put_page(page) | 3018 | #define put_hwpoison_page(page) put_page(page) |
| @@ -1493,7 +1493,7 @@ static void memory_failure_work_func(struct work_struct *work) · 1492 unmodified lines | |||
| 1493 | unsigned long proc_flags; | 1493 | unsigned long proc_flags; |
| 1494 | int gotten; | 1494 | int gotten; |
| 1495 | 1495 | ||
| 1496 | mf_cpu = this_cpu_ptr(&memory_failure_cpu); | 1496 | mf_cpu = container_of(work, struct memory_failure_cpu, work); |
| 1497 | for (;;) { | 1497 | for (;;) { |
| 1498 | spin_lock_irqsave(&mf_cpu->lock, proc_flags); | 1498 | spin_lock_irqsave(&mf_cpu->lock, proc_flags); |
| 1499 | gotten = kfifo_get(&mf_cpu->fifo, &entry); | 1499 | gotten = kfifo_get(&mf_cpu->fifo, &entry); |
| @@ -1507,6 +1507,19 @@ static void memory_failure_work_func(struct work_struct *work) · 7 unmodified lines | |||
| 1507 | } | 1507 | } |
| 1508 | } | 1508 | } |
| 1509 | 1509 | ||
| 1510 | /* | ||
| 1511 | * Process memory_failure work queued on the specified CPU. | ||
| 1512 | * Used to avoid return-to-userspace racing with the memory_failure workqueue. | ||
| 1513 | */ | ||
| 1514 | void memory_failure_queue_kick(int cpu) | ||
| 1515 | { | ||
| 1516 | struct memory_failure_cpu *mf_cpu; | ||
| 1517 | |||
| 1518 | mf_cpu = &per_cpu(memory_failure_cpu, cpu); | ||
| 1519 | cancel_work_sync(&mf_cpu->work); | ||
| 1520 | memory_failure_work_func(&mf_cpu->work); | ||
| 1521 | } | ||
| 1522 | |||
| 1510 | static int __init memory_failure_init(void) | 1523 | static int __init memory_failure_init(void) |
| 1511 | { | 1524 | { |
| 1512 | struct memory_failure_cpu *mf_cpu; | 1525 | struct memory_failure_cpu *mf_cpu; |