diff options
Diffstat (limited to 'drivers/thermal/thermal_core.c')
| -rw-r--r-- | drivers/thermal/thermal_core.c | 148 | 
1 files changed, 113 insertions, 35 deletions
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 4962a6aaf29..71b0ec0c370 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -34,6 +34,7 @@  #include <linux/thermal.h>  #include <linux/reboot.h>  #include <linux/string.h> +#include <linux/of.h>  #include <net/netlink.h>  #include <net/genetlink.h> @@ -55,10 +56,15 @@ static LIST_HEAD(thermal_governor_list);  static DEFINE_MUTEX(thermal_list_lock);  static DEFINE_MUTEX(thermal_governor_lock); +static struct thermal_governor *def_governor; +  static struct thermal_governor *__find_governor(const char *name)  {  	struct thermal_governor *pos; +	if (!name || !name[0]) +		return def_governor; +  	list_for_each_entry(pos, &thermal_governor_list, governor_list)  		if (!strnicmp(name, pos->name, THERMAL_NAME_LENGTH))  			return pos; @@ -81,17 +87,23 @@ int thermal_register_governor(struct thermal_governor *governor)  	if (__find_governor(governor->name) == NULL) {  		err = 0;  		list_add(&governor->governor_list, &thermal_governor_list); +		if (!def_governor && !strncmp(governor->name, +			DEFAULT_THERMAL_GOVERNOR, THERMAL_NAME_LENGTH)) +			def_governor = governor;  	}  	mutex_lock(&thermal_list_lock);  	list_for_each_entry(pos, &thermal_tz_list, node) { +		/* +		 * only thermal zones with specified tz->tzp->governor_name +		 * may run with tz->govenor unset +		 */  		if (pos->governor)  			continue; -		if (pos->tzp) -			name = pos->tzp->governor_name; -		else -			name = DEFAULT_THERMAL_GOVERNOR; + +		name = pos->tzp->governor_name; +  		if (!strnicmp(name, governor->name, THERMAL_NAME_LENGTH))  			pos->governor = governor;  	} @@ -247,10 +259,11 @@ static void bind_cdev(struct thermal_cooling_device *cdev)  		if (!pos->tzp && !pos->ops->bind)  			continue; -		if (!pos->tzp && pos->ops->bind) { +		if (pos->ops->bind) {  			ret = pos->ops->bind(pos, cdev);  			if (ret)  				print_bind_err_msg(pos, cdev, ret); +			continue;  		}  		tzp = pos->tzp; @@ -282,8 +295,8 @@ static void bind_tz(struct thermal_zone_device *tz)  	mutex_lock(&thermal_list_lock); -	/* If there is no platform data, try to use ops->bind */ -	if (!tzp && tz->ops->bind) { +	/* If there is ops->bind, try to use ops->bind */ +	if (tz->ops->bind) {  		list_for_each_entry(pos, &thermal_cdev_list, node) {  			ret = tz->ops->bind(tz, pos);  			if (ret) @@ -340,8 +353,8 @@ static void monitor_thermal_zone(struct thermal_zone_device *tz)  static void handle_non_critical_trips(struct thermal_zone_device *tz,  			int trip, enum thermal_trip_type trip_type)  { -	if (tz->governor) -		tz->governor->throttle(tz, trip); +	tz->governor ? tz->governor->throttle(tz, trip) : +		       def_governor->throttle(tz, trip);  }  static void handle_critical_trips(struct thermal_zone_device *tz, @@ -402,7 +415,7 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, unsigned long *temp)  	enum thermal_trip_type type;  #endif -	if (!tz || IS_ERR(tz)) +	if (!tz || IS_ERR(tz) || !tz->ops->get_temp)  		goto exit;  	mutex_lock(&tz->lock); @@ -449,12 +462,18 @@ static void update_temperature(struct thermal_zone_device *tz)  	tz->last_temperature = tz->temperature;  	tz->temperature = temp;  	mutex_unlock(&tz->lock); + +	dev_dbg(&tz->device, "last_temperature=%d, current_temperature=%d\n", +				tz->last_temperature, tz->temperature);  }  void thermal_zone_device_update(struct thermal_zone_device *tz)  {  	int count; +	if (!tz->ops->get_temp) +		return; +  	update_temperature(tz);  	for (count = 0; count < tz->trips; count++) @@ -773,6 +792,9 @@ emul_temp_store(struct device *dev, struct device_attribute *attr,  		ret = tz->ops->set_emul_temp(tz, temperature);  	} +	if (!ret) +		thermal_zone_device_update(tz); +  	return ret ? ret : count;  }  static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store); @@ -1038,7 +1060,8 @@ static void thermal_release(struct device *dev)  		     sizeof("thermal_zone") - 1)) {  		tz = to_thermal_zone(dev);  		kfree(tz); -	} else { +	} else if(!strncmp(dev_name(dev), "cooling_device", +			sizeof("cooling_device") - 1)){  		cdev = to_cooling_device(dev);  		kfree(cdev);  	} @@ -1050,7 +1073,8 @@ static struct class thermal_class = {  };  /** - * thermal_cooling_device_register() - register a new thermal cooling device + * __thermal_cooling_device_register() - register a new thermal cooling device + * @np:		a pointer to a device tree node.   * @type:	the thermal cooling device type.   * @devdata:	device private data.   * @ops:		standard thermal cooling devices callbacks. @@ -1058,13 +1082,16 @@ static struct class thermal_class = {   * This interface function adds a new thermal cooling device (fan/processor/...)   * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself   * to all the thermal zone devices registered at the same time. + * It also gives the opportunity to link the cooling device to a device tree + * node, so that it can be bound to a thermal zone created out of device tree.   *   * Return: a pointer to the created struct thermal_cooling_device or an   * ERR_PTR. Caller must check return value with IS_ERR*() helpers.   */ -struct thermal_cooling_device * -thermal_cooling_device_register(char *type, void *devdata, -				const struct thermal_cooling_device_ops *ops) +static struct thermal_cooling_device * +__thermal_cooling_device_register(struct device_node *np, +				  char *type, void *devdata, +				  const struct thermal_cooling_device_ops *ops)  {  	struct thermal_cooling_device *cdev;  	int result; @@ -1089,8 +1116,9 @@ thermal_cooling_device_register(char *type, void *devdata,  	strlcpy(cdev->type, type ? : "", sizeof(cdev->type));  	mutex_init(&cdev->lock);  	INIT_LIST_HEAD(&cdev->thermal_instances); +	cdev->np = np;  	cdev->ops = ops; -	cdev->updated = true; +	cdev->updated = false;  	cdev->device.class = &thermal_class;  	cdev->devdata = devdata;  	dev_set_name(&cdev->device, "cooling_device%d", cdev->id); @@ -1131,9 +1159,53 @@ unregister:  	device_unregister(&cdev->device);  	return ERR_PTR(result);  } + +/** + * thermal_cooling_device_register() - register a new thermal cooling device + * @type:	the thermal cooling device type. + * @devdata:	device private data. + * @ops:		standard thermal cooling devices callbacks. + * + * This interface function adds a new thermal cooling device (fan/processor/...) + * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself + * to all the thermal zone devices registered at the same time. + * + * Return: a pointer to the created struct thermal_cooling_device or an + * ERR_PTR. Caller must check return value with IS_ERR*() helpers. + */ +struct thermal_cooling_device * +thermal_cooling_device_register(char *type, void *devdata, +				const struct thermal_cooling_device_ops *ops) +{ +	return __thermal_cooling_device_register(NULL, type, devdata, ops); +}  EXPORT_SYMBOL_GPL(thermal_cooling_device_register);  /** + * thermal_of_cooling_device_register() - register an OF thermal cooling device + * @np:		a pointer to a device tree node. + * @type:	the thermal cooling device type. + * @devdata:	device private data. + * @ops:		standard thermal cooling devices callbacks. + * + * This function will register a cooling device with device tree node reference. + * This interface function adds a new thermal cooling device (fan/processor/...) + * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself + * to all the thermal zone devices registered at the same time. + * + * Return: a pointer to the created struct thermal_cooling_device or an + * ERR_PTR. Caller must check return value with IS_ERR*() helpers. + */ +struct thermal_cooling_device * +thermal_of_cooling_device_register(struct device_node *np, +				   char *type, void *devdata, +				   const struct thermal_cooling_device_ops *ops) +{ +	return __thermal_cooling_device_register(np, type, devdata, ops); +} +EXPORT_SYMBOL_GPL(thermal_of_cooling_device_register); + +/**   * thermal_cooling_device_unregister - removes the registered thermal cooling device   * @cdev:	the thermal cooling device to remove.   * @@ -1205,6 +1277,8 @@ void thermal_cdev_update(struct thermal_cooling_device *cdev)  	mutex_lock(&cdev->lock);  	/* Make sure cdev enters the deepest cooling state */  	list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) { +		dev_dbg(&cdev->device, "zone%d->target=%lu\n", +				instance->tz->id, instance->target);  		if (instance->target == THERMAL_NO_TARGET)  			continue;  		if (instance->target > target) @@ -1213,6 +1287,7 @@ void thermal_cdev_update(struct thermal_cooling_device *cdev)  	mutex_unlock(&cdev->lock);  	cdev->ops->set_cur_state(cdev, target);  	cdev->updated = true; +	dev_dbg(&cdev->device, "set to state %lu\n", target);  }  EXPORT_SYMBOL(thermal_cdev_update); @@ -1368,7 +1443,7 @@ static void remove_trip_attrs(struct thermal_zone_device *tz)   */  struct thermal_zone_device *thermal_zone_device_register(const char *type,  	int trips, int mask, void *devdata, -	const struct thermal_zone_device_ops *ops, +	struct thermal_zone_device_ops *ops,  	const struct thermal_zone_params *tzp,  	int passive_delay, int polling_delay)  { @@ -1384,7 +1459,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,  	if (trips > THERMAL_MAX_TRIPS || trips < 0 || mask >> trips)  		return ERR_PTR(-EINVAL); -	if (!ops || !ops->get_temp) +	if (!ops)  		return ERR_PTR(-EINVAL);  	if (trips > 0 && (!ops->get_trip_type || !ops->get_trip_temp)) @@ -1469,7 +1544,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,  	if (tz->tzp)  		tz->governor = __find_governor(tz->tzp->governor_name);  	else -		tz->governor = __find_governor(DEFAULT_THERMAL_GOVERNOR); +		tz->governor = def_governor;  	mutex_unlock(&thermal_governor_lock); @@ -1488,6 +1563,9 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,  	INIT_DELAYED_WORK(&(tz->poll_queue), thermal_zone_device_check); +	if (!tz->ops->get_temp) +		thermal_zone_device_set_polling(tz, 0); +  	thermal_zone_device_update(tz);  	if (!result) @@ -1606,15 +1684,17 @@ exit:  EXPORT_SYMBOL_GPL(thermal_zone_get_zone_by_name);  #ifdef CONFIG_NET +static const struct genl_multicast_group thermal_event_mcgrps[] = { +	{ .name = THERMAL_GENL_MCAST_GROUP_NAME, }, +}; +  static struct genl_family thermal_event_genl_family = {  	.id = GENL_ID_GENERATE,  	.name = THERMAL_GENL_FAMILY_NAME,  	.version = THERMAL_GENL_VERSION,  	.maxattr = THERMAL_GENL_ATTR_MAX, -}; - -static struct genl_multicast_group thermal_event_mcgrp = { -	.name = THERMAL_GENL_MCAST_GROUP_NAME, +	.mcgrps = thermal_event_mcgrps, +	.n_mcgrps = ARRAY_SIZE(thermal_event_mcgrps),  };  int thermal_generate_netlink_event(struct thermal_zone_device *tz, @@ -1675,7 +1755,8 @@ int thermal_generate_netlink_event(struct thermal_zone_device *tz,  		return result;  	} -	result = genlmsg_multicast(skb, 0, thermal_event_mcgrp.id, GFP_ATOMIC); +	result = genlmsg_multicast(&thermal_event_genl_family, skb, 0, +				   0, GFP_ATOMIC);  	if (result)  		dev_err(&tz->device, "Failed to send netlink event:%d", result); @@ -1685,17 +1766,7 @@ EXPORT_SYMBOL_GPL(thermal_generate_netlink_event);  static int genetlink_init(void)  { -	int result; - -	result = genl_register_family(&thermal_event_genl_family); -	if (result) -		return result; - -	result = genl_register_mc_group(&thermal_event_genl_family, -					&thermal_event_mcgrp); -	if (result) -		genl_unregister_family(&thermal_event_genl_family); -	return result; +	return genl_register_family(&thermal_event_genl_family);  }  static void genetlink_exit(void) @@ -1745,8 +1816,14 @@ static int __init thermal_init(void)  	if (result)  		goto unregister_class; +	result = of_parse_thermal_zones(); +	if (result) +		goto exit_netlink; +  	return 0; +exit_netlink: +	genetlink_exit();  unregister_governors:  	thermal_unregister_governors();  unregister_class: @@ -1762,6 +1839,7 @@ error:  static void __exit thermal_exit(void)  { +	of_thermal_destroy_zones();  	genetlink_exit();  	class_unregister(&thermal_class);  	thermal_unregister_governors();  | 
