[PPC440SPe] Improve PCIe configuration space access

- correct configuration space mapping
- correct bus numbering
- better access to config space

Prior to this patch, the 440SPe host/PCIe bridge was able to configure only the
first device on the first bus. We now allow to configure up to 16 buses;
also, scanning for devices behind the PCIe-PCIe bridge is supported, so
peripheral devices farther in hierarchy can be identified.

Signed-off-by: Grzegorz Bernacki <gjb@semihalf.com>
diff --git a/cpu/ppc4xx/405gp_pci.c b/cpu/ppc4xx/405gp_pci.c
index 2837929..282e7a1 100644
--- a/cpu/ppc4xx/405gp_pci.c
+++ b/cpu/ppc4xx/405gp_pci.c
@@ -443,7 +443,7 @@
 static struct pci_controller ppc440_hose = {0};
 
 
-void pci_440_init (struct pci_controller *hose)
+int pci_440_init (struct pci_controller *hose)
 {
 	int reg_num = 0;
 
@@ -459,7 +459,7 @@
 	if ((strap & SDR0_SDSTP1_PISE_MASK) == 0) {
 		printf("PCI: SDR0_STRP1[PISE] not set.\n");
 		printf("PCI: Configuration aborted.\n");
-		return;
+		return -1;
 	}
 #elif defined(CONFIG_440GP)
 	unsigned long strap;
@@ -468,7 +468,7 @@
 	if ((strap & CPC0_STRP1_PISE_MASK) == 0) {
 		printf("PCI: CPC0_STRP1[PISE] not set.\n");
 		printf("PCI: Configuration aborted.\n");
-		return;
+		return -1;
 	}
 #endif
 #endif /* CONFIG_DISABLE_PISE_TEST */
@@ -477,7 +477,7 @@
 	 * PCI controller init
 	 *--------------------------------------------------------------------------*/
 	hose->first_busno = 0;
-	hose->last_busno = 0xff;
+	hose->last_busno = 0;
 
 	/* PCI I/O space */
 	pci_set_region(hose->regions + reg_num++,
@@ -515,7 +515,7 @@
 	if (pci_pre_init (hose) == 0) {
 		printf("PCI: Board-specific initialization failed.\n");
 		printf("PCI: Configuration aborted.\n");
-		return;
+		return -1;
 	}
 
 	pci_register_hose( hose );
@@ -578,13 +578,16 @@
 #endif
 		hose->last_busno = pci_hose_scan(hose);
 	}
+	return hose->last_busno;
 }
 
 void pci_init_board(void)
 {
-	pci_440_init (&ppc440_hose);
+	int busno;
+
+	busno = pci_440_init (&ppc440_hose);
 #if defined(CONFIG_440SPE)
-	pcie_setup_hoses();
+	pcie_setup_hoses(busno + 1);
 #endif
 }
 
diff --git a/cpu/ppc4xx/440spe_pcie.c b/cpu/ppc4xx/440spe_pcie.c
index 2d0b406..158f1c5 100644
--- a/cpu/ppc4xx/440spe_pcie.c
+++ b/cpu/ppc4xx/440spe_pcie.c
@@ -40,6 +40,23 @@
 	LNKW_X8			= 0x8
 };
 
+static u8* pcie_get_base(struct pci_controller *hose, unsigned int devfn)
+{
+	u8 *base = (u8*)hose->cfg_data;
+
+	/* use local configuration space for the first bus */
+	if (PCI_BUS(devfn) == 0) {
+		if (hose->cfg_data == (u8*)CFG_PCIE0_CFGBASE)
+			base = (u8*)CFG_PCIE0_XCFGBASE;
+		if (hose->cfg_data == (u8*)CFG_PCIE1_CFGBASE)
+			base = (u8*)CFG_PCIE1_XCFGBASE;
+		if (hose->cfg_data == (u8*)CFG_PCIE2_CFGBASE)
+			base = (u8*)CFG_PCIE2_XCFGBASE;
+	}
+
+	return base;
+}
+
 static void pcie_dmer_disable(void)
 {
 	mtdcr (DCRN_PEGPL_CFG(DCRN_PCIE0_BASE),
@@ -60,18 +77,35 @@
 		mfdcr (DCRN_PEGPL_CFG(DCRN_PCIE2_BASE)) & ~GPL_DMER_MASK_DISA);
 }
 
