Re: [PATCH] bogo-resource-management for 2.2.15 bios32.c (Miata+SRM+IDE fix)

From: Jay Estabrook (Jay.Estabrook@compaq.com)
Date: Thu May 11 2000 - 11:44:59 EST


On Wed, May 10, 2000 at 05:23:08PM -0400, David Huggins-Daines wrote:
>
> Certain Miatas (most notably the 600au) have a problem where the SRM
> console will configure the Cypress PCI-IDE interface's I/O space at
> 0x90a0.
>
> Because the 2.2 kernel ignores the Cypress chip completely when doing
> PCI bus layout, it has a bad habit of putting the Qlogic-ISP's I/O
> space controller at 0x9000. If one enables the PCI IDE driver, it
> claims this space to the exclusion of the SCSI host adaptor, with
> predictable results...
>
> This patch tries to avoid placing devices on top of the Cypress chip's
> I/O space.

Much better (and simpler) to do The Right Thing (tm), and *not* ignore
the appropriate BAR initializations (see below).

> This patch is admittedly bogus, but so is bios32.c (or, arguably, so
> is the SRM console on Miata, or the Cypress chip, or any number of
> other things :-) 2.3 does things right, thankfully.

Well, it is really a case of bios32 being negligent; fixed below.

Note that the patches below also contain a few other things, primary
being code that saves and restores any PCI-PCI bridge settings for
boxes booted from SRM console. This code is necessary specifically to
prevent hanging certain platforms during shutdown when equipped with
MYLEX ExtremeRAID 1100, but may be useful generically as well...

Please, give them a try and let me know what happens...

--Jay++

-----------------------------------------------------------------------------
Jay A Estabrook Alpha Engineering - LINUX Project
Compaq Computer Corp. - MRO1-2/K20 (508) 467-2080
200 Forest Street, Marlboro MA 01752 Jay.Estabrook@compaq.com
-----------------------------------------------------------------------------

diff -urP old/arch/alpha/kernel/bios32.c new/arch/alpha/kernel/bios32.c
--- old/arch/alpha/kernel/bios32.c Fri May 5 12:00:46 2000
+++ new/arch/alpha/kernel/bios32.c Thu May 11 12:13:29 2000
@@ -327,6 +327,28 @@
  */
 #define ALIGN(val,align) (((val) + ((align) - 1)) & ~((align) - 1))
 
+static short __inline__
+__disable_dev(struct pci_dev *dev)
+{
+ unsigned short cmd, orig_cmd;
+
+ pcibios_read_config_word(dev->bus->number, dev->devfn,
+ PCI_COMMAND, &cmd);
+
+ orig_cmd = cmd;
+ cmd &= (~PCI_COMMAND_IO & ~PCI_COMMAND_MEMORY & ~PCI_COMMAND_MASTER);
+
+ pcibios_write_config_word(dev->bus->number, dev->devfn,
+ PCI_COMMAND, cmd);
+ return orig_cmd;
+}
+
+static void __inline__
+__enable_dev(struct pci_dev *dev, short orig_cmd)
+{
+ pcibios_write_config_word(dev->bus->number, dev->devfn,
+ PCI_COMMAND, orig_cmd);
+}
 
 /*
  * The following structure records initial configuration of devices
@@ -344,6 +366,7 @@
         struct srm_io_reset *next;
         struct pci_dev *dev;
         u32 io;
+ short cmd;
         u8 reg;
 } *srm_io_resets;
 
@@ -354,6 +377,7 @@
 {
         struct srm_irq_reset *qreset;
         struct srm_io_reset *ireset;
+ struct pci_dev *last_dev;
 
         /* Reset any IRQs that we changed. */
         for (qreset = srm_irq_resets; qreset ; qreset = qreset->next) {
@@ -370,6 +394,15 @@
 #endif
         }
 
+ /* Disable any devices which had IO addresses that we changed. */
+ last_dev = NULL;
+ for (ireset = srm_io_resets; ireset ; ireset = ireset->next) {
+ if (ireset->dev != last_dev) {
+ ireset->cmd = __disable_dev(ireset->dev);
+ last_dev = ireset->dev;
+ }
+ }
+
         /* Reset any IO addresses that we changed. */
         for (ireset = srm_io_resets; ireset ; ireset = ireset->next) {
                 pcibios_write_config_dword(ireset->dev->bus->number,
@@ -383,31 +416,40 @@
                        ireset->io);
 #endif
         }
