summaryrefslogtreecommitdiff
path: root/drivers/thermal
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/thermal')
-rw-r--r--drivers/thermal/amlogic_thermal.c10
-rw-r--r--drivers/thermal/armada_thermal.c9
-rw-r--r--drivers/thermal/cpufreq_cooling.c3
-rw-r--r--drivers/thermal/gov_bang_bang.c97
-rw-r--r--drivers/thermal/gov_fair_share.c82
-rw-r--r--drivers/thermal/gov_power_allocator.c45
-rw-r--r--drivers/thermal/gov_step_wise.c107
-rw-r--r--drivers/thermal/gov_user_space.c10
-rw-r--r--drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c4
-rw-r--r--drivers/thermal/intel/int340x_thermal/int3400_thermal.c10
-rw-r--r--drivers/thermal/intel/int340x_thermal/int3403_thermal.c1
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c1
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_power_floor.c1
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c1
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c1
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_wt_hint.c1
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_wt_req.c1
-rw-r--r--drivers/thermal/intel/intel_hfi.c113
-rw-r--r--drivers/thermal/intel/intel_soc_dts_iosf.c1
-rw-r--r--drivers/thermal/k3_bandgap.c1
-rw-r--r--drivers/thermal/loongson2_thermal.c117
-rw-r--r--drivers/thermal/mediatek/lvts_thermal.c443
-rw-r--r--drivers/thermal/qcom/lmh.c3
-rw-r--r--drivers/thermal/qcom/qcom-spmi-temp-alarm.c1
-rw-r--r--drivers/thermal/qcom/tsens-v2.c1
-rw-r--r--drivers/thermal/qcom/tsens.c33
-rw-r--r--drivers/thermal/qcom/tsens.h5
-rw-r--r--drivers/thermal/rcar_gen3_thermal.c165
-rw-r--r--drivers/thermal/thermal_core.c211
-rw-r--r--drivers/thermal/thermal_core.h123
-rw-r--r--drivers/thermal/thermal_debugfs.c181
-rw-r--r--drivers/thermal/thermal_debugfs.h8
-rw-r--r--drivers/thermal/thermal_helpers.c8
-rw-r--r--drivers/thermal/thermal_netlink.c68
-rw-r--r--drivers/thermal/thermal_netlink.h26
-rw-r--r--drivers/thermal/thermal_sysfs.c20
-rw-r--r--drivers/thermal/thermal_trace.h12
-rw-r--r--drivers/thermal/thermal_trace_ipa.h2
-rw-r--r--drivers/thermal/thermal_trip.c38
39 files changed, 1357 insertions, 607 deletions
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);
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, '-', '_');
}
/*
diff --git a/drivers/thermal/cpufreq_cooling.c b/drivers/thermal/cpufreq_cooling.c
index 9d1b1459700d..280071be30b1 100644
--- a/drivers/thermal/cpufreq_cooling.c
+++ b/drivers/thermal/cpufreq_cooling.c
@@ -477,7 +477,6 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
unsigned long state)
{
struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata;
- struct cpumask *cpus;
unsigned int frequency;
int ret;
@@ -494,8 +493,6 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
ret = freq_qos_update_request(&cpufreq_cdev->qos_req, frequency);
if (ret >= 0) {
cpufreq_cdev->cpufreq_state = state;
- cpus = cpufreq_cdev->policy->related_cpus;
- arch_update_thermal_pressure(cpus, frequency);
ret = 0;
}
diff --git a/drivers/thermal/gov_bang_bang.c b/drivers/thermal/gov_bang_bang.c
index c3b2943a2db8..acb52c9ee10f 100644
--- a/drivers/thermal/gov_bang_bang.c
+++ b/drivers/thermal/gov_bang_bang.c
@@ -13,60 +13,11 @@
#include "thermal_core.h"
-static int thermal_zone_trip_update(struct thermal_zone_device *tz,
- const struct thermal_trip *trip)
-{
- 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);
-
- 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);
- instance->target = 1;
- }
-
- /*
- * enable fan when temperature exceeds trip_temp and disable
- * the fan in case it falls below trip_temp minus hysteresis
- */
- if (instance->target == 0 && tz->temperature >= trip->temperature)
- instance->target = 1;
- else if (instance->target == 1 &&
- tz->temperature < trip->temperature - trip->hysteresis)
- instance->target = 0;
-
- dev_dbg(&instance->cdev->device, "target=%d\n",
- (int)instance->target);
-
- mutex_lock(&instance->cdev->lock);
- 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 +41,54 @@ 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;
+ 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);
-
- 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);
diff --git a/drivers/thermal/gov_fair_share.c b/drivers/thermal/gov_fair_share.c
index 4da25a0009d7..ce0ea571ed67 100644
--- a/drivers/thermal/gov_fair_share.c
+++ b/drivers/thermal/gov_fair_share.c
@@ -17,97 +17,111 @@
static int get_trip_level(struct thermal_zone_device *tz)
{
- const struct thermal_trip *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(tz, trip) {
- if (trip->temperature >= tz->temperature)
+ for_each_trip_desc(tz, td) {
+ 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;
}
-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
* @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.
*
* 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. 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);
+ 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,
- cur_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);
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);
diff --git a/drivers/thermal/gov_power_allocator.c b/drivers/thermal/gov_power_allocator.c
index e25e48d76aa7..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;
@@ -395,7 +397,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 +412,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 +473,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;
}
/**
@@ -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) {
@@ -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);
@@ -743,40 +745,29 @@ 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;
- bool update;
+ const struct thermal_trip *trip = params->trip_switch_on;
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;
+ allow_maximum_power(tz);
+ params->update_cdevs = false;
+ return;
}
- tz->passive = 1;
-
- return allocate_power(tz, params->trip_max->temperature);
+ allocate_power(tz, params->trip_max->temperature);
+ params->update_cdevs = true;
}
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);
diff --git a/drivers/thermal/gov_step_wise.c b/drivers/thermal/gov_step_wise.c
index 5436aa58d41e..65974fe8be0d 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,112 +39,116 @@ 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,
- const struct thermal_trip *trip)
+ 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->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) {
+ 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;
- 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 the target state for this thermal instance
+ * changes from THERMAL_NO_TARGET to something else,
+ * ensure that the zone temperature will be updated
+ * (assuming enabled passive cooling) until it becomes
+ * THERMAL_NO_TARGET again, or the cooling device may
+ * not be reset to its initial state.
+ */
+ 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--;
}
instance->initialized = true;
+
mutex_lock(&instance->cdev->lock);
instance->cdev->updated = false; /* cdev needs update */
mutex_unlock(&instance->cdev->lock);
}
}
-/**
- * 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, td->threshold);
+ }
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);
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);
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;
diff --git a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c
index 427d370648d5..fa96972266e4 100644
--- a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c
+++ b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c
@@ -73,14 +73,7 @@ struct odvp_attr {
struct device_attribute attr;
};
-static ssize_t data_vault_read(struct file *file, struct kobject *kobj,
- struct bin_attribute *attr, char *buf, loff_t off, size_t count)
-{
- memcpy(buf, attr->private + off, count);
- return count;
-}
-
-static BIN_ATTR_RO(data_vault, 0);
+static BIN_ATTR_SIMPLE_RO(data_vault);
static struct bin_attribute *data_attributes[] = {
&bin_attr_data_vault,
@@ -705,6 +698,7 @@ static const struct acpi_device_id int3400_thermal_match[] = {
{"INTC1040", 0},
{"INTC1041", 0},
{"INTC1042", 0},
+ {"INTC1068", 0},
{"INTC10A0", 0},
{}
};
diff --git a/drivers/thermal/intel/int340x_thermal/int3403_thermal.c b/drivers/thermal/intel/int340x_thermal/int3403_thermal.c
index 9b33fd3a66da..86901f9f54d8 100644
--- a/drivers/thermal/intel/int340x_thermal/int3403_thermal.c
+++ b/drivers/thermal/intel/int340x_thermal/int3403_thermal.c
@@ -284,6 +284,7 @@ static const struct acpi_device_id int3403_device_ids[] = {
{"INTC1043", 0},
{"INTC1046", 0},
{"INTC1062", 0},
+ {"INTC1069", 0},
{"INTC10A1", 0},
{"", 0},
};
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 @@ unlock:
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_hfi.c b/drivers/thermal/intel/intel_hfi.c
index 40d664a66cdc..a180a98bb9f1 100644
--- a/drivers/thermal/intel/intel_hfi.c
+++ b/drivers/thermal/intel/intel_hfi.c
@@ -159,14 +159,15 @@ 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;
static DEFINE_MUTEX(hfi_instance_lock);
static struct workqueue_struct *hfi_updates_wq;
-#define HFI_UPDATE_INTERVAL HZ
-#define HFI_MAX_THERM_NOTIFY_COUNT 16
+#define HFI_UPDATE_DELAY_MS 100
+#define HFI_THERMNL_CAPS_PER_EVENT 64
static void get_hfi_caps(struct hfi_instance *hfi_instance,
struct thermal_genl_cpu_caps *cpu_caps)
@@ -217,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;
@@ -321,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);
+ msecs_to_jiffies(HFI_UPDATE_DELAY_MS));
}
static void init_hfi_cpu_index(struct hfi_cpu_info *info)
@@ -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];
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");
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 */
diff --git a/drivers/thermal/loongson2_thermal.c b/drivers/thermal/loongson2_thermal.c
index 0f475fe46bc9..2d6b75b0539f 100644
--- a/drivers/thermal/loongson2_thermal.c
+++ b/drivers/thermal/loongson2_thermal.c
@@ -14,58 +14,81 @@
#include <linux/property.h>
#include <linux/thermal.h>
#include <linux/units.h>
+
#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_INT_EN (LOONGSON2_THSENS_INT_LO | \
+ LOONGSON2_THSENS_INT_HIGH)
+#define LOONGSON2_THSENS_OUT_MASK 0xFF
-#define LOONGSON2_THSENS_INT_LO BIT(0)
-#define LOONGSON2_THSENS_INT_HIGH BIT(1)
-#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 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;
};
+static void loongson2_set_ctrl_regs(struct loongson2_thermal_data *data,
+ int ctrl_data, bool low, bool enable)
+{
+ 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;
+
+ reg_ctrl = ctrl_data + HECTO;
+ reg_ctrl |= enable ? 0x100 : 0;
+ writew(reg_ctrl, data->ctrl_reg + ctrl_reg + reg_off);
+}
+
static int loongson2_thermal_set(struct loongson2_thermal_data *data,
- int low, int high, bool enable)
+ int low, int high, bool enable)
{
- u64 reg_ctrl = 0;
- int reg_off = data->chip_data->thermal_sensor_sel * 2;
+ /* Set low temperature threshold */
+ loongson2_set_ctrl_regs(data, clamp(-40, low, high), true, enable);
- low = clamp(-40, low, high);
- high = clamp(125, low, high);
+ /* Set high temperature threshold */
+ loongson2_set_ctrl_regs(data, clamp(125, low, high), false, enable);
- low += HECTO;
- high += HECTO;
+ return 0;
+}
- reg_ctrl = low;
- reg_ctrl |= enable ? 0x100 : 0;
- writew(reg_ctrl, data->regs + LOONGSON2_THSENS_CTRL_LOW_REG + reg_off);
+static int loongson2_2k1000_get_temp(struct thermal_zone_device *tz, int *temp)
+{
+ int val;
+ struct loongson2_thermal_data *data = thermal_zone_device_priv(tz);
- reg_ctrl = high;
- reg_ctrl |= enable ? 0x100 : 0;
- writew(reg_ctrl, data->regs + LOONGSON2_THSENS_CTRL_HI_REG + reg_off);
+ val = readl(data->ctrl_reg + LOONGSON2_THSENS_OUT_REG);
+ *temp = ((val & LOONGSON2_THSENS_OUT_MASK) - HECTO) * KILO;
return 0;
}
-static int loongson2_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
+static int loongson2_2k2000_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->temp_reg);
+ *temp = ((val & 0xffff) * 820 / 0x4000 - 311) * KILO;
return 0;
}
@@ -75,8 +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_LO | LOONGSON2_THSENS_INT_HIGH, 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);
@@ -90,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,
};
@@ -108,22 +130,30 @@ 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_LO | LOONGSON2_THSENS_INT_HIGH, 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);
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 +165,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");
@@ -146,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[] = {
@@ -153,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);
@@ -167,4 +207,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");
diff --git a/drivers/thermal/mediatek/lvts_thermal.c b/drivers/thermal/mediatek/lvts_thermal.c
index fd4bd650c77a..0bb3a495b56e 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
@@ -102,22 +100,34 @@ static int golden_temp_offset;
struct lvts_sensor_data {
int dt_id;
+ u8 cal_offsets[3];
};
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;
+ 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) \
+ for ((i) = 0; (i) < LVTS_SENSOR_MAX; (i)++) \
+ if (!((lvts_ctrl)->valid_sensor_mask & BIT(i))) \
+ continue; \
+ else
+
struct lvts_data {
const struct lvts_ctrl_data *lvts_ctrl;
int num_lvts_ctrl;
int temp_factor;
int temp_offset;
+ int gt_calib_bit_offset;
};
struct lvts_sensor {
@@ -135,7 +145,7 @@ struct lvts_ctrl {
const struct lvts_data *lvts_data;
u32 calibration[LVTS_SENSOR_MAX];
u32 hw_tshut_raw_temp;
- int num_lvts_sensor;
+ u8 valid_sensor_mask;
int mode;
void __iomem *base;
int low_thresh;
@@ -347,7 +357,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)
if (lvts_ctrl->sensors[i].high_thresh == lvts_ctrl->high_thresh
&& lvts_ctrl->sensors[i].low_thresh == lvts_ctrl->low_thresh)
return false;
@@ -551,6 +561,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),
@@ -567,7 +578,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;
@@ -607,7 +618,7 @@ 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;
+ lvts_ctrl->valid_sensor_mask = lvts_ctrl_data->valid_sensor_mask;
return 0;
}
@@ -668,18 +679,31 @@ 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,
- u8 *efuse_calibration)
+ u8 *efuse_calibration,
+ size_t calib_len)
{
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], 2);
+ lvts_for_each_valid_sensor(i, lvts_ctrl_data) {
+ 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) +
+ (efuse_calibration[sensor->cal_offsets[2]] << 16);
+ }
return 0;
}
@@ -734,16 +758,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, u32 *value, int temp_offset)
+static int lvts_golden_temp_init(struct device *dev, u8 *calib,
+ const struct lvts_data *lvts_data)
{
u32 gt;
- gt = (*value) >> 24;
+ /*
+ * The golden temp information is contained in the first 32-bit
+ * word of efuse data at a specific bit offset.
+ */
+ 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;
}
@@ -762,11 +791,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);
if (ret)
return ret;
@@ -786,7 +811,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;
@@ -801,7 +827,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;
@@ -1089,7 +1115,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) {
int dt_id = lvts_sensors[i].dt_id;
@@ -1246,6 +1272,8 @@ static int lvts_probe(struct platform_device *pdev)
return -ENOMEM;
lvts_data = of_device_get_match_data(dev);
+ if (!lvts_data)
+ return -ENODEV;
lvts_td->clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(lvts_td->clk))
@@ -1302,28 +1330,32 @@ 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,
+ VALID_SENSOR_MAP(1, 1, 1, 1),
.offset = 0x0,
- .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT7988,
},
{
- .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,
+ VALID_SENSOR_MAP(1, 1, 1, 1),
.offset = 0x100,
- .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT7988,
}
};
@@ -1359,164 +1391,301 @@ 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 } }
+ },
+ VALID_SENSOR_MAP(1, 1, 1, 1),
+ .offset = 0x0,
+ },
+ {
+ .lvts_sensor = {
+ { .dt_id = MT8186_BIG_CPU0,
+ .cal_offsets = { 22, 23, 16 } },
+ { .dt_id = MT8186_BIG_CPU1,
+ .cal_offsets = { 27, 20, 21 } }
+ },
+ VALID_SENSOR_MAP(1, 1, 0, 0),
+ .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 } }
+ },
+ VALID_SENSOR_MAP(1, 1, 1, 0),
+ .offset = 0x200,
+ }
+};
+
+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[] = {
{
- .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,
+ VALID_SENSOR_MAP(1, 1, 0, 0),
.offset = 0x0,
- .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8192,
.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,
+ VALID_SENSOR_MAP(1, 1, 0, 0),
.offset = 0x100,
- .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8192,
.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,
+ VALID_SENSOR_MAP(1, 1, 1, 1),
.offset = 0x200,
- .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8192,
.mode = LVTS_MSR_FILTERED_MODE,
}
};
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,
+ VALID_SENSOR_MAP(1, 1, 0, 0),
.offset = 0x0,
- .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8192,
},
{
- .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,
+ VALID_SENSOR_MAP(1, 1, 0, 0),
.offset = 0x100,
- .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8192,
},
{
- .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,
+ VALID_SENSOR_MAP(1, 1, 0, 0),
.offset = 0x200,
- .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8192,
},
{
- .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,
+ VALID_SENSOR_MAP(1, 1, 1, 0),
.offset = 0x300,
- .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8192,
}
};
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,
+ VALID_SENSOR_MAP(1, 1, 0, 0),
.offset = 0x0,
- .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195,
},
{
- .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,
+ VALID_SENSOR_MAP(1, 1, 0, 0),
.offset = 0x100,
- .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195,
},
{
- .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,
+ VALID_SENSOR_MAP(1, 1, 1, 1),
.offset = 0x200,
- .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195,
}
};
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,
+ VALID_SENSOR_MAP(1, 1, 0, 0),
.offset = 0x0,
- .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195,
},
{
- .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,
+ VALID_SENSOR_MAP(1, 1, 0, 0),
.offset = 0x100,
- .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195,
},
{
- .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,
+ VALID_SENSOR_MAP(1, 1, 1, 0),
.offset = 0x200,
- .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195,
},
{
- .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,
+ VALID_SENSOR_MAP(1, 1, 0, 0),
.offset = 0x300,
- .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195,
}
};
@@ -1525,16 +1694,47 @@ 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 = {
+ .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,
+ .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),
+ .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 = {
.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,
+ .gt_calib_bit_offset = 24,
};
static const struct lvts_data mt8195_lvts_mcu_data = {
@@ -1542,6 +1742,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 = {
@@ -1549,10 +1750,14 @@ 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[] = {
{ .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 },
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;
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;
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..e76e23026dc8 100644
--- a/drivers/thermal/qcom/tsens.c
+++ b/drivers/thermal/qcom/tsens.c
@@ -17,6 +17,7 @@
#include <linux/pm.h>
#include <linux/regmap.h>
#include <linux/slab.h>
+#include <linux/suspend.h>
#include <linux/thermal.h>
#include "../thermal_hwmon.h"
#include "tsens.h"
@@ -264,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;
@@ -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;
diff --git a/drivers/thermal/rcar_gen3_thermal.c b/drivers/thermal/rcar_gen3_thermal.c
index a764cb1115a5..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;
- int tj_t;
+ /* Different coefficients are used depending on a threshold. */
+ struct {
+ struct equation_set_coef below;
+ struct equation_set_coef above;
+ } coef;
int thcode[3];
};
@@ -93,6 +96,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;
};
@@ -111,84 +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.
+ *
+ * The constant thadj is one of the THCODE values, which one to use depends on
+ * the threshold and input value.
*
- * 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 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_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)
{
- /* 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));
- 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 =
+ DIV_ROUND_CLOSEST((priv->ptat[1] - priv->ptat[2]) * priv->info->scale,
+ priv->ptat[0] - priv->ptat[2])
+ + priv->info->adj_below;
}
-
-static int rcar_gen3_thermal_round(int temp)
+static void rcar_gen3_thermal_tsc_coefs(struct rcar_gen3_thermal_priv *priv,
+ struct rcar_gen3_thermal_tsc *tsc)
{
- int result, round_offs;
+ 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]);
- 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;
}
@@ -196,15 +191,22 @@ 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)
{
- int celsius, val;
+ struct rcar_gen3_thermal_priv *priv = tsc->priv;
+ const struct equation_set_coef *coef;
+ int adj, celsius, thcode;
celsius = DIV_ROUND_CLOSEST(mcelsius, 1000);
- if (celsius <= INT_FIXPT(tsc->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)
@@ -369,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,
};
@@ -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)) {
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index 34a31bc72023..d70e76dd3c94 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -15,6 +15,7 @@
#include <linux/slab.h>
#include <linux/kdev_t.h>
#include <linux/idr.h>
+#include <linux/list_sort.h>
#include <linux/thermal.h>
#include <linux/reboot.h>
#include <linux/string.h>
@@ -295,17 +296,18 @@ 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);
}
-static void handle_non_critical_trips(struct thermal_zone_device *tz,
- const struct thermal_trip *trip)
+static struct thermal_governor *thermal_get_tz_governor(struct thermal_zone_device *tz)
{
- tz->governor ? tz->governor->throttle(tz, trip) :
- def_governor->throttle(tz, trip);
+ if (tz->governor)
+ return tz->governor;
+
+ return def_governor;
}
void thermal_governor_update_tz(struct thermal_zone_device *tz,
@@ -348,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)
@@ -361,55 +359,60 @@ 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,
+ struct list_head *way_up_list,
+ struct list_head *way_down_list)
{
+ const struct thermal_trip *trip = &td->trip;
+ int old_threshold;
+
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) {
+ /*
+ * 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.
+ */
+ old_threshold = td->threshold;
+ td->threshold = trip->temperature;
+
+ if (tz->last_temperature >= old_threshold &&
+ tz->last_temperature != THERMAL_TEMP_INVALID) {
/*
- * 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.
+ * 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) {
- thermal_notify_tz_trip_up(tz, trip);
- thermal_debug_tz_trip_up(tz, trip);
- trip->threshold = trip->temperature - trip->hysteresis;
+ 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 {
- trip->threshold = trip->temperature;
+ td->threshold -= trip->hysteresis;
}
- } else {
+ } else if (tz->temperature >= trip->temperature) {
/*
- * 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.
+ * 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 - trip->hysteresis) {
- thermal_notify_tz_trip_down(tz, trip);
- thermal_debug_tz_trip_down(tz, trip);
- trip->threshold = trip->temperature;
- } else {
- trip->threshold = trip->temperature - trip->hysteresis;
- }
+ list_add_tail(&td->notify_list_node, way_up_list);
+ td->notify_temp = trip->temperature;
+ td->threshold -= trip->hysteresis;
+
+ 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);
}
-
- 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)
@@ -431,7 +434,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)
@@ -449,16 +451,56 @@ 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)
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 void thermal_trip_crossed(struct thermal_zone_device *tz,
+ const struct thermal_trip *trip,
+ struct thermal_governor *governor,
+ bool crossed_up)
+{
+ if (crossed_up) {
+ thermal_notify_tz_trip_up(tz, trip);
+ thermal_debug_tz_trip_up(tz, trip);
+ } else {
+ thermal_notify_tz_trip_down(tz, trip);
+ thermal_debug_tz_trip_down(tz, trip);
+ }
+ thermal_governor_trip_crossed(governor, tz, trip, crossed_up);
+}
+
+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 *trip;
+ struct thermal_governor *governor = thermal_get_tz_governor(tz);
+ struct thermal_trip_desc *td;
+ LIST_HEAD(way_down_list);
+ LIST_HEAD(way_up_list);
if (tz->suspended)
return;
@@ -468,12 +510,28 @@ 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;
- for_each_trip(tz, trip)
- handle_thermal_trip(tz, trip);
+ for_each_trip_desc(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_trip_crossed(tz, &td->trip, governor, true);
+
+ list_sort(NULL, &way_down_list, thermal_trip_notify_cmp);
+ list_for_each_entry(td, &way_down_list, notify_list_node)
+ thermal_trip_crossed(tz, &td->trip, governor, false);
+
+ if (governor->manage)
+ governor->manage(tz);
+
+ thermal_debug_update_trip_stats(tz);
monitor_thermal_zone(tz);
}
@@ -544,6 +602,12 @@ void thermal_zone_device_update(struct thermal_zone_device *tz,
}
EXPORT_SYMBOL_GPL(thermal_zone_device_update);
+void thermal_zone_trip_down(struct thermal_zone_device *tz,
+ const struct thermal_trip *trip)
+{
+ thermal_trip_crossed(tz, trip, thermal_get_tz_governor(tz), false);
+}
+
int for_each_thermal_governor(int (*cb)(struct thermal_governor *, void *),
void *data)
{
@@ -766,7 +830,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 +889,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);
@@ -897,6 +961,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 ||
@@ -934,6 +999,18 @@ __thermal_cooling_device_register(struct device_node *np,
if (ret)
goto out_cdev_type;
+ /*
+ * The cooling device's current state is only needed for debug
+ * initialization below, so a failure to get it does not cause
+ * the entire cooling device initialization to fail. However,
+ * the debug will not work for the device if its initial state
+ * cannot be determined and drivers are responsible for ensuring
+ * that this will not happen.
+ */
+ ret = cdev->ops->get_cur_state(cdev, &current_state);
+ if (ret)
+ current_state = ULONG_MAX;
+
thermal_cooling_device_setup_sysfs(cdev);
ret = dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
@@ -947,6 +1024,9 @@ __thermal_cooling_device_register(struct device_node *np,
return ERR_PTR(ret);
}
+ if (current_state <= cdev->max_state)
+ thermal_debug_cdev_add(cdev, current_state);
+
/* Add 'this' new cdev to the global cdev list */
mutex_lock(&thermal_list_lock);
@@ -962,8 +1042,6 @@ __thermal_cooling_device_register(struct device_node *np,
mutex_unlock(&thermal_list_lock);
- thermal_debug_cdev_add(cdev);
-
return cdev;
out_cooling_dev:
@@ -1221,16 +1299,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 +1355,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 +1422,15 @@ 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++;
+ /*
+ * Mark all thresholds as invalid to start with even though
+ * this only matters for the trips that start as invalid and
+ * become valid later.
+ */
+ td->threshold = INT_MAX;
+ }
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..20e7b45673d6 100644
--- a/drivers/thermal/thermal_core.h
+++ b/drivers/thermal/thermal_core.h
@@ -15,6 +15,120 @@
#include "thermal_netlink.h"
#include "thermal_debugfs.h"
+struct thermal_trip_desc {
+ struct thermal_trip trip;
+ struct list_head notify_list_node;
+ int notify_temp;
+ 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.
+ * @trip_crossed: called for trip points that have just been crossed
+ * @manage: called on thermal zone temperature updates
+ * @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);
+ void (*trip_crossed)(struct thermal_zone_device *tz,
+ const struct thermal_trip *trip,
+ bool crossed_up);
+ void (*manage)(struct thermal_zone_device *tz);
+ 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
+ * @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"
@@ -120,8 +234,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,
@@ -129,6 +246,8 @@ int thermal_zone_trip_id(const struct thermal_zone_device *tz,
void thermal_zone_trip_updated(struct thermal_zone_device *tz,
const struct thermal_trip *trip);
int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp);
+void thermal_zone_trip_down(struct thermal_zone_device *tz,
+ const struct thermal_trip *trip);
/* sysfs I/F */
int thermal_zone_create_device_groups(struct thermal_zone_device *tz);
diff --git a/drivers/thermal/thermal_debugfs.c b/drivers/thermal/thermal_debugfs.c
index c617e8b9f0dd..942447229157 100644
--- a/drivers/thermal/thermal_debugfs.c
+++ b/drivers/thermal/thermal_debugfs.c
@@ -91,6 +91,8 @@ struct cdev_record {
*
* @timestamp: the trip crossing timestamp
* @duration: total time when the zone temperature was above the trip point
+ * @trip_temp: trip temperature at mitigation start
+ * @trip_hyst: trip hysteresis at mitigation start
* @count: the number of times the zone temperature was above the trip point
* @max: maximum recorded temperature above the trip point
* @min: minimum recorded temperature above the trip point
@@ -99,6 +101,8 @@ struct cdev_record {
struct trip_stats {
ktime_t timestamp;
ktime_t duration;
+ int trip_temp;
+ int trip_hyst;
int count;
int max;
int min;
@@ -139,11 +143,13 @@ struct tz_episode {
* we keep track of the current position in the history array.
*
* @tz_episodes: a list of thermal mitigation episodes
+ * @tz: thermal zone this object belongs to
* @trips_crossed: an array of trip points crossed by id
* @nr_trips: the number of trip points currently being crossed
*/
struct tz_debugfs {
struct list_head tz_episodes;
+ struct thermal_zone_device *tz;
int *trips_crossed;
int nr_trips;
};
@@ -433,6 +439,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;
/*
@@ -458,8 +472,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;
@@ -476,9 +491,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);
@@ -503,15 +525,23 @@ void thermal_debug_cdev_add(struct thermal_cooling_device *cdev)
*/
void thermal_debug_cdev_remove(struct thermal_cooling_device *cdev)
{
- struct thermal_debugfs *thermal_dbg = cdev->debugfs;
+ struct thermal_debugfs *thermal_dbg;
- if (!thermal_dbg)
+ mutex_lock(&cdev->lock);
+
+ thermal_dbg = cdev->debugfs;
+ if (!thermal_dbg) {
+ mutex_unlock(&cdev->lock);
return;
+ }
+
+ cdev->debugfs = NULL;
+
+ mutex_unlock(&cdev->lock);
mutex_lock(&thermal_dbg->lock);
thermal_debugfs_cdev_clear(&thermal_dbg->cdev_dbg);
- cdev->debugfs = NULL;
mutex_unlock(&thermal_dbg->lock);
@@ -530,6 +560,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;
@@ -545,9 +576,9 @@ 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();
+ struct trip_stats *trip_stats;
if (!thermal_dbg)
return;
@@ -613,12 +644,10 @@ void thermal_debug_tz_trip_up(struct thermal_zone_device *tz,
tz_dbg->trips_crossed[tz_dbg->nr_trips++] = trip_id;
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].avg = tze->trip_stats[trip_id].avg +
- (temperature - tze->trip_stats[trip_id].avg) /
- tze->trip_stats[trip_id].count;
+ trip_stats = &tze->trip_stats[trip_id];
+ trip_stats->trip_temp = trip->temperature;
+ trip_stats->trip_hyst = trip->hysteresis;
+ trip_stats->timestamp = now;
unlock:
mutex_unlock(&thermal_dbg->lock);
@@ -671,6 +700,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.
@@ -682,12 +714,12 @@ out:
mutex_unlock(&thermal_dbg->lock);
}
-void thermal_debug_update_temp(struct thermal_zone_device *tz)
+void thermal_debug_update_trip_stats(struct thermal_zone_device *tz)
{
struct thermal_debugfs *thermal_dbg = tz->debugfs;
- struct tz_episode *tze;
struct tz_debugfs *tz_dbg;
- int trip_id, i;
+ struct tz_episode *tze;
+ int i;
if (!thermal_dbg)
return;
@@ -699,15 +731,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);
@@ -715,8 +748,7 @@ out:
static void *tze_seq_start(struct seq_file *s, loff_t *pos)
{
- struct thermal_zone_device *tz = s->private;
- struct thermal_debugfs *thermal_dbg = tz->debugfs;
+ struct thermal_debugfs *thermal_dbg = s->private;
struct tz_debugfs *tz_dbg = &thermal_dbg->tz_dbg;
mutex_lock(&thermal_dbg->lock);
@@ -726,8 +758,7 @@ static void *tze_seq_start(struct seq_file *s, loff_t *pos)
static void *tze_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
- struct thermal_zone_device *tz = s->private;
- struct thermal_debugfs *thermal_dbg = tz->debugfs;
+ struct thermal_debugfs *thermal_dbg = s->private;
struct tz_debugfs *tz_dbg = &thermal_dbg->tz_dbg;
return seq_list_next(v, &tz_dbg->tz_episodes, pos);
@@ -735,29 +766,42 @@ static void *tze_seq_next(struct seq_file *s, void *v, loff_t *pos)
static void tze_seq_stop(struct seq_file *s, void *v)
{
- struct thermal_zone_device *tz = s->private;
- struct thermal_debugfs *thermal_dbg = tz->debugfs;
+ struct thermal_debugfs *thermal_dbg = s->private;
mutex_unlock(&thermal_dbg->lock);
}
static int tze_seq_show(struct seq_file *s, void *v)
{
- struct thermal_zone_device *tz = s->private;
- struct thermal_trip *trip;
+ struct thermal_debugfs *thermal_dbg = s->private;
+ struct thermal_zone_device *tz = thermal_dbg->tz_dbg.tz;
+ 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, ",-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");
+ 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;
+ struct trip_stats *trip_stats;
- for_each_trip(tz, trip) {
/*
* There is no possible mitigation happening at the
* critical trip point, so the stats will be always
@@ -766,6 +810,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)
@@ -773,17 +824,28 @@ static int tze_seq_show(struct seq_file *s, void *v)
else
type = "hot";
- trip_id = thermal_zone_trip_id(tz, trip);
+ 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 | %*lld | %*d | %*d | %*d |\n",
+ 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(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);
+ 9, trip_stats->trip_temp,
+ 9, trip_stats->trip_hyst,
+ c, 10, duration_ms,
+ 9, trip_stats->avg,
+ 9, trip_stats->min,
+ 9, trip_stats->max);
}
return 0;
@@ -809,6 +871,8 @@ void thermal_debug_tz_add(struct thermal_zone_device *tz)
tz_dbg = &thermal_dbg->tz_dbg;
+ tz_dbg->tz = tz;
+
tz_dbg->trips_crossed = kzalloc(sizeof(int) * tz->num_trips, GFP_KERNEL);
if (!tz_dbg->trips_crossed) {
thermal_debugfs_remove_id(thermal_dbg);
@@ -817,23 +881,44 @@ void thermal_debug_tz_add(struct thermal_zone_device *tz)
INIT_LIST_HEAD(&tz_dbg->tz_episodes);
- debugfs_create_file("mitigations", 0400, thermal_dbg->d_top, tz, &tze_fops);
+ debugfs_create_file("mitigations", 0400, thermal_dbg->d_top,
+ thermal_dbg, &tze_fops);
tz->debugfs = thermal_dbg;
}
void thermal_debug_tz_remove(struct thermal_zone_device *tz)
{
- struct thermal_debugfs *thermal_dbg = tz->debugfs;
+ struct thermal_debugfs *thermal_dbg;
+ struct tz_episode *tze, *tmp;
+ struct tz_debugfs *tz_dbg;
+ int *trips_crossed;
- if (!thermal_dbg)
+ mutex_lock(&tz->lock);
+
+ thermal_dbg = tz->debugfs;
+ if (!thermal_dbg) {
+ mutex_unlock(&tz->lock);
return;
+ }
+
+ tz->debugfs = NULL;
+
+ mutex_unlock(&tz->lock);
+
+ tz_dbg = &thermal_dbg->tz_dbg;
mutex_lock(&thermal_dbg->lock);
- tz->debugfs = NULL;
+ trips_crossed = tz_dbg->trips_crossed;
+
+ list_for_each_entry_safe(tze, tmp, &tz_dbg->tz_episodes, node) {
+ list_del(&tze->node);
+ kfree(tze);
+ }
mutex_unlock(&thermal_dbg->lock);
thermal_debugfs_remove_id(thermal_dbg);
+ kfree(trips_crossed);
}
diff --git a/drivers/thermal/thermal_debugfs.h b/drivers/thermal/thermal_debugfs.h
index 155b9af5fe87..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);
@@ -11,10 +11,10 @@ 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) {}
+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) {}
@@ -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 */
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..97157c453630 100644
--- a/drivers/thermal/thermal_netlink.c
+++ b/drivers/thermal/thermal_netlink.c
@@ -7,17 +7,13 @@
* Generic netlink for thermal management framework
*/
#include <linux/module.h>
+#include <linux/notifier.h>
#include <linux/kernel.h>
#include <net/genetlink.h>
#include <uapi/linux/thermal.h>
#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, },
@@ -74,11 +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 *******************************/
@@ -95,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;
@@ -108,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:
@@ -282,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;
@@ -292,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;
@@ -445,7 +442,7 @@ out_cancel_nest:
static int thermal_genl_cmd_tz_get_trip(struct param *p)
{
struct sk_buff *msg = p->msg;
- const struct thermal_trip *trip;
+ const struct thermal_trip_desc *td;
struct thermal_zone_device *tz;
struct nlattr *start_trip;
int id;
@@ -465,7 +462,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) ||
@@ -593,7 +592,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;
@@ -625,7 +624,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;
@@ -645,6 +644,27 @@ out_free_msg:
return ret;
}
+static int thermal_genl_bind(int mcgrp)
+{
+ struct thermal_genl_notify n = { .mcgrp = mcgrp };
+
+ if (WARN_ON_ONCE(mcgrp > THERMAL_GENL_MAX_GROUP))
+ return -EINVAL;
+
+ blocking_notifier_call_chain(&thermal_genl_chain, THERMAL_NOTIFY_BIND, &n);
+ return 0;
+}
+
+static void thermal_genl_unbind(int mcgrp)
+{
+ struct thermal_genl_notify n = { .mcgrp = mcgrp };
+
+ if (WARN_ON_ONCE(mcgrp > THERMAL_GENL_MAX_GROUP))
+ return;
+
+ blocking_notifier_call_chain(&thermal_genl_chain, THERMAL_NOTIFY_UNBIND, &n);
+}
+
static const struct genl_small_ops thermal_genl_ops[] = {
{
.cmd = THERMAL_GENL_CMD_TZ_GET_ID,
@@ -673,12 +693,14 @@ 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,
.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,12 +708,22 @@ 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);
+ 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);
}
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;
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_trace.h b/drivers/thermal/thermal_trace.h
index 459c8ce6cf3b..df8f4edd6068 100644
--- a/drivers/thermal/thermal_trace.h
+++ b/drivers/thermal/thermal_trace.h
@@ -9,6 +9,8 @@
#include <linux/thermal.h>
#include <linux/tracepoint.h>
+#include "thermal_core.h"
+
TRACE_DEFINE_ENUM(THERMAL_TRIP_CRITICAL);
TRACE_DEFINE_ENUM(THERMAL_TRIP_HOT);
TRACE_DEFINE_ENUM(THERMAL_TRIP_PASSIVE);
@@ -35,7 +37,7 @@ TRACE_EVENT(thermal_temperature,
),
TP_fast_assign(
- __assign_str(thermal_zone, tz->type);
+ __assign_str(thermal_zone);
__entry->id = tz->id;
__entry->temp_prev = tz->last_temperature;
__entry->temp = tz->temperature;
@@ -58,7 +60,7 @@ TRACE_EVENT(cdev_update,
),
TP_fast_assign(
- __assign_str(type, cdev->type);
+ __assign_str(type);
__entry->target = target;
),
@@ -80,7 +82,7 @@ TRACE_EVENT(thermal_zone_trip,
),
TP_fast_assign(
- __assign_str(thermal_zone, tz->type);
+ __assign_str(thermal_zone);
__entry->id = tz->id;
__entry->trip = trip;
__entry->trip_type = trip_type;
@@ -154,7 +156,7 @@ TRACE_EVENT(thermal_power_devfreq_get_power,
),
TP_fast_assign(
- __assign_str(type, cdev->type);
+ __assign_str(type);
__entry->freq = freq;
__entry->busy_time = status->busy_time;
__entry->total_time = status->total_time;
@@ -182,7 +184,7 @@ TRACE_EVENT(thermal_power_devfreq_limit,
),
TP_fast_assign(
- __assign_str(type, cdev->type);
+ __assign_str(type);
__entry->freq = freq;
__entry->cdev_state = cdev_state;
__entry->power = power;
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 <linux/tracepoint.h>
+#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/drivers/thermal/thermal_trip.c b/drivers/thermal/thermal_trip.c
index 497abf0d47ca..49e63db68517 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,8 +136,9 @@ 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)
{
@@ -152,5 +154,27 @@ void thermal_zone_set_trip_temp(struct thermal_zone_device *tz,
trip->temperature = temp;
thermal_notify_tz_trip_change(tz, trip);
+
+ if (temp == THERMAL_TEMP_INVALID) {
+ struct thermal_trip_desc *td = trip_to_trip_desc(trip);
+
+ if (tz->temperature >= td->threshold) {
+ /*
+ * The trip has been crossed on the way up, so some
+ * adjustments are needed to compensate for the lack
+ * of it going forward.
+ */
+ if (trip->type == THERMAL_TRIP_PASSIVE) {
+ tz->passive--;
+ WARN_ON_ONCE(tz->passive < 0);
+ }
+ thermal_zone_trip_down(tz, trip);
+ }
+ /*
+ * Invalidate the threshold to avoid triggering a spurious
+ * trip crossing notification when the trip becomes valid.
+ */
+ td->threshold = INT_MAX;
+ }
}
EXPORT_SYMBOL_GPL(thermal_zone_set_trip_temp);