diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/acpi/apei/ghes.c | 67 | ||||
-rw-r--r-- | drivers/acpi/dptf/dptf_power.c | 147 | ||||
-rw-r--r-- | drivers/acpi/pmic/intel_pmic_chtdc_ti.c | 1 | ||||
-rw-r--r-- | drivers/acpi/video_detect.c | 10 |
4 files changed, 205 insertions, 20 deletions
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 24c9642e8fc7..5abca09455ad 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -40,6 +40,7 @@ #include <linux/sched/clock.h> #include <linux/uuid.h> #include <linux/ras.h> +#include <linux/task_work.h> #include <acpi/actbl1.h> #include <acpi/ghes.h> @@ -414,23 +415,46 @@ static void ghes_clear_estatus(struct ghes *ghes, ghes_ack_error(ghes->generic_v2); } -static void ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata, int sev) +/* + * Called as task_work before returning to user-space. + * Ensure any queued work has been done before we return to the context that + * triggered the notification. + */ +static void ghes_kick_task_work(struct callback_head *head) +{ + struct acpi_hest_generic_status *estatus; + struct ghes_estatus_node *estatus_node; + u32 node_len; + + estatus_node = container_of(head, struct ghes_estatus_node, task_work); + if (IS_ENABLED(CONFIG_ACPI_APEI_MEMORY_FAILURE)) + memory_failure_queue_kick(estatus_node->task_work_cpu); + + estatus = GHES_ESTATUS_FROM_NODE(estatus_node); + node_len = GHES_ESTATUS_NODE_LEN(cper_estatus_len(estatus)); + gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node, node_len); +} + +static bool ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata, + int sev) { -#ifdef CONFIG_ACPI_APEI_MEMORY_FAILURE unsigned long pfn; int flags = -1; int sec_sev = ghes_severity(gdata->error_severity); struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata); + if (!IS_ENABLED(CONFIG_ACPI_APEI_MEMORY_FAILURE)) + return false; + if (!(mem_err->validation_bits & CPER_MEM_VALID_PA)) - return; + return false; pfn = mem_err->physical_addr >> PAGE_SHIFT; if (!pfn_valid(pfn)) { pr_warn_ratelimited(FW_WARN GHES_PFX "Invalid address in generic error data: %#llx\n", mem_err->physical_addr); - return; + return false; } /* 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 if (sev == GHES_SEV_RECOVERABLE && sec_sev == GHES_SEV_RECOVERABLE) flags = 0; - if (flags != -1) + if (flags != -1) { memory_failure_queue(pfn, flags); -#endif + return true; + } + + return false; } /* @@ -490,7 +517,7 @@ static void ghes_handle_aer(struct acpi_hest_generic_data *gdata) #endif } -static void ghes_do_proc(struct ghes *ghes, +static bool ghes_do_proc(struct ghes *ghes, const struct acpi_hest_generic_status *estatus) { int sev, sec_sev; @@ -498,6 +525,7 @@ static void ghes_do_proc(struct ghes *ghes, guid_t *sec_type; const guid_t *fru_id = &guid_null; char *fru_text = ""; + bool queued = false; sev = ghes_severity(estatus->error_severity); apei_estatus_for_each_section(estatus, gdata) { @@ -515,7 +543,7 @@ static void ghes_do_proc(struct ghes *ghes, ghes_edac_report_mem_error(sev, mem_err); arch_apei_report_mem_error(sev, mem_err); - ghes_handle_memory_failure(gdata, sev); + queued = ghes_handle_memory_failure(gdata, sev); } else if (guid_equal(sec_type, &CPER_SEC_PCIE)) { ghes_handle_aer(gdata); @@ -532,6 +560,8 @@ static void ghes_do_proc(struct ghes *ghes, gdata->error_data_length); } } + + return queued; } static void __ghes_print_estatus(const char *pfx, @@ -827,7 +857,9 @@ static void ghes_proc_in_irq(struct irq_work *irq_work) struct ghes_estatus_node *estatus_node; struct acpi_hest_generic *generic; struct acpi_hest_generic_status *estatus; + bool task_work_pending; u32 len, node_len; + int ret; llnode = llist_del_all(&ghes_estatus_llist); /* @@ -842,14 +874,26 @@ static void ghes_proc_in_irq(struct irq_work *irq_work) estatus = GHES_ESTATUS_FROM_NODE(estatus_node); len = cper_estatus_len(estatus); node_len = GHES_ESTATUS_NODE_LEN(len); - ghes_do_proc(estatus_node->ghes, estatus); + task_work_pending = ghes_do_proc(estatus_node->ghes, estatus); if (!ghes_estatus_cached(estatus)) { generic = estatus_node->generic; if (ghes_print_estatus(NULL, generic, estatus)) ghes_estatus_cache_add(generic, estatus); } - gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node, - node_len); + + if (task_work_pending && current->mm != &init_mm) { + estatus_node->task_work.func = ghes_kick_task_work; + estatus_node->task_work_cpu = smp_processor_id(); + ret = task_work_add(current, &estatus_node->task_work, + true); + if (ret) + estatus_node->task_work.func = NULL; + } + + if (!estatus_node->task_work.func) + gen_pool_free(ghes_estatus_pool, + (unsigned long)estatus_node, node_len); + llnode = next; } } @@ -909,6 +953,7 @@ static int ghes_in_nmi_queue_one_entry(struct ghes *ghes, estatus_node->ghes = ghes; estatus_node->generic = ghes->generic; + estatus_node->task_work.func = NULL; estatus = GHES_ESTATUS_FROM_NODE(estatus_node); if (__ghes_read_estatus(estatus, buf_paddr, fixmap_idx, len)) { diff --git a/drivers/acpi/dptf/dptf_power.c b/drivers/acpi/dptf/dptf_power.c index e4e8b75d39f0..5fab7e350db8 100644 --- a/drivers/acpi/dptf/dptf_power.c +++ b/drivers/acpi/dptf/dptf_power.c @@ -10,12 +10,19 @@ #include <linux/platform_device.h> /* - * Presentation of attributes which are defined for INT3407. They are: + * Presentation of attributes which are defined for INT3407 and INT3532. + * They are: * PMAX : Maximum platform powe * PSRC : Platform power source * ARTG : Adapter rating * CTYP : Charger type * PBSS : Battery steady power + * PROP : Rest of worst case platform Power + * PBSS : Power Battery Steady State + * PBSS : Power Battery Steady State + * RBHF : High Frequency Impedance + * VBNL : Instantaneous No-Load Voltage + * CMPP : Current Discharge Capability */ #define DPTF_POWER_SHOW(name, object) \ static ssize_t name##_show(struct device *dev,\ @@ -39,12 +46,42 @@ DPTF_POWER_SHOW(platform_power_source, PSRC) DPTF_POWER_SHOW(adapter_rating_mw, ARTG) DPTF_POWER_SHOW(battery_steady_power_mw, PBSS) DPTF_POWER_SHOW(charger_type, CTYP) +DPTF_POWER_SHOW(rest_of_platform_power_mw, PROP) +DPTF_POWER_SHOW(max_steady_state_power_mw, PBSS) +DPTF_POWER_SHOW(high_freq_impedance_mohm, RBHF) +DPTF_POWER_SHOW(no_load_voltage_mv, VBNL) +DPTF_POWER_SHOW(current_discharge_capbility_ma, CMPP); static DEVICE_ATTR_RO(max_platform_power_mw); static DEVICE_ATTR_RO(platform_power_source); static DEVICE_ATTR_RO(adapter_rating_mw); static DEVICE_ATTR_RO(battery_steady_power_mw); static DEVICE_ATTR_RO(charger_type); +static DEVICE_ATTR_RO(rest_of_platform_power_mw); +static DEVICE_ATTR_RO(max_steady_state_power_mw); +static DEVICE_ATTR_RO(high_freq_impedance_mohm); +static DEVICE_ATTR_RO(no_load_voltage_mv); +static DEVICE_ATTR_RO(current_discharge_capbility_ma); + +static ssize_t prochot_confirm_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct acpi_device *acpi_dev = dev_get_drvdata(dev); + acpi_status status; + int seq_no; + + if (kstrtouint(buf, 0, &seq_no) < 0) + return -EINVAL; + + status = acpi_execute_simple_method(acpi_dev->handle, "PBOK", seq_no); + if (ACPI_SUCCESS(status)) + return count; + + return -EINVAL; +} + +static DEVICE_ATTR_WO(prochot_confirm); static struct attribute *dptf_power_attrs[] = { &dev_attr_max_platform_power_mw.attr, @@ -52,6 +89,8 @@ static struct attribute *dptf_power_attrs[] = { &dev_attr_adapter_rating_mw.attr, &dev_attr_battery_steady_power_mw.attr, &dev_attr_charger_type.attr, + &dev_attr_rest_of_platform_power_mw.attr, + &dev_attr_prochot_confirm.attr, NULL }; @@ -60,10 +99,79 @@ static const struct attribute_group dptf_power_attribute_group = { .name = "dptf_power" }; +static struct attribute *dptf_battery_attrs[] = { + &dev_attr_max_platform_power_mw.attr, + &dev_attr_max_steady_state_power_mw.attr, + &dev_attr_high_freq_impedance_mohm.attr, + &dev_attr_no_load_voltage_mv.attr, + &dev_attr_current_discharge_capbility_ma.attr, + NULL +}; + +static const struct attribute_group dptf_battery_attribute_group = { + .attrs = dptf_battery_attrs, + .name = "dptf_battery" +}; + +#define MAX_POWER_CHANGED 0x80 +#define POWER_STATE_CHANGED 0x81 +#define STEADY_STATE_POWER_CHANGED 0x83 +#define POWER_PROP_CHANGE_EVENT 0x84 +#define IMPEDANCED_CHNGED 0x85 +#define VOLTAGE_CURRENT_CHANGED 0x86 + +static long long dptf_participant_type(acpi_handle handle) +{ + unsigned long long ptype; + acpi_status status; + + status = acpi_evaluate_integer(handle, "PTYP", NULL, &ptype); + if (ACPI_FAILURE(status)) + return -ENODEV; + + return ptype; +} + +static void dptf_power_notify(acpi_handle handle, u32 event, void *data) +{ + struct platform_device *pdev = data; + char *attr; + + switch (event) { + case POWER_STATE_CHANGED: + attr = "platform_power_source"; + break; + case POWER_PROP_CHANGE_EVENT: + attr = "rest_of_platform_power_mw"; + break; + case MAX_POWER_CHANGED: + attr = "max_platform_power_mw"; + break; + case STEADY_STATE_POWER_CHANGED: + attr = "max_steady_state_power_mw"; + break; + case VOLTAGE_CURRENT_CHANGED: + attr = "no_load_voltage_mv"; + break; + default: + dev_err(&pdev->dev, "Unsupported event [0x%x]\n", event); + return; + } + + /* + * Notify that an attribute is changed, so that user space can read + * again. + */ + if (dptf_participant_type(handle) == 0x0CULL) + sysfs_notify(&pdev->dev.kobj, "dptf_battery", attr); + else + sysfs_notify(&pdev->dev.kobj, "dptf_power", attr); +} + static int dptf_power_add(struct platform_device *pdev) { + const struct attribute_group *attr_group; struct acpi_device *acpi_dev; - acpi_status status; unsigned long long ptype; int result; @@ -71,17 +179,29 @@ static int dptf_power_add(struct platform_device *pdev) if (!acpi_dev) return -ENODEV; - status = acpi_evaluate_integer(acpi_dev->handle, "PTYP", NULL, &ptype); - if (ACPI_FAILURE(status)) + ptype = dptf_participant_type(acpi_dev->handle); + if (ptype == 0x11) + attr_group = &dptf_power_attribute_group; + else if (ptype == 0x0C) + attr_group = &dptf_battery_attribute_group; + else return -ENODEV; - if (ptype != 0x11) - return -ENODEV; + result = acpi_install_notify_handler(acpi_dev->handle, + ACPI_DEVICE_NOTIFY, + dptf_power_notify, + (void *)pdev); + if (result) + return result; result = sysfs_create_group(&pdev->dev.kobj, - &dptf_power_attribute_group); - if (result) + attr_group); + if (result) { + acpi_remove_notify_handler(acpi_dev->handle, + ACPI_DEVICE_NOTIFY, + dptf_power_notify); return result; + } platform_set_drvdata(pdev, acpi_dev); @@ -90,14 +210,23 @@ static int dptf_power_add(struct platform_device *pdev) static int dptf_power_remove(struct platform_device *pdev) { + struct acpi_device *acpi_dev = platform_get_drvdata(pdev); + + acpi_remove_notify_handler(acpi_dev->handle, + ACPI_DEVICE_NOTIFY, + dptf_power_notify); - sysfs_remove_group(&pdev->dev.kobj, &dptf_power_attribute_group); + if (dptf_participant_type(acpi_dev->handle) == 0x0CULL) + sysfs_remove_group(&pdev->dev.kobj, &dptf_battery_attribute_group); + else + sysfs_remove_group(&pdev->dev.kobj, &dptf_power_attribute_group); return 0; } static const struct acpi_device_id int3407_device_ids[] = { {"INT3407", 0}, + {"INT3532", 0}, {"INTC1047", 0}, {"", 0}, }; diff --git a/drivers/acpi/pmic/intel_pmic_chtdc_ti.c b/drivers/acpi/pmic/intel_pmic_chtdc_ti.c index 7ccd7d9660bc..a5101b07611a 100644 --- a/drivers/acpi/pmic/intel_pmic_chtdc_ti.c +++ b/drivers/acpi/pmic/intel_pmic_chtdc_ti.c @@ -102,6 +102,7 @@ static struct intel_pmic_opregion_data chtdc_ti_pmic_opregion_data = { .power_table_count = ARRAY_SIZE(chtdc_ti_power_table), .thermal_table = chtdc_ti_thermal_table, .thermal_table_count = ARRAY_SIZE(chtdc_ti_thermal_table), + .pmic_i2c_address = 0x5e, }; static int chtdc_ti_pmic_opregion_probe(struct platform_device *pdev) diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index b4994e50608d..2499d7e3c710 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -361,6 +361,16 @@ static const struct dmi_system_id video_detect_dmi_table[] = { DMI_MATCH(DMI_BOARD_NAME, "JV50"), }, }, + { + /* https://bugzilla.kernel.org/show_bug.cgi?id=207835 */ + .callback = video_detect_force_native, + .ident = "Acer TravelMate 5735Z", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 5735Z"), + DMI_MATCH(DMI_BOARD_NAME, "BA51_MV"), + }, + }, /* * Desktops which falsely report a backlight and which our heuristics |