/* PCI.c - PCI functions */

/* Copyright - Galileo technology. */

#include <ppcboot.h>
#include <galileo/pci.h>

#undef DEBUG
#undef IDE_SET_NATIVE_MODE

static const unsigned char pci_irq_swizzle[2][PCI_MAX_DEVICES] = {
#ifdef ZUMA_NTL
    {0,0,0,0,0,0,0,29, [8 ... PCI_MAX_DEVICES-1]=0},
    {0,0,0,0,0,0,0,28, [8 ... PCI_MAX_DEVICES-1]=0}
#else	/* EVB??? This is a guess */
    {0,0,0,0,0,0,0,27,27, [9 ... PCI_MAX_DEVICES-1]=0},
    {0,0,0,0,0,0,0,29,29, [9 ... PCI_MAX_DEVICES-1]=0}
#endif
};

static const unsigned int BAR_REG[]={BAR0,BAR1,BAR2,BAR3,BAR4,BAR5};

static PCI_DEVICE pci_dev_list[2][PCI_MAX_DEVICES];
static unsigned int pci_mem_bar_alloc[2]={CFG_PCI0_MEM_BASE,CFG_PCI1_MEM_BASE};
static unsigned int pci_io_bar_alloc[2]={CFG_PCI0_IO_SPACE_PCI,CFG_PCI1_IO_SPACE_PCI};

static void decode_class(char *buf, int class)
{
    static const char * const decode_legend[] = {
	"Old generation device",			/* 0 */
	"Mass storage controller",			/* 1 */
	"Network controller",				/* 2 */
	"Display controller",				/* 3 */
	"Multimedia device",				/* 4 */
	"Memory controller",				/* 5 */
	"Bridge Device",				/* 6 */
	"Simple Communication controllers",		/* 7 */
	"Base system peripherals",			/* 8 */
	"Input Devices",				/* 9 */
	"Docking stations",				/* a */
	"Processors",					/* b */
	"Serial bus controllers",			/* c */
	"Wireless controllers",				/* d */
	"Intelligent I/O controllers",			/* e */
	"Satellite communication controllers",		/* f */
	"Encryption/Decryption controllers",		/* 10 */
	"Data acquisition and signal processing controllers", /* 11 */
	"\0"
    };

    if(class>=0 && class<=0x11)
	strncpy(buf,decode_legend[class],40);
    else if(class==0xff)
	strncpy(buf,"Other",40);
    else 
	sprintf(buf,"Unknown class 0x%x", class);
}

static const unsigned int pci_p2p_configuration_reg[]={
    PCI_0P2P_CONFIGURATION, PCI_1P2P_CONFIGURATION};

static const unsigned int pci_configuration_address[]={
    PCI_0CONFIGURATION_ADDRESS, PCI_1CONFIGURATION_ADDRESS};

static const unsigned int pci_configuration_data[]={
    PCI_0CONFIGURATION_DATA_VIRTUAL_REGISTER,
    PCI_1CONFIGURATION_DATA_VIRTUAL_REGISTER};

static const unsigned int pci_error_cause_reg[]={
    PCI_0ERROR_CAUSE, PCI_1ERROR_CAUSE};

static const unsigned int pci_arbiter_control[]={
    PCI_0ARBITER_CONTROL, PCI_1ARBITER_CONTROL};

static const unsigned int pci_snoop_control_base_0_low[]={
    PCI_0SNOOP_CONTROL_BASE_0_LOW, PCI_1SNOOP_CONTROL_BASE_0_LOW};
static const unsigned int pci_snoop_control_top_0[]={
    PCI_0SNOOP_CONTROL_TOP_0, PCI_1SNOOP_CONTROL_TOP_0};

static const unsigned int pci_access_control_base_0_low[]={
    PCI_0ACCESS_CONTROL_BASE_0_LOW, PCI_1ACCESS_CONTROL_BASE_0_LOW};
static const unsigned int pci_access_control_top_0[]={
    PCI_0ACCESS_CONTROL_TOP_0, PCI_1ACCESS_CONTROL_TOP_0};

static const unsigned int pci_scs_bank_size[2][4] = {
    {PCI_0SCS_0_BANK_SIZE, PCI_0SCS_1_BANK_SIZE,
     PCI_0SCS_2_BANK_SIZE, PCI_0SCS_3_BANK_SIZE},
    {PCI_1SCS_0_BANK_SIZE, PCI_1SCS_1_BANK_SIZE,
     PCI_1SCS_2_BANK_SIZE, PCI_1SCS_3_BANK_SIZE}};

static const unsigned int pci_p2p_configuration[] = {
    PCI_0P2P_CONFIGURATION, PCI_1P2P_CONFIGURATION};

static u32 size_bar(int host, int barnum, int device, PCI_BAR *bar)
{
    u32 detect, size=0;

    pciWriteConfigReg(host,BAR_REG[barnum],device,0x0);
    if((((detect=pciReadConfigReg(host,BAR_REG[barnum],device))&~9)!=0x0)) {
	static const int ide_bar[]={8,4,8,4,0,0};
        u32 classCode = pciReadConfigReg(host,PCI_CLASS_CODE_AND_REVISION_ID,
                                          device);
	if((classCode>>16)!=0x0101) {
	    printf("  %d:%d weird bar and not PCI/IDE (classCode=%08x)\n",
		    device,barnum,classCode);
	    printf("  %d:%d 0->bar: BAR:%x\n", device,barnum,detect);
	    pciWriteConfigReg(host,BAR_REG[barnum],device,0xffffffff);
	    detect=pciReadConfigReg(host,BAR_REG[barnum],device);
	    printf("  %d:%d f->bar: BAR:%x\n", device,barnum,detect);
	    return 0;
	}

	bar->type &= ~BAR_MEM_TYPE_MASK; /* set to type 0 */
	return ide_bar[barnum];
    }

    pciWriteConfigReg(host,BAR_REG[barnum],device,0xffffffff);
    bar->detectBase = pciReadConfigReg(host,BAR_REG[barnum],device);

    if(bar->detectBase == 0) { /* case of an empty BAR */
	return 0;
    }

    if (bar->type & BAR_SEL_MEM_IO) {
	bar->type &= BAR_SEL_MEM_IO;
	size = ~(bar->detectBase & 0xfffffffc) + 1;
    } else {
	size = ~(bar->detectBase & 0xfffffff0) + 1;
    }

    return size;

}

/********************************************************************
* pciScanDevices   - This function scan PCIn bus, if found any device on
*                     this bus it interrogate the Device for the information 
*                     it can discover.
*                     The fields with all information are the following: 
*    char            type[20];
*    unsigned int    deviceNum;
*    unsigned int    venID;                                            
*    unsigned int    deviceID;
*    struct {
*        unsigned int    base;
*        unsigned int    size;
*        unsigned int    type;
*    } bar[6];
* 
* Inputs:   PCI_DEVICE* pciDetect - Pointer to an array of STRUCT PCI_DEVICE.
*           unsigned int numberOfElment - The PCI_DEVICE Array length.
* Output:   None.
*********************************************************************/
void    pciScanDevices(PCI_HOST host, PCI_DEVICE* pciDetect,unsigned int numberOfElment) 
{
    PCI_DEVICE*             pciArrayPointer = pciDetect;
    unsigned int            id;         /* PCI Configuration register 0x0.*/
    unsigned int            device;     /* device`s Counter.*/
    unsigned int            classCode;  /* PCI Configuration register 0x8 */
    unsigned int            arrayCounter = 0;
    unsigned int            memBaseAddress;
    unsigned int            pciCause;

    RESET_REG_BITS(pci_error_cause_reg[host],BIT8); /* clears PCI_0 cause. */

    PCI_MASTER_ENABLE(host,SELF);

    for(device = 0 ; device < PCI_MAX_DEVICES ; device ++) 
    {
        id = pciReadConfigReg(host, PCI_DEVICE_AND_VENDOR_ID,device);
        GT_REG_READ(pci_error_cause_reg[host],&pciCause);
        /* Clearing bit 8 of in the Cause Register by writing 0. */
        RESET_REG_BITS(pci_error_cause_reg[host],BIT8); /* clears PCI_n cause. */
        if ( (id != 0xffffffff) && !(pciCause & BIT8) ) { /* agent was found */
	    int i;
#if 0
	    if(device!=0) {
		int statcmd = pciReadConfigReg(host,PCI_STATUS_AND_COMMAND,
			device);
		statcmd &= ~(MASTER_ENABLE | MEMORY_ENABLE | I_O_ENABLE);
		/* disable this device */
		pciWriteConfigReg(host,PCI_STATUS_AND_COMMAND, device, statcmd);
	    }
#endif

            classCode = pciReadConfigReg(host,PCI_CLASS_CODE_AND_REVISION_ID,
                                          device);

            pciArrayPointer->host = host;
            pciArrayPointer->deviceNum = device;
            pciArrayPointer->venID = ( id & 0xffff);
            pciArrayPointer->deviceID = ( ( id & 0xffff0000) >> 16 );

#ifdef IDE_SET_NATIVE_MODE
	    if ((classCode >> 16) == 0x0101) {	/* IDE/PCI hack to set to native mode */
#ifdef DEBUG
		printf("%d:%d setting PCI IDE native mode\n", host, device);
#endif
		pciWriteConfigReg(host,PCI_CLASS_CODE_AND_REVISION_ID,device,
		classCode | 0x0500);

		for(i=0x50;i<0x5b;i+=4) {
            		printf("%x: %08x\n",i, pciReadConfigReg(host,i,device));
		}
	    }
#endif

	    for(i=0;i<6;i++) {
		PCI_BAR *pbar=&pciArrayPointer->bar[i];
                /* BARn parameters */
		memBaseAddress = pciReadConfigReg(host,BAR_REG[i],device);
		pbar->type = memBaseAddress & BAR_CONFIG_MASK;
		pbar->base = memBaseAddress & 0xfffff000;
		pbar->size = size_bar(host,i,device, pbar);
		/* restore the previous value to BAR_n base */
		pciWriteConfigReg(host,BAR_REG[i],device,memBaseAddress);
	    }

	    decode_class(pciArrayPointer->type, classCode>>24);

            arrayCounter++; /* point to the next element in the Array. */
            if(arrayCounter == numberOfElment) 
                return; /* When the Array is fully used, return. */
            /* Else, points to next free Element.*/
            pciArrayPointer = &pciDetect[arrayCounter]; 
        }     
    }         
    pciArrayPointer->deviceNum = -1; /* -1 => End of List */
}

