hwmon: (it87) Support automatic pwm control on newer chips
authorGuenter Roeck <linux@roeck-us.net>
Sun, 5 Apr 2015 18:53:29 +0000 (11:53 -0700)
committerGuenter Roeck <linux@roeck-us.net>
Tue, 19 Apr 2016 13:32:37 +0000 (06:32 -0700)
Tested-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
drivers/hwmon/it87.c

index 5adb269..e3fbcf4 100644 (file)
@@ -252,8 +252,10 @@ static const u8 IT87_REG_VIN[]     = { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26,
 
 #define IT87_REG_CHIPID        0x58
 
-#define IT87_REG_AUTO_TEMP(nr, i) (0x60 + (nr) * 8 + (i))
-#define IT87_REG_AUTO_PWM(nr, i)  (0x65 + (nr) * 8 + (i))
+static const u8 IT87_REG_AUTO_BASE[] = { 0x60, 0x68, 0x70, 0x78, 0xa0, 0xa8 };
+
+#define IT87_REG_AUTO_TEMP(nr, i) (IT87_REG_AUTO_BASE[nr] + (i))
+#define IT87_REG_AUTO_PWM(nr, i)  (IT87_REG_AUTO_BASE[nr] + 5 + (i))
 
 #define IT87_REG_TEMP456_ENABLE        0x77
 
@@ -673,6 +675,30 @@ static void it87_update_pwm_ctrl(struct it87_data *data, int nr)
                for (i = 0; i < 3 ; i++)
                        data->auto_pwm[nr][i] = it87_read_value(data,
                                                IT87_REG_AUTO_PWM(nr, i));
+       } else if (has_newer_autopwm(data)) {
+               int i;
+
+               /*
+                * 0: temperature hysteresis (base + 5)
+                * 1: fan off temperature (base + 0)
+                * 2: fan start temperature (base + 1)
+                * 3: fan max temperature (base + 2)
+                */
+               data->auto_temp[nr][0] =
+                       it87_read_value(data, IT87_REG_AUTO_TEMP(nr, 5));
+
+               for (i = 0; i < 3 ; i++)
+                       data->auto_temp[nr][i + 1] =
+                               it87_read_value(data,
+                                               IT87_REG_AUTO_TEMP(nr, i));
+               /*
+                * 0: start pwm value (base + 3)
+                * 1: pwm slope (base + 4, 1/8th pwm)
+                */
+               data->auto_pwm[nr][0] =
+                       it87_read_value(data, IT87_REG_AUTO_TEMP(nr, 3));
+               data->auto_pwm[nr][1] =
+                       it87_read_value(data, IT87_REG_AUTO_TEMP(nr, 4));
        }
 }
 
@@ -1216,6 +1242,11 @@ static int check_trip_points(struct device *dev, int nr)
                        if (data->auto_pwm[nr][i] > data->auto_pwm[nr][i + 1])
                                err = -EINVAL;
                }
