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