]> git.pld-linux.org Git - projects/rc-scripts.git/blob - src/pci-config.c
- added Wake-On-Lan work-around for nForce ethernet drivers
[projects/rc-scripts.git] / src / pci-config.c
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
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";
30
31 static 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
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. */
88         { 0, 0, 0, 0 }
89 };
90
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;
94
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,
99                                                         int dev_num);
100
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,
103                                           void *config);
104 static void acpi_sleep(unsigned char bus, unsigned char devfn, void *pci_cfg);
105 static int dump_mem_region(long addr);
106
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);
119
120 \f
121 int 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
187 static 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
235 static 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 }
260 static 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
284 static 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
301 static 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
334 static 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. */
344 static 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
366 int 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 }
374 int 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 }
382 int 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 }
390 void 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 }
398 void 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 }
406 void 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. */
418 static 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.073758 seconds and 3 git commands to generate.