thermal: Add generic device cooling support
To compatible with previous implementation, add generic device cooling support, each thermal zone will register a cooling device, and when temperature exceed passive trip, the device cooling driver will send out a system wide notification, each device supporting cooling will need to register device cooling and takes action when passive trip is exceeded; Signed-off-by: Anson Huang <Anson.Huang@nxp.com>5.4-rM2-2.2.x-imx-squashed
parent
1d22bec32b
commit
2afca9456e
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 <linux/module.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
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);
|
|
@ -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 <linux/thermal.h>
|
||||
|
||||
#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__ */
|
Loading…
Reference in New Issue