linux/drivers/gpu/drm/nouveau/nvkm/subdev/bios/perf.c
<<
>>
Prefs
   1/*
   2 * Copyright 2012 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#include <subdev/bios.h>
  25#include <subdev/bios/bit.h>
  26#include <subdev/bios/perf.h>
  27#include <subdev/pci.h>
  28
  29u32
  30nvbios_perf_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr,
  31                  u8 *cnt, u8 *len, u8 *snr, u8 *ssz)
  32{
  33        struct bit_entry bit_P;
  34        u32 perf = 0;
  35
  36        if (!bit_entry(bios, 'P', &bit_P)) {
  37                if (bit_P.version <= 2) {
  38                        perf = nvbios_rd32(bios, bit_P.offset + 0);
  39                        if (perf) {
  40                                *ver = nvbios_rd08(bios, perf + 0);
  41                                *hdr = nvbios_rd08(bios, perf + 1);
  42                                if (*ver >= 0x40 && *ver < 0x41) {
  43                                        *cnt = nvbios_rd08(bios, perf + 5);
  44                                        *len = nvbios_rd08(bios, perf + 2);
  45                                        *snr = nvbios_rd08(bios, perf + 4);
  46                                        *ssz = nvbios_rd08(bios, perf + 3);
  47                                        return perf;
  48                                } else
  49                                if (*ver >= 0x20 && *ver < 0x40) {
  50                                        *cnt = nvbios_rd08(bios, perf + 2);
  51                                        *len = nvbios_rd08(bios, perf + 3);
  52                                        *snr = nvbios_rd08(bios, perf + 4);
  53                                        *ssz = nvbios_rd08(bios, perf + 5);
  54                                        return perf;
  55                                }
  56                        }
  57                }
  58        }
  59
  60        if (bios->bmp_offset) {
  61                if (nvbios_rd08(bios, bios->bmp_offset + 6) >= 0x25) {
  62                        perf = nvbios_rd16(bios, bios->bmp_offset + 0x94);
  63                        if (perf) {
  64                                *hdr = nvbios_rd08(bios, perf + 0);
  65                                *ver = nvbios_rd08(bios, perf + 1);
  66                                *cnt = nvbios_rd08(bios, perf + 2);
  67                                *len = nvbios_rd08(bios, perf + 3);
  68                                *snr = 0;
  69                                *ssz = 0;
  70                                return perf;
  71                        }
  72                }
  73        }
  74
  75        return 0;
  76}
  77
  78u32
  79nvbios_perf_entry(struct nvkm_bios *bios, int idx,
  80                  u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
  81{
  82        u8  snr, ssz;
  83        u32 perf = nvbios_perf_table(bios, ver, hdr, cnt, len, &snr, &ssz);
  84        if (perf && idx < *cnt) {
  85                perf = perf + *hdr + (idx * (*len + (snr * ssz)));
  86                *hdr = *len;
  87                *cnt = snr;
  88                *len = ssz;
  89                return perf;
  90        }
  91        return 0;
  92}
  93
  94u32
  95nvbios_perfEp(struct nvkm_bios *bios, int idx,
  96              u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_perfE *info)
  97{
  98        u32 perf = nvbios_perf_entry(bios, idx, ver, hdr, cnt, len);
  99        memset(info, 0x00, sizeof(*info));
 100        info->pstate = nvbios_rd08(bios, perf + 0x00);
 101        switch (!!perf * *ver) {
 102        case 0x12:
 103        case 0x13:
 104        case 0x14:
 105                info->core     = nvbios_rd32(bios, perf + 0x01) * 10;
 106                info->memory   = nvbios_rd32(bios, perf + 0x05) * 20;
 107                info->fanspeed = nvbios_rd08(bios, perf + 0x37);
 108                if (*hdr > 0x38)
 109                        info->voltage = nvbios_rd08(bios, perf + 0x38);
 110                break;
 111        case 0x21:
 112        case 0x23:
 113        case 0x24:
 114                info->fanspeed = nvbios_rd08(bios, perf + 0x04);
 115                info->voltage  = nvbios_rd08(bios, perf + 0x05);
 116                info->shader   = nvbios_rd16(bios, perf + 0x06) * 1000;
 117                info->core     = info->shader + (signed char)
 118                                 nvbios_rd08(bios, perf + 0x08) * 1000;
 119                switch (bios->subdev.device->chipset) {
 120                case 0x49:
 121                case 0x4b:
 122                        info->memory = nvbios_rd16(bios, perf + 0x0b) * 1000;
 123                        break;
 124                default:
 125                        info->memory = nvbios_rd16(bios, perf + 0x0b) * 2000;
 126                        break;
 127                }
 128                break;
 129        case 0x25:
 130                info->fanspeed = nvbios_rd08(bios, perf + 0x04);
 131                info->voltage  = nvbios_rd08(bios, perf + 0x05);
 132                info->core     = nvbios_rd16(bios, perf + 0x06) * 1000;
 133                info->shader   = nvbios_rd16(bios, perf + 0x0a) * 1000;
 134                info->memory   = nvbios_rd16(bios, perf + 0x0c) * 1000;
 135                break;
 136        case 0x30:
 137                info->script   = nvbios_rd16(bios, perf + 0x02);
 138        case 0x35:
 139                info->fanspeed = nvbios_rd08(bios, perf + 0x06);
 140                info->voltage  = nvbios_rd08(bios, perf + 0x07);
 141                info->core     = nvbios_rd16(bios, perf + 0x08) * 1000;
 142                info->shader   = nvbios_rd16(bios, perf + 0x0a) * 1000;
 143                info->memory   = nvbios_rd16(bios, perf + 0x0c) * 1000;
 144                info->vdec     = nvbios_rd16(bios, perf + 0x10) * 1000;
 145                info->disp     = nvbios_rd16(bios, perf + 0x14) * 1000;
 146                break;
 147        case 0x40:
 148                info->voltage  = nvbios_rd08(bios, perf + 0x02);
 149                switch (nvbios_rd08(bios, perf + 0xb) & 0x3) {
 150                case 0:
 151                        info->pcie_speed = NVKM_PCIE_SPEED_5_0;
 152                        break;
 153                case 3:
 154                case 1:
 155                        info->pcie_speed = NVKM_PCIE_SPEED_2_5;
 156                        break;
 157                case 2:
 158                        info->pcie_speed = NVKM_PCIE_SPEED_8_0;
 159                        break;
 160                default:
 161                        break;
 162                }
 163                info->pcie_width = 0xff;
 164                break;
 165        default:
 166                return 0;
 167        }
 168        return perf;
 169}
 170
 171u32
 172nvbios_perfSe(struct nvkm_bios *bios, u32 perfE, int idx,
 173              u8 *ver, u8 *hdr, u8 cnt, u8 len)
 174{
 175        u32 data = 0x00000000;
 176        if (idx < cnt) {
 177                data = perfE + *hdr + (idx * len);
 178                *hdr = len;
 179        }
 180        return data;
 181}
 182
 183u32
 184nvbios_perfSp(struct nvkm_bios *bios, u32 perfE, int idx,
 185              u8 *ver, u8 *hdr, u8 cnt, u8 len,
 186              struct nvbios_perfS *info)
 187{
 188        u32 data = nvbios_perfSe(bios, perfE, idx, ver, hdr, cnt, len);
 189        memset(info, 0x00, sizeof(*info));
 190        switch (!!data * *ver) {
 191        case 0x40:
 192                info->v40.freq = (nvbios_rd16(bios, data + 0x00) & 0x3fff) * 1000;
 193                break;
 194        default:
 195                break;
 196        }
 197        return data;
 198}
 199
 200int
 201nvbios_perf_fan_parse(struct nvkm_bios *bios,
 202                      struct nvbios_perf_fan *fan)
 203{
 204        u8  ver, hdr, cnt, len, snr, ssz;
 205        u32 perf = nvbios_perf_table(bios, &ver, &hdr, &cnt, &len, &snr, &ssz);
 206        if (!perf)
 207                return -ENODEV;
 208
 209        if (ver >= 0x20 && ver < 0x40 && hdr > 6)
 210                fan->pwm_divisor = nvbios_rd16(bios, perf + 6);
 211        else
 212                fan->pwm_divisor = 0;
 213
 214        return 0;
 215}
 216