Merge branch 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelv...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 16 Jun 2009 18:28:50 +0000 (11:28 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 16 Jun 2009 18:28:50 +0000 (11:28 -0700)
* 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelvare/staging:
  hwmon: (max6650) Add support for alarms
  hwmon: (f71882fg) Add support for the F71858F
  hwmon: (f71882fg) Add temp#_fault sysfs attr for f8000
  hwmon: (f71882fg) Sanity check f8000 pwm settings
  hwmon: (f71882fg) Cleanup f8000 pwm handling
  hwmon: PCI quirk for hwmon access on MSI MS-7031 board
  hwmon: (w83627ehf) Add W83627DHG-P support
  hwmon: (tmp401) Add documentation
  hwmon: (tmp401) Add support for TI's TMP411 sensors chip
  hwmon: (tmp401) Add support for TI's TMP401 sensor chip
  hwmon: (ibmaem) Automatically load on HC10 blade
  hwmon: Fix more __devexit_p glitches

14 files changed:
Documentation/hwmon/f71882fg
Documentation/hwmon/ibmaem
Documentation/hwmon/sysfs-interface
Documentation/hwmon/tmp401 [new file with mode: 0644]
Documentation/hwmon/w83627ehf
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/hwmon/f71882fg.c
drivers/hwmon/hwmon.c
drivers/hwmon/ibmaem.c
drivers/hwmon/max6650.c
drivers/hwmon/sht15.c
drivers/hwmon/tmp401.c [new file with mode: 0644]
drivers/hwmon/w83627ehf.c

index a832126..bee4c30 100644 (file)
@@ -2,14 +2,18 @@ Kernel driver f71882fg
 ======================
 
 Supported chips:
-  * Fintek F71882FG and F71883FG
-    Prefix: 'f71882fg'
+  * Fintek F71858FG
+    Prefix: 'f71858fg'
     Addresses scanned: none, address read from Super I/O config space
     Datasheet: Available from the Fintek website
   * Fintek F71862FG and F71863FG
     Prefix: 'f71862fg'
     Addresses scanned: none, address read from Super I/O config space
     Datasheet: Available from the Fintek website
+  * Fintek F71882FG and F71883FG
+    Prefix: 'f71882fg'
+    Addresses scanned: none, address read from Super I/O config space
+    Datasheet: Available from the Fintek website
   * Fintek F8000
     Prefix: 'f8000'
     Addresses scanned: none, address read from Super I/O config space
@@ -66,13 +70,13 @@ printed when loading the driver.
 
 Three different fan control modes are supported; the mode number is written
 to the pwm#_enable file. Note that not all modes are supported on all
-chips, and some modes may only be available in RPM / PWM mode on the F8000.
+chips, and some modes may only be available in RPM / PWM mode.
 Writing an unsupported mode will result in an invalid parameter error.
 
 * 1: Manual mode
   You ask for a specific PWM duty cycle / DC voltage or a specific % of
   fan#_full_speed by writing to the pwm# file. This mode is only
-  available on the F8000 if the fan channel is in RPM mode.
+  available on the F71858FG / F8000 if the fan channel is in RPM mode.
 
 * 2: Normal auto mode
   You can define a number of temperature/fan speed trip points, which % the
index e98bdfe..1e0d59e 100644 (file)
@@ -7,7 +7,7 @@ henceforth as AEM.
 Supported systems:
   * Any recent IBM System X server with AEM support.
     This includes the x3350, x3550, x3650, x3655, x3755, x3850 M2,
-    x3950 M2, and certain HS2x/LS2x/QS2x blades.  The IPMI host interface
+    x3950 M2, and certain HC10/HS2x/LS2x/QS2x blades.  The IPMI host interface
     driver ("ipmi-si") needs to be loaded for this driver to do anything.
     Prefix: 'ibmaem'
     Datasheet: Not available
index 004ee16..dcbd502 100644 (file)
@@ -70,6 +70,7 @@ are interpreted as 0! For more on how written strings are interpreted see the
 [0-*]  denotes any positive number starting from 0
 [1-*]  denotes any positive number starting from 1
 RO     read only value
+WO     write only value
 RW     read/write value
 
 Read/write values may be read-only for some chips, depending on the
@@ -295,6 +296,24 @@ temp[1-*]_label    Suggested temperature channel label.
                user-space.
                RO
 
+temp[1-*]_lowest
+               Historical minimum temperature
+               Unit: millidegree Celsius
+               RO
+
+temp[1-*]_highest
+               Historical maximum temperature
+               Unit: millidegree Celsius
+               RO
+
+temp[1-*]_reset_history
+               Reset temp_lowest and temp_highest
+               WO
+
+temp_reset_history
+               Reset temp_lowest and temp_highest for all sensors
+               WO
+
 Some chips measure temperature using external thermistors and an ADC, and
 report the temperature measurement as a voltage. Converting this voltage
 back to a temperature (or the other way around for limits) requires
diff --git a/Documentation/hwmon/tmp401 b/Documentation/hwmon/tmp401
new file mode 100644 (file)
index 0000000..9fc4472
--- /dev/null
@@ -0,0 +1,42 @@
+Kernel driver tmp401
+====================
+
+Supported chips:
+  * Texas Instruments TMP401
+    Prefix: 'tmp401'
+    Addresses scanned: I2C 0x4c
+    Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp401.html
+  * Texas Instruments TMP411
+    Prefix: 'tmp411'
+    Addresses scanned: I2C 0x4c
+    Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp411.html
+
+Authors:
+         Hans de Goede <hdegoede@redhat.com>
+        Andre Prendel <andre.prendel@gmx.de>
+
+Description
+-----------
+
+This driver implements support for Texas Instruments TMP401 and
+TMP411 chips. These chips implements one remote and one local
+temperature sensor. Temperature is measured in degrees
+Celsius. Resolution of the remote sensor is 0.0625 degree. Local
+sensor resolution can be set to 0.5, 0.25, 0.125 or 0.0625 degree (not
+supported by the driver so far, so using the default resolution of 0.5
+degree).
+
+The driver provides the common sysfs-interface for temperatures (see
+/Documentation/hwmon/sysfs-interface under Temperatures).
+
+The TMP411 chip is compatible with TMP401. It provides some additional
+features.
+
+* Minimum and Maximum temperature measured since power-on, chip-reset
+
+  Exported via sysfs attributes tempX_lowest and tempX_highest.
+
+* Reset of historical minimum/maximum temperature measurements
+
+  Exported via sysfs attribute temp_reset_history. Writing 1 to this
+  file triggers a reset.
index b6eb593..02b7489 100644 (file)
@@ -12,6 +12,10 @@ Supported chips:
     Addresses scanned: ISA address retrieved from Super I/O registers
     Datasheet:
         http://www.nuvoton.com.tw/NR/rdonlyres/7885623D-A487-4CF9-A47F-30C5F73D6FE6/0/W83627DHG.pdf
+  * Winbond W83627DHG-P
+    Prefix: 'w83627dhg'
+    Addresses scanned: ISA address retrieved from Super I/O registers
+    Datasheet: not available
   * Winbond W83667HG
     Prefix: 'w83667hg'
     Addresses scanned: ISA address retrieved from Super I/O registers
@@ -28,8 +32,8 @@ Description
 -----------
 
 This driver implements support for the Winbond W83627EHF, W83627EHG,
-W83627DHG and W83667HG super I/O chips. We will refer to them collectively
-as Winbond chips.
+W83627DHG, W83627DHG-P and W83667HG 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 (only nine for the 627DHG), one
@@ -135,3 +139,6 @@ done in the driver for all register addresses.
 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.
+
+The DHG-P has an additional automatic fan speed control mode named Smart Fan
+(TM) III+. This mode is not yet supported by the driver.
index d73f5f4..f8090e1 100644 (file)
@@ -306,11 +306,11 @@ config SENSORS_F71805F
          will be called f71805f.
 
 config SENSORS_F71882FG
-       tristate "Fintek F71862FG, F71882FG and F8000"
+       tristate "Fintek F71858FG, F71862FG, F71882FG and F8000"
        depends on EXPERIMENTAL
        help
          If you say yes here you get support for hardware monitoring
-         features of the Fintek F71882FG/F71883FG, F71862FG/71863FG
+         features of the Fintek F71858FG, F71862FG/71863FG, F71882FG/F71883FG
          and F8000 Super-I/O chips.
 
          This driver can also be built as a module.  If so, the module
@@ -418,7 +418,7 @@ config SENSORS_IBMAEM
          power sensors and capping hardware in various IBM System X
          servers that support Active Energy Manager.  This includes
          the x3350, x3550, x3650, x3655, x3755, x3850 M2, x3950 M2,
-         and certain HS2x/LS2x/QS2x blades.
+         and certain HC10/HS2x/LS2x/QS2x blades.
 
          This driver can also be built as a module.  If so, the module
          will be called ibmaem.
@@ -787,6 +787,16 @@ config SENSORS_THMC50
          This driver can also be built as a module.  If so, the module
          will be called thmc50.
 
+config SENSORS_TMP401
+       tristate "Texas Instruments TMP401 and compatibles"
+       depends on I2C && EXPERIMENTAL
+       help
+         If you say yes here you get support for Texas Instruments TMP401 and
+         TMP411 temperature sensor chips.
+
+         This driver can also be built as a module.  If so, the module
+         will be called tmp401.
+
 config SENSORS_VIA686A
        tristate "VIA686A"
        depends on PCI
index 0ae2698..b793dce 100644 (file)
@@ -82,6 +82,7 @@ obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
 obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
 obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
 obj-$(CONFIG_SENSORS_THMC50)   += thmc50.o
+obj-$(CONFIG_SENSORS_TMP401)   += tmp401.o
 obj-$(CONFIG_SENSORS_VIA686A)  += via686a.o
 obj-$(CONFIG_SENSORS_VT1211)   += vt1211.o
 obj-$(CONFIG_SENSORS_VT8231)   += vt8231.o
index 5f81ddf..4146105 100644 (file)
@@ -1,6 +1,6 @@
 /***************************************************************************
  *   Copyright (C) 2006 by Hans Edgington <hans@edgington.nl>              *
- *   Copyright (C) 2007,2008 by Hans de Goede <hdegoede@redhat.com>        *
+ *   Copyright (C) 2007-2009 Hans de Goede <hdegoede@redhat.com>           *
  *                                                                         *
  *   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  *
@@ -32,6 +32,7 @@
 
 #define DRVNAME "f71882fg"
 
+#define SIO_F71858FG_LD_HWM    0x02    /* Hardware monitor logical device */
 #define SIO_F71882FG_LD_HWM    0x04    /* Hardware monitor logical device */
 #define SIO_UNLOCK_KEY         0x87    /* Key to enable Super-I/O */
 #define SIO_LOCK_KEY           0xAA    /* Key to diasble Super-I/O */
@@ -44,6 +45,7 @@
 #define SIO_REG_ADDR           0x60    /* Logical device address (2 bytes) */
 
 #define SIO_FINTEK_ID          0x1934  /* Manufacturers ID */
+#define SIO_F71858_ID          0x0507  /* Chipset ID */
 #define SIO_F71862_ID          0x0601  /* Chipset ID */
 #define SIO_F71882_ID          0x0541  /* Chipset ID */
 #define SIO_F8000_ID           0x0581  /* Chipset ID */
@@ -70,6 +72,7 @@
 #define F71882FG_REG_TEMP_HIGH(nr)     (0x81 + 2 * (nr))
 #define F71882FG_REG_TEMP_STATUS       0x62
 #define F71882FG_REG_TEMP_BEEP         0x63
+#define F71882FG_REG_TEMP_CONFIG       0x69
 #define F71882FG_REG_TEMP_HYST(nr)     (0x6C + (nr))
 #define F71882FG_REG_TEMP_TYPE         0x6B
 #define F71882FG_REG_TEMP_DIODE_OPEN   0x6F
@@ -92,9 +95,10 @@ static unsigned short force_id;
 module_param(force_id, ushort, 0);
 MODULE_PARM_DESC(force_id, "Override the detected device ID");
 
-enum chips { f71862fg, f71882fg, f8000 };
+enum chips { f71858fg, f71862fg, f71882fg, f8000 };
 
 static const char *f71882fg_names[] = {
+       "f71858fg",
        "f71862fg",
        "f71882fg",
        "f8000",
@@ -119,6 +123,7 @@ struct f71882fg_data {
        struct device *hwmon_dev;
 
        struct mutex update_lock;
+       int temp_start;                 /* temp numbering start (0 or 1) */
        char valid;                     /* !=0 if following fields are valid */
        unsigned long last_updated;     /* In jiffies */
        unsigned long last_limits;      /* In jiffies */
@@ -136,7 +141,7 @@ struct f71882fg_data {
        /* Note: all models have only 3 temperature channels, but on some
           they are addressed as 0-2 and on others as 1-3, so for coding
           convenience we reserve space for 4 channels */
-       u     temp[4];
+       u16     temp[4];
        u8      temp_ovt[4];
        u8      temp_high[4];
        u8      temp_hyst[2]; /* 2 hysts stored per reg */
@@ -144,6 +149,7 @@ struct f71882fg_data {
        u8      temp_status;
        u8      temp_beep;
        u8      temp_diode_open;
+       u8      temp_config;
        u8      pwm[4];
        u8      pwm_enable;
        u8      pwm_auto_point_hyst[2];
@@ -247,11 +253,55 @@ static struct platform_driver f71882fg_driver = {
                .name   = DRVNAME,
        },
        .probe          = f71882fg_probe,
-       .remove         = __devexit_p(f71882fg_remove),
+       .remove         = f71882fg_remove,
 };
 
 static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
 
+/* Temp and in attr for the f71858fg */
+static struct sensor_device_attribute_2 f71858fg_in_temp_attr[] = {
+       SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0),
+       SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1),
+       SENSOR_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 0, 2),
+       SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0),
+       SENSOR_ATTR_2(temp1_max, S_IRUGO|S_IWUSR, show_temp_max,
+               store_temp_max, 0, 0),
+       SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst,
+               store_temp_max_hyst, 0, 0),
+       SENSOR_ATTR_2(temp1_max_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 0),
+       SENSOR_ATTR_2(temp1_crit, S_IRUGO|S_IWUSR, show_temp_crit,
+               store_temp_crit, 0, 0),
+       SENSOR_ATTR_2(temp1_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL,
+               0, 0),
+       SENSOR_ATTR_2(temp1_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 4),
+       SENSOR_ATTR_2(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0, 0),
+       SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 1),
+       SENSOR_ATTR_2(temp2_max, S_IRUGO|S_IWUSR, show_temp_max,
+               store_temp_max, 0, 1),
+       SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst,
+               store_temp_max_hyst, 0, 1),
+       SENSOR_ATTR_2(temp2_max_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 1),
+       SENSOR_ATTR_2(temp2_crit, S_IRUGO|S_IWUSR, show_temp_crit,
+               store_temp_crit, 0, 1),
+       SENSOR_ATTR_2(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL,
+               0, 1),
+       SENSOR_ATTR_2(temp2_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 5),
+       SENSOR_ATTR_2(temp2_type, S_IRUGO, show_temp_type, NULL, 0, 1),
+       SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 1),
+       SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 2),
+       SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_max,
+               store_temp_max, 0, 2),
+       SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst,
+               store_temp_max_hyst, 0, 2),
+       SENSOR_ATTR_2(temp3_max_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 2),
+       SENSOR_ATTR_2(temp3_crit, S_IRUGO|S_IWUSR, show_temp_crit,
+               store_temp_crit, 0, 2),
+       SENSOR_ATTR_2(temp3_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL,
+               0, 2),
+       SENSOR_ATTR_2(temp3_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 6),
+       SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 2),
+};
+
 /* Temp and in attr common to both the f71862fg and f71882fg */
 static struct sensor_device_attribute_2 f718x2fg_in_temp_attr[] = {
        SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0),
@@ -344,6 +394,7 @@ static struct sensor_device_attribute_2 f8000_in_temp_attr[] = {
        SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max,
                store_temp_max, 0, 0),
        SENSOR_ATTR_2(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 4),
+       SENSOR_ATTR_2(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0, 0),
        SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 1),
        SENSOR_ATTR_2(temp2_max, S_IRUGO|S_IWUSR, show_temp_crit,
                store_temp_crit, 0, 1),
@@ -351,12 +402,14 @@ static struct sensor_device_attribute_2 f8000_in_temp_attr[] = {
                store_temp_max, 0, 1),
        SENSOR_ATTR_2(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 5),
        SENSOR_ATTR_2(temp2_type, S_IRUGO, show_temp_type, NULL, 0, 1),
+       SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 1),
        SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 2),
        SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_crit,
                store_temp_crit, 0, 2),
        SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO|S_IWUSR, show_temp_max,
                store_temp_max, 0, 2),
        SENSOR_ATTR_2(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 6),
+       SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 2),
 };
 
 /* Fan / PWM attr common to all models */
@@ -395,6 +448,9 @@ static struct sensor_device_attribute_2 fxxxx_fan_attr[] = {
                      show_pwm_auto_point_channel,
                      store_pwm_auto_point_channel, 0, 1),
 