+
+ /* Re-enable any devices which had IO addresses that we changed. */
+ last_dev = NULL;
+ for (ireset = srm_io_resets; ireset ; ireset = ireset->next) {
+ if (ireset->dev != last_dev) {
+ __enable_dev(ireset->dev, ireset->cmd);
+ last_dev = ireset->dev;
+ }
+ }
 }
 
 static void
 new_irq_reset(struct pci_dev *dev, u8 irq)
 {
- struct srm_irq_reset *n;
- n = kmalloc(sizeof(*n), GFP_KERNEL);
+ struct srm_irq_reset *new;
+ new = kmalloc(sizeof(*new), GFP_KERNEL);
 
- n->next = srm_irq_resets;
- n->dev = dev;
- n->irq = irq;
- srm_irq_resets = n;
+ new->next = srm_irq_resets;
+ new->dev = dev;
+ new->irq = irq;
+ srm_irq_resets = new;
 }
 
 static void
 new_io_reset(struct pci_dev *dev, u8 reg, u32 io)
 {
- struct srm_io_reset *n;
- n = kmalloc(sizeof(*n), GFP_KERNEL);
+ struct srm_io_reset *new;
+ new = kmalloc(sizeof(*new), GFP_KERNEL);
 
- n->next = srm_io_resets;
- n->dev = dev;
- n->reg = reg;
- n->io = io;
- srm_io_resets = n;
+ new->next = srm_io_resets;
+ new->dev = dev;
+ new->reg = reg;
+ new->io = io;
+ srm_io_resets = new;
 }
 
 
@@ -418,9 +460,6 @@
 static void __init
 disable_dev(struct pci_dev *dev)
 {
- struct pci_bus *bus;
- unsigned short cmd;
-
         /*
          * HACK: the PCI-to-EISA bridge does not seem to identify
          * itself as a bridge... :-(
@@ -442,10 +481,11 @@
         /*
          * We don't have code that will init the CYPRESS bridge correctly
          * so we do the next best thing, and depend on the previous
- * console code to do the right thing, and ignore it here... :-\
+ * console code to do the right thing, and ignore it mostly... :-\
          */
         if (dev->vendor == PCI_VENDOR_ID_CONTAQ &&
- dev->device == PCI_DEVICE_ID_CONTAQ_82C693) {
+ dev->device == PCI_DEVICE_ID_CONTAQ_82C693 &&
+ PCI_FUNC(dev->devfn) == 0) {
                 DBG_DEVS(("disable_dev: ignoring CYPRESS bridge...\n"));
                 return;
         }
@@ -463,12 +503,7 @@
         DBG_DEVS(("disable_dev: disabling %04x:%04x\n",
                   dev->vendor, dev->device));
 
- bus = dev->bus;
- pcibios_read_config_word(bus->number, dev->devfn, PCI_COMMAND, &cmd);
-
- /* hack, turn it off first... */
- cmd &= (~PCI_COMMAND_IO & ~PCI_COMMAND_MEMORY & ~PCI_COMMAND_MASTER);
- pcibios_write_config_word(bus->number, dev->devfn, PCI_COMMAND, cmd);
+ (void)__disable_dev(dev);
 }
 
 
@@ -489,6 +524,7 @@
         unsigned int orig_base;
         unsigned int alignto;
         unsigned long handle;
+ int start_idx = 0;
 
         /*
          * HACK: the PCI-to-EISA bridge does not seem to identify
@@ -511,18 +547,24 @@
         /*
          * We don't have code that will init the CYPRESS bridge correctly
          * so we do the next best thing, and depend on the previous
- * console code to do the right thing, and ignore it here... :-\
+ * console code to do the right thing, and ignore it mostly... :-\
          */
         if (dev->vendor == PCI_VENDOR_ID_CONTAQ &&
             dev->device == PCI_DEVICE_ID_CONTAQ_82C693) {
+ int func = PCI_FUNC(dev->devfn);
+ if (func == 0) {
                 DBG_DEVS(("layout_dev: ignoring CYPRESS bridge...\n"));
                 return;
+ }
+ if (func == 1 || func == 2) {
+ start_idx = 4; /* bypass BAR 0 - 3 for the IDE devs */
+ }
         }
 
         bus = dev->bus;
         pcibios_read_config_word(bus->number, dev->devfn, PCI_COMMAND, &cmd);
 
