mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 05:02:12 +00:00
Thermal control updates for 6.2-rc1
- Fix race conditions related to thermal device operations that are not protected against thermal device removal (Guenter Roeck). - Fix error code in __thermal_cooling_device_register() (Dan Carpenter). - Validate new cooling device state (coming from user space) in cur_state_store() and reuse the max_state value from cooling device structure in the sysfs interface (Viresh Kumar). - Fix some possible name leaks in error paths in the thermal control core code (Yang Yingliang). - Detect TCC lock bit set in the intel_tcc_cooling driver and make it refuse to update the TCC offset in that case (Zhang Rui). - Add TCC cooling support for RaptorLake-S (Zhang Rui). - Prevent accidental clearing of HFI status by one of the other drivers using the same status register (Srinivas Pandruvada). - Protect clearing of thermal status bits in Intel thermal control drivers (Srinivas Pandruvada). - Allow the HFI thermal control driver to ACK an HFI event for the previously observed timestamp (Srinivas Pandruvada). - Remove a pointless die_id check from the HFI thermal driver and adjust the definition a data structure used by it (Ricardo Neri). -----BEGIN PGP SIGNATURE----- iQJGBAABCAAwFiEE4fcc61cGeeHD/fCwgsRv/nhiVHEFAmOXWUoSHHJqd0Byand5 c29ja2kubmV0AAoJEILEb/54YlRxAhoQAI3OFLiQ/boXpTUgPFWbio+E5RZbDGTa Tb38JEAhREpXoce3QiT1ZDqoLUGS9tMpyfIBu8QkUXtgdyHkxFJc/AqwvFKUUmVN oS5GMn6s3ufQKTeiFoBd/hJ6zaGwcW4e/2QXzTZjq8oUaI0yca0HCc0QUYx6kWF6 HqSD2KtFRVw4xe6uqZmwTVQddyIqA5FWRaZvhGolDPmVpg8NRTYVbsOzMf/StnRu lXyGrgk1tNIkJnGlOKnqsV9xIsH5iS8SRAgZaUc9wK/Wzor486VzH4TdVn6oYdUn fGCA1W982VX+mUlvVZQpyjGj8bL1JylZZSAcn8cg5cyKnTP1OinM6j/TIKc1JHK3 ZFTvTxLuQbJD90frCKub/iIigcTX/GTk8iVRSuzmmzUiBkZ/3/15X6PVA15OEcQU zzHaiz0lir7LP0l2BhPDNbfTa1wJkiAtxVW7hcYUOtxL8S8vBldSEB9Kr0+e9A6i Lis8OOFS2nZYbGZxplX68JXp3Lo42pcF2QcdekU+iOp0eLziLc2WCB7Lj97TWJoR QAeyYf4eAsdKrW6vAiB4ygVxNDm4RPmesqlaNtlfhsFLUY7b5tc5vTinv6it5ktE CCNEi0N0NCIGifkIBnuthjToPsvhrTaYF9C1BPTi7p6mZp6VVacScA/qZ3rw2DG6 W7FV8CJt5m5/ =ZdzX -----END PGP SIGNATURE----- Merge tag 'thermal-6.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm Pull thermal control updates from Rafael Wysocki: "These include thermal core fixes to protect thermal device operations against thermal device removal, other thermal core fixes and updates of Intel thermal control drivers. Specifics: - Fix race conditions related to thermal device operations that are not protected against thermal device removal (Guenter Roeck) - Fix error code in __thermal_cooling_device_register() (Dan Carpenter) - Validate new cooling device state (coming from user space) in cur_state_store() and reuse the max_state value from cooling device structure in the sysfs interface (Viresh Kumar) - Fix some possible name leaks in error paths in the thermal control core code (Yang Yingliang) - Detect TCC lock bit set in the intel_tcc_cooling driver and make it refuse to update the TCC offset in that case (Zhang Rui) - Add TCC cooling support for RaptorLake-S (Zhang Rui) - Prevent accidental clearing of HFI status by one of the other drivers using the same status register (Srinivas Pandruvada) - Protect clearing of thermal status bits in Intel thermal control drivers (Srinivas Pandruvada) - Allow the HFI thermal control driver to ACK an HFI event for the previously observed timestamp (Srinivas Pandruvada) - Remove a pointless die_id check from the HFI thermal driver and adjust the definition a data structure used by it (Ricardo Neri)" * tag 'thermal-6.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: thermal: intel: hfi: Remove a pointless die_id check thermal: core: fix some possible name leaks in error paths thermal: intel: hfi: ACK HFI for the same timestamp thermal: intel: Protect clearing of thermal status bits thermal: intel: Prevent accidental clearing of HFI status thermal/core: Protect thermal device operations against thermal device removal thermal/core: Remove thermal_zone_set_trips() thermal/core: Protect sysfs accesses to thermal operations with thermal zone mutex thermal/core: Protect hwmon accesses to thermal operations with thermal zone mutex thermal/core: Introduce locked version of thermal_zone_device_update thermal/core: Move parameter validation from __thermal_zone_get_temp to thermal_zone_get_temp thermal/core: Ensure that thermal device is registered in thermal_zone_get_temp thermal/core: Delete device under thermal device zone lock thermal/core: Destroy thermal zone device mutex in release function thermal: intel: intel_tcc_cooling: Add TCC cooling support for RaptorLake-S thermal: intel: intel_tcc_cooling: Detect TCC lock bit thermal: intel: hfi: Improve the type of hfi_features::nr_table_pages thermal/core: fix error code in __thermal_cooling_device_register() thermal: sysfs: Reuse cdev->max_state thermal: Validate new state in cur_state_store()
This commit is contained in:
commit
691806e977
@ -49,11 +49,7 @@ static int get_trip_level(struct thermal_zone_device *tz)
|
||||
static long get_target_state(struct thermal_zone_device *tz,
|
||||
struct thermal_cooling_device *cdev, int percentage, int level)
|
||||
{
|
||||
unsigned long max_state;
|
||||
|
||||
cdev->ops->get_max_state(cdev, &max_state);
|
||||
|
||||
return (long)(percentage * level * max_state) / (100 * tz->num_trips);
|
||||
return (long)(percentage * level * cdev->max_state) / (100 * tz->num_trips);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -42,9 +42,7 @@
|
||||
|
||||
#include "../thermal_core.h"
|
||||
#include "intel_hfi.h"
|
||||
|
||||
#define THERM_STATUS_CLEAR_PKG_MASK (BIT(1) | BIT(3) | BIT(5) | BIT(7) | \
|
||||
BIT(9) | BIT(11) | BIT(26))
|
||||
#include "thermal_interrupt.h"
|
||||
|
||||
/* Hardware Feedback Interface MSR configuration bits */
|
||||
#define HW_FEEDBACK_PTR_VALID_BIT BIT(0)
|
||||
@ -137,7 +135,7 @@ struct hfi_instance {
|
||||
* Parameters and supported features that are common to all HFI instances
|
||||
*/
|
||||
struct hfi_features {
|
||||
unsigned int nr_table_pages;
|
||||
size_t nr_table_pages;
|
||||
unsigned int cpu_stride;
|
||||
unsigned int hdr_size;
|
||||
};
|
||||
@ -252,7 +250,7 @@ void intel_hfi_process_event(__u64 pkg_therm_status_msr_val)
|
||||
struct hfi_instance *hfi_instance;
|
||||
int cpu = smp_processor_id();
|
||||
struct hfi_cpu_info *info;
|
||||
u64 new_timestamp;
|
||||
u64 new_timestamp, msr, hfi;
|
||||
|
||||
if (!pkg_therm_status_msr_val)
|
||||
return;
|
||||
@ -281,9 +279,21 @@ void intel_hfi_process_event(__u64 pkg_therm_status_msr_val)
|
||||
if (!raw_spin_trylock(&hfi_instance->event_lock))
|
||||
return;
|
||||
|
||||
/* Skip duplicated updates. */
|
||||
rdmsrl(MSR_IA32_PACKAGE_THERM_STATUS, msr);
|
||||
hfi = msr & PACKAGE_THERM_STATUS_HFI_UPDATED;
|
||||
if (!hfi) {
|
||||
raw_spin_unlock(&hfi_instance->event_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ack duplicate update. Since there is an active HFI
|
||||
* status from HW, it must be a new event, not a case
|
||||
* where a lagging CPU entered the locked region.
|
||||
*/
|
||||
new_timestamp = *(u64 *)hfi_instance->hw_table;
|
||||
if (*hfi_instance->timestamp == new_timestamp) {
|
||||
thermal_clear_package_intr_status(PACKAGE_LEVEL, PACKAGE_THERM_STATUS_HFI_UPDATED);
|
||||
raw_spin_unlock(&hfi_instance->event_lock);
|
||||
return;
|
||||
}
|
||||
@ -297,16 +307,14 @@ void intel_hfi_process_event(__u64 pkg_therm_status_msr_val)
|
||||
memcpy(hfi_instance->local_table, hfi_instance->hw_table,
|
||||
hfi_features.nr_table_pages << PAGE_SHIFT);
|
||||
|
||||
raw_spin_unlock(&hfi_instance->table_lock);
|
||||
raw_spin_unlock(&hfi_instance->event_lock);
|
||||
|
||||
/*
|
||||
* Let hardware know that we are done reading the HFI table and it is
|
||||
* free to update it again.
|
||||
*/
|
||||
pkg_therm_status_msr_val &= THERM_STATUS_CLEAR_PKG_MASK &
|
||||
~PACKAGE_THERM_STATUS_HFI_UPDATED;
|
||||
wrmsrl(MSR_IA32_PACKAGE_THERM_STATUS, pkg_therm_status_msr_val);
|
||||
thermal_clear_package_intr_status(PACKAGE_LEVEL, PACKAGE_THERM_STATUS_HFI_UPDATED);
|
||||
|
||||
raw_spin_unlock(&hfi_instance->table_lock);
|
||||
raw_spin_unlock(&hfi_instance->event_lock);
|
||||
|
||||
queue_delayed_work(hfi_updates_wq, &hfi_instance->update_work,
|
||||
HFI_UPDATE_INTERVAL);
|
||||
@ -371,7 +379,7 @@ void intel_hfi_online(unsigned int cpu)
|
||||
die_id = topology_logical_die_id(cpu);
|
||||
hfi_instance = info->hfi_instance;
|
||||
if (!hfi_instance) {
|
||||
if (die_id < 0 || die_id >= max_hfi_instances)
|
||||
if (die_id >= max_hfi_instances)
|
||||
return;
|
||||
|
||||
hfi_instance = &hfi_instances[die_id];
|
||||
|
@ -14,6 +14,7 @@
|
||||
#define TCC_SHIFT 24
|
||||
#define TCC_MASK (0x3fULL<<24)
|
||||
#define TCC_PROGRAMMABLE BIT(30)
|
||||
#define TCC_LOCKED BIT(31)
|
||||
|
||||
static struct thermal_cooling_device *tcc_cdev;
|
||||
|
||||
@ -84,6 +85,7 @@ static const struct x86_cpu_id tcc_ids[] __initconst = {
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_N, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_P, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_S, NULL),
|
||||
{}
|
||||
};
|
||||
|
||||
@ -108,6 +110,15 @@ static int __init tcc_cooling_init(void)
|
||||
if (!(val & TCC_PROGRAMMABLE))
|
||||
return -ENODEV;
|
||||
|
||||
err = rdmsrl_safe(MSR_IA32_TEMPERATURE_TARGET, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (val & TCC_LOCKED) {
|
||||
pr_info("TCC Offset locked\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
pr_info("Programmable TCC Offset detected\n");
|
||||
|
||||
tcc_cdev =
|
||||
|
@ -190,32 +190,33 @@ static const struct attribute_group thermal_attr_group = {
|
||||
};
|
||||
#endif /* CONFIG_SYSFS */
|
||||
|
||||
#define CORE_LEVEL 0
|
||||
#define PACKAGE_LEVEL 1
|
||||
|
||||
#define THERM_THROT_POLL_INTERVAL HZ
|
||||
#define THERM_STATUS_PROCHOT_LOG BIT(1)
|
||||
|
||||
#define THERM_STATUS_CLEAR_CORE_MASK (BIT(1) | BIT(3) | BIT(5) | BIT(7) | BIT(9) | BIT(11) | BIT(13) | BIT(15))
|
||||
#define THERM_STATUS_CLEAR_PKG_MASK (BIT(1) | BIT(3) | BIT(5) | BIT(7) | BIT(9) | BIT(11))
|
||||
#define THERM_STATUS_CLEAR_PKG_MASK (BIT(1) | BIT(3) | BIT(5) | BIT(7) | BIT(9) | BIT(11) | BIT(26))
|
||||
|
||||
static void clear_therm_status_log(int level)
|
||||
/*
|
||||
* Clear the bits in package thermal status register for bit = 1
|
||||
* in bitmask
|
||||
*/
|
||||
void thermal_clear_package_intr_status(int level, u64 bit_mask)
|
||||
{
|
||||
u64 msr_val;
|
||||
int msr;
|
||||
u64 mask, msr_val;
|
||||
|
||||
if (level == CORE_LEVEL) {
|
||||
msr = MSR_IA32_THERM_STATUS;
|
||||
mask = THERM_STATUS_CLEAR_CORE_MASK;
|
||||
msr_val = THERM_STATUS_CLEAR_CORE_MASK;
|
||||
} else {
|
||||
msr = MSR_IA32_PACKAGE_THERM_STATUS;
|
||||
mask = THERM_STATUS_CLEAR_PKG_MASK;
|
||||
msr_val = THERM_STATUS_CLEAR_PKG_MASK;
|
||||
}
|
||||
|
||||
rdmsrl(msr, msr_val);
|
||||
msr_val &= mask;
|
||||
wrmsrl(msr, msr_val & ~THERM_STATUS_PROCHOT_LOG);
|
||||
msr_val &= ~bit_mask;
|
||||
wrmsrl(msr, msr_val);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_clear_package_intr_status);
|
||||
|
||||
static void get_therm_status(int level, bool *proc_hot, u8 *temp)
|
||||
{
|
||||
@ -295,7 +296,7 @@ static void __maybe_unused throttle_active_work(struct work_struct *work)
|
||||
state->average = avg;
|
||||
|
||||
re_arm:
|
||||
clear_therm_status_log(state->level);
|
||||
thermal_clear_package_intr_status(state->level, THERM_STATUS_PROCHOT_LOG);
|
||||
schedule_delayed_work_on(this_cpu, &state->therm_work, THERM_THROT_POLL_INTERVAL);
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,9 @@
|
||||
#ifndef _INTEL_THERMAL_INTERRUPT_H
|
||||
#define _INTEL_THERMAL_INTERRUPT_H
|
||||
|
||||
#define CORE_LEVEL 0
|
||||
#define PACKAGE_LEVEL 1
|
||||
|
||||
/* Interrupt Handler for package thermal thresholds */
|
||||
extern int (*platform_thermal_package_notify)(__u64 msr_val);
|
||||
|
||||
@ -15,4 +18,7 @@ extern bool (*platform_thermal_package_rate_control)(void);
|
||||
/* Handle HWP interrupt */
|
||||
extern void notify_hwp_interrupt(void);
|
||||
|
||||
/* Common function to clear Package thermal status register */
|
||||
extern void thermal_clear_package_intr_status(int level, u64 bit_mask);
|
||||
|
||||
#endif /* _INTEL_THERMAL_INTERRUPT_H */
|
||||
|
@ -265,7 +265,6 @@ static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work)
|
||||
struct thermal_zone_device *tzone = NULL;
|
||||
int cpu = smp_processor_id();
|
||||
struct zone_device *zonedev;
|
||||
u64 msr_val, wr_val;
|
||||
|
||||
mutex_lock(&thermal_zone_mutex);
|
||||
raw_spin_lock_irq(&pkg_temp_lock);
|
||||
@ -279,12 +278,8 @@ static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work)
|
||||
}
|
||||
zonedev->work_scheduled = false;
|
||||
|
||||
rdmsrl(MSR_IA32_PACKAGE_THERM_STATUS, msr_val);
|
||||
wr_val = msr_val & ~(THERM_LOG_THRESHOLD0 | THERM_LOG_THRESHOLD1);
|
||||
if (wr_val != msr_val) {
|
||||
wrmsrl(MSR_IA32_PACKAGE_THERM_STATUS, wr_val);
|
||||
tzone = zonedev->tzone;
|
||||
}
|
||||
thermal_clear_package_intr_status(PACKAGE_LEVEL, THERM_LOG_THRESHOLD0 | THERM_LOG_THRESHOLD1);
|
||||
tzone = zonedev->tzone;
|
||||
|
||||
enable_pkg_thres_interrupt();
|
||||
raw_spin_unlock_irq(&pkg_temp_lock);
|
||||
|
@ -203,6 +203,9 @@ int thermal_zone_device_set_policy(struct thermal_zone_device *tz,
|
||||
mutex_lock(&thermal_governor_lock);
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
if (!device_is_registered(&tz->device))
|
||||
goto exit;
|
||||
|
||||
gov = __find_governor(strim(policy));
|
||||
if (!gov)
|
||||
goto exit;
|
||||
@ -403,6 +406,34 @@ static void thermal_zone_device_init(struct thermal_zone_device *tz)
|
||||
pos->initialized = false;
|
||||
}
|
||||
|
||||
void __thermal_zone_device_update(struct thermal_zone_device *tz,
|
||||
enum thermal_notify_event event)
|
||||
{
|
||||
int count;
|
||||
|
||||
if (atomic_read(&in_suspend))
|
||||
return;
|
||||
|
||||
if (WARN_ONCE(!tz->ops->get_temp,
|
||||
"'%s' must not be called without 'get_temp' ops set\n",
|
||||
__func__))
|
||||
return;
|
||||
|
||||
if (!thermal_zone_device_is_enabled(tz))
|
||||
return;
|
||||
|
||||
update_temperature(tz);
|
||||
|
||||
__thermal_zone_set_trips(tz);
|
||||
|
||||
tz->notify_event = event;
|
||||
|
||||
for (count = 0; count < tz->num_trips; count++)
|
||||
handle_thermal_trip(tz, count);
|
||||
|
||||
monitor_thermal_zone(tz);
|
||||
}
|
||||
|
||||
static int thermal_zone_device_set_mode(struct thermal_zone_device *tz,
|
||||
enum thermal_device_mode mode)
|
||||
{
|
||||
@ -417,15 +448,21 @@ static int thermal_zone_device_set_mode(struct thermal_zone_device *tz,
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!device_is_registered(&tz->device)) {
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (tz->ops->change_mode)
|
||||
ret = tz->ops->change_mode(tz, mode);
|
||||
|
||||
if (!ret)
|
||||
tz->mode = mode;
|
||||
|
||||
mutex_unlock(&tz->lock);
|
||||
__thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
|
||||
|
||||
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
if (mode == THERMAL_DEVICE_ENABLED)
|
||||
thermal_notify_tz_enable(tz->id);
|
||||
@ -457,31 +494,9 @@ int thermal_zone_device_is_enabled(struct thermal_zone_device *tz)
|
||||
void thermal_zone_device_update(struct thermal_zone_device *tz,
|
||||
enum thermal_notify_event event)
|
||||
{
|
||||
int count;
|
||||
|
||||
if (atomic_read(&in_suspend))
|
||||
return;
|
||||
|
||||
if (WARN_ONCE(!tz->ops->get_temp, "'%s' must not be called without "
|
||||
"'get_temp' ops set\n", __func__))
|
||||
return;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
if (!thermal_zone_device_is_enabled(tz))
|
||||
goto out;
|
||||
|
||||
update_temperature(tz);
|
||||
|
||||
__thermal_zone_set_trips(tz);
|
||||
|
||||
tz->notify_event = event;
|
||||
|
||||
for (count = 0; count < tz->num_trips; count++)
|
||||
handle_thermal_trip(tz, count);
|
||||
|
||||
monitor_thermal_zone(tz);
|
||||
out:
|
||||
if (device_is_registered(&tz->device))
|
||||
__thermal_zone_device_update(tz, event);
|
||||
mutex_unlock(&tz->lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_zone_device_update);
|
||||
@ -603,8 +618,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
|
||||
struct thermal_instance *pos;
|
||||
struct thermal_zone_device *pos1;
|
||||
struct thermal_cooling_device *pos2;
|
||||
unsigned long max_state;
|
||||
int result, ret;
|
||||
int result;
|
||||
|
||||
if (trip >= tz->num_trips || trip < 0)
|
||||
return -EINVAL;
|
||||
@ -621,15 +635,11 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
|
||||
if (tz != pos1 || cdev != pos2)
|
||||
return -EINVAL;
|
||||
|
||||
ret = cdev->ops->get_max_state(cdev, &max_state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* lower default 0, upper default max_state */
|
||||
lower = lower == THERMAL_NO_LIMIT ? 0 : lower;
|
||||
upper = upper == THERMAL_NO_LIMIT ? max_state : upper;
|
||||
upper = upper == THERMAL_NO_LIMIT ? cdev->max_state : upper;
|
||||
|
||||
if (lower > upper || upper > max_state)
|
||||
if (lower > upper || upper > cdev->max_state)
|
||||
return -EINVAL;
|
||||
|
||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||
@ -759,6 +769,7 @@ static void thermal_release(struct device *dev)
|
||||
sizeof("thermal_zone") - 1)) {
|
||||
tz = to_thermal_zone(dev);
|
||||
thermal_zone_destroy_device_groups(tz);
|
||||
mutex_destroy(&tz->lock);
|
||||
kfree(tz);
|
||||
} else if (!strncmp(dev_name(dev), "cooling_device",
|
||||
sizeof("cooling_device") - 1)) {
|
||||
@ -883,10 +894,6 @@ __thermal_cooling_device_register(struct device_node *np,
|
||||
cdev->id = ret;
|
||||
id = ret;
|
||||
|
||||
ret = dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
|
||||
if (ret)
|
||||
goto out_ida_remove;
|
||||
|
||||
cdev->type = kstrdup(type ? type : "", GFP_KERNEL);
|
||||
if (!cdev->type) {
|
||||
ret = -ENOMEM;
|
||||
@ -900,7 +907,17 @@ __thermal_cooling_device_register(struct device_node *np,
|
||||
cdev->updated = false;
|
||||
cdev->device.class = &thermal_class;
|
||||
cdev->devdata = devdata;
|
||||
|
||||
ret = cdev->ops->get_max_state(cdev, &cdev->max_state);
|
||||
if (ret)
|
||||
goto out_kfree_type;
|
||||
|
||||
thermal_cooling_device_setup_sysfs(cdev);
|
||||
ret = dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
|
||||
if (ret) {
|
||||
thermal_cooling_device_destroy_sysfs(cdev);
|
||||
goto out_kfree_type;
|
||||
}
|
||||
ret = device_register(&cdev->device);
|
||||
if (ret)
|
||||
goto out_kfree_type;
|
||||
@ -1234,10 +1251,6 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t
|
||||
tz->id = id;
|
||||
strscpy(tz->type, type, sizeof(tz->type));
|
||||
|
||||
result = dev_set_name(&tz->device, "thermal_zone%d", tz->id);
|
||||
if (result)
|
||||
goto remove_id;
|
||||
|
||||
if (!ops->critical)
|
||||
ops->critical = thermal_zone_device_critical;
|
||||
|
||||
@ -1260,6 +1273,11 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t
|
||||
/* A new thermal zone needs to be updated anyway. */
|
||||
atomic_set(&tz->need_update, 1);
|
||||
|
||||
result = dev_set_name(&tz->device, "thermal_zone%d", tz->id);
|
||||
if (result) {
|
||||
thermal_zone_destroy_device_groups(tz);
|
||||
goto remove_id;
|
||||
}
|
||||
result = device_register(&tz->device);
|
||||
if (result)
|
||||
goto release_device;
|
||||
@ -1390,8 +1408,12 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
|
||||
thermal_remove_hwmon_sysfs(tz);
|
||||
ida_free(&thermal_tz_ida, tz->id);
|
||||
ida_destroy(&tz->ida);
|
||||
mutex_destroy(&tz->lock);
|
||||
device_unregister(&tz->device);
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
device_del(&tz->device);
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
put_device(&tz->device);
|
||||
|
||||
thermal_notify_tz_delete(tz_id);
|
||||
}
|
||||
|
@ -109,9 +109,10 @@ int thermal_register_governor(struct thermal_governor *);
|
||||
void thermal_unregister_governor(struct thermal_governor *);
|
||||
int thermal_zone_device_set_policy(struct thermal_zone_device *, char *);
|
||||
int thermal_build_list_of_policies(char *buf);
|
||||
void __thermal_zone_device_update(struct thermal_zone_device *tz,
|
||||
enum thermal_notify_event event);
|
||||
|
||||
/* Helpers */
|
||||
void thermal_zone_set_trips(struct thermal_zone_device *tz);
|
||||
void __thermal_zone_set_trips(struct thermal_zone_device *tz);
|
||||
int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp);
|
||||
|
||||
|
@ -64,6 +64,20 @@ get_thermal_instance(struct thermal_zone_device *tz,
|
||||
}
|
||||
EXPORT_SYMBOL(get_thermal_instance);
|
||||
|
||||
/**
|
||||
* __thermal_zone_get_temp() - returns the temperature of a thermal zone
|
||||
* @tz: a valid pointer to a struct thermal_zone_device
|
||||
* @temp: a valid pointer to where to store the resulting temperature.
|
||||
*
|
||||
* When a valid thermal zone reference is passed, it will fetch its
|
||||
* temperature and fill @temp.
|
||||
*
|
||||
* Both tz and tz->ops must be valid pointers when calling this function,
|
||||
* and the tz->ops->get_temp callback must be provided.
|
||||
* The function must be called under tz->lock.
|
||||
*
|
||||
* Return: On success returns 0, an error code otherwise
|
||||
*/
|
||||
int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
@ -73,9 +87,6 @@ int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
|
||||
|
||||
lockdep_assert_held(&tz->lock);
|
||||
|
||||
if (!tz || IS_ERR(tz) || !tz->ops->get_temp)
|
||||
return -EINVAL;
|
||||
|
||||
ret = tz->ops->get_temp(tz, temp);
|
||||
|
||||
if (IS_ENABLED(CONFIG_THERMAL_EMULATION) && tz->emul_temperature) {
|
||||
@ -114,14 +125,43 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (IS_ERR_OR_NULL(tz))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
ret = __thermal_zone_get_temp(tz, temp);
|
||||
|
||||
if (!tz->ops->get_temp) {
|
||||
ret = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (device_is_registered(&tz->device))
|
||||
ret = __thermal_zone_get_temp(tz, temp);
|
||||
else
|
||||
ret = -ENODEV;
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_zone_get_temp);
|
||||
|
||||
/**
|
||||
* __thermal_zone_set_trips - Computes the next trip points for the driver
|
||||
* @tz: a pointer to a thermal zone device structure
|
||||
*
|
||||
* The function computes the next temperature boundaries by browsing
|
||||
* the trip points. The result is the closer low and high trip points
|
||||
* to the current temperature. These values are passed to the backend
|
||||
* driver to let it set its own notification mechanism (usually an
|
||||
* interrupt).
|
||||
*
|
||||
* This function must be called with tz->lock held. Both tz and tz->ops
|
||||
* must be valid pointers.
|
||||
*
|
||||
* It does not return a value
|
||||
*/
|
||||
void __thermal_zone_set_trips(struct thermal_zone_device *tz)
|
||||
{
|
||||
int low = -INT_MAX;
|
||||
@ -168,25 +208,6 @@ void __thermal_zone_set_trips(struct thermal_zone_device *tz)
|
||||
dev_err(&tz->device, "Failed to set trips: %d\n", ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* thermal_zone_set_trips - Computes the next trip points for the driver
|
||||
* @tz: a pointer to a thermal zone device structure
|
||||
*
|
||||
* The function computes the next temperature boundaries by browsing
|
||||
* the trip points. The result is the closer low and high trip points
|
||||
* to the current temperature. These values are passed to the backend
|
||||
* driver to let it set its own notification mechanism (usually an
|
||||
* interrupt).
|
||||
*
|
||||
* It does not return a value
|
||||
*/
|
||||
void thermal_zone_set_trips(struct thermal_zone_device *tz)
|
||||
{
|
||||
mutex_lock(&tz->lock);
|
||||
__thermal_zone_set_trips(tz);
|
||||
mutex_unlock(&tz->lock);
|
||||
}
|
||||
|
||||
static void thermal_cdev_set_cur_state(struct thermal_cooling_device *cdev,
|
||||
int target)
|
||||
{
|
||||
|
@ -77,7 +77,15 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
int temperature;
|
||||
int ret;
|
||||
|
||||
ret = tz->ops->get_crit_temp(tz, &temperature);
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
if (device_is_registered(&tz->device))
|
||||
ret = tz->ops->get_crit_temp(tz, &temperature);
|
||||
else
|
||||
ret = -ENODEV;
|
||||
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -92,7 +92,14 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr,
|
||||
if (sscanf(attr->attr.name, "trip_point_%d_type", &trip) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
result = tz->ops->get_trip_type(tz, trip, &type);
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
if (device_is_registered(dev))
|
||||
result = tz->ops->get_trip_type(tz, trip, &type);
|
||||
else
|
||||
result = -ENODEV;
|
||||
|
||||
mutex_unlock(&tz->lock);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
@ -128,10 +135,17 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr,
|
||||
if (kstrtoint(buf, 10, &temperature))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
if (!device_is_registered(dev)) {
|
||||
ret = -ENODEV;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (tz->ops->set_trip_temp) {
|
||||
ret = tz->ops->set_trip_temp(tz, trip, temperature);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (tz->trips)
|
||||
@ -140,16 +154,22 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr,
|
||||
if (tz->ops->get_trip_hyst) {
|
||||
ret = tz->ops->get_trip_hyst(tz, trip, &hyst);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ret = tz->ops->get_trip_type(tz, trip, &type);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto unlock;
|
||||
|
||||
thermal_notify_tz_trip_change(tz->id, trip, type, temperature, hyst);
|
||||
|
||||
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
|
||||
__thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
@ -168,7 +188,14 @@ trip_point_temp_show(struct device *dev, struct device_attribute *attr,
|
||||
if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
ret = tz->ops->get_trip_temp(tz, trip, &temperature);
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
if (device_is_registered(dev))
|
||||
ret = tz->ops->get_trip_temp(tz, trip, &temperature);
|
||||
else
|
||||
ret = -ENODEV;
|
||||
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -193,6 +220,13 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
|
||||
if (kstrtoint(buf, 10, &temperature))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
if (!device_is_registered(dev)) {
|
||||
ret = -ENODEV;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* We are not doing any check on the 'temperature' value
|
||||
* here. The driver implementing 'set_trip_hyst' has to
|
||||
@ -201,7 +235,10 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
|
||||
ret = tz->ops->set_trip_hyst(tz, trip, temperature);
|
||||
|
||||
if (!ret)
|
||||
thermal_zone_set_trips(tz);
|
||||
__thermal_zone_set_trips(tz);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
return ret ? ret : count;
|
||||
}
|
||||
@ -220,7 +257,14 @@ trip_point_hyst_show(struct device *dev, struct device_attribute *attr,
|
||||
if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
ret = tz->ops->get_trip_hyst(tz, trip, &temperature);
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
if (device_is_registered(dev))
|
||||
ret = tz->ops->get_trip_hyst(tz, trip, &temperature);
|
||||
else
|
||||
ret = -ENODEV;
|
||||
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
return ret ? ret : sprintf(buf, "%d\n", temperature);
|
||||
}
|
||||
@ -269,16 +313,23 @@ emul_temp_store(struct device *dev, struct device_attribute *attr,
|
||||
if (kstrtoint(buf, 10, &temperature))
|
||||
return -EINVAL;
|
||||
|
||||
if (!tz->ops->set_emul_temp) {
|
||||
mutex_lock(&tz->lock);
|
||||
tz->emul_temperature = temperature;
|
||||
mutex_unlock(&tz->lock);
|
||||
} else {
|
||||
ret = tz->ops->set_emul_temp(tz, temperature);
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
if (!device_is_registered(dev)) {
|
||||
ret = -ENODEV;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (!tz->ops->set_emul_temp)
|
||||
tz->emul_temperature = temperature;
|
||||
else
|
||||
ret = tz->ops->set_emul_temp(tz, temperature);
|
||||
|
||||
if (!ret)
|
||||
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
|
||||
__thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
return ret ? ret : count;
|
||||
}
|
||||
@ -589,13 +640,8 @@ static ssize_t max_state_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct thermal_cooling_device *cdev = to_cooling_device(dev);
|
||||
unsigned long state;
|
||||
int ret;
|
||||
|
||||
ret = cdev->ops->get_max_state(cdev, &state);
|
||||
if (ret)
|
||||
return ret;
|
||||
return sprintf(buf, "%ld\n", state);
|
||||
return sprintf(buf, "%ld\n", cdev->max_state);
|
||||
}
|
||||
|
||||
static ssize_t cur_state_show(struct device *dev, struct device_attribute *attr,
|
||||
@ -625,6 +671,10 @@ cur_state_store(struct device *dev, struct device_attribute *attr,
|
||||
if ((long)state < 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Requested state should be less than max_state + 1 */
|
||||
if (state > cdev->max_state)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&cdev->lock);
|
||||
|
||||
result = cdev->ops->set_cur_state(cdev, state);
|
||||
@ -662,7 +712,6 @@ struct cooling_dev_stats {
|
||||
spinlock_t lock;
|
||||
unsigned int total_trans;
|
||||
unsigned long state;
|
||||
unsigned long max_states;
|
||||
ktime_t last_time;
|
||||
ktime_t *time_in_state;
|
||||
unsigned int *trans_table;
|
||||
@ -692,7 +741,7 @@ void thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev,
|
||||
goto unlock;
|
||||
|
||||
update_time_in_state(stats);
|
||||
stats->trans_table[stats->state * stats->max_states + new_state]++;
|
||||
stats->trans_table[stats->state * (cdev->max_state + 1) + new_state]++;
|
||||
stats->state = new_state;
|
||||
stats->total_trans++;
|
||||
|
||||
@ -726,7 +775,7 @@ time_in_state_ms_show(struct device *dev, struct device_attribute *attr,
|
||||
spin_lock(&stats->lock);
|
||||
update_time_in_state(stats);
|
||||
|
||||
for (i = 0; i < stats->max_states; i++) {
|
||||
for (i = 0; i <= cdev->max_state; i++) {
|
||||
len += sprintf(buf + len, "state%u\t%llu\n", i,
|
||||
ktime_to_ms(stats->time_in_state[i]));
|
||||
}
|
||||
@ -741,7 +790,7 @@ reset_store(struct device *dev, struct device_attribute *attr, const char *buf,
|
||||
{
|
||||
struct thermal_cooling_device *cdev = to_cooling_device(dev);
|
||||
struct cooling_dev_stats *stats = cdev->stats;
|
||||
int i, states = stats->max_states;
|
||||
int i, states = cdev->max_state + 1;
|
||||
|
||||
spin_lock(&stats->lock);
|
||||
|
||||
@ -750,7 +799,7 @@ reset_store(struct device *dev, struct device_attribute *attr, const char *buf,
|
||||
memset(stats->trans_table, 0,
|
||||
states * states * sizeof(*stats->trans_table));
|
||||
|
||||
for (i = 0; i < stats->max_states; i++)
|
||||
for (i = 0; i < states; i++)
|
||||
stats->time_in_state[i] = ktime_set(0, 0);
|
||||
|
||||
spin_unlock(&stats->lock);
|
||||
@ -768,7 +817,7 @@ static ssize_t trans_table_show(struct device *dev,
|
||||
|
||||
len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n");
|
||||
len += snprintf(buf + len, PAGE_SIZE - len, " : ");
|
||||
for (i = 0; i < stats->max_states; i++) {
|
||||
for (i = 0; i <= cdev->max_state; i++) {
|
||||
if (len >= PAGE_SIZE)
|
||||
break;
|
||||
len += snprintf(buf + len, PAGE_SIZE - len, "state%2u ", i);
|
||||
@ -778,17 +827,17 @@ static ssize_t trans_table_show(struct device *dev,
|
||||
|
||||
len += snprintf(buf + len, PAGE_SIZE - len, "\n");
|
||||
|
||||
for (i = 0; i < stats->max_states; i++) {
|
||||
for (i = 0; i <= cdev->max_state; i++) {
|
||||
if (len >= PAGE_SIZE)
|
||||
break;
|
||||
|
||||
len += snprintf(buf + len, PAGE_SIZE - len, "state%2u:", i);
|
||||
|
||||
for (j = 0; j < stats->max_states; j++) {
|
||||
for (j = 0; j <= cdev->max_state; j++) {
|
||||
if (len >= PAGE_SIZE)
|
||||
break;
|
||||
len += snprintf(buf + len, PAGE_SIZE - len, "%8u ",
|
||||
stats->trans_table[i * stats->max_states + j]);
|
||||
stats->trans_table[i * (cdev->max_state + 1) + j]);
|
||||
}
|
||||
if (len >= PAGE_SIZE)
|
||||
break;
|
||||
@ -824,14 +873,10 @@ static void cooling_device_stats_setup(struct thermal_cooling_device *cdev)
|
||||
{
|
||||
const struct attribute_group *stats_attr_group = NULL;
|
||||
struct cooling_dev_stats *stats;
|
||||
unsigned long states;
|
||||
/* Total number of states is highest state + 1 */
|
||||
unsigned long states = cdev->max_state + 1;
|
||||
int var;
|
||||
|
||||
if (cdev->ops->get_max_state(cdev, &states))
|
||||
goto out;
|
||||
|
||||
states++; /* Total number of states is highest state + 1 */
|
||||
|
||||
var = sizeof(*stats);
|
||||
var += sizeof(*stats->time_in_state) * states;
|
||||
var += sizeof(*stats->trans_table) * states * states;
|
||||
@ -844,7 +889,6 @@ static void cooling_device_stats_setup(struct thermal_cooling_device *cdev)
|
||||
stats->trans_table = (unsigned int *)(stats->time_in_state + states);
|
||||
cdev->stats = stats;
|
||||
stats->last_time = ktime_get();
|
||||
stats->max_states = states;
|
||||
|
||||
spin_lock_init(&stats->lock);
|
||||
|
||||
|
@ -100,6 +100,7 @@ struct thermal_cooling_device_ops {
|
||||
struct thermal_cooling_device {
|
||||
int id;
|
||||
char *type;
|
||||
unsigned long max_state;
|
||||
struct device device;
|
||||
struct device_node *np;
|
||||
void *devdata;
|
||||
|
Loading…
Reference in New Issue
Block a user