153 lines
3.6 KiB
C
153 lines
3.6 KiB
C
/*
|
|
* 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);
|