diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index f5e0eb299610..0084f308b790 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -10,10 +10,12 @@ #define pr_fmt(fmt) "ACPI: battery: " fmt +#include #include #include #include #include +#include #include #include #include @@ -21,6 +23,7 @@ #include #include #include +#include #include @@ -43,6 +46,9 @@ #define MAX_STRING_LENGTH 64 +#define MAX_QUEUED_EVENTS 16 +#define NOTIF_MERGING_MS 10 + MODULE_AUTHOR("Paul Diefenbaugh"); MODULE_AUTHOR("Alexey Starikovskiy "); MODULE_DESCRIPTION("ACPI Battery Driver"); @@ -95,6 +101,8 @@ struct acpi_battery { struct power_supply_desc bat_desc; struct acpi_device *device; struct device *phys_dev; + struct kfifo acpi_notif_fifo; + struct delayed_work acpi_notif_dwork; struct notifier_block pm_nb; struct list_head list; unsigned long update_time; @@ -150,27 +158,28 @@ static int acpi_battery_technology(struct acpi_battery *battery) static int acpi_battery_get_state(struct acpi_battery *battery); +static bool acpi_battery_is_full(struct acpi_battery *battery) +{ + /* battery not reporting charge */ + if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN || + battery->capacity_now == 0) + return false; + + /* good batteries update full_charge as the batteries degrade */ + if (battery->full_charge_capacity == battery->capacity_now) + return true; + + /* fallback to using design values for broken batteries */ + return battery->design_capacity <= battery->capacity_now; +} + static int acpi_battery_is_charged(struct acpi_battery *battery) { /* charging, discharging, critical low or charge limited */ if (battery->state != 0) return 0; - /* battery not reporting charge */ - if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN || - battery->capacity_now == 0) - return 0; - - /* good batteries update full_charge as the batteries degrade */ - if (battery->full_charge_capacity == battery->capacity_now) - return 1; - - /* fallback to using design values for broken batteries */ - if (battery->design_capacity <= battery->capacity_now) - return 1; - - /* we don't do any sort of metric based on percentages */ - return 0; + return acpi_battery_is_full(battery); } static bool acpi_battery_is_degraded(struct acpi_battery *battery) @@ -211,13 +220,14 @@ static int acpi_battery_get_property(struct power_supply *psy, if (battery->state & ACPI_BATTERY_STATE_DISCHARGING) val->intval = acpi_battery_handle_discharging(battery); else if (battery->state & ACPI_BATTERY_STATE_CHARGING) - /* Validate the status by checking the current. */ - if (battery->rate_now != ACPI_BATTERY_VALUE_UNKNOWN && - battery->rate_now == 0) { - /* On charge but no current (0W/0mA). */ - val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; - } else { + /* Check the rate and capacity to validate the status. */ + if (!acpi_battery_is_full(battery) || + (battery->rate_now != ACPI_BATTERY_VALUE_UNKNOWN && + battery->rate_now > 0)) { val->intval = POWER_SUPPLY_STATUS_CHARGING; + } else { + /* Full and zero rate. */ + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; } else if (battery->state & ACPI_BATTERY_STATE_CHARGE_LIMITING) val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; @@ -483,6 +493,15 @@ static int acpi_battery_get_status(struct acpi_battery *battery) return 0; } +static void acpi_battery_clean_unprintable_chars(char *str, size_t length) +{ + for (unsigned int i = 0; i < length; i++) { + if (!isascii(str[i]) || !isprint(str[i])) { + str[i] = '\0'; + break; + } + } +} static int extract_battery_info(const int use_bix, struct acpi_battery *battery, @@ -524,6 +543,10 @@ static int extract_battery_info(const int use_bix, battery->capacity_now > battery->full_charge_capacity) battery->capacity_now = battery->full_charge_capacity; + if (!result) + acpi_battery_clean_unprintable_chars(battery->model_number, + ARRAY_SIZE(battery->model_number)); + return result; } @@ -668,9 +691,13 @@ static ssize_t acpi_battery_alarm_store(struct device *dev, { unsigned long x; struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev)); + int err; - if (sscanf(buf, "%lu\n", &x) == 1) - battery->alarm = x/1000; + err = kstrtoul(buf, 10, &x); + if (err) + return err; + + battery->alarm = x / 1000; if (acpi_battery_present(battery)) acpi_battery_set_alarm(battery); return count; @@ -1059,14 +1086,24 @@ static void acpi_battery_refresh(struct acpi_battery *battery) } /* Driver Interface */ -static void acpi_battery_notify(acpi_handle handle, u32 event, void *data) +static void acpi_battery_notification_worker(struct work_struct *work) { - struct acpi_battery *battery = data; + struct acpi_battery *battery = container_of(work, struct acpi_battery, + acpi_notif_dwork.work); struct acpi_device *device = battery->device; + u32 events[MAX_QUEUED_EVENTS]; struct power_supply *old; + unsigned int count, i; guard(mutex)(&battery->update_lock); + count = kfifo_out(&battery->acpi_notif_fifo, events, sizeof(events)); + count /= sizeof(events[0]); + if (!count) + return; + + pr_debug("merged %u battery notifications within %dms\n", count, NOTIF_MERGING_MS); + old = battery->bat; /* * On Acer Aspire V5-573G notifications are sometimes triggered too @@ -1076,19 +1113,46 @@ static void acpi_battery_notify(acpi_handle handle, u32 event, void *data) */ if (battery_notification_delay_ms > 0) msleep(battery_notification_delay_ms); - if (event == ACPI_BATTERY_NOTIFY_INFO) - acpi_battery_refresh(battery); + + for (i = 0; i < count; i++) { + if (events[i] == ACPI_BATTERY_NOTIFY_INFO) { + acpi_battery_refresh(battery); + break; + } + } + acpi_battery_update(battery, false); - acpi_bus_generate_netlink_event(ACPI_BATTERY_CLASS, - dev_name(&device->dev), event, - acpi_battery_present(battery)); - acpi_notifier_call_chain(ACPI_BATTERY_CLASS, acpi_device_bid(device), - event, acpi_battery_present(battery)); + + for (i = 0; i < count; i++) { + acpi_bus_generate_netlink_event(ACPI_BATTERY_CLASS, + dev_name(&device->dev), events[i], + acpi_battery_present(battery)); + acpi_notifier_call_chain(ACPI_BATTERY_CLASS, acpi_device_bid(device), + events[i], acpi_battery_present(battery)); + } + /* acpi_battery_update could remove power_supply object */ if (old && battery->bat) power_supply_changed(battery->bat); } +static void acpi_battery_notify(acpi_handle handle, u32 event, void *data) +{ + struct acpi_battery *battery = data; + + guard(mutex)(&battery->update_lock); + + if (kfifo_avail(&battery->acpi_notif_fifo) >= sizeof(event)) { + kfifo_in(&battery->acpi_notif_fifo, &event, sizeof(event)); + schedule_delayed_work(&battery->acpi_notif_dwork, + msecs_to_jiffies(NOTIF_MERGING_MS)); + + return; + } + + pr_err_ratelimited("too many battery notifications within %dms\n", NOTIF_MERGING_MS); +} + static int battery_notify(struct notifier_block *nb, unsigned long mode, void *_unused) { @@ -1231,6 +1295,29 @@ static int devm_acpi_battery_update_retry(struct device *dev, return ret; } +static void acpi_battery_notify_dwork_cleanup(void *data) +{ + struct acpi_battery *battery = data; + + cancel_delayed_work_sync(&battery->acpi_notif_dwork); + kfifo_free(&battery->acpi_notif_fifo); +} + +static int devm_acpi_battery_init_notify_dwork(struct device *dev, + struct acpi_battery *battery) +{ + int ret; + + INIT_DELAYED_WORK(&battery->acpi_notif_dwork, acpi_battery_notification_worker); + + ret = kfifo_alloc(&battery->acpi_notif_fifo, + MAX_QUEUED_EVENTS * sizeof(u32), GFP_KERNEL); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, acpi_battery_notify_dwork_cleanup, battery); +} + static int acpi_battery_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1272,6 +1359,10 @@ static int acpi_battery_probe(struct platform_device *pdev) if (result) return result; + result = devm_acpi_battery_init_notify_dwork(dev, battery); + if (result) + return result; + result = devm_acpi_install_notify_handler(dev, ACPI_ALL_NOTIFY, acpi_battery_notify, battery); if (result) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 64ad4cfa6208..a89f10256dbb 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1510,6 +1510,24 @@ static bool install_gpio_irq_event_handler(struct acpi_ec *ec) IRQF_SHARED | IRQF_ONESHOT, "ACPI EC", ec) >= 0; } +static int ec_prepare_gpio_irq(struct acpi_ec *ec, struct acpi_device *device) +{ + int irq; + + if (!device || ec->gpe >= 0 || ec->irq >= 0) + return 0; + + /* ACPI reduced hardware platforms use a GpioInt from _CRS. */ + irq = acpi_dev_gpio_irq_get(device, 0); + if (irq == -EPROBE_DEFER) + return irq; + + if (irq >= 0) + ec->irq = irq; + + return 0; +} + /** * ec_install_handlers - Install service callbacks and register query methods. * @ec: Target EC. @@ -1524,7 +1542,6 @@ static bool install_gpio_irq_event_handler(struct acpi_ec *ec) * Return: * -ENODEV if the address space handler cannot be installed, which means * "unable to handle transactions", - * -EPROBE_DEFER if GPIO IRQ acquisition needs to be deferred, * or 0 (success) otherwise. */ static int ec_install_handlers(struct acpi_ec *ec, struct acpi_device *device, @@ -1557,19 +1574,6 @@ static int ec_install_handlers(struct acpi_ec *ec, struct acpi_device *device, if (!device) return 0; - if (ec->gpe < 0) { - /* ACPI reduced hardware platforms use a GpioInt from _CRS. */ - int irq = acpi_dev_gpio_irq_get(device, 0); - /* - * Bail out right away for deferred probing or complete the - * initialization regardless of any other errors. - */ - if (irq == -EPROBE_DEFER) - return -EPROBE_DEFER; - else if (irq >= 0) - ec->irq = irq; - } - if (!test_bit(EC_FLAGS_QUERY_METHODS_INSTALLED, &ec->flags)) { /* Find and register all query methods */ acpi_walk_namespace(ACPI_TYPE_METHOD, ec->handle, 1, @@ -1647,6 +1651,14 @@ static int acpi_ec_setup(struct acpi_ec *ec, struct acpi_device *device, bool ca { int ret; + /* + * GPIO IRQ lookup can defer. Do it before publishing the EC + * OpRegion to AML to avoid a spurious _REG(disconnect). + */ + ret = ec_prepare_gpio_irq(ec, device); + if (ret) + return ret; + /* First EC capable of handling transactions */ if (!first_ec) first_ec = ec; diff --git a/drivers/acpi/fan.h b/drivers/acpi/fan.h index 97ce3212edf3..e20d6ad9df80 100644 --- a/drivers/acpi/fan.h +++ b/drivers/acpi/fan.h @@ -10,20 +10,21 @@ #ifndef _ACPI_FAN_H_ #define _ACPI_FAN_H_ -#include +#include #include +#include -#define ACPI_FAN_DEVICE_IDS \ - {"INT3404", }, /* Fan */ \ - {"INTC1044", }, /* Fan for Tiger Lake generation */ \ - {"INTC1048", }, /* Fan for Alder Lake generation */ \ - {"INTC1063", }, /* Fan for Meteor Lake generation */ \ - {"INTC106A", }, /* Fan for Lunar Lake generation */ \ - {"INTC10A2", }, /* Fan for Raptor Lake generation */ \ - {"INTC10D6", }, /* Fan for Panther Lake generation */ \ - {"INTC10FE", }, /* Fan for Wildcat Lake generation */ \ - {"INTC10F5", }, /* Fan for Nova Lake generation */ \ - {"PNP0C0B", } /* Generic ACPI fan */ +#define ACPI_FAN_DEVICE_IDS \ + { .id = "INT3404" }, /* Fan */ \ + { .id = "INTC1044" }, /* Fan for Tiger Lake generation */ \ + { .id = "INTC1048" }, /* Fan for Alder Lake generation */ \ + { .id = "INTC1063" }, /* Fan for Meteor Lake generation */ \ + { .id = "INTC106A" }, /* Fan for Lunar Lake generation */ \ + { .id = "INTC10A2" }, /* Fan for Raptor Lake generation */ \ + { .id = "INTC10D6" }, /* Fan for Panther Lake generation */ \ + { .id = "INTC10FE" }, /* Fan for Wildcat Lake generation */ \ + { .id = "INTC10F5" }, /* Fan for Nova Lake generation */ \ + { .id = "PNP0C0B" } /* Generic ACPI fan */ #define ACPI_FPS_NAME_LEN 20 @@ -69,7 +70,7 @@ struct acpi_fan { /** * acpi_fan_speed_valid - Check if fan speed value is valid - * @speeed: Speed value returned by the ACPI firmware + * @speed: Speed value returned by the ACPI firmware * * Check if the fan speed value returned by the ACPI firmware is valid. This function is * necessary as ACPI firmware implementations can return 0xFFFFFFFF to signal that the diff --git a/drivers/acpi/fan_core.c b/drivers/acpi/fan_core.c index fb08b8549ed7..624d0736b581 100644 --- a/drivers/acpi/fan_core.c +++ b/drivers/acpi/fan_core.c @@ -489,26 +489,6 @@ static void acpi_fan_notify_handler(acpi_handle handle, u32 event, void *context } } -static void acpi_fan_notify_remove(void *data) -{ - struct acpi_fan *fan = data; - - acpi_remove_notify_handler(fan->handle, ACPI_DEVICE_NOTIFY, acpi_fan_notify_handler); -} - -static int devm_acpi_fan_notify_init(struct device *dev) -{ - struct acpi_fan *fan = dev_get_drvdata(dev); - acpi_status status; - - status = acpi_install_notify_handler(fan->handle, ACPI_DEVICE_NOTIFY, - acpi_fan_notify_handler, dev); - if (ACPI_FAILURE(status)) - return -EIO; - - return devm_add_action_or_reset(dev, acpi_fan_notify_remove, fan); -} - static int acpi_fan_probe(struct platform_device *pdev) { int result = 0; @@ -556,7 +536,10 @@ static int acpi_fan_probe(struct platform_device *pdev) if (result) return result; - result = devm_acpi_fan_notify_init(&pdev->dev); + result = devm_acpi_install_notify_handler(&pdev->dev, + ACPI_DEVICE_NOTIFY, + acpi_fan_notify_handler, + &pdev->dev); if (result) return result; diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index a4498357bd16..3bf076c150fa 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -336,11 +336,26 @@ int acpi_get_cpuid(acpi_handle handle, int type, u32 acpi_id) EXPORT_SYMBOL_GPL(acpi_get_cpuid); #ifdef CONFIG_ACPI_HOTPLUG_IOAPIC -static int get_ioapic_id(struct acpi_subtable_header *entry, u32 gsi_base, +static bool madt_entry_is_valid(struct acpi_subtable_header *entry, + unsigned long end) +{ + unsigned long start = (unsigned long)entry; + + if (start >= end || end - start < sizeof(*entry)) + return false; + + return entry->length >= sizeof(*entry) && entry->length <= end - start; +} + +static int get_ioapic_id(struct acpi_subtable_header *entry, + const unsigned long end, u32 gsi_base, u64 *phys_addr, int *ioapic_id) { struct acpi_madt_io_apic *ioapic = (struct acpi_madt_io_apic *)entry; + if (!madt_entry_is_valid(entry, end) || BAD_MADT_ENTRY(ioapic, end)) + return 0; + if (ioapic->global_irq_base != gsi_base) return 0; @@ -361,17 +376,19 @@ static int parse_madt_ioapic_entry(u32 gsi_base, u64 *phys_addr) return apic_id; entry = (unsigned long)madt; + if (madt->header.length < sizeof(*madt)) + return apic_id; madt_end = entry + madt->header.length; /* Parse all entries looking for a match. */ entry += sizeof(struct acpi_table_madt); - while (entry + sizeof(struct acpi_subtable_header) < madt_end) { + while (madt_entry_is_valid((struct acpi_subtable_header *)entry, + madt_end)) { hdr = (struct acpi_subtable_header *)entry; if (hdr->type == ACPI_MADT_TYPE_IO_APIC && - get_ioapic_id(hdr, gsi_base, phys_addr, &apic_id)) + get_ioapic_id(hdr, madt_end, gsi_base, phys_addr, &apic_id)) break; - else - entry += hdr->length; + entry += hdr->length; } return apic_id; @@ -398,7 +415,9 @@ static int parse_mat_ioapic_entry(acpi_handle handle, u32 gsi_base, header = (struct acpi_subtable_header *)obj->buffer.pointer; if (header->type == ACPI_MADT_TYPE_IO_APIC) - get_ioapic_id(header, gsi_base, phys_addr, &apic_id); + get_ioapic_id(header, + (unsigned long)header + obj->buffer.length, + gsi_base, phys_addr, &apic_id); exit: kfree(buffer.pointer); diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index cda8fd720000..cdc2ae1632b2 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -285,6 +285,12 @@ static int __init acpi_processor_driver_init(void) unregister_idle_drv: acpi_processor_unregister_idle_driver(); + if (acpi_processor_cpufreq_init) { + cpufreq_unregister_notifier(&acpi_processor_notifier_block, + CPUFREQ_POLICY_NOTIFIER); + acpi_processor_cpufreq_init = false; + } + return result; }