diff options
author | Thomas Bushnell <thomas@gnu.org> | 1999-04-26 05:58:44 +0000 |
---|---|---|
committer | Thomas Bushnell <thomas@gnu.org> | 1999-04-26 05:58:44 +0000 |
commit | 86297c41a26f18d924e64fc93321c59cbc4c48dd (patch) | |
tree | 376954c6b95b735d361875319a1a2a9db6a27527 /linux/src/arch | |
parent | 851137902d3e7ad87af177487df3eea53e940a1c (diff) |
1998-11-30 OKUJI Yoshinori <okuji@kuicr.kyoto-u.ac.jp>
Clean up linux emulation code to make it architecture-independent
as much as possible.
* linux: Renamed from linuxdev.
* Makefile.in (objfiles): Add linux.o instead of linuxdev.o.
(MAKE): New variable. Used for the linux.o target.
* configure.in: Add AC_CHECK_TOOL(MAKE, make).
* i386/i386/spl.h: Include <i386/ipl.h>, for compatibility with
OSF Mach 3.0. Suggested by Elgin Lee <ehl@funghi.com>.
* linux/src: Renamed from linux/linux.
* linux/dev: Renamed from linux/mach.
* linux/Drivers.in (AC_INIT): Use dev/include/linux/autoconf.h,
instead of mach/include/linux/autoconf.h.
* Makefile.in (all): Target ../linux.o instead of ../linuxdev.o.
* linux/dev/drivers/block/genhd.c: Include <machine/spl.h> instead
of <i386/ipl.h>.
* linux/dev/drivers/net/auto_irq.c: Remove unneeded header files,
<i386/ipl.h> and <i386/pic.h>.
* linux/dev/init/main.c: Many i386-dependent codes moved to ...
* linux/dev/arch/i386/irq.c: ... here.
* linux/dev/arch/i386/setup.c: New file.
* linux/dev/arch/i386/linux_emul.h: Likewise.
* linux/dev/arch/i386/glue/timer.c: Merged into sched.c.
* linux/dev/arch/i386/glue/sched.c: Include <machine/spl.h> instead
of <i386/ipl.h>, and moved to ...
* linux/dev/kernel/sched.c: ... here.
* linux/dev/arch/i386/glue/block.c: Include <machine/spl.h> and
<linux_emul.h>, instead of i386-dependent header files, and
moved to ...
* linux/dev/glue/blocl.c: ... here.
* linux/dev/arch/i386/glue/net.c: Include <machine/spl.h> and
<linux_emul.h>, instead of i386-dependent header files, and
moved to ...
* linux/dev/glue/net.c: ... here.
* linux/dev/arch/i386/glue/misc.c: Remove `x86' and moved to ...
* linux/dev/glue/misc.c: ... here.
* linux/dev/arch/i386/glue/kmem.c: Moved to ...
* linux/dev/glue/kmem.c: ... here.
Diffstat (limited to 'linux/src/arch')
-rw-r--r-- | linux/src/arch/i386/kernel/bios32.c | 914 | ||||
-rw-r--r-- | linux/src/arch/i386/kernel/irq.c | 582 | ||||
-rw-r--r-- | linux/src/arch/i386/lib/delay.S | 18 | ||||
-rw-r--r-- | linux/src/arch/i386/lib/semaphore.S | 35 |
4 files changed, 1549 insertions, 0 deletions
diff --git a/linux/src/arch/i386/kernel/bios32.c b/linux/src/arch/i386/kernel/bios32.c new file mode 100644 index 0000000..5a0fc38 --- /dev/null +++ b/linux/src/arch/i386/kernel/bios32.c @@ -0,0 +1,914 @@ +/* + * bios32.c - BIOS32, PCI BIOS functions. + * + * $Id: bios32.c,v 1.1 1999/04/26 05:50:57 tb Exp $ + * + * Sponsored by + * iX Multiuser Multitasking Magazine + * Hannover, Germany + * hm@ix.de + * + * Copyright 1993, 1994 Drew Eckhardt + * Visionary Computing + * (Unix and Linux consulting and custom programming) + * Drew@Colorado.EDU + * +1 (303) 786-7975 + * + * For more information, please consult + * + * PCI BIOS Specification Revision + * PCI Local Bus Specification + * PCI System Design Guide + * + * PCI Special Interest Group + * M/S HF3-15A + * 5200 N.E. Elam Young Parkway + * Hillsboro, Oregon 97124-6497 + * +1 (503) 696-2000 + * +1 (800) 433-5177 + * + * Manuals are $25 each or $50 for all three, plus $7 shipping + * within the United States, $35 abroad. + * + * + * CHANGELOG : + * Jun 17, 1994 : Modified to accommodate the broken pre-PCI BIOS SPECIFICATION + * Revision 2.0 present on <thys@dennis.ee.up.ac.za>'s ASUS mainboard. + * + * Jan 5, 1995 : Modified to probe PCI hardware at boot time by Frederic + * Potter, potter@cao-vlsi.ibp.fr + * + * Jan 10, 1995 : Modified to store the information about configured pci + * devices into a list, which can be accessed via /proc/pci by + * Curtis Varner, cvarner@cs.ucr.edu + * + * Jan 12, 1995 : CPU-PCI bridge optimization support by Frederic Potter. + * Alpha version. Intel & UMC chipset support only. + * + * Apr 16, 1995 : Source merge with the DEC Alpha PCI support. Most of the code + * moved to drivers/pci/pci.c. + * + * Dec 7, 1996 : Added support for direct configuration access of boards + * with Intel compatible access schemes (tsbogend@alpha.franken.de) + * + * Feb 3, 1997 : Set internal functions to static, save/restore flags + * avoid dead locks reading broken PCI BIOS, werner@suse.de + * + * Apr 26, 1997 : Fixed case when there is BIOS32, but not PCI BIOS + * (mj@atrey.karlin.mff.cuni.cz) + * + * May 7, 1997 : Added some missing cli()'s. [mj] + * + * Jun 20, 1997 : Corrected problems in "conf1" type accesses. + * (paubert@iram.es) + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/bios32.h> +#include <linux/pci.h> + +#include <asm/segment.h> +#include <asm/system.h> +#include <asm/io.h> + +#define PCIBIOS_PCI_FUNCTION_ID 0xb1XX +#define PCIBIOS_PCI_BIOS_PRESENT 0xb101 +#define PCIBIOS_FIND_PCI_DEVICE 0xb102 +#define PCIBIOS_FIND_PCI_CLASS_CODE 0xb103 +#define PCIBIOS_GENERATE_SPECIAL_CYCLE 0xb106 +#define PCIBIOS_READ_CONFIG_BYTE 0xb108 +#define PCIBIOS_READ_CONFIG_WORD 0xb109 +#define PCIBIOS_READ_CONFIG_DWORD 0xb10a +#define PCIBIOS_WRITE_CONFIG_BYTE 0xb10b +#define PCIBIOS_WRITE_CONFIG_WORD 0xb10c +#define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d + + +/* BIOS32 signature: "_32_" */ +#define BIOS32_SIGNATURE (('_' << 0) + ('3' << 8) + ('2' << 16) + ('_' << 24)) + +/* PCI signature: "PCI " */ +#define PCI_SIGNATURE (('P' << 0) + ('C' << 8) + ('I' << 16) + (' ' << 24)) + +/* PCI service signature: "$PCI" */ +#define PCI_SERVICE (('$' << 0) + ('P' << 8) + ('C' << 16) + ('I' << 24)) + +/* + * This is the standard structure used to identify the entry point + * to the BIOS32 Service Directory, as documented in + * Standard BIOS 32-bit Service Directory Proposal + * Revision 0.4 May 24, 1993 + * Phoenix Technologies Ltd. + * Norwood, MA + * and the PCI BIOS specification. + */ + +union bios32 { + struct { + unsigned long signature; /* _32_ */ + unsigned long entry; /* 32 bit physical address */ + unsigned char revision; /* Revision level, 0 */ + unsigned char length; /* Length in paragraphs should be 01 */ + unsigned char checksum; /* All bytes must add up to zero */ + unsigned char reserved[5]; /* Must be zero */ + } fields; + char chars[16]; +}; + +#ifdef CONFIG_PCI +/* + * Physical address of the service directory. I don't know if we're + * allowed to have more than one of these or not, so just in case + * we'll make pcibios_present() take a memory start parameter and store + * the array there. + */ + +static unsigned long bios32_entry = 0; +static struct { + unsigned long address; + unsigned short segment; +} bios32_indirect = { 0, KERNEL_CS }; + + +/* + * function table for accessing PCI configuration space + */ +struct pci_access { + 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 *); + int (*write_config_byte)(unsigned char, unsigned char, unsigned char, unsigned char); + int (*write_config_word)(unsigned char, unsigned char, unsigned char, unsigned short); + int (*write_config_dword)(unsigned char, unsigned char, unsigned char, unsigned int); +}; + +/* + * pointer to selected PCI access function table + */ +static struct pci_access *access_pci = NULL; + + + +/* + * Returns the entry point for the given service, NULL on error + */ + +static unsigned long bios32_service(unsigned long service) +{ + unsigned char return_code; /* %al */ + unsigned long address; /* %ebx */ + unsigned long length; /* %ecx */ + unsigned long entry; /* %edx */ + unsigned long flags; + + save_flags(flags); cli(); + __asm__("lcall (%%edi)" + : "=a" (return_code), + "=b" (address), + "=c" (length), + "=d" (entry) + : "0" (service), + "1" (0), + "D" (&bios32_indirect)); + restore_flags(flags); + + switch (return_code) { + case 0: + return address + entry; + case 0x80: /* Not present */ + 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", + service, return_code); + return 0; + } +} + +static long pcibios_entry = 0; +static struct { + unsigned long address; + unsigned short segment; +} pci_indirect = { 0, KERNEL_CS }; + + +static int check_pcibios(void) +{ + unsigned long signature; + unsigned char present_status; + unsigned char major_revision; + unsigned char minor_revision; + unsigned long flags; + int pack; + + if ((pcibios_entry = bios32_service(PCI_SERVICE))) { + pci_indirect.address = pcibios_entry; + + save_flags(flags); cli(); + __asm__("lcall (%%edi)\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:\tshl $8, %%eax\n\t" + "movw %%bx, %%ax" + : "=d" (signature), + "=a" (pack) + : "1" (PCIBIOS_PCI_BIOS_PRESENT), + "D" (&pci_indirect) + : "bx", "cx"); + restore_flags(flags); + + present_status = (pack >> 16) & 0xff; + major_revision = (pack >> 8) & 0xff; + minor_revision = pack & 0xff; + if (present_status || (signature != PCI_SIGNATURE)) { + printk ("pcibios_init : %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", + (signature == PCI_SIGNATURE) ? "WARNING" : "ERROR", + present_status, signature, + (char) (signature >> 0), (char) (signature >> 8), + (char) (signature >> 16), (char) (signature >> 24)); + + if (signature != PCI_SIGNATURE) + pcibios_entry = 0; + } + if (pcibios_entry) { + printk ("pcibios_init : PCI BIOS revision %x.%02x entry at 0x%lx\n", + major_revision, minor_revision, pcibios_entry); + return 1; + } + } + return 0; +} + + +static int pci_bios_find_class (unsigned int class_code, unsigned short index, + unsigned char *bus, unsigned char *device_fn) +{ + unsigned long bx; + unsigned long ret; + unsigned long flags; + + save_flags(flags); cli(); + __asm__ ("lcall (%%edi)\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=b" (bx), + "=a" (ret) + : "1" (PCIBIOS_FIND_PCI_CLASS_CODE), + "c" (class_code), + "S" ((int) index), + "D" (&pci_indirect)); + restore_flags(flags); + *bus = (bx >> 8) & 0xff; + *device_fn = bx & 0xff; + return (int) (ret & 0xff00) >> 8; +} + + +static int pci_bios_find_device (unsigned short vendor, unsigned short device_id, + unsigned short index, unsigned char *bus, unsigned char *device_fn) +{ + unsigned short bx; + unsigned short ret; + unsigned long flags; + + save_flags(flags); cli(); + __asm__("lcall (%%edi)\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=b" (bx), + "=a" (ret) + : "1" (PCIBIOS_FIND_PCI_DEVICE), + "c" (device_id), + "d" (vendor), + "S" ((int) index), + "D" (&pci_indirect)); + restore_flags(flags); + *bus = (bx >> 8) & 0xff; + *device_fn = bx & 0xff; + return (int) (ret & 0xff00) >> 8; +} + +static int pci_bios_read_config_byte(unsigned char bus, + unsigned char device_fn, unsigned char where, unsigned char *value) +{ + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + unsigned long flags; + + save_flags(flags); cli(); + __asm__("lcall (%%esi)\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=c" (*value), + "=a" (ret) + : "1" (PCIBIOS_READ_CONFIG_BYTE), + "b" (bx), + "D" ((long) where), + "S" (&pci_indirect)); + restore_flags(flags); + return (int) (ret & 0xff00) >> 8; +} + +static int pci_bios_read_config_word (unsigned char bus, + unsigned char device_fn, unsigned char where, unsigned short *value) +{ + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + unsigned long flags; + + save_flags(flags); cli(); + __asm__("lcall (%%esi)\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=c" (*value), + "=a" (ret) + : "1" (PCIBIOS_READ_CONFIG_WORD), + "b" (bx), + "D" ((long) where), + "S" (&pci_indirect)); + restore_flags(flags); + return (int) (ret & 0xff00) >> 8; +} + +static int pci_bios_read_config_dword (unsigned char bus, + unsigned char device_fn, unsigned char where, unsigned int *value) +{ + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + unsigned long flags; + + save_flags(flags); cli(); + __asm__("lcall (%%esi)\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=c" (*value), + "=a" (ret) + : "1" (PCIBIOS_READ_CONFIG_DWORD), + "b" (bx), + "D" ((long) where), + "S" (&pci_indirect)); + restore_flags(flags); + return (int) (ret & 0xff00) >> 8; +} + +static int pci_bios_write_config_byte (unsigned char bus, + unsigned char device_fn, unsigned char where, unsigned char value) +{ + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + unsigned long flags; + + save_flags(flags); cli(); + __asm__("lcall (%%esi)\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=a" (ret) + : "0" (PCIBIOS_WRITE_CONFIG_BYTE), + "c" (value), + "b" (bx), + "D" ((long) where), + "S" (&pci_indirect)); + restore_flags(flags); + return (int) (ret & 0xff00) >> 8; +} + +static int pci_bios_write_config_word (unsigned char bus, + unsigned char device_fn, unsigned char where, unsigned short value) +{ + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + unsigned long flags; + + save_flags(flags); cli(); + __asm__("lcall (%%esi)\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=a" (ret) + : "0" (PCIBIOS_WRITE_CONFIG_WORD), + "c" (value), + "b" (bx), + "D" ((long) where), + "S" (&pci_indirect)); + restore_flags(flags); + return (int) (ret & 0xff00) >> 8; +} + +static int pci_bios_write_config_dword (unsigned char bus, + unsigned char device_fn, unsigned char where, unsigned int value) +{ + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + unsigned long flags; + + save_flags(flags); cli(); + __asm__("lcall (%%esi)\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=a" (ret) + : "0" (PCIBIOS_WRITE_CONFIG_DWORD), + "c" (value), + "b" (bx), + "D" ((long) where), + "S" (&pci_indirect)); + restore_flags(flags); + return (int) (ret & 0xff00) >> 8; +} + +/* + * function table for BIOS32 access + */ +static struct pci_access pci_bios_access = { + pci_bios_find_device, + pci_bios_find_class, + pci_bios_read_config_byte, + pci_bios_read_config_word, + pci_bios_read_config_dword, + pci_bios_write_config_byte, + pci_bios_write_config_word, + pci_bios_write_config_dword +}; + + + +/* + * Given the vendor and device ids, find the n'th instance of that device + * in the system. + */ +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; +} + + +/* + * Given the class, find the n'th instance of that device + * in the system. + */ +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; +} + +/* + * Functions for accessing PCI configuration space with type 1 accesses + */ +#define CONFIG_CMD(bus, device_fn, where) (0x80000000 | (bus << 16) | (device_fn << 8) | (where & ~3)) + +static int pci_conf1_read_config_byte(unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned char *value) +{ + unsigned long flags; + + save_flags(flags); cli(); + outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); + *value = inb(0xCFC + (where&3)); + restore_flags(flags); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf1_read_config_word (unsigned char bus, + unsigned char device_fn, unsigned char where, unsigned short *value) +{ + unsigned long flags; + + if (where&1) return PCIBIOS_BAD_REGISTER_NUMBER; + save_flags(flags); cli(); + outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); + *value = inw(0xCFC + (where&2)); + restore_flags(flags); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf1_read_config_dword (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned int *value) +{ + unsigned long flags; + + if (where&3) return PCIBIOS_BAD_REGISTER_NUMBER; + save_flags(flags); cli(); + outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); + *value = inl(0xCFC); + restore_flags(flags); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf1_write_config_byte (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned char value) +{ + unsigned long flags; + + save_flags(flags); cli(); + outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); + outb(value, 0xCFC + (where&3)); + restore_flags(flags); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf1_write_config_word (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned short value) +{ + unsigned long flags; + + if (where&1) return PCIBIOS_BAD_REGISTER_NUMBER; + save_flags(flags); cli(); + outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); + outw(value, 0xCFC + (where&2)); + restore_flags(flags); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf1_write_config_dword (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned int value) +{ + unsigned long flags; + + if (where&3) return PCIBIOS_BAD_REGISTER_NUMBER; + save_flags(flags); cli(); + outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); + outl(value, 0xCFC); + restore_flags(flags); + return PCIBIOS_SUCCESSFUL; +} + +#undef CONFIG_CMD + +/* + * functiontable for type 1 + */ +static struct pci_access pci_direct_conf1 = { + pci_direct_find_device, + pci_direct_find_class, + pci_conf1_read_config_byte, + pci_conf1_read_config_word, + pci_conf1_read_config_dword, + pci_conf1_write_config_byte, + pci_conf1_write_config_word, + pci_conf1_write_config_dword +}; + +/* + * Functions for accessing PCI configuration space with type 2 accesses + */ +#define IOADDR(devfn, where) ((0xC000 | ((devfn & 0x78) << 5)) + where) +#define FUNC(devfn) (((devfn & 7) << 1) | 0xf0) + +static int pci_conf2_read_config_byte(unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned char *value) +{ + unsigned long flags; + + if (device_fn & 0x80) + return PCIBIOS_DEVICE_NOT_FOUND; + save_flags(flags); cli(); + outb (FUNC(device_fn), 0xCF8); + outb (bus, 0xCFA); + *value = inb(IOADDR(device_fn,where)); + outb (0, 0xCF8); + restore_flags(flags); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf2_read_config_word (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned short *value) +{ + unsigned long flags; + + if (device_fn & 0x80) + return PCIBIOS_DEVICE_NOT_FOUND; + save_flags(flags); cli(); + outb (FUNC(device_fn), 0xCF8); + outb (bus, 0xCFA); + *value = inw(IOADDR(device_fn,where)); + outb (0, 0xCF8); + restore_flags(flags); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf2_read_config_dword (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned int *value) +{ + unsigned long flags; + + if (device_fn & 0x80) + return PCIBIOS_DEVICE_NOT_FOUND; + save_flags(flags); cli(); + outb (FUNC(device_fn), 0xCF8); + outb (bus, 0xCFA); + *value = inl (IOADDR(device_fn,where)); + outb (0, 0xCF8); + restore_flags(flags); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf2_write_config_byte (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned char value) +{ + unsigned long flags; + + save_flags(flags); cli(); + outb (FUNC(device_fn), 0xCF8); + outb (bus, 0xCFA); + outb (value, IOADDR(device_fn,where)); + outb (0, 0xCF8); + restore_flags(flags); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf2_write_config_word (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned short value) +{ + unsigned long flags; + + save_flags(flags); cli(); + outb (FUNC(device_fn), 0xCF8); + outb (bus, 0xCFA); + outw (value, IOADDR(device_fn,where)); + outb (0, 0xCF8); + restore_flags(flags); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf2_write_config_dword (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned int value) +{ + unsigned long flags; + + save_flags(flags); cli(); + outb (FUNC(device_fn), 0xCF8); + outb (bus, 0xCFA); + outl (value, IOADDR(device_fn,where)); + outb (0, 0xCF8); + restore_flags(flags); + return PCIBIOS_SUCCESSFUL; +} + +#undef IOADDR +#undef FUNC + +/* + * functiontable for type 2 + */ +static struct pci_access pci_direct_conf2 = { + pci_direct_find_device, + pci_direct_find_class, + pci_conf2_read_config_byte, + pci_conf2_read_config_word, + pci_conf2_read_config_dword, + pci_conf2_write_config_byte, + pci_conf2_write_config_word, + pci_conf2_write_config_dword +}; + + +static struct pci_access *check_direct_pci(void) +{ + unsigned int tmp; + unsigned long flags; + + save_flags(flags); cli(); + + /* + * 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("pcibios_init: 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 (0xCFB) == 0x00) { + restore_flags(flags); + printk("pcibios_init: Using configuration type 2\n"); + return &pci_direct_conf2; + } + restore_flags(flags); + printk("pcibios_init: Not supported chipset for direct PCI access !\n"); + return NULL; +} + + +/* + * access defined pcibios functions via + * the function table + */ + +int pcibios_present(void) +{ + return access_pci ? 1 : 0; +} + +int pcibios_find_class (unsigned int class_code, unsigned short index, + unsigned char *bus, unsigned char *device_fn) +{ + if (access_pci && access_pci->find_class) + return access_pci->find_class(class_code, index, bus, device_fn); + + return PCIBIOS_FUNC_NOT_SUPPORTED; +} + +int pcibios_find_device (unsigned short vendor, unsigned short device_id, + unsigned short index, unsigned char *bus, unsigned char *device_fn) +{ + if (access_pci && access_pci->find_device) + return access_pci->find_device(vendor, device_id, index, bus, device_fn); + + return PCIBIOS_FUNC_NOT_SUPPORTED; +} + +int pcibios_read_config_byte (unsigned char bus, + unsigned char device_fn, unsigned char where, unsigned char *value) +{ + if (access_pci && access_pci->read_config_byte) + return access_pci->read_config_byte(bus, device_fn, where, value); + + return PCIBIOS_FUNC_NOT_SUPPORTED; +} + +int pcibios_read_config_word (unsigned char bus, + unsigned char device_fn, unsigned char where, unsigned short *value) +{ + if (access_pci && access_pci->read_config_word) + return access_pci->read_config_word(bus, device_fn, where, value); + + return PCIBIOS_FUNC_NOT_SUPPORTED; +} + +int pcibios_read_config_dword (unsigned char bus, + unsigned char device_fn, unsigned char where, unsigned int *value) +{ + if (access_pci && access_pci->read_config_dword) + return access_pci->read_config_dword(bus, device_fn, where, value); + + return PCIBIOS_FUNC_NOT_SUPPORTED; +} + +int pcibios_write_config_byte (unsigned char bus, + unsigned char device_fn, unsigned char where, unsigned char value) +{ + if (access_pci && access_pci->write_config_byte) + return access_pci->write_config_byte(bus, device_fn, where, value); + + return PCIBIOS_FUNC_NOT_SUPPORTED; +} + +int pcibios_write_config_word (unsigned char bus, + unsigned char device_fn, unsigned char where, unsigned short value) +{ + if (access_pci && access_pci->write_config_word) + return access_pci->write_config_word(bus, device_fn, where, value); + + return PCIBIOS_FUNC_NOT_SUPPORTED; +} + +int pcibios_write_config_dword (unsigned char bus, + unsigned char device_fn, unsigned char where, unsigned int value) +{ + if (access_pci && access_pci->write_config_dword) + return access_pci->write_config_dword(bus, device_fn, where, value); + + return PCIBIOS_FUNC_NOT_SUPPORTED; +} + +const char *pcibios_strerror (int error) +{ + static char buf[80]; + + switch (error) { + case PCIBIOS_SUCCESSFUL: + return "SUCCESSFUL"; + + case PCIBIOS_FUNC_NOT_SUPPORTED: + return "FUNC_NOT_SUPPORTED"; + + case PCIBIOS_BAD_VENDOR_ID: + return "SUCCESSFUL"; + + case PCIBIOS_DEVICE_NOT_FOUND: + return "DEVICE_NOT_FOUND"; + + case PCIBIOS_BAD_REGISTER_NUMBER: + return "BAD_REGISTER_NUMBER"; + + case PCIBIOS_SET_FAILED: + return "SET_FAILED"; + + case PCIBIOS_BUFFER_TOO_SMALL: + return "BUFFER_TOO_SMALL"; + + default: + sprintf (buf, "UNKNOWN RETURN 0x%x", error); + return buf; + } +} + + +unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end) +{ + return mem_start; +} + +#endif + +unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) +{ +#ifdef CONFIG_PCI + union bios32 *check; + unsigned char sum; + int i, length; + + /* + * Follow the standard procedure for locating the BIOS32 Service + * directory by scanning the permissible address range from + * 0xe0000 through 0xfffff for a valid BIOS32 structure. + * + */ + + for (check = (union bios32 *) 0xe0000; + check <= (union bios32 *) 0xffff0; + ++check) { + if (check->fields.signature != BIOS32_SIGNATURE) + continue; + length = check->fields.length * 16; + if (!length) + continue; + sum = 0; + for (i = 0; i < length ; ++i) + sum += check->chars[i]; + if (sum != 0) + continue; + if (check->fields.revision != 0) { + printk("pcibios_init : unsupported revision %d at 0x%p, mail drew@colorado.edu\n", + check->fields.revision, check); + continue; + } + printk ("pcibios_init : BIOS32 Service Directory structure at 0x%p\n", check); + if (!bios32_entry) { + if (check->fields.entry >= 0x100000) { + printk("pcibios_init: entry in high memory, trying direct PCI access\n"); + access_pci = check_direct_pci(); + } else { + bios32_entry = check->fields.entry; + printk ("pcibios_init : BIOS32 Service Directory entry at 0x%lx\n", bios32_entry); + bios32_indirect.address = bios32_entry; + } + } + } + if (bios32_entry && check_pcibios()) + access_pci = &pci_bios_access; +#endif + return memory_start; +} diff --git a/linux/src/arch/i386/kernel/irq.c b/linux/src/arch/i386/kernel/irq.c new file mode 100644 index 0000000..6db6115 --- /dev/null +++ b/linux/src/arch/i386/kernel/irq.c @@ -0,0 +1,582 @@ +/* + * linux/arch/i386/kernel/irq.c + * + * Copyright (C) 1992 Linus Torvalds + * + * This file contains the code used by various IRQ handling routines: + * asking for different IRQ's should be done through these routines + * instead of just grabbing them. Thus setups with different IRQ numbers + * shouldn't result in any weird surprises, and installing new handlers + * should be easier. + */ + +/* + * IRQ's are in fact implemented a bit like signal handlers for the kernel. + * Naturally it's not a 1:1 relation, but there are similarities. + */ + +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/kernel_stat.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/timex.h> +#include <linux/malloc.h> +#include <linux/random.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/bitops.h> +#include <asm/smp.h> + +#define CR0_NE 32 + +static unsigned char cache_21 = 0xff; +static unsigned char cache_A1 = 0xff; + +#ifdef __SMP_PROF__ +static unsigned int int_count[NR_CPUS][NR_IRQS] = {{0},}; +#endif + +static inline void mask_irq(unsigned int irq_nr) +{ + unsigned char mask; + + mask = 1 << (irq_nr & 7); + if (irq_nr < 8) { + cache_21 |= mask; + outb(cache_21,0x21); + } else { + cache_A1 |= mask; + outb(cache_A1,0xA1); + } +} + +static inline void unmask_irq(unsigned int irq_nr) +{ + unsigned char mask; + + mask = ~(1 << (irq_nr & 7)); + if (irq_nr < 8) { + cache_21 &= mask; + outb(cache_21,0x21); + } else { + cache_A1 &= mask; + outb(cache_A1,0xA1); + } +} + +void disable_irq(unsigned int irq_nr) +{ + unsigned long flags; + + save_flags(flags); + cli(); + mask_irq(irq_nr); + restore_flags(flags); +} + +void enable_irq(unsigned int irq_nr) +{ + unsigned long flags; + save_flags(flags); + cli(); + unmask_irq(irq_nr); + restore_flags(flags); +} + +/* + * This builds up the IRQ handler stubs using some ugly macros in irq.h + * + * These macros create the low-level assembly IRQ routines that do all + * the operations that are needed to keep the AT interrupt-controller + * happy. They are also written to be fast - and to disable interrupts + * as little as humanly possible. + * + * NOTE! These macros expand to three different handlers for each line: one + * complete handler that does all the fancy stuff (including signal handling), + * and one fast handler that is meant for simple IRQ's that want to be + * atomic. The specific handler is chosen depending on the SA_INTERRUPT + * flag when installing a handler. Finally, one "bad interrupt" handler, that + * is used when no handler is present. + * + * The timer interrupt is handled specially to insure that the jiffies + * variable is updated at all times. Specifically, the timer interrupt is + * just like the complete handlers except that it is invoked with interrupts + * disabled and should never re-enable them. If other interrupts were + * allowed to be processed while the timer interrupt is active, then the + * other interrupts would have to avoid using the jiffies variable for delay + * and interval timing operations to avoid hanging the system. + */ +BUILD_TIMER_IRQ(FIRST,0,0x01) +BUILD_IRQ(FIRST,1,0x02) +BUILD_IRQ(FIRST,2,0x04) +BUILD_IRQ(FIRST,3,0x08) +BUILD_IRQ(FIRST,4,0x10) +BUILD_IRQ(FIRST,5,0x20) +BUILD_IRQ(FIRST,6,0x40) +BUILD_IRQ(FIRST,7,0x80) +BUILD_IRQ(SECOND,8,0x01) +BUILD_IRQ(SECOND,9,0x02) +BUILD_IRQ(SECOND,10,0x04) +BUILD_IRQ(SECOND,11,0x08) +BUILD_IRQ(SECOND,12,0x10) +#ifdef __SMP__ +BUILD_MSGIRQ(SECOND,13,0x20) +#else +BUILD_IRQ(SECOND,13,0x20) +#endif +BUILD_IRQ(SECOND,14,0x40) +BUILD_IRQ(SECOND,15,0x80) +#ifdef __SMP__ +BUILD_RESCHEDIRQ(16) +#endif + +/* + * Pointers to the low-level handlers: first the general ones, then the + * fast ones, then the bad ones. + */ +static void (*interrupt[17])(void) = { + IRQ0_interrupt, IRQ1_interrupt, IRQ2_interrupt, IRQ3_interrupt, + IRQ4_interrupt, IRQ5_interrupt, IRQ6_interrupt, IRQ7_interrupt, + IRQ8_interrupt, IRQ9_interrupt, IRQ10_interrupt, IRQ11_interrupt, + IRQ12_interrupt, IRQ13_interrupt, IRQ14_interrupt, IRQ15_interrupt +#ifdef __SMP__ + ,IRQ16_interrupt +#endif +}; + +static void (*fast_interrupt[16])(void) = { + fast_IRQ0_interrupt, fast_IRQ1_interrupt, + fast_IRQ2_interrupt, fast_IRQ3_interrupt, + fast_IRQ4_interrupt, fast_IRQ5_interrupt, + fast_IRQ6_interrupt, fast_IRQ7_interrupt, + fast_IRQ8_interrupt, fast_IRQ9_interrupt, + fast_IRQ10_interrupt, fast_IRQ11_interrupt, + fast_IRQ12_interrupt, fast_IRQ13_interrupt, + fast_IRQ14_interrupt, fast_IRQ15_interrupt +}; + +static void (*bad_interrupt[16])(void) = { + bad_IRQ0_interrupt, bad_IRQ1_interrupt, + bad_IRQ2_interrupt, bad_IRQ3_interrupt, + bad_IRQ4_interrupt, bad_IRQ5_interrupt, + bad_IRQ6_interrupt, bad_IRQ7_interrupt, + bad_IRQ8_interrupt, bad_IRQ9_interrupt, + bad_IRQ10_interrupt, bad_IRQ11_interrupt, + bad_IRQ12_interrupt, bad_IRQ13_interrupt, + bad_IRQ14_interrupt, bad_IRQ15_interrupt +}; + +/* + * Initial irq handlers. + */ + +static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { } + +#ifdef __SMP__ + +/* + * On SMP boards, irq13 is used for interprocessor interrupts (IPI's). + */ +static struct irqaction irq13 = { smp_message_irq, SA_INTERRUPT, 0, "IPI", NULL, NULL }; + +#else + +/* + * Note that on a 486, we don't want to do a SIGFPE on a irq13 + * as the irq is unreliable, and exception 16 works correctly + * (ie as explained in the intel literature). On a 386, you + * can't use exception 16 due to bad IBM design, so we have to + * rely on the less exact irq13. + * + * Careful.. Not only is IRQ13 unreliable, but it is also + * leads to races. IBM designers who came up with it should + * be shot. + */ + + +static void math_error_irq(int cpl, void *dev_id, struct pt_regs *regs) +{ + outb(0,0xF0); + if (ignore_irq13 || !hard_math) + return; + math_error(); +} + +static struct irqaction irq13 = { math_error_irq, 0, 0, "math error", NULL, NULL }; + +#endif + +/* + * IRQ2 is cascade interrupt to second interrupt controller + */ +static struct irqaction irq2 = { no_action, 0, 0, "cascade", NULL, NULL}; + +static struct irqaction *irq_action[16] = { + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL +}; + +int get_irq_list(char *buf) +{ + int i, len = 0; + struct irqaction * action; + + for (i = 0 ; i < 16 ; i++) { + action = irq_action[i]; + if (!action) + continue; + len += sprintf(buf+len, "%2d: %10u %c %s", + i, kstat.interrupts[i], + (action->flags & SA_INTERRUPT) ? '+' : ' ', + action->name); + for (action=action->next; action; action = action->next) { + len += sprintf(buf+len, ",%s %s", + (action->flags & SA_INTERRUPT) ? " +" : "", + action->name); + } + len += sprintf(buf+len, "\n"); + } +/* + * Linus - should you add NMI counts here ????? + */ +#ifdef __SMP_PROF__ + len+=sprintf(buf+len, "IPI: %8lu received\n", + ipi_count); +#endif + return len; +} + +#ifdef __SMP_PROF__ + +int get_smp_prof_list(char *buf) { + int i,j, len = 0; + struct irqaction * action; + unsigned long sum_spins = 0; + unsigned long sum_spins_syscall = 0; + unsigned long sum_spins_sys_idle = 0; + unsigned long sum_smp_idle_count = 0; + + for (i=0;i<smp_num_cpus;i++) { + int cpunum = cpu_logical_map[i]; + sum_spins+=smp_spins[cpunum]; + sum_spins_syscall+=smp_spins_syscall[cpunum]; + sum_spins_sys_idle+=smp_spins_sys_idle[cpunum]; + sum_smp_idle_count+=smp_idle_count[cpunum]; + } + + len += sprintf(buf+len,"CPUS: %10i \n", smp_num_cpus); + len += sprintf(buf+len," SUM "); + for (i=0;i<smp_num_cpus;i++) + len += sprintf(buf+len," P%1d ",cpu_logical_map[i]); + len += sprintf(buf+len,"\n"); + for (i = 0 ; i < NR_IRQS ; i++) { + action = *(i + irq_action); + if (!action || !action->handler) + continue; + len += sprintf(buf+len, "%3d: %10d ", + i, kstat.interrupts[i]); + for (j=0;j<smp_num_cpus;j++) + len+=sprintf(buf+len, "%10d ", + int_count[cpu_logical_map[j]][i]); + len += sprintf(buf+len, "%c %s", + (action->flags & SA_INTERRUPT) ? '+' : ' ', + action->name); + for (action=action->next; action; action = action->next) { + len += sprintf(buf+len, ",%s %s", + (action->flags & SA_INTERRUPT) ? " +" : "", + action->name); + } + len += sprintf(buf+len, "\n"); + } + len+=sprintf(buf+len, "LCK: %10lu", + sum_spins); + + for (i=0;i<smp_num_cpus;i++) + len+=sprintf(buf+len," %10lu",smp_spins[cpu_logical_map[i]]); + + len +=sprintf(buf+len," spins from int\n"); + + len+=sprintf(buf+len, "LCK: %10lu", + sum_spins_syscall); + + for (i=0;i<smp_num_cpus;i++) + len+=sprintf(buf+len," %10lu",smp_spins_syscall[cpu_logical_map[i]]); + + len +=sprintf(buf+len," spins from syscall\n"); + + len+=sprintf(buf+len, "LCK: %10lu", + sum_spins_sys_idle); + + for (i=0;i<smp_num_cpus;i++) + len+=sprintf(buf+len," %10lu",smp_spins_sys_idle[cpu_logical_map[i]]); + + len +=sprintf(buf+len," spins from sysidle\n"); + len+=sprintf(buf+len,"IDLE %10lu",sum_smp_idle_count); + + for (i=0;i<smp_num_cpus;i++) + len+=sprintf(buf+len," %10lu",smp_idle_count[cpu_logical_map[i]]); + + len +=sprintf(buf+len," idle ticks\n"); + + len+=sprintf(buf+len, "IPI: %10lu received\n", + ipi_count); + + return len; +} +#endif + + + +/* + * do_IRQ handles IRQ's that have been installed without the + * SA_INTERRUPT flag: it uses the full signal-handling return + * and runs with other interrupts enabled. All relatively slow + * IRQ's should use this format: notably the keyboard/timer + * routines. + */ +asmlinkage void do_IRQ(int irq, struct pt_regs * regs) +{ + struct irqaction * action = *(irq + irq_action); + int do_random = 0; + int c,intm,mask; +#ifdef IRQ_DEBUG + static int count; + if (smp_processor_id() != 0 && count++ < 1000) + printk("IRQ %d: done by CPU %d\n",irq,smp_processor_id()); +#endif + if (irq >= 8) { + c = cache_A1; + intm = inb(0xA1); + mask = 1 << (irq - 8); + } else { + c = cache_21; + intm = inb(0x21); + mask = 1 << irq; + } + if (!(c & mask) || !(intm & mask)) { +#ifdef IRQ_DEBUG + printk("IRQ %d (proc %d):cache_x1=0x%x,INT mask=0x%x\n", irq, smp_processor_id(),c,intm); +#endif + /* better to return because the interrupt may be asserted again, + the bad thing is that we may loose some interrupts */ + return; + } +#ifdef __SMP__ + if(smp_threads_ready && active_kernel_processor!=smp_processor_id()) + panic("IRQ %d: active processor set wrongly(%d not %d).\n", irq, active_kernel_processor, smp_processor_id()); +#endif + + kstat.interrupts[irq]++; +#ifdef __SMP_PROF__ + int_count[smp_processor_id()][irq]++; +#endif + while (action) { + do_random |= action->flags; + action->handler(irq, action->dev_id, regs); + action = action->next; + } + if (do_random & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); +} + +/* + * do_fast_IRQ handles IRQ's that don't need the fancy interrupt return + * stuff - the handler is also running with interrupts disabled unless + * it explicitly enables them later. + */ +asmlinkage void do_fast_IRQ(int irq) +{ + struct irqaction * action = *(irq + irq_action); + int do_random = 0; + +#ifdef __SMP__ + /* IRQ 13 is allowed - that's a flush tlb */ + if(smp_threads_ready && active_kernel_processor!=smp_processor_id() && irq!=13) + panic("fast_IRQ %d: active processor set wrongly(%d not %d).\n", irq, active_kernel_processor, smp_processor_id()); +#endif + + kstat.interrupts[irq]++; +#ifdef __SMP_PROF__ + int_count[smp_processor_id()][irq]++; +#endif + while (action) { + do_random |= action->flags; + action->handler(irq, action->dev_id, NULL); + action = action->next; + } + if (do_random & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); +} + +int setup_x86_irq(int irq, struct irqaction * new) +{ + int shared = 0; + struct irqaction *old, **p; + unsigned long flags; + + p = irq_action + irq; + if ((old = *p) != NULL) { + /* Can't share interrupts unless both agree to */ + if (!(old->flags & new->flags & SA_SHIRQ)) + return -EBUSY; + + /* Can't share interrupts unless both are same type */ + if ((old->flags ^ new->flags) & SA_INTERRUPT) + return -EBUSY; + + /* add new interrupt at end of irq queue */ + do { + p = &old->next; + old = *p; + } while (old); + shared = 1; + } + + if (new->flags & SA_SAMPLE_RANDOM) + rand_initialize_irq(irq); + + save_flags(flags); + cli(); + *p = new; + + if (!shared) { + if (new->flags & SA_INTERRUPT) + set_intr_gate(0x20+irq,fast_interrupt[irq]); + else + set_intr_gate(0x20+irq,interrupt[irq]); + unmask_irq(irq); + } + restore_flags(flags); + return 0; +} + +int request_irq(unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, + const char * devname, + void *dev_id) +{ + int retval; + struct irqaction * action; + + if (irq > 15) + return -EINVAL; + if (!handler) + return -EINVAL; + + action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL); + if (!action) + return -ENOMEM; + + action->handler = handler; + action->flags = irqflags; + action->mask = 0; + action->name = devname; + action->next = NULL; + action->dev_id = dev_id; + + retval = setup_x86_irq(irq, action); + + if (retval) + kfree(action); + return retval; +} + +void free_irq(unsigned int irq, void *dev_id) +{ + struct irqaction * action, **p; + unsigned long flags; + + if (irq > 15) { + printk("Trying to free IRQ%d\n",irq); + return; + } + for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) { + if (action->dev_id != dev_id) + continue; + + /* Found it - now free it */ + save_flags(flags); + cli(); + *p = action->next; + if (!irq[irq_action]) { + mask_irq(irq); + set_intr_gate(0x20+irq,bad_interrupt[irq]); + } + restore_flags(flags); + kfree(action); + return; + } + printk("Trying to free free IRQ%d\n",irq); +} + +unsigned long probe_irq_on (void) +{ + unsigned int i, irqs = 0, irqmask; + unsigned long delay; + + /* first, enable any unassigned irqs */ + for (i = 15; i > 0; i--) { + if (!irq_action[i]) { + enable_irq(i); + irqs |= (1 << i); + } + } + + /* wait for spurious interrupts to mask themselves out again */ + for (delay = jiffies + HZ/10; delay > jiffies; ) + /* about 100ms delay */; + + /* now filter out any obviously spurious interrupts */ + irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int)cache_21; + return irqs & ~irqmask; +} + +int probe_irq_off (unsigned long irqs) +{ + unsigned int i, irqmask; + + irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int)cache_21; +#ifdef DEBUG + printk("probe_irq_off: irqs=0x%04lx irqmask=0x%04x\n", irqs, irqmask); +#endif + irqs &= irqmask; + if (!irqs) + return 0; + i = ffz(~irqs); + if (irqs != (irqs & (1 << i))) + i = -i; + return i; +} + +void init_IRQ(void) +{ + int i; + static unsigned char smptrap=0; + if(smptrap) + return; + smptrap=1; + + /* set the clock to 100 Hz */ + outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */ + outb_p(LATCH & 0xff , 0x40); /* LSB */ + outb(LATCH >> 8 , 0x40); /* MSB */ + for (i = 0; i < 16 ; i++) + set_intr_gate(0x20+i,bad_interrupt[i]); + /* This bit is a hack because we don't send timer messages to all processors yet */ + /* It has to be here .. it doesn't work if you put it down the bottom - assembler explodes 8) */ +#ifdef __SMP__ + set_intr_gate(0x20+i, interrupt[i]); /* IRQ '16' - IPI for rescheduling */ +#endif + request_region(0x20,0x20,"pic1"); + request_region(0xa0,0x20,"pic2"); + setup_x86_irq(2, &irq2); + setup_x86_irq(13, &irq13); +} diff --git a/linux/src/arch/i386/lib/delay.S b/linux/src/arch/i386/lib/delay.S new file mode 100644 index 0000000..9d36420 --- /dev/null +++ b/linux/src/arch/i386/lib/delay.S @@ -0,0 +1,18 @@ +#include <linux/linkage.h> + +/* + * BogoMips loop. Non-inlined because various x86's have so wildly + * varying results depending on the exact alignment. + */ + +ENTRY(__do_delay) + jmp 1f +.align 16 +1: jmp 2f +.align 16 +2: decl %eax + jns 2b + ret + + + diff --git a/linux/src/arch/i386/lib/semaphore.S b/linux/src/arch/i386/lib/semaphore.S new file mode 100644 index 0000000..e09655c --- /dev/null +++ b/linux/src/arch/i386/lib/semaphore.S @@ -0,0 +1,35 @@ +/* + * linux/arch/i386/lib/semaphore.S + * + * Copyright (C) 1996 Linus Torvalds + */ + +#include <linux/linkage.h> + +/* + * "down_failed" is called with the eventual return address + * in %eax, and the address of the semaphore in %ecx. We need + * to increment the number of waiters on the semaphore, + * call "__down()", and then eventually return to try again. + */ +ENTRY(down_failed) + pushl %eax + pushl %ecx + call SYMBOL_NAME(__down) + popl %ecx + ret + +ENTRY(up_wakeup) + pushl %eax + pushl %ecx + call SYMBOL_NAME(__up) + popl %ecx + ret + +ENTRY(down_failed_interruptible) + pushl %eax + pushl %ecx + call SYMBOL_NAME(__down_interruptible) + popl %ecx + ret + |