Commit 691806e9 authored by Linus Torvalds's avatar Linus Torvalds

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()
parents 456ed864 75b15aa0
...@@ -49,11 +49,7 @@ static int get_trip_level(struct thermal_zone_device *tz) ...@@ -49,11 +49,7 @@ static int get_trip_level(struct thermal_zone_device *tz)
static long get_target_state(struct thermal_zone_device *tz, static long get_target_state(struct thermal_zone_device *tz,
struct thermal_cooling_device *cdev, int percentage, int level) struct thermal_cooling_device *cdev, int percentage, int level)
{ {
unsigned long max_state; return (long)(percentage * level * cdev->max_state) / (100 * tz->num_trips);
cdev->ops->get_max_state(cdev, &max_state);
return (long)(percentage * level * max_state) / (100 * tz->num_trips);
} }
/** /**
......
...@@ -42,9 +42,7 @@ ...@@ -42,9 +42,7 @@
#include "../thermal_core.h" #include "../thermal_core.h"
#include "intel_hfi.h" #include "intel_hfi.h"
#include "thermal_interrupt.h"
#define THERM_STATUS_CLEAR_PKG_MASK (BIT(1) | BIT(3) | BIT(5) | BIT(7) | \
BIT(9) | BIT(11) | BIT(26))
/* Hardware Feedback Interface MSR configuration bits */ /* Hardware Feedback Interface MSR configuration bits */
#define HW_FEEDBACK_PTR_VALID_BIT BIT(0) #define HW_FEEDBACK_PTR_VALID_BIT BIT(0)
...@@ -137,7 +135,7 @@ struct hfi_instance { ...@@ -137,7 +135,7 @@ struct hfi_instance {
* Parameters and supported features that are common to all HFI instances * Parameters and supported features that are common to all HFI instances
*/ */
struct hfi_features { struct hfi_features {
unsigned int nr_table_pages; size_t nr_table_pages;
unsigned int cpu_stride; unsigned int cpu_stride;
unsigned int hdr_size; unsigned int hdr_size;
}; };
...@@ -252,7 +250,7 @@ void intel_hfi_process_event(__u64 pkg_therm_status_msr_val) ...@@ -252,7 +250,7 @@ void intel_hfi_process_event(__u64 pkg_therm_status_msr_val)
struct hfi_instance *hfi_instance; struct hfi_instance *hfi_instance;
int cpu = smp_processor_id(); int cpu = smp_processor_id();
struct hfi_cpu_info *info; struct hfi_cpu_info *info;
u64 new_timestamp; u64 new_timestamp, msr, hfi;
if (!pkg_therm_status_msr_val) if (!pkg_therm_status_msr_val)
return; return;
...@@ -281,9 +279,21 @@ void intel_hfi_process_event(__u64 pkg_therm_status_msr_val) ...@@ -281,9 +279,21 @@ void intel_hfi_process_event(__u64 pkg_therm_status_msr_val)
if (!raw_spin_trylock(&hfi_instance->event_lock)) if (!raw_spin_trylock(&hfi_instance->event_lock))
return; 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; new_timestamp = *(u64 *)hfi_instance->hw_table;
if (*hfi_instance->timestamp == new_timestamp) { 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); raw_spin_unlock(&hfi_instance->event_lock);
return; return;
} }
...@@ -297,16 +307,14 @@ void intel_hfi_process_event(__u64 pkg_therm_status_msr_val) ...@@ -297,16 +307,14 @@ void intel_hfi_process_event(__u64 pkg_therm_status_msr_val)
memcpy(hfi_instance->local_table, hfi_instance->hw_table, memcpy(hfi_instance->local_table, hfi_instance->hw_table,
hfi_features.nr_table_pages << PAGE_SHIFT); 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 * Let hardware know that we are done reading the HFI table and it is
* free to update it again. * free to update it again.
*/ */
pkg_therm_status_msr_val &= THERM_STATUS_CLEAR_PKG_MASK & thermal_clear_package_intr_status(PACKAGE_LEVEL, PACKAGE_THERM_STATUS_HFI_UPDATED);
~PACKAGE_THERM_STATUS_HFI_UPDATED;
wrmsrl(MSR_IA32_PACKAGE_THERM_STATUS, pkg_therm_status_msr_val); raw_spin_unlock(&hfi_instance->table_lock);
raw_spin_unlock(&hfi_instance->event_lock);
queue_delayed_work(hfi_updates_wq, &hfi_instance->update_work, queue_delayed_work(hfi_updates_wq, &hfi_instance->update_work,
HFI_UPDATE_INTERVAL); HFI_UPDATE_INTERVAL);
...@@ -371,7 +379,7 @@ void intel_hfi_online(unsigned int cpu) ...@@ -371,7 +379,7 @@ void intel_hfi_online(unsigned int cpu)
die_id = topology_logical_die_id(cpu); die_id = topology_logical_die_id(cpu);
hfi_instance = info->hfi_instance; hfi_instance = info->hfi_instance;
if (!hfi_instance) { if (!hfi_instance) {
if (die_id < 0 || die_id >= max_hfi_instances) if (die_id >= max_hfi_instances)
return; return;
hfi_instance = &hfi_instances[die_id]; hfi_instance = &hfi_instances[die_id];
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#define TCC_SHIFT 24 #define TCC_SHIFT 24
#define TCC_MASK (0x3fULL<<24) #define TCC_MASK (0x3fULL<<24)
#define TCC_PROGRAMMABLE BIT(30) #define TCC_PROGRAMMABLE BIT(30)
#define TCC_LOCKED BIT(31)
static struct thermal_cooling_device *tcc_cdev; static struct thermal_cooling_device *tcc_cdev;
...@@ -84,6 +85,7 @@ static const struct x86_cpu_id tcc_ids[] __initconst = { ...@@ -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(ALDERLAKE_N, NULL),
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE, NULL), X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE, NULL),
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_P, 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) ...@@ -108,6 +110,15 @@ static int __init tcc_cooling_init(void)
if (!(val & TCC_PROGRAMMABLE)) if (!(val & TCC_PROGRAMMABLE))
return -ENODEV; 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"); pr_info("Programmable TCC Offset detected\n");
tcc_cdev = tcc_cdev =
......
...@@ -190,32 +190,33 @@ static const struct attribute_group thermal_attr_group = { ...@@ -190,32 +190,33 @@ static const struct attribute_group thermal_attr_group = {
}; };
#endif /* CONFIG_SYSFS */ #endif /* CONFIG_SYSFS */
#define CORE_LEVEL 0
#define PACKAGE_LEVEL 1
#define THERM_THROT_POLL_INTERVAL HZ #define THERM_THROT_POLL_INTERVAL HZ
#define THERM_STATUS_PROCHOT_LOG BIT(1) #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_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; int msr;
u64 mask, msr_val;
if (level == CORE_LEVEL) { if (level == CORE_LEVEL) {
msr = MSR_IA32_THERM_STATUS; msr = MSR_IA32_THERM_STATUS;
mask = THERM_STATUS_CLEAR_CORE_MASK; msr_val = THERM_STATUS_CLEAR_CORE_MASK;
} else { } else {
msr = MSR_IA32_PACKAGE_THERM_STATUS; 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 &= ~bit_mask;
msr_val &= mask; wrmsrl(msr, msr_val);
wrmsrl(msr, msr_val & ~THERM_STATUS_PROCHOT_LOG);
} }
EXPORT_SYMBOL_GPL(thermal_clear_package_intr_status);
static void get_therm_status(int level, bool *proc_hot, u8 *temp) 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) ...@@ -295,7 +296,7 @@ static void __maybe_unused throttle_active_work(struct work_struct *work)
state->average = avg; state->average = avg;
re_arm: 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); schedule_delayed_work_on(this_cpu, &state->therm_work, THERM_THROT_POLL_INTERVAL);
} }
......
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
#ifndef _INTEL_THERMAL_INTERRUPT_H #ifndef _INTEL_THERMAL_INTERRUPT_H
#define _INTEL_THERMAL_INTERRUPT_H #define _INTEL_THERMAL_INTERRUPT_H
#define CORE_LEVEL 0
#define PACKAGE_LEVEL 1
/* Interrupt Handler for package thermal thresholds */ /* Interrupt Handler for package thermal thresholds */
extern int (*platform_thermal_package_notify)(__u64 msr_val); extern int (*platform_thermal_package_notify)(__u64 msr_val);
...@@ -15,4 +18,7 @@ extern bool (*platform_thermal_package_rate_control)(void); ...@@ -15,4 +18,7 @@ extern bool (*platform_thermal_package_rate_control)(void);
/* Handle HWP interrupt */ /* Handle HWP interrupt */
extern void notify_hwp_interrupt(void); 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 */ #endif /* _INTEL_THERMAL_INTERRUPT_H */
...@@ -265,7 +265,6 @@ static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work) ...@@ -265,7 +265,6 @@ static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work)
struct thermal_zone_device *tzone = NULL; struct thermal_zone_device *tzone = NULL;
int cpu = smp_processor_id(); int cpu = smp_processor_id();
struct zone_device *zonedev; struct zone_device *zonedev;
u64 msr_val, wr_val;
mutex_lock(&thermal_zone_mutex); mutex_lock(&thermal_zone_mutex);
raw_spin_lock_irq(&pkg_temp_lock); raw_spin_lock_irq(&pkg_temp_lock);
...@@ -279,12 +278,8 @@ static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work) ...@@ -279,12 +278,8 @@ static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work)
} }
zonedev->work_scheduled = false; zonedev->work_scheduled = false;
rdmsrl(MSR_IA32_PACKAGE_THERM_STATUS, msr_val); thermal_clear_package_intr_status(PACKAGE_LEVEL, THERM_LOG_THRESHOLD0 | THERM_LOG_THRESHOLD1);
wr_val = msr_val & ~(THERM_LOG_THRESHOLD0 | THERM_LOG_THRESHOLD1); tzone = zonedev->tzone;
if (wr_val != msr_val) {
wrmsrl(MSR_IA32_PACKAGE_THERM_STATUS, wr_val);
tzone = zonedev->tzone;
}
enable_pkg_thres_interrupt(); enable_pkg_thres_interrupt();
raw_spin_unlock_irq(&pkg_temp_lock); raw_spin_unlock_irq(&pkg_temp_lock);
......
...@@ -203,6 +203,9 @@ int thermal_zone_device_set_policy(struct thermal_zone_device *tz, ...@@ -203,6 +203,9 @@ int thermal_zone_device_set_policy(struct thermal_zone_device *tz,
mutex_lock(&thermal_governor_lock); mutex_lock(&thermal_governor_lock);
mutex_lock(&tz->lock); mutex_lock(&tz->lock);
if (!device_is_registered(&tz->device))
goto exit;
gov = __find_governor(strim(policy)); gov = __find_governor(strim(policy));
if (!gov) if (!gov)
goto exit; goto exit;
...@@ -403,6 +406,34 @@ static void thermal_zone_device_init(struct thermal_zone_device *tz) ...@@ -403,6 +406,34 @@ static void thermal_zone_device_init(struct thermal_zone_device *tz)
pos->initialized = false; 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, static int thermal_zone_device_set_mode(struct thermal_zone_device *tz,
enum thermal_device_mode mode) enum thermal_device_mode mode)
{ {
...@@ -417,15 +448,21 @@ static int thermal_zone_device_set_mode(struct thermal_zone_device *tz, ...@@ -417,15 +448,21 @@ static int thermal_zone_device_set_mode(struct thermal_zone_device *tz,
return ret; return ret;
} }
if (!device_is_registered(&tz->device)) {
mutex_unlock(&tz->lock);
return -ENODEV;
}
if (tz->ops->change_mode) if (tz->ops->change_mode)
ret = tz->ops->change_mode(tz, mode); ret = tz->ops->change_mode(tz, mode);
if (!ret) if (!ret)
tz->mode = mode; 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) if (mode == THERMAL_DEVICE_ENABLED)
thermal_notify_tz_enable(tz->id); thermal_notify_tz_enable(tz->id);
...@@ -457,31 +494,9 @@ int thermal_zone_device_is_enabled(struct thermal_zone_device *tz) ...@@ -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, void thermal_zone_device_update(struct thermal_zone_device *tz,
enum thermal_notify_event event) 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); mutex_lock(&tz->lock);
if (device_is_registered(&tz->device))
if (!thermal_zone_device_is_enabled(tz)) __thermal_zone_device_update(tz, event);
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:
mutex_unlock(&tz->lock); mutex_unlock(&tz->lock);
} }
EXPORT_SYMBOL_GPL(thermal_zone_device_update); EXPORT_SYMBOL_GPL(thermal_zone_device_update);
...@@ -603,8 +618,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, ...@@ -603,8 +618,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
struct thermal_instance *pos; struct thermal_instance *pos;
struct thermal_zone_device *pos1; struct thermal_zone_device *pos1;
struct thermal_cooling_device *pos2; struct thermal_cooling_device *pos2;
unsigned long max_state; int result;
int result, ret;
if (trip >= tz->num_trips || trip < 0) if (trip >= tz->num_trips || trip < 0)
return -EINVAL; return -EINVAL;
...@@ -621,15 +635,11 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, ...@@ -621,15 +635,11 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
if (tz != pos1 || cdev != pos2) if (tz != pos1 || cdev != pos2)
return -EINVAL; return -EINVAL;
ret = cdev->ops->get_max_state(cdev, &max_state);
if (ret)
return ret;
/* lower default 0, upper default max_state */ /* lower default 0, upper default max_state */
lower = lower == THERMAL_NO_LIMIT ? 0 : lower; 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; return -EINVAL;
dev = kzalloc(sizeof(*dev), GFP_KERNEL); dev = kzalloc(sizeof(*dev), GFP_KERNEL);
...@@ -759,6 +769,7 @@ static void thermal_release(struct device *dev) ...@@ -759,6 +769,7 @@ static void thermal_release(struct device *dev)
sizeof("thermal_zone") - 1)) { sizeof("thermal_zone") - 1)) {
tz = to_thermal_zone(dev); tz = to_thermal_zone(dev);
thermal_zone_destroy_device_groups(tz); thermal_zone_destroy_device_groups(tz);
mutex_destroy(&tz->lock);
kfree(tz); kfree(tz);
} else if (!strncmp(dev_name(dev), "cooling_device", } else if (!strncmp(dev_name(dev), "cooling_device",
sizeof("cooling_device") - 1)) { sizeof("cooling_device") - 1)) {
...@@ -883,10 +894,6 @@ __thermal_cooling_device_register(struct device_node *np, ...@@ -883,10 +894,6 @@ __thermal_cooling_device_register(struct device_node *np,
cdev->id = ret; cdev->id = ret;
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); cdev->type = kstrdup(type ? type : "", GFP_KERNEL);
if (!cdev->type) { if (!cdev->type) {
ret = -ENOMEM; ret = -ENOMEM;
...@@ -900,7 +907,17 @@ __thermal_cooling_device_register(struct device_node *np, ...@@ -900,7 +907,17 @@ __thermal_cooling_device_register(struct device_node *np,
cdev->updated = false; cdev->updated = false;
cdev->device.class = &thermal_class; cdev->device.class = &thermal_class;
cdev->devdata = devdata; 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); 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); ret = device_register(&cdev->device);
if (ret) if (ret)
goto out_kfree_type; goto out_kfree_type;
...@@ -1234,10 +1251,6 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t ...@@ -1234,10 +1251,6 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t
tz->id = id; tz->id = id;
strscpy(tz->type, type, sizeof(tz->type)); 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) if (!ops->critical)
ops->critical = thermal_zone_device_critical; ops->critical = thermal_zone_device_critical;
...@@ -1260,6 +1273,11 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t ...@@ -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. */ /* A new thermal zone needs to be updated anyway. */
atomic_set(&tz->need_update, 1); 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); result = device_register(&tz->device);
if (result) if (result)
goto release_device; goto release_device;
...@@ -1390,8 +1408,12 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) ...@@ -1390,8 +1408,12 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
thermal_remove_hwmon_sysfs(tz); thermal_remove_hwmon_sysfs(tz);
ida_free(&thermal_tz_ida, tz->id); ida_free(&thermal_tz_ida, tz->id);
ida_destroy(&tz->ida); 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); thermal_notify_tz_delete(tz_id);
} }
......
...@@ -109,9 +109,10 @@ int thermal_register_governor(struct thermal_governor *); ...@@ -109,9 +109,10 @@ int thermal_register_governor(struct thermal_governor *);
void thermal_unregister_governor(struct thermal_governor *); void thermal_unregister_governor(struct thermal_governor *);
int thermal_zone_device_set_policy(struct thermal_zone_device *, char *); int thermal_zone_device_set_policy(struct thermal_zone_device *, char *);
int thermal_build_list_of_policies(char *buf); int thermal_build_list_of_policies(char *buf);
void __thermal_zone_device_update(struct thermal_zone_device *tz,
enum thermal_notify_event event);
/* Helpers */ /* Helpers */
void thermal_zone_set_trips(struct thermal_zone_device *tz);
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); int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp);
......
...@@ -64,6 +64,20 @@ get_thermal_instance(struct thermal_zone_device *tz, ...@@ -64,6 +64,20 @@ get_thermal_instance(struct thermal_zone_device *tz,
} }
EXPORT_SYMBOL(get_thermal_instance); 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 __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
{ {
int ret = -EINVAL; int ret = -EINVAL;
...@@ -73,9 +87,6 @@ int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp) ...@@ -73,9 +87,6 @@ int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
lockdep_assert_held(&tz->lock); lockdep_assert_held(&tz->lock);
if (!tz || IS_ERR(tz) || !tz->ops->get_temp)
return -EINVAL;
ret = tz->ops->get_temp(tz, temp); ret = tz->ops->get_temp(tz, temp);
if (IS_ENABLED(CONFIG_THERMAL_EMULATION) && tz->emul_temperature) { 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) ...@@ -114,14 +125,43 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
{ {
int ret; int ret;
if (IS_ERR_OR_NULL(tz))
return -EINVAL;
mutex_lock(&tz->lock); 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); mutex_unlock(&tz->lock);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(thermal_zone_get_temp); 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) void __thermal_zone_set_trips(struct thermal_zone_device *tz)
{ {
int low = -INT_MAX; int low = -INT_MAX;
...@@ -168,25 +208,6 @@ void __thermal_zone_set_trips(struct thermal_zone_device *tz) ...@@ -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); 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, static void thermal_cdev_set_cur_state(struct thermal_cooling_device *cdev,
int target) int target)
{ {
......
...@@ -77,7 +77,15 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf) ...@@ -77,7 +77,15 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf)
int temperature; int temperature;
int ret; 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) if (ret)
return ret; return ret;
......
...@@ -92,7 +92,14 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr, ...@@ -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) if (sscanf(attr->attr.name, "trip_point_%d_type", &trip) != 1)
return -EINVAL; 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) if (result)
return result; return result;
...@@ -128,10 +135,17 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr, ...@@ -128,10 +135,17 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr,
if (kstrtoint(buf, 10, &temperature)) if (kstrtoint(buf, 10, &temperature))
return -EINVAL; return -EINVAL;
mutex_lock(&tz->lock);
if (!device_is_registered(dev)) {
ret = -ENODEV;
goto unlock;
}
if (tz->ops->set_trip_temp) { if (tz->ops->set_trip_temp) {
ret = tz->ops->set_trip_temp(tz, trip, temperature); ret = tz->ops->set_trip_temp(tz, trip, temperature);
if (ret) if (ret)
return ret; goto unlock;
} }
if (tz->trips) if (tz->trips)
...@@ -140,16 +154,22 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr, ...@@ -140,16 +154,22 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr,
if (tz->ops->get_trip_hyst) { if (tz->ops->get_trip_hyst) {
ret = tz->ops->get_trip_hyst(tz, trip, &hyst); ret = tz->ops->get_trip_hyst(tz, trip, &hyst);
if (ret) if (ret)
return ret; goto unlock;
} }
ret = tz->ops->get_trip_type(tz, trip, &type); ret = tz->ops->get_trip_type(tz, trip, &type);
if (ret) if (ret)
return ret; goto unlock;
thermal_notify_tz_trip_change(tz->id, trip, type, temperature, hyst); 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; return count;
} }
...@@ -168,7 +188,14 @@ trip_point_temp_show(struct device *dev, struct device_attribute *attr, ...@@ -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) if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip) != 1)
return -EINVAL; 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) if (ret)
return ret; return ret;
...@@ -193,6 +220,13 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr, ...@@ -193,6 +220,13 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
if (kstrtoint(buf, 10, &temperature)) if (kstrtoint(buf, 10, &temperature))
return -EINVAL; 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 * We are not doing any check on the 'temperature' value
* here. The driver implementing 'set_trip_hyst' has to * here. The driver implementing 'set_trip_hyst' has to
...@@ -201,7 +235,10 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr, ...@@ -201,7 +235,10 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
ret = tz->ops->set_trip_hyst(tz, trip, temperature); ret = tz->ops->set_trip_hyst(tz, trip, temperature);
if (!ret) if (!ret)
thermal_zone_set_trips(tz); __thermal_zone_set_trips(tz);
unlock:
mutex_unlock(&tz->lock);
return ret ? ret : count; return ret ? ret : count;
} }
...@@ -220,7 +257,14 @@ trip_point_hyst_show(struct device *dev, struct device_attribute *attr, ...@@ -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) if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip) != 1)
return -EINVAL; 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); return ret ? ret : sprintf(buf, "%d\n", temperature);
} }
...@@ -269,16 +313,23 @@ emul_temp_store(struct device *dev, struct device_attribute *attr, ...@@ -269,16 +313,23 @@ emul_temp_store(struct device *dev, struct device_attribute *attr,
if (kstrtoint(buf, 10, &temperature)) if (kstrtoint(buf, 10, &temperature))
return -EINVAL; return -EINVAL;
if (!tz->ops->set_emul_temp) { mutex_lock(&tz->lock);
mutex_lock(&tz->lock);
if (!device_is_registered(dev)) {
ret = -ENODEV;
goto unlock;
}
if (!tz->ops->set_emul_temp)
tz->emul_temperature = temperature; tz->emul_temperature = temperature;
mutex_unlock(&tz->lock); else
} else {
ret = tz->ops->set_emul_temp(tz, temperature); ret = tz->ops->set_emul_temp(tz, temperature);
}
if (!ret) 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; return ret ? ret : count;
} }
...@@ -589,13 +640,8 @@ static ssize_t max_state_show(struct device *dev, struct device_attribute *attr, ...@@ -589,13 +640,8 @@ static ssize_t max_state_show(struct device *dev, struct device_attribute *attr,
char *buf) char *buf)
{ {
struct thermal_cooling_device *cdev = to_cooling_device(dev); struct thermal_cooling_device *cdev = to_cooling_device(dev);
unsigned long state;
int ret;
ret = cdev->ops->get_max_state(cdev, &state); return sprintf(buf, "%ld\n", cdev->max_state);
if (ret)
return ret;
return sprintf(buf, "%ld\n", state);
} }
static ssize_t cur_state_show(struct device *dev, struct device_attribute *attr, 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, ...@@ -625,6 +671,10 @@ cur_state_store(struct device *dev, struct device_attribute *attr,
if ((long)state < 0) if ((long)state < 0)
return -EINVAL; return -EINVAL;
/* Requested state should be less than max_state + 1 */
if (state > cdev->max_state)
return -EINVAL;
mutex_lock(&cdev->lock); mutex_lock(&cdev->lock);
result = cdev->ops->set_cur_state(cdev, state); result = cdev->ops->set_cur_state(cdev, state);
...@@ -662,7 +712,6 @@ struct cooling_dev_stats { ...@@ -662,7 +712,6 @@ struct cooling_dev_stats {
spinlock_t lock; spinlock_t lock;
unsigned int total_trans; unsigned int total_trans;
unsigned long state; unsigned long state;
unsigned long max_states;
ktime_t last_time; ktime_t last_time;
ktime_t *time_in_state; ktime_t *time_in_state;
unsigned int *trans_table; unsigned int *trans_table;
...@@ -692,7 +741,7 @@ void thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev, ...@@ -692,7 +741,7 @@ void thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev,
goto unlock; goto unlock;
update_time_in_state(stats); 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->state = new_state;
stats->total_trans++; stats->total_trans++;
...@@ -726,7 +775,7 @@ time_in_state_ms_show(struct device *dev, struct device_attribute *attr, ...@@ -726,7 +775,7 @@ time_in_state_ms_show(struct device *dev, struct device_attribute *attr,
spin_lock(&stats->lock); spin_lock(&stats->lock);
update_time_in_state(stats); 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, len += sprintf(buf + len, "state%u\t%llu\n", i,
ktime_to_ms(stats->time_in_state[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, ...@@ -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 thermal_cooling_device *cdev = to_cooling_device(dev);
struct cooling_dev_stats *stats = cdev->stats; struct cooling_dev_stats *stats = cdev->stats;
int i, states = stats->max_states; int i, states = cdev->max_state + 1;
spin_lock(&stats->lock); spin_lock(&stats->lock);
...@@ -750,7 +799,7 @@ reset_store(struct device *dev, struct device_attribute *attr, const char *buf, ...@@ -750,7 +799,7 @@ reset_store(struct device *dev, struct device_attribute *attr, const char *buf,
memset(stats->trans_table, 0, memset(stats->trans_table, 0,
states * states * sizeof(*stats->trans_table)); 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); stats->time_in_state[i] = ktime_set(0, 0);
spin_unlock(&stats->lock); spin_unlock(&stats->lock);
...@@ -768,7 +817,7 @@ static ssize_t trans_table_show(struct device *dev, ...@@ -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, " From : To\n");
len += snprintf(buf + len, PAGE_SIZE - len, " : "); 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) if (len >= PAGE_SIZE)
break; break;
len += snprintf(buf + len, PAGE_SIZE - len, "state%2u ", i); len += snprintf(buf + len, PAGE_SIZE - len, "state%2u ", i);
...@@ -778,17 +827,17 @@ static ssize_t trans_table_show(struct device *dev, ...@@ -778,17 +827,17 @@ static ssize_t trans_table_show(struct device *dev,
len += snprintf(buf + len, PAGE_SIZE - len, "\n"); 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) if (len >= PAGE_SIZE)
break; break;
len += snprintf(buf + len, PAGE_SIZE - len, "state%2u:", i); 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) if (len >= PAGE_SIZE)
break; break;
len += snprintf(buf + len, PAGE_SIZE - len, "%8u ", 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) if (len >= PAGE_SIZE)
break; break;
...@@ -824,14 +873,10 @@ static void cooling_device_stats_setup(struct thermal_cooling_device *cdev) ...@@ -824,14 +873,10 @@ static void cooling_device_stats_setup(struct thermal_cooling_device *cdev)
{ {
const struct attribute_group *stats_attr_group = NULL; const struct attribute_group *stats_attr_group = NULL;
struct cooling_dev_stats *stats; 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; 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);
var += sizeof(*stats->time_in_state) * states; var += sizeof(*stats->time_in_state) * states;
var += sizeof(*stats->trans_table) * states * states; var += sizeof(*stats->trans_table) * states * states;
...@@ -844,7 +889,6 @@ static void cooling_device_stats_setup(struct thermal_cooling_device *cdev) ...@@ -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); stats->trans_table = (unsigned int *)(stats->time_in_state + states);
cdev->stats = stats; cdev->stats = stats;
stats->last_time = ktime_get(); stats->last_time = ktime_get();
stats->max_states = states;
spin_lock_init(&stats->lock); spin_lock_init(&stats->lock);
......
...@@ -100,6 +100,7 @@ struct thermal_cooling_device_ops { ...@@ -100,6 +100,7 @@ struct thermal_cooling_device_ops {
struct thermal_cooling_device { struct thermal_cooling_device {
int id; int id;
char *type; char *type;
unsigned long max_state;
struct device device; struct device device;
struct device_node *np; struct device_node *np;
void *devdata; void *devdata;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment