]> git.pld-linux.org Git - projects/rc-scripts.git/blame - src/pci-config.c
- added Wake-On-Lan work-around for nForce ethernet drivers
[projects/rc-scripts.git] / src / pci-config.c
CommitLineData
3b04fe1f 1/* pci-config-space.c: Read the PCI configuration space.
2
3 Read the PCI configuration space using the Intel bus-bridge interface
4 registers. This bypasses the BIOS32 entry, and thus we are not assured
5 of working on all systems.
6
7 Copyright 1998-2002 by Donald Becker.
8 This software may be used and distributed according to the terms of
9 the GNU General Public License (GPL), incorporated herein by reference.
10 Contact the author for use under other terms.
11
12 The author may be reached as becker@scyld.com, or C/O
13 Scyld Computing Corporation
14 410 Severn Ave., Suite 210
15 Annapolis MD 21403
16
17 Support and updates available at
18 http://www.scyld.com/diag/index.html
19
20 Common-sense licensing statement: Using any portion of this program in
21 your own program means that you must give credit to the original author
22 and release the resulting code under the GPL.
23 */
24
25static char *version_msg =
26"pci-config.c:v2.03 4/15/2002 Donald Becker (becker@scyld.com)\n"
27" http://www.scyld.com/diag/index.html\n";
28static char *usage_msg =
29"Usage: pci-config [-aDfSvVW] [-# <device_index>]\n";
30
31static char *long_usage_msg ="
32
33 This program shows the contents of PCI configuration space.
34 It reads the hardware registers, and thus must be run as 'root'.
35
36 Running this program with no options shows the installed PCI devices.
37 Each line is prefixed by its index which may be used with -#<index>
38 e.g. \"pci-config -#3\" to specify the device to operate on.
39
40 Commonly use options are
41 -# <device-index> Operate only on DEVICE-INDEX e.g -#3
42
43 The operations on the selected device are
44 -a --show-addresses Show PCI address registers.
45 -S --sleep Put device to sleep (ACPI D3 state)
46 -W --wake Wake a sleeping device (ACPI D0 state)
47
48 Less commonly used options are
49 -B <bus> --bus <bus> Show only devices on BUS.
50 -A <addr> --set-addresses Set PCI address register 1 to the ADDR.
51 -D --debug Show details of operations
52 -f --force Override checks and perform the operation
53 -u --usage Show this long usage message
54 -v --verbose Verbose mode
55 -V --version Display this program's version information
56
57";
58
59#include <unistd.h>
60#include <stdio.h>
61#include <stdlib.h>
62#include <sys/mman.h>
63#include <sys/stat.h>
64#include <fcntl.h>
65#include <getopt.h>
66#if defined(__linux__) && __GNU_LIBRARY__ == 1
67#include <asm/io.h> /* Newer libraries use <sys/io.h> instead. */
68#else
69#include <sys/io.h>
70#endif
71#if !defined(__OPTIMIZE__)
72#error You must compile this driver with "-O"!
73#endif
74
75struct option longopts[] = {
76 {"show-addresses", 0, 0, 'a'}, /* Show PCI address registers. */
77 {"set-addresses", 1, 0, 'A'}, /* Show PCI address registers. */
78 {"bus", 1, 0, 'B'}, /* Show only devices on BUS. */
79 {"debug", 0, 0, 'D'}, /* Increase debug level. */
80 {"force", 0, 0, 'f'}, /* Force operation. */
81 {"set-WOL", 0, 0, 'M'}, /* Set to Wake-On-LAN mode. */
82 {"sleep", 0, 0, 'S'}, /* Put device to sleep (ACPI D3 state). */
83 {"usage", 0, 0, 'u'}, /* Show the long usage message. */
84 {"verbose", 0, 0, 'v'}, /* Verbose mode */
85 {"version", 0, 0, 'V'}, /* Display version number */
86 {"wake-on-lan", 0, 0, 'W'}, /* Wake (set to D0 state) the device. */
87 {"device-index", 1, 0, '#'}, /* Operate only on device INDEX. */
88 { 0, 0, 0, 0 }
89};
90
91static int verbose=1, opt_a=0, opt_f=0, opt_wake=0, opt_set_WOL=0, debug=0;
92static int opt_sleep = 0;
93static long set_address = -1;
94
95static void show_addr_config(unsigned char pci_bus, unsigned char pci_dev_fn);
96static void show_ext_caps(unsigned int *cfg_space, unsigned char pci_bus,
97 unsigned char pci_dev_fn);
98static void show_one_device(unsigned char pci_bus, unsigned char pci_dev_fn,
99 int dev_num);
100
101static void cyclone_WOL(int pci_bus, int pci_dev_fn, void *pci_config_space);
102static void acpi_wake(unsigned char pci_bus, unsigned char pci_dev_fn,
103 void *config);
104static void acpi_sleep(unsigned char bus, unsigned char devfn, void *pci_cfg);
105static int dump_mem_region(long addr);
106
107extern int pcibios_read_config_byte (unsigned char bus, unsigned char dev_fn,
108 unsigned char where, unsigned char *val);
109int pcibios_read_config_word (unsigned char bus, unsigned char dev_fn,
110 unsigned char regnum, unsigned short *val);
111extern int pcibios_read_config_dword (unsigned char bus, unsigned char dev_fn,
112 unsigned char regnum, unsigned int *val);
113void pcibios_write_config_byte (unsigned char bus, unsigned char dev_fn,
114 unsigned char regnum, unsigned char val);
115void pcibios_write_config_word (unsigned char bus, unsigned char dev_fn,
116 unsigned char regnum, unsigned short val);
117void pcibios_write_config_dword (unsigned char bus, unsigned char dev_fn,
118 unsigned char regnum, unsigned int val);
119
120\f
121int main(int argc, char **argv)
122{
123 int pci_bus = 0, pci_dev_fn;
124 int errflag = 0, show_version = 0;
125 int c, longind, card_num = 0;
126 int dev_num = 0;
127
128 while ((c = getopt_long(argc, argv, "#:aA:b:B:DfMSuvVW",
129 longopts, &longind))
130 != -1)
131 switch (c) {
132 case 'a': opt_a++; break;
133 case 'A': set_address = strtol(optarg, NULL, 16); break;
134 case 'b': printf("Setting bus to %s.\n", optarg);
135 case 'B': pci_bus = strtol(optarg, NULL, 0); break;
136 case 'D': debug++; break;
137 case 'f': opt_f++; break;
138 case 'M': opt_set_WOL++; break;
139 case 'S': opt_sleep++; break;
140 case 'u': printf("%s%s", usage_msg, long_usage_msg); return 0;
141 case 'v': verbose++; break;
142 case 'V': show_version++; break;
143 case 'W': opt_wake++; break;
144 case '#': card_num = atoi(optarg); break;
145 case '?':
146 errflag++;
147 }
148 if (errflag) {
149 fprintf(stderr, "%s%s", usage_msg, " Use -u for more information.\n");
150 return 3;
151 }
152
153 if (verbose)
154 printf(version_msg);
155
156 /* Get access to all of I/O space. */
157 if (iopl(3) < 0) {
158 perror("pci-config: iopl()");
159 fprintf(stderr, "This program must be run as root.\n");
160 return 2;
161 }
162 for (pci_dev_fn = 0; pci_dev_fn < 256; pci_dev_fn++) {
163 /*unsigned char cb;*/
164 unsigned int pci_id;
165
166 pcibios_read_config_dword(pci_bus, pci_dev_fn, 0, &pci_id);
167 if (pci_id == 0xffffffff)
168 continue;
169 dev_num++;
170 if (card_num == 0) {
171 printf("Device #%d at bus %d device/function %d/%d, %8.8x.\n",
172 dev_num, pci_bus, pci_dev_fn>>3, pci_dev_fn&7, pci_id);
173 } else if (card_num == dev_num) {
174 show_one_device(pci_bus, pci_dev_fn, dev_num);
175 }
176 if ((pci_dev_fn & 7) == 0) {
177 unsigned int cdw;
178 pcibios_read_config_dword(pci_bus, pci_dev_fn, 3*4, &cdw);
179 if ((cdw & 0x00800000) == 0)
180 pci_dev_fn += 7;
181 }
182 }
183
184 return 0;
185}
186
187static void show_one_device(unsigned char pci_bus, unsigned char pci_dev_fn,
188 int dev_num)
189{
190 unsigned int config[64];
191 int i;
192 int pci_id;
193
194 printf("Device #%d at bus %d device/function %d/%d.",
195 dev_num, pci_bus, pci_dev_fn>>3, pci_dev_fn&7);
196 for (i = 0; i < 64; i++) {
197 pcibios_read_config_dword(pci_bus, pci_dev_fn, i<<2, &config[i]);
198 printf("%s%8.8x", i % 8 == 0 ? "\n " : " ", config[i]);
199 }
200 printf("\n");
201 for (i = 0; i < 5; i++) {
202 unsigned int pciaddr = config[4 + i];
203 if (pciaddr)
204 printf(" Base Address %d: %s at %8.8x.\n",
205 i, pciaddr & 1 ? "I/O" : "Memory", pciaddr & ~1);
206 }
207 if (set_address >= 0) {
208 fprintf(stderr, "Setting PCI address register 1 to 0x%lx.\n",
209 set_address);
210 pcibios_write_config_dword(pci_bus, pci_dev_fn, 0x14, set_address);
211 pcibios_write_config_dword(pci_bus, pci_dev_fn, 0x04, config[1] | 3);
212 }
213 if (opt_a)
214 show_addr_config(pci_bus, pci_dev_fn);
215 pci_id = config[0];
216 if (config[1] & 0x00100000)
217 show_ext_caps(config, pci_bus, pci_dev_fn);
218 if (config[10]) {
219 char *cis_addr_space[] = {"PCI configuration space", "BAR 0", "BAR 1", "BAR 2", "BAR 3", };
220 int space = config[10] & 7;
221 printf(" CardBus CIS pointer 0x%4.4x (%s), address %x.\n", config[10],
222 cis_addr_space[space], config[4 + (space-1)]);
223 if (space > 0 && space < 4)
224 dump_mem_region(config[4 + ((space-1))]);
225 }
226 if (opt_sleep)
227 acpi_sleep(pci_bus, pci_dev_fn, config);
228 if (opt_wake)
229 acpi_wake(pci_bus, pci_dev_fn, config);
230 if (opt_set_WOL)
231 if (pci_id == 0x905510b7)
232 cyclone_WOL(pci_bus, pci_dev_fn, config);
233}
234
235static void show_addr_config(unsigned char pci_bus, unsigned char pci_dev_fn)
236{
237 int i;
238 unsigned int pciaddr, cdw;
239 for (i = 0; i < 5; i++) {
240 int cfg_i = 0x10 + (i<<2);
241 pcibios_read_config_dword(pci_bus, pci_dev_fn, cfg_i, &pciaddr);
242 pcibios_write_config_dword(pci_bus, pci_dev_fn, cfg_i, 0xffffffff);
243 pcibios_read_config_dword(pci_bus, pci_dev_fn, cfg_i, &cdw);
244 if (cdw == 0)
245 break;
246 printf(" Address %d %s at %8.8x, decoded bits are %8.8x.\n",
247 i, cdw & 1 ? "is I/O" : "memory", pciaddr & ~1, ~cdw);
248 pcibios_write_config_dword(pci_bus, pci_dev_fn, cfg_i, pciaddr);
249 }
250 pcibios_read_config_dword(pci_bus, pci_dev_fn, 0x30, &pciaddr);
251 pcibios_write_config_dword(pci_bus, pci_dev_fn, 0x30, 0xfffffffe);
252 pcibios_read_config_dword(pci_bus, pci_dev_fn, 0x30, &cdw);
253 pcibios_write_config_dword(pci_bus, pci_dev_fn, 0x30, pciaddr);
254 if (cdw)
255 printf(" BIOS ROM at %8.8x, decoded bits are %8.8x.\n", pciaddr, cdw);
256 else
257 printf(" No BIOS extension (boot ROM).\n");
258 return;
259}
260static void show_ext_caps(unsigned int *cfg_space, unsigned char pci_bus,
261 unsigned char pci_dev_fn)
262{
263 unsigned char *pcfg = (void *)cfg_space;
264 int cap_idx = cfg_space[13] & 0xff;
265
266 printf(" Extended capabilities, first structure at offset 0x%x.\n",
267 cap_idx);
268 for (; cap_idx; cap_idx = pcfg[cap_idx + 1]) {
269 printf(" Extended PCI capability type %d at 0x%2.2x, next %d.\n",
270 pcfg[cap_idx], cap_idx, pcfg[cap_idx + 1]);
271 if (pcfg[cap_idx] == 1) {
272 printf(" Power management entry ver. %d: Capabilities %2.2x%2.2x"
273 ", Ctrl %2.2x%2.2x, Event %2.2x%2.2x.\n",
274 pcfg[cap_idx + 2] & 7,
275 pcfg[cap_idx + 3], pcfg[cap_idx + 2],
276 pcfg[cap_idx + 5], pcfg[cap_idx + 4],
277 pcfg[cap_idx + 7], pcfg[cap_idx + 6]);
278 printf(" Power state D%d.\n", pcfg[cap_idx + 4] & 3);
279 }
280 }
281}
282
283
284static int acpi_find(unsigned char pci_bus, unsigned char pci_dev_fn,
285 void *config)
286{
287 unsigned char *pcfg = (void *)config;
288 if (pcfg[6] & 0x10) {
289 int cap_idx = pcfg[0x34];
290
291 printf(" Extended capabilities, first structure at offset 0x%x.\n",
292 cap_idx);
293 for (; cap_idx; cap_idx = pcfg[cap_idx + 1]) {
294 if (pcfg[cap_idx] == 1)
295 return cap_idx;
296 }
297 }
298 return 0;
299}
300
301static void acpi_wake(unsigned char pci_bus, unsigned char pci_dev_fn,
302 void *pci_config_space)
303{
304 unsigned char *config = pci_config_space;
305 unsigned short *configw = pci_config_space;
306 unsigned int *configdw = pci_config_space;
307 int pwr_idx = acpi_find(pci_bus, pci_dev_fn, config);
308 unsigned short pwr_command = configw[(pwr_idx + 4)>>1];
309 int i;
310
311 if (debug)
312 printf("Power index is %#x.\n", pwr_idx);
313
314 printf(" Waking up an ACPI device. Currently powered %s, "
315 "I/O %#x IRQ %d.\n"
316 " Updating the power state of %4.4x->%4.4x.\n",
317 pwr_command & 3 ? "down" : "up", configdw[0x10>>2], config[0x3c],
318 pwr_command, pwr_command & ~3);
319 pcibios_write_config_word(pci_bus, pci_dev_fn, pwr_idx + 4,
320 pwr_command & ~3);
321 /* Many devices must have their PCI register state restored when changing
322 from D3 state. */
323 for (i = 0x10; i <= 0x20; i+=4)
324 pcibios_write_config_dword(pci_bus, pci_dev_fn, i, configdw[i >> 2]);
325 /* PCI_ROM_ADDRESS, interrupt line, cache line size, latency timer */
326 pcibios_write_config_dword(pci_bus, pci_dev_fn, 0x30, configdw[0x30 >> 2]);
327 pcibios_write_config_byte(pci_bus, pci_dev_fn, 0x3c, config[0x3c]);
328 pcibios_write_config_byte(pci_bus, pci_dev_fn, 0x0c, config[0x0c]);
329 pcibios_write_config_byte(pci_bus, pci_dev_fn, 0x0d, config[0x0d]);
330 /* Finally, restore the command register. */
331 pcibios_write_config_word(pci_bus, pci_dev_fn, 0x04, configw[4>>1]);
332}
333
334static void acpi_sleep(unsigned char bus, unsigned char devfn,
335 void *pci_config_space)
336{
337 unsigned short *configw = pci_config_space;
338 int pwr_idx = acpi_find(bus, devfn, pci_config_space);
339 unsigned short pwr_command = configw[(pwr_idx + 4)>>1];
340 pcibios_write_config_word(bus, devfn, pwr_idx + 4, pwr_command | 0x0103);
341}
342
343/* Put the 3Com Cyclone e.g. 3c905B series into Wake On LAN mode. */
344static void cyclone_WOL(int pci_bus, int pci_dev_fn, void *config)
345{
346 int pwr_idx = acpi_find(pci_bus, pci_dev_fn, config);
347 unsigned short pwr_command = ((unsigned short *)config)[(pwr_idx + 4)>>1];
348 long ioaddr = ((int *)config)[0x10];
349
350 acpi_wake(pci_bus, pci_dev_fn, config);
351
352 outw(0x801f, ioaddr + 0x0e); /* Set RxFilter to accept frames. */
353 outw((1<<11) + 7, ioaddr + 0x0e);
354 outw(7, ioaddr + 0x0c);
355 printf(" Window 7 Power Management Event is %4.4x.\n",
356 inw(ioaddr + 0x0c));
357 printf(" Changing the power state from %4.4x to 0103.\n", pwr_command);
358 outw(0x2000, ioaddr + 0x0e); /* RxEnable. */
359 pcibios_write_config_word(pci_bus, pci_dev_fn, 0xe0, pwr_command | 0x8103);
360}
361
362\f
363#define PCI_CONFIG_ADDR 0x0cf8
364#define PCI_CONFIG_DATA 0x0cfc
365
366int pcibios_read_config_byte (unsigned char bus, unsigned char dev_fn,
367 unsigned char regnum, unsigned char *val)
368{
369 outl(0x80000000 | (bus<<16) | (dev_fn << 8) | (regnum & 0xfc),
370 PCI_CONFIG_ADDR);
371 *val = inb(PCI_CONFIG_DATA + (regnum & 3));
372 return 0;
373}
374int pcibios_read_config_word (unsigned char bus, unsigned char dev_fn,
375 unsigned char regnum, unsigned short *val)
376{
377 outl(0x80000000 | (bus<<16) | (dev_fn << 8) | (regnum & 0xfc),
378 PCI_CONFIG_ADDR);
379 *val = inw(PCI_CONFIG_DATA + (regnum & 2));
380 return 0;
381}
382int pcibios_read_config_dword (unsigned char bus, unsigned char dev_fn,
383 unsigned char regnum, unsigned int *val)
384{
385 outl(0x80000000 | (bus<<16) | (dev_fn << 8) | (regnum & 0xfc),
386 PCI_CONFIG_ADDR);
387 *val = inl(PCI_CONFIG_DATA);
388 return 0;
389}
390void pcibios_write_config_byte (unsigned char bus, unsigned char dev_fn,
391 unsigned char regnum, unsigned char val)
392{
393 outl(0x80000000 | (bus<<16) | (dev_fn << 8) | (regnum & 0xfc),
394 PCI_CONFIG_ADDR);
395 outb(val, PCI_CONFIG_DATA + (regnum & 3));
396 return;
397}
398void pcibios_write_config_word (unsigned char bus, unsigned char dev_fn,
399 unsigned char regnum, unsigned short val)
400{
401 outl(0x80000000 | (bus<<16) | (dev_fn << 8) | (regnum & 0xfc),
402 PCI_CONFIG_ADDR);
403 outw(val, PCI_CONFIG_DATA + (regnum & 2));
404 return;
405}
406void pcibios_write_config_dword (unsigned char bus, unsigned char dev_fn,
407 unsigned char regnum, unsigned int val)
408{
409 outl(0x80000000 | (bus<<16) | (dev_fn << 8) | (regnum & 0xfc),
410 PCI_CONFIG_ADDR);
411 outl(val, PCI_CONFIG_DATA);
412 return;
413}
414
415\f
416/* Map the board shared memory into our address space -- this code is
417 a good example of non-kernel access to devices on the PCI bus. */
418static int dump_mem_region(long addr)
419{
420 unsigned short *shared_mem;
421 int i;
422 int memfd = open("/dev/kmem", O_RDWR);
423
424 if (memfd < 0) {
425 perror("/dev/kmem (shared memory)");
426 return 2;
427 } else
428 printf("Opened /dev/kmem for PCI memory.\n");
429 shared_mem = mmap(0, 0x8000, PROT_READ|PROT_WRITE,
430 MAP_SHARED, memfd, addr);
431 printf("Shared memory at %#x (%#x).\n", (int)addr, (int)shared_mem);
432 for (i = 0; i < 100; i++)
433 printf(" %4.4x", shared_mem[i]);
434 close(memfd);
435 printf(" ...\n");
436 return 0;
437}
438
439/*
440 * Local variables:
441 * compile-command: "cc -O -Wall -o pci-config pci-config.c"
442 * tab-width: 4
443 * c-indent-level: 4
444 * c-basic-offset: 4
445 * End:
446 */
This page took 0.092832 seconds and 4 git commands to generate.