]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] ACPI patch 3/9
authorAndy Grover <agrover@groveronline.com>
Fri, 15 Mar 2002 07:28:27 +0000 (23:28 -0800)
committerLinus Torvalds <torvalds@home.transmeta.com>
Fri, 15 Mar 2002 07:28:27 +0000 (23:28 -0800)
This patch updates the acpi IA32 arch-specific files. Part of this is
taking what was acpitable.c and implementing it with better integration
with the rest of the ACPI code.

arch/i386/kernel/acpi.c [new file with mode: 0644]
arch/i386/kernel/acpi_wakeup.S [new file with mode: 0644]
include/asm-i386/acpi.h [new file with mode: 0644]

diff --git a/arch/i386/kernel/acpi.c b/arch/i386/kernel/acpi.c
new file mode 100644 (file)
index 0000000..1fc9d68
--- /dev/null
@@ -0,0 +1,437 @@
+/*
+ *  acpi.c - Architecture-Specific Low-Level ACPI Support
+ *
+ *  Copyright (C) 2001 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *  Copyright (C) 2001 Jun Nakajima <jun.nakajima@intel.com>
+ *  Copyright (C) 2001 Patrick Mochel <mochel@osdl.org>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/bootmem.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/acpi.h>
+#include <asm/mpspec.h>
+#include <asm/io.h>
+#include <asm/apic.h>
+#include <asm/apicdef.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#include <asm/io_apic.h>
+
+
+#define PREFIX                 "ACPI: "
+
+
+/* --------------------------------------------------------------------------
+                              Boot-time Configuration
+   -------------------------------------------------------------------------- */
+
+#ifdef CONFIG_ACPI_BOOT
+
+/*
+ * Use reserved fixmap pages for physical-to-virtual mappings of ACPI tables.
+ * Note that the same range is used for each table, so tables that need to
+ * persist should be memcpy'd.
+ */
+char *
+__acpi_map_table (
+       unsigned long   phys_addr,
+       unsigned long   size)
+{
+       unsigned long   base = 0;
+       unsigned long   mapped_phys = phys_addr;
+       unsigned long   offset = phys_addr & (PAGE_SIZE - 1);
+       unsigned long   mapped_size = PAGE_SIZE - offset;
+       unsigned long   avail_size = mapped_size + (PAGE_SIZE * FIX_ACPI_PAGES);
+       int             idx = FIX_ACPI_BEGIN;
+
+       if (!phys_addr || !size)
+               return NULL;
+
+       base = fix_to_virt(FIX_ACPI_BEGIN);
+
+       set_fixmap(idx, mapped_phys);
+
+       if (size > avail_size)
+               return NULL;
+
+       /* If the table doesn't map completely into the fist page... */
+       if (size > mapped_size) {
+               do {
+                       /* Make sure we don't go past our range */
+                       if (idx++ == FIX_ACPI_END)
+                               return NULL;
+                       mapped_phys = mapped_phys + PAGE_SIZE;
+                       set_fixmap(idx, mapped_phys);
+                       mapped_size = mapped_size + PAGE_SIZE;
+               } while (mapped_size < size);
+       }
+
+       return ((unsigned char *) base + offset);
+}
+
+
+#ifdef CONFIG_X86_LOCAL_APIC
+
+static int total_cpus __initdata = 0;
+
+/* From mpparse.c */
+extern void __init MP_processor_info(struct mpc_config_processor *);
+
+int __init
+acpi_parse_lapic (
+       acpi_table_entry_header *header)
+{
+       struct acpi_table_lapic *cpu = NULL;
+       struct mpc_config_processor proc_entry;
+
+       cpu = (struct acpi_table_lapic*) header;
+       if (!cpu)
+               return -EINVAL;
+
+       acpi_table_print_madt_entry(header);
+
+       if (!cpu->flags.enabled) {
+               printk(KERN_INFO "Processor #%d disabled\n", cpu->id);
+               return 0;
+       }
+
+       if (cpu->id >= MAX_APICS) {
+               printk(KERN_WARNING "Processor #%d invalid (max %d)\n",
+                       cpu->id, MAX_APICS);
+               return -ENODEV;
+       }
+
+       /*
+        * Fill in the info we want to save.  Not concerned about
+        * the processor ID.  Processor features aren't present in
+        * the table.
+        */
+       proc_entry.mpc_type = MP_PROCESSOR;
+       proc_entry.mpc_apicid = cpu->id;
+       proc_entry.mpc_cpuflag = CPU_ENABLED;
+       if (cpu->id == boot_cpu_physical_apicid) {
+               /* TBD: Circular reference trying to establish BSP */
+               proc_entry.mpc_cpuflag |= CPU_BOOTPROCESSOR;
+       }
+       proc_entry.mpc_cpufeature = (boot_cpu_data.x86 << 8)
+               | (boot_cpu_data.x86_model << 4) | boot_cpu_data.x86_mask;
+       proc_entry.mpc_featureflag = boot_cpu_data.x86_capability[0];
+       proc_entry.mpc_reserved[0] = 0;
+       proc_entry.mpc_reserved[1] = 0;
+       proc_entry.mpc_apicver = 0x10;  /* integrated APIC */
+
+       MP_processor_info(&proc_entry);
+
+       total_cpus++;
+
+       return 0;
+}
+
+
+int __init
+acpi_parse_lapic_addr_ovr (
+       acpi_table_entry_header *header)
+{
+       struct acpi_table_lapic_addr_ovr *lapic_addr_ovr = NULL;
+
+       lapic_addr_ovr = (struct acpi_table_lapic_addr_ovr*) header;
+       if (!lapic_addr_ovr)
+               return -EINVAL;
+
+       /* TBD: Support local APIC address override entries */
+
+       return 0;
+}
+
+
+int __init
+acpi_parse_lapic_nmi (
+       acpi_table_entry_header *header)
+{
+       struct acpi_table_lapic_nmi *lacpi_nmi = NULL;
+
+       lacpi_nmi = (struct acpi_table_lapic_nmi*) header;
+       if (!lacpi_nmi)
+               return -EINVAL;
+
+       acpi_table_print_madt_entry(header);
+
+       /* TBD: Support local APIC NMI entries */
+
+       return 0;
+}
+
+#endif /*CONFIG_X86_LOCAL_APIC*/
+
+
+#ifdef CONFIG_X86_IO_APIC
+
+int __init
+acpi_parse_ioapic (
+       acpi_table_entry_header *header)
+{
+       struct acpi_table_ioapic *ioapic = NULL;
+       
+       ioapic = (struct acpi_table_ioapic*) header;
+       if (!ioapic)
+               return -EINVAL;
+
+       acpi_table_print_madt_entry(header);
+
+       /* TBD: Support ioapic entries */
+
+       return 0;
+}
+
+#endif /*CONFIG_X86_IO_APIC*/
+
+
+int __init
+acpi_parse_madt (
+       unsigned long           phys_addr,
+       unsigned long           size)
+{
+       struct acpi_table_madt  *madt = NULL;
+
+       if (!phys_addr || !size)
+               return -EINVAL;
+
+       madt = (struct acpi_table_madt *) __acpi_map_table(phys_addr, size);
+       if (!madt) {
+               printk(KERN_WARNING PREFIX "Unable to map MADT\n");
+               return -ENODEV;
+       }
+
+#ifdef CONFIG_X86_LOCAL_APIC
+       if (madt->lapic_address)
+               mp_lapic_addr = madt->lapic_address;
+       else
+               mp_lapic_addr = APIC_DEFAULT_PHYS_BASE;
+#endif /*CONFIG_X86_LOCAL_APIC*/
+
+       printk(KERN_INFO PREFIX "Local APIC address 0x%08x\n",
+               madt->lapic_address);
+
+       return 0;
+}
+
+
+static unsigned long __init
+acpi_scan_rsdp (
+       unsigned long           start,
+       unsigned long           length)
+{
+       unsigned long           offset = 0;
+       unsigned long           sig_len = sizeof("RSD PTR ") - 1;
+
+       /*
+        * Scan all 16-byte boundaries of the physical memory region for the
+        * RSDP signature.
+        */
+       for (offset = 0; offset < length; offset += 16) {
+               if (0 != strncmp((char *) (start + offset), "RSD PTR ", sig_len))
+                       continue;
+               return (start + offset);
+       }
+
+       return 0;
+}
+
+
+int __init
+acpi_find_rsdp (
+       unsigned long           *rsdp_phys)
+{
+       if (!rsdp_phys)
+               return -EINVAL;
+
+       /*
+        * Scan memory looking for the RSDP signature. First search EBDA (low
+        * memory) paragraphs and then search upper memory (E0000-FFFFF).
+        */
+       (*rsdp_phys) = acpi_scan_rsdp (0, 0x400);
+       if (!(*rsdp_phys))
+               (*rsdp_phys) = acpi_scan_rsdp (0xE0000, 0xFFFFF);
+
+       if (!(*rsdp_phys))
+               return -ENODEV;
+
+       return 0;
+}
+
+
+#endif /*CONFIG_ACPI_BOOT*/
+
+
+/* --------------------------------------------------------------------------
+                            PCI Interrupt Routing Support
+   -------------------------------------------------------------------------- */
+
+#ifdef CONFIG_ACPI_PCI
+int __init
+acpi_get_interrupt_model (
+       int             *type)
+{
+       if (!type)
+               return -EINVAL;
+
+#ifdef CONFIG_X86_IO_APIC
+       if (io_apic_assign_pci_irqs)
+               *type = ACPI_PCI_ROUTING_IOAPIC;
+       else
+#endif
+               *type = ACPI_PCI_ROUTING_PIC;
+
+       return 0;
+}
+#endif
+
+
+/* --------------------------------------------------------------------------
+                              Low-Level Sleep Support
+   -------------------------------------------------------------------------- */
+
+#ifdef CONFIG_ACPI_SLEEP
+
+#define DEBUG
+
+#ifdef DEBUG
+#include <linux/serial.h>
+#endif
+
+/* address in low memory of the wakeup routine. */
+unsigned long acpi_wakeup_address = 0;
+
+/* new page directory that we will be using */
+static pmd_t *pmd;
+
+/* saved page directory */
+static pmd_t saved_pmd;
+
+/* page which we'll use for the new page directory */
+static pte_t *ptep;
+
+extern unsigned long FASTCALL(acpi_copy_wakeup_routine(unsigned long));
+
+/*
+ * acpi_create_identity_pmd
+ *
+ * Create a new, identity mapped pmd.
+ *
+ * Do this by creating new page directory, and marking all the pages as R/W
+ * Then set it as the new Page Middle Directory.
+ * And, of course, flush the TLB so it takes effect.
+ *
+ * We save the address of the old one, for later restoration.
+ */
+static void acpi_create_identity_pmd (void)
+{
+       pgd_t *pgd;
+       int i;
+
+       ptep = (pte_t*)__get_free_page(GFP_KERNEL);
+
+       /* fill page with low mapping */
+       for (i = 0; i < PTRS_PER_PTE; i++)
+               set_pte(ptep + i, mk_pte_phys(i << PAGE_SHIFT, PAGE_SHARED));
+
+       pgd = pgd_offset(current->active_mm, 0);
+       pmd = pmd_alloc(current->mm,pgd, 0);
+
+       /* save the old pmd */
+       saved_pmd = *pmd;
+
+       /* set the new one */
+       set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(ptep)));
+
+       /* flush the TLB */
+       local_flush_tlb();
+}
+
+/*
+ * acpi_restore_pmd
+ *
+ * Restore the old pmd saved by acpi_create_identity_pmd and
+ * free the page that said function alloc'd
+ */
+static void acpi_restore_pmd (void)
+{
+       set_pmd(pmd, saved_pmd);
+       local_flush_tlb();
+       free_page((unsigned long)ptep);
+}
+
+/**
+ * acpi_save_state_mem - save kernel state
+ *
+ * Create an identity mapped page table and copy the wakeup routine to
+ * low memory.
+ */
+int acpi_save_state_mem (void)
+{
+       acpi_create_identity_pmd();
+       acpi_copy_wakeup_routine(acpi_wakeup_address);
+
+       return 0;
+}
+
+/**
+ * acpi_save_state_disk - save kernel state to disk
+ *
+ */
+int acpi_save_state_disk (void)
+{
+       return 1;
+}
+
+/*
+ * acpi_restore_state
+ */
+void acpi_restore_state_mem (void)
+{
+       acpi_restore_pmd();
+}
+
+/**
+ * acpi_reserve_bootmem - do _very_ early ACPI initialisation
+ *
+ * We allocate a page in low memory for the wakeup
+ * routine for when we come back from a sleep state. The
+ * runtime allocator allows specification of <16M pages, but not
+ * <1M pages.
+ */
+void __init acpi_reserve_bootmem(void)
+{
+       acpi_wakeup_address = (unsigned long)alloc_bootmem_low(PAGE_SIZE);
+       printk(KERN_DEBUG "ACPI: have wakeup address 0x%8.8lx\n", acpi_wakeup_address);
+}
+
+#endif /*CONFIG_ACPI_SLEEP*/
+
diff --git a/arch/i386/kernel/acpi_wakeup.S b/arch/i386/kernel/acpi_wakeup.S
new file mode 100644 (file)
index 0000000..c079ec4
--- /dev/null
@@ -0,0 +1,137 @@
+
+.text
+#include <linux/linkage.h>
+#include <asm/segment.h>
+
+
+ALIGN
+wakeup_start:
+wakeup_code:
+       wakeup_code_start = .
+       .code16
+
+       cli
+       cld
+
+       # setup data segment
+       movw    %cs, %ax
+
+       addw    $(wakeup_data - wakeup_code) >> 4, %ax
+       movw    %ax, %ds
+
+       # set up page table
+       movl    (real_save_cr3 - wakeup_data), %eax
+       movl    %eax, %cr3
+
+       # make sure %cr4 is set correctly (features, etc)
+       movl    (real_save_cr4 - wakeup_data), %eax
+       movl    %eax, %cr4
+
+       # need a gdt
+       lgdt    real_save_gdt - wakeup_data
+
+       movl    %cr0, %eax
+       orl     $0x80000001, %eax
+       movl    %eax, %cr0
+
+       ljmpl   $__KERNEL_CS,$SYMBOL_NAME(wakeup_pmode_return)
+
+       .code32
+       ALIGN
+
+.org   0x100
+wakeup_data:
+               .word 0
+real_save_gdt: .word 0
+               .long 0
+real_save_cr3: .long 0
+real_save_cr4: .long 0
+
+.org   0x300
+wakeup_stack:
+wakeup_end:
+
+wakeup_pmode_return:
+       # restore data segment
+       movl    $__KERNEL_DS, %eax
+       movw    %ax, %ds
+       movw    %ax, %es
+
+       # and restore the stack
+       movw    %ax, %ss
+       movl    saved_esp, %esp
+
+       # restore other segment registers
+       xorl    %eax, %eax
+       movw    %ax, %fs
+       movw    %ax, %gs
+
+       # reload the gdt, as we need the full 32 bit address
+       lgdt    saved_gdt
+       lidt    saved_idt
+       lldt    saved_ldt
+
+       # restore the other general registers
+       movl    saved_ebx, %ebx
+       movl    saved_edi, %edi
+       movl    saved_esi, %esi
+       movl    saved_ebp, %ebp
+
+       # jump to place where we left off
+       movl    saved_eip,%eax
+       jmp     *%eax
+
+##
+# acpi_copy_wakeup_routine
+#
+# Copy the above routine to low memory.
+#
+# Parameters:
+# %eax:        place to copy wakeup routine to
+#
+# Returned address is location of code in low memory (past data and stack)
+#
+ENTRY(acpi_copy_wakeup_routine)
+
+       pushl   %esi
+       pushl   %edi
+
+       sgdt    saved_gdt
+       sidt    saved_idt
+       sldt    saved_ldt
+       str     saved_tss
+
+       movl    %eax, %edi
+       leal    wakeup_start, %esi
+       movl    $(wakeup_end - wakeup_start) >> 2, %ecx
+
+       rep ;  movsl
+
+       movl    %cr3, %edx
+       movl    %edx, real_save_cr3 - wakeup_start (%eax)
+       movl    %cr4, %edx
+       movl    %edx, real_save_cr4 - wakeup_start (%eax)
+       sgdt    real_save_gdt - wakeup_start (%eax)
+
+       # restore the regs we used
+       popl    %edi
+       popl    %esi
+       ret
+
+
+.data
+ALIGN
+# saved registers
+saved_gdt:     .long   0,0
+saved_idt:     .long   0,0
+saved_ldt:     .long   0
+saved_tss:     .long   0
+saved_cr0:     .long   0
+
+ENTRY(saved_ebp)       .long   0
+ENTRY(saved_esi)       .long   0
+ENTRY(saved_edi)       .long   0
+ENTRY(saved_ebx)       .long   0
+
+ENTRY(saved_eip)       .long   0
+ENTRY(saved_esp)       .long   0
diff --git a/include/asm-i386/acpi.h b/include/asm-i386/acpi.h
new file mode 100644 (file)
index 0000000..4b51cff
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ *  asm-i386/acpi.h
+ *
+ *  Copyright (C) 2001 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *  Copyright (C) 2001 Patrick Mochel <mochel@osdl.org>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#ifndef _ASM_ACPI_H
+#define _ASM_ACPI_H
+
+#ifdef __KERNEL__
+
+#ifdef CONFIG_ACPI_BOOT
+
+/* Fixmap pages to reserve for ACPI boot-time tables (see fixmap.h) */
+#define FIX_ACPI_PAGES         4
+
+char * __acpi_map_table (unsigned long phys_addr, unsigned long size);
+extern int acpi_find_rsdp (unsigned long *phys_addr);
+extern int acpi_parse_madt (unsigned long phys_addr, unsigned long size);
+
+#endif /*CONFIG_ACPI_BOOT*/
+
+#ifdef CONFIG_ACPI_PCI
+int acpi_get_interrupt_model (int *type);
+#endif /*CONFIG_ACPI_PCI*/
+
+
+#ifdef CONFIG_ACPI_SLEEP
+
+extern unsigned long saved_eip;
+extern unsigned long saved_esp;
+extern unsigned long saved_ebp;
+extern unsigned long saved_ebx;
+extern unsigned long saved_esi;
+extern unsigned long saved_edi;
+
+static inline void acpi_save_register_state(unsigned long return_point)
+{
+       saved_eip = return_point;
+       asm volatile ("movl %%esp,(%0)" : "=m" (saved_esp));
+       asm volatile ("movl %%ebp,(%0)" : "=m" (saved_ebp));
+       asm volatile ("movl %%ebx,(%0)" : "=m" (saved_ebx));
+       asm volatile ("movl %%edi,(%0)" : "=m" (saved_edi));
+       asm volatile ("movl %%esi,(%0)" : "=m" (saved_esi));
+}
+
+#define acpi_restore_register_state()  do {} while (0)
+
+/* routines for saving/restoring kernel state */
+extern int acpi_save_state_mem(void);
+extern int acpi_save_state_disk(void);
+extern void acpi_restore_state_mem(void);
+
+extern unsigned long acpi_wakeup_address;
+
+/* early initialization routine */
+extern void acpi_reserve_bootmem(void);
+
+#endif /*CONFIG_ACPI_SLEEP*/
+
+
+#endif /*__KERNEL__*/
+
+#endif /*_ASM_ACPI_H*/