root/drivers/gpu/drm/nouveau/nvkm/subdev/volt/base.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. nvkm_volt_get
  2. nvkm_volt_set
  3. nvkm_volt_map_min
  4. nvkm_volt_map
  5. nvkm_volt_set_id
  6. nvkm_volt_parse_bios
  7. nvkm_volt_speedo_read
  8. nvkm_volt_init
  9. nvkm_volt_oneinit
  10. nvkm_volt_dtor
  11. nvkm_volt_ctor
  12. nvkm_volt_new_

   1 /*
   2  * Copyright 2013 Red Hat Inc.
   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: Ben Skeggs
  23  */
  24 #include "priv.h"
  25 
  26 #include <subdev/bios.h>
  27 #include <subdev/bios/vmap.h>
  28 #include <subdev/bios/volt.h>
  29 #include <subdev/therm.h>
  30 
  31 int
  32 nvkm_volt_get(struct nvkm_volt *volt)
  33 {
  34         int ret, i;
  35 
  36         if (volt->func->volt_get)
  37                 return volt->func->volt_get(volt);
  38 
  39         ret = volt->func->vid_get(volt);
  40         if (ret >= 0) {
  41                 for (i = 0; i < volt->vid_nr; i++) {
  42                         if (volt->vid[i].vid == ret)
  43                                 return volt->vid[i].uv;
  44                 }
  45                 ret = -EINVAL;
  46         }
  47         return ret;
  48 }
  49 
  50 static int
  51 nvkm_volt_set(struct nvkm_volt *volt, u32 uv)
  52 {
  53         struct nvkm_subdev *subdev = &volt->subdev;
  54         int i, ret = -EINVAL, best_err = volt->max_uv, best = -1;
  55 
  56         if (volt->func->volt_set)
  57                 return volt->func->volt_set(volt, uv);
  58 
  59         for (i = 0; i < volt->vid_nr; i++) {
  60                 int err = volt->vid[i].uv - uv;
  61                 if (err < 0 || err > best_err)
  62                         continue;
  63 
  64                 best_err = err;
  65                 best = i;
  66                 if (best_err == 0)
  67                         break;
  68         }
  69 
  70         if (best == -1) {
  71                 nvkm_error(subdev, "couldn't set %iuv\n", uv);
  72                 return ret;
  73         }
  74 
  75         ret = volt->func->vid_set(volt, volt->vid[best].vid);
  76         nvkm_debug(subdev, "set req %duv to %duv: %d\n", uv,
  77                    volt->vid[best].uv, ret);
  78         return ret;
  79 }
  80 
  81 int
  82 nvkm_volt_map_min(struct nvkm_volt *volt, u8 id)
  83 {
  84         struct nvkm_bios *bios = volt->subdev.device->bios;
  85         struct nvbios_vmap_entry info;
  86         u8  ver, len;
  87         u32 vmap;
  88 
  89         vmap = nvbios_vmap_entry_parse(bios, id, &ver, &len, &info);
  90         if (vmap) {
  91                 if (info.link != 0xff) {
  92                         int ret = nvkm_volt_map_min(volt, info.link);
  93                         if (ret < 0)
  94                                 return ret;
  95                         info.min += ret;
  96                 }
  97                 return info.min;
  98         }
  99 
 100         return id ? id * 10000 : -ENODEV;
 101 }
 102 
 103 int
 104 nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temp)
 105 {
 106         struct nvkm_bios *bios = volt->subdev.device->bios;
 107         struct nvbios_vmap_entry info;
 108         u8  ver, len;
 109         u32 vmap;
 110 
 111         vmap = nvbios_vmap_entry_parse(bios, id, &ver, &len, &info);
 112         if (vmap) {
 113                 s64 result;
 114 
 115                 if (volt->speedo < 0)
 116                         return volt->speedo;
 117 
 118                 if (ver == 0x10 || (ver == 0x20 && info.mode == 0)) {
 119                         result  = div64_s64((s64)info.arg[0], 10);
 120                         result += div64_s64((s64)info.arg[1] * volt->speedo, 10);
 121                         result += div64_s64((s64)info.arg[2] * volt->speedo * volt->speedo, 100000);
 122                 } else if (ver == 0x20) {
 123                         switch (info.mode) {
 124                         /* 0x0 handled above! */
 125                         case 0x1:
 126                                 result =  ((s64)info.arg[0] * 15625) >> 18;
 127                                 result += ((s64)info.arg[1] * volt->speedo * 15625) >> 18;
 128                                 result += ((s64)info.arg[2] * temp * 15625) >> 10;
 129                                 result += ((s64)info.arg[3] * volt->speedo * temp * 15625) >> 18;
 130                                 result += ((s64)info.arg[4] * volt->speedo * volt->speedo * 15625) >> 30;
 131                                 result += ((s64)info.arg[5] * temp * temp * 15625) >> 18;
 132                                 break;
 133                         case 0x3:
 134                                 result = (info.min + info.max) / 2;
 135                                 break;
 136                         case 0x2:
 137                         default:
 138                                 result = info.min;
 139                                 break;
 140                         }
 141                 } else {
 142                         return -ENODEV;
 143                 }
 144 
 145                 result = min(max(result, (s64)info.min), (s64)info.max);
 146 
 147                 if (info.link != 0xff) {
 148                         int ret = nvkm_volt_map(volt, info.link, temp);
 149                         if (ret < 0)
 150                                 return ret;
 151                         result += ret;
 152                 }
 153                 return result;
 154         }
 155 
 156         return id ? id * 10000 : -ENODEV;
 157 }
 158 
 159 int
 160 nvkm_volt_set_id(struct nvkm_volt *volt, u8 id, u8 min_id, u8 temp,
 161                  int condition)
 162 {
 163         int ret;
 164 
 165         if (volt->func->set_id)
 166                 return volt->func->set_id(volt, id, condition);
 167 
 168         ret = nvkm_volt_map(volt, id, temp);
 169         if (ret >= 0) {
 170                 int prev = nvkm_volt_get(volt);
 171                 if (!condition || prev < 0 ||
 172                     (condition < 0 && ret < prev) ||
 173                     (condition > 0 && ret > prev)) {
 174                         int min = nvkm_volt_map(volt, min_id, temp);
 175                         if (min >= 0)
 176                                 ret = max(min, ret);
 177                         ret = nvkm_volt_set(volt, ret);
 178                 } else {
 179                         ret = 0;
 180                 }
 181         }
 182         return ret;
 183 }
 184 
 185 static void
 186 nvkm_volt_parse_bios(struct nvkm_bios *bios, struct nvkm_volt *volt)
 187 {
 188         struct nvkm_subdev *subdev = &bios->subdev;
 189         struct nvbios_volt_entry ivid;
 190         struct nvbios_volt info;
 191         u8  ver, hdr, cnt, len;
 192         u32 data;
 193         int i;
 194 
 195         data = nvbios_volt_parse(bios, &ver, &hdr, &cnt, &len, &info);
 196         if (data && info.vidmask && info.base && info.step && info.ranged) {
 197                 nvkm_debug(subdev, "found ranged based VIDs\n");
 198                 volt->min_uv = info.min;
 199                 volt->max_uv = info.max;
 200                 for (i = 0; i < info.vidmask + 1; i++) {
 201                         if (info.base >= info.min &&
 202                                 info.base <= info.max) {
 203                                 volt->vid[volt->vid_nr].uv = info.base;
 204                                 volt->vid[volt->vid_nr].vid = i;
 205                                 volt->vid_nr++;
 206                         }
 207                         info.base += info.step;
 208                 }
 209                 volt->vid_mask = info.vidmask;
 210         } else if (data && info.vidmask && !info.ranged) {
 211                 nvkm_debug(subdev, "found entry based VIDs\n");
 212                 volt->min_uv = 0xffffffff;
 213                 volt->max_uv = 0;
 214                 for (i = 0; i < cnt; i++) {
 215                         data = nvbios_volt_entry_parse(bios, i, &ver, &hdr,
 216                                                        &ivid);
 217                         if (data) {
 218                                 volt->vid[volt->vid_nr].uv = ivid.voltage;
 219                                 volt->vid[volt->vid_nr].vid = ivid.vid;
 220                                 volt->vid_nr++;
 221                                 volt->min_uv = min(volt->min_uv, ivid.voltage);
 222                                 volt->max_uv = max(volt->max_uv, ivid.voltage);
 223                         }
 224                 }
 225                 volt->vid_mask = info.vidmask;
 226         } else if (data && info.type == NVBIOS_VOLT_PWM) {
 227                 volt->min_uv = info.base;
 228                 volt->max_uv = info.base + info.pwm_range;
 229         }
 230 }
 231 
 232 static int
 233 nvkm_volt_speedo_read(struct nvkm_volt *volt)
 234 {
 235         if (volt->func->speedo_read)
 236                 return volt->func->speedo_read(volt);
 237         return -EINVAL;
 238 }
 239 
 240 static int
 241 nvkm_volt_init(struct nvkm_subdev *subdev)
 242 {
 243         struct nvkm_volt *volt = nvkm_volt(subdev);
 244         int ret = nvkm_volt_get(volt);
 245         if (ret < 0) {
 246                 if (ret != -ENODEV)
 247                         nvkm_debug(subdev, "current voltage unknown\n");
 248                 return 0;
 249         }
 250         nvkm_debug(subdev, "current voltage: %duv\n", ret);
 251         return 0;
 252 }
 253 
 254 static int
 255 nvkm_volt_oneinit(struct nvkm_subdev *subdev)
 256 {
 257         struct nvkm_volt *volt = nvkm_volt(subdev);
 258 
 259         volt->speedo = nvkm_volt_speedo_read(volt);
 260         if (volt->speedo > 0)
 261                 nvkm_debug(&volt->subdev, "speedo %x\n", volt->speedo);
 262 
 263         if (volt->func->oneinit)
 264                 return volt->func->oneinit(volt);
 265 
 266         return 0;
 267 }
 268 
 269 static void *
 270 nvkm_volt_dtor(struct nvkm_subdev *subdev)
 271 {
 272         return nvkm_volt(subdev);
 273 }
 274 
 275 static const struct nvkm_subdev_func
 276 nvkm_volt = {
 277         .dtor = nvkm_volt_dtor,
 278         .init = nvkm_volt_init,
 279         .oneinit = nvkm_volt_oneinit,
 280 };
 281 
 282 void
 283 nvkm_volt_ctor(const struct nvkm_volt_func *func, struct nvkm_device *device,
 284                int index, struct nvkm_volt *volt)
 285 {
 286         struct nvkm_bios *bios = device->bios;
 287         int i;
 288 
 289         nvkm_subdev_ctor(&nvkm_volt, device, index, &volt->subdev);
 290         volt->func = func;
 291 
 292         /* Assuming the non-bios device should build the voltage table later */
 293         if (bios) {
 294                 u8 ver, hdr, cnt, len;
 295                 struct nvbios_vmap vmap;
 296 
 297                 nvkm_volt_parse_bios(bios, volt);
 298                 nvkm_debug(&volt->subdev, "min: %iuv max: %iuv\n",
 299                            volt->min_uv, volt->max_uv);
 300 
 301                 if (nvbios_vmap_parse(bios, &ver, &hdr, &cnt, &len, &vmap)) {
 302                         volt->max0_id = vmap.max0;
 303                         volt->max1_id = vmap.max1;
 304                         volt->max2_id = vmap.max2;
 305                 } else {
 306                         volt->max0_id = 0xff;
 307                         volt->max1_id = 0xff;
 308                         volt->max2_id = 0xff;
 309                 }
 310         }
 311 
 312         if (volt->vid_nr) {
 313                 for (i = 0; i < volt->vid_nr; i++) {
 314                         nvkm_debug(&volt->subdev, "VID %02x: %duv\n",
 315                                    volt->vid[i].vid, volt->vid[i].uv);
 316                 }
 317         }
 318 }
 319 
 320 int
 321 nvkm_volt_new_(const struct nvkm_volt_func *func, struct nvkm_device *device,
 322                int index, struct nvkm_volt **pvolt)
 323 {
 324         if (!(*pvolt = kzalloc(sizeof(**pvolt), GFP_KERNEL)))
 325                 return -ENOMEM;
 326         nvkm_volt_ctor(func, device, index, *pvolt);
 327         return 0;
 328 }

/* [<][>][^][v][top][bottom][index][help] */