Commit f5c05974 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

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()
  ...
parents c0a1ef9c e9654659
......@@ -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);
}
}
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);
}
if (acpi_fetch_acpi_dev(handle) == cdev_adev)
return 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 @@ static int tegra_tsensor_disable_hw_channel(const struct tegra_tsensor *ts,
return 0;
}
static void tegra_tsensor_get_hw_channel_trips(struct thermal_zone_device *tzd,
int *hot_trip, int *crit_trip)
struct trip_temps {
int hot_trip;
int crit_trip;
};
static int tegra_tsensor_get_trips_cb(struct thermal_trip *trip, void *arg)
{
unsigned int i;
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;
for (i = 0; i < thermal_zone_get_num_trips(tzd); i++) {
struct thermal_trip trip;
temps->hot_trip = 85000;
temps->crit_trip = 90000;
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);
/*
......
# 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
// 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");
/* 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);
This diff is collapsed.
This diff is collapsed.
......@@ -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;
if (sscanf(attr->attr.name, "trip_point_%d_type", &trip_id) != 1)
return -EINVAL;
struct thermal_trip *trip = thermal_trip_of_attr(attr, type);
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;
if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip_id) != 1)
return -EINVAL;
struct thermal_trip *trip = thermal_trip_of_attr(attr, hyst);
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);
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;
snprintf(trip_attrs->temp.name, THERMAL_NAME_LENGTH,
"trip_point_%d_temp", i);
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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment