]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/hwmon/it87.c
it87: Copyright update
[linux-2.6-omap-h63xx.git] / drivers / hwmon / it87.c
index e7f14e61d6f2b1fb052ab42fd6ea5747c571eff8..af5740d5d70fb459588f17cc32ffbcbf9d5342b9 100644 (file)
@@ -5,10 +5,11 @@
     Supports: IT8705F  Super I/O chip w/LPC interface
               IT8712F  Super I/O chip w/LPC interface & SMBus
               IT8716F  Super I/O chip w/LPC interface
+              IT8718F  Super I/O chip w/LPC interface
               Sis950   A clone of the IT8705F
 
     Copyright (C) 2001 Chris Gauthron <chrisg@0-in.com> 
-    Largely inspired by lm78.c of the same package
+    Copyright (C) 2005-2006 Jean Delvare <khali@linux-fr.org>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
-/*
-    djg@pdp8.net David Gesswein 7/18/01
-    Modified to fix bug with not all alarms enabled.
-    Added ability to read battery voltage and select temperature sensor
-    type at module load time.
-*/
-
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/slab.h>
@@ -51,12 +45,13 @@ static unsigned short normal_i2c[] = { 0x2d, I2C_CLIENT_END };
 static unsigned short isa_address;
 
 /* Insmod parameters */
-I2C_CLIENT_INSMOD_3(it87, it8712, it8716);
+I2C_CLIENT_INSMOD_4(it87, it8712, it8716, it8718);
 
 #define        REG     0x2e    /* The register to read/write */
 #define        DEV     0x07    /* Register: Logical device select */
 #define        VAL     0x2f    /* The value to read/write */
 #define PME    0x04    /* The device with the fan registers in it */
+#define GPIO   0x07    /* The device with the IT8718F VID value in it */
 #define        DEVID   0x20    /* Register: Device ID */
 #define        DEVREV  0x22    /* Register: Device Revision */
 
@@ -78,10 +73,10 @@ static int superio_inw(int reg)
 }
 
 static inline void
-superio_select(void)
+superio_select(int ldn)
 {
        outb(DEV, REG);
-       outb(PME, VAL);
+       outb(ldn, VAL);
 }
 
 static inline void
@@ -100,21 +95,27 @@ superio_exit(void)
        outb(0x02, VAL);
 }
 
+/* Logical device 4 registers */
 #define IT8712F_DEVID 0x8712
 #define IT8705F_DEVID 0x8705
 #define IT8716F_DEVID 0x8716
+#define IT8718F_DEVID 0x8718
 #define IT87_ACT_REG  0x30
 #define IT87_BASE_REG 0x60
 
+/* Logical device 7 registers (IT8712F and later) */
+#define IT87_SIO_PINX2_REG     0x2c    /* Pin selection */
+#define IT87_SIO_VID_REG       0xfc    /* VID value */
+
 /* Update battery voltage after every reading if true */
 static int update_vbat;
 
 /* Not all BIOSes properly configure the PWM registers */
 static int fix_pwm_polarity;
 
-/* Chip Type */
-
+/* Values read from Super-I/O config space */
 static u16 chip_type;
+static u8 vid_value;
 
 /* Many IT87 constants specified below */
 
@@ -133,6 +134,8 @@ static u16 chip_type;
 #define IT87_REG_ALARM2        0x02
 #define IT87_REG_ALARM3        0x03
 
+/* The IT8718F has the VID value in a different register, in Super-I/O
+   configuration space. */
 #define IT87_REG_VID           0x0a
 /* Warning: register 0x0b is used for something completely different in
    new chips/revisions. I suspect only 16-bit tachometer mode will work
@@ -198,7 +201,7 @@ static inline u16 FAN16_TO_REG(long rpm)
 static int DIV_TO_REG(int val)
 {
        int answer = 0;
-       while ((val >>= 1) != 0)
+       while (answer < 7 && (val >>= 1))
                answer++;
        return answer;
 }
@@ -220,8 +223,9 @@ struct it87_data {
        unsigned long last_updated;     /* In jiffies */
 
        u8 in[9];               /* Register value */
-       u8 in_max[9];           /* Register value */
-       u8 in_min[9];           /* Register value */
+       u8 in_max[8];           /* Register value */
+       u8 in_min[8];           /* Register value */
+       u8 has_fan;             /* Bitfield, fans enabled */
        u16 fan[3];             /* Register values, possibly combined */
        u16 fan_min[3];         /* Register values, possibly combined */
        u8 temp[3];             /* Register value */
