Skip to contentSkip to conversation

PATCH: pci_auto bridge support

PATCH Bradley D. LaRonde 2 participants 6 messages Oct 27, 2001 – Oct 30, 2001 MIPS

lore
Share & tools
lore
#0 lore raw

2001-10-26 Bradley D. LaRonde <[email protected]>

* PCI bridge support. See change log entry below.

mips/kernel/pci_auto.c +153 -118
@@ -4,6 +4,7 @@
  * Author: Matt Porter <[email protected]>
  *
  * Copyright 2000, 2001 MontaVista Software Inc.
+ * Copyright 2001 Bradley D. LaRonde <[email protected]>
  *
  * This program is free software; you can redistribute  it and/or modify it
  * under  the terms of  the GNU General  Public License as published by the
@@ -19,6 +20,15 @@
  * . change most int to u32.
  *
  * Further modified to include it as mips generic code, [email protected].
+ *
+ * 2001-10-26  Bradley D. LaRonde <[email protected]>
+ * - Add a top_bus argument to the "early config" functions so that
+ *   they can set a fake parent bus pointer to convince the underlying
+ *   pci ops to use type 1 configuration for sub busses.
+ * - Set bridge base and limit registers correctly.
+ * - Align io and memory base properly before and after bridge setup.
+ * - Don't fall through to pci_setup_bars for bridge.
+ * - Reformat the debug output to look more like lspci's output.
  */
 
 #include <linux/kernel.h>
@@ -34,14 +44,47 @@
 #else
 #define	DBG(x...)	
 #endif
+
+/*
+ * These functions are used early on before PCI scanning is done
+ * and all of the pci_dev and pci_bus structures have been created.
+ */
+static struct pci_dev *fake_pci_dev(struct pci_channel *hose,
+	int top_bus, int busnr, int devfn)
+{
+	static struct pci_dev dev;
+	static struct pci_bus bus;
+
+	dev.bus = &bus;
+	dev.sysdata = hose;
+	dev.devfn = devfn;
+	bus.number = busnr;
+	bus.ops = hose->pci_ops;
+
+	if(busnr != top_bus)
+		/* Fake a parent bus structure. */
+		bus.parent = &bus;
+	else
+		bus.parent = NULL;
+
+	return &dev;
+}
+
+#define EARLY_PCI_OP(rw, size, type)					\
+int early_##rw##_config_##size(struct pci_channel *hose,		\
+	int top_bus, int bus, int devfn, int offset, type value)	\
+{									\
+	return pci_##rw##_config_##size(				\
+		fake_pci_dev(hose, top_bus, bus, devfn),		\
+		offset, value);						\
+}
 
-/* These are used for config access before all the PCI probing has been done. */
-int early_read_config_byte(struct pci_channel *hose, int bus, int dev_fn, int where, u8 *val);
-int early_read_config_word(struct pci_channel *hose, int bus, int dev_fn, int where, u16 *val);
-int early_read_config_dword(struct pci_channel *hose, int bus, int dev_fn, int where, u32 *val);
-int early_write_config_byte(struct pci_channel *hose, int bus, int dev_fn, int where, u8 val);
-int early_write_config_word(struct pci_channel *hose, int bus, int dev_fn, int where, u16 val);
-int early_write_config_dword(struct pci_channel *hose, int bus, int dev_fn, int where, u32 val);
+EARLY_PCI_OP(read, byte, u8 *)
+EARLY_PCI_OP(read, word, u16 *)
+EARLY_PCI_OP(read, dword, u32 *)
+EARLY_PCI_OP(write, byte, u8)
+EARLY_PCI_OP(write, word, u16)
+EARLY_PCI_OP(write, dword, u32)
 
 static u32 pciauto_lower_iospc;
 static u32 pciauto_upper_iospc;
@@ -51,6 +94,7 @@
 
 void __init 
 pciauto_setup_bars(struct pci_channel *hose,
+		   int top_bus,
 		   int current_bus,
 		   int pci_devfn)
 {
@@ -60,17 +104,14 @@
 	u32 * lower_limit;
 	int found_mem64 = 0;
 
-	DBG("PCI Autoconfig: Found Bus %d, Device %d, Function %d\n",
-	    current_bus, PCI_SLOT(pci_devfn), PCI_FUNC(pci_devfn) );
-
 	for (bar = PCI_BASE_ADDRESS_0; bar <= PCI_BASE_ADDRESS_5; bar+=4) {
 		/* Tickle the BAR and get the response */
-		early_write_config_dword(hose,
+		early_write_config_dword(hose, top_bus,
 					 current_bus,
 					 pci_devfn,
 					 bar,
 					 0xffffffff);
-		early_read_config_dword(hose,
+		early_read_config_dword(hose, top_bus,
 					current_bus,
 					pci_devfn,
 					bar,
@@ -80,12 +121,20 @@
 		if (!bar_response)
 			continue;
 
+		/*
+		 * Workaround for a BAR that doesn't use its upper word,
+		 * like the ALi 1535D+ PCI DC-97 Controller Modem (M5457).
+		 * bdl <[email protected]>
+		 */
+		if (!(bar_response & 0xffff0000))
+			bar_response |= 0xffff0000;
+
 		/* Check the BAR type and set our address mask */
 		if (bar_response & PCI_BASE_ADDRESS_SPACE) {
 			addr_mask = PCI_BASE_ADDRESS_IO_MASK;
 			upper_limit = &pciauto_upper_iospc;
 			lower_limit = &pciauto_lower_iospc;
-			DBG("PCI Autoconfig: BAR %d, I/O, ", bar_nr);
+			DBG("        I/O");
 		} else {
 			if ((bar_response & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
 			    PCI_BASE_ADDRESS_MEM_TYPE_64)
@@ -94,7 +143,7 @@
 			addr_mask = PCI_BASE_ADDRESS_MEM_MASK;		
 			upper_limit = &pciauto_upper_memspc;
 			lower_limit = &pciauto_lower_memspc;
-			DBG("PCI Autoconfig: BAR %d, Mem, ", bar_nr);
+			DBG("        Mem");
 		}
 
 		/* Calculate requested size */
@@ -104,7 +153,7 @@
 		bar_value = ((*lower_limit - 1) & ~(bar_size - 1)) + bar_size;
 
 		/* Write it out and update our limit */
-		early_write_config_dword(hose, current_bus, pci_devfn,
+		early_write_config_dword(hose, top_bus, current_bus, pci_devfn,
 					 bar, bar_value);
 
 		*lower_limit = bar_value + bar_size;
@@ -116,97 +165,99 @@
 		 */ 
 		if (found_mem64) {
 			bar += 4;
-			early_write_config_dword(hose,
+			early_write_config_dword(hose, top_bus,
 						 current_bus,
 						 pci_devfn,
 						 bar,
 						 0x00000000);
 		}
 
-		bar_nr++;
+		DBG(" at 0x%.8x [size=0x%x]\n", bar_value, bar_size);
 
-		DBG("size=0x%x, address=0x%x\n",
-		    bar_size, bar_value);
+		bar_nr++;
 	}
 
 }
 
 void __init
 pciauto_prescan_setup_bridge(struct pci_channel *hose,
+			     int top_bus,
 			     int current_bus,
 			     int pci_devfn,
 			     int sub_bus)
 {
-	int cmdstat;
-
 	/* Configure bus number registers */
-	early_write_config_byte(hose, current_bus, pci_devfn,
+	early_write_config_byte(hose, top_bus, current_bus, pci_devfn,
 	                        PCI_PRIMARY_BUS, current_bus);
-	early_write_config_byte(hose, current_bus, pci_devfn,
+	early_write_config_byte(hose, top_bus, current_bus, pci_devfn,
 				PCI_SECONDARY_BUS, sub_bus + 1);
-	early_write_config_byte(hose, current_bus, pci_devfn,
+	early_write_config_byte(hose, top_bus, current_bus, pci_devfn,
 				PCI_SUBORDINATE_BUS, 0xff);
-
-	/* Round memory allocator to 1MB boundary */
-	pciauto_upper_memspc &= ~(0x100000 - 1);
 
-	/* Round I/O allocator to 4KB boundary */
-	pciauto_upper_iospc &= ~(0x1000 - 1);
+	/* Align memory and I/O to 1MB and 4KB boundaries. */
+	pciauto_lower_memspc = (pciauto_lower_memspc + (0x100000 - 1))
+		& ~(0x100000 - 1);
+	pciauto_lower_iospc = (pciauto_lower_iospc + (0x1000 - 1))
+		& ~(0x1000 - 1);
+
+	/* Set base (lower limit) of address range behind bridge. */
+	early_write_config_word(hose, top_bus, current_bus, pci_devfn,
+		PCI_MEMORY_BASE, pciauto_lower_memspc >> 16);
+	early_write_config_byte(hose, top_bus, current_bus, pci_devfn,
+		PCI_IO_BASE, (pciauto_lower_iospc & 0x0000f000) >> 8);
+	early_write_config_word(hose, top_bus, current_bus, pci_devfn,
+		PCI_IO_BASE_UPPER16, pciauto_lower_iospc >> 16);
 
-	/* Set up memory and I/O filter limits, assume 32-bit I/O space */
-	early_write_config_word(hose, current_bus, pci_devfn, PCI_MEMORY_LIMIT,
-				((pciauto_upper_memspc - 1) & 0xfff00000) >> 16);
-	early_write_config_byte(hose, current_bus, pci_devfn, PCI_IO_LIMIT,
-				((pciauto_upper_iospc - 1) & 0x0000f000) >> 8);
-	early_write_config_word(hose, current_bus, pci_devfn,
-				PCI_IO_LIMIT_UPPER16,
-				((pciauto_upper_iospc - 1) & 0xffff0000) >> 16);
-			
 	/* We don't support prefetchable memory for now, so disable */
-	early_write_config_word(hose, current_bus, pci_devfn,
-				PCI_PREF_MEMORY_BASE, 0x1000);
-	early_write_config_word(hose, current_bus, pci_devfn,
-				PCI_PREF_MEMORY_LIMIT, 0x1000);
-
-	/* Enable memory and I/O accesses, enable bus master */
-	early_read_config_dword(hose, current_bus, pci_devfn, PCI_COMMAND,
-				&cmdstat);
-	early_write_config_dword(hose, current_bus, pci_devfn, PCI_COMMAND,
-				 cmdstat | PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
-				 PCI_COMMAND_MASTER);
+	early_write_config_word(hose, top_bus, current_bus, pci_devfn,
+				PCI_PREF_MEMORY_BASE, 0);
+	early_write_config_word(hose, top_bus, current_bus, pci_devfn,
+				PCI_PREF_MEMORY_LIMIT, 0);
 }
 
 void __init
 pciauto_postscan_setup_bridge(struct pci_channel *hose,
+			      int top_bus,
 			      int current_bus,
 			      int pci_devfn,
 			      int sub_bus)
 {
+	u32 temp;
+
 	/* Configure bus number registers */
-	early_write_config_byte(hose, current_bus, pci_devfn,
+	early_write_config_byte(hose, top_bus, current_bus, pci_devfn,
 				PCI_SUBORDINATE_BUS, sub_bus);
 
-	/* Round memory allocator to 1MB boundary */
-	pciauto_upper_memspc &= ~(0x100000 - 1);
-	early_write_config_word(hose, current_bus, pci_devfn, PCI_MEMORY_BASE,
-				pciauto_upper_memspc >> 16);
-
-	/* Round I/O allocator to 4KB boundary */
-	pciauto_upper_iospc &= ~(0x1000 - 1);
-	early_write_config_byte(hose, current_bus, pci_devfn, PCI_IO_BASE,
-				(pciauto_upper_iospc & 0x0000f000) >> 8);
-	early_write_config_word(hose, current_bus, pci_devfn,
-				PCI_IO_BASE_UPPER16, pciauto_upper_iospc >> 16);
+	/* Set upper limit of address range behind bridge. */
+	early_write_config_word(hose, top_bus, current_bus, pci_devfn,
+		PCI_MEMORY_LIMIT, pciauto_lower_memspc >> 16);
+	early_write_config_byte(hose, top_bus, current_bus, pci_devfn,
+		PCI_IO_LIMIT, (pciauto_lower_iospc & 0x0000f000) >> 8);
+	early_write_config_word(hose, top_bus, current_bus, pci_devfn,
+		PCI_IO_LIMIT_UPPER16, pciauto_lower_iospc >> 16);
+
+	/* Align memory and I/O to 1MB and 4KB boundaries. */
+	pciauto_lower_memspc = (pciauto_lower_memspc + (0x100000 - 1))
+		& ~(0x100000 - 1);
+	pciauto_lower_iospc = (pciauto_lower_iospc + (0x1000 - 1))
+		& ~(0x1000 - 1);
+
+	/* Enable memory and I/O accesses, enable bus master */
+	early_read_config_dword(hose, top_bus, current_bus, pci_devfn,
+		PCI_COMMAND, &temp);
+	early_write_config_dword(hose, top_bus, current_bus, pci_devfn,
+		PCI_COMMAND, temp | PCI_COMMAND_IO | PCI_COMMAND_MEMORY
+		| PCI_COMMAND_MASTER);
 }
 
 #define      PCIAUTO_IDE_MODE_MASK           0x05
 
 int __init
-pciauto_bus_scan(struct pci_channel *hose, int current_bus)
+pciauto_bus_scan(struct pci_channel *hose, int top_bus, int current_bus)
 {
 	int sub_bus;
 	u32 pci_devfn, pci_class, cmdstat, found_multi=0;
-	unsigned short vid;
+	unsigned short vid, did;
 	unsigned char header_type;
 	int devfn_start = 0;
 	int devfn_stop = 0xff;
@@ -223,54 +274,70 @@
 		if (PCI_FUNC(pci_devfn) && !found_multi)
 			continue;
 
-		early_read_config_byte(hose, current_bus, pci_devfn,
+		early_read_config_word(hose, top_bus, current_bus, pci_devfn,
+				       PCI_VENDOR_ID, &vid);
+
+		if (vid == 0xffff) continue;
+
+		early_read_config_byte(hose, top_bus, current_bus, pci_devfn,
 				       PCI_HEADER_TYPE, &header_type);
 
 		if (!PCI_FUNC(pci_devfn))
 			found_multi = header_type & 0x80;
 
-		early_read_config_word(hose, current_bus, pci_devfn,
-				       PCI_VENDOR_ID, &vid);
+		early_read_config_word(hose, top_bus, current_bus, pci_devfn,
+				       PCI_DEVICE_ID, &did);
 
-		if (vid == 0xffff) continue;
-
-		early_read_config_dword(hose, current_bus, pci_devfn,
+		early_read_config_dword(hose, top_bus, current_bus, pci_devfn,
 					PCI_CLASS_REVISION, &pci_class);
+
+		DBG("%.2x:%.2x.%x Class %.4x: %.4x:%.4x",
+			current_bus, PCI_SLOT(pci_devfn), PCI_FUNC(pci_devfn),
+			pci_class >> 16, vid, did);
+		if (pci_class & 0xff)
+			DBG(" (rev %.2x)", pci_class & 0xff);
+		DBG("\n");
+
 		if ((pci_class >> 16) == PCI_CLASS_BRIDGE_PCI) {
-			DBG("PCI Autoconfig: Found P2P bridge, device %d\n", PCI_SLOT(pci_devfn));
-			pciauto_prescan_setup_bridge(hose, current_bus,
+			DBG("        Bridge: primary=%.2x, secondary=%.2x\n",
+				current_bus, sub_bus + 1);
+			pciauto_prescan_setup_bridge(hose, top_bus, current_bus,
 						     pci_devfn, sub_bus);
-			sub_bus = pciauto_bus_scan(hose, sub_bus+1);
-			pciauto_postscan_setup_bridge(hose, current_bus,
+			DBG("Scanning sub bus %.2x, I/O 0x%.8x, Mem 0x%.8x\n",
+				sub_bus + 1,
+				pciauto_lower_iospc, pciauto_lower_memspc);
+			sub_bus = pciauto_bus_scan(hose, top_bus, sub_bus+1);
+			DBG("Back to bus %.2x\n", current_bus);
+			pciauto_postscan_setup_bridge(hose, top_bus, current_bus,
 						      pci_devfn, sub_bus);
-
+			continue;
 		} else if ((pci_class >> 16) == PCI_CLASS_STORAGE_IDE) {
 
 			unsigned char prg_iface;
 
-			early_read_config_byte(hose, current_bus, pci_devfn,
-					       PCI_CLASS_PROG, &prg_iface);
+			early_read_config_byte(hose, top_bus, current_bus,
+				pci_devfn, PCI_CLASS_PROG, &prg_iface);
 			if (!(prg_iface & PCIAUTO_IDE_MODE_MASK)) {
-				DBG("PCI Autoconfig: Skipping legacy mode IDE controller\n");
+				DBG("Skipping legacy mode IDE controller\n");
 				continue;
 			}
 		}
 
-		/*
+ 		/*
 		 * Found a peripheral, enable some standard
 		 * settings
 		 */
-		early_read_config_dword(hose, current_bus, pci_devfn,
+		early_read_config_dword(hose, top_bus, current_bus, pci_devfn,
 					PCI_COMMAND, &cmdstat);
-		early_write_config_dword(hose, current_bus, pci_devfn,
+		early_write_config_dword(hose, top_bus, current_bus, pci_devfn,
 					 PCI_COMMAND, cmdstat | PCI_COMMAND_IO |
 					 PCI_COMMAND_MEMORY |
 					 PCI_COMMAND_MASTER);
-		early_write_config_byte(hose, current_bus, pci_devfn,
+		early_write_config_byte(hose, top_bus, current_bus, pci_devfn,
 					PCI_LATENCY_TIMER, 0x80);
 
 		/* Allocate PCI I/O and/or memory space */
-		pciauto_setup_bars(hose, current_bus, pci_devfn);
+		pciauto_setup_bars(hose, top_bus, current_bus, pci_devfn);
 	}
 	return sub_bus;
 }
@@ -283,41 +350,9 @@
 	pciauto_upper_iospc = hose->io_resource->end + 1;
 	pciauto_lower_memspc = hose->mem_resource->start;
 	pciauto_upper_memspc = hose->mem_resource->end + 1;
+	DBG("Autoconfig PCI channel 0x%p\n", hose);
+	DBG("Scanning bus %.2x, I/O 0x%.8x, Mem 0x%.8x\n",
+		busno, pciauto_lower_iospc, pciauto_lower_memspc);
 
-	return pciauto_bus_scan(hose, busno);
+	return pciauto_bus_scan(hose, busno, busno);
 }
-
-
-/*
- * These functions are used early on before PCI scanning is done
- * and all of the pci_dev and pci_bus structures have been created.
- */
-static struct pci_dev *fake_pci_dev(struct pci_channel *hose, int busnr,
-                                    int devfn)
-{
-	static struct pci_dev dev;
-	static struct pci_bus bus;
-
-	dev.bus = &bus;
-	dev.sysdata = hose;
-	dev.devfn = devfn;
-	bus.number = busnr;
-	bus.ops = hose->pci_ops;
-
-	return &dev;
-}
-
-#define EARLY_PCI_OP(rw, size, type)					\
-int early_##rw##_config_##size(struct pci_channel *hose, int bus,	\
-                               int devfn, int offset, type value)	\
-{									\
-	return pci_##rw##_config_##size(fake_pci_dev(hose, bus, devfn),	\
-	                                offset, value);			\
-}
-
-EARLY_PCI_OP(read, byte, u8 *)
-EARLY_PCI_OP(read, word, u16 *)
-EARLY_PCI_OP(read, dword, u32 *)
-EARLY_PCI_OP(write, byte, u8)
-EARLY_PCI_OP(write, word, u16)
-EARLY_PCI_OP(write, dword, u32)
#2 lore raw
In reply to Jun Sun

I considered that, but since only this small chuck of run-once surrogate bios autoconfig code needs to know, I figured better keep it separate.

Regards,
Brad

----- Original Message -----
From: "Jun Sun" <[email protected]>
To: "Bradley D. LaRonde" <[email protected]>
Cc: <[email protected]>; <[email protected]>
Sent: Monday, October 29, 2001 2:24 PM
Subject: Re: PATCH: pci_auto bridge support

Quoted 14 lines

Brad,

Have you considered embedding "topbus" argument into hose structure? That sounds like potentially better solution.

Quoted 5 lines · Bradley D. LaRonde

2001-10-26 Bradley D. LaRonde <[email protected]>

* PCI bridge support. See change log entry below.

Quoted 9 lines

mips/kernel/pci_auto.c 2001/10/27 01:01:21 line 9

Quoted 9 lines
mips/kernel/pci_auto.c 2001/10/27 01:01:21 renamed from mips/kernel/pci_auto.c 2001/08/18 06:21:53 1.1 +1 -0
@@ -4,6 +4,7 @@
  * Author: Matt Porter <[email protected]>
  *
  * Copyright 2000, 2001 MontaVista Software Inc.
+ * Copyright 2001 Bradley D. LaRonde <[email protected]>
  *
  * This program is free software; you can redistribute  it and/or

modify it

Quoted 1 line

mips/kernel/pci_auto.c 2001/10/27 01:01:21 line 10

Quoted 1 line
mips/kernel/pci_auto.c 2001/10/27 01:01:21 renamed from mips/kernel/pci_auto.c 2001/08/18 06:21:53 1.1 +0 -0
  * under  the terms of  the GNU General  Public License as published by

the

Quoted 4 lines

mips/kernel/pci_auto.c 2001/10/27 01:01:21 line 22

Quoted 4 lines
mips/kernel/pci_auto.c 2001/10/27 01:01:21 renamed from mips/kernel/pci_auto.c 2001/08/18 06:21:53 1.1 +0 -0
@@ -19,6 +20,15 @@
  * . change most int to u32.
  *
  * Further modified to include it as mips generic code,

[email protected].

Quoted 10 lines
Quoted 10 lines
mips/kernel/pci_auto.c 2001/10/27 01:01:21 renamed from mips/kernel/pci_auto.c 2001/08/18 06:21:53 1.1 +9 -0
+ *
+ * 2001-10-26  Bradley D. LaRonde <[email protected]>
+ * - Add a top_bus argument to the "early config" functions so that
+ *   they can set a fake parent bus pointer to convince the underlying
+ *   pci ops to use type 1 configuration for sub busses.
+ * - Set bridge base and limit registers correctly.
+ * - Align io and memory base properly before and after bridge setup.
+ * - Don't fall through to pci_setup_bars for bridge.
+ * - Reformat the debug output to look more like lspci's output.
  */
#1 lore raw
In reply to Bradley D. LaRonde

Brad,

Have you considered embedding "topbus" argument into hose structure? That sounds like potentially better solution.

Quoted 438 lines · Bradley D. LaRonde

2001-10-26 Bradley D. LaRonde <[email protected]>

* PCI bridge support. See change log entry below.

mips/kernel/pci_auto.c 2001/10/27 01:01:21 renamed from mips/kernel/pci_auto.c 2001/08/18 06:21:53 1.1 +153 -118
@@ -4,6 +4,7 @@
  * Author: Matt Porter <[email protected]>
  *
  * Copyright 2000, 2001 MontaVista Software Inc.
+ * Copyright 2001 Bradley D. LaRonde <[email protected]>
  *
  * This program is free software; you can redistribute  it and/or modify it
  * under  the terms of  the GNU General  Public License as published by the
@@ -19,6 +20,15 @@
  * . change most int to u32.
  *
  * Further modified to include it as mips generic code, [email protected].
+ *
+ * 2001-10-26  Bradley D. LaRonde <[email protected]>
+ * - Add a top_bus argument to the "early config" functions so that
+ *   they can set a fake parent bus pointer to convince the underlying
+ *   pci ops to use type 1 configuration for sub busses.
+ * - Set bridge base and limit registers correctly.
+ * - Align io and memory base properly before and after bridge setup.
+ * - Don't fall through to pci_setup_bars for bridge.
+ * - Reformat the debug output to look more like lspci's output.
  */
 
 #include <linux/kernel.h>
@@ -34,14 +44,47 @@
 #else
 #define        DBG(x...)
 #endif
+
+/*
+ * These functions are used early on before PCI scanning is done
+ * and all of the pci_dev and pci_bus structures have been created.
+ */
+static struct pci_dev *fake_pci_dev(struct pci_channel *hose,
+       int top_bus, int busnr, int devfn)
+{
+       static struct pci_dev dev;
+       static struct pci_bus bus;
+
+       dev.bus = &bus;
+       dev.sysdata = hose;
+       dev.devfn = devfn;
+       bus.number = busnr;
+       bus.ops = hose->pci_ops;
+
+       if(busnr != top_bus)
+               /* Fake a parent bus structure. */
+               bus.parent = &bus;
+       else
+               bus.parent = NULL;
+
+       return &dev;
+}
+
+#define EARLY_PCI_OP(rw, size, type)                                   \
+int early_##rw##_config_##size(struct pci_channel *hose,               \
+       int top_bus, int bus, int devfn, int offset, type value)        \
+{                                                                      \
+       return pci_##rw##_config_##size(                                \
+               fake_pci_dev(hose, top_bus, bus, devfn),                \
+               offset, value);                                         \
+}
 
-/* These are used for config access before all the PCI probing has been done. */
-int early_read_config_byte(struct pci_channel *hose, int bus, int dev_fn, int where, u8 *val);
-int early_read_config_word(struct pci_channel *hose, int bus, int dev_fn, int where, u16 *val);
-int early_read_config_dword(struct pci_channel *hose, int bus, int dev_fn, int where, u32 *val);
-int early_write_config_byte(struct pci_channel *hose, int bus, int dev_fn, int where, u8 val);
-int early_write_config_word(struct pci_channel *hose, int bus, int dev_fn, int where, u16 val);
-int early_write_config_dword(struct pci_channel *hose, int bus, int dev_fn, int where, u32 val);
+EARLY_PCI_OP(read, byte, u8 *)
+EARLY_PCI_OP(read, word, u16 *)
+EARLY_PCI_OP(read, dword, u32 *)
+EARLY_PCI_OP(write, byte, u8)
+EARLY_PCI_OP(write, word, u16)
+EARLY_PCI_OP(write, dword, u32)
 
 static u32 pciauto_lower_iospc;
 static u32 pciauto_upper_iospc;
@@ -51,6 +94,7 @@
 
 void __init
 pciauto_setup_bars(struct pci_channel *hose,
+                  int top_bus,
                   int current_bus,
                   int pci_devfn)
 {
@@ -60,17 +104,14 @@
        u32 * lower_limit;
        int found_mem64 = 0;
 
-       DBG("PCI Autoconfig: Found Bus %d, Device %d, Function %d\n",
-           current_bus, PCI_SLOT(pci_devfn), PCI_FUNC(pci_devfn) );
-
        for (bar = PCI_BASE_ADDRESS_0; bar <= PCI_BASE_ADDRESS_5; bar+=4) {
                /* Tickle the BAR and get the response */
-               early_write_config_dword(hose,
+               early_write_config_dword(hose, top_bus,
                                         current_bus,
                                         pci_devfn,
                                         bar,
                                         0xffffffff);
-               early_read_config_dword(hose,
+               early_read_config_dword(hose, top_bus,
                                        current_bus,
                                        pci_devfn,
                                        bar,
@@ -80,12 +121,20 @@
                if (!bar_response)
                        continue;
 
+               /*
+                * Workaround for a BAR that doesn't use its upper word,
+                * like the ALi 1535D+ PCI DC-97 Controller Modem (M5457).
+                * bdl <[email protected]>
+                */
+               if (!(bar_response & 0xffff0000))
+                       bar_response |= 0xffff0000;
+
                /* Check the BAR type and set our address mask */
                if (bar_response & PCI_BASE_ADDRESS_SPACE) {
                        addr_mask = PCI_BASE_ADDRESS_IO_MASK;
                        upper_limit = &pciauto_upper_iospc;
                        lower_limit = &pciauto_lower_iospc;
-                       DBG("PCI Autoconfig: BAR %d, I/O, ", bar_nr);
+                       DBG("        I/O");
                } else {
                        if ((bar_response & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
                            PCI_BASE_ADDRESS_MEM_TYPE_64)
@@ -94,7 +143,7 @@
                        addr_mask = PCI_BASE_ADDRESS_MEM_MASK;
                        upper_limit = &pciauto_upper_memspc;
                        lower_limit = &pciauto_lower_memspc;
-                       DBG("PCI Autoconfig: BAR %d, Mem, ", bar_nr);
+                       DBG("        Mem");
                }
 
                /* Calculate requested size */
@@ -104,7 +153,7 @@
                bar_value = ((*lower_limit - 1) & ~(bar_size - 1)) + bar_size;
 
                /* Write it out and update our limit */
-               early_write_config_dword(hose, current_bus, pci_devfn,
+               early_write_config_dword(hose, top_bus, current_bus, pci_devfn,
                                         bar, bar_value);
 
                *lower_limit = bar_value + bar_size;
@@ -116,97 +165,99 @@
                 */
                if (found_mem64) {
                        bar += 4;
-                       early_write_config_dword(hose,
+                       early_write_config_dword(hose, top_bus,
                                                 current_bus,
                                                 pci_devfn,
                                                 bar,
                                                 0x00000000);
                }
 
-               bar_nr++;
+               DBG(" at 0x%.8x [size=0x%x]\n", bar_value, bar_size);
 
-               DBG("size=0x%x, address=0x%x\n",
-                   bar_size, bar_value);
+               bar_nr++;
        }
 
 }
 
 void __init
 pciauto_prescan_setup_bridge(struct pci_channel *hose,
+                            int top_bus,
                             int current_bus,
                             int pci_devfn,
                             int sub_bus)
 {
-       int cmdstat;
-
        /* Configure bus number registers */
-       early_write_config_byte(hose, current_bus, pci_devfn,
+       early_write_config_byte(hose, top_bus, current_bus, pci_devfn,
                                PCI_PRIMARY_BUS, current_bus);
-       early_write_config_byte(hose, current_bus, pci_devfn,
+       early_write_config_byte(hose, top_bus, current_bus, pci_devfn,
                                PCI_SECONDARY_BUS, sub_bus + 1);
-       early_write_config_byte(hose, current_bus, pci_devfn,
+       early_write_config_byte(hose, top_bus, current_bus, pci_devfn,
                                PCI_SUBORDINATE_BUS, 0xff);
-
-       /* Round memory allocator to 1MB boundary */
-       pciauto_upper_memspc &= ~(0x100000 - 1);
 
-       /* Round I/O allocator to 4KB boundary */
-       pciauto_upper_iospc &= ~(0x1000 - 1);
+       /* Align memory and I/O to 1MB and 4KB boundaries. */
+       pciauto_lower_memspc = (pciauto_lower_memspc + (0x100000 - 1))
+               & ~(0x100000 - 1);
+       pciauto_lower_iospc = (pciauto_lower_iospc + (0x1000 - 1))
+               & ~(0x1000 - 1);
+
+       /* Set base (lower limit) of address range behind bridge. */
+       early_write_config_word(hose, top_bus, current_bus, pci_devfn,
+               PCI_MEMORY_BASE, pciauto_lower_memspc >> 16);
+       early_write_config_byte(hose, top_bus, current_bus, pci_devfn,
+               PCI_IO_BASE, (pciauto_lower_iospc & 0x0000f000) >> 8);
+       early_write_config_word(hose, top_bus, current_bus, pci_devfn,
+               PCI_IO_BASE_UPPER16, pciauto_lower_iospc >> 16);
 
-       /* Set up memory and I/O filter limits, assume 32-bit I/O space */
-       early_write_config_word(hose, current_bus, pci_devfn, PCI_MEMORY_LIMIT,
-                               ((pciauto_upper_memspc - 1) & 0xfff00000) >> 16);
-       early_write_config_byte(hose, current_bus, pci_devfn, PCI_IO_LIMIT,
-                               ((pciauto_upper_iospc - 1) & 0x0000f000) >> 8);
-       early_write_config_word(hose, current_bus, pci_devfn,
-                               PCI_IO_LIMIT_UPPER16,
-                               ((pciauto_upper_iospc - 1) & 0xffff0000) >> 16);
-
        /* We don't support prefetchable memory for now, so disable */
-       early_write_config_word(hose, current_bus, pci_devfn,
-                               PCI_PREF_MEMORY_BASE, 0x1000);
-       early_write_config_word(hose, current_bus, pci_devfn,
-                               PCI_PREF_MEMORY_LIMIT, 0x1000);
-
-       /* Enable memory and I/O accesses, enable bus master */
-       early_read_config_dword(hose, current_bus, pci_devfn, PCI_COMMAND,
-                               &cmdstat);
-       early_write_config_dword(hose, current_bus, pci_devfn, PCI_COMMAND,
-                                cmdstat | PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
-                                PCI_COMMAND_MASTER);
+       early_write_config_word(hose, top_bus, current_bus, pci_devfn,
+                               PCI_PREF_MEMORY_BASE, 0);
+       early_write_config_word(hose, top_bus, current_bus, pci_devfn,
+                               PCI_PREF_MEMORY_LIMIT, 0);
 }
 
 void __init
 pciauto_postscan_setup_bridge(struct pci_channel *hose,
+                             int top_bus,
                              int current_bus,
                              int pci_devfn,
                              int sub_bus)
 {
+       u32 temp;
+
        /* Configure bus number registers */
-       early_write_config_byte(hose, current_bus, pci_devfn,
+       early_write_config_byte(hose, top_bus, current_bus, pci_devfn,
                                PCI_SUBORDINATE_BUS, sub_bus);
 
-       /* Round memory allocator to 1MB boundary */
-       pciauto_upper_memspc &= ~(0x100000 - 1);
-       early_write_config_word(hose, current_bus, pci_devfn, PCI_MEMORY_BASE,
-                               pciauto_upper_memspc >> 16);
-
-       /* Round I/O allocator to 4KB boundary */
-       pciauto_upper_iospc &= ~(0x1000 - 1);
-       early_write_config_byte(hose, current_bus, pci_devfn, PCI_IO_BASE,
-                               (pciauto_upper_iospc & 0x0000f000) >> 8);
-       early_write_config_word(hose, current_bus, pci_devfn,
-                               PCI_IO_BASE_UPPER16, pciauto_upper_iospc >> 16);
+       /* Set upper limit of address range behind bridge. */
+       early_write_config_word(hose, top_bus, current_bus, pci_devfn,
+               PCI_MEMORY_LIMIT, pciauto_lower_memspc >> 16);
+       early_write_config_byte(hose, top_bus, current_bus, pci_devfn,
+               PCI_IO_LIMIT, (pciauto_lower_iospc & 0x0000f000) >> 8);
+       early_write_config_word(hose, top_bus, current_bus, pci_devfn,
+               PCI_IO_LIMIT_UPPER16, pciauto_lower_iospc >> 16);
+
+       /* Align memory and I/O to 1MB and 4KB boundaries. */
+       pciauto_lower_memspc = (pciauto_lower_memspc + (0x100000 - 1))
+               & ~(0x100000 - 1);
+       pciauto_lower_iospc = (pciauto_lower_iospc + (0x1000 - 1))
+               & ~(0x1000 - 1);
+
+       /* Enable memory and I/O accesses, enable bus master */
+       early_read_config_dword(hose, top_bus, current_bus, pci_devfn,
+               PCI_COMMAND, &temp);
+       early_write_config_dword(hose, top_bus, current_bus, pci_devfn,
+               PCI_COMMAND, temp | PCI_COMMAND_IO | PCI_COMMAND_MEMORY
+               | PCI_COMMAND_MASTER);
 }
 
 #define      PCIAUTO_IDE_MODE_MASK           0x05
 
 int __init
-pciauto_bus_scan(struct pci_channel *hose, int current_bus)
+pciauto_bus_scan(struct pci_channel *hose, int top_bus, int current_bus)
 {
        int sub_bus;
        u32 pci_devfn, pci_class, cmdstat, found_multi=0;
-       unsigned short vid;
+       unsigned short vid, did;
        unsigned char header_type;
        int devfn_start = 0;
        int devfn_stop = 0xff;
@@ -223,54 +274,70 @@
                if (PCI_FUNC(pci_devfn) && !found_multi)
                        continue;
 
-               early_read_config_byte(hose, current_bus, pci_devfn,
+               early_read_config_word(hose, top_bus, current_bus, pci_devfn,
+                                      PCI_VENDOR_ID, &vid);
+
+               if (vid == 0xffff) continue;
+
+               early_read_config_byte(hose, top_bus, current_bus, pci_devfn,
                                       PCI_HEADER_TYPE, &header_type);
 
                if (!PCI_FUNC(pci_devfn))
                        found_multi = header_type & 0x80;
 
-               early_read_config_word(hose, current_bus, pci_devfn,
-                                      PCI_VENDOR_ID, &vid);
+               early_read_config_word(hose, top_bus, current_bus, pci_devfn,
+                                      PCI_DEVICE_ID, &did);
 
-               if (vid == 0xffff) continue;
-
-               early_read_config_dword(hose, current_bus, pci_devfn,
+               early_read_config_dword(hose, top_bus, current_bus, pci_devfn,
                                        PCI_CLASS_REVISION, &pci_class);
+
+               DBG("%.2x:%.2x.%x Class %.4x: %.4x:%.4x",
+                       current_bus, PCI_SLOT(pci_devfn), PCI_FUNC(pci_devfn),
+                       pci_class >> 16, vid, did);
+               if (pci_class & 0xff)
+                       DBG(" (rev %.2x)", pci_class & 0xff);
+               DBG("\n");
+
                if ((pci_class >> 16) == PCI_CLASS_BRIDGE_PCI) {
-                       DBG("PCI Autoconfig: Found P2P bridge, device %d\n", PCI_SLOT(pci_devfn));
-                       pciauto_prescan_setup_bridge(hose, current_bus,
+                       DBG("        Bridge: primary=%.2x, secondary=%.2x\n",
+                               current_bus, sub_bus + 1);
+                       pciauto_prescan_setup_bridge(hose, top_bus, current_bus,
                                                     pci_devfn, sub_bus);
-                       sub_bus = pciauto_bus_scan(hose, sub_bus+1);
-                       pciauto_postscan_setup_bridge(hose, current_bus,
+                       DBG("Scanning sub bus %.2x, I/O 0x%.8x, Mem 0x%.8x\n",
+                               sub_bus + 1,
+                               pciauto_lower_iospc, pciauto_lower_memspc);
+                       sub_bus = pciauto_bus_scan(hose, top_bus, sub_bus+1);
+                       DBG("Back to bus %.2x\n", current_bus);
+                       pciauto_postscan_setup_bridge(hose, top_bus, current_bus,
                                                      pci_devfn, sub_bus);
-
+                       continue;
                } else if ((pci_class >> 16) == PCI_CLASS_STORAGE_IDE) {
 
                        unsigned char prg_iface;
 
-                       early_read_config_byte(hose, current_bus, pci_devfn,
-                                              PCI_CLASS_PROG, &prg_iface);
+                       early_read_config_byte(hose, top_bus, current_bus,
+                               pci_devfn, PCI_CLASS_PROG, &prg_iface);
                        if (!(prg_iface & PCIAUTO_IDE_MODE_MASK)) {
-                               DBG("PCI Autoconfig: Skipping legacy mode IDE controller\n");
+                               DBG("Skipping legacy mode IDE controller\n");
                                continue;
                        }
                }
 
-               /*
+               /*
                 * Found a peripheral, enable some standard
                 * settings
                 */
-               early_read_config_dword(hose, current_bus, pci_devfn,
+               early_read_config_dword(hose, top_bus, current_bus, pci_devfn,
                                        PCI_COMMAND, &cmdstat);
-               early_write_config_dword(hose, current_bus, pci_devfn,
+               early_write_config_dword(hose, top_bus, current_bus, pci_devfn,
                                         PCI_COMMAND, cmdstat | PCI_COMMAND_IO |
                                         PCI_COMMAND_MEMORY |
                                         PCI_COMMAND_MASTER);
-               early_write_config_byte(hose, current_bus, pci_devfn,
+               early_write_config_byte(hose, top_bus, current_bus, pci_devfn,
                                        PCI_LATENCY_TIMER, 0x80);
 
                /* Allocate PCI I/O and/or memory space */
-               pciauto_setup_bars(hose, current_bus, pci_devfn);
+               pciauto_setup_bars(hose, top_bus, current_bus, pci_devfn);
        }
        return sub_bus;
 }
@@ -283,41 +350,9 @@
        pciauto_upper_iospc = hose->io_resource->end + 1;
        pciauto_lower_memspc = hose->mem_resource->start;
        pciauto_upper_memspc = hose->mem_resource->end + 1;
+       DBG("Autoconfig PCI channel 0x%p\n", hose);
+       DBG("Scanning bus %.2x, I/O 0x%.8x, Mem 0x%.8x\n",
+               busno, pciauto_lower_iospc, pciauto_lower_memspc);
 
-       return pciauto_bus_scan(hose, busno);
+       return pciauto_bus_scan(hose, busno, busno);
 }
-
-
-/*
- * These functions are used early on before PCI scanning is done
- * and all of the pci_dev and pci_bus structures have been created.
- */
-static struct pci_dev *fake_pci_dev(struct pci_channel *hose, int busnr,
-                                    int devfn)
-{
-       static struct pci_dev dev;
-       static struct pci_bus bus;
-
-       dev.bus = &bus;
-       dev.sysdata = hose;
-       dev.devfn = devfn;
-       bus.number = busnr;
-       bus.ops = hose->pci_ops;
-
-       return &dev;
-}
-
-#define EARLY_PCI_OP(rw, size, type)                                   \
-int early_##rw##_config_##size(struct pci_channel *hose, int bus,      \
-                               int devfn, int offset, type value)      \
-{                                                                      \
-       return pci_##rw##_config_##size(fake_pci_dev(hose, bus, devfn), \
-                                       offset, value);                 \
-}
-
-EARLY_PCI_OP(read, byte, u8 *)
-EARLY_PCI_OP(read, word, u16 *)
-EARLY_PCI_OP(read, dword, u32 *)
-EARLY_PCI_OP(write, byte, u8)
-EARLY_PCI_OP(write, word, u16)
-EARLY_PCI_OP(write, dword, u32)
#3 lore raw
In reply to Bradley D. LaRonde
Quoted 4 lines · Bradley D. LaRonde

I considered that, but since only this small chuck of run-once surrogate bios autoconfig code needs to know, I figured better keep it separate.

I would vote to put it inside the hose structure:

. It makes a workaround look like a real fix. :-)

. In other implementations of pci_auto, hose is the private sys data of a pci dev. Having a bus number inside is very useful (e.g., pci_ops can tell whether it is type0 of type1 configuration based on the bus number rather than a shaky NULL parent bus pointer). In the future, all pci_auto should be combined into the pci driver. So that is probably the right direction to go.

I think hose may evolve to be the data structure that represents the topology of PCI buses. It should have more uses in the future (e.g., the standard IRQ routing across PCI-PCI bridges).

Jun

Quoted 49 lines

----- Original Message -----
From: "Jun Sun" <[email protected]>
To: "Bradley D. LaRonde" <[email protected]>
Cc: <[email protected]>; <[email protected]>
Sent: Monday, October 29, 2001 2:24 PM
Subject: Re: PATCH: pci_auto bridge support

Quoted 14 lines

Brad,

Have you considered embedding "topbus" argument into hose structure? That sounds like potentially better solution.

Quoted 5 lines · Bradley D. LaRonde

2001-10-26 Bradley D. LaRonde <[email protected]>

* PCI bridge support. See change log entry below.

Quoted 9 lines

mips/kernel/pci_auto.c 2001/10/27 01:01:21 line 9

Quoted 9 lines
mips/kernel/pci_auto.c 2001/10/27 01:01:21 renamed from mips/kernel/pci_auto.c 2001/08/18 06:21:53 1.1 +1 -0
@@ -4,6 +4,7 @@
  * Author: Matt Porter <[email protected]>
  *
  * Copyright 2000, 2001 MontaVista Software Inc.
+ * Copyright 2001 Bradley D. LaRonde <[email protected]>
  *
  * This program is free software; you can redistribute  it and/or

modify it

Quoted 1 line

mips/kernel/pci_auto.c 2001/10/27 01:01:21 line 10

Quoted 1 line
mips/kernel/pci_auto.c 2001/10/27 01:01:21 renamed from mips/kernel/pci_auto.c 2001/08/18 06:21:53 1.1 +0 -0
  * under  the terms of  the GNU General  Public License as published by

the

Quoted 4 lines

mips/kernel/pci_auto.c 2001/10/27 01:01:21 line 22

Quoted 4 lines
mips/kernel/pci_auto.c 2001/10/27 01:01:21 renamed from mips/kernel/pci_auto.c 2001/08/18 06:21:53 1.1 +0 -0
@@ -19,6 +20,15 @@
  * . change most int to u32.
  *
  * Further modified to include it as mips generic code,

[email protected].

Quoted 10 lines
Quoted 10 lines
mips/kernel/pci_auto.c 2001/10/27 01:01:21 renamed from mips/kernel/pci_auto.c 2001/08/18 06:21:53 1.1 +9 -0
+ *
+ * 2001-10-26  Bradley D. LaRonde <[email protected]>
+ * - Add a top_bus argument to the "early config" functions so that
+ *   they can set a fake parent bus pointer to convince the underlying
+ *   pci ops to use type 1 configuration for sub busses.
+ * - Set bridge base and limit registers correctly.
+ * - Align io and memory base properly before and after bridge setup.
+ * - Don't fall through to pci_setup_bars for bridge.
+ * - Reformat the debug output to look more like lspci's output.
  */
#4 lore raw

----- Original Message -----
From: "Jun Sun" <[email protected]>
To: "Bradley D. LaRonde" <[email protected]>
Cc: <[email protected]>; <[email protected]>
Sent: Monday, October 29, 2001 5:38 PM
Subject: [Linux-mips-kernel]Re: PATCH: pci_auto bridge support

Quoted 11 lines
Quoted 4 lines · Bradley D. LaRonde

I considered that, but since only this small chuck of run-once surrogate bios autoconfig code needs to know, I figured better keep it separate.

I would vote to put it inside the hose structure:

. It makes a workaround look like a real fix. :-)

. In other implementations of pci_auto, hose is the private sys data of a

pci

Quoted 1 line

dev. Having a bus number inside is very useful (e.g., pci_ops can tell

whether

Quoted 1 line

it is type0 of type1 configuration based on the bus number rather than a

shaky

Quoted 1 line

NULL parent bus pointer). In the future, all pci_auto should be combined

into

Quoted 3 lines

the pci driver. So that is probably the right direction to go.

I think hose may evolve to be the data structure that represents the

topology

Quoted 1 line

of PCI buses. It should have more uses in the future (e.g., the standard

IRQ

Quoted 1 line

routing across PCI-PCI bridges).

Isn't the bus topology already adequately represented in the pci_dev and pci_channel structures?

I look at the pci autoconfig stuff as a bios replacement. The fact that we can use some of the same structures and functions to help us implement it is a bonus, but not a mandate to mess with the existing model.

Isn't Linux already handling PCI-PCI bridges and multiple PCI channles fine already, or has our autoconfig code exposed some existing non-arch-specific weakness?

Regards,
Brad

#5 lore raw
Quoted 28 lines · Bradley D. LaRonde

----- Original Message -----
From: "Jun Sun" <[email protected]>
To: "Bradley D. LaRonde" <[email protected]>
Cc: <[email protected]>; <[email protected]>
Sent: Monday, October 29, 2001 5:38 PM
Subject: [Linux-mips-kernel]Re: PATCH: pci_auto bridge support

Quoted 11 lines
Quoted 4 lines · Bradley D. LaRonde

I considered that, but since only this small chuck of run-once surrogate bios autoconfig code needs to know, I figured better keep it separate.

I would vote to put it inside the hose structure:

. It makes a workaround look like a real fix. :-)

. In other implementations of pci_auto, hose is the private sys data of a

pci

Quoted 1 line

dev. Having a bus number inside is very useful (e.g., pci_ops can tell

whether

Quoted 1 line

it is type0 of type1 configuration based on the bus number rather than a

shaky

Quoted 1 line

NULL parent bus pointer). In the future, all pci_auto should be combined

into

Quoted 2 lines

the pci driver. So that is probably the right direction to go.

Quoted 9 lines
Quoted 1 line

I think hose may evolve to be the data structure that represents the

topology

Quoted 1 line

of PCI buses. It should have more uses in the future (e.g., the standard

IRQ

Quoted 1 line

routing across PCI-PCI bridges).

Isn't the bus topology already adequately represented in the pci_dev and pci_channel structures?

Not really. For example, we don't know which bus is the sub-bus of which, directly, and how their address space translate into each other. Those data structures are needed when we start to support dynamically mapped and/or arbitrarily mapped PCI memory spaces.

Quoted 4 lines

I look at the pci autoconfig stuff as a bios replacement. The fact that we can use some of the same structures and functions to help us implement it is a bonus, but not a mandate to mess with the existing model.

That is a right point. I might be too far ahead of myself. :-)

Quoted 4 lines

Isn't Linux already handling PCI-PCI bridges and multiple PCI channles fine already, or has our autoconfig code exposed some existing non-arch-specific weakness?

I think on PCs, P2P still largely depends on BIOS. (Correct me if I am wrong).

There are some post-scan_bus() mechanism to setup P2P bridges. I have not looked into it closely.

Jun