Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,41 @@
# SPDX-License-Identifier: MIT
nvkm-y += nvkm/subdev/bios/base.o
nvkm-y += nvkm/subdev/bios/bit.o
nvkm-y += nvkm/subdev/bios/boost.o
nvkm-y += nvkm/subdev/bios/conn.o
nvkm-y += nvkm/subdev/bios/cstep.o
nvkm-y += nvkm/subdev/bios/dcb.o
nvkm-y += nvkm/subdev/bios/disp.o
nvkm-y += nvkm/subdev/bios/dp.o
nvkm-y += nvkm/subdev/bios/extdev.o
nvkm-y += nvkm/subdev/bios/fan.o
nvkm-y += nvkm/subdev/bios/gpio.o
nvkm-y += nvkm/subdev/bios/i2c.o
nvkm-y += nvkm/subdev/bios/iccsense.o
nvkm-y += nvkm/subdev/bios/image.o
nvkm-y += nvkm/subdev/bios/init.o
nvkm-y += nvkm/subdev/bios/mxm.o
nvkm-y += nvkm/subdev/bios/npde.o
nvkm-y += nvkm/subdev/bios/pcir.o
nvkm-y += nvkm/subdev/bios/perf.o
nvkm-y += nvkm/subdev/bios/pll.o
nvkm-y += nvkm/subdev/bios/pmu.o
nvkm-y += nvkm/subdev/bios/power_budget.o
nvkm-y += nvkm/subdev/bios/ramcfg.o
nvkm-y += nvkm/subdev/bios/rammap.o
nvkm-y += nvkm/subdev/bios/shadow.o
nvkm-y += nvkm/subdev/bios/shadowacpi.o
nvkm-y += nvkm/subdev/bios/shadowof.o
nvkm-y += nvkm/subdev/bios/shadowpci.o
nvkm-y += nvkm/subdev/bios/shadowramin.o
nvkm-y += nvkm/subdev/bios/shadowrom.o
nvkm-y += nvkm/subdev/bios/timing.o
nvkm-y += nvkm/subdev/bios/therm.o
nvkm-y += nvkm/subdev/bios/vmap.o
nvkm-y += nvkm/subdev/bios/volt.o
nvkm-y += nvkm/subdev/bios/vpstate.o
nvkm-y += nvkm/subdev/bios/xpio.o
nvkm-y += nvkm/subdev/bios/M0203.o
nvkm-y += nvkm/subdev/bios/M0205.o
nvkm-y += nvkm/subdev/bios/M0209.o
nvkm-y += nvkm/subdev/bios/P0260.o
@@ -0,0 +1,129 @@
/*
* Copyright 2014 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include <subdev/bios.h>
#include <subdev/bios/bit.h>
#include <subdev/bios/M0203.h>
u32
nvbios_M0203Te(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
struct bit_entry bit_M;
u32 data = 0x00000000;
if (!bit_entry(bios, 'M', &bit_M)) {
if (bit_M.version == 2 && bit_M.length > 0x04)
data = nvbios_rd16(bios, bit_M.offset + 0x03);
if (data) {
*ver = nvbios_rd08(bios, data + 0x00);
switch (*ver) {
case 0x10:
*hdr = nvbios_rd08(bios, data + 0x01);
*len = nvbios_rd08(bios, data + 0x02);
*cnt = nvbios_rd08(bios, data + 0x03);
return data;
default:
break;
}
}
}
return 0x00000000;
}
u32
nvbios_M0203Tp(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
struct nvbios_M0203T *info)
{
u32 data = nvbios_M0203Te(bios, ver, hdr, cnt, len);
memset(info, 0x00, sizeof(*info));
switch (!!data * *ver) {
case 0x10:
info->type = nvbios_rd08(bios, data + 0x04);
info->pointer = nvbios_rd16(bios, data + 0x05);
break;
default:
break;
}
return data;
}
u32
nvbios_M0203Ee(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr)
{
u8 cnt, len;
u32 data = nvbios_M0203Te(bios, ver, hdr, &cnt, &len);
if (data && idx < cnt) {
data = data + *hdr + idx * len;
*hdr = len;
return data;
}
return 0x00000000;
}
u32
nvbios_M0203Ep(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr,
struct nvbios_M0203E *info)
{
u32 data = nvbios_M0203Ee(bios, idx, ver, hdr);
memset(info, 0x00, sizeof(*info));
switch (!!data * *ver) {
case 0x10:
info->type = (nvbios_rd08(bios, data + 0x00) & 0x0f) >> 0;
info->strap = (nvbios_rd08(bios, data + 0x00) & 0xf0) >> 4;
info->group = (nvbios_rd08(bios, data + 0x01) & 0x0f) >> 0;
return data;
default:
break;
}
return 0x00000000;
}
u32
nvbios_M0203Em(struct nvkm_bios *bios, u8 ramcfg, u8 *ver, u8 *hdr,
struct nvbios_M0203E *info)
{
struct nvkm_subdev *subdev = &bios->subdev;
struct nvbios_M0203T M0203T;
u8 cnt, len, idx = 0xff;
u32 data;
if (!nvbios_M0203Tp(bios, ver, hdr, &cnt, &len, &M0203T)) {
nvkm_warn(subdev, "M0203T not found\n");
return 0x00000000;
}
while ((data = nvbios_M0203Ep(bios, ++idx, ver, hdr, info))) {
switch (M0203T.type) {
case M0203T_TYPE_RAMCFG:
if (info->strap != ramcfg)
continue;
return data;
default:
nvkm_warn(subdev, "M0203T type %02x\n", M0203T.type);
return 0x00000000;
}
}
return data;
}
@@ -0,0 +1,135 @@
/*
* Copyright 2013 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include <subdev/bios.h>
#include <subdev/bios/bit.h>
#include <subdev/bios/M0205.h>
u32
nvbios_M0205Te(struct nvkm_bios *bios,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz)
{
struct bit_entry bit_M;
u32 data = 0x00000000;
if (!bit_entry(bios, 'M', &bit_M)) {
if (bit_M.version == 2 && bit_M.length > 0x08)
data = nvbios_rd32(bios, bit_M.offset + 0x05);
if (data) {
*ver = nvbios_rd08(bios, data + 0x00);
switch (*ver) {
case 0x10:
*hdr = nvbios_rd08(bios, data + 0x01);
*len = nvbios_rd08(bios, data + 0x02);
*ssz = nvbios_rd08(bios, data + 0x03);
*snr = nvbios_rd08(bios, data + 0x04);
*cnt = nvbios_rd08(bios, data + 0x05);
return data;
default:
break;
}
}
}
return 0x00000000;
}
u32
nvbios_M0205Tp(struct nvkm_bios *bios,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz,
struct nvbios_M0205T *info)
{
u32 data = nvbios_M0205Te(bios, ver, hdr, cnt, len, snr, ssz);
memset(info, 0x00, sizeof(*info));
switch (!!data * *ver) {
case 0x10:
info->freq = nvbios_rd16(bios, data + 0x06);
break;
default:
break;
}
return data;
}
u32
nvbios_M0205Ee(struct nvkm_bios *bios, int idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
u8 snr, ssz;
u32 data = nvbios_M0205Te(bios, ver, hdr, cnt, len, &snr, &ssz);
if (data && idx < *cnt) {
data = data + *hdr + idx * (*len + (snr * ssz));
*hdr = *len;
*cnt = snr;
*len = ssz;
return data;
}
return 0x00000000;
}
u32
nvbios_M0205Ep(struct nvkm_bios *bios, int idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
struct nvbios_M0205E *info)
{
u32 data = nvbios_M0205Ee(bios, idx, ver, hdr, cnt, len);
memset(info, 0x00, sizeof(*info));
switch (!!data * *ver) {
case 0x10:
info->type = nvbios_rd08(bios, data + 0x00) & 0x0f;
return data;
default:
break;
}
return 0x00000000;
}
u32
nvbios_M0205Se(struct nvkm_bios *bios, int ent, int idx, u8 *ver, u8 *hdr)
{
u8 cnt, len;
u32 data = nvbios_M0205Ee(bios, ent, ver, hdr, &cnt, &len);
if (data && idx < cnt) {
data = data + *hdr + idx * len;
*hdr = len;
return data;
}
return 0x00000000;
}
u32
nvbios_M0205Sp(struct nvkm_bios *bios, int ent, int idx, u8 *ver, u8 *hdr,
struct nvbios_M0205S *info)
{
u32 data = nvbios_M0205Se(bios, ent, idx, ver, hdr);
memset(info, 0x00, sizeof(*info));
switch (!!data * *ver) {
case 0x10:
info->data = nvbios_rd08(bios, data + 0x00);
return data;
default:
break;
}
return 0x00000000;
}
@@ -0,0 +1,135 @@
/*
* Copyright 2013 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include <subdev/bios.h>
#include <subdev/bios/bit.h>
#include <subdev/bios/M0209.h>
u32
nvbios_M0209Te(struct nvkm_bios *bios,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz)
{
struct bit_entry bit_M;
u32 data = 0x00000000;
if (!bit_entry(bios, 'M', &bit_M)) {
if (bit_M.version == 2 && bit_M.length > 0x0c)
data = nvbios_rd32(bios, bit_M.offset + 0x09);
if (data) {
*ver = nvbios_rd08(bios, data + 0x00);
switch (*ver) {
case 0x10:
*hdr = nvbios_rd08(bios, data + 0x01);
*len = nvbios_rd08(bios, data + 0x02);
*ssz = nvbios_rd08(bios, data + 0x03);
*snr = 1;
*cnt = nvbios_rd08(bios, data + 0x04);
return data;
default:
break;
}
}
}
return 0x00000000;
}
u32
nvbios_M0209Ee(struct nvkm_bios *bios, int idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
u8 snr, ssz;
u32 data = nvbios_M0209Te(bios, ver, hdr, cnt, len, &snr, &ssz);
if (data && idx < *cnt) {
data = data + *hdr + idx * (*len + (snr * ssz));
*hdr = *len;
*cnt = snr;
*len = ssz;
return data;
}
return 0x00000000;
}
u32
nvbios_M0209Ep(struct nvkm_bios *bios, int idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_M0209E *info)
{
u32 data = nvbios_M0209Ee(bios, idx, ver, hdr, cnt, len);
memset(info, 0x00, sizeof(*info));
switch (!!data * *ver) {
case 0x10:
info->v00_40 = (nvbios_rd08(bios, data + 0x00) & 0x40) >> 6;
info->bits = nvbios_rd08(bios, data + 0x00) & 0x3f;
info->modulo = nvbios_rd08(bios, data + 0x01);
info->v02_40 = (nvbios_rd08(bios, data + 0x02) & 0x40) >> 6;
info->v02_07 = nvbios_rd08(bios, data + 0x02) & 0x07;
info->v03 = nvbios_rd08(bios, data + 0x03);
return data;
default:
break;
}
return 0x00000000;
}
u32
nvbios_M0209Se(struct nvkm_bios *bios, int ent, int idx, u8 *ver, u8 *hdr)
{
u8 cnt, len;
u32 data = nvbios_M0209Ee(bios, ent, ver, hdr, &cnt, &len);
if (data && idx < cnt) {
data = data + *hdr + idx * len;
*hdr = len;
return data;
}
return 0x00000000;
}
u32
nvbios_M0209Sp(struct nvkm_bios *bios, int ent, int idx, u8 *ver, u8 *hdr,
struct nvbios_M0209S *info)
{
struct nvbios_M0209E M0209E;
u8 cnt, len;
u32 data = nvbios_M0209Ep(bios, ent, ver, hdr, &cnt, &len, &M0209E);
if (data) {
u32 i, data = nvbios_M0209Se(bios, ent, idx, ver, hdr);
memset(info, 0x00, sizeof(*info));
switch (!!data * *ver) {
case 0x10:
for (i = 0; i < ARRAY_SIZE(info->data); i++) {
u32 bits = (i % M0209E.modulo) * M0209E.bits;
u32 mask = (1ULL << M0209E.bits) - 1;
u16 off = bits / 8;
u8 mod = bits % 8;
info->data[i] = nvbios_rd32(bios, data + off);
info->data[i] = info->data[i] >> mod;
info->data[i] = info->data[i] & mask;
}
return data;
default:
break;
}
}
return 0x00000000;
}
@@ -0,0 +1,107 @@
/*
* Copyright 2013 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include <subdev/bios.h>
#include <subdev/bios/bit.h>
#include <subdev/bios/P0260.h>
u32
nvbios_P0260Te(struct nvkm_bios *bios,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *xnr, u8 *xsz)
{
struct bit_entry bit_P;
u32 data = 0x00000000;
if (!bit_entry(bios, 'P', &bit_P)) {
if (bit_P.version == 2 && bit_P.length > 0x63)
data = nvbios_rd32(bios, bit_P.offset + 0x60);
if (data) {
*ver = nvbios_rd08(bios, data + 0);
switch (*ver) {
case 0x10:
*hdr = nvbios_rd08(bios, data + 1);
*cnt = nvbios_rd08(bios, data + 2);
*len = 4;
*xnr = nvbios_rd08(bios, data + 3);
*xsz = 4;
return data;
default:
break;
}
}
}
return 0x00000000;
}
u32
nvbios_P0260Ee(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len)
{
u8 hdr, cnt, xnr, xsz;
u32 data = nvbios_P0260Te(bios, ver, &hdr, &cnt, len, &xnr, &xsz);
if (data && idx < cnt)
return data + hdr + (idx * *len);
return 0x00000000;
}
u32
nvbios_P0260Ep(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len,
struct nvbios_P0260E *info)
{
u32 data = nvbios_P0260Ee(bios, idx, ver, len);
memset(info, 0x00, sizeof(*info));
switch (!!data * *ver) {
case 0x10:
info->data = nvbios_rd32(bios, data);
return data;
default:
break;
}
return 0x00000000;
}
u32
nvbios_P0260Xe(struct nvkm_bios *bios, int idx, u8 *ver, u8 *xsz)
{
u8 hdr, cnt, len, xnr;
u32 data = nvbios_P0260Te(bios, ver, &hdr, &cnt, &len, &xnr, xsz);
if (data && idx < xnr)
return data + hdr + (cnt * len) + (idx * *xsz);
return 0x00000000;
}
u32
nvbios_P0260Xp(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr,
struct nvbios_P0260X *info)
{
u32 data = nvbios_P0260Xe(bios, idx, ver, hdr);
memset(info, 0x00, sizeof(*info));
switch (!!data * *ver) {
case 0x10:
info->data = nvbios_rd32(bios, data);
return data;
default:
break;
}
return 0x00000000;
}
@@ -0,0 +1,213 @@
/*
* Copyright 2012 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include "priv.h"
#include <subdev/bios.h>
#include <subdev/bios/bmp.h>
#include <subdev/bios/bit.h>
#include <subdev/bios/image.h>
static bool
nvbios_addr(struct nvkm_bios *bios, u32 *addr, u8 size)
{
u32 p = *addr;
if (*addr >= bios->image0_size && bios->imaged_addr) {
*addr -= bios->image0_size;
*addr += bios->imaged_addr;
}
if (unlikely(*addr + size > bios->size)) {
nvkm_error(&bios->subdev, "OOB %d %08x %08x\n", size, p, *addr);
return false;
}
return true;
}
void *
nvbios_pointer(struct nvkm_bios *bios, u32 addr)
{
if (likely(nvbios_addr(bios, &addr, 0)))
return &bios->data[addr];
return NULL;
}
u8
nvbios_rd08(struct nvkm_bios *bios, u32 addr)
{
if (likely(nvbios_addr(bios, &addr, 1)))
return bios->data[addr];
return 0x00;
}
u16
nvbios_rd16(struct nvkm_bios *bios, u32 addr)
{
if (likely(nvbios_addr(bios, &addr, 2)))
return get_unaligned_le16(&bios->data[addr]);
return 0x0000;
}
u32
nvbios_rd32(struct nvkm_bios *bios, u32 addr)
{
if (likely(nvbios_addr(bios, &addr, 4)))
return get_unaligned_le32(&bios->data[addr]);
return 0x00000000;
}
u8
nvbios_checksum(const u8 *data, int size)
{
u8 sum = 0;
while (size--)
sum += *data++;
return sum;
}
u16
nvbios_findstr(const u8 *data, int size, const char *str, int len)
{
int i, j;
for (i = 0; i <= (size - len); i++) {
for (j = 0; j < len; j++)
if ((char)data[i + j] != str[j])
break;
if (j == len)
return i;
}
return 0;
}
int
nvbios_memcmp(struct nvkm_bios *bios, u32 addr, const char *str, u32 len)
{
unsigned char c1, c2;
while (len--) {
c1 = nvbios_rd08(bios, addr++);
c2 = *(str++);
if (c1 != c2)
return c1 - c2;
}
return 0;
}
int
nvbios_extend(struct nvkm_bios *bios, u32 length)
{
if (bios->size < length) {
u8 *prev = bios->data;
if (!(bios->data = kmalloc(length, GFP_KERNEL))) {
bios->data = prev;
return -ENOMEM;
}
memcpy(bios->data, prev, bios->size);
bios->size = length;
kfree(prev);
return 1;
}
return 0;
}
static void *
nvkm_bios_dtor(struct nvkm_subdev *subdev)
{
struct nvkm_bios *bios = nvkm_bios(subdev);
kfree(bios->data);
return bios;
}
static const struct nvkm_subdev_func
nvkm_bios = {
.dtor = nvkm_bios_dtor,
};
int
nvkm_bios_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
struct nvkm_bios **pbios)
{
struct nvkm_bios *bios;
struct nvbios_image image;
struct bit_entry bit_i;
int ret, idx = 0;
if (!(bios = *pbios = kzalloc_obj(*bios)))
return -ENOMEM;
nvkm_subdev_ctor(&nvkm_bios, device, type, inst, &bios->subdev);
ret = nvbios_shadow(bios);
if (ret)
return ret;
/* Some tables have weird pointers that need adjustment before
* they're dereferenced. I'm not entirely sure why...
*/
if (nvbios_image(bios, idx++, &image)) {
bios->image0_size = image.size;
while (nvbios_image(bios, idx++, &image)) {
if (image.type == 0xe0) {
bios->imaged_addr = image.base;
break;
}
}
}
/* detect type of vbios we're dealing with */
bios->bmp_offset = nvbios_findstr(bios->data, bios->size,
"\xff\x7f""NV\0", 5);
if (bios->bmp_offset) {
nvkm_debug(&bios->subdev, "BMP version %x.%x\n",
bmp_version(bios) >> 8,
bmp_version(bios) & 0xff);
}
bios->bit_offset = nvbios_findstr(bios->data, bios->size,
"\xff\xb8""BIT", 5);
if (bios->bit_offset)
nvkm_debug(&bios->subdev, "BIT signature found\n");
/* determine the vbios version number */
if (!bit_entry(bios, 'i', &bit_i) && bit_i.length >= 4) {
bios->version.major = nvbios_rd08(bios, bit_i.offset + 3);
bios->version.chip = nvbios_rd08(bios, bit_i.offset + 2);
bios->version.minor = nvbios_rd08(bios, bit_i.offset + 1);
bios->version.micro = nvbios_rd08(bios, bit_i.offset + 0);
bios->version.patch = nvbios_rd08(bios, bit_i.offset + 4);
} else
if (bmp_version(bios)) {
bios->version.major = nvbios_rd08(bios, bios->bmp_offset + 13);
bios->version.chip = nvbios_rd08(bios, bios->bmp_offset + 12);
bios->version.minor = nvbios_rd08(bios, bios->bmp_offset + 11);
bios->version.micro = nvbios_rd08(bios, bios->bmp_offset + 10);
}
nvkm_info(&bios->subdev, "version %02x.%02x.%02x.%02x.%02x\n",
bios->version.major, bios->version.chip,
bios->version.minor, bios->version.micro, bios->version.patch);
return 0;
}
@@ -0,0 +1,49 @@
/*
* Copyright 2012 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include <subdev/bios.h>
#include <subdev/bios/bit.h>
int
bit_entry(struct nvkm_bios *bios, u8 id, struct bit_entry *bit)
{
if (likely(bios->bit_offset)) {
u8 entries = nvbios_rd08(bios, bios->bit_offset + 10);
u32 entry = bios->bit_offset + 12;
while (entries--) {
if (nvbios_rd08(bios, entry + 0) == id) {
bit->id = nvbios_rd08(bios, entry + 0);
bit->version = nvbios_rd08(bios, entry + 1);
bit->length = nvbios_rd16(bios, entry + 2);
bit->offset = nvbios_rd16(bios, entry + 4);
return 0;
}
entry += nvbios_rd08(bios, bios->bit_offset + 9);
}
return -ENOENT;
}
return -EINVAL;
}
@@ -0,0 +1,126 @@
/*
* Copyright 2013 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include <subdev/bios.h>
#include <subdev/bios/bit.h>
#include <subdev/bios/boost.h>
u32
nvbios_boostTe(struct nvkm_bios *bios,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz)
{
struct bit_entry bit_P;
u32 boost = 0;
if (!bit_entry(bios, 'P', &bit_P)) {
if (bit_P.version == 2 && bit_P.length >= 0x34)
boost = nvbios_rd32(bios, bit_P.offset + 0x30);
if (boost) {
*ver = nvbios_rd08(bios, boost + 0);
switch (*ver) {
case 0x11:
*hdr = nvbios_rd08(bios, boost + 1);
*cnt = nvbios_rd08(bios, boost + 5);
*len = nvbios_rd08(bios, boost + 2);
*snr = nvbios_rd08(bios, boost + 4);
*ssz = nvbios_rd08(bios, boost + 3);
return boost;
default:
break;
}
}
}
return 0;
}
u32
nvbios_boostEe(struct nvkm_bios *bios, int idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
u8 snr, ssz;
u32 data = nvbios_boostTe(bios, ver, hdr, cnt, len, &snr, &ssz);
if (data && idx < *cnt) {
data = data + *hdr + (idx * (*len + (snr * ssz)));
*hdr = *len;
*cnt = snr;
*len = ssz;
return data;
}
return 0;
}
u32
nvbios_boostEp(struct nvkm_bios *bios, int idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_boostE *info)
{
u32 data = nvbios_boostEe(bios, idx, ver, hdr, cnt, len);
memset(info, 0x00, sizeof(*info));
if (data) {
info->pstate = (nvbios_rd16(bios, data + 0x00) & 0x01e0) >> 5;
info->min = nvbios_rd16(bios, data + 0x02) * 1000;
info->max = nvbios_rd16(bios, data + 0x04) * 1000;
}
return data;
}
u32
nvbios_boostEm(struct nvkm_bios *bios, u8 pstate,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_boostE *info)
{
u32 data, idx = 0;
while ((data = nvbios_boostEp(bios, idx++, ver, hdr, cnt, len, info))) {
if (info->pstate == pstate)
break;
}
return data;
}
u32
nvbios_boostSe(struct nvkm_bios *bios, int idx,
u32 data, u8 *ver, u8 *hdr, u8 cnt, u8 len)
{
if (data && idx < cnt) {
data = data + *hdr + (idx * len);
*hdr = len;
return data;
}
return 0;
}
u32
nvbios_boostSp(struct nvkm_bios *bios, int idx,
u32 data, u8 *ver, u8 *hdr, u8 cnt, u8 len,
struct nvbios_boostS *info)
{
data = nvbios_boostSe(bios, idx, data, ver, hdr, cnt, len);
memset(info, 0x00, sizeof(*info));
if (data) {
info->domain = nvbios_rd08(bios, data + 0x00);
info->percent = nvbios_rd08(bios, data + 0x01);
info->min = nvbios_rd16(bios, data + 0x02) * 1000;
info->max = nvbios_rd16(bios, data + 0x04) * 1000;
}
return data;
}
@@ -0,0 +1,97 @@
/*
* Copyright 2012 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include <subdev/bios.h>
#include <subdev/bios/dcb.h>
#include <subdev/bios/conn.h>
u32
nvbios_connTe(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
u32 dcb = dcb_table(bios, ver, hdr, cnt, len);
if (dcb && *ver >= 0x30 && *hdr >= 0x16) {
u32 data = nvbios_rd16(bios, dcb + 0x14);
if (data) {
*ver = nvbios_rd08(bios, data + 0);
*hdr = nvbios_rd08(bios, data + 1);
*cnt = nvbios_rd08(bios, data + 2);
*len = nvbios_rd08(bios, data + 3);
return data;
}
}
return 0x00000000;
}
u32
nvbios_connTp(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
struct nvbios_connT *info)
{
u32 data = nvbios_connTe(bios, ver, hdr, cnt, len);
memset(info, 0x00, sizeof(*info));
switch (!!data * *ver) {
case 0x30:
case 0x40:
return data;
default:
break;
}
return 0x00000000;
}
u32
nvbios_connEe(struct nvkm_bios *bios, u8 idx, u8 *ver, u8 *len)
{
u8 hdr, cnt;
u32 data = nvbios_connTe(bios, ver, &hdr, &cnt, len);
if (data && idx < cnt)
return data + hdr + (idx * *len);
return 0x00000000;
}
u32
nvbios_connEp(struct nvkm_bios *bios, u8 idx, u8 *ver, u8 *len,
struct nvbios_connE *info)
{
u32 data = nvbios_connEe(bios, idx, ver, len);
memset(info, 0x00, sizeof(*info));
switch (!!data * *ver) {
case 0x30:
case 0x40:
info->type = nvbios_rd08(bios, data + 0x00);
info->location = nvbios_rd08(bios, data + 0x01) & 0x0f;
info->hpd = (nvbios_rd08(bios, data + 0x01) & 0x30) >> 4;
info->dp = (nvbios_rd08(bios, data + 0x01) & 0xc0) >> 6;
if (*len < 4)
return data;
info->hpd |= (nvbios_rd08(bios, data + 0x02) & 0x03) << 2;
info->dp |= nvbios_rd08(bios, data + 0x02) & 0x0c;
info->di = (nvbios_rd08(bios, data + 0x02) & 0xf0) >> 4;
info->hpd |= (nvbios_rd08(bios, data + 0x03) & 0x07) << 4;
info->sr = (nvbios_rd08(bios, data + 0x03) & 0x08) >> 3;
info->lcdid = (nvbios_rd08(bios, data + 0x03) & 0x70) >> 4;
return data;
default:
break;
}
return 0x00000000;
}
@@ -0,0 +1,122 @@
/*
* Copyright 2013 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include <subdev/bios.h>
#include <subdev/bios/bit.h>
#include <subdev/bios/cstep.h>
u32
nvbios_cstepTe(struct nvkm_bios *bios,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *xnr, u8 *xsz)
{
struct bit_entry bit_P;
u32 cstep = 0;
if (!bit_entry(bios, 'P', &bit_P)) {
if (bit_P.version == 2 && bit_P.length >= 0x38)
cstep = nvbios_rd32(bios, bit_P.offset + 0x34);
if (cstep) {
*ver = nvbios_rd08(bios, cstep + 0);
switch (*ver) {
case 0x10:
*hdr = nvbios_rd08(bios, cstep + 1);
*cnt = nvbios_rd08(bios, cstep + 3);
*len = nvbios_rd08(bios, cstep + 2);
*xnr = nvbios_rd08(bios, cstep + 5);
*xsz = nvbios_rd08(bios, cstep + 4);
return cstep;
default:
break;
}
}
}
return 0;
}
u32
nvbios_cstepEe(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr)
{
u8 cnt, len, xnr, xsz;
u32 data = nvbios_cstepTe(bios, ver, hdr, &cnt, &len, &xnr, &xsz);
if (data && idx < cnt) {
data = data + *hdr + (idx * len);
*hdr = len;
return data;
}
return 0;
}
u32
nvbios_cstepEp(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr,
struct nvbios_cstepE *info)
{
u32 data = nvbios_cstepEe(bios, idx, ver, hdr);
memset(info, 0x00, sizeof(*info));
if (data) {
info->pstate = (nvbios_rd16(bios, data + 0x00) & 0x01e0) >> 5;
info->index = nvbios_rd08(bios, data + 0x03);
}
return data;
}
u32
nvbios_cstepEm(struct nvkm_bios *bios, u8 pstate, u8 *ver, u8 *hdr,
struct nvbios_cstepE *info)
{
u32 data, idx = 0;
while ((data = nvbios_cstepEp(bios, idx++, ver, hdr, info))) {
if (info->pstate == pstate)
break;
}
return data;
}
u32
nvbios_cstepXe(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr)
{
u8 cnt, len, xnr, xsz;
u32 data = nvbios_cstepTe(bios, ver, hdr, &cnt, &len, &xnr, &xsz);
if (data && idx < xnr) {
data = data + *hdr + (cnt * len) + (idx * xsz);
*hdr = xsz;
return data;
}
return 0;
}
u32
nvbios_cstepXp(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr,
struct nvbios_cstepX *info)
{
u32 data = nvbios_cstepXe(bios, idx, ver, hdr);
memset(info, 0x00, sizeof(*info));
if (data) {
info->freq = nvbios_rd16(bios, data + 0x00) * 1000;
info->unkn[0] = nvbios_rd08(bios, data + 0x02);
info->unkn[1] = nvbios_rd08(bios, data + 0x03);
info->voltage = nvbios_rd08(bios, data + 0x04);
}
return data;
}
@@ -0,0 +1,235 @@
/*
* Copyright 2012 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include <subdev/bios.h>
#include <subdev/bios/dcb.h>
u16
dcb_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
struct nvkm_subdev *subdev = &bios->subdev;
struct nvkm_device *device = subdev->device;
u16 dcb = 0x0000;
if (device->card_type > NV_04)
dcb = nvbios_rd16(bios, 0x36);
if (!dcb) {
nvkm_warn(subdev, "DCB table not found\n");
return dcb;
}
*ver = nvbios_rd08(bios, dcb);
if (*ver >= 0x42) {
nvkm_warn(subdev, "DCB version 0x%02x unknown\n", *ver);
return 0x0000;
} else
if (*ver >= 0x30) {
if (nvbios_rd32(bios, dcb + 6) == 0x4edcbdcb) {
*hdr = nvbios_rd08(bios, dcb + 1);
*cnt = nvbios_rd08(bios, dcb + 2);
*len = nvbios_rd08(bios, dcb + 3);
return dcb;
}
} else
if (*ver >= 0x20) {
if (nvbios_rd32(bios, dcb + 4) == 0x4edcbdcb) {
u16 i2c = nvbios_rd16(bios, dcb + 2);
*hdr = 8;
*cnt = (i2c - dcb) / 8;
*len = 8;
return dcb;
}
} else
if (*ver >= 0x15) {
if (!nvbios_memcmp(bios, dcb - 7, "DEV_REC", 7)) {
u16 i2c = nvbios_rd16(bios, dcb + 2);
*hdr = 4;
*cnt = (i2c - dcb) / 10;
*len = 10;
return dcb;
}
} else {
/*
* v1.4 (some NV15/16, NV11+) seems the same as v1.5, but
* always has the same single (crt) entry, even when tv-out
* present, so the conclusion is this version cannot really
* be used.
*
* v1.2 tables (some NV6/10, and NV15+) normally have the
* same 5 entries, which are not specific to the card and so
* no use.
*
* v1.2 does have an I2C table that read_dcb_i2c_table can
* handle, but cards exist (nv11 in #14821) with a bad i2c
* table pointer, so use the indices parsed in
* parse_bmp_structure.
*
* v1.1 (NV5+, maybe some NV4) is entirely unhelpful
*/
nvkm_debug(subdev, "DCB contains no useful data\n");
return 0x0000;
}
nvkm_warn(subdev, "DCB header validation failed\n");
return 0x0000;
}
u16
dcb_outp(struct nvkm_bios *bios, u8 idx, u8 *ver, u8 *len)
{
u8 hdr, cnt;
u16 dcb = dcb_table(bios, ver, &hdr, &cnt, len);
if (dcb && idx < cnt)
return dcb + hdr + (idx * *len);
return 0x0000;
}
static inline u16
dcb_outp_hasht(struct dcb_output *outp)
{
return (outp->extdev << 8) | (outp->location << 4) | outp->type;
}
static inline u16
dcb_outp_hashm(struct dcb_output *outp)
{
return (outp->heads << 8) | (outp->link << 6) | outp->or;
}
u16
dcb_outp_parse(struct nvkm_bios *bios, u8 idx, u8 *ver, u8 *len,
struct dcb_output *outp)
{
u16 dcb = dcb_outp(bios, idx, ver, len);
memset(outp, 0x00, sizeof(*outp));
if (dcb) {
if (*ver >= 0x20) {
u32 conn = nvbios_rd32(bios, dcb + 0x00);
outp->or = (conn & 0x0f000000) >> 24;
outp->location = (conn & 0x00300000) >> 20;
outp->bus = (conn & 0x000f0000) >> 16;
outp->connector = (conn & 0x0000f000) >> 12;
outp->heads = (conn & 0x00000f00) >> 8;
outp->i2c_index = (conn & 0x000000f0) >> 4;
outp->type = (conn & 0x0000000f);
outp->link = 0;
} else {
dcb = 0x0000;
}
if (*ver >= 0x40) {
u32 conf = nvbios_rd32(bios, dcb + 0x04);
switch (outp->type) {
case DCB_OUTPUT_DP:
switch (conf & 0x00e00000) {
case 0x00000000: /* 1.62 */
outp->dpconf.link_bw = 0x06;
break;
case 0x00200000: /* 2.7 */
outp->dpconf.link_bw = 0x0a;
break;
case 0x00400000: /* 5.4 */
outp->dpconf.link_bw = 0x14;
break;
case 0x00600000: /* 8.1 */
default:
outp->dpconf.link_bw = 0x1e;
break;
}
switch ((conf & 0x0f000000) >> 24) {
case 0xf:
case 0x4:
outp->dpconf.link_nr = 4;
break;
case 0x3:
case 0x2:
outp->dpconf.link_nr = 2;
break;
case 0x1:
default:
outp->dpconf.link_nr = 1;
break;
}
fallthrough;
case DCB_OUTPUT_TMDS:
case DCB_OUTPUT_LVDS:
outp->link = (conf & 0x00000030) >> 4;
outp->sorconf.link = outp->link; /*XXX*/
outp->extdev = 0x00;
if (outp->location != 0)
outp->extdev = (conf & 0x0000ff00) >> 8;
break;
default:
break;
}
}
outp->hasht = dcb_outp_hasht(outp);
outp->hashm = dcb_outp_hashm(outp);
}
return dcb;
}
u16
dcb_outp_match(struct nvkm_bios *bios, u16 type, u16 mask,
u8 *ver, u8 *len, struct dcb_output *outp)
{
u16 dcb, idx = 0;
while ((dcb = dcb_outp_parse(bios, idx++, ver, len, outp))) {
if ((dcb_outp_hasht(outp) & 0x00ff) == (type & 0x00ff)) {
if ((dcb_outp_hashm(outp) & mask) == mask)
break;
}
}
return dcb;
}
int
dcb_outp_foreach(struct nvkm_bios *bios, void *data,
int (*exec)(struct nvkm_bios *, void *, int, u16))
{
int ret, idx = -1;
u8 ver, len;
u16 outp;
while ((outp = dcb_outp(bios, ++idx, &ver, &len))) {
if (nvbios_rd32(bios, outp) == 0x00000000)
break; /* seen on an NV11 with DCB v1.5 */
if (nvbios_rd32(bios, outp) == 0xffffffff)
break; /* seen on an NV17 with DCB v2.0 */
if (nvbios_rd08(bios, outp) == DCB_OUTPUT_UNUSED)
continue;
if (nvbios_rd08(bios, outp) == DCB_OUTPUT_EOL)
break;
ret = exec(bios, data, idx, outp);
if (ret)
return ret;
}
return 0;
}
@@ -0,0 +1,174 @@
/*
* Copyright 2012 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include <subdev/bios.h>
#include <subdev/bios/bit.h>
#include <subdev/bios/disp.h>
u16
nvbios_disp_table(struct nvkm_bios *bios,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *sub)
{
struct bit_entry U;
if (!bit_entry(bios, 'U', &U)) {
if (U.version == 1) {
u16 data = nvbios_rd16(bios, U.offset);
if (data) {
*ver = nvbios_rd08(bios, data + 0x00);
switch (*ver) {
case 0x20:
case 0x21:
case 0x22:
*hdr = nvbios_rd08(bios, data + 0x01);
*len = nvbios_rd08(bios, data + 0x02);
*cnt = nvbios_rd08(bios, data + 0x03);
*sub = nvbios_rd08(bios, data + 0x04);
return data;
default:
break;
}
}
}
}
return 0x0000;
}
u16
nvbios_disp_entry(struct nvkm_bios *bios, u8 idx, u8 *ver, u8 *len, u8 *sub)
{
u8 hdr, cnt;
u16 data = nvbios_disp_table(bios, ver, &hdr, &cnt, len, sub);
if (data && idx < cnt)
return data + hdr + (idx * *len);
*ver = 0x00;
return 0x0000;
}
u16
nvbios_disp_parse(struct nvkm_bios *bios, u8 idx, u8 *ver, u8 *len, u8 *sub,
struct nvbios_disp *info)
{
u16 data = nvbios_disp_entry(bios, idx, ver, len, sub);
if (data && *len >= 2) {
info->data = nvbios_rd16(bios, data + 0);
return data;
}
return 0x0000;
}
u16
nvbios_outp_entry(struct nvkm_bios *bios, u8 idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
struct nvbios_disp info;
u16 data = nvbios_disp_parse(bios, idx, ver, len, hdr, &info);
if (data) {
*cnt = nvbios_rd08(bios, info.data + 0x05);
*len = 0x06;
data = info.data;
}
return data;
}
u16
nvbios_outp_parse(struct nvkm_bios *bios, u8 idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_outp *info)
{
u16 data = nvbios_outp_entry(bios, idx, ver, hdr, cnt, len);
if (data && *hdr >= 0x0a) {
info->type = nvbios_rd16(bios, data + 0x00);
info->mask = nvbios_rd32(bios, data + 0x02);
if (*ver <= 0x20) /* match any link */
info->mask |= 0x00c0;
info->script[0] = nvbios_rd16(bios, data + 0x06);
info->script[1] = nvbios_rd16(bios, data + 0x08);
info->script[2] = 0x0000;
if (*hdr >= 0x0c)
info->script[2] = nvbios_rd16(bios, data + 0x0a);
return data;
}
return 0x0000;
}
u16
nvbios_outp_match(struct nvkm_bios *bios, u16 type, u16 mask,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_outp *info)
{
u16 data, idx = 0;
while ((data = nvbios_outp_parse(bios, idx++, ver, hdr, cnt, len, info)) || *ver) {
if (data && info->type == type) {
if ((info->mask & mask) == mask)
break;
}
}
return data;
}
u16
nvbios_ocfg_entry(struct nvkm_bios *bios, u16 outp, u8 idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
if (idx < *cnt)
return outp + *hdr + (idx * *len);
return 0x0000;
}
u16
nvbios_ocfg_parse(struct nvkm_bios *bios, u16 outp, u8 idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ocfg *info)
{
u16 data = nvbios_ocfg_entry(bios, outp, idx, ver, hdr, cnt, len);
if (data) {
info->proto = nvbios_rd08(bios, data + 0x00);
info->flags = nvbios_rd16(bios, data + 0x01);
info->clkcmp[0] = nvbios_rd16(bios, data + 0x02);
info->clkcmp[1] = nvbios_rd16(bios, data + 0x04);
}
return data;
}
u16
nvbios_ocfg_match(struct nvkm_bios *bios, u16 outp, u8 proto, u8 flags,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ocfg *info)
{
u16 data, idx = 0;
while ((data = nvbios_ocfg_parse(bios, outp, idx++, ver, hdr, cnt, len, info))) {
if ((info->proto == proto || info->proto == 0xff) &&
(info->flags == flags))
break;
}
return data;
}
u16
nvbios_oclk_match(struct nvkm_bios *bios, u16 cmp, u32 khz)
{
while (cmp) {
if (khz / 10 >= nvbios_rd16(bios, cmp + 0x00))
return nvbios_rd16(bios, cmp + 0x02);
cmp += 0x04;
}
return 0x0000;
}
@@ -0,0 +1,232 @@
/*
* Copyright 2012 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include <subdev/bios.h>
#include <subdev/bios/bit.h>
#include <subdev/bios/dp.h>
u16
nvbios_dp_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
struct bit_entry d;
if (!bit_entry(bios, 'd', &d)) {
if (d.version == 1 && d.length >= 2) {
u16 data = nvbios_rd16(bios, d.offset);
if (data) {
*ver = nvbios_rd08(bios, data + 0x00);
switch (*ver) {
case 0x20:
case 0x21:
case 0x30:
case 0x40:
case 0x41:
case 0x42:
*hdr = nvbios_rd08(bios, data + 0x01);
*len = nvbios_rd08(bios, data + 0x02);
*cnt = nvbios_rd08(bios, data + 0x03);
return data;
default:
break;
}
}
}
}
return 0x0000;
}
static u16
nvbios_dpout_entry(struct nvkm_bios *bios, u8 idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
u16 data = nvbios_dp_table(bios, ver, hdr, cnt, len);
if (data && idx < *cnt) {
u16 outp = nvbios_rd16(bios, data + *hdr + idx * *len);
switch (*ver * !!outp) {
case 0x20:
case 0x21:
case 0x30:
*hdr = nvbios_rd08(bios, data + 0x04);
*len = nvbios_rd08(bios, data + 0x05);
*cnt = nvbios_rd08(bios, outp + 0x04);
break;
case 0x40:
case 0x41:
case 0x42:
*hdr = nvbios_rd08(bios, data + 0x04);
*cnt = 0;
*len = 0;
break;
default:
break;
}
return outp;
}
*ver = 0x00;
return 0x0000;
}
u16
nvbios_dpout_parse(struct nvkm_bios *bios, u8 idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
struct nvbios_dpout *info)
{
u16 data = nvbios_dpout_entry(bios, idx, ver, hdr, cnt, len);
memset(info, 0x00, sizeof(*info));
if (data && *ver) {
info->type = nvbios_rd16(bios, data + 0x00);
info->mask = nvbios_rd16(bios, data + 0x02);
switch (*ver) {
case 0x20:
info->mask |= 0x00c0; /* match any link */
fallthrough;
case 0x21:
case 0x30:
info->flags = nvbios_rd08(bios, data + 0x05);
info->script[0] = nvbios_rd16(bios, data + 0x06);
info->script[1] = nvbios_rd16(bios, data + 0x08);
if (*len >= 0x0c)
info->lnkcmp = nvbios_rd16(bios, data + 0x0a);
if (*len >= 0x0f) {
info->script[2] = nvbios_rd16(bios, data + 0x0c);
info->script[3] = nvbios_rd16(bios, data + 0x0e);
}
if (*len >= 0x11)
info->script[4] = nvbios_rd16(bios, data + 0x10);
break;
case 0x40:
case 0x41:
case 0x42:
info->flags = nvbios_rd08(bios, data + 0x04);
info->script[0] = nvbios_rd16(bios, data + 0x05);
info->script[1] = nvbios_rd16(bios, data + 0x07);
info->lnkcmp = nvbios_rd16(bios, data + 0x09);
info->script[2] = nvbios_rd16(bios, data + 0x0b);
info->script[3] = nvbios_rd16(bios, data + 0x0d);
info->script[4] = nvbios_rd16(bios, data + 0x0f);
break;
default:
data = 0x0000;
break;
}
}
return data;
}
u16
nvbios_dpout_match(struct nvkm_bios *bios, u16 type, u16 mask,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
struct nvbios_dpout *info)
{
u16 data, idx = 0;
while ((data = nvbios_dpout_parse(bios, idx++, ver, hdr, cnt, len, info)) || *ver) {
if (data && info->type == type) {
if ((info->mask & mask) == mask)
break;
}
}
return data;
}
static u16
nvbios_dpcfg_entry(struct nvkm_bios *bios, u16 outp, u8 idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
if (*ver >= 0x40) {
outp = nvbios_dp_table(bios, ver, hdr, cnt, len);
*hdr = *hdr + (*len * * cnt);
*len = nvbios_rd08(bios, outp + 0x06);
*cnt = nvbios_rd08(bios, outp + 0x07) *
nvbios_rd08(bios, outp + 0x05);
}
if (idx < *cnt)
return outp + *hdr + (idx * *len);
return 0x0000;
}
u16
nvbios_dpcfg_parse(struct nvkm_bios *bios, u16 outp, u8 idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
struct nvbios_dpcfg *info)
{
u16 data = nvbios_dpcfg_entry(bios, outp, idx, ver, hdr, cnt, len);
memset(info, 0x00, sizeof(*info));
if (data) {
switch (*ver) {
case 0x20:
case 0x21:
info->dc = nvbios_rd08(bios, data + 0x02);
info->pe = nvbios_rd08(bios, data + 0x03);
info->tx_pu = nvbios_rd08(bios, data + 0x04);
break;
case 0x30:
case 0x40:
case 0x41:
info->pc = nvbios_rd08(bios, data + 0x00);
info->dc = nvbios_rd08(bios, data + 0x01);
info->pe = nvbios_rd08(bios, data + 0x02);
info->tx_pu = nvbios_rd08(bios, data + 0x03);
break;
case 0x42:
info->dc = nvbios_rd08(bios, data + 0x00);
info->pe = nvbios_rd08(bios, data + 0x01);
info->tx_pu = nvbios_rd08(bios, data + 0x02);
break;
default:
data = 0x0000;
break;
}
}
return data;
}
u16
nvbios_dpcfg_match(struct nvkm_bios *bios, u16 outp, u8 pc, u8 vs, u8 pe,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
struct nvbios_dpcfg *info)
{
u8 idx = 0xff;
u16 data;
if (*ver >= 0x30) {
static const u8 vsoff[] = { 0, 4, 7, 9 };
idx = (pc * 10) + vsoff[vs] + pe;
if (*ver >= 0x40 && *ver <= 0x41 && *hdr >= 0x12)
idx += nvbios_rd08(bios, outp + 0x11) * 40;
else
if (*ver >= 0x42)
idx += nvbios_rd08(bios, outp + 0x11) * 10;
} else {
while ((data = nvbios_dpcfg_entry(bios, outp, ++idx,
ver, hdr, cnt, len))) {
if (nvbios_rd08(bios, data + 0x00) == vs &&
nvbios_rd08(bios, data + 0x01) == pe)
break;
}
}
return nvbios_dpcfg_parse(bios, outp, idx, ver, hdr, cnt, len, info);
}
@@ -0,0 +1,110 @@
/*
* Copyright 2012 Nouveau Community
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Martin Peres
*/
#include <subdev/bios.h>
#include <subdev/bios/dcb.h>
#include <subdev/bios/extdev.h>
static u16
extdev_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *len, u8 *cnt)
{
u8 dcb_ver, dcb_hdr, dcb_cnt, dcb_len;
u16 dcb, extdev = 0;
dcb = dcb_table(bios, &dcb_ver, &dcb_hdr, &dcb_cnt, &dcb_len);
if (!dcb || (dcb_ver != 0x30 && dcb_ver != 0x40 && dcb_ver != 0x41))
return 0x0000;
extdev = nvbios_rd16(bios, dcb + 18);
if (!extdev)
return 0x0000;
*ver = nvbios_rd08(bios, extdev + 0);
*hdr = nvbios_rd08(bios, extdev + 1);
*cnt = nvbios_rd08(bios, extdev + 2);
*len = nvbios_rd08(bios, extdev + 3);
return extdev + *hdr;
}
bool
nvbios_extdev_skip_probe(struct nvkm_bios *bios)
{
u8 ver, hdr, len, cnt;
u16 data = extdev_table(bios, &ver, &hdr, &len, &cnt);
if (data && ver == 0x40 && hdr >= 5) {
u8 flags = nvbios_rd08(bios, data - hdr + 4);
if (flags & 1)
return true;
}
return false;
}
static u16
nvbios_extdev_entry(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len)
{
u8 hdr, cnt;
u16 extdev = extdev_table(bios, ver, &hdr, len, &cnt);
if (extdev && idx < cnt)
return extdev + idx * *len;
return 0x0000;
}
static void
extdev_parse_entry(struct nvkm_bios *bios, u16 offset,
struct nvbios_extdev_func *entry)
{
entry->type = nvbios_rd08(bios, offset + 0);
entry->addr = nvbios_rd08(bios, offset + 1);
entry->bus = (nvbios_rd08(bios, offset + 2) >> 4) & 1;
}
int
nvbios_extdev_parse(struct nvkm_bios *bios, int idx,
struct nvbios_extdev_func *func)
{
u8 ver, len;
u16 entry;
if (!(entry = nvbios_extdev_entry(bios, idx, &ver, &len)))
return -EINVAL;
extdev_parse_entry(bios, entry, func);
return 0;
}
int
nvbios_extdev_find(struct nvkm_bios *bios, enum nvbios_extdev_type type,
struct nvbios_extdev_func *func)
{
u8 ver, len, i;
u16 entry;
i = 0;
while ((entry = nvbios_extdev_entry(bios, i++, &ver, &len))) {
extdev_parse_entry(bios, entry, func);
if (func->type == type)
return 0;
}
return -EINVAL;
}
@@ -0,0 +1,94 @@
/*
* Copyright 2014 Martin Peres
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Martin Peres
*/
#include <subdev/bios.h>
#include <subdev/bios/bit.h>
#include <subdev/bios/fan.h>
static u32
nvbios_fan_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
struct bit_entry bit_P;
u32 fan = 0;
if (!bit_entry(bios, 'P', &bit_P)) {
if (bit_P.version == 2 && bit_P.length >= 0x5c)
fan = nvbios_rd32(bios, bit_P.offset + 0x58);
if (fan) {
*ver = nvbios_rd08(bios, fan + 0);
switch (*ver) {
case 0x10:
*hdr = nvbios_rd08(bios, fan + 1);
*len = nvbios_rd08(bios, fan + 2);
*cnt = nvbios_rd08(bios, fan + 3);
return fan;
default:
break;
}
}
}
return 0;
}
static u32
nvbios_fan_entry(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr,
u8 *cnt, u8 *len)
{
u32 data = nvbios_fan_table(bios, ver, hdr, cnt, len);
if (data && idx < *cnt)
return data + *hdr + (idx * (*len));
return 0;
}
u32
nvbios_fan_parse(struct nvkm_bios *bios, struct nvbios_therm_fan *fan)
{
u8 ver, hdr, cnt, len;
u32 data = nvbios_fan_entry(bios, 0, &ver, &hdr, &cnt, &len);
if (data) {
u8 type = nvbios_rd08(bios, data + 0x00);
switch (type) {
case 0:
fan->type = NVBIOS_THERM_FAN_TOGGLE;
break;
case 1:
case 2:
/* TODO: Understand the difference between the two! */
fan->type = NVBIOS_THERM_FAN_PWM;
break;
default:
fan->type = NVBIOS_THERM_FAN_UNK;
}
fan->fan_mode = NVBIOS_THERM_FAN_LINEAR;
fan->min_duty = nvbios_rd08(bios, data + 0x02);
fan->max_duty = nvbios_rd08(bios, data + 0x03);
fan->pwm_freq = nvbios_rd32(bios, data + 0x0b) & 0xffffff;
}
return data;
}
@@ -0,0 +1,150 @@
/*
* Copyright 2012 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include <subdev/bios.h>
#include <subdev/bios/dcb.h>
#include <subdev/bios/gpio.h>
#include <subdev/bios/xpio.h>
u16
dcb_gpio_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
u16 data = 0x0000;
u16 dcb = dcb_table(bios, ver, hdr, cnt, len);
if (dcb) {
if (*ver >= 0x30 && *hdr >= 0x0c)
data = nvbios_rd16(bios, dcb + 0x0a);
else
if (*ver >= 0x22 && nvbios_rd08(bios, dcb - 1) >= 0x13)
data = nvbios_rd16(bios, dcb - 0x0f);
if (data) {
*ver = nvbios_rd08(bios, data + 0x00);
if (*ver < 0x30) {
*hdr = 3;
*cnt = nvbios_rd08(bios, data + 0x02);
*len = nvbios_rd08(bios, data + 0x01);
} else
if (*ver <= 0x41) {
*hdr = nvbios_rd08(bios, data + 0x01);
*cnt = nvbios_rd08(bios, data + 0x02);
*len = nvbios_rd08(bios, data + 0x03);
} else {
data = 0x0000;
}
}
}
return data;
}
u16
dcb_gpio_entry(struct nvkm_bios *bios, int idx, int ent, u8 *ver, u8 *len)
{
u8 hdr, cnt, xver; /* use gpio version for xpio entry parsing */
u16 gpio;
if (!idx--)
gpio = dcb_gpio_table(bios, ver, &hdr, &cnt, len);
else
gpio = dcb_xpio_table(bios, idx, &xver, &hdr, &cnt, len);
if (gpio && ent < cnt)
return gpio + hdr + (ent * *len);
return 0x0000;
}
u16
dcb_gpio_parse(struct nvkm_bios *bios, int idx, int ent, u8 *ver, u8 *len,
struct dcb_gpio_func *gpio)
{
u16 data = dcb_gpio_entry(bios, idx, ent, ver, len);
if (data) {
if (*ver < 0x40) {
u16 info = nvbios_rd16(bios, data);
*gpio = (struct dcb_gpio_func) {
.line = (info & 0x001f) >> 0,
.func = (info & 0x07e0) >> 5,
.log[0] = (info & 0x1800) >> 11,
.log[1] = (info & 0x6000) >> 13,
.param = !!(info & 0x8000),
};
} else
if (*ver < 0x41) {
u32 info = nvbios_rd32(bios, data);
*gpio = (struct dcb_gpio_func) {
.line = (info & 0x0000001f) >> 0,
.func = (info & 0x0000ff00) >> 8,
.log[0] = (info & 0x18000000) >> 27,
.log[1] = (info & 0x60000000) >> 29,
.param = !!(info & 0x80000000),
};
} else {
u32 info = nvbios_rd32(bios, data + 0);
u8 info1 = nvbios_rd32(bios, data + 4);
*gpio = (struct dcb_gpio_func) {
.line = (info & 0x0000003f) >> 0,
.func = (info & 0x0000ff00) >> 8,
.log[0] = (info1 & 0x30) >> 4,
.log[1] = (info1 & 0xc0) >> 6,
.param = !!(info & 0x80000000),
};
}
}
return data;
}
u16
dcb_gpio_match(struct nvkm_bios *bios, int idx, u8 func, u8 line,
u8 *ver, u8 *len, struct dcb_gpio_func *gpio)
{
u8 hdr, cnt, i = 0;
u16 data;
while ((data = dcb_gpio_parse(bios, idx, i++, ver, len, gpio))) {
if ((line == 0xff || line == gpio->line) &&
(func == 0xff || func == gpio->func))
return data;
}
/* DCB 2.2, fixed TVDAC GPIO data */
if ((data = dcb_table(bios, ver, &hdr, &cnt, len))) {
if (*ver >= 0x22 && *ver < 0x30 && func == DCB_GPIO_TVDAC0) {
u8 conf = nvbios_rd08(bios, data - 5);
u8 addr = nvbios_rd08(bios, data - 4);
if (conf & 0x01) {
*gpio = (struct dcb_gpio_func) {
.func = DCB_GPIO_TVDAC0,
.line = addr >> 4,
.log[0] = !!(conf & 0x02),
.log[1] = !(conf & 0x02),
};
*ver = 0x00;
return data;
}
}
}
return 0x0000;
}
@@ -0,0 +1,164 @@
/*
* Copyright 2012 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include <subdev/bios.h>
#include <subdev/bios/dcb.h>
#include <subdev/bios/i2c.h>
u16
dcb_i2c_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
u16 i2c = 0x0000;
u16 dcb = dcb_table(bios, ver, hdr, cnt, len);
if (dcb) {
if (*ver >= 0x15)
i2c = nvbios_rd16(bios, dcb + 2);
if (*ver >= 0x30)
i2c = nvbios_rd16(bios, dcb + 4);
}
if (i2c && *ver >= 0x42) {
nvkm_warn(&bios->subdev, "ccb %02x not supported\n", *ver);
return 0x0000;
}
if (i2c && *ver >= 0x30) {
*ver = nvbios_rd08(bios, i2c + 0);
*hdr = nvbios_rd08(bios, i2c + 1);
*cnt = nvbios_rd08(bios, i2c + 2);
*len = nvbios_rd08(bios, i2c + 3);
} else {
*ver = *ver; /* use DCB version */
*hdr = 0;
*cnt = 16;
*len = 4;
}
return i2c;
}
u16
dcb_i2c_entry(struct nvkm_bios *bios, u8 idx, u8 *ver, u8 *len)
{
u8 hdr, cnt;
u16 i2c = dcb_i2c_table(bios, ver, &hdr, &cnt, len);
if (i2c && idx < cnt)
return i2c + hdr + (idx * *len);
return 0x0000;
}
int
dcb_i2c_parse(struct nvkm_bios *bios, u8 idx, struct dcb_i2c_entry *info)
{
struct nvkm_subdev *subdev = &bios->subdev;
u8 ver, len;
u16 ent = dcb_i2c_entry(bios, idx, &ver, &len);
if (ent) {
if (ver >= 0x41) {
u32 ent_value = nvbios_rd32(bios, ent);
u8 i2c_port = (ent_value >> 0) & 0x1f;
u8 dpaux_port = (ent_value >> 5) & 0x1f;
/* value 0x1f means unused according to DCB 4.x spec */
if (i2c_port == 0x1f && dpaux_port == 0x1f)
info->type = DCB_I2C_UNUSED;
else
info->type = DCB_I2C_PMGR;
} else
if (ver >= 0x30) {
info->type = nvbios_rd08(bios, ent + 0x03);
} else {
info->type = nvbios_rd08(bios, ent + 0x03) & 0x07;
if (info->type == 0x07)
info->type = DCB_I2C_UNUSED;
}
info->drive = DCB_I2C_UNUSED;
info->sense = DCB_I2C_UNUSED;
info->share = DCB_I2C_UNUSED;
info->auxch = DCB_I2C_UNUSED;
switch (info->type) {
case DCB_I2C_NV04_BIT:
info->drive = nvbios_rd08(bios, ent + 0);
info->sense = nvbios_rd08(bios, ent + 1);
return 0;
case DCB_I2C_NV4E_BIT:
info->drive = nvbios_rd08(bios, ent + 1);
return 0;
case DCB_I2C_NVIO_BIT:
info->drive = nvbios_rd08(bios, ent + 0) & 0x0f;
if (nvbios_rd08(bios, ent + 1) & 0x01)
info->share = nvbios_rd08(bios, ent + 1) >> 1;
return 0;
case DCB_I2C_NVIO_AUX:
info->auxch = nvbios_rd08(bios, ent + 0) & 0x0f;
if (nvbios_rd08(bios, ent + 1) & 0x01)
info->share = info->auxch;
return 0;
case DCB_I2C_PMGR:
info->drive = (nvbios_rd16(bios, ent + 0) & 0x01f) >> 0;
if (info->drive == 0x1f)
info->drive = DCB_I2C_UNUSED;
info->auxch = (nvbios_rd16(bios, ent + 0) & 0x3e0) >> 5;
if (info->auxch == 0x1f)
info->auxch = DCB_I2C_UNUSED;
info->share = info->auxch;
return 0;
case DCB_I2C_UNUSED:
return 0;
default:
nvkm_warn(subdev, "unknown i2c type %d\n", info->type);
info->type = DCB_I2C_UNUSED;
return 0;
}
}
if (bios->bmp_offset && idx < 2) {
/* BMP (from v4.0 has i2c info in the structure, it's in a
* fixed location on earlier VBIOS
*/
if (nvbios_rd08(bios, bios->bmp_offset + 5) < 4)
ent = 0x0048;
else
ent = 0x0036 + bios->bmp_offset;
if (idx == 0) {
info->drive = nvbios_rd08(bios, ent + 4);
if (!info->drive) info->drive = 0x3f;
info->sense = nvbios_rd08(bios, ent + 5);
if (!info->sense) info->sense = 0x3e;
} else
if (idx == 1) {
info->drive = nvbios_rd08(bios, ent + 6);
if (!info->drive) info->drive = 0x37;
info->sense = nvbios_rd08(bios, ent + 7);
if (!info->sense) info->sense = 0x36;
}
info->type = DCB_I2C_NV04_BIT;
info->share = DCB_I2C_UNUSED;
return 0;
}
return -ENOENT;
}
@@ -0,0 +1,128 @@
/*
* Copyright 2015 Martin Peres
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Martin Peres
*/
#include <subdev/bios.h>
#include <subdev/bios/bit.h>
#include <subdev/bios/extdev.h>
#include <subdev/bios/iccsense.h>
static u32
nvbios_iccsense_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt,
u8 *len)
{
struct bit_entry bit_P;
u32 iccsense;
if (bit_entry(bios, 'P', &bit_P) || bit_P.version != 2 ||
bit_P.length < 0x2c)
return 0;
iccsense = nvbios_rd32(bios, bit_P.offset + 0x28);
if (!iccsense)
return 0;
*ver = nvbios_rd08(bios, iccsense + 0);
switch (*ver) {
case 0x10:
case 0x20:
*hdr = nvbios_rd08(bios, iccsense + 1);
*len = nvbios_rd08(bios, iccsense + 2);
*cnt = nvbios_rd08(bios, iccsense + 3);
return iccsense;
default:
break;
}
return 0;
}
int
nvbios_iccsense_parse(struct nvkm_bios *bios, struct nvbios_iccsense *iccsense)
{
struct nvkm_subdev *subdev = &bios->subdev;
u8 ver, hdr, cnt, len, i;
u32 table, entry;
table = nvbios_iccsense_table(bios, &ver, &hdr, &cnt, &len);
if (!table || !cnt)
return -EINVAL;
if (ver != 0x10 && ver != 0x20) {
nvkm_error(subdev, "ICCSENSE version 0x%02x unknown\n", ver);
return -EINVAL;
}
iccsense->nr_entry = cnt;
iccsense->rail = kmalloc_objs(struct pwr_rail_t, cnt);
if (!iccsense->rail)
return -ENOMEM;
for (i = 0; i < cnt; ++i) {
struct nvbios_extdev_func extdev;
struct pwr_rail_t *rail = &iccsense->rail[i];
u8 res_start = 0;
int r;
entry = table + hdr + i * len;
switch(ver) {
case 0x10:
if ((nvbios_rd08(bios, entry + 0x1) & 0xf8) == 0xf8)
rail->mode = 1;
else
rail->mode = 0;
rail->extdev_id = nvbios_rd08(bios, entry + 0x2);
res_start = 0x3;
break;
case 0x20:
rail->mode = nvbios_rd08(bios, entry);
rail->extdev_id = nvbios_rd08(bios, entry + 0x1);
res_start = 0x5;
break;
}
if (nvbios_extdev_parse(bios, rail->extdev_id, &extdev))
continue;
switch (extdev.type) {
case NVBIOS_EXTDEV_INA209:
case NVBIOS_EXTDEV_INA219:
rail->resistor_count = 1;
break;
case NVBIOS_EXTDEV_INA3221:
rail->resistor_count = 3;
break;
default:
rail->resistor_count = 0;
break;
}
for (r = 0; r < rail->resistor_count; ++r) {
rail->resistors[r].mohm = nvbios_rd08(bios, entry + res_start + r * 2);
rail->resistors[r].enabled = !(nvbios_rd08(bios, entry + res_start + r * 2 + 1) & 0x40);
}
rail->config = nvbios_rd16(bios, entry + res_start + rail->resistor_count * 2);
}
return 0;
}
@@ -0,0 +1,83 @@
/*
* Copyright 2014 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
#include <subdev/bios.h>
#include <subdev/bios/image.h>
#include <subdev/bios/pcir.h>
#include <subdev/bios/npde.h>
static bool
nvbios_imagen(struct nvkm_bios *bios, struct nvbios_image *image)
{
struct nvkm_subdev *subdev = &bios->subdev;
struct nvbios_pcirT pcir;
struct nvbios_npdeT npde;
u8 ver;
u16 hdr;
u32 data;
switch ((data = nvbios_rd16(bios, image->base + 0x00))) {
case 0xaa55:
case 0xbb77:
case 0x4e56: /* NV */
break;
default:
nvkm_debug(subdev, "%08x: ROM signature (%04x) unknown\n",
image->base, data);
return false;
}
if (!(data = nvbios_pcirTp(bios, image->base, &ver, &hdr, &pcir)))
return false;
image->size = pcir.image_size;
image->type = pcir.image_type;
image->last = pcir.last;
if (image->type != 0x70) {
if (!(data = nvbios_npdeTp(bios, image->base, &npde)))
return true;
image->size = npde.image_size;
image->last = npde.last;
} else {
image->last = true;
}
return true;
}
bool
nvbios_image(struct nvkm_bios *bios, int idx, struct nvbios_image *image)
{
u32 imaged_addr = bios->imaged_addr;
memset(image, 0x00, sizeof(*image));
bios->imaged_addr = 0;
do {
image->base += image->size;
if (image->last || !nvbios_imagen(bios, image)) {
bios->imaged_addr = imaged_addr;
return false;
}
} while(idx--);
bios->imaged_addr = imaged_addr;
return true;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,137 @@
/*
* Copyright 2012 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include <subdev/bios.h>
#include <subdev/bios/bit.h>
#include <subdev/bios/mxm.h>
u16
mxm_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr)
{
struct nvkm_subdev *subdev = &bios->subdev;
struct bit_entry x;
if (bit_entry(bios, 'x', &x)) {
nvkm_debug(subdev, "BIT 'x' table not present\n");
return 0x0000;
}
*ver = x.version;
*hdr = x.length;
if (*ver != 1 || *hdr < 3) {
nvkm_warn(subdev, "BIT 'x' table %d/%d unknown\n", *ver, *hdr);
return 0x0000;
}
return x.offset;
}
/* These map MXM v2.x digital connection values to the appropriate SOR/link,
* hopefully they're correct for all boards within the same chipset...
*
* MXM v3.x VBIOS are nicer and provide pointers to these tables.
*/
static u8 g84_sor_map[16] = {
0x00, 0x12, 0x22, 0x11, 0x32, 0x31, 0x11, 0x31,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static u8 g92_sor_map[16] = {
0x00, 0x12, 0x22, 0x11, 0x32, 0x31, 0x11, 0x31,
0x11, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static u8 g94_sor_map[16] = {
0x00, 0x14, 0x24, 0x11, 0x34, 0x31, 0x11, 0x31,
0x11, 0x31, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00
};
static u8 g98_sor_map[16] = {
0x00, 0x14, 0x12, 0x11, 0x00, 0x31, 0x11, 0x31,
0x11, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
u8
mxm_sor_map(struct nvkm_bios *bios, u8 conn)
{
struct nvkm_subdev *subdev = &bios->subdev;
u8 ver, hdr;
u16 mxm = mxm_table(bios, &ver, &hdr);
if (mxm && hdr >= 6) {
u16 map = nvbios_rd16(bios, mxm + 4);
if (map) {
ver = nvbios_rd08(bios, map);
if (ver == 0x10 || ver == 0x11) {
if (conn < nvbios_rd08(bios, map + 3)) {
map += nvbios_rd08(bios, map + 1);
map += conn;
return nvbios_rd08(bios, map);
}
return 0x00;
}
nvkm_warn(subdev, "unknown sor map v%02x\n", ver);
}
}
if (bios->version.chip == 0x84 || bios->version.chip == 0x86)
return g84_sor_map[conn];
if (bios->version.chip == 0x92)
return g92_sor_map[conn];
if (bios->version.chip == 0x94 || bios->version.chip == 0x96)
return g94_sor_map[conn];
if (bios->version.chip == 0x98)
return g98_sor_map[conn];
nvkm_warn(subdev, "missing sor map\n");
return 0x00;
}
u8
mxm_ddc_map(struct nvkm_bios *bios, u8 port)
{
struct nvkm_subdev *subdev = &bios->subdev;
u8 ver, hdr;
u16 mxm = mxm_table(bios, &ver, &hdr);
if (mxm && hdr >= 8) {
u16 map = nvbios_rd16(bios, mxm + 6);
if (map) {
ver = nvbios_rd08(bios, map);
if (ver == 0x10) {
if (port < nvbios_rd08(bios, map + 3)) {
map += nvbios_rd08(bios, map + 1);
map += port;
return nvbios_rd08(bios, map);
}
return 0x00;
}
nvkm_warn(subdev, "unknown ddc map v%02x\n", ver);
}
}
/* v2.x: directly write port as dcb i2cidx */
return (port << 4) | port;
}
@@ -0,0 +1,59 @@
/*
* Copyright 2014 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
#include <subdev/bios.h>
#include <subdev/bios/npde.h>
#include <subdev/bios/pcir.h>
u32
nvbios_npdeTe(struct nvkm_bios *bios, u32 base)
{
struct nvbios_pcirT pcir;
u8 ver; u16 hdr;
u32 data = nvbios_pcirTp(bios, base, &ver, &hdr, &pcir);
if (data = (data + hdr + 0x0f) & ~0x0f, data) {
switch (nvbios_rd32(bios, data + 0x00)) {
case 0x4544504e: /* NPDE */
break;
default:
nvkm_debug(&bios->subdev,
"%08x: NPDE signature (%08x) unknown\n",
data, nvbios_rd32(bios, data + 0x00));
data = 0;
break;
}
}
return data;
}
u32
nvbios_npdeTp(struct nvkm_bios *bios, u32 base, struct nvbios_npdeT *info)
{
u32 data = nvbios_npdeTe(bios, base);
memset(info, 0x00, sizeof(*info));
if (data) {
info->image_size = nvbios_rd16(bios, data + 0x08) * 512;
info->last = nvbios_rd08(bios, data + 0x0a) & 0x80;
}
return data;
}
@@ -0,0 +1,69 @@
/*
* Copyright 2014 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
#include <subdev/bios.h>
#include <subdev/bios/pcir.h>
u32
nvbios_pcirTe(struct nvkm_bios *bios, u32 base, u8 *ver, u16 *hdr)
{
u32 data = nvbios_rd16(bios, base + 0x18);
if (data) {
data += base;
switch (nvbios_rd32(bios, data + 0x00)) {
case 0x52494350: /* PCIR */
case 0x53494752: /* RGIS */
case 0x5344504e: /* NPDS */
*hdr = nvbios_rd16(bios, data + 0x0a);
*ver = nvbios_rd08(bios, data + 0x0c);
break;
default:
nvkm_debug(&bios->subdev,
"%08x: PCIR signature (%08x) unknown\n",
data, nvbios_rd32(bios, data + 0x00));
data = 0;
break;
}
}
return data;
}
u32
nvbios_pcirTp(struct nvkm_bios *bios, u32 base, u8 *ver, u16 *hdr,
struct nvbios_pcirT *info)
{
u32 data = nvbios_pcirTe(bios, base, ver, hdr);
memset(info, 0x00, sizeof(*info));
if (data) {
info->vendor_id = nvbios_rd16(bios, data + 0x04);
info->device_id = nvbios_rd16(bios, data + 0x06);
info->class_code[0] = nvbios_rd08(bios, data + 0x0d);
info->class_code[1] = nvbios_rd08(bios, data + 0x0e);
info->class_code[2] = nvbios_rd08(bios, data + 0x0f);
info->image_size = nvbios_rd16(bios, data + 0x10) * 512;
info->image_rev = nvbios_rd16(bios, data + 0x12);
info->image_type = nvbios_rd08(bios, data + 0x14);
info->last = nvbios_rd08(bios, data + 0x15) & 0x80;
}
return data;
}
@@ -0,0 +1,216 @@
/*
* Copyright 2012 Nouveau Community
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Martin Peres
*/
#include <subdev/bios.h>
#include <subdev/bios/bit.h>
#include <subdev/bios/perf.h>
#include <subdev/pci.h>
u32
nvbios_perf_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr,
u8 *cnt, u8 *len, u8 *snr, u8 *ssz)
{
struct bit_entry bit_P;
u32 perf = 0;
if (!bit_entry(bios, 'P', &bit_P)) {
if (bit_P.version <= 2) {
perf = nvbios_rd32(bios, bit_P.offset + 0);
if (perf) {
*ver = nvbios_rd08(bios, perf + 0);
*hdr = nvbios_rd08(bios, perf + 1);
if (*ver >= 0x40 && *ver < 0x41) {
*cnt = nvbios_rd08(bios, perf + 5);
*len = nvbios_rd08(bios, perf + 2);
*snr = nvbios_rd08(bios, perf + 4);
*ssz = nvbios_rd08(bios, perf + 3);
return perf;
} else
if (*ver >= 0x20 && *ver < 0x40) {
*cnt = nvbios_rd08(bios, perf + 2);
*len = nvbios_rd08(bios, perf + 3);
*snr = nvbios_rd08(bios, perf + 4);
*ssz = nvbios_rd08(bios, perf + 5);
return perf;
}
}
}
}
if (bios->bmp_offset) {
if (nvbios_rd08(bios, bios->bmp_offset + 6) >= 0x25) {
perf = nvbios_rd16(bios, bios->bmp_offset + 0x94);
if (perf) {
*hdr = nvbios_rd08(bios, perf + 0);
*ver = nvbios_rd08(bios, perf + 1);
*cnt = nvbios_rd08(bios, perf + 2);
*len = nvbios_rd08(bios, perf + 3);
*snr = 0;
*ssz = 0;
return perf;
}
}
}
return 0;
}
u32
nvbios_perf_entry(struct nvkm_bios *bios, int idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
u8 snr, ssz;
u32 perf = nvbios_perf_table(bios, ver, hdr, cnt, len, &snr, &ssz);
if (perf && idx < *cnt) {
perf = perf + *hdr + (idx * (*len + (snr * ssz)));
*hdr = *len;
*cnt = snr;
*len = ssz;
return perf;
}
return 0;
}
u32
nvbios_perfEp(struct nvkm_bios *bios, int idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_perfE *info)
{
u32 perf = nvbios_perf_entry(bios, idx, ver, hdr, cnt, len);
memset(info, 0x00, sizeof(*info));
info->pstate = nvbios_rd08(bios, perf + 0x00);
switch (!!perf * *ver) {
case 0x12:
case 0x13:
case 0x14:
info->core = nvbios_rd32(bios, perf + 0x01) * 10;
info->memory = nvbios_rd32(bios, perf + 0x05) * 20;
info->fanspeed = nvbios_rd08(bios, perf + 0x37);
if (*hdr > 0x38)
info->voltage = nvbios_rd08(bios, perf + 0x38);
break;
case 0x21:
case 0x23:
case 0x24:
info->fanspeed = nvbios_rd08(bios, perf + 0x04);
info->voltage = nvbios_rd08(bios, perf + 0x05);
info->shader = nvbios_rd16(bios, perf + 0x06) * 1000;
info->core = info->shader + (signed char)
nvbios_rd08(bios, perf + 0x08) * 1000;
switch (bios->subdev.device->chipset) {
case 0x49:
case 0x4b:
info->memory = nvbios_rd16(bios, perf + 0x0b) * 1000;
break;
default:
info->memory = nvbios_rd16(bios, perf + 0x0b) * 2000;
break;
}
break;
case 0x25:
info->fanspeed = nvbios_rd08(bios, perf + 0x04);
info->voltage = nvbios_rd08(bios, perf + 0x05);
info->core = nvbios_rd16(bios, perf + 0x06) * 1000;
info->shader = nvbios_rd16(bios, perf + 0x0a) * 1000;
info->memory = nvbios_rd16(bios, perf + 0x0c) * 1000;
break;
case 0x30:
info->script = nvbios_rd16(bios, perf + 0x02);
fallthrough;
case 0x35:
info->fanspeed = nvbios_rd08(bios, perf + 0x06);
info->voltage = nvbios_rd08(bios, perf + 0x07);
info->core = nvbios_rd16(bios, perf + 0x08) * 1000;
info->shader = nvbios_rd16(bios, perf + 0x0a) * 1000;
info->memory = nvbios_rd16(bios, perf + 0x0c) * 1000;
info->vdec = nvbios_rd16(bios, perf + 0x10) * 1000;
info->disp = nvbios_rd16(bios, perf + 0x14) * 1000;
break;
case 0x40:
info->voltage = nvbios_rd08(bios, perf + 0x02);
switch (nvbios_rd08(bios, perf + 0xb) & 0x3) {
case 0:
info->pcie_speed = NVKM_PCIE_SPEED_5_0;
break;
case 3:
case 1:
info->pcie_speed = NVKM_PCIE_SPEED_2_5;
break;
case 2:
info->pcie_speed = NVKM_PCIE_SPEED_8_0;
break;
default:
break;
}
info->pcie_width = 0xff;
break;
default:
return 0;
}
return perf;
}
u32
nvbios_perfSe(struct nvkm_bios *bios, u32 perfE, int idx,
u8 *ver, u8 *hdr, u8 cnt, u8 len)
{
u32 data = 0x00000000;
if (idx < cnt) {
data = perfE + *hdr + (idx * len);
*hdr = len;
}
return data;
}
u32
nvbios_perfSp(struct nvkm_bios *bios, u32 perfE, int idx,
u8 *ver, u8 *hdr, u8 cnt, u8 len,
struct nvbios_perfS *info)
{
u32 data = nvbios_perfSe(bios, perfE, idx, ver, hdr, cnt, len);
memset(info, 0x00, sizeof(*info));
switch (!!data * *ver) {
case 0x40:
info->v40.freq = (nvbios_rd16(bios, data + 0x00) & 0x3fff) * 1000;
break;
default:
break;
}
return data;
}
int
nvbios_perf_fan_parse(struct nvkm_bios *bios,
struct nvbios_perf_fan *fan)
{
u8 ver, hdr, cnt, len, snr, ssz;
u32 perf = nvbios_perf_table(bios, &ver, &hdr, &cnt, &len, &snr, &ssz);
if (!perf)
return -ENODEV;
if (ver >= 0x20 && ver < 0x40 && hdr > 6)
fan->pwm_divisor = nvbios_rd16(bios, perf + 6);
else
fan->pwm_divisor = 0;
return 0;
}
@@ -0,0 +1,440 @@
/*
* Copyright 2005-2006 Erik Waling
* Copyright 2006 Stephane Marchesin
* Copyright 2007-2009 Stuart Bennett
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <subdev/bios.h>
#include <subdev/bios/bit.h>
#include <subdev/bios/bmp.h>
#include <subdev/bios/pll.h>
#include <subdev/vga.h>
struct pll_mapping {
u8 type;
u32 reg;
};
static struct pll_mapping
nv04_pll_mapping[] = {
{ PLL_CORE , 0x680500 },
{ PLL_MEMORY, 0x680504 },
{ PLL_VPLL0 , 0x680508 },
{ PLL_VPLL1 , 0x680520 },
{}
};
static struct pll_mapping
nv40_pll_mapping[] = {
{ PLL_CORE , 0x004000 },
{ PLL_MEMORY, 0x004020 },
{ PLL_VPLL0 , 0x680508 },
{ PLL_VPLL1 , 0x680520 },
{}
};
static struct pll_mapping
nv50_pll_mapping[] = {
{ PLL_CORE , 0x004028 },
{ PLL_SHADER, 0x004020 },
{ PLL_UNK03 , 0x004000 },
{ PLL_MEMORY, 0x004008 },
{ PLL_UNK40 , 0x00e810 },
{ PLL_UNK41 , 0x00e818 },
{ PLL_UNK42 , 0x00e824 },
{ PLL_VPLL0 , 0x614100 },
{ PLL_VPLL1 , 0x614900 },
{}
};
static struct pll_mapping
g84_pll_mapping[] = {
{ PLL_CORE , 0x004028 },
{ PLL_SHADER, 0x004020 },
{ PLL_MEMORY, 0x004008 },
{ PLL_VDEC , 0x004030 },
{ PLL_UNK41 , 0x00e818 },
{ PLL_VPLL0 , 0x614100 },
{ PLL_VPLL1 , 0x614900 },
{}
};
static u32
pll_limits_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
struct bit_entry bit_C;
u32 data = 0x0000;
if (!bit_entry(bios, 'C', &bit_C)) {
if (bit_C.version == 1 && bit_C.length >= 10)
data = nvbios_rd16(bios, bit_C.offset + 8);
if (bit_C.version == 2 && bit_C.length >= 4)
data = nvbios_rd32(bios, bit_C.offset + 0);
if (data) {
*ver = nvbios_rd08(bios, data + 0);
*hdr = nvbios_rd08(bios, data + 1);
*len = nvbios_rd08(bios, data + 2);
*cnt = nvbios_rd08(bios, data + 3);
return data;
}
}
if (bmp_version(bios) >= 0x0524) {
data = nvbios_rd16(bios, bios->bmp_offset + 142);
if (data) {
*ver = nvbios_rd08(bios, data + 0);
*hdr = 1;
*cnt = 1;
*len = 0x18;
return data;
}
}
*ver = 0x00;
return data;
}
static struct pll_mapping *
pll_map(struct nvkm_bios *bios)
{
struct nvkm_device *device = bios->subdev.device;
switch (device->card_type) {
case NV_04:
case NV_10:
case NV_11:
case NV_20:
case NV_30:
return nv04_pll_mapping;
case NV_40:
return nv40_pll_mapping;
case NV_50:
if (device->chipset == 0x50)
return nv50_pll_mapping;
else
if (device->chipset < 0xa3 ||
device->chipset == 0xaa ||
device->chipset == 0xac)
return g84_pll_mapping;
fallthrough;
default:
return NULL;
}
}
static u32
pll_map_reg(struct nvkm_bios *bios, u32 reg, u32 *type, u8 *ver, u8 *len)
{
struct pll_mapping *map;
u8 hdr, cnt;
u32 data;
data = pll_limits_table(bios, ver, &hdr, &cnt, len);
if (data && *ver >= 0x30) {
data += hdr;
while (cnt--) {
if (nvbios_rd32(bios, data + 3) == reg) {
*type = nvbios_rd08(bios, data + 0);
return data;
}
data += *len;
}
return 0x0000;
}
map = pll_map(bios);
while (map && map->reg) {
if (map->reg == reg && *ver >= 0x20) {
u32 addr = (data += hdr);
*type = map->type;
while (cnt--) {
if (nvbios_rd32(bios, data) == map->reg)
return data;
data += *len;
}
return addr;
} else
if (map->reg == reg) {
*type = map->type;
return data + 1;
}
map++;
}
return 0x0000;
}
static u32
pll_map_type(struct nvkm_bios *bios, u8 type, u32 *reg, u8 *ver, u8 *len)
{
struct pll_mapping *map;
u8 hdr, cnt;
u32 data;
data = pll_limits_table(bios, ver, &hdr, &cnt, len);
if (data && *ver >= 0x30) {
data += hdr;
while (cnt--) {
if (nvbios_rd08(bios, data + 0) == type) {
if (*ver < 0x50)
*reg = nvbios_rd32(bios, data + 3);
else
*reg = 0;
return data;
}
data += *len;
}
return 0x0000;
}
map = pll_map(bios);
while (map && map->reg) {
if (map->type == type && *ver >= 0x20) {
u32 addr = (data += hdr);
*reg = map->reg;
while (cnt--) {
if (nvbios_rd32(bios, data) == map->reg)
return data;
data += *len;
}
return addr;
} else
if (map->type == type) {
*reg = map->reg;
return data + 1;
}
map++;
}
return 0x0000;
}
int
nvbios_pll_parse(struct nvkm_bios *bios, u32 type, struct nvbios_pll *info)
{
struct nvkm_subdev *subdev = &bios->subdev;
struct nvkm_device *device = subdev->device;
u8 ver, len;
u32 reg = type;
u32 data;
if (type > PLL_MAX) {
reg = type;
data = pll_map_reg(bios, reg, &type, &ver, &len);
} else {
data = pll_map_type(bios, type, &reg, &ver, &len);
}
if (ver && !data)
return -ENOENT;
memset(info, 0, sizeof(*info));
info->type = type;
info->reg = reg;
switch (ver) {
case 0x00:
break;
case 0x10:
case 0x11:
info->vco1.min_freq = nvbios_rd32(bios, data + 0);
info->vco1.max_freq = nvbios_rd32(bios, data + 4);
info->vco2.min_freq = nvbios_rd32(bios, data + 8);
info->vco2.max_freq = nvbios_rd32(bios, data + 12);
info->vco1.min_inputfreq = nvbios_rd32(bios, data + 16);
info->vco2.min_inputfreq = nvbios_rd32(bios, data + 20);
info->vco1.max_inputfreq = INT_MAX;
info->vco2.max_inputfreq = INT_MAX;
info->max_p = 0x7;
info->max_p_usable = 0x6;
/* these values taken from nv30/31/36 */
switch (bios->version.chip) {
case 0x36:
info->vco1.min_n = 0x5;
break;
default:
info->vco1.min_n = 0x1;
break;
}
info->vco1.max_n = 0xff;
info->vco1.min_m = 0x1;
info->vco1.max_m = 0xd;
/*
* On nv30, 31, 36 (i.e. all cards with two stage PLLs with this
* table version (apart from nv35)), N2 is compared to
* maxN2 (0x46) and 10 * maxM2 (0x4), so set maxN2 to 0x28 and
* save a comparison
*/
info->vco2.min_n = 0x4;
switch (bios->version.chip) {
case 0x30:
case 0x35:
info->vco2.max_n = 0x1f;
break;
default:
info->vco2.max_n = 0x28;
break;
}
info->vco2.min_m = 0x1;
info->vco2.max_m = 0x4;
break;
case 0x20:
case 0x21:
info->vco1.min_freq = nvbios_rd16(bios, data + 4) * 1000;
info->vco1.max_freq = nvbios_rd16(bios, data + 6) * 1000;
info->vco2.min_freq = nvbios_rd16(bios, data + 8) * 1000;
info->vco2.max_freq = nvbios_rd16(bios, data + 10) * 1000;
info->vco1.min_inputfreq = nvbios_rd16(bios, data + 12) * 1000;
info->vco2.min_inputfreq = nvbios_rd16(bios, data + 14) * 1000;
info->vco1.max_inputfreq = nvbios_rd16(bios, data + 16) * 1000;
info->vco2.max_inputfreq = nvbios_rd16(bios, data + 18) * 1000;
info->vco1.min_n = nvbios_rd08(bios, data + 20);
info->vco1.max_n = nvbios_rd08(bios, data + 21);
info->vco1.min_m = nvbios_rd08(bios, data + 22);
info->vco1.max_m = nvbios_rd08(bios, data + 23);
info->vco2.min_n = nvbios_rd08(bios, data + 24);
info->vco2.max_n = nvbios_rd08(bios, data + 25);
info->vco2.min_m = nvbios_rd08(bios, data + 26);
info->vco2.max_m = nvbios_rd08(bios, data + 27);
info->max_p = nvbios_rd08(bios, data + 29);
info->max_p_usable = info->max_p;
if (bios->version.chip < 0x60)
info->max_p_usable = 0x6;
info->bias_p = nvbios_rd08(bios, data + 30);
if (len > 0x22)
info->refclk = nvbios_rd32(bios, data + 31);
break;
case 0x30:
data = nvbios_rd16(bios, data + 1);
info->vco1.min_freq = nvbios_rd16(bios, data + 0) * 1000;
info->vco1.max_freq = nvbios_rd16(bios, data + 2) * 1000;
info->vco2.min_freq = nvbios_rd16(bios, data + 4) * 1000;
info->vco2.max_freq = nvbios_rd16(bios, data + 6) * 1000;
info->vco1.min_inputfreq = nvbios_rd16(bios, data + 8) * 1000;
info->vco2.min_inputfreq = nvbios_rd16(bios, data + 10) * 1000;
info->vco1.max_inputfreq = nvbios_rd16(bios, data + 12) * 1000;
info->vco2.max_inputfreq = nvbios_rd16(bios, data + 14) * 1000;
info->vco1.min_n = nvbios_rd08(bios, data + 16);
info->vco1.max_n = nvbios_rd08(bios, data + 17);
info->vco1.min_m = nvbios_rd08(bios, data + 18);
info->vco1.max_m = nvbios_rd08(bios, data + 19);
info->vco2.min_n = nvbios_rd08(bios, data + 20);
info->vco2.max_n = nvbios_rd08(bios, data + 21);
info->vco2.min_m = nvbios_rd08(bios, data + 22);
info->vco2.max_m = nvbios_rd08(bios, data + 23);
info->max_p_usable = info->max_p = nvbios_rd08(bios, data + 25);
info->bias_p = nvbios_rd08(bios, data + 27);
info->refclk = nvbios_rd32(bios, data + 28);
break;
case 0x40:
info->refclk = nvbios_rd16(bios, data + 9) * 1000;
data = nvbios_rd16(bios, data + 1);
info->vco1.min_freq = nvbios_rd16(bios, data + 0) * 1000;
info->vco1.max_freq = nvbios_rd16(bios, data + 2) * 1000;
info->vco1.min_inputfreq = nvbios_rd16(bios, data + 4) * 1000;
info->vco1.max_inputfreq = nvbios_rd16(bios, data + 6) * 1000;
info->vco1.min_m = nvbios_rd08(bios, data + 8);
info->vco1.max_m = nvbios_rd08(bios, data + 9);
info->vco1.min_n = nvbios_rd08(bios, data + 10);
info->vco1.max_n = nvbios_rd08(bios, data + 11);
info->min_p = nvbios_rd08(bios, data + 12);
info->max_p = nvbios_rd08(bios, data + 13);
break;
case 0x50:
info->refclk = nvbios_rd16(bios, data + 1) * 1000;
/* info->refclk_alt = nvbios_rd16(bios, data + 3) * 1000; */
info->vco1.min_freq = nvbios_rd16(bios, data + 5) * 1000;
info->vco1.max_freq = nvbios_rd16(bios, data + 7) * 1000;
info->vco1.min_inputfreq = nvbios_rd16(bios, data + 9) * 1000;
info->vco1.max_inputfreq = nvbios_rd16(bios, data + 11) * 1000;
info->vco1.min_m = nvbios_rd08(bios, data + 13);
info->vco1.max_m = nvbios_rd08(bios, data + 14);
info->vco1.min_n = nvbios_rd08(bios, data + 15);
info->vco1.max_n = nvbios_rd08(bios, data + 16);
info->min_p = nvbios_rd08(bios, data + 17);
info->max_p = nvbios_rd08(bios, data + 18);
break;
default:
nvkm_error(subdev, "unknown pll limits version 0x%02x\n", ver);
return -EINVAL;
}
if (!info->refclk) {
info->refclk = device->crystal;
if (bios->version.chip == 0x51) {
u32 sel_clk = nvkm_rd32(device, 0x680524);
if ((info->reg == 0x680508 && sel_clk & 0x20) ||
(info->reg == 0x680520 && sel_clk & 0x80)) {
if (nvkm_rdvgac(device, 0, 0x27) < 0xa3)
info->refclk = 200000;
else
info->refclk = 25000;
}
}
}
/*
* By now any valid limit table ought to have set a max frequency for
* vco1, so if it's zero it's either a pre limit table bios, or one
* with an empty limit table (seen on nv18)
*/
if (!info->vco1.max_freq) {
info->vco1.max_freq = nvbios_rd32(bios, bios->bmp_offset + 67);
info->vco1.min_freq = nvbios_rd32(bios, bios->bmp_offset + 71);
if (bmp_version(bios) < 0x0506) {
info->vco1.max_freq = 256000;
info->vco1.min_freq = 128000;
}
info->vco1.min_inputfreq = 0;
info->vco1.max_inputfreq = INT_MAX;
info->vco1.min_n = 0x1;
info->vco1.max_n = 0xff;
info->vco1.min_m = 0x1;
if (device->crystal == 13500) {
/* nv05 does this, nv11 doesn't, nv10 unknown */
if (bios->version.chip < 0x11)
info->vco1.min_m = 0x7;
info->vco1.max_m = 0xd;
} else {
if (bios->version.chip < 0x11)
info->vco1.min_m = 0x8;
info->vco1.max_m = 0xe;
}
if (bios->version.chip < 0x17 ||
bios->version.chip == 0x1a ||
bios->version.chip == 0x20)
info->max_p = 4;
else
info->max_p = 5;
info->max_p_usable = info->max_p;
}
return 0;
}
@@ -0,0 +1,102 @@
/*
* Copyright 2014 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
#include <subdev/bios.h>
#include <subdev/bios/bit.h>
#include <subdev/bios/image.h>
#include <subdev/bios/pmu.h>
u32
nvbios_pmuTe(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
struct bit_entry bit_p;
u32 data = 0;
if (!bit_entry(bios, 'p', &bit_p)) {
if (bit_p.version == 2 && bit_p.length >= 4)
data = nvbios_rd32(bios, bit_p.offset + 0x00);
if (data) {
*ver = nvbios_rd08(bios, data + 0x00); /* maybe? */
*hdr = nvbios_rd08(bios, data + 0x01);
*len = nvbios_rd08(bios, data + 0x02);
*cnt = nvbios_rd08(bios, data + 0x03);
}
}
return data;
}
u32
nvbios_pmuEe(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr)
{
u8 cnt, len;
u32 data = nvbios_pmuTe(bios, ver, hdr, &cnt, &len);
if (data && idx < cnt) {
data = data + *hdr + (idx * len);
*hdr = len;
return data;
}
return 0;
}
u32
nvbios_pmuEp(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr,
struct nvbios_pmuE *info)
{
u32 data = nvbios_pmuEe(bios, idx, ver, hdr);
if (data) {
info->type = nvbios_rd08(bios, data + 0x00);
info->data = nvbios_rd32(bios, data + 0x02);
}
return data;
}
bool
nvbios_pmuRm(struct nvkm_bios *bios, u8 type, struct nvbios_pmuR *info)
{
struct nvbios_pmuE pmuE;
u8 ver, hdr, idx = 0;
u32 data;
memset(info, 0x00, sizeof(*info));
while ((data = nvbios_pmuEp(bios, idx++, &ver, &hdr, &pmuE))) {
if (pmuE.type == type && (data = pmuE.data)) {
info->init_addr_pmu = nvbios_rd32(bios, data + 0x08);
info->args_addr_pmu = nvbios_rd32(bios, data + 0x0c);
info->boot_addr = data + 0x30;
info->boot_addr_pmu = nvbios_rd32(bios, data + 0x10) +
nvbios_rd32(bios, data + 0x18);
info->boot_size = nvbios_rd32(bios, data + 0x1c) -
nvbios_rd32(bios, data + 0x18);
info->code_addr = info->boot_addr + info->boot_size;
info->code_addr_pmu = info->boot_addr_pmu +
info->boot_size;
info->code_size = nvbios_rd32(bios, data + 0x20);
info->data_addr = data + 0x30 +
nvbios_rd32(bios, data + 0x24);
info->data_addr_pmu = nvbios_rd32(bios, data + 0x28);
info->data_size = nvbios_rd32(bios, data + 0x2c);
return true;
}
}
return false;
}
@@ -0,0 +1,125 @@
/*
* Copyright 2016 Karol Herbst
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Karol Herbst
*/
#include <subdev/bios.h>
#include <subdev/bios/bit.h>
#include <subdev/bios/power_budget.h>
static u32
nvbios_power_budget_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt,
u8 *len)
{
struct bit_entry bit_P;
u32 power_budget;
if (bit_entry(bios, 'P', &bit_P) || bit_P.version != 2 ||
bit_P.length < 0x30)
return 0;
power_budget = nvbios_rd32(bios, bit_P.offset + 0x2c);
if (!power_budget)
return 0;
*ver = nvbios_rd08(bios, power_budget);
switch (*ver) {
case 0x20:
case 0x30:
*hdr = nvbios_rd08(bios, power_budget + 0x1);
*len = nvbios_rd08(bios, power_budget + 0x2);
*cnt = nvbios_rd08(bios, power_budget + 0x3);
return power_budget;
default:
break;
}
return 0;
}
int
nvbios_power_budget_header(struct nvkm_bios *bios,
struct nvbios_power_budget *budget)
{
u8 ver, hdr, cnt, len, cap_entry;
u32 header;
if (!bios || !budget)
return -EINVAL;
header = nvbios_power_budget_table(bios, &ver, &hdr, &cnt, &len);
if (!header || !cnt)
return -ENODEV;
switch (ver) {
case 0x20:
cap_entry = nvbios_rd08(bios, header + 0x9);
break;
case 0x30:
cap_entry = nvbios_rd08(bios, header + 0xa);
break;
default:
cap_entry = 0xff;
}
if (cap_entry >= cnt && cap_entry != 0xff) {
nvkm_warn(&bios->subdev,
"invalid cap_entry in power budget table found\n");
budget->cap_entry = 0xff;
return -EINVAL;
}
budget->offset = header;
budget->ver = ver;
budget->hlen = hdr;
budget->elen = len;
budget->ecount = cnt;
budget->cap_entry = cap_entry;
return 0;
}
int
nvbios_power_budget_entry(struct nvkm_bios *bios,
struct nvbios_power_budget *budget,
u8 idx, struct nvbios_power_budget_entry *entry)
{
u32 entry_offset;
if (!bios || !budget || !budget->offset || idx >= budget->ecount
|| !entry)
return -EINVAL;
entry_offset = budget->offset + budget->hlen + idx * budget->elen;
if (budget->ver >= 0x20) {
entry->min_w = nvbios_rd32(bios, entry_offset + 0x2);
entry->avg_w = nvbios_rd32(bios, entry_offset + 0x6);
entry->max_w = nvbios_rd32(bios, entry_offset + 0xa);
} else {
entry->min_w = 0;
entry->max_w = nvbios_rd32(bios, entry_offset + 0x2);
entry->avg_w = entry->max_w;
}
return 0;
}
@@ -0,0 +1,29 @@
/* SPDX-License-Identifier: MIT */
#ifndef __NVKM_BIOS_PRIV_H__
#define __NVKM_BIOS_PRIV_H__
#define nvkm_bios(p) container_of((p), struct nvkm_bios, subdev)
#include <subdev/bios.h>
struct nvbios_source {
const char *name;
void *(*init)(struct nvkm_bios *, const char *);
void (*fini)(void *);
u32 (*read)(void *, u32 offset, u32 length, struct nvkm_bios *);
u32 (*size)(void *);
bool rw;
bool ignore_checksum;
bool no_pcir;
bool require_checksum;
};
int nvbios_extend(struct nvkm_bios *, u32 length);
int nvbios_shadow(struct nvkm_bios *);
extern const struct nvbios_source nvbios_prom;
extern const struct nvbios_source nvbios_ramin;
extern const struct nvbios_source nvbios_acpi_fast;
extern const struct nvbios_source nvbios_acpi_slow;
extern const struct nvbios_source nvbios_pcirom;
extern const struct nvbios_source nvbios_platform;
extern const struct nvbios_source nvbios_of;
#endif
@@ -0,0 +1,78 @@
/*
* Copyright 2013 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
#include <subdev/bios.h>
#include <subdev/bios/bit.h>
#include <subdev/bios/ramcfg.h>
#include <subdev/bios/M0203.h>
static u8
nvbios_ramcfg_strap(struct nvkm_subdev *subdev)
{
return (nvkm_rd32(subdev->device, 0x101000) & 0x0000003c) >> 2;
}
u8
nvbios_ramcfg_count(struct nvkm_bios *bios)
{
struct bit_entry bit_M;
if (!bit_entry(bios, 'M', &bit_M)) {
if (bit_M.version == 1 && bit_M.length >= 5)
return nvbios_rd08(bios, bit_M.offset + 2);
if (bit_M.version == 2 && bit_M.length >= 3)
return nvbios_rd08(bios, bit_M.offset + 0);
}
return 0x00;
}
u8
nvbios_ramcfg_index(struct nvkm_subdev *subdev)
{
struct nvkm_bios *bios = subdev->device->bios;
u8 strap = nvbios_ramcfg_strap(subdev);
u32 xlat = 0x00000000;
struct bit_entry bit_M;
struct nvbios_M0203E M0203E;
u8 ver, hdr;
if (!bit_entry(bios, 'M', &bit_M)) {
if (bit_M.version == 1 && bit_M.length >= 5)
xlat = nvbios_rd16(bios, bit_M.offset + 3);
if (bit_M.version == 2 && bit_M.length >= 3) {
/*XXX: is M ever shorter than this?
* if not - what is xlat used for now?
* also - sigh..
*/
if (bit_M.length >= 7 &&
nvbios_M0203Em(bios, strap, &ver, &hdr, &M0203E))
return M0203E.group;
xlat = nvbios_rd16(bios, bit_M.offset + 1);
}
}
if (xlat)
strap = nvbios_rd08(bios, xlat + strap);
return strap;
}
@@ -0,0 +1,258 @@
/*
* Copyright 2013 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include <subdev/bios.h>
#include <subdev/bios/bit.h>
#include <subdev/bios/rammap.h>
u32
nvbios_rammapTe(struct nvkm_bios *bios, u8 *ver, u8 *hdr,
u8 *cnt, u8 *len, u8 *snr, u8 *ssz)
{
struct bit_entry bit_P;
u32 rammap = 0x0000;
if (!bit_entry(bios, 'P', &bit_P)) {
if (bit_P.version == 2)
rammap = nvbios_rd32(bios, bit_P.offset + 4);
if (rammap) {
*ver = nvbios_rd08(bios, rammap + 0);
switch (*ver) {
case 0x10:
case 0x11:
*hdr = nvbios_rd08(bios, rammap + 1);
*cnt = nvbios_rd08(bios, rammap + 5);
*len = nvbios_rd08(bios, rammap + 2);
*snr = nvbios_rd08(bios, rammap + 4);
*ssz = nvbios_rd08(bios, rammap + 3);
return rammap;
default:
break;
}
}
}
return 0x0000;
}
u32
nvbios_rammapEe(struct nvkm_bios *bios, int idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
u8 snr, ssz;
u32 rammap = nvbios_rammapTe(bios, ver, hdr, cnt, len, &snr, &ssz);
if (rammap && idx < *cnt) {
rammap = rammap + *hdr + (idx * (*len + (snr * ssz)));
*hdr = *len;
*cnt = snr;
*len = ssz;
return rammap;
}
return 0x0000;
}
/* Pretend a performance mode is also a rammap entry, helps coalesce entries
* later on */
u32
nvbios_rammapEp_from_perf(struct nvkm_bios *bios, u32 data, u8 size,
struct nvbios_ramcfg *p)
{
memset(p, 0x00, sizeof(*p));
p->rammap_00_16_20 = (nvbios_rd08(bios, data + 0x16) & 0x20) >> 5;
p->rammap_00_16_40 = (nvbios_rd08(bios, data + 0x16) & 0x40) >> 6;
p->rammap_00_17_02 = (nvbios_rd08(bios, data + 0x17) & 0x02) >> 1;
return data;
}
u32
nvbios_rammapEp(struct nvkm_bios *bios, int idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ramcfg *p)
{
u32 data = nvbios_rammapEe(bios, idx, ver, hdr, cnt, len), temp;
memset(p, 0x00, sizeof(*p));
p->rammap_ver = *ver;
p->rammap_hdr = *hdr;
switch (!!data * *ver) {
case 0x10:
p->rammap_min = nvbios_rd16(bios, data + 0x00);
p->rammap_max = nvbios_rd16(bios, data + 0x02);
p->rammap_10_04_02 = (nvbios_rd08(bios, data + 0x04) & 0x02) >> 1;
p->rammap_10_04_08 = (nvbios_rd08(bios, data + 0x04) & 0x08) >> 3;
break;
case 0x11:
p->rammap_min = nvbios_rd16(bios, data + 0x00);
p->rammap_max = nvbios_rd16(bios, data + 0x02);
p->rammap_11_08_01 = (nvbios_rd08(bios, data + 0x08) & 0x01) >> 0;
p->rammap_11_08_0c = (nvbios_rd08(bios, data + 0x08) & 0x0c) >> 2;
p->rammap_11_08_10 = (nvbios_rd08(bios, data + 0x08) & 0x10) >> 4;
temp = nvbios_rd32(bios, data + 0x09);
p->rammap_11_09_01ff = (temp & 0x000001ff) >> 0;
p->rammap_11_0a_03fe = (temp & 0x0003fe00) >> 9;
p->rammap_11_0a_0400 = (temp & 0x00040000) >> 18;
p->rammap_11_0a_0800 = (temp & 0x00080000) >> 19;
p->rammap_11_0b_01f0 = (temp & 0x01f00000) >> 20;
p->rammap_11_0b_0200 = (temp & 0x02000000) >> 25;
p->rammap_11_0b_0400 = (temp & 0x04000000) >> 26;
p->rammap_11_0b_0800 = (temp & 0x08000000) >> 27;
p->rammap_11_0d = nvbios_rd08(bios, data + 0x0d);
p->rammap_11_0e = nvbios_rd08(bios, data + 0x0e);
p->rammap_11_0f = nvbios_rd08(bios, data + 0x0f);
p->rammap_11_11_0c = (nvbios_rd08(bios, data + 0x11) & 0x0c) >> 2;
break;
default:
data = 0;
break;
}
return data;
}
u32
nvbios_rammapEm(struct nvkm_bios *bios, u16 mhz,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ramcfg *info)
{
int idx = 0;
u32 data;
while ((data = nvbios_rammapEp(bios, idx++, ver, hdr, cnt, len, info))) {
if (mhz >= info->rammap_min && mhz <= info->rammap_max)
break;
}
return data;
}
u32
nvbios_rammapSe(struct nvkm_bios *bios, u32 data,
u8 ever, u8 ehdr, u8 ecnt, u8 elen, int idx, u8 *ver, u8 *hdr)
{
if (idx < ecnt) {
data = data + ehdr + (idx * elen);
*ver = ever;
*hdr = elen;
return data;
}
return 0;
}
u32
nvbios_rammapSp_from_perf(struct nvkm_bios *bios, u32 data, u8 size, int idx,
struct nvbios_ramcfg *p)
{
data += (idx * size);
if (size < 11)
return 0x00000000;
p->ramcfg_ver = 0;
p->ramcfg_timing = nvbios_rd08(bios, data + 0x01);
p->ramcfg_00_03_01 = (nvbios_rd08(bios, data + 0x03) & 0x01) >> 0;
p->ramcfg_00_03_02 = (nvbios_rd08(bios, data + 0x03) & 0x02) >> 1;
p->ramcfg_DLLoff = (nvbios_rd08(bios, data + 0x03) & 0x04) >> 2;
p->ramcfg_00_03_08 = (nvbios_rd08(bios, data + 0x03) & 0x08) >> 3;
p->ramcfg_RON = (nvbios_rd08(bios, data + 0x03) & 0x10) >> 3;
p->ramcfg_FBVDDQ = (nvbios_rd08(bios, data + 0x03) & 0x80) >> 7;
p->ramcfg_00_04_02 = (nvbios_rd08(bios, data + 0x04) & 0x02) >> 1;
p->ramcfg_00_04_04 = (nvbios_rd08(bios, data + 0x04) & 0x04) >> 2;
p->ramcfg_00_04_20 = (nvbios_rd08(bios, data + 0x04) & 0x20) >> 5;
p->ramcfg_00_05 = (nvbios_rd08(bios, data + 0x05) & 0xff) >> 0;
p->ramcfg_00_06 = (nvbios_rd08(bios, data + 0x06) & 0xff) >> 0;
p->ramcfg_00_07 = (nvbios_rd08(bios, data + 0x07) & 0xff) >> 0;
p->ramcfg_00_08 = (nvbios_rd08(bios, data + 0x08) & 0xff) >> 0;
p->ramcfg_00_09 = (nvbios_rd08(bios, data + 0x09) & 0xff) >> 0;
p->ramcfg_00_0a_0f = (nvbios_rd08(bios, data + 0x0a) & 0x0f) >> 0;
p->ramcfg_00_0a_f0 = (nvbios_rd08(bios, data + 0x0a) & 0xf0) >> 4;
return data;
}
u32
nvbios_rammapSp(struct nvkm_bios *bios, u32 data,
u8 ever, u8 ehdr, u8 ecnt, u8 elen, int idx,
u8 *ver, u8 *hdr, struct nvbios_ramcfg *p)
{
data = nvbios_rammapSe(bios, data, ever, ehdr, ecnt, elen, idx, ver, hdr);
p->ramcfg_ver = *ver;
p->ramcfg_hdr = *hdr;
switch (!!data * *ver) {
case 0x10:
p->ramcfg_timing = nvbios_rd08(bios, data + 0x01);
p->ramcfg_10_02_01 = (nvbios_rd08(bios, data + 0x02) & 0x01) >> 0;
p->ramcfg_10_02_02 = (nvbios_rd08(bios, data + 0x02) & 0x02) >> 1;
p->ramcfg_10_02_04 = (nvbios_rd08(bios, data + 0x02) & 0x04) >> 2;
p->ramcfg_10_02_08 = (nvbios_rd08(bios, data + 0x02) & 0x08) >> 3;
p->ramcfg_10_02_10 = (nvbios_rd08(bios, data + 0x02) & 0x10) >> 4;
p->ramcfg_10_02_20 = (nvbios_rd08(bios, data + 0x02) & 0x20) >> 5;
p->ramcfg_DLLoff = (nvbios_rd08(bios, data + 0x02) & 0x40) >> 6;
p->ramcfg_10_03_0f = (nvbios_rd08(bios, data + 0x03) & 0x0f) >> 0;
p->ramcfg_10_04_01 = (nvbios_rd08(bios, data + 0x04) & 0x01) >> 0;
p->ramcfg_FBVDDQ = (nvbios_rd08(bios, data + 0x04) & 0x08) >> 3;
p->ramcfg_10_05 = (nvbios_rd08(bios, data + 0x05) & 0xff) >> 0;
p->ramcfg_10_06 = (nvbios_rd08(bios, data + 0x06) & 0xff) >> 0;
p->ramcfg_10_07 = (nvbios_rd08(bios, data + 0x07) & 0xff) >> 0;
p->ramcfg_10_08 = (nvbios_rd08(bios, data + 0x08) & 0xff) >> 0;
p->ramcfg_10_09_0f = (nvbios_rd08(bios, data + 0x09) & 0x0f) >> 0;
p->ramcfg_10_09_f0 = (nvbios_rd08(bios, data + 0x09) & 0xf0) >> 4;
break;
case 0x11:
p->ramcfg_timing = nvbios_rd08(bios, data + 0x00);
p->ramcfg_11_01_01 = (nvbios_rd08(bios, data + 0x01) & 0x01) >> 0;
p->ramcfg_11_01_02 = (nvbios_rd08(bios, data + 0x01) & 0x02) >> 1;
p->ramcfg_11_01_04 = (nvbios_rd08(bios, data + 0x01) & 0x04) >> 2;
p->ramcfg_11_01_08 = (nvbios_rd08(bios, data + 0x01) & 0x08) >> 3;
p->ramcfg_11_01_10 = (nvbios_rd08(bios, data + 0x01) & 0x10) >> 4;
p->ramcfg_DLLoff = (nvbios_rd08(bios, data + 0x01) & 0x20) >> 5;
p->ramcfg_11_01_40 = (nvbios_rd08(bios, data + 0x01) & 0x40) >> 6;
p->ramcfg_11_01_80 = (nvbios_rd08(bios, data + 0x01) & 0x80) >> 7;
p->ramcfg_11_02_03 = (nvbios_rd08(bios, data + 0x02) & 0x03) >> 0;
p->ramcfg_11_02_04 = (nvbios_rd08(bios, data + 0x02) & 0x04) >> 2;
p->ramcfg_11_02_08 = (nvbios_rd08(bios, data + 0x02) & 0x08) >> 3;
p->ramcfg_11_02_10 = (nvbios_rd08(bios, data + 0x02) & 0x10) >> 4;
p->ramcfg_11_02_40 = (nvbios_rd08(bios, data + 0x02) & 0x40) >> 6;
p->ramcfg_11_02_80 = (nvbios_rd08(bios, data + 0x02) & 0x80) >> 7;
p->ramcfg_11_03_0f = (nvbios_rd08(bios, data + 0x03) & 0x0f) >> 0;
p->ramcfg_11_03_30 = (nvbios_rd08(bios, data + 0x03) & 0x30) >> 4;
p->ramcfg_11_03_c0 = (nvbios_rd08(bios, data + 0x03) & 0xc0) >> 6;
p->ramcfg_11_03_f0 = (nvbios_rd08(bios, data + 0x03) & 0xf0) >> 4;
p->ramcfg_11_04 = (nvbios_rd08(bios, data + 0x04) & 0xff) >> 0;
p->ramcfg_11_06 = (nvbios_rd08(bios, data + 0x06) & 0xff) >> 0;
p->ramcfg_11_07_02 = (nvbios_rd08(bios, data + 0x07) & 0x02) >> 1;
p->ramcfg_11_07_04 = (nvbios_rd08(bios, data + 0x07) & 0x04) >> 2;
p->ramcfg_11_07_08 = (nvbios_rd08(bios, data + 0x07) & 0x08) >> 3;
p->ramcfg_11_07_10 = (nvbios_rd08(bios, data + 0x07) & 0x10) >> 4;
p->ramcfg_11_07_40 = (nvbios_rd08(bios, data + 0x07) & 0x40) >> 6;
p->ramcfg_11_07_80 = (nvbios_rd08(bios, data + 0x07) & 0x80) >> 7;
p->ramcfg_11_08_01 = (nvbios_rd08(bios, data + 0x08) & 0x01) >> 0;
p->ramcfg_11_08_02 = (nvbios_rd08(bios, data + 0x08) & 0x02) >> 1;
p->ramcfg_11_08_04 = (nvbios_rd08(bios, data + 0x08) & 0x04) >> 2;
p->ramcfg_11_08_08 = (nvbios_rd08(bios, data + 0x08) & 0x08) >> 3;
p->ramcfg_11_08_10 = (nvbios_rd08(bios, data + 0x08) & 0x10) >> 4;
p->ramcfg_11_08_20 = (nvbios_rd08(bios, data + 0x08) & 0x20) >> 5;
p->ramcfg_11_09 = (nvbios_rd08(bios, data + 0x09) & 0xff) >> 0;
break;
default:
data = 0;
break;
}
return data;
}
@@ -0,0 +1,248 @@
/*
* Copyright 2014 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
#include "priv.h"
#include <core/option.h>
#include <subdev/bios.h>
#include <subdev/bios/image.h>
struct shadow {
u32 skip;
const struct nvbios_source *func;
void *data;
u32 size;
int score;
};
static bool
shadow_fetch(struct nvkm_bios *bios, struct shadow *mthd, u32 upto)
{
const u32 limit = (upto + 3) & ~3;
const u32 start = bios->size;
void *data = mthd->data;
if (nvbios_extend(bios, limit) > 0) {
u32 read = mthd->func->read(data, start, limit - start, bios);
bios->size = start + read;
}
return bios->size >= upto;
}
static int
shadow_image(struct nvkm_bios *bios, int idx, u32 offset, struct shadow *mthd)
{
struct nvkm_subdev *subdev = &bios->subdev;
struct nvbios_image image;
int score = 1;
if (mthd->func->no_pcir) {
image.base = 0;
image.type = 0;
image.size = mthd->func->size(mthd->data);
image.last = 1;
} else {
if (!shadow_fetch(bios, mthd, offset + 0x1000)) {
nvkm_debug(subdev, "%08x: header fetch failed\n",
offset);
return 0;
}
if (!nvbios_image(bios, idx, &image)) {
nvkm_debug(subdev, "image %d invalid\n", idx);
return 0;
}
}
nvkm_debug(subdev, "%08x: type %02x, %d bytes\n",
image.base, image.type, image.size);
if (!shadow_fetch(bios, mthd, image.base + image.size)) {
nvkm_debug(subdev, "%08x: fetch failed\n", image.base);
return 0;
}
switch (image.type) {
case 0x00:
if (!mthd->func->ignore_checksum &&
nvbios_checksum(&bios->data[image.base], image.size)) {
nvkm_debug(subdev, "%08x: checksum failed\n",
image.base);
if (!mthd->func->require_checksum) {
if (mthd->func->rw)
score += 1;
score += 1;
} else
return 0;
} else {
score += 3;
}
break;
default:
score += 3;
break;
}
if (!image.last)
score += shadow_image(bios, idx + 1, offset + image.size, mthd);
return score;
}
static int
shadow_method(struct nvkm_bios *bios, struct shadow *mthd, const char *name)
{
const struct nvbios_source *func = mthd->func;
struct nvkm_subdev *subdev = &bios->subdev;
if (func->name) {
nvkm_debug(subdev, "trying %s...\n", name ? name : func->name);
if (func->init) {
mthd->data = func->init(bios, name);
if (IS_ERR(mthd->data)) {
mthd->data = NULL;
return 0;
}
}
mthd->score = shadow_image(bios, 0, 0, mthd);
if (func->fini)
func->fini(mthd->data);
nvkm_debug(subdev, "scored %d\n", mthd->score);
mthd->data = bios->data;
mthd->size = bios->size;
bios->data = NULL;
bios->size = 0;
}
return mthd->score;
}
static u32
shadow_fw_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios)
{
const struct firmware *fw = data;
if (offset + length <= fw->size) {
memcpy(bios->data + offset, fw->data + offset, length);
return length;
}
return 0;
}
static void *
shadow_fw_init(struct nvkm_bios *bios, const char *name)
{
struct device *dev = bios->subdev.device->dev;
const struct firmware *fw;
int ret = request_firmware(&fw, name, dev);
if (ret)
return ERR_PTR(-ENOENT);
return (void *)fw;
}
static void
shadow_fw_release(void *fw)
{
release_firmware(fw);
}
static const struct nvbios_source
shadow_fw = {
.name = "firmware",
.init = shadow_fw_init,
.fini = shadow_fw_release,
.read = shadow_fw_read,
.rw = false,
};
int
nvbios_shadow(struct nvkm_bios *bios)
{
struct nvkm_subdev *subdev = &bios->subdev;
struct nvkm_device *device = subdev->device;
struct shadow mthds[] = {
{ 0, &nvbios_of },
{ 0, &nvbios_ramin },
{ 0, &nvbios_prom },
{ 0, &nvbios_acpi_fast },
{ 4, &nvbios_acpi_slow },
{ 1, &nvbios_pcirom },
{ 1, &nvbios_platform },
{}
}, *mthd, *best = NULL;
const char *optarg;
char *source;
int optlen;
/* handle user-specified bios source */
optarg = nvkm_stropt(device->cfgopt, "NvBios", &optlen);
source = optarg ? kstrndup(optarg, optlen, GFP_KERNEL) : NULL;
if (source) {
/* try to match one of the built-in methods */
for (mthd = mthds; mthd->func; mthd++) {
if (mthd->func->name &&
!strcasecmp(source, mthd->func->name)) {
best = mthd;
if (shadow_method(bios, mthd, NULL))
break;
}
}
/* otherwise, attempt to load as firmware */
if (!best && (best = mthd)) {
mthd->func = &shadow_fw;
shadow_method(bios, mthd, source);
mthd->func = NULL;
}
if (!best->score) {
nvkm_error(subdev, "%s invalid\n", source);
kfree(source);
source = NULL;
}
}
/* scan all potential bios sources, looking for best image */
if (!best || !best->score) {
for (mthd = mthds, best = mthd; mthd->func; mthd++) {
if (!mthd->skip || best->score < mthd->skip) {
if (shadow_method(bios, mthd, NULL)) {
if (mthd->score > best->score)
best = mthd;
}
}
}
}
/* cleanup the ones we didn't use */
for (mthd = mthds; mthd->func; mthd++) {
if (mthd != best)
kfree(mthd->data);
}
if (!best->score) {
nvkm_error(subdev, "unable to locate usable image\n");
return -EINVAL;
}
nvkm_debug(subdev, "using image from %s\n", best->func ?
best->func->name : source);
bios->data = best->data;
bios->size = best->size;
kfree(source);
return 0;
}
@@ -0,0 +1,140 @@
/*
* Copyright 2012 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include "priv.h"
static int
acpi_read_bios(acpi_handle rom_handle, u8 *bios, u32 offset, u32 length)
{
#if defined(CONFIG_ACPI) && defined(CONFIG_X86)
acpi_status status;
union acpi_object rom_arg_elements[2], *obj;
struct acpi_object_list rom_arg;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL};
rom_arg.count = 2;
rom_arg.pointer = &rom_arg_elements[0];
rom_arg_elements[0].type = ACPI_TYPE_INTEGER;
rom_arg_elements[0].integer.value = offset;
rom_arg_elements[1].type = ACPI_TYPE_INTEGER;
rom_arg_elements[1].integer.value = length;
status = acpi_evaluate_object(rom_handle, NULL, &rom_arg, &buffer);
if (ACPI_FAILURE(status)) {
pr_info("failed to evaluate ROM got %s\n",
acpi_format_exception(status));
return -ENODEV;
}
obj = (union acpi_object *)buffer.pointer;
length = min(length, obj->buffer.length);
memcpy(bios+offset, obj->buffer.pointer, length);
kfree(buffer.pointer);
return length;
#else
return -EINVAL;
#endif
}
/* This version of the shadow function disobeys the ACPI spec and tries
* to fetch in units of more than 4KiB at a time. This is a LOT faster
* on some systems, such as Lenovo W530.
*/
static u32
acpi_read_fast(void *data, u32 offset, u32 length, struct nvkm_bios *bios)
{
u32 limit = (offset + length + 0xfff) & ~0xfff;
u32 start = offset & ~0x00000fff;
u32 fetch = limit - start;
if (nvbios_extend(bios, limit) >= 0) {
int ret = acpi_read_bios(data, bios->data, start, fetch);
if (ret == fetch)
return fetch;
}
return 0;
}
/* Other systems, such as the one in fdo#55948, will report a success
* but only return 4KiB of data. The common bios fetching logic will
* detect an invalid image, and fall back to this version of the read
* function.
*/
static u32
acpi_read_slow(void *data, u32 offset, u32 length, struct nvkm_bios *bios)
{
u32 limit = (offset + length + 0xfff) & ~0xfff;
u32 start = offset & ~0xfff;
u32 fetch = 0;
if (nvbios_extend(bios, limit) >= 0) {
while (start + fetch < limit) {
int ret = acpi_read_bios(data, bios->data,
start + fetch, 0x1000);
if (ret != 0x1000)
break;
fetch += 0x1000;
}
}
return fetch;
}
static void *
acpi_init(struct nvkm_bios *bios, const char *name)
{
#if defined(CONFIG_ACPI) && defined(CONFIG_X86)
acpi_status status;
acpi_handle dhandle, rom_handle;
dhandle = ACPI_HANDLE(bios->subdev.device->dev);
if (!dhandle)
return ERR_PTR(-ENODEV);
status = acpi_get_handle(dhandle, "_ROM", &rom_handle);
if (ACPI_FAILURE(status))
return ERR_PTR(-ENODEV);
return rom_handle;
#else
return ERR_PTR(-ENODEV);
#endif
}
const struct nvbios_source
nvbios_acpi_fast = {
.name = "ACPI",
.init = acpi_init,
.read = acpi_read_fast,
.rw = false,
.require_checksum = true,
};
const struct nvbios_source
nvbios_acpi_slow = {
.name = "ACPI",
.init = acpi_init,
.read = acpi_read_slow,
.rw = false,
};
@@ -0,0 +1,89 @@
/*
* Copyright 2012 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include "priv.h"
#include <core/pci.h>
#if defined(__powerpc__)
struct priv {
const void __iomem *data;
int size;
};
static u32
of_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios)
{
struct priv *priv = data;
if (offset < priv->size) {
length = min_t(u32, length, priv->size - offset);
memcpy_fromio(bios->data + offset, priv->data + offset, length);
return length;
}
return 0;
}
static u32
of_size(void *data)
{
struct priv *priv = data;
return priv->size;
}
static void *
of_init(struct nvkm_bios *bios, const char *name)
{
struct nvkm_device *device = bios->subdev.device;
struct pci_dev *pdev = device->func->pci(device)->pdev;
struct device_node *dn;
struct priv *priv;
if (!(dn = pci_device_to_OF_node(pdev)))
return ERR_PTR(-ENODEV);
if (!(priv = kzalloc_obj(*priv)))
return ERR_PTR(-ENOMEM);
if ((priv->data = of_get_property(dn, "NVDA,BMP", &priv->size)))
return priv;
kfree(priv);
return ERR_PTR(-EINVAL);
}
static void of_fini(void *p)
{
kfree(p);
}
const struct nvbios_source
nvbios_of = {
.name = "OpenFirmware",
.init = of_init,
.fini = of_fini,
.read = of_read,
.size = of_size,
.rw = false,
.ignore_checksum = true,
.no_pcir = true,
};
#else
const struct nvbios_source
nvbios_of = {
};
#endif
@@ -0,0 +1,134 @@
/*
* Copyright 2012 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include "priv.h"
#include <core/pci.h>
struct priv {
struct pci_dev *pdev;
void __iomem *rom;
size_t size;
};
static u32
pcirom_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios)
{
struct priv *priv = data;
if (offset + length <= priv->size) {
memcpy_fromio(bios->data + offset, priv->rom + offset, length);
return length;
}
return 0;
}
static void
pcirom_fini(void *data)
{
struct priv *priv = data;
pci_unmap_rom(priv->pdev, priv->rom);
pci_disable_rom(priv->pdev);
kfree(priv);
}
static void *
pcirom_init(struct nvkm_bios *bios, const char *name)
{
struct nvkm_device *device = bios->subdev.device;
struct priv *priv = NULL;
struct pci_dev *pdev;
int ret;
if (device->func->pci)
pdev = device->func->pci(device)->pdev;
else
return ERR_PTR(-ENODEV);
if (!(ret = pci_enable_rom(pdev))) {
if (ret = -ENOMEM,
(priv = kmalloc_obj(*priv))) {
if (ret = -EFAULT,
(priv->rom = pci_map_rom(pdev, &priv->size))) {
priv->pdev = pdev;
return priv;
}
kfree(priv);
}
pci_disable_rom(pdev);
}
return ERR_PTR(ret);
}
const struct nvbios_source
nvbios_pcirom = {
.name = "PCIROM",
.init = pcirom_init,
.fini = pcirom_fini,
.read = pcirom_read,
.rw = true,
};
static void *
platform_init(struct nvkm_bios *bios, const char *name)
{
struct nvkm_device *device = bios->subdev.device;
struct pci_dev *pdev;
struct priv *priv;
int ret = -ENOMEM;
if (device->func->pci)
pdev = device->func->pci(device)->pdev;
else
return ERR_PTR(-ENODEV);
if (!pdev->rom || pdev->romlen == 0)
return ERR_PTR(-ENODEV);
if ((priv = kmalloc_obj(*priv))) {
priv->size = pdev->romlen;
if (ret = -ENODEV,
(priv->rom = ioremap(pdev->rom, pdev->romlen)))
return priv;
kfree(priv);
}
return ERR_PTR(ret);
}
static void
platform_fini(void *data)
{
struct priv *priv = data;
iounmap(priv->rom);
kfree(priv);
}
const struct nvbios_source
nvbios_platform = {
.name = "PLATFORM",
.init = platform_init,
.fini = platform_fini,
.read = pcirom_read,
.rw = true,
};
@@ -0,0 +1,124 @@
/*
* Copyright 2012 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include "priv.h"
struct priv {
struct nvkm_bios *bios;
u32 bar0;
};
static u32
pramin_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios)
{
struct nvkm_device *device = bios->subdev.device;
u32 i;
if (offset + length <= 0x00100000) {
for (i = offset; i < offset + length; i += 4)
*(u32 *)&bios->data[i] = nvkm_rd32(device, 0x700000 + i);
return length;
}
return 0;
}
static void
pramin_fini(void *data)
{
struct priv *priv = data;
if (priv) {
struct nvkm_device *device = priv->bios->subdev.device;
nvkm_wr32(device, 0x001700, priv->bar0);
kfree(priv);
}
}
static void *
pramin_init(struct nvkm_bios *bios, const char *name)
{
struct nvkm_subdev *subdev = &bios->subdev;
struct nvkm_device *device = subdev->device;
struct priv *priv = NULL;
u64 addr = 0;
/* PRAMIN always potentially available prior to nv50 */
if (device->card_type < NV_50)
return NULL;
/* we can't get the bios image pointer without PDISP */
if (device->card_type >= GA100)
addr = nvkm_rd32(device, 0x820c04);
else
if (device->card_type >= GM100)
addr = nvkm_rd32(device, 0x021c04);
else
if (device->card_type >= NV_C0)
addr = nvkm_rd32(device, 0x022500);
if (addr & 0x00000001) {
nvkm_debug(subdev, "... display disabled\n");
return ERR_PTR(-ENODEV);
}
/* check that the window is enabled and in vram, particularly
* important as we don't want to be touching vram on an
* uninitialised board
*/
if (device->card_type >= GV100)
addr = nvkm_rd32(device, 0x625f04);
else
addr = nvkm_rd32(device, 0x619f04);
if (!(addr & 0x00000008)) {
nvkm_debug(subdev, "... not enabled\n");
return ERR_PTR(-ENODEV);
}
if ( (addr & 0x00000003) != 1) {
nvkm_debug(subdev, "... not in vram\n");
return ERR_PTR(-ENODEV);
}
/* some alternate method inherited from xf86-video-nv... */
addr = (addr & 0xffffff00) << 8;
if (!addr) {
addr = (u64)nvkm_rd32(device, 0x001700) << 16;
addr += 0xf0000;
}
/* modify bar0 PRAMIN window to cover the bios image */
if (!(priv = kmalloc_obj(*priv))) {
nvkm_error(subdev, "... out of memory\n");
return ERR_PTR(-ENOMEM);
}
priv->bios = bios;
priv->bar0 = nvkm_rd32(device, 0x001700);
nvkm_wr32(device, 0x001700, addr >> 16);
return priv;
}
const struct nvbios_source
nvbios_ramin = {
.name = "PRAMIN",
.init = pramin_init,
.fini = pramin_fini,
.read = pramin_read,
.rw = true,
};
@@ -0,0 +1,156 @@
/*
* Copyright 2012 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include "priv.h"
#include <subdev/pci.h>
#define NV_PBUS_IFR_FMT_FIXED0_SIGNATURE_VALUE 0x4947564E /* "NVGI" */
#define NV_ROM_DIRECTORY_IDENTIFIER 0x44524652 /* "RFRD" */
struct priv {
struct nvkm_device *device;
u32 pci_rom_offset;
};
static u32
nvbios_prom_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios)
{
struct priv *priv = data;
struct nvkm_device *device = priv->device;
u32 i;
/* Make sure we don't try to read past the end of data[] */
if (offset + length > bios->size)
return 0;
/* Make sure the read falls within the 1MB PROM window */
if (offset + priv->pci_rom_offset + length > 0x00100000)
return 0;
for (i = offset; i < offset + length; i += 4)
*(u32 *)&bios->data[i] = nvkm_rd32(device, 0x300000 + priv->pci_rom_offset + i);
return length;
}
static void
nvbios_prom_fini(void *data)
{
struct priv *priv = data;
struct nvkm_device *device = priv->device;
nvkm_pci_rom_shadow(device->pci, true);
kfree(data);
}
static void *
nvbios_prom_init(struct nvkm_bios *bios, const char *name)
{
struct nvkm_device *device = bios->subdev.device;
struct priv *priv;
u32 fixed0;
/* There is no PROM on NV4x iGPUs */
if (device->card_type == NV_40 && device->chipset >= 0x4c)
return ERR_PTR(-ENODEV);
priv = kzalloc_obj(*priv);
if (!priv)
return ERR_PTR(-ENOMEM);
/* Disable the PCI ROM shadow so that we can read PROM. */
nvkm_pci_rom_shadow(device->pci, false);
/*
* Check for an IFR header. If present, parse it to find the actual PCI ROM header.
*
* The IFR header is documented in Documentation/gpu/nova/core/vbios.rst
*/
fixed0 = nvkm_rd32(device, 0x300000);
if (fixed0 == NV_PBUS_IFR_FMT_FIXED0_SIGNATURE_VALUE) {
u32 fixed1 = nvkm_rd32(device, 0x300004);
u8 version = (fixed1 >> 8) & 0xff;
u32 fixed2, data_size, offset, signature;
switch (version) {
case 1:
case 2:
data_size = (fixed1 >> 16) & 0x7fff;
priv->pci_rom_offset = nvkm_rd32(device, 0x300000 + data_size + 4);
break;
case 3:
fixed2 = nvkm_rd32(device, 0x300008);
data_size = fixed2 & 0x000fffff;
/* ROM directory offset */
offset = nvkm_rd32(device, 0x300000 + data_size) + 4096;
signature = nvkm_rd32(device, 0x300000 + offset);
if (signature != NV_ROM_DIRECTORY_IDENTIFIER) {
nvkm_error(&bios->subdev, "could not find IFR ROM directory\n");
goto fail;
}
priv->pci_rom_offset = nvkm_rd32(device, 0x300000 + offset + 8);
break;
default:
nvkm_error(&bios->subdev, "unsupported IFR header version %u\n",
version);
goto fail;
}
/* Double-check that the offset is valid */
if (priv->pci_rom_offset >= 0x00100000) {
nvkm_error(&bios->subdev,
"PCI ROM offset of 0x%x is too large\n", priv->pci_rom_offset);
goto fail;
}
/* If there is an IFR header, there must also be a PCI ROM header. */
signature = nvkm_rd32(device, 0x300000 + priv->pci_rom_offset) & 0xffff;
if (signature != 0xaa55) {
nvkm_error(&bios->subdev,
"could not find PCI ROM signature at offset 0x%x\n",
priv->pci_rom_offset);
goto fail;
}
}
priv->device = device;
return priv;
fail:
nvkm_pci_rom_shadow(device->pci, true);
kfree(priv);
return ERR_PTR(-ENODEV);
}
const struct nvbios_source
nvbios_prom = {
.name = "PROM",
.init = nvbios_prom_init,
.fini = nvbios_prom_fini,
.read = nvbios_prom_read,
.rw = false,
};
@@ -0,0 +1,212 @@
/*
* Copyright 2012 Nouveau Community
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Martin Peres
*/
#include <subdev/bios.h>
#include <subdev/bios/bit.h>
#include <subdev/bios/therm.h>
static u32
therm_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *len, u8 *cnt)
{
struct bit_entry bit_P;
u32 therm = 0;
if (!bit_entry(bios, 'P', &bit_P)) {
if (bit_P.version == 1)
therm = nvbios_rd32(bios, bit_P.offset + 12);
else if (bit_P.version == 2)
therm = nvbios_rd32(bios, bit_P.offset + 16);
else
nvkm_error(&bios->subdev,
"unknown offset for thermal in BIT P %d\n",
bit_P.version);
}
/* exit now if we haven't found the thermal table */
if (!therm)
return 0;
*ver = nvbios_rd08(bios, therm + 0);
*hdr = nvbios_rd08(bios, therm + 1);
*len = nvbios_rd08(bios, therm + 2);
*cnt = nvbios_rd08(bios, therm + 3);
return therm + nvbios_rd08(bios, therm + 1);
}
static u32
nvbios_therm_entry(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len)
{
u8 hdr, cnt;
u32 therm = therm_table(bios, ver, &hdr, len, &cnt);
if (therm && idx < cnt)
return therm + idx * *len;
return 0;
}
int
nvbios_therm_sensor_parse(struct nvkm_bios *bios,
enum nvbios_therm_domain domain,
struct nvbios_therm_sensor *sensor)
{
s8 thrs_section, sensor_section, offset;
u8 ver, len, i;
u32 entry;
/* we only support the core domain for now */
if (domain != NVBIOS_THERM_DOMAIN_CORE)
return -EINVAL;
/* Read the entries from the table */
thrs_section = 0;
sensor_section = -1;
i = 0;
while ((entry = nvbios_therm_entry(bios, i++, &ver, &len))) {
s16 value = nvbios_rd16(bios, entry + 1);
switch (nvbios_rd08(bios, entry + 0)) {
case 0x0:
thrs_section = value;
if (value > 0)
return 0; /* we do not try to support ambient */
break;
case 0x01:
sensor_section++;
if (sensor_section == 0) {
offset = ((s8) nvbios_rd08(bios, entry + 2)) / 2;
sensor->offset_constant = offset;
}
break;
case 0x04:
if (thrs_section == 0) {
sensor->thrs_critical.temp = (value & 0xff0) >> 4;
sensor->thrs_critical.hysteresis = value & 0xf;
}
break;
case 0x07:
if (thrs_section == 0) {
sensor->thrs_down_clock.temp = (value & 0xff0) >> 4;
sensor->thrs_down_clock.hysteresis = value & 0xf;
}
break;
case 0x08:
if (thrs_section == 0) {
sensor->thrs_fan_boost.temp = (value & 0xff0) >> 4;
sensor->thrs_fan_boost.hysteresis = value & 0xf;
}
break;
case 0x10:
if (sensor_section == 0)
sensor->offset_num = value;
break;
case 0x11:
if (sensor_section == 0)
sensor->offset_den = value;
break;
case 0x12:
if (sensor_section == 0)
sensor->slope_mult = value;
break;
case 0x13:
if (sensor_section == 0)
sensor->slope_div = value;
break;
case 0x32:
if (thrs_section == 0) {
sensor->thrs_shutdown.temp = (value & 0xff0) >> 4;
sensor->thrs_shutdown.hysteresis = value & 0xf;
}
break;
}
}
return 0;
}
int
nvbios_therm_fan_parse(struct nvkm_bios *bios, struct nvbios_therm_fan *fan)
{
struct nvbios_therm_trip_point *cur_trip = NULL;
u8 ver, len, i;
u32 entry;
uint8_t duty_lut[] = { 0, 0, 25, 0, 40, 0, 50, 0,
75, 0, 85, 0, 100, 0, 100, 0 };
i = 0;
fan->nr_fan_trip = 0;
fan->fan_mode = NVBIOS_THERM_FAN_OTHER;
while ((entry = nvbios_therm_entry(bios, i++, &ver, &len))) {
s16 value = nvbios_rd16(bios, entry + 1);
switch (nvbios_rd08(bios, entry + 0)) {
case 0x22:
fan->min_duty = value & 0xff;
fan->max_duty = (value & 0xff00) >> 8;
break;
case 0x24:
fan->nr_fan_trip++;
if (fan->fan_mode > NVBIOS_THERM_FAN_TRIP)
fan->fan_mode = NVBIOS_THERM_FAN_TRIP;
cur_trip = &fan->trip[fan->nr_fan_trip - 1];
cur_trip->hysteresis = value & 0xf;
cur_trip->temp = (value & 0xff0) >> 4;
cur_trip->fan_duty = duty_lut[(value & 0xf000) >> 12];
break;
case 0x25:
cur_trip = &fan->trip[fan->nr_fan_trip - 1];
cur_trip->fan_duty = value;
break;
case 0x26:
if (!fan->pwm_freq)
fan->pwm_freq = value;
break;
case 0x3b:
fan->bump_period = value;
break;
case 0x3c:
fan->slow_down_period = value;
break;
case 0x46:
if (fan->fan_mode > NVBIOS_THERM_FAN_LINEAR)
fan->fan_mode = NVBIOS_THERM_FAN_LINEAR;
fan->linear_min_temp = nvbios_rd08(bios, entry + 1);
fan->linear_max_temp = nvbios_rd08(bios, entry + 2);
break;
}
}
/* starting from fermi, fan management is always linear */
if (bios->subdev.device->card_type >= NV_C0 &&
fan->fan_mode == NVBIOS_THERM_FAN_OTHER) {
fan->fan_mode = NVBIOS_THERM_FAN_LINEAR;
}
return 0;
}
@@ -0,0 +1,173 @@
/*
* Copyright 2013 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include <subdev/bios.h>
#include <subdev/bios/bit.h>
#include <subdev/bios/timing.h>
u32
nvbios_timingTe(struct nvkm_bios *bios,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz)
{
struct bit_entry bit_P;
u32 timing = 0;
if (!bit_entry(bios, 'P', &bit_P)) {
if (bit_P.version == 1)
timing = nvbios_rd32(bios, bit_P.offset + 4);
else
if (bit_P.version == 2)
timing = nvbios_rd32(bios, bit_P.offset + 8);
if (timing) {
*ver = nvbios_rd08(bios, timing + 0);
switch (*ver) {
case 0x10:
*hdr = nvbios_rd08(bios, timing + 1);
*cnt = nvbios_rd08(bios, timing + 2);
*len = nvbios_rd08(bios, timing + 3);
*snr = 0;
*ssz = 0;
return timing;
case 0x20:
*hdr = nvbios_rd08(bios, timing + 1);
*cnt = nvbios_rd08(bios, timing + 5);
*len = nvbios_rd08(bios, timing + 2);
*snr = nvbios_rd08(bios, timing + 4);
*ssz = nvbios_rd08(bios, timing + 3);
return timing;
default:
break;
}
}
}
return 0;
}
u32
nvbios_timingEe(struct nvkm_bios *bios, int idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
u8 snr, ssz;
u32 timing = nvbios_timingTe(bios, ver, hdr, cnt, len, &snr, &ssz);
if (timing && idx < *cnt) {
timing += *hdr + idx * (*len + (snr * ssz));
*hdr = *len;
*cnt = snr;
*len = ssz;
return timing;
}
return 0;
}
u32
nvbios_timingEp(struct nvkm_bios *bios, int idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ramcfg *p)
{
u32 data = nvbios_timingEe(bios, idx, ver, hdr, cnt, len), temp;
p->timing_ver = *ver;
p->timing_hdr = *hdr;
switch (!!data * *ver) {
case 0x10:
p->timing_10_WR = nvbios_rd08(bios, data + 0x00);
p->timing_10_WTR = nvbios_rd08(bios, data + 0x01);
p->timing_10_CL = nvbios_rd08(bios, data + 0x02);
p->timing_10_RC = nvbios_rd08(bios, data + 0x03);
p->timing_10_RFC = nvbios_rd08(bios, data + 0x05);
p->timing_10_RAS = nvbios_rd08(bios, data + 0x07);
p->timing_10_RP = nvbios_rd08(bios, data + 0x09);
p->timing_10_RCDRD = nvbios_rd08(bios, data + 0x0a);
p->timing_10_RCDWR = nvbios_rd08(bios, data + 0x0b);
p->timing_10_RRD = nvbios_rd08(bios, data + 0x0c);
p->timing_10_13 = nvbios_rd08(bios, data + 0x0d);
p->timing_10_ODT = nvbios_rd08(bios, data + 0x0e) & 0x07;
if (p->ramcfg_ver >= 0x10)
p->ramcfg_RON = nvbios_rd08(bios, data + 0x0e) & 0x07;
p->timing_10_24 = 0xff;
p->timing_10_21 = 0;
p->timing_10_20 = 0;
p->timing_10_CWL = 0;
p->timing_10_18 = 0;
p->timing_10_16 = 0;
switch (min_t(u8, *hdr, 25)) {
case 25:
p->timing_10_24 = nvbios_rd08(bios, data + 0x18);
fallthrough;
case 24:
case 23:
case 22:
p->timing_10_21 = nvbios_rd08(bios, data + 0x15);
fallthrough;
case 21:
p->timing_10_20 = nvbios_rd08(bios, data + 0x14);
fallthrough;
case 20:
p->timing_10_CWL = nvbios_rd08(bios, data + 0x13);
fallthrough;
case 19:
p->timing_10_18 = nvbios_rd08(bios, data + 0x12);
fallthrough;
case 18:
case 17:
p->timing_10_16 = nvbios_rd08(bios, data + 0x10);
}
break;
case 0x20:
p->timing[0] = nvbios_rd32(bios, data + 0x00);
p->timing[1] = nvbios_rd32(bios, data + 0x04);
p->timing[2] = nvbios_rd32(bios, data + 0x08);
p->timing[3] = nvbios_rd32(bios, data + 0x0c);
p->timing[4] = nvbios_rd32(bios, data + 0x10);
p->timing[5] = nvbios_rd32(bios, data + 0x14);
p->timing[6] = nvbios_rd32(bios, data + 0x18);
p->timing[7] = nvbios_rd32(bios, data + 0x1c);
p->timing[8] = nvbios_rd32(bios, data + 0x20);
p->timing[9] = nvbios_rd32(bios, data + 0x24);
p->timing[10] = nvbios_rd32(bios, data + 0x28);
p->timing_20_2e_03 = (nvbios_rd08(bios, data + 0x2e) & 0x03) >> 0;
p->timing_20_2e_30 = (nvbios_rd08(bios, data + 0x2e) & 0x30) >> 4;
p->timing_20_2e_c0 = (nvbios_rd08(bios, data + 0x2e) & 0xc0) >> 6;
p->timing_20_2f_03 = (nvbios_rd08(bios, data + 0x2f) & 0x03) >> 0;
temp = nvbios_rd16(bios, data + 0x2c);
p->timing_20_2c_003f = (temp & 0x003f) >> 0;
p->timing_20_2c_1fc0 = (temp & 0x1fc0) >> 6;
p->timing_20_30_07 = (nvbios_rd08(bios, data + 0x30) & 0x07) >> 0;
p->timing_20_30_f8 = (nvbios_rd08(bios, data + 0x30) & 0xf8) >> 3;
temp = nvbios_rd16(bios, data + 0x31);
p->timing_20_31_0007 = (temp & 0x0007) >> 0;
p->timing_20_31_0078 = (temp & 0x0078) >> 3;
p->timing_20_31_0780 = (temp & 0x0780) >> 7;
p->timing_20_31_0800 = (temp & 0x0800) >> 11;
p->timing_20_31_7000 = (temp & 0x7000) >> 12;
p->timing_20_31_8000 = (temp & 0x8000) >> 15;
break;
default:
data = 0;
break;
}
return data;
}
@@ -0,0 +1,121 @@
/*
* Copyright 2012 Nouveau Community
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Martin Peres
*/
#include <subdev/bios.h>
#include <subdev/bios/bit.h>
#include <subdev/bios/vmap.h>
u32
nvbios_vmap_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
struct bit_entry bit_P;
u32 vmap = 0;
if (!bit_entry(bios, 'P', &bit_P)) {
if (bit_P.version == 2) {
vmap = nvbios_rd32(bios, bit_P.offset + 0x20);
if (vmap) {
*ver = nvbios_rd08(bios, vmap + 0);
switch (*ver) {
case 0x10:
case 0x20:
*hdr = nvbios_rd08(bios, vmap + 1);
*cnt = nvbios_rd08(bios, vmap + 3);
*len = nvbios_rd08(bios, vmap + 2);
return vmap;
default:
break;
}
}
}
}
return 0;
}
u32
nvbios_vmap_parse(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
struct nvbios_vmap *info)
{
u32 vmap = nvbios_vmap_table(bios, ver, hdr, cnt, len);
memset(info, 0x00, sizeof(*info));
switch (!!vmap * *ver) {
case 0x10:
info->max0 = 0xff;
info->max1 = 0xff;
info->max2 = 0xff;
break;
case 0x20:
info->max0 = nvbios_rd08(bios, vmap + 0x7);
info->max1 = nvbios_rd08(bios, vmap + 0x8);
if (*len >= 0xc)
info->max2 = nvbios_rd08(bios, vmap + 0xc);
else
info->max2 = 0xff;
break;
}
return vmap;
}
u32
nvbios_vmap_entry(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len)
{
u8 hdr, cnt;
u32 vmap = nvbios_vmap_table(bios, ver, &hdr, &cnt, len);
if (vmap && idx < cnt) {
vmap = vmap + hdr + (idx * *len);
return vmap;
}
return 0;
}
u32
nvbios_vmap_entry_parse(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len,
struct nvbios_vmap_entry *info)
{
u32 vmap = nvbios_vmap_entry(bios, idx, ver, len);
memset(info, 0x00, sizeof(*info));
switch (!!vmap * *ver) {
case 0x10:
info->link = 0xff;
info->min = nvbios_rd32(bios, vmap + 0x00);
info->max = nvbios_rd32(bios, vmap + 0x04);
info->arg[0] = nvbios_rd32(bios, vmap + 0x08);
info->arg[1] = nvbios_rd32(bios, vmap + 0x0c);
info->arg[2] = nvbios_rd32(bios, vmap + 0x10);
break;
case 0x20:
info->mode = nvbios_rd08(bios, vmap + 0x00);
info->link = nvbios_rd08(bios, vmap + 0x01);
info->min = nvbios_rd32(bios, vmap + 0x02);
info->max = nvbios_rd32(bios, vmap + 0x06);
info->arg[0] = nvbios_rd32(bios, vmap + 0x0a);
info->arg[1] = nvbios_rd32(bios, vmap + 0x0e);
info->arg[2] = nvbios_rd32(bios, vmap + 0x12);
info->arg[3] = nvbios_rd32(bios, vmap + 0x16);
info->arg[4] = nvbios_rd32(bios, vmap + 0x1a);
info->arg[5] = nvbios_rd32(bios, vmap + 0x1e);
break;
}
return vmap;
}
@@ -0,0 +1,160 @@
/*
* Copyright 2012 Nouveau Community
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Martin Peres
*/
#include <subdev/bios.h>
#include <subdev/bios/bit.h>
#include <subdev/bios/volt.h>
u32
nvbios_volt_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
struct bit_entry bit_P;
u32 volt = 0;
if (!bit_entry(bios, 'P', &bit_P)) {
if (bit_P.version == 2)
volt = nvbios_rd32(bios, bit_P.offset + 0x0c);
else
if (bit_P.version == 1)
volt = nvbios_rd32(bios, bit_P.offset + 0x10);
if (volt) {
*ver = nvbios_rd08(bios, volt + 0);
switch (*ver) {
case 0x12:
*hdr = 5;
*cnt = nvbios_rd08(bios, volt + 2);
*len = nvbios_rd08(bios, volt + 1);
return volt;
case 0x20:
*hdr = nvbios_rd08(bios, volt + 1);
*cnt = nvbios_rd08(bios, volt + 2);
*len = nvbios_rd08(bios, volt + 3);
return volt;
case 0x30:
case 0x40:
case 0x50:
*hdr = nvbios_rd08(bios, volt + 1);
*cnt = nvbios_rd08(bios, volt + 3);
*len = nvbios_rd08(bios, volt + 2);
return volt;
}
}
}
return 0;
}
u32
nvbios_volt_parse(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
struct nvbios_volt *info)
{
u32 volt = nvbios_volt_table(bios, ver, hdr, cnt, len);
memset(info, 0x00, sizeof(*info));
switch (!!volt * *ver) {
case 0x12:
info->type = NVBIOS_VOLT_GPIO;
info->vidmask = nvbios_rd08(bios, volt + 0x04);
info->ranged = false;
break;
case 0x20:
info->type = NVBIOS_VOLT_GPIO;
info->vidmask = nvbios_rd08(bios, volt + 0x05);
info->ranged = false;
break;
case 0x30:
info->type = NVBIOS_VOLT_GPIO;
info->vidmask = nvbios_rd08(bios, volt + 0x04);
info->ranged = false;
break;
case 0x40:
info->type = NVBIOS_VOLT_GPIO;
info->base = nvbios_rd32(bios, volt + 0x04);
info->step = nvbios_rd16(bios, volt + 0x08);
info->vidmask = nvbios_rd08(bios, volt + 0x0b);
info->ranged = true; /* XXX: find the flag byte */
info->min = min(info->base,
info->base + info->step * info->vidmask);
info->max = nvbios_rd32(bios, volt + 0x0e);
if (!info->max)
info->max = max(info->base, info->base + info->step * info->vidmask);
break;
case 0x50:
info->min = nvbios_rd32(bios, volt + 0x0a);
info->max = nvbios_rd32(bios, volt + 0x0e);
info->base = nvbios_rd32(bios, volt + 0x12) & 0x00ffffff;
/* offset 4 seems to be a flag byte */
if (nvbios_rd32(bios, volt + 0x4) & 1) {
info->type = NVBIOS_VOLT_PWM;
info->pwm_freq = nvbios_rd32(bios, volt + 0x5) / 1000;
info->pwm_range = nvbios_rd32(bios, volt + 0x16);
} else {
info->type = NVBIOS_VOLT_GPIO;
info->vidmask = nvbios_rd08(bios, volt + 0x06);
info->step = nvbios_rd16(bios, volt + 0x16);
info->ranged =
!!(nvbios_rd08(bios, volt + 0x4) & 0x2);
}
break;
}
return volt;
}
u32
nvbios_volt_entry(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len)
{
u8 hdr, cnt;
u32 volt = nvbios_volt_table(bios, ver, &hdr, &cnt, len);
if (volt && idx < cnt) {
volt = volt + hdr + (idx * *len);
return volt;
}
return 0;
}
u32
nvbios_volt_entry_parse(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len,
struct nvbios_volt_entry *info)
{
u32 volt = nvbios_volt_entry(bios, idx, ver, len);
memset(info, 0x00, sizeof(*info));
switch (!!volt * *ver) {
case 0x12:
case 0x20:
info->voltage = nvbios_rd08(bios, volt + 0x00) * 10000;
info->vid = nvbios_rd08(bios, volt + 0x01);
break;
case 0x30:
info->voltage = nvbios_rd08(bios, volt + 0x00) * 10000;
info->vid = nvbios_rd08(bios, volt + 0x01) >> 2;
break;
case 0x40:
break;
case 0x50:
info->voltage = nvbios_rd32(bios, volt) & 0x001fffff;
info->vid = (nvbios_rd32(bios, volt) >> 23) & 0xff;
break;
}
return volt;
}
@@ -0,0 +1,88 @@
/*
* Copyright 2016 Karol Herbst
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Karol Herbst
*/
#include <subdev/bios.h>
#include <subdev/bios/bit.h>
#include <subdev/bios/vpstate.h>
static u32
nvbios_vpstate_offset(struct nvkm_bios *b)
{
struct bit_entry bit_P;
if (!bit_entry(b, 'P', &bit_P)) {
if (bit_P.version == 2 && bit_P.length >= 0x3c)
return nvbios_rd32(b, bit_P.offset + 0x38);
}
return 0x0000;
}
int
nvbios_vpstate_parse(struct nvkm_bios *b, struct nvbios_vpstate_header *h)
{
if (!h)
return -EINVAL;
h->offset = nvbios_vpstate_offset(b);
if (!h->offset)
return -ENODEV;
h->version = nvbios_rd08(b, h->offset);
switch (h->version) {
case 0x10:
h->hlen = nvbios_rd08(b, h->offset + 0x1);
h->elen = nvbios_rd08(b, h->offset + 0x2);
h->slen = nvbios_rd08(b, h->offset + 0x3);
h->scount = nvbios_rd08(b, h->offset + 0x4);
h->ecount = nvbios_rd08(b, h->offset + 0x5);
h->base_id = nvbios_rd08(b, h->offset + 0x0f);
if (h->hlen > 0x10)
h->boost_id = nvbios_rd08(b, h->offset + 0x10);
else
h->boost_id = 0xff;
if (h->hlen > 0x11)
h->tdp_id = nvbios_rd08(b, h->offset + 0x11);
else
h->tdp_id = 0xff;
return 0;
default:
return -EINVAL;
}
}
int
nvbios_vpstate_entry(struct nvkm_bios *b, struct nvbios_vpstate_header *h,
u8 idx, struct nvbios_vpstate_entry *e)
{
u32 offset;
if (!e || !h || idx > h->ecount)
return -EINVAL;
offset = h->offset + h->hlen + idx * (h->elen + (h->slen * h->scount));
e->pstate = nvbios_rd08(b, offset);
e->clock_mhz = nvbios_rd16(b, offset + 0x5);
return 0;
}
@@ -0,0 +1,74 @@
/*
* Copyright 2012 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include <subdev/bios.h>
#include <subdev/bios/gpio.h>
#include <subdev/bios/xpio.h>
static u16
dcb_xpiod_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
u16 data = dcb_gpio_table(bios, ver, hdr, cnt, len);
if (data && *ver >= 0x40 && *hdr >= 0x06) {
u16 xpio = nvbios_rd16(bios, data + 0x04);
if (xpio) {
*ver = nvbios_rd08(bios, data + 0x00);
*hdr = nvbios_rd08(bios, data + 0x01);
*cnt = nvbios_rd08(bios, data + 0x02);
*len = nvbios_rd08(bios, data + 0x03);
return xpio;
}
}
return 0x0000;
}
u16
dcb_xpio_table(struct nvkm_bios *bios, u8 idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
u16 data = dcb_xpiod_table(bios, ver, hdr, cnt, len);
if (data && idx < *cnt) {
u16 xpio = nvbios_rd16(bios, data + *hdr + (idx * *len));
if (xpio) {
*ver = nvbios_rd08(bios, data + 0x00);
*hdr = nvbios_rd08(bios, data + 0x01);
*cnt = nvbios_rd08(bios, data + 0x02);
*len = nvbios_rd08(bios, data + 0x03);
return xpio;
}
}
return 0x0000;
}
u16
dcb_xpio_parse(struct nvkm_bios *bios, u8 idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_xpio *info)
{
u16 data = dcb_xpio_table(bios, idx, ver, hdr, cnt, len);
if (data && *len >= 6) {
info->type = nvbios_rd08(bios, data + 0x04);
info->addr = nvbios_rd08(bios, data + 0x05);
info->flags = nvbios_rd08(bios, data + 0x06);
}
return 0x0000;
}