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