Thermal control updates for 6.12-rc1

- 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).
 
  - Refuse to accept trip point temperature or hysteresis that would lead
    to an invalid threshold value when setting them via sysfs (Rafael
    Wysocki).
 
  - Adjust states of all uninitialized instances in the .manage()
    callback of the Bang-bang thermal governor (Rafael Wysocki).
 
  - Drop a couple of redundant checks along with the code depending on
    them from the thermal core (Rafael Wysocki).
 
  - Rearrange the thermal core to avoid redundant checks and simplify
    control flow in a couple of code paths (Rafael Wysocki).
 
  - Add power domain DT bindings for new Amlogic SoCs (Georges Stark).
 
  - Switch from CONFIG_PM_SLEEP guards to pm_sleep_ptr() in the ST
    driver and add a Kconfig dependency on THERMAL_OF subsystem for the
    STi driver (Raphael Gallais-Pou).
 
  - Simplify the error code path in the probe functions in the brcmstb
    driver with the helo of dev_err_probe() (Yan Zhen).
 
  - Make imx_sc_thermal use dev_err_probe() (Alexander Stein).
 
  - Remove trailing space after \n newline in the Renesas driver (Colin
    Ian King).
 
  - Add DT binding compatible string for the SA8255p to the tsens thermal
    driver (Nikunj Kela).
 
  - Use the devm_clk_get_enabled() helpers to simplify the init routine
    in the sprd thermal driver (Huan Yang).
 
  - Remove __maybe_unused notations for the functions by using the new
    RUNTIME_PM_OPS() and SYSTEM_SLEEP_PM_OPS() macros on the IMx and
    Qoriq drivers (Fabio Estevam)
 
  - Remove unused declarations from the ti-soc-thermal driver's header
    file as the functions in question were removed previously (Zhang
    Zekun).
 -----BEGIN PGP SIGNATURE-----
 
 iQJGBAABCAAwFiEE4fcc61cGeeHD/fCwgsRv/nhiVHEFAmbjJz8SHHJqd0Byand5
 c29ja2kubmV0AAoJEILEb/54YlRx3vAP/iS4NTxdF7RJk1ocNCHDyX5pwcS51vZ4
 6OfU4EDEieCZMmgIUUexjGvnhwDBy1CRhYD3BeRAmj9AL+89Dpm5DcXPLVcCf2P9
 wnVrTDfEE2udGvJIJnpKcwsWR96/zot4mt5PSPprtUvDnskTqYlflZYF1FhA1DiS
 rPPKa553wdBAja1ypyGcP/N4nq3DQpcIFi1VQVUgnmdcAe50CA8yd8aQukWcfXoO
 L5pmHMOqPWdP1pxwxx1uUzHX9BRlPVHsxpNfKojcwrv9rZQ99Nkyy+M28/DTQEaT
 I17tdANTv04GUzzu421D2KREeXNsq3GtXtBRQhUegNZiQQxXe/wCB2UU/EFZDEQg
 MSXmGmensDV1xsEBuUy3x99vVsdZND0mnY0R3Gk2LvIWPEoRVMWkS3NUD2cqq48R
 0C0kERkxlAaGQU/GpEZZTun/u3LeicNUKs4vOaFsqADEzoiDKm/kLMhdKzU4FVDD
 wGJLIkTJInVL2sMWYuYeTxnx03Qs0aCYW5TTTzJUqkU15fx8/smLDe3cM5Px2Hbk
 HvRVGXPK0uz6CJdPcUqbdV0916INLyzdrfAGdRy2gFUp7DjBHROhQHSAkxQYiRfL
 W00ZSI8FSMx3+pv0HYmr4WNNk6gNrVyXBmRLLZAE2Th7c9tVw9pEMzXF5lnpYbII
 FFc6OY/gNxyr
 =mr7q
 -----END PGP SIGNATURE-----

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

