summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/misc/therm_est.txt108
-rw-r--r--drivers/misc/therm_est.c260
2 files changed, 367 insertions, 1 deletions
diff --git a/Documentation/devicetree/bindings/misc/therm_est.txt b/Documentation/devicetree/bindings/misc/therm_est.txt
new file mode 100644
index 000000000000..1a0adb7689eb
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/therm_est.txt
@@ -0,0 +1,108 @@
+Thermal estimator driver.
+
+Properties :
+ - compatible : Should contain "nvidia,therm-est".
+ - toffset : Temperature offset for thermal estimation, in milli-celsius.
+ - polling-period : Polling wait times for thermal estimation, in milliseconds.
+ - passive-delay : Polling wait times for passive cooling, in milliseconds.
+ - tc1 : Coefficient 1 for thermal trend calculation.
+ - tc2 : Coefficient 2 for thermal trend calculation.
+ - node for trip : Node for trip point information. Required.
+ This node can be numerous.
+ - node for subdev : Node for subdevice information. Required.
+ This node can be numerous.
+ - node for tzp : Node for thermal zone platform parameters. Optional.
+ - node for timer trip : Node for timer trip point information. Optional.
+ This node can be numerous.
+
+Properties in trips node : Required. Can be numerous.
+ - compatible : Should contain "nvidia,therm-est-trip".
+ - cdev-type : Thermal cooling device type for binding with the thermal
+ estimator thermal zone.
+ - trip-type : Type of this trip point.
+ This should be one of types in active, passive, hot, and critical.
+ - trip-temp : Temperature to fire this trip point, in milli-celsius.
+ - hysteresis : Hysteresis temperature for this trip point, in milli-celsius.
+ - upper : Upper limit of the cooling state for this trip point. Optional.
+ "-1" = THERMAL_NO_LIMIT: no upper limit.
+ If no upper property, THERMAL_NO_LIMIT will be used.
+ - lower : Lower limit of the cooling state for this trip point. Optional.
+ "-1" = THERMAL_NO_LIMIT: no lower limit.
+ If no lower property, THERMAL_NO_LIMIT will be used.
+
+Properties in subdevice node : Required. Can be numerous.
+ - compatible : Should contain "nvidia,therm-est-subdev".
+ - dev_data : Thermal zone device type for thermal estimation.
+ - coeffs : An array of coefficients, the number of entries should be twenty.
+
+Properties in tzp node : Optional
+ - compatible : Should contain "nvidia,therm-est-tzp".
+ - governor : Thermal throttling governor name.
+ The available governors could be different by Kernel build options.
+
+Properties in timer trip : Optional. Can be numerous.
+ - compatible : Should contain "nvidia,therm-est-timer-trip".
+ - trip : Trip point to apply timer trip in the thermal estimator thermal zone.
+ - node for timer : Nodes for timer trip point information. Required.
+ This node can be numerous.
+
+Properties in timer : Required if the timer trip is exist. Can be numerous.
+ - compatible : Should contain "nvidia,therm-est-timer-trip".
+ - time-after : Expiration time of the timer, in milliseconds.
+ - trip-temp : Trip temperature will be used for this trip point after timer
+ expires, in milli-celsius.
+ - hysteresis : Hysteresis temperature will be used for this trip point after
+ timer expires, in milli-celsius.
+
+Example:
+
+ therm_est {
+ compatible = "nvidia,therm-est";
+ toffset = <0>;
+ polling-period = <1100>;
+ passive-delay = <15000>;
+ tc1 = <10>;
+ tc2 = <1>;
+ trips@0 {
+ compatible = "nvidia,therm-est-trip";
+ cdev-type = "skin-balanced";
+ trip-type = "active";
+ trip-temp = <40000>;
+ hysteresis = <0>;
+ upper = "1"; // fix cooling state to 1
+ lower = "1";
+ };
+ trips@1 {
+ compatible = "nvidia,therm-est-trip";
+ cdev-type = "skin-balanced";
+ trip-type = "passive";
+ trip-temp = <45000>;
+ hysteresis = <5000>;
+ upper = "-1"; // THERMAL_NO_LIMIT
+ lower = "-1"; // THERMAL_NO_LIMIT
+ };
+ subdevs@0 {
+ compatible = "nvidia,therm-est-subdev";
+ dev-data = "nct_ext";
+ coeffs = "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0";
+ };
+ subdevs@1 {
+ compatible = "nvidia,therm-est-subdev";
+ dev-data = "nct_int";
+ coeffs = "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0";
+ };
+ tzp {
+ compatible = "nvidia,therm-est-tzp";
+ governor = "pid_thermal_gov";
+ };
+ timer-trips@0 {
+ compatible = "nvidia,therm-est-timer-trip";
+ trip = <1>; // this timer trip will be applied to trip1
+ timers@0 {
+ compatible = "nvidia,therm-est-timer";
+ time-after = <600000>;
+ trip-temp = <43000>;
+ hysteresis = <5000>;
+ };
+ };
+ };
diff --git a/drivers/misc/therm_est.c b/drivers/misc/therm_est.c
index 17471bfe6aee..699c8ce5ca03 100644
--- a/drivers/misc/therm_est.c
+++ b/drivers/misc/therm_est.c
@@ -32,6 +32,8 @@
#include <linux/module.h>
#include <linux/hwmon-sysfs.h>
#include <linux/suspend.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
struct therm_estimator {
struct thermal_zone_device *thz;
@@ -694,6 +696,262 @@ static int therm_est_pm_notify(struct notifier_block *nb,
}
#endif
+#ifdef CONFIG_OF
+static int __parse_dt_trip(struct device_node *np,
+ struct thermal_trip_info *trips)
+{
+ const char *str;
+ u32 val;
+ int ret;
+
+ ret = of_property_read_string(np, "cdev-type", &str);
+ if (ret < 0)
+ return ret;
+ trips->cdev_type = (char *)str;
+
+ ret = of_property_read_string(np, "trip-type", &str);
+ if (ret < 0)
+ return ret;
+
+ if (!strcasecmp("active", str))
+ trips->trip_type = THERMAL_TRIP_ACTIVE;
+ else if (!strcasecmp("passive", str))
+ trips->trip_type = THERMAL_TRIP_PASSIVE;
+ else if (!strcasecmp("hot", str))
+ trips->trip_type = THERMAL_TRIP_HOT;
+ else if (!strcasecmp("critical", str))
+ trips->trip_type = THERMAL_TRIP_CRITICAL;
+ else
+ return -EINVAL;
+
+ ret = of_property_read_u32(np, "trip-temp", &val);
+ if (ret < 0)
+ return ret;
+ trips->trip_temp = val;
+
+ trips->hysteresis = 0;
+ if (of_property_read_u32(np, "hysteresis", &val) == 0)
+ trips->hysteresis = val;
+
+ trips->upper = THERMAL_NO_LIMIT;
+ if (of_property_read_string(np, "upper", &str) == 0) {
+ if (kstrtou32(str, 10, &val) == 0)
+ trips->upper = val;
+ }
+
+ trips->lower = THERMAL_NO_LIMIT;
+ if (of_property_read_string(np, "lower", &str) == 0) {
+ if (kstrtou32(str, 10, &val) == 0)
+ trips->lower = val;
+ }
+
+ return 0;
+}
+
+static int __parse_dt_subdev(struct device_node *np,
+ struct therm_est_subdevice *subdev)
+{
+ const char *str;
+ char *sbegin;
+ int i = 0;
+ int ret;
+
+ subdev->dev_data = (void *)of_get_property(np, "dev-data", NULL);
+ if (!subdev->dev_data)
+ return -ENODATA;
+
+ ret = of_property_read_string(np, "coeffs", &str);
+ if (ret < 0)
+ return ret;
+
+ while (str && (i < HIST_LEN)) {
+ str = skip_spaces(str);
+ sbegin = strsep((char **)&str, " ");
+ if (!sbegin || (kstrtol((const char *)sbegin, 10,
+ &subdev->coeffs[i++]) < 0))
+ break;
+ }
+
+ if (i != HIST_LEN)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int __parse_dt_tzp(struct device_node *np,
+ struct thermal_zone_params *tzp)
+{
+ const char *str;
+
+ if (of_property_read_string(np, "governor", &str) == 0)
+ strncpy(tzp->governor_name, str, THERMAL_NAME_LENGTH);
+
+ return 0;
+}
+
+static int __parse_dt_timer_trip(struct device_node *np,
+ struct therm_est_timer_trip_info *timer_info)
+{
+ struct device_node *ch;
+ u32 val;
+ int n_timers;
+ int ret;
+
+ ret = of_property_read_u32(np, "trip", &val);
+ if (ret < 0)
+ return ret;
+ timer_info->trip = val;
+
+ n_timers = 0;
+ for_each_child_of_node(np, ch) {
+ if (!of_device_is_compatible(ch, "nvidia,therm-est-timer"))
+ continue;
+
+ ret = of_property_read_u32(ch, "time-after", &val);
+ if (ret < 0)
+ return ret;
+ timer_info->timers[n_timers].time_after = val;
+
+ ret = of_property_read_u32(ch, "trip-temp", &val);
+ if (ret < 0)
+ return ret;
+ timer_info->timers[n_timers].trip_temp = val;
+
+ timer_info->timers[n_timers].hysteresis = 0;
+ if (of_property_read_u32(ch, "hysteresis", &val) == 0)
+ timer_info->timers[n_timers].hysteresis = val;
+
+ n_timers++;
+ }
+
+ timer_info->num_timers = n_timers;
+
+ return 0;
+}
+
+static struct therm_est_data *therm_est_get_pdata(struct device *dev)
+{
+ struct therm_est_data *data;
+ struct device_node *np;
+ struct device_node *ch;
+ u32 val;
+ int i, j, k, l;
+ int ret;
+
+ np = of_find_compatible_node(NULL, NULL, "nvidia,therm-est");
+ if (!np)
+ return dev->platform_data;
+
+ data = devm_kzalloc(dev, sizeof(struct therm_est_data), GFP_KERNEL);
+ if (!data)
+ return ERR_PTR(-ENOMEM);
+
+ ret = of_property_read_u32(np, "toffset", &val);
+ if (ret < 0)
+ return ERR_PTR(ret);
+ data->toffset = val;
+
+ ret = of_property_read_u32(np, "polling-period", &val);
+ if (ret < 0)
+ return ERR_PTR(ret);
+ data->polling_period = val;
+
+ ret = of_property_read_u32(np, "passive-delay", &val);
+ if (ret < 0)
+ return ERR_PTR(ret);
+ data->passive_delay = val;
+
+ ret = of_property_read_u32(np, "tc1", &val);
+ if (ret < 0)
+ return ERR_PTR(ret);
+ data->tc1 = val;
+
+ ret = of_property_read_u32(np, "tc2", &val);
+ if (ret < 0)
+ return ERR_PTR(ret);
+ data->tc2 = val;
+
+ i = j = k = l = 0;
+ for_each_child_of_node(np, ch) {
+ if (of_device_is_compatible(ch, "nvidia,therm-est-trip"))
+ i++;
+ else if (of_device_is_compatible(ch, "nvidia,therm-est-subdev"))
+ j++;
+ else if (of_device_is_compatible(ch, "nvidia,therm-est-tzp"))
+ k++;
+ else if (of_device_is_compatible(ch,
+ "nvidia,therm-est-timer-trip"))
+ l++;
+ }
+
+ /* trip point information and subdevices are must required data. */
+ if ((i == 0) || (j == 0))
+ return ERR_PTR(-ENOENT);
+
+ data->trips = devm_kzalloc(dev, sizeof(struct thermal_trip_info) * i,
+ GFP_KERNEL);
+ if (!data->trips)
+ return ERR_PTR(-ENOMEM);
+
+ data->devs = devm_kzalloc(dev, sizeof(struct therm_est_subdevice) * j,
+ GFP_KERNEL);
+ if (!data->devs)
+ return ERR_PTR(-ENOMEM);
+
+ /* thermal zone params is optional data. */
+ if (k > 0) {
+ data->tzp = devm_kzalloc(dev,
+ sizeof(struct thermal_zone_params) * k, GFP_KERNEL);
+ if (!data->tzp)
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* timer trip point information is optional data. */
+ if (l > 0) {
+ data->timer_trips = devm_kzalloc(dev,
+ sizeof(struct therm_est_timer_trip_info) * l,
+ GFP_KERNEL);
+ if (!data->timer_trips)
+ return ERR_PTR(-ENOMEM);
+ }
+
+ i = j = l = 0;
+ for_each_child_of_node(np, ch) {
+ if (of_device_is_compatible(ch, "nvidia,therm-est-trip")) {
+ ret = __parse_dt_trip(ch, &data->trips[i++]);
+ if (ret < 0)
+ return ERR_PTR(ret);
+ } else if (of_device_is_compatible(ch,
+ "nvidia,therm-est-subdev")) {
+ ret = __parse_dt_subdev(ch, &data->devs[j++]);
+ if (ret < 0)
+ return ERR_PTR(ret);
+ } else if (of_device_is_compatible(ch,
+ "nvidia,therm-est-tzp")) {
+ ret = __parse_dt_tzp(ch, data->tzp);
+ if (ret < 0)
+ return ERR_PTR(ret);
+ } else if (of_device_is_compatible(ch,
+ "nvidia,therm-est-timer-trip")) {
+ ret = __parse_dt_timer_trip(ch, &data->timer_trips[l]);
+ if (!ret)
+ l++;
+ }
+ }
+
+ data->num_trips = i;
+ data->ndevs = j;
+ data->num_timer_trips = l;
+
+ return data;
+}
+#else
+static struct therm_est_data *therm_est_get_pdata(struct device *dev)
+{
+ return dev->platform_data;
+}
+#endif /* CONFIG_OF */
+
static int __devinit therm_est_probe(struct platform_device *pdev)
{
int i;
@@ -706,7 +964,7 @@ static int __devinit therm_est_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, est);
- data = pdev->dev.platform_data;
+ data = therm_est_get_pdata(&pdev->dev);
est->devs = data->devs;
est->ndevs = data->ndevs;