diff options
Diffstat (limited to 'arch/i386')
-rw-r--r-- | arch/i386/boot/Makefile | 8 | ||||
-rw-r--r-- | arch/i386/boot/compressed/Makefile | 2 | ||||
-rw-r--r-- | arch/i386/boot/compressed/head.S | 4 | ||||
-rw-r--r-- | arch/i386/boot/setup.S | 4 | ||||
-rw-r--r-- | arch/i386/config.in | 11 | ||||
-rw-r--r-- | arch/i386/defconfig | 24 | ||||
-rw-r--r-- | arch/i386/kernel/Makefile | 4 | ||||
-rw-r--r-- | arch/i386/kernel/acpi.c | 1344 | ||||
-rw-r--r-- | arch/i386/kernel/apm.c | 150 | ||||
-rw-r--r-- | arch/i386/kernel/entry.S | 17 | ||||
-rw-r--r-- | arch/i386/kernel/head.S | 26 | ||||
-rw-r--r-- | arch/i386/kernel/i386_ksyms.c | 3 | ||||
-rw-r--r-- | arch/i386/kernel/irq.c | 46 | ||||
-rw-r--r-- | arch/i386/kernel/mtrr.c | 32 | ||||
-rw-r--r-- | arch/i386/kernel/pci-i386.c | 60 | ||||
-rw-r--r-- | arch/i386/kernel/pci-pc.c | 98 | ||||
-rw-r--r-- | arch/i386/kernel/pci-visws.c | 4 | ||||
-rw-r--r-- | arch/i386/kernel/process.c | 114 | ||||
-rw-r--r-- | arch/i386/kernel/setup.c | 17 | ||||
-rw-r--r-- | arch/i386/kernel/smp.c | 8 | ||||
-rw-r--r-- | arch/i386/kernel/vm86.c | 2 | ||||
-rw-r--r-- | arch/i386/math-emu/Makefile | 2 |
22 files changed, 1729 insertions, 251 deletions
diff --git a/arch/i386/boot/Makefile b/arch/i386/boot/Makefile index 56a941a62..e75eb2fdf 100644 --- a/arch/i386/boot/Makefile +++ b/arch/i386/boot/Makefile @@ -49,7 +49,7 @@ bootsect.o: bootsect.s $(AS) -o $@ $< bootsect.s: bootsect.S Makefile $(BOOT_INCL) - $(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ + $(CPP) $(CPPFLAGS) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ bbootsect: bbootsect.o $(LD) -Ttext 0x0 -s -oformat binary $< -o $@ @@ -58,7 +58,7 @@ bbootsect.o: bbootsect.s $(AS) -o $@ $< bbootsect.s: bootsect.S Makefile $(BOOT_INCL) - $(CPP) -D__BIG_KERNEL__ -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ + $(CPP) $(CPPFLAGS) -D__BIG_KERNEL__ -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ setup: setup.o $(LD) -Ttext 0x0 -s -oformat binary -e begtext -o $@ $< @@ -67,7 +67,7 @@ setup.o: setup.s $(AS) -o $@ $< setup.s: setup.S video.S Makefile $(BOOT_INCL) $(TOPDIR)/include/linux/version.h $(TOPDIR)/include/linux/compile.h - $(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ + $(CPP) $(CPPFLAGS) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ bsetup: bsetup.o $(LD) -Ttext 0x0 -s -oformat binary -e begtext -o $@ $< @@ -76,7 +76,7 @@ bsetup.o: bsetup.s $(AS) -o $@ $< bsetup.s: setup.S video.S Makefile $(BOOT_INCL) $(TOPDIR)/include/linux/version.h $(TOPDIR)/include/linux/compile.h - $(CPP) -D__BIG_KERNEL__ -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ + $(CPP) $(CPPFLAGS) -D__BIG_KERNEL__ -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ dep: diff --git a/arch/i386/boot/compressed/Makefile b/arch/i386/boot/compressed/Makefile index 83a245add..6e4b4998d 100644 --- a/arch/i386/boot/compressed/Makefile +++ b/arch/i386/boot/compressed/Makefile @@ -9,7 +9,7 @@ SYSTEM = $(TOPDIR)/vmlinux OBJECTS = $(HEAD) misc.o -CFLAGS = -O2 -DSTDC_HEADERS +CFLAGS = $(CPPFLAGS) -O2 -DSTDC_HEADERS ZLDFLAGS = -e startup_32 # diff --git a/arch/i386/boot/compressed/head.S b/arch/i386/boot/compressed/head.S index 0aa8ddc44..eb2a9a2c5 100644 --- a/arch/i386/boot/compressed/head.S +++ b/arch/i386/boot/compressed/head.S @@ -53,9 +53,9 @@ startup_32: xorl %eax,%eax # Back to 0 mov %cx,%ax # SP low 16 bits movl %eax,%esp - pushl 0 # Clear NT + pushl $0 # Clear NT popfl - ljmp $(__KERNEL_CS), $0x100000 # Into C and sanity + ljmp $(__KERNEL_CS), $0x100000 # Into C and sanity 2: #endif diff --git a/arch/i386/boot/setup.S b/arch/i386/boot/setup.S index cbfa7b3e8..1a08bc3ab 100644 --- a/arch/i386/boot/setup.S +++ b/arch/i386/boot/setup.S @@ -202,9 +202,9 @@ bad_sig: xorb %bh, %bh movb (497), %bl # get setup sect from bootsect subw $4, %bx # LILO loads 4 sectors of setup - shlw $7, %bx # convert to dwords (1sect=2^7 dwords) + shlw $8, %bx # convert to words (1sect=2^8 words) movw %bx, %cx - shrw $2, %bx # convert to segment + shrw $3, %bx # convert to segment addw $SYSSEG, %bx movw %bx, %cs:start_sys_seg # Move rest of setup code/data to here diff --git a/arch/i386/config.in b/arch/i386/config.in index 4a09997b2..654602855 100644 --- a/arch/i386/config.in +++ b/arch/i386/config.in @@ -119,6 +119,13 @@ tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC source drivers/parport/Config.in +bool 'ACPI support' CONFIG_ACPI +if [ "$CONFIG_ACPI" != "n" ]; then + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool ' Enter S1 for sleep (EXPERIMENTAL)' CONFIG_ACPI_S1_SLEEP + fi +fi + bool 'Advanced Power Management BIOS support' CONFIG_APM if [ "$CONFIG_APM" != "n" ]; then bool ' Ignore USER SUSPEND' CONFIG_APM_IGNORE_USER_SUSPEND @@ -129,6 +136,8 @@ if [ "$CONFIG_APM" != "n" ]; then bool ' Ignore multiple suspend/resume cycles' CONFIG_APM_IGNORE_SUSPEND_BOUNCE bool ' RTC stores time in GMT' CONFIG_APM_RTC_IS_GMT bool ' Allow interrupts during APM BIOS calls' CONFIG_APM_ALLOW_INTS + bool ' Entry point offset fix (some Acer laptops)' CONFIG_APM_BAD_ENTRY_OFFSET + bool ' Use real mode APM BIOS call to power off' CONFIG_APM_REAL_MODE_POWER_OFF fi endmenu @@ -142,6 +151,8 @@ if [ "$CONFIG_NET" = "y" ]; then source net/Config.in fi +source drivers/telephony/Config.in + mainmenu_option next_comment comment 'SCSI support' diff --git a/arch/i386/defconfig b/arch/i386/defconfig index 8d1e2a67f..9b248a6be 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -59,8 +59,9 @@ CONFIG_PCI_NAMES=y # CONFIG_PCMCIA=y CONFIG_CARDBUS=y -CONFIG_I82365=y -# CONFIG_TCIC is not set +CONFIG_YENTA=y +# CONFIG_I82365 is not set +CONFIG_TCIC=y CONFIG_SYSVIPC=y # CONFIG_BSD_PROCESS_ACCT is not set CONFIG_SYSCTL=y @@ -70,6 +71,7 @@ CONFIG_BINFMT_AOUT=y CONFIG_BINFMT_ELF=y CONFIG_BINFMT_MISC=y # CONFIG_PARPORT is not set +CONFIG_ACPI=y # CONFIG_APM is not set # @@ -105,8 +107,6 @@ CONFIG_BLK_DEV_IDEPCI=y # CONFIG_BLK_DEV_IDEDMA_PCI is not set # CONFIG_BLK_DEV_OFFBOARD is not set # CONFIG_BLK_DEV_AEC6210 is not set -CONFIG_BLK_DEV_PIIX=y -# CONFIG_BLK_DEV_PIIX_TUNING is not set # CONFIG_IDE_CHIPSETS is not set # CONFIG_BLK_CPQ_DA is not set @@ -155,6 +155,12 @@ CONFIG_SKB_LARGE=y # CONFIG_ATALK is not set # +# Telephony Support +# +# CONFIG_PHONE is not set +# CONFIG_PHONE_IXJ is not set + +# # SCSI support # CONFIG_SCSI=y @@ -311,6 +317,7 @@ CONFIG_PCMCIA_PCNET=y # CONFIG_PCMCIA_3C575 is not set # CONFIG_PCMCIA_TULIP is not set # CONFIG_PCMCIA_EPIC100 is not set +CONFIG_NET_PCMCIA_RADIO=y CONFIG_PCMCIA_RAYCS=y # CONFIG_PCMCIA_NETWAVE is not set # CONFIG_PCMCIA_WAVELAN is not set @@ -349,6 +356,11 @@ CONFIG_UNIX98_PTYS=y CONFIG_UNIX98_PTY_COUNT=256 # +# I2C support +# +# CONFIG_I2C is not set + +# # Mice # # CONFIG_BUSMOUSE is not set @@ -393,14 +405,13 @@ CONFIG_DRM_TDFX=y # CONFIG_PCMCIA_SERIAL_CB is not set # -# Support for USB +# USB support # # CONFIG_USB is not set # # Misc devices # -CONFIG_ACPI=y # # Filesystems @@ -412,6 +423,7 @@ CONFIG_AUTOFS_FS=y # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set # CONFIG_VFAT_FS is not set +# CONFIG_CRAMFS is not set CONFIG_ISO9660_FS=y # CONFIG_JOLIET is not set # CONFIG_MINIX_FS is not set diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile index 55790ac25..e60e15620 100644 --- a/arch/i386/kernel/Makefile +++ b/arch/i386/kernel/Makefile @@ -39,6 +39,10 @@ else endif endif +ifeq ($(CONFIG_ACPI),y) + OX_OBJS += acpi.o +endif + ifeq ($(CONFIG_APM),y) OX_OBJS += apm.o else diff --git a/arch/i386/kernel/acpi.c b/arch/i386/kernel/acpi.c new file mode 100644 index 000000000..49444d258 --- /dev/null +++ b/arch/i386/kernel/acpi.c @@ -0,0 +1,1344 @@ +/* + * acpi.c - Linux ACPI driver + * + * Copyright (C) 1999 Andrew Henroid + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * See http://www.geocities.com/SiliconValley/Hardware/3165/ + * for the user-level ACPI stuff + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/miscdevice.h> +#include <linux/sched.h> +#include <linux/time.h> +#include <linux/wait.h> +#include <linux/spinlock.h> +#include <linux/ioport.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <linux/sysctl.h> +#include <linux/delay.h> +#include <linux/acpi.h> + +/* + * Defines for 2.2.x + */ +#ifndef __exit +#define __exit +#endif +#ifndef module_init +#define module_init(x) int init_module(void) {return x();} +#endif +#ifndef module_exit +#define module_exit(x) void cleanup_module(void) {x();} +#endif +#ifndef DECLARE_WAIT_QUEUE_HEAD +#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue * x = NULL +#endif + +/* + * Yes, it's unfortunate that we are relying on get_cmos_time + * because it is slow (> 1 sec.) and i386 only. It might be better + * to use some of the code from drivers/char/rtc.c in the near future + */ +extern unsigned long get_cmos_time(void); + +static int acpi_control_thread(void *context); +static int acpi_do_ulong(ctl_table *ctl, + int write, + struct file *file, + void *buffer, + size_t *len); +static int acpi_do_event_reg(ctl_table *ctl, + int write, + struct file *file, + void *buffer, + size_t *len); +static int acpi_do_event(ctl_table *ctl, + int write, + struct file *file, + void *buffer, + size_t *len); +static int acpi_do_sleep(ctl_table *ctl, + int write, + struct file *file, + void *buffer, + size_t *len); + +DECLARE_WAIT_QUEUE_HEAD(acpi_control_wait); + +static struct ctl_table_header *acpi_sysctl = NULL; + +static struct acpi_facp *acpi_facp = NULL; +static int acpi_fake_facp = 0; +static struct acpi_facs *acpi_facs = NULL; +static unsigned long acpi_facp_addr = 0; +static unsigned long acpi_dsdt_addr = 0; + +// current system sleep state (S0 - S4) +static acpi_sstate_t acpi_sleep_state = ACPI_S0; +// time sleep began +static unsigned long acpi_sleep_start = 0; + +static spinlock_t acpi_event_lock = SPIN_LOCK_UNLOCKED; +static volatile u32 acpi_pm1_status = 0; +static volatile u32 acpi_gpe_status = 0; +static volatile u32 acpi_gpe_level = 0; +static volatile acpi_sstate_t acpi_event_state = ACPI_S0; +static DECLARE_WAIT_QUEUE_HEAD(acpi_event_wait); + +static spinlock_t acpi_devs_lock = SPIN_LOCK_UNLOCKED; +static LIST_HEAD(acpi_devs); + +/* Make it impossible to enter C2/C3 until after we've initialized */ +static unsigned long acpi_enter_lvl2_lat = ACPI_INFINITE_LAT; +static unsigned long acpi_enter_lvl3_lat = ACPI_INFINITE_LAT; +static unsigned long acpi_p_lvl2_lat = ACPI_INFINITE_LAT; +static unsigned long acpi_p_lvl3_lat = ACPI_INFINITE_LAT; + +static unsigned long acpi_p_blk = 0; + +static int acpi_p_lvl2_tested = 0; +static int acpi_p_lvl3_tested = 0; + +static int acpi_disabled = 0; +int acpi_active = 0; + +// bits 8-15 are SLP_TYPa, bits 0-7 are SLP_TYPb +static unsigned long acpi_slp_typ[] = +{ + ACPI_SLP_TYP_DISABLED, /* S0 */ + ACPI_SLP_TYP_DISABLED, /* S1 */ + ACPI_SLP_TYP_DISABLED, /* S2 */ + ACPI_SLP_TYP_DISABLED, /* S3 */ + ACPI_SLP_TYP_DISABLED, /* S4 */ + ACPI_SLP_TYP_DISABLED /* S5 */ +}; + +static struct ctl_table acpi_table[] = +{ + {ACPI_FACP, "facp", + &acpi_facp_addr, sizeof(acpi_facp_addr), + 0400, NULL, &acpi_do_ulong}, + + {ACPI_DSDT, "dsdt", + &acpi_dsdt_addr, sizeof(acpi_dsdt_addr), + 0400, NULL, &acpi_do_ulong}, + + {ACPI_PM1_ENABLE, "pm1_enable", + NULL, 0, + 0600, NULL, &acpi_do_event_reg}, + + {ACPI_GPE_ENABLE, "gpe_enable", + NULL, 0, + 0600, NULL, &acpi_do_event_reg}, + + {ACPI_GPE_LEVEL, "gpe_level", + NULL, 0, + 0600, NULL, &acpi_do_event_reg}, + + {ACPI_EVENT, "event", NULL, 0, 0400, NULL, &acpi_do_event}, + + {ACPI_P_BLK, "p_blk", + &acpi_p_blk, sizeof(acpi_p_blk), + 0600, NULL, &acpi_do_ulong}, + + {ACPI_P_LVL2_LAT, "p_lvl2_lat", + &acpi_p_lvl2_lat, sizeof(acpi_p_lvl2_lat), + 0644, NULL, &acpi_do_ulong}, + + {ACPI_P_LVL3_LAT, "p_lvl3_lat", + &acpi_p_lvl3_lat, sizeof(acpi_p_lvl3_lat), + 0644, NULL, &acpi_do_ulong}, + + {ACPI_S0_SLP_TYP, "s0_slp_typ", + &acpi_slp_typ[ACPI_S0], sizeof(acpi_slp_typ[ACPI_S0]), + 0600, NULL, &acpi_do_ulong}, + + {ACPI_S1_SLP_TYP, "s1_slp_typ", + &acpi_slp_typ[ACPI_S1], sizeof(acpi_slp_typ[ACPI_S1]), + 0600, NULL, &acpi_do_ulong}, + + {ACPI_S5_SLP_TYP, "s5_slp_typ", + &acpi_slp_typ[ACPI_S5], sizeof(acpi_slp_typ[ACPI_S5]), + 0600, NULL, &acpi_do_ulong}, + + {ACPI_SLEEP, "sleep", NULL, 0, 0600, NULL, &acpi_do_sleep}, + + {0} +}; + +static struct ctl_table acpi_dir_table[] = +{ + {CTL_ACPI, "acpi", NULL, 0, 0555, acpi_table}, + {0} +}; + + +/* + * Get the value of the PM1 control register (SCI_EN, ...) + */ +static u32 acpi_read_pm1_control(struct acpi_facp *facp) +{ + u32 value = 0; + if (facp->pm1a_cnt) + value = inw(facp->pm1a_cnt); + if (facp->pm1b_cnt) + value |= inw(facp->pm1b_cnt); + return value; +} + +/* + * Set the value of the PM1 control register (BM_RLD, ...) + */ +static void acpi_write_pm1_control(struct acpi_facp *facp, u32 value) +{ + if (facp->pm1a_cnt) + outw(value, facp->pm1a_cnt); + if (facp->pm1b_cnt) + outw(value, facp->pm1b_cnt); +} + +/* + * Get the value of the fixed event status register + */ +static u32 acpi_read_pm1_status(struct acpi_facp *facp) +{ + u32 value = 0; + if (facp->pm1a_evt) + value = inw(facp->pm1a_evt); + if (facp->pm1b_evt) + value |= inw(facp->pm1b_evt); + return value; +} + +/* + * Set the value of the fixed event status register (clear events) + */ +static void acpi_write_pm1_status(struct acpi_facp *facp, u32 value) +{ + if (facp->pm1a_evt) + outw(value, facp->pm1a_evt); + if (facp->pm1b_evt) + outw(value, facp->pm1b_evt); +} + +/* + * Get the value of the fixed event enable register + */ +static u32 acpi_read_pm1_enable(struct acpi_facp *facp) +{ + int offset = facp->pm1_evt_len >> 1; + u32 value = 0; + if (facp->pm1a_evt) + value = inw(facp->pm1a_evt + offset); + if (facp->pm1b_evt) + value |= inw(facp->pm1b_evt + offset); + return value; +} + +/* + * Set the value of the fixed event enable register (enable events) + */ +static void acpi_write_pm1_enable(struct acpi_facp *facp, u32 value) +{ + int offset = facp->pm1_evt_len >> 1; + if (facp->pm1a_evt) + outw(value, facp->pm1a_evt + offset); + if (facp->pm1b_evt) + outw(value, facp->pm1b_evt + offset); +} + +/* + * Get the value of the general-purpose event status register + */ +static u32 acpi_read_gpe_status(struct acpi_facp *facp) +{ + u32 value = 0; + int i, size; + + if (facp->gpe1) { + size = facp->gpe1_len >> 1; + for (i = size - 1; i >= 0; i--) + value = (value << 8) | inb(facp->gpe1 + i); + } + if (facp->gpe0) { + size = facp->gpe0_len >> 1; + for (i = size - 1; i >= 0; i--) + value = (value << 8) | inb(facp->gpe0 + i); + } + return value; +} + +/* + * Set the value of the general-purpose event status register (clear events) + */ +static void acpi_write_gpe_status(struct acpi_facp *facp, u32 value) +{ + int i, size; + + if (facp->gpe0) { + size = facp->gpe0_len >> 1; + for (i = 0; i < size; i++) { + outb(value & 0xff, facp->gpe0 + i); + value >>= 8; + } + } + if (facp->gpe1) { + size = facp->gpe1_len >> 1; + for (i = 0; i < size; i++) { + outb(value & 0xff, facp->gpe1 + i); + value >>= 8; + } + } +} + +/* + * Get the value of the general-purpose event enable register + */ +static u32 acpi_read_gpe_enable(struct acpi_facp *facp) +{ + u32 value = 0; + int i, size, offset; + + offset = facp->gpe0_len >> 1; + if (facp->gpe1) { + size = facp->gpe1_len >> 1; + for (i = size - 1; i >= 0; i--) { + value = (value << 8) | inb(facp->gpe1 + offset + i); + } + } + if (facp->gpe0) { + size = facp->gpe0_len >> 1; + for (i = size - 1; i >= 0; i--) + value = (value << 8) | inb(facp->gpe0 + offset + i); + } + return value; +} + +/* + * Set the value of the general-purpose event enable register (enable events) + */ +static void acpi_write_gpe_enable(struct acpi_facp *facp, u32 value) +{ + int i, offset; + + offset = facp->gpe0_len >> 1; + if (facp->gpe0) { + for (i = 0; i < offset; i++) { + outb(value & 0xff, facp->gpe0 + offset + i); + value >>= 8; + } + } + if (facp->gpe1) { + offset = facp->gpe1_len >> 1; + for (i = 0; i < offset; i++) { + outb(value & 0xff, facp->gpe1 + offset + i); + value >>= 8; + } + } +} + +/* + * Map an ACPI table into virtual memory + */ +static struct acpi_table *__init acpi_map_table(u32 addr) +{ + struct acpi_table *table = NULL; + if (addr) { + // map table header to determine size + table = (struct acpi_table *) + ioremap((unsigned long) addr, + sizeof(struct acpi_table)); + if (table) { + unsigned long table_size = table->length; + iounmap(table); + // remap entire table + table = (struct acpi_table *) + ioremap((unsigned long) addr, table_size); + } + + if (!table) { + /* ioremap is a pain, it returns NULL if the + * table starts within mapped physical memory. + * Hopefully, no table straddles a mapped/unmapped + * physical memory boundary, ugh + */ + table = (struct acpi_table*) phys_to_virt(addr); + } + } + return table; +} + +/* + * Unmap an ACPI table from virtual memory + */ +static void acpi_unmap_table(struct acpi_table *table) +{ + // iounmap ignores addresses within physical memory + if (table) + iounmap(table); +} + +/* + * Locate and map ACPI tables + */ +static int __init acpi_find_tables(void) +{ + struct acpi_rsdp *rsdp; + struct acpi_table *rsdt; + u32 *rsdt_entry; + int rsdt_entry_count; + unsigned long i; + + // search BIOS memory for RSDP + for (i = ACPI_BIOS_ROM_BASE; i < ACPI_BIOS_ROM_END; i += 16) { + rsdp = (struct acpi_rsdp *) phys_to_virt(i); + if (rsdp->signature[0] == ACPI_RSDP1_SIG + && rsdp->signature[1] == ACPI_RSDP2_SIG) { + char oem[7]; + int j; + + // strip trailing space and print OEM identifier + memcpy(oem, rsdp->oem, 6); + oem[6] = '\0'; + for (j = 5; + j > 0 && (oem[j] == '\0' || oem[j] == ' '); + j--) { + oem[j] = '\0'; + } + printk(KERN_INFO "ACPI: \"%s\" found at 0x%p\n", + oem, (void *) i); + + break; + } + } + if (i >= ACPI_BIOS_ROM_END) + return -ENODEV; + + // fetch RSDT from RSDP + rsdt = acpi_map_table(rsdp->rsdt); + if (!rsdt) { + printk(KERN_ERR "ACPI: missing RSDT at 0x%p\n", + (void*) rsdp->rsdt); + return -ENODEV; + } + else if (rsdt->signature != ACPI_RSDT_SIG) { + printk(KERN_ERR "ACPI: bad RSDT at 0x%p (%08x)\n", + (void*) rsdp->rsdt, (unsigned) rsdt->signature); + acpi_unmap_table(rsdt); + return -ENODEV; + } + // search RSDT for FACP + acpi_facp = NULL; + rsdt_entry = (u32 *) (rsdt + 1); + rsdt_entry_count = (int) ((rsdt->length - sizeof(*rsdt)) >> 2); + while (rsdt_entry_count) { + struct acpi_table *dt = acpi_map_table(*rsdt_entry); + if (dt && dt->signature == ACPI_FACP_SIG) { + acpi_facp = (struct acpi_facp*) dt; + acpi_facp_addr = *rsdt_entry; + acpi_dsdt_addr = acpi_facp->dsdt; + + // map FACS if it exists + if (acpi_facp->facs) { + dt = acpi_map_table(acpi_facp->facs); + if (dt && dt->signature == ACPI_FACS_SIG) { + acpi_facs = (struct acpi_facs*) dt; + } + else { + acpi_unmap_table(dt); + } + } + } + else { + acpi_unmap_table(dt); + } + rsdt_entry++; + rsdt_entry_count--; + } + + acpi_unmap_table(rsdt); + + if (!acpi_facp) { + printk(KERN_ERR "ACPI: missing FACP\n"); + return -ENODEV; + } + return 0; +} + +/* + * Unmap or destroy ACPI tables + */ +static void acpi_destroy_tables(void) +{ + if (!acpi_fake_facp) + acpi_unmap_table((struct acpi_table*) acpi_facp); + else + kfree(acpi_facp); + acpi_unmap_table((struct acpi_table*) acpi_facs); +} + +/* + * Locate PIIX4 device and create a fake FACP + */ +static int __init acpi_find_piix4(void) +{ + struct pci_dev *dev; + u32 base; + u16 cmd; + u8 pmregmisc; + + dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82371AB_3, + NULL); + if (!dev) + return -ENODEV; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + if (!(cmd & PCI_COMMAND_IO)) + return -ENODEV; + + pci_read_config_byte(dev, ACPI_PIIX4_PMREGMISC, &pmregmisc); + if (!(pmregmisc & ACPI_PIIX4_PMIOSE)) + return -ENODEV; + + pci_read_config_dword(dev, 0x40, &base); + if (!(base & PCI_BASE_ADDRESS_SPACE_IO)) + return -ENODEV; + + base &= PCI_BASE_ADDRESS_IO_MASK; + if (!base) + return -ENODEV; + + printk(KERN_INFO "ACPI: found PIIX4 at 0x%04x\n", base); + + acpi_facp = kmalloc(sizeof(struct acpi_facp), GFP_KERNEL); + if (!acpi_facp) + return -ENOMEM; + + acpi_fake_facp = 1; + memset(acpi_facp, 0, sizeof(struct acpi_facp)); + acpi_facp->int_model = ACPI_PIIX4_INT_MODEL; + acpi_facp->sci_int = ACPI_PIIX4_SCI_INT; + acpi_facp->smi_cmd = ACPI_PIIX4_SMI_CMD; + acpi_facp->acpi_enable = ACPI_PIIX4_ACPI_ENABLE; + acpi_facp->acpi_disable = ACPI_PIIX4_ACPI_DISABLE; + acpi_facp->s4bios_req = ACPI_PIIX4_S4BIOS_REQ; + acpi_facp->pm1a_evt = base + ACPI_PIIX4_PM1_EVT; + acpi_facp->pm1a_cnt = base + ACPI_PIIX4_PM1_CNT; + acpi_facp->pm2_cnt = ACPI_PIIX4_PM2_CNT; + acpi_facp->pm_tmr = base + ACPI_PIIX4_PM_TMR; + acpi_facp->gpe0 = base + ACPI_PIIX4_GPE0; + acpi_facp->pm1_evt_len = ACPI_PIIX4_PM1_EVT_LEN; + acpi_facp->pm1_cnt_len = ACPI_PIIX4_PM1_CNT_LEN; + acpi_facp->pm2_cnt_len = ACPI_PIIX4_PM2_CNT_LEN; + acpi_facp->pm_tm_len = ACPI_PIIX4_PM_TM_LEN; + acpi_facp->gpe0_len = ACPI_PIIX4_GPE0_LEN; + acpi_facp->p_lvl2_lat = (__u16) ACPI_INFINITE_LAT; + acpi_facp->p_lvl3_lat = (__u16) ACPI_INFINITE_LAT; + + acpi_facp_addr = virt_to_phys(acpi_facp); + acpi_dsdt_addr = 0; + + acpi_p_blk = base + ACPI_PIIX4_P_BLK; + + return 0; +} + +/* + * Handle an ACPI SCI (fixed or general purpose event) + */ +static void acpi_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + u32 pm1_status, gpe_status, gpe_level, gpe_edge; + unsigned long flags; + + // detect and clear fixed events + pm1_status = (acpi_read_pm1_status(acpi_facp) + & acpi_read_pm1_enable(acpi_facp)); + acpi_write_pm1_status(acpi_facp, pm1_status); + + // detect and handle general-purpose events + gpe_status = (acpi_read_gpe_status(acpi_facp) + & acpi_read_gpe_enable(acpi_facp)); + gpe_level = gpe_status & acpi_gpe_level; + if (gpe_level) { + // disable level-triggered events (re-enabled after handling) + acpi_write_gpe_enable( + acpi_facp, + acpi_read_gpe_enable(acpi_facp) & ~gpe_level); + } + gpe_edge = gpe_status & ~gpe_level; + if (gpe_edge) { + // clear edge-triggered events + while (acpi_read_gpe_status(acpi_facp) & gpe_edge) + acpi_write_gpe_status(acpi_facp, gpe_edge); + } + + // notify process waiting on /dev/acpi + spin_lock_irqsave(&acpi_event_lock, flags); + acpi_pm1_status |= pm1_status; + acpi_gpe_status |= gpe_status; + spin_unlock_irqrestore(&acpi_event_lock, flags); + acpi_event_state = acpi_sleep_state; + wake_up_interruptible(&acpi_event_wait); +} + +/* + * Is ACPI enabled or not? + */ +static inline int acpi_is_enabled(struct acpi_facp *facp) +{ + return ((acpi_read_pm1_control(facp) & ACPI_SCI_EN) ? 1:0); +} + +/* + * Enable SCI + */ +static int acpi_enable(struct acpi_facp *facp) +{ + if (facp->smi_cmd) + outb(facp->acpi_enable, facp->smi_cmd); + return (acpi_is_enabled(facp) ? 0:-1); +} + +/* + * Disable SCI + */ +static int acpi_disable(struct acpi_facp *facp) +{ + // disable and clear any pending events + acpi_write_gpe_enable(facp, 0); + while (acpi_read_gpe_status(facp)) + acpi_write_gpe_status(facp, acpi_read_gpe_status(facp)); + acpi_write_pm1_enable(facp, 0); + acpi_write_pm1_status(facp, acpi_read_pm1_status(facp)); + + /* writing acpi_disable to smi_cmd would be appropriate + * here but this causes a nasty crash on many systems + */ + + return 0; +} + +static inline int bm_activity(void) +{ + return 0 && acpi_read_pm1_status(acpi_facp) & ACPI_BM; +} + +static inline void clear_bm_activity(void) +{ + acpi_write_pm1_status(acpi_facp, ACPI_BM); +} + +static void sleep_on_busmaster(void) +{ + u32 pm1_cntr = acpi_read_pm1_control(acpi_facp); + if (pm1_cntr & ACPI_BM_RLD) { + pm1_cntr &= ~ACPI_BM_RLD; + acpi_write_pm1_control(acpi_facp, pm1_cntr); + } +} + +static void wake_on_busmaster(void) +{ + u32 pm1_cntr = acpi_read_pm1_control(acpi_facp); + if (!(pm1_cntr & ACPI_BM_RLD)) { + pm1_cntr |= ACPI_BM_RLD; + acpi_write_pm1_control(acpi_facp, pm1_cntr); + } + clear_bm_activity(); +} + +/* + * Idle loop (uniprocessor only) + */ +static void acpi_idle_handler(void) +{ + static int sleep_level = 1; + + if (!acpi_facp->pm_tmr || !acpi_p_blk) + goto not_initialized; + + /* + * start from the previous sleep level.. + */ + if (sleep_level == 1) + goto sleep1; + if (sleep_level == 2 || bm_activity()) + goto sleep2; +sleep3: + sleep_level = 3; + if (!acpi_p_lvl3_tested) { + printk("ACPI C3 works\n"); + acpi_p_lvl3_tested = 1; + } + wake_on_busmaster(); + if (acpi_facp->pm2_cnt) + goto sleep3_with_arbiter; + + for (;;) { + unsigned long time; + __cli(); + if (current->need_resched) + goto out; + time = inl(acpi_facp->pm_tmr); + inb(acpi_p_blk + ACPI_P_LVL3); + time = inl(acpi_facp->pm_tmr) - time; + __sti(); + if (time > acpi_p_lvl3_lat || bm_activity()) + goto sleep2; + } + +sleep3_with_arbiter: + for (;;) { + unsigned long time; + unsigned int pm2_cntr = acpi_facp->pm2_cnt; + __cli(); + if (current->need_resched) + goto out; + time = inl(acpi_facp->pm_tmr); + outb(inb(pm2_cntr) | ACPI_ARB_DIS, pm2_cntr); + inb(acpi_p_blk + ACPI_P_LVL3); + outb(inb(pm2_cntr) & ~ACPI_ARB_DIS, pm2_cntr); + time = inl(acpi_facp->pm_tmr) - time; + __sti(); + if (time > acpi_p_lvl3_lat || bm_activity()) + goto sleep2; + } + +sleep2: + sleep_level = 2; + if (!acpi_p_lvl2_tested) { + printk("ACPI C2 works\n"); + acpi_p_lvl2_tested = 1; + } + wake_on_busmaster(); /* Required to track BM activity.. */ + for (;;) { + unsigned long time; + __cli(); + if (current->need_resched) + goto out; + time = inl(acpi_facp->pm_tmr); + inb(acpi_p_blk + ACPI_P_LVL2); + time = inl(acpi_facp->pm_tmr) - time; + __sti(); + if (time > acpi_p_lvl2_lat) + goto sleep1; + if (bm_activity()) { + clear_bm_activity(); + continue; + } + if (time < acpi_enter_lvl3_lat) + goto sleep3; + } + +sleep1: + sleep_level = 1; + sleep_on_busmaster(); + for (;;) { + unsigned long time; + __cli(); + if (current->need_resched) + goto out; + time = inl(acpi_facp->pm_tmr); + __asm__ __volatile__("sti ; hlt": : :"memory"); + time = inl(acpi_facp->pm_tmr) - time; + if (time < acpi_enter_lvl2_lat) + goto sleep2; + } + +not_initialized: + for (;;) { + __cli(); + if (current->need_resched) + goto out; + __asm__ __volatile__("sti ; hlt": : :"memory"); + } + +out: + __sti(); +} + +/* + * Put all devices into specified D-state + */ +static int acpi_enter_dx(acpi_dstate_t state) +{ + int status = 0; + struct list_head *i = acpi_devs.next; + + while (i != &acpi_devs) { + struct acpi_dev *dev = list_entry(i, struct acpi_dev, entry); + if (dev->state != state) { + int dev_status = 0; + if (dev->info.transition) + dev_status = dev->info.transition(dev, state); + if (!dev_status) { + // put hardware into D-state + dev->state = state; + } + if (dev_status) + status = dev_status; + } + + i = i->next; + } + + return status; +} + +/* + * Update system time from real-time clock + */ +static void acpi_update_clock(void) +{ + if (acpi_sleep_start) { + unsigned long delta; + struct timeval tv; + + delta = get_cmos_time() - acpi_sleep_start; + do_gettimeofday(&tv); + tv.tv_sec += delta; + do_settimeofday(&tv); + + acpi_sleep_start = 0; + } +} + + +/* + * Enter system sleep state + */ +static void acpi_enter_sx(acpi_sstate_t state) +{ + unsigned long slp_typ = acpi_slp_typ[(int) state]; + if (slp_typ != ACPI_SLP_TYP_DISABLED) { + u16 typa, typb, value; + + // bits 8-15 are SLP_TYPa, bits 0-7 are SLP_TYPb + typa = (slp_typ >> 8) & 0xff; + typb = slp_typ & 0xff; + + typa = ((typa << ACPI_SLP_TYP_SHIFT) & ACPI_SLP_TYP_MASK); + typb = ((typb << ACPI_SLP_TYP_SHIFT) & ACPI_SLP_TYP_MASK); + + if (state != ACPI_S0) { + acpi_sleep_start = get_cmos_time(); + acpi_enter_dx(ACPI_D3); + acpi_sleep_state = state; + } + + // clear wake status + acpi_write_pm1_status(acpi_facp, ACPI_WAK); + + // set SLP_TYPa/b and SLP_EN + if (acpi_facp->pm1a_cnt) { + value = inw(acpi_facp->pm1a_cnt) & ~ACPI_SLP_TYP_MASK; + outw(value | typa | ACPI_SLP_EN, acpi_facp->pm1a_cnt); + } + if (acpi_facp->pm1b_cnt) { + value = inw(acpi_facp->pm1b_cnt) & ~ACPI_SLP_TYP_MASK; + outw(value | typb | ACPI_SLP_EN, acpi_facp->pm1b_cnt); + } + + if (state == ACPI_S0) { + acpi_sleep_state = state; + acpi_enter_dx(ACPI_D0); + acpi_sleep_start = 0; + } + else if (state == ACPI_S1) { + // wait until S1 is entered + while (!(acpi_read_pm1_status(acpi_facp) & ACPI_WAK)) ; + // finished sleeping, update system time + acpi_update_clock(); + } + } +} + +/* + * Enter soft-off (S5) + */ +static void acpi_power_off_handler(void) +{ + acpi_enter_sx(ACPI_S5); +} + +/* + * Claim ACPI I/O ports + */ +static int acpi_claim_ioports(struct acpi_facp *facp) +{ + // we don't get a guarantee of contiguity for any of the ACPI registers + if (facp->pm1a_evt) + request_region(facp->pm1a_evt, facp->pm1_evt_len, "acpi"); + if (facp->pm1b_evt) + request_region(facp->pm1b_evt, facp->pm1_evt_len, "acpi"); + if (facp->pm1a_cnt) + request_region(facp->pm1a_cnt, facp->pm1_cnt_len, "acpi"); + if (facp->pm1b_cnt) + request_region(facp->pm1b_cnt, facp->pm1_cnt_len, "acpi"); + if (facp->pm_tmr) + request_region(facp->pm_tmr, facp->pm_tm_len, "acpi"); + if (facp->gpe0) + request_region(facp->gpe0, facp->gpe0_len, "acpi"); + if (facp->gpe1) + request_region(facp->gpe1, facp->gpe1_len, "acpi"); + + return 0; +} + +/* + * Free ACPI I/O ports + */ +static int acpi_release_ioports(struct acpi_facp *facp) +{ + // we don't get a guarantee of contiguity for any of the ACPI registers + if (facp->pm1a_evt) + release_region(facp->pm1a_evt, facp->pm1_evt_len); + if (facp->pm1b_evt) + release_region(facp->pm1b_evt, facp->pm1_evt_len); + if (facp->pm1a_cnt) + release_region(facp->pm1a_cnt, facp->pm1_cnt_len); + if (facp->pm1b_cnt) + release_region(facp->pm1b_cnt, facp->pm1_cnt_len); + if (facp->pm_tmr) + release_region(facp->pm_tmr, facp->pm_tm_len); + if (facp->gpe0) + release_region(facp->gpe0, facp->gpe0_len); + if (facp->gpe1) + release_region(facp->gpe1, facp->gpe1_len); + + return 0; +} + +/* + * Examine/modify value + */ +static int acpi_do_ulong(ctl_table *ctl, + int write, + struct file *file, + void *buffer, + size_t *len) +{ + char str[2 * sizeof(unsigned long) + 4], *strend; + unsigned long val; + int size; + + if (!write) { + if (file->f_pos) { + *len = 0; + return 0; + } + + val = *(unsigned long*) ctl->data; + size = sprintf(str, "0x%08lx\n", val); + if (*len >= size) { + copy_to_user(buffer, str, size); + *len = size; + } + else + *len = 0; + } + else { + size = sizeof(str) - 1; + if (size > *len) + size = *len; + copy_from_user(str, buffer, size); + str[size] = '\0'; + val = simple_strtoul(str, &strend, 0); + if (strend == str) + return -EINVAL; + *(unsigned long*) ctl->data = val; + } + + file->f_pos += *len; + return 0; +} + +/* + * Examine/modify event register + */ +static int acpi_do_event_reg(ctl_table *ctl, + int write, + struct file *file, + void *buffer, + size_t *len) +{ + char str[2 * sizeof(u32) + 4], *strend; + u32 val, enabling; + int size; + + if (!write) { + if (file->f_pos) { + *len = 0; + return 0; + } + + val = 0; + switch (ctl->ctl_name) { + case ACPI_PM1_ENABLE: + val = acpi_read_pm1_enable(acpi_facp); + break; + case ACPI_GPE_ENABLE: + val = acpi_read_gpe_enable(acpi_facp); + break; + case ACPI_GPE_LEVEL: + val = acpi_gpe_level; + break; + } + + size = sprintf(str, "0x%08x\n", val); + if (*len >= size) { + copy_to_user(buffer, str, size); + *len = size; + } + else + *len = 0; + } + else + { + // fetch user value + size = sizeof(str) - 1; + if (size > *len) + size = *len; + copy_from_user(str, buffer, size); + str[size] = '\0'; + val = (u32) simple_strtoul(str, &strend, 0); + if (strend == str) + return -EINVAL; + + // store value in register + switch (ctl->ctl_name) { + case ACPI_PM1_ENABLE: + // clear previously disabled events + enabling = (val + & ~acpi_read_pm1_enable(acpi_facp)); + acpi_write_pm1_status(acpi_facp, enabling); + + if (val) { + // enable ACPI unless it is already + if (!acpi_is_enabled(acpi_facp)) + acpi_enable(acpi_facp); + } + else if (!acpi_read_gpe_enable(acpi_facp)) { + // disable ACPI unless it is already + if (acpi_is_enabled(acpi_facp)) + acpi_disable(acpi_facp); + } + + acpi_write_pm1_enable(acpi_facp, val); + break; + case ACPI_GPE_ENABLE: + // clear previously disabled events + enabling = (val + & ~acpi_read_gpe_enable(acpi_facp)); + while (acpi_read_gpe_status(acpi_facp) & enabling) + acpi_write_gpe_status(acpi_facp, enabling); + + if (val) { + // enable ACPI unless it is already + if (!acpi_is_enabled(acpi_facp)) + acpi_enable(acpi_facp); + } + else if (!acpi_read_pm1_enable(acpi_facp)) { + // disable ACPI unless it is already + if (acpi_is_enabled(acpi_facp)) + acpi_disable(acpi_facp); + } + + acpi_write_gpe_enable(acpi_facp, val); + break; + case ACPI_GPE_LEVEL: + acpi_gpe_level = val; + break; + } + } + + file->f_pos += *len; + return 0; +} + +/* + * Wait for next event + */ +static int acpi_do_event(ctl_table *ctl, + int write, + struct file *file, + void *buffer, + size_t *len) +{ + u32 pm1_status = 0, gpe_status = 0; + acpi_sstate_t event_state = 0; + char str[27]; + int size; + + if (write) + return -EPERM; + if (*len < sizeof(str)) { + *len = 0; + return 0; + } + + for (;;) { + unsigned long flags; + + // we need an atomic exchange here + spin_lock_irqsave(&acpi_event_lock, flags); + pm1_status = acpi_pm1_status; + acpi_pm1_status = 0; + gpe_status = acpi_gpe_status; + acpi_gpe_status = 0; + spin_unlock_irqrestore(&acpi_event_lock, flags); + event_state = acpi_event_state; + + if (pm1_status || gpe_status) + break; + + // wait for an event to arrive + interruptible_sleep_on(&acpi_event_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + } + + size = sprintf(str, "0x%08x 0x%08x 0x%01x\n", + pm1_status, + gpe_status, + event_state); + copy_to_user(buffer, str, size); + *len = size; + file->f_pos += size; + + return 0; +} + +/* + * Enter system sleep state + */ +static int acpi_do_sleep(ctl_table *ctl, + int write, + struct file *file, + void *buffer, + size_t *len) +{ + if (!write) { + if (file->f_pos) { + *len = 0; + return 0; + } + } + else + { +#ifdef CONFIG_ACPI_S1_SLEEP + acpi_enter_sx(ACPI_S1); + acpi_enter_sx(ACPI_S0); +#endif + } + file->f_pos += *len; + return 0; +} + +/* + * Initialize and enable ACPI + */ +static int __init acpi_init(void) +{ + int pid; + + if (acpi_disabled) + return -ENODEV; + + if (acpi_find_tables() && acpi_find_piix4()) { + // no ACPI tables and not PIIX4 + return -ENODEV; + } + + /* + * Internally we always keep latencies in timer + * ticks, which is simpler and more consistent (what is + * an uS to us?). Besides, that gives people more + * control in the /proc interfaces. + */ + if (acpi_facp->p_lvl2_lat + && acpi_facp->p_lvl2_lat <= ACPI_MAX_P_LVL2_LAT) { + acpi_p_lvl2_lat = ACPI_uS_TO_TMR_TICKS(acpi_facp->p_lvl2_lat); + acpi_enter_lvl2_lat = ACPI_uS_TO_TMR_TICKS(ACPI_TMR_HZ / 1000); + } + if (acpi_facp->p_lvl3_lat + && acpi_facp->p_lvl3_lat <= ACPI_MAX_P_LVL3_LAT) { + acpi_p_lvl3_lat = ACPI_uS_TO_TMR_TICKS(acpi_facp->p_lvl3_lat); + acpi_enter_lvl3_lat + = ACPI_uS_TO_TMR_TICKS(acpi_facp->p_lvl3_lat * 5); + } + + if (acpi_facp->sci_int + && request_irq(acpi_facp->sci_int, + acpi_irq, + SA_INTERRUPT | SA_SHIRQ, + "acpi", + acpi_facp)) { + printk(KERN_ERR "ACPI: SCI (IRQ%d) allocation failed\n", + acpi_facp->sci_int); + acpi_destroy_tables(); + return -ENODEV; + } + + acpi_claim_ioports(acpi_facp); + acpi_sysctl = register_sysctl_table(acpi_dir_table, 1); + + pid = kernel_thread(acpi_control_thread, + NULL, + CLONE_FS | CLONE_FILES | CLONE_SIGHAND); + + acpi_power_off = acpi_power_off_handler; + + acpi_active = 1; + + /* + * Set up the ACPI idle function. Note that we can't really + * do this with multiple CPU's, we'd need a per-CPU ACPI + * device.. + */ +#ifdef __SMP__ + if (smp_num_cpus > 1) + return 0; +#endif + + if (acpi_facp->pm_tmr) + acpi_idle = acpi_idle_handler; + + return 0; +} + +/* + * Disable and deinitialize ACPI + */ +static void __exit acpi_exit(void) +{ + acpi_idle = NULL; + acpi_power_off = NULL; + + unregister_sysctl_table(acpi_sysctl); + acpi_disable(acpi_facp); + acpi_release_ioports(acpi_facp); + + if (acpi_facp->sci_int) + free_irq(acpi_facp->sci_int, acpi_facp); + + acpi_destroy_tables(); +} + +static int __init acpi_setup(char *str) +{ + while (str && *str) { + if (strncmp(str, "off", 3) == 0) + acpi_disabled = 1; + else if (strncmp(str, "on", 2) == 0) + acpi_disabled = 0; + str = strpbrk(str, ","); + if (str) + str += strspn(str, ","); + } + return 1; +} + +__setup("acpi=", acpi_setup); + +/* + * Register a device with the ACPI subsystem + */ +struct acpi_dev* acpi_register(struct acpi_dev_info *info, unsigned long adr) +{ + struct acpi_dev *dev = NULL; + if (info) { + dev = kmalloc(sizeof(struct acpi_dev), GFP_KERNEL); + if (dev) { + unsigned long flags; + + memset(dev, 0, sizeof(*dev)); + memcpy(&dev->info, info, sizeof(dev->info)); + dev->adr = adr; + + spin_lock_irqsave(&acpi_devs_lock, flags); + list_add(&dev->entry, &acpi_devs); + spin_unlock_irqrestore(&acpi_devs_lock, flags); + } + } + return dev; +} + +/* + * Unregister a device with ACPI + */ +void acpi_unregister(struct acpi_dev *dev) +{ + if (dev) { + unsigned long flags; + + spin_lock_irqsave(&acpi_devs_lock, flags); + list_del(&dev->entry); + spin_unlock_irqrestore(&acpi_devs_lock, flags); + + kfree(dev); + } +} + +/* + * Wake up a device + */ +void acpi_wakeup(struct acpi_dev *dev) +{ + // run _PS0 or tell parent bus to wake device up +} + +/* + * Manage idle devices + */ +static int acpi_control_thread(void *context) +{ + exit_mm(current); + exit_files(current); + strcpy(current->comm, "acpi"); + + for(;;) { + interruptible_sleep_on(&acpi_control_wait); + if (signal_pending(current)) + break; + + // find all idle devices and set idle timer + } + + return 0; +} + +__initcall(acpi_init); + +/* + * Module visible symbols + */ +EXPORT_SYMBOL(acpi_control_wait); +EXPORT_SYMBOL(acpi_register); +EXPORT_SYMBOL(acpi_unregister); +EXPORT_SYMBOL(acpi_wakeup); diff --git a/arch/i386/kernel/apm.c b/arch/i386/kernel/apm.c index 564e6b42b..8f3d24645 100644 --- a/arch/i386/kernel/apm.c +++ b/arch/i386/kernel/apm.c @@ -34,6 +34,7 @@ * Jan 1999, Version 1.8 * Jan 1999, Version 1.9 * Oct 1999, Version 1.10 + * Nov 1999, Version 1.11 * * History: * 0.6b: first version in official kernel, Linux 1.3.46 @@ -101,6 +102,14 @@ * configurable (default on). * Make debug only a boot time parameter (remove APM_DEBUG). * Try to blank all devices on any error. + * 1.11: Remove APM dependencies in drivers/char/console.c + * Check nr_running to detect if we are idle (from + * Borislav Deianov <borislav@lix.polytechnique.fr>) + * Fix for bioses that don't zero the top part of the + * entrypoint offset (Mario Sitta <sitta@al.unipmn.it>) + * (reported by Panos Katsaloulis <teras@writeme.com>). + * Real mode power off patch (Walter Hofmann + * <Walter.Hofmann@physik.stud.uni-erlangen.de>). * * APM 1.1 Reference: * @@ -135,6 +144,7 @@ #include <linux/miscdevice.h> #include <linux/apm_bios.h> #include <linux/init.h> +#include <linux/sched.h> #include <asm/system.h> #include <asm/uaccess.h> @@ -149,8 +159,14 @@ EXPORT_SYMBOL(apm_register_callback); EXPORT_SYMBOL(apm_unregister_callback); extern unsigned long get_cmos_time(void); +extern void machine_real_restart(unsigned char *, int); +#ifdef CONFIG_MAGIC_SYSRQ extern void (*sysrq_power_off)(void); +#endif +#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT) +extern int (*console_blank_hook)(int); +#endif /* * The apm_bios device is one of the misc char devices. @@ -269,7 +285,7 @@ static int power_off_enabled = 1; static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue); static struct apm_bios_struct * user_list = NULL; -static char driver_version[] = "1.10"; /* no spaces */ +static char driver_version[] = "1.11"; /* no spaces */ static char * apm_event_name[] = { "system standby", @@ -380,7 +396,7 @@ static u8 apm_bios_call(u32 func, u32 ebx_in, u32 ecx_in, __asm__ __volatile__(APM_DO_ZERO_SEGS "pushl %%edi\n\t" "pushl %%ebp\n\t" - "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "; cld\n\t" + "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t" "setc %%al\n\t" "popl %%ebp\n\t" "popl %%edi\n\t" @@ -413,7 +429,7 @@ static u8 apm_bios_call_simple(u32 func, u32 ebx_in, u32 ecx_in, u32 *eax) __asm__ __volatile__(APM_DO_ZERO_SEGS "pushl %%edi\n\t" "pushl %%ebp\n\t" - "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry)"; cld\n\t" + "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry)"\n\t" "setc %%bl\n\t" "popl %%ebp\n\t" "popl %%edi\n\t" @@ -551,24 +567,24 @@ static void apm_power_off(void) * they are doing because they booted with the smp-power-off * kernel option. */ - if (apm_enabled || (smp_hack == 2)) + if (apm_enabled || (smp_hack == 2)) { +#ifdef CONFIG_APM_REAL_MODE_POWER_OFF + unsigned char po_bios_call[] = { + 0xb8, 0x00, 0x10, /* movw $0x1000,ax */ + 0x8e, 0xd0, /* movw ax,ss */ + 0xbc, 0x00, 0xf0, /* movw $0xf000,sp */ + 0xb8, 0x07, 0x53, /* movw $0x5307,ax */ + 0xbb, 0x01, 0x00, /* movw $0x0001,bx */ + 0xb9, 0x03, 0x00, /* movw $0x0003,cx */ + 0xcd, 0x15 /* int $0x15 */ + }; + + machine_real_restart(po_bios_call, sizeof(po_bios_call)); +#else (void) apm_set_power_state(APM_STATE_OFF); -} - -#ifdef CONFIG_APM_DISPLAY_BLANK -/* Called by apm_display_blank and apm_display_unblank when apm_enabled. */ -static int apm_set_display_power_state(u_short state) -{ - int error; - - /* Blank the first display device */ - error = set_power_state(0x0100, state); - if (error != APM_SUCCESS) - /* try to blank them all instead */ - error = set_power_state(0x01ff, state); - return error; -} #endif + } +} #ifdef CONFIG_APM_DO_ENABLE static int __init apm_enable_power_management(void) @@ -651,33 +667,25 @@ static void apm_error(char *str, int err) str, err); } +#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT) /* Called from console driver -- must make sure apm_enabled. */ -int apm_display_blank(void) +static int apm_console_blank(int blank) { -#ifdef CONFIG_APM_DISPLAY_BLANK - if (apm_enabled) { - int error = apm_set_display_power_state(APM_STATE_STANDBY); - if ((error == APM_SUCCESS) || (error == APM_NO_ERROR)) - return 1; - apm_error("set display standby", error); - } -#endif - return 0; -} + int error; + u_short state; -/* Called from console driver -- must make sure apm_enabled. */ -int apm_display_unblank(void) -{ -#ifdef CONFIG_APM_DISPLAY_BLANK - if (apm_enabled) { - int error = apm_set_display_power_state(APM_STATE_READY); - if ((error == APM_SUCCESS) || (error == APM_NO_ERROR)) - return 1; - apm_error("set display ready", error); - } -#endif + state = blank ? APM_STATE_STANDBY : APM_STATE_READY; + /* Blank the first display device */ + error = set_power_state(0x100, state); + if (error != APM_SUCCESS) + /* try to blank them all instead */ + error = set_power_state(0x1ff, state); + if ((error == APM_SUCCESS) || (error == APM_NO_ERROR)) + return 1; + apm_error("set display", error); return 0; } +#endif int apm_register_callback(int (*callback)(apm_event_t)) { @@ -884,12 +892,15 @@ static void check_events(void) case APM_USER_STANDBY: #ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND if (waiting_for_resume) - return; - waiting_for_resume = 1; + break; #endif - if (send_event(event, APM_STANDBY_RESUME, NULL) - && (standbys_pending <= 0)) - standby(); + if (send_event(event, APM_STANDBY_RESUME, NULL)) { +#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND + waiting_for_resume = 1; +#endif + if (standbys_pending <= 0) + standby(); + } break; case APM_USER_SUSPEND: @@ -905,12 +916,15 @@ static void check_events(void) #endif #ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND if (waiting_for_resume) - return; - waiting_for_resume = 1; + break; +#endif + if (send_event(event, APM_NORMAL_RESUME, NULL)) { +#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND + waiting_for_resume = 1; #endif - if (send_event(event, APM_NORMAL_RESUME, NULL) - && (suspends_pending <= 0)) - suspend(); + if (suspends_pending <= 0) + suspend(); + } break; case APM_NORMAL_RESUME: @@ -947,21 +961,18 @@ static void check_events(void) static void apm_event_handler(void) { static int pending_count = 0; + int err; - if (((standbys_pending > 0) || (suspends_pending > 0)) - && (apm_bios_info.version > 0x100)) { - if (pending_count-- <= 0) { - int err; - + if ((standbys_pending > 0) || (suspends_pending > 0)) { + if ((apm_bios_info.version > 0x100) && (pending_count-- <= 0)) { pending_count = 4; err = apm_set_power_state(APM_STATE_BUSY); if (err) apm_error("busy", err); } - } else { + } else pending_count = 0; - check_events(); - } + check_events(); } /* @@ -970,12 +981,8 @@ static void apm_event_handler(void) * Check whether we're the only running process to * decide if we should just power down. * - * Do this by checking the runqueue: if we're the - * only one, then the current process run_list will - * have both prev and next pointing to the same - * entry (the true idle process) */ -#define system_idle() (current->run_list.next == current->run_list.prev) +#define system_idle() (nr_running == 1) static void apm_mainloop(void) { @@ -1367,10 +1374,13 @@ static int apm(void *unused) /* Install our power off handler.. */ if (power_off_enabled) acpi_power_off = apm_power_off; - #ifdef CONFIG_MAGIC_SYSRQ sysrq_power_off = apm_power_off; #endif +#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT) + console_blank_hook = apm_console_blank; +#endif + apm_mainloop(); return 0; } @@ -1481,6 +1491,13 @@ static int __init apm_init(void) APM_INIT_ERROR_RETURN; } +#ifdef CONFIG_ACPI + if (acpi_active) { + printk(KERN_NOTICE "apm: overridden by ACPI.\n"); + APM_INIT_ERROR_RETURN; + } +#endif + /* * Set up a segment that references the real mode segment 0x40 * that extends up to the end of page zero (that we have reserved). @@ -1492,6 +1509,9 @@ static int __init apm_init(void) _set_limit((char *)&gdt[APM_40 >> 3], 4095 - (0x40 << 4)); apm_bios_entry.offset = apm_bios_info.offset; +#ifdef CONFIG_APM_BAD_ENTRY_OFFSET + apm_bios_entry.offset &= 0xffff; +#endif apm_bios_entry.segment = APM_CS; set_base(gdt[APM_CS >> 3], __va((unsigned long)apm_bios_info.cseg << 4)); diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S index 4eef2d8c9..ecfe0697d 100644 --- a/arch/i386/kernel/entry.S +++ b/arch/i386/kernel/entry.S @@ -92,8 +92,8 @@ ENOSYS = 38 pushl %ecx; \ pushl %ebx; \ movl $(__KERNEL_DS),%edx; \ - movl %dx,%ds; \ - movl %dx,%es; + movl %edx,%ds; \ + movl %edx,%es; #define RESTORE_ALL \ popl %ebx; \ @@ -288,15 +288,15 @@ error_code: pushl %ecx pushl %ebx cld - movl %es,%cx + movl %es,%ecx xchgl %eax, ORIG_EAX(%esp) # orig_eax (get the error code. ) movl %esp,%edx xchgl %ecx, ES(%esp) # get the address and save es. pushl %eax # push the error code pushl %edx movl $(__KERNEL_DS),%edx - movl %dx,%ds - movl %dx,%es + movl %edx,%ds + movl %edx,%es GET_CURRENT(%ebx) call *%ecx addl $8,%esp @@ -595,7 +595,10 @@ ENTRY(sys_call_table) .long SYMBOL_NAME(sys_mmap2) .long SYMBOL_NAME(sys_truncate64) .long SYMBOL_NAME(sys_ftruncate64) - /* 195 */ + .long SYMBOL_NAME(sys_stat64) /* 195 */ + .long SYMBOL_NAME(sys_lstat64) + .long SYMBOL_NAME(sys_fstat64) + /* * NOTE!! This doesn't have to be exact - we just have @@ -603,6 +606,6 @@ ENTRY(sys_call_table) * entries. Don't panic if you notice that this hasn't * been shrunk every time we add a new system call. */ - .rept NR_syscalls-194 + .rept NR_syscalls-197 .long SYMBOL_NAME(sys_ni_syscall) .endr diff --git a/arch/i386/kernel/head.S b/arch/i386/kernel/head.S index b3f1335d0..0c8250ad3 100644 --- a/arch/i386/kernel/head.S +++ b/arch/i386/kernel/head.S @@ -47,10 +47,10 @@ startup_32: */ cld movl $(__KERNEL_DS),%eax - movl %ax,%ds - movl %ax,%es - movl %ax,%fs - movl %ax,%gs + movl %eax,%ds + movl %eax,%es + movl %eax,%fs + movl %eax,%gs #ifdef __SMP__ orw %bx,%bx jz 1f @@ -133,9 +133,9 @@ startup_32: movl $ SYMBOL_NAME(empty_zero_page)+2048,%edi movzwl CL_OFFSET,%esi addl $(CL_BASE_ADDR),%esi - movl $2048,%ecx + movl $512,%ecx rep - movsb + movsl 1: #ifdef __SMP__ checkCPUtype: @@ -231,13 +231,13 @@ is386: pushl %ecx # restore original EFLAGS lidt idt_descr ljmp $(__KERNEL_CS),$1f 1: movl $(__KERNEL_DS),%eax # reload all the segment registers - movl %ax,%ds # after changing gdt. - movl %ax,%es - movl %ax,%fs - movl %ax,%gs + movl %eax,%ds # after changing gdt. + movl %eax,%es + movl %eax,%fs + movl %eax,%gs #ifdef __SMP__ movl $(__KERNEL_DS), %eax - mov %ax,%ss # Reload the stack pointer (segment only) + movl %eax,%ss # Reload the stack pointer (segment only) #else lss stack_start,%esp # Load processor stack #endif @@ -323,8 +323,8 @@ ignore_int: pushl %es pushl %ds movl $(__KERNEL_DS),%eax - movl %ax,%ds - movl %ax,%es + movl %eax,%ds + movl %eax,%es pushl $int_msg call SYMBOL_NAME(printk) popl %eax diff --git a/arch/i386/kernel/i386_ksyms.c b/arch/i386/kernel/i386_ksyms.c index 3c72c9cb5..9f75d94bf 100644 --- a/arch/i386/kernel/i386_ksyms.c +++ b/arch/i386/kernel/i386_ksyms.c @@ -56,6 +56,9 @@ EXPORT_SYMBOL_NOVERS(__down_failed); EXPORT_SYMBOL_NOVERS(__down_failed_interruptible); EXPORT_SYMBOL_NOVERS(__down_failed_trylock); EXPORT_SYMBOL_NOVERS(__up_wakeup); +EXPORT_SYMBOL_NOVERS(__down_write_failed); +EXPORT_SYMBOL_NOVERS(__down_read_failed); +EXPORT_SYMBOL_NOVERS(__rwsem_wake); /* Networking helper routines. */ EXPORT_SYMBOL(csum_partial_copy); EXPORT_SYMBOL(csum_partial_copy_generic); diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c index 041e44320..a111eb516 100644 --- a/arch/i386/kernel/irq.c +++ b/arch/i386/kernel/irq.c @@ -707,9 +707,11 @@ void free_irq(unsigned int irq, void *dev_id) } spin_unlock_irqrestore(&irq_controller_lock,flags); +#ifdef __SMP__ /* Wait to make sure it's not being used on another CPU */ while (irq_desc[irq].status & IRQ_INPROGRESS) barrier(); +#endif kfree(action); return; } @@ -731,6 +733,7 @@ unsigned long probe_irq_on(void) { unsigned int i; unsigned long delay; + unsigned long val; /* * first, enable any unassigned irqs @@ -754,6 +757,7 @@ unsigned long probe_irq_on(void) /* * Now filter out any obviously spurious interrupts */ + val = 0; spin_lock_irq(&irq_controller_lock); for (i=0; i<NR_IRQS; i++) { unsigned int status = irq_desc[i].status; @@ -766,18 +770,50 @@ unsigned long probe_irq_on(void) irq_desc[i].status = status & ~IRQ_AUTODETECT; irq_desc[i].handler->shutdown(i); } + + if (i < 32) + val |= 1 << i; } spin_unlock_irq(&irq_controller_lock); - return 0x12345678; + return val; } -int probe_irq_off(unsigned long unused) +/* + * Return a mask of triggered interrupts (this + * can handle only legacy ISA interrupts). + */ +unsigned int probe_irq_mask(unsigned long val) { - int i, irq_found, nr_irqs; + int i; + unsigned int mask; + + mask = 0; + spin_lock_irq(&irq_controller_lock); + for (i = 0; i < 16; i++) { + unsigned int status = irq_desc[i].status; + + if (!(status & IRQ_AUTODETECT)) + continue; - if (unused != 0x12345678) - printk("Bad IRQ probe from %lx\n", (&unused)[-1]); + if (!(status & IRQ_WAITING)) + mask |= 1 << i; + + irq_desc[i].status = status & ~IRQ_AUTODETECT; + irq_desc[i].handler->shutdown(i); + } + spin_unlock_irq(&irq_controller_lock); + + return mask & val; +} + +/* + * Return the one interrupt that triggered (this can + * handle any interrupt source) + */ +int probe_irq_off(unsigned long val) +{ + int i, irq_found, nr_irqs; nr_irqs = 0; irq_found = 0; diff --git a/arch/i386/kernel/mtrr.c b/arch/i386/kernel/mtrr.c index 1c1f6b74b..0061bc14d 100644 --- a/arch/i386/kernel/mtrr.c +++ b/arch/i386/kernel/mtrr.c @@ -139,41 +139,41 @@ Changed locking to spin with reschedule. Made use of new <smp_call_function>. v1.28 - 19990201 Zoltan Boszormenyi <zboszor@mol.hu> + 19990201 Zoltán Böszörményi <zboszor@mail.externet.hu> Extended the driver to be able to use Cyrix style ARRs. 19990204 Richard Gooch <rgooch@atnf.csiro.au> Restructured Cyrix support. v1.29 - 19990204 Zoltan Boszormenyi <zboszor@mol.hu> + 19990204 Zoltán Böszörményi <zboszor@mail.externet.hu> Refined ARR support: enable MAPEN in set_mtrr_prepare() and disable MAPEN in set_mtrr_done(). 19990205 Richard Gooch <rgooch@atnf.csiro.au> Minor cleanups. v1.30 - 19990208 Zoltan Boszormenyi <zboszor@mol.hu> + 19990208 Zoltán Böszörményi <zboszor@mail.externet.hu> Protect plain 6x86s (and other processors without the Page Global Enable feature) against accessing CR4 in set_mtrr_prepare() and set_mtrr_done(). 19990210 Richard Gooch <rgooch@atnf.csiro.au> Turned <set_mtrr_up> and <get_mtrr> into function pointers. v1.31 - 19990212 Zoltan Boszormenyi <zboszor@mol.hu> + 19990212 Zoltán Böszörményi <zboszor@mail.externet.hu> Major rewrite of cyrix_arr_init(): do not touch ARRs, leave them as the BIOS have set them up. Enable usage of all 8 ARRs. Avoid multiplications by 3 everywhere and other code clean ups/speed ups. - 19990213 Zoltan Boszormenyi <zboszor@mol.hu> + 19990213 Zoltán Böszörményi <zboszor@mail.externet.hu> Set up other Cyrix processors identical to the boot cpu. Since Cyrix don't support Intel APIC, this is l'art pour l'art. Weigh ARRs by size: If size <= 32M is given, set up ARR# we were given. If size > 32M is given, set up ARR7 only if it is free, fail otherwise. - 19990214 Zoltan Boszormenyi <zboszor@mol.hu> + 19990214 Zoltán Böszörményi <zboszor@mail.externet.hu> Also check for size >= 256K if we are to set up ARR7, mtrr_add() returns the value it gets from set_mtrr() - 19990218 Zoltan Boszormenyi <zboszor@mol.hu> + 19990218 Zoltán Böszörményi <zboszor@mail.externet.hu> Remove Cyrix "coma bug" workaround from here. Moved to linux/arch/i386/kernel/setup.c and linux/include/asm-i386/bugs.h @@ -187,7 +187,7 @@ 19990305 Richard Gooch <rgooch@atnf.csiro.au> Temporarily disable AMD support now MTRR capability flag is set. v1.32 - 19990308 Zoltan Boszormenyi <zboszor@mol.hu> + 19990308 Zoltán Böszörményi <zboszor@mail.externet.hu> Adjust my changes (19990212-19990218) to Richard Gooch's latest changes. (19990228-19990305) v1.33 @@ -201,23 +201,23 @@ 19990512 Richard Gooch <rgooch@atnf.csiro.au> Minor cleanups. v1.35 - 19990707 Zoltan Boszormenyi <zboszor@mol.hu> + 19990707 Zoltán Böszörményi <zboszor@mail.externet.hu> Check whether ARR3 is protected in cyrix_get_free_region() and mtrr_del(). The code won't attempt to delete or change it from now on if the BIOS protected ARR3. It silently skips ARR3 in cyrix_get_free_region() or returns with an error code from mtrr_del(). - 19990711 Zoltan Boszormenyi <zboszor@mol.hu> + 19990711 Zoltán Böszörményi <zboszor@mail.externet.hu> Reset some bits in the CCRs in cyrix_arr_init() to disable SMM if ARR3 isn't protected. This is needed because if SMM is active and ARR3 isn't protected then deleting and setting ARR3 again may lock up the processor. With SMM entirely disabled, it does not happen. - 19990812 Zoltan Boszormenyi <zboszor@mol.hu> + 19990812 Zoltán Böszörményi <zboszor@mail.externet.hu> Rearrange switch() statements so the driver accomodates to the fact that the AMD Athlon handles its MTRRs the same way as Intel does. - 19990814 Zoltan Boszormenyi <zboszor@mol.hu> + 19990814 Zoltán Böszörményi <zboszor@mail.externet.hu> Double check for Intel in mtrr_add()'s big switch() because that revision check is only valid for Intel CPUs. 19990819 Alan Cox <alan@redhat.com> @@ -957,11 +957,11 @@ static void set_mtrr_smp (unsigned int reg, unsigned long base, wait_barrier_execute = TRUE; wait_barrier_cache_enable = TRUE; atomic_set (&undone_count, smp_num_cpus - 1); - /* Flush and disable the local CPU's cache and start the ball rolling on - other CPUs */ - set_mtrr_prepare (&ctxt); + /* Start the ball rolling on other CPUs */ if (smp_call_function (ipi_handler, &data, 1, 0) != 0) panic ("mtrr: timed out waiting for other CPUs\n"); + /* Flush and disable the local CPU's cache */ + set_mtrr_prepare (&ctxt); /* Wait for all other CPUs to flush and disable their caches */ while (atomic_read (&undone_count) > 0) barrier (); /* Set up for completion wait and then release other CPUs to change MTRRs*/ @@ -1481,8 +1481,6 @@ static struct file_operations mtrr_fops = mtrr_close, /* Release */ NULL, /* Fsync */ NULL, /* Fasync */ - NULL, /* CheckMediaChange */ - NULL, /* Revalidate */ NULL, /* Lock */ }; diff --git a/arch/i386/kernel/pci-i386.c b/arch/i386/kernel/pci-i386.c index 746b21258..e94868cd9 100644 --- a/arch/i386/kernel/pci-i386.c +++ b/arch/i386/kernel/pci-i386.c @@ -12,7 +12,7 @@ * Hannover, Germany * hm@ix.de * - * Copyright 1997--1999 Martin Mares <mj@suse.cz> + * Copyright 1997--2000 Martin Mares <mj@suse.cz> * * For more information, please consult the following manuals (look at * http://www.pcisig.com/ for how to get them): @@ -102,7 +102,7 @@ * Expects start=0, end=size-1, flags=resource type. */ -static int __init pcibios_assign_resource(struct pci_dev *dev, int i) +int pci_assign_resource(struct pci_dev *dev, int i) { struct resource *r = &dev->resource[i]; struct resource *pr = pci_find_parent_resource(dev, r); @@ -180,14 +180,17 @@ static int __init pcibios_assign_resource(struct pci_dev *dev, int i) * as well. */ -static void __init pcibios_allocate_bus_resources(struct pci_bus *bus) +static void __init pcibios_allocate_bus_resources(struct list_head *bus_list) { + struct list_head *ln; + struct pci_bus *bus; struct pci_dev *dev; int idx; struct resource *r, *pr; /* Depth-First Search on bus tree */ - while (bus) { + for (ln=bus_list->next; ln != bus_list; ln=ln->next) { + bus = pci_bus_b(ln); if ((dev = bus->self)) { for (idx = PCI_BRIDGE_RESOURCES; idx < PCI_NUM_RESOURCES; idx++) { r = &dev->resource[idx]; @@ -198,9 +201,7 @@ static void __init pcibios_allocate_bus_resources(struct pci_bus *bus) printk(KERN_ERR "PCI: Cannot allocate resource region %d of bridge %s\n", idx, dev->slot_name); } } - if (bus->children) - pcibios_allocate_bus_resources(bus->children); - bus = bus->next; + pcibios_allocate_bus_resources(&bus->children); } } @@ -211,7 +212,7 @@ static void __init pcibios_allocate_resources(int pass) u16 command; struct resource *r, *pr; - for(dev=pci_devices; dev; dev=dev->next) { + pci_for_each_dev(dev) { pci_read_config_word(dev, PCI_COMMAND, &command); for(idx = 0; idx < 6; idx++) { r = &dev->resource[idx]; @@ -255,39 +256,46 @@ static void __init pcibios_assign_resources(void) int idx; struct resource *r; - for(dev=pci_devices; dev; dev=dev->next) { + pci_for_each_dev(dev) { + int class = dev->class >> 8; + + /* Don't touch classless devices and host bridges */ + if (!class || class == PCI_CLASS_BRIDGE_HOST) + continue; + for(idx=0; idx<6; idx++) { r = &dev->resource[idx]; - if (((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && idx < 4) || - ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA && (r->flags & IORESOURCE_IO)) || - !dev->class || (dev->class >> 8) == PCI_CLASS_BRIDGE_HOST) - /* - * Don't touch IDE controllers and I/O ports of video cards! - * Also avoid classless devices and host bridges. - */ + + /* + * Don't touch IDE controllers and I/O ports of video cards! + */ + if ((class == PCI_CLASS_STORAGE_IDE && idx < 4) || + (class == PCI_CLASS_DISPLAY_VGA && (r->flags & IORESOURCE_IO))) continue; - if (!r->start && r->end) { - /* - * We shall assign a new address to this resource, either because - * the BIOS forgot to do so or because we have decided the old - * address was unusable for some reason. - */ - pcibios_assign_resource(dev, idx); - } + + /* + * We shall assign a new address to this resource, either because + * the BIOS forgot to do so or because we have decided the old + * address was unusable for some reason. + */ + if (!r->start && r->end) + pci_assign_resource(dev, idx); } + if (pci_probe & PCI_ASSIGN_ROMS) { r = &dev->resource[PCI_ROM_RESOURCE]; r->end -= r->start; r->start = 0; if (r->end) - pcibios_assign_resource(dev, PCI_ROM_RESOURCE); + pci_assign_resource(dev, PCI_ROM_RESOURCE); } } } void __init pcibios_resource_survey(void) { - pcibios_allocate_bus_resources(pci_root); + DBG("PCI: Allocating resources\n"); + pcibios_allocate_bus_resources(&pci_root_buses); pcibios_allocate_resources(0); pcibios_allocate_resources(1); pcibios_assign_resources(); diff --git a/arch/i386/kernel/pci-pc.c b/arch/i386/kernel/pci-pc.c index 9ee1d6355..6b7d65589 100644 --- a/arch/i386/kernel/pci-pc.c +++ b/arch/i386/kernel/pci-pc.c @@ -1,7 +1,7 @@ /* * Low-Level PCI Support for PC * - * (c) 1999 Martin Mares <mj@suse.cz> + * (c) 1999--2000 Martin Mares <mj@suse.cz> */ #include <linux/config.h> @@ -22,6 +22,8 @@ unsigned int pci_probe = PCI_PROBE_BIOS | PCI_PROBE_CONF1 | PCI_PROBE_CONF2; +static struct pci_bus *pci_root_bus; + /* * IRQ routing table provided by the BIOS */ @@ -624,26 +626,30 @@ static struct pci_ops * __init pci_find_bios(void) static void __init pcibios_sort(void) { - struct pci_dev *dev = pci_devices; - struct pci_dev **last = &pci_devices; - struct pci_dev *d, **dd, *e; - int idx; + LIST_HEAD(sorted_devices); + struct list_head *ln; + struct pci_dev *dev, *d; + int idx, found; unsigned char bus, devfn; DBG("PCI: Sorting device list...\n"); - while ((e = dev)) { - idx = 0; - while (pci_bios_find_device(e->vendor, e->device, idx, &bus, &devfn) == PCIBIOS_SUCCESSFUL) { + while (!list_empty(&pci_devices)) { + ln = pci_devices.next; + dev = pci_dev_g(ln); + idx = found = 0; + while (pci_bios_find_device(dev->vendor, dev->device, idx, &bus, &devfn) == PCIBIOS_SUCCESSFUL) { idx++; - for(dd=&dev; (d = *dd); dd = &d->next) { + for (ln=pci_devices.next; ln != &pci_devices; ln=ln->next) { + d = pci_dev_g(ln); if (d->bus->number == bus && d->devfn == devfn) { - *dd = d->next; - *last = d; - last = &d->next; + list_del(&d->global_list); + list_add_tail(&d->global_list, &sorted_devices); + if (d == dev) + found = 1; break; } } - if (!d) { + if (ln == &pci_devices) { printk("PCI: BIOS reporting unknown device %02x:%02x\n", bus, devfn); /* * We must not continue scanning as several buggy BIOSes @@ -652,16 +658,14 @@ static void __init pcibios_sort(void) break; } } - if (e == dev) { + if (!found) { printk("PCI: Device %02x:%02x not found by BIOS\n", dev->bus->number, dev->devfn); - d = dev; - dev = dev->next; - *last = d; - last = &d->next; + list_del(&dev->global_list); + list_add_tail(&dev->global_list, &sorted_devices); } } - *last = NULL; + list_splice(&sorted_devices, &pci_devices); } /* @@ -736,16 +740,19 @@ static struct irq_routing_table * __init pcibios_get_irq_routing_table(void) static void __init pcibios_fixup_ghosts(struct pci_bus *b) { - struct pci_dev *d, *e, **z; + struct list_head *ln, *mn; + struct pci_dev *d, *e; int mirror = PCI_DEVFN(16,0); int seen_host_bridge = 0; int i; DBG("PCI: Scanning for ghost devices on bus %d\n", b->number); - for(d=b->devices; d && d->devfn < mirror; d=d->sibling) { + for (ln=b->devices.next; ln != &b->devices; ln=ln->next) { + d = pci_dev_b(ln); if ((d->class >> 8) == PCI_CLASS_BRIDGE_HOST) seen_host_bridge++; - for(e=d->next; e; e=e->sibling) { + for (mn=ln->next; mn != &b->devices; mn=mn->next) { + e = pci_dev_b(mn); if (e->devfn != d->devfn + mirror || e->vendor != d->vendor || e->device != d->device || @@ -758,20 +765,23 @@ static void __init pcibios_fixup_ghosts(struct pci_bus *b) continue; break; } - if (!e) + if (mn == &b->devices) return; } if (!seen_host_bridge) return; printk("PCI: Ignoring ghost devices on bus %02x\n", b->number); - for(e=b->devices; e->sibling != d; e=e->sibling); - e->sibling = NULL; - for(z=&pci_devices; (d=*z);) - if (d->bus == b && d->devfn >= mirror) { - *z = d->next; - kfree_s(d, sizeof(*d)); + + ln = &b->devices; + while (ln->next != &b->devices) { + d = pci_dev_b(ln->next); + if (d->devfn >= mirror) { + list_del(&d->global_list); + list_del(&d->bus_list); + kfree(d); } else - z = &d->next; + ln = ln->next; + } } /* @@ -783,10 +793,10 @@ static void __init pcibios_fixup_ghosts(struct pci_bus *b) */ static void __init pcibios_fixup_peer_bridges(void) { - struct pci_bus *b = pci_root; + struct list_head *ln; + struct pci_bus *b = pci_root_bus; int n, cnt=-1; - struct pci_dev *d; - struct pci_ops *ops = pci_root->ops; + struct pci_ops *ops = pci_root_bus->ops; #ifdef CONFIG_PCI_DIRECT /* @@ -798,8 +808,10 @@ static void __init pcibios_fixup_peer_bridges(void) return; #endif - for(d=b->devices; d; d=d->sibling) - if ((d->class >> 8) == PCI_CLASS_BRIDGE_HOST) + DBG("PCI: Peer bridge fixup\n"); + + for(ln=b->devices.next; ln != &b->devices; ln=ln->next) + if ((pci_dev_b(ln)->class >> 8) == PCI_CLASS_BRIDGE_HOST) cnt++; n = b->subordinate + 1; while (n <= 0xff) { @@ -864,9 +876,9 @@ static void __init pci_fixup_i450nx(struct pci_dev *d) pci_read_config_byte(d, reg++, &subb); DBG("i450NX PXB %d: %02x/%02x/%02x\n", pxb, busno, suba, subb); if (busno) - pci_scan_bus(busno, pci_root->ops, NULL); /* Bus A */ + pci_scan_bus(busno, pci_root_bus->ops, NULL); /* Bus A */ if (suba < subb) - pci_scan_bus(suba+1, pci_root->ops, NULL); /* Bus B */ + pci_scan_bus(suba+1, pci_root_bus->ops, NULL); /* Bus B */ } } @@ -879,7 +891,7 @@ static void __init pci_fixup_rcc(struct pci_dev *d) u8 busno; pci_read_config_byte(d, 0x44, &busno); printk("PCI: RCC host bridge: secondary bus %02x\n", busno); - pci_scan_bus(busno, pci_root->ops, NULL); + pci_scan_bus(busno, pci_root_bus->ops, NULL); } static void __init pci_fixup_compaq(struct pci_dev *d) @@ -891,7 +903,7 @@ static void __init pci_fixup_compaq(struct pci_dev *d) u8 busno; pci_read_config_byte(d, 0xc8, &busno); printk("PCI: Compaq host bridge: secondary bus %02x\n", busno); - pci_scan_bus(busno, pci_root->ops, NULL); + pci_scan_bus(busno, pci_root_bus->ops, NULL); } static void __init pci_fixup_umc_ide(struct pci_dev *d) @@ -1000,7 +1012,7 @@ static void __init pcibios_irq_peer_trick(struct irq_routing_table *rt) * It might be a secondary bus, but in this case its parent is already * known (ascending bus order) and therefore pci_scan_bus returns immediately. */ - if (busmap[i] && pci_scan_bus(i, pci_root->ops, NULL)) + if (busmap[i] && pci_scan_bus(i, pci_root_bus->ops, NULL)) printk("PCI: Discovered primary peer bus %02x [IRQ]\n", i); } @@ -1077,6 +1089,7 @@ static char *pcibios_lookup_irq(struct pci_dev *dev, struct irq_routing_table *r } DBG(" -> [PIIX] sink\n"); return NULL; + case ID(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533): default: DBG(" -> unknown router %04x/%04x\n", rt->rtr_vendor, rt->rtr_device); if (newirq && mask == (1 << newirq)) { @@ -1095,6 +1108,7 @@ static void __init pcibios_fixup_irqs(void) struct pci_dev *dev; u8 pin; + DBG("PCI: IRQ fixup\n"); rtable = pirq_table = pcibios_find_irq_routing_table(); #ifdef CONFIG_PCI_BIOS if (!rtable && pci_bios_present) @@ -1104,7 +1118,7 @@ static void __init pcibios_fixup_irqs(void) if (rtable) pcibios_irq_peer_trick(rtable); - for(dev=pci_devices; dev; dev=dev->next) { + pci_for_each_dev(dev) { pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); #if defined(CONFIG_X86_IO_APIC) /* @@ -1197,7 +1211,7 @@ void __init pcibios_init(void) } printk("PCI: Probing PCI hardware\n"); - pci_scan_bus(0, ops, NULL); + pci_root_bus = pci_scan_bus(0, ops, NULL); pcibios_fixup_irqs(); if (pci_probe & PCI_PEER_FIXUP) diff --git a/arch/i386/kernel/pci-visws.c b/arch/i386/kernel/pci-visws.c index 2ba24c8b3..401a4ee13 100644 --- a/arch/i386/kernel/pci-visws.c +++ b/arch/i386/kernel/pci-visws.c @@ -1,7 +1,7 @@ /* * Low-Level PCI Support for SGI Visual Workstation * - * (c) 1999 Martin Mares <mj@suse.cz> + * (c) 1999--2000 Martin Mares <mj@suse.cz> */ #include <linux/config.h> @@ -85,7 +85,7 @@ static void __init pcibios_fixup_irqs(void) u8 pin; int irq; - for(dev=pci_devices; dev; dev=dev->next) { + pci_for_each_dev(dev) { pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); dev->irq = 0; if (!pin) diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index 4f9c94353..703482425 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c @@ -69,6 +69,16 @@ void (*acpi_idle)(void) = NULL; void (*acpi_power_off)(void) = NULL; /* + * We use this if we don't have any better + * idle routine.. + */ +static void default_idle(void) +{ + if (current_cpu_data.hlt_works_ok && !hlt_counter) + asm volatile("sti ; hlt" : : : "memory"); +} + +/* * The idle thread. There's no useful work to be * done, so just try to conserve power and have a * low exit latency (ie sit in a loop waiting for @@ -82,26 +92,16 @@ void cpu_idle(void) current->counter = -100; while (1) { - while (!current->need_resched) { - if (!current_cpu_data.hlt_works_ok) - continue; - if (hlt_counter) - continue; - asm volatile("sti ; hlt" : : : "memory"); - } + void (*idle)(void) = acpi_idle; + if (!idle) + idle = default_idle; + while (!current->need_resched) + idle(); schedule(); check_pgt_cache(); - if (acpi_idle) - acpi_idle(); } } -/* - * This routine reboots the machine by asking the keyboard - * controller to pulse the reset-line low. We try that for a while, - * and if it doesn't work, we do some other stupid things. - */ - static long no_idt[2] = {0, 0}; static int reboot_mode = 0; static int reboot_thru_bios = 0; @@ -187,7 +187,10 @@ static unsigned char real_mode_switch [] = 0x74, 0x02, /* jz f */ 0x0f, 0x08, /* invd */ 0x24, 0x10, /* f: andb $0x10,al */ - 0x66, 0x0f, 0x22, 0xc0, /* movl %eax,%cr0 */ + 0x66, 0x0f, 0x22, 0xc0 /* movl %eax,%cr0 */ +}; +static unsigned char jump_to_bios [] = +{ 0xea, 0x00, 0x00, 0xff, 0xff /* ljmp $0xffff,$0x0000 */ }; @@ -200,34 +203,13 @@ static inline void kb_wait(void) break; } -void machine_restart(char * __unused) +/* + * Switch to real mode and then execute the code + * specified by the code and length parameters. + * We assume that length will aways be less that 100! + */ +void machine_real_restart(unsigned char *code, int length) { -#if __SMP__ - /* - * Stop all CPUs and turn off local APICs and the IO-APIC, so - * other OSs see a clean IRQ state. - */ - smp_send_stop(); - disable_IO_APIC(); -#endif - - if(!reboot_thru_bios) { - /* rebooting needs to touch the page at absolute addr 0 */ - *((unsigned short *)__va(0x472)) = reboot_mode; - for (;;) { - int i; - for (i=0; i<100; i++) { - kb_wait(); - udelay(50); - outb(0xfe,0x64); /* pulse reset low */ - udelay(50); - } - /* That didn't work - force a triple fault.. */ - __asm__ __volatile__("lidt %0": :"m" (no_idt)); - __asm__ __volatile__("int3"); - } - } - cli(); /* Write zero to CMOS register number 0x0f, which the BIOS POST @@ -273,8 +255,9 @@ void machine_restart(char * __unused) off paging. Copy it near the end of the first page, out of the way of BIOS variables. */ - memcpy ((void *) (0x1000 - sizeof (real_mode_switch)), + memcpy ((void *) (0x1000 - sizeof (real_mode_switch) - 100), real_mode_switch, sizeof (real_mode_switch)); + memcpy ((void *) (0x1000 - 100), code, length); /* Set up the IDT for real mode. */ @@ -293,11 +276,11 @@ void machine_restart(char * __unused) the values are consistent for real mode operation already. */ __asm__ __volatile__ ("movl $0x0010,%%eax\n" - "\tmovl %%ax,%%ds\n" - "\tmovl %%ax,%%es\n" - "\tmovl %%ax,%%fs\n" - "\tmovl %%ax,%%gs\n" - "\tmovl %%ax,%%ss" : : : "eax"); + "\tmovl %%eax,%%ds\n" + "\tmovl %%eax,%%es\n" + "\tmovl %%eax,%%fs\n" + "\tmovl %%eax,%%gs\n" + "\tmovl %%eax,%%ss" : : : "eax"); /* Jump to the 16-bit code that we copied earlier. It disables paging and the cache, switches to real mode, and jumps to the BIOS reset @@ -305,7 +288,38 @@ void machine_restart(char * __unused) __asm__ __volatile__ ("ljmp $0x0008,%0" : - : "i" ((void *) (0x1000 - sizeof (real_mode_switch)))); + : "i" ((void *) (0x1000 - sizeof (real_mode_switch) - 100))); +} + +void machine_restart(char * __unused) +{ +#if __SMP__ + /* + * Stop all CPUs and turn off local APICs and the IO-APIC, so + * other OSs see a clean IRQ state. + */ + smp_send_stop(); + disable_IO_APIC(); +#endif + + if(!reboot_thru_bios) { + /* rebooting needs to touch the page at absolute addr 0 */ + *((unsigned short *)__va(0x472)) = reboot_mode; + for (;;) { + int i; + for (i=0; i<100; i++) { + kb_wait(); + udelay(50); + outb(0xfe,0x64); /* pulse reset low */ + udelay(50); + } + /* That didn't work - force a triple fault.. */ + __asm__ __volatile__("lidt %0": :"m" (no_idt)); + __asm__ __volatile__("int3"); + } + } + + machine_real_restart(jump_to_bios, sizeof(jump_to_bios)); } void machine_halt(void) diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index 646532f86..cd80009d4 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -7,8 +7,8 @@ * and Martin Mares, November 1997. * * Force Cyrix 6x86(MX) and M II processors to report MTRR capability - * and fix against Cyrix "coma bug" by - * Zoltan Boszormenyi <zboszor@mol.hu> February 1999. + * and Cyrix "coma bug" recognition by + * Zoltán Böszörményi <zboszor@mail.externet.hu> February 1999. * * Force Centaur C6 processors to report MTRR capability. * Bart Hartgers <bart@etpmod.phys.tue.nl>, May 1999. @@ -592,13 +592,16 @@ void __init setup_arch(char **cmdline_p) */ max_pfn = 0; for (i = 0; i < e820.nr_map; i++) { - unsigned long curr_pfn; + unsigned long start, end; /* RAM? */ if (e820.map[i].type != E820_RAM) continue; - curr_pfn = PFN_DOWN(e820.map[i].addr + e820.map[i].size); - if (curr_pfn > max_pfn) - max_pfn = curr_pfn; + start = PFN_UP(e820.map[i].addr); + end = PFN_DOWN(e820.map[i].addr + e820.map[i].size); + if (start >= end) + continue; + if (end > max_pfn) + max_pfn = end; } /* @@ -706,7 +709,7 @@ void __init setup_arch(char **cmdline_p) #endif #ifdef CONFIG_BLK_DEV_INITRD - if (LOADER_TYPE) { + if (LOADER_TYPE && INITRD_START) { if (INITRD_START + INITRD_SIZE < (max_low_pfn << PAGE_SHIFT)) { reserve_bootmem(INITRD_START, INITRD_SIZE); initrd_start = diff --git a/arch/i386/kernel/smp.c b/arch/i386/kernel/smp.c index 59af7034d..9acf81556 100644 --- a/arch/i386/kernel/smp.c +++ b/arch/i386/kernel/smp.c @@ -11,6 +11,7 @@ #include <linux/init.h> #include <linux/mm.h> +#include <linux/spinlock.h> #include <linux/kernel_stat.h> #include <linux/smp_lock.h> #include <linux/irq.h> @@ -638,12 +639,19 @@ int prof_counter[NR_CPUS] = { 1, }; */ static unsigned int __init get_8254_timer_count(void) { + extern rwlock_t xtime_lock; + unsigned long flags; + unsigned int count; + write_lock_irqsave(&xtime_lock, flags); + outb_p(0x00, 0x43); count = inb_p(0x40); count |= inb_p(0x40) << 8; + write_unlock_irqrestore(&xtime_lock, flags); + return count; } diff --git a/arch/i386/kernel/vm86.c b/arch/i386/kernel/vm86.c index 5719ecaf1..4974d5c32 100644 --- a/arch/i386/kernel/vm86.c +++ b/arch/i386/kernel/vm86.c @@ -260,7 +260,7 @@ static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk mark_screen_rdonly(tsk); unlock_kernel(); __asm__ __volatile__( - "xorl %%eax,%%eax; movl %%ax,%%fs; movl %%ax,%%gs\n\t" + "xorl %%eax,%%eax; movl %%eax,%%fs; movl %%eax,%%gs\n\t" "movl %0,%%esp\n\t" "jmp ret_from_sys_call" : /* no outputs */ diff --git a/arch/i386/math-emu/Makefile b/arch/i386/math-emu/Makefile index 588f7ada2..68b327a2a 100644 --- a/arch/i386/math-emu/Makefile +++ b/arch/i386/math-emu/Makefile @@ -10,7 +10,7 @@ PARANOID = -DPARANOID CFLAGS := $(CFLAGS) $(PARANOID) $(DEBUG) -fno-builtin $(MATH_EMULATION) .S.o: - $(CC) -D__ASSEMBLY__ $(PARANOID) -c $< + $(CC) -D__ASSEMBLY__ $(AFLAGS) $(PARANOID) -c $< # From 'C' language sources: C_OBJS =fpu_entry.o errors.o \ |