summaryrefslogblamecommitdiffstats
path: root/c/src/lib/libbsp/sparc/leon3/pci/pci.c
blob: 583983bf36fe1b86e8b063d67d6776bf68fbb3e8 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12











                                                                          
                                                        







                                                        
                    




                                       
                   












                                                                         
                  
 
               



                           
                 


















                                                  
                          



























                                                                                          
 
 

                          






                         


                                                       
 




                                  

                                                    










                                  
                                                                                                                                                                          




                              
          
                                                                                                                                    










                                                              
          
                                                                                                                                   









                                                              

                                                                                                                                   






                                                                   

                                                                








                                
                                                                                                                                                                            




                              
          
                                                                                                                                    











                                                                             
          
                                                                                                                                   




                                                                         
 





                                                                     





                              

  




                                            











                                                                                                                                        
                                                                                                            

                                                                                                 
 

                                                                   

                                                      

                                                      

                                                            
                                               
                                                     











                                                                                
                     



                                   
                                      


                 
                     










                                                                                  
                     



                                   
                                      


                 
                     

               
 





                                                                                    
                                                                                      






                                                                                       
                                                                                      
















                                                                                        

                                                                                               











                                                                                     
                                                                   















                                                                   
 


















                                                                           
 




















                                                                                                 
                                                






















                                                                             
 

                                 
 




                                                                                                     
                                                     

                                                            


                                   
 

 




                                                    


                                                                   







                                                 
 

                                                                    

                                           


                                                                          
 








                                                                                                                      
 


                             
             




                 
 







                                
                  



                                                       
                                      











































































                                                                     
                               


                          
/*
 * pci.c :  this file contains basic PCI Io functions.
 *
 *  Copyright (C) 1999 valette@crf.canon.fr
 *
 *  This code is heavily inspired by the public specification of STREAM V2
 *  that can be found at :
 *
 *      <http://www.chorus.com/Documentation/index.html> by following
 *  the STREAM API Specification Document link.
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.rtems.com/license/LICENSE.
 *
 *  pci.c,v 1.2.4.4 2004/11/10 22:15:01 joel Exp
 *
 *  Till Straumann, <strauman@slac.stanford.edu>, 1/2002
 *   - separated bridge detection code out of this file
 *
 *
 *  Adapted to GRPCI
 *  Copyright (C) 2006 Gaisler Research
 *
 */

#include <pci.h>
#include <stdlib.h>
#include <rtems/bspIo.h>

#define PCI_ADDR 0x80000400
#define DMAPCI_ADDR 0x80000500
#define PCI_CONF 0xfff50000
#define PCI_MEM_START 0xe0000000
#define PCI_MEM_END   0xf0000000
#define PCI_MEM_SIZE  (PCI_MEM_START - PCI_MEM_END)

/* If uncommented byte twisting is enabled */
/*#define BT_ENABLED 1*/

/* Define PCI_INFO to get a listing of configured devices at boot time */
#define PCI_INFO 1

#define DEBUG 1

#ifdef DEBUG
#define DBG(x...) printk(x)
#else
#define DBG(x...)
#endif

/* allow for overriding these definitions */
#ifndef PCI_CONFIG_ADDR
#define PCI_CONFIG_ADDR			0xcf8
#endif
#ifndef PCI_CONFIG_DATA
#define PCI_CONFIG_DATA			0xcfc
#endif

#define PCI_INVALID_VENDORDEVICEID	0xffffffff
#define PCI_MULTI_FUNCTION		0x80

/* define a shortcut */
#define pci	BSP_pci_configuration

/*
 * Bit encode for PCI_CONFIG_HEADER_TYPE register
 */
unsigned char ucMaxPCIBus;
typedef struct {
	volatile unsigned int cfg_stat;
	volatile unsigned int bar0;
	volatile unsigned int page0;
	volatile unsigned int bar1;
	volatile unsigned int page1;
	volatile unsigned int iomap;
	volatile unsigned int stat_cmd;
} LEON3_GRPCI_Regs_Map;