Pull thermal control updates from Rafael Wysocki:
 "These mostly continue to rework the thermal core and the thermal zone
  driver interface to make the code more straightforward and reduce
  bloat

  The most significant piece of this work is a change of the code
  related to binding cooling devices to thermal zones which, among other
  things, replaces two previously existing thermal zone operations with
  one allowing driver implementations to be much simpler

  There is also a new thermal core testing module allowing mock thermal
  zones to be created and controlled via debugfs in order to exercise
  the thermal core functionality. It is expected to be used for
  implementing thermal core self tests in the future

  Apart from the above, there are assorted thermal driver updates

  Specifics:

   - 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)

   - Refuse to accept trip point temperature or hysteresis that would
     lead to an invalid threshold value when setting them via sysfs
     (Rafael Wysocki)

   - Adjust states of all uninitialized instances in the .manage()
     callback of the Bang-bang thermal governor (Rafael Wysocki)

   - Drop a couple of redundant checks along with the code depending on
     them from the thermal core (Rafael Wysocki)

   - Rearrange the thermal core to avoid redundant checks and simplify
     control flow in a couple of code paths (Rafael Wysocki)

   - Add power domain DT bindings for new Amlogic SoCs (Georges Stark)

   - Switch from CONFIG_PM_SLEEP guards to pm_sleep_ptr() in the ST
     driver and add a Kconfig dependency on THERMAL_OF subsystem for the
     STi driver (Raphael Gallais-Pou)

   - Simplify the error code path in the probe functions in the brcmstb
     driver with the helo of dev_err_probe() (Yan Zhen)

   - Make imx_sc_thermal use dev_err_probe() (Alexander Stein)

   - Remove trailing space after \n newline in the Renesas driver (Colin
     Ian King)

   - Add DT binding compatible string for the SA8255p to the tsens
     thermal driver (Nikunj Kela)

   - Use the devm_clk_get_enabled() helpers to simplify the init routine
     in the sprd thermal driver (Huan Yang)

   - Remove __maybe_unused notations for the functions by using the new
     RUNTIME_PM_OPS() and SYSTEM_SLEEP_PM_OPS() macros on the IMx and
     Qoriq drivers (Fabio Estevam)

   - Remove unused declarations from the ti-soc-thermal driver's header
     file as the functions in question were removed previously (Zhang
     Zekun)"

* tag 'thermal-6.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (48 commits)
  thermal: core: Drop thermal_zone_device_is_enabled()
  thermal: core: Check passive delay in monitor_thermal_zone()
  thermal: core: Drop dead code from monitor_thermal_zone()
  thermal: core: Drop redundant lockdep_assert_held()
  thermal: gov_bang_bang: Adjust states of all uninitialized instances
  thermal: sysfs: Add sanity checks for trip temperature and hysteresis
  thermal/drivers/imx_sc_thermal: Use dev_err_probe
  thermal/drivers/ti-soc-thermal: Remove unused declarations
  thermal/drivers/imx: Remove __maybe_unused notations
  thermal/drivers/qoriq: Remove __maybe_unused notations
  thermal/drivers/sprd: Use devm_clk_get_enabled() helpers
  dt-bindings: thermal: tsens: document support on SA8255p
  thermal/drivers/renesas: Remove trailing space after \n newline
  thermal/drivers/brcmstb_thermal: Simplify with dev_err_probe()
  thermal/drivers/sti: Depend on THERMAL_OF subsystem
  thermal/drivers/st: Switch from CONFIG_PM_SLEEP guards to pm_sleep_ptr()
  dt-bindings: thermal: amlogic,thermal: add optional power-domains
  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
  ...
This commit is contained in:
Linus Torvalds 2024-09-16 08:05:54 +02:00
commit d22300518d
36 changed files with 1161 additions and 929 deletions

View File

@ -32,6 +32,9 @@ properties:
clocks:
maxItems: 1
power-domains:
maxItems: 1
amlogic,ao-secure:
description: phandle to the ao-secure syscon
$ref: /schemas/types.yaml#/definitions/phandle

View File

@ -51,6 +51,7 @@ properties:
- qcom,msm8996-tsens
- qcom,msm8998-tsens
- qcom,qcm2290-tsens
- qcom,sa8255p-tsens
- qcom,sa8775p-tsens
- qcom,sc7180-tsens
- qcom,sc7280-tsens

View File

@ -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

View File

@ -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,

View File

