drm/nouveau/therm: add support for fan-control modes
authorMartin Peres <martin.peres@labri.fr>
Tue, 4 Sep 2012 11:52:00 +0000 (13:52 +0200)
committerBen Skeggs <bskeggs@redhat.com>
Wed, 3 Oct 2012 03:13:15 +0000 (13:13 +1000)
For now, only 2 control modes are available:
- NONE: The fan is never touched (default)
- MANUAL: The fan is set to the user-defined fan speed (pwm1)

This patch introduces a distinction between ptherm internal fan management
and external fan management. The latter is bound to respect the fan mode
while the first can still select the speed it wants unless the NONE mode
is selected. This is important for automatic fan management.

Signed-off-by: Martin Peres <martin.peres@labri.fr>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/core/subdev/therm/base.c
drivers/gpu/drm/nouveau/core/subdev/therm/fan.c
drivers/gpu/drm/nouveau/core/subdev/therm/nv40.c
drivers/gpu/drm/nouveau/core/subdev/therm/nv50.c
drivers/gpu/drm/nouveau/core/subdev/therm/priv.h
drivers/gpu/drm/nouveau/nouveau_pm.c

index 6502dfb95dd65de3a1e6757a9154e4bbd1cf74cd..1674c74a76c8815789ec85b7dd340f7219e55975 100644 (file)
@@ -40,6 +40,8 @@ nouveau_therm_attr_get(struct nouveau_therm *therm,
                return priv->bios_fan.min_duty;
        case NOUVEAU_THERM_ATTR_FAN_MAX_DUTY:
                return priv->bios_fan.max_duty;
+       case NOUVEAU_THERM_ATTR_FAN_MODE:
+               return priv->fan.mode;
        case NOUVEAU_THERM_ATTR_THRS_FAN_BOOST:
                return priv->bios_sensor.thrs_fan_boost.temp;
        case NOUVEAU_THERM_ATTR_THRS_FAN_BOOST_HYST:
@@ -82,6 +84,8 @@ nouveau_therm_attr_set(struct nouveau_therm *therm,
                        value = priv->bios_fan.min_duty;
                priv->bios_fan.max_duty = value;
                return 0;
+       case NOUVEAU_THERM_ATTR_FAN_MODE:
+               return nouveau_therm_fan_set_mode(therm, value);
        case NOUVEAU_THERM_ATTR_THRS_FAN_BOOST:
                priv->bios_sensor.thrs_fan_boost.temp = value;
                return 0;
index 409b95d5b679eb4d4695aeafff5c365f4e0deb42..b29237970fa082ba3cdc5dcd9497e5d7bbe0714b 100644 (file)
@@ -69,6 +69,9 @@ nouveau_therm_fan_set(struct nouveau_therm *therm, int percent)
        u32 divs, duty;
        int ret;
 
+       if (priv->fan.mode == FAN_CONTROL_NONE)
+               return -EINVAL;
+
        if (!priv->fan.pwm_set)
                return -ENODEV;
 
@@ -138,7 +141,52 @@ nouveau_therm_fan_sense(struct nouveau_therm *therm)
                return 0;
 }
 
-static void
+int
+nouveau_therm_fan_set_mode(struct nouveau_therm *therm,
+                          enum nouveau_therm_fan_mode mode)
+{
+       struct nouveau_therm_priv *priv = (void *)therm;
+
+       if (priv->fan.mode == mode)
+               return 0;
+
+       if (mode < FAN_CONTROL_NONE || mode >= FAN_CONTROL_NR)
+               return -EINVAL;
+
+       switch (mode)
+       {
+       case FAN_CONTROL_NONE:
+               nv_info(therm, "switch fan to no-control mode\n");
+               break;
+       case FAN_CONTROL_MANUAL:
+               nv_info(therm, "switch fan to manual mode\n");
+               break;
+       case FAN_CONTROL_NR:
+               break;
+       }
+
+       priv->fan.mode = mode;
+       return 0;
+}
+
+int
+nouveau_therm_fan_user_get(struct nouveau_therm *therm)
+{
+       return nouveau_therm_fan_get(therm);
+}
+
+int
+nouveau_therm_fan_user_set(struct nouveau_therm *therm, int percent)
+{
+       struct nouveau_therm_priv *priv = (void *)therm;
+
+       if (priv->fan.mode != FAN_CONTROL_MANUAL)
+               return -EINVAL;
+
+       return nouveau_therm_fan_set(therm, percent);
+}
+
+void
 nouveau_therm_fan_set_defaults(struct nouveau_therm *therm)
 {
        struct nouveau_therm_priv *priv = (void *)therm;
@@ -180,5 +228,7 @@ nouveau_therm_fan_ctor(struct nouveau_therm *therm)
                nv_error(therm, "parsing the thermal table failed\n");
        nouveau_therm_fan_safety_checks(therm);
 
+       nouveau_therm_fan_set_mode(therm, FAN_CONTROL_NONE);
+
        return 0;
 }
index 9021b541da8d89d250e70d6df209fd76d5763b71..fcf2cfe731d668cf64ec5a65ef63d50bdab877a7 100644 (file)
@@ -142,8 +142,8 @@ nv40_therm_ctor(struct nouveau_object *parent,
        priv->fan.pwm_set = nv40_fan_pwm_set;
 
        therm->temp_get = nv40_temp_get;
-       therm->fan_get = nouveau_therm_fan_get;
-       therm->fan_set = nouveau_therm_fan_set;
+       therm->fan_get = nouveau_therm_fan_user_get;
+       therm->fan_set = nouveau_therm_fan_user_set;
        therm->fan_sense = nouveau_therm_fan_sense;
        therm->attr_get = nouveau_therm_attr_get;
        therm->attr_set = nouveau_therm_attr_set;
index de7dc20ed436a328593e43a5e28311045bc5f304..f87a7a3eb4e7c4c55548aed7bf398b6a8be83bad 100644 (file)
@@ -136,8 +136,8 @@ nv50_therm_ctor(struct nouveau_object *parent,
        priv->fan.pwm_clock = nv50_fan_pwm_clock;
 
        therm->temp_get = nv50_temp_get;
-       therm->fan_get = nouveau_therm_fan_get;
-       therm->fan_set = nouveau_therm_fan_set;
+       therm->fan_get = nouveau_therm_fan_user_get;
+       therm->fan_set = nouveau_therm_fan_user_set;
        therm->fan_sense = nouveau_therm_fan_sense;
        therm->attr_get = nouveau_therm_attr_get;
        therm->attr_set = nouveau_therm_attr_set;
index c53eb53969726e535175a5112f577c2bb1fa2edc..1c3cd6abc36ebe9de03efd7a73ea7d478b446ca3 100644 (file)
@@ -38,6 +38,7 @@ struct nouveau_therm_priv {
 
        /* fan priv */
        struct {
+               enum nouveau_therm_fan_mode mode;
                int percent;
 
                int (*pwm_get)(struct nouveau_therm *, int line, u32*, u32*);
@@ -63,5 +64,10 @@ int nouveau_therm_sensor_ctor(struct nouveau_therm *therm);
 int nouveau_therm_fan_ctor(struct nouveau_therm *therm);
 int nouveau_therm_fan_get(struct nouveau_therm *therm);
 int nouveau_therm_fan_set(struct nouveau_therm *therm, int percent);
+int nouveau_therm_fan_user_get(struct nouveau_therm *therm);
+int nouveau_therm_fan_user_set(struct nouveau_therm *therm, int percent);
+int nouveau_therm_fan_set_mode(struct nouveau_therm *therm,
+                          enum nouveau_therm_fan_mode mode);
+
 
 int nouveau_therm_fan_sense(struct nouveau_therm *therm);
index 0dca191ee173469e91acf5ef30ddf77fa18ed6e2..b9d5335df74274d60837b59a2342418eb4fcae02 100644 (file)
@@ -503,6 +503,45 @@ nouveau_hwmon_show_fan0_input(struct device *d, struct device_attribute *attr,
 static SENSOR_DEVICE_ATTR(fan0_input, S_IRUGO, nouveau_hwmon_show_fan0_input,
                          NULL, 0);
 
+ static ssize_t
+nouveau_hwmon_get_pwm1_enable(struct device *d,
+                          struct device_attribute *a, char *buf)
+{
+       struct drm_device *dev = dev_get_drvdata(d);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_therm *therm = nouveau_therm(drm->device);
+       int ret;
+
+       ret = therm->attr_get(therm, NOUVEAU_THERM_ATTR_FAN_MODE);
+       if (ret < 0)
+               return ret;
+
+       return sprintf(buf, "%i\n", ret);
+}
+
+static ssize_t
+nouveau_hwmon_set_pwm1_enable(struct device *d, struct device_attribute *a,
+                          const char *buf, size_t count)
+{
+       struct drm_device *dev = dev_get_drvdata(d);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_therm *therm = nouveau_therm(drm->device);
+       long value;
+       int ret;
+
+       if (strict_strtol(buf, 10, &value) == -EINVAL)
+               return -EINVAL;
+
+       ret = therm->attr_set(therm, NOUVEAU_THERM_ATTR_FAN_MODE, value);
+       if (ret)
+               return ret;
+       else
+               return count;
+}
+static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
+                         nouveau_hwmon_get_pwm1_enable,
+                         nouveau_hwmon_set_pwm1_enable, 0);
+
 static ssize_t
 nouveau_hwmon_get_pwm1(struct device *d, struct device_attribute *a, char *buf)
 {
@@ -638,6 +677,7 @@ static struct attribute *hwmon_fan_rpm_attributes[] = {
        NULL
 };
 static struct attribute *hwmon_pwm_fan_attributes[] = {
+       &sensor_dev_attr_pwm1_enable.dev_attr.attr,
        &sensor_dev_attr_pwm1.dev_attr.attr,
        &sensor_dev_attr_pwm1_min.dev_attr.attr,
        &sensor_dev_attr_pwm1_max.dev_attr.attr,