/********************************************************************
* pciWriteConfigReg - Write to a PCI configuration register
*                    - Make sure the GT is configured as a master before writing
*                      to another device on the PCI.
*                    - The function takes care of Big/Little endian conversion.
*
*
* Inputs:   unsigned int regOffset: The register offset as it apears in the GT spec 
*                   (or any other PCI device spec)
*           pciDevNum: The device number needs to be addressed.                
*
*  Configuration Address 0xCF8:
*
*       31 30    24 23  16 15  11 10     8 7      2  0     <=bit Number
*  |congif|Reserved|  Bus |Device|Function|Register|00|
*  |Enable|        |Number|Number| Number | Number |  |    <=field Name
*
*********************************************************************/
void pciWriteConfigReg(PCI_HOST host, unsigned int regOffset,unsigned int pciDevNum,unsigned int data)
{                                                      
    volatile unsigned int DataForAddrReg;
    unsigned int functionNum;
    unsigned int busNum = 0;
    unsigned int addr;

    if(pciDevNum > 32) /* illegal device Number */
        return;
    if(pciDevNum == SELF) /* configure our configuration space. */
    {
        pciDevNum = (GTREGREAD(pci_p2p_configuration_reg[host]) >> 24) & 0x1f;
        busNum = GTREGREAD(pci_p2p_configuration_reg[host]) & 0xff0000;
    }
    functionNum =  regOffset & 0x00000700;
    pciDevNum = pciDevNum << 11;
    regOffset = regOffset & 0xfc;
    DataForAddrReg = ( regOffset | pciDevNum | functionNum | busNum) | BIT31;
    GT_REG_WRITE(pci_configuration_address[host],DataForAddrReg);
    GT_REG_READ(pci_configuration_address[host], &addr);
    if (addr != DataForAddrReg) return;
    GT_REG_WRITE(pci_configuration_data[host],data);
}

/********************************************************************
* pciReadConfigReg  - Read from a PCI0 configuration register
*                    - Make sure the GT is configured as a master before reading
*                     from another device on the PCI.
*                   - The function takes care of Big/Little endian conversion.
* INPUTS:   regOffset: The register offset as it apears in the GT spec (or PCI
*                        spec)
*           pciDevNum: The device number needs to be addressed.                
* RETURNS: data , if the data == 0xffffffff check the master abort bit in the 
*                 cause register to make sure the data is valid
*
*  Configuration Address 0xCF8:
*
*       31 30    24 23  16 15  11 10     8 7      2  0     <=bit Number
*  |congif|Reserved|  Bus |Device|Function|Register|00|
*  |Enable|        |Number|Number| Number | Number |  |    <=field Name
*
*********************************************************************/
unsigned int pciReadConfigReg (PCI_HOST host, unsigned int regOffset,unsigned int pciDevNum)
{
    volatile unsigned int DataForAddrReg;
   	unsigned int data;
    unsigned int functionNum;
    unsigned int busNum = 0;
   	
    if(pciDevNum > 32) /* illegal device Number */
        return 0xffffffff;
    if(pciDevNum == SELF) /* configure our configuration space. */
    {
        pciDevNum = (GTREGREAD(pci_p2p_configuration_reg[host]) >> 24) & 0x1f;
        busNum = GTREGREAD(pci_p2p_configuration_reg[host]) & 0xff0000;
    }
    functionNum = regOffset & 0x00000700;
    pciDevNum = pciDevNum << 11;
    regOffset = regOffset & 0xfc;
    DataForAddrReg = (regOffset | pciDevNum | functionNum | busNum) | BIT31 ;
    GT_REG_WRITE(pci_configuration_address[host],DataForAddrReg);
    GT_REG_READ(pci_configuration_address[host], &data);
    if (data != DataForAddrReg)
        return 0xffffffff;
    GT_REG_READ(pci_configuration_data[host], &data);
    return data;
}

/********************************************************************
* pciOverBridgeWriteConfigReg - Write to a PCI configuration register where  
*                               the agent is placed on another Bus. For more 
*                               information read P2P in the PCI spec.
*
* Inputs:   unsigned int regOffset - The register offset as it apears in the  
*           GT spec (or any other PCI device spec).
*           unsigned int pciDevNum - The device number needs to be addressed.
*           unsigned int busNum - On which bus does the Target agent connect
*                                 to.  
*           unsigned int data - data to be written.              
*
*  Configuration Address 0xCF8:
*
*       31 30    24 23  16 15  11 10     8 7      2  0     <=bit Number
*  |congif|Reserved|  Bus |Device|Function|Register|01|
*  |Enable|        |Number|Number| Number | Number |  |    <=field Name
*
*  The configuration Address is configure as type-I (bits[1:0] = '01') due to
*   PCI spec referring to P2P.   
*
*********************************************************************/
void pciOverBridgeWriteConfigReg(PCI_HOST host,
				 unsigned int regOffset,
                                 unsigned int pciDevNum,
                                 unsigned int busNum,unsigned int data)
{
   	unsigned int   DataForReg;
    unsigned int   functionNum;

   	functionNum =  regOffset & 0x00000700;
    pciDevNum = pciDevNum << 11;
    regOffset = regOffset & 0xff;
    busNum = busNum << 16;
    if(pciDevNum == SELF) /* This board */
    {
        DataForReg = ( regOffset | pciDevNum | functionNum) | BIT0;
    }
    else
    {
        DataForReg = ( regOffset | pciDevNum | functionNum | busNum) | 
            BIT31 | BIT0;
    }
    GT_REG_WRITE(pci_configuration_address[host],DataForReg);
    if(pciDevNum == SELF) /* This board */
    {
        GT_REG_WRITE(pci_configuration_data[host],data);
    }
    else /* configuration Transaction over the pci. */
    {
        /* The PCI is working in LE Mode So it swap the Data. */
        GT_REG_WRITE(pci_configuration_data[host],WORD_SWAP(data));
    }
}


/********************************************************************
* pciOverBridgeReadConfigReg  - Read from a PCIn configuration register where  
*                               the agent target locate on another PCI bus.
*                             - Make sure the GT is configured as a master  
*                               before reading from another device on the PCI.
*                             - The function takes care of Big/Little endian 
*                               conversion.
* INPUTS:   regOffset: The register offset as it apears in the GT spec (or PCI
*                        spec). (configuration register offset.)
*           pciDevNum: The device number needs to be addressed.
*           busNum: the Bus number where the agent is place.               
* RETURNS: data , if the data == 0xffffffff check the master abort bit in the 
*                 cause register to make sure the data is valid
*
*  Configuration Address 0xCF8:
*
*       31 30    24 23  16 15  11 10     8 7      2  0     <=bit Number
*  |congif|Reserved|  Bus |Device|Function|Register|01|
*  |Enable|        |Number|Number| Number | Number |  |    <=field Name
*
*********************************************************************/
unsigned int pciOverBridgeReadConfigReg(PCI_HOST host,
					unsigned int regOffset,
                                        unsigned int pciDevNum,
                                        unsigned int busNum)
{
    unsigned int DataForReg;
    unsigned int data;
    unsigned int functionNum;      
   	
    functionNum = regOffset & 0x00000700;
    pciDevNum = pciDevNum << 11;
    regOffset = regOffset & 0xff;
    busNum = busNum << 16;
    if (pciDevNum == SELF) /* This board */
    {
        DataForReg = (regOffset | pciDevNum | functionNum) | BIT31 ;
    }
    else /* agent on another bus */
    {
        DataForReg = (regOffset | pciDevNum | functionNum | busNum) | 
        BIT0 | BIT31 ;
    }
    GT_REG_WRITE(pci_configuration_address[host],DataForReg);
    if (pciDevNum == SELF) /* This board */
   	{
        GT_REG_READ(pci_configuration_data[host], &data);
    	return data;
    }
    else /* The PCI is working in LE Mode So it swap the Data. */
    {
        GT_REG_READ(pci_configuration_data[host], &data);
    	return WORD_SWAP(data);
    }
}

