Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:52

0001 /* Adjust a PCI bus range's I/O address space by adding an offset */
0002 
0003 /* 
0004  * Authorship
0005  * ----------
0006  * This software ('beatnik' RTEMS BSP for MVME6100 and MVME5500) was
0007  *     created by Till Straumann <strauman@slac.stanford.edu>, 2005-2007,
0008  *      Stanford Linear Accelerator Center, Stanford University.
0009  * 
0010  * Acknowledgement of sponsorship
0011  * ------------------------------
0012  * The 'beatnik' BSP was produced by
0013  *     the Stanford Linear Accelerator Center, Stanford University,
0014  *      under Contract DE-AC03-76SFO0515 with the Department of Energy.
0015  * 
0016  * Government disclaimer of liability
0017  * ----------------------------------
0018  * Neither the United States nor the United States Department of Energy,
0019  * nor any of their employees, makes any warranty, express or implied, or
0020  * assumes any legal liability or responsibility for the accuracy,
0021  * completeness, or usefulness of any data, apparatus, product, or process
0022  * disclosed, or represents that its use would not infringe privately owned
0023  * rights.
0024  * 
0025  * Stanford disclaimer of liability
0026  * --------------------------------
0027  * Stanford University makes no representations or warranties, express or
0028  * implied, nor assumes any liability for the use of this software.
0029  * 
0030  * Stanford disclaimer of copyright
0031  * --------------------------------
0032  * Stanford University, owner of the copyright, hereby disclaims its
0033  * copyright and all other rights in this software.  Hence, anyone may
0034  * freely use it for any purpose without restriction.  
0035  * 
0036  * Maintenance of notices
0037  * ----------------------
0038  * In the interest of clarity regarding the origin and status of this
0039  * SLAC software, this and all the preceding Stanford University notices
0040  * are to remain affixed to any copy or derivative of this software made
0041  * or distributed by the recipient and are to be affixed to any copy of
0042  * software made or distributed by the recipient that contains a copy or
0043  * derivative of this software.
0044  * 
0045  * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
0046  */ 
0047 
0048 #include <rtems.h>
0049 #include <rtems/bspIo.h>
0050 #include <bsp/pci.h>
0051 #include <stdint.h>
0052 #include "pci_io_remap.h"
0053 
0054 #ifndef PCI_MULTI_FUN
0055 #define PCI_MULTI_FUN 0x80
0056 #endif
0057 
0058 #ifndef PCI_HEADER_TYPE_MSK
0059 #define PCI_HEADER_TYPE_MSK 0x7f
0060 #endif
0061 
0062 /* Reconfigure all I/O base address registers for a range of PCI busses
0063  * (from and including 'bus_from' up to and not including 'bus_to').
0064  * adding an offset. This involves adjusting the base and limit registers
0065  * of PCI-PCI bridges, too.
0066  *
0067  * RESTRICTIONS: 'offset' must be 4k aligned (PCI req.); no argument check
0068  *       on the bus numbers is done.
0069  *
0070  * RETURNS: 0 on success and a number > 0 indicating the number of
0071  *          non-32bit bridges found where the offset couldn't be added.
0072  *          Devices behind such a bridge are not accessible through I/O
0073  *          and should probably be switched off (not done by this code).
0074  */
0075 int
0076 rtems_pci_io_remap(int bus_from, int bus_to, uint32_t offset)
0077 {
0078   int           rval = 0;
0079   int           bus, dev, fun, maxf;
0080   int           bar, numBars = 0;
0081   uint8_t       b;
0082   uint16_t      s;
0083   uint32_t      d;
0084   unsigned int  bas, lim;
0085 
0086   if ( offset & ((1<<12)-1) ) {
0087     rtems_panic("rtems_pci_io_remap(): offset must be 4k aligned");
0088     return -1;
0089   }
0090 
0091 
0092   for ( bus=bus_from; bus < bus_to; bus++ ) {
0093     for ( dev = 0; dev<PCI_MAX_DEVICES; dev++ ) {
0094 
0095       maxf = 1;
0096 
0097       for ( fun = 0; fun < maxf; fun++ ) {
0098         pci_read_config_word( bus, dev, fun, PCI_VENDOR_ID, &s );
0099         if ( 0xffff == s )
0100           continue;
0101 
0102         pci_read_config_byte( bus, dev, fun, PCI_HEADER_TYPE, &b );
0103 
0104         /* readjust the max. function number to scan if this is a multi-function
0105          * device.
0106          */
0107         if ( 0 == fun && (PCI_MULTI_FUN & b) )
0108           maxf = PCI_MAX_FUNCTIONS;
0109 
0110         /* Check the header type; panic if unknown.
0111          * header type 0 has 6 bars, header type 1 (PCI-PCI bridge) has 2
0112          */
0113         b &= PCI_HEADER_TYPE_MSK;
0114         switch ( b ) {
0115           default:
0116             printk("PCI header type %i (@%i/%i/%i)\n", b, bus, dev, fun);
0117             rtems_panic("rtems_pci_io_remap(): unknown PCI header type");
0118           return -1; /* keep compiler happy */
0119 
0120           case PCI_HEADER_TYPE_CARDBUS:
0121             printk("PCI header type %i (@%i/%i/%i)\n", b, bus, dev, fun);
0122             rtems_panic("rtems_pci_io_remap():  don't know how to deal with Cardbus bridge");
0123           return -1;
0124 
0125           case PCI_HEADER_TYPE_NORMAL:
0126             numBars = 6*4;  /* loop below counts reg. offset in bytes */
0127           break;
0128 
0129           case PCI_HEADER_TYPE_BRIDGE:
0130             numBars = 2*4;  /* loop below counts reg. offset in bytes */
0131           break;
0132 
0133         }
0134 
0135         for ( bar = 0; bar < numBars; bar+=4 ) {
0136           pci_read_config_dword( bus, dev, fun, PCI_BASE_ADDRESS_0 + bar, &d );
0137           if ( PCI_BASE_ADDRESS_SPACE_IO & d ) {
0138             /* It's an I/O BAR; remap */
0139             d &= PCI_BASE_ADDRESS_IO_MASK;
0140             if ( d ) {
0141               /* IO bar was configured; add offset */
0142               d += offset;
0143               pci_write_config_dword( bus, dev, fun, PCI_BASE_ADDRESS_0 + bar, d );
0144             }
0145           } else {
0146             /* skip upper half of 64-bit window */
0147             d &= PCI_BASE_ADDRESS_MEM_TYPE_MASK;
0148             if ( PCI_BASE_ADDRESS_MEM_TYPE_64 == d )
0149               bar+=4;
0150           }
0151         }
0152 
0153         /* Now it's time to deal with bridges */
0154         if ( PCI_HEADER_TYPE_BRIDGE == b ) {
0155           /* must adjust the limit registers */
0156           pci_read_config_byte( bus, dev, fun, PCI_IO_LIMIT, &b );
0157           pci_read_config_word( bus, dev, fun, PCI_IO_LIMIT_UPPER16, &s );
0158           lim  = (s<<16) + (( b & PCI_IO_RANGE_MASK ) << 8);
0159           lim += offset;
0160 
0161           pci_read_config_byte( bus, dev, fun, PCI_IO_BASE, &b );
0162           pci_read_config_word( bus, dev, fun, PCI_IO_BASE_UPPER16, &s );
0163           bas  = (s<<16) + (( b & PCI_IO_RANGE_MASK ) << 8);
0164           bas += offset;
0165 
0166           b &= PCI_IO_RANGE_TYPE_MASK;
0167           switch ( b ) {
0168             default:
0169               printk("Unknown IO range type 0x%x (@%i/%i/%i)\n", b, bus, dev, fun);
0170               rtems_panic("rtems_pci_io_remap(): unknown IO range type");
0171             return -1;
0172 
0173             case PCI_IO_RANGE_TYPE_16:
0174               if ( bas > 0xffff || lim > 0xffff ) {
0175                 printk("PCI I/O range type 1 (16bit) bridge (@%i/%i/%i) found:\n", bus, dev, fun);
0176                 printk("WARNING: base (0x%08x) or limit (0x%08x) exceed 16-bit;\n", bas, lim);
0177                 printk("         devices behind this bridge are NOT accessible!\n");
0178 
0179                 /* FIXME: should we disable devices behind this bridge ? */
0180                 bas = lim = 0;
0181               }
0182             break;
0183 
0184             case PCI_IO_RANGE_TYPE_32:
0185             break;
0186           }
0187 
0188           b = (uint8_t)((bas>>8) & PCI_IO_RANGE_MASK);
0189           pci_write_config_byte( bus, dev, fun, PCI_IO_BASE, b );
0190 
0191           s = (uint16_t)((bas>>16)&0xffff);
0192           pci_write_config_word( bus, dev, fun, PCI_IO_BASE_UPPER16, s);
0193 
0194           b = (uint8_t)((lim>>8) & PCI_IO_RANGE_MASK);
0195           pci_write_config_byte( bus, dev, fun, PCI_IO_LIMIT, b );
0196           s = (uint16_t)((lim>>16)&0xffff);
0197           pci_write_config_word( bus, dev, fun, PCI_IO_LIMIT_UPPER16, s );
0198         }
0199       }
0200     }
0201   }
0202   return rval;
0203 }