Commit f952b6c8 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'thermal-6.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull thermal control updates from Rafael Wysocki:
 "The most significant part of this is a rework of thermal governors,
  including a redesign of the thermal governor interface and changes to
  make some of them take trip point hysteresis into account properly, as
  well as some related cleanups of the thermal governors and thermal
  core.

  The above is based on preliminary changes refactoring thermal data
  structures and moving the definitions of some of them into the thermal
  core which also ensure that trip point crossing notifications will be
  sent to user space via netlink and recorded in the debug statistics in
  temperature order.

  In addition, netlink bind/unbind notifications are added to the
  thermal core and the Intel HFI driver is modified to use them to avoid
  sending netlink messages until there are subscribers.

  Apart from that, multiple thermal drivers are updated which includes
  new hardware support (MediaTek MT8188 and MT8186, Amlogic A1 thermal
  sensor, Loongson-2K2000, Lmh QCM2290), fixes, cleanups and
  documentation updates, and the recently added thermal debug code is
  fixed and cleaned up.

  Specifics:

   - Redesign the thermal governor interface to allow the governors to
     work in a more straightforward way (Rafael Wysocki)

   - Make thermal governors take the current trip point thresholds into
     account in their computations which allows trip hysteresis to be
     observed more accurately (Rafael Wysocki)

   - Make the thermal core manage passive polling for thermal zones and
     remove passive polling management from thermal governors (Rafael
     Wysocki)

   - Refactor trip point representation and move the definition of
     thermal governor and thermal zone device structures to the thermal
     core (Rafael Wysocki)

   - Sort trip point crossing notifications and debug recording of trip
     point crossing events by temperature (Rafael Wysocki)

   - Improve the handling of cooling device states and thermal
     mitigation episodes in progress in the thermal debug code (Rafael
     Wysocki)

   - Avoid excessive updates of trip point statistics and clean up the
     printing of thermal mitigation episode information (Rafael Wysocki)

   - Clean up thermal governors and thermal core (Rafael Wysocki)

   - Allow thermal drivers to register notifiers that will be invoked on
     netlink events like BIND and UNBIND, so that they can adjust their
     activity depending on whether or not there are any subscribers of
     netlink messages coming from them, and make the Intel HFI driver
     use this mechanism (Stanislaw Gruszka)

   - Adjust the update delay and capabilities-per-event values in the
     Intel HFI thermal driver to prevent it from missing events and
     allow it to process more data in one go (Ricardo Neri)

   - Add missing MODULE_DESCRIPTION() to multiple files in the
     int340x_thermal and intel_soc_dts_iosf drivers (Srinivas
     Pandruvada)

   - Replace deprecated strncpy() with strscpy() in the int340x_thermal
     driver (Justin Stitt)

   - Add QCM2290 compatible DT bindings for Lmh and fix a NULL pointer
     dereference in the lmh driver when the SCM is not present (Konrad
     Dybcio)

   - Use the strreplace() function instead of doing it manually in the
     Armada driver (Rasmus Villemoes)

   - Convert st,stih407-thermal to DT schema and fix up missing
     properties (Raphael Gallais-Pou)

   - Add suspend/resume by restoring the context of the tsens sensor
     (Priyansh Jain)

   - Support A1 SoC family Thermal Sensor controller and add the DT
     bindings (Dmitry Rokosov)

   - Improve the temperature approximation calculation and consolidate
     the Tj constant into a shared area of the structure instead of
     duplicating it on the Rcar Gen3 (Niklas Söderlund)

   - Fix the Mediatek LVTS sensor coefficient for the MT8192 in order to
     support it correctly (Hsin-Te Yuan)

   - Fix a NULL pointer dereference in the tsens driver when the
     function compute_intercept_slope() is called with a NULL parameter
     (Aleksandr Mishin)

   - Remove some unused fields in struct qpnp_tm_chip and k3_bandgap
     (Christophe Jaillet)

   - Fix up calibration efuse data decoding, consolidate the code by
     checking boundaries and refactor some part of the LVTS Mediatek
     driver. After setting the scene, add MT8186 and MT8188 along with
     the DT bindings (Nicolas Pitre)

   - Add Loongson-2K2000 support after some minor code adjustements and
     providing the DT bindings definition (Binbin Zhou)"