#if 0 // FIXME eventually - this is crap
/********************************************************************
* pci0ScanSelfBars - update in PCI_SELF_BARS struct all bars base and sizes.
*
* Inputs: PCI_SELF_BARS *pci0SelfBars - the struct which the data will be update
*                                       in.
*********************************************************************/
void pci0ScanSelfBars(PCI_SELF_BARS *pciSelfBars)
{
    unsigned int size;
    pciSelfBars->SCS0Base = pciReadConfigReg(0,PCI_SCS_0_BASE_ADDRESS,SELF) &
                             0xfffff000;
    size = GTREGREAD(PCI_0SCS_0_BANK_SIZE);
    if(size)
        pciSelfBars->SCS0Size = size + 0x1000;
    else
        pciSelfBars->SCS0Size = 0;
    pciSelfBars->SCS1Base = pciReadConfigReg(0,PCI_SCS_1_BASE_ADDRESS,SELF) &
                             0xfffff000;
    size = GTREGREAD(PCI_0SCS_1_BANK_SIZE);
    if(size)
        pciSelfBars->SCS1Size = size + 0x1000;
    else
        pciSelfBars->SCS1Size = 0;
    pciSelfBars->SCS2Base = pciReadConfigReg(0,PCI_SCS_2_BASE_ADDRESS,SELF) &
                             0xfffff000;
    size = GTREGREAD(PCI_0SCS_2_BANK_SIZE);
    if(size)
        pciSelfBars->SCS2Size = size + 0x1000;
    else
        pciSelfBars->SCS2Size = 0;
    pciSelfBars->SCS3Base = pciReadConfigReg(0,PCI_SCS_3_BASE_ADDRESS,SELF) &
                             0xfffff000;
    size = GTREGREAD(PCI_0SCS_3_BANK_SIZE);
    if(size)
        pciSelfBars->SCS3Size = size + 0x1000;
    else
        size = 0;
    pciSelfBars->CS0Base = pciReadConfigReg(0,PCI_CS_0_BASE_ADDRESS,SELF) &
                             0xfffff000;
    size = GTREGREAD(PCI_0CS_0_BANK_SIZE);
    if(size)
        pciSelfBars->CS0Size = size + 0x1000;
    else
        pciSelfBars->CS0Size = 0;
    pciSelfBars->CS1Base = pciReadConfigReg(0,PCI_CS_1_BASE_ADDRESS,SELF) &
                             0xfffff000;
    size = GTREGREAD(PCI_0CS_1_BANK_SIZE);
    if(size)
        pciSelfBars->CS1Size = size + 0x1000;
    else
        pciSelfBars->CS1Size = 0;
    pciSelfBars->CS2Base = pciReadConfigReg(0,PCI_CS_2_BASE_ADDRESS,SELF) &
                             0xfffff000;
    size = GTREGREAD(PCI_0CS_2_BANK_SIZE);
    if(size)
        pciSelfBars->CS2Size = size + 0x1000;
    else
        pciSelfBars->CS2Size = 0;
    pciSelfBars->CS3Base = pciReadConfigReg(0,PCI_CS_3_BASE_ADDRESS,SELF) &
                             0xfffff000;
    size = GTREGREAD(PCI_0CS_3_BANK_SIZE);
    if(size)
        pciSelfBars->CS3Size = size + 0x1000;
    else
        pciSelfBars->CS3Size = 0;
    pciSelfBars->CSBootBase = pciReadConfigReg(0,PCI_BOOTCS_BASE_ADDRESS,SELF) &
                             0xfffff000;
    size = GTREGREAD(PCI_0CS_BOOT_BANK_SIZE);
    if(size)
        pciSelfBars->CSBootSize = size + 0x1000;
    else
        pciSelfBars->CSBootSize = 0;
    pciSelfBars->internalMemBase = pciReadConfigReg(0,
        PCI_INTERNAL_REGISTERS_MEMORY_MAPPED_BASE_ADDRESS,SELF) &
                             0xfffff000;
    pciSelfBars->internalIOBase = pciReadConfigReg(0,
        PCI_INTERNAL_REGISTERS_I_OMAPPED_BASE_ADDRESS,SELF) &
                             0xfffff000;
    pciSelfBars->P2PMem0Base = pciReadConfigReg(0,PCI_P2P_MEM0_BASE_ADDRESS,SELF) &
                             0xfffff000;
    size = GTREGREAD(PCI_0P2P_MEM0_BAR_SIZE);
    if(size)
        pciSelfBars->P2PMem0Size = size + 0x1000;
    else
        pciSelfBars->P2PMem0Size = 0;
    pciSelfBars->P2PMem1Base = pciReadConfigReg(0,PCI_P2P_MEM1_BASE_ADDRESS,SELF) &
                             0xfffff000;
    size = GTREGREAD(PCI_0P2P_MEM1_BAR_SIZE);
    if(size)
        pciSelfBars->P2PMem1Size = size + 0x1000;
    else
        pciSelfBars->P2PMem1Size = 0;
    pciSelfBars->P2PIOBase = pciReadConfigReg(0,PCI_P2P_I_O_BASE_ADDRESS,SELF) &
                             0xfffff000;
    size = GTREGREAD(PCI_0P2P_I_O_BAR_SIZE);
    if(size)
        pciSelfBars->P2PIOSize = size + 0x1000;
    else
        pciSelfBars->P2PIOSize = 0;
    pciSelfBars->CPUBase = pciReadConfigReg(0,PCI_CPU_BASE_ADDRESS,SELF) &
                             0xfffff000;
    size = GTREGREAD(PCI_0CPU_BAR_SIZE);
    if(size)
        pciSelfBars->CPUSize = size + 0x1000;
    else
        pciSelfBars->CPUSize = 0;
}

