drm/nvd7/therm: handle another kind of PWM fans
authorMartin Peres <martin.peres@labri.fr>
Tue, 18 Feb 2014 02:56:11 +0000 (03:56 +0100)
committerBen Skeggs <bskeggs@redhat.com>
Wed, 26 Mar 2014 04:08:24 +0000 (14:08 +1000)
This should fix fan management on many nvd7+ chipsets.

Signed-off-by: Martin Peres <martin.peres@labri.fr>
Tested-by: Timothée Ravier <tim@siosm.fr>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/core/include/subdev/therm.h
drivers/gpu/drm/nouveau/core/subdev/therm/fan.c
drivers/gpu/drm/nouveau/core/subdev/therm/fanpwm.c
drivers/gpu/drm/nouveau/core/subdev/therm/nv50.c
drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c
drivers/gpu/drm/nouveau/core/subdev/therm/priv.h

index 69891d4a3fe7e3f50a840e4cdc2a34196bcd0010..d4a68179e5863c383640cd9c339ce941e34d9909 100644 (file)
@@ -31,7 +31,7 @@ struct nouveau_therm {
        int (*pwm_ctrl)(struct nouveau_therm *, int line, bool);
        int (*pwm_get)(struct nouveau_therm *, int line, u32 *, u32 *);
        int (*pwm_set)(struct nouveau_therm *, int line, u32, u32);
-       int (*pwm_clock)(struct nouveau_therm *);
+       int (*pwm_clock)(struct nouveau_therm *, int line);
 
        int (*fan_get)(struct nouveau_therm *);
        int (*fan_set)(struct nouveau_therm *, int);
index 29d4c417a5b367ff245c45c35c9f47553e539286..ceb85281cbe9b17e81562f8ed7a252d3e9f57140 100644 (file)
@@ -242,7 +242,8 @@ nouveau_therm_fan_ctor(struct nouveau_therm *therm)
        /* attempt to locate a drivable fan, and determine control method */
        ret = gpio->find(gpio, 0, DCB_GPIO_FAN, 0xff, &func);
        if (ret == 0) {
-               if (func.log[0] & DCB_GPIO_LOG_DIR_IN) {
+               /* FIXME: is this really the place to perform such checks ? */
+               if (func.line != 16 && func.log[0] & DCB_GPIO_LOG_DIR_IN) {
                        nv_debug(therm, "GPIO_FAN is in input mode\n");
                        ret = -EINVAL;
                } else {
index 5f71db8e8992e0c38ec2c46f945450d8e9778c51..9a5c07340263305f1310d1e6a3520808c2606049 100644 (file)
@@ -67,7 +67,7 @@ nouveau_fanpwm_set(struct nouveau_therm *therm, int percent)
        if (priv->base.bios.pwm_freq) {
                divs = 1;
                if (therm->pwm_clock)
-                       divs = therm->pwm_clock(therm);
+                       divs = therm->pwm_clock(therm, priv->func.line);
                divs /= priv->base.bios.pwm_freq;
        }
 
index 8cf7597a2182d78ac6f6a57e7ee5e997bf1713ff..321db927d638c91a82141373fd8ff6af9a7be262 100644 (file)
@@ -93,7 +93,7 @@ nv50_fan_pwm_set(struct nouveau_therm *therm, int line, u32 divs, u32 duty)
 }
 
 int
-nv50_fan_pwm_clock(struct nouveau_therm *therm)
+nv50_fan_pwm_clock(struct nouveau_therm *therm, int line)
 {
        int chipset = nv_device(therm)->chipset;
        int crystal = nv_device(therm)->crystal;
index 4dd4f81ae873d32c6aa47ae1c64d65fd252cae11..43fec17ea540b6c53af704931d4baf29d2391e4e 100644 (file)
@@ -32,10 +32,12 @@ static int
 pwm_info(struct nouveau_therm *therm, int line)
 {
        u32 gpio = nv_rd32(therm, 0x00d610 + (line * 0x04));
+
        switch (gpio & 0x000000c0) {
        case 0x00000000: /* normal mode, possibly pwm forced off by us */
        case 0x00000040: /* nvio special */
                switch (gpio & 0x0000001f) {
+               case 0x00: return 2;
                case 0x19: return 1;
                case 0x1c: return 0;
                default:
@@ -56,8 +58,9 @@ nvd0_fan_pwm_ctrl(struct nouveau_therm *therm, int line, bool enable)
        int indx = pwm_info(therm, line);
        if (indx < 0)
                return indx;
-
-       nv_mask(therm, 0x00d610 + (line * 0x04), 0x000000c0, data);
+       else if (indx < 2)
+               nv_mask(therm, 0x00d610 + (line * 0x04), 0x000000c0, data);
+       /* nothing to do for indx == 2, it seems hardwired to PTHERM */
        return 0;
 }
 
@@ -67,10 +70,15 @@ nvd0_fan_pwm_get(struct nouveau_therm *therm, int line, u32 *divs, u32 *duty)
        int indx = pwm_info(therm, line);
        if (indx < 0)
                return indx;
-
-       if (nv_rd32(therm, 0x00d610 + (line * 0x04)) & 0x00000040) {
-               *divs = nv_rd32(therm, 0x00e114 + (indx * 8));
-               *duty = nv_rd32(therm, 0x00e118 + (indx * 8));
+       else if (indx < 2) {
+               if (nv_rd32(therm, 0x00d610 + (line * 0x04)) & 0x00000040) {
+                       *divs = nv_rd32(therm, 0x00e114 + (indx * 8));
+                       *duty = nv_rd32(therm, 0x00e118 + (indx * 8));
+                       return 0;
+               }
+       } else if (indx == 2) {
+               *divs = nv_rd32(therm, 0x0200d8) & 0x1fff;
+               *duty = nv_rd32(therm, 0x0200dc) & 0x1fff;
                return 0;
        }
 
@@ -83,16 +91,26 @@ nvd0_fan_pwm_set(struct nouveau_therm *therm, int line, u32 divs, u32 duty)
        int indx = pwm_info(therm, line);
        if (indx < 0)
                return indx;
-
-       nv_wr32(therm, 0x00e114 + (indx * 8), divs);
-       nv_wr32(therm, 0x00e118 + (indx * 8), duty | 0x80000000);
+       else if (indx < 2) {
+               nv_wr32(therm, 0x00e114 + (indx * 8), divs);
+               nv_wr32(therm, 0x00e118 + (indx * 8), duty | 0x80000000);
+       } else if (indx == 2) {
+               nv_mask(therm, 0x0200d8, 0x1fff, divs); /* keep the high bits */
+               nv_wr32(therm, 0x0200dc, duty | 0x40000000);
+       }
        return 0;
 }
 
 static int
-nvd0_fan_pwm_clock(struct nouveau_therm *therm)
+nvd0_fan_pwm_clock(struct nouveau_therm *therm, int line)
 {
-       return (nv_device(therm)->crystal * 1000) / 20;
+       int indx = pwm_info(therm, line);
+       if (indx < 0)
+               return 0;
+       else if (indx < 2)
+               return (nv_device(therm)->crystal * 1000) / 20;
+       else
+               return nv_device(therm)->crystal * 1000 / 10;
 }
 
 static int
index 96f8f95693ce10654a1a4339a1e0eb16dba6848e..916fca5c78162488a23e65e11188cda9b2d0e0eb 100644 (file)
@@ -143,7 +143,7 @@ void nv40_therm_intr(struct nouveau_subdev *);
 int nv50_fan_pwm_ctrl(struct nouveau_therm *, int, bool);
 int nv50_fan_pwm_get(struct nouveau_therm *, int, u32 *, u32 *);
 int nv50_fan_pwm_set(struct nouveau_therm *, int, u32, u32);
-int nv50_fan_pwm_clock(struct nouveau_therm *);
+int nv50_fan_pwm_clock(struct nouveau_therm *, int);
 int nv84_temp_get(struct nouveau_therm *therm);
 int nv84_therm_fini(struct nouveau_object *object, bool suspend);