1/* $NetBSD: nouveau_nvkm_subdev_bios_volt.c,v 1.3 2021/12/18 23:45:38 riastradh Exp $ */ 2 3/* 4 * Copyright 2012 Nouveau Community 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 * 24 * Authors: Martin Peres 25 */ 26#include <sys/cdefs.h> 27__KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_subdev_bios_volt.c,v 1.3 2021/12/18 23:45:38 riastradh Exp $"); 28 29#include <subdev/bios.h> 30#include <subdev/bios/bit.h> 31#include <subdev/bios/volt.h> 32 33u32 34nvbios_volt_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) 35{ 36 struct bit_entry bit_P; 37 u32 volt = 0; 38 39 if (!bit_entry(bios, 'P', &bit_P)) { 40 if (bit_P.version == 2) 41 volt = nvbios_rd32(bios, bit_P.offset + 0x0c); 42 else 43 if (bit_P.version == 1) 44 volt = nvbios_rd32(bios, bit_P.offset + 0x10); 45 46 if (volt) { 47 *ver = nvbios_rd08(bios, volt + 0); 48 switch (*ver) { 49 case 0x12: 50 *hdr = 5; 51 *cnt = nvbios_rd08(bios, volt + 2); 52 *len = nvbios_rd08(bios, volt + 1); 53 return volt; 54 case 0x20: 55 *hdr = nvbios_rd08(bios, volt + 1); 56 *cnt = nvbios_rd08(bios, volt + 2); 57 *len = nvbios_rd08(bios, volt + 3); 58 return volt; 59 case 0x30: 60 case 0x40: 61 case 0x50: 62 *hdr = nvbios_rd08(bios, volt + 1); 63 *cnt = nvbios_rd08(bios, volt + 3); 64 *len = nvbios_rd08(bios, volt + 2); 65 return volt; 66 } 67 } 68 } 69 70 return 0; 71} 72 73u32 74nvbios_volt_parse(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, 75 struct nvbios_volt *info) 76{ 77 u32 volt = nvbios_volt_table(bios, ver, hdr, cnt, len); 78 memset(info, 0x00, sizeof(*info)); 79 switch (!!volt * *ver) { 80 case 0x12: 81 info->type = NVBIOS_VOLT_GPIO; 82 info->vidmask = nvbios_rd08(bios, volt + 0x04); 83 info->ranged = false; 84 break; 85 case 0x20: 86 info->type = NVBIOS_VOLT_GPIO; 87 info->vidmask = nvbios_rd08(bios, volt + 0x05); 88 info->ranged = false; 89 break; 90 case 0x30: 91 info->type = NVBIOS_VOLT_GPIO; 92 info->vidmask = nvbios_rd08(bios, volt + 0x04); 93 info->ranged = false; 94 break; 95 case 0x40: 96 info->type = NVBIOS_VOLT_GPIO; 97 info->base = nvbios_rd32(bios, volt + 0x04); 98 info->step = nvbios_rd16(bios, volt + 0x08); 99 info->vidmask = nvbios_rd08(bios, volt + 0x0b); 100 info->ranged = true; /* XXX: find the flag byte */ 101 info->min = min(info->base, 102 info->base + info->step * info->vidmask); 103 info->max = nvbios_rd32(bios, volt + 0x0e); 104 if (!info->max) 105 info->max = max(info->base, info->base + info->step * info->vidmask); 106 break; 107 case 0x50: 108 info->min = nvbios_rd32(bios, volt + 0x0a); 109 info->max = nvbios_rd32(bios, volt + 0x0e); 110 info->base = nvbios_rd32(bios, volt + 0x12) & 0x00ffffff; 111 112 /* offset 4 seems to be a flag byte */ 113 if (nvbios_rd32(bios, volt + 0x4) & 1) { 114 info->type = NVBIOS_VOLT_PWM; 115 info->pwm_freq = nvbios_rd32(bios, volt + 0x5) / 1000; 116 info->pwm_range = nvbios_rd32(bios, volt + 0x16); 117 } else { 118 info->type = NVBIOS_VOLT_GPIO; 119 info->vidmask = nvbios_rd08(bios, volt + 0x06); 120 info->step = nvbios_rd16(bios, volt + 0x16); 121 info->ranged = 122 !!(nvbios_rd08(bios, volt + 0x4) & 0x2); 123 } 124 break; 125 } 126 return volt; 127} 128 129u32 130nvbios_volt_entry(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len) 131{ 132 u8 hdr, cnt; 133 u32 volt = nvbios_volt_table(bios, ver, &hdr, &cnt, len); 134 if (volt && idx < cnt) { 135 volt = volt + hdr + (idx * *len); 136 return volt; 137 } 138 return 0; 139} 140 141u32 142nvbios_volt_entry_parse(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len, 143 struct nvbios_volt_entry *info) 144{ 145 u32 volt = nvbios_volt_entry(bios, idx, ver, len); 146 memset(info, 0x00, sizeof(*info)); 147 switch (!!volt * *ver) { 148 case 0x12: 149 case 0x20: 150 info->voltage = nvbios_rd08(bios, volt + 0x00) * 10000; 151 info->vid = nvbios_rd08(bios, volt + 0x01); 152 break; 153 case 0x30: 154 info->voltage = nvbios_rd08(bios, volt + 0x00) * 10000; 155 info->vid = nvbios_rd08(bios, volt + 0x01) >> 2; 156 break; 157 case 0x40: 158 break; 159 case 0x50: 160 info->voltage = nvbios_rd32(bios, volt) & 0x001fffff; 161 info->vid = (nvbios_rd32(bios, volt) >> 23) & 0xff; 162 break; 163 } 164 return volt; 165} 166