diff options
author | Jinyoung Park <jinyoungp@nvidia.com> | 2013-04-12 23:25:42 +0900 |
---|---|---|
committer | Riham Haidar <rhaidar@nvidia.com> | 2013-04-18 12:43:33 -0700 |
commit | ba09eedfa1eb90751c670edf3736146b84a369f7 (patch) | |
tree | 936c76a1a121f7eb5cecad9b74c1bf4d74631cbe | |
parent | 0e49971fdd7b06be32f91476161062238c7a926f (diff) |
drivers: misc: therm_est: Add DT support
Add DT support and documentation for thermal estimator.
Bug 1173854
Bug 1240803
Change-Id: I4631fc499cb042d7649681fe097a9087aa5c5098
Signed-off-by: Jinyoung Park <jinyoungp@nvidia.com>
Reviewed-on: http://git-master/r/211125
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Bitan Biswas <bbiswas@nvidia.com>
GVS: Gerrit_Virtual_Submit
Reviewed-by: Diwakar Tundlam <dtundlam@nvidia.com>
-rw-r--r-- | Documentation/devicetree/bindings/misc/therm_est.txt | 108 | ||||
-rw-r--r-- | drivers/misc/therm_est.c | 260 |
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; |