summaryrefslogtreecommitdiffstats
path: root/drivers/hwmon/it87.c
diff options
context:
space:
mode:
authorJean Delvare <khali@linux-fr.org>2010-10-28 20:31:51 +0200
committerJean Delvare <khali@endymion.delvare>2010-10-28 20:31:51 +0200
commit44c1bcd4bcde32b2a31a6775a277706ab489c0dc (patch)
treebb8f17e5c2d0e6e871906f3cddb32290cf1a09b8 /drivers/hwmon/it87.c
parenthwmon: (it87) Move conversion functions (diff)
downloadlinux-44c1bcd4bcde32b2a31a6775a277706ab489c0dc.tar.xz
linux-44c1bcd4bcde32b2a31a6775a277706ab489c0dc.zip
hwmon: (it87) Add support for the IT8721F/IT8758E
Add support for the IT8721F/IT8758E. These new chips differ from the older IT87xxF chips in the following ways: * ADC LSB is 12 mV instead of 16 mV. * PWM values are 8-bit instead of 7-bit. There are other minor changes we don't have to care about in the driver. Another change is that we will handle internal voltage scaling in the driver instead of delegating the work to user-space. Signed-off-by: Jean Delvare <khali@linux-fr.org>
Diffstat (limited to 'drivers/hwmon/it87.c')
-rw-r--r--drivers/hwmon/it87.c116
1 files changed, 94 insertions, 22 deletions
diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c
index 7a3616ccbf05..14a5d981be7d 100644
--- a/drivers/hwmon/it87.c
+++ b/drivers/hwmon/it87.c
@@ -15,7 +15,9 @@
* IT8716F Super I/O chip w/LPC interface
* IT8718F Super I/O chip w/LPC interface
* IT8720F Super I/O chip w/LPC interface
+ * IT8721F Super I/O chip w/LPC interface
* IT8726F Super I/O chip w/LPC interface
+ * IT8758E Super I/O chip w/LPC interface
* Sis950 A clone of the IT8705F
*
* Copyright (C) 2001 Chris Gauthron
@@ -54,7 +56,7 @@
#define DRVNAME "it87"
-enum chips { it87, it8712, it8716, it8718, it8720 };
+enum chips { it87, it8712, it8716, it8718, it8720, it8721 };
static unsigned short force_id;
module_param(force_id, ushort, 0);
@@ -126,6 +128,7 @@ superio_exit(void)
#define IT8716F_DEVID 0x8716
#define IT8718F_DEVID 0x8718
#define IT8720F_DEVID 0x8720
+#define IT8721F_DEVID 0x8721
#define IT8726F_DEVID 0x8726
#define IT87_ACT_REG 0x30
#define IT87_BASE_REG 0x60
@@ -229,6 +232,7 @@ struct it87_data {
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
+ u16 in_scaled; /* Internal voltage sensors are scaled */
u8 in[9]; /* Register value */
u8 in_max[8]; /* Register value */
u8 in_min[8]; /* Register value */
@@ -260,8 +264,32 @@ struct it87_data {
s8 auto_temp[3][5]; /* [nr][0] is point1_temp_hyst */
};
-#define IN_TO_REG(val) (SENSORS_LIMIT((((val) + 8) / 16), 0, 255))
-#define IN_FROM_REG(val) ((val) * 16)
+static u8 in_to_reg(const struct it87_data *data, int nr, long val)
+{
+ long lsb;
+
+ if (data->type == it8721) {
+ if (data->in_scaled & (1 << nr))
+ lsb = 24;
+ else
+ lsb = 12;
+ } else
+ lsb = 16;
+
+ val = DIV_ROUND_CLOSEST(val, lsb);
+ return SENSORS_LIMIT(val, 0, 255);
+}
+
+static int in_from_reg(const struct it87_data *data, int nr, int val)
+{
+ if (data->type == it8721) {
+ if (data->in_scaled & (1 << nr))
+ return val * 24;
+ else
+ return val * 12;
+ } else
+ return val * 16;
+}
static inline u8 FAN_TO_REG(long rpm, int div)
{
@@ -289,8 +317,22 @@ static inline u16 FAN16_TO_REG(long rpm)
((val) + 500) / 1000), -128, 127))
#define TEMP_FROM_REG(val) ((val) * 1000)
-#define PWM_TO_REG(val) ((val) >> 1)
-#define PWM_FROM_REG(val) (((val) & 0x7f) << 1)
+static u8 pwm_to_reg(const struct it87_data *data, long val)
+{
+ if (data->type == it8721)
+ return val;
+ else
+ return val >> 1;
+}
+
+static int pwm_from_reg(const struct it87_data *data, u8 reg)
+{
+ if (data->type == it8721)
+ return reg;
+ else
+ return (reg & 0x7f) << 1;
+}
+
static int DIV_TO_REG(int val)
{
@@ -321,7 +363,8 @@ static inline int has_16bit_fans(const struct it87_data *data)
|| (data->type == it8712 && data->revision >= 0x08)
|| data->type == it8716
|| data->type == it8718
- || data->type == it8720;
+ || data->type == it8720
+ || data->type == it8721;
}
static inline int has_old_autopwm(const struct it87_data *data)
@@ -359,7 +402,7 @@ static ssize_t show_in(struct device *dev, struct device_attribute *attr,
int nr = sensor_attr->index;
struct it87_data *data = it87_update_device(dev);
- return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr]));
+ return sprintf(buf, "%d\n", in_from_reg(data, nr, data->in[nr]));
}
static ssize_t show_in_min(struct device *dev, struct device_attribute *attr,
@@ -369,7 +412,7 @@ static ssize_t show_in_min(struct device *dev, struct device_attribute *attr,
int nr = sensor_attr->index;
struct it87_data *data = it87_update_device(dev);
- return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr]));
+ return sprintf(buf, "%d\n", in_from_reg(data, nr, data->in_min[nr]));
}
static ssize_t show_in_max(struct device *dev, struct device_attribute *attr,
@@ -379,7 +422,7 @@ static ssize_t show_in_max(struct device *dev, struct device_attribute *attr,
int nr = sensor_attr->index;
struct it87_data *data = it87_update_device(dev);
- return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr]));
+ return sprintf(buf, "%d\n", in_from_reg(data, nr, data->in_max[nr]));
}
static ssize_t set_in_min(struct device *dev, struct device_attribute *attr,
@@ -395,7 +438,7 @@ static ssize_t set_in_min(struct device *dev, struct device_attribute *attr,
return -EINVAL;
mutex_lock(&data->update_lock);
- data->in_min[nr] = IN_TO_REG(val);
+ data->in_min[nr] = in_to_reg(data, nr, val);
it87_write_value(data, IT87_REG_VIN_MIN(nr),
data->in_min[nr]);
mutex_unlock(&data->update_lock);
@@ -414,7 +457,7 @@ static ssize_t set_in_max(struct device *dev, struct device_attribute *attr,
return -EINVAL;
mutex_lock(&data->update_lock);
- data->in_max[nr] = IN_TO_REG(val);
+ data->in_max[nr] = in_to_reg(data, nr, val);
it87_write_value(data, IT87_REG_VIN_MAX(nr),
data->in_max[nr]);
mutex_unlock(&data->update_lock);
@@ -644,7 +687,8 @@ static ssize_t show_pwm(struct device *dev, struct device_attribute *attr,
int nr = sensor_attr->index;
struct it87_data *data = it87_update_device(dev);
- return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm_duty[nr]));
+ return sprintf(buf, "%d\n",
+ pwm_from_reg(data, data->pwm_duty[nr]));
}
static ssize_t show_pwm_freq(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -814,7 +858,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
return -EINVAL;
mutex_lock(&data->update_lock);
- data->pwm_duty[nr] = PWM_TO_REG(val);
+ data->pwm_duty[nr] = pwm_to_reg(data, val);
/* If we are in manual mode, write the duty cycle immediately;
* otherwise, just store it for later use. */
if (!(data->pwm_ctrl[nr] & 0x80)) {
@@ -918,7 +962,8 @@ static ssize_t show_auto_pwm(struct device *dev,
int nr = sensor_attr->nr;
int point = sensor_attr->index;
- return sprintf(buf, "%d\n", PWM_FROM_REG(data->auto_pwm[nr][point]));
+ return sprintf(buf, "%d\n",
+ pwm_from_reg(data, data->auto_pwm[nr][point]));
}
static ssize_t set_auto_pwm(struct device *dev,
@@ -935,7 +980,7 @@ static ssize_t set_auto_pwm(struct device *dev,
return -EINVAL;
mutex_lock(&data->update_lock);
- data->auto_pwm[nr][point] = PWM_TO_REG(val);
+ data->auto_pwm[nr][point] = pwm_to_reg(data, val);
it87_write_value(data, IT87_REG_AUTO_PWM(nr, point),
data->auto_pwm[nr][point]);
mutex_unlock(&data->update_lock);
@@ -1205,9 +1250,16 @@ static ssize_t show_label(struct device *dev, struct device_attribute *attr,
"5VSB",
"Vbat",
};
+ static const char *labels_it8721[] = {
+ "+3.3V",
+ "3VSB",
+ "Vbat",
+ };
+ struct it87_data *data = dev_get_drvdata(dev);
int nr = to_sensor_dev_attr(attr)->index;
- return sprintf(buf, "%s\n", labels[nr]);
+ return sprintf(buf, "%s\n", data->type == it8721 ? labels_it8721[nr]
+ : labels[nr]);
}
static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_label, NULL, 0);
static SENSOR_DEVICE_ATTR(in7_label, S_IRUGO, show_label, NULL, 1);
@@ -1492,6 +1544,9 @@ static int __init it87_find(unsigned short *address,
case IT8720F_DEVID:
sio_data->type = it8720;
break;
+ case IT8721F_DEVID:
+ sio_data->type = it8721;
+ break;
case 0xffff: /* No device at all */
goto exit;
default:
@@ -1532,11 +1587,17 @@ static int __init it87_find(unsigned short *address,
int reg;
superio_select(GPIO);
- /* We need at least 4 VID pins */
+
reg = superio_inb(IT87_SIO_GPIO3_REG);
- if (reg & 0x0f) {
- pr_info("it87: VID is disabled (pins used for GPIO)\n");
+ if (sio_data->type == it8721) {
+ /* The IT8721F/IT8758E doesn't have VID pins at all */
sio_data->skip_vid = 1;
+ } else {
+ /* We need at least 4 VID pins */
+ if (reg & 0x0f) {
+ pr_info("it87: VID is disabled (pins used for GPIO)\n");
+ sio_data->skip_vid = 1;
+ }
}
/* Check if fan3 is there or not */
@@ -1574,7 +1635,7 @@ static int __init it87_find(unsigned short *address,
}
if (reg & (1 << 0))
sio_data->internal |= (1 << 0);
- if (reg & (1 << 1))
+ if ((reg & (1 << 1)) || sio_data->type == it8721)
sio_data->internal |= (1 << 1);
sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f;
@@ -1652,6 +1713,7 @@ static int __devinit it87_probe(struct platform_device *pdev)
"it8716",
"it8718",
"it8720",
+ "it8721",
};
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
@@ -1688,6 +1750,16 @@ static int __devinit it87_probe(struct platform_device *pdev)
/* Check PWM configuration */
enable_pwm_interface = it87_check_pwm(dev);
+ /* Starting with IT8721F, we handle scaling of internal voltages */
+ if (data->type == it8721) {
+ if (sio_data->internal & (1 << 0))
+ data->in_scaled |= (1 << 3); /* in3 is AVCC */
+ if (sio_data->internal & (1 << 1))
+ data->in_scaled |= (1 << 7); /* in7 is VSB */
+ if (sio_data->internal & (1 << 2))
+ data->in_scaled |= (1 << 8); /* in8 is Vbat */
+ }
+
/* Initialize the IT87 chip */
it87_init_device(pdev);
@@ -2053,7 +2125,7 @@ static struct it87_data *it87_update_device(struct device *dev)
data->sensor = it87_read_value(data, IT87_REG_TEMP_ENABLE);
/* The 8705 does not have VID capability.
- The 8718 and the 8720 don't use IT87_REG_VID for the
+ The 8718 and later don't use IT87_REG_VID for the
same purpose. */
if (data->type == it8712 || data->type == it8716) {
data->vid = it87_read_value(data, IT87_REG_VID);
@@ -2153,7 +2225,7 @@ static void __exit sm_it87_exit(void)
MODULE_AUTHOR("Chris Gauthron, "
"Jean Delvare <khali@linux-fr.org>");
-MODULE_DESCRIPTION("IT8705F/8712F/8716F/8718F/8720F/8726F, SiS950 driver");
+MODULE_DESCRIPTION("IT8705F/IT871xF/IT872xF hardware monitoring driver");
module_param(update_vbat, bool, 0);
MODULE_PARM_DESC(update_vbat, "Update vbat if set else return powerup value");
module_param(fix_pwm_polarity, bool, 0);