* tag 'thermal-6.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (72 commits)
  thermal: intel: hfi: Increase the number of CPU capabilities per netlink event
  thermal: intel: hfi: Rename HFI_MAX_THERM_NOTIFY_COUNT
  thermal: intel: hfi: Shorten the thermal netlink event delay to 100ms
  thermal: intel: hfi: Rename HFI_UPDATE_INTERVAL
  thermal: intel: Add missing module description
  thermal: core: Move passive polling management to the core
  thermal: core: Do not call handle_thermal_trip() if zone temperature is invalid
  thermal: trip: Add missing empty code line
  thermal/debugfs: Avoid printing zero duration for mitigation events in progress
  thermal/debugfs: Pass cooling device state to thermal_debug_cdev_add()
  thermal/debugfs: Create records for cdev states as they get used
  thermal: core: Introduce thermal_governor_trip_crossed()
  thermal/debugfs: Make tze_seq_show() skip invalid trips and trips with no stats
  thermal/debugfs: Rename thermal_debug_update_temp() to thermal_debug_update_trip_stats()
  thermal/debugfs: Clean up thermal_debug_update_temp()
  thermal/debugfs: Avoid excessive updates of trip point statistics
  thermal: core: Relocate critical and hot trip handling
  thermal: core: Drop the .throttle() governor callback
  thermal: gov_user_space: Use .trip_crossed() instead of .throttle()
  thermal: gov_fair_share: Eliminate unnecessary integer divisions
  ...
