From cf580ad490514cca4abbfef710fe4099738abfbd Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Fri, 23 Feb 2024 16:59:40 +0100 Subject: thermal: netlink: Add genetlink bind/unbind notifications Introduce a new feature to the thermal netlink framework, enabling the registration of sub drivers to receive events via a notifier mechanism. Specifically, implement genetlink family bind and unbind callbacks to send BIND and UNBIND events. The primary purpose of this enhancement is to facilitate the tracking of user-space consumers by the Intel HFI driver. By leveraging these notifications, the driver can determine when consumers are present or absent. Suggested-by: Jakub Kicinski Signed-off-by: Stanislaw Gruszka Signed-off-by: Rafael J. Wysocki --- drivers/thermal/thermal_netlink.c | 40 ++++++++++++++++++++++++++++++++++----- drivers/thermal/thermal_netlink.h | 26 +++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/drivers/thermal/thermal_netlink.c b/drivers/thermal/thermal_netlink.c index 76a231a2965451..d407130563e5bc 100644 --- a/drivers/thermal/thermal_netlink.c +++ b/drivers/thermal/thermal_netlink.c @@ -7,17 +7,13 @@ * Generic netlink for thermal management framework */ #include +#include #include #include #include #include "thermal_core.h" -enum thermal_genl_multicast_groups { - THERMAL_GENL_SAMPLING_GROUP = 0, - THERMAL_GENL_EVENT_GROUP = 1, -}; - static const struct genl_multicast_group thermal_genl_mcgrps[] = { [THERMAL_GENL_SAMPLING_GROUP] = { .name = THERMAL_GENL_SAMPLING_GROUP_NAME, }, [THERMAL_GENL_EVENT_GROUP] = { .name = THERMAL_GENL_EVENT_GROUP_NAME, }, @@ -75,6 +71,7 @@ struct param { typedef int (*cb_t)(struct param *); static struct genl_family thermal_gnl_family; +static BLOCKING_NOTIFIER_HEAD(thermal_genl_chain); static int thermal_group_has_listeners(enum thermal_genl_multicast_groups group) { @@ -645,6 +642,27 @@ out_free_msg: return ret; } +static int thermal_genl_bind(int mcgrp) +{ + struct thermal_genl_notify n = { .mcgrp = mcgrp }; + + if (WARN_ON_ONCE(mcgrp > THERMAL_GENL_MAX_GROUP)) + return -EINVAL; + + blocking_notifier_call_chain(&thermal_genl_chain, THERMAL_NOTIFY_BIND, &n); + return 0; +} + +static void thermal_genl_unbind(int mcgrp) +{ + struct thermal_genl_notify n = { .mcgrp = mcgrp }; + + if (WARN_ON_ONCE(mcgrp > THERMAL_GENL_MAX_GROUP)) + return; + + blocking_notifier_call_chain(&thermal_genl_chain, THERMAL_NOTIFY_UNBIND, &n); +} + static const struct genl_small_ops thermal_genl_ops[] = { { .cmd = THERMAL_GENL_CMD_TZ_GET_ID, @@ -679,6 +697,8 @@ static struct genl_family thermal_gnl_family __ro_after_init = { .version = THERMAL_GENL_VERSION, .maxattr = THERMAL_GENL_ATTR_MAX, .policy = thermal_genl_policy, + .bind = thermal_genl_bind, + .unbind = thermal_genl_unbind, .small_ops = thermal_genl_ops, .n_small_ops = ARRAY_SIZE(thermal_genl_ops), .resv_start_op = THERMAL_GENL_CMD_CDEV_GET + 1, @@ -686,6 +706,16 @@ static struct genl_family thermal_gnl_family __ro_after_init = { .n_mcgrps = ARRAY_SIZE(thermal_genl_mcgrps), }; +int thermal_genl_register_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&thermal_genl_chain, nb); +} + +int thermal_genl_unregister_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&thermal_genl_chain, nb); +} + int __init thermal_netlink_init(void) { return genl_register_family(&thermal_gnl_family); diff --git a/drivers/thermal/thermal_netlink.h b/drivers/thermal/thermal_netlink.h index 93a927e144d55f..e01221e8816b42 100644 --- a/drivers/thermal/thermal_netlink.h +++ b/drivers/thermal/thermal_netlink.h @@ -10,6 +10,19 @@ struct thermal_genl_cpu_caps { int efficiency; }; +enum thermal_genl_multicast_groups { + THERMAL_GENL_SAMPLING_GROUP = 0, + THERMAL_GENL_EVENT_GROUP = 1, + THERMAL_GENL_MAX_GROUP = THERMAL_GENL_EVENT_GROUP, +}; + +#define THERMAL_NOTIFY_BIND 0 +#define THERMAL_NOTIFY_UNBIND 1 + +struct thermal_genl_notify { + int mcgrp; +}; + struct thermal_zone_device; struct thermal_trip; struct thermal_cooling_device; @@ -18,6 +31,9 @@ struct thermal_cooling_device; #ifdef CONFIG_THERMAL_NETLINK int __init thermal_netlink_init(void); void __init thermal_netlink_exit(void); +int thermal_genl_register_notifier(struct notifier_block *nb); +int thermal_genl_unregister_notifier(struct notifier_block *nb); + int thermal_notify_tz_create(const struct thermal_zone_device *tz); int thermal_notify_tz_delete(const struct thermal_zone_device *tz); int thermal_notify_tz_enable(const struct thermal_zone_device *tz); @@ -48,6 +64,16 @@ static inline int thermal_notify_tz_create(const struct thermal_zone_device *tz) return 0; } +static inline int thermal_genl_register_notifier(struct notifier_block *nb) +{ + return 0; +} + +static inline int thermal_genl_unregister_notifier(struct notifier_block *nb) +{ + return 0; +} + static inline int thermal_notify_tz_delete(const struct thermal_zone_device *tz) { return 0; -- cgit 1.2.3-korg From afdaff3706918b7414e0741c4c5c20a12726a207 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Fri, 23 Feb 2024 16:59:41 +0100 Subject: thermal: netlink: Rename thermal_gnl_family Almost all thermal netlink structures use thermal_genl prefix. Change thermal_gnl_family name accordingly for consistency. No functional impact. Signed-off-by: Stanislaw Gruszka Signed-off-by: Rafael J. Wysocki --- drivers/thermal/thermal_netlink.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/thermal/thermal_netlink.c b/drivers/thermal/thermal_netlink.c index d407130563e5bc..bef14ce69ec418 100644 --- a/drivers/thermal/thermal_netlink.c +++ b/drivers/thermal/thermal_netlink.c @@ -70,12 +70,12 @@ struct param { typedef int (*cb_t)(struct param *); -static struct genl_family thermal_gnl_family; +static struct genl_family thermal_genl_family; static BLOCKING_NOTIFIER_HEAD(thermal_genl_chain); static int thermal_group_has_listeners(enum thermal_genl_multicast_groups group) { - return genl_has_listeners(&thermal_gnl_family, &init_net, group); + return genl_has_listeners(&thermal_genl_family, &init_net, group); } /************************** Sampling encoding *******************************/ @@ -92,7 +92,7 @@ int thermal_genl_sampling_temp(int id, int temp) if (!skb) return -ENOMEM; - hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0, + hdr = genlmsg_put(skb, 0, 0, &thermal_genl_family, 0, THERMAL_GENL_SAMPLING_TEMP); if (!hdr) goto out_free; @@ -105,7 +105,7 @@ int thermal_genl_sampling_temp(int id, int temp) genlmsg_end(skb, hdr); - genlmsg_multicast(&thermal_gnl_family, skb, 0, THERMAL_GENL_SAMPLING_GROUP, GFP_KERNEL); + genlmsg_multicast(&thermal_genl_family, skb, 0, THERMAL_GENL_SAMPLING_GROUP, GFP_KERNEL); return 0; out_cancel: @@ -279,7 +279,7 @@ static int thermal_genl_send_event(enum thermal_genl_event event, return -ENOMEM; p->msg = msg; - hdr = genlmsg_put(msg, 0, 0, &thermal_gnl_family, 0, event); + hdr = genlmsg_put(msg, 0, 0, &thermal_genl_family, 0, event); if (!hdr) goto out_free_msg; @@ -289,7 +289,7 @@ static int thermal_genl_send_event(enum thermal_genl_event event, genlmsg_end(msg, hdr); - genlmsg_multicast(&thermal_gnl_family, msg, 0, THERMAL_GENL_EVENT_GROUP, GFP_KERNEL); + genlmsg_multicast(&thermal_genl_family, msg, 0, THERMAL_GENL_EVENT_GROUP, GFP_KERNEL); return 0; @@ -590,7 +590,7 @@ static int thermal_genl_cmd_dumpit(struct sk_buff *skb, int ret; void *hdr; - hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0, cmd); + hdr = genlmsg_put(skb, 0, 0, &thermal_genl_family, 0, cmd); if (!hdr) return -EMSGSIZE; @@ -622,7 +622,7 @@ static int thermal_genl_cmd_doit(struct sk_buff *skb, return -ENOMEM; p.msg = msg; - hdr = genlmsg_put_reply(msg, info, &thermal_gnl_family, 0, cmd); + hdr = genlmsg_put_reply(msg, info, &thermal_genl_family, 0, cmd); if (!hdr) goto out_free_msg; @@ -691,7 +691,7 @@ static const struct genl_small_ops thermal_genl_ops[] = { }, }; -static struct genl_family thermal_gnl_family __ro_after_init = { +static struct genl_family thermal_genl_family __ro_after_init = { .hdrsize = 0, .name = THERMAL_GENL_FAMILY_NAME, .version = THERMAL_GENL_VERSION, @@ -718,10 +718,10 @@ int thermal_genl_unregister_notifier(struct notifier_block *nb) int __init thermal_netlink_init(void) { - return genl_register_family(&thermal_gnl_family); + return genl_register_family(&thermal_genl_family); } void __init thermal_netlink_exit(void) { - genl_unregister_family(&thermal_gnl_family); + genl_unregister_family(&thermal_genl_family); } -- cgit 1.2.3-korg From b33f3d2677b8ddd7a3aba2b02497422a1d2c2a01 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Fri, 23 Feb 2024 16:59:42 +0100 Subject: thermal: intel: hfi: Enable HFI only when required Enable and disable hardware feedback interface (HFI) when user space handler is present. For example, enable HFI, when intel-speed-select or Intel Low Power daemon is running and subscribing to thermal netlink events. When user space handlers exit or remove subscription for thermal netlink events, disable HFI. Summary of changes: - Register a thermal genetlink notifier - In the notifier, process THERMAL_NOTIFY_BIND and THERMAL_NOTIFY_UNBIND reason codes to count number of thermal event group netlink multicast clients. If thermal netlink group has any listener enable HFI on all packages. If there are no listener disable HFI on all packages. - When CPU is online, instead of blindly enabling HFI, check if the thermal netlink group has any listener. This will make sure that HFI is not enabled by default during boot time. - Actual processing to enable/disable matches what is done in suspend/resume callbacks. Create two functions hfi_enable_instance() and hfi_disable_instance(), which can be called from the netlink notifier callback and suspend/resume callbacks. Signed-off-by: Stanislaw Gruszka Signed-off-by: Rafael J. Wysocki --- drivers/thermal/intel/intel_hfi.c | 97 +++++++++++++++++++++++++++++++++++---- 1 file changed, 89 insertions(+), 8 deletions(-) diff --git a/drivers/thermal/intel/intel_hfi.c b/drivers/thermal/intel/intel_hfi.c index 40d664a66cdcb3..fbc7f0cd83d701 100644 --- a/drivers/thermal/intel/intel_hfi.c +++ b/drivers/thermal/intel/intel_hfi.c @@ -159,6 +159,7 @@ struct hfi_cpu_info { static DEFINE_PER_CPU(struct hfi_cpu_info, hfi_cpu_info) = { .index = -1 }; static int max_hfi_instances; +static int hfi_clients_nr; static struct hfi_instance *hfi_instances; static struct hfi_features hfi_features; @@ -477,8 +478,11 @@ void intel_hfi_online(unsigned int cpu) enable: cpumask_set_cpu(cpu, hfi_instance->cpus); - /* Enable this HFI instance if this is its first online CPU. */ - if (cpumask_weight(hfi_instance->cpus) == 1) { + /* + * Enable this HFI instance if this is its first online CPU and + * there are user-space clients of thermal events. + */ + if (cpumask_weight(hfi_instance->cpus) == 1 && hfi_clients_nr > 0) { hfi_set_hw_table(hfi_instance); hfi_enable(); } @@ -573,18 +577,33 @@ static __init int hfi_parse_features(void) return 0; } -static void hfi_do_enable(void) +/* + * If concurrency is not prevented by other means, the HFI enable/disable + * routines must be called under hfi_instance_lock." + */ +static void hfi_enable_instance(void *ptr) +{ + hfi_set_hw_table(ptr); + hfi_enable(); +} + +static void hfi_disable_instance(void *ptr) +{ + hfi_disable(); +} + +static void hfi_syscore_resume(void) { /* This code runs only on the boot CPU. */ struct hfi_cpu_info *info = &per_cpu(hfi_cpu_info, 0); struct hfi_instance *hfi_instance = info->hfi_instance; /* No locking needed. There is no concurrency with CPU online. */ - hfi_set_hw_table(hfi_instance); - hfi_enable(); + if (hfi_clients_nr > 0) + hfi_enable_instance(hfi_instance); } -static int hfi_do_disable(void) +static int hfi_syscore_suspend(void) { /* No locking needed. There is no concurrency with CPU offline. */ hfi_disable(); @@ -593,8 +612,58 @@ static int hfi_do_disable(void) } static struct syscore_ops hfi_pm_ops = { - .resume = hfi_do_enable, - .suspend = hfi_do_disable, + .resume = hfi_syscore_resume, + .suspend = hfi_syscore_suspend, +}; + +static int hfi_thermal_notify(struct notifier_block *nb, unsigned long state, + void *_notify) +{ + struct thermal_genl_notify *notify = _notify; + struct hfi_instance *hfi_instance; + smp_call_func_t func = NULL; + unsigned int cpu; + int i; + + if (notify->mcgrp != THERMAL_GENL_EVENT_GROUP) + return NOTIFY_DONE; + + if (state != THERMAL_NOTIFY_BIND && state != THERMAL_NOTIFY_UNBIND) + return NOTIFY_DONE; + + mutex_lock(&hfi_instance_lock); + + switch (state) { + case THERMAL_NOTIFY_BIND: + if (++hfi_clients_nr == 1) + func = hfi_enable_instance; + break; + case THERMAL_NOTIFY_UNBIND: + if (--hfi_clients_nr == 0) + func = hfi_disable_instance; + break; + } + + if (!func) + goto out; + + for (i = 0; i < max_hfi_instances; i++) { + hfi_instance = &hfi_instances[i]; + if (cpumask_empty(hfi_instance->cpus)) + continue; + + cpu = cpumask_any(hfi_instance->cpus); + smp_call_function_single(cpu, func, hfi_instance, true); + } + +out: + mutex_unlock(&hfi_instance_lock); + + return NOTIFY_OK; +} + +static struct notifier_block hfi_thermal_nb = { + .notifier_call = hfi_thermal_notify, }; void __init intel_hfi_init(void) @@ -628,10 +697,22 @@ void __init intel_hfi_init(void) if (!hfi_updates_wq) goto err_nomem; + /* + * Both thermal core and Intel HFI can not be build as modules. + * As kernel build-in drivers they are initialized before user-space + * starts, hence we can not miss BIND/UNBIND events when applications + * add/remove thermal multicast group to/from a netlink socket. + */ + if (thermal_genl_register_notifier(&hfi_thermal_nb)) + goto err_nl_notif; + register_syscore_ops(&hfi_pm_ops); return; +err_nl_notif: + destroy_workqueue(hfi_updates_wq); + err_nomem: for (j = 0; j < i; ++j) { hfi_instance = &hfi_instances[j]; -- cgit 1.2.3-korg From 03fa9a3ad1d61992a2105aeb1062b349f1a85012 Mon Sep 17 00:00:00 2001 From: Justin Stitt Date: Mon, 18 Mar 2024 22:36:10 +0000 Subject: thermal: intel: int340x_thermal: replace deprecated strncpy() with strscpy() strncpy() is deprecated for use on NUL-terminated destination strings [1] and as such we should prefer more robust and less ambiguous string interfaces. psvt->limit.string can only be 8 bytes so let's use the appropriate size macro ACPI_LIMIT_STR_MAX_LEN. Neither psvt->limit.string or psvt_user[i].limit.string requires the NUL-padding behavior that strncpy() provides as they have both been filled with NUL-bytes prior to the string operation. | memset(&psvt->limit, 0, sizeof(u64)); and | psvt_user = kzalloc(psvt_len, GFP_KERNEL); Let's use `strscpy` [2] due to the fact that it guarantees NUL-termination on the destination buffer without unnecessarily NUL-padding. Link: https://www.kernel.org/doc/html/latest/process/deprecated.html#strncpy-on-nul-terminated-strings # [1] Link: https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html [2] Link: https://github.com/KSPP/linux/issues/90 Signed-off-by: Justin Stitt Reviewed-by: Srinivas Pandruvada Signed-off-by: Rafael J. Wysocki --- drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c index dc519a665c1854..4b4a4d63e61fe5 100644 --- a/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c +++ b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c @@ -309,7 +309,7 @@ static int acpi_parse_psvt(acpi_handle handle, int *psvt_count, struct psvt **ps if (knob->type == ACPI_TYPE_STRING) { memset(&psvt->limit, 0, sizeof(u64)); - strncpy(psvt->limit.string, psvt_ptr->limit.str_ptr, knob->string.length); + strscpy(psvt->limit.string, psvt_ptr->limit.str_ptr, ACPI_LIMIT_STR_MAX_LEN); } else { psvt->limit.integer = psvt_ptr->limit.integer; } @@ -468,7 +468,7 @@ static int fill_psvt(char __user *ubuf) psvt_user[i].unlimit_coeff = psvts[i].unlimit_coeff; psvt_user[i].control_knob_type = psvts[i].control_knob_type; if (psvt_user[i].control_knob_type == ACPI_TYPE_STRING) - strncpy(psvt_user[i].limit.string, psvts[i].limit.string, + strscpy(psvt_user[i].limit.string, psvts[i].limit.string, ACPI_LIMIT_STR_MAX_LEN); else psvt_user[i].limit.integer = psvts[i].limit.integer; -- cgit 1.2.3-korg From db9ea3b22315b74fd682d0c381a6e2ad09a105e3 Mon Sep 17 00:00:00 2001 From: Xuewen Yan Date: Tue, 19 Mar 2024 16:01:53 +0800 Subject: cpufreq: Use a smaller freq for the policy->max when verify When driver use the cpufreq_frequency_table_verify() as the cpufreq_driver->verify's callback. It may cause the policy->max bigger than the freq_qos's max freq. Just as follow: unisoc:/sys/devices/system/cpu/cpufreq/policy0 # cat scaling_available_frequencies 614400 768000 988000 1228800 1469000 1586000 1690000 1833000 2002000 2093000 unisoc:/sys/devices/system/cpu/cpufreq/policy0 # echo 1900000 > scaling_max_freq unisoc:/sys/devices/system/cpu/cpufreq/policy0 # echo 1900000 > scaling_min_freq unisoc:/sys/devices/system/cpu/cpufreq/policy0 # cat scaling_max_freq 2002000 unisoc:/sys/devices/system/cpu/cpufreq/policy0 # cat scaling_min_freq 2002000 When user set the qos_min and qos_max as the same value, and the value is not in the freq-table, the above scenario will occur. This is because in cpufreq_frequency_table_verify() func, when it can not find the freq in table, it will change the policy->max to be a bigger freq, as above, because there is no 1.9G in the freq-table, the policy->max would be set to 2.002G. As a result, the cpufreq_policy->max is bigger than the user's qos_max. This is unreasonable. So use a smaller freq when can not find the freq in fre-table, to prevent the policy->max exceed the qos's max freq. Signed-off-by: Xuewen Yan Acked-by: Viresh Kumar Reviewed-by: Dhruva Gole Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/freq_table.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c index c17dc51a5a022d..40e146942f3e30 100644 --- a/drivers/cpufreq/freq_table.c +++ b/drivers/cpufreq/freq_table.c @@ -70,7 +70,7 @@ int cpufreq_frequency_table_verify(struct cpufreq_policy_data *policy, struct cpufreq_frequency_table *table) { struct cpufreq_frequency_table *pos; - unsigned int freq, next_larger = ~0; + unsigned int freq, prev_smaller = 0; bool found = false; pr_debug("request for verification of policy (%u - %u kHz) for cpu %u\n", @@ -86,12 +86,12 @@ int cpufreq_frequency_table_verify(struct cpufreq_policy_data *policy, break; } - if ((next_larger > freq) && (freq > policy->max)) - next_larger = freq; + if ((prev_smaller < freq) && (freq <= policy->max)) + prev_smaller = freq; } if (!found) { - policy->max = next_larger; + policy->max = prev_smaller; cpufreq_verify_within_cpu_limits(policy); } -- cgit 1.2.3-korg From eb68d909d53eed0ec9722fcb18747647ca33a18f Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 25 Mar 2024 17:09:53 -0500 Subject: Documentation: PM: Update platform_pci_wakeup_init() reference platform_pci_wakeup_init() was removed by d2e5f0c16ad6 ("ACPI / PCI: Rework the setup and cleanup of device wakeup") but was still mentioned in the documentation. Update the doc to refer to pci_acpi_setup(), which does the equivalent work. Signed-off-by: Bjorn Helgaas Reviewed-by: Dhruva Gole Signed-off-by: Rafael J. Wysocki --- Documentation/power/pci.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/power/pci.rst b/Documentation/power/pci.rst index 12070320307e5c..e2c1fb8a569a8c 100644 --- a/Documentation/power/pci.rst +++ b/Documentation/power/pci.rst @@ -333,7 +333,7 @@ struct pci_dev. The PCI subsystem's first task related to device power management is to prepare the device for power management and initialize the fields of struct pci_dev used for this purpose. This happens in two functions defined in -drivers/pci/pci.c, pci_pm_init() and platform_pci_wakeup_init(). +drivers/pci/, pci_pm_init() and pci_acpi_setup(). The first of these functions checks if the device supports native PCI PM and if that's the case the offset of its power management capability structure -- cgit 1.2.3-korg From 2f7d7ea44adbe7497b225bbb7bfc29e3c792094d Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 19 Mar 2024 09:30:15 +0100 Subject: ACPI: NHLT: Reintroduce types the table consists of ACPICA commit 32260f5ce519e854546ce907fc0cc449e1fe51fe Non HDAudio Link Table (NHLT) is designed to separate hardware-related description (registers) from AudioDSP firmware-related one i.e.: pipelines and modules that together make up the audio stream on Intel DSPs. This task is important as same set of hardware registers can be used with different topologies and vice versa, same topology could be utilized with different set of hardware. As the hardware registers description is directly tied to specific platform, intention is to have such description part of low-level firmware e.g.: BIOS. The initial design has been provided in early Sky Lake (SKL) days. The audio architecture goes by the name cAVS. SKL is a representative of cAVS 1.5. The table helps describe endpoint capabilities ever since. While Raptor Lake (RPL) is the last of cAVS architecture - cAVS 2.5 to be precise - its successor, the ACE architecture which begun with Meteor Lake (MTL) inherited the design for all I2S and PDM configurations. These two configurations are the primary targets for NHLT table. Due to naming conflicts with existing code, several structs are named 'nhlt2' rather than 'nhlt'. Follow up changes clean this up once existing code has no users and is removed. Link: https://github.com/acpica/acpica/pull/912 Signed-off-by: Cezary Rojewski Signed-off-by: Rafael J. Wysocki --- include/acpi/actbl2.h | 189 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h index 9775384d61c693..8030a17431004f 100644 --- a/include/acpi/actbl2.h +++ b/include/acpi/actbl2.h @@ -2141,6 +2141,195 @@ struct acpi_nhlt_device_info { u8 device_port_id; }; +/******************************************************************************* + * + * NHLT - Non HDAudio Link Table + * Version 1 + * + ******************************************************************************/ + +struct acpi_table_nhlt2 { + struct acpi_table_header header; /* Common ACPI table header */ + u8 endpoints_count; + /* + * struct acpi_nhlt_endpoint endpoints[]; + * struct acpi_nhlt_config oed_config; + */ +}; + +struct acpi_nhlt2_endpoint { + u32 length; + u8 link_type; + u8 instance_id; + u16 vendor_id; + u16 device_id; + u16 revision_id; + u32 subsystem_id; + u8 device_type; + u8 direction; + u8 virtual_bus_id; + /* + * struct acpi_nhlt_config device_config; + * struct acpi_nhlt_formats_config formats_config; + * struct acpi_nhlt_devices_info devices_info; + */ +}; + +/* + * Values for link_type field above + * + * Only types PDM and SSP are used + */ +#define ACPI_NHLT_LINKTYPE_HDA 0 +#define ACPI_NHLT_LINKTYPE_DSP 1 +#define ACPI_NHLT_LINKTYPE_PDM 2 +#define ACPI_NHLT_LINKTYPE_SSP 3 +#define ACPI_NHLT_LINKTYPE_SLIMBUS 4 +#define ACPI_NHLT_LINKTYPE_SDW 5 +#define ACPI_NHLT_LINKTYPE_UAOL 6 + +/* Values for device_id field above */ + +#define ACPI_NHLT_DEVICEID_DMIC 0xAE20 +#define ACPI_NHLT_DEVICEID_BT 0xAE30 +#define ACPI_NHLT_DEVICEID_I2S 0xAE34 + +/* Values for device_type field above */ + +/* + * Device types unique to endpoint of link_type=PDM + * + * Type PDM used for all SKL+ platforms + */ +#define ACPI_NHLT_DEVICETYPE_PDM 0 +#define ACPI_NHLT_DEVICETYPE_PDM_SKL 1 +/* Device types unique to endpoint of link_type=SSP */ +#define ACPI_NHLT_DEVICETYPE_BT 0 +#define ACPI_NHLT_DEVICETYPE_FM 1 +#define ACPI_NHLT_DEVICETYPE_MODEM 2 +#define ACPI_NHLT_DEVICETYPE_CODEC 4 + +/* Values for Direction field above */ + +#define ACPI_NHLT_DIR_RENDER 0 +#define ACPI_NHLT_DIR_CAPTURE 1 + +struct acpi_nhlt_config { + u32 capabilities_size; + u8 capabilities[]; +}; + +struct acpi_nhlt_gendevice_config { + u8 virtual_slot; + u8 config_type; +}; + +/* Values for config_type field above */ + +#define ACPI_NHLT_CONFIGTYPE_GENERIC 0 +#define ACPI_NHLT_CONFIGTYPE_MICARRAY 1 + +struct acpi_nhlt_micdevice_config { + u8 virtual_slot; + u8 config_type; + u8 array_type; +}; + +/* Values for array_type field above */ + +#define ACPI_NHLT_ARRAYTYPE_LINEAR2_SMALL 0xA +#define ACPI_NHLT_ARRAYTYPE_LINEAR2_BIG 0xB +#define ACPI_NHLT_ARRAYTYPE_LINEAR4_GEO1 0xC +#define ACPI_NHLT_ARRAYTYPE_PLANAR4_LSHAPED 0xD +#define ACPI_NHLT_ARRAYTYPE_LINEAR4_GEO2 0xE +#define ACPI_NHLT_ARRAYTYPE_VENDOR 0xF + +struct acpi_nhlt2_vendor_mic_config { + u8 type; + u8 panel; + u16 speaker_position_distance; /* mm */ + u16 horizontal_offset; /* mm */ + u16 vertical_offset; /* mm */ + u8 frequency_low_band; /* 5*Hz */ + u8 frequency_high_band; /* 500*Hz */ + u16 direction_angle; /* -180 - +180 */ + u16 elevation_angle; /* -180 - +180 */ + u16 work_vertical_angle_begin; /* -180 - +180 with 2 deg step */ + u16 work_vertical_angle_end; /* -180 - +180 with 2 deg step */ + u16 work_horizontal_angle_begin; /* -180 - +180 with 2 deg step */ + u16 work_horizontal_angle_end; /* -180 - +180 with 2 deg step */ +}; + +/* Values for Type field above */ + +#define ACPI_NHLT_MICTYPE_OMNIDIRECTIONAL 0 +#define ACPI_NHLT_MICTYPE_SUBCARDIOID 1 +#define ACPI_NHLT_MICTYPE_CARDIOID 2 +#define ACPI_NHLT_MICTYPE_SUPERCARDIOID 3 +#define ACPI_NHLT_MICTYPE_HYPERCARDIOID 4 +#define ACPI_NHLT_MICTYPE_8SHAPED 5 +#define ACPI_NHLT_MICTYPE_RESERVED 6 +#define ACPI_NHLT_MICTYPE_VENDORDEFINED 7 + +/* Values for Panel field above */ + +#define ACPI_NHLT_MICLOCATION_TOP 0 +#define ACPI_NHLT_MICLOCATION_BOTTOM 1 +#define ACPI_NHLT_MICLOCATION_LEFT 2 +#define ACPI_NHLT_MICLOCATION_RIGHT 3 +#define ACPI_NHLT_MICLOCATION_FRONT 4 +#define ACPI_NHLT_MICLOCATION_REAR 5 + +struct acpi_nhlt_vendor_micdevice_config { + u8 virtual_slot; + u8 config_type; + u8 array_type; + u8 mics_count; + struct acpi_nhlt2_vendor_mic_config mics[]; +}; + +union acpi_nhlt_device_config { + u8 virtual_slot; + struct acpi_nhlt_gendevice_config gen; + struct acpi_nhlt_micdevice_config mic; + struct acpi_nhlt_vendor_micdevice_config vendor_mic; +}; + +/* Inherited from Microsoft's WAVEFORMATEXTENSIBLE. */ +struct acpi_nhlt2_wave_formatext { + u16 format_tag; + u16 channel_count; + u32 samples_per_sec; + u32 avg_bytes_per_sec; + u16 block_align; + u16 bits_per_sample; + u16 extra_format_size; + u16 valid_bits_per_sample; + u32 channel_mask; + u8 subformat[16]; +}; + +struct acpi_nhlt2_format_config { + struct acpi_nhlt2_wave_formatext format; + struct acpi_nhlt_config config; +}; + +struct acpi_nhlt2_formats_config { + u8 formats_count; + struct acpi_nhlt2_format_config formats[]; +}; + +struct acpi_nhlt2_device_info { + u8 id[16]; + u8 instance_id; + u8 port_id; +}; + +struct acpi_nhlt_devices_info { + u8 devices_count; + struct acpi_nhlt2_device_info devices[]; +}; + /******************************************************************************* * * PCCT - Platform Communications Channel Table (ACPI 5.0) -- cgit 1.2.3-korg From 82b8acc06ea48b69c2482ac2ac994656c3740d08 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 19 Mar 2024 09:30:16 +0100 Subject: ACPI: NHLT: Introduce API for the table The table is composed of a range of endpoints with each describing audio formats they support. Most of the operations involve iterating over elements of the table and filtering them. Simplify the process by implementing range of getters. While the acpi_nhlt_endpoint_mic_count() stands out a bit, it is a critical component for any AudioDSP driver to know how many digital microphones it is dealing with. Signed-off-by: Cezary Rojewski Signed-off-by: Rafael J. Wysocki --- drivers/acpi/Kconfig | 3 + drivers/acpi/Makefile | 1 + drivers/acpi/nhlt.c | 289 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/acpi/nhlt.h | 181 +++++++++++++++++++++++++++++++ 4 files changed, 474 insertions(+) create mode 100644 drivers/acpi/nhlt.c create mode 100644 include/acpi/nhlt.h diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index ff1689bb3124dd..e3a7c2aedd5f00 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -469,6 +469,9 @@ config ACPI_REDUCED_HARDWARE_ONLY If you are unsure what to do, do not enable this option. +config ACPI_NHLT + bool + source "drivers/acpi/nfit/Kconfig" source "drivers/acpi/numa/Kconfig" source "drivers/acpi/apei/Kconfig" diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 8cc8c0d9c8732b..d69d5444acdb34 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -93,6 +93,7 @@ obj-$(CONFIG_ACPI_THERMAL_LIB) += thermal_lib.o obj-$(CONFIG_ACPI_THERMAL) += thermal.o obj-$(CONFIG_ACPI_PLATFORM_PROFILE) += platform_profile.o obj-$(CONFIG_ACPI_NFIT) += nfit/ +obj-$(CONFIG_ACPI_NHLT) += nhlt.o obj-$(CONFIG_ACPI_NUMA) += numa/ obj-$(CONFIG_ACPI) += acpi_memhotplug.o obj-$(CONFIG_ACPI_HOTPLUG_IOAPIC) += ioapic.o diff --git a/drivers/acpi/nhlt.c b/drivers/acpi/nhlt.c new file mode 100644 index 00000000000000..ab722a95cbb5be --- /dev/null +++ b/drivers/acpi/nhlt.c @@ -0,0 +1,289 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright(c) 2023-2024 Intel Corporation + * + * Authors: Cezary Rojewski + * Amadeusz Slawinski + */ + +#define pr_fmt(fmt) "ACPI: NHLT: " fmt + +#include +#include +#include +#include +#include +#include +#include + +static struct acpi_table_nhlt2 *acpi_gbl_nhlt; + +static struct acpi_table_nhlt2 empty_nhlt = { + .header = { + .signature = ACPI_SIG_NHLT, + }, +}; + +/** + * acpi_nhlt_get_gbl_table - Retrieve a pointer to the first NHLT table. + * + * If there is no NHLT in the system, acpi_gbl_nhlt will instead point to an + * empty table. + * + * Return: ACPI status code of the operation. + */ +acpi_status acpi_nhlt_get_gbl_table(void) +{ + acpi_status status; + + status = acpi_get_table(ACPI_SIG_NHLT, 0, (struct acpi_table_header **)(&acpi_gbl_nhlt)); + if (!acpi_gbl_nhlt) + acpi_gbl_nhlt = &empty_nhlt; + return status; +} +EXPORT_SYMBOL_GPL(acpi_nhlt_get_gbl_table); + +/** + * acpi_nhlt_put_gbl_table - Release the global NHLT table. + */ +void acpi_nhlt_put_gbl_table(void) +{ + acpi_put_table((struct acpi_table_header *)acpi_gbl_nhlt); +} +EXPORT_SYMBOL_GPL(acpi_nhlt_put_gbl_table); + +/** + * acpi_nhlt_endpoint_match - Verify if an endpoint matches criteria. + * @ep: the endpoint to check. + * @link_type: the hardware link type, e.g.: PDM or SSP. + * @dev_type: the device type. + * @dir: stream direction. + * @bus_id: the ID of virtual bus hosting the endpoint. + * + * Either of @link_type, @dev_type, @dir or @bus_id may be set to a negative + * value to ignore the parameter when matching. + * + * Return: %true if endpoint matches specified criteria or %false otherwise. + */ +bool acpi_nhlt_endpoint_match(const struct acpi_nhlt2_endpoint *ep, + int link_type, int dev_type, int dir, int bus_id) +{ + return ep && + (link_type < 0 || ep->link_type == link_type) && + (dev_type < 0 || ep->device_type == dev_type) && + (bus_id < 0 || ep->virtual_bus_id == bus_id) && + (dir < 0 || ep->direction == dir); +} +EXPORT_SYMBOL_GPL(acpi_nhlt_endpoint_match); + +/** + * acpi_nhlt_tb_find_endpoint - Search a NHLT table for an endpoint. + * @tb: the table to search. + * @link_type: the hardware link type, e.g.: PDM or SSP. + * @dev_type: the device type. + * @dir: stream direction. + * @bus_id: the ID of virtual bus hosting the endpoint. + * + * Either of @link_type, @dev_type, @dir or @bus_id may be set to a negative + * value to ignore the parameter during the search. + * + * Return: A pointer to endpoint matching the criteria, %NULL if not found or + * an ERR_PTR() otherwise. + */ +struct acpi_nhlt2_endpoint * +acpi_nhlt_tb_find_endpoint(const struct acpi_table_nhlt2 *tb, + int link_type, int dev_type, int dir, int bus_id) +{ + struct acpi_nhlt2_endpoint *ep; + + for_each_nhlt_endpoint(tb, ep) + if (acpi_nhlt_endpoint_match(ep, link_type, dev_type, dir, bus_id)) + return ep; + return NULL; +} +EXPORT_SYMBOL_GPL(acpi_nhlt_tb_find_endpoint); + +/** + * acpi_nhlt_find_endpoint - Search all NHLT tables for an endpoint. + * @link_type: the hardware link type, e.g.: PDM or SSP. + * @dev_type: the device type. + * @dir: stream direction. + * @bus_id: the ID of virtual bus hosting the endpoint. + * + * Either of @link_type, @dev_type, @dir or @bus_id may be set to a negative + * value to ignore the parameter during the search. + * + * Return: A pointer to endpoint matching the criteria, %NULL if not found or + * an ERR_PTR() otherwise. + */ +struct acpi_nhlt2_endpoint * +acpi_nhlt_find_endpoint(int link_type, int dev_type, int dir, int bus_id) +{ + /* TODO: Currently limited to table of index 0. */ + return acpi_nhlt_tb_find_endpoint(acpi_gbl_nhlt, link_type, dev_type, dir, bus_id); +} +EXPORT_SYMBOL_GPL(acpi_nhlt_find_endpoint); + +/** + * acpi_nhlt_endpoint_find_fmtcfg - Search endpoint's formats configuration space + * for a specific format. + * @ep: the endpoint to search. + * @ch: number of channels. + * @rate: samples per second. + * @vbps: valid bits per sample. + * @bps: bits per sample. + * + * Return: A pointer to format matching the criteria, %NULL if not found or + * an ERR_PTR() otherwise. + */ +struct acpi_nhlt2_format_config * +acpi_nhlt_endpoint_find_fmtcfg(const struct acpi_nhlt2_endpoint *ep, + u16 ch, u32 rate, u16 vbps, u16 bps) +{ + struct acpi_nhlt2_wave_formatext *wav; + struct acpi_nhlt2_format_config *fmt; + + for_each_nhlt_endpoint_fmtcfg(ep, fmt) { + wav = &fmt->format; + + if (wav->valid_bits_per_sample == vbps && + wav->samples_per_sec == rate && + wav->bits_per_sample == bps && + wav->channel_count == ch) + return fmt; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(acpi_nhlt_endpoint_find_fmtcfg); + +/** + * acpi_nhlt_tb_find_fmtcfg - Search a NHLT table for a specific format. + * @tb: the table to search. + * @link_type: the hardware link type, e.g.: PDM or SSP. + * @dev_type: the device type. + * @dir: stream direction. + * @bus_id: the ID of virtual bus hosting the endpoint. + * + * @ch: number of channels. + * @rate: samples per second. + * @vbps: valid bits per sample. + * @bps: bits per sample. + * + * Either of @link_type, @dev_type, @dir or @bus_id may be set to a negative + * value to ignore the parameter during the search. + * + * Return: A pointer to format matching the criteria, %NULL if not found or + * an ERR_PTR() otherwise. + */ +struct acpi_nhlt2_format_config * +acpi_nhlt_tb_find_fmtcfg(const struct acpi_table_nhlt2 *tb, + int link_type, int dev_type, int dir, int bus_id, + u16 ch, u32 rate, u16 vbps, u16 bps) +{ + struct acpi_nhlt2_format_config *fmt; + struct acpi_nhlt2_endpoint *ep; + + for_each_nhlt_endpoint(tb, ep) { + if (!acpi_nhlt_endpoint_match(ep, link_type, dev_type, dir, bus_id)) + continue; + + fmt = acpi_nhlt_endpoint_find_fmtcfg(ep, ch, rate, vbps, bps); + if (fmt) + return fmt; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(acpi_nhlt_tb_find_fmtcfg); + +/** + * acpi_nhlt_find_fmtcfg - Search all NHLT tables for a specific format. + * @link_type: the hardware link type, e.g.: PDM or SSP. + * @dev_type: the device type. + * @dir: stream direction. + * @bus_id: the ID of virtual bus hosting the endpoint. + * + * @ch: number of channels. + * @rate: samples per second. + * @vbps: valid bits per sample. + * @bps: bits per sample. + * + * Either of @link_type, @dev_type, @dir or @bus_id may be set to a negative + * value to ignore the parameter during the search. + * + * Return: A pointer to format matching the criteria, %NULL if not found or + * an ERR_PTR() otherwise. + */ +struct acpi_nhlt2_format_config * +acpi_nhlt_find_fmtcfg(int link_type, int dev_type, int dir, int bus_id, + u16 ch, u32 rate, u16 vbps, u16 bps) +{ + /* TODO: Currently limited to table of index 0. */ + return acpi_nhlt_tb_find_fmtcfg(acpi_gbl_nhlt, link_type, dev_type, dir, bus_id, + ch, rate, vbps, bps); +} +EXPORT_SYMBOL_GPL(acpi_nhlt_find_fmtcfg); + +static bool acpi_nhlt_config_is_micdevice(struct acpi_nhlt_config *cfg) +{ + return cfg->capabilities_size >= sizeof(struct acpi_nhlt_micdevice_config); +} + +static bool acpi_nhlt_config_is_vendor_micdevice(struct acpi_nhlt_config *cfg) +{ + struct acpi_nhlt_vendor_micdevice_config *devcfg = __acpi_nhlt_config_caps(cfg); + + return cfg->capabilities_size >= sizeof(*devcfg) && + cfg->capabilities_size == struct_size(devcfg, mics, devcfg->mics_count); +} + +/** + * acpi_nhlt_endpoint_mic_count - Retrieve number of digital microphones for a PDM endpoint. + * @ep: the endpoint to return microphones count for. + * + * Return: A number of microphones or an error code if an invalid endpoint is provided. + */ +int acpi_nhlt_endpoint_mic_count(const struct acpi_nhlt2_endpoint *ep) +{ + union acpi_nhlt_device_config *devcfg; + struct acpi_nhlt2_format_config *fmt; + struct acpi_nhlt_config *cfg; + u16 max_ch = 0; + + if (!ep || ep->link_type != ACPI_NHLT_LINKTYPE_PDM) + return -EINVAL; + + /* Find max number of channels based on formats configuration. */ + for_each_nhlt_endpoint_fmtcfg(ep, fmt) + max_ch = max(fmt->format.channel_count, max_ch); + + cfg = __acpi_nhlt_endpoint_config(ep); + devcfg = __acpi_nhlt_config_caps(cfg); + + /* If @ep is not a mic array, fallback to channels count. */ + if (!acpi_nhlt_config_is_micdevice(cfg) || + devcfg->gen.config_type != ACPI_NHLT_CONFIGTYPE_MICARRAY) + return max_ch; + + switch (devcfg->mic.array_type) { + case ACPI_NHLT_ARRAYTYPE_LINEAR2_SMALL: + case ACPI_NHLT_ARRAYTYPE_LINEAR2_BIG: + return 2; + + case ACPI_NHLT_ARRAYTYPE_LINEAR4_GEO1: + case ACPI_NHLT_ARRAYTYPE_PLANAR4_LSHAPED: + case ACPI_NHLT_ARRAYTYPE_LINEAR4_GEO2: + return 4; + + case ACPI_NHLT_ARRAYTYPE_VENDOR: + if (!acpi_nhlt_config_is_vendor_micdevice(cfg)) + return -EINVAL; + return devcfg->vendor_mic.mics_count; + + default: + pr_warn("undefined mic array type: %#x\n", devcfg->mic.array_type); + return max_ch; + } +} +EXPORT_SYMBOL_GPL(acpi_nhlt_endpoint_mic_count); diff --git a/include/acpi/nhlt.h b/include/acpi/nhlt.h new file mode 100644 index 00000000000000..7c394e7bc2bb69 --- /dev/null +++ b/include/acpi/nhlt.h @@ -0,0 +1,181 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright(c) 2023-2024 Intel Corporation + * + * Authors: Cezary Rojewski + * Amadeusz Slawinski + */ + +#ifndef __ACPI_NHLT_H__ +#define __ACPI_NHLT_H__ + +#include +#include +#include +#include + +#define __acpi_nhlt_endpoint_config(ep) ((void *)((ep) + 1)) +#define __acpi_nhlt_config_caps(cfg) ((void *)((cfg) + 1)) + +/** + * acpi_nhlt_endpoint_fmtscfg - Get the formats configuration space. + * @ep: the endpoint to retrieve the space for. + * + * Return: A pointer to the formats configuration space. + */ +static inline struct acpi_nhlt2_formats_config * +acpi_nhlt_endpoint_fmtscfg(const struct acpi_nhlt2_endpoint *ep) +{ + struct acpi_nhlt_config *cfg = __acpi_nhlt_endpoint_config(ep); + + return (struct acpi_nhlt2_formats_config *)((u8 *)(cfg + 1) + cfg->capabilities_size); +} + +#define __acpi_nhlt_first_endpoint(tb) \ + ((void *)(tb + 1)) + +#define __acpi_nhlt_next_endpoint(ep) \ + ((void *)((u8 *)(ep) + (ep)->length)) + +#define __acpi_nhlt_get_endpoint(tb, ep, i) \ + ((i) ? __acpi_nhlt_next_endpoint(ep) : __acpi_nhlt_first_endpoint(tb)) + +#define __acpi_nhlt_first_fmtcfg(fmts) \ + ((void *)(fmts + 1)) + +#define __acpi_nhlt_next_fmtcfg(fmt) \ + ((void *)((u8 *)((fmt) + 1) + (fmt)->config.capabilities_size)) + +#define __acpi_nhlt_get_fmtcfg(fmts, fmt, i) \ + ((i) ? __acpi_nhlt_next_fmtcfg(fmt) : __acpi_nhlt_first_fmtcfg(fmts)) + +/* + * The for_each_nhlt_*() macros rely on an iterator to deal with the + * variable length of each endpoint structure and the possible presence + * of an OED-Config used by Windows only. + */ + +/** + * for_each_nhlt_endpoint - Iterate over endpoints in a NHLT table. + * @tb: the pointer to a NHLT table. + * @ep: the pointer to endpoint to use as loop cursor. + */ +#define for_each_nhlt_endpoint(tb, ep) \ + for (unsigned int __i = 0; \ + __i < (tb)->endpoints_count && \ + (ep = __acpi_nhlt_get_endpoint(tb, ep, __i)); \ + __i++) + +/** + * for_each_nhlt_fmtcfg - Iterate over format configurations. + * @fmts: the pointer to formats configuration space. + * @fmt: the pointer to format to use as loop cursor. + */ +#define for_each_nhlt_fmtcfg(fmts, fmt) \ + for (unsigned int __i = 0; \ + __i < (fmts)->formats_count && \ + (fmt = __acpi_nhlt_get_fmtcfg(fmts, fmt, __i)); \ + __i++) + +/** + * for_each_nhlt_endpoint_fmtcfg - Iterate over format configurations in an endpoint. + * @ep: the pointer to an endpoint. + * @fmt: the pointer to format to use as loop cursor. + */ +#define for_each_nhlt_endpoint_fmtcfg(ep, fmt) \ + for_each_nhlt_fmtcfg(acpi_nhlt_endpoint_fmtscfg(ep), fmt) + +#if IS_ENABLED(CONFIG_ACPI_NHLT) + +/* + * System-wide pointer to the first NHLT table. + * + * A sound driver may utilize acpi_nhlt_get/put_gbl_table() on its + * initialization and removal respectively to avoid excessive mapping + * and unmapping of the memory occupied by the table between streaming + * operations. + */ + +acpi_status acpi_nhlt_get_gbl_table(void); +void acpi_nhlt_put_gbl_table(void); + +bool acpi_nhlt_endpoint_match(const struct acpi_nhlt2_endpoint *ep, + int link_type, int dev_type, int dir, int bus_id); +struct acpi_nhlt2_endpoint * +acpi_nhlt_tb_find_endpoint(const struct acpi_table_nhlt2 *tb, + int link_type, int dev_type, int dir, int bus_id); +struct acpi_nhlt2_endpoint * +acpi_nhlt_find_endpoint(int link_type, int dev_type, int dir, int bus_id); +struct acpi_nhlt2_format_config * +acpi_nhlt_endpoint_find_fmtcfg(const struct acpi_nhlt2_endpoint *ep, + u16 ch, u32 rate, u16 vbps, u16 bps); +struct acpi_nhlt2_format_config * +acpi_nhlt_tb_find_fmtcfg(const struct acpi_table_nhlt2 *tb, + int link_type, int dev_type, int dir, int bus_id, + u16 ch, u32 rate, u16 vpbs, u16 bps); +struct acpi_nhlt2_format_config * +acpi_nhlt_find_fmtcfg(int link_type, int dev_type, int dir, int bus_id, + u16 ch, u32 rate, u16 vpbs, u16 bps); +int acpi_nhlt_endpoint_mic_count(const struct acpi_nhlt2_endpoint *ep); + +#else /* !CONFIG_ACPI_NHLT */ + +static inline acpi_status acpi_nhlt_get_gbl_table(void) +{ + return AE_NOT_FOUND; +} + +static inline void acpi_nhlt_put_gbl_table(void) +{ +} + +static inline bool +acpi_nhlt_endpoint_match(const struct acpi_nhlt2_endpoint *ep, + int link_type, int dev_type, int dir, int bus_id) +{ + return false; +} + +static inline struct acpi_nhlt2_endpoint * +acpi_nhlt_tb_find_endpoint(const struct acpi_table_nhlt2 *tb, + int link_type, int dev_type, int dir, int bus_id) +{ + return NULL; +} + +static inline struct acpi_nhlt2_format_config * +acpi_nhlt_endpoint_find_fmtcfg(const struct acpi_nhlt2_endpoint *ep, + u16 ch, u32 rate, u16 vbps, u16 bps) +{ + return NULL; +} + +static inline struct acpi_nhlt2_format_config * +acpi_nhlt_tb_find_fmtcfg(const struct acpi_table_nhlt2 *tb, + int link_type, int dev_type, int dir, int bus_id, + u16 ch, u32 rate, u16 vpbs, u16 bps) +{ + return NULL; +} + +static inline int acpi_nhlt_endpoint_mic_count(const struct acpi_nhlt2_endpoint *ep) +{ + return 0; +} + +static inline struct acpi_nhlt2_endpoint * +acpi_nhlt_find_endpoint(int link_type, int dev_type, int dir, int bus_id) +{ + return NULL; +} + +static inline struct acpi_nhlt2_format_config * +acpi_nhlt_find_fmtcfg(int link_type, int dev_type, int dir, int bus_id, + u16 ch, u32 rate, u16 vpbs, u16 bps) +{ + return NULL; +} + +#endif /* CONFIG_ACPI_NHLT */ + +#endif /* __ACPI_NHLT_H__ */ -- cgit 1.2.3-korg From 659a9490ccfbfad4aa5ab351f8a6f5f83ce5ed6c Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 19 Mar 2024 09:30:17 +0100 Subject: ACPI: NHLT: Drop redundant types ACPICA commit 0c7379eae2a0342bfc36d6b7db0bb90ad13a5a3e There are no users for the duplicated NHLT table components. Link: https://github.com/acpica/acpica/pull/890 Signed-off-by: Cezary Rojewski Signed-off-by: Rafael J. Wysocki --- include/acpi/actbl2.h | 254 -------------------------------------------------- 1 file changed, 254 deletions(-) diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h index 8030a17431004f..31a716a7434030 100644 --- a/include/acpi/actbl2.h +++ b/include/acpi/actbl2.h @@ -1887,260 +1887,6 @@ struct nfit_device_handle { #define ACPI_NFIT_GET_NODE_ID(handle) \ (((handle) & ACPI_NFIT_NODE_ID_MASK) >> ACPI_NFIT_NODE_ID_OFFSET) -/******************************************************************************* - * - * NHLT - Non HD Audio Link Table - * - * Conforms to: Intel Smart Sound Technology NHLT Specification - * Version 0.8.1, January 2020. - * - ******************************************************************************/ - -/* Main table */ - -struct acpi_table_nhlt { - struct acpi_table_header header; /* Common ACPI table header */ - u8 endpoint_count; -}; - -struct acpi_nhlt_endpoint { - u32 descriptor_length; - u8 link_type; - u8 instance_id; - u16 vendor_id; - u16 device_id; - u16 revision_id; - u32 subsystem_id; - u8 device_type; - u8 direction; - u8 virtual_bus_id; -}; - -/* Types for link_type field above */ - -#define ACPI_NHLT_RESERVED_HD_AUDIO 0 -#define ACPI_NHLT_RESERVED_DSP 1 -#define ACPI_NHLT_PDM 2 -#define ACPI_NHLT_SSP 3 -#define ACPI_NHLT_RESERVED_SLIMBUS 4 -#define ACPI_NHLT_RESERVED_SOUNDWIRE 5 -#define ACPI_NHLT_TYPE_RESERVED 6 /* 6 and above are reserved */ - -/* All other values above are reserved */ - -/* Values for device_id field above */ - -#define ACPI_NHLT_PDM_DMIC 0xAE20 -#define ACPI_NHLT_BT_SIDEBAND 0xAE30 -#define ACPI_NHLT_I2S_TDM_CODECS 0xAE23 - -/* Values for device_type field above */ - -/* SSP Link */ - -#define ACPI_NHLT_LINK_BT_SIDEBAND 0 -#define ACPI_NHLT_LINK_FM 1 -#define ACPI_NHLT_LINK_MODEM 2 -/* 3 is reserved */ -#define ACPI_NHLT_LINK_SSP_ANALOG_CODEC 4 - -/* PDM Link */ - -#define ACPI_NHLT_PDM_ON_CAVS_1P8 0 -#define ACPI_NHLT_PDM_ON_CAVS_1P5 1 - -/* Values for Direction field above */ - -#define ACPI_NHLT_DIR_RENDER 0 -#define ACPI_NHLT_DIR_CAPTURE 1 -#define ACPI_NHLT_DIR_RENDER_LOOPBACK 2 -#define ACPI_NHLT_DIR_RENDER_FEEDBACK 3 -#define ACPI_NHLT_DIR_RESERVED 4 /* 4 and above are reserved */ - -struct acpi_nhlt_device_specific_config { - u32 capabilities_size; - u8 virtual_slot; - u8 config_type; -}; - -struct acpi_nhlt_device_specific_config_a { - u32 capabilities_size; - u8 virtual_slot; - u8 config_type; - u8 array_type; -}; - -/* Values for Config Type above */ - -#define ACPI_NHLT_CONFIG_TYPE_GENERIC 0x00 -#define ACPI_NHLT_CONFIG_TYPE_MIC_ARRAY 0x01 -#define ACPI_NHLT_CONFIG_TYPE_RENDER_FEEDBACK 0x03 -#define ACPI_NHLT_CONFIG_TYPE_RESERVED 0x04 /* 4 and above are reserved */ - -struct acpi_nhlt_device_specific_config_b { - u32 capabilities_size; -}; - -struct acpi_nhlt_device_specific_config_c { - u32 capabilities_size; - u8 virtual_slot; -}; - -struct acpi_nhlt_render_device_specific_config { - u32 capabilities_size; - u8 virtual_slot; -}; - -struct acpi_nhlt_wave_extensible { - u16 format_tag; - u16 channel_count; - u32 samples_per_sec; - u32 avg_bytes_per_sec; - u16 block_align; - u16 bits_per_sample; - u16 extra_format_size; - u16 valid_bits_per_sample; - u32 channel_mask; - u8 sub_format_guid[16]; -}; - -/* Values for channel_mask above */ - -#define ACPI_NHLT_SPKR_FRONT_LEFT 0x1 -#define ACPI_NHLT_SPKR_FRONT_RIGHT 0x2 -#define ACPI_NHLT_SPKR_FRONT_CENTER 0x4 -#define ACPI_NHLT_SPKR_LOW_FREQ 0x8 -#define ACPI_NHLT_SPKR_BACK_LEFT 0x10 -#define ACPI_NHLT_SPKR_BACK_RIGHT 0x20 -#define ACPI_NHLT_SPKR_FRONT_LEFT_OF_CENTER 0x40 -#define ACPI_NHLT_SPKR_FRONT_RIGHT_OF_CENTER 0x80 -#define ACPI_NHLT_SPKR_BACK_CENTER 0x100 -#define ACPI_NHLT_SPKR_SIDE_LEFT 0x200 -#define ACPI_NHLT_SPKR_SIDE_RIGHT 0x400 -#define ACPI_NHLT_SPKR_TOP_CENTER 0x800 -#define ACPI_NHLT_SPKR_TOP_FRONT_LEFT 0x1000 -#define ACPI_NHLT_SPKR_TOP_FRONT_CENTER 0x2000 -#define ACPI_NHLT_SPKR_TOP_FRONT_RIGHT 0x4000 -#define ACPI_NHLT_SPKR_TOP_BACK_LEFT 0x8000 -#define ACPI_NHLT_SPKR_TOP_BACK_CENTER 0x10000 -#define ACPI_NHLT_SPKR_TOP_BACK_RIGHT 0x20000 - -struct acpi_nhlt_format_config { - struct acpi_nhlt_wave_extensible format; - u32 capability_size; - u8 capabilities[]; -}; - -struct acpi_nhlt_formats_config { - u8 formats_count; -}; - -struct acpi_nhlt_device_specific_hdr { - u8 virtual_slot; - u8 config_type; -}; - -/* Types for config_type above */ - -#define ACPI_NHLT_GENERIC 0 -#define ACPI_NHLT_MIC 1 -#define ACPI_NHLT_RENDER 3 - -struct acpi_nhlt_mic_device_specific_config { - struct acpi_nhlt_device_specific_hdr device_config; - u8 array_type_ext; -}; - -/* Values for array_type_ext above */ - -#define ACPI_NHLT_ARRAY_TYPE_RESERVED 0x09 /* 9 and below are reserved */ -#define ACPI_NHLT_SMALL_LINEAR_2ELEMENT 0x0A -#define ACPI_NHLT_BIG_LINEAR_2ELEMENT 0x0B -#define ACPI_NHLT_FIRST_GEOMETRY_LINEAR_4ELEMENT 0x0C -#define ACPI_NHLT_PLANAR_LSHAPED_4ELEMENT 0x0D -#define ACPI_NHLT_SECOND_GEOMETRY_LINEAR_4ELEMENT 0x0E -#define ACPI_NHLT_VENDOR_DEFINED 0x0F -#define ACPI_NHLT_ARRAY_TYPE_MASK 0x0F -#define ACPI_NHLT_ARRAY_TYPE_EXT_MASK 0x10 - -#define ACPI_NHLT_NO_EXTENSION 0x0 -#define ACPI_NHLT_MIC_SNR_SENSITIVITY_EXT (1<<4) - -struct acpi_nhlt_vendor_mic_count { - u8 microphone_count; -}; - -struct acpi_nhlt_vendor_mic_config { - u8 type; - u8 panel; - u16 speaker_position_distance; /* mm */ - u16 horizontal_offset; /* mm */ - u16 vertical_offset; /* mm */ - u8 frequency_low_band; /* 5*Hz */ - u8 frequency_high_band; /* 500*Hz */ - u16 direction_angle; /* -180 - + 180 */ - u16 elevation_angle; /* -180 - + 180 */ - u16 work_vertical_angle_begin; /* -180 - + 180 with 2 deg step */ - u16 work_vertical_angle_end; /* -180 - + 180 with 2 deg step */ - u16 work_horizontal_angle_begin; /* -180 - + 180 with 2 deg step */ - u16 work_horizontal_angle_end; /* -180 - + 180 with 2 deg step */ -}; - -/* Values for Type field above */ - -#define ACPI_NHLT_MIC_OMNIDIRECTIONAL 0 -#define ACPI_NHLT_MIC_SUBCARDIOID 1 -#define ACPI_NHLT_MIC_CARDIOID 2 -#define ACPI_NHLT_MIC_SUPER_CARDIOID 3 -#define ACPI_NHLT_MIC_HYPER_CARDIOID 4 -#define ACPI_NHLT_MIC_8_SHAPED 5 -#define ACPI_NHLT_MIC_RESERVED6 6 /* 6 is reserved */ -#define ACPI_NHLT_MIC_VENDOR_DEFINED 7 -#define ACPI_NHLT_MIC_RESERVED 8 /* 8 and above are reserved */ - -/* Values for Panel field above */ - -#define ACPI_NHLT_MIC_POSITION_TOP 0 -#define ACPI_NHLT_MIC_POSITION_BOTTOM 1 -#define ACPI_NHLT_MIC_POSITION_LEFT 2 -#define ACPI_NHLT_MIC_POSITION_RIGHT 3 -#define ACPI_NHLT_MIC_POSITION_FRONT 4 -#define ACPI_NHLT_MIC_POSITION_BACK 5 -#define ACPI_NHLT_MIC_POSITION_RESERVED 6 /* 6 and above are reserved */ - -struct acpi_nhlt_vendor_mic_device_specific_config { - struct acpi_nhlt_mic_device_specific_config mic_array_device_config; - u8 number_of_microphones; - struct acpi_nhlt_vendor_mic_config mic_config[]; /* Indexed by number_of_microphones */ -}; - -/* Microphone SNR and Sensitivity extension */ - -struct acpi_nhlt_mic_snr_sensitivity_extension { - u32 SNR; - u32 sensitivity; -}; - -/* Render device with feedback */ - -struct acpi_nhlt_render_feedback_device_specific_config { - u8 feedback_virtual_slot; /* Render slot in case of capture */ - u16 feedback_channels; /* Informative only */ - u16 feedback_valid_bits_per_sample; -}; - -/* Non documented structures */ - -struct acpi_nhlt_device_info_count { - u8 structure_count; -}; - -struct acpi_nhlt_device_info { - u8 device_id[16]; - u8 device_instance_id; - u8 device_port_id; -}; - /******************************************************************************* * * NHLT - Non HDAudio Link Table -- cgit 1.2.3-korg From a640acab545b21ed1f347376f34d34e461ea92ba Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 19 Mar 2024 09:30:18 +0100 Subject: ACPI: NHLT: Streamline struct naming Few recently introduced structs are named 'nhlt2' instead of 'nhlt' to avoid naming conflicts. With duplicate types gone, the conflicts are no more. Signed-off-by: Cezary Rojewski Signed-off-by: Rafael J. Wysocki --- drivers/acpi/nhlt.c | 36 ++++++++++++++++++------------------ include/acpi/actbl2.h | 22 +++++++++++----------- include/acpi/nhlt.h | 46 +++++++++++++++++++++++----------------------- 3 files changed, 52 insertions(+), 52 deletions(-) diff --git a/drivers/acpi/nhlt.c b/drivers/acpi/nhlt.c index ab722a95cbb5be..dc1bd0df9228ed 100644 --- a/drivers/acpi/nhlt.c +++ b/drivers/acpi/nhlt.c @@ -16,9 +16,9 @@ #include #include -static struct acpi_table_nhlt2 *acpi_gbl_nhlt; +static struct acpi_table_nhlt *acpi_gbl_nhlt; -static struct acpi_table_nhlt2 empty_nhlt = { +static struct acpi_table_nhlt empty_nhlt = { .header = { .signature = ACPI_SIG_NHLT, }, @@ -65,7 +65,7 @@ EXPORT_SYMBOL_GPL(acpi_nhlt_put_gbl_table); * * Return: %true if endpoint matches specified criteria or %false otherwise. */ -bool acpi_nhlt_endpoint_match(const struct acpi_nhlt2_endpoint *ep, +bool acpi_nhlt_endpoint_match(const struct acpi_nhlt_endpoint *ep, int link_type, int dev_type, int dir, int bus_id) { return ep && @@ -90,11 +90,11 @@ EXPORT_SYMBOL_GPL(acpi_nhlt_endpoint_match); * Return: A pointer to endpoint matching the criteria, %NULL if not found or * an ERR_PTR() otherwise. */ -struct acpi_nhlt2_endpoint * -acpi_nhlt_tb_find_endpoint(const struct acpi_table_nhlt2 *tb, +struct acpi_nhlt_endpoint * +acpi_nhlt_tb_find_endpoint(const struct acpi_table_nhlt *tb, int link_type, int dev_type, int dir, int bus_id) { - struct acpi_nhlt2_endpoint *ep; + struct acpi_nhlt_endpoint *ep; for_each_nhlt_endpoint(tb, ep) if (acpi_nhlt_endpoint_match(ep, link_type, dev_type, dir, bus_id)) @@ -116,7 +116,7 @@ EXPORT_SYMBOL_GPL(acpi_nhlt_tb_find_endpoint); * Return: A pointer to endpoint matching the criteria, %NULL if not found or * an ERR_PTR() otherwise. */ -struct acpi_nhlt2_endpoint * +struct acpi_nhlt_endpoint * acpi_nhlt_find_endpoint(int link_type, int dev_type, int dir, int bus_id) { /* TODO: Currently limited to table of index 0. */ @@ -136,12 +136,12 @@ EXPORT_SYMBOL_GPL(acpi_nhlt_find_endpoint); * Return: A pointer to format matching the criteria, %NULL if not found or * an ERR_PTR() otherwise. */ -struct acpi_nhlt2_format_config * -acpi_nhlt_endpoint_find_fmtcfg(const struct acpi_nhlt2_endpoint *ep, +struct acpi_nhlt_format_config * +acpi_nhlt_endpoint_find_fmtcfg(const struct acpi_nhlt_endpoint *ep, u16 ch, u32 rate, u16 vbps, u16 bps) { - struct acpi_nhlt2_wave_formatext *wav; - struct acpi_nhlt2_format_config *fmt; + struct acpi_nhlt_wave_formatext *wav; + struct acpi_nhlt_format_config *fmt; for_each_nhlt_endpoint_fmtcfg(ep, fmt) { wav = &fmt->format; @@ -176,13 +176,13 @@ EXPORT_SYMBOL_GPL(acpi_nhlt_endpoint_find_fmtcfg); * Return: A pointer to format matching the criteria, %NULL if not found or * an ERR_PTR() otherwise. */ -struct acpi_nhlt2_format_config * -acpi_nhlt_tb_find_fmtcfg(const struct acpi_table_nhlt2 *tb, +struct acpi_nhlt_format_config * +acpi_nhlt_tb_find_fmtcfg(const struct acpi_table_nhlt *tb, int link_type, int dev_type, int dir, int bus_id, u16 ch, u32 rate, u16 vbps, u16 bps) { - struct acpi_nhlt2_format_config *fmt; - struct acpi_nhlt2_endpoint *ep; + struct acpi_nhlt_format_config *fmt; + struct acpi_nhlt_endpoint *ep; for_each_nhlt_endpoint(tb, ep) { if (!acpi_nhlt_endpoint_match(ep, link_type, dev_type, dir, bus_id)) @@ -215,7 +215,7 @@ EXPORT_SYMBOL_GPL(acpi_nhlt_tb_find_fmtcfg); * Return: A pointer to format matching the criteria, %NULL if not found or * an ERR_PTR() otherwise. */ -struct acpi_nhlt2_format_config * +struct acpi_nhlt_format_config * acpi_nhlt_find_fmtcfg(int link_type, int dev_type, int dir, int bus_id, u16 ch, u32 rate, u16 vbps, u16 bps) { @@ -244,10 +244,10 @@ static bool acpi_nhlt_config_is_vendor_micdevice(struct acpi_nhlt_config *cfg) * * Return: A number of microphones or an error code if an invalid endpoint is provided. */ -int acpi_nhlt_endpoint_mic_count(const struct acpi_nhlt2_endpoint *ep) +int acpi_nhlt_endpoint_mic_count(const struct acpi_nhlt_endpoint *ep) { union acpi_nhlt_device_config *devcfg; - struct acpi_nhlt2_format_config *fmt; + struct acpi_nhlt_format_config *fmt; struct acpi_nhlt_config *cfg; u16 max_ch = 0; diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h index 31a716a7434030..f237269bd1cb04 100644 --- a/include/acpi/actbl2.h +++ b/include/acpi/actbl2.h @@ -1894,7 +1894,7 @@ struct nfit_device_handle { * ******************************************************************************/ -struct acpi_table_nhlt2 { +struct acpi_table_nhlt { struct acpi_table_header header; /* Common ACPI table header */ u8 endpoints_count; /* @@ -1903,7 +1903,7 @@ struct acpi_table_nhlt2 { */ }; -struct acpi_nhlt2_endpoint { +struct acpi_nhlt_endpoint { u32 length; u8 link_type; u8 instance_id; @@ -1990,7 +1990,7 @@ struct acpi_nhlt_micdevice_config { #define ACPI_NHLT_ARRAYTYPE_LINEAR4_GEO2 0xE #define ACPI_NHLT_ARRAYTYPE_VENDOR 0xF -struct acpi_nhlt2_vendor_mic_config { +struct acpi_nhlt_vendor_mic_config { u8 type; u8 panel; u16 speaker_position_distance; /* mm */ @@ -2031,7 +2031,7 @@ struct acpi_nhlt_vendor_micdevice_config { u8 config_type; u8 array_type; u8 mics_count; - struct acpi_nhlt2_vendor_mic_config mics[]; + struct acpi_nhlt_vendor_mic_config mics[]; }; union acpi_nhlt_device_config { @@ -2042,7 +2042,7 @@ union acpi_nhlt_device_config { }; /* Inherited from Microsoft's WAVEFORMATEXTENSIBLE. */ -struct acpi_nhlt2_wave_formatext { +struct acpi_nhlt_wave_formatext { u16 format_tag; u16 channel_count; u32 samples_per_sec; @@ -2055,17 +2055,17 @@ struct acpi_nhlt2_wave_formatext { u8 subformat[16]; }; -struct acpi_nhlt2_format_config { - struct acpi_nhlt2_wave_formatext format; +struct acpi_nhlt_format_config { + struct acpi_nhlt_wave_formatext format; struct acpi_nhlt_config config; }; -struct acpi_nhlt2_formats_config { +struct acpi_nhlt_formats_config { u8 formats_count; - struct acpi_nhlt2_format_config formats[]; + struct acpi_nhlt_format_config formats[]; }; -struct acpi_nhlt2_device_info { +struct acpi_nhlt_device_info { u8 id[16]; u8 instance_id; u8 port_id; @@ -2073,7 +2073,7 @@ struct acpi_nhlt2_device_info { struct acpi_nhlt_devices_info { u8 devices_count; - struct acpi_nhlt2_device_info devices[]; + struct acpi_nhlt_device_info devices[]; }; /******************************************************************************* diff --git a/include/acpi/nhlt.h b/include/acpi/nhlt.h index 7c394e7bc2bb69..2108aa6d0207f3 100644 --- a/include/acpi/nhlt.h +++ b/include/acpi/nhlt.h @@ -23,12 +23,12 @@ * * Return: A pointer to the formats configuration space. */ -static inline struct acpi_nhlt2_formats_config * -acpi_nhlt_endpoint_fmtscfg(const struct acpi_nhlt2_endpoint *ep) +static inline struct acpi_nhlt_formats_config * +acpi_nhlt_endpoint_fmtscfg(const struct acpi_nhlt_endpoint *ep) { struct acpi_nhlt_config *cfg = __acpi_nhlt_endpoint_config(ep); - return (struct acpi_nhlt2_formats_config *)((u8 *)(cfg + 1) + cfg->capabilities_size); + return (struct acpi_nhlt_formats_config *)((u8 *)(cfg + 1) + cfg->capabilities_size); } #define __acpi_nhlt_first_endpoint(tb) \ @@ -99,24 +99,24 @@ acpi_nhlt_endpoint_fmtscfg(const struct acpi_nhlt2_endpoint *ep) acpi_status acpi_nhlt_get_gbl_table(void); void acpi_nhlt_put_gbl_table(void); -bool acpi_nhlt_endpoint_match(const struct acpi_nhlt2_endpoint *ep, +bool acpi_nhlt_endpoint_match(const struct acpi_nhlt_endpoint *ep, int link_type, int dev_type, int dir, int bus_id); -struct acpi_nhlt2_endpoint * -acpi_nhlt_tb_find_endpoint(const struct acpi_table_nhlt2 *tb, +struct acpi_nhlt_endpoint * +acpi_nhlt_tb_find_endpoint(const struct acpi_table_nhlt *tb, int link_type, int dev_type, int dir, int bus_id); -struct acpi_nhlt2_endpoint * +struct acpi_nhlt_endpoint * acpi_nhlt_find_endpoint(int link_type, int dev_type, int dir, int bus_id); -struct acpi_nhlt2_format_config * -acpi_nhlt_endpoint_find_fmtcfg(const struct acpi_nhlt2_endpoint *ep, +struct acpi_nhlt_format_config * +acpi_nhlt_endpoint_find_fmtcfg(const struct acpi_nhlt_endpoint *ep, u16 ch, u32 rate, u16 vbps, u16 bps); -struct acpi_nhlt2_format_config * -acpi_nhlt_tb_find_fmtcfg(const struct acpi_table_nhlt2 *tb, +struct acpi_nhlt_format_config * +acpi_nhlt_tb_find_fmtcfg(const struct acpi_table_nhlt *tb, int link_type, int dev_type, int dir, int bus_id, u16 ch, u32 rate, u16 vpbs, u16 bps); -struct acpi_nhlt2_format_config * +struct acpi_nhlt_format_config * acpi_nhlt_find_fmtcfg(int link_type, int dev_type, int dir, int bus_id, u16 ch, u32 rate, u16 vpbs, u16 bps); -int acpi_nhlt_endpoint_mic_count(const struct acpi_nhlt2_endpoint *ep); +int acpi_nhlt_endpoint_mic_count(const struct acpi_nhlt_endpoint *ep); #else /* !CONFIG_ACPI_NHLT */ @@ -130,46 +130,46 @@ static inline void acpi_nhlt_put_gbl_table(void) } static inline bool -acpi_nhlt_endpoint_match(const struct acpi_nhlt2_endpoint *ep, +acpi_nhlt_endpoint_match(const struct acpi_nhlt_endpoint *ep, int link_type, int dev_type, int dir, int bus_id) { return false; } -static inline struct acpi_nhlt2_endpoint * -acpi_nhlt_tb_find_endpoint(const struct acpi_table_nhlt2 *tb, +static inline struct acpi_nhlt_endpoint * +acpi_nhlt_tb_find_endpoint(const struct acpi_table_nhlt *tb, int link_type, int dev_type, int dir, int bus_id) { return NULL; } -static inline struct acpi_nhlt2_format_config * -acpi_nhlt_endpoint_find_fmtcfg(const struct acpi_nhlt2_endpoint *ep, +static inline struct acpi_nhlt_format_config * +acpi_nhlt_endpoint_find_fmtcfg(const struct acpi_nhlt_endpoint *ep, u16 ch, u32 rate, u16 vbps, u16 bps) { return NULL; } -static inline struct acpi_nhlt2_format_config * -acpi_nhlt_tb_find_fmtcfg(const struct acpi_table_nhlt2 *tb, +static inline struct acpi_nhlt_format_config * +acpi_nhlt_tb_find_fmtcfg(const struct acpi_table_nhlt *tb, int link_type, int dev_type, int dir, int bus_id, u16 ch, u32 rate, u16 vpbs, u16 bps) { return NULL; } -static inline int acpi_nhlt_endpoint_mic_count(const struct acpi_nhlt2_endpoint *ep) +static inline int acpi_nhlt_endpoint_mic_count(const struct acpi_nhlt_endpoint *ep) { return 0; } -static inline struct acpi_nhlt2_endpoint * +static inline struct acpi_nhlt_endpoint * acpi_nhlt_find_endpoint(int link_type, int dev_type, int dir, int bus_id) { return NULL; } -static inline struct acpi_nhlt2_format_config * +static inline struct acpi_nhlt_format_config * acpi_nhlt_find_fmtcfg(int link_type, int dev_type, int dir, int bus_id, u16 ch, u32 rate, u16 vpbs, u16 bps) { -- cgit 1.2.3-korg From 95d43290f1e476b3be782dd17642e452d0436266 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Sat, 9 Mar 2024 21:13:06 +0100 Subject: ACPI: bus: Indicate support for _TFP thru _OSC The ACPI thermal driver already uses the _TPF ACPI method to retrieve precise sampling time values, but this is not reported thru _OSC. Fix this by setting bit 9 ("Fast Thermal Sampling support") when evaluating _OSC. Fixes: a2ee7581afd5 ("ACPI: thermal: Add Thermal fast Sampling Period (_TFP) support") Signed-off-by: Armin Wolf Signed-off-by: Rafael J. Wysocki --- drivers/acpi/bus.c | 2 ++ include/linux/acpi.h | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index d9fa730416f191..9c13a4e43fa826 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -316,6 +316,8 @@ static void acpi_bus_osc_negotiate_platform_control(void) capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PAD_SUPPORT; if (IS_ENABLED(CONFIG_ACPI_PROCESSOR)) capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PPC_OST_SUPPORT; + if (IS_ENABLED(CONFIG_ACPI_THERMAL)) + capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_FAST_THERMAL_SAMPLING_SUPPORT; capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_HOTPLUG_OST_SUPPORT; capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PCLPI_SUPPORT; diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 34829f2c517ac2..51bdd9e08f6dac 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -573,6 +573,7 @@ acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context); #define OSC_SB_CPCV2_SUPPORT 0x00000040 #define OSC_SB_PCLPI_SUPPORT 0x00000080 #define OSC_SB_OSLPI_SUPPORT 0x00000100 +#define OSC_SB_FAST_THERMAL_SAMPLING_SUPPORT 0x00000200 #define OSC_SB_CPC_DIVERSE_HIGH_SUPPORT 0x00001000 #define OSC_SB_GENERIC_INITIATOR_SUPPORT 0x00002000 #define OSC_SB_CPC_FLEXIBLE_ADR_SPACE 0x00004000 -- cgit 1.2.3-korg From 6e8345f23ca37d6d41bb76be5d6a705ddf542817 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Sat, 9 Mar 2024 21:13:07 +0100 Subject: ACPI: bus: Indicate support for more than 16 p-states thru _OSC The code responsible for parsing the available p-states should have no problems handling more than 16 p-states. Indicate this by setting bit 10 ("Greater Than 16 p-state support") when evaluating _OSC. Signed-off-by: Armin Wolf Signed-off-by: Rafael J. Wysocki --- drivers/acpi/bus.c | 1 + include/linux/acpi.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 9c13a4e43fa826..d5b0e80dc48e46 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -321,6 +321,7 @@ static void acpi_bus_osc_negotiate_platform_control(void) capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_HOTPLUG_OST_SUPPORT; capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PCLPI_SUPPORT; + capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_OVER_16_PSTATES_SUPPORT; if (IS_ENABLED(CONFIG_ACPI_PRMT)) capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PRM_SUPPORT; if (IS_ENABLED(CONFIG_ACPI_FFH)) diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 51bdd9e08f6dac..0e0b027e27e213 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -574,6 +574,7 @@ acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context); #define OSC_SB_PCLPI_SUPPORT 0x00000080 #define OSC_SB_OSLPI_SUPPORT 0x00000100 #define OSC_SB_FAST_THERMAL_SAMPLING_SUPPORT 0x00000200 +#define OSC_SB_OVER_16_PSTATES_SUPPORT 0x00000400 #define OSC_SB_CPC_DIVERSE_HIGH_SUPPORT 0x00001000 #define OSC_SB_GENERIC_INITIATOR_SUPPORT 0x00002000 #define OSC_SB_CPC_FLEXIBLE_ADR_SPACE 0x00004000 -- cgit 1.2.3-korg From a8a967a243d71dd635ede076020f665a4df51c63 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Sat, 9 Mar 2024 21:13:08 +0100 Subject: ACPI: bus: Indicate support for the Generic Event Device thru _OSC A device driver for the Generic Event Device (ACPI0013) already exists for quite some time, but support for it was never reported thru _OSC. Fix this by setting bit 11 ("Generic Event Device support") when evaluating _OSC. Fixes: 3db80c230da1 ("ACPI: implement Generic Event Device") Signed-off-by: Armin Wolf Signed-off-by: Rafael J. Wysocki --- drivers/acpi/bus.c | 1 + include/linux/acpi.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index d5b0e80dc48e46..0c48b603098a85 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -322,6 +322,7 @@ static void acpi_bus_osc_negotiate_platform_control(void) capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_HOTPLUG_OST_SUPPORT; capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PCLPI_SUPPORT; capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_OVER_16_PSTATES_SUPPORT; + capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_GED_SUPPORT; if (IS_ENABLED(CONFIG_ACPI_PRMT)) capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PRM_SUPPORT; if (IS_ENABLED(CONFIG_ACPI_FFH)) diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 0e0b027e27e213..b0d909d1f5fc30 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -575,6 +575,7 @@ acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context); #define OSC_SB_OSLPI_SUPPORT 0x00000100 #define OSC_SB_FAST_THERMAL_SAMPLING_SUPPORT 0x00000200 #define OSC_SB_OVER_16_PSTATES_SUPPORT 0x00000400 +#define OSC_SB_GED_SUPPORT 0x00000800 #define OSC_SB_CPC_DIVERSE_HIGH_SUPPORT 0x00001000 #define OSC_SB_GENERIC_INITIATOR_SUPPORT 0x00002000 #define OSC_SB_CPC_FLEXIBLE_ADR_SPACE 0x00004000 -- cgit 1.2.3-korg From d0d4f1474e36b195eaad477373127ae621334c01 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Sat, 9 Mar 2024 21:13:09 +0100 Subject: ACPI: Fix Generic Initiator Affinity _OSC bit The ACPI spec says bit 17 should be used to indicate support for Generic Initiator Affinity Structure in SRAT, but we currently set bit 13 ("Interrupt ResourceSource support"). Fix this by actually setting bit 17 when evaluating _OSC. Fixes: 01aabca2fd54 ("ACPI: Let ACPI know we support Generic Initiator Affinity Structures") Signed-off-by: Armin Wolf Signed-off-by: Rafael J. Wysocki --- include/linux/acpi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/acpi.h b/include/linux/acpi.h index b0d909d1f5fc30..e77783e101c36d 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -577,8 +577,8 @@ acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context); #define OSC_SB_OVER_16_PSTATES_SUPPORT 0x00000400 #define OSC_SB_GED_SUPPORT 0x00000800 #define OSC_SB_CPC_DIVERSE_HIGH_SUPPORT 0x00001000 -#define OSC_SB_GENERIC_INITIATOR_SUPPORT 0x00002000 #define OSC_SB_CPC_FLEXIBLE_ADR_SPACE 0x00004000 +#define OSC_SB_GENERIC_INITIATOR_SUPPORT 0x00020000 #define OSC_SB_NATIVE_USB4_SUPPORT 0x00040000 #define OSC_SB_PRM_SUPPORT 0x00200000 #define OSC_SB_FFH_OPR_SUPPORT 0x00400000 -- cgit 1.2.3-korg From 403ad17c06509794fdf6e4d4b3070bd5b56e2a8e Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Sat, 9 Mar 2024 21:13:10 +0100 Subject: ACPI: bus: Indicate support for IRQ ResourceSource thru _OSC The ACPI IRQ mapping code supports parsing of ResourceSource, but this is not reported thru _OSC. Fix this by setting bit 13 ("Interrupt ResourceSource support") when evaluating _OSC. Fixes: d44fa3d46079 ("ACPI: Add support for ResourceSource/IRQ domain mapping") Signed-off-by: Armin Wolf Signed-off-by: Rafael J. Wysocki --- drivers/acpi/bus.c | 1 + include/linux/acpi.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 0c48b603098a85..a87b10eef77df2 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -323,6 +323,7 @@ static void acpi_bus_osc_negotiate_platform_control(void) capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PCLPI_SUPPORT; capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_OVER_16_PSTATES_SUPPORT; capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_GED_SUPPORT; + capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_IRQ_RESOURCE_SOURCE_SUPPORT; if (IS_ENABLED(CONFIG_ACPI_PRMT)) capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PRM_SUPPORT; if (IS_ENABLED(CONFIG_ACPI_FFH)) diff --git a/include/linux/acpi.h b/include/linux/acpi.h index e77783e101c36d..168201e4c78276 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -577,6 +577,7 @@ acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context); #define OSC_SB_OVER_16_PSTATES_SUPPORT 0x00000400 #define OSC_SB_GED_SUPPORT 0x00000800 #define OSC_SB_CPC_DIVERSE_HIGH_SUPPORT 0x00001000 +#define OSC_SB_IRQ_RESOURCE_SOURCE_SUPPORT 0x00002000 #define OSC_SB_CPC_FLEXIBLE_ADR_SPACE 0x00004000 #define OSC_SB_GENERIC_INITIATOR_SUPPORT 0x00020000 #define OSC_SB_NATIVE_USB4_SUPPORT 0x00040000 -- cgit 1.2.3-korg From f186b2dace86f36cc08872b693185eaf71128898 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 21 Mar 2024 20:29:43 +0100 Subject: cpufreq: intel_pstate: Drop redundant locking from intel_pstate_driver_cleanup() Remove the spinlock locking from intel_pstate_driver_cleanup() as it is not necessary because no other code accessing all_cpu_data[] can run in parallel with that function. Had the locking been necessary, though, it would have been incorrect because the lock in question is acquired from a hardirq handler and it cannot be acquired from thread context without disabling interrupts. Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/intel_pstate.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index dbbf299f42197b..bcbeed92458d42 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -3135,10 +3135,8 @@ static void intel_pstate_driver_cleanup(void) if (intel_pstate_driver == &intel_pstate) intel_pstate_clear_update_util_hook(cpu); - spin_lock(&hwp_notify_lock); kfree(all_cpu_data[cpu]); WRITE_ONCE(all_cpu_data[cpu], NULL); - spin_unlock(&hwp_notify_lock); } } cpus_read_unlock(); -- cgit 1.2.3-korg From 12ebba42d2f1eadc0f897ffeb6dbcfaf2449e107 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 21 Mar 2024 20:30:42 +0100 Subject: cpufreq: intel_pstate: Simplify spinlock locking Because intel_pstate_enable/disable_hwp_interrupt() are only called from thread context, they need not save the IRQ flags when using a spinlock as interrupts are guaranteed to be enabled when they run, so make them use spin_lock/unlock_irq(). Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/intel_pstate.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index bcbeed92458d42..c0abf77c56ba0f 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -1682,30 +1682,26 @@ ack_intr: static void intel_pstate_disable_hwp_interrupt(struct cpudata *cpudata) { - unsigned long flags; - if (!boot_cpu_has(X86_FEATURE_HWP_NOTIFY)) return; /* wrmsrl_on_cpu has to be outside spinlock as this can result in IPC */ wrmsrl_on_cpu(cpudata->cpu, MSR_HWP_INTERRUPT, 0x00); - spin_lock_irqsave(&hwp_notify_lock, flags); + spin_lock_irq(&hwp_notify_lock); if (cpumask_test_and_clear_cpu(cpudata->cpu, &hwp_intr_enable_mask)) cancel_delayed_work(&cpudata->hwp_notify_work); - spin_unlock_irqrestore(&hwp_notify_lock, flags); + spin_unlock_irq(&hwp_notify_lock); } static void intel_pstate_enable_hwp_interrupt(struct cpudata *cpudata) { /* Enable HWP notification interrupt for guaranteed performance change */ if (boot_cpu_has(X86_FEATURE_HWP_NOTIFY)) { - unsigned long flags; - - spin_lock_irqsave(&hwp_notify_lock, flags); + spin_lock_irq(&hwp_notify_lock); INIT_DELAYED_WORK(&cpudata->hwp_notify_work, intel_pstate_notify_work); cpumask_set_cpu(cpudata->cpu, &hwp_intr_enable_mask); - spin_unlock_irqrestore(&hwp_notify_lock, flags); + spin_unlock_irq(&hwp_notify_lock); /* wrmsrl_on_cpu has to be outside spinlock as this can result in IPC */ wrmsrl_on_cpu(cpudata->cpu, MSR_HWP_INTERRUPT, 0x01); -- cgit 1.2.3-korg From 432acb219af4edecdd11d360f30b7cc643524db8 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 21 Mar 2024 20:32:02 +0100 Subject: cpufreq: intel_pstate: Wait for canceled delayed work to complete Make intel_pstate_disable_hwp_interrupt() wait for canceled delayed work to complete to avoid leftover work items running when it returns which may be during driver unregistration and may confuse things going forward. Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/intel_pstate.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index c0abf77c56ba0f..b702430dac29f1 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -1682,6 +1682,8 @@ ack_intr: static void intel_pstate_disable_hwp_interrupt(struct cpudata *cpudata) { + bool cancel_work; + if (!boot_cpu_has(X86_FEATURE_HWP_NOTIFY)) return; @@ -1689,9 +1691,11 @@ static void intel_pstate_disable_hwp_interrupt(struct cpudata *cpudata) wrmsrl_on_cpu(cpudata->cpu, MSR_HWP_INTERRUPT, 0x00); spin_lock_irq(&hwp_notify_lock); - if (cpumask_test_and_clear_cpu(cpudata->cpu, &hwp_intr_enable_mask)) - cancel_delayed_work(&cpudata->hwp_notify_work); + cancel_work = cpumask_test_and_clear_cpu(cpudata->cpu, &hwp_intr_enable_mask); spin_unlock_irq(&hwp_notify_lock); + + if (cancel_work) + cancel_delayed_work_sync(&cpudata->hwp_notify_work); } static void intel_pstate_enable_hwp_interrupt(struct cpudata *cpudata) -- cgit 1.2.3-korg From 0f2828e17b6f41b8b345f0031e3fe58529991748 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 28 Mar 2024 19:52:06 +0100 Subject: cpufreq: intel_pstate: Get rid of unnecessary READ_ONCE() annotations Drop two redundant checks involving READ_ONCE() from notify_hwp_interrupt() and make it check hwp_active without READ_ONCE() which is not necessary, because that variable is only set once during the early initialization of the driver. In order to make that clear, annotate hwp_active with __ro_after_init. Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/intel_pstate.c | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index b702430dac29f1..fa707a207c8e5c 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -292,7 +292,7 @@ struct pstate_funcs { static struct pstate_funcs pstate_funcs __read_mostly; -static int hwp_active __read_mostly; +static bool hwp_active __ro_after_init; static int hwp_mode_bdw __read_mostly; static bool per_cpu_limits __read_mostly; static bool hwp_boost __read_mostly; @@ -1636,11 +1636,10 @@ static cpumask_t hwp_intr_enable_mask; void notify_hwp_interrupt(void) { unsigned int this_cpu = smp_processor_id(); - struct cpudata *cpudata; unsigned long flags; u64 value; - if (!READ_ONCE(hwp_active) || !boot_cpu_has(X86_FEATURE_HWP_NOTIFY)) + if (!hwp_active || !boot_cpu_has(X86_FEATURE_HWP_NOTIFY)) return; rdmsrl_safe(MSR_HWP_STATUS, &value); @@ -1652,24 +1651,8 @@ void notify_hwp_interrupt(void) if (!cpumask_test_cpu(this_cpu, &hwp_intr_enable_mask)) goto ack_intr; - /* - * Currently we never free all_cpu_data. And we can't reach here - * without this allocated. But for safety for future changes, added - * check. - */ - if (unlikely(!READ_ONCE(all_cpu_data))) - goto ack_intr; - - /* - * The free is done during cleanup, when cpufreq registry is failed. - * We wouldn't be here if it fails on init or switch status. But for - * future changes, added check. - */ - cpudata = READ_ONCE(all_cpu_data[this_cpu]); - if (unlikely(!cpudata)) - goto ack_intr; - - schedule_delayed_work(&cpudata->hwp_notify_work, msecs_to_jiffies(10)); + schedule_delayed_work(&all_cpu_data[this_cpu]->hwp_notify_work, + msecs_to_jiffies(10)); spin_unlock_irqrestore(&hwp_notify_lock, flags); @@ -3464,7 +3447,7 @@ static int __init intel_pstate_init(void) * deal with it. */ if ((!no_hwp && boot_cpu_has(X86_FEATURE_HWP_EPP)) || hwp_forced) { - WRITE_ONCE(hwp_active, 1); + hwp_active = true; hwp_mode_bdw = id->driver_data; intel_pstate.attr = hwp_cpufreq_attrs; intel_cpufreq.attr = hwp_cpufreq_attrs; -- cgit 1.2.3-korg From e97a98238da68aea4a0be0b2cc40e39527c880b1 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 21 Mar 2024 20:34:06 +0100 Subject: cpufreq: intel_pstate: Use __ro_after_init for three variables There are at least 3 variables in intel_pstate that do not get updated after they have been initialized, so annotate them with __ro_after_init. Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/intel_pstate.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index fa707a207c8e5c..a9e36bbea4faa8 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -293,10 +293,10 @@ struct pstate_funcs { static struct pstate_funcs pstate_funcs __read_mostly; static bool hwp_active __ro_after_init; -static int hwp_mode_bdw __read_mostly; -static bool per_cpu_limits __read_mostly; +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; -static bool hwp_forced __read_mostly; static struct cpufreq_driver *intel_pstate_driver __read_mostly; -- cgit 1.2.3-korg From 032c5565eb80edb6f2faeb31939540c897987119 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 25 Mar 2024 18:01:58 +0100 Subject: cpufreq: intel_pstate: Fold intel_pstate_max_within_limits() into caller Fold intel_pstate_max_within_limits() into its only caller. No functional impact. Signed-off-by: Rafael J. Wysocki Acked-by: Srinivas Pandruvada --- drivers/cpufreq/intel_pstate.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index a9e36bbea4faa8..a401767bdf845a 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -2012,14 +2012,6 @@ static void intel_pstate_set_min_pstate(struct cpudata *cpu) intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate); } -static void intel_pstate_max_within_limits(struct cpudata *cpu) -{ - int pstate = max(cpu->pstate.min_pstate, cpu->max_perf_ratio); - - update_turbo_state(); - intel_pstate_set_pstate(cpu, pstate); -} - static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) { int perf_ctl_max_phys = pstate_funcs.get_max_physical(cpu->cpu); @@ -2594,12 +2586,15 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy) intel_pstate_update_perf_limits(cpu, policy->min, policy->max); if (cpu->policy == CPUFREQ_POLICY_PERFORMANCE) { + int pstate = max(cpu->pstate.min_pstate, cpu->max_perf_ratio); + /* * NOHZ_FULL CPUs need this as the governor callback may not * be invoked on them. */ intel_pstate_clear_update_util_hook(policy->cpu); - intel_pstate_max_within_limits(cpu); + update_turbo_state(); + intel_pstate_set_pstate(cpu, pstate); } else { intel_pstate_set_update_util_hook(policy->cpu); } -- cgit 1.2.3-korg From 0940f1a8011fd69be5082015068e0dc31c800c20 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 25 Mar 2024 18:02:42 +0100 Subject: cpufreq: intel_pstate: Do not update global.turbo_disabled after initialization The global.turbo_disabled is updated quite often, especially in the passive mode in which case it is updated every time the scheduler calls into the driver. However, this is generally not necessary and it adds MSR read overhead to scheduler code paths (and that particular MSR is slow to read). For this reason, make the driver read MSR_IA32_MISC_ENABLE_TURBO_DISABLE just once at the cpufreq driver registration time and remove all of the in-flight updates of global.turbo_disabled. Signed-off-by: Rafael J. Wysocki Acked-by: Srinivas Pandruvada --- drivers/cpufreq/intel_pstate.c | 51 +++++++----------------------------------- 1 file changed, 8 insertions(+), 43 deletions(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index a401767bdf845a..7c00087e840d47 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -173,7 +173,6 @@ struct vid_data { * based on the MSR_IA32_MISC_ENABLE value and whether or * not the maximum reported turbo P-state is different from * the maximum reported non-turbo one. - * @turbo_disabled_mf: The @turbo_disabled value reflected by cpuinfo.max_freq. * @min_perf_pct: Minimum capacity limit in percent of the maximum turbo * P-state capacity. * @max_perf_pct: Maximum capacity limit in percent of the maximum turbo @@ -182,7 +181,6 @@ struct vid_data { struct global_params { bool no_turbo; bool turbo_disabled; - bool turbo_disabled_mf; int max_perf_pct; int min_perf_pct; }; @@ -594,12 +592,13 @@ static void intel_pstate_hybrid_hwp_adjust(struct cpudata *cpu) cpu->pstate.min_pstate = intel_pstate_freq_to_hwp(cpu, freq); } -static inline void update_turbo_state(void) +static bool turbo_is_disabled(void) { u64 misc_en; rdmsrl(MSR_IA32_MISC_ENABLE, misc_en); - global.turbo_disabled = misc_en & MSR_IA32_MISC_ENABLE_TURBO_DISABLE; + + return !!(misc_en & MSR_IA32_MISC_ENABLE_TURBO_DISABLE); } static int min_perf_pct_min(void) @@ -1154,40 +1153,16 @@ static void intel_pstate_update_policies(void) static void __intel_pstate_update_max_freq(struct cpudata *cpudata, struct cpufreq_policy *policy) { - policy->cpuinfo.max_freq = global.turbo_disabled_mf ? + policy->cpuinfo.max_freq = global.turbo_disabled ? cpudata->pstate.max_freq : cpudata->pstate.turbo_freq; refresh_frequency_limits(policy); } -static void intel_pstate_update_max_freq(unsigned int cpu) -{ - struct cpufreq_policy *policy = cpufreq_cpu_acquire(cpu); - - if (!policy) - return; - - __intel_pstate_update_max_freq(all_cpu_data[cpu], policy); - - cpufreq_cpu_release(policy); -} - static void intel_pstate_update_limits(unsigned int cpu) { mutex_lock(&intel_pstate_driver_lock); - update_turbo_state(); - /* - * If turbo has been turned on or off globally, policy limits for - * all CPUs need to be updated to reflect that. - */ - if (global.turbo_disabled_mf != global.turbo_disabled) { - global.turbo_disabled_mf = global.turbo_disabled; - arch_set_max_freq_ratio(global.turbo_disabled); - for_each_possible_cpu(cpu) - intel_pstate_update_max_freq(cpu); - } else { - cpufreq_update_policy(cpu); - } + cpufreq_update_policy(cpu); mutex_unlock(&intel_pstate_driver_lock); } @@ -1287,7 +1262,6 @@ static ssize_t show_no_turbo(struct kobject *kobj, return -EAGAIN; } - update_turbo_state(); if (global.turbo_disabled) ret = sprintf(buf, "%u\n", global.turbo_disabled); else @@ -1317,7 +1291,6 @@ static ssize_t store_no_turbo(struct kobject *a, struct kobj_attribute *b, mutex_lock(&intel_pstate_limits_lock); - update_turbo_state(); if (global.turbo_disabled) { pr_notice_once("Turbo disabled by BIOS or unavailable on processor\n"); mutex_unlock(&intel_pstate_limits_lock); @@ -2281,8 +2254,6 @@ static void intel_pstate_adjust_pstate(struct cpudata *cpu) struct sample *sample; int target_pstate; - update_turbo_state(); - target_pstate = get_target_pstate(cpu); target_pstate = intel_pstate_prepare_request(cpu, target_pstate); trace_cpu_frequency(target_pstate * cpu->pstate.scaling, cpu->cpu); @@ -2593,7 +2564,6 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy) * be invoked on them. */ intel_pstate_clear_update_util_hook(policy->cpu); - update_turbo_state(); intel_pstate_set_pstate(cpu, pstate); } else { intel_pstate_set_update_util_hook(policy->cpu); @@ -2637,7 +2607,6 @@ static void intel_pstate_verify_cpu_policy(struct cpudata *cpu, { int max_freq; - update_turbo_state(); if (hwp_active) { intel_pstate_get_hwp_cap(cpu); max_freq = global.no_turbo || global.turbo_disabled ? @@ -2734,8 +2703,6 @@ static int __intel_pstate_cpu_init(struct cpufreq_policy *policy) /* cpuinfo and default policy values */ policy->cpuinfo.min_freq = cpu->pstate.min_freq; - update_turbo_state(); - global.turbo_disabled_mf = global.turbo_disabled; policy->cpuinfo.max_freq = global.turbo_disabled ? cpu->pstate.max_freq : cpu->pstate.turbo_freq; @@ -2901,8 +2868,6 @@ static int intel_cpufreq_target(struct cpufreq_policy *policy, struct cpufreq_freqs freqs; int target_pstate; - update_turbo_state(); - freqs.old = policy->cur; freqs.new = target_freq; @@ -2924,8 +2889,6 @@ static unsigned int intel_cpufreq_fast_switch(struct cpufreq_policy *policy, struct cpudata *cpu = all_cpu_data[policy->cpu]; int target_pstate; - update_turbo_state(); - target_pstate = intel_pstate_freq_to_hwp(cpu, target_freq); target_pstate = intel_cpufreq_update_pstate(policy, target_pstate, true); @@ -2943,7 +2906,6 @@ static void intel_cpufreq_adjust_perf(unsigned int cpunum, int old_pstate = cpu->pstate.current_pstate; int cap_pstate, min_pstate, max_pstate, target_pstate; - update_turbo_state(); cap_pstate = global.turbo_disabled ? HWP_GUARANTEED_PERF(hwp_cap) : HWP_HIGHEST_PERF(hwp_cap); @@ -3131,6 +3093,9 @@ static int intel_pstate_register_driver(struct cpufreq_driver *driver) memset(&global, 0, sizeof(global)); global.max_perf_pct = 100; + global.turbo_disabled = turbo_is_disabled(); + + arch_set_max_freq_ratio(global.turbo_disabled); intel_pstate_driver = driver; ret = cpufreq_register_driver(intel_pstate_driver); -- cgit 1.2.3-korg From c626a438452079824139f97137f17af47b1a8989 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 25 Mar 2024 18:03:25 +0100 Subject: cpufreq: intel_pstate: Rearrange show_no_turbo() and store_no_turbo() Now that global.turbo_disabled can only change at the cpufreq driver registration time, initialize global.no_turbo at that time too so they are in sync to start with (if the former is set, the latter cannot be updated later anyway). That allows show_no_turbo() to be simlified because it does not need to check global.turbo_disabled and store_no_turbo() can be rearranged to avoid doing anything if the new value of global.no_turbo is equal to the current one and only return an error on attempts to clear global.no_turbo when global.turbo_disabled. While at it, eliminate the redundant ret variable from store_no_turbo(). No intentional functional impact. Signed-off-by: Rafael J. Wysocki Acked-by: Srinivas Pandruvada --- drivers/cpufreq/intel_pstate.c | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 7c00087e840d47..357993ae145432 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -1262,10 +1262,7 @@ static ssize_t show_no_turbo(struct kobject *kobj, return -EAGAIN; } - if (global.turbo_disabled) - ret = sprintf(buf, "%u\n", global.turbo_disabled); - else - ret = sprintf(buf, "%u\n", global.no_turbo); + ret = sprintf(buf, "%u\n", global.no_turbo); mutex_unlock(&intel_pstate_driver_lock); @@ -1276,31 +1273,34 @@ static ssize_t store_no_turbo(struct kobject *a, struct kobj_attribute *b, const char *buf, size_t count) { unsigned int input; - int ret; + bool no_turbo; - ret = sscanf(buf, "%u", &input); - if (ret != 1) + if (sscanf(buf, "%u", &input) != 1) return -EINVAL; mutex_lock(&intel_pstate_driver_lock); if (!intel_pstate_driver) { - mutex_unlock(&intel_pstate_driver_lock); - return -EAGAIN; + count = -EAGAIN; + goto unlock_driver; } - mutex_lock(&intel_pstate_limits_lock); + no_turbo = !!clamp_t(int, input, 0, 1); + + if (no_turbo == global.no_turbo) + goto unlock_driver; if (global.turbo_disabled) { pr_notice_once("Turbo disabled by BIOS or unavailable on processor\n"); - mutex_unlock(&intel_pstate_limits_lock); - mutex_unlock(&intel_pstate_driver_lock); - return -EPERM; + count = -EPERM; + goto unlock_driver; } - global.no_turbo = clamp_t(int, input, 0, 1); + global.no_turbo = no_turbo; + + mutex_lock(&intel_pstate_limits_lock); - if (global.no_turbo) { + if (no_turbo) { struct cpudata *cpu = all_cpu_data[0]; int pct = cpu->pstate.max_pstate * 100 / cpu->pstate.turbo_pstate; @@ -1312,8 +1312,9 @@ static ssize_t store_no_turbo(struct kobject *a, struct kobj_attribute *b, mutex_unlock(&intel_pstate_limits_lock); intel_pstate_update_policies(); - arch_set_max_freq_ratio(global.no_turbo); + arch_set_max_freq_ratio(no_turbo); +unlock_driver: mutex_unlock(&intel_pstate_driver_lock); return count; @@ -3094,6 +3095,7 @@ static int intel_pstate_register_driver(struct cpufreq_driver *driver) memset(&global, 0, sizeof(global)); global.max_perf_pct = 100; global.turbo_disabled = turbo_is_disabled(); + global.no_turbo = global.turbo_disabled; arch_set_max_freq_ratio(global.turbo_disabled); -- cgit 1.2.3-korg From 9558fae8ce97b3b320b387dd7c88309df2c36d4d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 25 Mar 2024 18:04:24 +0100 Subject: cpufreq: intel_pstate: Read global.no_turbo under READ_ONCE() Because global.no_turbo is generally not read under intel_pstate_driver_lock make store_no_turbo() use WRITE_ONCE() for updating it (this is the only place at which it is updated except for the initialization) and make the majority of places reading it use READ_ONCE(). Also remove redundant global.turbo_disabled checks from places that depend on the 'true' value of global.no_turbo because it can only be 'true' if global.turbo_disabled is also 'true'. Signed-off-by: Rafael J. Wysocki Acked-by: Srinivas Pandruvada --- drivers/cpufreq/intel_pstate.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 357993ae145432..3a707e34acd8a9 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -1296,7 +1296,7 @@ static ssize_t store_no_turbo(struct kobject *a, struct kobj_attribute *b, goto unlock_driver; } - global.no_turbo = no_turbo; + WRITE_ONCE(global.no_turbo, no_turbo); mutex_lock(&intel_pstate_limits_lock); @@ -1748,7 +1748,7 @@ static u64 atom_get_val(struct cpudata *cpudata, int pstate) u32 vid; val = (u64)pstate << 8; - if (global.no_turbo && !global.turbo_disabled) + if (READ_ONCE(global.no_turbo) && !global.turbo_disabled) val |= (u64)1 << 32; vid_fp = cpudata->vid.min + mul_fp( @@ -1913,7 +1913,7 @@ static u64 core_get_val(struct cpudata *cpudata, int pstate) u64 val; val = (u64)pstate << 8; - if (global.no_turbo && !global.turbo_disabled) + if (READ_ONCE(global.no_turbo) && !global.turbo_disabled) val |= (u64)1 << 32; return val; @@ -2211,7 +2211,7 @@ static inline int32_t get_target_pstate(struct cpudata *cpu) sample->busy_scaled = busy_frac * 100; - target = global.no_turbo || global.turbo_disabled ? + target = READ_ONCE(global.no_turbo) ? cpu->pstate.max_pstate : cpu->pstate.turbo_pstate; target += target >> 2; target = mul_fp(target, busy_frac); @@ -2473,7 +2473,7 @@ static void intel_pstate_clear_update_util_hook(unsigned int cpu) static int intel_pstate_get_max_freq(struct cpudata *cpu) { - return global.turbo_disabled || global.no_turbo ? + return READ_ONCE(global.no_turbo) ? cpu->pstate.max_freq : cpu->pstate.turbo_freq; } @@ -2610,7 +2610,7 @@ static void intel_pstate_verify_cpu_policy(struct cpudata *cpu, if (hwp_active) { intel_pstate_get_hwp_cap(cpu); - max_freq = global.no_turbo || global.turbo_disabled ? + max_freq = READ_ONCE(global.no_turbo) ? cpu->pstate.max_freq : cpu->pstate.turbo_freq; } else { max_freq = intel_pstate_get_max_freq(cpu); -- cgit 1.2.3-korg From f32587dcbe5f40e160d8de262add6abab79356a7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 25 Mar 2024 18:05:06 +0100 Subject: cpufreq: intel_pstate: Replace three global.turbo_disabled checks Replace the global.turbo_disabled in __intel_pstate_update_max_freq() with a global.no_turbo one to make store_no_turbo() actually update the maximum CPU frequency on the trubo preference changes, which needs to be consistent with arch_set_max_freq_ratio() called from there. For more consistency, replace the global.turbo_disabled checks in __intel_pstate_cpu_init() and intel_cpufreq_adjust_perf() with global.no_turbo checks either. Signed-off-by: Rafael J. Wysocki Acked-by: Srinivas Pandruvada --- drivers/cpufreq/intel_pstate.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 3a707e34acd8a9..f1d6de05bcab4e 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -1153,7 +1153,7 @@ static void intel_pstate_update_policies(void) static void __intel_pstate_update_max_freq(struct cpudata *cpudata, struct cpufreq_policy *policy) { - policy->cpuinfo.max_freq = global.turbo_disabled ? + policy->cpuinfo.max_freq = READ_ONCE(global.no_turbo) ? cpudata->pstate.max_freq : cpudata->pstate.turbo_freq; refresh_frequency_limits(policy); } @@ -2704,7 +2704,7 @@ static int __intel_pstate_cpu_init(struct cpufreq_policy *policy) /* cpuinfo and default policy values */ policy->cpuinfo.min_freq = cpu->pstate.min_freq; - policy->cpuinfo.max_freq = global.turbo_disabled ? + policy->cpuinfo.max_freq = READ_ONCE(global.no_turbo) ? cpu->pstate.max_freq : cpu->pstate.turbo_freq; policy->min = policy->cpuinfo.min_freq; @@ -2907,8 +2907,9 @@ static void intel_cpufreq_adjust_perf(unsigned int cpunum, int old_pstate = cpu->pstate.current_pstate; int cap_pstate, min_pstate, max_pstate, target_pstate; - cap_pstate = global.turbo_disabled ? HWP_GUARANTEED_PERF(hwp_cap) : - HWP_HIGHEST_PERF(hwp_cap); + cap_pstate = READ_ONCE(global.no_turbo) ? + HWP_GUARANTEED_PERF(hwp_cap) : + HWP_HIGHEST_PERF(hwp_cap); /* Optimization: Avoid unnecessary divisions. */ -- cgit 1.2.3-korg From e8217b4bece379e66d43ab5070431712f07bf625 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 28 Mar 2024 19:52:45 +0100 Subject: cpufreq: intel_pstate: Update the maximum CPU frequency consistently There are 3 places at which the maximum CPU frequency may change, store_no_turbo(), intel_pstate_update_limits() (when called by the cpufreq core) and intel_pstate_notify_work() (when handling a HWP change notification). Currently, cpuinfo.max_freq is only updated by store_no_turbo() and intel_pstate_notify_work(), although it principle it may be necessary to update it in intel_pstate_update_limits() either. Make all of them mutually consistent. Signed-off-by: Rafael J. Wysocki Acked-by: Srinivas Pandruvada --- drivers/cpufreq/intel_pstate.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index f1d6de05bcab4e..02f9e494e86e0f 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -1153,18 +1153,32 @@ static void intel_pstate_update_policies(void) static void __intel_pstate_update_max_freq(struct cpudata *cpudata, struct cpufreq_policy *policy) { + intel_pstate_get_hwp_cap(cpudata); + policy->cpuinfo.max_freq = READ_ONCE(global.no_turbo) ? cpudata->pstate.max_freq : cpudata->pstate.turbo_freq; + refresh_frequency_limits(policy); } static void intel_pstate_update_limits(unsigned int cpu) { - mutex_lock(&intel_pstate_driver_lock); + struct cpufreq_policy *policy = cpufreq_cpu_acquire(cpu); - cpufreq_update_policy(cpu); + if (!policy) + return; - mutex_unlock(&intel_pstate_driver_lock); + __intel_pstate_update_max_freq(all_cpu_data[cpu], policy); + + cpufreq_cpu_release(policy); +} + +static void intel_pstate_update_limits_for_all(void) +{ + int cpu; + + for_each_possible_cpu(cpu) + intel_pstate_update_limits(cpu); } /************************** sysfs begin ************************/ @@ -1311,7 +1325,7 @@ static ssize_t store_no_turbo(struct kobject *a, struct kobj_attribute *b, mutex_unlock(&intel_pstate_limits_lock); - intel_pstate_update_policies(); + intel_pstate_update_limits_for_all(); arch_set_max_freq_ratio(no_turbo); unlock_driver: @@ -1595,7 +1609,6 @@ static void intel_pstate_notify_work(struct work_struct *work) struct cpufreq_policy *policy = cpufreq_cpu_acquire(cpudata->cpu); if (policy) { - intel_pstate_get_hwp_cap(cpudata); __intel_pstate_update_max_freq(cpudata, policy); cpufreq_cpu_release(policy); -- cgit 1.2.3-korg From 8c556541a53848d6611ff8b5f9bf52e96c56f48e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 3 Apr 2024 10:06:45 +0200 Subject: cpufreq: intel_pstate: hide unused intel_pstate_cpu_oob_ids[] The reference to this variable is hidden in an #ifdef: drivers/cpufreq/intel_pstate.c:2440:32: error: 'intel_pstate_cpu_oob_ids' defined but not used [-Werror=unused-const-variable=] Use the same check around the definition. Signed-off-by: Arnd Bergmann Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/intel_pstate.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 02f9e494e86e0f..5f19d3824a4b28 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -2397,6 +2397,7 @@ static const struct x86_cpu_id intel_pstate_cpu_ids[] = { }; MODULE_DEVICE_TABLE(x86cpu, intel_pstate_cpu_ids); +#ifdef CONFIG_ACPI static const struct x86_cpu_id intel_pstate_cpu_oob_ids[] __initconst = { X86_MATCH(BROADWELL_D, core_funcs), X86_MATCH(BROADWELL_X, core_funcs), @@ -2405,6 +2406,7 @@ static const struct x86_cpu_id intel_pstate_cpu_oob_ids[] __initconst = { X86_MATCH(SAPPHIRERAPIDS_X, core_funcs), {} }; +#endif static const struct x86_cpu_id intel_pstate_cpu_ee_disable_ids[] = { X86_MATCH(KABYLAKE, core_funcs), -- cgit 1.2.3-korg From afde996a33ee4dbe3692e1eff28b56c820331428 Mon Sep 17 00:00:00 2001 From: Dhruva Gole Date: Mon, 18 Mar 2024 20:46:32 +0530 Subject: PM: wakeup: make device_wakeup_disable() return void The device_wakeup_disable() call only returns an error if no dev exists, but there's not much a user can do at that point. Rather, make this function return void. Signed-off-by: Dhruva Gole Signed-off-by: Rafael J. Wysocki --- drivers/base/power/wakeup.c | 11 +++++++---- drivers/mmc/host/sdhci-pci-core.c | 2 +- include/linux/pm_wakeup.h | 5 ++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index a917219feea62d..752b417e81290e 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -451,16 +451,15 @@ static struct wakeup_source *device_wakeup_detach(struct device *dev) * Detach the @dev's wakeup source object from it, unregister this wakeup source * object and destroy it. */ -int device_wakeup_disable(struct device *dev) +void device_wakeup_disable(struct device *dev) { struct wakeup_source *ws; if (!dev || !dev->power.can_wakeup) - return -EINVAL; + return; ws = device_wakeup_detach(dev); wakeup_source_unregister(ws); - return 0; } EXPORT_SYMBOL_GPL(device_wakeup_disable); @@ -502,7 +501,11 @@ EXPORT_SYMBOL_GPL(device_set_wakeup_capable); */ int device_set_wakeup_enable(struct device *dev, bool enable) { - return enable ? device_wakeup_enable(dev) : device_wakeup_disable(dev); + if (enable) + return device_wakeup_enable(dev); + + device_wakeup_disable(dev); + return 0; } EXPORT_SYMBOL_GPL(device_set_wakeup_enable); diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 025b31aa712caa..ef89ec382bfef9 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -63,7 +63,7 @@ static int sdhci_pci_init_wakeup(struct sdhci_pci_chip *chip) if ((pm_flags & MMC_PM_KEEP_POWER) && (pm_flags & MMC_PM_WAKE_SDIO_IRQ)) return device_wakeup_enable(&chip->pdev->dev); else if (!cap_cd_wake) - return device_wakeup_disable(&chip->pdev->dev); + device_wakeup_disable(&chip->pdev->dev); return 0; } diff --git a/include/linux/pm_wakeup.h b/include/linux/pm_wakeup.h index 6eb9adaef52beb..428803eed79868 100644 --- a/include/linux/pm_wakeup.h +++ b/include/linux/pm_wakeup.h @@ -107,7 +107,7 @@ extern void wakeup_sources_read_unlock(int idx); extern struct wakeup_source *wakeup_sources_walk_start(void); extern struct wakeup_source *wakeup_sources_walk_next(struct wakeup_source *ws); extern int device_wakeup_enable(struct device *dev); -extern int device_wakeup_disable(struct device *dev); +extern void device_wakeup_disable(struct device *dev); extern void device_set_wakeup_capable(struct device *dev, bool capable); extern int device_set_wakeup_enable(struct device *dev, bool enable); extern void __pm_stay_awake(struct wakeup_source *ws); @@ -154,10 +154,9 @@ static inline int device_wakeup_enable(struct device *dev) return 0; } -static inline int device_wakeup_disable(struct device *dev) +static inline void device_wakeup_disable(struct device *dev) { dev->power.should_wakeup = false; - return 0; } static inline int device_set_wakeup_enable(struct device *dev, bool enable) -- cgit 1.2.3-korg From 3642c7ed52312ac2b95c9aba45c40e50bd8798ad Mon Sep 17 00:00:00 2001 From: Dhruva Gole Date: Mon, 18 Mar 2024 20:46:33 +0530 Subject: PM: wakeup: Remove unnecessary else from device_init_wakeup() Checkpatch warns that else is generally not necessary after a return condition which exists in the if part of this function. Hence, just to abide by what checkpatch recommends, follow it's guidelines. Signed-off-by: Dhruva Gole Signed-off-by: Rafael J. Wysocki --- include/linux/pm_wakeup.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/include/linux/pm_wakeup.h b/include/linux/pm_wakeup.h index 428803eed79868..76cd1f9f136530 100644 --- a/include/linux/pm_wakeup.h +++ b/include/linux/pm_wakeup.h @@ -234,11 +234,10 @@ static inline int device_init_wakeup(struct device *dev, bool enable) if (enable) { device_set_wakeup_capable(dev, true); return device_wakeup_enable(dev); - } else { - device_wakeup_disable(dev); - device_set_wakeup_capable(dev, false); - return 0; } + device_wakeup_disable(dev); + device_set_wakeup_capable(dev, false); + return 0; } #endif /* _LINUX_PM_WAKEUP_H */ -- cgit 1.2.3-korg From 32666d9cb3ddcf0041b6377cbab68f4c2d7c4171 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 25 Mar 2024 14:32:57 +0200 Subject: ACPI: bus: Make container_of() no-op where it makes sense Move list head node to be the first member in a few data structures in order to make container_of() no-op at compile time. On x86_64 with a custom (default + a few dozens of drivers enabled) configuration: add/remove: 0/0 grow/shrink: 5/12 up/down: 21/-124 (-103) ... Total: Before=39924675, After=39924572, chg -0.00% Signed-off-by: Andy Shevchenko Signed-off-by: Rafael J. Wysocki --- include/acpi/acpi_bus.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 5de954e2b18aaa..e4d8d48bba1fae 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -124,8 +124,8 @@ static inline struct acpi_hotplug_profile *to_acpi_hotplug_profile( } struct acpi_scan_handler { - const struct acpi_device_id *ids; struct list_head list_node; + const struct acpi_device_id *ids; bool (*match)(const char *idstr, const struct acpi_device_id **matchid); int (*attach)(struct acpi_device *dev, const struct acpi_device_id *id); void (*detach)(struct acpi_device *dev); @@ -269,6 +269,7 @@ struct acpi_device_power_flags { }; struct acpi_device_power_state { + struct list_head resources; /* Power resources referenced */ struct { u8 valid:1; u8 explicit_set:1; /* _PSx present? */ @@ -276,7 +277,6 @@ struct acpi_device_power_state { } flags; int power; /* % Power (compared to D0) */ int latency; /* Dx->D0 time (microseconds) */ - struct list_head resources; /* Power resources referenced */ }; struct acpi_device_power { @@ -342,16 +342,16 @@ struct acpi_device_wakeup { }; struct acpi_device_physical_node { - unsigned int node_id; struct list_head node; struct device *dev; + unsigned int node_id; bool put_online:1; }; struct acpi_device_properties { + struct list_head list; const guid_t *guid; union acpi_object *properties; - struct list_head list; void **bufs; }; @@ -488,12 +488,12 @@ struct acpi_device { /* Non-device subnode */ struct acpi_data_node { + struct list_head sibling; const char *name; acpi_handle handle; struct fwnode_handle fwnode; struct fwnode_handle *parent; struct acpi_device_data data; - struct list_head sibling; struct kobject kobj; struct completion kobj_done; }; -- cgit 1.2.3-korg From 336153053293a090400cfdd4ae724b6b8a4beb98 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 25 Mar 2024 14:32:58 +0200 Subject: ACPI: bus: Don't use "proxy" headers Update header inclusions to follow the IWYU (Include What You Use) principle. Signed-off-by: Andy Shevchenko Signed-off-by: Rafael J. Wysocki --- include/acpi/acpi_bus.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index e4d8d48bba1fae..a6ced88a08f35d 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -9,8 +9,13 @@ #ifndef __ACPI_BUS_H__ #define __ACPI_BUS_H__ +#include +#include #include +#include +#include #include +#include struct acpi_handle_list { u32 count; -- cgit 1.2.3-korg From 2649a0f29a39f455ff39eb2296269e20df3bba0d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 25 Mar 2024 14:33:00 +0200 Subject: ACPI: scan: Use list_first_entry_or_null() in acpi_device_hid() To replace list_empty() + list_first_entry() pair to simplify code. Signed-off-by: Andy Shevchenko Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 7c157bf926956b..dc625653b19888 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1298,10 +1298,10 @@ const char *acpi_device_hid(struct acpi_device *device) { struct acpi_hardware_id *hid; - if (list_empty(&device->pnp.ids)) + hid = list_first_entry_or_null(&device->pnp.ids, struct acpi_hardware_id, list); + if (!hid) return dummy_hid; - hid = list_first_entry(&device->pnp.ids, struct acpi_hardware_id, list); return hid->id; } EXPORT_SYMBOL(acpi_device_hid); -- cgit 1.2.3-korg From e80d4122df9c707ebc9cfe20ab60be302fc9b833 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 25 Mar 2024 14:33:01 +0200 Subject: ACPI: scan: Move misleading comment to acpi_dma_configure_id() The acpi_iommu_configure_id() implementation has a misleading comment since after it the flow does something different to what it states. Move the commit to the caller and with that unshadow the error code inside acpi_iommu_configure_id(). Signed-off-by: Andy Shevchenko Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index dc625653b19888..e64e1ec626b361 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1625,12 +1625,11 @@ static int acpi_iommu_configure_id(struct device *dev, const u32 *id_in) if (!err && dev->bus) err = iommu_probe_device(dev); - /* Ignore all other errors apart from EPROBE_DEFER */ - if (err == -EPROBE_DEFER) { + if (err == -EPROBE_DEFER) return err; - } else if (err) { + if (err) { dev_dbg(dev, "Adding to IOMMU failed: %d\n", err); - return -ENODEV; + return err; } if (!acpi_iommu_fwspec_ops(dev)) return -ENODEV; @@ -1671,13 +1670,14 @@ int acpi_dma_configure_id(struct device *dev, enum dev_dma_attr attr, acpi_arch_dma_setup(dev); + /* Ignore all other errors apart from EPROBE_DEFER */ ret = acpi_iommu_configure_id(dev, input_id); if (ret == -EPROBE_DEFER) return -EPROBE_DEFER; /* * Historically this routine doesn't fail driver probing due to errors - * in acpi_iommu_configure_id() + * in acpi_iommu_configure_id(). */ arch_setup_dma_ops(dev, 0, U64_MAX, attr == DEV_DMA_COHERENT); -- cgit 1.2.3-korg From 602401e32847f90c513df4a34bcac4dd6b02dd8d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 25 Mar 2024 14:33:02 +0200 Subject: ACPI: scan: Use standard error checking pattern Check for an error and return it as it's the usual way to handle this. Signed-off-by: Andy Shevchenko Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index e64e1ec626b361..b9a33364e553bd 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1581,12 +1581,13 @@ int acpi_iommu_fwspec_init(struct device *dev, u32 id, struct fwnode_handle *fwnode, const struct iommu_ops *ops) { - int ret = iommu_fwspec_init(dev, fwnode, ops); + int ret; - if (!ret) - ret = iommu_fwspec_add_ids(dev, &id, 1); + ret = iommu_fwspec_init(dev, fwnode, ops); + if (ret) + return ret; - return ret; + return iommu_fwspec_add_ids(dev, &id, 1); } static inline const struct iommu_ops *acpi_iommu_fwspec_ops(struct device *dev) -- cgit 1.2.3-korg From f5c519fc3628915c2eccd65f86d7ee2ce5cc8645 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 25 Mar 2024 14:33:03 +0200 Subject: ACPI: scan: Introduce typedef:s for struct acpi_hotplug_context members Follow the struct acpi_device_ops approach and introduce typedef:s for the members. It makes code less verbose and more particular on what parameters we take or types we use. Signed-off-by: Andy Shevchenko Signed-off-by: Rafael J. Wysocki --- drivers/acpi/dock.c | 48 +++++++++++++++++------------------------------- drivers/acpi/scan.c | 5 ++--- include/acpi/acpi_bus.h | 13 ++++++++----- 3 files changed, 27 insertions(+), 39 deletions(-) diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index a7c00ef78086cf..34affbda295eb7 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -88,43 +88,29 @@ static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event, enum dock_callback_type cb_type) { struct acpi_device *adev = dd->adev; + acpi_hp_fixup fixup = NULL; + acpi_hp_uevent uevent = NULL; + acpi_hp_notify notify = NULL; acpi_lock_hp_context(); - if (!adev->hp) - goto out; - - if (cb_type == DOCK_CALL_FIXUP) { - void (*fixup)(struct acpi_device *); - - fixup = adev->hp->fixup; - if (fixup) { - acpi_unlock_hp_context(); - fixup(adev); - return; - } - } else if (cb_type == DOCK_CALL_UEVENT) { - void (*uevent)(struct acpi_device *, u32); - - uevent = adev->hp->uevent; - if (uevent) { - acpi_unlock_hp_context(); - uevent(adev, event); - return; - } - } else { - int (*notify)(struct acpi_device *, u32); - - notify = adev->hp->notify; - if (notify) { - acpi_unlock_hp_context(); - notify(adev, event); - return; - } + if (adev->hp) { + if (cb_type == DOCK_CALL_FIXUP) + fixup = adev->hp->fixup; + else if (cb_type == DOCK_CALL_UEVENT) + uevent = adev->hp->uevent; + else + notify = adev->hp->notify; } - out: acpi_unlock_hp_context(); + + if (fixup) + fixup(adev); + else if (uevent) + uevent(adev, event); + else if (notify) + notify(adev, event); } static struct dock_station *find_dock_station(acpi_handle handle) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index b9a33364e553bd..f06d92b1183fae 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -73,8 +73,7 @@ void acpi_unlock_hp_context(void) void acpi_initialize_hp_context(struct acpi_device *adev, struct acpi_hotplug_context *hp, - int (*notify)(struct acpi_device *, u32), - void (*uevent)(struct acpi_device *, u32)) + acpi_hp_notify notify, acpi_hp_uevent uevent) { acpi_lock_hp_context(); hp->notify = notify; @@ -428,7 +427,7 @@ void acpi_device_hotplug(struct acpi_device *adev, u32 src) } else if (adev->flags.hotplug_notify) { error = acpi_generic_hotplug_event(adev, src); } else { - int (*notify)(struct acpi_device *, u32); + acpi_hp_notify notify; acpi_lock_hp_context(); notify = adev->hp ? adev->hp->notify : NULL; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index a6ced88a08f35d..6f8cc4cc61be14 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -144,11 +144,15 @@ struct acpi_scan_handler { * -------------------- */ +typedef int (*acpi_hp_notify) (struct acpi_device *, u32); +typedef void (*acpi_hp_uevent) (struct acpi_device *, u32); +typedef void (*acpi_hp_fixup) (struct acpi_device *); + struct acpi_hotplug_context { struct acpi_device *self; - int (*notify)(struct acpi_device *, u32); - void (*uevent)(struct acpi_device *, u32); - void (*fixup)(struct acpi_device *); + acpi_hp_notify notify; + acpi_hp_uevent uevent; + acpi_hp_fixup fixup; }; /* @@ -583,8 +587,7 @@ static inline void acpi_set_hp_context(struct acpi_device *adev, void acpi_initialize_hp_context(struct acpi_device *adev, struct acpi_hotplug_context *hp, - int (*notify)(struct acpi_device *, u32), - void (*uevent)(struct acpi_device *, u32)); + acpi_hp_notify notify, acpi_hp_uevent uevent); /* acpi_device.dev.bus == &acpi_bus_type */ extern const struct bus_type acpi_bus_type; -- cgit 1.2.3-korg From f1164d333cc3c063cfdf7d03ddbca4f93a5a5c41 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 3 Apr 2024 20:11:10 +0200 Subject: thermal: gov_step_wise: Simplify get_target_state() The step-wise governor's get_target_state() function contains redundant braces, redundant parens and a redundant next_target local variable, so get rid of all that stuff. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba --- drivers/thermal/gov_step_wise.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/drivers/thermal/gov_step_wise.c b/drivers/thermal/gov_step_wise.c index 5436aa58d41ec7..aad98e06d66b9d 100644 --- a/drivers/thermal/gov_step_wise.c +++ b/drivers/thermal/gov_step_wise.c @@ -32,7 +32,6 @@ static unsigned long get_target_state(struct thermal_instance *instance, { struct thermal_cooling_device *cdev = instance->cdev; unsigned long cur_state; - unsigned long next_target; /* * We keep this instance the way it is by default. @@ -40,32 +39,26 @@ static unsigned long get_target_state(struct thermal_instance *instance, * cdev in use to determine the next_target. */ cdev->ops->get_cur_state(cdev, &cur_state); - next_target = instance->target; dev_dbg(&cdev->device, "cur_state=%ld\n", cur_state); if (!instance->initialized) { - if (throttle) { - next_target = clamp((cur_state + 1), instance->lower, instance->upper); - } else { - next_target = THERMAL_NO_TARGET; - } + if (throttle) + return clamp(cur_state + 1, instance->lower, instance->upper); - return next_target; + return THERMAL_NO_TARGET; } if (throttle) { if (trend == THERMAL_TREND_RAISING) - next_target = clamp((cur_state + 1), instance->lower, instance->upper); - } else { - if (trend == THERMAL_TREND_DROPPING) { - if (cur_state <= instance->lower) - next_target = THERMAL_NO_TARGET; - else - next_target = clamp((cur_state - 1), instance->lower, instance->upper); - } + return clamp(cur_state + 1, instance->lower, instance->upper); + } else if (trend == THERMAL_TREND_DROPPING) { + if (cur_state <= instance->lower) + return THERMAL_NO_TARGET; + + return clamp(cur_state - 1, instance->lower, instance->upper); } - return next_target; + return instance->target; } static void thermal_zone_trip_update(struct thermal_zone_device *tz, -- cgit 1.2.3-korg From 053b852c46626250b5f7da43ba8574da756db022 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 3 Apr 2024 20:12:02 +0200 Subject: thermal: gov_step_wise: Simplify checks related to passive trips Make it more clear from the code flow that the passive polling status updates only take place for passive trip points. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba --- drivers/thermal/gov_step_wise.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/thermal/gov_step_wise.c b/drivers/thermal/gov_step_wise.c index aad98e06d66b9d..ee2fb4e63d1478 100644 --- a/drivers/thermal/gov_step_wise.c +++ b/drivers/thermal/gov_step_wise.c @@ -92,15 +92,13 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, if (instance->initialized && old_target == instance->target) continue; - if (old_target == THERMAL_NO_TARGET && - instance->target != THERMAL_NO_TARGET) { - /* Activate a passive thermal instance */ - if (trip->type == THERMAL_TRIP_PASSIVE) + if (trip->type == THERMAL_TRIP_PASSIVE) { + /* If needed, update the status of passive polling. */ + if (old_target == THERMAL_NO_TARGET && + instance->target != THERMAL_NO_TARGET) tz->passive++; - } else if (old_target != THERMAL_NO_TARGET && - instance->target == THERMAL_NO_TARGET) { - /* Deactivate a passive thermal instance */ - if (trip->type == THERMAL_TRIP_PASSIVE) + else if (old_target != THERMAL_NO_TARGET && + instance->target == THERMAL_NO_TARGET) tz->passive--; } -- cgit 1.2.3-korg From daeeb032f42d066a49e07b7f6effc9f51b7a5479 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 2 Apr 2024 20:56:43 +0200 Subject: thermal: core: Move threshold out of struct thermal_trip The threshold field in struct thermal_trip is only used internally by the thermal core and it is better to prevent drivers from misusing it. It also takes some space unnecessarily in the trip tables passed by drivers to the core during thermal zone registration. For this reason, introduce struct thermal_trip_desc as a wrapper around struct thermal_trip, move the threshold field directly into it and make the thermal core store struct thermal_trip_desc objects in the internal thermal zone trip tables. Adjust all of the code using trip tables in the thermal core accordingly. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba --- drivers/thermal/gov_fair_share.c | 7 ++++-- drivers/thermal/gov_power_allocator.c | 6 +++-- drivers/thermal/thermal_core.c | 46 ++++++++++++++++++++--------------- drivers/thermal/thermal_core.h | 7 ++++-- drivers/thermal/thermal_debugfs.c | 6 +++-- drivers/thermal/thermal_helpers.c | 8 +++--- drivers/thermal/thermal_netlink.c | 6 +++-- drivers/thermal/thermal_sysfs.c | 20 +++++++-------- drivers/thermal/thermal_trip.c | 15 ++++++------ include/linux/thermal.h | 9 ++++--- 10 files changed, 78 insertions(+), 52 deletions(-) diff --git a/drivers/thermal/gov_fair_share.c b/drivers/thermal/gov_fair_share.c index 4da25a0009d73d..6ef8cfde7749a9 100644 --- a/drivers/thermal/gov_fair_share.c +++ b/drivers/thermal/gov_fair_share.c @@ -17,10 +17,13 @@ static int get_trip_level(struct thermal_zone_device *tz) { - const struct thermal_trip *trip, *level_trip = NULL; + const struct thermal_trip *level_trip = NULL; + const struct thermal_trip_desc *td; int trip_level = -1; - for_each_trip(tz, trip) { + for_each_trip_desc(tz, td) { + const struct thermal_trip *trip = &td->trip; + if (trip->temperature >= tz->temperature) continue; diff --git a/drivers/thermal/gov_power_allocator.c b/drivers/thermal/gov_power_allocator.c index e25e48d76aa79c..ac1d02193a1bf1 100644 --- a/drivers/thermal/gov_power_allocator.c +++ b/drivers/thermal/gov_power_allocator.c @@ -496,9 +496,11 @@ static void get_governor_trips(struct thermal_zone_device *tz, const struct thermal_trip *first_passive = NULL; const struct thermal_trip *last_passive = NULL; const struct thermal_trip *last_active = NULL; - const struct thermal_trip *trip; + const struct thermal_trip_desc *td; + + for_each_trip_desc(tz, td) { + const struct thermal_trip *trip = &td->trip; - for_each_trip(tz, trip) { switch (trip->type) { case THERMAL_TRIP_PASSIVE: if (!first_passive) { diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 34a31bc7202302..fc6ff0a4faa1d1 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -361,17 +361,19 @@ static void handle_critical_trips(struct thermal_zone_device *tz, } static void handle_thermal_trip(struct thermal_zone_device *tz, - struct thermal_trip *trip) + struct thermal_trip_desc *td) { + const struct thermal_trip *trip = &td->trip; + if (trip->temperature == THERMAL_TEMP_INVALID) return; if (tz->last_temperature == THERMAL_TEMP_INVALID) { /* Initialization. */ - trip->threshold = trip->temperature; - if (tz->temperature >= trip->threshold) - trip->threshold -= trip->hysteresis; - } else if (tz->last_temperature < trip->threshold) { + td->threshold = trip->temperature; + if (tz->temperature >= td->threshold) + td->threshold -= trip->hysteresis; + } else if (tz->last_temperature < td->threshold) { /* * The trip threshold is equal to the trip temperature, unless * the latter has changed in the meantime. In either case, @@ -382,9 +384,9 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, if (tz->temperature >= trip->temperature) { thermal_notify_tz_trip_up(tz, trip); thermal_debug_tz_trip_up(tz, trip); - trip->threshold = trip->temperature - trip->hysteresis; + td->threshold = trip->temperature - trip->hysteresis; } else { - trip->threshold = trip->temperature; + td->threshold = trip->temperature; } } else { /* @@ -400,9 +402,9 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, if (tz->temperature < trip->temperature - trip->hysteresis) { thermal_notify_tz_trip_down(tz, trip); thermal_debug_tz_trip_down(tz, trip); - trip->threshold = trip->temperature; + td->threshold = trip->temperature; } else { - trip->threshold = trip->temperature - trip->hysteresis; + td->threshold = trip->temperature - trip->hysteresis; } } @@ -458,7 +460,7 @@ static void thermal_zone_device_init(struct thermal_zone_device *tz) void __thermal_zone_device_update(struct thermal_zone_device *tz, enum thermal_notify_event event) { - struct thermal_trip *trip; + struct thermal_trip_desc *td; if (tz->suspended) return; @@ -472,8 +474,8 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, tz->notify_event = event; - for_each_trip(tz, trip) - handle_thermal_trip(tz, trip); + for_each_trip_desc(tz, td) + handle_thermal_trip(tz, td); monitor_thermal_zone(tz); } @@ -766,7 +768,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, if (trip_index < 0 || trip_index >= tz->num_trips) return -EINVAL; - return thermal_bind_cdev_to_trip(tz, &tz->trips[trip_index], cdev, + return thermal_bind_cdev_to_trip(tz, &tz->trips[trip_index].trip, cdev, upper, lower, weight); } EXPORT_SYMBOL_GPL(thermal_zone_bind_cooling_device); @@ -825,7 +827,7 @@ int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz, if (trip_index < 0 || trip_index >= tz->num_trips) return -EINVAL; - return thermal_unbind_cdev_from_trip(tz, &tz->trips[trip_index], cdev); + return thermal_unbind_cdev_from_trip(tz, &tz->trips[trip_index].trip, cdev); } EXPORT_SYMBOL_GPL(thermal_zone_unbind_cooling_device); @@ -1221,16 +1223,19 @@ static void thermal_set_delay_jiffies(unsigned long *delay_jiffies, int delay_ms int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp) { - int i, ret = -EINVAL; + const struct thermal_trip_desc *td; + int ret = -EINVAL; if (tz->ops.get_crit_temp) return tz->ops.get_crit_temp(tz, temp); mutex_lock(&tz->lock); - for (i = 0; i < tz->num_trips; i++) { - if (tz->trips[i].type == THERMAL_TRIP_CRITICAL) { - *temp = tz->trips[i].temperature; + for_each_trip_desc(tz, td) { + const struct thermal_trip *trip = &td->trip; + + if (trip->type == THERMAL_TRIP_CRITICAL) { + *temp = trip->temperature; ret = 0; break; } @@ -1274,7 +1279,9 @@ thermal_zone_device_register_with_trips(const char *type, const struct thermal_zone_params *tzp, int passive_delay, int polling_delay) { + const struct thermal_trip *trip = trips; struct thermal_zone_device *tz; + struct thermal_trip_desc *td; int id; int result; struct thermal_governor *governor; @@ -1339,7 +1346,8 @@ thermal_zone_device_register_with_trips(const char *type, tz->device.class = thermal_class; tz->devdata = devdata; tz->num_trips = num_trips; - memcpy(tz->trips, trips, num_trips * sizeof(*trips)); + for_each_trip_desc(tz, td) + td->trip = *trip++; thermal_set_delay_jiffies(&tz->passive_delay_jiffies, passive_delay); thermal_set_delay_jiffies(&tz->polling_delay_jiffies, polling_delay); diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 0d8a42bb7ce833..4ad9c671bc1304 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -120,8 +120,11 @@ void thermal_governor_update_tz(struct thermal_zone_device *tz, enum thermal_notify_event reason); /* Helpers */ -#define for_each_trip(__tz, __trip) \ - for (__trip = __tz->trips; __trip - __tz->trips < __tz->num_trips; __trip++) +#define for_each_trip_desc(__tz, __td) \ + for (__td = __tz->trips; __td - __tz->trips < __tz->num_trips; __td++) + +#define trip_to_trip_desc(__trip) \ + container_of(__trip, struct thermal_trip_desc, trip) void __thermal_zone_set_trips(struct thermal_zone_device *tz); int thermal_zone_trip_id(const struct thermal_zone_device *tz, diff --git a/drivers/thermal/thermal_debugfs.c b/drivers/thermal/thermal_debugfs.c index c617e8b9f0ddfe..78c4cb30e91d74 100644 --- a/drivers/thermal/thermal_debugfs.c +++ b/drivers/thermal/thermal_debugfs.c @@ -744,7 +744,7 @@ static void tze_seq_stop(struct seq_file *s, void *v) static int tze_seq_show(struct seq_file *s, void *v) { struct thermal_zone_device *tz = s->private; - struct thermal_trip *trip; + struct thermal_trip_desc *td; struct tz_episode *tze; const char *type; int trip_id; @@ -757,7 +757,9 @@ static int tze_seq_show(struct seq_file *s, void *v) seq_printf(s, "| trip | type | temp(°mC) | hyst(°mC) | duration | avg(°mC) | min(°mC) | max(°mC) |\n"); - for_each_trip(tz, trip) { + for_each_trip_desc(tz, td) { + const struct thermal_trip *trip = &td->trip; + /* * There is no possible mitigation happening at the * critical trip point, so the stats will be always diff --git a/drivers/thermal/thermal_helpers.c b/drivers/thermal/thermal_helpers.c index c5a057b59c42d3..d9f4e26ec1257c 100644 --- a/drivers/thermal/thermal_helpers.c +++ b/drivers/thermal/thermal_helpers.c @@ -50,7 +50,7 @@ get_thermal_instance(struct thermal_zone_device *tz, mutex_lock(&tz->lock); mutex_lock(&cdev->lock); - trip = &tz->trips[trip_index]; + trip = &tz->trips[trip_index].trip; list_for_each_entry(pos, &tz->thermal_instances, tz_node) { if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { @@ -82,7 +82,7 @@ EXPORT_SYMBOL(get_thermal_instance); */ int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp) { - const struct thermal_trip *trip; + const struct thermal_trip_desc *td; int crit_temp = INT_MAX; int ret = -EINVAL; @@ -91,7 +91,9 @@ int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp) ret = tz->ops.get_temp(tz, temp); if (IS_ENABLED(CONFIG_THERMAL_EMULATION) && tz->emul_temperature) { - for_each_trip(tz, trip) { + for_each_trip_desc(tz, td) { + const struct thermal_trip *trip = &td->trip; + if (trip->type == THERMAL_TRIP_CRITICAL) { crit_temp = trip->temperature; break; diff --git a/drivers/thermal/thermal_netlink.c b/drivers/thermal/thermal_netlink.c index 76a231a2965451..0ef6368da76604 100644 --- a/drivers/thermal/thermal_netlink.c +++ b/drivers/thermal/thermal_netlink.c @@ -445,7 +445,7 @@ out_cancel_nest: static int thermal_genl_cmd_tz_get_trip(struct param *p) { struct sk_buff *msg = p->msg; - const struct thermal_trip *trip; + const struct thermal_trip_desc *td; struct thermal_zone_device *tz; struct nlattr *start_trip; int id; @@ -465,7 +465,9 @@ static int thermal_genl_cmd_tz_get_trip(struct param *p) mutex_lock(&tz->lock); - for_each_trip(tz, trip) { + for_each_trip_desc(tz, td) { + const struct thermal_trip *trip = &td->trip; + if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, thermal_zone_trip_id(tz, trip)) || nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, trip->type) || diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c index 5b533fa40437c3..88211ccdfbd62d 100644 --- a/drivers/thermal/thermal_sysfs.c +++ b/drivers/thermal/thermal_sysfs.c @@ -88,7 +88,7 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr, if (sscanf(attr->attr.name, "trip_point_%d_type", &trip_id) != 1) return -EINVAL; - switch (tz->trips[trip_id].type) { + switch (tz->trips[trip_id].trip.type) { case THERMAL_TRIP_CRITICAL: return sprintf(buf, "critical\n"); case THERMAL_TRIP_HOT: @@ -120,7 +120,7 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr, mutex_lock(&tz->lock); - trip = &tz->trips[trip_id]; + trip = &tz->trips[trip_id].trip; if (temp != trip->temperature) { if (tz->ops.set_trip_temp) { @@ -150,7 +150,7 @@ trip_point_temp_show(struct device *dev, struct device_attribute *attr, if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip_id) != 1) return -EINVAL; - return sprintf(buf, "%d\n", tz->trips[trip_id].temperature); + return sprintf(buf, "%d\n", tz->trips[trip_id].trip.temperature); } static ssize_t @@ -171,7 +171,7 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr, mutex_lock(&tz->lock); - trip = &tz->trips[trip_id]; + trip = &tz->trips[trip_id].trip; if (hyst != trip->hysteresis) { trip->hysteresis = hyst; @@ -194,7 +194,7 @@ trip_point_hyst_show(struct device *dev, struct device_attribute *attr, if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip_id) != 1) return -EINVAL; - return sprintf(buf, "%d\n", tz->trips[trip_id].hysteresis); + return sprintf(buf, "%d\n", tz->trips[trip_id].trip.hysteresis); } static ssize_t @@ -393,7 +393,7 @@ static const struct attribute_group *thermal_zone_attribute_groups[] = { */ static int create_trip_attrs(struct thermal_zone_device *tz) { - const struct thermal_trip *trip; + const struct thermal_trip_desc *td; struct attribute **attrs; /* This function works only for zones with at least one trip */ @@ -429,8 +429,8 @@ static int create_trip_attrs(struct thermal_zone_device *tz) return -ENOMEM; } - for_each_trip(tz, trip) { - int indx = thermal_zone_trip_id(tz, trip); + for_each_trip_desc(tz, td) { + int indx = thermal_zone_trip_id(tz, &td->trip); /* create trip type attribute */ snprintf(tz->trip_type_attrs[indx].name, THERMAL_NAME_LENGTH, @@ -452,7 +452,7 @@ static int create_trip_attrs(struct thermal_zone_device *tz) tz->trip_temp_attrs[indx].name; tz->trip_temp_attrs[indx].attr.attr.mode = S_IRUGO; tz->trip_temp_attrs[indx].attr.show = trip_point_temp_show; - if (trip->flags & THERMAL_TRIP_FLAG_RW_TEMP) { + if (td->trip.flags & THERMAL_TRIP_FLAG_RW_TEMP) { tz->trip_temp_attrs[indx].attr.attr.mode |= S_IWUSR; tz->trip_temp_attrs[indx].attr.store = trip_point_temp_store; @@ -467,7 +467,7 @@ static int create_trip_attrs(struct thermal_zone_device *tz) tz->trip_hyst_attrs[indx].name; tz->trip_hyst_attrs[indx].attr.attr.mode = S_IRUGO; tz->trip_hyst_attrs[indx].attr.show = trip_point_hyst_show; - if (trip->flags & THERMAL_TRIP_FLAG_RW_HYST) { + if (td->trip.flags & THERMAL_TRIP_FLAG_RW_HYST) { tz->trip_hyst_attrs[indx].attr.attr.mode |= S_IWUSR; tz->trip_hyst_attrs[indx].attr.store = trip_point_hyst_store; diff --git a/drivers/thermal/thermal_trip.c b/drivers/thermal/thermal_trip.c index 497abf0d47cac5..7cf43b6972725c 100644 --- a/drivers/thermal/thermal_trip.c +++ b/drivers/thermal/thermal_trip.c @@ -13,11 +13,11 @@ int for_each_thermal_trip(struct thermal_zone_device *tz, int (*cb)(struct thermal_trip *, void *), void *data) { - struct thermal_trip *trip; + struct thermal_trip_desc *td; int ret; - for_each_trip(tz, trip) { - ret = cb(trip, data); + for_each_trip_desc(tz, td) { + ret = cb(&td->trip, data); if (ret) return ret; } @@ -63,7 +63,7 @@ EXPORT_SYMBOL_GPL(thermal_zone_get_num_trips); */ void __thermal_zone_set_trips(struct thermal_zone_device *tz) { - const struct thermal_trip *trip; + const struct thermal_trip_desc *td; int low = -INT_MAX, high = INT_MAX; int ret; @@ -72,7 +72,8 @@ void __thermal_zone_set_trips(struct thermal_zone_device *tz) if (!tz->ops.set_trips) return; - for_each_trip(tz, trip) { + for_each_trip_desc(tz, td) { + const struct thermal_trip *trip = &td->trip; int trip_low; trip_low = trip->temperature - trip->hysteresis; @@ -110,7 +111,7 @@ int __thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id, if (!tz || trip_id < 0 || trip_id >= tz->num_trips || !trip) return -EINVAL; - *trip = tz->trips[trip_id]; + *trip = tz->trips[trip_id].trip; return 0; } EXPORT_SYMBOL_GPL(__thermal_zone_get_trip); @@ -135,7 +136,7 @@ int thermal_zone_trip_id(const struct thermal_zone_device *tz, * Assume the trip to be located within the bounds of the thermal * zone's trips[] table. */ - return trip - tz->trips; + return trip_to_trip_desc(trip) - tz->trips; } void thermal_zone_trip_updated(struct thermal_zone_device *tz, const struct thermal_trip *trip) diff --git a/include/linux/thermal.h b/include/linux/thermal.h index c33f50177f5185..67bd13303349c3 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -61,7 +61,6 @@ enum thermal_notify_event { * struct thermal_trip - representation of a point in temperature domain * @temperature: temperature value in miliCelsius * @hysteresis: relative hysteresis in miliCelsius - * @threshold: trip crossing notification threshold miliCelsius * @type: trip point type * @priv: pointer to driver data associated with this trip * @flags: flags representing binary properties of the trip @@ -69,12 +68,16 @@ enum thermal_notify_event { struct thermal_trip { int temperature; int hysteresis; - int threshold; enum thermal_trip_type type; u8 flags; void *priv; }; +struct thermal_trip_desc { + struct thermal_trip trip; + int threshold; +}; + #define THERMAL_TRIP_FLAG_RW_TEMP BIT(0) #define THERMAL_TRIP_FLAG_RW_HYST BIT(1) @@ -203,7 +206,7 @@ struct thermal_zone_device { #ifdef CONFIG_THERMAL_DEBUGFS struct thermal_debugfs *debugfs; #endif - struct thermal_trip trips[] __counted_by(num_trips); + struct thermal_trip_desc trips[] __counted_by(num_trips); }; /** -- cgit 1.2.3-korg From b1ae92dcfa8ed7d5044c525c3a868fcb4d0f9c0e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 2 Apr 2024 20:57:57 +0200 Subject: thermal: core: Make struct thermal_zone_device definition internal Move the definitions of struct thermal_trip_desc and struct thermal_zone_device to an internal header file in the thermal core, as they don't need to be accessible to any code other than the thermal core and so they don't need to be present in a global header. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Acked-by: Daniel Lezcano --- drivers/thermal/thermal_core.h | 85 ++++++++++++++++++++++++++++++++++++ drivers/thermal/thermal_trace.h | 2 + drivers/thermal/thermal_trace_ipa.h | 2 + include/linux/thermal.h | 87 +------------------------------------ 4 files changed, 91 insertions(+), 85 deletions(-) diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 4ad9c671bc1304..5dd3be585e9e9d 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -15,6 +15,91 @@ #include "thermal_netlink.h" #include "thermal_debugfs.h" +struct thermal_trip_desc { + struct thermal_trip trip; + int threshold; +}; + +/** + * struct thermal_zone_device - structure for a thermal zone + * @id: unique id number for each thermal zone + * @type: the thermal zone device type + * @device: &struct device for this thermal zone + * @removal: removal completion + * @trip_temp_attrs: attributes for trip points for sysfs: trip temperature + * @trip_type_attrs: attributes for trip points for sysfs: trip type + * @trip_hyst_attrs: attributes for trip points for sysfs: trip hysteresis + * @mode: current mode of this thermal zone + * @devdata: private pointer for device private data + * @num_trips: number of trip points the thermal zone supports + * @passive_delay_jiffies: number of jiffies to wait between polls when + * performing passive cooling. + * @polling_delay_jiffies: number of jiffies to wait between polls when + * checking whether trip points have been crossed (0 for + * interrupt driven systems) + * @temperature: current temperature. This is only for core code, + * drivers should use thermal_zone_get_temp() to get the + * current temperature + * @last_temperature: previous temperature read + * @emul_temperature: emulated temperature when using CONFIG_THERMAL_EMULATION + * @passive: 1 if you've crossed a passive trip point, 0 otherwise. + * @prev_low_trip: the low current temperature if you've crossed a passive + trip point. + * @prev_high_trip: the above current temperature if you've crossed a + passive trip point. + * @need_update: if equals 1, thermal_zone_device_update needs to be invoked. + * @ops: operations this &thermal_zone_device supports + * @tzp: thermal zone parameters + * @governor: pointer to the governor for this thermal zone + * @governor_data: private pointer for governor data + * @thermal_instances: list of &struct thermal_instance of this thermal zone + * @ida: &struct ida to generate unique id for this zone's cooling + * devices + * @lock: lock to protect thermal_instances list + * @node: node in thermal_tz_list (in thermal_core.c) + * @poll_queue: delayed work for polling + * @notify_event: Last notification event + * @suspended: thermal zone suspend indicator + * @trips: array of struct thermal_trip objects + */ +struct thermal_zone_device { + int id; + char type[THERMAL_NAME_LENGTH]; + struct device device; + struct completion removal; + struct attribute_group trips_attribute_group; + struct thermal_attr *trip_temp_attrs; + struct thermal_attr *trip_type_attrs; + struct thermal_attr *trip_hyst_attrs; + enum thermal_device_mode mode; + void *devdata; + int num_trips; + unsigned long passive_delay_jiffies; + unsigned long polling_delay_jiffies; + int temperature; + int last_temperature; + int emul_temperature; + int passive; + int prev_low_trip; + int prev_high_trip; + atomic_t need_update; + struct thermal_zone_device_ops ops; + struct thermal_zone_params *tzp; + struct thermal_governor *governor; + void *governor_data; + struct list_head thermal_instances; + struct ida ida; + struct mutex lock; + struct list_head node; + struct delayed_work poll_queue; + enum thermal_notify_event notify_event; + bool suspended; +#ifdef CONFIG_THERMAL_DEBUGFS + struct thermal_debugfs *debugfs; +#endif + struct thermal_trip_desc trips[] __counted_by(num_trips); +}; + /* Default Thermal Governor */ #if defined(CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE) #define DEFAULT_THERMAL_GOVERNOR "step_wise" diff --git a/drivers/thermal/thermal_trace.h b/drivers/thermal/thermal_trace.h index 459c8ce6cf3b0d..88a962f560f223 100644 --- a/drivers/thermal/thermal_trace.h +++ b/drivers/thermal/thermal_trace.h @@ -9,6 +9,8 @@ #include #include +#include "thermal_core.h" + TRACE_DEFINE_ENUM(THERMAL_TRIP_CRITICAL); TRACE_DEFINE_ENUM(THERMAL_TRIP_HOT); TRACE_DEFINE_ENUM(THERMAL_TRIP_PASSIVE); diff --git a/drivers/thermal/thermal_trace_ipa.h b/drivers/thermal/thermal_trace_ipa.h index b16b5dd863d957..a82821eebc88c3 100644 --- a/drivers/thermal/thermal_trace_ipa.h +++ b/drivers/thermal/thermal_trace_ipa.h @@ -7,6 +7,8 @@ #include +#include "thermal_core.h" + TRACE_EVENT(thermal_power_allocator, TP_PROTO(struct thermal_zone_device *tz, u32 total_req_power, u32 total_granted_power, int num_actors, u32 power_range, diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 67bd13303349c3..62b174528cfe31 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -73,17 +73,14 @@ struct thermal_trip { void *priv; }; -struct thermal_trip_desc { - struct thermal_trip trip; - int threshold; -}; - #define THERMAL_TRIP_FLAG_RW_TEMP BIT(0) #define THERMAL_TRIP_FLAG_RW_HYST BIT(1) #define THERMAL_TRIP_FLAG_RW (THERMAL_TRIP_FLAG_RW_TEMP | \ THERMAL_TRIP_FLAG_RW_HYST) +struct thermal_zone_device; + struct thermal_zone_device_ops { int (*bind) (struct thermal_zone_device *, struct thermal_cooling_device *); @@ -129,86 +126,6 @@ struct thermal_cooling_device { #endif }; -/** - * struct thermal_zone_device - structure for a thermal zone - * @id: unique id number for each thermal zone - * @type: the thermal zone device type - * @device: &struct device for this thermal zone - * @removal: removal completion - * @trip_temp_attrs: attributes for trip points for sysfs: trip temperature - * @trip_type_attrs: attributes for trip points for sysfs: trip type - * @trip_hyst_attrs: attributes for trip points for sysfs: trip hysteresis - * @mode: current mode of this thermal zone - * @devdata: private pointer for device private data - * @num_trips: number of trip points the thermal zone supports - * @passive_delay_jiffies: number of jiffies to wait between polls when - * performing passive cooling. - * @polling_delay_jiffies: number of jiffies to wait between polls when - * checking whether trip points have been crossed (0 for - * interrupt driven systems) - * @temperature: current temperature. This is only for core code, - * drivers should use thermal_zone_get_temp() to get the - * current temperature - * @last_temperature: previous temperature read - * @emul_temperature: emulated temperature when using CONFIG_THERMAL_EMULATION - * @passive: 1 if you've crossed a passive trip point, 0 otherwise. - * @prev_low_trip: the low current temperature if you've crossed a passive - trip point. - * @prev_high_trip: the above current temperature if you've crossed a - passive trip point. - * @need_update: if equals 1, thermal_zone_device_update needs to be invoked. - * @ops: operations this &thermal_zone_device supports - * @tzp: thermal zone parameters - * @governor: pointer to the governor for this thermal zone - * @governor_data: private pointer for governor data - * @thermal_instances: list of &struct thermal_instance of this thermal zone - * @ida: &struct ida to generate unique id for this zone's cooling - * devices - * @lock: lock to protect thermal_instances list - * @node: node in thermal_tz_list (in thermal_core.c) - * @poll_queue: delayed work for polling - * @notify_event: Last notification event - * @suspended: thermal zone suspend indicator - * @trips: array of struct thermal_trip objects - */ -struct thermal_zone_device { - int id; - char type[THERMAL_NAME_LENGTH]; - struct device device; - struct completion removal; - struct attribute_group trips_attribute_group; - struct thermal_attr *trip_temp_attrs; - struct thermal_attr *trip_type_attrs; - struct thermal_attr *trip_hyst_attrs; - enum thermal_device_mode mode; - void *devdata; - int num_trips; - unsigned long passive_delay_jiffies; - unsigned long polling_delay_jiffies; - int temperature; - int last_temperature; - int emul_temperature; - int passive; - int prev_low_trip; - int prev_high_trip; - atomic_t need_update; - struct thermal_zone_device_ops ops; - struct thermal_zone_params *tzp; - struct thermal_governor *governor; - void *governor_data; - struct list_head thermal_instances; - struct ida ida; - struct mutex lock; - struct list_head node; - struct delayed_work poll_queue; - enum thermal_notify_event notify_event; - bool suspended; -#ifdef CONFIG_THERMAL_DEBUGFS - struct thermal_debugfs *debugfs; -#endif - struct thermal_trip_desc trips[] __counted_by(num_trips); -}; - /** * struct thermal_governor - structure that holds thermal governor information * @name: name of the governor -- cgit 1.2.3-korg From f99c1b87a902fcc81df569f2ff939d47880cd741 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 2 Apr 2024 20:59:01 +0200 Subject: thermal: core: Rewrite comments in handle_thermal_trip() Make the comments regarding trip crossing and threshold updates in handle_thermal_trip() slightly more clear. No functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Acked-by: Daniel Lezcano --- drivers/thermal/thermal_core.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index fc6ff0a4faa1d1..c4bc7973a6cd33 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -368,6 +368,13 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, if (trip->temperature == THERMAL_TEMP_INVALID) return; + /* + * If the trip temperature or hysteresis has been updated recently, + * the threshold needs to be computed again using the new values. + * However, its initial value still reflects the old ones and that + * is what needs to be compared with the previous zone temperature + * to decide which action to take. + */ if (tz->last_temperature == THERMAL_TEMP_INVALID) { /* Initialization. */ td->threshold = trip->temperature; @@ -375,11 +382,9 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, td->threshold -= trip->hysteresis; } else if (tz->last_temperature < td->threshold) { /* - * The trip threshold is equal to the trip temperature, unless - * the latter has changed in the meantime. In either case, - * the trip is crossed if the current zone temperature is at - * least equal to its temperature, but otherwise ensure that - * the threshold and the trip temperature will be equal. + * There is no mitigation under way, so it needs to be started + * if the zone temperature exceeds the trip one. The new + * threshold is then set to the low temperature of the trip. */ if (tz->temperature >= trip->temperature) { thermal_notify_tz_trip_up(tz, trip); @@ -390,14 +395,9 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, } } else { /* - * The previous zone temperature was above or equal to the trip - * threshold, which would be equal to the "low temperature" of - * the trip (its temperature minus its hysteresis), unless the - * trip temperature or hysteresis had changed. In either case, - * the trip is crossed if the current zone temperature is below - * the low temperature of the trip, but otherwise ensure that - * the trip threshold will be equal to the low temperature of - * the trip. + * Mitigation is under way, so it needs to stop if the zone + * temperature falls below the low temperature of the trip. + * In that case, the trip temperature becomes the new threshold. */ if (tz->temperature < trip->temperature - trip->hysteresis) { thermal_notify_tz_trip_down(tz, trip); -- cgit 1.2.3-korg From 9ad18043fb35feb1d82c1e594575346f16d47dc7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 2 Apr 2024 21:02:10 +0200 Subject: thermal: core: Send trip crossing notifications at init time if needed If a trip point is already exceeded by the zone temperature at the initialization time, no trip crossing notification is send regarding this even though mitigation should be started then. Address this by rearranging the code in handle_thermal_trip() to send a trip crossing notification for trip points already exceeded by the zone temperature initially which also allows to reduce its size by using the observation that the initialization and regular trip crossing on the way up become the same case then. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba --- drivers/thermal/thermal_core.c | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index c4bc7973a6cd33..28f5d9b06341fa 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -364,6 +364,7 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, struct thermal_trip_desc *td) { const struct thermal_trip *trip = &td->trip; + int old_threshold; if (trip->temperature == THERMAL_TEMP_INVALID) return; @@ -375,25 +376,11 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, * is what needs to be compared with the previous zone temperature * to decide which action to take. */ - if (tz->last_temperature == THERMAL_TEMP_INVALID) { - /* Initialization. */ - td->threshold = trip->temperature; - if (tz->temperature >= td->threshold) - td->threshold -= trip->hysteresis; - } else if (tz->last_temperature < td->threshold) { - /* - * There is no mitigation under way, so it needs to be started - * if the zone temperature exceeds the trip one. The new - * threshold is then set to the low temperature of the trip. - */ - if (tz->temperature >= trip->temperature) { - thermal_notify_tz_trip_up(tz, trip); - thermal_debug_tz_trip_up(tz, trip); - td->threshold = trip->temperature - trip->hysteresis; - } else { - td->threshold = trip->temperature; - } - } else { + old_threshold = td->threshold; + td->threshold = trip->temperature; + + if (tz->last_temperature >= old_threshold && + tz->last_temperature != THERMAL_TEMP_INVALID) { /* * Mitigation is under way, so it needs to stop if the zone * temperature falls below the low temperature of the trip. @@ -402,10 +389,18 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, if (tz->temperature < trip->temperature - trip->hysteresis) { thermal_notify_tz_trip_down(tz, trip); thermal_debug_tz_trip_down(tz, trip); - td->threshold = trip->temperature; } else { - td->threshold = trip->temperature - trip->hysteresis; + td->threshold -= trip->hysteresis; } + } else if (tz->temperature >= trip->temperature) { + /* + * There is no mitigation under way, so it needs to be started + * if the zone temperature exceeds the trip one. The new + * threshold is then set to the low temperature of the trip. + */ + thermal_notify_tz_trip_up(tz, trip); + thermal_debug_tz_trip_up(tz, trip); + td->threshold -= trip->hysteresis; } if (trip->type == THERMAL_TRIP_CRITICAL || trip->type == THERMAL_TRIP_HOT) -- cgit 1.2.3-korg From 7454f2c42cce10a74312343b66aa2c3dee05d868 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 2 Apr 2024 21:03:36 +0200 Subject: thermal: core: Sort trip point crossing notifications by temperature If multiple trip points are crossed in one go and the trips table in the thermal zone device object is not sorted, the corresponding trip point crossing notifications sent to user space will not be ordered either. Moreover, if the trips table is sorted by trip temperature in ascending order, the trip crossing notifications on the way up will be sent in that order too, but the trip crossing notifications on the way down will be sent in the reverse order. This is generally confusing and it is better to make the kernel send the notifications in the order of growing (on the way up) or falling (on the way down) trip temperature. To achieve that, instead of sending a trip crossing notification and recording a trip crossing event in the statistics right away from handle_thermal_trip(), put the trip in question on a list that will be sorted by __thermal_zone_device_update() after processing all of the trips and before sending the notifications and recording trip crossing events. Link: https://lore.kernel.org/linux-pm/20240306085428.88011-1-daniel.lezcano@linaro.org/ Reported-by: Daniel Lezcano Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba --- drivers/thermal/thermal_core.c | 41 +++++++++++++++++++++++++++++++++++------ drivers/thermal/thermal_core.h | 2 ++ 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 28f5d9b06341fa..58e60bcdc0a58a 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -361,7 +362,9 @@ static void handle_critical_trips(struct thermal_zone_device *tz, } static void handle_thermal_trip(struct thermal_zone_device *tz, - struct thermal_trip_desc *td) + struct thermal_trip_desc *td, + struct list_head *way_up_list, + struct list_head *way_down_list) { const struct thermal_trip *trip = &td->trip; int old_threshold; @@ -387,8 +390,8 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, * In that case, the trip temperature becomes the new threshold. */ if (tz->temperature < trip->temperature - trip->hysteresis) { - thermal_notify_tz_trip_down(tz, trip); - thermal_debug_tz_trip_down(tz, trip); + list_add(&td->notify_list_node, way_down_list); + td->notify_temp = trip->temperature - trip->hysteresis; } else { td->threshold -= trip->hysteresis; } @@ -398,8 +401,8 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, * if the zone temperature exceeds the trip one. The new * threshold is then set to the low temperature of the trip. */ - thermal_notify_tz_trip_up(tz, trip); - thermal_debug_tz_trip_up(tz, trip); + list_add_tail(&td->notify_list_node, way_up_list); + td->notify_temp = trip->temperature; td->threshold -= trip->hysteresis; } @@ -452,10 +455,24 @@ static void thermal_zone_device_init(struct thermal_zone_device *tz) pos->initialized = false; } +static int thermal_trip_notify_cmp(void *ascending, const struct list_head *a, + const struct list_head *b) +{ + struct thermal_trip_desc *tda = container_of(a, struct thermal_trip_desc, + notify_list_node); + struct thermal_trip_desc *tdb = container_of(b, struct thermal_trip_desc, + notify_list_node); + int ret = tdb->notify_temp - tda->notify_temp; + + return ascending ? ret : -ret; +} + void __thermal_zone_device_update(struct thermal_zone_device *tz, enum thermal_notify_event event) { struct thermal_trip_desc *td; + LIST_HEAD(way_down_list); + LIST_HEAD(way_up_list); if (tz->suspended) return; @@ -470,7 +487,19 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, tz->notify_event = event; for_each_trip_desc(tz, td) - handle_thermal_trip(tz, td); + handle_thermal_trip(tz, td, &way_up_list, &way_down_list); + + list_sort(&way_up_list, &way_up_list, thermal_trip_notify_cmp); + list_for_each_entry(td, &way_up_list, notify_list_node) { + thermal_notify_tz_trip_up(tz, &td->trip); + thermal_debug_tz_trip_up(tz, &td->trip); + } + + list_sort(NULL, &way_down_list, thermal_trip_notify_cmp); + list_for_each_entry(td, &way_down_list, notify_list_node) { + thermal_notify_tz_trip_down(tz, &td->trip); + thermal_debug_tz_trip_down(tz, &td->trip); + } monitor_thermal_zone(tz); } diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 5dd3be585e9e9d..9d3ef1e0645fd4 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -17,6 +17,8 @@ struct thermal_trip_desc { struct thermal_trip trip; + struct list_head notify_list_node; + int notify_temp; int threshold; }; -- cgit 1.2.3-korg From 0444d574fbc3d3af0e426f7c2b72f5830269f096 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 4 Apr 2024 21:27:07 +0200 Subject: thermal: core: Relocate the struct thermal_governor definition Notice that struct thermal_governor is only used by the thermal core and so move its definition to thermal_core.h. No functional impact. Signed-off-by: Rafael J. Wysocki Acked-by: Daniel Lezcano Reviewed-by: Lukasz Luba --- drivers/thermal/thermal_core.h | 25 +++++++++++++++++++++++++ include/linux/thermal.h | 25 ------------------------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 9d3ef1e0645fd4..b461d9583834cf 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -22,6 +22,31 @@ struct thermal_trip_desc { int threshold; }; +/** + * struct thermal_governor - structure that holds thermal governor information + * @name: name of the governor + * @bind_to_tz: callback called when binding to a thermal zone. If it + * returns 0, the governor is bound to the thermal zone, + * otherwise it fails. + * @unbind_from_tz: callback called when a governor is unbound from a + * thermal zone. + * @throttle: callback called for every trip point even if temperature is + * below the trip point temperature + * @update_tz: callback called when thermal zone internals have changed, e.g. + * thermal cooling instance was added/removed + * @governor_list: node in thermal_governor_list (in thermal_core.c) + */ +struct thermal_governor { + const char *name; + int (*bind_to_tz)(struct thermal_zone_device *tz); + void (*unbind_from_tz)(struct thermal_zone_device *tz); + int (*throttle)(struct thermal_zone_device *tz, + const struct thermal_trip *trip); + void (*update_tz)(struct thermal_zone_device *tz, + enum thermal_notify_event reason); + struct list_head governor_list; +}; + /** * struct thermal_zone_device - structure for a thermal zone * @id: unique id number for each thermal zone diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 62b174528cfe31..f1155c0439c4b4 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -126,31 +126,6 @@ struct thermal_cooling_device { #endif }; -/** - * struct thermal_governor - structure that holds thermal governor information - * @name: name of the governor - * @bind_to_tz: callback called when binding to a thermal zone. If it - * returns 0, the governor is bound to the thermal zone, - * otherwise it fails. - * @unbind_from_tz: callback called when a governor is unbound from a - * thermal zone. - * @throttle: callback called for every trip point even if temperature is - * below the trip point temperature - * @update_tz: callback called when thermal zone internals have changed, e.g. - * thermal cooling instance was added/removed - * @governor_list: node in thermal_governor_list (in thermal_core.c) - */ -struct thermal_governor { - const char *name; - int (*bind_to_tz)(struct thermal_zone_device *tz); - void (*unbind_from_tz)(struct thermal_zone_device *tz); - int (*throttle)(struct thermal_zone_device *tz, - const struct thermal_trip *trip); - void (*update_tz)(struct thermal_zone_device *tz, - enum thermal_notify_event reason); - struct list_head governor_list; -}; - /* Structure to define Thermal Zone parameters */ struct thermal_zone_params { const char *governor_name; -- cgit 1.2.3-korg From e3ac0f367d5806af09d2070bb7951af2f59d1f52 Mon Sep 17 00:00:00 2001 From: Lukasz Luba Date: Wed, 3 Apr 2024 16:49:04 +0100 Subject: OPP: OF: Export dev_opp_pm_calc_power() for usage from EM There are device drivers which can modify voltage values for OPPs. It could be due to the chip binning and those drivers have specific chip knowledge about it. This adjustment can happen after Energy Model is registered, thus EM can have stale data about power. Export dev_opp_pm_calc_power() which can be used by Energy Model to calculate new power with the new voltage for OPPs. Acked-by: Viresh Kumar Reviewed-by: Dietmar Eggemann Signed-off-by: Lukasz Luba Signed-off-by: Rafael J. Wysocki --- drivers/opp/of.c | 17 ++++++++++++----- include/linux/pm_opp.h | 8 ++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/opp/of.c b/drivers/opp/of.c index f9f0b22bccbb43..282eb5966fd035 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c @@ -1494,20 +1494,26 @@ _get_dt_power(struct device *dev, unsigned long *uW, unsigned long *kHz) return 0; } -/* - * Callback function provided to the Energy Model framework upon registration. +/** + * dev_pm_opp_calc_power() - Calculate power value for device with EM + * @dev : Device for which an Energy Model has to be registered + * @uW : New power value that is calculated + * @kHz : Frequency for which the new power is calculated + * * This computes the power estimated by @dev at @kHz if it is the frequency * of an existing OPP, or at the frequency of the first OPP above @kHz otherwise * (see dev_pm_opp_find_freq_ceil()). This function updates @kHz to the ceiled * frequency and @uW to the associated power. The power is estimated as * P = C * V^2 * f with C being the device's capacitance and V and f * respectively the voltage and frequency of the OPP. + * It is also used as a callback function provided to the Energy Model + * framework upon registration. * * Returns -EINVAL if the power calculation failed because of missing * parameters, 0 otherwise. */ -static int __maybe_unused _get_power(struct device *dev, unsigned long *uW, - unsigned long *kHz) +int dev_pm_opp_calc_power(struct device *dev, unsigned long *uW, + unsigned long *kHz) { struct dev_pm_opp *opp; struct device_node *np; @@ -1544,6 +1550,7 @@ static int __maybe_unused _get_power(struct device *dev, unsigned long *uW, return 0; } +EXPORT_SYMBOL_GPL(dev_pm_opp_calc_power); static bool _of_has_opp_microwatt_property(struct device *dev) { @@ -1619,7 +1626,7 @@ int dev_pm_opp_of_register_em(struct device *dev, struct cpumask *cpus) goto failed; } - EM_SET_ACTIVE_POWER_CB(em_cb, _get_power); + EM_SET_ACTIVE_POWER_CB(em_cb, dev_pm_opp_calc_power); register_em: ret = em_dev_register_perf_domain(dev, nr_opp, &em_cb, cpus, true); diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 065a47382302cc..dd7c8441af4247 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -476,6 +476,8 @@ struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp); int of_get_required_opp_performance_state(struct device_node *np, int index); int dev_pm_opp_of_find_icc_paths(struct device *dev, struct opp_table *opp_table); int dev_pm_opp_of_register_em(struct device *dev, struct cpumask *cpus); +int dev_pm_opp_calc_power(struct device *dev, unsigned long *uW, + unsigned long *kHz); static inline void dev_pm_opp_of_unregister_em(struct device *dev) { em_dev_unregister_perf_domain(dev); @@ -539,6 +541,12 @@ static inline void dev_pm_opp_of_unregister_em(struct device *dev) { } +static inline int dev_pm_opp_calc_power(struct device *dev, unsigned long *uW, + unsigned long *kHz) +{ + return -EOPNOTSUPP; +} + static inline int of_get_required_opp_performance_state(struct device_node *np, int index) { return -EOPNOTSUPP; -- cgit 1.2.3-korg From d61c2695bddf56f4527f71cc6ebc31897be36cff Mon Sep 17 00:00:00 2001 From: Lukasz Luba Date: Wed, 3 Apr 2024 16:49:05 +0100 Subject: PM: EM: Refactor em_adjust_new_capacity() Extract em_table_dup() and em_recalc_and_update() from em_adjust_new_capacity(). Both functions will be later reused by the 'update EM due to chip binning' functionality. Reviewed-by: Dietmar Eggemann Signed-off-by: Lukasz Luba Signed-off-by: Rafael J. Wysocki --- kernel/power/energy_model.c | 58 ++++++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/kernel/power/energy_model.c b/kernel/power/energy_model.c index 9e1c9aa399ea9c..6960dd7393b2dc 100644 --- a/kernel/power/energy_model.c +++ b/kernel/power/energy_model.c @@ -674,23 +674,15 @@ void em_dev_unregister_perf_domain(struct device *dev) } EXPORT_SYMBOL_GPL(em_dev_unregister_perf_domain); -/* - * Adjustment of CPU performance values after boot, when all CPUs capacites - * are correctly calculated. - */ -static void em_adjust_new_capacity(struct device *dev, - struct em_perf_domain *pd, - u64 max_cap) +static struct em_perf_table __rcu *em_table_dup(struct em_perf_domain *pd) { struct em_perf_table __rcu *em_table; struct em_perf_state *ps, *new_ps; - int ret, ps_size; + int ps_size; em_table = em_table_alloc(pd); - if (!em_table) { - dev_warn(dev, "EM: allocation failed\n"); - return; - } + if (!em_table) + return NULL; new_ps = em_table->state; @@ -702,24 +694,52 @@ static void em_adjust_new_capacity(struct device *dev, rcu_read_unlock(); - em_init_performance(dev, pd, new_ps, pd->nr_perf_states); - ret = em_compute_costs(dev, new_ps, NULL, pd->nr_perf_states, + return em_table; +} + +static int em_recalc_and_update(struct device *dev, struct em_perf_domain *pd, + struct em_perf_table __rcu *em_table) +{ + int ret; + + ret = em_compute_costs(dev, em_table->state, NULL, pd->nr_perf_states, pd->flags); - if (ret) { - dev_warn(dev, "EM: compute costs failed\n"); - return; - } + if (ret) + goto free_em_table; ret = em_dev_update_perf_domain(dev, em_table); if (ret) - dev_warn(dev, "EM: update failed %d\n", ret); + goto free_em_table; /* * This is one-time-update, so give up the ownership in this updater. * The EM framework has incremented the usage counter and from now * will keep the reference (then free the memory when needed). */ +free_em_table: em_table_free(em_table); + return ret; +} + +/* + * Adjustment of CPU performance values after boot, when all CPUs capacites + * are correctly calculated. + */ +static void em_adjust_new_capacity(struct device *dev, + struct em_perf_domain *pd, + u64 max_cap) +{ + struct em_perf_table __rcu *em_table; + + em_table = em_table_dup(pd); + if (!em_table) { + dev_warn(dev, "EM: allocation failed\n"); + return; + } + + em_init_performance(dev, pd, em_table->state, pd->nr_perf_states); + + em_recalc_and_update(dev, pd, em_table); } static void em_check_capacity_update(void) -- cgit 1.2.3-korg From cf61d53b026805e8222ca28ac2795611eb7fa547 Mon Sep 17 00:00:00 2001 From: Lukasz Luba Date: Wed, 3 Apr 2024 16:49:06 +0100 Subject: PM: EM: Add em_dev_update_chip_binning() Add a function which allows to modify easily the EM after the new voltage information is available. The device drivers for the chip can adjust the voltage values after setup. The voltage for the same frequency in OPP can be different due to chip binning. The voltage impacts the power usage and the EM power values can be updated to reflect that. Reviewed-by: Dietmar Eggemann Signed-off-by: Lukasz Luba Signed-off-by: Rafael J. Wysocki --- include/linux/energy_model.h | 5 +++++ kernel/power/energy_model.c | 48 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/include/linux/energy_model.h b/include/linux/energy_model.h index 70cd7258cd29f5..1ff52020cf7576 100644 --- a/include/linux/energy_model.h +++ b/include/linux/energy_model.h @@ -172,6 +172,7 @@ struct em_perf_table __rcu *em_table_alloc(struct em_perf_domain *pd); void em_table_free(struct em_perf_table __rcu *table); int em_dev_compute_costs(struct device *dev, struct em_perf_state *table, int nr_states); +int em_dev_update_chip_binning(struct device *dev); /** * em_pd_get_efficient_state() - Get an efficient performance state from the EM @@ -386,6 +387,10 @@ int em_dev_compute_costs(struct device *dev, struct em_perf_state *table, { return -EINVAL; } +static inline int em_dev_update_chip_binning(struct device *dev) +{ + return -EINVAL; +} #endif #endif diff --git a/kernel/power/energy_model.c b/kernel/power/energy_model.c index 6960dd7393b2dc..927cc55ba0b3d1 100644 --- a/kernel/power/energy_model.c +++ b/kernel/power/energy_model.c @@ -808,3 +808,51 @@ static void em_update_workfn(struct work_struct *work) { em_check_capacity_update(); } + +/** + * em_dev_update_chip_binning() - Update Energy Model after the new voltage + * information is present in the OPPs. + * @dev : Device for which the Energy Model has to be updated. + * + * This function allows to update easily the EM with new values available in + * the OPP framework and DT. It can be used after the chip has been properly + * verified by device drivers and the voltages adjusted for the 'chip binning'. + */ +int em_dev_update_chip_binning(struct device *dev) +{ + struct em_perf_table __rcu *em_table; + struct em_perf_domain *pd; + int i, ret; + + if (IS_ERR_OR_NULL(dev)) + return -EINVAL; + + pd = em_pd_get(dev); + if (!pd) { + dev_warn(dev, "Couldn't find Energy Model\n"); + return -EINVAL; + } + + em_table = em_table_dup(pd); + if (!em_table) { + dev_warn(dev, "EM: allocation failed\n"); + return -ENOMEM; + } + + /* Update power values which might change due to new voltage in OPPs */ + for (i = 0; i < pd->nr_perf_states; i++) { + unsigned long freq = em_table->state[i].frequency; + unsigned long power; + + ret = dev_pm_opp_calc_power(dev, &power, &freq); + if (ret) { + em_table_free(em_table); + return ret; + } + + em_table->state[i].power = power; + } + + return em_recalc_and_update(dev, pd, em_table); +} +EXPORT_SYMBOL_GPL(em_dev_update_chip_binning); -- cgit 1.2.3-korg From a5bb5e0877dee3595037eb8767b6bed047c898a5 Mon Sep 17 00:00:00 2001 From: Lukasz Luba Date: Wed, 3 Apr 2024 16:49:07 +0100 Subject: soc: samsung: exynos-asv: Update Energy Model after adjusting voltage When the voltage for OPPs is adjusted there is a need to also update Energy Model framework. The EM data contains power values which depend on voltage values. The EM structure is used for thermal (IPA governor) and in scheduler task placement (EAS) so it should reflect the real HW model as best as possible to operate properly. Based on data on Exynos5422 ASV tables the maximum power difference might be ~29%. An Odroid-XU4 (with a random sample SoC in this chip lottery) showed power difference for some OPPs ~20%. Therefore, it's worth to update the EM. Reviewed-by: Krzysztof Kozlowski Reviewed-by: Dietmar Eggemann Signed-off-by: Lukasz Luba Signed-off-by: Rafael J. Wysocki --- drivers/soc/samsung/exynos-asv.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/soc/samsung/exynos-asv.c b/drivers/soc/samsung/exynos-asv.c index d60af8acc39162..97006cc3b94610 100644 --- a/drivers/soc/samsung/exynos-asv.c +++ b/drivers/soc/samsung/exynos-asv.c @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -97,9 +98,16 @@ static int exynos_asv_update_opps(struct exynos_asv *asv) last_opp_table = opp_table; ret = exynos_asv_update_cpu_opps(asv, cpu); - if (ret < 0) + if (!ret) { + /* + * Update EM power values since OPP + * voltage values may have changed. + */ + em_dev_update_chip_binning(cpu); + } else { dev_err(asv->dev, "Couldn't udate OPPs for cpu%d\n", cpuid); + } } dev_pm_opp_put_opp_table(opp_table); -- cgit 1.2.3-korg From 79b510c49207489f7e019b4c397b04d22b4dfd8d Mon Sep 17 00:00:00 2001 From: Sumeet Pawnikar Date: Fri, 5 Apr 2024 17:48:19 +0530 Subject: ACPI: DPTF: Add Lunar Lake support Add Lunar Lake ACPI IDs for DPTF devices. Signed-off-by: Sumeet Pawnikar Signed-off-by: Rafael J. Wysocki --- drivers/acpi/dptf/dptf_pch_fivr.c | 1 + drivers/acpi/dptf/dptf_power.c | 2 ++ drivers/acpi/dptf/int340x_thermal.c | 6 ++++++ drivers/acpi/fan.h | 1 + drivers/thermal/intel/int340x_thermal/int3400_thermal.c | 1 + drivers/thermal/intel/int340x_thermal/int3403_thermal.c | 1 + 6 files changed, 12 insertions(+) diff --git a/drivers/acpi/dptf/dptf_pch_fivr.c b/drivers/acpi/dptf/dptf_pch_fivr.c index 654aaa53c67f77..d202730fafd8d6 100644 --- a/drivers/acpi/dptf/dptf_pch_fivr.c +++ b/drivers/acpi/dptf/dptf_pch_fivr.c @@ -150,6 +150,7 @@ static const struct acpi_device_id pch_fivr_device_ids[] = { {"INTC1045", 0}, {"INTC1049", 0}, {"INTC1064", 0}, + {"INTC106B", 0}, {"INTC10A3", 0}, {"", 0}, }; diff --git a/drivers/acpi/dptf/dptf_power.c b/drivers/acpi/dptf/dptf_power.c index b8187babbbbb50..8023b3e2331561 100644 --- a/drivers/acpi/dptf/dptf_power.c +++ b/drivers/acpi/dptf/dptf_power.c @@ -232,6 +232,8 @@ static const struct acpi_device_id int3407_device_ids[] = { {"INTC1061", 0}, {"INTC1065", 0}, {"INTC1066", 0}, + {"INTC106C", 0}, + {"INTC106D", 0}, {"INTC10A4", 0}, {"INTC10A5", 0}, {"", 0}, diff --git a/drivers/acpi/dptf/int340x_thermal.c b/drivers/acpi/dptf/int340x_thermal.c index b7113fa92fa685..014ada75995443 100644 --- a/drivers/acpi/dptf/int340x_thermal.c +++ b/drivers/acpi/dptf/int340x_thermal.c @@ -43,6 +43,12 @@ static const struct acpi_device_id int340x_thermal_device_ids[] = { {"INTC1064"}, {"INTC1065"}, {"INTC1066"}, + {"INTC1068"}, + {"INTC1069"}, + {"INTC106A"}, + {"INTC106B"}, + {"INTC106C"}, + {"INTC106D"}, {"INTC10A0"}, {"INTC10A1"}, {"INTC10A2"}, diff --git a/drivers/acpi/fan.h b/drivers/acpi/fan.h index e7b4b4e4a55e48..f89d19c922dc69 100644 --- a/drivers/acpi/fan.h +++ b/drivers/acpi/fan.h @@ -15,6 +15,7 @@ {"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 */ \ {"PNP0C0B", } /* Generic ACPI fan */ diff --git a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c index 427d370648d5af..f8ebdd19d3402f 100644 --- a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c +++ b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c @@ -705,6 +705,7 @@ static const struct acpi_device_id int3400_thermal_match[] = { {"INTC1040", 0}, {"INTC1041", 0}, {"INTC1042", 0}, + {"INTC1068", 0}, {"INTC10A0", 0}, {} }; diff --git a/drivers/thermal/intel/int340x_thermal/int3403_thermal.c b/drivers/thermal/intel/int340x_thermal/int3403_thermal.c index 9b33fd3a66da7a..86901f9f54d8af 100644 --- a/drivers/thermal/intel/int340x_thermal/int3403_thermal.c +++ b/drivers/thermal/intel/int340x_thermal/int3403_thermal.c @@ -284,6 +284,7 @@ static const struct acpi_device_id int3403_device_ids[] = { {"INTC1043", 0}, {"INTC1046", 0}, {"INTC1062", 0}, + {"INTC1069", 0}, {"INTC10A1", 0}, {"", 0}, }; -- cgit 1.2.3-korg From 48b9c4862bd303628b2e772591f2ef958e02a316 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 28 Mar 2024 20:49:11 +0100 Subject: ACPI: store owner from modules with acpi_bus_register_driver() Modules registering driver with acpi_bus_register_driver() often forget to set .owner field. The field is used by some of other kernel parts for reference counting (try_module_get()), so it is expected that drivers will set it. Solve the problem by moving this task away from the drivers to the core ACPI bus code, just like we did for platform_driver in commit 9447057eaff8 ("platform_device: use a macro instead of platform_driver_register"). Acked-by: Rafael J. Wysocki Signed-off-by: Krzysztof Kozlowski Signed-off-by: Rafael J. Wysocki --- drivers/acpi/bus.c | 9 +++++---- include/acpi/acpi_bus.h | 7 ++++++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index a87b10eef77df2..844c4644791421 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -995,25 +995,26 @@ EXPORT_SYMBOL_GPL(acpi_driver_match_device); -------------------------------------------------------------------------- */ /** - * acpi_bus_register_driver - register a driver with the ACPI bus + * __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) +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 = driver->owner; + driver->drv.owner = owner; return driver_register(&driver->drv); } -EXPORT_SYMBOL(acpi_bus_register_driver); +EXPORT_SYMBOL(__acpi_bus_register_driver); /** * acpi_bus_unregister_driver - unregisters a driver with the ACPI bus diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 5de954e2b18aaa..7453be56f855dd 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -656,7 +656,12 @@ void acpi_scan_lock_release(void); void acpi_lock_hp_context(void); void acpi_unlock_hp_context(void); int acpi_scan_add_handler(struct acpi_scan_handler *handler); -int acpi_bus_register_driver(struct acpi_driver *driver); +/* + * use a macro to avoid include chaining to get THIS_MODULE + */ +#define acpi_bus_register_driver(drv) \ + __acpi_bus_register_driver(drv, THIS_MODULE) +int __acpi_bus_register_driver(struct acpi_driver *driver, struct module *owner); void acpi_bus_unregister_driver(struct acpi_driver *driver); int acpi_bus_scan(acpi_handle handle); void acpi_bus_trim(struct acpi_device *start); -- cgit 1.2.3-korg From 726c149e079898214f5e7fb303c6d0c2b5ac6459 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 28 Mar 2024 20:49:12 +0100 Subject: Input: atlas - drop owner assignment ACPI bus core already sets the .owner, so driver does not need to. Acked-by: Dmitry Torokhov Acked-by: Rafael J. Wysocki Signed-off-by: Krzysztof Kozlowski Signed-off-by: Rafael J. Wysocki --- drivers/input/misc/atlas_btns.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/misc/atlas_btns.c b/drivers/input/misc/atlas_btns.c index 3c9bbd04e14349..5b9be2957746db 100644 --- a/drivers/input/misc/atlas_btns.c +++ b/drivers/input/misc/atlas_btns.c @@ -127,7 +127,6 @@ MODULE_DEVICE_TABLE(acpi, atlas_device_ids); static struct acpi_driver atlas_acpi_driver = { .name = ACPI_ATLAS_NAME, .class = ACPI_ATLAS_CLASS, - .owner = THIS_MODULE, .ids = atlas_device_ids, .ops = { .add = atlas_acpi_button_add, -- cgit 1.2.3-korg From 3bdef399d00e27a26bfc55b8ce6f9a142718a402 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 28 Mar 2024 20:49:13 +0100 Subject: net: fjes: drop owner assignment ACPI bus core already sets the .owner, so driver does not need to. Acked-by: Rafael J. Wysocki Signed-off-by: Krzysztof Kozlowski Signed-off-by: Rafael J. Wysocki --- drivers/net/fjes/fjes_main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/fjes/fjes_main.c b/drivers/net/fjes/fjes_main.c index 5fbe33a09bb0b8..324b34f3ac9376 100644 --- a/drivers/net/fjes/fjes_main.c +++ b/drivers/net/fjes/fjes_main.c @@ -156,7 +156,6 @@ static void fjes_acpi_remove(struct acpi_device *device) static struct acpi_driver fjes_acpi_driver = { .name = DRV_NAME, .class = DRV_NAME, - .owner = THIS_MODULE, .ids = fjes_acpi_ids, .ops = { .add = fjes_acpi_add, -- cgit 1.2.3-korg From 245d97ff34734f5236fd82fb66b8c97a1dd4b136 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 28 Mar 2024 20:49:14 +0100 Subject: platform/chrome: wilco_ec: drop owner assignment ACPI bus core already sets the .owner, so driver does not need to. Acked-by: Tzung-Bi Shih Acked-by: Rafael J. Wysocki Signed-off-by: Krzysztof Kozlowski Signed-off-by: Rafael J. Wysocki --- drivers/platform/chrome/wilco_ec/event.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/platform/chrome/wilco_ec/event.c b/drivers/platform/chrome/wilco_ec/event.c index 13291fb4214e82..ae34e81c5d1808 100644 --- a/drivers/platform/chrome/wilco_ec/event.c +++ b/drivers/platform/chrome/wilco_ec/event.c @@ -523,7 +523,6 @@ static struct acpi_driver event_driver = { .notify = event_device_notify, .remove = event_device_remove, }, - .owner = THIS_MODULE, }; static int __init event_module_init(void) -- cgit 1.2.3-korg From eda8304c74f0211325ab0472a3ac5b4f5aca2c49 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 28 Mar 2024 20:49:15 +0100 Subject: platform: asus-laptop: drop owner assignment ACPI bus core already sets the .owner, so driver does not need to. Acked-by: Hans de Goede Acked-by: Rafael J. Wysocki Signed-off-by: Krzysztof Kozlowski Signed-off-by: Rafael J. Wysocki --- drivers/platform/x86/asus-laptop.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index bf03ea1b127478..78c42767295a11 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -1925,7 +1925,6 @@ MODULE_DEVICE_TABLE(acpi, asus_device_ids); static struct acpi_driver asus_acpi_driver = { .name = ASUS_LAPTOP_NAME, .class = ASUS_LAPTOP_CLASS, - .owner = THIS_MODULE, .ids = asus_device_ids, .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, .ops = { -- cgit 1.2.3-korg From be24e9a0933707f46d568911060dbeb5bcc28578 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 28 Mar 2024 20:49:16 +0100 Subject: platform: classmate-laptop: drop owner assignment ACPI bus core already sets the .owner, so driver does not need to. Acked-by: Thadeu Lima de Souza Cascardo Acked-by: Hans de Goede Acked-by: Rafael J. Wysocki Signed-off-by: Krzysztof Kozlowski Signed-off-by: Rafael J. Wysocki --- drivers/platform/x86/classmate-laptop.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 2edaea2492df73..87462e7c6219f1 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -434,7 +434,6 @@ static const struct acpi_device_id cmpc_accel_device_ids_v4[] = { }; static struct acpi_driver cmpc_accel_acpi_driver_v4 = { - .owner = THIS_MODULE, .name = "cmpc_accel_v4", .class = "cmpc_accel_v4", .ids = cmpc_accel_device_ids_v4, @@ -660,7 +659,6 @@ static const struct acpi_device_id cmpc_accel_device_ids[] = { }; static struct acpi_driver cmpc_accel_acpi_driver = { - .owner = THIS_MODULE, .name = "cmpc_accel", .class = "cmpc_accel", .ids = cmpc_accel_device_ids, @@ -754,7 +752,6 @@ static const struct acpi_device_id cmpc_tablet_device_ids[] = { }; static struct acpi_driver cmpc_tablet_acpi_driver = { - .owner = THIS_MODULE, .name = "cmpc_tablet", .class = "cmpc_tablet", .ids = cmpc_tablet_device_ids, @@ -996,7 +993,6 @@ static const struct acpi_device_id cmpc_ipml_device_ids[] = { }; static struct acpi_driver cmpc_ipml_acpi_driver = { - .owner = THIS_MODULE, .name = "cmpc", .class = "cmpc", .ids = cmpc_ipml_device_ids, @@ -1064,7 +1060,6 @@ static const struct acpi_device_id cmpc_keys_device_ids[] = { }; static struct acpi_driver cmpc_keys_acpi_driver = { - .owner = THIS_MODULE, .name = "cmpc_keys", .class = "cmpc_keys", .ids = cmpc_keys_device_ids, -- cgit 1.2.3-korg From 1baad72e9026a8d4a03cec7eace3b4c1c53b5653 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 28 Mar 2024 20:49:17 +0100 Subject: platform/x86/dell: drop owner assignment ACPI bus core already sets the .owner, so driver does not need to. Acked-by: Hans de Goede Acked-by: Rafael J. Wysocki Signed-off-by: Krzysztof Kozlowski Signed-off-by: Rafael J. Wysocki --- drivers/platform/x86/dell/dell-rbtn.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/platform/x86/dell/dell-rbtn.c b/drivers/platform/x86/dell/dell-rbtn.c index c8fcb537fd65db..a415c432d4c38f 100644 --- a/drivers/platform/x86/dell/dell-rbtn.c +++ b/drivers/platform/x86/dell/dell-rbtn.c @@ -295,7 +295,6 @@ static struct acpi_driver rbtn_driver = { .remove = rbtn_remove, .notify = rbtn_notify, }, - .owner = THIS_MODULE, }; -- cgit 1.2.3-korg From 4313188f8128e9207eeb6808201b121cdaed1861 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 28 Mar 2024 20:49:18 +0100 Subject: platform/x86/eeepc: drop owner assignment ACPI bus core already sets the .owner, so driver does not need to. Acked-by: Hans de Goede Acked-by: Rafael J. Wysocki Signed-off-by: Krzysztof Kozlowski Signed-off-by: Rafael J. Wysocki --- drivers/platform/x86/eeepc-laptop.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index ff1b70269ccbf8..447364bed249c3 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -1463,7 +1463,6 @@ MODULE_DEVICE_TABLE(acpi, eeepc_device_ids); static struct acpi_driver eeepc_acpi_driver = { .name = EEEPC_LAPTOP_NAME, .class = EEEPC_ACPI_CLASS, - .owner = THIS_MODULE, .ids = eeepc_device_ids, .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, .ops = { -- cgit 1.2.3-korg From 68370cc2e32aa811c841b109a34c92ae56c03caa Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 28 Mar 2024 20:49:19 +0100 Subject: platform/x86/intel/rst: drop owner assignment ACPI bus core already sets the .owner, so driver does not need to. Acked-by: Hans de Goede Acked-by: Rafael J. Wysocki Signed-off-by: Krzysztof Kozlowski Signed-off-by: Rafael J. Wysocki --- drivers/platform/x86/intel/rst.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/platform/x86/intel/rst.c b/drivers/platform/x86/intel/rst.c index 35814a7707af79..6bc9c4a603e076 100644 --- a/drivers/platform/x86/intel/rst.c +++ b/drivers/platform/x86/intel/rst.c @@ -125,7 +125,6 @@ static const struct acpi_device_id irst_ids[] = { }; static struct acpi_driver irst_driver = { - .owner = THIS_MODULE, .name = "intel_rapid_start", .class = "intel_rapid_start", .ids = irst_ids, -- cgit 1.2.3-korg From e84a761f121524103df7b3674a8cde67a4f78fa6 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 28 Mar 2024 20:49:20 +0100 Subject: platform/x86/intel/smartconnect: drop owner assignment ACPI bus core already sets the .owner, so driver does not need to. Acked-by: Hans de Goede Acked-by: Rafael J. Wysocki Signed-off-by: Krzysztof Kozlowski Signed-off-by: Rafael J. Wysocki --- drivers/platform/x86/intel/smartconnect.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/platform/x86/intel/smartconnect.c b/drivers/platform/x86/intel/smartconnect.c index 64c2dc93472f47..cd25d05853249b 100644 --- a/drivers/platform/x86/intel/smartconnect.c +++ b/drivers/platform/x86/intel/smartconnect.c @@ -32,7 +32,6 @@ static const struct acpi_device_id smartconnect_ids[] = { MODULE_DEVICE_TABLE(acpi, smartconnect_ids); static struct acpi_driver smartconnect_driver = { - .owner = THIS_MODULE, .name = "intel_smart_connect", .class = "intel_smart_connect", .ids = smartconnect_ids, -- cgit 1.2.3-korg From 2929a735d92e232f92517b0c7a7e4ac8c8fa669b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 28 Mar 2024 20:49:21 +0100 Subject: platform/x86/lg-laptop: drop owner assignment ACPI bus core already sets the .owner, so driver does not need to. Acked-by: Hans de Goede Acked-by: Rafael J. Wysocki Signed-off-by: Krzysztof Kozlowski Signed-off-by: Rafael J. Wysocki --- drivers/platform/x86/lg-laptop.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c index ad3c39e9e9f586..36e88b9d59eaf6 100644 --- a/drivers/platform/x86/lg-laptop.c +++ b/drivers/platform/x86/lg-laptop.c @@ -790,7 +790,6 @@ static struct acpi_driver acpi_driver = { .remove = acpi_remove, .notify = acpi_notify, }, - .owner = THIS_MODULE, }; static int __init acpi_init(void) -- cgit 1.2.3-korg From 562231f34ceade26c540121cba16b02f9a0f1d9f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 28 Mar 2024 20:49:22 +0100 Subject: platform/x86/sony-laptop: drop owner assignment ACPI bus core already sets the .owner, so driver does not need to. Acked-by: Hans de Goede Acked-by: Rafael J. Wysocki Signed-off-by: Krzysztof Kozlowski Signed-off-by: Rafael J. Wysocki --- drivers/platform/x86/sony-laptop.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 40878e327afdf7..3e94fdd1ea52e0 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -3303,7 +3303,6 @@ static struct acpi_driver sony_nc_driver = { .name = SONY_NC_DRIVER_NAME, .class = SONY_NC_CLASS, .ids = sony_nc_device_ids, - .owner = THIS_MODULE, .ops = { .add = sony_nc_add, .remove = sony_nc_remove, @@ -4844,7 +4843,6 @@ static struct acpi_driver sony_pic_driver = { .name = SONY_PIC_DRIVER_NAME, .class = SONY_PIC_CLASS, .ids = sony_pic_device_ids, - .owner = THIS_MODULE, .ops = { .add = sony_pic_add, .remove = sony_pic_remove, -- cgit 1.2.3-korg From b655cda9f08915f07a057c6e6ea052a6645b55d4 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 28 Mar 2024 20:49:23 +0100 Subject: platform/x86/toshiba_acpi: drop owner assignment ACPI bus core already sets the .owner, so driver does not need to. Acked-by: Hans de Goede Acked-by: Rafael J. Wysocki Signed-off-by: Krzysztof Kozlowski Signed-off-by: Rafael J. Wysocki --- drivers/platform/x86/toshiba_acpi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 291f14ef67024a..6f9bd675f04459 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -3581,7 +3581,6 @@ static SIMPLE_DEV_PM_OPS(toshiba_acpi_pm, static struct acpi_driver toshiba_acpi_driver = { .name = "Toshiba ACPI driver", - .owner = THIS_MODULE, .ids = toshiba_device_ids, .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, .ops = { -- cgit 1.2.3-korg From ce69eeb2ccb76155a9a34572250764a1e625c7c7 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 28 Mar 2024 20:49:24 +0100 Subject: platform/x86/toshiba_bluetooth: drop owner assignment ACPI bus core already sets the .owner, so driver does not need to. Acked-by: Hans de Goede Acked-by: Rafael J. Wysocki Signed-off-by: Krzysztof Kozlowski Signed-off-by: Rafael J. Wysocki --- drivers/platform/x86/toshiba_bluetooth.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/platform/x86/toshiba_bluetooth.c b/drivers/platform/x86/toshiba_bluetooth.c index d8f81962a2402d..dad2c3e55904cc 100644 --- a/drivers/platform/x86/toshiba_bluetooth.c +++ b/drivers/platform/x86/toshiba_bluetooth.c @@ -59,7 +59,6 @@ static struct acpi_driver toshiba_bt_rfkill_driver = { .remove = toshiba_bt_rfkill_remove, .notify = toshiba_bt_rfkill_notify, }, - .owner = THIS_MODULE, .drv.pm = &toshiba_bt_pm, }; -- cgit 1.2.3-korg From eb22f3ba0c2efc8f036f645b5e2ff699115a5109 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 28 Mar 2024 20:49:25 +0100 Subject: platform/x86/toshiba_haps: drop owner assignment ACPI bus core already sets the .owner, so driver does not need to. Acked-by: Hans de Goede Acked-by: Rafael J. Wysocki Signed-off-by: Krzysztof Kozlowski Signed-off-by: Rafael J. Wysocki --- drivers/platform/x86/toshiba_haps.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/platform/x86/toshiba_haps.c b/drivers/platform/x86/toshiba_haps.c index 8c9f76286b0807..03dfddeee0c0af 100644 --- a/drivers/platform/x86/toshiba_haps.c +++ b/drivers/platform/x86/toshiba_haps.c @@ -251,7 +251,6 @@ MODULE_DEVICE_TABLE(acpi, haps_device_ids); static struct acpi_driver toshiba_haps_driver = { .name = "Toshiba HAPS", - .owner = THIS_MODULE, .ids = haps_device_ids, .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, .ops = { -- cgit 1.2.3-korg From d49c09ddfbd5566f0071aa482093dc0c2153223b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 28 Mar 2024 20:49:26 +0100 Subject: platform/x86/wireless-hotkey: drop owner assignment ACPI bus core already sets the .owner, so driver does not need to. Acked-by: Hans de Goede Acked-by: Rafael J. Wysocki Signed-off-by: Krzysztof Kozlowski Signed-off-by: Rafael J. Wysocki --- drivers/platform/x86/wireless-hotkey.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/platform/x86/wireless-hotkey.c b/drivers/platform/x86/wireless-hotkey.c index 4422863f47bbe6..e95cdbbfb70899 100644 --- a/drivers/platform/x86/wireless-hotkey.c +++ b/drivers/platform/x86/wireless-hotkey.c @@ -110,7 +110,6 @@ static void wl_remove(struct acpi_device *device) static struct acpi_driver wl_driver = { .name = "wireless-hotkey", - .owner = THIS_MODULE, .ids = wl_ids, .ops = { .add = wl_add, -- cgit 1.2.3-korg From cd3eda2e35082094db5643553035f7169b006883 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 28 Mar 2024 20:49:27 +0100 Subject: ptp: vmw: drop owner assignment ACPI bus core already sets the .owner, so driver does not need to. Acked-by: Rafael J. Wysocki Signed-off-by: Krzysztof Kozlowski Signed-off-by: Rafael J. Wysocki --- drivers/ptp/ptp_vmw.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/ptp/ptp_vmw.c b/drivers/ptp/ptp_vmw.c index 27c5547aa8a9aa..7ec90359428abf 100644 --- a/drivers/ptp/ptp_vmw.c +++ b/drivers/ptp/ptp_vmw.c @@ -120,7 +120,6 @@ static struct acpi_driver ptp_vmw_acpi_driver = { .add = ptp_vmw_acpi_add, .remove = ptp_vmw_acpi_remove }, - .owner = THIS_MODULE }; static int __init ptp_vmw_init(void) -- cgit 1.2.3-korg From 00e8b52bf9f9fdf991274cd1b140316305edb040 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 28 Mar 2024 20:49:28 +0100 Subject: virt: vmgenid: drop owner assignment ACPI bus core already sets the .owner, so driver does not need to. Acked-by: Rafael J. Wysocki Signed-off-by: Krzysztof Kozlowski Signed-off-by: Rafael J. Wysocki --- drivers/virt/vmgenid.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/virt/vmgenid.c b/drivers/virt/vmgenid.c index b67a28da47026d..8f6880c3a87fa7 100644 --- a/drivers/virt/vmgenid.c +++ b/drivers/virt/vmgenid.c @@ -88,7 +88,6 @@ static const struct acpi_device_id vmgenid_ids[] = { static struct acpi_driver vmgenid_driver = { .name = "vmgenid", .ids = vmgenid_ids, - .owner = THIS_MODULE, .ops = { .add = vmgenid_add, .notify = vmgenid_notify -- cgit 1.2.3-korg From cc85f9c05bba23eb525497b42ac5b74801ccbd87 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 28 Mar 2024 20:49:29 +0100 Subject: ACPI: drop redundant owner from acpi_driver Once all .owner is removed from all acpi_driver instances, drop it from the structure. Acked-by: Rafael J. Wysocki Signed-off-by: Krzysztof Kozlowski Signed-off-by: Rafael J. Wysocki --- include/acpi/acpi_bus.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 7453be56f855dd..32aae3ee99ac3a 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -170,7 +170,6 @@ struct acpi_driver { unsigned int flags; struct acpi_device_ops ops; struct device_driver drv; - struct module *owner; }; /* -- cgit 1.2.3-korg From 5a87e0020d53f21965adf42420766e4d8719a229 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 29 Mar 2024 11:02:01 +0100 Subject: ACPI: APEI: EINJ: mark remove callback as __exit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The einj_driver driver is registered using platform_driver_probe(). In this case it cannot get unbound via sysfs and it's ok to put the remove callback into an exit section. To prevent the modpost warning about einj_driver referencing .exit.text, mark the driver struct with __refdata and explain the situation in a comment. This is an improvement over commit a24118a8a687 ("ACPI: APEI: EINJ: mark remove callback as non-__exit") which recently addressed the same issue, but picked a less optimal variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Dan Williams Acked-by: Arnd Bergmann Signed-off-by: Rafael J. Wysocki --- drivers/acpi/apei/einj-core.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/apei/einj-core.c b/drivers/acpi/apei/einj-core.c index 01faca3a238a3a..9515bcfe5e9732 100644 --- a/drivers/acpi/apei/einj-core.c +++ b/drivers/acpi/apei/einj-core.c @@ -851,7 +851,7 @@ err_put_table: return rc; } -static void einj_remove(struct platform_device *pdev) +static void __exit einj_remove(struct platform_device *pdev) { struct apei_exec_context ctx; @@ -873,8 +873,14 @@ static void einj_remove(struct platform_device *pdev) } static struct platform_device *einj_dev; -static struct platform_driver einj_driver = { - .remove_new = einj_remove, +/* + * einj_remove() lives in .exit.text. For drivers registered via + * platform_driver_probe() this is ok because they cannot get unbound at + * runtime. So mark the driver struct with __refdata to prevent modpost + * triggering a section mismatch warning. + */ +static struct platform_driver einj_driver __refdata = { + .remove_new = __exit_p(einj_remove), .driver = { .name = "acpi-einj", }, -- cgit 1.2.3-korg From 07b73ee599428b41d0240f2f7b31b524eba07dd0 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 4 Apr 2024 15:06:58 +0300 Subject: ACPI: LPSS: Advertise number of chip selects via property Advertise number of chip selects via property for Intel Braswell. Fixes: 620c803f42de ("ACPI: LPSS: Provide an SSP type to the driver") Signed-off-by: Andy Shevchenko Reviewed-by: Kuppuswamy Sathyanarayanan Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_lpss.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 04e273167e92a6..8e01792228d1e1 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -325,6 +325,7 @@ static const struct lpss_device_desc bsw_i2c_dev_desc = { static const struct property_entry bsw_spi_properties[] = { PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_BSW_SSP), + PROPERTY_ENTRY_U32("num-cs", 2), { } }; -- cgit 1.2.3-korg From 1b2a34f20bf0bfe4c86781fd6d7b323ec786ba1a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 4 Apr 2024 15:06:59 +0300 Subject: ACPI: LPSS: Remove nested ifdeffery for CONFIG_PM There is no need to have the ifdef CONFIG_PM to be nested. The second and so on will obviously become a no-op. Remove redundant nested ifdeffery. Signed-off-by: Andy Shevchenko Reviewed-by: Kuppuswamy Sathyanarayanan Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_lpss.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 8e01792228d1e1..a3d2d94be5c0d0 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -887,10 +887,8 @@ static int acpi_lpss_activate(struct device *dev) if (pdata->dev_desc->flags & (LPSS_SAVE_CTX | LPSS_SAVE_CTX_ONCE)) lpss_deassert_reset(pdata); -#ifdef CONFIG_PM if (pdata->dev_desc->flags & LPSS_SAVE_CTX_ONCE) acpi_lpss_save_ctx(dev, pdata); -#endif return 0; } -- cgit 1.2.3-korg From d85eb4152bce8ce1f0e4f4c5b4226142de6220bd Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 4 Apr 2024 21:23:39 +0300 Subject: ACPI: x86: Introduce a Makefile There will be more modules coming here, so, introduce a separate Makefile and include it in parent one via obj-$(CONFIG_X86). Signed-off-by: Andy Shevchenko Signed-off-by: Rafael J. Wysocki --- drivers/acpi/Makefile | 4 +--- drivers/acpi/x86/Makefile | 4 ++++ 2 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 drivers/acpi/x86/Makefile diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 8cc8c0d9c8732b..f84de92b07136c 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -56,9 +56,6 @@ acpi-y += evged.o acpi-y += sysfs.o acpi-y += property.o acpi-$(CONFIG_X86) += acpi_cmos_rtc.o -acpi-$(CONFIG_X86) += x86/apple.o -acpi-$(CONFIG_X86) += x86/utils.o -acpi-$(CONFIG_X86) += x86/s2idle.o acpi-$(CONFIG_DEBUG_FS) += debugfs.o acpi-y += acpi_lpat.o acpi-$(CONFIG_ACPI_FPDT) += acpi_fpdt.o @@ -132,3 +129,4 @@ obj-$(CONFIG_ARM64) += arm64/ obj-$(CONFIG_ACPI_VIOT) += viot.o obj-$(CONFIG_RISCV) += riscv/ +obj-$(CONFIG_X86) += x86/ diff --git a/drivers/acpi/x86/Makefile b/drivers/acpi/x86/Makefile new file mode 100644 index 00000000000000..bd17dd2c2c5bbf --- /dev/null +++ b/drivers/acpi/x86/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_ACPI) += acpi-x86.o +acpi-x86-y += apple.o +acpi-x86-y += s2idle.o +acpi-x86-y += utils.o -- cgit 1.2.3-korg From 49db108391e231ed11fec937b00a2a8c46522a11 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 4 Apr 2024 21:23:40 +0300 Subject: ACPI: x86: Move acpi_cmos_rtc to x86 folder acpi_cmos_rtc is built solely for x86, move it to the respective folder. Signed-off-by: Andy Shevchenko Signed-off-by: Rafael J. Wysocki --- drivers/acpi/Makefile | 1 - drivers/acpi/acpi_cmos_rtc.c | 98 -------------------------------------------- drivers/acpi/x86/Makefile | 1 + drivers/acpi/x86/cmos_rtc.c | 98 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 99 deletions(-) delete mode 100644 drivers/acpi/acpi_cmos_rtc.c create mode 100644 drivers/acpi/x86/cmos_rtc.c diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index f84de92b07136c..5e14aa11c2cbf9 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -55,7 +55,6 @@ acpi-y += event.o acpi-y += evged.o acpi-y += sysfs.o acpi-y += property.o -acpi-$(CONFIG_X86) += acpi_cmos_rtc.o acpi-$(CONFIG_DEBUG_FS) += debugfs.o acpi-y += acpi_lpat.o acpi-$(CONFIG_ACPI_FPDT) += acpi_fpdt.o diff --git a/drivers/acpi/acpi_cmos_rtc.c b/drivers/acpi/acpi_cmos_rtc.c deleted file mode 100644 index 9b55d1593d160c..00000000000000 --- a/drivers/acpi/acpi_cmos_rtc.c +++ /dev/null @@ -1,98 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * ACPI support for CMOS RTC Address Space access - * - * Copyright (C) 2013, Intel Corporation - * Authors: Lan Tianyu - */ - -#define pr_fmt(fmt) "ACPI: " fmt - -#include -#include -#include -#include -#include -#include - -#include "internal.h" - -static const struct acpi_device_id acpi_cmos_rtc_ids[] = { - { "PNP0B00" }, - { "PNP0B01" }, - { "PNP0B02" }, - {} -}; - -static acpi_status -acpi_cmos_rtc_space_handler(u32 function, acpi_physical_address address, - u32 bits, u64 *value64, - void *handler_context, void *region_context) -{ - int i; - u8 *value = (u8 *)value64; - - if (address > 0xff || !value64) - return AE_BAD_PARAMETER; - - if (function != ACPI_WRITE && function != ACPI_READ) - return AE_BAD_PARAMETER; - - spin_lock_irq(&rtc_lock); - - for (i = 0; i < DIV_ROUND_UP(bits, 8); ++i, ++address, ++value) - if (function == ACPI_READ) - *value = CMOS_READ(address); - else - CMOS_WRITE(*value, address); - - spin_unlock_irq(&rtc_lock); - - return AE_OK; -} - -int acpi_install_cmos_rtc_space_handler(acpi_handle handle) -{ - acpi_status status; - - status = acpi_install_address_space_handler(handle, - ACPI_ADR_SPACE_CMOS, - &acpi_cmos_rtc_space_handler, - NULL, NULL); - if (ACPI_FAILURE(status)) { - pr_err("Error installing CMOS-RTC region handler\n"); - return -ENODEV; - } - - return 1; -} -EXPORT_SYMBOL_GPL(acpi_install_cmos_rtc_space_handler); - -void acpi_remove_cmos_rtc_space_handler(acpi_handle handle) -{ - if (ACPI_FAILURE(acpi_remove_address_space_handler(handle, - ACPI_ADR_SPACE_CMOS, &acpi_cmos_rtc_space_handler))) - pr_err("Error removing CMOS-RTC region handler\n"); -} -EXPORT_SYMBOL_GPL(acpi_remove_cmos_rtc_space_handler); - -static int acpi_cmos_rtc_attach_handler(struct acpi_device *adev, const struct acpi_device_id *id) -{ - return acpi_install_cmos_rtc_space_handler(adev->handle); -} - -static void acpi_cmos_rtc_detach_handler(struct acpi_device *adev) -{ - acpi_remove_cmos_rtc_space_handler(adev->handle); -} - -static struct acpi_scan_handler cmos_rtc_handler = { - .ids = acpi_cmos_rtc_ids, - .attach = acpi_cmos_rtc_attach_handler, - .detach = acpi_cmos_rtc_detach_handler, -}; - -void __init acpi_cmos_rtc_init(void) -{ - acpi_scan_add_handler(&cmos_rtc_handler); -} diff --git a/drivers/acpi/x86/Makefile b/drivers/acpi/x86/Makefile index bd17dd2c2c5bbf..b97b1bcf840495 100644 --- a/drivers/acpi/x86/Makefile +++ b/drivers/acpi/x86/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_ACPI) += acpi-x86.o acpi-x86-y += apple.o +acpi-x86-y += cmos_rtc.o acpi-x86-y += s2idle.o acpi-x86-y += utils.o diff --git a/drivers/acpi/x86/cmos_rtc.c b/drivers/acpi/x86/cmos_rtc.c new file mode 100644 index 00000000000000..51643ff6fe5fc4 --- /dev/null +++ b/drivers/acpi/x86/cmos_rtc.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ACPI support for CMOS RTC Address Space access + * + * Copyright (C) 2013, Intel Corporation + * Authors: Lan Tianyu + */ + +#define pr_fmt(fmt) "ACPI: " fmt + +#include +#include +#include +#include +#include +#include + +#include "../internal.h" + +static const struct acpi_device_id acpi_cmos_rtc_ids[] = { + { "PNP0B00" }, + { "PNP0B01" }, + { "PNP0B02" }, + {} +}; + +static acpi_status +acpi_cmos_rtc_space_handler(u32 function, acpi_physical_address address, + u32 bits, u64 *value64, + void *handler_context, void *region_context) +{ + int i; + u8 *value = (u8 *)value64; + + if (address > 0xff || !value64) + return AE_BAD_PARAMETER; + + if (function != ACPI_WRITE && function != ACPI_READ) + return AE_BAD_PARAMETER; + + spin_lock_irq(&rtc_lock); + + for (i = 0; i < DIV_ROUND_UP(bits, 8); ++i, ++address, ++value) + if (function == ACPI_READ) + *value = CMOS_READ(address); + else + CMOS_WRITE(*value, address); + + spin_unlock_irq(&rtc_lock); + + return AE_OK; +} + +int acpi_install_cmos_rtc_space_handler(acpi_handle handle) +{ + acpi_status status; + + status = acpi_install_address_space_handler(handle, + ACPI_ADR_SPACE_CMOS, + &acpi_cmos_rtc_space_handler, + NULL, NULL); + if (ACPI_FAILURE(status)) { + pr_err("Error installing CMOS-RTC region handler\n"); + return -ENODEV; + } + + return 1; +} +EXPORT_SYMBOL_GPL(acpi_install_cmos_rtc_space_handler); + +void acpi_remove_cmos_rtc_space_handler(acpi_handle handle) +{ + if (ACPI_FAILURE(acpi_remove_address_space_handler(handle, + ACPI_ADR_SPACE_CMOS, &acpi_cmos_rtc_space_handler))) + pr_err("Error removing CMOS-RTC region handler\n"); +} +EXPORT_SYMBOL_GPL(acpi_remove_cmos_rtc_space_handler); + +static int acpi_cmos_rtc_attach_handler(struct acpi_device *adev, const struct acpi_device_id *id) +{ + return acpi_install_cmos_rtc_space_handler(adev->handle); +} + +static void acpi_cmos_rtc_detach_handler(struct acpi_device *adev) +{ + acpi_remove_cmos_rtc_space_handler(adev->handle); +} + +static struct acpi_scan_handler cmos_rtc_handler = { + .ids = acpi_cmos_rtc_ids, + .attach = acpi_cmos_rtc_attach_handler, + .detach = acpi_cmos_rtc_detach_handler, +}; + +void __init acpi_cmos_rtc_init(void) +{ + acpi_scan_add_handler(&cmos_rtc_handler); +} -- cgit 1.2.3-korg From 3d26b94fa11e5e7aa46aef5ee7748d5015900acd Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 4 Apr 2024 21:23:41 +0300 Subject: ACPI: x86: Move blacklist to x86 folder blacklist is built solely for x86, move it to the respective folder. Signed-off-by: Andy Shevchenko Signed-off-by: Rafael J. Wysocki --- drivers/acpi/Makefile | 1 - drivers/acpi/blacklist.c | 140 ------------------------------------------- drivers/acpi/x86/Makefile | 2 + drivers/acpi/x86/blacklist.c | 140 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 142 insertions(+), 141 deletions(-) delete mode 100644 drivers/acpi/blacklist.c create mode 100644 drivers/acpi/x86/blacklist.c diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 5e14aa11c2cbf9..0f73a6b953b330 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -14,7 +14,6 @@ tables.o: $(src)/../../include/$(CONFIG_ACPI_CUSTOM_DSDT_FILE) ; endif obj-$(CONFIG_ACPI) += tables.o -obj-$(CONFIG_X86) += blacklist.o # # ACPI Core Subsystem (Interpreter) diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c deleted file mode 100644 index a558d24fb7884c..00000000000000 --- a/drivers/acpi/blacklist.c +++ /dev/null @@ -1,140 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * blacklist.c - * - * Check to see if the given machine has a known bad ACPI BIOS - * or if the BIOS is too old. - * Check given machine against acpi_rev_dmi_table[]. - * - * Copyright (C) 2004 Len Brown - * Copyright (C) 2002 Andy Grover - */ - -#define pr_fmt(fmt) "ACPI: " fmt - -#include -#include -#include -#include - -#include "internal.h" - -#ifdef CONFIG_DMI -static const struct dmi_system_id acpi_rev_dmi_table[] __initconst; -#endif - -/* - * POLICY: If *anything* doesn't work, put it on the blacklist. - * If they are critical errors, mark it critical, and abort driver load. - */ -static struct acpi_platform_list acpi_blacklist[] __initdata = { - /* Compaq Presario 1700 */ - {"PTLTD ", " DSDT ", 0x06040000, ACPI_SIG_DSDT, less_than_or_equal, - "Multiple problems", 1}, - /* Sony FX120, FX140, FX150? */ - {"SONY ", "U0 ", 0x20010313, ACPI_SIG_DSDT, less_than_or_equal, - "ACPI driver problem", 1}, - /* Compaq Presario 800, Insyde BIOS */ - {"INT440", "SYSFexxx", 0x00001001, ACPI_SIG_DSDT, less_than_or_equal, - "Does not use _REG to protect EC OpRegions", 1}, - /* IBM 600E - _ADR should return 7, but it returns 1 */ - {"IBM ", "TP600E ", 0x00000105, ACPI_SIG_DSDT, less_than_or_equal, - "Incorrect _ADR", 1}, - - { } -}; - -int __init acpi_blacklisted(void) -{ - int i; - int blacklisted = 0; - - i = acpi_match_platform_list(acpi_blacklist); - if (i >= 0) { - pr_err("Vendor \"%6.6s\" System \"%8.8s\" Revision 0x%x has a known ACPI BIOS problem.\n", - acpi_blacklist[i].oem_id, - acpi_blacklist[i].oem_table_id, - acpi_blacklist[i].oem_revision); - - pr_err("Reason: %s. This is a %s error\n", - acpi_blacklist[i].reason, - (acpi_blacklist[i].data ? - "non-recoverable" : "recoverable")); - - blacklisted = acpi_blacklist[i].data; - } - - (void)early_acpi_osi_init(); -#ifdef CONFIG_DMI - dmi_check_system(acpi_rev_dmi_table); -#endif - - return blacklisted; -} -#ifdef CONFIG_DMI -#ifdef CONFIG_ACPI_REV_OVERRIDE_POSSIBLE -static int __init dmi_enable_rev_override(const struct dmi_system_id *d) -{ - pr_notice("DMI detected: %s (force ACPI _REV to 5)\n", d->ident); - acpi_rev_override_setup(NULL); - return 0; -} -#endif - -static const struct dmi_system_id acpi_rev_dmi_table[] __initconst = { -#ifdef CONFIG_ACPI_REV_OVERRIDE_POSSIBLE - /* - * DELL XPS 13 (2015) switches sound between HDA and I2S - * depending on the ACPI _REV callback. If userspace supports - * I2S sufficiently (or if you do not care about sound), you - * can safely disable this quirk. - */ - { - .callback = dmi_enable_rev_override, - .ident = "DELL XPS 13 (2015)", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "XPS 13 9343"), - }, - }, - { - .callback = dmi_enable_rev_override, - .ident = "DELL Precision 5520", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Precision 5520"), - }, - }, - { - .callback = dmi_enable_rev_override, - .ident = "DELL Precision 3520", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Precision 3520"), - }, - }, - /* - * Resolves a quirk with the Dell Latitude 3350 that - * causes the ethernet adapter to not function. - */ - { - .callback = dmi_enable_rev_override, - .ident = "DELL Latitude 3350", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Latitude 3350"), - }, - }, - { - .callback = dmi_enable_rev_override, - .ident = "DELL Inspiron 7537", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7537"), - }, - }, -#endif - {} -}; - -#endif /* CONFIG_DMI */ diff --git a/drivers/acpi/x86/Makefile b/drivers/acpi/x86/Makefile index b97b1bcf840495..1f3c5fa84f9e1a 100644 --- a/drivers/acpi/x86/Makefile +++ b/drivers/acpi/x86/Makefile @@ -3,3 +3,5 @@ acpi-x86-y += apple.o acpi-x86-y += cmos_rtc.o acpi-x86-y += s2idle.o acpi-x86-y += utils.o + +obj-$(CONFIG_X86) += blacklist.o diff --git a/drivers/acpi/x86/blacklist.c b/drivers/acpi/x86/blacklist.c new file mode 100644 index 00000000000000..55214d0a12b1d0 --- /dev/null +++ b/drivers/acpi/x86/blacklist.c @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * blacklist.c + * + * Check to see if the given machine has a known bad ACPI BIOS + * or if the BIOS is too old. + * Check given machine against acpi_rev_dmi_table[]. + * + * Copyright (C) 2004 Len Brown + * Copyright (C) 2002 Andy Grover + */ + +#define pr_fmt(fmt) "ACPI: " fmt + +#include +#include +#include +#include + +#include "../internal.h" + +#ifdef CONFIG_DMI +static const struct dmi_system_id acpi_rev_dmi_table[] __initconst; +#endif + +/* + * POLICY: If *anything* doesn't work, put it on the blacklist. + * If they are critical errors, mark it critical, and abort driver load. + */ +static struct acpi_platform_list acpi_blacklist[] __initdata = { + /* Compaq Presario 1700 */ + {"PTLTD ", " DSDT ", 0x06040000, ACPI_SIG_DSDT, less_than_or_equal, + "Multiple problems", 1}, + /* Sony FX120, FX140, FX150? */ + {"SONY ", "U0 ", 0x20010313, ACPI_SIG_DSDT, less_than_or_equal, + "ACPI driver problem", 1}, + /* Compaq Presario 800, Insyde BIOS */ + {"INT440", "SYSFexxx", 0x00001001, ACPI_SIG_DSDT, less_than_or_equal, + "Does not use _REG to protect EC OpRegions", 1}, + /* IBM 600E - _ADR should return 7, but it returns 1 */ + {"IBM ", "TP600E ", 0x00000105, ACPI_SIG_DSDT, less_than_or_equal, + "Incorrect _ADR", 1}, + + { } +}; + +int __init acpi_blacklisted(void) +{ + int i; + int blacklisted = 0; + + i = acpi_match_platform_list(acpi_blacklist); + if (i >= 0) { + pr_err("Vendor \"%6.6s\" System \"%8.8s\" Revision 0x%x has a known ACPI BIOS problem.\n", + acpi_blacklist[i].oem_id, + acpi_blacklist[i].oem_table_id, + acpi_blacklist[i].oem_revision); + + pr_err("Reason: %s. This is a %s error\n", + acpi_blacklist[i].reason, + (acpi_blacklist[i].data ? + "non-recoverable" : "recoverable")); + + blacklisted = acpi_blacklist[i].data; + } + + (void)early_acpi_osi_init(); +#ifdef CONFIG_DMI + dmi_check_system(acpi_rev_dmi_table); +#endif + + return blacklisted; +} +#ifdef CONFIG_DMI +#ifdef CONFIG_ACPI_REV_OVERRIDE_POSSIBLE +static int __init dmi_enable_rev_override(const struct dmi_system_id *d) +{ + pr_notice("DMI detected: %s (force ACPI _REV to 5)\n", d->ident); + acpi_rev_override_setup(NULL); + return 0; +} +#endif + +static const struct dmi_system_id acpi_rev_dmi_table[] __initconst = { +#ifdef CONFIG_ACPI_REV_OVERRIDE_POSSIBLE + /* + * DELL XPS 13 (2015) switches sound between HDA and I2S + * depending on the ACPI _REV callback. If userspace supports + * I2S sufficiently (or if you do not care about sound), you + * can safely disable this quirk. + */ + { + .callback = dmi_enable_rev_override, + .ident = "DELL XPS 13 (2015)", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "XPS 13 9343"), + }, + }, + { + .callback = dmi_enable_rev_override, + .ident = "DELL Precision 5520", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Precision 5520"), + }, + }, + { + .callback = dmi_enable_rev_override, + .ident = "DELL Precision 3520", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Precision 3520"), + }, + }, + /* + * Resolves a quirk with the Dell Latitude 3350 that + * causes the ethernet adapter to not function. + */ + { + .callback = dmi_enable_rev_override, + .ident = "DELL Latitude 3350", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Latitude 3350"), + }, + }, + { + .callback = dmi_enable_rev_override, + .ident = "DELL Inspiron 7537", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7537"), + }, + }, +#endif + {} +}; + +#endif /* CONFIG_DMI */ -- cgit 1.2.3-korg From 2d5d5abebf1a342d672f738b60ea930ebc1070b0 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 4 Apr 2024 21:23:42 +0300 Subject: ACPI: x86: Move LPSS to x86 folder LPSS is built solely for x86, move it to the respective folder. Signed-off-by: Andy Shevchenko Signed-off-by: Rafael J. Wysocki --- drivers/acpi/Makefile | 1 - drivers/acpi/acpi_lpss.c | 1355 --------------------------------------------- drivers/acpi/internal.h | 3 +- drivers/acpi/x86/Makefile | 1 + drivers/acpi/x86/lpss.c | 1355 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1358 insertions(+), 1357 deletions(-) delete mode 100644 drivers/acpi/acpi_lpss.c create mode 100644 drivers/acpi/x86/lpss.c diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 0f73a6b953b330..2046f108a91ac7 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -45,7 +45,6 @@ acpi-y += ec.o acpi-$(CONFIG_ACPI_DOCK) += dock.o acpi-$(CONFIG_PCI) += pci_root.o pci_link.o pci_irq.o obj-$(CONFIG_ACPI_MCFG) += pci_mcfg.o -acpi-$(CONFIG_PCI) += acpi_lpss.o acpi-y += acpi_apd.o acpi-y += acpi_platform.o acpi-y += acpi_pnp.o diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c deleted file mode 100644 index a3d2d94be5c0d0..00000000000000 --- a/drivers/acpi/acpi_lpss.c +++ /dev/null @@ -1,1355 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * ACPI support for Intel Lynxpoint LPSS. - * - * Copyright (C) 2013, Intel Corporation - * Authors: Mika Westerberg - * Rafael J. Wysocki - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "internal.h" - -#ifdef CONFIG_X86_INTEL_LPSS - -#include -#include -#include - -#define LPSS_ADDR(desc) ((unsigned long)&desc) - -#define LPSS_CLK_SIZE 0x04 -#define LPSS_LTR_SIZE 0x18 - -/* Offsets relative to LPSS_PRIVATE_OFFSET */ -#define LPSS_CLK_DIVIDER_DEF_MASK (BIT(1) | BIT(16)) -#define LPSS_RESETS 0x04 -#define LPSS_RESETS_RESET_FUNC BIT(0) -#define LPSS_RESETS_RESET_APB BIT(1) -#define LPSS_GENERAL 0x08 -#define LPSS_GENERAL_LTR_MODE_SW BIT(2) -#define LPSS_GENERAL_UART_RTS_OVRD BIT(3) -#define LPSS_SW_LTR 0x10 -#define LPSS_AUTO_LTR 0x14 -#define LPSS_LTR_SNOOP_REQ BIT(15) -#define LPSS_LTR_SNOOP_MASK 0x0000FFFF -#define LPSS_LTR_SNOOP_LAT_1US 0x800 -#define LPSS_LTR_SNOOP_LAT_32US 0xC00 -#define LPSS_LTR_SNOOP_LAT_SHIFT 5 -#define LPSS_LTR_SNOOP_LAT_CUTOFF 3000 -#define LPSS_LTR_MAX_VAL 0x3FF -#define LPSS_TX_INT 0x20 -#define LPSS_TX_INT_MASK BIT(1) - -#define LPSS_PRV_REG_COUNT 9 - -/* LPSS Flags */ -#define LPSS_CLK BIT(0) -#define LPSS_CLK_GATE BIT(1) -#define LPSS_CLK_DIVIDER BIT(2) -#define LPSS_LTR BIT(3) -#define LPSS_SAVE_CTX BIT(4) -/* - * For some devices the DSDT AML code for another device turns off the device - * before our suspend handler runs, causing us to read/save all 1-s (0xffffffff) - * as ctx register values. - * Luckily these devices always use the same ctx register values, so we can - * work around this by saving the ctx registers once on activation. - */ -#define LPSS_SAVE_CTX_ONCE BIT(5) -#define LPSS_NO_D3_DELAY BIT(6) - -struct lpss_private_data; - -struct lpss_device_desc { - unsigned int flags; - const char *clk_con_id; - unsigned int prv_offset; - size_t prv_size_override; - const struct property_entry *properties; - void (*setup)(struct lpss_private_data *pdata); - bool resume_from_noirq; -}; - -static const struct lpss_device_desc lpss_dma_desc = { - .flags = LPSS_CLK, -}; - -struct lpss_private_data { - struct acpi_device *adev; - void __iomem *mmio_base; - resource_size_t mmio_size; - unsigned int fixed_clk_rate; - struct clk *clk; - const struct lpss_device_desc *dev_desc; - u32 prv_reg_ctx[LPSS_PRV_REG_COUNT]; -}; - -/* Devices which need to be in D3 before lpss_iosf_enter_d3_state() proceeds */ -static u32 pmc_atom_d3_mask = 0xfe000ffe; - -/* LPSS run time quirks */ -static unsigned int lpss_quirks; - -/* - * LPSS_QUIRK_ALWAYS_POWER_ON: override power state for LPSS DMA device. - * - * The LPSS DMA controller has neither _PS0 nor _PS3 method. Moreover - * it can be powered off automatically whenever the last LPSS device goes down. - * In case of no power any access to the DMA controller will hang the system. - * The behaviour is reproduced on some HP laptops based on Intel BayTrail as - * well as on ASuS T100TA transformer. - * - * This quirk overrides power state of entire LPSS island to keep DMA powered - * on whenever we have at least one other device in use. - */ -#define LPSS_QUIRK_ALWAYS_POWER_ON BIT(0) - -/* UART Component Parameter Register */ -#define LPSS_UART_CPR 0xF4 -#define LPSS_UART_CPR_AFCE BIT(4) - -static void lpss_uart_setup(struct lpss_private_data *pdata) -{ - unsigned int offset; - u32 val; - - offset = pdata->dev_desc->prv_offset + LPSS_TX_INT; - val = readl(pdata->mmio_base + offset); - writel(val | LPSS_TX_INT_MASK, pdata->mmio_base + offset); - - val = readl(pdata->mmio_base + LPSS_UART_CPR); - if (!(val & LPSS_UART_CPR_AFCE)) { - offset = pdata->dev_desc->prv_offset + LPSS_GENERAL; - val = readl(pdata->mmio_base + offset); - val |= LPSS_GENERAL_UART_RTS_OVRD; - writel(val, pdata->mmio_base + offset); - } -} - -static void lpss_deassert_reset(struct lpss_private_data *pdata) -{ - unsigned int offset; - u32 val; - - offset = pdata->dev_desc->prv_offset + LPSS_RESETS; - val = readl(pdata->mmio_base + offset); - val |= LPSS_RESETS_RESET_APB | LPSS_RESETS_RESET_FUNC; - writel(val, pdata->mmio_base + offset); -} - -/* - * BYT PWM used for backlight control by the i915 driver on systems without - * the Crystal Cove PMIC. - */ -static struct pwm_lookup byt_pwm_lookup[] = { - PWM_LOOKUP_WITH_MODULE("80860F09:00", 0, "0000:00:02.0", - "pwm_soc_backlight", 0, PWM_POLARITY_NORMAL, - "pwm-lpss-platform"), -}; - -static void byt_pwm_setup(struct lpss_private_data *pdata) -{ - /* Only call pwm_add_table for the first PWM controller */ - if (acpi_dev_uid_match(pdata->adev, 1)) - pwm_add_table(byt_pwm_lookup, ARRAY_SIZE(byt_pwm_lookup)); -} - -#define LPSS_I2C_ENABLE 0x6c - -static void byt_i2c_setup(struct lpss_private_data *pdata) -{ - acpi_handle handle = pdata->adev->handle; - unsigned long long shared_host = 0; - acpi_status status; - u64 uid; - - /* Expected to always be successfull, but better safe then sorry */ - if (!acpi_dev_uid_to_integer(pdata->adev, &uid) && uid) { - /* Detect I2C bus shared with PUNIT and ignore its d3 status */ - status = acpi_evaluate_integer(handle, "_SEM", NULL, &shared_host); - if (ACPI_SUCCESS(status) && shared_host) - pmc_atom_d3_mask &= ~(BIT_LPSS2_F1_I2C1 << (uid - 1)); - } - - lpss_deassert_reset(pdata); - - if (readl(pdata->mmio_base + pdata->dev_desc->prv_offset)) - pdata->fixed_clk_rate = 133000000; - - writel(0, pdata->mmio_base + LPSS_I2C_ENABLE); -} - -/* - * BSW PWM1 is used for backlight control by the i915 driver - * BSW PWM2 is used for backlight control for fixed (etched into the glass) - * touch controls on some models. These touch-controls have specialized - * drivers which know they need the "pwm_soc_lpss_2" con-id. - */ -static struct pwm_lookup bsw_pwm_lookup[] = { - PWM_LOOKUP_WITH_MODULE("80862288:00", 0, "0000:00:02.0", - "pwm_soc_backlight", 0, PWM_POLARITY_NORMAL, - "pwm-lpss-platform"), - PWM_LOOKUP_WITH_MODULE("80862289:00", 0, NULL, - "pwm_soc_lpss_2", 0, PWM_POLARITY_NORMAL, - "pwm-lpss-platform"), -}; - -static void bsw_pwm_setup(struct lpss_private_data *pdata) -{ - /* Only call pwm_add_table for the first PWM controller */ - if (acpi_dev_uid_match(pdata->adev, 1)) - pwm_add_table(bsw_pwm_lookup, ARRAY_SIZE(bsw_pwm_lookup)); -} - -static const struct property_entry lpt_spi_properties[] = { - PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_LPT_SSP), - { } -}; - -static const struct lpss_device_desc lpt_spi_dev_desc = { - .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR - | LPSS_SAVE_CTX, - .prv_offset = 0x800, - .properties = lpt_spi_properties, -}; - -static const struct lpss_device_desc lpt_i2c_dev_desc = { - .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_LTR | LPSS_SAVE_CTX, - .prv_offset = 0x800, -}; - -static struct property_entry uart_properties[] = { - PROPERTY_ENTRY_U32("reg-io-width", 4), - PROPERTY_ENTRY_U32("reg-shift", 2), - PROPERTY_ENTRY_BOOL("snps,uart-16550-compatible"), - { }, -}; - -static const struct lpss_device_desc lpt_uart_dev_desc = { - .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR - | LPSS_SAVE_CTX, - .clk_con_id = "baudclk", - .prv_offset = 0x800, - .setup = lpss_uart_setup, - .properties = uart_properties, -}; - -static const struct lpss_device_desc lpt_sdio_dev_desc = { - .flags = LPSS_LTR, - .prv_offset = 0x1000, - .prv_size_override = 0x1018, -}; - -static const struct lpss_device_desc byt_pwm_dev_desc = { - .flags = LPSS_SAVE_CTX, - .prv_offset = 0x800, - .setup = byt_pwm_setup, -}; - -static const struct lpss_device_desc bsw_pwm_dev_desc = { - .flags = LPSS_SAVE_CTX_ONCE | LPSS_NO_D3_DELAY, - .prv_offset = 0x800, - .setup = bsw_pwm_setup, - .resume_from_noirq = true, -}; - -static const struct lpss_device_desc bsw_pwm2_dev_desc = { - .flags = LPSS_SAVE_CTX_ONCE | LPSS_NO_D3_DELAY, - .prv_offset = 0x800, - .resume_from_noirq = true, -}; - -static const struct lpss_device_desc byt_uart_dev_desc = { - .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX, - .clk_con_id = "baudclk", - .prv_offset = 0x800, - .setup = lpss_uart_setup, - .properties = uart_properties, -}; - -static const struct lpss_device_desc bsw_uart_dev_desc = { - .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX - | LPSS_NO_D3_DELAY, - .clk_con_id = "baudclk", - .prv_offset = 0x800, - .setup = lpss_uart_setup, - .properties = uart_properties, -}; - -static const struct property_entry byt_spi_properties[] = { - PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_BYT_SSP), - { } -}; - -static const struct lpss_device_desc byt_spi_dev_desc = { - .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX, - .prv_offset = 0x400, - .properties = byt_spi_properties, -}; - -static const struct lpss_device_desc byt_sdio_dev_desc = { - .flags = LPSS_CLK, -}; - -static const struct lpss_device_desc byt_i2c_dev_desc = { - .flags = LPSS_CLK | LPSS_SAVE_CTX, - .prv_offset = 0x800, - .setup = byt_i2c_setup, - .resume_from_noirq = true, -}; - -static const struct lpss_device_desc bsw_i2c_dev_desc = { - .flags = LPSS_CLK | LPSS_SAVE_CTX | LPSS_NO_D3_DELAY, - .prv_offset = 0x800, - .setup = byt_i2c_setup, - .resume_from_noirq = true, -}; - -static const struct property_entry bsw_spi_properties[] = { - PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_BSW_SSP), - PROPERTY_ENTRY_U32("num-cs", 2), - { } -}; - -static const struct lpss_device_desc bsw_spi_dev_desc = { - .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX - | LPSS_NO_D3_DELAY, - .prv_offset = 0x400, - .setup = lpss_deassert_reset, - .properties = bsw_spi_properties, -}; - -static const struct x86_cpu_id lpss_cpu_ids[] = { - X86_MATCH_INTEL_FAM6_MODEL(ATOM_SILVERMONT, NULL), - X86_MATCH_INTEL_FAM6_MODEL(ATOM_AIRMONT, NULL), - {} -}; - -#else - -#define LPSS_ADDR(desc) (0UL) - -#endif /* CONFIG_X86_INTEL_LPSS */ - -static const struct acpi_device_id acpi_lpss_device_ids[] = { - /* Generic LPSS devices */ - { "INTL9C60", LPSS_ADDR(lpss_dma_desc) }, - - /* Lynxpoint LPSS devices */ - { "INT33C0", LPSS_ADDR(lpt_spi_dev_desc) }, - { "INT33C1", LPSS_ADDR(lpt_spi_dev_desc) }, - { "INT33C2", LPSS_ADDR(lpt_i2c_dev_desc) }, - { "INT33C3", LPSS_ADDR(lpt_i2c_dev_desc) }, - { "INT33C4", LPSS_ADDR(lpt_uart_dev_desc) }, - { "INT33C5", LPSS_ADDR(lpt_uart_dev_desc) }, - { "INT33C6", LPSS_ADDR(lpt_sdio_dev_desc) }, - - /* BayTrail LPSS devices */ - { "80860F09", LPSS_ADDR(byt_pwm_dev_desc) }, - { "80860F0A", LPSS_ADDR(byt_uart_dev_desc) }, - { "80860F0E", LPSS_ADDR(byt_spi_dev_desc) }, - { "80860F14", LPSS_ADDR(byt_sdio_dev_desc) }, - { "80860F41", LPSS_ADDR(byt_i2c_dev_desc) }, - - /* Braswell LPSS devices */ - { "80862286", LPSS_ADDR(lpss_dma_desc) }, - { "80862288", LPSS_ADDR(bsw_pwm_dev_desc) }, - { "80862289", LPSS_ADDR(bsw_pwm2_dev_desc) }, - { "8086228A", LPSS_ADDR(bsw_uart_dev_desc) }, - { "8086228E", LPSS_ADDR(bsw_spi_dev_desc) }, - { "808622C0", LPSS_ADDR(lpss_dma_desc) }, - { "808622C1", LPSS_ADDR(bsw_i2c_dev_desc) }, - - /* Broadwell LPSS devices */ - { "INT3430", LPSS_ADDR(lpt_spi_dev_desc) }, - { "INT3431", LPSS_ADDR(lpt_spi_dev_desc) }, - { "INT3432", LPSS_ADDR(lpt_i2c_dev_desc) }, - { "INT3433", LPSS_ADDR(lpt_i2c_dev_desc) }, - { "INT3434", LPSS_ADDR(lpt_uart_dev_desc) }, - { "INT3435", LPSS_ADDR(lpt_uart_dev_desc) }, - { "INT3436", LPSS_ADDR(lpt_sdio_dev_desc) }, - - /* Wildcat Point LPSS devices */ - { "INT3438", LPSS_ADDR(lpt_spi_dev_desc) }, - - { } -}; - -#ifdef CONFIG_X86_INTEL_LPSS - -/* LPSS main clock device. */ -static struct platform_device *lpss_clk_dev; - -static inline void lpt_register_clock_device(void) -{ - lpss_clk_dev = platform_device_register_simple("clk-lpss-atom", - PLATFORM_DEVID_NONE, - NULL, 0); -} - -static int register_device_clock(struct acpi_device *adev, - struct lpss_private_data *pdata) -{ - const struct lpss_device_desc *dev_desc = pdata->dev_desc; - const char *devname = dev_name(&adev->dev); - struct clk *clk; - struct lpss_clk_data *clk_data; - const char *parent, *clk_name; - void __iomem *prv_base; - - if (!lpss_clk_dev) - lpt_register_clock_device(); - - if (IS_ERR(lpss_clk_dev)) - return PTR_ERR(lpss_clk_dev); - - clk_data = platform_get_drvdata(lpss_clk_dev); - if (!clk_data) - return -ENODEV; - clk = clk_data->clk; - - if (!pdata->mmio_base - || pdata->mmio_size < dev_desc->prv_offset + LPSS_CLK_SIZE) - return -ENODATA; - - parent = clk_data->name; - prv_base = pdata->mmio_base + dev_desc->prv_offset; - - if (pdata->fixed_clk_rate) { - clk = clk_register_fixed_rate(NULL, devname, parent, 0, - pdata->fixed_clk_rate); - goto out; - } - - if (dev_desc->flags & LPSS_CLK_GATE) { - clk = clk_register_gate(NULL, devname, parent, 0, - prv_base, 0, 0, NULL); - parent = devname; - } - - if (dev_desc->flags & LPSS_CLK_DIVIDER) { - /* Prevent division by zero */ - if (!readl(prv_base)) - writel(LPSS_CLK_DIVIDER_DEF_MASK, prv_base); - - clk_name = kasprintf(GFP_KERNEL, "%s-div", devname); - if (!clk_name) - return -ENOMEM; - clk = clk_register_fractional_divider(NULL, clk_name, parent, - 0, prv_base, 1, 15, 16, 15, - CLK_FRAC_DIVIDER_POWER_OF_TWO_PS, - NULL); - parent = clk_name; - - clk_name = kasprintf(GFP_KERNEL, "%s-update", devname); - if (!clk_name) { - kfree(parent); - return -ENOMEM; - } - clk = clk_register_gate(NULL, clk_name, parent, - CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, - prv_base, 31, 0, NULL); - kfree(parent); - kfree(clk_name); - } -out: - if (IS_ERR(clk)) - return PTR_ERR(clk); - - pdata->clk = clk; - clk_register_clkdev(clk, dev_desc->clk_con_id, devname); - return 0; -} - -struct lpss_device_links { - const char *supplier_hid; - const char *supplier_uid; - const char *consumer_hid; - const char *consumer_uid; - u32 flags; - const struct dmi_system_id *dep_missing_ids; -}; - -/* Please keep this list sorted alphabetically by vendor and model */ -static const struct dmi_system_id i2c1_dep_missing_dmi_ids[] = { - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "T200TA"), - }, - }, - {} -}; - -/* - * The _DEP method is used to identify dependencies but instead of creating - * device links for every handle in _DEP, only links in the following list are - * created. That is necessary because, in the general case, _DEP can refer to - * devices that might not have drivers, or that are on different buses, or where - * the supplier is not enumerated until after the consumer is probed. - */ -static const struct lpss_device_links lpss_device_links[] = { - /* CHT External sdcard slot controller depends on PMIC I2C ctrl */ - {"808622C1", "7", "80860F14", "3", DL_FLAG_PM_RUNTIME}, - /* CHT iGPU depends on PMIC I2C controller */ - {"808622C1", "7", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME}, - /* BYT iGPU depends on the Embedded Controller I2C controller (UID 1) */ - {"80860F41", "1", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME, - i2c1_dep_missing_dmi_ids}, - /* BYT CR iGPU depends on PMIC I2C controller (UID 5 on CR) */ - {"80860F41", "5", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME}, - /* BYT iGPU depends on PMIC I2C controller (UID 7 on non CR) */ - {"80860F41", "7", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME}, -}; - -static bool acpi_lpss_is_supplier(struct acpi_device *adev, - const struct lpss_device_links *link) -{ - return acpi_dev_hid_uid_match(adev, link->supplier_hid, link->supplier_uid); -} - -static bool acpi_lpss_is_consumer(struct acpi_device *adev, - const struct lpss_device_links *link) -{ - return acpi_dev_hid_uid_match(adev, link->consumer_hid, link->consumer_uid); -} - -struct hid_uid { - const char *hid; - const char *uid; -}; - -static int match_hid_uid(struct device *dev, const void *data) -{ - struct acpi_device *adev = ACPI_COMPANION(dev); - const struct hid_uid *id = data; - - if (!adev) - return 0; - - return acpi_dev_hid_uid_match(adev, id->hid, id->uid); -} - -static struct device *acpi_lpss_find_device(const char *hid, const char *uid) -{ - struct device *dev; - - struct hid_uid data = { - .hid = hid, - .uid = uid, - }; - - dev = bus_find_device(&platform_bus_type, NULL, &data, match_hid_uid); - if (dev) - return dev; - - return bus_find_device(&pci_bus_type, NULL, &data, match_hid_uid); -} - -static void acpi_lpss_link_consumer(struct device *dev1, - const struct lpss_device_links *link) -{ - struct device *dev2; - - dev2 = acpi_lpss_find_device(link->consumer_hid, link->consumer_uid); - if (!dev2) - return; - - if ((link->dep_missing_ids && dmi_check_system(link->dep_missing_ids)) - || acpi_device_dep(ACPI_HANDLE(dev2), ACPI_HANDLE(dev1))) - device_link_add(dev2, dev1, link->flags); - - put_device(dev2); -} - -static void acpi_lpss_link_supplier(struct device *dev1, - const struct lpss_device_links *link) -{ - struct device *dev2; - - dev2 = acpi_lpss_find_device(link->supplier_hid, link->supplier_uid); - if (!dev2) - return; - - if ((link->dep_missing_ids && dmi_check_system(link->dep_missing_ids)) - || acpi_device_dep(ACPI_HANDLE(dev1), ACPI_HANDLE(dev2))) - device_link_add(dev1, dev2, link->flags); - - put_device(dev2); -} - -static void acpi_lpss_create_device_links(struct acpi_device *adev, - struct platform_device *pdev) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(lpss_device_links); i++) { - const struct lpss_device_links *link = &lpss_device_links[i]; - - if (acpi_lpss_is_supplier(adev, link)) - acpi_lpss_link_consumer(&pdev->dev, link); - - if (acpi_lpss_is_consumer(adev, link)) - acpi_lpss_link_supplier(&pdev->dev, link); - } -} - -static int acpi_lpss_create_device(struct acpi_device *adev, - const struct acpi_device_id *id) -{ - const struct lpss_device_desc *dev_desc; - struct lpss_private_data *pdata; - struct resource_entry *rentry; - struct list_head resource_list; - struct platform_device *pdev; - int ret; - - dev_desc = (const struct lpss_device_desc *)id->driver_data; - if (!dev_desc) - return -EINVAL; - - pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - INIT_LIST_HEAD(&resource_list); - ret = acpi_dev_get_memory_resources(adev, &resource_list); - if (ret < 0) - goto err_out; - - rentry = list_first_entry_or_null(&resource_list, struct resource_entry, node); - if (rentry) { - if (dev_desc->prv_size_override) - pdata->mmio_size = dev_desc->prv_size_override; - else - pdata->mmio_size = resource_size(rentry->res); - pdata->mmio_base = ioremap(rentry->res->start, pdata->mmio_size); - } - - acpi_dev_free_resource_list(&resource_list); - - if (!pdata->mmio_base) { - /* Avoid acpi_bus_attach() instantiating a pdev for this dev. */ - adev->pnp.type.platform_id = 0; - goto out_free; - } - - pdata->adev = adev; - pdata->dev_desc = dev_desc; - - if (dev_desc->setup) - dev_desc->setup(pdata); - - if (dev_desc->flags & LPSS_CLK) { - ret = register_device_clock(adev, pdata); - if (ret) - goto out_free; - } - - /* - * This works around a known issue in ACPI tables where LPSS devices - * have _PS0 and _PS3 without _PSC (and no power resources), so - * acpi_bus_init_power() will assume that the BIOS has put them into D0. - */ - acpi_device_fix_up_power(adev); - - adev->driver_data = pdata; - pdev = acpi_create_platform_device(adev, dev_desc->properties); - if (IS_ERR_OR_NULL(pdev)) { - adev->driver_data = NULL; - ret = PTR_ERR(pdev); - goto err_out; - } - - acpi_lpss_create_device_links(adev, pdev); - return 1; - -out_free: - /* Skip the device, but continue the namespace scan */ - ret = 0; -err_out: - kfree(pdata); - return ret; -} - -static u32 __lpss_reg_read(struct lpss_private_data *pdata, unsigned int reg) -{ - return readl(pdata->mmio_base + pdata->dev_desc->prv_offset + reg); -} - -static void __lpss_reg_write(u32 val, struct lpss_private_data *pdata, - unsigned int reg) -{ - writel(val, pdata->mmio_base + pdata->dev_desc->prv_offset + reg); -} - -static int lpss_reg_read(struct device *dev, unsigned int reg, u32 *val) -{ - struct acpi_device *adev = ACPI_COMPANION(dev); - struct lpss_private_data *pdata; - unsigned long flags; - int ret; - - if (WARN_ON(!adev)) - return -ENODEV; - - spin_lock_irqsave(&dev->power.lock, flags); - if (pm_runtime_suspended(dev)) { - ret = -EAGAIN; - goto out; - } - pdata = acpi_driver_data(adev); - if (WARN_ON(!pdata || !pdata->mmio_base)) { - ret = -ENODEV; - goto out; - } - *val = __lpss_reg_read(pdata, reg); - ret = 0; - - out: - spin_unlock_irqrestore(&dev->power.lock, flags); - return ret; -} - -static ssize_t lpss_ltr_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - u32 ltr_value = 0; - unsigned int reg; - int ret; - - reg = strcmp(attr->attr.name, "auto_ltr") ? LPSS_SW_LTR : LPSS_AUTO_LTR; - ret = lpss_reg_read(dev, reg, <r_value); - if (ret) - return ret; - - return sysfs_emit(buf, "%08x\n", ltr_value); -} - -static ssize_t lpss_ltr_mode_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u32 ltr_mode = 0; - char *outstr; - int ret; - - ret = lpss_reg_read(dev, LPSS_GENERAL, <r_mode); - if (ret) - return ret; - - outstr = (ltr_mode & LPSS_GENERAL_LTR_MODE_SW) ? "sw" : "auto"; - return sprintf(buf, "%s\n", outstr); -} - -static DEVICE_ATTR(auto_ltr, S_IRUSR, lpss_ltr_show, NULL); -static DEVICE_ATTR(sw_ltr, S_IRUSR, lpss_ltr_show, NULL); -static DEVICE_ATTR(ltr_mode, S_IRUSR, lpss_ltr_mode_show, NULL); - -static struct attribute *lpss_attrs[] = { - &dev_attr_auto_ltr.attr, - &dev_attr_sw_ltr.attr, - &dev_attr_ltr_mode.attr, - NULL, -}; - -static const struct attribute_group lpss_attr_group = { - .attrs = lpss_attrs, - .name = "lpss_ltr", -}; - -static void acpi_lpss_set_ltr(struct device *dev, s32 val) -{ - struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); - u32 ltr_mode, ltr_val; - - ltr_mode = __lpss_reg_read(pdata, LPSS_GENERAL); - if (val < 0) { - if (ltr_mode & LPSS_GENERAL_LTR_MODE_SW) { - ltr_mode &= ~LPSS_GENERAL_LTR_MODE_SW; - __lpss_reg_write(ltr_mode, pdata, LPSS_GENERAL); - } - return; - } - ltr_val = __lpss_reg_read(pdata, LPSS_SW_LTR) & ~LPSS_LTR_SNOOP_MASK; - if (val >= LPSS_LTR_SNOOP_LAT_CUTOFF) { - ltr_val |= LPSS_LTR_SNOOP_LAT_32US; - val = LPSS_LTR_MAX_VAL; - } else if (val > LPSS_LTR_MAX_VAL) { - ltr_val |= LPSS_LTR_SNOOP_LAT_32US | LPSS_LTR_SNOOP_REQ; - val >>= LPSS_LTR_SNOOP_LAT_SHIFT; - } else { - ltr_val |= LPSS_LTR_SNOOP_LAT_1US | LPSS_LTR_SNOOP_REQ; - } - ltr_val |= val; - __lpss_reg_write(ltr_val, pdata, LPSS_SW_LTR); - if (!(ltr_mode & LPSS_GENERAL_LTR_MODE_SW)) { - ltr_mode |= LPSS_GENERAL_LTR_MODE_SW; - __lpss_reg_write(ltr_mode, pdata, LPSS_GENERAL); - } -} - -#ifdef CONFIG_PM -/** - * acpi_lpss_save_ctx() - Save the private registers of LPSS device - * @dev: LPSS device - * @pdata: pointer to the private data of the LPSS device - * - * Most LPSS devices have private registers which may loose their context when - * the device is powered down. acpi_lpss_save_ctx() saves those registers into - * prv_reg_ctx array. - */ -static void acpi_lpss_save_ctx(struct device *dev, - struct lpss_private_data *pdata) -{ - unsigned int i; - - for (i = 0; i < LPSS_PRV_REG_COUNT; i++) { - unsigned long offset = i * sizeof(u32); - - pdata->prv_reg_ctx[i] = __lpss_reg_read(pdata, offset); - dev_dbg(dev, "saving 0x%08x from LPSS reg at offset 0x%02lx\n", - pdata->prv_reg_ctx[i], offset); - } -} - -/** - * acpi_lpss_restore_ctx() - Restore the private registers of LPSS device - * @dev: LPSS device - * @pdata: pointer to the private data of the LPSS device - * - * Restores the registers that were previously stored with acpi_lpss_save_ctx(). - */ -static void acpi_lpss_restore_ctx(struct device *dev, - struct lpss_private_data *pdata) -{ - unsigned int i; - - for (i = 0; i < LPSS_PRV_REG_COUNT; i++) { - unsigned long offset = i * sizeof(u32); - - __lpss_reg_write(pdata->prv_reg_ctx[i], pdata, offset); - dev_dbg(dev, "restoring 0x%08x to LPSS reg at offset 0x%02lx\n", - pdata->prv_reg_ctx[i], offset); - } -} - -static void acpi_lpss_d3_to_d0_delay(struct lpss_private_data *pdata) -{ - /* - * The following delay is needed or the subsequent write operations may - * fail. The LPSS devices are actually PCI devices and the PCI spec - * expects 10ms delay before the device can be accessed after D3 to D0 - * transition. However some platforms like BSW does not need this delay. - */ - unsigned int delay = 10; /* default 10ms delay */ - - if (pdata->dev_desc->flags & LPSS_NO_D3_DELAY) - delay = 0; - - msleep(delay); -} - -static int acpi_lpss_activate(struct device *dev) -{ - struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); - int ret; - - ret = acpi_dev_resume(dev); - if (ret) - return ret; - - acpi_lpss_d3_to_d0_delay(pdata); - - /* - * This is called only on ->probe() stage where a device is either in - * known state defined by BIOS or most likely powered off. Due to this - * we have to deassert reset line to be sure that ->probe() will - * recognize the device. - */ - if (pdata->dev_desc->flags & (LPSS_SAVE_CTX | LPSS_SAVE_CTX_ONCE)) - lpss_deassert_reset(pdata); - - if (pdata->dev_desc->flags & LPSS_SAVE_CTX_ONCE) - acpi_lpss_save_ctx(dev, pdata); - - return 0; -} - -static void acpi_lpss_dismiss(struct device *dev) -{ - acpi_dev_suspend(dev, false); -} - -/* IOSF SB for LPSS island */ -#define LPSS_IOSF_UNIT_LPIOEP 0xA0 -#define LPSS_IOSF_UNIT_LPIO1 0xAB -#define LPSS_IOSF_UNIT_LPIO2 0xAC - -#define LPSS_IOSF_PMCSR 0x84 -#define LPSS_PMCSR_D0 0 -#define LPSS_PMCSR_D3hot 3 -#define LPSS_PMCSR_Dx_MASK GENMASK(1, 0) - -#define LPSS_IOSF_GPIODEF0 0x154 -#define LPSS_GPIODEF0_DMA1_D3 BIT(2) -#define LPSS_GPIODEF0_DMA2_D3 BIT(3) -#define LPSS_GPIODEF0_DMA_D3_MASK GENMASK(3, 2) -#define LPSS_GPIODEF0_DMA_LLP BIT(13) - -static DEFINE_MUTEX(lpss_iosf_mutex); -static bool lpss_iosf_d3_entered = true; - -static void lpss_iosf_enter_d3_state(void) -{ - u32 value1 = 0; - u32 mask1 = LPSS_GPIODEF0_DMA_D3_MASK | LPSS_GPIODEF0_DMA_LLP; - u32 value2 = LPSS_PMCSR_D3hot; - u32 mask2 = LPSS_PMCSR_Dx_MASK; - /* - * PMC provides an information about actual status of the LPSS devices. - * Here we read the values related to LPSS power island, i.e. LPSS - * devices, excluding both LPSS DMA controllers, along with SCC domain. - */ - u32 func_dis, d3_sts_0, pmc_status; - int ret; - - ret = pmc_atom_read(PMC_FUNC_DIS, &func_dis); - if (ret) - return; - - mutex_lock(&lpss_iosf_mutex); - - ret = pmc_atom_read(PMC_D3_STS_0, &d3_sts_0); - if (ret) - goto exit; - - /* - * Get the status of entire LPSS power island per device basis. - * Shutdown both LPSS DMA controllers if and only if all other devices - * are already in D3hot. - */ - pmc_status = (~(d3_sts_0 | func_dis)) & pmc_atom_d3_mask; - if (pmc_status) - goto exit; - - iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO1, MBI_CFG_WRITE, - LPSS_IOSF_PMCSR, value2, mask2); - - iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO2, MBI_CFG_WRITE, - LPSS_IOSF_PMCSR, value2, mask2); - - iosf_mbi_modify(LPSS_IOSF_UNIT_LPIOEP, MBI_CR_WRITE, - LPSS_IOSF_GPIODEF0, value1, mask1); - - lpss_iosf_d3_entered = true; - -exit: - mutex_unlock(&lpss_iosf_mutex); -} - -static void lpss_iosf_exit_d3_state(void) -{ - u32 value1 = LPSS_GPIODEF0_DMA1_D3 | LPSS_GPIODEF0_DMA2_D3 | - LPSS_GPIODEF0_DMA_LLP; - u32 mask1 = LPSS_GPIODEF0_DMA_D3_MASK | LPSS_GPIODEF0_DMA_LLP; - u32 value2 = LPSS_PMCSR_D0; - u32 mask2 = LPSS_PMCSR_Dx_MASK; - - mutex_lock(&lpss_iosf_mutex); - - if (!lpss_iosf_d3_entered) - goto exit; - - lpss_iosf_d3_entered = false; - - iosf_mbi_modify(LPSS_IOSF_UNIT_LPIOEP, MBI_CR_WRITE, - LPSS_IOSF_GPIODEF0, value1, mask1); - - iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO2, MBI_CFG_WRITE, - LPSS_IOSF_PMCSR, value2, mask2); - - iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO1, MBI_CFG_WRITE, - LPSS_IOSF_PMCSR, value2, mask2); - -exit: - mutex_unlock(&lpss_iosf_mutex); -} - -static int acpi_lpss_suspend(struct device *dev, bool wakeup) -{ - struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); - int ret; - - if (pdata->dev_desc->flags & LPSS_SAVE_CTX) - acpi_lpss_save_ctx(dev, pdata); - - ret = acpi_dev_suspend(dev, wakeup); - - /* - * This call must be last in the sequence, otherwise PMC will return - * wrong status for devices being about to be powered off. See - * lpss_iosf_enter_d3_state() for further information. - */ - if (acpi_target_system_state() == ACPI_STATE_S0 && - lpss_quirks & LPSS_QUIRK_ALWAYS_POWER_ON && iosf_mbi_available()) - lpss_iosf_enter_d3_state(); - - return ret; -} - -static int acpi_lpss_resume(struct device *dev) -{ - struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); - int ret; - - /* - * This call is kept first to be in symmetry with - * acpi_lpss_runtime_suspend() one. - */ - if (lpss_quirks & LPSS_QUIRK_ALWAYS_POWER_ON && iosf_mbi_available()) - lpss_iosf_exit_d3_state(); - - ret = acpi_dev_resume(dev); - if (ret) - return ret; - - acpi_lpss_d3_to_d0_delay(pdata); - - if (pdata->dev_desc->flags & (LPSS_SAVE_CTX | LPSS_SAVE_CTX_ONCE)) - acpi_lpss_restore_ctx(dev, pdata); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int acpi_lpss_do_suspend_late(struct device *dev) -{ - int ret; - - if (dev_pm_skip_suspend(dev)) - return 0; - - ret = pm_generic_suspend_late(dev); - return ret ? ret : acpi_lpss_suspend(dev, device_may_wakeup(dev)); -} - -static int acpi_lpss_suspend_late(struct device *dev) -{ - struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); - - if (pdata->dev_desc->resume_from_noirq) - return 0; - - return acpi_lpss_do_suspend_late(dev); -} - -static int acpi_lpss_suspend_noirq(struct device *dev) -{ - struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); - int ret; - - if (pdata->dev_desc->resume_from_noirq) { - /* - * The driver's ->suspend_late callback will be invoked by - * acpi_lpss_do_suspend_late(), with the assumption that the - * driver really wanted to run that code in ->suspend_noirq, but - * it could not run after acpi_dev_suspend() and the driver - * expected the latter to be called in the "late" phase. - */ - ret = acpi_lpss_do_suspend_late(dev); - if (ret) - return ret; - } - - return acpi_subsys_suspend_noirq(dev); -} - -static int acpi_lpss_do_resume_early(struct device *dev) -{ - int ret = acpi_lpss_resume(dev); - - return ret ? ret : pm_generic_resume_early(dev); -} - -static int acpi_lpss_resume_early(struct device *dev) -{ - struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); - - if (pdata->dev_desc->resume_from_noirq) - return 0; - - if (dev_pm_skip_resume(dev)) - return 0; - - return acpi_lpss_do_resume_early(dev); -} - -static int acpi_lpss_resume_noirq(struct device *dev) -{ - struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); - int ret; - - /* Follow acpi_subsys_resume_noirq(). */ - if (dev_pm_skip_resume(dev)) - return 0; - - ret = pm_generic_resume_noirq(dev); - if (ret) - return ret; - - if (!pdata->dev_desc->resume_from_noirq) - return 0; - - /* - * The driver's ->resume_early callback will be invoked by - * acpi_lpss_do_resume_early(), with the assumption that the driver - * really wanted to run that code in ->resume_noirq, but it could not - * run before acpi_dev_resume() and the driver expected the latter to be - * called in the "early" phase. - */ - return acpi_lpss_do_resume_early(dev); -} - -static int acpi_lpss_do_restore_early(struct device *dev) -{ - int ret = acpi_lpss_resume(dev); - - return ret ? ret : pm_generic_restore_early(dev); -} - -static int acpi_lpss_restore_early(struct device *dev) -{ - struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); - - if (pdata->dev_desc->resume_from_noirq) - return 0; - - return acpi_lpss_do_restore_early(dev); -} - -static int acpi_lpss_restore_noirq(struct device *dev) -{ - struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); - int ret; - - ret = pm_generic_restore_noirq(dev); - if (ret) - return ret; - - if (!pdata->dev_desc->resume_from_noirq) - return 0; - - /* This is analogous to what happens in acpi_lpss_resume_noirq(). */ - return acpi_lpss_do_restore_early(dev); -} - -static int acpi_lpss_do_poweroff_late(struct device *dev) -{ - int ret = pm_generic_poweroff_late(dev); - - return ret ? ret : acpi_lpss_suspend(dev, device_may_wakeup(dev)); -} - -static int acpi_lpss_poweroff_late(struct device *dev) -{ - struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); - - if (dev_pm_skip_suspend(dev)) - return 0; - - if (pdata->dev_desc->resume_from_noirq) - return 0; - - return acpi_lpss_do_poweroff_late(dev); -} - -static int acpi_lpss_poweroff_noirq(struct device *dev) -{ - struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); - - if (dev_pm_skip_suspend(dev)) - return 0; - - if (pdata->dev_desc->resume_from_noirq) { - /* This is analogous to the acpi_lpss_suspend_noirq() case. */ - int ret = acpi_lpss_do_poweroff_late(dev); - - if (ret) - return ret; - } - - return pm_generic_poweroff_noirq(dev); -} -#endif /* CONFIG_PM_SLEEP */ - -static int acpi_lpss_runtime_suspend(struct device *dev) -{ - int ret = pm_generic_runtime_suspend(dev); - - return ret ? ret : acpi_lpss_suspend(dev, true); -} - -static int acpi_lpss_runtime_resume(struct device *dev) -{ - int ret = acpi_lpss_resume(dev); - - return ret ? ret : pm_generic_runtime_resume(dev); -} -#endif /* CONFIG_PM */ - -static struct dev_pm_domain acpi_lpss_pm_domain = { -#ifdef CONFIG_PM - .activate = acpi_lpss_activate, - .dismiss = acpi_lpss_dismiss, -#endif - .ops = { -#ifdef CONFIG_PM -#ifdef CONFIG_PM_SLEEP - .prepare = acpi_subsys_prepare, - .complete = acpi_subsys_complete, - .suspend = acpi_subsys_suspend, - .suspend_late = acpi_lpss_suspend_late, - .suspend_noirq = acpi_lpss_suspend_noirq, - .resume_noirq = acpi_lpss_resume_noirq, - .resume_early = acpi_lpss_resume_early, - .freeze = acpi_subsys_freeze, - .poweroff = acpi_subsys_poweroff, - .poweroff_late = acpi_lpss_poweroff_late, - .poweroff_noirq = acpi_lpss_poweroff_noirq, - .restore_noirq = acpi_lpss_restore_noirq, - .restore_early = acpi_lpss_restore_early, -#endif - .runtime_suspend = acpi_lpss_runtime_suspend, - .runtime_resume = acpi_lpss_runtime_resume, -#endif - }, -}; - -static int acpi_lpss_platform_notify(struct notifier_block *nb, - unsigned long action, void *data) -{ - struct platform_device *pdev = to_platform_device(data); - struct lpss_private_data *pdata; - struct acpi_device *adev; - const struct acpi_device_id *id; - - id = acpi_match_device(acpi_lpss_device_ids, &pdev->dev); - if (!id || !id->driver_data) - return 0; - - adev = ACPI_COMPANION(&pdev->dev); - if (!adev) - return 0; - - pdata = acpi_driver_data(adev); - if (!pdata) - return 0; - - if (pdata->mmio_base && - pdata->mmio_size < pdata->dev_desc->prv_offset + LPSS_LTR_SIZE) { - dev_err(&pdev->dev, "MMIO size insufficient to access LTR\n"); - return 0; - } - - switch (action) { - case BUS_NOTIFY_BIND_DRIVER: - dev_pm_domain_set(&pdev->dev, &acpi_lpss_pm_domain); - break; - case BUS_NOTIFY_DRIVER_NOT_BOUND: - case BUS_NOTIFY_UNBOUND_DRIVER: - dev_pm_domain_set(&pdev->dev, NULL); - break; - case BUS_NOTIFY_ADD_DEVICE: - dev_pm_domain_set(&pdev->dev, &acpi_lpss_pm_domain); - if (pdata->dev_desc->flags & LPSS_LTR) - return sysfs_create_group(&pdev->dev.kobj, - &lpss_attr_group); - break; - case BUS_NOTIFY_DEL_DEVICE: - if (pdata->dev_desc->flags & LPSS_LTR) - sysfs_remove_group(&pdev->dev.kobj, &lpss_attr_group); - dev_pm_domain_set(&pdev->dev, NULL); - break; - default: - break; - } - - return 0; -} - -static struct notifier_block acpi_lpss_nb = { - .notifier_call = acpi_lpss_platform_notify, -}; - -static void acpi_lpss_bind(struct device *dev) -{ - struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); - - if (!pdata || !pdata->mmio_base || !(pdata->dev_desc->flags & LPSS_LTR)) - return; - - if (pdata->mmio_size >= pdata->dev_desc->prv_offset + LPSS_LTR_SIZE) - dev->power.set_latency_tolerance = acpi_lpss_set_ltr; - else - dev_err(dev, "MMIO size insufficient to access LTR\n"); -} - -static void acpi_lpss_unbind(struct device *dev) -{ - dev->power.set_latency_tolerance = NULL; -} - -static struct acpi_scan_handler lpss_handler = { - .ids = acpi_lpss_device_ids, - .attach = acpi_lpss_create_device, - .bind = acpi_lpss_bind, - .unbind = acpi_lpss_unbind, -}; - -void __init acpi_lpss_init(void) -{ - const struct x86_cpu_id *id; - int ret; - - ret = lpss_atom_clk_init(); - if (ret) - return; - - id = x86_match_cpu(lpss_cpu_ids); - if (id) - lpss_quirks |= LPSS_QUIRK_ALWAYS_POWER_ON; - - bus_register_notifier(&platform_bus_type, &acpi_lpss_nb); - acpi_scan_add_handler(&lpss_handler); -} - -#else - -static struct acpi_scan_handler lpss_handler = { - .ids = acpi_lpss_device_ids, -}; - -void __init acpi_lpss_init(void) -{ - acpi_scan_add_handler(&lpss_handler); -} - -#endif /* CONFIG_X86_INTEL_LPSS */ diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index ca72a0dc57151a..60c483836756d5 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -69,7 +69,8 @@ void acpi_debugfs_init(void); #else static inline void acpi_debugfs_init(void) { return; } #endif -#ifdef CONFIG_PCI + +#if defined(CONFIG_X86) && defined(CONFIG_PCI) void acpi_lpss_init(void); #else static inline void acpi_lpss_init(void) {} diff --git a/drivers/acpi/x86/Makefile b/drivers/acpi/x86/Makefile index 1f3c5fa84f9e1a..63c99509ed9de6 100644 --- a/drivers/acpi/x86/Makefile +++ b/drivers/acpi/x86/Makefile @@ -1,6 +1,7 @@ obj-$(CONFIG_ACPI) += acpi-x86.o acpi-x86-y += apple.o acpi-x86-y += cmos_rtc.o +acpi-x86-$(CONFIG_PCI) += lpss.o acpi-x86-y += s2idle.o acpi-x86-y += utils.o diff --git a/drivers/acpi/x86/lpss.c b/drivers/acpi/x86/lpss.c new file mode 100644 index 00000000000000..148e29c2c526cb --- /dev/null +++ b/drivers/acpi/x86/lpss.c @@ -0,0 +1,1355 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ACPI support for Intel Lynxpoint LPSS. + * + * Copyright (C) 2013, Intel Corporation + * Authors: Mika Westerberg + * Rafael J. Wysocki + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../internal.h" + +#ifdef CONFIG_X86_INTEL_LPSS + +#include +#include +#include + +#define LPSS_ADDR(desc) ((unsigned long)&desc) + +#define LPSS_CLK_SIZE 0x04 +#define LPSS_LTR_SIZE 0x18 + +/* Offsets relative to LPSS_PRIVATE_OFFSET */ +#define LPSS_CLK_DIVIDER_DEF_MASK (BIT(1) | BIT(16)) +#define LPSS_RESETS 0x04 +#define LPSS_RESETS_RESET_FUNC BIT(0) +#define LPSS_RESETS_RESET_APB BIT(1) +#define LPSS_GENERAL 0x08 +#define LPSS_GENERAL_LTR_MODE_SW BIT(2) +#define LPSS_GENERAL_UART_RTS_OVRD BIT(3) +#define LPSS_SW_LTR 0x10 +#define LPSS_AUTO_LTR 0x14 +#define LPSS_LTR_SNOOP_REQ BIT(15) +#define LPSS_LTR_SNOOP_MASK 0x0000FFFF +#define LPSS_LTR_SNOOP_LAT_1US 0x800 +#define LPSS_LTR_SNOOP_LAT_32US 0xC00 +#define LPSS_LTR_SNOOP_LAT_SHIFT 5 +#define LPSS_LTR_SNOOP_LAT_CUTOFF 3000 +#define LPSS_LTR_MAX_VAL 0x3FF +#define LPSS_TX_INT 0x20 +#define LPSS_TX_INT_MASK BIT(1) + +#define LPSS_PRV_REG_COUNT 9 + +/* LPSS Flags */ +#define LPSS_CLK BIT(0) +#define LPSS_CLK_GATE BIT(1) +#define LPSS_CLK_DIVIDER BIT(2) +#define LPSS_LTR BIT(3) +#define LPSS_SAVE_CTX BIT(4) +/* + * For some devices the DSDT AML code for another device turns off the device + * before our suspend handler runs, causing us to read/save all 1-s (0xffffffff) + * as ctx register values. + * Luckily these devices always use the same ctx register values, so we can + * work around this by saving the ctx registers once on activation. + */ +#define LPSS_SAVE_CTX_ONCE BIT(5) +#define LPSS_NO_D3_DELAY BIT(6) + +struct lpss_private_data; + +struct lpss_device_desc { + unsigned int flags; + const char *clk_con_id; + unsigned int prv_offset; + size_t prv_size_override; + const struct property_entry *properties; + void (*setup)(struct lpss_private_data *pdata); + bool resume_from_noirq; +}; + +static const struct lpss_device_desc lpss_dma_desc = { + .flags = LPSS_CLK, +}; + +struct lpss_private_data { + struct acpi_device *adev; + void __iomem *mmio_base; + resource_size_t mmio_size; + unsigned int fixed_clk_rate; + struct clk *clk; + const struct lpss_device_desc *dev_desc; + u32 prv_reg_ctx[LPSS_PRV_REG_COUNT]; +}; + +/* Devices which need to be in D3 before lpss_iosf_enter_d3_state() proceeds */ +static u32 pmc_atom_d3_mask = 0xfe000ffe; + +/* LPSS run time quirks */ +static unsigned int lpss_quirks; + +/* + * LPSS_QUIRK_ALWAYS_POWER_ON: override power state for LPSS DMA device. + * + * The LPSS DMA controller has neither _PS0 nor _PS3 method. Moreover + * it can be powered off automatically whenever the last LPSS device goes down. + * In case of no power any access to the DMA controller will hang the system. + * The behaviour is reproduced on some HP laptops based on Intel BayTrail as + * well as on ASuS T100TA transformer. + * + * This quirk overrides power state of entire LPSS island to keep DMA powered + * on whenever we have at least one other device in use. + */ +#define LPSS_QUIRK_ALWAYS_POWER_ON BIT(0) + +/* UART Component Parameter Register */ +#define LPSS_UART_CPR 0xF4 +#define LPSS_UART_CPR_AFCE BIT(4) + +static void lpss_uart_setup(struct lpss_private_data *pdata) +{ + unsigned int offset; + u32 val; + + offset = pdata->dev_desc->prv_offset + LPSS_TX_INT; + val = readl(pdata->mmio_base + offset); + writel(val | LPSS_TX_INT_MASK, pdata->mmio_base + offset); + + val = readl(pdata->mmio_base + LPSS_UART_CPR); + if (!(val & LPSS_UART_CPR_AFCE)) { + offset = pdata->dev_desc->prv_offset + LPSS_GENERAL; + val = readl(pdata->mmio_base + offset); + val |= LPSS_GENERAL_UART_RTS_OVRD; + writel(val, pdata->mmio_base + offset); + } +} + +static void lpss_deassert_reset(struct lpss_private_data *pdata) +{ + unsigned int offset; + u32 val; + + offset = pdata->dev_desc->prv_offset + LPSS_RESETS; + val = readl(pdata->mmio_base + offset); + val |= LPSS_RESETS_RESET_APB | LPSS_RESETS_RESET_FUNC; + writel(val, pdata->mmio_base + offset); +} + +/* + * BYT PWM used for backlight control by the i915 driver on systems without + * the Crystal Cove PMIC. + */ +static struct pwm_lookup byt_pwm_lookup[] = { + PWM_LOOKUP_WITH_MODULE("80860F09:00", 0, "0000:00:02.0", + "pwm_soc_backlight", 0, PWM_POLARITY_NORMAL, + "pwm-lpss-platform"), +}; + +static void byt_pwm_setup(struct lpss_private_data *pdata) +{ + /* Only call pwm_add_table for the first PWM controller */ + if (acpi_dev_uid_match(pdata->adev, 1)) + pwm_add_table(byt_pwm_lookup, ARRAY_SIZE(byt_pwm_lookup)); +} + +#define LPSS_I2C_ENABLE 0x6c + +static void byt_i2c_setup(struct lpss_private_data *pdata) +{ + acpi_handle handle = pdata->adev->handle; + unsigned long long shared_host = 0; + acpi_status status; + u64 uid; + + /* Expected to always be successfull, but better safe then sorry */ + if (!acpi_dev_uid_to_integer(pdata->adev, &uid) && uid) { + /* Detect I2C bus shared with PUNIT and ignore its d3 status */ + status = acpi_evaluate_integer(handle, "_SEM", NULL, &shared_host); + if (ACPI_SUCCESS(status) && shared_host) + pmc_atom_d3_mask &= ~(BIT_LPSS2_F1_I2C1 << (uid - 1)); + } + + lpss_deassert_reset(pdata); + + if (readl(pdata->mmio_base + pdata->dev_desc->prv_offset)) + pdata->fixed_clk_rate = 133000000; + + writel(0, pdata->mmio_base + LPSS_I2C_ENABLE); +} + +/* + * BSW PWM1 is used for backlight control by the i915 driver + * BSW PWM2 is used for backlight control for fixed (etched into the glass) + * touch controls on some models. These touch-controls have specialized + * drivers which know they need the "pwm_soc_lpss_2" con-id. + */ +static struct pwm_lookup bsw_pwm_lookup[] = { + PWM_LOOKUP_WITH_MODULE("80862288:00", 0, "0000:00:02.0", + "pwm_soc_backlight", 0, PWM_POLARITY_NORMAL, + "pwm-lpss-platform"), + PWM_LOOKUP_WITH_MODULE("80862289:00", 0, NULL, + "pwm_soc_lpss_2", 0, PWM_POLARITY_NORMAL, + "pwm-lpss-platform"), +}; + +static void bsw_pwm_setup(struct lpss_private_data *pdata) +{ + /* Only call pwm_add_table for the first PWM controller */ + if (acpi_dev_uid_match(pdata->adev, 1)) + pwm_add_table(bsw_pwm_lookup, ARRAY_SIZE(bsw_pwm_lookup)); +} + +static const struct property_entry lpt_spi_properties[] = { + PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_LPT_SSP), + { } +}; + +static const struct lpss_device_desc lpt_spi_dev_desc = { + .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR + | LPSS_SAVE_CTX, + .prv_offset = 0x800, + .properties = lpt_spi_properties, +}; + +static const struct lpss_device_desc lpt_i2c_dev_desc = { + .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_LTR | LPSS_SAVE_CTX, + .prv_offset = 0x800, +}; + +static struct property_entry uart_properties[] = { + PROPERTY_ENTRY_U32("reg-io-width", 4), + PROPERTY_ENTRY_U32("reg-shift", 2), + PROPERTY_ENTRY_BOOL("snps,uart-16550-compatible"), + { }, +}; + +static const struct lpss_device_desc lpt_uart_dev_desc = { + .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR + | LPSS_SAVE_CTX, + .clk_con_id = "baudclk", + .prv_offset = 0x800, + .setup = lpss_uart_setup, + .properties = uart_properties, +}; + +static const struct lpss_device_desc lpt_sdio_dev_desc = { + .flags = LPSS_LTR, + .prv_offset = 0x1000, + .prv_size_override = 0x1018, +}; + +static const struct lpss_device_desc byt_pwm_dev_desc = { + .flags = LPSS_SAVE_CTX, + .prv_offset = 0x800, + .setup = byt_pwm_setup, +}; + +static const struct lpss_device_desc bsw_pwm_dev_desc = { + .flags = LPSS_SAVE_CTX_ONCE | LPSS_NO_D3_DELAY, + .prv_offset = 0x800, + .setup = bsw_pwm_setup, + .resume_from_noirq = true, +}; + +static const struct lpss_device_desc bsw_pwm2_dev_desc = { + .flags = LPSS_SAVE_CTX_ONCE | LPSS_NO_D3_DELAY, + .prv_offset = 0x800, + .resume_from_noirq = true, +}; + +static const struct lpss_device_desc byt_uart_dev_desc = { + .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX, + .clk_con_id = "baudclk", + .prv_offset = 0x800, + .setup = lpss_uart_setup, + .properties = uart_properties, +}; + +static const struct lpss_device_desc bsw_uart_dev_desc = { + .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX + | LPSS_NO_D3_DELAY, + .clk_con_id = "baudclk", + .prv_offset = 0x800, + .setup = lpss_uart_setup, + .properties = uart_properties, +}; + +static const struct property_entry byt_spi_properties[] = { + PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_BYT_SSP), + { } +}; + +static const struct lpss_device_desc byt_spi_dev_desc = { + .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX, + .prv_offset = 0x400, + .properties = byt_spi_properties, +}; + +static const struct lpss_device_desc byt_sdio_dev_desc = { + .flags = LPSS_CLK, +}; + +static const struct lpss_device_desc byt_i2c_dev_desc = { + .flags = LPSS_CLK | LPSS_SAVE_CTX, + .prv_offset = 0x800, + .setup = byt_i2c_setup, + .resume_from_noirq = true, +}; + +static const struct lpss_device_desc bsw_i2c_dev_desc = { + .flags = LPSS_CLK | LPSS_SAVE_CTX | LPSS_NO_D3_DELAY, + .prv_offset = 0x800, + .setup = byt_i2c_setup, + .resume_from_noirq = true, +}; + +static const struct property_entry bsw_spi_properties[] = { + PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_BSW_SSP), + PROPERTY_ENTRY_U32("num-cs", 2), + { } +}; + +static const struct lpss_device_desc bsw_spi_dev_desc = { + .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX + | LPSS_NO_D3_DELAY, + .prv_offset = 0x400, + .setup = lpss_deassert_reset, + .properties = bsw_spi_properties, +}; + +static const struct x86_cpu_id lpss_cpu_ids[] = { + X86_MATCH_INTEL_FAM6_MODEL(ATOM_SILVERMONT, NULL), + X86_MATCH_INTEL_FAM6_MODEL(ATOM_AIRMONT, NULL), + {} +}; + +#else + +#define LPSS_ADDR(desc) (0UL) + +#endif /* CONFIG_X86_INTEL_LPSS */ + +static const struct acpi_device_id acpi_lpss_device_ids[] = { + /* Generic LPSS devices */ + { "INTL9C60", LPSS_ADDR(lpss_dma_desc) }, + + /* Lynxpoint LPSS devices */ + { "INT33C0", LPSS_ADDR(lpt_spi_dev_desc) }, + { "INT33C1", LPSS_ADDR(lpt_spi_dev_desc) }, + { "INT33C2", LPSS_ADDR(lpt_i2c_dev_desc) }, + { "INT33C3", LPSS_ADDR(lpt_i2c_dev_desc) }, + { "INT33C4", LPSS_ADDR(lpt_uart_dev_desc) }, + { "INT33C5", LPSS_ADDR(lpt_uart_dev_desc) }, + { "INT33C6", LPSS_ADDR(lpt_sdio_dev_desc) }, + + /* BayTrail LPSS devices */ + { "80860F09", LPSS_ADDR(byt_pwm_dev_desc) }, + { "80860F0A", LPSS_ADDR(byt_uart_dev_desc) }, + { "80860F0E", LPSS_ADDR(byt_spi_dev_desc) }, + { "80860F14", LPSS_ADDR(byt_sdio_dev_desc) }, + { "80860F41", LPSS_ADDR(byt_i2c_dev_desc) }, + + /* Braswell LPSS devices */ + { "80862286", LPSS_ADDR(lpss_dma_desc) }, + { "80862288", LPSS_ADDR(bsw_pwm_dev_desc) }, + { "80862289", LPSS_ADDR(bsw_pwm2_dev_desc) }, + { "8086228A", LPSS_ADDR(bsw_uart_dev_desc) }, + { "8086228E", LPSS_ADDR(bsw_spi_dev_desc) }, + { "808622C0", LPSS_ADDR(lpss_dma_desc) }, + { "808622C1", LPSS_ADDR(bsw_i2c_dev_desc) }, + + /* Broadwell LPSS devices */ + { "INT3430", LPSS_ADDR(lpt_spi_dev_desc) }, + { "INT3431", LPSS_ADDR(lpt_spi_dev_desc) }, + { "INT3432", LPSS_ADDR(lpt_i2c_dev_desc) }, + { "INT3433", LPSS_ADDR(lpt_i2c_dev_desc) }, + { "INT3434", LPSS_ADDR(lpt_uart_dev_desc) }, + { "INT3435", LPSS_ADDR(lpt_uart_dev_desc) }, + { "INT3436", LPSS_ADDR(lpt_sdio_dev_desc) }, + + /* Wildcat Point LPSS devices */ + { "INT3438", LPSS_ADDR(lpt_spi_dev_desc) }, + + { } +}; + +#ifdef CONFIG_X86_INTEL_LPSS + +/* LPSS main clock device. */ +static struct platform_device *lpss_clk_dev; + +static inline void lpt_register_clock_device(void) +{ + lpss_clk_dev = platform_device_register_simple("clk-lpss-atom", + PLATFORM_DEVID_NONE, + NULL, 0); +} + +static int register_device_clock(struct acpi_device *adev, + struct lpss_private_data *pdata) +{ + const struct lpss_device_desc *dev_desc = pdata->dev_desc; + const char *devname = dev_name(&adev->dev); + struct clk *clk; + struct lpss_clk_data *clk_data; + const char *parent, *clk_name; + void __iomem *prv_base; + + if (!lpss_clk_dev) + lpt_register_clock_device(); + + if (IS_ERR(lpss_clk_dev)) + return PTR_ERR(lpss_clk_dev); + + clk_data = platform_get_drvdata(lpss_clk_dev); + if (!clk_data) + return -ENODEV; + clk = clk_data->clk; + + if (!pdata->mmio_base + || pdata->mmio_size < dev_desc->prv_offset + LPSS_CLK_SIZE) + return -ENODATA; + + parent = clk_data->name; + prv_base = pdata->mmio_base + dev_desc->prv_offset; + + if (pdata->fixed_clk_rate) { + clk = clk_register_fixed_rate(NULL, devname, parent, 0, + pdata->fixed_clk_rate); + goto out; + } + + if (dev_desc->flags & LPSS_CLK_GATE) { + clk = clk_register_gate(NULL, devname, parent, 0, + prv_base, 0, 0, NULL); + parent = devname; + } + + if (dev_desc->flags & LPSS_CLK_DIVIDER) { + /* Prevent division by zero */ + if (!readl(prv_base)) + writel(LPSS_CLK_DIVIDER_DEF_MASK, prv_base); + + clk_name = kasprintf(GFP_KERNEL, "%s-div", devname); + if (!clk_name) + return -ENOMEM; + clk = clk_register_fractional_divider(NULL, clk_name, parent, + 0, prv_base, 1, 15, 16, 15, + CLK_FRAC_DIVIDER_POWER_OF_TWO_PS, + NULL); + parent = clk_name; + + clk_name = kasprintf(GFP_KERNEL, "%s-update", devname); + if (!clk_name) { + kfree(parent); + return -ENOMEM; + } + clk = clk_register_gate(NULL, clk_name, parent, + CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, + prv_base, 31, 0, NULL); + kfree(parent); + kfree(clk_name); + } +out: + if (IS_ERR(clk)) + return PTR_ERR(clk); + + pdata->clk = clk; + clk_register_clkdev(clk, dev_desc->clk_con_id, devname); + return 0; +} + +struct lpss_device_links { + const char *supplier_hid; + const char *supplier_uid; + const char *consumer_hid; + const char *consumer_uid; + u32 flags; + const struct dmi_system_id *dep_missing_ids; +}; + +/* Please keep this list sorted alphabetically by vendor and model */ +static const struct dmi_system_id i2c1_dep_missing_dmi_ids[] = { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "T200TA"), + }, + }, + {} +}; + +/* + * The _DEP method is used to identify dependencies but instead of creating + * device links for every handle in _DEP, only links in the following list are + * created. That is necessary because, in the general case, _DEP can refer to + * devices that might not have drivers, or that are on different buses, or where + * the supplier is not enumerated until after the consumer is probed. + */ +static const struct lpss_device_links lpss_device_links[] = { + /* CHT External sdcard slot controller depends on PMIC I2C ctrl */ + {"808622C1", "7", "80860F14", "3", DL_FLAG_PM_RUNTIME}, + /* CHT iGPU depends on PMIC I2C controller */ + {"808622C1", "7", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME}, + /* BYT iGPU depends on the Embedded Controller I2C controller (UID 1) */ + {"80860F41", "1", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME, + i2c1_dep_missing_dmi_ids}, + /* BYT CR iGPU depends on PMIC I2C controller (UID 5 on CR) */ + {"80860F41", "5", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME}, + /* BYT iGPU depends on PMIC I2C controller (UID 7 on non CR) */ + {"80860F41", "7", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME}, +}; + +static bool acpi_lpss_is_supplier(struct acpi_device *adev, + const struct lpss_device_links *link) +{ + return acpi_dev_hid_uid_match(adev, link->supplier_hid, link->supplier_uid); +} + +static bool acpi_lpss_is_consumer(struct acpi_device *adev, + const struct lpss_device_links *link) +{ + return acpi_dev_hid_uid_match(adev, link->consumer_hid, link->consumer_uid); +} + +struct hid_uid { + const char *hid; + const char *uid; +}; + +static int match_hid_uid(struct device *dev, const void *data) +{ + struct acpi_device *adev = ACPI_COMPANION(dev); + const struct hid_uid *id = data; + + if (!adev) + return 0; + + return acpi_dev_hid_uid_match(adev, id->hid, id->uid); +} + +static struct device *acpi_lpss_find_device(const char *hid, const char *uid) +{ + struct device *dev; + + struct hid_uid data = { + .hid = hid, + .uid = uid, + }; + + dev = bus_find_device(&platform_bus_type, NULL, &data, match_hid_uid); + if (dev) + return dev; + + return bus_find_device(&pci_bus_type, NULL, &data, match_hid_uid); +} + +static void acpi_lpss_link_consumer(struct device *dev1, + const struct lpss_device_links *link) +{ + struct device *dev2; + + dev2 = acpi_lpss_find_device(link->consumer_hid, link->consumer_uid); + if (!dev2) + return; + + if ((link->dep_missing_ids && dmi_check_system(link->dep_missing_ids)) + || acpi_device_dep(ACPI_HANDLE(dev2), ACPI_HANDLE(dev1))) + device_link_add(dev2, dev1, link->flags); + + put_device(dev2); +} + +static void acpi_lpss_link_supplier(struct device *dev1, + const struct lpss_device_links *link) +{ + struct device *dev2; + + dev2 = acpi_lpss_find_device(link->supplier_hid, link->supplier_uid); + if (!dev2) + return; + + if ((link->dep_missing_ids && dmi_check_system(link->dep_missing_ids)) + || acpi_device_dep(ACPI_HANDLE(dev1), ACPI_HANDLE(dev2))) + device_link_add(dev1, dev2, link->flags); + + put_device(dev2); +} + +static void acpi_lpss_create_device_links(struct acpi_device *adev, + struct platform_device *pdev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(lpss_device_links); i++) { + const struct lpss_device_links *link = &lpss_device_links[i]; + + if (acpi_lpss_is_supplier(adev, link)) + acpi_lpss_link_consumer(&pdev->dev, link); + + if (acpi_lpss_is_consumer(adev, link)) + acpi_lpss_link_supplier(&pdev->dev, link); + } +} + +static int acpi_lpss_create_device(struct acpi_device *adev, + const struct acpi_device_id *id) +{ + const struct lpss_device_desc *dev_desc; + struct lpss_private_data *pdata; + struct resource_entry *rentry; + struct list_head resource_list; + struct platform_device *pdev; + int ret; + + dev_desc = (const struct lpss_device_desc *)id->driver_data; + if (!dev_desc) + return -EINVAL; + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + INIT_LIST_HEAD(&resource_list); + ret = acpi_dev_get_memory_resources(adev, &resource_list); + if (ret < 0) + goto err_out; + + rentry = list_first_entry_or_null(&resource_list, struct resource_entry, node); + if (rentry) { + if (dev_desc->prv_size_override) + pdata->mmio_size = dev_desc->prv_size_override; + else + pdata->mmio_size = resource_size(rentry->res); + pdata->mmio_base = ioremap(rentry->res->start, pdata->mmio_size); + } + + acpi_dev_free_resource_list(&resource_list); + + if (!pdata->mmio_base) { + /* Avoid acpi_bus_attach() instantiating a pdev for this dev. */ + adev->pnp.type.platform_id = 0; + goto out_free; + } + + pdata->adev = adev; + pdata->dev_desc = dev_desc; + + if (dev_desc->setup) + dev_desc->setup(pdata); + + if (dev_desc->flags & LPSS_CLK) { + ret = register_device_clock(adev, pdata); + if (ret) + goto out_free; + } + + /* + * This works around a known issue in ACPI tables where LPSS devices + * have _PS0 and _PS3 without _PSC (and no power resources), so + * acpi_bus_init_power() will assume that the BIOS has put them into D0. + */ + acpi_device_fix_up_power(adev); + + adev->driver_data = pdata; + pdev = acpi_create_platform_device(adev, dev_desc->properties); + if (IS_ERR_OR_NULL(pdev)) { + adev->driver_data = NULL; + ret = PTR_ERR(pdev); + goto err_out; + } + + acpi_lpss_create_device_links(adev, pdev); + return 1; + +out_free: + /* Skip the device, but continue the namespace scan */ + ret = 0; +err_out: + kfree(pdata); + return ret; +} + +static u32 __lpss_reg_read(struct lpss_private_data *pdata, unsigned int reg) +{ + return readl(pdata->mmio_base + pdata->dev_desc->prv_offset + reg); +} + +static void __lpss_reg_write(u32 val, struct lpss_private_data *pdata, + unsigned int reg) +{ + writel(val, pdata->mmio_base + pdata->dev_desc->prv_offset + reg); +} + +static int lpss_reg_read(struct device *dev, unsigned int reg, u32 *val) +{ + struct acpi_device *adev = ACPI_COMPANION(dev); + struct lpss_private_data *pdata; + unsigned long flags; + int ret; + + if (WARN_ON(!adev)) + return -ENODEV; + + spin_lock_irqsave(&dev->power.lock, flags); + if (pm_runtime_suspended(dev)) { + ret = -EAGAIN; + goto out; + } + pdata = acpi_driver_data(adev); + if (WARN_ON(!pdata || !pdata->mmio_base)) { + ret = -ENODEV; + goto out; + } + *val = __lpss_reg_read(pdata, reg); + ret = 0; + + out: + spin_unlock_irqrestore(&dev->power.lock, flags); + return ret; +} + +static ssize_t lpss_ltr_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + u32 ltr_value = 0; + unsigned int reg; + int ret; + + reg = strcmp(attr->attr.name, "auto_ltr") ? LPSS_SW_LTR : LPSS_AUTO_LTR; + ret = lpss_reg_read(dev, reg, <r_value); + if (ret) + return ret; + + return sysfs_emit(buf, "%08x\n", ltr_value); +} + +static ssize_t lpss_ltr_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 ltr_mode = 0; + char *outstr; + int ret; + + ret = lpss_reg_read(dev, LPSS_GENERAL, <r_mode); + if (ret) + return ret; + + outstr = (ltr_mode & LPSS_GENERAL_LTR_MODE_SW) ? "sw" : "auto"; + return sprintf(buf, "%s\n", outstr); +} + +static DEVICE_ATTR(auto_ltr, S_IRUSR, lpss_ltr_show, NULL); +static DEVICE_ATTR(sw_ltr, S_IRUSR, lpss_ltr_show, NULL); +static DEVICE_ATTR(ltr_mode, S_IRUSR, lpss_ltr_mode_show, NULL); + +static struct attribute *lpss_attrs[] = { + &dev_attr_auto_ltr.attr, + &dev_attr_sw_ltr.attr, + &dev_attr_ltr_mode.attr, + NULL, +}; + +static const struct attribute_group lpss_attr_group = { + .attrs = lpss_attrs, + .name = "lpss_ltr", +}; + +static void acpi_lpss_set_ltr(struct device *dev, s32 val) +{ + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + u32 ltr_mode, ltr_val; + + ltr_mode = __lpss_reg_read(pdata, LPSS_GENERAL); + if (val < 0) { + if (ltr_mode & LPSS_GENERAL_LTR_MODE_SW) { + ltr_mode &= ~LPSS_GENERAL_LTR_MODE_SW; + __lpss_reg_write(ltr_mode, pdata, LPSS_GENERAL); + } + return; + } + ltr_val = __lpss_reg_read(pdata, LPSS_SW_LTR) & ~LPSS_LTR_SNOOP_MASK; + if (val >= LPSS_LTR_SNOOP_LAT_CUTOFF) { + ltr_val |= LPSS_LTR_SNOOP_LAT_32US; + val = LPSS_LTR_MAX_VAL; + } else if (val > LPSS_LTR_MAX_VAL) { + ltr_val |= LPSS_LTR_SNOOP_LAT_32US | LPSS_LTR_SNOOP_REQ; + val >>= LPSS_LTR_SNOOP_LAT_SHIFT; + } else { + ltr_val |= LPSS_LTR_SNOOP_LAT_1US | LPSS_LTR_SNOOP_REQ; + } + ltr_val |= val; + __lpss_reg_write(ltr_val, pdata, LPSS_SW_LTR); + if (!(ltr_mode & LPSS_GENERAL_LTR_MODE_SW)) { + ltr_mode |= LPSS_GENERAL_LTR_MODE_SW; + __lpss_reg_write(ltr_mode, pdata, LPSS_GENERAL); + } +} + +#ifdef CONFIG_PM +/** + * acpi_lpss_save_ctx() - Save the private registers of LPSS device + * @dev: LPSS device + * @pdata: pointer to the private data of the LPSS device + * + * Most LPSS devices have private registers which may loose their context when + * the device is powered down. acpi_lpss_save_ctx() saves those registers into + * prv_reg_ctx array. + */ +static void acpi_lpss_save_ctx(struct device *dev, + struct lpss_private_data *pdata) +{ + unsigned int i; + + for (i = 0; i < LPSS_PRV_REG_COUNT; i++) { + unsigned long offset = i * sizeof(u32); + + pdata->prv_reg_ctx[i] = __lpss_reg_read(pdata, offset); + dev_dbg(dev, "saving 0x%08x from LPSS reg at offset 0x%02lx\n", + pdata->prv_reg_ctx[i], offset); + } +} + +/** + * acpi_lpss_restore_ctx() - Restore the private registers of LPSS device + * @dev: LPSS device + * @pdata: pointer to the private data of the LPSS device + * + * Restores the registers that were previously stored with acpi_lpss_save_ctx(). + */ +static void acpi_lpss_restore_ctx(struct device *dev, + struct lpss_private_data *pdata) +{ + unsigned int i; + + for (i = 0; i < LPSS_PRV_REG_COUNT; i++) { + unsigned long offset = i * sizeof(u32); + + __lpss_reg_write(pdata->prv_reg_ctx[i], pdata, offset); + dev_dbg(dev, "restoring 0x%08x to LPSS reg at offset 0x%02lx\n", + pdata->prv_reg_ctx[i], offset); + } +} + +static void acpi_lpss_d3_to_d0_delay(struct lpss_private_data *pdata) +{ + /* + * The following delay is needed or the subsequent write operations may + * fail. The LPSS devices are actually PCI devices and the PCI spec + * expects 10ms delay before the device can be accessed after D3 to D0 + * transition. However some platforms like BSW does not need this delay. + */ + unsigned int delay = 10; /* default 10ms delay */ + + if (pdata->dev_desc->flags & LPSS_NO_D3_DELAY) + delay = 0; + + msleep(delay); +} + +static int acpi_lpss_activate(struct device *dev) +{ + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + int ret; + + ret = acpi_dev_resume(dev); + if (ret) + return ret; + + acpi_lpss_d3_to_d0_delay(pdata); + + /* + * This is called only on ->probe() stage where a device is either in + * known state defined by BIOS or most likely powered off. Due to this + * we have to deassert reset line to be sure that ->probe() will + * recognize the device. + */ + if (pdata->dev_desc->flags & (LPSS_SAVE_CTX | LPSS_SAVE_CTX_ONCE)) + lpss_deassert_reset(pdata); + + if (pdata->dev_desc->flags & LPSS_SAVE_CTX_ONCE) + acpi_lpss_save_ctx(dev, pdata); + + return 0; +} + +static void acpi_lpss_dismiss(struct device *dev) +{ + acpi_dev_suspend(dev, false); +} + +/* IOSF SB for LPSS island */ +#define LPSS_IOSF_UNIT_LPIOEP 0xA0 +#define LPSS_IOSF_UNIT_LPIO1 0xAB +#define LPSS_IOSF_UNIT_LPIO2 0xAC + +#define LPSS_IOSF_PMCSR 0x84 +#define LPSS_PMCSR_D0 0 +#define LPSS_PMCSR_D3hot 3 +#define LPSS_PMCSR_Dx_MASK GENMASK(1, 0) + +#define LPSS_IOSF_GPIODEF0 0x154 +#define LPSS_GPIODEF0_DMA1_D3 BIT(2) +#define LPSS_GPIODEF0_DMA2_D3 BIT(3) +#define LPSS_GPIODEF0_DMA_D3_MASK GENMASK(3, 2) +#define LPSS_GPIODEF0_DMA_LLP BIT(13) + +static DEFINE_MUTEX(lpss_iosf_mutex); +static bool lpss_iosf_d3_entered = true; + +static void lpss_iosf_enter_d3_state(void) +{ + u32 value1 = 0; + u32 mask1 = LPSS_GPIODEF0_DMA_D3_MASK | LPSS_GPIODEF0_DMA_LLP; + u32 value2 = LPSS_PMCSR_D3hot; + u32 mask2 = LPSS_PMCSR_Dx_MASK; + /* + * PMC provides an information about actual status of the LPSS devices. + * Here we read the values related to LPSS power island, i.e. LPSS + * devices, excluding both LPSS DMA controllers, along with SCC domain. + */ + u32 func_dis, d3_sts_0, pmc_status; + int ret; + + ret = pmc_atom_read(PMC_FUNC_DIS, &func_dis); + if (ret) + return; + + mutex_lock(&lpss_iosf_mutex); + + ret = pmc_atom_read(PMC_D3_STS_0, &d3_sts_0); + if (ret) + goto exit; + + /* + * Get the status of entire LPSS power island per device basis. + * Shutdown both LPSS DMA controllers if and only if all other devices + * are already in D3hot. + */ + pmc_status = (~(d3_sts_0 | func_dis)) & pmc_atom_d3_mask; + if (pmc_status) + goto exit; + + iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO1, MBI_CFG_WRITE, + LPSS_IOSF_PMCSR, value2, mask2); + + iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO2, MBI_CFG_WRITE, + LPSS_IOSF_PMCSR, value2, mask2); + + iosf_mbi_modify(LPSS_IOSF_UNIT_LPIOEP, MBI_CR_WRITE, + LPSS_IOSF_GPIODEF0, value1, mask1); + + lpss_iosf_d3_entered = true; + +exit: + mutex_unlock(&lpss_iosf_mutex); +} + +static void lpss_iosf_exit_d3_state(void) +{ + u32 value1 = LPSS_GPIODEF0_DMA1_D3 | LPSS_GPIODEF0_DMA2_D3 | + LPSS_GPIODEF0_DMA_LLP; + u32 mask1 = LPSS_GPIODEF0_DMA_D3_MASK | LPSS_GPIODEF0_DMA_LLP; + u32 value2 = LPSS_PMCSR_D0; + u32 mask2 = LPSS_PMCSR_Dx_MASK; + + mutex_lock(&lpss_iosf_mutex); + + if (!lpss_iosf_d3_entered) + goto exit; + + lpss_iosf_d3_entered = false; + + iosf_mbi_modify(LPSS_IOSF_UNIT_LPIOEP, MBI_CR_WRITE, + LPSS_IOSF_GPIODEF0, value1, mask1); + + iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO2, MBI_CFG_WRITE, + LPSS_IOSF_PMCSR, value2, mask2); + + iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO1, MBI_CFG_WRITE, + LPSS_IOSF_PMCSR, value2, mask2); + +exit: + mutex_unlock(&lpss_iosf_mutex); +} + +static int acpi_lpss_suspend(struct device *dev, bool wakeup) +{ + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + int ret; + + if (pdata->dev_desc->flags & LPSS_SAVE_CTX) + acpi_lpss_save_ctx(dev, pdata); + + ret = acpi_dev_suspend(dev, wakeup); + + /* + * This call must be last in the sequence, otherwise PMC will return + * wrong status for devices being about to be powered off. See + * lpss_iosf_enter_d3_state() for further information. + */ + if (acpi_target_system_state() == ACPI_STATE_S0 && + lpss_quirks & LPSS_QUIRK_ALWAYS_POWER_ON && iosf_mbi_available()) + lpss_iosf_enter_d3_state(); + + return ret; +} + +static int acpi_lpss_resume(struct device *dev) +{ + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + int ret; + + /* + * This call is kept first to be in symmetry with + * acpi_lpss_runtime_suspend() one. + */ + if (lpss_quirks & LPSS_QUIRK_ALWAYS_POWER_ON && iosf_mbi_available()) + lpss_iosf_exit_d3_state(); + + ret = acpi_dev_resume(dev); + if (ret) + return ret; + + acpi_lpss_d3_to_d0_delay(pdata); + + if (pdata->dev_desc->flags & (LPSS_SAVE_CTX | LPSS_SAVE_CTX_ONCE)) + acpi_lpss_restore_ctx(dev, pdata); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int acpi_lpss_do_suspend_late(struct device *dev) +{ + int ret; + + if (dev_pm_skip_suspend(dev)) + return 0; + + ret = pm_generic_suspend_late(dev); + return ret ? ret : acpi_lpss_suspend(dev, device_may_wakeup(dev)); +} + +static int acpi_lpss_suspend_late(struct device *dev) +{ + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + + if (pdata->dev_desc->resume_from_noirq) + return 0; + + return acpi_lpss_do_suspend_late(dev); +} + +static int acpi_lpss_suspend_noirq(struct device *dev) +{ + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + int ret; + + if (pdata->dev_desc->resume_from_noirq) { + /* + * The driver's ->suspend_late callback will be invoked by + * acpi_lpss_do_suspend_late(), with the assumption that the + * driver really wanted to run that code in ->suspend_noirq, but + * it could not run after acpi_dev_suspend() and the driver + * expected the latter to be called in the "late" phase. + */ + ret = acpi_lpss_do_suspend_late(dev); + if (ret) + return ret; + } + + return acpi_subsys_suspend_noirq(dev); +} + +static int acpi_lpss_do_resume_early(struct device *dev) +{ + int ret = acpi_lpss_resume(dev); + + return ret ? ret : pm_generic_resume_early(dev); +} + +static int acpi_lpss_resume_early(struct device *dev) +{ + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + + if (pdata->dev_desc->resume_from_noirq) + return 0; + + if (dev_pm_skip_resume(dev)) + return 0; + + return acpi_lpss_do_resume_early(dev); +} + +static int acpi_lpss_resume_noirq(struct device *dev) +{ + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + int ret; + + /* Follow acpi_subsys_resume_noirq(). */ + if (dev_pm_skip_resume(dev)) + return 0; + + ret = pm_generic_resume_noirq(dev); + if (ret) + return ret; + + if (!pdata->dev_desc->resume_from_noirq) + return 0; + + /* + * The driver's ->resume_early callback will be invoked by + * acpi_lpss_do_resume_early(), with the assumption that the driver + * really wanted to run that code in ->resume_noirq, but it could not + * run before acpi_dev_resume() and the driver expected the latter to be + * called in the "early" phase. + */ + return acpi_lpss_do_resume_early(dev); +} + +static int acpi_lpss_do_restore_early(struct device *dev) +{ + int ret = acpi_lpss_resume(dev); + + return ret ? ret : pm_generic_restore_early(dev); +} + +static int acpi_lpss_restore_early(struct device *dev) +{ + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + + if (pdata->dev_desc->resume_from_noirq) + return 0; + + return acpi_lpss_do_restore_early(dev); +} + +static int acpi_lpss_restore_noirq(struct device *dev) +{ + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + int ret; + + ret = pm_generic_restore_noirq(dev); + if (ret) + return ret; + + if (!pdata->dev_desc->resume_from_noirq) + return 0; + + /* This is analogous to what happens in acpi_lpss_resume_noirq(). */ + return acpi_lpss_do_restore_early(dev); +} + +static int acpi_lpss_do_poweroff_late(struct device *dev) +{ + int ret = pm_generic_poweroff_late(dev); + + return ret ? ret : acpi_lpss_suspend(dev, device_may_wakeup(dev)); +} + +static int acpi_lpss_poweroff_late(struct device *dev) +{ + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + + if (dev_pm_skip_suspend(dev)) + return 0; + + if (pdata->dev_desc->resume_from_noirq) + return 0; + + return acpi_lpss_do_poweroff_late(dev); +} + +static int acpi_lpss_poweroff_noirq(struct device *dev) +{ + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + + if (dev_pm_skip_suspend(dev)) + return 0; + + if (pdata->dev_desc->resume_from_noirq) { + /* This is analogous to the acpi_lpss_suspend_noirq() case. */ + int ret = acpi_lpss_do_poweroff_late(dev); + + if (ret) + return ret; + } + + return pm_generic_poweroff_noirq(dev); +} +#endif /* CONFIG_PM_SLEEP */ + +static int acpi_lpss_runtime_suspend(struct device *dev) +{ + int ret = pm_generic_runtime_suspend(dev); + + return ret ? ret : acpi_lpss_suspend(dev, true); +} + +static int acpi_lpss_runtime_resume(struct device *dev) +{ + int ret = acpi_lpss_resume(dev); + + return ret ? ret : pm_generic_runtime_resume(dev); +} +#endif /* CONFIG_PM */ + +static struct dev_pm_domain acpi_lpss_pm_domain = { +#ifdef CONFIG_PM + .activate = acpi_lpss_activate, + .dismiss = acpi_lpss_dismiss, +#endif + .ops = { +#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP + .prepare = acpi_subsys_prepare, + .complete = acpi_subsys_complete, + .suspend = acpi_subsys_suspend, + .suspend_late = acpi_lpss_suspend_late, + .suspend_noirq = acpi_lpss_suspend_noirq, + .resume_noirq = acpi_lpss_resume_noirq, + .resume_early = acpi_lpss_resume_early, + .freeze = acpi_subsys_freeze, + .poweroff = acpi_subsys_poweroff, + .poweroff_late = acpi_lpss_poweroff_late, + .poweroff_noirq = acpi_lpss_poweroff_noirq, + .restore_noirq = acpi_lpss_restore_noirq, + .restore_early = acpi_lpss_restore_early, +#endif + .runtime_suspend = acpi_lpss_runtime_suspend, + .runtime_resume = acpi_lpss_runtime_resume, +#endif + }, +}; + +static int acpi_lpss_platform_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct platform_device *pdev = to_platform_device(data); + struct lpss_private_data *pdata; + struct acpi_device *adev; + const struct acpi_device_id *id; + + id = acpi_match_device(acpi_lpss_device_ids, &pdev->dev); + if (!id || !id->driver_data) + return 0; + + adev = ACPI_COMPANION(&pdev->dev); + if (!adev) + return 0; + + pdata = acpi_driver_data(adev); + if (!pdata) + return 0; + + if (pdata->mmio_base && + pdata->mmio_size < pdata->dev_desc->prv_offset + LPSS_LTR_SIZE) { + dev_err(&pdev->dev, "MMIO size insufficient to access LTR\n"); + return 0; + } + + switch (action) { + case BUS_NOTIFY_BIND_DRIVER: + dev_pm_domain_set(&pdev->dev, &acpi_lpss_pm_domain); + break; + case BUS_NOTIFY_DRIVER_NOT_BOUND: + case BUS_NOTIFY_UNBOUND_DRIVER: + dev_pm_domain_set(&pdev->dev, NULL); + break; + case BUS_NOTIFY_ADD_DEVICE: + dev_pm_domain_set(&pdev->dev, &acpi_lpss_pm_domain); + if (pdata->dev_desc->flags & LPSS_LTR) + return sysfs_create_group(&pdev->dev.kobj, + &lpss_attr_group); + break; + case BUS_NOTIFY_DEL_DEVICE: + if (pdata->dev_desc->flags & LPSS_LTR) + sysfs_remove_group(&pdev->dev.kobj, &lpss_attr_group); + dev_pm_domain_set(&pdev->dev, NULL); + break; + default: + break; + } + + return 0; +} + +static struct notifier_block acpi_lpss_nb = { + .notifier_call = acpi_lpss_platform_notify, +}; + +static void acpi_lpss_bind(struct device *dev) +{ + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + + if (!pdata || !pdata->mmio_base || !(pdata->dev_desc->flags & LPSS_LTR)) + return; + + if (pdata->mmio_size >= pdata->dev_desc->prv_offset + LPSS_LTR_SIZE) + dev->power.set_latency_tolerance = acpi_lpss_set_ltr; + else + dev_err(dev, "MMIO size insufficient to access LTR\n"); +} + +static void acpi_lpss_unbind(struct device *dev) +{ + dev->power.set_latency_tolerance = NULL; +} + +static struct acpi_scan_handler lpss_handler = { + .ids = acpi_lpss_device_ids, + .attach = acpi_lpss_create_device, + .bind = acpi_lpss_bind, + .unbind = acpi_lpss_unbind, +}; + +void __init acpi_lpss_init(void) +{ + const struct x86_cpu_id *id; + int ret; + + ret = lpss_atom_clk_init(); + if (ret) + return; + + id = x86_match_cpu(lpss_cpu_ids); + if (id) + lpss_quirks |= LPSS_QUIRK_ALWAYS_POWER_ON; + + bus_register_notifier(&platform_bus_type, &acpi_lpss_nb); + acpi_scan_add_handler(&lpss_handler); +} + +#else + +static struct acpi_scan_handler lpss_handler = { + .ids = acpi_lpss_device_ids, +}; + +void __init acpi_lpss_init(void) +{ + acpi_scan_add_handler(&lpss_handler); +} + +#endif /* CONFIG_X86_INTEL_LPSS */ -- cgit 1.2.3-korg From bfd1a492b56082034cdf560d66c26b3b636fad18 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 6 Apr 2024 15:56:24 +0200 Subject: ACPI: x86: utils: Mark SMO8810 accel on Dell XPS 15 9550 as always present The Dell XPS 15 9550 has a SMO8110 accelerometer / HDD freefall sensor which is wrongly marked as not present. Mark this as always present so that the dell-smo8800 driver can bind to it. Signed-off-by: Hans de Goede Signed-off-by: Rafael J. Wysocki --- drivers/acpi/x86/utils.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/acpi/x86/utils.c b/drivers/acpi/x86/utils.c index 90c3d2eab9e990..c7af2d2986fd01 100644 --- a/drivers/acpi/x86/utils.c +++ b/drivers/acpi/x86/utils.c @@ -100,6 +100,15 @@ static const struct override_status_id override_status_ids[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Venue 11 Pro 7139"), }), + /* + * The Dell XPS 15 9550 has a SMO8110 accelerometer / + * HDD freefall sensor which is wrongly marked as not present. + */ + PRESENT_ENTRY_HID("SMO8810", "1", SKYLAKE, { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "XPS 15 9550"), + }), + /* * The GPD win BIOS dated 20170221 has disabled the accelerometer, the * drivers sometimes cause crashes under Windows and this is how the -- cgit 1.2.3-korg From d8f20383a2fc3a3844b08a4999cf0e81164a0e56 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 6 Apr 2024 15:56:25 +0200 Subject: ACPI: x86: Add PNP_UART1_SKIP quirk for Lenovo Blade2 tablets The x86 Android tablets on which quirks to skip looking for a matching UartSerialBus resource and instead unconditionally create a serial bus device (serdev) are necessary there are 2 sorts of serialports: ACPI enumerated highspeed designware UARTs, these are the ones which typcially need to be skipped since they need a serdev for the attached BT HCI. A PNP enumerated UART which is part of the PCU. So far the existing quirks have ignored this. But on the Lenovo Yoga Tablet 2 Pro 1380 models this is used for a custom fastcharging protocol. There is a Micro USB switch which can switch the USB data lines to this uart and then a 600 baud protocol is used to configure the charger for a voltage higher then 5V. Add a new ACPI_QUIRK_PNP_UART1_SKIP quirk type and set this for the existing entry for the Lenovo Yoga Tablet 2 830 / 1050 models. Note this will lead to unnecessarily also creating a serdev for the PCU UART on the 830 / 1050 which don't need this, but the UART is not used otherwise there so that is not a problem. Signed-off-by: Hans de Goede Signed-off-by: Rafael J. Wysocki --- drivers/acpi/x86/utils.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/acpi/x86/utils.c b/drivers/acpi/x86/utils.c index c7af2d2986fd01..7dca73417e2bf5 100644 --- a/drivers/acpi/x86/utils.c +++ b/drivers/acpi/x86/utils.c @@ -269,9 +269,10 @@ bool force_storage_d3(void) #define ACPI_QUIRK_SKIP_I2C_CLIENTS BIT(0) #define ACPI_QUIRK_UART1_SKIP BIT(1) #define ACPI_QUIRK_UART1_TTY_UART2_SKIP BIT(2) -#define ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY BIT(3) -#define ACPI_QUIRK_USE_ACPI_AC_AND_BATTERY BIT(4) -#define ACPI_QUIRK_SKIP_GPIO_EVENT_HANDLERS BIT(5) +#define ACPI_QUIRK_PNP_UART1_SKIP BIT(3) +#define ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY BIT(4) +#define ACPI_QUIRK_USE_ACPI_AC_AND_BATTERY BIT(5) +#define ACPI_QUIRK_SKIP_GPIO_EVENT_HANDLERS BIT(6) static const struct dmi_system_id acpi_quirk_skip_dmi_ids[] = { /* @@ -351,6 +352,7 @@ static const struct dmi_system_id acpi_quirk_skip_dmi_ids[] = { DMI_MATCH(DMI_BIOS_VERSION, "BLADE_21"), }, .driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS | + ACPI_QUIRK_PNP_UART1_SKIP | ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY), }, { @@ -449,14 +451,18 @@ static int acpi_dmi_skip_serdev_enumeration(struct device *controller_parent, bo if (ret) return 0; - /* to not match on PNP enumerated debug UARTs */ - if (!dev_is_platform(controller_parent)) - return 0; - dmi_id = dmi_first_match(acpi_quirk_skip_dmi_ids); if (dmi_id) quirks = (unsigned long)dmi_id->driver_data; + if (!dev_is_platform(controller_parent)) { + /* PNP enumerated UARTs */ + if ((quirks & ACPI_QUIRK_PNP_UART1_SKIP) && uid == 1) + *skip = true; + + return 0; + } + if ((quirks & ACPI_QUIRK_UART1_SKIP) && uid == 1) *skip = true; -- cgit 1.2.3-korg From 5b9eda2b9aa8a2332305857604b6e4e5fd462449 Mon Sep 17 00:00:00 2001 From: Len Brown Date: Fri, 5 Apr 2024 15:12:25 -0400 Subject: PM: sleep: Take advantage of %ps to simplify debug output initcall_debug previous and new output: ...PM: calling pci_pm_suspend+0x0/0x1b0 @ 3233, parent: pci0000:00 ...PM: calling pci_pm_suspend @ 3233, parent: pci0000:00 Signed-off-by: Len Brown Signed-off-by: Rafael J. Wysocki --- drivers/base/power/main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 5679f966f676d5..4a67e83300e164 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -208,7 +208,7 @@ static ktime_t initcall_debug_start(struct device *dev, void *cb) if (!pm_print_times_enabled) return 0; - dev_info(dev, "calling %pS @ %i, parent: %s\n", cb, + dev_info(dev, "calling %ps @ %i, parent: %s\n", cb, task_pid_nr(current), dev->parent ? dev_name(dev->parent) : "none"); return ktime_get(); @@ -223,7 +223,7 @@ static void initcall_debug_report(struct device *dev, ktime_t calltime, return; rettime = ktime_get(); - dev_info(dev, "%pS returned %d after %Ld usecs\n", cb, error, + dev_info(dev, "%ps returned %d after %Ld usecs\n", cb, error, (unsigned long long)ktime_us_delta(rettime, calltime)); } @@ -1927,7 +1927,7 @@ EXPORT_SYMBOL_GPL(dpm_suspend_start); void __suspend_report_result(const char *function, struct device *dev, void *fn, int ret) { if (ret) - dev_err(dev, "%s(): %pS returns %d\n", function, fn, ret); + dev_err(dev, "%s(): %ps returns %d\n", function, fn, ret); } EXPORT_SYMBOL_GPL(__suspend_report_result); -- cgit 1.2.3-korg From a3403d304708f60565582d60af4316289d0316a0 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 9 Apr 2024 16:00:55 +0200 Subject: ACPI: disable -Wstringop-truncation gcc -Wstringop-truncation warns about copying a string that results in a missing nul termination: drivers/acpi/acpica/tbfind.c: In function 'acpi_tb_find_table': drivers/acpi/acpica/tbfind.c:60:9: error: 'strncpy' specified bound 6 equals destination size [-Werror=stringop-truncation] 60 | strncpy(header.oem_id, oem_id, ACPI_OEM_ID_SIZE); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/acpi/acpica/tbfind.c:61:9: error: 'strncpy' specified bound 8 equals destination size [-Werror=stringop-truncation] 61 | strncpy(header.oem_table_id, oem_table_id, ACPI_OEM_TABLE_ID_SIZE); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The code works as intended, and the warning could be addressed by using a memcpy(), but turning the warning off for this file works equally well and may be easier to merge. Fixes: 47c08729bf1c ("ACPICA: Fix for LoadTable operator, input strings") Link: https://lore.kernel.org/lkml/CAJZ5v0hoUfv54KW7y4223Mn9E7D4xvR7whRFNLTBqCZMUxT50Q@mail.gmail.com/#t Signed-off-by: Arnd Bergmann Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile index 30f3fc13c29d12..8d18af396de92c 100644 --- a/drivers/acpi/acpica/Makefile +++ b/drivers/acpi/acpica/Makefile @@ -5,6 +5,7 @@ ccflags-y := -D_LINUX -DBUILDING_ACPICA ccflags-$(CONFIG_ACPI_DEBUG) += -DACPI_DEBUG_OUTPUT +CFLAGS_tbfind.o += $(call cc-disable-warning, stringop-truncation) # use acpi.o to put all files here into acpi.o modparam namespace obj-y += acpi.o -- cgit 1.2.3-korg From b8f85833c05730d631576008daaa34096bc7f3ce Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 12 Apr 2024 11:19:20 +0530 Subject: cpufreq: exit() callback is optional The exit() callback is optional and shouldn't be called without checking a valid pointer first. Also, we must clear freq_table pointer even if the exit() callback isn't present. Signed-off-by: Viresh Kumar Fixes: 91a12e91dc39 ("cpufreq: Allow light-weight tear down and bring up of CPUs") Fixes: f339f3541701 ("cpufreq: Rearrange locking in cpufreq_remove_dev()") Reported-by: Lizhe Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 66e10a19d76abc..fd9c3ed21f49c4 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1679,10 +1679,13 @@ static void __cpufreq_offline(unsigned int cpu, struct cpufreq_policy *policy) */ if (cpufreq_driver->offline) { cpufreq_driver->offline(policy); - } else if (cpufreq_driver->exit) { - cpufreq_driver->exit(policy); - policy->freq_table = NULL; + return; } + + if (cpufreq_driver->exit) + cpufreq_driver->exit(policy); + + policy->freq_table = NULL; } static int cpufreq_offline(unsigned int cpu) @@ -1740,7 +1743,7 @@ static void cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif) } /* We did light-weight exit earlier, do full tear down now */ - if (cpufreq_driver->offline) + if (cpufreq_driver->offline && cpufreq_driver->exit) cpufreq_driver->exit(policy); up_write(&policy->rwsem); -- cgit 1.2.3-korg From cd1b30824ff270201c43c41b9b829a369cb4e944 Mon Sep 17 00:00:00 2001 From: Ben Cheatham Date: Thu, 20 Jul 2023 14:46:14 -0500 Subject: ACPICA: actbl1.h: Add EINJ CXL error types ACPICA commit c7171588a9f684afafc83c6c18ed0bab9274e5e6 Add EINJ CXL error types added in ACPI v6.5. Link: https://github.com/acpica/acpica/commit/c7171588 Signed-off-by: Ben Cheatham Signed-off-by: Rafael J. Wysocki --- include/acpi/actbl1.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h index a33375e055ad70..1f58c5d8686913 100644 --- a/include/acpi/actbl1.h +++ b/include/acpi/actbl1.h @@ -1096,6 +1096,12 @@ enum acpi_einj_command_status { #define ACPI_EINJ_PLATFORM_CORRECTABLE (1<<9) #define ACPI_EINJ_PLATFORM_UNCORRECTABLE (1<<10) #define ACPI_EINJ_PLATFORM_FATAL (1<<11) +#define ACPI_EINJ_CXL_CACHE_CORRECTABLE (1<<12) +#define ACPI_EINJ_CXL_CACHE_UNCORRECTABLE (1<<13) +#define ACPI_EINJ_CXL_CACHE_FATAL (1<<14) +#define ACPI_EINJ_CXL_MEM_CORRECTABLE (1<<15) +#define ACPI_EINJ_CXL_MEM_UNCORRECTABLE (1<<16) +#define ACPI_EINJ_CXL_MEM_FATAL (1<<17) #define ACPI_EINJ_VENDOR_DEFINED (1<<31) /******************************************************************************* -- cgit 1.2.3-korg From 2e94dc11898042eb528eda3e09db242529722916 Mon Sep 17 00:00:00 2001 From: Shiju Jose Date: Wed, 27 Sep 2023 17:41:52 +0100 Subject: ACPICA: ACPI 6.5: RAS2: Add support for RAS2 table ACPICA commit c581606cf49b7574d29c02b1a3bc144650375e32 Add support for ACPI RAS2 feature table(RAS2) defined in the ACPI 6.5 Specification & upwards revision, section 5.2.21. The RAS2 table provides interfaces for platform RAS features. RAS2 offers the same services as RASF, but is more scalable than the latter. RAS2 supports independent RAS controls and capabilities for a given RAS feature for multiple instances of the same component in a given system. The platform can support either RAS2 or RASF but not both. Link: https://github.com/acpica/acpica/commit/c581606c Signed-off-by: Shiju Jose Signed-off-by: Rafael J. Wysocki --- include/acpi/actbl2.h | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h index 9775384d61c693..64a3f9ae2641e7 100644 --- a/include/acpi/actbl2.h +++ b/include/acpi/actbl2.h @@ -47,6 +47,7 @@ #define ACPI_SIG_PPTT "PPTT" /* Processor Properties Topology Table */ #define ACPI_SIG_PRMT "PRMT" /* Platform Runtime Mechanism Table */ #define ACPI_SIG_RASF "RASF" /* RAS Feature table */ +#define ACPI_SIG_RAS2 "RAS2" /* RAS2 Feature table */ #define ACPI_SIG_RGRT "RGRT" /* Regulatory Graphics Resource Table */ #define ACPI_SIG_RHCT "RHCT" /* RISC-V Hart Capabilities Table */ #define ACPI_SIG_SBST "SBST" /* Smart Battery Specification Table */ @@ -2751,6 +2752,134 @@ enum acpi_rasf_status { #define ACPI_RASF_ERROR (1<<2) #define ACPI_RASF_STATUS (0x1F<<3) +/******************************************************************************* + * + * RAS2 - RAS2 Feature Table (ACPI 6.5) + * Version 1 + * + * + ******************************************************************************/ + +struct acpi_table_ras2 { + struct acpi_table_header header; /* Common ACPI table header */ + u16 reserved; + u16 num_pcc_descs; +}; + +/* RAS2 Platform Communication Channel Descriptor */ + +struct acpi_ras2_pcc_desc { + u8 channel_id; + u16 reserved; + u8 feature_type; + u32 instance; +}; + +/* RAS2 Platform Communication Channel Shared Memory Region */ + +struct acpi_ras2_shared_memory { + u32 signature; + u16 command; + u16 status; + u16 version; + u8 features[16]; + u8 set_capabilities[16]; + u16 num_parameter_blocks; + u32 set_capabilities_status; +}; + +/* RAS2 Parameter Block Structure for PATROL_SCRUB */ + +struct acpi_ras2_parameter_block { + u16 type; + u16 version; + u16 length; +}; + +/* RAS2 Parameter Block Structure for PATROL_SCRUB */ + +struct acpi_ras2_patrol_scrub_parameter { + struct acpi_ras2_parameter_block header; + u16 patrol_scrub_command; + u64 requested_address_range[2]; + u64 actual_address_range[2]; + u32 flags; + u32 scrub_params_out; + u32 scrub_params_in; +}; + +/* Masks for Flags field above */ + +#define ACPI_RAS2_SCRUBBER_RUNNING 1 + +/* RAS2 Parameter Block Structure for LA2PA_TRANSLATION */ + +struct acpi_ras2_la2pa_translation_parameter { + struct acpi_ras2_parameter_block header; + u16 addr_translation_command; + u64 sub_inst_id; + u64 logical_address; + u64 physical_address; + u32 status; +}; + +/* Channel Commands */ + +enum acpi_ras2_commands { + ACPI_RAS2_EXECUTE_RAS2_COMMAND = 1 +}; + +/* Platform RAS2 Features */ + +enum acpi_ras2_features { + ACPI_RAS2_PATROL_SCRUB_SUPPORTED = 0, + ACPI_RAS2_LA2PA_TRANSLATION = 1 +}; + +/* RAS2 Patrol Scrub Commands */ + +enum acpi_ras2_patrol_scrub_commands { + ACPI_RAS2_GET_PATROL_PARAMETERS = 1, + ACPI_RAS2_START_PATROL_SCRUBBER = 2, + ACPI_RAS2_STOP_PATROL_SCRUBBER = 3 +}; + +/* RAS2 LA2PA Translation Commands */ + +enum acpi_ras2_la2_pa_translation_commands { + ACPI_RAS2_GET_LA2PA_TRANSLATION = 1, +}; + +/* RAS2 LA2PA Translation Status values */ + +enum acpi_ras2_la2_pa_translation_status { + ACPI_RAS2_LA2PA_TRANSLATION_SUCCESS = 0, + ACPI_RAS2_LA2PA_TRANSLATION_FAIL = 1, +}; + +/* Channel Command flags */ + +#define ACPI_RAS2_GENERATE_SCI (1<<15) + +/* Status values */ + +enum acpi_ras2_status { + ACPI_RAS2_SUCCESS = 0, + ACPI_RAS2_NOT_VALID = 1, + ACPI_RAS2_NOT_SUPPORTED = 2, + ACPI_RAS2_BUSY = 3, + ACPI_RAS2_FAILED = 4, + ACPI_RAS2_ABORTED = 5, + ACPI_RAS2_INVALID_DATA = 6 +}; + +/* Status flags */ + +#define ACPI_RAS2_COMMAND_COMPLETE (1) +#define ACPI_RAS2_SCI_DOORBELL (1<<1) +#define ACPI_RAS2_ERROR (1<<2) +#define ACPI_RAS2_STATUS (0x1F<<3) + /******************************************************************************* * * RGRT - Regulatory Graphics Resource Table -- cgit 1.2.3-korg From c15fe3916b77bc809577898c0d5037b1bfd4183b Mon Sep 17 00:00:00 2001 From: Saket Dumbre Date: Wed, 18 Oct 2023 15:30:43 -0700 Subject: ACPICA: Attempt 1 to fix issue #900 ACPICA commit f5910dd1ab60780b95eed16d36860f18b01bc156 Link: https://github.com/acpica/acpica/commit/f5910dd1 Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/utdebug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/acpica/utdebug.c b/drivers/acpi/acpica/utdebug.c index c5f6c85a3a092b..2f37d673e28565 100644 --- a/drivers/acpi/acpica/utdebug.c +++ b/drivers/acpi/acpica/utdebug.c @@ -42,7 +42,6 @@ void acpi_ut_init_stack_ptr_trace(void) #pragma GCC diagnostic ignored "-Wdangling-pointer=" #endif acpi_gbl_entry_stack_pointer = ¤t_sp; -#pragma GCC diagnostic pop } /******************************************************************************* @@ -63,6 +62,7 @@ void acpi_ut_track_stack_ptr(void) if (¤t_sp < acpi_gbl_lowest_stack_pointer) { acpi_gbl_lowest_stack_pointer = ¤t_sp; +#pragma GCC diagnostic pop } if (acpi_gbl_nesting_level > acpi_gbl_deepest_nesting) { -- cgit 1.2.3-korg From 86645830e665cc0cd96ffff3ed2bd72237eb0378 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sun, 26 Nov 2023 17:38:33 +0000 Subject: ACPICA: Fix various spelling mistakes in text files and code comments ACPICA commit 6cd47047aca6e273c84a5ce95d2f6d8485f958d1 There are a handful of spelling mistakes in various files as found using codespell. Fix these. No code changes. Link: https://github.com/acpica/acpica/commit/6cd47047 Signed-off-by: Colin Ian King Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/aclocal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 82563b44af3514..02012168a087a9 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -547,7 +547,7 @@ struct acpi_field_info { struct acpi_ged_handler_info { struct acpi_ged_handler_info *next; - u32 int_id; /* The interrupt ID that triggers the execution ofthe evt_method. */ + u32 int_id; /* The interrupt ID that triggers the execution of the evt_method. */ struct acpi_namespace_node *evt_method; /* The _EVT method to be executed when an interrupt with ID = int_ID is received */ }; -- cgit 1.2.3-korg From 5a02527783caae57535e0742262cd96b7167f983 Mon Sep 17 00:00:00 2001 From: Saket Dumbre Date: Tue, 26 Dec 2023 13:32:56 -0800 Subject: ACPICA: Clean up the fix for Issue #900 ACPICA commit b6b38edb0c18017af0bd2aff4eaa502810c8873f Link: https://github.com/acpica/acpica/commit/b6b38edb Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/utdebug.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/acpica/utdebug.c b/drivers/acpi/acpica/utdebug.c index 2f37d673e28565..98027d2f7bfbbd 100644 --- a/drivers/acpi/acpica/utdebug.c +++ b/drivers/acpi/acpica/utdebug.c @@ -42,6 +42,7 @@ void acpi_ut_init_stack_ptr_trace(void) #pragma GCC diagnostic ignored "-Wdangling-pointer=" #endif acpi_gbl_entry_stack_pointer = ¤t_sp; +#pragma GCC diagnostic pop } /******************************************************************************* @@ -61,8 +62,12 @@ void acpi_ut_track_stack_ptr(void) acpi_size current_sp; if (¤t_sp < acpi_gbl_lowest_stack_pointer) { +#pragma GCC diagnostic push +#if defined(__GNUC__) && __GNUC__ >= 12 +#pragma GCC diagnostic ignored "-Wdangling-pointer=" +#endif acpi_gbl_lowest_stack_pointer = ¤t_sp; -#pragma GCC diagnostic pop +#pragma GCC diagnostic popmake } if (acpi_gbl_nesting_level > acpi_gbl_deepest_nesting) { -- cgit 1.2.3-korg From ed5addd09827566df237df16357c514e2f18a9cb Mon Sep 17 00:00:00 2001 From: Saket Dumbre Date: Tue, 26 Dec 2023 13:36:14 -0800 Subject: ACPICA: Fix spelling and typos ACPICA commit a6a236c44c7d3eb54562fb5ddde4144d8347e0ac Clean up the fix for Issue #900. Link: https://github.com/acpica/acpica/commit/a6a236c4 Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/utdebug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/acpica/utdebug.c b/drivers/acpi/acpica/utdebug.c index 98027d2f7bfbbd..3d71bd9245c7de 100644 --- a/drivers/acpi/acpica/utdebug.c +++ b/drivers/acpi/acpica/utdebug.c @@ -67,7 +67,7 @@ void acpi_ut_track_stack_ptr(void) #pragma GCC diagnostic ignored "-Wdangling-pointer=" #endif acpi_gbl_lowest_stack_pointer = ¤t_sp; -#pragma GCC diagnostic popmake +#pragma GCC diagnostic pop } if (acpi_gbl_nesting_level > acpi_gbl_deepest_nesting) { -- cgit 1.2.3-korg From 66536b86c57365840cd468d9c608b7833a7a0890 Mon Sep 17 00:00:00 2001 From: lijun Date: Wed, 3 Jan 2024 13:39:35 +0800 Subject: ACPICA: Modify ACPI_OBJECT_COMMON_HEADER ACPICA commit 9788e0dc955b8d439c05ee369e43865e6f106caa modify 4 macros: ACPI_OBJECT_COMMON_HEADER, ACPI_COMMON_BUFFER_INFO, ACPI_COMMON_NOTIFY_INFO, ACPI_COMMON_FIELD_INFO they cause poor readability.so del the last ";" and when use them in a single line with the ";"in the end. Link: https://github.com/acpica/acpica/commit/9788e0dc Signed-off-by: lijun Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/acobject.h | 107 +++++++++++++++++++++++++++-------------- 1 file changed, 71 insertions(+), 36 deletions(-) diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h index 1bdfeee5d7c58c..8fc02946d3cdb9 100644 --- a/drivers/acpi/acpica/acobject.h +++ b/drivers/acpi/acpica/acobject.h @@ -48,7 +48,7 @@ u8 descriptor_type; /* To differentiate various internal objs */\ u8 type; /* acpi_object_type */\ u16 reference_count; /* For object deletion management */\ - u8 flags; + u8 flags /* * Note: There are 3 bytes available here before the * next natural alignment boundary (for both 32/64 cases) @@ -71,10 +71,12 @@ *****************************************************************************/ struct acpi_object_common { -ACPI_OBJECT_COMMON_HEADER}; + ACPI_OBJECT_COMMON_HEADER; +}; struct acpi_object_integer { - ACPI_OBJECT_COMMON_HEADER u8 fill[3]; /* Prevent warning on some compilers */ + ACPI_OBJECT_COMMON_HEADER; + u8 fill[3]; /* Prevent warning on some compilers */ u64 value; }; @@ -86,23 +88,26 @@ struct acpi_object_integer { */ #define ACPI_COMMON_BUFFER_INFO(_type) \ _type *pointer; \ - u32 length; + u32 length /* Null terminated, ASCII characters only */ struct acpi_object_string { - ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_BUFFER_INFO(char) /* String in AML stream or allocated string */ + ACPI_OBJECT_COMMON_HEADER; + ACPI_COMMON_BUFFER_INFO(char); /* String in AML stream or allocated string */ }; struct acpi_object_buffer { - ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_BUFFER_INFO(u8) /* Buffer in AML stream or allocated buffer */ + ACPI_OBJECT_COMMON_HEADER; + ACPI_COMMON_BUFFER_INFO(u8); /* Buffer in AML stream or allocated buffer */ u32 aml_length; u8 *aml_start; struct acpi_namespace_node *node; /* Link back to parent node */ }; struct acpi_object_package { - ACPI_OBJECT_COMMON_HEADER struct acpi_namespace_node *node; /* Link back to parent node */ + ACPI_OBJECT_COMMON_HEADER; + struct acpi_namespace_node *node; /* Link back to parent node */ union acpi_operand_object **elements; /* Array of pointers to acpi_objects */ u8 *aml_start; u32 aml_length; @@ -116,11 +121,13 @@ struct acpi_object_package { *****************************************************************************/ struct acpi_object_event { - ACPI_OBJECT_COMMON_HEADER acpi_semaphore os_semaphore; /* Actual OS synchronization object */ + ACPI_OBJECT_COMMON_HEADER; + acpi_semaphore os_semaphore; /* Actual OS synchronization object */ }; struct acpi_object_mutex { - ACPI_OBJECT_COMMON_HEADER u8 sync_level; /* 0-15, specified in Mutex() call */ + ACPI_OBJECT_COMMON_HEADER; + u8 sync_level; /* 0-15, specified in Mutex() call */ u16 acquisition_depth; /* Allow multiple Acquires, same thread */ acpi_mutex os_mutex; /* Actual OS synchronization object */ acpi_thread_id thread_id; /* Current owner of the mutex */ @@ -132,7 +139,8 @@ struct acpi_object_mutex { }; struct acpi_object_region { - ACPI_OBJECT_COMMON_HEADER u8 space_id; + ACPI_OBJECT_COMMON_HEADER; + u8 space_id; struct acpi_namespace_node *node; /* Containing namespace node */ union acpi_operand_object *handler; /* Handler for region access */ union acpi_operand_object *next; @@ -142,7 +150,8 @@ struct acpi_object_region { }; struct acpi_object_method { - ACPI_OBJECT_COMMON_HEADER u8 info_flags; + ACPI_OBJECT_COMMON_HEADER; + u8 info_flags; u8 param_count; u8 sync_level; union acpi_operand_object *mutex; @@ -178,33 +187,43 @@ struct acpi_object_method { */ #define ACPI_COMMON_NOTIFY_INFO \ union acpi_operand_object *notify_list[2]; /* Handlers for system/device notifies */\ - union acpi_operand_object *handler; /* Handler for Address space */ + union acpi_operand_object *handler /* Handler for Address space */ /* COMMON NOTIFY for POWER, PROCESSOR, DEVICE, and THERMAL */ struct acpi_object_notify_common { -ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_NOTIFY_INFO}; + ACPI_OBJECT_COMMON_HEADER; + ACPI_COMMON_NOTIFY_INFO; +}; struct acpi_object_device { - ACPI_OBJECT_COMMON_HEADER - ACPI_COMMON_NOTIFY_INFO struct acpi_gpe_block_info *gpe_block; + ACPI_OBJECT_COMMON_HEADER; + ACPI_COMMON_NOTIFY_INFO; + struct acpi_gpe_block_info *gpe_block; }; struct acpi_object_power_resource { - ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_NOTIFY_INFO u32 system_level; + ACPI_OBJECT_COMMON_HEADER; + ACPI_COMMON_NOTIFY_INFO; + u32 system_level; u32 resource_order; }; struct acpi_object_processor { - ACPI_OBJECT_COMMON_HEADER - /* The next two fields take advantage of the 3-byte space before NOTIFY_INFO */ + ACPI_OBJECT_COMMON_HEADER; + + /* The next two fields take advantage of the 3-byte space before NOTIFY_INFO */ + u8 proc_id; u8 length; - ACPI_COMMON_NOTIFY_INFO acpi_io_address address; + ACPI_COMMON_NOTIFY_INFO; + acpi_io_address address; }; struct acpi_object_thermal_zone { -ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_NOTIFY_INFO}; + ACPI_OBJECT_COMMON_HEADER; + ACPI_COMMON_NOTIFY_INFO; +}; /****************************************************************************** * @@ -226,17 +245,21 @@ ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_NOTIFY_INFO}; u32 base_byte_offset; /* Byte offset within containing object */\ u32 value; /* Value to store into the Bank or Index register */\ u8 start_field_bit_offset;/* Bit offset within first field datum (0-63) */\ - u8 access_length; /* For serial regions/fields */ + u8 access_length /* For serial regions/fields */ /* COMMON FIELD (for BUFFER, REGION, BANK, and INDEX fields) */ struct acpi_object_field_common { - ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO union acpi_operand_object *region_obj; /* Parent Operation Region object (REGION/BANK fields only) */ + ACPI_OBJECT_COMMON_HEADER; + ACPI_COMMON_FIELD_INFO; + union acpi_operand_object *region_obj; /* Parent Operation Region object (REGION/BANK fields only) */ }; struct acpi_object_region_field { - ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO u16 resource_length; + ACPI_OBJECT_COMMON_HEADER; + ACPI_COMMON_FIELD_INFO; + u16 resource_length; union acpi_operand_object *region_obj; /* Containing op_region object */ u8 *resource_buffer; /* resource_template for serial regions/fields */ u16 pin_number_index; /* Index relative to previous Connection/Template */ @@ -244,16 +267,20 @@ struct acpi_object_region_field { }; struct acpi_object_bank_field { - ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO union acpi_operand_object *region_obj; /* Containing op_region object */ + ACPI_OBJECT_COMMON_HEADER; + ACPI_COMMON_FIELD_INFO; + union acpi_operand_object *region_obj; /* Containing op_region object */ union acpi_operand_object *bank_obj; /* bank_select Register object */ }; struct acpi_object_index_field { - ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO - /* - * No "RegionObj" pointer needed since the Index and Data registers - * are each field definitions unto themselves. - */ + ACPI_OBJECT_COMMON_HEADER; + ACPI_COMMON_FIELD_INFO; + + /* + * No "RegionObj" pointer needed since the Index and Data registers + * are each field definitions unto themselves. + */ union acpi_operand_object *index_obj; /* Index register */ union acpi_operand_object *data_obj; /* Data register */ }; @@ -261,7 +288,9 @@ struct acpi_object_index_field { /* The buffer_field is different in that it is part of a Buffer, not an op_region */ struct acpi_object_buffer_field { - ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO u8 is_create_field; /* Special case for objects created by create_field() */ + ACPI_OBJECT_COMMON_HEADER; + ACPI_COMMON_FIELD_INFO; + u8 is_create_field; /* Special case for objects created by create_field() */ union acpi_operand_object *buffer_obj; /* Containing Buffer object */ }; @@ -272,7 +301,8 @@ struct acpi_object_buffer_field { *****************************************************************************/ struct acpi_object_notify_handler { - ACPI_OBJECT_COMMON_HEADER struct acpi_namespace_node *node; /* Parent device */ + ACPI_OBJECT_COMMON_HEADER; + struct acpi_namespace_node *node; /* Parent device */ u32 handler_type; /* Type: Device/System/Both */ acpi_notify_handler handler; /* Handler address */ void *context; @@ -280,7 +310,8 @@ struct acpi_object_notify_handler { }; struct acpi_object_addr_handler { - ACPI_OBJECT_COMMON_HEADER u8 space_id; + ACPI_OBJECT_COMMON_HEADER; + u8 space_id; u8 handler_flags; acpi_adr_space_handler handler; struct acpi_namespace_node *node; /* Parent device */ @@ -307,7 +338,8 @@ struct acpi_object_addr_handler { * The Reference.Class differentiates these types. */ struct acpi_object_reference { - ACPI_OBJECT_COMMON_HEADER u8 class; /* Reference Class */ + ACPI_OBJECT_COMMON_HEADER; + u8 class; /* Reference Class */ u8 target_type; /* Used for Index Op */ u8 resolved; /* Reference has been resolved to a value */ void *object; /* name_op=>HANDLE to obj, index_op=>union acpi_operand_object */ @@ -340,7 +372,8 @@ typedef enum { * Currently: Region and field_unit types */ struct acpi_object_extra { - ACPI_OBJECT_COMMON_HEADER struct acpi_namespace_node *method_REG; /* _REG method for this region (if any) */ + ACPI_OBJECT_COMMON_HEADER; + struct acpi_namespace_node *method_REG; /* _REG method for this region (if any) */ struct acpi_namespace_node *scope_node; void *region_context; /* Region-specific data */ u8 *aml_start; @@ -350,14 +383,16 @@ struct acpi_object_extra { /* Additional data that can be attached to namespace nodes */ struct acpi_object_data { - ACPI_OBJECT_COMMON_HEADER acpi_object_handler handler; + ACPI_OBJECT_COMMON_HEADER; + acpi_object_handler handler; void *pointer; }; /* Structure used when objects are cached for reuse */ struct acpi_object_cache_list { - ACPI_OBJECT_COMMON_HEADER union acpi_operand_object *next; /* Link for object cache and internal lists */ + ACPI_OBJECT_COMMON_HEADER; + union acpi_operand_object *next; /* Link for object cache and internal lists */ }; /****************************************************************************** -- cgit 1.2.3-korg From fe1c408d50604f6013ca273d14b0ffeb845f23b1 Mon Sep 17 00:00:00 2001 From: Haibo Xu Date: Wed, 17 Jan 2024 21:06:43 +0800 Subject: ACPICA: SRAT: Add RISC-V RINTC affinity structure ACPICA commit 93caddbf2f620769052c59ec471f018281dc3a24 Add definition of RISC-V Interrupt Controller(RINTC) affinity structure which was approved by UEFI forum and will be part of next ACPI spec version(6.6). Link: https://github.com/acpica/acpica/commit/93caddbf Signed-off-by: Haibo Xu Signed-off-by: Rafael J. Wysocki --- include/acpi/actbl3.h | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/include/acpi/actbl3.h b/include/acpi/actbl3.h index c080d579a54624..e32149d605dc95 100644 --- a/include/acpi/actbl3.h +++ b/include/acpi/actbl3.h @@ -192,7 +192,8 @@ enum acpi_srat_type { ACPI_SRAT_TYPE_GIC_ITS_AFFINITY = 4, /* ACPI 6.2 */ ACPI_SRAT_TYPE_GENERIC_AFFINITY = 5, /* ACPI 6.3 */ ACPI_SRAT_TYPE_GENERIC_PORT_AFFINITY = 6, /* ACPI 6.4 */ - ACPI_SRAT_TYPE_RESERVED = 7 /* 7 and greater are reserved */ + ACPI_SRAT_TYPE_RINTC_AFFINITY = 7, /* ACPI 6.6 */ + ACPI_SRAT_TYPE_RESERVED = 8 /* 8 and greater are reserved */ }; /* @@ -296,6 +297,21 @@ struct acpi_srat_generic_affinity { #define ACPI_SRAT_GENERIC_AFFINITY_ENABLED (1) /* 00: Use affinity structure */ #define ACPI_SRAT_ARCHITECTURAL_TRANSACTIONS (1<<1) /* ACPI 6.4 */ +/* 7: RINTC Affinity Structure(ACPI 6.6) */ + +struct acpi_srat_rintc_affinity { + struct acpi_subtable_header header; + u16 reserved; + u32 proximity_domain; + u32 acpi_processor_uid; + u32 flags; + u32 clock_domain; +}; + +/* Flags for ACPI_SRAT_RINTC_AFFINITY */ + +#define ACPI_SRAT_RINTC_ENABLED (1) /* 00: Use affinity structure */ + /******************************************************************************* * * STAO - Status Override Table (_STA override) - ACPI 6.0 -- cgit 1.2.3-korg From e19481071d0afe3b05cbb6602d7a53599e3e8506 Mon Sep 17 00:00:00 2001 From: Haibo Xu Date: Wed, 17 Jan 2024 21:17:35 +0800 Subject: ACPICA: SRAT: Add dump and compiler support for RINTC affinity structure ACPICA commit b9423c1d35b072c8f2acf97a5842b9f144449eaa After adding RISC-V RINTC affinity structure definition, enable corresponding dump and compiler support. Link: https://github.com/acpica/acpica/commit/b9423c1d Signed-off-by: Haibo Xu Signed-off-by: Rafael J. Wysocki --- include/acpi/actbl3.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/acpi/actbl3.h b/include/acpi/actbl3.h index e32149d605dc95..8f775e3a08fdfb 100644 --- a/include/acpi/actbl3.h +++ b/include/acpi/actbl3.h @@ -308,7 +308,7 @@ struct acpi_srat_rintc_affinity { u32 clock_domain; }; -/* Flags for ACPI_SRAT_RINTC_AFFINITY */ +/* Flags for struct acpi_srat_rintc_affinity */ #define ACPI_SRAT_RINTC_ENABLED (1) /* 00: Use affinity structure */ -- cgit 1.2.3-korg From 7f35712c2da2b92e03baf618fe469c64591d96fd Mon Sep 17 00:00:00 2001 From: Hojin Nam Date: Mon, 19 Feb 2024 10:06:52 +0900 Subject: ACPICA: Fix CXL 3.0 structure (RDPAS) in the CEDT table ACPICA commit a0ad1ed5105fb8a15f6f8384b8ab0a2157efaf23 struct acpi_cedt_rdpas does not match with CXL r3.0 9.17.1.5 Table 9-24. reserved1 and length fields are already added by struct acpi_cedt_header. Link: https://github.com/acpica/acpica/commit/a0ad1ed5 Signed-off-by: Hojin Nam Signed-off-by: Rafael J. Wysocki --- include/acpi/actbl1.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h index 1f58c5d8686913..841ef9f2279512 100644 --- a/include/acpi/actbl1.h +++ b/include/acpi/actbl1.h @@ -571,8 +571,6 @@ struct acpi_cedt_cxims { struct acpi_cedt_rdpas { struct acpi_cedt_header header; - u8 reserved1; - u16 length; u16 segment; u16 bdf; u8 protocol; -- cgit 1.2.3-korg From a210accc067ad18d55ea19b6d7a9d9176d809a74 Mon Sep 17 00:00:00 2001 From: Daniil Tatianin <99danilt@gmail.com> Date: Thu, 21 Mar 2024 21:57:12 +0300 Subject: ACPICA: events/evgpeinit: don't forget to increment registered GPE count ACPICA commit ba8a36b5c7343cb56af6b331362e97b25e898eb2 This was used to log the number of newly discovered GPEs post table load in acpi_ev_update_gpes(), but we never incremented the number inside acpi_ev_match_gpe_method(), so that was never logged. Link: https://github.com/acpica/acpica/commit/ba8a36b5 Signed-off-by: Daniil Tatianin <99danilt@gmail.com> Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/evgpeinit.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/acpi/acpica/evgpeinit.c b/drivers/acpi/acpica/evgpeinit.c index 0dbc4d88919a73..38f408cf13ce41 100644 --- a/drivers/acpi/acpica/evgpeinit.c +++ b/drivers/acpi/acpica/evgpeinit.c @@ -413,6 +413,7 @@ acpi_ev_match_gpe_method(acpi_handle obj_handle, gpe_event_info->flags &= ~(ACPI_GPE_DISPATCH_MASK); gpe_event_info->flags |= (u8)(type | ACPI_GPE_DISPATCH_METHOD); gpe_event_info->dispatch.method_node = method_node; + walk_info->count++; ACPI_DEBUG_PRINT((ACPI_DB_LOAD, "Registered GPE method %s as GPE number 0x%.2X\n", -- cgit 1.2.3-korg From e1d3f9d46f17e762d2dd4027456386509a6e6774 Mon Sep 17 00:00:00 2001 From: Saket Dumbre Date: Thu, 21 Mar 2024 22:00:09 -0700 Subject: ACPICA: Update acpixf.h for new ACPICA release 20240322 ACPICA commit 718374cd1bc21d08960b61069c8ac62b0cf67c0c Link: https://github.com/acpica/acpica/commit/718374cd Signed-off-by: Rafael J. Wysocki --- include/acpi/acpixf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 3d90716f952296..94d0fc3bd412d3 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -12,7 +12,7 @@ /* Current ACPICA subsystem version in YYYYMMDD format */ -#define ACPI_CA_VERSION 0x20230628 +#define ACPI_CA_VERSION 0x20240322 #include #include -- cgit 1.2.3-korg From 0654acd8eb7de3d82d0e8dc0235f1c7a67577da4 Mon Sep 17 00:00:00 2001 From: Dawei Li Date: Mon, 15 Apr 2024 17:48:21 +0800 Subject: powercap: DTPM: Avoid explicit cpumask allocation on stack In general it's preferable to avoid placing cpumasks on the stack, as for large values of NR_CPUS these can consume significant amounts of stack space and make stack overflows more likely. Use cpumask_weight_and() to avoid the need for a temporary cpumask on the stack. Signed-off-by: Dawei Li Signed-off-by: Rafael J. Wysocki --- drivers/powercap/dtpm_cpu.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/powercap/dtpm_cpu.c b/drivers/powercap/dtpm_cpu.c index bc90126f1b5f60..6b6f51b215501b 100644 --- a/drivers/powercap/dtpm_cpu.c +++ b/drivers/powercap/dtpm_cpu.c @@ -43,13 +43,11 @@ static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit) struct dtpm_cpu *dtpm_cpu = to_dtpm_cpu(dtpm); struct em_perf_domain *pd = em_cpu_get(dtpm_cpu->cpu); struct em_perf_state *table; - struct cpumask cpus; unsigned long freq; u64 power; int i, nr_cpus; - cpumask_and(&cpus, cpu_online_mask, to_cpumask(pd->cpus)); - nr_cpus = cpumask_weight(&cpus); + nr_cpus = cpumask_weight_and(cpu_online_mask, to_cpumask(pd->cpus)); rcu_read_lock(); table = em_perf_state_from_pd(pd); @@ -123,11 +121,9 @@ static int update_pd_power_uw(struct dtpm *dtpm) struct dtpm_cpu *dtpm_cpu = to_dtpm_cpu(dtpm); struct em_perf_domain *em = em_cpu_get(dtpm_cpu->cpu); struct em_perf_state *table; - struct cpumask cpus; int nr_cpus; - cpumask_and(&cpus, cpu_online_mask, to_cpumask(em->cpus)); - nr_cpus = cpumask_weight(&cpus); + nr_cpus = cpumask_weight_and(cpu_online_mask, to_cpumask(em->cpus)); rcu_read_lock(); table = em_perf_state_from_pd(em); -- cgit 1.2.3-korg From 94baae2b91818ad3167a0ba429438242b196fb88 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 8 Apr 2024 12:05:48 +0800 Subject: powercap: intel_rapl: Add support for ArrowLake-H platform Add support for ArrowLake-H platform. Signed-off-by: Zhang Rui Signed-off-by: Rafael J. Wysocki --- drivers/powercap/intel_rapl_common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/powercap/intel_rapl_common.c b/drivers/powercap/intel_rapl_common.c index a28d54fd5222cb..c02851c73751c4 100644 --- a/drivers/powercap/intel_rapl_common.c +++ b/drivers/powercap/intel_rapl_common.c @@ -1263,6 +1263,7 @@ static const struct x86_cpu_id rapl_ids[] __initconst = { X86_MATCH_INTEL_FAM6_MODEL(SAPPHIRERAPIDS_X, &rapl_defaults_spr_server), X86_MATCH_INTEL_FAM6_MODEL(EMERALDRAPIDS_X, &rapl_defaults_spr_server), X86_MATCH_INTEL_FAM6_MODEL(LUNARLAKE_M, &rapl_defaults_core), + X86_MATCH_INTEL_FAM6_MODEL(ARROWLAKE_H, &rapl_defaults_core), X86_MATCH_INTEL_FAM6_MODEL(ARROWLAKE, &rapl_defaults_core), X86_MATCH_INTEL_FAM6_MODEL(LAKEFIELD, &rapl_defaults_core), -- cgit 1.2.3-korg From 72b8b94155d957f82697802555d53c142d82dece Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 8 Apr 2024 11:51:39 +0800 Subject: powercap: intel_rapl: Sort header files Sort header files alphabetically. Signed-off-by: Zhang Rui Signed-off-by: Rafael J. Wysocki --- drivers/powercap/intel_rapl_common.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/powercap/intel_rapl_common.c b/drivers/powercap/intel_rapl_common.c index c02851c73751c4..c4302caeb631d5 100644 --- a/drivers/powercap/intel_rapl_common.c +++ b/drivers/powercap/intel_rapl_common.c @@ -5,27 +5,27 @@ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include +#include +#include +#include +#include #include -#include #include -#include -#include -#include #include -#include -#include -#include -#include +#include +#include #include -#include -#include #include -#include +#include +#include +#include +#include -#include #include #include +#include /* bitmasks for RAPL MSRs, used by primitive access functions */ #define ENERGY_STATUS_MASK 0xffffffff -- cgit 1.2.3-korg From 80f5fd45c764816fe9dbe8e94bd2677b4a8a3f4d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 18:10:53 +0200 Subject: thermal: core: Introduce .trip_crossed() callback for thermal governors Introduce a new thermal governor callback called .trip_crossed() that will be invoked whenever a trip point is crossed by the zone temperature, either on the way up or on the way down. The trip crossing direction information will be passed to it and if multiple trips are crossed in the same direction during one thermal zone update, the new callback will be invoked for them in temperature order, either ascending or descending, depending on the trip crossing direction. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba --- drivers/thermal/thermal_core.c | 19 +++++++++++++++++-- drivers/thermal/thermal_core.h | 4 ++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 58e60bcdc0a58a..62fe062d7ff549 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -302,11 +302,21 @@ static void monitor_thermal_zone(struct thermal_zone_device *tz) thermal_zone_device_set_polling(tz, tz->polling_delay_jiffies); } +static struct thermal_governor *thermal_get_tz_governor(struct thermal_zone_device *tz) +{ + if (tz->governor) + return tz->governor; + + return def_governor; +} + static void handle_non_critical_trips(struct thermal_zone_device *tz, const struct thermal_trip *trip) { - tz->governor ? tz->governor->throttle(tz, trip) : - def_governor->throttle(tz, trip); + struct thermal_governor *governor = thermal_get_tz_governor(tz); + + if (governor->throttle) + governor->throttle(tz, trip); } void thermal_governor_update_tz(struct thermal_zone_device *tz, @@ -470,6 +480,7 @@ static int thermal_trip_notify_cmp(void *ascending, const struct list_head *a, void __thermal_zone_device_update(struct thermal_zone_device *tz, enum thermal_notify_event event) { + struct thermal_governor *governor = thermal_get_tz_governor(tz); struct thermal_trip_desc *td; LIST_HEAD(way_down_list); LIST_HEAD(way_up_list); @@ -493,12 +504,16 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, list_for_each_entry(td, &way_up_list, notify_list_node) { thermal_notify_tz_trip_up(tz, &td->trip); thermal_debug_tz_trip_up(tz, &td->trip); + if (governor->trip_crossed) + governor->trip_crossed(tz, &td->trip, true); } list_sort(NULL, &way_down_list, thermal_trip_notify_cmp); list_for_each_entry(td, &way_down_list, notify_list_node) { thermal_notify_tz_trip_down(tz, &td->trip); thermal_debug_tz_trip_down(tz, &td->trip); + if (governor->trip_crossed) + governor->trip_crossed(tz, &td->trip, false); } monitor_thermal_zone(tz); diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index b461d9583834cf..9a3585492558d3 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -30,6 +30,7 @@ struct thermal_trip_desc { * otherwise it fails. * @unbind_from_tz: callback called when a governor is unbound from a * thermal zone. + * @trip_crossed: called for trip points that have just been crossed * @throttle: callback called for every trip point even if temperature is * below the trip point temperature * @update_tz: callback called when thermal zone internals have changed, e.g. @@ -40,6 +41,9 @@ struct thermal_governor { const char *name; int (*bind_to_tz)(struct thermal_zone_device *tz); void (*unbind_from_tz)(struct thermal_zone_device *tz); + void (*trip_crossed)(struct thermal_zone_device *tz, + const struct thermal_trip *trip, + bool crossed_up); int (*throttle)(struct thermal_zone_device *tz, const struct thermal_trip *trip); void (*update_tz)(struct thermal_zone_device *tz, -- cgit 1.2.3-korg From 6eaf375a5a98642ba4c327f79673f4f308e0ac03 Mon Sep 17 00:00:00 2001 From: Guenter Schafranek Date: Mon, 15 Apr 2024 20:51:18 +0200 Subject: ACPI: resource: Do IRQ override on GMxBGxx (XMG APEX 17 M23) The XM APEX 17 M23 (TongFang?) GMxBGxx (got using `sudo dmidecode -s baseboard-product-name`) needs IRQ overriding for the keyboard to work. Adding an entry for this laptop to the override_table makes the internal keyboard functional [1]. Successfully tested with Arch Linux Kernel v6.8 under Manjaro Linux v23.1.4. Link: https://www.reddit.com/r/XMG_gg/comments/15kd5pg/xmg_apex_17_m23_keyboard_not_working_on_linux/ # [1] Signed-off-by: Guenter Schafranek Signed-off-by: Rafael J. Wysocki --- drivers/acpi/resource.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index 59423fe9d0f29d..c9af5d2f4d2d74 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -533,6 +533,12 @@ static const struct dmi_system_id irq1_level_low_skip_override[] = { * to have a working keyboard. */ static const struct dmi_system_id irq1_edge_low_force_override[] = { + { + /* XMG APEX 17 (M23) */ + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GMxBGxx"), + }, + }, { /* TongFang GMxRGxx/XMG CORE 15 (M22)/TUXEDO Stellaris 15 Gen4 AMD */ .matches = { -- cgit 1.2.3-korg From c81bf14f9db68311c2e75428eea070d97d603975 Mon Sep 17 00:00:00 2001 From: Christoffer Sandberg Date: Mon, 22 Apr 2024 10:04:36 +0200 Subject: ACPI: resource: Do IRQ override on TongFang GXxHRXx and GMxHGxx Listed devices need the override for the keyboard to work. Signed-off-by: Christoffer Sandberg Signed-off-by: Werner Sembach Cc: All applicable Signed-off-by: Rafael J. Wysocki --- drivers/acpi/resource.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index c9af5d2f4d2d74..65fa94729d9de6 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -636,6 +636,18 @@ static const struct dmi_system_id irq1_edge_low_force_override[] = { DMI_MATCH(DMI_BOARD_NAME, "X565"), }, }, + { + /* TongFang GXxHRXx/TUXEDO InfinityBook Pro Gen9 AMD */ + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GXxHRXx"), + }, + }, + { + /* TongFang GMxHGxx/TUXEDO Stellaris Slim Gen1 AMD */ + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GMxHGxx"), + }, + }, { } }; -- cgit 1.2.3-korg From 59c9450b881190b497a0a61ab793a5a1b6bd006d Mon Sep 17 00:00:00 2001 From: Niklas Schnelle Date: Fri, 5 Apr 2024 16:22:26 +0200 Subject: PNP: add HAS_IOPORT dependencies In a future patch HAS_IOPORT=n will disable inb()/outb() and friends at compile time. We thus need to depend on HAS_IOPORT even when compile testing only. Acked-by: Rafael J. Wysocki Co-developed-by: Arnd Bergmann Signed-off-by: Arnd Bergmann Signed-off-by: Niklas Schnelle Signed-off-by: Rafael J. Wysocki --- drivers/pnp/isapnp/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pnp/isapnp/Kconfig b/drivers/pnp/isapnp/Kconfig index 8b5f2e461a80b9..8e5dec59e342c8 100644 --- a/drivers/pnp/isapnp/Kconfig +++ b/drivers/pnp/isapnp/Kconfig @@ -4,7 +4,7 @@ # config ISAPNP bool "ISA Plug and Play support" - depends on ISA || COMPILE_TEST + depends on ISA || (HAS_IOPORT && COMPILE_TEST) help Say Y here if you would like support for ISA Plug and Play devices. Some information is in . -- cgit 1.2.3-korg From e014998c2756772c25fc3d5ec917c11178de9ff3 Mon Sep 17 00:00:00 2001 From: Kuppuswamy Sathyanarayanan Date: Sat, 6 Apr 2024 23:33:41 -0700 Subject: ACPI: Declare acpi_blacklisted() only if CONFIG_X86 is enabled The function acpi_blacklisted() is defined only when CONFIG_X86 is enabled. So to keep it consistent, protect its declaration with CONFIG_X86. Signed-off-by: Kuppuswamy Sathyanarayanan Signed-off-by: Rafael J. Wysocki --- include/linux/acpi.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 34829f2c517ac2..3ad6bed9eb4fa2 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -421,7 +421,9 @@ extern char *wmi_get_acpi_device_uid(const char *guid); extern char acpi_video_backlight_string[]; extern long acpi_is_video_device(acpi_handle handle); +#ifdef CONFIG_X86 extern int acpi_blacklisted(void); +#endif extern void acpi_osi_setup(char *str); extern bool acpi_osi_is_win8(void); -- cgit 1.2.3-korg From 530c932bdf753a58cb29ba9eb39d9514458f9073 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 18:04:39 +0200 Subject: thermal: gov_bang_bang: Use .trip_crossed() instead of .throttle() The Bang-Bang governor really is only concerned about trip point crossing, so it can use the new .trip_crossed() callback instead of .throttle() that is not particularly suitable for it. Modify it to do so which also takes trip hysteresis into account, so the governor does not need to use it directly any more. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Acked-by: Daniel Lezcano --- drivers/thermal/gov_bang_bang.c | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/drivers/thermal/gov_bang_bang.c b/drivers/thermal/gov_bang_bang.c index c3b2943a2db8bf..5a9095a6d9ea95 100644 --- a/drivers/thermal/gov_bang_bang.c +++ b/drivers/thermal/gov_bang_bang.c @@ -13,8 +13,9 @@ #include "thermal_core.h" -static int thermal_zone_trip_update(struct thermal_zone_device *tz, - const struct thermal_trip *trip) +static void thermal_zone_trip_update(struct thermal_zone_device *tz, + const struct thermal_trip *trip, + bool crossed_up) { int trip_index = thermal_zone_trip_id(tz, trip); struct thermal_instance *instance; @@ -43,13 +44,12 @@ static int thermal_zone_trip_update(struct thermal_zone_device *tz, } /* - * enable fan when temperature exceeds trip_temp and disable - * the fan in case it falls below trip_temp minus hysteresis + * Enable the fan when the trip is crossed on the way up and + * disable it when the trip is crossed on the way down. */ - if (instance->target == 0 && tz->temperature >= trip->temperature) + if (instance->target == 0 && crossed_up) instance->target = 1; - else if (instance->target == 1 && - tz->temperature < trip->temperature - trip->hysteresis) + else if (instance->target == 1 && !crossed_up) instance->target = 0; dev_dbg(&instance->cdev->device, "target=%d\n", @@ -59,14 +59,13 @@ static int thermal_zone_trip_update(struct thermal_zone_device *tz, instance->cdev->updated = false; /* cdev needs update */ mutex_unlock(&instance->cdev->lock); } - - return 0; } /** * bang_bang_control - controls devices associated with the given zone * @tz: thermal_zone_device * @trip: the trip point + * @crossed_up: whether or not the trip has been crossed on the way up * * Regulation Logic: a two point regulation, deliver cooling state depending * on the previous state shown in this diagram: @@ -90,26 +89,22 @@ static int thermal_zone_trip_update(struct thermal_zone_device *tz, * (trip_temp - hyst) so that the fan gets turned off again. * */ -static int bang_bang_control(struct thermal_zone_device *tz, - const struct thermal_trip *trip) +static void bang_bang_control(struct thermal_zone_device *tz, + const struct thermal_trip *trip, + bool crossed_up) { struct thermal_instance *instance; - int ret; lockdep_assert_held(&tz->lock); - ret = thermal_zone_trip_update(tz, trip); - if (ret) - return ret; + thermal_zone_trip_update(tz, trip, crossed_up); list_for_each_entry(instance, &tz->thermal_instances, tz_node) thermal_cdev_update(instance->cdev); - - return 0; } static struct thermal_governor thermal_gov_bang_bang = { .name = "bang_bang", - .throttle = bang_bang_control, + .trip_crossed = bang_bang_control, }; THERMAL_GOVERNOR_DECLARE(thermal_gov_bang_bang); -- cgit 1.2.3-korg From 4526c581098e3fe08535345c6526654a9a55a695 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 18:05:44 +0200 Subject: thermal: gov_bang_bang: Clean up thermal_zone_trip_update() Do the following cleanups in thermal_zone_trip_update(): * Drop the useless "zero hysteresis" message. * Eliminate the trip_index local variable that is redundant. * Drop 2 comments that are not useful. * Downgrade a diagnostic message from pr_warn() to pr_debug(). * Use consistent field formatting in diagnostic messages. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Acked-by: Daniel Lezcano --- drivers/thermal/gov_bang_bang.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/drivers/thermal/gov_bang_bang.c b/drivers/thermal/gov_bang_bang.c index 5a9095a6d9ea95..ffc800484b8f8e 100644 --- a/drivers/thermal/gov_bang_bang.c +++ b/drivers/thermal/gov_bang_bang.c @@ -17,29 +17,23 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, const struct thermal_trip *trip, bool crossed_up) { - int trip_index = thermal_zone_trip_id(tz, trip); struct thermal_instance *instance; - if (!trip->hysteresis) - dev_info_once(&tz->device, - "Zero hysteresis value for thermal zone %s\n", tz->type); - dev_dbg(&tz->device, "Trip%d[temp=%d]:temp=%d:hyst=%d\n", - trip_index, trip->temperature, tz->temperature, - trip->hysteresis); + thermal_zone_trip_id(tz, trip), trip->temperature, + tz->temperature, trip->hysteresis); list_for_each_entry(instance, &tz->thermal_instances, tz_node) { if (instance->trip != trip) continue; - /* in case fan is in initial state, switch the fan off */ if (instance->target == THERMAL_NO_TARGET) instance->target = 0; - /* in case fan is neither on nor off set the fan to active */ if (instance->target != 0 && instance->target != 1) { - pr_warn("Thermal instance %s controlled by bang-bang has unexpected state: %ld\n", - instance->name, instance->target); + pr_debug("Unexpected state %ld of thermal instance %s in bang-bang\n", + instance->target, instance->name); + instance->target = 1; } @@ -52,8 +46,7 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, else if (instance->target == 1 && !crossed_up) instance->target = 0; - dev_dbg(&instance->cdev->device, "target=%d\n", - (int)instance->target); + dev_dbg(&instance->cdev->device, "target=%ld\n", instance->target); mutex_lock(&instance->cdev->lock); instance->cdev->updated = false; /* cdev needs update */ -- cgit 1.2.3-korg From 0ae204a667452e58a1e5081c18a179e557df33c8 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 18:06:34 +0200 Subject: thermal: gov_bang_bang: Fold thermal_zone_trip_update() into its caller Fold thermal_zone_trip_update() into bang_bang_control() which is the only caller of it to reduce code size and make it easier to follow. No functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Acked-by: Daniel Lezcano --- drivers/thermal/gov_bang_bang.c | 75 ++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 42 deletions(-) diff --git a/drivers/thermal/gov_bang_bang.c b/drivers/thermal/gov_bang_bang.c index ffc800484b8f8e..acb52c9ee10f1a 100644 --- a/drivers/thermal/gov_bang_bang.c +++ b/drivers/thermal/gov_bang_bang.c @@ -13,47 +13,6 @@ #include "thermal_core.h" -static void thermal_zone_trip_update(struct thermal_zone_device *tz, - const struct thermal_trip *trip, - bool crossed_up) -{ - struct thermal_instance *instance; - - dev_dbg(&tz->device, "Trip%d[temp=%d]:temp=%d:hyst=%d\n", - thermal_zone_trip_id(tz, trip), trip->temperature, - tz->temperature, trip->hysteresis); - - list_for_each_entry(instance, &tz->thermal_instances, tz_node) { - if (instance->trip != trip) - continue; - - if (instance->target == THERMAL_NO_TARGET) - instance->target = 0; - - if (instance->target != 0 && instance->target != 1) { - pr_debug("Unexpected state %ld of thermal instance %s in bang-bang\n", - instance->target, instance->name); - - instance->target = 1; - } - - /* - * Enable the fan when the trip is crossed on the way up and - * disable it when the trip is crossed on the way down. - */ - if (instance->target == 0 && crossed_up) - instance->target = 1; - else if (instance->target == 1 && !crossed_up) - instance->target = 0; - - dev_dbg(&instance->cdev->device, "target=%ld\n", instance->target); - - mutex_lock(&instance->cdev->lock); - instance->cdev->updated = false; /* cdev needs update */ - mutex_unlock(&instance->cdev->lock); - } -} - /** * bang_bang_control - controls devices associated with the given zone * @tz: thermal_zone_device @@ -90,7 +49,39 @@ static void bang_bang_control(struct thermal_zone_device *tz, lockdep_assert_held(&tz->lock); - thermal_zone_trip_update(tz, trip, crossed_up); + dev_dbg(&tz->device, "Trip%d[temp=%d]:temp=%d:hyst=%d\n", + thermal_zone_trip_id(tz, trip), trip->temperature, + tz->temperature, trip->hysteresis); + + list_for_each_entry(instance, &tz->thermal_instances, tz_node) { + if (instance->trip != trip) + continue; + + if (instance->target == THERMAL_NO_TARGET) + instance->target = 0; + + if (instance->target != 0 && instance->target != 1) { + pr_debug("Unexpected state %ld of thermal instance %s in bang-bang\n", + instance->target, instance->name); + + instance->target = 1; + } + + /* + * Enable the fan when the trip is crossed on the way up and + * disable it when the trip is crossed on the way down. + */ + if (instance->target == 0 && crossed_up) + instance->target = 1; + else if (instance->target == 1 && !crossed_up) + instance->target = 0; + + dev_dbg(&instance->cdev->device, "target=%ld\n", instance->target); + + mutex_lock(&instance->cdev->lock); + instance->cdev->updated = false; /* cdev needs update */ + mutex_unlock(&instance->cdev->lock); + } list_for_each_entry(instance, &tz->thermal_instances, tz_node) thermal_cdev_update(instance->cdev); -- cgit 1.2.3-korg From 976f44133f76eb24becdf3336d832eb3c720b458 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 18:08:12 +0200 Subject: thermal: core: Introduce .manage() callback for thermal governors Introduce a new thermal governor callback called .manage() that will be invoked once per thermal zone update after processing all of the trip points in the core. This will allow governors that look at multiple trip points together to check all of them in a consistent configuration, so they don't need to play tricks with skipping .throttle() invocations that they are not interested in and they can avoid carrying out the same computations for multiple times in one cycle. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Acked-by: Daniel Lezcano --- drivers/thermal/thermal_core.c | 3 +++ drivers/thermal/thermal_core.h | 2 ++ 2 files changed, 5 insertions(+) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 62fe062d7ff549..221e1924240d87 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -516,6 +516,9 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, governor->trip_crossed(tz, &td->trip, false); } + if (governor->manage) + governor->manage(tz); + monitor_thermal_zone(tz); } diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 9a3585492558d3..95cbf7a3d169e2 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -31,6 +31,7 @@ struct thermal_trip_desc { * @unbind_from_tz: callback called when a governor is unbound from a * thermal zone. * @trip_crossed: called for trip points that have just been crossed + * @manage: called on thermal zone temperature updates * @throttle: callback called for every trip point even if temperature is * below the trip point temperature * @update_tz: callback called when thermal zone internals have changed, e.g. @@ -44,6 +45,7 @@ struct thermal_governor { void (*trip_crossed)(struct thermal_zone_device *tz, const struct thermal_trip *trip, bool crossed_up); + void (*manage)(struct thermal_zone_device *tz); int (*throttle)(struct thermal_zone_device *tz, const struct thermal_trip *trip); void (*update_tz)(struct thermal_zone_device *tz, -- cgit 1.2.3-korg From 41ddbcc6fd2cd8ec3100fdea9044f3f377b6ec11 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 18:10:14 +0200 Subject: thermal: gov_power_allocator: Use .manage() callback instead of .throttle() The Power Allocator governor really only wants to be called once per thermal zone update and it does a special check to skip the extra, from its perspective, invocations of the .throttle() callback. Make it use .manage() instead of .throttle(). Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba --- drivers/thermal/gov_power_allocator.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/drivers/thermal/gov_power_allocator.c b/drivers/thermal/gov_power_allocator.c index ac1d02193a1bf1..008b3ed60a913e 100644 --- a/drivers/thermal/gov_power_allocator.c +++ b/drivers/thermal/gov_power_allocator.c @@ -395,7 +395,7 @@ static void divvy_up_power(struct power_actor *power, int num_actors, } } -static int allocate_power(struct thermal_zone_device *tz, int control_temp) +static void allocate_power(struct thermal_zone_device *tz, int control_temp) { struct power_allocator_params *params = tz->governor_data; unsigned int num_actors = params->num_actors; @@ -410,7 +410,7 @@ static int allocate_power(struct thermal_zone_device *tz, int control_temp) int i = 0, ret; if (!num_actors) - return -ENODEV; + return; /* Clean all buffers for new power estimations */ memset(power, 0, params->buffer_size); @@ -471,8 +471,6 @@ static int allocate_power(struct thermal_zone_device *tz, int control_temp) num_actors, power_range, max_allocatable_power, tz->temperature, control_temp - tz->temperature); - - return 0; } /** @@ -745,40 +743,32 @@ static void power_allocator_unbind(struct thermal_zone_device *tz) tz->governor_data = NULL; } -static int power_allocator_throttle(struct thermal_zone_device *tz, - const struct thermal_trip *trip) +static void power_allocator_manage(struct thermal_zone_device *tz) { struct power_allocator_params *params = tz->governor_data; + const struct thermal_trip *trip = params->trip_switch_on; bool update; lockdep_assert_held(&tz->lock); - /* - * We get called for every trip point but we only need to do - * our calculations once - */ - if (trip != params->trip_max) - return 0; - - trip = params->trip_switch_on; if (trip && tz->temperature < trip->temperature) { update = tz->passive; tz->passive = 0; reset_pid_controller(params); allow_maximum_power(tz, update); - return 0; + return; } tz->passive = 1; - return allocate_power(tz, params->trip_max->temperature); + allocate_power(tz, params->trip_max->temperature); } static struct thermal_governor thermal_gov_power_allocator = { .name = "power_allocator", .bind_to_tz = power_allocator_bind, .unbind_from_tz = power_allocator_unbind, - .throttle = power_allocator_throttle, + .manage = power_allocator_manage, .update_tz = power_allocator_update_tz, }; THERMAL_GOVERNOR_DECLARE(thermal_gov_power_allocator); -- cgit 1.2.3-korg From ca0e9728d37215afe943d508a1935d13a96ea88e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 18:12:45 +0200 Subject: thermal: gov_power_allocator: Eliminate a redundant variable Notice that the passive field in struct thermal_zone_device is not used by the Power Allocator governor itself and so the ordering of its updates with respect to allow_maximum_power() or allocate_power() does not matter. Accordingly, make power_allocator_manage() update that field right before returning, which allows the current value of it to be passed directly to allow_maximum_power() without using the additional update variable that can be dropped. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba --- drivers/thermal/gov_power_allocator.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/thermal/gov_power_allocator.c b/drivers/thermal/gov_power_allocator.c index 008b3ed60a913e..7450ab77d5f09e 100644 --- a/drivers/thermal/gov_power_allocator.c +++ b/drivers/thermal/gov_power_allocator.c @@ -747,21 +747,18 @@ static void power_allocator_manage(struct thermal_zone_device *tz) { struct power_allocator_params *params = tz->governor_data; const struct thermal_trip *trip = params->trip_switch_on; - bool update; lockdep_assert_held(&tz->lock); if (trip && tz->temperature < trip->temperature) { - update = tz->passive; - tz->passive = 0; reset_pid_controller(params); - allow_maximum_power(tz, update); + allow_maximum_power(tz, tz->passive); + tz->passive = 0; return; } - tz->passive = 1; - allocate_power(tz, params->trip_max->temperature); + tz->passive = 1; } static struct thermal_governor thermal_gov_power_allocator = { -- cgit 1.2.3-korg From a6ce8c7da59bbdafcab23b5d26f26865af77a5fa Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 18:13:55 +0200 Subject: thermal: gov_step_wise: Use .manage() callback instead of .throttle() Make the Step-Wise governor use the new .manage() callback instead of .throttle(). Even though using .throttle() is not particularly problematic for the Step-Wise governor, using .manage() instead still allows it to reduce overhead by updating all of the cooling devices once after setting target values for all of the thermal instances. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Acked-by: Daniel Lezcano --- drivers/thermal/gov_step_wise.c | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/drivers/thermal/gov_step_wise.c b/drivers/thermal/gov_step_wise.c index ee2fb4e63d1478..5c223445f169db 100644 --- a/drivers/thermal/gov_step_wise.c +++ b/drivers/thermal/gov_step_wise.c @@ -109,34 +109,37 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, } } -/** - * step_wise_throttle - throttles devices associated with the given zone - * @tz: thermal_zone_device - * @trip: trip point - * - * Throttling Logic: This uses the trend of the thermal zone to throttle. - * If the thermal zone is 'heating up' this throttles all the cooling - * devices associated with the zone and its particular trip point, by one - * step. If the zone is 'cooling down' it brings back the performance of - * the devices by one step. - */ -static int step_wise_throttle(struct thermal_zone_device *tz, - const struct thermal_trip *trip) +static void step_wise_manage(struct thermal_zone_device *tz) { + const struct thermal_trip_desc *td; struct thermal_instance *instance; lockdep_assert_held(&tz->lock); - thermal_zone_trip_update(tz, trip); + /* + * Throttling Logic: Use the trend of the thermal zone to throttle. + * If the thermal zone is 'heating up', throttle all of the cooling + * devices associated with each trip point by one step. If the zone + * is 'cooling down', it brings back the performance of the devices + * by one step. + */ + for_each_trip_desc(tz, td) { + const struct thermal_trip *trip = &td->trip; + + if (trip->temperature == THERMAL_TEMP_INVALID || + trip->type == THERMAL_TRIP_CRITICAL || + trip->type == THERMAL_TRIP_HOT) + continue; + + thermal_zone_trip_update(tz, trip); + } list_for_each_entry(instance, &tz->thermal_instances, tz_node) thermal_cdev_update(instance->cdev); - - return 0; } static struct thermal_governor thermal_gov_step_wise = { - .name = "step_wise", - .throttle = step_wise_throttle, + .name = "step_wise", + .manage = step_wise_manage, }; THERMAL_GOVERNOR_DECLARE(thermal_gov_step_wise); -- cgit 1.2.3-korg From e4065f144fa60df56a8596bdc364a5b4fe8cbf40 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 18:43:07 +0200 Subject: thermal: gov_step_wise: Use trip thresholds instead of trip temperatures In principle, the Step-Wise governor should take trip hysteresis into account. After all, once a trip has been crossed on the way up, mitigation is still needed until it is crossed on the way down. For this reason, make it use trip thresholds that are computed by the core when trips are crossed, so as to apply mitigations in the hysteresis rages of trips that were crossed on the way up, but have not been crossed on the way down yet. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Acked-by: Daniel Lezcano --- drivers/thermal/gov_step_wise.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/thermal/gov_step_wise.c b/drivers/thermal/gov_step_wise.c index 5c223445f169db..30a32ad8c4732b 100644 --- a/drivers/thermal/gov_step_wise.c +++ b/drivers/thermal/gov_step_wise.c @@ -62,7 +62,8 @@ static unsigned long get_target_state(struct thermal_instance *instance, } static void thermal_zone_trip_update(struct thermal_zone_device *tz, - const struct thermal_trip *trip) + const struct thermal_trip *trip, + int trip_threshold) { int trip_id = thermal_zone_trip_id(tz, trip); enum thermal_trend trend; @@ -72,13 +73,13 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, trend = get_tz_trend(tz, trip); - if (tz->temperature >= trip->temperature) { + if (tz->temperature >= trip_threshold) { throttle = true; trace_thermal_zone_trip(tz, trip_id, trip->type); } dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d]:trend=%d,throttle=%d\n", - trip_id, trip->type, trip->temperature, trend, throttle); + trip_id, trip->type, trip_threshold, trend, throttle); list_for_each_entry(instance, &tz->thermal_instances, tz_node) { if (instance->trip != trip) @@ -131,7 +132,7 @@ static void step_wise_manage(struct thermal_zone_device *tz) trip->type == THERMAL_TRIP_HOT) continue; - thermal_zone_trip_update(tz, trip); + thermal_zone_trip_update(tz, trip, td->threshold); } list_for_each_entry(instance, &tz->thermal_instances, tz_node) -- cgit 1.2.3-korg From fe036266504796c84adee28b64c347d3acf4206e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 18:44:14 +0200 Subject: thermal: gov_step_wise: Clean up thermal_zone_trip_update() Do some assorted cleanups in thermal_zone_trip_update(): * Compute the trend value upfront. * Move old_target definition to the block where it is used. * Adjust white space around diagnostic messages and locking. * Use suitable field formatting in a message to avoid an explicit cast to int. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba --- drivers/thermal/gov_step_wise.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/thermal/gov_step_wise.c b/drivers/thermal/gov_step_wise.c index 30a32ad8c4732b..0196a1a02290c9 100644 --- a/drivers/thermal/gov_step_wise.c +++ b/drivers/thermal/gov_step_wise.c @@ -65,13 +65,10 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, const struct thermal_trip *trip, int trip_threshold) { + enum thermal_trend trend = get_tz_trend(tz, trip); int trip_id = thermal_zone_trip_id(tz, trip); - enum thermal_trend trend; struct thermal_instance *instance; bool throttle = false; - int old_target; - - trend = get_tz_trend(tz, trip); if (tz->temperature >= trip_threshold) { throttle = true; @@ -82,13 +79,16 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, trip_id, trip->type, trip_threshold, trend, throttle); list_for_each_entry(instance, &tz->thermal_instances, tz_node) { + int old_target; + if (instance->trip != trip) continue; old_target = instance->target; instance->target = get_target_state(instance, trend, throttle); - dev_dbg(&instance->cdev->device, "old_target=%d, target=%d\n", - old_target, (int)instance->target); + + dev_dbg(&instance->cdev->device, "old_target=%d, target=%ld\n", + old_target, instance->target); if (instance->initialized && old_target == instance->target) continue; @@ -104,6 +104,7 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, } instance->initialized = true; + mutex_lock(&instance->cdev->lock); instance->cdev->updated = false; /* cdev needs update */ mutex_unlock(&instance->cdev->lock); -- cgit 1.2.3-korg From bec55332c24eb671a1b6de17b38a70dee2472245 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 18:57:38 +0200 Subject: thermal: gov_fair_share: Use .manage() callback instead of .throttle() The Fair Share governor tries very hard to be stateless and so it calls get_trip_level() from fair_share_throttle() every time, even though the number produced by this function for all of the trips during a given thermal zone update is actually the same. Since get_trip_level() walks all of the trips in the thermal zone every time it is called, doing this may generate quite a bit of completely useless overhead. For this reason, make the governor use the new .manage() callback instead of .throttle() which allows it to call get_trip_level() just once and use the value computed by it to handle all of the trips. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba --- drivers/thermal/gov_fair_share.c | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/drivers/thermal/gov_fair_share.c b/drivers/thermal/gov_fair_share.c index 6ef8cfde7749a9..ff81aeb5ac040e 100644 --- a/drivers/thermal/gov_fair_share.c +++ b/drivers/thermal/gov_fair_share.c @@ -53,6 +53,7 @@ static long get_target_state(struct thermal_zone_device *tz, * fair_share_throttle - throttles devices associated with the given zone * @tz: thermal_zone_device * @trip: trip point + * @trip_level: number of trips crossed by the zone temperature * * Throttling Logic: This uses three parameters to calculate the new * throttle state of the cooling devices associated with the given zone. @@ -61,22 +62,19 @@ static long get_target_state(struct thermal_zone_device *tz, * P1. max_state: Maximum throttle state exposed by the cooling device. * P2. percentage[i]/100: * How 'effective' the 'i'th device is, in cooling the given zone. - * P3. cur_trip_level/max_no_of_trips: + * P3. trip_level/max_no_of_trips: * This describes the extent to which the devices should be throttled. * We do not want to throttle too much when we trip a lower temperature, * whereas the throttling is at full swing if we trip critical levels. - * (Heavily assumes the trip points are in ascending order) * new_state of cooling device = P3 * P2 * P1 */ -static int fair_share_throttle(struct thermal_zone_device *tz, - const struct thermal_trip *trip) +static void fair_share_throttle(struct thermal_zone_device *tz, + const struct thermal_trip *trip, + int trip_level) { struct thermal_instance *instance; int total_weight = 0; int total_instance = 0; - int cur_trip_level = get_trip_level(tz); - - lockdep_assert_held(&tz->lock); list_for_each_entry(instance, &tz->thermal_instances, tz_node) { if (instance->trip != trip) @@ -99,18 +97,35 @@ static int fair_share_throttle(struct thermal_zone_device *tz, percentage = (instance->weight * 100) / total_weight; instance->target = get_target_state(tz, cdev, percentage, - cur_trip_level); + trip_level); mutex_lock(&cdev->lock); __thermal_cdev_update(cdev); mutex_unlock(&cdev->lock); } +} + +static void fair_share_manage(struct thermal_zone_device *tz) +{ + int trip_level = get_trip_level(tz); + const struct thermal_trip_desc *td; + + lockdep_assert_held(&tz->lock); + + for_each_trip_desc(tz, td) { + const struct thermal_trip *trip = &td->trip; - return 0; + if (trip->temperature == THERMAL_TEMP_INVALID || + trip->type == THERMAL_TRIP_CRITICAL || + trip->type == THERMAL_TRIP_HOT) + continue; + + fair_share_throttle(tz, trip, trip_level); + } } static struct thermal_governor thermal_gov_fair_share = { - .name = "fair_share", - .throttle = fair_share_throttle, + .name = "fair_share", + .manage = fair_share_manage, }; THERMAL_GOVERNOR_DECLARE(thermal_gov_fair_share); -- cgit 1.2.3-korg From 0292991ce46c75c28d6ccfe7a7f8ac962a824291 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 18:58:56 +0200 Subject: thermal: gov_fair_share: Use trip thresholds instead of trip temperatures In principle, the Fair Share governor should take trip hysteresis into account. After all, once a trip has been crossed on the way up, mitigation is still needed until it is crossed on the way down. For this reason, make it use trip thresholds that are computed by the core when trips are crossed, so as to apply mitigations if the zone temperature is in a hysteresis rage of one or more trips that were crossed on the way up, but have not been crossed on the way down yet. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba --- drivers/thermal/gov_fair_share.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/thermal/gov_fair_share.c b/drivers/thermal/gov_fair_share.c index ff81aeb5ac040e..ca6d6152ccf24f 100644 --- a/drivers/thermal/gov_fair_share.c +++ b/drivers/thermal/gov_fair_share.c @@ -17,28 +17,26 @@ static int get_trip_level(struct thermal_zone_device *tz) { - const struct thermal_trip *level_trip = NULL; + const struct thermal_trip_desc *level_td = NULL; const struct thermal_trip_desc *td; int trip_level = -1; for_each_trip_desc(tz, td) { - const struct thermal_trip *trip = &td->trip; - - if (trip->temperature >= tz->temperature) + if (td->threshold > tz->temperature) continue; trip_level++; - if (!level_trip || trip->temperature > level_trip->temperature) - level_trip = trip; + if (!level_td || td->threshold > level_td->threshold) + level_td = td; } /* Bail out if the temperature is not greater than any trips. */ if (trip_level < 0) return 0; - trace_thermal_zone_trip(tz, thermal_zone_trip_id(tz, level_trip), - level_trip->type); + trace_thermal_zone_trip(tz, thermal_zone_trip_id(tz, &level_td->trip), + level_td->trip.type); return trip_level; } -- cgit 1.2.3-korg From c98e24795e8b53ff68b317ed31ddcc37cf8b6a26 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 19:00:10 +0200 Subject: thermal: gov_fair_share: Eliminate unnecessary integer divisions The computations carried out by fair_share_throttle() for each trip point include at least one redundant integer division which introduces superfluous rounding errors. Also the multiplications by 100 in it are not really necessary and can be eliminated. Rearrange fair_share_throttle() to carry out only one integer division per trip and only as many integer multiplications as necessary and rename one variable in it (while at it). Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba --- drivers/thermal/gov_fair_share.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/drivers/thermal/gov_fair_share.c b/drivers/thermal/gov_fair_share.c index ca6d6152ccf24f..ce0ea571ed67ab 100644 --- a/drivers/thermal/gov_fair_share.c +++ b/drivers/thermal/gov_fair_share.c @@ -41,12 +41,6 @@ static int get_trip_level(struct thermal_zone_device *tz) return trip_level; } -static long get_target_state(struct thermal_zone_device *tz, - struct thermal_cooling_device *cdev, int percentage, int level) -{ - return (long)(percentage * level * cdev->max_state) / (100 * tz->num_trips); -} - /** * fair_share_throttle - throttles devices associated with the given zone * @tz: thermal_zone_device @@ -58,7 +52,7 @@ static long get_target_state(struct thermal_zone_device *tz, * * Parameters used for Throttling: * P1. max_state: Maximum throttle state exposed by the cooling device. - * P2. percentage[i]/100: + * P2. weight[i]/total_weight: * How 'effective' the 'i'th device is, in cooling the given zone. * P3. trip_level/max_no_of_trips: * This describes the extent to which the devices should be throttled. @@ -72,30 +66,34 @@ static void fair_share_throttle(struct thermal_zone_device *tz, { struct thermal_instance *instance; int total_weight = 0; - int total_instance = 0; + int nr_instances = 0; list_for_each_entry(instance, &tz->thermal_instances, tz_node) { if (instance->trip != trip) continue; total_weight += instance->weight; - total_instance++; + nr_instances++; } list_for_each_entry(instance, &tz->thermal_instances, tz_node) { - int percentage; struct thermal_cooling_device *cdev = instance->cdev; + u64 dividend; + u32 divisor; if (instance->trip != trip) continue; - if (!total_weight) - percentage = 100 / total_instance; - else - percentage = (instance->weight * 100) / total_weight; - - instance->target = get_target_state(tz, cdev, percentage, - trip_level); + dividend = trip_level; + dividend *= cdev->max_state; + divisor = tz->num_trips; + if (total_weight) { + dividend *= instance->weight; + divisor *= total_weight; + } else { + divisor *= nr_instances; + } + instance->target = div_u64(dividend, divisor); mutex_lock(&cdev->lock); __thermal_cdev_update(cdev); -- cgit 1.2.3-korg From c1beda1cfca53363ad3261c194df6cba4198f021 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 19:03:10 +0200 Subject: thermal: gov_user_space: Use .trip_crossed() instead of .throttle() Notifying user space about trip points that have not been crossed is not particularly useful, so modify the User Space governor to use the .trip_crossed() callback, which is only invoked for trips that have been crossed, instead of .throttle() that is invoked for all trips in a thermal zone every time the zone is updated. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Acked-by: Daniel Lezcano --- drivers/thermal/gov_user_space.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/thermal/gov_user_space.c b/drivers/thermal/gov_user_space.c index 7a1790b7e8f557..75137b419eb239 100644 --- a/drivers/thermal/gov_user_space.c +++ b/drivers/thermal/gov_user_space.c @@ -26,11 +26,13 @@ static int user_space_bind(struct thermal_zone_device *tz) * notify_user_space - Notifies user space about thermal events * @tz: thermal_zone_device * @trip: trip point + * @crossed_up: whether or not the trip has been crossed on the way up * * This function notifies the user space through UEvents. */ -static int notify_user_space(struct thermal_zone_device *tz, - const struct thermal_trip *trip) +static void notify_user_space(struct thermal_zone_device *tz, + const struct thermal_trip *trip, + bool crossed_up) { char *thermal_prop[5]; int i; @@ -46,13 +48,11 @@ static int notify_user_space(struct thermal_zone_device *tz, kobject_uevent_env(&tz->device.kobj, KOBJ_CHANGE, thermal_prop); for (i = 0; i < 4; ++i) kfree(thermal_prop[i]); - - return 0; } static struct thermal_governor thermal_gov_user_space = { .name = "user_space", - .throttle = notify_user_space, + .trip_crossed = notify_user_space, .bind_to_tz = user_space_bind, }; THERMAL_GOVERNOR_DECLARE(thermal_gov_user_space); -- cgit 1.2.3-korg From ad2f8bccd0e6e65af4e771e69cc2f2cfa69a6e07 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 19:42:35 +0200 Subject: thermal: core: Drop the .throttle() governor callback Since all of the governors in the tree have been switched over to using the new callbacks, either .trip_crossed() or .manage(), the .throttle() governor callback is not used any more, so drop it. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Acked-by: Daniel Lezcano --- drivers/thermal/thermal_core.c | 11 ----------- drivers/thermal/thermal_core.h | 4 ---- 2 files changed, 15 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 221e1924240d87..39ea842d883d9c 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -310,15 +310,6 @@ static struct thermal_governor *thermal_get_tz_governor(struct thermal_zone_devi return def_governor; } -static void handle_non_critical_trips(struct thermal_zone_device *tz, - const struct thermal_trip *trip) -{ - struct thermal_governor *governor = thermal_get_tz_governor(tz); - - if (governor->throttle) - governor->throttle(tz, trip); -} - void thermal_governor_update_tz(struct thermal_zone_device *tz, enum thermal_notify_event reason) { @@ -418,8 +409,6 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, if (trip->type == THERMAL_TRIP_CRITICAL || trip->type == THERMAL_TRIP_HOT) handle_critical_trips(tz, trip); - else - handle_non_critical_trips(tz, trip); } static void update_temperature(struct thermal_zone_device *tz) diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 95cbf7a3d169e2..d9785e5bbb08cd 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -32,8 +32,6 @@ struct thermal_trip_desc { * thermal zone. * @trip_crossed: called for trip points that have just been crossed * @manage: called on thermal zone temperature updates - * @throttle: callback called for every trip point even if temperature is - * below the trip point temperature * @update_tz: callback called when thermal zone internals have changed, e.g. * thermal cooling instance was added/removed * @governor_list: node in thermal_governor_list (in thermal_core.c) @@ -46,8 +44,6 @@ struct thermal_governor { const struct thermal_trip *trip, bool crossed_up); void (*manage)(struct thermal_zone_device *tz); - int (*throttle)(struct thermal_zone_device *tz, - const struct thermal_trip *trip); void (*update_tz)(struct thermal_zone_device *tz, enum thermal_notify_event reason); struct list_head governor_list; -- cgit 1.2.3-korg From 2ae0998c672ccf3bb1c480a5aca104ac8a98a287 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 19:44:34 +0200 Subject: thermal: core: Relocate critical and hot trip handling Modify handle_thermal_trip() to call handle_critical_trips() only after finding that the trip temperature has been crossed on the way up and remove the redundant temperature check from the latter. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Acked-by: Daniel Lezcano --- drivers/thermal/thermal_core.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 39ea842d883d9c..87b3cb8679d577 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -350,10 +350,6 @@ void thermal_zone_device_critical_reboot(struct thermal_zone_device *tz) static void handle_critical_trips(struct thermal_zone_device *tz, const struct thermal_trip *trip) { - /* If we have not crossed the trip_temp, we do not care. */ - if (trip->temperature <= 0 || tz->temperature < trip->temperature) - return; - trace_thermal_zone_trip(tz, thermal_zone_trip_id(tz, trip), trip->type); if (trip->type == THERMAL_TRIP_CRITICAL) @@ -405,10 +401,11 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, list_add_tail(&td->notify_list_node, way_up_list); td->notify_temp = trip->temperature; td->threshold -= trip->hysteresis; - } - if (trip->type == THERMAL_TRIP_CRITICAL || trip->type == THERMAL_TRIP_HOT) - handle_critical_trips(tz, trip); + if (trip->type == THERMAL_TRIP_CRITICAL || + trip->type == THERMAL_TRIP_HOT) + handle_critical_trips(tz, trip); + } } static void update_temperature(struct thermal_zone_device *tz) -- cgit 1.2.3-korg From 0a293c77580581c4b058eb40287acadac6ffd14a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 17 Apr 2024 15:09:46 +0200 Subject: thermal/debugfs: Avoid excessive updates of trip point statistics Since thermal_debug_update_temp() is called before invoking thermal_debug_tz_trip_down() for the trips that were crossed by the zone temperature on the way up, it updates the statistics for them as though the current zone temperature was above the low temperature of each of them. However, if a given trip has just been crossed on the way down, the zone temperature is in fact below its low temperature, but this is handled by thermal_debug_tz_trip_down() running after the update of the trip statistics. The remedy is to call thermal_debug_update_temp() after thermal_debug_tz_trip_down() has been invoked for all of the trips in question, but then thermal_debug_tz_trip_up() needs to be adjusted, so it does not update the statistics for the trips that has just been crossed on the way up, as that will be taken care of by thermal_debug_update_temp() down the road. Modify the code accordingly. Fixes: 7ef01f228c9f ("thermal/debugfs: Add thermal debugfs information for mitigation episodes") Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Acked-by: Daniel Lezcano --- drivers/thermal/thermal_core.c | 3 ++- drivers/thermal/thermal_debugfs.c | 7 ------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 87b3cb8679d577..914aaee6b39afe 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -427,7 +427,6 @@ static void update_temperature(struct thermal_zone_device *tz) trace_thermal_temperature(tz); thermal_genl_sampling_temp(tz->id, temp); - thermal_debug_update_temp(tz); } static void thermal_zone_device_check(struct work_struct *work) @@ -505,6 +504,8 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, if (governor->manage) governor->manage(tz); + thermal_debug_update_temp(tz); + monitor_thermal_zone(tz); } diff --git a/drivers/thermal/thermal_debugfs.c b/drivers/thermal/thermal_debugfs.c index 40ea0071a691e0..878f047e72acfd 100644 --- a/drivers/thermal/thermal_debugfs.c +++ b/drivers/thermal/thermal_debugfs.c @@ -545,7 +545,6 @@ void thermal_debug_tz_trip_up(struct thermal_zone_device *tz, struct tz_episode *tze; struct tz_debugfs *tz_dbg; struct thermal_debugfs *thermal_dbg = tz->debugfs; - int temperature = tz->temperature; int trip_id = thermal_zone_trip_id(tz, trip); ktime_t now = ktime_get(); @@ -614,12 +613,6 @@ void thermal_debug_tz_trip_up(struct thermal_zone_device *tz, tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node); tze->trip_stats[trip_id].timestamp = now; - tze->trip_stats[trip_id].max = max(tze->trip_stats[trip_id].max, temperature); - tze->trip_stats[trip_id].min = min(tze->trip_stats[trip_id].min, temperature); - tze->trip_stats[trip_id].count++; - tze->trip_stats[trip_id].avg = tze->trip_stats[trip_id].avg + - (temperature - tze->trip_stats[trip_id].avg) / - tze->trip_stats[trip_id].count; unlock: mutex_unlock(&thermal_dbg->lock); -- cgit 1.2.3-korg From e271f9974d7e35a6eb05224660b2084035085173 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 17 Apr 2024 15:10:34 +0200 Subject: thermal/debugfs: Clean up thermal_debug_update_temp() Notice that it is not necessary to compute tze in every iteration of the for () loop in thermal_debug_update_temp() because it is the same for all trips, so compute it once before the loop starts. Also use a trip_stats local variable to make the code in that loop easier to follow and move the trip_id variable definition into that loop because it is not used elsewhere in the function. While at it, change to order of local variable definitions in the function to follow the reverse-xmas-tree pattern. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Acked-by: Daniel Lezcano --- drivers/thermal/thermal_debugfs.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/thermal/thermal_debugfs.c b/drivers/thermal/thermal_debugfs.c index 878f047e72acfd..4f7b50e08c857d 100644 --- a/drivers/thermal/thermal_debugfs.c +++ b/drivers/thermal/thermal_debugfs.c @@ -679,9 +679,9 @@ out: void thermal_debug_update_temp(struct thermal_zone_device *tz) { struct thermal_debugfs *thermal_dbg = tz->debugfs; - struct tz_episode *tze; struct tz_debugfs *tz_dbg; - int trip_id, i; + struct tz_episode *tze; + int i; if (!thermal_dbg) return; @@ -693,15 +693,16 @@ void thermal_debug_update_temp(struct thermal_zone_device *tz) if (!tz_dbg->nr_trips) goto out; + tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node); + for (i = 0; i < tz_dbg->nr_trips; i++) { - trip_id = tz_dbg->trips_crossed[i]; - tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node); - tze->trip_stats[trip_id].count++; - tze->trip_stats[trip_id].max = max(tze->trip_stats[trip_id].max, tz->temperature); - tze->trip_stats[trip_id].min = min(tze->trip_stats[trip_id].min, tz->temperature); - tze->trip_stats[trip_id].avg = tze->trip_stats[trip_id].avg + - (tz->temperature - tze->trip_stats[trip_id].avg) / - tze->trip_stats[trip_id].count; + int trip_id = tz_dbg->trips_crossed[i]; + struct trip_stats *trip_stats = &tze->trip_stats[trip_id]; + + trip_stats->max = max(trip_stats->max, tz->temperature); + trip_stats->min = min(trip_stats->min, tz->temperature); + trip_stats->avg += (tz->temperature - trip_stats->avg) / + ++trip_stats->count; } out: mutex_unlock(&thermal_dbg->lock); -- cgit 1.2.3-korg From 8dff6e8438351c627289fd06893c36f3bd9666e5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 17 Apr 2024 15:11:50 +0200 Subject: thermal/debugfs: Rename thermal_debug_update_temp() to thermal_debug_update_trip_stats() Rename thermal_debug_update_temp() to thermal_debug_update_trip_stats() which is a better match for the purpose of the function. No functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Acked-by: Daniel Lezcano --- drivers/thermal/thermal_core.c | 2 +- drivers/thermal/thermal_debugfs.c | 2 +- drivers/thermal/thermal_debugfs.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 914aaee6b39afe..4debd3c9f32717 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -504,7 +504,7 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, if (governor->manage) governor->manage(tz); - thermal_debug_update_temp(tz); + thermal_debug_update_trip_stats(tz); monitor_thermal_zone(tz); } diff --git a/drivers/thermal/thermal_debugfs.c b/drivers/thermal/thermal_debugfs.c index 4f7b50e08c857d..357cd5f2de64d8 100644 --- a/drivers/thermal/thermal_debugfs.c +++ b/drivers/thermal/thermal_debugfs.c @@ -676,7 +676,7 @@ out: mutex_unlock(&thermal_dbg->lock); } -void thermal_debug_update_temp(struct thermal_zone_device *tz) +void thermal_debug_update_trip_stats(struct thermal_zone_device *tz) { struct thermal_debugfs *thermal_dbg = tz->debugfs; struct tz_debugfs *tz_dbg; diff --git a/drivers/thermal/thermal_debugfs.h b/drivers/thermal/thermal_debugfs.h index 155b9af5fe8708..027c145501dde4 100644 --- a/drivers/thermal/thermal_debugfs.h +++ b/drivers/thermal/thermal_debugfs.h @@ -11,7 +11,7 @@ void thermal_debug_tz_trip_up(struct thermal_zone_device *tz, const struct thermal_trip *trip); void thermal_debug_tz_trip_down(struct thermal_zone_device *tz, const struct thermal_trip *trip); -void thermal_debug_update_temp(struct thermal_zone_device *tz); +void thermal_debug_update_trip_stats(struct thermal_zone_device *tz); #else static inline void thermal_debug_init(void) {} static inline void thermal_debug_cdev_add(struct thermal_cooling_device *cdev) {} @@ -24,5 +24,5 @@ static inline void thermal_debug_tz_trip_up(struct thermal_zone_device *tz, const struct thermal_trip *trip) {}; static inline void thermal_debug_tz_trip_down(struct thermal_zone_device *tz, const struct thermal_trip *trip) {} -static inline void thermal_debug_update_temp(struct thermal_zone_device *tz) {} +static inline void thermal_debug_update_trip_stats(struct thermal_zone_device *tz) {} #endif /* CONFIG_THERMAL_DEBUGFS */ -- cgit 1.2.3-korg From a6258fde8de34b2bfd7e1d4e55df261426e49071 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 17 Apr 2024 17:51:29 +0200 Subject: thermal/debugfs: Make tze_seq_show() skip invalid trips and trips with no stats Currently, tze_seq_show() output includes all of the trips in the zone except for critical ones, including invalid trips and trips with no stats which is confusing. Make it skip the trips for which there is not mitigation information. Signed-off-by: Rafael J. Wysocki Acked-by: Daniel Lezcano --- drivers/thermal/thermal_debugfs.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/thermal/thermal_debugfs.c b/drivers/thermal/thermal_debugfs.c index 357cd5f2de64d8..d9f33df2ad2d1c 100644 --- a/drivers/thermal/thermal_debugfs.c +++ b/drivers/thermal/thermal_debugfs.c @@ -754,6 +754,11 @@ static int tze_seq_show(struct seq_file *s, void *v) for_each_trip_desc(tz, td) { const struct thermal_trip *trip = &td->trip; + struct trip_stats *trip_stats; + + /* Skip invalid trips. */ + if (trip->temperature == THERMAL_TEMP_INVALID) + continue; /* * There is no possible mitigation happening at the @@ -763,6 +768,13 @@ static int tze_seq_show(struct seq_file *s, void *v) if (trip->type == THERMAL_TRIP_CRITICAL) continue; + trip_id = thermal_zone_trip_id(tz, trip); + trip_stats = &tze->trip_stats[trip_id]; + + /* Skip trips without any stats. */ + if (trip_stats->min > trip_stats->max) + continue; + if (trip->type == THERMAL_TRIP_PASSIVE) type = "passive"; else if (trip->type == THERMAL_TRIP_ACTIVE) @@ -770,17 +782,15 @@ static int tze_seq_show(struct seq_file *s, void *v) else type = "hot"; - trip_id = thermal_zone_trip_id(tz, trip); - seq_printf(s, "| %*d | %*s | %*d | %*d | %*lld | %*d | %*d | %*d |\n", 4 , trip_id, 8, type, 9, trip->temperature, 9, trip->hysteresis, - 10, ktime_to_ms(tze->trip_stats[trip_id].duration), - 9, tze->trip_stats[trip_id].avg, - 9, tze->trip_stats[trip_id].min, - 9, tze->trip_stats[trip_id].max); + 10, ktime_to_ms(trip_stats->duration), + 9, trip_stats->avg, + 9, trip_stats->min, + 9, trip_stats->max); } return 0; -- cgit 1.2.3-korg From f831892e2351dc13b37b4c1fc60472be781a15be Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 23 Apr 2024 21:01:15 +0200 Subject: thermal: core: Introduce thermal_governor_trip_crossed() Add a wrapper around the .trip_crossed() governor callback invocation to reduce code duplications slightly and improve the code layout in __thermal_zone_device_update(). No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba --- drivers/thermal/thermal_core.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 4debd3c9f32717..64036372e240c4 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -450,6 +450,15 @@ static void thermal_zone_device_init(struct thermal_zone_device *tz) pos->initialized = false; } +static void thermal_governor_trip_crossed(struct thermal_governor *governor, + struct thermal_zone_device *tz, + const struct thermal_trip *trip, + bool crossed_up) +{ + if (governor->trip_crossed) + governor->trip_crossed(tz, trip, crossed_up); +} + static int thermal_trip_notify_cmp(void *ascending, const struct list_head *a, const struct list_head *b) { @@ -489,16 +498,14 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, list_for_each_entry(td, &way_up_list, notify_list_node) { thermal_notify_tz_trip_up(tz, &td->trip); thermal_debug_tz_trip_up(tz, &td->trip); - if (governor->trip_crossed) - governor->trip_crossed(tz, &td->trip, true); + thermal_governor_trip_crossed(governor, tz, &td->trip, true); } list_sort(NULL, &way_down_list, thermal_trip_notify_cmp); list_for_each_entry(td, &way_down_list, notify_list_node) { thermal_notify_tz_trip_down(tz, &td->trip); thermal_debug_tz_trip_down(tz, &td->trip); - if (governor->trip_crossed) - governor->trip_crossed(tz, &td->trip, false); + thermal_governor_trip_crossed(governor, tz, &td->trip, false); } if (governor->manage) -- cgit 1.2.3-korg From 72c1afffa4c645fe0e0f1c03e5f34395ed65b5f4 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 25 Apr 2024 19:52:12 +0200 Subject: thermal/debugfs: Free all thermal zone debug memory on zone removal Because thermal_debug_tz_remove() does not free all memory allocated for thermal zone diagnostics, some of that memory becomes unreachable after freeing the thermal zone's struct thermal_debugfs object. Address this by making thermal_debug_tz_remove() free all of the memory in question. Fixes: 7ef01f228c9f ("thermal/debugfs: Add thermal debugfs information for mitigation episodes") Cc :6.8+ # 6.8+ Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba --- drivers/thermal/thermal_debugfs.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/thermal/thermal_debugfs.c b/drivers/thermal/thermal_debugfs.c index d78d54ae2605e8..8d66b3a96be1a1 100644 --- a/drivers/thermal/thermal_debugfs.c +++ b/drivers/thermal/thermal_debugfs.c @@ -826,15 +826,28 @@ void thermal_debug_tz_add(struct thermal_zone_device *tz) void thermal_debug_tz_remove(struct thermal_zone_device *tz) { struct thermal_debugfs *thermal_dbg = tz->debugfs; + struct tz_episode *tze, *tmp; + struct tz_debugfs *tz_dbg; + int *trips_crossed; if (!thermal_dbg) return; + tz_dbg = &thermal_dbg->tz_dbg; + mutex_lock(&thermal_dbg->lock); + trips_crossed = tz_dbg->trips_crossed; + + list_for_each_entry_safe(tze, tmp, &tz_dbg->tz_episodes, node) { + list_del(&tze->node); + kfree(tze); + } + tz->debugfs = NULL; mutex_unlock(&thermal_dbg->lock); thermal_debugfs_remove_id(thermal_dbg); + kfree(trips_crossed); } -- cgit 1.2.3-korg From c7f7c37271787a7f77d7eedc132b0b419a76b4c8 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 25 Apr 2024 20:00:33 +0200 Subject: thermal/debugfs: Fix two locking issues with thermal zone debug With the current thermal zone locking arrangement in the debugfs code, user space can open the "mitigations" file for a thermal zone before the zone's debugfs pointer is set which will result in a NULL pointer dereference in tze_seq_start(). Moreover, thermal_debug_tz_remove() is not called under the thermal zone lock, so it can run in parallel with the other functions accessing the thermal zone's struct thermal_debugfs object. Then, it may clear tz->debugfs after one of those functions has checked it and the struct thermal_debugfs object may be freed prematurely. To address the first problem, pass a pointer to the thermal zone's struct thermal_debugfs object to debugfs_create_file() in thermal_debug_tz_add() and make tze_seq_start(), tze_seq_next(), tze_seq_stop(), and tze_seq_show() retrieve it from s->private instead of a pointer to the thermal zone object. This will ensure that tz_debugfs will be valid across the "mitigations" file accesses until thermal_debugfs_remove_id() called by thermal_debug_tz_remove() removes that file. To address the second problem, use tz->lock in thermal_debug_tz_remove() around the tz->debugfs value check (in case the same thermal zone is removed at the same time in two different threads) and its reset to NULL. Fixes: 7ef01f228c9f ("thermal/debugfs: Add thermal debugfs information for mitigation episodes") Cc :6.8+ # 6.8+ Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba --- drivers/thermal/thermal_debugfs.c | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/drivers/thermal/thermal_debugfs.c b/drivers/thermal/thermal_debugfs.c index 8d66b3a96be1a1..1fe2914a8853bb 100644 --- a/drivers/thermal/thermal_debugfs.c +++ b/drivers/thermal/thermal_debugfs.c @@ -139,11 +139,13 @@ struct tz_episode { * we keep track of the current position in the history array. * * @tz_episodes: a list of thermal mitigation episodes + * @tz: thermal zone this object belongs to * @trips_crossed: an array of trip points crossed by id * @nr_trips: the number of trip points currently being crossed */ struct tz_debugfs { struct list_head tz_episodes; + struct thermal_zone_device *tz; int *trips_crossed; int nr_trips; }; @@ -716,8 +718,7 @@ out: static void *tze_seq_start(struct seq_file *s, loff_t *pos) { - struct thermal_zone_device *tz = s->private; - struct thermal_debugfs *thermal_dbg = tz->debugfs; + struct thermal_debugfs *thermal_dbg = s->private; struct tz_debugfs *tz_dbg = &thermal_dbg->tz_dbg; mutex_lock(&thermal_dbg->lock); @@ -727,8 +728,7 @@ static void *tze_seq_start(struct seq_file *s, loff_t *pos) static void *tze_seq_next(struct seq_file *s, void *v, loff_t *pos) { - struct thermal_zone_device *tz = s->private; - struct thermal_debugfs *thermal_dbg = tz->debugfs; + struct thermal_debugfs *thermal_dbg = s->private; struct tz_debugfs *tz_dbg = &thermal_dbg->tz_dbg; return seq_list_next(v, &tz_dbg->tz_episodes, pos); @@ -736,15 +736,15 @@ static void *tze_seq_next(struct seq_file *s, void *v, loff_t *pos) static void tze_seq_stop(struct seq_file *s, void *v) { - struct thermal_zone_device *tz = s->private; - struct thermal_debugfs *thermal_dbg = tz->debugfs; + struct thermal_debugfs *thermal_dbg = s->private; mutex_unlock(&thermal_dbg->lock); } static int tze_seq_show(struct seq_file *s, void *v) { - struct thermal_zone_device *tz = s->private; + struct thermal_debugfs *thermal_dbg = s->private; + struct thermal_zone_device *tz = thermal_dbg->tz_dbg.tz; struct thermal_trip *trip; struct tz_episode *tze; const char *type; @@ -810,6 +810,8 @@ void thermal_debug_tz_add(struct thermal_zone_device *tz) tz_dbg = &thermal_dbg->tz_dbg; + tz_dbg->tz = tz; + tz_dbg->trips_crossed = kzalloc(sizeof(int) * tz->num_trips, GFP_KERNEL); if (!tz_dbg->trips_crossed) { thermal_debugfs_remove_id(thermal_dbg); @@ -818,20 +820,30 @@ void thermal_debug_tz_add(struct thermal_zone_device *tz) INIT_LIST_HEAD(&tz_dbg->tz_episodes); - debugfs_create_file("mitigations", 0400, thermal_dbg->d_top, tz, &tze_fops); + debugfs_create_file("mitigations", 0400, thermal_dbg->d_top, + thermal_dbg, &tze_fops); tz->debugfs = thermal_dbg; } void thermal_debug_tz_remove(struct thermal_zone_device *tz) { - struct thermal_debugfs *thermal_dbg = tz->debugfs; + struct thermal_debugfs *thermal_dbg; struct tz_episode *tze, *tmp; struct tz_debugfs *tz_dbg; int *trips_crossed; - if (!thermal_dbg) + mutex_lock(&tz->lock); + + thermal_dbg = tz->debugfs; + if (!thermal_dbg) { + mutex_unlock(&tz->lock); return; + } + + tz->debugfs = NULL; + + mutex_unlock(&tz->lock); tz_dbg = &thermal_dbg->tz_dbg; @@ -844,8 +856,6 @@ void thermal_debug_tz_remove(struct thermal_zone_device *tz) kfree(tze); } - tz->debugfs = NULL; - mutex_unlock(&thermal_dbg->lock); thermal_debugfs_remove_id(thermal_dbg); -- cgit 1.2.3-korg From d351eb0ab04c3e8109895fc33250cebbce9c11da Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 26 Apr 2024 11:10:10 +0200 Subject: thermal/debugfs: Prevent use-after-free from occurring after cdev removal Since thermal_debug_cdev_remove() does not run under cdev->lock, it can run in parallel with thermal_debug_cdev_state_update() and it may free the struct thermal_debugfs object used by the latter after it has been checked against NULL. If that happens, thermal_debug_cdev_state_update() will access memory that has been freed already causing the kernel to crash. Address this by using cdev->lock in thermal_debug_cdev_remove() around the cdev->debugfs value check (in case the same cdev is removed at the same time in two different threads) and its reset to NULL. Fixes: 755113d76786 ("thermal/debugfs: Add thermal cooling device debugfs information") Cc :6.8+ # 6.8+ Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba --- drivers/thermal/thermal_debugfs.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/thermal/thermal_debugfs.c b/drivers/thermal/thermal_debugfs.c index 1fe2914a8853bb..5693cc8b231aac 100644 --- a/drivers/thermal/thermal_debugfs.c +++ b/drivers/thermal/thermal_debugfs.c @@ -505,15 +505,23 @@ void thermal_debug_cdev_add(struct thermal_cooling_device *cdev) */ void thermal_debug_cdev_remove(struct thermal_cooling_device *cdev) { - struct thermal_debugfs *thermal_dbg = cdev->debugfs; + struct thermal_debugfs *thermal_dbg; - if (!thermal_dbg) + mutex_lock(&cdev->lock); + + thermal_dbg = cdev->debugfs; + if (!thermal_dbg) { + mutex_unlock(&cdev->lock); return; + } + + cdev->debugfs = NULL; + + mutex_unlock(&cdev->lock); mutex_lock(&thermal_dbg->lock); thermal_debugfs_cdev_clear(&thermal_dbg->cdev_dbg); - cdev->debugfs = NULL; mutex_unlock(&thermal_dbg->lock); -- cgit 1.2.3-korg From e97d05b5e1bdaab61489942d1492bcd5eca9f0d5 Mon Sep 17 00:00:00 2001 From: John Watts Date: Wed, 24 Apr 2024 08:16:43 +1000 Subject: Documentation: firmware-guide: ACPI: Fix namespace typo The ACPI namespace has always started with LNXSYSTM, not LNXSYSTEM. Fix the documentation accordingly. Signed-off-by: John Watts Signed-off-by: Rafael J. Wysocki --- Documentation/firmware-guide/acpi/namespace.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/firmware-guide/acpi/namespace.rst b/Documentation/firmware-guide/acpi/namespace.rst index 4ef963679a3d07..5021843b526b6c 100644 --- a/Documentation/firmware-guide/acpi/namespace.rst +++ b/Documentation/firmware-guide/acpi/namespace.rst @@ -15,7 +15,7 @@ ACPI Device Tree - Representation of ACPI Namespace Abstract ======== The Linux ACPI subsystem converts ACPI namespace objects into a Linux -device tree under the /sys/devices/LNXSYSTEM:00 and updates it upon +device tree under the /sys/devices/LNXSYSTM:00 and updates it upon receiving ACPI hotplug notification events. For each device object in this hierarchy there is a corresponding symbolic link in the /sys/bus/acpi/devices. @@ -326,7 +326,7 @@ example ACPI namespace illustrated in Figure 2 with the addition of fixed PWR_BUTTON/SLP_BUTTON devices is shown below:: +--------------+---+-----------------+ - | LNXSYSTEM:00 | \ | acpi:LNXSYSTEM: | + | LNXSYSTM:00 | \ | acpi:LNXSYSTM: | +--------------+---+-----------------+ | | +-------------+-----+----------------+ -- cgit 1.2.3-korg From 49c192d2af8cf1364a8130652cfc9ec5cda775f2 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 22 Apr 2024 21:13:42 +0300 Subject: ACPI: property: Add reference to UEFI DSD Guide The UEFI DSD Guide specifies a number of GUIDs supported by the _DSD parser. Point to the DSD Guide in the documentation. Signed-off-by: Sakari Ailus Reviewed-by: Andy Shevchenko Signed-off-by: Rafael J. Wysocki --- drivers/acpi/property.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 2b73580c9f36d2..80a52a4e66dd16 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -31,9 +31,14 @@ static int acpi_data_get_property_array(const struct acpi_device_data *data, * not defined without a warning. For instance if any of the properties * from different GUID appear in a property list of another, it will be * accepted by the kernel. Firmware validation tools should catch these. + * + * References: + * + * [1] UEFI DSD Guide. + * https://github.com/UEFI/DSD-Guide/blob/main/src/dsd-guide.adoc */ static const guid_t prp_guids[] = { - /* ACPI _DSD device properties GUID: daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */ + /* ACPI _DSD device properties GUID [1]: daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */ GUID_INIT(0xdaffd814, 0x6eba, 0x4d8c, 0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01), /* Hotplug in D3 GUID: 6211e2c0-58a3-4af3-90e1-927a4e0c55a4 */ @@ -53,12 +58,12 @@ static const guid_t prp_guids[] = { 0xa5, 0x61, 0x99, 0xa5, 0x18, 0x97, 0x62, 0xd0), }; -/* ACPI _DSD data subnodes GUID: dbb8e3e6-5886-4ba6-8795-1319f52a966b */ +/* ACPI _DSD data subnodes GUID [1]: dbb8e3e6-5886-4ba6-8795-1319f52a966b */ static const guid_t ads_guid = GUID_INIT(0xdbb8e3e6, 0x5886, 0x4ba6, 0x87, 0x95, 0x13, 0x19, 0xf5, 0x2a, 0x96, 0x6b); -/* ACPI _DSD data buffer GUID: edb12dd0-363d-4085-a3d2-49522ca160c4 */ +/* ACPI _DSD data buffer GUID [1]: edb12dd0-363d-4085-a3d2-49522ca160c4 */ static const guid_t buffer_prop_guid = GUID_INIT(0xedb12dd0, 0x363d, 0x4085, 0xa3, 0xd2, 0x49, 0x52, 0x2c, 0xa1, 0x60, 0xc4); -- cgit 1.2.3-korg From d7bd0aeb5ab6609b71ca87db4ec6d8bba24b2209 Mon Sep 17 00:00:00 2001 From: Chen Yu Date: Fri, 26 Apr 2024 17:48:50 +0800 Subject: ACPI: tools: pfrut: Print the update_cap field during capability query There is request from the end user to print this field to better query what type of update capability is supported on this platform. Signed-off-by: Chen Yu Signed-off-by: Rafael J. Wysocki --- tools/power/acpi/tools/pfrut/pfrut.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/power/acpi/tools/pfrut/pfrut.c b/tools/power/acpi/tools/pfrut/pfrut.c index 388c9e3ad0407b..44a9ecbd91e883 100644 --- a/tools/power/acpi/tools/pfrut/pfrut.c +++ b/tools/power/acpi/tools/pfrut/pfrut.c @@ -174,6 +174,8 @@ void print_cap(struct pfru_update_cap_info *cap) exit(1); } + printf("update capability:%d\n", cap->update_cap); + uuid_unparse(cap->code_type, uuid); printf("code injection image type:%s\n", uuid); printf("fw_version:%d\n", cap->fw_version); -- cgit 1.2.3-korg