/////////////////////////////////////////////////////////////////////////
// Switch settings:
// SW1 00000110 PCI boot
// SW2 01101111 Disable JTAG chain, 8144 enters debug after reset
// SW3 10010111 PCI (256M DDR)
// SW4 01100010
/////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include "mpc8560pci.h"

// Check which ADx pin is connected to IDSEL
#define	MSC8144_PCIDEVICENUM	21 
#define	MPC8560_PCIDEVICENUM	0

// PCI device id for MSC8144
#define MSC8144_PCIDEVID		0x1400	
#define MPC8560_PCIDEVID		0x0009	
#define MPC8560_PCIVENDORID		0x1057	
#define MSC8144_PCIVENDORID		0x1957	


#define READ_UINT8(data, arg)       data = (uint8_t)(arg)
#define READ_UINT16(data, arg)      data = (uint16_t)(arg)
#define READ_UINT32(data, arg)      data = (uint32_t)(arg)
#define GET_UINT8(arg)              (uint8_t)(arg)
#define GET_UINT16(arg)             (uint16_t)(arg)
#define GET_UINT32(arg)             (uint32_t)(arg)
#define WRITE_UINT8(arg, data)      arg = (uint8_t)(data)
#define WRITE_UINT16(arg, data)     arg = (uint16_t)(data)
#define WRITE_UINT32(arg, data)     arg = (uint32_t)(data)

uint32_t IMMR8144Local = 0x90000000; 
uint32_t OutboundLocal = 0x80000000;
uint32_t InboundLocal  = 0x00000000;

uint32_t IMMR8144PCI   = 0x30000000; 
uint32_t OutboundPCI   = 0xC0000000;
uint32_t InboundPCI    = 0xE0000000;

uint32_t PCI8144BASE   = 0x01F7A000;
		
//msc8144_pci_regs *msc8144 = (msc8144_pci_regs*)0x91F7A000;
msc8144_pci_regs *msc8144;
mpc8560_pci_regs *mpc8560 = (mpc8560_pci_regs*)0x40008000;


void setBCSR1();
uint32_t SwapLong(uint32_t);
uint16_t SwapWord(uint16_t);
uint32_t ConstructConfigWord (uint32_t ,uint32_t, uint32_t);
void setPIMMR(uint32_t);
void scanDevices();
uint32_t getWindowSize(uint32_t, uint32_t, uint32_t);
void setOutbound1(uint32_t, uint32_t, uint32_t);
void setOutbound2(uint32_t, uint32_t, uint32_t);


uint32_t calcWindowSize(uint32_t);
void writePCIConfigReg(uint32_t, uint32_t, uint32_t, uint32_t);
void setPCIConfigReg32(uint32_t, uint32_t, uint32_t, uint32_t);
void setPCIConfigReg16(uint32_t, uint32_t, uint32_t, uint16_t);
void setPCIConfigReg8(uint32_t, uint32_t, uint32_t, uint8_t);
uint32_t readPCIConfigReg(uint32_t, uint32_t, uint32_t);
uint32_t getPCIConfigReg32(uint32_t, uint32_t, uint32_t);
uint16_t getPCIConfigReg16(uint32_t, uint32_t, uint32_t);
uint8_t getPCIConfigReg8(uint32_t, uint32_t, uint32_t);


uint32_t SwapLong(uint32_t value)
{
  value = ((value & 0xFF)         << 24) | 
          ((value & 0xFF00)       <<  8) | 
          ((value & 0xFF0000UL)   >>  8) | 
          ((value & 0xFF000000UL) >> 24) ;
  return value;
}

uint16_t SwapWord(uint16_t value)
{
  value = ((value & 0x00FF) << 8) | 
          ((value & 0xFF00) >> 8) ;
  return value;	
}


uint32_t ConstructConfigWord (uint32_t BusNum,uint32_t DevNum,uint32_t RegNum)
{
  uint32_t value;
  uint32_t FuncNum=0x0;

  value = (
          ((BusNum  & 0xFF)  << 16) | 
          ((DevNum  & 0xFF)  << 11) | 
          ((FuncNum & 0xFF)  <<  8) | 
          ((RegNum  & 0xFC)       ) |
           ENABLE	              ) ;

  return value;
}

void writePCIConfigReg(uint32_t BusNum, uint32_t DevNum, uint32_t Reg, uint32_t Data)
{
	uint32_t cfg_addr;
	uint32_t data32;
	
	data32 = SwapLong(Data);
	if ((Reg & 0x3) == 1)
		data32 = data32 >> 8;
	else if ((Reg & 0x3) == 2)
		data32 = data32 >> 16;
	else if ((Reg & 0x3) == 3)
		data32 = data32 >> 24;
	
	cfg_addr = ConstructConfigWord(BusNum, DevNum, Reg);
 	WRITE_UINT32(mpc8560->config_addr, cfg_addr);   																	    
  	WRITE_UINT32(mpc8560->config_data, data32);	
}


