hwmon: (pc87427) Add temperature monitoring support

Add support for the 6 temperature monitoring channels of the PC87427.
Note that the sensors resolution can vary, and I couldn't find a way
to figure it out, so we might have to compensate in user-space.

Signed-off-by: Jean Delvare <khali@linux-fr.org>
Acked-by: Guenter Roeck <guenter.roeck@ericsson.com>
This commit is contained in:
Jean Delvare
2010-08-14 21:08:59 +02:00
parent 9d32df192d
commit 008e5f3350
3 changed files with 379 additions and 7 deletions

View File

@@ -1,7 +1,7 @@
/*
* pc87427.c - hardware monitoring driver for the
* National Semiconductor PC87427 Super-I/O chip
* Copyright (C) 2006, 2008 Jean Delvare <khali@linux-fr.org>
* Copyright (C) 2006, 2008, 2010 Jean Delvare <khali@linux-fr.org>
*
* 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
@@ -15,10 +15,11 @@
* Supports the following chips:
*
* Chip #vin #fan #pwm #temp devid
* PC87427 - 8 4 - 0xF2
* PC87427 - 8 4 6 0xF2
*
* This driver assumes that no more than one chip is present.
* Only fans are supported so far, although the chip can do much more.
* Only fans are fully supported so far. Temperatures are in read-only
* mode, and voltages aren't supported at all.
*/
#include <linux/module.h>
@@ -62,6 +63,14 @@ struct pc87427_data {
u8 pwm_auto_ok; /* bit vector */
u8 pwm_enable[4]; /* register values */
u8 pwm[4]; /* register values */
u8 temp_enabled; /* bit vector */
s16 temp[6]; /* register values */
s8 temp_min[6]; /* register values */
s8 temp_max[6]; /* register values */
s8 temp_crit[6]; /* register values */
u8 temp_status[6]; /* register values */
u8 temp_type[6]; /* register values */
};
struct pc87427_sio_data {
@@ -120,6 +129,8 @@ static inline void superio_exit(int sioaddr)
#define BANK_FM(nr) (nr)
#define BANK_FT(nr) (0x08 + (nr))
#define BANK_FC(nr) (0x10 + (nr) * 2)
#define BANK_TM(nr) (nr)
#define BANK_VM(nr) (0x08 + (nr))
/*
* I/O access functions
@@ -251,6 +262,72 @@ static inline u8 pwm_enable_to_reg(unsigned long val, u8 pwmval)
}
}
/*
* Temperature registers and conversions
*/
#define PC87427_REG_TEMP_STATUS 0x10
#define PC87427_REG_TEMP 0x14
#define PC87427_REG_TEMP_MAX 0x18
#define PC87427_REG_TEMP_MIN 0x19
#define PC87427_REG_TEMP_CRIT 0x1a
#define PC87427_REG_TEMP_TYPE 0x1d
#define TEMP_STATUS_CHANEN (1 << 0)
#define TEMP_STATUS_LOWFLG (1 << 1)
#define TEMP_STATUS_HIGHFLG (1 << 2)
#define TEMP_STATUS_CRITFLG (1 << 3)
#define TEMP_STATUS_SENSERR (1 << 5)
#define TEMP_TYPE_MASK (3 << 5)
#define TEMP_TYPE_THERMISTOR (1 << 5)
#define TEMP_TYPE_REMOTE_DIODE (2 << 5)
#define TEMP_TYPE_LOCAL_DIODE (3 << 5)
/* Dedicated function to read all registers related to a given temperature
input. This saves us quite a few locks and bank selections.
Must be called with data->lock held.
nr is from 0 to 5 */
static void pc87427_readall_temp(struct pc87427_data *data, u8 nr)
{
int iobase = data->address[LD_TEMP];
outb(BANK_TM(nr), iobase + PC87427_REG_BANK);
data->temp[nr] = le16_to_cpu(inw(iobase + PC87427_REG_TEMP));
data->temp_max[nr] = inb(iobase + PC87427_REG_TEMP_MAX);
data->temp_min[nr] = inb(iobase + PC87427_REG_TEMP_MIN);
data->temp_crit[nr] = inb(iobase + PC87427_REG_TEMP_CRIT);
data->temp_type[nr] = inb(iobase + PC87427_REG_TEMP_TYPE);
data->temp_status[nr] = inb(iobase + PC87427_REG_TEMP_STATUS);
/* Clear fan alarm bits */
outb(data->temp_status[nr], iobase + PC87427_REG_TEMP_STATUS);
}
static inline unsigned int temp_type_from_reg(u8 reg)
{
switch (reg & TEMP_TYPE_MASK) {
case TEMP_TYPE_THERMISTOR:
return 4;
case TEMP_TYPE_REMOTE_DIODE:
case TEMP_TYPE_LOCAL_DIODE:
return 3;
default:
return 0;
}
}
/* We assume 8-bit thermal sensors; 9-bit thermal sensors are possible
too, but I have no idea how to figure out when they are used. */
static inline long temp_from_reg(s16 reg)
{
return reg * 1000 / 256;
}
static inline long temp_from_reg8(s8 reg)
{
return reg * 1000;
}
/*
* Data interface
*/
@@ -279,6 +356,13 @@ static struct pc87427_data *pc87427_update_device(struct device *dev)
pc87427_readall_pwm(data, i);
}
/* Temperature channels */
for (i = 0; i < 6; i++) {
if (!(data->temp_enabled & (1 << i)))
continue;
pc87427_readall_temp(data, i);
}
data->last_updated = jiffies;
done:
@@ -595,6 +679,251 @@ static const struct attribute_group pc87427_group_pwm[4] = {
{ .attrs = pc87427_attributes_pwm[3] },
};
static ssize_t show_temp_input(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct pc87427_data *data = pc87427_update_device(dev);
int nr = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%ld\n", temp_from_reg(data->temp[nr]));
}
static ssize_t show_temp_min(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct pc87427_data *data = pc87427_update_device(dev);
int nr = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%ld\n", temp_from_reg8(data->temp_min[nr]));
}
static ssize_t show_temp_max(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct pc87427_data *data = pc87427_update_device(dev);
int nr = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%ld\n", temp_from_reg8(data->temp_max[nr]));
}
static ssize_t show_temp_crit(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct pc87427_data *data = pc87427_update_device(dev);
int nr = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%ld\n", temp_from_reg8(data->temp_crit[nr]));
}
static ssize_t show_temp_type(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct pc87427_data *data = pc87427_update_device(dev);
int nr = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%u\n", temp_type_from_reg(data->temp_type[nr]));
}
static ssize_t show_temp_min_alarm(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct pc87427_data *data = pc87427_update_device(dev);
int nr = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%d\n", !!(data->temp_status[nr]
& TEMP_STATUS_LOWFLG));
}
static ssize_t show_temp_max_alarm(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct pc87427_data *data = pc87427_update_device(dev);
int nr = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%d\n", !!(data->temp_status[nr]
& TEMP_STATUS_HIGHFLG));
}
static ssize_t show_temp_crit_alarm(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct pc87427_data *data = pc87427_update_device(dev);
int nr = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%d\n", !!(data->temp_status[nr]
& TEMP_STATUS_CRITFLG));
}
static ssize_t show_temp_fault(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct pc87427_data *data = pc87427_update_device(dev);
int nr = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%d\n", !!(data->temp_status[nr]
& TEMP_STATUS_SENSERR));
}
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_input, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp_input, NULL, 2);
static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp_input, NULL, 3);
static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp_input, NULL, 4);
static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, show_temp_input, NULL, 5);
static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO, show_temp_min, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO, show_temp_min, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO, show_temp_min, NULL, 2);
static SENSOR_DEVICE_ATTR(temp4_min, S_IRUGO, show_temp_min, NULL, 3);
static SENSOR_DEVICE_ATTR(temp5_min, S_IRUGO, show_temp_min, NULL, 4);
static SENSOR_DEVICE_ATTR(temp6_min, S_IRUGO, show_temp_min, NULL, 5);
static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO, show_temp_max, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO, show_temp_max, NULL, 2);
static SENSOR_DEVICE_ATTR(temp4_max, S_IRUGO, show_temp_max, NULL, 3);
static SENSOR_DEVICE_ATTR(temp5_max, S_IRUGO, show_temp_max, NULL, 4);
static SENSOR_DEVICE_ATTR(temp6_max, S_IRUGO, show_temp_max, NULL, 5);
static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp_crit, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp_crit, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_crit, S_IRUGO, show_temp_crit, NULL, 2);
static SENSOR_DEVICE_ATTR(temp4_crit, S_IRUGO, show_temp_crit, NULL, 3);
static SENSOR_DEVICE_ATTR(temp5_crit, S_IRUGO, show_temp_crit, NULL, 4);
static SENSOR_DEVICE_ATTR(temp6_crit, S_IRUGO, show_temp_crit, NULL, 5);
static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, show_temp_type, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_type, S_IRUGO, show_temp_type, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2);
static SENSOR_DEVICE_ATTR(temp4_type, S_IRUGO, show_temp_type, NULL, 3);
static SENSOR_DEVICE_ATTR(temp5_type, S_IRUGO, show_temp_type, NULL, 4);
static SENSOR_DEVICE_ATTR(temp6_type, S_IRUGO, show_temp_type, NULL, 5);
static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO,
show_temp_min_alarm, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO,
show_temp_min_alarm, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO,
show_temp_min_alarm, NULL, 2);
static SENSOR_DEVICE_ATTR(temp4_min_alarm, S_IRUGO,
show_temp_min_alarm, NULL, 3);
static SENSOR_DEVICE_ATTR(temp5_min_alarm, S_IRUGO,
show_temp_min_alarm, NULL, 4);
static SENSOR_DEVICE_ATTR(temp6_min_alarm, S_IRUGO,
show_temp_min_alarm, NULL, 5);
static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO,
show_temp_max_alarm, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO,
show_temp_max_alarm, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO,
show_temp_max_alarm, NULL, 2);
static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO,
show_temp_max_alarm, NULL, 3);
static SENSOR_DEVICE_ATTR(temp5_max_alarm, S_IRUGO,
show_temp_max_alarm, NULL, 4);
static SENSOR_DEVICE_ATTR(temp6_max_alarm, S_IRUGO,
show_temp_max_alarm, NULL, 5);
static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO,
show_temp_crit_alarm, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO,
show_temp_crit_alarm, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_crit_alarm, S_IRUGO,
show_temp_crit_alarm, NULL, 2);
static SENSOR_DEVICE_ATTR(temp4_crit_alarm, S_IRUGO,
show_temp_crit_alarm, NULL, 3);
static SENSOR_DEVICE_ATTR(temp5_crit_alarm, S_IRUGO,
show_temp_crit_alarm, NULL, 4);
static SENSOR_DEVICE_ATTR(temp6_crit_alarm, S_IRUGO,
show_temp_crit_alarm, NULL, 5);
static SENSOR_DEVICE_ATTR(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_temp_fault, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_temp_fault, NULL, 2);
static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_temp_fault, NULL, 3);
static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_temp_fault, NULL, 4);
static SENSOR_DEVICE_ATTR(temp6_fault, S_IRUGO, show_temp_fault, NULL, 5);
static struct attribute *pc87427_attributes_temp[6][10] = {
{
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp1_min.dev_attr.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr,
&sensor_dev_attr_temp1_crit.dev_attr.attr,
&sensor_dev_attr_temp1_type.dev_attr.attr,
&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp1_fault.dev_attr.attr,
NULL
}, {
&sensor_dev_attr_temp2_input.dev_attr.attr,
&sensor_dev_attr_temp2_min.dev_attr.attr,
&sensor_dev_attr_temp2_max.dev_attr.attr,
&sensor_dev_attr_temp2_crit.dev_attr.attr,
&sensor_dev_attr_temp2_type.dev_attr.attr,
&sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_fault.dev_attr.attr,
NULL
}, {
&sensor_dev_attr_temp3_input.dev_attr.attr,
&sensor_dev_attr_temp3_min.dev_attr.attr,
&sensor_dev_attr_temp3_max.dev_attr.attr,
&sensor_dev_attr_temp3_crit.dev_attr.attr,
&sensor_dev_attr_temp3_type.dev_attr.attr,
&sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
&sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp3_fault.dev_attr.attr,
NULL
}, {
&sensor_dev_attr_temp4_input.dev_attr.attr,
&sensor_dev_attr_temp4_min.dev_attr.attr,
&sensor_dev_attr_temp4_max.dev_attr.attr,
&sensor_dev_attr_temp4_crit.dev_attr.attr,
&sensor_dev_attr_temp4_type.dev_attr.attr,
&sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
&sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp4_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp4_fault.dev_attr.attr,
NULL
}, {
&sensor_dev_attr_temp5_input.dev_attr.attr,
&sensor_dev_attr_temp5_min.dev_attr.attr,
&sensor_dev_attr_temp5_max.dev_attr.attr,
&sensor_dev_attr_temp5_crit.dev_attr.attr,
&sensor_dev_attr_temp5_type.dev_attr.attr,
&sensor_dev_attr_temp5_min_alarm.dev_attr.attr,
&sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp5_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp5_fault.dev_attr.attr,
NULL
}, {
&sensor_dev_attr_temp6_input.dev_attr.attr,
&sensor_dev_attr_temp6_min.dev_attr.attr,
&sensor_dev_attr_temp6_max.dev_attr.attr,
&sensor_dev_attr_temp6_crit.dev_attr.attr,
&sensor_dev_attr_temp6_type.dev_attr.attr,
&sensor_dev_attr_temp6_min_alarm.dev_attr.attr,
&sensor_dev_attr_temp6_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp6_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp6_fault.dev_attr.attr,
NULL
}
};
static const struct attribute_group pc87427_group_temp[6] = {
{ .attrs = pc87427_attributes_temp[0] },
{ .attrs = pc87427_attributes_temp[1] },
{ .attrs = pc87427_attributes_temp[2] },
{ .attrs = pc87427_attributes_temp[3] },
{ .attrs = pc87427_attributes_temp[4] },
{ .attrs = pc87427_attributes_temp[5] },
};
static ssize_t show_name(struct device *dev, struct device_attribute
*devattr, char *buf)
{
@@ -659,7 +988,7 @@ static void __devinit pc87427_init_device(struct device *dev)
/* The FMC module should be ready */
reg = pc87427_read8(data, LD_FAN, PC87427_REG_BANK);
if (!(reg & 0x80))
dev_warn(dev, "FMC module not ready!\n");
dev_warn(dev, "%s module not ready!\n", "FMC");
/* Check which fans are enabled */
for (i = 0; i < 8; i++) {
@@ -701,6 +1030,19 @@ static void __devinit pc87427_init_device(struct device *dev)
data->pwm_auto_ok |= (1 << i);
}
}
/* The HMC module should be ready */
reg = pc87427_read8(data, LD_TEMP, PC87427_REG_BANK);
if (!(reg & 0x80))
dev_warn(dev, "%s module not ready!\n", "HMC");
/* Check which temperature channels are enabled */
for (i = 0; i < 6; i++) {
reg = pc87427_read8_bank(data, LD_TEMP, BANK_TM(i),
PC87427_REG_TEMP_STATUS);
if (reg & TEMP_STATUS_CHANEN)
data->temp_enabled |= (1 << i);
}
}
static int __devinit pc87427_probe(struct platform_device *pdev)
@@ -749,6 +1091,14 @@ static int __devinit pc87427_probe(struct platform_device *pdev)
if (err)
goto exit_remove_files;
}
for (i = 0; i < 6; i++) {
if (!(data->temp_enabled & (1 << i)))
continue;
err = sysfs_create_group(&pdev->dev.kobj,
&pc87427_group_temp[i]);
if (err)
goto exit_remove_files;
}
data->hwmon_dev = hwmon_device_register(&pdev->dev);
if (IS_ERR(data->hwmon_dev)) {
@@ -770,6 +1120,11 @@ exit_remove_files:
continue;
sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_pwm[i]);
}
for (i = 0; i < 6; i++) {
if (!(data->temp_enabled & (1 << i)))
continue;
sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_temp[i]);
}
exit_release_region:
pc87427_release_regions(pdev, res_count);
exit_kfree:
@@ -798,6 +1153,11 @@ static int __devexit pc87427_remove(struct platform_device *pdev)
continue;
sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_pwm[i]);
}
for (i = 0; i < 6; i++) {
if (!(data->temp_enabled & (1 << i)))
continue;
sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_temp[i]);
}
platform_set_drvdata(pdev, NULL);
kfree(data);