xref: /openbmc/linux/drivers/gpu/drm/nouveau/nvkm/subdev/bios/volt.c (revision 976e3645923bdd2fe7893aae33fd7a21098bfb28)
1c39f472eSBen Skeggs /*
2c39f472eSBen Skeggs  * Copyright 2012 Nouveau Community
3c39f472eSBen Skeggs  *
4c39f472eSBen Skeggs  * Permission is hereby granted, free of charge, to any person obtaining a
5c39f472eSBen Skeggs  * copy of this software and associated documentation files (the "Software"),
6c39f472eSBen Skeggs  * to deal in the Software without restriction, including without limitation
7c39f472eSBen Skeggs  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8c39f472eSBen Skeggs  * and/or sell copies of the Software, and to permit persons to whom the
9c39f472eSBen Skeggs  * Software is furnished to do so, subject to the following conditions:
10c39f472eSBen Skeggs  *
11c39f472eSBen Skeggs  * The above copyright notice and this permission notice shall be included in
12c39f472eSBen Skeggs  * all copies or substantial portions of the Software.
13c39f472eSBen Skeggs  *
14c39f472eSBen Skeggs  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15c39f472eSBen Skeggs  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16c39f472eSBen Skeggs  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17c39f472eSBen Skeggs  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18c39f472eSBen Skeggs  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19c39f472eSBen Skeggs  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20c39f472eSBen Skeggs  * OTHER DEALINGS IN THE SOFTWARE.
21c39f472eSBen Skeggs  *
22c39f472eSBen Skeggs  * Authors: Martin Peres
23c39f472eSBen Skeggs  */
24c39f472eSBen Skeggs #include <subdev/bios.h>
25c39f472eSBen Skeggs #include <subdev/bios/bit.h>
26c39f472eSBen Skeggs #include <subdev/bios/volt.h>
27c39f472eSBen Skeggs 
28ff535412SBen Skeggs u32
nvbios_volt_table(struct nvkm_bios * bios,u8 * ver,u8 * hdr,u8 * cnt,u8 * len)29d390b480SBen Skeggs nvbios_volt_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
30c39f472eSBen Skeggs {
31c39f472eSBen Skeggs 	struct bit_entry bit_P;
32ff535412SBen Skeggs 	u32 volt = 0;
33c39f472eSBen Skeggs 
34c39f472eSBen Skeggs 	if (!bit_entry(bios, 'P', &bit_P)) {
35c39f472eSBen Skeggs 		if (bit_P.version == 2)
36ff535412SBen Skeggs 			volt = nvbios_rd32(bios, bit_P.offset + 0x0c);
37c39f472eSBen Skeggs 		else
38c39f472eSBen Skeggs 		if (bit_P.version == 1)
39ff535412SBen Skeggs 			volt = nvbios_rd32(bios, bit_P.offset + 0x10);
40c39f472eSBen Skeggs 
41c39f472eSBen Skeggs 		if (volt) {
427f5f518fSBen Skeggs 			*ver = nvbios_rd08(bios, volt + 0);
43c39f472eSBen Skeggs 			switch (*ver) {
44c39f472eSBen Skeggs 			case 0x12:
45c39f472eSBen Skeggs 				*hdr = 5;
467f5f518fSBen Skeggs 				*cnt = nvbios_rd08(bios, volt + 2);
477f5f518fSBen Skeggs 				*len = nvbios_rd08(bios, volt + 1);
48c39f472eSBen Skeggs 				return volt;
49c39f472eSBen Skeggs 			case 0x20:
507f5f518fSBen Skeggs 				*hdr = nvbios_rd08(bios, volt + 1);
517f5f518fSBen Skeggs 				*cnt = nvbios_rd08(bios, volt + 2);
527f5f518fSBen Skeggs 				*len = nvbios_rd08(bios, volt + 3);
53c39f472eSBen Skeggs 				return volt;
54c39f472eSBen Skeggs 			case 0x30:
55c39f472eSBen Skeggs 			case 0x40:
56c39f472eSBen Skeggs 			case 0x50:
577f5f518fSBen Skeggs 				*hdr = nvbios_rd08(bios, volt + 1);
587f5f518fSBen Skeggs 				*cnt = nvbios_rd08(bios, volt + 3);
597f5f518fSBen Skeggs 				*len = nvbios_rd08(bios, volt + 2);
60c39f472eSBen Skeggs 				return volt;
61c39f472eSBen Skeggs 			}
62c39f472eSBen Skeggs 		}
63c39f472eSBen Skeggs 	}
64c39f472eSBen Skeggs 
65ff535412SBen Skeggs 	return 0;
66c39f472eSBen Skeggs }
67c39f472eSBen Skeggs 
68ff535412SBen Skeggs u32
nvbios_volt_parse(struct nvkm_bios * bios,u8 * ver,u8 * hdr,u8 * cnt,u8 * len,struct nvbios_volt * info)69d390b480SBen Skeggs nvbios_volt_parse(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
70c39f472eSBen Skeggs 		  struct nvbios_volt *info)
71c39f472eSBen Skeggs {
72ff535412SBen Skeggs 	u32 volt = nvbios_volt_table(bios, ver, hdr, cnt, len);
73c39f472eSBen Skeggs 	memset(info, 0x00, sizeof(*info));
74c39f472eSBen Skeggs 	switch (!!volt * *ver) {
75c39f472eSBen Skeggs 	case 0x12:
767bddeba9SMartin Peres 		info->type    = NVBIOS_VOLT_GPIO;
777f5f518fSBen Skeggs 		info->vidmask = nvbios_rd08(bios, volt + 0x04);
7817f486deSKarol Herbst 		info->ranged  = false;
79c39f472eSBen Skeggs 		break;
80c39f472eSBen Skeggs 	case 0x20:
817bddeba9SMartin Peres 		info->type    = NVBIOS_VOLT_GPIO;
827f5f518fSBen Skeggs 		info->vidmask = nvbios_rd08(bios, volt + 0x05);
8317f486deSKarol Herbst 		info->ranged  = false;
84c39f472eSBen Skeggs 		break;
85c39f472eSBen Skeggs 	case 0x30:
867bddeba9SMartin Peres 		info->type    = NVBIOS_VOLT_GPIO;
877f5f518fSBen Skeggs 		info->vidmask = nvbios_rd08(bios, volt + 0x04);
8817f486deSKarol Herbst 		info->ranged  = false;
89c39f472eSBen Skeggs 		break;
90c39f472eSBen Skeggs 	case 0x40:
917bddeba9SMartin Peres 		info->type    = NVBIOS_VOLT_GPIO;
927f5f518fSBen Skeggs 		info->base    = nvbios_rd32(bios, volt + 0x04);
937f5f518fSBen Skeggs 		info->step    = nvbios_rd16(bios, volt + 0x08);
947f5f518fSBen Skeggs 		info->vidmask = nvbios_rd08(bios, volt + 0x0b);
9517f486deSKarol Herbst 		info->ranged  = true; /* XXX: find the flag byte */
96c0cd0470SKarol Herbst 		info->min     = min(info->base,
97c0cd0470SKarol Herbst 				    info->base + info->step * info->vidmask);
98c0cd0470SKarol Herbst 		info->max     = nvbios_rd32(bios, volt + 0x0e);
99*a1af2afbSMark Menzynski 		if (!info->max)
100*a1af2afbSMark Menzynski 			info->max = max(info->base, info->base + info->step * info->vidmask);
101c39f472eSBen Skeggs 		break;
102c39f472eSBen Skeggs 	case 0x50:
1037f5f518fSBen Skeggs 		info->min     = nvbios_rd32(bios, volt + 0x0a);
1047f5f518fSBen Skeggs 		info->max     = nvbios_rd32(bios, volt + 0x0e);
1057f5f518fSBen Skeggs 		info->base    = nvbios_rd32(bios, volt + 0x12) & 0x00ffffff;
1067bddeba9SMartin Peres 
1077bddeba9SMartin Peres 		/* offset 4 seems to be a flag byte */
1087bddeba9SMartin Peres 		if (nvbios_rd32(bios, volt + 0x4) & 1) {
1097bddeba9SMartin Peres 			info->type      = NVBIOS_VOLT_PWM;
1107bddeba9SMartin Peres 			info->pwm_freq  = nvbios_rd32(bios, volt + 0x5) / 1000;
1117bddeba9SMartin Peres 			info->pwm_range = nvbios_rd32(bios, volt + 0x16);
1127bddeba9SMartin Peres 		} else {
1137bddeba9SMartin Peres 			info->type    = NVBIOS_VOLT_GPIO;
1147bddeba9SMartin Peres 			info->vidmask = nvbios_rd08(bios, volt + 0x06);
1157f5f518fSBen Skeggs 			info->step    = nvbios_rd16(bios, volt + 0x16);
11617f486deSKarol Herbst 			info->ranged  =
11717f486deSKarol Herbst 				!!(nvbios_rd08(bios, volt + 0x4) & 0x2);
1187bddeba9SMartin Peres 		}
119c39f472eSBen Skeggs 		break;
120c39f472eSBen Skeggs 	}
121c39f472eSBen Skeggs 	return volt;
122c39f472eSBen Skeggs }
123c39f472eSBen Skeggs 
124ff535412SBen Skeggs u32
nvbios_volt_entry(struct nvkm_bios * bios,int idx,u8 * ver,u8 * len)125d390b480SBen Skeggs nvbios_volt_entry(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len)
126c39f472eSBen Skeggs {
127c39f472eSBen Skeggs 	u8  hdr, cnt;
128ff535412SBen Skeggs 	u32 volt = nvbios_volt_table(bios, ver, &hdr, &cnt, len);
129c39f472eSBen Skeggs 	if (volt && idx < cnt) {
130c39f472eSBen Skeggs 		volt = volt + hdr + (idx * *len);
131c39f472eSBen Skeggs 		return volt;
132c39f472eSBen Skeggs 	}
133ff535412SBen Skeggs 	return 0;
134c39f472eSBen Skeggs }
135c39f472eSBen Skeggs 
136ff535412SBen Skeggs u32
nvbios_volt_entry_parse(struct nvkm_bios * bios,int idx,u8 * ver,u8 * len,struct nvbios_volt_entry * info)137d390b480SBen Skeggs nvbios_volt_entry_parse(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len,
138c39f472eSBen Skeggs 			struct nvbios_volt_entry *info)
139c39f472eSBen Skeggs {
140ff535412SBen Skeggs 	u32 volt = nvbios_volt_entry(bios, idx, ver, len);
141c39f472eSBen Skeggs 	memset(info, 0x00, sizeof(*info));
142c39f472eSBen Skeggs 	switch (!!volt * *ver) {
143c39f472eSBen Skeggs 	case 0x12:
144c39f472eSBen Skeggs 	case 0x20:
1457f5f518fSBen Skeggs 		info->voltage = nvbios_rd08(bios, volt + 0x00) * 10000;
1467f5f518fSBen Skeggs 		info->vid     = nvbios_rd08(bios, volt + 0x01);
147c39f472eSBen Skeggs 		break;
148c39f472eSBen Skeggs 	case 0x30:
1497f5f518fSBen Skeggs 		info->voltage = nvbios_rd08(bios, volt + 0x00) * 10000;
1507f5f518fSBen Skeggs 		info->vid     = nvbios_rd08(bios, volt + 0x01) >> 2;
151c39f472eSBen Skeggs 		break;
152c39f472eSBen Skeggs 	case 0x40:
15332dd7f23SKarol Herbst 		break;
154c39f472eSBen Skeggs 	case 0x50:
15532dd7f23SKarol Herbst 		info->voltage = nvbios_rd32(bios, volt) & 0x001fffff;
15632dd7f23SKarol Herbst 		info->vid     = (nvbios_rd32(bios, volt) >> 23) & 0xff;
157c39f472eSBen Skeggs 		break;
158c39f472eSBen Skeggs 	}
159c39f472eSBen Skeggs 	return volt;
160c39f472eSBen Skeggs }
161