void setPCIConfigReg32(uint32_t BusNum, uint32_t DevNum, uint32_t Reg, uint32_t Data)
{
	writePCIConfigReg(BusNum, DevNum, Reg, Data);
}

void setPCIConfigReg16(uint32_t BusNum, uint32_t DevNum, uint32_t Reg, uint16_t Data)
{
	writePCIConfigReg(BusNum, DevNum, Reg, Data);
}

void setPCIConfigReg8(uint32_t BusNum, uint32_t DevNum, uint32_t Reg, uint8_t Data)
{
	writePCIConfigReg(BusNum, DevNum, Reg, Data);
}


uint32_t readPCIConfigReg(uint32_t BusNum, uint32_t DevNum, uint32_t Reg)
{
	uint32_t cfg_addr;
	uint32_t cfg_data;

	cfg_addr = ConstructConfigWord(BusNum, DevNum, Reg);
 	WRITE_UINT32(mpc8560->config_addr, cfg_addr);   																	    
 	READ_UINT32(cfg_data, mpc8560->config_data);
 	cfg_data = SwapLong(cfg_data);

	if ((Reg & 0x3) == 1)
		cfg_data = cfg_data >> 8;
	else if ((Reg & 0x3) == 2)
		cfg_data = cfg_data >> 16;
	else if ((Reg & 0x3) == 3)
		cfg_data = cfg_data >> 24;
	
 	return cfg_data;			    
}


uint32_t getPCIConfigReg32(uint32_t BusNum, uint32_t DevNum, uint32_t Reg)
{
	return (uint32_t)readPCIConfigReg(BusNum, DevNum, Reg);	
}

uint16_t getPCIConfigReg16(uint32_t BusNum, uint32_t DevNum, uint32_t Reg)
{
	return (uint16_t)readPCIConfigReg(BusNum, DevNum, Reg);

}

uint8_t getPCIConfigReg8(uint32_t BusNum, uint32_t DevNum, uint32_t Reg)
{
	return (uint8_t)readPCIConfigReg(BusNum, DevNum, Reg);
}

void scanDevices()
{
	uint32_t i;
	uint16_t VendorID, DeviceID, dummy;
	uint32_t BusNum = 0;
	
			
	for(i = 0; i < 0x100; i++)
	{
	 	dummy = getPCIConfigReg16(BusNum, i, REG_DEVID);
	 	VendorID = getPCIConfigReg16(BusNum, i, REG_VENDORID);
	 	DeviceID = getPCIConfigReg16(BusNum, i, REG_DEVID);
		if(VendorID != 0xFFFF)
		{		
			printf(" Device found:\tDevice %d\tBus %d\tDevID = 0x%4x\tVendorID = 0x%4x\n", 
		         i, BusNum, DeviceID, VendorID);
		}
	}	
}

void setBCSR1()
{
	uint8_t *bcsr1 = (uint8_t*)0xF8000001;
	*bcsr1 |= 0x30; 	
}


uint32_t getWindowSize(uint32_t BusNum, uint32_t DevNum, uint32_t Reg)
{
	uint32_t	new, orig;
	uint32_t	size;
	
	// Read CfgReg 
	orig = getPCIConfigReg32(BusNum, DevNum, Reg);
	
	// Write all 1's
 	setPCIConfigReg32(BusNum, DevNum, Reg, 0xFFFFFFFF);
 	
 	// Read back to determine size
 	new = getPCIConfigReg32(BusNum, DevNum, Reg);
	
	// Restore orig register value
 	setPCIConfigReg32(BusNum, DevNum, Reg, orig);
 		
 	// Calculate size required by agent
	if (new & 1)
	    size = (~new | 3) + 1;		// I/O space 
	else
	    size = (~new | 0xF) + 1;	// Memory space 
	
	return size;
	
}



void setOutbound2(uint32_t LocalAddr, uint32_t PCIAddr, uint32_t WindowSize)
{
	// PCI address
	mpc8560->potar2   = PCIAddr   >> 12;
	
	// Local Addr
	mpc8560->powbar2  = LocalAddr >> 12;
	
	// Enable, memory read/write, window size
	mpc8560->powar2   = 0x80044000 | WindowSize ;
}

void setOutbound1(uint32_t LocalAddr, uint32_t PCIAddr, uint32_t WindowSize)
{
	// PCI address
	mpc8560->potar1   = PCIAddr   >> 12;
	
	// Local Addr
	mpc8560->powbar1  = LocalAddr >> 12;
	
	// Enable, memory read/write, window size
	mpc8560->powar1   = 0x80044000 | WindowSize ;
}

