Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright 2012 The Nouveau community
0003  *
0004  * Permission is hereby granted, free of charge, to any person obtaining a
0005  * copy of this software and associated documentation files (the "Software"),
0006  * to deal in the Software without restriction, including without limitation
0007  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
0008  * and/or sell copies of the Software, and to permit persons to whom the
0009  * Software is furnished to do so, subject to the following conditions:
0010  *
0011  * The above copyright notice and this permission notice shall be included in
0012  * all copies or substantial portions of the Software.
0013  *
0014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0015  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0016  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
0017  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
0018  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
0019  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
0020  * OTHER DEALINGS IN THE SOFTWARE.
0021  *
0022  * Authors: Martin Peres
0023  */
0024 #include "priv.h"
0025 
0026 #include <core/option.h>
0027 #include <subdev/pmu.h>
0028 
0029 int
0030 nvkm_therm_temp_get(struct nvkm_therm *therm)
0031 {
0032     if (therm->func->temp_get)
0033         return therm->func->temp_get(therm);
0034     return -ENODEV;
0035 }
0036 
0037 static int
0038 nvkm_therm_update_trip(struct nvkm_therm *therm)
0039 {
0040     struct nvbios_therm_trip_point *trip = therm->fan->bios.trip,
0041                        *cur_trip = NULL,
0042                        *last_trip = therm->last_trip;
0043     u8  temp = therm->func->temp_get(therm);
0044     u16 duty, i;
0045 
0046     /* look for the trip point corresponding to the current temperature */
0047     cur_trip = NULL;
0048     for (i = 0; i < therm->fan->bios.nr_fan_trip; i++) {
0049         if (temp >= trip[i].temp)
0050             cur_trip = &trip[i];
0051     }
0052 
0053     /* account for the hysteresis cycle */
0054     if (last_trip && temp <= (last_trip->temp) &&
0055         temp > (last_trip->temp - last_trip->hysteresis))
0056         cur_trip = last_trip;
0057 
0058     if (cur_trip) {
0059         duty = cur_trip->fan_duty;
0060         therm->last_trip = cur_trip;
0061     } else {
0062         duty = 0;
0063         therm->last_trip = NULL;
0064     }
0065 
0066     return duty;
0067 }
0068 
0069 static int
0070 nvkm_therm_compute_linear_duty(struct nvkm_therm *therm, u8 linear_min_temp,
0071                                u8 linear_max_temp)
0072 {
0073     u8  temp = therm->func->temp_get(therm);
0074     u16 duty;
0075 
0076     /* handle the non-linear part first */
0077     if (temp < linear_min_temp)
0078         return therm->fan->bios.min_duty;
0079     else if (temp > linear_max_temp)
0080         return therm->fan->bios.max_duty;
0081 
0082     /* we are in the linear zone */
0083     duty  = (temp - linear_min_temp);
0084     duty *= (therm->fan->bios.max_duty - therm->fan->bios.min_duty);
0085     duty /= (linear_max_temp - linear_min_temp);
0086     duty += therm->fan->bios.min_duty;
0087     return duty;
0088 }
0089 
0090 static int
0091 nvkm_therm_update_linear(struct nvkm_therm *therm)
0092 {
0093     u8  min = therm->fan->bios.linear_min_temp;
0094     u8  max = therm->fan->bios.linear_max_temp;
0095     return nvkm_therm_compute_linear_duty(therm, min, max);
0096 }
0097 
0098 static int
0099 nvkm_therm_update_linear_fallback(struct nvkm_therm *therm)
0100 {
0101     u8 max = therm->bios_sensor.thrs_fan_boost.temp;
0102     return nvkm_therm_compute_linear_duty(therm, 30, max);
0103 }
0104 
0105 static void
0106 nvkm_therm_update(struct nvkm_therm *therm, int mode)
0107 {
0108     struct nvkm_subdev *subdev = &therm->subdev;
0109     struct nvkm_timer *tmr = subdev->device->timer;
0110     unsigned long flags;
0111     bool immd = true;
0112     bool poll = true;
0113     int duty = -1;
0114 
0115     spin_lock_irqsave(&therm->lock, flags);
0116     if (mode < 0)
0117         mode = therm->mode;
0118     therm->mode = mode;
0119 
0120     switch (mode) {
0121     case NVKM_THERM_CTRL_MANUAL:
0122         nvkm_timer_alarm(tmr, 0, &therm->alarm);
0123         duty = nvkm_therm_fan_get(therm);
0124         if (duty < 0)
0125             duty = 100;
0126         poll = false;
0127         break;
0128     case NVKM_THERM_CTRL_AUTO:
0129         switch(therm->fan->bios.fan_mode) {
0130         case NVBIOS_THERM_FAN_TRIP:
0131             duty = nvkm_therm_update_trip(therm);
0132             break;
0133         case NVBIOS_THERM_FAN_LINEAR:
0134             duty = nvkm_therm_update_linear(therm);
0135             break;
0136         case NVBIOS_THERM_FAN_OTHER:
0137             if (therm->cstate) {
0138                 duty = therm->cstate;
0139                 poll = false;
0140             } else {
0141                 duty = nvkm_therm_update_linear_fallback(therm);
0142             }
0143             break;
0144         }
0145         immd = false;
0146         break;
0147     case NVKM_THERM_CTRL_NONE:
0148     default:
0149         nvkm_timer_alarm(tmr, 0, &therm->alarm);
0150         poll = false;
0151     }
0152 
0153     if (poll)
0154         nvkm_timer_alarm(tmr, 1000000000ULL, &therm->alarm);
0155     spin_unlock_irqrestore(&therm->lock, flags);
0156 
0157     if (duty >= 0) {
0158         nvkm_debug(subdev, "FAN target request: %d%%\n", duty);
0159         nvkm_therm_fan_set(therm, immd, duty);
0160     }
0161 }
0162 
0163 int
0164 nvkm_therm_cstate(struct nvkm_therm *therm, int fan, int dir)
0165 {
0166     struct nvkm_subdev *subdev = &therm->subdev;
0167     if (!dir || (dir < 0 && fan < therm->cstate) ||
0168             (dir > 0 && fan > therm->cstate)) {
0169         nvkm_debug(subdev, "default fan speed -> %d%%\n", fan);
0170         therm->cstate = fan;
0171         nvkm_therm_update(therm, -1);
0172     }
0173     return 0;
0174 }
0175 
0176 static void
0177 nvkm_therm_alarm(struct nvkm_alarm *alarm)
0178 {
0179     struct nvkm_therm *therm =
0180            container_of(alarm, struct nvkm_therm, alarm);
0181     nvkm_therm_update(therm, -1);
0182 }
0183 
0184 int
0185 nvkm_therm_fan_mode(struct nvkm_therm *therm, int mode)
0186 {
0187     struct nvkm_subdev *subdev = &therm->subdev;
0188     struct nvkm_device *device = subdev->device;
0189     static const char *name[] = {
0190         "disabled",
0191         "manual",
0192         "automatic"
0193     };
0194 
0195     /* The default PPWR ucode on fermi interferes with fan management */
0196     if ((mode >= ARRAY_SIZE(name)) ||
0197         (mode != NVKM_THERM_CTRL_NONE && nvkm_pmu_fan_controlled(device)))
0198         return -EINVAL;
0199 
0200     /* do not allow automatic fan management if the thermal sensor is
0201      * not available */
0202     if (mode == NVKM_THERM_CTRL_AUTO &&
0203         therm->func->temp_get(therm) < 0)
0204         return -EINVAL;
0205 
0206     if (therm->mode == mode)
0207         return 0;
0208 
0209     nvkm_debug(subdev, "fan management: %s\n", name[mode]);
0210     nvkm_therm_update(therm, mode);
0211     return 0;
0212 }
0213 
0214 int
0215 nvkm_therm_attr_get(struct nvkm_therm *therm, enum nvkm_therm_attr_type type)
0216 {
0217     switch (type) {
0218     case NVKM_THERM_ATTR_FAN_MIN_DUTY:
0219         return therm->fan->bios.min_duty;
0220     case NVKM_THERM_ATTR_FAN_MAX_DUTY:
0221         return therm->fan->bios.max_duty;
0222     case NVKM_THERM_ATTR_FAN_MODE:
0223         return therm->mode;
0224     case NVKM_THERM_ATTR_THRS_FAN_BOOST:
0225         return therm->bios_sensor.thrs_fan_boost.temp;
0226     case NVKM_THERM_ATTR_THRS_FAN_BOOST_HYST:
0227         return therm->bios_sensor.thrs_fan_boost.hysteresis;
0228     case NVKM_THERM_ATTR_THRS_DOWN_CLK:
0229         return therm->bios_sensor.thrs_down_clock.temp;
0230     case NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST:
0231         return therm->bios_sensor.thrs_down_clock.hysteresis;
0232     case NVKM_THERM_ATTR_THRS_CRITICAL:
0233         return therm->bios_sensor.thrs_critical.temp;
0234     case NVKM_THERM_ATTR_THRS_CRITICAL_HYST:
0235         return therm->bios_sensor.thrs_critical.hysteresis;
0236     case NVKM_THERM_ATTR_THRS_SHUTDOWN:
0237         return therm->bios_sensor.thrs_shutdown.temp;
0238     case NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST:
0239         return therm->bios_sensor.thrs_shutdown.hysteresis;
0240     }
0241 
0242     return -EINVAL;
0243 }
0244 
0245 int
0246 nvkm_therm_attr_set(struct nvkm_therm *therm,
0247             enum nvkm_therm_attr_type type, int value)
0248 {
0249     switch (type) {
0250     case NVKM_THERM_ATTR_FAN_MIN_DUTY:
0251         if (value < 0)
0252             value = 0;
0253         if (value > therm->fan->bios.max_duty)
0254             value = therm->fan->bios.max_duty;
0255         therm->fan->bios.min_duty = value;
0256         return 0;
0257     case NVKM_THERM_ATTR_FAN_MAX_DUTY:
0258         if (value < 0)
0259             value = 0;
0260         if (value < therm->fan->bios.min_duty)
0261             value = therm->fan->bios.min_duty;
0262         therm->fan->bios.max_duty = value;
0263         return 0;
0264     case NVKM_THERM_ATTR_FAN_MODE:
0265         return nvkm_therm_fan_mode(therm, value);
0266     case NVKM_THERM_ATTR_THRS_FAN_BOOST:
0267         therm->bios_sensor.thrs_fan_boost.temp = value;
0268         therm->func->program_alarms(therm);
0269         return 0;
0270     case NVKM_THERM_ATTR_THRS_FAN_BOOST_HYST:
0271         therm->bios_sensor.thrs_fan_boost.hysteresis = value;
0272         therm->func->program_alarms(therm);
0273         return 0;
0274     case NVKM_THERM_ATTR_THRS_DOWN_CLK:
0275         therm->bios_sensor.thrs_down_clock.temp = value;
0276         therm->func->program_alarms(therm);
0277         return 0;
0278     case NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST:
0279         therm->bios_sensor.thrs_down_clock.hysteresis = value;
0280         therm->func->program_alarms(therm);
0281         return 0;
0282     case NVKM_THERM_ATTR_THRS_CRITICAL:
0283         therm->bios_sensor.thrs_critical.temp = value;
0284         therm->func->program_alarms(therm);
0285         return 0;
0286     case NVKM_THERM_ATTR_THRS_CRITICAL_HYST:
0287         therm->bios_sensor.thrs_critical.hysteresis = value;
0288         therm->func->program_alarms(therm);
0289         return 0;
0290     case NVKM_THERM_ATTR_THRS_SHUTDOWN:
0291         therm->bios_sensor.thrs_shutdown.temp = value;
0292         therm->func->program_alarms(therm);
0293         return 0;
0294     case NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST:
0295         therm->bios_sensor.thrs_shutdown.hysteresis = value;
0296         therm->func->program_alarms(therm);
0297         return 0;
0298     }
0299 
0300     return -EINVAL;
0301 }
0302 
0303 void
0304 nvkm_therm_clkgate_enable(struct nvkm_therm *therm)
0305 {
0306     if (!therm || !therm->func->clkgate_enable || !therm->clkgating_enabled)
0307         return;
0308 
0309     nvkm_debug(&therm->subdev,
0310            "Enabling clockgating\n");
0311     therm->func->clkgate_enable(therm);
0312 }
0313 
0314 void
0315 nvkm_therm_clkgate_fini(struct nvkm_therm *therm, bool suspend)
0316 {
0317     if (!therm || !therm->func->clkgate_fini || !therm->clkgating_enabled)
0318         return;
0319 
0320     nvkm_debug(&therm->subdev,
0321            "Preparing clockgating for %s\n",
0322            suspend ? "suspend" : "fini");
0323     therm->func->clkgate_fini(therm, suspend);
0324 }
0325 
0326 static void
0327 nvkm_therm_clkgate_oneinit(struct nvkm_therm *therm)
0328 {
0329     if (!therm->func->clkgate_enable || !therm->clkgating_enabled)
0330         return;
0331 
0332     nvkm_info(&therm->subdev, "Clockgating enabled\n");
0333 }
0334 
0335 static void
0336 nvkm_therm_intr(struct nvkm_subdev *subdev)
0337 {
0338     struct nvkm_therm *therm = nvkm_therm(subdev);
0339     if (therm->func->intr)
0340         therm->func->intr(therm);
0341 }
0342 
0343 static int
0344 nvkm_therm_fini(struct nvkm_subdev *subdev, bool suspend)
0345 {
0346     struct nvkm_therm *therm = nvkm_therm(subdev);
0347 
0348     if (therm->func->fini)
0349         therm->func->fini(therm);
0350 
0351     nvkm_therm_fan_fini(therm, suspend);
0352     nvkm_therm_sensor_fini(therm, suspend);
0353 
0354     if (suspend) {
0355         therm->suspend = therm->mode;
0356         therm->mode = NVKM_THERM_CTRL_NONE;
0357     }
0358 
0359     return 0;
0360 }
0361 
0362 static int
0363 nvkm_therm_oneinit(struct nvkm_subdev *subdev)
0364 {
0365     struct nvkm_therm *therm = nvkm_therm(subdev);
0366     nvkm_therm_sensor_ctor(therm);
0367     nvkm_therm_ic_ctor(therm);
0368     nvkm_therm_fan_ctor(therm);
0369     nvkm_therm_fan_mode(therm, NVKM_THERM_CTRL_AUTO);
0370     nvkm_therm_sensor_preinit(therm);
0371     nvkm_therm_clkgate_oneinit(therm);
0372     return 0;
0373 }
0374 
0375 static int
0376 nvkm_therm_init(struct nvkm_subdev *subdev)
0377 {
0378     struct nvkm_therm *therm = nvkm_therm(subdev);
0379 
0380     if (therm->func->init)
0381         therm->func->init(therm);
0382 
0383     if (therm->suspend >= 0) {
0384         /* restore the pwm value only when on manual or auto mode */
0385         if (therm->suspend > 0)
0386             nvkm_therm_fan_set(therm, true, therm->fan->percent);
0387 
0388         nvkm_therm_fan_mode(therm, therm->suspend);
0389     }
0390 
0391     nvkm_therm_sensor_init(therm);
0392     nvkm_therm_fan_init(therm);
0393     return 0;
0394 }
0395 
0396 void
0397 nvkm_therm_clkgate_init(struct nvkm_therm *therm,
0398             const struct nvkm_therm_clkgate_pack *p)
0399 {
0400     if (!therm || !therm->func->clkgate_init || !therm->clkgating_enabled)
0401         return;
0402 
0403     therm->func->clkgate_init(therm, p);
0404 }
0405 
0406 static void *
0407 nvkm_therm_dtor(struct nvkm_subdev *subdev)
0408 {
0409     struct nvkm_therm *therm = nvkm_therm(subdev);
0410     kfree(therm->fan);
0411     return therm;
0412 }
0413 
0414 static const struct nvkm_subdev_func
0415 nvkm_therm = {
0416     .dtor = nvkm_therm_dtor,
0417     .oneinit = nvkm_therm_oneinit,
0418     .init = nvkm_therm_init,
0419     .fini = nvkm_therm_fini,
0420     .intr = nvkm_therm_intr,
0421 };
0422 
0423 void
0424 nvkm_therm_ctor(struct nvkm_therm *therm, struct nvkm_device *device, enum nvkm_subdev_type type,
0425         int inst, const struct nvkm_therm_func *func)
0426 {
0427     nvkm_subdev_ctor(&nvkm_therm, device, type, inst, &therm->subdev);
0428     therm->func = func;
0429 
0430     nvkm_alarm_init(&therm->alarm, nvkm_therm_alarm);
0431     spin_lock_init(&therm->lock);
0432     spin_lock_init(&therm->sensor.alarm_program_lock);
0433 
0434     therm->fan_get = nvkm_therm_fan_user_get;
0435     therm->fan_set = nvkm_therm_fan_user_set;
0436     therm->attr_get = nvkm_therm_attr_get;
0437     therm->attr_set = nvkm_therm_attr_set;
0438     therm->mode = therm->suspend = -1; /* undefined */
0439 
0440     therm->clkgating_enabled = nvkm_boolopt(device->cfgopt,
0441                         "NvPmEnableGating", false);
0442 }
0443 
0444 int
0445 nvkm_therm_new_(const struct nvkm_therm_func *func, struct nvkm_device *device,
0446         enum nvkm_subdev_type type, int inst, struct nvkm_therm **ptherm)
0447 {
0448     struct nvkm_therm *therm;
0449 
0450     if (!(therm = *ptherm = kzalloc(sizeof(*therm), GFP_KERNEL)))
0451         return -ENOMEM;
0452 
0453     nvkm_therm_ctor(therm, device, type, inst, func);
0454     return 0;
0455 }