/********************************************************************
* pci1ScanSelfBars - update in PCI_SELF_BARS struct all bars base and sizes.
*
* Inputs: PCI_SELF_BARS *pci1SelfBars - the struct which the data will be update
*                                       in.
*********************************************************************/
void pci1ScanSelfBars(PCI_SELF_BARS *pciSelfBars)
{
    unsigned int size;
    pciSelfBars->SCS0Base = pciReadConfigReg(1,PCI_SCS_0_BASE_ADDRESS,SELF) &
                             0xfffff000;
    size = GTREGREAD(PCI_0SCS_0_BANK_SIZE);
    if(size)
        pciSelfBars->SCS0Size = size + 0x1000;
    else
        pciSelfBars->SCS0Size = 0;
    pciSelfBars->SCS1Base = pciReadConfigReg(1,PCI_SCS_1_BASE_ADDRESS,SELF) &
                             0xfffff000;
    size = GTREGREAD(PCI_0SCS_1_BANK_SIZE);
    if(size)
        pciSelfBars->SCS1Size = size + 0x1000;
    else
        pciSelfBars->SCS1Size = 0;
    pciSelfBars->SCS2Base = pciReadConfigReg(1,PCI_SCS_2_BASE_ADDRESS,SELF) &
                             0xfffff000;
    size = GTREGREAD(PCI_0SCS_2_BANK_SIZE);
    if(size)
        pciSelfBars->SCS2Size = size + 0x1000;
    else
        pciSelfBars->SCS2Size = 0;
    pciSelfBars->SCS3Base = pciReadConfigReg(1,PCI_SCS_3_BASE_ADDRESS,SELF) &
                             0xfffff000;
    size = GTREGREAD(PCI_0SCS_3_BANK_SIZE);
    if(size)
        pciSelfBars->SCS3Size = size + 0x1000;
    else
        size = 0;
    pciSelfBars->CS0Base = pciReadConfigReg(1,PCI_CS_0_BASE_ADDRESS,SELF) &
                             0xfffff000;
    size = GTREGREAD(PCI_0CS_0_BANK_SIZE);
    if(size)
        pciSelfBars->CS0Size = size + 0x1000;
    else
        pciSelfBars->CS0Size = 0;
    pciSelfBars->CS1Base = pciReadConfigReg(1,PCI_CS_1_BASE_ADDRESS,SELF) &
                             0xfffff000;
    size = GTREGREAD(PCI_0CS_1_BANK_SIZE);
    if(size)
        pciSelfBars->CS1Size = size + 0x1000;
    else
        pciSelfBars->CS1Size = 0;
    pciSelfBars->CS2Base = pciReadConfigReg(1,PCI_CS_2_BASE_ADDRESS,SELF) &
                             0xfffff000;
    size = GTREGREAD(PCI_0CS_2_BANK_SIZE);
    if(size)
        pciSelfBars->CS2Size = size + 0x1000;
    else
        pciSelfBars->CS2Size = 0;
    pciSelfBars->CS3Base = pciReadConfigReg(1,PCI_CS_3_BASE_ADDRESS,SELF) &
                             0xfffff000;
    size = GTREGREAD(PCI_0CS_3_BANK_SIZE);
    if(size)
        pciSelfBars->CS3Size = size + 0x1000;
    else
        pciSelfBars->CS3Size = 0;
    pciSelfBars->CSBootBase = pciReadConfigReg(1,PCI_BOOTCS_BASE_ADDRESS,SELF) &
                             0xfffff000;
    size = GTREGREAD(PCI_0CS_BOOT_BANK_SIZE);
    if(size)
        pciSelfBars->CSBootSize = size + 0x1000;
    else
        pciSelfBars->CSBootSize = 0;
    pciSelfBars->internalMemBase = pciReadConfigReg(1,
        PCI_INTERNAL_REGISTERS_MEMORY_MAPPED_BASE_ADDRESS,SELF) &
                             0xfffff000;
    pciSelfBars->internalIOBase = pciReadConfigReg(1,
        PCI_INTERNAL_REGISTERS_I_OMAPPED_BASE_ADDRESS,SELF) &
                             0xfffff000;
    pciSelfBars->P2PMem0Base = pciReadConfigReg(1,PCI_P2P_MEM0_BASE_ADDRESS,SELF) &
                             0xfffff000;
    size = GTREGREAD(PCI_0P2P_MEM0_BAR_SIZE);
    if(size)
        pciSelfBars->P2PMem0Size = size + 0x1000;
    else
        pciSelfBars->P2PMem0Size = 0;
    pciSelfBars->P2PMem1Base = pciReadConfigReg(1,PCI_P2P_MEM1_BASE_ADDRESS,SELF) &
                             0xfffff000;
    size = GTREGREAD(PCI_0P2P_MEM1_BAR_SIZE);
    if(size)
        pciSelfBars->P2PMem1Size = size + 0x1000;
    else
        pciSelfBars->P2PMem1Size = 0;
    pciSelfBars->P2PIOBase = pciReadConfigReg(1,PCI_P2P_I_O_BASE_ADDRESS,SELF) &
                             0xfffff000;
    size = GTREGREAD(PCI_0P2P_I_O_BAR_SIZE);
    if(size)
        pciSelfBars->P2PIOSize = size + 0x1000;
    else
        pciSelfBars->P2PIOSize = 0;
    pciSelfBars->CPUBase = pciReadConfigReg(1,PCI_CPU_BASE_ADDRESS,SELF) &
                             0xfffff000;
    size = GTREGREAD(PCI_0CPU_BAR_SIZE);
    if(size)
        pciSelfBars->CPUSize = size + 0x1000;
    else
        pciSelfBars->CPUSize = 0;
}
#endif

/********************************************************************
* pciGetRegOffset - Gets the register offset for this region config.
*
* INPUT:   Bus, Region - The bus and region we ask for its base address.
* OUTPUT:   N/A  
* RETURNS: PCI register base address
*********************************************************************/
static unsigned int pciGetRegOffset(PCI_HOST host, PCI_REGION region)
{
    switch (host) 
    {
	case PCI_HOST0:
	    switch(region) {
		case PCI_IO:		return PCI_0I_O_LOW_DECODE_ADDRESS;
		case PCI_REGION0:	return PCI_0MEMORY0_LOW_DECODE_ADDRESS;
		case PCI_REGION1:	return PCI_0MEMORY1_LOW_DECODE_ADDRESS;
		case PCI_REGION2:	return PCI_0MEMORY2_LOW_DECODE_ADDRESS;
		case PCI_REGION3:	return PCI_0MEMORY3_LOW_DECODE_ADDRESS;
	    }
	case PCI_HOST1:
	    switch(region) {
		case PCI_IO:		return PCI_1I_O_LOW_DECODE_ADDRESS;
		case PCI_REGION0:	return PCI_1MEMORY0_LOW_DECODE_ADDRESS;
		case PCI_REGION1:	return PCI_1MEMORY1_LOW_DECODE_ADDRESS;
		case PCI_REGION2:	return PCI_1MEMORY2_LOW_DECODE_ADDRESS;
		case PCI_REGION3:	return PCI_1MEMORY3_LOW_DECODE_ADDRESS;
	    }
    }
    return PCI_0MEMORY0_LOW_DECODE_ADDRESS;
}

static unsigned int pciGetRemapOffset(PCI_HOST host, PCI_REGION region)
{
    switch (host) 
    {
	case PCI_HOST0:
	    switch(region) {
		case PCI_IO:		return PCI_0I_O_ADDRESS_REMAP;
		case PCI_REGION0:	return PCI_0MEMORY0_ADDRESS_REMAP;
		case PCI_REGION1:	return PCI_0MEMORY1_ADDRESS_REMAP;
		case PCI_REGION2:	return PCI_0MEMORY2_ADDRESS_REMAP;
		case PCI_REGION3:	return PCI_0MEMORY3_ADDRESS_REMAP;
	    }
	case PCI_HOST1:
	    switch(region) {
		case PCI_IO:		return PCI_1I_O_ADDRESS_REMAP;
		case PCI_REGION0:	return PCI_1MEMORY0_ADDRESS_REMAP;
		case PCI_REGION1:	return PCI_1MEMORY1_ADDRESS_REMAP;
		case PCI_REGION2:	return PCI_1MEMORY2_ADDRESS_REMAP;
		case PCI_REGION3:	return PCI_1MEMORY3_ADDRESS_REMAP;
	    }
    }
    return PCI_0MEMORY0_ADDRESS_REMAP;
}

/********************************************************************
* pciGetBaseAddress - Gets the base address of a PCI.
*           - If the PCI size is 0 then this base address has no meaning!!!
*
*
* INPUT:   Bus, Region - The bus and region we ask for its base address.
* OUTPUT:   N/A  
* RETURNS: PCI base address.
*********************************************************************/
unsigned int pciGetBaseAddress(PCI_HOST host, PCI_REGION region)
{
    unsigned int regBase;
    unsigned int regEnd;
    unsigned int regOffset=pciGetRegOffset(host, region);

    GT_REG_READ(regOffset, &regBase);
    GT_REG_READ(regOffset+8, &regEnd);

    if(regEnd<=regBase) return 0xffffffff;	/* ERROR !!! */

    regBase = regBase << 20;
    return regBase;
}

bool pciMapSpace(PCI_HOST host, PCI_REGION region, unsigned int remapBase, unsigned int bankBase,unsigned int bankLength) 
{
    unsigned int low=0xfff;
    unsigned int high=0x0;
    unsigned int regOffset=pciGetRegOffset(host, region);
    unsigned int remapOffset=pciGetRemapOffset(host, region);

    if(bankLength!=0) {
	low = (bankBase >> 20) & 0xfff;
	high=((bankBase+bankLength)>>20)-1;
    }

    GT_REG_WRITE(regOffset, low | (1<<24));	/* no swapping */
    GT_REG_WRITE(regOffset+8, high);

    if(bankLength!=0) {	/* must do AFTER writing maps */
	GT_REG_WRITE(remapOffset, remapBase>>20);	/* sorry, 32 bits only.
							   dont support upper 32
							   in this driver */
    }
    return true;
}

unsigned int pciGetSpaceBase(PCI_HOST host, PCI_REGION region)
{
    unsigned int low;
    unsigned int regOffset=pciGetRegOffset(host, region);
    GT_REG_READ(regOffset,&low);
    return (low&0xfff)<<20;
}

unsigned int pciGetSpaceSize(PCI_HOST host, PCI_REGION region)
{
    unsigned int low,high;
    unsigned int regOffset=pciGetRegOffset(host, region);
    GT_REG_READ(regOffset,&low);
    GT_REG_READ(regOffset+8,&high);
    high&=0xfff;
    low&=0xfff;
    if(high<=low) return 0;
    return (high+1-low)<<20;
}

/********************************************************************
* pciMapMemoryBank - Maps PCI_host memory bank "bank" for the slave.
*
* Inputs: base and size of PCI SCS
*********************************************************************/
void pciMapMemoryBank(PCI_HOST host, MEMORY_BANK bank, unsigned int pciDramBase,unsigned int pciDramSize)
{
  	pciDramBase = pciDramBase & 0xfffff000;
    pciDramBase = pciDramBase | (pciReadConfigReg(host,
        PCI_SCS_0_BASE_ADDRESS + 4*bank,SELF) & 0x00000fff);
    pciWriteConfigReg(host,PCI_SCS_0_BASE_ADDRESS + 4*bank,SELF,pciDramBase);
    if(pciDramSize == 0) 
        pciDramSize ++;
    GT_REG_WRITE(pci_scs_bank_size[host][bank], pciDramSize-1);
}