LEON3_GRPCI_Regs_Map *pcic = (LEON3_GRPCI_Regs_Map *) PCI_ADDR;
unsigned int *pcidma = (unsigned int *)DMAPCI_ADDR;

struct pci_res {
    unsigned int size;
    unsigned char bar;
    unsigned char devfn;
};

static inline unsigned int flip_dword (unsigned int l)
{
        return ((l&0xff)<<24) | (((l>>8)&0xff)<<16) | (((l>>16)&0xff)<<8)| ((l>>24)&0xff);
}


/*  The configuration access functions uses the DMA functionality of the
 *  AT697 pci controller to be able access all slots
 */


static int
BSP_pci_read_config_dword(
  unsigned char bus,
  unsigned char slot,
  unsigned char function,
  unsigned char offset,
  unsigned int *val
)
{
    volatile unsigned int *pci_conf;

    if (offset & 3) return PCIBIOS_BAD_REGISTER_NUMBER;

    if (slot > 21) {
        *val = 0xffffffff;
        return PCIBIOS_SUCCESSFUL;
    }

    pci_conf = (volatile unsigned int *) (PCI_CONF +
        ((slot<<11) | (function<<8) | offset));

#ifdef BT_ENABLED
    *val =  flip_dword(*pci_conf);
#else
    *val = *pci_conf;
#endif

    if (pcic->cfg_stat & 0x100) {
        *val = 0xffffffff;
    }

   DBG("pci_read - bus: %d, dev: %d, fn: %d, off: %d => addr: %x, val: %x\n", bus, slot, function, offset,  (1<<(11+slot) ) | ((function & 7)<<8) |  (offset&0x3f), *val);

    return PCIBIOS_SUCCESSFUL;
}


static int
BSP_pci_read_config_word(unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, unsigned short *val) {
    unsigned int v;

    if (offset & 1) return PCIBIOS_BAD_REGISTER_NUMBER;

    pci_read_config_dword(bus, slot, function, offset&~3, &v);
    *val = 0xffff & (v >> (8*(offset & 3)));

    return PCIBIOS_SUCCESSFUL;
}


static int
BSP_pci_read_config_byte(unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, unsigned char *val) {
    unsigned int v;

    pci_read_config_dword(bus, slot, function, offset&~3, &v);

    *val = 0xff & (v >> (8*(offset & 3)));

    return PCIBIOS_SUCCESSFUL;
}


static int
BSP_pci_write_config_dword(unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, unsigned int val) {

    volatile unsigned int *pci_conf;
    unsigned int value;

    if (offset & 3 || bus != 0) return PCIBIOS_BAD_REGISTER_NUMBER;


    pci_conf = (volatile unsigned int *) (PCI_CONF +
                  ((slot<<11) | (function<<8) | (offset & ~3)));

#ifdef BT_ENABLED
        value = flip_dword(val);
#else
        value = val;
#endif

    *pci_conf = value;

    DBG("pci write - bus: %d, dev: %d, fn: %d, off: %d => addr: %x, val: %x\n", bus, slot, function, offset, (1<<(11+slot) ) | ((function & 7)<<8) |  (offset&0x3f), value);

    return PCIBIOS_SUCCESSFUL;
}


static int
BSP_pci_write_config_word(unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, unsigned short val) {
    unsigned int v;

    if (offset & 1) return PCIBIOS_BAD_REGISTER_NUMBER;

    pci_read_config_dword(bus, slot, function, offset&~3, &v);

    v = (v & ~(0xffff << (8*(offset&3)))) | ((0xffff&val) << (8*(offset&3)));

    return pci_write_config_dword(bus, slot, function, offset&~3, v);
}


static int
BSP_pci_write_config_byte(unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, unsigned char val) {
    unsigned int v;

    pci_read_config_dword(bus, slot, function, offset&~3, &v);

    v = (v & ~(0xff << (8*(offset&3)))) | ((0xff&val) << (8*(offset&3)));

    return pci_write_config_dword(bus, slot, function, offset&~3, v);
}



const pci_config_access_functions pci_access_functions = {
    BSP_pci_read_config_byte,
    BSP_pci_read_config_word,
    BSP_pci_read_config_dword,
    BSP_pci_write_config_byte,
    BSP_pci_write_config_word,
    BSP_pci_write_config_dword
};

rtems_pci_config_t BSP_pci_configuration = {
   (volatile unsigned char*)PCI_CONFIG_ADDR,
   (volatile unsigned char*)PCI_CONFIG_DATA,
   &pci_access_functions
};


int init_grpci(void) {

    volatile unsigned int *page0 =  (unsigned volatile int *) PCI_MEM_START;
    unsigned int data, addr;

#ifndef BT_ENABLED
    pci_write_config_dword(0,0,0,0x10, 0xffffffff);
    pci_read_config_dword(0,0,0,0x10, &addr);
    pci_write_config_dword(0,0,0,0x10, flip_dword(0x10000000));    /* Setup bar0 to nonzero value (grpci considers BAR==0 as invalid) */
    addr = (~flip_dword(addr)+1)>>1;                               /* page0 is accessed through upper half of bar0 */
    pcic->cfg_stat |= 0x10000000;                                  /* Setup mmap reg so we can reach bar0 */
    page0[addr/4] = 0;                                             /* Disable bytetwisting ... */
#endif

    /* set 1:1 mapping between AHB -> PCI memory */
    pcic->cfg_stat = (pcic->cfg_stat & 0x0fffffff) | PCI_MEM_START;

    /* and map system RAM at pci address 0x40000000 */
    pci_write_config_dword(0, 0, 0, 0x14, 0x40000000);
    pcic->page1 = 0x40000000;

     /* set as bus master and enable pci memory responses */
    pci_read_config_dword(0, 0, 0, 0x4, &data);
    pci_write_config_dword(0, 0, 0, 0x4, data | 0x6);

    return 0;
}

/* DMA functions which uses GRPCIs optional DMA controller (len in words) */
int dma_to_pci(unsigned int ahb_addr, unsigned int pci_addr, unsigned int len) {
    int ret = 0;

    pcidma[0] = 0x82;
    pcidma[1] = ahb_addr;
    pcidma[2] = pci_addr;
    pcidma[3] = len;
    pcidma[0] = 0x83;

    while ( (pcidma[0] & 0x4) == 0)
        ;

    if (pcidma[0] & 0x8) { /* error */
        ret = -1;
    }

    pcidma[0] |= 0xC;
    return ret;

}

int dma_from_pci(unsigned int ahb_addr, unsigned int pci_addr, unsigned int len) {
    int ret = 0;

    pcidma[0] = 0x80;
    pcidma[1] = ahb_addr;
    pcidma[2] = pci_addr;
    pcidma[3] = len;
    pcidma[0] = 0x81;

    while ( (pcidma[0] & 0x4) == 0)
        ;

    if (pcidma[0] & 0x8) { /* error */
        ret = -1;
    }

    pcidma[0] |= 0xC;
    return ret;

}


void pci_mem_enable(unsigned char bus, unsigned char slot, unsigned char function) {
    unsigned int data;

    pci_read_config_dword(0, slot, function, PCI_COMMAND, &data);
    pci_write_config_dword(0, slot, function, PCI_COMMAND, data | PCI_COMMAND_MEMORY);

}

void pci_master_enable(unsigned char bus, unsigned char slot, unsigned char function) {
    unsigned int data;

    pci_read_config_dword(0, slot, function, PCI_COMMAND, &data);
    pci_write_config_dword(0, slot, function, PCI_COMMAND, data | PCI_COMMAND_MASTER);

}

static inline void swap_res(struct pci_res **p1, struct pci_res **p2) {

    struct pci_res *tmp = *p1;
    *p1 = *p2;
    *p2 = tmp;

}

