correctly mark code segments as code in SELF
[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 void enter_conf_mode_fintek_7777(uint16_t port)
62 {
63         OUTB(0x77, port);
64         OUTB(0x77, port);
65 }
66
67 void exit_conf_mode_fintek_7777(uint16_t port)
68 {
69         OUTB(0xaa, port);               /* Fintek */
70 }
71
72 int superio_unknown(const struct superio_registers reg_table[], uint16_t id)
73 {
74         return !strncmp(get_superio_name(reg_table, id), "<unknown>", 9);
75 }
76
77
78 const char *get_superio_name(const struct superio_registers reg_table[],
79                              uint16_t id)
80 {
81         int i;
82
83         for (i = 0; /* Nothing */; i++) {
84                 if (reg_table[i].superio_id == EOT)
85                         break;
86
87                 if ((uint16_t)reg_table[i].superio_id != id)
88                         continue;
89
90                 return reg_table[i].name;
91         }
92
93         return "<unknown>";
94 }
95
96 static void dump_regs(const struct superio_registers reg_table[],
97                       int i, int j, uint16_t port, uint8_t ldn_sel)
98 {
99         int k;
100         const int16_t *idx;
101
102         if (reg_table[i].ldn[j].ldn != NOLDN) {
103                 printf("LDN 0x%02x", reg_table[i].ldn[j].ldn);
104                 if (reg_table[i].ldn[j].name != NULL)
105                         printf(" (%s)", reg_table[i].ldn[j].name);
106                 regwrite(port, ldn_sel, reg_table[i].ldn[j].ldn);
107         } else {
108                 if (reg_table[i].ldn[j].name == NULL)
109                         printf("Register dump:");
110                 else
111                         printf("(%s)", reg_table[i].ldn[j].name);
112         }
113
114         idx = reg_table[i].ldn[j].idx;
115
116         printf("\nidx");
117         for (k = 0; idx[k] != EOT; k++) {
118                 if (k && !(k % 8))
119                         putchar(' ');
120                 printf(" %02x", idx[k]);
121         }
122
123         printf("\nval");
124         for (k = 0; idx[k] != EOT; k++) {
125                 if (k && !(k % 8))
126                         putchar(' ');
127                 printf(" %02x", regval(port, idx[k]));
128         }
129
130         printf("\ndef");
131         idx = reg_table[i].ldn[j].def;
132         for (k = 0; idx[k] != EOT; k++) {
133                 if (k && !(k % 8))
134                         putchar(' ');
135                 if (idx[k] == NANA)
136                         printf(" NA");
137                 else if (idx[k] == RSVD)
138                         printf(" RR");
139                 else if (idx[k] == MISC)
140                         printf(" MM");
141                 else
142                         printf(" %02x", idx[k]);
143         }
144         printf("\n");
145 }
146
147 void dump_superio(const char *vendor,
148                   const struct superio_registers reg_table[],
149                   uint16_t port, uint16_t id, uint8_t ldn_sel)
150 {
151         int i, j, no_dump_available = 1;
152
153         if (!dump)
154                 return;
155
156         for (i = 0; /* Nothing */; i++) {
157                 if (reg_table[i].superio_id == EOT)
158                         break;
159
160                 if ((uint16_t)reg_table[i].superio_id != id)
161                         continue;
162
163                 for (j = 0; /* Nothing */; j++) {
164                         if (reg_table[i].ldn[j].ldn == EOT)
165                                 break;
166                         no_dump_available = 0;
167                         dump_regs(reg_table, i, j, port, ldn_sel);
168                 }
169
170                 if (no_dump_available)
171                         printf("No dump available for this Super I/O\n");
172         }
173 }
174
175 void dump_io(uint16_t iobase, uint16_t length)
176 {
177         uint16_t i;
178
179         printf("Dumping %d I/O mapped registers at base 0x%04x:\n",
180                         length, iobase);
181         for (i = 0; i < length; i++)
182                 printf("%02x ", i);
183         printf("\n");
184         for (i = 0; i < length; i++)
185                 printf("%02x ", INB(iobase + i));
186         printf("\n");
187 }
188
189 void probing_for(const char *vendor, const char *info, uint16_t port)
190 {
191         if (!verbose)
192                 return;
193
194         /* Yes, there's no space between '%s' and 'at'! */
195         printf("Probing for %s Super I/O %sat 0x%x...\n", vendor, info, port);
196 }
197
198 /** Print a list of all supported chips from the given vendor. */
199 void print_vendor_chips(const char *vendor,
200                         const struct superio_registers reg_table[])
201 {
202         int i;
203
204         for (i = 0; reg_table[i].superio_id != EOT; i++) {
205                 printf("%s %s", vendor, reg_table[i].name);
206
207                 /* Unless the ldn is empty, assume this chip has a dump. */
208                 if (reg_table[i].ldn[0].ldn != EOT)
209                         printf(" (dump available)");
210
211                 printf("\n");
212         }
213
214         /* If we printed any chips for this vendor, put in a blank line. */
215         if (i != 0)
216                 printf("\n");
217 }
218
219 /** Print a list of all chips supported by superiotool. */
220 void print_list_of_supported_chips(void)
221 {
222         int i;
223
224         printf("Supported Super I/O chips:\n\n");
225
226         for (i = 0; i < ARRAY_SIZE(vendor_print_functions); i++)
227                 vendor_print_functions[i].print_list();
228
229         printf("See <http://coreboot.org/Superiotool#Supported_devices> "
230                "for more information.\n");
231 }
232
233 static void print_version(void)
234 {
235         printf("superiotool r%s\n", SUPERIOTOOL_VERSION);
236 }
237
238 int main(int argc, char *argv[])
239 {
240         int i, j, opt, option_index;
241 #if defined(__FreeBSD__)
242         int io_fd;
243 #endif
244
245         static const struct option long_options[] = {
246                 {"dump",                no_argument, NULL, 'd'},
247                 {"extra-dump",          no_argument, NULL, 'e'},
248                 {"list-supported",      no_argument, NULL, 'l'},
249                 {"verbose",             no_argument, NULL, 'V'},
250                 {"version",             no_argument, NULL, 'v'},
251                 {"help",                no_argument, NULL, 'h'},
252                 {0, 0, 0, 0}
253         };
254
255         while ((opt = getopt_long(argc, argv, "delVvh",
256                                   long_options, &option_index)) != EOF) {
257                 switch (opt) {
258                 case 'd':
259                         dump = 1;
260                         break;
261                 case 'e':
262                         extra_dump = 1;
263                         break;
264                 case 'l':
265                         print_list_of_supported_chips();
266                         exit(0);
267                         break;
268                 case 'V':
269                         verbose = 1;
270                         break;
271                 case 'v':
272                         print_version();
273                         exit(0);
274                         break;
275                 case 'h':
276                         printf(USAGE);
277                         printf(USAGE_INFO);
278                         exit(0);
279                         break;
280                 default:
281                         /* Unknown option. */
282                         exit(1);
283                         break;
284                 }
285         }
286
287 #if defined(__FreeBSD__)
288         if ((io_fd = open("/dev/io", O_RDWR)) < 0) {
289                 perror("/dev/io");
290 #else
291         if (iopl(3) < 0) {
292                 perror("iopl");
293 #endif
294                 printf("Superiotool must be run as root.\n");
295                 exit(1);
296         }
297
298         print_version();
299
300 #ifdef PCI_SUPPORT
301         /* Do some basic libpci init. */
302         pacc = pci_alloc();
303         pci_init(pacc);
304         pci_scan_bus(pacc);
305 #endif
306
307         for (i = 0; i < ARRAY_SIZE(superio_ports_table); i++) {
308                 for (j = 0; superio_ports_table[i].ports[j] != EOT; j++)
309                         superio_ports_table[i].probe_idregs(
310                                 superio_ports_table[i].ports[j]);
311         }
312
313         if (!chip_found)
314                 printf("No Super I/O found\n");
315
316 #if defined(__FreeBSD__)
317         close(io_fd);
318 #endif
319         return 0;
320 }