#if 0 // FIXME eventually - this is crap
/********************************************************************
* pci0MapInternalRegSpace - Maps the internal registers memory space for the 
*                           slave.
*
* Inputs: base of pci0 internal register
*********************************************************************/
void pci0MapInternalRegSpace(unsigned int pci0InternalBase)
{
  	pci0InternalBase = pci0InternalBase & 0xfffff000;
    pci0InternalBase = pci0InternalBase | (pciReadConfigReg(0,
        PCI_INTERNAL_REGISTERS_MEMORY_MAPPED_BASE_ADDRESS,SELF) & 0x00000fff);
    pciWriteConfigReg(0,PCI_INTERNAL_REGISTERS_MEMORY_MAPPED_BASE_ADDRESS,SELF,
                       pci0InternalBase);
}

/********************************************************************
* pci1MapInternalRegSpace - Maps the internal registers memory space for the 
*                           slave.
*
* Inputs: base of pci1 internal register
*********************************************************************/
void pci1MapInternalRegSpace(unsigned int pci1InternalBase)
{
  	pci1InternalBase = pci1InternalBase & 0xfffff000;
    pci1InternalBase = pci1InternalBase | (pciReadConfigReg(1,
        PCI_INTERNAL_REGISTERS_MEMORY_MAPPED_BASE_ADDRESS,SELF) & 0x00000fff);
    pciWriteConfigReg(1,PCI_INTERNAL_REGISTERS_MEMORY_MAPPED_BASE_ADDRESS,SELF,
                       pci1InternalBase);
}

/********************************************************************
* pci0MapInternalRegIOSpace - Maps the internal registers IO space for the 
*                             slave.
*
* Inputs: base of pci0 internal io register
*********************************************************************/
void pci0MapInternalRegIOSpace(unsigned int pci0InternalBase)
{
  	pci0InternalBase = pci0InternalBase & 0xfffff000;
    pci0InternalBase = pci0InternalBase | (pciReadConfigReg(0,
        PCI_INTERNAL_REGISTERS_I_OMAPPED_BASE_ADDRESS,SELF) & 0x00000fff);
    pciWriteConfigReg(0,PCI_INTERNAL_REGISTERS_I_OMAPPED_BASE_ADDRESS,SELF,
                       pci0InternalBase);  
}

/********************************************************************
* pci1MapInternalRegIOSpace - Maps the internal registers IO space for the 
*                             slave.
* Inputs: base of pci1 internal io register
*********************************************************************/
void pci1MapInternalRegIOSpace(unsigned int pci1InternalBase)
{
  	pci1InternalBase = pci1InternalBase & 0xfffff000;
    pci1InternalBase = pci1InternalBase | (pciReadConfigReg(1,
        PCI_INTERNAL_REGISTERS_I_OMAPPED_BASE_ADDRESS,SELF) & 0x00000fff);
    pciWriteConfigReg(1,PCI_INTERNAL_REGISTERS_I_OMAPPED_BASE_ADDRESS,SELF,
                       pci1InternalBase);
}

/********************************************************************
* pci0MapDevice0MemorySpace - Maps PCI0 device0 memory spaces for the slave.
*
* Inputs: base and length of pci0 device0
*********************************************************************/
void pci0MapDevice0MemorySpace(unsigned int pci0Dev0Base,
                               unsigned int pci0Dev0Length)
{
  	pci0Dev0Base = pci0Dev0Base & 0xfffff000;
    pci0Dev0Base = pci0Dev0Base | (pciReadConfigReg(0,
        PCI_CS_0_BASE_ADDRESS,SELF) & 0x00000fff);
    pciWriteConfigReg(0,PCI_CS_0_BASE_ADDRESS,SELF,pci0Dev0Base );
    if(pci0Dev0Length == 0) 
        pci0Dev0Length++;
  	GT_REG_WRITE(PCI_0CS_0_BANK_SIZE,pci0Dev0Length-1);
}

/********************************************************************
* pci0MapDevice1MemorySpace - Maps PCI0 device1 memory spaces for the slave.
*
* Inputs: base and length of pci0 device1
*********************************************************************/
void pci0MapDevice1MemorySpace(unsigned int pci0Dev1Base,
                               unsigned int pci0Dev1Length)
{
  	pci0Dev1Base = pci0Dev1Base & 0xfffff000;
    pci0Dev1Base = pci0Dev1Base | (pciReadConfigReg(0,
        PCI_CS_1_BASE_ADDRESS,SELF) & 0x00000fff);
    pciWriteConfigReg(0,PCI_CS_1_BASE_ADDRESS,SELF,pci0Dev1Base );
    if(pci0Dev1Length == 0) 
        pci0Dev1Length++;
  	GT_REG_WRITE(PCI_0CS_1_BANK_SIZE,pci0Dev1Length-1);
}

/********************************************************************
* pci0MapDevice2MemorySpace - Maps PCI0 device2 memory spaces for the slave.
*
* Inputs: base and length of pci0 device1
*********************************************************************/
void pci0MapDevice2MemorySpace(unsigned int pci0Dev2Base,
                               unsigned int pci0Dev2Length)
{
  	pci0Dev2Base = pci0Dev2Base & 0xfffff000;
    pci0Dev2Base = pci0Dev2Base | (pciReadConfigReg(0,
        PCI_CS_2_BASE_ADDRESS,SELF) & 0x00000fff);
    pciWriteConfigReg(0,PCI_CS_2_BASE_ADDRESS,SELF,pci0Dev2Base );
    if(pci0Dev2Length == 0) 
        pci0Dev2Length++;
  	GT_REG_WRITE(PCI_0CS_2_BANK_SIZE,pci0Dev2Length-1);
}

/********************************************************************
* pci1MapDevice0MemorySpace - Maps PCI1 device0 memory spaces for the slave.
*
* Inputs: base and length of pci1 device0
*********************************************************************/
void pci1MapDevice0MemorySpace(unsigned int pci1Dev0Base,
                               unsigned int pci1Dev0Length)
{
  	pci1Dev0Base = pci1Dev0Base & 0xfffff000;
    pci1Dev0Base = pci1Dev0Base | (pciReadConfigReg(1,
        PCI_CS_0_BASE_ADDRESS,SELF) & 0x00000fff);
    pciWriteConfigReg(1,PCI_CS_0_BASE_ADDRESS,SELF,pci1Dev0Base );
    if(pci1Dev0Length == 0) 
        pci1Dev0Length++;
  	GT_REG_WRITE(PCI_1CS_0_BANK_SIZE,pci1Dev0Length-1);
}

/********************************************************************
* pci1MapDevice1MemorySpace - Maps PCI1 device1 memory spaces for the slave.
*
* Inputs: base and lengthof pci1 device1
*********************************************************************/
void pci1MapDevice1MemorySpace(unsigned int pci1Dev1Base,
                               unsigned int pci1Dev1Length)
{
  	pci1Dev1Base = pci1Dev1Base & 0xfffff000;
    pci1Dev1Base = pci1Dev1Base | (pciReadConfigReg(1,
        PCI_CS_1_BASE_ADDRESS,SELF) & 0x00000fff);
    pciWriteConfigReg(1,PCI_CS_1_BASE_ADDRESS,SELF,pci1Dev1Base);
    if(pci1Dev1Length == 0) 
        pci1Dev1Length++;
  	GT_REG_WRITE(PCI_1CS_1_BANK_SIZE,pci1Dev1Length-1);
}

/********************************************************************
* pci1MapDevice2MemorySpace - Maps PCI1 device2 memory spaces for the slave.
*
* Inputs: base and length of pci1 device2
*********************************************************************/
void pci1MapDevice2MemorySpace(unsigned int pci1Dev2Base,
                               unsigned int pci1Dev2Length)
{
  	pci1Dev2Base = pci1Dev2Base & 0xfffff000;
    pci1Dev2Base = pci1Dev2Base | (pciReadConfigReg(1,
        PCI_CS_2_BASE_ADDRESS,SELF) & 0x00000fff);
    pciWriteConfigReg(1,PCI_CS_2_BASE_ADDRESS,SELF,pci1Dev2Base);
    if(pci1Dev2Length == 0) 
        pci1Dev2Length++;
  	GT_REG_WRITE(PCI_1CS_2_BANK_SIZE,pci1Dev2Length-1);
}

/********************************************************************
* pci0MapDevice3MemorySpace - Maps PCI0 devices 3 spaces for the slave.
*
* Inputs: base and length of pci0 device3.
*********************************************************************/
void pci0MapDevice3MemorySpace(unsigned int pci0Dev3Base,
                               unsigned int pci0Dev3Length)
{
  	pci0Dev3Base = pci0Dev3Base & 0xfffff000;
    pci0Dev3Base = pci0Dev3Base | (pciReadConfigReg(0,
        PCI_CS_3_BASE_ADDRESS,SELF) & 0x00000fff);
    pciWriteConfigReg(0,PCI_CS_3_BASE_ADDRESS,SELF,pci0Dev3Base );
  	if(pci0Dev3Length == 0) 
        pci0Dev3Length++;
    GT_REG_WRITE(PCI_0CS_3_BANK_SIZE , pci0Dev3Length-1);
}

