hwmon: (max6650) Add support for alarms
Christian Engelmayer [Mon, 15 Jun 2009 16:39:52 +0000 (18:39 +0200)]
Export the alarm flags provided by the MAX6650/MAX6651 fan-speed regulator
and monitor chips via sysfs.

Signed-off-by: Christian Engelmayer <christian.engelmayer@frequentis.com>
Acked-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Jean Delvare <khali@linux-fr.org>

drivers/hwmon/max6650.c

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;
        }