@@ -562,15 +566,15 @@ static ssize_t set_fan_div(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);
-       int i, min[3];
+       unsigned long val = simple_strtoul(buf, NULL, 10);
+       int min;
        u8 old;
 
        mutex_lock(&data->update_lock);
        old = it87_read_value(client, IT87_REG_FAN_DIV);
 
-       for (i = 0; i < 3; i++)
-               min[i] = FAN_FROM_REG(data->fan_min[i], DIV_FROM_REG(data->fan_div[i]));
+       /* Save fan min limit */
+       min = FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr]));
 
        switch (nr) {
        case 0:
@@ -590,10 +594,10 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,
                val |= 0x1 << 6;
        it87_write_value(client, IT87_REG_FAN_DIV, val);
 
-       for (i = 0; i < 3; i++) {
-               data->fan_min[i]=FAN_TO_REG(min[i], DIV_FROM_REG(data->fan_div[i]));
-               it87_write_value(client, IT87_REG_FAN_MIN(i), data->fan_min[i]);
-       }
+       /* Restore fan min limit */
+       data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
+       it87_write_value(client, IT87_REG_FAN_MIN(nr), data->fan_min[nr]);
+
        mutex_unlock(&data->update_lock);
        return count;
 }
@@ -792,10 +796,11 @@ static int __init it87_find(unsigned short *address)
        chip_type = superio_inw(DEVID);
        if (chip_type != IT8712F_DEVID
         && chip_type != IT8716F_DEVID
+        && chip_type != IT8718F_DEVID
         && chip_type != IT8705F_DEVID)
                goto exit;
 
-       superio_select();
+       superio_select(PME);
        if (!(superio_inb(IT87_ACT_REG) & 0x01)) {
                pr_info("it87: Device not activated, skipping\n");
                goto exit;
@@ -811,6 +816,21 @@ static int __init it87_find(unsigned short *address)
        pr_info("it87: Found IT%04xF chip at 0x%x, revision %d\n",
                chip_type, *address, superio_inb(DEVREV) & 0x0f);
 
+       /* Read GPIO config and VID value from LDN 7 (GPIO) */
+       if (chip_type != IT8705F_DEVID) {
+               int reg;
+
+               superio_select(GPIO);
+               if (chip_type == it8718)
+                       vid_value = superio_inb(IT87_SIO_VID_REG);
+
+               reg = superio_inb(IT87_SIO_PINX2_REG);
+               if (reg & (1 << 0))
+                       pr_info("it87: in3 is VCC (+5V)\n");
+               if (reg & (1 << 1))
+                       pr_info("it87: in7 is VCCH (+5V Stand-By)\n");
+       }
+
 exit:
        superio_exit();
        return err;
@@ -879,6 +899,9 @@ static int it87_detect(struct i2c_adapter *adapter, int address, int kind)
                                case IT8716F_DEVID:
                                        kind = it8716;
                                        break;
+                               case IT8718F_DEVID:
+                                       kind = it8718;
+                                       break;
                                }
                        }
                }
@@ -899,6 +922,8 @@ static int it87_detect(struct i2c_adapter *adapter, int address, int kind)
                name = "it8712";
        } else if (kind == it8716) {
                name = "it8716";
+       } else if (kind == it8718) {
+               name = "it8718";
        }
 
        /* Fill in the remaining client fields and put it into the global list */
@@ -967,38 +992,53 @@ static int it87_detect(struct i2c_adapter *adapter, int address, int kind)
        device_create_file(&new_client->dev, &sensor_dev_attr_temp2_type.dev_attr);
        device_create_file(&new_client->dev, &sensor_dev_attr_temp3_type.dev_attr);
 
-       if (data->type == it8716) { /* 16-bit tachometers */
-               device_create_file(&new_client->dev,
-                                  &sensor_dev_attr_fan1_input16.dev_attr);
-               device_create_file(&new_client->dev,
-                                  &sensor_dev_attr_fan2_input16.dev_attr);
-               device_create_file(&new_client->dev,
-                                  &sensor_dev_attr_fan3_input16.dev_attr);
-               device_create_file(&new_client->dev,
-                                  &sensor_dev_attr_fan1_min16.dev_attr);
-               device_create_file(&new_client->dev,
-                                  &sensor_dev_attr_fan2_min16.dev_attr);
-               device_create_file(&new_client->dev,
-                                  &sensor_dev_attr_fan3_min16.dev_attr);
+       /* Do not create fan files for disabled fans */
+       if (data->type == it8716 || data->type == it8718) {
+               /* 16-bit tachometers */
+               if (data->has_fan & (1 << 0)) {
+                       device_create_file(&new_client->dev,
+                               &sensor_dev_attr_fan1_input16.dev_attr);
+                       device_create_file(&new_client->dev,
+                               &sensor_dev_attr_fan1_min16.dev_attr);
+               }
+               if (data->has_fan & (1 << 1)) {
+                       device_create_file(&new_client->dev,
+                               &sensor_dev_attr_fan2_input16.dev_attr);
+                       device_create_file(&new_client->dev,
+                               &sensor_dev_attr_fan2_min16.dev_attr);
+               }
+               if (data->has_fan & (1 << 2)) {
+                       device_create_file(&new_client->dev,
+                               &sensor_dev_attr_fan3_input16.dev_attr);
+                       device_create_file(&new_client->dev,
+                               &sensor_dev_attr_fan3_min16.dev_attr);
+               }
        } else {
-               device_create_file(&new_client->dev,
-                                  &sensor_dev_attr_fan1_input.dev_attr);
-               device_create_file(&new_client->dev,
-                                  &sensor_dev_attr_fan2_input.dev_attr);
-               device_create_file(&new_client->dev,
-                                  &sensor_dev_attr_fan3_input.dev_attr);
-               device_create_file(&new_client->dev,
-                                  &sensor_dev_attr_fan1_min.dev_attr);
-               device_create_file(&new_client->dev,
-                                  &sensor_dev_attr_fan2_min.dev_attr);
-               device_create_file(&new_client->dev,
-                                  &sensor_dev_attr_fan3_min.dev_attr);
-               device_create_file(&new_client->dev,
-                                  &sensor_dev_attr_fan1_div.dev_attr);
-               device_create_file(&new_client->dev,
-                                  &sensor_dev_attr_fan2_div.dev_attr);
-               device_create_file(&new_client->dev,
-                                  &sensor_dev_attr_fan3_div.dev_attr);
+               /* 8-bit tachometers with clock divider */
+               if (data->has_fan & (1 << 0)) {
+                       device_create_file(&new_client->dev,
+                               &sensor_dev_attr_fan1_input.dev_attr);
+                       device_create_file(&new_client->dev,
+                               &sensor_dev_attr_fan1_min.dev_attr);
+                       device_create_file(&new_client->dev,
+                               &sensor_dev_attr_fan1_div.dev_attr);
+               }
+               if (data->has_fan & (1 << 1)) {
+                       device_create_file(&new_client->dev,
+                               &sensor_dev_attr_fan2_input.dev_attr);
+                       device_create_file(&new_client->dev,
+                               &sensor_dev_attr_fan2_min.dev_attr);
+                       device_create_file(&new_client->dev,
+                               &sensor_dev_attr_fan2_div.dev_attr);
+               }
+               if (data->has_fan & (1 << 2)) {
+                       device_create_file(&new_client->dev,
+                               &sensor_dev_attr_fan3_input.dev_attr);
+                       device_create_file(&new_client->dev,
+                               &sensor_dev_attr_fan3_min.dev_attr);
+                       device_create_file(&new_client->dev,
+                               &sensor_dev_attr_fan3_div.dev_attr);
+               }
        }
 
        device_create_file(&new_client->dev, &dev_attr_alarms);
@@ -1011,8 +1051,11 @@ static int it87_detect(struct i2c_adapter *adapter, int address, int kind)
                device_create_file(&new_client->dev, &sensor_dev_attr_pwm3.dev_attr);
        }
 
-       if (data->type == it8712 || data->type == it8716) {
+       if (data->type == it8712 || data->type == it8716
+        || data->type == it8718) {
                data->vrm = vid_which_vrm();
+               /* VID reading from Super-I/O config space if available */
+               data->vid = vid_value;
                device_create_file_vrm(new_client);
                device_create_file_vid(new_client);
        }
@@ -1152,6 +1195,22 @@ static void it87_init_client(struct i2c_client *client, struct it87_data *data)
                data->manual_pwm_ctl[i] = 0xff;
        }
 
+       /* Some chips seem to have default value 0xff for all limit
+        * registers. For low voltage limits it makes no sense and triggers
+        * alarms, so change to 0 instead. For high temperature limits, it
+        * means -1 degree C, which surprisingly doesn't trigger an alarm,
+        * but is still confusing, so change to 127 degrees C. */
+       for (i = 0; i < 8; i++) {
+               tmp = it87_read_value(client, IT87_REG_VIN_MIN(i));
+               if (tmp == 0xff)
+                       it87_write_value(client, IT87_REG_VIN_MIN(i), 0);
+       }
+       for (i = 0; i < 3; i++) {
+               tmp = it87_read_value(client, IT87_REG_TEMP_HIGH(i));
+               if (tmp == 0xff)
+                       it87_write_value(client, IT87_REG_TEMP_HIGH(i), 127);
+       }
+
        /* Check if temperature channnels are reset manually or by some reason */
        tmp = it87_read_value(client, IT87_REG_TEMP_ENABLE);
        if ((tmp & 0x3f) == 0) {
@@ -1175,11 +1234,12 @@ static void it87_init_client(struct i2c_client *client, struct it87_data *data)
                data->fan_main_ctrl |= 0x70;
                it87_write_value(client, IT87_REG_FAN_MAIN_CTRL, data->fan_main_ctrl);
        }
+       data->has_fan = (data->fan_main_ctrl >> 4) & 0x07;
 
        /* Set tachometers to 16-bit mode if needed */
-       if (data->type == it8716) {
+       if (data->type == it8716 || data->type == it8718) {
                tmp = it87_read_value(client, IT87_REG_FAN_16BIT);
-               if ((tmp & 0x07) != 0x07) {
+               if (~tmp & 0x07 & data->has_fan) {
                        dev_dbg(&client->dev,
                                "Setting fan1-3 to 16-bit mode\n");
                        it87_write_value(client, IT87_REG_FAN_16BIT,
@@ -1236,20 +1296,21 @@ static struct it87_data *it87_update_device(struct device *dev)
                        data->in_max[i] =
                            it87_read_value(client, IT87_REG_VIN_MAX(i));
                }
+               /* in8 (battery) has no limit registers */
                data->in[8] =
                    it87_read_value(client, IT87_REG_VIN(8));
-               /* Temperature sensor doesn't have limit registers, set
-                  to min and max value */
-               data->in_min[8] = 0;
-               data->in_max[8] = 255;
 
                for (i = 0; i < 3; i++) {
+                       /* Skip disabled fans */
+                       if (!(data->has_fan & (1 << i)))
+                               continue;
+
                        data->fan_min[i] =
                            it87_read_value(client, IT87_REG_FAN_MIN(i));
                        data->fan[i] = it87_read_value(client,
                                       IT87_REG_FAN(i));
                        /* Add high byte if in 16-bit mode */
-                       if (data->type == it8716) {
+                       if (data->type == it8716 || data->type == it8718) {
                                data->fan[i] |= it87_read_value(client,
                                                IT87_REG_FANX(i)) << 8;
                                data->fan_min[i] |= it87_read_value(client,
@@ -1266,7 +1327,8 @@ static struct it87_data *it87_update_device(struct device *dev)
                }
 
                /* Newer chips don't have clock dividers */
-               if (data->type != it8716) {
+               if ((data->has_fan & 0x07) && data->type != it8716
+                && data->type != it8718) {
                        i = it87_read_value(client, IT87_REG_FAN_DIV);
                        data->fan_div[0] = i & 0x07;
                        data->fan_div[1] = (i >> 3) & 0x07;
@@ -1323,8 +1385,9 @@ static void __exit sm_it87_exit(void)
 }
 
 
-MODULE_AUTHOR("Chris Gauthron <chrisg@0-in.com>");
-MODULE_DESCRIPTION("IT8705F/8712F/8716F, SiS950 driver");
+MODULE_AUTHOR("Chris Gauthron <chrisg@0-in.com>, "
+             "Jean Delvare <khali@linux-fr.org>");
+MODULE_DESCRIPTION("IT8705F/8712F/8716F/8718F, SiS950 driver");
 module_param(update_vbat, bool, 0);
 MODULE_PARM_DESC(update_vbat, "Update vbat if set else return powerup value");
 module_param(fix_pwm_polarity, bool, 0);