-
 static int pcie_read_config(struct pci_controller *hose, unsigned int devfn,
 	int offset, int len, u32 *val) {
 
+	u8 *address;
 	*val = 0;
+
 	/*
-	 * 440SPE implements only one function per port
+	 * Bus numbers are relative to hose->first_busno
 	 */
-	if (!((PCI_FUNC(devfn) == 0) && (PCI_DEV(devfn) == 1)))
+	devfn -= PCI_BDF(hose->first_busno, 0, 0);
+
+	/*
+	 * NOTICE: configuration space ranges are currenlty mapped only for
+	 * the first 16 buses, so such limit must be imposed. In case more
+	 * buses are required the TLB settings in board/amcc/<board>/init.S
+	 * need to be altered accordingly (one bus takes 1 MB of memory space).
+	 */
+	if (PCI_BUS(devfn) >= 16)
 		return 0;
 
-	devfn = PCI_BDF(0,0,0);
+	/*
+	 * Only single device/single function is supported for the primary and
+	 * secondary buses of the 440SPe host bridge.
+	 */
+	if ((!((PCI_FUNC(devfn) == 0) && (PCI_DEV(devfn) == 0))) &&
+		((PCI_BUS(devfn) == 0) || (PCI_BUS(devfn) == 1)))
+		return 0;
+		
+	address = pcie_get_base(hose, devfn);
 	offset += devfn << 4;
 
 	/*
@@ -101,13 +135,24 @@
 static int pcie_write_config(struct pci_controller *hose, unsigned int devfn,
 	int offset, int len, u32 val) {
 
+	u8 *address;
+	
 	/*
-	 * 440SPE implements only one function per port
+	 * Bus numbers are relative to hose->first_busno
 	 */
-	if (!((PCI_FUNC(devfn) == 0) && (PCI_DEV(devfn) == 1)))
+	devfn -= PCI_BDF(hose->first_busno, 0, 0);
+	
+	/*
+	 * Same constraints as in pcie_read_config().
+	 */
+	if (PCI_BUS(devfn) >= 16)
 		return 0;
 
-	devfn = PCI_BDF(0,0,0);
+	if ((!((PCI_FUNC(devfn) == 0) && (PCI_DEV(devfn) == 0))) &&
+		((PCI_BUS(devfn) == 0) || (PCI_BUS(devfn) == 1)))
+		return 0;
+	
+	address = pcie_get_base(hose, devfn);
 	offset += devfn << 4;
 
 	/*
@@ -137,7 +182,7 @@
 	u32 v;
 	int rv;
 
-	rv =  pcie_read_config(hose, dev, offset, 1, &v);
+	rv = pcie_read_config(hose, dev, offset, 1, &v);
 	*val = (u8)v;
 	return rv;
 }
@@ -794,12 +839,12 @@
 	volatile void *rmbase = NULL;
 
 	pci_set_ops(hose,
-		    pcie_read_config_byte,
-		    pcie_read_config_word,
-		    pcie_read_config_dword,
-		    pcie_write_config_byte,
-		    pcie_write_config_word,
-		    pcie_write_config_dword);
+		pcie_read_config_byte,
+		pcie_read_config_word,
+		pcie_read_config_dword,
+		pcie_write_config_byte,
+		pcie_write_config_word,
+		pcie_write_config_dword);
 
 	switch (port) {
 	case 0:
@@ -822,14 +867,9 @@
 	/*
 	 * Set bus numbers on our root port
 	 */
-	if (ppc440spe_revB()) {
-		out_8((u8 *)mbase + PCI_PRIMARY_BUS, 0);
-		out_8((u8 *)mbase + PCI_SECONDARY_BUS, 1);
-		out_8((u8 *)mbase + PCI_SUBORDINATE_BUS, 1);
-	} else {
-		out_8((u8 *)mbase + PCI_PRIMARY_BUS, 0);
-		out_8((u8 *)mbase + PCI_SECONDARY_BUS, 0);
-	}
+	out_8((u8 *)mbase + PCI_PRIMARY_BUS, 0);
+	out_8((u8 *)mbase + PCI_SECONDARY_BUS, 1);
+	out_8((u8 *)mbase + PCI_SUBORDINATE_BUS, 1);
 
 	/*
 	 * Set up outbound translation to hose->mem_space from PLB
@@ -886,6 +926,29 @@
 		 in_le16((u16 *)(mbase + PCI_COMMAND)) |
 		 PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
 	printf("PCIE:%d successfully set as rootpoint\n",port);
+	
+	/* Set Device and Vendor Id */
+	switch (port) {
+	case 0:
+		out_le16(mbase + 0x200, 0xaaa0);
+		out_le16(mbase + 0x202, 0xbed0);
+		break;
+	case 1:
+		out_le16(mbase + 0x200, 0xaaa1);
+		out_le16(mbase + 0x202, 0xbed1);
+		break;
+	case 2:
+		out_le16(mbase + 0x200, 0xaaa2);
+		out_le16(mbase + 0x202, 0xbed2);
+		break;
+	default:
+		out_le16(mbase + 0x200, 0xaaa3);
+		out_le16(mbase + 0x202, 0xbed3);
+	}
+
+	/* Set Class Code to PCI-PCI bridge and Revision Id to 1 */
+	out_le32(mbase + 0x208, 0x06040001);
+
 }
 
 int ppc440spe_setup_pcie_endpoint(struct pci_controller *hose, int port)
@@ -963,8 +1026,8 @@
 
 	/* Enable I/O, Mem, and Busmaster cycles */
 	out_le16((u16 *)(mbase + PCI_COMMAND),
-		 in_le16((u16 *)(mbase + PCI_COMMAND)) |
-		 PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
+		in_le16((u16 *)(mbase + PCI_COMMAND)) |
+		PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
 	out_le16(mbase + 0x200,0xcaad);			/* Setting vendor ID */
 	out_le16(mbase + 0x202,0xfeed);			/* Setting device ID */
 	attempts = 10;