uint32_t calcWindowSize(uint32_t Size)
{
	int i;
	uint32_t WindowSize = 0;
	
	for(i = 0; i < 32; i++)
	{
		if(Size & 0x01)
		{
			WindowSize += i;
		}
			Size = Size >> 1;
	}
	
	WindowSize = WindowSize - 1;
	
	return WindowSize;
}



void main()
{
	uint32_t BusNum = 0;
	uint32_t *outbound, *inbound;
	uint32_t size;
	uint32_t windowstart;
	uint16_t status_8144;
	uint32_t data;

		
	// Set BCSR1[RGMII1EN and RGMII2EN] = 1 (Disable) to select PCI/UTP instead of RGMII
	setBCSR1();
   	status_8144 = getPCIConfigReg16(BusNum, MSC8144_PCIDEVICENUM, REG_BUSSTATUS);
	
	
	// Scan devices on the bus
	scanDevices();
	
	// Set 8560 as the arbiter
	setPCIConfigReg16(BusNum, MPC8560_PCIDEVICENUM, REG_ARBITER, 0);
	
	// Set latency timer to max clock cycles to generate stop
	setPCIConfigReg8(BusNum, MPC8560_PCIDEVICENUM, REG_BUSLATENCY, 0xF8);
	
	

	// ************************************************************
	// 8560 Outbound / 8144 Inbound
	// 8560 0x8000_0000 --> PCI 0x8000_0000 --> 8144 0xC000_0000 
	// Size 512KB
	// ************************************************************
	
	// Determine window sizes for 8144 GPLx = 0x0010_0000
	size = getWindowSize(BusNum, MSC8144_PCIDEVICENUM, REG_GPLBAR0); //M2   
		
	// Reassign 8144 inbound window 0 start addr in PCI memory space 
	setPCIConfigReg32(BusNum, MSC8144_PCIDEVICENUM, REG_GPLBAR0, OutboundPCI);
	
	// Read 8144 PCI base inbound window = 0x2000_0008 (prefetchable)
	windowstart = getPCIConfigReg32(BusNum, MSC8144_PCIDEVICENUM, REG_GPLBAR0);
	
	// Set outbound window local address 0x80000000, PCI address 0x20000000, 512KB size
	setOutbound1(OutboundLocal, OutboundPCI, calcWindowSize(size));
	

		
	
	// ************************************************************
	// 8144 Outbound / 8560 Inbound
	// 8144 0xE000_0000 --> PCI 0xE000_0000 --> 8560 0x0000_0000 
	// Size 256MB
	// ************************************************************
	// Set inbound  window local address 0x00000000, PCI address 0xE0000000
	  
	// PCI address
	mpc8560->piwbar1 = InboundPCI   >> 12;
	
	// Local Addr
	mpc8560->pitar1  = InboundLocal >> 12;

	// Enable, prefetch, target i/f local mem, r/w snoop, 256MB size
	mpc8560->piwar1  = 0xA0F5501B;	
	
	
	

	// ************************************************************
	// 8144 PIMMR memory mapped space
	// 8560 0x9000_0000 --> PCI 0x3000_0000 --> 8144 0xFE00_0000 (IMMR)
	// Size 32MB
	// ************************************************************

	// Determine window size and set start addr for 8144 PIMMR
	size  = getWindowSize(BusNum, MSC8144_PCIDEVICENUM, REG_PIMMRBACR);	//IMMR 32MB 	

	// Reassign 8144 inbound window 1 start addr in PCI memory space 
	setPCIConfigReg32(BusNum, MSC8144_PCIDEVICENUM, REG_PIMMRBACR, IMMR8144PCI);
	windowstart = getPCIConfigReg32(BusNum, MSC8144_PCIDEVICENUM, REG_PIMMRBACR);


	// Set outbound  window local address 0x00000000, PCI address 0x30000000, 32MB size
	setOutbound2 (IMMR8144Local, IMMR8144PCI, calcWindowSize(size));


 	// BMST=1, MEM=1, PERR/SERR=1
	setPCIConfigReg16(BusNum, MPC8560_PCIDEVICENUM, REG_BUSCMD, 0x0146);
	
 	// BMST=1, MEM=1, PERR/SERR=1
	setPCIConfigReg16(BusNum, MSC8144_PCIDEVICENUM, REG_BUSCMD, 0x0146);	
	       	      			         	       	
	
	
	
	PCI8144BASE = PCI8144BASE + IMMR8144Local;
	msc8144 = (msc8144_pci_regs*)PCI8144BASE;
	
	// MSC8144 outbound 0 enable, prefetch, streaming
	msc8144->outbound[0].potar = InboundPCI >> 12;
	msc8144->outbound[0].pobar = 0x000E0000;
	msc8144->outbound[0].pocmr = 0xA00F8000;
	
	
	outbound =  (uint32_t *)OutboundLocal;
	*outbound = (uint32_t)0x11223344;

	inbound = (uint32_t *)InboundLocal;
	data = *inbound;
	

	
}




