source: rtems/c/src/lib/libbsp/powerpc/shared/pci/pci.c @ 2d5c486

4.115
Last change on this file since 2d5c486 was 2d5c486, checked in by Nick Withers <nick.withers@…>, on Nov 27, 2014 at 6:39:36 AM

Use fixed-width C99 types for PowerPC in_be16() and co.

Also use the const qualifier on the address pointer's target in in_*()

Closes #2128

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