+       SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 2),
+       SENSOR_ATTR_2(pwm3_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
+                     store_pwm_enable, 0, 2),
        SENSOR_ATTR_2(pwm3_interpolate, S_IRUGO|S_IWUSR,
                      show_pwm_interpolate, store_pwm_interpolate, 0, 2),
        SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR,
@@ -450,9 +506,6 @@ static struct sensor_device_attribute_2 f71862fg_fan_attr[] = {
        SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO,
                      show_pwm_auto_point_temp_hyst, NULL, 3, 1),
 
-       SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 2),
-       SENSOR_ATTR_2(pwm3_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
-                     store_pwm_enable, 0, 2),
        SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR,
                      show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
                      1, 2),
@@ -473,22 +526,8 @@ static struct sensor_device_attribute_2 f71862fg_fan_attr[] = {
                      show_pwm_auto_point_temp_hyst, NULL, 3, 2),
 };
 
-/* Fan / PWM attr for the f71882fg */
-static struct sensor_device_attribute_2 f71882fg_fan_attr[] = {
-       SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep,
-               store_fan_beep, 0, 0),
-       SENSOR_ATTR_2(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep,
-               store_fan_beep, 0, 1),
-       SENSOR_ATTR_2(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep,
-               store_fan_beep, 0, 2),
-       SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3),
-       SENSOR_ATTR_2(fan4_full_speed, S_IRUGO|S_IWUSR,
-                     show_fan_full_speed,
-                     store_fan_full_speed, 0, 3),
-       SENSOR_ATTR_2(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep,
-               store_fan_beep, 0, 3),
-       SENSOR_ATTR_2(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 3),
-
+/* Fan / PWM attr common to both the f71882fg and f71858fg */
+static struct sensor_device_attribute_2 f71882fg_f71858fg_fan_attr[] = {
        SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR,
                      show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
                      0, 0),
@@ -565,9 +604,6 @@ static struct sensor_device_attribute_2 f71882fg_fan_attr[] = {
        SENSOR_ATTR_2(pwm2_auto_point4_temp_hyst, S_IRUGO,
                      show_pwm_auto_point_temp_hyst, NULL, 3, 1),
 
-       SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 2),
-       SENSOR_ATTR_2(pwm3_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
-                     store_pwm_enable, 0, 2),
        SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR,
                      show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
                      0, 2),
@@ -605,6 +641,24 @@ static struct sensor_device_attribute_2 f71882fg_fan_attr[] = {
                      show_pwm_auto_point_temp_hyst, NULL, 2, 2),
        SENSOR_ATTR_2(pwm3_auto_point4_temp_hyst, S_IRUGO,
                      show_pwm_auto_point_temp_hyst, NULL, 3, 2),
