diff options
| author | Mark Brown <broonie@kernel.org> | 2026-07-23 14:18:14 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-07-23 14:18:14 +0100 |
| commit | cb80df544f31e233d767e4ecf94bbc1ae712da1f (patch) | |
| tree | 01ace82b5d385af627d87911d39e1e55721db23c /drivers | |
| parent | e08a9005e6fa61e8e0d0aa72f565101d368a032b (diff) | |
| parent | 2f67e70aaf88cf8b81daac740e90956446f17a5e (diff) | |
| download | linux-next-cb80df544f31e233d767e4ecf94bbc1ae712da1f.tar.gz linux-next-cb80df544f31e233d767e4ecf94bbc1ae712da1f.zip | |
Merge branch 'linux-next' of https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git
Diffstat (limited to 'drivers')
49 files changed, 1477 insertions, 892 deletions
diff --git a/drivers/acpi/acpi_fpdt.c b/drivers/acpi/acpi_fpdt.c index e75dd28d31a9..79191ed20f76 100644 --- a/drivers/acpi/acpi_fpdt.c +++ b/drivers/acpi/acpi_fpdt.c @@ -168,7 +168,7 @@ static int fpdt_process_subtable(u64 address, u32 subtable_type) struct fpdt_subtable_header *subtable_header; struct fpdt_record_header *record_header; char *signature = (subtable_type == SUBTABLE_FBPT ? "FBPT" : "S3PT"); - u32 length, offset; + u32 length, offset, remaining; int result; if (!fpdt_address_valid(address)) { @@ -182,10 +182,17 @@ static int fpdt_process_subtable(u64 address, u32 subtable_type) if (strncmp((char *)&subtable_header->signature, signature, 4)) { pr_info(FW_BUG "subtable signature and type mismatch!\n"); + acpi_os_unmap_memory(subtable_header, sizeof(*subtable_header)); return -EINVAL; } length = subtable_header->length; + if (length < sizeof(*subtable_header)) { + pr_err(FW_BUG "Invalid FPDT subtable length %u.\n", length); + acpi_os_unmap_memory(subtable_header, sizeof(*subtable_header)); + return -EINVAL; + } + acpi_os_unmap_memory(subtable_header, sizeof(*subtable_header)); subtable_header = acpi_os_map_memory(address, length); @@ -194,17 +201,29 @@ static int fpdt_process_subtable(u64 address, u32 subtable_type) offset = sizeof(*subtable_header); while (offset < length) { - record_header = (void *)subtable_header + offset; - offset += record_header->length; + remaining = length - offset; + if (remaining < sizeof(*record_header)) { + pr_err(FW_BUG "Truncated FPDT record header.\n"); + result = -EINVAL; + goto err; + } - if (!record_header->length) { - pr_err(FW_BUG "Zero-length record found in FPTD.\n"); + record_header = (void *)subtable_header + offset; + if (record_header->length < sizeof(*record_header) || + record_header->length > remaining) { + pr_err(FW_BUG "Invalid FPDT record length %u.\n", + record_header->length); result = -EINVAL; goto err; } + offset += record_header->length; switch (record_header->type) { case RECORD_S3_RESUME: + if (record_header->length < sizeof(*record_resume)) { + result = -EINVAL; + goto err; + } if (subtable_type != SUBTABLE_S3PT) { pr_err(FW_BUG "Invalid record %d for subtable %s\n", record_header->type, signature); @@ -221,6 +240,10 @@ static int fpdt_process_subtable(u64 address, u32 subtable_type) goto err; break; case RECORD_S3_SUSPEND: + if (record_header->length < sizeof(*record_suspend)) { + result = -EINVAL; + goto err; + } if (subtable_type != SUBTABLE_S3PT) { pr_err(FW_BUG "Invalid %d for subtable %s\n", record_header->type, signature); @@ -236,6 +259,10 @@ static int fpdt_process_subtable(u64 address, u32 subtable_type) goto err; break; case RECORD_BOOT: + if (record_header->length < sizeof(*record_boot)) { + result = -EINVAL; + goto err; + } if (subtable_type != SUBTABLE_FBPT) { pr_err(FW_BUG "Invalid %d for subtable %s\n", record_header->type, signature); @@ -317,7 +344,21 @@ static int __init acpi_init_fpdt(void) } while (offset < header->length) { + if (header->length - offset < sizeof(*subtable)) { + pr_err(FW_BUG "Truncated FPDT subtable entry.\n"); + result = -EINVAL; + goto err_subtable; + } + subtable = (void *)header + offset; + if (subtable->length < sizeof(*subtable) || + subtable->length > header->length - offset) { + pr_err(FW_BUG "Invalid FPDT subtable entry length %u.\n", + subtable->length); + result = -EINVAL; + goto err_subtable; + } + switch (subtable->type) { case SUBTABLE_FBPT: case SUBTABLE_S3PT: @@ -330,7 +371,7 @@ static int __init acpi_init_fpdt(void) /* Other types are reserved in ACPI 6.4 spec. */ break; } - offset += sizeof(*subtable); + offset += subtable->length; } return 0; err_subtable: diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c index 00775b91bd41..5fbab54171b7 100644 --- a/drivers/acpi/acpi_processor.c +++ b/drivers/acpi/acpi_processor.c @@ -994,3 +994,357 @@ end: } EXPORT_SYMBOL_NS_GPL(acpi_processor_evaluate_cst, "ACPI_PROCESSOR_IDLE"); #endif /* CONFIG_ACPI_PROCESSOR_CSTATE */ + +#ifdef CONFIG_ACPI_PROCESSOR_IDLE +struct acpi_lpi_states_array { + unsigned int size; + unsigned int composite_states_size; + struct acpi_lpi_state *entries; + struct acpi_lpi_state *composite_states[ACPI_PROCESSOR_MAX_POWER]; +}; + +static int obj_get_integer(union acpi_object *obj, u32 *value) +{ + if (obj->type != ACPI_TYPE_INTEGER) + return -EINVAL; + + *value = obj->integer.value; + return 0; +} + +#define lpi_state_debug(handle, message, state_idx) \ + acpi_handle_debug(handle, message " for _LPI state %u\n", state_idx) + +static void process_lpi_state_package(union acpi_object *lpi_pkg, + struct acpi_lpi_state *lpi_state, + acpi_handle handle, + unsigned int state_idx, bool strict) +{ + union acpi_object *lpi_pkg_elem, *obj; + + if (lpi_pkg->type != ACPI_TYPE_PACKAGE || lpi_pkg->package.count < 7) + return; + + lpi_pkg_elem = lpi_pkg->package.elements; + + /* Get the entry method first and skip the state if that fails. */ + obj = &lpi_pkg_elem[6]; + if (obj->type == ACPI_TYPE_BUFFER) { + struct acpi_power_register *reg; + + if (obj->buffer.length < sizeof(*reg)) { + lpi_state_debug(handle, "Invalid register data", state_idx); + return; + } + + reg = (struct acpi_power_register *)obj->buffer.pointer; + if (reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) { + lpi_state_debug(handle, "Unsupported entry method", state_idx); + return; + } + + lpi_state->entry_method = ACPI_CSTATE_FFH; + lpi_state->address = reg->address; + } else if (obj->type == ACPI_TYPE_INTEGER) { + lpi_state->entry_method = ACPI_CSTATE_INTEGER; + lpi_state->address = obj->integer.value; + } else { + lpi_state_debug(handle, "Invalid entry method", state_idx); + return; + } + + if (obj_get_integer(&lpi_pkg_elem[0], &lpi_state->min_residency)) { + if (strict) { + lpi_state_debug(handle, "No min. residency", state_idx); + return; + } + + lpi_state_debug(handle, "Assuming 10 us min. residency", state_idx); + lpi_state->min_residency = 10; + } + + if (obj_get_integer(&lpi_pkg_elem[1], &lpi_state->wake_latency)) { + if (strict) { + lpi_state_debug(handle, "No wake latency", state_idx); + return; + } + + lpi_state_debug(handle, "Assuming 10 us wake latency", state_idx); + lpi_state->wake_latency = 10; + } + + if (obj_get_integer(&lpi_pkg_elem[2], &lpi_state->flags)) + lpi_state->flags = 0; + + if (obj_get_integer(&lpi_pkg_elem[3], &lpi_state->arch_flags)) + lpi_state->arch_flags = 0; + + if (obj_get_integer(&lpi_pkg_elem[4], &lpi_state->res_cnt_freq)) + lpi_state->res_cnt_freq = 1; + + if (obj_get_integer(&lpi_pkg_elem[5], &lpi_state->enable_parent_state)) + lpi_state->enable_parent_state = 0; + + /* Skip elements [7-8] i.e. Residency/Usage counters. */ + + /* + * Avoid out-of-bounds access if the size of the package is less than + * expected. + */ + if (lpi_pkg->package.count < 10) + return; + + obj = &lpi_pkg_elem[9]; + if (obj->type == ACPI_TYPE_STRING) + strscpy(lpi_state->desc, obj->string.pointer, ACPI_CX_DESC_LEN); +} + +static int acpi_processor_evaluate_lpi(acpi_handle handle, + struct acpi_lpi_states_array *info, + bool strict) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *lpi_data, *lpi_pkg; + unsigned int lpi_pkg_count, state_idx; + struct acpi_lpi_state *lpi_state; + acpi_status status; + int ret = 0; + + status = acpi_evaluate_object(handle, "_LPI", NULL, &buffer); + if (ACPI_FAILURE(status)) { + acpi_handle_debug(handle, "No _LPI, giving up\n"); + return -ENODEV; + } + + lpi_data = buffer.pointer; + + /* There must be at least 4 elements = 3 elements + 1 package */ + if (!lpi_data || lpi_data->type != ACPI_TYPE_PACKAGE || + lpi_data->package.count < 4) { + acpi_handle_debug(handle, "Not enough elements in _LPI\n"); + ret = -ENODATA; + goto end; + } + + lpi_pkg_count = lpi_data->package.elements[2].integer.value; + + /* Validate number of power states. */ + if (!lpi_pkg_count || lpi_pkg_count != lpi_data->package.count - 3) { + acpi_handle_debug(handle, "Invalid _LPI state count\n"); + ret = -ENODATA; + goto end; + } + + lpi_state = kzalloc_objs(*lpi_state, lpi_pkg_count); + if (!lpi_state) { + ret = -ENOMEM; + goto end; + } + + info->size = lpi_pkg_count; + info->entries = lpi_state; + + /* _LPI State packages start at index 3. */ + lpi_pkg = &lpi_data->package.elements[3]; + + for (state_idx = 1; state_idx <= lpi_pkg_count; state_idx++) { + lpi_state->index = state_idx; + process_lpi_state_package(lpi_pkg++, lpi_state++, handle, + state_idx, strict); + } + + acpi_handle_debug(handle, "Found %u power states\n", lpi_pkg_count); +end: + kfree(buffer.pointer); + return ret; +} + +/** + * combine_lpi_states - combine local and parent LPI states to form a composite LPI state + * + * @local: local LPI state + * @parent: parent LPI state + * @result: composite LPI state + */ +static bool combine_lpi_states(struct acpi_lpi_state *local, + struct acpi_lpi_state *parent, + struct acpi_lpi_state *result) +{ + if (parent->entry_method == ACPI_CSTATE_INTEGER) { + if (!parent->address) /* 0 means autopromotable */ + return false; + result->address = local->address + parent->address; + } else { + result->address = parent->address; + } + + result->min_residency = max(local->min_residency, parent->min_residency); + result->wake_latency = local->wake_latency + parent->wake_latency; + result->enable_parent_state = parent->enable_parent_state; + result->entry_method = local->entry_method; + + result->flags = parent->flags; + result->arch_flags = parent->arch_flags; + result->index = parent->index; + + scnprintf(result->desc, ACPI_CX_DESC_LEN, "%s+%s", local->desc, parent->desc); + return true; +} + +#define ACPI_LPI_STATE_FLAGS_ENABLED BIT(0) + +static void stash_composite_state(struct acpi_lpi_states_array *curr_level, + struct acpi_lpi_state *t) +{ + curr_level->composite_states[curr_level->composite_states_size++] = t; +} + +static bool too_many_states(acpi_handle handle, unsigned int state_count) +{ + if (state_count < ACPI_PROCESSOR_MAX_POWER) + return false; + + acpi_handle_info(handle, "No space for more _LPI states than %d\n", + ACPI_PROCESSOR_MAX_POWER); + return true; +} + +static unsigned int flatten_lpi_states(acpi_handle handle, + struct acpi_lpi_state *lpi_states, + unsigned int state_count, + struct acpi_lpi_states_array *curr, + struct acpi_lpi_states_array *prev) +{ + struct acpi_lpi_state *parent_lpi = curr->entries; + unsigned int j; + + /* + * Combine each of the "raw" _LPI states from the current (processor + * container) level with all of the composite _LPI states from the + * previous (processor or processor container) level. + */ + for (j = 0; j < curr->size; j++, parent_lpi++) { + struct acpi_lpi_state *flpi; + int i; + + if (!(parent_lpi->flags & ACPI_LPI_STATE_FLAGS_ENABLED)) + continue; + + if (too_many_states(handle, state_count)) + break; + + flpi = &lpi_states[state_count]; + + for (i = 0; i < prev->composite_states_size; i++) { + struct acpi_lpi_state *local_lpi = prev->composite_states[i]; + + if (parent_lpi->index > local_lpi->enable_parent_state) + continue; + + if (!combine_lpi_states(local_lpi, parent_lpi, flpi)) + continue; + + stash_composite_state(curr, flpi); + state_count++; + flpi++; + + if (state_count >= ACPI_PROCESSOR_MAX_POWER) + break; + } + } + + return state_count; +} + +int acpi_processor_extract_lpi_info(acpi_handle pr_handle, + struct acpi_processor_power *pr_power, + bool strict) +{ + struct acpi_lpi_states_array info[2], *prev, *curr; + acpi_handle handle = pr_handle; + unsigned int state_count = 0; + unsigned int i; + int ret; + + if (!osc_pc_lpi_support_confirmed) + return -EOPNOTSUPP; + + curr = &info[0]; + curr->composite_states_size = 0; + + ret = acpi_processor_evaluate_lpi(handle, curr, strict); + if (ret) + return ret; + + /* Copy all of the usable first-level states to power.lpi_states[]. */ + for (i = 0; i < curr->size; i++) { + struct acpi_lpi_state *lpi = &curr->entries[i]; + struct acpi_lpi_state *flpi; + + /* + * Skip states that are not enabled or have an inadequate entry + * method for this level. + */ + if (!(lpi->flags & ACPI_LPI_STATE_FLAGS_ENABLED) || + lpi->entry_method == ACPI_CSTATE_INTEGER) + continue; + + if (too_many_states(pr_handle, state_count)) + break; + + flpi = &pr_power->lpi_states[state_count++]; + memcpy(flpi, lpi, sizeof(*lpi)); + stash_composite_state(curr, flpi); + } + + kfree(curr->entries); + + /* + * If there are no _LPI states at the first level, there are no _LPI + * states at all. + */ + if (!state_count) + return -ENODATA; + + prev = curr; + curr = &info[1]; + + for (;;) { + struct acpi_lpi_states_array *tmp; + struct acpi_device *d; + + if (ACPI_FAILURE(acpi_get_parent(handle, &handle))) + break; + + d = acpi_fetch_acpi_dev(handle); + if (!d) + break; + + if (strcmp(acpi_device_hid(d), ACPI_PROCESSOR_CONTAINER_HID)) + break; + + curr->composite_states_size = 0; + + ret = acpi_processor_evaluate_lpi(handle, curr, strict); + if (ret) + break; + + /* flatten all the LPI states in this level of hierarchy */ + state_count = flatten_lpi_states(pr_handle, pr_power->lpi_states, + state_count, curr, prev); + + kfree(curr->entries); + + tmp = prev, prev = curr, curr = tmp; + } + + /* reset the index after flattening */ + for (i = 0; i < state_count; i++) + pr_power->lpi_states[i].index = i; + + pr_power->count = state_count; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(acpi_processor_extract_lpi_info, "ACPI_PROCESSOR_IDLE"); +#endif /* CONFIG_ACPI_PROCESSOR_IDLE */ diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index f93e877f87f6..4d6fd9f6e9ad 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -1048,7 +1048,7 @@ static int acpi_video_bus_check(struct acpi_video_bus *video) if (!video) return -EINVAL; - dev = acpi_get_pci_dev(video->device->handle); + dev = acpi_dev_get_pci_dev(video->device); if (!dev) return -ENODEV; pci_dev_put(dev); @@ -1702,7 +1702,6 @@ static void acpi_video_dev_register_backlight(struct acpi_video_device *device) { struct backlight_properties props; struct pci_dev *pdev; - acpi_handle acpi_parent; struct device *parent = NULL; int result; static int count; @@ -1717,13 +1716,9 @@ static void acpi_video_dev_register_backlight(struct acpi_video_device *device) return; count++; - if (ACPI_SUCCESS(acpi_get_parent(device->dev->handle, &acpi_parent))) { - pdev = acpi_get_pci_dev(acpi_parent); - if (pdev) { - parent = &pdev->dev; - pci_dev_put(pdev); - } - } + pdev = acpi_dev_get_pci_dev(acpi_dev_parent(device->dev)); + if (pdev) + parent = &pdev->dev; memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_FIRMWARE; @@ -1734,6 +1729,7 @@ static void acpi_video_dev_register_backlight(struct acpi_video_device *device) device, &acpi_backlight_ops, &props); + put_device(parent); kfree(name); if (IS_ERR(device->backlight)) { device->backlight = NULL; diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 3236a3ce79d6..48f915ee5a0d 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -1397,8 +1397,8 @@ no_work: return rc; } -static int ghes_in_nmi_spool_from_list(struct list_head *rcu_list, - enum fixed_addresses fixmap_idx) +static int __maybe_unused ghes_in_nmi_spool_from_list(struct list_head *rcu_list, + enum fixed_addresses fixmap_idx) { int ret = -ENOENT; struct ghes *ghes; diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c index af7a9b2fd5bc..34412cd697d8 100644 --- a/drivers/acpi/arm64/iort.c +++ b/drivers/acpi/arm64/iort.c @@ -789,11 +789,9 @@ struct irq_domain *iort_get_device_domain(struct device *dev, u32 id, return irq_find_matching_fwnode(handle, bus_token); } -struct fwnode_handle *iort_iwb_handle(u32 iwb_id) +acpi_handle iort_iwb_handle(u32 iwb_id) { - struct fwnode_handle *fwnode; struct acpi_iort_node *node; - struct acpi_device *device; struct acpi_iort_iwb *iwb; acpi_status status; acpi_handle handle; @@ -808,6 +806,19 @@ struct fwnode_handle *iort_iwb_handle(u32 iwb_id) if (ACPI_FAILURE(status)) return NULL; + return handle; +} + +struct fwnode_handle *iort_iwb_handle_fwnode(u32 iwb_id) +{ + struct fwnode_handle *fwnode; + struct acpi_device *device; + acpi_handle handle; + + handle = iort_iwb_handle(iwb_id); + if (!handle) + return NULL; + device = acpi_get_acpi_dev(handle); if (!device) return NULL; @@ -2090,6 +2101,11 @@ static void __init iort_init_platform_devices(void) } } +u32 arch_acpi_add_auto_dep(acpi_handle handle) +{ + return acpi_irq_add_auto_dep(handle); +} + void __init acpi_iort_init(void) { acpi_status status; 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 <linux/ctype.h> #include <linux/delay.h> #include <linux/dmi.h> #include <linux/jiffies.h> #include <linux/kernel.h> +#include <linux/kfifo.h> #include <linux/list.h> #include <linux/module.h> #include <linux/mutex.h> @@ -21,6 +23,7 @@ #include <linux/slab.h> #include <linux/suspend.h> #include <linux/types.h> +#include <linux/workqueue.h> #include <linux/unaligned.h> @@ -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 <astarikovskiy@suse.de>"); 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 int acpi_battery_is_charged(struct acpi_battery *battery) +static bool acpi_battery_is_full(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; + return false; /* good batteries update full_charge as the batteries degrade */ if (battery->full_charge_capacity == battery->capacity_now) - return 1; + return true; /* fallback to using design values for broken batteries */ - if (battery->design_capacity <= battery->capacity_now) - return 1; + return battery->design_capacity <= battery->capacity_now; +} - /* we don't do any sort of metric based on percentages */ - return 0; +static int acpi_battery_is_charged(struct acpi_battery *battery) +{ + /* charging, discharging, critical low or charge limited */ + if (battery->state != 0) + 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; + + err = kstrtoul(buf, 10, &x); + if (err) + return err; - if (sscanf(buf, "%lu\n", &x) == 1) - battery->alarm = x/1000; + 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/bus.c b/drivers/acpi/bus.c index a30a904f6535..fae79cdd3610 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -620,41 +620,6 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data) acpi_evaluate_ost(handle, type, ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL); } -static void acpi_notify_device(acpi_handle handle, u32 event, void *data) -{ - struct acpi_device *device = data; - struct acpi_driver *acpi_drv = to_acpi_driver(device->dev.driver); - - acpi_drv->ops.notify(device, event); -} - -static int acpi_device_install_notify_handler(struct acpi_device *device, - struct acpi_driver *acpi_drv) -{ - u32 type = acpi_drv->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS ? - ACPI_ALL_NOTIFY : ACPI_DEVICE_NOTIFY; - acpi_status status; - - status = acpi_install_notify_handler(device->handle, type, - acpi_notify_device, device); - if (ACPI_FAILURE(status)) - return -EINVAL; - - return 0; -} - -static void acpi_device_remove_notify_handler(struct acpi_device *device, - struct acpi_driver *acpi_drv) -{ - u32 type = acpi_drv->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS ? - ACPI_ALL_NOTIFY : ACPI_DEVICE_NOTIFY; - - acpi_remove_notify_handler(device->handle, type, - acpi_notify_device); - - acpi_os_wait_events_complete(); -} - int acpi_dev_install_notify_handler(struct acpi_device *adev, u32 handler_type, acpi_notify_handler handler, void *context) @@ -1122,56 +1087,12 @@ bool acpi_driver_match_device(struct device *dev, EXPORT_SYMBOL_GPL(acpi_driver_match_device); /* -------------------------------------------------------------------------- - ACPI Driver Management - -------------------------------------------------------------------------- */ - -/** - * __acpi_bus_register_driver - register a driver with the ACPI bus - * @driver: driver being registered - * @owner: owning module/driver - * - * Registers a driver with the ACPI bus. Searches the namespace for all - * devices that match the driver's criteria and binds. Returns zero for - * success or a negative error status for failure. - */ -int __acpi_bus_register_driver(struct acpi_driver *driver, struct module *owner) -{ - if (acpi_disabled) - return -ENODEV; - driver->drv.name = driver->name; - driver->drv.bus = &acpi_bus_type; - driver->drv.owner = owner; - - return driver_register(&driver->drv); -} - -EXPORT_SYMBOL(__acpi_bus_register_driver); - -/** - * acpi_bus_unregister_driver - unregisters a driver with the ACPI bus - * @driver: driver to unregister - * - * Unregisters a driver with the ACPI bus. Searches the namespace for all - * devices that match the driver's criteria and unbinds. - */ -void acpi_bus_unregister_driver(struct acpi_driver *driver) -{ - driver_unregister(&driver->drv); -} - -EXPORT_SYMBOL(acpi_bus_unregister_driver); - -/* -------------------------------------------------------------------------- ACPI Bus operations -------------------------------------------------------------------------- */ static int acpi_bus_match(struct device *dev, const struct device_driver *drv) { - struct acpi_device *acpi_dev = to_acpi_device(dev); - const struct acpi_driver *acpi_drv = to_acpi_driver(drv); - - return acpi_dev->flags.match_driver - && !acpi_match_device_ids(acpi_dev, acpi_drv->ids); + return 0; } static int acpi_device_uevent(const struct device *dev, struct kobj_uevent_env *env) @@ -1179,66 +1100,9 @@ static int acpi_device_uevent(const struct device *dev, struct kobj_uevent_env * return __acpi_device_uevent_modalias(to_acpi_device(dev), env); } -static int acpi_device_probe(struct device *dev) -{ - struct acpi_device *acpi_dev = to_acpi_device(dev); - struct acpi_driver *acpi_drv = to_acpi_driver(dev->driver); - int ret; - - if (acpi_dev->handler && !acpi_is_pnp_device(acpi_dev)) - return -EINVAL; - - if (!acpi_drv->ops.add) - return -ENOSYS; - - ret = acpi_drv->ops.add(acpi_dev); - if (ret) { - acpi_dev->driver_data = NULL; - return ret; - } - - pr_debug("Driver [%s] successfully bound to device [%s]\n", - acpi_drv->name, acpi_dev->pnp.bus_id); - - if (acpi_drv->ops.notify) { - ret = acpi_device_install_notify_handler(acpi_dev, acpi_drv); - if (ret) { - if (acpi_drv->ops.remove) - acpi_drv->ops.remove(acpi_dev); - - acpi_dev->driver_data = NULL; - return ret; - } - } - - pr_debug("Found driver [%s] for device [%s]\n", acpi_drv->name, - acpi_dev->pnp.bus_id); - - get_device(dev); - return 0; -} - -static void acpi_device_remove(struct device *dev) -{ - struct acpi_device *acpi_dev = to_acpi_device(dev); - struct acpi_driver *acpi_drv = to_acpi_driver(dev->driver); - - if (acpi_drv->ops.notify) - acpi_device_remove_notify_handler(acpi_dev, acpi_drv); - - if (acpi_drv->ops.remove) - acpi_drv->ops.remove(acpi_dev); - - acpi_dev->driver_data = NULL; - - put_device(dev); -} - const struct bus_type acpi_bus_type = { .name = "acpi", .match = acpi_bus_match, - .probe = acpi_device_probe, - .remove = acpi_device_remove, .uevent = acpi_device_uevent, }; diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index b6356476a46e..9f1e7c16a092 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -475,17 +475,29 @@ bool acpi_cpc_valid(void) } EXPORT_SYMBOL_GPL(acpi_cpc_valid); -bool cppc_allow_fast_switch(void) +bool cppc_allow_fast_switch(const struct cpumask *cpus) { - struct cpc_register_resource *desired_reg; + struct cpc_register_resource *desired_reg, *min_reg, *max_reg; struct cpc_desc *cpc_ptr; int cpu; - for_each_online_cpu(cpu) { + for_each_cpu(cpu, cpus) { cpc_ptr = per_cpu(cpc_desc_ptr, cpu); + if (!cpc_ptr) + return false; desired_reg = &cpc_ptr->cpc_regs[DESIRED_PERF]; - if (!CPC_IN_SYSTEM_MEMORY(desired_reg) && - !CPC_IN_SYSTEM_IO(desired_reg)) + min_reg = &cpc_ptr->cpc_regs[MIN_PERF]; + max_reg = &cpc_ptr->cpc_regs[MAX_PERF]; + + if (!CPC_SUPPORTED(desired_reg) || + (!CPC_IN_SYSTEM_MEMORY(desired_reg) && + !CPC_IN_SYSTEM_IO(desired_reg)) || + (CPC_SUPPORTED(min_reg) && + !CPC_IN_SYSTEM_MEMORY(min_reg) && + !CPC_IN_SYSTEM_IO(min_reg)) || + (CPC_SUPPORTED(max_reg) && + !CPC_IN_SYSTEM_MEMORY(max_reg) && + !CPC_IN_SYSTEM_IO(max_reg))) return false; } 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 <linux/kconfig.h> +#include <linux/device.h> #include <linux/limits.h> +#include <linux/types.h> -#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/irq.c b/drivers/acpi/irq.c index d1595156c86a..e4293458bf61 100644 --- a/drivers/acpi/irq.c +++ b/drivers/acpi/irq.c @@ -13,6 +13,7 @@ enum acpi_irq_model_id acpi_irq_model; static acpi_gsi_domain_disp_fn acpi_get_gsi_domain_id; +static acpi_gsi_handle_disp_fn acpi_get_gsi_handle; static u32 (*acpi_gsi_to_irq_fallback)(u32 gsi); /** @@ -321,15 +322,19 @@ const struct cpumask *acpi_irq_get_affinity(acpi_handle handle, /** * acpi_set_irq_model - Setup the GSI irqdomain information - * @model: the value assigned to acpi_irq_model - * @fn: a dispatcher function that will return the domain fwnode - * for a given GSI + * @model: the value assigned to acpi_irq_model + * @fn: a dispatcher function that will return the domain fwnode + * for a given GSI + * @gsi_dep_fn: a function to retrieve the acpi_handle a GSI interrupt is + * dependent on + * */ void __init acpi_set_irq_model(enum acpi_irq_model_id model, - acpi_gsi_domain_disp_fn fn) + acpi_gsi_domain_disp_fn fn, acpi_gsi_handle_disp_fn gsi_dep_fn) { acpi_irq_model = model; acpi_get_gsi_domain_id = fn; + acpi_get_gsi_handle = gsi_dep_fn; } /* @@ -385,3 +390,162 @@ struct irq_domain *acpi_irq_create_hierarchy(unsigned int flags, host_data); } EXPORT_SYMBOL_GPL(acpi_irq_create_hierarchy); + +struct acpi_irq_dep_ctx { + int rc; + unsigned int index; + acpi_handle handle; +}; + +static acpi_status acpi_irq_get_parent(struct acpi_resource *ares, void *context) +{ + struct acpi_irq_dep_ctx *ctx = context; + struct acpi_resource_irq *irq; + struct acpi_resource_extended_irq *eirq; + + switch (ares->type) { + case ACPI_RESOURCE_TYPE_IRQ: + irq = &ares->data.irq; + if (ctx->index >= irq->interrupt_count) { + ctx->index -= irq->interrupt_count; + return AE_OK; + } + ctx->handle = acpi_get_gsi_handle(irq->interrupts[ctx->index]); + ctx->rc = 0; + return AE_CTRL_TERMINATE; + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + eirq = &ares->data.extended_irq; + if (eirq->producer_consumer == ACPI_PRODUCER) + return AE_OK; + + if (ctx->index >= eirq->interrupt_count) { + ctx->index -= eirq->interrupt_count; + return AE_OK; + } + + /* Support GSIs only */ + if (eirq->resource_source.string_length) + return AE_OK; + + ctx->handle = acpi_get_gsi_handle(eirq->interrupts[ctx->index]); + ctx->rc = 0; + return AE_CTRL_TERMINATE; + } + + return AE_OK; +} + +static int acpi_irq_get_dep(acpi_handle handle, unsigned int index, acpi_handle *gsi_handle) +{ + struct acpi_irq_dep_ctx ctx = {-EINVAL, index, NULL}; + + if (!gsi_handle) + return -EINVAL; + + acpi_walk_resources(handle, METHOD_NAME__CRS, acpi_irq_get_parent, &ctx); + *gsi_handle = ctx.handle; + + return ctx.rc; +} + +static bool acpi_prt_entry_valid(void *prt_entry) +{ + struct acpi_pci_routing_table *entry = prt_entry; + + return entry && entry->length > 0; +} + +static void *acpi_prt_next_entry(void *prt_entry) +{ + struct acpi_pci_routing_table *entry = prt_entry; + + return prt_entry + entry->length; +} + +static u32 acpi_add_prt_dep(acpi_handle handle) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_pci_routing_table *entry; + struct acpi_handle_list dep_devices; + acpi_handle gsi_handle; + acpi_handle link_handle; + acpi_status status; + u32 count = 0; + + status = acpi_get_irq_routing_table(handle, &buffer); + if (ACPI_FAILURE(status)) { + acpi_handle_err(handle, "failed to get IRQ routing table\n"); + kfree(buffer.pointer); + return 0; + } + + entry = buffer.pointer; + for (; acpi_prt_entry_valid(entry); entry = acpi_prt_next_entry(entry)) { + if (entry->source[0]) { + status = acpi_get_handle(handle, entry->source, &link_handle); + if (ACPI_FAILURE(status)) + continue; + dep_devices.count = 1; + dep_devices.handles = kcalloc(1, sizeof(*dep_devices.handles), GFP_KERNEL); + if (!dep_devices.handles) { + acpi_handle_err(handle, "failed to allocate memory\n"); + continue; + } + + dep_devices.handles[0] = link_handle; + count += acpi_scan_add_dep(handle, &dep_devices); + } else { + gsi_handle = acpi_get_gsi_handle(entry->source_index); + if (!gsi_handle) + continue; + dep_devices.count = 1; + dep_devices.handles = kcalloc(1, sizeof(*dep_devices.handles), GFP_KERNEL); + if (!dep_devices.handles) { + acpi_handle_err(handle, "failed to allocate memory\n"); + continue; + } + + dep_devices.handles[0] = gsi_handle; + count += acpi_scan_add_dep(handle, &dep_devices); + } + } + + kfree(buffer.pointer); + return count; +} + +static u32 acpi_add_irq_dep(acpi_handle handle) +{ + struct acpi_handle_list dep_devices; + acpi_handle gsi_handle; + u32 count = 0; + int i; + + for (i = 0; !acpi_irq_get_dep(handle, i, &gsi_handle); i++) { + if (!gsi_handle) + continue; + + dep_devices.count = 1; + dep_devices.handles = kcalloc(1, sizeof(*dep_devices.handles), GFP_KERNEL); + if (!dep_devices.handles) { + acpi_handle_err(handle, "failed to allocate memory\n"); + continue; + } + + dep_devices.handles[0] = gsi_handle; + count += acpi_scan_add_dep(handle, &dep_devices); + } + + return count; +} + +u32 acpi_irq_add_auto_dep(acpi_handle handle) +{ + if (!acpi_get_gsi_handle) + return 0; + + if (acpi_has_method(handle, "_PRT")) + return acpi_add_prt_dep(handle); + + return acpi_add_irq_dep(handle); +} diff --git a/drivers/acpi/numa/srat.c b/drivers/acpi/numa/srat.c index 62d4a8df0b8c..5c407dc6401e 100644 --- a/drivers/acpi/numa/srat.c +++ b/drivers/acpi/numa/srat.c @@ -399,8 +399,6 @@ acpi_parse_memory_affinity(union acpi_subtable_headers *header, goto out_err_bad_srat; } - node_set(node, numa_nodes_parsed); - pr_info("SRAT: Node %u PXM %u [mem %#010Lx-%#010Lx]%s%s\n", node, pxm, (unsigned long long) start, (unsigned long long) end - 1, diff --git a/drivers/acpi/nvs.c b/drivers/acpi/nvs.c index 6eaad7dd0241..12aee4102696 100644 --- a/drivers/acpi/nvs.c +++ b/drivers/acpi/nvs.c @@ -133,7 +133,7 @@ void suspend_nvs_free(void) list_for_each_entry(entry, &nvs_list, node) if (entry->data) { - free_page((unsigned long)entry->data); + kfree(entry->data); entry->data = NULL; if (entry->kaddr) { if (entry->unmap) { @@ -156,7 +156,7 @@ int suspend_nvs_alloc(void) struct nvs_page *entry; list_for_each_entry(entry, &nvs_list, node) { - entry->data = (void *)__get_free_page(GFP_KERNEL); + entry->data = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!entry->data) { suspend_nvs_free(); return -ENOMEM; diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 4c06c3ffd0cb..6f78f96332ea 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -8,6 +8,7 @@ #define pr_fmt(fmt) "ACPI: " fmt +#include <linux/cleanup.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -292,41 +293,37 @@ struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle) EXPORT_SYMBOL_GPL(acpi_pci_find_root); /** - * acpi_get_pci_dev - convert ACPI CA handle to struct pci_dev - * @handle: the handle in question + * acpi_dev_get_pci_dev - Get a struct pci_dev for a given ACPI device + * @adev: Target ACPI device. * - * Given an ACPI CA handle, the desired PCI device is located in the - * list of PCI devices. + * Find the PCI device associated with @adev, if any, and bump up its reference + * counter. * - * If the device is found, its reference count is increased and this - * function returns a pointer to its data structure. The caller must - * decrement the reference count by calling pci_dev_put(). - * If no device is found, %NULL is returned. + * Callers are responsible for dropping the PCI device reference obtained by + * this function. + * + * Return: The struct pci_dev pointer of a reference-counted PCI device on + * success or NULL on failure. */ -struct pci_dev *acpi_get_pci_dev(acpi_handle handle) +struct pci_dev *acpi_dev_get_pci_dev(struct acpi_device *adev) { - struct acpi_device *adev = acpi_fetch_acpi_dev(handle); struct acpi_device_physical_node *pn; - struct pci_dev *pci_dev = NULL; if (!adev) return NULL; - mutex_lock(&adev->physical_node_lock); + guard(mutex)(&adev->physical_node_lock); list_for_each_entry(pn, &adev->physical_node_list, node) { if (dev_is_pci(pn->dev)) { get_device(pn->dev); - pci_dev = to_pci_dev(pn->dev); - break; + return to_pci_dev(pn->dev); } } - mutex_unlock(&adev->physical_node_lock); - - return pci_dev; + return NULL; } -EXPORT_SYMBOL_GPL(acpi_get_pci_dev); +EXPORT_SYMBOL_GPL(acpi_dev_get_pci_dev); /** * acpi_pci_osc_control_set - Request control of PCI root _OSC features. @@ -725,7 +722,6 @@ static int acpi_pci_root_add(struct acpi_device *device, dev_err(&device->dev, "Bus %04x:%02x not present in PCI namespace\n", root->segment, (unsigned int)root->secondary.start); - device->driver_data = NULL; result = -ENODEV; goto remove_dmar; } @@ -765,6 +761,7 @@ remove_dmar: if (hotadd) dmar_device_remove(handle); end: + device->driver_data = NULL; kfree(root); return result; } @@ -788,6 +785,7 @@ static void acpi_pci_root_remove(struct acpi_device *device) pci_unlock_rescan_remove(); + device->driver_data = NULL; kfree(root); } diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index d4131c184be8..23a4e207a01e 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -954,7 +954,6 @@ struct acpi_device *acpi_add_power_resource(acpi_handle handle) INIT_LIST_HEAD(&resource->list_node); INIT_LIST_HEAD(&resource->dependents); device->power.state = ACPI_STATE_UNKNOWN; - device->flags.match_driver = true; /* Evaluate the object to get the system level and resource order. */ status = acpi_evaluate_object(handle, NULL, NULL, &buffer); 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_idle.c b/drivers/acpi/processor_idle.c index 4482cf28f56a..e113bcbbb882 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -853,223 +853,6 @@ static int acpi_processor_setup_cstates(struct acpi_processor *pr) #endif /* CONFIG_ACPI_PROCESSOR_CSTATE */ -struct acpi_lpi_states_array { - unsigned int size; - unsigned int composite_states_size; - struct acpi_lpi_state *entries; - struct acpi_lpi_state *composite_states[ACPI_PROCESSOR_MAX_POWER]; -}; - -static int obj_get_integer(union acpi_object *obj, u32 *value) -{ - if (obj->type != ACPI_TYPE_INTEGER) - return -EINVAL; - - *value = obj->integer.value; - return 0; -} - -static int acpi_processor_evaluate_lpi(acpi_handle handle, - struct acpi_lpi_states_array *info) -{ - acpi_status status; - int ret = 0; - int pkg_count, state_idx = 1, loop; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *lpi_data; - struct acpi_lpi_state *lpi_state; - - status = acpi_evaluate_object(handle, "_LPI", NULL, &buffer); - if (ACPI_FAILURE(status)) { - acpi_handle_debug(handle, "No _LPI, giving up\n"); - return -ENODEV; - } - - lpi_data = buffer.pointer; - - /* There must be at least 4 elements = 3 elements + 1 package */ - if (!lpi_data || lpi_data->type != ACPI_TYPE_PACKAGE || - lpi_data->package.count < 4) { - pr_debug("not enough elements in _LPI\n"); - ret = -ENODATA; - goto end; - } - - pkg_count = lpi_data->package.elements[2].integer.value; - - /* Validate number of power states. */ - if (pkg_count < 1 || pkg_count != lpi_data->package.count - 3) { - pr_debug("count given by _LPI is not valid\n"); - ret = -ENODATA; - goto end; - } - - lpi_state = kzalloc_objs(*lpi_state, pkg_count); - if (!lpi_state) { - ret = -ENOMEM; - goto end; - } - - info->size = pkg_count; - info->entries = lpi_state; - - /* LPI States start at index 3 */ - for (loop = 3; state_idx <= pkg_count; loop++, state_idx++, lpi_state++) { - union acpi_object *element, *pkg_elem, *obj; - - element = &lpi_data->package.elements[loop]; - if (element->type != ACPI_TYPE_PACKAGE || element->package.count < 7) - continue; - - pkg_elem = element->package.elements; - - obj = pkg_elem + 6; - if (obj->type == ACPI_TYPE_BUFFER) { - struct acpi_power_register *reg; - - reg = (struct acpi_power_register *)obj->buffer.pointer; - if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO && - reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) - continue; - - lpi_state->address = reg->address; - lpi_state->entry_method = - reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE ? - ACPI_CSTATE_FFH : ACPI_CSTATE_SYSTEMIO; - } else if (obj->type == ACPI_TYPE_INTEGER) { - lpi_state->entry_method = ACPI_CSTATE_INTEGER; - lpi_state->address = obj->integer.value; - } else { - pr_debug("Entry method of state-%d is invalid, disable it.\n", - state_idx); - continue; - } - - /* elements[7,8] skipped for now i.e. Residency/Usage counter*/ - - obj = pkg_elem + 9; - if (obj->type == ACPI_TYPE_STRING) - strscpy(lpi_state->desc, obj->string.pointer, - ACPI_CX_DESC_LEN); - - lpi_state->index = state_idx; - if (obj_get_integer(pkg_elem + 0, &lpi_state->min_residency)) { - pr_debug("No min. residency found, assuming 10 us\n"); - lpi_state->min_residency = 10; - } - - if (obj_get_integer(pkg_elem + 1, &lpi_state->wake_latency)) { - pr_debug("No wakeup residency found, assuming 10 us\n"); - lpi_state->wake_latency = 10; - } - - if (obj_get_integer(pkg_elem + 2, &lpi_state->flags)) - lpi_state->flags = 0; - - if (obj_get_integer(pkg_elem + 3, &lpi_state->arch_flags)) - lpi_state->arch_flags = 0; - - if (obj_get_integer(pkg_elem + 4, &lpi_state->res_cnt_freq)) - lpi_state->res_cnt_freq = 1; - - if (obj_get_integer(pkg_elem + 5, &lpi_state->enable_parent_state)) - lpi_state->enable_parent_state = 0; - } - - acpi_handle_debug(handle, "Found %d power states\n", state_idx); -end: - kfree(buffer.pointer); - return ret; -} - -/** - * combine_lpi_states - combine local and parent LPI states to form a composite LPI state - * - * @local: local LPI state - * @parent: parent LPI state - * @result: composite LPI state - */ -static bool combine_lpi_states(struct acpi_lpi_state *local, - struct acpi_lpi_state *parent, - struct acpi_lpi_state *result) -{ - if (parent->entry_method == ACPI_CSTATE_INTEGER) { - if (!parent->address) /* 0 means autopromotable */ - return false; - result->address = local->address + parent->address; - } else { - result->address = parent->address; - } - - result->min_residency = max(local->min_residency, parent->min_residency); - result->wake_latency = local->wake_latency + parent->wake_latency; - result->enable_parent_state = parent->enable_parent_state; - result->entry_method = local->entry_method; - - result->flags = parent->flags; - result->arch_flags = parent->arch_flags; - result->index = parent->index; - - scnprintf(result->desc, ACPI_CX_DESC_LEN, "%s+%s", local->desc, parent->desc); - return true; -} - -#define ACPI_LPI_STATE_FLAGS_ENABLED BIT(0) - -static void stash_composite_state(struct acpi_lpi_states_array *curr_level, - struct acpi_lpi_state *t) -{ - curr_level->composite_states[curr_level->composite_states_size++] = t; -} - -static unsigned int flatten_lpi_states(struct acpi_processor *pr, - unsigned int flat_state_cnt, - struct acpi_lpi_states_array *curr_level, - struct acpi_lpi_states_array *prev_level) -{ - int i, j, state_count = curr_level->size; - struct acpi_lpi_state *p, *t = curr_level->entries; - - curr_level->composite_states_size = 0; - for (j = 0; j < state_count; j++, t++) { - struct acpi_lpi_state *flpi; - - if (!(t->flags & ACPI_LPI_STATE_FLAGS_ENABLED)) - continue; - - if (flat_state_cnt >= ACPI_PROCESSOR_MAX_POWER) { - pr_warn("Limiting number of LPI states to max (%d)\n", - ACPI_PROCESSOR_MAX_POWER); - pr_warn("Please increase ACPI_PROCESSOR_MAX_POWER if needed.\n"); - break; - } - - flpi = &pr->power.lpi_states[flat_state_cnt]; - - if (!prev_level) { /* leaf/processor node */ - memcpy(flpi, t, sizeof(*t)); - stash_composite_state(curr_level, flpi); - flat_state_cnt++; - continue; - } - - for (i = 0; i < prev_level->composite_states_size; i++) { - p = prev_level->composite_states[i]; - if (t->index <= p->enable_parent_state && - combine_lpi_states(p, t, flpi)) { - stash_composite_state(curr_level, flpi); - flat_state_cnt++; - flpi++; - if (flat_state_cnt >= ACPI_PROCESSOR_MAX_POWER) - break; - } - } - } - - kfree(curr_level->entries); - return flat_state_cnt; -} - int __weak acpi_processor_ffh_lpi_probe(unsigned int cpu) { return -EOPNOTSUPP; @@ -1077,64 +860,16 @@ int __weak acpi_processor_ffh_lpi_probe(unsigned int cpu) static int acpi_processor_get_lpi_info(struct acpi_processor *pr) { - int ret, i; - acpi_status status; - acpi_handle handle = pr->handle, pr_ahandle; - struct acpi_device *d = NULL; - struct acpi_lpi_states_array info[2], *tmp, *prev, *curr; - unsigned int state_count; + int ret; /* make sure our architecture has support */ ret = acpi_processor_ffh_lpi_probe(pr->id); if (ret == -EOPNOTSUPP) return ret; - if (!osc_pc_lpi_support_confirmed) - return -EOPNOTSUPP; - - if (!acpi_has_method(handle, "_LPI")) - return -EINVAL; - - prev = &info[0]; - curr = &info[1]; - handle = pr->handle; - ret = acpi_processor_evaluate_lpi(handle, prev); + ret = acpi_processor_extract_lpi_info(pr->handle, &pr->power, false); if (ret) return ret; - state_count = flatten_lpi_states(pr, 0, prev, NULL); - - status = acpi_get_parent(handle, &pr_ahandle); - while (ACPI_SUCCESS(status)) { - d = acpi_fetch_acpi_dev(pr_ahandle); - if (!d) - break; - - handle = pr_ahandle; - - if (strcmp(acpi_device_hid(d), ACPI_PROCESSOR_CONTAINER_HID)) - break; - - /* can be optional ? */ - if (!acpi_has_method(handle, "_LPI")) - break; - - ret = acpi_processor_evaluate_lpi(handle, curr); - if (ret) - break; - - /* flatten all the LPI states in this level of hierarchy */ - state_count = flatten_lpi_states(pr, state_count, curr, prev); - - tmp = prev, prev = curr, curr = tmp; - - status = acpi_get_parent(handle, &pr_ahandle); - } - - /* reset the index after flattening */ - for (i = 0; i < state_count; i++) - pr->power.lpi_states[i].index = i; - - pr->power.count = state_count; /* Tell driver that _LPI is supported. */ pr->flags.has_lpi = 1; diff --git a/drivers/acpi/riscv/irq.c b/drivers/acpi/riscv/irq.c index 9b88d0993e88..da2c42e0ebfd 100644 --- a/drivers/acpi/riscv/irq.c +++ b/drivers/acpi/riscv/irq.c @@ -23,12 +23,6 @@ struct riscv_ext_intc_list { struct list_head list; }; -struct acpi_irq_dep_ctx { - int rc; - unsigned int index; - acpi_handle handle; -}; - LIST_HEAD(ext_intc_list); static int irqchip_cmp_func(const void *in0, const void *in1) @@ -254,7 +248,7 @@ void __init riscv_acpi_init_gsi_mapping(void) acpi_get_devices("RSCV0006", riscv_acpi_create_gsi_map_smsi, NULL, NULL); } -static acpi_handle riscv_acpi_get_gsi_handle(u32 gsi) +acpi_handle acpi_get_riscv_gsi_handle(u32 gsi) { struct riscv_ext_intc_list *ext_intc_element; struct list_head *i; @@ -269,138 +263,7 @@ static acpi_handle riscv_acpi_get_gsi_handle(u32 gsi) return NULL; } -static acpi_status riscv_acpi_irq_get_parent(struct acpi_resource *ares, void *context) -{ - struct acpi_irq_dep_ctx *ctx = context; - struct acpi_resource_irq *irq; - struct acpi_resource_extended_irq *eirq; - - switch (ares->type) { - case ACPI_RESOURCE_TYPE_IRQ: - irq = &ares->data.irq; - if (ctx->index >= irq->interrupt_count) { - ctx->index -= irq->interrupt_count; - return AE_OK; - } - ctx->handle = riscv_acpi_get_gsi_handle(irq->interrupts[ctx->index]); - return AE_CTRL_TERMINATE; - case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: - eirq = &ares->data.extended_irq; - if (eirq->producer_consumer == ACPI_PRODUCER) - return AE_OK; - - if (ctx->index >= eirq->interrupt_count) { - ctx->index -= eirq->interrupt_count; - return AE_OK; - } - - /* Support GSIs only */ - if (eirq->resource_source.string_length) - return AE_OK; - - ctx->handle = riscv_acpi_get_gsi_handle(eirq->interrupts[ctx->index]); - return AE_CTRL_TERMINATE; - } - - return AE_OK; -} - -static int riscv_acpi_irq_get_dep(acpi_handle handle, unsigned int index, acpi_handle *gsi_handle) -{ - struct acpi_irq_dep_ctx ctx = {-EINVAL, index, NULL}; - - if (!gsi_handle) - return 0; - - acpi_walk_resources(handle, METHOD_NAME__CRS, riscv_acpi_irq_get_parent, &ctx); - *gsi_handle = ctx.handle; - if (*gsi_handle) - return 1; - - return 0; -} - -static u32 riscv_acpi_add_prt_dep(acpi_handle handle) -{ - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - struct acpi_pci_routing_table *entry; - struct acpi_handle_list dep_devices; - acpi_handle gsi_handle; - acpi_handle link_handle; - acpi_status status; - u32 count = 0; - - status = acpi_get_irq_routing_table(handle, &buffer); - if (ACPI_FAILURE(status)) { - acpi_handle_err(handle, "failed to get IRQ routing table\n"); - kfree(buffer.pointer); - return 0; - } - - entry = buffer.pointer; - while (entry && (entry->length > 0)) { - if (entry->source[0]) { - acpi_get_handle(handle, entry->source, &link_handle); - dep_devices.count = 1; - dep_devices.handles = kzalloc_objs(*dep_devices.handles, - 1); - if (!dep_devices.handles) { - acpi_handle_err(handle, "failed to allocate memory\n"); - continue; - } - - dep_devices.handles[0] = link_handle; - count += acpi_scan_add_dep(handle, &dep_devices); - } else { - gsi_handle = riscv_acpi_get_gsi_handle(entry->source_index); - dep_devices.count = 1; - dep_devices.handles = kzalloc_objs(*dep_devices.handles, - 1); - if (!dep_devices.handles) { - acpi_handle_err(handle, "failed to allocate memory\n"); - continue; - } - - dep_devices.handles[0] = gsi_handle; - count += acpi_scan_add_dep(handle, &dep_devices); - } - - entry = (struct acpi_pci_routing_table *) - ((unsigned long)entry + entry->length); - } - - kfree(buffer.pointer); - return count; -} - -static u32 riscv_acpi_add_irq_dep(acpi_handle handle) -{ - struct acpi_handle_list dep_devices; - acpi_handle gsi_handle; - u32 count = 0; - int i; - - for (i = 0; - riscv_acpi_irq_get_dep(handle, i, &gsi_handle); - i++) { - dep_devices.count = 1; - dep_devices.handles = kzalloc_objs(*dep_devices.handles, 1); - if (!dep_devices.handles) { - acpi_handle_err(handle, "failed to allocate memory\n"); - continue; - } - - dep_devices.handles[0] = gsi_handle; - count += acpi_scan_add_dep(handle, &dep_devices); - } - - return count; -} - u32 arch_acpi_add_auto_dep(acpi_handle handle) { - if (acpi_has_method(handle, "_PRT")) - return riscv_acpi_add_prt_dep(handle); - - return riscv_acpi_add_irq_dep(handle); + return acpi_irq_add_auto_dep(handle); } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 21937912a1a5..f4cc1da48224 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -273,13 +273,9 @@ static int acpi_scan_check_and_detach(struct acpi_device *adev, void *p) } } - adev->flags.match_driver = false; - if (handler) { - if (handler->detach) - handler->detach(adev); - } else { - device_release_driver(&adev->dev); - } + if (handler && handler->detach) + handler->detach(adev); + /* * Most likely, the device is going away, so put it into D3cold before * that. @@ -854,6 +850,7 @@ static const char * const acpi_ignore_dep_ids[] = { /* List of HIDs for which we honor deps of matching ACPI devs, when checking _DEP lists. */ static const char * const acpi_honor_dep_ids[] = { + "ARMH0003", /* ARM GICv5 IWB */ "INT3472", /* Camera sensor PMIC / clk and regulator info */ "INTC1059", /* IVSC (TGL) driver must be loaded to allow i2c access to camera sensors */ "INTC1095", /* IVSC (ADL) driver must be loaded to allow i2c access to camera sensors */ @@ -1769,7 +1766,6 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device) * Some ACPI devs contain SerialBus resources even though they are not * attached to a serial bus at all. */ - {ACPI_VIDEO_HID, }, {"MSHW0028", }, /* * HIDs of device with an UartSerialBusV2 resource for which userspace @@ -1792,6 +1788,9 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device) fwnode_property_present(&device->fwnode, "baud"))) return true; + if (acpi_dev_is_video_device(device)) + return false; + if (!acpi_match_device_ids(device, ignore_serial_bus_ids)) return false; @@ -1816,13 +1815,13 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, device->dev.release = release; device->dev.bus = &acpi_bus_type; device->dev.groups = acpi_groups; + device_set_pm_not_required(&device->dev); fwnode_init(&device->fwnode, &acpi_device_fwnode_ops); acpi_set_device_status(device, ACPI_STA_DEFAULT); acpi_device_get_busid(device); acpi_set_pnp_ids(handle, &device->pnp, type); acpi_init_properties(device); acpi_bus_get_flags(device); - device->flags.match_driver = false; device->flags.initialized = true; device->flags.enumeration_by_parent = acpi_device_enumeration_by_parent(device); @@ -2376,16 +2375,11 @@ static int acpi_bus_attach(struct acpi_device *device, void *first_pass) if (ret < 0) return 0; - device->flags.match_driver = true; if (ret > 0 && !device->flags.enumeration_by_parent) { acpi_device_set_enumerated(device); goto ok; } - ret = device_attach(&device->dev); - if (ret < 0) - return 0; - if (device->pnp.type.platform_id || device->pnp.type.backlight || device->flags.enumeration_by_parent) acpi_default_enumeration(device); diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index 908cc5c7e643..dd4a99f09efe 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -316,6 +316,7 @@ struct acpi_table_attr { struct acpi_data_attr { struct bin_attribute attr; u64 addr; + char filename[ACPI_NAMESEG_SIZE+ACPI_INST_SIZE]; }; static ssize_t acpi_table_show(struct file *filp, struct kobject *kobj, @@ -447,13 +448,12 @@ static int acpi_bert_data_init(void *th, struct acpi_data_attr *data_attr) struct acpi_table_bert *bert = th; if (bert->header.length < sizeof(struct acpi_table_bert) || - bert->region_length < sizeof(struct acpi_hest_generic_status)) { + bert->region_length < sizeof(struct acpi_bert_region)) { kfree(data_attr); return -EINVAL; } data_attr->addr = bert->address; data_attr->attr.size = bert->region_length; - data_attr->attr.attr.name = "BERT"; return sysfs_create_bin_file(tables_data_kobj, &data_attr->attr); } @@ -469,7 +469,6 @@ static int acpi_ccel_data_init(void *th, struct acpi_data_attr *data_attr) } data_attr->addr = ccel->log_area_start_address; data_attr->attr.size = ccel->log_area_minimum_length; - data_attr->attr.attr.name = "CCEL"; return sysfs_create_bin_file(tables_data_kobj, &data_attr->attr); } @@ -484,7 +483,7 @@ static struct acpi_data_obj { #define NUM_ACPI_DATA_OBJS ARRAY_SIZE(acpi_data_objs) -static int acpi_table_data_init(struct acpi_table_header *th) +static int acpi_table_data_init(struct acpi_table_header *th, struct acpi_table_attr *table_attr) { struct acpi_data_attr *data_attr; int i; @@ -497,6 +496,8 @@ static int acpi_table_data_init(struct acpi_table_header *th) sysfs_attr_init(&data_attr->attr.attr); data_attr->attr.read = acpi_data_show; data_attr->attr.attr.mode = 0400; + strscpy(data_attr->filename, table_attr->filename); + data_attr->attr.attr.name = data_attr->filename; return acpi_data_objs[i].fn(th, data_attr); } } @@ -543,7 +544,7 @@ static int acpi_tables_sysfs_init(void) return ret; } list_add_tail(&table_attr->node, &acpi_table_attr_list); - acpi_table_data_init(table_header); + acpi_table_data_init(table_header, table_attr); } kobject_uevent(tables_kobj, KOBJ_ADD); diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index 6ab27e4826d1..d499b72574ab 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -1048,6 +1048,23 @@ static int __init acpi_backlight(char *str) __setup("acpi_backlight=", acpi_backlight); /** + * acpi_dev_is_video_device - test if device matches against ACPI video device IDs + * @adev: ACPI device to test + * + * Return: true when matches, otherwise false. + */ +bool acpi_dev_is_video_device(struct acpi_device *adev) +{ + static const struct acpi_device_id video_device_ids[] = { + { .id = ACPI_VIDEO_HID }, + { } + }; + + return adev && !acpi_match_device_ids(adev, video_device_ids); +} +EXPORT_SYMBOL(acpi_dev_is_video_device); + +/** * acpi_match_platform_list - Check if the system matches with a given list * @plat: pointer to acpi_platform_list table terminated by a NULL entry * diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index 458efa4fe9d4..5b0dc679e322 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -67,13 +67,8 @@ find_video(acpi_handle handle, u32 lvl, void *context, void **rv) long *cap = context; struct pci_dev *dev; - static const struct acpi_device_id video_ids[] = { - {ACPI_VIDEO_HID, 0}, - {"", 0}, - }; - - if (acpi_dev && !acpi_match_device_ids(acpi_dev, video_ids)) { - dev = acpi_get_pci_dev(handle); + if (acpi_dev_is_video_device(acpi_dev)) { + dev = acpi_dev_get_pci_dev(acpi_dev); if (!dev) return AE_OK; pci_dev_put(dev); diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 335288e8b5b3..fab38bc98113 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -469,9 +469,6 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev) if (retval == -EACCES) retval = -EAGAIN; - if (retval != -EAGAIN && retval != -EBUSY) - dev->power.runtime_error = retval; - return retval; } @@ -751,6 +748,9 @@ static int rpm_suspend(struct device *dev, int rpmflags) dev->power.deferred_resume = false; wake_up_all(&dev->power.wait_queue); + if (retval != -EAGAIN && retval != -EBUSY) + dev->power.runtime_error = retval; + /* * On transient errors, if the callback routine failed an autosuspend, * and if the last_busy time has been updated so that there is a new diff --git a/drivers/cpufreq/amd-pstate-ut.c b/drivers/cpufreq/amd-pstate-ut.c index 735b29f76438..2142838ad6cc 100644 --- a/drivers/cpufreq/amd-pstate-ut.c +++ b/drivers/cpufreq/amd-pstate-ut.c @@ -560,6 +560,11 @@ out: static int __init amd_pstate_ut_init(void) { u32 i = 0, arr_size = ARRAY_SIZE(amd_pstate_ut_cases); + enum amd_pstate_mode mode = amd_pstate_get_status(); + + /* don't test if no running amd-pstate driver */ + if (mode == AMD_PSTATE_UNDEFINED || mode == AMD_PSTATE_DISABLE) + return -EOPNOTSUPP; for (i = 0; i < arr_size; i++) { int ret; diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c index a74a4cf99d22..6e255a22a0b8 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -199,7 +199,7 @@ static inline int get_mode_idx_from_str(const char *str, size_t size) static DEFINE_MUTEX(amd_pstate_driver_lock); -static u8 msr_get_epp(struct amd_cpudata *cpudata) +static int msr_get_epp(struct amd_cpudata *cpudata) { u64 value; int ret; @@ -215,12 +215,12 @@ static u8 msr_get_epp(struct amd_cpudata *cpudata) DEFINE_STATIC_CALL(amd_pstate_get_epp, msr_get_epp); -static inline s16 amd_pstate_get_epp(struct amd_cpudata *cpudata) +static inline int amd_pstate_get_epp(struct amd_cpudata *cpudata) { return static_call(amd_pstate_get_epp)(cpudata); } -static u8 shmem_get_epp(struct amd_cpudata *cpudata) +static int shmem_get_epp(struct amd_cpudata *cpudata) { u64 epp; int ret; @@ -526,9 +526,6 @@ static int shmem_init_perf(struct amd_cpudata *cpudata) WRITE_ONCE(cpudata->perf, perf); WRITE_ONCE(cpudata->prefcore_ranking, cppc_perf.highest_perf); - if (cppc_state == AMD_PSTATE_ACTIVE) - return 0; - ret = cppc_get_auto_sel(cpudata->cpu, &auto_sel); if (ret) { pr_warn("failed to get auto_sel, ret: %d\n", ret); @@ -782,6 +779,7 @@ static unsigned int amd_pstate_fast_switch(struct cpufreq_policy *policy, static void amd_pstate_adjust_perf(struct cpufreq_policy *policy, unsigned long _min_perf, unsigned long target_perf, + unsigned long _max_perf, unsigned long capacity) { u8 max_perf, min_perf, des_perf, cap_perf; @@ -1031,7 +1029,7 @@ static int amd_pstate_init_freq(struct amd_cpudata *cpudata) return -EINVAL; } - if (lowest_nonlinear_freq <= min_freq || lowest_nonlinear_freq > nominal_freq) { + if (lowest_nonlinear_freq < min_freq || lowest_nonlinear_freq > nominal_freq) { pr_err("lowest_nonlinear_freq(%d) value is out of range [min_freq(%d), nominal_freq(%d)]\n", lowest_nonlinear_freq, min_freq, nominal_freq); return -EINVAL; @@ -1173,6 +1171,9 @@ static int amd_pstate_power_supply_notifier(struct notifier_block *nb, if (cpudata->current_profile != PLATFORM_PROFILE_BALANCED) return 0; + if (!policy) + return NOTIFY_OK; + epp = amd_pstate_get_balanced_epp(policy); ret = amd_pstate_set_epp(policy, epp); @@ -1208,6 +1209,9 @@ static int amd_pstate_profile_set(struct device *dev, struct cpufreq_policy *policy __free(put_cpufreq_policy) = cpufreq_cpu_get(cpudata->cpu); int ret; + if (!policy) + return -ENODEV; + switch (profile) { case PLATFORM_PROFILE_LOW_POWER: ret = amd_pstate_set_epp(policy, AMD_CPPC_EPP_POWERSAVE); @@ -1876,6 +1880,7 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) struct amd_cpudata *cpudata; union perf_cached perf; struct device *dev; + int default_epp; int ret; /* @@ -1924,6 +1929,14 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) policy->boost_supported = READ_ONCE(cpudata->boost_supported); + /* Cache the firmware programmed EPP */ + default_epp = amd_pstate_get_epp(cpudata); + if (default_epp < 0) { + ret = default_epp; + goto free_cpudata1; + } + FIELD_MODIFY(AMD_CPPC_EPP_PERF_MASK, &cpudata->cppc_req_cached, default_epp); + /* * Set the policy to provide a valid fallback value in case * the default cpufreq governor is neither powersave nor performance. @@ -1931,7 +1944,7 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) if (amd_pstate_acpi_pm_profile_server() || amd_pstate_acpi_pm_profile_undefined()) { policy->policy = CPUFREQ_POLICY_PERFORMANCE; - cpudata->epp_default_ac = cpudata->epp_default_dc = amd_pstate_get_epp(cpudata); + cpudata->epp_default_ac = cpudata->epp_default_dc = default_epp; cpudata->current_profile = PLATFORM_PROFILE_PERFORMANCE; } else { policy->policy = CPUFREQ_POLICY_POWERSAVE; @@ -2167,6 +2180,7 @@ static struct cpufreq_driver amd_pstate_epp_driver = { }; /* + * Processors without frequency scaling support can't do CPPC. * CPPC function is not supported for family ID 17H with model_ID ranging from 0x10 to 0x2F. * show the debug message that helps to check if the CPU has CPPC support for loading issue. */ @@ -2175,6 +2189,11 @@ static bool amd_cppc_supported(void) struct cpuinfo_x86 *c = &cpu_data(0); bool warn = false; + if (!cpu_feature_enabled(X86_FEATURE_HW_PSTATE)) { + pr_debug_once("frequency scaling is not supported by the processor\n"); + return false; + } + if ((boot_cpu_data.x86 == 0x17) && (boot_cpu_data.x86_model < 0x30)) { pr_debug_once("CPPC feature is not supported by the processor\n"); return false; diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index f6cea0c54dd9..6fe0e972952a 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -290,19 +290,32 @@ static inline void cppc_freq_invariance_exit(void) } #endif /* CONFIG_ACPI_CPPC_CPUFREQ_FIE */ -static void cppc_cpufreq_update_perf_limits(struct cppc_cpudata *cpu_data, - struct cpufreq_policy *policy) +static void cppc_cpufreq_get_perf_limits(struct cppc_cpudata *cpu_data, + struct cpufreq_policy *policy, + u32 *min_perf, u32 *max_perf) { struct cppc_perf_caps *caps = &cpu_data->perf_caps; - u32 min_perf, max_perf; + unsigned int min_freq, max_freq; + u32 min, max; + + min_freq = READ_ONCE(policy->min); + max_freq = READ_ONCE(policy->max); + if (unlikely(min_freq > max_freq)) + min_freq = max_freq; + + min = cppc_khz_to_perf(caps, min_freq); + max = cppc_khz_to_perf(caps, max_freq); - min_perf = cppc_khz_to_perf(caps, policy->min); - max_perf = cppc_khz_to_perf(caps, policy->max); + *min_perf = clamp_t(u32, min, caps->lowest_perf, caps->highest_perf); + *max_perf = clamp_t(u32, max, caps->lowest_perf, caps->highest_perf); +} - cpu_data->perf_ctrls.min_perf = - clamp_t(u32, min_perf, caps->lowest_perf, caps->highest_perf); - cpu_data->perf_ctrls.max_perf = - clamp_t(u32, max_perf, caps->lowest_perf, caps->highest_perf); +static void cppc_cpufreq_update_perf_limits(struct cppc_cpudata *cpu_data, + struct cpufreq_policy *policy) +{ + cppc_cpufreq_get_perf_limits(cpu_data, policy, + &cpu_data->perf_ctrls.min_perf, + &cpu_data->perf_ctrls.max_perf); } static int cppc_cpufreq_set_target(struct cpufreq_policy *policy, @@ -693,7 +706,7 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) goto out; } - policy->fast_switch_possible = cppc_allow_fast_switch(); + policy->fast_switch_possible = cppc_allow_fast_switch(policy->cpus); policy->dvfs_possible_from_any_cpu = true; /* diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index b898b6544069..0d0df986fa3d 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -2225,14 +2225,17 @@ EXPORT_SYMBOL_GPL(cpufreq_driver_fast_switch); * @policy: cpufreq policy object of the target CPU. * @min_perf: Minimum (required) performance level (units of @capacity). * @target_perf: Target (desired) performance level (units of @capacity). + * @max_perf: Maximum (allowed) performance level (units of @capacity). * @capacity: Capacity of the target CPU. * - * Carry out a fast performance level switch of @cpu without sleeping. + * Carry out a fast performance level adjustment for the CPU represented by + * @policy without sleeping. * * The driver's ->adjust_perf() callback invoked by this function must be - * suitable for being called from within RCU-sched read-side critical sections - * and it is expected to select a suitable performance level equal to or above - * @min_perf and preferably equal to or below @target_perf. + * suitable for calling from within RCU-sched read-side critical sections and + * it is expected to program the processor to select suitable performance + * levels between @min_perf and @max_perf inclusive and preferably close to + * @target_perf going forward for the CPU represented by @policy. * * This function must not be called if policy->fast_switch_enabled is unset. * @@ -2244,9 +2247,10 @@ EXPORT_SYMBOL_GPL(cpufreq_driver_fast_switch); void cpufreq_driver_adjust_perf(struct cpufreq_policy *policy, unsigned long min_perf, unsigned long target_perf, + unsigned long max_perf, unsigned long capacity) { - cpufreq_driver->adjust_perf(policy, min_perf, target_perf, capacity); + cpufreq_driver->adjust_perf(policy, min_perf, target_perf, max_perf, capacity); } /** diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 6e984c114d96..2cbf810db66e 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -299,7 +299,6 @@ struct pstate_funcs { static struct pstate_funcs pstate_funcs __read_mostly; static bool hwp_active __ro_after_init; -static int hwp_mode_bdw __ro_after_init; static bool per_cpu_limits __ro_after_init; static bool hwp_forced __ro_after_init; static bool hwp_boost __read_mostly; @@ -587,21 +586,14 @@ static void intel_pstate_hybrid_hwp_adjust(struct cpudata *cpu) hwp_is_hybrid = true; - cpu->pstate.turbo_freq = rounddown(cpu->pstate.turbo_pstate * scaling, - perf_ctl_scaling); - cpu->pstate.max_freq = rounddown(cpu->pstate.max_pstate * scaling, - perf_ctl_scaling); - freq = perf_ctl_max_phys * perf_ctl_scaling; cpu->pstate.max_pstate_physical = intel_pstate_freq_to_hwp(cpu, freq); - freq = cpu->pstate.min_pstate * perf_ctl_scaling; - cpu->pstate.min_freq = freq; /* * Cast the min P-state value retrieved via pstate_funcs.get_min() to * the effective range of HWP performance levels. */ - cpu->pstate.min_pstate = intel_pstate_freq_to_hwp(cpu, freq); + cpu->pstate.min_pstate = intel_pstate_freq_to_hwp(cpu, cpu->pstate.min_freq); } static bool turbo_is_disabled(void) @@ -979,12 +971,10 @@ static int hybrid_get_cost(struct device *dev, unsigned long freq, * capacity. Similarly, P-cores start to be populated when E-cores are * utilized above 60% of the capacity. */ - if (hybrid_get_cpu_type(dev->id) == INTEL_CPU_TYPE_ATOM) { - if (hybrid_has_l3(dev->id)) /* E-core */ - *cost += 1; - } else { /* P-core */ + if (hybrid_get_cpu_type(dev->id) == INTEL_CPU_TYPE_CORE) /* P-core */ *cost += 2; - } + else if (hybrid_has_l3(dev->id)) /* E-core */ + *cost += 1; return 0; } @@ -1185,6 +1175,22 @@ static bool hybrid_clear_max_perf_cpu(void) return ret; } +static void intel_pstate_update_freq_limits(struct cpudata *cpu) +{ + int scaling = cpu->pstate.scaling; + unsigned int turbo_freq = cpu->pstate.turbo_pstate * scaling; + unsigned int max_freq = cpu->pstate.max_pstate * scaling; + int perf_ctl_scaling = cpu->pstate.perf_ctl_scaling; + + if (scaling != perf_ctl_scaling) { + turbo_freq = rounddown(turbo_freq, perf_ctl_scaling); + max_freq = rounddown(max_freq, perf_ctl_scaling); + } + + cpu->pstate.turbo_freq = turbo_freq; + cpu->pstate.max_freq = max_freq; +} + static void __intel_pstate_get_hwp_cap(struct cpudata *cpu) { u64 cap; @@ -1197,20 +1203,8 @@ static void __intel_pstate_get_hwp_cap(struct cpudata *cpu) static void intel_pstate_get_hwp_cap(struct cpudata *cpu) { - int scaling = cpu->pstate.scaling; - __intel_pstate_get_hwp_cap(cpu); - - cpu->pstate.max_freq = cpu->pstate.max_pstate * scaling; - cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * scaling; - if (scaling != cpu->pstate.perf_ctl_scaling) { - int perf_ctl_scaling = cpu->pstate.perf_ctl_scaling; - - cpu->pstate.max_freq = rounddown(cpu->pstate.max_freq, - perf_ctl_scaling); - cpu->pstate.turbo_freq = rounddown(cpu->pstate.turbo_freq, - perf_ctl_scaling); - } + intel_pstate_update_freq_limits(cpu); } static void hybrid_update_capacity(struct cpudata *cpu) @@ -2299,33 +2293,16 @@ static int hwp_get_cpu_scaling(int cpu) return intel_pstate_cppc_get_scaling(cpu); } -static void intel_pstate_set_pstate(struct cpudata *cpu, int pstate) -{ - trace_cpu_frequency(pstate * cpu->pstate.scaling, cpu->cpu); - cpu->pstate.current_pstate = pstate; - /* - * Generally, there is no guarantee that this code will always run on - * the CPU being updated, so force the register update to run on the - * right CPU. - */ - wrmsrq_on_cpu(cpu->cpu, MSR_IA32_PERF_CTL, - pstate_funcs.get_val(cpu, pstate)); -} - -static void intel_pstate_set_min_pstate(struct cpudata *cpu) -{ - intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate); -} - static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) { int perf_ctl_scaling = pstate_funcs.get_scaling(); cpu->pstate.max_pstate_physical = pstate_funcs.get_max_physical(cpu->cpu); cpu->pstate.min_pstate = pstate_funcs.get_min(cpu->cpu); + cpu->pstate.min_freq = cpu->pstate.min_pstate * perf_ctl_scaling; cpu->pstate.perf_ctl_scaling = perf_ctl_scaling; - if (hwp_active && !hwp_mode_bdw) { + if (hwp_active) { __intel_pstate_get_hwp_cap(cpu); if (pstate_funcs.get_cpu_scaling) { @@ -2345,19 +2322,13 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) cpu->pstate.turbo_pstate = pstate_funcs.get_turbo(cpu->cpu); } - if (cpu->pstate.scaling == perf_ctl_scaling) { - cpu->pstate.min_freq = cpu->pstate.min_pstate * perf_ctl_scaling; - cpu->pstate.max_freq = cpu->pstate.max_pstate * perf_ctl_scaling; - cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * perf_ctl_scaling; - } + intel_pstate_update_freq_limits(cpu); if (pstate_funcs.get_aperf_mperf_shift) cpu->aperf_mperf_shift = pstate_funcs.get_aperf_mperf_shift(); if (pstate_funcs.get_vid) pstate_funcs.get_vid(cpu); - - intel_pstate_set_min_pstate(cpu); } /* @@ -2884,6 +2855,19 @@ static void intel_pstate_update_perf_limits(struct cpudata *cpu, cpu->min_perf_ratio); } +static void intel_pstate_set_pstate(struct cpudata *cpu, int pstate) +{ + trace_cpu_frequency(pstate * cpu->pstate.scaling, cpu->cpu); + cpu->pstate.current_pstate = pstate; + /* + * Generally, there is no guarantee that this code will always run on + * the CPU being updated, so force the register update to run on the + * right CPU. + */ + wrmsrq_on_cpu(cpu->cpu, MSR_IA32_PERF_CTL, + pstate_funcs.get_val(cpu, pstate)); +} + static int intel_pstate_set_policy(struct cpufreq_policy *policy) { struct cpudata *cpu; @@ -2971,6 +2955,11 @@ static int intel_pstate_verify_policy(struct cpufreq_policy_data *policy) return 0; } +static void intel_pstate_set_min_pstate(struct cpudata *cpu) +{ + intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate); +} + static int intel_cpufreq_cpu_offline(struct cpufreq_policy *policy) { struct cpudata *cpu = all_cpu_data[policy->cpu]; @@ -3063,6 +3052,7 @@ static int __intel_pstate_cpu_init(struct cpufreq_policy *policy) static int intel_pstate_cpu_init(struct cpufreq_policy *policy) { int ret = __intel_pstate_cpu_init(policy); + struct cpudata *cpu; if (ret) return ret; @@ -3073,11 +3063,11 @@ static int intel_pstate_cpu_init(struct cpufreq_policy *policy) */ policy->policy = CPUFREQ_POLICY_POWERSAVE; - if (hwp_active) { - struct cpudata *cpu = all_cpu_data[policy->cpu]; - + cpu = all_cpu_data[policy->cpu]; + if (hwp_active) cpu->epp_cached = intel_pstate_get_epp(cpu, 0); - } + else + intel_pstate_set_min_pstate(cpu); return 0; } @@ -3243,6 +3233,7 @@ static unsigned int intel_cpufreq_fast_switch(struct cpufreq_policy *policy, static void intel_cpufreq_adjust_perf(struct cpufreq_policy *policy, unsigned long min_perf, unsigned long target_perf, + unsigned long max_perf, unsigned long capacity) { struct cpudata *cpu = all_cpu_data[policy->cpu]; @@ -3273,7 +3264,13 @@ static void intel_cpufreq_adjust_perf(struct cpufreq_policy *policy, if (min_pstate > cpu->max_perf_ratio) min_pstate = cpu->max_perf_ratio; - max_pstate = min(cap_pstate, cpu->max_perf_ratio); + max_pstate = cap_pstate; + if (max_perf < capacity) + max_pstate = DIV_ROUND_UP(cap_pstate * max_perf, capacity); + + if (max_pstate > cpu->max_perf_ratio) + max_pstate = cpu->max_perf_ratio; + if (max_pstate < min_pstate) max_pstate = min_pstate; @@ -3301,8 +3298,6 @@ static int intel_cpufreq_cpu_init(struct cpufreq_policy *policy) return ret; policy->cpuinfo.transition_latency = INTEL_CPUFREQ_TRANSITION_LATENCY; - /* This reflects the intel_pstate_get_cpu_pstates() setting. */ - policy->cur = policy->cpuinfo.min_freq; req = kzalloc_objs(*req, 2); if (!req) { @@ -3323,9 +3318,15 @@ static int intel_cpufreq_cpu_init(struct cpufreq_policy *policy) WRITE_ONCE(cpu->hwp_req_cached, value); cpu->epp_cached = intel_pstate_get_epp(cpu, value); + + intel_cpufreq_hwp_update(cpu, cpu->pstate.min_pstate, + cpu->pstate.max_pstate, + cpu->pstate.min_pstate, false); } else { policy->transition_delay_us = INTEL_CPUFREQ_TRANSITION_DELAY; + intel_pstate_set_min_pstate(cpu); } + policy->cur = policy->cpuinfo.min_freq; freq = DIV_ROUND_UP(cpu->pstate.turbo_freq * global.min_perf_pct, 100); @@ -3676,14 +3677,14 @@ static inline bool intel_pstate_has_acpi_ppc(void) { return false; } static inline void intel_pstate_request_control_from_smm(void) {} #endif /* CONFIG_ACPI */ -#define INTEL_PSTATE_HWP_BROADWELL 0x01 +#define INTEL_PSTATE_HWP_NOT_HYBRID 0x01 #define X86_MATCH_HWP(vfm, hwp_mode) \ X86_MATCH_VFM_FEATURE(vfm, X86_FEATURE_HWP, hwp_mode) static const struct x86_cpu_id hwp_support_ids[] __initconst = { - X86_MATCH_HWP(INTEL_BROADWELL_X, INTEL_PSTATE_HWP_BROADWELL), - X86_MATCH_HWP(INTEL_BROADWELL_D, INTEL_PSTATE_HWP_BROADWELL), + X86_MATCH_HWP(INTEL_BROADWELL_X, INTEL_PSTATE_HWP_NOT_HYBRID), + X86_MATCH_HWP(INTEL_BROADWELL_D, INTEL_PSTATE_HWP_NOT_HYBRID), X86_MATCH_HWP(INTEL_ANY, 0), {} }; @@ -3808,7 +3809,6 @@ static int __init intel_pstate_init(void) if (!no_hwp) { hwp_active = true; - hwp_mode_bdw = id->driver_data; intel_pstate.attr = hwp_cpufreq_attrs; intel_cpufreq.attr = hwp_cpufreq_attrs; intel_cpufreq.flags |= CPUFREQ_NEED_UPDATE_LIMITS; @@ -3816,7 +3816,8 @@ static int __init intel_pstate_init(void) if (!default_driver) default_driver = &intel_pstate; - pstate_funcs.get_cpu_scaling = hwp_get_cpu_scaling; + if (!id->driver_data) + pstate_funcs.get_cpu_scaling = hwp_get_cpu_scaling; goto hwp_cpu_matched; } diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c index 10cdceaba475..8f3bdd50186e 100644 --- a/drivers/i2c/i2c-core-acpi.c +++ b/drivers/i2c/i2c-core-acpi.c @@ -131,15 +131,6 @@ static int i2c_acpi_fill_info(struct acpi_resource *ares, void *data) return 1; } -static const struct acpi_device_id i2c_acpi_ignored_device_ids[] = { - /* - * ACPI video acpi_devices, which are handled by the acpi-video driver - * sometimes contain a SERIAL_TYPE_I2C ACPI resource, ignore these. - */ - { ACPI_VIDEO_HID, 0 }, - {} -}; - struct i2c_acpi_irq_context { int irq; bool wake_capable; @@ -158,7 +149,11 @@ static int i2c_acpi_do_lookup(struct acpi_device *adev, if (!acpi_dev_ready_for_enumeration(adev)) return -ENODEV; - if (acpi_match_device_ids(adev, i2c_acpi_ignored_device_ids) == 0) + /* + * ACPI video devices, which are handled by the acpi-video driver, + * sometimes contain a SERIAL_TYPE_I2C ACPI resource, ignore these. + */ + if (acpi_dev_is_video_device(adev)) return -ENODEV; memset(info, 0, sizeof(*info)); diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index d74b478db280..bcc2725769c5 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -1779,6 +1779,7 @@ module_param_named(no_native, no_native, bool, 0444); MODULE_PARM_DESC(no_native, "Ignore cpu specific (native) idle states in lieu of ACPI idle states"); static struct acpi_processor_power acpi_state_table __initdata; +static bool acpi_lpi_available __initdata; /** * intel_idle_cst_usable - Check if the _CST information can be used. @@ -1803,18 +1804,37 @@ static bool __init intel_idle_cst_usable(void) return true; } -static bool __init intel_idle_acpi_cst_extract(void) +static bool __init intel_idle_acpi_extract_lpi_cstates(void) { unsigned int cpu; - if (no_acpi) { - pr_debug("Not allowed to use ACPI _CST\n"); - return false; + for_each_possible_cpu(cpu) { + struct acpi_processor *pr; + + pr = per_cpu(processors, cpu); + if (!pr) + continue; + + if (acpi_processor_extract_lpi_info(pr->handle, + &acpi_state_table, true)) + continue; + + acpi_lpi_available = true; + return true; } + pr_debug("No ACPI _LPI idle states\n"); + return false; +} + +static bool __init intel_idle_acpi_extract_cst_cstates(void) +{ + unsigned int cpu; + for_each_possible_cpu(cpu) { - struct acpi_processor *pr = per_cpu(processors, cpu); + struct acpi_processor *pr; + pr = per_cpu(processors, cpu); if (!pr) continue; @@ -1826,18 +1846,96 @@ static bool __init intel_idle_acpi_cst_extract(void) if (!intel_idle_cst_usable()) continue; - if (!acpi_processor_claim_cst_control()) - break; + return true; + } + + pr_debug("ACPI _CST not found or not usable\n"); + return false; +} + +static bool __init intel_idle_acpi_extract_cstates(void) +{ + if (intel_idle_acpi_extract_lpi_cstates()) + return true; + if (intel_idle_acpi_extract_cst_cstates()) return true; + + return false; +} + +static bool __init intel_idle_acpi_probe(void) +{ + if (no_acpi) { + pr_debug("Not allowed to use ACPI for C-states extraction\n"); + return false; } + if (intel_idle_acpi_extract_cstates() && + acpi_processor_claim_cst_control()) + return true; + acpi_state_table.count = 0; - pr_debug("ACPI _CST not found or not usable\n"); return false; } -static void __init intel_idle_init_cstates_acpi(struct cpuidle_driver *drv) +static void __init intel_idle_complete_state_init(struct cpuidle_state *state) +{ + if (intel_idle_state_needs_timer_stop(state)) + state->flags |= CPUIDLE_FLAG_TIMER_STOP; + + state->enter = intel_idle; + state->enter_dead = intel_idle_enter_dead; + state->enter_s2idle = intel_idle_s2idle; +} + +static void __init intel_idle_init_cstates_acpi_lpi(struct cpuidle_driver *drv) +{ + int index; + + for (index = 0; index < acpi_state_table.count; index++) { + struct acpi_lpi_state *lpi_state; + struct cpuidle_state *state; + + if (intel_idle_max_cstate_reached(index)) + break; + + lpi_state = &acpi_state_table.lpi_states[index]; + + state = &drv->states[drv->state_count++]; + + scnprintf(state->name, CPUIDLE_NAME_LEN, "C%d_LPI", index + 1); + strscpy(state->desc, lpi_state->desc, CPUIDLE_DESC_LEN); + state->exit_latency = lpi_state->wake_latency; + state->target_residency = lpi_state->min_residency; + state->flags = MWAIT2flg(lpi_state->address); + /* + * Assume that entering any of the idle states extracted from + * _LPI except for the first two will cause the TLB to be + * flushed and let the core call leave_mm() for them upfront + * to avoid unnecessary wakeups due to TLB shootdowns. + */ + if (index > 1) + state->flags |= CPUIDLE_FLAG_TLB_FLUSHED; + + if (disabled_states_mask & BIT(index + 1)) + state->flags |= CPUIDLE_FLAG_OFF; + + intel_idle_complete_state_init(state); + + pr_info("%s: MWAIT hint 0x%x\n", state->name, flg2MWAIT(state->flags)); + } + + /* + * Assume the first idle state in the table to be C1 and if any deeper + * idle states are exposed while X86_FEATURE_NONSTOP_TSC is unset, mark + * the TSC as unstable. + */ + if (index > 1 && !boot_cpu_has(X86_FEATURE_NONSTOP_TSC)) + mark_tsc_unstable("TSC halts in idle"); +} + +static void __init intel_idle_init_cstates_acpi_cst(struct cpuidle_driver *drv) { int cstate, limit = min_t(int, CPUIDLE_STATE_MAX, acpi_state_table.count); @@ -1879,49 +1977,76 @@ static void __init intel_idle_init_cstates_acpi(struct cpuidle_driver *drv) if (disabled_states_mask & BIT(cstate)) state->flags |= CPUIDLE_FLAG_OFF; - if (intel_idle_state_needs_timer_stop(state)) - state->flags |= CPUIDLE_FLAG_TIMER_STOP; - if (cx->type > ACPI_STATE_C1 && !boot_cpu_has(X86_FEATURE_NONSTOP_TSC)) mark_tsc_unstable("TSC halts in idle"); - state->enter = intel_idle; - state->enter_dead = intel_idle_enter_dead; - state->enter_s2idle = intel_idle_s2idle; + intel_idle_complete_state_init(state); } } -static bool __init intel_idle_off_by_default(unsigned int flags, u32 mwait_hint) +static void __init intel_idle_init_cstates_acpi(struct cpuidle_driver *drv) { - int cstate, limit; + if (acpi_lpi_available) + intel_idle_init_cstates_acpi_lpi(drv); + else + intel_idle_init_cstates_acpi_cst(drv); +} - /* - * If there are no _CST C-states, do not disable any C-states by - * default. - */ - if (!acpi_state_table.count) - return false; +static bool __init intel_idle_acpi_hint_match(unsigned int flags, u32 acpi_hint, + u32 table_hint) +{ + if (flags & CPUIDLE_FLAG_PARTIAL_HINT_MATCH) { + acpi_hint &= ~MWAIT_SUBSTATE_MASK; + table_hint &= ~MWAIT_SUBSTATE_MASK; + } + return acpi_hint == table_hint; +} + +static bool __init intel_idle_off_by_default_lpi(unsigned int flags, u32 mwait_hint) +{ + int index; + + for (index = 0; index < acpi_state_table.count; index++) { + u32 acpi_hint = acpi_state_table.lpi_states[index].address; + + if (intel_idle_acpi_hint_match(flags, acpi_hint, mwait_hint)) + return false; + } + return true; +} + +static bool __init intel_idle_off_by_default_cst(unsigned int flags, u32 mwait_hint) +{ + int cstate, limit = min_t(int, CPUIDLE_STATE_MAX, acpi_state_table.count); - limit = min_t(int, CPUIDLE_STATE_MAX, acpi_state_table.count); /* * If limit > 0, intel_idle_cst_usable() has returned 'true', so all of * the interesting states are ACPI_CSTATE_FFH. */ for (cstate = 1; cstate < limit; cstate++) { u32 acpi_hint = acpi_state_table.states[cstate].address; - u32 table_hint = mwait_hint; - if (flags & CPUIDLE_FLAG_PARTIAL_HINT_MATCH) { - acpi_hint &= ~MWAIT_SUBSTATE_MASK; - table_hint &= ~MWAIT_SUBSTATE_MASK; - } - - if (acpi_hint == table_hint) + if (intel_idle_acpi_hint_match(flags, acpi_hint, mwait_hint)) return false; } return true; } +static bool __init intel_idle_off_by_default(unsigned int flags, u32 mwait_hint) +{ + /* + * If there is no C-states information in the ACPI tables, do not + * disable any C-states by default. + */ + if (!acpi_state_table.count) + return false; + + if (acpi_lpi_available) + return intel_idle_off_by_default_lpi(flags, mwait_hint); + + return intel_idle_off_by_default_cst(flags, mwait_hint); +} + static inline bool ignore_native(void) { return no_native && !no_acpi; @@ -1929,7 +2054,7 @@ static inline bool ignore_native(void) #else /* !CONFIG_ACPI_PROCESSOR_CSTATE */ #define force_use_acpi (false) -static inline bool intel_idle_acpi_cst_extract(void) { return false; } +static inline bool intel_idle_acpi_probe(void) { return false; } static inline void intel_idle_init_cstates_acpi(struct cpuidle_driver *drv) { } static inline bool intel_idle_off_by_default(unsigned int flags, u32 mwait_hint) { @@ -2741,7 +2866,7 @@ static int __init intel_idle_init(void) if (icpu) { if (icpu->state_table) cpuidle_state_table = icpu->state_table; - else if (!intel_idle_acpi_cst_extract()) + else if (!intel_idle_acpi_probe()) return -ENODEV; auto_demotion_disable_flags = icpu->auto_demotion_disable_flags; @@ -2750,8 +2875,8 @@ static int __init intel_idle_init(void) if (icpu->c1_demotion_supported) c1_demotion_supported = true; if (icpu->use_acpi || force_use_acpi) - intel_idle_acpi_cst_extract(); - } else if (!intel_idle_acpi_cst_extract()) { + intel_idle_acpi_probe(); + } else if (!intel_idle_acpi_probe()) { return -ENODEV; } diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 99444a1b2ffa..2673954d4577 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -2588,7 +2588,7 @@ gic_acpi_init(union acpi_subtable_headers *header, const unsigned long end) if (err) goto out_fwhandle_free; - acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, gic_v3_get_gsi_domain_id); + acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, gic_v3_get_gsi_domain_id, NULL); if (static_branch_likely(&supports_deactivate_key)) gic_acpi_setup_kvm_info(); diff --git a/drivers/irqchip/irq-gic-v5-iwb.c b/drivers/irqchip/irq-gic-v5-iwb.c index 9103feb70ce8..6b02b90d9cce 100644 --- a/drivers/irqchip/irq-gic-v5-iwb.c +++ b/drivers/irqchip/irq-gic-v5-iwb.c @@ -269,6 +269,8 @@ static int gicv5_iwb_device_probe(struct platform_device *pdev) if (IS_ERR(iwb_node)) return PTR_ERR(iwb_node); + acpi_device_clear_deps(&pdev->dev); + return 0; } diff --git a/drivers/irqchip/irq-gic-v5.c b/drivers/irqchip/irq-gic-v5.c index e9d1795235a6..5dcf404ab4c8 100644 --- a/drivers/irqchip/irq-gic-v5.c +++ b/drivers/irqchip/irq-gic-v5.c @@ -1226,11 +1226,19 @@ static struct fwnode_handle *gsi_domain_handle; static struct fwnode_handle *gic_v5_get_gsi_domain_id(u32 gsi) { if (FIELD_GET(GICV5_GSI_IC_TYPE, gsi) == GICV5_GSI_IWB_TYPE) - return iort_iwb_handle(FIELD_GET(GICV5_GSI_IWB_FRAME_ID, gsi)); + return iort_iwb_handle_fwnode(FIELD_GET(GICV5_GSI_IWB_FRAME_ID, gsi)); return gsi_domain_handle; } +static acpi_handle gic_v5_get_gsi_handle(u32 gsi) +{ + if (FIELD_GET(GICV5_GSI_IC_TYPE, gsi) == GICV5_GSI_IWB_TYPE) + return iort_iwb_handle(FIELD_GET(GICV5_GSI_IWB_FRAME_ID, gsi)); + + return NULL; +} + static int __init gic_acpi_init(union acpi_subtable_headers *header, const unsigned long end) { struct acpi_madt_gicv5_irs *irs = (struct acpi_madt_gicv5_irs *)header; @@ -1251,7 +1259,8 @@ static int __init gic_acpi_init(union acpi_subtable_headers *header, const unsig if (ret) goto out_irs; - acpi_set_irq_model(ACPI_IRQ_MODEL_GIC_V5, gic_v5_get_gsi_domain_id); + acpi_set_irq_model(ACPI_IRQ_MODEL_GIC_V5, gic_v5_get_gsi_domain_id, + gic_v5_get_gsi_handle); return 0; diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index ec70c84e9f91..f6bc29f515fb 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -1690,7 +1690,7 @@ static int __init gic_v2_acpi_init(union acpi_subtable_headers *header, return ret; } - acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, gic_v2_get_gsi_domain_id); + acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, gic_v2_get_gsi_domain_id, NULL); if (IS_ENABLED(CONFIG_ARM_GIC_V2M)) gicv2m_init(NULL, gic_data[0].domain); diff --git a/drivers/irqchip/irq-loongarch-cpu.c b/drivers/irqchip/irq-loongarch-cpu.c index 950bc087e388..84ce24889488 100644 --- a/drivers/irqchip/irq-loongarch-cpu.c +++ b/drivers/irqchip/irq-loongarch-cpu.c @@ -168,7 +168,7 @@ static int __init cpuintc_acpi_init(union acpi_subtable_headers *header, panic("Failed to add irqdomain for LoongArch CPU"); set_handle_irq(&handle_cpu_irq); - acpi_set_irq_model(ACPI_IRQ_MODEL_LPIC, lpic_get_gsi_domain_id); + acpi_set_irq_model(ACPI_IRQ_MODEL_LPIC, lpic_get_gsi_domain_id, NULL); acpi_set_gsi_to_irq_fallback(lpic_gsi_to_irq); ret = acpi_cascade_irqdomain_init(); diff --git a/drivers/irqchip/irq-riscv-intc.c b/drivers/irqchip/irq-riscv-intc.c index 84418dbd5a27..0595144116e2 100644 --- a/drivers/irqchip/irq-riscv-intc.c +++ b/drivers/irqchip/irq-riscv-intc.c @@ -384,7 +384,8 @@ static int __init riscv_intc_acpi_init(union acpi_subtable_headers *header, if (rc) irq_domain_free_fwnode(fn); else - acpi_set_irq_model(ACPI_IRQ_MODEL_RINTC, riscv_acpi_get_gsi_domain_id); + acpi_set_irq_model(ACPI_IRQ_MODEL_RINTC, riscv_acpi_get_gsi_domain_id, + acpi_get_riscv_gsi_handle); return rc; } diff --git a/drivers/pci/vgaarb.c b/drivers/pci/vgaarb.c index c360eee11dd9..3de05aee7859 100644 --- a/drivers/pci/vgaarb.c +++ b/drivers/pci/vgaarb.c @@ -575,9 +575,7 @@ static bool vga_is_firmware_default(struct pci_dev *pdev) static bool vga_arb_integrated_gpu(struct device *dev) { #if defined(CONFIG_ACPI) - struct acpi_device *adev = ACPI_COMPANION(dev); - - return adev && !strcmp(acpi_device_hid(adev), ACPI_VIDEO_HID); + return acpi_dev_is_video_device(ACPI_COMPANION(dev)); #else return false; #endif diff --git a/drivers/platform/x86/lenovo/thinkpad_acpi.c b/drivers/platform/x86/lenovo/thinkpad_acpi.c index 445e1403308e..b528a1e67dd3 100644 --- a/drivers/platform/x86/lenovo/thinkpad_acpi.c +++ b/drivers/platform/x86/lenovo/thinkpad_acpi.c @@ -768,7 +768,7 @@ static acpi_status __init tpacpi_acpi_handle_locate_callback(acpi_handle handle, if (!strcmp(context, "video")) { struct acpi_device *dev = acpi_fetch_acpi_dev(handle); - if (!dev || strcmp(ACPI_VIDEO_HID, acpi_device_hid(dev))) + if (!acpi_dev_is_video_device(dev)) return AE_OK; } diff --git a/drivers/pnp/card.c b/drivers/pnp/card.c index 87f5af454751..df5e3d3cbf6c 100644 --- a/drivers/pnp/card.c +++ b/drivers/pnp/card.c @@ -254,9 +254,16 @@ int pnp_add_card(struct pnp_card *card) /* we wait until now to add devices in order to ensure the drivers * will be able to use all of the related devices on the card * without waiting an unreasonable length of time */ - list_for_each(pos, &card->devices) { + list_for_each_safe(pos, temp, &card->devices) { struct pnp_dev *dev = card_to_pnp_dev(pos); - __pnp_add_device(dev); + error = __pnp_add_device(dev); + if (error) { + mutex_lock(&pnp_lock); + list_del(&dev->card_list); + dev->card = NULL; + mutex_unlock(&pnp_lock); + put_device(&dev->dev); + } } /* match with card drivers */ diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c index fbf03ff007eb..d8ec0ad2ab4e 100644 --- a/drivers/pnp/pnpacpi/core.c +++ b/drivers/pnp/pnpacpi/core.c @@ -248,10 +248,7 @@ static int __init pnpacpi_add_device(struct acpi_device *device) if (acpi_has_method(device->handle, "_DIS")) dev->capabilities |= PNP_DISABLE; - if (strlen(acpi_device_name(device))) - strscpy(dev->name, acpi_device_name(device), sizeof(dev->name)); - else - strscpy(dev->name, acpi_device_bid(device), sizeof(dev->name)); + strscpy(dev->name, acpi_device_bid(device), sizeof(dev->name)); if (dev->active) pnpacpi_parse_allocated_resource(dev); diff --git a/drivers/pnp/system.c b/drivers/pnp/system.c index 835113b2cb04..bd939b564379 100644 --- a/drivers/pnp/system.c +++ b/drivers/pnp/system.c @@ -17,10 +17,10 @@ static const struct pnp_device_id pnp_dev_table[] = { /* General ID for reserving resources */ - {"PNP0c02", 0}, + { .id = "PNP0c02" }, /* memory controller */ - {"PNP0c01", 0}, - {"", 0} + { .id = "PNP0c01" }, + { } }; static void reserve_range(struct pnp_dev *dev, struct resource *r, int port) diff --git a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c index d200734625ee..5d70301d4a3d 100644 --- a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c +++ b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c @@ -356,8 +356,10 @@ static void cleanup_odvp(struct int3400_thermal_priv *priv) kfree(priv->odvp_attrs[i].attr.attr.name); } kfree(priv->odvp_attrs); + priv->odvp_attrs = NULL; } kfree(priv->odvp); + priv->odvp = NULL; priv->odvp_count = 0; } @@ -635,7 +637,6 @@ free_notify: acpi_remove_notify_handler(priv->adev->handle, ACPI_DEVICE_NOTIFY, int3400_notify); free_sysfs: - cleanup_odvp(priv); if (!ZERO_OR_NULL_PTR(priv->data_vault)) { device_remove_bin_file(&pdev->dev, &bin_attr_data_vault); kfree(priv->data_vault); @@ -649,6 +650,7 @@ free_rel_misc: acpi_thermal_rel_misc_device_remove(priv->adev->handle); thermal_zone_device_unregister(priv->thermal); free_art_trt: + cleanup_odvp(priv); kfree(priv->trts); kfree(priv->arts); free_priv: diff --git a/drivers/thermal/intel/int340x_thermal/platform_temperature_control.c b/drivers/thermal/intel/int340x_thermal/platform_temperature_control.c index d92a6f84a778..43cb809c5dcc 100644 --- a/drivers/thermal/intel/int340x_thermal/platform_temperature_control.c +++ b/drivers/thermal/intel/int340x_thermal/platform_temperature_control.c @@ -227,17 +227,12 @@ static ssize_t ptc_temperature_write(struct file *file, const char __user *data, { struct ptc_data *ptc_instance = file->private_data; struct pci_dev *pdev = ptc_instance->pdev; - char buf[32]; - ssize_t len; u32 value; + int ret; - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, data, len)) - return -EFAULT; - - buf[len] = '\0'; - if (kstrtouint(buf, 0, &value)) - return -EINVAL; + ret = kstrtou32_from_user(data, count, 0, &value); + if (unlikely(ret)) + return ret; if (ptc_mmio_regs[PTC_TEMP_OVERRIDE_INDEX].units) value /= ptc_mmio_regs[PTC_TEMP_OVERRIDE_INDEX].units; diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c index c693d934103a..c5131423ec9b 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c @@ -308,10 +308,8 @@ static int proc_thermal_setup_msi(struct pci_dev *pdev, struct proc_thermal_pci ret = devm_request_threaded_irq(&pdev->dev, irq, proc_thermal_irq_handler, proc_thermal_irq_thread_handler, 0, KBUILD_MODNAME, pci_info); - if (ret) { - dev_err(&pdev->dev, "Request IRQ %d failed\n", irq); + if (ret) goto err_free_msi_vectors; - } proc_thermal_msi_map[i] = irq; } @@ -394,10 +392,8 @@ static int proc_thermal_pci_probe(struct pci_dev *pdev, const struct pci_device_ ret = devm_request_threaded_irq(&pdev->dev, irq, proc_thermal_irq_handler, proc_thermal_irq_thread_handler, irq_flag, KBUILD_MODNAME, pci_info); - if (ret) { - dev_err(&pdev->dev, "Request IRQ %d failed\n", pdev->irq); + if (ret) goto err_ret_tzone; - } } ret = thermal_zone_device_enable(pci_info->tzone); diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c index 1a7e134dfcf8..96279756177f 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c @@ -491,12 +491,11 @@ int proc_thermal_rfim_add(struct pci_dev *pdev, struct proc_thermal_device *proc if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DVFS) { ret = sysfs_create_group(&pdev->dev.kobj, &dvfs_attribute_group); - if (ret && proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR) { - sysfs_remove_group(&pdev->dev.kobj, &fivr_attribute_group); - return ret; - } - if (ret && proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DLVR) { - sysfs_remove_group(&pdev->dev.kobj, &dlvr_attribute_group); + if (ret) { + if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DLVR) + sysfs_remove_group(&pdev->dev.kobj, &dlvr_attribute_group); + if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR) + sysfs_remove_group(&pdev->dev.kobj, &fivr_attribute_group); return ret; } } diff --git a/drivers/thermal/intel/intel_bxt_pmic_thermal.c b/drivers/thermal/intel/intel_bxt_pmic_thermal.c index 6312c6ba081f..aeaefbbd5d8f 100644 --- a/drivers/thermal/intel/intel_bxt_pmic_thermal.c +++ b/drivers/thermal/intel/intel_bxt_pmic_thermal.c @@ -245,10 +245,8 @@ static int pmic_thermal_probe(struct platform_device *pdev) NULL, pmic_thermal_irq_handler, IRQF_ONESHOT, "pmic_thermal", pdev); - if (ret) { - dev_err(dev, "request irq(%d) failed: %d\n", virq, ret); + if (ret) return ret; - } pmic_irq_count++; } diff --git a/drivers/thermal/intel/therm_throt.c b/drivers/thermal/intel/therm_throt.c index 45a8ef4a608b..d1f9cf8bf6c5 100644 --- a/drivers/thermal/intel/therm_throt.c +++ b/drivers/thermal/intel/therm_throt.c @@ -14,12 +14,14 @@ * Credits: Adapted from Zwane Mwaikambo's original code in mce_intel.c. * Inspired by Ross Biro's and Al Borchers' counter code. */ +#include <linux/syscore_ops.h> #include <linux/interrupt.h> #include <linux/notifier.h> #include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/percpu.h> #include <linux/export.h> +#include <linux/delay.h> #include <linux/types.h> #include <linux/init.h> #include <linux/smp.h> @@ -244,16 +246,23 @@ static void thermal_intr_init_pkg_clear_mask(void) * IA32_PACKAGE_THERM_STATUS. */ - /* All bits except BIT 26 depend on CPUID.06H: EAX[6] = 1 */ + /* All bits except BITs 25 and 26 depend on CPUID.06H: EAX[6] = 1 */ if (boot_cpu_has(X86_FEATURE_PTS)) therm_intr_pkg_clear_mask = (BIT(1) | BIT(3) | BIT(5) | BIT(7) | BIT(9) | BIT(11)); /* - * Intel SDM Volume 2A: Thermal and Power Management Leaf + * Intel SDM Volume 1: Thermal and Power Management Leaf * Bit 26: CPUID.06H: EAX[19] = 1 */ if (boot_cpu_has(X86_FEATURE_HFI)) therm_intr_pkg_clear_mask |= BIT(26); + + /* + * Intel SDM Volume 1: Thermal and Power Management Leaf + * Bit 25: CPUID.06H: EAX[24] = 1 + */ + if (boot_cpu_has(X86_FEATURE_DPTI)) + therm_intr_pkg_clear_mask |= BIT(25); } /* @@ -524,6 +533,254 @@ static void thermal_throttle_remove_dev(struct device *dev) sysfs_remove_group(&dev->kobj, &thermal_attr_group); } +static int check_directed_thermal_pkg_intr_ack(void) +{ + unsigned int count = 15000; + u64 msr_val; + + /* + * Hardware acknowledges the directed interrupt setup in 10ms or less. + * Wait 15ms to be safe. + */ + do { + rdmsrq(MSR_IA32_PACKAGE_THERM_STATUS, msr_val); + udelay(1); + } while (!(msr_val & PACKAGE_THERM_STATUS_DPTI_ACK) && --count); + + if (!count) + return -ETIMEDOUT; + + thermal_clear_package_intr_status(PACKAGE_LEVEL, + PACKAGE_THERM_STATUS_DPTI_ACK); + + return 0; +} + +static void config_directed_thermal_pkg_intr(void *info) +{ + bool enable = *((bool *)info); + u64 msr_val; + + rdmsrq(MSR_IA32_THERM_INTERRUPT, msr_val); + + if (enable) + msr_val |= THERM_INT_DPTI_ENABLE; + else + msr_val &= ~THERM_INT_DPTI_ENABLE; + + wrmsrq(MSR_IA32_THERM_INTERRUPT, msr_val); +} + +/* + * Accessed from CPU hotplug callbacks and from code that runs while CPU + * hotplug is inactive: the init and cleanup paths as well as syscore callbacks. + * No extra locking needed. + */ +static unsigned int *directed_intr_handler_cpus; + +static bool directed_thermal_pkg_intr_supported(void) +{ + if (!boot_cpu_has(X86_FEATURE_DPTI)) + return false; + + if (!directed_intr_handler_cpus) + return false; + + return true; +} + +/* + * Must be called with cpu_hotplug_lock held to prevent CPUs from going offline + * while iterating through packages and interrupts must be enabled to avoid + * deadlocks in SMP function calls. The syscore shutdown callback also calls + * this function, but runs with CPU hotplug disabled (and interrupts enabled). + */ +static void disable_directed_thermal_pkg_intr_all(void) +{ + bool enable = false; + int i; + + if (!directed_thermal_pkg_intr_supported()) + return; + + for (i = 0; i < topology_max_packages(); i++) { + if (directed_intr_handler_cpus[i] == nr_cpu_ids) + continue; + + smp_call_function_single(directed_intr_handler_cpus[i], + config_directed_thermal_pkg_intr, + &enable, true); + } +} + +static int enable_directed_thermal_pkg_intr(unsigned int cpu) +{ + bool enable = true; + u16 pkg_id; + + if (!directed_thermal_pkg_intr_supported()) + return 0; + + pkg_id = topology_logical_package_id(cpu); + if (pkg_id >= topology_max_packages()) + return -EINVAL; + + /* Another CPU in this package already handles the directed interrupt. */ + if (directed_intr_handler_cpus[pkg_id] != nr_cpu_ids) + return 0; + + thermal_clear_package_intr_status(PACKAGE_LEVEL, + PACKAGE_THERM_STATUS_DPTI_ACK); + + config_directed_thermal_pkg_intr(&enable); + if (!check_directed_thermal_pkg_intr_ack()) { + directed_intr_handler_cpus[pkg_id] = cpu; + return 0; + } + + /* + * A failure indicates faulty hardware. Roll back completely so that + * no other CPU tries. This is especially important during boot as all + * CPUs may come online and would otherwise keep trying. + */ + enable = false; + config_directed_thermal_pkg_intr(&enable); + + return -ETIMEDOUT; +} + +static void disable_directed_thermal_pkg_intr(unsigned int cpu) +{ + unsigned int new_cpu; + bool enable; + u16 pkg_id; + + if (!directed_thermal_pkg_intr_supported()) + return; + + pkg_id = topology_logical_package_id(cpu); + if (pkg_id >= topology_max_packages()) + return; + + /* Not the CPU handling the directed interrupt. */ + if (directed_intr_handler_cpus[pkg_id] != cpu) + return; + + /* + * The package-level interrupt must remain directed after this CPU goes + * offline. + */ + new_cpu = cpumask_any_but(topology_core_cpumask(cpu), cpu); + if (new_cpu < nr_cpu_ids) { + enable = true; + thermal_clear_package_intr_status(PACKAGE_LEVEL, + PACKAGE_THERM_STATUS_DPTI_ACK); + + /* + * We are here via CPU hotplug. Since we are holding the + * cpu_hotplug_lock, @new_cpu cannot go offline and interrupts + * are enabled, so the SMP function call is safe. + * + * The syscore suspend callback runs with interrupts disabled, + * but it does not reach this path because all the secondary + * CPUs are offline. + */ + smp_call_function_single(new_cpu, config_directed_thermal_pkg_intr, + &enable, true); + } + + /* + * If hardware does not acknowledge the directed interrupt setup on + * @new_cpu, disable the redirection. Since no other CPU is configured + * to receive the package-level interrupt, all CPUs in the package will + * receive it. + */ + enable = false; + if (new_cpu < nr_cpu_ids && check_directed_thermal_pkg_intr_ack()) { + smp_call_function_single(new_cpu, config_directed_thermal_pkg_intr, + &enable, true); + + pr_warn_once("Failed to redirect package thermal interrupt from CPU%u to CPU%u; reverting to broadcast.\n", + cpu, new_cpu); + + new_cpu = nr_cpu_ids; + } + + /* + * Clear the directed interrupt on @cpu. Hardware acknowledgment can be + * ignored since @cpu is going offline. + */ + config_directed_thermal_pkg_intr(&enable); + + directed_intr_handler_cpus[pkg_id] = (new_cpu < nr_cpu_ids) ? new_cpu : nr_cpu_ids; +} + +/* + * CPU0 may be handling the directed interrupt, but the CPU hotplug callbacks + * are not called for CPU0 during suspend and resume. + */ +static void directed_pkg_intr_syscore_resume(void *data) +{ + /* + * We can't do anything to handle errors. If direction fails for CPU0, + * another CPU will take over or disable direction entirely during CPU + * hotplug. + */ + enable_directed_thermal_pkg_intr(0); +} + +static int directed_pkg_intr_syscore_suspend(void *data) +{ + disable_directed_thermal_pkg_intr(0); + + return 0; +} + +static void directed_pkg_intr_syscore_shutdown(void *data) +{ + disable_directed_thermal_pkg_intr_all(); +} + +static const struct syscore_ops directed_pkg_intr_pm_ops = { + .resume = directed_pkg_intr_syscore_resume, + .suspend = directed_pkg_intr_syscore_suspend, + .shutdown = directed_pkg_intr_syscore_shutdown, +}; + +static struct syscore directed_pkg_intr_pm = { + .ops = &directed_pkg_intr_pm_ops, +}; + +static __init void init_directed_pkg_intr(void) +{ + int i; + + if (!boot_cpu_has(X86_FEATURE_DPTI)) + return; + + directed_intr_handler_cpus = kmalloc_array(topology_max_packages(), + sizeof(*directed_intr_handler_cpus), + GFP_KERNEL); + if (!directed_intr_handler_cpus) + return; + + for (i = 0; i < topology_max_packages(); i++) + directed_intr_handler_cpus[i] = nr_cpu_ids; + + register_syscore(&directed_pkg_intr_pm); +} + +static void cleanup_directed_pkg_thermal_intr(void) +{ + if (!directed_thermal_pkg_intr_supported()) + return; + + unregister_syscore(&directed_pkg_intr_pm); + disable_directed_thermal_pkg_intr_all(); + kfree(directed_intr_handler_cpus); + directed_intr_handler_cpus = NULL; +} + /* Get notified when a cpu comes on/off. Be hotplug friendly. */ static int thermal_throttle_online(unsigned int cpu) { @@ -549,6 +806,11 @@ static int thermal_throttle_online(unsigned int cpu) */ intel_hfi_online(cpu); + if (enable_directed_thermal_pkg_intr(cpu)) { + pr_info_once("Failed to direct package thermal interrupts. All CPUs will receive it.\n"); + cleanup_directed_pkg_thermal_intr(); + } + /* Unmask the thermal vector after the above workqueues are initialized. */ l = apic_read(APIC_LVTTHMR); apic_write(APIC_LVTTHMR, l & ~APIC_LVT_MASKED); @@ -566,6 +828,8 @@ static int thermal_throttle_offline(unsigned int cpu) l = apic_read(APIC_LVTTHMR); apic_write(APIC_LVTTHMR, l | APIC_LVT_MASKED); + disable_directed_thermal_pkg_intr(cpu); + intel_hfi_offline(cpu); cancel_delayed_work_sync(&state->package_throttle.therm_work); @@ -585,12 +849,19 @@ static __init int thermal_throttle_init_device(void) if (!atomic_read(&therm_throt_en)) return 0; + init_directed_pkg_intr(); + intel_hfi_init(); ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/therm:online", thermal_throttle_online, thermal_throttle_offline); - return ret < 0 ? ret : 0; + if (ret >= 0) + return 0; + + cleanup_directed_pkg_thermal_intr(); + + return ret; } device_initcall(thermal_throttle_init_device); diff --git a/drivers/xen/xen-acpi-pad.c b/drivers/xen/xen-acpi-pad.c index 5b98e0e93807..2fa9c7d3581f 100644 --- a/drivers/xen/xen-acpi-pad.c +++ b/drivers/xen/xen-acpi-pad.c @@ -17,8 +17,6 @@ #include <xen/xen-ops.h> #include <asm/xen/hypercall.h> -#define ACPI_PROCESSOR_AGGREGATOR_CLASS "acpi_pad" -#define ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME "Processor Aggregator" #define ACPI_PROCESSOR_AGGREGATOR_NOTIFY 0x80 static DEFINE_MUTEX(xen_cpu_lock); @@ -117,9 +115,6 @@ static int acpi_pad_probe(struct platform_device *pdev) if (!device) return -ENODEV; - strcpy(acpi_device_name(device), ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME); - strcpy(acpi_device_class(device), ACPI_PROCESSOR_AGGREGATOR_CLASS); - status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, acpi_pad_notify, device); if (ACPI_FAILURE(status)) |
