summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-07-23 14:18:14 +0100
committerMark Brown <broonie@kernel.org>2026-07-23 14:18:14 +0100
commitcb80df544f31e233d767e4ecf94bbc1ae712da1f (patch)
tree01ace82b5d385af627d87911d39e1e55721db23c /drivers
parente08a9005e6fa61e8e0d0aa72f565101d368a032b (diff)
parent2f67e70aaf88cf8b81daac740e90956446f17a5e (diff)
downloadlinux-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')
-rw-r--r--drivers/acpi/acpi_fpdt.c53
-rw-r--r--drivers/acpi/acpi_processor.c354
-rw-r--r--drivers/acpi/acpi_video.c14
-rw-r--r--drivers/acpi/apei/ghes.c4
-rw-r--r--drivers/acpi/arm64/iort.c22
-rw-r--r--drivers/acpi/battery.c147
-rw-r--r--drivers/acpi/bus.c138
-rw-r--r--drivers/acpi/cppc_acpi.c22
-rw-r--r--drivers/acpi/ec.c40
-rw-r--r--drivers/acpi/fan.h27
-rw-r--r--drivers/acpi/fan_core.c25
-rw-r--r--drivers/acpi/irq.c172
-rw-r--r--drivers/acpi/numa/srat.c2
-rw-r--r--drivers/acpi/nvs.c4
-rw-r--r--drivers/acpi/pci_root.c36
-rw-r--r--drivers/acpi/power.c1
-rw-r--r--drivers/acpi/processor_core.c31
-rw-r--r--drivers/acpi/processor_idle.c269
-rw-r--r--drivers/acpi/riscv/irq.c141
-rw-r--r--drivers/acpi/scan.c22
-rw-r--r--drivers/acpi/sysfs.c11
-rw-r--r--drivers/acpi/utils.c17
-rw-r--r--drivers/acpi/video_detect.c9
-rw-r--r--drivers/base/power/runtime.c6
-rw-r--r--drivers/cpufreq/amd-pstate-ut.c5
-rw-r--r--drivers/cpufreq/amd-pstate.c35
-rw-r--r--drivers/cpufreq/cppc_cpufreq.c33
-rw-r--r--drivers/cpufreq/cpufreq.c14
-rw-r--r--drivers/cpufreq/intel_pstate.c131
-rw-r--r--drivers/i2c/i2c-core-acpi.c15
-rw-r--r--drivers/idle/intel_idle.c195
-rw-r--r--drivers/irqchip/irq-gic-v3.c2
-rw-r--r--drivers/irqchip/irq-gic-v5-iwb.c2
-rw-r--r--drivers/irqchip/irq-gic-v5.c13
-rw-r--r--drivers/irqchip/irq-gic.c2
-rw-r--r--drivers/irqchip/irq-loongarch-cpu.c2
-rw-r--r--drivers/irqchip/irq-riscv-intc.c3
-rw-r--r--drivers/pci/vgaarb.c4
-rw-r--r--drivers/platform/x86/lenovo/thinkpad_acpi.c2
-rw-r--r--drivers/pnp/card.c11
-rw-r--r--drivers/pnp/pnpacpi/core.c5
-rw-r--r--drivers/pnp/system.c6
-rw-r--r--drivers/thermal/intel/int340x_thermal/int3400_thermal.c4
-rw-r--r--drivers/thermal/intel/int340x_thermal/platform_temperature_control.c13
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c8
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c11
-rw-r--r--drivers/thermal/intel/intel_bxt_pmic_thermal.c4
-rw-r--r--drivers/thermal/intel/therm_throt.c277
-rw-r--r--drivers/xen/xen-acpi-pad.c5
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))