This patch adds a new concept of a container thermal zone, which agregates subzones. There is only one level allowed. It relays on virtual temperature sensor which calculates the temp based on real sensors from subzones.
Signed-off-by: Lukasz Luba l.luba@partner.samsung.com --- drivers/thermal/of-thermal.c | 104 +++++++++++++++++++++++++++++++-- drivers/thermal/thermal_core.h | 1 + include/linux/thermal.h | 3 + 3 files changed, 102 insertions(+), 6 deletions(-)
diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index 4f2816559205..1825d30e9b0b 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c @@ -801,7 +801,7 @@ static int thermal_of_populate_trip(struct device_node *np, * check the return value with help of IS_ERR() helper. */ static struct __thermal_zone -__init *thermal_of_build_thermal_zone(struct device_node *np) +__init *thermal_of_build_thermal_zone(struct device_node *np, bool container) { struct device_node *child = NULL, *gchild; struct __thermal_zone *tz; @@ -813,6 +813,15 @@ __init *thermal_of_build_thermal_zone(struct device_node *np) return ERR_PTR(-EINVAL); }
+ /* + * Make sure we are populating the right zone type in the + * right phase + */ + if (of_property_read_bool(np, "container") ^ container) { + return ERR_PTR(-EAGAIN); + } + + tz = kzalloc(sizeof(*tz), GFP_KERNEL); if (!tz) return ERR_PTR(-ENOMEM); @@ -931,6 +940,69 @@ static inline void of_thermal_free_zone(struct __thermal_zone *tz) kfree(tz); }
+static int thermal_add_subzone(struct thermal_zone_device *container, + struct thermal_zone_device *zone, + u32 capacitance) +{ + if (IS_ERR_OR_NULL(container) || IS_ERR_OR_NULL(zone)) + return -EINVAL; + + mutex_lock(&zone->lock); + zone->capacitance = capacitance; + mutex_unlock(&zone->lock); + + mutex_lock(&container->lock); + list_add_tail(&zone->subzones, &container->subzones); + mutex_unlock(&container->lock); + + return 0; +} + +static int thermal_build_subzones(struct thermal_zone_device *zone, + struct device_node *np) +{ + int i = 0; + int ret = 0; + + INIT_LIST_HEAD(&zone->subzones); + + for ( ;; i++) { + struct thermal_zone_device *tz; + struct of_phandle_args zone_specs; + struct device_node *child; + u32 capacitance; + + ret = of_parse_phandle_with_fixed_args(np, "subzones", 1, i, + &zone_specs); + if (ret) { + of_node_put(zone_specs.np); + break; + } + + + tz = thermal_zone_get_zone_by_name(zone_specs.np->name); + if (IS_ERR_OR_NULL(tz)) { + pr_warn("Parsing thermal subzone %s failed %d\n", + of_node_full_name(zone_specs.np), ret); + of_node_put(zone_specs.np); + continue; + } + + if (zone_specs.args_count == 1) + capacitance = zone_specs.args[0]; + else + capacitance = THERMAL_SUBZONE_DEFAULT_CAPACITANCE; + + + ret = thermal_add_subzone(zone, tz, capacitance); + + of_node_put(zone_specs.np); + + } + + return ret; +} + /** * of_parse_thermal_zones - parse device tree thermal data * @@ -943,11 +1015,12 @@ static inline void of_thermal_free_zone(struct __thermal_zone *tz) * Return: 0 on success, proper error code otherwise * */ -int __init of_parse_thermal_zones(void) +int __init of_parse_thermal_zones_containers(bool container) { struct device_node *np, *child; struct __thermal_zone *tz; struct thermal_zone_device_ops *ops; + int ret;
np = of_find_node_by_name(NULL, "thermal-zones"); if (!np) { @@ -961,11 +1034,12 @@ int __init of_parse_thermal_zones(void) int i, mask = 0; u32 prop;
- tz = thermal_of_build_thermal_zone(child); + tz = thermal_of_build_thermal_zone(child, container); if (IS_ERR(tz)) { - pr_err("failed to build thermal zone %s: %ld\n", - child->name, - PTR_ERR(tz)); + if (tz != ERR_PTR(-EAGAIN)) + pr_err("failed to build thermal zone %s: %ld\n", + child->name, + PTR_ERR(tz)); continue; }
@@ -1005,7 +1079,13 @@ int __init of_parse_thermal_zones(void) of_thermal_free_zone(tz); /* attempting to build remaining zones still */ } + + zone->container = container; + if (container) { + ret = thermal_build_subzones(zone, child); + } } + of_node_put(np);
return 0; @@ -1021,6 +1101,18 @@ int __init of_parse_thermal_zones(void) return -ENOMEM; }
+ +int __init of_parse_thermal_zones(void) +{ + int ret; + + /* First parse normal zones, then containers */ + ret = of_parse_thermal_zones_containers(false); + if (ret) + return ret; + return of_parse_thermal_zones_containers(true); +} + /** * of_thermal_destroy_zones - remove all zones parsed and allocated resources * diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index f6ccd8fc2ba5..66527810a06c 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -14,6 +14,7 @@
/* Initial state of a cooling device during binding */ #define THERMAL_NO_TARGET -1UL +#define THERMAL_SUBZONE_DEFAULT_CAPACITANCE 1
/* * This structure is used to describe the behavior of diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 6c944ab78d17..0b47cb72b96e 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -218,6 +218,9 @@ struct thermal_zone_device { struct mutex lock; struct list_head node; struct delayed_work poll_queue; + bool container; + struct list_head subzones; + u32 capacitance; enum thermal_notify_event notify_event; };