/********************************************************************
* pci0MapBootDeviceMemorySpace - Maps PCI0 boot memory spaces for the slave.
*
* Inputs: base and length of pci0 boot.
*********************************************************************/
void pci0MapBootDeviceMemorySpace(unsigned int pci0DevBootBase,
                                  unsigned int pci0DevBootLength)
{
  	pci0DevBootBase = pci0DevBootBase & 0xfffff000;
    pci0DevBootBase = pci0DevBootBase | (pciReadConfigReg(0,
        PCI_BOOTCS_BASE_ADDRESS,SELF) & 0x00000fff);
    pciWriteConfigReg(0,PCI_BOOTCS_BASE_ADDRESS,SELF,pci0DevBootBase );
  	if(pci0DevBootLength == 0) 
        pci0DevBootLength++;
    GT_REG_WRITE(PCI_0CS_BOOT_BANK_SIZE , pci0DevBootLength-1);
}

/********************************************************************
* pci1MapDevice3MemorySpace - Maps PCI1 devices 3 spaces for the slave.
*
* Inputs: base and length of pci1 device3.
*********************************************************************/
void pci1MapDevice3MemorySpace(unsigned int pci1Dev3Base,
                               unsigned int pci1Dev3Length)
{
  	pci1Dev3Base = pci1Dev3Base & 0xfffff000;
    pci1Dev3Base = pci1Dev3Base | (pciReadConfigReg(1,
        PCI_CS_3_BASE_ADDRESS,SELF) & 0x00000fff);
    pciWriteConfigReg(1,PCI_CS_3_BASE_ADDRESS,SELF,pci1Dev3Base );
  	if(pci1Dev3Length == 0) 
        pci1Dev3Length++;
    GT_REG_WRITE(PCI_1CS_3_BANK_SIZE , pci1Dev3Length-1);
}

/********************************************************************
* pci1MapBootDeviceMemorySpace - Maps PCI1 boot memory spaces for the slave.
*
* Inputs: base and length of pci1 boot.
*********************************************************************/
void pci1MapBootDeviceMemorySpace(unsigned int pci1DevBootBase,
                                  unsigned int pci1DevBootLength)
{
  	pci1DevBootBase = pci1DevBootBase & 0xfffff000;
    pci1DevBootBase = pci1DevBootBase | (pciReadConfigReg(1,
        PCI_BOOTCS_BASE_ADDRESS,SELF) & 0x00000fff);
    pciWriteConfigReg(1,PCI_BOOTCS_BASE_ADDRESS,SELF,pci1DevBootBase );
  	if(pci1DevBootLength == 0) 
        pci1DevBootLength++;
    GT_REG_WRITE(PCI_1CS_BOOT_BANK_SIZE , pci1DevBootLength-1);
}

/********************************************************************
* pci0p2pMem0 - Maps PCI0 pci2pci memory0 spaces for the slave.
*
* Inputs: base and length of pci0P2pMem0 
*********************************************************************/
void pci0MapP2pMem0Space(unsigned int pci0P2pMem0Base, 
                         unsigned int pci0P2pMem0Length)

{
    pci0P2pMem0Base = pci0P2pMem0Base & 0xfffff000;
    pci0P2pMem0Base = pci0P2pMem0Base | (pciReadConfigReg(0,
        PCI_P2P_MEM0_BASE_ADDRESS,SELF) & 0x00000fff);
    pciWriteConfigReg(0,PCI_P2P_MEM0_BASE_ADDRESS,SELF,pci0P2pMem0Base);
    if(pci0P2pMem0Length == 0) 
        pci0P2pMem0Length++;
    GT_REG_WRITE(PCI_0P2P_MEM0_BAR_SIZE , pci0P2pMem0Length-1);
}

/********************************************************************
* pci1p2pMem0 - Maps PCI1 pci2pci memory0 spaces for the slave.
*
* Inputs: base and length of pci1P2pMem0
*********************************************************************/
void pci1MapP2pMem0Space(unsigned int pci1P2pMem0Base,
                            unsigned int pci1P2pMem0Length)
{
    pci1P2pMem0Base = pci1P2pMem0Base & 0xfffff000;
    pci1P2pMem0Base = pci1P2pMem0Base | (pciReadConfigReg(1,
        PCI_P2P_MEM0_BASE_ADDRESS,SELF) & 0x00000fff);
    pciWriteConfigReg(1,PCI_P2P_MEM0_BASE_ADDRESS,SELF,pci1P2pMem0Base);
    if(pci1P2pMem0Length == 0) 
        pci1P2pMem0Length++;
    GT_REG_WRITE(PCI_1P2P_MEM0_BAR_SIZE , pci1P2pMem0Length-1);
}

/********************************************************************
* pci0p2pMem1 - Maps PCI0 pci2pci memory1 spaces for the slave.
*
* Inputs: base and length of pci0P2pMem1 
*********************************************************************/
void pci0MapP2pMem1Space(unsigned int pci0P2pMem1Base, 
                         unsigned int pci0P2pMem1Length)
{
    pci0P2pMem1Base = pci0P2pMem1Base & 0xfffff000;
    pci0P2pMem1Base = pci0P2pMem1Base | (pciReadConfigReg(0,
        PCI_P2P_MEM1_BASE_ADDRESS,SELF) & 0x00000fff);
    pciWriteConfigReg(0,PCI_P2P_MEM1_BASE_ADDRESS,SELF,pci0P2pMem1Base );
    if(pci0P2pMem1Length == 0) 
        pci0P2pMem1Length++;
    GT_REG_WRITE(PCI_0P2P_MEM1_BAR_SIZE , pci0P2pMem1Length-1);
}

/********************************************************************
* pci1p2pMem1 - Maps PCI1 pci2pci memory1 spaces for the slave.
*
* Inputs: base and length of pci1P2pMem1 
*********************************************************************/
void pci1MapP2pMem1Space(unsigned int pci1P2pMem1Base, 
                         unsigned int pci1P2pMem1Length)
{
    pci1P2pMem1Base = pci1P2pMem1Base & 0xfffff000;
    pci1P2pMem1Base = pci1P2pMem1Base | (pciReadConfigReg(1,
        PCI_P2P_MEM1_BASE_ADDRESS,SELF) & 0x00000fff);
    pciWriteConfigReg(1,PCI_P2P_MEM1_BASE_ADDRESS,SELF,pci1P2pMem1Base );
    if(pci1P2pMem1Length == 0) 
        pci1P2pMem1Length++;
    GT_REG_WRITE(PCI_1P2P_MEM1_BAR_SIZE , pci1P2pMem1Length-1);
}

/********************************************************************
* pci0p2pIO - Maps PCI0 pci2pci io spaces for the slave.
*
* Inputs: base and length of pci0P2pIo 
*********************************************************************/
void    pci0MapP2pIoSpace(unsigned int pci0P2pIoBase, 
                          unsigned int pci0P2pIoLength)
{
    pci0P2pIoBase = pci0P2pIoBase & 0xfffff000;
    pci0P2pIoBase = pci0P2pIoBase | (pciReadConfigReg(0,
        PCI_P2P_MEM0_BASE_ADDRESS,SELF) & 0x00000fff);
    pciWriteConfigReg(0,PCI_P2P_I_O_BASE_ADDRESS,SELF,pci0P2pIoBase );
    if(pci0P2pIoLength == 0) 
        pci0P2pIoLength++;
    GT_REG_WRITE(PCI_0P2P_I_O_BAR_SIZE , pci0P2pIoLength-1);
}

/********************************************************************
* pci1p2pIO - Maps PCI1 pci2pci io spaces for the slave.
*
* Inputs: base and length of pci1P2pIo 
*********************************************************************/
void pci1MapP2pIoSpace(unsigned int pci1P2pIoBase, unsigned int pci1P2pIoLength)

{
    pci1P2pIoBase = pci1P2pIoBase & 0xfffff000;
    pci1P2pIoBase = pci1P2pIoBase | (pciReadConfigReg(1,
        PCI_P2P_I_O_BASE_ADDRESS,SELF) & 0x00000fff);
    pciWriteConfigReg(1,PCI_P2P_I_O_BASE_ADDRESS,SELF,pci1P2pIoBase);
    if(pci1P2pIoLength == 0) 
        pci1P2pIoLength++;
    GT_REG_WRITE(PCI_1P2P_I_O_BAR_SIZE , pci1P2pIoLength-1);
}

/********************************************************************
* pci0CPU - Maps PCI0 cpu spaces for the slave.
*
* Inputs: base and length of pci0Cpu 
*********************************************************************/
void pci0MapCPUspace(unsigned int pci0CpuBase, unsigned int pci0CpuLengs)
{
    pci0CpuBase = pci0CpuBase & 0xfffff000;
    pci0CpuBase = pci0CpuBase | (pciReadConfigReg(0,
        PCI_CPU_BASE_ADDRESS,SELF) & 0x00000fff);
    pciWriteConfigReg(0,PCI_CPU_BASE_ADDRESS,SELF,pci0CpuBase );
    if(pci0CpuLengs == 0) 
        pci0CpuLengs++;
    GT_REG_WRITE(PCI_0CPU_BAR_SIZE , pci0CpuLengs-1);
}

