From cf580ad490514cca4abbfef710fe4099738abfbd Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Fri, 23 Feb 2024 16:59:40 +0100 Subject: [PATCH 01/72] 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 76a231a29654..d407130563e5 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 @@ static int thermal_genl_cmd_doit(struct sk_buff *skb, 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 93a927e144d5..e01221e8816b 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; From afdaff3706918b7414e0741c4c5c20a12726a207 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Fri, 23 Feb 2024 16:59:41 +0100 Subject: [PATCH 02/72] 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 d407130563e5..bef14ce69ec4 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); } From b33f3d2677b8ddd7a3aba2b02497422a1d2c2a01 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Fri, 23 Feb 2024 16:59:42 +0100 Subject: [PATCH 03/72] 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 40d664a66cdc..fbc7f0cd83d7 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]; From 03fa9a3ad1d61992a2105aeb1062b349f1a85012 Mon Sep 17 00:00:00 2001 From: Justin Stitt Date: Mon, 18 Mar 2024 22:36:10 +0000 Subject: [PATCH 04/72] 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 dc519a665c18..4b4a4d63e61f 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; From f1164d333cc3c063cfdf7d03ddbca4f93a5a5c41 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 3 Apr 2024 20:11:10 +0200 Subject: [PATCH 05/72] 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 5436aa58d41e..aad98e06d66b 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, From 053b852c46626250b5f7da43ba8574da756db022 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 3 Apr 2024 20:12:02 +0200 Subject: [PATCH 06/72] 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 aad98e06d66b..ee2fb4e63d14 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--; } From daeeb032f42d066a49e07b7f6effc9f51b7a5479 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 2 Apr 2024 20:56:43 +0200 Subject: [PATCH 07/72] 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 4da25a0009d7..6ef8cfde7749 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 e25e48d76aa7..ac1d02193a1b 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 34a31bc72023..fc6ff0a4faa1 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 0d8a42bb7ce8..4ad9c671bc13 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 c617e8b9f0dd..78c4cb30e91d 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 c5a057b59c42..d9f4e26ec125 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 76a231a29654..0ef6368da766 100644 --- a/drivers/thermal/thermal_netlink.c +++ b/drivers/thermal/thermal_netlink.c @@ -445,7 +445,7 @@ static int thermal_genl_cmd_tz_get_id(struct param *p) 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 5b533fa40437..88211ccdfbd6 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 497abf0d47ca..7cf43b697272 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 c33f50177f51..67bd13303349 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); }; /** From b1ae92dcfa8ed7d5044c525c3a868fcb4d0f9c0e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 2 Apr 2024 20:57:57 +0200 Subject: [PATCH 08/72] 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 4ad9c671bc13..5dd3be585e9e 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 459c8ce6cf3b..88a962f560f2 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 b16b5dd863d9..a82821eebc88 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 67bd13303349..62b174528cfe 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 From f99c1b87a902fcc81df569f2ff939d47880cd741 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 2 Apr 2024 20:59:01 +0200 Subject: [PATCH 09/72] 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 fc6ff0a4faa1..c4bc7973a6cd 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); From 9ad18043fb35feb1d82c1e594575346f16d47dc7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 2 Apr 2024 21:02:10 +0200 Subject: [PATCH 10/72] 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 c4bc7973a6cd..28f5d9b06341 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) From 7454f2c42cce10a74312343b66aa2c3dee05d868 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 2 Apr 2024 21:03:36 +0200 Subject: [PATCH 11/72] 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 28f5d9b06341..58e60bcdc0a5 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 5dd3be585e9e..9d3ef1e0645f 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; }; From 0444d574fbc3d3af0e426f7c2b72f5830269f096 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 4 Apr 2024 21:27:07 +0200 Subject: [PATCH 12/72] 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 9d3ef1e0645f..b461d9583834 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 62b174528cfe..f1155c0439c4 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; From 80f5fd45c764816fe9dbe8e94bd2677b4a8a3f4d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 18:10:53 +0200 Subject: [PATCH 13/72] 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 58e60bcdc0a5..62fe062d7ff5 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 b461d9583834..9a3585492558 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, From c0f14ec95262cdcf557016f84b87e45f54e0b881 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Sat, 9 Mar 2024 14:15:02 +0100 Subject: [PATCH 14/72] dt-bindings: thermal: lmh: Add QCM2290 compatible Document the QCM2290 LMH. Signed-off-by: Konrad Dybcio Reviewed-by: Krzysztof Kozlowski Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20240308-topic-rb1_lmh-v2-1-bac3914b0fe3@linaro.org --- .../devicetree/bindings/thermal/qcom-lmh.yaml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/thermal/qcom-lmh.yaml b/Documentation/devicetree/bindings/thermal/qcom-lmh.yaml index 5ff72ce5c887..1175bb358382 100644 --- a/Documentation/devicetree/bindings/thermal/qcom-lmh.yaml +++ b/Documentation/devicetree/bindings/thermal/qcom-lmh.yaml @@ -17,10 +17,14 @@ description: properties: compatible: - enum: - - qcom,sc8180x-lmh - - qcom,sdm845-lmh - - qcom,sm8150-lmh + oneOf: + - enum: + - qcom,sc8180x-lmh + - qcom,sdm845-lmh + - qcom,sm8150-lmh + - items: + - const: qcom,qcm2290-lmh + - const: qcom,sm8150-lmh reg: items: From d9d3490c48df572edefc0b64655259eefdcbb9be Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Sat, 9 Mar 2024 14:15:03 +0100 Subject: [PATCH 15/72] thermal/drivers/qcom/lmh: Check for SCM availability at probe Up until now, the necessary scm availability check has not been performed, leading to possible null pointer dereferences (which did happen for me on RB1). Fix that. Fixes: 53bca371cdf7 ("thermal/drivers/qcom: Add support for LMh driver") Cc: Reviewed-by: Dmitry Baryshkov Reviewed-by: Bjorn Andersson Signed-off-by: Konrad Dybcio Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20240308-topic-rb1_lmh-v2-2-bac3914b0fe3@linaro.org --- drivers/thermal/qcom/lmh.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/thermal/qcom/lmh.c b/drivers/thermal/qcom/lmh.c index f6edb12ec004..5225b3621a56 100644 --- a/drivers/thermal/qcom/lmh.c +++ b/drivers/thermal/qcom/lmh.c @@ -95,6 +95,9 @@ static int lmh_probe(struct platform_device *pdev) unsigned int enable_alg; u32 node_id; + if (!qcom_scm_is_available()) + return -EPROBE_DEFER; + lmh_data = devm_kzalloc(dev, sizeof(*lmh_data), GFP_KERNEL); if (!lmh_data) return -ENOMEM; From 58b1569244fea51cbf675774fad9423359d76c57 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 20 Mar 2024 11:49:39 +0100 Subject: [PATCH 16/72] thermal/drivers/armada: Simplify name sanitization Simplify the code by using the helper we have for doing exactly this. Signed-off-by: Rasmus Villemoes Reviewed-by: Miquel Raynal Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20240320104940.65031-1-linux@rasmusvillemoes.dk --- drivers/thermal/armada_thermal.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/thermal/armada_thermal.c b/drivers/thermal/armada_thermal.c index f783547ef964..fdcb077cfd54 100644 --- a/drivers/thermal/armada_thermal.c +++ b/drivers/thermal/armada_thermal.c @@ -763,7 +763,6 @@ static void armada_set_sane_name(struct platform_device *pdev, struct armada_thermal_priv *priv) { const char *name = dev_name(&pdev->dev); - char *insane_char; if (strlen(name) > THERMAL_NAME_LENGTH) { /* @@ -781,12 +780,8 @@ static void armada_set_sane_name(struct platform_device *pdev, /* Save the name locally */ strscpy(priv->zone_name, name, THERMAL_NAME_LENGTH); - /* Then check there are no '-' or hwmon core will complain */ - do { - insane_char = strpbrk(priv->zone_name, "-"); - if (insane_char) - *insane_char = '_'; - } while (insane_char); + /* Then ensure there are no '-' or hwmon core will complain */ + strreplace(priv->zone_name, '-', '_'); } /* From ff96922d33dfeb3cfe7c4fac051267155722c1ae Mon Sep 17 00:00:00 2001 From: Raphael Gallais-Pou Date: Wed, 20 Mar 2024 22:33:48 +0100 Subject: [PATCH 17/72] dt-bindings: thermal: convert st,stih407-thermal to DT schema 'st,passive_colling_temp' does not appear in the device-tree, 'reg' and '#thermal-sensor-cells' are also missing in the device description. Convert st,stih407-thermal binding to DT schema format in order to clean unused 'st,passive_cooling_temp' and add missing properties. Signed-off-by: Raphael Gallais-Pou Reviewed-by: Krzysztof Kozlowski Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20240320-thermal-v3-1-700296694c4a@gmail.com --- .../bindings/thermal/st,stih407-thermal.yaml | 58 +++++++++++++++++++ .../bindings/thermal/st-thermal.txt | 32 ---------- 2 files changed, 58 insertions(+), 32 deletions(-) create mode 100644 Documentation/devicetree/bindings/thermal/st,stih407-thermal.yaml delete mode 100644 Documentation/devicetree/bindings/thermal/st-thermal.txt diff --git a/Documentation/devicetree/bindings/thermal/st,stih407-thermal.yaml b/Documentation/devicetree/bindings/thermal/st,stih407-thermal.yaml new file mode 100644 index 000000000000..9f6fc5c95c55 --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/st,stih407-thermal.yaml @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/thermal/st,stih407-thermal.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: STMicroelectronics STi digital thermal sensor (DTS) + +maintainers: + - Patrice Chotard + - Lee Jones + +allOf: + - $ref: thermal-sensor.yaml + +properties: + compatible: + const: st,stih407-thermal + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: thermal + + interrupts: + description: + For thermal sensors for which no interrupt has been defined, a polling + delay of 1000ms will be used to read the temperature from device. + maxItems: 1 + + '#thermal-sensor-cells': + const: 0 + +required: + - compatible + - reg + - clocks + - clock-names + +unevaluatedProperties: false + +examples: + - | + #include + temperature-sensor@91a0000 { + compatible = "st,stih407-thermal"; + reg = <0x91a0000 0x28>; + clock-names = "thermal"; + clocks = <&CLK_SYSIN>; + interrupts = ; + #thermal-sensor-cells = <0>; + }; +... diff --git a/Documentation/devicetree/bindings/thermal/st-thermal.txt b/Documentation/devicetree/bindings/thermal/st-thermal.txt deleted file mode 100644 index a2f939137e35..000000000000 --- a/Documentation/devicetree/bindings/thermal/st-thermal.txt +++ /dev/null @@ -1,32 +0,0 @@ -Binding for Thermal Sensor driver for STMicroelectronics STi series of SoCs. - -Required parameters: -------------------- - -compatible : Should be "st,stih407-thermal" - -clock-names : Should be "thermal". - See: Documentation/devicetree/bindings/resource-names.txt -clocks : Phandle of the clock used by the thermal sensor. - See: Documentation/devicetree/bindings/clock/clock-bindings.txt - -Optional parameters: -------------------- - -reg : For non-sysconf based sensors, this should be the physical base - address and length of the sensor's registers. -interrupts : Standard way to define interrupt number. - NB: For thermal sensor's for which no interrupt has been - defined, a polling delay of 1000ms will be used to read the - temperature from device. - -Example: - - temp0@91a0000 { - compatible = "st,stih407-thermal"; - reg = <0x91a0000 0x28>; - clock-names = "thermal"; - clocks = <&CLK_SYSIN>; - interrupts = ; - st,passive_cooling_temp = <110>; - }; From 34b9a92b6850279145430af181d2d2dedb98cc76 Mon Sep 17 00:00:00 2001 From: Priyansh Jain Date: Thu, 28 Mar 2024 10:32:30 +0530 Subject: [PATCH 18/72] thermal/drivers/tsens: Add suspend to RAM support for tsens As part of suspend to RAM, tsens hardware will be turned off. While resume callback, re-initialize tsens hardware. Signed-off-by: Priyansh Jain Acked-by: Amit Kucheria Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20240328050230.31770-1-quic_priyjain@quicinc.com --- drivers/thermal/qcom/tsens-v2.c | 1 + drivers/thermal/qcom/tsens.c | 31 +++++++++++++++++++++++++++++++ drivers/thermal/qcom/tsens.h | 5 +++++ 3 files changed, 37 insertions(+) diff --git a/drivers/thermal/qcom/tsens-v2.c b/drivers/thermal/qcom/tsens-v2.c index 29a61d2d6ca3..0cb7301eca6e 100644 --- a/drivers/thermal/qcom/tsens-v2.c +++ b/drivers/thermal/qcom/tsens-v2.c @@ -107,6 +107,7 @@ static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = { static const struct tsens_ops ops_generic_v2 = { .init = init_common, .get_temp = get_temp_tsens_valid, + .resume = tsens_resume_common, }; struct tsens_plat_data data_tsens_v2 = { diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c index 6d7c16ccb44d..1c9df4f84641 100644 --- a/drivers/thermal/qcom/tsens.c +++ b/drivers/thermal/qcom/tsens.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "../thermal_hwmon.h" #include "tsens.h" @@ -1193,6 +1194,36 @@ static int tsens_register_irq(struct tsens_priv *priv, char *irqname, return ret; } +#ifdef CONFIG_SUSPEND +static int tsens_reinit(struct tsens_priv *priv) +{ + if (tsens_version(priv) >= VER_2_X) { + /* + * Re-enable the watchdog, unmask the bark. + * Disable cycle completion monitoring + */ + if (priv->feat->has_watchdog) { + regmap_field_write(priv->rf[WDOG_BARK_MASK], 0); + regmap_field_write(priv->rf[CC_MON_MASK], 1); + } + + /* Re-enable interrupts */ + tsens_enable_irq(priv); + } + + return 0; +} + +int tsens_resume_common(struct tsens_priv *priv) +{ + if (pm_suspend_target_state == PM_SUSPEND_MEM) + tsens_reinit(priv); + + return 0; +} + +#endif /* !CONFIG_SUSPEND */ + static int tsens_register(struct tsens_priv *priv) { int i, ret; diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h index cb637fa289ca..cab39de045b1 100644 --- a/drivers/thermal/qcom/tsens.h +++ b/drivers/thermal/qcom/tsens.h @@ -634,6 +634,11 @@ void compute_intercept_slope(struct tsens_priv *priv, u32 *pt1, u32 *pt2, u32 mo int init_common(struct tsens_priv *priv); int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp); int get_temp_common(const struct tsens_sensor *s, int *temp); +#ifdef CONFIG_SUSPEND +int tsens_resume_common(struct tsens_priv *priv); +#else +#define tsens_resume_common NULL +#endif /* TSENS target */ extern struct tsens_plat_data data_8960; From 63d96b1253169126a791d6b1b0ef2e708ffb0d75 Mon Sep 17 00:00:00 2001 From: Dmitry Rokosov Date: Thu, 28 Mar 2024 22:13:10 +0300 Subject: [PATCH 19/72] dt-bindings: thermal: amlogic: add support for A1 thermal sensor Provide right compatible properties for Amlogic A1 Thermal Sensor controller. A1 family supports only one thermal node - CPU thermal sensor. Signed-off-by: Dmitry Rokosov Acked-by: Conor Dooley Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20240328191322.17551-2-ddrokosov@salutedevices.com --- .../devicetree/bindings/thermal/amlogic,thermal.yaml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml b/Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml index 20f8f9b3b971..01fccdfc4178 100644 --- a/Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml +++ b/Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml @@ -13,11 +13,13 @@ description: Binding for Amlogic Thermal properties: compatible: - items: - - enum: - - amlogic,g12a-cpu-thermal - - amlogic,g12a-ddr-thermal - - const: amlogic,g12a-thermal + oneOf: + - items: + - enum: + - amlogic,g12a-cpu-thermal + - amlogic,g12a-ddr-thermal + - const: amlogic,g12a-thermal + - const: amlogic,a1-cpu-thermal reg: maxItems: 1 From 7fcd7dfa5ec29afe0b3fe8ad6a5f42c19e71d6be Mon Sep 17 00:00:00 2001 From: Dmitry Rokosov Date: Thu, 28 Mar 2024 22:13:11 +0300 Subject: [PATCH 20/72] thermal/drivers/amlogic: Support A1 SoC family Thermal Sensor controller In comparison to other Amlogic chips, there is one key difference. The offset for the sec_ao base, also known as u_efuse_off, is special, while other aspects remain the same. Signed-off-by: Dmitry Rokosov Reviewed-by: Neil Armstrong Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20240328191322.17551-3-ddrokosov@salutedevices.com --- drivers/thermal/amlogic_thermal.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/thermal/amlogic_thermal.c b/drivers/thermal/amlogic_thermal.c index df7a5ed55385..cd4776aa805e 100644 --- a/drivers/thermal/amlogic_thermal.c +++ b/drivers/thermal/amlogic_thermal.c @@ -220,6 +220,12 @@ static const struct amlogic_thermal_data amlogic_thermal_g12a_ddr_param = { .regmap_config = &amlogic_thermal_regmap_config_g12a, }; +static const struct amlogic_thermal_data amlogic_thermal_a1_cpu_param = { + .u_efuse_off = 0x114, + .calibration_parameters = &amlogic_thermal_g12a, + .regmap_config = &amlogic_thermal_regmap_config_g12a, +}; + static const struct of_device_id of_amlogic_thermal_match[] = { { .compatible = "amlogic,g12a-ddr-thermal", @@ -229,6 +235,10 @@ static const struct of_device_id of_amlogic_thermal_match[] = { .compatible = "amlogic,g12a-cpu-thermal", .data = &amlogic_thermal_g12a_cpu_param, }, + { + .compatible = "amlogic,a1-cpu-thermal", + .data = &amlogic_thermal_a1_cpu_param, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, of_amlogic_thermal_match); From 566e0ea7d02ff596d7bcd81ca547e91b5074032f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Wed, 27 Mar 2024 14:30:12 +0100 Subject: [PATCH 21/72] thermal/drivers/rcar_gen3: Move Tj_T storage to shared private data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The calculated Tj_T constant is calculated from the PTAT data either read from the first TSC zone on the device if calibration data is fused, or from fallback values in the driver itself. The value calculated is shared among all TSC zones. Move the Tj_T constant to the shared private data structure instead of duplicating it in each TSC private data. This requires adding a pointer to the shared data to the TSC private data structure. This back pointer make it easier to further rework the temperature conversion logic. Signed-off-by: Niklas Söderlund Reviewed-by: Geert Uytterhoeven Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20240327133013.3982199-2-niklas.soderlund+renesas@ragnatech.se --- drivers/thermal/rcar_gen3_thermal.c | 35 +++++++++++++++++++---------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/drivers/thermal/rcar_gen3_thermal.c b/drivers/thermal/rcar_gen3_thermal.c index a764cb1115a5..7c1ca912b9b1 100644 --- a/drivers/thermal/rcar_gen3_thermal.c +++ b/drivers/thermal/rcar_gen3_thermal.c @@ -81,10 +81,10 @@ struct rcar_thermal_info { }; struct rcar_gen3_thermal_tsc { + struct rcar_gen3_thermal_priv *priv; void __iomem *base; struct thermal_zone_device *zone; struct equation_coefs coef; - int tj_t; int thcode[3]; }; @@ -93,6 +93,7 @@ struct rcar_gen3_thermal_priv { struct thermal_zone_device_ops ops; unsigned int num_tscs; int ptat[3]; + int tj_t; const struct rcar_thermal_info *info; }; @@ -136,26 +137,32 @@ static inline void rcar_gen3_thermal_write(struct rcar_gen3_thermal_tsc *tsc, /* no idea where these constants come from */ #define TJ_3 -41 -static void rcar_gen3_thermal_calc_coefs(struct rcar_gen3_thermal_priv *priv, - struct rcar_gen3_thermal_tsc *tsc, - int ths_tj_1) +static void rcar_gen3_thermal_shared_coefs(struct rcar_gen3_thermal_priv *priv) { + int tj1 = priv->info->ths_tj_1; + + priv->tj_t = (FIXPT_INT((priv->ptat[1] - priv->ptat[2]) * (tj1 - TJ_3)) + / (priv->ptat[0] - priv->ptat[2])) + FIXPT_INT(TJ_3); +} + +static void rcar_gen3_thermal_tsc_coefs(struct rcar_gen3_thermal_priv *priv, + struct rcar_gen3_thermal_tsc *tsc) +{ + int tj1 = priv->info->ths_tj_1; + /* TODO: Find documentation and document constant calculation formula */ /* * Division is not scaled in BSP and if scaled it might overflow * the dividend (4095 * 4095 << 14 > INT_MAX) so keep it unscaled */ - tsc->tj_t = (FIXPT_INT((priv->ptat[1] - priv->ptat[2]) * (ths_tj_1 - TJ_3)) - / (priv->ptat[0] - priv->ptat[2])) + FIXPT_INT(TJ_3); - tsc->coef.a1 = FIXPT_DIV(FIXPT_INT(tsc->thcode[1] - tsc->thcode[2]), - tsc->tj_t - FIXPT_INT(TJ_3)); + priv->tj_t - FIXPT_INT(TJ_3)); tsc->coef.b1 = FIXPT_INT(tsc->thcode[2]) - tsc->coef.a1 * TJ_3; tsc->coef.a2 = FIXPT_DIV(FIXPT_INT(tsc->thcode[1] - tsc->thcode[0]), - tsc->tj_t - FIXPT_INT(ths_tj_1)); - tsc->coef.b2 = FIXPT_INT(tsc->thcode[0]) - tsc->coef.a2 * ths_tj_1; + priv->tj_t - FIXPT_INT(tj1)); + tsc->coef.b2 = FIXPT_INT(tsc->thcode[0]) - tsc->coef.a2 * tj1; } static int rcar_gen3_thermal_round(int temp) @@ -196,10 +203,11 @@ static int rcar_gen3_thermal_get_temp(struct thermal_zone_device *tz, int *temp) static int rcar_gen3_thermal_mcelsius_to_temp(struct rcar_gen3_thermal_tsc *tsc, int mcelsius) { + struct rcar_gen3_thermal_priv *priv = tsc->priv; int celsius, val; celsius = DIV_ROUND_CLOSEST(mcelsius, 1000); - if (celsius <= INT_FIXPT(tsc->tj_t)) + if (celsius <= INT_FIXPT(priv->tj_t)) val = celsius * tsc->coef.a1 + tsc->coef.b1; else val = celsius * tsc->coef.a2 + tsc->coef.b2; @@ -516,6 +524,7 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) goto error_unregister; } + tsc->priv = priv; tsc->base = devm_ioremap_resource(dev, res); if (IS_ERR(tsc->base)) { ret = PTR_ERR(tsc->base); @@ -530,11 +539,13 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) if (!rcar_gen3_thermal_read_fuses(priv)) dev_info(dev, "No calibration values fused, fallback to driver values\n"); + rcar_gen3_thermal_shared_coefs(priv); + for (i = 0; i < priv->num_tscs; i++) { struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i]; rcar_gen3_thermal_init(priv, tsc); - rcar_gen3_thermal_calc_coefs(priv, tsc, priv->info->ths_tj_1); + rcar_gen3_thermal_tsc_coefs(priv, tsc); zone = devm_thermal_of_zone_register(dev, i, tsc, &priv->ops); if (IS_ERR(zone)) { From 63d23fb78140a882e268201ff730453cd8b4f8e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Wed, 27 Mar 2024 14:30:13 +0100 Subject: [PATCH 22/72] thermal/drivers/rcar_gen3: Update temperature approximation calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The initial driver used a formula to approximate the temperature and register values reversed engineered from an out-of-tree BSP driver. This was needed as the datasheet at the time did not contain any information on how to do this. Later Gen3 (Rev 2.30) and Gen4 (all) now contains this information. Update the approximation formula to use the datasheet's information instead of the reversed-engineered one. On an idle M3-N without fused calibration values for PTAT and THCODE the old formula reports, zone0: 52000 zone1: 53000 zone2: 52500 While the new formula under the same circumstances reports, zone0: 52500 zone1: 54000 zone2: 54000 Signed-off-by: Niklas Söderlund Reviewed-by: Geert Uytterhoeven Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20240327133013.3982199-3-niklas.soderlund+renesas@ragnatech.se --- drivers/thermal/rcar_gen3_thermal.c | 156 ++++++++++++++-------------- 1 file changed, 78 insertions(+), 78 deletions(-) diff --git a/drivers/thermal/rcar_gen3_thermal.c b/drivers/thermal/rcar_gen3_thermal.c index 7c1ca912b9b1..02494fa142c3 100644 --- a/drivers/thermal/rcar_gen3_thermal.c +++ b/drivers/thermal/rcar_gen3_thermal.c @@ -65,26 +65,29 @@ #define TSC_MAX_NUM 5 -/* Structure for thermal temperature calculation */ -struct equation_coefs { - int a1; - int b1; - int a2; - int b2; -}; - struct rcar_gen3_thermal_priv; struct rcar_thermal_info { - int ths_tj_1; + int scale; + int adj_below; + int adj_above; void (*read_fuses)(struct rcar_gen3_thermal_priv *priv); }; +struct equation_set_coef { + int a; + int b; +}; + struct rcar_gen3_thermal_tsc { struct rcar_gen3_thermal_priv *priv; void __iomem *base; struct thermal_zone_device *zone; - struct equation_coefs coef; + /* Different coefficients are used depending on a threshold. */ + struct { + struct equation_set_coef below; + struct equation_set_coef above; + } coef; int thcode[3]; }; @@ -112,90 +115,75 @@ static inline void rcar_gen3_thermal_write(struct rcar_gen3_thermal_tsc *tsc, /* * Linear approximation for temperature * - * [reg] = [temp] * a + b => [temp] = ([reg] - b) / a + * [temp] = ((thadj - [reg]) * a) / b + adj + * [reg] = thadj - ([temp] - adj) * b / a * * The constants a and b are calculated using two triplets of int values PTAT * and THCODE. PTAT and THCODE can either be read from hardware or use hard - * coded values from driver. The formula to calculate a and b are taken from - * BSP and sparsely documented and understood. + * coded values from the driver. The formula to calculate a and b are taken from + * the datasheet. Different calculations are needed for a and b depending on + * if the input variables ([temp] or [reg]) are above or below a threshold. The + * threshold is also calculated from PTAT and THCODE using formulas from the + * datasheet. * - * Examining the linear formula and the formula used to calculate constants a - * and b while knowing that the span for PTAT and THCODE values are between - * 0x000 and 0xfff the largest integer possible is 0xfff * 0xfff == 0xffe001. - * Integer also needs to be signed so that leaves 7 bits for binary - * fixed point scaling. + * The constant thadj is one of the THCODE values, which one to use depends on + * the threshold and input value. + * + * The constants adj is taken verbatim from the datasheet. Two values exists, + * which one to use depends on the input value and the calculated threshold. + * Furthermore different SoC models supported by the driver have different sets + * of values. The values for each model are stored in the device match data. */ -#define FIXPT_SHIFT 7 -#define FIXPT_INT(_x) ((_x) << FIXPT_SHIFT) -#define INT_FIXPT(_x) ((_x) >> FIXPT_SHIFT) -#define FIXPT_DIV(_a, _b) DIV_ROUND_CLOSEST(((_a) << FIXPT_SHIFT), (_b)) -#define FIXPT_TO_MCELSIUS(_x) ((_x) * 1000 >> FIXPT_SHIFT) - -#define RCAR3_THERMAL_GRAN 500 /* mili Celsius */ - -/* no idea where these constants come from */ -#define TJ_3 -41 - static void rcar_gen3_thermal_shared_coefs(struct rcar_gen3_thermal_priv *priv) { - int tj1 = priv->info->ths_tj_1; - - priv->tj_t = (FIXPT_INT((priv->ptat[1] - priv->ptat[2]) * (tj1 - TJ_3)) - / (priv->ptat[0] - priv->ptat[2])) + FIXPT_INT(TJ_3); + priv->tj_t = + DIV_ROUND_CLOSEST((priv->ptat[1] - priv->ptat[2]) * priv->info->scale, + priv->ptat[0] - priv->ptat[2]) + + priv->info->adj_below; } - static void rcar_gen3_thermal_tsc_coefs(struct rcar_gen3_thermal_priv *priv, struct rcar_gen3_thermal_tsc *tsc) { - int tj1 = priv->info->ths_tj_1; + tsc->coef.below.a = priv->info->scale * (priv->ptat[2] - priv->ptat[1]); + tsc->coef.above.a = priv->info->scale * (priv->ptat[0] - priv->ptat[1]); - /* TODO: Find documentation and document constant calculation formula */ - - /* - * Division is not scaled in BSP and if scaled it might overflow - * the dividend (4095 * 4095 << 14 > INT_MAX) so keep it unscaled - */ - tsc->coef.a1 = FIXPT_DIV(FIXPT_INT(tsc->thcode[1] - tsc->thcode[2]), - priv->tj_t - FIXPT_INT(TJ_3)); - tsc->coef.b1 = FIXPT_INT(tsc->thcode[2]) - tsc->coef.a1 * TJ_3; - - tsc->coef.a2 = FIXPT_DIV(FIXPT_INT(tsc->thcode[1] - tsc->thcode[0]), - priv->tj_t - FIXPT_INT(tj1)); - tsc->coef.b2 = FIXPT_INT(tsc->thcode[0]) - tsc->coef.a2 * tj1; -} - -static int rcar_gen3_thermal_round(int temp) -{ - int result, round_offs; - - round_offs = temp >= 0 ? RCAR3_THERMAL_GRAN / 2 : - -RCAR3_THERMAL_GRAN / 2; - result = (temp + round_offs) / RCAR3_THERMAL_GRAN; - return result * RCAR3_THERMAL_GRAN; + tsc->coef.below.b = (priv->ptat[2] - priv->ptat[0]) * (tsc->thcode[2] - tsc->thcode[1]); + tsc->coef.above.b = (priv->ptat[0] - priv->ptat[2]) * (tsc->thcode[1] - tsc->thcode[0]); } static int rcar_gen3_thermal_get_temp(struct thermal_zone_device *tz, int *temp) { struct rcar_gen3_thermal_tsc *tsc = thermal_zone_device_priv(tz); - int mcelsius, val; - int reg; + struct rcar_gen3_thermal_priv *priv = tsc->priv; + const struct equation_set_coef *coef; + int adj, decicelsius, reg, thcode; /* Read register and convert to mili Celsius */ reg = rcar_gen3_thermal_read(tsc, REG_GEN3_TEMP) & CTEMP_MASK; - if (reg <= tsc->thcode[1]) - val = FIXPT_DIV(FIXPT_INT(reg) - tsc->coef.b1, - tsc->coef.a1); - else - val = FIXPT_DIV(FIXPT_INT(reg) - tsc->coef.b2, - tsc->coef.a2); - mcelsius = FIXPT_TO_MCELSIUS(val); + if (reg < tsc->thcode[1]) { + adj = priv->info->adj_below; + coef = &tsc->coef.below; + thcode = tsc->thcode[2]; + } else { + adj = priv->info->adj_above; + coef = &tsc->coef.above; + thcode = tsc->thcode[0]; + } + + /* + * The dividend can't be grown as it might overflow, instead shorten the + * divisor to convert to decidegree Celsius. If we convert after the + * division precision is lost as we will scale up from whole degrees + * Celsius. + */ + decicelsius = DIV_ROUND_CLOSEST(coef->a * (thcode - reg), coef->b / 10); /* Guaranteed operating range is -40C to 125C. */ - /* Round value to device granularity setting */ - *temp = rcar_gen3_thermal_round(mcelsius); + /* Reporting is done in millidegree Celsius */ + *temp = decicelsius * 100 + adj * 1000; return 0; } @@ -204,15 +192,21 @@ static int rcar_gen3_thermal_mcelsius_to_temp(struct rcar_gen3_thermal_tsc *tsc, int mcelsius) { struct rcar_gen3_thermal_priv *priv = tsc->priv; - int celsius, val; + const struct equation_set_coef *coef; + int adj, celsius, thcode; celsius = DIV_ROUND_CLOSEST(mcelsius, 1000); - if (celsius <= INT_FIXPT(priv->tj_t)) - val = celsius * tsc->coef.a1 + tsc->coef.b1; - else - val = celsius * tsc->coef.a2 + tsc->coef.b2; + if (celsius < priv->tj_t) { + coef = &tsc->coef.below; + adj = priv->info->adj_below; + thcode = tsc->thcode[2]; + } else { + coef = &tsc->coef.above; + adj = priv->info->adj_above; + thcode = tsc->thcode[0]; + } - return INT_FIXPT(val); + return thcode - DIV_ROUND_CLOSEST((celsius - adj) * coef->b, coef->a); } static int rcar_gen3_thermal_set_trips(struct thermal_zone_device *tz, int low, int high) @@ -377,17 +371,23 @@ static void rcar_gen3_thermal_init(struct rcar_gen3_thermal_priv *priv, } static const struct rcar_thermal_info rcar_m3w_thermal_info = { - .ths_tj_1 = 116, + .scale = 157, + .adj_below = -41, + .adj_above = 116, .read_fuses = rcar_gen3_thermal_read_fuses_gen3, }; static const struct rcar_thermal_info rcar_gen3_thermal_info = { - .ths_tj_1 = 126, + .scale = 167, + .adj_below = -41, + .adj_above = 126, .read_fuses = rcar_gen3_thermal_read_fuses_gen3, }; static const struct rcar_thermal_info rcar_gen4_thermal_info = { - .ths_tj_1 = 126, + .scale = 167, + .adj_below = -41, + .adj_above = 126, .read_fuses = rcar_gen3_thermal_read_fuses_gen4, }; From 7954c92ede882b0dfd52a5db90291a4151b44c1a Mon Sep 17 00:00:00 2001 From: Hsin-Te Yuan Date: Tue, 16 Apr 2024 07:35:47 +0000 Subject: [PATCH 23/72] thermal/drivers/mediatek/lvts_thermal: Add coeff for mt8192 In order for lvts_raw_to_temp to function properly on mt8192, temperature coefficients for mt8192 need to be added. Fixes: 288732242db4 ("thermal/drivers/mediatek/lvts_thermal: Add mt8192 support") Signed-off-by: Hsin-Te Yuan Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20240416-lvts_thermal-v2-1-f8a36882cc53@chromium.org --- drivers/thermal/mediatek/lvts_thermal.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/thermal/mediatek/lvts_thermal.c b/drivers/thermal/mediatek/lvts_thermal.c index fd4bd650c77a..4e5c213a8922 100644 --- a/drivers/thermal/mediatek/lvts_thermal.c +++ b/drivers/thermal/mediatek/lvts_thermal.c @@ -1530,11 +1530,15 @@ static const struct lvts_data mt7988_lvts_ap_data = { static const struct lvts_data mt8192_lvts_mcu_data = { .lvts_ctrl = mt8192_lvts_mcu_data_ctrl, .num_lvts_ctrl = ARRAY_SIZE(mt8192_lvts_mcu_data_ctrl), + .temp_factor = LVTS_COEFF_A_MT8195, + .temp_offset = LVTS_COEFF_B_MT8195, }; static const struct lvts_data mt8192_lvts_ap_data = { .lvts_ctrl = mt8192_lvts_ap_data_ctrl, .num_lvts_ctrl = ARRAY_SIZE(mt8192_lvts_ap_data_ctrl), + .temp_factor = LVTS_COEFF_A_MT8195, + .temp_offset = LVTS_COEFF_B_MT8195, }; static const struct lvts_data mt8195_lvts_mcu_data = { From d998ddc86a27c92140b9f7984ff41e3d1d07a48f Mon Sep 17 00:00:00 2001 From: Aleksandr Mishin Date: Thu, 11 Apr 2024 14:40:21 +0300 Subject: [PATCH 24/72] thermal/drivers/tsens: Fix null pointer dereference compute_intercept_slope() is called from calibrate_8960() (in tsens-8960.c) as compute_intercept_slope(priv, p1, NULL, ONE_PT_CALIB) which lead to null pointer dereference (if DEBUG or DYNAMIC_DEBUG set). Fix this bug by adding null pointer check. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: dfc1193d4dbd ("thermal/drivers/tsens: Replace custom 8960 apis with generic apis") Signed-off-by: Aleksandr Mishin Reviewed-by: Konrad Dybcio Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20240411114021.12203-1-amishin@t-argos.ru --- drivers/thermal/qcom/tsens.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c index 1c9df4f84641..e76e23026dc8 100644 --- a/drivers/thermal/qcom/tsens.c +++ b/drivers/thermal/qcom/tsens.c @@ -265,7 +265,7 @@ void compute_intercept_slope(struct tsens_priv *priv, u32 *p1, for (i = 0; i < priv->num_sensors; i++) { dev_dbg(priv->dev, "%s: sensor%d - data_point1:%#x data_point2:%#x\n", - __func__, i, p1[i], p2[i]); + __func__, i, p1[i], p2 ? p2[i] : 0); if (!priv->sensor[i].slope) priv->sensor[i].slope = SLOPE_DEFAULT; From 0a0c8db8843c5d5e22e2ee275c35d6ecd0659414 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Tue, 9 Apr 2024 21:56:34 +0200 Subject: [PATCH 25/72] thermal/drivers/qcom: Remove some unused fields in struct qpnp_tm_chip In "struct qpnp_tm_chip", the 'prev_stage' field is unused. Remove it. Found with cppcheck, unusedStructMember. Signed-off-by: Christophe JAILLET Reviewed-by: Bjorn Andersson Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/d1c3a3c455f485dae46290e3488daf1dcc1d355a.1712687589.git.christophe.jaillet@wanadoo.fr --- drivers/thermal/qcom/qcom-spmi-temp-alarm.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c index 78c5cfe6a0c0..3cd74f6cac8f 100644 --- a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c +++ b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c @@ -74,7 +74,6 @@ struct qpnp_tm_chip { long temp; unsigned int thresh; unsigned int stage; - unsigned int prev_stage; unsigned int base; /* protects .thresh, .stage and chip registers */ struct mutex lock; From 61fad0a90685d3aaafb05fb6abc1fe39fbb90320 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Tue, 9 Apr 2024 21:56:43 +0200 Subject: [PATCH 26/72] thermal/drivers/k3_bandgap: Remove some unused fields in struct k3_bandgap In "struct k3_bandgap", the 'conf' field is unused. Remove it. Found with cppcheck, unusedStructMember. Signed-off-by: Christophe JAILLET Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/206d39bed9ec6df9b4d80b1fc064e499389fc7fc.1712687420.git.christophe.jaillet@wanadoo.fr --- drivers/thermal/k3_bandgap.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/thermal/k3_bandgap.c b/drivers/thermal/k3_bandgap.c index e88192d2afea..2a703770fc91 100644 --- a/drivers/thermal/k3_bandgap.c +++ b/drivers/thermal/k3_bandgap.c @@ -78,7 +78,6 @@ static const int k3_adc_to_temp[] = { struct k3_bandgap { void __iomem *base; - const struct k3_bandgap_data *conf; }; /* common data structures */ From 8c25958f71d8c173b81cb422a44c5743beb76af7 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Mon, 1 Apr 2024 23:25:35 -0400 Subject: [PATCH 27/72] thermal/drivers/mediatek/lvts_thermal: Retrieve all calibration bytes Calibration values are 24-bit wide. Those values so far appear to span only 16 bits but let's not push our luck. Found while looking at the original Mediatek driver code. Signed-off-by: Nicolas Pitre Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20240402032729.2736685-2-nico@fluxnic.net --- drivers/thermal/mediatek/lvts_thermal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/mediatek/lvts_thermal.c b/drivers/thermal/mediatek/lvts_thermal.c index 4e5c213a8922..d26662608b4c 100644 --- a/drivers/thermal/mediatek/lvts_thermal.c +++ b/drivers/thermal/mediatek/lvts_thermal.c @@ -679,7 +679,7 @@ static int lvts_calibration_init(struct device *dev, struct lvts_ctrl *lvts_ctrl for (i = 0; i < lvts_ctrl_data->num_lvts_sensor; i++) memcpy(&lvts_ctrl->calibration[i], - efuse_calibration + lvts_ctrl_data->cal_offset[i], 2); + efuse_calibration + lvts_ctrl_data->cal_offset[i], 3); return 0; } From 62194e637d16ac9b61eec0bfe6d5f662efcddbe7 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Mon, 1 Apr 2024 23:25:36 -0400 Subject: [PATCH 28/72] thermal/drivers/mediatek/lvts_thermal: Move comment Move efuse data interpretation inside lvts_golden_temp_init() alongside the actual code retrieving wanted value. Signed-off-by: Nicolas Pitre Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20240402032729.2736685-3-nico@fluxnic.net --- drivers/thermal/mediatek/lvts_thermal.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/thermal/mediatek/lvts_thermal.c b/drivers/thermal/mediatek/lvts_thermal.c index d26662608b4c..040116228850 100644 --- a/drivers/thermal/mediatek/lvts_thermal.c +++ b/drivers/thermal/mediatek/lvts_thermal.c @@ -734,11 +734,15 @@ static int lvts_calibration_read(struct device *dev, struct lvts_domain *lvts_td return 0; } -static int lvts_golden_temp_init(struct device *dev, u32 *value, int temp_offset) +static int lvts_golden_temp_init(struct device *dev, u8 *calib, int temp_offset) { u32 gt; - gt = (*value) >> 24; + /* + * The golden temp information is contained in the 4th byte (index = 3) + * of efuse data. + */ + gt = calib[3]; if (gt && gt < LVTS_GOLDEN_TEMP_MAX) golden_temp = gt; @@ -762,11 +766,7 @@ static int lvts_ctrl_init(struct device *dev, struct lvts_domain *lvts_td, if (ret) return ret; - /* - * The golden temp information is contained in the first chunk - * of efuse data. - */ - ret = lvts_golden_temp_init(dev, (u32 *)lvts_td->calib, lvts_data->temp_offset); + ret = lvts_golden_temp_init(dev, lvts_td->calib, lvts_data->temp_offset); if (ret) return ret; From 554bca31308ab8234bac505d0b86d9082b549a40 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Mon, 1 Apr 2024 23:25:37 -0400 Subject: [PATCH 29/72] thermal/drivers/mediatek/lvts_thermal: Remove .hw_tshut_temp All the .hw_tshut_temp instances are initialized with the same value. Let's remove those and use a common definition instead. If ever a different value must be used in the future then an override parameter could be added back. Signed-off-by: Nicolas Pitre Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20240402032729.2736685-4-nico@fluxnic.net --- drivers/thermal/mediatek/lvts_thermal.c | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/drivers/thermal/mediatek/lvts_thermal.c b/drivers/thermal/mediatek/lvts_thermal.c index 040116228850..08cf379d433b 100644 --- a/drivers/thermal/mediatek/lvts_thermal.c +++ b/drivers/thermal/mediatek/lvts_thermal.c @@ -91,9 +91,7 @@ #define LVTS_MSR_READ_TIMEOUT_US 400 #define LVTS_MSR_READ_WAIT_US (LVTS_MSR_READ_TIMEOUT_US / 2) -#define LVTS_HW_SHUTDOWN_MT7988 105000 -#define LVTS_HW_SHUTDOWN_MT8192 105000 -#define LVTS_HW_SHUTDOWN_MT8195 105000 +#define LVTS_HW_TSHUT_TEMP 105000 #define LVTS_MINIMUM_THRESHOLD 20000 @@ -107,7 +105,6 @@ struct lvts_sensor_data { struct lvts_ctrl_data { struct lvts_sensor_data lvts_sensor[LVTS_SENSOR_MAX]; int cal_offset[LVTS_SENSOR_MAX]; - int hw_tshut_temp; int num_lvts_sensor; int offset; int mode; @@ -801,7 +798,7 @@ static int lvts_ctrl_init(struct device *dev, struct lvts_domain *lvts_td, * after initializing the calibration. */ lvts_ctrl[i].hw_tshut_raw_temp = - lvts_temp_to_raw(lvts_data->lvts_ctrl[i].hw_tshut_temp, + lvts_temp_to_raw(LVTS_HW_TSHUT_TEMP, lvts_data->temp_factor); lvts_ctrl[i].low_thresh = INT_MIN; @@ -1311,7 +1308,6 @@ static const struct lvts_ctrl_data mt7988_lvts_ap_data_ctrl[] = { }, .num_lvts_sensor = 4, .offset = 0x0, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT7988, }, { .cal_offset = { 0x14, 0x18, 0x1c, 0x20 }, @@ -1323,7 +1319,6 @@ static const struct lvts_ctrl_data mt7988_lvts_ap_data_ctrl[] = { }, .num_lvts_sensor = 4, .offset = 0x100, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT7988, } }; @@ -1368,7 +1363,6 @@ static const struct lvts_ctrl_data mt8192_lvts_mcu_data_ctrl[] = { }, .num_lvts_sensor = 2, .offset = 0x0, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8192, .mode = LVTS_MSR_FILTERED_MODE, }, { @@ -1379,7 +1373,6 @@ static const struct lvts_ctrl_data mt8192_lvts_mcu_data_ctrl[] = { }, .num_lvts_sensor = 2, .offset = 0x100, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8192, .mode = LVTS_MSR_FILTERED_MODE, }, { @@ -1392,7 +1385,6 @@ static const struct lvts_ctrl_data mt8192_lvts_mcu_data_ctrl[] = { }, .num_lvts_sensor = 4, .offset = 0x200, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8192, .mode = LVTS_MSR_FILTERED_MODE, } }; @@ -1406,7 +1398,6 @@ static const struct lvts_ctrl_data mt8192_lvts_ap_data_ctrl[] = { }, .num_lvts_sensor = 2, .offset = 0x0, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8192, }, { .cal_offset = { 0x2c, 0x30 }, @@ -1416,7 +1407,6 @@ static const struct lvts_ctrl_data mt8192_lvts_ap_data_ctrl[] = { }, .num_lvts_sensor = 2, .offset = 0x100, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8192, }, { .cal_offset = { 0x34, 0x38 }, @@ -1426,7 +1416,6 @@ static const struct lvts_ctrl_data mt8192_lvts_ap_data_ctrl[] = { }, .num_lvts_sensor = 2, .offset = 0x200, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8192, }, { .cal_offset = { 0x3c, 0x40, 0x44 }, @@ -1437,7 +1426,6 @@ static const struct lvts_ctrl_data mt8192_lvts_ap_data_ctrl[] = { }, .num_lvts_sensor = 3, .offset = 0x300, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8192, } }; @@ -1450,7 +1438,6 @@ static const struct lvts_ctrl_data mt8195_lvts_mcu_data_ctrl[] = { }, .num_lvts_sensor = 2, .offset = 0x0, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195, }, { .cal_offset = { 0x0d, 0x10 }, @@ -1460,7 +1447,6 @@ static const struct lvts_ctrl_data mt8195_lvts_mcu_data_ctrl[] = { }, .num_lvts_sensor = 2, .offset = 0x100, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195, }, { .cal_offset = { 0x16, 0x19, 0x1c, 0x1f }, @@ -1472,7 +1458,6 @@ static const struct lvts_ctrl_data mt8195_lvts_mcu_data_ctrl[] = { }, .num_lvts_sensor = 4, .offset = 0x200, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195, } }; @@ -1485,7 +1470,6 @@ static const struct lvts_ctrl_data mt8195_lvts_ap_data_ctrl[] = { }, .num_lvts_sensor = 2, .offset = 0x0, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195, }, { .cal_offset = { 0x2e, 0x31 }, @@ -1495,7 +1479,6 @@ static const struct lvts_ctrl_data mt8195_lvts_ap_data_ctrl[] = { }, .num_lvts_sensor = 2, .offset = 0x100, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195, }, { .cal_offset = { 0x37, 0x3a, 0x3d }, @@ -1506,7 +1489,6 @@ static const struct lvts_ctrl_data mt8195_lvts_ap_data_ctrl[] = { }, .num_lvts_sensor = 3, .offset = 0x200, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195, }, { .cal_offset = { 0x43, 0x46 }, @@ -1516,7 +1498,6 @@ static const struct lvts_ctrl_data mt8195_lvts_ap_data_ctrl[] = { }, .num_lvts_sensor = 2, .offset = 0x300, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195, } }; From 5b3367e28a2c47d74bd566b96854ef0de3caa6d7 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Mon, 1 Apr 2024 23:25:38 -0400 Subject: [PATCH 30/72] thermal/drivers/mediatek/lvts_thermal: Use offsets for every calibration byte Current code assumes calibration values are always stored contiguously in host endian order. A future patch will prove this wrong. Let's specify the offset for each calibration byte instead. Signed-off-by: Nicolas Pitre Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20240402032729.2736685-5-nico@fluxnic.net --- drivers/thermal/mediatek/lvts_thermal.c | 164 ++++++++++++++---------- 1 file changed, 99 insertions(+), 65 deletions(-) diff --git a/drivers/thermal/mediatek/lvts_thermal.c b/drivers/thermal/mediatek/lvts_thermal.c index 08cf379d433b..9e6ed8bf6d9a 100644 --- a/drivers/thermal/mediatek/lvts_thermal.c +++ b/drivers/thermal/mediatek/lvts_thermal.c @@ -100,6 +100,7 @@ static int golden_temp_offset; struct lvts_sensor_data { int dt_id; + u8 cal_offsets[3]; }; struct lvts_ctrl_data { @@ -665,8 +666,9 @@ static int lvts_sensor_init(struct device *dev, struct lvts_ctrl *lvts_ctrl, * <-----ap--tc#3-----> <-----sensor#7-----> <-----sensor#8-----> * 0x40 | 0x41 | 0x42 | 0x43 | 0x44 | 0x45 | 0x46 | 0x47 | 0x48 * - * The data description gives the offset of the calibration data in - * this bytes stream for each sensor. + * Note: In some cases, values don't strictly follow a little endian ordering. + * The data description gives byte offsets constituting each calibration value + * for each sensor. */ static int lvts_calibration_init(struct device *dev, struct lvts_ctrl *lvts_ctrl, const struct lvts_ctrl_data *lvts_ctrl_data, @@ -674,9 +676,15 @@ static int lvts_calibration_init(struct device *dev, struct lvts_ctrl *lvts_ctrl { int i; - for (i = 0; i < lvts_ctrl_data->num_lvts_sensor; i++) - memcpy(&lvts_ctrl->calibration[i], - efuse_calibration + lvts_ctrl_data->cal_offset[i], 3); + for (i = 0; i < lvts_ctrl_data->num_lvts_sensor; i++) { + const struct lvts_sensor_data *sensor = + &lvts_ctrl_data->lvts_sensor[i]; + + lvts_ctrl->calibration[i] = + (efuse_calibration[sensor->cal_offsets[0]] << 0) + + (efuse_calibration[sensor->cal_offsets[1]] << 8) + + (efuse_calibration[sensor->cal_offsets[2]] << 16); + } return 0; } @@ -1299,23 +1307,29 @@ static void lvts_remove(struct platform_device *pdev) static const struct lvts_ctrl_data mt7988_lvts_ap_data_ctrl[] = { { - .cal_offset = { 0x00, 0x04, 0x08, 0x0c }, .lvts_sensor = { - { .dt_id = MT7988_CPU_0 }, - { .dt_id = MT7988_CPU_1 }, - { .dt_id = MT7988_ETH2P5G_0 }, - { .dt_id = MT7988_ETH2P5G_1 } + { .dt_id = MT7988_CPU_0, + .cal_offsets = { 0x00, 0x01, 0x02 } }, + { .dt_id = MT7988_CPU_1, + .cal_offsets = { 0x04, 0x05, 0x06 } }, + { .dt_id = MT7988_ETH2P5G_0, + .cal_offsets = { 0x08, 0x09, 0x0a } }, + { .dt_id = MT7988_ETH2P5G_1, + .cal_offsets = { 0x0c, 0x0d, 0x0e } } }, .num_lvts_sensor = 4, .offset = 0x0, }, { - .cal_offset = { 0x14, 0x18, 0x1c, 0x20 }, .lvts_sensor = { - { .dt_id = MT7988_TOPS_0}, - { .dt_id = MT7988_TOPS_1}, - { .dt_id = MT7988_ETHWARP_0}, - { .dt_id = MT7988_ETHWARP_1} + { .dt_id = MT7988_TOPS_0, + .cal_offsets = { 0x14, 0x15, 0x16 } }, + { .dt_id = MT7988_TOPS_1, + .cal_offsets = { 0x18, 0x19, 0x1a } }, + { .dt_id = MT7988_ETHWARP_0, + .cal_offsets = { 0x1c, 0x1d, 0x1e } }, + { .dt_id = MT7988_ETHWARP_1, + .cal_offsets = { 0x20, 0x21, 0x22 } } }, .num_lvts_sensor = 4, .offset = 0x100, @@ -1356,32 +1370,37 @@ static int lvts_resume(struct device *dev) static const struct lvts_ctrl_data mt8192_lvts_mcu_data_ctrl[] = { { - .cal_offset = { 0x04, 0x08 }, .lvts_sensor = { - { .dt_id = MT8192_MCU_BIG_CPU0 }, - { .dt_id = MT8192_MCU_BIG_CPU1 } + { .dt_id = MT8192_MCU_BIG_CPU0, + .cal_offsets = { 0x04, 0x05, 0x06 } }, + { .dt_id = MT8192_MCU_BIG_CPU1, + .cal_offsets = { 0x08, 0x09, 0x0a } } }, .num_lvts_sensor = 2, .offset = 0x0, .mode = LVTS_MSR_FILTERED_MODE, }, { - .cal_offset = { 0x0c, 0x10 }, .lvts_sensor = { - { .dt_id = MT8192_MCU_BIG_CPU2 }, - { .dt_id = MT8192_MCU_BIG_CPU3 } + { .dt_id = MT8192_MCU_BIG_CPU2, + .cal_offsets = { 0x0c, 0x0d, 0x0e } }, + { .dt_id = MT8192_MCU_BIG_CPU3, + .cal_offsets = { 0x10, 0x11, 0x12 } } }, .num_lvts_sensor = 2, .offset = 0x100, .mode = LVTS_MSR_FILTERED_MODE, }, { - .cal_offset = { 0x14, 0x18, 0x1c, 0x20 }, .lvts_sensor = { - { .dt_id = MT8192_MCU_LITTLE_CPU0 }, - { .dt_id = MT8192_MCU_LITTLE_CPU1 }, - { .dt_id = MT8192_MCU_LITTLE_CPU2 }, - { .dt_id = MT8192_MCU_LITTLE_CPU3 } + { .dt_id = MT8192_MCU_LITTLE_CPU0, + .cal_offsets = { 0x14, 0x15, 0x16 } }, + { .dt_id = MT8192_MCU_LITTLE_CPU1, + .cal_offsets = { 0x18, 0x19, 0x1a } }, + { .dt_id = MT8192_MCU_LITTLE_CPU2, + .cal_offsets = { 0x1c, 0x1d, 0x1e } }, + { .dt_id = MT8192_MCU_LITTLE_CPU3, + .cal_offsets = { 0x20, 0x21, 0x22 } } }, .num_lvts_sensor = 4, .offset = 0x200, @@ -1390,39 +1409,44 @@ static const struct lvts_ctrl_data mt8192_lvts_mcu_data_ctrl[] = { }; static const struct lvts_ctrl_data mt8192_lvts_ap_data_ctrl[] = { - { - .cal_offset = { 0x24, 0x28 }, + { .lvts_sensor = { - { .dt_id = MT8192_AP_VPU0 }, - { .dt_id = MT8192_AP_VPU1 } + { .dt_id = MT8192_AP_VPU0, + .cal_offsets = { 0x24, 0x25, 0x26 } }, + { .dt_id = MT8192_AP_VPU1, + .cal_offsets = { 0x28, 0x29, 0x2a } } }, .num_lvts_sensor = 2, .offset = 0x0, }, { - .cal_offset = { 0x2c, 0x30 }, .lvts_sensor = { - { .dt_id = MT8192_AP_GPU0 }, - { .dt_id = MT8192_AP_GPU1 } + { .dt_id = MT8192_AP_GPU0, + .cal_offsets = { 0x2c, 0x2d, 0x2e } }, + { .dt_id = MT8192_AP_GPU1, + .cal_offsets = { 0x30, 0x31, 0x32 } } }, .num_lvts_sensor = 2, .offset = 0x100, }, { - .cal_offset = { 0x34, 0x38 }, .lvts_sensor = { - { .dt_id = MT8192_AP_INFRA }, - { .dt_id = MT8192_AP_CAM }, + { .dt_id = MT8192_AP_INFRA, + .cal_offsets = { 0x34, 0x35, 0x36 } }, + { .dt_id = MT8192_AP_CAM, + .cal_offsets = { 0x38, 0x39, 0x3a } }, }, .num_lvts_sensor = 2, .offset = 0x200, }, { - .cal_offset = { 0x3c, 0x40, 0x44 }, .lvts_sensor = { - { .dt_id = MT8192_AP_MD0 }, - { .dt_id = MT8192_AP_MD1 }, - { .dt_id = MT8192_AP_MD2 } + { .dt_id = MT8192_AP_MD0, + .cal_offsets = { 0x3c, 0x3d, 0x3e } }, + { .dt_id = MT8192_AP_MD1, + .cal_offsets = { 0x40, 0x41, 0x42 } }, + { .dt_id = MT8192_AP_MD2, + .cal_offsets = { 0x44, 0x45, 0x46 } } }, .num_lvts_sensor = 3, .offset = 0x300, @@ -1431,30 +1455,35 @@ static const struct lvts_ctrl_data mt8192_lvts_ap_data_ctrl[] = { static const struct lvts_ctrl_data mt8195_lvts_mcu_data_ctrl[] = { { - .cal_offset = { 0x04, 0x07 }, .lvts_sensor = { - { .dt_id = MT8195_MCU_BIG_CPU0 }, - { .dt_id = MT8195_MCU_BIG_CPU1 } + { .dt_id = MT8195_MCU_BIG_CPU0, + .cal_offsets = { 0x04, 0x05, 0x06 } }, + { .dt_id = MT8195_MCU_BIG_CPU1, + .cal_offsets = { 0x07, 0x08, 0x09 } } }, .num_lvts_sensor = 2, .offset = 0x0, }, { - .cal_offset = { 0x0d, 0x10 }, .lvts_sensor = { - { .dt_id = MT8195_MCU_BIG_CPU2 }, - { .dt_id = MT8195_MCU_BIG_CPU3 } + { .dt_id = MT8195_MCU_BIG_CPU2, + .cal_offsets = { 0x0d, 0x0e, 0x0f } }, + { .dt_id = MT8195_MCU_BIG_CPU3, + .cal_offsets = { 0x10, 0x11, 0x12 } } }, .num_lvts_sensor = 2, .offset = 0x100, }, { - .cal_offset = { 0x16, 0x19, 0x1c, 0x1f }, .lvts_sensor = { - { .dt_id = MT8195_MCU_LITTLE_CPU0 }, - { .dt_id = MT8195_MCU_LITTLE_CPU1 }, - { .dt_id = MT8195_MCU_LITTLE_CPU2 }, - { .dt_id = MT8195_MCU_LITTLE_CPU3 } + { .dt_id = MT8195_MCU_LITTLE_CPU0, + .cal_offsets = { 0x16, 0x17, 0x18 } }, + { .dt_id = MT8195_MCU_LITTLE_CPU1, + .cal_offsets = { 0x19, 0x1a, 0x1b } }, + { .dt_id = MT8195_MCU_LITTLE_CPU2, + .cal_offsets = { 0x1c, 0x1d, 0x1e } }, + { .dt_id = MT8195_MCU_LITTLE_CPU3, + .cal_offsets = { 0x1f, 0x20, 0x21 } } }, .num_lvts_sensor = 4, .offset = 0x200, @@ -1462,39 +1491,44 @@ static const struct lvts_ctrl_data mt8195_lvts_mcu_data_ctrl[] = { }; static const struct lvts_ctrl_data mt8195_lvts_ap_data_ctrl[] = { - { - .cal_offset = { 0x25, 0x28 }, + { .lvts_sensor = { - { .dt_id = MT8195_AP_VPU0 }, - { .dt_id = MT8195_AP_VPU1 } + { .dt_id = MT8195_AP_VPU0, + .cal_offsets = { 0x25, 0x26, 0x27 } }, + { .dt_id = MT8195_AP_VPU1, + .cal_offsets = { 0x28, 0x29, 0x2a } } }, .num_lvts_sensor = 2, .offset = 0x0, }, { - .cal_offset = { 0x2e, 0x31 }, .lvts_sensor = { - { .dt_id = MT8195_AP_GPU0 }, - { .dt_id = MT8195_AP_GPU1 } + { .dt_id = MT8195_AP_GPU0, + .cal_offsets = { 0x2e, 0x2f, 0x30 } }, + { .dt_id = MT8195_AP_GPU1, + .cal_offsets = { 0x31, 0x32, 0x33 } } }, .num_lvts_sensor = 2, .offset = 0x100, }, { - .cal_offset = { 0x37, 0x3a, 0x3d }, .lvts_sensor = { - { .dt_id = MT8195_AP_VDEC }, - { .dt_id = MT8195_AP_IMG }, - { .dt_id = MT8195_AP_INFRA }, + { .dt_id = MT8195_AP_VDEC, + .cal_offsets = { 0x37, 0x38, 0x39 } }, + { .dt_id = MT8195_AP_IMG, + .cal_offsets = { 0x3a, 0x3b, 0x3c } }, + { .dt_id = MT8195_AP_INFRA, + .cal_offsets = { 0x3d, 0x3e, 0x3f } } }, .num_lvts_sensor = 3, .offset = 0x200, }, { - .cal_offset = { 0x43, 0x46 }, .lvts_sensor = { - { .dt_id = MT8195_AP_CAM0 }, - { .dt_id = MT8195_AP_CAM1 } + { .dt_id = MT8195_AP_CAM0, + .cal_offsets = { 0x43, 0x44, 0x45 } }, + { .dt_id = MT8195_AP_CAM1, + .cal_offsets = { 0x46, 0x47, 0x48 } } }, .num_lvts_sensor = 2, .offset = 0x300, From 2cc0b1a2169b0f4af83cc5a52a1693c8ab2e2f1d Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Mon, 1 Apr 2024 23:25:39 -0400 Subject: [PATCH 31/72] thermal/drivers/mediatek/lvts_thermal: Guard against efuse data buffer overflow We don't want to silently fetch garbage past the actual buffer. Signed-off-by: Nicolas Pitre Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20240402032729.2736685-6-nico@fluxnic.net --- drivers/thermal/mediatek/lvts_thermal.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/thermal/mediatek/lvts_thermal.c b/drivers/thermal/mediatek/lvts_thermal.c index 9e6ed8bf6d9a..146799462338 100644 --- a/drivers/thermal/mediatek/lvts_thermal.c +++ b/drivers/thermal/mediatek/lvts_thermal.c @@ -672,7 +672,8 @@ static int lvts_sensor_init(struct device *dev, struct lvts_ctrl *lvts_ctrl, */ static int lvts_calibration_init(struct device *dev, struct lvts_ctrl *lvts_ctrl, const struct lvts_ctrl_data *lvts_ctrl_data, - u8 *efuse_calibration) + u8 *efuse_calibration, + size_t calib_len) { int i; @@ -680,6 +681,11 @@ static int lvts_calibration_init(struct device *dev, struct lvts_ctrl *lvts_ctrl const struct lvts_sensor_data *sensor = &lvts_ctrl_data->lvts_sensor[i]; + if (sensor->cal_offsets[0] >= calib_len || + sensor->cal_offsets[1] >= calib_len || + sensor->cal_offsets[2] >= calib_len) + return -EINVAL; + lvts_ctrl->calibration[i] = (efuse_calibration[sensor->cal_offsets[0]] << 0) + (efuse_calibration[sensor->cal_offsets[1]] << 8) + @@ -791,7 +797,8 @@ static int lvts_ctrl_init(struct device *dev, struct lvts_domain *lvts_td, ret = lvts_calibration_init(dev, &lvts_ctrl[i], &lvts_data->lvts_ctrl[i], - lvts_td->calib); + lvts_td->calib, + lvts_td->calib_len); if (ret) return ret; From a2ca202350f97bced8dac4cbfbb3cbf855973e57 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Mon, 1 Apr 2024 23:25:40 -0400 Subject: [PATCH 32/72] dt-bindings: thermal: mediatek: Add LVTS thermal controller definition for MT8186 Add LVTS thermal controller definition for MT8186. Signed-off-by: Nicolas Pitre Acked-by: Krzysztof Kozlowski Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20240402032729.2736685-7-nico@fluxnic.net --- .../bindings/thermal/mediatek,lvts-thermal.yaml | 2 ++ include/dt-bindings/thermal/mediatek,lvts-thermal.h | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml b/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml index e6665af52ee6..4173bae53032 100644 --- a/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml +++ b/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml @@ -19,6 +19,7 @@ properties: compatible: enum: - mediatek,mt7988-lvts-ap + - mediatek,mt8186-lvts - mediatek,mt8192-lvts-ap - mediatek,mt8192-lvts-mcu - mediatek,mt8195-lvts-ap @@ -75,6 +76,7 @@ allOf: compatible: contains: enum: + - mediatek,mt8186-lvts - mediatek,mt8195-lvts-ap - mediatek,mt8195-lvts-mcu then: diff --git a/include/dt-bindings/thermal/mediatek,lvts-thermal.h b/include/dt-bindings/thermal/mediatek,lvts-thermal.h index 997e2f55128a..433d298826d3 100644 --- a/include/dt-bindings/thermal/mediatek,lvts-thermal.h +++ b/include/dt-bindings/thermal/mediatek,lvts-thermal.h @@ -16,6 +16,16 @@ #define MT7988_ETHWARP_0 6 #define MT7988_ETHWARP_1 7 +#define MT8186_LITTLE_CPU0 0 +#define MT8186_LITTLE_CPU1 1 +#define MT8186_LITTLE_CPU2 2 +#define MT8186_CAM 3 +#define MT8186_BIG_CPU0 4 +#define MT8186_BIG_CPU1 5 +#define MT8186_NNA 6 +#define MT8186_ADSP 7 +#define MT8186_MFG 8 + #define MT8195_MCU_BIG_CPU0 0 #define MT8195_MCU_BIG_CPU1 1 #define MT8195_MCU_BIG_CPU2 2 From a4c1ab2f4c6c94edfc6481edf37e141079e24f1d Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Mon, 1 Apr 2024 23:25:42 -0400 Subject: [PATCH 33/72] thermal/drivers/mediatek/lvts_thermal: Add MT8186 support Various values extracted from the vendor's kernel driver. Signed-off-by: Nicolas Pitre Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20240402032729.2736685-9-nico@fluxnic.net --- drivers/thermal/mediatek/lvts_thermal.c | 61 +++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/drivers/thermal/mediatek/lvts_thermal.c b/drivers/thermal/mediatek/lvts_thermal.c index 146799462338..6f711aec0d0f 100644 --- a/drivers/thermal/mediatek/lvts_thermal.c +++ b/drivers/thermal/mediatek/lvts_thermal.c @@ -1375,6 +1375,59 @@ static int lvts_resume(struct device *dev) return 0; } +/* + * The MT8186 calibration data is stored as packed 3-byte little-endian + * values using a weird layout that makes sense only when viewed as a 32-bit + * hexadecimal word dump. Let's suppose SxBy where x = sensor number and + * y = byte number where the LSB is y=0. We then have: + * + * [S0B2-S0B1-S0B0-S1B2] [S1B1-S1B0-S2B2-S2B1] [S2B0-S3B2-S3B1-S3B0] + * + * However, when considering a byte stream, those appear as follows: + * + * [S1B2] [S0B0[ [S0B1] [S0B2] [S2B1] [S2B2] [S1B0] [S1B1] [S3B0] [S3B1] [S3B2] [S2B0] + * + * Hence the rather confusing offsets provided below. + */ +static const struct lvts_ctrl_data mt8186_lvts_data_ctrl[] = { + { + .lvts_sensor = { + { .dt_id = MT8186_LITTLE_CPU0, + .cal_offsets = { 5, 6, 7 } }, + { .dt_id = MT8186_LITTLE_CPU1, + .cal_offsets = { 10, 11, 4 } }, + { .dt_id = MT8186_LITTLE_CPU2, + .cal_offsets = { 15, 8, 9 } }, + { .dt_id = MT8186_CAM, + .cal_offsets = { 12, 13, 14 } } + }, + .num_lvts_sensor = 4, + .offset = 0x0, + }, + { + .lvts_sensor = { + { .dt_id = MT8186_BIG_CPU0, + .cal_offsets = { 22, 23, 16 } }, + { .dt_id = MT8186_BIG_CPU1, + .cal_offsets = { 27, 20, 21 } } + }, + .num_lvts_sensor = 2, + .offset = 0x100, + }, + { + .lvts_sensor = { + { .dt_id = MT8186_NNA, + .cal_offsets = { 29, 30, 31 } }, + { .dt_id = MT8186_ADSP, + .cal_offsets = { 34, 35, 28 } }, + { .dt_id = MT8186_MFG, + .cal_offsets = { 39, 32, 33 } } + }, + .num_lvts_sensor = 3, + .offset = 0x200, + } +}; + static const struct lvts_ctrl_data mt8192_lvts_mcu_data_ctrl[] = { { .lvts_sensor = { @@ -1549,6 +1602,13 @@ static const struct lvts_data mt7988_lvts_ap_data = { .temp_offset = LVTS_COEFF_B_MT7988, }; +static const struct lvts_data mt8186_lvts_data = { + .lvts_ctrl = mt8186_lvts_data_ctrl, + .num_lvts_ctrl = ARRAY_SIZE(mt8186_lvts_data_ctrl), + .temp_factor = LVTS_COEFF_A_MT7988, + .temp_offset = LVTS_COEFF_B_MT7988, +}; + static const struct lvts_data mt8192_lvts_mcu_data = { .lvts_ctrl = mt8192_lvts_mcu_data_ctrl, .num_lvts_ctrl = ARRAY_SIZE(mt8192_lvts_mcu_data_ctrl), @@ -1579,6 +1639,7 @@ static const struct lvts_data mt8195_lvts_ap_data = { static const struct of_device_id lvts_of_match[] = { { .compatible = "mediatek,mt7988-lvts-ap", .data = &mt7988_lvts_ap_data }, + { .compatible = "mediatek,mt8186-lvts", .data = &mt8186_lvts_data }, { .compatible = "mediatek,mt8192-lvts-mcu", .data = &mt8192_lvts_mcu_data }, { .compatible = "mediatek,mt8192-lvts-ap", .data = &mt8192_lvts_ap_data }, { .compatible = "mediatek,mt8195-lvts-mcu", .data = &mt8195_lvts_mcu_data }, From 684cbb49f9ceed6be0f33dac9d62089e7cd1c032 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Mon, 1 Apr 2024 23:25:44 -0400 Subject: [PATCH 34/72] thermal/drivers/mediatek/lvts_thermal: Provision for gt variable location The golden temperature calibration value in nvram is not always the 3rd byte. A future commit will prove this assumption wrong. Signed-off-by: Nicolas Pitre Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20240402032729.2736685-11-nico@fluxnic.net --- drivers/thermal/mediatek/lvts_thermal.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/thermal/mediatek/lvts_thermal.c b/drivers/thermal/mediatek/lvts_thermal.c index 6f711aec0d0f..34f5a8150430 100644 --- a/drivers/thermal/mediatek/lvts_thermal.c +++ b/drivers/thermal/mediatek/lvts_thermal.c @@ -116,6 +116,7 @@ struct lvts_data { int num_lvts_ctrl; int temp_factor; int temp_offset; + int gt_calib_bit_offset; }; struct lvts_sensor { @@ -745,20 +746,21 @@ static int lvts_calibration_read(struct device *dev, struct lvts_domain *lvts_td return 0; } -static int lvts_golden_temp_init(struct device *dev, u8 *calib, int temp_offset) +static int lvts_golden_temp_init(struct device *dev, u8 *calib, + const struct lvts_data *lvts_data) { u32 gt; /* - * The golden temp information is contained in the 4th byte (index = 3) - * of efuse data. + * The golden temp information is contained in the first 32-bit + * word of efuse data at a specific bit offset. */ - gt = calib[3]; + gt = (((u32 *)calib)[0] >> lvts_data->gt_calib_bit_offset) & 0xff; if (gt && gt < LVTS_GOLDEN_TEMP_MAX) golden_temp = gt; - golden_temp_offset = golden_temp * 500 + temp_offset; + golden_temp_offset = golden_temp * 500 + lvts_data->temp_offset; return 0; } @@ -777,7 +779,7 @@ static int lvts_ctrl_init(struct device *dev, struct lvts_domain *lvts_td, if (ret) return ret; - ret = lvts_golden_temp_init(dev, lvts_td->calib, lvts_data->temp_offset); + ret = lvts_golden_temp_init(dev, lvts_td->calib, lvts_data); if (ret) return ret; @@ -1600,6 +1602,7 @@ static const struct lvts_data mt7988_lvts_ap_data = { .num_lvts_ctrl = ARRAY_SIZE(mt7988_lvts_ap_data_ctrl), .temp_factor = LVTS_COEFF_A_MT7988, .temp_offset = LVTS_COEFF_B_MT7988, + .gt_calib_bit_offset = 24, }; static const struct lvts_data mt8186_lvts_data = { @@ -1607,6 +1610,7 @@ static const struct lvts_data mt8186_lvts_data = { .num_lvts_ctrl = ARRAY_SIZE(mt8186_lvts_data_ctrl), .temp_factor = LVTS_COEFF_A_MT7988, .temp_offset = LVTS_COEFF_B_MT7988, + .gt_calib_bit_offset = 24, }; static const struct lvts_data mt8192_lvts_mcu_data = { @@ -1614,6 +1618,7 @@ static const struct lvts_data mt8192_lvts_mcu_data = { .num_lvts_ctrl = ARRAY_SIZE(mt8192_lvts_mcu_data_ctrl), .temp_factor = LVTS_COEFF_A_MT8195, .temp_offset = LVTS_COEFF_B_MT8195, + .gt_calib_bit_offset = 24, }; static const struct lvts_data mt8192_lvts_ap_data = { @@ -1621,6 +1626,7 @@ static const struct lvts_data mt8192_lvts_ap_data = { .num_lvts_ctrl = ARRAY_SIZE(mt8192_lvts_ap_data_ctrl), .temp_factor = LVTS_COEFF_A_MT8195, .temp_offset = LVTS_COEFF_B_MT8195, + .gt_calib_bit_offset = 24, }; static const struct lvts_data mt8195_lvts_mcu_data = { @@ -1628,6 +1634,7 @@ static const struct lvts_data mt8195_lvts_mcu_data = { .num_lvts_ctrl = ARRAY_SIZE(mt8195_lvts_mcu_data_ctrl), .temp_factor = LVTS_COEFF_A_MT8195, .temp_offset = LVTS_COEFF_B_MT8195, + .gt_calib_bit_offset = 24, }; static const struct lvts_data mt8195_lvts_ap_data = { @@ -1635,6 +1642,7 @@ static const struct lvts_data mt8195_lvts_ap_data = { .num_lvts_ctrl = ARRAY_SIZE(mt8195_lvts_ap_data_ctrl), .temp_factor = LVTS_COEFF_A_MT8195, .temp_offset = LVTS_COEFF_B_MT8195, + .gt_calib_bit_offset = 24, }; static const struct of_device_id lvts_of_match[] = { From 11e6f4c3144743b2b2c26a5cd58ae3696379b3ab Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Mon, 1 Apr 2024 23:25:45 -0400 Subject: [PATCH 35/72] thermal/drivers/mediatek/lvts_thermal: Allow early empty sensor slots Some systems don't always populate sensor controller slots starting at slot 0. Use a bitmap instead of a count to indicate valid sensor slots. Also create a pretty iterator for that. About that iterator: it causes checkpatch to complain with "ERROR: Macros with multiple statements should be enclosed in a do - while loop". However this is not possible here. And many similar iterators do exist using the same form in the tree already. Signed-off-by: Nicolas Pitre Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20240402032729.2736685-12-nico@fluxnic.net --- drivers/thermal/mediatek/lvts_thermal.c | 63 +++++++++++++++---------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/drivers/thermal/mediatek/lvts_thermal.c b/drivers/thermal/mediatek/lvts_thermal.c index 34f5a8150430..ced4fa8d9425 100644 --- a/drivers/thermal/mediatek/lvts_thermal.c +++ b/drivers/thermal/mediatek/lvts_thermal.c @@ -107,10 +107,23 @@ struct lvts_ctrl_data { struct lvts_sensor_data lvts_sensor[LVTS_SENSOR_MAX]; int cal_offset[LVTS_SENSOR_MAX]; int num_lvts_sensor; + u8 valid_sensor_mask; int offset; int mode; }; +#define VALID_SENSOR_MAP(s0, s1, s2, s3) \ + .valid_sensor_mask = (((s0) ? BIT(0) : 0) | \ + ((s1) ? BIT(1) : 0) | \ + ((s2) ? BIT(2) : 0) | \ + ((s3) ? BIT(3) : 0)) + +#define lvts_for_each_valid_sensor(i, lvts_ctrl_data) \ + for ((i) = 0; (i) < LVTS_SENSOR_MAX; (i)++) \ + if (!((lvts_ctrl_data)->valid_sensor_mask & BIT(i))) \ + continue; \ + else + struct lvts_data { const struct lvts_ctrl_data *lvts_ctrl; int num_lvts_ctrl; @@ -134,7 +147,6 @@ struct lvts_ctrl { const struct lvts_data *lvts_data; u32 calibration[LVTS_SENSOR_MAX]; u32 hw_tshut_raw_temp; - int num_lvts_sensor; int mode; void __iomem *base; int low_thresh; @@ -346,7 +358,7 @@ static bool lvts_should_update_thresh(struct lvts_ctrl *lvts_ctrl, int high) if (high > lvts_ctrl->high_thresh) return true; - for (i = 0; i < lvts_ctrl->num_lvts_sensor; i++) + lvts_for_each_valid_sensor(i, lvts_ctrl->lvts_data->lvts_ctrl) if (lvts_ctrl->sensors[i].high_thresh == lvts_ctrl->high_thresh && lvts_ctrl->sensors[i].low_thresh == lvts_ctrl->low_thresh) return false; @@ -550,6 +562,7 @@ static int lvts_sensor_init(struct device *dev, struct lvts_ctrl *lvts_ctrl, const struct lvts_ctrl_data *lvts_ctrl_data) { struct lvts_sensor *lvts_sensor = lvts_ctrl->sensors; + void __iomem *msr_regs[] = { LVTS_MSR0(lvts_ctrl->base), LVTS_MSR1(lvts_ctrl->base), @@ -566,7 +579,7 @@ static int lvts_sensor_init(struct device *dev, struct lvts_ctrl *lvts_ctrl, int i; - for (i = 0; i < lvts_ctrl_data->num_lvts_sensor; i++) { + lvts_for_each_valid_sensor(i, lvts_ctrl_data) { int dt_id = lvts_ctrl_data->lvts_sensor[i].dt_id; @@ -606,8 +619,6 @@ static int lvts_sensor_init(struct device *dev, struct lvts_ctrl *lvts_ctrl, lvts_sensor[i].high_thresh = INT_MIN; }; - lvts_ctrl->num_lvts_sensor = lvts_ctrl_data->num_lvts_sensor; - return 0; } @@ -678,7 +689,7 @@ static int lvts_calibration_init(struct device *dev, struct lvts_ctrl *lvts_ctrl { int i; - for (i = 0; i < lvts_ctrl_data->num_lvts_sensor; i++) { + lvts_for_each_valid_sensor(i, lvts_ctrl_data) { const struct lvts_sensor_data *sensor = &lvts_ctrl_data->lvts_sensor[i]; @@ -1103,7 +1114,7 @@ static int lvts_ctrl_start(struct device *dev, struct lvts_ctrl *lvts_ctrl) u32 *sensor_bitmap = lvts_ctrl->mode == LVTS_MSR_IMMEDIATE_MODE ? sensor_imm_bitmap : sensor_filt_bitmap; - for (i = 0; i < lvts_ctrl->num_lvts_sensor; i++) { + lvts_for_each_valid_sensor(i, lvts_ctrl->lvts_data->lvts_ctrl) { int dt_id = lvts_sensors[i].dt_id; @@ -1326,7 +1337,7 @@ static const struct lvts_ctrl_data mt7988_lvts_ap_data_ctrl[] = { { .dt_id = MT7988_ETH2P5G_1, .cal_offsets = { 0x0c, 0x0d, 0x0e } } }, - .num_lvts_sensor = 4, + VALID_SENSOR_MAP(1, 1, 1, 1), .offset = 0x0, }, { @@ -1340,7 +1351,7 @@ static const struct lvts_ctrl_data mt7988_lvts_ap_data_ctrl[] = { { .dt_id = MT7988_ETHWARP_1, .cal_offsets = { 0x20, 0x21, 0x22 } } }, - .num_lvts_sensor = 4, + VALID_SENSOR_MAP(1, 1, 1, 1), .offset = 0x100, } }; @@ -1403,7 +1414,7 @@ static const struct lvts_ctrl_data mt8186_lvts_data_ctrl[] = { { .dt_id = MT8186_CAM, .cal_offsets = { 12, 13, 14 } } }, - .num_lvts_sensor = 4, + VALID_SENSOR_MAP(1, 1, 1, 1), .offset = 0x0, }, { @@ -1413,7 +1424,7 @@ static const struct lvts_ctrl_data mt8186_lvts_data_ctrl[] = { { .dt_id = MT8186_BIG_CPU1, .cal_offsets = { 27, 20, 21 } } }, - .num_lvts_sensor = 2, + VALID_SENSOR_MAP(1, 1, 0, 0), .offset = 0x100, }, { @@ -1425,7 +1436,7 @@ static const struct lvts_ctrl_data mt8186_lvts_data_ctrl[] = { { .dt_id = MT8186_MFG, .cal_offsets = { 39, 32, 33 } } }, - .num_lvts_sensor = 3, + VALID_SENSOR_MAP(1, 1, 1, 0), .offset = 0x200, } }; @@ -1438,7 +1449,7 @@ static const struct lvts_ctrl_data mt8192_lvts_mcu_data_ctrl[] = { { .dt_id = MT8192_MCU_BIG_CPU1, .cal_offsets = { 0x08, 0x09, 0x0a } } }, - .num_lvts_sensor = 2, + VALID_SENSOR_MAP(1, 1, 0, 0), .offset = 0x0, .mode = LVTS_MSR_FILTERED_MODE, }, @@ -1449,7 +1460,7 @@ static const struct lvts_ctrl_data mt8192_lvts_mcu_data_ctrl[] = { { .dt_id = MT8192_MCU_BIG_CPU3, .cal_offsets = { 0x10, 0x11, 0x12 } } }, - .num_lvts_sensor = 2, + VALID_SENSOR_MAP(1, 1, 0, 0), .offset = 0x100, .mode = LVTS_MSR_FILTERED_MODE, }, @@ -1464,7 +1475,7 @@ static const struct lvts_ctrl_data mt8192_lvts_mcu_data_ctrl[] = { { .dt_id = MT8192_MCU_LITTLE_CPU3, .cal_offsets = { 0x20, 0x21, 0x22 } } }, - .num_lvts_sensor = 4, + VALID_SENSOR_MAP(1, 1, 1, 1), .offset = 0x200, .mode = LVTS_MSR_FILTERED_MODE, } @@ -1478,7 +1489,7 @@ static const struct lvts_ctrl_data mt8192_lvts_ap_data_ctrl[] = { { .dt_id = MT8192_AP_VPU1, .cal_offsets = { 0x28, 0x29, 0x2a } } }, - .num_lvts_sensor = 2, + VALID_SENSOR_MAP(1, 1, 0, 0), .offset = 0x0, }, { @@ -1488,7 +1499,7 @@ static const struct lvts_ctrl_data mt8192_lvts_ap_data_ctrl[] = { { .dt_id = MT8192_AP_GPU1, .cal_offsets = { 0x30, 0x31, 0x32 } } }, - .num_lvts_sensor = 2, + VALID_SENSOR_MAP(1, 1, 0, 0), .offset = 0x100, }, { @@ -1498,7 +1509,7 @@ static const struct lvts_ctrl_data mt8192_lvts_ap_data_ctrl[] = { { .dt_id = MT8192_AP_CAM, .cal_offsets = { 0x38, 0x39, 0x3a } }, }, - .num_lvts_sensor = 2, + VALID_SENSOR_MAP(1, 1, 0, 0), .offset = 0x200, }, { @@ -1510,7 +1521,7 @@ static const struct lvts_ctrl_data mt8192_lvts_ap_data_ctrl[] = { { .dt_id = MT8192_AP_MD2, .cal_offsets = { 0x44, 0x45, 0x46 } } }, - .num_lvts_sensor = 3, + VALID_SENSOR_MAP(1, 1, 1, 0), .offset = 0x300, } }; @@ -1523,7 +1534,7 @@ static const struct lvts_ctrl_data mt8195_lvts_mcu_data_ctrl[] = { { .dt_id = MT8195_MCU_BIG_CPU1, .cal_offsets = { 0x07, 0x08, 0x09 } } }, - .num_lvts_sensor = 2, + VALID_SENSOR_MAP(1, 1, 0, 0), .offset = 0x0, }, { @@ -1533,7 +1544,7 @@ static const struct lvts_ctrl_data mt8195_lvts_mcu_data_ctrl[] = { { .dt_id = MT8195_MCU_BIG_CPU3, .cal_offsets = { 0x10, 0x11, 0x12 } } }, - .num_lvts_sensor = 2, + VALID_SENSOR_MAP(1, 1, 0, 0), .offset = 0x100, }, { @@ -1547,7 +1558,7 @@ static const struct lvts_ctrl_data mt8195_lvts_mcu_data_ctrl[] = { { .dt_id = MT8195_MCU_LITTLE_CPU3, .cal_offsets = { 0x1f, 0x20, 0x21 } } }, - .num_lvts_sensor = 4, + VALID_SENSOR_MAP(1, 1, 1, 1), .offset = 0x200, } }; @@ -1560,7 +1571,7 @@ static const struct lvts_ctrl_data mt8195_lvts_ap_data_ctrl[] = { { .dt_id = MT8195_AP_VPU1, .cal_offsets = { 0x28, 0x29, 0x2a } } }, - .num_lvts_sensor = 2, + VALID_SENSOR_MAP(1, 1, 0, 0), .offset = 0x0, }, { @@ -1570,7 +1581,7 @@ static const struct lvts_ctrl_data mt8195_lvts_ap_data_ctrl[] = { { .dt_id = MT8195_AP_GPU1, .cal_offsets = { 0x31, 0x32, 0x33 } } }, - .num_lvts_sensor = 2, + VALID_SENSOR_MAP(1, 1, 0, 0), .offset = 0x100, }, { @@ -1582,7 +1593,7 @@ static const struct lvts_ctrl_data mt8195_lvts_ap_data_ctrl[] = { { .dt_id = MT8195_AP_INFRA, .cal_offsets = { 0x3d, 0x3e, 0x3f } } }, - .num_lvts_sensor = 3, + VALID_SENSOR_MAP(1, 1, 1, 0), .offset = 0x200, }, { @@ -1592,7 +1603,7 @@ static const struct lvts_ctrl_data mt8195_lvts_ap_data_ctrl[] = { { .dt_id = MT8195_AP_CAM1, .cal_offsets = { 0x46, 0x47, 0x48 } } }, - .num_lvts_sensor = 2, + VALID_SENSOR_MAP(1, 1, 0, 0), .offset = 0x300, } }; From 78c88534e5e1f8c5334c380b626c6ee91b62161b Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Mon, 1 Apr 2024 23:25:46 -0400 Subject: [PATCH 36/72] dt-bindings: thermal: mediatek: Add LVTS thermal controller definition for MT8188 Add LVTS thermal controller definition for MT8188. Signed-off-by: Nicolas Pitre Reviewed-by: Rob Herring Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20240402032729.2736685-13-nico@fluxnic.net --- .../bindings/thermal/mediatek,lvts-thermal.yaml | 4 ++++ .../dt-bindings/thermal/mediatek,lvts-thermal.h | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml b/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml index 4173bae53032..331cf4e662e3 100644 --- a/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml +++ b/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml @@ -20,6 +20,8 @@ properties: enum: - mediatek,mt7988-lvts-ap - mediatek,mt8186-lvts + - mediatek,mt8188-lvts-ap + - mediatek,mt8188-lvts-mcu - mediatek,mt8192-lvts-ap - mediatek,mt8192-lvts-mcu - mediatek,mt8195-lvts-ap @@ -61,6 +63,8 @@ allOf: compatible: contains: enum: + - mediatek,mt8188-lvts-ap + - mediatek,mt8188-lvts-mcu - mediatek,mt8192-lvts-ap - mediatek,mt8192-lvts-mcu then: diff --git a/include/dt-bindings/thermal/mediatek,lvts-thermal.h b/include/dt-bindings/thermal/mediatek,lvts-thermal.h index 433d298826d3..bf95309d2525 100644 --- a/include/dt-bindings/thermal/mediatek,lvts-thermal.h +++ b/include/dt-bindings/thermal/mediatek,lvts-thermal.h @@ -26,6 +26,22 @@ #define MT8186_ADSP 7 #define MT8186_MFG 8 +#define MT8188_MCU_LITTLE_CPU0 0 +#define MT8188_MCU_LITTLE_CPU1 1 +#define MT8188_MCU_LITTLE_CPU2 2 +#define MT8188_MCU_LITTLE_CPU3 3 +#define MT8188_MCU_BIG_CPU0 4 +#define MT8188_MCU_BIG_CPU1 5 + +#define MT8188_AP_APU 0 +#define MT8188_AP_GPU1 1 +#define MT8188_AP_GPU2 2 +#define MT8188_AP_SOC1 3 +#define MT8188_AP_SOC2 4 +#define MT8188_AP_SOC3 5 +#define MT8188_AP_CAM1 6 +#define MT8188_AP_CAM2 7 + #define MT8195_MCU_BIG_CPU0 0 #define MT8195_MCU_BIG_CPU1 1 #define MT8195_MCU_BIG_CPU2 2 From f4745f546e6077e3ed2bab90e931154519e96ae0 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Mon, 1 Apr 2024 23:25:48 -0400 Subject: [PATCH 37/72] thermal/drivers/mediatek/lvts_thermal: Add MT8188 support Various values extracted from the vendor's kernel driver. Signed-off-by: Nicolas Pitre Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20240402032729.2736685-15-nico@fluxnic.net --- drivers/thermal/mediatek/lvts_thermal.c | 96 +++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/drivers/thermal/mediatek/lvts_thermal.c b/drivers/thermal/mediatek/lvts_thermal.c index ced4fa8d9425..86b2f44355ac 100644 --- a/drivers/thermal/mediatek/lvts_thermal.c +++ b/drivers/thermal/mediatek/lvts_thermal.c @@ -1441,6 +1441,84 @@ static const struct lvts_ctrl_data mt8186_lvts_data_ctrl[] = { } }; +static const struct lvts_ctrl_data mt8188_lvts_mcu_data_ctrl[] = { + { + .lvts_sensor = { + { .dt_id = MT8188_MCU_LITTLE_CPU0, + .cal_offsets = { 22, 23, 24 } }, + { .dt_id = MT8188_MCU_LITTLE_CPU1, + .cal_offsets = { 25, 26, 27 } }, + { .dt_id = MT8188_MCU_LITTLE_CPU2, + .cal_offsets = { 28, 29, 30 } }, + { .dt_id = MT8188_MCU_LITTLE_CPU3, + .cal_offsets = { 31, 32, 33 } }, + }, + VALID_SENSOR_MAP(1, 1, 1, 1), + .offset = 0x0, + .mode = LVTS_MSR_FILTERED_MODE, + }, + { + .lvts_sensor = { + { .dt_id = MT8188_MCU_BIG_CPU0, + .cal_offsets = { 34, 35, 36 } }, + { .dt_id = MT8188_MCU_BIG_CPU1, + .cal_offsets = { 37, 38, 39 } }, + }, + VALID_SENSOR_MAP(1, 1, 0, 0), + .offset = 0x100, + .mode = LVTS_MSR_FILTERED_MODE, + } +}; + +static const struct lvts_ctrl_data mt8188_lvts_ap_data_ctrl[] = { + { + .lvts_sensor = { + + { /* unused */ }, + { .dt_id = MT8188_AP_APU, + .cal_offsets = { 40, 41, 42 } }, + }, + VALID_SENSOR_MAP(0, 1, 0, 0), + .offset = 0x0, + .mode = LVTS_MSR_FILTERED_MODE, + }, + { + .lvts_sensor = { + { .dt_id = MT8188_AP_GPU1, + .cal_offsets = { 43, 44, 45 } }, + { .dt_id = MT8188_AP_GPU2, + .cal_offsets = { 46, 47, 48 } }, + { .dt_id = MT8188_AP_SOC1, + .cal_offsets = { 49, 50, 51 } }, + }, + VALID_SENSOR_MAP(1, 1, 1, 0), + .offset = 0x100, + .mode = LVTS_MSR_FILTERED_MODE, + }, + { + .lvts_sensor = { + { .dt_id = MT8188_AP_SOC2, + .cal_offsets = { 52, 53, 54 } }, + { .dt_id = MT8188_AP_SOC3, + .cal_offsets = { 55, 56, 57 } }, + }, + VALID_SENSOR_MAP(1, 1, 0, 0), + .offset = 0x200, + .mode = LVTS_MSR_FILTERED_MODE, + }, + { + .lvts_sensor = { + { .dt_id = MT8188_AP_CAM1, + .cal_offsets = { 58, 59, 60 } }, + { .dt_id = MT8188_AP_CAM2, + .cal_offsets = { 61, 62, 63 } }, + }, + VALID_SENSOR_MAP(1, 1, 0, 0), + .offset = 0x300, + .mode = LVTS_MSR_FILTERED_MODE, + } +}; + static const struct lvts_ctrl_data mt8192_lvts_mcu_data_ctrl[] = { { .lvts_sensor = { @@ -1624,6 +1702,22 @@ static const struct lvts_data mt8186_lvts_data = { .gt_calib_bit_offset = 24, }; +static const struct lvts_data mt8188_lvts_mcu_data = { + .lvts_ctrl = mt8188_lvts_mcu_data_ctrl, + .num_lvts_ctrl = ARRAY_SIZE(mt8188_lvts_mcu_data_ctrl), + .temp_factor = LVTS_COEFF_A_MT8195, + .temp_offset = LVTS_COEFF_B_MT8195, + .gt_calib_bit_offset = 20, +}; + +static const struct lvts_data mt8188_lvts_ap_data = { + .lvts_ctrl = mt8188_lvts_ap_data_ctrl, + .num_lvts_ctrl = ARRAY_SIZE(mt8188_lvts_ap_data_ctrl), + .temp_factor = LVTS_COEFF_A_MT8195, + .temp_offset = LVTS_COEFF_B_MT8195, + .gt_calib_bit_offset = 20, +}; + static const struct lvts_data mt8192_lvts_mcu_data = { .lvts_ctrl = mt8192_lvts_mcu_data_ctrl, .num_lvts_ctrl = ARRAY_SIZE(mt8192_lvts_mcu_data_ctrl), @@ -1659,6 +1753,8 @@ static const struct lvts_data mt8195_lvts_ap_data = { static const struct of_device_id lvts_of_match[] = { { .compatible = "mediatek,mt7988-lvts-ap", .data = &mt7988_lvts_ap_data }, { .compatible = "mediatek,mt8186-lvts", .data = &mt8186_lvts_data }, + { .compatible = "mediatek,mt8188-lvts-mcu", .data = &mt8188_lvts_mcu_data }, + { .compatible = "mediatek,mt8188-lvts-ap", .data = &mt8188_lvts_ap_data }, { .compatible = "mediatek,mt8192-lvts-mcu", .data = &mt8192_lvts_mcu_data }, { .compatible = "mediatek,mt8192-lvts-ap", .data = &mt8192_lvts_ap_data }, { .compatible = "mediatek,mt8195-lvts-mcu", .data = &mt8195_lvts_mcu_data }, From 6ef6d5ff5eddb3457eef8d78d7b8b451be19b5c3 Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Tue, 23 Apr 2024 09:59:00 +0800 Subject: [PATCH 38/72] thermal/drivers/loongson2: Trivial code style adjustment Here are some minor code style adjustment. Such as fix whitespace code style; align function call arguments to opening parenthesis. Signed-off-by: Binbin Zhou Acked-by: Huacai Chen Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/ccca50f2ad3fd8c84fcbfcb1f875427ea7f637a0.1713837379.git.zhoubinbin@loongson.cn --- drivers/thermal/loongson2_thermal.c | 65 +++++++++++++++-------------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/drivers/thermal/loongson2_thermal.c b/drivers/thermal/loongson2_thermal.c index 0f475fe46bc9..5c9cf45a3719 100644 --- a/drivers/thermal/loongson2_thermal.c +++ b/drivers/thermal/loongson2_thermal.c @@ -14,47 +14,51 @@ #include #include #include + #include "thermal_hwmon.h" -#define LOONGSON2_MAX_SENSOR_SEL_NUM 3 +#define LOONGSON2_MAX_SENSOR_SEL_NUM 3 -#define LOONGSON2_THSENS_CTRL_HI_REG 0x0 -#define LOONGSON2_THSENS_CTRL_LOW_REG 0x8 -#define LOONGSON2_THSENS_STATUS_REG 0x10 -#define LOONGSON2_THSENS_OUT_REG 0x14 +#define LOONGSON2_THSENS_CTRL_HI_REG 0x0 +#define LOONGSON2_THSENS_CTRL_LOW_REG 0x8 +#define LOONGSON2_THSENS_STATUS_REG 0x10 +#define LOONGSON2_THSENS_OUT_REG 0x14 -#define LOONGSON2_THSENS_INT_LO BIT(0) -#define LOONGSON2_THSENS_INT_HIGH BIT(1) -#define LOONGSON2_THSENS_OUT_MASK 0xFF +#define LOONGSON2_THSENS_INT_LO BIT(0) +#define LOONGSON2_THSENS_INT_HIGH BIT(1) +#define LOONGSON2_THSENS_INT_EN (LOONGSON2_THSENS_INT_LO | \ + LOONGSON2_THSENS_INT_HIGH) +#define LOONGSON2_THSENS_OUT_MASK 0xFF struct loongson2_thermal_chip_data { - unsigned int thermal_sensor_sel; + unsigned int thermal_sensor_sel; }; struct loongson2_thermal_data { - void __iomem *regs; + void __iomem *regs; const struct loongson2_thermal_chip_data *chip_data; }; -static int loongson2_thermal_set(struct loongson2_thermal_data *data, - int low, int high, bool enable) +static void loongson2_set_ctrl_regs(struct loongson2_thermal_data *data, + int ctrl_data, bool low, bool enable) { - u64 reg_ctrl = 0; - int reg_off = data->chip_data->thermal_sensor_sel * 2; + int reg_ctrl = 0; + int reg_off = data->chip_data->thermal_sensor_sel * 2; + int ctrl_reg = low ? LOONGSON2_THSENS_CTRL_LOW_REG : LOONGSON2_THSENS_CTRL_HI_REG; - low = clamp(-40, low, high); - high = clamp(125, low, high); - - low += HECTO; - high += HECTO; - - reg_ctrl = low; + reg_ctrl = ctrl_data + HECTO; reg_ctrl |= enable ? 0x100 : 0; - writew(reg_ctrl, data->regs + LOONGSON2_THSENS_CTRL_LOW_REG + reg_off); + writew(reg_ctrl, data->regs + ctrl_reg + reg_off); +} - reg_ctrl = high; - reg_ctrl |= enable ? 0x100 : 0; - writew(reg_ctrl, data->regs + LOONGSON2_THSENS_CTRL_HI_REG + reg_off); +static int loongson2_thermal_set(struct loongson2_thermal_data *data, + int low, int high, bool enable) +{ + /* Set low temperature threshold */ + loongson2_set_ctrl_regs(data, clamp(-40, low, high), true, enable); + + /* Set high temperature threshold */ + loongson2_set_ctrl_regs(data, clamp(125, low, high), false, enable); return 0; } @@ -75,8 +79,7 @@ static irqreturn_t loongson2_thermal_irq_thread(int irq, void *dev) struct thermal_zone_device *tzd = dev; struct loongson2_thermal_data *data = thermal_zone_device_priv(tzd); - writeb(LOONGSON2_THSENS_INT_LO | LOONGSON2_THSENS_INT_HIGH, data->regs + - LOONGSON2_THSENS_STATUS_REG); + writeb(LOONGSON2_THSENS_INT_EN, data->regs + LOONGSON2_THSENS_STATUS_REG); thermal_zone_device_update(tzd, THERMAL_EVENT_UNSPECIFIED); @@ -116,14 +119,13 @@ static int loongson2_thermal_probe(struct platform_device *pdev) if (irq < 0) return irq; - writeb(LOONGSON2_THSENS_INT_LO | LOONGSON2_THSENS_INT_HIGH, data->regs + - LOONGSON2_THSENS_STATUS_REG); + writeb(LOONGSON2_THSENS_INT_EN, data->regs + LOONGSON2_THSENS_STATUS_REG); loongson2_thermal_set(data, 0, 0, false); for (i = 0; i <= LOONGSON2_MAX_SENSOR_SEL_NUM; i++) { tzd = devm_thermal_of_zone_register(dev, i, data, - &loongson2_of_thermal_ops); + &loongson2_of_thermal_ops); if (!IS_ERR(tzd)) break; @@ -135,7 +137,7 @@ static int loongson2_thermal_probe(struct platform_device *pdev) } ret = devm_request_threaded_irq(dev, irq, NULL, loongson2_thermal_irq_thread, - IRQF_ONESHOT, "loongson2_thermal", tzd); + IRQF_ONESHOT, "loongson2_thermal", tzd); if (ret < 0) return dev_err_probe(dev, ret, "failed to request alarm irq\n"); @@ -167,4 +169,5 @@ static struct platform_driver loongson2_thermal_driver = { module_platform_driver(loongson2_thermal_driver); MODULE_DESCRIPTION("Loongson2 thermal driver"); +MODULE_AUTHOR("Loongson Technology Corporation Limited"); MODULE_LICENSE("GPL"); From 25c7d8472f6e90390931e93f59135478af3e5d86 Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Tue, 23 Apr 2024 09:59:01 +0800 Subject: [PATCH 39/72] dt-bindings: thermal: loongson,ls2k-thermal: Add Loongson-2K0500 compatible The thermal on the Loongson-2K0500 shares the design with the Loongson-2K1000. Define corresponding compatible string, having the loongson,ls2k1000-thermal as a fallback. Signed-off-by: Binbin Zhou Acked-by: Rob Herring Acked-by: Huacai Chen Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/26524a63abd2d032e4c45efe6ce3fedb46841768.1713837379.git.zhoubinbin@loongson.cn --- .../devicetree/bindings/thermal/loongson,ls2k-thermal.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/thermal/loongson,ls2k-thermal.yaml b/Documentation/devicetree/bindings/thermal/loongson,ls2k-thermal.yaml index b634f57cd011..9748a479dcd4 100644 --- a/Documentation/devicetree/bindings/thermal/loongson,ls2k-thermal.yaml +++ b/Documentation/devicetree/bindings/thermal/loongson,ls2k-thermal.yaml @@ -20,6 +20,7 @@ properties: - loongson,ls2k1000-thermal - items: - enum: + - loongson,ls2k0500-thermal - loongson,ls2k2000-thermal - const: loongson,ls2k1000-thermal From c8c4353685778e75e186103411e9d01a4a3f2b90 Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Tue, 23 Apr 2024 09:59:02 +0800 Subject: [PATCH 40/72] dt-bindings: thermal: loongson,ls2k-thermal: Fix incorrect compatible definition The temperature output register of the Loongson-2K2000 is defined in the chip configuration domain, which is different from the Loongson-2K1000, so it can't be fallbacked. We need to use two groups of registers to describe it: the first group is the high and low temperature threshold setting register; the second group is the temperature output register. It is true that this fix will cause ABI corruption, but it is necessary otherwise the Loongson-2K2000 temperature sensor will not work properly. Fixes: 72684d99a854 ("thermal: dt-bindings: add loongson-2 thermal") Cc: Yinbo Zhu Signed-off-by: Binbin Zhou Reviewed-by: Krzysztof Kozlowski Acked-by: Huacai Chen Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/5198999d679f1a1c3457385acb9fadfc85da1f1e.1713837379.git.zhoubinbin@loongson.cn --- .../thermal/loongson,ls2k-thermal.yaml | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/thermal/loongson,ls2k-thermal.yaml b/Documentation/devicetree/bindings/thermal/loongson,ls2k-thermal.yaml index 9748a479dcd4..ca81c8afba79 100644 --- a/Documentation/devicetree/bindings/thermal/loongson,ls2k-thermal.yaml +++ b/Documentation/devicetree/bindings/thermal/loongson,ls2k-thermal.yaml @@ -18,14 +18,15 @@ properties: oneOf: - enum: - loongson,ls2k1000-thermal + - loongson,ls2k2000-thermal - items: - enum: - loongson,ls2k0500-thermal - - loongson,ls2k2000-thermal - const: loongson,ls2k1000-thermal reg: - maxItems: 1 + minItems: 1 + maxItems: 2 interrupts: maxItems: 1 @@ -39,6 +40,24 @@ required: - interrupts - '#thermal-sensor-cells' +if: + properties: + compatible: + contains: + enum: + - loongson,ls2k2000-thermal + +then: + properties: + reg: + minItems: 2 + maxItems: 2 + +else: + properties: + reg: + maxItems: 1 + unevaluatedProperties: false examples: From 734b5def91b594d3aa1147d60c45eded139ce2eb Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Tue, 23 Apr 2024 09:59:26 +0800 Subject: [PATCH 41/72] thermal/drivers/loongson2: Add Loongson-2K2000 support The Loongson-2K2000 and Loongson-2K1000 have similar thermal sensors, except that the temperature is read differently. In particular, the temperature output registers of the Loongson-2K2000 are defined in the chip configuration domain and are read in a different way. Signed-off-by: Binbin Zhou Acked-by: Huacai Chen Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/fdbfdcc3231a36a4ee0bcf1377ddcbd6f8c944b5.1713837379.git.zhoubinbin@loongson.cn --- drivers/thermal/loongson2_thermal.c | 64 +++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/drivers/thermal/loongson2_thermal.c b/drivers/thermal/loongson2_thermal.c index 5c9cf45a3719..2d6b75b0539f 100644 --- a/drivers/thermal/loongson2_thermal.c +++ b/drivers/thermal/loongson2_thermal.c @@ -30,12 +30,20 @@ LOONGSON2_THSENS_INT_HIGH) #define LOONGSON2_THSENS_OUT_MASK 0xFF +/* + * This flag is used to indicate the temperature reading + * method of the Loongson-2K2000 + */ +#define LS2K2000_THSENS_OUT_FLAG BIT(0) + struct loongson2_thermal_chip_data { unsigned int thermal_sensor_sel; + unsigned int flags; }; struct loongson2_thermal_data { - void __iomem *regs; + void __iomem *ctrl_reg; + void __iomem *temp_reg; const struct loongson2_thermal_chip_data *chip_data; }; @@ -48,7 +56,7 @@ static void loongson2_set_ctrl_regs(struct loongson2_thermal_data *data, reg_ctrl = ctrl_data + HECTO; reg_ctrl |= enable ? 0x100 : 0; - writew(reg_ctrl, data->regs + ctrl_reg + reg_off); + writew(reg_ctrl, data->ctrl_reg + ctrl_reg + reg_off); } static int loongson2_thermal_set(struct loongson2_thermal_data *data, @@ -63,13 +71,24 @@ static int loongson2_thermal_set(struct loongson2_thermal_data *data, return 0; } -static int loongson2_thermal_get_temp(struct thermal_zone_device *tz, int *temp) +static int loongson2_2k1000_get_temp(struct thermal_zone_device *tz, int *temp) { - u32 reg_val; + int val; struct loongson2_thermal_data *data = thermal_zone_device_priv(tz); - reg_val = readl(data->regs + LOONGSON2_THSENS_OUT_REG); - *temp = ((reg_val & LOONGSON2_THSENS_OUT_MASK) - HECTO) * KILO; + val = readl(data->ctrl_reg + LOONGSON2_THSENS_OUT_REG); + *temp = ((val & LOONGSON2_THSENS_OUT_MASK) - HECTO) * KILO; + + return 0; +} + +static int loongson2_2k2000_get_temp(struct thermal_zone_device *tz, int *temp) +{ + int val; + struct loongson2_thermal_data *data = thermal_zone_device_priv(tz); + + val = readl(data->temp_reg); + *temp = ((val & 0xffff) * 820 / 0x4000 - 311) * KILO; return 0; } @@ -79,7 +98,7 @@ static irqreturn_t loongson2_thermal_irq_thread(int irq, void *dev) struct thermal_zone_device *tzd = dev; struct loongson2_thermal_data *data = thermal_zone_device_priv(tzd); - writeb(LOONGSON2_THSENS_INT_EN, data->regs + LOONGSON2_THSENS_STATUS_REG); + writeb(LOONGSON2_THSENS_INT_EN, data->ctrl_reg + LOONGSON2_THSENS_STATUS_REG); thermal_zone_device_update(tzd, THERMAL_EVENT_UNSPECIFIED); @@ -93,8 +112,8 @@ static int loongson2_thermal_set_trips(struct thermal_zone_device *tz, int low, return loongson2_thermal_set(data, low/MILLI, high/MILLI, true); } -static const struct thermal_zone_device_ops loongson2_of_thermal_ops = { - .get_temp = loongson2_thermal_get_temp, +static struct thermal_zone_device_ops loongson2_of_thermal_ops = { + .get_temp = loongson2_2k1000_get_temp, .set_trips = loongson2_thermal_set_trips, }; @@ -111,15 +130,24 @@ static int loongson2_thermal_probe(struct platform_device *pdev) data->chip_data = device_get_match_data(dev); - data->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(data->regs)) - return PTR_ERR(data->regs); + data->ctrl_reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(data->ctrl_reg)) + return PTR_ERR(data->ctrl_reg); + + /* The temperature output register is separate for Loongson-2K2000 */ + if (data->chip_data->flags & LS2K2000_THSENS_OUT_FLAG) { + data->temp_reg = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(data->temp_reg)) + return PTR_ERR(data->temp_reg); + + loongson2_of_thermal_ops.get_temp = loongson2_2k2000_get_temp; + } irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; - writeb(LOONGSON2_THSENS_INT_EN, data->regs + LOONGSON2_THSENS_STATUS_REG); + writeb(LOONGSON2_THSENS_INT_EN, data->ctrl_reg + LOONGSON2_THSENS_STATUS_REG); loongson2_thermal_set(data, 0, 0, false); @@ -148,6 +176,12 @@ static int loongson2_thermal_probe(struct platform_device *pdev) static const struct loongson2_thermal_chip_data loongson2_thermal_ls2k1000_data = { .thermal_sensor_sel = 0, + .flags = 0, +}; + +static const struct loongson2_thermal_chip_data loongson2_thermal_ls2k2000_data = { + .thermal_sensor_sel = 0, + .flags = LS2K2000_THSENS_OUT_FLAG, }; static const struct of_device_id of_loongson2_thermal_match[] = { @@ -155,6 +189,10 @@ static const struct of_device_id of_loongson2_thermal_match[] = { .compatible = "loongson,ls2k1000-thermal", .data = &loongson2_thermal_ls2k1000_data, }, + { + .compatible = "loongson,ls2k2000-thermal", + .data = &loongson2_thermal_ls2k2000_data, + }, { /* end */ } }; MODULE_DEVICE_TABLE(of, of_loongson2_thermal_match); From 530c932bdf753a58cb29ba9eb39d9514458f9073 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 18:04:39 +0200 Subject: [PATCH 42/72] 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 c3b2943a2db8..5a9095a6d9ea 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); From 4526c581098e3fe08535345c6526654a9a55a695 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 18:05:44 +0200 Subject: [PATCH 43/72] 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 5a9095a6d9ea..ffc800484b8f 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 */ From 0ae204a667452e58a1e5081c18a179e557df33c8 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 18:06:34 +0200 Subject: [PATCH 44/72] 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 ffc800484b8f..acb52c9ee10f 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); From 976f44133f76eb24becdf3336d832eb3c720b458 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 18:08:12 +0200 Subject: [PATCH 45/72] 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 62fe062d7ff5..221e1924240d 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 9a3585492558..95cbf7a3d169 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, From 41ddbcc6fd2cd8ec3100fdea9044f3f377b6ec11 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 18:10:14 +0200 Subject: [PATCH 46/72] 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 ac1d02193a1b..008b3ed60a91 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); From ca0e9728d37215afe943d508a1935d13a96ea88e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 18:12:45 +0200 Subject: [PATCH 47/72] 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 008b3ed60a91..7450ab77d5f0 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 = { From a6ce8c7da59bbdafcab23b5d26f26865af77a5fa Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 18:13:55 +0200 Subject: [PATCH 48/72] 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 ee2fb4e63d14..5c223445f169 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); From e4065f144fa60df56a8596bdc364a5b4fe8cbf40 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 18:43:07 +0200 Subject: [PATCH 49/72] 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 5c223445f169..30a32ad8c473 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) From fe036266504796c84adee28b64c347d3acf4206e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 18:44:14 +0200 Subject: [PATCH 50/72] 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 30a32ad8c473..0196a1a02290 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); From bec55332c24eb671a1b6de17b38a70dee2472245 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 18:57:38 +0200 Subject: [PATCH 51/72] 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 6ef8cfde7749..ff81aeb5ac04 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); } +} - return 0; +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; + + 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); From 0292991ce46c75c28d6ccfe7a7f8ac962a824291 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 18:58:56 +0200 Subject: [PATCH 52/72] 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 ff81aeb5ac04..ca6d6152ccf2 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; } From c98e24795e8b53ff68b317ed31ddcc37cf8b6a26 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 19:00:10 +0200 Subject: [PATCH 53/72] 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 ca6d6152ccf2..ce0ea571ed67 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); From c1beda1cfca53363ad3261c194df6cba4198f021 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 19:03:10 +0200 Subject: [PATCH 54/72] 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 7a1790b7e8f5..75137b419eb2 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); From ad2f8bccd0e6e65af4e771e69cc2f2cfa69a6e07 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 19:42:35 +0200 Subject: [PATCH 55/72] 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 221e1924240d..39ea842d883d 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 95cbf7a3d169..d9785e5bbb08 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; From 2ae0998c672ccf3bb1c480a5aca104ac8a98a287 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Apr 2024 19:44:34 +0200 Subject: [PATCH 56/72] 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 39ea842d883d..87b3cb8679d5 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) From 0a293c77580581c4b058eb40287acadac6ffd14a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 17 Apr 2024 15:09:46 +0200 Subject: [PATCH 57/72] 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 87b3cb8679d5..914aaee6b39a 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 40ea0071a691..878f047e72ac 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); From e271f9974d7e35a6eb05224660b2084035085173 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 17 Apr 2024 15:10:34 +0200 Subject: [PATCH 58/72] 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 878f047e72ac..4f7b50e08c85 100644 --- a/drivers/thermal/thermal_debugfs.c +++ b/drivers/thermal/thermal_debugfs.c @@ -679,9 +679,9 @@ void thermal_debug_tz_trip_down(struct thermal_zone_device *tz, 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); From 8dff6e8438351c627289fd06893c36f3bd9666e5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 17 Apr 2024 15:11:50 +0200 Subject: [PATCH 59/72] 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 914aaee6b39a..4debd3c9f327 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 4f7b50e08c85..357cd5f2de64 100644 --- a/drivers/thermal/thermal_debugfs.c +++ b/drivers/thermal/thermal_debugfs.c @@ -676,7 +676,7 @@ void thermal_debug_tz_trip_down(struct thermal_zone_device *tz, 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 155b9af5fe87..027c145501dd 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 */ From a6258fde8de34b2bfd7e1d4e55df261426e49071 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 17 Apr 2024 17:51:29 +0200 Subject: [PATCH 60/72] 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 357cd5f2de64..d9f33df2ad2d 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; From f831892e2351dc13b37b4c1fc60472be781a15be Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 23 Apr 2024 21:01:15 +0200 Subject: [PATCH 61/72] 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 4debd3c9f327..64036372e240 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) From f4ae18fcb652c6cccc834ded525ac37f91d5cdb1 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 25 Apr 2024 14:24:10 +0200 Subject: [PATCH 62/72] thermal/debugfs: Create records for cdev states as they get used Because thermal_debug_cdev_state_update() only creates a duration record for the old state of a cooling device, if its new state is used for the first time, there will be no record for it and cdev_dt_seq_show() will not print the duration information for it even though it contains code to compute the duration value in that case. Address this by making thermal_debug_cdev_state_update() create a duration record for the new state if there is none. Fixes: 755113d76786 ("thermal/debugfs: Add thermal cooling device debugfs information") Reported-by: Lukasz Luba Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Tested-by: Lukasz Luba --- drivers/thermal/thermal_debugfs.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/thermal/thermal_debugfs.c b/drivers/thermal/thermal_debugfs.c index 771ef5ecadf6..c93d2b5405de 100644 --- a/drivers/thermal/thermal_debugfs.c +++ b/drivers/thermal/thermal_debugfs.c @@ -435,6 +435,14 @@ void thermal_debug_cdev_state_update(const struct thermal_cooling_device *cdev, } cdev_dbg->current_state = new_state; + + /* + * Create a record for the new state if it is not there, so its + * duration will be printed by cdev_dt_seq_show() as expected if it + * runs before the next state transition. + */ + thermal_debugfs_cdev_record_get(thermal_dbg, cdev_dbg->durations, new_state); + transition = (old_state << 16) | new_state; /* From 31a0fa0019b022024cc082ae292951a596b06f8c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 25 Apr 2024 14:24:20 +0200 Subject: [PATCH 63/72] thermal/debugfs: Pass cooling device state to thermal_debug_cdev_add() If cdev_dt_seq_show() runs before the first state transition of a cooling device, it will not print any state residency information for it, even though it might be reasonably expected to print residency information for the initial state of the cooling device. For this reason, rearrange the code to get the initial state of a cooling device at the registration time and pass it to thermal_debug_cdev_add(), so that the latter can create a duration record for that state which will allow cdev_dt_seq_show() to print its residency information. Fixes: 755113d76786 ("thermal/debugfs: Add thermal cooling device debugfs information") Reported-by: Lukasz Luba Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Tested-by: Lukasz Luba --- drivers/thermal/thermal_core.c | 9 +++++++-- drivers/thermal/thermal_debugfs.c | 12 ++++++++++-- drivers/thermal/thermal_debugfs.h | 4 ++-- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 64036372e240..f43237ef2cd0 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -935,6 +935,7 @@ __thermal_cooling_device_register(struct device_node *np, { struct thermal_cooling_device *cdev; struct thermal_zone_device *pos = NULL; + unsigned long current_state; int id, ret; if (!ops || !ops->get_max_state || !ops->get_cur_state || @@ -972,6 +973,10 @@ __thermal_cooling_device_register(struct device_node *np, if (ret) goto out_cdev_type; + ret = cdev->ops->get_cur_state(cdev, ¤t_state); + if (ret) + goto out_cdev_type; + thermal_cooling_device_setup_sysfs(cdev); ret = dev_set_name(&cdev->device, "cooling_device%d", cdev->id); @@ -985,6 +990,8 @@ __thermal_cooling_device_register(struct device_node *np, return ERR_PTR(ret); } + thermal_debug_cdev_add(cdev, current_state); + /* Add 'this' new cdev to the global cdev list */ mutex_lock(&thermal_list_lock); @@ -1000,8 +1007,6 @@ __thermal_cooling_device_register(struct device_node *np, mutex_unlock(&thermal_list_lock); - thermal_debug_cdev_add(cdev); - return cdev; out_cooling_dev: diff --git a/drivers/thermal/thermal_debugfs.c b/drivers/thermal/thermal_debugfs.c index c93d2b5405de..59b1d999383c 100644 --- a/drivers/thermal/thermal_debugfs.c +++ b/drivers/thermal/thermal_debugfs.c @@ -468,8 +468,9 @@ void thermal_debug_cdev_state_update(const struct thermal_cooling_device *cdev, * Allocates a cooling device object for debug, initializes the * statistics and create the entries in sysfs. * @cdev: a pointer to a cooling device + * @state: current state of the cooling device */ -void thermal_debug_cdev_add(struct thermal_cooling_device *cdev) +void thermal_debug_cdev_add(struct thermal_cooling_device *cdev, int state) { struct thermal_debugfs *thermal_dbg; struct cdev_debugfs *cdev_dbg; @@ -486,9 +487,16 @@ void thermal_debug_cdev_add(struct thermal_cooling_device *cdev) INIT_LIST_HEAD(&cdev_dbg->durations[i]); } - cdev_dbg->current_state = 0; + cdev_dbg->current_state = state; cdev_dbg->timestamp = ktime_get(); + /* + * Create a record for the initial cooling device state, so its + * duration will be printed by cdev_dt_seq_show() as expected if it + * runs before the first state transition. + */ + thermal_debugfs_cdev_record_get(thermal_dbg, cdev_dbg->durations, state); + debugfs_create_file("trans_table", 0400, thermal_dbg->d_top, thermal_dbg, &tt_fops); diff --git a/drivers/thermal/thermal_debugfs.h b/drivers/thermal/thermal_debugfs.h index 027c145501dd..74ee65ee82ff 100644 --- a/drivers/thermal/thermal_debugfs.h +++ b/drivers/thermal/thermal_debugfs.h @@ -2,7 +2,7 @@ #ifdef CONFIG_THERMAL_DEBUGFS void thermal_debug_init(void); -void thermal_debug_cdev_add(struct thermal_cooling_device *cdev); +void thermal_debug_cdev_add(struct thermal_cooling_device *cdev, int state); void thermal_debug_cdev_remove(struct thermal_cooling_device *cdev); void thermal_debug_cdev_state_update(const struct thermal_cooling_device *cdev, int state); void thermal_debug_tz_add(struct thermal_zone_device *tz); @@ -14,7 +14,7 @@ void thermal_debug_tz_trip_down(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) {} +static inline void thermal_debug_cdev_add(struct thermal_cooling_device *cdev, int state) {} static inline void thermal_debug_cdev_remove(struct thermal_cooling_device *cdev) {} static inline void thermal_debug_cdev_state_update(const struct thermal_cooling_device *cdev, int state) {} From bd700ba9adef01cf8aaeb5f5fcf911ee1a705543 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 25 Apr 2024 14:24:02 +0200 Subject: [PATCH 64/72] thermal/debugfs: Avoid printing zero duration for mitigation events in progress If a thermal mitigation event is in progress, its duration value has not been updated yet, so 0 will be printed as the event duration by tze_seq_show() which is confusing. Avoid doing that by marking the beginning of the event with the KTIME_MIN duration value and making tze_seq_show() compute the current event duration on the fly, in which case '>' will be printed instead of '=' in the event duration value field. Similarly, for trip points that have been crossed on the down, mark the end of mitigation with the KTIME_MAX timestamp value and make tze_seq_show() compute the current duration on the fly for the trip points still involved in the mitigation, in which cases the duration value printed by it will be prepended with a '>' character. Fixes: 7ef01f228c9f ("thermal/debugfs: Add thermal debugfs information for mitigation episodes") Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Tested-by: Lukasz Luba --- drivers/thermal/thermal_debugfs.c | 39 ++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/drivers/thermal/thermal_debugfs.c b/drivers/thermal/thermal_debugfs.c index 59b1d999383c..91f9c21235a8 100644 --- a/drivers/thermal/thermal_debugfs.c +++ b/drivers/thermal/thermal_debugfs.c @@ -556,6 +556,7 @@ static struct tz_episode *thermal_debugfs_tz_event_alloc(struct thermal_zone_dev INIT_LIST_HEAD(&tze->node); tze->timestamp = now; + tze->duration = KTIME_MIN; for (i = 0; i < tz->num_trips; i++) { tze->trip_stats[i].min = INT_MAX; @@ -691,6 +692,9 @@ void thermal_debug_tz_trip_down(struct thermal_zone_device *tz, tze->trip_stats[trip_id].duration = ktime_add(delta, tze->trip_stats[trip_id].duration); + /* Mark the end of mitigation for this trip point. */ + tze->trip_stats[trip_id].timestamp = KTIME_MAX; + /* * This event closes the mitigation as we are crossing the * last trip point the way down. @@ -766,15 +770,25 @@ static int tze_seq_show(struct seq_file *s, void *v) struct thermal_trip_desc *td; struct tz_episode *tze; const char *type; + u64 duration_ms; int trip_id; + char c; tze = list_entry((struct list_head *)v, struct tz_episode, node); - seq_printf(s, ",-Mitigation at %lluus, duration=%llums\n", - ktime_to_us(tze->timestamp), - ktime_to_ms(tze->duration)); + if (tze->duration == KTIME_MIN) { + /* Mitigation in progress. */ + duration_ms = ktime_to_ms(ktime_sub(ktime_get(), tze->timestamp)); + c = '>'; + } else { + duration_ms = ktime_to_ms(tze->duration); + c = '='; + } - seq_printf(s, "| trip | type | temp(°mC) | hyst(°mC) | duration | avg(°mC) | min(°mC) | max(°mC) |\n"); + seq_printf(s, ",-Mitigation at %lluus, duration%c%llums\n", + ktime_to_us(tze->timestamp), c, duration_ms); + + seq_printf(s, "| trip | type | temp(°mC) | hyst(°mC) | duration | avg(°mC) | min(°mC) | max(°mC) |\n"); for_each_trip_desc(tz, td) { const struct thermal_trip *trip = &td->trip; @@ -806,12 +820,25 @@ static int tze_seq_show(struct seq_file *s, void *v) else type = "hot"; - seq_printf(s, "| %*d | %*s | %*d | %*d | %*lld | %*d | %*d | %*d |\n", + if (trip_stats->timestamp != KTIME_MAX) { + /* Mitigation in progress. */ + ktime_t delta = ktime_sub(ktime_get(), + trip_stats->timestamp); + + delta = ktime_add(delta, trip_stats->duration); + duration_ms = ktime_to_ms(delta); + c = '>'; + } else { + duration_ms = ktime_to_ms(trip_stats->duration); + c = ' '; + } + + seq_printf(s, "| %*d | %*s | %*d | %*d | %c%*lld | %*d | %*d | %*d |\n", 4 , trip_id, 8, type, 9, trip->temperature, 9, trip->hysteresis, - 10, ktime_to_ms(trip_stats->duration), + c, 10, duration_ms, 9, trip_stats->avg, 9, trip_stats->min, 9, trip_stats->max); From 1502718a66c573cb2df811da67d8de7bf5c4657e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 25 Apr 2024 16:07:38 +0200 Subject: [PATCH 65/72] thermal: trip: Add missing empty code line Add missing empty line of code to thermal_zone_trip_id(). Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba --- drivers/thermal/thermal_trip.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/thermal/thermal_trip.c b/drivers/thermal/thermal_trip.c index 7cf43b697272..21ece8399997 100644 --- a/drivers/thermal/thermal_trip.c +++ b/drivers/thermal/thermal_trip.c @@ -138,6 +138,7 @@ int thermal_zone_trip_id(const struct thermal_zone_device *tz, */ return trip_to_trip_desc(trip) - tz->trips; } + void thermal_zone_trip_updated(struct thermal_zone_device *tz, const struct thermal_trip *trip) { From 202aa0d4bb532338cd27bcc64c60abc2987a2be7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 30 Apr 2024 17:45:55 +0200 Subject: [PATCH 66/72] thermal: core: Do not call handle_thermal_trip() if zone temperature is invalid Make __thermal_zone_device_update() bail out if update_temperature() fails to update the zone temperature because __thermal_zone_get_temp() has returned an error and the current zone temperature is THERMAL_TEMP_INVALID (user space receiving netlink thermal messages, thermal debug code and thermal governors may get confused otherwise). Fixes: 9ad18043fb35 ("thermal: core: Send trip crossing notifications at init time if needed") Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Tested-by: Lukasz Luba --- drivers/thermal/thermal_core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index f43237ef2cd0..8bffd9101a32 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -487,6 +487,9 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, update_temperature(tz); + if (tz->temperature == THERMAL_TEMP_INVALID) + return; + __thermal_zone_set_trips(tz); tz->notify_event = event; From 042a3d80f118821c9e0b4c25021e32e45efe9b3a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 30 Apr 2024 17:52:33 +0200 Subject: [PATCH 67/72] thermal: core: Move passive polling management to the core Passive polling is enabled by setting the 'passive' field in struct thermal_zone_device to a positive value so long as the 'passive_delay_jiffies' field is greater than zero. It causes the thermal core to actively check the thermal zone temperature periodically which in theory should be done after crossing a passive trip point on the way up in order to allow governors to react more rapidly to temperature changes and adjust mitigation more precisely. However, the 'passive' field in struct thermal_zone_device is currently managed by governors which is quite problematic. First of all, only two governors, Step-Wise and Power Allocator, update that field at all, so the other governors do not benefit from passive polling, although in principle they should. Moreover, if the zone governor is changed from, say, Step-Wise to Fair-Share after 'passive' has been incremented by the former, it is not going to be reset back to zero by the latter even if the zone temperature falls down below all passive trip points. For this reason, make handle_thermal_trip() increment 'passive' to enable passive polling for the given thermal zone whenever a passive trip point is crossed on the way up and decrement it whenever a passive trip point is crossed on the way down. Also remove the 'passive' field updates from governors and additionally clear it in thermal_zone_device_init() to prevent passive polling from being enabled after a system resume just beacuse it was enabled before suspending the system. Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Tested-by: Lukasz Luba --- drivers/thermal/gov_power_allocator.c | 12 +++++++----- drivers/thermal/gov_step_wise.c | 10 ---------- drivers/thermal/thermal_core.c | 14 +++++++++++--- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/thermal/gov_power_allocator.c b/drivers/thermal/gov_power_allocator.c index 7450ab77d5f0..45f04a25255a 100644 --- a/drivers/thermal/gov_power_allocator.c +++ b/drivers/thermal/gov_power_allocator.c @@ -66,6 +66,7 @@ struct power_actor { * struct power_allocator_params - parameters for the power allocator governor * @allocated_tzp: whether we have allocated tzp for this thermal zone and * it needs to be freed on unbind + * @update_cdevs: whether or not update cdevs on the next run * @err_integral: accumulated error in the PID controller. * @prev_err: error in the previous iteration of the PID controller. * Used to calculate the derivative term. @@ -84,6 +85,7 @@ struct power_actor { */ struct power_allocator_params { bool allocated_tzp; + bool update_cdevs; s64 err_integral; s32 prev_err; u32 sustainable_power; @@ -533,7 +535,7 @@ static void reset_pid_controller(struct power_allocator_params *params) params->prev_err = 0; } -static void allow_maximum_power(struct thermal_zone_device *tz, bool update) +static void allow_maximum_power(struct thermal_zone_device *tz) { struct power_allocator_params *params = tz->governor_data; struct thermal_cooling_device *cdev; @@ -555,7 +557,7 @@ static void allow_maximum_power(struct thermal_zone_device *tz, bool update) */ cdev->ops->get_requested_power(cdev, &req_power); - if (update) + if (params->update_cdevs) __thermal_cdev_update(cdev); mutex_unlock(&cdev->lock); @@ -752,13 +754,13 @@ static void power_allocator_manage(struct thermal_zone_device *tz) if (trip && tz->temperature < trip->temperature) { reset_pid_controller(params); - allow_maximum_power(tz, tz->passive); - tz->passive = 0; + allow_maximum_power(tz); + params->update_cdevs = false; return; } allocate_power(tz, params->trip_max->temperature); - tz->passive = 1; + params->update_cdevs = true; } static struct thermal_governor thermal_gov_power_allocator = { diff --git a/drivers/thermal/gov_step_wise.c b/drivers/thermal/gov_step_wise.c index 0196a1a02290..e0fdc497bfcc 100644 --- a/drivers/thermal/gov_step_wise.c +++ b/drivers/thermal/gov_step_wise.c @@ -93,16 +93,6 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, if (instance->initialized && old_target == instance->target) continue; - 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) - tz->passive--; - } - instance->initialized = true; mutex_lock(&instance->cdev->lock); diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 8bffd9101a32..11750a145d74 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -296,7 +296,7 @@ static void monitor_thermal_zone(struct thermal_zone_device *tz) { if (tz->mode != THERMAL_DEVICE_ENABLED) thermal_zone_device_set_polling(tz, 0); - else if (tz->passive) + else if (tz->passive > 0) thermal_zone_device_set_polling(tz, tz->passive_delay_jiffies); else if (tz->polling_delay_jiffies) thermal_zone_device_set_polling(tz, tz->polling_delay_jiffies); @@ -389,6 +389,11 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, if (tz->temperature < trip->temperature - trip->hysteresis) { list_add(&td->notify_list_node, way_down_list); td->notify_temp = trip->temperature - trip->hysteresis; + + if (trip->type == THERMAL_TRIP_PASSIVE) { + tz->passive--; + WARN_ON(tz->passive < 0); + } } else { td->threshold -= trip->hysteresis; } @@ -402,8 +407,10 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, td->notify_temp = trip->temperature; td->threshold -= trip->hysteresis; - if (trip->type == THERMAL_TRIP_CRITICAL || - trip->type == THERMAL_TRIP_HOT) + if (trip->type == THERMAL_TRIP_PASSIVE) + tz->passive++; + else if (trip->type == THERMAL_TRIP_CRITICAL || + trip->type == THERMAL_TRIP_HOT) handle_critical_trips(tz, trip); } } @@ -444,6 +451,7 @@ static void thermal_zone_device_init(struct thermal_zone_device *tz) INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check); tz->temperature = THERMAL_TEMP_INVALID; + tz->passive = 0; tz->prev_low_trip = -INT_MAX; tz->prev_high_trip = INT_MAX; list_for_each_entry(pos, &tz->thermal_instances, tz_node) From 48d722fd397cb71a4685fecb60b2d3bc1421c74b Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 30 Apr 2024 15:58:25 -0700 Subject: [PATCH 68/72] thermal: intel: Add missing module description Fix warnings displayed by "make W=1" build: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/thermal/intel/intel_soc_dts_iosf.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/thermal/intel/int340x_thermal/processor_thermal_wt_req.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/thermal/intel/int340x_thermal/processor_thermal_wt_hint.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/thermal/intel/int340x_thermal/processor_thermal_power_floor Reported-by: Andy Shevchenko Signed-off-by: Srinivas Pandruvada Reviewed-by: Andy Shevchenko Signed-off-by: Rafael J. Wysocki --- drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c | 1 + .../intel/int340x_thermal/processor_thermal_power_floor.c | 1 + drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c | 1 + drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c | 1 + .../thermal/intel/int340x_thermal/processor_thermal_wt_hint.c | 1 + drivers/thermal/intel/int340x_thermal/processor_thermal_wt_req.c | 1 + drivers/thermal/intel/intel_soc_dts_iosf.c | 1 + 7 files changed, 7 insertions(+) diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c index 4d3bd32ff9ea..af2ec0beb7a1 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c @@ -156,3 +156,4 @@ int processor_thermal_mbox_interrupt_config(struct pci_dev *pdev, bool enable, EXPORT_SYMBOL_NS_GPL(processor_thermal_mbox_interrupt_config, INT340X_THERMAL); MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Processor Thermal Mail Box Interface"); diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_power_floor.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_power_floor.c index a1a108407f0f..d6b787ca2741 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_power_floor.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_power_floor.c @@ -124,3 +124,4 @@ EXPORT_SYMBOL_NS_GPL(proc_thermal_power_floor_intr_callback, INT340X_THERMAL); MODULE_IMPORT_NS(INT340X_THERMAL); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Processor Thermal power floor notification Interface"); diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c index e964a9375722..e9aa9e23aab9 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c @@ -133,3 +133,4 @@ void proc_thermal_rapl_remove(void) EXPORT_SYMBOL_GPL(proc_thermal_rapl_remove); MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("RAPL interface using MMIO"); diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c index 546b70434004..e56db75a94fb 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c @@ -384,3 +384,4 @@ void proc_thermal_rfim_remove(struct pci_dev *pdev) EXPORT_SYMBOL_GPL(proc_thermal_rfim_remove); MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Processor Thermal RFIM Interface"); diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_hint.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_hint.c index 9d5e4c169d1b..e947d84f4977 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_hint.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_hint.c @@ -253,3 +253,4 @@ EXPORT_SYMBOL_NS_GPL(proc_thermal_wt_hint_remove, INT340X_THERMAL); MODULE_IMPORT_NS(INT340X_THERMAL); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Processor Thermal Work Load type hint Interface"); diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_req.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_req.c index 711c4f761c9a..f298e7442662 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_req.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_req.c @@ -134,3 +134,4 @@ EXPORT_SYMBOL_GPL(proc_thermal_wt_req_remove); MODULE_IMPORT_NS(INT340X_THERMAL); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Processor Thermal Work Load type request Interface"); diff --git a/drivers/thermal/intel/intel_soc_dts_iosf.c b/drivers/thermal/intel/intel_soc_dts_iosf.c index 2ab943b66f7a..7adf942665d4 100644 --- a/drivers/thermal/intel/intel_soc_dts_iosf.c +++ b/drivers/thermal/intel/intel_soc_dts_iosf.c @@ -387,3 +387,4 @@ EXPORT_SYMBOL_GPL(intel_soc_dts_iosf_exit); MODULE_IMPORT_NS(INTEL_TCC); MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("SoC DTS driver using side band interface"); From 564a88eb7af885d4f5d193d6485cb0e0ac3ca7a5 Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Tue, 7 May 2024 20:43:49 -0700 Subject: [PATCH 69/72] thermal: intel: hfi: Rename HFI_UPDATE_INTERVAL The name of the constant HFI_UPDATE_INTERVAL is misleading. It is not a periodic interval at which HFI updates are processed. It is the delay in the processing of an HFI update after the arrival of an HFI interrupt. Acked-by: Zhang Rui Signed-off-by: Ricardo Neri Signed-off-by: Rafael J. Wysocki --- drivers/thermal/intel/intel_hfi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/thermal/intel/intel_hfi.c b/drivers/thermal/intel/intel_hfi.c index fbc7f0cd83d7..e2b82d71ab6b 100644 --- a/drivers/thermal/intel/intel_hfi.c +++ b/drivers/thermal/intel/intel_hfi.c @@ -166,7 +166,7 @@ static struct hfi_features hfi_features; static DEFINE_MUTEX(hfi_instance_lock); static struct workqueue_struct *hfi_updates_wq; -#define HFI_UPDATE_INTERVAL HZ +#define HFI_UPDATE_DELAY HZ #define HFI_MAX_THERM_NOTIFY_COUNT 16 static void get_hfi_caps(struct hfi_instance *hfi_instance, @@ -322,7 +322,7 @@ void intel_hfi_process_event(__u64 pkg_therm_status_msr_val) raw_spin_unlock(&hfi_instance->event_lock); queue_delayed_work(hfi_updates_wq, &hfi_instance->update_work, - HFI_UPDATE_INTERVAL); + HFI_UPDATE_DELAY); } static void init_hfi_cpu_index(struct hfi_cpu_info *info) From ba1a587ed66faf6482748b20d5484dcd97589e79 Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Tue, 7 May 2024 20:43:50 -0700 Subject: [PATCH 70/72] thermal: intel: hfi: Shorten the thermal netlink event delay to 100ms The delay between an HFI interrupt and its corresponding thermal netlink event has so far been hard-coded to CONFIG_HZ jiffies (1 second). This delay is too long for hardware that generates updates every tens of milliseconds. The HFI driver uses a delayed workqueue to send thermal netlink events. No subsequent events will be sent if there is pending work. As a result, much of the information of consecutive hardware updates will be lost if the workqueue delay is too long. User space entities may act on obsolete data. If the delay is too short, multiple events may overwhelm listeners. Set the delay to 100ms to strike a balance between too many and too few events. Use milliseconds instead of jiffies to improve readability. Signed-off-by: Ricardo Neri Acked-by: Zhang Rui Signed-off-by: Rafael J. Wysocki --- drivers/thermal/intel/intel_hfi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/thermal/intel/intel_hfi.c b/drivers/thermal/intel/intel_hfi.c index e2b82d71ab6b..d82b8788b0f8 100644 --- a/drivers/thermal/intel/intel_hfi.c +++ b/drivers/thermal/intel/intel_hfi.c @@ -166,7 +166,7 @@ static struct hfi_features hfi_features; static DEFINE_MUTEX(hfi_instance_lock); static struct workqueue_struct *hfi_updates_wq; -#define HFI_UPDATE_DELAY HZ +#define HFI_UPDATE_DELAY_MS 100 #define HFI_MAX_THERM_NOTIFY_COUNT 16 static void get_hfi_caps(struct hfi_instance *hfi_instance, @@ -322,7 +322,7 @@ void intel_hfi_process_event(__u64 pkg_therm_status_msr_val) raw_spin_unlock(&hfi_instance->event_lock); queue_delayed_work(hfi_updates_wq, &hfi_instance->update_work, - HFI_UPDATE_DELAY); + msecs_to_jiffies(HFI_UPDATE_DELAY_MS)); } static void init_hfi_cpu_index(struct hfi_cpu_info *info) From 07c6f3a7ff57e9526de96b54d7615659ffd06f8f Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Tue, 7 May 2024 20:43:51 -0700 Subject: [PATCH 71/72] thermal: intel: hfi: Rename HFI_MAX_THERM_NOTIFY_COUNT When processing a hardware update, HFI generates as many thermal netlink events as needed to relay all the updated CPU capabilities to user space. The constant HFI_MAX_THERM_NOTIFY_COUNT is the number of CPU capabilities updated per each of those events. Give this constant a more descriptive name. Signed-off-by: Ricardo Neri Acked-by: Zhang Rui Signed-off-by: Rafael J. Wysocki --- drivers/thermal/intel/intel_hfi.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/thermal/intel/intel_hfi.c b/drivers/thermal/intel/intel_hfi.c index d82b8788b0f8..c6658f8c5cca 100644 --- a/drivers/thermal/intel/intel_hfi.c +++ b/drivers/thermal/intel/intel_hfi.c @@ -167,7 +167,7 @@ static DEFINE_MUTEX(hfi_instance_lock); static struct workqueue_struct *hfi_updates_wq; #define HFI_UPDATE_DELAY_MS 100 -#define HFI_MAX_THERM_NOTIFY_COUNT 16 +#define HFI_THERMNL_CAPS_PER_EVENT 16 static void get_hfi_caps(struct hfi_instance *hfi_instance, struct thermal_genl_cpu_caps *cpu_caps) @@ -218,14 +218,14 @@ static void update_capabilities(struct hfi_instance *hfi_instance) get_hfi_caps(hfi_instance, cpu_caps); - if (cpu_count < HFI_MAX_THERM_NOTIFY_COUNT) + if (cpu_count < HFI_THERMNL_CAPS_PER_EVENT) goto last_cmd; - /* Process complete chunks of HFI_MAX_THERM_NOTIFY_COUNT capabilities. */ + /* Process complete chunks of HFI_THERMNL_CAPS_PER_EVENT capabilities. */ for (i = 0; - (i + HFI_MAX_THERM_NOTIFY_COUNT) <= cpu_count; - i += HFI_MAX_THERM_NOTIFY_COUNT) - thermal_genl_cpu_capability_event(HFI_MAX_THERM_NOTIFY_COUNT, + (i + HFI_THERMNL_CAPS_PER_EVENT) <= cpu_count; + i += HFI_THERMNL_CAPS_PER_EVENT) + thermal_genl_cpu_capability_event(HFI_THERMNL_CAPS_PER_EVENT, &cpu_caps[i]); cpu_count = cpu_count - i; From 608fa8523563d8a2ee0dd832311f034d6326e2af Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Tue, 7 May 2024 20:43:52 -0700 Subject: [PATCH 72/72] thermal: intel: hfi: Increase the number of CPU capabilities per netlink event The number of updated CPU capabilities per netlink event is hard-coded to 16. On systems with more than 16 CPUs (a common case), it takes more than one thermal netlink event to relay all the new capabilities after an HFI interrupt. This adds unnecessary overhead to both the kernel and user space entities. Increase the number of CPU capabilities updated per event to 64. Any system with 64 CPUs or less can now update all the capabilities in a single thermal netlink event. Signed-off-by: Ricardo Neri Acked-by: Zhang Rui Signed-off-by: Rafael J. Wysocki --- drivers/thermal/intel/intel_hfi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/intel/intel_hfi.c b/drivers/thermal/intel/intel_hfi.c index c6658f8c5cca..a180a98bb9f1 100644 --- a/drivers/thermal/intel/intel_hfi.c +++ b/drivers/thermal/intel/intel_hfi.c @@ -167,7 +167,7 @@ static DEFINE_MUTEX(hfi_instance_lock); static struct workqueue_struct *hfi_updates_wq; #define HFI_UPDATE_DELAY_MS 100 -#define HFI_THERMNL_CAPS_PER_EVENT 16 +#define HFI_THERMNL_CAPS_PER_EVENT 64 static void get_hfi_caps(struct hfi_instance *hfi_instance, struct thermal_genl_cpu_caps *cpu_caps)