diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 66143cb9958a..24925791a3f4 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -244,6 +244,13 @@ config IMX_SC_THERMAL sensor. It supports one critical trip point and one passive trip point for each thermal sensor. +config DEVICE_THERMAL + tristate "generic device cooling support" + help + Support for device cooling. + It supports notification of crossing passive trip for devices, + devices need to do their own actions to cool down the SOC. + config MAX77620_THERMAL tristate "Temperature sensor driver for Maxim MAX77620 PMIC" depends on MFD_MAX77620 diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 717a1bac19ce..2ebeac39196c 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o obj-$(CONFIG_IMX_SC_THERMAL) += imx_sc_thermal.o +obj-$(CONFIG_DEVICE_THERMAL) += device_cooling.o obj-$(CONFIG_MAX77620_THERMAL) += max77620_thermal.o obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o obj-$(CONFIG_DA9062_THERMAL) += da9062-thermal.o diff --git a/drivers/thermal/device_cooling.c b/drivers/thermal/device_cooling.c new file mode 100644 index 000000000000..c2f7653cbf0b --- /dev/null +++ b/drivers/thermal/device_cooling.c @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2013-2015 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include + +struct devfreq_cooling_device { + int id; + struct thermal_cooling_device *cool_dev; + unsigned int devfreq_state; +}; + +static DEFINE_IDR(devfreq_idr); +static DEFINE_MUTEX(devfreq_cooling_lock); + +#define MAX_STATE 1 + +static BLOCKING_NOTIFIER_HEAD(devfreq_cooling_chain_head); + +int register_devfreq_cooling_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register( + &devfreq_cooling_chain_head, nb); +} +EXPORT_SYMBOL_GPL(register_devfreq_cooling_notifier); + +int unregister_devfreq_cooling_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister( + &devfreq_cooling_chain_head, nb); +} +EXPORT_SYMBOL_GPL(unregister_devfreq_cooling_notifier); + +static int devfreq_cooling_notifier_call_chain(unsigned long val) +{ + return (blocking_notifier_call_chain( + &devfreq_cooling_chain_head, val, NULL) + == NOTIFY_BAD) ? -EINVAL : 0; +} + +static int devfreq_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + struct devfreq_cooling_device *devfreq_device = cdev->devdata; + int ret; + + ret = devfreq_cooling_notifier_call_chain(state); + if (ret) + return -EINVAL; + + devfreq_device->devfreq_state = state; + + return 0; +} + +static int devfreq_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + *state = MAX_STATE; + + return 0; +} + +static int devfreq_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct devfreq_cooling_device *devfreq_device = cdev->devdata; + + *state = devfreq_device->devfreq_state; + + return 0; +} + +static struct thermal_cooling_device_ops const devfreq_cooling_ops = { + .get_max_state = devfreq_get_max_state, + .get_cur_state = devfreq_get_cur_state, + .set_cur_state = devfreq_set_cur_state, +}; + +static int get_idr(struct idr *idr, int *id) +{ + int ret; + + mutex_lock(&devfreq_cooling_lock); + ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL); + mutex_unlock(&devfreq_cooling_lock); + if (unlikely(ret < 0)) + return ret; + *id = ret; + + return 0; +} + +static void release_idr(struct idr *idr, int id) +{ + mutex_lock(&devfreq_cooling_lock); + idr_remove(idr, id); + mutex_unlock(&devfreq_cooling_lock); +} + +struct thermal_cooling_device *devfreq_cooling_register(void) +{ + struct thermal_cooling_device *cool_dev; + struct devfreq_cooling_device *devfreq_dev = NULL; + char dev_name[THERMAL_NAME_LENGTH]; + int ret = 0; + + devfreq_dev = kzalloc(sizeof(struct devfreq_cooling_device), + GFP_KERNEL); + if (!devfreq_dev) + return ERR_PTR(-ENOMEM); + + ret = get_idr(&devfreq_idr, &devfreq_dev->id); + if (ret) { + kfree(devfreq_dev); + return ERR_PTR(-EINVAL); + } + + snprintf(dev_name, sizeof(dev_name), "thermal-devfreq-%d", + devfreq_dev->id); + + cool_dev = thermal_cooling_device_register(dev_name, devfreq_dev, + &devfreq_cooling_ops); + if (!cool_dev) { + release_idr(&devfreq_idr, devfreq_dev->id); + kfree(devfreq_dev); + return ERR_PTR(-EINVAL); + } + devfreq_dev->cool_dev = cool_dev; + devfreq_dev->devfreq_state = 0; + + return cool_dev; +} +EXPORT_SYMBOL_GPL(devfreq_cooling_register); + +void devfreq_cooling_unregister(struct thermal_cooling_device *cdev) +{ + struct devfreq_cooling_device *devfreq_dev = cdev->devdata; + + thermal_cooling_device_unregister(devfreq_dev->cool_dev); + release_idr(&devfreq_idr, devfreq_dev->id); + kfree(devfreq_dev); +} +EXPORT_SYMBOL_GPL(devfreq_cooling_unregister); diff --git a/include/linux/device_cooling.h b/include/linux/device_cooling.h new file mode 100644 index 000000000000..18d9e3b90ed9 --- /dev/null +++ b/include/linux/device_cooling.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2013-2015 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __DEVICE_THERMAL_H__ +#define __DEVICE_THERMAL_H__ + +#include + +#ifdef CONFIG_DEVICE_THERMAL +int register_devfreq_cooling_notifier(struct notifier_block *nb); +int unregister_devfreq_cooling_notifier(struct notifier_block *nb); +struct thermal_cooling_device *devfreq_cooling_register(void); +void devfreq_cooling_unregister(struct thermal_cooling_device *cdev); +#else +static inline +int register_devfreq_cooling_notifier(struct notifier_block *nb) +{ + return 0; +} + +static inline +int unregister_devfreq_cooling_notifier(struct notifier_block *nb) +{ + return 0; +} + +static inline +struct thermal_cooling_device *devfreq_cooling_register(void) +{ + return NULL; +} + +static inline +void devfreq_cooling_unregister(struct thermal_cooling_device *cdev) +{ + return; +} +#endif +#endif /* __DEVICE_THERMAL_H__ */