Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branches 'acpi-apei', 'acpi-pmic', 'acpi-video' and 'acpi-dptf'

* 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

+314 -31
+58 -4
Documentation/ABI/testing/sysfs-platform-dptf
··· 27 27 Contact: linux-acpi@vger.kernel.org 28 28 Description: 29 29 (RO) Display the platform power source 30 - 0x00 = DC 31 - 0x01 = AC 32 - 0x02 = USB 33 - 0x03 = Wireless Charger 30 + bits[3:0] Current power source 31 + 0x00 = DC 32 + 0x01 = AC 33 + 0x02 = USB 34 + 0x03 = Wireless Charger 35 + bits[7:4] Power source sequence number 34 36 35 37 What: /sys/bus/platform/devices/INT3407:00/dptf_power/battery_steady_power 36 38 Date: Jul, 2016 ··· 40 38 Contact: linux-acpi@vger.kernel.org 41 39 Description: 42 40 (RO) The maximum sustained power for battery in milliwatts. 41 + 42 + What: /sys/bus/platform/devices/INT3407:00/dptf_power/rest_of_platform_power_mw 43 + Date: June, 2020 44 + KernelVersion: v5.8 45 + Contact: linux-acpi@vger.kernel.org 46 + Description: 47 + (RO) Shows the rest (outside of SoC) of worst-case platform power. 48 + 49 + What: /sys/bus/platform/devices/INT3407:00/dptf_power/prochot_confirm 50 + Date: June, 2020 51 + KernelVersion: v5.8 52 + Contact: linux-acpi@vger.kernel.org 53 + Description: 54 + (WO) Confirm embedded controller about a prochot notification. 55 + 56 + What: /sys/bus/platform/devices/INT3532:00/dptf_battery/max_platform_power_mw 57 + Date: June, 2020 58 + KernelVersion: v5.8 59 + Contact: linux-acpi@vger.kernel.org 60 + Description: 61 + (RO) The maximum platform power that can be supported by the battery in milli watts. 62 + 63 + What: /sys/bus/platform/devices/INT3532:00/dptf_battery/max_steady_state_power_mw 64 + Date: June, 2020 65 + KernelVersion: v5.8 66 + Contact: linux-acpi@vger.kernel.org 67 + Description: 68 + (RO) The maximum sustained power for battery in milli watts. 69 + 70 + What: /sys/bus/platform/devices/INT3532:00/dptf_battery/high_freq_impedance_mohm 71 + Date: June, 2020 72 + KernelVersion: v5.8 73 + Contact: linux-acpi@vger.kernel.org 74 + Description: 75 + (RO) The high frequency impedance value that can be obtained from battery 76 + fuel gauge in milli Ohms. 77 + 78 + What: /sys/bus/platform/devices/INT3532:00/dptf_battery/no_load_voltage_mv 79 + Date: June, 2020 80 + KernelVersion: v5.8 81 + Contact: linux-acpi@vger.kernel.org 82 + Description: 83 + (RO) The no-load voltage that can be obtained from battery fuel gauge in 84 + milli volts. 85 + 86 + What: /sys/bus/platform/devices/INT3532:00/dptf_battery/current_discharge_capbility_ma 87 + Date: June, 2020 88 + KernelVersion: v5.8 89 + Contact: linux-acpi@vger.kernel.org 90 + Description: 91 + (RO) The battery discharge current capability obtained from battery fuel gauge in 92 + milli Amps.
+25
arch/arm64/kernel/acpi.c
··· 19 19 #include <linux/init.h> 20 20 #include <linux/irq.h> 21 21 #include <linux/irqdomain.h> 22 + #include <linux/irq_work.h> 22 23 #include <linux/memblock.h> 23 24 #include <linux/of_fdt.h> 24 25 #include <linux/smp.h> ··· 270 269 int apei_claim_sea(struct pt_regs *regs) 271 270 { 272 271 int err = -ENOENT; 272 + bool return_to_irqs_enabled; 273 273 unsigned long current_flags; 274 274 275 275 if (!IS_ENABLED(CONFIG_ACPI_APEI_GHES)) 276 276 return err; 277 277 278 278 current_flags = local_daif_save_flags(); 279 + 280 + /* current_flags isn't useful here as daif doesn't tell us about pNMI */ 281 + return_to_irqs_enabled = !irqs_disabled_flags(arch_local_save_flags()); 282 + 283 + if (regs) 284 + return_to_irqs_enabled = interrupts_enabled(regs); 279 285 280 286 /* 281 287 * SEA can interrupt SError, mask it and describe this as an NMI so ··· 292 284 nmi_enter(); 293 285 err = ghes_notify_sea(); 294 286 nmi_exit(); 287 + 288 + /* 289 + * APEI NMI-like notifications are deferred to irq_work. Unless 290 + * we interrupted irqs-masked code, we can do that now. 291 + */ 292 + if (!err) { 293 + if (return_to_irqs_enabled) { 294 + local_daif_restore(DAIF_PROCCTX_NOIRQ); 295 + __irq_enter(); 296 + irq_work_run(); 297 + __irq_exit(); 298 + } else { 299 + pr_warn_ratelimited("APEI work queued but not completed"); 300 + err = -EINPROGRESS; 301 + } 302 + } 303 + 295 304 local_daif_restore(current_flags); 296 305 297 306 return err;
+7 -5
arch/arm64/mm/fault.c
··· 635 635 636 636 inf = esr_to_fault_info(esr); 637 637 638 - /* 639 - * Return value ignored as we rely on signal merging. 640 - * Future patches will make this more robust. 641 - */ 642 - apei_claim_sea(regs); 638 + if (user_mode(regs) && apei_claim_sea(regs) == 0) { 639 + /* 640 + * APEI claimed this as a firmware-first notification. 641 + * Some processing deferred to task_work before ret_to_user(). 642 + */ 643 + return 0; 644 + } 643 645 644 646 if (esr & ESR_ELx_FnV) 645 647 siaddr = NULL;
+56 -11
drivers/acpi/apei/ghes.c
··· 40 40 #include <linux/sched/clock.h> 41 41 #include <linux/uuid.h> 42 42 #include <linux/ras.h> 43 + #include <linux/task_work.h> 43 44 44 45 #include <acpi/actbl1.h> 45 46 #include <acpi/ghes.h> ··· 415 414 ghes_ack_error(ghes->generic_v2); 416 415 } 417 416 418 - static void ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata, int sev) 417 + /* 418 + * Called as task_work before returning to user-space. 419 + * Ensure any queued work has been done before we return to the context that 420 + * triggered the notification. 421 + */ 422 + static void ghes_kick_task_work(struct callback_head *head) 419 423 { 420 - #ifdef CONFIG_ACPI_APEI_MEMORY_FAILURE 424 + struct acpi_hest_generic_status *estatus; 425 + struct ghes_estatus_node *estatus_node; 426 + u32 node_len; 427 + 428 + estatus_node = container_of(head, struct ghes_estatus_node, task_work); 429 + if (IS_ENABLED(CONFIG_ACPI_APEI_MEMORY_FAILURE)) 430 + memory_failure_queue_kick(estatus_node->task_work_cpu); 431 + 432 + estatus = GHES_ESTATUS_FROM_NODE(estatus_node); 433 + node_len = GHES_ESTATUS_NODE_LEN(cper_estatus_len(estatus)); 434 + gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node, node_len); 435 + } 436 + 437 + static bool ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata, 438 + int sev) 439 + { 421 440 unsigned long pfn; 422 441 int flags = -1; 423 442 int sec_sev = ghes_severity(gdata->error_severity); 424 443 struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata); 425 444 445 + if (!IS_ENABLED(CONFIG_ACPI_APEI_MEMORY_FAILURE)) 446 + return false; 447 + 426 448 if (!(mem_err->validation_bits & CPER_MEM_VALID_PA)) 427 - return; 449 + return false; 428 450 429 451 pfn = mem_err->physical_addr >> PAGE_SHIFT; 430 452 if (!pfn_valid(pfn)) { 431 453 pr_warn_ratelimited(FW_WARN GHES_PFX 432 454 "Invalid address in generic error data: %#llx\n", 433 455 mem_err->physical_addr); 434 - return; 456 + return false; 435 457 } 436 458 437 459 /* iff following two events can be handled properly by now */ ··· 464 440 if (sev == GHES_SEV_RECOVERABLE && sec_sev == GHES_SEV_RECOVERABLE) 465 441 flags = 0; 466 442 467 - if (flags != -1) 443 + if (flags != -1) { 468 444 memory_failure_queue(pfn, flags); 469 - #endif 445 + return true; 446 + } 447 + 448 + return false; 470 449 } 471 450 472 451 /* ··· 517 490 #endif 518 491 } 519 492 520 - static void ghes_do_proc(struct ghes *ghes, 493 + static bool ghes_do_proc(struct ghes *ghes, 521 494 const struct acpi_hest_generic_status *estatus) 522 495 { 523 496 int sev, sec_sev; ··· 525 498 guid_t *sec_type; 526 499 const guid_t *fru_id = &guid_null; 527 500 char *fru_text = ""; 501 + bool queued = false; 528 502 529 503 sev = ghes_severity(estatus->error_severity); 530 504 apei_estatus_for_each_section(estatus, gdata) { ··· 543 515 ghes_edac_report_mem_error(sev, mem_err); 544 516 545 517 arch_apei_report_mem_error(sev, mem_err); 546 - ghes_handle_memory_failure(gdata, sev); 518 + queued = ghes_handle_memory_failure(gdata, sev); 547 519 } 548 520 else if (guid_equal(sec_type, &CPER_SEC_PCIE)) { 549 521 ghes_handle_aer(gdata); ··· 560 532 gdata->error_data_length); 561 533 } 562 534 } 535 + 536 + return queued; 563 537 } 564 538 565 539 static void __ghes_print_estatus(const char *pfx, ··· 857 827 struct ghes_estatus_node *estatus_node; 858 828 struct acpi_hest_generic *generic; 859 829 struct acpi_hest_generic_status *estatus; 830 + bool task_work_pending; 860 831 u32 len, node_len; 832 + int ret; 861 833 862 834 llnode = llist_del_all(&ghes_estatus_llist); 863 835 /* ··· 874 842 estatus = GHES_ESTATUS_FROM_NODE(estatus_node); 875 843 len = cper_estatus_len(estatus); 876 844 node_len = GHES_ESTATUS_NODE_LEN(len); 877 - ghes_do_proc(estatus_node->ghes, estatus); 845 + task_work_pending = ghes_do_proc(estatus_node->ghes, estatus); 878 846 if (!ghes_estatus_cached(estatus)) { 879 847 generic = estatus_node->generic; 880 848 if (ghes_print_estatus(NULL, generic, estatus)) 881 849 ghes_estatus_cache_add(generic, estatus); 882 850 } 883 - gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node, 884 - node_len); 851 + 852 + if (task_work_pending && current->mm != &init_mm) { 853 + estatus_node->task_work.func = ghes_kick_task_work; 854 + estatus_node->task_work_cpu = smp_processor_id(); 855 + ret = task_work_add(current, &estatus_node->task_work, 856 + true); 857 + if (ret) 858 + estatus_node->task_work.func = NULL; 859 + } 860 + 861 + if (!estatus_node->task_work.func) 862 + gen_pool_free(ghes_estatus_pool, 863 + (unsigned long)estatus_node, node_len); 864 + 885 865 llnode = next; 886 866 } 887 867 } ··· 953 909 954 910 estatus_node->ghes = ghes; 955 911 estatus_node->generic = ghes->generic; 912 + estatus_node->task_work.func = NULL; 956 913 estatus = GHES_ESTATUS_FROM_NODE(estatus_node); 957 914 958 915 if (__ghes_read_estatus(estatus, buf_paddr, fixmap_idx, len)) {
+139 -10
drivers/acpi/dptf/dptf_power.c
··· 10 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 15 * PMAX : Maximum platform powe 15 16 * PSRC : Platform power source 16 17 * ARTG : Adapter rating 17 18 * CTYP : Charger type 18 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 27 #define DPTF_POWER_SHOW(name, object) \ 21 28 static ssize_t name##_show(struct device *dev,\ ··· 46 39 DPTF_POWER_SHOW(adapter_rating_mw, ARTG) 47 40 DPTF_POWER_SHOW(battery_steady_power_mw, PBSS) 48 41 DPTF_POWER_SHOW(charger_type, CTYP) 42 + DPTF_POWER_SHOW(rest_of_platform_power_mw, PROP) 43 + DPTF_POWER_SHOW(max_steady_state_power_mw, PBSS) 44 + DPTF_POWER_SHOW(high_freq_impedance_mohm, RBHF) 45 + DPTF_POWER_SHOW(no_load_voltage_mv, VBNL) 46 + DPTF_POWER_SHOW(current_discharge_capbility_ma, CMPP); 49 47 50 48 static DEVICE_ATTR_RO(max_platform_power_mw); 51 49 static DEVICE_ATTR_RO(platform_power_source); 52 50 static DEVICE_ATTR_RO(adapter_rating_mw); 53 51 static DEVICE_ATTR_RO(battery_steady_power_mw); 54 52 static DEVICE_ATTR_RO(charger_type); 53 + static DEVICE_ATTR_RO(rest_of_platform_power_mw); 54 + static DEVICE_ATTR_RO(max_steady_state_power_mw); 55 + static DEVICE_ATTR_RO(high_freq_impedance_mohm); 56 + static DEVICE_ATTR_RO(no_load_voltage_mv); 57 + static DEVICE_ATTR_RO(current_discharge_capbility_ma); 58 + 59 + static ssize_t prochot_confirm_store(struct device *dev, 60 + struct device_attribute *attr, 61 + const char *buf, size_t count) 62 + { 63 + struct acpi_device *acpi_dev = dev_get_drvdata(dev); 64 + acpi_status status; 65 + int seq_no; 66 + 67 + if (kstrtouint(buf, 0, &seq_no) < 0) 68 + return -EINVAL; 69 + 70 + status = acpi_execute_simple_method(acpi_dev->handle, "PBOK", seq_no); 71 + if (ACPI_SUCCESS(status)) 72 + return count; 73 + 74 + return -EINVAL; 75 + } 76 + 77 + static DEVICE_ATTR_WO(prochot_confirm); 55 78 56 79 static struct attribute *dptf_power_attrs[] = { 57 80 &dev_attr_max_platform_power_mw.attr, ··· 89 52 &dev_attr_adapter_rating_mw.attr, 90 53 &dev_attr_battery_steady_power_mw.attr, 91 54 &dev_attr_charger_type.attr, 55 + &dev_attr_rest_of_platform_power_mw.attr, 56 + &dev_attr_prochot_confirm.attr, 92 57 NULL 93 58 }; 94 59 ··· 99 60 .name = "dptf_power" 100 61 }; 101 62 63 + static struct attribute *dptf_battery_attrs[] = { 64 + &dev_attr_max_platform_power_mw.attr, 65 + &dev_attr_max_steady_state_power_mw.attr, 66 + &dev_attr_high_freq_impedance_mohm.attr, 67 + &dev_attr_no_load_voltage_mv.attr, 68 + &dev_attr_current_discharge_capbility_ma.attr, 69 + NULL 70 + }; 71 + 72 + static const struct attribute_group dptf_battery_attribute_group = { 73 + .attrs = dptf_battery_attrs, 74 + .name = "dptf_battery" 75 + }; 76 + 77 + #define MAX_POWER_CHANGED 0x80 78 + #define POWER_STATE_CHANGED 0x81 79 + #define STEADY_STATE_POWER_CHANGED 0x83 80 + #define POWER_PROP_CHANGE_EVENT 0x84 81 + #define IMPEDANCED_CHNGED 0x85 82 + #define VOLTAGE_CURRENT_CHANGED 0x86 83 + 84 + static long long dptf_participant_type(acpi_handle handle) 85 + { 86 + unsigned long long ptype; 87 + acpi_status status; 88 + 89 + status = acpi_evaluate_integer(handle, "PTYP", NULL, &ptype); 90 + if (ACPI_FAILURE(status)) 91 + return -ENODEV; 92 + 93 + return ptype; 94 + } 95 + 96 + static void dptf_power_notify(acpi_handle handle, u32 event, void *data) 97 + { 98 + struct platform_device *pdev = data; 99 + char *attr; 100 + 101 + switch (event) { 102 + case POWER_STATE_CHANGED: 103 + attr = "platform_power_source"; 104 + break; 105 + case POWER_PROP_CHANGE_EVENT: 106 + attr = "rest_of_platform_power_mw"; 107 + break; 108 + case MAX_POWER_CHANGED: 109 + attr = "max_platform_power_mw"; 110 + break; 111 + case STEADY_STATE_POWER_CHANGED: 112 + attr = "max_steady_state_power_mw"; 113 + break; 114 + case VOLTAGE_CURRENT_CHANGED: 115 + attr = "no_load_voltage_mv"; 116 + break; 117 + default: 118 + dev_err(&pdev->dev, "Unsupported event [0x%x]\n", event); 119 + return; 120 + } 121 + 122 + /* 123 + * Notify that an attribute is changed, so that user space can read 124 + * again. 125 + */ 126 + if (dptf_participant_type(handle) == 0x0CULL) 127 + sysfs_notify(&pdev->dev.kobj, "dptf_battery", attr); 128 + else 129 + sysfs_notify(&pdev->dev.kobj, "dptf_power", attr); 130 + } 131 + 102 132 static int dptf_power_add(struct platform_device *pdev) 103 133 { 134 + const struct attribute_group *attr_group; 104 135 struct acpi_device *acpi_dev; 105 - acpi_status status; 106 136 unsigned long long ptype; 107 137 int result; 108 138 ··· 179 71 if (!acpi_dev) 180 72 return -ENODEV; 181 73 182 - status = acpi_evaluate_integer(acpi_dev->handle, "PTYP", NULL, &ptype); 183 - if (ACPI_FAILURE(status)) 74 + ptype = dptf_participant_type(acpi_dev->handle); 75 + if (ptype == 0x11) 76 + attr_group = &dptf_power_attribute_group; 77 + else if (ptype == 0x0C) 78 + attr_group = &dptf_battery_attribute_group; 79 + else 184 80 return -ENODEV; 185 81 186 - if (ptype != 0x11) 187 - return -ENODEV; 188 - 189 - result = sysfs_create_group(&pdev->dev.kobj, 190 - &dptf_power_attribute_group); 82 + result = acpi_install_notify_handler(acpi_dev->handle, 83 + ACPI_DEVICE_NOTIFY, 84 + dptf_power_notify, 85 + (void *)pdev); 191 86 if (result) 192 87 return result; 88 + 89 + result = sysfs_create_group(&pdev->dev.kobj, 90 + attr_group); 91 + if (result) { 92 + acpi_remove_notify_handler(acpi_dev->handle, 93 + ACPI_DEVICE_NOTIFY, 94 + dptf_power_notify); 95 + return result; 96 + } 193 97 194 98 platform_set_drvdata(pdev, acpi_dev); 195 99 ··· 210 90 211 91 static int dptf_power_remove(struct platform_device *pdev) 212 92 { 93 + struct acpi_device *acpi_dev = platform_get_drvdata(pdev); 213 94 214 - sysfs_remove_group(&pdev->dev.kobj, &dptf_power_attribute_group); 95 + acpi_remove_notify_handler(acpi_dev->handle, 96 + ACPI_DEVICE_NOTIFY, 97 + dptf_power_notify); 98 + 99 + if (dptf_participant_type(acpi_dev->handle) == 0x0CULL) 100 + sysfs_remove_group(&pdev->dev.kobj, &dptf_battery_attribute_group); 101 + else 102 + sysfs_remove_group(&pdev->dev.kobj, &dptf_power_attribute_group); 215 103 216 104 return 0; 217 105 } 218 106 219 107 static const struct acpi_device_id int3407_device_ids[] = { 220 108 {"INT3407", 0}, 109 + {"INT3532", 0}, 221 110 {"INTC1047", 0}, 222 111 {"", 0}, 223 112 };
+1
drivers/acpi/pmic/intel_pmic_chtdc_ti.c
··· 102 102 .power_table_count = ARRAY_SIZE(chtdc_ti_power_table), 103 103 .thermal_table = chtdc_ti_thermal_table, 104 104 .thermal_table_count = ARRAY_SIZE(chtdc_ti_thermal_table), 105 + .pmic_i2c_address = 0x5e, 105 106 }; 106 107 107 108 static int chtdc_ti_pmic_opregion_probe(struct platform_device *pdev)
+10
drivers/acpi/video_detect.c
··· 361 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 376 * Desktops which falsely report a backlight and which our heuristics
+3
include/acpi/ghes.h
··· 33 33 struct llist_node llnode; 34 34 struct acpi_hest_generic *generic; 35 35 struct ghes *ghes; 36 + 37 + int task_work_cpu; 38 + struct callback_head task_work; 36 39 }; 37 40 38 41 struct ghes_estatus_cache {
+1
include/linux/mm.h
··· 3012 3012 }; 3013 3013 extern int memory_failure(unsigned long pfn, int flags); 3014 3014 extern void memory_failure_queue(unsigned long pfn, int flags); 3015 + extern void memory_failure_queue_kick(int cpu); 3015 3016 extern int unpoison_memory(unsigned long pfn); 3016 3017 extern int get_hwpoison_page(struct page *page); 3017 3018 #define put_hwpoison_page(page) put_page(page)
+14 -1
mm/memory-failure.c
··· 1493 1493 unsigned long proc_flags; 1494 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 1497 for (;;) { 1498 1498 spin_lock_irqsave(&mf_cpu->lock, proc_flags); 1499 1499 gotten = kfifo_get(&mf_cpu->fifo, &entry); ··· 1505 1505 else 1506 1506 memory_failure(entry.pfn, entry.flags); 1507 1507 } 1508 + } 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); 1508 1521 } 1509 1522 1510 1523 static int __init memory_failure_init(void)