9d3840f1f7230f3ce3df786300fd680d7981643b
[coreboot.git] / util / superiotool / superiotool.c
1 /*
2  * This file is part of the superiotool project.
3  *
4  * Copyright (C) 2006 Ronald Minnich <rminnich@gmail.com>
5  * Copyright (C) 2007 Uwe Hermann <uwe@hermann-uwe.de>
6  * Copyright (C) 2007 Carl-Daniel Hailfinger
7  * Copyright (C) 2008 Robinson P. Tryon <bishop.robinson@gmail.com>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
22  */
23
24 #include "superiotool.h"
25
26 #if defined(__FreeBSD__)
27 #include <fcntl.h>
28 #include <unistd.h>
29 #endif
30
31 /* Command line options. */
32 int dump = 0, verbose = 0, extra_dump = 0;
33
34 /* Global flag which indicates whether a chip was detected at all. */
35 int chip_found = 0;
36
37 uint8_t regval(uint16_t port, uint8_t reg)
38 {
39         OUTB(reg, port);
40         return INB(port + ((port == 0x3bd) ? 2 : 1)); /* 0x3bd is special. */
41 }
42
43 void regwrite(uint16_t port, uint8_t reg, uint8_t val)
44 {
45         OUTB(reg, port);
46         OUTB(val, port + 1);
47 }
48
49 void enter_conf_mode_winbond_fintek_ite_8787(uint16_t port)
50 {
51         OUTB(0x87, port);
52         OUTB(0x87, port);
53 }
54
55 void exit_conf_mode_winbond_fintek_ite_8787(uint16_t port)
56 {
57         OUTB(0xaa, port);               /* Fintek, Winbond */
58         regwrite(port, 0x02, 0x02);     /* ITE */
59 }
60
61 int superio_unknown(const struct superio_registers reg_table[], uint16_t id)
62 {
63         return !strncmp(get_superio_name(reg_table, id), "<unknown>", 9);
64 }
65
66 const char *get_superio_name(const struct superio_registers reg_table[],
67                              uint16_t id)
68 {
69         int i;
70
71         for (i = 0; /* Nothing */; i++) {
72                 if (reg_table[i].superio_id == EOT)
73                         break;
74
75                 if ((uint16_t)reg_table[i].superio_id != id)
76                         continue;
77
78                 return reg_table[i].name;
79         }
80
81         return "<unknown>";
82 }
83
84 static void dump_regs(const struct superio_registers reg_table[],
85                       int i, int j, uint16_t port, uint8_t ldn_sel)
86 {
87         int k;
88         const int16_t *idx;
89
90         if (reg_table[i].ldn[j].ldn != NOLDN) {
91                 printf("LDN 0x%02x", reg_table[i].ldn[j].ldn);
92                 if (reg_table[i].ldn[j].name != NULL)
93                         printf(" (%s)", reg_table[i].ldn[j].name);
94                 regwrite(port, ldn_sel, reg_table[i].ldn[j].ldn);
95         } else {
96                 printf("Register dump:");
97         }
98
99         idx = reg_table[i].ldn[j].idx;
100
101         printf("\nidx");
102         for (k = 0; idx[k] != EOT; k++) {
103                 if (k && !(k % 8))
104                         putchar(' ');
105                 printf(" %02x", idx[k]);
106         }
107
108         printf("\nval");
109         for (k = 0; idx[k] != EOT; k++) {
110                 if (k && !(k % 8))
111                         putchar(' ');
112                 printf(" %02x", regval(port, idx[k]));
113         }
114
115         printf("\ndef");
116         idx = reg_table[i].ldn[j].def;
117         for (k = 0; idx[k] != EOT; k++) {
118                 if (k && !(k % 8))
119                         putchar(' ');
120                 if (idx[k] == NANA)
121                         printf(" NA");
122                 else if (idx[k] == RSVD)
123                         printf(" RR");
124                 else if (idx[k] == MISC)
125                         printf(" MM");
126                 else
127                         printf(" %02x", idx[k]);
128         }
129         printf("\n");
130 }
131
132 void dump_superio(const char *vendor,
133                   const struct superio_registers reg_table[],
134                   uint16_t port, uint16_t id, uint8_t ldn_sel)
135 {
136         int i, j, no_dump_available = 1;
137
138         if (!dump)
139                 return;
140
141         for (i = 0; /* Nothing */; i++) {
142                 if (reg_table[i].superio_id == EOT)
143                         break;
144
145                 if ((uint16_t)reg_table[i].superio_id != id)
146                         continue;
147
148                 for (j = 0; /* Nothing */; j++) {
149                         if (reg_table[i].ldn[j].ldn == EOT)
150                                 break;
151                         no_dump_available = 0;
152                         dump_regs(reg_table, i, j, port, ldn_sel);
153                 }
154
155                 if (no_dump_available)
156                         printf("No dump available for this Super I/O\n");
157         }
158 }
159
160 void probing_for(const char *vendor, const char *info, uint16_t port)
161 {
162         if (!verbose)
163                 return;
164
165         /* Yes, there's no space between '%s' and 'at'! */
166         printf("Probing for %s Super I/O %sat 0x%x...\n", vendor, info, port);
167 }
168
169 /** Print a list of all supported chips from the given vendor. */
170 void print_vendor_chips(const char *vendor,
171                         const struct superio_registers reg_table[])
172 {
173         int i;
174
175         for (i = 0; reg_table[i].superio_id != EOT; i++) {
176                 printf("%s %s", vendor, reg_table[i].name);
177
178                 /* Unless the ldn is empty, assume this chip has a dump. */
179                 if (reg_table[i].ldn[0].ldn != EOT)
180                         printf(" (dump available)");
181
182                 printf("\n");
183         }
184
185         /* If we printed any chips for this vendor, put in a blank line. */
186         if (i != 0)
187                 printf("\n");
188 }
189
190 /** Print a list of all chips supported by superiotool. */
191 void print_list_of_supported_chips(void)
192 {
193         int i;
194
195         printf("Supported Super I/O chips:\n\n");
196
197         for (i = 0; i < ARRAY_SIZE(vendor_print_functions); i++)
198                 vendor_print_functions[i].print_list();
199
200         printf("See <http://coreboot.org/Superiotool#Supported_devices> "
201                "for more information.\n");
202 }
203
204 static void print_version(void)
205 {
206         printf("superiotool r%s\n", SUPERIOTOOL_VERSION);
207 }
208
209 int main(int argc, char *argv[])
210 {
211         int i, j, opt, option_index;
212 #if defined(__FreeBSD__)
213         int io_fd;
214 #endif
215
216         static const struct option long_options[] = {
217                 {"dump",                no_argument, NULL, 'd'},
218                 {"extra-dump",          no_argument, NULL, 'e'},
219                 {"list-supported",      no_argument, NULL, 'l'},
220                 {"verbose",             no_argument, NULL, 'V'},
221                 {"version",             no_argument, NULL, 'v'},
222                 {"help",                no_argument, NULL, 'h'},
223                 {0, 0, 0, 0}
224         };
225
226         while ((opt = getopt_long(argc, argv, "delVvh",
227                                   long_options, &option_index)) != EOF) {
228                 switch (opt) {
229                 case 'd':
230                         dump = 1;
231                         break;
232                 case 'e':
233                         extra_dump = 1;
234                         break;
235                 case 'l':
236                         print_list_of_supported_chips();
237                         exit(0);
238                         break;
239                 case 'V':
240                         verbose = 1;
241                         break;
242                 case 'v':
243                         print_version();
244                         exit(0);
245                         break;
246                 case 'h':
247                         printf(USAGE);
248                         printf(USAGE_INFO);
249                         exit(0);
250                         break;
251                 default:
252                         /* Unknown option. */
253                         exit(1);
254                         break;
255                 }
256         }
257
258 #if defined(__FreeBSD__)
259         if ((io_fd = open("/dev/io", O_RDWR)) < 0) {
260                 perror("/dev/io");
261 #else
262         if (iopl(3) < 0) {
263                 perror("iopl");
264 #endif
265                 printf("Superiotool must be run as root.\n");
266                 exit(1);
267         }
268
269         print_version();
270
271         for (i = 0; i < ARRAY_SIZE(superio_ports_table); i++) {
272                 for (j = 0; superio_ports_table[i].ports[j] != EOT; j++)
273                         superio_ports_table[i].probe_idregs(
274                                 superio_ports_table[i].ports[j]);
275         }
276
277         if (!chip_found)
278                 printf("No Super I/O found\n");
279
280 #if defined(__FreeBSD__)
281         close(io_fd);
282 #endif
283         return 0;
284 }