mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [patch] 2.5: PCI setup reorg (alpha, arm, parisc)
@ 2001-12-22 16:05 Ivan Kokshaysky
  2001-12-22 17:15 ` Russell King
  0 siblings, 1 reply; 3+ messages in thread
From: Ivan Kokshaysky @ 2001-12-22 16:05 UTC (permalink / raw)
  To: linux-kernel

[Ouch. Forgot to cc this to l-k, hence another post...]

Only generic and alpha changes here - parisc folks should be almost in
sync, and required ARM changes look more or less straightforward.

Generic changes:
- Code disabling pci devices before reallocation is now arch specific
  and supposed to be in pcibios_fixup_bus(). Same with re-enabling
  devices after allocation - much hated pdev_enable_device() removed thus.
  This gives architectures more freedom to handle specific console
  devices and other odd stuff.
- Handling of PCI-PCI bridge resources gets completely generic, so
  the only thing architectures must care of is proper initialization
  of the root bus resources.
- Fast-Back-to-Back/PERR/SERR support.
  Added new field in the struct pci_bus - bridge_ctl. It has the same
  format as PCI-PCI bridge control register (and allows to get rid
  of IORESOURCE_BUS_HAS_VGA hack).
  New function - pcibios_init_bus(). Common for all platforms, but
  safely could be defined as noop, as it's done for i386.
  Primary purpose - set appropriate bits in the bus->bridge_ctl
  depending on bus capabilities. The FBB bit will be cleared by
  pci_setup_device() if any of devices on the bus doesn't support FBB;
  PERR/SERR could be cleared in arch specific code as well if certain
  broken devices are present.
  Some specific bridge setup (setting secondary latency etc.)
  also can be done in pcibios_init_bus().
- Two recent patches from Russell King included.

Prefetchable memory support not included for now.

Alpha specific:
- Do not enable _all_ devices on startup - pcibios_enable_device()
  isn't noop anymore.
- Allow configs with multiple graphic cards, as Jay and Richard
  suggested; make sure that at least one display is enabled.
- Framework for setting up PCI control on various core logic chipsets -
  alpha_mv.pci_control(). I have code for cia/pyxis (reasonably tested)
  and tsunami (completely untested), but it's not included for now...

Ivan.

diff -urpN 2.5.1/arch/alpha/kernel/pci.c linux/arch/alpha/kernel/pci.c
--- 2.5.1/arch/alpha/kernel/pci.c	Fri Dec 21 21:29:03 2001
+++ linux/arch/alpha/kernel/pci.c	Fri Dec 21 21:52:27 2001
@@ -24,6 +24,12 @@
 #include "proto.h"
 #include "pci_impl.h"
 
+#define DEBUG_CONFIG 1
+#if DEBUG_CONFIG
+# define DBGC(args)     printk args
+#else
+# define DBGC(args)
+#endif
 
 /*
  * Some string constants used by the various core logics. 
@@ -109,6 +115,33 @@ quirk_cypress(struct pci_dev *dev)
 	}
 }
 
+static int __initdata vga_enabled;
+
+/* Called for each device after PCI setup is done. */
+static void __init
+pcibios_fixup_final(struct pci_dev *dev)
+{
+	/* If no display devices are enabled, enable
+	   the first one we've found. */
+	if (((dev->class >> 8) == PCI_CLASS_NOT_DEFINED_VGA ||
+	     (dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) && !vga_enabled) {
+		pci_enable_device(dev);
+		vga_enabled = 1;
+	}
+	/* These (may) have I/O scattered all around and may not
+	   use I/O base address registers at all.  So we just have to
+	   always enable IO to these devices.  */
+	else if ((dev->class >> 8) == PCI_CLASS_NOT_DEFINED ||
+		 (dev->class >> 8) == PCI_CLASS_STORAGE_IDE) {
+			u16 cmd;
+			pci_read_config_word(dev, PCI_COMMAND, &cmd);
+			cmd |= PCI_COMMAND_IO;
+			pci_write_config_word(dev, PCI_COMMAND, cmd);
+			pci_enable_device(dev);
+			pci_set_master(dev);
+	}
+}
+
 struct pci_fixup pcibios_fixups[] __initdata = {
 	{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82375,
 	  quirk_eisa_bridge },
@@ -118,6 +151,8 @@ struct pci_fixup pcibios_fixups[] __init
 	  quirk_ali_ide_ports },
 	{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_CONTAQ, PCI_DEVICE_ID_CONTAQ_82C693,
 	  quirk_cypress },
+	{ PCI_FIXUP_FINAL,  PCI_ANY_ID,	PCI_ANY_ID,
+	  pcibios_fixup_final },
 	{ 0 }
 };
 
@@ -235,40 +270,69 @@ pcibios_fixup_device_resources(struct pc
 void __init
 pcibios_fixup_bus(struct pci_bus *bus)
 {
-	/* Propogate hose info into the subordinate devices.  */
+	/* Propagate hose info into the subordinate devices.
+	   Setup FBB/PERR/SERR depending on the bus capabilities.  */
 
-	struct pci_controller *hose = bus->sysdata;
 	struct list_head *ln;
-	struct pci_dev *dev = bus->self;
+	u16 cmdbits = 0;
+
+	if (!bus->self) {
+		/* Initialize the root bus resources.  */
+		struct pci_controller *hose = bus->sysdata;
 
-	if (!dev) {
-		/* Root bus */
 		bus->resource[0] = hose->io_space;
 		bus->resource[1] = hose->mem_space;
-	} else {
-		/* This is a bridge. Do not care how it's initialized,
-		   just link its resources to the bus ones */
-		int i;
-
-		for(i=0; i<3; i++) {
-			bus->resource[i] =
-				&dev->resource[PCI_BRIDGE_RESOURCES+i];
-			bus->resource[i]->name = bus->name;
-		}
-		bus->resource[0]->flags |= pci_bridge_check_io(dev);
-		bus->resource[1]->flags |= IORESOURCE_MEM;
-		/* For now, propogate hose limits to the bus;
-		   we'll adjust them later. */
-		bus->resource[0]->end = hose->io_space->end;
-		bus->resource[1]->end = hose->mem_space->end;
-		/* Turn off downstream PF memory address range by default */
-		bus->resource[2]->start = 1024*1024;
-		bus->resource[2]->end = bus->resource[2]->start - 1;
+
+		/* Setup our pci controller.  */
+		if (alpha_mv.pci_control)
+			bus->bridge_ctl = alpha_mv.pci_control(bus);
+		else
+			bus->bridge_ctl &= ~PCI_COMMAND_FAST_BACK;
 	}
 
+	if (bus->bridge_ctl & PCI_BRIDGE_CTL_PARITY)
+		cmdbits |= PCI_COMMAND_PARITY;
+	if (bus->bridge_ctl & PCI_BRIDGE_CTL_SERR)
+		cmdbits |= PCI_COMMAND_SERR;
+	if (bus->bridge_ctl & PCI_BRIDGE_CTL_FAST_BACK)
+		cmdbits |= PCI_COMMAND_FAST_BACK;
+
 	for (ln = bus->devices.next; ln != &bus->devices; ln = ln->next) {
 		struct pci_dev *dev = pci_dev_b(ln);
-		if ((dev->class >> 8) != PCI_CLASS_BRIDGE_PCI)
+		u16 class = dev->class >> 8;
+		u16 cmd;
+		u32 reg;
+
+		pci_read_config_word(dev, PCI_COMMAND, &cmd);
+
+		/* First, disable the device to avoid side
+		   effects of possibly overlapping I/O and
+		   memory ranges.
+		   Don't touch VGAs and all sorts of bridges.  */
+		if (class == PCI_CLASS_NOT_DEFINED_VGA ||
+		    class >> 8 == PCI_BASE_CLASS_DISPLAY) {
+			/* Check that at least one display device
+			   is enabled.  */
+			if (cmd & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY))
+				vga_enabled = 1;
+		} else if (class >> 8 != PCI_BASE_CLASS_BRIDGE)
+			cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY
+						| PCI_COMMAND_MASTER);
+
+		cmd |= cmdbits;
+		pci_write_config_word(dev, PCI_COMMAND, cmd);
+
+		/* Disable the ROM.  Several devices act funny (ie. do not
+		   respond to memory space writes) when it is left enabled.
+		   A good example are QlogicISP adapters.  */
+		if (dev->rom_base_reg) {
+			pci_read_config_dword(dev, dev->rom_base_reg, &reg);
+			reg &= ~PCI_ROM_ADDRESS_ENABLE;
+			pci_write_config_dword(dev, dev->rom_base_reg, reg);
+			dev->resource[PCI_ROM_RESOURCE].flags &=
+						~PCI_ROM_ADDRESS_ENABLE;
+		}
+		if (class != PCI_CLASS_BRIDGE_PCI)
 			pcibios_fixup_device_resources(dev, bus);
 	}
 }
@@ -353,24 +417,79 @@ pcibios_fixup_pbus_ranges(struct pci_bus
 int
 pcibios_enable_device(struct pci_dev *dev)
 {
-	/* Nothing to do, since we enable all devices at startup.  */
+	u16 cmd, oldcmd;
+	int i;
+
+	pci_read_config_word(dev, PCI_COMMAND, &cmd);
+	oldcmd = cmd;
+
+	for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+		struct resource *res = &dev->resource[i];
+
+		if (res->flags & IORESOURCE_IO)
+			cmd |= PCI_COMMAND_IO;
+		else if (res->flags & IORESOURCE_MEM)
+			cmd |= PCI_COMMAND_MEMORY;
+	}
+
+	if (cmd != oldcmd) {
+		DBGC((KERN_INFO "PCI: Enabling device: (%s), cmd %x\n",
+				dev->name, cmd));
+		/* Enable the appropriate bits in the PCI command register.  */
+		pci_write_config_word(dev, PCI_COMMAND, cmd);
+	}
 	return 0;
 }
 
-/*
- *  If we set up a device for bus mastering, we need to check the latency
- *  timer as certain firmware forgets to set it properly, as seen
- *  on SX164 and LX164 with SRM.
- */
 void
 pcibios_set_master(struct pci_dev *dev)
 {
-	u8 lat;
-	pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
-	if (lat >= 16) return;
-	printk("PCI: Setting latency timer of device %s to 64\n",
-							dev->slot_name);
-	pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
+	/* Set the cacheline size and default latency (32).  */
+	pci_write_config_word(dev, PCI_CACHE_LINE_SIZE,
+			(32 << 8) | (L1_CACHE_BYTES / sizeof(u32)));
+
+#if 0	/* ??? Turning on MWI might be dangerous... */
+	{
+		u8 cacheline;
+
+		pci_read_config_byte(dev, PCI_CACHE_LINE_SIZE, &cacheline);
+		/* If this cacheline size is supported,
+		   turn on write/invalidate. */
+		if (cacheline) {
+			u16 cmd;
+
+			pci_read_config_word(dev, PCI_COMMAND, &cmd);
+			cmd |= PCI_COMMAND_INVALIDATE;
+			pci_write_config_word(dev, PCI_COMMAND, cmd);
+		}
+	}
+#endif
+}
+
+void __init
+pcibios_init_bus(struct pci_bus *bus)
+{
+	struct pci_dev *dev = bus->self;
+	u16 status;
+	/* Set FBB bit for now. Disable ISA IO forwarding. Enable PERR/SERR. */
+	u16 ctl = PCI_BRIDGE_CTL_FAST_BACK | PCI_BRIDGE_CTL_NO_ISA |
+		  PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR;
+
+	/* We deal only with pci controllers and pci-pci bridges. */
+	if (dev) {
+		if ((dev->class >> 8) != PCI_CLASS_BRIDGE_PCI)
+			return;
+		/* PCI-PCI bridge - set default latency (32)
+		   for secondary bus. */
+		pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER, 32);
+		/* Read bridge control. */
+		pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &bus->bridge_ctl);
+		/* Does the bridge support FBB on its secondary bus? */
+		pci_read_config_word(dev, PCI_SEC_STATUS, &status);
+		if (!(status & PCI_BRIDGE_CTL_FAST_BACK))
+			ctl &= ~PCI_BRIDGE_CTL_FAST_BACK;
+	}
+	bus->bridge_ctl |= ctl;
 }
 
 void __init
diff -urpN 2.5.1/drivers/pci/pci.c linux/drivers/pci/pci.c
--- 2.5.1/drivers/pci/pci.c	Fri Dec 21 21:29:03 2001
+++ linux/drivers/pci/pci.c	Fri Dec 21 21:29:43 2001
@@ -914,13 +914,15 @@ static void pci_read_bases(struct pci_de
 			l = 0;
 		if ((l & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) {
 			res->start = l & PCI_BASE_ADDRESS_MEM_MASK;
+			res->flags |= l & ~PCI_BASE_ADDRESS_MEM_MASK;
 			sz = pci_size(sz, PCI_BASE_ADDRESS_MEM_MASK);
 		} else {
 			res->start = l & PCI_BASE_ADDRESS_IO_MASK;
+			res->flags |= l & ~PCI_BASE_ADDRESS_IO_MASK;
 			sz = pci_size(sz, PCI_BASE_ADDRESS_IO_MASK & 0xffff);
 		}
 		res->end = res->start + (unsigned long) sz;
-		res->flags |= (l & 0xf) | pci_calc_resource_flags(l);
+		res->flags |= pci_calc_resource_flags(l);
 		if ((l & (PCI_BASE_ADDRESS_SPACE | PCI_BASE_ADDRESS_MEM_TYPE_MASK))
 		    == (PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64)) {
 			pci_read_config_dword(dev, reg+4, &l);
@@ -1210,6 +1212,7 @@ static void pci_read_irq(struct pci_dev 
 int pci_setup_device(struct pci_dev * dev)
 {
 	u32 class;
+	unsigned short status;
 
 	sprintf(dev->slot_name, "%02x:%02x.%d", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
 	sprintf(dev->name, "PCI device %04x:%04x", dev->vendor, dev->device);
@@ -1260,6 +1263,11 @@ int pci_setup_device(struct pci_dev * de
 		dev->class = PCI_CLASS_NOT_DEFINED;
 	}
 
+	/* FBB capability - if not, clear bit in parent bridge_ctl.  */
+	pci_read_config_word(dev, PCI_STATUS, &status);
+	if (!(status & PCI_STATUS_FAST_BACK))
+		dev->bus->bridge_ctl &= ~PCI_BRIDGE_CTL_FAST_BACK;
+
 	/* We found a fine healthy device, go go go... */
 	return 0;
 }
@@ -1341,6 +1349,8 @@ unsigned int __devinit pci_do_scan_bus(s
 	unsigned int devfn, max, pass;
 	struct list_head *ln;
 	struct pci_dev *dev, dev0;
+
+	pcibios_init_bus(bus);
 
 	DBG("Scanning bus %02x\n", bus->number);
 	max = bus->secondary;
diff -urpN 2.5.1/drivers/pci/setup-bus.c linux/drivers/pci/setup-bus.c
--- 2.5.1/drivers/pci/setup-bus.c	Fri Dec 21 21:29:03 2001
+++ linux/drivers/pci/setup-bus.c	Fri Dec 21 21:33:58 2001
@@ -12,6 +12,16 @@
 /*
  * Nov 2000, Ivan Kokshaysky <ink@jurassic.park.msu.ru>
  *	     PCI-PCI bridges cleanup, sorted resource allocation
+ * Jul 2001, Grant Grundler <grundler@parisc-linux.org>,
+ *	     Ivan Kokshaysky <ink@jurassic.park.msu.ru>
+ *	     Further cleanup, generic Fast-Back-to-Back/PERR/SERR support
+ */
+
+/* NOTE: during reallocation we may have temporarily overlapping
+ * IO or MEM ranges, so the arch code (pcibios_fixup_bus()) is
+ * responsible for disabling all devices, probably except console
+ * (VGA, serial etc.) and bridges the console device might be
+ * behind -- typically AGP or PCI-(E)ISA bridges.
  */
 
 #include <linux/init.h>
@@ -46,23 +56,9 @@ pbus_assign_resources_sorted(struct pci_
 	for (ln=bus->devices.next; ln != &bus->devices; ln=ln->next) {
 		struct pci_dev *dev = pci_dev_b(ln);
 		u16 class = dev->class >> 8;
-		u16 cmd;
 
-		/* First, disable the device to avoid side
-		   effects of possibly overlapping I/O and
-		   memory ranges.
-		   Leave VGA enabled - for obvious reason. :-)
-		   Same with all sorts of bridges - they may
-		   have VGA behind them.  */
-		if (class == PCI_CLASS_DISPLAY_VGA
-				|| class == PCI_CLASS_NOT_DEFINED_VGA)
-			found_vga = 1;
-		else if (class >> 8 != PCI_BASE_CLASS_BRIDGE) {
-			pci_read_config_word(dev, PCI_COMMAND, &cmd);
-			cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY
-						| PCI_COMMAND_MASTER);
-			pci_write_config_word(dev, PCI_COMMAND, cmd);
-		}
+		found_vga |= (class == PCI_CLASS_DISPLAY_VGA ||
+			      class == PCI_CLASS_NOT_DEFINED_VGA);
 
 		/* Reserve some resources for CardBus.
 		   Are these values reasonable? */
@@ -100,20 +96,6 @@ pbus_assign_resources_sorted(struct pci_
 	ranges->io_end += io_reserved;
 	ranges->mem_end += mem_reserved;
 
-	/* PCI-to-PCI Bridge Architecture Specification rev. 1.1 (1998)
-	   requires that if there is no I/O ports or memory behind the
-	   bridge, corresponding range must be turned off by writing base
-	   value greater than limit to the bridge's base/limit registers.  */
-#if 1
-	/* But assuming that some hardware designed before 1998 might
-	   not support this (very unlikely - at least all DEC bridges
-	   are ok and I believe that was standard de-facto. -ink), we
-	   must allow for at least one unit.  */
-	if (ranges->io_end == ranges->io_start)
-		ranges->io_end += 1;
-	if (ranges->mem_end == ranges->mem_start)
-		ranges->mem_end += 1;
-#endif
 	ranges->io_end = ROUND_UP(ranges->io_end, 4*1024);
 	ranges->mem_end = ROUND_UP(ranges->mem_end, 1024*1024);
 
@@ -130,6 +112,7 @@ pci_setup_bridge(struct pci_bus *bus)
 
 	if (!bridge || (bridge->class >> 8) != PCI_CLASS_BRIDGE_PCI)
 		return;
+
 	ranges.io_start = bus->resource[0]->start;
 	ranges.io_end = bus->resource[0]->end;
 	ranges.mem_start = bus->resource[1]->start;
@@ -147,8 +130,11 @@ pci_setup_bridge(struct pci_bus *bus)
 	l |= ranges.io_end & 0xf000;
 	pci_write_config_dword(bridge, PCI_IO_BASE, l);
 
-	/* Clear upper 16 bits of I/O base/limit. */
-	pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, 0);
+	/* Set up upper 16 bits of I/O base/limit. */
+	pci_write_config_word(bridge, PCI_IO_BASE_UPPER16,
+			ranges.io_start >> 16);
+	pci_write_config_word(bridge, PCI_IO_LIMIT_UPPER16,
+			ranges.io_end >> 16);
 
 	/* Clear out the upper 32 bits of PREF base/limit. */
 	pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, 0);
@@ -160,15 +146,34 @@ pci_setup_bridge(struct pci_bus *bus)
 	l |= ranges.mem_end & 0xfff00000;
 	pci_write_config_dword(bridge, PCI_MEMORY_BASE, l);
 
-	/* Set up PREF base/limit. */
-	l = (bus->resource[2]->start >> 16) & 0xfff0;
-	l |= bus->resource[2]->end & 0xfff00000;
-	pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, l);
-
-	/* Check if we have VGA behind the bridge.
-	   Enable ISA in either case. */
-	l = (bus->resource[0]->flags & IORESOURCE_BUS_HAS_VGA) ? 0x0c : 0x04;
-	pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, l);
+	/* Disable PREF memory range. */
+	pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0x0000fff0);
+
+	pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, bus->bridge_ctl);
+
+	pci_enable_device(bridge);
+	pci_set_master(bridge);
+}
+
+/* Check whether the bridge supports I/O forwarding.
+   If not, its I/O base/limit register must be
+   read-only and read as 0. */
+static unsigned long __init
+pci_bridge_check_io(struct pci_dev *bridge)
+{
+	u16 io;
+
+	pci_read_config_word(bridge, PCI_IO_BASE, &io);
+	if (!io) {
+		pci_write_config_word(bridge, PCI_IO_BASE, 0xf0f0);
+		pci_read_config_word(bridge, PCI_IO_BASE, &io);
+		pci_write_config_word(bridge, PCI_IO_BASE, 0x0);
+	}
+	if (io)
+		return IORESOURCE_IO;
+	printk(KERN_WARNING "PCI: bridge %s does not support I/O forwarding!\n",
+				bridge->name);
+	return 0;
 }
 
 static void __init
@@ -181,26 +186,52 @@ pbus_assign_resources(struct pci_bus *bu
 		struct pci_bus *b;
 
 		ranges->found_vga = 1;
-		/* Propogate presence of the VGA to upstream bridges */
-		for (b = bus; b->parent; b = b->parent) {
-#if 0
-			/* ? Do we actually need to enable PF memory? */
-			b->resource[2]->start = 0;
-#endif
-			b->resource[0]->flags |= IORESOURCE_BUS_HAS_VGA;
-		}
+		/* Propagate presence of the VGA to upstream bridges */
+		for (b = bus; b->parent; b = b->parent)
+			b->bridge_ctl |= PCI_BRIDGE_CTL_VGA;
 	}
 	for (ln=bus->children.next; ln != &bus->children; ln=ln->next) {
 		struct pci_bus *b = pci_bus_b(ln);
+		struct pci_dev *bridge = b->self;
+		int i;
 
+		/* Link bus resources to the bridge ones.  */
+		for (i = 0; i < 3; i++) {
+			b->resource[i] =
+				&bridge->resource[PCI_BRIDGE_RESOURCES + i];
+			b->resource[i]->name = bus->name;
+		}
+		b->resource[0]->flags |= pci_bridge_check_io(bridge);
+		b->resource[1]->flags |= IORESOURCE_MEM;
 		b->resource[0]->start = ranges->io_start = ranges->io_end;
 		b->resource[1]->start = ranges->mem_start = ranges->mem_end;
 
+		/* For now, set IO and MEM limits of this bus
+		   same as limits of its parent bus. */
+		b->resource[0]->end = bus->resource[0]->end;
+		b->resource[1]->end = bus->resource[1]->end;
+
 		pbus_assign_resources(b, ranges);
 
+		/* PCI-to-PCI Bridge Architecture Specification rev. 1.1 (1998)
+		   requires that if there is no I/O ports or memory behind the
+		   bridge, corresponding range must be turned off by writing
+		   base value greater than limit to the bridge's base/limit
+		   registers.
+		   This is done automatically for empty (start==end) ranges. */
 		b->resource[0]->end = ranges->io_end - 1;
 		b->resource[1]->end = ranges->mem_end - 1;
 
+		/* Add bridge resources to the resource tree. */
+		if (b->resource[0]->end > b->resource[0]->start &&
+		    request_resource(bus->resource[0], b->resource[0]) < 0)
+			printk(KERN_ERR "PCI: failed to request IO "
+					"for bus %d\n",	b->number);
+		if (b->resource[1]->end > b->resource[1]->start &&
+		    request_resource(bus->resource[1], b->resource[1]) < 0)
+			printk(KERN_ERR "PCI: failed to request MEM "
+					"for bus %d\n", b->number);
+
 		pci_setup_bridge(b);
 	}
 }
@@ -210,7 +241,6 @@ pci_assign_unassigned_resources(void)
 {
 	struct pbus_set_ranges_data ranges;
 	struct list_head *ln;
-	struct pci_dev *dev;
 
 	for(ln=pci_root_buses.next; ln != &pci_root_buses; ln=ln->next) {
 		struct pci_bus *b = pci_bus_b(ln);
@@ -222,28 +252,4 @@ pci_assign_unassigned_resources(void)
 		ranges.found_vga = 0;
 		pbus_assign_resources(b, &ranges);
 	}
-	pci_for_each_dev(dev) {
-		pdev_enable_device(dev);
-	}
-}
-
-/* Check whether the bridge supports I/O forwarding.
-   If not, its I/O base/limit register must be
-   read-only and read as 0. */
-unsigned long __init
-pci_bridge_check_io(struct pci_dev *bridge)
-{
-	u16 io;
-
-	pci_read_config_word(bridge, PCI_IO_BASE, &io);
-	if (!io) {
-		pci_write_config_word(bridge, PCI_IO_BASE, 0xf0f0);
-		pci_read_config_word(bridge, PCI_IO_BASE, &io);
-		pci_write_config_word(bridge, PCI_IO_BASE, 0x0);
-	}
-	if (io)
-		return IORESOURCE_IO;
-	printk(KERN_WARNING "PCI: bridge %s does not support I/O forwarding!\n",
-				bridge->name);
-	return 0;
 }
diff -urpN 2.5.1/drivers/pci/setup-res.c linux/drivers/pci/setup-res.c
--- 2.5.1/drivers/pci/setup-res.c	Fri Dec 21 21:29:03 2001
+++ linux/drivers/pci/setup-res.c	Fri Dec 21 21:29:43 2001
@@ -177,57 +177,3 @@ pdev_sort_resources(struct pci_dev *dev,
 		}
 	}
 }
-
-void __init
-pdev_enable_device(struct pci_dev *dev)
-{
-	u32 reg;
-	u16 cmd;
-	int i;
-
-	DBGC((KERN_ERR "PCI enable device: (%s)\n", dev->name));
-
-	pci_read_config_word(dev, PCI_COMMAND, &cmd);
-
-	for (i = 0; i < PCI_NUM_RESOURCES; i++) {
-		struct resource *res = &dev->resource[i];
-
-		if (res->flags & IORESOURCE_IO)
-			cmd |= PCI_COMMAND_IO;
-		else if (res->flags & IORESOURCE_MEM)
-			cmd |= PCI_COMMAND_MEMORY;
-	}
-
-	/* Special case, disable the ROM.  Several devices act funny
-	   (ie. do not respond to memory space writes) when it is left
-	   enabled.  A good example are QlogicISP adapters.  */
-
-	if (dev->rom_base_reg) {
-		pci_read_config_dword(dev, dev->rom_base_reg, &reg);
-		reg &= ~PCI_ROM_ADDRESS_ENABLE;
-		pci_write_config_dword(dev, dev->rom_base_reg, reg);
-		dev->resource[PCI_ROM_RESOURCE].flags &= ~PCI_ROM_ADDRESS_ENABLE;
-	}
-
-	/* All of these (may) have I/O scattered all around and may not
-	   use I/O base address registers at all.  So we just have to
-	   always enable IO to these devices.  */
-	if ((dev->class >> 8) == PCI_CLASS_NOT_DEFINED
-	    || (dev->class >> 8) == PCI_CLASS_NOT_DEFINED_VGA
-	    || (dev->class >> 8) == PCI_CLASS_STORAGE_IDE
-	    || (dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) {
-		cmd |= PCI_COMMAND_IO;
-	}
-
-	/* Do not enable bus mastering.  A device could corrupt
-	 * system memory by DMAing before a driver is ready for it. */
-
-	/* Set the cache line and default latency (32).  */
-	pci_write_config_word(dev, PCI_CACHE_LINE_SIZE,
-			(32 << 8) | (L1_CACHE_BYTES / sizeof(u32)));
-
-	/* Enable the appropriate bits in the PCI command register.  */
-	pci_write_config_word(dev, PCI_COMMAND, cmd);
-
-	DBGC((KERN_ERR "  cmd reg 0x%x\n", cmd));
-}
diff -urpN 2.5.1/include/asm-alpha/machvec.h linux/include/asm-alpha/machvec.h
--- 2.5.1/include/asm-alpha/machvec.h	Fri Dec 21 21:29:03 2001
+++ linux/include/asm-alpha/machvec.h	Fri Dec 21 21:29:43 2001
@@ -18,7 +18,7 @@ struct task_struct;
 struct mm_struct;
 struct pt_regs;
 struct vm_area_struct;
-struct linux_hose_info;
+struct pci_bus;
 struct pci_dev;
 struct pci_ops;
 struct pci_controller;
@@ -79,6 +79,7 @@ struct alpha_machine_vector
 	void (*ack_irq)(unsigned long);
 	void (*device_interrupt)(unsigned long vector, struct pt_regs *regs);
 	void (*machine_check)(u64 vector, u64 la, struct pt_regs *regs);
+	u16 (*pci_control)(struct pci_bus *);
 
 	void (*init_arch)(void);
 	void (*init_irq)(void);
diff -urpN 2.5.1/include/asm-i386/pci.h linux/include/asm-i386/pci.h
--- 2.5.1/include/asm-i386/pci.h	Fri Dec 21 21:29:03 2001
+++ linux/include/asm-i386/pci.h	Fri Dec 21 21:29:43 2001
@@ -263,6 +263,11 @@ static inline int pci_controller_num(str
 	return 0;
 }
 
+/* Set up bus control bits (FBB/PERR/SERR etc.) supported by PCI controller. */
+static inline void pcibios_init_bus(struct pci_bus *bus)
+{
+}
+
 #define HAVE_PCI_MMAP
 extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
 			       enum pci_mmap_state mmap_state, int write_combine);
diff -urpN 2.5.1/include/linux/ioport.h linux/include/linux/ioport.h
--- 2.5.1/include/linux/ioport.h	Fri Dec 21 21:29:03 2001
+++ linux/include/linux/ioport.h	Fri Dec 21 21:29:43 2001
@@ -40,7 +40,6 @@ struct resource_list {
 #define IORESOURCE_CACHEABLE	0x00004000
 #define IORESOURCE_RANGELENGTH	0x00008000
 #define IORESOURCE_SHADOWABLE	0x00010000
-#define IORESOURCE_BUS_HAS_VGA	0x00080000
 
 #define IORESOURCE_UNSET	0x20000000
 #define IORESOURCE_AUTO		0x40000000
diff -urpN 2.5.1/include/linux/pci.h linux/include/linux/pci.h
--- 2.5.1/include/linux/pci.h	Fri Dec 21 21:29:03 2001
+++ linux/include/linux/pci.h	Fri Dec 21 21:29:43 2001
@@ -429,6 +429,8 @@ struct pci_bus {
 	unsigned char	productver;	/* product version */
 	unsigned char	checksum;	/* if zero - checksum passed */
 	unsigned char	pad1;
+	unsigned short  bridge_ctl;	/* manage NO_ISA/FBB/et al behaviors */
+	unsigned short  pad2;
 };
 
 #define pci_bus_b(n) list_entry(n, struct pci_bus, node)
@@ -491,8 +493,10 @@ struct pci_driver {
 #define pci_for_each_dev(dev) \
 	for(dev = pci_dev_g(pci_devices.next); dev != pci_dev_g(&pci_devices); dev = pci_dev_g(dev->global_list.next))
 
-void pcibios_init(void);
-void pcibios_fixup_bus(struct pci_bus *);
+/* pcibios_*() == arch specific implementation */
+void pcibios_init(void);			/* global init */
+void pcibios_init_bus(struct pci_bus *);	/* before each bus scan */
+void pcibios_fixup_bus(struct pci_bus *);	/* after each bus scan */
 int pcibios_enable_device(struct pci_dev *);
 char *pcibios_setup (char *str);
 
@@ -575,9 +579,7 @@ int pci_enable_wake(struct pci_dev *dev,
 
 int pci_claim_resource(struct pci_dev *, int);
 void pci_assign_unassigned_resources(void);
-void pdev_enable_device(struct pci_dev *);
 void pdev_sort_resources(struct pci_dev *, struct resource_list *, u32);
-unsigned long pci_bridge_check_io(struct pci_dev *);
 void pci_fixup_irqs(u8 (*)(struct pci_dev *, u8 *),
 		    int (*)(struct pci_dev *, u8, u8));
 #define HAVE_PCI_REQ_REGIONS

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [patch] 2.5: PCI setup reorg (alpha, arm, parisc)
  2001-12-22 16:05 [patch] 2.5: PCI setup reorg (alpha, arm, parisc) Ivan Kokshaysky
@ 2001-12-22 17:15 ` Russell King
  2001-12-24 10:38   ` Ivan Kokshaysky
  0 siblings, 1 reply; 3+ messages in thread
From: Russell King @ 2001-12-22 17:15 UTC (permalink / raw)
  To: Ivan Kokshaysky; +Cc: linux-kernel

On Sat, Dec 22, 2001 at 07:05:48PM +0300, Ivan Kokshaysky wrote:
> Only generic and alpha changes here - parisc folks should be almost in
> sync, and required ARM changes look more or less straightforward.

What's happening about prefetchable memory regions?  You said in your
previous emails on this subject that it would be covered in your up
and coming patch (which I believe this is), but I haven't seen any
mention of it, nor do I see any sign of it in the code.

Thanks.

--
Russell King (rmk@arm.linux.org.uk)                The developer of ARM Linux
             http://www.arm.linux.org.uk/personal/aboutme.html


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [patch] 2.5: PCI setup reorg (alpha, arm, parisc)
  2001-12-22 17:15 ` Russell King
