aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@woody.linux-foundation.org>2007-02-19 13:36:16 -0800
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-02-19 13:36:16 -0800
commit42eaf0d8f2e7b8201afc00b0ebe1bd89ea51d42d (patch)
treea2c92841628fb7cf63a0df9ec6e7e4c8b8acdd19
parentMerge git://git.infradead.org/mtd-2.6 (diff)
parenthwmon/vt1211: Add probing of alternate config index port (diff)
downloadlinux-dev-42eaf0d8f2e7b8201afc00b0ebe1bd89ea51d42d.tar.xz
linux-dev-42eaf0d8f2e7b8201afc00b0ebe1bd89ea51d42d.zip
Merge branch 'hwmon-for-linus' of git://jdelvare.pck.nerim.net/jdelvare-2.6
* 'hwmon-for-linus' of git://jdelvare.pck.nerim.net/jdelvare-2.6: hwmon/vt1211: Add probing of alternate config index port hwmon/f71805f: Fix a race condition hwmon/abituguru: Fix unchecked return status hwmon: New driver for the Analog Devices ADM1029 hwmon/w83627ehf: Add support for the W83627DHG chip hwmon: Use subsys_initcall hwmon/lm70: Make lm70_remove a __devexit function hwmon: Cleanup a bogus legacy comment hwmon: Simplify the locking model of two drivers hwmon: Drop unused mutexes in two drivers hwmon/it87: Add PWM base frequency control
-rw-r--r--Documentation/hwmon/it8710
-rw-r--r--Documentation/hwmon/sysfs-interface15
-rw-r--r--Documentation/hwmon/w83627ehf54
-rw-r--r--MAINTAINERS6
-rw-r--r--drivers/hwmon/Kconfig11
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/abituguru.c30
-rw-r--r--drivers/hwmon/adm1026.c1
-rw-r--r--drivers/hwmon/adm1029.c508
-rw-r--r--drivers/hwmon/f71805f.c30
-rw-r--r--drivers/hwmon/hwmon.c2
-rw-r--r--drivers/hwmon/it87.c84
-rw-r--r--drivers/hwmon/lm70.c2
-rw-r--r--drivers/hwmon/lm78.c6
-rw-r--r--drivers/hwmon/lm85.c8
-rw-r--r--drivers/hwmon/sis5595.c6
-rw-r--r--drivers/hwmon/via686a.c5
-rw-r--r--drivers/hwmon/vt1211.c58
-rw-r--r--drivers/hwmon/w83627ehf.c54
-rw-r--r--drivers/hwmon/w83627hf.c5
-rw-r--r--drivers/hwmon/w83781d.c10
21 files changed, 769 insertions, 137 deletions
diff --git a/Documentation/hwmon/it87 b/Documentation/hwmon/it87
index 74a80992d237..c0528d6f9ace 100644
--- a/Documentation/hwmon/it87
+++ b/Documentation/hwmon/it87
@@ -135,6 +135,16 @@ Give 0 for unused sensor. Any other value is invalid. To configure this at
startup, consult lm_sensors's /etc/sensors.conf. (2 = thermistor;
3 = thermal diode)
+
+Fan speed control
+-----------------
+
The fan speed control features are limited to manual PWM mode. Automatic
"Smart Guardian" mode control handling is not implemented. However
if you want to go for "manual mode" just write 1 to pwmN_enable.
+
+If you are only able to control the fan speed with very small PWM values,
+try lowering the PWM base frequency (pwm1_freq). Depending on the fan,
+it may give you a somewhat greater control range. The same frequency is
+used to drive all fan outputs, which is why pwm2_freq and pwm3_freq are
+read-only.
diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface
index efef3b962cd3..d73d2e8c7534 100644
--- a/Documentation/hwmon/sysfs-interface
+++ b/Documentation/hwmon/sysfs-interface
@@ -166,16 +166,21 @@ pwm[1-*] Pulse width modulation fan control.
pwm[1-*]_enable
Switch PWM on and off.
- Not always present even if fan*_pwm is.
+ Not always present even if pwmN is.
0: turn off
1: turn on in manual mode
2+: turn on in automatic mode
- Check individual chip documentation files for automatic mode details.
+ Check individual chip documentation files for automatic mode
+ details.
RW
-pwm[1-*]_mode
- 0: DC mode
- 1: PWM mode
+pwm[1-*]_mode 0: DC mode (direct current)
+ 1: PWM mode (pulse-width modulation)
+ RW
+
+pwm[1-*]_freq Base PWM frequency in Hz.
+ Only possibly available when pwmN_mode is PWM, but not always
+ present even then.
RW
pwm[1-*]_auto_channels_temp
diff --git a/Documentation/hwmon/w83627ehf b/Documentation/hwmon/w83627ehf
index 8a15a7408753..030fac6cec7a 100644
--- a/Documentation/hwmon/w83627ehf
+++ b/Documentation/hwmon/w83627ehf
@@ -2,26 +2,29 @@ Kernel driver w83627ehf
=======================
Supported chips:
- * Winbond W83627EHF/EHG (ISA access ONLY)
+ * Winbond W83627EHF/EHG/DHG (ISA access ONLY)
Prefix: 'w83627ehf'
Addresses scanned: ISA address retrieved from Super I/O registers
- Datasheet: http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83627EHF_%20W83627EHGb.pdf
+ Datasheet:
+ http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83627EHF_%20W83627EHGb.pdf
+ DHG datasheet confidential.
Authors:
Jean Delvare <khali@linux-fr.org>
Yuan Mu (Winbond)
Rudolf Marek <r.marek@assembler.cz>
+ David Hubbard <david.c.hubbard@gmail.com>
Description
-----------
-This driver implements support for the Winbond W83627EHF and W83627EHG
-super I/O chips. We will refer to them collectively as Winbond chips.
+This driver implements support for the Winbond W83627EHF, W83627EHG, and
+W83627DHG super I/O chips. We will refer to them collectively as Winbond chips.
The chips implement three temperature sensors, five fan rotation
-speed sensors, ten analog voltage sensors, alarms with beep warnings (control
-unimplemented), and some automatic fan regulation strategies (plus manual
-fan control mode).
+speed sensors, ten analog voltage sensors (only nine for the 627DHG), alarms
+with beep warnings (control unimplemented), and some automatic fan regulation
+strategies (plus manual fan control mode).
Temperatures are measured in degrees Celsius and measurement resolution is 1
degC for temp1 and 0.5 degC for temp2 and temp3. An alarm is triggered when
@@ -55,6 +58,9 @@ prog -> pwm4 (the programmable setting is not supported by the driver)
/sys files
----------
+name - this is a standard hwmon device entry. For the W83627EHF and W83627EHG,
+ it is set to "w83627ehf" and for the W83627DHG it is set to "w83627dhg"
+
pwm[1-4] - this file stores PWM duty cycle or DC value (fan speed) in range:
0 (stop) to 255 (full)
@@ -83,3 +89,37 @@ pwm[1-4]_stop_time - how many milliseconds [ms] must elapse to switch
Note: last two functions are influenced by other control bits, not yet exported
by the driver, so a change might not have any effect.
+
+Implementation Details
+----------------------
+
+Future driver development should bear in mind that the following registers have
+different functions on the 627EHF and the 627DHG. Some registers also have
+different power-on default values, but BIOS should already be loading
+appropriate defaults. Note that bank selection must be performed as is currently
+done in the driver for all register addresses.
+
+0x49: only on DHG, selects temperature source for AUX fan, CPU fan0
+0x4a: not completely documented for the EHF and the DHG documentation assigns
+ different behavior to bits 7 and 6, including extending the temperature
+ input selection to SmartFan I, not just SmartFan III. Testing on the EHF
+ will reveal whether they are compatible or not.
+
+0x58: Chip ID: 0xa1=EHF 0xc1=DHG
+0x5e: only on DHG, has bits to enable "current mode" temperature detection and
+ critical temperature protection
+0x45b: only on EHF, bit 3, vin4 alarm (EHF supports 10 inputs, only 9 on DHG)
+0x552: only on EHF, vin4
+0x558: only on EHF, vin4 high limit
+0x559: only on EHF, vin4 low limit
+0x6b: only on DHG, SYS fan critical temperature
+0x6c: only on DHG, CPU fan0 critical temperature
+0x6d: only on DHG, AUX fan critical temperature
+0x6e: only on DHG, CPU fan1 critical temperature
+
+0x50-0x55 and 0x650-0x657 are marked "Test Register" for the EHF, but "Reserved
+ Register" for the DHG
+
+The DHG also supports PECI, where the DHG queries Intel CPU temperatures, and
+the ICH8 southbridge gets that data via PECI from the DHG, so that the
+southbridge drives the fans. And the DHG supports SST, a one-wire serial bus.
diff --git a/MAINTAINERS b/MAINTAINERS
index 270c6b006b91..6261597a1bce 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -275,6 +275,12 @@ M: khali@linux-fr.org
L: lm-sensors@lm-sensors.org
S: Maintained
+ADM1029 HARDWARE MONITOR DRIVER
+P: Corentin Labbe
+M: corentin.labbe@geomatys.fr
+L: lm-sensors@lm-sensors.org
+S: Maintained
+
ADT746X FAN DRIVER
P: Colin Leroy
M: colin@colino.net
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 891ef6d0b1bf..c3d4856fb618 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -73,6 +73,17 @@ config SENSORS_ADM1026
This driver can also be built as a module. If so, the module
will be called adm1026.
+config SENSORS_ADM1029
+ tristate "Analog Devices ADM1029"
+ depends on HWMON && I2C && EXPERIMENTAL
+ help
+ If you say yes here you get support for Analog Devices ADM1029
+ sensor chip.
+ Very rare chip, please let us know you use it.
+
+ This driver can also be built as a module. If so, the module
+ will be called adm1029.
+
config SENSORS_ADM1031
tristate "Analog Devices ADM1031 and compatibles"
depends on HWMON && I2C && EXPERIMENTAL
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 31661124271e..4165c27a2f25 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o
obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o
obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o
obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o
+obj-$(CONFIG_SENSORS_ADM1029) += adm1029.o
obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o
obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o
obj-$(CONFIG_SENSORS_AMS) += ams/
diff --git a/drivers/hwmon/abituguru.c b/drivers/hwmon/abituguru.c
index b1dc63e4ac7b..bede4d990ea6 100644
--- a/drivers/hwmon/abituguru.c
+++ b/drivers/hwmon/abituguru.c
@@ -1267,30 +1267,42 @@ static int __devinit abituguru_probe(struct platform_device *pdev)
printk(KERN_INFO ABIT_UGURU_NAME ": found Abit uGuru\n");
/* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&pdev->dev);
- if (IS_ERR(data->class_dev)) {
- res = PTR_ERR(data->class_dev);
- goto abituguru_probe_error;
- }
for (i = 0; i < sysfs_attr_i; i++)
- device_create_file(&pdev->dev, &data->sysfs_attr[i].dev_attr);
+ if (device_create_file(&pdev->dev,
+ &data->sysfs_attr[i].dev_attr))
+ goto abituguru_probe_error;
for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++)
- device_create_file(&pdev->dev,
- &abituguru_sysfs_attr[i].dev_attr);
+ if (device_create_file(&pdev->dev,
+ &abituguru_sysfs_attr[i].dev_attr))
+ goto abituguru_probe_error;
- return 0;
+ data->class_dev = hwmon_device_register(&pdev->dev);
+ if (!IS_ERR(data->class_dev))
+ return 0; /* success */
+ res = PTR_ERR(data->class_dev);
abituguru_probe_error:
+ for (i = 0; data->sysfs_attr[i].dev_attr.attr.name; i++)
+ device_remove_file(&pdev->dev, &data->sysfs_attr[i].dev_attr);
+ for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++)
+ device_remove_file(&pdev->dev,
+ &abituguru_sysfs_attr[i].dev_attr);
kfree(data);
return res;
}
static int __devexit abituguru_remove(struct platform_device *pdev)
{
+ int i;
struct abituguru_data *data = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
hwmon_device_unregister(data->class_dev);
+ for (i = 0; data->sysfs_attr[i].dev_attr.attr.name; i++)
+ device_remove_file(&pdev->dev, &data->sysfs_attr[i].dev_attr);
+ for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++)
+ device_remove_file(&pdev->dev,
+ &abituguru_sysfs_attr[i].dev_attr);
kfree(data);
return 0;
diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c
index b4618b2705f7..ba80cd3258c6 100644
--- a/drivers/hwmon/adm1026.c
+++ b/drivers/hwmon/adm1026.c
@@ -261,7 +261,6 @@ struct pwm_data {
struct adm1026_data {
struct i2c_client client;
struct class_device *class_dev;
- struct mutex lock;
enum chips type;
struct mutex update_lock;
diff --git a/drivers/hwmon/adm1029.c b/drivers/hwmon/adm1029.c
new file mode 100644
index 000000000000..73ce31b31511
--- /dev/null
+++ b/drivers/hwmon/adm1029.c
@@ -0,0 +1,508 @@
+/*
+ * adm1029.c - Part of lm_sensors, Linux kernel modules for hardware monitoring
+ *
+ * Copyright (C) 2006 Corentin LABBE <corentin.labbe@geomatys.fr>
+ *
+ * Based on LM83 Driver by Jean Delvare <khali@linux-fr.org>
+ *
+ * Give only processor, motherboard temperatures and fan tachs
+ * Very rare chip please let me know if you use it
+ *
+ * http://www.analog.com/UploadedFiles/Data_Sheets/ADM1029.pdf
+ *
+ *
+ * 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
+ * the Free Software Foundation version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+/*
+ * Addresses to scan
+ */
+
+static unsigned short normal_i2c[] = {
+ 0x28, 0x29, 0x2a,
+ 0x2b, 0x2c, 0x2d,
+ 0x2e, 0x2f, I2C_CLIENT_END
+};
+
+/*
+ * Insmod parameters
+ */
+
+I2C_CLIENT_INSMOD_1(adm1029);
+
+/*
+ * The ADM1029 registers
+ * Manufacturer ID is 0x41 for Analog Devices
+ */
+
+#define ADM1029_REG_MAN_ID 0x0D
+#define ADM1029_REG_CHIP_ID 0x0E
+#define ADM1029_REG_CONFIG 0x01
+#define ADM1029_REG_NB_FAN_SUPPORT 0x02
+
+#define ADM1029_REG_TEMP_DEVICES_INSTALLED 0x06
+
+#define ADM1029_REG_LOCAL_TEMP 0xA0
+#define ADM1029_REG_REMOTE1_TEMP 0xA1
+#define ADM1029_REG_REMOTE2_TEMP 0xA2
+
+#define ADM1029_REG_LOCAL_TEMP_HIGH 0x90
+#define ADM1029_REG_REMOTE1_TEMP_HIGH 0x91
+#define ADM1029_REG_REMOTE2_TEMP_HIGH 0x92
+
+#define ADM1029_REG_LOCAL_TEMP_LOW 0x98
+#define ADM1029_REG_REMOTE1_TEMP_LOW 0x99
+#define ADM1029_REG_REMOTE2_TEMP_LOW 0x9A
+
+#define ADM1029_REG_FAN1 0x70
+#define ADM1029_REG_FAN2 0x71
+
+#define ADM1029_REG_FAN1_MIN 0x78
+#define ADM1029_REG_FAN2_MIN 0x79
+
+#define ADM1029_REG_FAN1_CONFIG 0x68
+#define ADM1029_REG_FAN2_CONFIG 0x69
+
+#define TEMP_FROM_REG(val) ((val) * 1000)
+
+#define DIV_FROM_REG(val) ( 1 << (((val) >> 6) - 1))
+
+/* Registers to be checked by adm1029_update_device() */
+static const u8 ADM1029_REG_TEMP[] = {
+ ADM1029_REG_LOCAL_TEMP,
+ ADM1029_REG_REMOTE1_TEMP,
+ ADM1029_REG_REMOTE2_TEMP,
+ ADM1029_REG_LOCAL_TEMP_HIGH,
+ ADM1029_REG_REMOTE1_TEMP_HIGH,
+ ADM1029_REG_REMOTE2_TEMP_HIGH,
+ ADM1029_REG_LOCAL_TEMP_LOW,
+ ADM1029_REG_REMOTE1_TEMP_LOW,
+ ADM1029_REG_REMOTE2_TEMP_LOW,
+};
+
+static const u8 ADM1029_REG_FAN[] = {
+ ADM1029_REG_FAN1,
+ ADM1029_REG_FAN2,
+ ADM1029_REG_FAN1_MIN,
+ ADM1029_REG_FAN2_MIN,
+};
+
+static const u8 ADM1029_REG_FAN_DIV[] = {
+ ADM1029_REG_FAN1_CONFIG,
+ ADM1029_REG_FAN2_CONFIG,
+};
+
+/*
+ * Functions declaration
+ */
+
+static int adm1029_attach_adapter(struct i2c_adapter *adapter);
+static int adm1029_detect(struct i2c_adapter *adapter, int address, int kind);
+static int adm1029_detach_client(struct i2c_client *client);
+static struct adm1029_data *adm1029_update_device(struct device *dev);
+static int adm1029_init_client(struct i2c_client *client);
+
+/*
+ * Driver data (common to all clients)
+ */
+
+static struct i2c_driver adm1029_driver = {
+ .driver = {
+ .name = "adm1029",
+ },
+ .attach_adapter = adm1029_attach_adapter,
+ .detach_client = adm1029_detach_client,
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct adm1029_data {
+ struct i2c_client client;
+ struct class_device *class_dev;
+ struct mutex update_lock;
+ char valid; /* zero until following fields are valid */
+ unsigned long last_updated; /* in jiffies */
+
+ /* registers values, signed for temperature, unsigned for other stuff */
+ s8 temp[ARRAY_SIZE(ADM1029_REG_TEMP)];
+ u8 fan[ARRAY_SIZE(ADM1029_REG_FAN)];
+ u8 fan_div[ARRAY_SIZE(ADM1029_REG_FAN_DIV)];
+};
+
+/*
+ * Sysfs stuff
+ */
+
+static ssize_t
+show_temp(struct device *dev, struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct adm1029_data *data = adm1029_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index]));
+}
+
+static ssize_t
+show_fan(struct device *dev, struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct adm1029_data *data = adm1029_update_device(dev);
+ u16 val;
+ if (data->fan[attr->index] == 0 || data->fan_div[attr->index] == 0
+ || data->fan[attr->index] == 255) {
+ return sprintf(buf, "0\n");
+ }
+
+ val = 1880 * 120 / DIV_FROM_REG(data->fan_div[attr->index])
+ / data->fan[attr->index];
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t
+show_fan_div(struct device *dev, struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct adm1029_data *data = adm1029_update_device(dev);
+ if (data->fan_div[attr->index] == 0)
+ return sprintf(buf, "0\n");
+ return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[attr->index]));
+}
+
+static ssize_t set_fan_div(struct device *dev,
+ struct device_attribute *devattr, const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct adm1029_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ long val = simple_strtol(buf, NULL, 10);
+ u8 reg;
+
+ mutex_lock(&data->update_lock);
+
+ /*Read actual config */
+ reg = i2c_smbus_read_byte_data(client,
+ ADM1029_REG_FAN_DIV[attr->index]);
+
+ switch (val) {
+ case 1:
+ val = 1;
+ break;
+ case 2:
+ val = 2;
+ break;
+ case 4:
+ val = 3;
+ break;
+ default:
+ mutex_unlock(&data->update_lock);
+ dev_err(&client->dev, "fan_div value %ld not "
+ "supported. Choose one of 1, 2 or 4!\n", val);
+ return -EINVAL;
+ }
+ /* Update the value */
+ reg = (reg & 0x3F) | (val << 6);
+
+ /* Write value */
+ i2c_smbus_write_byte_data(client,
+ ADM1029_REG_FAN_DIV[attr->index], reg);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/*
+Access rights on sysfs, S_IRUGO stand for Is Readable by User, Group and Others
+ S_IWUSR stand for Is Writable by User
+*/
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2);
+
+static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO, show_temp, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO, show_temp, NULL, 5);
+
+static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO, show_temp, NULL, 6);
+static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO, show_temp, NULL, 7);
+static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO, show_temp, NULL, 8);
+
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);
+
+static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO, show_fan, NULL, 2);
+static SENSOR_DEVICE_ATTR(fan2_min, S_IRUGO, show_fan, NULL, 3);
+
+static SENSOR_DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR,
+ show_fan_div, set_fan_div, 0);
+static SENSOR_DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR,
+ show_fan_div, set_fan_div, 1);
+
+static struct attribute *adm1029_attributes[] = {
+ &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_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &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_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
+ &sensor_dev_attr_fan2_div.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group adm1029_group = {
+ .attrs = adm1029_attributes,
+};
+
+/*
+ * Real code
+ */
+
+static int adm1029_attach_adapter(struct i2c_adapter *adapter)
+{
+ if (!(adapter->class & I2C_CLASS_HWMON))
+ return 0;
+ return i2c_probe(adapter, &addr_data, adm1029_detect);
+}
+
+/*
+ * The following function does more than just detection. If detection
+ * succeeds, it also registers the new chip.
+ */
+
+static int adm1029_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+ struct i2c_client *client;
+ struct adm1029_data *data;
+ int err = 0;
+ const char *name = "";
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ goto exit;
+
+ if (!(data = kzalloc(sizeof(struct adm1029_data), GFP_KERNEL))) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ client = &data->client;
+ i2c_set_clientdata(client, data);
+ client->addr = address;
+ client->adapter = adapter;
+ client->driver = &adm1029_driver;
+
+ /* Now we do the detection and identification. A negative kind
+ * means that the driver was loaded with no force parameter
+ * (default), so we must both detect and identify the chip
+ * (actually there is only one possible kind of chip for now, adm1029).
+ * A zero kind means that the driver was loaded with the force
+ * parameter, the detection step shall be skipped. A positive kind
+ * means that the driver was loaded with the force parameter and a
+ * given kind of chip is requested, so both the detection and the
+ * identification steps are skipped. */
+
+ /* Default to an adm1029 if forced */
+ if (kind == 0)
+ kind = adm1029;
+
+ /* ADM1029 doesn't have CHIP ID, check just MAN ID
+ * For better detection we check also ADM1029_TEMP_DEVICES_INSTALLED,
+ * ADM1029_REG_NB_FAN_SUPPORT and compare it with possible values
+ * documented
+ */
+
+ if (kind <= 0) { /* identification */
+ u8 man_id, chip_id, temp_devices_installed, nb_fan_support;
+
+ man_id = i2c_smbus_read_byte_data(client, ADM1029_REG_MAN_ID);
+ chip_id = i2c_smbus_read_byte_data(client, ADM1029_REG_CHIP_ID);
+ temp_devices_installed = i2c_smbus_read_byte_data(client,
+ ADM1029_REG_TEMP_DEVICES_INSTALLED);
+ nb_fan_support = i2c_smbus_read_byte_data(client,
+ ADM1029_REG_NB_FAN_SUPPORT);
+ /* 0x41 is Analog Devices */
+ if (man_id == 0x41 && (temp_devices_installed & 0xf9) == 0x01
+ && nb_fan_support == 0x03) {
+ if ((chip_id & 0xF0) == 0x00) {
+ kind = adm1029;
+ } else {
+ /* There are no "official" CHIP ID, so actually
+ * we use Major/Minor revision for that */
+ printk(KERN_INFO
+ "adm1029: Unknown major revision %x, "
+ "please let us know\n", chip_id);
+ }
+ }
+
+ if (kind <= 0) { /* identification failed */
+ pr_debug("adm1029: Unsupported chip (man_id=0x%02X, "
+ "chip_id=0x%02X)\n", man_id, chip_id);
+ goto exit_free;
+ }
+ }
+
+ if (kind == adm1029) {
+ name = "adm1029";
+ }
+
+ /* We can fill in the remaining client fields */
+ strlcpy(client->name, name, I2C_NAME_SIZE);
+ mutex_init(&data->update_lock);
+
+ /* Tell the I2C layer a new client has arrived */
+ if ((err = i2c_attach_client(client)))
+ goto exit_free;
+
+ /*
+ * Initialize the ADM1029 chip
+ * Check config register
+ */
+ if (adm1029_init_client(client) == 0)
+ goto exit_detach;
+
+ /* Register sysfs hooks */
+ if ((err = sysfs_create_group(&client->dev.kobj, &adm1029_group)))
+ goto exit_detach;
+
+ data->class_dev = hwmon_device_register(&client->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto exit_remove_files;
+ }
+
+ return 0;
+
+ exit_remove_files:
+ sysfs_remove_group(&client->dev.kobj, &adm1029_group);
+ exit_detach:
+ i2c_detach_client(client);
+ exit_free:
+ kfree(data);
+ exit:
+ return err;
+}
+
+static int adm1029_init_client(struct i2c_client *client)
+{
+ u8 config;
+ config = i2c_smbus_read_byte_data(client, ADM1029_REG_CONFIG);
+ if ((config & 0x10) == 0) {
+ i2c_smbus_write_byte_data(client, ADM1029_REG_CONFIG,
+ config | 0x10);
+ }
+ /* recheck config */
+ config = i2c_smbus_read_byte_data(client, ADM1029_REG_CONFIG);
+ if ((config & 0x10) == 0) {
+ dev_err(&client->dev, "Initialization failed!\n");
+ return 0;
+ }
+ return 1;
+}
+
+static int adm1029_detach_client(struct i2c_client *client)
+{
+ struct adm1029_data *data = i2c_get_clientdata(client);
+ int err;
+
+ hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &adm1029_group);
+
+ if ((err = i2c_detach_client(client)))
+ return err;
+
+ kfree(data);
+ return 0;
+}
+
+/*
+function that update the status of the chips (temperature for exemple)
+*/
+static struct adm1029_data *adm1029_update_device(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct adm1029_data *data = i2c_get_clientdata(client);
+
+ mutex_lock(&data->update_lock);
+ /*
+ * Use the "cache" Luke, don't recheck values
+ * if there are already checked not a long time later
+ */
+ if (time_after(jiffies, data->last_updated + HZ * 2)
+ || !data->valid) {
+ int nr;
+
+ dev_dbg(&client->dev, "Updating adm1029 data\n");
+
+ for (nr = 0; nr < ARRAY_SIZE(ADM1029_REG_TEMP); nr++) {
+ data->temp[nr] =
+ i2c_smbus_read_byte_data(client,
+ ADM1029_REG_TEMP[nr]);
+ }
+ for (nr = 0; nr < ARRAY_SIZE(ADM1029_REG_FAN); nr++) {
+ data->fan[nr] =
+ i2c_smbus_read_byte_data(client,
+ ADM1029_REG_FAN[nr]);
+ }
+ for (nr = 0; nr < ARRAY_SIZE(ADM1029_REG_FAN_DIV); nr++) {
+ data->fan_div[nr] =
+ i2c_smbus_read_byte_data(client,
+ ADM1029_REG_FAN_DIV[nr]);
+ }
+
+ data->last_updated = jiffies;
+ data->valid = 1;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+/*
+ Common module stuff
+*/
+static int __init sensors_adm1029_init(void)
+{
+
+ return i2c_add_driver(&adm1029_driver);
+}
+
+static void __exit sensors_adm1029_exit(void)
+{
+
+ i2c_del_driver(&adm1029_driver);
+}
+
+MODULE_AUTHOR("Corentin LABBE <corentin.labbe@geomatys.fr>");
+MODULE_DESCRIPTION("adm1029 driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(sensors_adm1029_init);
+module_exit(sensors_adm1029_exit);
diff --git a/drivers/hwmon/f71805f.c b/drivers/hwmon/f71805f.c
index a272cae8f60e..7c2973487122 100644
--- a/drivers/hwmon/f71805f.c
+++ b/drivers/hwmon/f71805f.c
@@ -146,7 +146,6 @@ superio_exit(int base)
struct f71805f_data {
unsigned short addr;
const char *name;
- struct mutex lock;
struct class_device *class_dev;
struct mutex update_lock;
@@ -271,50 +270,42 @@ static inline u8 temp_to_reg(long val)
* Device I/O access
*/
+/* Must be called with data->update_lock held, except during initialization */
static u8 f71805f_read8(struct f71805f_data *data, u8 reg)
{
- u8 val;
-
- mutex_lock(&data->lock);
outb(reg, data->addr + ADDR_REG_OFFSET);
- val = inb(data->addr + DATA_REG_OFFSET);
- mutex_unlock(&data->lock);
-
- return val;
+ return inb(data->addr + DATA_REG_OFFSET);
}
+/* Must be called with data->update_lock held, except during initialization */
static void f71805f_write8(struct f71805f_data *data, u8 reg, u8 val)
{
- mutex_lock(&data->lock);
outb(reg, data->addr + ADDR_REG_OFFSET);
outb(val, data->addr + DATA_REG_OFFSET);
- mutex_unlock(&data->lock);
}
/* It is important to read the MSB first, because doing so latches the
- value of the LSB, so we are sure both bytes belong to the same value. */
+ value of the LSB, so we are sure both bytes belong to the same value.
+ Must be called with data->update_lock held, except during initialization */
static u16 f71805f_read16(struct f71805f_data *data, u8 reg)
{
u16 val;
- mutex_lock(&data->lock);
outb(reg, data->addr + ADDR_REG_OFFSET);
val = inb(data->addr + DATA_REG_OFFSET) << 8;
outb(++reg, data->addr + ADDR_REG_OFFSET);
val |= inb(data->addr + DATA_REG_OFFSET);
- mutex_unlock(&data->lock);
return val;
}
+/* Must be called with data->update_lock held, except during initialization */
static void f71805f_write16(struct f71805f_data *data, u8 reg, u16 val)
{
- mutex_lock(&data->lock);
outb(reg, data->addr + ADDR_REG_OFFSET);
outb(val >> 8, data->addr + DATA_REG_OFFSET);
outb(++reg, data->addr + ADDR_REG_OFFSET);
outb(val & 0xff, data->addr + DATA_REG_OFFSET);
- mutex_unlock(&data->lock);
}
static struct f71805f_data *f71805f_update_device(struct device *dev)
@@ -1150,7 +1141,6 @@ static int __devinit f71805f_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
data->addr = res->start;
- mutex_init(&data->lock);
data->name = names[sio_data->kind];
mutex_init(&data->update_lock);
@@ -1300,14 +1290,11 @@ static int __init f71805f_device_add(unsigned short address,
if (err) {
printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
err);
- goto exit_kfree_data;
+ goto exit_device_put;
}
return 0;
-exit_kfree_data:
- kfree(pdev->dev.platform_data);
- pdev->dev.platform_data = NULL;
exit_device_put:
platform_device_put(pdev);
exit:
@@ -1400,10 +1387,7 @@ exit:
static void __exit f71805f_exit(void)
{
- kfree(pdev->dev.platform_data);
- pdev->dev.platform_data = NULL;
platform_device_unregister(pdev);
-
platform_driver_unregister(&f71805f_driver);
}
diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c
index 106fa01cdb60..affcc00764d3 100644
--- a/drivers/hwmon/hwmon.c
+++ b/drivers/hwmon/hwmon.c
@@ -101,7 +101,7 @@ static void __exit hwmon_exit(void)
class_destroy(hwmon_class);
}
-module_init(hwmon_init);
+subsys_initcall(hwmon_init);
module_exit(hwmon_exit);
EXPORT_SYMBOL_GPL(hwmon_device_register);
diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c
index 1ed8b7e2c35d..62afc63708a5 100644
--- a/drivers/hwmon/it87.c
+++ b/drivers/hwmon/it87.c
@@ -202,15 +202,23 @@ static int DIV_TO_REG(int val)
}
#define DIV_FROM_REG(val) (1 << (val))
+static const unsigned int pwm_freq[8] = {
+ 48000000 / 128,
+ 24000000 / 128,
+ 12000000 / 128,
+ 8000000 / 128,
+ 6000000 / 128,
+ 3000000 / 128,
+ 1500000 / 128,
+ 750000 / 128,
+};
+
-/* For each registered IT87, we need to keep some data in memory. That
- data is pointed to by it87_list[NR]->data. The structure itself is
- dynamically allocated, at the same time when a new it87 client is
- allocated. */
+/* For each registered chip, we need to keep some data in memory.
+ The structure is dynamically allocated. */
struct it87_data {
struct i2c_client client;
struct class_device *class_dev;
- struct mutex lock;
enum chips type;
struct mutex update_lock;
@@ -232,6 +240,7 @@ struct it87_data {
u8 vrm;
u32 alarms; /* Register encoding, combined */
u8 fan_main_ctrl; /* Register value */
+ u8 fan_ctl; /* Register value */
u8 manual_pwm_ctl[3]; /* manual PWM value set by user */
};
@@ -519,6 +528,14 @@ static ssize_t show_pwm(struct device *dev, struct device_attribute *attr,
struct it87_data *data = it87_update_device(dev);
return sprintf(buf,"%d\n", data->manual_pwm_ctl[nr]);
}
+static ssize_t show_pwm_freq(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct it87_data *data = it87_update_device(dev);
+ int index = (data->fan_ctl >> 4) & 0x07;
+
+ return sprintf(buf, "%u\n", pwm_freq[index]);
+}
static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
@@ -528,9 +545,10 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr,
struct i2c_client *client = to_i2c_client(dev);
struct it87_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10);
- u8 reg = it87_read_value(client, IT87_REG_FAN_DIV);
+ u8 reg;
mutex_lock(&data->update_lock);
+ reg = it87_read_value(client, IT87_REG_FAN_DIV);
switch (nr) {
case 0: data->fan_div[nr] = reg & 0x07; break;
case 1: data->fan_div[nr] = (reg >> 3) & 0x07; break;
@@ -639,6 +657,28 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
mutex_unlock(&data->update_lock);
return count;
}
+static ssize_t set_pwm_freq(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct it87_data *data = i2c_get_clientdata(client);
+ unsigned long val = simple_strtoul(buf, NULL, 10);
+ int i;
+
+ /* Search for the nearest available frequency */
+ for (i = 0; i < 7; i++) {
+ if (val > (pwm_freq[i] + pwm_freq[i+1]) / 2)
+ break;
+ }
+
+ mutex_lock(&data->update_lock);
+ data->fan_ctl = it87_read_value(client, IT87_REG_FAN_CTL) & 0x8f;
+ data->fan_ctl |= i << 4;
+ it87_write_value(client, IT87_REG_FAN_CTL, data->fan_ctl);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
#define show_fan_offset(offset) \
static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, \
@@ -656,7 +696,10 @@ show_fan_offset(3);
static SENSOR_DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \
show_pwm_enable, set_pwm_enable, offset - 1); \
static SENSOR_DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \
- show_pwm, set_pwm, offset - 1);
+ show_pwm, set_pwm, offset - 1); \
+static DEVICE_ATTR(pwm##offset##_freq, \
+ (offset == 1 ? S_IRUGO | S_IWUSR : S_IRUGO), \
+ show_pwm_freq, (offset == 1 ? set_pwm_freq : NULL));
show_pwm_offset(1);
show_pwm_offset(2);
@@ -904,7 +947,6 @@ static int it87_detect(struct i2c_adapter *adapter)
}
new_client = &data->client;
- mutex_init(&data->lock);
i2c_set_clientdata(new_client, data);
new_client->addr = isa_address;
new_client->adapter = adapter;
@@ -1021,7 +1063,13 @@ static int it87_detect(struct i2c_adapter *adapter)
|| (err = device_create_file(&new_client->dev,
&sensor_dev_attr_pwm2.dev_attr))
|| (err = device_create_file(&new_client->dev,
- &sensor_dev_attr_pwm3.dev_attr)))
+ &sensor_dev_attr_pwm3.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_pwm1_freq))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_pwm2_freq))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_pwm3_freq)))
goto ERROR4;
}
@@ -1076,33 +1124,22 @@ static int it87_detach_client(struct i2c_client *client)
return 0;
}
-/* ISA access must be locked explicitly!
+/* Must be called with data->update_lock held, except during initialization.
We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks,
would slow down the IT87 access and should not be necessary. */
static int it87_read_value(struct i2c_client *client, u8 reg)
{
- struct it87_data *data = i2c_get_clientdata(client);
- int res;
-
- mutex_lock(&data->lock);
outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET);
- res = inb_p(client->addr + IT87_DATA_REG_OFFSET);
- mutex_unlock(&data->lock);
-
- return res;
+ return inb_p(client->addr + IT87_DATA_REG_OFFSET);
}
-/* ISA access must be locked explicitly!
+/* Must be called with data->update_lock held, except during initialization.
We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks,
would slow down the IT87 access and should not be necessary. */
static void it87_write_value(struct i2c_client *client, u8 reg, u8 value)
{
- struct it87_data *data = i2c_get_clientdata(client);
-
- mutex_lock(&data->lock);
outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET);
outb_p(value, client->addr + IT87_DATA_REG_OFFSET);
- mutex_unlock(&data->lock);
}
/* Return 1 if and only if the PWM interface is safe to use */
@@ -1316,6 +1353,7 @@ static struct it87_data *it87_update_device(struct device *dev)
(it87_read_value(client, IT87_REG_ALARM2) << 8) |
(it87_read_value(client, IT87_REG_ALARM3) << 16);
data->fan_main_ctrl = it87_read_value(client, IT87_REG_FAN_MAIN_CTRL);
+ data->fan_ctl = it87_read_value(client, IT87_REG_FAN_CTL);
data->sensor = it87_read_value(client, IT87_REG_TEMP_ENABLE);
/* The 8705 does not have VID capability */
diff --git a/drivers/hwmon/lm70.c b/drivers/hwmon/lm70.c
index 6ba84731b9cd..7eaae3834e15 100644
--- a/drivers/hwmon/lm70.c
+++ b/drivers/hwmon/lm70.c
@@ -126,7 +126,7 @@ out_dev_reg_failed:
return status;
}
-static int __exit lm70_remove(struct spi_device *spi)
+static int __devexit lm70_remove(struct spi_device *spi)
{
struct lm70 *p_lm70 = dev_get_drvdata(&spi->dev);
diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c
index 73bc2ffc598d..886786c33916 100644
--- a/drivers/hwmon/lm78.c
+++ b/drivers/hwmon/lm78.c
@@ -125,10 +125,8 @@ static inline int TEMP_FROM_REG(s8 val)
bad. Quite a lot of bookkeeping is done. A real driver can often cut
some corners. */
-/* For each registered LM78, we need to keep some data in memory. That
- data is pointed to by lm78_list[NR]->data. The structure itself is
- dynamically allocated, at the same time when a new lm78 client is
- allocated. */
+/* For each registered chip, we need to keep some data in memory.
+ The structure is dynamically allocated. */
struct lm78_data {
struct i2c_client client;
struct class_device *class_dev;
diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c
index 2c3293cf69d1..20a8c648280d 100644
--- a/drivers/hwmon/lm85.c
+++ b/drivers/hwmon/lm85.c
@@ -298,11 +298,6 @@ static int ZONE_TO_REG( int zone )
#define LM85_DATA_INTERVAL (HZ + HZ / 2)
#define LM85_CONFIG_INTERVAL (1 * 60 * HZ)
-/* For each registered LM85, we need to keep some data in memory. That
- data is pointed to by lm85_list[NR]->data. The structure itself is
- dynamically allocated, at the same time when a new lm85 client is
- allocated. */
-
/* LM85 can automatically adjust fan speeds based on temperature
* This structure encapsulates an entire Zone config. There are
* three zones (one for each temperature input) on the lm85
@@ -329,10 +324,11 @@ struct lm85_autofan {
u8 min_off; /* Min PWM or OFF below "limit", flag */
};
+/* For each registered chip, we need to keep some data in memory.
+ The structure is dynamically allocated. */
struct lm85_data {
struct i2c_client client;
struct class_device *class_dev;
- struct mutex lock;
enum chips type;
struct mutex update_lock;
diff --git a/drivers/hwmon/sis5595.c b/drivers/hwmon/sis5595.c
index 95a4b5d9eaf2..3f400263fc0f 100644
--- a/drivers/hwmon/sis5595.c
+++ b/drivers/hwmon/sis5595.c
@@ -162,10 +162,8 @@ static inline u8 DIV_TO_REG(int val)
}
#define DIV_FROM_REG(val) (1 << (val))
-/* For the SIS5595, we need to keep some data in memory. That
- data is pointed to by sis5595_list[NR]->data. The structure itself is
- dynamically allocated, at the time when the new sis5595 client is
- allocated. */
+/* For each registered chip, we need to keep some data in memory.
+ The structure is dynamically allocated. */
struct sis5595_data {
struct i2c_client client;
struct class_device *class_dev;
diff --git a/drivers/hwmon/via686a.c b/drivers/hwmon/via686a.c
index f8acada0537a..9a440c8cc520 100644
--- a/drivers/hwmon/via686a.c
+++ b/drivers/hwmon/via686a.c
@@ -292,9 +292,8 @@ static inline long TEMP_FROM_REG10(u16 val)
#define DIV_FROM_REG(val) (1 << (val))
#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
-/* For the VIA686A, we need to keep some data in memory.
- The structure is dynamically allocated, at the same time when a new
- via686a client is allocated. */
+/* For each registered chip, we need to keep some data in memory.
+ The structure is dynamically allocated. */
struct via686a_data {
struct i2c_client client;
struct class_device *class_dev;
diff --git a/drivers/hwmon/vt1211.c b/drivers/hwmon/vt1211.c
index 25cc56003d7a..89c23d6add7b 100644
--- a/drivers/hwmon/vt1211.c
+++ b/drivers/hwmon/vt1211.c
@@ -178,9 +178,10 @@ struct vt1211_data {
* Super-I/O constants and functions
* --------------------------------------------------------------------- */
-/* Configuration & data index port registers */
-#define SIO_REG_CIP 0x2e
-#define SIO_REG_DIP 0x2f
+/* Configuration index port registers
+ * The vt1211 can live at 2 different addresses so we need to probe both */
+#define SIO_REG_CIP1 0x2e
+#define SIO_REG_CIP2 0x4e
/* Configuration registers */
#define SIO_VT1211_LDN 0x07 /* logical device number */
@@ -193,33 +194,33 @@ struct vt1211_data {
/* VT1211 logical device numbers */
#define SIO_VT1211_LDN_HWMON 0x0b /* HW monitor */
-static inline void superio_outb(int reg, int val)
+static inline void superio_outb(int sio_cip, int reg, int val)
{
- outb(reg, SIO_REG_CIP);
- outb(val, SIO_REG_DIP);
+ outb(reg, sio_cip);
+ outb(val, sio_cip + 1);
}
-static inline int superio_inb(int reg)
+static inline int superio_inb(int sio_cip, int reg)
{
- outb(reg, SIO_REG_CIP);
- return inb(SIO_REG_DIP);
+ outb(reg, sio_cip);
+ return inb(sio_cip + 1);
}
-static inline void superio_select(int ldn)
+static inline void superio_select(int sio_cip, int ldn)
{
- outb(SIO_VT1211_LDN, SIO_REG_CIP);
- outb(ldn, SIO_REG_DIP);
+ outb(SIO_VT1211_LDN, sio_cip);
+ outb(ldn, sio_cip + 1);
}
-static inline void superio_enter(void)
+static inline void superio_enter(int sio_cip)
{
- outb(0x87, SIO_REG_CIP);
- outb(0x87, SIO_REG_CIP);
+ outb(0x87, sio_cip);
+ outb(0x87, sio_cip);
}
-static inline void superio_exit(void)
+static inline void superio_exit(int sio_cip)
{
- outb(0xaa, SIO_REG_CIP);
+ outb(0xaa, sio_cip);
}
/* ---------------------------------------------------------------------
@@ -1263,26 +1264,26 @@ EXIT:
return err;
}
-static int __init vt1211_find(unsigned short *address)
+static int __init vt1211_find(int sio_cip, unsigned short *address)
{
int err = -ENODEV;
- superio_enter();
+ superio_enter(sio_cip);
- if (superio_inb(SIO_VT1211_DEVID) != SIO_VT1211_ID) {
+ if (superio_inb(sio_cip, SIO_VT1211_DEVID) != SIO_VT1211_ID) {
goto EXIT;
}
- superio_select(SIO_VT1211_LDN_HWMON);
+ superio_select(sio_cip, SIO_VT1211_LDN_HWMON);
- if ((superio_inb(SIO_VT1211_ACTIVE) & 1) == 0) {
+ if ((superio_inb(sio_cip, SIO_VT1211_ACTIVE) & 1) == 0) {
printk(KERN_WARNING DRVNAME ": HW monitor is disabled, "
"skipping\n");
goto EXIT;
}
- *address = ((superio_inb(SIO_VT1211_BADDR) << 8) |
- (superio_inb(SIO_VT1211_BADDR + 1))) & 0xff00;
+ *address = ((superio_inb(sio_cip, SIO_VT1211_BADDR) << 8) |
+ (superio_inb(sio_cip, SIO_VT1211_BADDR + 1))) & 0xff00;
if (*address == 0) {
printk(KERN_WARNING DRVNAME ": Base address is not set, "
"skipping\n");
@@ -1291,10 +1292,11 @@ static int __init vt1211_find(unsigned short *address)
err = 0;
printk(KERN_INFO DRVNAME ": Found VT1211 chip at 0x%04x, "
- "revision %u\n", *address, superio_inb(SIO_VT1211_DEVREV));
+ "revision %u\n", *address,
+ superio_inb(sio_cip, SIO_VT1211_DEVREV));
EXIT:
- superio_exit();
+ superio_exit(sio_cip);
return err;
}
@@ -1303,8 +1305,8 @@ static int __init vt1211_init(void)
int err;
unsigned short address = 0;
- err = vt1211_find(&address);
- if (err) {
+ if ((err = vt1211_find(SIO_REG_CIP1, &address)) &&
+ (err = vt1211_find(SIO_REG_CIP2, &address))) {
goto EXIT;
}
diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c
index 212a1558c63b..da5828f2dfc2 100644
--- a/drivers/hwmon/w83627ehf.c
+++ b/drivers/hwmon/w83627ehf.c
@@ -32,8 +32,10 @@
Supports the following chips:
- Chip #vin #fan #pwm #temp chip_id man_id
- w83627ehf 10 5 4 3 0x88,0xa1 0x5ca3
+ Chip #vin #fan #pwm #temp chip IDs man ID
+ w83627ehf 10 5 4 3 0x8850 0x88 0x5ca3
+ 0x8860 0xa1
+ w83627dhg 9 5 4 3 0xa020 0xc1 0x5ca3
*/
#include <linux/module.h>
@@ -55,8 +57,18 @@ static unsigned short address;
* Super-I/O constants and functions
*/
+/*
+ * The three following globals are initialized in w83627ehf_find(), before
+ * the i2c-isa device is created. Otherwise, they could be stored in
+ * w83627ehf_data. This is ugly, but necessary, and when the driver is next
+ * updated to become a platform driver, the globals will disappear.
+ */
static int REG; /* The register to read/write */
static int VAL; /* The value to read/write */
+/* The w83627ehf/ehg have 10 voltage inputs, but the w83627dhg has 9. This
+ * value is also used in w83627ehf_detect() to export a device name in sysfs
+ * (e.g. w83627ehf or w83627dhg) */
+static int w83627ehf_num_in;
#define W83627EHF_LD_HWM 0x0b
@@ -65,8 +77,10 @@ static int VAL; /* The value to read/write */
#define SIO_REG_ENABLE 0x30 /* Logical device enable */
#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
-#define SIO_W83627EHF_ID 0x8840
-#define SIO_ID_MASK 0xFFC0
+#define SIO_W83627EHF_ID 0x8850
+#define SIO_W83627EHG_ID 0x8860
+#define SIO_W83627DHG_ID 0xa020
+#define SIO_ID_MASK 0xFFF0
static inline void
superio_outb(int reg, int val)
@@ -115,8 +129,12 @@ superio_exit(void)
#define W83627EHF_REG_BANK 0x4E
#define W83627EHF_REG_CONFIG 0x40
-#define W83627EHF_REG_CHIP_ID 0x49
-#define W83627EHF_REG_MAN_ID 0x4F
+
+/* Not currently used:
+ * REG_MAN_ID has the value 0x5ca3 for all supported chips.
+ * REG_CHIP_ID == 0x88/0xa1/0xc1 depending on chip model.
+ * REG_MAN_ID is at port 0x4f
+ * REG_CHIP_ID is at port 0x58 */
static const u16 W83627EHF_REG_FAN[] = { 0x28, 0x29, 0x2a, 0x3f, 0x553 };
static const u16 W83627EHF_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d, 0x3e, 0x55c };
@@ -429,7 +447,7 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
}
/* Measured voltages and limits */
- for (i = 0; i < 10; i++) {
+ for (i = 0; i < w83627ehf_num_in; i++) {
data->in[i] = w83627ehf_read_value(client,
W83627EHF_REG_IN(i));
data->in_min[i] = w83627ehf_read_value(client,
@@ -1121,7 +1139,7 @@ static void w83627ehf_device_remove_files(struct device *dev)
device_remove_file(dev, &sda_sf3_arrays[i].dev_attr);
for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++)
device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr);
- for (i = 0; i < 10; i++) {
+ for (i = 0; i < w83627ehf_num_in; i++) {
device_remove_file(dev, &sda_in_input[i].dev_attr);
device_remove_file(dev, &sda_in_alarm[i].dev_attr);
device_remove_file(dev, &sda_in_min[i].dev_attr);
@@ -1196,7 +1214,11 @@ static int w83627ehf_detect(struct i2c_adapter *adapter)
client->flags = 0;
dev = &client->dev;
- strlcpy(client->name, "w83627ehf", I2C_NAME_SIZE);
+ if (w83627ehf_num_in == 9)
+ strlcpy(client->name, "w83627dhg", I2C_NAME_SIZE);
+ else /* just say ehf. 627EHG is 627EHF in lead-free packaging. */
+ strlcpy(client->name, "w83627ehf", I2C_NAME_SIZE);
+
data->valid = 0;
mutex_init(&data->update_lock);
@@ -1246,7 +1268,7 @@ static int w83627ehf_detect(struct i2c_adapter *adapter)
goto exit_remove;
}
- for (i = 0; i < 10; i++)
+ for (i = 0; i < w83627ehf_num_in; i++)
if ((err = device_create_file(dev, &sda_in_input[i].dev_attr))
|| (err = device_create_file(dev,
&sda_in_alarm[i].dev_attr))
@@ -1340,7 +1362,17 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr)
val = (superio_inb(SIO_REG_DEVID) << 8)
| superio_inb(SIO_REG_DEVID + 1);
- if ((val & SIO_ID_MASK) != SIO_W83627EHF_ID) {
+ switch (val & SIO_ID_MASK) {
+ case SIO_W83627DHG_ID:
+ w83627ehf_num_in = 9;
+ break;
+ case SIO_W83627EHF_ID:
+ case SIO_W83627EHG_ID:
+ w83627ehf_num_in = 10;
+ break;
+ default:
+ printk(KERN_WARNING "w83627ehf: unsupported chip ID: 0x%04x\n",
+ val);
superio_exit();
return -ENODEV;
}
diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c
index dfdc29c77123..d7e240635b3b 100644
--- a/drivers/hwmon/w83627hf.c
+++ b/drivers/hwmon/w83627hf.c
@@ -286,9 +286,8 @@ static inline u8 DIV_TO_REG(long val)
return ((u8) i);
}
-/* For each registered chip, we need to keep some data in memory. That
- data is pointed to by w83627hf_list[NR]->data. The structure itself is
- dynamically allocated, at the same time when a new client is allocated. */
+/* For each registered chip, we need to keep some data in memory.
+ The structure is dynamically allocated. */
struct w83627hf_data {
struct i2c_client client;
struct class_device *class_dev;
diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c
index 1232171c3aad..a47da3ec5472 100644
--- a/drivers/hwmon/w83781d.c
+++ b/drivers/hwmon/w83781d.c
@@ -221,14 +221,8 @@ DIV_TO_REG(long val, enum chips type)
a bit - except if there could be more than one SMBus. Groan. No solution
for this yet. */
-/* This module may seem overly long and complicated. In fact, it is not so
- bad. Quite a lot of bookkeeping is done. A real driver can often cut
- some corners. */
-
-/* For each registered W83781D, we need to keep some data in memory. That
- data is pointed to by w83781d_list[NR]->data. The structure itself is
- dynamically allocated, at the same time when a new w83781d client is
- allocated. */
+/* For each registered chip, we need to keep some data in memory.
+ The structure is dynamically allocated. */
struct w83781d_data {
struct i2c_client client;
struct class_device *class_dev;