This is just me giving a headsup on the current progress of getting armv7 ACPI prototype stuff into armv8.
Currently it can see the linux,acpi-start and linux,acpi-len values passed from bootwrapper but when I try and access the address given by virt_to_phys on that it locks up the model.
Thanks
Graeme
From: Graeme Gregory graeme.gregory@linaro.org
Start of initialisation of ACPI ported over from armv7 tree.
Currently it dies when we dereference sig[] after the phys_to_virt()
Signed-off-by: Graeme Gregory graeme.gregory@linaro.org --- arch/arm64/Kconfig | 2 + arch/arm64/include/asm/acpi.h | 153 +++ arch/arm64/include/asm/io.h | 1 + arch/arm64/include/asm/processor.h | 13 + arch/arm64/kernel/Makefile | 3 + arch/arm64/kernel/acpi/Makefile | 6 + arch/arm64/kernel/acpi/acpi_div_64_by_32.S | 105 ++ arch/arm64/kernel/acpi/boot.c | 1581 ++++++++++++++++++++++++++++ arch/arm64/kernel/acpi/cstate.c | 210 ++++ arch/arm64/kernel/acpi/sleep.c | 126 +++ arch/arm64/kernel/acpi/sleep.h | 17 + arch/arm64/kernel/acpi/wakeup_32.S | 100 ++ arch/arm64/kernel/acpi/wakeup_64.S | 124 +++ arch/arm64/kernel/setup.c | 8 + drivers/acpi/Kconfig | 26 +- drivers/acpi/Makefile | 4 +- drivers/acpi/osl.c | 77 ++ drivers/acpi/processor_core.c | 36 +- drivers/acpi/reboot.c | 11 +- drivers/acpi/scan.c | 4 + drivers/of/fdt.c | 51 + drivers/pnp/pnpacpi/rsparser.c | 2 + include/acpi/pdc_arm64.h | 39 + include/linux/acpi.h | 1 + include/linux/of_fdt.h | 4 + 25 files changed, 2688 insertions(+), 16 deletions(-) create mode 100644 arch/arm64/include/asm/acpi.h create mode 100644 arch/arm64/kernel/acpi/Makefile create mode 100644 arch/arm64/kernel/acpi/acpi_div_64_by_32.S create mode 100644 arch/arm64/kernel/acpi/boot.c create mode 100644 arch/arm64/kernel/acpi/cstate.c create mode 100644 arch/arm64/kernel/acpi/sleep.c create mode 100644 arch/arm64/kernel/acpi/sleep.h create mode 100644 arch/arm64/kernel/acpi/wakeup_32.S create mode 100644 arch/arm64/kernel/acpi/wakeup_64.S create mode 100644 include/acpi/pdc_arm64.h
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 43b0e9f..b795dda 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -238,6 +238,8 @@ source "net/Kconfig"
source "drivers/Kconfig"
+source "drivers/acpi/Kconfig" + source "fs/Kconfig"
source "arch/arm64/Kconfig.debug" diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h new file mode 100644 index 0000000..a5ce403 --- /dev/null +++ b/arch/arm64/include/asm/acpi.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2013, Al Stone ahs3@redhat.com + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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_ARM_ACPI_H +#define _ASM_ARM_ACPI_H + +#ifdef __KERNEL__ + +#include <acpi/pdc_arm64.h> + +#include <asm/cacheflush.h> + +#include <linux/init.h> + +#define COMPILER_DEPENDENT_INT64 long long +#define COMPILER_DEPENDENT_UINT64 unsigned long long + +/* + * Calling conventions: + * + * ACPI_SYSTEM_XFACE - Interfaces to host OS (handlers, threads) + * ACPI_EXTERNAL_XFACE - External ACPI interfaces + * ACPI_INTERNAL_XFACE - Internal ACPI interfaces + * ACPI_INTERNAL_VAR_XFACE - Internal variable-parameter list interfaces + */ +#define ACPI_SYSTEM_XFACE +#define ACPI_EXTERNAL_XFACE +#define ACPI_INTERNAL_XFACE +#define ACPI_INTERNAL_VAR_XFACE + +/* Asm macros */ +#define ACPI_ASM_MACROS +#define BREAKPOINT3 +#define ACPI_DISABLE_IRQS() local_irq_disable() +#define ACPI_ENABLE_IRQS() local_irq_enable() +#define ACPI_FLUSH_CPU_CACHE() flush_cache_all() + +#define ACPI_DIV_64_BY_32(n_hi, n_lo, d32, q32, r32) \ + asm ("mov r0, %2\n" \ + "mov r1, %3\n" \ + "mov r2, %4\n" \ + "bl __arm_acpi_div_64_by_32\n" \ + "mov %0, r0\n" \ + "mov %1, r1\n" \ + : "=r"(q32), "=r"(r32) /* output operands */ \ + : "r"(n_hi), "r"(n_lo), "r"(d32) /* input operands */ \ + : "r0", "r1", "r2" /* clobbered registers */ \ + ) + +#define ACPI_SHIFT_RIGHT_64(n_hi, n_lo) \ + asm ("mov r0, %2\n" \ + "mov r1, %3\n" \ + "and r2, r0, #1\n" \ + "lsr r0, r0, #1\n" \ + "lsr r1, r1, #1\n" \ + "orr r1, r1, r2, lsl #31\n" \ + "mov %0, r0\n" \ + "mov %1, r1\n" \ + : "=r"(n_hi), "=r"(n_lo) /* output operands */ \ + : "0"(n_hi), "1"(n_lo) /* input operands */ \ + : "r0", "r1", "r2" /* clobbered registers */ \ + ) + +/* Blob handling macros */ +#define ACPI_BLOB_HEADER_SIZE 8 + +int __acpi_acquire_global_lock(unsigned int *lock); +int __acpi_release_global_lock(unsigned int *lock); + +#define ACPI_ACQUIRE_GLOBAL_LOCK(facs, Acq) \ + ((Acq) = __acpi_acquire_global_lock(&facs->global_lock)) + +#define ACPI_RELEASE_GLOBAL_LOCK(facs, Acq) \ + ((Acq) = __acpi_release_global_lock(&facs->global_lock)) + +/* Basic configuration for ACPI */ +/* BOZO: hardware reduced acpi only? */ +#ifdef CONFIG_ACPI +extern int acpi_disabled; +extern int acpi_noirq; +extern int acpi_pci_disabled; +extern int acpi_strict; + +struct acpi_arm_root { + unsigned long phys_address; + unsigned long size; +}; +extern struct acpi_arm_root acpi_arm_rsdp_info; + +/* Low-level suspend routine. */ +extern int acpi_suspend_lowlevel(void); + +/* Physical address to resume after wakeup */ +/* BOZO: was... +#define acpi_wakeup_address ((unsigned long)(real_mode_header->wakeup_start)) +*/ +#define acpi_wakeup_address (0) + + +static inline void disable_acpi(void) +{ + acpi_disabled = 1; + acpi_pci_disabled = 1; + acpi_noirq = 1; +} + +static inline bool arch_has_acpi_pdc(void) +{ + /* BOZO: replace x86 specific-ness here */ + return 0; /* always false for now */ +} + +static inline void arch_acpi_set_pdc_bits(u32 *buf) +{ + /* BOZO: replace x86 specific-ness here */ +} + +static inline void acpi_noirq_set(void) { acpi_noirq = 1; } +static inline void acpi_disable_pci(void) +{ + acpi_pci_disabled = 1; + acpi_noirq_set(); +} + +#else /* !CONFIG_ACPI */ +#define acpi_disabled 1 /* ACPI sometimes enabled on ARM */ +#define acpi_noirq 1 /* ACPI sometimes enabled on ARM */ +#define acpi_pci_disabled 1 /* ACPI PCI sometimes enabled on ARM */ +#define acpi_strict 1 /* no ACPI spec workarounds on ARM */ +#endif + +#endif /*__KERNEL__*/ + +#endif /*_ASM_ARM_ACPI_H*/ diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h index 2e12258..849af4b 100644 --- a/arch/arm64/include/asm/io.h +++ b/arch/arm64/include/asm/io.h @@ -232,6 +232,7 @@ extern void __iounmap(volatile void __iomem *addr); #define ioremap(addr, size) __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE)) #define ioremap_nocache(addr, size) __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE)) #define ioremap_wc(addr, size) __ioremap((addr), (size), __pgprot(PROT_NORMAL_NC)) +#define ioremap_cache(addr, size) __ioremap((addr), (size), __pgprot(PROT_DEFAULT)) #define iounmap __iounmap
#define PROT_SECT_DEFAULT (PMD_TYPE_SECT | PMD_SECT_AF) diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h index ab239b2..0cbc1f6 100644 --- a/arch/arm64/include/asm/processor.h +++ b/arch/arm64/include/asm/processor.h @@ -155,6 +155,19 @@ static inline void spin_lock_prefetch(const void *x) prefetchw(x); }
+/* + * Not all ARM devices have ACPI, but some do + * BOZO: is this correct? + */ +#ifdef CONFIG_ACPI +enum idle_boot_override { IDLE_NO_OVERRIDE=0, IDLE_HALT, IDLE_NOMWAIT, + IDLE_POLL, IDLE_FORCE_MWAIT }; + +extern unsigned long boot_option_idle_override; +#endif + +/* end BOZO */ + #define HAVE_ARCH_PICK_MMAP_LAYOUT
#endif diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 7b4b564..615c672 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -27,3 +27,6 @@ extra-y := $(head-y) vmlinux.lds # vDSO - this must be built first to generate the symbol offsets $(call objectify,$(arm64-obj-y)): $(obj)/vdso/vdso-offsets.h $(obj)/vdso/vdso-offsets.h: $(obj)/vdso + +obj-$(CONFIG_ACPI) += acpi/ + diff --git a/arch/arm64/kernel/acpi/Makefile b/arch/arm64/kernel/acpi/Makefile new file mode 100644 index 0000000..e28c063 --- /dev/null +++ b/arch/arm64/kernel/acpi/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_ACPI) += boot.o + +# BOZO: need to re-enable this properly +#obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup_$(BITS).o +obj-$(CONFIG_ACPI_SLEEP) += sleep.o + diff --git a/arch/arm64/kernel/acpi/acpi_div_64_by_32.S b/arch/arm64/kernel/acpi/acpi_div_64_by_32.S new file mode 100644 index 0000000..99a14a4 --- /dev/null +++ b/arch/arm64/kernel/acpi/acpi_div_64_by_32.S @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2013, Al Stone ahs3@redhat.com + * + * __acpi_arm_div64_by_32: perform integer division of a 64-bit value + * a 32-bit value + * + * The algorithm is borrowed from the GMP library, but has been redone + * here in order to put this implementation under a GPLv2 license. + * + * 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 + */ + +#ifdef __ARM_ARCH_8__ + +#include <linux/linkage.h> + +/* + * This needs to be called in the following manner: + * n_lo => r0 # these are the low 32 bits of the dividend + * n_hi => r1 # the high 32 bits of the dividend + * d32 => r2 # the 32-bit divisor + * + * The result is: + * q32 <= r0 # the 32-bit quotient + * r32 <= r1 # the 32-bit remainder + * + * This should be consistent with the normal ARMv7 calling conventions. + * + */ + +ENTRY(__arm_acpi_div_64_by_32) + mov r12, #32 // loop counter + cmp r2, #0x80000000 // check divisor MSB and clear carry + bcs bigdiv + +loop: adcs r1, r1, r1 // handle each bit + adc r0, r0, r0 + cmp r0, r2 + subcs r0, r0, r2 + sub r12, r12, #1 + teq r12, #0 + bne loop + + mov r3, r0 // stash the remainder for a tic + adc r0, r1, r1 // quotient: add in last carry + mov r1, r3 // remainder (now in right register) + mov pc, lr + +bigdiv: stmfd sp!, { r8, lr } // clear some scratch space + + and r8, r1, #1 // save LSB of dividend + mov lr, r0, lsl #31 + orrs r1, lr, r1, lsr #1 // r1 = lower part >> 1 bit + mov r0, r0, lsr #1 // r0 = higher part >> 1 bit + + and lr, r2, #1 // save LSB of divisor + movs r2, r2, lsr #1 // r2 = floor(divisor / 2) + adc r2, r2, #0 // r2 = ceil(divisor / 2) + +loop2: adcs r1, r1, r1 // handle each bit + adc r0, r0, r0 + cmp r0, r2 + subcs r0, r0, r2 + sub r12, r12, #1 + teq r12, #0 + bne loop2 + + adc r1, r1, r1 // shift and add last carry + add r0, r8, r0, lsl #1 // shift in remaining dividend LSB + tst lr, lr + beq evendiv + + rsb r2, lr, r2, lsl #1 // restore divisor value + adds r0, r0, r1 // adjust for omitted divisor LSB + addcs r1, r1, #1 // adjust quotient if a carry results + subcs r0, r0, r2 // adjust remainder, if carry + cmp r0, r2 + subcs r0, r0, #1 // adjust remainder + addcs r1, r1, #1 // adjust quotient + +evendiv: + mov r3, r0 // stash the remainder for a tic + mov r0, r1 // quotient + mov r1, r3 // remainder + + ldmfd sp!, { r8, pc } // restore the registers used + +ENDPROC(__arm_acpi_div_64_by_32) + +#else /* ! __ARM_ARCH_7A__ */ +#error __arm_acpi_div_64_by_32 not defined for this architecture +#endif + diff --git a/arch/arm64/kernel/acpi/boot.c b/arch/arm64/kernel/acpi/boot.c new file mode 100644 index 0000000..dc79b37 --- /dev/null +++ b/arch/arm64/kernel/acpi/boot.c @@ -0,0 +1,1581 @@ +/* + * boot.c - Architecture-Specific Low-Level ACPI Boot Support + * + * Copyright (C) 2001, 2002 Paul Diefenbaugh paul.s.diefenbaugh@intel.com + * Copyright (C) 2001 Jun Nakajima jun.nakajima@intel.com + * Copyright (C) 2013, Al Stone ahs3@redhat.com (ARM version) + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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 + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +/* + * BOZO: this needs to be done right.... + */ +#include <linux/init.h> +#include <linux/acpi.h> +#include <linux/acpi_pmtmr.h> +#include <linux/efi.h> +#include <linux/cpumask.h> +#include <linux/memblock.h> +#include <linux/module.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/slab.h> +#include <linux/bootmem.h> +#include <linux/ioport.h> +#include <linux/pci.h> + +#include <asm/pgtable.h> +#include <asm/io.h> +#include <asm/smp.h> +#include <asm/acpi.h> + +static int __initdata acpi_force = 0; +u32 acpi_rsdt_forced; +int acpi_disabled = 0; +EXPORT_SYMBOL(acpi_disabled); + +#define BAD_MADT_ENTRY(entry, end) ( \ + (!entry) || (unsigned long)entry + sizeof(*entry) > end || \ + ((struct acpi_subtable_header *)entry)->length < sizeof(*entry)) + +#define PREFIX "ACPI: " + +int acpi_noirq; /* skip ACPI IRQ initialization */ +int acpi_pci_disabled; /* skip ACPI PCI scan and IRQ initialization */ +EXPORT_SYMBOL(acpi_pci_disabled); + +int acpi_lapic; +int acpi_ioapic; +int acpi_strict; + +u8 acpi_sci_flags __initdata; +int acpi_sci_override_gsi __initdata; +int acpi_skip_timer_override __initdata; +int acpi_use_timer_override __initdata; +int acpi_fix_pin2_polarity __initdata; + +struct acpi_arm_root acpi_arm_rsdp_info; /* info about RSDP from FDT */ + +#ifdef CONFIG_X86_LOCAL_APIC +static u64 acpi_lapic_addr __initdata = APIC_DEFAULT_PHYS_BASE; +#endif + +/* -------------------------------------------------------------------------- + Boot-time Configuration + -------------------------------------------------------------------------- */ + +/* + * The default interrupt routing model is PIC (8259). This gets + * overridden if IOAPICs are enumerated (below). + * + * Since we're on ARM, it clearly has to be GIC. + */ +enum acpi_irq_model_id acpi_irq_model = ACPI_IRQ_MODEL_GIC; + +static unsigned int gsi_to_irq(unsigned int gsi) +{ + int irq = irq_create_mapping(NULL, gsi); + + return irq; +} + +/* + * BOZO: is it reasonable to just reserve the memory space? Or are there + * other restrictions needed? Or does it need copying to some other place? + */ +char *__init __acpi_map_table(unsigned long phys, unsigned long size) +{ + if (!phys || !size) + return NULL; + + /* we're already in memory so we cannot io_remap the entry */ + return phys_to_virt(phys); +} + +void __init __acpi_unmap_table(char *map, unsigned long size) +{ + if (!map || !size) + return; + + /* we're already in memory so we cannot io_remap the entry; + * since we're not io_remap'ing, unmap'ing is especially + * pointless + */ + return; +} + +#ifdef CONFIG_X86_LOCAL_APIC +static int __init acpi_parse_madt(struct acpi_table_header *table) +{ + struct acpi_table_madt *madt = NULL; + + if (!cpu_has_apic) + return -EINVAL; + + madt = (struct acpi_table_madt *)table; + if (!madt) { + printk(KERN_WARNING PREFIX "Unable to map MADT\n"); + return -ENODEV; + } + + if (madt->address) { + acpi_lapic_addr = (u64) madt->address; + + printk(KERN_DEBUG PREFIX "Local APIC address 0x%08x\n", + madt->address); + } + + default_acpi_madt_oem_check(madt->header.oem_id, + madt->header.oem_table_id); + + return 0; +} + +static void __cpuinit acpi_register_lapic(int id, u8 enabled) +{ + unsigned int ver = 0; + + if (id >= (MAX_LOCAL_APIC-1)) { + printk(KERN_INFO PREFIX "skipped apicid that is too big\n"); + return; + } + + if (!enabled) { + ++disabled_cpus; + return; + } + + if (boot_cpu_physical_apicid != -1U) + ver = apic_version[boot_cpu_physical_apicid]; + + generic_processor_info(id, ver); +} + +static int __init +acpi_parse_x2apic(struct acpi_subtable_header *header, const unsigned long end) +{ + struct acpi_madt_local_x2apic *processor = NULL; + int apic_id; + u8 enabled; + + processor = (struct acpi_madt_local_x2apic *)header; + + if (BAD_MADT_ENTRY(processor, end)) + return -EINVAL; + + acpi_table_print_madt_entry(header); + + apic_id = processor->local_apic_id; + enabled = processor->lapic_flags & ACPI_MADT_ENABLED; +#ifdef CONFIG_X86_X2APIC + /* + * We need to register disabled CPU as well to permit + * counting disabled CPUs. This allows us to size + * cpus_possible_map more accurately, to permit + * to not preallocating memory for all NR_CPUS + * when we use CPU hotplug. + */ + if (!apic->apic_id_valid(apic_id) && enabled) + printk(KERN_WARNING PREFIX "x2apic entry ignored\n"); + else + acpi_register_lapic(apic_id, enabled); +#else + printk(KERN_WARNING PREFIX "x2apic entry ignored\n"); +#endif + + return 0; +} + +static int __init +acpi_parse_lapic(struct acpi_subtable_header * header, const unsigned long end) +{ + struct acpi_madt_local_apic *processor = NULL; + + processor = (struct acpi_madt_local_apic *)header; + + if (BAD_MADT_ENTRY(processor, end)) + return -EINVAL; + + acpi_table_print_madt_entry(header); + + /* + * We need to register disabled CPU as well to permit + * counting disabled CPUs. This allows us to size + * cpus_possible_map more accurately, to permit + * to not preallocating memory for all NR_CPUS + * when we use CPU hotplug. + */ + acpi_register_lapic(processor->id, /* APIC ID */ + processor->lapic_flags & ACPI_MADT_ENABLED); + + return 0; +} + +static int __init +acpi_parse_sapic(struct acpi_subtable_header *header, const unsigned long end) +{ + struct acpi_madt_local_sapic *processor = NULL; + + processor = (struct acpi_madt_local_sapic *)header; + + if (BAD_MADT_ENTRY(processor, end)) + return -EINVAL; + + acpi_table_print_madt_entry(header); + + acpi_register_lapic((processor->id << 8) | processor->eid,/* APIC ID */ + processor->lapic_flags & ACPI_MADT_ENABLED); + + return 0; +} + +static int __init +acpi_parse_lapic_addr_ovr(struct acpi_subtable_header * header, + const unsigned long end) +{ + struct acpi_madt_local_apic_override *lapic_addr_ovr = NULL; + + lapic_addr_ovr = (struct acpi_madt_local_apic_override *)header; + + if (BAD_MADT_ENTRY(lapic_addr_ovr, end)) + return -EINVAL; + + acpi_lapic_addr = lapic_addr_ovr->address; + + return 0; +} + +static int __init +acpi_parse_x2apic_nmi(struct acpi_subtable_header *header, + const unsigned long end) +{ + struct acpi_madt_local_x2apic_nmi *x2apic_nmi = NULL; + + x2apic_nmi = (struct acpi_madt_local_x2apic_nmi *)header; + + if (BAD_MADT_ENTRY(x2apic_nmi, end)) + return -EINVAL; + + acpi_table_print_madt_entry(header); + + if (x2apic_nmi->lint != 1) + printk(KERN_WARNING PREFIX "NMI not connected to LINT 1!\n"); + + return 0; +} + +static int __init +acpi_parse_lapic_nmi(struct acpi_subtable_header * header, const unsigned long end) +{ + struct acpi_madt_local_apic_nmi *lapic_nmi = NULL; + + lapic_nmi = (struct acpi_madt_local_apic_nmi *)header; + + if (BAD_MADT_ENTRY(lapic_nmi, end)) + return -EINVAL; + + acpi_table_print_madt_entry(header); + + if (lapic_nmi->lint != 1) + printk(KERN_WARNING PREFIX "NMI not connected to LINT 1!\n"); + + return 0; +} + +#endif /*CONFIG_X86_LOCAL_APIC */ + +#ifdef CONFIG_X86_IO_APIC + +static int __init +acpi_parse_ioapic(struct acpi_subtable_header * header, const unsigned long end) +{ + struct acpi_madt_io_apic *ioapic = NULL; + + ioapic = (struct acpi_madt_io_apic *)header; + + if (BAD_MADT_ENTRY(ioapic, end)) + return -EINVAL; + + acpi_table_print_madt_entry(header); + + mp_register_ioapic(ioapic->id, + ioapic->address, ioapic->global_irq_base); + + return 0; +} + +/* + * Parse Interrupt Source Override for the ACPI SCI + */ +static void __init acpi_sci_ioapic_setup(u8 bus_irq, u16 polarity, u16 trigger, u32 gsi) +{ + if (trigger == 0) /* compatible SCI trigger is level */ + trigger = 3; + + if (polarity == 0) /* compatible SCI polarity is low */ + polarity = 3; + + /* Command-line over-ride via acpi_sci= */ + if (acpi_sci_flags & ACPI_MADT_TRIGGER_MASK) + trigger = (acpi_sci_flags & ACPI_MADT_TRIGGER_MASK) >> 2; + + if (acpi_sci_flags & ACPI_MADT_POLARITY_MASK) + polarity = acpi_sci_flags & ACPI_MADT_POLARITY_MASK; + + /* + * mp_config_acpi_legacy_irqs() already setup IRQs < 16 + * If GSI is < 16, this will update its flags, + * else it will create a new mp_irqs[] entry. + */ + mp_override_legacy_irq(bus_irq, polarity, trigger, gsi); + + /* + * stash over-ride to indicate we've been here + * and for later update of acpi_gbl_FADT + */ + acpi_sci_override_gsi = gsi; + return; +} + +static int __init +acpi_parse_int_src_ovr(struct acpi_subtable_header * header, + const unsigned long end) +{ + struct acpi_madt_interrupt_override *intsrc = NULL; + + intsrc = (struct acpi_madt_interrupt_override *)header; + + if (BAD_MADT_ENTRY(intsrc, end)) + return -EINVAL; + + acpi_table_print_madt_entry(header); + + if (intsrc->source_irq == acpi_gbl_FADT.sci_interrupt) { + acpi_sci_ioapic_setup(intsrc->source_irq, + intsrc->inti_flags & ACPI_MADT_POLARITY_MASK, + (intsrc->inti_flags & ACPI_MADT_TRIGGER_MASK) >> 2, + intsrc->global_irq); + return 0; + } + + if (intsrc->source_irq == 0) { + if (acpi_skip_timer_override) { + printk(PREFIX "BIOS IRQ0 override ignored.\n"); + return 0; + } + + if ((intsrc->global_irq == 2) && acpi_fix_pin2_polarity + && (intsrc->inti_flags & ACPI_MADT_POLARITY_MASK)) { + intsrc->inti_flags &= ~ACPI_MADT_POLARITY_MASK; + printk(PREFIX "BIOS IRQ0 pin2 override: forcing polarity to high active.\n"); + } + } + + mp_override_legacy_irq(intsrc->source_irq, + intsrc->inti_flags & ACPI_MADT_POLARITY_MASK, + (intsrc->inti_flags & ACPI_MADT_TRIGGER_MASK) >> 2, + intsrc->global_irq); + + return 0; +} + +static int __init +acpi_parse_nmi_src(struct acpi_subtable_header * header, const unsigned long end) +{ + struct acpi_madt_nmi_source *nmi_src = NULL; + + nmi_src = (struct acpi_madt_nmi_source *)header; + + if (BAD_MADT_ENTRY(nmi_src, end)) + return -EINVAL; + + acpi_table_print_madt_entry(header); + + /* TBD: Support nimsrc entries? */ + + return 0; +} + +#endif /* CONFIG_X86_IO_APIC */ + +/* + * acpi_pic_sci_set_trigger() + * + * use ELCR to set PIC-mode trigger type for SCI + * + * If a PIC-mode SCI is not recognized or gives spurious IRQ7's + * it may require Edge Trigger -- use "acpi_sci=edge" + * + * Port 0x4d0-4d1 are ECLR1 and ECLR2, the Edge/Level Control Registers + * for the 8259 PIC. bit[n] = 1 means irq[n] is Level, otherwise Edge. + * ECLR1 is IRQs 0-7 (IRQ 0, 1, 2 must be 0) + * ECLR2 is IRQs 8-15 (IRQ 8, 13 must be 0) + */ + +void __init acpi_pic_sci_set_trigger(unsigned int irq, u16 trigger) +{ + unsigned int mask = 1 << irq; + unsigned int old, new; + + /* Real old ELCR mask */ + old = inb(0x4d0) | (inb(0x4d1) << 8); + + /* + * If we use ACPI to set PCI IRQs, then we should clear ELCR + * since we will set it correctly as we enable the PCI irq + * routing. + */ + new = acpi_noirq ? old : 0; + + /* + * Update SCI information in the ELCR, it isn't in the PCI + * routing tables.. + */ + switch (trigger) { + case 1: /* Edge - clear */ + new &= ~mask; + break; + case 3: /* Level - set */ + new |= mask; + break; + } + + if (old == new) + return; + + printk(PREFIX "setting ELCR to %04x (from %04x)\n", new, old); + outb(new, 0x4d0); + outb(new >> 8, 0x4d1); +} + +int acpi_gsi_to_irq(u32 gsi, unsigned int *irq) +{ + *irq = gsi_to_irq(gsi); + + return 0; +} +EXPORT_SYMBOL_GPL(acpi_gsi_to_irq); + +static int acpi_register_gsi_pic(struct device *dev, u32 gsi, + int trigger, int polarity) +{ +#ifdef CONFIG_PCI + /* + * Make sure all (legacy) PCI IRQs are set as level-triggered. + */ + if (trigger == ACPI_LEVEL_SENSITIVE) + eisa_set_level_irq(gsi); +#endif + + return gsi; +} + +static int acpi_register_gsi_ioapic(struct device *dev, u32 gsi, + int trigger, int polarity) +{ +#ifdef CONFIG_X86_IO_APIC + gsi = mp_register_gsi(dev, gsi, trigger, polarity); +#endif + + return gsi; +} + +int (*__acpi_register_gsi)(struct device *dev, u32 gsi, + int trigger, int polarity) = acpi_register_gsi_pic; + +/* + * success: return IRQ number (>=0) + * failure: return < 0 + */ +int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, int polarity) +{ + unsigned int irq; + unsigned int plat_gsi = gsi; + + plat_gsi = (*__acpi_register_gsi)(dev, gsi, trigger, polarity); + + irq = gsi_to_irq(plat_gsi); + + return irq; +} +EXPORT_SYMBOL_GPL(acpi_register_gsi); + +void acpi_unregister_gsi(u32 gsi) +{ +} +EXPORT_SYMBOL_GPL(acpi_unregister_gsi); + +void __init acpi_set_irq_model_pic(void) +{ + acpi_irq_model = ACPI_IRQ_MODEL_PIC; + __acpi_register_gsi = acpi_register_gsi_pic; + acpi_ioapic = 0; +} + +void __init acpi_set_irq_model_ioapic(void) +{ + acpi_irq_model = ACPI_IRQ_MODEL_IOAPIC; + __acpi_register_gsi = acpi_register_gsi_ioapic; + acpi_ioapic = 1; +} + +/* + * ACPI based hotplug support for CPU + */ +#ifdef CONFIG_ACPI_HOTPLUG_CPU +#include <acpi/processor.h> + +static void __cpuinit acpi_map_cpu2node(acpi_handle handle, int cpu, int physid) +{ +#ifdef CONFIG_ACPI_NUMA + int nid; + + nid = acpi_get_node(handle); + if (nid == -1 || !node_online(nid)) + return; + set_apicid_to_node(physid, nid); + numa_set_node(cpu, nid); +#endif +} + +static int __cpuinit _acpi_map_lsapic(acpi_handle handle, int *pcpu) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + struct acpi_madt_local_apic *lapic; + cpumask_var_t tmp_map, new_map; + u8 physid; + int cpu; + int retval = -ENOMEM; + + if (ACPI_FAILURE(acpi_evaluate_object(handle, "_MAT", NULL, &buffer))) + return -EINVAL; + + if (!buffer.length || !buffer.pointer) + return -EINVAL; + + obj = buffer.pointer; + if (obj->type != ACPI_TYPE_BUFFER || + obj->buffer.length < sizeof(*lapic)) { + kfree(buffer.pointer); + return -EINVAL; + } + + lapic = (struct acpi_madt_local_apic *)obj->buffer.pointer; + + if (lapic->header.type != ACPI_MADT_TYPE_LOCAL_APIC || + !(lapic->lapic_flags & ACPI_MADT_ENABLED)) { + kfree(buffer.pointer); + return -EINVAL; + } + + physid = lapic->id; + + kfree(buffer.pointer); + buffer.length = ACPI_ALLOCATE_BUFFER; + buffer.pointer = NULL; + lapic = NULL; + + if (!alloc_cpumask_var(&tmp_map, GFP_KERNEL)) + goto out; + + if (!alloc_cpumask_var(&new_map, GFP_KERNEL)) + goto free_tmp_map; + + cpumask_copy(tmp_map, cpu_present_mask); +#ifdef CONFIG_X86 + /* BOZO: ?? */ + acpi_register_lapic(physid, ACPI_MADT_ENABLED); +#endif + + /* + * If acpi_register_lapic successfully generates a new logical cpu + * number, then the following will get us exactly what was mapped + */ + cpumask_andnot(new_map, cpu_present_mask, tmp_map); + if (cpumask_empty(new_map)) { + printk ("Unable to map lapic to logical cpu number\n"); + retval = -EINVAL; + goto free_new_map; + } + + acpi_processor_set_pdc(handle); + + cpu = cpumask_first(new_map); + acpi_map_cpu2node(handle, cpu, physid); + + *pcpu = cpu; + retval = 0; + +free_new_map: + free_cpumask_var(new_map); +free_tmp_map: + free_cpumask_var(tmp_map); +out: + return retval; +} + +/* wrapper to silence section mismatch warning */ +int __ref acpi_map_lsapic(acpi_handle handle, int *pcpu) +{ + return _acpi_map_lsapic(handle, pcpu); +} +EXPORT_SYMBOL(acpi_map_lsapic); + +int acpi_unmap_lsapic(int cpu) +{ +#ifdef CONFIG_X86 + /* BOZO: ??? */ + per_cpu(x86_cpu_to_apicid, cpu) = -1; + set_cpu_present(cpu, false); + num_processors--; +#endif + + return (0); +} + +EXPORT_SYMBOL(acpi_unmap_lsapic); +#endif /* CONFIG_ACPI_HOTPLUG_CPU */ + +int acpi_register_ioapic(acpi_handle handle, u64 phys_addr, u32 gsi_base) +{ + /* TBD */ + return -EINVAL; +} + +EXPORT_SYMBOL(acpi_register_ioapic); + +int acpi_unregister_ioapic(acpi_handle handle, u32 gsi_base) +{ + /* TBD */ + return -EINVAL; +} + +EXPORT_SYMBOL(acpi_unregister_ioapic); + +static int __init acpi_parse_sbf(struct acpi_table_header *table) +{ + struct acpi_table_boot *sb; + + sb = (struct acpi_table_boot *)table; + if (!sb) { + printk(KERN_WARNING PREFIX "Unable to map SBF\n"); + return -ENODEV; + } + +#ifdef CONFIG_X86 + /* BOZO: can this be ignored? do we need SBF? */ + sbf_port = sb->cmos_index; /* Save CMOS port */ +#endif + + return 0; +} + +#ifdef CONFIG_HPET_TIMER +#include <asm/hpet.h> + +static struct __initdata resource *hpet_res; + +static int __init acpi_parse_hpet(struct acpi_table_header *table) +{ + struct acpi_table_hpet *hpet_tbl; + + hpet_tbl = (struct acpi_table_hpet *)table; + if (!hpet_tbl) { + printk(KERN_WARNING PREFIX "Unable to map HPET\n"); + return -ENODEV; + } + + if (hpet_tbl->address.space_id != ACPI_SPACE_MEM) { + printk(KERN_WARNING PREFIX "HPET timers must be located in " + "memory.\n"); + return -1; + } + + hpet_address = hpet_tbl->address.address; + hpet_blockid = hpet_tbl->sequence; + + /* + * Some broken BIOSes advertise HPET at 0x0. We really do not + * want to allocate a resource there. + */ + if (!hpet_address) { + printk(KERN_WARNING PREFIX + "HPET id: %#x base: %#lx is invalid\n", + hpet_tbl->id, hpet_address); + return 0; + } +#ifdef CONFIG_X86_64 + /* + * Some even more broken BIOSes advertise HPET at + * 0xfed0000000000000 instead of 0xfed00000. Fix it up and add + * some noise: + */ + if (hpet_address == 0xfed0000000000000UL) { + if (!hpet_force_user) { + printk(KERN_WARNING PREFIX "HPET id: %#x " + "base: 0xfed0000000000000 is bogus\n " + "try hpet=force on the kernel command line to " + "fix it up to 0xfed00000.\n", hpet_tbl->id); + hpet_address = 0; + return 0; + } + printk(KERN_WARNING PREFIX + "HPET id: %#x base: 0xfed0000000000000 fixed up " + "to 0xfed00000.\n", hpet_tbl->id); + hpet_address >>= 32; + } +#endif + printk(KERN_INFO PREFIX "HPET id: %#x base: %#lx\n", + hpet_tbl->id, hpet_address); + + /* + * Allocate and initialize the HPET firmware resource for adding into + * the resource tree during the lateinit timeframe. + */ +#define HPET_RESOURCE_NAME_SIZE 9 + hpet_res = alloc_bootmem(sizeof(*hpet_res) + HPET_RESOURCE_NAME_SIZE); + + hpet_res->name = (void *)&hpet_res[1]; + hpet_res->flags = IORESOURCE_MEM; + snprintf((char *)hpet_res->name, HPET_RESOURCE_NAME_SIZE, "HPET %u", + hpet_tbl->sequence); + + hpet_res->start = hpet_address; + hpet_res->end = hpet_address + (1 * 1024) - 1; + + return 0; +} + +/* + * hpet_insert_resource inserts the HPET resources used into the resource + * tree. + */ +static __init int hpet_insert_resource(void) +{ + if (!hpet_res) + return 1; + + return insert_resource(&iomem_resource, hpet_res); +} + +late_initcall(hpet_insert_resource); + +#else +#define acpi_parse_hpet NULL +#endif + +static int __init acpi_parse_fadt(struct acpi_table_header *table) +{ + +#ifdef CONFIG_X86_PM_TIMER + /* detect the location of the ACPI PM Timer */ + if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID) { + /* FADT rev. 2 */ + if (acpi_gbl_FADT.xpm_timer_block.space_id != + ACPI_ADR_SPACE_SYSTEM_IO) + return 0; + + pmtmr_ioport = acpi_gbl_FADT.xpm_timer_block.address; + /* + * "X" fields are optional extensions to the original V1.0 + * fields, so we must selectively expand V1.0 fields if the + * corresponding X field is zero. + */ + if (!pmtmr_ioport) + pmtmr_ioport = acpi_gbl_FADT.pm_timer_block; + } else { + /* FADT rev. 1 */ + pmtmr_ioport = acpi_gbl_FADT.pm_timer_block; + } + if (pmtmr_ioport) + printk(KERN_INFO PREFIX "PM-Timer IO Port: %#x\n", + pmtmr_ioport); +#endif + return 0; +} + +#ifdef CONFIG_X86_LOCAL_APIC +/* + * Parse LAPIC entries in MADT + * returns 0 on success, < 0 on error + */ + +static int __init early_acpi_parse_madt_lapic_addr_ovr(void) +{ + int count; + + if (!cpu_has_apic) + return -ENODEV; + + /* + * Note that the LAPIC address is obtained from the MADT (32-bit value) + * and (optionally) overriden by a LAPIC_ADDR_OVR entry (64-bit value). + */ + + count = + acpi_table_parse_madt(ACPI_MADT_TYPE_LOCAL_APIC_OVERRIDE, + acpi_parse_lapic_addr_ovr, 0); + if (count < 0) { + printk(KERN_ERR PREFIX + "Error parsing LAPIC address override entry\n"); + return count; + } + + register_lapic_address(acpi_lapic_addr); + + return count; +} + +static int __init acpi_parse_madt_lapic_entries(void) +{ + int count; + int x2count = 0; + + if (!cpu_has_apic) + return -ENODEV; + + /* + * Note that the LAPIC address is obtained from the MADT (32-bit value) + * and (optionally) overriden by a LAPIC_ADDR_OVR entry (64-bit value). + */ + + count = + acpi_table_parse_madt(ACPI_MADT_TYPE_LOCAL_APIC_OVERRIDE, + acpi_parse_lapic_addr_ovr, 0); + if (count < 0) { + printk(KERN_ERR PREFIX + "Error parsing LAPIC address override entry\n"); + return count; + } + + register_lapic_address(acpi_lapic_addr); + + count = acpi_table_parse_madt(ACPI_MADT_TYPE_LOCAL_SAPIC, + acpi_parse_sapic, MAX_LOCAL_APIC); + + if (!count) { + x2count = acpi_table_parse_madt(ACPI_MADT_TYPE_LOCAL_X2APIC, + acpi_parse_x2apic, MAX_LOCAL_APIC); + count = acpi_table_parse_madt(ACPI_MADT_TYPE_LOCAL_APIC, + acpi_parse_lapic, MAX_LOCAL_APIC); + } + if (!count && !x2count) { + printk(KERN_ERR PREFIX "No LAPIC entries present\n"); + /* TBD: Cleanup to allow fallback to MPS */ + return -ENODEV; + } else if (count < 0 || x2count < 0) { + printk(KERN_ERR PREFIX "Error parsing LAPIC entry\n"); + /* TBD: Cleanup to allow fallback to MPS */ + return count; + } + + x2count = + acpi_table_parse_madt(ACPI_MADT_TYPE_LOCAL_X2APIC_NMI, + acpi_parse_x2apic_nmi, 0); + count = + acpi_table_parse_madt(ACPI_MADT_TYPE_LOCAL_APIC_NMI, acpi_parse_lapic_nmi, 0); + if (count < 0 || x2count < 0) { + printk(KERN_ERR PREFIX "Error parsing LAPIC NMI entry\n"); + /* TBD: Cleanup to allow fallback to MPS */ + return count; + } + return 0; +} +#endif /* CONFIG_X86_LOCAL_APIC */ + +#ifdef CONFIG_X86_IO_APIC +#define MP_ISA_BUS 0 + +#ifdef CONFIG_X86_ES7000 +extern int es7000_plat; +#endif + +void __init mp_override_legacy_irq(u8 bus_irq, u8 polarity, u8 trigger, u32 gsi) +{ + int ioapic; + int pin; + struct mpc_intsrc mp_irq; + + /* + * Convert 'gsi' to 'ioapic.pin'. + */ + ioapic = mp_find_ioapic(gsi); + if (ioapic < 0) + return; + pin = mp_find_ioapic_pin(ioapic, gsi); + + /* + * TBD: This check is for faulty timer entries, where the override + * erroneously sets the trigger to level, resulting in a HUGE + * increase of timer interrupts! + */ + if ((bus_irq == 0) && (trigger == 3)) + trigger = 1; + + mp_irq.type = MP_INTSRC; + mp_irq.irqtype = mp_INT; + mp_irq.irqflag = (trigger << 2) | polarity; + mp_irq.srcbus = MP_ISA_BUS; + mp_irq.srcbusirq = bus_irq; /* IRQ */ + mp_irq.dstapic = mpc_ioapic_id(ioapic); /* APIC ID */ + mp_irq.dstirq = pin; /* INTIN# */ + + mp_save_irq(&mp_irq); + + isa_irq_to_gsi[bus_irq] = gsi; +} + +void __init mp_config_acpi_legacy_irqs(void) +{ + int i; + struct mpc_intsrc mp_irq; + +#ifdef CONFIG_EISA + /* + * Fabricate the legacy ISA bus (bus #31). + */ + mp_bus_id_to_type[MP_ISA_BUS] = MP_BUS_ISA; +#endif + set_bit(MP_ISA_BUS, mp_bus_not_pci); + pr_debug("Bus #%d is ISA\n", MP_ISA_BUS); + +#ifdef CONFIG_X86_ES7000 + /* + * Older generations of ES7000 have no legacy identity mappings + */ + if (es7000_plat == 1) + return; +#endif + + /* + * Use the default configuration for the IRQs 0-15. Unless + * overridden by (MADT) interrupt source override entries. + */ + for (i = 0; i < 16; i++) { + int ioapic, pin; + unsigned int dstapic; + int idx; + u32 gsi; + + /* Locate the gsi that irq i maps to. */ + if (acpi_isa_irq_to_gsi(i, &gsi)) + continue; + + /* + * Locate the IOAPIC that manages the ISA IRQ. + */ + ioapic = mp_find_ioapic(gsi); + if (ioapic < 0) + continue; + pin = mp_find_ioapic_pin(ioapic, gsi); + dstapic = mpc_ioapic_id(ioapic); + + for (idx = 0; idx < mp_irq_entries; idx++) { + struct mpc_intsrc *irq = mp_irqs + idx; + + /* Do we already have a mapping for this ISA IRQ? */ + if (irq->srcbus == MP_ISA_BUS && irq->srcbusirq == i) + break; + + /* Do we already have a mapping for this IOAPIC pin */ + if (irq->dstapic == dstapic && irq->dstirq == pin) + break; + } + + if (idx != mp_irq_entries) { + printk(KERN_DEBUG "ACPI: IRQ%d used by override.\n", i); + continue; /* IRQ already used */ + } + + mp_irq.type = MP_INTSRC; + mp_irq.irqflag = 0; /* Conforming */ + mp_irq.srcbus = MP_ISA_BUS; + mp_irq.dstapic = dstapic; + mp_irq.irqtype = mp_INT; + mp_irq.srcbusirq = i; /* Identity mapped */ + mp_irq.dstirq = pin; + + mp_save_irq(&mp_irq); + } +} + +static int mp_config_acpi_gsi(struct device *dev, u32 gsi, int trigger, + int polarity) +{ +#ifdef CONFIG_X86_MPPARSE + struct mpc_intsrc mp_irq; + struct pci_dev *pdev; + unsigned char number; + unsigned int devfn; + int ioapic; + u8 pin; + + if (!acpi_ioapic) + return 0; + if (!dev) + return 0; + if (dev->bus != &pci_bus_type) + return 0; + + pdev = to_pci_dev(dev); + number = pdev->bus->number; + devfn = pdev->devfn; + pin = pdev->pin; + /* print the entry should happen on mptable identically */ + mp_irq.type = MP_INTSRC; + mp_irq.irqtype = mp_INT; + mp_irq.irqflag = (trigger == ACPI_EDGE_SENSITIVE ? 4 : 0x0c) | + (polarity == ACPI_ACTIVE_HIGH ? 1 : 3); + mp_irq.srcbus = number; + mp_irq.srcbusirq = (((devfn >> 3) & 0x1f) << 2) | ((pin - 1) & 3); + ioapic = mp_find_ioapic(gsi); + mp_irq.dstapic = mpc_ioapic_id(ioapic); + mp_irq.dstirq = mp_find_ioapic_pin(ioapic, gsi); + + mp_save_irq(&mp_irq); +#endif + return 0; +} + +int mp_register_gsi(struct device *dev, u32 gsi, int trigger, int polarity) +{ + int ioapic; + int ioapic_pin; + struct io_apic_irq_attr irq_attr; + + if (acpi_irq_model != ACPI_IRQ_MODEL_IOAPIC) + return gsi; + + /* Don't set up the ACPI SCI because it's already set up */ + if (acpi_gbl_FADT.sci_interrupt == gsi) + return gsi; + + ioapic = mp_find_ioapic(gsi); + if (ioapic < 0) { + printk(KERN_WARNING "No IOAPIC for GSI %u\n", gsi); + return gsi; + } + + ioapic_pin = mp_find_ioapic_pin(ioapic, gsi); + + if (ioapic_pin > MP_MAX_IOAPIC_PIN) { + printk(KERN_ERR "Invalid reference to IOAPIC pin " + "%d-%d\n", mpc_ioapic_id(ioapic), + ioapic_pin); + return gsi; + } + + if (enable_update_mptable) + mp_config_acpi_gsi(dev, gsi, trigger, polarity); + + set_io_apic_irq_attr(&irq_attr, ioapic, ioapic_pin, + trigger == ACPI_EDGE_SENSITIVE ? 0 : 1, + polarity == ACPI_ACTIVE_HIGH ? 0 : 1); + io_apic_set_pci_routing(dev, gsi_to_irq(gsi), &irq_attr); + + return gsi; +} + +/* + * Parse IOAPIC related entries in MADT + * returns 0 on success, < 0 on error + */ +static int __init acpi_parse_madt_ioapic_entries(void) +{ + int count; + + /* + * ACPI interpreter is required to complete interrupt setup, + * so if it is off, don't enumerate the io-apics with ACPI. + * If MPS is present, it will handle them, + * otherwise the system will stay in PIC mode + */ + if (acpi_disabled || acpi_noirq) + return -ENODEV; + + if (!cpu_has_apic) + return -ENODEV; + + /* + * if "noapic" boot option, don't look for IO-APICs + */ + if (skip_ioapic_setup) { + printk(KERN_INFO PREFIX "Skipping IOAPIC probe " + "due to 'noapic' option.\n"); + return -ENODEV; + } + + count = + acpi_table_parse_madt(ACPI_MADT_TYPE_IO_APIC, acpi_parse_ioapic, + MAX_IO_APICS); + if (!count) { + printk(KERN_ERR PREFIX "No IOAPIC entries present\n"); + return -ENODEV; + } else if (count < 0) { + printk(KERN_ERR PREFIX "Error parsing IOAPIC entry\n"); + return count; + } + + count = + acpi_table_parse_madt(ACPI_MADT_TYPE_INTERRUPT_OVERRIDE, acpi_parse_int_src_ovr, + nr_irqs); + if (count < 0) { + printk(KERN_ERR PREFIX + "Error parsing interrupt source overrides entry\n"); + /* TBD: Cleanup to allow fallback to MPS */ + return count; + } + + /* + * If BIOS did not supply an INT_SRC_OVR for the SCI + * pretend we got one so we can set the SCI flags. + */ + if (!acpi_sci_override_gsi) + acpi_sci_ioapic_setup(acpi_gbl_FADT.sci_interrupt, 0, 0, + acpi_gbl_FADT.sci_interrupt); + + /* Fill in identity legacy mappings where no override */ + mp_config_acpi_legacy_irqs(); + + count = + acpi_table_parse_madt(ACPI_MADT_TYPE_NMI_SOURCE, acpi_parse_nmi_src, + nr_irqs); + if (count < 0) { + printk(KERN_ERR PREFIX "Error parsing NMI SRC entry\n"); + /* TBD: Cleanup to allow fallback to MPS */ + return count; + } + + return 0; +} +#else +static inline int acpi_parse_madt_ioapic_entries(void) +{ + return -1; +} +#endif /* !CONFIG_X86_IO_APIC */ + +static void __init early_acpi_process_madt(void) +{ +#ifdef CONFIG_X86_LOCAL_APIC + int error; + + if (!acpi_table_parse(ACPI_SIG_MADT, acpi_parse_madt)) { + + /* + * Parse MADT LAPIC entries + */ + error = early_acpi_parse_madt_lapic_addr_ovr(); + if (!error) { + acpi_lapic = 1; + smp_found_config = 1; + } + if (error == -EINVAL) { + /* + * Dell Precision Workstation 410, 610 come here. + */ + printk(KERN_ERR PREFIX + "Invalid BIOS MADT, disabling ACPI\n"); + disable_acpi(); + } + } +#endif +} + +static void __init acpi_process_madt(void) +{ +#ifdef CONFIG_X86_LOCAL_APIC + int error; + + if (!acpi_table_parse(ACPI_SIG_MADT, acpi_parse_madt)) { + + /* + * Parse MADT LAPIC entries + */ + error = acpi_parse_madt_lapic_entries(); + if (!error) { + acpi_lapic = 1; + + /* + * Parse MADT IO-APIC entries + */ + error = acpi_parse_madt_ioapic_entries(); + if (!error) { + acpi_set_irq_model_ioapic(); + + smp_found_config = 1; + } + } + if (error == -EINVAL) { + /* + * Dell Precision Workstation 410, 610 come here. + */ + printk(KERN_ERR PREFIX + "Invalid BIOS MADT, disabling ACPI\n"); + disable_acpi(); + } + } else { + /* + * ACPI found no MADT, and so ACPI wants UP PIC mode. + * In the event an MPS table was found, forget it. + * Boot with "acpi=off" to use MPS on such a system. + */ + if (smp_found_config) { + printk(KERN_WARNING PREFIX + "No APIC-table, disabling MPS\n"); + smp_found_config = 0; + } + } + + /* + * ACPI supports both logical (e.g. Hyper-Threading) and physical + * processors, where MPS only supports physical. + */ + if (acpi_lapic && acpi_ioapic) + printk(KERN_INFO "Using ACPI (MADT) for SMP configuration " + "information\n"); + else if (acpi_lapic) + printk(KERN_INFO "Using ACPI for processor (LAPIC) " + "configuration information\n"); +#endif + return; +} + +void set_checksum(u8 *start, int len, u8 *cksum) +{ + u8 newsum, oldsum; + u8 *p; + + newsum = 0; + for (p = (u8 *)start; p < (u8 *)(start + len); p++) + newsum += *p; + + oldsum = *cksum; + newsum = (u8)(newsum - oldsum); + + *cksum = (u8)(0 - newsum); +} + +void __init acpi_arm_blob_relocate(void) +{ + /* + * Fortunately, there are only a few tables that need to + * have their offsets converted to actual addresses. + * + * NB: all values in the blob are little-endian. + */ + + struct acpi_table_rsdp *rp; + struct acpi_table_xsdt *xp; + struct acpi_table_fadt *fp; + u32 vaddress, paddress; + u32 entries; + u32 ii; + u64 *tmp; + + if (!acpi_arm_rsdp_info.phys_address && !acpi_arm_rsdp_info.size) { + printk(KERN_ERR "(E) ACPI: failed to find rsdp info\n"); + return; + } + + paddress = (u32)acpi_arm_rsdp_info.phys_address; + paddress += ACPI_BLOB_HEADER_SIZE; + vaddress = (u32)(phys_to_virt(paddress)); + + /* fixups for the rsdp */ + rp = (struct acpi_table_rsdp *)vaddress; + if (rp->rsdt_physical_address) + rp->rsdt_physical_address += paddress; + if (rp->xsdt_physical_address) + rp->xsdt_physical_address += paddress; + set_checksum((u8 *)rp, rp->length, &(rp->checksum)); + + /* fixups for the xsdt */ + vaddress = (u32)(phys_to_virt(rp->xsdt_physical_address)); + xp = (struct acpi_table_xsdt *)vaddress; + entries = xp->header.length - sizeof (struct acpi_table_header); + entries /= 8; /* length is in bytes */ + tmp = (u64 *)(&(xp->table_offset_entry[0])); + for (ii = 0; ii < entries; ii++) + *tmp++ += paddress; + set_checksum((u8 *)xp, xp->header.length, &(xp->header.checksum)); + + /* fixups for the fadt */ + vaddress = (u32)(phys_to_virt(xp->table_offset_entry[0])); + fp = (struct acpi_table_fadt *)vaddress; + if (fp->facs) + fp->facs += paddress; + if (fp->dsdt) + fp->dsdt += paddress; + if (fp->Xfacs) + fp->Xfacs += paddress; + if (fp->Xdsdt) + fp->Xdsdt += paddress; + + /* Always fix up the checksums since we've changed bits. */ + set_checksum((u8 *)fp, fp->header.length, &(fp->header.checksum)); +} + +/* + * ========== OLD COMMENTS FROM x86 ================================= + * acpi_boot_table_init() and acpi_boot_init() + * called from setup_arch(), always. + * 1. checksums all tables + * 2. enumerates lapics + * 3. enumerates io-apics + * + * acpi_table_init() is separate to allow reading SRAT without + * other side effects. + * + * side effects of acpi_boot_init: + * acpi_lapic = 1 if LAPIC found + * acpi_ioapic = 1 if IOAPIC found + * if (acpi_lapic && acpi_ioapic) smp_found_config = 1; + * if acpi_blacklisted() acpi_disabled = 1; + * acpi_irq_model=... + * ... + * ================================================================== + * + * We have to approach this a little different on ARMv7. We are + * passed in an ACPI blob and we really have no idea where in RAM + * it will be located. So, what should have been the physical + * addresses of other tables cannot really be hardcoded into the + * tables. What we will do is put an offset in the blob that is + * the offset from the beginning of the RSDP structure. However, + * what that means is that we have to unpack the blob and do a + * bit of fixup work on the offsets to turn them into kernel + * virtual addresses so we can pass them on for later use. + */ + +void __init acpi_boot_table_init(void) +{ + /* + * If acpi_disabled, bail out + */ + if (acpi_disabled) + return; + + printk(KERN_DEBUG "acpi: enter acpi_boot_table_init\n"); + + /* + * Fix up the addresses in the ACPI we've loaded + * in. The blob has them as offsets and we need + * actual addresses. + */ + acpi_arm_blob_relocate(); + + /* + * Initialize the ACPI boot-time table parser. + */ + if (acpi_table_init()) { + disable_acpi(); + return; + } + + printk(KERN_INFO "(I) acpi_table_init call completed\n"); + acpi_table_parse(ACPI_SIG_BOOT, acpi_parse_sbf); + + /* + * blacklist may disable ACPI entirely + */ + if (acpi_blacklisted()) { + if (acpi_force) { + printk(KERN_WARNING PREFIX "acpi=force override\n"); + } else { + printk(KERN_WARNING PREFIX "Disabling ACPI support\n"); + disable_acpi(); + return; + } + } + + printk(KERN_INFO "(I) exit acpi_boot_table_init\n"); +} + +int __init early_acpi_boot_init(void) +{ + /* + * If acpi_disabled, bail out + */ + if (acpi_disabled) + return 1; + + printk(KERN_INFO "enter early_acpi_boot_init\n"); + + /* + * Process the Multiple APIC Description Table (MADT), if present + */ + early_acpi_process_madt(); + + return 0; +} + +int __init acpi_boot_init(void) +{ + /* + * If acpi_disabled, bail out + */ + if (acpi_disabled) + return 1; + + printk(KERN_INFO "enter acpi_boot_init\n"); + + acpi_table_parse(ACPI_SIG_BOOT, acpi_parse_sbf); + + /* + * set sci_int and PM timer address + */ + acpi_table_parse(ACPI_SIG_FADT, acpi_parse_fadt); + + /* + * Process the Multiple APIC Description Table (MADT), if present + */ + acpi_process_madt(); + + acpi_table_parse(ACPI_SIG_HPET, acpi_parse_hpet); + +#ifdef CONFIG_X86 + if (!acpi_noirq) + x86_init.pci.init = pci_acpi_init; +#endif + + return 0; +} + +static int __init parse_acpi(char *arg) +{ + if (!arg) + return -EINVAL; + + /* "acpi=off" disables both ACPI table parsing and interpreter */ + if (strcmp(arg, "off") == 0) { + disable_acpi(); + } + /* acpi=force to over-ride black-list */ + else if (strcmp(arg, "force") == 0) { + acpi_force = 1; + acpi_disabled = 0; + } + /* acpi=strict disables out-of-spec workarounds */ + else if (strcmp(arg, "strict") == 0) { + acpi_strict = 1; + } + /* acpi=rsdt use RSDT instead of XSDT */ + else if (strcmp(arg, "rsdt") == 0) { + acpi_rsdt_forced = 1; + } + /* "acpi=noirq" disables ACPI interrupt routing */ + else if (strcmp(arg, "noirq") == 0) { + acpi_noirq_set(); + } + /* "acpi=copy_dsdt" copys DSDT */ + else if (strcmp(arg, "copy_dsdt") == 0) { + acpi_gbl_copy_dsdt_locally = 1; + } else { + /* Core will printk when we return error. */ + return -EINVAL; + } + return 0; +} +early_param("acpi", parse_acpi); + +/* FIXME: Using pci= for an ACPI parameter is a travesty. */ +static int __init parse_pci(char *arg) +{ + if (arg && strcmp(arg, "noacpi") == 0) + acpi_disable_pci(); + return 0; +} +early_param("pci", parse_pci); + +int __init acpi_mps_check(void) +{ +#if defined(CONFIG_X86_LOCAL_APIC) && !defined(CONFIG_X86_MPPARSE) +/* mptable code is not built-in*/ + if (acpi_disabled || acpi_noirq) { + printk(KERN_WARNING "MPS support code is not built-in.\n" + "Using acpi=off or acpi=noirq or pci=noacpi " + "may have problem\n"); + return 1; + } +#endif + return 0; +} + +#ifdef CONFIG_X86_IO_APIC +static int __init parse_acpi_skip_timer_override(char *arg) +{ + acpi_skip_timer_override = 1; + return 0; +} +early_param("acpi_skip_timer_override", parse_acpi_skip_timer_override); + +static int __init parse_acpi_use_timer_override(char *arg) +{ + acpi_use_timer_override = 1; + return 0; +} +early_param("acpi_use_timer_override", parse_acpi_use_timer_override); +#endif /* CONFIG_X86_IO_APIC */ + +static int __init setup_acpi_sci(char *s) +{ + if (!s) + return -EINVAL; + if (!strcmp(s, "edge")) + acpi_sci_flags = ACPI_MADT_TRIGGER_EDGE | + (acpi_sci_flags & ~ACPI_MADT_TRIGGER_MASK); + else if (!strcmp(s, "level")) + acpi_sci_flags = ACPI_MADT_TRIGGER_LEVEL | + (acpi_sci_flags & ~ACPI_MADT_TRIGGER_MASK); + else if (!strcmp(s, "high")) + acpi_sci_flags = ACPI_MADT_POLARITY_ACTIVE_HIGH | + (acpi_sci_flags & ~ACPI_MADT_POLARITY_MASK); + else if (!strcmp(s, "low")) + acpi_sci_flags = ACPI_MADT_POLARITY_ACTIVE_LOW | + (acpi_sci_flags & ~ACPI_MADT_POLARITY_MASK); + else + return -EINVAL; + return 0; +} +early_param("acpi_sci", setup_acpi_sci); + +int __acpi_acquire_global_lock(unsigned int *lock) +{ + unsigned int old, new, val; + do { + old = *lock; + new = (((old & ~0x3) + 2) + ((old >> 1) & 0x1)); + val = cmpxchg(lock, old, new); + } while (unlikely (val != old)); + return (new < 3) ? -1 : 0; +} + +int __acpi_release_global_lock(unsigned int *lock) +{ + unsigned int old, new, val; + do { + old = *lock; + new = old & ~0x3; + val = cmpxchg(lock, old, new); + } while (unlikely (val != old)); + return old & 0x1; +} + diff --git a/arch/arm64/kernel/acpi/cstate.c b/arch/arm64/kernel/acpi/cstate.c new file mode 100644 index 0000000..80d4b3d --- /dev/null +++ b/arch/arm64/kernel/acpi/cstate.c @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2005 Intel Corporation + * Venkatesh Pallipadi venkatesh.pallipadi@intel.com + * - Added _PDC for SMP C-states on Intel CPUs + */ + +/* BOZO: i think we just want to ignore C-states for now */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/acpi.h> +#include <linux/cpu.h> +#include <linux/sched.h> + +#include <acpi/processor.h> +#include <asm/acpi.h> +#ifdef CONFIG_X86 +#include <asm/mwait.h> +#include <asm/special_insns.h> +#endif + +/* + * Initialize bm_flags based on the CPU cache properties + * On SMP it depends on cache configuration + * - When cache is not shared among all CPUs, we flush cache + * before entering C3. + * - When cache is shared among all CPUs, we use bm_check + * mechanism as in UP case + * + * This routine is called only after all the CPUs are online + */ +void acpi_processor_power_init_bm_check(struct acpi_processor_flags *flags, + unsigned int cpu) +{ +#ifdef CONFIG_X86 + struct cpuinfo_x86 *c = &cpu_data(cpu); + + flags->bm_check = 0; + if (num_online_cpus() == 1) + flags->bm_check = 1; + else if (c->x86_vendor == X86_VENDOR_INTEL) { + /* + * Today all MP CPUs that support C3 share cache. + * And caches should not be flushed by software while + * entering C3 type state. + */ + flags->bm_check = 1; + } + + /* + * On all recent Intel platforms, ARB_DISABLE is a nop. + * So, set bm_control to zero to indicate that ARB_DISABLE + * is not required while entering C3 type state on + * P4, Core and beyond CPUs + */ + if (c->x86_vendor == X86_VENDOR_INTEL && + (c->x86 > 0xf || (c->x86 == 6 && c->x86_model >= 0x0f))) + flags->bm_control = 0; +#endif +} +EXPORT_SYMBOL(acpi_processor_power_init_bm_check); + +/* The code below handles cstate entry with monitor-mwait pair on Intel*/ + +struct cstate_entry { + struct { + unsigned int eax; + unsigned int ecx; + } states[ACPI_PROCESSOR_MAX_POWER]; +}; +static struct cstate_entry __percpu *cpu_cstate_entry; /* per CPU ptr */ + +static short mwait_supported[ACPI_PROCESSOR_MAX_POWER]; + +#define NATIVE_CSTATE_BEYOND_HALT (2) + +static long acpi_processor_ffh_cstate_probe_cpu(void *_cx) +{ + struct acpi_processor_cx *cx = _cx; + long retval; + unsigned int eax, ebx, ecx, edx; + unsigned int edx_part; + unsigned int cstate_type; /* C-state type and not ACPI C-state type */ + unsigned int num_cstate_subtype; + + cpuid(CPUID_MWAIT_LEAF, &eax, &ebx, &ecx, &edx); + + /* Check whether this particular cx_type (in CST) is supported or not */ + cstate_type = ((cx->address >> MWAIT_SUBSTATE_SIZE) & + MWAIT_CSTATE_MASK) + 1; + edx_part = edx >> (cstate_type * MWAIT_SUBSTATE_SIZE); + num_cstate_subtype = edx_part & MWAIT_SUBSTATE_MASK; + + retval = 0; + if (num_cstate_subtype < (cx->address & MWAIT_SUBSTATE_MASK)) { + retval = -1; + goto out; + } + + /* mwait ecx extensions INTERRUPT_BREAK should be supported for C2/C3 */ + if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED) || + !(ecx & CPUID5_ECX_INTERRUPT_BREAK)) { + retval = -1; + goto out; + } + + if (!mwait_supported[cstate_type]) { + mwait_supported[cstate_type] = 1; + printk(KERN_DEBUG + "Monitor-Mwait will be used to enter C-%d " + "state\n", cx->type); + } + snprintf(cx->desc, + ACPI_CX_DESC_LEN, "ACPI FFH INTEL MWAIT 0x%x", + cx->address); +out: + return retval; +} + +int acpi_processor_ffh_cstate_probe(unsigned int cpu, + struct acpi_processor_cx *cx, struct acpi_power_register *reg) +{ + struct cstate_entry *percpu_entry; + struct cpuinfo_x86 *c = &cpu_data(cpu); + long retval; + + if (!cpu_cstate_entry || c->cpuid_level < CPUID_MWAIT_LEAF) + return -1; + + if (reg->bit_offset != NATIVE_CSTATE_BEYOND_HALT) + return -1; + + percpu_entry = per_cpu_ptr(cpu_cstate_entry, cpu); + percpu_entry->states[cx->index].eax = 0; + percpu_entry->states[cx->index].ecx = 0; + + /* Make sure we are running on right CPU */ + + retval = work_on_cpu(cpu, acpi_processor_ffh_cstate_probe_cpu, cx); + if (retval == 0) { + /* Use the hint in CST */ + percpu_entry->states[cx->index].eax = cx->address; + percpu_entry->states[cx->index].ecx = MWAIT_ECX_INTERRUPT_BREAK; + } + + /* + * For _CST FFH on Intel, if GAS.access_size bit 1 is cleared, + * then we should skip checking BM_STS for this C-state. + * ref: "Intel Processor Vendor-Specific ACPI Interface Specification" + */ + if ((c->x86_vendor == X86_VENDOR_INTEL) && !(reg->access_size & 0x2)) + cx->bm_sts_skip = 1; + + return retval; +} +EXPORT_SYMBOL_GPL(acpi_processor_ffh_cstate_probe); + +/* + * This uses new MONITOR/MWAIT instructions on P4 processors with PNI, + * which can obviate IPI to trigger checking of need_resched. + * We execute MONITOR against need_resched and enter optimized wait state + * through MWAIT. Whenever someone changes need_resched, we would be woken + * up from MWAIT (without an IPI). + * + * New with Core Duo processors, MWAIT can take some hints based on CPU + * capability. + */ +void mwait_idle_with_hints(unsigned long ax, unsigned long cx) +{ + if (!need_resched()) { + if (this_cpu_has(X86_FEATURE_CLFLUSH_MONITOR)) + clflush((void *)¤t_thread_info()->flags); + + __monitor((void *)¤t_thread_info()->flags, 0, 0); + smp_mb(); + if (!need_resched()) + __mwait(ax, cx); + } +} + +void acpi_processor_ffh_cstate_enter(struct acpi_processor_cx *cx) +{ + unsigned int cpu = smp_processor_id(); + struct cstate_entry *percpu_entry; + + percpu_entry = per_cpu_ptr(cpu_cstate_entry, cpu); + mwait_idle_with_hints(percpu_entry->states[cx->index].eax, + percpu_entry->states[cx->index].ecx); +} +EXPORT_SYMBOL_GPL(acpi_processor_ffh_cstate_enter); + +static int __init ffh_cstate_init(void) +{ + struct cpuinfo_x86 *c = &boot_cpu_data; + if (c->x86_vendor != X86_VENDOR_INTEL) + return -1; + + cpu_cstate_entry = alloc_percpu(struct cstate_entry); + return 0; +} + +static void __exit ffh_cstate_exit(void) +{ + free_percpu(cpu_cstate_entry); + cpu_cstate_entry = NULL; +} + +arch_initcall(ffh_cstate_init); +__exitcall(ffh_cstate_exit); diff --git a/arch/arm64/kernel/acpi/sleep.c b/arch/arm64/kernel/acpi/sleep.c new file mode 100644 index 0000000..d9d4209 --- /dev/null +++ b/arch/arm64/kernel/acpi/sleep.c @@ -0,0 +1,126 @@ +/* + * sleep.c - x86-specific ACPI sleep support. + * + * Copyright (C) 2001-2003 Patrick Mochel + * Copyright (C) 2001-2003 Pavel Machek pavel@ucw.cz + */ + +int acpi_suspend_lowlevel(void) +{ + /* BOZO: dummy routine; see below for actual */ + return 0; +} + +#ifdef CONFIG_X86 +/* BOZO: disable everything for now... */ + +#include <linux/acpi.h> +#include <linux/bootmem.h> +#include <linux/memblock.h> +#include <linux/dmi.h> +#include <linux/cpumask.h> +#include <asm/segment.h> +#include <asm/desc.h> +#include <asm/pgtable.h> +#include <asm/cacheflush.h> +#include <asm/realmode.h> + +#include "../../realmode/rm/wakeup.h" +#include "sleep.h" + +unsigned long acpi_realmode_flags; + +#if defined(CONFIG_SMP) && defined(CONFIG_64BIT) +static char temp_stack[4096]; +#endif + +/** + * acpi_suspend_lowlevel - save kernel state + * + * Create an identity mapped page table and copy the wakeup routine to + * low memory. + */ +int acpi_suspend_lowlevel(void) +{ + struct wakeup_header *header = + (struct wakeup_header *) __va(real_mode_header->wakeup_header); + + if (header->signature != WAKEUP_HEADER_SIGNATURE) { + printk(KERN_ERR "wakeup header does not match\n"); + return -EINVAL; + } + + header->video_mode = saved_video_mode; + + header->pmode_behavior = 0; + +#ifndef CONFIG_64BIT + store_gdt((struct desc_ptr *)&header->pmode_gdt); + + if (!rdmsr_safe(MSR_EFER, + &header->pmode_efer_low, + &header->pmode_efer_high)) + header->pmode_behavior |= (1 << WAKEUP_BEHAVIOR_RESTORE_EFER); +#endif /* !CONFIG_64BIT */ + + header->pmode_cr0 = read_cr0(); + if (__this_cpu_read(cpu_info.cpuid_level) >= 0) { + header->pmode_cr4 = read_cr4(); + header->pmode_behavior |= (1 << WAKEUP_BEHAVIOR_RESTORE_CR4); + } + if (!rdmsr_safe(MSR_IA32_MISC_ENABLE, + &header->pmode_misc_en_low, + &header->pmode_misc_en_high)) + header->pmode_behavior |= + (1 << WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE); + header->realmode_flags = acpi_realmode_flags; + header->real_magic = 0x12345678; + +#ifndef CONFIG_64BIT + header->pmode_entry = (u32)&wakeup_pmode_return; + header->pmode_cr3 = (u32)__pa(&initial_page_table); + saved_magic = 0x12345678; +#else /* CONFIG_64BIT */ +#ifdef CONFIG_SMP + stack_start = (unsigned long)temp_stack + sizeof(temp_stack); + early_gdt_descr.address = + (unsigned long)get_cpu_gdt_table(smp_processor_id()); + initial_gs = per_cpu_offset(smp_processor_id()); +#endif + initial_code = (unsigned long)wakeup_long64; + saved_magic = 0x123456789abcdef0L; +#endif /* CONFIG_64BIT */ + + do_suspend_lowlevel(); + return 0; +} + +static int __init acpi_sleep_setup(char *str) +{ + while ((str != NULL) && (*str != '\0')) { + if (strncmp(str, "s3_bios", 7) == 0) + acpi_realmode_flags |= 1; + if (strncmp(str, "s3_mode", 7) == 0) + acpi_realmode_flags |= 2; + if (strncmp(str, "s3_beep", 7) == 0) + acpi_realmode_flags |= 4; +#ifdef CONFIG_HIBERNATION + if (strncmp(str, "s4_nohwsig", 10) == 0) + acpi_no_s4_hw_signature(); +#endif + if (strncmp(str, "nonvs", 5) == 0) + acpi_nvs_nosave(); + if (strncmp(str, "nonvs_s3", 8) == 0) + acpi_nvs_nosave_s3(); + if (strncmp(str, "old_ordering", 12) == 0) + acpi_old_suspend_ordering(); + str = strchr(str, ','); + if (str != NULL) + str += strspn(str, ", \t"); + } + return 1; +} + +__setup("acpi_sleep=", acpi_sleep_setup); + +#endif diff --git a/arch/arm64/kernel/acpi/sleep.h b/arch/arm64/kernel/acpi/sleep.h new file mode 100644 index 0000000..67f59f8c --- /dev/null +++ b/arch/arm64/kernel/acpi/sleep.h @@ -0,0 +1,17 @@ +/* + * Variables and functions used by the code in sleep.c + */ + +#include <asm/realmode.h> + +extern unsigned long saved_video_mode; +extern long saved_magic; + +extern int wakeup_pmode_return; + +extern u8 wake_sleep_flags; + +extern unsigned long acpi_copy_wakeup_routine(unsigned long); +extern void wakeup_long64(void); + +extern void do_suspend_lowlevel(void); diff --git a/arch/arm64/kernel/acpi/wakeup_32.S b/arch/arm64/kernel/acpi/wakeup_32.S new file mode 100644 index 0000000..13ab720 --- /dev/null +++ b/arch/arm64/kernel/acpi/wakeup_32.S @@ -0,0 +1,100 @@ + .section .text..page_aligned +#include <linux/linkage.h> +#include <asm/segment.h> +#include <asm/page_types.h> + +# Copyright 2003, 2008 Pavel Machek pavel@suse.cz, distribute under GPLv2 + + .code32 + ALIGN + +ENTRY(wakeup_pmode_return) +wakeup_pmode_return: + movw $__KERNEL_DS, %ax + movw %ax, %ss + movw %ax, %ds + movw %ax, %es + 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 + ljmp $(__KERNEL_CS), $1f +1: + movl %cr3, %eax + movl %eax, %cr3 + wbinvd + + # and restore the stack ... but you need gdt for this to work + movl saved_context_esp, %esp + + movl %cs:saved_magic, %eax + cmpl $0x12345678, %eax + jne bogus_magic + + # jump to place where we left off + movl saved_eip, %eax + jmp *%eax + +bogus_magic: + jmp bogus_magic + + + +save_registers: + sgdt saved_gdt + sidt saved_idt + sldt saved_ldt + str saved_tss + + leal 4(%esp), %eax + movl %eax, saved_context_esp + movl %ebx, saved_context_ebx + movl %ebp, saved_context_ebp + movl %esi, saved_context_esi + movl %edi, saved_context_edi + pushfl + popl saved_context_eflags + + movl $ret_point, saved_eip + ret + + +restore_registers: + movl saved_context_ebp, %ebp + movl saved_context_ebx, %ebx + movl saved_context_esi, %esi + movl saved_context_edi, %edi + pushl saved_context_eflags + popfl + ret + +ENTRY(do_suspend_lowlevel) + call save_processor_state + call save_registers + pushl $3 + call acpi_enter_sleep_state + addl $4, %esp + +# In case of S3 failure, we'll emerge here. Jump +# to ret_point to recover + jmp ret_point + .p2align 4,,7 +ret_point: + call restore_registers + call restore_processor_state + ret + +.data +ALIGN +ENTRY(saved_magic) .long 0 +ENTRY(saved_eip) .long 0 + +# saved registers +saved_gdt: .long 0,0 +saved_idt: .long 0,0 +saved_ldt: .long 0 +saved_tss: .long 0 + diff --git a/arch/arm64/kernel/acpi/wakeup_64.S b/arch/arm64/kernel/acpi/wakeup_64.S new file mode 100644 index 0000000..8ea5164 --- /dev/null +++ b/arch/arm64/kernel/acpi/wakeup_64.S @@ -0,0 +1,124 @@ +.text +#include <linux/linkage.h> +#include <asm/segment.h> +#include <asm/pgtable_types.h> +#include <asm/page_types.h> +#include <asm/msr.h> +#include <asm/asm-offsets.h> + +# Copyright 2003 Pavel Machek pavel@suse.cz, distribute under GPLv2 + +.code64 + /* + * Hooray, we are in Long 64-bit mode (but still running in low memory) + */ +ENTRY(wakeup_long64) + movq saved_magic, %rax + movq $0x123456789abcdef0, %rdx + cmpq %rdx, %rax + jne bogus_64_magic + + movw $__KERNEL_DS, %ax + movw %ax, %ss + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movq saved_rsp, %rsp + + movq saved_rbx, %rbx + movq saved_rdi, %rdi + movq saved_rsi, %rsi + movq saved_rbp, %rbp + + movq saved_rip, %rax + jmp *%rax +ENDPROC(wakeup_long64) + +bogus_64_magic: + jmp bogus_64_magic + +ENTRY(do_suspend_lowlevel) + subq $8, %rsp + xorl %eax, %eax + call save_processor_state + + movq $saved_context, %rax + movq %rsp, pt_regs_sp(%rax) + movq %rbp, pt_regs_bp(%rax) + movq %rsi, pt_regs_si(%rax) + movq %rdi, pt_regs_di(%rax) + movq %rbx, pt_regs_bx(%rax) + movq %rcx, pt_regs_cx(%rax) + movq %rdx, pt_regs_dx(%rax) + movq %r8, pt_regs_r8(%rax) + movq %r9, pt_regs_r9(%rax) + movq %r10, pt_regs_r10(%rax) + movq %r11, pt_regs_r11(%rax) + movq %r12, pt_regs_r12(%rax) + movq %r13, pt_regs_r13(%rax) + movq %r14, pt_regs_r14(%rax) + movq %r15, pt_regs_r15(%rax) + pushfq + popq pt_regs_flags(%rax) + + movq $resume_point, saved_rip(%rip) + + movq %rsp, saved_rsp + movq %rbp, saved_rbp + movq %rbx, saved_rbx + movq %rdi, saved_rdi + movq %rsi, saved_rsi + + addq $8, %rsp + movl $3, %edi + xorl %eax, %eax + call acpi_enter_sleep_state + /* in case something went wrong, restore the machine status and go on */ + jmp resume_point + + .align 4 +resume_point: + /* We don't restore %rax, it must be 0 anyway */ + movq $saved_context, %rax + movq saved_context_cr4(%rax), %rbx + movq %rbx, %cr4 + movq saved_context_cr3(%rax), %rbx + movq %rbx, %cr3 + movq saved_context_cr2(%rax), %rbx + movq %rbx, %cr2 + movq saved_context_cr0(%rax), %rbx + movq %rbx, %cr0 + pushq pt_regs_flags(%rax) + popfq + movq pt_regs_sp(%rax), %rsp + movq pt_regs_bp(%rax), %rbp + movq pt_regs_si(%rax), %rsi + movq pt_regs_di(%rax), %rdi + movq pt_regs_bx(%rax), %rbx + movq pt_regs_cx(%rax), %rcx + movq pt_regs_dx(%rax), %rdx + movq pt_regs_r8(%rax), %r8 + movq pt_regs_r9(%rax), %r9 + movq pt_regs_r10(%rax), %r10 + movq pt_regs_r11(%rax), %r11 + movq pt_regs_r12(%rax), %r12 + movq pt_regs_r13(%rax), %r13 + movq pt_regs_r14(%rax), %r14 + movq pt_regs_r15(%rax), %r15 + + xorl %eax, %eax + addq $8, %rsp + jmp restore_processor_state +ENDPROC(do_suspend_lowlevel) + +.data +ENTRY(saved_rbp) .quad 0 +ENTRY(saved_rsi) .quad 0 +ENTRY(saved_rdi) .quad 0 +ENTRY(saved_rbx) .quad 0 + +ENTRY(saved_rip) .quad 0 +ENTRY(saved_rsp) .quad 0 + +ENTRY(saved_magic) .quad 0 diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index 6a9a532..ed3ac6d 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -54,6 +54,10 @@ #include <asm/memblock.h> #include <asm/psci.h>
+#ifdef CONFIG_ACPI +#include <asm/acpi.h> +#endif + unsigned int processor_id; EXPORT_SYMBOL(processor_id);
@@ -166,6 +170,10 @@ static void __init setup_machine_fdt(phys_addr_t dt_phys)
/* Retrieve various information from the /chosen node */ of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line); +#ifdef CONFIG_ACPI + /* Retrieve ACPI pointers from /chosen node */ + of_scan_flat_dt(early_init_dt_scan_acpi, &acpi_arm_rsdp_info); +#endif /* Initialize {size,address}-cells info */ of_scan_flat_dt(early_init_dt_scan_root, NULL); /* Setup memory, calling early_init_dt_add_memory_arch */ diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 4bf68c8..ef39891 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -5,10 +5,9 @@ menuconfig ACPI bool "ACPI (Advanced Configuration and Power Interface) Support" depends on !IA64_HP_SIM - depends on IA64 || X86 - depends on PCI + depends on ((IA64 || X86) && PCI) || (ARM || ARM64) select PNP - default y + default y if !(ARM || ARM64) help Advanced Configuration and Power Interface (ACPI) support for Linux requires an ACPI-compliant platform (hardware/firmware), @@ -46,6 +45,7 @@ config ACPI_SLEEP config ACPI_PROCFS bool "Deprecated /proc/acpi files" depends on PROC_FS + depends on IA64 || X86 help For backwards compatibility, this option allows deprecated /proc/acpi/ files to exist, even when @@ -59,6 +59,7 @@ config ACPI_PROCFS config ACPI_PROCFS_POWER bool "Deprecated power /proc/acpi directories" depends on PROC_FS + depends on IA64 || X86 help For backwards compatibility, this option allows deprecated power /proc/acpi/ directories to exist, even when @@ -94,6 +95,7 @@ config ACPI_EC_DEBUGFS config ACPI_PROC_EVENT bool "Deprecated /proc/acpi/event support" depends on PROC_FS + depends on IA64 || X86 default y help A user-space daemon, acpid, typically reads /proc/acpi/event @@ -111,9 +113,9 @@ config ACPI_PROC_EVENT
config ACPI_AC tristate "AC Adapter" - depends on X86 + depends on X86 || (ARM || ARM64) select POWER_SUPPLY - default y + default y if !(ARM || ARM64) help This driver supports the AC Adapter object, which indicates whether a system is on AC or not. If you have a system that can @@ -124,9 +126,9 @@ config ACPI_AC
config ACPI_BATTERY tristate "Battery" - depends on X86 + depends on X86 || (ARM || ARM64) select POWER_SUPPLY - default y + default y if !(ARM || ARM64) help This driver adds support for battery information through /proc/acpi/battery. If you have a mobile system with a battery, @@ -150,7 +152,8 @@ config ACPI_BUTTON
config ACPI_VIDEO tristate "Video" - depends on X86 && BACKLIGHT_CLASS_DEVICE && VIDEO_OUTPUT_CONTROL + depends on (X86 || (ARM || ARM64)) && \ + BACKLIGHT_CLASS_DEVICE && VIDEO_OUTPUT_CONTROL depends on INPUT select THERMAL help @@ -220,7 +223,7 @@ config ACPI_HOTPLUG_CPU config ACPI_PROCESSOR_AGGREGATOR tristate "Processor Aggregator" depends on ACPI_PROCESSOR - depends on X86 + depends on X86 || (ARM || ARM64) help ACPI 4.0 defines processor Aggregator, which enables OS to perform specific processor configuration and control that applies to all @@ -245,7 +248,7 @@ config ACPI_THERMAL config ACPI_NUMA bool "NUMA support" depends on NUMA - depends on (X86 || IA64) + depends on (X86 || IA64 || ARM64) default y if IA64_GENERIC || IA64_SGI_SN2
config ACPI_CUSTOM_DSDT_FILE @@ -309,6 +312,7 @@ config ACPI_DEBUG_FUNC_TRACE config ACPI_PCI_SLOT bool "PCI slot detection driver" depends on SYSFS + depends on PCI default n help This driver creates entries in /sys/bus/pci/slots/ for all PCI @@ -363,7 +367,7 @@ config ACPI_HOTPLUG_MEMORY
config ACPI_SBS tristate "Smart Battery System" - depends on X86 + depends on X86 || (ARM || ARM64) select POWER_SUPPLY help This driver supports the Smart Battery System, another diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 474fcfe..3dc16fb 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -9,7 +9,7 @@ ccflags-$(CONFIG_ACPI_DEBUG) += -DACPI_DEBUG_OUTPUT # ACPI Boot-Time Table Parsing # obj-y += tables.o -obj-$(CONFIG_X86) += blacklist.o +obj-$(CONFIG_ACPI) += blacklist.o
# # ACPI Core Subsystem (Interpreter) @@ -37,7 +37,7 @@ acpi-y += resource.o acpi-y += processor_core.o acpi-y += ec.o acpi-$(CONFIG_ACPI_DOCK) += dock.o -acpi-y += pci_root.o pci_link.o pci_irq.o +acpi-$(CONFIG_PCI) += pci_root.o pci_link.o pci_irq.o acpi-y += csrt.o acpi-y += acpi_platform.o acpi-y += power.o diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 586e7e9..7bc7655 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -242,6 +242,67 @@ static int __init setup_acpi_rsdp(char *arg) early_param("acpi_rsdp", setup_acpi_rsdp); #endif
+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) +#include <asm/byteorder.h> +#include <asm/acpi.h> +#include <acpi/actbl.h> + +void acpi_find_arm_root_pointer(acpi_physical_address *pa) +{ + /* BOZO: temporarily clunky. + * What we do is, while using u-boot still, is use the values + * that have already been retrieved from the FDT node + * (/chosen/linux,acpi-start and /chosen/linux,acpi-len) which + * contain the address of the first byte of the RSDP after it + * has been loaded into RAM during u-boot (e.g., using something + * like fatload mmc 0:2 42008000 my.blob), and the size of the + * data in the complete ACPI blob. We only do this since we have + * to collaborate with FDT so we have to load FDT and the ACPI + * tables in but only have one address we can use via bootm. + * With UEFI, we should just be able to use the efi_enabled + * branch below in acpi_os_get_root_pointer(). + */ + + u32 address; + struct acpi_table_rsdp *rp; + + if (!acpi_arm_rsdp_info.phys_address && !acpi_arm_rsdp_info.size) { + printk(KERN_ERR "(E) ACPI: failed to find rsdp info\n"); + *pa = (acpi_physical_address)NULL; + return; + } + + address = (u32)(phys_to_virt(acpi_arm_rsdp_info.phys_address)); + address += ACPI_BLOB_HEADER_SIZE; + *pa = (acpi_physical_address)address; + + rp = (struct acpi_table_rsdp *)address; + printk(KERN_DEBUG "(I) ACPI rsdp rp: 0x%08lx\n", (long unsigned int)rp); + if (rp) { + printk(KERN_DEBUG "(I) ACPI rsdp content:\n"); + printk(KERN_DEBUG "(I) signature: %.8s\n", rp->signature); + printk(KERN_DEBUG "(I) checksum: 0x%02x\n", rp->checksum); + printk(KERN_DEBUG "(I) oem_id: %.6s\n", rp->oem_id); + printk(KERN_DEBUG "(I) revision: %d\n", rp->revision); + printk(KERN_DEBUG "(I) rsdt: 0x%08lX\n", + (long unsigned int)rp->rsdt_physical_address); + printk(KERN_DEBUG "(I) length: %d\n", rp->length); + printk(KERN_DEBUG "(I) xsdt: 0x%016llX\n", + (u64)rp->xsdt_physical_address); + printk(KERN_DEBUG "(I) x_checksum: 0x%02x\n", + rp->extended_checksum); + + *pa = (acpi_physical_address)(virt_to_phys(rp)); + } + else { + printk(KERN_ERR "(E) ACPI missing rsdp info\n"); + *pa = (acpi_physical_address)NULL; + } + + return; +} +#endif + acpi_physical_address __init acpi_os_get_root_pointer(void) { #ifdef CONFIG_KEXEC @@ -262,7 +323,11 @@ acpi_physical_address __init acpi_os_get_root_pointer(void) } else { acpi_physical_address pa = 0;
+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) + acpi_find_arm_root_pointer(&pa); +#else acpi_find_root_pointer(&pa); +#endif return pa; } } @@ -1030,9 +1095,16 @@ acpi_os_read_pci_configuration(struct acpi_pci_id * pci_id, u32 reg, return AE_ERROR; }
+#ifdef CONFIG_X86 + /* BOZO: probably should not call this function at all + * if there is no PCI... */ result = raw_pci_read(pci_id->segment, pci_id->bus, PCI_DEVFN(pci_id->device, pci_id->function), reg, size, &value32); +#else + result = 0; + value32 = 0; +#endif *value = value32;
return (result ? AE_ERROR : AE_OK); @@ -1058,9 +1130,14 @@ acpi_os_write_pci_configuration(struct acpi_pci_id * pci_id, u32 reg, return AE_ERROR; }
+#ifdef CONFIG_X86 + /* BOZO: how do we handle not having PCI? */ result = raw_pci_write(pci_id->segment, pci_id->bus, PCI_DEVFN(pci_id->device, pci_id->function), reg, size, value); +#else + result = 0; +#endif
return (result ? AE_ERROR : AE_OK); } diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index 164d495..1d9033d 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -90,6 +90,22 @@ static int map_lsapic_id(struct acpi_subtable_header *entry, return 1; }
+static int map_gic_id(struct acpi_subtable_header *entry, + u32 acpi_id, int *apic_id) +{ + struct acpi_madt_generic_interrupt *gic = + (struct acpi_madt_generic_interrupt *)entry; + + if (!(gic->flags & ACPI_MADT_ENABLED)) + return 0; + + if (gic->uid != acpi_id) + return 0; + + *apic_id = gic->gic_id; + return 1; +} + static int map_madt_entry(int type, u32 acpi_id) { unsigned long madt_end, entry; @@ -125,7 +141,10 @@ static int map_madt_entry(int type, u32 acpi_id) } else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC) { if (map_lsapic_id(header, type, acpi_id, &apic_id)) break; - } + } else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) { + if (map_gic_id(header, acpi_id, &apic_id)) + break; + } entry += header->length; } return apic_id; @@ -155,6 +174,8 @@ static int map_mat_entry(acpi_handle handle, int type, u32 acpi_id) map_lapic_id(header, acpi_id, &apic_id); } else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC) { map_lsapic_id(header, type, acpi_id, &apic_id); + } else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) { + map_gic_id(header, acpi_id, &apic_id); }
exit: @@ -199,6 +220,16 @@ int acpi_get_cpuid(acpi_handle handle, int type, u32 acpi_id) return apic_id; }
+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) + return apic_id; + /* + * BOZO: need to abstract this out to have it make sense -- + * it's not that ARM has no equivalent, it's that apic_id is + * arch-specific + */ + +#else + #ifdef CONFIG_SMP for_each_possible_cpu(i) { if (cpu_physical_id(i) == apic_id) @@ -209,6 +240,9 @@ int acpi_get_cpuid(acpi_handle handle, int type, u32 acpi_id) if (apic_id == 0) return apic_id; #endif + +#endif + return -1; } EXPORT_SYMBOL_GPL(acpi_get_cpuid); diff --git a/drivers/acpi/reboot.c b/drivers/acpi/reboot.c index a6c77e8b..410d0be 100644 --- a/drivers/acpi/reboot.c +++ b/drivers/acpi/reboot.c @@ -6,9 +6,7 @@ void acpi_reboot(void) { struct acpi_generic_address *rr; - struct pci_bus *bus0; u8 reset_value; - unsigned int devfn;
if (acpi_disabled) return; @@ -31,7 +29,15 @@ void acpi_reboot(void) /* The reset register can only exist in I/O, Memory or PCI config space * on a device on bus 0. */ switch (rr->space_id) { +/* + * There are some rare cases in the ARM world with PCI is not one + * of the buses available to us, even though we use ACPI. + */ +#ifdef CONFIG_PCI case ACPI_ADR_SPACE_PCI_CONFIG: + struct pci_bus *bus0; + unsigned int devfn; + /* The reset register can only live on bus 0. */ bus0 = pci_find_bus(0, 0); if (!bus0) @@ -44,6 +50,7 @@ void acpi_reboot(void) pci_bus_write_config_byte(bus0, devfn, (rr->address & 0xffff), reset_value); break; +#endif
case ACPI_ADR_SPACE_SYSTEM_MEMORY: case ACPI_ADR_SPACE_SYSTEM_IO: diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 5e7e991..97b9227 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1785,8 +1785,10 @@ int __init acpi_scan_init(void) printk(KERN_ERR PREFIX "Could not register bus type\n"); }
+#if defined(CONFIG_PCI) acpi_pci_root_init(); acpi_pci_link_init(); +#endif acpi_platform_init(); acpi_csrt_init(); acpi_container_init(); @@ -1812,7 +1814,9 @@ int __init acpi_scan_init(void)
acpi_update_all_gpes();
+#if defined(CONFIG_PCI) acpi_pci_root_hp_init(); +#endif
out: mutex_unlock(&acpi_scan_lock); diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 808be06..728b158 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -696,6 +696,57 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, return 1; }
+#if (defined(CONFIG_ARM64) || defined (CONFIG_ARM)) && defined(CONFIG_ACPI) +#include <linux/memblock.h> +#include <linux/acpi.h> +#include <asm/acpi.h> +#include <acpi/actbl.h> + +int __init early_init_dt_scan_acpi(unsigned long node, const char *uname, + int depth, void *data) +{ + unsigned long l; + unsigned int *p; + struct acpi_arm_root *pinfo; + unsigned char *sig; + + pr_debug("search "chosen" for acpi info, depth: %d, uname: %s\n", + depth, uname); + + if (depth != 1 || !data || + (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0)) + return 0; + + /* Retrieve acpi,address line */ + pinfo = (struct acpi_arm_root *)data; + p = of_get_flat_dt_prop(node, "linux,acpi-start", &l); + if (p != NULL && l > 0) + pinfo->phys_address = be32_to_cpu(*p); + + /* Retrieve acpi,size line */ + p = of_get_flat_dt_prop(node, "linux,acpi-len", &l); + if (p != NULL && l > 0) + pinfo->size = be32_to_cpu(*p); + + printk(KERN_DEBUG "acpi: start info is 0x%08lX, %lu bytes\n", + pinfo->phys_address, pinfo->size); + + sig = phys_to_virt(pinfo->phys_address); + + printk(KERN_DEBUG "acpi: virtual address %p\n", sig); + return 1; + + printk(KERN_DEBUG "acpi: sig is "%c%c%c%c"\n", + sig[0], sig[1], sig[2], sig[3]); + printk(KERN_DEBUG "acpi: info is %02x %02x %02x %02x\n", + sig[4], sig[5], sig[6], sig[7]); + printk(KERN_DEBUG "acpi: first table is "%c%c%c%c"\n", + sig[8], sig[9], sig[10], sig[11]); + + return 1; +} +#endif + /** * unflatten_device_tree - create tree of device_nodes from flat blob * diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c index b8f4ea7..464f8c9 100644 --- a/drivers/pnp/pnpacpi/rsparser.c +++ b/drivers/pnp/pnpacpi/rsparser.c @@ -113,8 +113,10 @@ static int dma_flags(struct pnp_dev *dev, int type, int bus_master,
static void pnpacpi_add_irqresource(struct pnp_dev *dev, struct resource *r) { +#ifdef CONFIG_PCI if (!(r->flags & IORESOURCE_DISABLED)) pcibios_penalize_isa_irq(r->start, 1); +#endif
pnp_add_resource(dev, r); } diff --git a/include/acpi/pdc_arm64.h b/include/acpi/pdc_arm64.h new file mode 100644 index 0000000..ac8f197 --- /dev/null +++ b/include/acpi/pdc_arm64.h @@ -0,0 +1,39 @@ + +/* _PDC bit definition for ARMv7 processors */ + +#ifndef __PDC_ARM_H__ +#define __PDC_ARM_H__ + +/* BOZO: is this even necessary? */ + +/* _PDC bit definition for ARM processors */ + +#define ACPI_PDC_P_FFH (0x0001) +#define ACPI_PDC_C_C1_HALT (0x0002) +#define ACPI_PDC_T_FFH (0x0004) +#define ACPI_PDC_SMP_C1PT (0x0008) +#define ACPI_PDC_SMP_C2C3 (0x0010) +#define ACPI_PDC_SMP_P_SWCOORD (0x0020) +#define ACPI_PDC_SMP_C_SWCOORD (0x0040) +#define ACPI_PDC_SMP_T_SWCOORD (0x0080) +#define ACPI_PDC_C_C1_FFH (0x0100) +#define ACPI_PDC_C_C2C3_FFH (0x0200) +#define ACPI_PDC_SMP_P_HWCOORD (0x0800) + +#define ACPI_PDC_EST_CAPABILITY_SMP (ACPI_PDC_SMP_C1PT | \ + ACPI_PDC_C_C1_HALT | \ + ACPI_PDC_P_FFH) + +#define ACPI_PDC_EST_CAPABILITY_SWSMP (ACPI_PDC_SMP_C1PT | \ + ACPI_PDC_C_C1_HALT | \ + ACPI_PDC_SMP_P_SWCOORD | \ + ACPI_PDC_SMP_P_HWCOORD | \ + ACPI_PDC_P_FFH) + +#define ACPI_PDC_C_CAPABILITY_SMP (ACPI_PDC_SMP_C2C3 | \ + ACPI_PDC_SMP_C1PT | \ + ACPI_PDC_C_C1_HALT | \ + ACPI_PDC_C_C1_FFH | \ + ACPI_PDC_C_C2C3_FFH) + +#endif /* __PDC_ARM_H__ */ diff --git a/include/linux/acpi.h b/include/linux/acpi.h index bcbdd74..893670e 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -49,6 +49,7 @@ enum acpi_irq_model_id { ACPI_IRQ_MODEL_IOAPIC, ACPI_IRQ_MODEL_IOSAPIC, ACPI_IRQ_MODEL_PLATFORM, + ACPI_IRQ_MODEL_GIC, ACPI_IRQ_MODEL_COUNT };
diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index ed136ad..98c1029 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h @@ -93,6 +93,10 @@ extern unsigned long of_get_flat_dt_root(void);
extern int early_init_dt_scan_chosen(unsigned long node, const char *uname, int depth, void *data); +#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) +extern int early_init_dt_scan_acpi(unsigned long node, const char *uname, + int depth, void *data); +#endif extern void early_init_dt_check_for_initrd(unsigned long node); extern int early_init_dt_scan_memory(unsigned long node, const char *uname, int depth, void *data);