/********************************************************************
* pci1CPU - Maps PCI1 cpu spaces for the slave.
*
* Inputs: base and length of pci1Cpu 
*********************************************************************/
void pci1MapCPUspace(unsigned int pci1CpuBase, unsigned int pci1CpuLengs)
{
    pci1CpuBase = pci1CpuBase & 0xfffff000;
    pci1CpuBase = pci1CpuBase | (pciReadConfigReg(1,
        PCI_CPU_BASE_ADDRESS,SELF) & 0x00000fff);
    pciWriteConfigReg(1,PCI_CPU_BASE_ADDRESS,SELF,pci1CpuBase );
    if(pci1CpuLengs == 0) 
        pci1CpuLengs++;
    GT_REG_WRITE(PCI_1CPU_BAR_SIZE , pci1CpuLengs-1);
}
#endif


/********************************************************************
* pciSetRegionFeatures - This function modifys one of the 8 regions with 
*                         feature bits given as an input.
*                       - Be advised to check the spec before modifying them.
* Inputs: PCI_PROTECT_REGION region - one of the eight regions.
*         unsigned int features - See file: pci.h there are defintion for those
*                                 region features.
*         unsigned int baseAddress - The region base Address.
*         unsigned int topAddress - The region top Address.
* Returns: false if one of the parameters is erroneous true otherwise.
*********************************************************************/
bool pciSetRegionFeatures(PCI_HOST host, PCI_ACCESS_REGIONS region,unsigned int features, 
                           unsigned int baseAddress,unsigned int regionLength)
{
    unsigned int accessLow;
    unsigned int accessHigh;
    unsigned int accessTop = baseAddress + regionLength;

    if(regionLength == 0) /* close the region. */
    {
        pciDisableAccessRegion(host, region);
        return true;
    }
    /* base Address is store is bits [11:0] */
    accessLow = (baseAddress & 0xfff00000) >> 20;
    /* All the features are update according to the defines in pci.h (to be on
       the safe side we disable bits: [11:0] */
    accessLow = accessLow | (features & 0xfffff000);
    /* write to the Low Access Region register */
    GT_REG_WRITE( pci_access_control_base_0_low[host] + 0x10*region,accessLow);

    accessHigh = (accessTop & 0xfff00000) >> 20;

    /* write to the High Access Region register */
    GT_REG_WRITE(pci_access_control_top_0[host] + 0x10*region,accessHigh - 1);
    return true;
}

/********************************************************************
* pciDisableAccessRegion - Disable The given Region by writing MAX size
*                           to its low Address and MIN size to its high Address. 
*
* Inputs:   PCI_ACCESS_REGIONS region - The region we to be Disabled.
* Returns:  N/A.
*********************************************************************/
void pciDisableAccessRegion(PCI_HOST host, PCI_ACCESS_REGIONS region)
{
    /* writing back the registers default values. */
    GT_REG_WRITE(pci_access_control_base_0_low[host] + 0x10*region,0x01001fff);
    GT_REG_WRITE(pci_access_control_top_0[host] + 0x10*region,0);
}

/********************************************************************
* pciArbiterEnable - Enables PCI-0`s Arbitration mechanism.
*
* Inputs:   N/A
* Returns:  true.
*********************************************************************/
bool pciArbiterEnable(PCI_HOST host)
{
    unsigned int regData;

    GT_REG_READ(pci_arbiter_control[host],&regData);
    GT_REG_WRITE(pci_arbiter_control[host],regData | BIT31);
    return true;
}

/********************************************************************
* pciArbiterDisable - Disable PCI-0`s Arbitration mechanism.
*
* Inputs:   N/A
* Returns:  true
*********************************************************************/
bool pciArbiterDisable(PCI_HOST host)
{
    unsigned int regData;

    GT_REG_READ(pci_arbiter_control[host],&regData);
    GT_REG_WRITE(pci_arbiter_control[host],regData & 0x7fffffff);
    return true;
}

/********************************************************************
* pciSetArbiterAgentsPriority - Priority setup for the PCI agents (Hi or Low)   
*
* Inputs:   PCI_AGENT_PRIO internalAgent - priotity for internal agent.
*           PCI_AGENT_PRIO externalAgent0 - priotity for external#0 agent.
*           PCI_AGENT_PRIO externalAgent1 - priotity for external#1 agent.
*           PCI_AGENT_PRIO externalAgent2 - priotity for external#2 agent.
*           PCI_AGENT_PRIO externalAgent3 - priotity for external#3 agent.
*           PCI_AGENT_PRIO externalAgent4 - priotity for external#4 agent.
*           PCI_AGENT_PRIO externalAgent5 - priotity for external#5 agent.
* Returns:  true
*********************************************************************/
bool pciSetArbiterAgentsPriority(PCI_HOST host, PCI_AGENT_PRIO internalAgent,
                                  PCI_AGENT_PRIO externalAgent0,
                                  PCI_AGENT_PRIO externalAgent1,
                                  PCI_AGENT_PRIO externalAgent2,
                                  PCI_AGENT_PRIO externalAgent3,
                                  PCI_AGENT_PRIO externalAgent4,
                                  PCI_AGENT_PRIO externalAgent5)
{
    unsigned int regData;
    unsigned int writeData;

    GT_REG_READ(pci_arbiter_control[host],&regData);
    writeData = (internalAgent << 7) + (externalAgent0 << 8) +      
                (externalAgent1 << 9) + (externalAgent2 << 10) +    
                (externalAgent3 << 11) + (externalAgent4 << 12) +   
                (externalAgent5 << 13);
    regData = (regData & 0xffffc07f) | writeData;
    GT_REG_WRITE(pci_arbiter_control[host],regData & regData);
    return true;
}

/********************************************************************
* pciParkingDisable - Park on last option disable, with this function you can
*                      disable the park on last mechanism for each agent.
*                      disabling this option for all agents results parking
*                      on the internal master.    
*
* Inputs: PCI_AGENT_PARK internalAgent -  parking Disable for internal agent.
*         PCI_AGENT_PARK externalAgent0 - parking Disable for external#0 agent.
*         PCI_AGENT_PARK externalAgent1 - parking Disable for external#1 agent.
*         PCI_AGENT_PARK externalAgent2 - parking Disable for external#2 agent.
*         PCI_AGENT_PARK externalAgent3 - parking Disable for external#3 agent.
*         PCI_AGENT_PARK externalAgent4 - parking Disable for external#4 agent.
*         PCI_AGENT_PARK externalAgent5 - parking Disable for external#5 agent.
* Returns:  true 
*********************************************************************/
bool pciParkingDisable(PCI_HOST host, PCI_AGENT_PARK internalAgent,
                        PCI_AGENT_PARK externalAgent0,
                        PCI_AGENT_PARK externalAgent1,
                        PCI_AGENT_PARK externalAgent2,
                        PCI_AGENT_PARK externalAgent3,
                        PCI_AGENT_PARK externalAgent4,
                        PCI_AGENT_PARK externalAgent5)
{
    unsigned int regData;
    unsigned int writeData;

    GT_REG_READ(pci_arbiter_control[host],&regData);
    writeData = (internalAgent << 14) + (externalAgent0 << 15) +     \
                (externalAgent1 << 16) + (externalAgent2 << 17) +    \
                (externalAgent3 << 18) + (externalAgent4 << 19) +    \
                (externalAgent5 << 20);
    regData = (regData & ~(0x7f<<14)) | writeData;
    GT_REG_WRITE(pci_arbiter_control[host],regData);
    return true;
}

/********************************************************************
* pciEnableBrokenAgentDetection - A master is said to be broken if it fails to
*                       respond to grant assertion within a window specified in
*                       the input value: 'brokenValue'. 
*
* Inputs: unsigned char brokenValue -  A value which limits the Master to hold the
*                       grant without asserting frame.
* Returns:  Error for illegal broken value otherwise true. 
*********************************************************************/
bool pciEnableBrokenAgentDetection(PCI_HOST host, unsigned char brokenValue)
{
    unsigned int data;
    unsigned int regData;

    if (brokenValue > 0xf) 
        return false; /* brokenValue must be 4 bit */
    data = brokenValue << 3;
    GT_REG_READ(pci_arbiter_control[host],&regData);
    regData = (regData & 0xffffff87) | data;
    GT_REG_WRITE(pci_arbiter_control[host],regData | BIT1);
    return true;
}

/********************************************************************
* pciDisableBrokenAgentDetection - This function disable the Broken agent
*                           Detection mechanism.
*                           NOTE: This operation may cause a dead lock on the 
*                           pci0 arbitration.
*
* Inputs:   N/A
* Returns:  true. 
*********************************************************************/
bool pciDisableBrokenAgentDetection(PCI_HOST host)
{
    unsigned int regData;

    GT_REG_READ(pci_arbiter_control[host],&regData);
    regData = regData & 0xfffffffd;
    GT_REG_WRITE(pci_arbiter_control[host],regData);
    return true;
}