+};
+
+/* Fan / PWM attr found on the f71882fg but not on the f71858fg */
+static struct sensor_device_attribute_2 f71882fg_fan_attr[] = {
+       SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep,
+               store_fan_beep, 0, 0),
+       SENSOR_ATTR_2(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep,
+               store_fan_beep, 0, 1),
+       SENSOR_ATTR_2(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep,
+               store_fan_beep, 0, 2),
+
+       SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3),
+       SENSOR_ATTR_2(fan4_full_speed, S_IRUGO|S_IWUSR,
+                     show_fan_full_speed,
+                     store_fan_full_speed, 0, 3),
+       SENSOR_ATTR_2(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep,
+               store_fan_beep, 0, 3),
+       SENSOR_ATTR_2(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 3),
 
        SENSOR_ATTR_2(pwm4, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 3),
        SENSOR_ATTR_2(pwm4_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
@@ -659,8 +713,6 @@ static struct sensor_device_attribute_2 f71882fg_fan_attr[] = {
 static struct sensor_device_attribute_2 f8000_fan_attr[] = {
        SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3),
 
-       SENSOR_ATTR_2(pwm3, S_IRUGO, show_pwm, NULL, 0, 2),
-
        SENSOR_ATTR_2(temp1_auto_point1_pwm, S_IRUGO|S_IWUSR,
                      show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
                      0, 2),
@@ -857,13 +909,20 @@ static void f71882fg_write16(struct f71882fg_data *data, u8 reg, u16 val)
        outb(val & 255, data->addr + DATA_REG_OFFSET);
 }
 
+static u16 f71882fg_read_temp(struct f71882fg_data *data, int nr)
+{
+       if (data->type == f71858fg)
+               return f71882fg_read16(data, F71882FG_REG_TEMP(nr));
+       else
+               return f71882fg_read8(data, F71882FG_REG_TEMP(nr));
+}
+
 static struct f71882fg_data *f71882fg_update_device(struct device *dev)
 {
        struct f71882fg_data *data = dev_get_drvdata(dev);
        int nr, reg = 0, reg2;
        int nr_fans = (data->type == f71882fg) ? 4 : 3;
-       int nr_ins = (data->type == f8000) ? 3 : 9;
-       int temp_start = (data->type == f8000) ? 0 : 1;
+       int nr_ins = (data->type == f71858fg || data->type == f8000) ? 3 : 9;
 
        mutex_lock(&data->update_lock);
 
@@ -878,7 +937,7 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
                }
 
                /* Get High & boundary temps*/
-               for (nr = temp_start; nr < 3 + temp_start; nr++) {
+               for (nr = data->temp_start; nr < 3 + data->temp_start; nr++) {
                        data->temp_ovt[nr] = f71882fg_read8(data,
                                                F71882FG_REG_TEMP_OVT(nr));
                        data->temp_high[nr] = f71882fg_read8(data,
@@ -886,14 +945,17 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
                }
 
                if (data->type != f8000) {
-                       data->fan_beep = f71882fg_read8(data,
-                                               F71882FG_REG_FAN_BEEP);
-                       data->temp_beep = f71882fg_read8(data,
-                                               F71882FG_REG_TEMP_BEEP);
                        data->temp_hyst[0] = f71882fg_read8(data,
                                                F71882FG_REG_TEMP_HYST(0));
                        data->temp_hyst[1] = f71882fg_read8(data,
                                                F71882FG_REG_TEMP_HYST(1));
+               }
+
+               if (data->type == f71862fg || data->type == f71882fg) {
+                       data->fan_beep = f71882fg_read8(data,
+                                               F71882FG_REG_FAN_BEEP);
+                       data->temp_beep = f71882fg_read8(data,
+                                               F71882FG_REG_TEMP_BEEP);
                        /* Have to hardcode type, because temp1 is special */
                        reg  = f71882fg_read8(data, F71882FG_REG_TEMP_TYPE);
                        data->temp_type[2] = (reg & 0x04) ? 2 : 4;
@@ -904,10 +966,10 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
                        data->temp_type[1] = 6 /* PECI */;
                else if ((reg2 & 0x03) == 0x02)
                        data->temp_type[1] = 5 /* AMDSI */;
-               else if (data->type != f8000)
+               else if (data->type == f71862fg || data->type == f71882fg)
                        data->temp_type[1] = (reg & 0x02) ? 2 : 4;
                else
-                       data->temp_type[1] = 2; /* F8000 only supports BJT */
+                       data->temp_type[1] = 2; /* Only supports BJT */
 
                data->pwm_enable = f71882fg_read8(data,
                                                  F71882FG_REG_PWM_ENABLE);
@@ -963,9 +1025,8 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
                                                F71882FG_REG_TEMP_STATUS);
                data->temp_diode_open = f71882fg_read8(data,
                                                F71882FG_REG_TEMP_DIODE_OPEN);
-               for (nr = temp_start; nr < 3 + temp_start; nr++)
-                       data->temp[nr] = f71882fg_read8(data,
-                                               F71882FG_REG_TEMP(nr));
+               for (nr = data->temp_start; nr < 3 + data->temp_start; nr++)
+                       data->temp[nr] = f71882fg_read_temp(data, nr);
 
                data->fan_status = f71882fg_read8(data,
                                                F71882FG_REG_FAN_STATUS);
@@ -1168,8 +1229,24 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
 {
        struct f71882fg_data *data = f71882fg_update_device(dev);
        int nr = to_sensor_dev_attr_2(devattr)->index;
+       int sign, temp;
+
+       if (data->type == f71858fg) {
+               /* TEMP_TABLE_SEL 1 or 3 ? */
+               if (data->temp_config & 1) {
+                       sign = data->temp[nr] & 0x0001;
+                       temp = (data->temp[nr] >> 5) & 0x7ff;
+               } else {
+                       sign = data->temp[nr] & 0x8000;
+                       temp = (data->temp[nr] >> 5) & 0x3ff;
+               }
+               temp *= 125;
+               if (sign)
+                       temp -= 128000;
+       } else
+               temp = data->temp[nr] * 1000;
 
-       return sprintf(buf, "%d\n", data->temp[nr] * 1000);
+       return sprintf(buf, "%d\n", temp);
 }
 
 static ssize_t show_temp_max(struct device *dev, struct device_attribute
@@ -1440,6 +1517,10 @@ static ssize_t store_pwm_enable(struct device *dev, struct device_attribute
        int nr = to_sensor_dev_attr_2(devattr)->index;
        long val = simple_strtol(buf, NULL, 10);
 
+       /* Special case for F8000 pwm channel 3 which only does auto mode */
+       if (data->type == f8000 && nr == 2 && val != 2)
+               return -EINVAL;
+
        mutex_lock(&data->update_lock);
        data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE);
        /* Special case for F8000 auto PWM mode / Thermostat mode */
@@ -1458,6 +1539,12 @@ static ssize_t store_pwm_enable(struct device *dev, struct device_attribute
        } else {
                switch (val) {
                case 1:
+                       /* The f71858fg does not support manual RPM mode */
+                       if (data->type == f71858fg &&
+                           ((data->pwm_enable >> (2 * nr)) & 1)) {
+                               count = -EINVAL;
+                               goto leave;
+                       }
                        data->pwm_enable |= 2 << (2 * nr);
                        break;          /* Manual */
                case 2:
@@ -1616,9 +1703,9 @@ static ssize_t show_pwm_auto_point_channel(struct device *dev,
        int result;
        struct f71882fg_data *data = f71882fg_update_device(dev);
        int nr = to_sensor_dev_attr_2(devattr)->index;
-       int temp_start = (data->type == f8000) ? 0 : 1;
 
-       result = 1 << ((data->pwm_auto_point_mapping[nr] & 3) - temp_start);
+       result = 1 << ((data->pwm_auto_point_mapping[nr] & 3) -
+                      data->temp_start);
 
        return sprintf(buf, "%d\n", result);
 }
@@ -1629,7 +1716,6 @@ static ssize_t store_pwm_auto_point_channel(struct device *dev,
 {
        struct f71882fg_data *data = dev_get_drvdata(dev);
        int nr = to_sensor_dev_attr_2(devattr)->index;
-       int temp_start = (data->type == f8000) ? 0 : 1;
        long val = simple_strtol(buf, NULL, 10);
 
        switch (val) {
@@ -1645,7 +1731,7 @@ static ssize_t store_pwm_auto_point_channel(struct device *dev,
        default:
                return -EINVAL;
        }
-       val += temp_start;
+       val += data->temp_start;
        mutex_lock(&data->update_lock);
        data->pwm_auto_point_mapping[nr] =
                f71882fg_read8(data, F71882FG_REG_POINT_MAPPING(nr));
@@ -1721,6 +1807,8 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
 
        data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start;
        data->type = sio_data->type;
+       data->temp_start =
+           (data->type == f71858fg || data->type == f8000) ? 0 : 1;
        mutex_init(&data->update_lock);
        platform_set_drvdata(pdev, data);
 
@@ -1736,19 +1824,6 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
                goto exit_free;
        }
 
-       data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE);
-       /* If it is a 71862 and the fan / pwm part is enabled sanity check
-          the pwm settings */
-       if (data->type == f71862fg && (start_reg & 0x02)) {
-               if ((data->pwm_enable & 0x15) != 0x15) {
-                       dev_err(&pdev->dev,
-                               "Invalid (reserved) pwm settings: 0x%02x\n",
-                               (unsigned int)data->pwm_enable);
-                       err = -ENODEV;
-                       goto exit_free;
-               }
-       }
-
        /* Register sysfs interface files */
        err = device_create_file(&pdev->dev, &dev_attr_name);
        if (err)
@@ -1756,6 +1831,20 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
 
        if (start_reg & 0x01) {
                switch (data->type) {
+               case f71858fg:
+                       data->temp_config =
+                               f71882fg_read8(data, F71882FG_REG_TEMP_CONFIG);
+                       if (data->temp_config & 0x10)
+                               /* The f71858fg temperature alarms behave as
+                                  the f8000 alarms in this mode */
+                               err = f71882fg_create_sysfs_files(pdev,
+                                       f8000_in_temp_attr,
+                                       ARRAY_SIZE(f8000_in_temp_attr));
+                       else
+                               err = f71882fg_create_sysfs_files(pdev,
+                                       f71858fg_in_temp_attr,
+                                       ARRAY_SIZE(f71858fg_in_temp_attr));
+                       break;
                case f71882fg:
                        err = f71882fg_create_sysfs_files(pdev,
                                        f71882fg_in_temp_attr,
@@ -1779,6 +1868,35 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
        }
 
        if (start_reg & 0x02) {
+               data->pwm_enable =
+                       f71882fg_read8(data, F71882FG_REG_PWM_ENABLE);
+
+               /* Sanity check the pwm settings */
+               switch (data->type) {
+               case f71858fg:
+                       err = 0;
+                       for (i = 0; i < nr_fans; i++)
+                               if (((data->pwm_enable >> (i * 2)) & 3) == 3)
+                                       err = 1;
+                       break;
+               case f71862fg:
+                       err = (data->pwm_enable & 0x15) != 0x15;
+                       break;
+               case f71882fg:
+                       err = 0;
+                       break;
+               case f8000:
+                       err = data->pwm_enable & 0x20;
+                       break;
+               }
+               if (err) {
+                       dev_err(&pdev->dev,
+                               "Invalid (reserved) pwm settings: 0x%02x\n",
+                               (unsigned int)data->pwm_enable);
+                       err = -ENODEV;
+                       goto exit_unregister_sysfs;
+               }
+
                err = f71882fg_create_sysfs_files(pdev, fxxxx_fan_attr,
                                        ARRAY_SIZE(fxxxx_fan_attr));
                if (err)
@@ -1794,6 +1912,13 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
                        err = f71882fg_create_sysfs_files(pdev,
                                        f71882fg_fan_attr,
                                        ARRAY_SIZE(f71882fg_fan_attr));
+                       if (err)
+                               goto exit_unregister_sysfs;
+                       /* fall through! */
+               case f71858fg:
+                       err = f71882fg_create_sysfs_files(pdev,
+                                       f71882fg_f71858fg_fan_attr,
+                                       ARRAY_SIZE(f71882fg_f71858fg_fan_attr));
                        break;
                case f8000:
                        err = f71882fg_create_sysfs_files(pdev,
@@ -1878,6 +2003,9 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address,
 
        devid = force_id ? force_id : superio_inw(sioaddr, SIO_REG_DEVID);
        switch (devid) {
+       case SIO_F71858_ID:
+               sio_data->type = f71858fg;
+               break;
        case SIO_F71862_ID:
                sio_data->type = f71862fg;
                break;
@@ -1892,7 +2020,11 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address,
                goto exit;
        }
 
-       superio_select(sioaddr, SIO_F71882FG_LD_HWM);
+       if (sio_data->type == f71858fg)
+               superio_select(sioaddr, SIO_F71858FG_LD_HWM);
+       else
+               superio_select(sioaddr, SIO_F71882FG_LD_HWM);
+
        if (!(superio_inb(sioaddr, SIO_REG_ENABLE) & 0x01)) {
                printk(KERN_WARNING DRVNAME ": Device not activated\n");
                goto exit;
index e15c3e7..29ea675 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/hwmon.h>
 #include <linux/gfp.h>
 #include <linux/spinlock.h>
+#include <linux/pci.h>
 
 #define HWMON_ID_PREFIX "hwmon"
 #define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d"
@@ -86,8 +87,36 @@ void hwmon_device_unregister(struct device *dev)
                        "hwmon_device_unregister() failed: bad class ID!\n");
 }
 
+static void __init hwmon_pci_quirks(void)
+{
+#if defined CONFIG_X86 && defined CONFIG_PCI
+       struct pci_dev *sb;
+       u16 base;
+       u8 enable;
+
+       /* Open access to 0x295-0x296 on MSI MS-7031 */
+       sb = pci_get_device(PCI_VENDOR_ID_ATI, 0x436c, NULL);
+       if (sb &&
+           (sb->subsystem_vendor == 0x1462 &&  /* MSI */
+            sb->subsystem_device == 0x0031)) { /* MS-7031 */
+
+               pci_read_config_byte(sb, 0x48, &enable);
+               pci_read_config_word(sb, 0x64, &base);
+
+               if (base == 0 && !(enable & BIT(2))) {
+                       dev_info(&sb->dev,
+                                "Opening wide generic port at 0x295\n");
+                       pci_write_config_word(sb, 0x64, 0x295);
+                       pci_write_config_byte(sb, 0x48, enable | BIT(2));
+               }
+       }
+#endif
+}
+
 static int __init hwmon_init(void)
 {
+       hwmon_pci_quirks();
+
        hwmon_class = class_create(THIS_MODULE, "hwmon");
        if (IS_ERR(hwmon_class)) {
                printk(KERN_ERR "hwmon.c: couldn't create sysfs class\n");
index fe74609..405d3fb 100644 (file)
@@ -1127,3 +1127,4 @@ MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650-*");
 MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3655-*");
 MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3755-*");
 MODULE_ALIAS("dmi:bvnIBM:*:pnIBM3850M2/x3950M2-*");
+MODULE_ALIAS("dmi:bvnIBM:*:pnIBMBladeHC10-*");
index f27af6a..86142a8 100644 (file)
@@ -12,7 +12,7 @@
  * also work with the MAX6651. It does not distinguish max6650 and max6651
  * chips.
  *
- * Tha datasheet was last seen at:
+ * The datasheet was last seen at:
  *
  *        http://pdfserv.maxim-ic.com/en/ds/MAX6650-MAX6651.pdf
  *
@@ -98,6 +98,16 @@ I2C_CLIENT_INSMOD_1(max6650);
 #define MAX6650_CFG_MODE_OPEN_LOOP     0x30
 #define MAX6650_COUNT_MASK             0x03
 
+/*
+ * Alarm status register bits
+ */
+
+#define MAX6650_ALRM_MAX       0x01
+#define MAX6650_ALRM_MIN       0x02
+#define MAX6650_ALRM_TACH      0x04
+#define MAX6650_ALRM_GPIO1     0x08
+#define MAX6650_ALRM_GPIO2     0x10
+
 /* Minimum and maximum values of the FAN-RPM */
 #define FAN_RPM_MIN 240
 #define FAN_RPM_MAX 30000
@@ -151,6 +161,7 @@ struct max6650_data
        u8 tach[4];
        u8 count;
        u8 dac;
+       u8 alarm;
 };
 
 static ssize_t get_fan(struct device *dev, struct device_attribute *devattr,
@@ -418,6 +429,33 @@ static ssize_t set_div(struct device *dev, struct device_attribute *devattr,
        return count;
 }
 
+/*
+ * Get alarm stati:
+ * Possible values:
+ * 0 = no alarm
+ * 1 = alarm
+ */
+
+static ssize_t get_alarm(struct device *dev, struct device_attribute *devattr,
+                        char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct max6650_data *data = max6650_update_device(dev);
+       struct i2c_client *client = to_i2c_client(dev);
+       int alarm = 0;
+
+       if (data->alarm & attr->index) {
+               mutex_lock(&data->update_lock);
+               alarm = 1;
+               data->alarm &= ~attr->index;
+               data->alarm |= i2c_smbus_read_byte_data(client,
+                                                       MAX6650_REG_ALARM);
+               mutex_unlock(&data->update_lock);
+       }
+
+       return sprintf(buf, "%d\n", alarm);
+}
+
 static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, get_fan, NULL, 0);
 static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, get_fan, NULL, 1);
 static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, get_fan, NULL, 2);
@@ -426,7 +464,41 @@ static DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO, get_target, set_target);
 static DEVICE_ATTR(fan1_div, S_IWUSR | S_IRUGO, get_div, set_div);
 static DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, get_enable, set_enable);
 static DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm, set_pwm);
+static SENSOR_DEVICE_ATTR(fan1_max_alarm, S_IRUGO, get_alarm, NULL,
+                         MAX6650_ALRM_MAX);
+static SENSOR_DEVICE_ATTR(fan1_min_alarm, S_IRUGO, get_alarm, NULL,
+                         MAX6650_ALRM_MIN);
+static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, get_alarm, NULL,
+                         MAX6650_ALRM_TACH);
+static SENSOR_DEVICE_ATTR(gpio1_alarm, S_IRUGO, get_alarm, NULL,
+                         MAX6650_ALRM_GPIO1);
+static SENSOR_DEVICE_ATTR(gpio2_alarm, S_IRUGO, get_alarm, NULL,
+                         MAX6650_ALRM_GPIO2);
+
+static mode_t max6650_attrs_visible(struct kobject *kobj, struct attribute *a,
+                                   int n)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct i2c_client *client = to_i2c_client(dev);
+       u8 alarm_en = i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM_EN);
+       struct device_attribute *devattr;
 
+       /*
+        * Hide the alarms that have not been enabled by the firmware
+        */
+
+       devattr = container_of(a, struct device_attribute, attr);
+       if (devattr == &sensor_dev_attr_fan1_max_alarm.dev_attr
+        || devattr == &sensor_dev_attr_fan1_min_alarm.dev_attr
+        || devattr == &sensor_dev_attr_fan1_fault.dev_attr
+        || devattr == &sensor_dev_attr_gpio1_alarm.dev_attr
+        || devattr == &sensor_dev_attr_gpio2_alarm.dev_attr) {
+               if (!(alarm_en & to_sensor_dev_attr(devattr)->index))
+                       return 0;
+       }
+
+       return a->mode;
+}
 
 static struct attribute *max6650_attrs[] = {
        &sensor_dev_attr_fan1_input.dev_attr.attr,
@@ -437,11 +509,17 @@ static struct attribute *max6650_attrs[] = {
        &dev_attr_fan1_div.attr,
        &dev_attr_pwm1_enable.attr,
        &dev_attr_pwm1.attr,
+       &sensor_dev_attr_fan1_max_alarm.dev_attr.attr,
+       &sensor_dev_attr_fan1_min_alarm.dev_attr.attr,
+       &sensor_dev_attr_fan1_fault.dev_attr.attr,
+       &sensor_dev_attr_gpio1_alarm.dev_attr.attr,
+       &sensor_dev_attr_gpio2_alarm.dev_attr.attr,
        NULL
 };
 
 static struct attribute_group max6650_attr_grp = {
        .attrs = max6650_attrs,
+       .is_visible = max6650_attrs_visible,
 };
 
 /*
@@ -659,6 +737,12 @@ static struct max6650_data *max6650_update_device(struct device *dev)
                                                        MAX6650_REG_COUNT);
                data->dac = i2c_smbus_read_byte_data(client, MAX6650_REG_DAC);
 
+               /* Alarms are cleared on read in case the condition that
+                * caused the alarm is removed. Keep the value latched here
+                * for providing the register through different alarm files. */
+               data->alarm |= i2c_smbus_read_byte_data(client,
+                                                       MAX6650_REG_ALARM);
+
                data->last_updated = jiffies;
                data->valid = 1;
        }
index 6cbdc2f..56cd600 100644 (file)
@@ -627,35 +627,35 @@ static struct platform_driver sht_drivers[] = {
                        .owner = THIS_MODULE,
                },
                .probe = sht15_probe,
-               .remove = sht15_remove,
+               .remove = __devexit_p(sht15_remove),
        }, {
                .driver = {
                        .name = "sht11",
                        .owner = THIS_MODULE,
                },
                .probe = sht15_probe,
-               .remove = sht15_remove,
+               .remove = __devexit_p(sht15_remove),
        }, {
                .driver = {
                        .name = "sht15",
                        .owner = THIS_MODULE,
                },
                .probe = sht15_probe,
-               .remove = sht15_remove,
+               .remove = __devexit_p(sht15_remove),
        }, {
                .driver = {
                        .name = "sht71",
                        .owner = THIS_MODULE,
                },
                .probe = sht15_probe,
-               .remove = sht15_remove,
+               .remove = __devexit_p(sht15_remove),
        }, {
                .driver = {
                        .name = "sht75",
                        .owner = THIS_MODULE,
                },
                .probe = sht15_probe,
-               .remove = sht15_remove,
+               .remove = __devexit_p(sht15_remove),
        },
 };
 
diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c
new file mode 100644 (file)
index 0000000..7b34f2c
--- /dev/null
@@ -0,0 +1,690 @@
+/* tmp401.c
+ *
+ * Copyright (C) 2007,2008 Hans de Goede <hdegoede@redhat.com>
+ * Preliminary tmp411 support by:
+ * Gabriel Konat, Sander Leget, Wouter Willems
+ * Copyright (C) 2009 Andre Prendel <andre.prendel@gmx.de>
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ */
+
+/*
+ * Driver for the Texas Instruments TMP401 SMBUS temperature sensor IC.
+ *
+ * Note this IC is in some aspect similar to the LM90, but it has quite a
+ * few differences too, for example the local temp has a higher resolution
+ * and thus has 16 bits registers for its value and limit instead of 8 bits.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = { 0x4c, I2C_CLIENT_END };
+
+/* Insmod parameters */
+I2C_CLIENT_INSMOD_2(tmp401, tmp411);
+
+/*
+ * The TMP401 registers, note some registers have different addresses for
+ * reading and writing
+ */
+#define TMP401_STATUS                          0x02
+#define TMP401_CONFIG_READ                     0x03
+#define TMP401_CONFIG_WRITE                    0x09
+#define TMP401_CONVERSION_RATE_READ            0x04
+#define TMP401_CONVERSION_RATE_WRITE           0x0A
+#define TMP401_TEMP_CRIT_HYST                  0x21
+#define TMP401_CONSECUTIVE_ALERT               0x22
+#define TMP401_MANUFACTURER_ID_REG             0xFE
+#define TMP401_DEVICE_ID_REG                   0xFF
+#define TMP411_N_FACTOR_REG                    0x18
+
+static const u8 TMP401_TEMP_MSB[2]                     = { 0x00, 0x01 };
+static const u8 TMP401_TEMP_LSB[2]                     = { 0x15, 0x10 };
+static const u8 TMP401_TEMP_LOW_LIMIT_MSB_READ[2]      = { 0x06, 0x08 };
+static const u8 TMP401_TEMP_LOW_LIMIT_MSB_WRITE[2]     = { 0x0C, 0x0E };
+static const u8 TMP401_TEMP_LOW_LIMIT_LSB[2]           = { 0x17, 0x14 };
+static const u8 TMP401_TEMP_HIGH_LIMIT_MSB_READ[2]     = { 0x05, 0x07 };
+static const u8 TMP401_TEMP_HIGH_LIMIT_MSB_WRITE[2]    = { 0x0B, 0x0D };
+static const u8 TMP401_TEMP_HIGH_LIMIT_LSB[2]          = { 0x16, 0x13 };
+/* These are called the THERM limit / hysteresis / mask in the datasheet */
+static const u8 TMP401_TEMP_CRIT_LIMIT[2]              = { 0x20, 0x19 };
+
+static const u8 TMP411_TEMP_LOWEST_MSB[2]              = { 0x30, 0x34 };
+static const u8 TMP411_TEMP_LOWEST_LSB[2]              = { 0x31, 0x35 };
+static const u8 TMP411_TEMP_HIGHEST_MSB[2]             = { 0x32, 0x36 };
+static const u8 TMP411_TEMP_HIGHEST_LSB[2]             = { 0x33, 0x37 };
+
+/* Flags */
+#define TMP401_CONFIG_RANGE            0x04
+#define TMP401_CONFIG_SHUTDOWN         0x40
+#define TMP401_STATUS_LOCAL_CRIT               0x01
+#define TMP401_STATUS_REMOTE_CRIT              0x02
+#define TMP401_STATUS_REMOTE_OPEN              0x04
+#define TMP401_STATUS_REMOTE_LOW               0x08
+#define TMP401_STATUS_REMOTE_HIGH              0x10
+#define TMP401_STATUS_LOCAL_LOW                0x20
+#define TMP401_STATUS_LOCAL_HIGH               0x40
+
+/* Manufacturer / Device ID's */
+#define TMP401_MANUFACTURER_ID                 0x55
+#define TMP401_DEVICE_ID                       0x11
+#define TMP411_DEVICE_ID                       0x12
+
+/*
+ * Functions declarations
+ */
+
+static int tmp401_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id);
+static int tmp401_detect(struct i2c_client *client, int kind,
+                        struct i2c_board_info *info);
+static int tmp401_remove(struct i2c_client *client);
+static struct tmp401_data *tmp401_update_device(struct device *dev);
+
+/*
+ * Driver data (common to all clients)
+ */
+
+static const struct i2c_device_id tmp401_id[] = {
+       { "tmp401", tmp401 },
+       { "tmp411", tmp411 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, tmp401_id);
+
+static struct i2c_driver tmp401_driver = {
+       .class          = I2C_CLASS_HWMON,
+       .driver = {
+               .name   = "tmp401",
+       },
+       .probe          = tmp401_probe,
+       .remove         = tmp401_remove,
+       .id_table       = tmp401_id,
+       .detect         = tmp401_detect,
+       .address_data   = &addr_data,
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct tmp401_data {
+       struct device *hwmon_dev;
+       struct mutex update_lock;
+       char valid; /* zero until following fields are valid */
+       unsigned long last_updated; /* in jiffies */
+       int kind;
+
+       /* register values */
+       u8 status;
+       u8 config;
+       u16 temp[2];
+       u16 temp_low[2];
+       u16 temp_high[2];
+       u8 temp_crit[2];
+       u8 temp_crit_hyst;
+       u16 temp_lowest[2];
+       u16 temp_highest[2];
+};
+
+/*
+ * Sysfs attr show / store functions
+ */
+
+static int tmp401_register_to_temp(u16 reg, u8 config)
+{
+       int temp = reg;
+
+       if (config & TMP401_CONFIG_RANGE)
+               temp -= 64 * 256;
+
+       return (temp * 625 + 80) / 160;
+}
+
+static u16 tmp401_temp_to_register(long temp, u8 config)
+{
+       if (config & TMP401_CONFIG_RANGE) {
+               temp = SENSORS_LIMIT(temp, -64000, 191000);
+               temp += 64000;
+       } else
+               temp = SENSORS_LIMIT(temp, 0, 127000);
+
+       return (temp * 160 + 312) / 625;
+}
+
+static int tmp401_crit_register_to_temp(u8 reg, u8 config)
+{
+       int temp = reg;
+
+       if (config & TMP401_CONFIG_RANGE)
+               temp -= 64;
+
+       return temp * 1000;
+}
+
+static u8 tmp401_crit_temp_to_register(long temp, u8 config)
+{
+       if (config & TMP401_CONFIG_RANGE) {
+               temp = SENSORS_LIMIT(temp, -64000, 191000);
+               temp += 64000;
+       } else
+               temp = SENSORS_LIMIT(temp, 0, 127000);
+
+       return (temp + 500) / 1000;
+}
+
+static ssize_t show_temp_value(struct device *dev,
+       struct device_attribute *devattr, char *buf)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct tmp401_data *data = tmp401_update_device(dev);
+
+       return sprintf(buf, "%d\n",
+               tmp401_register_to_temp(data->temp[index], data->config));
+}
+
+static ssize_t show_temp_min(struct device *dev,
+       struct device_attribute *devattr, char *buf)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct tmp401_data *data = tmp401_update_device(dev);
+
+       return sprintf(buf, "%d\n",
+               tmp401_register_to_temp(data->temp_low[index], data->config));
+}
+
+static ssize_t show_temp_max(struct device *dev,
+       struct device_attribute *devattr, char *buf)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct tmp401_data *data = tmp401_update_device(dev);
+
+       return sprintf(buf, "%d\n",
+               tmp401_register_to_temp(data->temp_high[index], data->config));
+}
+
+static ssize_t show_temp_crit(struct device *dev,
+       struct device_attribute *devattr, char *buf)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct tmp401_data *data = tmp401_update_device(dev);
+
+       return sprintf(buf, "%d\n",
+                       tmp401_crit_register_to_temp(data->temp_crit[index],
+                                                       data->config));
+}
+
+static ssize_t show_temp_crit_hyst(struct device *dev,
+       struct device_attribute *devattr, char *buf)
+{
+       int temp, index = to_sensor_dev_attr(devattr)->index;
+       struct tmp401_data *data = tmp401_update_device(dev);
+
+       mutex_lock(&data->update_lock);
+       temp = tmp401_crit_register_to_temp(data->temp_crit[index],
+                                               data->config);
+       temp -= data->temp_crit_hyst * 1000;
+       mutex_unlock(&data->update_lock);
+
+       return sprintf(buf, "%d\n", temp);
+}
+
+static ssize_t show_temp_lowest(struct device *dev,
+       struct device_attribute *devattr, char *buf)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct tmp401_data *data = tmp401_update_device(dev);
+
+       return sprintf(buf, "%d\n",
+               tmp401_register_to_temp(data->temp_lowest[index],
+                                       data->config));
+}
+
+static ssize_t show_temp_highest(struct device *dev,
+       struct device_attribute *devattr, char *buf)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct tmp401_data *data = tmp401_update_device(dev);
+
+       return sprintf(buf, "%d\n",
+               tmp401_register_to_temp(data->temp_highest[index],
+                                       data->config));
+}
+
+static ssize_t show_status(struct device *dev,
+       struct device_attribute *devattr, char *buf)
+{
+       int mask = to_sensor_dev_attr(devattr)->index;
+       struct tmp401_data *data = tmp401_update_device(dev);
+
+       if (data->status & mask)
+               return sprintf(buf, "1\n");
+       else
+               return sprintf(buf, "0\n");
+}
+
+static ssize_t store_temp_min(struct device *dev, struct device_attribute
+       *devattr, const char *buf, size_t count)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct tmp401_data *data = tmp401_update_device(dev);
+       long val;
+       u16 reg;
+
+       if (strict_strtol(buf, 10, &val))
+               return -EINVAL;
+
+       reg = tmp401_temp_to_register(val, data->config);
+
+       mutex_lock(&data->update_lock);
+
+       i2c_smbus_write_byte_data(to_i2c_client(dev),
+               TMP401_TEMP_LOW_LIMIT_MSB_WRITE[index], reg >> 8);
+       i2c_smbus_write_byte_data(to_i2c_client(dev),
+               TMP401_TEMP_LOW_LIMIT_LSB[index], reg & 0xFF);
+
+       data->temp_low[index] = reg;
+
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static ssize_t store_temp_max(struct device *dev, struct device_attribute
+       *devattr, const char *buf, size_t count)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct tmp401_data *data = tmp401_update_device(dev);
+       long val;
+       u16 reg;
+
+       if (strict_strtol(buf, 10, &val))
+               return -EINVAL;
+
+       reg = tmp401_temp_to_register(val, data->config);
+
+       mutex_lock(&data->update_lock);
+
+       i2c_smbus_write_byte_data(to_i2c_client(dev),
+               TMP401_TEMP_HIGH_LIMIT_MSB_WRITE[index], reg >> 8);
+       i2c_smbus_write_byte_data(to_i2c_client(dev),
+               TMP401_TEMP_HIGH_LIMIT_LSB[index], reg & 0xFF);
+
+       data->temp_high[index] = reg;
+
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static ssize_t store_temp_crit(struct device *dev, struct device_attribute
+       *devattr, const char *buf, size_t count)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct tmp401_data *data = tmp401_update_device(dev);
+       long val;
+       u8 reg;
+
+       if (strict_strtol(buf, 10, &val))
+               return -EINVAL;
+
+       reg = tmp401_crit_temp_to_register(val, data->config);
+
+       mutex_lock(&data->update_lock);
+
+       i2c_smbus_write_byte_data(to_i2c_client(dev),
+               TMP401_TEMP_CRIT_LIMIT[index], reg);
+
+       data->temp_crit[index] = reg;
+
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static ssize_t store_temp_crit_hyst(struct device *dev, struct device_attribute
+       *devattr, const char *buf, size_t count)
+{
+       int temp, index = to_sensor_dev_attr(devattr)->index;
+       struct tmp401_data *data = tmp401_update_device(dev);
+       long val;
+       u8 reg;
+
+       if (strict_strtol(buf, 10, &val))
+               return -EINVAL;
+
+       if (data->config & TMP401_CONFIG_RANGE)
+               val = SENSORS_LIMIT(val, -64000, 191000);
+       else
+               val = SENSORS_LIMIT(val, 0, 127000);
+
+       mutex_lock(&data->update_lock);
+       temp = tmp401_crit_register_to_temp(data->temp_crit[index],
+                                               data->config);
+       val = SENSORS_LIMIT(val, temp - 255000, temp);
+       reg = ((temp - val) + 500) / 1000;
+
+       i2c_smbus_write_byte_data(to_i2c_client(dev),
+               TMP401_TEMP_CRIT_HYST, reg);
+
+       data->temp_crit_hyst = reg;
+
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+/*
+ * Resets the historical measurements of minimum and maximum temperatures.
+ * This is done by writing any value to any of the minimum/maximum registers
+ * (0x30-0x37).
+ */
+static ssize_t reset_temp_history(struct device *dev,
+       struct device_attribute *devattr, const char *buf, size_t count)
+{
+       long val;
+
+       if (strict_strtol(buf, 10, &val))
+               return -EINVAL;
+
+       if (val != 1) {
+               dev_err(dev, "temp_reset_history value %ld not"
+                       " supported. Use 1 to reset the history!\n", val);
+               return -EINVAL;
+       }
+       i2c_smbus_write_byte_data(to_i2c_client(dev),
+               TMP411_TEMP_LOWEST_MSB[0], val);
+
+       return count;
+}
+
+static struct sensor_device_attribute tmp401_attr[] = {
+       SENSOR_ATTR(temp1_input, 0444, show_temp_value, NULL, 0),
+       SENSOR_ATTR(temp1_min, 0644, show_temp_min, store_temp_min, 0),
+       SENSOR_ATTR(temp1_max, 0644, show_temp_max, store_temp_max, 0),
+       SENSOR_ATTR(temp1_crit, 0644, show_temp_crit, store_temp_crit, 0),
+       SENSOR_ATTR(temp1_crit_hyst, 0644, show_temp_crit_hyst,
+                   store_temp_crit_hyst, 0),
+       SENSOR_ATTR(temp1_min_alarm, 0444, show_status, NULL,
+                   TMP401_STATUS_LOCAL_LOW),
+       SENSOR_ATTR(temp1_max_alarm, 0444, show_status, NULL,
+                   TMP401_STATUS_LOCAL_HIGH),
+       SENSOR_ATTR(temp1_crit_alarm, 0444, show_status, NULL,
+                   TMP401_STATUS_LOCAL_CRIT),
+       SENSOR_ATTR(temp2_input, 0444, show_temp_value, NULL, 1),
+       SENSOR_ATTR(temp2_min, 0644, show_temp_min, store_temp_min, 1),
+       SENSOR_ATTR(temp2_max, 0644, show_temp_max, store_temp_max, 1),
+       SENSOR_ATTR(temp2_crit, 0644, show_temp_crit, store_temp_crit, 1),
+       SENSOR_ATTR(temp2_crit_hyst, 0444, show_temp_crit_hyst, NULL, 1),
+       SENSOR_ATTR(temp2_fault, 0444, show_status, NULL,
+                   TMP401_STATUS_REMOTE_OPEN),
+       SENSOR_ATTR(temp2_min_alarm, 0444, show_status, NULL,
+                   TMP401_STATUS_REMOTE_LOW),
+       SENSOR_ATTR(temp2_max_alarm, 0444, show_status, NULL,
+                   TMP401_STATUS_REMOTE_HIGH),
+       SENSOR_ATTR(temp2_crit_alarm, 0444, show_status, NULL,
+                   TMP401_STATUS_REMOTE_CRIT),
+};
+
+/*
+ * Additional features of the TMP411 chip.
+ * The TMP411 stores the minimum and maximum
+ * temperature measured since power-on, chip-reset, or
+ * minimum and maximum register reset for both the local
+ * and remote channels.
+ */
+static struct sensor_device_attribute tmp411_attr[] = {
+       SENSOR_ATTR(temp1_highest, 0444, show_temp_highest, NULL, 0),
+       SENSOR_ATTR(temp1_lowest, 0444, show_temp_lowest, NULL, 0),
+       SENSOR_ATTR(temp2_highest, 0444, show_temp_highest, NULL, 1),
+       SENSOR_ATTR(temp2_lowest, 0444, show_temp_lowest, NULL, 1),
+       SENSOR_ATTR(temp_reset_history, 0200, NULL, reset_temp_history, 0),
+};
+
+/*
+ * Begin non sysfs callback code (aka Real code)
+ */
+
+static void tmp401_init_client(struct i2c_client *client)
+{
+       int config, config_orig;
+
+       /* Set the conversion rate to 2 Hz */
+       i2c_smbus_write_byte_data(client, TMP401_CONVERSION_RATE_WRITE, 5);
+
+       /* Start conversions (disable shutdown if necessary) */
+       config = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ);
+       if (config < 0) {
+               dev_warn(&client->dev, "Initialization failed!\n");
+               return;
+       }
+
+       config_orig = config;
+       config &= ~TMP401_CONFIG_SHUTDOWN;
+
+       if (config != config_orig)
+               i2c_smbus_write_byte_data(client, TMP401_CONFIG_WRITE, config);
+}
+
+static int tmp401_detect(struct i2c_client *client, int kind,
+                        struct i2c_board_info *info)
+{
+       struct i2c_adapter *adapter = client->adapter;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+               return -ENODEV;
+
+       /* Detect and identify the chip */
+       if (kind <= 0) {
+               u8 reg;
+
+               reg = i2c_smbus_read_byte_data(client,
+                                              TMP401_MANUFACTURER_ID_REG);
+               if (reg != TMP401_MANUFACTURER_ID)
+                       return -ENODEV;
+
+               reg = i2c_smbus_read_byte_data(client, TMP401_DEVICE_ID_REG);
+
+               switch (reg) {
+               case TMP401_DEVICE_ID:
+                       kind = tmp401;
+                       break;
+               case TMP411_DEVICE_ID:
+                       kind = tmp411;
+                       break;
+               default:
+                       return -ENODEV;
+               }
+
+               reg = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ);
+               if (reg & 0x1b)
+                       return -ENODEV;
+
+               reg = i2c_smbus_read_byte_data(client,
+                                              TMP401_CONVERSION_RATE_READ);
+               /* Datasheet says: 0x1-0x6 */
+               if (reg > 15)
+                       return -ENODEV;
+       }
+       strlcpy(info->type, tmp401_id[kind - 1].name, I2C_NAME_SIZE);
+
+       return 0;
+}
+
+static int tmp401_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       int i, err = 0;
+       struct tmp401_data *data;
+       const char *names[] = { "TMP401", "TMP411" };
+
+       data = kzalloc(sizeof(struct tmp401_data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       i2c_set_clientdata(client, data);
+       mutex_init(&data->update_lock);
+       data->kind = id->driver_data;
+
+       /* Initialize the TMP401 chip */
+       tmp401_init_client(client);
+
+       /* Register sysfs hooks */
+       for (i = 0; i < ARRAY_SIZE(tmp401_attr); i++) {
+               err = device_create_file(&client->dev,
+                                        &tmp401_attr[i].dev_attr);
+               if (err)
+                       goto exit_remove;
+       }
+
+       /* Register aditional tmp411 sysfs hooks */
+       if (data->kind == tmp411) {
+               for (i = 0; i < ARRAY_SIZE(tmp411_attr); i++) {
+                       err = device_create_file(&client->dev,
+                                                &tmp411_attr[i].dev_attr);
+                       if (err)
+                               goto exit_remove;
+               }
+       }
+
+       data->hwmon_dev = hwmon_device_register(&client->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
+               data->hwmon_dev = NULL;
+               goto exit_remove;
+       }
+
+       dev_info(&client->dev, "Detected TI %s chip\n",
+                names[data->kind - 1]);
+
+       return 0;
+
+exit_remove:
+       tmp401_remove(client); /* will also free data for us */
+       return err;
+}
+
+static int tmp401_remove(struct i2c_client *client)
+{
+       struct tmp401_data *data = i2c_get_clientdata(client);
+       int i;
+
+       if (data->hwmon_dev)
+               hwmon_device_unregister(data->hwmon_dev);
+
+       for (i = 0; i < ARRAY_SIZE(tmp401_attr); i++)
+               device_remove_file(&client->dev, &tmp401_attr[i].dev_attr);
+
+       if (data->kind == tmp411) {
+               for (i = 0; i < ARRAY_SIZE(tmp411_attr); i++)
+                       device_remove_file(&client->dev,
+                                          &tmp411_attr[i].dev_attr);
+       }
+
+       kfree(data);
+       return 0;
+}
+
+static struct tmp401_data *tmp401_update_device_reg16(
+       struct i2c_client *client, struct tmp401_data *data)
+{
+       int i;
+
+       for (i = 0; i < 2; i++) {
+               /*
+                * High byte must be read first immediately followed
+                * by the low byte
+                */
+               data->temp[i] = i2c_smbus_read_byte_data(client,
+                       TMP401_TEMP_MSB[i]) << 8;
+               data->temp[i] |= i2c_smbus_read_byte_data(client,
+                       TMP401_TEMP_LSB[i]);
+               data->temp_low[i] = i2c_smbus_read_byte_data(client,
+                       TMP401_TEMP_LOW_LIMIT_MSB_READ[i]) << 8;
+               data->temp_low[i] |= i2c_smbus_read_byte_data(client,
+                       TMP401_TEMP_LOW_LIMIT_LSB[i]);
+               data->temp_high[i] = i2c_smbus_read_byte_data(client,
+                       TMP401_TEMP_HIGH_LIMIT_MSB_READ[i]) << 8;
+               data->temp_high[i] |= i2c_smbus_read_byte_data(client,
+                       TMP401_TEMP_HIGH_LIMIT_LSB[i]);
+               data->temp_crit[i] = i2c_smbus_read_byte_data(client,
+                       TMP401_TEMP_CRIT_LIMIT[i]);
+
+               if (data->kind == tmp411) {
+                       data->temp_lowest[i] = i2c_smbus_read_byte_data(client,
+                               TMP411_TEMP_LOWEST_MSB[i]) << 8;
+                       data->temp_lowest[i] |= i2c_smbus_read_byte_data(
+                               client, TMP411_TEMP_LOWEST_LSB[i]);
+
+                       data->temp_highest[i] = i2c_smbus_read_byte_data(
+                               client, TMP411_TEMP_HIGHEST_MSB[i]) << 8;
+                       data->temp_highest[i] |= i2c_smbus_read_byte_data(
+                               client, TMP411_TEMP_HIGHEST_LSB[i]);
+               }
+       }
+       return data;
+}
+
+static struct tmp401_data *tmp401_update_device(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct tmp401_data *data = i2c_get_clientdata(client);
+
+       mutex_lock(&data->update_lock);
+
+       if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+               data->status = i2c_smbus_read_byte_data(client, TMP401_STATUS);
+               data->config = i2c_smbus_read_byte_data(client,
+                                               TMP401_CONFIG_READ);
+               tmp401_update_device_reg16(client, data);
+
+               data->temp_crit_hyst = i2c_smbus_read_byte_data(client,
+                                               TMP401_TEMP_CRIT_HYST);
+
+               data->last_updated = jiffies;
+               data->valid = 1;
+       }
+
+       mutex_unlock(&data->update_lock);
+
+       return data;
+}
+
+static int __init tmp401_init(void)
+{
+       return i2c_add_driver(&tmp401_driver);
+}
+
+static void __exit tmp401_exit(void)
+{
+       i2c_del_driver(&tmp401_driver);
+}
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Texas Instruments TMP401 temperature sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(tmp401_init);
+module_exit(tmp401_exit);
index e64b420..0e97469 100644 (file)
@@ -36,6 +36,7 @@
     w83627ehf   10      5       4       3      0x8850 0x88    0x5ca3
                                                0x8860 0xa1
     w83627dhg    9      5       4       3      0xa020 0xc1    0x5ca3
