source: rtems/c/src/lib/libbsp/powerpc/shared/pci/pci.c @ 18a3bbe

4.104.114.84.95
Last change on this file since 18a3bbe was 18a3bbe, checked in by Ralf Corsepius <ralf.corsepius@…>, on 03/31/07 at 01:14:39

Use stdint.h fixed-size types.

  • Property mode set to 100644
File size: 16.5 KB
Line 
1/*
2 * pci.c :  this file contains basic PCI Io functions.
3 *
4 *  Copyright (C) 1999 valette@crf.canon.fr
5 *
6 *  This code is heavily inspired by the public specification of STREAM V2
7 *  that can be found at :
8 *
9 *      <http://www.chorus.com/Documentation/index.html> by following
10 *  the STREAM API Specification Document link.
11 *
12 *  The license and distribution terms for this file may be
13 *  found in found in the file LICENSE in this distribution or at
14 *  http://www.rtems.com/license/LICENSE.
15 *
16 *  $Id$
17 *
18 *  Till Straumann, <strauman@slac.stanford.edu>, 1/2002
19 *   - separated bridge detection code out of this file
20 */
21
22#include <rtems.h>
23#include <bsp.h>
24
25#include <libcpu/io.h>
26#include <bsp/pci.h>
27#include <rtems/bspIo.h>
28
29/* allow for overriding these definitions */
30#ifndef PCI_CONFIG_ADDR
31#define PCI_CONFIG_ADDR      0xcf8
32#endif
33#ifndef PCI_CONFIG_DATA
34#define PCI_CONFIG_DATA      0xcfc
35#endif
36
37#define PCI_INVALID_VENDORDEVICEID  0xffffffff
38#define PCI_MULTI_FUNCTION    0x80
39
40/* define a shortcut */
41#define pci  BSP_pci_configuration
42
43/*
44 * Bit encode for PCI_CONFIG_HEADER_TYPE register
45 */
46unsigned char ucMaxPCIBus;
47
48static int
49indirect_pci_read_config_byte(
50  unsigned char bus,
51  unsigned char slot,
52  unsigned char function,
53  unsigned char offset,
54  unsigned char *val
55) {
56  out_be32((unsigned int*) pci.pci_config_addr,
57     0x80|(bus<<8)|(PCI_DEVFN(slot,function)<<16)|((offset&~3)<<24));
58  *val = in_8(pci.pci_config_data + (offset&3));
59  return PCIBIOS_SUCCESSFUL;
60}
61
62static int
63indirect_pci_read_config_word(
64  unsigned char bus,
65  unsigned char slot,
66  unsigned char function,
67  unsigned char offset,
68  unsigned short *val
69) {
70  *val = 0xffff;
71  if (offset&1)
72    return PCIBIOS_BAD_REGISTER_NUMBER;
73
74  out_be32((unsigned int*) pci.pci_config_addr,
75     0x80|(bus<<8)|(PCI_DEVFN(slot,function)<<16)|((offset&~3)<<24));
76  *val = in_le16((volatile unsigned short *)(pci.pci_config_data + (offset&3)));
77  return PCIBIOS_SUCCESSFUL;
78}
79
80static int
81indirect_pci_read_config_dword(
82  unsigned char bus,
83  unsigned char slot,
84  unsigned char function,
85  unsigned char offset,
86  uint32_t *val
87) {
88  *val = 0xffffffff;
89  if (offset&3)
90    return PCIBIOS_BAD_REGISTER_NUMBER;
91
92  out_be32((unsigned int*) pci.pci_config_addr,
93     0x80|(bus<<8)|(PCI_DEVFN(slot,function)<<16)|(offset<<24));
94  *val = in_le32((volatile unsigned int *)pci.pci_config_data);
95  return PCIBIOS_SUCCESSFUL;
96}
97
98static int
99indirect_pci_write_config_byte(
100  unsigned char bus,
101  unsigned char slot,
102  unsigned char function,
103  unsigned char offset,
104  unsigned char val
105) {
106  out_be32((unsigned int*) pci.pci_config_addr,
107     0x80|(bus<<8)|(PCI_DEVFN(slot,function)<<16)|((offset&~3)<<24));
108  out_8(pci.pci_config_data + (offset&3), val);
109  return PCIBIOS_SUCCESSFUL;
110}
111
112static int
113indirect_pci_write_config_word(
114  unsigned char bus,
115  unsigned char slot,
116  unsigned char function,
117  unsigned char offset,
118  unsigned short val
119) {
120  if (offset&1)
121    return PCIBIOS_BAD_REGISTER_NUMBER;
122
123  out_be32((unsigned int*) pci.pci_config_addr,
124     0x80|(bus<<8)|(PCI_DEVFN(slot,function)<<16)|((offset&~3)<<24));
125  out_le16((volatile unsigned short *)(pci.pci_config_data + (offset&3)), val);
126  return PCIBIOS_SUCCESSFUL;
127}
128
129static int
130indirect_pci_write_config_dword(
131  unsigned char bus,
132  unsigned char slot,
133  unsigned char function,
134  unsigned char offset,
135  uint32_t      val
136) {
137  if (offset&3)
138    return PCIBIOS_BAD_REGISTER_NUMBER;
139  out_be32((unsigned int*) pci.pci_config_addr,
140     0x80|(bus<<8)|(PCI_DEVFN(slot,function)<<16)|(offset<<24));
141  out_le32((volatile unsigned int *)pci.pci_config_data, val);
142  return PCIBIOS_SUCCESSFUL;
143}
144
145const pci_config_access_functions pci_indirect_functions = {
146  indirect_pci_read_config_byte,
147  indirect_pci_read_config_word,
148  indirect_pci_read_config_dword,
149  indirect_pci_write_config_byte,
150  indirect_pci_write_config_word,
151  indirect_pci_write_config_dword
152};
153
154pci_config BSP_pci_configuration = {
155  (volatile unsigned char*)PCI_CONFIG_ADDR,
156  (volatile unsigned char*)PCI_CONFIG_DATA,
157  &pci_indirect_functions
158};
159
160static int
161direct_pci_read_config_byte(
162  unsigned char bus,
163  unsigned char slot,
164  unsigned char function,
165  unsigned char offset,
166  unsigned char *val
167) {
168  if (bus != 0 || (1<<slot & 0xff8007fe)) {
169    *val=0xff;
170     return PCIBIOS_DEVICE_NOT_FOUND;
171  }
172  *val=in_8(pci.pci_config_data + ((1<<slot)&~1)
173   + (function<<8) + offset);
174  return PCIBIOS_SUCCESSFUL;
175}
176
177static int
178direct_pci_read_config_word(
179  unsigned char bus,
180  unsigned char slot,
181  unsigned char function,
182  unsigned char offset,
183  unsigned short *val
184) {
185  *val = 0xffff;
186  if (offset&1)
187    return PCIBIOS_BAD_REGISTER_NUMBER;
188  if (bus != 0 || (1<<slot & 0xff8007fe))
189     return PCIBIOS_DEVICE_NOT_FOUND;
190
191  *val=in_le16((volatile unsigned short *)
192      (pci.pci_config_data + ((1<<slot)&~1)
193       + (function<<8) + offset));
194  return PCIBIOS_SUCCESSFUL;
195}
196
197static int
198direct_pci_read_config_dword(
199  unsigned char bus,
200  unsigned char slot,
201  unsigned char function,
202  unsigned char offset,
203  uint32_t     *val
204) {
205  *val = 0xffffffff;
206  if (offset&3)
207    return PCIBIOS_BAD_REGISTER_NUMBER;
208  if (bus != 0 || (1<<slot & 0xff8007fe))
209     return PCIBIOS_DEVICE_NOT_FOUND;
210
211  *val=in_le32((volatile unsigned int *)
212      (pci.pci_config_data + ((1<<slot)&~1)
213       + (function<<8) + offset));
214  return PCIBIOS_SUCCESSFUL;
215}
216
217static int
218direct_pci_write_config_byte(
219  unsigned char bus,
220  unsigned char slot,
221  unsigned char function,
222  unsigned char offset,
223  unsigned char val
224) {
225  if (bus != 0 || (1<<slot & 0xff8007fe))
226     return PCIBIOS_DEVICE_NOT_FOUND;
227
228  out_8(pci.pci_config_data + ((1<<slot)&~1)
229     + (function<<8) + offset,
230     val);
231  return PCIBIOS_SUCCESSFUL;
232}
233
234static int
235direct_pci_write_config_word(
236  unsigned char bus,
237  unsigned char slot,
238  unsigned char function,
239  unsigned char offset,
240  unsigned short val
241) {
242  if (offset&1)
243    return PCIBIOS_BAD_REGISTER_NUMBER;
244  if (bus != 0 || (1<<slot & 0xff8007fe))
245     return PCIBIOS_DEVICE_NOT_FOUND;
246
247  out_le16((volatile unsigned short *)
248     (pci.pci_config_data + ((1<<slot)&~1)
249   + (function<<8) + offset),
250     val);
251  return PCIBIOS_SUCCESSFUL;
252}
253
254static int
255direct_pci_write_config_dword(
256  unsigned char bus,
257  unsigned char slot,
258  unsigned char function,
259  unsigned char offset,
260  uint32_t      val
261) {
262  if (offset&3)
263    return PCIBIOS_BAD_REGISTER_NUMBER;
264  if (bus != 0 || (1<<slot & 0xff8007fe))
265     return PCIBIOS_DEVICE_NOT_FOUND;
266
267  out_le32((volatile unsigned int *)
268     (pci.pci_config_data + ((1<<slot)&~1)
269   + (function<<8) + offset),
270     val);
271  return PCIBIOS_SUCCESSFUL;
272}
273
274const pci_config_access_functions pci_direct_functions = {
275  direct_pci_read_config_byte,
276  direct_pci_read_config_word,
277  direct_pci_read_config_dword,
278  direct_pci_write_config_byte,
279  direct_pci_write_config_word,
280  direct_pci_write_config_dword
281};
282
283#define PRINT_MSG() \
284  printk("pci : Device %d:0x%02x:%d routed to interrupt_line %d\n", \
285    pbus, pslot, pfun, int_name )
286
287/*
288** Validate a test interrupt name and print a warning if its not one of
289** the names defined in the routing record.
290*/
291static int test_intname(
292  const struct _int_map *row,
293  int pbus,
294  int pslot,
295  int pfun,
296  int int_pin,
297  int int_name
298) {
299  int j, k;
300  int _nopin= -1, _noname= -1;
301
302  for (j=0; row->pin_route[j].pin > -1; j++) {
303    if ( row->pin_route[j].pin == int_pin ) {
304   _nopin = 0;
305
306   for (k=0; k<4 && row->pin_route[j].int_name[k] > -1; k++ ) {
307     if ( row->pin_route[j].int_name[k] == int_name ) {
308       _noname=0; break;
309     }
310   }
311   break;
312    }
313  }
314
315   if( _nopin  )
316   {
317      printk("pci : Device %d:0x%02x:%d supplied a bogus interrupt_pin %d\n", pbus, pslot, pfun, int_pin );
318      return -1;
319   }
320   else
321   {
322      if( _noname ) {
323                unsigned char v = row->pin_route[j].int_name[0];
324                printk("pci : Device %d:0x%02x:%d supplied a suspicious interrupt_line %d, ", pbus, pslot, pfun, int_name );
325                if ( (row->opts & PCI_FIXUP_OPT_OVERRIDE_NAME) && 255 != (v = row->pin_route[j].int_name[0]) ) {
326                        printk("OVERRIDING with %d from fixup table\n", v);
327            pci_write_config_byte(pbus,pslot,pfun,PCI_INTERRUPT_LINE,v);
328                } else {
329                printk("using it anyway\n");
330                }
331          }
332   }
333   return 0;
334}
335
336struct pcibridge
337{
338  int bus;
339  int slot;
340};
341
342static int FindPCIbridge( int mybus, struct pcibridge *pb )
343{
344  int          pbus, pslot;
345  uint8_t      bussec, buspri;
346  uint16_t     devid, vendorid, dclass;
347
348  for(pbus=0; pbus< pci_bus_count(); pbus++) {
349    for(pslot=0; pslot< PCI_MAX_DEVICES; pslot++) {
350      pci_read_config_word(pbus, pslot, 0, PCI_DEVICE_ID, &devid);
351      if ( devid == 0xffff ) continue;
352
353      pci_read_config_word(pbus, pslot, 0, PCI_DEVICE_ID, &vendorid);
354      if ( vendorid == 0xffff ) continue;
355
356      pci_read_config_word(pbus, pslot, 0, PCI_CLASS_DEVICE, &dclass);
357
358      if ( dclass == PCI_CLASS_BRIDGE_PCI ) {
359        pci_read_config_byte(pbus, pslot, 0, PCI_PRIMARY_BUS,    &buspri);
360        pci_read_config_byte(pbus, pslot, 0, PCI_SECONDARY_BUS,  &bussec);
361
362#if 0
363         printk("pci : Found bridge at %d:0x%02x, mybus %d, pribus %d, secbus %d ",
364                 pbus, pslot, mybus, buspri, bussec );
365#endif
366         if ( bussec == mybus ) {
367#if 0
368           printk("match\n");
369#endif
370           /* found our nearest bridge going towards the root */
371           pb->bus = pbus;
372           pb->slot = pslot;
373
374           return 0;
375        }
376#if 0
377         printk("no match\n");
378#endif
379      }
380
381     }
382   }
383   return -1;
384}
385
386void FixupPCI( const struct _int_map *bspmap, int (*swizzler)(int,int) )
387{
388  unsigned char        cvalue;
389  uint16_t             devid;
390  int                  ismatch, i, j, pbus, pslot, pfun, int_pin, int_name, nfuns;
391
392  /*
393   * If the device has a non-zero INTERRUPT_PIN, assign a bsp-specific
394   * INTERRUPT_NAME if one isn't already in place.  Then, drivers can
395   * trivially use INTERRUPT_NAME to hook up with devices.
396   */
397
398  for (pbus=0; pbus< pci_bus_count(); pbus++) {
399        for (pslot=0; pslot< PCI_MAX_DEVICES; pslot++) {
400          pci_read_config_word(pbus, pslot, 0, PCI_DEVICE_ID, &devid);
401          if ( devid == 0xffff ) continue;
402
403          /* got a device */
404          pci_read_config_byte(pbus, pslot, 0, PCI_HEADER_TYPE, &cvalue);
405          nfuns = cvalue & PCI_MULTI_FUNCTION ? PCI_MAX_FUNCTIONS : 1;
406
407          for (pfun=0; pfun< nfuns; pfun++) {
408                pci_read_config_word(pbus, pslot, pfun, PCI_DEVICE_ID, &devid);
409                if( devid == 0xffff ) continue;
410
411                pci_read_config_byte( pbus, pslot, pfun, PCI_INTERRUPT_PIN, &cvalue);
412                int_pin = cvalue;
413
414                pci_read_config_byte( pbus, pslot, pfun, PCI_INTERRUPT_LINE, &cvalue);
415                int_name = cvalue;
416
417                /* printk("pci : device %d:0x%02x:%i devid %04x, intpin %d, intline  %d\n",
418                   pbus, pslot, pfun, devid, int_pin, int_name ); */
419
420#if 0
421                {
422                  unsigned short cmd,stat;
423                  unsigned char  lat, seclat, csize;
424
425                  pci_read_config_word(pbus,pslot,pfun,PCI_COMMAND, &cmd );
426                  pci_read_config_word(pbus,pslot,pfun,PCI_STATUS, &stat );
427                  pci_read_config_byte(pbus,pslot,pfun,PCI_LATENCY_TIMER, &lat );
428                  pci_read_config_byte(pbus,pslot,pfun,PCI_SEC_LATENCY_TIMER, &seclat );
429                  pci_read_config_byte(pbus,pslot,pfun,PCI_CACHE_LINE_SIZE, &csize );
430
431
432                  printk("pci : device %d:0x%02x:%d  cmd %04X, stat %04X, latency %d, "
433                          " sec_latency %d, clsize %d\n", pbus, pslot, pfun, cmd, stat,
434                          lat, seclat, csize);
435                }
436#endif
437
438                if ( int_pin > 0 ) {
439                  ismatch = 0;
440
441                  /*
442                   * first run thru the bspmap table and see if we have an
443                   * explicit configuration
444                   */
445                  for (i=0; bspmap[i].bus > -1; i++) {
446                        if ( bspmap[i].bus == pbus && bspmap[i].slot == pslot ) {
447                          ismatch = -1;
448                          /* we have a record in the table that gives specific
449                           * pins and interrupts for devices in this slot */
450                          if ( int_name == 255 ) {
451                                /* find the vector associated with whatever pin the
452                                 * device gives us
453                                 */
454                                for ( int_name=-1, j=0; bspmap[i].pin_route[j].pin > -1; j++ ) {
455                                  if ( bspmap[i].pin_route[j].pin == int_pin ) {
456                                        int_name = bspmap[i].pin_route[j].int_name[0];
457                                        break;
458                                  }
459                                }
460                                if ( int_name == -1 ) {
461                                  printk("pci : Unable to resolve device %d:0x%02x:%d w/ swizzled int "
462                                          "pin %i to an interrupt_line.\n", pbus, pslot, pfun, int_pin );
463                                } else {
464                                  PRINT_MSG();
465                                  pci_write_config_byte( pbus,pslot,pfun,
466                                          PCI_INTERRUPT_LINE,(cvalue= int_name, cvalue));
467                                }
468                          } else {
469                                test_intname( &bspmap[i],pbus,pslot,pfun,int_pin,int_name);
470                          }
471                          break;
472                        }
473                  }
474
475                  if ( !ismatch ) {
476                        /*
477                         * no match, which means we're on a bus someplace.  Work
478                         * backwards from it to one of our defined busses,
479                         * swizzling thru each bridge on the way.
480                         */
481
482                        /* keep pbus, pslot pointed to the device being
483                         * configured while we track down the bridges using
484                         * tbus,tslot.  We keep searching the routing table because
485                         * we may end up finding our bridge in it
486                         */
487
488                        int tbus= pbus, tslot= pslot;
489
490                        for (;;) {
491                          for (i=0; bspmap[i].bus > -1; i++) {
492                                if ( bspmap[i].bus == tbus &&
493                                        (bspmap[i].slot == tslot || bspmap[i].slot == -1) ) {
494                                  ismatch = -1;
495                                  /* found a record for this bus, so swizzle the
496                                   * int_pin which we then use to find the
497                                   * interrupt_name.
498                                   */
499
500                                  if ( int_name == 255 ) {
501                                        /*
502                                         * FIXME.  I can't believe this little hack
503                                         * is right.  It does not yield an error in
504                                         * convienently simple situations.
505                                         */
506                                        if ( tbus ) int_pin = (*swizzler)(tslot,int_pin);
507
508                                        /*
509                                         * int_pin points to the interrupt channel
510                                         * this card ends up delivering interrupts
511                                         * on.  Find the int_name servicing it.
512                                         */
513                                        for (int_name=-1, j=0; bspmap[i].pin_route[j].pin > -1; j++){
514                                          if ( bspmap[i].pin_route[j].pin == int_pin ) {
515                                                int_name = bspmap[i].pin_route[j].int_name[0];
516                                                break;
517                                          }
518                                        }
519
520                                        if ( int_name == -1 ) {
521                                          printk("pci : Unable to resolve device %d:0x%02x:%d w/ swizzled "
522                                                  "int pin %i to an interrupt_line.\n",
523                                                  pbus, pslot, pfun, int_pin );
524                                        } else {
525                                          PRINT_MSG();
526                                          pci_write_config_byte(pbus,pslot,pfun,
527                                                  PCI_INTERRUPT_LINE,(cvalue=int_name, cvalue));
528                                        }
529                                  } else {
530                                        test_intname(&bspmap[i],pbus,pslot,pfun,int_pin,int_name);
531                                  }
532                                  goto donesearch;
533                                }
534                          }
535
536                          if ( !ismatch ) {
537                                struct pcibridge   pb;
538
539                                /*
540                                 * Haven't found our bus in the int map, so work
541                                 * upwards thru the bridges till we find it.
542                                 */
543
544                                if ( FindPCIbridge( tbus, &pb )== 0 ) {
545                                  int_pin = (*swizzler)(tslot,int_pin);
546
547                                  /* our next bridge up is on pb.bus, pb.slot- now
548                                   * instead of pointing to the device we're
549                                   * trying to configure, we move from bridge to
550                                   * bridge.
551                                   */
552
553                                  tbus = pb.bus;
554                                  tslot = pb.slot;
555                                } else {
556                                  printk("pci : No bridge from bus %i towards root found\n",
557                                          tbus );
558                                  goto donesearch;
559                                }
560                          }
561                        }
562                  }
563donesearch:
564
565                  if ( !ismatch && int_pin != 0 && int_name == 255 ) {
566                        printk("pci : Unable to match device %d:0x%02x:%d with an int "
567                                "routing table entry\n", pbus, pslot, pfun  );
568                  }
569                }
570          }
571        }
572  }
573}
574
575/*
576 * This routine determines the maximum bus number in the system
577 */
578int pci_initialize()
579{
580  extern void detect_host_bridge();
581  unsigned char ucSlotNumber, ucFnNumber, ucNumFuncs;
582  unsigned char ucHeader;
583  unsigned char ucMaxSubordinate;
584  uint32_t ulClass;
585  uint32_t ulDeviceID;
586
587  detect_host_bridge();
588
589  /*
590   * Scan PCI bus 0 looking for PCI-PCI bridges
591   */
592  for (ucSlotNumber=0;ucSlotNumber<PCI_MAX_DEVICES;ucSlotNumber++) {
593    pci_read_config_dword(0, ucSlotNumber, 0, PCI_VENDOR_ID, &ulDeviceID);
594    if (ulDeviceID==PCI_INVALID_VENDORDEVICEID) {
595      /* This slot is empty */
596      continue;
597    }
598    pci_read_config_byte(0, ucSlotNumber, 0, PCI_HEADER_TYPE, &ucHeader);
599    if (ucHeader&PCI_MULTI_FUNCTION)  {
600      ucNumFuncs=PCI_MAX_FUNCTIONS;
601    } else {
602      ucNumFuncs=1;
603    }
604    for (ucFnNumber=0;ucFnNumber<ucNumFuncs;ucFnNumber++) {
605      pci_read_config_dword(0, ucSlotNumber, ucFnNumber,
606                            PCI_VENDOR_ID, &ulDeviceID);
607      if (ulDeviceID==PCI_INVALID_VENDORDEVICEID) {
608        /* This slot/function is empty */
609        continue;
610      }
611
612      /* This slot/function has a device fitted. */
613      pci_read_config_dword(0, ucSlotNumber, ucFnNumber,
614                            PCI_CLASS_REVISION, &ulClass);
615      ulClass >>= 16;
616      if (ulClass == PCI_CLASS_BRIDGE_PCI) {
617        /* We have found a PCI-PCI bridge */
618        pci_read_config_byte(0, ucSlotNumber, ucFnNumber,
619                     PCI_SUBORDINATE_BUS, &ucMaxSubordinate);
620       if (ucMaxSubordinate>ucMaxPCIBus) {
621         ucMaxPCIBus=ucMaxSubordinate;
622       }
623     }
624   }
625 }
626 return PCIB_ERR_SUCCESS;
627}
628
629/*
630 * Return the number of PCI busses in the system
631 */
632unsigned char pci_bus_count()
633{
634  return (ucMaxPCIBus+1);
635}
Note: See TracBrowser for help on using the repository browser.