vserver 2.0 rc7
[linux-2.6.git] / drivers / i2c / chips / lm78.c
index 640aa01..6d52d14 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/slab.h>
+#include <linux/jiffies.h>
 #include <linux/i2c.h>
 #include <linux/i2c-sensor.h>
 #include <asm/io.h>
@@ -81,9 +82,8 @@ static inline u8 IN_TO_REG(unsigned long val)
 
 static inline u8 FAN_TO_REG(long rpm, int div)
 {
-       if (rpm == 0)
+       if (rpm <= 0)
                return 255;
-       rpm = SENSORS_LIMIT(rpm, 1, 1000000);
        return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
 }
 
@@ -94,15 +94,15 @@ static inline int FAN_FROM_REG(u8 val, int div)
 
 /* TEMP: mC (-128C to +127C)
    REG: 1C/bit, two's complement */
-static inline u8 TEMP_TO_REG(int val)
+static inline s8 TEMP_TO_REG(int val)
 {
        int nval = SENSORS_LIMIT(val, -128000, 127000) ;
-       return nval<0 ? (nval-500)/1000+0x100 : (nval+500)/1000;
+       return nval<0 ? (nval-500)/1000 : (nval+500)/1000;
 }
 
-static inline int TEMP_FROM_REG(u8 val)
+static inline int TEMP_FROM_REG(s8 val)
 {
-       return (val>=0x80 ? val-0x100 : val) * 1000;
+       return val * 1000;
 }
 
 /* VID: mV
@@ -112,16 +112,6 @@ static inline int VID_FROM_REG(u8 val)
        return val==0x1f ? 0 : val>=0x10 ? 5100-val*100 : 2050-val*50;
 }
 
-/* ALARMS: chip-specific bitmask
-   REG: (same) */
-#define ALARMS_FROM_REG(val) (val)
-
-/* FAN DIV: 1, 2, 4, or 8 (defaults to 2)
-   REG: 0, 1, 2, or 3 (respectively) (defaults to 1) */
-static inline u8 DIV_TO_REG(int val)
-{
-       return val==8 ? 3 : val==4 ? 2 : val==1 ? 0 : 1;
-}
 #define DIV_FROM_REG(val) (1 << (val))
 
 /* There are some complications in a module like this. First off, LM78 chips
@@ -157,9 +147,9 @@ struct lm78_data {
        u8 in_min[7];           /* Register value */
        u8 fan[3];              /* Register value */
        u8 fan_min[3];          /* Register value */
-       u8 temp;                /* Register value */
-       u8 temp_over;           /* Register value */
-       u8 temp_hyst;           /* Register value */
+       s8 temp;                /* Register value */
+       s8 temp_over;           /* Register value */
+       s8 temp_hyst;           /* Register value */
        u8 fan_div[3];          /* Register encoding, shifted right */
        u8 vid;                 /* Register encoding, combined */
        u16 alarms;             /* Register encoding, combined */
@@ -210,8 +200,11 @@ static ssize_t set_in_min(struct device *dev, const char *buf,
        struct i2c_client *client = to_i2c_client(dev);
        struct lm78_data *data = i2c_get_clientdata(client);
        unsigned long val = simple_strtoul(buf, NULL, 10);
+
+       down(&data->update_lock);
        data->in_min[nr] = IN_TO_REG(val);
        lm78_write_value(client, LM78_REG_IN_MIN(nr), data->in_min[nr]);
+       up(&data->update_lock);
        return count;
 }
 
@@ -221,8 +214,11 @@ static ssize_t set_in_max(struct device *dev, const char *buf,
        struct i2c_client *client = to_i2c_client(dev);
        struct lm78_data *data = i2c_get_clientdata(client);
        unsigned long val = simple_strtoul(buf, NULL, 10);
+
+       down(&data->update_lock);
        data->in_max[nr] = IN_TO_REG(val);
        lm78_write_value(client, LM78_REG_IN_MAX(nr), data->in_max[nr]);
+       up(&data->update_lock);
        return count;
 }
        
@@ -285,8 +281,11 @@ static ssize_t set_temp_over(struct device *dev, const char *buf, size_t count)
        struct i2c_client *client = to_i2c_client(dev);
        struct lm78_data *data = i2c_get_clientdata(client);
        long val = simple_strtol(buf, NULL, 10);
+
+       down(&data->update_lock);
        data->temp_over = TEMP_TO_REG(val);
        lm78_write_value(client, LM78_REG_TEMP_OVER, data->temp_over);
+       up(&data->update_lock);
        return count;
 }
 
@@ -301,8 +300,11 @@ static ssize_t set_temp_hyst(struct device *dev, const char *buf, size_t count)
        struct i2c_client *client = to_i2c_client(dev);
        struct lm78_data *data = i2c_get_clientdata(client);
        long val = simple_strtol(buf, NULL, 10);
+
+       down(&data->update_lock);
        data->temp_hyst = TEMP_TO_REG(val);
        lm78_write_value(client, LM78_REG_TEMP_HYST, data->temp_hyst);
+       up(&data->update_lock);
        return count;
 }
 
@@ -333,8 +335,11 @@ static ssize_t set_fan_min(struct device *dev, const char *buf,
        struct i2c_client *client = to_i2c_client(dev);
        struct lm78_data *data = i2c_get_clientdata(client);
        unsigned long val = simple_strtoul(buf, NULL, 10);
+
+       down(&data->update_lock);
        data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
        lm78_write_value(client, LM78_REG_FAN_MIN(nr), data->fan_min[nr]);
+       up(&data->update_lock);
        return count;
 }
 
@@ -353,11 +358,27 @@ static ssize_t set_fan_div(struct device *dev, const char *buf,
 {
        struct i2c_client *client = to_i2c_client(dev);
        struct lm78_data *data = i2c_get_clientdata(client);
-       unsigned long min = FAN_FROM_REG(data->fan_min[nr],
-                       DIV_FROM_REG(data->fan_div[nr]));
        unsigned long val = simple_strtoul(buf, NULL, 10);
-       int reg = lm78_read_value(client, LM78_REG_VID_FANDIV);
-       data->fan_div[nr] = DIV_TO_REG(val);
+       unsigned long min;
+       u8 reg;
+
+       down(&data->update_lock);
+       min = FAN_FROM_REG(data->fan_min[nr],
+                          DIV_FROM_REG(data->fan_div[nr]));
+
+       switch (val) {
+       case 1: data->fan_div[nr] = 0; break;
+       case 2: data->fan_div[nr] = 1; break;
+       case 4: data->fan_div[nr] = 2; break;
+       case 8: data->fan_div[nr] = 3; break;
+       default:
+               dev_err(&client->dev, "fan_div value %ld not "
+                       "supported. Choose one of 1, 2, 4 or 8!\n", val);
+               up(&data->update_lock);
+               return -EINVAL;
+       }
+
+       reg = lm78_read_value(client, LM78_REG_VID_FANDIV);
        switch (nr) {
        case 0:
                reg = (reg & 0xcf) | (data->fan_div[nr] << 4);
@@ -367,9 +388,12 @@ static ssize_t set_fan_div(struct device *dev, const char *buf,
                break;
        }
        lm78_write_value(client, LM78_REG_VID_FANDIV, reg);
+
        data->fan_min[nr] =
                FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
        lm78_write_value(client, LM78_REG_FAN_MIN(nr), data->fan_min[nr]);
+       up(&data->update_lock);
+
        return count;
 }
 
@@ -430,7 +454,7 @@ static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
 static ssize_t show_alarms(struct device *dev, char *buf)
 {
        struct lm78_data *data = lm78_update_device(dev);
-       return sprintf(buf, "%d\n", ALARMS_FROM_REG(data->alarms));
+       return sprintf(buf, "%u\n", data->alarms);
 }
 static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
 
@@ -633,17 +657,15 @@ static int lm78_detach_client(struct i2c_client *client)
 {
        int err;
 
-       /* release ISA region first */
-       if(i2c_is_isa_client(client))
-               release_region(client->addr, LM78_EXTENT);
-
-       /* now it's safe to scrap the rest */
        if ((err = i2c_detach_client(client))) {
                dev_err(&client->dev,
                    "Client deregistration failed, client not detached.\n");
                return err;
        }
 
+       if(i2c_is_isa_client(client))
+               release_region(client->addr, LM78_EXTENT);
+
        kfree(i2c_get_clientdata(client));
 
        return 0;
@@ -653,9 +675,7 @@ static int lm78_detach_client(struct i2c_client *client)
    We don't want to lock the whole ISA bus, so we lock each client
    separately.
    We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks,
-   would slow down the LM78 access and should not be necessary. 
-   There are some ugly typecasts here, but the good new is - they should
-   nowhere else be necessary! */
+   would slow down the LM78 access and should not be necessary.  */
 static int lm78_read_value(struct i2c_client *client, u8 reg)
 {
        int res;
@@ -709,8 +729,8 @@ static struct lm78_data *lm78_update_device(struct device *dev)
 
        down(&data->update_lock);
 
-       if ((jiffies - data->last_updated > HZ + HZ / 2) ||
-           (jiffies < data->last_updated) || !data->valid) {
+       if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
+           || !data->valid) {
 
                dev_dbg(&client->dev, "Starting lm78 update\n");