@ -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,
};
@ -450,6 +388,7 @@ mlxsw_thermal_module_init(struct mlxsw_thermal *thermal,
struct mlxsw_thermal_area *area, u8 module)
{
struct mlxsw_thermal_module *module_tz;
int i;
module_tz = &area->tz_module_arr[module];
module_tz->module = module;
@ -461,6 +400,8 @@ mlxsw_thermal_module_init(struct mlxsw_thermal *thermal,
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];
return mlxsw_thermal_module_tz_init(module_tz);
}
@ -566,7 +507,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);
@ -593,6 +534,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;
@ -709,6 +653,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);

View File

@ -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,

View File

@ -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
@ -429,7 +438,7 @@ source "drivers/thermal/samsung/Kconfig"
endmenu
menu "STMicroelectronics thermal drivers"
depends on (ARCH_STI || ARCH_STM32) && OF
depends on (ARCH_STI || ARCH_STM32) && THERMAL_OF
source "drivers/thermal/st/Kconfig"
endmenu

View File

@ -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/

View File

@ -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;

View File

@ -338,11 +338,9 @@ static int brcmstb_thermal_probe(struct platform_device *pdev)
thermal = devm_thermal_of_zone_register(&pdev->dev, 0, priv,
of_ops);
if (IS_ERR(thermal)) {
ret = PTR_ERR(thermal);
dev_err(&pdev->dev, "could not register sensor: %d\n", ret);
return ret;
}
if (IS_ERR(thermal))
return dev_err_probe(&pdev->dev, PTR_ERR(thermal),
"could not register sensor\n");
priv->thermal = thermal;
@ -352,10 +350,9 @@ static int brcmstb_thermal_probe(struct platform_device *pdev)
brcmstb_tmon_irq_thread,
IRQF_ONESHOT,
DRV_NAME, priv);
if (ret < 0) {
dev_err(&pdev->dev, "could not request IRQ: %d\n", ret);
return ret;
}
if (ret < 0)
return dev_err_probe(&pdev->dev, ret,
"could not request IRQ\n");
}
dev_info(&pdev->dev, "registered AVS TMON of-sensor driver\n");

View File

@ -92,23 +92,21 @@ static void bang_bang_manage(struct thermal_zone_device *tz)
for_each_trip_desc(tz, td) {
const struct thermal_trip *trip = &td->trip;
bool turn_on;
if (tz->temperature >= td->threshold ||
trip->temperature == THERMAL_TEMP_INVALID ||
if (trip->temperature == THERMAL_TEMP_INVALID ||
trip->type == THERMAL_TRIP_CRITICAL ||
trip->type == THERMAL_TRIP_HOT)
continue;
/*
* If the initial cooling device state is "on", but the zone
* temperature is not above the trip point, the core will not
* call bang_bang_control() until the zone temperature reaches
* the trip point temperature which may be never. In those
* cases, set the initial state of the cooling device to 0.
* Adjust the target states for uninitialized thermal instances
* to the thermal zone temperature and the trip point threshold.
*/
turn_on = tz->temperature >= td->threshold;
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
if (!instance->initialized && instance->trip == trip)
bang_bang_set_instance_target(instance, 0);
bang_bang_set_instance_target(instance, turn_on);
}
}

View File

@ -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;
}

View File

@ -111,8 +111,7 @@ static int imx_sc_thermal_probe(struct platform_device *pdev)
if (ret == -ENODEV)
continue;
dev_err(&pdev->dev, "failed to register thermal zone\n");
return ret;
return dev_err_probe(&pdev->dev, ret, "failed to register thermal zone\n");
}
devm_thermal_add_hwmon_sysfs(&pdev->dev, sensor->tzd);

View File

