linux/drivers/gpu/drm/nouveau/core/subdev/therm/temp.c
<<
>>
Prefs
   1/*
   2 * Copyright 2012 The Nouveau community
   3 *
   4 * Permission is hereby granted, free of charge, to any person obtaining a
   5 * copy of this software and associated documentation files (the "Software"),
   6 * to deal in the Software without restriction, including without limitation
   7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   8 * and/or sell copies of the Software, and to permit persons to whom the
   9 * Software is furnished to do so, subject to the following conditions:
  10 *
  11 * The above copyright notice and this permission notice shall be included in
  12 * all copies or substantial portions of the Software.
  13 *
  14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
  18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  20 * OTHER DEALINGS IN THE SOFTWARE.
  21 *
  22 * Authors: Martin Peres
  23 */
  24
  25#include "priv.h"
  26
  27#include <core/object.h>
  28#include <core/device.h>
  29
  30#include <subdev/bios.h>
  31
  32static void
  33nouveau_therm_temp_set_defaults(struct nouveau_therm *therm)
  34{
  35        struct nouveau_therm_priv *priv = (void *)therm;
  36
  37        priv->bios_sensor.offset_constant = 0;
  38
  39        priv->bios_sensor.thrs_fan_boost.temp = 90;
  40        priv->bios_sensor.thrs_fan_boost.hysteresis = 3;
  41
  42        priv->bios_sensor.thrs_down_clock.temp = 95;
  43        priv->bios_sensor.thrs_down_clock.hysteresis = 3;
  44
  45        priv->bios_sensor.thrs_critical.temp = 105;
  46        priv->bios_sensor.thrs_critical.hysteresis = 5;
  47
  48        priv->bios_sensor.thrs_shutdown.temp = 135;
  49        priv->bios_sensor.thrs_shutdown.hysteresis = 5; /*not that it matters */
  50}
  51
  52
  53static void
  54nouveau_therm_temp_safety_checks(struct nouveau_therm *therm)
  55{
  56        struct nouveau_therm_priv *priv = (void *)therm;
  57        struct nvbios_therm_sensor *s = &priv->bios_sensor;
  58
  59        /* enforce a minimum hysteresis on thresholds */
  60        s->thrs_fan_boost.hysteresis = max_t(u8, s->thrs_fan_boost.hysteresis, 2);
  61        s->thrs_down_clock.hysteresis = max_t(u8, s->thrs_down_clock.hysteresis, 2);
  62        s->thrs_critical.hysteresis = max_t(u8, s->thrs_critical.hysteresis, 2);
  63        s->thrs_shutdown.hysteresis = max_t(u8, s->thrs_shutdown.hysteresis, 2);
  64}
  65
  66/* must be called with alarm_program_lock taken ! */
  67void nouveau_therm_sensor_set_threshold_state(struct nouveau_therm *therm,
  68                                             enum nouveau_therm_thrs thrs,
  69                                             enum nouveau_therm_thrs_state st)
  70{
  71        struct nouveau_therm_priv *priv = (void *)therm;
  72        priv->sensor.alarm_state[thrs] = st;
  73}
  74
  75/* must be called with alarm_program_lock taken ! */
  76enum nouveau_therm_thrs_state
  77nouveau_therm_sensor_get_threshold_state(struct nouveau_therm *therm,
  78                                         enum nouveau_therm_thrs thrs)
  79{
  80        struct nouveau_therm_priv *priv = (void *)therm;
  81        return priv->sensor.alarm_state[thrs];
  82}
  83
  84static void
  85nv_poweroff_work(struct work_struct *work)
  86{
  87        orderly_poweroff(true);
  88        kfree(work);
  89}
  90
  91void nouveau_therm_sensor_event(struct nouveau_therm *therm,
  92                                enum nouveau_therm_thrs thrs,
  93                                enum nouveau_therm_thrs_direction dir)
  94{
  95        struct nouveau_therm_priv *priv = (void *)therm;
  96        bool active;
  97        const char *thresolds[] = {
  98                "fanboost", "downclock", "critical", "shutdown"
  99        };
 100        int temperature = therm->temp_get(therm);
 101
 102        if (thrs < 0 || thrs > 3)
 103                return;
 104
 105        if (dir == NOUVEAU_THERM_THRS_FALLING)
 106                nv_info(therm, "temperature (%i C) went below the '%s' threshold\n",
 107                        temperature, thresolds[thrs]);
 108        else
 109                nv_info(therm, "temperature (%i C) hit the '%s' threshold\n",
 110                        temperature, thresolds[thrs]);
 111
 112        active = (dir == NOUVEAU_THERM_THRS_RISING);
 113        switch (thrs) {
 114        case NOUVEAU_THERM_THRS_FANBOOST:
 115                if (active) {
 116                        nouveau_therm_fan_set(therm, true, 100);
 117                        nouveau_therm_fan_mode(therm, NOUVEAU_THERM_CTRL_AUTO);
 118                }
 119                break;
 120        case NOUVEAU_THERM_THRS_DOWNCLOCK:
 121                if (priv->emergency.downclock)
 122                        priv->emergency.downclock(therm, active);
 123                break;
 124        case NOUVEAU_THERM_THRS_CRITICAL:
 125                if (priv->emergency.pause)
 126                        priv->emergency.pause(therm, active);
 127                break;
 128        case NOUVEAU_THERM_THRS_SHUTDOWN:
 129                if (active) {
 130                        struct work_struct *work;
 131
 132                        work = kmalloc(sizeof(*work), GFP_ATOMIC);
 133                        if (work) {
 134                                INIT_WORK(work, nv_poweroff_work);
 135                                schedule_work(work);
 136                        }
 137                }
 138                break;
 139        case NOUVEAU_THERM_THRS_NR:
 140                break;
 141        }
 142
 143}
 144
 145/* must be called with alarm_program_lock taken ! */
 146static void
 147nouveau_therm_threshold_hyst_polling(struct nouveau_therm *therm,
 148                                   const struct nvbios_therm_threshold *thrs,
 149                                   enum nouveau_therm_thrs thrs_name)
 150{
 151        enum nouveau_therm_thrs_direction direction;
 152        enum nouveau_therm_thrs_state prev_state, new_state;
 153        int temp = therm->temp_get(therm);
 154
 155        prev_state = nouveau_therm_sensor_get_threshold_state(therm, thrs_name);
 156
 157        if (temp >= thrs->temp && prev_state == NOUVEAU_THERM_THRS_LOWER) {
 158                direction = NOUVEAU_THERM_THRS_RISING;
 159                new_state = NOUVEAU_THERM_THRS_HIGHER;
 160        } else if (temp <= thrs->temp - thrs->hysteresis &&
 161                        prev_state == NOUVEAU_THERM_THRS_HIGHER) {
 162                direction = NOUVEAU_THERM_THRS_FALLING;
 163                new_state = NOUVEAU_THERM_THRS_LOWER;
 164        } else
 165                return; /* nothing to do */
 166
 167        nouveau_therm_sensor_set_threshold_state(therm, thrs_name, new_state);
 168        nouveau_therm_sensor_event(therm, thrs_name, direction);
 169}
 170
 171static void
 172alarm_timer_callback(struct nouveau_alarm *alarm)
 173{
 174        struct nouveau_therm_priv *priv =
 175        container_of(alarm, struct nouveau_therm_priv, sensor.therm_poll_alarm);
 176        struct nvbios_therm_sensor *sensor = &priv->bios_sensor;
 177        struct nouveau_timer *ptimer = nouveau_timer(priv);
 178        struct nouveau_therm *therm = &priv->base;
 179        unsigned long flags;
 180
 181        spin_lock_irqsave(&priv->sensor.alarm_program_lock, flags);
 182
 183        nouveau_therm_threshold_hyst_polling(therm, &sensor->thrs_fan_boost,
 184                                             NOUVEAU_THERM_THRS_FANBOOST);
 185
 186        nouveau_therm_threshold_hyst_polling(therm, &sensor->thrs_down_clock,
 187                                             NOUVEAU_THERM_THRS_DOWNCLOCK);
 188
 189        nouveau_therm_threshold_hyst_polling(therm, &sensor->thrs_critical,
 190                                             NOUVEAU_THERM_THRS_CRITICAL);
 191
 192        nouveau_therm_threshold_hyst_polling(therm, &sensor->thrs_shutdown,
 193                                             NOUVEAU_THERM_THRS_SHUTDOWN);
 194
 195        spin_unlock_irqrestore(&priv->sensor.alarm_program_lock, flags);
 196
 197        /* schedule the next poll in one second */
 198        if (therm->temp_get(therm) >= 0 && list_empty(&alarm->head))
 199                ptimer->alarm(ptimer, 1000000000ULL, alarm);
 200}
 201
 202void
 203nouveau_therm_program_alarms_polling(struct nouveau_therm *therm)
 204{
 205        struct nouveau_therm_priv *priv = (void *)therm;
 206        struct nvbios_therm_sensor *sensor = &priv->bios_sensor;
 207
 208        nv_debug(therm,
 209                 "programmed thresholds [ %d(%d), %d(%d), %d(%d), %d(%d) ]\n",
 210                 sensor->thrs_fan_boost.temp, sensor->thrs_fan_boost.hysteresis,
 211                 sensor->thrs_down_clock.temp,
 212                 sensor->thrs_down_clock.hysteresis,
 213                 sensor->thrs_critical.temp, sensor->thrs_critical.hysteresis,
 214                 sensor->thrs_shutdown.temp, sensor->thrs_shutdown.hysteresis);
 215
 216        alarm_timer_callback(&priv->sensor.therm_poll_alarm);
 217}
 218
 219int
 220nouveau_therm_sensor_init(struct nouveau_therm *therm)
 221{
 222        struct nouveau_therm_priv *priv = (void *)therm;
 223        priv->sensor.program_alarms(therm);
 224        return 0;
 225}
 226
 227int
 228nouveau_therm_sensor_fini(struct nouveau_therm *therm, bool suspend)
 229{
 230        struct nouveau_therm_priv *priv = (void *)therm;
 231        struct nouveau_timer *ptimer = nouveau_timer(therm);
 232
 233        if (suspend)
 234                ptimer->alarm_cancel(ptimer, &priv->sensor.therm_poll_alarm);
 235        return 0;
 236}
 237
 238void
 239nouveau_therm_sensor_preinit(struct nouveau_therm *therm)
 240{
 241        const char *sensor_avail = "yes";
 242
 243        if (therm->temp_get(therm) < 0)
 244                sensor_avail = "no";
 245
 246        nv_info(therm, "internal sensor: %s\n", sensor_avail);
 247}
 248
 249int
 250nouveau_therm_sensor_ctor(struct nouveau_therm *therm)
 251{
 252        struct nouveau_therm_priv *priv = (void *)therm;
 253        struct nouveau_bios *bios = nouveau_bios(therm);
 254
 255        nouveau_alarm_init(&priv->sensor.therm_poll_alarm, alarm_timer_callback);
 256
 257        nouveau_therm_temp_set_defaults(therm);
 258        if (nvbios_therm_sensor_parse(bios, NVBIOS_THERM_DOMAIN_CORE,
 259                                      &priv->bios_sensor))
 260                nv_error(therm, "nvbios_therm_sensor_parse failed\n");
 261        nouveau_therm_temp_safety_checks(therm);
 262
 263        return 0;
 264}
 265