PCI fixes

Martin Mares (mj@atrey.karlin.mff.cuni.cz)
Thu, 9 Apr 1998 17:05:12 +0200


Hi,

Some more patches to PCI (against 2.1.94):

(1) /proc/bus/pci and /proc/pci again work as expected. I've moved
their initialization to the PCI init which is now possible as
the procfs is already initialized there.

(2) Killed the PCI_BASE_INDEX macro used in PCI probing on Alpha.
(and modified the code to not use it, of course).

(3) Added a "pci=reverse" option to reverse order of PCI probing.
This should solve the occasional ordering incompatibilities
with older kernels which used BIOS order.

(4) Very low latency timer values are now automatically raised
to 32.

(5) Automatically enabling I/O and memory whereever BIOS forgot
to do it (only on PC's) instead of just reporting them. If it
makes any troubles, please let me know.

(6) Reporting cards with unassigned IRQ's.

(7) Killed the "INIT IRQ" message.

Have a nice fortnight

-- 
Martin `MJ' Mares   <mj@ucw.cz>   http://atrey.karlin.mff.cuni.cz/~mj/
Faculty of Math and Physics, Charles University, Prague, Czech Rep., Earth
"If you are feeling good, don't worry. You'll get over it."

--- ./fs/proc/root.c.mj Thu Apr 9 15:14:36 1998 +++ ./fs/proc/root.c Thu Apr 9 15:18:20 1998 @@ -499,13 +499,6 @@ S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_array_inode_operations }; -#ifdef CONFIG_PCI_OLD_PROC -static struct proc_dir_entry proc_root_pci = { - PROC_PCI, 3, "pci", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations -}; -#endif #ifdef CONFIG_ZORRO static struct proc_dir_entry proc_root_zorro = { PROC_ZORRO, 5, "zorro", @@ -640,9 +633,6 @@ proc_register(&proc_root, &proc_root_meminfo); proc_register(&proc_root, &proc_root_kmsg); proc_register(&proc_root, &proc_root_version); -#ifdef CONFIG_PCI_OLD_PROC - proc_register(&proc_root, &proc_root_pci); -#endif #ifdef CONFIG_ZORRO proc_register(&proc_root, &proc_root_zorro); #endif @@ -707,9 +697,6 @@ #endif proc_bus = create_proc_entry("bus", S_IFDIR, 0); -#ifdef CONFIG_PCI - proc_bus_pci_init(); -#endif } /* --- ./include/linux/pci.h.mj Thu Apr 9 15:00:06 1998 +++ ./include/linux/pci.h Thu Apr 9 15:21:03 1998 @@ -977,9 +977,6 @@ #ifdef __KERNEL__ -/* Create an index into the pci_dev base_address[] array from an offset. */ -#define PCI_BASE_INDEX(o) (((o)-PCI_BASE_ADDRESS_0)>>2) - /* * Error values that may be returned by the PCI bios. Use * pcibios_strerror() to convert to a printable string. @@ -1078,6 +1075,8 @@ void pci_setup(char *str, int *ints); void pci_quirks_init(void); unsigned int pci_scan_bus(struct pci_bus *bus); +void proc_bus_pci_init(void); +void proc_old_pci_init(void); struct pci_dev *pci_find_device (unsigned int vendor, unsigned int device, struct pci_dev *from); struct pci_dev *pci_find_class (unsigned int class, struct pci_dev *from); --- ./include/linux/proc_fs.h.mj Thu Apr 9 15:18:29 1998 +++ ./include/linux/proc_fs.h Thu Apr 9 15:18:35 1998 @@ -275,7 +275,6 @@ extern void proc_root_init(void); extern void proc_base_init(void); -extern void proc_bus_pci_init(void); extern int proc_register(struct proc_dir_entry *, struct proc_dir_entry *); extern int proc_unregister(struct proc_dir_entry *, int); --- ./drivers/pci/pci.c.mj Thu Apr 9 15:04:29 1998 +++ ./drivers/pci/pci.c Thu Apr 9 16:45:28 1998 @@ -20,6 +20,7 @@ struct pci_bus pci_root; struct pci_dev *pci_devices = NULL; static struct pci_dev **pci_last_dev_p = &pci_devices; +static int pci_reverse __initdata = 0; #undef DEBUG @@ -193,8 +194,13 @@ * Put it into the global PCI device chain. It's used to * find devices once everything is set up. */ - *pci_last_dev_p = dev; - pci_last_dev_p = &dev->next; + if (!pci_reverse) { + *pci_last_dev_p = dev; + pci_last_dev_p = &dev->next; + } else { + dev->next = pci_devices; + pci_devices = dev; + } /* * Now insert it into the list of devices held @@ -204,6 +210,17 @@ bus->devices = dev; /* + * In case the latency timer value is less than 32, + * which makes everything very sllooowww, set it to + * 32. Pciutils should be used to fine-tune it later. + * Note that we don't check if the device is a bus-master: + * if it isn't, write to the latency timer should be ignored. + */ + pcibios_read_config_byte(bus->number, dev->devfn, PCI_LATENCY_TIMER, &tmp); + if (tmp < 32) + pcibios_write_config_byte(bus->number, dev->devfn, PCI_LATENCY_TIMER, 32); + + /* * If it's a bridge, scan the bus behind it. */ if (class >> 8 == PCI_CLASS_BRIDGE_PCI) { @@ -299,6 +316,7 @@ if (!pci_present()) { printk("PCI: No PCI bus detected\n"); + return; } printk("PCI: Probing PCI hardware.\n"); @@ -312,12 +330,30 @@ #ifdef CONFIG_PCI_OPTIMIZE pci_quirks_init(); #endif + +#ifdef CONFIG_PROC_FS + proc_bus_pci_init(); +#ifdef CONFIG_PCI_OLD_PROC + proc_old_pci_init(); +#endif +#endif } __initfunc(void pci_setup (char *str, int *ints)) { str = pcibios_setup(str); - if (*str) - printk(KERN_ERR "PCI: Unknown option `%s'\n", str); + while (str) { + char *k = strchr(str, ','); + if (k) + *k++ = 0; + if (*str) { + if (!(str = pcibios_setup(str)) || !*str) + continue; + if (!strcmp(str, "reverse")) + pci_reverse = 1; + else printk(KERN_ERR "PCI: Unknown option `%s'\n", str); + } + str = k; + } } --- ./drivers/pci/oldproc.c.mj Thu Apr 9 15:19:29 1998 +++ ./drivers/pci/oldproc.c Thu Apr 9 15:21:16 1998 @@ -13,6 +13,8 @@ #include <linux/pci.h> #include <linux/string.h> #include <linux/sched.h> +#include <linux/init.h> +#include <linux/proc_fs.h> #include <asm/page.h> #ifdef CONFIG_PROC_FS @@ -910,6 +912,17 @@ len += nprinted; } return len; +} + +static struct proc_dir_entry proc_old_pci = { + PROC_PCI, 3, "pci", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_array_inode_operations +}; + +__initfunc(void proc_old_pci_init(void)) +{ + proc_register(&proc_root, &proc_old_pci); } #endif /* CONFIG_PROC_FS */ --- ./arch/i386/kernel/bios32.c.mj Thu Apr 9 16:06:39 1998 +++ ./arch/i386/kernel/bios32.c Thu Apr 9 16:39:36 1998 @@ -834,44 +834,52 @@ __initfunc(void pcibios_fixup(void)) { struct pci_dev *dev; - int i, has_io; + int i, has_io, has_mem; + unsigned short cmd; + unsigned char pin; for(dev = pci_devices; dev; dev=dev->next) { /* - * There are probably some buggy BIOSes that forget to assign I/O port - * addresses to several devices. We probably should assign new addresses - * to such devices, but we need to gather some information first. [mj] + * There are buggy BIOSes that forget to enable I/O and memory + * access to PCI devices. We try to fix this, but we need to + * be sure that the BIOS didn't forget to assign an address + * to the device. [mj] */ - has_io = 0; + has_io = has_mem = 0; for(i=0; i<6; i++) { unsigned long a = dev->base_address[i]; if (a & PCI_BASE_ADDRESS_SPACE_IO) { - has_io = 1; + has_io |= 1; a &= PCI_BASE_ADDRESS_IO_MASK; - if (!a || a == PCI_BASE_ADDRESS_IO_MASK) + if (!a || a == PCI_BASE_ADDRESS_IO_MASK) { printk(KERN_WARNING "PCI: BIOS forgot to assign address #%d to device %02x:%02x," " please report to <mj@ucw.cz>\n", i, dev->bus->number, dev->devfn); - } + has_io |= 2; + } + } else if (a & PCI_BASE_ADDRESS_MEM_MASK) + has_mem = 1; } - /* - * Check if the I/O space access is allowed. If not, moan loudly. [mj] - */ - if (has_io) { - unsigned short cmd; - pci_read_config_word(dev, PCI_COMMAND, &cmd); - if (!(cmd & PCI_COMMAND_IO)) - printk(KERN_WARNING "PCI: BIOS forgot to enable I/O for device %02x:%02x," - " please report to <mj@ucw.cz>\n", dev->bus->number, dev->devfn); + pci_read_config_word(dev, PCI_COMMAND, &cmd); + if (has_io == 1 && !(cmd & PCI_COMMAND_IO)) { + printk("PCI: Enabling I/O for device %02x:%02x\n", + dev->bus->number, dev->devfn); + cmd |= PCI_COMMAND_IO; + pci_write_config_word(dev, PCI_COMMAND, cmd); } + if (has_mem && !(cmd & PCI_COMMAND_MEMORY)) { + printk("PCI: Enabling memory for device %02x:%02x\n", + dev->bus->number, dev->devfn); + cmd |= PCI_COMMAND_MEMORY; + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); #ifdef __SMP__ /* * Recalculate IRQ numbers if we use the I/O APIC */ { - unsigned char pin; int irq; - pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); if (pin) { pin--; /* interrupt pins are numbered starting from 1 */ irq = IO_APIC_get_PCI_irq_vector (dev->bus->number, PCI_SLOT(dev->devfn), pin); @@ -884,10 +892,13 @@ } #endif /* - * Fix out-of-range IRQ numbers. + * Fix out-of-range IRQ numbers and report bogus IRQ. */ if (dev->irq >= NR_IRQS) dev->irq = 0; + if (pin && !dev->irq) + printk(KERN_WARNING "PCI: Bogus IRQ for device %02x:%02x [pin=%x], please report to <mj@ucw.cz>\n", + dev->bus->number, dev->devfn, pin); } } @@ -919,25 +930,25 @@ { if (!strncmp(str, "off", 3)) { pci_probe = 0; - return str+3; + return NULL; } #ifdef CONFIG_PCI_BIOS else if (!strncmp(str, "bios", 4)) { pci_probe = PCI_PROBE_BIOS; - return str+4; + return NULL; } else if (!strncmp(str, "nobios", 6)) { pci_probe &= ~PCI_PROBE_BIOS; - return str+6; + return NULL; } #endif #ifdef CONFIG_PCI_DIRECT else if (!strncmp(str, "conf1", 5)) { pci_probe = PCI_PROBE_CONF1; - return str+5; + return NULL; } else if (!strncmp(str, "conf2", 5)) { pci_probe = PCI_PROBE_CONF2; - return str+5; + return NULL; } #endif return str; --- ./arch/i386/kernel/irq.c.mj Thu Apr 9 16:11:55 1998 +++ ./arch/i386/kernel/irq.c Thu Apr 9 16:12:02 1998 @@ -1073,7 +1073,6 @@ outb_p(LATCH & 0xff , 0x40); /* LSB */ outb(LATCH >> 8 , 0x40); /* MSB */ - printk("INIT IRQ\n"); for (i=0; i<NR_IRQS; i++) { irq_events[i] = 0; disabled_irq[i] = 0; --- ./arch/alpha/kernel/bios32.c.mj Thu Apr 9 14:58:00 1998 +++ ./arch/alpha/kernel/bios32.c Thu Apr 9 15:00:01 1998 @@ -250,7 +250,7 @@ { struct pci_bus *bus; unsigned short cmd; - unsigned int base, mask, size, off; + unsigned int base, mask, size, off, idx; unsigned int alignto; unsigned long handle; @@ -278,7 +278,8 @@ bus = dev->bus; pcibios_read_config_word(bus->number, dev->devfn, PCI_COMMAND, &cmd); - for (off = PCI_BASE_ADDRESS_0; off <= PCI_BASE_ADDRESS_5; off += 4) { + for (idx = 0; idx <= 5; idx++) { + off = PCI_BASE_ADDRESS_0 + 4*idx; /* * Figure out how much space and of what type this * device wants. @@ -288,7 +289,7 @@ pcibios_read_config_dword(bus->number, dev->devfn, off, &base); if (!base) { /* this base-address register is unused */ - dev->base_address[PCI_BASE_INDEX(off)] = 0; + dev->base_address[idx] = 0; continue; } @@ -324,7 +325,7 @@ off, base | 0x1); handle = HANDLE(bus->number) | base | 1; - dev->base_address[PCI_BASE_INDEX(off)] = handle; + dev->base_address[idx] = handle; DBG_DEVS(("layout_dev: dev 0x%x IO @ 0x%lx (0x%x)\n", dev->device, handle, size)); @@ -348,7 +349,7 @@ "slot %d, function %d: \n", PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); - off += 4; /* skip extra 4 bytes */ + idx++; /* skip extra 4 bytes */ continue; case PCI_BASE_ADDRESS_MEM_TYPE_1M: --- ./arch/alpha/kernel/apecs.c.mj Thu Apr 9 15:05:14 1998 +++ ./arch/alpha/kernel/apecs.c Thu Apr 9 15:05:14 1998 @@ -10,7 +10,6 @@ #include <linux/kernel.h> #include <linux/config.h> #include <linux/types.h> -#include <linux/bios32.h> #include <linux/pci.h> #include <asm/system.h>

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu