hwmon: (max16064) Add support for peak attributes
[linux-2.6.git] / drivers / hwmon / f71882fg.c
index aa8cf5d..2d96ed2 100644 (file)
 
 #define SIO_FINTEK_ID          0x1934  /* Manufacturers ID */
 #define SIO_F71808E_ID         0x0901  /* Chipset ID */
+#define SIO_F71808A_ID         0x1001  /* Chipset ID */
 #define SIO_F71858_ID          0x0507  /* Chipset ID */
 #define SIO_F71862_ID          0x0601  /* Chipset ID */
 #define SIO_F71869_ID          0x0814  /* Chipset ID */
+#define SIO_F71869A_ID         0x1007  /* Chipset ID */
 #define SIO_F71882_ID          0x0541  /* Chipset ID */
 #define SIO_F71889_ID          0x0723  /* Chipset ID */
 #define SIO_F71889E_ID         0x0909  /* Chipset ID */
+#define SIO_F71889A_ID         0x1005  /* Chipset ID */
 #define SIO_F8000_ID           0x0581  /* Chipset ID */
 #define SIO_F81865_ID          0x0704  /* Chipset ID */
 
@@ -106,77 +109,110 @@ static unsigned short force_id;
 module_param(force_id, ushort, 0);
 MODULE_PARM_DESC(force_id, "Override the detected device ID");
 
-enum chips { f71808e, f71858fg, f71862fg, f71869, f71882fg, f71889fg,
-            f71889ed, f8000, f81865f };
+enum chips { f71808e, f71808a, f71858fg, f71862fg, f71869, f71869a, f71882fg,
+            f71889fg, f71889ed, f71889a, f8000, f81865f };
 
 static const char *f71882fg_names[] = {
        "f71808e",
+       "f71808a",
        "f71858fg",
        "f71862fg",
        "f71869", /* Both f71869f and f71869e, reg. compatible and same id */
+       "f71869a",
        "f71882fg",
        "f71889fg", /* f81801u too, same id */
        "f71889ed",
+       "f71889a",
        "f8000",
        "f81865f",
 };
 
 static const char f71882fg_has_in[][F71882FG_MAX_INS] = {
        [f71808e]       = { 1, 1, 1, 1, 1, 1, 0, 1, 1 },
+       [f71808a]       = { 1, 1, 1, 1, 0, 0, 0, 1, 1 },
        [f71858fg]      = { 1, 1, 1, 0, 0, 0, 0, 0, 0 },
        [f71862fg]      = { 1, 1, 1, 1, 1, 1, 1, 1, 1 },
        [f71869]        = { 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+       [f71869a]       = { 1, 1, 1, 1, 1, 1, 1, 1, 1 },
        [f71882fg]      = { 1, 1, 1, 1, 1, 1, 1, 1, 1 },
        [f71889fg]      = { 1, 1, 1, 1, 1, 1, 1, 1, 1 },
        [f71889ed]      = { 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+       [f71889a]       = { 1, 1, 1, 1, 1, 1, 1, 1, 1 },
        [f8000]         = { 1, 1, 1, 0, 0, 0, 0, 0, 0 },
        [f81865f]       = { 1, 1, 1, 1, 1, 1, 1, 0, 0 },
 };
 
 static const char f71882fg_has_in1_alarm[] = {
        [f71808e]       = 0,
+       [f71808a]       = 0,
        [f71858fg]      = 0,
        [f71862fg]      = 0,
        [f71869]        = 0,
+       [f71869a]       = 0,
        [f71882fg]      = 1,
        [f71889fg]      = 1,
        [f71889ed]      = 1,
+       [f71889a]       = 1,
        [f8000]         = 0,
        [f81865f]       = 1,
 };
 
-static const char f71882fg_has_beep[] = {
+static const char f71882fg_fan_has_beep[] = {
        [f71808e]       = 0,
+       [f71808a]       = 0,
        [f71858fg]      = 0,
        [f71862fg]      = 1,
        [f71869]        = 1,
+       [f71869a]       = 1,
        [f71882fg]      = 1,
        [f71889fg]      = 1,
        [f71889ed]      = 1,
+       [f71889a]       = 1,
        [f8000]         = 0,
        [f81865f]       = 1,
 };
 
 static const char f71882fg_nr_fans[] = {
        [f71808e]       = 3,
+       [f71808a]       = 2, /* +1 fan which is monitor + simple pwm only */
        [f71858fg]      = 3,
        [f71862fg]      = 3,
        [f71869]        = 3,
+       [f71869a]       = 3,
        [f71882fg]      = 4,
        [f71889fg]      = 3,
        [f71889ed]      = 3,
-       [f8000]         = 3,
+       [f71889a]       = 3,
+       [f8000]         = 3, /* +1 fan which is monitor only */
        [f81865f]       = 2,
 };
 
+static const char f71882fg_temp_has_beep[] = {
+       [f71808e]       = 0,
+       [f71808a]       = 1,
+       [f71858fg]      = 0,
+       [f71862fg]      = 1,
+       [f71869]        = 1,
+       [f71869a]       = 1,
+       [f71882fg]      = 1,
+       [f71889fg]      = 1,
+       [f71889ed]      = 1,
+       [f71889a]       = 1,
+       [f8000]         = 0,
+       [f81865f]       = 1,
+};
+
 static const char f71882fg_nr_temps[] = {
        [f71808e]       = 2,
+       [f71808a]       = 2,
        [f71858fg]      = 3,
        [f71862fg]      = 3,
        [f71869]        = 3,
+       [f71869a]       = 3,
        [f71882fg]      = 3,
        [f71889fg]      = 3,
        [f71889ed]      = 3,
+       [f71889a]       = 3,
        [f8000]         = 3,
        [f81865f]       = 2,
 };
@@ -294,6 +330,10 @@ static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr,
        char *buf);
 static ssize_t store_pwm(struct device *dev, struct device_attribute *devattr,
        const char *buf, size_t count);
+static ssize_t show_simple_pwm(struct device *dev,
+       struct device_attribute *devattr, char *buf);
+static ssize_t store_simple_pwm(struct device *dev,
+       struct device_attribute *devattr, const char *buf, size_t count);
 static ssize_t show_pwm_enable(struct device *dev,
        struct device_attribute *devattr, char *buf);
 static ssize_t store_pwm_enable(struct device *dev,
@@ -543,6 +583,14 @@ static struct sensor_device_attribute_2 fxxxx_fan_attr[4][6] = { {
                      show_pwm_interpolate, store_pwm_interpolate, 0, 3),
 } };
 
+/* Attr for the third fan of the f71808a, which only has manual pwm */
+static struct sensor_device_attribute_2 f71808a_fan3_attr[] = {
+       SENSOR_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 0, 2),
+       SENSOR_ATTR_2(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 2),
+       SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR,
+                     show_simple_pwm, store_simple_pwm, 0, 2),
+};
+
 /* Attr for models which can beep on Fan alarm */
 static struct sensor_device_attribute_2 fxxxx_fan_beep_attr[] = {
        SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep,
@@ -1139,12 +1187,13 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
                        data->temp_type[3] = (reg & 0x08) ? 2 : 4;
                }
 
-               if (f71882fg_has_beep[data->type]) {
+               if (f71882fg_fan_has_beep[data->type])
                        data->fan_beep = f71882fg_read8(data,
                                                F71882FG_REG_FAN_BEEP);
+
+               if (f71882fg_temp_has_beep[data->type])
                        data->temp_beep = f71882fg_read8(data,
                                                F71882FG_REG_TEMP_BEEP);
-               }
 
                data->pwm_enable = f71882fg_read8(data,
                                                  F71882FG_REG_PWM_ENABLE);
@@ -1225,7 +1274,13 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
                        data->pwm[nr] =
                            f71882fg_read8(data, F71882FG_REG_PWM(nr));
                }
-               /* The f8000 can monitor 1 more fan, but has no pwm for it */
+               /* Some models have 1 more fan with limited capabilities */
+               if (data->type == f71808a) {
+                       data->fan[2] = f71882fg_read16(data,
+                                               F71882FG_REG_FAN(2));
+                       data->pwm[2] = f71882fg_read8(data,
+                                                       F71882FG_REG_PWM(2));
+               }
                if (data->type == f8000)
                        data->fan[3] = f71882fg_read16(data,
                                                F71882FG_REG_FAN(3));
@@ -1715,6 +1770,38 @@ leave:
        return count;
 }
 
+static ssize_t show_simple_pwm(struct device *dev,
+                              struct device_attribute *devattr, char *buf)
+{
+       struct f71882fg_data *data = f71882fg_update_device(dev);
+       int val, nr = to_sensor_dev_attr_2(devattr)->index;
+
+       val = data->pwm[nr];
+       return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t store_simple_pwm(struct device *dev,
+                               struct device_attribute *devattr,
+                               const char *buf, size_t count)
+{
+       struct f71882fg_data *data = dev_get_drvdata(dev);
+       int err, nr = to_sensor_dev_attr_2(devattr)->index;
+       long val;
+
+       err = strict_strtol(buf, 10, &val);
+       if (err)
+               return err;
+
+       val = SENSORS_LIMIT(val, 0, 255);
+
+       mutex_lock(&data->update_lock);
+       f71882fg_write8(data, F71882FG_REG_PWM(nr), val);
+       data->pwm[nr] = val;
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
 static ssize_t show_pwm_enable(struct device *dev,
                               struct device_attribute *devattr, char *buf)
 {
@@ -2133,7 +2220,7 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
                if (err)
                        goto exit_unregister_sysfs;
 
-               if (f71882fg_has_beep[data->type]) {
+               if (f71882fg_temp_has_beep[data->type]) {
                        err = f71882fg_create_sysfs_files(pdev,
                                        &fxxxx_temp_beep_attr[0][0],
                                        ARRAY_SIZE(fxxxx_temp_beep_attr[0])
@@ -2162,12 +2249,15 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
        if (start_reg & 0x02) {
                switch (data->type) {
                case f71808e:
+               case f71808a:
                case f71869:
+               case f71869a:
                        /* These always have signed auto point temps */
                        data->auto_point_temp_signed = 1;
                        /* Fall through to select correct fan/pwm reg bank! */
                case f71889fg:
                case f71889ed:
+               case f71889a:
                        reg = f71882fg_read8(data, F71882FG_REG_FAN_FAULT_T);
                        if (reg & F71882FG_FAN_NEG_TEMP_EN)
                                data->auto_point_temp_signed = 1;
@@ -2213,7 +2303,7 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
                if (err)
                        goto exit_unregister_sysfs;
 
-               if (f71882fg_has_beep[data->type]) {
+               if (f71882fg_fan_has_beep[data->type]) {
                        err = f71882fg_create_sysfs_files(pdev,
                                        fxxxx_fan_beep_attr, nr_fans);
                        if (err)
@@ -2222,9 +2312,12 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
 
                switch (data->type) {
                case f71808e:
+               case f71808a:
                case f71869:
+               case f71869a:
                case f71889fg:
                case f71889ed:
+               case f71889a:
                        for (i = 0; i < nr_fans; i++) {
                                data->pwm_auto_point_mapping[i] =
                                        f71882fg_read8(data,
@@ -2246,6 +2339,16 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
                }
 
                switch (data->type) {
+               case f71808a:
+                       err = f71882fg_create_sysfs_files(pdev,
+                               &fxxxx_auto_pwm_attr[0][0],
+                               ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans);
+                       if (err)
+                               goto exit_unregister_sysfs;
+                       err = f71882fg_create_sysfs_files(pdev,
+                                       f71808a_fan3_attr,
+                                       ARRAY_SIZE(f71808a_fan3_attr));
+                       break;
                case f71862fg:
                        err = f71882fg_create_sysfs_files(pdev,
                                        f71862fg_auto_pwm_attr,
@@ -2334,7 +2437,7 @@ static int f71882fg_remove(struct platform_device *pdev)
                                &fxxxx_temp_attr[0][0],
                                ARRAY_SIZE(fxxxx_temp_attr[0]) * nr_temps);
                }
-               if (f71882fg_has_beep[data->type]) {
+               if (f71882fg_temp_has_beep[data->type]) {
                        f71882fg_remove_sysfs_files(pdev,
                               &fxxxx_temp_beep_attr[0][0],
                               ARRAY_SIZE(fxxxx_temp_beep_attr[0]) * nr_temps);
@@ -2357,12 +2460,20 @@ static int f71882fg_remove(struct platform_device *pdev)
                f71882fg_remove_sysfs_files(pdev, &fxxxx_fan_attr[0][0],
                                ARRAY_SIZE(fxxxx_fan_attr[0]) * nr_fans);
 
-               if (f71882fg_has_beep[data->type]) {
+               if (f71882fg_fan_has_beep[data->type]) {
                        f71882fg_remove_sysfs_files(pdev,
                                        fxxxx_fan_beep_attr, nr_fans);
                }
 
                switch (data->type) {
+               case f71808a:
+                       f71882fg_remove_sysfs_files(pdev,
+                               &fxxxx_auto_pwm_attr[0][0],
+                               ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans);
+                       f71882fg_remove_sysfs_files(pdev,
+                                       f71808a_fan3_attr,
+                                       ARRAY_SIZE(f71808a_fan3_attr));
+                       break;
                case f71862fg:
                        f71882fg_remove_sysfs_files(pdev,
                                        f71862fg_auto_pwm_attr,
@@ -2415,6 +2526,9 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address,
        case SIO_F71808E_ID:
                sio_data->type = f71808e;
                break;
+       case SIO_F71808A_ID:
+               sio_data->type = f71808a;
+               break;
        case SIO_F71858_ID:
                sio_data->type = f71858fg;
                break;
@@ -2424,6 +2538,9 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address,
        case SIO_F71869_ID:
                sio_data->type = f71869;
                break;
+       case SIO_F71869A_ID:
+               sio_data->type = f71869a;
+               break;
        case SIO_F71882_ID:
                sio_data->type = f71882fg;
                break;
@@ -2433,6 +2550,9 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address,
        case SIO_F71889E_ID:
                sio_data->type = f71889ed;
                break;
+       case SIO_F71889A_ID:
+               sio_data->type = f71889a;
+               break;
        case SIO_F8000_ID:
                sio_data->type = f8000;
                break;
@@ -2555,7 +2675,7 @@ static void __exit f71882fg_exit(void)
 }
 
 MODULE_DESCRIPTION("F71882FG Hardware Monitoring Driver");
-MODULE_AUTHOR("Hans Edgington, Hans de Goede (hdegoede@redhat.com)");
+MODULE_AUTHOR("Hans Edgington, Hans de Goede <hdegoede@redhat.com>");
 MODULE_LICENSE("GPL");
 
 module_init(f71882fg_init);