@ 2001-12-24 10:38   ` Ivan Kokshaysky
  0 siblings, 0 replies; 3+ messages in thread
From: Ivan Kokshaysky @ 2001-12-24 10:38 UTC (permalink / raw)
  To: Russell King; +Cc: linux-kernel

On Sat, Dec 22, 2001 at 05:15:38PM +0000, Russell King wrote:
> What's happening about prefetchable memory regions?  You said in your
> previous emails on this subject that it would be covered in your up
> and coming patch (which I believe this is), but I haven't seen any
> mention of it, nor do I see any sign of it in the code.

You are right, it wasn't included, because I would like to see that
one integrated first. :-)
Anyway, appended here, applies on the top of previous patch.
Some more work is needed though - we should handle specific pci-pci bridge
quirks. I think we need something like this in the struct pci_bus:

	unsigned short  bridge_ctl;	/* manage NO_ISA/FBB/et al behaviors */
-	unsigned short  pad2;
+	unsigned short  bridge_quirks;
};

so that chip specific routines could pass info to the generic code.
Currently I have one candidate - DECchip 21050 (very first pci-pci bridge,
I think). It has address boundary disconnect problem causing data corruption,
and most reliable workaround seems to be disabling prefetchable window.

Ivan.

--- linux/drivers/pci/setup-bus.c.orig	Fri Dec 21 21:33:58 2001
+++ linux/drivers/pci/setup-bus.c	Mon Dec 24 13:01:52 2001
@@ -44,7 +44,8 @@
 
 static int __init
 pbus_assign_resources_sorted(struct pci_bus *bus,
-			     struct pbus_set_ranges_data *ranges)
+			     struct pbus_set_ranges_data *ranges,
+			     int pfmem)
 {
 	struct list_head *ln;
 	struct resource *res;
@@ -57,6 +58,12 @@ pbus_assign_resources_sorted(struct pci_
 		struct pci_dev *dev = pci_dev_b(ln);
 		u16 class = dev->class >> 8;
 
+		if (pfmem) {
+			pdev_sort_resources(dev, &head_mem,
+						 IORESOURCE_PREFETCH);
+			continue;
+		}
+
 		found_vga |= (class == PCI_CLASS_DISPLAY_VGA ||
 			      class == PCI_CLASS_NOT_DEFINED_VGA);
 
@@ -72,22 +79,25 @@ pbus_assign_resources_sorted(struct pci_
 		pdev_sort_resources(dev, &head_mem, IORESOURCE_MEM);
 	}
 
-	for (list = head_io.next; list;) {
+	for (list = head_mem.next; list;) {
 		res = list->res;
 		idx = res - &list->dev->resource[0];
 		if (pci_assign_resource(list->dev, idx) == 0
-		    && ranges->io_end < res->end)
-			ranges->io_end = res->end;
+		    && ranges->mem_end < res->end)
+			ranges->mem_end = res->end;
 		tmp = list;
 		list = list->next;
 		kfree(tmp);
 	}
-	for (list = head_mem.next; list;) {
+	if (pfmem)
+		goto pfmem_done;
+
+	for (list = head_io.next; list;) {
 		res = list->res;
 		idx = res - &list->dev->resource[0];
 		if (pci_assign_resource(list->dev, idx) == 0
-		    && ranges->mem_end < res->end)
-			ranges->mem_end = res->end;
+		    && ranges->io_end < res->end)
+			ranges->io_end = res->end;
 		tmp = list;
 		list = list->next;
 		kfree(tmp);
@@ -97,6 +107,8 @@ pbus_assign_resources_sorted(struct pci_
 	ranges->mem_end += mem_reserved;
 
 	ranges->io_end = ROUND_UP(ranges->io_end, 4*1024);
+
+ pfmem_done:
 	ranges->mem_end = ROUND_UP(ranges->mem_end, 1024*1024);
 
 	return found_vga;
@@ -119,9 +131,12 @@ pci_setup_bridge(struct pci_bus *bus)
 	ranges.mem_end = bus->resource[1]->end;
 	pcibios_fixup_pbus_ranges(bus, &ranges);
 
-	DBGC((KERN_ERR "PCI: Bus %d, bridge: %s\n", bus->number, bridge->name));
-	DBGC((KERN_ERR "  IO window: %04lx-%04lx\n", ranges.io_start, ranges.io_end));
-	DBGC((KERN_ERR "  MEM window: %08lx-%08lx\n", ranges.mem_start, ranges.mem_end));
+	DBGC((KERN_INFO "PCI: Bus %d, bridge: %s\n",
+			bus->number, bridge->name));
+	DBGC((KERN_INFO "  IO window: %04lx-%04lx\n",
+			ranges.io_start, ranges.io_end));
+	DBGC((KERN_INFO "  MEM window: %08lx-%08lx\n",
+			ranges.mem_start, ranges.mem_end));
 
 	/* Set up the top and bottom of the PCI I/O segment for this bus. */
 	pci_read_config_dword(bridge, PCI_IO_BASE, &l);
@@ -146,8 +161,18 @@ pci_setup_bridge(struct pci_bus *bus)
 	l |= ranges.mem_end & 0xfff00000;
 	pci_write_config_dword(bridge, PCI_MEMORY_BASE, l);
 
-	/* Disable PREF memory range. */
-	pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0x0000fff0);
+	/* Set up the top and bottom of the PCI Prefetchable Memory segment
+	   for this bus. */
+	ranges.mem_start = bus->resource[2]->start;
+	ranges.mem_end = bus->resource[2]->end;
+	pcibios_fixup_pbus_ranges(bus, &ranges);
+
+	DBGC((KERN_INFO "  PREF MEM window: %08lx-%08lx\n",
+			ranges.mem_start, ranges.mem_end));
+
+	l = (ranges.mem_start >> 16) & 0xfff0;
+	l |= ranges.mem_end & 0xfff00000;
+	pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, l);
 
 	pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, bus->bridge_ctl);
 
@@ -155,13 +180,15 @@ pci_setup_bridge(struct pci_bus *bus)
 	pci_set_master(bridge);
 }
 
-/* Check whether the bridge supports I/O forwarding.
-   If not, its I/O base/limit register must be
-   read-only and read as 0. */
+/* Check whether the bridge supports optional I/O and
+   prefetchable memory ranges. If not, the respective
+   base/limit registers must be read-only and read as 0. */
 static unsigned long __init
-pci_bridge_check_io(struct pci_dev *bridge)
+pci_bridge_check_ranges(struct pci_dev *bridge)
 {
 	u16 io;
+	u32 pmem;
+	unsigned long ret = 0;
 
 	pci_read_config_word(bridge, PCI_IO_BASE, &io);
 	if (!io) {
@@ -170,29 +197,32 @@ pci_bridge_check_io(struct pci_dev *brid
 		pci_write_config_word(bridge, PCI_IO_BASE, 0x0);
 	}
 	if (io)
-		return IORESOURCE_IO;
-	printk(KERN_WARNING "PCI: bridge %s does not support I/O forwarding!\n",
-				bridge->name);
-	return 0;
+		ret |= IORESOURCE_IO;
+	pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem);
+	if (!pmem) {
+		pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE,
+					       0xfff0fff0);
+		pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem);
+		pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0x0);
+	}
+	if (pmem)
+		ret |= IORESOURCE_PREFETCH;
+	return ret;
 }
 
 static void __init
-pbus_assign_resources(struct pci_bus *bus, struct pbus_set_ranges_data *ranges)
+pbus_assign_pfmem_resources(struct pci_bus *bus,
+			    struct pbus_set_ranges_data *ranges)
 {
 	struct list_head *ln;
-	int found_vga = pbus_assign_resources_sorted(bus, ranges);
 
-	if (!ranges->found_vga && found_vga) {
-		struct pci_bus *b;
+	pbus_assign_resources_sorted(bus, ranges, 1);
 
-		ranges->found_vga = 1;
-		/* Propagate presence of the VGA to upstream bridges */
-		for (b = bus; b->parent; b = b->parent)
-			b->bridge_ctl |= PCI_BRIDGE_CTL_VGA;
-	}
 	for (ln=bus->children.next; ln != &bus->children; ln=ln->next) {
 		struct pci_bus *b = pci_bus_b(ln);
 		struct pci_dev *bridge = b->self;
+		struct resource *parent;
+		unsigned long flags = pci_bridge_check_ranges(bridge);
 		int i;
 
 		/* Link bus resources to the bridge ones.  */
@@ -201,8 +231,49 @@ pbus_assign_resources(struct pci_bus *bu
 				&bridge->resource[PCI_BRIDGE_RESOURCES + i];
 			b->resource[i]->name = bus->name;
 		}
-		b->resource[0]->flags |= pci_bridge_check_io(bridge);
+		b->resource[0]->flags |= flags & IORESOURCE_IO;
+		b->resource[2]->flags |= flags & IORESOURCE_PREFETCH;
+
+		b->resource[2]->start = ranges->mem_start = ranges->mem_end;
+
+		/* For now, set prefetcheble memory limit of this bus
+		   same as limit of its parent bus. If the parent bus
+		   doesn't support prefetch, use non-prefetchable area. */
+		parent = bus->resource[2] ? bus->resource[2] : bus->resource[1];
+		b->resource[2]->end = parent->end;
+
+		pbus_assign_pfmem_resources(b, ranges);
+
+		b->resource[2]->end = ranges->mem_end - 1;
+
+		/* Add this to the resource tree. */
+		if (b->resource[2]->end > b->resource[2]->start &&
+		    request_resource(parent, b->resource[2]) < 0)
+			printk(KERN_ERR "PCI: failed to request PREF MEM "
+					"for bus %d\n", b->number);
+	}
+}
+
+static void __init
+pbus_assign_iomem_resources(struct pci_bus *bus,
+			    struct pbus_set_ranges_data *ranges)
+{
+	struct list_head *ln;
+	int found_vga = pbus_assign_resources_sorted(bus, ranges, 0);
+
+	if (!ranges->found_vga && found_vga) {
+		struct pci_bus *b;
+
+		ranges->found_vga = 1;
+		/* Propagate presence of the VGA to upstream bridges */
+		for (b = bus; b->parent; b = b->parent)
+			b->bridge_ctl |= PCI_BRIDGE_CTL_VGA;
+	}
+	for (ln=bus->children.next; ln != &bus->children; ln=ln->next) {
+		struct pci_bus *b = pci_bus_b(ln);
+
 		b->resource[1]->flags |= IORESOURCE_MEM;
+
 		b->resource[0]->start = ranges->io_start = ranges->io_end;
 		b->resource[1]->start = ranges->mem_start = ranges->mem_end;
 
@@ -211,7 +282,7 @@ pbus_assign_resources(struct pci_bus *bu
 		b->resource[0]->end = bus->resource[0]->end;
 		b->resource[1]->end = bus->resource[1]->end;
 
-		pbus_assign_resources(b, ranges);
+		pbus_assign_iomem_resources(b, ranges);
 
 		/* PCI-to-PCI Bridge Architecture Specification rev. 1.1 (1998)
 		   requires that if there is no I/O ports or memory behind the
@@ -244,12 +315,24 @@ pci_assign_unassigned_resources(void)
 
 	for(ln=pci_root_buses.next; ln != &pci_root_buses; ln=ln->next) {
 		struct pci_bus *b = pci_bus_b(ln);
+		struct resource *pfmem = b->resource[2];
+		unsigned long memstart;
+
+		memstart = b->resource[1]->start + PCIBIOS_MIN_MEM;
+
+		/* First, allocate prefetchable memory resources.
+		   If the root bus doesn't have prefetching region,
+		   use regular PCI memory area. */
+		ranges.mem_start = pfmem ? pfmem->start : memstart;
+		ranges.mem_end = ranges.mem_start;
+		pbus_assign_pfmem_resources(b, &ranges);
 
 		ranges.io_start = b->resource[0]->start + PCIBIOS_MIN_IO;
-		ranges.mem_start = b->resource[1]->start + PCIBIOS_MIN_MEM;
 		ranges.io_end = ranges.io_start;
-		ranges.mem_end = ranges.mem_start;
 		ranges.found_vga = 0;
-		pbus_assign_resources(b, &ranges);
+		if (pfmem)
+			ranges.mem_end = memstart;
+		ranges.mem_start = ranges.mem_end;
+		pbus_assign_iomem_resources(b, &ranges);
 	}
 }

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2001-12-24 10:39 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2001-12-22 16:05 [patch] 2.5: PCI setup reorg (alpha, arm, parisc) Ivan Kokshaysky
2001-12-22 17:15 ` Russell King
2001-12-24 10:38   ` Ivan Kokshaysky

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

Powered by JetHome