@ -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,
@ -773,7 +765,7 @@ static void imx_thermal_remove(struct platform_device *pdev)
imx_thermal_unregister_legacy_cooling(data);
}
static int __maybe_unused imx_thermal_suspend(struct device *dev)
static int imx_thermal_suspend(struct device *dev)
{
struct imx_thermal_data *data = dev_get_drvdata(dev);
int ret;
@ -792,7 +784,7 @@ static int __maybe_unused imx_thermal_suspend(struct device *dev)
return pm_runtime_force_suspend(data->dev);
}
static int __maybe_unused imx_thermal_resume(struct device *dev)
static int imx_thermal_resume(struct device *dev)
{
struct imx_thermal_data *data = dev_get_drvdata(dev);
int ret;
@ -804,7 +796,7 @@ static int __maybe_unused imx_thermal_resume(struct device *dev)
return thermal_zone_device_enable(data->tz);
}
static int __maybe_unused imx_thermal_runtime_suspend(struct device *dev)
static int imx_thermal_runtime_suspend(struct device *dev)
{
struct imx_thermal_data *data = dev_get_drvdata(dev);
const struct thermal_soc_data *socdata = data->socdata;
@ -826,7 +818,7 @@ static int __maybe_unused imx_thermal_runtime_suspend(struct device *dev)
return 0;
}
static int __maybe_unused imx_thermal_runtime_resume(struct device *dev)
static int imx_thermal_runtime_resume(struct device *dev)
{
struct imx_thermal_data *data = dev_get_drvdata(dev);
const struct thermal_soc_data *socdata = data->socdata;
@ -857,15 +849,15 @@ static int __maybe_unused imx_thermal_runtime_resume(struct device *dev)
}
static const struct dev_pm_ops imx_thermal_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(imx_thermal_suspend, imx_thermal_resume)
SET_RUNTIME_PM_OPS(imx_thermal_runtime_suspend,
SYSTEM_SLEEP_PM_OPS(imx_thermal_suspend, imx_thermal_resume)
RUNTIME_PM_OPS(imx_thermal_runtime_suspend,
imx_thermal_runtime_resume, NULL)
};
static struct platform_driver imx_thermal = {
.driver = {
.name = "imx_thermal",
.pm = &imx_thermal_pm_ops,
.pm = pm_ptr(&imx_thermal_pm_ops),
.of_match_table = of_imx_thermal_match,
},
.probe = imx_thermal_probe,

View File

@ -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);

View File

@ -347,7 +347,7 @@ static int qoriq_tmu_probe(struct platform_device *pdev)
return 0;
}
static int __maybe_unused qoriq_tmu_suspend(struct device *dev)
static int qoriq_tmu_suspend(struct device *dev)
{
struct qoriq_tmu_data *data = dev_get_drvdata(dev);
int ret;
@ -361,7 +361,7 @@ static int __maybe_unused qoriq_tmu_suspend(struct device *dev)
return 0;
}
static int __maybe_unused qoriq_tmu_resume(struct device *dev)
static int qoriq_tmu_resume(struct device *dev)
{
int ret;
struct qoriq_tmu_data *data = dev_get_drvdata(dev);
@ -374,7 +374,7 @@ static int __maybe_unused qoriq_tmu_resume(struct device *dev)
return regmap_update_bits(data->regmap, REGS_TMR, TMR_ME, TMR_ME);
}
static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops,
static DEFINE_SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops,
qoriq_tmu_suspend, qoriq_tmu_resume);
static const struct of_device_id qoriq_tmu_match[] = {
@ -387,7 +387,7 @@ MODULE_DEVICE_TABLE(of, qoriq_tmu_match);
static struct platform_driver qoriq_tmu = {
.driver = {
.name = "qoriq_thermal",
.pm = &qoriq_tmu_pm_ops,
.pm = pm_sleep_ptr(&qoriq_tmu_pm_ops),
.of_match_table = qoriq_tmu_match,
},
.probe = qoriq_tmu_probe,

View File

@ -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) {

View File

@ -359,21 +359,17 @@ static int sprd_thm_probe(struct platform_device *pdev)
return -EINVAL;
}
thm->clk = devm_clk_get(&pdev->dev, "enable");
thm->clk = devm_clk_get_enabled(&pdev->dev, "enable");
if (IS_ERR(thm->clk)) {
dev_err(&pdev->dev, "failed to get enable clock\n");
return PTR_ERR(thm->clk);
}
ret = clk_prepare_enable(thm->clk);
if (ret)
return ret;
sprd_thm_para_config(thm);
ret = sprd_thm_cal_read(np, "thm_sign_cal", &val);
if (ret)
goto disable_clk;
return ret;
if (val > 0)
thm->ratio_sign = -1;
@ -382,7 +378,7 @@ static int sprd_thm_probe(struct platform_device *pdev)
ret = sprd_thm_cal_read(np, "thm_ratio_cal", &thm->ratio_off);
if (ret)
goto disable_clk;
return ret;
for_each_child_of_node(np, sen_child) {
sen = devm_kzalloc(&pdev->dev, sizeof(*sen), GFP_KERNEL);
@ -439,8 +435,6 @@ static int sprd_thm_probe(struct platform_device *pdev)
of_put:
of_node_put(sen_child);
disable_clk:
clk_disable_unprepare(thm->clk);
return ret;
}
@ -526,8 +520,6 @@ static void sprd_thm_remove(struct platform_device *pdev)
devm_thermal_of_zone_unregister(&pdev->dev,
thm->sensor[i]->tzd);
}
clk_disable_unprepare(thm->clk);
}
static const struct of_device_id sprd_thermal_of_match[] = {

View File

@ -12,6 +12,7 @@
#include <linux/of_device.h>
#include "st_thermal.h"
#include "../thermal_hwmon.h"
/* The Thermal Framework expects millidegrees */
#define mcelsius(temp) ((temp) * 1000)
@ -135,8 +136,6 @@ static struct thermal_zone_device_ops st_tz_ops = {
.get_temp = st_thermal_get_temp,
};
static struct thermal_trip trip;
int st_thermal_register(struct platform_device *pdev,
const struct of_device_id *st_thermal_of_match)
{
@ -145,7 +144,6 @@ int st_thermal_register(struct platform_device *pdev,
struct device_node *np = dev->of_node;
const struct of_device_id *match;
int polling_delay;
int ret;
if (!np) {
@ -197,29 +195,24 @@ int st_thermal_register(struct platform_device *pdev,
if (ret)
goto sensor_off;
polling_delay = sensor->ops->register_enable_irq ? 0 : 1000;
trip.temperature = sensor->cdata->crit_temp;
trip.type = THERMAL_TRIP_CRITICAL;
sensor->thermal_dev =
thermal_zone_device_register_with_trips(dev_name(dev), &trip, 1, sensor,
&st_tz_ops, NULL, 0, polling_delay);
devm_thermal_of_zone_register(dev, 0, sensor, &st_tz_ops);
if (IS_ERR(sensor->thermal_dev)) {
dev_err(dev, "failed to register thermal zone device\n");
dev_err(dev, "failed to register thermal of zone\n");
ret = PTR_ERR(sensor->thermal_dev);
goto sensor_off;
}
ret = thermal_zone_device_enable(sensor->thermal_dev);
if (ret)
goto tzd_unregister;
platform_set_drvdata(pdev, sensor);
/*
* devm_thermal_of_zone_register() doesn't enable hwmon by default
* Enable it here
*/
devm_thermal_add_hwmon_sysfs(dev, sensor->thermal_dev);
return 0;
tzd_unregister:
thermal_zone_device_unregister(sensor->thermal_dev);
sensor_off:
st_thermal_sensor_off(sensor);
@ -232,11 +225,11 @@ void st_thermal_unregister(struct platform_device *pdev)
struct st_thermal_sensor *sensor = platform_get_drvdata(pdev);
st_thermal_sensor_off(sensor);
thermal_zone_device_unregister(sensor->thermal_dev);
thermal_remove_hwmon_sysfs(sensor->thermal_dev);
devm_thermal_of_zone_unregister(sensor->dev, sensor->thermal_dev);
}
EXPORT_SYMBOL_GPL(st_thermal_unregister);
#ifdef CONFIG_PM_SLEEP
static int st_thermal_suspend(struct device *dev)
{
struct st_thermal_sensor *sensor = dev_get_drvdata(dev);
@ -265,9 +258,8 @@ static int st_thermal_resume(struct device *dev)
return 0;
}
#endif
SIMPLE_DEV_PM_OPS(st_thermal_pm_ops, st_thermal_suspend, st_thermal_resume);
DEFINE_SIMPLE_DEV_PM_OPS(st_thermal_pm_ops, st_thermal_suspend, st_thermal_resume);
EXPORT_SYMBOL_GPL(st_thermal_pm_ops);
MODULE_AUTHOR("STMicroelectronics (R&D) Limited <ajitpal.singh@st.com>");

View File

@ -170,7 +170,7 @@ static void st_mmap_remove(struct platform_device *pdev)
static struct platform_driver st_mmap_thermal_driver = {
.driver = {
.name = "st_thermal_mmap",
.pm = &st_thermal_pm_ops,
.pm = pm_sleep_ptr(&st_thermal_pm_ops),
.of_match_table = st_mmap_thermal_of_match,
},
.probe = st_mmap_probe,

View File

@ -440,7 +440,6 @@ thermal_unprepare:
return ret;
}
#ifdef CONFIG_PM_SLEEP
static int stm_thermal_suspend(struct device *dev)
{
struct stm_thermal_sensor *sensor = dev_get_drvdata(dev);
@ -466,9 +465,8 @@ static int stm_thermal_resume(struct device *dev)
return 0;
}
#endif /* CONFIG_PM_SLEEP */
static SIMPLE_DEV_PM_OPS(stm_thermal_pm_ops,
static DEFINE_SIMPLE_DEV_PM_OPS(stm_thermal_pm_ops,
stm_thermal_suspend, stm_thermal_resume);
static const struct thermal_zone_device_ops stm_tz_ops = {
@ -580,7 +578,7 @@ static void stm_thermal_remove(struct platform_device *pdev)
static struct platform_driver stm_thermal_driver = {
.driver = {
.name = "stm_thermal",
.pm = &stm_thermal_pm_ops,
.pm = pm_sleep_ptr(&stm_thermal_pm_ops),
.of_match_table = stm_thermal_of_match,
},
.probe = stm_thermal_probe,

View File

@ -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++) {
ret = thermal_zone_get_trip(tz, i, &trip);
if (ret)
return -EINVAL;
if (trip.type == THERMAL_TRIP_HOT) {
*trip_id = i;
if (trip->type != THERMAL_TRIP_HOT)
return 0;
}
*trip_ret = trip;
/* Return nonzero to terminate the search. */
return 1;
}
return -EINVAL;
static const struct thermal_trip *get_hot_trip(struct thermal_zone_device *tz)
{
const struct thermal_trip *trip = NULL;
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;

View File

@ -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);
/*

View 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

View 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");

View 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);

View 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);
}
}

View File

@ -323,11 +323,10 @@ 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
cancel_delayed_work(&tz->poll_queue);
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)
@ -360,9 +359,7 @@ static void thermal_zone_recheck(struct thermal_zone_device *tz, int error)
static void monitor_thermal_zone(struct thermal_zone_device *tz)
{
if (tz->mode != THERMAL_DEVICE_ENABLED)
thermal_zone_device_set_polling(tz, 0);
else if (tz->passive > 0)
if (tz->passive > 0 && tz->passive_delay_jiffies)
thermal_zone_device_set_polling(tz, tz->passive_delay_jiffies);
else if (tz->polling_delay_jiffies)
thermal_zone_device_set_polling(tz, tz->polling_delay_jiffies);
@ -547,12 +544,10 @@ 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)
return;
if (!thermal_zone_device_is_enabled(tz))
if (tz->suspended || tz->mode != THERMAL_DEVICE_ENABLED)
return;
ret = __thermal_zone_get_temp(tz, &temp);
@ -580,10 +575,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)
@ -647,13 +649,6 @@ int thermal_zone_device_disable(struct thermal_zone_device *tz)
}
EXPORT_SYMBOL_GPL(thermal_zone_device_disable);
int thermal_zone_device_is_enabled(struct thermal_zone_device *tz)
{
lockdep_assert_held(&tz->lock);
return tz->mode == THERMAL_DEVICE_ENABLED;
}
static bool thermal_zone_is_present(struct thermal_zone_device *tz)
{
return !list_empty(&tz->node);
@ -757,15 +752,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 +760,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 +828,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 +842,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 +857,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 +867,28 @@ 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,
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);
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 +896,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 +923,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 (!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(pos, cdev, ret);
}
print_bind_err_msg(tz, trip, cdev, ret);
}
mutex_unlock(&tz->lock);
}
/**
@ -1100,7 +1055,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 +1257,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 +1296,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 +1305,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 +1365,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,21 +1389,16 @@ 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);
}
if (num_trips > 0 && !trips)
return ERR_PTR(-EINVAL);
if (polling_delay) {
if (passive_delay > polling_delay)
if (polling_delay && passive_delay > polling_delay)
return ERR_PTR(-EINVAL);
if (!passive_delay)
passive_delay = polling_delay;
}
if (!thermal_class)
return ERR_PTR(-ENODEV);
@ -1509,8 +1446,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 +1491,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 +1592,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);

View File

@ -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);
@ -289,7 +284,4 @@ thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev,
unsigned long new_state) {}
#endif /* CONFIG_THERMAL_STATISTICS */
/* device tree support */
int thermal_zone_device_is_enabled(struct thermal_zone_device *tz);
#endif /* __THERMAL_CORE_H__ */

View File

@ -39,7 +39,7 @@ 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,
static bool thermal_instance_present(struct thermal_zone_device *tz,
struct thermal_cooling_device *cdev,
const struct thermal_trip *trip)
{
@ -47,10 +47,10 @@ static struct thermal_instance *get_instance(struct thermal_zone_device *tz,
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)

View File

@ -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,
static bool thermal_of_should_bind(struct thermal_zone_device *tz,
const struct thermal_trip *trip,
struct thermal_cooling_device *cdev,
int (*action)(struct device_node *, int, int,
struct thermal_zone_device *, struct thermal_cooling_device *))
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);
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)

View File

@ -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>
@ -52,7 +53,7 @@ mode_show(struct device *dev, struct device_attribute *attr, char *buf)
int enabled;
mutex_lock(&tz->lock);
enabled = thermal_zone_device_is_enabled(tz);
enabled = tz->mode == THERMAL_DEVICE_ENABLED;
mutex_unlock(&tz->lock);
return sprintf(buf, "%s\n", enabled ? "enabled" : "disabled");
@ -78,40 +79,48 @@ 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)
goto unlock;
/* Arrange the condition to avoid integer overflows. */
if (temp != THERMAL_TEMP_INVALID &&
temp <= trip->hysteresis + THERMAL_TEMP_INVALID) {
ret = -EINVAL;
goto unlock;
}
if (temp != trip->temperature) {
if (tz->ops.set_trip_temp) {
ret = tz->ops.set_trip_temp(tz, trip, temp);
if (ret)
@ -121,7 +130,6 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr,
thermal_zone_set_trip_temp(tz, trip, temp);
__thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED);
}
unlock:
mutex_unlock(&tz->lock);
@ -133,57 +141,61 @@ 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)
goto unlock;
if (hyst != trip->hysteresis) {
/*
* Allow the hysteresis to be updated when the temperature is invalid
* to allow user space to avoid having to adjust hysteresis after a
* valid temperature has been set, but in that case just change the
* value and do nothing else.
*/
if (trip->temperature == THERMAL_TEMP_INVALID) {
WRITE_ONCE(trip->hysteresis, hyst);
thermal_zone_trip_updated(tz, trip);
goto unlock;
}
if (trip->temperature - hyst <= THERMAL_TEMP_INVALID) {
ret = -EINVAL;
goto unlock;
}
thermal_zone_set_trip_hyst(tz, trip, hyst);
__thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED);
unlock:
mutex_unlock(&tz->lock);
return count;
return ret ? ret : count;
}
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 +394,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,12 +459,7 @@ 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);
if (tz)
kfree(tz->trips_attribute_group.attrs);
}
@ -887,13 +862,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 +883,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 +894,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;
}

View File

@ -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,

View File

@ -336,10 +336,6 @@ struct ti_bandgap_data {
struct ti_temp_sensor sensors[];
};
int ti_bandgap_read_thot(struct ti_bandgap *bgp, int id, int *thot);
int ti_bandgap_write_thot(struct ti_bandgap *bgp, int id, int val);
int ti_bandgap_read_tcold(struct ti_bandgap *bgp, int id, int *tcold);
int ti_bandgap_write_tcold(struct ti_bandgap *bgp, int id, int val);
int ti_bandgap_read_update_interval(struct ti_bandgap *bgp, int id,
int *interval);
int ti_bandgap_write_update_interval(struct ti_bandgap *bgp, int id,

View File

@ -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);