- for (idx = 0; idx <= 5; idx++) {
+ for (idx = start_idx; idx <= 5; idx++) {
                 off = PCI_BASE_ADDRESS_0 + 4*idx;
                 /*
                  * Figure out how much space and of what type this
@@ -713,6 +755,46 @@
                   dev->device, dev->class, cmd|PCI_COMMAND_MASTER));
 }
 
+/* We must save away the current bridge settings for restore during exit. */
+static void __init
+save_bridge_setup(struct pci_dev *bridge)
+{
+ unsigned int dword;
+
+ pcibios_read_config_dword(bridge->bus->number, bridge->devfn,
+ PCI_IO_BASE, &dword);
+ new_io_reset(bridge, PCI_IO_BASE, dword);
+
+ pcibios_read_config_dword(bridge->bus->number, bridge->devfn,
+ PCI_IO_BASE_UPPER16, &dword);
+ new_io_reset(bridge, PCI_IO_BASE_UPPER16, dword);
+
+ pcibios_read_config_dword(bridge->bus->number, bridge->devfn,
+ PCI_PREF_BASE_UPPER32, &dword);
+ new_io_reset(bridge, PCI_PREF_BASE_UPPER32, dword);
+
+ pcibios_read_config_dword(bridge->bus->number, bridge->devfn,
+ PCI_PREF_LIMIT_UPPER32, &dword);
+ new_io_reset(bridge, PCI_PREF_LIMIT_UPPER32, dword);
+
+ pcibios_read_config_dword(bridge->bus->number, bridge->devfn,
+ PCI_MEMORY_BASE, &dword);
+ new_io_reset(bridge, PCI_MEMORY_BASE, dword);
+
+ pcibios_read_config_dword(bridge->bus->number, bridge->devfn,
+ PCI_PREF_MEMORY_BASE, &dword);
+ new_io_reset(bridge, PCI_PREF_MEMORY_BASE, dword);
+
+ /* Must use dword that contains PCI_BRIDGE_CONTROL. */
+ pcibios_read_config_dword(bridge->bus->number, bridge->devfn,
+ PCI_INTERRUPT_LINE, &dword);
+ new_io_reset(bridge, PCI_INTERRUPT_LINE, dword);
+
+ pcibios_read_config_dword(bridge->bus->number, bridge->devfn,
+ PCI_COMMAND, &dword);
+ new_io_reset(bridge, PCI_COMMAND, dword);
+}
+
 static int __init
 layout_bus(struct pci_bus *bus)
 {
@@ -764,6 +846,7 @@
                 if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA)
                         found_vga = 1;
         }
+
         /*
          * Recursively allocate space for all of the sub-buses:
          */
@@ -772,6 +855,7 @@
             for (child = bus->children; child; child = child->next) {
                 found_vga += layout_bus(child);
         }
+
         /*
          * Align the current bases on 4K and 1MB boundaries:
          */
@@ -783,6 +867,8 @@
 
                 DBG_DEVS(("layout_bus: config bus %d bridge\n", bus->number));
 
+ save_bridge_setup(bridge);
+
                 /*
                  * Set up the top and bottom of the PCI I/O segment
                  * for this bus.
@@ -1364,7 +1450,6 @@
 layout_hoses(void)
 {
         struct linux_hose_info * hose;
- int i;
 
         /* On multiple bus machines, we play games with pci_root in order
            that all of the busses are probed as part of the normal PCI
diff -urP old/arch/alpha/kernel/time.c new/arch/alpha/kernel/time.c
--- old/arch/alpha/kernel/time.c Fri May 5 12:00:03 2000
+++ new/arch/alpha/kernel/time.c Thu May 11 10:07:48 2000
@@ -236,7 +236,7 @@
 {
         void (*irq_handler)(int, void *, struct pt_regs *);
         unsigned int year, mon, day, hour, min, sec, cc1, cc2;
- unsigned long cycle_freq, one_percent;
+ unsigned long cycle_freq, ppm_error;
         long diff;
 
         /*
@@ -261,17 +261,24 @@
                 cc1 = cc2;
         }
 
- /* If the given value is within 1% of what we calculated,
- accept it. Otherwise, use what we found. */
+ /* This code used to check for a 1% error.
+ * PWS600au reports 598802395 which is way off. (ntpd has problems.)
+ * So I tightened down the check. Hal Murray, Feb 27, 2000.
+ */
         cycle_freq = hwrpb->cycle_freq;
- one_percent = cycle_freq / 100;
         diff = cycle_freq - est_cycle_freq;
         if (diff < 0)
                 diff = -diff;
- if (diff > one_percent) {
+ ppm_error = (diff * 1000000L) / cycle_freq;
+#if 0
+ printk("Alpha clock init: HWRPB %lu, Measured %lu, error=%lu ppm.\n",
+ hwrpb->cycle_freq, est_cycle_freq, ppm_error);
+#endif
+ if (ppm_error > 1000) {
+ printk("HWRPB cycle frequency (%lu) seems inaccurate -"
+ " using the measured value of %lu Hz\n",
+ cycle_freq, est_cycle_freq);
                 cycle_freq = est_cycle_freq;
- printk("HWRPB cycle frequency bogus. Estimated %lu Hz\n",
- cycle_freq);
         }
         else {
                 est_cycle_freq = 0;
diff -urP old/arch/alpha/vmlinux.lds new/arch/alpha/vmlinux.lds
--- old/arch/alpha/vmlinux.lds Wed May 14 01:41:00 1997
+++ new/arch/alpha/vmlinux.lds Thu May 11 10:07:48 2000
@@ -1,10 +1,11 @@
 OUTPUT_FORMAT("elf64-alpha")
 ENTRY(__start)
+PHDRS { kernel PT_LOAD ; }
 SECTIONS
 {
    . = 0xfffffc0000310000;
    _text = .;
- .text : { *(.text) }
+ .text : { *(.text) } :kernel
    .text2 : { *(.text2) }
    _etext = .;
 
-----------------------------------------------------------------------------



This archive was generated by hypermail 2b29 : Mon May 15 2000 - 21:00:29 EST