/* pci_allocate_resources
 *
 * This function scans the bus and assigns PCI addresses to all devices. It handles both
 * single function and multi function devices. All allocated devices are enabled and
 * latency timers are set to 40.
 *
 * NOTE that it only allocates PCI memory space devices (that are at least 1 KB).
 * IO spaces are not enabled. Also, it does not handle pci-pci bridges. They are left disabled.
 *
 *
*/
void pci_allocate_resources(void) {

    unsigned int slot, numfuncs, func, id, pos, size, tmp, i, swapped, addr, dev, fn;
    unsigned char header;
    struct pci_res **res;

    res = (struct pci_res **) malloc(sizeof(struct pci_res *)*32*8*6);

    for (i = 0; i < 32*8*6; i++) {
        res[i] = (struct pci_res *) malloc(sizeof(struct pci_res));
        res[i]->size = 0;
        res[i]->devfn = i;
    }

    for(slot = 1; slot < PCI_MAX_DEVICES; slot++) {

        pci_read_config_dword(0, slot, 0, PCI_VENDOR_ID, &id);

        if(id == PCI_INVALID_VENDORDEVICEID || id == 0) {
            /*
             * This slot is empty
             */
            continue;
        }

        pci_read_config_byte(0, slot, 0, PCI_HEADER_TYPE, &header);

        if(header & PCI_MULTI_FUNCTION)	{
            numfuncs = PCI_MAX_FUNCTIONS;
        }
        else {
            numfuncs = 1;
        }

        for(func = 0; func < numfuncs; func++) {

            pci_read_config_dword(0, slot, func, PCI_VENDOR_ID, &id);
            if(id == PCI_INVALID_VENDORDEVICEID || id == 0) {
                continue;
            }

            pci_read_config_dword(0, slot, func, PCI_CLASS_REVISION, &tmp);
            tmp >>= 16;
            if (tmp == PCI_CLASS_BRIDGE_PCI) {
                continue;
            }

            for (pos = 0; pos < 6; pos++) {
                pci_write_config_dword(0, slot, func, PCI_BASE_ADDRESS_0 + (pos<<2), 0xffffffff);
                pci_read_config_dword(0, slot, func, PCI_BASE_ADDRESS_0 + (pos<<2), &size);

                if (size == 0 || size == 0xffffffff || (size & 0x3f1) != 0){
                    pci_write_config_dword(0, slot, func, PCI_BASE_ADDRESS_0 + (pos<<2), 0);
                    continue;

                }else {
                    size &= 0xfffffff0;
                    res[slot*8*6+func*6+pos]->size  = ~size+1;
                    res[slot*8*6+func*6+pos]->devfn = slot*8 + func;
                    res[slot*8*6+func*6+pos]->bar   = pos;

                    DBG("Slot: %d, function: %d, bar%d size: %x\n", slot, func, pos, ~size+1);
                }
            }
        }
    }


    /* Sort the resources in descending order */
    swapped = 1;
    while (swapped == 1) {
        swapped = 0;
        for (i = 0; i < 32*8*6-1; i++) {
            if (res[i]->size < res[i+1]->size) {
                swap_res(&res[i], &res[i+1]);
                swapped = 1;
            }
        }
        i++;
    }

    /* Assign the BARs */
    addr = PCI_MEM_START;
    for (i = 0; i < 32*8*6; i++) {

        if (res[i]->size == 0) {
            goto done;
        }
        if ( (addr + res[i]->size) > PCI_MEM_END) {
            printk("Out of PCI memory space, all devices not configured.\n");
            goto done;
        }

        dev = res[i]->devfn >> 3;
        fn  = res[i]->devfn & 7;

        DBG("Assigning PCI addr %x to device %d, function %d, bar %d\n", addr, dev, fn, res[i]->bar);
        pci_write_config_dword(0, dev, fn, PCI_BASE_ADDRESS_0+res[i]->bar*4, addr);
        addr += res[i]->size;

        /* Set latency timer to 64 */
        pci_read_config_dword(0, dev, fn, 0xC, &tmp);
        pci_write_config_dword(0, dev, fn, 0xC, tmp|0x4000);

        pci_mem_enable(0, dev, fn);

    }



done:

#ifdef PCI_INFO
    printk("\nPCI devices found and configured:\n");
    for (slot = 1; slot < PCI_MAX_DEVICES; slot++) {

        pci_read_config_byte(0, slot, 0, PCI_HEADER_TYPE, &header);

        if(header & PCI_MULTI_FUNCTION)	{
            numfuncs = PCI_MAX_FUNCTIONS;
        }
        else {
            numfuncs = 1;
        }

        for (func = 0; func < numfuncs; func++) {

            pci_read_config_dword(0, slot, func, PCI_COMMAND, &tmp);

            if (tmp & PCI_COMMAND_MEMORY) {

                pci_read_config_dword(0, slot, func, PCI_VENDOR_ID,  &id);

                if (id == PCI_INVALID_VENDORDEVICEID || id == 0) continue;

                printk("\nSlot %d function: %d\nVendor id: 0x%x, device id: 0x%x\n", slot, func, id & 0xffff, id>>16);

                for (pos = 0; pos < 6; pos++) {
                    pci_read_config_dword(0, slot, func, PCI_BASE_ADDRESS_0 + pos*4, &tmp);

                    if (tmp != 0 && tmp != 0xffffffff && (tmp & 0x3f1) == 0) {

                        printk("\tBAR %d: %x\n", pos, tmp);
                    }

                }
                printk("\n");

            }

        }
     }
    printk("\n");
#endif

    for (i = 0; i < 1536; i++) {
        free(res[i]);
    }
    free(res);
}



