Commit 51c997d8 authored by Jean Delvare's avatar Jean Delvare Committed by Jean Delvare

hwmon/f71805f: Add support for the Fintek F71872F/FG chip

Add support for the Fintek F71872F/FG Super-I/O chip. It is basically the
same as the Fintek F71805F/FG as far as hardware monitoring is concerned,
with two additional internal voltages monitored (VSB and battery), and 6
VID inputs (not yet supported.)

To make things a bit more confusing, two of the voltage input pins (in4
and in8) can be used for other functions. The driver reads the pin
configuration from the Super-I/O configuration space to decide whether
it must create interface files for these inputs or not.

Many thanks to Nikolay Derkach for testing the early iterations of this
code and reporting bugs.
Signed-off-by: default avatarJean Delvare <khali@linux-fr.org>
parent ba224e2c
...@@ -6,6 +6,10 @@ Supported chips: ...@@ -6,6 +6,10 @@ Supported chips:
Prefix: 'f71805f' Prefix: 'f71805f'
Addresses scanned: none, address read from Super I/O config space Addresses scanned: none, address read from Super I/O config space
Datasheet: Provided by Fintek on request Datasheet: Provided by Fintek on request
* Fintek F71872F/FG
Prefix: 'f71872f'
Addresses scanned: none, address read from Super I/O config space
Datasheet: Provided by Fintek on request
Author: Jean Delvare <khali@linux-fr.org> Author: Jean Delvare <khali@linux-fr.org>
...@@ -30,6 +34,10 @@ source), 3 fans and 3 temperature sensors. ...@@ -30,6 +34,10 @@ source), 3 fans and 3 temperature sensors.
This chip also has fan controlling features, using either DC or PWM, in This chip also has fan controlling features, using either DC or PWM, in
three different modes (one manual, two automatic). three different modes (one manual, two automatic).
The Fintek F71872F/FG Super I/O chip is almost the same, with two
additional internal voltages monitored (VSB and battery). It also features
6 VID inputs. The VID inputs are not yet supported by this driver.
The driver assumes that no more than one chip is present, which seems The driver assumes that no more than one chip is present, which seems
reasonable. reasonable.
...@@ -41,7 +49,8 @@ Voltages are sampled by an 8-bit ADC with a LSB of 8 mV. The supported ...@@ -41,7 +49,8 @@ Voltages are sampled by an 8-bit ADC with a LSB of 8 mV. The supported
range is thus from 0 to 2.040 V. Voltage values outside of this range range is thus from 0 to 2.040 V. Voltage values outside of this range
need external resistors. An exception is in0, which is used to monitor need external resistors. An exception is in0, which is used to monitor
the chip's own power source (+3.3V), and is divided internally by a the chip's own power source (+3.3V), and is divided internally by a
factor 2. factor 2. For the F71872F/FG, in9 (VSB) and in10 (battery) are also
divided internally by a factor 2.
The two LSB of the voltage limit registers are not used (always 0), so The two LSB of the voltage limit registers are not used (always 0), so
you can only set the limits in steps of 32 mV (before scaling). you can only set the limits in steps of 32 mV (before scaling).
...@@ -60,9 +69,12 @@ in5 VIN5 +12V 200K 20K 11.00 1.05 V ...@@ -60,9 +69,12 @@ in5 VIN5 +12V 200K 20K 11.00 1.05 V
in6 VIN6 VCC1.5V 10K - 1.00 1.50 V in6 VIN6 VCC1.5V 10K - 1.00 1.50 V
in7 VIN7 VCORE 10K - 1.00 ~1.40 V (1) in7 VIN7 VCORE 10K - 1.00 ~1.40 V (1)
in8 VIN8 VSB5V 200K 47K 1.00 0.95 V in8 VIN8 VSB5V 200K 47K 1.00 0.95 V
in10 VSB VSB3.3V int. int. 2.00 1.65 V (3)
in9 VBAT VBATTERY int. int. 2.00 1.50 V (3)
(1) Depends on your hardware setup. (1) Depends on your hardware setup.
(2) Obviously not correct, swapping R1 and R2 would make more sense. (2) Obviously not correct, swapping R1 and R2 would make more sense.
(3) F71872F/FG only.
These values can be used as hints at best, as motherboard manufacturers These values can be used as hints at best, as motherboard manufacturers
are free to use a completely different setup. As a matter of fact, the are free to use a completely different setup. As a matter of fact, the
......
...@@ -142,11 +142,12 @@ config SENSORS_DS1621 ...@@ -142,11 +142,12 @@ config SENSORS_DS1621
will be called ds1621. will be called ds1621.
config SENSORS_F71805F config SENSORS_F71805F
tristate "Fintek F71805F/FG" tristate "Fintek F71805F/FG and F71872F/FG"
depends on HWMON && EXPERIMENTAL depends on HWMON && EXPERIMENTAL
help help
If you say yes here you get support for hardware monitoring If you say yes here you get support for hardware monitoring
features of the Fintek F71805F/FG chips. features of the Fintek F71805F/FG and F71872F/FG Super-I/O
chips.
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called f71805f. will be called f71805f.
......
/* /*
* f71805f.c - driver for the Fintek F71805F/FG Super-I/O chip integrated * f71805f.c - driver for the Fintek F71805F/FG and F71872F/FG Super-I/O
* hardware monitoring features * chips integrated hardware monitoring features
* Copyright (C) 2005-2006 Jean Delvare <khali@linux-fr.org> * Copyright (C) 2005-2006 Jean Delvare <khali@linux-fr.org>
* *
* The F71805F/FG is a LPC Super-I/O chip made by Fintek. It integrates * The F71805F/FG is a LPC Super-I/O chip made by Fintek. It integrates
* complete hardware monitoring features: voltage, fan and temperature * complete hardware monitoring features: voltage, fan and temperature
* sensors, and manual and automatic fan speed control. * sensors, and manual and automatic fan speed control.
* *
* The F71872F/FG is almost the same, with two more voltages monitored,
* and 6 VID inputs.
*
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or * the Free Software Foundation; either version 2 of the License, or
...@@ -37,6 +40,7 @@ ...@@ -37,6 +40,7 @@
static struct platform_device *pdev; static struct platform_device *pdev;
#define DRVNAME "f71805f" #define DRVNAME "f71805f"
enum kinds { f71805f, f71872f };
/* /*
* Super-I/O constants and functions * Super-I/O constants and functions
...@@ -48,11 +52,13 @@ static struct platform_device *pdev; ...@@ -48,11 +52,13 @@ static struct platform_device *pdev;
#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */ #define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
#define SIO_REG_DEVREV 0x22 /* Device revision */ #define SIO_REG_DEVREV 0x22 /* Device revision */
#define SIO_REG_MANID 0x23 /* Fintek ID (2 bytes) */ #define SIO_REG_MANID 0x23 /* Fintek ID (2 bytes) */
#define SIO_REG_FNSEL1 0x29 /* Multi Function Select 1 (F71872F) */
#define SIO_REG_ENABLE 0x30 /* Logical device enable */ #define SIO_REG_ENABLE 0x30 /* Logical device enable */
#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ #define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
#define SIO_FINTEK_ID 0x1934 #define SIO_FINTEK_ID 0x1934
#define SIO_F71805F_ID 0x0406 #define SIO_F71805F_ID 0x0406
#define SIO_F71872F_ID 0x0341
static inline int static inline int
superio_inb(int base, int reg) superio_inb(int base, int reg)
...@@ -104,10 +110,10 @@ superio_exit(int base) ...@@ -104,10 +110,10 @@ superio_exit(int base)
* Registers * Registers
*/ */
/* in nr from 0 to 8 (8-bit values) */ /* in nr from 0 to 10 (8-bit values) */
#define F71805F_REG_IN(nr) (0x10 + (nr)) #define F71805F_REG_IN(nr) (0x10 + (nr))
#define F71805F_REG_IN_HIGH(nr) (0x40 + 2 * (nr)) #define F71805F_REG_IN_HIGH(nr) ((nr) < 10 ? 0x40 + 2 * (nr) : 0x2E)
#define F71805F_REG_IN_LOW(nr) (0x41 + 2 * (nr)) #define F71805F_REG_IN_LOW(nr) ((nr) < 10 ? 0x41 + 2 * (nr) : 0x2F)
/* fan nr from 0 to 2 (12-bit values, two registers) */ /* fan nr from 0 to 2 (12-bit values, two registers) */
#define F71805F_REG_FAN(nr) (0x20 + 2 * (nr)) #define F71805F_REG_FAN(nr) (0x20 + 2 * (nr))
#define F71805F_REG_FAN_LOW(nr) (0x28 + 2 * (nr)) #define F71805F_REG_FAN_LOW(nr) (0x28 + 2 * (nr))
...@@ -150,9 +156,10 @@ struct f71805f_data { ...@@ -150,9 +156,10 @@ struct f71805f_data {
unsigned long last_limits; /* In jiffies */ unsigned long last_limits; /* In jiffies */
/* Register values */ /* Register values */
u8 in[9]; u8 in[11];
u8 in_high[9]; u8 in_high[11];
u8 in_low[9]; u8 in_low[11];
u16 has_in;
u16 fan[3]; u16 fan[3];
u16 fan_low[3]; u16 fan_low[3];
u16 fan_target[3]; u16 fan_target[3];
...@@ -166,6 +173,11 @@ struct f71805f_data { ...@@ -166,6 +173,11 @@ struct f71805f_data {
unsigned long alarms; unsigned long alarms;
}; };
struct f71805f_sio_data {
enum kinds kind;
u8 fnsel1;
};
static inline long in_from_reg(u8 reg) static inline long in_from_reg(u8 reg)
{ {
return (reg * 8); return (reg * 8);
...@@ -316,7 +328,9 @@ static struct f71805f_data *f71805f_update_device(struct device *dev) ...@@ -316,7 +328,9 @@ static struct f71805f_data *f71805f_update_device(struct device *dev)
/* Limit registers cache is refreshed after 60 seconds */ /* Limit registers cache is refreshed after 60 seconds */
if (time_after(jiffies, data->last_updated + 60 * HZ) if (time_after(jiffies, data->last_updated + 60 * HZ)
|| !data->valid) { || !data->valid) {
for (nr = 0; nr < 9; nr++) { for (nr = 0; nr < 11; nr++) {
if (!(data->has_in & (1 << nr)))
continue;
data->in_high[nr] = f71805f_read8(data, data->in_high[nr] = f71805f_read8(data,
F71805F_REG_IN_HIGH(nr)); F71805F_REG_IN_HIGH(nr));
data->in_low[nr] = f71805f_read8(data, data->in_low[nr] = f71805f_read8(data,
...@@ -346,7 +360,9 @@ static struct f71805f_data *f71805f_update_device(struct device *dev) ...@@ -346,7 +360,9 @@ static struct f71805f_data *f71805f_update_device(struct device *dev)
/* Measurement registers cache is refreshed after 1 second */ /* Measurement registers cache is refreshed after 1 second */
if (time_after(jiffies, data->last_updated + HZ) if (time_after(jiffies, data->last_updated + HZ)
|| !data->valid) { || !data->valid) {
for (nr = 0; nr < 9; nr++) { for (nr = 0; nr < 11; nr++) {
if (!(data->has_in & (1 << nr)))
continue;
data->in[nr] = f71805f_read8(data, data->in[nr] = f71805f_read8(data,
F71805F_REG_IN(nr)); F71805F_REG_IN(nr));
} }
...@@ -385,35 +401,43 @@ static ssize_t show_in0(struct device *dev, struct device_attribute *devattr, ...@@ -385,35 +401,43 @@ static ssize_t show_in0(struct device *dev, struct device_attribute *devattr,
char *buf) char *buf)
{ {
struct f71805f_data *data = f71805f_update_device(dev); struct f71805f_data *data = f71805f_update_device(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
int nr = attr->index;
return sprintf(buf, "%ld\n", in0_from_reg(data->in[0])); return sprintf(buf, "%ld\n", in0_from_reg(data->in[nr]));
} }
static ssize_t show_in0_max(struct device *dev, struct device_attribute static ssize_t show_in0_max(struct device *dev, struct device_attribute
*devattr, char *buf) *devattr, char *buf)
{ {
struct f71805f_data *data = f71805f_update_device(dev); struct f71805f_data *data = f71805f_update_device(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
int nr = attr->index;
return sprintf(buf, "%ld\n", in0_from_reg(data->in_high[0])); return sprintf(buf, "%ld\n", in0_from_reg(data->in_high[nr]));
} }
static ssize_t show_in0_min(struct device *dev, struct device_attribute static ssize_t show_in0_min(struct device *dev, struct device_attribute
*devattr, char *buf) *devattr, char *buf)
{ {
struct f71805f_data *data = f71805f_update_device(dev); struct f71805f_data *data = f71805f_update_device(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
int nr = attr->index;
return sprintf(buf, "%ld\n", in0_from_reg(data->in_low[0])); return sprintf(buf, "%ld\n", in0_from_reg(data->in_low[nr]));
} }
static ssize_t set_in0_max(struct device *dev, struct device_attribute static ssize_t set_in0_max(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count) *devattr, const char *buf, size_t count)
{ {
struct f71805f_data *data = dev_get_drvdata(dev); struct f71805f_data *data = dev_get_drvdata(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
int nr = attr->index;
long val = simple_strtol(buf, NULL, 10); long val = simple_strtol(buf, NULL, 10);
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
data->in_high[0] = in0_to_reg(val); data->in_high[nr] = in0_to_reg(val);
f71805f_write8(data, F71805F_REG_IN_HIGH(0), data->in_high[0]); f71805f_write8(data, F71805F_REG_IN_HIGH(nr), data->in_high[nr]);
mutex_unlock(&data->update_lock); mutex_unlock(&data->update_lock);
return count; return count;
...@@ -423,11 +447,13 @@ static ssize_t set_in0_min(struct device *dev, struct device_attribute ...@@ -423,11 +447,13 @@ static ssize_t set_in0_min(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count) *devattr, const char *buf, size_t count)
{ {
struct f71805f_data *data = dev_get_drvdata(dev); struct f71805f_data *data = dev_get_drvdata(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
int nr = attr->index;
long val = simple_strtol(buf, NULL, 10); long val = simple_strtol(buf, NULL, 10);
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
data->in_low[0] = in0_to_reg(val); data->in_low[nr] = in0_to_reg(val);
f71805f_write8(data, F71805F_REG_IN_LOW(0), data->in_low[0]); f71805f_write8(data, F71805F_REG_IN_LOW(nr), data->in_low[nr]);
mutex_unlock(&data->update_lock); mutex_unlock(&data->update_lock);
return count; return count;
...@@ -770,7 +796,7 @@ static ssize_t show_alarms_in(struct device *dev, struct device_attribute ...@@ -770,7 +796,7 @@ static ssize_t show_alarms_in(struct device *dev, struct device_attribute
{ {
struct f71805f_data *data = f71805f_update_device(dev); struct f71805f_data *data = f71805f_update_device(dev);
return sprintf(buf, "%lu\n", data->alarms & 0x1ff); return sprintf(buf, "%lu\n", data->alarms & 0x7ff);
} }
static ssize_t show_alarms_fan(struct device *dev, struct device_attribute static ssize_t show_alarms_fan(struct device *dev, struct device_attribute
...@@ -807,9 +833,11 @@ static ssize_t show_name(struct device *dev, struct device_attribute ...@@ -807,9 +833,11 @@ static ssize_t show_name(struct device *dev, struct device_attribute
return sprintf(buf, "%s\n", data->name); return sprintf(buf, "%s\n", data->name);
} }
static DEVICE_ATTR(in0_input, S_IRUGO, show_in0, NULL); static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in0, NULL, 0);
static DEVICE_ATTR(in0_max, S_IRUGO| S_IWUSR, show_in0_max, set_in0_max); static SENSOR_DEVICE_ATTR(in0_max, S_IRUGO| S_IWUSR,
static DEVICE_ATTR(in0_min, S_IRUGO| S_IWUSR, show_in0_min, set_in0_min); show_in0_max, set_in0_max, 0);
static SENSOR_DEVICE_ATTR(in0_min, S_IRUGO| S_IWUSR,
show_in0_min, set_in0_min, 0);
static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1); static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1);
static SENSOR_DEVICE_ATTR(in1_max, S_IRUGO | S_IWUSR, static SENSOR_DEVICE_ATTR(in1_max, S_IRUGO | S_IWUSR,
show_in_max, set_in_max, 1); show_in_max, set_in_max, 1);
...@@ -850,6 +878,16 @@ static SENSOR_DEVICE_ATTR(in8_max, S_IRUGO | S_IWUSR, ...@@ -850,6 +878,16 @@ static SENSOR_DEVICE_ATTR(in8_max, S_IRUGO | S_IWUSR,
show_in_max, set_in_max, 8); show_in_max, set_in_max, 8);
static SENSOR_DEVICE_ATTR(in8_min, S_IRUGO | S_IWUSR, static SENSOR_DEVICE_ATTR(in8_min, S_IRUGO | S_IWUSR,
show_in_min, set_in_min, 8); show_in_min, set_in_min, 8);
static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, show_in0, NULL, 9);
static SENSOR_DEVICE_ATTR(in9_max, S_IRUGO | S_IWUSR,
show_in0_max, set_in0_max, 9);
static SENSOR_DEVICE_ATTR(in9_min, S_IRUGO | S_IWUSR,
show_in0_min, set_in0_min, 9);
static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, show_in0, NULL, 10);
static SENSOR_DEVICE_ATTR(in10_max, S_IRUGO | S_IWUSR,
show_in0_max, set_in0_max, 10);
static SENSOR_DEVICE_ATTR(in10_min, S_IRUGO | S_IWUSR,
show_in0_min, set_in0_min, 10);
static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO | S_IWUSR, static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO | S_IWUSR,
...@@ -916,6 +954,8 @@ static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 5); ...@@ -916,6 +954,8 @@ static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 5);
static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 6); static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 6);
static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 7); static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 7);
static SENSOR_DEVICE_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 8); static SENSOR_DEVICE_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 8);
static SENSOR_DEVICE_ATTR(in9_alarm, S_IRUGO, show_alarm, NULL, 9);
static SENSOR_DEVICE_ATTR(in10_alarm, S_IRUGO, show_alarm, NULL, 10);
static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 11); static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 11);
static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 12); static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 12);
static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13); static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13);
...@@ -929,9 +969,9 @@ static DEVICE_ATTR(alarms_temp, S_IRUGO, show_alarms_temp, NULL); ...@@ -929,9 +969,9 @@ static DEVICE_ATTR(alarms_temp, S_IRUGO, show_alarms_temp, NULL);
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
static struct attribute *f71805f_attributes[] = { static struct attribute *f71805f_attributes[] = {
&dev_attr_in0_input.attr, &sensor_dev_attr_in0_input.dev_attr.attr,
&dev_attr_in0_max.attr, &sensor_dev_attr_in0_max.dev_attr.attr,
&dev_attr_in0_min.attr, &sensor_dev_attr_in0_min.dev_attr.attr,
&sensor_dev_attr_in1_input.dev_attr.attr, &sensor_dev_attr_in1_input.dev_attr.attr,
&sensor_dev_attr_in1_max.dev_attr.attr, &sensor_dev_attr_in1_max.dev_attr.attr,
&sensor_dev_attr_in1_min.dev_attr.attr, &sensor_dev_attr_in1_min.dev_attr.attr,
...@@ -941,9 +981,6 @@ static struct attribute *f71805f_attributes[] = { ...@@ -941,9 +981,6 @@ static struct attribute *f71805f_attributes[] = {
&sensor_dev_attr_in3_input.dev_attr.attr, &sensor_dev_attr_in3_input.dev_attr.attr,
&sensor_dev_attr_in3_max.dev_attr.attr, &sensor_dev_attr_in3_max.dev_attr.attr,
&sensor_dev_attr_in3_min.dev_attr.attr, &sensor_dev_attr_in3_min.dev_attr.attr,
&sensor_dev_attr_in4_input.dev_attr.attr,
&sensor_dev_attr_in4_max.dev_attr.attr,
&sensor_dev_attr_in4_min.dev_attr.attr,
&sensor_dev_attr_in5_input.dev_attr.attr, &sensor_dev_attr_in5_input.dev_attr.attr,
&sensor_dev_attr_in5_max.dev_attr.attr, &sensor_dev_attr_in5_max.dev_attr.attr,
&sensor_dev_attr_in5_min.dev_attr.attr, &sensor_dev_attr_in5_min.dev_attr.attr,
...@@ -953,9 +990,6 @@ static struct attribute *f71805f_attributes[] = { ...@@ -953,9 +990,6 @@ static struct attribute *f71805f_attributes[] = {
&sensor_dev_attr_in7_input.dev_attr.attr, &sensor_dev_attr_in7_input.dev_attr.attr,
&sensor_dev_attr_in7_max.dev_attr.attr, &sensor_dev_attr_in7_max.dev_attr.attr,
&sensor_dev_attr_in7_min.dev_attr.attr, &sensor_dev_attr_in7_min.dev_attr.attr,
&sensor_dev_attr_in8_input.dev_attr.attr,
&sensor_dev_attr_in8_max.dev_attr.attr,
&sensor_dev_attr_in8_min.dev_attr.attr,
&sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr,
...@@ -974,11 +1008,9 @@ static struct attribute *f71805f_attributes[] = { ...@@ -974,11 +1008,9 @@ static struct attribute *f71805f_attributes[] = {
&sensor_dev_attr_in1_alarm.dev_attr.attr, &sensor_dev_attr_in1_alarm.dev_attr.attr,
&sensor_dev_attr_in2_alarm.dev_attr.attr, &sensor_dev_attr_in2_alarm.dev_attr.attr,
&sensor_dev_attr_in3_alarm.dev_attr.attr, &sensor_dev_attr_in3_alarm.dev_attr.attr,
&sensor_dev_attr_in4_alarm.dev_attr.attr,
&sensor_dev_attr_in5_alarm.dev_attr.attr, &sensor_dev_attr_in5_alarm.dev_attr.attr,
&sensor_dev_attr_in6_alarm.dev_attr.attr, &sensor_dev_attr_in6_alarm.dev_attr.attr,
&sensor_dev_attr_in7_alarm.dev_attr.attr, &sensor_dev_attr_in7_alarm.dev_attr.attr,
&sensor_dev_attr_in8_alarm.dev_attr.attr,
&dev_attr_alarms_in.attr, &dev_attr_alarms_in.attr,
&sensor_dev_attr_temp1_alarm.dev_attr.attr, &sensor_dev_attr_temp1_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_alarm.dev_attr.attr, &sensor_dev_attr_temp2_alarm.dev_attr.attr,
...@@ -994,6 +1026,41 @@ static const struct attribute_group f71805f_group = { ...@@ -994,6 +1026,41 @@ static const struct attribute_group f71805f_group = {
.attrs = f71805f_attributes, .attrs = f71805f_attributes,
}; };
static struct attribute *f71805f_attributes_optin[4][5] = {
{
&sensor_dev_attr_in4_input.dev_attr.attr,
&sensor_dev_attr_in4_max.dev_attr.attr,
&sensor_dev_attr_in4_min.dev_attr.attr,
&sensor_dev_attr_in4_alarm.dev_attr.attr,
NULL
}, {
&sensor_dev_attr_in8_input.dev_attr.attr,
&sensor_dev_attr_in8_max.dev_attr.attr,
&sensor_dev_attr_in8_min.dev_attr.attr,
&sensor_dev_attr_in8_alarm.dev_attr.attr,
NULL
}, {
&sensor_dev_attr_in9_input.dev_attr.attr,
&sensor_dev_attr_in9_max.dev_attr.attr,
&sensor_dev_attr_in9_min.dev_attr.attr,
&sensor_dev_attr_in9_alarm.dev_attr.attr,
NULL
}, {
&sensor_dev_attr_in10_input.dev_attr.attr,
&sensor_dev_attr_in10_max.dev_attr.attr,
&sensor_dev_attr_in10_min.dev_attr.attr,
&sensor_dev_attr_in10_alarm.dev_attr.attr,
NULL
}
};
static const struct attribute_group f71805f_group_optin[4] = {
{ .attrs = f71805f_attributes_optin[0] },
{ .attrs = f71805f_attributes_optin[1] },
{ .attrs = f71805f_attributes_optin[2] },
{ .attrs = f71805f_attributes_optin[3] },
};
static struct attribute *f71805f_attributes_fan[3][8] = { static struct attribute *f71805f_attributes_fan[3][8] = {
{ {
&sensor_dev_attr_fan1_input.dev_attr.attr, &sensor_dev_attr_fan1_input.dev_attr.attr,
...@@ -1084,10 +1151,16 @@ static void __devinit f71805f_init_device(struct f71805f_data *data) ...@@ -1084,10 +1151,16 @@ static void __devinit f71805f_init_device(struct f71805f_data *data)
static int __devinit f71805f_probe(struct platform_device *pdev) static int __devinit f71805f_probe(struct platform_device *pdev)
{ {
struct f71805f_sio_data *sio_data = pdev->dev.platform_data;
struct f71805f_data *data; struct f71805f_data *data;
struct resource *res; struct resource *res;
int i, err; int i, err;
static const char *names[] = {
"f71805f",
"f71872f",
};
if (!(data = kzalloc(sizeof(struct f71805f_data), GFP_KERNEL))) { if (!(data = kzalloc(sizeof(struct f71805f_data), GFP_KERNEL))) {
err = -ENOMEM; err = -ENOMEM;
printk(KERN_ERR DRVNAME ": Out of memory\n"); printk(KERN_ERR DRVNAME ": Out of memory\n");
...@@ -1097,17 +1170,51 @@ static int __devinit f71805f_probe(struct platform_device *pdev) ...@@ -1097,17 +1170,51 @@ static int __devinit f71805f_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_IO, 0); res = platform_get_resource(pdev, IORESOURCE_IO, 0);
data->addr = res->start; data->addr = res->start;
mutex_init(&data->lock); mutex_init(&data->lock);
data->name = "f71805f"; data->name = names[sio_data->kind];
mutex_init(&data->update_lock); mutex_init(&data->update_lock);
platform_set_drvdata(pdev, data); platform_set_drvdata(pdev, data);
/* Some voltage inputs depend on chip model and configuration */
switch (sio_data->kind) {
case f71805f:
data->has_in = 0x1ff;
break;
case f71872f:
data->has_in = 0x6ef;
if (sio_data->fnsel1 & 0x01)
data->has_in |= (1 << 4); /* in4 */
if (sio_data->fnsel1 & 0x02)
data->has_in |= (1 << 8); /* in8 */
break;
}
/* Initialize the F71805F chip */ /* Initialize the F71805F chip */
f71805f_init_device(data); f71805f_init_device(data);
/* Register sysfs interface files */ /* Register sysfs interface files */
if ((err = sysfs_create_group(&pdev->dev.kobj, &f71805f_group))) if ((err = sysfs_create_group(&pdev->dev.kobj, &f71805f_group)))
goto exit_free; goto exit_free;
if (data->has_in & (1 << 4)) { /* in4 */
if ((err = sysfs_create_group(&pdev->dev.kobj,
&f71805f_group_optin[0])))
goto exit_remove_files;
}
if (data->has_in & (1 << 8)) { /* in8 */
if ((err = sysfs_create_group(&pdev->dev.kobj,
&f71805f_group_optin[1])))
goto exit_remove_files;
}
if (data->has_in & (1 << 9)) { /* in9 (F71872F/FG only) */
if ((err = sysfs_create_group(&pdev->dev.kobj,
&f71805f_group_optin[2])))
goto exit_remove_files;
}
if (data->has_in & (1 << 10)) { /* in9 (F71872F/FG only) */
if ((err = sysfs_create_group(&pdev->dev.kobj,
&f71805f_group_optin[3])))
goto exit_remove_files;
}
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
if (data->fan_ctrl[i] & FAN_CTRL_SKIP) if (data->fan_ctrl[i] & FAN_CTRL_SKIP)
continue; continue;
...@@ -1143,6 +1250,8 @@ static int __devinit f71805f_probe(struct platform_device *pdev) ...@@ -1143,6 +1250,8 @@ static int __devinit f71805f_probe(struct platform_device *pdev)
exit_remove_files: exit_remove_files:
sysfs_remove_group(&pdev->dev.kobj, &f71805f_group); sysfs_remove_group(&pdev->dev.kobj, &f71805f_group);
for (i = 0; i < 4; i++)
sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_optin[i]);
for (i = 0; i < 3; i++) for (i = 0; i < 3; i++)
sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_fan[i]); sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_fan[i]);
sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_pwm_freq); sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_pwm_freq);
...@@ -1161,6 +1270,8 @@ static int __devexit f71805f_remove(struct platform_device *pdev) ...@@ -1161,6 +1270,8 @@ static int __devexit f71805f_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
hwmon_device_unregister(data->class_dev); hwmon_device_unregister(data->class_dev);
sysfs_remove_group(&pdev->dev.kobj, &f71805f_group); sysfs_remove_group(&pdev->dev.kobj, &f71805f_group);
for (i = 0; i < 4; i++)
sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_optin[i]);
for (i = 0; i < 3; i++) for (i = 0; i < 3; i++)
sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_fan[i]); sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_fan[i]);
sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_pwm_freq); sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_pwm_freq);
...@@ -1178,7 +1289,8 @@ static struct platform_driver f71805f_driver = { ...@@ -1178,7 +1289,8 @@ static struct platform_driver f71805f_driver = {
.remove = __devexit_p(f71805f_remove), .remove = __devexit_p(f71805f_remove),
}; };
static int __init f71805f_device_add(unsigned short address) static int __init f71805f_device_add(unsigned short address,
const struct f71805f_sio_data *sio_data)
{ {
struct resource res = { struct resource res = {
.start = address, .start = address,
...@@ -1202,26 +1314,45 @@ static int __init f71805f_device_add(unsigned short address) ...@@ -1202,26 +1314,45 @@ static int __init f71805f_device_add(unsigned short address)
goto exit_device_put; goto exit_device_put;
} }
pdev->dev.platform_data = kmalloc(sizeof(struct f71805f_sio_data),
GFP_KERNEL);
if (!pdev->dev.platform_data) {
err = -ENOMEM;
printk(KERN_ERR DRVNAME ": Platform data allocation failed\n");
goto exit_device_put;
}
memcpy(pdev->dev.platform_data, sio_data,
sizeof(struct f71805f_sio_data));
err = platform_device_add(pdev); err = platform_device_add(pdev);
if (err) { if (err) {
printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
err); err);
goto exit_device_put; goto exit_kfree_data;
} }
return 0; return 0;
exit_kfree_data:
kfree(pdev->dev.platform_data);
pdev->dev.platform_data = NULL;
exit_device_put: exit_device_put:
platform_device_put(pdev); platform_device_put(pdev);
exit: exit:
return err; return err;
} }
static int __init f71805f_find(int sioaddr, unsigned short *address) static int __init f71805f_find(int sioaddr, unsigned short *address,
struct f71805f_sio_data *sio_data)
{ {
int err = -ENODEV; int err = -ENODEV;
u16 devid; u16 devid;
static const char *names[] = {
"F71805F/FG",
"F71872F/FG",
};
superio_enter(sioaddr); superio_enter(sioaddr);
devid = superio_inw(sioaddr, SIO_REG_MANID); devid = superio_inw(sioaddr, SIO_REG_MANID);
...@@ -1229,7 +1360,15 @@ static int __init f71805f_find(int sioaddr, unsigned short *address) ...@@ -1229,7 +1360,15 @@ static int __init f71805f_find(int sioaddr, unsigned short *address)
goto exit; goto exit;
devid = superio_inw(sioaddr, SIO_REG_DEVID); devid = superio_inw(sioaddr, SIO_REG_DEVID);
if (devid != SIO_F71805F_ID) { switch (devid) {
case SIO_F71805F_ID:
sio_data->kind = f71805f;
break;
case SIO_F71872F_ID:
sio_data->kind = f71872f;
sio_data->fnsel1 = superio_inb(sioaddr, SIO_REG_FNSEL1);
break;
default:
printk(KERN_INFO DRVNAME ": Unsupported Fintek device, " printk(KERN_INFO DRVNAME ": Unsupported Fintek device, "
"skipping\n"); "skipping\n");
goto exit; goto exit;
...@@ -1250,8 +1389,9 @@ static int __init f71805f_find(int sioaddr, unsigned short *address) ...@@ -1250,8 +1389,9 @@ static int __init f71805f_find(int sioaddr, unsigned short *address)
} }
err = 0; err = 0;
printk(KERN_INFO DRVNAME ": Found F71805F chip at %#x, revision %u\n", printk(KERN_INFO DRVNAME ": Found %s chip at %#x, revision %u\n",
*address, superio_inb(sioaddr, SIO_REG_DEVREV)); names[sio_data->kind], *address,
superio_inb(sioaddr, SIO_REG_DEVREV));
exit: exit:
superio_exit(sioaddr); superio_exit(sioaddr);
...@@ -1262,9 +1402,10 @@ static int __init f71805f_init(void) ...@@ -1262,9 +1402,10 @@ static int __init f71805f_init(void)
{ {
int err; int err;
unsigned short address; unsigned short address;
struct f71805f_sio_data sio_data;
if (f71805f_find(0x2e, &address) if (f71805f_find(0x2e, &address, &sio_data)
&& f71805f_find(0x4e, &address)) && f71805f_find(0x4e, &address, &sio_data))
return -ENODEV; return -ENODEV;
err = platform_driver_register(&f71805f_driver); err = platform_driver_register(&f71805f_driver);
...@@ -1272,7 +1413,7 @@ static int __init f71805f_init(void) ...@@ -1272,7 +1413,7 @@ static int __init f71805f_init(void)
goto exit; goto exit;
/* Sets global pdev as a side effect */ /* Sets global pdev as a side effect */
err = f71805f_device_add(address); err = f71805f_device_add(address, &sio_data);
if (err) if (err)
goto exit_driver; goto exit_driver;
...@@ -1286,13 +1427,16 @@ static int __init f71805f_init(void) ...@@ -1286,13 +1427,16 @@ static int __init f71805f_init(void)
static void __exit f71805f_exit(void) static void __exit f71805f_exit(void)
{ {
kfree(pdev->dev.platform_data);
pdev->dev.platform_data = NULL;
platform_device_unregister(pdev); platform_device_unregister(pdev);
platform_driver_unregister(&f71805f_driver); platform_driver_unregister(&f71805f_driver);
} }
MODULE_AUTHOR("Jean Delvare <khali@linux-fr>"); MODULE_AUTHOR("Jean Delvare <khali@linux-fr>");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("F71805F hardware monitoring driver"); MODULE_DESCRIPTION("F71805F/F71872F hardware monitoring driver");
module_init(f71805f_init); module_init(f71805f_init);
module_exit(f71805f_exit); module_exit(f71805f_exit);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment