mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 04:18:39 +08:00
Merge branch 'thermal-core'
Merge thermal core updates for 6.12 which, among other things, rework the thermal driver interface for binding cooling devices to thermal zones and add a thermal core testing module: - Update some thermal drivers to eliminate thermal_zone_get_trip() calls from them and get rid of that function (Rafael Wysocki). - Update the thermal sysfs code to store trip point attributes in trip descriptors and get to trip points via attribute pointers (Rafael Wysocki). - Move the computation of the low and high boundaries for thermal_zone_set_trips() to __thermal_zone_device_update() (Daniel Lezcano). - Introduce a debugfs-based facility for thermal core testing (Rafael Wysocki). - Replace the thermal zone .bind() and .unbind() callbacks for binding cooling devices to thermal zones with one .should_bind() callback used for deciding whether or not a given cooling devices should be bound to a given trip point in a given thermal zone (Rafael Wysocki). - Eliminate code that has no more users after the other changes, drop some redundant checks from the thermal core and clean it up (Rafael Wysocki). - Fix rounding of delay jiffies in the thermal core (Rafael Wysocki). * thermal-core: (31 commits) thermal: core: Drop tz field from struct thermal_instance thermal: core: Drop redundant checks from thermal_bind_cdev_to_trip() thermal: core: Rename cdev-to-thermal-zone bind/unbind functions thermal: core: Fix rounding of delay jiffies thermal: core: Clean up trip bind/unbind functions thermal: core: Drop unused bind/unbind functions and callbacks thermal/of: Use the .should_bind() thermal zone callback thermal: imx: Use the .should_bind() thermal zone callback mlxsw: core_thermal: Use the .should_bind() thermal zone callback platform/x86: acerhdf: Use the .should_bind() thermal zone callback thermal: core: Unexport thermal_bind_cdev_to_trip() and thermal_unbind_cdev_from_trip() thermal: ACPI: Use the .should_bind() thermal zone callback thermal: core: Introduce .should_bind() thermal zone callback thermal: core: Move thermal zone locking out of bind/unbind functions thermal: sysfs: Use the dev argument in instance-related show/store thermal: core: Drop redundant thermal instance checks thermal: core: Rearrange checks in thermal_bind_cdev_to_trip() thermal: core: Fold two functions into their respective callers thermal: Introduce a debugfs-based testing facility thermal/core: Compute low and high boundaries in thermal_zone_device_update() ...
This commit is contained in:
commit
f5c0597434
@ -58,10 +58,9 @@ temperature) and throttle appropriate devices.
|
||||
ops:
|
||||
thermal zone device call-backs.
|
||||
|
||||
.bind:
|
||||
bind the thermal zone device with a thermal cooling device.
|
||||
.unbind:
|
||||
unbind the thermal zone device with a thermal cooling device.
|
||||
.should_bind:
|
||||
check whether or not a given cooling device should be bound to
|
||||
a given trip point in this thermal zone.
|
||||
.get_temp:
|
||||
get the current temperature of the thermal zone.
|
||||
.set_trips:
|
||||
@ -246,56 +245,6 @@ temperature) and throttle appropriate devices.
|
||||
It deletes the corresponding entry from /sys/class/thermal folder and
|
||||
unbinds itself from all the thermal zone devices using it.
|
||||
|
||||
1.3 interface for binding a thermal zone device with a thermal cooling device
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
::
|
||||
|
||||
int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
|
||||
int trip, struct thermal_cooling_device *cdev,
|
||||
unsigned long upper, unsigned long lower, unsigned int weight);
|
||||
|
||||
This interface function binds a thermal cooling device to a particular trip
|
||||
point of a thermal zone device.
|
||||
|
||||
This function is usually called in the thermal zone device .bind callback.
|
||||
|
||||
tz:
|
||||
the thermal zone device
|
||||
cdev:
|
||||
thermal cooling device
|
||||
trip:
|
||||
indicates which trip point in this thermal zone the cooling device
|
||||
is associated with.
|
||||
upper:
|
||||
the Maximum cooling state for this trip point.
|
||||
THERMAL_NO_LIMIT means no upper limit,
|
||||
and the cooling device can be in max_state.
|
||||
lower:
|
||||
the Minimum cooling state can be used for this trip point.
|
||||
THERMAL_NO_LIMIT means no lower limit,
|
||||
and the cooling device can be in cooling state 0.
|
||||
weight:
|
||||
the influence of this cooling device in this thermal
|
||||
zone. See 1.4.1 below for more information.
|
||||
|
||||
::
|
||||
|
||||
int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
|
||||
int trip, struct thermal_cooling_device *cdev);
|
||||
|
||||
This interface function unbinds a thermal cooling device from a particular
|
||||
trip point of a thermal zone device. This function is usually called in
|
||||
the thermal zone device .unbind callback.
|
||||
|
||||
tz:
|
||||
the thermal zone device
|
||||
cdev:
|
||||
thermal cooling device
|
||||
trip:
|
||||
indicates which trip point in this thermal zone the cooling device
|
||||
is associated with.
|
||||
|
||||
1.4 Thermal Zone Parameters
|
||||
---------------------------
|
||||
|
||||
@ -366,8 +315,6 @@ Thermal cooling device sys I/F, created once it's registered::
|
||||
|
||||
Then next two dynamic attributes are created/removed in pairs. They represent
|
||||
the relationship between a thermal zone and its associated cooling device.
|
||||
They are created/removed for each successful execution of
|
||||
thermal_zone_bind_cooling_device/thermal_zone_unbind_cooling_device.
|
||||
|
||||
::
|
||||
|
||||
@ -459,14 +406,7 @@ are supposed to implement the callback. If they don't, the thermal
|
||||
framework calculated the trend by comparing the previous and the current
|
||||
temperature values.
|
||||
|
||||
4.2. get_thermal_instance
|
||||
-------------------------
|
||||
|
||||
This function returns the thermal_instance corresponding to a given
|
||||
{thermal_zone, cooling_device, trip_point} combination. Returns NULL
|
||||
if such an instance does not exist.
|
||||
|
||||
4.3. thermal_cdev_update
|
||||
4.2. thermal_cdev_update
|
||||
------------------------
|
||||
|
||||
This function serves as an arbitrator to set the state of a cooling
|
||||
|
@ -558,77 +558,31 @@ static void acpi_thermal_zone_device_critical(struct thermal_zone_device *therma
|
||||
thermal_zone_device_critical(thermal);
|
||||
}
|
||||
|
||||
struct acpi_thermal_bind_data {
|
||||
struct thermal_zone_device *thermal;
|
||||
struct thermal_cooling_device *cdev;
|
||||
bool bind;
|
||||
};
|
||||
|
||||
static int bind_unbind_cdev_cb(struct thermal_trip *trip, void *arg)
|
||||
static bool acpi_thermal_should_bind_cdev(struct thermal_zone_device *thermal,
|
||||
const struct thermal_trip *trip,
|
||||
struct thermal_cooling_device *cdev,
|
||||
struct cooling_spec *c)
|
||||
{
|
||||
struct acpi_thermal_trip *acpi_trip = trip->priv;
|
||||
struct acpi_thermal_bind_data *bd = arg;
|
||||
struct thermal_zone_device *thermal = bd->thermal;
|
||||
struct thermal_cooling_device *cdev = bd->cdev;
|
||||
struct acpi_device *cdev_adev = cdev->devdata;
|
||||
int i;
|
||||
|
||||
/* Skip critical and hot trips. */
|
||||
if (!acpi_trip)
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
for (i = 0; i < acpi_trip->devices.count; i++) {
|
||||
acpi_handle handle = acpi_trip->devices.handles[i];
|
||||
struct acpi_device *adev = acpi_fetch_acpi_dev(handle);
|
||||
|
||||
if (adev != cdev_adev)
|
||||
continue;
|
||||
|
||||
if (bd->bind) {
|
||||
int ret;
|
||||
|
||||
ret = thermal_bind_cdev_to_trip(thermal, trip, cdev,
|
||||
THERMAL_NO_LIMIT,
|
||||
THERMAL_NO_LIMIT,
|
||||
THERMAL_WEIGHT_DEFAULT);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
thermal_unbind_cdev_from_trip(thermal, trip, cdev);
|
||||
}
|
||||
if (acpi_fetch_acpi_dev(handle) == cdev_adev)
|
||||
return true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acpi_thermal_bind_unbind_cdev(struct thermal_zone_device *thermal,
|
||||
struct thermal_cooling_device *cdev,
|
||||
bool bind)
|
||||
{
|
||||
struct acpi_thermal_bind_data bd = {
|
||||
.thermal = thermal, .cdev = cdev, .bind = bind
|
||||
};
|
||||
|
||||
return for_each_thermal_trip(thermal, bind_unbind_cdev_cb, &bd);
|
||||
}
|
||||
|
||||
static int
|
||||
acpi_thermal_bind_cooling_device(struct thermal_zone_device *thermal,
|
||||
struct thermal_cooling_device *cdev)
|
||||
{
|
||||
return acpi_thermal_bind_unbind_cdev(thermal, cdev, true);
|
||||
}
|
||||
|
||||
static int
|
||||
acpi_thermal_unbind_cooling_device(struct thermal_zone_device *thermal,
|
||||
struct thermal_cooling_device *cdev)
|
||||
{
|
||||
return acpi_thermal_bind_unbind_cdev(thermal, cdev, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct thermal_zone_device_ops acpi_thermal_zone_ops = {
|
||||
.bind = acpi_thermal_bind_cooling_device,
|
||||
.unbind = acpi_thermal_unbind_cooling_device,
|
||||
.should_bind = acpi_thermal_should_bind_cdev,
|
||||
.get_temp = thermal_get_temp,
|
||||
.get_trend = thermal_get_trend,
|
||||
.hot = acpi_thermal_zone_device_hot,
|
||||
|
@ -165,52 +165,22 @@ static int mlxsw_get_cooling_device_idx(struct mlxsw_thermal *thermal,
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int mlxsw_thermal_bind(struct thermal_zone_device *tzdev,
|
||||
struct thermal_cooling_device *cdev)
|
||||
static bool mlxsw_thermal_should_bind(struct thermal_zone_device *tzdev,
|
||||
const struct thermal_trip *trip,
|
||||
struct thermal_cooling_device *cdev,
|
||||
struct cooling_spec *c)
|
||||
{
|
||||
struct mlxsw_thermal *thermal = thermal_zone_device_priv(tzdev);
|
||||
struct device *dev = thermal->bus_info->dev;
|
||||
int i, err;
|
||||
const struct mlxsw_cooling_states *state = trip->priv;
|
||||
|
||||
/* If the cooling device is one of ours bind it */
|
||||
if (mlxsw_get_cooling_device_idx(thermal, cdev) < 0)
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
for (i = 0; i < MLXSW_THERMAL_NUM_TRIPS; i++) {
|
||||
const struct mlxsw_cooling_states *state = &thermal->cooling_states[i];
|
||||
c->upper = state->max_state;
|
||||
c->lower = state->min_state;
|
||||
|
||||
err = thermal_zone_bind_cooling_device(tzdev, i, cdev,
|
||||
state->max_state,
|
||||
state->min_state,
|
||||
THERMAL_WEIGHT_DEFAULT);
|
||||
if (err < 0) {
|
||||
dev_err(dev, "Failed to bind cooling device to trip %d\n", i);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mlxsw_thermal_unbind(struct thermal_zone_device *tzdev,
|
||||
struct thermal_cooling_device *cdev)
|
||||
{
|
||||
struct mlxsw_thermal *thermal = thermal_zone_device_priv(tzdev);
|
||||
struct device *dev = thermal->bus_info->dev;
|
||||
int i;
|
||||
int err;
|
||||
|
||||
/* If the cooling device is our one unbind it */
|
||||
if (mlxsw_get_cooling_device_idx(thermal, cdev) < 0)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < MLXSW_THERMAL_NUM_TRIPS; i++) {
|
||||
err = thermal_zone_unbind_cooling_device(tzdev, i, cdev);
|
||||
if (err < 0) {
|
||||
dev_err(dev, "Failed to unbind cooling device\n");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int mlxsw_thermal_get_temp(struct thermal_zone_device *tzdev,
|
||||
@ -240,57 +210,27 @@ static struct thermal_zone_params mlxsw_thermal_params = {
|
||||
};
|
||||
|
||||
static struct thermal_zone_device_ops mlxsw_thermal_ops = {
|
||||
.bind = mlxsw_thermal_bind,
|
||||
.unbind = mlxsw_thermal_unbind,
|
||||
.should_bind = mlxsw_thermal_should_bind,
|
||||
.get_temp = mlxsw_thermal_get_temp,
|
||||
};
|
||||
|
||||
static int mlxsw_thermal_module_bind(struct thermal_zone_device *tzdev,
|
||||
struct thermal_cooling_device *cdev)
|
||||
static bool mlxsw_thermal_module_should_bind(struct thermal_zone_device *tzdev,
|
||||
const struct thermal_trip *trip,
|
||||
struct thermal_cooling_device *cdev,
|
||||
struct cooling_spec *c)
|
||||
{
|
||||
struct mlxsw_thermal_module *tz = thermal_zone_device_priv(tzdev);
|
||||
const struct mlxsw_cooling_states *state = trip->priv;
|
||||
struct mlxsw_thermal *thermal = tz->parent;
|
||||
int i, j, err;
|
||||
|
||||
/* If the cooling device is one of ours bind it */
|
||||
if (mlxsw_get_cooling_device_idx(thermal, cdev) < 0)
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
for (i = 0; i < MLXSW_THERMAL_NUM_TRIPS; i++) {
|
||||
const struct mlxsw_cooling_states *state = &tz->cooling_states[i];
|
||||
c->upper = state->max_state;
|
||||
c->lower = state->min_state;
|
||||
|
||||
err = thermal_zone_bind_cooling_device(tzdev, i, cdev,
|
||||
state->max_state,
|
||||
state->min_state,
|
||||
THERMAL_WEIGHT_DEFAULT);
|
||||
if (err < 0)
|
||||
goto err_thermal_zone_bind_cooling_device;
|
||||
}
|
||||
return 0;
|
||||
|
||||
err_thermal_zone_bind_cooling_device:
|
||||
for (j = i - 1; j >= 0; j--)
|
||||
thermal_zone_unbind_cooling_device(tzdev, j, cdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mlxsw_thermal_module_unbind(struct thermal_zone_device *tzdev,
|
||||
struct thermal_cooling_device *cdev)
|
||||
{
|
||||
struct mlxsw_thermal_module *tz = thermal_zone_device_priv(tzdev);
|
||||
struct mlxsw_thermal *thermal = tz->parent;
|
||||
int i;
|
||||
int err;
|
||||
|
||||
/* If the cooling device is one of ours unbind it */
|
||||
if (mlxsw_get_cooling_device_idx(thermal, cdev) < 0)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < MLXSW_THERMAL_NUM_TRIPS; i++) {
|
||||
err = thermal_zone_unbind_cooling_device(tzdev, i, cdev);
|
||||
WARN_ON(err);
|
||||
}
|
||||
return err;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int mlxsw_thermal_module_temp_get(struct thermal_zone_device *tzdev,
|
||||
@ -313,8 +253,7 @@ static int mlxsw_thermal_module_temp_get(struct thermal_zone_device *tzdev,
|
||||
}
|
||||
|
||||
static struct thermal_zone_device_ops mlxsw_thermal_module_ops = {
|
||||
.bind = mlxsw_thermal_module_bind,
|
||||
.unbind = mlxsw_thermal_module_unbind,
|
||||
.should_bind = mlxsw_thermal_module_should_bind,
|
||||
.get_temp = mlxsw_thermal_module_temp_get,
|
||||
};
|
||||
|
||||
@ -342,8 +281,7 @@ static int mlxsw_thermal_gearbox_temp_get(struct thermal_zone_device *tzdev,
|
||||
}
|
||||
|
||||
static struct thermal_zone_device_ops mlxsw_thermal_gearbox_ops = {
|
||||
.bind = mlxsw_thermal_module_bind,
|
||||
.unbind = mlxsw_thermal_module_unbind,
|
||||
.should_bind = mlxsw_thermal_module_should_bind,
|
||||
.get_temp = mlxsw_thermal_gearbox_temp_get,
|
||||
};
|
||||
|
||||
@ -451,6 +389,7 @@ mlxsw_thermal_module_init(struct device *dev, struct mlxsw_core *core,
|
||||
struct mlxsw_thermal_area *area, u8 module)
|
||||
{
|
||||
struct mlxsw_thermal_module *module_tz;
|
||||
int i;
|
||||
|
||||
module_tz = &area->tz_module_arr[module];
|
||||
/* Skip if parent is already set (case of port split). */
|
||||
@ -465,6 +404,8 @@ mlxsw_thermal_module_init(struct device *dev, struct mlxsw_core *core,
|
||||
sizeof(thermal->trips));
|
||||
memcpy(module_tz->cooling_states, default_cooling_states,
|
||||
sizeof(thermal->cooling_states));
|
||||
for (i = 0; i < MLXSW_THERMAL_NUM_TRIPS; i++)
|
||||
module_tz->trips[i].priv = &module_tz->cooling_states[i];
|
||||
}
|
||||
|
||||
static void mlxsw_thermal_module_fini(struct mlxsw_thermal_module *module_tz)
|
||||
@ -579,7 +520,7 @@ mlxsw_thermal_gearboxes_init(struct device *dev, struct mlxsw_core *core,
|
||||
struct mlxsw_thermal_module *gearbox_tz;
|
||||
char mgpir_pl[MLXSW_REG_MGPIR_LEN];
|
||||
u8 gbox_num;
|
||||
int i;
|
||||
int i, j;
|
||||
int err;
|
||||
|
||||
mlxsw_reg_mgpir_pack(mgpir_pl, area->slot_index);
|
||||
@ -606,6 +547,9 @@ mlxsw_thermal_gearboxes_init(struct device *dev, struct mlxsw_core *core,
|
||||
sizeof(thermal->trips));
|
||||
memcpy(gearbox_tz->cooling_states, default_cooling_states,
|
||||
sizeof(thermal->cooling_states));
|
||||
for (j = 0; j < MLXSW_THERMAL_NUM_TRIPS; j++)
|
||||
gearbox_tz->trips[j].priv = &gearbox_tz->cooling_states[j];
|
||||
|
||||
gearbox_tz->module = i;
|
||||
gearbox_tz->parent = thermal;
|
||||
gearbox_tz->slot_index = area->slot_index;
|
||||
@ -722,6 +666,9 @@ int mlxsw_thermal_init(struct mlxsw_core *core,
|
||||
thermal->bus_info = bus_info;
|
||||
memcpy(thermal->trips, default_thermal_trips, sizeof(thermal->trips));
|
||||
memcpy(thermal->cooling_states, default_cooling_states, sizeof(thermal->cooling_states));
|
||||
for (i = 0; i < MLXSW_THERMAL_NUM_TRIPS; i++)
|
||||
thermal->trips[i].priv = &thermal->cooling_states[i];
|
||||
|
||||
thermal->line_cards[0].slot_index = 0;
|
||||
|
||||
err = mlxsw_reg_query(thermal->core, MLXSW_REG(mfcr), mfcr_pl);
|
||||
|
@ -378,33 +378,13 @@ static int acerhdf_get_ec_temp(struct thermal_zone_device *thermal, int *t)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acerhdf_bind(struct thermal_zone_device *thermal,
|
||||
struct thermal_cooling_device *cdev)
|
||||
static bool acerhdf_should_bind(struct thermal_zone_device *thermal,
|
||||
const struct thermal_trip *trip,
|
||||
struct thermal_cooling_device *cdev,
|
||||
struct cooling_spec *c)
|
||||
{
|
||||
/* if the cooling device is the one from acerhdf bind it */
|
||||
if (cdev != cl_dev)
|
||||
return 0;
|
||||
|
||||
if (thermal_zone_bind_cooling_device(thermal, 0, cdev,
|
||||
THERMAL_NO_LIMIT, THERMAL_NO_LIMIT,
|
||||
THERMAL_WEIGHT_DEFAULT)) {
|
||||
pr_err("error binding cooling dev\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acerhdf_unbind(struct thermal_zone_device *thermal,
|
||||
struct thermal_cooling_device *cdev)
|
||||
{
|
||||
if (cdev != cl_dev)
|
||||
return 0;
|
||||
|
||||
if (thermal_zone_unbind_cooling_device(thermal, 0, cdev)) {
|
||||
pr_err("error unbinding cooling dev\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
return cdev == cl_dev && trip->type == THERMAL_TRIP_ACTIVE;
|
||||
}
|
||||
|
||||
static inline void acerhdf_revert_to_bios_mode(void)
|
||||
@ -447,8 +427,7 @@ static int acerhdf_get_crit_temp(struct thermal_zone_device *thermal,
|
||||
|
||||
/* bind callback functions to thermalzone */
|
||||
static struct thermal_zone_device_ops acerhdf_dev_ops = {
|
||||
.bind = acerhdf_bind,
|
||||
.unbind = acerhdf_unbind,
|
||||
.should_bind = acerhdf_should_bind,
|
||||
.get_temp = acerhdf_get_ec_temp,
|
||||
.change_mode = acerhdf_change_mode,
|
||||
.get_crit_temp = acerhdf_get_crit_temp,
|
||||
|
@ -40,6 +40,15 @@ config THERMAL_DEBUGFS
|
||||
Say Y to allow the thermal subsystem to collect diagnostic
|
||||
information that can be accessed via debugfs.
|
||||
|
||||
config THERMAL_CORE_TESTING
|
||||
tristate "Thermal core testing facility"
|
||||
depends on DEBUG_FS
|
||||
help
|
||||
Say Y to add a debugfs-based thermal core testing facility.
|
||||
It allows test thermal zones to be created and populated
|
||||
with trip points in order to exercise the thermal core
|
||||
functionality in a controlled way.
|
||||
|
||||
config THERMAL_EMERGENCY_POWEROFF_DELAY_MS
|
||||
int "Emergency poweroff delay in milli-seconds"
|
||||
default 0
|
||||
|
@ -63,3 +63,4 @@ obj-$(CONFIG_AMLOGIC_THERMAL) += amlogic_thermal.o
|
||||
obj-$(CONFIG_SPRD_THERMAL) += sprd_thermal.o
|
||||
obj-$(CONFIG_KHADAS_MCU_FAN_THERMAL) += khadas_mcu_fan.o
|
||||
obj-$(CONFIG_LOONGSON2_THERMAL) += loongson2_thermal.o
|
||||
obj-$(CONFIG_THERMAL_CORE_TESTING) += testing/
|
||||
|
@ -208,8 +208,7 @@ static int bcm2835_thermal_probe(struct platform_device *pdev)
|
||||
*/
|
||||
val = readl(data->regs + BCM2835_TS_TSENSCTL);
|
||||
if (!(val & BCM2835_TS_TSENSCTL_RSTB)) {
|
||||
struct thermal_trip trip;
|
||||
int offset, slope;
|
||||
int offset, slope, crit_temp;
|
||||
|
||||
slope = thermal_zone_get_slope(tz);
|
||||
offset = thermal_zone_get_offset(tz);
|
||||
@ -217,7 +216,7 @@ static int bcm2835_thermal_probe(struct platform_device *pdev)
|
||||
* For now we deal only with critical, otherwise
|
||||
* would need to iterate
|
||||
*/
|
||||
err = thermal_zone_get_trip(tz, 0, &trip);
|
||||
err = thermal_zone_get_crit_temp(tz, &crit_temp);
|
||||
if (err < 0) {
|
||||
dev_err(dev, "Not able to read trip_temp: %d\n", err);
|
||||
return err;
|
||||
@ -232,7 +231,7 @@ static int bcm2835_thermal_probe(struct platform_device *pdev)
|
||||
val |= (0xFE << BCM2835_TS_TSENSCTL_RSTDELAY_SHIFT);
|
||||
|
||||
/* trip_adc value from info */
|
||||
val |= bcm2835_thermal_temp2adc(trip.temperature,
|
||||
val |= bcm2835_thermal_temp2adc(crit_temp,
|
||||
offset,
|
||||
slope)
|
||||
<< BCM2835_TS_TSENSCTL_THOLD_SHIFT;
|
||||
|
@ -465,11 +465,22 @@ static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int hisi_trip_walk_cb(struct thermal_trip *trip, void *arg)
|
||||
{
|
||||
struct hisi_thermal_sensor *sensor = arg;
|
||||
|
||||
if (trip->type != THERMAL_TRIP_PASSIVE)
|
||||
return 0;
|
||||
|
||||
sensor->thres_temp = trip->temperature;
|
||||
/* Return nonzero to terminate the search. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int hisi_thermal_register_sensor(struct platform_device *pdev,
|
||||
struct hisi_thermal_sensor *sensor)
|
||||
{
|
||||
int ret, i;
|
||||
struct thermal_trip trip;
|
||||
int ret;
|
||||
|
||||
sensor->tzd = devm_thermal_of_zone_register(&pdev->dev,
|
||||
sensor->id, sensor,
|
||||
@ -482,15 +493,7 @@ static int hisi_thermal_register_sensor(struct platform_device *pdev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < thermal_zone_get_num_trips(sensor->tzd); i++) {
|
||||
|
||||
thermal_zone_get_trip(sensor->tzd, i, &trip);
|
||||
|
||||
if (trip.type == THERMAL_TRIP_PASSIVE) {
|
||||
sensor->thres_temp = trip.temperature;
|
||||
break;
|
||||
}
|
||||
}
|
||||
thermal_zone_for_each_trip(sensor->tzd, hisi_trip_walk_cb, sensor);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -353,24 +353,16 @@ static int imx_set_trip_temp(struct thermal_zone_device *tz,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_bind(struct thermal_zone_device *tz,
|
||||
struct thermal_cooling_device *cdev)
|
||||
static bool imx_should_bind(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip,
|
||||
struct thermal_cooling_device *cdev,
|
||||
struct cooling_spec *c)
|
||||
{
|
||||
return thermal_zone_bind_cooling_device(tz, IMX_TRIP_PASSIVE, cdev,
|
||||
THERMAL_NO_LIMIT,
|
||||
THERMAL_NO_LIMIT,
|
||||
THERMAL_WEIGHT_DEFAULT);
|
||||
}
|
||||
|
||||
static int imx_unbind(struct thermal_zone_device *tz,
|
||||
struct thermal_cooling_device *cdev)
|
||||
{
|
||||
return thermal_zone_unbind_cooling_device(tz, IMX_TRIP_PASSIVE, cdev);
|
||||
return trip->type == THERMAL_TRIP_PASSIVE;
|
||||
}
|
||||
|
||||
static struct thermal_zone_device_ops imx_tz_ops = {
|
||||
.bind = imx_bind,
|
||||
.unbind = imx_unbind,
|
||||
.should_bind = imx_should_bind,
|
||||
.get_temp = imx_get_temp,
|
||||
.change_mode = imx_change_mode,
|
||||
.set_trip_temp = imx_set_trip_temp,
|
||||
|
@ -291,24 +291,6 @@ static irqreturn_t qpnp_tm_isr(int irq, void *data)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int qpnp_tm_get_critical_trip_temp(struct qpnp_tm_chip *chip)
|
||||
{
|
||||
struct thermal_trip trip;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < thermal_zone_get_num_trips(chip->tz_dev); i++) {
|
||||
|
||||
ret = thermal_zone_get_trip(chip->tz_dev, i, &trip);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
if (trip.type == THERMAL_TRIP_CRITICAL)
|
||||
return trip.temperature;
|
||||
}
|
||||
|
||||
return THERMAL_TEMP_INVALID;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function initializes the internal temp value based on only the
|
||||
* current thermal stage and threshold. Setup threshold control and
|
||||
@ -343,7 +325,9 @@ static int qpnp_tm_init(struct qpnp_tm_chip *chip)
|
||||
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
crit_temp = qpnp_tm_get_critical_trip_temp(chip);
|
||||
ret = thermal_zone_get_crit_temp(chip->tz_dev, &crit_temp);
|
||||
if (ret)
|
||||
crit_temp = THERMAL_TEMP_INVALID;
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
|
||||
|
@ -563,11 +563,7 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto error_unregister;
|
||||
|
||||
ret = thermal_zone_get_num_trips(tsc->zone);
|
||||
if (ret < 0)
|
||||
goto error_unregister;
|
||||
|
||||
dev_info(dev, "Sensor %u: Loaded %d trip points\n", i, ret);
|
||||
dev_info(dev, "Sensor %u: Loaded\n", i);
|
||||
}
|
||||
|
||||
if (!priv->num_tscs) {
|
||||
|
@ -682,24 +682,25 @@ static const struct thermal_zone_device_ops tegra_of_thermal_ops = {
|
||||
.set_trips = tegra_thermctl_set_trips,
|
||||
};
|
||||
|
||||
static int get_hot_temp(struct thermal_zone_device *tz, int *trip_id, int *temp)
|
||||
static int get_hot_trip_cb(struct thermal_trip *trip, void *arg)
|
||||
{
|
||||
int i, ret;
|
||||
struct thermal_trip trip;
|
||||
const struct thermal_trip **trip_ret = arg;
|
||||
|
||||
for (i = 0; i < thermal_zone_get_num_trips(tz); i++) {
|
||||
if (trip->type != THERMAL_TRIP_HOT)
|
||||
return 0;
|
||||
|
||||
ret = thermal_zone_get_trip(tz, i, &trip);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
*trip_ret = trip;
|
||||
/* Return nonzero to terminate the search. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (trip.type == THERMAL_TRIP_HOT) {
|
||||
*trip_id = i;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
static const struct thermal_trip *get_hot_trip(struct thermal_zone_device *tz)
|
||||
{
|
||||
const struct thermal_trip *trip = NULL;
|
||||
|
||||
return -EINVAL;
|
||||
thermal_zone_for_each_trip(tz, get_hot_trip_cb, &trip);
|
||||
|
||||
return trip;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -731,8 +732,9 @@ static int tegra_soctherm_set_hwtrips(struct device *dev,
|
||||
struct thermal_zone_device *tz)
|
||||
{
|
||||
struct tegra_soctherm *ts = dev_get_drvdata(dev);
|
||||
const struct thermal_trip *hot_trip;
|
||||
struct soctherm_throt_cfg *stc;
|
||||
int i, trip, temperature, ret;
|
||||
int i, temperature, ret;
|
||||
|
||||
/* Get thermtrips. If missing, try to get critical trips. */
|
||||
temperature = tsensor_group_thermtrip_get(ts, sg->id);
|
||||
@ -749,8 +751,8 @@ static int tegra_soctherm_set_hwtrips(struct device *dev,
|
||||
dev_info(dev, "thermtrip: will shut down when %s reaches %d mC\n",
|
||||
sg->name, temperature);
|
||||
|
||||
ret = get_hot_temp(tz, &trip, &temperature);
|
||||
if (ret) {
|
||||
hot_trip = get_hot_trip(tz);
|
||||
if (!hot_trip) {
|
||||
dev_info(dev, "throttrip: %s: missing hot temperature\n",
|
||||
sg->name);
|
||||
return 0;
|
||||
@ -763,7 +765,7 @@ static int tegra_soctherm_set_hwtrips(struct device *dev,
|
||||
continue;
|
||||
|
||||
cdev = ts->throt_cfgs[i].cdev;
|
||||
if (get_thermal_instance(tz, cdev, trip))
|
||||
if (thermal_trip_is_bound_to_cdev(tz, hot_trip, cdev))
|
||||
stc = find_throttle_cfg_by_name(ts, cdev->type);
|
||||
else
|
||||
continue;
|
||||
|
@ -303,33 +303,37 @@ stop_channel:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tegra_tsensor_get_hw_channel_trips(struct thermal_zone_device *tzd,
|
||||
int *hot_trip, int *crit_trip)
|
||||
{
|
||||
unsigned int i;
|
||||
struct trip_temps {
|
||||
int hot_trip;
|
||||
int crit_trip;
|
||||
};
|
||||
|
||||
static int tegra_tsensor_get_trips_cb(struct thermal_trip *trip, void *arg)
|
||||
{
|
||||
struct trip_temps *temps = arg;
|
||||
|
||||
if (trip->type == THERMAL_TRIP_HOT)
|
||||
temps->hot_trip = trip->temperature;
|
||||
else if (trip->type == THERMAL_TRIP_CRITICAL)
|
||||
temps->crit_trip = trip->temperature;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tegra_tsensor_get_hw_channel_trips(struct thermal_zone_device *tzd,
|
||||
struct trip_temps *temps)
|
||||
{
|
||||
/*
|
||||
* 90C is the maximal critical temperature of all Tegra30 SoC variants,
|
||||
* use it for the default trip if unspecified in a device-tree.
|
||||
*/
|
||||
*hot_trip = 85000;
|
||||
*crit_trip = 90000;
|
||||
temps->hot_trip = 85000;
|
||||
temps->crit_trip = 90000;
|
||||
|
||||
for (i = 0; i < thermal_zone_get_num_trips(tzd); i++) {
|
||||
|
||||
struct thermal_trip trip;
|
||||
|
||||
thermal_zone_get_trip(tzd, i, &trip);
|
||||
|
||||
if (trip.type == THERMAL_TRIP_HOT)
|
||||
*hot_trip = trip.temperature;
|
||||
|
||||
if (trip.type == THERMAL_TRIP_CRITICAL)
|
||||
*crit_trip = trip.temperature;
|
||||
}
|
||||
thermal_zone_for_each_trip(tzd, tegra_tsensor_get_trips_cb, temps);
|
||||
|
||||
/* clamp hardware trips to the calibration limits */
|
||||
*hot_trip = clamp(*hot_trip, 25000, 90000);
|
||||
temps->hot_trip = clamp(temps->hot_trip, 25000, 90000);
|
||||
|
||||
/*
|
||||
* Kernel will perform a normal system shut down if it will
|
||||
@ -338,7 +342,7 @@ static void tegra_tsensor_get_hw_channel_trips(struct thermal_zone_device *tzd,
|
||||
* shut down gracefully before sending signal to the Power
|
||||
* Management controller.
|
||||
*/
|
||||
*crit_trip = clamp(*crit_trip + 5000, 25000, 90000);
|
||||
temps->crit_trip = clamp(temps->crit_trip + 5000, 25000, 90000);
|
||||
}
|
||||
|
||||
static int tegra_tsensor_enable_hw_channel(const struct tegra_tsensor *ts,
|
||||
@ -346,7 +350,8 @@ static int tegra_tsensor_enable_hw_channel(const struct tegra_tsensor *ts,
|
||||
{
|
||||
const struct tegra_tsensor_channel *tsc = &ts->ch[id];
|
||||
struct thermal_zone_device *tzd = tsc->tzd;
|
||||
int err, hot_trip = 0, crit_trip = 0;
|
||||
struct trip_temps temps = { 0 };
|
||||
int err;
|
||||
u32 val;
|
||||
|
||||
if (!tzd) {
|
||||
@ -357,24 +362,24 @@ static int tegra_tsensor_enable_hw_channel(const struct tegra_tsensor *ts,
|
||||
return 0;
|
||||
}
|
||||
|
||||
tegra_tsensor_get_hw_channel_trips(tzd, &hot_trip, &crit_trip);
|
||||
tegra_tsensor_get_hw_channel_trips(tzd, &temps);
|
||||
|
||||
dev_info_once(ts->dev, "ch%u: PMC emergency shutdown trip set to %dC\n",
|
||||
id, DIV_ROUND_CLOSEST(crit_trip, 1000));
|
||||
id, DIV_ROUND_CLOSEST(temps.crit_trip, 1000));
|
||||
|
||||
hot_trip = tegra_tsensor_temp_to_counter(ts, hot_trip);
|
||||
crit_trip = tegra_tsensor_temp_to_counter(ts, crit_trip);
|
||||
temps.hot_trip = tegra_tsensor_temp_to_counter(ts, temps.hot_trip);
|
||||
temps.crit_trip = tegra_tsensor_temp_to_counter(ts, temps.crit_trip);
|
||||
|
||||
/* program LEVEL2 counter threshold */
|
||||
val = readl_relaxed(tsc->regs + TSENSOR_SENSOR0_CONFIG1);
|
||||
val &= ~TSENSOR_SENSOR0_CONFIG1_TH2;
|
||||
val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG1_TH2, hot_trip);
|
||||
val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG1_TH2, temps.hot_trip);
|
||||
writel_relaxed(val, tsc->regs + TSENSOR_SENSOR0_CONFIG1);
|
||||
|
||||
/* program LEVEL3 counter threshold */
|
||||
val = readl_relaxed(tsc->regs + TSENSOR_SENSOR0_CONFIG2);
|
||||
val &= ~TSENSOR_SENSOR0_CONFIG2_TH3;
|
||||
val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG2_TH3, crit_trip);
|
||||
val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG2_TH3, temps.crit_trip);
|
||||
writel_relaxed(val, tsc->regs + TSENSOR_SENSOR0_CONFIG2);
|
||||
|
||||
/*
|
||||
|
7
drivers/thermal/testing/Makefile
Normal file
7
drivers/thermal/testing/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Thermal core testing facility.
|
||||
|
||||
obj-$(CONFIG_THERMAL_CORE_TESTING) += thermal-testing.o
|
||||
|
||||
thermal-testing-y := command.o zone.o
|
221
drivers/thermal/testing/command.c
Normal file
221
drivers/thermal/testing/command.c
Normal file
@ -0,0 +1,221 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2024, Intel Corporation
|
||||
*
|
||||
* Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
||||
*
|
||||
* Thermal subsystem testing facility.
|
||||
*
|
||||
* This facility allows the thermal core functionality to be exercised in a
|
||||
* controlled way in order to verify its behavior.
|
||||
*
|
||||
* It resides in the "thermal-testing" directory under the debugfs root and
|
||||
* starts with a single file called "command" which can be written a string
|
||||
* representing a thermal testing facility command.
|
||||
*
|
||||
* The currently supported commands are listed in the tt_commands enum below.
|
||||
*
|
||||
* The "addtz" command causes a new test thermal zone template to be created,
|
||||
* for example:
|
||||
*
|
||||
* # echo addtz > /sys/kernel/debug/thermal-testing/command
|
||||
*
|
||||
* That template will be represented as a subdirectory in the "thermal-testing"
|
||||
* directory, for example
|
||||
*
|
||||
* # ls /sys/kernel/debug/thermal-testing/
|
||||
* command tz0
|
||||
*
|
||||
* The thermal zone template can be populated with trip points with the help of
|
||||
* the "tzaddtrip" command, for example:
|
||||
*
|
||||
* # echo tzaddtrip:0 > /sys/kernel/debug/thermal-testing/command
|
||||
*
|
||||
* which causes a trip point template to be added to the test thermal zone
|
||||
* template 0 (represented by the tz0 subdirectory in "thermal-testing").
|
||||
*
|
||||
* # ls /sys/kernel/debug/thermal-testing/tz0
|
||||
* init_temp temp trip_0_temp trip_0_hyst
|
||||
*
|
||||
* The temperature of a trip point template is initially THERMAL_TEMP_INVALID
|
||||
* and its hysteresis is initially 0. They can be adjusted by writing to the
|
||||
* "trip_x_temp" and "trip_x_hyst" files correspoinding to that trip point
|
||||
* template, respectively.
|
||||
*
|
||||
* The initial temperature of a thermal zone based on a template can be set by
|
||||
* writing to the "init_temp" file in its directory under "thermal-testing", for
|
||||
* example:
|
||||
*
|
||||
* echo 50000 > /sys/kernel/debug/thermal-testing/tz0/init_temp
|
||||
*
|
||||
* When ready, "tzreg" command can be used for registering and enabling a
|
||||
* thermal zone based on a given template with the thermal core, for example
|
||||
*
|
||||
* # echo tzreg:0 > /sys/kernel/debug/thermal-testing/command
|
||||
*
|
||||
* In this case, test thermal zone template 0 is used for registering a new
|
||||
* thermal zone and the set of trip point templates associated with it is used
|
||||
* for populating the new thermal zone's trip points table. The type of the new
|
||||
* thermal zone is "test_tz".
|
||||
*
|
||||
* The temperature and hysteresis of all of the trip points in that new thermal
|
||||
* zone are adjustable via sysfs, so they can be updated at any time.
|
||||
*
|
||||
* The current temperature of the new thermal zone can be set by writing to the
|
||||
* "temp" file in the corresponding thermal zone template's directory under
|
||||
* "thermal-testing", for example
|
||||
*
|
||||
* echo 10000 > /sys/kernel/debug/thermal-testing/tz0/temp
|
||||
*
|
||||
* which will also trigger a temperature update for this zone in the thermal
|
||||
* core, including checking its trip points, sending notifications to user space
|
||||
* if any of them have been crossed and so on.
|
||||
*
|
||||
* When it is not needed any more, a test thermal zone template can be deleted
|
||||
* with the help of the "deltz" command, for example
|
||||
*
|
||||
* # echo deltz:0 > /sys/kernel/debug/thermal-testing/command
|
||||
*
|
||||
* which will also unregister the thermal zone based on it, if present.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "thermal-testing: " fmt
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "thermal_testing.h"
|
||||
|
||||
struct dentry *d_testing;
|
||||
|
||||
#define TT_COMMAND_SIZE 16
|
||||
|
||||
enum tt_commands {
|
||||
TT_CMD_ADDTZ,
|
||||
TT_CMD_DELTZ,
|
||||
TT_CMD_TZADDTRIP,
|
||||
TT_CMD_TZREG,
|
||||
TT_CMD_TZUNREG,
|
||||
};
|
||||
|
||||
static const char *tt_command_strings[] = {
|
||||
[TT_CMD_ADDTZ] = "addtz",
|
||||
[TT_CMD_DELTZ] = "deltz",
|
||||
[TT_CMD_TZADDTRIP] = "tzaddtrip",
|
||||
[TT_CMD_TZREG] = "tzreg",
|
||||
[TT_CMD_TZUNREG] = "tzunreg",
|
||||
};
|
||||
|
||||
static int tt_command_exec(int index, const char *arg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (index) {
|
||||
case TT_CMD_ADDTZ:
|
||||
ret = tt_add_tz();
|
||||
break;
|
||||
|
||||
case TT_CMD_DELTZ:
|
||||
ret = tt_del_tz(arg);
|
||||
break;
|
||||
|
||||
case TT_CMD_TZADDTRIP:
|
||||
ret = tt_zone_add_trip(arg);
|
||||
break;
|
||||
|
||||
case TT_CMD_TZREG:
|
||||
ret = tt_zone_reg(arg);
|
||||
break;
|
||||
|
||||
case TT_CMD_TZUNREG:
|
||||
ret = tt_zone_unreg(arg);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t tt_command_process(struct dentry *dentry, const char __user *user_buf,
|
||||
size_t count)
|
||||
{
|
||||
char *buf __free(kfree);
|
||||
char *arg;
|
||||
int i;
|
||||
|
||||
buf = kmalloc(count + 1, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(buf, user_buf, count))
|
||||
return -EFAULT;
|
||||
|
||||
buf[count] = '\0';
|
||||
strim(buf);
|
||||
|
||||
arg = strstr(buf, ":");
|
||||
if (arg) {
|
||||
*arg = '\0';
|
||||
arg++;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tt_command_strings); i++) {
|
||||
if (!strcmp(buf, tt_command_strings[i]))
|
||||
return tt_command_exec(i, arg);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static ssize_t tt_command_write(struct file *file, const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct dentry *dentry = file->f_path.dentry;
|
||||
ssize_t ret;
|
||||
|
||||
if (*ppos)
|
||||
return -EINVAL;
|
||||
|
||||
if (count + 1 > TT_COMMAND_SIZE)
|
||||
return -E2BIG;
|
||||
|
||||
ret = debugfs_file_get(dentry);
|
||||
if (unlikely(ret))
|
||||
return ret;
|
||||
|
||||
ret = tt_command_process(dentry, user_buf, count);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations tt_command_fops = {
|
||||
.write = tt_command_write,
|
||||
.open = simple_open,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static int __init thermal_testing_init(void)
|
||||
{
|
||||
d_testing = debugfs_create_dir("thermal-testing", NULL);
|
||||
if (!IS_ERR(d_testing))
|
||||
debugfs_create_file("command", 0200, d_testing, NULL,
|
||||
&tt_command_fops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
module_init(thermal_testing_init);
|
||||
|
||||
static void __exit thermal_testing_exit(void)
|
||||
{
|
||||
debugfs_remove(d_testing);
|
||||
tt_zone_cleanup();
|
||||
}
|
||||
module_exit(thermal_testing_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Thermal core testing facility");
|
||||
MODULE_LICENSE("GPL v2");
|
11
drivers/thermal/testing/thermal_testing.h
Normal file
11
drivers/thermal/testing/thermal_testing.h
Normal file
@ -0,0 +1,11 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
extern struct dentry *d_testing;
|
||||
|
||||
int tt_add_tz(void);
|
||||
int tt_del_tz(const char *arg);
|
||||
int tt_zone_add_trip(const char *arg);
|
||||
int tt_zone_reg(const char *arg);
|
||||
int tt_zone_unreg(const char *arg);
|
||||
|
||||
void tt_zone_cleanup(void);
|
468
drivers/thermal/testing/zone.c
Normal file
468
drivers/thermal/testing/zone.c
Normal file
@ -0,0 +1,468 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2024, Intel Corporation
|
||||
*
|
||||
* Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
||||
*
|
||||
* Thermal zone tempalates handling for thermal core testing.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "thermal-testing: " fmt
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "thermal_testing.h"
|
||||
|
||||
#define TT_MAX_FILE_NAME_LENGTH 16
|
||||
|
||||
/**
|
||||
* struct tt_thermal_zone - Testing thermal zone template
|
||||
*
|
||||
* Represents a template of a thermal zone that can be used for registering
|
||||
* a test thermal zone with the thermal core.
|
||||
*
|
||||
* @list_node: Node in the list of all testing thermal zone templates.
|
||||
* @trips: List of trip point templates for this thermal zone template.
|
||||
* @d_tt_zone: Directory in debugfs representing this template.
|
||||
* @tz: Test thermal zone based on this template, if present.
|
||||
* @lock: Mutex for synchronizing changes of this template.
|
||||
* @ida: IDA for trip point IDs.
|
||||
* @id: The ID of this template for the debugfs interface.
|
||||
* @temp: Temperature value.
|
||||
* @tz_temp: Current thermal zone temperature (after registration).
|
||||
* @num_trips: Number of trip points in the @trips list.
|
||||
* @refcount: Reference counter for usage and removal synchronization.
|
||||
*/
|
||||
struct tt_thermal_zone {
|
||||
struct list_head list_node;
|
||||
struct list_head trips;
|
||||
struct dentry *d_tt_zone;
|
||||
struct thermal_zone_device *tz;
|
||||
struct mutex lock;
|
||||
struct ida ida;
|
||||
int id;
|
||||
int temp;
|
||||
int tz_temp;
|
||||
unsigned int num_trips;
|
||||
unsigned int refcount;
|
||||
};
|
||||
|
||||
DEFINE_GUARD(tt_zone, struct tt_thermal_zone *, mutex_lock(&_T->lock), mutex_unlock(&_T->lock))
|
||||
|
||||
/**
|
||||
* struct tt_trip - Testing trip point template
|
||||
*
|
||||
* Represents a template of a trip point to be used for populating a trip point
|
||||
* during the registration of a thermal zone based on a given zone template.
|
||||
*
|
||||
* @list_node: Node in the list of all trip templates in the zone template.
|
||||
* @trip: Trip point data to use for thernal zone registration.
|
||||
* @id: The ID of this trip template for the debugfs interface.
|
||||
*/
|
||||
struct tt_trip {
|
||||
struct list_head list_node;
|
||||
struct thermal_trip trip;
|
||||
int id;
|
||||
};
|
||||
|
||||
/*
|
||||
* It is both questionable and potentially problematic from the sychnronization
|
||||
* perspective to attempt to manipulate debugfs from within a debugfs file
|
||||
* "write" operation, so auxiliary work items are used for that. The majority
|
||||
* of zone-related command functions have a part that runs from a workqueue and
|
||||
* make changes in debugs, among other things.
|
||||
*/
|
||||
struct tt_work {
|
||||
struct work_struct work;
|
||||
struct tt_thermal_zone *tt_zone;
|
||||
struct tt_trip *tt_trip;
|
||||
};
|
||||
|
||||
static inline struct tt_work *tt_work_of_work(struct work_struct *work)
|
||||
{
|
||||
return container_of(work, struct tt_work, work);
|
||||
}
|
||||
|
||||
static LIST_HEAD(tt_thermal_zones);
|
||||
static DEFINE_IDA(tt_thermal_zones_ida);
|
||||
static DEFINE_MUTEX(tt_thermal_zones_lock);
|
||||
|
||||
static int tt_int_get(void *data, u64 *val)
|
||||
{
|
||||
*val = *(int *)data;
|
||||
return 0;
|
||||
}
|
||||
static int tt_int_set(void *data, u64 val)
|
||||
{
|
||||
if ((int)val < THERMAL_TEMP_INVALID)
|
||||
return -EINVAL;
|
||||
|
||||
*(int *)data = val;
|
||||
return 0;
|
||||
}
|
||||
DEFINE_DEBUGFS_ATTRIBUTE_SIGNED(tt_int_attr, tt_int_get, tt_int_set, "%lld\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(tt_unsigned_int_attr, tt_int_get, tt_int_set, "%llu\n");
|
||||
|
||||
static int tt_zone_tz_temp_get(void *data, u64 *val)
|
||||
{
|
||||
struct tt_thermal_zone *tt_zone = data;
|
||||
|
||||
guard(tt_zone)(tt_zone);
|
||||
|
||||
if (!tt_zone->tz)
|
||||
return -EBUSY;
|
||||
|
||||
*val = tt_zone->tz_temp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int tt_zone_tz_temp_set(void *data, u64 val)
|
||||
{
|
||||
struct tt_thermal_zone *tt_zone = data;
|
||||
|
||||
guard(tt_zone)(tt_zone);
|
||||
|
||||
if (!tt_zone->tz)
|
||||
return -EBUSY;
|
||||
|
||||
WRITE_ONCE(tt_zone->tz_temp, val);
|
||||
thermal_zone_device_update(tt_zone->tz, THERMAL_EVENT_TEMP_SAMPLE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_DEBUGFS_ATTRIBUTE_SIGNED(tt_zone_tz_temp_attr, tt_zone_tz_temp_get,
|
||||
tt_zone_tz_temp_set, "%lld\n");
|
||||
|
||||
static void tt_zone_free_trips(struct tt_thermal_zone *tt_zone)
|
||||
{
|
||||
struct tt_trip *tt_trip, *aux;
|
||||
|
||||
list_for_each_entry_safe(tt_trip, aux, &tt_zone->trips, list_node) {
|
||||
list_del(&tt_trip->list_node);
|
||||
ida_free(&tt_zone->ida, tt_trip->id);
|
||||
kfree(tt_trip);
|
||||
}
|
||||
}
|
||||
|
||||
static void tt_zone_free(struct tt_thermal_zone *tt_zone)
|
||||
{
|
||||
tt_zone_free_trips(tt_zone);
|
||||
ida_free(&tt_thermal_zones_ida, tt_zone->id);
|
||||
ida_destroy(&tt_zone->ida);
|
||||
kfree(tt_zone);
|
||||
}
|
||||
|
||||
static void tt_add_tz_work_fn(struct work_struct *work)
|
||||
{
|
||||
struct tt_work *tt_work = tt_work_of_work(work);
|
||||
struct tt_thermal_zone *tt_zone = tt_work->tt_zone;
|
||||
char f_name[TT_MAX_FILE_NAME_LENGTH];
|
||||
|
||||
kfree(tt_work);
|
||||
|
||||
snprintf(f_name, TT_MAX_FILE_NAME_LENGTH, "tz%d", tt_zone->id);
|
||||
tt_zone->d_tt_zone = debugfs_create_dir(f_name, d_testing);
|
||||
if (IS_ERR(tt_zone->d_tt_zone)) {
|
||||
tt_zone_free(tt_zone);
|
||||
return;
|
||||
}
|
||||
|
||||
debugfs_create_file_unsafe("temp", 0600, tt_zone->d_tt_zone, tt_zone,
|
||||
&tt_zone_tz_temp_attr);
|
||||
|
||||
debugfs_create_file_unsafe("init_temp", 0600, tt_zone->d_tt_zone,
|
||||
&tt_zone->temp, &tt_int_attr);
|
||||
|
||||
guard(mutex)(&tt_thermal_zones_lock);
|
||||
|
||||
list_add_tail(&tt_zone->list_node, &tt_thermal_zones);
|
||||
}
|
||||
|
||||
int tt_add_tz(void)
|
||||
{
|
||||
struct tt_thermal_zone *tt_zone __free(kfree);
|
||||
struct tt_work *tt_work __free(kfree);
|
||||
int ret;
|
||||
|
||||
tt_zone = kzalloc(sizeof(*tt_zone), GFP_KERNEL);
|
||||
if (!tt_zone)
|
||||
return -ENOMEM;
|
||||
|
||||
tt_work = kzalloc(sizeof(*tt_work), GFP_KERNEL);
|
||||
if (!tt_work)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&tt_zone->trips);
|
||||
mutex_init(&tt_zone->lock);
|
||||
ida_init(&tt_zone->ida);
|
||||
tt_zone->temp = THERMAL_TEMP_INVALID;
|
||||
|
||||
ret = ida_alloc(&tt_thermal_zones_ida, GFP_KERNEL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
tt_zone->id = ret;
|
||||
|
||||
INIT_WORK(&tt_work->work, tt_add_tz_work_fn);
|
||||
tt_work->tt_zone = no_free_ptr(tt_zone);
|
||||
schedule_work(&(no_free_ptr(tt_work)->work));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tt_del_tz_work_fn(struct work_struct *work)
|
||||
{
|
||||
struct tt_work *tt_work = tt_work_of_work(work);
|
||||
struct tt_thermal_zone *tt_zone = tt_work->tt_zone;
|
||||
|
||||
kfree(tt_work);
|
||||
|
||||
debugfs_remove(tt_zone->d_tt_zone);
|
||||
tt_zone_free(tt_zone);
|
||||
}
|
||||
|
||||
static void tt_zone_unregister_tz(struct tt_thermal_zone *tt_zone)
|
||||
{
|
||||
guard(tt_zone)(tt_zone);
|
||||
|
||||
if (tt_zone->tz) {
|
||||
thermal_zone_device_unregister(tt_zone->tz);
|
||||
tt_zone->tz = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int tt_del_tz(const char *arg)
|
||||
{
|
||||
struct tt_work *tt_work __free(kfree);
|
||||
struct tt_thermal_zone *tt_zone, *aux;
|
||||
int ret;
|
||||
int id;
|
||||
|
||||
ret = sscanf(arg, "%d", &id);
|
||||
if (ret != 1)
|
||||
return -EINVAL;
|
||||
|
||||
tt_work = kzalloc(sizeof(*tt_work), GFP_KERNEL);
|
||||
if (!tt_work)
|
||||
return -ENOMEM;
|
||||
|
||||
guard(mutex)(&tt_thermal_zones_lock);
|
||||
|
||||
ret = -EINVAL;
|
||||
list_for_each_entry_safe(tt_zone, aux, &tt_thermal_zones, list_node) {
|
||||
if (tt_zone->id == id) {
|
||||
if (tt_zone->refcount) {
|
||||
ret = -EBUSY;
|
||||
} else {
|
||||
list_del(&tt_zone->list_node);
|
||||
ret = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tt_zone_unregister_tz(tt_zone);
|
||||
|
||||
INIT_WORK(&tt_work->work, tt_del_tz_work_fn);
|
||||
tt_work->tt_zone = tt_zone;
|
||||
schedule_work(&(no_free_ptr(tt_work)->work));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct tt_thermal_zone *tt_get_tt_zone(const char *arg)
|
||||
{
|
||||
struct tt_thermal_zone *tt_zone;
|
||||
int ret, id;
|
||||
|
||||
ret = sscanf(arg, "%d", &id);
|
||||
if (ret != 1)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
guard(mutex)(&tt_thermal_zones_lock);
|
||||
|
||||
ret = -EINVAL;
|
||||
list_for_each_entry(tt_zone, &tt_thermal_zones, list_node) {
|
||||
if (tt_zone->id == id) {
|
||||
tt_zone->refcount++;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return tt_zone;
|
||||
}
|
||||
|
||||
static void tt_put_tt_zone(struct tt_thermal_zone *tt_zone)
|
||||
{
|
||||
guard(mutex)(&tt_thermal_zones_lock);
|
||||
|
||||
tt_zone->refcount--;
|
||||
}
|
||||
|
||||
static void tt_zone_add_trip_work_fn(struct work_struct *work)
|
||||
{
|
||||
struct tt_work *tt_work = tt_work_of_work(work);
|
||||
struct tt_thermal_zone *tt_zone = tt_work->tt_zone;
|
||||
struct tt_trip *tt_trip = tt_work->tt_trip;
|
||||
char d_name[TT_MAX_FILE_NAME_LENGTH];
|
||||
|
||||
kfree(tt_work);
|
||||
|
||||
snprintf(d_name, TT_MAX_FILE_NAME_LENGTH, "trip_%d_temp", tt_trip->id);
|
||||
debugfs_create_file_unsafe(d_name, 0600, tt_zone->d_tt_zone,
|
||||
&tt_trip->trip.temperature, &tt_int_attr);
|
||||
|
||||
snprintf(d_name, TT_MAX_FILE_NAME_LENGTH, "trip_%d_hyst", tt_trip->id);
|
||||
debugfs_create_file_unsafe(d_name, 0600, tt_zone->d_tt_zone,
|
||||
&tt_trip->trip.hysteresis, &tt_unsigned_int_attr);
|
||||
|
||||
tt_put_tt_zone(tt_zone);
|
||||
}
|
||||
|
||||
int tt_zone_add_trip(const char *arg)
|
||||
{
|
||||
struct tt_work *tt_work __free(kfree);
|
||||
struct tt_trip *tt_trip __free(kfree);
|
||||
struct tt_thermal_zone *tt_zone;
|
||||
int id;
|
||||
|
||||
tt_work = kzalloc(sizeof(*tt_work), GFP_KERNEL);
|
||||
if (!tt_work)
|
||||
return -ENOMEM;
|
||||
|
||||
tt_trip = kzalloc(sizeof(*tt_trip), GFP_KERNEL);
|
||||
if (!tt_trip)
|
||||
return -ENOMEM;
|
||||
|
||||
tt_zone = tt_get_tt_zone(arg);
|
||||
if (IS_ERR(tt_zone))
|
||||
return PTR_ERR(tt_zone);
|
||||
|
||||
id = ida_alloc(&tt_zone->ida, GFP_KERNEL);
|
||||
if (id < 0) {
|
||||
tt_put_tt_zone(tt_zone);
|
||||
return id;
|
||||
}
|
||||
|
||||
tt_trip->trip.type = THERMAL_TRIP_ACTIVE;
|
||||
tt_trip->trip.temperature = THERMAL_TEMP_INVALID;
|
||||
tt_trip->trip.flags = THERMAL_TRIP_FLAG_RW;
|
||||
tt_trip->id = id;
|
||||
|
||||
guard(tt_zone)(tt_zone);
|
||||
|
||||
list_add_tail(&tt_trip->list_node, &tt_zone->trips);
|
||||
tt_zone->num_trips++;
|
||||
|
||||
INIT_WORK(&tt_work->work, tt_zone_add_trip_work_fn);
|
||||
tt_work->tt_zone = tt_zone;
|
||||
tt_work->tt_trip = no_free_ptr(tt_trip);
|
||||
schedule_work(&(no_free_ptr(tt_work)->work));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tt_zone_get_temp(struct thermal_zone_device *tz, int *temp)
|
||||
{
|
||||
struct tt_thermal_zone *tt_zone = thermal_zone_device_priv(tz);
|
||||
|
||||
*temp = READ_ONCE(tt_zone->tz_temp);
|
||||
|
||||
if (*temp < THERMAL_TEMP_INVALID)
|
||||
return -ENODATA;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct thermal_zone_device_ops tt_zone_ops = {
|
||||
.get_temp = tt_zone_get_temp,
|
||||
};
|
||||
|
||||
static int tt_zone_register_tz(struct tt_thermal_zone *tt_zone)
|
||||
{
|
||||
struct thermal_trip *trips __free(kfree);
|
||||
struct thermal_zone_device *tz;
|
||||
struct tt_trip *tt_trip;
|
||||
int i;
|
||||
|
||||
guard(tt_zone)(tt_zone);
|
||||
|
||||
if (tt_zone->tz)
|
||||
return -EINVAL;
|
||||
|
||||
trips = kcalloc(tt_zone->num_trips, sizeof(*trips), GFP_KERNEL);
|
||||
if (!trips)
|
||||
return -ENOMEM;
|
||||
|
||||
i = 0;
|
||||
list_for_each_entry(tt_trip, &tt_zone->trips, list_node)
|
||||
trips[i++] = tt_trip->trip;
|
||||
|
||||
tt_zone->tz_temp = tt_zone->temp;
|
||||
|
||||
tz = thermal_zone_device_register_with_trips("test_tz", trips, i, tt_zone,
|
||||
&tt_zone_ops, NULL, 0, 0);
|
||||
if (IS_ERR(tz))
|
||||
return PTR_ERR(tz);
|
||||
|
||||
tt_zone->tz = tz;
|
||||
|
||||
thermal_zone_device_enable(tz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tt_zone_reg(const char *arg)
|
||||
{
|
||||
struct tt_thermal_zone *tt_zone;
|
||||
int ret;
|
||||
|
||||
tt_zone = tt_get_tt_zone(arg);
|
||||
if (IS_ERR(tt_zone))
|
||||
return PTR_ERR(tt_zone);
|
||||
|
||||
ret = tt_zone_register_tz(tt_zone);
|
||||
|
||||
tt_put_tt_zone(tt_zone);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tt_zone_unreg(const char *arg)
|
||||
{
|
||||
struct tt_thermal_zone *tt_zone;
|
||||
|
||||
tt_zone = tt_get_tt_zone(arg);
|
||||
if (IS_ERR(tt_zone))
|
||||
return PTR_ERR(tt_zone);
|
||||
|
||||
tt_zone_unregister_tz(tt_zone);
|
||||
|
||||
tt_put_tt_zone(tt_zone);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tt_zone_cleanup(void)
|
||||
{
|
||||
struct tt_thermal_zone *tt_zone, *aux;
|
||||
|
||||
list_for_each_entry_safe(tt_zone, aux, &tt_thermal_zones, list_node) {
|
||||
tt_zone_unregister_tz(tt_zone);
|
||||
|
||||
list_del(&tt_zone->list_node);
|
||||
|
||||
tt_zone_free(tt_zone);
|
||||
}
|
||||
}
|
@ -323,11 +323,15 @@ static void thermal_zone_broken_disable(struct thermal_zone_device *tz)
|
||||
static void thermal_zone_device_set_polling(struct thermal_zone_device *tz,
|
||||
unsigned long delay)
|
||||
{
|
||||
if (delay)
|
||||
mod_delayed_work(system_freezable_power_efficient_wq,
|
||||
&tz->poll_queue, delay);
|
||||
else
|
||||
if (!delay) {
|
||||
cancel_delayed_work(&tz->poll_queue);
|
||||
return;
|
||||
}
|
||||
|
||||
if (delay > HZ)
|
||||
delay = round_jiffies_relative(delay);
|
||||
|
||||
mod_delayed_work(system_freezable_power_efficient_wq, &tz->poll_queue, delay);
|
||||
}
|
||||
|
||||
static void thermal_zone_recheck(struct thermal_zone_device *tz, int error)
|
||||
@ -547,6 +551,7 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz,
|
||||
struct thermal_trip_desc *td;
|
||||
LIST_HEAD(way_down_list);
|
||||
LIST_HEAD(way_up_list);
|
||||
int low = -INT_MAX, high = INT_MAX;
|
||||
int temp, ret;
|
||||
|
||||
if (tz->suspended)
|
||||
@ -580,10 +585,17 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz,
|
||||
|
||||
tz->notify_event = event;
|
||||
|
||||
for_each_trip_desc(tz, td)
|
||||
for_each_trip_desc(tz, td) {
|
||||
handle_thermal_trip(tz, td, &way_up_list, &way_down_list);
|
||||
|
||||
thermal_zone_set_trips(tz);
|
||||
if (td->threshold <= tz->temperature && td->threshold > low)
|
||||
low = td->threshold;
|
||||
|
||||
if (td->threshold >= tz->temperature && td->threshold < high)
|
||||
high = td->threshold;
|
||||
}
|
||||
|
||||
thermal_zone_set_trips(tz, low, high);
|
||||
|
||||
list_sort(NULL, &way_up_list, thermal_trip_notify_cmp);
|
||||
list_for_each_entry(td, &way_up_list, notify_list_node)
|
||||
@ -757,15 +769,7 @@ struct thermal_zone_device *thermal_zone_get_by_id(int id)
|
||||
* @tz: pointer to struct thermal_zone_device
|
||||
* @trip: trip point the cooling devices is associated with in this zone.
|
||||
* @cdev: pointer to struct thermal_cooling_device
|
||||
* @upper: the Maximum cooling state for this trip point.
|
||||
* THERMAL_NO_LIMIT means no upper limit,
|
||||
* and the cooling device can be in max_state.
|
||||
* @lower: the Minimum cooling state can be used for this trip point.
|
||||
* THERMAL_NO_LIMIT means no lower limit,
|
||||
* and the cooling device can be in cooling state 0.
|
||||
* @weight: The weight of the cooling device to be bound to the
|
||||
* thermal zone. Use THERMAL_WEIGHT_DEFAULT for the
|
||||
* default value
|
||||
* @cool_spec: cooling specification for @trip and @cdev
|
||||
*
|
||||
* This interface function bind a thermal cooling device to the certain trip
|
||||
* point of a thermal zone device.
|
||||
@ -773,55 +777,41 @@ struct thermal_zone_device *thermal_zone_get_by_id(int id)
|
||||
*
|
||||
* Return: 0 on success, the proper error value otherwise.
|
||||
*/
|
||||
int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz,
|
||||
static int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip,
|
||||
struct thermal_cooling_device *cdev,
|
||||
unsigned long upper, unsigned long lower,
|
||||
unsigned int weight)
|
||||
struct cooling_spec *cool_spec)
|
||||
{
|
||||
struct thermal_instance *dev;
|
||||
struct thermal_instance *pos;
|
||||
struct thermal_zone_device *pos1;
|
||||
struct thermal_cooling_device *pos2;
|
||||
bool upper_no_limit;
|
||||
int result;
|
||||
|
||||
list_for_each_entry(pos1, &thermal_tz_list, node) {
|
||||
if (pos1 == tz)
|
||||
break;
|
||||
}
|
||||
list_for_each_entry(pos2, &thermal_cdev_list, node) {
|
||||
if (pos2 == cdev)
|
||||
break;
|
||||
}
|
||||
|
||||
if (tz != pos1 || cdev != pos2)
|
||||
return -EINVAL;
|
||||
|
||||
/* lower default 0, upper default max_state */
|
||||
lower = lower == THERMAL_NO_LIMIT ? 0 : lower;
|
||||
if (cool_spec->lower == THERMAL_NO_LIMIT)
|
||||
cool_spec->lower = 0;
|
||||
|
||||
if (upper == THERMAL_NO_LIMIT) {
|
||||
upper = cdev->max_state;
|
||||
if (cool_spec->upper == THERMAL_NO_LIMIT) {
|
||||
cool_spec->upper = cdev->max_state;
|
||||
upper_no_limit = true;
|
||||
} else {
|
||||
upper_no_limit = false;
|
||||
}
|
||||
|
||||
if (lower > upper || upper > cdev->max_state)
|
||||
if (cool_spec->lower > cool_spec->upper || cool_spec->upper > cdev->max_state)
|
||||
return -EINVAL;
|
||||
|
||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
dev->tz = tz;
|
||||
|
||||
dev->cdev = cdev;
|
||||
dev->trip = trip;
|
||||
dev->upper = upper;
|
||||
dev->upper = cool_spec->upper;
|
||||
dev->upper_no_limit = upper_no_limit;
|
||||
dev->lower = lower;
|
||||
dev->lower = cool_spec->lower;
|
||||
dev->target = THERMAL_NO_TARGET;
|
||||
dev->weight = weight;
|
||||
dev->weight = cool_spec->weight;
|
||||
|
||||
result = ida_alloc(&tz->ida, GFP_KERNEL);
|
||||
if (result < 0)
|
||||
@ -855,10 +845,9 @@ int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz,
|
||||
if (result)
|
||||
goto remove_trip_file;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
mutex_lock(&cdev->lock);
|
||||
list_for_each_entry(pos, &tz->thermal_instances, tz_node)
|
||||
if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
|
||||
if (pos->trip == trip && pos->cdev == cdev) {
|
||||
result = -EEXIST;
|
||||
break;
|
||||
}
|
||||
@ -870,7 +859,6 @@ int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz,
|
||||
thermal_governor_update_tz(tz, THERMAL_TZ_BIND_CDEV);
|
||||
}
|
||||
mutex_unlock(&cdev->lock);
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
if (!result)
|
||||
return 0;
|
||||
@ -886,21 +874,6 @@ free_mem:
|
||||
kfree(dev);
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_bind_cdev_to_trip);
|
||||
|
||||
int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
|
||||
int trip_index,
|
||||
struct thermal_cooling_device *cdev,
|
||||
unsigned long upper, unsigned long lower,
|
||||
unsigned int weight)
|
||||
{
|
||||
if (trip_index < 0 || trip_index >= tz->num_trips)
|
||||
return -EINVAL;
|
||||
|
||||
return thermal_bind_cdev_to_trip(tz, &tz->trips[trip_index].trip, cdev,
|
||||
upper, lower, weight);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_zone_bind_cooling_device);
|
||||
|
||||
/**
|
||||
* thermal_unbind_cdev_from_trip - unbind a cooling device from a thermal zone.
|
||||
@ -911,33 +884,30 @@ EXPORT_SYMBOL_GPL(thermal_zone_bind_cooling_device);
|
||||
* This interface function unbind a thermal cooling device from the certain
|
||||
* trip point of a thermal zone device.
|
||||
* This function is usually called in the thermal zone device .unbind callback.
|
||||
*
|
||||
* Return: 0 on success, the proper error value otherwise.
|
||||
*/
|
||||
int thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip,
|
||||
struct thermal_cooling_device *cdev)
|
||||
static void thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip,
|
||||
struct thermal_cooling_device *cdev)
|
||||
{
|
||||
struct thermal_instance *pos, *next;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
lockdep_assert_held(&tz->lock);
|
||||
|
||||
mutex_lock(&cdev->lock);
|
||||
list_for_each_entry_safe(pos, next, &tz->thermal_instances, tz_node) {
|
||||
if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
|
||||
if (pos->trip == trip && pos->cdev == cdev) {
|
||||
list_del(&pos->tz_node);
|
||||
list_del(&pos->cdev_node);
|
||||
|
||||
thermal_governor_update_tz(tz, THERMAL_TZ_UNBIND_CDEV);
|
||||
|
||||
mutex_unlock(&cdev->lock);
|
||||
mutex_unlock(&tz->lock);
|
||||
goto unbind;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&cdev->lock);
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
return -ENODEV;
|
||||
return;
|
||||
|
||||
unbind:
|
||||
device_remove_file(&tz->device, &pos->weight_attr);
|
||||
@ -945,20 +915,7 @@ unbind:
|
||||
sysfs_remove_link(&tz->device.kobj, pos->name);
|
||||
ida_free(&tz->ida, pos->id);
|
||||
kfree(pos);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_unbind_cdev_from_trip);
|
||||
|
||||
int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
|
||||
int trip_index,
|
||||
struct thermal_cooling_device *cdev)
|
||||
{
|
||||
if (trip_index < 0 || trip_index >= tz->num_trips)
|
||||
return -EINVAL;
|
||||
|
||||
return thermal_unbind_cdev_from_trip(tz, &tz->trips[trip_index].trip, cdev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_zone_unbind_cooling_device);
|
||||
|
||||
static void thermal_release(struct device *dev)
|
||||
{
|
||||
@ -985,24 +942,41 @@ static struct class *thermal_class;
|
||||
|
||||
static inline
|
||||
void print_bind_err_msg(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip,
|
||||
struct thermal_cooling_device *cdev, int ret)
|
||||
{
|
||||
dev_err(&tz->device, "binding zone %s with cdev %s failed:%d\n",
|
||||
tz->type, cdev->type, ret);
|
||||
dev_err(&tz->device, "binding cdev %s to trip %d failed: %d\n",
|
||||
cdev->type, thermal_zone_trip_id(tz, trip), ret);
|
||||
}
|
||||
|
||||
static void bind_cdev(struct thermal_cooling_device *cdev)
|
||||
static void thermal_zone_cdev_bind(struct thermal_zone_device *tz,
|
||||
struct thermal_cooling_device *cdev)
|
||||
{
|
||||
int ret;
|
||||
struct thermal_zone_device *pos = NULL;
|
||||
struct thermal_trip_desc *td;
|
||||
|
||||
list_for_each_entry(pos, &thermal_tz_list, node) {
|
||||
if (pos->ops.bind) {
|
||||
ret = pos->ops.bind(pos, cdev);
|
||||
if (ret)
|
||||
print_bind_err_msg(pos, cdev, ret);
|
||||
}
|
||||
if (!tz->ops.should_bind)
|
||||
return;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
for_each_trip_desc(tz, td) {
|
||||
struct thermal_trip *trip = &td->trip;
|
||||
struct cooling_spec c = {
|
||||
.upper = THERMAL_NO_LIMIT,
|
||||
.lower = THERMAL_NO_LIMIT,
|
||||
.weight = THERMAL_WEIGHT_DEFAULT
|
||||
};
|
||||
int ret;
|
||||
|
||||
if (!tz->ops.should_bind(tz, trip, cdev, &c))
|
||||
continue;
|
||||
|
||||
ret = thermal_bind_cdev_to_trip(tz, trip, cdev, &c);
|
||||
if (ret)
|
||||
print_bind_err_msg(tz, trip, cdev, ret);
|
||||
}
|
||||
|
||||
mutex_unlock(&tz->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1100,7 +1074,8 @@ __thermal_cooling_device_register(struct device_node *np,
|
||||
list_add(&cdev->node, &thermal_cdev_list);
|
||||
|
||||
/* Update binding information for 'this' new cdev */
|
||||
bind_cdev(cdev);
|
||||
list_for_each_entry(pos, &thermal_tz_list, node)
|
||||
thermal_zone_cdev_bind(pos, cdev);
|
||||
|
||||
list_for_each_entry(pos, &thermal_tz_list, node)
|
||||
if (atomic_cmpxchg(&pos->need_update, 1, 0))
|
||||
@ -1301,6 +1276,19 @@ unlock_list:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_cooling_device_update);
|
||||
|
||||
static void thermal_zone_cdev_unbind(struct thermal_zone_device *tz,
|
||||
struct thermal_cooling_device *cdev)
|
||||
{
|
||||
struct thermal_trip_desc *td;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
for_each_trip_desc(tz, td)
|
||||
thermal_unbind_cdev_from_trip(tz, &td->trip, cdev);
|
||||
|
||||
mutex_unlock(&tz->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* thermal_cooling_device_unregister - removes a thermal cooling device
|
||||
* @cdev: the thermal cooling device to remove.
|
||||
@ -1327,10 +1315,8 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)
|
||||
list_del(&cdev->node);
|
||||
|
||||
/* Unbind all thermal zones associated with 'this' cdev */
|
||||
list_for_each_entry(tz, &thermal_tz_list, node) {
|
||||
if (tz->ops.unbind)
|
||||
tz->ops.unbind(tz, cdev);
|
||||
}
|
||||
list_for_each_entry(tz, &thermal_tz_list, node)
|
||||
thermal_zone_cdev_unbind(tz, cdev);
|
||||
|
||||
mutex_unlock(&thermal_list_lock);
|
||||
|
||||
@ -1338,32 +1324,6 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister);
|
||||
|
||||
static void bind_tz(struct thermal_zone_device *tz)
|
||||
{
|
||||
int ret;
|
||||
struct thermal_cooling_device *pos = NULL;
|
||||
|
||||
if (!tz->ops.bind)
|
||||
return;
|
||||
|
||||
mutex_lock(&thermal_list_lock);
|
||||
|
||||
list_for_each_entry(pos, &thermal_cdev_list, node) {
|
||||
ret = tz->ops.bind(tz, pos);
|
||||
if (ret)
|
||||
print_bind_err_msg(tz, pos, ret);
|
||||
}
|
||||
|
||||
mutex_unlock(&thermal_list_lock);
|
||||
}
|
||||
|
||||
static void thermal_set_delay_jiffies(unsigned long *delay_jiffies, int delay_ms)
|
||||
{
|
||||
*delay_jiffies = msecs_to_jiffies(delay_ms);
|
||||
if (delay_ms > 1000)
|
||||
*delay_jiffies = round_jiffies(*delay_jiffies);
|
||||
}
|
||||
|
||||
int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp)
|
||||
{
|
||||
const struct thermal_trip_desc *td;
|
||||
@ -1424,6 +1384,7 @@ thermal_zone_device_register_with_trips(const char *type,
|
||||
unsigned int polling_delay)
|
||||
{
|
||||
const struct thermal_trip *trip = trips;
|
||||
struct thermal_cooling_device *cdev;
|
||||
struct thermal_zone_device *tz;
|
||||
struct thermal_trip_desc *td;
|
||||
int id;
|
||||
@ -1447,7 +1408,7 @@ thermal_zone_device_register_with_trips(const char *type,
|
||||
}
|
||||
|
||||
if (!ops || !ops->get_temp) {
|
||||
pr_err("Thermal zone device ops not defined\n");
|
||||
pr_err("Thermal zone device ops not defined or invalid\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
@ -1509,8 +1470,8 @@ thermal_zone_device_register_with_trips(const char *type,
|
||||
td->threshold = INT_MAX;
|
||||
}
|
||||
|
||||
thermal_set_delay_jiffies(&tz->passive_delay_jiffies, passive_delay);
|
||||
thermal_set_delay_jiffies(&tz->polling_delay_jiffies, polling_delay);
|
||||
tz->polling_delay_jiffies = msecs_to_jiffies(polling_delay);
|
||||
tz->passive_delay_jiffies = msecs_to_jiffies(passive_delay);
|
||||
tz->recheck_delay_jiffies = THERMAL_RECHECK_DELAY;
|
||||
|
||||
/* sys I/F */
|
||||
@ -1554,13 +1515,16 @@ thermal_zone_device_register_with_trips(const char *type,
|
||||
}
|
||||
|
||||
mutex_lock(&thermal_list_lock);
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
list_add_tail(&tz->node, &thermal_tz_list);
|
||||
mutex_unlock(&tz->lock);
|
||||
mutex_unlock(&thermal_list_lock);
|
||||
|
||||
/* Bind cooling devices for this zone */
|
||||
bind_tz(tz);
|
||||
list_for_each_entry(cdev, &thermal_cdev_list, node)
|
||||
thermal_zone_cdev_bind(tz, cdev);
|
||||
|
||||
mutex_unlock(&thermal_list_lock);
|
||||
|
||||
thermal_zone_device_init(tz);
|
||||
/* Update the new thermal zone and mark it as already updated. */
|
||||
@ -1652,8 +1616,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
|
||||
|
||||
/* Unbind all cdevs associated with 'this' thermal zone */
|
||||
list_for_each_entry(cdev, &thermal_cdev_list, node)
|
||||
if (tz->ops.unbind)
|
||||
tz->ops.unbind(tz, cdev);
|
||||
thermal_zone_cdev_unbind(tz, cdev);
|
||||
|
||||
mutex_unlock(&thermal_list_lock);
|
||||
|
||||
|
@ -15,8 +15,20 @@
|
||||
#include "thermal_netlink.h"
|
||||
#include "thermal_debugfs.h"
|
||||
|
||||
struct thermal_attr {
|
||||
struct device_attribute attr;
|
||||
char name[THERMAL_NAME_LENGTH];
|
||||
};
|
||||
|
||||
struct thermal_trip_attrs {
|
||||
struct thermal_attr type;
|
||||
struct thermal_attr temp;
|
||||
struct thermal_attr hyst;
|
||||
};
|
||||
|
||||
struct thermal_trip_desc {
|
||||
struct thermal_trip trip;
|
||||
struct thermal_trip_attrs trip_attrs;
|
||||
struct list_head notify_list_node;
|
||||
int notify_temp;
|
||||
int threshold;
|
||||
@ -56,9 +68,6 @@ struct thermal_governor {
|
||||
* @device: &struct device for this thermal zone
|
||||
* @removal: removal completion
|
||||
* @resume: resume 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
|
||||
@ -102,9 +111,6 @@ struct thermal_zone_device {
|
||||
struct completion removal;
|
||||
struct completion resume;
|
||||
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;
|
||||
@ -188,11 +194,6 @@ int for_each_thermal_governor(int (*cb)(struct thermal_governor *, void *),
|
||||
|
||||
struct thermal_zone_device *thermal_zone_get_by_id(int id);
|
||||
|
||||
struct thermal_attr {
|
||||
struct device_attribute attr;
|
||||
char name[THERMAL_NAME_LENGTH];
|
||||
};
|
||||
|
||||
static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev)
|
||||
{
|
||||
return cdev->ops->get_requested_power && cdev->ops->state2power &&
|
||||
@ -204,11 +205,6 @@ void __thermal_cdev_update(struct thermal_cooling_device *cdev);
|
||||
|
||||
int get_tz_trend(struct thermal_zone_device *tz, const struct thermal_trip *trip);
|
||||
|
||||
struct thermal_instance *
|
||||
get_thermal_instance(struct thermal_zone_device *tz,
|
||||
struct thermal_cooling_device *cdev,
|
||||
int trip);
|
||||
|
||||
/*
|
||||
* This structure is used to describe the behavior of
|
||||
* a certain cooling device on a certain trip point
|
||||
@ -217,7 +213,6 @@ get_thermal_instance(struct thermal_zone_device *tz,
|
||||
struct thermal_instance {
|
||||
int id;
|
||||
char name[THERMAL_NAME_LENGTH];
|
||||
struct thermal_zone_device *tz;
|
||||
struct thermal_cooling_device *cdev;
|
||||
const struct thermal_trip *trip;
|
||||
bool initialized;
|
||||
@ -259,14 +254,14 @@ void thermal_governor_update_tz(struct thermal_zone_device *tz,
|
||||
|
||||
const char *thermal_trip_type_name(enum thermal_trip_type trip_type);
|
||||
|
||||
void thermal_zone_set_trips(struct thermal_zone_device *tz);
|
||||
void thermal_zone_set_trips(struct thermal_zone_device *tz, int low, int high);
|
||||
int thermal_zone_trip_id(const struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip);
|
||||
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);
|
||||
void thermal_zone_set_trip_hyst(struct thermal_zone_device *tz,
|
||||
struct thermal_trip *trip, int hyst);
|
||||
|
||||
/* sysfs I/F */
|
||||
int thermal_zone_create_device_groups(struct thermal_zone_device *tz);
|
||||
|
@ -39,18 +39,18 @@ int get_tz_trend(struct thermal_zone_device *tz, const struct thermal_trip *trip
|
||||
return trend;
|
||||
}
|
||||
|
||||
static struct thermal_instance *get_instance(struct thermal_zone_device *tz,
|
||||
struct thermal_cooling_device *cdev,
|
||||
const struct thermal_trip *trip)
|
||||
static bool thermal_instance_present(struct thermal_zone_device *tz,
|
||||
struct thermal_cooling_device *cdev,
|
||||
const struct thermal_trip *trip)
|
||||
{
|
||||
struct thermal_instance *ti;
|
||||
|
||||
list_for_each_entry(ti, &tz->thermal_instances, tz_node) {
|
||||
if (ti->trip == trip && ti->cdev == cdev)
|
||||
return ti;
|
||||
return true;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool thermal_trip_is_bound_to_cdev(struct thermal_zone_device *tz,
|
||||
@ -62,7 +62,7 @@ bool thermal_trip_is_bound_to_cdev(struct thermal_zone_device *tz,
|
||||
mutex_lock(&tz->lock);
|
||||
mutex_lock(&cdev->lock);
|
||||
|
||||
ret = !!get_instance(tz, cdev, trip);
|
||||
ret = thermal_instance_present(tz, cdev, trip);
|
||||
|
||||
mutex_unlock(&cdev->lock);
|
||||
mutex_unlock(&tz->lock);
|
||||
@ -71,24 +71,6 @@ bool thermal_trip_is_bound_to_cdev(struct thermal_zone_device *tz,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_trip_is_bound_to_cdev);
|
||||
|
||||
struct thermal_instance *
|
||||
get_thermal_instance(struct thermal_zone_device *tz,
|
||||
struct thermal_cooling_device *cdev, int trip_index)
|
||||
{
|
||||
struct thermal_instance *ti;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
mutex_lock(&cdev->lock);
|
||||
|
||||
ti = get_instance(tz, cdev, &tz->trips[trip_index].trip);
|
||||
|
||||
mutex_unlock(&cdev->lock);
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
return ti;
|
||||
}
|
||||
EXPORT_SYMBOL(get_thermal_instance);
|
||||
|
||||
/**
|
||||
* __thermal_zone_get_temp() - returns the temperature of a thermal zone
|
||||
* @tz: a valid pointer to a struct thermal_zone_device
|
||||
@ -199,8 +181,6 @@ void __thermal_cdev_update(struct thermal_cooling_device *cdev)
|
||||
|
||||
/* Make sure cdev enters the deepest cooling state */
|
||||
list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) {
|
||||
dev_dbg(&cdev->device, "zone%d->target=%lu\n",
|
||||
instance->tz->id, instance->target);
|
||||
if (instance->target == THERMAL_NO_TARGET)
|
||||
continue;
|
||||
if (instance->target > target)
|
||||
|
@ -20,37 +20,6 @@
|
||||
|
||||
/*** functions parsing device tree nodes ***/
|
||||
|
||||
static int of_find_trip_id(struct device_node *np, struct device_node *trip)
|
||||
{
|
||||
struct device_node *trips;
|
||||
struct device_node *t;
|
||||
int i = 0;
|
||||
|
||||
trips = of_get_child_by_name(np, "trips");
|
||||
if (!trips) {
|
||||
pr_err("Failed to find 'trips' node\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the trip id point associated with the cooling device map
|
||||
*/
|
||||
for_each_child_of_node(trips, t) {
|
||||
|
||||
if (t == trip) {
|
||||
of_node_put(t);
|
||||
goto out;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
i = -ENXIO;
|
||||
out:
|
||||
of_node_put(trips);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
* It maps 'enum thermal_trip_type' found in include/linux/thermal.h
|
||||
* into the device tree binding of 'trip', property type.
|
||||
@ -119,6 +88,8 @@ static int thermal_of_populate_trip(struct device_node *np,
|
||||
|
||||
trip->flags = THERMAL_TRIP_FLAG_RW_TEMP;
|
||||
|
||||
trip->priv = np;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -291,39 +262,9 @@ static struct device_node *thermal_of_zone_get_by_name(struct thermal_zone_devic
|
||||
return tz_np;
|
||||
}
|
||||
|
||||
static int __thermal_of_unbind(struct device_node *map_np, int index, int trip_id,
|
||||
struct thermal_zone_device *tz, struct thermal_cooling_device *cdev)
|
||||
{
|
||||
struct of_phandle_args cooling_spec;
|
||||
int ret;
|
||||
|
||||
ret = of_parse_phandle_with_args(map_np, "cooling-device", "#cooling-cells",
|
||||
index, &cooling_spec);
|
||||
|
||||
if (ret < 0) {
|
||||
pr_err("Invalid cooling-device entry\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
of_node_put(cooling_spec.np);
|
||||
|
||||
if (cooling_spec.args_count < 2) {
|
||||
pr_err("wrong reference to cooling device, missing limits\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (cooling_spec.np != cdev->np)
|
||||
return 0;
|
||||
|
||||
ret = thermal_zone_unbind_cooling_device(tz, trip_id, cdev);
|
||||
if (ret)
|
||||
pr_err("Failed to unbind '%s' with '%s': %d\n", tz->type, cdev->type, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __thermal_of_bind(struct device_node *map_np, int index, int trip_id,
|
||||
struct thermal_zone_device *tz, struct thermal_cooling_device *cdev)
|
||||
static bool thermal_of_get_cooling_spec(struct device_node *map_np, int index,
|
||||
struct thermal_cooling_device *cdev,
|
||||
struct cooling_spec *c)
|
||||
{
|
||||
struct of_phandle_args cooling_spec;
|
||||
int ret, weight = THERMAL_WEIGHT_DEFAULT;
|
||||
@ -335,104 +276,73 @@ static int __thermal_of_bind(struct device_node *map_np, int index, int trip_id,
|
||||
|
||||
if (ret < 0) {
|
||||
pr_err("Invalid cooling-device entry\n");
|
||||
return ret;
|
||||
return false;
|
||||
}
|
||||
|
||||
of_node_put(cooling_spec.np);
|
||||
|
||||
if (cooling_spec.args_count < 2) {
|
||||
pr_err("wrong reference to cooling device, missing limits\n");
|
||||
return -EINVAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cooling_spec.np != cdev->np)
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
ret = thermal_zone_bind_cooling_device(tz, trip_id, cdev, cooling_spec.args[1],
|
||||
cooling_spec.args[0],
|
||||
weight);
|
||||
if (ret)
|
||||
pr_err("Failed to bind '%s' with '%s': %d\n", tz->type, cdev->type, ret);
|
||||
c->lower = cooling_spec.args[0];
|
||||
c->upper = cooling_spec.args[1];
|
||||
c->weight = weight;
|
||||
|
||||
return ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int thermal_of_for_each_cooling_device(struct device_node *tz_np, struct device_node *map_np,
|
||||
struct thermal_zone_device *tz, struct thermal_cooling_device *cdev,
|
||||
int (*action)(struct device_node *, int, int,
|
||||
struct thermal_zone_device *, struct thermal_cooling_device *))
|
||||
{
|
||||
struct device_node *tr_np;
|
||||
int count, i, trip_id;
|
||||
|
||||
tr_np = of_parse_phandle(map_np, "trip", 0);
|
||||
if (!tr_np)
|
||||
return -ENODEV;
|
||||
|
||||
trip_id = of_find_trip_id(tz_np, tr_np);
|
||||
if (trip_id < 0)
|
||||
return trip_id;
|
||||
|
||||
count = of_count_phandle_with_args(map_np, "cooling-device", "#cooling-cells");
|
||||
if (count <= 0) {
|
||||
pr_err("Add a cooling_device property with at least one device\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/*
|
||||
* At this point, we don't want to bail out when there is an
|
||||
* error, we will try to bind/unbind as many as possible
|
||||
* cooling devices
|
||||
*/
|
||||
for (i = 0; i < count; i++)
|
||||
action(map_np, i, trip_id, tz, cdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_of_for_each_cooling_maps(struct thermal_zone_device *tz,
|
||||
struct thermal_cooling_device *cdev,
|
||||
int (*action)(struct device_node *, int, int,
|
||||
struct thermal_zone_device *, struct thermal_cooling_device *))
|
||||
static bool thermal_of_should_bind(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip,
|
||||
struct thermal_cooling_device *cdev,
|
||||
struct cooling_spec *c)
|
||||
{
|
||||
struct device_node *tz_np, *cm_np, *child;
|
||||
int ret = 0;
|
||||
bool result = false;
|
||||
|
||||
tz_np = thermal_of_zone_get_by_name(tz);
|
||||
if (IS_ERR(tz_np)) {
|
||||
pr_err("Failed to get node tz by name\n");
|
||||
return PTR_ERR(tz_np);
|
||||
return false;
|
||||
}
|
||||
|
||||
cm_np = of_get_child_by_name(tz_np, "cooling-maps");
|
||||
if (!cm_np)
|
||||
goto out;
|
||||
|
||||
/* Look up the trip and the cdev in the cooling maps. */
|
||||
for_each_child_of_node(cm_np, child) {
|
||||
ret = thermal_of_for_each_cooling_device(tz_np, child, tz, cdev, action);
|
||||
if (ret) {
|
||||
of_node_put(child);
|
||||
break;
|
||||
struct device_node *tr_np;
|
||||
int count, i;
|
||||
|
||||
tr_np = of_parse_phandle(child, "trip", 0);
|
||||
if (tr_np != trip->priv)
|
||||
continue;
|
||||
|
||||
/* The trip has been found, look up the cdev. */
|
||||
count = of_count_phandle_with_args(child, "cooling-device", "#cooling-cells");
|
||||
if (count <= 0)
|
||||
pr_err("Add a cooling_device property with at least one device\n");
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
result = thermal_of_get_cooling_spec(child, i, cdev, c);
|
||||
if (result)
|
||||
break;
|
||||
}
|
||||
|
||||
of_node_put(child);
|
||||
break;
|
||||
}
|
||||
|
||||
of_node_put(cm_np);
|
||||
out:
|
||||
of_node_put(tz_np);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int thermal_of_bind(struct thermal_zone_device *tz,
|
||||
struct thermal_cooling_device *cdev)
|
||||
{
|
||||
return thermal_of_for_each_cooling_maps(tz, cdev, __thermal_of_bind);
|
||||
}
|
||||
|
||||
static int thermal_of_unbind(struct thermal_zone_device *tz,
|
||||
struct thermal_cooling_device *cdev)
|
||||
{
|
||||
return thermal_of_for_each_cooling_maps(tz, cdev, __thermal_of_unbind);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -504,8 +414,7 @@ static struct thermal_zone_device *thermal_of_zone_register(struct device_node *
|
||||
|
||||
thermal_of_parameters_init(np, &tzp);
|
||||
|
||||
of_ops.bind = thermal_of_bind;
|
||||
of_ops.unbind = thermal_of_unbind;
|
||||
of_ops.should_bind = thermal_of_should_bind;
|
||||
|
||||
ret = of_property_read_string(np, "critical-action", &action);
|
||||
if (!ret)
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/container_of.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
@ -78,39 +79,38 @@ mode_store(struct device *dev, struct device_attribute *attr,
|
||||
return count;
|
||||
}
|
||||
|
||||
#define thermal_trip_of_attr(_ptr_, _attr_) \
|
||||
({ \
|
||||
struct thermal_trip_desc *td; \
|
||||
\
|
||||
td = container_of(_ptr_, struct thermal_trip_desc, \
|
||||
trip_attrs._attr_.attr); \
|
||||
&td->trip; \
|
||||
})
|
||||
|
||||
static ssize_t
|
||||
trip_point_type_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct thermal_zone_device *tz = to_thermal_zone(dev);
|
||||
int trip_id;
|
||||
struct thermal_trip *trip = thermal_trip_of_attr(attr, type);
|
||||
|
||||
if (sscanf(attr->attr.name, "trip_point_%d_type", &trip_id) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
return sprintf(buf, "%s\n", thermal_trip_type_name(tz->trips[trip_id].trip.type));
|
||||
return sprintf(buf, "%s\n", thermal_trip_type_name(trip->type));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
trip_point_temp_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct thermal_trip *trip = thermal_trip_of_attr(attr, temp);
|
||||
struct thermal_zone_device *tz = to_thermal_zone(dev);
|
||||
struct thermal_trip *trip;
|
||||
int trip_id, ret;
|
||||
int temp;
|
||||
int ret, temp;
|
||||
|
||||
ret = kstrtoint(buf, 10, &temp);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
|
||||
if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip_id) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
trip = &tz->trips[trip_id].trip;
|
||||
|
||||
if (temp != trip->temperature) {
|
||||
if (tz->ops.set_trip_temp) {
|
||||
ret = tz->ops.set_trip_temp(tz, trip, temp);
|
||||
@ -133,39 +133,29 @@ static ssize_t
|
||||
trip_point_temp_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct thermal_zone_device *tz = to_thermal_zone(dev);
|
||||
int trip_id;
|
||||
struct thermal_trip *trip = thermal_trip_of_attr(attr, temp);
|
||||
|
||||
if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip_id) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
return sprintf(buf, "%d\n", READ_ONCE(tz->trips[trip_id].trip.temperature));
|
||||
return sprintf(buf, "%d\n", READ_ONCE(trip->temperature));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct thermal_trip *trip = thermal_trip_of_attr(attr, hyst);
|
||||
struct thermal_zone_device *tz = to_thermal_zone(dev);
|
||||
struct thermal_trip *trip;
|
||||
int trip_id, ret;
|
||||
int hyst;
|
||||
int ret, hyst;
|
||||
|
||||
ret = kstrtoint(buf, 10, &hyst);
|
||||
if (ret || hyst < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip_id) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
trip = &tz->trips[trip_id].trip;
|
||||
|
||||
if (hyst != trip->hysteresis) {
|
||||
WRITE_ONCE(trip->hysteresis, hyst);
|
||||
thermal_zone_set_trip_hyst(tz, trip, hyst);
|
||||
|
||||
thermal_zone_trip_updated(tz, trip);
|
||||
__thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED);
|
||||
}
|
||||
|
||||
mutex_unlock(&tz->lock);
|
||||
@ -177,13 +167,9 @@ static ssize_t
|
||||
trip_point_hyst_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct thermal_zone_device *tz = to_thermal_zone(dev);
|
||||
int trip_id;
|
||||
struct thermal_trip *trip = thermal_trip_of_attr(attr, hyst);
|
||||
|
||||
if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip_id) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
return sprintf(buf, "%d\n", READ_ONCE(tz->trips[trip_id].trip.hysteresis));
|
||||
return sprintf(buf, "%d\n", READ_ONCE(trip->hysteresis));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@ -382,87 +368,55 @@ static const struct attribute_group *thermal_zone_attribute_groups[] = {
|
||||
*/
|
||||
static int create_trip_attrs(struct thermal_zone_device *tz)
|
||||
{
|
||||
const struct thermal_trip_desc *td;
|
||||
struct thermal_trip_desc *td;
|
||||
struct attribute **attrs;
|
||||
|
||||
/* This function works only for zones with at least one trip */
|
||||
if (tz->num_trips <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
tz->trip_type_attrs = kcalloc(tz->num_trips, sizeof(*tz->trip_type_attrs),
|
||||
GFP_KERNEL);
|
||||
if (!tz->trip_type_attrs)
|
||||
return -ENOMEM;
|
||||
|
||||
tz->trip_temp_attrs = kcalloc(tz->num_trips, sizeof(*tz->trip_temp_attrs),
|
||||
GFP_KERNEL);
|
||||
if (!tz->trip_temp_attrs) {
|
||||
kfree(tz->trip_type_attrs);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
tz->trip_hyst_attrs = kcalloc(tz->num_trips,
|
||||
sizeof(*tz->trip_hyst_attrs),
|
||||
GFP_KERNEL);
|
||||
if (!tz->trip_hyst_attrs) {
|
||||
kfree(tz->trip_type_attrs);
|
||||
kfree(tz->trip_temp_attrs);
|
||||
return -ENOMEM;
|
||||
}
|
||||
int i;
|
||||
|
||||
attrs = kcalloc(tz->num_trips * 3 + 1, sizeof(*attrs), GFP_KERNEL);
|
||||
if (!attrs) {
|
||||
kfree(tz->trip_type_attrs);
|
||||
kfree(tz->trip_temp_attrs);
|
||||
kfree(tz->trip_hyst_attrs);
|
||||
if (!attrs)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
for_each_trip_desc(tz, td) {
|
||||
int indx = thermal_zone_trip_id(tz, &td->trip);
|
||||
struct thermal_trip_attrs *trip_attrs = &td->trip_attrs;
|
||||
|
||||
/* create trip type attribute */
|
||||
snprintf(tz->trip_type_attrs[indx].name, THERMAL_NAME_LENGTH,
|
||||
"trip_point_%d_type", indx);
|
||||
snprintf(trip_attrs->type.name, THERMAL_NAME_LENGTH,
|
||||
"trip_point_%d_type", i);
|
||||
|
||||
sysfs_attr_init(&tz->trip_type_attrs[indx].attr.attr);
|
||||
tz->trip_type_attrs[indx].attr.attr.name =
|
||||
tz->trip_type_attrs[indx].name;
|
||||
tz->trip_type_attrs[indx].attr.attr.mode = S_IRUGO;
|
||||
tz->trip_type_attrs[indx].attr.show = trip_point_type_show;
|
||||
attrs[indx] = &tz->trip_type_attrs[indx].attr.attr;
|
||||
sysfs_attr_init(&trip_attrs->type.attr.attr);
|
||||
trip_attrs->type.attr.attr.name = trip_attrs->type.name;
|
||||
trip_attrs->type.attr.attr.mode = S_IRUGO;
|
||||
trip_attrs->type.attr.show = trip_point_type_show;
|
||||
attrs[i] = &trip_attrs->type.attr.attr;
|
||||
|
||||
/* create trip temp attribute */
|
||||
snprintf(tz->trip_temp_attrs[indx].name, THERMAL_NAME_LENGTH,
|
||||
"trip_point_%d_temp", indx);
|
||||
snprintf(trip_attrs->temp.name, THERMAL_NAME_LENGTH,
|
||||
"trip_point_%d_temp", i);
|
||||
|
||||
sysfs_attr_init(&tz->trip_temp_attrs[indx].attr.attr);
|
||||
tz->trip_temp_attrs[indx].attr.attr.name =
|
||||
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;
|
||||
sysfs_attr_init(&trip_attrs->temp.attr.attr);
|
||||
trip_attrs->temp.attr.attr.name = trip_attrs->temp.name;
|
||||
trip_attrs->temp.attr.attr.mode = S_IRUGO;
|
||||
trip_attrs->temp.attr.show = trip_point_temp_show;
|
||||
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;
|
||||
trip_attrs->temp.attr.attr.mode |= S_IWUSR;
|
||||
trip_attrs->temp.attr.store = trip_point_temp_store;
|
||||
}
|
||||
attrs[indx + tz->num_trips] = &tz->trip_temp_attrs[indx].attr.attr;
|
||||
attrs[i + tz->num_trips] = &trip_attrs->temp.attr.attr;
|
||||
|
||||
snprintf(tz->trip_hyst_attrs[indx].name, THERMAL_NAME_LENGTH,
|
||||
"trip_point_%d_hyst", indx);
|
||||
snprintf(trip_attrs->hyst.name, THERMAL_NAME_LENGTH,
|
||||
"trip_point_%d_hyst", i);
|
||||
|
||||
sysfs_attr_init(&tz->trip_hyst_attrs[indx].attr.attr);
|
||||
tz->trip_hyst_attrs[indx].attr.attr.name =
|
||||
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;
|
||||
sysfs_attr_init(&trip_attrs->hyst.attr.attr);
|
||||
trip_attrs->hyst.attr.attr.name = trip_attrs->hyst.name;
|
||||
trip_attrs->hyst.attr.attr.mode = S_IRUGO;
|
||||
trip_attrs->hyst.attr.show = trip_point_hyst_show;
|
||||
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;
|
||||
trip_attrs->hyst.attr.attr.mode |= S_IWUSR;
|
||||
trip_attrs->hyst.attr.store = trip_point_hyst_store;
|
||||
}
|
||||
attrs[indx + tz->num_trips * 2] =
|
||||
&tz->trip_hyst_attrs[indx].attr.attr;
|
||||
attrs[i + 2 * tz->num_trips] = &trip_attrs->hyst.attr.attr;
|
||||
i++;
|
||||
}
|
||||
attrs[tz->num_trips * 3] = NULL;
|
||||
|
||||
@ -479,13 +433,8 @@ static int create_trip_attrs(struct thermal_zone_device *tz)
|
||||
*/
|
||||
static void destroy_trip_attrs(struct thermal_zone_device *tz)
|
||||
{
|
||||
if (!tz)
|
||||
return;
|
||||
|
||||
kfree(tz->trip_type_attrs);
|
||||
kfree(tz->trip_temp_attrs);
|
||||
kfree(tz->trip_hyst_attrs);
|
||||
kfree(tz->trips_attribute_group.attrs);
|
||||
if (tz)
|
||||
kfree(tz->trips_attribute_group.attrs);
|
||||
}
|
||||
|
||||
int thermal_zone_create_device_groups(struct thermal_zone_device *tz)
|
||||
@ -887,13 +836,12 @@ void thermal_cooling_device_stats_reinit(struct thermal_cooling_device *cdev)
|
||||
ssize_t
|
||||
trip_point_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct thermal_zone_device *tz = to_thermal_zone(dev);
|
||||
struct thermal_instance *instance;
|
||||
|
||||
instance =
|
||||
container_of(attr, struct thermal_instance, attr);
|
||||
instance = container_of(attr, struct thermal_instance, attr);
|
||||
|
||||
return sprintf(buf, "%d\n",
|
||||
thermal_zone_trip_id(instance->tz, instance->trip));
|
||||
return sprintf(buf, "%d\n", thermal_zone_trip_id(tz, instance->trip));
|
||||
}
|
||||
|
||||
ssize_t
|
||||
@ -909,6 +857,7 @@ weight_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
ssize_t weight_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct thermal_zone_device *tz = to_thermal_zone(dev);
|
||||
struct thermal_instance *instance;
|
||||
int ret, weight;
|
||||
|
||||
@ -919,14 +868,13 @@ ssize_t weight_store(struct device *dev, struct device_attribute *attr,
|
||||
instance = container_of(attr, struct thermal_instance, weight_attr);
|
||||
|
||||
/* Don't race with governors using the 'weight' value */
|
||||
mutex_lock(&instance->tz->lock);
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
instance->weight = weight;
|
||||
|
||||
thermal_governor_update_tz(instance->tz,
|
||||
THERMAL_INSTANCE_WEIGHT_CHANGED);
|
||||
thermal_governor_update_tz(tz, THERMAL_INSTANCE_WEIGHT_CHANGED);
|
||||
|
||||
mutex_unlock(&instance->tz->lock);
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
@ -55,31 +55,8 @@ int thermal_zone_for_each_trip(struct thermal_zone_device *tz,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_zone_for_each_trip);
|
||||
|
||||
int thermal_zone_get_num_trips(struct thermal_zone_device *tz)
|
||||
void thermal_zone_set_trips(struct thermal_zone_device *tz, int low, int high)
|
||||
{
|
||||
return tz->num_trips;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_zone_get_num_trips);
|
||||
|
||||
/**
|
||||
* thermal_zone_set_trips - Computes the next trip points for the driver
|
||||
* @tz: a pointer to a thermal zone device structure
|
||||
*
|
||||
* The function computes the next temperature boundaries by browsing
|
||||
* the trip points. The result is the closer low and high trip points
|
||||
* to the current temperature. These values are passed to the backend
|
||||
* driver to let it set its own notification mechanism (usually an
|
||||
* interrupt).
|
||||
*
|
||||
* This function must be called with tz->lock held. Both tz and tz->ops
|
||||
* must be valid pointers.
|
||||
*
|
||||
* It does not return a value
|
||||
*/
|
||||
void thermal_zone_set_trips(struct thermal_zone_device *tz)
|
||||
{
|
||||
const struct thermal_trip_desc *td;
|
||||
int low = -INT_MAX, high = INT_MAX;
|
||||
int ret;
|
||||
|
||||
lockdep_assert_held(&tz->lock);
|
||||
@ -87,14 +64,6 @@ void thermal_zone_set_trips(struct thermal_zone_device *tz)
|
||||
if (!tz->ops.set_trips)
|
||||
return;
|
||||
|
||||
for_each_trip_desc(tz, td) {
|
||||
if (td->threshold <= tz->temperature && td->threshold > low)
|
||||
low = td->threshold;
|
||||
|
||||
if (td->threshold >= tz->temperature && td->threshold < high)
|
||||
high = td->threshold;
|
||||
}
|
||||
|
||||
/* No need to change trip points */
|
||||
if (tz->prev_low_trip == low && tz->prev_high_trip == high)
|
||||
return;
|
||||
@ -114,20 +83,6 @@ void thermal_zone_set_trips(struct thermal_zone_device *tz)
|
||||
dev_err(&tz->device, "Failed to set trips: %d\n", ret);
|
||||
}
|
||||
|
||||
int thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id,
|
||||
struct thermal_trip *trip)
|
||||
{
|
||||
if (!tz || !trip || trip_id < 0 || trip_id >= tz->num_trips)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
*trip = tz->trips[trip_id].trip;
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_zone_get_trip);
|
||||
|
||||
int thermal_zone_trip_id(const struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip)
|
||||
{
|
||||
@ -138,11 +93,11 @@ int thermal_zone_trip_id(const struct thermal_zone_device *tz,
|
||||
return trip_to_trip_desc(trip) - tz->trips;
|
||||
}
|
||||
|
||||
void thermal_zone_trip_updated(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip)
|
||||
void thermal_zone_set_trip_hyst(struct thermal_zone_device *tz,
|
||||
struct thermal_trip *trip, int hyst)
|
||||
{
|
||||
WRITE_ONCE(trip->hysteresis, hyst);
|
||||
thermal_notify_tz_trip_change(tz, trip);
|
||||
__thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED);
|
||||
}
|
||||
|
||||
void thermal_zone_set_trip_temp(struct thermal_zone_device *tz,
|
||||
|
@ -85,11 +85,17 @@ struct thermal_trip {
|
||||
|
||||
struct thermal_zone_device;
|
||||
|
||||
struct cooling_spec {
|
||||
unsigned long upper; /* Highest cooling state */
|
||||
unsigned long lower; /* Lowest cooling state */
|
||||
unsigned int weight; /* Cooling device weight */
|
||||
};
|
||||
|
||||
struct thermal_zone_device_ops {
|
||||
int (*bind) (struct thermal_zone_device *,
|
||||
struct thermal_cooling_device *);
|
||||
int (*unbind) (struct thermal_zone_device *,
|
||||
struct thermal_cooling_device *);
|
||||
bool (*should_bind) (struct thermal_zone_device *,
|
||||
const struct thermal_trip *,
|
||||
struct thermal_cooling_device *,
|
||||
struct cooling_spec *);
|
||||
int (*get_temp) (struct thermal_zone_device *, int *);
|
||||
int (*set_trips) (struct thermal_zone_device *, int, int);
|
||||
int (*change_mode) (struct thermal_zone_device *,
|
||||
@ -203,15 +209,12 @@ static inline void devm_thermal_of_zone_unregister(struct device *dev,
|
||||
}
|
||||
#endif
|
||||
|
||||
int thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id,
|
||||
struct thermal_trip *trip);
|
||||
int for_each_thermal_trip(struct thermal_zone_device *tz,
|
||||
int (*cb)(struct thermal_trip *, void *),
|
||||
void *data);
|
||||
int thermal_zone_for_each_trip(struct thermal_zone_device *tz,
|
||||
int (*cb)(struct thermal_trip *, void *),
|
||||
void *data);
|
||||
int thermal_zone_get_num_trips(struct thermal_zone_device *tz);
|
||||
void thermal_zone_set_trip_temp(struct thermal_zone_device *tz,
|
||||
struct thermal_trip *trip, int temp);
|
||||
|
||||
@ -240,20 +243,6 @@ const char *thermal_zone_device_type(struct thermal_zone_device *tzd);
|
||||
int thermal_zone_device_id(struct thermal_zone_device *tzd);
|
||||
struct device *thermal_zone_device(struct thermal_zone_device *tzd);
|
||||
|
||||
int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip,
|
||||
struct thermal_cooling_device *cdev,
|
||||
unsigned long upper, unsigned long lower,
|
||||
unsigned int weight);
|
||||
int thermal_zone_bind_cooling_device(struct thermal_zone_device *, int,
|
||||
struct thermal_cooling_device *,
|
||||
unsigned long, unsigned long,
|
||||
unsigned int);
|
||||
int thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz,
|
||||
const struct thermal_trip *trip,
|
||||
struct thermal_cooling_device *cdev);
|
||||
int thermal_zone_unbind_cooling_device(struct thermal_zone_device *, int,
|
||||
struct thermal_cooling_device *);
|
||||
void thermal_zone_device_update(struct thermal_zone_device *,
|
||||
enum thermal_notify_event);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user