1 /* pci-config-space.c: Read the PCI configuration space.
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.
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.
12 The author may be reached as becker@scyld.com, or C/O
13 Scyld Computing Corporation
14 410 Severn Ave., Suite 210
17 Support and updates available at
18 http://www.scyld.com/diag/index.html
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.
25 static 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";
28 static char *usage_msg =
29 "Usage: pci-config [-aDfSvVW] [-# <device_index>]\n";
31 static char *long_usage_msg ="
33 This program shows the contents of PCI configuration space.
34 It reads the hardware registers, and thus must be run as 'root'.
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.
40 Commonly use options are
41 -# <device-index> Operate only on DEVICE-INDEX e.g -#3
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)
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
66 #if defined(__linux__) && __GNU_LIBRARY__ == 1
67 #include <asm/io.h> /* Newer libraries use <sys/io.h> instead. */
71 #if !defined(__OPTIMIZE__)
72 #error You must compile this driver with "-O"!
75 struct 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. */
91 static int verbose=1, opt_a=0, opt_f=0, opt_wake=0, opt_set_WOL=0, debug=0;
92 static int opt_sleep = 0;
93 static long set_address = -1;
95 static void show_addr_config(unsigned char pci_bus, unsigned char pci_dev_fn);
96 static void show_ext_caps(unsigned int *cfg_space, unsigned char pci_bus,
97 unsigned char pci_dev_fn);
98 static void show_one_device(unsigned char pci_bus, unsigned char pci_dev_fn,
101 static void cyclone_WOL(int pci_bus, int pci_dev_fn, void *pci_config_space);
102 static void acpi_wake(unsigned char pci_bus, unsigned char pci_dev_fn,
104 static void acpi_sleep(unsigned char bus, unsigned char devfn, void *pci_cfg);
105 static int dump_mem_region(long addr);
107 extern int pcibios_read_config_byte (unsigned char bus, unsigned char dev_fn,
108 unsigned char where, unsigned char *val);
109 int pcibios_read_config_word (unsigned char bus, unsigned char dev_fn,
110 unsigned char regnum, unsigned short *val);
111 extern int pcibios_read_config_dword (unsigned char bus, unsigned char dev_fn,
112 unsigned char regnum, unsigned int *val);
113 void pcibios_write_config_byte (unsigned char bus, unsigned char dev_fn,
114 unsigned char regnum, unsigned char val);
115 void pcibios_write_config_word (unsigned char bus, unsigned char dev_fn,
116 unsigned char regnum, unsigned short val);
117 void pcibios_write_config_dword (unsigned char bus, unsigned char dev_fn,
118 unsigned char regnum, unsigned int val);
121 int main(int argc, char **argv)
123 int pci_bus = 0, pci_dev_fn;
124 int errflag = 0, show_version = 0;
125 int c, longind, card_num = 0;
128 while ((c = getopt_long(argc, argv, "#:aA:b:B:DfMSuvVW",
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;
149 fprintf(stderr, "%s%s", usage_msg, " Use -u for more information.\n");
156 /* Get access to all of I/O space. */
158 perror("pci-config: iopl()");
159 fprintf(stderr, "This program must be run as root.\n");
162 for (pci_dev_fn = 0; pci_dev_fn < 256; pci_dev_fn++) {
163 /*unsigned char cb;*/
166 pcibios_read_config_dword(pci_bus, pci_dev_fn, 0, &pci_id);
167 if (pci_id == 0xffffffff)
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);
176 if ((pci_dev_fn & 7) == 0) {
178 pcibios_read_config_dword(pci_bus, pci_dev_fn, 3*4, &cdw);
179 if ((cdw & 0x00800000) == 0)
187 static void show_one_device(unsigned char pci_bus, unsigned char pci_dev_fn,
190 unsigned int config[64];
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]);
201 for (i = 0; i < 5; i++) {
202 unsigned int pciaddr = config[4 + i];
204 printf(" Base Address %d: %s at %8.8x.\n",
205 i, pciaddr & 1 ? "I/O" : "Memory", pciaddr & ~1);
207 if (set_address >= 0) {
208 fprintf(stderr, "Setting PCI address register 1 to 0x%lx.\n",
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);
214 show_addr_config(pci_bus, pci_dev_fn);
216 if (config[1] & 0x00100000)
217 show_ext_caps(config, pci_bus, pci_dev_fn);
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))]);
227 acpi_sleep(pci_bus, pci_dev_fn, config);
229 acpi_wake(pci_bus, pci_dev_fn, config);
231 if (pci_id == 0x905510b7)
232 cyclone_WOL(pci_bus, pci_dev_fn, config);
235 static void show_addr_config(unsigned char pci_bus, unsigned char pci_dev_fn)
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);
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);
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);
255 printf(" BIOS ROM at %8.8x, decoded bits are %8.8x.\n", pciaddr, cdw);
257 printf(" No BIOS extension (boot ROM).\n");
260 static void show_ext_caps(unsigned int *cfg_space, unsigned char pci_bus,
261 unsigned char pci_dev_fn)
263 unsigned char *pcfg = (void *)cfg_space;
264 int cap_idx = cfg_space[13] & 0xff;
266 printf(" Extended capabilities, first structure at offset 0x%x.\n",
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);
284 static int acpi_find(unsigned char pci_bus, unsigned char pci_dev_fn,
287 unsigned char *pcfg = (void *)config;
288 if (pcfg[6] & 0x10) {
289 int cap_idx = pcfg[0x34];
291 printf(" Extended capabilities, first structure at offset 0x%x.\n",
293 for (; cap_idx; cap_idx = pcfg[cap_idx + 1]) {
294 if (pcfg[cap_idx] == 1)
301 static void acpi_wake(unsigned char pci_bus, unsigned char pci_dev_fn,
302 void *pci_config_space)
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];
312 printf("Power index is %#x.\n", pwr_idx);
314 printf(" Waking up an ACPI device. Currently powered %s, "
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,
321 /* Many devices must have their PCI register state restored when changing
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]);
334 static void acpi_sleep(unsigned char bus, unsigned char devfn,
335 void *pci_config_space)
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);
343 /* Put the 3Com Cyclone e.g. 3c905B series into Wake On LAN mode. */
344 static void cyclone_WOL(int pci_bus, int pci_dev_fn, void *config)
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];
350 acpi_wake(pci_bus, pci_dev_fn, config);
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",
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);
363 #define PCI_CONFIG_ADDR 0x0cf8
364 #define PCI_CONFIG_DATA 0x0cfc
366 int pcibios_read_config_byte (unsigned char bus, unsigned char dev_fn,
367 unsigned char regnum, unsigned char *val)
369 outl(0x80000000 | (bus<<16) | (dev_fn << 8) | (regnum & 0xfc),
371 *val = inb(PCI_CONFIG_DATA + (regnum & 3));
374 int pcibios_read_config_word (unsigned char bus, unsigned char dev_fn,
375 unsigned char regnum, unsigned short *val)
377 outl(0x80000000 | (bus<<16) | (dev_fn << 8) | (regnum & 0xfc),
379 *val = inw(PCI_CONFIG_DATA + (regnum & 2));
382 int pcibios_read_config_dword (unsigned char bus, unsigned char dev_fn,
383 unsigned char regnum, unsigned int *val)
385 outl(0x80000000 | (bus<<16) | (dev_fn << 8) | (regnum & 0xfc),
387 *val = inl(PCI_CONFIG_DATA);
390 void pcibios_write_config_byte (unsigned char bus, unsigned char dev_fn,
391 unsigned char regnum, unsigned char val)
393 outl(0x80000000 | (bus<<16) | (dev_fn << 8) | (regnum & 0xfc),
395 outb(val, PCI_CONFIG_DATA + (regnum & 3));
398 void pcibios_write_config_word (unsigned char bus, unsigned char dev_fn,
399 unsigned char regnum, unsigned short val)
401 outl(0x80000000 | (bus<<16) | (dev_fn << 8) | (regnum & 0xfc),
403 outw(val, PCI_CONFIG_DATA + (regnum & 2));
406 void pcibios_write_config_dword (unsigned char bus, unsigned char dev_fn,
407 unsigned char regnum, unsigned int val)
409 outl(0x80000000 | (bus<<16) | (dev_fn << 8) | (regnum & 0xfc),
411 outl(val, PCI_CONFIG_DATA);
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. */
418 static int dump_mem_region(long addr)
420 unsigned short *shared_mem;
422 int memfd = open("/dev/kmem", O_RDWR);
425 perror("/dev/kmem (shared memory)");
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]);
441 * compile-command: "cc -O -Wall -o pci-config pci-config.c"