1 /*
2  * Copyright 2015 Martin Peres
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * Authors: Martin Peres
23  */
24 #include <subdev/bios.h>
25 #include <subdev/bios/bit.h>
26 #include <subdev/bios/iccsense.h>
27 
28 static u16
29 nvbios_iccsense_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt,
30 		      u8 *len)
31 {
32 	struct bit_entry bit_P;
33 	u16 iccsense;
34 
35 	if (bit_entry(bios, 'P', &bit_P) || bit_P.version != 2 ||
36 	    bit_P.length < 0x2c)
37 		return 0;
38 
39 	iccsense = nvbios_rd16(bios, bit_P.offset + 0x28);
40 	if (!iccsense)
41 		return 0;
42 
43 	*ver = nvbios_rd08(bios, iccsense + 0);
44 	switch (*ver) {
45 	case 0x10:
46 	case 0x20:
47 		*hdr = nvbios_rd08(bios, iccsense + 1);
48 		*len = nvbios_rd08(bios, iccsense + 2);
49 		*cnt = nvbios_rd08(bios, iccsense + 3);
50 		return iccsense;
51 	default:
52 		break;
53 	}
54 
55 	return 0;
56 }
57 
58 int
59 nvbios_iccsense_parse(struct nvkm_bios *bios, struct nvbios_iccsense *iccsense)
60 {
61 	struct nvkm_subdev *subdev = &bios->subdev;
62 	u8 ver, hdr, cnt, len, i;
63 	u16 table, entry;
64 
65 	table = nvbios_iccsense_table(bios, &ver, &hdr, &cnt, &len);
66 	if (!table || !cnt)
67 		return -EINVAL;
68 
69 	if (ver != 0x10 && ver != 0x20) {
70 		nvkm_error(subdev, "ICCSENSE version 0x%02x unknown\n", ver);
71 		return -EINVAL;
72 	}
73 
74 	iccsense->nr_entry = cnt;
75 	iccsense->rail = kmalloc(sizeof(struct pwr_rail_t) * cnt, GFP_KERNEL);
76 	if (!iccsense->rail)
77 		return -ENOMEM;
78 
79 	for (i = 0; i < cnt; ++i) {
80 		struct pwr_rail_t *rail = &iccsense->rail[i];
81 		entry = table + hdr + i * len;
82 
83 		switch(ver) {
84 		case 0x10:
85 			rail->mode = nvbios_rd08(bios, entry + 0x1);
86 			rail->extdev_id = nvbios_rd08(bios, entry + 0x2);
87 			rail->resistor_mohm = nvbios_rd08(bios, entry + 0x3);
88 			rail->rail = nvbios_rd08(bios, entry + 0x4);
89 			break;
90 		case 0x20:
91 			rail->mode = nvbios_rd08(bios, entry);
92 			rail->extdev_id = nvbios_rd08(bios, entry + 0x1);
93 			rail->resistor_mohm = nvbios_rd08(bios, entry + 0x5);
94 			rail->rail = nvbios_rd08(bios, entry + 0x6);
95 			break;
96 		};
97 	}
98 
99 	return 0;
100 }
101