int hysteresis;
 };
 
+enum nvbios_therm_fan_mode {
+       NVBIOS_THERM_FAN_TRIP = 0,
+       NVBIOS_THERM_FAN_LINEAR = 1,
+       NVBIOS_THERM_FAN_OTHER = 2,
+};
+
 struct nvbios_therm_fan {
        u16 pwm_freq;
 
        u16 bump_period;
        u16 slow_down_period;
 
+       enum nvbios_therm_fan_mode fan_mode;
        struct nouveau_therm_trip_point trip[NOUVEAU_TEMP_FAN_TRIP_MAX];
        u8 nr_fan_trip;
        u8 linear_min_temp;
 
 
        i = 0;
        fan->nr_fan_trip = 0;
+       fan->fan_mode = NVBIOS_THERM_FAN_OTHER;
        while ((entry = nvbios_therm_entry(bios, i++, &ver, &len))) {
                s16 value = nv_ro16(bios, entry + 1);
 
                        break;
                case 0x24:
                        fan->nr_fan_trip++;
+                       if (fan->fan_mode > NVBIOS_THERM_FAN_TRIP)
+                               fan->fan_mode = NVBIOS_THERM_FAN_TRIP;
                        cur_trip = &fan->trip[fan->nr_fan_trip - 1];
                        cur_trip->hysteresis = value & 0xf;
                        cur_trip->temp = (value & 0xff0) >> 4;
                        fan->slow_down_period = value;
                        break;
                case 0x46:
+                       if (fan->fan_mode > NVBIOS_THERM_FAN_LINEAR)
+                               fan->fan_mode = NVBIOS_THERM_FAN_LINEAR;
                        fan->linear_min_temp = nv_ro08(bios, entry + 1);
                        fan->linear_max_temp = nv_ro08(bios, entry + 2);
                        break;
                }
        }
 
+       /* starting from fermi, fan management is always linear */
+       if (nv_device(bios)->card_type >= NV_C0 &&
+               fan->fan_mode == NVBIOS_THERM_FAN_OTHER) {
+               fan->fan_mode = NVBIOS_THERM_FAN_LINEAR;
+       }
+
        return 0;
 }
 
                poll = false;
                break;
        case NOUVEAU_THERM_CTRL_AUTO:
-               if (priv->fan->bios.nr_fan_trip) {
+               switch(priv->fan->bios.fan_mode) {
+               case NVBIOS_THERM_FAN_TRIP:
                        duty = nouveau_therm_update_trip(therm);
-               } else
-               if (priv->fan->bios.linear_min_temp ||
-                   priv->fan->bios.linear_max_temp) {
+                       break;
+               case NVBIOS_THERM_FAN_LINEAR:
                        duty = nouveau_therm_update_linear(therm);
-               } else {
+                       break;
+               case NVBIOS_THERM_FAN_OTHER:
                        if (priv->cstate)
                                duty = priv->cstate;
                        poll = false;
+                       break;
                }
                immd = false;
                break;
 
        priv->fan->bios.max_duty = 100;
        priv->fan->bios.bump_period = 500;
        priv->fan->bios.slow_down_period = 2000;
-/*XXX: talk to mupuf */
-#if 0
        priv->fan->bios.linear_min_temp = 40;
        priv->fan->bios.linear_max_temp = 85;
-#endif
 }
 
 static void