Linux-libre 4.9.88-gnu
[librecmc/linux-libre.git] / drivers / gpu / drm / nouveau / nvkm / subdev / bios / iccsense.c
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 }