parents 4b95dc87 3a47fbdd
......@@ -13,11 +13,13 @@ description: Binding for Amlogic Thermal
properties:
compatible:
items:
- enum:
- amlogic,g12a-cpu-thermal
- amlogic,g12a-ddr-thermal
- const: amlogic,g12a-thermal
oneOf:
- items:
- enum:
- amlogic,g12a-cpu-thermal
- amlogic,g12a-ddr-thermal
- const: amlogic,g12a-thermal
- const: amlogic,a1-cpu-thermal
reg:
maxItems: 1
......
......@@ -18,13 +18,15 @@ properties:
oneOf:
- enum:
- loongson,ls2k1000-thermal
- loongson,ls2k2000-thermal
- items:
- enum:
- loongson,ls2k2000-thermal
- loongson,ls2k0500-thermal
- const: loongson,ls2k1000-thermal
reg:
maxItems: 1
minItems: 1
maxItems: 2
interrupts:
maxItems: 1
......@@ -38,6 +40,24 @@ required:
- interrupts
- '#thermal-sensor-cells'
if:
properties:
compatible:
contains:
enum:
- loongson,ls2k2000-thermal
then:
properties:
reg:
minItems: 2
maxItems: 2
else:
properties:
reg:
maxItems: 1
unevaluatedProperties: false
examples:
......
......@@ -19,6 +19,9 @@ properties:
compatible:
enum:
- mediatek,mt7988-lvts-ap
- mediatek,mt8186-lvts
- mediatek,mt8188-lvts-ap
- mediatek,mt8188-lvts-mcu
- mediatek,mt8192-lvts-ap
- mediatek,mt8192-lvts-mcu
- mediatek,mt8195-lvts-ap
......@@ -60,6 +63,8 @@ allOf:
compatible:
contains:
enum:
- mediatek,mt8188-lvts-ap
- mediatek,mt8188-lvts-mcu
- mediatek,mt8192-lvts-ap
- mediatek,mt8192-lvts-mcu
then:
......@@ -75,6 +80,7 @@ allOf:
compatible:
contains:
enum:
- mediatek,mt8186-lvts
- mediatek,mt8195-lvts-ap
- mediatek,mt8195-lvts-mcu
then:
......
......@@ -17,10 +17,14 @@ description:
properties:
compatible:
enum:
- qcom,sc8180x-lmh
- qcom,sdm845-lmh
- qcom,sm8150-lmh
oneOf:
- enum:
- qcom,sc8180x-lmh
- qcom,sdm845-lmh
- qcom,sm8150-lmh
- items:
- const: qcom,qcm2290-lmh
- const: qcom,sm8150-lmh
reg:
items:
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/thermal/st,stih407-thermal.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: STMicroelectronics STi digital thermal sensor (DTS)
maintainers:
- Patrice Chotard <patrice.chotard@foss.st.com>
- Lee Jones <lee@kernel.org>
allOf:
- $ref: thermal-sensor.yaml
properties:
compatible:
const: st,stih407-thermal
reg:
maxItems: 1
clocks:
maxItems: 1
clock-names:
items:
- const: thermal
interrupts:
description:
For thermal sensors for which no interrupt has been defined, a polling
delay of 1000ms will be used to read the temperature from device.
maxItems: 1
'#thermal-sensor-cells':
const: 0
required:
- compatible
- reg
- clocks
- clock-names
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
temperature-sensor@91a0000 {
compatible = "st,stih407-thermal";
reg = <0x91a0000 0x28>;
clock-names = "thermal";
clocks = <&CLK_SYSIN>;
interrupts = <GIC_SPI 205 IRQ_TYPE_EDGE_RISING>;
#thermal-sensor-cells = <0>;
};
...
Binding for Thermal Sensor driver for STMicroelectronics STi series of SoCs.
Required parameters:
-------------------
compatible : Should be "st,stih407-thermal"
clock-names : Should be "thermal".
See: Documentation/devicetree/bindings/resource-names.txt
clocks : Phandle of the clock used by the thermal sensor.
See: Documentation/devicetree/bindings/clock/clock-bindings.txt
Optional parameters:
-------------------
reg : For non-sysconf based sensors, this should be the physical base
address and length of the sensor's registers.
interrupts : Standard way to define interrupt number.
NB: For thermal sensor's for which no interrupt has been
defined, a polling delay of 1000ms will be used to read the
temperature from device.
Example:
temp0@91a0000 {
compatible = "st,stih407-thermal";
reg = <0x91a0000 0x28>;
clock-names = "thermal";
clocks = <&CLK_SYSIN>;
interrupts = <GIC_SPI 205 IRQ_TYPE_EDGE_RISING>;
st,passive_cooling_temp = <110>;
};
......@@ -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);
......
......@@ -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, '-', '_');
}
/*
......
......@@ -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);
......@@ -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);
......@@ -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);
......@@ -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,99 @@ 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)
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)
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);
......@@ -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);
......@@ -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;
......
......@@ -156,3 +156,4 @@ int processor_thermal_mbox_interrupt_config(struct pci_dev *pdev, bool enable,
EXPORT_SYMBOL_NS_GPL(processor_thermal_mbox_interrupt_config, INT340X_THERMAL);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Processor Thermal Mail Box Interface");
......@@ -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");
......@@ -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");
......@@ -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");
......@@ -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");
......@@ -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");
......@@ -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];
......
......@@ -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");
......@@ -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 */
......
......@@ -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");
This diff is collapsed.
......@@ -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;
......
......@@ -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;
......
......@@ -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 = {
......
......@@ -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;
......
......@@ -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;
......
......@@ -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)) {
......
This diff is collapsed.
......@@ -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,
......
......@@ -435,6 +435,14 @@ void thermal_debug_cdev_state_update(const struct thermal_cooling_device *cdev,
}
cdev_dbg->current_state = new_state;
/*
* Create a record for the new state if it is not there, so its
* duration will be printed by cdev_dt_seq_show() as expected if it
* runs before the next state transition.
*/
thermal_debugfs_cdev_record_get(thermal_dbg, cdev_dbg->durations, new_state);
transition = (old_state << 16) | new_state;
/*
......@@ -460,8 +468,9 @@ void thermal_debug_cdev_state_update(const struct thermal_cooling_device *cdev,
* Allocates a cooling device object for debug, initializes the
* statistics and create the entries in sysfs.
* @cdev: a pointer to a cooling device
* @state: current state of the cooling device
*/
void thermal_debug_cdev_add(struct thermal_cooling_device *cdev)
void thermal_debug_cdev_add(struct thermal_cooling_device *cdev, int state)
{
struct thermal_debugfs *thermal_dbg;
struct cdev_debugfs *cdev_dbg;
......@@ -478,9 +487,16 @@ void thermal_debug_cdev_add(struct thermal_cooling_device *cdev)
INIT_LIST_HEAD(&cdev_dbg->durations[i]);
}
cdev_dbg->current_state = 0;
cdev_dbg->current_state = state;
cdev_dbg->timestamp = ktime_get();
/*
* Create a record for the initial cooling device state, so its
* duration will be printed by cdev_dt_seq_show() as expected if it
* runs before the first state transition.
*/
thermal_debugfs_cdev_record_get(thermal_dbg, cdev_dbg->durations, state);
debugfs_create_file("trans_table", 0400, thermal_dbg->d_top,
thermal_dbg, &tt_fops);
......@@ -540,6 +556,7 @@ static struct tz_episode *thermal_debugfs_tz_event_alloc(struct thermal_zone_dev
INIT_LIST_HEAD(&tze->node);
tze->timestamp = now;
tze->duration = KTIME_MIN;
for (i = 0; i < tz->num_trips; i++) {
tze->trip_stats[i].min = INT_MAX;
......@@ -555,7 +572,6 @@ void thermal_debug_tz_trip_up(struct thermal_zone_device *tz,
struct tz_episode *tze;
struct tz_debugfs *tz_dbg;
struct thermal_debugfs *thermal_dbg = tz->debugfs;
int temperature = tz->temperature;
int trip_id = thermal_zone_trip_id(tz, trip);
ktime_t now = ktime_get();
......@@ -624,12 +640,6 @@ void thermal_debug_tz_trip_up(struct thermal_zone_device *tz,
tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node);
tze->trip_stats[trip_id].timestamp = now;
tze->trip_stats[trip_id].max = max(tze->trip_stats[trip_id].max, temperature);
tze->trip_stats[trip_id].min = min(tze->trip_stats[trip_id].min, temperature);
tze->trip_stats[trip_id].count++;
tze->trip_stats[trip_id].avg = tze->trip_stats[trip_id].avg +
(temperature - tze->trip_stats[trip_id].avg) /
tze->trip_stats[trip_id].count;
unlock:
mutex_unlock(&thermal_dbg->lock);
......@@ -682,6 +692,9 @@ void thermal_debug_tz_trip_down(struct thermal_zone_device *tz,
tze->trip_stats[trip_id].duration =
ktime_add(delta, tze->trip_stats[trip_id].duration);
/* Mark the end of mitigation for this trip point. */
tze->trip_stats[trip_id].timestamp = KTIME_MAX;
/*
* This event closes the mitigation as we are crossing the
* last trip point the way down.
......@@ -693,12 +706,12 @@ void thermal_debug_tz_trip_down(struct thermal_zone_device *tz,
mutex_unlock(&thermal_dbg->lock);
}
void thermal_debug_update_temp(struct thermal_zone_device *tz)
void thermal_debug_update_trip_stats(struct thermal_zone_device *tz)
{
struct thermal_debugfs *thermal_dbg = tz->debugfs;
struct tz_episode *tze;
struct tz_debugfs *tz_dbg;
int trip_id, i;
struct tz_episode *tze;
int i;
if (!thermal_dbg)
return;
......@@ -710,15 +723,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);
......@@ -753,20 +767,37 @@ static int tze_seq_show(struct seq_file *s, void *v)
{
struct thermal_debugfs *thermal_dbg = s->private;
struct thermal_zone_device *tz = thermal_dbg->tz_dbg.tz;
struct thermal_trip *trip;
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;
/* Skip invalid trips. */
if (trip->temperature == THERMAL_TEMP_INVALID)
continue;
for_each_trip(tz, trip) {
/*
* There is no possible mitigation happening at the
* critical trip point, so the stats will be always
......@@ -775,6 +806,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)
......@@ -782,17 +820,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);
c, 10, duration_ms,
9, trip_stats->avg,
9, trip_stats->min,
9, trip_stats->max);
}
return 0;
......
......@@ -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 */
......@@ -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;
......
......@@ -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 @@ static int thermal_genl_cmd_tz_get_id(struct param *p)
static int thermal_genl_cmd_tz_get_trip(struct param *p)
{
struct sk_buff *msg = p->msg;
const struct thermal_trip *trip;
const struct thermal_trip_desc *td;
struct thermal_zone_device *tz;
struct nlattr *start_trip;
int id;
......@@ -465,7 +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 @@ static int thermal_genl_cmd_doit(struct sk_buff *skb,
return ret;
}
static int thermal_genl_bind(int mcgrp)
{
struct thermal_genl_notify n = { .mcgrp = mcgrp };
if (WARN_ON_ONCE(mcgrp > THERMAL_GENL_MAX_GROUP))
return -EINVAL;
blocking_notifier_call_chain(&thermal_genl_chain, THERMAL_NOTIFY_BIND, &n);
return 0;
}
static void thermal_genl_unbind(int mcgrp)
{
struct thermal_genl_notify n = { .mcgrp = mcgrp };
if (WARN_ON_ONCE(mcgrp > THERMAL_GENL_MAX_GROUP))
return;
blocking_notifier_call_chain(&thermal_genl_chain, THERMAL_NOTIFY_UNBIND, &n);
}
static const struct genl_small_ops thermal_genl_ops[] = {
{
.cmd = THERMAL_GENL_CMD_TZ_GET_ID,
......@@ -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);
}
......@@ -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;
......
......@@ -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;
......
......@@ -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);
......
......@@ -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,
......
......@@ -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)
{
......
......@@ -16,6 +16,32 @@
#define MT7988_ETHWARP_0 6
#define MT7988_ETHWARP_1 7
#define MT8186_LITTLE_CPU0 0
#define MT8186_LITTLE_CPU1 1
#define MT8186_LITTLE_CPU2 2
#define MT8186_CAM 3
#define MT8186_BIG_CPU0 4
#define MT8186_BIG_CPU1 5
#define MT8186_NNA 6
#define MT8186_ADSP 7
#define MT8186_MFG 8
#define MT8188_MCU_LITTLE_CPU0 0
#define MT8188_MCU_LITTLE_CPU1 1
#define MT8188_MCU_LITTLE_CPU2 2
#define MT8188_MCU_LITTLE_CPU3 3
#define MT8188_MCU_BIG_CPU0 4
#define MT8188_MCU_BIG_CPU1 5
#define MT8188_AP_APU 0
#define MT8188_AP_GPU1 1
#define MT8188_AP_GPU2 2
#define MT8188_AP_SOC1 3
#define MT8188_AP_SOC2 4
#define MT8188_AP_SOC3 5
#define MT8188_AP_CAM1 6
#define MT8188_AP_CAM2 7
#define MT8195_MCU_BIG_CPU0 0
#define MT8195_MCU_BIG_CPU1 1
#define MT8195_MCU_BIG_CPU2 2
......
......@@ -61,7 +61,6 @@ enum thermal_notify_event {
* struct thermal_trip - representation of a point in temperature domain
* @temperature: temperature value in miliCelsius
* @hysteresis: relative hysteresis in miliCelsius
* @threshold: trip crossing notification threshold miliCelsius
* @type: trip point type
* @priv: pointer to driver data associated with this trip
* @flags: flags representing binary properties of the trip
......@@ -69,7 +68,6 @@ enum thermal_notify_event {
struct thermal_trip {
int temperature;
int hysteresis;
int threshold;
enum thermal_trip_type type;
u8 flags;
void *priv;
......@@ -81,6 +79,8 @@ struct thermal_trip {
#define THERMAL_TRIP_FLAG_RW (THERMAL_TRIP_FLAG_RW_TEMP | \
THERMAL_TRIP_FLAG_RW_HYST)
struct thermal_zone_device;
struct thermal_zone_device_ops {
int (*bind) (struct thermal_zone_device *,
struct thermal_cooling_device *);
......@@ -126,111 +126,6 @@ struct thermal_cooling_device {
#endif
};
/**
* struct thermal_zone_device - structure for a thermal zone
* @id: unique id number for each thermal zone
* @type: the thermal zone device type
* @device: &struct device for this thermal zone
* @removal: removal completion
* @trip_temp_attrs: attributes for trip points for sysfs: trip temperature
* @trip_type_attrs: attributes for trip points for sysfs: trip type
* @trip_hyst_attrs: attributes for trip points for sysfs: trip hysteresis
* @mode: current mode of this thermal zone
* @devdata: private pointer for device private data
* @num_trips: number of trip points the thermal zone supports
* @passive_delay_jiffies: number of jiffies to wait between polls when
* performing passive cooling.
* @polling_delay_jiffies: number of jiffies to wait between polls when
* checking whether trip points have been crossed (0 for
* interrupt driven systems)
* @temperature: current temperature. This is only for core code,
* drivers should use thermal_zone_get_temp() to get the
* current temperature
* @last_temperature: previous temperature read
* @emul_temperature: emulated temperature when using CONFIG_THERMAL_EMULATION
* @passive: 1 if you've crossed a passive trip point, 0 otherwise.
* @prev_low_trip: the low current temperature if you've crossed a passive
trip point.
* @prev_high_trip: the above current temperature if you've crossed a
passive trip point.
* @need_update: if equals 1, thermal_zone_device_update needs to be invoked.
* @ops: operations this &thermal_zone_device supports
* @tzp: thermal zone parameters
* @governor: pointer to the governor for this thermal zone
* @governor_data: private pointer for governor data
* @thermal_instances: list of &struct thermal_instance of this thermal zone
* @ida: &struct ida to generate unique id for this zone's cooling
* devices
* @lock: lock to protect thermal_instances list
* @node: node in thermal_tz_list (in thermal_core.c)
* @poll_queue: delayed work for polling
* @notify_event: Last notification event
* @suspended: thermal zone suspend indicator
* @trips: array of struct thermal_trip objects
*/
struct thermal_zone_device {
int id;
char type[THERMAL_NAME_LENGTH];
struct device device;
struct completion removal;
struct attribute_group trips_attribute_group;
struct thermal_attr *trip_temp_attrs;
struct thermal_attr *trip_type_attrs;
struct thermal_attr *trip_hyst_attrs;
enum thermal_device_mode mode;
void *devdata;
int num_trips;
unsigned long passive_delay_jiffies;
unsigned long polling_delay_jiffies;
int temperature;
int last_temperature;
int emul_temperature;
int passive;
int prev_low_trip;
int prev_high_trip;
atomic_t need_update;
struct thermal_zone_device_ops ops;
struct thermal_zone_params *tzp;
struct thermal_governor *governor;
void *governor_data;
struct list_head thermal_instances;
struct ida ida;
struct mutex lock;
struct list_head node;
struct delayed_work poll_queue;
enum thermal_notify_event notify_event;
bool suspended;
#ifdef CONFIG_THERMAL_DEBUGFS
struct thermal_debugfs *debugfs;
#endif
struct thermal_trip trips[] __counted_by(num_trips);
};
/**
* struct thermal_governor - structure that holds thermal governor information
* @name: name of the governor
* @bind_to_tz: callback called when binding to a thermal zone. If it
* returns 0, the governor is bound to the thermal zone,
* otherwise it fails.
* @unbind_from_tz: callback called when a governor is unbound from a
* thermal zone.
* @throttle: callback called for every trip point even if temperature is
* below the trip point temperature
* @update_tz: callback called when thermal zone internals have changed, e.g.
* thermal cooling instance was added/removed
* @governor_list: node in thermal_governor_list (in thermal_core.c)
*/
struct thermal_governor {
const char *name;
int (*bind_to_tz)(struct thermal_zone_device *tz);
void (*unbind_from_tz)(struct thermal_zone_device *tz);
int (*throttle)(struct thermal_zone_device *tz,
const struct thermal_trip *trip);
void (*update_tz)(struct thermal_zone_device *tz,
enum thermal_notify_event reason);
struct list_head governor_list;
};
/* Structure to define Thermal Zone parameters */
struct thermal_zone_params {
const char *governor_name;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment