diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2024-09-16 09:05:54 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2024-09-16 09:05:54 +0300 |
commit | d22300518d875f78203e9afacb5aa0b0316da523 (patch) | |
tree | 384603167b4a6964ad7679eee36af5b47181347c /drivers | |
parent | 02824a5fd11f99b4637668926a59aab3698b46a9 (diff) | |
parent | 3bc5ed15bdc5077d7ee621f44872f550babbea3e (diff) | |
download | linux-d22300518d875f78203e9afacb5aa0b0316da523.tar.xz |
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
...
Diffstat (limited to 'drivers')
32 files changed, 1142 insertions, 843 deletions
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index a0cfc857fb55..78db38c7076e 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -558,77 +558,31 @@ static void acpi_thermal_zone_device_critical(struct thermal_zone_device *therma thermal_zone_device_critical(thermal); } -struct acpi_thermal_bind_data { - struct thermal_zone_device *thermal; - struct thermal_cooling_device *cdev; - bool bind; -}; - -static int bind_unbind_cdev_cb(struct thermal_trip *trip, void *arg) +static bool acpi_thermal_should_bind_cdev(struct thermal_zone_device *thermal, + const struct thermal_trip *trip, + struct thermal_cooling_device *cdev, + struct cooling_spec *c) { struct acpi_thermal_trip *acpi_trip = trip->priv; - struct acpi_thermal_bind_data *bd = arg; - struct thermal_zone_device *thermal = bd->thermal; - struct thermal_cooling_device *cdev = bd->cdev; struct acpi_device *cdev_adev = cdev->devdata; int i; /* Skip critical and hot trips. */ if (!acpi_trip) - return 0; + return false; for (i = 0; i < acpi_trip->devices.count; i++) { acpi_handle handle = acpi_trip->devices.handles[i]; - struct acpi_device *adev = acpi_fetch_acpi_dev(handle); - - if (adev != cdev_adev) - continue; - - if (bd->bind) { - int ret; - - ret = thermal_bind_cdev_to_trip(thermal, trip, cdev, - THERMAL_NO_LIMIT, - THERMAL_NO_LIMIT, - THERMAL_WEIGHT_DEFAULT); - if (ret) - return ret; - } else { - thermal_unbind_cdev_from_trip(thermal, trip, cdev); - } - } - - return 0; -} -static int acpi_thermal_bind_unbind_cdev(struct thermal_zone_device *thermal, - struct thermal_cooling_device *cdev, - bool bind) -{ - struct acpi_thermal_bind_data bd = { - .thermal = thermal, .cdev = cdev, .bind = bind - }; - - return for_each_thermal_trip(thermal, bind_unbind_cdev_cb, &bd); -} - -static int -acpi_thermal_bind_cooling_device(struct thermal_zone_device *thermal, - struct thermal_cooling_device *cdev) -{ - return acpi_thermal_bind_unbind_cdev(thermal, cdev, true); -} + if (acpi_fetch_acpi_dev(handle) == cdev_adev) + return true; + } -static int -acpi_thermal_unbind_cooling_device(struct thermal_zone_device *thermal, - struct thermal_cooling_device *cdev) -{ - return acpi_thermal_bind_unbind_cdev(thermal, cdev, false); + return false; } static const struct thermal_zone_device_ops acpi_thermal_zone_ops = { - .bind = acpi_thermal_bind_cooling_device, - .unbind = acpi_thermal_unbind_cooling_device, + .should_bind = acpi_thermal_should_bind_cdev, .get_temp = thermal_get_temp, .get_trend = thermal_get_trend, .hot = acpi_thermal_zone_device_hot, diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c b/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c index 303d2ce4dc1e..e746cd9c68ed 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c @@ -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); diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index 018c48429616..4c3bb68e8fe4 100644 --- a/drivers/platform/x86/acerhdf.c +++ b/drivers/platform/x86/acerhdf.c @@ -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, diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index ed16897584b4..61e7ae524b1f 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -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 diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index ce7a4752ef52..41c4d56beb40 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -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/ diff --git a/drivers/thermal/broadcom/bcm2835_thermal.c b/drivers/thermal/broadcom/bcm2835_thermal.c index 5ad87eb3f578..7d61493082b5 100644 --- a/drivers/thermal/broadcom/bcm2835_thermal.c +++ b/drivers/thermal/broadcom/bcm2835_thermal.c @@ -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; diff --git a/drivers/thermal/broadcom/brcmstb_thermal.c b/drivers/thermal/broadcom/brcmstb_thermal.c index 9674e5ffcfa2..270982740fde 100644 --- a/drivers/thermal/broadcom/brcmstb_thermal.c +++ b/drivers/thermal/broadcom/brcmstb_thermal.c @@ -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"); diff --git a/drivers/thermal/gov_bang_bang.c b/drivers/thermal/gov_bang_bang.c index daed67d19efb..863e7a4272e6 100644 --- a/drivers/thermal/gov_bang_bang.c +++ b/drivers/thermal/gov_bang_bang.c @@ -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); } } diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c index 0eb657db62e4..f1fe0f8ab04f 100644 --- a/drivers/thermal/hisi_thermal.c +++ b/drivers/thermal/hisi_thermal.c @@ -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; } diff --git a/drivers/thermal/imx_sc_thermal.c b/drivers/thermal/imx_sc_thermal.c index 7224f8d21db9..88558ce58880 100644 --- a/drivers/thermal/imx_sc_thermal.c +++ b/drivers/thermal/imx_sc_thermal.c @@ -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); diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c index 091fb30dedf3..b8e85a405351 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c @@ -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, - imx_thermal_runtime_resume, NULL) + 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, diff --git a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c index 96daad28b0c0..c2d59cbfaea9 100644 --- a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c +++ b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c @@ -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); diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c index 404f01cca4da..52e26be8c53d 100644 --- a/drivers/thermal/qoriq_thermal.c +++ b/drivers/thermal/qoriq_thermal.c @@ -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,8 +374,8 @@ 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, - qoriq_tmu_suspend, qoriq_tmu_resume); +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[] = { { .compatible = "fsl,qoriq-tmu", }, @@ -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, diff --git a/drivers/thermal/renesas/rcar_gen3_thermal.c b/drivers/thermal/renesas/rcar_gen3_thermal.c index 5c769871753a..810f86677461 100644 --- a/drivers/thermal/renesas/rcar_gen3_thermal.c +++ b/drivers/thermal/renesas/rcar_gen3_thermal.c @@ -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) { diff --git a/drivers/thermal/renesas/rcar_thermal.c b/drivers/thermal/renesas/rcar_thermal.c index 1e93f60b6d74..ddc8341e5c3f 100644 --- a/drivers/thermal/renesas/rcar_thermal.c +++ b/drivers/thermal/renesas/rcar_thermal.c @@ -447,7 +447,7 @@ static int rcar_thermal_probe(struct platform_device *pdev) ret = devm_request_irq(dev, irq, rcar_thermal_irq, IRQF_SHARED, dev_name(dev), common); if (ret) { - dev_err(dev, "irq request failed\n "); + dev_err(dev, "irq request failed\n"); goto error_unregister; } diff --git a/drivers/thermal/sprd_thermal.c b/drivers/thermal/sprd_thermal.c index 874192546548..dfd1d529c410 100644 --- a/drivers/thermal/sprd_thermal.c +++ b/drivers/thermal/sprd_thermal.c @@ -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[] = { diff --git a/drivers/thermal/st/st_thermal.c b/drivers/thermal/st/st_thermal.c index 2a105409864e..a14a37d54698 100644 --- a/drivers/thermal/st/st_thermal.c +++ b/drivers/thermal/st/st_thermal.c @@ -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>"); diff --git a/drivers/thermal/st/st_thermal_memmap.c b/drivers/thermal/st/st_thermal_memmap.c index e427117381a4..97493d2b2f49 100644 --- a/drivers/thermal/st/st_thermal_memmap.c +++ b/drivers/thermal/st/st_thermal_memmap.c @@ -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, diff --git a/drivers/thermal/st/stm_thermal.c b/drivers/thermal/st/stm_thermal.c index 34785b9276fc..ffd988600ed6 100644 --- a/drivers/thermal/st/stm_thermal.c +++ b/drivers/thermal/st/stm_thermal.c @@ -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,10 +465,9 @@ static int stm_thermal_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM_SLEEP */ -static SIMPLE_DEV_PM_OPS(stm_thermal_pm_ops, - stm_thermal_suspend, stm_thermal_resume); +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 = { .get_temp = stm_thermal_get_temp, @@ -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, diff --git a/drivers/thermal/tegra/soctherm.c b/drivers/thermal/tegra/soctherm.c index d3dfc34c62c6..a023c948afbd 100644 --- a/drivers/thermal/tegra/soctherm.c +++ b/drivers/thermal/tegra/soctherm.c @@ -682,24 +682,25 @@ static const struct thermal_zone_device_ops tegra_of_thermal_ops = { .set_trips = tegra_thermctl_set_trips, }; -static int get_hot_temp(struct thermal_zone_device *tz, int *trip_id, int *temp) +static int get_hot_trip_cb(struct thermal_trip *trip, void *arg) { - int i, ret; - struct thermal_trip trip; + const struct thermal_trip **trip_ret = arg; - for (i = 0; i < thermal_zone_get_num_trips(tz); i++) { + if (trip->type != THERMAL_TRIP_HOT) + return 0; - ret = thermal_zone_get_trip(tz, i, &trip); - if (ret) - return -EINVAL; + *trip_ret = trip; + /* Return nonzero to terminate the search. */ + return 1; +} - if (trip.type == THERMAL_TRIP_HOT) { - *trip_id = i; - return 0; - } - } +static const struct thermal_trip *get_hot_trip(struct thermal_zone_device *tz) +{ + const struct thermal_trip *trip = NULL; - return -EINVAL; + thermal_zone_for_each_trip(tz, get_hot_trip_cb, &trip); + + return trip; } /** @@ -731,8 +732,9 @@ static int tegra_soctherm_set_hwtrips(struct device *dev, struct thermal_zone_device *tz) { struct tegra_soctherm *ts = dev_get_drvdata(dev); + const struct thermal_trip *hot_trip; struct soctherm_throt_cfg *stc; - int i, trip, temperature, ret; + int i, temperature, ret; /* Get thermtrips. If missing, try to get critical trips. */ temperature = tsensor_group_thermtrip_get(ts, sg->id); @@ -749,8 +751,8 @@ static int tegra_soctherm_set_hwtrips(struct device *dev, dev_info(dev, "thermtrip: will shut down when %s reaches %d mC\n", sg->name, temperature); - ret = get_hot_temp(tz, &trip, &temperature); - if (ret) { + hot_trip = get_hot_trip(tz); + if (!hot_trip) { dev_info(dev, "throttrip: %s: missing hot temperature\n", sg->name); return 0; @@ -763,7 +765,7 @@ static int tegra_soctherm_set_hwtrips(struct device *dev, continue; cdev = ts->throt_cfgs[i].cdev; - if (get_thermal_instance(tz, cdev, trip)) + if (thermal_trip_is_bound_to_cdev(tz, hot_trip, cdev)) stc = find_throttle_cfg_by_name(ts, cdev->type); else continue; diff --git a/drivers/thermal/tegra/tegra30-tsensor.c b/drivers/thermal/tegra/tegra30-tsensor.c index d911fa60f100..6245f6b97f43 100644 --- a/drivers/thermal/tegra/tegra30-tsensor.c +++ b/drivers/thermal/tegra/tegra30-tsensor.c @@ -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) +struct trip_temps { + int hot_trip; + int crit_trip; +}; + +static int tegra_tsensor_get_trips_cb(struct thermal_trip *trip, void *arg) { - unsigned int i; + struct trip_temps *temps = arg; + + if (trip->type == THERMAL_TRIP_HOT) + temps->hot_trip = trip->temperature; + else if (trip->type == THERMAL_TRIP_CRITICAL) + temps->crit_trip = trip->temperature; + + return 0; +} +static void tegra_tsensor_get_hw_channel_trips(struct thermal_zone_device *tzd, + struct trip_temps *temps) +{ /* * 90C is the maximal critical temperature of all Tegra30 SoC variants, * use it for the default trip if unspecified in a device-tree. */ - *hot_trip = 85000; - *crit_trip = 90000; - - for (i = 0; i < thermal_zone_get_num_trips(tzd); i++) { - - struct thermal_trip trip; + temps->hot_trip = 85000; + temps->crit_trip = 90000; - thermal_zone_get_trip(tzd, i, &trip); - - if (trip.type == THERMAL_TRIP_HOT) - *hot_trip = trip.temperature; - - if (trip.type == THERMAL_TRIP_CRITICAL) - *crit_trip = trip.temperature; - } + thermal_zone_for_each_trip(tzd, tegra_tsensor_get_trips_cb, temps); /* clamp hardware trips to the calibration limits */ - *hot_trip = clamp(*hot_trip, 25000, 90000); + temps->hot_trip = clamp(temps->hot_trip, 25000, 90000); /* * Kernel will perform a normal system shut down if it will @@ -338,7 +342,7 @@ static void tegra_tsensor_get_hw_channel_trips(struct thermal_zone_device *tzd, * shut down gracefully before sending signal to the Power * Management controller. */ - *crit_trip = clamp(*crit_trip + 5000, 25000, 90000); + temps->crit_trip = clamp(temps->crit_trip + 5000, 25000, 90000); } static int tegra_tsensor_enable_hw_channel(const struct tegra_tsensor *ts, @@ -346,7 +350,8 @@ static int tegra_tsensor_enable_hw_channel(const struct tegra_tsensor *ts, { const struct tegra_tsensor_channel *tsc = &ts->ch[id]; struct thermal_zone_device *tzd = tsc->tzd; - int err, hot_trip = 0, crit_trip = 0; + struct trip_temps temps = { 0 }; + int err; u32 val; if (!tzd) { @@ -357,24 +362,24 @@ static int tegra_tsensor_enable_hw_channel(const struct tegra_tsensor *ts, return 0; } - tegra_tsensor_get_hw_channel_trips(tzd, &hot_trip, &crit_trip); + tegra_tsensor_get_hw_channel_trips(tzd, &temps); dev_info_once(ts->dev, "ch%u: PMC emergency shutdown trip set to %dC\n", - id, DIV_ROUND_CLOSEST(crit_trip, 1000)); + id, DIV_ROUND_CLOSEST(temps.crit_trip, 1000)); - hot_trip = tegra_tsensor_temp_to_counter(ts, hot_trip); - crit_trip = tegra_tsensor_temp_to_counter(ts, crit_trip); + temps.hot_trip = tegra_tsensor_temp_to_counter(ts, temps.hot_trip); + temps.crit_trip = tegra_tsensor_temp_to_counter(ts, temps.crit_trip); /* program LEVEL2 counter threshold */ val = readl_relaxed(tsc->regs + TSENSOR_SENSOR0_CONFIG1); val &= ~TSENSOR_SENSOR0_CONFIG1_TH2; - val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG1_TH2, hot_trip); + val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG1_TH2, temps.hot_trip); writel_relaxed(val, tsc->regs + TSENSOR_SENSOR0_CONFIG1); /* program LEVEL3 counter threshold */ val = readl_relaxed(tsc->regs + TSENSOR_SENSOR0_CONFIG2); val &= ~TSENSOR_SENSOR0_CONFIG2_TH3; - val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG2_TH3, crit_trip); + val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG2_TH3, temps.crit_trip); writel_relaxed(val, tsc->regs + TSENSOR_SENSOR0_CONFIG2); /* diff --git a/drivers/thermal/testing/Makefile b/drivers/thermal/testing/Makefile new file mode 100644 index 000000000000..ede9678efbce --- /dev/null +++ b/drivers/thermal/testing/Makefile @@ -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 diff --git a/drivers/thermal/testing/command.c b/drivers/thermal/testing/command.c new file mode 100644 index 000000000000..ba11d70e8021 --- /dev/null +++ b/drivers/thermal/testing/command.c @@ -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"); diff --git a/drivers/thermal/testing/thermal_testing.h b/drivers/thermal/testing/thermal_testing.h new file mode 100644 index 000000000000..c790a32aae4e --- /dev/null +++ b/drivers/thermal/testing/thermal_testing.h @@ -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); diff --git a/drivers/thermal/testing/zone.c b/drivers/thermal/testing/zone.c new file mode 100644 index 000000000000..c6d8c66f40f9 --- /dev/null +++ b/drivers/thermal/testing/zone.c @@ -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); + } +} diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index e6669aeda1ff..073d02e21352 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -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, - const struct thermal_trip *trip, - struct thermal_cooling_device *cdev) +static void thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz, + const struct thermal_trip *trip, + struct thermal_cooling_device *cdev) { struct thermal_instance *pos, *next; - mutex_lock(&tz->lock); 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 (ret) - print_bind_err_msg(pos, cdev, ret); - } + if (!tz->ops.should_bind) + return; + + mutex_lock(&tz->lock); + + for_each_trip_desc(tz, td) { + struct thermal_trip *trip = &td->trip; + struct cooling_spec c = { + .upper = THERMAL_NO_LIMIT, + .lower = THERMAL_NO_LIMIT, + .weight = THERMAL_WEIGHT_DEFAULT + }; + int ret; + + if (!tz->ops.should_bind(tz, trip, cdev, &c)) + continue; + + ret = thermal_bind_cdev_to_trip(tz, trip, cdev, &c); + if (ret) + print_bind_err_msg(tz, trip, cdev, ret); } + + mutex_unlock(&tz->lock); } /** @@ -1100,7 +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,20 +1389,15 @@ 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) - return ERR_PTR(-EINVAL); - - if (!passive_delay) - passive_delay = polling_delay; - } + if (polling_delay && passive_delay > polling_delay) + return ERR_PTR(-EINVAL); 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); diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 4cf2b7230d04..50b858aa173a 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -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__ */ diff --git a/drivers/thermal/thermal_helpers.c b/drivers/thermal/thermal_helpers.c index aedb8369e2aa..dc374a7a1a65 100644 --- a/drivers/thermal/thermal_helpers.c +++ b/drivers/thermal/thermal_helpers.c @@ -39,18 +39,18 @@ int get_tz_trend(struct thermal_zone_device *tz, const struct thermal_trip *trip return trend; } -static struct thermal_instance *get_instance(struct thermal_zone_device *tz, - struct thermal_cooling_device *cdev, - const struct thermal_trip *trip) +static bool thermal_instance_present(struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev, + const struct thermal_trip *trip) { struct thermal_instance *ti; list_for_each_entry(ti, &tz->thermal_instances, tz_node) { if (ti->trip == trip && ti->cdev == cdev) - return ti; + return true; } - return NULL; + return false; } bool thermal_trip_is_bound_to_cdev(struct thermal_zone_device *tz, @@ -62,7 +62,7 @@ bool thermal_trip_is_bound_to_cdev(struct thermal_zone_device *tz, mutex_lock(&tz->lock); mutex_lock(&cdev->lock); - ret = !!get_instance(tz, cdev, trip); + ret = thermal_instance_present(tz, cdev, trip); mutex_unlock(&cdev->lock); mutex_unlock(&tz->lock); @@ -71,24 +71,6 @@ bool thermal_trip_is_bound_to_cdev(struct thermal_zone_device *tz, } EXPORT_SYMBOL_GPL(thermal_trip_is_bound_to_cdev); -struct thermal_instance * -get_thermal_instance(struct thermal_zone_device *tz, - struct thermal_cooling_device *cdev, int trip_index) -{ - struct thermal_instance *ti; - - mutex_lock(&tz->lock); - mutex_lock(&cdev->lock); - - ti = get_instance(tz, cdev, &tz->trips[trip_index].trip); - - mutex_unlock(&cdev->lock); - mutex_unlock(&tz->lock); - - return ti; -} -EXPORT_SYMBOL(get_thermal_instance); - /** * __thermal_zone_get_temp() - returns the temperature of a thermal zone * @tz: a valid pointer to a struct thermal_zone_device @@ -199,8 +181,6 @@ void __thermal_cdev_update(struct thermal_cooling_device *cdev) /* Make sure cdev enters the deepest cooling state */ list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) { - dev_dbg(&cdev->device, "zone%d->target=%lu\n", - instance->tz->id, instance->target); if (instance->target == THERMAL_NO_TARGET) continue; if (instance->target > target) diff --git a/drivers/thermal/thermal_of.c b/drivers/thermal/thermal_of.c index 1f252692815a..a4caf7899f8e 100644 --- a/drivers/thermal/thermal_of.c +++ b/drivers/thermal/thermal_of.c @@ -20,37 +20,6 @@ /*** functions parsing device tree nodes ***/ -static int of_find_trip_id(struct device_node *np, struct device_node *trip) -{ - struct device_node *trips; - struct device_node *t; - int i = 0; - - trips = of_get_child_by_name(np, "trips"); - if (!trips) { - pr_err("Failed to find 'trips' node\n"); - return -EINVAL; - } - - /* - * Find the trip id point associated with the cooling device map - */ - for_each_child_of_node(trips, t) { - - if (t == trip) { - of_node_put(t); - goto out; - } - i++; - } - - i = -ENXIO; -out: - of_node_put(trips); - - return i; -} - /* * It maps 'enum thermal_trip_type' found in include/linux/thermal.h * into the device tree binding of 'trip', property type. @@ -119,6 +88,8 @@ static int thermal_of_populate_trip(struct device_node *np, trip->flags = THERMAL_TRIP_FLAG_RW_TEMP; + trip->priv = np; + return 0; } @@ -291,39 +262,9 @@ static struct device_node *thermal_of_zone_get_by_name(struct thermal_zone_devic return tz_np; } -static int __thermal_of_unbind(struct device_node *map_np, int index, int trip_id, - struct thermal_zone_device *tz, struct thermal_cooling_device *cdev) -{ - struct of_phandle_args cooling_spec; - int ret; - - ret = of_parse_phandle_with_args(map_np, "cooling-device", "#cooling-cells", - index, &cooling_spec); - - if (ret < 0) { - pr_err("Invalid cooling-device entry\n"); - return ret; - } - - of_node_put(cooling_spec.np); - - if (cooling_spec.args_count < 2) { - pr_err("wrong reference to cooling device, missing limits\n"); - return -EINVAL; - } - - if (cooling_spec.np != cdev->np) - return 0; - - ret = thermal_zone_unbind_cooling_device(tz, trip_id, cdev); - if (ret) - pr_err("Failed to unbind '%s' with '%s': %d\n", tz->type, cdev->type, ret); - - return ret; -} - -static int __thermal_of_bind(struct device_node *map_np, int index, int trip_id, - struct thermal_zone_device *tz, struct thermal_cooling_device *cdev) +static bool thermal_of_get_cooling_spec(struct device_node *map_np, int index, + struct thermal_cooling_device *cdev, + struct cooling_spec *c) { struct of_phandle_args cooling_spec; int ret, weight = THERMAL_WEIGHT_DEFAULT; @@ -335,104 +276,73 @@ static int __thermal_of_bind(struct device_node *map_np, int index, int trip_id, if (ret < 0) { pr_err("Invalid cooling-device entry\n"); - return ret; + return false; } of_node_put(cooling_spec.np); if (cooling_spec.args_count < 2) { pr_err("wrong reference to cooling device, missing limits\n"); - return -EINVAL; + return false; } if (cooling_spec.np != cdev->np) - return 0; + return false; - ret = thermal_zone_bind_cooling_device(tz, trip_id, cdev, cooling_spec.args[1], - cooling_spec.args[0], - weight); - if (ret) - pr_err("Failed to bind '%s' with '%s': %d\n", tz->type, cdev->type, ret); + c->lower = cooling_spec.args[0]; + c->upper = cooling_spec.args[1]; + c->weight = weight; - return ret; + return true; } -static int thermal_of_for_each_cooling_device(struct device_node *tz_np, struct device_node *map_np, - struct thermal_zone_device *tz, struct thermal_cooling_device *cdev, - int (*action)(struct device_node *, int, int, - struct thermal_zone_device *, struct thermal_cooling_device *)) -{ - struct device_node *tr_np; - int count, i, trip_id; - - tr_np = of_parse_phandle(map_np, "trip", 0); - if (!tr_np) - return -ENODEV; - - trip_id = of_find_trip_id(tz_np, tr_np); - if (trip_id < 0) - return trip_id; - - count = of_count_phandle_with_args(map_np, "cooling-device", "#cooling-cells"); - if (count <= 0) { - pr_err("Add a cooling_device property with at least one device\n"); - return -ENOENT; - } - - /* - * At this point, we don't want to bail out when there is an - * error, we will try to bind/unbind as many as possible - * cooling devices - */ - for (i = 0; i < count; i++) - action(map_np, i, trip_id, tz, cdev); - - return 0; -} - -static int thermal_of_for_each_cooling_maps(struct thermal_zone_device *tz, - struct thermal_cooling_device *cdev, - int (*action)(struct device_node *, int, int, - struct thermal_zone_device *, struct thermal_cooling_device *)) +static bool thermal_of_should_bind(struct thermal_zone_device *tz, + const struct thermal_trip *trip, + struct thermal_cooling_device *cdev, + struct cooling_spec *c) { struct device_node *tz_np, *cm_np, *child; - int ret = 0; + bool result = false; tz_np = thermal_of_zone_get_by_name(tz); if (IS_ERR(tz_np)) { pr_err("Failed to get node tz by name\n"); - return PTR_ERR(tz_np); + return false; } cm_np = of_get_child_by_name(tz_np, "cooling-maps"); if (!cm_np) goto out; + /* Look up the trip and the cdev in the cooling maps. */ for_each_child_of_node(cm_np, child) { - ret = thermal_of_for_each_cooling_device(tz_np, child, tz, cdev, action); - if (ret) { - of_node_put(child); - break; + struct device_node *tr_np; + int count, i; + + tr_np = of_parse_phandle(child, "trip", 0); + if (tr_np != trip->priv) + continue; + + /* The trip has been found, look up the cdev. */ + count = of_count_phandle_with_args(child, "cooling-device", "#cooling-cells"); + if (count <= 0) + pr_err("Add a cooling_device property with at least one device\n"); + + for (i = 0; i < count; i++) { + result = thermal_of_get_cooling_spec(child, i, cdev, c); + if (result) + break; } + + of_node_put(child); + break; } of_node_put(cm_np); out: of_node_put(tz_np); - return ret; -} - -static int thermal_of_bind(struct thermal_zone_device *tz, - struct thermal_cooling_device *cdev) -{ - return thermal_of_for_each_cooling_maps(tz, cdev, __thermal_of_bind); -} - -static int thermal_of_unbind(struct thermal_zone_device *tz, - struct thermal_cooling_device *cdev) -{ - return thermal_of_for_each_cooling_maps(tz, cdev, __thermal_of_unbind); + return result; } /** @@ -504,8 +414,7 @@ static struct thermal_zone_device *thermal_of_zone_register(struct device_node * thermal_of_parameters_init(np, &tzp); - of_ops.bind = thermal_of_bind; - of_ops.unbind = thermal_of_unbind; + of_ops.should_bind = thermal_of_should_bind; ret = of_property_read_string(np, "critical-action", &action); if (!ret) diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c index 72b302bf914e..1838aa729bb5 100644 --- a/drivers/thermal/thermal_sysfs.c +++ b/drivers/thermal/thermal_sysfs.c @@ -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,51 +79,58 @@ mode_store(struct device *dev, struct device_attribute *attr, return count; } +#define thermal_trip_of_attr(_ptr_, _attr_) \ + ({ \ + struct thermal_trip_desc *td; \ + \ + td = container_of(_ptr_, struct thermal_trip_desc, \ + trip_attrs._attr_.attr); \ + &td->trip; \ + }) + static ssize_t trip_point_type_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct thermal_zone_device *tz = to_thermal_zone(dev); - int trip_id; + struct thermal_trip *trip = thermal_trip_of_attr(attr, type); - if (sscanf(attr->attr.name, "trip_point_%d_type", &trip_id) != 1) - return -EINVAL; - - return sprintf(buf, "%s\n", thermal_trip_type_name(tz->trips[trip_id].trip.type)); + return sprintf(buf, "%s\n", thermal_trip_type_name(trip->type)); } static ssize_t trip_point_temp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + struct thermal_trip *trip = thermal_trip_of_attr(attr, temp); struct thermal_zone_device *tz = to_thermal_zone(dev); - struct thermal_trip *trip; - int trip_id, ret; - int temp; + int ret, temp; ret = kstrtoint(buf, 10, &temp); if (ret) return -EINVAL; - if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip_id) != 1) - return -EINVAL; - mutex_lock(&tz->lock); - trip = &tz->trips[trip_id].trip; - - if (temp != trip->temperature) { - if (tz->ops.set_trip_temp) { - ret = tz->ops.set_trip_temp(tz, trip, temp); - if (ret) - goto unlock; - } + if (temp == trip->temperature) + goto unlock; - thermal_zone_set_trip_temp(tz, trip, temp); + /* Arrange the condition to avoid integer overflows. */ + if (temp != THERMAL_TEMP_INVALID && + temp <= trip->hysteresis + THERMAL_TEMP_INVALID) { + ret = -EINVAL; + goto unlock; + } - __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED); + if (tz->ops.set_trip_temp) { + ret = tz->ops.set_trip_temp(tz, trip, temp); + if (ret) + goto unlock; } + 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; - - if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip_id) != 1) - return -EINVAL; + struct thermal_trip *trip = thermal_trip_of_attr(attr, temp); - 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); + goto unlock; + } - thermal_zone_trip_updated(tz, trip); + 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); - - sysfs_attr_init(&tz->trip_temp_attrs[indx].attr.attr); - tz->trip_temp_attrs[indx].attr.attr.name = - tz->trip_temp_attrs[indx].name; - tz->trip_temp_attrs[indx].attr.attr.mode = S_IRUGO; - tz->trip_temp_attrs[indx].attr.show = trip_point_temp_show; + snprintf(trip_attrs->temp.name, THERMAL_NAME_LENGTH, + "trip_point_%d_temp", i); + + sysfs_attr_init(&trip_attrs->temp.attr.attr); + trip_attrs->temp.attr.attr.name = trip_attrs->temp.name; + trip_attrs->temp.attr.attr.mode = S_IRUGO; + trip_attrs->temp.attr.show = trip_point_temp_show; if (td->trip.flags & THERMAL_TRIP_FLAG_RW_TEMP) { - tz->trip_temp_attrs[indx].attr.attr.mode |= S_IWUSR; - tz->trip_temp_attrs[indx].attr.store = - trip_point_temp_store; + trip_attrs->temp.attr.attr.mode |= S_IWUSR; + trip_attrs->temp.attr.store = trip_point_temp_store; } - attrs[indx + tz->num_trips] = &tz->trip_temp_attrs[indx].attr.attr; + attrs[i + tz->num_trips] = &trip_attrs->temp.attr.attr; - snprintf(tz->trip_hyst_attrs[indx].name, THERMAL_NAME_LENGTH, - "trip_point_%d_hyst", indx); + snprintf(trip_attrs->hyst.name, THERMAL_NAME_LENGTH, + "trip_point_%d_hyst", i); - sysfs_attr_init(&tz->trip_hyst_attrs[indx].attr.attr); - tz->trip_hyst_attrs[indx].attr.attr.name = - tz->trip_hyst_attrs[indx].name; - tz->trip_hyst_attrs[indx].attr.attr.mode = S_IRUGO; - tz->trip_hyst_attrs[indx].attr.show = trip_point_hyst_show; + sysfs_attr_init(&trip_attrs->hyst.attr.attr); + trip_attrs->hyst.attr.attr.name = trip_attrs->hyst.name; + trip_attrs->hyst.attr.attr.mode = S_IRUGO; + trip_attrs->hyst.attr.show = trip_point_hyst_show; if (td->trip.flags & THERMAL_TRIP_FLAG_RW_HYST) { - tz->trip_hyst_attrs[indx].attr.attr.mode |= S_IWUSR; - tz->trip_hyst_attrs[indx].attr.store = - trip_point_hyst_store; + trip_attrs->hyst.attr.attr.mode |= S_IWUSR; + trip_attrs->hyst.attr.store = trip_point_hyst_store; } - attrs[indx + tz->num_trips * 2] = - &tz->trip_hyst_attrs[indx].attr.attr; + attrs[i + 2 * tz->num_trips] = &trip_attrs->hyst.attr.attr; + i++; } attrs[tz->num_trips * 3] = NULL; @@ -479,13 +459,8 @@ static int create_trip_attrs(struct thermal_zone_device *tz) */ static void destroy_trip_attrs(struct thermal_zone_device *tz) { - if (!tz) - return; - - kfree(tz->trip_type_attrs); - kfree(tz->trip_temp_attrs); - kfree(tz->trip_hyst_attrs); - kfree(tz->trips_attribute_group.attrs); + if (tz) + kfree(tz->trips_attribute_group.attrs); } int thermal_zone_create_device_groups(struct thermal_zone_device *tz) @@ -887,13 +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; } diff --git a/drivers/thermal/thermal_trip.c b/drivers/thermal/thermal_trip.c index 06a0554ddc38..b53fac333ec5 100644 --- a/drivers/thermal/thermal_trip.c +++ b/drivers/thermal/thermal_trip.c @@ -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, diff --git a/drivers/thermal/ti-soc-thermal/ti-bandgap.h b/drivers/thermal/ti-soc-thermal/ti-bandgap.h index 1f4bbaf31675..46263c1da8b6 100644 --- a/drivers/thermal/ti-soc-thermal/ti-bandgap.h +++ b/drivers/thermal/ti-soc-thermal/ti-bandgap.h @@ -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, |