From: Thierry Reding <[email protected]>
Drivers for cooling devices can implement these operations when they
need to perform extra work at the time when a cooling device is bound to
a given thermal zone.
Signed-off-by: Thierry Reding <[email protected]>
---
drivers/thermal/thermal_core.c | 11 +++++++++++
include/linux/thermal.h | 6 ++++++
2 files changed, 17 insertions(+)
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index 0675df54c8e6..627fccd100ef 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -694,6 +694,14 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
mutex_unlock(&cdev->lock);
mutex_unlock(&tz->lock);
+ if (cdev->ops->bind) {
+ result = cdev->ops->bind(cdev, tz, trip, upper, lower, weight);
+ if (result < 0) {
+ pr_err("failed to bind %s to %s: %d\n", cdev->type,
+ tz->type, result);
+ }
+ }
+
if (!result)
return 0;
@@ -730,6 +738,9 @@ int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
{
struct thermal_instance *pos, *next;
+ if (cdev->ops->unbind)
+ cdev->ops->unbind(cdev, tz, trip);
+
mutex_lock(&tz->lock);
mutex_lock(&cdev->lock);
list_for_each_entry_safe(pos, next, &tz->thermal_instances, tz_node) {
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 2bb4bf33f4f3..95ed8a0cbba8 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -89,6 +89,12 @@ struct thermal_trip {
};
struct thermal_cooling_device_ops {
+ int (*bind)(struct thermal_cooling_device *cdev,
+ struct thermal_zone_device *tz, int trip_id,
+ unsigned long upper, unsigned long lower,
+ unsigned int weight);
+ void (*unbind)(struct thermal_cooling_device *cdev,
+ struct thermal_zone_device *tz, int trip_id);
int (*get_max_state) (struct thermal_cooling_device *, unsigned long *);
int (*get_cur_state) (struct thermal_cooling_device *, unsigned long *);
int (*set_cur_state) (struct thermal_cooling_device *, unsigned long);
--
2.39.1
From: Thierry Reding <[email protected]>
The SOCTHERM hardware found on Tegra implements a way of throttling the
CPU and GPU when a given temperature threshold is reached. As opposed to
traditional cooling devices, the programming for this happens during the
initialization stage rather than dynamically at runtime when the thermal
framework gets notified of thresholds being crossed.
Use the newly introduced ->bind() and ->unbind() operations to make sure
the SOCTHERM programming happens at the right time. This allows us to
get rid of calls to the get_thermal_instance() helper which is not
supposed to be accessed by drivers.
Reported-by: Daniel Lezcano <[email protected]>
Signed-off-by: Thierry Reding <[email protected]>
---
drivers/thermal/tegra/soctherm.c | 146 ++++++++++++++-----------------
1 file changed, 67 insertions(+), 79 deletions(-)
diff --git a/drivers/thermal/tegra/soctherm.c b/drivers/thermal/tegra/soctherm.c
index 220873298d77..cdc8764e88aa 100644
--- a/drivers/thermal/tegra/soctherm.c
+++ b/drivers/thermal/tegra/soctherm.c
@@ -303,6 +303,8 @@ struct tegra_thermctl_zone {
struct tegra_soctherm *ts;
struct thermal_zone_device *tz;
const struct tegra_tsensor_group *sg;
+ /* instance of an internal throttle cooling device */
+ struct thermal_cooling_device *cdev;
};
struct soctherm_oc_cfg {
@@ -315,6 +317,7 @@ struct soctherm_oc_cfg {
};
struct soctherm_throt_cfg {
+ struct tegra_soctherm *soctherm;
const char *name;
unsigned int id;
u8 priority;
@@ -585,10 +588,10 @@ static int tsensor_group_thermtrip_get(struct tegra_soctherm *ts, int id)
static int tegra_thermctl_set_trip_temp(struct thermal_zone_device *tz, int trip_id, int temp)
{
struct tegra_thermctl_zone *zone = tz->devdata;
- struct tegra_soctherm *ts = zone->ts;
- struct thermal_trip trip;
const struct tegra_tsensor_group *sg = zone->sg;
+ struct tegra_soctherm *ts = zone->ts;
struct device *dev = zone->dev;
+ struct thermal_trip trip;
int ret;
if (!tz)
@@ -610,26 +613,14 @@ static int tegra_thermctl_set_trip_temp(struct thermal_zone_device *tz, int trip
return 0;
} else if (trip.type == THERMAL_TRIP_HOT) {
- int i;
-
- for (i = 0; i < THROTTLE_SIZE; i++) {
- struct thermal_cooling_device *cdev;
- struct soctherm_throt_cfg *stc;
-
- if (!ts->throt_cfgs[i].init)
- continue;
-
- cdev = ts->throt_cfgs[i].cdev;
- if (get_thermal_instance(tz, cdev, trip_id))
- stc = find_throttle_cfg_by_name(ts, cdev->type);
- else
- continue;
+ if (zone->cdev) {
+ struct soctherm_throt_cfg *stc = zone->cdev->devdata;
return throttrip_program(dev, sg, stc, temp);
}
}
- return 0;
+ return ret;
}
static void thermal_irq_enable(struct tegra_thermctl_zone *zn)
@@ -687,26 +678,6 @@ 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)
-{
- int i, ret;
- struct thermal_trip trip;
-
- for (i = 0; i < thermal_zone_get_num_trips(tz); i++) {
-
- ret = thermal_zone_get_trip(tz, i, &trip);
- if (ret)
- return -EINVAL;
-
- if (trip.type == THERMAL_TRIP_HOT) {
- *trip_id = i;
- return 0;
- }
- }
-
- return -EINVAL;
-}
-
/**
* tegra_soctherm_set_hwtrips() - set HW trip point from DT data
* @dev: struct device * of the SOC_THERM instance
@@ -736,8 +707,7 @@ static int tegra_soctherm_set_hwtrips(struct device *dev,
struct thermal_zone_device *tz)
{
struct tegra_soctherm *ts = dev_get_drvdata(dev);
- struct soctherm_throt_cfg *stc;
- int i, trip, temperature, ret;
+ int temperature, ret;
/* Get thermtrips. If missing, try to get critical trips. */
temperature = tsensor_group_thermtrip_get(ts, sg->id);
@@ -754,42 +724,6 @@ 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) {
- dev_info(dev, "throttrip: %s: missing hot temperature\n",
- sg->name);
- return 0;
- }
-
- for (i = 0; i < THROTTLE_OC1; i++) {
- struct thermal_cooling_device *cdev;
-
- if (!ts->throt_cfgs[i].init)
- continue;
-
- cdev = ts->throt_cfgs[i].cdev;
- if (get_thermal_instance(tz, cdev, trip))
- stc = find_throttle_cfg_by_name(ts, cdev->type);
- else
- continue;
-
- ret = throttrip_program(dev, sg, stc, temperature);
- if (ret) {
- dev_err(dev, "throttrip: %s: error during enable\n",
- sg->name);
- return ret;
- }
-
- dev_info(dev,
- "throttrip: will throttle when %s reaches %d mC\n",
- sg->name, temperature);
- break;
- }
-
- if (i == THROTTLE_SIZE)
- dev_info(dev, "throttrip: %s: missing throttle cdev\n",
- sg->name);
-
return 0;
}
@@ -1497,6 +1431,55 @@ static int soctherm_clk_enable(struct platform_device *pdev, bool enable)
return 0;
}
+static int throt_bind(struct thermal_cooling_device *cdev,
+ struct thermal_zone_device *tz, int trip_id,
+ unsigned long upper, unsigned long lower,
+ unsigned int weight)
+{
+ struct tegra_thermctl_zone *zone = tz->devdata;
+ struct device *dev = &cdev->device;
+ struct thermal_trip trip;
+ int err;
+
+ err = thermal_zone_get_trip(tz, trip_id, &trip);
+ if (err < 0)
+ return err;
+
+ if (trip.type == THERMAL_TRIP_HOT) {
+ struct soctherm_throt_cfg *stc = cdev->devdata;
+
+ err = throttrip_program(zone->dev, zone->sg, stc, trip.temperature);
+ if (err < 0) {
+ dev_err(dev, "throttrip: %s: error during enable\n",
+ zone->sg->name);
+ return err;
+ }
+
+ dev_info(dev, "throttrip: will throttle when %s reaches %d mC\n",
+ zone->sg->name, trip.temperature);
+
+ /* keep a reference to this for ->set_trip_temp() */
+ zone->cdev = cdev;
+ }
+
+ return 0;
+}
+
+static void throt_unbind(struct thermal_cooling_device *cdev,
+ struct thermal_zone_device *tz, int trip_id)
+{
+ struct tegra_thermctl_zone *zone = tz->devdata;
+ struct thermal_trip trip;
+ int err;
+
+ err = __thermal_zone_get_trip(tz, trip_id, &trip);
+ if (err < 0)
+ return;
+
+ if (trip.type == THERMAL_TRIP_HOT)
+ zone->cdev = NULL;
+}
+
static int throt_get_cdev_max_state(struct thermal_cooling_device *cdev,
unsigned long *max_state)
{
@@ -1507,7 +1490,8 @@ static int throt_get_cdev_max_state(struct thermal_cooling_device *cdev,
static int throt_get_cdev_cur_state(struct thermal_cooling_device *cdev,
unsigned long *cur_state)
{
- struct tegra_soctherm *ts = cdev->devdata;
+ struct soctherm_throt_cfg *stc = cdev->devdata;
+ struct tegra_soctherm *ts = stc->soctherm;
u32 r;
r = readl(ts->regs + THROT_STATUS);
@@ -1526,6 +1510,8 @@ static int throt_set_cdev_state(struct thermal_cooling_device *cdev,
}
static const struct thermal_cooling_device_ops throt_cooling_ops = {
+ .bind = throt_bind,
+ .unbind = throt_unbind,
.get_max_state = throt_get_cdev_max_state,
.get_cur_state = throt_get_cdev_cur_state,
.set_cur_state = throt_set_cdev_state,
@@ -1576,8 +1562,8 @@ static int soctherm_thermtrips_parse(struct platform_device *pdev)
}
static void soctherm_oc_cfg_parse(struct device *dev,
- struct device_node *np_oc,
- struct soctherm_throt_cfg *stc)
+ struct device_node *np_oc,
+ struct soctherm_throt_cfg *stc)
{
u32 val;
@@ -1694,13 +1680,15 @@ static void soctherm_init_hw_throt_cdev(struct platform_device *pdev)
if (err)
continue;
+ stc->soctherm = ts;
+
if (stc->id >= THROTTLE_OC1) {
soctherm_oc_cfg_parse(dev, np_stcc, stc);
stc->init = true;
} else {
tcd = thermal_of_cooling_device_register(np_stcc,
- (char *)name, ts,
+ (char *)name, stc,
&throt_cooling_ops);
if (IS_ERR_OR_NULL(tcd)) {
dev_err(dev,
--
2.39.1
On Thu, Feb 09, 2023 at 05:35:54PM +0100, Thierry Reding wrote:
> From: Thierry Reding <[email protected]>
>
> Drivers for cooling devices can implement these operations when they
> need to perform extra work at the time when a cooling device is bound to
> a given thermal zone.
The approach is not correct.
I'll react to the initial email:
Re: thermal/drivers/tegra: Getting rid of the get_thermal_instance() usage
https://lore.kernel.org/all/Y9J4WAFyXyV%2FnqlG@orome/
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs
Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog