diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2024-08-29 12:45:08 +0300 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2024-08-29 12:45:08 +0300 |
commit | f5c05974349c8e3d80e125b71bd80695807d8528 (patch) | |
tree | 59213ecb6cbc5956f042c31587bae98d546de7f2 /drivers | |
parent | c0a1ef9c5be72ff28a5413deb1b3e1a066593c13 (diff) | |
parent | e9654659fe3c5387f9eafa861d6cc2ad19dd3c5c (diff) | |
download | linux-f5c05974349c8e3d80e125b71bd80695807d8528.tar.xz |
Merge branch 'thermal-core'
Merge thermal core updates for 6.12 which, among other things, rework
the thermal driver interface for binding cooling devices to thermal
zones and add a thermal core testing module:
- Update some thermal drivers to eliminate thermal_zone_get_trip()
calls from them and get rid of that function (Rafael Wysocki).
- Update the thermal sysfs code to store trip point attributes in trip
descriptors and get to trip points via attribute pointers (Rafael
Wysocki).
- Move the computation of the low and high boundaries for
thermal_zone_set_trips() to __thermal_zone_device_update() (Daniel
Lezcano).
- Introduce a debugfs-based facility for thermal core testing (Rafael
Wysocki).
- Replace the thermal zone .bind() and .unbind() callbacks for binding
cooling devices to thermal zones with one .should_bind() callback
used for deciding whether or not a given cooling devices should be
bound to a given trip point in a given thermal zone (Rafael Wysocki).
- Eliminate code that has no more users after the other changes, drop
some redundant checks from the thermal core and clean it up (Rafael
Wysocki).
- Fix rounding of delay jiffies in the thermal core (Rafael Wysocki).
* thermal-core: (31 commits)
thermal: core: Drop tz field from struct thermal_instance
thermal: core: Drop redundant checks from thermal_bind_cdev_to_trip()
thermal: core: Rename cdev-to-thermal-zone bind/unbind functions
thermal: core: Fix rounding of delay jiffies
thermal: core: Clean up trip bind/unbind functions
thermal: core: Drop unused bind/unbind functions and callbacks
thermal/of: Use the .should_bind() thermal zone callback
thermal: imx: Use the .should_bind() thermal zone callback
mlxsw: core_thermal: Use the .should_bind() thermal zone callback
platform/x86: acerhdf: Use the .should_bind() thermal zone callback
thermal: core: Unexport thermal_bind_cdev_to_trip() and thermal_unbind_cdev_from_trip()
thermal: ACPI: Use the .should_bind() thermal zone callback
thermal: core: Introduce .should_bind() thermal zone callback
thermal: core: Move thermal zone locking out of bind/unbind functions
thermal: sysfs: Use the dev argument in instance-related show/store
thermal: core: Drop redundant thermal instance checks
thermal: core: Rearrange checks in thermal_bind_cdev_to_trip()
thermal: core: Fold two functions into their respective callers
thermal: Introduce a debugfs-based testing facility
thermal/core: Compute low and high boundaries in thermal_zone_device_update()
...
Diffstat (limited to 'drivers')
22 files changed, 1062 insertions, 734 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 d61478c0c632..0c50a0cc316d 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, }; @@ -451,6 +389,7 @@ mlxsw_thermal_module_init(struct device *dev, struct mlxsw_core *core, struct mlxsw_thermal_area *area, u8 module) { struct mlxsw_thermal_module *module_tz; + int i; module_tz = &area->tz_module_arr[module]; /* Skip if parent is already set (case of port split). */ @@ -465,6 +404,8 @@ mlxsw_thermal_module_init(struct device *dev, struct mlxsw_core *core, sizeof(thermal->trips)); memcpy(module_tz->cooling_states, default_cooling_states, sizeof(thermal->cooling_states)); + for (i = 0; i < MLXSW_THERMAL_NUM_TRIPS; i++) + module_tz->trips[i].priv = &module_tz->cooling_states[i]; } static void mlxsw_thermal_module_fini(struct mlxsw_thermal_module *module_tz) @@ -579,7 +520,7 @@ mlxsw_thermal_gearboxes_init(struct device *dev, struct mlxsw_core *core, struct mlxsw_thermal_module *gearbox_tz; char mgpir_pl[MLXSW_REG_MGPIR_LEN]; u8 gbox_num; - int i; + int i, j; int err; mlxsw_reg_mgpir_pack(mgpir_pl, area->slot_index); @@ -606,6 +547,9 @@ mlxsw_thermal_gearboxes_init(struct device *dev, struct mlxsw_core *core, sizeof(thermal->trips)); memcpy(gearbox_tz->cooling_states, default_cooling_states, sizeof(thermal->cooling_states)); + for (j = 0; j < MLXSW_THERMAL_NUM_TRIPS; j++) + gearbox_tz->trips[j].priv = &gearbox_tz->cooling_states[j]; + gearbox_tz->module = i; gearbox_tz->parent = thermal; gearbox_tz->slot_index = area->slot_index; @@ -722,6 +666,9 @@ int mlxsw_thermal_init(struct mlxsw_core *core, thermal->bus_info = bus_info; memcpy(thermal->trips, default_thermal_trips, sizeof(thermal->trips)); memcpy(thermal->cooling_states, default_cooling_states, sizeof(thermal->cooling_states)); + for (i = 0; i < MLXSW_THERMAL_NUM_TRIPS; i++) + thermal->trips[i].priv = &thermal->cooling_states[i]; + thermal->line_cards[0].slot_index = 0; err = mlxsw_reg_query(thermal->core, MLXSW_REG(mfcr), mfcr_pl); 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..55acbd141560 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 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/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_thermal.c b/drivers/thermal/imx_thermal.c index 091fb30dedf3..8da10265c221 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, 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/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/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..4187f207bce9 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -323,11 +323,15 @@ static void thermal_zone_broken_disable(struct thermal_zone_device *tz) static void thermal_zone_device_set_polling(struct thermal_zone_device *tz, unsigned long delay) { - if (delay) - mod_delayed_work(system_freezable_power_efficient_wq, - &tz->poll_queue, delay); - else + if (!delay) { cancel_delayed_work(&tz->poll_queue); + return; + } + + if (delay > HZ) + delay = round_jiffies_relative(delay); + + mod_delayed_work(system_freezable_power_efficient_wq, &tz->poll_queue, delay); } static void thermal_zone_recheck(struct thermal_zone_device *tz, int error) @@ -547,6 +551,7 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, struct thermal_trip_desc *td; LIST_HEAD(way_down_list); LIST_HEAD(way_up_list); + int low = -INT_MAX, high = INT_MAX; int temp, ret; if (tz->suspended) @@ -580,10 +585,17 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, tz->notify_event = event; - for_each_trip_desc(tz, td) + for_each_trip_desc(tz, td) { handle_thermal_trip(tz, td, &way_up_list, &way_down_list); - thermal_zone_set_trips(tz); + if (td->threshold <= tz->temperature && td->threshold > low) + low = td->threshold; + + if (td->threshold >= tz->temperature && td->threshold < high) + high = td->threshold; + } + + thermal_zone_set_trips(tz, low, high); list_sort(NULL, &way_up_list, thermal_trip_notify_cmp); list_for_each_entry(td, &way_up_list, notify_list_node) @@ -757,15 +769,7 @@ struct thermal_zone_device *thermal_zone_get_by_id(int id) * @tz: pointer to struct thermal_zone_device * @trip: trip point the cooling devices is associated with in this zone. * @cdev: pointer to struct thermal_cooling_device - * @upper: the Maximum cooling state for this trip point. - * THERMAL_NO_LIMIT means no upper limit, - * and the cooling device can be in max_state. - * @lower: the Minimum cooling state can be used for this trip point. - * THERMAL_NO_LIMIT means no lower limit, - * and the cooling device can be in cooling state 0. - * @weight: The weight of the cooling device to be bound to the - * thermal zone. Use THERMAL_WEIGHT_DEFAULT for the - * default value + * @cool_spec: cooling specification for @trip and @cdev * * This interface function bind a thermal cooling device to the certain trip * point of a thermal zone device. @@ -773,55 +777,41 @@ struct thermal_zone_device *thermal_zone_get_by_id(int id) * * Return: 0 on success, the proper error value otherwise. */ -int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, +static int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, const struct thermal_trip *trip, struct thermal_cooling_device *cdev, - unsigned long upper, unsigned long lower, - unsigned int weight) + struct cooling_spec *cool_spec) { struct thermal_instance *dev; struct thermal_instance *pos; - struct thermal_zone_device *pos1; - struct thermal_cooling_device *pos2; bool upper_no_limit; int result; - list_for_each_entry(pos1, &thermal_tz_list, node) { - if (pos1 == tz) - break; - } - list_for_each_entry(pos2, &thermal_cdev_list, node) { - if (pos2 == cdev) - break; - } - - if (tz != pos1 || cdev != pos2) - return -EINVAL; - /* lower default 0, upper default max_state */ - lower = lower == THERMAL_NO_LIMIT ? 0 : lower; + if (cool_spec->lower == THERMAL_NO_LIMIT) + cool_spec->lower = 0; - if (upper == THERMAL_NO_LIMIT) { - upper = cdev->max_state; + if (cool_spec->upper == THERMAL_NO_LIMIT) { + cool_spec->upper = cdev->max_state; upper_no_limit = true; } else { upper_no_limit = false; } - if (lower > upper || upper > cdev->max_state) + if (cool_spec->lower > cool_spec->upper || cool_spec->upper > cdev->max_state) return -EINVAL; dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; - dev->tz = tz; + dev->cdev = cdev; dev->trip = trip; - dev->upper = upper; + dev->upper = cool_spec->upper; dev->upper_no_limit = upper_no_limit; - dev->lower = lower; + dev->lower = cool_spec->lower; dev->target = THERMAL_NO_TARGET; - dev->weight = weight; + dev->weight = cool_spec->weight; result = ida_alloc(&tz->ida, GFP_KERNEL); if (result < 0) @@ -855,10 +845,9 @@ int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, if (result) goto remove_trip_file; - mutex_lock(&tz->lock); mutex_lock(&cdev->lock); list_for_each_entry(pos, &tz->thermal_instances, tz_node) - if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { + if (pos->trip == trip && pos->cdev == cdev) { result = -EEXIST; break; } @@ -870,7 +859,6 @@ int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, thermal_governor_update_tz(tz, THERMAL_TZ_BIND_CDEV); } mutex_unlock(&cdev->lock); - mutex_unlock(&tz->lock); if (!result) return 0; @@ -886,21 +874,6 @@ free_mem: kfree(dev); return result; } -EXPORT_SYMBOL_GPL(thermal_bind_cdev_to_trip); - -int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, - int trip_index, - struct thermal_cooling_device *cdev, - unsigned long upper, unsigned long lower, - unsigned int weight) -{ - if (trip_index < 0 || trip_index >= tz->num_trips) - return -EINVAL; - - return thermal_bind_cdev_to_trip(tz, &tz->trips[trip_index].trip, cdev, - upper, lower, weight); -} -EXPORT_SYMBOL_GPL(thermal_zone_bind_cooling_device); /** * thermal_unbind_cdev_from_trip - unbind a cooling device from a thermal zone. @@ -911,33 +884,30 @@ EXPORT_SYMBOL_GPL(thermal_zone_bind_cooling_device); * This interface function unbind a thermal cooling device from the certain * trip point of a thermal zone device. * This function is usually called in the thermal zone device .unbind callback. - * - * Return: 0 on success, the proper error value otherwise. */ -int thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz, - const struct thermal_trip *trip, - struct thermal_cooling_device *cdev) +static void thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz, + const struct thermal_trip *trip, + struct thermal_cooling_device *cdev) { struct thermal_instance *pos, *next; - mutex_lock(&tz->lock); + lockdep_assert_held(&tz->lock); + mutex_lock(&cdev->lock); list_for_each_entry_safe(pos, next, &tz->thermal_instances, tz_node) { - if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { + if (pos->trip == trip && pos->cdev == cdev) { list_del(&pos->tz_node); list_del(&pos->cdev_node); thermal_governor_update_tz(tz, THERMAL_TZ_UNBIND_CDEV); mutex_unlock(&cdev->lock); - mutex_unlock(&tz->lock); goto unbind; } } mutex_unlock(&cdev->lock); - mutex_unlock(&tz->lock); - return -ENODEV; + return; unbind: device_remove_file(&tz->device, &pos->weight_attr); @@ -945,20 +915,7 @@ unbind: sysfs_remove_link(&tz->device.kobj, pos->name); ida_free(&tz->ida, pos->id); kfree(pos); - return 0; -} -EXPORT_SYMBOL_GPL(thermal_unbind_cdev_from_trip); - -int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz, - int trip_index, - struct thermal_cooling_device *cdev) -{ - if (trip_index < 0 || trip_index >= tz->num_trips) - return -EINVAL; - - return thermal_unbind_cdev_from_trip(tz, &tz->trips[trip_index].trip, cdev); } -EXPORT_SYMBOL_GPL(thermal_zone_unbind_cooling_device); static void thermal_release(struct device *dev) { @@ -985,24 +942,41 @@ static struct class *thermal_class; static inline void print_bind_err_msg(struct thermal_zone_device *tz, + const struct thermal_trip *trip, struct thermal_cooling_device *cdev, int ret) { - dev_err(&tz->device, "binding zone %s with cdev %s failed:%d\n", - tz->type, cdev->type, ret); + dev_err(&tz->device, "binding cdev %s to trip %d failed: %d\n", + cdev->type, thermal_zone_trip_id(tz, trip), ret); } -static void bind_cdev(struct thermal_cooling_device *cdev) +static void thermal_zone_cdev_bind(struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev) { - int ret; - struct thermal_zone_device *pos = NULL; + struct thermal_trip_desc *td; - list_for_each_entry(pos, &thermal_tz_list, node) { - if (pos->ops.bind) { - ret = pos->ops.bind(pos, cdev); - if (ret) - print_bind_err_msg(pos, cdev, ret); - } + if (!tz->ops.should_bind) + return; + + mutex_lock(&tz->lock); + + for_each_trip_desc(tz, td) { + struct thermal_trip *trip = &td->trip; + struct cooling_spec c = { + .upper = THERMAL_NO_LIMIT, + .lower = THERMAL_NO_LIMIT, + .weight = THERMAL_WEIGHT_DEFAULT + }; + int ret; + + if (!tz->ops.should_bind(tz, trip, cdev, &c)) + continue; + + ret = thermal_bind_cdev_to_trip(tz, trip, cdev, &c); + if (ret) + print_bind_err_msg(tz, trip, cdev, ret); } + + mutex_unlock(&tz->lock); } /** @@ -1100,7 +1074,8 @@ __thermal_cooling_device_register(struct device_node *np, list_add(&cdev->node, &thermal_cdev_list); /* Update binding information for 'this' new cdev */ - bind_cdev(cdev); + list_for_each_entry(pos, &thermal_tz_list, node) + thermal_zone_cdev_bind(pos, cdev); list_for_each_entry(pos, &thermal_tz_list, node) if (atomic_cmpxchg(&pos->need_update, 1, 0)) @@ -1301,6 +1276,19 @@ unlock_list: } EXPORT_SYMBOL_GPL(thermal_cooling_device_update); +static void thermal_zone_cdev_unbind(struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev) +{ + struct thermal_trip_desc *td; + + mutex_lock(&tz->lock); + + for_each_trip_desc(tz, td) + thermal_unbind_cdev_from_trip(tz, &td->trip, cdev); + + mutex_unlock(&tz->lock); +} + /** * thermal_cooling_device_unregister - removes a thermal cooling device * @cdev: the thermal cooling device to remove. @@ -1327,10 +1315,8 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) list_del(&cdev->node); /* Unbind all thermal zones associated with 'this' cdev */ - list_for_each_entry(tz, &thermal_tz_list, node) { - if (tz->ops.unbind) - tz->ops.unbind(tz, cdev); - } + list_for_each_entry(tz, &thermal_tz_list, node) + thermal_zone_cdev_unbind(tz, cdev); mutex_unlock(&thermal_list_lock); @@ -1338,32 +1324,6 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) } EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister); -static void bind_tz(struct thermal_zone_device *tz) -{ - int ret; - struct thermal_cooling_device *pos = NULL; - - if (!tz->ops.bind) - return; - - mutex_lock(&thermal_list_lock); - - list_for_each_entry(pos, &thermal_cdev_list, node) { - ret = tz->ops.bind(tz, pos); - if (ret) - print_bind_err_msg(tz, pos, ret); - } - - mutex_unlock(&thermal_list_lock); -} - -static void thermal_set_delay_jiffies(unsigned long *delay_jiffies, int delay_ms) -{ - *delay_jiffies = msecs_to_jiffies(delay_ms); - if (delay_ms > 1000) - *delay_jiffies = round_jiffies(*delay_jiffies); -} - int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp) { const struct thermal_trip_desc *td; @@ -1424,6 +1384,7 @@ thermal_zone_device_register_with_trips(const char *type, unsigned int polling_delay) { const struct thermal_trip *trip = trips; + struct thermal_cooling_device *cdev; struct thermal_zone_device *tz; struct thermal_trip_desc *td; int id; @@ -1447,7 +1408,7 @@ thermal_zone_device_register_with_trips(const char *type, } if (!ops || !ops->get_temp) { - pr_err("Thermal zone device ops not defined\n"); + pr_err("Thermal zone device ops not defined or invalid\n"); return ERR_PTR(-EINVAL); } @@ -1509,8 +1470,8 @@ thermal_zone_device_register_with_trips(const char *type, td->threshold = INT_MAX; } - thermal_set_delay_jiffies(&tz->passive_delay_jiffies, passive_delay); - thermal_set_delay_jiffies(&tz->polling_delay_jiffies, polling_delay); + tz->polling_delay_jiffies = msecs_to_jiffies(polling_delay); + tz->passive_delay_jiffies = msecs_to_jiffies(passive_delay); tz->recheck_delay_jiffies = THERMAL_RECHECK_DELAY; /* sys I/F */ @@ -1554,13 +1515,16 @@ thermal_zone_device_register_with_trips(const char *type, } mutex_lock(&thermal_list_lock); + mutex_lock(&tz->lock); list_add_tail(&tz->node, &thermal_tz_list); mutex_unlock(&tz->lock); - mutex_unlock(&thermal_list_lock); /* Bind cooling devices for this zone */ - bind_tz(tz); + list_for_each_entry(cdev, &thermal_cdev_list, node) + thermal_zone_cdev_bind(tz, cdev); + + mutex_unlock(&thermal_list_lock); thermal_zone_device_init(tz); /* Update the new thermal zone and mark it as already updated. */ @@ -1652,8 +1616,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) /* Unbind all cdevs associated with 'this' thermal zone */ list_for_each_entry(cdev, &thermal_cdev_list, node) - if (tz->ops.unbind) - tz->ops.unbind(tz, cdev); + thermal_zone_cdev_unbind(tz, cdev); mutex_unlock(&thermal_list_lock); diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 4cf2b7230d04..9b19b614a1bc 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); 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..b740b60032ee 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> @@ -78,39 +79,38 @@ mode_store(struct device *dev, struct device_attribute *attr, return count; } +#define thermal_trip_of_attr(_ptr_, _attr_) \ + ({ \ + struct thermal_trip_desc *td; \ + \ + td = container_of(_ptr_, struct thermal_trip_desc, \ + trip_attrs._attr_.attr); \ + &td->trip; \ + }) + static ssize_t trip_point_type_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct thermal_zone_device *tz = to_thermal_zone(dev); - int trip_id; - - if (sscanf(attr->attr.name, "trip_point_%d_type", &trip_id) != 1) - return -EINVAL; + struct thermal_trip *trip = thermal_trip_of_attr(attr, type); - return sprintf(buf, "%s\n", thermal_trip_type_name(tz->trips[trip_id].trip.type)); + return sprintf(buf, "%s\n", thermal_trip_type_name(trip->type)); } static ssize_t trip_point_temp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + struct thermal_trip *trip = thermal_trip_of_attr(attr, temp); struct thermal_zone_device *tz = to_thermal_zone(dev); - struct thermal_trip *trip; - int trip_id, ret; - int temp; + int ret, temp; ret = kstrtoint(buf, 10, &temp); if (ret) return -EINVAL; - if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip_id) != 1) - return -EINVAL; - mutex_lock(&tz->lock); - trip = &tz->trips[trip_id].trip; - if (temp != trip->temperature) { if (tz->ops.set_trip_temp) { ret = tz->ops.set_trip_temp(tz, trip, temp); @@ -133,39 +133,29 @@ static ssize_t trip_point_temp_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct thermal_zone_device *tz = to_thermal_zone(dev); - int trip_id; + struct thermal_trip *trip = thermal_trip_of_attr(attr, temp); - if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip_id) != 1) - return -EINVAL; - - return sprintf(buf, "%d\n", READ_ONCE(tz->trips[trip_id].trip.temperature)); + return sprintf(buf, "%d\n", READ_ONCE(trip->temperature)); } static ssize_t trip_point_hyst_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + struct thermal_trip *trip = thermal_trip_of_attr(attr, hyst); struct thermal_zone_device *tz = to_thermal_zone(dev); - struct thermal_trip *trip; - int trip_id, ret; - int hyst; + int ret, hyst; ret = kstrtoint(buf, 10, &hyst); if (ret || hyst < 0) return -EINVAL; - if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip_id) != 1) - return -EINVAL; - mutex_lock(&tz->lock); - trip = &tz->trips[trip_id].trip; - if (hyst != trip->hysteresis) { - WRITE_ONCE(trip->hysteresis, hyst); + thermal_zone_set_trip_hyst(tz, trip, hyst); - thermal_zone_trip_updated(tz, trip); + __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED); } mutex_unlock(&tz->lock); @@ -177,13 +167,9 @@ static ssize_t trip_point_hyst_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct thermal_zone_device *tz = to_thermal_zone(dev); - int trip_id; - - if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip_id) != 1) - return -EINVAL; + struct thermal_trip *trip = thermal_trip_of_attr(attr, hyst); - return sprintf(buf, "%d\n", READ_ONCE(tz->trips[trip_id].trip.hysteresis)); + return sprintf(buf, "%d\n", READ_ONCE(trip->hysteresis)); } static ssize_t @@ -382,87 +368,55 @@ static const struct attribute_group *thermal_zone_attribute_groups[] = { */ static int create_trip_attrs(struct thermal_zone_device *tz) { - const struct thermal_trip_desc *td; + struct thermal_trip_desc *td; struct attribute **attrs; - - /* This function works only for zones with at least one trip */ - if (tz->num_trips <= 0) - return -EINVAL; - - tz->trip_type_attrs = kcalloc(tz->num_trips, sizeof(*tz->trip_type_attrs), - GFP_KERNEL); - if (!tz->trip_type_attrs) - return -ENOMEM; - - tz->trip_temp_attrs = kcalloc(tz->num_trips, sizeof(*tz->trip_temp_attrs), - GFP_KERNEL); - if (!tz->trip_temp_attrs) { - kfree(tz->trip_type_attrs); - return -ENOMEM; - } - - tz->trip_hyst_attrs = kcalloc(tz->num_trips, - sizeof(*tz->trip_hyst_attrs), - GFP_KERNEL); - if (!tz->trip_hyst_attrs) { - kfree(tz->trip_type_attrs); - kfree(tz->trip_temp_attrs); - return -ENOMEM; - } + int i; attrs = kcalloc(tz->num_trips * 3 + 1, sizeof(*attrs), GFP_KERNEL); - if (!attrs) { - kfree(tz->trip_type_attrs); - kfree(tz->trip_temp_attrs); - kfree(tz->trip_hyst_attrs); + if (!attrs) return -ENOMEM; - } + i = 0; for_each_trip_desc(tz, td) { - int indx = thermal_zone_trip_id(tz, &td->trip); + struct thermal_trip_attrs *trip_attrs = &td->trip_attrs; /* create trip type attribute */ - snprintf(tz->trip_type_attrs[indx].name, THERMAL_NAME_LENGTH, - "trip_point_%d_type", indx); + snprintf(trip_attrs->type.name, THERMAL_NAME_LENGTH, + "trip_point_%d_type", i); - sysfs_attr_init(&tz->trip_type_attrs[indx].attr.attr); - tz->trip_type_attrs[indx].attr.attr.name = - tz->trip_type_attrs[indx].name; - tz->trip_type_attrs[indx].attr.attr.mode = S_IRUGO; - tz->trip_type_attrs[indx].attr.show = trip_point_type_show; - attrs[indx] = &tz->trip_type_attrs[indx].attr.attr; + sysfs_attr_init(&trip_attrs->type.attr.attr); + trip_attrs->type.attr.attr.name = trip_attrs->type.name; + trip_attrs->type.attr.attr.mode = S_IRUGO; + trip_attrs->type.attr.show = trip_point_type_show; + attrs[i] = &trip_attrs->type.attr.attr; /* create trip temp attribute */ - snprintf(tz->trip_temp_attrs[indx].name, THERMAL_NAME_LENGTH, - "trip_point_%d_temp", indx); - - sysfs_attr_init(&tz->trip_temp_attrs[indx].attr.attr); - tz->trip_temp_attrs[indx].attr.attr.name = - tz->trip_temp_attrs[indx].name; - tz->trip_temp_attrs[indx].attr.attr.mode = S_IRUGO; - tz->trip_temp_attrs[indx].attr.show = trip_point_temp_show; + snprintf(trip_attrs->temp.name, THERMAL_NAME_LENGTH, + "trip_point_%d_temp", i); + + sysfs_attr_init(&trip_attrs->temp.attr.attr); + trip_attrs->temp.attr.attr.name = trip_attrs->temp.name; + trip_attrs->temp.attr.attr.mode = S_IRUGO; + trip_attrs->temp.attr.show = trip_point_temp_show; if (td->trip.flags & THERMAL_TRIP_FLAG_RW_TEMP) { - tz->trip_temp_attrs[indx].attr.attr.mode |= S_IWUSR; - tz->trip_temp_attrs[indx].attr.store = - trip_point_temp_store; + trip_attrs->temp.attr.attr.mode |= S_IWUSR; + trip_attrs->temp.attr.store = trip_point_temp_store; } - attrs[indx + tz->num_trips] = &tz->trip_temp_attrs[indx].attr.attr; + attrs[i + tz->num_trips] = &trip_attrs->temp.attr.attr; - snprintf(tz->trip_hyst_attrs[indx].name, THERMAL_NAME_LENGTH, - "trip_point_%d_hyst", indx); + snprintf(trip_attrs->hyst.name, THERMAL_NAME_LENGTH, + "trip_point_%d_hyst", i); - sysfs_attr_init(&tz->trip_hyst_attrs[indx].attr.attr); - tz->trip_hyst_attrs[indx].attr.attr.name = - tz->trip_hyst_attrs[indx].name; - tz->trip_hyst_attrs[indx].attr.attr.mode = S_IRUGO; - tz->trip_hyst_attrs[indx].attr.show = trip_point_hyst_show; + sysfs_attr_init(&trip_attrs->hyst.attr.attr); + trip_attrs->hyst.attr.attr.name = trip_attrs->hyst.name; + trip_attrs->hyst.attr.attr.mode = S_IRUGO; + trip_attrs->hyst.attr.show = trip_point_hyst_show; if (td->trip.flags & THERMAL_TRIP_FLAG_RW_HYST) { - tz->trip_hyst_attrs[indx].attr.attr.mode |= S_IWUSR; - tz->trip_hyst_attrs[indx].attr.store = - trip_point_hyst_store; + trip_attrs->hyst.attr.attr.mode |= S_IWUSR; + trip_attrs->hyst.attr.store = trip_point_hyst_store; } - attrs[indx + tz->num_trips * 2] = - &tz->trip_hyst_attrs[indx].attr.attr; + attrs[i + 2 * tz->num_trips] = &trip_attrs->hyst.attr.attr; + i++; } attrs[tz->num_trips * 3] = NULL; @@ -479,13 +433,8 @@ static int create_trip_attrs(struct thermal_zone_device *tz) */ static void destroy_trip_attrs(struct thermal_zone_device *tz) { - if (!tz) - return; - - kfree(tz->trip_type_attrs); - kfree(tz->trip_temp_attrs); - kfree(tz->trip_hyst_attrs); - kfree(tz->trips_attribute_group.attrs); + if (tz) + kfree(tz->trips_attribute_group.attrs); } int thermal_zone_create_device_groups(struct thermal_zone_device *tz) @@ -887,13 +836,12 @@ void thermal_cooling_device_stats_reinit(struct thermal_cooling_device *cdev) ssize_t trip_point_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct thermal_zone_device *tz = to_thermal_zone(dev); struct thermal_instance *instance; - instance = - container_of(attr, struct thermal_instance, attr); + instance = container_of(attr, struct thermal_instance, attr); - return sprintf(buf, "%d\n", - thermal_zone_trip_id(instance->tz, instance->trip)); + return sprintf(buf, "%d\n", thermal_zone_trip_id(tz, instance->trip)); } ssize_t @@ -909,6 +857,7 @@ weight_show(struct device *dev, struct device_attribute *attr, char *buf) ssize_t weight_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + struct thermal_zone_device *tz = to_thermal_zone(dev); struct thermal_instance *instance; int ret, weight; @@ -919,14 +868,13 @@ ssize_t weight_store(struct device *dev, struct device_attribute *attr, instance = container_of(attr, struct thermal_instance, weight_attr); /* Don't race with governors using the 'weight' value */ - mutex_lock(&instance->tz->lock); + mutex_lock(&tz->lock); instance->weight = weight; - thermal_governor_update_tz(instance->tz, - THERMAL_INSTANCE_WEIGHT_CHANGED); + thermal_governor_update_tz(tz, THERMAL_INSTANCE_WEIGHT_CHANGED); - mutex_unlock(&instance->tz->lock); + mutex_unlock(&tz->lock); return count; } 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, |