diff mbox series

[2/2] thermal: tegra: Implement bind/unbind for cooling device

Message ID 20230209163555.1993557-2-thierry.reding@gmail.com (mailing list archive)
State New, archived
Delegated to: Daniel Lezcano
Headers show
Series [1/2] thermal: Add support for cooling device bind/unbind operations | expand

Commit Message

Thierry Reding Feb. 9, 2023, 4:35 p.m. UTC
From: Thierry Reding <treding@nvidia.com>

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 <daniel.lezcano@linaro.org>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/thermal/tegra/soctherm.c | 146 ++++++++++++++-----------------
 1 file changed, 67 insertions(+), 79 deletions(-)
diff mbox series

Patch

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,