PCI subsystem cleanup

Martin Mares (mj@atrey.karlin.mff.cuni.cz)
Fri, 13 Feb 1998 15:44:56 +0100


Hello, world!

This is a major cleanup of the PCI subsystem. Changes:

- Searching for PCI devices by class or ID is now handled by the
generic code.

- ia32 IO-APIC IRQ handling moved to pcibios_fixup, pcibios_read_config_byte
still contains an exception for IRQ's, but when all drivers will be fixed,
it will go away.

- Added a "pci=..." kernel option, now used only on the PC where it
allows selection between using PCI BIOS, conf1 and conf2 accesses.

- Module symbols for PCI subsystem moved to separate file.

- Added new functions pci_... operating on pci_dev structure
(pci_find_device, pci_find_class, pci_find_slot, pci_(read|write)_...).

- /proc/bus/pci/... restricted to the standard header for normal users
to prevent system lockups on buggy HW.

- Added partial support for CardBus bridges (header type #2).

- Few remaining bits from bios32.h moved to pci.h. bios32.h now only
gives a #warning.

- Added a howto file on writing PCI drivers.

All drivers using the old interface (they can be simply identified as they
include bios32.h) should be modified to fetch IRQ's and base addresses from
struct pci_dev and preferably also use the new functions for probing.

If you have any bug reports or suggestions, send them to me.

Happy Hacking

-- 
Martin `MJ' Mares   <mj@ucw.cz>   http://atrey.karlin.mff.cuni.cz/~mj/
Faculty of Math and Physics, Charles University, Prague, Czech Rep., Earth
"29A, the hexadecimal of the Beast."

--- /mj/linux-2.1/arch/alpha/kernel/bios32.c Wed Jan 14 11:52:41 1998 +++ /mj/linux/arch/alpha/kernel/bios32.c Thu Feb 12 16:18:10 1998 @@ -53,7 +53,6 @@ #else /* CONFIG_PCI */ -#include <linux/bios32.h> #include <linux/pci.h> #include <linux/malloc.h> #include <linux/mm.h> @@ -476,55 +475,6 @@ #endif /* !PCI_MODIFY */ -/* - * Given the vendor and device ids, find the n'th instance of that device - * in the system. - */ -int pcibios_find_device (unsigned short vendor, unsigned short device_id, - unsigned short index, unsigned char *bus, - unsigned char *devfn) -{ - unsigned int curr = 0; - struct pci_dev *dev; - - for (dev = pci_devices; dev; dev = dev->next) { - if (dev->vendor == vendor && dev->device == device_id) { - if (curr == index) { - *devfn = dev->devfn; - *bus = dev->bus->number; - return PCIBIOS_SUCCESSFUL; - } - ++curr; - } - } - return PCIBIOS_DEVICE_NOT_FOUND; -} - - -/* - * Given the class, find the n'th instance of that device - * in the system. - */ -int pcibios_find_class (unsigned int class_code, unsigned short index, - unsigned char *bus, unsigned char *devfn) -{ - unsigned int curr = 0; - struct pci_dev *dev; - - for (dev = pci_devices; dev; dev = dev->next) { - if (dev->class == class_code) { - if (curr == index) { - *devfn = dev->devfn; - *bus = dev->bus->number; - return PCIBIOS_SUCCESSFUL; - } - ++curr; - } - } - return PCIBIOS_DEVICE_NOT_FOUND; -} - - int pcibios_present(void) { return 1; @@ -1985,5 +1937,10 @@ return 0; } #endif /* CONFIG_ALPHA_MIATA */ + +__initfunc(char *pcibios_setup(char *str)) +{ + return str; +} #endif /* CONFIG_PCI */ --- /mj/linux-2.1/init/main.c Fri Feb 13 14:00:00 1998 +++ /mj/linux/init/main.c Thu Feb 12 16:30:06 1998 @@ -40,6 +40,9 @@ #include <stdarg.h> +#ifdef CONFIG_PCI +#include <linux/pci.h> +#endif /* * Versions of gcc older than that listed below may actually compile @@ -66,6 +69,5 @@ extern long console_init(long, long); extern void sock_init(void); extern void uidcache_init(void); -extern unsigned long pci_init(unsigned long, unsigned long); extern long mca_init(long, long); extern long sbus_init(long, long); @@ -475,8 +483,10 @@ #ifdef __SMP__ { "nosmp", smp_setup }, { "maxcpus=", smp_setup }, +#ifdef __i386__ { "pirq=", ioapic_pirq_setup }, #endif +#endif #ifdef CONFIG_BLK_DEV_RAM { "ramdisk_start=", ramdisk_start_setup }, { "load_ramdisk=", load_ramdisk }, @@ -741,6 +749,9 @@ #ifdef CONFIG_IP_PNP { "ip=", ip_auto_config_setup }, #endif +#ifdef CONFIG_PCI + { "pci=", pci_setup }, +#endif #ifdef CONFIG_PARIDE_PD { "pd.", pd_setup }, #endif diff -ruN /mj/linux-2.1/drivers/pci/Makefile /mj/linux/drivers/pci/Makefile --- /mj/linux-2.1/drivers/pci/Makefile Fri Feb 13 13:57:35 1998 +++ /mj/linux/drivers/pci/Makefile Thu Feb 12 16:23:43 1998 @@ -9,8 +9,17 @@ # parent makefile. # -L_OBJS := pci.o L_TARGET := pci.a + +# Nasty trick as nobody references pcisyms.o, but we still want it linked. +ifeq ($(CONFIG_MODULES),y) +O_TARGET = pci_syms.o +OX_OBJS = pcisyms.o +O_OBJS = pci.o +L_OBJS := pci_syms.o +else +L_OBJS := pci.o +endif ifdef CONFIG_PROC_FS L_OBJS += proc.o diff -ruN /mj/linux-2.1/drivers/pci/oldproc.c /mj/linux/drivers/pci/oldproc.c --- /mj/linux-2.1/drivers/pci/oldproc.c Fri Feb 13 13:57:35 1998 +++ /mj/linux/drivers/pci/oldproc.c Fri Feb 6 20:57:35 1998 @@ -1,5 +1,5 @@ /* - * $Id: oldproc.c,v 1.4 1998/01/05 14:16:18 mj Exp $ + * $Id: oldproc.c,v 1.7 1998/02/06 19:51:34 mj Exp $ * * Backward-compatible procfs interface for PCI. * @@ -10,7 +10,6 @@ #include <linux/config.h> #include <linux/types.h> #include <linux/kernel.h> -#include <linux/bios32.h> #include <linux/pci.h> #include <linux/string.h> #include <linux/sched.h> @@ -253,6 +252,8 @@ DEVICE( ASP, ASP_ABP940U, "ABP940U"), DEVICE( CERN, CERN_SPSB_PMC, "STAR/RD24 SCI-PCI (PMC)"), DEVICE( CERN, CERN_SPSB_PCI, "STAR/RD24 SCI-PCI (PMC)"), + DEVICE( CERN, CERN_HIPPI_DST, "HIPPI destination"), + DEVICE( CERN, CERN_HIPPI_SRC, "HIPPI source"), DEVICE( IMS, IMS_8849, "8849"), DEVICE( TEKRAM2, TEKRAM2_690c, "DC690c"), DEVICE( TUNDRA, TUNDRA_CA91C042,"CA91C042 Universe"), @@ -345,6 +346,7 @@ DEVICE( RP, RP8INTF, "RocketPort 8 Intf"), DEVICE( RP, RP16INTF, "RocketPort 16 Intf"), DEVICE( RP, RP32INTF, "RocketPort 32 Intf"), + DEVICE( ESSENTIAL, ROADRUNNER, "RoadRunner serial HIPPI"), DEVICE( CYCLADES, CYCLOM_Y_Lo, "Cyclom-Y below 1Mbyte"), DEVICE( CYCLADES, CYCLOM_Y_Hi, "Cyclom-Y above 1Mbyte"), DEVICE( CYCLADES, CYCLOM_Z_Lo, "Cyclom-Z below 1Mbyte"), diff -ruN /mj/linux-2.1/drivers/pci/pci.c /mj/linux/drivers/pci/pci.c --- /mj/linux-2.1/drivers/pci/pci.c Fri Feb 13 13:57:35 1998 +++ /mj/linux/drivers/pci/pci.c Thu Feb 12 16:23:46 1998 @@ -1,16 +1,15 @@ /* - * $Id: pci.c,v 1.55 1997/12/27 12:17:54 mj Exp $ + * $Id: pci.c,v 1.64 1998/02/12 13:05:34 mj Exp $ * - * PCI services that are built on top of the BIOS32 service. + * PCI Bus Services * - * Copyright 1993, 1994, 1995, 1997 Drew Eckhardt, Frederic Potter, + * Copyright 1993 -- 1998 Drew Eckhardt, Frederic Potter, * David Mosberger-Tang, Martin Mares */ #include <linux/config.h> #include <linux/types.h> #include <linux/kernel.h> -#include <linux/bios32.h> #include <linux/pci.h> #include <linux/string.h> #include <linux/init.h> @@ -18,7 +17,7 @@ #include <asm/page.h> struct pci_bus pci_root; -struct pci_dev *pci_devices = 0; +struct pci_dev *pci_devices = NULL; #undef DEBUG @@ -68,6 +67,81 @@ } +struct pci_dev * +pci_find_slot(unsigned int bus, unsigned int devfn) +{ + struct pci_dev *dev; + + for(dev=pci_devices; dev; dev=dev->next) + if (dev->bus->number == bus && dev->devfn == devfn) + break; + return dev; +} + + +struct pci_dev * +pci_find_device(unsigned int vendor, unsigned int device, struct pci_dev *from) +{ + if (!from) + from = pci_devices; + else + from = from->next; + while (from && (from->vendor != vendor || from->device != device)) + from = from->next; + return from; +} + + +struct pci_dev * +pci_find_class(unsigned int class, struct pci_dev *from) +{ + if (!from) + from = pci_devices; + else + from = from->next; + while (from && from->class != class) + from = from->next; + return from; +} + + +#ifndef NO_COMPATIBILITY_GLUE + +int +pcibios_find_class(unsigned int class, unsigned short index, unsigned char *bus, unsigned char *devfn) +{ + struct pci_dev *dev = NULL; + int cnt = 0; + + while ((dev = pci_find_class(class, dev))) + if (index == cnt++) { + *bus = dev->bus->number; + *devfn = dev->devfn; + return PCIBIOS_SUCCESSFUL; + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + + +int +pcibios_find_device(unsigned short vendor, unsigned short device, unsigned short index, + unsigned char *bus, unsigned char *devfn) +{ + struct pci_dev *dev = NULL; + int cnt = 0; + + while ((dev = pci_find_device(vendor, device, dev))) + if (index == cnt++) { + *bus = dev->bus->number; + *devfn = dev->devfn; + return PCIBIOS_SUCCESSFUL; + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + +#endif + + unsigned int pci_scan_bus(struct pci_bus *bus, unsigned long *mem_startp) { unsigned int devfn, l, max, class; @@ -112,9 +186,10 @@ pcibios_read_config_dword(bus->number, devfn, PCI_CLASS_REVISION, &class); class >>= 8; /* upper 3 bytes */ dev->class = class; + dev->hdr_type = hdr_type; switch (hdr_type & 0x7f) { /* header type */ - case 0: /* standard header */ + case PCI_HEADER_TYPE_NORMAL: /* standard header */ if (class >> 8 == PCI_CLASS_BRIDGE_PCI) goto bad; /* read irq level (may be changed during pcibios_fixup()): */ @@ -129,7 +204,7 @@ dev->base_address[reg] = (l == 0xffffffff) ? 0 : l; } break; - case 1: /* bridge header */ + case PCI_HEADER_TYPE_BRIDGE: /* bridge header */ if (class >> 8 != PCI_CLASS_BRIDGE_PCI) goto bad; for (reg = 0; reg < 2; reg++) { @@ -137,6 +212,14 @@ dev->base_address[reg] = (l == 0xffffffff) ? 0 : l; } break; + case PCI_HEADER_TYPE_CARDBUS: /* CardBus bridge header */ + if (class >> 16 != PCI_BASE_CLASS_BRIDGE) + goto bad; + for (reg = 0; reg < 2; reg++) { + pcibios_read_config_dword(bus->number, devfn, PCI_CB_MEMORY_BASE_0 + (reg << 3), &l); + dev->base_address[reg] = (l == 0xffffffff) ? 0 : l; + } + break; default: /* unknown header */ bad: printk(KERN_ERR "PCI: %02x:%02x [%04x/%04x/%06x] has unknown header type %02x, ignoring.\n", @@ -258,7 +341,7 @@ return mem_start; } - printk("Probing PCI hardware.\n"); + printk("PCI: Probing PCI hardware.\n"); memset(&pci_root, 0, sizeof(pci_root)); pci_root.subordinate = pci_scan_bus(&pci_root, &mem_start); @@ -271,4 +354,12 @@ #endif return mem_start; +} + + +__initfunc(void pci_setup (char *str, int *ints)) +{ + str = pcibios_setup(str); + if (*str) + printk(KERN_ERR "PCI: Unknown option `%s'\n", str); } diff -ruN /mj/linux-2.1/drivers/pci/pcisyms.c /mj/linux/drivers/pci/pcisyms.c --- /mj/linux-2.1/drivers/pci/pcisyms.c Thu Jan 1 01:00:00 1970 +++ /mj/linux/drivers/pci/pcisyms.c Fri Feb 6 20:57:35 1998 @@ -0,0 +1,25 @@ +/* + * $Id: pcisyms.c,v 1.1 1998/02/06 19:51:38 mj Exp $ + * + * PCI Bus Services -- Exported Symbols + * + * Copyright 1998 Martin Mares + */ + +#include <linux/module.h> +#include <linux/pci.h> + +EXPORT_SYMBOL(pcibios_present); +EXPORT_SYMBOL(pcibios_find_class); +EXPORT_SYMBOL(pcibios_find_device); +EXPORT_SYMBOL(pcibios_read_config_byte); +EXPORT_SYMBOL(pcibios_read_config_word); +EXPORT_SYMBOL(pcibios_read_config_dword); +EXPORT_SYMBOL(pcibios_write_config_byte); +EXPORT_SYMBOL(pcibios_write_config_word); +EXPORT_SYMBOL(pcibios_write_config_dword); +EXPORT_SYMBOL(pcibios_strerror); +EXPORT_SYMBOL(pci_devices); +EXPORT_SYMBOL(pci_find_class); +EXPORT_SYMBOL(pci_find_device); +EXPORT_SYMBOL(pci_find_slot); Binary files /mj/linux-2.1/drivers/pci/pcisyms.o and /mj/linux/drivers/pci/pcisyms.o differ diff -ruN /mj/linux-2.1/drivers/pci/proc.c /mj/linux/drivers/pci/proc.c --- /mj/linux-2.1/drivers/pci/proc.c Fri Feb 13 13:57:35 1998 +++ /mj/linux/drivers/pci/proc.c Tue Feb 10 18:36:45 1998 @@ -1,15 +1,13 @@ /* - * $Id: proc.c,v 1.1 1997/12/22 17:22:31 mj Exp $ + * $Id: proc.c,v 1.6 1998/02/07 09:35:32 mj Exp $ * * Procfs interface for the PCI bus. * - * Copyright (c) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz> + * Copyright (c) 1997, 1998 Martin Mares <mj@atrey.karlin.mff.cuni.cz> */ -#include <linux/config.h> #include <linux/types.h> #include <linux/kernel.h> -#include <linux/bios32.h> #include <linux/pci.h> #include <linux/proc_fs.h> #include <linux/init.h> @@ -50,14 +48,27 @@ int pos = *ppos; unsigned char bus = dev->bus->number; unsigned char dfn = dev->devfn; - int cnt; + int cnt, size; - if (pos >= PCI_CFG_SPACE_SIZE) + /* + * Normal users can read only the standardized portion of the + * configuration space as several chips lock up when trying to read + * undefined locations (think of Intel PIIX4 as a typical example). + */ + + if (fsuser()) + size = PCI_CFG_SPACE_SIZE; + else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) + size = 128; + else + size = 64; + + if (pos >= size) return 0; - if (nbytes >= PCI_CFG_SPACE_SIZE) - nbytes = PCI_CFG_SPACE_SIZE; - if (pos + nbytes > PCI_CFG_SPACE_SIZE) - nbytes = PCI_CFG_SPACE_SIZE - pos; + if (nbytes >= size) + nbytes = size; + if (pos + nbytes > size) + nbytes = size - pos; cnt = nbytes; if (!access_ok(VERIFY_WRITE, buf, cnt)) diff -ruN /mj/linux-2.1/drivers/pci/quirks.c /mj/linux/drivers/pci/quirks.c --- /mj/linux-2.1/drivers/pci/quirks.c Mon Dec 22 02:27:18 1997 +++ /mj/linux/drivers/pci/quirks.c Fri Feb 6 20:57:42 1998 @@ -1,5 +1,5 @@ /* - * $Id: quirks.c,v 1.2 1997/09/20 21:43:34 davem Exp $ + * $Id: quirks.c,v 1.3 1998/02/06 19:51:42 mj Exp $ * * PCI Chipset-Specific Quirks * @@ -13,7 +13,6 @@ #include <linux/types.h> #include <linux/kernel.h> -#include <linux/bios32.h> #include <linux/pci.h> #include <linux/string.h> #include <linux/init.h> --- /mj/linux-2.1/include/linux/pci.h Fri Feb 13 13:57:37 1998 +++ /mj/linux/include/linux/pci.h Fri Feb 13 13:47:06 1998 @@ -1,5 +1,5 @@ /* - * $Id: pci.h,v 1.51 1997/12/27 13:55:23 mj Exp $ + * $Id: pci.h,v 1.58 1998/02/13 12:45:18 mj Exp $ * * PCI defines and function prototypes * Copyright 1994, Drew Eckhardt @@ -68,6 +68,10 @@ #define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */ #define PCI_LATENCY_TIMER 0x0d /* 8 bits */ #define PCI_HEADER_TYPE 0x0e /* 8 bits */ +#define PCI_HEADER_TYPE_NORMAL 0 +#define PCI_HEADER_TYPE_BRIDGE 1 +#define PCI_HEADER_TYPE_CARDBUS 2 + #define PCI_BIST 0x0f /* 8 bits */ #define PCI_BIST_CODE_MASK 0x0f /* Return result */ #define PCI_BIST_START 0x40 /* 1 to start BIST, 2 secs or less */ @@ -99,8 +103,8 @@ /* Header type 0 (normal devices) */ #define PCI_CARDBUS_CIS 0x28 -#define PCI_SUBSYSTEM_ID 0x2c -#define PCI_SUBSYSTEM_VENDOR_ID 0x2e +#define PCI_SUBSYSTEM_VENDOR_ID 0x2c +#define PCI_SUBSYSTEM_ID 0x2e #define PCI_ROM_ADDRESS 0x30 /* 32 bits */ #define PCI_ROM_ADDRESS_ENABLE 0x01 /* Write 1 to enable ROM, bits 31..11 are address, @@ -149,6 +153,36 @@ #define PCI_BRIDGE_CTL_BUS_RESET 0x40 /* Secondary bus reset */ #define PCI_BRIDGE_CTL_FAST_BACK 0x80 /* Fast Back2Back enabled on secondary interface */ +/* Header type 2 (CardBus bridges) -- detailed info welcome */ +#define PCI_CB_CARDBUS_BASE 0x10 /* CardBus Socket/ExCa base address */ +#define PCI_CB_CARDBUS_BASE_TYPE_MASK 0xfff +#define PCI_CB_CARDBUS_BASE_MASK ~0xfff +#define PCI_CB_CAPABILITIES 0x14 /* Offset of list of capabilities in cfg space */ +/* 0x15 reserved */ +#define PCI_CB_SEC_STATUS 0x16 /* Secondary status */ +#define PCI_CB_BUS_NUMBER 0x18 /* PCI bus number */ +#define PCI_CB_CARDBUS_NUMBER 0x19 /* CardBus bus number */ +#define PCI_CB_SUBORDINATE_BUS 0x1a /* Subordinate bus number */ +#define PCI_CB_CARDBUS_LATENCY 0x1b /* CardBus latency timer */ +#define PCI_CB_MEMORY_BASE_0 0x1c +#define PCI_CB_MEMORY_LIMIT_0 0x20 +#define PCI_CB_MEMORY_BASE_1 0x24 +#define PCI_CB_MEMORY_LIMIT_1 0x28 +#define PCI_CB_IO_BASE_0 0x2c +#define PCI_CB_IO_BASE_0_HI 0x2e +#define PCI_CB_IO_LIMIT_0 0x30 +#define PCI_CB_IO_LIMIT_0_HI 0x32 +#define PCI_CB_IO_BASE_1 0x34 +#define PCI_CB_IO_BASE_1_HI 0x36 +#define PCI_CB_IO_LIMIT_1 0x38 +#define PCI_CB_IO_LIMIT_1_HI 0x3a +/* 0x3c-0x3d are same as for htype 0 */ +/* 0x3e-0x3f are same as for htype 1 */ +#define PCI_CB_SUBSYSTEM_ID 0x40 +#define PCI_CB_SUBSYSTEM_VENDOR_ID 0x42 +#define PCI_CB_LEGACY_MODE_BASE 0x44 /* 16-bit PC Card legacy mode base address (ExCa) */ +/* 0x48-0x7f reserved */ + /* Device classes and subclasses */ #define PCI_CLASS_NOT_DEFINED 0x0000 @@ -577,6 +611,8 @@ #define PCI_VENDOR_ID_CERN 0x10dc #define PCI_DEVICE_ID_CERN_SPSB_PMC 0x0001 #define PCI_DEVICE_ID_CERN_SPSB_PCI 0x0002 +#define PCI_DEVICE_ID_CERN_HIPPI_DST 0x0021 +#define PCI_DEVICE_ID_CERN_HIPPI_SRC 0x0022 #define PCI_VENDOR_ID_NVIDIA 0x10de @@ -742,6 +778,9 @@ #define PCI_DEVICE_ID_CYCLOM_Z_Lo 0x0200 #define PCI_DEVICE_ID_CYCLOM_Z_Hi 0x0201 +#define PCI_VENDOR_ID_ESSENTIAL 0x120f +#define PCI_DEVICE_ID_ROADRUNNER 0x0001 + #define PCI_VENDOR_ID_3DFX 0x121a #define PCI_DEVICE_ID_3DFX_VOODOO 0x0001 @@ -888,6 +927,46 @@ #define PCI_FUNC(devfn) ((devfn) & 0x07) #ifdef __KERNEL__ + +/* + * Error values that may be returned by the PCI bios. Use + * pcibios_strerror() to convert to a printable string. + */ +#define PCIBIOS_SUCCESSFUL 0x00 +#define PCIBIOS_FUNC_NOT_SUPPORTED 0x81 +#define PCIBIOS_BAD_VENDOR_ID 0x83 +#define PCIBIOS_DEVICE_NOT_FOUND 0x86 +#define PCIBIOS_BAD_REGISTER_NUMBER 0x87 +#define PCIBIOS_SET_FAILED 0x88 +#define PCIBIOS_BUFFER_TOO_SMALL 0x89 + +/* Direct configuration space access */ + +int pcibios_present (void); +unsigned long pcibios_init (unsigned long memory_start, unsigned long memory_end); +unsigned long pcibios_fixup (unsigned long memory_start, unsigned long memory_end); +char *pcibios_setup (char *str); +int pcibios_read_config_byte (unsigned char bus, unsigned char dev_fn, + unsigned char where, unsigned char *val); +int pcibios_read_config_word (unsigned char bus, unsigned char dev_fn, + unsigned char where, unsigned short *val); +int pcibios_read_config_dword (unsigned char bus, unsigned char dev_fn, + unsigned char where, unsigned int *val); +int pcibios_write_config_byte (unsigned char bus, unsigned char dev_fn, + unsigned char where, unsigned char val); +int pcibios_write_config_word (unsigned char bus, unsigned char dev_fn, + unsigned char where, unsigned short val); +int pcibios_write_config_dword (unsigned char bus, unsigned char dev_fn, + unsigned char where, unsigned int val); +const char *pcibios_strerror (int error); + +/* Don't use these in new code, use pci_find_... instead */ + +int pcibios_find_class (unsigned int class_code, unsigned short index, unsigned char *bus, unsigned char *dev_fn); +int pcibios_find_device (unsigned short vendor, unsigned short dev_id, + unsigned short index, unsigned char *bus, + unsigned char *dev_fn); + /* * There is one pci_dev structure for each slot-number/function-number * combination: @@ -903,6 +982,7 @@ unsigned short vendor; unsigned short device; unsigned int class; /* 3 bytes: (base,sub,prog-if) */ + unsigned int hdr_type; /* PCI header type */ unsigned int master : 1; /* set if device is master capable */ /* * In theory, the irq level can be read from configuration @@ -942,13 +1022,24 @@ extern struct pci_bus pci_root; /* root bus */ extern struct pci_dev *pci_devices; /* list of all devices */ -extern unsigned long pci_init (unsigned long mem_start, unsigned long mem_end); - -extern unsigned int pci_scan_bus (struct pci_bus *bus, unsigned long *mem_startp); - -extern int get_pci_list (char *buf); +unsigned long pci_init (unsigned long mem_start, unsigned long mem_end); +void pci_setup (char *str, int *ints); +void pci_quirks_init (void); +unsigned int pci_scan_bus (struct pci_bus *bus, unsigned long *mem_startp); + +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); +struct pci_dev *pci_find_slot (unsigned int bus, unsigned int devfn); + +#define pci_present pcibios_present +#define pci_read_config_byte(dev, where, val) pcibios_read_config_byte(dev->bus->number, dev->devfn, where, val) +#define pci_read_config_word(dev, where, val) pcibios_read_config_word(dev->bus->number, dev->devfn, where, val) +#define pci_read_config_dword(dev, where, val) pcibios_read_config_dword(dev->bus->number, dev->devfn, where, val) +#define pci_write_config_byte(dev, where, val) pcibios_write_config_byte(dev->bus->number, dev->devfn, where, val) +#define pci_write_config_word(dev, where, val) pcibios_write_config_word(dev->bus->number, dev->devfn, where, val) +#define pci_write_config_dword(dev, where, val) pcibios_write_config_dword(dev->bus->number, dev->devfn, where, val) -extern void pci_quirks_init (void); +int get_pci_list (char *buf); #endif /* __KERNEL__ */ #endif /* LINUX_PCI_H */ diff -ruN /mj/linux-2.1/Documentation/pci.txt /mj/linux/Documentation/pci.txt --- /mj/linux-2.1/Documentation/pci.txt Thu Jan 1 01:00:00 1970 +++ /mj/linux/Documentation/pci.txt Fri Feb 13 14:22:19 1998 @@ -0,0 +1,60 @@ + Few Notes About The PCI Subsystem + + or + + "What should you avoid when writing PCI drivers" + + by Martin Mares <mj@atrey.karlin.mff.cuni.cz> on 13-Feb-1998 + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +1. How to find PCI devices +~~~~~~~~~~~~~~~~~~~~~~~~~~ + In case your driver wants to search for all devices with given vendor/device +ID, it should use: + + struct pci_dev *dev = NULL; + while (dev = pci_find_device(VENDOR_ID, DEVICE_ID, dev)) + configure_device(dev); + + For class-based search, use pci_find_class(CLASS_ID, dev). + + In case you want to do some complex matching, look at pci_devices -- it's +a linked list of pci_dev structures for all PCI devices in the system. + + All these methods return pointer to a pci_dev structure which is used as a +parameter for many other PCI functions. The rest of them accept bus and +device/function numbers which can be found in pci_dev->bus->number and +pci_dev->devfn. Feel free to use all other fields of the pci_dev structure, but +don't modify them. + + The pci_present() function can be used to test presence of PCI in the +machine. + +2. How to access PCI config space +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + You can use pci_(read|write)_config_(byte|word|dword) to access the config +space of device represented by pci_dev. All these functions return 0 when +successfull or an error code (PCIBIOS_...) which can be translated to text +string by pcibios_strerror. Most drivers expect that accesses to valid PCI +devices don't fail. + + In case you want to address the devices by bus/device/function numbers, +use pcibios_(read_write)_config_(byte|word|dword). + + If you access fields in the standard portion of the config header, please +use symbolic names of locations and bits declared in <linux/pci.h>. + +3. Addresses and interrupts +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Memory and port addresses and interrupt numbers should NOT be read from the +config space. You should use the values in the pci_dev structure as they might +have been remapped by the kernel. + +4. Obsolete functions +~~~~~~~~~~~~~~~~~~~~~ + +<linux/bios32.h> is obsolete and should not be included in new code. + +pcibios_find_(device|class) are also obsolete and should be replaced by +pci_find_(device|class). --- /mj/linux-2.1/include/linux/bios32.h Wed Jan 15 01:46:07 1997 +++ /mj/linux/include/linux/bios32.h Fri Feb 6 20:05:13 1998 @@ -1,61 +1,3 @@ -/* - * BIOS32, PCI BIOS functions and defines - * Copyright 1994, Drew Eckhardt - * - * For more information, please consult - * - * PCI BIOS Specification Revision - * PCI Local Bus Specification - * PCI System Design Guide - * - * PCI Special Interest Group - * P.O. Box 14070 - * Portland, OR 97214 - * U. S. A. - * Phone: 800-433-5177 / +1-503-797-4207 - * Fax: +1-503-234-6762 - * - * Manuals are $25 each or $50 for all three, plus $7 shipping - * within the United States, $35 abroad. - */ +/* Stub until drivers are fixed */ -#ifndef BIOS32_H -#define BIOS32_H - -/* - * Error values that may be returned by the PCI bios. Use - * pcibios_strerror() to convert to a printable string. - */ -#define PCIBIOS_SUCCESSFUL 0x00 -#define PCIBIOS_FUNC_NOT_SUPPORTED 0x81 -#define PCIBIOS_BAD_VENDOR_ID 0x83 -#define PCIBIOS_DEVICE_NOT_FOUND 0x86 -#define PCIBIOS_BAD_REGISTER_NUMBER 0x87 -#define PCIBIOS_SET_FAILED 0x88 -#define PCIBIOS_BUFFER_TOO_SMALL 0x89 - -extern int pcibios_present (void); -extern unsigned long pcibios_init (unsigned long memory_start, - unsigned long memory_end); -extern unsigned long pcibios_fixup (unsigned long memory_start, - unsigned long memory_end); -extern int pcibios_find_class (unsigned int class_code, unsigned short index, - unsigned char *bus, unsigned char *dev_fn); -extern int pcibios_find_device (unsigned short vendor, unsigned short dev_id, - unsigned short index, unsigned char *bus, - unsigned char *dev_fn); -extern int pcibios_read_config_byte (unsigned char bus, unsigned char dev_fn, - unsigned char where, unsigned char *val); -extern int pcibios_read_config_word (unsigned char bus, unsigned char dev_fn, - unsigned char where, unsigned short *val); -extern int pcibios_read_config_dword (unsigned char bus, unsigned char dev_fn, - unsigned char where, unsigned int *val); -extern int pcibios_write_config_byte (unsigned char bus, unsigned char dev_fn, - unsigned char where, unsigned char val); -extern int pcibios_write_config_word (unsigned char bus, unsigned char dev_fn, - unsigned char where, unsigned short val); -extern int pcibios_write_config_dword (unsigned char bus, unsigned char dev_fn, - unsigned char where, unsigned int val); -extern const char *pcibios_strerror (int error); - -#endif /* BIOS32_H */ +#warning This driver uses the old PCI interface, please fix it. --- /mj/linux-2.1/arch/i386/kernel/bios32.c Fri Feb 13 13:59:38 1998 +++ /mj/linux/arch/i386/kernel/bios32.c Fri Feb 13 15:17:36 1998 @@ -1,7 +1,7 @@ /* - * bios32.c - BIOS32, PCI BIOS functions. + * bios32.c - Low-Level PCI Access * - * $Id: bios32.c,v 1.17 1997/11/16 11:03:41 mj Exp $ + * $Id: bios32.c,v 1.23 1998/02/13 14:15:48 mj Exp $ * * Sponsored by * iX Multiuser Multitasking Magazine @@ -64,12 +64,13 @@ * * Aug 2, 1997 : Split to PCI BIOS handling and direct PCI access parts * and cleaned it up... Martin Mares <mj@atrey.karlin.mff.cuni.cz> + * + * Feb 6, 1998 : No longer using BIOS to find devices and device classes. [mj] */ #include <linux/config.h> #include <linux/types.h> #include <linux/kernel.h> -#include <linux/bios32.h> #include <linux/pci.h> #include <linux/init.h> @@ -91,8 +92,6 @@ struct pci_access { int pci_present; - int (*find_device)(unsigned short, unsigned short, unsigned short, unsigned char *, unsigned char *); - int (*find_class)(unsigned int, unsigned short, unsigned char *, unsigned char *); int (*read_config_byte)(unsigned char, unsigned char, unsigned char, unsigned char *); int (*read_config_word)(unsigned char, unsigned char, unsigned char, unsigned short *); int (*read_config_dword)(unsigned char, unsigned char, unsigned char, unsigned int *); @@ -108,8 +107,6 @@ static struct pci_access pci_access_none = { 0, /* No PCI present */ - (void *) pci_stub, /* No functions implemented */ - (void *) pci_stub, (void *) pci_stub, (void *) pci_stub, (void *) pci_stub, @@ -125,54 +122,16 @@ return access_pci->pci_present; } -int pcibios_find_class (unsigned int class_code, unsigned short index, - unsigned char *bus, unsigned char *device_fn) -{ - return access_pci->find_class(class_code, index, bus, device_fn); -} - -int pcibios_find_device (unsigned short vendor, unsigned short device_id, - unsigned short index, unsigned char *bus, unsigned char *device_fn) -{ - return access_pci->find_device(vendor, device_id, index, bus, device_fn); -} - int pcibios_read_config_byte (unsigned char bus, unsigned char device_fn, unsigned char where, unsigned char *value) { - int res; - - res = access_pci->read_config_byte(bus, device_fn, where, value); - -#ifdef __SMP__ -/* - * IOAPICs can take PCI IRQs directly, lets first check the mptable: - */ if (where == PCI_INTERRUPT_LINE) { - int irq; - char pin; - - /* - * get the PCI IRQ INT _physical pin_ for this device - */ - access_pci->read_config_byte(bus, device_fn, - PCI_INTERRUPT_PIN, &pin); - /* - * subtle, PCI pins are numbered starting from 1 ... - */ - pin--; - - irq = IO_APIC_get_PCI_irq_vector (bus,PCI_SLOT(device_fn),pin); - if (irq != -1) - *value = (unsigned char) irq; - - printk("PCI->APIC IRQ transform: (B%d,I%d,P%d) -> %d\n", - bus,PCI_SLOT(device_fn), pin, irq); - + struct pci_dev *dev = pci_find_slot(bus, device_fn); + *value = (dev ? dev->irq : 0); + return 0; } -#endif - return res; + return access_pci->read_config_byte(bus, device_fn, where, value); } int pcibios_read_config_word (unsigned char bus, @@ -205,60 +164,17 @@ return access_pci->write_config_dword(bus, device_fn, where, value); } -/* - * Direct access to PCI hardware... - */ - -/* - * Given the vendor and device ids, find the n'th instance of that device - * in the system. - */ +static unsigned int pci_probe = ~0; -#ifdef CONFIG_PCI_DIRECT - -static int pci_direct_find_device (unsigned short vendor, unsigned short device_id, - unsigned short index, unsigned char *bus, - unsigned char *devfn) -{ - unsigned int curr = 0; - struct pci_dev *dev; - - for (dev = pci_devices; dev; dev = dev->next) { - if (dev->vendor == vendor && dev->device == device_id) { - if (curr == index) { - *devfn = dev->devfn; - *bus = dev->bus->number; - return PCIBIOS_SUCCESSFUL; - } - ++curr; - } - } - return PCIBIOS_DEVICE_NOT_FOUND; -} +#define PCI_PROBE_BIOS 1 +#define PCI_PROBE_CONF1 2 +#define PCI_PROBE_CONF2 4 /* - * Given the class, find the n'th instance of that device - * in the system. + * Direct access to PCI hardware... */ -static int pci_direct_find_class (unsigned int class_code, unsigned short index, - unsigned char *bus, unsigned char *devfn) -{ - unsigned int curr = 0; - struct pci_dev *dev; - - for (dev = pci_devices; dev; dev = dev->next) { - if (dev->class == class_code) { - if (curr == index) { - *devfn = dev->devfn; - *bus = dev->bus->number; - return PCIBIOS_SUCCESSFUL; - } - ++curr; - } - } - return PCIBIOS_DEVICE_NOT_FOUND; -} +#ifdef CONFIG_PCI_DIRECT /* * Functions for accessing PCI configuration space with type 1 accesses @@ -346,8 +262,6 @@ static struct pci_access pci_direct_conf1 = { 1, - pci_direct_find_device, - pci_direct_find_class, pci_conf1_read_config_byte, pci_conf1_read_config_word, pci_conf1_read_config_dword, @@ -458,8 +372,6 @@ static struct pci_access pci_direct_conf2 = { 1, - pci_direct_find_device, - pci_direct_find_class, pci_conf2_read_config_byte, pci_conf2_read_config_word, pci_conf2_read_config_dword, @@ -470,39 +382,43 @@ __initfunc(static struct pci_access *pci_check_direct(void)) { - unsigned int tmp; - unsigned long flags; + unsigned int tmp; + unsigned long flags; - save_flags(flags); cli(); + save_flags(flags); cli(); + + /* + * Check if configuration type 1 works. + */ + if (pci_probe & PCI_PROBE_CONF1) { + outb (0x01, 0xCFB); + tmp = inl (0xCF8); + outl (0x80000000, 0xCF8); + if (inl (0xCF8) == 0x80000000) { + outl (tmp, 0xCF8); + restore_flags(flags); + printk("PCI: Using configuration type 1\n"); + return &pci_direct_conf1; + } + outl (tmp, 0xCF8); + } + + /* + * Check if configuration type 2 works. + */ + if (pci_probe & PCI_PROBE_CONF2) { + outb (0x00, 0xCFB); + outb (0x00, 0xCF8); + outb (0x00, 0xCFA); + if (inb (0xCF8) == 0x00 && inb (0xCFA) == 0x00) { + restore_flags(flags); + printk("PCI: Using configuration type 2\n"); + return &pci_direct_conf2; + } + } - /* - * Check if configuration type 1 works. - */ - outb (0x01, 0xCFB); - tmp = inl (0xCF8); - outl (0x80000000, 0xCF8); - if (inl (0xCF8) == 0x80000000) { - outl (tmp, 0xCF8); - restore_flags(flags); - printk("PCI: Using configuration type 1\n"); - return &pci_direct_conf1; - } - outl (tmp, 0xCF8); - - /* - * Check if configuration type 2 works. - */ - outb (0x00, 0xCFB); - outb (0x00, 0xCF8); - outb (0x00, 0xCFA); - if (inb (0xCF8) == 0x00 && inb (0xCFA) == 0x00) { restore_flags(flags); - printk("PCI: Using configuration type 2\n"); - return &pci_direct_conf2; - } - restore_flags(flags); - printk("PCI: PCI hardware not found (i.e., not present or not supported).\n"); - return NULL; + return NULL; } #endif @@ -599,7 +515,7 @@ printk("bios32_service(0x%lx): not present\n", service); return 0; default: /* Shouldn't happen */ - printk("bios32_service(0x%lx): returned 0x%x, mail drew@colorado.edu\n", + printk("bios32_service(0x%lx): returned 0x%x, mail mj@ucw.cz.\n", service, return_code); return 0; } @@ -642,7 +558,7 @@ if (present_status || (signature != PCI_SIGNATURE)) { printk ("PCI: %s: BIOS32 Service Directory says PCI BIOS is present,\n" " but PCI_BIOS_PRESENT subfunction fails with present status of 0x%x\n" - " and signature of 0x%08lx (%c%c%c%c). Mail drew@Colorado.EDU\n", + " and signature of 0x%08lx (%c%c%c%c). Mail mj@ucw.cz.\n", (signature == PCI_SIGNATURE) ? "WARNING" : "ERROR", present_status, signature, (char) (signature >> 0), (char) (signature >> 8), @@ -660,6 +576,8 @@ return 0; } +#if 0 /* Not used */ + static int pci_bios_find_class (unsigned int class_code, unsigned short index, unsigned char *bus, unsigned char *device_fn) { @@ -709,6 +627,8 @@ return (int) (ret & 0xff00) >> 8; } +#endif + static int pci_bios_read_config_byte(unsigned char bus, unsigned char device_fn, unsigned char where, unsigned char *value) { @@ -847,8 +767,6 @@ static struct pci_access pci_bios_access = { 1, - pci_bios_find_device, - pci_bios_find_class, pci_bios_read_config_byte, pci_bios_read_config_word, pci_bios_read_config_dword, @@ -887,18 +805,14 @@ if (sum != 0) continue; if (check->fields.revision != 0) { - printk("PCI: unsupported BIOS32 revision %d at 0x%p, mail drew@colorado.edu\n", + printk("PCI: unsupported BIOS32 revision %d at 0x%p, mail mj@ucw.cz\n", check->fields.revision, check); continue; } printk ("PCI: BIOS32 Service Directory structure at 0x%p\n", check); if (check->fields.entry >= 0x100000) { -#ifdef CONFIG_PCI_DIRECT - printk("PCI: BIOS32 entry in high memory, trying direct PCI access.\n"); - return pci_check_direct(); -#else printk("PCI: BIOS32 entry in high memory, cannot use.\n"); -#endif + return NULL; } else { bios32_entry = check->fields.entry; printk ("PCI: BIOS32 Service Directory entry at 0x%lx\n", bios32_entry); @@ -909,18 +823,42 @@ break; /* Hopefully more than one BIOS32 cannot happen... */ } + /* + * If we were told to use the PCI BIOS and it's not present, avoid + * touching the hardware. + */ + pci_probe = 0; return NULL; } #endif /* - * No fixup function used. + * Recalculate IRQ's of all PCI devices in the system in case we use the IO APIC. */ __initfunc(unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end)) { - return mem_start; +#ifdef __SMP__ + struct pci_dev *dev; + unsigned char pin; + int irq; + + for(dev = pci_devices; dev; dev=dev->next) { + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + if (!pin) + continue; + pin--; /* interrupt pins are numbered starting from 1 */ + irq = IO_APIC_get_PCI_irq_vector (dev->bus, PCI_SLOT(dev->devfn), pin); + if (irq == -1) + continue; + printk("PCI->APIC IRQ transform: (B%d,I%d,P%d) -> %d\n", + dev->bus->number, PCI_SLOT(dev->devfn), pin, irq); + dev->irq = irq; + } +#endif + + return mem_start; } /* @@ -932,16 +870,47 @@ struct pci_access *a = NULL; #ifdef CONFIG_PCI_BIOS - a = pci_find_bios(); -#else -#ifdef CONFIG_PCI_DIRECT - a = pci_check_direct(); -#else -#error "You need to set CONFIG_PCI_BIOS or CONFIG_PCI_DIRECT if you want PCI support." + if (pci_probe & PCI_PROBE_BIOS) + a = pci_find_bios(); #endif +#ifdef CONFIG_PCI_DIRECT + if (!a && (pci_probe & (PCI_PROBE_CONF1 | PCI_PROBE_CONF2))) + a = pci_check_direct(); #endif if (a) access_pci = a; return memory_start; +} + +#if !defined(CONFIG_PCI_BIOS) && !defined(CONFIG_PCI_DIRECT) +#error PCI configured with neither PCI BIOS or PCI direct access support. +#endif + +__initfunc(char *pcibios_setup(char *str)) +{ + if (!strncmp(str, "off", 3)) { + pci_probe = 0; + return str+3; + } +#ifdef CONFIG_PCI_BIOS + else if (!strncmp(str, "bios", 4)) { + pci_probe = PCI_PROBE_BIOS; + return str+4; + } else if (!strncmp(str, "nobios", 6)) { + pci_probe &= ~PCI_PROBE_BIOS; + return str+6; + } +#endif +#ifdef CONFIG_PCI_DIRECT + else if (!strncmp(str, "conf1", 5)) { + pci_probe = PCI_PROBE_CONF1; + return str+5; + } + else if (!strncmp(str, "conf2", 5)) { + pci_probe = PCI_PROBE_CONF2; + return str+5; + } +#endif + return str; } --- /mj/linux-2.1/kernel/ksyms.c Fri Feb 13 14:00:00 1998 +++ /mj/linux/kernel/ksyms.c Thu Feb 12 16:30:10 1998 @@ -51,8 +51,4 @@ -#ifdef CONFIG_PCI -#include <linux/bios32.h> -#include <linux/pci.h> -#endif #if defined(CONFIG_PROC_FS) #include <linux/proc_fs.h> #endif @@ -99,19 +95,6 @@ #endif EXPORT_SYMBOL(get_options); -#ifdef CONFIG_PCI -EXPORT_SYMBOL(pcibios_present); -EXPORT_SYMBOL(pcibios_find_class); -EXPORT_SYMBOL(pcibios_find_device); -EXPORT_SYMBOL(pcibios_read_config_byte); -EXPORT_SYMBOL(pcibios_read_config_word); -EXPORT_SYMBOL(pcibios_read_config_dword); -EXPORT_SYMBOL(pcibios_write_config_byte); -EXPORT_SYMBOL(pcibios_write_config_word); -EXPORT_SYMBOL(pcibios_write_config_dword); -EXPORT_SYMBOL(pcibios_strerror); -#endif - /* process memory management */ EXPORT_SYMBOL(do_mmap); EXPORT_SYMBOL(do_munmap);

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