/********************************************************************
* pciP2PConfig - This function set the PCI_n P2P configurate.
*                 For more information on the P2P read PCI spec.  
*
* Inputs:  unsigned int SecondBusLow - Secondery PCI interface Bus Range Lower
*                                      Boundry.
*          unsigned int SecondBusHigh - Secondry PCI interface Bus Range upper
*                                      Boundry.         
*          unsigned int busNum - The CPI bus number to which the PCI interface
*                                      is connected.
*          unsigned int devNum - The PCI interface's device number.
*   
* Returns:  true. 
*********************************************************************/
bool pciP2PConfig(PCI_HOST host, unsigned int SecondBusLow,
		  unsigned int SecondBusHigh,
                  unsigned int busNum,unsigned int devNum)
{
    unsigned int regData;

    regData = (SecondBusLow & 0xff) | ((SecondBusHigh & 0xff) << 8) |
        ((busNum & 0xff) << 16) | ((devNum & 0x1f) << 24);
    GT_REG_WRITE(pci_p2p_configuration[host],regData);
    return true;
}

/********************************************************************
* pciSetRegionSnoopMode - This function modifys one of the 4 regions which   
*                          supports Cache Coherency in the PCI_n interface.
* Inputs: region - One of the four regions.
*         snoopType - There is four optional Types:
*                        1. No Snoop.
*                        2. Snoop to WT region.
*                        3. Snoop to WB region.
*                        4. Snoop & Invalidate to WB region.
*         baseAddress - Base Address of this region.
*         regionLength - Region length.
* Returns: false if one of the parameters is wrong otherwise return true.
*********************************************************************/
bool pciSetRegionSnoopMode(PCI_HOST host, PCI_SNOOP_REGION region,PCI_SNOOP_TYPE snoopType,
                            unsigned int baseAddress,
                            unsigned int regionLength)
{
    unsigned int snoopXbaseAddress;
    unsigned int snoopXtopAddress;
    unsigned int data;
    unsigned int snoopHigh = baseAddress + regionLength;

    if( (region > PCI_SNOOP_REGION3) || (snoopType > PCI_SNOOP_WB) )
        return false;
    snoopXbaseAddress = pci_snoop_control_base_0_low[host] + 0x10 * region;
    snoopXtopAddress = pci_snoop_control_top_0[host] + 0x10 * region;
    if(regionLength == 0) /* closing the region */
    {
        GT_REG_WRITE(snoopXbaseAddress,0x0000ffff);
        GT_REG_WRITE(snoopXtopAddress,0);
        return true;
    }
    baseAddress = baseAddress & 0xfff00000; /* Granularity of 1MByte */
    data = (baseAddress >> 20) | snoopType << 12;
    GT_REG_WRITE(snoopXbaseAddress,data); 
    snoopHigh = (snoopHigh & 0xfff00000) >> 20;
    GT_REG_WRITE(snoopXtopAddress,snoopHigh - 1);
    return true;
}

PCI_DEVICE *
pciFindDevice(unsigned short vendorID, unsigned short deviceID)
{
    int host;
    PCI_DEVICE *dev;
    for(host=PCI_HOST0;host<=PCI_HOST1;host++) {
	for(dev=pci_dev_list[host];dev->deviceNum!=-1;dev++) {
	    if(vendorID==dev->venID && deviceID==dev->deviceID) return dev;
	}
    }
    return NULL;
}

static const char * const pci_type_legend[]=
    {"32 bit","below 1M","64 bit", "reserved"};

static const char * const pci_timing_legend[]=
    {"Slow","Medium","Fast","???"};

static int pci_alloc_bar(PCI_DEVICE *dev, int index)
{
    PCI_BAR *bar=&dev->bar[index];
    unsigned int *alloc, reg=BAR0+index*4;

    if(!bar) {
	printf("error, no bar\n");
	return -1;
    }

    if (bar->type & BAR_SEL_MEM_IO) {
	alloc=&pci_io_bar_alloc[dev->host];
    } else {
	alloc=&pci_mem_bar_alloc[dev->host];
    }

    bar->base=((*alloc)+bar->size-1)&~(bar->size-1);
    pciWriteConfigReg(dev->host, reg, dev->deviceNum, bar->base);
    bar->base=pciReadConfigReg(dev->host, reg, dev->deviceNum)&(~1);
    *alloc=bar->base+bar->size;

    return 0;
}

#ifdef DEBUG
static char * smart_print(unsigned val, char *buf)
{
    char *unit="b";

    if(val>_1G) val/=_1G, unit="G";
    else if(val>_1M) val/=_1M, unit="M";
    else if(val>1024) val/=1024, unit="k";
    
    sprintf(buf, "%3d%s", val, unit);
    return buf;
}
#endif

static void setup_pci_devices(PCI_DEVICE *list)
{
    PCI_DEVICE *dev;
    int i,irq_pin;
    int command=0;

    for(dev=list;dev->deviceNum!=-1;dev++) {
	int statcmd = pciReadConfigReg(dev->host,PCI_STATUS_AND_COMMAND,
		dev->deviceNum);

	printf("%2d: %04x %04x (%s)\n", dev->deviceNum, dev->venID,
		dev->deviceID, dev->type);

	/* latency=64, cache linesize (in words), clear bist */
	pciWriteConfigReg(dev->host,
		PCI_BIST_HEADER_TYPE_LATENCY_TIMER_CACHE_LINE,
		dev->deviceNum, (0x04 << 8) | (CACHE_LINE_SIZE / 4));

	command = 0;
	if(dev->deviceNum!=0) {
	    statcmd &= ~(MASTER_ENABLE | MEMORY_ENABLE | I_O_ENABLE);
	    /* disable this device */
	    pciWriteConfigReg(dev->host,PCI_STATUS_AND_COMMAND,dev->deviceNum,
		    statcmd);

	}

	irq_pin=pciReadConfigReg(dev->host,PCI_INTERRUPT_PIN_AND_LINE, dev->deviceNum);
	if(((irq_pin>>8)&0xff)==1) {	/* only allow INT A */
	    unsigned char irq=pci_irq_swizzle[dev->host][dev->deviceNum];
	    if(irq) {
		irq_pin&=~0xff;
		irq_pin|=(irq|64);
		printf("  IRQ %d\n", irq);
		pciWriteConfigReg(dev->host,PCI_INTERRUPT_PIN_AND_LINE, dev->deviceNum, irq_pin);
	    }
	}

	/* for each BAR */
	for(i=0;i<5;i++) {
	    if(dev->bar[i].size) {
	        if(dev->deviceNum!=0) {
		    command |= MASTER_ENABLE;

		    if(dev->bar[i].type & BAR_SEL_MEM_IO) command |= I_O_ENABLE;
		    else  command |= MEMORY_ENABLE;

		    pci_alloc_bar(dev, i);
		}
#ifdef DEBUG
		{
		    char buf[10];
		    printf("  BAR %d: %08x %s %s, %s%s\n",i,
			dev->bar[i].base,
			smart_print(dev->bar[i].size, buf),
			dev->bar[i].type & BAR_SEL_MEM_IO ?"I/O":"MEM",
			pci_type_legend[(dev->bar[i].type&BAR_MEM_TYPE_MASK)>>1],
			dev->bar[i].type & BAR_PREFETCHABLE ?", prefetchable":""
		    );
		}
#endif
	    }
	}

	if(command) {
	    pciWriteConfigReg(dev->host,PCI_STATUS_AND_COMMAND,dev->deviceNum,
		    command | statcmd);
	    statcmd = pciReadConfigReg(dev->host,PCI_STATUS_AND_COMMAND,
		    dev->deviceNum);
	}

	printf("  %s%s%s%s%dMHz, %s%s timing\n",
		statcmd & (1<<0)?"I/O, ":"",
		statcmd & (1<<1)?"MEM, ":"",
		statcmd & (1<<2)?"Master, ":"",
		statcmd & (1<<20)?"Capability list, ":"",
		statcmd & (1<<21)?66:33,
		statcmd & (1<<23)?"Fast back to back capable, ":"",
		pci_timing_legend[(statcmd>>25)&3]);
    }
}

void
pci_init(bd_t *bd)
{
    PCI_HOST host;

    printf("PCI scan...\n");
    for(host=PCI_HOST0; host<=PCI_HOST1; host++) {
    	pciArbiterEnable(host);

	pciParkingDisable(host,1,1,1,1,1,1,1);
	memset(pci_dev_list[host],0,sizeof(pci_dev_list[host]));
	printf("PCI %d:\n",host);
	pciScanDevices(host,pci_dev_list[host],32);
	setup_pci_devices(pci_dev_list[host]);

	PCI_MEMORY_ENABLE(host, SELF);
    }
}