int init_pci(void)
{
    unsigned char ucSlotNumber, ucFnNumber, ucNumFuncs;
    unsigned char ucHeader;
    unsigned char ucMaxSubordinate;
    unsigned int  ulClass, ulDeviceID;

    DBG("Initializing PCI\n");
    if ( init_grpci() ) {
      return -1;
    }
    pci_allocate_resources();
    DBG("PCI resource allocation done\n");
/*
 * Scan PCI bus 0 looking for PCI-PCI bridges
 */
    for(ucSlotNumber=0;ucSlotNumber<PCI_MAX_DEVICES;ucSlotNumber++) {
        (void)pci_read_config_dword(0,
                                    ucSlotNumber,
                                    0,
                                    PCI_VENDOR_ID,
                                    &ulDeviceID);
        if(ulDeviceID==PCI_INVALID_VENDORDEVICEID) {
/*
 * This slot is empty
 */
            continue;
        }
        (void)pci_read_config_byte(0,
                                   ucSlotNumber,
                                   0,
                                   PCI_HEADER_TYPE,
                                   &ucHeader);
        if(ucHeader&PCI_MULTI_FUNCTION)	{
            ucNumFuncs=PCI_MAX_FUNCTIONS;
        }
        else {
            ucNumFuncs=1;
        }
        for(ucFnNumber=0;ucFnNumber<ucNumFuncs;ucFnNumber++) {
            (void)pci_read_config_dword(0,
                                        ucSlotNumber,
                                        ucFnNumber,
                                        PCI_VENDOR_ID,
                                        &ulDeviceID);
            if(ulDeviceID==PCI_INVALID_VENDORDEVICEID) {
/*
 * This slot/function is empty
 */
                continue;
            }

/*
 * This slot/function has a device fitted.
 */
            (void)pci_read_config_dword(0,
                                        ucSlotNumber,
                                        ucFnNumber,
                                        PCI_CLASS_REVISION,
                                        &ulClass);
            ulClass >>= 16;
            if (ulClass == PCI_CLASS_BRIDGE_PCI) {
/*
 * We have found a PCI-PCI bridge
 */
                (void)pci_read_config_byte(0,
                                           ucSlotNumber,
                                           ucFnNumber,
                                           PCI_SUBORDINATE_BUS,
                                           &ucMaxSubordinate);
                if(ucMaxSubordinate>ucMaxPCIBus) {
                    ucMaxPCIBus=ucMaxSubordinate;
                }
            }
        }
    }
    return 0;
}

/*
 * Return the number of PCI busses in the system
 */
unsigned char BusCountPCI(void)
{
    return(ucMaxPCIBus+1);
}