+    w83627dhg-p  9      5       4       3      0xb070 0xc1    0x5ca3
     w83667hg     9      5       3       3      0xa510 0xc1    0x5ca3
 */
 
 #include <asm/io.h>
 #include "lm75.h"
 
-enum kinds { w83627ehf, w83627dhg, w83667hg };
+enum kinds { w83627ehf, w83627dhg, w83627dhg_p, w83667hg };
 
 /* used to set data->name = w83627ehf_device_names[data->sio_kind] */
 static const char * w83627ehf_device_names[] = {
        "w83627ehf",
        "w83627dhg",
+       "w83627dhg",
        "w83667hg",
 };
 
@@ -86,6 +88,7 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID");
 #define SIO_W83627EHF_ID       0x8850
 #define SIO_W83627EHG_ID       0x8860
 #define SIO_W83627DHG_ID       0xa020
+#define SIO_W83627DHG_P_ID     0xb070
 #define SIO_W83667HG_ID        0xa510
 #define SIO_ID_MASK            0xFFF0
 
@@ -1517,6 +1520,7 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr,
        static const char __initdata sio_name_W83627EHF[] = "W83627EHF";
        static const char __initdata sio_name_W83627EHG[] = "W83627EHG";
        static const char __initdata sio_name_W83627DHG[] = "W83627DHG";
+       static const char __initdata sio_name_W83627DHG_P[] = "W83627DHG-P";
        static const char __initdata sio_name_W83667HG[] = "W83667HG";
 
        u16 val;
@@ -1542,6 +1546,10 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr,
                sio_data->kind = w83627dhg;
                sio_name = sio_name_W83627DHG;
                break;
+       case SIO_W83627DHG_P_ID:
+               sio_data->kind = w83627dhg_p;
+               sio_name = sio_name_W83627DHG_P;
+               break;
        case SIO_W83667HG_ID:
                sio_data->kind = w83667hg;
                sio_name = sio_name_W83667HG;