/* * Copyright (C) 2013 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);