source: rtems/c/src/lib/libbsp/powerpc/shared/pci/pci.c @ 548ed3f

4.104.114.84.9
Last change on this file since 548ed3f was 548ed3f, checked in by Jennifer Averett <Jennifer.Averett@…>, on May 4, 2005 at 7:33:46 PM

2005-05-04 Jennifer Averett <jennifer.averett@…>

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