#include <linux/idr.h>
#include <linux/thermal.h>
#include <linux/spinlock.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
-MODULE_AUTHOR("Zhang Rui")
+MODULE_AUTHOR("Zhang Rui");
MODULE_DESCRIPTION("Generic thermal management sysfs support");
MODULE_LICENSE("GPL");
static LIST_HEAD(thermal_cdev_list);
static DEFINE_MUTEX(thermal_list_lock);
+static struct device *thermal_hwmon;
+#define MAX_THERMAL_ZONES 10
+
static int get_idr(struct idr *idr, struct mutex *lock, int *id)
{
int err;
mutex_unlock(lock);
}
-/* sys I/F for thermal zone */
+/* hwmon sys I/F*/
+static ssize_t
+name_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "thermal_sys_class\n");
+}
+
+static ssize_t
+temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct thermal_zone_device *tz;
+ struct sensor_device_attribute *sensor_attr
+ = to_sensor_dev_attr(attr);
+
+ list_for_each_entry(tz, &thermal_tz_list, node)
+ if (tz->id == sensor_attr->index)
+ return tz->ops->get_temp(tz, buf);
+
+ return -ENODEV;
+}
+
+static ssize_t
+temp_crit_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct thermal_zone_device *tz;
+ struct sensor_device_attribute *sensor_attr
+ = to_sensor_dev_attr(attr);
+
+ list_for_each_entry(tz, &thermal_tz_list, node)
+ if (tz->id == sensor_attr->index)
+ return tz->ops->get_trip_temp(tz, 0, buf);
+
+ return -ENODEV;
+}
+
+static DEVICE_ATTR(name, 0444, name_show, NULL);
+static struct sensor_device_attribute sensor_attrs[] = {
+ SENSOR_ATTR(temp1_input, 0444, temp_input_show, NULL, 0),
+ SENSOR_ATTR(temp1_crit, 0444, temp_crit_show, NULL, 0),
+ SENSOR_ATTR(temp2_input, 0444, temp_input_show, NULL, 1),
+ SENSOR_ATTR(temp2_crit, 0444, temp_crit_show, NULL, 1),
+ SENSOR_ATTR(temp3_input, 0444, temp_input_show, NULL, 2),
+ SENSOR_ATTR(temp3_crit, 0444, temp_crit_show, NULL, 2),
+ SENSOR_ATTR(temp4_input, 0444, temp_input_show, NULL, 3),
+ SENSOR_ATTR(temp4_crit, 0444, temp_crit_show, NULL, 3),
+ SENSOR_ATTR(temp5_input, 0444, temp_input_show, NULL, 4),
+ SENSOR_ATTR(temp5_crit, 0444, temp_crit_show, NULL, 4),
+ SENSOR_ATTR(temp6_input, 0444, temp_input_show, NULL, 5),
+ SENSOR_ATTR(temp6_crit, 0444, temp_crit_show, NULL, 5),
+ SENSOR_ATTR(temp7_input, 0444, temp_input_show, NULL, 6),
+ SENSOR_ATTR(temp7_crit, 0444, temp_crit_show, NULL, 6),
+ SENSOR_ATTR(temp8_input, 0444, temp_input_show, NULL, 7),
+ SENSOR_ATTR(temp8_crit, 0444, temp_crit_show, NULL, 7),
+ SENSOR_ATTR(temp9_input, 0444, temp_input_show, NULL, 8),
+ SENSOR_ATTR(temp9_crit, 0444, temp_crit_show, NULL, 8),
+ SENSOR_ATTR(temp10_input, 0444, temp_input_show, NULL, 9),
+ SENSOR_ATTR(temp10_crit, 0444, temp_crit_show, NULL, 9),
+};
+
+/* thermal zone sys I/F */
#define to_thermal_zone(_dev) \
container_of(_dev, struct thermal_zone_device, device)
device_remove_file(_dev, &trip_point_attrs[_index * 2 + 1]); \
} while (0)
-/* sys I/F for cooling device */
+/* cooling device sys I/F */
#define to_cooling_device(_dev) \
container_of(_dev, struct thermal_cooling_device, device)
}
static struct device_attribute dev_attr_cdev_type =
- __ATTR(type, 0444, thermal_cooling_device_type_show, NULL);
+__ATTR(type, 0444, thermal_cooling_device_type_show, NULL);
static DEVICE_ATTR(max_state, 0444,
thermal_cooling_device_max_state_show, NULL);
static DEVICE_ATTR(cur_state, 0644,
static ssize_t
thermal_cooling_device_trip_point_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+ struct device_attribute *attr, char *buf)
{
struct thermal_cooling_device_instance *instance;
/**
* thermal_zone_bind_cooling_device - bind a cooling device to a thermal zone
- * this function is usually called in the thermal zone device .bind callback.
* @tz: thermal zone device
* @trip: indicates which trip point the cooling devices is
* associated with in this thermal zone.
* @cdev: thermal cooling device
+ *
+ * This function is usually called in the thermal zone device .bind callback.
*/
int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
int trip,
{
struct thermal_cooling_device_instance *dev;
struct thermal_cooling_device_instance *pos;
+ struct thermal_zone_device *pos1;
+ struct thermal_cooling_device *pos2;
int result;
- if (trip >= tz->trips ||
- (trip < 0 && trip != THERMAL_TRIPS_NONE))
+ if (trip >= tz->trips || (trip < 0 && trip != THERMAL_TRIPS_NONE))
return -EINVAL;
- if (!tz || !cdev)
+ list_for_each_entry(pos1, &thermal_tz_list, node) {
+ if (pos1 == tz)
+ break;
+ }
+ list_for_each_entry(pos2, &thermal_cdev_list, node) {
+ if (pos2 == cdev)
+ break;
+ }
+
+ if (tz != pos1 || cdev != pos2)
return -EINVAL;
dev =
kfree(dev);
return result;
}
+
EXPORT_SYMBOL(thermal_zone_bind_cooling_device);
/**
* thermal_zone_unbind_cooling_device - unbind a cooling device from a thermal zone
- * this function is usually called in the thermal zone device .unbind callback.
* @tz: thermal zone device
* @trip: indicates which trip point the cooling devices is
* associated with in this thermal zone.
* @cdev: thermal cooling device
+ *
+ * This function is usually called in the thermal zone device .unbind callback.
*/
int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
int trip,
mutex_lock(&tz->lock);
list_for_each_entry_safe(pos, next, &tz->cooling_devices, node) {
- if (pos->tz == tz && pos->trip == trip
- && pos->cdev == cdev) {
+ if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
list_del(&pos->node);
mutex_unlock(&tz->lock);
goto unbind;
kfree(pos);
return 0;
}
+
EXPORT_SYMBOL(thermal_zone_unbind_cooling_device);
static void thermal_release(struct device *dev)
* @ops: standard thermal cooling devices callbacks.
*/
struct thermal_cooling_device *thermal_cooling_device_register(char *type,
- void *devdata, struct thermal_cooling_device_ops *ops)
+ void *devdata,
+ struct
+ thermal_cooling_device_ops
+ *ops)
{
struct thermal_cooling_device *cdev;
struct thermal_zone_device *pos;
int result;
+ if (!type)
+ return ERR_PTR(-EINVAL);
+
if (strlen(type) >= THERMAL_NAME_LENGTH)
- return NULL;
+ return ERR_PTR(-EINVAL);
if (!ops || !ops->get_max_state || !ops->get_cur_state ||
- !ops->set_cur_state)
- return NULL;
+ !ops->set_cur_state)
+ return ERR_PTR(-EINVAL);
cdev = kzalloc(sizeof(struct thermal_cooling_device), GFP_KERNEL);
if (!cdev)
- return NULL;
+ return ERR_PTR(-ENOMEM);
result = get_idr(&thermal_cdev_idr, &thermal_idr_lock, &cdev->id);
if (result) {
kfree(cdev);
- return NULL;
+ return ERR_PTR(result);
}
strcpy(cdev->type, type);
if (result) {
release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
kfree(cdev);
- return NULL;
+ return ERR_PTR(result);
}
/* sys I/F */
- if (type) {
- result = device_create_file(&cdev->device,
- &dev_attr_cdev_type);
- if (result)
- goto unregister;
- }
+ result = device_create_file(&cdev->device, &dev_attr_cdev_type);
+ if (result)
+ goto unregister;
result = device_create_file(&cdev->device, &dev_attr_max_state);
if (result)
unregister:
release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
device_unregister(&cdev->device);
- return NULL;
+ return ERR_PTR(result);
}
+
EXPORT_SYMBOL(thermal_cooling_device_register);
/**
* thermal_cooling_device_unregister - removes the registered thermal cooling device
- *
* @cdev: the thermal cooling device to remove.
*
* thermal_cooling_device_unregister() must be called when the device is no
tz->ops->unbind(tz, cdev);
}
mutex_unlock(&thermal_list_lock);
- if (cdev->type[0])
- device_remove_file(&cdev->device,
- &dev_attr_cdev_type);
+
+ device_remove_file(&cdev->device, &dev_attr_cdev_type);
device_remove_file(&cdev->device, &dev_attr_max_state);
device_remove_file(&cdev->device, &dev_attr_cur_state);
device_unregister(&cdev->device);
return;
}
+
EXPORT_SYMBOL(thermal_cooling_device_unregister);
/**
* longer needed.
*/
struct thermal_zone_device *thermal_zone_device_register(char *type,
- int trips, void *devdata,
- struct thermal_zone_device_ops *ops)
+ int trips,
+ void *devdata, struct
+ thermal_zone_device_ops
+ *ops)
{
struct thermal_zone_device *tz;
struct thermal_cooling_device *pos;
int result;
int count;
+ if (!type)
+ return ERR_PTR(-EINVAL);
+
if (strlen(type) >= THERMAL_NAME_LENGTH)
- return NULL;
+ return ERR_PTR(-EINVAL);
if (trips > THERMAL_MAX_TRIPS || trips < 0)
- return NULL;
+ return ERR_PTR(-EINVAL);
if (!ops || !ops->get_temp)
- return NULL;
+ return ERR_PTR(-EINVAL);
tz = kzalloc(sizeof(struct thermal_zone_device), GFP_KERNEL);
if (!tz)
- return NULL;
+ return ERR_PTR(-ENOMEM);
INIT_LIST_HEAD(&tz->cooling_devices);
idr_init(&tz->idr);
result = get_idr(&thermal_tz_idr, &thermal_idr_lock, &tz->id);
if (result) {
kfree(tz);
- return NULL;
+ return ERR_PTR(result);
+ }
+ if (tz->id >= MAX_THERMAL_ZONES) {
+ printk(KERN_ERR PREFIX
+ "Too many thermal zones\n");
+ release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
+ kfree(tz);
+ return ERR_PTR(-EINVAL);
}
strcpy(tz->type, type);
if (result) {
release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
kfree(tz);
- return NULL;
+ return ERR_PTR(result);
}
- /* sys I/F */
- if (type) {
- result = device_create_file(&tz->device, &dev_attr_type);
- if (result)
- goto unregister;
+ /* hwmon sys I/F */
+ result = device_create_file(thermal_hwmon,
+ &sensor_attrs[tz->id * 2].dev_attr);
+ if (result)
+ goto unregister;
+
+ if (trips > 0) {
+ char buf[40];
+ result = tz->ops->get_trip_type(tz, 0, buf);
+ if (result > 0 && !strcmp(buf, "critical\n")) {
+ result = device_create_file(thermal_hwmon,
+ &sensor_attrs[tz->id * 2 + 1].dev_attr);
+ if (result)
+ goto unregister;
+ }
}
+ /* sys I/F */
+ result = device_create_file(&tz->device, &dev_attr_type);
+ if (result)
+ goto unregister;
+
result = device_create_file(&tz->device, &dev_attr_temp);
if (result)
goto unregister;
list_add_tail(&tz->node, &thermal_tz_list);
if (ops->bind)
list_for_each_entry(pos, &thermal_cdev_list, node) {
- result = ops->bind(tz, pos);
- if (result)
- break;
+ result = ops->bind(tz, pos);
+ if (result)
+ break;
}
mutex_unlock(&thermal_list_lock);
unregister:
release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
device_unregister(&tz->device);
- return NULL;
+ return ERR_PTR(result);
}
+
EXPORT_SYMBOL(thermal_zone_device_register);
/**
* thermal_device_unregister - removes the registered thermal zone device
- *
* @tz: the thermal zone device to remove
*/
void thermal_zone_device_unregister(struct thermal_zone_device *tz)
tz->ops->unbind(tz, cdev);
mutex_unlock(&thermal_list_lock);
- if (tz->type[0])
- device_remove_file(&tz->device, &dev_attr_type);
+ device_remove_file(thermal_hwmon,
+ &sensor_attrs[tz->id * 2].dev_attr);
+ if (tz->trips > 0) {
+ char buf[40];
+ if (tz->ops->get_trip_type(tz, 0, buf) > 0)
+ if (!strcmp(buf, "critical\n"))
+ device_remove_file(thermal_hwmon,
+ &sensor_attrs[tz->id * 2 + 1].dev_attr);
+ }
+
+ device_remove_file(&tz->device, &dev_attr_type);
device_remove_file(&tz->device, &dev_attr_temp);
if (tz->ops->get_mode)
device_remove_file(&tz->device, &dev_attr_mode);
device_unregister(&tz->device);
return;
}
+
EXPORT_SYMBOL(thermal_zone_device_unregister);
+static void thermal_exit(void)
+{
+ if (thermal_hwmon) {
+ device_remove_file(thermal_hwmon, &dev_attr_name);
+ hwmon_device_unregister(thermal_hwmon);
+ }
+ class_unregister(&thermal_class);
+ idr_destroy(&thermal_tz_idr);
+ idr_destroy(&thermal_cdev_idr);
+ mutex_destroy(&thermal_idr_lock);
+ mutex_destroy(&thermal_list_lock);
+}
+
static int __init thermal_init(void)
{
int result = 0;
mutex_destroy(&thermal_idr_lock);
mutex_destroy(&thermal_list_lock);
}
- return result;
-}
-static void __exit thermal_exit(void)
-{
- class_unregister(&thermal_class);
- idr_destroy(&thermal_tz_idr);
- idr_destroy(&thermal_cdev_idr);
- mutex_destroy(&thermal_idr_lock);
- mutex_destroy(&thermal_list_lock);
+ thermal_hwmon = hwmon_device_register(NULL);
+ if (IS_ERR(thermal_hwmon)) {
+ result = PTR_ERR(thermal_hwmon);
+ thermal_hwmon = NULL;
+ printk(KERN_ERR PREFIX
+ "unable to register hwmon device\n");
+ thermal_exit();
+ return result;
+ }
+
+ result = device_create_file(thermal_hwmon, &dev_attr_name);
+
+ return result;
}
subsys_initcall(thermal_init);