From 0a5d1b571ba5bca9c6737572ea4c8b1ad896c3fc Mon Sep 17 00:00:00 2001
From: Anson Huang <Anson.Huang@nxp.com>
Date: Wed, 7 Aug 2019 13:24:26 +0800
Subject: [PATCH] thermal: qoriq: Add device cooling support

Register device cooling for first thermal zone manually, when
temperature exceeds passive trip, system wide cooling notification
will be triggered.

Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
---
 drivers/thermal/qoriq_thermal.c | 80 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 79 insertions(+), 1 deletion(-)

--- a/drivers/thermal/qoriq_thermal.c
+++ b/drivers/thermal/qoriq_thermal.c
@@ -3,6 +3,7 @@
 // Copyright 2016 Freescale Semiconductor, Inc.
 
 #include <linux/clk.h>
+#include <linux/device_cooling.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/err.h>
@@ -14,6 +15,7 @@
 #include "thermal_core.h"
 
 #define SITES_MAX	16
+#define TMU_TEMP_PASSIVE_COOL_DELTA	10000
 
 /*
  * QorIQ TMU Registers
@@ -69,6 +71,9 @@ struct qoriq_sensor {
 	struct thermal_zone_device	*tzd;
 	struct qoriq_tmu_data		*qdata;
 	int				id;
+	int				temp_passive;
+	int				temp_critical;
+	struct thermal_cooling_device 	*cdev;
 };
 
 struct qoriq_tmu_data {
@@ -78,6 +83,12 @@ struct qoriq_tmu_data {
 	struct qoriq_sensor	*sensor[SITES_MAX];
 };
 
+enum tmu_trip {
+	TMU_TRIP_PASSIVE,
+	TMU_TRIP_CRITICAL,
+	TMU_TRIP_NUM,
+};
+
 static void tmu_write(struct qoriq_tmu_data *p, u32 val, void __iomem *addr)
 {
 	if (p->little_endian)
@@ -106,14 +117,51 @@ static int tmu_get_temp(void *p, int *te
 	return 0;
 }
 
+static int tmu_get_trend(void *p, int trip, enum thermal_trend *trend)
+{
+	struct qoriq_sensor *qsensor = p;
+	int trip_temp;
+
+	if (!qsensor->tzd)
+		return 0;
+
+	trip_temp = (trip == TMU_TRIP_PASSIVE) ? qsensor->temp_passive :
+					     qsensor->temp_critical;
+
+	if (qsensor->tzd->temperature >=
+		(trip_temp - TMU_TEMP_PASSIVE_COOL_DELTA))
+		*trend = THERMAL_TREND_RAISE_FULL;
+	else
+		*trend = THERMAL_TREND_DROP_FULL;
+
+	return 0;
+}
+
+static int tmu_set_trip_temp(void *p, int trip,
+			     int temp)
+{
+	struct qoriq_sensor *qsensor = p;
+
+	if (trip == TMU_TRIP_CRITICAL)
+		qsensor->temp_critical = temp;
+
+	if (trip == TMU_TRIP_PASSIVE)
+		qsensor->temp_passive = temp;
+
+	return 0;
+}
+
 static const struct thermal_zone_of_device_ops tmu_tz_ops = {
 	.get_temp = tmu_get_temp,
+	.get_trend = tmu_get_trend,
+	.set_trip_temp = tmu_set_trip_temp,
 };
 
 static int qoriq_tmu_register_tmu_zone(struct platform_device *pdev)
 {
 	struct qoriq_tmu_data *qdata = platform_get_drvdata(pdev);
-	int id, sites = 0;
+	const struct thermal_trip *trip;
+	int id, sites = 0, ret;
 
 	for (id = 0; id < SITES_MAX; id++) {
 		qdata->sensor[id] = devm_kzalloc(&pdev->dev,
@@ -132,6 +180,36 @@ static int qoriq_tmu_register_tmu_zone(s
 				return PTR_ERR(qdata->sensor[id]->tzd);
 		}
 
+		/* first thermal zone takes care of system-wide device cooling */
+		if (id == 0) {
+			qdata->sensor[id]->cdev = devfreq_cooling_register();
+			if (IS_ERR(qdata->sensor[id]->cdev)) {
+				ret = PTR_ERR(qdata->sensor[id]->cdev);
+				pr_err("failed to register devfreq cooling device: %d\n",
+					ret);
+				return ret;
+			}
+
+			ret = thermal_zone_bind_cooling_device(qdata->sensor[id]->tzd,
+				TMU_TRIP_PASSIVE,
+				qdata->sensor[id]->cdev,
+				THERMAL_NO_LIMIT,
+				THERMAL_NO_LIMIT,
+				THERMAL_WEIGHT_DEFAULT);
+			if (ret) {
+				pr_err("binding zone %s with cdev %s failed:%d\n",
+					qdata->sensor[id]->tzd->type,
+					qdata->sensor[id]->cdev->type,
+					ret);
+				devfreq_cooling_unregister(qdata->sensor[id]->cdev);
+				return ret;
+			}
+
+			trip = of_thermal_get_trip_points(qdata->sensor[id]->tzd);
+			qdata->sensor[id]->temp_passive = trip[0].temperature;
+			qdata->sensor[id]->temp_critical = trip[1].temperature;
+		}
+
 		sites |= 0x1 << (15 - id);
 	}
 