+       } else if (has_newer_autopwm(data)) {
+               for (i = 1; i < 3; i++) {
+                       if (data->auto_temp[nr][i] > data->auto_temp[nr][i + 1])
+                               err = -EINVAL;
+               }
        }
 
        if (err) {
@@ -1441,6 +1472,7 @@ static ssize_t set_auto_pwm(struct device *dev, struct device_attribute *attr,
                        to_sensor_dev_attr_2(attr);
        int nr = sensor_attr->nr;
        int point = sensor_attr->index;
+       int regaddr;
        long val;
 
        if (kstrtol(buf, 10, &val) < 0 || val < 0 || val > 255)
@@ -1448,8 +1480,41 @@ static ssize_t set_auto_pwm(struct device *dev, struct device_attribute *attr,
 
        mutex_lock(&data->update_lock);
        data->auto_pwm[nr][point] = pwm_to_reg(data, val);
-       it87_write_value(data, IT87_REG_AUTO_PWM(nr, point),
-                        data->auto_pwm[nr][point]);
+       if (has_newer_autopwm(data))
+               regaddr = IT87_REG_AUTO_TEMP(nr, 3);
+       else
+               regaddr = IT87_REG_AUTO_PWM(nr, point);
+       it87_write_value(data, regaddr, data->auto_pwm[nr][point]);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static ssize_t show_auto_pwm_slope(struct device *dev,
+                                  struct device_attribute *attr, char *buf)
+{
+       struct it87_data *data = it87_update_device(dev);
+       struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+       int nr = sensor_attr->index;
+
+       return sprintf(buf, "%d\n", data->auto_pwm[nr][1] & 0x7f);
+}
+
+static ssize_t set_auto_pwm_slope(struct device *dev,
+                                 struct device_attribute *attr,
+                                 const char *buf, size_t count)
+{
+       struct it87_data *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+       int nr = sensor_attr->index;
+       unsigned long val;
+
+       if (kstrtoul(buf, 10, &val) < 0 || val > 127)
+               return -EINVAL;
+
+       mutex_lock(&data->update_lock);
+       data->auto_pwm[nr][1] = (data->auto_pwm[nr][1] & 0x80) | val;
+       it87_write_value(data, IT87_REG_AUTO_TEMP(nr, 4),
+                        data->auto_pwm[nr][1]);
        mutex_unlock(&data->update_lock);
        return count;
 }
@@ -1462,8 +1527,14 @@ static ssize_t show_auto_temp(struct device *dev, struct device_attribute *attr,
                        to_sensor_dev_attr_2(attr);
        int nr = sensor_attr->nr;
        int point = sensor_attr->index;
+       int reg;
+
+       if (has_old_autopwm(data) || point)
+               reg = data->auto_temp[nr][point];
+       else
+               reg = data->auto_temp[nr][1] - (data->auto_temp[nr][0] & 0x1f);
 
-       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->auto_temp[nr][point]));
+       return sprintf(buf, "%d\n", TEMP_FROM_REG(reg));
 }
 
 static ssize_t set_auto_temp(struct device *dev, struct device_attribute *attr,
@@ -1475,14 +1546,24 @@ static ssize_t set_auto_temp(struct device *dev, struct device_attribute *attr,
        int nr = sensor_attr->nr;
        int point = sensor_attr->index;
        long val;
+       int reg;
 
        if (kstrtol(buf, 10, &val) < 0 || val < -128000 || val > 127000)
                return -EINVAL;
 
        mutex_lock(&data->update_lock);
-       data->auto_temp[nr][point] = TEMP_TO_REG(val);
-       it87_write_value(data, IT87_REG_AUTO_TEMP(nr, point),
-                        data->auto_temp[nr][point]);
+       if (has_newer_autopwm(data) && !point) {
+               reg = data->auto_temp[nr][1] - TEMP_TO_REG(val);
+               reg = clamp_val(reg, 0, 0x1f) | (data->auto_temp[nr][0] & 0xe0);
+               data->auto_temp[nr][0] = reg;
+               it87_write_value(data, IT87_REG_AUTO_TEMP(nr, 5), reg);
+       } else {
+               reg = TEMP_TO_REG(val);
+               data->auto_temp[nr][point] = reg;
+               if (has_newer_autopwm(data))
+                       point--;
+               it87_write_value(data, IT87_REG_AUTO_TEMP(nr, point), reg);
+       }
        mutex_unlock(&data->update_lock);
        return count;
 }
@@ -1542,6 +1623,10 @@ static SENSOR_DEVICE_ATTR_2(pwm1_auto_point3_temp, S_IRUGO | S_IWUSR,
                            show_auto_temp, set_auto_temp, 0, 3);
 static SENSOR_DEVICE_ATTR_2(pwm1_auto_point4_temp, S_IRUGO | S_IWUSR,
                            show_auto_temp, set_auto_temp, 0, 4);
+static SENSOR_DEVICE_ATTR_2(pwm1_auto_start, S_IRUGO | S_IWUSR,
+                           show_auto_pwm, set_auto_pwm, 0, 0);
+static SENSOR_DEVICE_ATTR(pwm1_auto_slope, S_IRUGO | S_IWUSR,
+                         show_auto_pwm_slope, set_auto_pwm_slope, 0);
 
 static SENSOR_DEVICE_ATTR(pwm2_enable, S_IRUGO | S_IWUSR,
                          show_pwm_enable, set_pwm_enable, 1);
@@ -1567,6 +1652,10 @@ static SENSOR_DEVICE_ATTR_2(pwm2_auto_point3_temp, S_IRUGO | S_IWUSR,
                            show_auto_temp, set_auto_temp, 1, 3);
 static SENSOR_DEVICE_ATTR_2(pwm2_auto_point4_temp, S_IRUGO | S_IWUSR,
                            show_auto_temp, set_auto_temp, 1, 4);
+static SENSOR_DEVICE_ATTR_2(pwm2_auto_start, S_IRUGO | S_IWUSR,
+                           show_auto_pwm, set_auto_pwm, 1, 0);
+static SENSOR_DEVICE_ATTR(pwm2_auto_slope, S_IRUGO | S_IWUSR,
+                         show_auto_pwm_slope, set_auto_pwm_slope, 1);
 
 static SENSOR_DEVICE_ATTR(pwm3_enable, S_IRUGO | S_IWUSR,
                          show_pwm_enable, set_pwm_enable, 2);
@@ -1592,6 +1681,10 @@ static SENSOR_DEVICE_ATTR_2(pwm3_auto_point3_temp, S_IRUGO | S_IWUSR,
                            show_auto_temp, set_auto_temp, 2, 3);
 static SENSOR_DEVICE_ATTR_2(pwm3_auto_point4_temp, S_IRUGO | S_IWUSR,
                            show_auto_temp, set_auto_temp, 2, 4);
+static SENSOR_DEVICE_ATTR_2(pwm3_auto_start, S_IRUGO | S_IWUSR,
+                           show_auto_pwm, set_auto_pwm, 2, 0);
+static SENSOR_DEVICE_ATTR(pwm3_auto_slope, S_IRUGO | S_IWUSR,
+                         show_auto_pwm_slope, set_auto_pwm_slope, 2);
 
 static SENSOR_DEVICE_ATTR(pwm4_enable, S_IRUGO | S_IWUSR,
                          show_pwm_enable, set_pwm_enable, 3);
@@ -1599,6 +1692,18 @@ static SENSOR_DEVICE_ATTR(pwm4, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 3);
 static SENSOR_DEVICE_ATTR(pwm4_freq, S_IRUGO, show_pwm_freq, NULL, 3);
 static SENSOR_DEVICE_ATTR(pwm4_auto_channels_temp, S_IRUGO,
                          show_pwm_temp_map, set_pwm_temp_map, 3);
+static SENSOR_DEVICE_ATTR_2(pwm4_auto_point1_temp, S_IRUGO | S_IWUSR,
+                           show_auto_temp, set_auto_temp, 2, 1);
+static SENSOR_DEVICE_ATTR_2(pwm4_auto_point1_temp_hyst, S_IRUGO | S_IWUSR,
+                           show_auto_temp, set_auto_temp, 2, 0);
+static SENSOR_DEVICE_ATTR_2(pwm4_auto_point2_temp, S_IRUGO | S_IWUSR,
+                           show_auto_temp, set_auto_temp, 2, 2);
+static SENSOR_DEVICE_ATTR_2(pwm4_auto_point3_temp, S_IRUGO | S_IWUSR,
+                           show_auto_temp, set_auto_temp, 2, 3);
+static SENSOR_DEVICE_ATTR_2(pwm4_auto_start, S_IRUGO | S_IWUSR,
+                           show_auto_pwm, set_auto_pwm, 3, 0);
+static SENSOR_DEVICE_ATTR(pwm4_auto_slope, S_IRUGO | S_IWUSR,
+                         show_auto_pwm_slope, set_auto_pwm_slope, 3);
 
 static SENSOR_DEVICE_ATTR(pwm5_enable, S_IRUGO | S_IWUSR,
                          show_pwm_enable, set_pwm_enable, 4);
@@ -1606,6 +1711,18 @@ static SENSOR_DEVICE_ATTR(pwm5, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 4);
 static SENSOR_DEVICE_ATTR(pwm5_freq, S_IRUGO, show_pwm_freq, NULL, 4);
 static SENSOR_DEVICE_ATTR(pwm5_auto_channels_temp, S_IRUGO,
                          show_pwm_temp_map, set_pwm_temp_map, 4);
+static SENSOR_DEVICE_ATTR_2(pwm5_auto_point1_temp, S_IRUGO | S_IWUSR,
+                           show_auto_temp, set_auto_temp, 2, 1);
+static SENSOR_DEVICE_ATTR_2(pwm5_auto_point1_temp_hyst, S_IRUGO | S_IWUSR,
+                           show_auto_temp, set_auto_temp, 2, 0);
+static SENSOR_DEVICE_ATTR_2(pwm5_auto_point2_temp, S_IRUGO | S_IWUSR,
+                           show_auto_temp, set_auto_temp, 2, 2);
+static SENSOR_DEVICE_ATTR_2(pwm5_auto_point3_temp, S_IRUGO | S_IWUSR,
+                           show_auto_temp, set_auto_temp, 2, 3);
+static SENSOR_DEVICE_ATTR_2(pwm5_auto_start, S_IRUGO | S_IWUSR,
+                           show_auto_pwm, set_auto_pwm, 4, 0);
+static SENSOR_DEVICE_ATTR(pwm5_auto_slope, S_IRUGO | S_IWUSR,
+                         show_auto_pwm_slope, set_auto_pwm_slope, 4);
 
 static SENSOR_DEVICE_ATTR(pwm6_enable, S_IRUGO | S_IWUSR,
                          show_pwm_enable, set_pwm_enable, 5);
@@ -1613,6 +1730,18 @@ static SENSOR_DEVICE_ATTR(pwm6, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 5);
 static SENSOR_DEVICE_ATTR(pwm6_freq, S_IRUGO, show_pwm_freq, NULL, 5);
 static SENSOR_DEVICE_ATTR(pwm6_auto_channels_temp, S_IRUGO,
                          show_pwm_temp_map, set_pwm_temp_map, 5);
+static SENSOR_DEVICE_ATTR_2(pwm6_auto_point1_temp, S_IRUGO | S_IWUSR,
+                           show_auto_temp, set_auto_temp, 2, 1);
+static SENSOR_DEVICE_ATTR_2(pwm6_auto_point1_temp_hyst, S_IRUGO | S_IWUSR,
+                           show_auto_temp, set_auto_temp, 2, 0);
+static SENSOR_DEVICE_ATTR_2(pwm6_auto_point2_temp, S_IRUGO | S_IWUSR,
+                           show_auto_temp, set_auto_temp, 2, 2);
+static SENSOR_DEVICE_ATTR_2(pwm6_auto_point3_temp, S_IRUGO | S_IWUSR,
+                           show_auto_temp, set_auto_temp, 2, 3);
+static SENSOR_DEVICE_ATTR_2(pwm6_auto_start, S_IRUGO | S_IWUSR,
+                           show_auto_pwm, set_auto_pwm, 5, 0);
+static SENSOR_DEVICE_ATTR(pwm6_auto_slope, S_IRUGO | S_IWUSR,
+                         show_auto_pwm_slope, set_auto_pwm_slope, 5);
 
 /* Alarms */
 static ssize_t show_alarms(struct device *dev, struct device_attribute *attr,
@@ -2050,8 +2179,8 @@ static umode_t it87_pwm_is_visible(struct kobject *kobj,
        if (!(data->has_pwm & BIT(i)))
                return 0;
 
-       /* pwmX_auto_channels_temp is only writable for old auto pwm */
-       if (a == 3 && has_old_autopwm(data))
+       /* pwmX_auto_channels_temp is only writable if auto pwm is supported */
+       if (a == 3 && (has_old_autopwm(data) || has_newer_autopwm(data)))
                return attr->mode | S_IWUSR;
 
        /* pwm2_freq is writable if there are two pwm frequency selects */
@@ -2105,11 +2234,28 @@ static umode_t it87_auto_pwm_is_visible(struct kobject *kobj,
 {
        struct device *dev = container_of(kobj, struct device, kobj);
        struct it87_data *data = dev_get_drvdata(dev);
-       int i = index / 9;      /* pwm index */
+       int i = index / 11;     /* pwm index */
+       int a = index % 11;     /* attribute index */
+
+       if (index >= 33) {      /* pwm 4..6 */
+               i = (index - 33) / 6 + 3;
+               a = (index - 33) % 6 + 4;
+       }
 
        if (!(data->has_pwm & BIT(i)))
                return 0;
 
+       if (has_newer_autopwm(data)) {
+               if (a < 4)      /* no auto point pwm */
+                       return 0;
+               if (a == 8)     /* no auto_point4 */
+                       return 0;
+       }
+       if (has_old_autopwm(data)) {
+               if (a >= 9)     /* no pwm_auto_start, pwm_auto_slope */
+                       return 0;
+       }
+
        return attr->mode;
 }
 
@@ -2123,8 +2269,10 @@ static struct attribute *it87_attributes_auto_pwm[] = {
        &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr,
        &sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr,
        &sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm1_auto_start.dev_attr.attr,
+       &sensor_dev_attr_pwm1_auto_slope.dev_attr.attr,
 
-       &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,    /* 11 */
        &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr,
        &sensor_dev_attr_pwm2_auto_point3_pwm.dev_attr.attr,
        &sensor_dev_attr_pwm2_auto_point4_pwm.dev_attr.attr,
@@ -2133,8 +2281,10 @@ static struct attribute *it87_attributes_auto_pwm[] = {
        &sensor_dev_attr_pwm2_auto_point2_temp.dev_attr.attr,
        &sensor_dev_attr_pwm2_auto_point3_temp.dev_attr.attr,
        &sensor_dev_attr_pwm2_auto_point4_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm2_auto_start.dev_attr.attr,
+       &sensor_dev_attr_pwm2_auto_slope.dev_attr.attr,
 
-       &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr,    /* 22 */
        &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr,
        &sensor_dev_attr_pwm3_auto_point3_pwm.dev_attr.attr,
        &sensor_dev_attr_pwm3_auto_point4_pwm.dev_attr.attr,
@@ -2143,6 +2293,29 @@ static struct attribute *it87_attributes_auto_pwm[] = {
        &sensor_dev_attr_pwm3_auto_point2_temp.dev_attr.attr,
        &sensor_dev_attr_pwm3_auto_point3_temp.dev_attr.attr,
        &sensor_dev_attr_pwm3_auto_point4_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm3_auto_start.dev_attr.attr,
+       &sensor_dev_attr_pwm3_auto_slope.dev_attr.attr,
+
+       &sensor_dev_attr_pwm4_auto_point1_temp.dev_attr.attr,   /* 33 */
+       &sensor_dev_attr_pwm4_auto_point1_temp_hyst.dev_attr.attr,
+       &sensor_dev_attr_pwm4_auto_point2_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm4_auto_point3_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm4_auto_start.dev_attr.attr,
+       &sensor_dev_attr_pwm4_auto_slope.dev_attr.attr,
+
+       &sensor_dev_attr_pwm5_auto_point1_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm5_auto_point1_temp_hyst.dev_attr.attr,
+       &sensor_dev_attr_pwm5_auto_point2_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm5_auto_point3_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm5_auto_start.dev_attr.attr,
+       &sensor_dev_attr_pwm5_auto_slope.dev_attr.attr,
+
+       &sensor_dev_attr_pwm6_auto_point1_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm6_auto_point1_temp_hyst.dev_attr.attr,
+       &sensor_dev_attr_pwm6_auto_point2_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm6_auto_point3_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm6_auto_start.dev_attr.attr,
+       &sensor_dev_attr_pwm6_auto_slope.dev_attr.attr,
 
        NULL,
 };
@@ -2839,7 +3012,7 @@ static int it87_probe(struct platform_device *pdev)
                data->has_pwm &= ~sio_data->skip_pwm;
 
                data->groups[4] = &it87_group_pwm;
-               if (has_old_autopwm(data))
+               if (has_old_autopwm(data) || has_newer_autopwm(data))
                        data->groups[5] = &it87_group_auto_pwm;
        }