diff options
author | Gary King <gking@nvidia.com> | 2009-12-07 16:18:05 -0800 |
---|---|---|
committer | Gary King <gking@nvidia.com> | 2009-12-07 16:18:05 -0800 |
commit | bb11d46b72a4a2b53f890faa970a380cf0084bf2 (patch) | |
tree | 2d9d2955e069fed69febbe567a835fcc48ec6ec6 | |
parent | e1b5e49debba7174e7b9c48195de8abfd54911dd (diff) | |
parent | 74ece4056571443eef30d4dff62180944b5a39d8 (diff) |
Merge commit 'arm/2.6.28-arm' into android-tegra-2.6.29
Conflicts:
MAINTAINERS
arch/arm/Kconfig
arch/arm/Makefile
arch/arm/boot/compressed/head.S
arch/arm/common/Makefile
arch/arm/configs/realview-smp_defconfig
arch/arm/configs/realview_defconfig
arch/arm/configs/versatile_defconfig
arch/arm/include/asm/elf.h
arch/arm/include/asm/uaccess.h
arch/arm/kernel/module.c
arch/arm/kernel/signal.c
arch/arm/mach-realview/Kconfig
arch/arm/mach-realview/Makefile
arch/arm/mach-realview/core.c
arch/arm/mach-realview/core.h
arch/arm/mach-realview/include/mach/board-pba8.h
arch/arm/mach-realview/include/mach/debug-macro.S
arch/arm/mach-realview/include/mach/hardware.h
arch/arm/mach-realview/include/mach/irqs.h
arch/arm/mach-realview/include/mach/memory.h
arch/arm/mach-realview/include/mach/uncompress.h
arch/arm/mach-realview/localtimer.c
arch/arm/mach-realview/platsmp.c
arch/arm/mach-realview/realview_eb.c
arch/arm/mach-realview/realview_pb1176.c
arch/arm/mach-realview/realview_pb11mp.c
arch/arm/mach-realview/realview_pba8.c
arch/arm/mm/Kconfig
arch/arm/mm/copypage-v6.c
arch/arm/mm/dma-mapping.c
arch/arm/mm/proc-v7.S
arch/arm/oprofile/op_model_mpcore.c
arch/arm/tools/mach-types
arch/arm/vfp/vfpmodule.c
drivers/mtd/maps/integrator-flash.c
drivers/net/smsc911x.c
drivers/net/smsc911x.h
218 files changed, 11199 insertions, 2407 deletions
@@ -190,8 +190,8 @@ SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ # Default value for CROSS_COMPILE is not to prefix executables # Note: Some architectures assign CROSS_COMPILE in their arch/*/Makefile export KBUILD_BUILDHOST := $(SUBARCH) -ARCH ?= $(SUBARCH) -CROSS_COMPILE ?= +ARCH ?= arm +CROSS_COMPILE ?= arm-none-linux-gnueabi- # Architecture as present in compile.h UTS_MACHINE := $(ARCH) @@ -523,8 +523,12 @@ all: vmlinux ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE KBUILD_CFLAGS += -Os else +ifdef CONFIG_CC_OPTIMIZE_FOR_DEBUG +KBUILD_CFLAGS += -O1 +else KBUILD_CFLAGS += -O2 endif +endif include $(srctree)/arch/$(SRCARCH)/Makefile diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 827196339893..b85246ee78da 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -49,10 +49,6 @@ config GENERIC_CLOCKEVENTS_BROADCAST depends on GENERIC_CLOCKEVENTS default y if SMP && !LOCAL_TIMERS -config MMU - bool - default y - config NO_IOPORT bool default n @@ -194,6 +190,13 @@ source "kernel/Kconfig.freezer" menu "System Type" +config MMU + bool "MMU-based Paged Memory Management Support" + default y + help + Select if you want MMU-based virtualised addressing space + support by paged memory management. If unsure, say 'Y'. + choice prompt "ARM system type" default ARCH_VERSATILE @@ -590,6 +593,19 @@ config ARCH_W90X900 Support for Nuvoton (Winbond logic dept.) ARM9 processor,You can login www.mcuos.com or www.nuvoton.com to know more. +config ARCH_MPS + bool "ARM Ltd. Microcontroller Prototyping System" + depends on !MMU + select ARM_AMBA + select HAVE_CLK + select COMMON_CLKDEV + select ICST307 + select GENERIC_TIME + select GENERIC_CLOCKEVENTS + help + This enables support for ARM Ltd. Microcontroller Prototyping + System platform. + endchoice source "arch/arm/mach-clps711x/Kconfig" @@ -676,6 +692,8 @@ source "arch/arm/mach-msm/Kconfig" source "arch/arm/mach-w90x900/Kconfig" +source "arch/arm/mach-mps/Kconfig" + # Definitions to make life easier config ARCH_ACORN bool @@ -706,6 +724,105 @@ if !MMU source "arch/arm/Kconfig-nommu" endif +config ARM_ERRATA_364296 + bool "Enable partial low interrupt latency mode for ARM1136" + depends on CPU_V6 && !SMP + default n + help + This options enables the workaround for the 364296 ARM1136 + r0pX errata (possible cache data corruption with + hit-under-miss enabled). It sets the undocumented bit 31 in + the auxiliary control register and the FI bit in the control + register, thus disabling hit-under-miss without putting the + processor into full low interrupt latency mode. ARM11MPCore + is not affected. + +config ARM_ERRATA_411920 + bool "Workaround for the global I cache invalidation on ARM1136" + depends on CPU_V6 && !SMP + default n + help + Invalidation of the Instruction Cache operation can + fail. This Erratum is present in 1136, 1156 and 1176. It + does not affect the MPCore. This option enables the ARM Ltd. + recommended workaround. + +config ARM_ERRATA_351422 + bool "Spinlocks using LDREX and STREX instructions can livelock" + depends on CPU_V6 && SMP + default n + help + According to the ARM11MPCore Erratum 351422 (r0p0), under + extremely rare conditions, in an MPCore node consisting of + at least 3 CPUs, two CPUs trying to perform a STREX to data + on the same shared cache line can enter a livelock + situation. This option adds variable spinning time to the + locking routines. + +config ARM_ERRATA_430973 + bool "Stale prediction on replaced interworking branch on Cortex-A8" + depends on CPU_V7 + default n + help + This option enables the workaround for the 430973 Cortex-A8 + (r1p0) erratum. If a code sequence containing an ARM/Thumb + interworking branch is replaced with another code sequence + at the same virtual address, whether due to self-modifying + code or virtual to physical address re-mapping, Cortex-A8 + does not recover from the stale interworking branch + prediction. This results in Cortex-A8 executing the new code + sequence in the incorrect ARM or Thumb state. + +config ARM_ERRATA_458693 + bool "Processor deadlock when a false hazard is created on Cortex-A8" + depends on CPU_V7 + default n + help + This option enables the workaround for the 458693 Cortex-A8 + (r2p0) erratum. For very specific sequences of memory + operations, it is possible for a hazard condition intended + for a cache line to instead be incorrectly associated with a + different cache line. This false hazard might then cause a + processor deadlock. + +config ARM_ERRATA_460075 + bool "Data written to the L2 cache can be overwritten with stale data on Cortex-A8" + depends on CPU_V7 + default n + help + This option enables the workaround for the 458692 Cortex-A8 + (r2p0) erratum. Any asynchronous access to the L2 cache may + encounter a situation in which recent store transactions to + the L2 cache are lost and overwritten with stale memory + contents from external memory. + +config ARM_ERRATUM_451034 + bool "Enable workaround for ARM erratum 451034" + depends on VFPv3 + help + On Cortex-A8 r1p0 and r1p1, executing a NEON store with an integer + store in the store buffer, can cause a processor deadlock under + certain conditions. + + See ARM Cortex-A8 Errata Notice (PR120-PRDC-008070) for full details. + + Say Y to include a partial workaround. + + WARNING: Even with this option enabled, userspace code can trigger + the deadlock. To safely run untrusted code, a different fix is + required. + +config ARM_ERRATA_484863 + bool "The Cache Sync operation does not guarantee that the the Eviction Buffer is empty" + depends on CACHE_L2X0 + default n + help + According to the L220 Erratum 484863, the actual behaviour of + the L220 cache controller is that the Cache Sync operation + only ensures that the Write Buffer and the Write Allocate + Buffer are empty but not the Eviction Buffer. This option + enables the first workaround from the Errata document. + endmenu source "arch/arm/common/Kconfig" @@ -773,7 +890,7 @@ source "kernel/time/Kconfig" config SMP bool "Symmetric Multi-Processing (EXPERIMENTAL)" - depends on EXPERIMENTAL && (REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP) + depends on EXPERIMENTAL && (REALVIEW_EB_ARM11MP || REALVIEW_EB_A9MP || MACH_REALVIEW_PB11MP || MACH_REALVIEW_PBX) select USE_GENERIC_SMP_HELPERS help This enables support for systems with more than one CPU. If you have @@ -830,7 +947,7 @@ config HOTPLUG_CPU config LOCAL_TIMERS bool "Use local timer interrupts" - depends on SMP && (REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || REALVIEW_EB_A9MP) + depends on SMP && (REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || REALVIEW_EB_A9MP || MACH_REALVIEW_PBX) default y help Enable support for local timers on SMP platforms, rather then the @@ -859,6 +976,23 @@ config HZ default AT91_TIMER_HZ if ARCH_AT91 default 100 +config THUMB2_KERNEL + bool "Compile the kernel in Thumb-2 mode" + depends on (CPU_V7 || CPU_V7M) && EXPERIMENTAL + default n + select AEABI + select ARM_ASM_UNIFIED + help + By enabling this option, the kernel will be compiled in + Thumb-2 mode. A compiler/assembler that understand the unified + ARM-Thumb syntax is needed. + + If unsure, say N. + +config ARM_ASM_UNIFIED + bool + default n + config AEABI bool "Use the ARM EABI to compile the kernel" help @@ -876,7 +1010,7 @@ config AEABI config OABI_COMPAT bool "Allow old ABI binaries to run with this kernel (EXPERIMENTAL)" - depends on AEABI && EXPERIMENTAL + depends on AEABI && !CPU_V7M && EXPERIMENTAL default y help This option preserves the old syscall interface along with the @@ -925,7 +1059,7 @@ config LEDS ARCH_OMAP || ARCH_P720T || ARCH_PXA_IDP || \ ARCH_SA1100 || ARCH_SHARK || ARCH_VERSATILE || \ ARCH_AT91 || ARCH_DAVINCI || \ - ARCH_KS8695 || MACH_RD88F5182 + ARCH_KS8695 || MACH_RD88F5182 || ARCH_REALVIEW help If you say Y here, the LEDs on your machine will be used to provide useful information about your current system status. @@ -1082,11 +1216,43 @@ config ATAGS_PROC Should the atags used to boot the kernel be exported in an "atags" file in procfs. Useful with kexec. +config NAKED_BOOT + bool "Kernel image for naked booting environments" + depends on CMDLINE != "" + default n + help + This produces a kernel binary that can simply be moved onto + the target machine and jumped to without any sort of boot + protocol from the bootloader. This should only be used in + cases where the bootloader fails to implement the required + calling convention for executing the kernel as described in + the file Documentation/arm/Booting. + + The use of this option imposes many restrictions on the kernel + as it may not support more than one target machine, and all + boot parameters such as memory size and root filesystem device + have to be hardcoded in the default kernel command string (see + CONFIG_CMDLINE). + + If dynamic ram configuration, multi-machine support or the + ability for the user to specify a kernel command string at + boot without the need for recompiling the kernel each time + is required, then the bootloader must implement the boot + protocol as described in Documentation/arm/Booting and this + config option not be used. + + Please consider fixing your bootloader according to the + documented boot protocol in all cases, or choose one + from the many excellent open source bootloaders available + on the net which already support the required boot protocol. + + If you're not sure what this is all about then say N. + endmenu menu "CPU Power Management" -if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_IMX || ARCH_PXA) +if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_IMX || ARCH_PXA || ARCH_REALVIEW) source "drivers/cpufreq/Kconfig" @@ -1126,6 +1292,15 @@ config CPU_FREQ_PXA default y select CPU_FREQ_DEFAULT_GOV_USERSPACE +config CPU_FREQ_REALVIEW + tristate "CPUfreq driver for ARM RealView platform" + depends on CPU_FREQ && MACH_REALVIEW_PB1176 + default y + help + This enables the CPUfreq driver for ARM RealView platform. + + If in doubt, say Y. + endif source "drivers/cpuidle/Kconfig" diff --git a/arch/arm/Kconfig-nommu b/arch/arm/Kconfig-nommu index 901e6dff8437..90521b3fa5e3 100644 --- a/arch/arm/Kconfig-nommu +++ b/arch/arm/Kconfig-nommu @@ -28,7 +28,7 @@ config FLASH_SIZE config PROCESSOR_ID hex 'Hard wire the processor ID' default 0x00007700 - depends on !CPU_CP15 + depends on !(CPU_CP15 || CPU_V7M) help If processor has no CP15 register, this processor ID is used instead of the auto-probing which utilizes the register. diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug index 192ee01a9ba2..f34b02fa20cc 100644 --- a/arch/arm/Kconfig.debug +++ b/arch/arm/Kconfig.debug @@ -2,18 +2,29 @@ menu "Kernel hacking" source "lib/Kconfig.debug" -# RMK wants arm kernels compiled with frame pointers so hardwire this to y. +# RMK wants arm kernels compiled with frame pointers or stack unwinding. # If you know what you are doing and are willing to live without stack # traces, you can get a slightly smaller kernel by setting this option to # n, but then RMK will have to kill you ;). config FRAME_POINTER bool - default y + default y if !ARM_UNWIND && !THUMB2_KERNEL help If you say N here, the resulting kernel will be slightly smaller and - faster. However, when a problem occurs with the kernel, the - information that is reported is severely limited. Most people - should say Y here. + faster. However, if neither FRAME_POINTER nor ARM_UNWIND are enabled, + when a problem occurs with the kernel, the information that is + reported is severely limited. + +config ARM_UNWIND + bool "Enable stack unwinding support" + depends on AEABI && EXPERIMENTAL + default y + help + This option enables stack unwinding support in the kernel + using the information automatically generated by the + compiler. The resulting kernel image is slightly bigger but + the performance is not affected. Currently, this feature + only works with EABI compilers. If unsure say Y. config DEBUG_USER bool "Verbose user fault messages" @@ -50,6 +61,57 @@ config DEBUG_STACK_USAGE Enables the display of the minimum amount of free stack which each task has ever had available in the sysrq-T output. +config DEBUG_RVIDCC + bool "Include the RealView ICE DCC channel device driver" + depends on !DEBUG_ICEDCC + help + Say Y here if you want a serial channel connected through ARM's + RealView ICE emulator through to your debugging host. + + IMPORTANT: if you are not using one of the pre-configured platforms + (notably ARM's Integrator or Versatile Platform) then you will need to + configure interrupts for DCC and pass the IRQ numbers in the module + parameters 'txirq' and 'rxirq', assuming they have been wired into your + interrupt controller. + + If interrupts are not used then a timed polling scheme is used. This + introduces poorer performance, using more CPU bandwidth and reducing + communication throughput on DCC. + + This feature requires at least the RealView ICE 1.5 firmware version. + +config DEBUG_DCC_KGDB + bool "Use the RealView ICE DCC serial channel for KGDB debugging" + depends on DEBUG_RVIDCC && KGDB + help + Say Y here if you want to remotely debug the kernel using gdb and the + KGDB kernel debugging patches. Refer to the KGDB documentation for more + details. + + Note that only one device can be enabled as the KGDB communications + device, so if this is selected, then KGDB on serial devices or Ethernet + must be deselected, or the kernel won't link. + +config DEBUG_DCC_RAW + bool "Disable virtual Ethernet on the RealView ICE DCC channel" + depends on DEBUG_RVIDCC + help + Say Y here if you want to disable the virtual Ethernet facility + of the RealView ICE. + + The virtual Ethernet facility provides both a serial channel and an + Ethernet channel, and can be used for its serial channel even if + Ethernet is not required. Using this has the advantage over raw + DCC communications in that serial channel writes that do not have + an integral multiple of 4 bytes are not stuffed with nulls to make + the packet size. + + DCC communicates in 32-bit words and, in raw mode, data is packed in + 4 bytes per word. Individual write calls pack the data provided, padding + any remainder with nulls. Any protocol using this channel must tolerate + the embedded nulls. GDB is such a protocol, provided the driver is fed with + complete packets, and not one byte at a time. + # These options are only for real kernel hackers who want to get their hands dirty. config DEBUG_LL bool "Kernel low-level debugging functions" @@ -59,6 +121,14 @@ config DEBUG_LL in the kernel. This is helpful if you are debugging code that executes before the console is initialized. +config DEBUG_LL_CONSOLE + bool "Kernel early console" + depends on DEBUG_LL + help + Say Y here if you want to have an early console using the Kernel + low-level debugging functions. Add earlyprintk to your kernel + parameters to enable this console. + config DEBUG_ICEDCC bool "Kernel low-level debugging via EmbeddedICE DCC channel" depends on DEBUG_LL diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 24e0f0187697..9810f6885bff 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -11,6 +11,9 @@ # Copyright (C) 1995-2001 by Russell King LDFLAGS_vmlinux :=-p --no-undefined -X +ifeq ($(CONFIG_CPU_ENDIAN_BE8),y) +LDFLAGS_vmlinux += --be8 +endif CPPFLAGS_vmlinux.lds = -DTEXT_OFFSET=$(TEXT_OFFSET) OBJCOPYFLAGS :=-O binary -R .note -R .note.gnu.build-id -R .comment -S GZFLAGS :=-9 @@ -47,6 +50,7 @@ comma = , # Note that GCC does not numerically define an architecture version # macro, but instead defines a whole series of macros which makes # testing for a specific architecture or later rather impossible. +arch-$(CONFIG_CPU_32v7M) :=-D__LINUX_ARM_ARCH__=7 -march=armv7-m -Wa,-march=armv7-m arch-$(CONFIG_CPU_32v7) :=-D__LINUX_ARM_ARCH__=7 $(call cc-option,-march=armv7-a,-march=armv5t -Wa$(comma)-march=armv7-a) arch-$(CONFIG_CPU_32v6) :=-D__LINUX_ARM_ARCH__=6 $(call cc-option,-march=armv6,-march=armv5t -Wa$(comma)-march=armv6) # Only override the compiler option if ARMv6. The ARMv6K extensions are @@ -85,9 +89,19 @@ else CFLAGS_ABI :=$(call cc-option,-mapcs-32,-mabi=apcs-gnu) $(call cc-option,-mno-thumb-interwork,) endif +ifeq ($(CONFIG_ARM_UNWIND),y) +CFLAGS_ABI +=-funwind-tables +endif + +ifeq ($(CONFIG_THUMB2_KERNEL),y) +AFLAGS_NOWARN :=$(call as-option,-Wa$(comma)-mno-warn-deprecated,-Wa$(comma)-W) +CFLAGS_THUMB2 :=-mthumb $(AFLAGS_NOWARN) +AFLAGS_THUMB2 :=$(CFLAGS_THUMB2) -Wa$(comma)-mthumb +endif + # Need -Uarm for gcc < 3.x -KBUILD_CFLAGS +=$(CFLAGS_ABI) $(arch-y) $(tune-y) $(call cc-option,-mshort-load-bytes,$(call cc-option,-malignment-traps,)) -msoft-float -Uarm -KBUILD_AFLAGS +=$(CFLAGS_ABI) $(arch-y) $(tune-y) -msoft-float +KBUILD_CFLAGS +=$(CFLAGS_ABI) $(CFLAGS_THUMB2) $(arch-y) $(tune-y) $(call cc-option,-mshort-load-bytes,$(call cc-option,-malignment-traps,)) -msoft-float -Uarm +KBUILD_AFLAGS +=$(CFLAGS_ABI) $(AFLAGS_THUMB2) $(arch-y) $(tune-y) -include asm/unified.h -msoft-float CHECKFLAGS += -D__arm__ @@ -149,6 +163,7 @@ endif machine-$(CONFIG_ARCH_LOKI) := loki machine-$(CONFIG_ARCH_MV78XX0) := mv78xx0 machine-$(CONFIG_ARCH_W90X900) := w90x900 + machine-$(CONFIG_ARCH_MPS) := mps ifeq ($(CONFIG_ARCH_EBSA110),y) # This is what happens if you forget the IOCS16 line. diff --git a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile index da226abce2d0..b2b146d39cec 100644 --- a/arch/arm/boot/Makefile +++ b/arch/arm/boot/Makefile @@ -61,7 +61,7 @@ endif quiet_cmd_uimage = UIMAGE $@ cmd_uimage = $(CONFIG_SHELL) $(MKIMAGE) -A arm -O linux -T kernel \ - -C none -a $(LOADADDR) -e $(LOADADDR) \ + -C none -a $(LOADADDR) -e $(STARTADDR) \ -n 'Linux-$(KERNELRELEASE)' -d $< $@ ifeq ($(CONFIG_ZBOOT_ROM),y) @@ -70,6 +70,13 @@ else $(obj)/uImage: LOADADDR=$(ZRELADDR) endif +ifeq ($(CONFIG_THUMB2_KERNEL),y) +# Set bit 0 to 1 so that "mov pc, lr" switches to Thumb-2 mode +$(obj)/uImage: STARTADDR=$(shell echo $(LOADADDR) | sed -e "s/.$$/1/") +else +$(obj)/uImage: STARTADDR=$(LOADADDR) +endif + $(obj)/uImage: $(obj)/zImage FORCE $(call if_changed,uimage) @echo ' Image $@ is ready' diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile index fbe5eef1f6c9..0234bf9bb1fc 100644 --- a/arch/arm/boot/compressed/Makefile +++ b/arch/arm/boot/compressed/Makefile @@ -40,7 +40,7 @@ ifeq ($(CONFIG_PXA_SHARPSL),y) OBJS += head-sharpsl.o endif -ifeq ($(CONFIG_CPU_BIG_ENDIAN),y) +ifeq ($(CONFIG_CPU_ENDIAN_BE32),y) ifeq ($(CONFIG_CPU_CP15),y) OBJS += big-endian.o else @@ -57,7 +57,7 @@ ifeq ($(CONFIG_ZBOOT_ROM),y) ZTEXTADDR := $(CONFIG_ZBOOT_ROM_TEXT) ZBSSADDR := $(CONFIG_ZBOOT_ROM_BSS) else -ZTEXTADDR := 0 +ZTEXTADDR := $(ZRELADDR) ZBSSADDR := ALIGN(4) endif @@ -72,12 +72,17 @@ KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS)) endif EXTRA_CFLAGS := -fpic -fno-builtin +ifneq ($(CONFIG_CPU_V7M),y) EXTRA_AFLAGS := -Wa,-march=all +endif # Supply ZRELADDR, INITRD_PHYS and PARAMS_PHYS to the decompressor via # linker symbols. We only define initrd_phys and params_phys if the # machine class defined the corresponding makefile variable. LDFLAGS_vmlinux := --defsym zreladdr=$(ZRELADDR) +ifeq ($(CONFIG_CPU_ENDIAN_BE8),y) +LDFLAGS_vmlinux += --be8 +endif ifneq ($(INITRD_PHYS),) LDFLAGS_vmlinux += --defsym initrd_phys=$(INITRD_PHYS) endif diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S index 77d614232d81..52954f91ee98 100644 --- a/arch/arm/boot/compressed/head.S +++ b/arch/arm/boot/compressed/head.S @@ -117,6 +117,12 @@ start: mov r0, r0 .endr +#ifdef CONFIG_NAKED_BOOT +#include <mach/debug-macro.S> + addruart r7 + inituart r8, r7 +#endif + b 1f .word 0x016f2818 @ Magic numbers to help the loader .word start @ absolute load/run zImage address @@ -124,6 +130,7 @@ start: 1: mov r7, r1 @ save architecture ID mov r8, r2 @ save atags pointer +#ifndef CONFIG_CPU_V7M #ifndef __ARM_ARCH_2__ /* * Booting from Angel - need to enter SVC mode and disable @@ -134,7 +141,8 @@ start: tst r2, #3 @ not user? bne not_angel mov r0, #0x17 @ angel_SWIreason_EnterSVC - swi 0x123456 @ angel_SWI_ARM + ARM( swi 0x123456 ) @ angel_SWI_ARM + THUMB( svc 0xab ) @ angel_SWI_THUMB not_angel: mrs r2, cpsr @ turn off interrupts to orr r2, r2, #0xc0 @ prevent angel from running @@ -142,6 +150,7 @@ not_angel: #else teqp pc, #0x0c000003 @ turn off interrupts #endif +#endif /* * Note that some cache flushing and other stuff may @@ -155,7 +164,9 @@ not_angel: .text adr r0, LC0 - ldmia r0, {r1, r2, r3, r4, r5, r6, ip, sp} + ARM( ldmia r0, {r1, r2, r3, r4, r5, r6, ip, sp} ) + THUMB( ldmia r0, {r1, r2, r3, r4, r5, r6, ip} ) + THUMB( ldr sp, [r0, #28] ) subs r0, r0, r1 @ calculate the delta offset @ if delta is zero, we are @@ -257,22 +268,25 @@ not_relocated: mov r0, #0 * r6 = processor ID * r7 = architecture ID * r8 = atags pointer - * r9-r14 = corrupted + * r9-r12,r14 = corrupted */ add r1, r5, r0 @ end of decompressed kernel adr r2, reloc_start ldr r3, LC1 add r3, r2, r3 -1: ldmia r2!, {r9 - r14} @ copy relocation code - stmia r1!, {r9 - r14} - ldmia r2!, {r9 - r14} - stmia r1!, {r9 - r14} +1: ldmia r2!, {r9 - r12, r14} @ copy relocation code + stmia r1!, {r9 - r12, r14} + ldmia r2!, {r9 - r12, r14} + stmia r1!, {r9 - r12, r14} cmp r2, r3 blo 1b - add sp, r1, #128 @ relocate the stack + mov sp, r1 + add sp, sp, #128 @ relocate the stack bl cache_clean_flush - add pc, r5, r0 @ call relocation code + ARM( add pc, r5, r0 ) @ call relocation code + THUMB( add r12, r5, r0 ) + THUMB( mov pc, r12 ) @ call relocation code /* * We're not in danger of overwriting ourselves. Do this the simple way. @@ -285,6 +299,7 @@ wont_overwrite: mov r0, r4 bl decompress_kernel b call_kernel + .align 2 .type LC0, #object LC0: .word LC0 @ r1 .word __bss_start @ r2 @@ -399,8 +414,10 @@ __setup_mmu: sub r3, r4, #16384 @ Page directory size orr r1, r1, #3 << 10 add r2, r3, #16384 1: cmp r1, r9 @ if virt > start of RAM + it hs orrhs r1, r1, #0x0c @ set cacheable, bufferable cmp r1, r10 @ if virt > end of RAM + it hs bichs r1, r1, #0x0c @ clear cacheable, bufferable str r1, [r0], #4 @ 1:1 mapping add r1, r1, #1048576 @@ -425,6 +442,7 @@ ENDPROC(__setup_mmu) __armv4_mmu_cache_on: mov r12, lr +#ifdef CONFIG_MMU bl __setup_mmu mov r0, #0 mcr p15, 0, r0, c7, c10, 4 @ drain write buffer @@ -432,27 +450,41 @@ __armv4_mmu_cache_on: mrc p15, 0, r0, c1, c0, 0 @ read control reg orr r0, r0, #0x5000 @ I-cache enable, RR cache replacement orr r0, r0, #0x0030 +#ifdef CONFIG_CPU_ENDIAN_BE8 + orr r0, r0, #1 << 25 @ big-endian page tables +#endif bl __common_mmu_cache_on mov r0, #0 mcr p15, 0, r0, c8, c7, 0 @ flush I,D TLBs +#endif mov pc, r12 __armv7_mmu_cache_on: mov r12, lr +#ifdef CONFIG_MMU mrc p15, 0, r11, c0, c1, 4 @ read ID_MMFR0 tst r11, #0xf @ VMSA + it ne blne __setup_mmu mov r0, #0 mcr p15, 0, r0, c7, c10, 4 @ drain write buffer tst r11, #0xf @ VMSA + it ne mcrne p15, 0, r0, c8, c7, 0 @ flush I,D TLBs +#endif mrc p15, 0, r0, c1, c0, 0 @ read control reg orr r0, r0, #0x5000 @ I-cache enable, RR cache replacement orr r0, r0, #0x003c @ write buffer +#ifdef CONFIG_CPU_ENDIAN_BE8 + orr r0, r0, #1 << 25 @ big-endian page tables +#endif +#ifdef CONFIG_MMU + itttt ne orrne r0, r0, #1 @ MMU enabled movne r1, #-1 mcrne p15, 0, r3, c2, c0, 0 @ load page table pointer mcrne p15, 0, r1, c3, c0, 0 @ load domain access control +#endif mcr p15, 0, r0, c1, c0, 0 @ load control register mrc p15, 0, r0, c1, c0, 0 @ and read it back mov r0, #0 @@ -472,6 +504,7 @@ __arm6_mmu_cache_on: mov pc, r12 __common_mmu_cache_on: +#ifndef CONFIG_THUMB2_KERNEL #ifndef DEBUG orr r0, r0, #0x000d @ Write buffer, mmu #endif @@ -483,6 +516,7 @@ __common_mmu_cache_on: 1: mcr p15, 0, r0, c1, c0, 0 @ load control register mrc p15, 0, r0, c1, c0, 0 @ and read it back to sub pc, lr, r0, lsr #32 @ properly flush pipeline +#endif /* * All code following this line is relocatable. It is relocated by @@ -496,7 +530,7 @@ __common_mmu_cache_on: * r6 = processor ID * r7 = architecture ID * r8 = atags pointer - * r9-r14 = corrupted + * r9-r12,r14 = corrupted */ .align 5 reloc_start: add r9, r5, r0 @@ -505,13 +539,14 @@ reloc_start: add r9, r5, r0 mov r1, r4 1: .rept 4 - ldmia r5!, {r0, r2, r3, r10 - r14} @ relocate kernel - stmia r1!, {r0, r2, r3, r10 - r14} + ldmia r5!, {r0, r2, r3, r10 - r12, r14} @ relocate kernel + stmia r1!, {r0, r2, r3, r10 - r12, r14} .endr cmp r5, r9 blo 1b - add sp, r1, #128 @ relocate the stack + mov sp, r1 + add sp, sp, #128 @ relocate the stack debug_reloc_end call_kernel: bl cache_clean_flush @@ -535,17 +570,24 @@ call_kernel: bl cache_clean_flush * r12 = corrupted */ -call_cache_fn: adr r12, proc_types +call_cache_fn: + adr r12, proc_types #ifdef CONFIG_CPU_CP15 mrc p15, 0, r6, c0, c0 @ get processor ID +#elif defined(CONFIG_CPU_V7M) + ldr r6, =0xe000ed00 @ CPUID register address + ldr r6, [r6] #else ldr r6, =CONFIG_PROCESSOR_ID #endif -1: ldr r1, [r12, #0] @ get value - ldr r2, [r12, #4] @ get mask - eor r1, r1, r6 @ (real ^ match) - tst r1, r2 @ & mask - addeq pc, r12, r3 @ call cache function +1: ldr r2, [r12, #4] @ get mask + ldr r1, [r12, #0] @ get value + and r2, r6, r2 @ keep the relevant bits + teq r1, r2 @ match with the value + itt eq + ARM( addeq pc, r12, r3 ) @ call cache function + THUMB( addeq r12, r3 ) + THUMB( moveq pc, r12 ) @ call cache function add r12, r12, #4*5 b 1b @@ -563,13 +605,15 @@ call_cache_fn: adr r12, proc_types * methods. Writeback caches _must_ have the flush method * defined. */ + .align 2 .type proc_types,#object proc_types: .word 0x41560600 @ ARM6/610 .word 0xffffffe0 - b __arm6_mmu_cache_off @ works, but slow - b __arm6_mmu_cache_off + W(b) __arm6_mmu_cache_off @ works, but slow + W(b) __arm6_mmu_cache_off mov pc, lr + THUMB( nop ) @ b __arm6_mmu_cache_on @ untested @ b __arm6_mmu_cache_off @ b __armv3_mmu_cache_flush @@ -577,52 +621,60 @@ proc_types: .word 0x00000000 @ old ARM ID .word 0x0000f000 mov pc, lr + THUMB( nop ) mov pc, lr + THUMB( nop ) mov pc, lr + THUMB( nop ) .word 0x41007000 @ ARM7/710 .word 0xfff8fe00 - b __arm7_mmu_cache_off - b __arm7_mmu_cache_off + W(b) __arm7_mmu_cache_off + W(b) __arm7_mmu_cache_off mov pc, lr + THUMB( nop ) .word 0x41807200 @ ARM720T (writethrough) .word 0xffffff00 - b __armv4_mmu_cache_on - b __armv4_mmu_cache_off + W(b) __armv4_mmu_cache_on + W(b) __armv4_mmu_cache_off mov pc, lr + THUMB( nop ) .word 0x41007400 @ ARM74x .word 0xff00ff00 - b __armv3_mpu_cache_on - b __armv3_mpu_cache_off - b __armv3_mpu_cache_flush + W(b) __armv3_mpu_cache_on + W(b) __armv3_mpu_cache_off + W(b) __armv3_mpu_cache_flush .word 0x41009400 @ ARM94x .word 0xff00ff00 - b __armv4_mpu_cache_on - b __armv4_mpu_cache_off - b __armv4_mpu_cache_flush + W(b) __armv4_mpu_cache_on + W(b) __armv4_mpu_cache_off + W(b) __armv4_mpu_cache_flush .word 0x00007000 @ ARM7 IDs .word 0x0000f000 mov pc, lr + THUMB( nop ) mov pc, lr + THUMB( nop ) mov pc, lr + THUMB( nop ) @ Everything from here on will be the new ID system. .word 0x4401a100 @ sa110 / sa1100 .word 0xffffffe0 - b __armv4_mmu_cache_on - b __armv4_mmu_cache_off - b __armv4_mmu_cache_flush + W(b) __armv4_mmu_cache_on + W(b) __armv4_mmu_cache_off + W(b) __armv4_mmu_cache_flush .word 0x6901b110 @ sa1110 .word 0xfffffff0 - b __armv4_mmu_cache_on - b __armv4_mmu_cache_off - b __armv4_mmu_cache_flush + W(b) __armv4_mmu_cache_on + W(b) __armv4_mmu_cache_off + W(b) __armv4_mmu_cache_flush .word 0x56056930 .word 0xff0ffff0 @ PXA935 @@ -632,47 +684,52 @@ proc_types: .word 0x56050000 @ Feroceon .word 0xff0f0000 - b __armv4_mmu_cache_on - b __armv4_mmu_cache_off - b __armv5tej_mmu_cache_flush + W(b) __armv4_mmu_cache_on + W(b) __armv4_mmu_cache_off + W(b) __armv5tej_mmu_cache_flush @ These match on the architecture ID .word 0x00020000 @ ARMv4T .word 0x000f0000 - b __armv4_mmu_cache_on - b __armv4_mmu_cache_off - b __armv4_mmu_cache_flush + W(b) __armv4_mmu_cache_on + W(b) __armv4_mmu_cache_off + W(b) __armv4_mmu_cache_flush .word 0x00050000 @ ARMv5TE .word 0x000f0000 - b __armv4_mmu_cache_on - b __armv4_mmu_cache_off - b __armv4_mmu_cache_flush + W(b) __armv4_mmu_cache_on + W(b) __armv4_mmu_cache_off + W(b) __armv4_mmu_cache_flush .word 0x00060000 @ ARMv5TEJ .word 0x000f0000 - b __armv4_mmu_cache_on - b __armv4_mmu_cache_off - b __armv5tej_mmu_cache_flush + W(b) __armv4_mmu_cache_on + W(b) __armv4_mmu_cache_off + W(b) __armv4_mmu_cache_flush .word 0x0007b000 @ ARMv6 .word 0x000ff000 - b __armv4_mmu_cache_on - b __armv4_mmu_cache_off - b __armv6_mmu_cache_flush + W(b) __armv4_mmu_cache_on + W(b) __armv4_mmu_cache_off + W(b) __armv6_mmu_cache_flush +#ifndef CONFIG_CPU_V7M .word 0x000f0000 @ new CPU Id .word 0x000f0000 - b __armv7_mmu_cache_on - b __armv7_mmu_cache_off - b __armv7_mmu_cache_flush + W(b) __armv7_mmu_cache_on + W(b) __armv7_mmu_cache_off + W(b) __armv7_mmu_cache_flush +#endif .word 0 @ unrecognised type .word 0 mov pc, lr + THUMB( nop ) mov pc, lr + THUMB( nop ) mov pc, lr + THUMB( nop ) .size proc_types, . - proc_types @@ -707,22 +764,30 @@ __armv3_mpu_cache_off: mov pc, lr __armv4_mmu_cache_off: +#ifdef CONFIG_MMU mrc p15, 0, r0, c1, c0 bic r0, r0, #0x000d mcr p15, 0, r0, c1, c0 @ turn MMU and cache off mov r0, #0 mcr p15, 0, r0, c7, c7 @ invalidate whole cache v4 mcr p15, 0, r0, c8, c7 @ invalidate whole TLB v4 +#endif mov pc, lr __armv7_mmu_cache_off: mrc p15, 0, r0, c1, c0 +#ifdef CONFIG_MMU bic r0, r0, #0x000d +#else + bic r0, r0, #0x000c +#endif mcr p15, 0, r0, c1, c0 @ turn MMU and cache off mov r12, lr bl __armv7_mmu_cache_flush mov r0, #0 +#ifdef CONFIG_MMU mcr p15, 0, r0, c8, c7, 0 @ invalidate whole TLB +#endif mcr p15, 0, r0, c7, c5, 6 @ invalidate BTC mcr p15, 0, r0, c7, c10, 4 @ DSB mcr p15, 0, r0, c7, c5, 4 @ ISB @@ -771,6 +836,7 @@ __armv4_mpu_cache_flush: bcs 1b @ segments 7 to 0 teq r2, #0 + it ne mcrne p15, 0, ip, c7, c5, 0 @ invalidate I cache mcr p15, 0, ip, c7, c10, 4 @ drain WB mov pc, lr @@ -793,7 +859,7 @@ __armv7_mmu_cache_flush: b iflush hierarchical: mcr p15, 0, r10, c7, c10, 5 @ DMB - stmfd sp!, {r0-r5, r7, r9, r11} + stmfd sp!, {r0-r7, r9-r11} mrc p15, 1, r0, c0, c0, 1 @ read clidr ands r3, r0, #0x7000000 @ extract loc from clidr mov r3, r3, lsr #23 @ left align loc bit field @@ -818,8 +884,12 @@ loop1: loop2: mov r9, r4 @ create working copy of max way size loop3: - orr r11, r10, r9, lsl r5 @ factor way and cache number into r11 - orr r11, r11, r7, lsl r2 @ factor index number into r11 + ARM( orr r11, r10, r9, lsl r5 ) @ factor way and cache number into r11 + ARM( orr r11, r11, r7, lsl r2 ) @ factor index number into r11 + THUMB( lsl r6, r9, r5 ) + THUMB( orr r11, r10, r6 ) @ factor way and cache number into r11 + THUMB( lsl r6, r7, r2 ) + THUMB( orr r11, r11, r6 ) @ factor index number into r11 mcr p15, 0, r11, c7, c14, 2 @ clean & invalidate by set/way subs r9, r9, #1 @ decrement the way bge loop3 @@ -830,7 +900,7 @@ skip: cmp r3, r10 bgt loop1 finished: - ldmfd sp!, {r0-r5, r7, r9, r11} + ldmfd sp!, {r0-r7, r9-r11} mov r10, #0 @ swith back to cache level 0 mcr p15, 2, r10, c0, c0, 0 @ select current cache level in cssr iflush: @@ -858,15 +928,20 @@ __armv4_mmu_cache_flush: mov r2, #1024 mov r2, r2, lsl r1 @ base dcache size *2 tst r3, #1 << 14 @ test M bit + it ne addne r2, r2, r2, lsr #1 @ +1/2 size if M == 1 mov r3, r3, lsr #12 and r3, r3, #3 mov r11, #8 mov r11, r11, lsl r3 @ cache line size in bytes no_cache_id: - bic r1, pc, #63 @ align to longest cache line + mov r1, pc + bic r1, r1, #63 @ align to longest cache line add r2, r1, r2 -1: ldr r3, [r1], r11 @ s/w flush D cache +1: + ARM( ldr r3, [r1], r11 ) @ s/w flush D cache + THUMB( ldr r3, [r1] ) @ s/w flush D cache + THUMB( add r1, r1, r11 ) teq r1, r2 bne 1b @@ -886,6 +961,7 @@ __armv3_mpu_cache_flush: * memory, which again must be relocatable. */ #ifdef DEBUG + .align 2 .type phexbuf,#object phexbuf: .space 12 .size phexbuf, . - phexbuf diff --git a/arch/arm/boot/compressed/misc.c b/arch/arm/boot/compressed/misc.c index 3fc08413fff0..0dd03b6e6b1c 100644 --- a/arch/arm/boot/compressed/misc.c +++ b/arch/arm/boot/compressed/misc.c @@ -19,56 +19,14 @@ unsigned int __machine_arch_type; #include <linux/string.h> +#include <mach/uncompress.h> #ifdef STANDALONE_DEBUG #define putstr printf +#elif defined(CONFIG_DEBUG_ICEDCC) || defined(CONFIG_DEBUG_RVIDCC) +#define putstr icedcc_putstr #else -static void putstr(const char *ptr); - -#include <linux/compiler.h> -#include <mach/uncompress.h> - -#ifdef CONFIG_DEBUG_ICEDCC - -#ifdef CONFIG_CPU_V6 - -static void icedcc_putc(int ch) -{ - int status, i = 0x4000000; - - do { - if (--i < 0) - return; - - asm volatile ("mrc p14, 0, %0, c0, c1, 0" : "=r" (status)); - } while (status & (1 << 29)); - - asm("mcr p14, 0, %0, c0, c5, 0" : : "r" (ch)); -} - -#else - -static void icedcc_putc(int ch) -{ - int status, i = 0x4000000; - - do { - if (--i < 0) - return; - - asm volatile ("mrc p14, 0, %0, c0, c0, 0" : "=r" (status)); - } while (status & 2); - - asm("mcr p14, 0, %0, c1, c0, 0" : : "r" (ch)); -} - -#endif - -#define putc(ch) icedcc_putc(ch) -#define flush() do { } while (0) -#endif - static void putstr(const char *ptr) { char c; @@ -332,3 +290,149 @@ int main() } #endif +#if defined(CONFIG_DEBUG_ICEDCC) || defined(CONFIG_DEBUG_RVIDCC) + +#define _DCC_ARM9_RBIT (1 << 0) +#define _DCC_ARM9_WBIT (1 << 1) +#define _DCC_ARM10_RBIT (1 << 7) +#define _DCC_ARM10_WBIT (1 << 6) +#define _DCC_ARM11_RBIT (1 << 30) +#define _DCC_ARM11_WBIT (1 << 29) + +#define _READ_CORE_ID(x) { __asm__ ("mrc p15, 0, %0, c0, c0, 0\n" : "=r" (x)); \ + x = (x >> 4) & 0xFFF; } + +#define _WRITE_ARM9_DCC(x) __asm__ volatile ("mcr p14, 0, %0, c1, c0, 0\n" : : "r" (x)) +#define _READ_ARM9_DCC(x) __asm__ volatile ("mrc p14, 0, %0, c1, c0, 0\n" : "=r" (x)) +#define _STATUS_ARM9_DCC(x) __asm__ volatile ("mrc p14, 0, %0, c0, c0, 0\n" : "=r" (x)) +#define _CAN_READ_ARM9_DCC(x) {_STATUS_ARM9_DCC(x); x &= _DCC_ARM9_RBIT;} +#define _CAN_WRITE_ARM9_DCC(x) {_STATUS_ARM9_DCC(x); x &= _DCC_ARM9_WBIT; x = (x==0);} + +#define _WRITE_ARM10_DCC(x) __asm__ volatile ("mcr p14, 0, %0, c0, c5, 0\n" : : "r" (x)) +#define _READ_ARM10_DCC(x) __asm__ volatile ("mrc p14, 0, %0, c0, c5, 0\n" : "=r" (x)) +#define _STATUS_ARM10_DCC(x) __asm__ volatile ("mrc p14, 0, %0, c0, c1, 0\n" : "=r" (x)) +#define _CAN_READ_ARM10_DCC(x) {_STATUS_ARM10_DCC(x); x &= _DCC_ARM10_RBIT;} +#define _CAN_WRITE_ARM10_DCC(x) {_STATUS_ARM10_DCC(x); x &= _DCC_ARM10_WBIT; x = (x==0);} + +#define _WRITE_ARM11_DCC(x) __asm__ volatile ("mcr p14, 0, %0, c0, c5, 0\n" : : "r" (x)) +#define _READ_ARM11_DCC(x) __asm__ volatile ("mrc p14, 0, %0, c0, c5, 0\n" : "=r" (x)) +#define _STATUS_ARM11_DCC(x) __asm__ volatile ("mrc p14, 0, %0, c0, c1, 0\n" : "=r" (x)) +#define _CAN_READ_ARM11_DCC(x) {_STATUS_ARM11_DCC(x); x &= _DCC_ARM11_RBIT;} +#define _CAN_WRITE_ARM11_DCC(x) {_STATUS_ARM11_DCC(x); x &= _DCC_ARM11_WBIT; x = (x==0);} + +#define TIMEOUT_COUNT 0x4000000 + +void icedcc_putc(unsigned int ch) +{ + static enum {unknown, arm9_and_earlier, arm10, arm11_and_later} _arm_type = unknown; + static int has_timed_out = 0; + + if (has_timed_out) + return; + + if (_arm_type == unknown) + { + register unsigned int id; + _READ_CORE_ID(id); + + if ((id & 0xF00) == 0xA00) + _arm_type = arm10; + else if (id >= 0xb00) + _arm_type = arm11_and_later; + else + _arm_type = arm9_and_earlier; + } + + if (_arm_type == arm9_and_earlier) + { + register unsigned int reg; + unsigned int timeout_count = TIMEOUT_COUNT; + while (--timeout_count) + { + _CAN_WRITE_ARM9_DCC(reg); + if (reg) + break; + } + if (timeout_count == 0) + has_timed_out = 1; + else + _WRITE_ARM9_DCC(ch); + } + else if (_arm_type == arm10) + { + register unsigned int reg; + unsigned int timeout_count = TIMEOUT_COUNT; + while (--timeout_count) + { + _CAN_WRITE_ARM10_DCC(reg); + if (reg) + break; + } + if (timeout_count == 0) + has_timed_out = 1; + else + _WRITE_ARM10_DCC(ch); + } + else + { + register unsigned int reg; + unsigned int timeout_count = TIMEOUT_COUNT; + while (--timeout_count) + { + _CAN_WRITE_ARM11_DCC(reg); + if (reg) + break; + } + if (timeout_count == 0) + has_timed_out = 1; + else + _WRITE_ARM11_DCC(ch); + } +} + +static void icedcc_putstr(const char *ptr) +{ +#if defined(CONFIG_DEBUG_ICEDCC) + for (; *ptr != '\0'; ptr++) + icedcc_putc(*ptr); +#else + unsigned int sendbuf[16]; + unsigned short cnt; + char *ptr1 = (char*)&sendbuf[1]; + unsigned int *wordptr; + + for (cnt=0; *ptr != '\0'; ptr++) + { + if (*ptr == '\n') + { + *ptr1++ = '\r'; + cnt++; + } + *ptr1++ = *ptr; + cnt++; + } + + *sendbuf = 0xa5580000 | cnt; + + while(cnt % 4) + { + *ptr1++ = '\0'; + cnt++; + } + + cnt /= 4; + +#ifdef CONFIG_DEBUG_DCC_RAW + wordptr = &sendbuf[1]; +#else + wordptr = &sendbuf[0]; + cnt++; +#endif + + while(cnt--) + icedcc_putc(*wordptr++); + +#endif +} + +#endif diff --git a/arch/arm/boot/compressed/vmlinux.lds.in b/arch/arm/boot/compressed/vmlinux.lds.in index 153a07e7222b..a5924b9b88bd 100644 --- a/arch/arm/boot/compressed/vmlinux.lds.in +++ b/arch/arm/boot/compressed/vmlinux.lds.in @@ -11,6 +11,11 @@ OUTPUT_ARCH(arm) ENTRY(_start) SECTIONS { + /DISCARD/ : { + *(.ARM.exidx*) + *(.ARM.extab*) + } + . = TEXT_START; _text = .; diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig index a2cd9beaf37d..1ecf3f9fb375 100644 --- a/arch/arm/common/Kconfig +++ b/arch/arm/common/Kconfig @@ -4,6 +4,9 @@ config ARM_GIC config ARM_VIC bool +config ARM_NVIC + bool + config ICST525 bool diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile index 7cb7961d81cb..0adef7fca2f9 100644 --- a/arch/arm/common/Makefile +++ b/arch/arm/common/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_ARM_GIC) += gic.o obj-$(CONFIG_ARM_VIC) += vic.o +obj-$(CONFIG_ARM_NVIC) += nvic.o obj-$(CONFIG_ICST525) += icst525.o obj-$(CONFIG_ICST307) += icst307.o obj-$(CONFIG_SA1111) += sa1111.o @@ -18,3 +19,5 @@ obj-$(CONFIG_ARCH_IXP2000) += uengine.o obj-$(CONFIG_ARCH_IXP23XX) += uengine.o obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o obj-$(CONFIG_COMMON_CLKDEV) += clkdev.o +obj-$(CONFIG_DEBUG_RVIDCC) += rvidcc.o +rvidcc-objs := rvidcc_common.o rvidcc_linux.o diff --git a/arch/arm/common/nvic.c b/arch/arm/common/nvic.c new file mode 100644 index 000000000000..ea40cc8c7927 --- /dev/null +++ b/arch/arm/common/nvic.c @@ -0,0 +1,99 @@ +/* + * linux/arch/arm/common/nvic.c + * + * Copyright (C) 2008 ARM Limited, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Support for the Nested Vectored Interrupt Controller found on the + * ARMv7-M CPUs (Cortex-M3) + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/smp.h> + +#include <asm/irq.h> +#include <asm/io.h> +#include <asm/mach/irq.h> +#include <asm/hardware/nvic.h> + +static DEFINE_SPINLOCK(irq_controller_lock); + +/* + * Routines to acknowledge, disable and enable interrupts + * + * Linux assumes that when we're done with an interrupt we need to + * unmask it, in the same way we need to unmask an interrupt when + * we first enable it. + * + * The NVIC has a separate notion of "end of interrupt" to re-enable + * an interrupt after handling, in order to support hardware + * prioritisation. + * + * We can make the NVIC behave in the way that Linux expects by making + * our "acknowledge" routine disable the interrupt, then mark it as + * complete. + */ +static void nvic_ack_irq(unsigned int irq) +{ + u32 mask = 1 << (irq % 32); + + spin_lock(&irq_controller_lock); + writel(mask, NVIC_CLEAR_ENABLE + irq / 32 * 4); + spin_unlock(&irq_controller_lock); +} + +static void nvic_mask_irq(unsigned int irq) +{ + u32 mask = 1 << (irq % 32); + + spin_lock(&irq_controller_lock); + writel(mask, NVIC_CLEAR_ENABLE + irq / 32 * 4); + spin_unlock(&irq_controller_lock); +} + +static void nvic_unmask_irq(unsigned int irq) +{ + u32 mask = 1 << (irq % 32); + + spin_lock(&irq_controller_lock); + writel(mask, NVIC_SET_ENABLE + irq / 32 * 4); + spin_unlock(&irq_controller_lock); +} + +static struct irq_chip nvic_chip = { + .name = "NVIC", + .ack = nvic_ack_irq, + .mask = nvic_mask_irq, + .unmask = nvic_unmask_irq, +}; + +void __init nvic_init(void) +{ + unsigned int max_irq, i; + + max_irq = ((readl(NVIC_INTR_CTRL) & 0x1f) + 1) * 32; + + /* + * Disable all interrupts + */ + for (i = 0; i < max_irq / 32; i++) + writel(~0, NVIC_CLEAR_ENABLE + i * 4); + + /* + * Set priority on all interrupts. + */ + for (i = 0; i < max_irq; i += 4) + writel(0, NVIC_PRIORITY + i); + + /* + * Setup the Linux IRQ subsystem. + */ + for (i = 0; i < NR_IRQS; i++) { + set_irq_chip(i, &nvic_chip); + set_irq_handler(i, handle_level_irq); + set_irq_flags(i, IRQF_VALID | IRQF_PROBE); + } +} diff --git a/arch/arm/common/rvidcc_common.c b/arch/arm/common/rvidcc_common.c new file mode 100644 index 000000000000..3e21709095d0 --- /dev/null +++ b/arch/arm/common/rvidcc_common.c @@ -0,0 +1,794 @@ +/* + Copyright (C) 2004-2007 ARM Limited. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License version 2 + as published by the Free Software Foundation. + + 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 + + As a special exception, if other files instantiate templates or use macros + or inline functions from this file, or you compile this file and link it + with other works to produce a work based on this file, this file does not + by itself cause the resulting work to be covered by the GNU General Public + License. However the source code for this file must still be made available + in accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work based on + this file might be covered by the GNU General Public License. +*/ + +/* ================================================================ + Universal RealView ICE DCC communications driver - How to use it + ================================================================ + + This driver is split into 2 sections: a generic section (this file) that + handles the DCC hardware access and buffering and an OS-dependent file that + integrates the driver into the OS network and TTY layers. The interface + between the sections is defined in rvidcc_interface.h, which contains + prototypes and documentation for the functions in the generic section that + can be called from the OS-dependent section and the callback functions + called from the generic section that must be provided by the OS-dependent + section. + + This driver is designed to be able to work with or without interrupts, to + use raw DCC comms (as a TTY) or virtual Ethernet. The driver is configured + in the rvidcc_config.h header, including options to select which cores to + support, whether to support ethernet, buffer sizes and how to + enable/disable interrupts. See the example configs for details of all + options. + + All buffers used by the driver are in the system memory space. All + pointer arguments for functions called by or from the generic section are + pointers to system memory. It is the responsibility of the OS-dependent + section to copy to/from user memory. + + ARM Ltd. highly recommends using interrupt-driven DCC communications, and + to use the virtual Ethernet system, even if only a TTY is required. + + Raw DCC communications transfers data in 32-bit words. Thus if virtual + Ethernet is not used, data transfers will contain null padding for blocks + of data that do not have a multiple of 4 bytes. For most command-line + applications, this is unacceptable. However protocols, such as the remote + gdb protocol, will tolerate the extra nulls, provided they occur between + packets. This, of course, requires that data be passed to the driver as + packets, and not as byte-by-byte as some TTY drivers are in the habit of + doing. + + Using the virtual Ethernet system does not force you to have to support + networking, or an IP stack, etc. The virtual Ethernet system contains a + TTY channel and an Ethernet channel, and if only the TTY channel is + required, the Ethernet channel and its data can be ignored. Using this + for TTY only has the advantage that data packets do not have to be a + multiple of 4 bytes, and so it is suitable for command-line consoles. + + The virtual Ethernet system is not true Ethernet in that it can only carry + IP traffic. It does, however, have a mechanism for obtaining a unique MAC + address from the ICE, and the target will receive ARP-style queries to + determine if the target has a particular IP address. Thus DHCP can be used + to obtain an IP address from an external DHCP server, without the need for + any special configuration of the ICE. +*/ + +#include "rvidcc_config.h" +#include "rvidcc_interface.h" + + +#ifdef __ARMCC_VERSION +#define INLINE __inline +#else +#define INLINE inline +#endif + + +#ifndef RVIDCC_RAW + +#define UNCOMP_TTY_START 0xa5580000 +#define UNCOMP_ETH_START 0xa55a0000 +#define START_MASK 0xfffc0000 +#define START_SENTINEL UNCOMP_TTY_START + +#endif /* RVIDCC_RAW */ + +#if !defined(RVIDCC_ARM79) && !defined(RVIDCC_ARM10) && !defined(RVIDCC_ARM11) +#warning No cores are enabled. Define at least one of RVIDCC_ARM79, RVIDCC_ARM10 or RVIDCC_ARM11 +#endif + + +#define DCC_ARM9_RBIT (1 << 0) +#define DCC_ARM9_WBIT (1 << 1) +#define DCC_ARM10_RBIT (1 << 7) +#define DCC_ARM10_WBIT (1 << 6) +#define DCC_ARM11_RBIT (1 << 30) +#define DCC_ARM11_WBIT (1 << 29) + +/* Access primitives: x must be unsigned int */ + +#ifdef __ARMCC_VERSION + +/* RVCT versions */ +#define READ_CORE_ID(x) { __asm { mrc p15, 0, x, c0, c0, 0 } \ + x = (x >> 4) & 0xFFF; } + +#ifdef RVIDCC_ARM79 +#define WRITE_ARM9_DCC(x) __asm { mcr p14, 0, x, c1, c0, 0 } +#define READ_ARM9_DCC(x) __asm { mrc p14, 0, x, c1, c0, 0 } +#define STATUS_ARM9_DCC(x) __asm { mrc p14, 0, x, c0, c0, 0 } +#define CAN_READ_ARM9_DCC(x) {STATUS_ARM9_DCC(x); x &= DCC_ARM9_RBIT;} +#define CAN_WRITE_ARM9_DCC(x) {STATUS_ARM9_DCC(x); x &= DCC_ARM9_WBIT; x = (x==0);} +#endif + +#ifdef RVIDCC_ARM10 +#define WRITE_ARM10_DCC(x) __asm { mcr p14, 0, x, c0, c5, 0 } +#define READ_ARM10_DCC(x) __asm { mrc p14, 0, x, c0, c5, 0 } +#define STATUS_ARM10_DCC(x) __asm { mrc p14, 0, x, c0, c1, 0 } +#define CAN_READ_ARM10_DCC(x) {STATUS_ARM10_DCC(x); x &= DCC_ARM10_RBIT;} +#define CAN_WRITE_ARM10_DCC(x) {STATUS_ARM10_DCC(x); x &= DCC_ARM10_WBIT; x = (x==0);} +#endif + +#ifdef RVIDCC_ARM11 +#define WRITE_ARM11_DCC(x) __asm { mcr p14, 0, x, c0, c5, 0 } +#define READ_ARM11_DCC(x) __asm { mrc p14, 0, x, c0, c5, 0 } +#define STATUS_ARM11_DCC(x) __asm { mrc p14, 0, x, c0, c1, 0 } +#define CAN_READ_ARM11_DCC(x) {STATUS_ARM11_DCC(x); x &= DCC_ARM11_RBIT;} +#define CAN_WRITE_ARM11_DCC(x) {STATUS_ARM11_DCC(x); x &= DCC_ARM11_WBIT; x = (x==0);} +#endif + +#else + +/* GCC versions */ +#define READ_CORE_ID(x) { __asm__ ("mrc p15, 0, %0, c0, c0, 0\n" : "=r" (x)); \ + x = (x >> 4) & 0xFFF; } + +#ifdef RVIDCC_ARM79 +#define WRITE_ARM9_DCC(x) __asm__ volatile ("mcr p14, 0, %0, c1, c0, 0\n" : : "r" (x)) +#define READ_ARM9_DCC(x) __asm__ volatile ("mrc p14, 0, %0, c1, c0, 0\n" : "=r" (x)) +#define STATUS_ARM9_DCC(x) __asm__ volatile ("mrc p14, 0, %0, c0, c0, 0\n" : "=r" (x)) +#define CAN_READ_ARM9_DCC(x) {STATUS_ARM9_DCC(x); x &= DCC_ARM9_RBIT;} +#define CAN_WRITE_ARM9_DCC(x) {STATUS_ARM9_DCC(x); x &= DCC_ARM9_WBIT; x = (x==0);} +#endif + +#ifdef RVIDCC_ARM10 +#define WRITE_ARM10_DCC(x) __asm__ volatile ("mcr p14, 0, %0, c0, c5, 0\n" : : "r" (x)) +#define READ_ARM10_DCC(x) __asm__ volatile ("mrc p14, 0, %0, c0, c5, 0\n" : "=r" (x)) +#define STATUS_ARM10_DCC(x) __asm__ volatile ("mrc p14, 0, %0, c0, c1, 0\n" : "=r" (x)) +#define CAN_READ_ARM10_DCC(x) {STATUS_ARM10_DCC(x); x &= DCC_ARM10_RBIT;} +#define CAN_WRITE_ARM10_DCC(x) {STATUS_ARM10_DCC(x); x &= DCC_ARM10_WBIT; x = (x==0);} +#endif + +#ifdef RVIDCC_ARM11 +#define WRITE_ARM11_DCC(x) __asm__ volatile ("mcr p14, 0, %0, c0, c5, 0\n" : : "r" (x)) +#define READ_ARM11_DCC(x) __asm__ volatile ("mrc p14, 0, %0, c0, c5, 0\n" : "=r" (x)) +#define STATUS_ARM11_DCC(x) __asm__ volatile ("mrc p14, 0, %0, c0, c1, 0\n" : "=r" (x)) +#define CAN_READ_ARM11_DCC(x) {STATUS_ARM11_DCC(x); x &= DCC_ARM11_RBIT;} +#define CAN_WRITE_ARM11_DCC(x) {STATUS_ARM11_DCC(x); x &= DCC_ARM11_WBIT; x = (x==0);} +#endif + +#endif + +static enum {arm9_and_earlier, arm10, arm11_and_later} arm_type = arm9_and_earlier; + +/* Prototypes */ +static void rvidcc_write_lowlevel(const void *ptr, size_t len); + +/* Queues */ + +struct ringbuffer +{ + unsigned char *buf; + size_t size; + unsigned char* readPtr; + unsigned char* writePtr; +}; + +static unsigned char dcc_inbuffer[RVIDCC_BUFSIZE]; +static struct ringbuffer dcc_inbuf; +static unsigned char dcc_outbuffer[RVIDCC_BUFSIZE]; +static struct ringbuffer dcc_outbuf; + +#ifndef RVIDCC_RAW +#define DCC_TTY_INBUF_SIZE (RVIDCC_BUFSIZE/2) +static unsigned char dcc_temp_source[DCC_TTY_INBUF_SIZE]; +static unsigned char dcc_tty_inbuffer[DCC_TTY_INBUF_SIZE]; +static struct ringbuffer dcc_tty_inbuf; +#define DCC_TTY_INBUF dcc_tty_inbuf + +#else + +#define DCC_TTY_INBUF dcc_inbuf + +#endif + + +/* This ringbuffer has no locks as it assumes only 1 reader and 1 writer and + * assumes a 32-bit access is atomic */ +INLINE static void ringbuf_advance_read(struct ringbuffer* ring_buf, size_t amount) +{ + unsigned char* temptr = ring_buf->readPtr + amount; + while (temptr >= ring_buf->buf + ring_buf->size) + temptr -= ring_buf->size; + ring_buf->readPtr = temptr; +} + + +INLINE static void ringbuf_advance_write(struct ringbuffer* ring_buf, size_t amount) +{ + unsigned char* temptr = ring_buf->writePtr + amount; + while (temptr >= ring_buf->buf + ring_buf->size) + temptr -= ring_buf->size; + ring_buf->writePtr = temptr; +} + + +INLINE static void ringbuf_get(struct ringbuffer* ring_buf, + unsigned char* dst, size_t amount) +{ + unsigned char* source = ring_buf->readPtr; + size_t len_to_end = ring_buf->buf + ring_buf->size - source; + if (amount > len_to_end) + { + memcpy(dst, source, len_to_end); + memcpy(dst + len_to_end, ring_buf->buf, (amount - len_to_end)); + } + else + memcpy(dst, source, amount); +} + + +INLINE static void ringbuf_put(struct ringbuffer* ring_buf, + unsigned char* src, size_t amount) +{ + unsigned char* dest = ring_buf->writePtr; + unsigned int len_to_end = ring_buf->buf + ring_buf->size - dest; + if (amount > len_to_end) + { + memcpy(dest, src, len_to_end); + memcpy(ring_buf->buf, src + len_to_end, amount - len_to_end); + } + else + memcpy(dest, src, amount); +} + + +INLINE static size_t ringbuf_available_data(struct ringbuffer* ring_buf) +{ + int avail = ring_buf->writePtr - ring_buf->readPtr; + + if (avail < 0) + /* handle wrap around */ + avail += ring_buf->size; + + return (size_t)avail; +} + + +INLINE static size_t ringbuf_available_space(struct ringbuffer* ring_buf) +{ + size_t avail; + + if (ring_buf->writePtr >= ring_buf->readPtr) + avail = ring_buf->size - (ring_buf->writePtr - ring_buf->readPtr) - 1; + else + avail = ring_buf->readPtr - ring_buf->writePtr - 1; + + return avail; +} + + +// conditionally swap endian of a 32bit word +#ifdef RVIDCC_BIG_ENDIAN +#define to_little_endian(x) ((((x)&0xff) << 24) | (((x)&0xff00) << 8) | (((x)&0xff0000) >> 8) | (((x)&0xff000000) >> 24)) +#else +#define to_little_endian(x) (x) +#endif + + +int rvidcc_init(void) +{ + register unsigned int id; + int err = 0; +#ifndef RVIDCC_RAW + unsigned int macRequest = to_little_endian(UNCOMP_ETH_START); +#endif + + RVIDCC_DISABLE_INTERRUPTS; + + /* check core type, raising error if core not supported */ + READ_CORE_ID(id); + if ((id & 0xF00) == 0xA00) + { + arm_type = arm10; +#ifndef RVIDCC_ARM10 + err = RVIDCC_ERR_CORE_NOT_SUPPORTED; +#endif + } + else if (id >= 0xb00) + { + arm_type = arm11_and_later; +#ifndef RVIDCC_ARM11 + err = RVIDCC_ERR_CORE_NOT_SUPPORTED; +#endif + } + else + { + arm_type = arm9_and_earlier; +#ifndef RVIDCC_ARM79 + err = RVIDCC_ERR_CORE_NOT_SUPPORTED; +#endif + } + + if (!err) + { + dcc_inbuf.buf = dcc_inbuffer; + dcc_inbuf.size = RVIDCC_BUFSIZE; + dcc_inbuf.readPtr = dcc_inbuf.buf; + dcc_inbuf.writePtr = dcc_inbuf.buf; + + dcc_outbuf.buf = dcc_outbuffer; + dcc_outbuf.size = RVIDCC_BUFSIZE; + dcc_outbuf.readPtr = dcc_outbuf.buf; + dcc_outbuf.writePtr = dcc_outbuf.buf; + +#ifndef RVIDCC_RAW + dcc_tty_inbuf.buf = dcc_tty_inbuffer; + dcc_tty_inbuf.size = DCC_TTY_INBUF_SIZE; + dcc_tty_inbuf.readPtr = dcc_tty_inbuf.buf; + dcc_tty_inbuf.writePtr = dcc_tty_inbuf.buf; + + /* request a MAC address */ + rvidcc_write_lowlevel(&macRequest, sizeof(unsigned int)); +#endif + + /* Enable interrupts (write interrupt will be enabled when something is written) */ + RVIDCC_ENABLE_READ_INTERRUPT; + RVIDCC_ENABLE_INTERRUPTS; + } + + return err; +} + + +void rvidcc_write(void) +{ + register unsigned int reg; + int pollcount = -1; + int some_transfer = 0; + + switch (arm_type) + { +#ifdef RVIDCC_ARM79 + case arm9_and_earlier: + { + /* Try to write everything available */ + while (ringbuf_available_data(&dcc_outbuf) >= sizeof(unsigned int)) + { + /* On the first word, the write is aborted immediately if the DCC write + * register is full. On subsequent words, the driver waits for a + * bit (RVIDCC_MAX_INLINE_POLLS times) as the RVI will probably + * read the DCC register soon because it knows data is being sent. */ + do + { + CAN_WRITE_ARM9_DCC(reg); + } while (!reg && pollcount != -1 && --pollcount > 0); + + if (!reg) + break; + + reg = to_little_endian(*(unsigned int *)dcc_outbuf.readPtr); + WRITE_ARM9_DCC(reg); + ringbuf_advance_read(&dcc_outbuf, sizeof(unsigned int)); + pollcount = RVIDCC_MAX_INLINE_POLLS; + some_transfer = 1; + } + + pollcount = -1; + break; + } +#endif + +#ifdef RVIDCC_ARM10 + case arm10: + { + /* Try to write everything available */ + while (ringbuf_available_data(&dcc_outbuf) >= sizeof(unsigned int)) + { + do + { + CAN_WRITE_ARM10_DCC(reg); + } while (!reg && pollcount != -1 && --pollcount > 0); + + if (!reg) + break; + + reg = to_little_endian(*(unsigned int *)dcc_outbuf.readPtr); + WRITE_ARM10_DCC(reg); + ringbuf_advance_read(&dcc_outbuf, sizeof(unsigned int)); + pollcount = RVIDCC_MAX_INLINE_POLLS; + some_transfer = 1; + } + + pollcount = -1; + break; + } +#endif + +#ifdef RVIDCC_ARM11 + case arm11_and_later: + { + /* Try to write everything available */ + while (ringbuf_available_data(&dcc_outbuf) >= sizeof(unsigned int)) + { + do + { + CAN_WRITE_ARM11_DCC(reg); + } while (!reg && pollcount != -1 && --pollcount > 0); + + if (!reg) + break; + + reg = to_little_endian(*(unsigned int *)dcc_outbuf.readPtr); + WRITE_ARM11_DCC(reg); + ringbuf_advance_read(&dcc_outbuf, sizeof(unsigned int)); + pollcount = RVIDCC_MAX_INLINE_POLLS; + some_transfer = 1; + } + + pollcount = -1; + break; + } +#endif + + default: + break; + } + + if (some_transfer) + rvidcc_cb_notify(); + + /* Disable interrupt if nothing left to write */ + if (ringbuf_available_data(&dcc_outbuf) == 0) + RVIDCC_DISABLE_WRITE_INTERRUPT; +} + + +/* this routine assumes it is non-interruptible by others that access the queue pointers */ +/* this is written out longhand because it may be called from an interrupt routine */ +void rvidcc_read(void) +{ + register unsigned int reg; + int pollcount = -1; + int some_transfer = 0; + + switch (arm_type) + { +#ifdef RVIDCC_ARM79 + case arm9_and_earlier: + { + /* Try to read everything available */ + while (ringbuf_available_space(&dcc_inbuf) >= sizeof(unsigned int)) + { + do + { + CAN_READ_ARM9_DCC(reg); + } while (!reg && pollcount != -1 && --pollcount > 0); + + if (!reg) + break; + + READ_ARM9_DCC(reg); + *(unsigned int *)dcc_inbuf.writePtr = to_little_endian(reg); + ringbuf_advance_write(&dcc_inbuf, sizeof(unsigned int)); + pollcount = RVIDCC_MAX_INLINE_POLLS; + some_transfer = 1; + } + break; + } +#endif + +#ifdef RVIDCC_ARM10 + case arm10: + { + /* Try to read everything available */ + while (ringbuf_available_space(&dcc_inbuf) >= sizeof(unsigned int)) + { + do + { + CAN_READ_ARM10_DCC(reg); + } while (!reg && pollcount != -1 && --pollcount > 0); + + if (!reg) + break; + + READ_ARM10_DCC(reg); + *(unsigned int *)dcc_inbuf.writePtr = to_little_endian(reg); + ringbuf_advance_write(&dcc_inbuf, sizeof(unsigned int)); + pollcount = RVIDCC_MAX_INLINE_POLLS; + some_transfer = 1; + } + break; + } +#endif + +#ifdef RVIDCC_ARM11 + case arm11_and_later: + { + /* Try to read everything available */ + while (ringbuf_available_space(&dcc_inbuf) >= sizeof(unsigned int)) + { + do + { + CAN_READ_ARM11_DCC(reg); + } while (!reg && pollcount != -1 && --pollcount > 0); + + if (!reg) + break; + + READ_ARM11_DCC(reg); + *(unsigned int *)dcc_inbuf.writePtr = to_little_endian(reg); + ringbuf_advance_write(&dcc_inbuf, sizeof(unsigned int)); + pollcount = RVIDCC_MAX_INLINE_POLLS; + some_transfer = 1; + } + break; + } +#endif + + default: + break; + } + + if (ringbuf_available_space(&dcc_inbuf) == 0) + RVIDCC_DISABLE_READ_INTERRUPT; + + if (some_transfer) + rvidcc_cb_notify(); +} + + +/* this routine assumes it is not interrupted by others that access the queue pointers */ +void rvidcc_poll(void) +{ + rvidcc_write(); + rvidcc_read(); +} + + +#ifndef RVIDCC_RAW +void rvidcc_process(void) +{ + unsigned int sentinel = 0; + int compsize = 0; + + /* Process received data */ + while (ringbuf_available_data(&dcc_inbuf) >= sizeof(unsigned int)) + { + int bytes; + unsigned char *buf; + + /* Search for start sentinel */ + sentinel = to_little_endian(*(unsigned int *)dcc_inbuf.readPtr); + compsize = sentinel & 0xffff; + + /* Discard if not a start sentinel */ + if ((sentinel & START_MASK) != START_SENTINEL || + compsize > sizeof(dcc_temp_source) || compsize <= 0) + { + ringbuf_advance_read(&dcc_inbuf, sizeof(unsigned int)); + continue; + } + + /* Sentinel found, come back later if not enough data */ + bytes = ringbuf_available_data(&dcc_inbuf); + if (bytes < compsize + sizeof(unsigned int)) + break; + + /* Copy to a contiguous buffer */ + ringbuf_get(&dcc_inbuf, + dcc_temp_source, compsize + sizeof(unsigned int)); + + sentinel &= 0xffff0000; + buf = dcc_temp_source + sizeof(unsigned int); + + bytes = compsize; + + /* Round up to nearest whole word */ + if (compsize & 0x3) + compsize = (compsize + 4) & ~0x3; + + /* advance by packet size + sentinel */ + ringbuf_advance_read(&dcc_inbuf, compsize + sizeof(unsigned int)); + + /* allow more data to be received */ + RVIDCC_ENABLE_READ_INTERRUPT; + + /* Process TTY or IP packet accordingly */ + if (sentinel == UNCOMP_TTY_START) + { + ringbuf_put(&dcc_tty_inbuf, buf, bytes); + ringbuf_advance_write(&dcc_tty_inbuf, bytes); + } + else + { + if (bytes == 4) /* ARP 'is this IP address you?' */ + { + if (rvidcc_cb_has_addr(buf)) + rvidcc_transmit_ip_packet(buf, 4); + } + else if (bytes == 6) /* set MAC address */ + rvidcc_cb_set_mac_address(buf); + else + rvidcc_cb_ip_packet_received(buf, (size_t)bytes); + } + } +} +#endif + + +#ifdef RVIDCC_SCAN_INPUT_FOR +int rvidcc_scan_input_for(char c, size_t span) +{ + int ret = 0; + + size_t bytesAvail = ringbuf_available_data(&DCC_TTY_INBUF); + if (bytesAvail && span) + { + unsigned char *p = DCC_TTY_INBUF.readPtr; + + if (span > bytesAvail) + span = bytesAvail; + + while (span--) + { + if (*p++ == c) + { + ret = 1; + break; + } + + if (p >= DCC_TTY_INBUF.buf + DCC_TTY_INBUF.size) + p = DCC_TTY_INBUF.buf; + } + } + return ret; +} +#endif + + +#ifdef RVIDCC_OUTBUF_DATA +size_t rvidcc_outbuf_data(void) +{ + return ringbuf_available_data(&dcc_outbuf); +} +#endif + + +size_t rvidcc_serial_can_read(void) +{ + return ringbuf_available_data(&DCC_TTY_INBUF); +} + + +size_t rvidcc_serial_can_write(void) +{ + size_t space = ringbuf_available_space(&dcc_outbuf); + + // protocol only has 16 bits for packet size, so limit packets to this size + if (space > (65536 - 4)) + space = (65536 - 4); + + return space; +} + + +size_t rvidcc_read_serial(void *ptr, size_t len) +{ + size_t bytes = ringbuf_available_data(&DCC_TTY_INBUF); + if (bytes && len) + { + if (bytes > len) + bytes = len; + + ringbuf_get(&DCC_TTY_INBUF, (unsigned char*)ptr, bytes); +#ifdef RVIDCC_RAW + /* round up to 4 bytes */ + if (bytes & 0x3) + ringbuf_advance_read(&DCC_TTY_INBUF, ((bytes + 4) & ~0x3)); + else + ringbuf_advance_read(&DCC_TTY_INBUF, bytes); + + RVIDCC_ENABLE_READ_INTERRUPT; +#else + ringbuf_advance_read(&DCC_TTY_INBUF, bytes); +#endif + } + else + bytes = 0; + + return bytes; +} + + +static void rvidcc_write_lowlevel(const void *ptr, size_t len) +{ + size_t rem; + + ringbuf_put(&dcc_outbuf, (unsigned char*)ptr, len); + + ringbuf_advance_write(&dcc_outbuf, len & ~0x3); // advance whole words + + /* ensure excess bytes up to word boundary are null */ + rem = (len & 0x3); + if (rem) + { + for (; rem < 4; rem++) + *(dcc_outbuf.writePtr + rem) = '\0'; + + ringbuf_advance_write(&dcc_outbuf, sizeof(unsigned long)); // advance remainder word + } + + + RVIDCC_ENABLE_WRITE_INTERRUPT; +} + + +size_t rvidcc_write_serial(const void *ptr, size_t len) +{ + size_t bytes; +#ifndef RVIDCC_RAW + unsigned long sentinel; +#endif + + bytes = rvidcc_serial_can_write(); + +#ifndef RVIDCC_RAW + if (bytes > sizeof(unsigned long)) + { + /* Size limit the amount of data to be written, allowing for sentinel */ + if (len > (bytes - sizeof(unsigned long))) + len = (bytes - sizeof(unsigned long)); + + sentinel = to_little_endian(UNCOMP_TTY_START | (unsigned short)len); + rvidcc_write_lowlevel(&sentinel, sizeof(unsigned long)); + rvidcc_write_lowlevel(ptr, len); + } +#else + if (bytes > 0) + { + /* Size limit the amount of data to be written */ + if (len > bytes) + len = bytes; + + rvidcc_write_lowlevel(ptr, len); + } +#endif + else + len = 0; + + return len; +} + + +#ifndef RVIDCC_RAW +int rvidcc_transmit_ip_packet(void *packet, size_t length) +{ + if (rvidcc_serial_can_write() >= (length + 4)) + { + unsigned long sentinel = to_little_endian(UNCOMP_ETH_START | (unsigned short)length); + rvidcc_write_lowlevel(&sentinel, 4); + rvidcc_write_lowlevel(packet, length); + } + else + length = 0; + + return length; +} +#endif + +/* End of file */ diff --git a/arch/arm/common/rvidcc_config.h b/arch/arm/common/rvidcc_config.h new file mode 100644 index 000000000000..faabe1db1b59 --- /dev/null +++ b/arch/arm/common/rvidcc_config.h @@ -0,0 +1,166 @@ +/* + Copyright (C) 2004-2007 ARM Limited. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License version 2 + as published by the Free Software Foundation. + + 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 + + As a special exception, if other files instantiate templates or use macros + or inline functions from this file, or you compile this file and link it + with other works to produce a work based on this file, this file does not + by itself cause the resulting work to be covered by the GNU General Public + License. However the source code for this file must still be made available + in accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work based on + this file might be covered by the GNU General Public License. +*/ + +#ifndef RVIDCC_CONFIG_H +#define RVIDCC_CONFIG_H + +/* + * + * Platform headers that define basic types (e.g. size_t) + * ------------------------------------------------------ + * + */ +#include <linux/types.h> +#include <linux/netdevice.h> + + +/* + * Put any platform specific definitions required by the interrupt macros here + * --------------------------------------------------------------------------- + */ + +/* Structure containing our internal state, etc. */ +struct dccconfig +{ + unsigned int rx_interrupt; + unsigned int tx_interrupt; + unsigned long timer_poll_period; + int use_timer_poll; + + volatile unsigned long irq_disable; +}; + +extern struct dccconfig dcc_config; + + +/* + * Configuration + * ------------- + */ + +/* + * Define which ARM cores this binary is to be used on + * - RVIDCC_ARM79 for all ARM 7 and ARM 9 based cores + * - RVIDCC_ARM10 for all ARM 10 cores + * - RVIDCC_ARM11 for all ARM 11 and later cores (including Cortex) + */ +#define RVIDCC_ARM79 +#define RVIDCC_ARM10 +#define RVIDCC_ARM11 + +/* + * Define RVIDCC_OUTBUF_DATA if the OS needs to check if all pending data + * has been sent out to the ICE. + */ +#define RVIDCC_OUTBUF_DATA + +/* + * Define RVIDCC_RAW if the virtual ethernet functionality is not required. See + * rvidcc.c for more information. + */ +#ifdef CONFIG_DEBUG_DCC_RAW +#define RVIDCC_RAW +#endif + +/* + * Define RVIDCC_BIG_ENDIAN if your platform is big endian. + */ +#ifdef CONFIG_CPU_BIG_ENDIAN +#define RVIDCC_BIG_ENDIAN +#endif + +/* + * Define RVIDCC_SCAN_INPUT_FOR if the OS needs to peek ahead in the TTY data + */ +#ifdef CONFIG_DEBUG_DCC_KGDB +#define RVIDCC_SCAN_INPUT_FOR +#endif + +/* + * RVIDCC_BUFSIZE defines the size of the input and output buffers for DCC data + */ +#ifndef RVIDCC_BUFSIZE +#ifdef RVIDCC_RAW +#define RVIDCC_BUFSIZE 4096 +#else +#define RVIDCC_BUFSIZE 8192 +#endif +#endif + +/* + * RVIDCC_MAX_INLINE_POLLS defines the number of times to the driver should + * check if the host is ready for more data in rvidcc_write or if the host has + * sent more data in rvidcc_read. Increasing this value may improve the data + * transfer rate, but will increase the length of time spent in the interrupt + * handler. Adjust this value according to the performance and latency + * requirements of the system. + */ +#define RVIDCC_MAX_INLINE_POLLS 500 + +/* + * Interrupt configuration + * + * Define macros here to enable and disable interrupts. If the OS uses + * polling instead of interrupts, define as empty. + * + */ + +#define RVIDCC_ENABLE_INTERRUPTS + +#define RVIDCC_DISABLE_INTERRUPTS + +/* Interrupt control macros */ +#define RVIDCC_ENABLE_WRITE_INTERRUPT do { \ + if (!dcc_config.use_timer_poll) { \ + if (test_and_clear_bit(0, &dcc_config.irq_disable)) \ + enable_irq(dcc_config.tx_interrupt); \ + } \ + } while(0) + +#define RVIDCC_DISABLE_WRITE_INTERRUPT do { \ + if (!dcc_config.use_timer_poll) { \ + if (!test_and_set_bit(0, &dcc_config.irq_disable)) \ + disable_irq(dcc_config.tx_interrupt); \ + } \ + } while(0) + +#define RVIDCC_ENABLE_READ_INTERRUPT do { \ + if (!dcc_config.use_timer_poll) { \ + if (test_and_clear_bit(1, &dcc_config.irq_disable)) \ + enable_irq(dcc_config.rx_interrupt); \ + } \ + } while(0) + +#define RVIDCC_DISABLE_READ_INTERRUPT do { \ + if (!dcc_config.use_timer_poll) { \ + if (!test_and_set_bit(1, &dcc_config.irq_disable)) \ + disable_irq(dcc_config.rx_interrupt); \ + } \ + } while(0) + + +#endif /* RVIDCC_CONFIG_H */ diff --git a/arch/arm/common/rvidcc_interface.h b/arch/arm/common/rvidcc_interface.h new file mode 100644 index 000000000000..9ccf439ce46e --- /dev/null +++ b/arch/arm/common/rvidcc_interface.h @@ -0,0 +1,182 @@ +/* + Copyright (C) 2004-2007 ARM Limited. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License version 2 + as published by the Free Software Foundation. + + 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 + + As a special exception, if other files instantiate templates or use macros + or inline functions from this file, or you compile this file and link it + with other works to produce a work based on this file, this file does not + by itself cause the resulting work to be covered by the GNU General Public + License. However the source code for this file must still be made available + in accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work based on + this file might be covered by the GNU General Public License. +*/ + +#ifndef RVIDCC_INTERFACE_H +#define RVIDCC_INTERFACE_H + +/* + * These are the functions provided by the generic section that the OS-dependent + * section is allowed to call + */ + +#define RVIDCC_ERR_CORE_NOT_SUPPORTED 1 + +/* Initialise DCC comms driver. + * + * Note that this triggers the request to the RVI for a MAC address when using + * virtual Ethernet. Returns 0 on success, RVIDCC_ERR_* on failure + */ +extern int rvidcc_init(void); + +/* Poll for DCC communications. + * + * If interrupts are not being used then this should be called periodically, + * otherwise this is the interrupt handler for DCC communications. + */ +extern void rvidcc_poll(void); + +#ifndef RVIDCC_RAW +/* Process received DCC data. + * + * This function is only relevant if using virtual Ethernet. This is a + * 'bottom half' routine and processes the data captured in rvidcc_poll(). If + * using interrupts, this routine should be scheduled to be called at the next + * available opportunity once the interrupt handler has completed. If not + * using interrupts then this can be called immediately on notification of the + * arrival of incoming data. + */ +extern void rvidcc_process(void); +#endif + +/* Handle low level reading of dcc information + * + * This should be called from the interrupt handler for the dcc read interrupt + * to read data from the DCC register. It is also called by rvidcc_poll. + */ +extern void rvidcc_read(void); + +/* Handle low level writing of dcc information + * + * This should be called from the interrupt handler for the dcc write interrupt + * to write data to the DCC register. It is also called by rvidcc_poll. + */ +extern void rvidcc_write(void); + +#ifdef RVIDCC_OUTBUF_DATA +/* Get the number of bytes left to be sent out to the ICE */ +extern size_t rvidcc_outbuf_data(void); +#endif + +/* Get the number of bytes available for reading from TTY. + * + * Zero is returned if no data is available. + */ +extern size_t rvidcc_serial_can_read(void); + +/* Get the number of bytes that can be written to the TTY. + * + * Zero is returned if no data can be written. + */ +extern size_t rvidcc_serial_can_write(void); + +/* Read and consume incoming TTY data. + * + * This is the primary TTY read routine. + */ +extern size_t rvidcc_read_serial(void *ptr, size_t len); + +/* Write and queue up outgoing TTY data. + * + * This is the primary TTY write routine. + */ +extern size_t rvidcc_write_serial(const void *ptr, size_t len); + +/* Send an IP packet out to the virtual network. + * + * The data provided must be a valid IP packet and must not contain an + * Ethernet header. The data provided is all copied, if there is space on the + * queue, othewise none of it is and the packet should be treated as still + * pending. The return value is the number of bytes copied. + */ +extern int rvidcc_transmit_ip_packet(void *packet, size_t length); + + +#ifdef RVIDCC_SCAN_INPUT_FOR +/* Scan pending incoming TTY data for a character. + * + * This is primarily provided for detecting ^C (interrupt) on pending incoming + * TTY data, e.g. to allow a gdb stub to be re-activated when a program is + * running. It should be called from 'bottom half' processing, or from a + * regular poll. The scan starts at the latest character received and works + * backwards for a maximum of 'span' characters. + */ +extern int rvidcc_scan_input_for(char c, size_t span); +#endif + + +/* + * Callbacks + * These are called from the generic section (in rvidcc.c) and should be provided + * by the OS-dependent section. + */ + + +/* DCC incoming data notification callback. + * + * This is called by rvidcc_poll() whenever new data is received on the DCC + * channel. It indicates rvidcc_process() should be called at the next + * available opportunity (and also rvidcc_scaninput_for() if required). If + * using interrupts, schedule a 'bottom half' or tasklet to run. If not using + * interrupts, rvidcc_process() can be called directly from here. + */ +extern void rvidcc_cb_notify(void); + +#ifndef RVIDCC_RAW + +/* Network IP packet received callback. + * + * This is called from rvidcc_process() whenever an IP packet is received. Note: + * this only contains IP data without any Ethernet headers. The data must be + * copied and passed on to the OS appropriately. + */ +extern void rvidcc_cb_ip_packet_received(void *packet, size_t length); + +/* Pseudo ARP callback. + * + * This is called from rvidcc_process() whenever the ICE needs to determine if + * it should direct IP packets for the given IP address to this processor. + * Return non-zero if this is the case, or zero otherwise. The parameter + * passed is a pointer to 4-byte array which is the binary IP address in + * network (big-endian) order. + */ +extern int rvidcc_cb_has_addr(unsigned char *ip_binary); + +/* Set Ethernet MAC address callback. + * + * This is called from rvidcc_process() whenever in response to _init_dcc()'s + * request to the ICE for a (globally unique) MAC address. This happens very + * soon after initialisation, and if a MAC is required, then the virtual + * Ethernet driver should not be considered fully up and running until this + * has been received. The parameter is a pointer to a 6-byte array which is + * the MAC address. + */ +extern void rvidcc_cb_set_mac_address(unsigned char *mac); + +#endif + + +#endif // RVIDCC_INTERFACE_H diff --git a/arch/arm/common/rvidcc_linux.c b/arch/arm/common/rvidcc_linux.c new file mode 100644 index 000000000000..c07058314475 --- /dev/null +++ b/arch/arm/common/rvidcc_linux.c @@ -0,0 +1,1014 @@ +/* + Copyright (C) 2004-2007 ARM Limited. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License version 2 + as published by the Free Software Foundation. + + 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 + + As a special exception, if other files instantiate templates or use macros + or inline functions from this file, or you compile this file and link it + with other works to produce a work based on this file, this file does not + by itself cause the resulting work to be covered by the GNU General Public + License. However the source code for this file must still be made available + in accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work based on + this file might be covered by the GNU General Public License. +*/ + +#include <linux/module.h> +#include <linux/reboot.h> +#include <linux/console.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/etherdevice.h> +#include <linux/if_arp.h> +#include <net/route.h> +#include <asm/hardware/rvidcc.h> + +#ifdef CONFIG_DEBUG_DCC_KGDB +#include <linux/kgdb.h> +#endif + +#include "rvidcc_config.h" +#include "rvidcc_interface.h" + +#define ARM_DCC_VER "1.0" +#define ARM_TTY_NAME "ttyDCC" +#define ARM_ETH_NAME "ethDCC" +#define ARM_DCC_MAJOR 204 +#define ARM_DCC_MINOR 46 +#define ARM_DCC_POLL_PERIOD 1 /* jiffies per poll: used in timed polled (non-interrupt) mode */ + +/* SMP not supported */ +#ifdef CONFIG_SMP +#error DCC module does not support SMP +#endif + +/* Interrupt defininitons for known achitectures that have them */ +#ifdef CONFIG_ARCH_INTEGRATOR + +#define DCC_RX_IRQ IRQ_CM_COMMRX +#define DCC_TX_IRQ IRQ_CM_COMMTX + +#elif defined(CONFIG_ARCH_L7200) + +#define DCC_RX_IRQ IRQ_DEBUG_RX +#define DCC_TX_IRQ IRQ_DEBUG_TX + +#elif defined(CONFIG_ARCH_LH7A404) + +#define DCC_RX_IRQ IRQ_COMMRX +#define DCC_TX_IRQ IRQ_COMMTX + +#elif defined(CONFIG_ARCH_VERSATILE_PB) + +#define DCC_RX_IRQ IRQ_COMMRx +#define DCC_TX_IRQ IRQ_COMMTx + +#else + +#define DCC_RX_IRQ 0 +#define DCC_TX_IRQ 0 + +#endif + +/* This section contains the primary OS driver functions and definitions */ + +/* Structure containing our internal state, etc. */ + +struct dccconfig dcc_config = +{ + .rx_interrupt = DCC_RX_IRQ, + .tx_interrupt = DCC_TX_IRQ, + .use_timer_poll = 1, /* use timer poll until interrupts are enabled (if ever) */ + .timer_poll_period = ARM_DCC_POLL_PERIOD +}; + +struct dccinfo +{ + struct tty_struct *tty; + int tty_opens; + int init_called; + int kernel_in_panic; + volatile unsigned long timerpoll_lock; + volatile unsigned long tx_locked; +#ifndef CONFIG_DEBUG_DCC_RAW + struct net_device *netdev; + char mac_address[ETH_ALEN]; + struct sk_buff *pending_skb; +#endif +}; +static struct dccinfo dcc_info; + +/* Module parameters - IRQ's etc. an be set on the boot line */ +module_param_named(rxirq, dcc_config.rx_interrupt, uint, 0444); +module_param_named(txirq, dcc_config.tx_interrupt, uint, 0444); +module_param_named(pollperiod, dcc_config.timer_poll_period, ulong, 0444); +MODULE_PARM_DESC(rxirq, "DCC receive IRQ number"); +MODULE_PARM_DESC(txirq, "DCC transmit IRQ number"); +MODULE_PARM_DESC(pollperiod, "DCC timer polling period"); + +/* Tasklet used as a "bottom half" - services data collected by interrupt or timed poll */ +static void dcc_tasklet_action(unsigned long data); +static DECLARE_TASKLET(dcc_tasklet, dcc_tasklet_action, 0); +static void dcc_poll(void); + +/* + * Write data on the serial channel if no other write is in progress + */ +static size_t dcc_write_serial(const void *ptr, size_t len) +{ + size_t res = 0; + + if (!test_and_set_bit(0, &dcc_info.tx_locked)) + { + res = rvidcc_write_serial(ptr, len); + clear_bit(0, &dcc_info.tx_locked); + } + + return res; +} + +#ifndef RVIDCC_RAW +/* + * Send a network packet if no other write is in progress + */ +static int dcc_transmit_ip_packet(void *packet, size_t length) +{ + int res = 0; + + if (!test_and_set_bit(0, &dcc_info.tx_locked)) + { + res = rvidcc_transmit_ip_packet(packet, length); + clear_bit(0, &dcc_info.tx_locked); + } + + return res; +} +#endif + +/* KGDB support + * + * KGDB and the console/tty channel are mutually exclusive, + * i.e. KGDB takes over the tty channel if it is enabled. + */ +#ifdef CONFIG_DEBUG_DCC_KGDB + +#define KGDB_BUF_SIZE 1024 /* needs to be big enough to hold one GDB packet */ +static int dcc_init_dcc(void); +static void dcc_print_banner(void); +static void dcc_tasklet_action(unsigned long data); + +static char kgdb_buf[KGDB_BUF_SIZE]; +static size_t kgdb_buf_chars = 0; + +/* KGDB getchar routine */ +static int kgdb_getDebugChar(void) +{ + char ch; + + /* The rest of the system is frozen at this point, so this is all that is running */ + while (!rvidcc_read_serial(&ch, 1)) + { + rvidcc_poll(); + dcc_tasklet_action(1); + } + + return (int)ch; +} + +/* KGDB flush routine - ensures each GDB packet is sent as one message (more efficient) */ +static void kgdb_flushDebugChar(void) +{ + size_t written = 0; + + /* The rest of the system is frozen at this point, so this is all that is running */ + while (written < kgdb_buf_chars) + { + written += dcc_write_serial(&kgdb_buf[written], kgdb_buf_chars - written); + rvidcc_poll(); + dcc_tasklet_action(1); + } + + kgdb_buf_chars = 0; +} + +/* KGDB putchar routine */ +static void kgdb_putDebugChar(int chr) +{ + kgdb_buf[kgdb_buf_chars++] = (char)chr; + if (kgdb_buf_chars >= KGDB_BUF_SIZE) + kgdb_flushDebugChar(); +} + +/* KGDB initialisation routine - can be called very early in kernel startup */ +static int init_kgdbDcc(void) +{ + int err = 0; + if (!dcc_info.init_called) + { + dcc_print_banner(); + err = dcc_init_dcc(); + if (!err) + printk(KERN_INFO "kgdb: debugging over DCC enabled\n"); + } + + return err; +} + +/* KGDB device driver structure - only one of these is allowed globally */ +struct kgdb_io kgdb_io_ops = +{ + .read_char = kgdb_getDebugChar, + .write_char = kgdb_putDebugChar, + .init = init_kgdbDcc, + .flush = kgdb_flushDebugChar +}; + +#else + +/* TTY device driver routines. Must peacefully co-exist with console support. + * + * Not present when KGDB is enabled. KGDB has its own driver format (above). + */ +/* TTY device open routine */ +static int dcc_chr_open(struct tty_struct *tty, struct file *filp) +{ + if (!dcc_info.tty_opens++) + dcc_info.tty = tty; + + return 0; +} + +/* TTY device close routine */ +static void dcc_chr_close(struct tty_struct *tty, struct file *filp) +{ + if (!--dcc_info.tty_opens) + dcc_info.tty = NULL; +} + +/* TTY device write routine */ +static int dcc_chr_write(struct tty_struct * tty, + const unsigned char *buf, int count) +{ + int bytes = 0; + + if (count == 0) + return 0; + + bytes = (int)dcc_write_serial(buf, count); + + if (dcc_config.use_timer_poll) + dcc_poll(); + + return bytes; +} + +/* TTY device routine to determine space left in output buffer */ +static int dcc_chr_write_room(struct tty_struct *tty) +{ + int space; + + if (dcc_config.use_timer_poll) + dcc_poll(); + + space = rvidcc_serial_can_write() - 4 ; /* Leave 1 word spare for sentinel*/ + return space > 0 ? space : 0; +} + +/* TTY device routine to determine number of chars waiting in input buffer */ +static int dcc_chr_chars_in_buffer(struct tty_struct *tty) +{ + if (dcc_config.use_timer_poll) + dcc_poll(); + + return rvidcc_serial_can_read(); +} + +/* TTY device ioctl routine. Global and affects virtual Ethernet as well */ +static int dcc_chr_ioctl(struct tty_struct *tty, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + int ret_val = 0; + + switch (cmd) { + case DCC_SETPOLLPERIOD: + dcc_config.timer_poll_period = arg; + break; + + case DCC_GETPOLLPERIOD: + ret_val = dcc_config.timer_poll_period; + break; + + default: + ret_val = -ENOIOCTLCMD; + break; + } + + return ret_val; +} + +#endif /* CONFIG_DEBUG_DCC_KGDB */ + +/* Network (virtual Ethernet over DCC) device driver routines + * + * Co-exists with KGDB, and will run when KGDB is at a breakpoint. + * Developer beware ! + */ +#ifndef CONFIG_DEBUG_DCC_RAW + +/* Network device driver structure */ +static struct net_device* dcc_netdev; + +/* Callback from the universal DCC driver when a MAC address arrives from the RVI */ +void rvidcc_cb_set_mac_address(unsigned char *mac) +{ + memcpy(dcc_info.mac_address, mac, ETH_ALEN); + + printk(KERN_INFO "DCC: MAC address %02x:%02x:%02x:%02x:%02x:%02x\n", dcc_info.mac_address[0], + dcc_info.mac_address[1], dcc_info.mac_address[2], dcc_info.mac_address[3], + dcc_info.mac_address[4], dcc_info.mac_address[5]); +} + + +/* Network device address validation */ +static int dcc_net_validate_addr(struct net_device *dev) +{ + /* Wait for the MAC address from the RVI */ + unsigned long startjif = jiffies; + while (dcc_info.mac_address[0] == 0 && + dcc_info.mac_address[1] == 0 && + dcc_info.mac_address[2] == 0 && + dcc_info.mac_address[3] == 0 && + dcc_info.mac_address[4] == 0 && + dcc_info.mac_address[5] == 0) + { + if (dcc_config.use_timer_poll) + dcc_poll(); + + /* Wait for a maximum of 1/2 a second */ + if (startjif - jiffies > HZ/2) + { + printk(KERN_ERR "DCC: Virtual ethernet timed out waiting for MAC address\n"); + break; + } + schedule(); + } + + memcpy(dev->dev_addr, dcc_info.mac_address, ETH_ALEN); + + if (!is_valid_ether_addr(dev->dev_addr)) + return -EINVAL; + + return 0; +} + +/* Network device open routine */ +static int dcc_net_open(struct net_device *dev) +{ + netif_start_queue(dev); + dcc_info.netdev = dev; /* mark as running */ + return 0; +} + +/* Network device close routine */ +static int dcc_net_close(struct net_device *dev) +{ + /* Discard any pending TX packet */ + if (dcc_info.pending_skb) + { + dev->stats.tx_dropped++; + dev_kfree_skb(dcc_info.pending_skb); + dcc_info.pending_skb = NULL; + } + + /* Mark as not running, to discard incoming packets */ + dcc_info.netdev = NULL; + + netif_stop_queue(dev); + return 0; +} + +/* Called by universal driver when an IP packet is received */ +void rvidcc_cb_ip_packet_received(void *packet, size_t length) +{ + /* Drop the packet if the device isn't open */ + if (dcc_info.netdev) + { + struct sk_buff *skb = dev_alloc_skb(length + 16); + struct ethhdr *buf; + + if (!skb) + { + dcc_info.netdev->stats.rx_dropped++; + return; + } + + /* Create Ethernet header */ + skb_reserve(skb,2); /* Force 16 byte alignment */ + buf = (struct ethhdr*)skb_put(skb, length + 14); + memcpy(buf->h_dest, dcc_info.mac_address, 6); + memcpy(&buf->h_source[3], &dcc_info.mac_address[3], 3); + buf->h_source[0] = 0x00; + buf->h_source[1] = 0x02; + buf->h_source[2] = 0xf7; /* RVI MAC address */ + buf->h_proto = htons(ETH_P_IP); + + /* Fill in the rest of the data */ + memcpy((char*)buf + sizeof(struct ethhdr), packet, length); + skb->dev = dcc_info.netdev; + skb->protocol = eth_type_trans(skb, dcc_info.netdev); + dcc_info.netdev->stats.rx_packets++; + dcc_info.netdev->stats.rx_bytes += length+14; + netif_rx(skb); + } +} + +/* Network device packet transmit routine */ +static int dcc_net_xmit(struct sk_buff *skb, struct net_device *dev) +{ + switch (ntohs(((struct ethhdr*)skb->data)->h_proto)) + { + case ETH_P_ARP: /* ARP protocol */ + { + struct sk_buff *skbreply = dev_alloc_skb(skb->len); + struct ethhdr *buf; + struct arphdr *arp; + unsigned char *arpdata, *origarpdata; + + if (!skbreply) + { + dcc_info.netdev->stats.tx_dropped++; + dev_kfree_skb(skb); + return 0; + } + + /* Set up manufactured ARP reply data */ + buf = (struct ethhdr*)skb_put(skbreply, skb->len); + arp = (struct arphdr*)((char*)buf + sizeof(struct ethhdr)); + arpdata = (unsigned char*)((char*)arp + sizeof(struct arphdr)); + origarpdata = skb->data + sizeof(struct ethhdr) + sizeof(struct arphdr); + memcpy(buf, skb->data, skb->len); + memcpy(buf->h_dest, dcc_info.mac_address, 6); + memcpy(&buf->h_source[3], &dcc_info.mac_address[3], 3); + buf->h_source[0] = 0x00; + buf->h_source[1] = 0x02; + buf->h_source[2] = 0xf7; /* RVI MAC address */ + arp->ar_op = htons(ARPOP_REPLY); + memcpy(&arpdata[0], buf->h_source, ETH_ALEN); + memcpy(&arpdata[10], buf->h_dest, ETH_ALEN); + memcpy(&arpdata[6], &origarpdata[16], 4); + memcpy(&arpdata[16], &origarpdata[6], 4); + + skbreply->dev = dcc_info.netdev; + skbreply->protocol = eth_type_trans(skbreply, dcc_info.netdev); + dcc_info.netdev->stats.rx_packets++; + dcc_info.netdev->stats.rx_bytes += skb->len; + dcc_info.netdev->stats.tx_packets++; + dcc_info.netdev->stats.tx_bytes += skb->len; + dev_kfree_skb(skb); + netif_rx(skbreply); + break; + } + case ETH_P_IP: /* IP protocol */ + { + if (dcc_transmit_ip_packet(&skb->data[14], skb->len-14)) /* discard eth header */ + { + dcc_info.netdev->stats.tx_packets++; + dcc_info.netdev->stats.tx_bytes += skb->len; + dev_kfree_skb(skb); + } + else + { + dcc_info.pending_skb = skb; + netif_stop_queue(dev); + } + + if (dcc_config.use_timer_poll) + dcc_poll(); + + break; + } + default: + printk(KERN_WARNING "DCC: dropping packet ethertype 0x%04x\n", + (int)ntohs(((struct ethhdr*)skb->data)->h_proto)); + dcc_info.netdev->stats.tx_dropped++; + dev_kfree_skb(skb); + } + + return 0; +} + +/* Universal driver callback to handle pseudo-ARP */ +int rvidcc_cb_has_addr(unsigned char *ip_binary) +{ + u32 ip; + memcpy(&ip, ip_binary, 4); + + if (!dcc_info.netdev || IN_LOOPBACK(ip) || IN_MULTICAST(ip)) + return 0; + + /* Return 1 if address is local to the target */ + return inet_addr_type(dev_net(dcc_info.netdev), ip) == RTN_LOCAL ? 1 : 0; +} + +/* Network device initialisation routine */ +static void dcc_net_init(struct net_device *dev) +{ + /* Like normal Ethernet, but with specific differences */ + ether_setup(dev); + dev->tx_queue_len = 3; + + /* Driver routines */ + dev->validate_addr = dcc_net_validate_addr; + dev->open = dcc_net_open; + dev->stop = dcc_net_close; + dev->hard_start_xmit = dcc_net_xmit; +} + +#endif /* !CONFIG_DEBUG_DCC_RAW */ + +/* Data transfer handling functionality + * + * We support interrupt and timer polled functionality + * if interrupts are not available. + */ +static void dcc_timer_poll(unsigned long arg); + +static struct timer_list dcc_timer = { + function: dcc_timer_poll +}; + +/* Poll routine for when interrupts are not used */ +static void dcc_poll(void) +{ + if (!test_and_set_bit(0, &dcc_info.timerpoll_lock) && + !dcc_info.kernel_in_panic) + { + rvidcc_poll(); + clear_bit(0, &dcc_info.timerpoll_lock); + } +} + +/* Self-recheduling timer poll routine */ +static void dcc_timer_poll(unsigned long arg) +{ + dcc_poll(); + dcc_timer.expires = jiffies + dcc_config.timer_poll_period; + add_timer(&dcc_timer); +} + +/* DCC interrupt handler */ +static irqreturn_t dcc_read_interrupt(int irq, void *devid) +{ + if (!dcc_info.kernel_in_panic) + rvidcc_read(); + return IRQ_HANDLED; +} + +/* DCC interrupt handler */ +static irqreturn_t dcc_write_interrupt(int irq, void *devid) +{ + if (!dcc_info.kernel_in_panic) + rvidcc_write(); + return IRQ_HANDLED; +} + +/* Routine to process incoming DCC data from interrupt or timer poll */ +static void dcc_tasklet_action(unsigned long data) +{ +#ifndef CONFIG_DEBUG_DCC_RAW + rvidcc_process(); + + /* Try to transmit any pending packet */ + if (dcc_info.pending_skb && dcc_info.netdev) + { + if (dcc_transmit_ip_packet(&dcc_info.pending_skb->data[14], dcc_info.pending_skb->len-14)) + { + dcc_info.netdev->stats.tx_packets++; + dcc_info.netdev->stats.tx_bytes += dcc_info.pending_skb->len; + dev_kfree_skb(dcc_info.pending_skb); + dcc_info.pending_skb = NULL; + netif_wake_queue(dcc_info.netdev); + } + } +#endif + + /* tasklet can only run on one CPU at a time and this is the only place + * we do a serial read, so no locking needed here */ +#ifdef CONFIG_DEBUG_DCC_KGDB + if (data != 1 && rvidcc_serial_can_read()) + { + /* Absorb ^C before triggering a hardcoded breakpoint */ + if (rvidcc_scan_input_for(0x03, 1)) + { + char buf; + rvidcc_read_serial(&buf, 1); + } + breakpoint(); + } +#else + /* Receive any bytes that may be pending if the device has been opened */ + if (dcc_info.tty) + { + size_t data_available = rvidcc_serial_can_read(); + if (data_available) + { + unsigned char* buf; + int len = tty_prepare_flip_string(dcc_info.tty, &buf, data_available); + if (len) + { + rvidcc_read_serial(buf, len); + tty_flip_buffer_push(dcc_info.tty); + } + } + } +#endif +} + +/* Universal driver callback called when data has arrived on DCC */ +void rvidcc_cb_notify() +{ + tasklet_schedule(&dcc_tasklet); +} + +/* Universal initialisation - ensures init only happens once */ +static int dcc_init_dcc(void) +{ + int err = 0; + if (!dcc_info.init_called) + { + err = rvidcc_init(); + if (err == 0) + dcc_info.init_called = 1; + else + return -ENODEV; + } + + return err; +} + +/* Banner to be printed on initialisation */ +static void dcc_print_banner(void) +{ + printk(KERN_INFO "RealView ICE DCC device driver %s " +#ifdef CONFIG_DEBUG_DCC_RAW + "[raw mode] " +#else + "[tty_eth mode] " +#endif + "(C)2004-2007 ARM Limited\n", ARM_DCC_VER); +} + +static void dcc_drain_outbuf(void) +{ + size_t bytesPending; + size_t lastBytesPending = 0; + int failcount = 0; + + while ((bytesPending = rvidcc_outbuf_data()) > 0) + { + if (bytesPending >= lastBytesPending) + ++failcount; + + if (failcount > 10) + { + /* The queue isn't draining: either the connection to the RVI is + * lost or we can't process messages fast enough. Either way, it's + * bad to block forever, so bail out */ + break; + } + + lastBytesPending = bytesPending; + + rvidcc_poll(); + } +} + +#ifndef CONFIG_DEBUG_DCC_KGDB + +/* TTY device driver structure */ +static const struct tty_operations dcc_tty_ops = +{ + .open = dcc_chr_open, + .close = dcc_chr_close, + .write = dcc_chr_write, + .write_room = dcc_chr_write_room, + .chars_in_buffer = dcc_chr_chars_in_buffer, + .ioctl = dcc_chr_ioctl +}; + +static struct tty_driver *dcc_tty_driver; + +/* Create and install TTY driver */ +static int dcc_init_tty_driver(void) +{ + int error = 0; + + dcc_tty_driver = alloc_tty_driver(1); + if (!dcc_tty_driver) + return -ENOMEM; + dcc_tty_driver->owner = THIS_MODULE; + dcc_tty_driver->driver_name = ARM_TTY_NAME; + dcc_tty_driver->name = ARM_TTY_NAME; + dcc_tty_driver->major = ARM_DCC_MAJOR; + dcc_tty_driver->minor_start = ARM_DCC_MINOR; + dcc_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + dcc_tty_driver->subtype = SERIAL_TYPE_NORMAL; + dcc_tty_driver->flags = TTY_DRIVER_REAL_RAW; + dcc_tty_driver->init_termios = tty_std_termios; + tty_set_operations(dcc_tty_driver, &dcc_tty_ops); + if ((error = tty_register_driver(dcc_tty_driver))) + { + printk(KERN_ERR "DCC: Can't register tty device %d, error = %d\n", + ARM_DCC_MINOR, error); + put_tty_driver(dcc_tty_driver); + dcc_tty_driver = NULL; + return error; + } + return 0; +} + +/* Console device print routine */ +static void dcc_console_print(struct console *co, const char *buf, unsigned count) +{ + unsigned int bytes = 0; + + /* Send message out */ + bytes = (unsigned int)dcc_write_serial(buf, count); + + /* If buffer is full stall the system until there is space */ + /* A flood of printk's usually means the system is dying anyway */ + if (bytes < count) + { + unsigned long flags; + local_irq_save(flags); + + while (bytes < count) + { + rvidcc_poll(); + bytes += (unsigned int)dcc_write_serial(buf + bytes, count - bytes); + } + + local_irq_restore(flags); + } + + /* Do an immediate flush if kernel has panicked */ + if (dcc_info.kernel_in_panic) + { + unsigned long flags; + local_irq_save(flags); + + dcc_drain_outbuf(); + + local_irq_restore(flags); + } +} + +/* Console device initialisation routine */ +static struct tty_driver* dcc_console_device(struct console *co, int *index) +{ + *index = 0; + return dcc_tty_driver; +} + +/* Console device driver */ +static struct console dcc_con_driver = +{ + .name = ARM_TTY_NAME, + .write = dcc_console_print, + .device = dcc_console_device, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + + +/* Routine to ensure DCC is flushed on reboot or kernel panic */ +static int dcc_reboot_handler(struct notifier_block *this, + unsigned long event, void *unused) +{ + unsigned long flags; + local_irq_save(flags); + + dcc_drain_outbuf(); + + local_irq_restore(flags); + + return NOTIFY_OK; +} + +/* Reboot notifier structure */ +static struct notifier_block dcc_reboot_notifier = +{ + dcc_reboot_handler, + NULL, + 0 +}; + +/* Called when kernel enters the panic state */ +static int dcc_panic_handler(struct notifier_block *this, + unsigned long event, void *unused) +{ + dcc_info.kernel_in_panic = 1; + return dcc_reboot_handler(this, event, unused); +} + +/* Panic notifier structure */ +static struct notifier_block dcc_panic_notifier = +{ + .notifier_call = dcc_panic_handler, + .next = NULL, + .priority = 0 +}; + +/* Early console initialization. Preceeds driver initialization. */ +static int __init dcc_console_init(void) +{ + int err = 0; + + if (!dcc_info.init_called) + { + dcc_print_banner(); + err = dcc_init_dcc(); + } + + if (!err) + { + register_reboot_notifier(&dcc_reboot_notifier); + atomic_notifier_chain_register(&panic_notifier_list, &dcc_panic_notifier); + register_console(&dcc_con_driver); + } + + return err; +} +#endif /* !CONFIG_DEBUG_DCC_KGDB */ + +/* Primary module (and devices) initialisation routine */ +int __init dcc_init(void) +{ + int ret = 0; + + if (!dcc_info.init_called) + dcc_print_banner(); + + /* Detect whether timer polling when required */ + if (dcc_config.rx_interrupt == dcc_config.tx_interrupt) + { + dcc_config.use_timer_poll = 1; + + if (dcc_config.rx_interrupt != 0) + printk(KERN_ERR "DCC: IRQ numbers must be different for Rx and Tx !!!!!\n"); + } + else + dcc_config.use_timer_poll = 0; + +#ifndef CONFIG_DEBUG_DCC_KGDB + ret = dcc_init_tty_driver(); +#endif + +#ifndef CONFIG_DEBUG_DCC_RAW + if (!ret) + { + /* Install network device driver */ + dcc_netdev = alloc_netdev(0, ARM_ETH_NAME, dcc_net_init); + if (dcc_netdev == NULL) + { + printk(KERN_ERR "DCC: Can't allocate net device\n"); + ret = -ENOMEM; + goto error; + } + else + { + ret = register_netdev(dcc_netdev); + + if (ret) + { + printk(KERN_ERR "DCC: Can't register net device\n"); + free_netdev(dcc_netdev); + goto error; + } + } + } +#endif + + /* Initiate interrupts or timer polling as necessary */ + if (!ret) + { + if (!dcc_config.use_timer_poll) + { + ret = dcc_init_dcc(); + + if (ret) + { + goto error; + } + else + { + if (request_irq(dcc_config.rx_interrupt, dcc_read_interrupt, 0, "dcc:rx", NULL) || + request_irq(dcc_config.tx_interrupt, dcc_write_interrupt, 0, "dcc:tx", NULL)) + { + printk(KERN_ERR "DCC: Can't install interrupt handlers for IRQ%u and/or IRQ%u\n", + dcc_config.rx_interrupt, dcc_config.tx_interrupt); + /* unregister tty & netdev drivers on error */ + ret = -EBUSY; + goto error; + } + else + printk(KERN_INFO "DCC: Using IRQ%u (Rx) and IRQ%u (Tx)\n", + dcc_config.rx_interrupt, dcc_config.tx_interrupt); + } + } + else + { + init_timer(&dcc_timer); + printk(KERN_INFO "DCC: Using timer polled mode\n"); + + ret = dcc_init_dcc(); + if (ret) + { + del_timer_sync(&dcc_timer); + goto error; + } + else + { + dcc_timer.expires = jiffies + dcc_config.timer_poll_period; + add_timer(&dcc_timer); + } + } + } + + return ret; + +error: + +#ifndef CONFIG_DEBUG_DCC_KGDB + if (dcc_tty_driver) + { + tty_unregister_driver(dcc_tty_driver); + put_tty_driver(dcc_tty_driver); + dcc_tty_driver = NULL; + } +#endif + +#ifndef CONFIG_DEBUG_DCC_RAW + if (dcc_netdev) + { + unregister_netdev(dcc_netdev); + free_netdev(dcc_netdev); + dcc_netdev = NULL; + } +#endif + + return ret; +} + +/* Module cleanup (exit) routine - only called if compiled as a module */ +void __exit dcc_cleanup(void) +{ + if (!dcc_config.use_timer_poll) + { + free_irq(dcc_config.rx_interrupt, NULL); + free_irq(dcc_config.tx_interrupt, NULL); + } + else + del_timer_sync(&dcc_timer); + +#ifndef CONFIG_DEBUG_DCC_KGDB + tty_unregister_driver(dcc_tty_driver); + put_tty_driver(dcc_tty_driver); +#endif + +#ifndef CONFIG_DEBUG_DCC_RAW + unregister_netdev(dcc_netdev); + free_netdev(dcc_netdev); +#endif + +#ifndef CONFIG_DEBUG_DCC_KGDB + unregister_console(&dcc_con_driver); + unregister_reboot_notifier(&dcc_reboot_notifier); + atomic_notifier_chain_unregister(&panic_notifier_list, &dcc_panic_notifier); +#endif + + dcc_drain_outbuf(); +} + +#ifndef CONFIG_DEBUG_DCC_KGDB +console_initcall(dcc_console_init); +#endif +module_init(dcc_init); +module_exit(dcc_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("ARM Ltd"); +MODULE_DESCRIPTION("Driver for the ARM Debug Communications Channel"); +MODULE_SUPPORTED_DEVICE(ARM_TTY_NAME); diff --git a/arch/arm/configs/realview-nommu_defconfig b/arch/arm/configs/realview-nommu_defconfig new file mode 100644 index 000000000000..71596a53e6ac --- /dev/null +++ b/arch/arm/configs/realview-nommu_defconfig @@ -0,0 +1,1373 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.28 +# Thu Feb 5 12:10:15 2009 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +# CONFIG_GENERIC_GPIO is not set +CONFIG_GENERIC_TIME=y +CONFIG_GENERIC_CLOCKEVENTS=y +# CONFIG_NO_IOPORT is not set +CONFIG_GENERIC_HARDIRQS=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_ARCH_HAS_ILOG2_U32 is not set +# CONFIG_ARCH_HAS_ILOG2_U64 is not set +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y +CONFIG_VECTORS_BASE=0x00000000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_CGROUPS is not set +# CONFIG_GROUP_SCHED is not set +# CONFIG_SYSFS_DEPRECATED_V2 is not set +# CONFIG_RELAY is not set +CONFIG_NAMESPACES=y +# CONFIG_UTS_NS is not set +# CONFIG_IPC_NS is not set +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +# CONFIG_EMBEDDED is not set +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_COMPAT_BRK=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_ANON_INODES=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_AIO=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_SLUB_DEBUG=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +# CONFIG_MARKERS is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_TINY_SHMEM=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_KMOD=y +CONFIG_BLOCK=y +# CONFIG_LBD is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_LSF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +CONFIG_IOSCHED_DEADLINE=y +# CONFIG_IOSCHED_CFQ is not set +# CONFIG_DEFAULT_AS is not set +CONFIG_DEFAULT_DEADLINE=y +# CONFIG_DEFAULT_CFQ is not set +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="deadline" +CONFIG_CLASSIC_RCU=y +# CONFIG_FREEZER is not set + +# +# System Type +# +# CONFIG_MMU is not set +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +CONFIG_ARCH_REALVIEW=y +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_ARCH_MSM is not set + +# +# Boot options +# + +# +# Power management +# + +# +# RealView platform type +# +CONFIG_MACH_REALVIEW_EB=y +# CONFIG_REALVIEW_EB_A9MP is not set +CONFIG_REALVIEW_EB_ARM11MP=y +# CONFIG_REALVIEW_EB_ARM11MP_REVB is not set +CONFIG_MACH_REALVIEW_PB11MP=y +CONFIG_MACH_REALVIEW_PB1176=y +# CONFIG_MACH_REALVIEW_PBA8 is not set +CONFIG_MACH_REALVIEW_PBX=y + +# +# Processor Type +# +CONFIG_CPU_32=y +# CONFIG_CPU_ARM7TDMI is not set +# CONFIG_CPU_ARM9TDMI is not set +# CONFIG_CPU_ARM926T is not set +CONFIG_CPU_V6=y +# CONFIG_CPU_32v6K is not set +# CONFIG_CPU_V7 is not set +CONFIG_CPU_32v6=y +CONFIG_CPU_ABRT_EV6=y +CONFIG_CPU_PABRT_NOIFAR=y +CONFIG_CPU_CACHE_V6=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +# CONFIG_CPU_HIGH_VECTOR is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_TLS_REG_EMUL=y +CONFIG_OUTER_CACHE=y +CONFIG_CACHE_L2X0=y +CONFIG_SET_MEM_PARAM=y +CONFIG_DRAM_BASE=0x00000000 +CONFIG_DRAM_SIZE=0x10000000 +CONFIG_FLASH_MEM_BASE=0x40000000 +CONFIG_FLASH_SIZE=0x04000000 +CONFIG_ARM_GIC=y +CONFIG_ICST307=y + +# +# Bus support +# +CONFIG_ARM_AMBA=y +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +# CONFIG_NO_HZ is not set +# CONFIG_HIGH_RES_TIMERS is not set +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +# CONFIG_SMP is not set +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +# CONFIG_ARM_ASM_UNIFIED is not set +CONFIG_AEABI=y +CONFIG_OABI_COMPAT=y +CONFIG_ARCH_FLATMEM_HAS_HOLES=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_RESOURCES_64BIT is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +CONFIG_ALIGNMENT_TRAP=y + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="root=/dev/nfs nfsroot=10.1.69.3:/work/nfsroot ip=dhcp console=ttyAMA0 mem=128M" +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set + +# +# CPU Power Management +# +# CONFIG_CPU_IDLE is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +# CONFIG_FPE_NWFPE is not set +# CONFIG_FPE_FASTFPE is not set +CONFIG_VFP=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_FLAT=y +CONFIG_BINFMT_ZFLAT=y +CONFIG_BINFMT_SHARED_FLAT=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +# CONFIG_PM is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +# CONFIG_IPV6 is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETFILTER is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_SCHED is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set +# CONFIG_PHONET is not set +# CONFIG_WIRELESS is not set +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +CONFIG_MTD_CONCAT=y +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=y +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_GEN_PROBE=y +# CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +CONFIG_MTD_CFI_INTELEXT=y +CONFIG_MTD_CFI_AMDSTD=y +# CONFIG_MTD_CFI_STAA is not set +CONFIG_MTD_CFI_UTIL=y +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PHYSMAP is not set +CONFIG_MTD_ARM_INTEGRATOR=y +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# UBI - Unsorted block images +# +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +CONFIG_MISC_DEVICES=y +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_C2PORT is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_SCSI_PROC_FS is not set + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_SCH is not set + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +CONFIG_SCSI_WAIT_SCAN=m + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +# CONFIG_SCSI_LOWLEVEL is not set +# CONFIG_SCSI_DH is not set +CONFIG_ATA=y +# CONFIG_ATA_NONSTANDARD is not set +# CONFIG_SATA_PMP is not set +CONFIG_ATA_SFF=y +# CONFIG_SATA_MV is not set +CONFIG_HAVE_PATA_PLATFORM=y +CONFIG_PATA_PLATFORM=y +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_PHYLIB is not set +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_AX88796 is not set +CONFIG_SMC91X=y +# CONFIG_DM9000 is not set +CONFIG_SMC911X=y +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set + +# +# Wireless LAN +# +# CONFIG_WLAN_PRE80211 is not set +# CONFIG_WLAN_80211 is not set +# CONFIG_IWLWIFI_LEDS is not set + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_USBNET is not set +# CONFIG_WAN is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +CONFIG_INPUT_MOUSE=y +CONFIG_MOUSE_PS2=y +CONFIG_MOUSE_PS2_ALPS=y +CONFIG_MOUSE_PS2_LOGIPS2PP=y +CONFIG_MOUSE_PS2_SYNAPTICS=y +CONFIG_MOUSE_PS2_LIFEBOOK=y +CONFIG_MOUSE_PS2_TRACKPOINT=y +# CONFIG_MOUSE_PS2_ELANTECH is not set +# CONFIG_MOUSE_PS2_TOUCHKIT is not set +# CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_APPLETOUCH is not set +# CONFIG_MOUSE_BCM5974 is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +# CONFIG_SERIO_SERPORT is not set +CONFIG_SERIO_AMBAKMI=y +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_VT_HW_CONSOLE_BINDING is not set +CONFIG_DEVKMEM=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_AMBA_PL010 is not set +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=16 +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_NVRAM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +# CONFIG_I2C_CHARDEV is not set +CONFIG_I2C_HELPER_AUTO=y +CONFIG_I2C_ALGOBIT=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_SIMTEC is not set +CONFIG_I2C_VERSATILE=y + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_STUB is not set + +# +# Miscellaneous I2C Chip support +# +# CONFIG_DS1682 is not set +# CONFIG_AT24 is not set +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_PCF8575 is not set +# CONFIG_SENSORS_PCA9539 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_MAX6875 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set +# CONFIG_SPI is not set +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +# CONFIG_THERMAL_HWMON is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM8350_I2C is not set + +# +# Multimedia devices +# + +# +# Multimedia core support +# +# CONFIG_VIDEO_DEV is not set +# CONFIG_DVB_CORE is not set +# CONFIG_VIDEO_MEDIA is not set + +# +# Multimedia drivers +# +# CONFIG_DAB is not set + +# +# Graphics support +# +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +CONFIG_FB_ARMCLCD=y +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set +# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set +# CONFIG_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_LOGO_LINUX_CLUT224=y +CONFIG_SOUND=y +CONFIG_SOUND_OSS_CORE=y +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +# CONFIG_SND_SEQUENCER is not set +CONFIG_SND_OSSEMUL=y +CONFIG_SND_MIXER_OSS=y +CONFIG_SND_PCM_OSS=y +CONFIG_SND_PCM_OSS_PLUGINS=y +# CONFIG_SND_DYNAMIC_MINORS is not set +CONFIG_SND_SUPPORT_OLD_API=y +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set +CONFIG_SND_VMASTER=y +CONFIG_SND_AC97_CODEC=y +# CONFIG_SND_DRIVERS is not set +CONFIG_SND_ARM=y +CONFIG_SND_ARMAACI=y +CONFIG_SND_USB=y +# CONFIG_SND_USB_AUDIO is not set +# CONFIG_SND_USB_CAIAQ is not set +# CONFIG_SND_SOC is not set +# CONFIG_SOUND_PRIME is not set +CONFIG_AC97_BUS=y +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HID_DEBUG is not set +# CONFIG_HIDRAW is not set + +# +# USB Input Devices +# +CONFIG_USB_HID=y +# CONFIG_HID_PID is not set +# CONFIG_USB_HIDDEV is not set + +# +# Special HID drivers +# +CONFIG_HID_COMPAT=y +CONFIG_HID_A4TECH=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_BRIGHT=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_CYPRESS=y +CONFIG_HID_DELL=y +CONFIG_HID_EZKEY=y +CONFIG_HID_GYRATION=y +CONFIG_HID_LOGITECH=y +# CONFIG_LOGITECH_FF is not set +# CONFIG_LOGIRUMBLEPAD2_FF is not set +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +CONFIG_HID_PANTHERLORD=y +# CONFIG_PANTHERLORD_FF is not set +CONFIG_HID_PETALYNX=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +CONFIG_HID_SUNPLUS=y +# CONFIG_THRUSTMASTER_FF is not set +# CONFIG_ZEROPLUS_FF is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_DEVICE_CLASS is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG is not set +CONFIG_USB_MON=y +# CONFIG_USB_WUSB is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +CONFIG_USB_ISP1760_HCD=y +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_HWA_HCD is not set +# CONFIG_USB_MUSB_HDRC is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may also be needed; +# + +# +# see USB_STORAGE Help for more information +# +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_ONETOUCH is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_BERRY_CHARGE is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_PHIDGET is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_VST is not set +# CONFIG_USB_GADGET is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_BOUNCE=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +CONFIG_MMC_ARMMMCI=y +# CONFIG_MMC_SDHCI is not set +# CONFIG_MEMSTICK is not set +# CONFIG_ACCESSIBILITY is not set +# CONFIG_NEW_LEDS is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +CONFIG_RTC_DRV_DS1307=y +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set + +# +# SPI RTC drivers +# + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +# CONFIG_RTC_DRV_PL030 is not set +# CONFIG_RTC_DRV_PL031 is not set +# CONFIG_DMADEVICES is not set +# CONFIG_REGULATOR is not set +# CONFIG_UIO is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +CONFIG_EXT3_FS=y +# CONFIG_EXT3_FS_XATTR is not set +# CONFIG_EXT4_FS is not set +CONFIG_JBD=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +# CONFIG_XFS_FS is not set +# CONFIG_OCFS2_FS is not set +CONFIG_DNOTIFY=y +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +# CONFIG_MSDOS_FS is not set +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS2_FS is not set +CONFIG_CRAMFS=y +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +CONFIG_ROMFS_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_REGISTER_V4 is not set +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set +# CONFIG_DLM is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +CONFIG_DETECT_SOFTLOCKUP=y +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 +# CONFIG_SCHED_DEBUG is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +CONFIG_DEBUG_MEMORY_INIT=y +# CONFIG_DEBUG_LIST is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_CPU_STALL_DETECTOR is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_SYSCTL_SYSCALL_CHECK is not set +CONFIG_HAVE_FUNCTION_TRACER=y + +# +# Tracers +# +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_SCHED_TRACER is not set +# CONFIG_CONTEXT_SWITCH_TRACER is not set +# CONFIG_BOOT_TRACER is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_DYNAMIC_PRINTK_DEBUG is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +CONFIG_ARM_UNWIND=y +CONFIG_DEBUG_USER=y +CONFIG_DEBUG_ERRORS=y +# CONFIG_DEBUG_STACK_USAGE is not set +CONFIG_DEBUG_LL=y +CONFIG_DEBUG_LL_CONSOLE=y +# CONFIG_DEBUG_ICEDCC is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +# CONFIG_SECURITY_FILE_CAPABILITIES is not set +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +# CONFIG_CRYPTO_FIPS is not set +# CONFIG_CRYPTO_MANAGER is not set +# CONFIG_CRYPTO_MANAGER2 is not set +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_XCBC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +# CONFIG_CRYPTO_AES is not set +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_HW is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC16 is not set +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_PLIST=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y diff --git a/arch/arm/configs/realview_defconfig b/arch/arm/configs/realview_defconfig index 7e253f58ed18..907e54344dad 100644 --- a/arch/arm/configs/realview_defconfig +++ b/arch/arm/configs/realview_defconfig @@ -1,204 +1,105 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.28-rc2 -# Mon Nov 10 14:39:48 2008 +# Linux kernel version: 2.6.14-rc2 +# Thu Sep 29 14:50:10 2005 # CONFIG_ARM=y -CONFIG_SYS_SUPPORTS_APM_EMULATION=y -# CONFIG_GENERIC_GPIO is not set -CONFIG_GENERIC_TIME=y -CONFIG_GENERIC_CLOCKEVENTS=y CONFIG_MMU=y -# CONFIG_NO_IOPORT is not set -CONFIG_GENERIC_HARDIRQS=y -CONFIG_STACKTRACE_SUPPORT=y -CONFIG_HAVE_LATENCYTOP_SUPPORT=y -CONFIG_LOCKDEP_SUPPORT=y -CONFIG_TRACE_IRQFLAGS_SUPPORT=y -CONFIG_HARDIRQS_SW_RESEND=y -CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_UID16=y CONFIG_RWSEM_GENERIC_SPINLOCK=y -# CONFIG_ARCH_HAS_ILOG2_U32 is not set -# CONFIG_ARCH_HAS_ILOG2_U64 is not set -CONFIG_GENERIC_HWEIGHT=y CONFIG_GENERIC_CALIBRATE_DELAY=y -CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y -CONFIG_VECTORS_BASE=0xffff0000 -CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" # -# General setup +# Code maturity level options # -CONFIG_EXPERIMENTAL=y +# CONFIG_EXPERIMENTAL is not set +CONFIG_CLEAN_COMPILE=y CONFIG_BROKEN_ON_SMP=y CONFIG_INIT_ENV_ARG_LIMIT=32 + +# +# General setup +# CONFIG_LOCALVERSION="" CONFIG_LOCALVERSION_AUTO=y # CONFIG_SWAP is not set CONFIG_SYSVIPC=y -CONFIG_SYSVIPC_SYSCTL=y -# CONFIG_POSIX_MQUEUE is not set # CONFIG_BSD_PROCESS_ACCT is not set -# CONFIG_TASKSTATS is not set +CONFIG_SYSCTL=y # CONFIG_AUDIT is not set +CONFIG_HOTPLUG=y +CONFIG_KOBJECT_UEVENT=y # CONFIG_IKCONFIG is not set -CONFIG_LOG_BUF_SHIFT=14 -# CONFIG_CGROUPS is not set -# CONFIG_GROUP_SCHED is not set -CONFIG_SYSFS_DEPRECATED=y -CONFIG_SYSFS_DEPRECATED_V2=y -# CONFIG_RELAY is not set -CONFIG_NAMESPACES=y -# CONFIG_UTS_NS is not set -# CONFIG_IPC_NS is not set -# CONFIG_USER_NS is not set -# CONFIG_PID_NS is not set -# CONFIG_BLK_DEV_INITRD is not set -CONFIG_CC_OPTIMIZE_FOR_SIZE=y -CONFIG_SYSCTL=y +CONFIG_INITRAMFS_SOURCE="" # CONFIG_EMBEDDED is not set -CONFIG_UID16=y -CONFIG_SYSCTL_SYSCALL=y CONFIG_KALLSYMS=y # CONFIG_KALLSYMS_ALL is not set # CONFIG_KALLSYMS_EXTRA_PASS is not set -CONFIG_HOTPLUG=y CONFIG_PRINTK=y CONFIG_BUG=y -CONFIG_ELF_CORE=y -CONFIG_COMPAT_BRK=y CONFIG_BASE_FULL=y CONFIG_FUTEX=y -CONFIG_ANON_INODES=y CONFIG_EPOLL=y -CONFIG_SIGNALFD=y -CONFIG_TIMERFD=y -CONFIG_EVENTFD=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_SHMEM=y -CONFIG_AIO=y -CONFIG_VM_EVENT_COUNTERS=y -CONFIG_SLAB=y -# CONFIG_SLUB is not set -# CONFIG_SLOB is not set -# CONFIG_PROFILING is not set -# CONFIG_MARKERS is not set -CONFIG_HAVE_OPROFILE=y -# CONFIG_KPROBES is not set -CONFIG_HAVE_KPROBES=y -CONFIG_HAVE_KRETPROBES=y -CONFIG_HAVE_CLK=y -CONFIG_HAVE_GENERIC_DMA_COHERENT=y -CONFIG_SLABINFO=y -CONFIG_RT_MUTEXES=y +CONFIG_CC_ALIGN_FUNCTIONS=0 +CONFIG_CC_ALIGN_LABELS=0 +CONFIG_CC_ALIGN_LOOPS=0 +CONFIG_CC_ALIGN_JUMPS=0 # CONFIG_TINY_SHMEM is not set CONFIG_BASE_SMALL=0 -CONFIG_MODULES=y -# CONFIG_MODULE_FORCE_LOAD is not set -CONFIG_MODULE_UNLOAD=y -# CONFIG_MODULE_FORCE_UNLOAD is not set -# CONFIG_MODVERSIONS is not set -# CONFIG_MODULE_SRCVERSION_ALL is not set -CONFIG_KMOD=y -CONFIG_BLOCK=y -# CONFIG_LBD is not set -# CONFIG_BLK_DEV_IO_TRACE is not set -# CONFIG_LSF is not set -# CONFIG_BLK_DEV_BSG is not set -# CONFIG_BLK_DEV_INTEGRITY is not set # -# IO Schedulers +# Loadable module support # -CONFIG_IOSCHED_NOOP=y -# CONFIG_IOSCHED_AS is not set -CONFIG_IOSCHED_DEADLINE=y -# CONFIG_IOSCHED_CFQ is not set -# CONFIG_DEFAULT_AS is not set -CONFIG_DEFAULT_DEADLINE=y -# CONFIG_DEFAULT_CFQ is not set -# CONFIG_DEFAULT_NOOP is not set -CONFIG_DEFAULT_IOSCHED="deadline" -CONFIG_CLASSIC_RCU=y -# CONFIG_FREEZER is not set +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODULE_SRCVERSION_ALL is not set +# CONFIG_KMOD is not set # # System Type # -# CONFIG_ARCH_AAEC2000 is not set -# CONFIG_ARCH_INTEGRATOR is not set -CONFIG_ARCH_REALVIEW=y -# CONFIG_ARCH_VERSATILE is not set -# CONFIG_ARCH_AT91 is not set # CONFIG_ARCH_CLPS7500 is not set # CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CO285 is not set # CONFIG_ARCH_EBSA110 is not set -# CONFIG_ARCH_EP93XX is not set # CONFIG_ARCH_FOOTBRIDGE is not set -# CONFIG_ARCH_NETX is not set -# CONFIG_ARCH_H720X is not set -# CONFIG_ARCH_IMX is not set -# CONFIG_ARCH_IOP13XX is not set -# CONFIG_ARCH_IOP32X is not set -# CONFIG_ARCH_IOP33X is not set -# CONFIG_ARCH_IXP23XX is not set -# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_IOP3XX is not set # CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_IXP2000 is not set # CONFIG_ARCH_L7200 is not set -# CONFIG_ARCH_KIRKWOOD is not set -# CONFIG_ARCH_KS8695 is not set -# CONFIG_ARCH_NS9XXX is not set -# CONFIG_ARCH_LOKI is not set -# CONFIG_ARCH_MV78XX0 is not set -# CONFIG_ARCH_MXC is not set -# CONFIG_ARCH_ORION5X is not set -# CONFIG_ARCH_PNX4008 is not set # CONFIG_ARCH_PXA is not set # CONFIG_ARCH_RPC is not set # CONFIG_ARCH_SA1100 is not set # CONFIG_ARCH_S3C2410 is not set # CONFIG_ARCH_SHARK is not set # CONFIG_ARCH_LH7A40X is not set -# CONFIG_ARCH_DAVINCI is not set # CONFIG_ARCH_OMAP is not set -# CONFIG_ARCH_MSM is not set - -# -# Boot options -# - -# -# Power management -# +# CONFIG_ARCH_VERSATILE is not set +CONFIG_ARCH_REALVIEW=y +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_AAEC2000 is not set # # RealView platform type # CONFIG_MACH_REALVIEW_EB=y -# CONFIG_REALVIEW_EB_A9MP is not set -CONFIG_REALVIEW_EB_ARM11MP=y -# CONFIG_REALVIEW_EB_ARM11MP_REVB is not set -CONFIG_MACH_REALVIEW_PB11MP=y -CONFIG_MACH_REALVIEW_PB1176=y -# CONFIG_MACH_REALVIEW_PBA8 is not set # # Processor Type # CONFIG_CPU_32=y -# CONFIG_CPU_ARM926T is not set -CONFIG_CPU_V6=y -# CONFIG_CPU_32v6K is not set -# CONFIG_CPU_V7 is not set -CONFIG_CPU_32v6=y -CONFIG_CPU_ABRT_EV6=y -CONFIG_CPU_PABRT_NOIFAR=y -CONFIG_CPU_CACHE_V6=y -CONFIG_CPU_CACHE_VIPT=y -CONFIG_CPU_COPY_V6=y -CONFIG_CPU_TLB_V6=y -CONFIG_CPU_HAS_ASID=y -CONFIG_CPU_CP15=y -CONFIG_CPU_CP15_MMU=y +CONFIG_CPU_ARM926T=y +# CONFIG_CPU_V6 is not set +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y # # Processor Features @@ -206,9 +107,8 @@ CONFIG_CPU_CP15_MMU=y CONFIG_ARM_THUMB=y # CONFIG_CPU_ICACHE_DISABLE is not set # CONFIG_CPU_DCACHE_DISABLE is not set -# CONFIG_CPU_BPREDICT_DISABLE is not set -CONFIG_OUTER_CACHE=y -CONFIG_CACHE_L2X0=y +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set CONFIG_ARM_GIC=y CONFIG_ICST307=y @@ -216,41 +116,20 @@ CONFIG_ICST307=y # Bus support # CONFIG_ARM_AMBA=y -# CONFIG_PCI_SYSCALL is not set -# CONFIG_ARCH_SUPPORTS_MSI is not set +CONFIG_ISA_DMA_API=y + +# +# PCCARD (PCMCIA/CardBus) support +# # CONFIG_PCCARD is not set # # Kernel Features # -# CONFIG_NO_HZ is not set -# CONFIG_HIGH_RES_TIMERS is not set -CONFIG_GENERIC_CLOCKEVENTS_BUILD=y -# CONFIG_SMP is not set -CONFIG_VMSPLIT_3G=y -# CONFIG_VMSPLIT_2G is not set -# CONFIG_VMSPLIT_1G is not set -CONFIG_PAGE_OFFSET=0xC0000000 -# CONFIG_PREEMPT is not set -CONFIG_HZ=100 -CONFIG_AEABI=y -CONFIG_OABI_COMPAT=y -CONFIG_ARCH_FLATMEM_HAS_HOLES=y -# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set -# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set -CONFIG_SELECT_MEMORY_MODEL=y -CONFIG_FLATMEM_MANUAL=y -# CONFIG_DISCONTIGMEM_MANUAL is not set -# CONFIG_SPARSEMEM_MANUAL is not set +# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set CONFIG_FLATMEM=y CONFIG_FLAT_NODE_MEM_MAP=y -CONFIG_PAGEFLAGS_EXTENDED=y -CONFIG_SPLIT_PTLOCK_CPUS=4 -# CONFIG_RESOURCES_64BIT is not set -# CONFIG_PHYS_ADDR_T_64BIT is not set -CONFIG_ZONE_DMA_FLAG=0 -CONFIG_VIRT_TO_BUS=y -CONFIG_UNEVICTABLE_LRU=y +# CONFIG_SPARSEMEM_STATIC is not set CONFIG_ALIGNMENT_TRAP=y # @@ -260,12 +139,6 @@ CONFIG_ZBOOT_ROM_TEXT=0x0 CONFIG_ZBOOT_ROM_BSS=0x0 CONFIG_CMDLINE="root=/dev/nfs nfsroot=10.1.69.3:/work/nfsroot ip=dhcp console=ttyAMA0 mem=128M" # CONFIG_XIP_KERNEL is not set -# CONFIG_KEXEC is not set - -# -# CPU Power Management -# -# CONFIG_CPU_IDLE is not set # # Floating point emulation @@ -274,24 +147,26 @@ CONFIG_CMDLINE="root=/dev/nfs nfsroot=10.1.69.3:/work/nfsroot ip=dhcp console=tt # # At least one emulation must be selected # -# CONFIG_FPE_NWFPE is not set -# CONFIG_FPE_FASTFPE is not set -CONFIG_VFP=y +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_VFP is not set # # Userspace binary formats # CONFIG_BINFMT_ELF=y -# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set -CONFIG_HAVE_AOUT=y # CONFIG_BINFMT_AOUT is not set # CONFIG_BINFMT_MISC is not set +# CONFIG_ARTHUR is not set # # Power management options # # CONFIG_PM is not set -CONFIG_ARCH_SUSPEND_POSSIBLE=y + +# +# Networking +# CONFIG_NET=y # @@ -300,11 +175,6 @@ CONFIG_NET=y CONFIG_PACKET=y # CONFIG_PACKET_MMAP is not set CONFIG_UNIX=y -CONFIG_XFRM=y -# CONFIG_XFRM_USER is not set -# CONFIG_XFRM_SUB_POLICY is not set -# CONFIG_XFRM_MIGRATE is not set -# CONFIG_XFRM_STATISTICS is not set # CONFIG_NET_KEY is not set CONFIG_INET=y # CONFIG_IP_MULTICAST is not set @@ -316,56 +186,34 @@ CONFIG_IP_PNP_BOOTP=y # CONFIG_IP_PNP_RARP is not set # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE is not set -# CONFIG_ARPD is not set # CONFIG_SYN_COOKIES is not set # CONFIG_INET_AH is not set # CONFIG_INET_ESP is not set # CONFIG_INET_IPCOMP is not set -# CONFIG_INET_XFRM_TUNNEL is not set # CONFIG_INET_TUNNEL is not set -CONFIG_INET_XFRM_MODE_TRANSPORT=y -CONFIG_INET_XFRM_MODE_TUNNEL=y -CONFIG_INET_XFRM_MODE_BEET=y -# CONFIG_INET_LRO is not set CONFIG_INET_DIAG=y CONFIG_INET_TCP_DIAG=y # CONFIG_TCP_CONG_ADVANCED is not set -CONFIG_TCP_CONG_CUBIC=y -CONFIG_DEFAULT_TCP_CONG="cubic" -# CONFIG_TCP_MD5SIG is not set +CONFIG_TCP_CONG_BIC=y # CONFIG_IPV6 is not set -# CONFIG_NETWORK_SECMARK is not set # CONFIG_NETFILTER is not set -# CONFIG_IP_DCCP is not set -# CONFIG_IP_SCTP is not set -# CONFIG_TIPC is not set -# CONFIG_ATM is not set # CONFIG_BRIDGE is not set -# CONFIG_NET_DSA is not set # CONFIG_VLAN_8021Q is not set # CONFIG_DECNET is not set # CONFIG_LLC2 is not set # CONFIG_IPX is not set # CONFIG_ATALK is not set -# CONFIG_X25 is not set -# CONFIG_LAPB is not set -# CONFIG_ECONET is not set -# CONFIG_WAN_ROUTER is not set # CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set # # Network testing # # CONFIG_NET_PKTGEN is not set # CONFIG_HAMRADIO is not set -# CONFIG_CAN is not set # CONFIG_IRDA is not set # CONFIG_BT is not set -# CONFIG_AF_RXRPC is not set -# CONFIG_PHONET is not set -# CONFIG_WIRELESS is not set -# CONFIG_RFKILL is not set -# CONFIG_NET_9P is not set +# CONFIG_IEEE80211 is not set # # Device Drivers @@ -374,37 +222,30 @@ CONFIG_DEFAULT_TCP_CONG="cubic" # # Generic Driver Options # -CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" CONFIG_STANDALONE=y CONFIG_PREVENT_FIRMWARE_BUILD=y -CONFIG_FW_LOADER=y -CONFIG_FIRMWARE_IN_KERNEL=y -CONFIG_EXTRA_FIRMWARE="" +# CONFIG_FW_LOADER is not set # CONFIG_DEBUG_DRIVER is not set -# CONFIG_DEBUG_DEVRES is not set -# CONFIG_SYS_HYPERVISOR is not set -# CONFIG_CONNECTOR is not set + +# +# Memory Technology Devices (MTD) +# CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set -CONFIG_MTD_CONCAT=y +# CONFIG_MTD_CONCAT is not set CONFIG_MTD_PARTITIONS=y # CONFIG_MTD_REDBOOT_PARTS is not set CONFIG_MTD_CMDLINE_PARTS=y # CONFIG_MTD_AFS_PARTS is not set -# CONFIG_MTD_AR7_PARTS is not set # # User Modules And Translation Layers # CONFIG_MTD_CHAR=y -CONFIG_MTD_BLKDEVS=y CONFIG_MTD_BLOCK=y # CONFIG_FTL is not set # CONFIG_NFTL is not set # CONFIG_INFTL is not set -# CONFIG_RFD_FTL is not set -# CONFIG_SSFDC is not set -# CONFIG_MTD_OOPS is not set # # RAM/ROM/Flash chip drivers @@ -425,6 +266,7 @@ CONFIG_MTD_CFI_I2=y # CONFIG_MTD_CFI_I8 is not set CONFIG_MTD_CFI_INTELEXT=y CONFIG_MTD_CFI_AMDSTD=y +CONFIG_MTD_CFI_AMDSTD_RETRY=0 # CONFIG_MTD_CFI_STAA is not set CONFIG_MTD_CFI_UTIL=y # CONFIG_MTD_RAM is not set @@ -437,6 +279,7 @@ CONFIG_MTD_CFI_UTIL=y # CONFIG_MTD_COMPLEX_MAPPINGS is not set # CONFIG_MTD_PHYSMAP is not set CONFIG_MTD_ARM_INTEGRATOR=y +# CONFIG_MTD_EDB7312 is not set # CONFIG_MTD_PLATRAM is not set # @@ -445,7 +288,7 @@ CONFIG_MTD_ARM_INTEGRATOR=y # CONFIG_MTD_SLRAM is not set # CONFIG_MTD_PHRAM is not set # CONFIG_MTD_MTDRAM is not set -# CONFIG_MTD_BLOCK2MTD is not set +# CONFIG_MTD_BLKMTD is not set # # Disk-On-Chip Device Drivers @@ -453,81 +296,121 @@ CONFIG_MTD_ARM_INTEGRATOR=y # CONFIG_MTD_DOC2000 is not set # CONFIG_MTD_DOC2001 is not set # CONFIG_MTD_DOC2001PLUS is not set + +# +# NAND Flash Device Drivers +# # CONFIG_MTD_NAND is not set -# CONFIG_MTD_ONENAND is not set # -# UBI - Unsorted block images +# Parallel port support # -# CONFIG_MTD_UBI is not set # CONFIG_PARPORT is not set -CONFIG_BLK_DEV=y + +# +# Plug and Play support +# + +# +# Block devices +# # CONFIG_BLK_DEV_COW_COMMON is not set # CONFIG_BLK_DEV_LOOP is not set # CONFIG_BLK_DEV_NBD is not set # CONFIG_BLK_DEV_RAM is not set +CONFIG_BLK_DEV_RAM_COUNT=16 # CONFIG_CDROM_PKTCDVD is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +CONFIG_IOSCHED_DEADLINE=y +# CONFIG_IOSCHED_CFQ is not set # CONFIG_ATA_OVER_ETH is not set -CONFIG_MISC_DEVICES=y -# CONFIG_EEPROM_93CX6 is not set -# CONFIG_ENCLOSURE_SERVICES is not set -CONFIG_HAVE_IDE=y -# CONFIG_IDE is not set # # SCSI device support # # CONFIG_RAID_ATTRS is not set # CONFIG_SCSI is not set -# CONFIG_SCSI_DMA is not set -# CONFIG_SCSI_NETLINK is not set -# CONFIG_ATA is not set + +# +# Multi-device support (RAID and LVM) +# # CONFIG_MD is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# Network device support +# CONFIG_NETDEVICES=y # CONFIG_DUMMY is not set # CONFIG_BONDING is not set -# CONFIG_MACVLAN is not set # CONFIG_EQUALIZER is not set # CONFIG_TUN is not set -# CONFIG_VETH is not set + +# +# PHY device support +# # CONFIG_PHYLIB is not set + +# +# Ethernet (10 or 100Mbit) +# CONFIG_NET_ETHERNET=y CONFIG_MII=y -# CONFIG_AX88796 is not set CONFIG_SMC91X=y # CONFIG_DM9000 is not set -CONFIG_SMC911X=y -# CONFIG_IBM_NEW_EMAC_ZMII is not set -# CONFIG_IBM_NEW_EMAC_RGMII is not set -# CONFIG_IBM_NEW_EMAC_TAH is not set -# CONFIG_IBM_NEW_EMAC_EMAC4 is not set -# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set -# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set -# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set -# CONFIG_B44 is not set -# CONFIG_NETDEV_1000 is not set -# CONFIG_NETDEV_10000 is not set - -# -# Wireless LAN -# -# CONFIG_WLAN_PRE80211 is not set -# CONFIG_WLAN_80211 is not set -# CONFIG_IWLWIFI_LEDS is not set + +# +# Ethernet (1000 Mbit) +# + +# +# Ethernet (10000 Mbit) +# + +# +# Token Ring devices +# + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Wan interfaces +# # CONFIG_WAN is not set # CONFIG_PPP is not set # CONFIG_SLIP is not set -# CONFIG_NETCONSOLE is not set # CONFIG_NETPOLL is not set # CONFIG_NET_POLL_CONTROLLER is not set + +# +# ISDN subsystem +# # CONFIG_ISDN is not set # # Input device support # CONFIG_INPUT=y -# CONFIG_INPUT_FF_MEMLESS is not set -# CONFIG_INPUT_POLLDEV is not set # # Userland interfaces @@ -537,6 +420,7 @@ CONFIG_INPUT_MOUSEDEV_PSAUX=y CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 # CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set # CONFIG_INPUT_EVDEV is not set # CONFIG_INPUT_EVBUG is not set @@ -549,19 +433,11 @@ CONFIG_KEYBOARD_ATKBD=y # CONFIG_KEYBOARD_LKKBD is not set # CONFIG_KEYBOARD_XTKBD is not set # CONFIG_KEYBOARD_NEWTON is not set -# CONFIG_KEYBOARD_STOWAWAY is not set CONFIG_INPUT_MOUSE=y CONFIG_MOUSE_PS2=y -CONFIG_MOUSE_PS2_ALPS=y -CONFIG_MOUSE_PS2_LOGIPS2PP=y -CONFIG_MOUSE_PS2_SYNAPTICS=y -CONFIG_MOUSE_PS2_LIFEBOOK=y -CONFIG_MOUSE_PS2_TRACKPOINT=y -# CONFIG_MOUSE_PS2_TOUCHKIT is not set # CONFIG_MOUSE_SERIAL is not set # CONFIG_MOUSE_VSXXXAA is not set # CONFIG_INPUT_JOYSTICK is not set -# CONFIG_INPUT_TABLET is not set # CONFIG_INPUT_TOUCHSCREEN is not set # CONFIG_INPUT_MISC is not set @@ -579,11 +455,8 @@ CONFIG_SERIO_LIBPS2=y # Character devices # CONFIG_VT=y -CONFIG_CONSOLE_TRANSLATIONS=y CONFIG_VT_CONSOLE=y CONFIG_HW_CONSOLE=y -# CONFIG_VT_HW_CONSOLE_BINDING is not set -CONFIG_DEVKMEM=y # CONFIG_SERIAL_NONSTANDARD is not set # @@ -602,91 +475,73 @@ CONFIG_SERIAL_CORE_CONSOLE=y CONFIG_UNIX98_PTYS=y CONFIG_LEGACY_PTYS=y CONFIG_LEGACY_PTY_COUNT=16 + +# +# IPMI +# # CONFIG_IPMI_HANDLER is not set -# CONFIG_HW_RANDOM is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set # CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set # CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# # CONFIG_RAW_DRIVER is not set -# CONFIG_TCG_TPM is not set + +# +# TPM devices +# + +# +# I2C support +# # CONFIG_I2C is not set -# CONFIG_SPI is not set -# CONFIG_W1 is not set -# CONFIG_POWER_SUPPLY is not set -# CONFIG_HWMON is not set -# CONFIG_THERMAL is not set -# CONFIG_THERMAL_HWMON is not set -# CONFIG_WATCHDOG is not set # -# Sonics Silicon Backplane +# Hardware Monitoring support # -CONFIG_SSB_POSSIBLE=y -# CONFIG_SSB is not set +# CONFIG_HWMON is not set +# CONFIG_HWMON_VID is not set # -# Multifunction device drivers +# Misc devices # -# CONFIG_MFD_CORE is not set -# CONFIG_MFD_SM501 is not set -# CONFIG_HTC_PASIC3 is not set -# CONFIG_MFD_TMIO is not set -# CONFIG_MFD_T7L66XB is not set -# CONFIG_MFD_TC6387XB is not set -# CONFIG_MFD_WM8400 is not set # -# Multimedia devices +# Multimedia Capabilities Port drivers # # -# Multimedia core support +# Multimedia devices # # CONFIG_VIDEO_DEV is not set -# CONFIG_DVB_CORE is not set -# CONFIG_VIDEO_MEDIA is not set # -# Multimedia drivers +# Digital Video Broadcasting Devices # -# CONFIG_DAB is not set +# CONFIG_DVB is not set # # Graphics support # -# CONFIG_VGASTATE is not set -# CONFIG_VIDEO_OUTPUT_CONTROL is not set CONFIG_FB=y -# CONFIG_FIRMWARE_EDID is not set -# CONFIG_FB_DDC is not set -# CONFIG_FB_BOOT_VESA_SUPPORT is not set CONFIG_FB_CFB_FILLRECT=y CONFIG_FB_CFB_COPYAREA=y CONFIG_FB_CFB_IMAGEBLIT=y -# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set -# CONFIG_FB_SYS_FILLRECT is not set -# CONFIG_FB_SYS_COPYAREA is not set -# CONFIG_FB_SYS_IMAGEBLIT is not set -# CONFIG_FB_FOREIGN_ENDIAN is not set -# CONFIG_FB_SYS_FOPS is not set -# CONFIG_FB_SVGALIB is not set +CONFIG_FB_SOFT_CURSOR=y # CONFIG_FB_MACMODES is not set -# CONFIG_FB_BACKLIGHT is not set # CONFIG_FB_MODE_HELPERS is not set # CONFIG_FB_TILEBLITTING is not set - -# -# Frame buffer hardware drivers -# CONFIG_FB_ARMCLCD=y # CONFIG_FB_S1D13XXX is not set # CONFIG_FB_VIRTUAL is not set -# CONFIG_FB_METRONOME is not set -# CONFIG_BACKLIGHT_LCD_SUPPORT is not set - -# -# Display device support -# -# CONFIG_DISPLAY_SUPPORT is not set # # Console display driver support @@ -694,17 +549,27 @@ CONFIG_FB_ARMCLCD=y # CONFIG_VGA_CONSOLE is not set CONFIG_DUMMY_CONSOLE=y CONFIG_FRAMEBUFFER_CONSOLE=y -# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set -# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set # CONFIG_FONTS is not set CONFIG_FONT_8x8=y CONFIG_FONT_8x16=y + +# +# Logo configuration +# CONFIG_LOGO=y # CONFIG_LOGO_LINUX_MONO is not set # CONFIG_LOGO_LINUX_VGA16 is not set CONFIG_LOGO_LINUX_CLUT224=y +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Sound +# CONFIG_SOUND=y -CONFIG_SOUND_OSS_CORE=y + +# +# Advanced Linux Sound Architecture +# CONFIG_SND=y CONFIG_SND_TIMER=y CONFIG_SND_PCM=y @@ -712,71 +577,59 @@ CONFIG_SND_PCM=y CONFIG_SND_OSSEMUL=y CONFIG_SND_MIXER_OSS=y CONFIG_SND_PCM_OSS=y -CONFIG_SND_PCM_OSS_PLUGINS=y -# CONFIG_SND_DYNAMIC_MINORS is not set -CONFIG_SND_SUPPORT_OLD_API=y -CONFIG_SND_VERBOSE_PROCFS=y # CONFIG_SND_VERBOSE_PRINTK is not set # CONFIG_SND_DEBUG is not set -CONFIG_SND_VMASTER=y -CONFIG_SND_AC97_CODEC=y -# CONFIG_SND_DRIVERS is not set -CONFIG_SND_ARM=y -CONFIG_SND_ARMAACI=y -# CONFIG_SND_SOC is not set + +# +# Generic devices +# +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set + +# +# ALSA ARM devices +# +# CONFIG_SND_ARMAACI is not set + +# +# Open Sound System +# # CONFIG_SOUND_PRIME is not set -CONFIG_AC97_BUS=y -# CONFIG_HID_SUPPORT is not set -# CONFIG_USB_SUPPORT is not set -CONFIG_MMC=y -# CONFIG_MMC_DEBUG is not set -# CONFIG_MMC_UNSAFE_RESUME is not set # -# MMC/SD/SDIO Card Drivers +# USB support # -CONFIG_MMC_BLOCK=y -CONFIG_MMC_BLOCK_BOUNCE=y -# CONFIG_SDIO_UART is not set -# CONFIG_MMC_TEST is not set +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB is not set # -# MMC/SD/SDIO Host Controller Drivers +# USB Gadget Support # -CONFIG_MMC_ARMMMCI=y -# CONFIG_MMC_SDHCI is not set -# CONFIG_MEMSTICK is not set -# CONFIG_ACCESSIBILITY is not set -# CONFIG_NEW_LEDS is not set -CONFIG_RTC_LIB=y -# CONFIG_RTC_CLASS is not set -# CONFIG_DMADEVICES is not set +# CONFIG_USB_GADGET is not set # -# Voltage and Current regulators +# MMC/SD Card support # -# CONFIG_REGULATOR is not set -# CONFIG_REGULATOR_FIXED_VOLTAGE is not set -# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set -# CONFIG_REGULATOR_BQ24022 is not set -# CONFIG_UIO is not set +# CONFIG_MMC is not set # # File systems # # CONFIG_EXT2_FS is not set # CONFIG_EXT3_FS is not set -# CONFIG_EXT4_FS is not set +# CONFIG_JBD is not set # CONFIG_REISERFS_FS is not set # CONFIG_JFS_FS is not set # CONFIG_FS_POSIX_ACL is not set -CONFIG_FILE_LOCKING=y # CONFIG_XFS_FS is not set -# CONFIG_OCFS2_FS is not set -CONFIG_DNOTIFY=y +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set CONFIG_INOTIFY=y -CONFIG_INOTIFY_USER=y # CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y # CONFIG_AUTOFS_FS is not set # CONFIG_AUTOFS4_FS is not set # CONFIG_FUSE_FS is not set @@ -801,59 +654,51 @@ CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" # Pseudo filesystems # CONFIG_PROC_FS=y -CONFIG_PROC_SYSCTL=y -CONFIG_PROC_PAGE_MONITOR=y CONFIG_SYSFS=y CONFIG_TMPFS=y -# CONFIG_TMPFS_POSIX_ACL is not set # CONFIG_HUGETLB_PAGE is not set -# CONFIG_CONFIGFS_FS is not set +CONFIG_RAMFS=y +# CONFIG_RELAYFS_FS is not set # # Miscellaneous filesystems # -# CONFIG_ADFS_FS is not set -# CONFIG_AFFS_FS is not set -# CONFIG_HFS_FS is not set # CONFIG_HFSPLUS_FS is not set -# CONFIG_BEFS_FS is not set -# CONFIG_BFS_FS is not set -# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set # CONFIG_JFFS2_FS is not set CONFIG_CRAMFS=y # CONFIG_VXFS_FS is not set -# CONFIG_MINIX_FS is not set -# CONFIG_OMFS_FS is not set # CONFIG_HPFS_FS is not set # CONFIG_QNX4FS_FS is not set -# CONFIG_ROMFS_FS is not set # CONFIG_SYSV_FS is not set # CONFIG_UFS_FS is not set -CONFIG_NETWORK_FILESYSTEMS=y + +# +# Network File Systems +# CONFIG_NFS_FS=y CONFIG_NFS_V3=y # CONFIG_NFS_V3_ACL is not set -# CONFIG_NFS_V4 is not set -CONFIG_ROOT_NFS=y # CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y CONFIG_LOCKD=y CONFIG_LOCKD_V4=y CONFIG_NFS_COMMON=y CONFIG_SUNRPC=y -# CONFIG_SUNRPC_REGISTER_V4 is not set -# CONFIG_RPCSEC_GSS_KRB5 is not set -# CONFIG_RPCSEC_GSS_SPKM3 is not set # CONFIG_SMB_FS is not set # CONFIG_CIFS is not set # CONFIG_NCP_FS is not set # CONFIG_CODA_FS is not set -# CONFIG_AFS_FS is not set # # Partition Types # # CONFIG_PARTITION_ADVANCED is not set CONFIG_MSDOS_PARTITION=y + +# +# Native Language Support +# CONFIG_NLS=y CONFIG_NLS_DEFAULT="iso8859-1" CONFIG_NLS_CODEPAGE_437=y @@ -894,71 +739,26 @@ CONFIG_NLS_ISO8859_1=y # CONFIG_NLS_KOI8_R is not set # CONFIG_NLS_KOI8_U is not set # CONFIG_NLS_UTF8 is not set -# CONFIG_DLM is not set # # Kernel hacking # # CONFIG_PRINTK_TIME is not set -CONFIG_ENABLE_WARN_DEPRECATED=y -CONFIG_ENABLE_MUST_CHECK=y -CONFIG_FRAME_WARN=1024 -CONFIG_MAGIC_SYSRQ=y -# CONFIG_UNUSED_SYMBOLS is not set -# CONFIG_DEBUG_FS is not set -# CONFIG_HEADERS_CHECK is not set CONFIG_DEBUG_KERNEL=y -# CONFIG_DEBUG_SHIRQ is not set +CONFIG_MAGIC_SYSRQ=y +CONFIG_LOG_BUF_SHIFT=14 CONFIG_DETECT_SOFTLOCKUP=y -# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set -CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 -# CONFIG_SCHED_DEBUG is not set # CONFIG_SCHEDSTATS is not set -# CONFIG_TIMER_STATS is not set -# CONFIG_DEBUG_OBJECTS is not set # CONFIG_DEBUG_SLAB is not set -# CONFIG_DEBUG_RT_MUTEXES is not set -# CONFIG_RT_MUTEX_TESTER is not set # CONFIG_DEBUG_SPINLOCK is not set -# CONFIG_DEBUG_MUTEXES is not set -# CONFIG_DEBUG_LOCK_ALLOC is not set -# CONFIG_PROVE_LOCKING is not set -# CONFIG_LOCK_STAT is not set # CONFIG_DEBUG_SPINLOCK_SLEEP is not set -# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set # CONFIG_DEBUG_KOBJECT is not set CONFIG_DEBUG_BUGVERBOSE=y # CONFIG_DEBUG_INFO is not set -# CONFIG_DEBUG_VM is not set -# CONFIG_DEBUG_WRITECOUNT is not set -CONFIG_DEBUG_MEMORY_INIT=y -# CONFIG_DEBUG_LIST is not set -# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_FS is not set CONFIG_FRAME_POINTER=y -# CONFIG_BOOT_PRINTK_DELAY is not set -# CONFIG_RCU_TORTURE_TEST is not set -# CONFIG_RCU_CPU_STALL_DETECTOR is not set -# CONFIG_BACKTRACE_SELF_TEST is not set -# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set -# CONFIG_FAULT_INJECTION is not set -# CONFIG_LATENCYTOP is not set -# CONFIG_SYSCTL_SYSCALL_CHECK is not set -CONFIG_NOP_TRACER=y -CONFIG_HAVE_FTRACE=y -CONFIG_HAVE_DYNAMIC_FTRACE=y -# CONFIG_FTRACE is not set -# CONFIG_IRQSOFF_TRACER is not set -# CONFIG_SCHED_TRACER is not set -# CONFIG_CONTEXT_SWITCH_TRACER is not set -# CONFIG_BOOT_TRACER is not set -# CONFIG_STACK_TRACER is not set -# CONFIG_DYNAMIC_PRINTK_DEBUG is not set -# CONFIG_SAMPLES is not set -CONFIG_HAVE_ARCH_KGDB=y -# CONFIG_KGDB is not set CONFIG_DEBUG_USER=y CONFIG_DEBUG_ERRORS=y -# CONFIG_DEBUG_STACK_USAGE is not set # CONFIG_DEBUG_LL is not set # @@ -966,106 +766,21 @@ CONFIG_DEBUG_ERRORS=y # # CONFIG_KEYS is not set # CONFIG_SECURITY is not set -# CONFIG_SECURITYFS is not set -# CONFIG_SECURITY_FILE_CAPABILITIES is not set -CONFIG_CRYPTO=y - -# -# Crypto core or helper -# -# CONFIG_CRYPTO_FIPS is not set -# CONFIG_CRYPTO_MANAGER is not set -# CONFIG_CRYPTO_GF128MUL is not set -# CONFIG_CRYPTO_NULL is not set -# CONFIG_CRYPTO_CRYPTD is not set -# CONFIG_CRYPTO_AUTHENC is not set -# CONFIG_CRYPTO_TEST is not set - -# -# Authenticated Encryption with Associated Data -# -# CONFIG_CRYPTO_CCM is not set -# CONFIG_CRYPTO_GCM is not set -# CONFIG_CRYPTO_SEQIV is not set - -# -# Block modes -# -# CONFIG_CRYPTO_CBC is not set -# CONFIG_CRYPTO_CTR is not set -# CONFIG_CRYPTO_CTS is not set -# CONFIG_CRYPTO_ECB is not set -# CONFIG_CRYPTO_LRW is not set -# CONFIG_CRYPTO_PCBC is not set -# CONFIG_CRYPTO_XTS is not set - -# -# Hash modes -# -# CONFIG_CRYPTO_HMAC is not set -# CONFIG_CRYPTO_XCBC is not set - -# -# Digest -# -# CONFIG_CRYPTO_CRC32C is not set -# CONFIG_CRYPTO_MD4 is not set -# CONFIG_CRYPTO_MD5 is not set -# CONFIG_CRYPTO_MICHAEL_MIC is not set -# CONFIG_CRYPTO_RMD128 is not set -# CONFIG_CRYPTO_RMD160 is not set -# CONFIG_CRYPTO_RMD256 is not set -# CONFIG_CRYPTO_RMD320 is not set -# CONFIG_CRYPTO_SHA1 is not set -# CONFIG_CRYPTO_SHA256 is not set -# CONFIG_CRYPTO_SHA512 is not set -# CONFIG_CRYPTO_TGR192 is not set -# CONFIG_CRYPTO_WP512 is not set - -# -# Ciphers -# -# CONFIG_CRYPTO_AES is not set -# CONFIG_CRYPTO_ANUBIS is not set -# CONFIG_CRYPTO_ARC4 is not set -# CONFIG_CRYPTO_BLOWFISH is not set -# CONFIG_CRYPTO_CAMELLIA is not set -# CONFIG_CRYPTO_CAST5 is not set -# CONFIG_CRYPTO_CAST6 is not set -# CONFIG_CRYPTO_DES is not set -# CONFIG_CRYPTO_FCRYPT is not set -# CONFIG_CRYPTO_KHAZAD is not set -# CONFIG_CRYPTO_SALSA20 is not set -# CONFIG_CRYPTO_SEED is not set -# CONFIG_CRYPTO_SERPENT is not set -# CONFIG_CRYPTO_TEA is not set -# CONFIG_CRYPTO_TWOFISH is not set # -# Compression +# Cryptographic options # -# CONFIG_CRYPTO_DEFLATE is not set -# CONFIG_CRYPTO_LZO is not set +# CONFIG_CRYPTO is not set # -# Random Number Generation +# Hardware crypto devices # -# CONFIG_CRYPTO_ANSI_CPRNG is not set -# CONFIG_CRYPTO_HW is not set # # Library routines # -CONFIG_BITREVERSE=y # CONFIG_CRC_CCITT is not set # CONFIG_CRC16 is not set -# CONFIG_CRC_T10DIF is not set -# CONFIG_CRC_ITU_T is not set CONFIG_CRC32=y -# CONFIG_CRC7 is not set # CONFIG_LIBCRC32C is not set CONFIG_ZLIB_INFLATE=y -CONFIG_PLIST=y -CONFIG_HAS_IOMEM=y -CONFIG_HAS_IOPORT=y -CONFIG_HAS_DMA=y diff --git a/arch/arm/configs/versatile_defconfig b/arch/arm/configs/versatile_defconfig index b11c5da3996c..8355f88f7292 100644 --- a/arch/arm/configs/versatile_defconfig +++ b/arch/arm/configs/versatile_defconfig @@ -611,7 +611,7 @@ CONFIG_I2C_ALGOBIT=y # # CONFIG_SENSORS_DS1337 is not set # CONFIG_SENSORS_DS1374 is not set -CONFIG_EEPROM_LEGACY=m +CONFIG_SENSORS_EEPROM=m # CONFIG_SENSORS_PCF8574 is not set # CONFIG_SENSORS_PCA9539 is not set # CONFIG_SENSORS_PCF8591 is not set diff --git a/arch/arm/include/asm/assembler.h b/arch/arm/include/asm/assembler.h index 6116e4893c0a..2539d254caa7 100644 --- a/arch/arm/include/asm/assembler.h +++ b/arch/arm/include/asm/assembler.h @@ -96,7 +96,11 @@ * assumes FIQs are enabled, and that the processor is in SVC mode. */ .macro save_and_disable_irqs, oldcpsr +#ifdef CONFIG_CPU_V7M + mrs \oldcpsr, primask +#else mrs \oldcpsr, cpsr +#endif disable_irq .endm @@ -105,7 +109,11 @@ * guarantee that this will preserve the flags. */ .macro restore_irqs, oldcpsr +#ifdef CONFIG_CPU_V7M + msr primask, \oldcpsr +#else msr cpsr_c, \oldcpsr +#endif .endm #define USER(x...) \ @@ -114,3 +122,90 @@ .align 3; \ .long 9999b,9001f; \ .previous + +#if defined(CONFIG_CPU_V7M) + .macro setmode, mode, reg + .endm +#elif defined(CONFIG_THUMB2_KERNEL) + .macro setmode, mode, reg + mov \reg, #\mode + msr cpsr_c, \reg + .endm +#else + .macro setmode, mode, reg + msr cpsr_c, #\mode + .endm +#endif + +/* + * STRT/LDRT access macros with ARM and Thumb-2 variants + */ +#ifdef CONFIG_THUMB2_KERNEL + + .macro usraccoff, instr, reg, ptr, inc, off, cond, abort +9999: + .if \inc == 1 + \instr\cond\()bt \reg, [\ptr, #\off] + .elseif \inc == 4 + \instr\cond\()t \reg, [\ptr, #\off] + .else + .error "Unsupported inc macro argument" + .endif + + .section __ex_table,"a" + .align 3 + .long 9999b, \abort + .previous + .endm + + .macro usracc, instr, reg, ptr, inc, cond, rept, abort + @ explicit IT instruction needed because of the label + @ introduced by the USER macro + .ifnc \cond,al + .if \rept == 1 + itt \cond + .elseif \rept == 2 + ittt \cond + .else + .error "Unsupported rept macro argument" + .endif + .endif + + @ Slightly optimised to avoid incrementing the pointer twice + usraccoff \instr, \reg, \ptr, \inc, 0, \cond, \abort + .if \rept == 2 + usraccoff \instr, \reg, \ptr, \inc, 4, \cond, \abort + .endif + + add\cond \ptr, #\rept * \inc + .endm + +#else /* !CONFIG_THUMB2_KERNEL */ + + .macro usracc, instr, reg, ptr, inc, cond, rept, abort + .rept \rept +9999: + .if \inc == 1 + \instr\cond\()bt \reg, [\ptr], #\inc + .elseif \inc == 4 + \instr\cond\()t \reg, [\ptr], #\inc + .else + .error "Unsupported inc macro argument" + .endif + + .section __ex_table,"a" + .align 3 + .long 9999b, \abort + .previous + .endr + .endm + +#endif /* !CONFIG_THUMB2_KERNEL */ + + .macro strusr, reg, ptr, inc, cond=al, rept=1, abort=9001f + usracc str, \reg, \ptr, \inc, \cond, \rept, \abort + .endm + + .macro ldrusr, reg, ptr, inc, cond=al, rept=1, abort=9001f + usracc ldr, \reg, \ptr, \inc, \cond, \rept, \abort + .endm diff --git a/arch/arm/include/asm/atomic.h b/arch/arm/include/asm/atomic.h index ee99723b3a6c..ce438d119a33 100644 --- a/arch/arm/include/asm/atomic.h +++ b/arch/arm/include/asm/atomic.h @@ -20,44 +20,49 @@ #ifdef __KERNEL__ #define atomic_read(v) ((v)->counter) +#define atomic_set(v,i) (((v)->counter) = (i)) #if __LINUX_ARM_ARCH__ >= 6 +#ifdef CONFIG_ARM_ERRATA_351422 +static inline int atomic_backoff_delay(void) +{ + unsigned int delay; + __asm__ __volatile__( + " mrc p15, 0, %0, c0, c0, 5\n" + " and %0, %0, #0xf\n" + " mov %0, %0, lsl #8\n" + "1: subs %0, %0, #1\n" + " bpl 1b\n" + : "=&r" (delay) + : + : "cc" ); + + return 1; +} +#else +#define atomic_backoff_delay() 1 +#endif + /* * ARMv6 UP and SMP safe atomic ops. We use load exclusive and * store exclusive to ensure that these are atomic. We may loop - * to ensure that the update happens. Writing to 'v->counter' - * without using the following operations WILL break the atomic - * nature of these ops. + * to ensure that the update happens. */ -static inline void atomic_set(atomic_t *v, int i) -{ - unsigned long tmp; - - __asm__ __volatile__("@ atomic_set\n" -"1: ldrex %0, [%1]\n" -" strex %0, %2, [%1]\n" -" teq %0, #0\n" -" bne 1b" - : "=&r" (tmp) - : "r" (&v->counter), "r" (i) - : "cc"); -} - static inline int atomic_add_return(int i, atomic_t *v) { unsigned long tmp; int result; + do { __asm__ __volatile__("@ atomic_add_return\n" "1: ldrex %0, [%2]\n" " add %0, %0, %3\n" " strex %1, %0, [%2]\n" -" teq %1, #0\n" -" bne 1b" : "=&r" (result), "=&r" (tmp) : "r" (&v->counter), "Ir" (i) : "cc"); + } while (tmp && atomic_backoff_delay()); return result; } @@ -67,15 +72,15 @@ static inline int atomic_sub_return(int i, atomic_t *v) unsigned long tmp; int result; + do { __asm__ __volatile__("@ atomic_sub_return\n" "1: ldrex %0, [%2]\n" " sub %0, %0, %3\n" " strex %1, %0, [%2]\n" -" teq %1, #0\n" -" bne 1b" : "=&r" (result), "=&r" (tmp) : "r" (&v->counter), "Ir" (i) : "cc"); + } while (tmp && atomic_backoff_delay()); return result; } @@ -89,11 +94,12 @@ static inline int atomic_cmpxchg(atomic_t *ptr, int old, int new) "ldrex %1, [%2]\n" "mov %0, #0\n" "teq %1, %3\n" + "it eq\n" "strexeq %0, %4, [%2]\n" : "=&r" (res), "=&r" (oldval) : "r" (&ptr->counter), "Ir" (old), "r" (new) : "cc"); - } while (res); + } while (res && atomic_backoff_delay()); return oldval; } @@ -102,15 +108,15 @@ static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) { unsigned long tmp, tmp2; + do { __asm__ __volatile__("@ atomic_clear_mask\n" "1: ldrex %0, [%2]\n" " bic %0, %0, %3\n" " strex %1, %0, [%2]\n" -" teq %1, #0\n" -" bne 1b" : "=&r" (tmp), "=&r" (tmp2) : "r" (addr), "Ir" (mask) : "cc"); + } while (tmp && atomic_backoff_delay()); } #else /* ARM_ARCH_6 */ @@ -121,8 +127,6 @@ static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) #error SMP not supported on pre-ARMv6 CPUs #endif -#define atomic_set(v,i) (((v)->counter) = (i)) - static inline int atomic_add_return(int i, atomic_t *v) { unsigned long flags; diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h index a160e0a9035c..b753d915e4df 100644 --- a/arch/arm/include/asm/cacheflush.h +++ b/arch/arm/include/asm/cacheflush.h @@ -114,6 +114,14 @@ //# endif #endif +#if defined(CONFIG_CPU_V7M) +# ifdef _CACHE +# error "Multi-cache not supported on ARMv7-M" +# else +# define _CACHE v7m +# endif +#endif + #if !defined(_CACHE) && !defined(MULTI_CACHE) #error Unknown cache maintainence model #endif @@ -234,6 +242,22 @@ extern struct cpu_cache_fns cpu_cache; #else +#ifdef CONFIG_CPU_V7M + +static inline void v7m_flush_kern_all(void) { } +static inline void v7m_flush_user_all(void) { } +static inline void v7m_flush_user_range(unsigned long a, unsigned long b, unsigned int c) { } + +static inline void v7m_coherent_kern_range(unsigned long a, unsigned long b) { } +static inline void v7m_coherent_user_range(unsigned long a, unsigned long b) { } +static inline void v7m_flush_kern_dcache_page(void *a) { } + +static inline void v7m_dma_inv_range(const void *a, const void *b) { } +static inline void v7m_dma_clean_range(const void *a, const void *b) { } +static inline void v7m_dma_flush_range(const void *a, const void *b) { } + +#endif + #define __cpuc_flush_kern_all __glue(_CACHE,_flush_kern_cache_all) #define __cpuc_flush_user_all __glue(_CACHE,_flush_user_cache_all) #define __cpuc_flush_user_range __glue(_CACHE,_flush_user_cache_range) @@ -264,6 +288,22 @@ extern void dmac_flush_range(const void *, const void *); #endif +#ifdef CONFIG_CPU_NO_CACHE_BCAST +enum smp_dma_cache_type { + SMP_DMA_CACHE_INV, + SMP_DMA_CACHE_CLEAN, + SMP_DMA_CACHE_FLUSH, +}; +extern void smp_dma_cache_op(int type, const void *start, const void *end); +#define smp_dma_inv_range(s, e) smp_dma_cache_op(SMP_DMA_CACHE_INV, s, e) +#define smp_dma_clean_range(s, e) smp_dma_cache_op(SMP_DMA_CACHE_CLEAN, s, e) +#define smp_dma_flush_range(s, e) smp_dma_cache_op(SMP_DMA_CACHE_FLUSH, s, e) +#else +#define smp_dma_inv_range dmac_inv_range +#define smp_dma_clean_range dmac_clean_range +#define smp_dma_flush_range dmac_flush_range +#endif + #ifdef CONFIG_OUTER_CACHE extern struct outer_cache_fns outer_cache; diff --git a/arch/arm/include/asm/checksum.h b/arch/arm/include/asm/checksum.h index 6dcc16430868..e4b9f6e17365 100644 --- a/arch/arm/include/asm/checksum.h +++ b/arch/arm/include/asm/checksum.h @@ -73,6 +73,7 @@ ip_fast_csum(const void *iph, unsigned int ihl) 1: adcs %0, %0, %3 \n\ ldr %3, [%1], #4 \n\ tst %2, #15 @ do this carefully \n\ + it ne \n\ subne %2, %2, #1 @ without destroying \n\ bne 1b @ the carry flag \n\ adcs %0, %0, %3 \n\ diff --git a/arch/arm/include/asm/cputype.h b/arch/arm/include/asm/cputype.h index 7b9d27e749b8..c2fe374fcb7f 100644 --- a/arch/arm/include/asm/cputype.h +++ b/arch/arm/include/asm/cputype.h @@ -8,6 +8,21 @@ #define CPUID_TCM 2 #define CPUID_TLBTYPE 3 +#define CPUID_EXT_PFR0 "c1, 0" +#define CPUID_EXT_PFR1 "c1, 1" +#define CPUID_EXT_DFR0 "c1, 2" +#define CPUID_EXT_AFR0 "c1, 3" +#define CPUID_EXT_MMFR0 "c1, 4" +#define CPUID_EXT_MMFR1 "c1, 5" +#define CPUID_EXT_MMFR2 "c1, 6" +#define CPUID_EXT_MMFR3 "c1, 7" +#define CPUID_EXT_ISAR0 "c2, 0" +#define CPUID_EXT_ISAR1 "c2, 1" +#define CPUID_EXT_ISAR2 "c2, 2" +#define CPUID_EXT_ISAR3 "c2, 3" +#define CPUID_EXT_ISAR4 "c2, 4" +#define CPUID_EXT_ISAR5 "c2, 5" + #ifdef CONFIG_CPU_CP15 #define read_cpuid(reg) \ ({ \ @@ -18,9 +33,21 @@ : "cc"); \ __val; \ }) +#define read_cpuid_ext(ext_reg) \ + ({ \ + unsigned int __val; \ + asm("mrc p15, 0, %0, c0, " ext_reg \ + : "=r" (__val) \ + : \ + : "cc"); \ + __val; \ + }) +#elif defined(CONFIG_CPU_V7M) +#define read_cpuid(reg) (*(unsigned int *)0xe000ed00) #else extern unsigned int processor_id; #define read_cpuid(reg) (processor_id) +#define read_cpuid_ext(reg) 0 #endif /* diff --git a/arch/arm/include/asm/elf.h b/arch/arm/include/asm/elf.h index 8196abfa2d8e..22d887df8832 100644 --- a/arch/arm/include/asm/elf.h +++ b/arch/arm/include/asm/elf.h @@ -55,6 +55,9 @@ typedef struct user_fp elf_fpregset_t; #define R_ARM_MOVW_ABS_NC 43 #define R_ARM_MOVT_ABS 44 +#define R_ARM_THM_JUMP24 30 +#define R_ARM_THM_CALL 10 + /* * These are used to set parameters in the core dumps. */ diff --git a/arch/arm/include/asm/futex.h b/arch/arm/include/asm/futex.h index 9ee743b95de8..0efee0f0c08e 100644 --- a/arch/arm/include/asm/futex.h +++ b/arch/arm/include/asm/futex.h @@ -99,6 +99,7 @@ futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) __asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n" "1: ldrt %0, [%3]\n" " teq %0, %1\n" + " it eq\n" "2: streqt %2, [%3]\n" "3:\n" " .section __ex_table,\"a\"\n" diff --git a/arch/arm/include/asm/glue.h b/arch/arm/include/asm/glue.h index a0e39d5d00c9..0a3bb3d8fab6 100644 --- a/arch/arm/include/asm/glue.h +++ b/arch/arm/include/asm/glue.h @@ -115,6 +115,14 @@ # endif #endif +#ifdef CONFIG_CPU_ABRT_EV7M +# ifdef CPU_DABORT_HANDLER +# define MULTI_DABORT 1 +# else +# define CPU_DABORT_HANDLER v7m_early_abort +# endif +#endif + #ifndef CPU_DABORT_HANDLER #error Unknown data abort handler type #endif diff --git a/arch/arm/include/asm/hardware/cache-l2x0.h b/arch/arm/include/asm/hardware/cache-l2x0.h index 64f2252a25cd..33d19e4160c4 100644 --- a/arch/arm/include/asm/hardware/cache-l2x0.h +++ b/arch/arm/include/asm/hardware/cache-l2x0.h @@ -24,6 +24,8 @@ #define L2X0_CACHE_TYPE 0x004 #define L2X0_CTRL 0x100 #define L2X0_AUX_CTRL 0x104 +#define L2X0_TAG_LATENCY_CTRL 0x108 +#define L2X0_DATA_LATENCY_CTRL 0x10C #define L2X0_EVENT_CNT_CTRL 0x200 #define L2X0_EVENT_CNT1_CFG 0x204 #define L2X0_EVENT_CNT0_CFG 0x208 @@ -49,8 +51,34 @@ #define L2X0_LINE_TAG 0xF30 #define L2X0_DEBUG_CTRL 0xF40 +/* Interrupt bits */ +#define L2X0_INTR_ECNTR 0x01 + +/* Aux Control bits */ +#define L2X0_AUX_CTRL_EMBUS (0x01<<20) + +/* Event Counter Control bits */ +#define L2X0_EVENT_CONTROL_ENABLE 0x1 +#define L2X0_EVENT_CONTROL_RESET_ALL 0x6 + +/* Event Counter Config bits */ +#define L2X0_EVENT_CONFIG_DISABLED 0x0 +#define L2X0_EVENT_CONFIG_CO (0x1<<2) +#define L2X0_EVENT_CONFIG_DRHIT (0x2<<2) +#define L2X0_EVENT_CONFIG_DRREQ (0x3<<2) +#define L2X0_EVENT_CONFIG_DWHIT (0x4<<2) +#define L2X0_EVENT_CONFIG_DWREQ (0x5<<2) +#define L2X0_EVENT_CONFIG_DWTREQ (0x6<<2) +#define L2X0_EVENT_CONFIG_IRHIT (0x7<<2) +#define L2X0_EVENT_CONFIG_IRREQ (0x8<<2) +#define L2X0_EVENT_CONFIG_WA (0x9<<2) +#define L2X0_EVENT_INTERRUPT_ON_INC 0x1 +#define L2X0_EVENT_INTERRUPT_ON_OVF 0x2 +#define L2X0_EVENT_INTERRUPT_DISABLED 0x3 + #ifndef __ASSEMBLY__ extern void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask); +extern bool l2x0_disabled; #endif #endif diff --git a/arch/arm/include/asm/hardware/debug-pl01x.S b/arch/arm/include/asm/hardware/debug-pl01x.S index f9fd083eff63..1605bc1a64bb 100644 --- a/arch/arm/include/asm/hardware/debug-pl01x.S +++ b/arch/arm/include/asm/hardware/debug-pl01x.S @@ -12,6 +12,14 @@ */ #include <linux/amba/serial.h> + .macro inituart,rd,rx + mov \rd, #0x10 + str \rd, [\rx, #UART011_IBRD] + mov \rd, #0xc300 + orr \rd, #0x0001 + str \rd, [\rx, #UART011_CR] + .endm + .macro senduart,rd,rx strb \rd, [\rx, #UART01x_DR] .endm diff --git a/arch/arm/include/asm/irqflags.h b/arch/arm/include/asm/irqflags.h index 6d09974e6646..5dbb6de221ce 100644 --- a/arch/arm/include/asm/irqflags.h +++ b/arch/arm/include/asm/irqflags.h @@ -10,6 +10,15 @@ */ #if __LINUX_ARM_ARCH__ >= 6 +#ifdef CONFIG_CPU_V7M +#define raw_local_irq_save(x) \ + ({ \ + __asm__ __volatile__( \ + "mrs %0, primask @ local_irq_save\n" \ + "cpsid i" \ + : "=r" (x) : : "memory", "cc"); \ + }) +#else #define raw_local_irq_save(x) \ ({ \ __asm__ __volatile__( \ @@ -17,6 +26,7 @@ "cpsid i" \ : "=r" (x) : : "memory", "cc"); \ }) +#endif #define raw_local_irq_enable() __asm__("cpsie i @ __sti" : : : "memory", "cc") #define raw_local_irq_disable() __asm__("cpsid i @ __cli" : : : "memory", "cc") @@ -103,6 +113,35 @@ #endif +#ifdef CONFIG_CPU_V7M + +/* + * Save the current interrupt enable state. + */ +#define raw_local_save_flags(x) \ + ({ \ + __asm__ __volatile__( \ + "mrs %0, primask @ local_save_flags" \ + : "=r" (x) : : "memory", "cc"); \ + }) + +/* + * restore saved IRQ & FIQ state + */ +#define raw_local_irq_restore(x) \ + __asm__ __volatile__( \ + "msr primask, %0 @ local_irq_restore\n" \ + : \ + : "r" (x) \ + : "memory", "cc") + +#define raw_irqs_disabled_flags(flags) \ +({ \ + (int)((flags) & 1); \ +}) + +#else + /* * Save the current interrupt enable state. */ @@ -128,5 +167,7 @@ (int)((flags) & PSR_I_BIT); \ }) +#endif /* CONFIG_CPU_V7M */ + #endif #endif diff --git a/arch/arm/include/asm/locks.h b/arch/arm/include/asm/locks.h index ef4c897772d1..c6f0c9363f0f 100644 --- a/arch/arm/include/asm/locks.h +++ b/arch/arm/include/asm/locks.h @@ -24,6 +24,7 @@ " teq ip, #0\n" \ " bne 1b\n" \ " teq lr, #0\n" \ +" itt mi\n" \ " movmi ip, %0\n" \ " blmi " #fail \ : \ @@ -43,6 +44,7 @@ " teq ip, #0\n" \ " bne 1b\n" \ " teq lr, #0\n" \ +" itet mi\n" \ " movmi ip, %1\n" \ " movpl ip, #0\n" \ " blmi " #fail "\n" \ @@ -65,6 +67,7 @@ " teq ip, #0\n" \ " bne 1b\n" \ " cmp lr, #0\n" \ +" itt le\n" \ " movle ip, %0\n" \ " blle " #wake \ : \ @@ -91,6 +94,7 @@ " teq ip, #0\n" \ " bne 1b\n" \ " teq lr, #0\n" \ +" itt ne\n" \ " movne ip, %0\n" \ " blne " #fail \ : \ @@ -150,6 +154,7 @@ " subs lr, lr, %1\n" \ " str lr, [%0]\n" \ " msr cpsr_c, ip\n" \ +" itt mi\n" \ " movmi ip, %0\n" \ " blmi " #fail \ : \ @@ -170,6 +175,7 @@ " subs lr, lr, %2\n" \ " str lr, [%1]\n" \ " msr cpsr_c, ip\n" \ +" itet mi\n" \ " movmi ip, %1\n" \ " movpl ip, #0\n" \ " blmi " #fail "\n" \ @@ -193,6 +199,7 @@ " adds lr, lr, %1\n" \ " str lr, [%0]\n" \ " msr cpsr_c, ip\n" \ +" itt le\n" \ " movle ip, %0\n" \ " blle " #wake \ : \ @@ -220,6 +227,7 @@ " subs lr, lr, %1\n" \ " str lr, [%0]\n" \ " msr cpsr_c, ip\n" \ +" itt ne\n" \ " movne ip, %0\n" \ " blne " #fail \ : \ @@ -239,6 +247,7 @@ " adds lr, lr, %1\n" \ " str lr, [%0]\n" \ " msr cpsr_c, ip\n" \ +" itt cs\n" \ " movcs ip, %0\n" \ " blcs " #wake \ : \ @@ -262,6 +271,7 @@ " adds lr, lr, %1\n" \ " str lr, [%0]\n" \ " msr cpsr_c, ip\n" \ +" itt eq\n" \ " moveq ip, %0\n" \ " bleq " #wake \ : \ diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h index 0202a7c20e62..4c541922f2f4 100644 --- a/arch/arm/include/asm/memory.h +++ b/arch/arm/include/asm/memory.h @@ -45,7 +45,12 @@ * and PAGE_OFFSET - it must be within 32MB of the kernel text. */ #define MODULES_END (PAGE_OFFSET) +#ifndef CONFIG_THUMB2_KERNEL #define MODULES_VADDR (MODULES_END - 16*1048576) +#else +/* smaller range for Thumb-2 symbols relocation (2^24)*/ +#define MODULES_VADDR (MODULES_END - 8*1048576) +#endif #if TASK_SIZE > MODULES_VADDR #error Top of user space clashes with start of module space diff --git a/arch/arm/include/asm/mmu.h b/arch/arm/include/asm/mmu.h index b561584d04a1..68870c776671 100644 --- a/arch/arm/include/asm/mmu.h +++ b/arch/arm/include/asm/mmu.h @@ -6,6 +6,7 @@ typedef struct { #ifdef CONFIG_CPU_HAS_ASID unsigned int id; + spinlock_t id_lock; #endif unsigned int kvm_seq; } mm_context_t; diff --git a/arch/arm/include/asm/mmu_context.h b/arch/arm/include/asm/mmu_context.h index 263fed05ea33..42939ee430a3 100644 --- a/arch/arm/include/asm/mmu_context.h +++ b/arch/arm/include/asm/mmu_context.h @@ -43,12 +43,23 @@ void __check_kvm_seq(struct mm_struct *mm); #define ASID_FIRST_VERSION (1 << ASID_BITS) extern unsigned int cpu_last_asid; +#ifdef CONFIG_SMP +DECLARE_PER_CPU(struct mm_struct *, current_mm); +#endif void __init_new_context(struct task_struct *tsk, struct mm_struct *mm); void __new_context(struct mm_struct *mm); static inline void check_context(struct mm_struct *mm) { + /* + * This code is executed with interrupts enabled. Therefore, + * mm->context.id cannot be updated to the latest ASID version + * on a different CPU (and condition below not triggered) + * without first getting an IPI to reset the context. The + * alternative is to take a read_lock on mm->context.id_lock + * (after changing its type to rwlock_t). + */ if (unlikely((mm->context.id ^ cpu_last_asid) >> ASID_BITS)) __new_context(mm); @@ -62,8 +73,10 @@ static inline void check_context(struct mm_struct *mm) static inline void check_context(struct mm_struct *mm) { +#ifdef CONFIG_MMU if (unlikely(mm->context.kvm_seq != init_mm.context.kvm_seq)) __check_kvm_seq(mm); +#endif } #define init_new_context(tsk,mm) 0 @@ -105,6 +118,10 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next, __flush_icache_all(); #endif if (!cpu_test_and_set(cpu, next->cpu_vm_mask) || prev != next) { +#ifdef CONFIG_SMP + struct mm_struct **crt_mm = &per_cpu(current_mm, cpu); + *crt_mm = next; +#endif check_context(next); cpu_switch_mm(next->pgd, next); if (cache_is_vivt()) diff --git a/arch/arm/include/asm/module.h b/arch/arm/include/asm/module.h index 24b168dc31a3..e4dfa69abb68 100644 --- a/arch/arm/include/asm/module.h +++ b/arch/arm/include/asm/module.h @@ -1,15 +1,27 @@ #ifndef _ASM_ARM_MODULE_H #define _ASM_ARM_MODULE_H -struct mod_arch_specific -{ - int foo; -}; - #define Elf_Shdr Elf32_Shdr #define Elf_Sym Elf32_Sym #define Elf_Ehdr Elf32_Ehdr +struct unwind_table; + +struct mod_arch_specific +{ +#ifdef CONFIG_ARM_UNWIND + Elf_Shdr *unw_sec_init; + Elf_Shdr *unw_sec_devinit; + Elf_Shdr *unw_sec_core; + Elf_Shdr *sec_init_text; + Elf_Shdr *sec_devinit_text; + Elf_Shdr *sec_core_text; + struct unwind_table *unwind_init; + struct unwind_table *unwind_devinit; + struct unwind_table *unwind_core; +#endif +}; + /* * Include the ARM architecture version. */ diff --git a/arch/arm/include/asm/mutex.h b/arch/arm/include/asm/mutex.h index 93226cf23ae0..5c3ede822a6e 100644 --- a/arch/arm/include/asm/mutex.h +++ b/arch/arm/include/asm/mutex.h @@ -111,8 +111,11 @@ __mutex_fastpath_trylock(atomic_t *count, int (*fail_fn)(atomic_t *)) "1: ldrex %0, [%3] \n\t" "subs %1, %0, #1 \n\t" + "it eq\n\t" "strexeq %2, %1, [%3] \n\t" + "it lt\n\t" "movlt %0, #0 \n\t" + "it eq\n\t" "cmpeq %2, #0 \n\t" "bgt 1b " diff --git a/arch/arm/include/asm/page-nommu.h b/arch/arm/include/asm/page-nommu.h index 3574c0deb37f..d1b162a18dcb 100644 --- a/arch/arm/include/asm/page-nommu.h +++ b/arch/arm/include/asm/page-nommu.h @@ -43,7 +43,4 @@ typedef unsigned long pgprot_t; #define __pmd(x) (x) #define __pgprot(x) (x) -extern unsigned long memory_start; -extern unsigned long memory_end; - #endif diff --git a/arch/arm/include/asm/proc-fns.h b/arch/arm/include/asm/proc-fns.h index db80203b68e0..d9faa2b32dfb 100644 --- a/arch/arm/include/asm/proc-fns.h +++ b/arch/arm/include/asm/proc-fns.h @@ -209,6 +209,14 @@ # define CPU_NAME cpu_v7 # endif # endif +# ifdef CONFIG_CPU_V7M +# ifdef CPU_NAME +# undef MULTI_CPU +# define MULTI_CPU +# else +# define CPU_NAME cpu_v7m +# endif +# endif #endif #ifndef __ASSEMBLY__ diff --git a/arch/arm/include/asm/processor.h b/arch/arm/include/asm/processor.h index 1845892260e7..c6dcc6a31a50 100644 --- a/arch/arm/include/asm/processor.h +++ b/arch/arm/include/asm/processor.h @@ -57,7 +57,15 @@ struct thread_struct { #ifdef CONFIG_MMU #define nommu_start_thread(regs) do { } while (0) #else +#ifndef CONFIG_CPU_V7M #define nommu_start_thread(regs) regs->ARM_r10 = current->mm->start_data +#else +#define nommu_start_thread(regs) do { \ + regs->ARM_r10 = current->mm->start_data; \ + regs->ARM_sp -= 32; /* exception return state */ \ + regs->ARM_EXC_lr = 0xfffffffdL; /* exception lr */ \ +} while (0) +#endif #endif #define start_thread(regs,pc,sp) \ @@ -71,7 +79,8 @@ struct thread_struct { regs->ARM_cpsr = USR26_MODE; \ if (elf_hwcap & HWCAP_THUMB && pc & 1) \ regs->ARM_cpsr |= PSR_T_BIT; \ - regs->ARM_pc = pc & ~1; /* pc */ \ + regs->ARM_cpsr |= PSR_ENDSTATE; \ + regs->ARM_pc = pc /*& ~1*/; /* pc */ \ regs->ARM_sp = sp; /* sp */ \ regs->ARM_r2 = stack[2]; /* r2 (envp) */ \ regs->ARM_r1 = stack[1]; /* r1 (argv) */ \ diff --git a/arch/arm/include/asm/ptrace.h b/arch/arm/include/asm/ptrace.h index 236a06b9b7ce..1f7419c9fcc0 100644 --- a/arch/arm/include/asm/ptrace.h +++ b/arch/arm/include/asm/ptrace.h @@ -37,19 +37,29 @@ #define FIQ26_MODE 0x00000001 #define IRQ26_MODE 0x00000002 #define SVC26_MODE 0x00000003 +#ifndef CONFIG_CPU_V7M #define USR_MODE 0x00000010 +#define SVC_MODE 0x00000013 +#else +#define USR_MODE 0x00000000 +#define SVC_MODE 0x00000000 +#endif #define FIQ_MODE 0x00000011 #define IRQ_MODE 0x00000012 -#define SVC_MODE 0x00000013 #define ABT_MODE 0x00000017 #define UND_MODE 0x0000001b #define SYSTEM_MODE 0x0000001f #define MODE32_BIT 0x00000010 #define MODE_MASK 0x0000001f +#ifndef CONFIG_CPU_V7M #define PSR_T_BIT 0x00000020 +#else +#define PSR_T_BIT 0x01000000 +#endif #define PSR_F_BIT 0x00000040 #define PSR_I_BIT 0x00000080 #define PSR_A_BIT 0x00000100 +#define PSR_E_BIT 0x00000200 #define PSR_J_BIT 0x01000000 #define PSR_Q_BIT 0x08000000 #define PSR_V_BIT 0x10000000 @@ -65,6 +75,30 @@ #define PSR_x 0x0000ff00 /* Extension */ #define PSR_c 0x000000ff /* Control */ +/* + * ARMv7 groups of APSR bits + */ +#define PSR_ISET_MASK 0x01000010 /* ISA state (J, T) mask */ +#define PSR_IT_MASK 0x0600fc00 /* If-Then execution state mask */ +#define PSR_ENDIAN_MASK 0x00000200 /* Endianness state mask */ + +/* + * Default endianness state + */ +#ifdef CONFIG_CPU_ENDIAN_BE8 +#define PSR_ENDSTATE PSR_E_BIT +#else +#define PSR_ENDSTATE 0 +#endif + +/* + * These are 'magic' values for PTRACE_PEEKUSR that return info about where a + * process is located in memory. + */ +#define PT_TEXT_ADDR 0x10000 +#define PT_DATA_ADDR 0x10004 +#define PT_TEXT_END_ADDR 0x10008 + #ifndef __ASSEMBLY__ /* @@ -76,6 +110,28 @@ struct pt_regs { long uregs[18]; }; +#ifdef CONFIG_CPU_V7M +/* Automatically saved registers */ +#define ARM_cpsr uregs[17] +#define ARM_pc uregs[16] +#define ARM_lr uregs[15] +#define ARM_ip uregs[14] +#define ARM_r3 uregs[13] +#define ARM_r2 uregs[12] +#define ARM_r1 uregs[11] +#define ARM_r0 uregs[10] +/* saved by the exception entry code */ +#define ARM_EXC_lr uregs[9] +#define ARM_sp uregs[8] +#define ARM_fp uregs[7] +#define ARM_r10 uregs[6] +#define ARM_r9 uregs[5] +#define ARM_r8 uregs[4] +#define ARM_r7 uregs[3] +#define ARM_r6 uregs[2] +#define ARM_r5 uregs[1] +#define ARM_r4 uregs[0] +#else #define ARM_cpsr uregs[16] #define ARM_pc uregs[15] #define ARM_lr uregs[14] @@ -94,6 +150,7 @@ struct pt_regs { #define ARM_r1 uregs[1] #define ARM_r0 uregs[0] #define ARM_ORIG_r0 uregs[17] +#endif #ifdef __KERNEL__ @@ -125,6 +182,7 @@ struct pt_regs { */ static inline int valid_user_regs(struct pt_regs *regs) { +#ifndef CONFIG_CPU_V7M if (user_mode(regs) && (regs->ARM_cpsr & PSR_I_BIT) == 0) { regs->ARM_cpsr &= ~(PSR_F_BIT | PSR_A_BIT); return 1; @@ -138,6 +196,9 @@ static inline int valid_user_regs(struct pt_regs *regs) regs->ARM_cpsr |= USR_MODE; return 0; +#else + return 1; +#endif } #define instruction_pointer(regs) (regs)->ARM_pc diff --git a/arch/arm/include/asm/spinlock.h b/arch/arm/include/asm/spinlock.h index 2b41ebbfa7ff..cfc6f6dc98c4 100644 --- a/arch/arm/include/asm/spinlock.h +++ b/arch/arm/include/asm/spinlock.h @@ -23,15 +23,38 @@ #define __raw_spin_lock_flags(lock, flags) __raw_spin_lock(lock) +#ifdef CONFIG_ARM_ERRATA_351422 +#define spinlock_backoff_delay() \ +{ \ + unsigned int delay; \ + __asm__ __volatile__( \ + "1: mrc p15, 0, %0, c0, c0, 5\n" \ + " and %0, %0, #0xf\n" \ + " mov %0, %0, lsl #8\n" \ + "2: subs %0, %0, #1\n" \ + " bpl 2b\n" \ + : "=&r" (delay) \ + : \ + : "cc" ); \ +} +#else +#define spinlock_backoff_delay() \ + __asm__ __volatile__("1: \n"); +#endif + static inline void __raw_spin_lock(raw_spinlock_t *lock) { unsigned long tmp; + spinlock_backoff_delay(); __asm__ __volatile__( -"1: ldrex %0, [%1]\n" +" ldrex %0, [%1]\n" " teq %0, #0\n" #ifdef CONFIG_CPU_32v6K +" itee ne\n" " wfene\n" +#else +" itt eq\n" #endif " strexeq %0, %2, [%1]\n" " teqeq %0, #0\n" @@ -47,9 +70,11 @@ static inline int __raw_spin_trylock(raw_spinlock_t *lock) { unsigned long tmp; + spinlock_backoff_delay(); __asm__ __volatile__( " ldrex %0, [%1]\n" " teq %0, #0\n" +" it eq\n" " strexeq %0, %2, [%1]" : "=&r" (tmp) : "r" (&lock->lock), "r" (1) @@ -90,11 +115,15 @@ static inline void __raw_write_lock(raw_rwlock_t *rw) { unsigned long tmp; + spinlock_backoff_delay(); __asm__ __volatile__( -"1: ldrex %0, [%1]\n" +" ldrex %0, [%1]\n" " teq %0, #0\n" #ifdef CONFIG_CPU_32v6K +" ite ne\n" " wfene\n" +#else +" it eq\n" #endif " strexeq %0, %2, [%1]\n" " teq %0, #0\n" @@ -110,9 +139,11 @@ static inline int __raw_write_trylock(raw_rwlock_t *rw) { unsigned long tmp; + spinlock_backoff_delay(); __asm__ __volatile__( "1: ldrex %0, [%1]\n" " teq %0, #0\n" +" it eq\n" " strexeq %0, %2, [%1]" : "=&r" (tmp) : "r" (&rw->lock), "r" (0x80000000) @@ -160,9 +191,15 @@ static inline void __raw_read_lock(raw_rwlock_t *rw) { unsigned long tmp, tmp2; + spinlock_backoff_delay(); __asm__ __volatile__( -"1: ldrex %0, [%2]\n" +" ldrex %0, [%2]\n" " adds %0, %0, #1\n" +#ifdef CONFIG_CPU_32v6K +" itet pl\n" +#else +" itt pl\n" +#endif " strexpl %1, %0, [%2]\n" #ifdef CONFIG_CPU_32v6K " wfemi\n" @@ -182,14 +219,16 @@ static inline void __raw_read_unlock(raw_rwlock_t *rw) smp_mb(); + spinlock_backoff_delay(); __asm__ __volatile__( -"1: ldrex %0, [%2]\n" +" ldrex %0, [%2]\n" " sub %0, %0, #1\n" " strex %1, %0, [%2]\n" " teq %1, #0\n" " bne 1b" #ifdef CONFIG_CPU_32v6K "\n cmp %0, #0\n" +" itt eq\n" " mcreq p15, 0, %0, c7, c10, 4\n" " seveq" #endif @@ -205,6 +244,7 @@ static inline int __raw_read_trylock(raw_rwlock_t *rw) __asm__ __volatile__( "1: ldrex %0, [%2]\n" " adds %0, %0, #1\n" +" it pl\n" " strexpl %1, %0, [%2]\n" : "=&r" (tmp), "+r" (tmp2) : "r" (&rw->lock) diff --git a/arch/arm/include/asm/stacktrace.h b/arch/arm/include/asm/stacktrace.h new file mode 100644 index 000000000000..4d0a16441b29 --- /dev/null +++ b/arch/arm/include/asm/stacktrace.h @@ -0,0 +1,15 @@ +#ifndef __ASM_STACKTRACE_H +#define __ASM_STACKTRACE_H + +struct stackframe { + unsigned long fp; + unsigned long sp; + unsigned long lr; + unsigned long pc; +}; + +extern int unwind_frame(struct stackframe *frame); +extern void walk_stackframe(struct stackframe *frame, + int (*fn)(struct stackframe *, void *), void *data); + +#endif /* __ASM_STACKTRACE_H */ diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h index 811be55f338e..9eeda3c177de 100644 --- a/arch/arm/include/asm/system.h +++ b/arch/arm/include/asm/system.h @@ -13,6 +13,7 @@ #define CPU_ARCH_ARMv5TEJ 7 #define CPU_ARCH_ARMv6 8 #define CPU_ARCH_ARMv7 9 +#define CPU_ARCH_ARMv7M 10 /* * CR1 bits (CP#15 CR1) @@ -198,7 +199,9 @@ static inline void set_copro_access(unsigned int val) * so enable interrupts over the context switch to avoid high * latency. */ +#ifndef CONFIG_CPU_V7M #define __ARCH_WANT_INTERRUPTS_ON_CTXSW +#endif /* * switch_to(prev, next) should switch from task `prev' to `next' @@ -321,6 +324,56 @@ extern void enable_hlt(void); #ifndef CONFIG_SMP #include <asm-generic/cmpxchg.h> +#elif __LINUX_ARM_ARCH__ >= 6 +extern void __bad_cmpxchg(volatile void *ptr, int size); + +static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, + unsigned long new, int size) +{ + unsigned long oldval, res; + + switch (size) { + case 1: + do { + asm volatile("@ __cmpxchg1\n" + " ldrexb %1, [%2]\n" + " mov %0, #0\n" + " teq %1, %3\n" + " it eq\n" + " strexeqb %0, %4, [%2]\n" + : "=&r" (res), "=&r" (oldval) + : "r" (ptr), "Ir" (old), "r" (new) + : "cc"); + } while (res); + break; + + case 4: + do { + asm volatile("@ __cmpxchg4\n" + " ldrex %1, [%2]\n" + " mov %0, #0\n" + " teq %1, %3\n" + " it eq\n" + " strexeq %0, %4, [%2]\n" + : "=&r" (res), "=&r" (oldval) + : "r" (ptr), "Ir" (old), "r" (new) + : "cc"); + } while (res); + break; + + default: + __bad_cmpxchg(ptr, size); + oldval = 0; + } + + return oldval; +} + +#define cmpxchg(ptr,o,n) \ + ((__typeof__(*(ptr)))__cmpxchg((ptr), \ + (unsigned long)(o), \ + (unsigned long)(n), \ + sizeof(*(ptr)))) #endif #endif /* __ASSEMBLY__ */ diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h index b9dc8a842573..4f8848260ee2 100644 --- a/arch/arm/include/asm/thread_info.h +++ b/arch/arm/include/asm/thread_info.h @@ -99,6 +99,8 @@ static inline struct thread_info *current_thread_info(void) #define thread_saved_pc(tsk) \ ((unsigned long)(task_thread_info(tsk)->cpu_context.pc)) +#define thread_saved_sp(tsk) \ + ((unsigned long)(task_thread_info(tsk)->cpu_context.sp)) #define thread_saved_fp(tsk) \ ((unsigned long)(task_thread_info(tsk)->cpu_context.fp)) diff --git a/arch/arm/include/asm/tlbflush.h b/arch/arm/include/asm/tlbflush.h index b543a054a17e..214c6501b3af 100644 --- a/arch/arm/include/asm/tlbflush.h +++ b/arch/arm/include/asm/tlbflush.h @@ -39,6 +39,11 @@ #define TLB_V6_D_ASID (1 << 17) #define TLB_V6_I_ASID (1 << 18) +/* Unified Inner Shareable TLB operations (ARMv7 MP extensions) */ +#define TLB_V7_UIS_PAGE (1 << 19) +#define TLB_V7_UIS_FULL (1 << 20) +#define TLB_V7_UIS_ASID (1 << 21) + #define TLB_L2CLEAN_FR (1 << 29) /* Feroceon */ #define TLB_DCLEAN (1 << 30) #define TLB_WB (1 << 31) @@ -158,9 +163,17 @@ # define v6wbi_always_flags (-1UL) #endif +#ifdef CONFIG_SMP +#define v7wbi_tlb_flags (TLB_WB | TLB_DCLEAN | \ + TLB_V7_UIS_FULL | TLB_V7_UIS_PAGE | TLB_V7_UIS_ASID) +#else +#define v7wbi_tlb_flags (TLB_WB | TLB_DCLEAN | \ + TLB_V6_U_FULL | TLB_V6_U_PAGE | TLB_V6_U_ASID) +#endif + #ifdef CONFIG_CPU_TLB_V7 -# define v7wbi_possible_flags v6wbi_tlb_flags -# define v7wbi_always_flags v6wbi_tlb_flags +# define v7wbi_possible_flags v7wbi_tlb_flags +# define v7wbi_always_flags v7wbi_tlb_flags # ifdef _TLB # define MULTI_TLB 1 # else @@ -178,6 +191,7 @@ #ifndef __ASSEMBLY__ #include <linux/sched.h> +#include <asm/cputype.h> struct cpu_tlb_fns { void (*flush_user_range)(unsigned long, unsigned long, struct vm_area_struct *); @@ -296,10 +310,11 @@ static inline void local_flush_tlb_all(void) asm("mcr p15, 0, %0, c8, c6, 0" : : "r" (zero) : "cc"); if (tlb_flag(TLB_V4_I_FULL | TLB_V6_I_FULL)) asm("mcr p15, 0, %0, c8, c5, 0" : : "r" (zero) : "cc"); + if (tlb_flag(TLB_V7_UIS_FULL)) + asm("mcr p15, 0, %0, c8, c3, 0" : : "r" (zero) : "cc"); - if (tlb_flag(TLB_V6_I_FULL | TLB_V6_D_FULL | - TLB_V6_I_PAGE | TLB_V6_D_PAGE | - TLB_V6_I_ASID | TLB_V6_D_ASID)) { + if (tlb_flag(TLB_V6_I_FULL | TLB_V6_D_FULL | TLB_V6_U_FULL | + TLB_V7_UIS_FULL)) { /* flush the branch target cache */ asm("mcr p15, 0, %0, c7, c5, 6" : : "r" (zero) : "cc"); dsb(); @@ -333,10 +348,11 @@ static inline void local_flush_tlb_mm(struct mm_struct *mm) asm("mcr p15, 0, %0, c8, c6, 2" : : "r" (asid) : "cc"); if (tlb_flag(TLB_V6_I_ASID)) asm("mcr p15, 0, %0, c8, c5, 2" : : "r" (asid) : "cc"); + if (tlb_flag(TLB_V7_UIS_ASID)) + asm("mcr p15, 0, %0, c8, c3, 2" : : "r" (asid) : "cc"); - if (tlb_flag(TLB_V6_I_FULL | TLB_V6_D_FULL | - TLB_V6_I_PAGE | TLB_V6_D_PAGE | - TLB_V6_I_ASID | TLB_V6_D_ASID)) { + if (tlb_flag(TLB_V6_I_ASID | TLB_V6_D_ASID | TLB_V6_U_ASID | + TLB_V7_UIS_ASID)) { /* flush the branch target cache */ asm("mcr p15, 0, %0, c7, c5, 6" : : "r" (zero) : "cc"); dsb(); @@ -373,10 +389,11 @@ local_flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr) asm("mcr p15, 0, %0, c8, c6, 1" : : "r" (uaddr) : "cc"); if (tlb_flag(TLB_V6_I_PAGE)) asm("mcr p15, 0, %0, c8, c5, 1" : : "r" (uaddr) : "cc"); + if (tlb_flag(TLB_V7_UIS_PAGE)) + asm("mcr p15, 0, %0, c8, c3, 1" : : "r" (uaddr) : "cc"); - if (tlb_flag(TLB_V6_I_FULL | TLB_V6_D_FULL | - TLB_V6_I_PAGE | TLB_V6_D_PAGE | - TLB_V6_I_ASID | TLB_V6_D_ASID)) { + if (tlb_flag(TLB_V6_I_PAGE | TLB_V6_D_PAGE | TLB_V6_U_PAGE | + TLB_V7_UIS_PAGE)) { /* flush the branch target cache */ asm("mcr p15, 0, %0, c7, c5, 6" : : "r" (zero) : "cc"); dsb(); @@ -410,10 +427,11 @@ static inline void local_flush_tlb_kernel_page(unsigned long kaddr) asm("mcr p15, 0, %0, c8, c6, 1" : : "r" (kaddr) : "cc"); if (tlb_flag(TLB_V6_I_PAGE)) asm("mcr p15, 0, %0, c8, c5, 1" : : "r" (kaddr) : "cc"); + if (tlb_flag(TLB_V7_UIS_PAGE)) + asm("mcr p15, 0, %0, c8, c3, 1" : : "r" (kaddr) : "cc"); - if (tlb_flag(TLB_V6_I_FULL | TLB_V6_D_FULL | - TLB_V6_I_PAGE | TLB_V6_D_PAGE | - TLB_V6_I_ASID | TLB_V6_D_ASID)) { + if (tlb_flag(TLB_V6_I_PAGE | TLB_V6_D_PAGE | TLB_V6_U_PAGE | + TLB_V7_UIS_PAGE)) { /* flush the branch target cache */ asm("mcr p15, 0, %0, c7, c5, 6" : : "r" (zero) : "cc"); dsb(); @@ -474,21 +492,70 @@ static inline void clean_pmd_entry(pmd_t *pmd) #define local_flush_tlb_kernel_range(s,e) __cpu_flush_kern_tlb_range(s,e) #ifndef CONFIG_SMP -#define flush_tlb_all local_flush_tlb_all -#define flush_tlb_mm local_flush_tlb_mm -#define flush_tlb_page local_flush_tlb_page -#define flush_tlb_kernel_page local_flush_tlb_kernel_page -#define flush_tlb_range local_flush_tlb_range -#define flush_tlb_kernel_range local_flush_tlb_kernel_range +#define tlb_ops_need_broadcast() 0 #else -extern void flush_tlb_all(void); -extern void flush_tlb_mm(struct mm_struct *mm); -extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr); -extern void flush_tlb_kernel_page(unsigned long kaddr); -extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end); -extern void flush_tlb_kernel_range(unsigned long start, unsigned long end); +/* all SMP configurations have the extended CPUID registers */ +static inline int tlb_ops_need_broadcast(void) +{ + return ((read_cpuid_ext(CPUID_EXT_MMFR3) >> 12) & 0xf) < 2; +} #endif +extern void smp_flush_tlb_all(void); +extern void smp_flush_tlb_mm(struct mm_struct *mm); +extern void smp_flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr); +extern void smp_flush_tlb_kernel_page(unsigned long kaddr); +extern void smp_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end); +extern void smp_flush_tlb_kernel_range(unsigned long start, unsigned long end); + +static inline void flush_tlb_all(void) +{ + if (tlb_ops_need_broadcast()) + smp_flush_tlb_all(); + else + local_flush_tlb_all(); +} + +static inline void flush_tlb_mm(struct mm_struct *mm) +{ + if (tlb_ops_need_broadcast()) + smp_flush_tlb_mm(mm); + else + local_flush_tlb_mm(mm); +} + +static inline void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr) +{ + if (tlb_ops_need_broadcast()) + smp_flush_tlb_page(vma, uaddr); + else + local_flush_tlb_page(vma, uaddr); +} + +static inline void flush_tlb_kernel_page(unsigned long kaddr) +{ + if (tlb_ops_need_broadcast()) + smp_flush_tlb_kernel_page(kaddr); + else + local_flush_tlb_kernel_page(kaddr); +} + +static inline void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end) +{ + if (tlb_ops_need_broadcast()) + smp_flush_tlb_range(vma, start, end); + else + local_flush_tlb_range(vma, start, end); +} + +static inline void flush_tlb_kernel_range(unsigned long start, unsigned long end) +{ + if (tlb_ops_need_broadcast()) + smp_flush_tlb_kernel_range(start, end); + else + local_flush_tlb_kernel_range(start, end); +} + /* * if PG_dcache_dirty is set for the page, we need to ensure that any * cache entries for the kernels virtual memory range are written diff --git a/arch/arm/include/asm/traps.h b/arch/arm/include/asm/traps.h index aa399aec568e..491960bf4260 100644 --- a/arch/arm/include/asm/traps.h +++ b/arch/arm/include/asm/traps.h @@ -25,5 +25,6 @@ static inline int in_exception_text(unsigned long ptr) } extern void __init early_trap_init(void); +extern void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame); #endif diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h index 7897464e0c24..7f92975565da 100644 --- a/arch/arm/include/asm/uaccess.h +++ b/arch/arm/include/asm/uaccess.h @@ -13,6 +13,8 @@ */ #include <linux/string.h> #include <linux/thread_info.h> +#include <linux/sched.h> +#include <asm/unified.h> #include <asm/errno.h> #include <asm/memory.h> #include <asm/domain.h> @@ -69,7 +71,7 @@ static inline void set_fs(mm_segment_t fs) #define __addr_ok(addr) ({ \ unsigned long flag; \ - __asm__("cmp %2, %0; movlo %0, #0" \ + __asm__("cmp %2, %0; it lo; movlo %0, #0" \ : "=&r" (flag) \ : "0" (current_thread_info()->addr_limit), "r" (addr) \ : "cc"); \ @@ -79,7 +81,7 @@ static inline void set_fs(mm_segment_t fs) #define __range_ok(addr,size) ({ \ unsigned long flag, roksum; \ __chk_user_ptr(addr); \ - __asm__("adds %1, %2, %3; sbcccs %1, %1, %0; movcc %0, #0" \ + __asm__("adds %1, %2, %3; it cc; sbcccs %1, %1, %0; it cc; movcc %0, #0" \ : "=&r" (flag), "=&r" (roksum) \ : "r" (addr), "Ir" (size), "0" (current_thread_info()->addr_limit) \ : "cc"); \ @@ -365,8 +367,10 @@ do { \ #define __put_user_asm_dword(x,__pu_addr,err) \ __asm__ __volatile__( \ - "1: strt " __reg_oper1 ", [%1], #4\n" \ - "2: strt " __reg_oper0 ", [%1]\n" \ + ARM( "1: strt " __reg_oper1 ", [%1], #4\n" ) \ + ARM( "2: strt " __reg_oper0 ", [%1]\n" ) \ + THUMB( "1: strt " __reg_oper1 ", [%1]\n" ) \ + THUMB( "2: strt " __reg_oper0 ", [%1, #4]\n" ) \ "3:\n" \ " .section .fixup,\"ax\"\n" \ " .align 2\n" \ diff --git a/arch/arm/include/asm/unified.h b/arch/arm/include/asm/unified.h new file mode 100644 index 000000000000..82194c0ca1ec --- /dev/null +++ b/arch/arm/include/asm/unified.h @@ -0,0 +1,126 @@ +/* + * include/asm-arm/unified.h - Unified Assembler Syntax helper macros + * + * Copyright (C) 2008 ARM Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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_UNIFIED_H +#define __ASM_UNIFIED_H + +#if defined(__ASSEMBLY__) && defined(CONFIG_ARM_ASM_UNIFIED) + .syntax unified +#endif + +#ifdef CONFIG_THUMB2_KERNEL + +#if __GNUC__ < 4 +#error Thumb-2 kernel requires gcc >= 4 +#endif + +/* The CPSR bit describing the instruction set (Thumb) */ +#define PSR_ISETSTATE PSR_T_BIT + +#define ARM(x...) +#define THUMB(x...) x +#define W(instr) instr.w +#define BSYM(sym) sym + 1 + +#else /* !CONFIG_THUMB2_KERNEL */ + +/* The CPSR bit describing the instruction set (ARM) */ +#define PSR_ISETSTATE 0 + +#define ARM(x...) x +#define THUMB(x...) +#define W(instr) instr +#define BSYM(sym) sym + +#endif /* CONFIG_THUMB2_KERNEL */ + +#ifndef CONFIG_ARM_ASM_UNIFIED + +/* + * If the unified assembly syntax isn't used (in ARM mode), these + * macros expand to an empty string + */ +#ifdef __ASSEMBLY__ + .macro it, cond + .endm + .macro itt, cond + .endm + .macro ite, cond + .endm + .macro ittt, cond + .endm + .macro itte, cond + .endm + .macro itet, cond + .endm + .macro itee, cond + .endm + .macro itttt, cond + .endm + .macro ittte, cond + .endm + .macro ittet, cond + .endm + .macro ittee, cond + .endm + .macro itett, cond + .endm + .macro itete, cond + .endm + .macro iteet, cond + .endm + .macro iteee, cond + .endm +#else /* !__ASSEMBLY__ */ +__asm__( +" .macro it, cond\n" +" .endm\n" +" .macro itt, cond\n" +" .endm\n" +" .macro ite, cond\n" +" .endm\n" +" .macro ittt, cond\n" +" .endm\n" +" .macro itte, cond\n" +" .endm\n" +" .macro itet, cond\n" +" .endm\n" +" .macro itee, cond\n" +" .endm\n" +" .macro itttt, cond\n" +" .endm\n" +" .macro ittte, cond\n" +" .endm\n" +" .macro ittet, cond\n" +" .endm\n" +" .macro ittee, cond\n" +" .endm\n" +" .macro itett, cond\n" +" .endm\n" +" .macro itete, cond\n" +" .endm\n" +" .macro iteet, cond\n" +" .endm\n" +" .macro iteee, cond\n" +" .endm\n"); +#endif /* __ASSEMBLY__ */ + +#endif /* CONFIG_ARM_ASM_UNIFIED */ + +#endif /* !CONFIG_ARM_ASM_UNIFIED */ diff --git a/arch/arm/include/asm/unwind.h b/arch/arm/include/asm/unwind.h new file mode 100644 index 000000000000..a5edf421005c --- /dev/null +++ b/arch/arm/include/asm/unwind.h @@ -0,0 +1,69 @@ +/* + * arch/arm/include/asm/unwind.h + * + * Copyright (C) 2008 ARM Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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_UNWIND_H +#define __ASM_UNWIND_H + +#ifndef __ASSEMBLY__ + +/* Unwind reason code according the the ARM EABI documents */ +enum unwind_reason_code { + URC_OK = 0, /* operation completed successfully */ + URC_CONTINUE_UNWIND = 8, + URC_FAILURE = 9 /* unspecified failure of some kind */ +}; + +struct unwind_idx { + unsigned long addr; + unsigned long insn; +}; + +struct unwind_table { + struct list_head list; + struct unwind_idx *start; + struct unwind_idx *stop; + unsigned long begin_addr; + unsigned long end_addr; +}; + +extern struct unwind_table *unwind_table_add(unsigned long start, + unsigned long size, + unsigned long text_addr, + unsigned long text_size); +extern void unwind_table_del(struct unwind_table *tab); +extern void unwind_backtrace(struct pt_regs *regs, struct task_struct *tsk); + +#ifdef CONFIG_ARM_UNWIND +extern int __init unwind_init(void); +#else +static inline int __init unwind_init(void) +{ + return 0; +} +#endif + +#endif /* !__ASSEMBLY__ */ + +#ifdef CONFIG_ARM_UNWIND +#define UNWIND(code...) code +#else +#define UNWIND(code...) +#endif + +#endif /* __ASM_UNWIND_H */ diff --git a/arch/arm/include/asm/vfpmacros.h b/arch/arm/include/asm/vfpmacros.h index 422f3cc204a2..79ccd6cedb59 100644 --- a/arch/arm/include/asm/vfpmacros.h +++ b/arch/arm/include/asm/vfpmacros.h @@ -25,6 +25,7 @@ VFPFMRX \tmp, MVFR0 @ Media and VFP Feature Register 0 and \tmp, \tmp, #MVFR0_A_SIMD_MASK @ A_SIMD field cmp \tmp, #2 @ 32 x 64bit registers? + ite eq ldceql p11, cr0, [\base],#32*4 @ FLDMIAD \base!, {d16-d31} addne \base, \base, #32*4 @ step over unused register space #endif @@ -32,6 +33,9 @@ @ write all the working registers out of the VFP .macro VFPFSTMIA, base, tmp +#ifdef CONFIG_ARM_ERRATUM_451034 + dmb +#endif #if __LINUX_ARM_ARCH__ < 6 STC p11, cr0, [\base],#33*4 @ FSTMIAX \base!, {d0-d15} #else @@ -41,6 +45,7 @@ VFPFMRX \tmp, MVFR0 @ Media and VFP Feature Register 0 and \tmp, \tmp, #MVFR0_A_SIMD_MASK @ A_SIMD field cmp \tmp, #2 @ 32 x 64bit registers? + ite eq stceql p11, cr0, [\base],#32*4 @ FSTMIAD \base!, {d16-d31} addne \base, \base, #32*4 @ step over unused register space #endif diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 4305345987d3..3f30f220f32c 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -10,10 +10,16 @@ endif # Object file lists. -obj-y := compat.o elf.o entry-armv.o entry-common.o irq.o \ +obj-y := compat.o elf.o entry-common.o irq.o \ process.o ptrace.o setup.o signal.o \ sys_arm.o stacktrace.o time.o traps.o +ifeq ($(CONFIG_CPU_V7M),y) +obj-y += entry-v7m.o +else +obj-y += entry-armv.o +endif + obj-$(CONFIG_ISA_DMA_API) += dma.o obj-$(CONFIG_ARCH_ACORN) += ecard.o obj-$(CONFIG_FIQ) += fiq.o @@ -29,6 +35,7 @@ obj-$(CONFIG_ATAGS_PROC) += atags.o obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o obj-$(CONFIG_ARM_THUMBEE) += thumbee.o obj-$(CONFIG_KGDB) += kgdb.o +obj-$(CONFIG_ARM_UNWIND) += unwind.o obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o AFLAGS_crunch-bits.o := -Wa,-mcpu=ep9312 @@ -44,5 +51,6 @@ endif head-y := head$(MMUEXT).o obj-$(CONFIG_DEBUG_LL) += debug.o +obj-$(CONFIG_DEBUG_LL_CONSOLE) += early_printk.o extra-y := $(head-y) init_task.o vmlinux.lds diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c index 531e1860e546..6b5a79d64807 100644 --- a/arch/arm/kernel/armksyms.c +++ b/arch/arm/kernel/armksyms.c @@ -47,8 +47,10 @@ extern void __aeabi_uidiv(void); extern void __aeabi_uidivmod(void); extern void __aeabi_ulcmp(void); +#ifndef CONFIG_CPU_V7M extern void fpundefinstr(void); extern void fp_enter(void); +#endif /* * This has a special calling convention; it doesn't @@ -66,7 +68,9 @@ extern void fp_enter(void); * floating point math emulator support. * These symbols will never change their calling convention... */ +#ifndef CONFIG_CPU_V7M EXPORT_SYMBOL_ALIAS(kern_fp_enter,fp_enter); +#endif EXPORT_SYMBOL_ALIAS(fp_printk,printk); EXPORT_SYMBOL_ALIAS(fp_send_sig,send_sig); diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c index 4a881258bb17..009c1a126c05 100644 --- a/arch/arm/kernel/asm-offsets.c +++ b/arch/arm/kernel/asm-offsets.c @@ -79,7 +79,11 @@ int main(void) DEFINE(S_LR, offsetof(struct pt_regs, ARM_lr)); DEFINE(S_PC, offsetof(struct pt_regs, ARM_pc)); DEFINE(S_PSR, offsetof(struct pt_regs, ARM_cpsr)); +#ifdef CONFIG_CPU_V7M + DEFINE(S_EXC_LR, offsetof(struct pt_regs, ARM_EXC_lr)); +#else DEFINE(S_OLD_R0, offsetof(struct pt_regs, ARM_ORIG_r0)); +#endif DEFINE(S_FRAME_SIZE, sizeof(struct pt_regs)); BLANK(); #ifdef CONFIG_CPU_HAS_ASID diff --git a/arch/arm/kernel/debug.S b/arch/arm/kernel/debug.S index f53c58290543..e95a5b6657f8 100644 --- a/arch/arm/kernel/debug.S +++ b/arch/arm/kernel/debug.S @@ -105,6 +105,7 @@ printhex: adr r2, hexbuf 1: and r1, r0, #15 mov r0, r0, lsr #4 cmp r1, #10 + ite lt addlt r1, r1, #'0' addge r1, r1, #'a' - 10 strb r1, [r3, #-1]! @@ -123,9 +124,11 @@ ENTRY(printascii) senduart r1, r3 busyuart r2, r3 teq r1, #'\n' + itt eq moveq r1, #'\r' beq 1b 2: teq r0, #0 + itt ne ldrneb r1, [r0], #1 teqne r1, #0 bne 1b diff --git a/arch/arm/kernel/early_printk.c b/arch/arm/kernel/early_printk.c new file mode 100644 index 000000000000..a0aea0a5e252 --- /dev/null +++ b/arch/arm/kernel/early_printk.c @@ -0,0 +1,38 @@ +/* + * linux/arch/arm/kernel/early_printk.c + * + * Copyright (C) 2009 Sascha Hauer <s.hauer@pengutronix.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/console.h> +#include <linux/init.h> + +extern void printch(int); + +static void early_serial_write(struct console *con, const char *s, unsigned n) +{ + while (*s && n-- > 0) { + if (*s == '\n') + printch('\r'); + printch(*s); + s++; + } +} + +static struct console early_serial_console = { + .name = "earlyser", + .write = early_serial_write, + .flags = CON_PRINTBUFFER | CON_BOOT, + .index = -1, +}; + +static int __init setup_early_printk(char *buf) +{ + register_console(&early_serial_console); + return 0; +} + +early_param("earlyprintk", setup_early_printk); diff --git a/arch/arm/kernel/elf.c b/arch/arm/kernel/elf.c index d4a0da1e48f4..950391f194c4 100644 --- a/arch/arm/kernel/elf.c +++ b/arch/arm/kernel/elf.c @@ -78,6 +78,15 @@ int arm_elf_read_implies_exec(const struct elf32_hdr *x, int executable_stack) return 1; if (cpu_architecture() < CPU_ARCH_ARMv6) return 1; +#if !defined(CONFIG_AEABI) || defined(CONFIG_OABI_COMPAT) + /* + * If we have support for OABI programs, we can never allow NX + * support - our signal syscall restart mechanism relies upon + * being able to execute code placed on the user stack. + */ + return 1; +#else return 0; +#endif } EXPORT_SYMBOL(arm_elf_read_implies_exec); diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index 85040cfeb5e5..fc08364c408c 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S @@ -20,6 +20,7 @@ #include <asm/vfpmacros.h> #include <mach/entry-macro.S> #include <asm/thread_notify.h> +#include <asm/unwind.h> #include "entry-header.S" @@ -29,12 +30,19 @@ .macro irq_handler get_irqnr_preamble r5, lr 1: get_irqnr_and_base r0, r6, r5, lr + ittt ne movne r1, sp @ @ routine called with r0 = irq number, r1 = struct pt_regs * @ - adrne lr, 1b +#if defined(CONFIG_MACH_REALVIEW_EB) && \ + defined(CONFIG_CPU_V6) && !defined(CONFIG_SMP) + @ GIC bug on RealView EB not allowing register polling + blne asm_do_IRQ +#else + adrne lr, BSYM(1b) bne asm_do_IRQ +#endif #ifdef CONFIG_SMP /* @@ -44,14 +52,16 @@ * preserved from get_irqnr_and_base above */ test_for_ipi r0, r6, r5, lr + ittt ne movne r0, sp - adrne lr, 1b + adrne lr, BSYM(1b) bne do_IPI #ifdef CONFIG_LOCAL_TIMERS test_for_ltirq r0, r6, r5, lr + ittt ne movne r0, sp - adrne lr, 1b + adrne lr, BSYM(1b) bne do_local_timer #endif #endif @@ -69,7 +79,10 @@ */ .macro inv_entry, reason sub sp, sp, #S_FRAME_SIZE - stmib sp, {r1 - lr} + ARM( stmib sp, {r1 - lr} ) + THUMB( stmia sp, {r0 - r12} ) + THUMB( str sp, [sp, #S_SP] ) + THUMB( str lr, [sp, #S_LR] ) mov r1, #\reason .endm @@ -123,17 +136,28 @@ ENDPROC(__und_invalid) #endif .macro svc_entry, stack_hole=0 - sub sp, sp, #(S_FRAME_SIZE + \stack_hole) + UNWIND(.fnstart ) + UNWIND(.save {r0 - pc} ) + sub sp, sp, #(S_FRAME_SIZE + \stack_hole - 4) +#ifdef CONFIG_THUMB2_KERNEL + SPFIX( str r0, [sp] ) @ temporarily saved + SPFIX( mov r0, sp ) + SPFIX( tst r0, #4 ) @ test original stack alignment + SPFIX( ldr r0, [sp] ) @ restored +#else SPFIX( tst sp, #4 ) - SPFIX( bicne sp, sp, #4 ) - stmib sp, {r1 - r12} +#endif + SPFIX( it eq ) + SPFIX( subeq sp, sp, #4 ) + stmia sp, {r1 - r12} ldmia r0, {r1 - r3} - add r5, sp, #S_SP @ here for interlock avoidance + add r5, sp, #S_SP - 4 @ here for interlock avoidance mov r4, #-1 @ "" "" "" "" - add r0, sp, #(S_FRAME_SIZE + \stack_hole) - SPFIX( addne r0, r0, #4 ) - str r1, [sp] @ save the "real" r0 copied + add r0, sp, #(S_FRAME_SIZE + \stack_hole - 4) + SPFIX( it eq ) + SPFIX( addeq r0, r0, #4 ) + str r1, [sp, #-4]! @ save the "real" r0 copied @ from the exception stack mov r1, lr @@ -159,6 +183,7 @@ __dabt_svc: @ mrs r9, cpsr tst r3, #PSR_I_BIT + it eq biceq r9, r9, #PSR_I_BIT @ @@ -193,9 +218,9 @@ __dabt_svc: @ @ restore SPSR and restart the instruction @ - ldr r0, [sp, #S_PSR] - msr spsr_cxsf, r0 - ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr + ldr r2, [sp, #S_PSR] + svc_exit r2 @ return from exception + UNWIND(.fnend ) ENDPROC(__dabt_svc) .align 5 @@ -217,17 +242,19 @@ __irq_svc: str r8, [tsk, #TI_PREEMPT] @ restore preempt count ldr r0, [tsk, #TI_FLAGS] @ get flags teq r8, #0 @ if preempt count != 0 + it ne movne r0, #0 @ force flags to 0 tst r0, #_TIF_NEED_RESCHED + it ne blne svc_preempt #endif - ldr r0, [sp, #S_PSR] @ irqs are already disabled - msr spsr_cxsf, r0 + ldr r4, [sp, #S_PSR] @ irqs are already disabled #ifdef CONFIG_TRACE_IRQFLAGS - tst r0, #PSR_I_BIT + tst r4, #PSR_I_BIT bleq trace_hardirqs_on #endif - ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr + svc_exit r4 @ return from exception + UNWIND(.fnend ) ENDPROC(__irq_svc) .ltorg @@ -238,6 +265,7 @@ svc_preempt: 1: bl preempt_schedule_irq @ irq en/disable is done inside ldr r0, [tsk, #TI_FLAGS] @ get new tasks TI_FLAGS tst r0, #_TIF_NEED_RESCHED + it eq moveq pc, r8 @ go again b 1b #endif @@ -260,8 +288,17 @@ __und_svc: @ @ r0 - instruction @ +#ifndef CONFIG_THUMB2_KERNEL ldr r0, [r2, #-4] - adr r9, 1f +#else + ldrh r0, [r2, #-2] @ Thumb instruction at LR - 2 + and r9, r0, #0xf800 + cmp r9, #0xe800 @ 32-bit instruction if xx >= 0 + itt hs + ldrhhs r9, [r2] @ bottom 16 bits + orrhs r0, r9, r0, lsl #16 +#endif + adr r9, BSYM(1f) bl call_fpe mov r0, sp @ struct pt_regs *regs @@ -275,9 +312,9 @@ __und_svc: @ @ restore SPSR and restart the instruction @ - ldr lr, [sp, #S_PSR] @ Get SVC cpsr - msr spsr_cxsf, lr - ldmia sp, {r0 - pc}^ @ Restore SVC registers + ldr r2, [sp, #S_PSR] @ Get SVC cpsr + svc_exit r2 @ return from exception + UNWIND(.fnend ) ENDPROC(__und_svc) .align 5 @@ -289,6 +326,7 @@ __pabt_svc: @ mrs r9, cpsr tst r3, #PSR_I_BIT + it eq biceq r9, r9, #PSR_I_BIT @ @@ -317,9 +355,9 @@ __pabt_svc: @ @ restore SPSR and restart the instruction @ - ldr r0, [sp, #S_PSR] - msr spsr_cxsf, r0 - ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr + ldr r2, [sp, #S_PSR] + svc_exit r2 @ return from exception + UNWIND(.fnend ) ENDPROC(__pabt_svc) .align 5 @@ -343,8 +381,11 @@ ENDPROC(__pabt_svc) #endif .macro usr_entry + UNWIND(.fnstart ) + UNWIND(.cantunwind ) @ don't unwind the user space sub sp, sp, #S_FRAME_SIZE - stmib sp, {r1 - r12} + ARM( stmib sp, {r1 - r12} ) + THUMB( stmia sp, {r0 - r12} ) ldmia r0, {r1 - r3} add r0, sp, #S_PC @ here for interlock avoidance @@ -363,7 +404,8 @@ ENDPROC(__pabt_svc) @ Also, separately save sp_usr and lr_usr @ stmia r0, {r2 - r4} - stmdb r0, {sp, lr}^ + ARM( stmdb r0, {sp, lr}^ ) + THUMB( store_user_sp_lr r0, r1, S_SP - S_PC ) @ @ Enable the alignment trap while in kernel mode @@ -418,8 +460,9 @@ __dabt_usr: @ enable_irq mov r2, sp - adr lr, ret_from_exception + adr lr, BSYM(ret_from_exception) b do_DataAbort + UNWIND(.fnend ) ENDPROC(__dabt_usr) .align 5 @@ -442,7 +485,10 @@ __irq_usr: ldr r0, [tsk, #TI_PREEMPT] str r8, [tsk, #TI_PREEMPT] teq r0, r7 - strne r0, [r0, -r0] + itt ne + ARM( strne r0, [r0, -r0] ) + THUMB( movne r0, #0 ) + THUMB( strne r0, [r0] ) #endif #ifdef CONFIG_TRACE_IRQFLAGS bl trace_hardirqs_on @@ -450,6 +496,7 @@ __irq_usr: mov why, #0 b ret_to_user + UNWIND(.fnend ) ENDPROC(__irq_usr) .ltorg @@ -465,16 +512,23 @@ __und_usr: @ @ r0 - instruction @ - adr r9, ret_from_exception - adr lr, __und_usr_unknown + adr r9, BSYM(ret_from_exception) + adr lr, BSYM(__und_usr_unknown) tst r3, #PSR_T_BIT @ Thumb mode? + itet eq subeq r4, r2, #4 @ ARM instr at LR - 4 subne r4, r2, #2 @ Thumb instr at LR - 2 1: ldreqt r0, [r4] +#ifdef CONFIG_CPU_ENDIAN_BE8 + reveq r0, r0 @ little endian instruction +#endif beq call_fpe @ Thumb instruction #if __LINUX_ARM_ARCH__ >= 7 -2: ldrht r5, [r4], #2 +2: + ARM( ldrht r5, [r4], #2 ) + THUMB( ldrht r5, [r4] ) + THUMB( add r4, r4, #2 ) and r0, r5, #0xf800 @ mask bits 111x x... .... .... cmp r0, #0xe800 @ 32bit instruction if xx != 0 blo __und_usr_unknown @@ -484,6 +538,7 @@ __und_usr: #else b __und_usr_unknown #endif + UNWIND(.fnend ) ENDPROC(__und_usr) @ @@ -554,6 +609,7 @@ call_fpe: 1: #endif tst r0, #0x08000000 @ only CDP/CPRT/LDC/STC have bit 27 + ite ne tstne r0, #0x04000000 @ bit 26 set on both ARM and Thumb-2 #if defined(CONFIG_CPU_ARM610) || defined(CONFIG_CPU_ARM710) and r8, r0, #0x0f000000 @ mask out op-code bits @@ -562,9 +618,11 @@ call_fpe: moveq pc, lr get_thread_info r10 @ get current thread and r8, r0, #0x00000f00 @ mask out CP number + THUMB( lsr r8, r8, #8 ) mov r7, #1 add r6, r10, #TI_USED_CP - strb r7, [r6, r8, lsr #8] @ set appropriate used_cp[] + ARM( strb r7, [r6, r8, lsr #8] ) @ set appropriate used_cp[] + THUMB( strb r7, [r6, r8] ) @ set appropriate used_cp[] #ifdef CONFIG_IWMMXT @ Test if we need to give access to iWMMXt coprocessors ldr r5, [r10, #TI_FLAGS] @@ -572,36 +630,38 @@ call_fpe: movcss r7, r5, lsr #(TIF_USING_IWMMXT + 1) bcs iwmmxt_task_enable #endif - add pc, pc, r8, lsr #6 - mov r0, r0 - - mov pc, lr @ CP#0 - b do_fpe @ CP#1 (FPE) - b do_fpe @ CP#2 (FPE) - mov pc, lr @ CP#3 + ARM( add pc, pc, r8, lsr #6 ) + THUMB( lsl r8, r8, #2 ) + THUMB( add pc, r8 ) + nop + + movw_pc lr @ CP#0 + W(b) do_fpe @ CP#1 (FPE) + W(b) do_fpe @ CP#2 (FPE) + movw_pc lr @ CP#3 #ifdef CONFIG_CRUNCH b crunch_task_enable @ CP#4 (MaverickCrunch) b crunch_task_enable @ CP#5 (MaverickCrunch) b crunch_task_enable @ CP#6 (MaverickCrunch) #else - mov pc, lr @ CP#4 - mov pc, lr @ CP#5 - mov pc, lr @ CP#6 + movw_pc lr @ CP#4 + movw_pc lr @ CP#5 + movw_pc lr @ CP#6 #endif - mov pc, lr @ CP#7 - mov pc, lr @ CP#8 - mov pc, lr @ CP#9 + movw_pc lr @ CP#7 + movw_pc lr @ CP#8 + movw_pc lr @ CP#9 #ifdef CONFIG_VFP - b do_vfp @ CP#10 (VFP) - b do_vfp @ CP#11 (VFP) + W(b) do_vfp @ CP#10 (VFP) + W(b) do_vfp @ CP#11 (VFP) #else - mov pc, lr @ CP#10 (VFP) - mov pc, lr @ CP#11 (VFP) + movw_pc lr @ CP#10 (VFP) + movw_pc lr @ CP#11 (VFP) #endif - mov pc, lr @ CP#12 - mov pc, lr @ CP#13 - mov pc, lr @ CP#14 (Debug) - mov pc, lr @ CP#15 (Control) + movw_pc lr @ CP#12 + movw_pc lr @ CP#13 + movw_pc lr @ CP#14 (Debug) + movw_pc lr @ CP#15 (Control) #ifdef CONFIG_NEON .align 6 @@ -647,12 +707,14 @@ ENTRY(fp_enter) .word no_fp .previous -no_fp: mov pc, lr +ENTRY(no_fp) + mov pc, lr +ENDPROC(no_fp) __und_usr_unknown: enable_irq mov r0, sp - adr lr, ret_from_exception + adr lr, BSYM(ret_from_exception) b do_undefinstr ENDPROC(__und_usr_unknown) @@ -671,14 +733,18 @@ __pabt_usr: enable_irq @ Enable interrupts mov r1, sp @ regs bl do_PrefetchAbort @ call abort handler + UNWIND(.fnend ) /* fall through */ /* * This is the return code to user mode for abort handlers */ ENTRY(ret_from_exception) + UNWIND(.fnstart ) + UNWIND(.cantunwind ) get_thread_info tsk mov why, #0 b ret_to_user + UNWIND(.fnend ) ENDPROC(__pabt_usr) ENDPROC(ret_from_exception) @@ -688,19 +754,17 @@ ENDPROC(ret_from_exception) * previous and next are guaranteed not to be the same. */ ENTRY(__switch_to) + UNWIND(.fnstart ) + UNWIND(.cantunwind ) add ip, r1, #TI_CPU_SAVE ldr r3, [r2, #TI_TP_VALUE] - stmia ip!, {r4 - sl, fp, sp, lr} @ Store most regs on stack + ARM( stmia ip!, {r4 - sl, fp, sp, lr} ) @ Store most regs on stack + THUMB( stmia ip!, {r4 - sl, fp} ) @ Store most regs on stack + THUMB( str sp, [ip], #4 ) + THUMB( str lr, [ip], #4 ) #ifdef CONFIG_MMU ldr r6, [r2, #TI_CPU_DOMAIN] #endif -#if __LINUX_ARM_ARCH__ >= 6 -#ifdef CONFIG_CPU_32v6K - clrex -#else - strex r5, r4, [ip] @ Clear exclusive monitor -#endif -#endif #if defined(CONFIG_HAS_TLS_REG) mcr p15, 0, r3, c13, c0, 3 @ set TLS register #elif !defined(CONFIG_TLS_REG_EMUL) @@ -715,8 +779,13 @@ ENTRY(__switch_to) ldr r0, =thread_notify_head mov r1, #THREAD_NOTIFY_SWITCH bl atomic_notifier_call_chain + THUMB( mov ip, r4 ) mov r0, r5 - ldmia r4, {r4 - sl, fp, sp, pc} @ Load all regs saved previously + ARM( ldmia r4, {r4 - sl, fp, sp, pc} ) @ Load all regs saved previously + THUMB( ldmia ip!, {r4 - sl, fp} ) @ Load all regs saved previously + THUMB( ldr sp, [ip], #4 ) + THUMB( ldr pc, [ip] ) + UNWIND(.fnend ) ENDPROC(__switch_to) __INIT @@ -750,6 +819,7 @@ ENDPROC(__switch_to) * if your compiled code is not going to use the new instructions for other * purpose. */ + THUMB( .arm ) .macro usr_ret, reg #ifdef CONFIG_ARM_THUMB @@ -921,6 +991,7 @@ kuser_cmpxchg_fixup: #endif 1: ldrex r3, [r2] subs r3, r3, r0 + it eq strexeq r3, r1, [r2] teqeq r3, #1 beq 1b @@ -1001,6 +1072,7 @@ __kuser_helper_version: @ 0xffff0ffc .globl __kuser_helper_end __kuser_helper_end: + THUMB( .thumb ) /* * Vector stubs. @@ -1035,17 +1107,23 @@ vector_\name: @ Prepare for SVC32 mode. IRQs remain disabled. @ mrs r0, cpsr - eor r0, r0, #(\mode ^ SVC_MODE) + eor r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE) msr spsr_cxsf, r0 @ @ the branch table must immediately follow this code @ and lr, lr, #0x0f + THUMB( adr r0, 1f ) + THUMB( ldr lr, [r0, lr, lsl #2] ) mov r0, sp - ldr lr, [pc, lr, lsl #2] + ARM( ldr lr, [pc, lr, lsl #2] ) movs pc, lr @ branch to handler in SVC mode ENDPROC(vector_\name) + + .align 2 + @ handler addresses follow this label +1: .endm .globl __stubs_start @@ -1183,14 +1261,16 @@ __stubs_end: .globl __vectors_start __vectors_start: - swi SYS_ERROR0 - b vector_und + stubs_offset - ldr pc, .LCvswi + stubs_offset - b vector_pabt + stubs_offset - b vector_dabt + stubs_offset - b vector_addrexcptn + stubs_offset - b vector_irq + stubs_offset - b vector_fiq + stubs_offset + ARM( swi SYS_ERROR0 ) + THUMB( svc #0 ) + THUMB( nop ) + W(b) vector_und + stubs_offset + W(ldr) pc, .LCvswi + stubs_offset + W(b) vector_pabt + stubs_offset + W(b) vector_dabt + stubs_offset + W(b) vector_addrexcptn + stubs_offset + W(b) vector_irq + stubs_offset + W(b) vector_fiq + stubs_offset .globl __vectors_end __vectors_end: diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index 159d0416f270..a68def8c9237 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S @@ -11,6 +11,7 @@ #include <asm/unistd.h> #include <asm/ftrace.h> #include <mach/entry-macro.S> +#include <asm/unwind.h> #include "entry-header.S" @@ -22,6 +23,8 @@ * stack. */ ret_fast_syscall: + UNWIND(.fnstart ) + UNWIND(.cantunwind ) disable_irq @ disable interrupts ldr r1, [tsk, #TI_FLAGS] tst r1, #_TIF_WORK_MASK @@ -30,14 +33,8 @@ ret_fast_syscall: /* perform architecture specific actions before user return */ arch_ret_to_user r1, lr - @ fast_restore_user_regs - ldr r1, [sp, #S_OFF + S_PSR] @ get calling cpsr - ldr lr, [sp, #S_OFF + S_PC]! @ get pc - msr spsr_cxsf, r1 @ save in spsr_svc - ldmdb sp, {r1 - lr}^ @ get calling r1 - lr - mov r0, r0 - add sp, sp, #S_FRAME_SIZE - S_PC - movs pc, lr @ return & move spsr_svc into cpsr + restore_user_regs fast = 1, offset = S_OFF + UNWIND(.fnend ) /* * Ok, we need to do extra processing, enter the slow path. @@ -69,14 +66,11 @@ no_work_pending: /* perform architecture specific actions before user return */ arch_ret_to_user r1, lr - @ slow_restore_user_regs - ldr r1, [sp, #S_PSR] @ get calling cpsr - ldr lr, [sp, #S_PC]! @ get pc - msr spsr_cxsf, r1 @ save in spsr_svc - ldmdb sp, {r0 - lr}^ @ get calling r0 - lr - mov r0, r0 - add sp, sp, #S_FRAME_SIZE - S_PC - movs pc, lr @ return & move spsr_svc into cpsr +#ifdef CONFIG_ARM_ERRATUM_451034 + dmb +#endif + + restore_user_regs fast = 0, offset = 0 ENDPROC(ret_to_user) /* @@ -176,14 +170,20 @@ ftrace_stub: .align 5 ENTRY(vector_swi) +#ifdef CONFIG_CPU_V7M + v7m_exception_entry +#else sub sp, sp, #S_FRAME_SIZE stmia sp, {r0 - r12} @ Calling r0 - r12 - add r8, sp, #S_PC - stmdb r8, {sp, lr}^ @ Calling sp, lr + ARM( add r8, sp, #S_PC ) + ARM( stmdb r8, {sp, lr}^ ) @ Calling sp, lr + THUMB( mov r8, sp ) + THUMB( store_user_sp_lr r8, r10, S_SP ) @ calling sp, lr mrs r8, spsr @ called from non-FIQ mode, so ok. str lr, [sp, #S_PC] @ Save calling PC str r8, [sp, #S_PSR] @ Save CPSR str r0, [sp, #S_OLD_R0] @ Save OLD_R0 +#endif zero_fp /* @@ -198,6 +198,7 @@ ENTRY(vector_swi) */ #ifdef CONFIG_ARM_THUMB tst r8, #PSR_T_BIT + ite ne movne r10, #0 @ no thumb OABI emulation ldreq r10, [lr, #-4] @ get SWI instruction #else @@ -206,6 +207,9 @@ ENTRY(vector_swi) A710( teq ip, #0x0f000000 ) A710( bne .Larm710bug ) #endif +#ifdef CONFIG_CPU_ENDIAN_BE8 + rev r10, r10 @ little endian instruction +#endif #elif defined(CONFIG_AEABI) @@ -253,6 +257,7 @@ ENTRY(vector_swi) * get the old ABI syscall table address. */ bics r10, r10, #0xff000000 + itt ne eorne scno, r10, #__NR_OABI_SYSCALL_BASE ldrne tbl, =sys_oabi_call_table #elif !defined(CONFIG_AEABI) @@ -265,7 +270,8 @@ ENTRY(vector_swi) bne __sys_trace cmp scno, #NR_syscalls @ check upper syscall limit - adr lr, ret_fast_syscall @ return address + adr lr, BSYM(ret_fast_syscall) @ return address + it cc ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine add r1, sp, #S_OFF @@ -286,16 +292,23 @@ __sys_trace: mov r0, #0 @ trace entry [IP = 0] bl syscall_trace - adr lr, __sys_trace_return @ return address + adr lr, BSYM(__sys_trace_return) @ return address mov scno, r0 @ syscall number (possibly new) - add r1, sp, #S_R0 + S_OFF @ pointer to regs + add r1, sp, #S_OFF @ pointer to regs cmp scno, #NR_syscalls @ check upper syscall limit + itt cc ldmccia r1, {r0 - r3} @ have to reload r0 - r3 ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine b 2b __sys_trace_return: +#ifdef CONFIG_CPU_V7M + @ S_R0 not at the beginning of struct pt_regs + add sp, #S_OFF + str r0, [sp, #S_R0] @ save returned r0 +#else str r0, [sp, #S_R0 + S_OFF]! @ save returned r0 +#endif mov r2, scno mov r1, sp mov r0, #1 @ trace exit [IP = 1] @@ -335,11 +348,14 @@ ENTRY(sys_call_table) sys_syscall: bic scno, r0, #__NR_OABI_SYSCALL_BASE cmp scno, #__NR_syscall - __NR_SYSCALL_BASE + it ne cmpne scno, #NR_syscalls @ check range + itttt lo stmloia sp, {r5, r6} @ shuffle args movlo r0, r1 movlo r1, r2 movlo r2, r3 + itt lo movlo r3, r4 ldrlo pc, [tbl, scno, lsl #2] b sys_ni_syscall @@ -393,12 +409,14 @@ ENDPROC(sys_sigaltstack_wrapper) sys_statfs64_wrapper: teq r1, #88 + it eq moveq r1, #84 b sys_statfs64 ENDPROC(sys_statfs64_wrapper) sys_fstatfs64_wrapper: teq r1, #88 + it eq moveq r1, #84 b sys_fstatfs64 ENDPROC(sys_fstatfs64_wrapper) @@ -410,6 +428,7 @@ ENDPROC(sys_fstatfs64_wrapper) sys_mmap2: #if PAGE_SHIFT > 12 tst r5, #PGOFF_MASK + ittt eq moveq r5, r5, lsr #PAGE_SHIFT - 12 streq r5, [sp, #4] beq do_mmap2 @@ -422,7 +441,9 @@ sys_mmap2: ENDPROC(sys_mmap2) ENTRY(pabort_ifar) +#ifndef CONFIG_CPU_V7M mrc p15, 0, r0, cr6, cr0, 2 +#endif ENTRY(pabort_noifar) mov pc, lr ENDPROC(pabort_ifar) diff --git a/arch/arm/kernel/entry-header.S b/arch/arm/kernel/entry-header.S index 87ab4e157997..1b0a2684a502 100644 --- a/arch/arm/kernel/entry-header.S +++ b/arch/arm/kernel/entry-header.S @@ -26,7 +26,7 @@ * The SWI code relies on the fact that R0 is at the bottom of the stack * (due to slow/fast restore user regs). */ -#if S_R0 != 0 +#if S_R0 != 0 && !defined(CONFIG_CPU_V7M) #error "Please fix" #endif @@ -36,11 +36,6 @@ #endif .endm - .macro get_thread_info, rd - mov \rd, sp, lsr #13 - mov \rd, \rd, lsl #13 - .endm - .macro alignment_trap, rtemp #ifdef CONFIG_ALIGNMENT_TRAP ldr \rtemp, .LCcralign @@ -49,6 +44,220 @@ #endif .endm +#ifdef CONFIG_CPU_V7M +/* + * ARMv7-M exception entry/exit macros. + * + * xPSR, ReturnAddress(), LR (R14), R12, R3, R2, R1, and R0 are + * automatically saved on the current stack (32 words) before + * switching to the exception stack (SP_main). The order of struct + * pt_regs members was changed to take advantage of the automatic + * state saving. + * + * If exception is taken while in user mode, SP_main is + * empty. Otherwise, SP_main is aligned to 64 bit automatically + * (CCR.STKALIGN set). + * + * Linux assumes that the interrupts are disabled when entering an + * exception handler and it may BUG if this is not the case. Interrupts + * are disabled during entry and reenabled in the exit macro. + * + * The v7m_exception_entry macro preserves the original r0-r5, r7 for + * the system call arguments. + * + * v7_exception_fast_exit is used when returning from interrupts. + * + * v7_exception_slow_exit is used when returning from SVC or PendSV. + * When returning to kernel mode, we don't return from exception. + */ + .macro v7m_exception_entry + cpsid i + cmp lr, #0xfffffffd @ check the return stack + beq 1f @ exception on process stack + add r12, sp, #32 @ MSP before exception + stmdb sp!, {r4-r12, lr} @ push unsaved registers + b 2f +1: + mrs r12, psp @ get the process stack + sub sp, #S_FRAME_SIZE + stmia sp, {r4-r12, lr} @ push unsaved registers + ldmia r12, {r0-r3, r6, r8-r10} @ load automatically saved registers + add r12, sp, #S_R0 + stmia r12, {r0-r3, r6, r8-r10} @ fill in the rest of struct pt_regs +2: + .endm + + .macro v7m_exception_fast_exit + clrex @ clear the exclusive monitor + ldmia sp!, {r4-r12, lr} @ restore previously saved state + cmp lr, #0xfffffffd @ check the return stack + it eq + addeq sp, #32 @ returning to PSP, just restore MSP + cpsie i + bx lr + .endm + + .macro v7m_exception_slow_exit ret_r0 + clrex @ clear the exclusive monitor + cpsid i + ldr lr, [sp, #S_EXC_LR] @ read exception LR + cmp lr, #0xfffffffd @ check the return stack + beq 1f @ returning to PSP + @ Prepare the MSP stack + ldmia sp, {r4-r11} @ restore previously saved state + ldr lr, [sp, #S_PC] + add sp, #S_R0 + ldmia sp, {r0-r3, r12} @ restore the rest of registers + add sp, #S_FRAME_SIZE-S_R0 @ restore the stack pointer + cpsie i + bx lr +1: + @ Prepare the PSP stack + ldr r12, [sp, #S_SP] @ read original PSP + .if \ret_r0 + add r11, sp, #S_R1 + ldmia r11, {r1-r7} @ read state saved on MSP (r0 preserved) + .else + add r11, sp, #S_R0 + ldmia r11, {r0-r7} @ read state saved on MSP + .endif + msr psp, r12 @ restore PSP + stmia r12, {r0-r7} @ restore saved state to PSP + ldmia sp, {r4-r11} @ restore previously saved state + add sp, #S_FRAME_SIZE @ restore the original MSP + cpsie i + bx lr + .endm +#endif /* CONFIG_CPU_V7M */ + + @ + @ Store/load the USER SP and LR registers by switching to the SYS + @ mode. Useful in Thumb-2 mode where "stm/ldm rd, {sp, lr}^" is not + @ available. Should only be called from SVC mode + @ + .macro store_user_sp_lr, rd, rtemp, offset = 0 + mrs \rtemp, cpsr + eor \rtemp, \rtemp, #(SVC_MODE ^ SYSTEM_MODE) + msr cpsr_c, \rtemp @ switch to the SYS mode + + str sp, [\rd, #\offset] @ save sp_usr + str lr, [\rd, #\offset + 4] @ save lr_usr + + eor \rtemp, \rtemp, #(SVC_MODE ^ SYSTEM_MODE) + msr cpsr_c, \rtemp @ switch back to the SVC mode + .endm + + .macro load_user_sp_lr, rd, rtemp, offset = 0 + mrs \rtemp, cpsr + eor \rtemp, \rtemp, #(SVC_MODE ^ SYSTEM_MODE) + msr cpsr_c, \rtemp @ switch to the SYS mode + + ldr sp, [\rd, #\offset] @ load sp_usr + ldr lr, [\rd, #\offset + 4] @ load lr_usr + + eor \rtemp, \rtemp, #(SVC_MODE ^ SYSTEM_MODE) + msr cpsr_c, \rtemp @ switch back to the SVC mode + .endm + +#ifndef CONFIG_THUMB2_KERNEL + .macro svc_exit, rpsr + msr spsr_cxsf, \rpsr +#if defined(CONFIG_CPU_32v6K) + clrex @ clear the exclusive monitor + ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr +#elif defined (CONFIG_CPU_V6) + ldr r0, [sp] + strex r1, r2, [sp] @ clear the exclusive monitor + ldmib sp, {r1 - pc}^ @ load r1 - pc, cpsr +#else + ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr +#endif + .endm + + .macro restore_user_regs, fast = 0, offset = 0 + ldr r1, [sp, #\offset + S_PSR] @ get calling cpsr + ldr lr, [sp, #\offset + S_PC]! @ get pc + msr spsr_cxsf, r1 @ save in spsr_svc +#if defined(CONFIG_CPU_32v6K) + clrex @ clear the exclusive monitor +#elif defined (CONFIG_CPU_V6) + strex r1, r2, [sp] @ clear the exclusive monitor +#endif + .if \fast + ldmdb sp, {r1 - lr}^ @ get calling r1 - lr + .else + ldmdb sp, {r0 - lr}^ @ get calling r0 - lr + .endif + add sp, sp, #S_FRAME_SIZE - S_PC + movs pc, lr @ return & move spsr_svc into cpsr + .endm + + .macro get_thread_info, rd + mov \rd, sp, lsr #13 + mov \rd, \rd, lsl #13 + .endm + + @ + @ 32-bit wide "mov pc, reg" + @ + .macro movw_pc, reg + mov pc, \reg + .endm +#else /* CONFIG_THUMB2_KERNEL */ + .macro svc_exit, rpsr + clrex @ clear the exclusive monitor + ldr r0, [sp, #S_SP] @ top of the stack + ldr r1, [sp, #S_PC] @ return address + tst r0, #4 @ orig stack 8-byte aligned? + stmdb r0, {r1, \rpsr} @ rfe context + ldmia sp, {r0 - r12} + ldr lr, [sp, #S_LR] + ite eq + addeq sp, sp, #S_FRAME_SIZE - 8 @ aligned + addne sp, sp, #S_FRAME_SIZE - 4 @ not aligned + rfeia sp! + .endm + +#ifdef CONFIG_CPU_V7M + .macro restore_user_regs, fast = 0, offset = 0 + .if \offset + add sp, #\offset + .endif + v7m_exception_slow_exit ret_r0 = \fast + .endm +#else /* !CONFIG_CPU_V7M */ + .macro restore_user_regs, fast = 0, offset = 0 + clrex @ clear the exclusive monitor + mov r2, sp + load_user_sp_lr r2, r3, \offset + S_SP @ calling sp, lr + ldr r1, [sp, #\offset + S_PSR] @ get calling cpsr + ldr lr, [sp, #\offset + S_PC] @ get pc + add sp, sp, #\offset + S_SP + msr spsr_cxsf, r1 @ save in spsr_svc + .if \fast + ldmdb sp, {r1 - r12} @ get calling r1 - r12 + .else + ldmdb sp, {r0 - r12} @ get calling r0 - r12 + .endif + add sp, sp, #S_FRAME_SIZE - S_SP + movs pc, lr @ return & move spsr_svc into cpsr + .endm +#endif /* CONFIG_CPU_V7M */ + + .macro get_thread_info, rd + mov \rd, sp + lsr \rd, \rd, #13 + mov \rd, \rd, lsl #13 + .endm + + @ + @ 32-bit wide "mov pc, reg" + @ + .macro movw_pc, reg + mov pc, \reg + nop + .endm +#endif /* CONFIG_THUMB2_KERNEL */ /* * These are the registers used in the syscall handler, and allow us to diff --git a/arch/arm/kernel/entry-v7m.S b/arch/arm/kernel/entry-v7m.S new file mode 100644 index 000000000000..ffa8be3477db --- /dev/null +++ b/arch/arm/kernel/entry-v7m.S @@ -0,0 +1,124 @@ +/* + * linux/arch/arm/kernel/entry-v7m.S + * + * Copyright (C) 2008 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Low-level vector interface routines for the ARMv7-M architecture + */ +#include <asm/memory.h> +#include <asm/glue.h> +#include <asm/thread_notify.h> + +#include <mach/entry-macro.S> + +#include "entry-header.S" + +#ifdef CONFIG_PREEMPT +#error "CONFIG_PREEMPT not supported on the current ARMv7M implementation" +#endif +#ifdef CONFIG_TRACE_IRQFLAGS +#error "CONFIG_TRACE_IRQFLAGS not supported on the current ARMv7M implementation" +#endif + +__invalid_entry: + v7m_exception_entry + mov r0, sp + bl __show_regs +1: b 1b +ENDPROC(__invalid_entry) + +__irq_entry: + v7m_exception_entry + + @ + @ Invoke the IRQ handler + @ + mrs r0, ipsr + and r0, #0xff + sub r0, #16 @ IRQ number + mov r1, sp + @ routine called with r0 = irq number, r1 = struct pt_regs * + bl asm_do_IRQ + + @ + @ Check for any pending work if returning to user + @ + ldr lr, [sp, #S_EXC_LR] + cmp lr, #0xfffffffd @ check the return stack + bne 2f @ returning to kernel + get_thread_info tsk + ldr r1, [tsk, #TI_FLAGS] + tst r1, #_TIF_WORK_MASK + beq 2f @ no work pending + ldr r1, =0xe000ed04 @ ICSR + mov r0, #1 << 28 @ ICSR.PENDSVSET + str r0, [r1] @ raise PendSV + +2: + v7m_exception_fast_exit +ENDPROC(__irq_entry) + +__pendsv_entry: + v7m_exception_entry + + ldr r1, =0xe000ed04 @ ICSR + mov r0, #1 << 27 @ ICSR.PENDSVCLR + str r0, [r1] @ clear PendSV + + @ execute the pending work, including reschedule + get_thread_info tsk + mov why, #0 + b ret_to_user +ENDPROC(__pendsv_entry) + +/* + * Register switch for ARMv7-M processors. + * r0 = previous task_struct, r1 = previous thread_info, r2 = next thread_info + * previous and next are guaranteed not to be the same. + */ +ENTRY(__switch_to) + add ip, r1, #TI_CPU_SAVE + stmia ip!, {r4 - sl, fp} @ Store most regs on stack + str sp, [ip], #4 + str lr, [ip], #4 + mov r5, r0 + add r4, r2, #TI_CPU_SAVE + ldr r0, =thread_notify_head + mov r1, #THREAD_NOTIFY_SWITCH + bl atomic_notifier_call_chain + mov ip, r4 + mov r0, r5 + ldmia ip!, {r4 - sl, fp} @ Load all regs saved previously + ldr sp, [ip], #4 + ldr pc, [ip] +ENDPROC(__switch_to) + + .data + .align 8 +/* + * Vector table (64 words => 256 bytes natural alignment) + */ +ENTRY(vector_table) + .long 0 @ 0 - Reset stack pointer + .long __invalid_entry @ 1 - Reset + .long __invalid_entry @ 2 - NMI + .long __invalid_entry @ 3 - HardFault + .long __invalid_entry @ 4 - MemManage + .long __invalid_entry @ 5 - BusFault + .long __invalid_entry @ 6 - UsageFault + .long __invalid_entry @ 7 - Reserved + .long __invalid_entry @ 8 - Reserved + .long __invalid_entry @ 9 - Reserved + .long __invalid_entry @ 10 - Reserved + .long vector_swi @ 11 - SVCall + .long __invalid_entry @ 12 - Debug Monitor + .long __invalid_entry @ 13 - Reserved + .long __pendsv_entry @ 14 - PendSV + .long __invalid_entry @ 15 - SysTick + .rept 64 - 16 + .long __irq_entry @ 16..64 - External Interrupts + .endr diff --git a/arch/arm/kernel/head-common.S b/arch/arm/kernel/head-common.S index 991952c644d1..1a4d5b4054ff 100644 --- a/arch/arm/kernel/head-common.S +++ b/arch/arm/kernel/head-common.S @@ -14,6 +14,7 @@ #define ATAG_CORE 0x54410001 #define ATAG_CORE_SIZE ((2*4 + 3*4) >> 2) + .align 2 .type __switch_data, %object __switch_data: .long __mmap_switched @@ -24,7 +25,11 @@ __switch_data: .long processor_id @ r4 .long __machine_arch_type @ r5 .long __atags_pointer @ r6 +#ifdef CONFIG_CPU_CP15 .long cr_alignment @ r7 +#else + .long 0 @ r7 +#endif .long init_thread_union + THREAD_START_SP @ sp /* @@ -41,6 +46,7 @@ __mmap_switched: ldmia r3!, {r4, r5, r6, r7} cmp r4, r5 @ Copy data segment if needed + itttt ne 1: cmpne r5, r6 ldrne fp, [r4], #4 strne fp, [r5], #4 @@ -48,10 +54,13 @@ __mmap_switched: mov fp, #0 @ Clear BSS (and zero fp) 1: cmp r6, r7 + itt cc strcc fp, [r6],#4 bcc 1b - ldmia r3, {r4, r5, r6, r7, sp} + ARM( ldmia r3, {r4, r5, r6, r7, sp}) + THUMB( ldmia r3, {r4, r5, r6, r7} ) + THUMB( ldr sp, [r3, #16] ) str r9, [r4] @ Save processor ID str r1, [r5] @ Save machine type str r2, [r6] @ Save atags pointer @@ -155,7 +164,8 @@ ENDPROC(__error) */ __lookup_processor_type: adr r3, 3f - ldmda r3, {r5 - r7} + ldmia r3, {r5 - r7} + add r3, r3, #8 sub r3, r3, r7 @ get offset between virt&phys add r5, r5, r3 @ convert virt addresses to add r6, r6, r3 @ physical address space @@ -185,9 +195,10 @@ ENDPROC(lookup_processor_type) * Look in <asm/procinfo.h> and arch/arm/kernel/arch.[ch] for * more information about the __proc_info and __arch_info structures. */ - .long __proc_info_begin + .align 2 +3: .long __proc_info_begin .long __proc_info_end -3: .long . +4: .long . .long __arch_info_begin .long __arch_info_end @@ -203,11 +214,12 @@ ENDPROC(lookup_processor_type) * r5 = mach_info pointer in physical address space */ __lookup_machine_type: - adr r3, 3b + adr r3, 4b ldmia r3, {r4, r5, r6} sub r3, r3, r4 @ get offset between virt&phys add r5, r5, r3 @ convert virt addresses to add r6, r6, r3 @ physical address space +#ifndef CONFIG_NAKED_BOOT 1: ldr r3, [r5, #MACHINFO_TYPE] @ get machine type teq r3, r1 @ matches loader number? beq 2f @ found @@ -216,6 +228,10 @@ __lookup_machine_type: blo 1b mov r5, #0 @ unknown machine 2: mov pc, lr +#else + ldr r1, [r5, #MACHINFO_TYPE] @ force our only machine type + mov pc, lr @ and we're done +#endif ENDPROC(__lookup_machine_type) /* diff --git a/arch/arm/kernel/head-nommu.S b/arch/arm/kernel/head-nommu.S index cc87e1765ed2..07a0fa7bb7bd 100644 --- a/arch/arm/kernel/head-nommu.S +++ b/arch/arm/kernel/head-nommu.S @@ -34,12 +34,15 @@ */ .section ".text.head", "ax" ENTRY(stext) - msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode + setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode @ and irqs disabled -#ifndef CONFIG_CPU_CP15 - ldr r9, =CONFIG_PROCESSOR_ID -#else +#if defined(CONFIG_CPU_CP15) mrc p15, 0, r9, c0, c0 @ get processor id +#elif defined(CONFIG_CPU_V7M) + ldr r9, =0xe000ed00 @ CPUID register address + ldr r9, [r9] +#else + ldr r9, =CONFIG_PROCESSOR_ID #endif bl __lookup_processor_type @ r5=procinfo r9=cpuid movs r10, r5 @ invalid processor (r5=0)? @@ -50,8 +53,10 @@ ENTRY(stext) ldr r13, __switch_data @ address to jump to after @ the initialization is done - adr lr, __after_proc_init @ return (PIC) address - add pc, r10, #PROCINFO_INITFUNC + adr lr, BSYM(__after_proc_init) @ return (PIC) address + ARM( add pc, r10, #PROCINFO_INITFUNC ) + THUMB( add r12, r10, #PROCINFO_INITFUNC ) + THUMB( mov pc, r12 ) ENDPROC(stext) /* @@ -59,7 +64,10 @@ ENDPROC(stext) */ __after_proc_init: #ifdef CONFIG_CPU_CP15 - mrc p15, 0, r0, c1, c0, 0 @ read control reg + /* + * CP15 system control register value returned in r0 from + * the CPU init function. + */ #ifdef CONFIG_ALIGNMENT_TRAP orr r0, r0, #CR_A #else @@ -82,7 +90,8 @@ __after_proc_init: mcr p15, 0, r0, c1, c0, 0 @ write control reg #endif /* CONFIG_CPU_CP15 */ - mov pc, r13 @ clear the BSS and jump + mov r3, r13 + mov pc, r3 @ clear the BSS and jump @ to start_kernel ENDPROC(__after_proc_init) .ltorg diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S index 21e17dc94cb5..fc9b3dee4013 100644 --- a/arch/arm/kernel/head.S +++ b/arch/arm/kernel/head.S @@ -76,7 +76,7 @@ */ .section ".text.head", "ax" ENTRY(stext) - msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode + setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode @ and irqs disabled mrc p15, 0, r9, c0, c0 @ get processor id bl __lookup_processor_type @ r5=procinfo r9=cpuid @@ -97,8 +97,10 @@ ENTRY(stext) */ ldr r13, __switch_data @ address to jump to after @ mmu has been enabled - adr lr, __enable_mmu @ return (PIC) address - add pc, r10, #PROCINFO_INITFUNC + adr lr, BSYM(__enable_mmu) @ return (PIC) address + ARM( add pc, r10, #PROCINFO_INITFUNC ) + THUMB( add r12, r10, #PROCINFO_INITFUNC ) + THUMB( mov pc, r12 ) ENDPROC(stext) #if defined(CONFIG_SMP) @@ -110,10 +112,11 @@ ENTRY(secondary_startup) * the processor type - there is no need to check the machine type * as it has already been validated by the primary processor. */ - msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE + setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 mrc p15, 0, r9, c0, c0 @ get processor id bl __lookup_processor_type movs r10, r5 @ invalid processor? + it eq moveq r0, #'p' @ yes, error 'p' beq __error @@ -121,12 +124,15 @@ ENTRY(secondary_startup) * Use the page tables supplied from __cpu_up. */ adr r4, __secondary_data - ldmia r4, {r5, r7, r13} @ address to jump to after + ldmia r4, {r5, r7, r12} @ address to jump to after sub r4, r4, r5 @ mmu has been enabled ldr r4, [r7, r4] @ get secondary_data.pgdir - adr lr, __enable_mmu @ return address - add pc, r10, #PROCINFO_INITFUNC @ initialise processor - @ (return control reg) + adr lr, BSYM(__enable_mmu) @ return address + mov r13, r12 @ __secondary_switched address + ARM( add pc, r10, #PROCINFO_INITFUNC ) @ initialise processor + @ (return control reg) + THUMB( add r12, r10, #PROCINFO_INITFUNC ) + THUMB( mov pc, r12 ) ENDPROC(secondary_startup) /* @@ -193,8 +199,8 @@ __turn_mmu_on: mcr p15, 0, r0, c1, c0, 0 @ write control reg mrc p15, 0, r3, c0, c0, 0 @ read id reg mov r3, r3 - mov r3, r3 - mov pc, r13 + mov r3, r13 + mov pc, r3 ENDPROC(__turn_mmu_on) @@ -235,7 +241,8 @@ __create_page_tables: * will be removed by paging_init(). We use our current program * counter to determine corresponding section base address. */ - mov r6, pc, lsr #20 @ start of kernel section + mov r6, pc + mov r6, r6, lsr #20 @ start of kernel section orr r3, r7, r6, lsl #20 @ flags + kernel base str r3, [r4, r6, lsl #2] @ identity mapping @@ -250,6 +257,7 @@ __create_page_tables: add r6, r4, r6, lsr #18 1: cmp r0, r6 add r3, r3, #1 << 20 + it ls strls r3, [r0], #4 bls 1b @@ -293,6 +301,7 @@ __create_page_tables: add r0, r4, r3 rsb r3, r3, #0x4000 @ PTRS_PER_PGD*sizeof(long) cmp r3, #0x0800 @ limit to 512MB + it hi movhi r3, #0x0800 add r6, r0, r3 ldr r3, [r8, #MACHINFO_PHYSIO] diff --git a/arch/arm/kernel/module.c b/arch/arm/kernel/module.c index 2ecffb8dcf50..59aaad65824a 100644 --- a/arch/arm/kernel/module.c +++ b/arch/arm/kernel/module.c @@ -22,6 +22,7 @@ #include <asm/pgtable.h> #include <asm/sections.h> +#include <asm/unwind.h> #ifdef CONFIG_XIP_KERNEL /* @@ -66,6 +67,24 @@ int module_frob_arch_sections(Elf_Ehdr *hdr, char *secstrings, struct module *mod) { +#ifdef CONFIG_ARM_UNWIND + Elf_Shdr *s, *sechdrs_end = sechdrs + hdr->e_shnum; + + for (s = sechdrs; s < sechdrs_end; s++) { + if (strcmp(".ARM.exidx.init.text", secstrings + s->sh_name) == 0) + mod->arch.unw_sec_init = s; + else if (strcmp(".ARM.exidx.devinit.text", secstrings + s->sh_name) == 0) + mod->arch.unw_sec_devinit = s; + else if (strcmp(".ARM.exidx", secstrings + s->sh_name) == 0) + mod->arch.unw_sec_core = s; + else if (strcmp(".init.text", secstrings + s->sh_name) == 0) + mod->arch.sec_init_text = s; + else if (strcmp(".devinit.text", secstrings + s->sh_name) == 0) + mod->arch.sec_devinit_text = s; + else if (strcmp(".text", secstrings + s->sh_name) == 0) + mod->arch.sec_core_text = s; + } +#endif return 0; } @@ -83,6 +102,7 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex, unsigned long loc; Elf32_Sym *sym; s32 offset; + u32 upper, lower, sign, j1, j2; offset = ELF32_R_SYM(rel->r_info); if (offset < 0 || offset > (symsec->sh_size / sizeof(Elf32_Sym))) { @@ -104,6 +124,10 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex, loc = dstsec->sh_addr + rel->r_offset; switch (ELF32_R_TYPE(rel->r_info)) { + case R_ARM_NONE: + /* ignore */ + break; + case R_ARM_ABS32: *(u32 *)loc += sym->st_value; break; @@ -145,6 +169,61 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex, *(u32 *)loc &= 0xfff0f000; *(u32 *)loc |= ((offset & 0xf000) << 4) | (offset & 0x0fff); + case R_ARM_PREL31: + offset = *(u32 *)loc + sym->st_value - loc; + *(u32 *)loc = offset & 0x7fffffff; + break; + + case R_ARM_THM_CALL: + case R_ARM_THM_JUMP24: + upper = *(u16 *)loc; + lower = *(u16 *)(loc + 2); + + /* + * 25 bit signed address range (Thumb-2 BL and B.W + * instructions): + * S:I1:I2:imm10:imm11:0 + * where: + * S = upper[10] = offset[24] + * I1 = ~(J1 ^ S) = offset[23] + * I2 = ~(J2 ^ S) = offset[22] + * imm10 = upper[9:0] = offset[21:12] + * imm11 = lower[10:0] = offset[11:1] + * J1 = lower[13] + * J2 = lower[11] + */ + sign = (upper >> 10) & 1; + j1 = (lower >> 13) & 1; + j2 = (lower >> 11) & 1; + offset = (sign << 24) | ((~(j1 ^ sign) & 1) << 23) | + ((~(j2 ^ sign) & 1) << 22) | + ((upper & 0x03ff) << 12) | + ((lower & 0x07ff) << 1); + if (offset & 0x01000000) + offset -= 0x02000000; + offset += sym->st_value - loc; + + /* only Thumb addresses allowed (no interworking) */ + if (!(offset & 1) || + offset <= (s32)0xff000000 || + offset >= (s32)0x01000000) { + printk(KERN_ERR + "%s: relocation out of range, section " + "%d reloc %d sym '%s'\n", module->name, + relindex, i, strtab + sym->st_name); + return -ENOEXEC; + } + + sign = (offset >> 24) & 1; + j1 = sign ^ (~(offset >> 23) & 1); + j2 = sign ^ (~(offset >> 22) & 1); + *(u16 *)loc = (u16)((upper & 0xf800) | (sign << 10) | + ((offset >> 12) & 0x03ff)); + *(u16 *)(loc + 2) = (u16)((lower & 0xd000) | + (j1 << 13) | (j2 << 11) | + ((offset >> 1) & 0x07ff)); + upper = *(u16 *)loc; + lower = *(u16 *)(loc + 2); break; default: @@ -165,14 +244,50 @@ apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab, return -ENOEXEC; } +#ifdef CONFIG_ARM_UNWIND +static void register_unwind_tables(struct module *mod) +{ + if (mod->arch.unw_sec_init && mod->arch.sec_init_text) + mod->arch.unwind_init = + unwind_table_add(mod->arch.unw_sec_init->sh_addr, + mod->arch.unw_sec_init->sh_size, + mod->arch.sec_init_text->sh_addr, + mod->arch.sec_init_text->sh_size); + if (mod->arch.unw_sec_devinit && mod->arch.sec_devinit_text) + mod->arch.unwind_devinit = + unwind_table_add(mod->arch.unw_sec_devinit->sh_addr, + mod->arch.unw_sec_devinit->sh_size, + mod->arch.sec_devinit_text->sh_addr, + mod->arch.sec_devinit_text->sh_size); + if (mod->arch.unw_sec_core && mod->arch.sec_core_text) + mod->arch.unwind_core = + unwind_table_add(mod->arch.unw_sec_core->sh_addr, + mod->arch.unw_sec_core->sh_size, + mod->arch.sec_core_text->sh_addr, + mod->arch.sec_core_text->sh_size); +} + +static void unregister_unwind_tables(struct module *mod) +{ + unwind_table_del(mod->arch.unwind_init); + unwind_table_del(mod->arch.unwind_devinit); + unwind_table_del(mod->arch.unwind_core); +} +#else +static inline void register_unwind_tables(struct module *mod) { } +static inline void unregister_unwind_tables(struct module *mod) { } +#endif + int module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs, struct module *module) { + register_unwind_tables(module); return 0; } void module_arch_cleanup(struct module *mod) { + unregister_unwind_tables(mod); } diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index f884b85030b7..c06f0481163b 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -34,6 +34,7 @@ #include <asm/processor.h> #include <asm/system.h> #include <asm/thread_notify.h> +#include <asm/stacktrace.h> #include <asm/mach/time.h> static const char *processor_modes[] = { @@ -434,6 +435,23 @@ asm( ".section .text\n" " .size kernel_thread_helper, . - kernel_thread_helper\n" " .previous"); +#ifdef CONFIG_ARM_UNWIND +extern void kernel_thread_exit(long code); +asm( ".section .text\n" +" .align\n" +" .type kernel_thread_exit, #function\n" +"kernel_thread_exit:\n" +" .fnstart\n" +" .cantunwind\n" +" bl do_exit\n" +" nop\n" +" .fnend\n" +" .size kernel_thread_exit, . - kernel_thread_exit\n" +" .previous"); +#else +#define kernel_thread_exit do_exit +#endif + /* * Create a kernel thread. */ @@ -445,9 +463,13 @@ pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) regs.ARM_r1 = (unsigned long)arg; regs.ARM_r2 = (unsigned long)fn; - regs.ARM_r3 = (unsigned long)do_exit; + regs.ARM_r3 = (unsigned long)kernel_thread_exit; regs.ARM_pc = (unsigned long)kernel_thread_helper; - regs.ARM_cpsr = SVC_MODE; + regs.ARM_cpsr = SVC_MODE | PSR_ENDSTATE | PSR_ISETSTATE; +#ifdef CONFIG_CPU_V7M + /* Return to Handler mode */ + regs.ARM_EXC_lr = 0xfffffff1L; +#endif return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); } @@ -455,23 +477,21 @@ EXPORT_SYMBOL(kernel_thread); unsigned long get_wchan(struct task_struct *p) { - unsigned long fp, lr; - unsigned long stack_start, stack_end; + struct stackframe frame; int count = 0; if (!p || p == current || p->state == TASK_RUNNING) return 0; - stack_start = (unsigned long)end_of_stack(p); - stack_end = (unsigned long)task_stack_page(p) + THREAD_SIZE; - - fp = thread_saved_fp(p); + frame.fp = thread_saved_fp(p); + frame.sp = thread_saved_sp(p); + frame.lr = 0; /* recovered from the stack */ + frame.pc = thread_saved_pc(p); do { - if (fp < stack_start || fp > stack_end) + int ret = unwind_frame(&frame); + if (ret < 0) return 0; - lr = ((unsigned long *)fp)[-1]; - if (!in_sched_functions(lr)) - return lr; - fp = *(unsigned long *) (fp - 12); + if (!in_sched_functions(frame.pc)) + return frame.pc; } while (count ++ < 16); return 0; } diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index 89882a1d0187..a2ea3854cb3c 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c @@ -521,7 +521,13 @@ static int ptrace_read_user(struct task_struct *tsk, unsigned long off, return -EIO; tmp = 0; - if (off < sizeof(struct pt_regs)) + if (off == PT_TEXT_ADDR) + tmp = tsk->mm->start_code; + else if (off == PT_DATA_ADDR) + tmp = tsk->mm->start_data; + else if (off == PT_TEXT_END_ADDR) + tmp = tsk->mm->end_code; + else if (off < sizeof(struct pt_regs)) tmp = get_user_reg(tsk, off >> 2); return put_user(tmp, ret); diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index d301a5385428..4d9b74a355a8 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -25,6 +25,7 @@ #include <linux/smp.h> #include <linux/fs.h> +#include <asm/unified.h> #include <asm/cpu.h> #include <asm/cputype.h> #include <asm/elf.h> @@ -40,6 +41,7 @@ #include <asm/mach/irq.h> #include <asm/mach/time.h> #include <asm/traps.h> +#include <asm/unwind.h> #include "compat.h" #include "atags.h" @@ -107,7 +109,9 @@ struct stack { u32 und[3]; } ____cacheline_aligned; +#ifndef CONFIG_CPU_V7M static struct stack stacks[NR_CPUS]; +#endif char elf_platform[ELF_PLATFORM_SIZE]; EXPORT_SYMBOL(elf_platform); @@ -195,6 +199,12 @@ static const char *proc_arch[] = { "?(17)", }; +#ifdef CONFIG_CPU_V7M +int cpu_architecture(void) +{ + return CPU_ARCH_ARMv7M; +} +#else int cpu_architecture(void) { int cpu_arch; @@ -214,7 +224,7 @@ int cpu_architecture(void) * Register 0 and check for VMSAv7 or PMSAv7 */ asm("mrc p15, 0, %0, c0, c1, 4" : "=r" (mmfr0)); - if ((mmfr0 & 0x0000000f) == 0x00000003 || + if ((mmfr0 & 0x0000000f) >= 0x00000003 || (mmfr0 & 0x000000f0) == 0x00000030) cpu_arch = CPU_ARCH_ARMv7; else if ((mmfr0 & 0x0000000f) == 0x00000002 || @@ -227,6 +237,8 @@ int cpu_architecture(void) return cpu_arch; } +#endif +EXPORT_SYMBOL(cpu_architecture); static void __init cacheid_init(void) { @@ -295,9 +307,15 @@ static void __init setup_processor(void) cpu_cache = *list->cache; #endif +#ifdef CONFIG_CPU_V7M + printk("CPU: %s [%08x] revision %d (ARMv%sM)\n", + cpu_name, processor_id, (int)processor_id & 15, + proc_arch[cpu_architecture()]); +#else printk("CPU: %s [%08x] revision %d (ARMv%s), cr=%08lx\n", cpu_name, read_cpuid_id(), read_cpuid_id() & 15, proc_arch[cpu_architecture()], cr_alignment); +#endif sprintf(init_utsname()->machine, "%s%c", list->arch_name, ENDIANNESS); sprintf(elf_platform, "%s%c", list->elf_name, ENDIANNESS); @@ -317,6 +335,7 @@ static void __init setup_processor(void) */ void cpu_init(void) { +#ifndef CONFIG_CPU_V7M unsigned int cpu = smp_processor_id(); struct stack *stk = &stacks[cpu]; @@ -326,26 +345,40 @@ void cpu_init(void) } /* + * Define the placement constraint for the inline asm directive below. + * In Thumb-2, msr with an immediate value is not allowed. + */ +#ifdef CONFIG_THUMB2_KERNEL +#define PLC "r" +#else +#define PLC "I" +#endif + + /* * setup stacks for re-entrant exception handlers */ __asm__ ( "msr cpsr_c, %1\n\t" - "add sp, %0, %2\n\t" + "add r14, %0, %2\n\t" + "mov sp, r14\n\t" "msr cpsr_c, %3\n\t" - "add sp, %0, %4\n\t" + "add r14, %0, %4\n\t" + "mov sp, r14\n\t" "msr cpsr_c, %5\n\t" - "add sp, %0, %6\n\t" + "add r14, %0, %6\n\t" + "mov sp, r14\n\t" "msr cpsr_c, %7" : : "r" (stk), - "I" (PSR_F_BIT | PSR_I_BIT | IRQ_MODE), + PLC (PSR_F_BIT | PSR_I_BIT | IRQ_MODE), "I" (offsetof(struct stack, irq[0])), - "I" (PSR_F_BIT | PSR_I_BIT | ABT_MODE), + PLC (PSR_F_BIT | PSR_I_BIT | ABT_MODE), "I" (offsetof(struct stack, abt[0])), - "I" (PSR_F_BIT | PSR_I_BIT | UND_MODE), + PLC (PSR_F_BIT | PSR_I_BIT | UND_MODE), "I" (offsetof(struct stack, und[0])), - "I" (PSR_F_BIT | PSR_I_BIT | SVC_MODE) + PLC (PSR_F_BIT | PSR_I_BIT | SVC_MODE) : "r14"); +#endif } static struct machine_desc * __init setup_machine(unsigned int nr) @@ -685,6 +718,8 @@ void __init setup_arch(char **cmdline_p) struct machine_desc *mdesc; char *from = default_command_line; + unwind_init(); + setup_processor(); mdesc = setup_machine(machine_arch_type); machine_name = mdesc->name; @@ -692,10 +727,12 @@ void __init setup_arch(char **cmdline_p) if (mdesc->soft_reboot) reboot_setup("s"); +#ifndef CONFIG_NAKED_BOOT if (__atags_pointer) tags = phys_to_virt(__atags_pointer); else if (mdesc->boot_params) tags = phys_to_virt(mdesc->boot_params); +#endif /* * If we have the old style parameters, convert them to diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 8f96922e2827..ceb130e0e9c2 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -426,9 +426,13 @@ setup_return(struct pt_regs *regs, struct k_sigaction *ka, */ thumb = handler & 1; - if (thumb) + if (thumb) { cpsr |= PSR_T_BIT; - else +#if __LINUX_ARM_ARCH__ >= 7 + /* clear the If-Then Thumb-2 execution state */ + cpsr &= ~PSR_IT_MASK; +#endif + } else cpsr &= ~PSR_T_BIT; } #endif @@ -542,7 +546,9 @@ static inline void restart_syscall(struct pt_regs *regs) regs->ARM_r0 = -EINTR; return; } +#ifndef CONFIG_CPU_V7M regs->ARM_r0 = regs->ARM_ORIG_r0; +#endif regs->ARM_pc -= thumb_mode(regs) ? 2 : 4; } diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 55fa7ff96a3e..fb671ac0e816 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -60,6 +60,9 @@ enum ipi_msg_type { IPI_CALL_FUNC, IPI_CALL_FUNC_SINGLE, IPI_CPU_STOP, +#ifdef CONFIG_CPU_NO_CACHE_BCAST + IPI_DMA_CACHE, +#endif }; int __cpuinit __cpu_up(unsigned int cpu) @@ -93,6 +96,7 @@ int __cpuinit __cpu_up(unsigned int cpu) pmd = pmd_offset(pgd + pgd_index(PHYS_OFFSET), PHYS_OFFSET); *pmd = __pmd((PHYS_OFFSET & PGDIR_MASK) | PMD_TYPE_SECT | PMD_SECT_AP_WRITE); + flush_pmd_entry(pmd); /* * We need to tell the secondary core where to find @@ -130,6 +134,7 @@ int __cpuinit __cpu_up(unsigned int cpu) secondary_data.pgdir = 0; *pmd = __pmd(0); + clean_pmd_entry(pmd); pgd_free(&init_mm, pgd); if (ret) { @@ -424,6 +429,10 @@ static void ipi_cpu_stop(unsigned int cpu) cpu_relax(); } +#ifdef CONFIG_CPU_NO_CACHE_BCAST +static void ipi_dma_cache_op(unsigned int cpu); +#endif + /* * Main handler for inter-processor interrupts * @@ -483,6 +492,12 @@ asmlinkage void __exception do_IPI(struct pt_regs *regs) ipi_cpu_stop(cpu); break; +#ifdef CONFIG_CPU_NO_CACHE_BCAST + case IPI_DMA_CACHE: + ipi_dma_cache_op(cpu); + break; +#endif + default: printk(KERN_CRIT "CPU%u: Unknown IPI message 0x%x\n", cpu, nextmsg); @@ -593,19 +608,19 @@ static inline void ipi_flush_tlb_kernel_range(void *arg) local_flush_tlb_kernel_range(ta->ta_start, ta->ta_end); } -void flush_tlb_all(void) +void smp_flush_tlb_all(void) { on_each_cpu(ipi_flush_tlb_all, NULL, 1); } -void flush_tlb_mm(struct mm_struct *mm) +void smp_flush_tlb_mm(struct mm_struct *mm) { cpumask_t mask = mm->cpu_vm_mask; on_each_cpu_mask(ipi_flush_tlb_mm, mm, 1, mask); } -void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr) +void smp_flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr) { cpumask_t mask = vma->vm_mm->cpu_vm_mask; struct tlb_args ta; @@ -616,7 +631,7 @@ void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr) on_each_cpu_mask(ipi_flush_tlb_page, &ta, 1, mask); } -void flush_tlb_kernel_page(unsigned long kaddr) +void smp_flush_tlb_kernel_page(unsigned long kaddr) { struct tlb_args ta; @@ -625,7 +640,7 @@ void flush_tlb_kernel_page(unsigned long kaddr) on_each_cpu(ipi_flush_tlb_kernel_page, &ta, 1); } -void flush_tlb_range(struct vm_area_struct *vma, +void smp_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end) { cpumask_t mask = vma->vm_mm->cpu_vm_mask; @@ -638,7 +653,7 @@ void flush_tlb_range(struct vm_area_struct *vma, on_each_cpu_mask(ipi_flush_tlb_range, &ta, 1, mask); } -void flush_tlb_kernel_range(unsigned long start, unsigned long end) +void smp_flush_tlb_kernel_range(unsigned long start, unsigned long end) { struct tlb_args ta; @@ -647,3 +662,123 @@ void flush_tlb_kernel_range(unsigned long start, unsigned long end) on_each_cpu(ipi_flush_tlb_kernel_range, &ta, 1); } + +#ifdef CONFIG_CPU_NO_CACHE_BCAST +/* + * DMA cache maintenance operations on SMP if the automatic hardware + * broadcasting is not available + */ +struct smp_dma_cache_struct { + int type; + const void *start; + const void *end; + cpumask_t unfinished; +}; + +static struct smp_dma_cache_struct *smp_dma_cache_data; +static DEFINE_RWLOCK(smp_dma_cache_data_lock); +static DEFINE_SPINLOCK(smp_dma_cache_lock); + +static void local_dma_cache_op(int type, const void *start, const void *end) +{ + switch (type) { + case SMP_DMA_CACHE_INV: + dmac_inv_range(start, end); + break; + case SMP_DMA_CACHE_CLEAN: + dmac_clean_range(start, end); + break; + case SMP_DMA_CACHE_FLUSH: + dmac_flush_range(start, end); + break; + default: + printk(KERN_CRIT "CPU%u: Unknown SMP DMA cache type %d\n", + smp_processor_id(), type); + } +} + +/* + * This function must be executed with interrupts disabled. + */ +static void ipi_dma_cache_op(unsigned int cpu) +{ + read_lock(&smp_dma_cache_data_lock); + + /* check for spurious IPI */ + if ((smp_dma_cache_data == NULL) || + (!cpu_isset(cpu, smp_dma_cache_data->unfinished))) + goto out; + local_dma_cache_op(smp_dma_cache_data->type, + smp_dma_cache_data->start, smp_dma_cache_data->end); + cpu_clear(cpu, smp_dma_cache_data->unfinished); + out: + read_unlock(&smp_dma_cache_data_lock); +} + +/* + * Execute the DMA cache operations on all online CPUs. This function + * can be called with interrupts disabled or from interrupt context. + */ +static void __smp_dma_cache_op(int type, const void *start, const void *end) +{ + struct smp_dma_cache_struct data; + cpumask_t callmap = cpu_online_map; + unsigned int cpu = get_cpu(); + unsigned long flags; + + cpu_clear(cpu, callmap); + data.type = type; + data.start = start; + data.end = end; + data.unfinished = callmap; + + /* + * If the spinlock cannot be acquired, other CPU is trying to + * send an IPI. If the interrupts are disabled, we have to + * poll for an incoming IPI. + */ + while (!spin_trylock_irqsave(&smp_dma_cache_lock, flags)) { + if (irqs_disabled()) + ipi_dma_cache_op(cpu); + } + + write_lock(&smp_dma_cache_data_lock); + smp_dma_cache_data = &data; + write_unlock(&smp_dma_cache_data_lock); + + if (!cpus_empty(callmap)) + send_ipi_message(callmap, IPI_DMA_CACHE); + /* run the local operation in parallel with the other CPUs */ + local_dma_cache_op(type, start, end); + + while (!cpus_empty(data.unfinished)) + barrier(); + + write_lock(&smp_dma_cache_data_lock); + smp_dma_cache_data = NULL; + write_unlock(&smp_dma_cache_data_lock); + + spin_unlock_irqrestore(&smp_dma_cache_lock, flags); + put_cpu(); +} + +#define DMA_MAX_RANGE SZ_4K + +/* + * Split the cache range in smaller pieces if interrupts are enabled + * to reduce the latency caused by disabling the interrupts during the + * broadcast. + */ +void smp_dma_cache_op(int type, const void *start, const void *end) +{ + if (irqs_disabled() || (end - start <= DMA_MAX_RANGE)) + __smp_dma_cache_op(type, start, end); + else { + const void *ptr; + for (ptr = start; ptr < end - DMA_MAX_RANGE; + ptr += DMA_MAX_RANGE) + __smp_dma_cache_op(type, ptr, ptr + DMA_MAX_RANGE); + __smp_dma_cache_op(type, ptr, end); + } +} +#endif diff --git a/arch/arm/kernel/stacktrace.c b/arch/arm/kernel/stacktrace.c index fc650f64df43..b371c54e548a 100644 --- a/arch/arm/kernel/stacktrace.c +++ b/arch/arm/kernel/stacktrace.c @@ -2,35 +2,60 @@ #include <linux/sched.h> #include <linux/stacktrace.h> -#include "stacktrace.h" - -int walk_stackframe(unsigned long fp, unsigned long low, unsigned long high, - int (*fn)(struct stackframe *, void *), void *data) +#include <asm/stacktrace.h> + +#if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND) +/* + * Unwind the current stack frame and store the new register values in the + * structure passed as argument. Unwinding is equivalent to a function return, + * hence the new PC value rather than LR should be used for backtrace. + * + * With framepointer enabled, a simple function prologue looks like this: + * mov ip, sp + * stmdb sp!, {fp, ip, lr, pc} + * sub fp, ip, #4 + * + * A simple function epilogue looks like this: + * ldm sp, {fp, sp, pc} + * + * Note that with framepointer enabled, even the leaf functions have the same + * prologue and epilogue, therefore we can ignore the LR value in this case. + */ +int unwind_frame(struct stackframe *frame) { - struct stackframe *frame; + unsigned long high, low; + unsigned long fp = frame->fp; - do { - /* - * Check current frame pointer is within bounds - */ - if (fp < (low + 12) || fp + 4 >= high) - break; + /* only go to a higher address on the stack */ + low = frame->sp; + high = ALIGN(low, THREAD_SIZE) + THREAD_SIZE; - frame = (struct stackframe *)(fp - 12); + /* check current frame pointer is within bounds */ + if (fp < (low + 12) || fp + 4 >= high) + return -EINVAL; - if (fn(frame, data)) - break; - - /* - * Update the low bound - the next frame must always - * be at a higher address than the current frame. - */ - low = fp + 4; - fp = frame->fp; - } while (fp); + /* restore the registers from the stack frame */ + frame->fp = *(unsigned long *)(fp - 12); + frame->sp = *(unsigned long *)(fp - 8); + frame->pc = *(unsigned long *)(fp - 4); return 0; } +#endif + +void walk_stackframe(struct stackframe *frame, + int (*fn)(struct stackframe *, void *), void *data) +{ + while (1) { + int ret; + + if (fn(frame, data)) + break; + ret = unwind_frame(frame); + if (ret < 0) + break; + } +} EXPORT_SYMBOL(walk_stackframe); #ifdef CONFIG_STACKTRACE @@ -44,7 +69,7 @@ static int save_trace(struct stackframe *frame, void *d) { struct stack_trace_data *data = d; struct stack_trace *trace = data->trace; - unsigned long addr = frame->lr; + unsigned long addr = frame->pc; if (data->no_sched_functions && in_sched_functions(addr)) return 0; @@ -61,11 +86,10 @@ static int save_trace(struct stackframe *frame, void *d) void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) { struct stack_trace_data data; - unsigned long fp, base; + struct stackframe frame; data.trace = trace; data.skip = trace->skip; - base = (unsigned long)task_stack_page(tsk); if (tsk != current) { #ifdef CONFIG_SMP @@ -76,14 +100,22 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) BUG(); #else data.no_sched_functions = 1; - fp = thread_saved_fp(tsk); + frame.fp = thread_saved_fp(tsk); + frame.sp = thread_saved_sp(tsk); + frame.lr = 0; /* recovered from the stack */ + frame.pc = thread_saved_pc(tsk); #endif } else { + register unsigned long current_sp asm ("sp"); + data.no_sched_functions = 0; - asm("mov %0, fp" : "=r" (fp)); + frame.fp = (unsigned long)__builtin_frame_address(0); + frame.sp = current_sp; + frame.lr = (unsigned long)__builtin_return_address(0); + frame.pc = (unsigned long)save_stack_trace_tsk; } - walk_stackframe(fp, base, base + THREAD_SIZE, save_trace, &data); + walk_stackframe(&frame, save_trace, &data); if (trace->nr_entries < trace->max_entries) trace->entries[trace->nr_entries++] = ULONG_MAX; } diff --git a/arch/arm/kernel/stacktrace.h b/arch/arm/kernel/stacktrace.h deleted file mode 100644 index e9fd20cb5662..000000000000 --- a/arch/arm/kernel/stacktrace.h +++ /dev/null @@ -1,9 +0,0 @@ -struct stackframe { - unsigned long fp; - unsigned long sp; - unsigned long lr; - unsigned long pc; -}; - -int walk_stackframe(unsigned long fp, unsigned long low, unsigned long high, - int (*fn)(struct stackframe *, void *), void *data); diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c index c68b44aa88d2..4cdc4a0bd02d 100644 --- a/arch/arm/kernel/time.c +++ b/arch/arm/kernel/time.c @@ -33,6 +33,7 @@ #include <asm/leds.h> #include <asm/thread_info.h> +#include <asm/stacktrace.h> #include <asm/mach/time.h> /* @@ -55,14 +56,22 @@ EXPORT_SYMBOL(rtc_lock); #ifdef CONFIG_SMP unsigned long profile_pc(struct pt_regs *regs) { - unsigned long fp, pc = instruction_pointer(regs); + struct stackframe frame; - if (in_lock_functions(pc)) { - fp = regs->ARM_fp; - pc = ((unsigned long *)fp)[-1]; - } + if (!in_lock_functions(regs->ARM_pc)) + return regs->ARM_pc; + + frame.fp = regs->ARM_fp; + frame.sp = regs->ARM_sp; + frame.lr = regs->ARM_lr; + frame.pc = regs->ARM_pc; + do { + int ret = unwind_frame(&frame); + if (ret < 0) + return 0; + } while (in_lock_functions(frame.pc)); - return pc; + return frame.pc; } EXPORT_SYMBOL(profile_pc); #endif diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 79abc4ddc0cf..6977342aa139 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -27,6 +27,7 @@ #include <asm/system.h> #include <asm/unistd.h> #include <asm/traps.h> +#include <asm/unwind.h> #include "ptrace.h" #include "signal.h" @@ -61,6 +62,7 @@ void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long dump_mem("Exception stack", frame + 4, frame + 4 + sizeof(struct pt_regs)); } +#ifndef CONFIG_ARM_UNWIND /* * Stack pointers should always be within the kernels view of * physical memory. If it is not there, then we can't dump @@ -74,6 +76,7 @@ static int verify_stack(unsigned long sp) return 0; } +#endif /* * Dump out the contents of some memory nicely... @@ -150,13 +153,33 @@ static void dump_instr(struct pt_regs *regs) set_fs(fs); } +#ifdef CONFIG_ARM_UNWIND +static inline void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) +{ + unwind_backtrace(regs, tsk); +} +#else static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) { - unsigned int fp; + unsigned int fp, mode; int ok = 1; printk("Backtrace: "); - fp = regs->ARM_fp; + + if (!tsk) + tsk = current; + + if (regs) { + fp = regs->ARM_fp; + mode = processor_mode(regs); + } else if (tsk != current) { + fp = thread_saved_fp(tsk); + mode = 0x10; + } else { + asm("mov %0, fp" : "=r" (fp) : : "cc"); + mode = 0x10; + } + if (!fp) { printk("no frame pointer"); ok = 0; @@ -168,29 +191,20 @@ static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) printk("\n"); if (ok) - c_backtrace(fp, processor_mode(regs)); + c_backtrace(fp, mode); } +#endif void dump_stack(void) { - __backtrace(); + dump_backtrace(NULL, NULL); } EXPORT_SYMBOL(dump_stack); void show_stack(struct task_struct *tsk, unsigned long *sp) { - unsigned long fp; - - if (!tsk) - tsk = current; - - if (tsk != current) - fp = thread_saved_fp(tsk); - else - asm("mov %0, fp" : "=r" (fp) : : "cc"); - - c_backtrace(fp, 0x10); + dump_backtrace(NULL, tsk); barrier(); } @@ -625,6 +639,14 @@ void __bad_xchg(volatile void *ptr, int size) } EXPORT_SYMBOL(__bad_xchg); +void __bad_cmpxchg(volatile void *ptr, int size) +{ + printk("cmpxchg: bad data size: pc 0x%p, ptr 0x%p, size %d\n", + __builtin_return_address(0), ptr, size); + BUG(); +} +EXPORT_SYMBOL(__bad_cmpxchg); + /* * A data abort trap was taken, but we did not handle the instruction. * Try to abort the user program, or panic if it was the kernel. @@ -707,6 +729,7 @@ void __init trap_init(void) void __init early_trap_init(void) { +#ifndef CONFIG_CPU_V7M unsigned long vectors = CONFIG_VECTORS_BASE; extern char __stubs_start[], __stubs_end[]; extern char __vectors_start[], __vectors_end[]; @@ -731,4 +754,5 @@ void __init early_trap_init(void) flush_icache_range(vectors, vectors + PAGE_SIZE); modify_domain(DOMAIN_USER, DOMAIN_CLIENT); +#endif } diff --git a/arch/arm/kernel/unwind.c b/arch/arm/kernel/unwind.c new file mode 100644 index 000000000000..39baf1128bfa --- /dev/null +++ b/arch/arm/kernel/unwind.c @@ -0,0 +1,437 @@ +/* + * arch/arm/kernel/unwind.c + * + * Copyright (C) 2008 ARM Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 + * + * + * Stack unwinding support for ARM + * + * An ARM EABI version of gcc is required to generate the unwind + * tables. For information about the structure of the unwind tables, + * see "Exception Handling ABI for the ARM Architecture" at: + * + * http://infocenter.arm.com/help/topic/com.arm.doc.subset.swdev.abi/index.html + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/list.h> + +#include <asm/stacktrace.h> +#include <asm/traps.h> +#include <asm/unwind.h> + +/* Dummy functions to avoid linker complaints */ +void __aeabi_unwind_cpp_pr0(void) +{ +}; +EXPORT_SYMBOL(__aeabi_unwind_cpp_pr0); + +void __aeabi_unwind_cpp_pr1(void) +{ +}; +EXPORT_SYMBOL(__aeabi_unwind_cpp_pr1); + +void __aeabi_unwind_cpp_pr2(void) +{ +}; +EXPORT_SYMBOL(__aeabi_unwind_cpp_pr2); + +struct unwind_ctrl_block { + unsigned long vrs[16]; /* virtual register set */ + unsigned long *insn; /* pointer to the current instructions word */ + int entries; /* number of entries left to interpret */ + int byte; /* current byte number in the instructions word */ +}; + +enum regs { +#ifdef CONFIG_THUMB2_KERNEL + FP = 7, +#else + FP = 11, +#endif + SP = 13, + LR = 14, + PC = 15 +}; + +extern struct unwind_idx __start_unwind_idx[]; +extern struct unwind_idx __stop_unwind_idx[]; + +static DEFINE_SPINLOCK(unwind_lock); +static LIST_HEAD(unwind_tables); + +/* Convert a prel31 symbol to an absolute address */ +#define prel31_to_addr(ptr) \ +({ \ + /* sign-extend to 32 bits */ \ + long offset = (((long)*(ptr)) << 1) >> 1; \ + (unsigned long)(ptr) + offset; \ +}) + +/* + * Binary search in the unwind index. The entries entries are + * guaranteed to be sorted in ascending order by the linker. + */ +static struct unwind_idx *search_index(unsigned long addr, + struct unwind_idx *first, + struct unwind_idx *last) +{ + pr_debug("%s(%08lx, %p, %p)\n", __func__, addr, first, last); + + if (addr < first->addr) { + pr_warning("unwind: Unknown symbol address %08lx\n", addr); + return NULL; + } else if (addr >= last->addr) + return last; + + while (first < last - 1) { + struct unwind_idx *mid = first + ((last - first + 1) >> 1); + + if (addr < mid->addr) + last = mid; + else + first = mid; + } + + return first; +} + +static struct unwind_idx *unwind_find_idx(unsigned long addr) +{ + struct unwind_idx *idx = NULL; + unsigned long flags; + + pr_debug("%s(%08lx)\n", __func__, addr); + + if (core_kernel_text(addr)) + /* main unwind table */ + idx = search_index(addr, __start_unwind_idx, + __stop_unwind_idx - 1); + else { + /* module unwind tables */ + struct unwind_table *table; + + spin_lock_irqsave(&unwind_lock, flags); + list_for_each_entry(table, &unwind_tables, list) { + if (addr >= table->begin_addr && + addr < table->end_addr) { + idx = search_index(addr, table->start, + table->stop - 1); + break; + } + } + spin_unlock_irqrestore(&unwind_lock, flags); + } + + pr_debug("%s: idx = %p\n", __func__, idx); + return idx; +} + +static unsigned long unwind_get_byte(struct unwind_ctrl_block *ctrl) +{ + unsigned long ret; + + if (ctrl->entries <= 0) { + pr_warning("unwind: Corrupt unwind table\n"); + return 0; + } + + ret = (*ctrl->insn >> (ctrl->byte * 8)) & 0xff; + + if (ctrl->byte == 0) { + ctrl->insn++; + ctrl->entries--; + ctrl->byte = 3; + } else + ctrl->byte--; + + return ret; +} + +/* + * Execute the current unwind instruction. + */ +static int unwind_exec_insn(struct unwind_ctrl_block *ctrl) +{ + unsigned long insn = unwind_get_byte(ctrl); + + pr_debug("%s: insn = %08lx\n", __func__, insn); + + if ((insn & 0xc0) == 0x00) + ctrl->vrs[SP] += ((insn & 0x3f) << 2) + 4; + else if ((insn & 0xc0) == 0x40) + ctrl->vrs[SP] -= ((insn & 0x3f) << 2) + 4; + else if ((insn & 0xf0) == 0x80) { + unsigned long mask; + unsigned long *vsp = (unsigned long *)ctrl->vrs[SP]; + int load_sp, reg = 4; + + insn = (insn << 8) | unwind_get_byte(ctrl); + mask = insn & 0x0fff; + if (mask == 0) { + pr_warning("unwind: 'Refuse to unwind' instruction %04lx\n", + insn); + return -URC_FAILURE; + } + + /* pop R4-R15 according to mask */ + load_sp = mask & (1 << (13 - 4)); + while (mask) { + if (mask & 1) + ctrl->vrs[reg] = *vsp++; + mask >>= 1; + reg++; + } + if (!load_sp) + ctrl->vrs[SP] = (unsigned long)vsp; + } else if ((insn & 0xf0) == 0x90 && + (insn & 0x0d) != 0x0d) + ctrl->vrs[SP] = ctrl->vrs[insn & 0x0f]; + else if ((insn & 0xf0) == 0xa0) { + unsigned long *vsp = (unsigned long *)ctrl->vrs[SP]; + int reg; + + /* pop R4-R[4+bbb] */ + for (reg = 4; reg <= 4 + (insn & 7); reg++) + ctrl->vrs[reg] = *vsp++; + if (insn & 0x80) + ctrl->vrs[14] = *vsp++; + ctrl->vrs[SP] = (unsigned long)vsp; + } else if (insn == 0xb0) { + if (ctrl->vrs[PC] == 0) + ctrl->vrs[PC] = ctrl->vrs[LR]; + /* no further processing */ + ctrl->entries = 0; + } else if (insn == 0xb1) { + unsigned long mask = unwind_get_byte(ctrl); + unsigned long *vsp = (unsigned long *)ctrl->vrs[SP]; + int reg = 0; + + if (mask == 0 || mask & 0xf0) { + pr_warning("unwind: Spare encoding %04lx\n", + (insn << 8) | mask); + return -URC_FAILURE; + } + + /* pop R0-R3 according to mask */ + while (mask) { + if (mask & 1) + ctrl->vrs[reg] = *vsp++; + mask >>= 1; + reg++; + } + ctrl->vrs[SP] = (unsigned long)vsp; + } else if (insn == 0xb2) { + unsigned long uleb128 = unwind_get_byte(ctrl); + + ctrl->vrs[SP] += 0x204 + (uleb128 << 2); + } else { + pr_warning("unwind: Unhandled instruction %02lx\n", insn); + return -URC_FAILURE; + } + + pr_debug("%s: fp = %08lx sp = %08lx lr = %08lx pc = %08lx\n", __func__, + ctrl->vrs[FP], ctrl->vrs[SP], ctrl->vrs[LR], ctrl->vrs[PC]); + + return URC_OK; +} + +/* + * Unwind a single frame starting with *sp for the symbol at *pc. It + * updates the *pc and *sp with the new values. + */ +int unwind_frame(struct stackframe *frame) +{ + unsigned long high, low; + struct unwind_idx *idx; + struct unwind_ctrl_block ctrl; + + /* only go to a higher address on the stack */ + low = frame->sp; + high = ALIGN(low, THREAD_SIZE) + THREAD_SIZE; + + pr_debug("%s(pc = %08lx lr = %08lx sp = %08lx)\n", __func__, + frame->pc, frame->lr, frame->sp); + + if (!kernel_text_address(frame->pc)) + return -URC_FAILURE; + + idx = unwind_find_idx(frame->pc); + if (!idx) { + pr_warning("unwind: Index not found %08lx\n", frame->pc); + return -URC_FAILURE; + } + + ctrl.vrs[FP] = frame->fp; + ctrl.vrs[SP] = frame->sp; + ctrl.vrs[LR] = frame->lr; + ctrl.vrs[PC] = 0; + + if (idx->insn == 1) + /* can't unwind */ + return -URC_FAILURE; + else if ((idx->insn & 0x80000000) == 0) + /* prel31 to the unwind table */ + ctrl.insn = (unsigned long *)prel31_to_addr(&idx->insn); + else if ((idx->insn & 0xff000000) == 0x80000000) + /* only personality routine 0 supported in the index */ + ctrl.insn = &idx->insn; + else { + pr_warning("unwind: Unsupported personality routine %08lx in the index at %p\n", + idx->insn, idx); + return -URC_FAILURE; + } + + /* check the personality routine */ + if ((*ctrl.insn & 0xff000000) == 0x80000000) { + ctrl.byte = 2; + ctrl.entries = 1; + } else if ((*ctrl.insn & 0xff000000) == 0x81000000) { + ctrl.byte = 1; + ctrl.entries = 1 + ((*ctrl.insn & 0x00ff0000) >> 16); + } else { + pr_warning("unwind: Unsupported personality routine %08lx at %p\n", + *ctrl.insn, ctrl.insn); + return -URC_FAILURE; + } + + while (ctrl.entries > 0) { + int urc = unwind_exec_insn(&ctrl); + if (urc < 0) + return urc; + if (ctrl.vrs[SP] < low || ctrl.vrs[SP] >= high) + return -URC_FAILURE; + } + + if (ctrl.vrs[PC] == 0) + ctrl.vrs[PC] = ctrl.vrs[LR]; + + /* check for infinite loop */ + if (frame->pc == ctrl.vrs[PC]) + return -URC_FAILURE; + + frame->fp = ctrl.vrs[FP]; + frame->sp = ctrl.vrs[SP]; + frame->lr = ctrl.vrs[LR]; + frame->pc = ctrl.vrs[PC]; + + return URC_OK; +} + +void unwind_backtrace(struct pt_regs *regs, struct task_struct *tsk) +{ + struct stackframe frame; + register unsigned long current_sp asm ("sp"); + + pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk); + + if (!tsk) + tsk = current; + + if (regs) { + frame.fp = regs->ARM_fp; + frame.sp = regs->ARM_sp; + frame.lr = regs->ARM_lr; + frame.pc = regs->ARM_pc; + } else if (tsk == current) { + frame.fp = (unsigned long)__builtin_frame_address(0); + frame.sp = current_sp; + frame.lr = (unsigned long)__builtin_return_address(0); + frame.pc = (unsigned long)unwind_backtrace; + } else { + /* task blocked in __switch_to */ + frame.fp = thread_saved_fp(tsk); + frame.sp = thread_saved_sp(tsk); + /* + * The function calling __switch_to cannot be a leaf function + * so LR is recovered from the stack. + */ + frame.lr = 0; + frame.pc = thread_saved_pc(tsk); + } + + while (1) { + int urc; + unsigned long where = frame.pc; + + urc = unwind_frame(&frame); + if (urc < 0) + break; + dump_backtrace_entry(where, frame.pc, frame.sp - 4); + } +} + +struct unwind_table *unwind_table_add(unsigned long start, unsigned long size, + unsigned long text_addr, + unsigned long text_size) +{ + unsigned long flags; + struct unwind_idx *idx; + struct unwind_table *tab = kmalloc(sizeof(*tab), GFP_KERNEL); + + pr_debug("%s(%08lx, %08lx, %08lx, %08lx)\n", __func__, start, size, + text_addr, text_size); + + if (!tab) + return tab; + + tab->start = (struct unwind_idx *)start; + tab->stop = (struct unwind_idx *)(start + size); + tab->begin_addr = text_addr; + tab->end_addr = text_addr + text_size; + + /* Convert the symbol addresses to absolute values */ + for (idx = tab->start; idx < tab->stop; idx++) + idx->addr = prel31_to_addr(&idx->addr); + + spin_lock_irqsave(&unwind_lock, flags); + list_add_tail(&tab->list, &unwind_tables); + spin_unlock_irqrestore(&unwind_lock, flags); + + return tab; +} + +void unwind_table_del(struct unwind_table *tab) +{ + unsigned long flags; + + if (!tab) + return; + + spin_lock_irqsave(&unwind_lock, flags); + list_del(&tab->list); + spin_unlock_irqrestore(&unwind_lock, flags); + + kfree(tab); +} + +int __init unwind_init(void) +{ + struct unwind_idx *idx; + + /* Convert the symbol addresses to absolute values */ + for (idx = __start_unwind_idx; idx < __stop_unwind_idx; idx++) + idx->addr = prel31_to_addr(&idx->addr); + + pr_debug("unwind: ARM stack unwinding initialised\n"); + + return 0; +} diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S index 00216071eaf7..433b1e4bf46a 100644 --- a/arch/arm/kernel/vmlinux.lds.S +++ b/arch/arm/kernel/vmlinux.lds.S @@ -80,6 +80,16 @@ SECTIONS EXIT_TEXT EXIT_DATA *(.exitcall.exit) + *(.ARM.exidx.exit.text) + *(.ARM.extab.exit.text) +#ifndef CONFIG_HOTPLUG_CPU + *(.ARM.exidx.cpuexit.text) + *(.ARM.extab.cpuexit.text) +#endif +#ifndef CONFIG_HOTPLUG + *(.ARM.exidx.devexit.text) + *(.ARM.extab.devexit.text) +#endif #ifndef CONFIG_MMU *(.fixup) *(__ex_table) @@ -110,6 +120,23 @@ SECTIONS _etext = .; /* End of text and rodata section */ +#ifdef CONFIG_ARM_UNWIND + /* + * Stack unwinding tables + */ + . = ALIGN(8); + .ARM.unwind_idx : { + __start_unwind_idx = .; + *(.ARM.exidx*) + __stop_unwind_idx = .; + } + .ARM.unwind_tab : { + __start_unwind_tab = .; + *(.ARM.extab*) + __stop_unwind_tab = .; + } +#endif + #ifdef CONFIG_XIP_KERNEL __data_loc = ALIGN(4); /* location in binary */ . = PAGE_OFFSET + TEXT_OFFSET; diff --git a/arch/arm/lib/ashldi3.S b/arch/arm/lib/ashldi3.S index 1154d924080b..b18944b85e4b 100644 --- a/arch/arm/lib/ashldi3.S +++ b/arch/arm/lib/ashldi3.S @@ -41,9 +41,12 @@ ENTRY(__aeabi_llsl) subs r3, r2, #32 rsb ip, r2, #32 + itett mi movmi ah, ah, lsl r2 movpl ah, al, lsl r3 - orrmi ah, ah, al, lsr ip + ARM( orrmi ah, ah, al, lsr ip ) + THUMB( lsrmi r3, al, ip ) + THUMB( orrmi ah, ah, r3 ) mov al, al, lsl r2 mov pc, lr diff --git a/arch/arm/lib/ashrdi3.S b/arch/arm/lib/ashrdi3.S index 9f8b35572f8c..0d5ace74dd9d 100644 --- a/arch/arm/lib/ashrdi3.S +++ b/arch/arm/lib/ashrdi3.S @@ -41,9 +41,12 @@ ENTRY(__aeabi_lasr) subs r3, r2, #32 rsb ip, r2, #32 + itett mi movmi al, al, lsr r2 movpl al, ah, asr r3 - orrmi al, al, ah, lsl ip + ARM( orrmi al, al, ah, lsl ip ) + THUMB( lslmi r3, ah, ip ) + THUMB( orrmi al, al, r3 ) mov ah, ah, asr r2 mov pc, lr diff --git a/arch/arm/lib/backtrace.S b/arch/arm/lib/backtrace.S index b0951d0e8b2c..41c4d3561b8f 100644 --- a/arch/arm/lib/backtrace.S +++ b/arch/arm/lib/backtrace.S @@ -28,7 +28,7 @@ ENTRY(__backtrace) ENTRY(c_backtrace) -#if !defined(CONFIG_FRAME_POINTER) || !defined(CONFIG_PRINTK) +#if defined(CONFIG_THUMB2_KERNEL) || !defined(CONFIG_FRAME_POINTER) || !defined(CONFIG_PRINTK) mov pc, lr ENDPROC(__backtrace) ENDPROC(c_backtrace) @@ -38,7 +38,10 @@ ENDPROC(c_backtrace) beq no_frame @ we have no stack frames tst r1, #0x10 @ 26 or 32-bit mode? - moveq mask, #0xfc000003 @ mask for 26-bit + itte eq + ARM( moveq mask, #0xfc000003 ) + THUMB( moveq mask, #0xfc000000 ) + THUMB( orreq mask, #0x03 ) movne mask, #0 @ mask for 32-bit 1: stmfd sp!, {pc} @ calculate offset of PC stored @@ -73,6 +76,7 @@ for_each_frame: tst frame, mask @ Check for address exceptions 1003: ldr r2, [sv_pc, #-4] @ if stmfd sp!, {args} exists, ldr r3, .Ldsi+4 @ adjust saved 'pc' back one teq r3, r2, lsr #10 @ instruction + ite ne subne r0, sv_pc, #4 @ allow for mov subeq r0, sv_pc, #8 @ allow for mov + stmia @@ -84,6 +88,7 @@ for_each_frame: tst frame, mask @ Check for address exceptions ldr r1, [sv_pc, #-4] @ if stmfd sp!, {args} exists, ldr r3, .Ldsi+4 teq r3, r1, lsr #10 + ittt eq ldreq r0, [frame, #-8] @ get sp subeq r0, r0, #4 @ point at the last arg bleq .Ldumpstm @ dump saved registers @@ -91,6 +96,7 @@ for_each_frame: tst frame, mask @ Check for address exceptions 1004: ldr r1, [sv_pc, #0] @ if stmfd sp!, {..., fp, ip, lr, pc} ldr r3, .Ldsi @ instruction exists, teq r3, r1, lsr #10 + itt eq subeq r0, frame, #16 bleq .Ldumpstm @ dump saved registers @@ -126,10 +132,13 @@ ENDPROC(c_backtrace) mov reg, #10 mov r7, #0 1: mov r3, #1 - tst instr, r3, lsl reg + ARM( tst instr, r3, lsl reg ) + THUMB( lsl r3, reg ) + THUMB( tst instr, r3 ) beq 2f add r7, r7, #1 teq r7, #6 + itte eq moveq r7, #1 moveq r1, #'\n' movne r1, #' ' @@ -140,6 +149,7 @@ ENDPROC(c_backtrace) 2: subs reg, reg, #1 bpl 1b teq r7, #0 + itt ne adrne r0, .Lcr blne printk ldmfd sp!, {instr, reg, stack, r7, pc} diff --git a/arch/arm/lib/bitops.h b/arch/arm/lib/bitops.h index 2e787d40d599..28c7e4e1ef76 100644 --- a/arch/arm/lib/bitops.h +++ b/arch/arm/lib/bitops.h @@ -5,6 +5,13 @@ and r3, r0, #7 @ Get bit offset add r1, r1, r0, lsr #3 @ Get byte offset mov r3, r2, lsl r3 +#ifdef CONFIG_ARM_ERRATA_351422 + mrc p15, 0, r0, c0, c0, 5 + and r0, r0, #0xf + mov r0, r0, lsl #8 +3: subs r0, r0, #1 + bpl 3b +#endif 1: ldrexb r2, [r1] \instr r2, r2, r3 strexb r0, r2, [r1] @@ -13,18 +20,29 @@ mov pc, lr .endm - .macro testop, instr, store + .macro testop, instr, store, cond=al and r3, r0, #7 @ Get bit offset mov r2, #1 add r1, r1, r0, lsr #3 @ Get byte offset mov r3, r2, lsl r3 @ create mask +#ifdef CONFIG_ARM_ERRATA_351422 + mrc p15, 0, r0, c0, c0, 5 + and r0, r0, #0xf + mov r0, r0, lsl #8 +3: subs r0, r0, #1 + bpl 3b +#endif 1: ldrexb r2, [r1] ands r0, r2, r3 @ save old value of bit - \instr r2, r2, r3 @ toggle bit + .ifnc \cond,al + it \cond + .endif + \instr r2, r2, r3 @ toggle bit strexb ip, r2, [r1] cmp ip, #0 bne 1b cmp r0, #0 + it ne movne r0, #1 2: mov pc, lr .endm @@ -49,7 +67,7 @@ * Note: we can trivially conditionalise the store instruction * to avoid dirtying the data cache. */ - .macro testop, instr, store + .macro testop, instr, store, cond=al add r1, r1, r0, lsr #3 and r3, r0, #7 mov r0, #1 diff --git a/arch/arm/lib/clear_user.S b/arch/arm/lib/clear_user.S index 4d6bc71231f3..2147bec77ce4 100644 --- a/arch/arm/lib/clear_user.S +++ b/arch/arm/lib/clear_user.S @@ -26,21 +26,20 @@ ENTRY(__clear_user) ands ip, r0, #3 beq 1f cmp ip, #2 -USER( strbt r2, [r0], #1) -USER( strlebt r2, [r0], #1) -USER( strltbt r2, [r0], #1) + strusr r2, r0, 1 + strusr r2, r0, 1, le + strusr r2, r0, 1, lt rsb ip, ip, #4 sub r1, r1, ip @ 7 6 5 4 3 2 1 1: subs r1, r1, #8 @ -1 -2 -3 -4 -5 -6 -7 -USER( strplt r2, [r0], #4) -USER( strplt r2, [r0], #4) + strusr r2, r0, 4, pl, rept=2 bpl 1b adds r1, r1, #4 @ 3 2 1 0 -1 -2 -3 -USER( strplt r2, [r0], #4) + strusr r2, r0, 4, pl 2: tst r1, #2 @ 1x 1x 0x 0x 1x 1x 0x -USER( strnebt r2, [r0], #1) -USER( strnebt r2, [r0], #1) + strusr r2, r0, 1, ne, rept=2 tst r1, #1 @ x1 x0 x1 x0 x1 x0 x1 + it ne @ explicit IT needed for the label USER( strnebt r2, [r0]) mov r0, #0 ldmfd sp!, {r1, pc} diff --git a/arch/arm/lib/copy_from_user.S b/arch/arm/lib/copy_from_user.S index 56799a165cc4..9a7a16426428 100644 --- a/arch/arm/lib/copy_from_user.S +++ b/arch/arm/lib/copy_from_user.S @@ -33,11 +33,15 @@ * Number of bytes NOT copied. */ +#ifndef CONFIG_THUMB2_KERNEL +#define LDR1W_SHIFT 0 +#else +#define LDR1W_SHIFT 1 +#endif +#define STR1W_SHIFT 0 + .macro ldr1w ptr reg abort -100: ldrt \reg, [\ptr], #4 - .section __ex_table, "a" - .long 100b, \abort - .previous + ldrusr \reg, \ptr, 4, abort=\abort .endm .macro ldr4w ptr reg1 reg2 reg3 reg4 abort @@ -53,14 +57,11 @@ .endm .macro ldr1b ptr reg cond=al abort -100: ldr\cond\()bt \reg, [\ptr], #1 - .section __ex_table, "a" - .long 100b, \abort - .previous + ldrusr \reg, \ptr, 1, \cond, abort=\abort .endm .macro str1w ptr reg abort - str \reg, [\ptr], #4 + W(str) \reg, [\ptr], #4 .endm .macro str8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort @@ -68,6 +69,9 @@ .endm .macro str1b ptr reg cond=al abort + .ifnc \cond,al + it \cond + .endif str\cond\()b \reg, [\ptr], #1 .endm diff --git a/arch/arm/lib/copy_page.S b/arch/arm/lib/copy_page.S index 6ae04db1ca4f..1c57a034cadf 100644 --- a/arch/arm/lib/copy_page.S +++ b/arch/arm/lib/copy_page.S @@ -39,8 +39,10 @@ ENTRY(copy_page) ldmia r1!, {r3, r4, ip, lr} @ 4 subs r2, r2, #1 @ 1 stmia r0!, {r3, r4, ip, lr} @ 4 + itt gt ldmgtia r1!, {r3, r4, ip, lr} @ 4 bgt 1b @ 1 + PLD( itt eq ) PLD( ldmeqia r1!, {r3, r4, ip, lr} ) PLD( beq 2b ) ldmfd sp!, {r4, pc} @ 3 diff --git a/arch/arm/lib/copy_template.S b/arch/arm/lib/copy_template.S index 139cce646055..8e8fc03f55bd 100644 --- a/arch/arm/lib/copy_template.S +++ b/arch/arm/lib/copy_template.S @@ -57,6 +57,13 @@ * * Restore registers with the values previously saved with the * 'preserv' macro. Called upon code termination. + * + * LDR1W_SHIFT + * STR1W_SHIFT + * + * Correction to be applied to the "ip" register when branching into + * the ldr1w or str1w instructions (some of these macros may expand to + * than one 32bit instruction in Thumb-2) */ @@ -99,9 +106,16 @@ 5: ands ip, r2, #28 rsb ip, ip, #32 +#if LDR1W_SHIFT > 0 + lsl ip, ip, #LDR1W_SHIFT +#endif + it ne addne pc, pc, ip @ C is always clear here b 7f -6: nop +6: + .rept (1 << LDR1W_SHIFT) + W(nop) + .endr ldr1w r1, r3, abort=20f ldr1w r1, r4, abort=20f ldr1w r1, r5, abort=20f @@ -110,9 +124,16 @@ ldr1w r1, r8, abort=20f ldr1w r1, lr, abort=20f +#if LDR1W_SHIFT < STR1W_SHIFT + lsl ip, ip, #STR1W_SHIFT - LDR1W_SHIFT +#elif LDR1W_SHIFT > STR1W_SHIFT + lsr ip, ip, #LDR1W_SHIFT - STR1W_SHIFT +#endif add pc, pc, ip nop - nop + .rept (1 << STR1W_SHIFT) + W(nop) + .endr str1w r0, r3, abort=20f str1w r0, r4, abort=20f str1w r0, r5, abort=20f diff --git a/arch/arm/lib/copy_to_user.S b/arch/arm/lib/copy_to_user.S index 22f968bbdffd..dc0fe7391527 100644 --- a/arch/arm/lib/copy_to_user.S +++ b/arch/arm/lib/copy_to_user.S @@ -33,8 +33,15 @@ * Number of bytes NOT copied. */ +#define LDR1W_SHIFT 0 +#ifndef CONFIG_THUMB2_KERNEL +#define STR1W_SHIFT 0 +#else +#define STR1W_SHIFT 1 +#endif + .macro ldr1w ptr reg abort - ldr \reg, [\ptr], #4 + W(ldr) \reg, [\ptr], #4 .endm .macro ldr4w ptr reg1 reg2 reg3 reg4 abort @@ -46,14 +53,14 @@ .endm .macro ldr1b ptr reg cond=al abort + .ifnc \cond,al + it \cond + .endif ldr\cond\()b \reg, [\ptr], #1 .endm .macro str1w ptr reg abort -100: strt \reg, [\ptr], #4 - .section __ex_table, "a" - .long 100b, \abort - .previous + strusr \reg, \ptr, 4, abort=\abort .endm .macro str8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort @@ -68,10 +75,7 @@ .endm .macro str1b ptr reg cond=al abort -100: str\cond\()bt \reg, [\ptr], #1 - .section __ex_table, "a" - .long 100b, \abort - .previous + strusr \reg, \ptr, 1, \cond, abort=\abort .endm .macro enter reg1 reg2 diff --git a/arch/arm/lib/csumpartial.S b/arch/arm/lib/csumpartial.S index 31d3cb34740d..e9a504e4302e 100644 --- a/arch/arm/lib/csumpartial.S +++ b/arch/arm/lib/csumpartial.S @@ -39,6 +39,7 @@ td3 .req lr /* we must have at least one byte. */ tst buf, #1 @ odd address? + itttt ne movne sum, sum, ror #8 ldrneb td0, [buf], #1 subne len, len, #1 @@ -68,25 +69,30 @@ td3 .req lr bne .Lless8_wordlp .Lless8_byte: tst len, #1 @ odd number of bytes + itt ne ldrneb td0, [buf], #1 @ include last byte adcnes sum, sum, td0, put_byte_0 @ update checksum .Ldone: adc r0, sum, #0 @ collect up the last carry ldr td0, [sp], #4 tst td0, #1 @ check buffer alignment + it ne movne r0, r0, ror #8 @ rotate checksum by 8 bits ldr pc, [sp], #4 @ return .Lnot_aligned: tst buf, #1 @ odd address + ittt ne ldrneb td0, [buf], #1 @ make even subne len, len, #1 adcnes sum, sum, td0, put_byte_1 @ update checksum tst buf, #2 @ 32-bit aligned? #if __LINUX_ARM_ARCH__ >= 4 + itt ne ldrneh td0, [buf], #2 @ make 32-bit aligned subne len, len, #2 #else + itttt ne ldrneb td0, [buf], #1 ldrneb ip, [buf], #1 subne len, len, #2 @@ -96,6 +102,7 @@ td3 .req lr orrne td0, ip, td0, lsl #8 #endif #endif + it ne adcnes sum, sum, td0 @ update checksum mov pc, lr @@ -105,10 +112,12 @@ ENTRY(csum_partial) blo .Lless8 @ 8 bytes to copy. tst buf, #1 + it ne movne sum, sum, ror #8 adds sum, sum, #0 @ C = 0 tst buf, #3 @ Test destination alignment + it ne blne .Lnot_aligned @ align destination, return here 1: bics ip, len, #31 diff --git a/arch/arm/lib/csumpartialcopygeneric.S b/arch/arm/lib/csumpartialcopygeneric.S index d620a5f22a09..8e1c141b6524 100644 --- a/arch/arm/lib/csumpartialcopygeneric.S +++ b/arch/arm/lib/csumpartialcopygeneric.S @@ -40,6 +40,7 @@ sum .req r3 adcs sum, sum, ip, put_byte_1 @ update checksum strb ip, [dst], #1 tst dst, #2 + it eq moveq pc, lr @ dst is now 32bit aligned .Ldst_16bit: load2b r8, ip @@ -94,6 +95,7 @@ FN_ENTRY adds sum, sum, #0 @ C = 0 tst dst, #3 @ Test destination alignment + it ne blne .Ldst_unaligned @ align destination, return here /* @@ -147,6 +149,7 @@ FN_ENTRY strb r5, [dst], #1 mov r5, r4, get_byte_2 .Lexit: tst len, #1 + ittt ne strneb r5, [dst], #1 andne r5, r5, #255 adcnes sum, sum, r5, put_byte_0 @@ -160,6 +163,7 @@ FN_ENTRY .Ldone: adc r0, sum, #0 ldr sum, [sp, #0] @ dst tst sum, #1 + it ne movne r0, r0, ror #8 load_regs diff --git a/arch/arm/lib/csumpartialcopyuser.S b/arch/arm/lib/csumpartialcopyuser.S index 14677fb4b0c4..152ed83480f7 100644 --- a/arch/arm/lib/csumpartialcopyuser.S +++ b/arch/arm/lib/csumpartialcopyuser.S @@ -26,50 +26,28 @@ .endm .macro load1b, reg1 -9999: ldrbt \reg1, [r0], $1 - .section __ex_table, "a" - .align 3 - .long 9999b, 6001f - .previous + ldrusr \reg1, r0, 1 .endm .macro load2b, reg1, reg2 -9999: ldrbt \reg1, [r0], $1 -9998: ldrbt \reg2, [r0], $1 - .section __ex_table, "a" - .long 9999b, 6001f - .long 9998b, 6001f - .previous + ldrusr \reg1, r0, 1 + ldrusr \reg2, r0, 1 .endm .macro load1l, reg1 -9999: ldrt \reg1, [r0], $4 - .section __ex_table, "a" - .align 3 - .long 9999b, 6001f - .previous + ldrusr \reg1, r0, 4 .endm .macro load2l, reg1, reg2 -9999: ldrt \reg1, [r0], $4 -9998: ldrt \reg2, [r0], $4 - .section __ex_table, "a" - .long 9999b, 6001f - .long 9998b, 6001f - .previous + ldrusr \reg1, r0, 4 + ldrusr \reg2, r0, 4 .endm .macro load4l, reg1, reg2, reg3, reg4 -9999: ldrt \reg1, [r0], $4 -9998: ldrt \reg2, [r0], $4 -9997: ldrt \reg3, [r0], $4 -9996: ldrt \reg4, [r0], $4 - .section __ex_table, "a" - .long 9999b, 6001f - .long 9998b, 6001f - .long 9997b, 6001f - .long 9996b, 6001f - .previous + ldrusr \reg1, r0, 4 + ldrusr \reg2, r0, 4 + ldrusr \reg3, r0, 4 + ldrusr \reg4, r0, 4 .endm /* @@ -92,14 +70,15 @@ */ .section .fixup,"ax" .align 4 -6001: mov r4, #-EFAULT +9001: mov r4, #-EFAULT ldr r5, [fp, #4] @ *err_ptr str r4, [r5] ldmia sp, {r1, r2} @ retrieve dst, len add r2, r2, r1 mov r0, #0 @ zero the buffer -6002: teq r2, r1 +9002: teq r2, r1 + it ne strneb r0, [r1], #1 - bne 6002b + bne 9002b load_regs .previous diff --git a/arch/arm/lib/delay.S b/arch/arm/lib/delay.S index 8d6a8762ab88..fcd87ffe2b1d 100644 --- a/arch/arm/lib/delay.S +++ b/arch/arm/lib/delay.S @@ -31,6 +31,7 @@ ENTRY(__const_udelay) @ 0 <= r0 <= 0x7fffff06 mov r2, r2, lsr #10 @ max = 0x00007fff mul r0, r2, r0 @ max = 2^32-1 movs r0, r0, lsr #6 + it eq moveq pc, lr /* @@ -58,6 +59,7 @@ ENTRY(__delay) movls pc, lr subs r0, r0, #1 #endif + it hi bhi __delay mov pc, lr ENDPROC(__udelay) diff --git a/arch/arm/lib/div64.S b/arch/arm/lib/div64.S index 1425e789ba86..d02268ac7baf 100644 --- a/arch/arm/lib/div64.S +++ b/arch/arm/lib/div64.S @@ -84,8 +84,10 @@ ENTRY(__do_div64) @ The division loop for needed upper bit positions. @ Break out early if dividend reaches 0. 2: cmp xh, yl + itt cs orrcs yh, yh, ip subcss xh, xh, yl + it ne movnes ip, ip, lsr #1 mov yl, yl, lsr #1 bne 2b @@ -93,7 +95,9 @@ ENTRY(__do_div64) @ See if we need to handle lower 32-bit result. 3: cmp xh, #0 mov yl, #0 + it eq cmpeq xl, r4 + itt lo movlo xh, xl movlo pc, lr @@ -104,7 +108,9 @@ ENTRY(__do_div64) 4: movs xl, xl, lsl #1 adcs xh, xh, xh beq 6f + it cc cmpcc xh, r4 + itt cs 5: orrcs yl, yl, ip subcs xh, xh, r4 movs ip, ip, lsr #1 @@ -116,6 +122,7 @@ ENTRY(__do_div64) @ Otherwise, if lower part is also null then we are done. 6: bcs 5b cmp xl, #0 + it eq moveq pc, lr @ We still have remainer bits in the low part. Bring them up. @@ -177,13 +184,16 @@ ENTRY(__do_div64) mov yh, xh, lsr ip mov yl, xl, lsr ip rsb ip, ip, #32 - orr yl, yl, xh, lsl ip + ARM( orr yl, yl, xh, lsl ip ) + THUMB( lsl xh, xh, ip ) + THUMB( orr yl, yl, xh ) mov xh, xl, lsl ip mov xh, xh, lsr ip mov pc, lr @ eq -> division by 1: obvious enough... -9: moveq yl, xl +9: itttt eq + moveq yl, xl moveq yh, xh moveq xh, #0 moveq pc, lr diff --git a/arch/arm/lib/findbit.S b/arch/arm/lib/findbit.S index 8c4defc4f3c4..1e4cbd4e7be9 100644 --- a/arch/arm/lib/findbit.S +++ b/arch/arm/lib/findbit.S @@ -25,7 +25,10 @@ ENTRY(_find_first_zero_bit_le) teq r1, #0 beq 3f mov r2, #0 -1: ldrb r3, [r0, r2, lsr #3] +1: + ARM( ldrb r3, [r0, r2, lsr #3] ) + THUMB( lsr r3, r2, #3 ) + THUMB( ldrb r3, [r0, r3] ) eors r3, r3, #0xff @ invert bits bne .L_found @ any now set - found zero bit add r2, r2, #8 @ next bit pointer @@ -44,7 +47,9 @@ ENTRY(_find_next_zero_bit_le) beq 3b ands ip, r2, #7 beq 1b @ If new byte, goto old routine - ldrb r3, [r0, r2, lsr #3] + ARM( ldrb r3, [r0, r2, lsr #3] ) + THUMB( lsr r3, r2, #3 ) + THUMB( ldrb r3, [r0, r3] ) eor r3, r3, #0xff @ now looking for a 1 bit movs r3, r3, lsr ip @ shift off unused bits bne .L_found @@ -61,7 +66,10 @@ ENTRY(_find_first_bit_le) teq r1, #0 beq 3f mov r2, #0 -1: ldrb r3, [r0, r2, lsr #3] +1: + ARM( ldrb r3, [r0, r2, lsr #3] ) + THUMB( lsr r3, r2, #3 ) + THUMB( ldrb r3, [r0, r3] ) movs r3, r3 bne .L_found @ any now set - found zero bit add r2, r2, #8 @ next bit pointer @@ -80,7 +88,9 @@ ENTRY(_find_next_bit_le) beq 3b ands ip, r2, #7 beq 1b @ If new byte, goto old routine - ldrb r3, [r0, r2, lsr #3] + ARM( ldrb r3, [r0, r2, lsr #3] ) + THUMB( lsr r3, r2, #3 ) + THUMB( ldrb r3, [r0, r3] ) movs r3, r3, lsr ip @ shift off unused bits bne .L_found orr r2, r2, #7 @ if zero, then no bits here @@ -95,7 +105,9 @@ ENTRY(_find_first_zero_bit_be) beq 3f mov r2, #0 1: eor r3, r2, #0x18 @ big endian byte ordering - ldrb r3, [r0, r3, lsr #3] + ARM( ldrb r3, [r0, r3, lsr #3] ) + THUMB( lsr r3, #3 ) + THUMB( ldrb r3, [r0, r3] ) eors r3, r3, #0xff @ invert bits bne .L_found @ any now set - found zero bit add r2, r2, #8 @ next bit pointer @@ -111,7 +123,9 @@ ENTRY(_find_next_zero_bit_be) ands ip, r2, #7 beq 1b @ If new byte, goto old routine eor r3, r2, #0x18 @ big endian byte ordering - ldrb r3, [r0, r3, lsr #3] + ARM( ldrb r3, [r0, r3, lsr #3] ) + THUMB( lsr r3, #3 ) + THUMB( ldrb r3, [r0, r3] ) eor r3, r3, #0xff @ now looking for a 1 bit movs r3, r3, lsr ip @ shift off unused bits bne .L_found @@ -125,7 +139,9 @@ ENTRY(_find_first_bit_be) beq 3f mov r2, #0 1: eor r3, r2, #0x18 @ big endian byte ordering - ldrb r3, [r0, r3, lsr #3] + ARM( ldrb r3, [r0, r3, lsr #3] ) + THUMB( lsr r3, #3 ) + THUMB( ldrb r3, [r0, r3] ) movs r3, r3 bne .L_found @ any now set - found zero bit add r2, r2, #8 @ next bit pointer @@ -141,7 +157,9 @@ ENTRY(_find_next_bit_be) ands ip, r2, #7 beq 1b @ If new byte, goto old routine eor r3, r2, #0x18 @ big endian byte ordering - ldrb r3, [r0, r3, lsr #3] + ARM( ldrb r3, [r0, r3, lsr #3] ) + THUMB( lsr r3, #3 ) + THUMB( ldrb r3, [r0, r3] ) movs r3, r3, lsr ip @ shift off unused bits bne .L_found orr r2, r2, #7 @ if zero, then no bits here diff --git a/arch/arm/lib/getuser.S b/arch/arm/lib/getuser.S index 6763088b7607..3e1b98056c50 100644 --- a/arch/arm/lib/getuser.S +++ b/arch/arm/lib/getuser.S @@ -36,7 +36,10 @@ ENTRY(__get_user_1) ENDPROC(__get_user_1) ENTRY(__get_user_2) -2: ldrbt r2, [r0], #1 +2: + ARM( ldrbt r2, [r0], #1 ) + THUMB( ldrbt r2, [r0] ) + THUMB( add r0, #1 ) 3: ldrbt r3, [r0] #ifndef __ARMEB__ orr r2, r2, r3, lsl #8 diff --git a/arch/arm/lib/io-readsb.S b/arch/arm/lib/io-readsb.S index 9f4238987fe9..45229e46ed5b 100644 --- a/arch/arm/lib/io-readsb.S +++ b/arch/arm/lib/io-readsb.S @@ -12,12 +12,15 @@ .Linsb_align: rsb ip, ip, #4 cmp ip, r2 + it gt movgt ip, r2 cmp ip, #2 ldrb r3, [r0] strb r3, [r1], #1 + itt ge ldrgeb r3, [r0] strgeb r3, [r1], #1 + itt gt ldrgtb r3, [r0] strgtb r3, [r1], #1 subs r2, r2, ip @@ -25,6 +28,7 @@ ENTRY(__raw_readsb) teq r2, #0 @ do we have to check for the zero len? + it eq moveq pc, lr ands ip, r1, #3 bne .Linsb_align @@ -72,6 +76,7 @@ ENTRY(__raw_readsb) bpl .Linsb_16_lp tst r2, #15 + it eq ldmeqfd sp!, {r4 - r6, pc} .Linsb_no_16: tst r2, #8 @@ -109,13 +114,16 @@ ENTRY(__raw_readsb) str r3, [r1], #4 .Linsb_no_4: ands r2, r2, #3 + it eq ldmeqfd sp!, {r4 - r6, pc} cmp r2, #2 ldrb r3, [r0] strb r3, [r1], #1 + itt ge ldrgeb r3, [r0] strgeb r3, [r1], #1 + itt gt ldrgtb r3, [r0] strgtb r3, [r1] diff --git a/arch/arm/lib/io-readsl.S b/arch/arm/lib/io-readsl.S index 5fb97e7f9f4b..1f02e66d079c 100644 --- a/arch/arm/lib/io-readsl.S +++ b/arch/arm/lib/io-readsl.S @@ -12,6 +12,7 @@ ENTRY(__raw_readsl) teq r2, #0 @ do we have to check for the zero len? + it eq moveq pc, lr ands ip, r1, #3 bne 3f @@ -28,9 +29,11 @@ ENTRY(__raw_readsl) bpl 1b ldmfd sp!, {r4, lr} 2: movs r2, r2, lsl #31 + ittt cs ldrcs r3, [r0, #0] ldrcs ip, [r0, #0] stmcsia r1!, {r3, ip} + itt ne ldrne r3, [r0, #0] strne r3, [r1, #0] mov pc, lr @@ -48,6 +51,7 @@ ENTRY(__raw_readsl) 4: subs r2, r2, #1 mov ip, r3, pull #24 + itttt ne ldrne r3, [r0] orrne ip, ip, r3, push #8 strne ip, [r1], #4 @@ -56,6 +60,7 @@ ENTRY(__raw_readsl) 5: subs r2, r2, #1 mov ip, r3, pull #16 + itttt ne ldrne r3, [r0] orrne ip, ip, r3, push #16 strne ip, [r1], #4 @@ -64,6 +69,7 @@ ENTRY(__raw_readsl) 6: subs r2, r2, #1 mov ip, r3, pull #8 + itttt ne ldrne r3, [r0] orrne ip, ip, r3, push #24 strne ip, [r1], #4 diff --git a/arch/arm/lib/io-readsw-armv4.S b/arch/arm/lib/io-readsw-armv4.S index 1f393d42593d..9db32f0541da 100644 --- a/arch/arm/lib/io-readsw-armv4.S +++ b/arch/arm/lib/io-readsw-armv4.S @@ -26,6 +26,7 @@ ENTRY(__raw_readsw) teq r2, #0 + it eq moveq pc, lr tst r1, #3 bne .Linsw_align @@ -76,7 +77,8 @@ ENTRY(__raw_readsw) pack r3, r3, ip str r3, [r1], #4 -.Lno_insw_2: ldrneh r3, [r0] +.Lno_insw_2: itt ne + ldrneh r3, [r0] strneh r3, [r1] ldmfd sp!, {r4, r5, pc} @@ -94,6 +96,7 @@ ENTRY(__raw_readsw) #endif .Linsw_noalign: stmfd sp!, {r4, lr} + it cc ldrccb ip, [r1, #-1]! bcc 1f @@ -121,6 +124,7 @@ ENTRY(__raw_readsw) 3: tst r2, #1 strb ip, [r1], #1 + itttt ne ldrneh ip, [r0] _BE_ONLY_( movne ip, ip, ror #8 ) strneb ip, [r1], #1 diff --git a/arch/arm/lib/io-writesb.S b/arch/arm/lib/io-writesb.S index 68b92f4acaeb..5fad6b0c7f05 100644 --- a/arch/arm/lib/io-writesb.S +++ b/arch/arm/lib/io-writesb.S @@ -32,12 +32,15 @@ .Loutsb_align: rsb ip, ip, #4 cmp ip, r2 + it gt movgt ip, r2 cmp ip, #2 ldrb r3, [r1], #1 strb r3, [r0] + itt ge ldrgeb r3, [r1], #1 strgeb r3, [r0] + itt gt ldrgtb r3, [r1], #1 strgtb r3, [r0] subs r2, r2, ip @@ -45,6 +48,7 @@ ENTRY(__raw_writesb) teq r2, #0 @ do we have to check for the zero len? + it eq moveq pc, lr ands ip, r1, #3 bne .Loutsb_align @@ -64,6 +68,7 @@ ENTRY(__raw_writesb) bpl .Loutsb_16_lp tst r2, #15 + it eq ldmeqfd sp!, {r4, r5, pc} .Loutsb_no_16: tst r2, #8 @@ -80,13 +85,16 @@ ENTRY(__raw_writesb) outword r3 .Loutsb_no_4: ands r2, r2, #3 + it eq ldmeqfd sp!, {r4, r5, pc} cmp r2, #2 ldrb r3, [r1], #1 strb r3, [r0] + itt ge ldrgeb r3, [r1], #1 strgeb r3, [r0] + itt gt ldrgtb r3, [r1] strgtb r3, [r0] diff --git a/arch/arm/lib/io-writesl.S b/arch/arm/lib/io-writesl.S index 8d3b7813725c..ced1d9169090 100644 --- a/arch/arm/lib/io-writesl.S +++ b/arch/arm/lib/io-writesl.S @@ -12,6 +12,7 @@ ENTRY(__raw_writesl) teq r2, #0 @ do we have to check for the zero len? + it eq moveq pc, lr ands ip, r1, #3 bne 3f @@ -28,10 +29,14 @@ ENTRY(__raw_writesl) bpl 1b ldmfd sp!, {r4, lr} 2: movs r2, r2, lsl #31 + itt cs ldmcsia r1!, {r3, ip} strcs r3, [r0, #0] + it ne ldrne r3, [r1, #0] + it cs strcs ip, [r0, #0] + it ne strne r3, [r0, #0] mov pc, lr diff --git a/arch/arm/lib/io-writesw-armv4.S b/arch/arm/lib/io-writesw-armv4.S index d6585612c86b..bb8530310ff3 100644 --- a/arch/arm/lib/io-writesw-armv4.S +++ b/arch/arm/lib/io-writesw-armv4.S @@ -31,6 +31,7 @@ ENTRY(__raw_writesw) teq r2, #0 + it eq moveq pc, lr ands r3, r1, #3 bne .Loutsw_align @@ -61,7 +62,8 @@ ENTRY(__raw_writesw) ldr r3, [r1], #4 outword r3 -.Lno_outsw_2: ldrneh r3, [r1] +.Lno_outsw_2: itt ne + ldrneh r3, [r1] strneh r3, [r0] ldmfd sp!, {r4, r5, pc} @@ -75,7 +77,11 @@ ENTRY(__raw_writesw) #endif .Loutsw_noalign: - ldr r3, [r1, -r3]! + ARM( ldr r3, [r1, -r3]! ) + THUMB( rsb r3, r3, #0 ) + THUMB( ldr r3, [r1, r3] ) + THUMB( sub r1, r3 ) + it cs subcs r2, r2, #1 bcs 2f subs r2, r2, #2 @@ -91,7 +97,8 @@ ENTRY(__raw_writesw) bpl 1b tst r2, #1 -3: movne ip, r3, lsr #8 +3: itt ne + movne ip, r3, lsr #8 strneh ip, [r0] mov pc, lr ENDPROC(__raw_writesw) diff --git a/arch/arm/lib/lib1funcs.S b/arch/arm/lib/lib1funcs.S index 67964bcfc854..1eb73e769c6c 100644 --- a/arch/arm/lib/lib1funcs.S +++ b/arch/arm/lib/lib1funcs.S @@ -56,6 +56,7 @@ Boston, MA 02111-1307, USA. */ @ at the left end of each 4 bit nibbles in the division loop @ to save one loop in most cases. tst \divisor, #0xe0000000 + itte eq moveq \divisor, \divisor, lsl #3 moveq \curbit, #8 movne \curbit, #1 @@ -65,6 +66,7 @@ Boston, MA 02111-1307, USA. */ @ division loop. Continue shifting until the divisor is @ larger than the dividend. 1: cmp \divisor, #0x10000000 + ittt lo cmplo \divisor, \dividend movlo \divisor, \divisor, lsl #4 movlo \curbit, \curbit, lsl #4 @@ -73,6 +75,7 @@ Boston, MA 02111-1307, USA. */ @ For very big divisors, we must shift it a bit at a time, or @ we will be in danger of overflowing. 1: cmp \divisor, #0x80000000 + ittt lo cmplo \divisor, \dividend movlo \divisor, \divisor, lsl #1 movlo \curbit, \curbit, lsl #1 @@ -84,19 +87,25 @@ Boston, MA 02111-1307, USA. */ @ Division loop 1: cmp \dividend, \divisor + itt hs subhs \dividend, \dividend, \divisor orrhs \result, \result, \curbit cmp \dividend, \divisor, lsr #1 + itt hs subhs \dividend, \dividend, \divisor, lsr #1 orrhs \result, \result, \curbit, lsr #1 cmp \dividend, \divisor, lsr #2 + itt hs subhs \dividend, \dividend, \divisor, lsr #2 orrhs \result, \result, \curbit, lsr #2 cmp \dividend, \divisor, lsr #3 + itt hs subhs \dividend, \dividend, \divisor, lsr #3 orrhs \result, \result, \curbit, lsr #3 cmp \dividend, #0 @ Early termination? + it ne movnes \curbit, \curbit, lsr #4 @ No, any more bits to do? + it ne movne \divisor, \divisor, lsr #4 bne 1b @@ -113,19 +122,24 @@ Boston, MA 02111-1307, USA. */ #else cmp \divisor, #(1 << 16) + itt hs movhs \divisor, \divisor, lsr #16 movhs \order, #16 + it lo movlo \order, #0 cmp \divisor, #(1 << 8) + itt hs movhs \divisor, \divisor, lsr #8 addhs \order, \order, #8 cmp \divisor, #(1 << 4) + itt hs movhs \divisor, \divisor, lsr #4 addhs \order, \order, #4 cmp \divisor, #(1 << 2) + ite hi addhi \order, \order, #3 addls \order, \order, \divisor, lsr #1 @@ -152,6 +166,7 @@ Boston, MA 02111-1307, USA. */ @ division loop. Continue shifting until the divisor is @ larger than the dividend. 1: cmp \divisor, #0x10000000 + ittt lo cmplo \divisor, \dividend movlo \divisor, \divisor, lsl #4 addlo \order, \order, #4 @@ -160,6 +175,7 @@ Boston, MA 02111-1307, USA. */ @ For very big divisors, we must shift it a bit at a time, or @ we will be in danger of overflowing. 1: cmp \divisor, #0x80000000 + ittt lo cmplo \divisor, \dividend movlo \divisor, \divisor, lsl #1 addlo \order, \order, #1 @@ -173,19 +189,25 @@ Boston, MA 02111-1307, USA. */ blt 2f 1: cmp \dividend, \divisor + it hs subhs \dividend, \dividend, \divisor cmp \dividend, \divisor, lsr #1 + it hs subhs \dividend, \dividend, \divisor, lsr #1 cmp \dividend, \divisor, lsr #2 + it hs subhs \dividend, \dividend, \divisor, lsr #2 cmp \dividend, \divisor, lsr #3 + it hs subhs \dividend, \dividend, \divisor, lsr #3 cmp \dividend, #1 mov \divisor, \divisor, lsr #4 + it ge subges \order, \order, #4 bge 1b tst \order, #3 + it ne teqne \dividend, #0 beq 5f @@ -194,12 +216,15 @@ Boston, MA 02111-1307, USA. */ blt 4f beq 3f cmp \dividend, \divisor + it hs subhs \dividend, \dividend, \divisor mov \divisor, \divisor, lsr #1 3: cmp \dividend, \divisor + it hs subhs \dividend, \dividend, \divisor mov \divisor, \divisor, lsr #1 4: cmp \dividend, \divisor + it hs subhs \dividend, \dividend, \divisor 5: .endm @@ -209,6 +234,7 @@ ENTRY(__udivsi3) ENTRY(__aeabi_uidiv) subs r2, r1, #1 + it eq moveq pc, lr bcc Ldiv0 cmp r0, r1 @@ -221,7 +247,8 @@ ENTRY(__aeabi_uidiv) mov r0, r2 mov pc, lr -11: moveq r0, #1 +11: ite eq + moveq r0, #1 movne r0, #0 mov pc, lr @@ -237,10 +264,14 @@ ENTRY(__umodsi3) subs r2, r1, #1 @ compare divisor with 1 bcc Ldiv0 + ite ne cmpne r0, r1 @ compare dividend with divisor moveq r0, #0 + it hi tsthi r1, r2 @ see if divisor is power of 2 + it eq andeq r0, r0, r2 + it ls movls pc, lr ARM_MOD_BODY r0, r1, r2, r3 @@ -255,10 +286,12 @@ ENTRY(__aeabi_idiv) cmp r1, #0 eor ip, r0, r1 @ save the sign of the result. beq Ldiv0 + it mi rsbmi r1, r1, #0 @ loops below use unsigned. subs r2, r1, #1 @ division by 1 or -1 ? beq 10f movs r3, r0 + it mi rsbmi r3, r0, #0 @ positive dividend value cmp r3, r1 bls 11f @@ -268,14 +301,18 @@ ENTRY(__aeabi_idiv) ARM_DIV_BODY r3, r1, r0, r2 cmp ip, #0 + it mi rsbmi r0, r0, #0 mov pc, lr 10: teq ip, r0 @ same sign ? + it mi rsbmi r0, r0, #0 mov pc, lr -11: movlo r0, #0 +11: it lo + movlo r0, #0 + itt eq moveq r0, ip, asr #31 orreq r0, r0, #1 mov pc, lr @@ -284,6 +321,7 @@ ENTRY(__aeabi_idiv) cmp ip, #0 mov r0, r3, lsr r2 + it mi rsbmi r0, r0, #0 mov pc, lr @@ -294,19 +332,25 @@ ENTRY(__modsi3) cmp r1, #0 beq Ldiv0 + it mi rsbmi r1, r1, #0 @ loops below use unsigned. movs ip, r0 @ preserve sign of dividend + it mi rsbmi r0, r0, #0 @ if negative make positive subs r2, r1, #1 @ compare divisor with 1 + ite ne cmpne r0, r1 @ compare dividend with divisor moveq r0, #0 + it hi tsthi r1, r2 @ see if divisor is power of 2 + it eq andeq r0, r0, r2 bls 10f ARM_MOD_BODY r0, r1, r2, r3 10: cmp ip, #0 + it mi rsbmi r0, r0, #0 mov pc, lr diff --git a/arch/arm/lib/lshrdi3.S b/arch/arm/lib/lshrdi3.S index 99ea338bf87c..57db3a265e5b 100644 --- a/arch/arm/lib/lshrdi3.S +++ b/arch/arm/lib/lshrdi3.S @@ -41,9 +41,12 @@ ENTRY(__aeabi_llsr) subs r3, r2, #32 rsb ip, r2, #32 + itett mi movmi al, al, lsr r2 movpl al, ah, lsr r3 - orrmi al, al, ah, lsl ip + ARM( orrmi al, al, ah, lsl ip ) + THUMB( lslmi r3, ah, ip ) + THUMB( orrmi al, al, r3 ) mov ah, ah, lsr r2 mov pc, lr diff --git a/arch/arm/lib/memchr.S b/arch/arm/lib/memchr.S index 1da86991d700..0d1d596ad8cd 100644 --- a/arch/arm/lib/memchr.S +++ b/arch/arm/lib/memchr.S @@ -21,6 +21,7 @@ ENTRY(memchr) teq r3, r1 bne 1b sub r0, r0, #1 -2: movne r0, #0 +2: it ne + movne r0, #0 mov pc, lr ENDPROC(memchr) diff --git a/arch/arm/lib/memcpy.S b/arch/arm/lib/memcpy.S index e0d002641d3f..c7a810dee294 100644 --- a/arch/arm/lib/memcpy.S +++ b/arch/arm/lib/memcpy.S @@ -13,8 +13,11 @@ #include <linux/linkage.h> #include <asm/assembler.h> +#define LDR1W_SHIFT 0 +#define STR1W_SHIFT 0 + .macro ldr1w ptr reg abort - ldr \reg, [\ptr], #4 + W(ldr) \reg, [\ptr], #4 .endm .macro ldr4w ptr reg1 reg2 reg3 reg4 abort @@ -26,11 +29,16 @@ .endm .macro ldr1b ptr reg cond=al abort + .ifnc \cond,al + it \cond ldr\cond\()b \reg, [\ptr], #1 + .else + ldrb \reg, [\ptr], #1 + .endif .endm .macro str1w ptr reg abort - str \reg, [\ptr], #4 + W(str) \reg, [\ptr], #4 .endm .macro str8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort @@ -38,7 +46,12 @@ .endm .macro str1b ptr reg cond=al abort + .ifnc \cond,al + it \cond str\cond\()b \reg, [\ptr], #1 + .else + strb \reg, [\ptr], #1 + .endif .endm .macro enter reg1 reg2 diff --git a/arch/arm/lib/memmove.S b/arch/arm/lib/memmove.S index 12549187088c..191a5dc41596 100644 --- a/arch/arm/lib/memmove.S +++ b/arch/arm/lib/memmove.S @@ -29,7 +29,9 @@ ENTRY(memmove) subs ip, r0, r1 + it hi cmphi r2, ip + it ls bls memcpy stmfd sp!, {r0, r4, lr} @@ -72,46 +74,55 @@ ENTRY(memmove) 5: ands ip, r2, #28 rsb ip, ip, #32 + it ne addne pc, pc, ip @ C is always clear here b 7f 6: nop - ldr r3, [r1, #-4]! - ldr r4, [r1, #-4]! - ldr r5, [r1, #-4]! - ldr r6, [r1, #-4]! - ldr r7, [r1, #-4]! - ldr r8, [r1, #-4]! - ldr lr, [r1, #-4]! + W(ldr) r3, [r1, #-4]! + W(ldr) r4, [r1, #-4]! + W(ldr) r5, [r1, #-4]! + W(ldr) r6, [r1, #-4]! + W(ldr) r7, [r1, #-4]! + W(ldr) r8, [r1, #-4]! + W(ldr) lr, [r1, #-4]! add pc, pc, ip nop nop - str r3, [r0, #-4]! - str r4, [r0, #-4]! - str r5, [r0, #-4]! - str r6, [r0, #-4]! - str r7, [r0, #-4]! - str r8, [r0, #-4]! - str lr, [r0, #-4]! + W(str) r3, [r0, #-4]! + W(str) r4, [r0, #-4]! + W(str) r5, [r0, #-4]! + W(str) r6, [r0, #-4]! + W(str) r7, [r0, #-4]! + W(str) r8, [r0, #-4]! + W(str) lr, [r0, #-4]! CALGN( bcs 2b ) 7: ldmfd sp!, {r5 - r8} 8: movs r2, r2, lsl #31 + it ne ldrneb r3, [r1, #-1]! + itt cs ldrcsb r4, [r1, #-1]! ldrcsb ip, [r1, #-1] + it ne strneb r3, [r0, #-1]! + itt cs strcsb r4, [r0, #-1]! strcsb ip, [r0, #-1] ldmfd sp!, {r0, r4, pc} 9: cmp ip, #2 + it gt ldrgtb r3, [r1, #-1]! + it ge ldrgeb r4, [r1, #-1]! ldrb lr, [r1, #-1]! + it gt strgtb r3, [r0, #-1]! + it ge strgeb r4, [r0, #-1]! subs r2, r2, ip strb lr, [r0, #-1]! diff --git a/arch/arm/lib/memset.S b/arch/arm/lib/memset.S index 650d5923ab83..5e1253d4df5c 100644 --- a/arch/arm/lib/memset.S +++ b/arch/arm/lib/memset.S @@ -19,7 +19,9 @@ 1: subs r2, r2, #4 @ 1 do we have enough blt 5f @ 1 bytes to align with? cmp r3, #2 @ 1 + it lt strltb r1, [r0], #1 @ 1 + it le strleb r1, [r0], #1 @ 1 strb r1, [r0], #1 @ 1 add r2, r2, r3 @ 1 (r2 = r2 - (4 - r3)) @@ -51,19 +53,23 @@ ENTRY(memset) mov lr, r1 2: subs r2, r2, #64 + itttt ge stmgeia r0!, {r1, r3, ip, lr} @ 64 bytes at a time. stmgeia r0!, {r1, r3, ip, lr} stmgeia r0!, {r1, r3, ip, lr} stmgeia r0!, {r1, r3, ip, lr} bgt 2b + it eq ldmeqfd sp!, {pc} @ Now <64 bytes to go. /* * No need to correct the count; we're only testing bits from now on */ tst r2, #32 + itt ne stmneia r0!, {r1, r3, ip, lr} stmneia r0!, {r1, r3, ip, lr} tst r2, #16 + it ne stmneia r0!, {r1, r3, ip, lr} ldr lr, [sp], #4 @@ -111,17 +117,21 @@ ENTRY(memset) #endif 4: tst r2, #8 + it ne stmneia r0!, {r1, r3} tst r2, #4 + it ne strne r1, [r0], #4 /* * When we get here, we've got less than 4 bytes to zero. We * may have an unaligned pointer as well. */ 5: tst r2, #2 + itt ne strneb r1, [r0], #1 strneb r1, [r0], #1 tst r2, #1 + it ne strneb r1, [r0], #1 mov pc, lr ENDPROC(memset) diff --git a/arch/arm/lib/memzero.S b/arch/arm/lib/memzero.S index 3fbdef5f802a..a0e319a4c03f 100644 --- a/arch/arm/lib/memzero.S +++ b/arch/arm/lib/memzero.S @@ -21,7 +21,9 @@ 1: subs r1, r1, #4 @ 1 do we have enough blt 5f @ 1 bytes to align with? cmp r3, #2 @ 1 + it lt strltb r2, [r0], #1 @ 1 + it le strleb r2, [r0], #1 @ 1 strb r2, [r0], #1 @ 1 add r1, r1, r3 @ 1 (r1 = r1 - (4 - r3)) @@ -51,19 +53,23 @@ ENTRY(__memzero) mov lr, r2 @ 1 3: subs r1, r1, #64 @ 1 write 32 bytes out per loop + itttt ge stmgeia r0!, {r2, r3, ip, lr} @ 4 stmgeia r0!, {r2, r3, ip, lr} @ 4 stmgeia r0!, {r2, r3, ip, lr} @ 4 stmgeia r0!, {r2, r3, ip, lr} @ 4 bgt 3b @ 1 + it eq ldmeqfd sp!, {pc} @ 1/2 quick exit /* * No need to correct the count; we're only testing bits from now on */ tst r1, #32 @ 1 + itt ne stmneia r0!, {r2, r3, ip, lr} @ 4 stmneia r0!, {r2, r3, ip, lr} @ 4 tst r1, #16 @ 1 16 bytes or more? + it ne stmneia r0!, {r2, r3, ip, lr} @ 4 ldr lr, [sp], #4 @ 1 @@ -109,17 +115,21 @@ ENTRY(__memzero) #endif 4: tst r1, #8 @ 1 8 bytes or more? + it ne stmneia r0!, {r2, r3} @ 2 tst r1, #4 @ 1 4 bytes or more? + it ne strne r2, [r0], #4 @ 1 /* * When we get here, we've got less than 4 bytes to zero. We * may have an unaligned pointer as well. */ 5: tst r1, #2 @ 1 2 bytes or more? + itt ne strneb r2, [r0], #1 @ 1 strneb r2, [r0], #1 @ 1 tst r1, #1 @ 1 a byte left over + it ne strneb r2, [r0], #1 @ 1 mov pc, lr @ 1 ENDPROC(__memzero) diff --git a/arch/arm/lib/putuser.S b/arch/arm/lib/putuser.S index 864f3c1c4f18..4d9dc1a526ee 100644 --- a/arch/arm/lib/putuser.S +++ b/arch/arm/lib/putuser.S @@ -38,10 +38,16 @@ ENDPROC(__put_user_1) ENTRY(__put_user_2) mov ip, r2, lsr #8 #ifndef __ARMEB__ -2: strbt r2, [r0], #1 +2: + ARM( strbt r2, [r0], #1 ) + THUMB( strbt r2, [r0] ) + THUMB( add r0, #1 ) 3: strbt ip, [r0] #else -2: strbt ip, [r0], #1 +2: + ARM( strbt ip, [r0], #1 ) + THUMB( strbt ip, [r0] ) + THUMB( add r0, #1 ) 3: strbt r2, [r0] #endif mov r0, #0 @@ -55,7 +61,10 @@ ENTRY(__put_user_4) ENDPROC(__put_user_4) ENTRY(__put_user_8) -5: strt r2, [r0], #4 +5: + ARM( strt r2, [r0], #4 ) + THUMB( strt r2, [r0] ) + THUMB( add r0, #4 ) 6: strt r3, [r0] mov r0, #0 mov pc, lr diff --git a/arch/arm/lib/sha1.S b/arch/arm/lib/sha1.S index a16fb208c841..09b548cac1a4 100644 --- a/arch/arm/lib/sha1.S +++ b/arch/arm/lib/sha1.S @@ -187,6 +187,7 @@ ENTRY(sha_transform) ENDPROC(sha_transform) + .align 2 .L_sha_K: .word 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 @@ -195,6 +196,7 @@ ENDPROC(sha_transform) * void sha_init(__u32 *buf) */ + .align 2 .L_sha_initial_digest: .word 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 diff --git a/arch/arm/lib/strchr.S b/arch/arm/lib/strchr.S index d8f2a1c1aea4..fd4014e54e37 100644 --- a/arch/arm/lib/strchr.S +++ b/arch/arm/lib/strchr.S @@ -18,9 +18,11 @@ ENTRY(strchr) and r1, r1, #0xff 1: ldrb r2, [r0], #1 teq r2, r1 + it ne teqne r2, #0 bne 1b teq r2, r1 + ite ne movne r0, #0 subeq r0, r0, #1 mov pc, lr diff --git a/arch/arm/lib/strncpy_from_user.S b/arch/arm/lib/strncpy_from_user.S index 330373c26dd9..1c9814f346c6 100644 --- a/arch/arm/lib/strncpy_from_user.S +++ b/arch/arm/lib/strncpy_from_user.S @@ -23,7 +23,7 @@ ENTRY(__strncpy_from_user) mov ip, r1 1: subs r2, r2, #1 -USER( ldrplbt r3, [r1], #1) + ldrusr r3, r1, 1, pl bmi 2f strb r3, [r0], #1 teq r3, #0 diff --git a/arch/arm/lib/strnlen_user.S b/arch/arm/lib/strnlen_user.S index 90bb9d020836..7855b2906659 100644 --- a/arch/arm/lib/strnlen_user.S +++ b/arch/arm/lib/strnlen_user.S @@ -23,7 +23,7 @@ ENTRY(__strnlen_user) mov r2, r0 1: -USER( ldrbt r3, [r0], #1) + ldrusr r3, r0, 1 teq r3, #0 beq 2f subs r1, r1, #1 diff --git a/arch/arm/lib/strrchr.S b/arch/arm/lib/strrchr.S index 302f20cd2423..d7a9440de6b8 100644 --- a/arch/arm/lib/strrchr.S +++ b/arch/arm/lib/strrchr.S @@ -18,6 +18,7 @@ ENTRY(strrchr) mov r3, #0 1: ldrb r2, [r0], #1 teq r2, r1 + it eq subeq r3, r0, #1 teq r2, #0 bne 1b diff --git a/arch/arm/lib/testclearbit.S b/arch/arm/lib/testclearbit.S index 543d7094d18e..df66c76e8b29 100644 --- a/arch/arm/lib/testclearbit.S +++ b/arch/arm/lib/testclearbit.S @@ -15,6 +15,6 @@ ENTRY(_test_and_clear_bit_be) eor r0, r0, #0x18 @ big endian byte ordering ENTRY(_test_and_clear_bit_le) - testop bicne, strneb + testop bicne, strneb, ne ENDPROC(_test_and_clear_bit_be) ENDPROC(_test_and_clear_bit_le) diff --git a/arch/arm/lib/testsetbit.S b/arch/arm/lib/testsetbit.S index 0b3f390401ce..3938bdf446a6 100644 --- a/arch/arm/lib/testsetbit.S +++ b/arch/arm/lib/testsetbit.S @@ -15,6 +15,6 @@ ENTRY(_test_and_set_bit_be) eor r0, r0, #0x18 @ big endian byte ordering ENTRY(_test_and_set_bit_le) - testop orreq, streqb + testop orreq, streqb, eq ENDPROC(_test_and_set_bit_be) ENDPROC(_test_and_set_bit_le) diff --git a/arch/arm/lib/ucmpdi2.S b/arch/arm/lib/ucmpdi2.S index f0df6a91db04..503288955242 100644 --- a/arch/arm/lib/ucmpdi2.S +++ b/arch/arm/lib/ucmpdi2.S @@ -27,9 +27,13 @@ ENTRY(__ucmpdi2) cmp xh, yh + it eq cmpeq xl, yl + it lo movlo r0, #0 + it eq moveq r0, #1 + it hi movhi r0, #2 mov pc, lr @@ -40,9 +44,13 @@ ENDPROC(__ucmpdi2) ENTRY(__aeabi_ulcmp) cmp xh, yh + it eq cmpeq xl, yl + it lo movlo r0, #-1 + it eq moveq r0, #0 + it hi movhi r0, #1 mov pc, lr diff --git a/arch/arm/mach-integrator/include/mach/debug-macro.S b/arch/arm/mach-integrator/include/mach/debug-macro.S index d347d659ea30..ffb7d8961f5e 100644 --- a/arch/arm/mach-integrator/include/mach/debug-macro.S +++ b/arch/arm/mach-integrator/include/mach/debug-macro.S @@ -14,6 +14,7 @@ .macro addruart,rx mrc p15, 0, \rx, c1, c0 tst \rx, #1 @ MMU enabled? + itee eq moveq \rx, #0x16000000 @ physical base address movne \rx, #0xf0000000 @ virtual base addne \rx, \rx, #0x16000000 >> 4 diff --git a/arch/arm/mach-integrator/include/mach/entry-macro.S b/arch/arm/mach-integrator/include/mach/entry-macro.S index 7649c57acb53..ce478b5bfb90 100644 --- a/arch/arm/mach-integrator/include/mach/entry-macro.S +++ b/arch/arm/mach-integrator/include/mach/entry-macro.S @@ -26,6 +26,7 @@ ldr \irqstat, [\base, #IRQ_STATUS] @ get masked status ldr \base, =IO_ADDRESS(INTEGRATOR_HDR_BASE) teq \irqstat, #0 + itt eq ldreq \irqstat, [\base, #(INTEGRATOR_HDR_IC_OFFSET+IRQ_STATUS)] moveq \irqnr, #IRQ_CIC_START diff --git a/arch/arm/mach-integrator/include/mach/hardware.h b/arch/arm/mach-integrator/include/mach/hardware.h index 1251319ef9ae..d795642fad22 100644 --- a/arch/arm/mach-integrator/include/mach/hardware.h +++ b/arch/arm/mach-integrator/include/mach/hardware.h @@ -36,8 +36,12 @@ #define PCIO_BASE PCI_IO_VADDR #define PCIMEM_BASE PCI_MEMORY_VADDR +#ifdef CONFIG_MMU /* macro to get at IO space when running virtually */ #define IO_ADDRESS(x) (((x) >> 4) + IO_BASE) +#else +#define IO_ADDRESS(x) (x) +#endif #define pcibios_assign_all_busses() 1 diff --git a/arch/arm/mach-integrator/integrator_cp.c b/arch/arm/mach-integrator/integrator_cp.c index 4ac04055c2ea..452931b2690e 100644 --- a/arch/arm/mach-integrator/integrator_cp.c +++ b/arch/arm/mach-integrator/integrator_cp.c @@ -49,14 +49,14 @@ #define INTCP_PA_CLCD_BASE 0xc0000000 -#define INTCP_VA_CIC_BASE 0xf1000040 -#define INTCP_VA_PIC_BASE 0xf1400000 -#define INTCP_VA_SIC_BASE 0xfca00000 +#define INTCP_VA_CIC_BASE IO_ADDRESS(INTEGRATOR_HDR_BASE) + 0x40 +#define INTCP_VA_PIC_BASE IO_ADDRESS(INTEGRATOR_IC_BASE) +#define INTCP_VA_SIC_BASE IO_ADDRESS(0xca000000) #define INTCP_PA_ETH_BASE 0xc8000000 #define INTCP_ETH_SIZE 0x10 -#define INTCP_VA_CTRL_BASE 0xfcb00000 +#define INTCP_VA_CTRL_BASE IO_ADDRESS(0xcb000000) #define INTCP_FLASHPROG 0x04 #define CINTEGRATOR_FLASHPROG_FLVPPEN (1 << 0) #define CINTEGRATOR_FLASHPROG_FLWREN (1 << 1) @@ -121,12 +121,12 @@ static struct map_desc intcp_io_desc[] __initdata = { .length = SZ_4K, .type = MT_DEVICE }, { - .virtual = 0xfca00000, + .virtual = IO_ADDRESS(0xca000000), .pfn = __phys_to_pfn(0xca000000), .length = SZ_4K, .type = MT_DEVICE }, { - .virtual = 0xfcb00000, + .virtual = IO_ADDRESS(0xcb000000), .pfn = __phys_to_pfn(0xcb000000), .length = SZ_4K, .type = MT_DEVICE @@ -394,8 +394,8 @@ static struct platform_device *intcp_devs[] __initdata = { */ static unsigned int mmc_status(struct device *dev) { - unsigned int status = readl(0xfca00004); - writel(8, 0xfcb00008); + unsigned int status = readl(IO_ADDRESS(0xca000000) + 4); + writel(8, IO_ADDRESS(0xcb000000) + 8); return status & 8; } diff --git a/arch/arm/mach-mps/Kconfig b/arch/arm/mach-mps/Kconfig new file mode 100644 index 000000000000..b5c1651eec37 --- /dev/null +++ b/arch/arm/mach-mps/Kconfig @@ -0,0 +1,11 @@ +if ARCH_MPS + +config MACH_MPS + bool + default y + select ARM_NVIC if CPU_V7M + help + Include support for the ARM Ltd. Microcontroller Prototyping + System platform. + +endif diff --git a/arch/arm/mach-mps/Makefile b/arch/arm/mach-mps/Makefile new file mode 100644 index 000000000000..38bde634fc84 --- /dev/null +++ b/arch/arm/mach-mps/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the linux kernel. +# + +obj-y := core.o clock.o +obj-$(CONFIG_MACH_MPS) += board-mps.o diff --git a/arch/arm/mach-mps/Makefile.boot b/arch/arm/mach-mps/Makefile.boot new file mode 100644 index 000000000000..8842bf950bdd --- /dev/null +++ b/arch/arm/mach-mps/Makefile.boot @@ -0,0 +1,3 @@ + zreladdr-y := 0x10008000 +params_phys-y := 0x10000100 +initrd_phys-y := 0x10800000 diff --git a/arch/arm/mach-mps/board-mps.c b/arch/arm/mach-mps/board-mps.c new file mode 100644 index 000000000000..03c1544893a5 --- /dev/null +++ b/arch/arm/mach-mps/board-mps.c @@ -0,0 +1,191 @@ +/* + * linux/arch/arm/mach-mps/board_mps.c + * + * Copyright (C) 2009 ARM Limited + * Copyright (C) 2000 Deep Blue Solutions Ltd + * + * 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/init.h> +#include <linux/platform_device.h> +#include <linux/sysdev.h> +#include <linux/amba/bus.h> + +#include <mach/hardware.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/leds.h> +#include <asm/mach-types.h> +#include <asm/hardware/gic.h> +#include <asm/hardware/nvic.h> +#include <asm/hardware/icst307.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/mmc.h> +#include <asm/mach/time.h> + +#include <mach/platform.h> +#include <mach/irqs.h> + +#include "core.h" +#include "clock.h" + +#if PAGE_OFFSET != PHYS_OFFSET +#error "PAGE_OFFSET != PHYS_OFFSET" +#endif + +static void __init mps_map_io(void) +{ +} + +/* + * MPS AMBA devices + */ + +#define GPIO2_IRQ { IRQ_MPS_GPIO2, NO_IRQ } +#define GPIO3_IRQ { IRQ_MPS_GPIO3, NO_IRQ } + +#define AACI_IRQ { IRQ_MPS_AACI, NO_IRQ } +#define MMCI_IRQ { IRQ_MPS_MMCIA, IRQ_MPS_MMCIB } + +#define SMC_IRQ { NO_IRQ, NO_IRQ } +#define MPMC_IRQ { NO_IRQ, NO_IRQ } +#define CLCD_IRQ { IRQ_MPS_CLCD, NO_IRQ } + +#define SCTL_IRQ { NO_IRQ, NO_IRQ } +#define WATCHDOG_IRQ { IRQ_MPS_WDOG, NO_IRQ } +#define GPIO0_IRQ { IRQ_MPS_GPIO0, NO_IRQ } +#define GPIO1_IRQ { IRQ_MPS_GPIO1, NO_IRQ } +#define RTC_IRQ { IRQ_MPS_RTC, NO_IRQ } + +#define UART0_IRQ { IRQ_MPS_UART0, NO_IRQ } +#define UART1_IRQ { IRQ_MPS_UART1, NO_IRQ } +#define UART2_IRQ { IRQ_MPS_UART2, NO_IRQ } +#define UART3_IRQ { IRQ_MPS_UART3, NO_IRQ } +#define SPI_IRQ { IRQ_MPS_SPI, NO_IRQ } + +/* FPGA Primecells */ +AMBA_DEVICE(aaci, "fpga:04", AACI, NULL); +AMBA_DEVICE(mmc0, "fpga:05", MMCI, &mps_mmc0_plat_data); +AMBA_DEVICE(uart3, "fpga:09", UART3, NULL); + +/* DevChip Primecells */ +AMBA_DEVICE(smc, "dev:00", SMC, NULL); +AMBA_DEVICE(clcd, "dev:20", CLCD, &clcd_plat_data); +AMBA_DEVICE(sctl, "dev:e0", SCTL, NULL); +AMBA_DEVICE(wdog, "dev:e1", WATCHDOG, NULL); +AMBA_DEVICE(rtc, "dev:e8", RTC, NULL); +AMBA_DEVICE(uart0, "dev:f1", UART0, NULL); +AMBA_DEVICE(uart1, "dev:f2", UART1, NULL); +AMBA_DEVICE(uart2, "dev:f3", UART2, NULL); +AMBA_DEVICE(spi, "dev:f4", SPI, NULL); + +static struct amba_device *amba_devs[] __initdata = { + &mmc0_device, + &uart0_device, + &uart1_device, + &uart2_device, + &uart3_device, + &smc_device, + &clcd_device, + &sctl_device, + &wdog_device, + &rtc_device, + &spi_device, + &aaci_device, +}; + +/* + * MPS platform devices + */ +static struct resource mps_flash_resource = { + .start = MPS_FLASH_BASE, + .end = MPS_FLASH_BASE + MPS_FLASH_SIZE - 1, + .flags = IORESOURCE_MEM, +}; + +static struct resource mps_eth_resources[] = { + [0] = { + .start = MPS_ETH_BASE, + .end = MPS_ETH_BASE + SZ_64K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_MPS_ETH, + .end = IRQ_MPS_ETH, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device mps_eth_device = { + .name = "smsc911x", + .id = 0, + .num_resources = ARRAY_SIZE(mps_eth_resources), + .resource = mps_eth_resources, +}; + +static void __init gic_init_irq(void) +{ + nvic_init(); +} + +static void __init timer_init(void) +{ + unsigned int timer_irq; + + timer0_va_base = __io_address(MPS_TIMER0_1_BASE); + timer1_va_base = __io_address(MPS_TIMER0_1_BASE) + 0x20; + timer2_va_base = __io_address(MPS_TIMER2_3_BASE); + timer3_va_base = __io_address(MPS_TIMER2_3_BASE) + 0x20; + + timer_irq = IRQ_MPS_TIMER0_1; + + mps_timer_init(timer_irq); +} + +static struct sys_timer mps_timer = { + .init = timer_init, +}; + +static void __init mps_init(void) +{ + int i; + clk_register(&mps_clcd_clk); + + mps_flash_register(&mps_flash_resource, 1); + platform_device_register(&mps_i2c_device); + platform_device_register(&mps_eth_device); + + for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { + struct amba_device *d = amba_devs[i]; + amba_device_register(d, &iomem_resource); + } + +#ifdef CONFIG_LEDS + leds_event = mps_leds_event; +#endif +} + +MACHINE_START(MPS, "ARM MPS") + .phys_io = MPS_UART0_BASE, + .io_pg_offst = (IO_ADDRESS(MPS_UART0_BASE) >> 18) & 0xfffc, + .boot_params = PHYS_OFFSET + 0x100, + .map_io = mps_map_io, + .init_irq = gic_init_irq, + .timer = &mps_timer, + .init_machine = mps_init, +MACHINE_END diff --git a/arch/arm/mach-mps/clock.c b/arch/arm/mach-mps/clock.c new file mode 100644 index 000000000000..d459efc2c2e2 --- /dev/null +++ b/arch/arm/mach-mps/clock.c @@ -0,0 +1,131 @@ +/* + * linux/arch/arm/mach-mps/clock.c + * + * Copyright (C) 2004 ARM Limited. + * Written by Deep Blue Solutions Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/mutex.h> + +#include <asm/hardware/icst307.h> + +#include "clock.h" + +static LIST_HEAD(clocks); +static DEFINE_MUTEX(clocks_mutex); + +struct clk *clk_get(struct device *dev, const char *id) +{ + struct clk *p, *clk = ERR_PTR(-ENOENT); + + mutex_lock(&clocks_mutex); + list_for_each_entry(p, &clocks, node) { + if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) { + clk = p; + break; + } + } + mutex_unlock(&clocks_mutex); + + return clk; +} +EXPORT_SYMBOL(clk_get); + +void clk_put(struct clk *clk) +{ + module_put(clk->owner); +} +EXPORT_SYMBOL(clk_put); + +int clk_enable(struct clk *clk) +{ + return 0; +} +EXPORT_SYMBOL(clk_enable); + +void clk_disable(struct clk *clk) +{ +} +EXPORT_SYMBOL(clk_disable); + +unsigned long clk_get_rate(struct clk *clk) +{ + return clk->rate; +} +EXPORT_SYMBOL(clk_get_rate); + +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + return rate; +} +EXPORT_SYMBOL(clk_round_rate); + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + int ret = -EIO; + + if (clk->setvco) { + struct icst307_vco vco; + + vco = icst307_khz_to_vco(clk->params, rate / 1000); + clk->rate = icst307_khz(clk->params, vco) * 1000; + + clk->setvco(clk, vco); + ret = 0; + } + return ret; +} +EXPORT_SYMBOL(clk_set_rate); + +/* + * These are fixed clocks. + */ +static struct clk kmi_clk = { + .name = "KMIREFCLK", + .rate = 24000000, +}; + +static struct clk uart_clk = { + .name = "UARTCLK", + .rate = 24000000, +}; + +static struct clk mmci_clk = { + .name = "MCLK", + .rate = 24000000, +}; + +int clk_register(struct clk *clk) +{ + mutex_lock(&clocks_mutex); + list_add(&clk->node, &clocks); + mutex_unlock(&clocks_mutex); + return 0; +} +EXPORT_SYMBOL(clk_register); + +void clk_unregister(struct clk *clk) +{ + mutex_lock(&clocks_mutex); + list_del(&clk->node); + mutex_unlock(&clocks_mutex); +} +EXPORT_SYMBOL(clk_unregister); + +static int __init clk_init(void) +{ + clk_register(&kmi_clk); + clk_register(&uart_clk); + clk_register(&mmci_clk); + return 0; +} +arch_initcall(clk_init); diff --git a/arch/arm/mach-mps/clock.h b/arch/arm/mach-mps/clock.h new file mode 100644 index 000000000000..dadba695e181 --- /dev/null +++ b/arch/arm/mach-mps/clock.h @@ -0,0 +1,25 @@ +/* + * linux/arch/arm/mach-realview/clock.h + * + * Copyright (C) 2004 ARM Limited. + * Written by Deep Blue Solutions Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +struct module; +struct icst307_params; + +struct clk { + struct list_head node; + unsigned long rate; + struct module *owner; + const char *name; + const struct icst307_params *params; + void *data; + void (*setvco)(struct clk *, struct icst307_vco vco); +}; + +int clk_register(struct clk *clk); +void clk_unregister(struct clk *clk); diff --git a/arch/arm/mach-mps/core.c b/arch/arm/mach-mps/core.c new file mode 100644 index 000000000000..090b5fe2fa56 --- /dev/null +++ b/arch/arm/mach-mps/core.c @@ -0,0 +1,386 @@ +/* + * linux/arch/arm/mach-mps/core.c + * + * Copyright (C) 2009 ARM Limited + * + * 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/init.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/sysdev.h> +#include <linux/interrupt.h> +#include <linux/amba/bus.h> +#include <linux/amba/clcd.h> +#include <linux/clocksource.h> +#include <linux/clockchips.h> +#include <linux/io.h> +#include <linux/smsc911x.h> +#include <linux/ata_platform.h> +#include <linux/delay.h> + +#include <asm/system.h> +#include <asm/irq.h> +#include <asm/leds.h> +#include <asm/mach-types.h> +#include <asm/hardware/arm_timer.h> +#include <asm/hardware/icst307.h> + +#include <asm/mach/arch.h> +#include <asm/mach/flash.h> +#include <asm/mach/irq.h> +#include <asm/mach/map.h> +#include <asm/mach/mmc.h> + +#include <mach/platform.h> +#include <mach/hardware.h> + +#include <asm/hardware/gic.h> + +#include "clock.h" +#include "core.h" + +#define MPS_REFCOUNTER (__io_address(MPS_SYS_BASE) + MPS_SYS_CNT25MHz_OFFSET) + +/* + * This is the MPS sched_clock implementation. This has a resolution of 40ns. + */ +unsigned long long sched_clock(void) +{ + return (unsigned long long)readl(MPS_REFCOUNTER) * 40; +} + +#define MPS_FLASHCTRL (__io_address(MPS_SYS_BASE) + MPS_SYS_FLASH_OFFSET) + +static struct flash_platform_data mps_flash_data = { + .map_name = "cfi_probe", + .width = 4, +}; + +struct platform_device mps_flash_device = { + .name = "armflash", + .id = 0, + .dev = { + .platform_data = &mps_flash_data, + }, +}; + +int mps_flash_register(struct resource *res, u32 num) +{ + mps_flash_device.resource = res; + mps_flash_device.num_resources = num; + return platform_device_register(&mps_flash_device); +} + +static struct smsc911x_platform_config smsc911x_config = { + .flags = SMSC911X_USE_32BIT, + .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_HIGH, + .irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL, + .phy_interface = PHY_INTERFACE_MODE_MII, +}; + +static struct platform_device mps_eth_device = { + .name = "smsc911x", + .id = 0, + .num_resources = 2, +}; + +int mps_eth_register(const char *name, struct resource *res) +{ + if (name) + mps_eth_device.name = name; + mps_eth_device.resource = res; + if (strcmp(mps_eth_device.name, "smsc911x") == 0) + mps_eth_device.dev.platform_data = &smsc911x_config; + + return platform_device_register(&mps_eth_device); +} + +struct platform_device mps_usb_device = { + .name = "isp1760", + .num_resources = 2, +}; + +int mps_usb_register(struct resource *res) +{ + mps_usb_device.resource = res; + return platform_device_register(&mps_usb_device); +} + +static struct resource mps_i2c_resource = { + .start = MPS_I2C_BASE, + .end = MPS_I2C_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, +}; + +struct platform_device mps_i2c_device = { + .name = "versatile-i2c", + .id = 0, + .num_resources = 1, + .resource = &mps_i2c_resource, +}; + +static struct i2c_board_info mps_i2c_board_info[] = { + { + I2C_BOARD_INFO("rtc-ds1307", 0xd0 >> 1), + .type = "ds1338", + }, +}; + +static int __init mps_i2c_init(void) +{ + return i2c_register_board_info(0, mps_i2c_board_info, + ARRAY_SIZE(mps_i2c_board_info)); +} +arch_initcall(mps_i2c_init); + +static unsigned int mps_mmc_status(struct device *dev) +{ + return readl(__io_address(MPS_MMCI_BASE)) & 1; +} + +struct mmc_platform_data mps_mmc0_plat_data = { + .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, + .status = mps_mmc_status, +}; + +struct clk mps_clcd_clk = { + .name = "CLCDCLK", +}; + +static struct clcd_panel vga = { + .mode = { + .name = "VGA", + .refresh = 60, + .xres = 640, + .yres = 480, + .pixclock = 39721, + .left_margin = 64, + .right_margin = 16, + .upper_margin = 13, + .lower_margin = 3, + .hsync_len = 80, + .vsync_len = 4, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED, + }, + .width = -1, + .height = -1, + .tim2 = TIM2_BCD | TIM2_IPC, + .cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP(1), + .bpp = 16, +}; + +static int mps_clcd_setup(struct clcd_fb *fb) +{ + fb->panel = &vga; + fb->fb.fix.smem_start = (unsigned long)MPS_DMC_BASE; + fb->fb.screen_base = (char __iomem *)MPS_DMC_BASE; + fb->fb.fix.smem_len = 640 * 480 * 2; /* VGA, 16bpp */ + + return 0; +} + +static int mps_clcd_mmap(struct clcd_fb *fb, struct vm_area_struct *vma) +{ + return dma_mmap_writecombine(&fb->dev->dev, vma, + fb->fb.screen_base, + fb->fb.fix.smem_start, + fb->fb.fix.smem_len); +} + +static void mps_clcd_remove(struct clcd_fb *fb) +{ +} + +struct clcd_board clcd_plat_data = { + .name = "MPS", + .check = clcdfb_check, + .decode = clcdfb_decode, + .setup = mps_clcd_setup, + .mmap = mps_clcd_mmap, + .remove = mps_clcd_remove, +}; + +/* + * Where is the timer (VA)? + */ +void __iomem *timer0_va_base; +void __iomem *timer1_va_base; +void __iomem *timer2_va_base; +void __iomem *timer3_va_base; + +/* + * How long is the timer interval? + */ +#define TIMER_INTERVAL (TICKS_PER_uSEC * mSEC_10) +#if TIMER_INTERVAL >= 0x100000 +#define TIMER_RELOAD (TIMER_INTERVAL >> 8) +#define TIMER_DIVISOR (TIMER_CTRL_DIV256) +#define TICKS2USECS(x) (256 * (x) / TICKS_PER_uSEC) +#elif TIMER_INTERVAL >= 0x10000 +#define TIMER_RELOAD (TIMER_INTERVAL >> 4) /* Divide by 16 */ +#define TIMER_DIVISOR (TIMER_CTRL_DIV16) +#define TICKS2USECS(x) (16 * (x) / TICKS_PER_uSEC) +#else +#define TIMER_RELOAD (TIMER_INTERVAL) +#define TIMER_DIVISOR (TIMER_CTRL_DIV1) +#define TICKS2USECS(x) ((x) / TICKS_PER_uSEC) +#endif + +static void timer_set_mode(enum clock_event_mode mode, + struct clock_event_device *clk) +{ + unsigned long ctrl; + + switch(mode) { + case CLOCK_EVT_MODE_PERIODIC: + writel(TIMER_RELOAD, timer0_va_base + TIMER_LOAD); + + ctrl = TIMER_CTRL_PERIODIC; + ctrl |= TIMER_CTRL_32BIT | TIMER_CTRL_IE | TIMER_CTRL_ENABLE; + break; + case CLOCK_EVT_MODE_ONESHOT: + /* period set, and timer enabled in 'next_event' hook */ + ctrl = TIMER_CTRL_ONESHOT; + ctrl |= TIMER_CTRL_32BIT | TIMER_CTRL_IE; + break; + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + default: + ctrl = 0; + } + + writel(ctrl, timer0_va_base + TIMER_CTRL); +} + +static int timer_set_next_event(unsigned long evt, + struct clock_event_device *unused) +{ + unsigned long ctrl = readl(timer0_va_base + TIMER_CTRL); + + writel(evt, timer0_va_base + TIMER_LOAD); + writel(ctrl | TIMER_CTRL_ENABLE, timer0_va_base + TIMER_CTRL); + + return 0; +} + +static struct clock_event_device timer0_clockevent = { + .name = "timer0", + .shift = 32, + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_mode = timer_set_mode, + .set_next_event = timer_set_next_event, + .rating = 300, + .cpumask = CPU_MASK_ALL, +}; + +static void __init mps_clockevents_init(unsigned int timer_irq) +{ + timer0_clockevent.irq = timer_irq; + timer0_clockevent.mult = + div_sc(1000000, NSEC_PER_SEC, timer0_clockevent.shift); + timer0_clockevent.max_delta_ns = + clockevent_delta2ns(0xffffffff, &timer0_clockevent); + timer0_clockevent.min_delta_ns = + clockevent_delta2ns(0xf, &timer0_clockevent); + + clockevents_register_device(&timer0_clockevent); +} + +/* + * IRQ handler for the timer + */ +static irqreturn_t mps_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = &timer0_clockevent; + + /* clear the interrupt */ + writel(1, timer0_va_base + TIMER_INTCLR); + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static struct irqaction mps_timer_irq = { + .name = "MPS Timer Tick", + .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, + .handler = mps_timer_interrupt, +}; + +static cycle_t mps_get_cycles(void) +{ + return ~readl(timer3_va_base + TIMER_VALUE); +} + +static struct clocksource clocksource_mps = { + .name = "timer3", + .rating = 200, + .read = mps_get_cycles, + .mask = CLOCKSOURCE_MASK(32), + .shift = 20, + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static void __init mps_clocksource_init(void) +{ + /* setup timer 0 as free-running clocksource */ + writel(0, timer3_va_base + TIMER_CTRL); + writel(0xffffffff, timer3_va_base + TIMER_LOAD); + writel(0xffffffff, timer3_va_base + TIMER_VALUE); + writel(TIMER_CTRL_32BIT | TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC, + timer3_va_base + TIMER_CTRL); + + clocksource_mps.mult = + clocksource_khz2mult(1000, clocksource_mps.shift); + clocksource_register(&clocksource_mps); +} + +/* + * Set up the clock source and clock events devices + */ +void __init mps_timer_init(unsigned int timer_irq) +{ + u32 val; + + /* + * set clock frequency: + * MPS_REFCLK is 32KHz + * MPS_TIMCLK is 1MHz + */ + val = readl(__io_address(MPS_SCTL_BASE)); + writel((MPS_TIMCLK << MPS_TIMER1_EnSel) | + (MPS_TIMCLK << MPS_TIMER2_EnSel) | + (MPS_TIMCLK << MPS_TIMER3_EnSel) | + (MPS_TIMCLK << MPS_TIMER4_EnSel) | val, + __io_address(MPS_SCTL_BASE)); + + /* + * Initialise to a known state (all timers off) + */ + writel(0, timer0_va_base + TIMER_CTRL); + writel(0, timer1_va_base + TIMER_CTRL); + writel(0, timer2_va_base + TIMER_CTRL); + writel(0, timer3_va_base + TIMER_CTRL); + + /* + * Make irqs happen for the system timer + */ + setup_irq(timer_irq, &mps_timer_irq); + + mps_clocksource_init(); + mps_clockevents_init(timer_irq); +} diff --git a/arch/arm/mach-mps/core.h b/arch/arm/mach-mps/core.h new file mode 100644 index 000000000000..051a994acb0d --- /dev/null +++ b/arch/arm/mach-mps/core.h @@ -0,0 +1,69 @@ +/* + * linux/arch/arm/mach-mps/core.h + * + * Copyright (C) 2004 ARM Limited + * Copyright (C) 2000 Deep Blue Solutions Ltd + * + * 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_ARCH_MPS_H +#define __ASM_ARCH_MPS_H + +#include <linux/amba/bus.h> +#include <linux/io.h> + +#include <asm/leds.h> + +#define AMBA_DEVICE(name,busid,base,plat) \ +static struct amba_device name##_device = { \ + .dev = { \ + .coherent_dma_mask = ~0, \ + .bus_id = busid, \ + .platform_data = plat, \ + }, \ + .res = { \ + .start = MPS_##base##_BASE, \ + .end = (MPS_##base##_BASE) + SZ_4K - 1, \ + .flags = IORESOURCE_MEM, \ + }, \ + .dma_mask = ~0, \ + .irq = base##_IRQ, \ + /* .dma = base##_DMA,*/ \ +} + +extern struct platform_device mps_flash_device; +extern struct platform_device mps_cf_device; +extern struct platform_device mps_i2c_device; +extern struct mmc_platform_data mps_mmc0_plat_data; +extern struct mmc_platform_data mps_mmc1_plat_data; +extern struct clk mps_clcd_clk; +extern struct clcd_board clcd_plat_data; +extern void __iomem *gic_cpu_base_addr; +#ifdef CONFIG_LOCAL_TIMERS +extern void __iomem *twd_base; +#endif +extern void __iomem *timer0_va_base; +extern void __iomem *timer1_va_base; +extern void __iomem *timer2_va_base; +extern void __iomem *timer3_va_base; + +extern void mps_leds_event(led_event_t ledevt); +extern void mps_timer_init(unsigned int timer_irq); +extern int mps_flash_register(struct resource *res, u32 num); +extern int mps_eth_register(const char *name, struct resource *res); +extern int mps_usb_register(struct resource *res); + +#endif diff --git a/arch/arm/mach-mps/include/mach/debug-macro.S b/arch/arm/mach-mps/include/mach/debug-macro.S new file mode 100644 index 000000000000..eb693a1e672a --- /dev/null +++ b/arch/arm/mach-mps/include/mach/debug-macro.S @@ -0,0 +1,24 @@ +/* + * arch/arm/mach-mps/include/mach/debug-macro.S + * + * Debugging macro include header + * + * Copyright (C) 2009 ARM Ltd. + * Copyright (C) 1994-1999 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + + .macro addruart,rx +#if 0 + mov \rx, #0x40000000 + orr \rx, \rx, #0x00006000 +#else + mov \rx, #0x1f000000 + orr \rx, \rx, #0x00005000 +#endif + .endm + +#include <asm/hardware/debug-pl01x.S> diff --git a/arch/arm/mach-mps/include/mach/dma.h b/arch/arm/mach-mps/include/mach/dma.h new file mode 100644 index 000000000000..291143e64bf1 --- /dev/null +++ b/arch/arm/mach-mps/include/mach/dma.h @@ -0,0 +1,18 @@ +/* + * arch/arm/mach-mps/include/mach/dma.h + * + * Copyright (C) 2009 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 + */ diff --git a/arch/arm/mach-mps/include/mach/entry-macro.S b/arch/arm/mach-mps/include/mach/entry-macro.S new file mode 100644 index 000000000000..a531a4ca9100 --- /dev/null +++ b/arch/arm/mach-mps/include/mach/entry-macro.S @@ -0,0 +1,21 @@ +/* + * arch/arm/mach-mps/include/mach/entry-macro.S + * + * Copyright (C) 2009 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 + */ + + .macro arch_ret_to_user, tmp1, tmp2 + .endm diff --git a/arch/arm/mach-mps/include/mach/hardware.h b/arch/arm/mach-mps/include/mach/hardware.h new file mode 100644 index 000000000000..c251699ca806 --- /dev/null +++ b/arch/arm/mach-mps/include/mach/hardware.h @@ -0,0 +1,31 @@ +/* + * arch/arm/mach-mps/include/mach/hardware.h + * + * Copyright (C) 2009 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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_ARCH_HARDWARE_H +#define __ASM_ARCH_HARDWARE_H + +#include <asm/sizes.h> + +#ifdef CONFIG_MMU +#error "ARM MPS platform only support !MMU" +#endif + +#define IO_ADDRESS(x) (x) +#define __io_address(n) __io(IO_ADDRESS(n)) + +#endif diff --git a/arch/arm/mach-mps/include/mach/io.h b/arch/arm/mach-mps/include/mach/io.h new file mode 100644 index 000000000000..583850e08d91 --- /dev/null +++ b/arch/arm/mach-mps/include/mach/io.h @@ -0,0 +1,32 @@ +/* + * arch/arm/mach-mps/include/mach/io.h + * + * Copyright (C) 2009 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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_ARCH_IO_H +#define __ASM_ARM_ARCH_IO_H + +#define IO_SPACE_LIMIT 0xffffffff + +static inline void __iomem *__io(unsigned long addr) +{ + return (void __iomem *)addr; +} + +#define __io(a) __io(a) +#define __mem_pci(a) (a) + +#endif diff --git a/arch/arm/mach-mps/include/mach/irqs.h b/arch/arm/mach-mps/include/mach/irqs.h new file mode 100644 index 000000000000..eb73a8acf7e7 --- /dev/null +++ b/arch/arm/mach-mps/include/mach/irqs.h @@ -0,0 +1,49 @@ +/* + * arch/arm/mach-mps/include/mach/irqs.h + * + * Copyright (C) 2009 ARM Ltd. + * + * 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_ARCH_IRQS_H +#define __ASM_ARCH_IRQS_H + +/* + * MPS interrupt sources + */ +#define IRQ_MPS_WDOG 0 /* Watchdog timer */ +#define IRQ_MPS_RTC 1 /* Real Time Clock */ +#define IRQ_MPS_TIMER0_1 2 /* Timer 0 and 1 */ +#define IRQ_MPS_TIMER2_3 3 /* Timer 2 and 3 */ +#define IRQ_MPS_MMCIA 4 /* Multimedia Card A */ +#define IRQ_MPS_MMCIB 5 /* Multimedia Card B */ +#define IRQ_MPS_UART0 6 /* UART 0 on development chip */ +#define IRQ_MPS_UART1 7 /* UART 1 on development chip */ +#define IRQ_MPS_UART2 8 /* UART 2 on development chip */ + /* Reserved */ +#define IRQ_MPS_AACI 10 /* Audio Codec */ +#define IRQ_MPS_CLCD 11 /* CLCD controller */ +#define IRQ_MPS_ETH 12 /* Ethernet controller */ +#define IRQ_MPS_USB 13 /* USB controller */ +#define IRQ_MPS_USB_HC 14 /* USB controller */ +#define IRQ_MPS_CHARLCD 15 /* Character LCD */ + /* 16 - 29 reserved */ +#define IRQ_MPS_UART3 30 /* UART 3 on development chip */ +#define IRQ_MPS_SPI 31 /* Touchscreen */ + +#define NR_IRQS 32 + +#endif diff --git a/arch/arm/mach-mps/include/mach/memory.h b/arch/arm/mach-mps/include/mach/memory.h new file mode 100644 index 000000000000..bb63833a1ee7 --- /dev/null +++ b/arch/arm/mach-mps/include/mach/memory.h @@ -0,0 +1,34 @@ +/* + * arch/arm/mach-mps/include/mach/memory.h + * + * Copyright (C) 2009 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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_ARCH_MEMORY_H +#define __ASM_ARCH_MEMORY_H + +#define PHYS_OFFSET UL(0x10000000) + +/* + * Virtual view <-> DMA view memory address translations + * virt_to_bus: Used to translate the virtual address to an + * address suitable to be passed to set_dma_addr + * bus_to_virt: Used to convert an address for DMA operations + * to an address that the kernel can use. + */ +#define __virt_to_bus(x) __virt_to_phys(x) +#define __bus_to_virt(x) __phys_to_virt(x) + +#endif diff --git a/arch/arm/mach-mps/include/mach/platform.h b/arch/arm/mach-mps/include/mach/platform.h new file mode 100644 index 000000000000..78dccb3d6cbe --- /dev/null +++ b/arch/arm/mach-mps/include/mach/platform.h @@ -0,0 +1,91 @@ +/* + * arch/arm/mach-mps/include/mach/platform.h + * + * Copyright (C) 2009 ARM Ltd. + * + * 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_ARCH_PLATFORM_H +#define __ASM_ARCH_PLATFORM_H + +/* + * MPS system registers + */ +#define MPS_SYS_BASE 0x40000000 +#define MPS_SYS_ID_OFFSET 0x00 /* Board and FPGA identifier */ +#define MPS_SYS_PERCFG_OFFSET 0x04 /* Peripheral control signals */ +#define MPS_SYS_SW_OFFSET 0x08 /* Indicates user switch settings */ +#define MPS_SYS_LED_OFFSET 0x0C /* Sets LED outputs */ +#define MPS_SYS_7SEG_OFFSET 0x10 /* Sets LED outputs */ +#define MPS_SYS_CNT25MHz_OFFSET 0x14 /* Free running counter incrementing at 25MHz */ +#define MPS_SYS_CNT100Hz_OFFSET 0x18 /* Free running counter incrementing at 100Hz */ + +/* + * MPS peripheral addresses + */ +#define MPS_FLASH_BASE 0x18000000 +#define MPS_FLASH_SIZE SZ_64M + +#define MPS_SPI_BASE 0x1F004000 /* Touchscreen */ +#define MPS_UART3_BASE 0x1F005000 /* UART 3 */ + +#define MPS_WATCHDOG_BASE 0x40000000 /* watchdog interface */ +#define MPS_RTC_BASE 0x40001000 /* Real Time Clock */ +#define MPS_TIMER0_1_BASE 0x40002000 /* Timer 0 and 1 */ +#define MPS_TIMER2_3_BASE 0x40003000 /* Timer 2 and 3 */ +#define MPS_SCTL_BASE 0x40004000 /* System controller */ +#define MPS_MMCI_BASE 0x40005000 /* MMC interface */ +#define MPS_UART0_BASE 0x40006000 /* UART 0 */ +#define MPS_UART1_BASE 0x40007000 /* UART 1 */ +#define MPS_UART2_BASE 0x40008000 /* UART 2 */ + /* Reserved */ +#define MPS_AACI_BASE 0x4000A000 /* Audio */ +#define MPS_I2C_BASE 0x4000B000 /* I2C control */ +#define MPS_CHAR_LCD_BASE 0x4000C000 /* Character LCD */ + +#define MPS_ETH_BASE 0x4FFE0000 /* Ethernet */ +#define MPS_CLCD_BASE 0x4FFF0000 /* CLCD */ + +#define MPS_DMC_BASE 0x60000000 /* Dynamic Memory Controller */ + +#define MPS_SMC_BASE 0xA0000000 /* Static Memory Controller */ +//#define MPS_USB_BASE 0xA0000000 /* USB */ + +/* + * System controller bit assignment + */ +#define MPS_REFCLK 0 +#define MPS_TIMCLK 1 + +#define MPS_TIMER1_EnSel 15 +#define MPS_TIMER2_EnSel 17 +#define MPS_TIMER3_EnSel 19 +#define MPS_TIMER4_EnSel 21 + +#define MAX_TIMER 2 +#define MAX_PERIOD 699050 +#define TICKS_PER_uSEC 1 + +/* + * These are useconds NOT ticks. + */ +#define mSEC_1 1000 +#define mSEC_5 (mSEC_1 * 5) +#define mSEC_10 (mSEC_1 * 10) +#define mSEC_25 (mSEC_1 * 25) +#define SEC_1 (mSEC_1 * 1000) + +#endif /* __ASM_ARCH_PLATFORM_H */ diff --git a/arch/arm/mach-mps/include/mach/system.h b/arch/arm/mach-mps/include/mach/system.h new file mode 100644 index 000000000000..e241770e1184 --- /dev/null +++ b/arch/arm/mach-mps/include/mach/system.h @@ -0,0 +1,35 @@ +/* + * arch/arm/mach-mps/include/mach/system.h + * + * Copyright (C) 2009 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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_ARCH_SYSTEM_H +#define __ASM_ARCH_SYSTEM_H + +#include <linux/io.h> +#include <mach/hardware.h> +#include <mach/platform.h> + +static inline void arch_idle(void) +{ + cpu_do_idle(); +} + +static inline void arch_reset(char mode) +{ +} + +#endif diff --git a/arch/arm/mach-mps/include/mach/timex.h b/arch/arm/mach-mps/include/mach/timex.h new file mode 100644 index 000000000000..91792e8aa490 --- /dev/null +++ b/arch/arm/mach-mps/include/mach/timex.h @@ -0,0 +1,20 @@ +/* + * arch/arm/mach-mps/include/mach/timex.h + * + * Copyright (C) 2009 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 + */ + +#define CLOCK_TICK_RATE (50000000 / 16) diff --git a/arch/arm/mach-mps/include/mach/uncompress.h b/arch/arm/mach-mps/include/mach/uncompress.h new file mode 100644 index 000000000000..e8eca2e52e99 --- /dev/null +++ b/arch/arm/mach-mps/include/mach/uncompress.h @@ -0,0 +1,62 @@ +/* + * arch/arm/mach-realview/include/mach/uncompress.h + * + * Copyright (C) 2009 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 <mach/hardware.h> +#include <asm/mach-types.h> + +#include <mach/platform.h> + +#define AMBA_UART_DR(base) (*(volatile unsigned char *)((base) + 0x00)) +#define AMBA_UART_LCRH(base) (*(volatile unsigned char *)((base) + 0x2c)) +#define AMBA_UART_CR(base) (*(volatile unsigned char *)((base) + 0x30)) +#define AMBA_UART_FR(base) (*(volatile unsigned char *)((base) + 0x18)) + +/* + * Return the UART base address + */ +static inline unsigned long get_uart_base(void) +{ + return MPS_UART3_BASE; +} + +/* + * This does not append a newline + */ +static inline void putc(int c) +{ + unsigned long base = get_uart_base(); + + while (AMBA_UART_FR(base) & (1 << 5)) + barrier(); + + AMBA_UART_DR(base) = c; +} + +static inline void flush(void) +{ + unsigned long base = get_uart_base(); + + while (AMBA_UART_FR(base) & (1 << 3)) + barrier(); +} + +/* + * nothing to do + */ +#define arch_decomp_setup() +#define arch_decomp_wdog() diff --git a/arch/arm/mach-mps/include/mach/vmalloc.h b/arch/arm/mach-mps/include/mach/vmalloc.h new file mode 100644 index 000000000000..fe0de1b507ac --- /dev/null +++ b/arch/arm/mach-mps/include/mach/vmalloc.h @@ -0,0 +1,21 @@ +/* + * arch/arm/mach-realview/include/mach/vmalloc.h + * + * Copyright (C) 2003 ARM Limited + * Copyright (C) 2000 Russell King. + * + * 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 + */ +#define VMALLOC_END 0xf8000000 diff --git a/arch/arm/mach-realview/Kconfig b/arch/arm/mach-realview/Kconfig index ad911854eb4c..5ccde7cf39e8 100644 --- a/arch/arm/mach-realview/Kconfig +++ b/arch/arm/mach-realview/Kconfig @@ -7,17 +7,9 @@ config MACH_REALVIEW_EB help Include support for the ARM(R) RealView Emulation Baseboard platform. -config REALVIEW_EB_A9MP - bool "Support Multicore Cortex-A9" - depends on MACH_REALVIEW_EB - select CPU_V7 - help - Enable support for the Cortex-A9MPCore tile on the Realview platform. - config REALVIEW_EB_ARM11MP bool "Support ARM11MPCore tile" depends on MACH_REALVIEW_EB - select CPU_V6 help Enable support for the ARM11MPCore tile on the Realview platform. @@ -33,7 +25,6 @@ config REALVIEW_EB_ARM11MP_REVB config MACH_REALVIEW_PB11MP bool "Support RealView/PB11MPCore platform" - select CPU_V6 select ARM_GIC help Include support for the ARM(R) RealView MPCore Platform Baseboard. @@ -42,29 +33,8 @@ config MACH_REALVIEW_PB11MP config MACH_REALVIEW_PB1176 bool "Support RealView/PB1176 platform" - select CPU_V6 select ARM_GIC help Include support for the ARM(R) RealView ARM1176 Platform Baseboard. -config MACH_REALVIEW_PBA8 - bool "Support RealView/PB-A8 platform" - select CPU_V7 - select ARM_GIC - help - Include support for the ARM(R) RealView Cortex-A8 Platform Baseboard. - PB-A8 is a platform with an on-board Cortex-A8 and has support for - PCI-E and Compact Flash. - -config REALVIEW_HIGH_PHYS_OFFSET - bool "High physical base address for the RealView platform" - depends on !MACH_REALVIEW_PB1176 - default y - help - RealView boards other than PB1176 have the RAM available at - 0x70000000, 256MB of which being mirrored at 0x00000000. If - the board supports 512MB of RAM, this option allows the - memory to be accessed contiguously at the high physical - offset. - endmenu diff --git a/arch/arm/mach-realview/Makefile b/arch/arm/mach-realview/Makefile index 7bea8ffc4b59..d2ae077431dd 100644 --- a/arch/arm/mach-realview/Makefile +++ b/arch/arm/mach-realview/Makefile @@ -6,6 +6,5 @@ obj-y := core.o clock.o obj-$(CONFIG_MACH_REALVIEW_EB) += realview_eb.o obj-$(CONFIG_MACH_REALVIEW_PB11MP) += realview_pb11mp.o obj-$(CONFIG_MACH_REALVIEW_PB1176) += realview_pb1176.o -obj-$(CONFIG_MACH_REALVIEW_PBA8) += realview_pba8.o obj-$(CONFIG_SMP) += platsmp.o headsmp.o localtimer.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o diff --git a/arch/arm/mach-realview/core.c b/arch/arm/mach-realview/core.c index bd2aa4f16141..2f04d54711e7 100644 --- a/arch/arm/mach-realview/core.c +++ b/arch/arm/mach-realview/core.c @@ -28,14 +28,11 @@ #include <linux/clocksource.h> #include <linux/clockchips.h> #include <linux/io.h> -#include <linux/smc911x.h> -#include <asm/clkdev.h> #include <asm/system.h> #include <mach/hardware.h> #include <asm/irq.h> #include <asm/leds.h> -#include <asm/mach-types.h> #include <asm/hardware/arm_timer.h> #include <asm/hardware/icst307.h> @@ -52,7 +49,7 @@ #define REALVIEW_REFCOUNTER (__io_address(REALVIEW_SYS_BASE) + REALVIEW_SYS_24MHz_OFFSET) -/* used by entry-macro.S and platsmp.c */ +/* used by entry-macro.S */ void __iomem *gic_cpu_base_addr; /* @@ -127,29 +124,6 @@ int realview_flash_register(struct resource *res, u32 num) return platform_device_register(&realview_flash_device); } -static struct smc911x_platdata realview_smc911x_platdata = { - .flags = SMC911X_USE_32BIT, - .irq_flags = IRQF_SHARED, - .irq_polarity = 1, -}; - -static struct platform_device realview_eth_device = { - .name = "smc911x", - .id = 0, - .num_resources = 2, -}; - -int realview_eth_register(const char *name, struct resource *res) -{ - if (name) - realview_eth_device.name = name; - realview_eth_device.resource = res; - if (strcmp(realview_eth_device.name, "smc911x") == 0) - realview_eth_device.dev.platform_data = &realview_smc911x_platdata; - - return platform_device_register(&realview_eth_device); -} - static struct resource realview_i2c_resource = { .start = REALVIEW_I2C_BASE, .end = REALVIEW_I2C_BASE + SZ_4K - 1, @@ -203,14 +177,9 @@ static const struct icst307_params realview_oscvco_params = { static void realview_oscvco_set(struct clk *clk, struct icst307_vco vco) { void __iomem *sys_lock = __io_address(REALVIEW_SYS_BASE) + REALVIEW_SYS_LOCK_OFFSET; - void __iomem *sys_osc; + void __iomem *sys_osc = __io_address(REALVIEW_SYS_BASE) + REALVIEW_SYS_OSC4_OFFSET; u32 val; - if (machine_is_realview_pb1176()) - sys_osc = __io_address(REALVIEW_SYS_BASE) + REALVIEW_SYS_OSC0_OFFSET; - else - sys_osc = __io_address(REALVIEW_SYS_BASE) + REALVIEW_SYS_OSC4_OFFSET; - val = readl(sys_osc) & ~0x7ffff; val |= vco.v | (vco.r << 9) | (vco.s << 16); @@ -219,60 +188,13 @@ static void realview_oscvco_set(struct clk *clk, struct icst307_vco vco) writel(0, sys_lock); } -static struct clk oscvco_clk = { +struct clk realview_clcd_clk = { + .name = "CLCDCLK", .params = &realview_oscvco_params, .setvco = realview_oscvco_set, }; /* - * These are fixed clocks. - */ -static struct clk ref24_clk = { - .rate = 24000000, -}; - -static struct clk_lookup lookups[] = { - { /* UART0 */ - .dev_id = "dev:f1", - .clk = &ref24_clk, - }, { /* UART1 */ - .dev_id = "dev:f2", - .clk = &ref24_clk, - }, { /* UART2 */ - .dev_id = "dev:f3", - .clk = &ref24_clk, - }, { /* UART3 */ - .dev_id = "fpga:09", - .clk = &ref24_clk, - }, { /* KMI0 */ - .dev_id = "fpga:06", - .clk = &ref24_clk, - }, { /* KMI1 */ - .dev_id = "fpga:07", - .clk = &ref24_clk, - }, { /* MMC0 */ - .dev_id = "fpga:05", - .clk = &ref24_clk, - }, { /* EB:CLCD */ - .dev_id = "dev:20", - .clk = &oscvco_clk, - }, { /* PB:CLCD */ - .dev_id = "issp:20", - .clk = &oscvco_clk, - } -}; - -static int __init clk_init(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(lookups); i++) - clkdev_add(&lookups[i]); - return 0; -} -arch_initcall(clk_init); - -/* * CLCD support. */ #define SYS_CLCD_NLCDIOON (1 << 2) @@ -304,30 +226,7 @@ static struct clcd_panel vga = { .width = -1, .height = -1, .tim2 = TIM2_BCD | TIM2_IPC, - .cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP(1), - .bpp = 16, -}; - -static struct clcd_panel xvga = { - .mode = { - .name = "XVGA", - .refresh = 60, - .xres = 1024, - .yres = 768, - .pixclock = 15748, - .left_margin = 152, - .right_margin = 48, - .upper_margin = 23, - .lower_margin = 3, - .hsync_len = 104, - .vsync_len = 4, - .sync = 0, - .vmode = FB_VMODE_NONINTERLACED, - }, - .width = -1, - .height = -1, - .tim2 = TIM2_BCD | TIM2_IPC, - .cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP(1), + .cntl = CNTL_LCDTFT | CNTL_LCDVCOMP(1), .bpp = 16, }; @@ -350,7 +249,7 @@ static struct clcd_panel sanyo_3_8_in = { .width = -1, .height = -1, .tim2 = TIM2_BCD, - .cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP(1), + .cntl = CNTL_LCDTFT | CNTL_LCDVCOMP(1), .bpp = 16, }; @@ -373,7 +272,7 @@ static struct clcd_panel sanyo_2_5_in = { .width = -1, .height = -1, .tim2 = TIM2_IVS | TIM2_IHS | TIM2_IPC, - .cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP(1), + .cntl = CNTL_LCDTFT | CNTL_LCDVCOMP(1), .bpp = 16, }; @@ -396,7 +295,7 @@ static struct clcd_panel epson_2_2_in = { .width = -1, .height = -1, .tim2 = TIM2_BCD | TIM2_IPC, - .cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP(1), + .cntl = CNTL_LCDTFT | CNTL_LCDVCOMP(1), .bpp = 16, }; @@ -409,15 +308,9 @@ static struct clcd_panel epson_2_2_in = { static struct clcd_panel *realview_clcd_panel(void) { void __iomem *sys_clcd = __io_address(REALVIEW_SYS_BASE) + REALVIEW_SYS_CLCD_OFFSET; - struct clcd_panel *vga_panel; - struct clcd_panel *panel; + struct clcd_panel *panel = &vga; u32 val; - if (machine_is_realview_eb()) - vga_panel = &vga; - else - vga_panel = &xvga; - val = readl(sys_clcd) & SYS_CLCD_ID_MASK; if (val == SYS_CLCD_ID_SANYO_3_8) panel = &sanyo_3_8_in; @@ -426,11 +319,11 @@ static struct clcd_panel *realview_clcd_panel(void) else if (val == SYS_CLCD_ID_EPSON_2_2) panel = &epson_2_2_in; else if (val == SYS_CLCD_ID_VGA) - panel = vga_panel; + panel = &vga; else { printk(KERN_ERR "CLCD: unknown LCD panel ID 0x%08x, using VGA\n", val); - panel = vga_panel; + panel = &vga; } return panel; @@ -465,18 +358,12 @@ static void realview_clcd_enable(struct clcd_fb *fb) writel(val, sys_clcd); } +static unsigned long framesize = SZ_1M; + static int realview_clcd_setup(struct clcd_fb *fb) { - unsigned long framesize; dma_addr_t dma; - if (machine_is_realview_eb()) - /* VGA, 16bpp */ - framesize = 640 * 480 * 2; - else - /* XVGA, 16bpp */ - framesize = 1024 * 768 * 2; - fb->panel = realview_clcd_panel(); fb->fb.screen_base = dma_alloc_writecombine(&fb->dev->dev, framesize, @@ -624,7 +511,7 @@ static struct clock_event_device timer0_clockevent = { .set_mode = timer_set_mode, .set_next_event = timer_set_next_event, .rating = 300, - .cpumask = cpu_all_mask, + .cpumask = CPU_MASK_ALL, }; static void __init realview_clockevents_init(unsigned int timer_irq) @@ -701,7 +588,7 @@ void __init realview_timer_init(unsigned int timer_irq) * The dummy clock device has to be registered before the main device * so that the latter will broadcast the clock events */ - local_timer_setup(); + local_timer_setup(smp_processor_id()); #endif /* diff --git a/arch/arm/mach-realview/core.h b/arch/arm/mach-realview/core.h index 44269b162d49..3cea92c70d8f 100644 --- a/arch/arm/mach-realview/core.h +++ b/arch/arm/mach-realview/core.h @@ -31,7 +31,7 @@ static struct amba_device name##_device = { \ .dev = { \ .coherent_dma_mask = ~0, \ - .init_name = busid, \ + .bus_id = busid, \ .platform_data = plat, \ }, \ .res = { \ @@ -48,10 +48,12 @@ extern struct platform_device realview_flash_device; extern struct platform_device realview_i2c_device; extern struct mmc_platform_data realview_mmc0_plat_data; extern struct mmc_platform_data realview_mmc1_plat_data; +extern struct clk realview_clcd_clk; extern struct clcd_board clcd_plat_data; extern void __iomem *gic_cpu_base_addr; #ifdef CONFIG_LOCAL_TIMERS -extern void __iomem *twd_base; +extern void __iomem *twd_base_addr; +extern unsigned int twd_size; #endif extern void __iomem *timer0_va_base; extern void __iomem *timer1_va_base; @@ -61,6 +63,5 @@ extern void __iomem *timer3_va_base; extern void realview_leds_event(led_event_t ledevt); extern void realview_timer_init(unsigned int timer_irq); extern int realview_flash_register(struct resource *res, u32 num); -extern int realview_eth_register(const char *name, struct resource *res); #endif diff --git a/arch/arm/mach-realview/include/mach/debug-macro.S b/arch/arm/mach-realview/include/mach/debug-macro.S index 92dbcb9e1792..7196bcadff0c 100644 --- a/arch/arm/mach-realview/include/mach/debug-macro.S +++ b/arch/arm/mach-realview/include/mach/debug-macro.S @@ -8,36 +8,15 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - */ - -#if defined(CONFIG_MACH_REALVIEW_EB) || \ - defined(CONFIG_MACH_REALVIEW_PB11MP) || \ - defined(CONFIG_MACH_REALVIEW_PBA8) -#ifndef DEBUG_LL_UART_OFFSET -#define DEBUG_LL_UART_OFFSET 0x00009000 -#elif DEBUG_LL_UART_OFFSET != 0x00009000 -#warning "DEBUG_LL_UART_OFFSET already defined to a different value" -#endif -#endif - -#ifdef CONFIG_MACH_REALVIEW_PB1176 -#ifndef DEBUG_LL_UART_OFFSET -#define DEBUG_LL_UART_OFFSET 0x0010c000 -#elif DEBUG_LL_UART_OFFSET != 0x0010c000 -#warning "DEBUG_LL_UART_OFFSET already defined to a different value" -#endif -#endif - -#ifndef DEBUG_LL_UART_OFFSET -#error "Unknown RealView platform" -#endif + * +*/ .macro addruart,rx mrc p15, 0, \rx, c1, c0 tst \rx, #1 @ MMU enabled? moveq \rx, #0x10000000 - movne \rx, #0xfb000000 @ virtual base - orr \rx, \rx, #DEBUG_LL_UART_OFFSET + movne \rx, #0xf0000000 @ virtual base + orr \rx, \rx, #0x00009000 .endm #include <asm/hardware/debug-pl01x.S> diff --git a/arch/arm/mach-realview/include/mach/hardware.h b/arch/arm/mach-realview/include/mach/hardware.h index b42c14f89acb..79a93b3dfca9 100644 --- a/arch/arm/mach-realview/include/mach/hardware.h +++ b/arch/arm/mach-realview/include/mach/hardware.h @@ -25,14 +25,7 @@ #include <asm/sizes.h> /* macro to get at IO space when running virtually */ -/* - * Statically mapped addresses: - * - * 10xx xxxx -> fbxx xxxx - * 1exx xxxx -> fdxx xxxx - * 1fxx xxxx -> fexx xxxx - */ -#define IO_ADDRESS(x) (((x) & 0x03ffffff) + 0xfb000000) +#define IO_ADDRESS(x) (((x) & 0x0fffffff) + 0xf0000000) #define __io_address(n) __io(IO_ADDRESS(n)) #endif diff --git a/arch/arm/mach-realview/include/mach/irqs.h b/arch/arm/mach-realview/include/mach/irqs.h index fe5cb987aa21..02a918529db3 100644 --- a/arch/arm/mach-realview/include/mach/irqs.h +++ b/arch/arm/mach-realview/include/mach/irqs.h @@ -25,7 +25,6 @@ #include <mach/board-eb.h> #include <mach/board-pb11mp.h> #include <mach/board-pb1176.h> -#include <mach/board-pba8.h> #define IRQ_LOCALTIMER 29 #define IRQ_LOCALWDOG 30 diff --git a/arch/arm/mach-realview/include/mach/memory.h b/arch/arm/mach-realview/include/mach/memory.h index 293c30025e7e..0e673483a141 100644 --- a/arch/arm/mach-realview/include/mach/memory.h +++ b/arch/arm/mach-realview/include/mach/memory.h @@ -23,10 +23,16 @@ /* * Physical DRAM offset. */ -#ifdef CONFIG_REALVIEW_HIGH_PHYS_OFFSET -#define PHYS_OFFSET UL(0x70000000) -#else #define PHYS_OFFSET UL(0x00000000) -#endif + +/* + * Virtual view <-> DMA view memory address translations + * virt_to_bus: Used to translate the virtual address to an + * address suitable to be passed to set_dma_addr + * bus_to_virt: Used to convert an address for DMA operations + * to an address that the kernel can use. + */ +#define __virt_to_bus(x) ((x) - PAGE_OFFSET) +#define __bus_to_virt(x) ((x) + PAGE_OFFSET) #endif diff --git a/arch/arm/mach-realview/include/mach/uncompress.h b/arch/arm/mach-realview/include/mach/uncompress.h index 415d634d52ab..79f50f218e77 100644 --- a/arch/arm/mach-realview/include/mach/uncompress.h +++ b/arch/arm/mach-realview/include/mach/uncompress.h @@ -23,7 +23,6 @@ #include <mach/board-eb.h> #include <mach/board-pb11mp.h> #include <mach/board-pb1176.h> -#include <mach/board-pba8.h> #define AMBA_UART_DR(base) (*(volatile unsigned char *)((base) + 0x00)) #define AMBA_UART_LCRH(base) (*(volatile unsigned char *)((base) + 0x2c)) @@ -41,8 +40,6 @@ static inline unsigned long get_uart_base(void) return REALVIEW_PB11MP_UART0_BASE; else if (machine_is_realview_pb1176()) return REALVIEW_PB1176_UART0_BASE; - else if (machine_is_realview_pba8()) - return REALVIEW_PBA8_UART0_BASE; else return 0; } diff --git a/arch/arm/mach-realview/localtimer.c b/arch/arm/mach-realview/localtimer.c index 67d6d9cc68b2..44d178cd5733 100644 --- a/arch/arm/mach-realview/localtimer.c +++ b/arch/arm/mach-realview/localtimer.c @@ -38,14 +38,18 @@ void local_timer_interrupt(void) #ifdef CONFIG_LOCAL_TIMERS +#define TWD_BASE(cpu) (twd_base_addr + (cpu) * twd_size) + /* set up by the platform code */ -void __iomem *twd_base; +void __iomem *twd_base_addr; +unsigned int twd_size; static unsigned long mpcore_timer_rate; static void local_timer_set_mode(enum clock_event_mode mode, struct clock_event_device *clk) { + void __iomem *base = TWD_BASE(smp_processor_id()); unsigned long ctrl; switch(mode) { @@ -64,16 +68,17 @@ static void local_timer_set_mode(enum clock_event_mode mode, ctrl = 0; } - __raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL); + __raw_writel(ctrl, base + TWD_TIMER_CONTROL); } static int local_timer_set_next_event(unsigned long evt, struct clock_event_device *unused) { - unsigned long ctrl = __raw_readl(twd_base + TWD_TIMER_CONTROL); + void __iomem *base = TWD_BASE(smp_processor_id()); + unsigned long ctrl = __raw_readl(base + TWD_TIMER_CONTROL); - __raw_writel(evt, twd_base + TWD_TIMER_COUNTER); - __raw_writel(ctrl | TWD_TIMER_CONTROL_ENABLE, twd_base + TWD_TIMER_CONTROL); + __raw_writel(evt, base + TWD_TIMER_COUNTER); + __raw_writel(ctrl | TWD_TIMER_CONTROL_ENABLE, base + TWD_TIMER_CONTROL); return 0; } @@ -86,16 +91,19 @@ static int local_timer_set_next_event(unsigned long evt, */ int local_timer_ack(void) { - if (__raw_readl(twd_base + TWD_TIMER_INTSTAT)) { - __raw_writel(1, twd_base + TWD_TIMER_INTSTAT); + void __iomem *base = TWD_BASE(smp_processor_id()); + + if (__raw_readl(base + TWD_TIMER_INTSTAT)) { + __raw_writel(1, base + TWD_TIMER_INTSTAT); return 1; } return 0; } -static void __cpuinit twd_calibrate_rate(void) +static void __cpuinit twd_calibrate_rate(unsigned int cpu) { + void __iomem *base = TWD_BASE(cpu); unsigned long load, count; u64 waitjiffies; @@ -116,15 +124,15 @@ static void __cpuinit twd_calibrate_rate(void) waitjiffies += 5; /* enable, no interrupt or reload */ - __raw_writel(0x1, twd_base + TWD_TIMER_CONTROL); + __raw_writel(0x1, base + TWD_TIMER_CONTROL); /* maximum value */ - __raw_writel(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER); + __raw_writel(0xFFFFFFFFU, base + TWD_TIMER_COUNTER); while (get_jiffies_64() < waitjiffies) udelay(10); - count = __raw_readl(twd_base + TWD_TIMER_COUNTER); + count = __raw_readl(base + TWD_TIMER_COUNTER); mpcore_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5); @@ -134,19 +142,18 @@ static void __cpuinit twd_calibrate_rate(void) load = mpcore_timer_rate / HZ; - __raw_writel(load, twd_base + TWD_TIMER_LOAD); + __raw_writel(load, base + TWD_TIMER_LOAD); } /* * Setup the local clock events for a CPU. */ -void __cpuinit local_timer_setup(void) +void __cpuinit local_timer_setup(unsigned int cpu) { - unsigned int cpu = smp_processor_id(); struct clock_event_device *clk = &per_cpu(local_clockevent, cpu); unsigned long flags; - twd_calibrate_rate(); + twd_calibrate_rate(cpu); clk->name = "local_timer"; clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; @@ -154,7 +161,7 @@ void __cpuinit local_timer_setup(void) clk->set_mode = local_timer_set_mode; clk->set_next_event = local_timer_set_next_event; clk->irq = IRQ_LOCALTIMER; - clk->cpumask = cpumask_of(cpu); + clk->cpumask = cpumask_of_cpu(cpu); clk->shift = 20; clk->mult = div_sc(mpcore_timer_rate, NSEC_PER_SEC, clk->shift); clk->max_delta_ns = clockevent_delta2ns(0xffffffff, clk); @@ -171,9 +178,9 @@ void __cpuinit local_timer_setup(void) /* * take a local timer down */ -void __cpuexit local_timer_stop(void) +void __cpuexit local_timer_stop(unsigned int cpu) { - __raw_writel(0, twd_base + TWD_TIMER_CONTROL); + __raw_writel(0, TWD_BASE(cpu) + TWD_TIMER_CONTROL); } #else /* CONFIG_LOCAL_TIMERS */ @@ -183,9 +190,8 @@ static void dummy_timer_set_mode(enum clock_event_mode mode, { } -void __cpuinit local_timer_setup(void) +void __cpuinit local_timer_setup(unsigned int cpu) { - unsigned int cpu = smp_processor_id(); struct clock_event_device *clk = &per_cpu(local_clockevent, cpu); clk->name = "dummy_timer"; @@ -193,7 +199,7 @@ void __cpuinit local_timer_setup(void) clk->rating = 200; clk->set_mode = dummy_timer_set_mode; clk->broadcast = smp_timer_broadcast; - clk->cpumask = cpumask_of(cpu); + clk->cpumask = cpumask_of_cpu(cpu); clockevents_register_device(clk); } diff --git a/arch/arm/mach-realview/platsmp.c b/arch/arm/mach-realview/platsmp.c index ea3c75595fa9..e102aeb0f76e 100644 --- a/arch/arm/mach-realview/platsmp.c +++ b/arch/arm/mach-realview/platsmp.c @@ -12,7 +12,6 @@ #include <linux/errno.h> #include <linux/delay.h> #include <linux/device.h> -#include <linux/jiffies.h> #include <linux/smp.h> #include <linux/io.h> @@ -24,8 +23,6 @@ #include <mach/board-pb11mp.h> #include <mach/scu.h> -#include "core.h" - extern void realview_secondary_startup(void); /* @@ -34,20 +31,15 @@ extern void realview_secondary_startup(void); */ volatile int __cpuinitdata pen_release = -1; -static void __iomem *scu_base_addr(void) -{ - if (machine_is_realview_eb_mp()) - return __io_address(REALVIEW_EB11MP_SCU_BASE); - else if (machine_is_realview_pb11mp()) - return __io_address(REALVIEW_TC11MP_SCU_BASE); - else - return (void __iomem *)0; -} - static unsigned int __init get_core_count(void) { unsigned int ncores; - void __iomem *scu_base = scu_base_addr(); + void __iomem *scu_base = 0; + + if (machine_is_realview_eb() && core_tile_eb11mp()) + scu_base = __io_address(REALVIEW_EB11MP_SCU_BASE); + else if (machine_is_realview_pb11mp()) + scu_base = __io_address(REALVIEW_TC11MP_SCU_BASE); if (scu_base) { ncores = __raw_readl(scu_base + SCU_CONFIG); @@ -64,7 +56,14 @@ static unsigned int __init get_core_count(void) static void scu_enable(void) { u32 scu_ctrl; - void __iomem *scu_base = scu_base_addr(); + void __iomem *scu_base; + + if (machine_is_realview_eb() && core_tile_eb11mp()) + scu_base = __io_address(REALVIEW_EB11MP_SCU_BASE); + else if (machine_is_realview_pb11mp()) + scu_base = __io_address(REALVIEW_TC11MP_SCU_BASE); + else + BUG(); scu_ctrl = __raw_readl(scu_base + SCU_CTRL); scu_ctrl |= 1; @@ -89,7 +88,10 @@ void __cpuinit platform_secondary_init(unsigned int cpu) * core (e.g. timer irq), then they will not have been enabled * for us: do so */ - gic_cpu_init(0, gic_cpu_base_addr); + if (machine_is_realview_eb() && core_tile_eb11mp()) + gic_cpu_init(0, __io_address(REALVIEW_EB11MP_GIC_CPU_BASE)); + else if (machine_is_realview_pb11mp()) + gic_cpu_init(0, __io_address(REALVIEW_TC11MP_GIC_CPU_BASE)); /* * let the primary processor know we're out of the @@ -230,7 +232,9 @@ void __init smp_prepare_cpus(unsigned int max_cpus) * dummy (!CONFIG_LOCAL_TIMERS), it was already registers in * realview_timer_init */ - local_timer_setup(); + if ((machine_is_realview_eb() && core_tile_eb11mp()) || + machine_is_realview_pb11mp()) + local_timer_setup(cpu); #endif /* diff --git a/arch/arm/mach-realview/realview_eb.c b/arch/arm/mach-realview/realview_eb.c index bed39ed97613..eb829eb1ebe2 100644 --- a/arch/arm/mach-realview/realview_eb.c +++ b/arch/arm/mach-realview/realview_eb.c @@ -108,7 +108,7 @@ static struct map_desc realview_eb11mp_io_desc[] __initdata = { static void __init realview_eb_map_io(void) { iotable_init(realview_eb_io_desc, ARRAY_SIZE(realview_eb_io_desc)); - if (core_tile_eb11mp() || core_tile_a9mp()) + if (core_tile_eb11mp()) iotable_init(realview_eb11mp_io_desc, ARRAY_SIZE(realview_eb11mp_io_desc)); } @@ -242,6 +242,12 @@ static struct resource realview_eb_eth_resources[] = { }, }; +static struct platform_device realview_eb_eth_device = { + .id = 0, + .num_resources = ARRAY_SIZE(realview_eb_eth_resources), + .resource = realview_eb_eth_resources, +}; + /* * Detect and register the correct Ethernet device. RealView/EB rev D * platforms use the newer SMSC LAN9118 Ethernet chip @@ -249,24 +255,26 @@ static struct resource realview_eb_eth_resources[] = { static int eth_device_register(void) { void __iomem *eth_addr = ioremap(REALVIEW_EB_ETH_BASE, SZ_4K); - const char *name = NULL; u32 idrev; if (!eth_addr) return -ENOMEM; idrev = readl(eth_addr + 0x50); - if ((idrev & 0xFFFF0000) != 0x01180000) - /* SMSC LAN9118 not present, use LAN91C111 instead */ - name = "smc91x"; + if ((idrev & 0xFFFF0000) == 0x01180000) + /* SMSC LAN9118 chip present */ + realview_eb_eth_device.name = "smc911x"; + else + /* SMSC 91C111 chip present */ + realview_eb_eth_device.name = "smc91x"; iounmap(eth_addr); - return realview_eth_register(name, realview_eb_eth_resources); + return platform_device_register(&realview_eb_eth_device); } static void __init gic_init_irq(void) { - if (core_tile_eb11mp() || core_tile_a9mp()) { + if (core_tile_eb11mp()) { unsigned int pldctrl; /* new irq mode */ @@ -334,9 +342,10 @@ static void __init realview_eb_timer_init(void) timer2_va_base = __io_address(REALVIEW_EB_TIMER2_3_BASE); timer3_va_base = __io_address(REALVIEW_EB_TIMER2_3_BASE) + 0x20; - if (core_tile_eb11mp() || core_tile_a9mp()) { + if (core_tile_eb11mp()) { #ifdef CONFIG_LOCAL_TIMERS - twd_base = __io_address(REALVIEW_EB11MP_TWD_BASE); + twd_base_addr = __io_address(REALVIEW_EB11MP_TWD_BASE); + twd_size = REALVIEW_EB11MP_TWD_SIZE; #endif timer_irq = IRQ_EB11MP_TIMER0_1; } else @@ -353,7 +362,7 @@ static void __init realview_eb_init(void) { int i; - if (core_tile_eb11mp() || core_tile_a9mp()) { + if (core_tile_eb11mp()) { realview_eb11mp_fixup(); #ifdef CONFIG_CACHE_L2X0 @@ -363,6 +372,8 @@ static void __init realview_eb_init(void) #endif } + clk_register(&realview_clcd_clk); + realview_flash_register(&realview_eb_flash_resource, 1); platform_device_register(&realview_i2c_device); eth_device_register(); @@ -381,7 +392,7 @@ MACHINE_START(REALVIEW_EB, "ARM-RealView EB") /* Maintainer: ARM Ltd/Deep Blue Solutions Ltd */ .phys_io = REALVIEW_EB_UART0_BASE, .io_pg_offst = (IO_ADDRESS(REALVIEW_EB_UART0_BASE) >> 18) & 0xfffc, - .boot_params = PHYS_OFFSET + 0x00000100, + .boot_params = 0x00000100, .map_io = realview_eb_map_io, .init_irq = gic_init_irq, .timer = &realview_eb_timer, diff --git a/arch/arm/mach-realview/realview_pb1176.c b/arch/arm/mach-realview/realview_pb1176.c index 8f0683c22140..cccdb3eb90fe 100644 --- a/arch/arm/mach-realview/realview_pb1176.c +++ b/arch/arm/mach-realview/realview_pb1176.c @@ -222,6 +222,13 @@ static struct resource realview_pb1176_smsc911x_resources[] = { }, }; +static struct platform_device realview_pb1176_smsc911x_device = { + .name = "smc911x", + .id = 0, + .num_resources = ARRAY_SIZE(realview_pb1176_smsc911x_resources), + .resource = realview_pb1176_smsc911x_resources, +}; + static void __init gic_init_irq(void) { /* ARM1176 DevChip GIC, primary */ @@ -258,8 +265,10 @@ static void __init realview_pb1176_init(void) l2x0_init(__io_address(REALVIEW_PB1176_L220_BASE), 0x00730000, 0xfe000fff); #endif + clk_register(&realview_clcd_clk); + realview_flash_register(&realview_pb1176_flash_resource, 1); - realview_eth_register(NULL, realview_pb1176_smsc911x_resources); + platform_device_register(&realview_pb1176_smsc911x_device); for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { struct amba_device *d = amba_devs[i]; @@ -275,7 +284,7 @@ MACHINE_START(REALVIEW_PB1176, "ARM-RealView PB1176") /* Maintainer: ARM Ltd/Deep Blue Solutions Ltd */ .phys_io = REALVIEW_PB1176_UART0_BASE, .io_pg_offst = (IO_ADDRESS(REALVIEW_PB1176_UART0_BASE) >> 18) & 0xfffc, - .boot_params = PHYS_OFFSET + 0x00000100, + .boot_params = 0x00000100, .map_io = realview_pb1176_map_io, .init_irq = gic_init_irq, .timer = &realview_pb1176_timer, diff --git a/arch/arm/mach-realview/realview_pb11mp.c b/arch/arm/mach-realview/realview_pb11mp.c index 3ebdb2dadd6f..8b863148ec18 100644 --- a/arch/arm/mach-realview/realview_pb11mp.c +++ b/arch/arm/mach-realview/realview_pb11mp.c @@ -230,6 +230,13 @@ static struct resource realview_pb11mp_smsc911x_resources[] = { }, }; +static struct platform_device realview_pb11mp_smsc911x_device = { + .name = "smc911x", + .id = 0, + .num_resources = ARRAY_SIZE(realview_pb11mp_smsc911x_resources), + .resource = realview_pb11mp_smsc911x_resources, +}; + struct resource realview_pb11mp_cf_resources[] = { [0] = { .start = REALVIEW_PB11MP_CF_BASE, @@ -285,7 +292,8 @@ static void __init realview_pb11mp_timer_init(void) timer3_va_base = __io_address(REALVIEW_PB11MP_TIMER2_3_BASE) + 0x20; #ifdef CONFIG_LOCAL_TIMERS - twd_base = __io_address(REALVIEW_TC11MP_TWD_BASE); + twd_base_addr = __io_address(REALVIEW_TC11MP_TWD_BASE); + twd_size = REALVIEW_TC11MP_TWD_SIZE; #endif realview_timer_init(IRQ_TC11MP_TIMER0_1); } @@ -304,9 +312,11 @@ static void __init realview_pb11mp_init(void) l2x0_init(__io_address(REALVIEW_TC11MP_L220_BASE), 0x00790000, 0xfe000fff); #endif + clk_register(&realview_clcd_clk); + realview_flash_register(realview_pb11mp_flash_resource, ARRAY_SIZE(realview_pb11mp_flash_resource)); - realview_eth_register(NULL, realview_pb11mp_smsc911x_resources); + platform_device_register(&realview_pb11mp_smsc911x_device); platform_device_register(&realview_i2c_device); platform_device_register(&realview_pb11mp_cf_device); @@ -324,7 +334,7 @@ MACHINE_START(REALVIEW_PB11MP, "ARM-RealView PB11MPCore") /* Maintainer: ARM Ltd/Deep Blue Solutions Ltd */ .phys_io = REALVIEW_PB11MP_UART0_BASE, .io_pg_offst = (IO_ADDRESS(REALVIEW_PB11MP_UART0_BASE) >> 18) & 0xfffc, - .boot_params = PHYS_OFFSET + 0x00000100, + .boot_params = 0x00000100, .map_io = realview_pb11mp_map_io, .init_irq = gic_init_irq, .timer = &realview_pb11mp_timer, diff --git a/arch/arm/mach-versatile/core.c b/arch/arm/mach-versatile/core.c index 1c43494f5c42..15b0434c1237 100644 --- a/arch/arm/mach-versatile/core.c +++ b/arch/arm/mach-versatile/core.c @@ -335,11 +335,25 @@ static struct resource versatile_i2c_resource = { static struct platform_device versatile_i2c_device = { .name = "versatile-i2c", - .id = -1, + .id = 0, .num_resources = 1, .resource = &versatile_i2c_resource, }; +static struct i2c_board_info versatile_i2c_board_info[] = { + { + I2C_BOARD_INFO("rtc-ds1307", 0xd0 >> 1), + .type = "ds1338", + }, +}; + +static int __init versatile_i2c_init(void) +{ + return i2c_register_board_info(0, versatile_i2c_board_info, + ARRAY_SIZE(versatile_i2c_board_info)); +} +arch_initcall(versatile_i2c_init); + #define VERSATILE_SYSMCI (__io_address(VERSATILE_SYS_BASE) + VERSATILE_SYS_MCI_OFFSET) unsigned int mmc_status(struct device *dev) @@ -455,12 +469,12 @@ static struct clcd_panel vga = { .xres = 640, .yres = 480, .pixclock = 39721, - .left_margin = 40, - .right_margin = 24, - .upper_margin = 32, - .lower_margin = 11, - .hsync_len = 96, - .vsync_len = 2, + .left_margin = 64, + .right_margin = 16, + .upper_margin = 13, + .lower_margin = 3, + .hsync_len = 80, + .vsync_len = 4, .sync = 0, .vmode = FB_VMODE_NONINTERLACED, }, diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index 91a76f472c24..42e1059505a2 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -362,7 +362,10 @@ config CPU_FEROCEON_OLD_ID # ARMv6 config CPU_V6 - bool "Support ARM V6 processor" if ARCH_INTEGRATOR || MACH_REALVIEW_EB + bool "Support ARM V6 processor" + depends on ARCH_INTEGRATOR || MACH_REALVIEW_EB || ARCH_OMAP2 || ARCH_MX3 || ARCH_MSM || MACH_REALVIEW_PB11MP || MACH_REALVIEW_PB1176 || MACH_REALVIEW_PB11MP || MACH_REALVIEW_PBX + default y if ARCH_MX3 + default y if ARCH_MSM select CPU_32v6 select CPU_ABRT_EV6 select CPU_PABRT_NOIFAR @@ -387,7 +390,8 @@ config CPU_32v6K # ARMv7 config CPU_V7 - bool "Support ARM V7 processor" if ARCH_INTEGRATOR || MACH_REALVIEW_EB + bool "Support ARM V7 processor" + depends on ARCH_INTEGRATOR || MACH_REALVIEW_EB || ARCH_OMAP3 || MACH_REALVIEW_PBA8 || MACH_REALVIEW_PBX select CPU_32v6K select CPU_32v7 select CPU_ABRT_EV7 @@ -399,6 +403,19 @@ config CPU_V7 select CPU_COPY_V6 if MMU select CPU_TLB_V7 if MMU +# ARMv7 +config CPU_V7M + bool "Support ARMv7-M processors" + depends on MACH_MPS + select THUMB2_KERNEL + select ARM_THUMB + select CPU_32v7M + select CPU_32v6K + select CPU_ABRT_EV7M + select CPU_PABRT_NOIFAR + select CPU_CACHE_V7M + select CPU_CACHE_VIPT + # Figure out what processor architecture version we should be using. # This defines the compiler instruction set which depends on the machine type. config CPU_32v3 @@ -428,6 +445,9 @@ config CPU_32v6 config CPU_32v7 bool +config CPU_32v7M + bool + # The abort model config CPU_ABRT_NOMMU bool @@ -453,6 +473,9 @@ config CPU_ABRT_EV6 config CPU_ABRT_EV7 bool +config CPU_ABRT_EV7M + bool + config CPU_PABRT_IFAR bool @@ -478,12 +501,20 @@ config CPU_CACHE_V6 config CPU_CACHE_V7 bool +config CPU_CACHE_V7M + bool + config CPU_CACHE_VIVT bool config CPU_CACHE_VIPT bool +config CPU_NO_CACHE_BCAST + bool + depends on SMP + default y if CPU_V6 + if MMU # The copy-page model config CPU_COPY_V3 @@ -569,7 +600,7 @@ comment "Processor Features" config ARM_THUMB bool "Support Thumb user binaries" - depends on CPU_ARM720T || CPU_ARM740T || CPU_ARM920T || CPU_ARM922T || CPU_ARM925T || CPU_ARM926T || CPU_ARM940T || CPU_ARM946E || CPU_ARM1020 || CPU_ARM1020E || CPU_ARM1022 || CPU_ARM1026 || CPU_XSCALE || CPU_XSC3 || CPU_V6 || CPU_V7 || CPU_FEROCEON + depends on CPU_ARM720T || CPU_ARM740T || CPU_ARM920T || CPU_ARM922T || CPU_ARM925T || CPU_ARM926T || CPU_ARM940T || CPU_ARM946E || CPU_ARM1020 || CPU_ARM1020E || CPU_ARM1022 || CPU_ARM1026 || CPU_XSCALE || CPU_XSC3 || CPU_V6 || CPU_V7 || CPU_FEROCEON || CPU_V7M default y help Say Y if you want to include kernel support for running user space @@ -597,6 +628,20 @@ config CPU_BIG_ENDIAN port must properly enable any big-endian related features of your chipset/board/processor. +config CPU_ENDIAN_BE8 + bool + depends on CPU_BIG_ENDIAN + default CPU_V6 || CPU_V7 + help + Support for the BE-8 (big-endian) mode on ARMv6 and ARMv7 processors. + +config CPU_ENDIAN_BE32 + bool + depends on CPU_BIG_ENDIAN + default !CPU_ENDIAN_BE8 + help + Support for the BE-32 (big-endian) mode on pre-ARMv6 processors. + config CPU_HIGH_VECTOR depends on !MMU && CPU_CP15 && !CPU_ARM740T bool "Select the High exception vector" @@ -704,7 +749,7 @@ config CACHE_FEROCEON_L2_WRITETHROUGH config CACHE_L2X0 bool "Enable the L2x0 outer cache controller" - depends on REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || MACH_REALVIEW_PB1176 || REALVIEW_EB_A9MP + depends on REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || MACH_REALVIEW_PB1176 || REALVIEW_EB_A9MP || MACH_REALVIEW_PBX default y select OUTER_CACHE help diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile index 480f78a3611a..9d99f79c8b6c 100644 --- a/arch/arm/mm/Makefile +++ b/arch/arm/mm/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_CPU_ABRT_EV5T) += abort-ev5t.o obj-$(CONFIG_CPU_ABRT_EV5TJ) += abort-ev5tj.o obj-$(CONFIG_CPU_ABRT_EV6) += abort-ev6.o obj-$(CONFIG_CPU_ABRT_EV7) += abort-ev7.o +obj-$(CONFIG_CPU_ABRT_EV7M) += abort-ev7m.o obj-$(CONFIG_CPU_CACHE_V3) += cache-v3.o obj-$(CONFIG_CPU_CACHE_V4) += cache-v4.o @@ -73,6 +74,7 @@ obj-$(CONFIG_CPU_XSC3) += proc-xsc3.o obj-$(CONFIG_CPU_FEROCEON) += proc-feroceon.o obj-$(CONFIG_CPU_V6) += proc-v6.o obj-$(CONFIG_CPU_V7) += proc-v7.o +obj-$(CONFIG_CPU_V7M) += proc-v7m.o obj-$(CONFIG_CACHE_FEROCEON_L2) += cache-feroceon-l2.o obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o diff --git a/arch/arm/mm/abort-ev6.S b/arch/arm/mm/abort-ev6.S index 94077fbd96b7..f332df7f0d37 100644 --- a/arch/arm/mm/abort-ev6.S +++ b/arch/arm/mm/abort-ev6.S @@ -29,14 +29,17 @@ ENTRY(v6_early_abort) mrc p15, 0, r1, c5, c0, 0 @ get FSR mrc p15, 0, r0, c6, c0, 0 @ get FAR /* - * Faulty SWP instruction on 1136 doesn't set bit 11 in DFSR. + * Faulty SWP instruction on 1136 doesn't set bit 11 in DFSR (erratum 326103). * The test below covers all the write situations, including Java bytecodes */ - bic r1, r1, #1 << 11 | 1 << 10 @ clear bits 11 and 10 of FSR + bic r1, r1, #1 << 11 @ clear bit 11 of FSR tst r3, #PSR_J_BIT @ Java? movne pc, lr do_thumb_abort ldreq r3, [r2] @ read aborted ARM instruction +#ifdef CONFIG_CPU_ENDIAN_BE8 + reveq r3, r3 +#endif do_ldrd_abort tst r3, #1 << 20 @ L = 0 -> write orreq r1, r1, #1 << 11 @ yes. diff --git a/arch/arm/mm/abort-ev7m.S b/arch/arm/mm/abort-ev7m.S new file mode 100644 index 000000000000..083e85b27850 --- /dev/null +++ b/arch/arm/mm/abort-ev7m.S @@ -0,0 +1,9 @@ +#include <linux/linkage.h> +#include <asm/assembler.h> +/* + * Function: v7m_early_abort + */ + .align 5 +ENTRY(v7m_early_abort) + mov pc, lr +ENDPROC(v7m_early_abort) diff --git a/arch/arm/mm/alignment.c b/arch/arm/mm/alignment.c index 3a398befed41..b270d6228fe2 100644 --- a/arch/arm/mm/alignment.c +++ b/arch/arm/mm/alignment.c @@ -62,6 +62,12 @@ #define SHIFT_ASR 0x40 #define SHIFT_RORRRX 0x60 +#define BAD_INSTR 0xdeadc0de + +/* Thumb-2 32 bit format per ARMv7 DDI0406A A6.3, either f800h,e800h,f800h */ +#define IS_T32(hi16) \ + (((hi16) & 0xe000) == 0xe000 && ((hi16) & 0x1800)) + static unsigned long ai_user; static unsigned long ai_sys; static unsigned long ai_skipped; @@ -153,7 +159,9 @@ union offset_union { #define __get8_unaligned_check(ins,val,addr,err) \ __asm__( \ - "1: "ins" %1, [%2], #1\n" \ + ARM( "1: "ins" %1, [%2], #1\n" ) \ + THUMB( "1: "ins" %1, [%2]\n" ) \ + THUMB( " add %2, %2, #1\n" ) \ "2:\n" \ " .section .fixup,\"ax\"\n" \ " .align 2\n" \ @@ -209,7 +217,9 @@ union offset_union { do { \ unsigned int err = 0, v = val, a = addr; \ __asm__( FIRST_BYTE_16 \ - "1: "ins" %1, [%2], #1\n" \ + ARM( "1: "ins" %1, [%2], #1\n" ) \ + THUMB( "1: "ins" %1, [%2]\n" ) \ + THUMB( " add %2, %2, #1\n" ) \ " mov %1, %1, "NEXT_BYTE"\n" \ "2: "ins" %1, [%2]\n" \ "3:\n" \ @@ -239,11 +249,17 @@ union offset_union { do { \ unsigned int err = 0, v = val, a = addr; \ __asm__( FIRST_BYTE_32 \ - "1: "ins" %1, [%2], #1\n" \ + ARM( "1: "ins" %1, [%2], #1\n" ) \ + THUMB( "1: "ins" %1, [%2]\n" ) \ + THUMB( " add %2, %2, #1\n" ) \ " mov %1, %1, "NEXT_BYTE"\n" \ - "2: "ins" %1, [%2], #1\n" \ + ARM( "2: "ins" %1, [%2], #1\n" ) \ + THUMB( "2: "ins" %1, [%2]\n" ) \ + THUMB( " add %2, %2, #1\n" ) \ " mov %1, %1, "NEXT_BYTE"\n" \ - "3: "ins" %1, [%2], #1\n" \ + ARM( "3: "ins" %1, [%2], #1\n" ) \ + THUMB( "3: "ins" %1, [%2]\n" ) \ + THUMB( " add %2, %2, #1\n" ) \ " mov %1, %1, "NEXT_BYTE"\n" \ "4: "ins" %1, [%2]\n" \ "5:\n" \ @@ -332,38 +348,48 @@ do_alignment_ldrdstrd(unsigned long addr, unsigned long instr, struct pt_regs *regs) { unsigned int rd = RD_BITS(instr); - - if (((rd & 1) == 1) || (rd == 14)) + unsigned int rd2; + int load; + + if ((instr & 0xfe000000) == 0xe8000000) { + /* ARMv7 Thumb-2 32-bit LDRD/STRD */ + rd2 = (instr >> 8) & 0xf; + load = !!(LDST_L_BIT(instr)); + } else if (((rd & 1) == 1) || (rd == 14)) goto bad; + else { + load = ((instr & 0xf0) == 0xd0); + rd2 = rd + 1; + } ai_dword += 1; if (user_mode(regs)) goto user; - if ((instr & 0xf0) == 0xd0) { + if (load) { unsigned long val; get32_unaligned_check(val, addr); regs->uregs[rd] = val; get32_unaligned_check(val, addr + 4); - regs->uregs[rd + 1] = val; + regs->uregs[rd2] = val; } else { put32_unaligned_check(regs->uregs[rd], addr); - put32_unaligned_check(regs->uregs[rd + 1], addr + 4); + put32_unaligned_check(regs->uregs[rd2], addr + 4); } return TYPE_LDST; user: - if ((instr & 0xf0) == 0xd0) { + if (load) { unsigned long val; get32t_unaligned_check(val, addr); regs->uregs[rd] = val; get32t_unaligned_check(val, addr + 4); - regs->uregs[rd + 1] = val; + regs->uregs[rd2] = val; } else { put32t_unaligned_check(regs->uregs[rd], addr); - put32t_unaligned_check(regs->uregs[rd + 1], addr + 4); + put32t_unaligned_check(regs->uregs[rd2], addr + 4); } return TYPE_LDST; @@ -616,8 +642,72 @@ thumb2arm(u16 tinstr) /* Else fall through for illegal instruction case */ default: - return 0xdeadc0de; + return BAD_INSTR; + } +} + +/* + * Convert Thumb-2 32 bit LDM, STM, LDRD, STRD to equivalent instruction + * handlable by ARM alignment handler, also find the corresponding handler, + * so that we can reuse ARM userland alignment fault fixups for Thumb. + * + * @pinstr: original Thumb-2 instruction; returns new handlable instruction + * @regs: register context. + * @poffset: return offset from faulted addr for later writeback + * + * NOTES: + * 1. Comments below refer to ARMv7 DDI0406A Thumb Instruction sections. + * 2. Register name Rt from ARMv7 is same as Rd from ARMv6 (Rd is Rt) + */ +static void * +do_alignment_t32_to_handler(unsigned long *pinstr, struct pt_regs *regs, + union offset_union *poffset) +{ + unsigned long instr = *pinstr; + u16 tinst1 = (instr >> 16) & 0xffff; + u16 tinst2 = instr & 0xffff; + poffset->un = 0; + + switch (tinst1 & 0xffe0) { + /* A6.3.5 Load/Store multiple */ + case 0xe880: /* STM/STMIA/STMEA,LDM/LDMIA, PUSH/POP T2 */ + case 0xe8a0: /* ...above writeback version */ + case 0xe900: /* STMDB/STMFD, LDMDB/LDMEA */ + case 0xe920: /* ...above writeback version */ + /* no need offset decision since handler calculates it */ + return do_alignment_ldmstm; + + case 0xf840: /* POP/PUSH T3 (single register) */ + if (RN_BITS(instr) == 13 && (tinst2 & 0x09ff) == 0x0904) { + u32 L = !!(LDST_L_BIT(instr)); + const u32 subset[2] = { + 0xe92d0000, /* STMDB sp!,{registers} */ + 0xe8bd0000, /* LDMIA sp!,{registers} */ + }; + *pinstr = subset[L] | (1<<RD_BITS(instr)); + return do_alignment_ldmstm; + } + /* Else fall through for illegal instruction case */ + break; + + /* A6.3.6 Load/store double, STRD/LDRD(immed, lit, reg) */ + case 0xe860: + case 0xe960: + case 0xe8e0: + case 0xe9e0: + poffset->un = (tinst2 & 0xff) << 2; + case 0xe940: + case 0xe9c0: + return do_alignment_ldrdstrd; + + /* + * No need to handle load/store instructions up to word size + * since ARMv6 and later CPUs can perform unaligned accesses. + */ + default: + break; } + return NULL; } static int @@ -630,6 +720,8 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs) mm_segment_t fs; unsigned int fault; u16 tinstr = 0; + int isize = 4; + int thumb2_32b = 0; instrptr = instruction_pointer(regs); @@ -637,8 +729,19 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs) set_fs(KERNEL_DS); if (thumb_mode(regs)) { fault = __get_user(tinstr, (u16 *)(instrptr & ~1)); - if (!(fault)) - instr = thumb2arm(tinstr); + if (!fault) { + if (cpu_architecture() >= CPU_ARCH_ARMv7 && + IS_T32(tinstr)) { + /* Thumb-2 32-bit */ + u16 tinst2 = 0; + fault = __get_user(tinst2, (u16 *)(instrptr+2)); + instr = (tinstr << 16) | tinst2; + thumb2_32b = 1; + } else { + isize = 2; + instr = thumb2arm(tinstr); + } + } } else fault = __get_user(instr, (u32 *)instrptr); set_fs(fs); @@ -655,7 +758,7 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs) fixup: - regs->ARM_pc += thumb_mode(regs) ? 2 : 4; + regs->ARM_pc += isize; switch (CODING_BITS(instr)) { case 0x00000000: /* 3.13.4 load/store instruction extensions */ @@ -714,18 +817,25 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs) handler = do_alignment_ldrstr; break; - case 0x08000000: /* ldm or stm */ - handler = do_alignment_ldmstm; + case 0x08000000: /* ldm or stm, or thumb-2 32bit instruction */ + if (thumb2_32b) + handler = do_alignment_t32_to_handler(&instr, regs, &offset); + else + handler = do_alignment_ldmstm; break; default: goto bad; } + if (!handler) + goto bad; type = handler(addr, instr, regs); - if (type == TYPE_ERROR || type == TYPE_FAULT) + if (type == TYPE_ERROR || type == TYPE_FAULT) { + regs->ARM_pc -= isize; goto bad_or_fault; + } if (type == TYPE_LDST) do_alignment_finish_ldst(addr, instr, regs, offset); @@ -735,7 +845,6 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs) bad_or_fault: if (type == TYPE_ERROR) goto bad; - regs->ARM_pc -= thumb_mode(regs) ? 2 : 4; /* * We got a fault - fix it up, or die. */ @@ -751,8 +860,8 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs) */ printk(KERN_ERR "Alignment trap: not handling instruction " "%0*lx at [<%08lx>]\n", - thumb_mode(regs) ? 4 : 8, - thumb_mode(regs) ? tinstr : instr, instrptr); + isize << 1, + isize == 2 ? tinstr : instr, instrptr); ai_skipped += 1; return 1; @@ -763,8 +872,8 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs) printk("Alignment trap: %s (%d) PC=0x%08lx Instr=0x%0*lx " "Address=0x%08lx FSR 0x%03x\n", current->comm, task_pid_nr(current), instrptr, - thumb_mode(regs) ? 4 : 8, - thumb_mode(regs) ? tinstr : instr, + isize << 1, + isize == 2 ? tinstr : instr, addr, fsr); if (ai_usermode & UM_FIXUP) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index b480f1d3591f..b97081b9cefd 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -27,6 +27,7 @@ static void __iomem *l2x0_base; static DEFINE_SPINLOCK(l2x0_lock); +bool l2x0_disabled; static inline void sync_writel(unsigned long val, unsigned long reg, unsigned long complete_mask) @@ -34,7 +35,11 @@ static inline void sync_writel(unsigned long val, unsigned long reg, unsigned long flags; spin_lock_irqsave(&l2x0_lock, flags); +#ifdef CONFIG_ARM_ERRATA_484863 + asm volatile("swp %0, %0, [%1]\n" : "+r" (val) : "r" (l2x0_base + reg)); +#else writel(val, l2x0_base + reg); +#endif /* wait for the operation to complete */ while (readl(l2x0_base + reg) & complete_mask) ; @@ -97,20 +102,25 @@ void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask) { __u32 aux; - l2x0_base = base; + if (l2x0_disabled) { + printk(KERN_INFO "L2X0 cache controller disabled\n"); + return; + } - /* disable L2X0 */ - writel(0, l2x0_base + L2X0_CTRL); + l2x0_base = base; - aux = readl(l2x0_base + L2X0_AUX_CTRL); - aux &= aux_mask; - aux |= aux_val; - writel(aux, l2x0_base + L2X0_AUX_CTRL); + if (!(readl(l2x0_base + L2X0_CTRL) & 1)) { + /* L2X0 cache controller disabled */ + aux = readl(l2x0_base + L2X0_AUX_CTRL); + aux &= aux_mask; + aux |= aux_val; + writel(aux, l2x0_base + L2X0_AUX_CTRL); - l2x0_inv_all(); + l2x0_inv_all(); - /* enable L2X0 */ - writel(1, l2x0_base + L2X0_CTRL); + /* enable L2X0 */ + writel(1, l2x0_base + L2X0_CTRL); + } outer_cache.inv_range = l2x0_inv_range; outer_cache.clean_range = l2x0_clean_range; @@ -118,3 +128,10 @@ void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask) printk(KERN_INFO "L2X0 cache controller enabled\n"); } + +static int __init l2x0_disable(char *unused) +{ + l2x0_disabled = 1; + return 0; +} +early_param("nol2x0", l2x0_disable); diff --git a/arch/arm/mm/cache-v6.S b/arch/arm/mm/cache-v6.S index 13936b2750f6..b804c9d3e420 100644 --- a/arch/arm/mm/cache-v6.S +++ b/arch/arm/mm/cache-v6.S @@ -12,6 +12,7 @@ #include <linux/linkage.h> #include <linux/init.h> #include <asm/assembler.h> +#include <asm/unwind.h> #include "proc-macros.S" @@ -20,6 +21,39 @@ #define D_CACHE_LINE_SIZE 32 #define BTB_FLUSH_SIZE 8 +#ifdef CONFIG_ARM_ERRATA_411920 +/* + * Invalidate the entire I cache (this code is a workaround for the ARM1136 + * Errata 411920 - Invalidate Instruction Cache operation can fail. This + * Errata is present in 1136, 1156 and 1176. It does not affect the MPCore + * + * Registers: + * r0 - set to 0 + * r1 - corrupted + */ +ENTRY(v6_icache_inval_all) + mov r0, #0 + mrs r1, cpsr + cpsid ifa @ disable interrupts + mcr p15, 0, r0, c7, c5, 0 @ invalidate entire I-cache + mcr p15, 0, r0, c7, c5, 0 @ invalidate entire I-cache + mcr p15, 0, r0, c7, c5, 0 @ invalidate entire I-cache + mcr p15, 0, r0, c7, c5, 0 @ invalidate entire I-cache + msr cpsr_cx, r1 @ restore interrupts + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + mov pc, lr +#endif + /* * v6_flush_cache_all() * @@ -31,8 +65,12 @@ ENTRY(v6_flush_kern_cache_all) mov r0, #0 #ifdef HARVARD_CACHE mcr p15, 0, r0, c7, c14, 0 @ D cache clean+invalidate +#ifndef CONFIG_ARM_ERRATA_411920 mcr p15, 0, r0, c7, c5, 0 @ I+BTB cache invalidate #else + b v6_icache_inval_all +#endif +#else mcr p15, 0, r0, c7, c15, 0 @ Cache clean+invalidate #endif mov pc, lr @@ -92,24 +130,43 @@ ENTRY(v6_coherent_kern_range) * - the Icache does not read data from the write buffer */ ENTRY(v6_coherent_user_range) - + UNWIND(.fnstart ) #ifdef HARVARD_CACHE bic r0, r0, #CACHE_LINE_SIZE - 1 -1: mcr p15, 0, r0, c7, c10, 1 @ clean D line +1: + USER( mcr p15, 0, r0, c7, c10, 1 ) @ clean D line add r0, r0, #CACHE_LINE_SIZE +2: cmp r0, r1 blo 1b #endif mov r0, #0 #ifdef HARVARD_CACHE mcr p15, 0, r0, c7, c10, 4 @ drain write buffer +#ifndef CONFIG_ARM_ERRATA_411920 mcr p15, 0, r0, c7, c5, 0 @ I+BTB cache invalidate #else + b v6_icache_inval_all +#endif +#else mcr p15, 0, r0, c7, c5, 6 @ invalidate BTB #endif mov pc, lr /* + * Fault handling for the cache operation above. If the virtual address in r0 + * isn't mapped, just try the next page. + */ +9001: + mov r0, r0, lsr #12 + mov r0, r0, lsl #12 + add r0, r0, #4096 + b 2b + UNWIND(.fnend ) +ENDPROC(v6_coherent_user_range) +ENDPROC(v6_coherent_kern_range) + +/* * v6_flush_kern_dcache_page(kaddr) * * Ensure that the data held in the page kaddr is written back diff --git a/arch/arm/mm/cache-v7.S b/arch/arm/mm/cache-v7.S index be93ff02a98d..4b733d14076a 100644 --- a/arch/arm/mm/cache-v7.S +++ b/arch/arm/mm/cache-v7.S @@ -13,6 +13,7 @@ #include <linux/linkage.h> #include <linux/init.h> #include <asm/assembler.h> +#include <asm/unwind.h> #include "proc-macros.S" @@ -21,7 +22,7 @@ * * Flush the whole D-cache. * - * Corrupted registers: r0-r5, r7, r9-r11 + * Corrupted registers: r0-r7, r9-r11 (r6 only in Thumb mode) * * - mm - mm_struct describing address space */ @@ -51,8 +52,12 @@ loop1: loop2: mov r9, r4 @ create working copy of max way size loop3: - orr r11, r10, r9, lsl r5 @ factor way and cache number into r11 - orr r11, r11, r7, lsl r2 @ factor index number into r11 + ARM( orr r11, r10, r9, lsl r5 ) @ factor way and cache number into r11 + THUMB( lsl r6, r9, r5 ) + THUMB( orr r11, r10, r6 ) @ factor way and cache number into r11 + ARM( orr r11, r11, r7, lsl r2 ) @ factor index number into r11 + THUMB( lsl r6, r7, r2 ) + THUMB( orr r11, r11, r6 ) @ factor index number into r11 mcr p15, 0, r11, c7, c14, 2 @ clean & invalidate by set/way subs r9, r9, #1 @ decrement the way bge loop3 @@ -82,11 +87,13 @@ ENDPROC(v7_flush_dcache_all) * */ ENTRY(v7_flush_kern_cache_all) - stmfd sp!, {r4-r5, r7, r9-r11, lr} + ARM( stmfd sp!, {r4-r5, r7, r9-r11, lr} ) + THUMB( stmfd sp!, {r4-r7, r9-r11, lr} ) bl v7_flush_dcache_all mov r0, #0 mcr p15, 0, r0, c7, c5, 0 @ I+BTB cache invalidate - ldmfd sp!, {r4-r5, r7, r9-r11, lr} + ARM( ldmfd sp!, {r4-r5, r7, r9-r11, lr} ) + THUMB( ldmfd sp!, {r4-r7, r9-r11, lr} ) mov pc, lr ENDPROC(v7_flush_kern_cache_all) @@ -147,13 +154,16 @@ ENTRY(v7_coherent_kern_range) * - the Icache does not read data from the write buffer */ ENTRY(v7_coherent_user_range) + UNWIND(.fnstart ) dcache_line_size r2, r3 sub r3, r2, #1 bic r0, r0, r3 -1: mcr p15, 0, r0, c7, c11, 1 @ clean D line to the point of unification +1: + USER( mcr p15, 0, r0, c7, c11, 1 ) @ clean D line to the point of unification dsb - mcr p15, 0, r0, c7, c5, 1 @ invalidate I line + USER( mcr p15, 0, r0, c7, c5, 1 ) @ invalidate I line add r0, r0, r2 +2: cmp r0, r1 blo 1b mov r0, #0 @@ -161,6 +171,17 @@ ENTRY(v7_coherent_user_range) dsb isb mov pc, lr + +/* + * Fault handling for the cache operation above. If the virtual address in r0 + * isn't mapped, just try the next page. + */ +9001: + mov r0, r0, lsr #12 + mov r0, r0, lsl #12 + add r0, r0, #4096 + b 2b + UNWIND(.fnend ) ENDPROC(v7_coherent_kern_range) ENDPROC(v7_coherent_user_range) @@ -199,10 +220,12 @@ ENTRY(v7_dma_inv_range) sub r3, r2, #1 tst r0, r3 bic r0, r0, r3 + it ne mcrne p15, 0, r0, c7, c14, 1 @ clean & invalidate D / U line tst r1, r3 bic r1, r1, r3 + it ne mcrne p15, 0, r1, c7, c14, 1 @ clean & invalidate D / U line 1: mcr p15, 0, r0, c7, c6, 1 @ invalidate D / U line diff --git a/arch/arm/mm/context.c b/arch/arm/mm/context.c index fc84fcc74380..661de18634fd 100644 --- a/arch/arm/mm/context.c +++ b/arch/arm/mm/context.c @@ -10,12 +10,17 @@ #include <linux/init.h> #include <linux/sched.h> #include <linux/mm.h> +#include <linux/smp.h> +#include <linux/percpu.h> #include <asm/mmu_context.h> #include <asm/tlbflush.h> static DEFINE_SPINLOCK(cpu_asid_lock); unsigned int cpu_last_asid = ASID_FIRST_VERSION; +#ifdef CONFIG_SMP +DEFINE_PER_CPU(struct mm_struct *, current_mm); +#endif /* * We fork()ed a process, and we need a new context for the child @@ -26,13 +31,105 @@ unsigned int cpu_last_asid = ASID_FIRST_VERSION; void __init_new_context(struct task_struct *tsk, struct mm_struct *mm) { mm->context.id = 0; + spin_lock_init(&mm->context.id_lock); } +static void flush_context(void) +{ + /* set the reserved ASID before flushing the TLB */ + asm("mcr p15, 0, %0, c13, c0, 1\n" : : "r" (0)); + isb(); + local_flush_tlb_all(); + if (icache_is_vivt_asid_tagged()) { + __flush_icache_all(); + dsb(); + } +} + +#ifdef CONFIG_SMP + +static void set_mm_context(struct mm_struct *mm, unsigned int asid) +{ + /* + * Locking needed for multi-threaded applications where the + * same mm->context.id could be set from different CPUs during + * the broadcast. + */ + spin_lock(&mm->context.id_lock); + if (likely((mm->context.id ^ cpu_last_asid) >> ASID_BITS)) { + /* + * Old version of ASID found. Set the new one and + * reset mm->cpu_vm_mask. + */ + mm->context.id = asid; + cpus_clear(mm->cpu_vm_mask); + } + spin_unlock(&mm->context.id_lock); + + /* + * Set the cpu_vm_mask bit for the current CPU. + */ + cpu_set(smp_processor_id(), mm->cpu_vm_mask); +} + +/* + * Reset the ASID on the current CPU. This function call is broadcast + * from the CPU handling the ASID rollover and holding cpu_asid_lock. + */ +static void reset_context(void *info) +{ + unsigned int asid; + unsigned int cpu = smp_processor_id(); + struct mm_struct *mm = per_cpu(current_mm, cpu); + + /* + * Check if a current_mm was set on this CPU as it might still + * be in the early booting stages and using the reserved ASID. + */ + if (!mm) + return; + + smp_rmb(); + asid = cpu_last_asid + cpu + 1; + + flush_context(); + set_mm_context(mm, asid); + + /* set the new ASID */ + asm("mcr p15, 0, %0, c13, c0, 1\n" : : "r" (mm->context.id)); +} + +#else + +static inline void set_mm_context(struct mm_struct *mm, unsigned int asid) +{ + mm->context.id = asid; + mm->cpu_vm_mask = cpumask_of_cpu(smp_processor_id()); +} + +#endif + void __new_context(struct mm_struct *mm) { unsigned int asid; spin_lock(&cpu_asid_lock); +#ifdef CONFIG_SMP + /* + * Check the ASID again, in case the change was broadcast from + * another CPU before we acquired the lock. + */ + if (unlikely(((mm->context.id ^ cpu_last_asid) >> ASID_BITS) == 0)) { + cpu_set(smp_processor_id(), mm->cpu_vm_mask); + spin_unlock(&cpu_asid_lock); + return; + } +#endif + /* + * At this point, it is guaranteed that the current mm (with + * an old ASID) isn't active on any other CPU since the ASIDs + * are changed simultaneously via IPI. + */ asid = ++cpu_last_asid; if (asid == 0) asid = cpu_last_asid = ASID_FIRST_VERSION; @@ -42,23 +139,15 @@ void __new_context(struct mm_struct *mm) * to start a new version and flush the TLB. */ if (unlikely((asid & ~ASID_MASK) == 0)) { - asid = ++cpu_last_asid; - /* set the reserved ASID before flushing the TLB */ - asm("mcr p15, 0, %0, c13, c0, 1 @ set reserved context ID\n" - : - : "r" (0)); - isb(); - flush_tlb_all(); - if (icache_is_vivt_asid_tagged()) { - asm("mcr p15, 0, %0, c7, c5, 0 @ invalidate I-cache\n" - "mcr p15, 0, %0, c7, c5, 6 @ flush BTAC/BTB\n" - : - : "r" (0)); - dsb(); - } + asid = cpu_last_asid + smp_processor_id() + 1; + flush_context(); +#ifdef CONFIG_SMP + smp_wmb(); + smp_call_function(reset_context, NULL, 1); +#endif + cpu_last_asid += NR_CPUS; } - spin_unlock(&cpu_asid_lock); - mm->cpu_vm_mask = cpumask_of_cpu(smp_processor_id()); - mm->context.id = asid; + set_mm_context(mm, asid); + spin_unlock(&cpu_asid_lock); } diff --git a/arch/arm/mm/copypage-v6.c b/arch/arm/mm/copypage-v6.c index 4127a7bddfe5..ed5c6a68fbd9 100644 --- a/arch/arm/mm/copypage-v6.c +++ b/arch/arm/mm/copypage-v6.c @@ -43,6 +43,7 @@ static void v6_copy_user_highpage_nonaliasing(struct page *to, copy_page(kto, kfrom); kunmap_atomic(kto, KM_USER1); kunmap_atomic(kfrom, KM_USER0); + __cpuc_flush_dcache_page(kto); } /* diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index f1ef5613ccd4..e37782419831 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -35,7 +35,13 @@ #define CONSISTENT_PTE_INDEX(x) (((unsigned long)(x) - CONSISTENT_BASE) >> PGDIR_SHIFT) #define NUM_CONSISTENT_PTES (CONSISTENT_DMA_SIZE >> PGDIR_SHIFT) +#ifdef CONFIG_MMU +#define arch_is_nommu() 0 +#else +#define arch_is_nommu() 1 +#endif +#ifdef CONFIG_MMU /* * These are the page tables (2MB each) covering uncached, DMA consistent allocations */ @@ -67,8 +73,7 @@ static DEFINE_SPINLOCK(consistent_lock); * .vm_end = VMALLOC_END, * }; * - * However, vmalloc_head.vm_start is variable (typically, it is dependent on - * the amount of RAM found at boot time.) I would imagine that get_vm_area() + * However, vmalloc_head.vm_start is variable (typically, it is depen* the amount of RAM found at boot time.) I would imagine that get_vm_area() * would have to initialise this each time prior to calling vm_region_alloc(). */ struct arm_vm_region { @@ -139,6 +144,7 @@ static struct arm_vm_region *arm_vm_region_find(struct arm_vm_region *head, unsi out: return c; } +#endif /* CONFIG_MMU */ #ifdef CONFIG_HUGETLB_PAGE #error ARM Coherent DMA allocator does not (yet) support huge TLB @@ -148,6 +154,7 @@ static void * __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp, pgprot_t prot) { +#ifdef CONFIG_MMU struct page *page; struct arm_vm_region *c; unsigned long order; @@ -207,7 +214,7 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp, { void *ptr = page_address(page); memset(ptr, 0, size); - dmac_flush_range(ptr, ptr + size); + smp_dma_flush_range(ptr, ptr + size); outer_flush_range(__pa(ptr), __pa(ptr) + size); } @@ -264,6 +271,7 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp, __free_pages(page, order); no_page: *handle = ~0; +#endif /* CONFIG_MMU */ return NULL; } @@ -279,7 +287,7 @@ dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gf if (dma_alloc_from_coherent(dev, size, handle, &memory)) return memory; - if (arch_is_coherent()) { + if (arch_is_coherent() || arch_is_nommu()) { void *virt; virt = kmalloc(size, gfp); @@ -302,6 +310,9 @@ EXPORT_SYMBOL(dma_alloc_coherent); void * dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp) { + if (arch_is_nommu()) + return dma_alloc_coherent(dev, size, handle, gfp); + return __dma_alloc(dev, size, handle, gfp, pgprot_writecombine(pgprot_kernel)); } @@ -310,6 +321,8 @@ EXPORT_SYMBOL(dma_alloc_writecombine); static int dma_mmap(struct device *dev, struct vm_area_struct *vma, void *cpu_addr, dma_addr_t dma_addr, size_t size) { + int ret = -ENXIO; +#ifdef CONFIG_MMU unsigned long flags, user_size, kern_size; struct arm_vm_region *c; int ret = -ENXIO; @@ -333,6 +346,7 @@ static int dma_mmap(struct device *dev, struct vm_area_struct *vma, vma->vm_page_prot); } } +#endif /* CONFIG_MMU */ return ret; } @@ -359,22 +373,25 @@ EXPORT_SYMBOL(dma_mmap_writecombine); */ void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t handle) { +#ifdef CONFIG_MMU struct arm_vm_region *c; unsigned long flags, addr; pte_t *ptep; int idx; u32 off; +#endif WARN_ON(irqs_disabled()); if (dma_release_from_coherent(dev, get_order(size), cpu_addr)) return; - if (arch_is_coherent()) { + if (arch_is_coherent() || arch_is_nommu()) { kfree(cpu_addr); return; } +#ifdef CONFIG_MMU size = PAGE_ALIGN(size); spin_lock_irqsave(&consistent_lock, flags); @@ -442,6 +459,7 @@ void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr printk(KERN_ERR "%s: trying to free invalid coherent area: %p\n", __func__, cpu_addr); dump_stack(); +#endif /* CONFIG_MMU */ } EXPORT_SYMBOL(dma_free_coherent); @@ -450,10 +468,12 @@ EXPORT_SYMBOL(dma_free_coherent); */ static int __init consistent_init(void) { + int ret = 0; +#ifdef CONFIG_MMU pgd_t *pgd; pmd_t *pmd; pte_t *pte; - int ret = 0, i = 0; + int i = 0; u32 base = CONSISTENT_BASE; do { @@ -476,6 +496,7 @@ static int __init consistent_init(void) consistent_pte[i++] = pte; base += (1 << PGDIR_SHIFT); } while (base < CONSISTENT_END); +#endif /* !CONFIG_MMU */ return ret; } diff --git a/arch/arm/mm/fault-armv.c b/arch/arm/mm/fault-armv.c index bc0099d5ae85..d0d17b6a3703 100644 --- a/arch/arm/mm/fault-armv.c +++ b/arch/arm/mm/fault-armv.c @@ -153,14 +153,11 @@ void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, pte_t pte) page = pfn_to_page(pfn); mapping = page_mapping(page); - if (mapping) { #ifndef CONFIG_SMP - int dirty = test_and_clear_bit(PG_dcache_dirty, &page->flags); - - if (dirty) - __flush_dcache_page(mapping, page); + if (test_and_clear_bit(PG_dcache_dirty, &page->flags)) + __flush_dcache_page(mapping, page); #endif - + if (mapping) { if (cache_is_vivt()) make_coherent(mapping, vma, addr, pfn); else if (vma->vm_flags & VM_EXEC) diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index 0455557a2899..91b0dbba6cfe 100644 --- a/arch/arm/mm/fault.c +++ b/arch/arm/mm/fault.c @@ -23,6 +23,7 @@ #include "fault.h" +#ifdef CONFIG_MMU #ifdef CONFIG_KPROBES static inline int notify_page_fault(struct pt_regs *regs, unsigned int fsr) @@ -97,6 +98,10 @@ void show_pte(struct mm_struct *mm, unsigned long addr) printk("\n"); } +#else /* CONFIG_MMU */ +void show_pte(struct mm_struct *mm, unsigned long addr) +{ } +#endif /* CONFIG_MMU */ /* * Oops. The kernel tried to access some page that wasn't present. @@ -171,6 +176,7 @@ void do_bad_area(unsigned long addr, unsigned int fsr, struct pt_regs *regs) __do_kernel_fault(mm, addr, fsr, regs); } +#ifdef CONFIG_MMU #define VM_FAULT_BADMAP 0x010000 #define VM_FAULT_BADACCESS 0x020000 @@ -322,6 +328,13 @@ no_context: __do_kernel_fault(mm, addr, fsr, regs); return 0; } +#else /* CONFIG_MMU */ +static int +do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) +{ + return 0; +} +#endif /* CONFIG_MMU */ /* * First Level Translation Fault Handler @@ -340,6 +353,7 @@ no_context: * interrupt or a critical region, and should only copy the information * from the master page table, nothing more. */ +#ifdef CONFIG_MMU static int __kprobes do_translation_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) @@ -378,6 +392,14 @@ bad_area: do_bad_area(addr, fsr, regs); return 0; } +#else /* CONFIG_MMU */ +static int +do_translation_fault(unsigned long addr, unsigned int fsr, + struct pt_regs *regs) +{ + return 0; +} +#endif /* CONFIG_MMU */ /* * Some section permission faults need to be handled gracefully. diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c index 0fa9bf388f0b..03e37fa1c294 100644 --- a/arch/arm/mm/flush.c +++ b/arch/arm/mm/flush.c @@ -32,10 +32,14 @@ static void flush_pfn_alias(unsigned long pfn, unsigned long vaddr) asm( "mcrr p15, 0, %1, %0, c14\n" " mcr p15, 0, %2, c7, c10, 4\n" +#ifndef CONFIG_ARM_ERRATA_411920 " mcr p15, 0, %2, c7, c5, 0\n" +#else + " bl v6_icache_inval_all\n" +#endif : : "r" (to), "r" (to + PAGE_SIZE - L1_CACHE_BYTES), "r" (zero) - : "cc"); + : "r0", "r1", "lr"); } void flush_cache_mm(struct mm_struct *mm) @@ -48,11 +52,15 @@ void flush_cache_mm(struct mm_struct *mm) if (cache_is_vipt_aliasing()) { asm( "mcr p15, 0, %0, c7, c14, 0\n" + " mcr p15, 0, %0, c7, c10, 4\n" +#ifndef CONFIG_ARM_ERRATA_411920 " mcr p15, 0, %0, c7, c5, 0\n" - " mcr p15, 0, %0, c7, c10, 4" +#else + " bl v6_icache_inval_all\n" +#endif : : "r" (0) - : "cc"); + : "r0", "r1", "lr", "cc"); } } @@ -67,11 +75,15 @@ void flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned if (cache_is_vipt_aliasing()) { asm( "mcr p15, 0, %0, c7, c14, 0\n" + " mcr p15, 0, %0, c7, c10, 4\n" +#ifndef CONFIG_ARM_ERRATA_411920 " mcr p15, 0, %0, c7, c5, 0\n" - " mcr p15, 0, %0, c7, c10, 4" +#else + " bl v6_icache_inval_all\n" +#endif : : "r" (0) - : "cc"); + : "r0", "r1", "lr", "cc"); } } @@ -107,8 +119,7 @@ void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, } /* VIPT non-aliasing cache */ - if (cpu_isset(smp_processor_id(), vma->vm_mm->cpu_vm_mask) && - vma->vm_flags & VM_EXEC) { + if (vma->vm_flags & VM_EXEC) { unsigned long addr = (unsigned long)kaddr; /* only flushing the kernel mapping on non-aliasing VIPT */ __cpuc_coherent_kern_range(addr, addr + len); diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 80fd3b69ae1f..30e684309139 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -422,7 +422,7 @@ free_memmap(int node, unsigned long start_pfn, unsigned long end_pfn) /* * Convert start_pfn/end_pfn to a struct page pointer. */ - start_pg = pfn_to_page(start_pfn); + start_pg = pfn_to_page(start_pfn - 1) + 1; end_pg = pfn_to_page(end_pfn); /* diff --git a/arch/arm/mm/nommu.c b/arch/arm/mm/nommu.c index ad7bacc693b2..b62c6a5b8619 100644 --- a/arch/arm/mm/nommu.c +++ b/arch/arm/mm/nommu.c @@ -12,6 +12,7 @@ #include <asm/cacheflush.h> #include <asm/sections.h> #include <asm/page.h> +#include <asm/setup.h> #include <asm/mach/arch.h> #include "mm.h" @@ -33,6 +34,7 @@ void __init reserve_node_zero(pg_data_t *pgdat) BOOTMEM_DEFAULT); #endif +#ifndef CONFIG_CPU_V7M /* * Register the exception vector page. * some architectures which the DRAM is the exception vector to trap, @@ -40,6 +42,7 @@ void __init reserve_node_zero(pg_data_t *pgdat) */ reserve_bootmem_node(pgdat, CONFIG_VECTORS_BASE, PAGE_SIZE, BOOTMEM_DEFAULT); +#endif } /* diff --git a/arch/arm/mm/proc-macros.S b/arch/arm/mm/proc-macros.S index 54b1f721dec8..f1559c227784 100644 --- a/arch/arm/mm/proc-macros.S +++ b/arch/arm/mm/proc-macros.S @@ -77,6 +77,7 @@ * Sanity check the PTE configuration for the code below - which makes * certain assumptions about how these bits are layed out. */ +#ifdef CONFIG_MMU #if L_PTE_SHARED != PTE_EXT_SHARED #error PTE shared bit mismatch #endif @@ -90,6 +91,7 @@ L_PTE_FILE+L_PTE_PRESENT) > L_PTE_SHARED #error Invalid Linux PTE bit settings #endif +#endif /* CONFIG_MMU */ /* * The ARMv6 and ARMv7 set_pte_ext translation function. diff --git a/arch/arm/mm/proc-v6.S b/arch/arm/mm/proc-v6.S index f0cc599facb7..7959567970c7 100644 --- a/arch/arm/mm/proc-v6.S +++ b/arch/arm/mm/proc-v6.S @@ -169,9 +169,28 @@ __v6_setup: #endif /* CONFIG_MMU */ adr r5, v6_crval ldmia r5, {r5, r6} +#ifdef CONFIG_CPU_ENDIAN_BE8 + orr r6, r6, #1 << 25 @ big-endian page tables +#endif mrc p15, 0, r0, c1, c0, 0 @ read control register bic r0, r0, r5 @ clear bits them orr r0, r0, r6 @ set them +#ifdef CONFIG_ARM_ERRATA_364296 + /* Workaround for the 364296 ARM1136 r0pX errata (possible cache data + * corruption with hit-under-miss enabled). The conditional code below + * (setting the undocumented bit 31 in the auxiliary control register + * and the FI bit in the control register) disables hit-under-miss + * without putting the processor into full low interrupt latency mode. + */ + ldr r6, =0x4107b360 @ id for ARM1136 r0pX + mrc p15, 0, r5, c0, c0, 0 @ get processor id + bic r5, r5, #0xf @ mask out part bits [3:0] + teq r5, r6 @ check for the faulty core + mrceq p15, 0, r5, c1, c0, 1 @ load aux control reg + orreq r5, r5, #(1 << 31) @ set the undocumented bit 31 + mcreq p15, 0, r5, c1, c0, 1 @ write aux control reg + orreq r0, r0, #(1 << 21) @ low interrupt latency configuration +#endif mov pc, lr @ return to head.S:__ret /* diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S index d1ebec42521d..fd90b1b55e13 100644 --- a/arch/arm/mm/proc-v7.S +++ b/arch/arm/mm/proc-v7.S @@ -18,12 +18,24 @@ #include "proc-macros.S" -#define TTB_C (1 << 0) #define TTB_S (1 << 1) #define TTB_RGN_NC (0 << 3) #define TTB_RGN_OC_WBWA (1 << 3) #define TTB_RGN_OC_WT (2 << 3) #define TTB_RGN_OC_WB (3 << 3) +#define TTB_NOS (1 << 5) +#define TTB_IRGN_NC ((0 << 0) | (0 << 6)) +#define TTB_IRGN_WBWA ((0 << 0) | (1 << 6)) +#define TTB_IRGN_WT ((1 << 0) | (0 << 6)) +#define TTB_IRGN_WB ((1 << 0) | (1 << 6)) + +#ifndef CONFIG_SMP +/* PTWs cacheable, inner WB not shareable, outer WB not shareable */ +#define TTB_FLAGS TTB_IRGN_WB|TTB_RGN_OC_WB +#else +/* PTWs cacheable, inner WBWA shareable, outer WBWA not shareable */ +#define TTB_FLAGS TTB_IRGN_WBWA|TTB_S|TTB_NOS|TTB_RGN_OC_WBWA +#endif #ifndef CONFIG_SMP #define TTB_FLAGS TTB_C|TTB_RGN_OC_WB @ mark PTWs cacheable, outer WB @@ -95,6 +107,9 @@ ENTRY(cpu_v7_switch_mm) mov r2, #0 ldr r1, [r1, #MM_CONTEXT_ID] @ get mm->context.id orr r0, r0, #TTB_FLAGS +#ifdef CONFIG_ARM_ERRATA_430973 + mcr p15, 0, r2, c7, c5, 6 @ flush BTAC/BTB +#endif mcr p15, 0, r2, c13, c0, 1 @ set reserved context ID isb 1: mcr p15, 0, r0, c2, c0, 0 @ set TTB 0 @@ -117,7 +132,9 @@ ENDPROC(cpu_v7_switch_mm) */ ENTRY(cpu_v7_set_pte_ext) #ifdef CONFIG_MMU - str r1, [r0], #-2048 @ linux version + ARM( str r1, [r0], #-2048 ) @ linux version + THUMB( str r1, [r0] ) @ linux version + THUMB( sub r0, r0, #2048 ) bic r3, r1, #0x000003f0 bic r3, r3, #PTE_TYPE_MASK @@ -125,21 +142,26 @@ ENTRY(cpu_v7_set_pte_ext) orr r3, r3, #PTE_EXT_AP0 | 2 tst r1, #1 << 4 + it ne orrne r3, r3, #PTE_EXT_TEX(1) tst r1, #L_PTE_WRITE + ite ne tstne r1, #L_PTE_DIRTY orreq r3, r3, #PTE_EXT_APX tst r1, #L_PTE_USER + ittt ne orrne r3, r3, #PTE_EXT_AP1 tstne r3, #PTE_EXT_APX bicne r3, r3, #PTE_EXT_APX | PTE_EXT_AP0 tst r1, #L_PTE_EXEC + it eq orreq r3, r3, #PTE_EXT_XN tst r1, #L_PTE_YOUNG + ite ne tstne r1, #L_PTE_PRESENT moveq r3, #0 @@ -173,13 +195,31 @@ cpu_v7_name: __v7_setup: #ifdef CONFIG_SMP mrc p15, 0, r0, c1, c0, 1 @ Enable SMP/nAMP mode - orr r0, r0, #(0x1 << 6) - mcr p15, 0, r0, c1, c0, 1 + tst r0, #(0x1 << 6) @ already enabled? + itt eq + orreq r0, r0, #(1 << 6) | (1 << 0) + mcreq p15, 0, r0, c1, c0, 1 #endif adr r12, __v7_setup_stack @ the local stack stmia r12, {r0-r5, r7, r9, r11, lr} bl v7_flush_dcache_all ldmia r12, {r0-r5, r7, r9, r11, lr} +#ifdef CONFIG_ARM_ERRATA_430973 + mrc p15, 0, r10, c1, c0, 1 @ read aux control register + orr r10, r10, #(1 << 6) @ set IBE to 1 + mcr p15, 0, r10, c1, c0, 1 @ write aux control register +#endif +#ifdef CONFIG_ARM_ERRATA_458693 + mrc p15, 0, r10, c1, c0, 1 @ read aux control register + orr r10, r10, #(1 << 5) @ set L1NEON to 1 + orr r10, r10, #(1 << 9) @ set PLDNOP to 1 + mcr p15, 0, r10, c1, c0, 1 @ write aux control register +#endif +#ifdef CONFIG_ARM_ERRATA_460075 + mrc p15, 1, r10, c9, c0, 2 @ read L2 cache aux ctrl register + orr r10, r10, #(1 << 22) @ set the Write Allocate disable bit + mcr p15, 1, r10, c9, c0, 2 @ write the L2 cache aux ctrl register +#endif mov r10, #0 #ifdef HARVARD_CACHE mcr p15, 0, r10, c7, c5, 0 @ I+BTB cache invalidate @@ -192,13 +232,16 @@ __v7_setup: mcr p15, 0, r4, c2, c0, 1 @ load TTB1 mov r10, #0x1f @ domains 0, 1 = manager mcr p15, 0, r10, c3, c0, 0 @ load domain access register -#endif ldr r5, =0xff0aa1a8 ldr r6, =0x40e040e0 mcr p15, 0, r5, c10, c2, 0 @ write PRRR mcr p15, 0, r6, c10, c2, 1 @ write NMRR +#endif adr r5, v7_crval ldmia r5, {r5, r6} +#ifdef CONFIG_CPU_ENDIAN_BE8 + orr r6, r6, #1 << 25 @ big-endian page tables +#endif mrc p15, 0, r0, c1, c0, 0 @ read control register bic r0, r0, r5 @ clear bits them orr r0, r0, r6 @ set them @@ -206,14 +249,15 @@ __v7_setup: ENDPROC(__v7_setup) /* AT - * TFR EV X F I D LR - * .EEE ..EE PUI. .T.T 4RVI ZFRS BLDP WCAM + * TFR EV X F I D LR S + * .EEE ..EE PUI. .T.T 4RVI ZWRS BLDP WCAM * rxxx rrxx xxx0 0101 xxxx xxxx x111 xxxx < forced - * 1 0 110 0011 1.00 .111 1101 < we want + * 1 0 110 0011 1100 .111 1101 < we want */ .type v7_crval, #object v7_crval: - crval clear=0x0120c302, mmuset=0x10c0387d, ucset=0x00c0187c + ARM( crval clear=0x0120c302, mmuset=0x10c03c7d, ucset=0x00c01c7c ) + THUMB( crval clear=0x0120c302, mmuset=0x50c03c7d, ucset=0x40c01c7c ) __v7_setup_stack: .space 4 * 11 @ 11 registers diff --git a/arch/arm/mm/proc-v7m.S b/arch/arm/mm/proc-v7m.S new file mode 100644 index 000000000000..ff97c011ae60 --- /dev/null +++ b/arch/arm/mm/proc-v7m.S @@ -0,0 +1,184 @@ +/* + * linux/arch/arm/mm/proc-v7m.S + * + * Copyright (C) 2008 ARM Ltd. + * Copyright (C) 2001 Deep Blue Solutions Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This is the "shell" of the ARMv7-M processor support. + */ +#include <linux/linkage.h> +#include <asm/assembler.h> + +ENTRY(cpu_v7m_proc_init) + mov pc, lr +ENDPROC(cpu_v7m_proc_init) + +ENTRY(cpu_v7m_proc_fin) + mov pc, lr +ENDPROC(cpu_v7m_proc_fin) + +/* + * cpu_v7m_reset(loc) + * + * Perform a soft reset of the system. Put the CPU into the + * same state as it would be if it had been reset, and branch + * to what would be the reset vector. + * + * - loc - location to jump to for soft reset + * + * It is assumed that: + */ + .align 5 +ENTRY(cpu_v7m_reset) + mov pc, r0 +ENDPROC(cpu_v7m_reset) + +/* + * cpu_v7m_do_idle() + * + * Idle the processor (eg, wait for interrupt). + * + * IRQs are already disabled. + */ +ENTRY(cpu_v7m_do_idle) + wfi + mov pc, lr +ENDPROC(cpu_v7m_do_idle) + +ENTRY(cpu_v7m_dcache_clean_area) + mov pc, lr +ENDPROC(cpu_v7m_dcache_clean_area) + +/* + * cpu_v7m_switch_mm(pgd_phys, tsk) + * + * Set the translation table base pointer to be pgd_phys + * + * - pgd_phys - physical address of new TTB + * + * It is assumed that: + * - we are not using split page tables + */ +ENTRY(cpu_v7m_switch_mm) + mov pc, lr +ENDPROC(cpu_v7m_switch_mm) + +ENTRY(cpu_v7m_set_pte_ext) + mov pc, lr +ENDPROC(cpu_v7m_set_pte_ext) + +cpu_v7m_name: + .ascii "ARMv7-M Processor" + .align + + .section ".text.init", #alloc, #execinstr + +/* + * __v7m_setup + * + * Initialise TLB, Caches, and MMU state ready to switch the MMU + * on. Return in r0 the new CP15 C1 control register setting. + * + * We automatically detect if we have a Harvard cache, and use the + * Harvard cache control instructions insead of the unified cache + * control instructions. + * + * This should be able to cover all ARMv7-M cores. + * + * It is assumed that: + * - cache type register is implemented + */ +__v7m_setup: + @ Configure the vector table base address + ldr r0, =0xe000ed08 @ vector table base address + ldr r12, =vector_table + str r12, [r0] + + @ Lower the priority of the SVC and PendSV exceptions + ldr r0, =0xe000ed1c + mov r5, #0x80000000 + str r5, [r0] @ set SVC priority + ldr r0, =0xe000ed20 + mov r5, #0x00800000 + str r5, [r0] @ set PendSV priority + + @ SVC to run the kernel in this mode + adr r0, BSYM(1f) + ldr r5, [r12, #11 * 4] @ read the SVC vector entry + str r0, [r12, #11 * 4] @ write the temporary SVC vector entry + mov r6, lr @ save LR + mov r7, sp @ save SP + ldr sp, =__v7m_setup_stack_top + cpsie i + svc #0 +1: cpsid i + str r5, [r12, #11 * 4] @ restore the original SVC vector entry + mov lr, r6 @ restore LR + mov sp, r7 @ restore SP + + @ Special-purpose control register + mov r0, #1 + msr control, r0 @ Thread mode has unpriviledged access + + @ Configure the System Control Register + ldr r0, =0xe000ed14 @ system control register + ldr r12, [r0] + orr r12, #1 << 9 @ STKALIGN + str r12, [r0] + mov pc, lr +ENDPROC(__v7m_setup) + + .align 2 + .type v7m_processor_functions, #object +ENTRY(v7m_processor_functions) + .word v7m_early_abort + .word cpu_v7m_proc_init + .word cpu_v7m_proc_fin + .word cpu_v7m_reset + .word cpu_v7m_do_idle + .word cpu_v7m_dcache_clean_area + .word cpu_v7m_switch_mm + .word cpu_v7m_set_pte_ext + .word pabort_noifar + .size v7m_processor_functions, . - v7m_processor_functions + + .type cpu_arch_name, #object +cpu_arch_name: + .asciz "armv7m" + .size cpu_arch_name, . - cpu_arch_name + + .type cpu_elf_name, #object +cpu_elf_name: + .asciz "v7m" + .size cpu_elf_name, . - cpu_elf_name + .align + + .section ".proc.info.init", #alloc, #execinstr + + /* + * Match any ARMv7-M processor core. + */ + .type __v7m_proc_info, #object +__v7m_proc_info: + .long 0x000f0000 @ Required ID value + .long 0x000f0000 @ Mask for ID + .long 0 @ proc_info_list.__cpu_mm_mmu_flags + .long 0 @ proc_info_list.__cpu_io_mmu_flags + b __v7m_setup @ proc_info_list.__cpu_flush + .long cpu_arch_name + .long cpu_elf_name + .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP + .long cpu_v7m_name + .long v7m_processor_functions @ proc_info_list.proc + .long 0 @ proc_info_list.tlb + .long 0 @ proc_info_list.user + .long 0 @ proc_info_list.cache + .size __v7m_proc_info, . - __v7m_proc_info + +__v7m_setup_stack: + .space 4 * 8 @ 8 registers +__v7m_setup_stack_top: diff --git a/arch/arm/mm/tlb-v7.S b/arch/arm/mm/tlb-v7.S index 24ba5109f2e7..010c4781615a 100644 --- a/arch/arm/mm/tlb-v7.S +++ b/arch/arm/mm/tlb-v7.S @@ -41,9 +41,11 @@ ENTRY(v7wbi_flush_user_tlb_range) mov r1, r1, lsl #PAGE_SHIFT vma_vm_flags r2, r2 @ get vma->vm_flags 1: - mcr p15, 0, r0, c8, c6, 1 @ TLB invalidate D MVA (was 1) - tst r2, #VM_EXEC @ Executable area ? - mcrne p15, 0, r0, c8, c5, 1 @ TLB invalidate I MVA (was 1) +#ifdef CONFIG_SMP + mcr p15, 0, r0, c8, c3, 1 @ TLB invalidate U MVA (shareable) +#else + mcr p15, 0, r0, c8, c7, 1 @ TLB invalidate U MVA +#endif add r0, r0, #PAGE_SZ cmp r0, r1 blo 1b @@ -68,8 +70,11 @@ ENTRY(v7wbi_flush_kern_tlb_range) mov r0, r0, lsl #PAGE_SHIFT mov r1, r1, lsl #PAGE_SHIFT 1: - mcr p15, 0, r0, c8, c6, 1 @ TLB invalidate D MVA - mcr p15, 0, r0, c8, c5, 1 @ TLB invalidate I MVA +#ifdef CONFIG_SMP + mcr p15, 0, r0, c8, c3, 1 @ TLB invalidate U MVA (shareable) +#else + mcr p15, 0, r0, c8, c7, 1 @ TLB invalidate U MVA +#endif add r0, r0, #PAGE_SZ cmp r0, r1 blo 1b @@ -86,5 +91,5 @@ ENDPROC(v7wbi_flush_kern_tlb_range) ENTRY(v7wbi_tlb_fns) .long v7wbi_flush_user_tlb_range .long v7wbi_flush_kern_tlb_range - .long v6wbi_tlb_flags + .long v7wbi_tlb_flags .size v7wbi_tlb_fns, . - v7wbi_tlb_fns diff --git a/arch/arm/oprofile/Makefile b/arch/arm/oprofile/Makefile index 88e31f549f50..1593e3753549 100644 --- a/arch/arm/oprofile/Makefile +++ b/arch/arm/oprofile/Makefile @@ -6,9 +6,7 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ oprofilefs.o oprofile_stats.o \ timer_int.o ) -oprofile-y := $(DRIVER_OBJS) common.o backtrace.o +oprofile-y := $(DRIVER_OBJS) common.o backtrace.o op_arm11.o op_v7.o op_model_v6-7.o +oprofile-$(CONFIG_CACHE_L2X0) += op_l2x0.o +oprofile-$(CONFIG_SMP) += op_scu.o oprofile-$(CONFIG_CPU_XSCALE) += op_model_xscale.o -oprofile-$(CONFIG_OPROFILE_ARM11_CORE) += op_model_arm11_core.o -oprofile-$(CONFIG_OPROFILE_ARMV6) += op_model_v6.o -oprofile-$(CONFIG_OPROFILE_MPCORE) += op_model_mpcore.o -oprofile-$(CONFIG_OPROFILE_ARMV7) += op_model_v7.o diff --git a/arch/arm/oprofile/backtrace.c b/arch/arm/oprofile/backtrace.c index cefc21c2eee4..d805a52b5032 100644 --- a/arch/arm/oprofile/backtrace.c +++ b/arch/arm/oprofile/backtrace.c @@ -18,15 +18,14 @@ #include <linux/mm.h> #include <linux/uaccess.h> #include <asm/ptrace.h> - -#include "../kernel/stacktrace.h" +#include <asm/stacktrace.h> static int report_trace(struct stackframe *frame, void *d) { unsigned int *depth = d; if (*depth) { - oprofile_add_trace(frame->lr); + oprofile_add_trace(frame->pc); (*depth)--; } @@ -70,9 +69,12 @@ void arm_backtrace(struct pt_regs * const regs, unsigned int depth) struct frame_tail *tail = ((struct frame_tail *) regs->ARM_fp) - 1; if (!user_mode(regs)) { - unsigned long base = ((unsigned long)regs) & ~(THREAD_SIZE - 1); - walk_stackframe(regs->ARM_fp, base, base + THREAD_SIZE, - report_trace, &depth); + struct stackframe frame; + frame.fp = regs->ARM_fp; + frame.sp = regs->ARM_sp; + frame.lr = regs->ARM_lr; + frame.pc = regs->ARM_pc; + walk_stackframe(&frame, report_trace, &depth); return; } diff --git a/arch/arm/oprofile/common.c b/arch/arm/oprofile/common.c index 3fcd752d6146..2680f4d4b2f3 100644 --- a/arch/arm/oprofile/common.c +++ b/arch/arm/oprofile/common.c @@ -14,6 +14,10 @@ #include <linux/sysdev.h> #include <linux/mutex.h> +#include <asm/system.h> +#include <asm/cputype.h> +#include <asm/io.h> + #include "op_counter.h" #include "op_arm_model.h" @@ -130,24 +134,37 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) { struct op_arm_model_spec *spec = NULL; int ret = -ENODEV; + int cpu_arch = cpu_architecture(); ops->backtrace = arm_backtrace; -#ifdef CONFIG_CPU_XSCALE + if (cpu_is_xscale()) spec = &op_xscale_spec; -#endif - -#ifdef CONFIG_OPROFILE_ARMV6 + else if (cpu_is_11mpcore() || cpu_arch == CPU_ARCH_ARMv6) { + /* cpu_architecture returns V7 for MPCore! */ spec = &op_armv6_spec; -#endif - -#ifdef CONFIG_OPROFILE_MPCORE - spec = &op_mpcore_spec; -#endif - -#ifdef CONFIG_OPROFILE_ARMV7 + if (cpu_is_11mpcore()) + spec->name = "arm/11mpcore"; + } + else if (cpu_arch == CPU_ARCH_ARMv7) { spec = &op_armv7_spec; -#endif + /* + * V7 CPUs all have the same kind of PMUs, but have a variable + * number of them. So the kernel side of Oprofile only needs + * to know whether we have the L2x0, and whether we're SMP. + * The user side needs more information, to decide which + * events file to use because, for example, some A8 event + * numbers differ from A9 event numbers). + */ + if (cpu_is_a9()) { + if (is_smp()) + spec->name = "arm/a9mpcore"; + else + spec->name = "arm/a9"; + } + else + spec->name = "arm/a8"; + } if (spec) { ret = spec->init(); @@ -167,7 +184,7 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) ops->start = op_arm_start; ops->stop = op_arm_stop; ops->cpu_type = op_arm_model->name; - printk(KERN_INFO "oprofile: using %s\n", spec->name); + printk(KERN_INFO "oprofile: cpu_architecture() returns 0x%x, using %s model\n", cpu_arch, spec->name); } return ret; diff --git a/arch/arm/oprofile/op_arm11.c b/arch/arm/oprofile/op_arm11.c new file mode 100644 index 000000000000..4daa0084df15 --- /dev/null +++ b/arch/arm/oprofile/op_arm11.c @@ -0,0 +1,164 @@ +/** + * @file op_arm11.c + * ARM11 CP15 Performance Monitor Unit Driver + * + * @remark Copyright 2004-7 ARM SMP Development Team + */ +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/oprofile.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/smp.h> + +#include "op_counter.h" +#include "op_arm_model.h" +#include "op_arm11.h" + +static inline void arm11_write_pmnc(u32 val) +{ + /* upper 4bits and 7, 11 are write-as-0 */ + val &= 0x0ffff77f; + asm volatile("mcr p15, 0, %0, c15, c12, 0" : : "r" (val)); +} + +static inline u32 arm11_read_pmnc(void) +{ + u32 val; + asm volatile("mrc p15, 0, %0, c15, c12, 0" : "=r" (val)); + return val; +} + +static void arm11_reset_counter(unsigned int cnt) +{ + u32 val = -(u32)counter_config[COUNTER_CPUn_PMNm(smp_processor_id(), cnt)].count; + switch (cnt) { + case CCNT: + asm volatile("mcr p15, 0, %0, c15, c12, 1" : : "r" (val)); + break; + + case PMN0: + asm volatile("mcr p15, 0, %0, c15, c12, 2" : : "r" (val)); + break; + + case PMN1: + asm volatile("mcr p15, 0, %0, c15, c12, 3" : : "r" (val)); + break; + } +} + +int arm11_setup_pmu(void) +{ + unsigned long event; + unsigned cpu; + u32 pmnc; + + cpu = smp_processor_id(); + if (arm11_read_pmnc() & PMCR_E) { + printk(KERN_ERR "oprofile: CPU%u PMU still enabled when setup new event counter.\n", cpu); + return -EBUSY; + } + + /* initialize PMNC, reset overflow, D bit, C bit and P bit. */ + arm11_write_pmnc(PMCR_OFL_PMN0 | PMCR_OFL_PMN1 | PMCR_OFL_CCNT | + PMCR_C | PMCR_P); + + pmnc = 0; + + if (counter_config[COUNTER_CPUn_PMNm(cpu, PMN0)].enabled) { + event = counter_config[COUNTER_CPUn_PMNm(cpu, PMN0)].event & 255; + pmnc |= event << 20; + pmnc |= PMCR_IEN_PMN0; + arm11_reset_counter(PMN0); + } + if (counter_config[COUNTER_CPUn_PMNm(cpu, PMN1)].enabled) { + event = counter_config[COUNTER_CPUn_PMNm(cpu, PMN1)].event & 255; + pmnc |= event << 12; + pmnc |= PMCR_IEN_PMN1; + arm11_reset_counter(PMN1); + } + if (counter_config[COUNTER_CPUn_CCNT(cpu)].enabled) { + pmnc |= PMCR_IEN_CCNT; + arm11_reset_counter(CCNT); + } + + arm11_write_pmnc(pmnc); + return 0; +} + +int arm11_start_pmu(void) +{ + arm11_write_pmnc(arm11_read_pmnc() | PMCR_E); + return 0; +} + +int arm11_stop_pmu(void) +{ + unsigned int cnt; + + arm11_write_pmnc(arm11_read_pmnc() & ~PMCR_E); + + for (cnt = PMN0; cnt <= CCNT; cnt++) + arm11_reset_counter(cnt); + + return 0; +} + +/* + * CPU counters' IRQ handler (one IRQ per CPU) + */ +static irqreturn_t arm11_pmu_interrupt(int irq, void *arg) +{ + struct pt_regs *regs = get_irq_regs(); + unsigned int cnt; + u32 pmnc; + + pmnc = arm11_read_pmnc(); + + /* First check if the two event counters have overflowed */ + for (cnt = PMN0; cnt <= PMN1; ++cnt) { + if ((pmnc & (PMCR_OFL_PMN0 << cnt)) && (pmnc & (PMCR_IEN_PMN0 << cnt))) { + arm11_reset_counter(cnt); + oprofile_add_sample(regs, COUNTER_CPUn_PMNm(smp_processor_id(), cnt)); + } + } + + /* Now check if the cycle counter has overflowed */ + if ((pmnc & PMCR_OFL_CCNT) && (pmnc & PMCR_IEN_CCNT)) { + arm11_reset_counter(CCNT); + oprofile_add_sample(regs, COUNTER_CPUn_CCNT(smp_processor_id())); + } + + /* Clear counter flag(s) */ + arm11_write_pmnc(pmnc); + return IRQ_HANDLED; +} + +int arm11_request_interrupts(int *irqs, int nr) +{ + unsigned int i; + int ret = 0; + + for(i = 0; i < nr; i++) { + ret = request_irq(irqs[i], arm11_pmu_interrupt, IRQF_DISABLED, "CP15 PMU", NULL); + if (ret != 0) { + printk(KERN_ERR "oprofile: unable to request IRQ%u for CP15 PMU\n", + irqs[i]); + break; + } + } + + if (i != nr) + while (i-- != 0) + free_irq(irqs[i], NULL); + + return ret; +} + +void arm11_release_interrupts(int *irqs, int nr) +{ + unsigned int i; + + for (i = 0; i < nr; i++) + free_irq(irqs[i], NULL); +} diff --git a/arch/arm/oprofile/op_arm11.h b/arch/arm/oprofile/op_arm11.h new file mode 100644 index 000000000000..6047ec209104 --- /dev/null +++ b/arch/arm/oprofile/op_arm11.h @@ -0,0 +1,44 @@ +/** + * @file op_arm11.h + * ARM11 CP15 Performance Monitor Unit Driver + * @remark Copyright 2004-7 ARM SMP Development Team + * @remark Copyright 2000-2004 Deepak Saxena <dsaxena@mvista.com> + * @remark Copyright 2000-2004 MontaVista Software Inc + * @remark Copyright 2004 Dave Jiang <dave.jiang@intel.com> + * @remark Copyright 2004 Intel Corporation + * @remark Copyright 2004 Zwane Mwaikambo <zwane@arm.linux.org.uk> + * @remark Copyright 2004 Oprofile Authors + * + * @remark Read the file COPYING + * + * @author Zwane Mwaikambo + */ +#ifndef OP_ARM11_H +#define OP_ARM11_H + +/* + * Per-CPU PMCR + */ +#define PMCR_E (1 << 0) /* Enable */ +#define PMCR_P (1 << 1) /* Count reset */ +#define PMCR_C (1 << 2) /* Cycle counter reset */ +#define PMCR_D (1 << 3) /* Cycle counter counts every 64th cpu cycle */ +#define PMCR_IEN_PMN0 (1 << 4) /* Interrupt enable count reg 0 */ +#define PMCR_IEN_PMN1 (1 << 5) /* Interrupt enable count reg 1 */ +#define PMCR_IEN_CCNT (1 << 6) /* Interrupt enable cycle counter */ +#define PMCR_OFL_PMN0 (1 << 8) /* Count reg 0 overflow */ +#define PMCR_OFL_PMN1 (1 << 9) /* Count reg 1 overflow */ +#define PMCR_OFL_CCNT (1 << 10) /* Cycle counter overflow */ + +#define PMN0 0 +#define PMN1 1 + +#define CPU_COUNTER(cpu, counter) ((cpu) * 3 + (counter)) + +int arm11_setup_pmu(void); +int arm11_start_pmu(void); +int arm11_stop_pmu(void); +int arm11_request_interrupts(int *, int); +void arm11_release_interrupts(int *, int); + +#endif diff --git a/arch/arm/oprofile/op_arm_model.h b/arch/arm/oprofile/op_arm_model.h index 8c4e4f6a1de3..05a206eff865 100644 --- a/arch/arm/oprofile/op_arm_model.h +++ b/arch/arm/oprofile/op_arm_model.h @@ -20,16 +20,97 @@ struct op_arm_model_spec { char *name; }; -#ifdef CONFIG_CPU_XSCALE extern struct op_arm_model_spec op_xscale_spec; -#endif - extern struct op_arm_model_spec op_armv6_spec; -extern struct op_arm_model_spec op_mpcore_spec; extern struct op_arm_model_spec op_armv7_spec; extern void arm_backtrace(struct pt_regs * const regs, unsigned int depth); extern int __init op_arm_init(struct oprofile_operations *ops, struct op_arm_model_spec *spec); extern void op_arm_exit(void); + +/* + * The macros need to be reimplemented as things we can call at runtime, + * along with cpu_is_xscale in system.h + */ +#ifdef CONFIG_CACHE_L2X0 +#define have_l2x0() 1 +#else +#define have_l2x0() 0 +#endif +#ifdef CONFIG_SMP +#define is_smp() 1 +#else +#define is_smp() 0 +#endif +#ifdef CONFIG_REALVIEW_EB_A9MP +#define cpu_is_a9() 1 +#else +#define cpu_is_a9() 0 +#endif +#if defined(CONFIG_REALVIEW_EB_ARM11MP) || defined(CONFIG_MACH_REALVIEW_PB11MP) +#define cpu_is_11mpcore() 1 +#else +#define cpu_is_11mpcore() 0 +#endif +#ifdef CONFIG_MACH_REALVIEW_PBX +#include <mach/hardware.h> +#include <mach/io.h> +#include <mach/board-pbx.h> +#undef cpu_is_11mpcore +#define cpu_is_11mpcore() core_tile_pbx11mp() +#undef cpu_is_a9 +#define cpu_is_a9() core_tile_pbxa9mp() +#endif + +/* + * ARM11MPCore SCU event monitor support + */ +#ifdef CONFIG_SMP +#if defined(CONFIG_MACH_REALVIEW_EB) +#define SCU_EVENTMONITORS_VA_BASE __io_address(REALVIEW_EB11MP_SCU_BASE + 0x10) +#elif defined(CONFIG_MACH_REALVIEW_PB11MP) +#define SCU_EVENTMONITORS_VA_BASE __io_address(REALVIEW_TC11MP_SCU_BASE + 0x10) +#elif defined(CONFIG_MACH_REALVIEW_PBX) +#define SCU_EVENTMONITORS_VA_BASE __io_address(REALVIEW_PBX_TILE_SCU_BASE + 0x10) +#else +#error Cannot determine the base address of the SCU +#endif +#endif + +/* + * IRQ numbers for PMUs (A9MPCore and 11MPCore) and SCU (11MPCore only) + */ +#if defined(CONFIG_MACH_REALVIEW_EB) || defined(CONFIG_MACH_REALVIEW_PB11MP) +#define IRQ_PMU_CPU0 IRQ_TC11MP_PMU_CPU0 +#define IRQ_PMU_CPU1 IRQ_TC11MP_PMU_CPU1 +#define IRQ_PMU_CPU2 IRQ_TC11MP_PMU_CPU2 +#define IRQ_PMU_CPU3 IRQ_TC11MP_PMU_CPU3 +#define IRQ_PMU_SCU0 IRQ_TC11MP_PMU_SCU0 +#define IRQ_PMU_SCU1 IRQ_TC11MP_PMU_SCU1 +#define IRQ_PMU_SCU2 IRQ_TC11MP_PMU_SCU2 +#define IRQ_PMU_SCU3 IRQ_TC11MP_PMU_SCU3 +#define IRQ_PMU_SCU4 IRQ_TC11MP_PMU_SCU4 +#define IRQ_PMU_SCU5 IRQ_TC11MP_PMU_SCU5 +#define IRQ_PMU_SCU6 IRQ_TC11MP_PMU_SCU6 +#define IRQ_PMU_SCU7 IRQ_TC11MP_PMU_SCU7 +#elif defined(CONFIG_MACH_REALVIEW_PBX) +#define IRQ_PMU_CPU0 IRQ_PBX_PMU_CPU0 +#define IRQ_PMU_CPU1 IRQ_PBX_PMU_CPU1 +#define IRQ_PMU_CPU2 IRQ_PBX_PMU_CPU2 +#define IRQ_PMU_CPU3 IRQ_PBX_PMU_CPU3 +#define IRQ_PMU_SCU0 IRQ_PBX_PMU_SCU0 +#define IRQ_PMU_SCU1 IRQ_PBX_PMU_SCU1 +#define IRQ_PMU_SCU2 IRQ_PBX_PMU_SCU2 +#define IRQ_PMU_SCU3 IRQ_PBX_PMU_SCU3 +#define IRQ_PMU_SCU4 IRQ_PBX_PMU_SCU4 +#define IRQ_PMU_SCU5 IRQ_PBX_PMU_SCU5 +#define IRQ_PMU_SCU6 IRQ_PBX_PMU_SCU6 +#define IRQ_PMU_SCU7 IRQ_PBX_PMU_SCU7 +#elif defined(CONFIG_ARCH_OMAP2) +#define IRQ_PMU_CPU0 3 +#else +#define IRQ_PMU_CPU0 NO_IRQ +#endif + #endif /* OP_ARM_MODEL_H */ diff --git a/arch/arm/oprofile/op_counter.h b/arch/arm/oprofile/op_counter.h index ca942a63b52f..3d92fb59b0a1 100644 --- a/arch/arm/oprofile/op_counter.h +++ b/arch/arm/oprofile/op_counter.h @@ -24,4 +24,31 @@ struct op_counter_config { extern struct op_counter_config *counter_config; + +/* + * List of userspace counter numbers: we use the same layout for both + * the V6 and V7 oprofile models. + * 0- 7 CPU0 event counters and cycle counter + * 8-15 CPU1 event counters and cycle counter + * 16-23 CPU2 event counters and cycle counter + * 24-31 CPU3 event counters and cycle counter + * 32-39 SCU counters + * 40-41 L2X0 counters + */ + +#define PMU_COUNTERS_PER_CPU 8 /* 7 event counters, 1 cycle counter */ +#define CCNT (PMU_COUNTERS_PER_CPU - 1) +#define MAX_CPUS 4 + +#define COUNTER_CPUn_PMNm(N,M) ((N) * PMU_COUNTERS_PER_CPU + (M)) +#define COUNTER_CPUn_CCNT(N) ((N+1) * PMU_COUNTERS_PER_CPU - 1) + +#define COUNTER_SCU_MN(N) (PMU_COUNTERS_PER_CPU * MAX_CPUS + (N)) +#define NUM_SCU_COUNTERS 8 + +#define COUNTER_L2X0_EC(N) (COUNTER_SCU_MN(NUM_SCU_COUNTERS) + (N)) +#define L2X0_NUM_COUNTERS 2 + +#define NUM_COUNTERS COUNTER_L2X0_EC(L2X0_NUM_COUNTERS) + #endif /* OP_COUNTER_H */ diff --git a/arch/arm/oprofile/op_l2x0.c b/arch/arm/oprofile/op_l2x0.c new file mode 100644 index 000000000000..28b5fa750910 --- /dev/null +++ b/arch/arm/oprofile/op_l2x0.c @@ -0,0 +1,244 @@ +/** + * @file op_model_l2x0.c + * ARM L220/L230 Level 2 Cache Controller Event Counter Driver + * @remark Copyright 2004-7 ARM SMP Development Team + */ +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/oprofile.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/smp.h> + +#include <asm/io.h> +#include <asm/hardware/cache-l2x0.h> +#include <mach/platform.h> +#include <mach/hardware.h> +#include <mach/irqs.h> + +#include "op_counter.h" +#include "op_arm_model.h" +#include "op_l2x0.h" + +static unsigned l2x0_base, l2x0_irq; + +/* + * Determine L2X0 base address and event counter IRQ + */ +void l2x0_ec_setup(void) +{ +#if defined(CONFIG_MACH_REALVIEW_EB) + l2x0_base = IO_ADDRESS(REALVIEW_EB11MP_L220_BASE); + l2x0_irq = IRQ_EB11MP_L220_EVENT; +#elif defined(CONFIG_MACH_REALVIEW_PB11MP) + l2x0_base = IO_ADDRESS(REALVIEW_TC11MP_L220_BASE); + l2x0_irq = IRQ_TC11MP_L220_EVENT; +#elif defined(CONFIG_MACH_REALVIEW_PBX) + l2x0_base = IO_ADDRESS(REALVIEW_PBX_TILE_L220_BASE); + l2x0_irq = IRQ_PBX_L220_EVENT; +#else +#error l2x0_base and l2x0_irq not set! +#endif +} + + + +/* + * Read the configuration of an event counter + */ +static inline u32 l2x0_ec_read_config(unsigned int cnt) +{ + return readl(l2x0_base + L2X0_EVENT_CNT0_CFG - cnt * 4); +} + +/* + * Change the configuration of an event counter + */ +static inline void l2x0_ec_write_config(unsigned int cnt, u32 config) +{ + writel(config, l2x0_base + L2X0_EVENT_CNT0_CFG - cnt * 4); +} + +/* + * Reset a counter to its initial value + */ +static inline void l2x0_ec_reset(unsigned int cnt) +{ + u32 val, temp; + + /* + * We can only write to the counter value when the counter is disabled + */ + temp = l2x0_ec_read_config(cnt); + l2x0_ec_write_config(cnt, L2X0_EVENT_CONFIG_DISABLED); + + /* + * Ok, set the counter value + */ + val = -(u32)counter_config[COUNTER_L2X0_EC(cnt)].count; + writel(val, l2x0_base + L2X0_EVENT_CNT0_VAL - cnt * 4); + + /* + * Now put the counter config back to what it was before + */ + l2x0_ec_write_config(cnt, temp); +} + +/* + * Read the current value of an event counter + */ +static inline u32 l2x0_ec_read_value(unsigned int cnt) +{ + return readl(l2x0_base + L2X0_EVENT_CNT0_VAL - cnt * 4); +} + +/* + * Enable/disable L220/L230 event counting system + * We assume the Event Monitoring Bus is already enabled + * (that is, bit 20 is set in the L2X0 Aux control register) + * because it can't be set while the L2X0 is enabled. + */ +static inline void l2x0_ec_system_setup(unsigned enable) +{ + u32 val; + unsigned cnt; + + /* + * Enable/disable and Reset all the counters + */ + val = L2X0_EVENT_CONTROL_RESET_ALL; + if (enable) + val |= L2X0_EVENT_CONTROL_ENABLE; + writel(val, l2x0_base + L2X0_EVENT_CNT_CTRL); + + /* + * Set the individual counters to disabled (for now at least) + */ + for (cnt = 0; cnt < L2X0_NUM_COUNTERS; ++cnt) + l2x0_ec_write_config(cnt, L2X0_EVENT_CONFIG_DISABLED); + + /* + * Clear any stray EC interrupt, and set the mask appropriately + */ + writel(L2X0_INTR_ECNTR, l2x0_base + L2X0_INTR_CLEAR); + val = readl(l2x0_base + L2X0_INTR_MASK); + if (enable) + val |= L2X0_INTR_ECNTR; + else + val &= !L2X0_INTR_ECNTR; + writel(val, l2x0_base + L2X0_INTR_MASK); +} + +#ifdef CONFIG_SMP +/* + * Rotate L220/L230 EC interrupts around all the online CPUs in an SMP system. + * We do this because we can't know which CPU caused an L220/L230 event, + * and this gives us a sensible statistical picture of what was running. + * This function is always called in interrupt context. + */ +static inline void l2x0_ec_rotate_irq(int irq) +{ + static unsigned cpu = 0; + cpumask_t mask; + + if (is_smp()) { + cpu = next_cpu(cpu, cpu_online_map); + if (cpu >= NR_CPUS) + cpu = first_cpu(cpu_online_map); + mask = cpumask_of_cpu(cpu); + irq_set_affinity(irq, mask); + } +} +#endif + +/* + * L220/L230 event counter IRQ handler ( + */ +static irqreturn_t l2x0_ec_interrupt(int irq, void *arg) +{ + u32 interrupt_status; + unsigned int cnt; + + /* If it's an L2X0 EC interrupt, process it */ + interrupt_status = readl(l2x0_base + L2X0_MASKED_INTR_STAT); + + if (interrupt_status & L2X0_INTR_ECNTR) { + /* + * A counter that has overflowed reads 0xffffffff + * This is not actually documented anywhere... + */ + for (cnt = 0; cnt < L2X0_NUM_COUNTERS; ++cnt) { + if (l2x0_ec_read_value(cnt) == 0xffffffff) { + oprofile_add_sample(get_irq_regs(), + COUNTER_L2X0_EC(cnt)); + l2x0_ec_reset(cnt); + } + } + /* + * Clear the interrupt, and move it onto the next CPU. + */ + writel(L2X0_INTR_ECNTR, l2x0_base + L2X0_INTR_CLEAR); +#ifdef CONFIG_SMP + l2x0_ec_rotate_irq(irq); +#endif + return IRQ_HANDLED; + } + else { + return IRQ_NONE; + } +} + +int l2x0_ec_start(void) +{ + int ret = 0; + unsigned cnt; + u32 cfg; + + /* + * Install handler for the L220/L230 event counter interrupt + */ + ret = request_irq(l2x0_irq, l2x0_ec_interrupt, IRQF_DISABLED, + "L2X0 EC", NULL); + if (ret) { + printk(KERN_ERR "oprofile: unable to request IRQ%u " + "for L2X0 Event Counter\n", l2x0_irq); + return ret; + } + + /* + * Enable the event counter system + */ + l2x0_ec_system_setup(1); + + /* + * Configure the events we're interested in, and reset the counters + */ + for (cnt = 0; cnt < L2X0_NUM_COUNTERS; ++cnt) { + if (counter_config[COUNTER_L2X0_EC(cnt)].enabled) { + cfg = counter_config[COUNTER_L2X0_EC(cnt)].event & 0xFF; + cfg <<= 2; + cfg |= L2X0_EVENT_INTERRUPT_ON_OVF; + l2x0_ec_write_config(cnt, cfg); + l2x0_ec_reset(cnt); + } + else + l2x0_ec_write_config(cnt, L2X0_EVENT_CONFIG_DISABLED); + } + + return 0; +} + +void l2x0_ec_stop(void) +{ + unsigned cnt; + + /* Disable individual L220/L230 event counters */ + for (cnt = 0; cnt < L2X0_NUM_COUNTERS; ++cnt) + l2x0_ec_write_config(cnt, L2X0_EVENT_CONFIG_DISABLED); + + /* Disable L220/L230 event counter system */ + l2x0_ec_system_setup(0); + + /* Remove L220/L230 event counter interrupt handler */ + free_irq(l2x0_irq, NULL); +} diff --git a/arch/arm/oprofile/op_l2x0.h b/arch/arm/oprofile/op_l2x0.h new file mode 100644 index 000000000000..8ec2df7b183d --- /dev/null +++ b/arch/arm/oprofile/op_l2x0.h @@ -0,0 +1,15 @@ +/** + * @file op_model_l220.h + * ARM L220/L230 Level 2 Cache Controller Event Counter Driver + * @remark Copyright 2007 ARM SMP Development Team + * + * @remark Read the file COPYING + */ +#ifndef OP_MODEL_L2X0_H +#define OP_MODEL_L2X0_H + +void l2x0_ec_setup(void); +int l2x0_ec_start(void); +void l2x0_ec_stop(void); + +#endif diff --git a/arch/arm/oprofile/op_model_arm11_core.c b/arch/arm/oprofile/op_model_arm11_core.c index ad80752cb9fb..e69de29bb2d1 100644 --- a/arch/arm/oprofile/op_model_arm11_core.c +++ b/arch/arm/oprofile/op_model_arm11_core.c @@ -1,162 +0,0 @@ -/** - * @file op_model_arm11_core.c - * ARM11 Event Monitor Driver - * @remark Copyright 2004 ARM SMP Development Team - */ -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/oprofile.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/smp.h> - -#include "op_counter.h" -#include "op_arm_model.h" -#include "op_model_arm11_core.h" - -/* - * ARM11 PMU support - */ -static inline void arm11_write_pmnc(u32 val) -{ - /* upper 4bits and 7, 11 are write-as-0 */ - val &= 0x0ffff77f; - asm volatile("mcr p15, 0, %0, c15, c12, 0" : : "r" (val)); -} - -static inline u32 arm11_read_pmnc(void) -{ - u32 val; - asm volatile("mrc p15, 0, %0, c15, c12, 0" : "=r" (val)); - return val; -} - -static void arm11_reset_counter(unsigned int cnt) -{ - u32 val = -(u32)counter_config[CPU_COUNTER(smp_processor_id(), cnt)].count; - switch (cnt) { - case CCNT: - asm volatile("mcr p15, 0, %0, c15, c12, 1" : : "r" (val)); - break; - - case PMN0: - asm volatile("mcr p15, 0, %0, c15, c12, 2" : : "r" (val)); - break; - - case PMN1: - asm volatile("mcr p15, 0, %0, c15, c12, 3" : : "r" (val)); - break; - } -} - -int arm11_setup_pmu(void) -{ - unsigned int cnt; - u32 pmnc; - - if (arm11_read_pmnc() & PMCR_E) { - printk(KERN_ERR "oprofile: CPU%u PMU still enabled when setup new event counter.\n", smp_processor_id()); - return -EBUSY; - } - - /* initialize PMNC, reset overflow, D bit, C bit and P bit. */ - arm11_write_pmnc(PMCR_OFL_PMN0 | PMCR_OFL_PMN1 | PMCR_OFL_CCNT | - PMCR_C | PMCR_P); - - for (pmnc = 0, cnt = PMN0; cnt <= CCNT; cnt++) { - unsigned long event; - - if (!counter_config[CPU_COUNTER(smp_processor_id(), cnt)].enabled) - continue; - - event = counter_config[CPU_COUNTER(smp_processor_id(), cnt)].event & 255; - - /* - * Set event (if destined for PMNx counters) - */ - if (cnt == PMN0) { - pmnc |= event << 20; - } else if (cnt == PMN1) { - pmnc |= event << 12; - } - - /* - * We don't need to set the event if it's a cycle count - * Enable interrupt for this counter - */ - pmnc |= PMCR_IEN_PMN0 << cnt; - arm11_reset_counter(cnt); - } - arm11_write_pmnc(pmnc); - - return 0; -} - -int arm11_start_pmu(void) -{ - arm11_write_pmnc(arm11_read_pmnc() | PMCR_E); - return 0; -} - -int arm11_stop_pmu(void) -{ - unsigned int cnt; - - arm11_write_pmnc(arm11_read_pmnc() & ~PMCR_E); - - for (cnt = PMN0; cnt <= CCNT; cnt++) - arm11_reset_counter(cnt); - - return 0; -} - -/* - * CPU counters' IRQ handler (one IRQ per CPU) - */ -static irqreturn_t arm11_pmu_interrupt(int irq, void *arg) -{ - struct pt_regs *regs = get_irq_regs(); - unsigned int cnt; - u32 pmnc; - - pmnc = arm11_read_pmnc(); - - for (cnt = PMN0; cnt <= CCNT; cnt++) { - if ((pmnc & (PMCR_OFL_PMN0 << cnt)) && (pmnc & (PMCR_IEN_PMN0 << cnt))) { - arm11_reset_counter(cnt); - oprofile_add_sample(regs, CPU_COUNTER(smp_processor_id(), cnt)); - } - } - /* Clear counter flag(s) */ - arm11_write_pmnc(pmnc); - return IRQ_HANDLED; -} - -int arm11_request_interrupts(int *irqs, int nr) -{ - unsigned int i; - int ret = 0; - - for(i = 0; i < nr; i++) { - ret = request_irq(irqs[i], arm11_pmu_interrupt, IRQF_DISABLED, "CP15 PMU", NULL); - if (ret != 0) { - printk(KERN_ERR "oprofile: unable to request IRQ%u for MPCORE-EM\n", - irqs[i]); - break; - } - } - - if (i != nr) - while (i-- != 0) - free_irq(irqs[i], NULL); - - return ret; -} - -void arm11_release_interrupts(int *irqs, int nr) -{ - unsigned int i; - - for (i = 0; i < nr; i++) - free_irq(irqs[i], NULL); -} diff --git a/arch/arm/oprofile/op_model_arm11_core.h b/arch/arm/oprofile/op_model_arm11_core.h index 6f8538e5a960..e69de29bb2d1 100644 --- a/arch/arm/oprofile/op_model_arm11_core.h +++ b/arch/arm/oprofile/op_model_arm11_core.h @@ -1,45 +0,0 @@ -/** - * @file op_model_arm11_core.h - * ARM11 Event Monitor Driver - * @remark Copyright 2004 ARM SMP Development Team - * @remark Copyright 2000-2004 Deepak Saxena <dsaxena@mvista.com> - * @remark Copyright 2000-2004 MontaVista Software Inc - * @remark Copyright 2004 Dave Jiang <dave.jiang@intel.com> - * @remark Copyright 2004 Intel Corporation - * @remark Copyright 2004 Zwane Mwaikambo <zwane@arm.linux.org.uk> - * @remark Copyright 2004 Oprofile Authors - * - * @remark Read the file COPYING - * - * @author Zwane Mwaikambo - */ -#ifndef OP_MODEL_ARM11_CORE_H -#define OP_MODEL_ARM11_CORE_H - -/* - * Per-CPU PMCR - */ -#define PMCR_E (1 << 0) /* Enable */ -#define PMCR_P (1 << 1) /* Count reset */ -#define PMCR_C (1 << 2) /* Cycle counter reset */ -#define PMCR_D (1 << 3) /* Cycle counter counts every 64th cpu cycle */ -#define PMCR_IEN_PMN0 (1 << 4) /* Interrupt enable count reg 0 */ -#define PMCR_IEN_PMN1 (1 << 5) /* Interrupt enable count reg 1 */ -#define PMCR_IEN_CCNT (1 << 6) /* Interrupt enable cycle counter */ -#define PMCR_OFL_PMN0 (1 << 8) /* Count reg 0 overflow */ -#define PMCR_OFL_PMN1 (1 << 9) /* Count reg 1 overflow */ -#define PMCR_OFL_CCNT (1 << 10) /* Cycle counter overflow */ - -#define PMN0 0 -#define PMN1 1 -#define CCNT 2 - -#define CPU_COUNTER(cpu, counter) ((cpu) * 3 + (counter)) - -int arm11_setup_pmu(void); -int arm11_start_pmu(void); -int arm11_stop_pmu(void); -int arm11_request_interrupts(int *, int); -void arm11_release_interrupts(int *, int); - -#endif diff --git a/arch/arm/oprofile/op_model_mpcore.h b/arch/arm/oprofile/op_model_mpcore.h index 73d811023688..e69de29bb2d1 100644 --- a/arch/arm/oprofile/op_model_mpcore.h +++ b/arch/arm/oprofile/op_model_mpcore.h @@ -1,61 +0,0 @@ -/** - * @file op_model_mpcore.c - * MPCORE Event Monitor Driver - * @remark Copyright 2004 ARM SMP Development Team - * @remark Copyright 2000-2004 Deepak Saxena <dsaxena@mvista.com> - * @remark Copyright 2000-2004 MontaVista Software Inc - * @remark Copyright 2004 Dave Jiang <dave.jiang@intel.com> - * @remark Copyright 2004 Intel Corporation - * @remark Copyright 2004 Zwane Mwaikambo <zwane@arm.linux.org.uk> - * @remark Copyright 2004 Oprofile Authors - * - * @remark Read the file COPYING - * - * @author Zwane Mwaikambo - */ -#ifndef OP_MODEL_MPCORE_H -#define OP_MODEL_MPCORE_H - -struct eventmonitor { - unsigned long PMCR; - unsigned char MCEB[8]; - unsigned long MC[8]; -}; - -/* - * List of userspace counter numbers: note that the structure is important. - * The code relies on CPUn's counters being CPU0's counters + 3n - * and on CPU0's counters starting at 0 - */ - -#define COUNTER_CPU0_PMN0 0 -#define COUNTER_CPU0_PMN1 1 -#define COUNTER_CPU0_CCNT 2 - -#define COUNTER_CPU1_PMN0 3 -#define COUNTER_CPU1_PMN1 4 -#define COUNTER_CPU1_CCNT 5 - -#define COUNTER_CPU2_PMN0 6 -#define COUNTER_CPU2_PMN1 7 -#define COUNTER_CPU2_CCNT 8 - -#define COUNTER_CPU3_PMN0 9 -#define COUNTER_CPU3_PMN1 10 -#define COUNTER_CPU3_CCNT 11 - -#define COUNTER_SCU_MN0 12 -#define COUNTER_SCU_MN1 13 -#define COUNTER_SCU_MN2 14 -#define COUNTER_SCU_MN3 15 -#define COUNTER_SCU_MN4 16 -#define COUNTER_SCU_MN5 17 -#define COUNTER_SCU_MN6 18 -#define COUNTER_SCU_MN7 19 -#define NUM_SCU_COUNTERS 8 - -#define SCU_COUNTER(number) ((number) + COUNTER_SCU_MN0) - -#define MPCORE_NUM_COUNTERS SCU_COUNTER(NUM_SCU_COUNTERS) - -#endif diff --git a/arch/arm/oprofile/op_model_v6-7.c b/arch/arm/oprofile/op_model_v6-7.c new file mode 100644 index 000000000000..6108a91331f7 --- /dev/null +++ b/arch/arm/oprofile/op_model_v6-7.c @@ -0,0 +1,243 @@ +/** + * @file op_model_v6-7.c + * ARM V6 and V7 Performance Monitor models + * + * Based on op_model_xscale.c + * + * @remark Copyright 2007 ARM SMP Development Team + * @remark Copyright 2000-2004 Deepak Saxena <dsaxena@mvista.com> + * @remark Copyright 2000-2004 MontaVista Software Inc + * @remark Copyright 2004 Dave Jiang <dave.jiang@intel.com> + * @remark Copyright 2004 Intel Corporation + * @remark Copyright 2004 Zwane Mwaikambo <zwane@arm.linux.org.uk> + * @remark Copyright 2004 OProfile Authors + * + * @remark Read the file COPYING + * + * @author Tony Lindgren <tony@atomide.com> + */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/oprofile.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <asm/irq.h> +#include <asm/system.h> + +#include <linux/smp.h> + +#include "op_counter.h" +#include "op_arm_model.h" +#include "op_arm11.h" +#include "op_v7.h" +#include "op_scu.h" +#include "op_l2x0.h" + +static int arm11_irqs[] = { + [0] = IRQ_PMU_CPU0, +#ifdef CONFIG_SMP + [1] = IRQ_PMU_CPU1, + [2] = IRQ_PMU_CPU2, + [3] = IRQ_PMU_CPU3 +#endif +}; + +static int v7_irqs[] = { + [0] = IRQ_PMU_CPU0, +#ifdef CONFIG_SMP + [1] = IRQ_PMU_CPU1, + [2] = IRQ_PMU_CPU2, + [3] = IRQ_PMU_CPU3 +#endif +}; + + +/* + * Functions and struct to enable calling a function on all CPUs in an SMP + * system. This works on a non-SMP system too (i.e. just calls the function!) + */ +struct em_function_data { + int (*fn)(void); + int ret; +}; + +static void em_func(void *data) +{ + struct em_function_data *d = data; + int ret = d->fn(); + if (ret) + d->ret = ret; +} + +static int em_call_function(int (*fn)(void)) +{ + struct em_function_data data; + + data.fn = fn; + data.ret = 0; + + get_cpu(); + if (is_smp()) + smp_call_function(em_func, &data, 1); + em_func(&data); + put_cpu(); + + return data.ret; +} + + +/* + * Why isn't there a function to route an IRQ to a specific CPU in + * genirq? + */ +#ifdef CONFIG_SMP +void em_route_irq(int irq, unsigned int cpu) +{ + irq_set_affinity(irq, *(get_cpu_mask(cpu))); +} +#endif + +/* + * ARM V6 Oprofile callbacks + */ +static void v6_stop(void) +{ + em_call_function(arm11_stop_pmu); + arm11_release_interrupts(arm11_irqs, ARRAY_SIZE(arm11_irqs)); + if (is_smp()) + scu_stop(); + if (have_l2x0()) + l2x0_ec_stop(); +} + +static int v6_start(void) +{ + int ret; +#ifdef CONFIG_SMP + unsigned i; + + if (is_smp()) { + /* + * Send SCU and CP15 PMU interrupts to the "owner" CPU. + */ + for (i=0; i<CONFIG_NR_CPUS; ++i) { + em_route_irq(IRQ_PMU_SCU0 + 2 * i, i); + em_route_irq(IRQ_PMU_SCU1 + 2 * i, i); + em_route_irq(IRQ_PMU_CPU0 + i, i); + } + } +#endif + + ret = arm11_request_interrupts(arm11_irqs, ARRAY_SIZE(arm11_irqs)); + if (ret == 0) { + em_call_function(arm11_start_pmu); + + if (is_smp()) + ret = scu_start(); + + if (!ret && have_l2x0()) + ret = l2x0_ec_start(); + + if (ret) + arm11_release_interrupts(arm11_irqs, ARRAY_SIZE(arm11_irqs)); + } + return ret; +} + +static int v6_init(void) +{ + return 0; +} + +static int v6_setup_ctrs(void) +{ + int ret; + ret = em_call_function(arm11_setup_pmu); + + if (ret == 0 && is_smp()) + scu_setup(); + + if (ret == 0 && have_l2x0()) + l2x0_ec_setup(); + + return ret; +} + +/* + * ARM V7 Oprofile callbacks + */ +static int v7_init(void) +{ + return 0; +} + + +static int v7_setup_ctrs(void) +{ + int ret; + + ret = em_call_function(v7_setup_pmu); + + if (ret == 0 && have_l2x0()) + l2x0_ec_setup(); + + return ret; +} + +static int v7_start(void) +{ + int ret; +#ifdef CONFIG_SMP + unsigned i; + + if (is_smp()) { + /* + * Send CP15 PMU interrupts to the owner CPU. + */ + for (i=0; i<CONFIG_NR_CPUS; ++i) { + em_route_irq(IRQ_PMU_CPU0 + i, i); + } + } +#endif + + ret = v7_request_interrupts(v7_irqs, ARRAY_SIZE(v7_irqs)); + if (ret == 0) { + em_call_function(v7_start_pmu); + + if (have_l2x0()) + ret = l2x0_ec_start(); + + if (ret) + v7_release_interrupts(v7_irqs, ARRAY_SIZE(v7_irqs)); + } + return ret; +} + +static void v7_stop(void) +{ + em_call_function(v7_stop_pmu); + v7_release_interrupts(v7_irqs, ARRAY_SIZE(v7_irqs)); + if (have_l2x0()) + l2x0_ec_stop(); +} + + +struct op_arm_model_spec op_armv6_spec = { + .init = v6_init, + .num_counters = NUM_COUNTERS, + .setup_ctrs = v6_setup_ctrs, + .start = v6_start, + .stop = v6_stop, + .name = "arm/v6", /* This may get overwritten in common.c */ +}; + +struct op_arm_model_spec op_armv7_spec = { + .init = v7_init, + .num_counters = NUM_COUNTERS, + .setup_ctrs = v7_setup_ctrs, + .start = v7_start, + .stop = v7_stop, + .name = "arm/v7", /* This gets overwritten in common.c */ +}; diff --git a/arch/arm/oprofile/op_model_v6.c b/arch/arm/oprofile/op_model_v6.c index fe581383d3e2..e69de29bb2d1 100644 --- a/arch/arm/oprofile/op_model_v6.c +++ b/arch/arm/oprofile/op_model_v6.c @@ -1,67 +0,0 @@ -/** - * @file op_model_v6.c - * ARM11 Performance Monitor Driver - * - * Based on op_model_xscale.c - * - * @remark Copyright 2000-2004 Deepak Saxena <dsaxena@mvista.com> - * @remark Copyright 2000-2004 MontaVista Software Inc - * @remark Copyright 2004 Dave Jiang <dave.jiang@intel.com> - * @remark Copyright 2004 Intel Corporation - * @remark Copyright 2004 Zwane Mwaikambo <zwane@arm.linux.org.uk> - * @remark Copyright 2004 OProfile Authors - * - * @remark Read the file COPYING - * - * @author Tony Lindgren <tony@atomide.com> - */ - -/* #define DEBUG */ -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/sched.h> -#include <linux/oprofile.h> -#include <linux/interrupt.h> -#include <asm/irq.h> -#include <asm/system.h> - -#include "op_counter.h" -#include "op_arm_model.h" -#include "op_model_arm11_core.h" - -static int irqs[] = { -#ifdef CONFIG_ARCH_OMAP2 - 3, -#endif -}; - -static void armv6_pmu_stop(void) -{ - arm11_stop_pmu(); - arm11_release_interrupts(irqs, ARRAY_SIZE(irqs)); -} - -static int armv6_pmu_start(void) -{ - int ret; - - ret = arm11_request_interrupts(irqs, ARRAY_SIZE(irqs)); - if (ret >= 0) - ret = arm11_start_pmu(); - - return ret; -} - -static int armv6_detect_pmu(void) -{ - return 0; -} - -struct op_arm_model_spec op_armv6_spec = { - .init = armv6_detect_pmu, - .num_counters = 3, - .setup_ctrs = arm11_setup_pmu, - .start = armv6_pmu_start, - .stop = armv6_pmu_stop, - .name = "arm/armv6", -}; diff --git a/arch/arm/oprofile/op_model_v7.c b/arch/arm/oprofile/op_model_v7.c index f20295f14adb..e69de29bb2d1 100644 --- a/arch/arm/oprofile/op_model_v7.c +++ b/arch/arm/oprofile/op_model_v7.c @@ -1,411 +0,0 @@ -/** - * op_model_v7.c - * ARM V7 (Cortex A8) Event Monitor Driver - * - * Copyright 2008 Jean Pihet <jpihet@mvista.com> - * Copyright 2004 ARM SMP Development Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/oprofile.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/smp.h> - -#include "op_counter.h" -#include "op_arm_model.h" -#include "op_model_v7.h" - -/* #define DEBUG */ - - -/* - * ARM V7 PMNC support - */ - -static u32 cnt_en[CNTMAX]; - -static inline void armv7_pmnc_write(u32 val) -{ - val &= PMNC_MASK; - asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val)); -} - -static inline u32 armv7_pmnc_read(void) -{ - u32 val; - - asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val)); - return val; -} - -static inline u32 armv7_pmnc_enable_counter(unsigned int cnt) -{ - u32 val; - - if (cnt >= CNTMAX) { - printk(KERN_ERR "oprofile: CPU%u enabling wrong PMNC counter" - " %d\n", smp_processor_id(), cnt); - return -1; - } - - if (cnt == CCNT) - val = CNTENS_C; - else - val = (1 << (cnt - CNT0)); - - val &= CNTENS_MASK; - asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val)); - - return cnt; -} - -static inline u32 armv7_pmnc_disable_counter(unsigned int cnt) -{ - u32 val; - - if (cnt >= CNTMAX) { - printk(KERN_ERR "oprofile: CPU%u disabling wrong PMNC counter" - " %d\n", smp_processor_id(), cnt); - return -1; - } - - if (cnt == CCNT) - val = CNTENC_C; - else - val = (1 << (cnt - CNT0)); - - val &= CNTENC_MASK; - asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val)); - - return cnt; -} - -static inline u32 armv7_pmnc_enable_intens(unsigned int cnt) -{ - u32 val; - - if (cnt >= CNTMAX) { - printk(KERN_ERR "oprofile: CPU%u enabling wrong PMNC counter" - " interrupt enable %d\n", smp_processor_id(), cnt); - return -1; - } - - if (cnt == CCNT) - val = INTENS_C; - else - val = (1 << (cnt - CNT0)); - - val &= INTENS_MASK; - asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (val)); - - return cnt; -} - -static inline u32 armv7_pmnc_getreset_flags(void) -{ - u32 val; - - /* Read */ - asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val)); - - /* Write to clear flags */ - val &= FLAG_MASK; - asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (val)); - - return val; -} - -static inline int armv7_pmnc_select_counter(unsigned int cnt) -{ - u32 val; - - if ((cnt == CCNT) || (cnt >= CNTMAX)) { - printk(KERN_ERR "oprofile: CPU%u selecting wrong PMNC counteri" - " %d\n", smp_processor_id(), cnt); - return -1; - } - - val = (cnt - CNT0) & SELECT_MASK; - asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val)); - - return cnt; -} - -static inline void armv7_pmnc_write_evtsel(unsigned int cnt, u32 val) -{ - if (armv7_pmnc_select_counter(cnt) == cnt) { - val &= EVTSEL_MASK; - asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val)); - } -} - -static void armv7_pmnc_reset_counter(unsigned int cnt) -{ - u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), cnt); - u32 val = -(u32)counter_config[cpu_cnt].count; - - switch (cnt) { - case CCNT: - armv7_pmnc_disable_counter(cnt); - - asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (val)); - - if (cnt_en[cnt] != 0) - armv7_pmnc_enable_counter(cnt); - - break; - - case CNT0: - case CNT1: - case CNT2: - case CNT3: - armv7_pmnc_disable_counter(cnt); - - if (armv7_pmnc_select_counter(cnt) == cnt) - asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (val)); - - if (cnt_en[cnt] != 0) - armv7_pmnc_enable_counter(cnt); - - break; - - default: - printk(KERN_ERR "oprofile: CPU%u resetting wrong PMNC counter" - " %d\n", smp_processor_id(), cnt); - break; - } -} - -int armv7_setup_pmnc(void) -{ - unsigned int cnt; - - if (armv7_pmnc_read() & PMNC_E) { - printk(KERN_ERR "oprofile: CPU%u PMNC still enabled when setup" - " new event counter.\n", smp_processor_id()); - return -EBUSY; - } - - /* - * Initialize & Reset PMNC: C bit, D bit and P bit. - * Note: Using a slower count for CCNT (D bit: divide by 64) results - * in a more stable system - */ - armv7_pmnc_write(PMNC_P | PMNC_C | PMNC_D); - - - for (cnt = CCNT; cnt < CNTMAX; cnt++) { - unsigned long event; - u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), cnt); - - /* - * Disable counter - */ - armv7_pmnc_disable_counter(cnt); - cnt_en[cnt] = 0; - - if (!counter_config[cpu_cnt].enabled) - continue; - - event = counter_config[cpu_cnt].event & 255; - - /* - * Set event (if destined for PMNx counters) - * We don't need to set the event if it's a cycle count - */ - if (cnt != CCNT) - armv7_pmnc_write_evtsel(cnt, event); - - /* - * Enable interrupt for this counter - */ - armv7_pmnc_enable_intens(cnt); - - /* - * Reset counter - */ - armv7_pmnc_reset_counter(cnt); - - /* - * Enable counter - */ - armv7_pmnc_enable_counter(cnt); - cnt_en[cnt] = 1; - } - - return 0; -} - -static inline void armv7_start_pmnc(void) -{ - armv7_pmnc_write(armv7_pmnc_read() | PMNC_E); -} - -static inline void armv7_stop_pmnc(void) -{ - armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E); -} - -/* - * CPU counters' IRQ handler (one IRQ per CPU) - */ -static irqreturn_t armv7_pmnc_interrupt(int irq, void *arg) -{ - struct pt_regs *regs = get_irq_regs(); - unsigned int cnt; - u32 flags; - - - /* - * Stop IRQ generation - */ - armv7_stop_pmnc(); - - /* - * Get and reset overflow status flags - */ - flags = armv7_pmnc_getreset_flags(); - - /* - * Cycle counter - */ - if (flags & FLAG_C) { - u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), CCNT); - armv7_pmnc_reset_counter(CCNT); - oprofile_add_sample(regs, cpu_cnt); - } - - /* - * PMNC counters 0:3 - */ - for (cnt = CNT0; cnt < CNTMAX; cnt++) { - if (flags & (1 << (cnt - CNT0))) { - u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), cnt); - armv7_pmnc_reset_counter(cnt); - oprofile_add_sample(regs, cpu_cnt); - } - } - - /* - * Allow IRQ generation - */ - armv7_start_pmnc(); - - return IRQ_HANDLED; -} - -int armv7_request_interrupts(int *irqs, int nr) -{ - unsigned int i; - int ret = 0; - - for (i = 0; i < nr; i++) { - ret = request_irq(irqs[i], armv7_pmnc_interrupt, - IRQF_DISABLED, "CP15 PMNC", NULL); - if (ret != 0) { - printk(KERN_ERR "oprofile: unable to request IRQ%u" - " for ARMv7\n", - irqs[i]); - break; - } - } - - if (i != nr) - while (i-- != 0) - free_irq(irqs[i], NULL); - - return ret; -} - -void armv7_release_interrupts(int *irqs, int nr) -{ - unsigned int i; - - for (i = 0; i < nr; i++) - free_irq(irqs[i], NULL); -} - -#ifdef DEBUG -static void armv7_pmnc_dump_regs(void) -{ - u32 val; - unsigned int cnt; - - printk(KERN_INFO "PMNC registers dump:\n"); - - asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val)); - printk(KERN_INFO "PMNC =0x%08x\n", val); - - asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r" (val)); - printk(KERN_INFO "CNTENS=0x%08x\n", val); - - asm volatile("mrc p15, 0, %0, c9, c14, 1" : "=r" (val)); - printk(KERN_INFO "INTENS=0x%08x\n", val); - - asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val)); - printk(KERN_INFO "FLAGS =0x%08x\n", val); - - asm volatile("mrc p15, 0, %0, c9, c12, 5" : "=r" (val)); - printk(KERN_INFO "SELECT=0x%08x\n", val); - - asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val)); - printk(KERN_INFO "CCNT =0x%08x\n", val); - - for (cnt = CNT0; cnt < CNTMAX; cnt++) { - armv7_pmnc_select_counter(cnt); - asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val)); - printk(KERN_INFO "CNT[%d] count =0x%08x\n", cnt-CNT0, val); - asm volatile("mrc p15, 0, %0, c9, c13, 1" : "=r" (val)); - printk(KERN_INFO "CNT[%d] evtsel=0x%08x\n", cnt-CNT0, val); - } -} -#endif - - -static int irqs[] = { -#ifdef CONFIG_ARCH_OMAP3 - INT_34XX_BENCH_MPU_EMUL, -#endif -}; - -static void armv7_pmnc_stop(void) -{ -#ifdef DEBUG - armv7_pmnc_dump_regs(); -#endif - armv7_stop_pmnc(); - armv7_release_interrupts(irqs, ARRAY_SIZE(irqs)); -} - -static int armv7_pmnc_start(void) -{ - int ret; - -#ifdef DEBUG - armv7_pmnc_dump_regs(); -#endif - ret = armv7_request_interrupts(irqs, ARRAY_SIZE(irqs)); - if (ret >= 0) - armv7_start_pmnc(); - - return ret; -} - -static int armv7_detect_pmnc(void) -{ - return 0; -} - -struct op_arm_model_spec op_armv7_spec = { - .init = armv7_detect_pmnc, - .num_counters = 5, - .setup_ctrs = armv7_setup_pmnc, - .start = armv7_pmnc_start, - .stop = armv7_pmnc_stop, - .name = "arm/armv7", -}; diff --git a/arch/arm/oprofile/op_model_v7.h b/arch/arm/oprofile/op_model_v7.h index 0e19bcc2e100..e69de29bb2d1 100644 --- a/arch/arm/oprofile/op_model_v7.h +++ b/arch/arm/oprofile/op_model_v7.h @@ -1,103 +0,0 @@ -/** - * op_model_v7.h - * ARM v7 (Cortex A8) Event Monitor Driver - * - * Copyright 2008 Jean Pihet <jpihet@mvista.com> - * Copyright 2004 ARM SMP Development Team - * Copyright 2000-2004 Deepak Saxena <dsaxena@mvista.com> - * Copyright 2000-2004 MontaVista Software Inc - * Copyright 2004 Dave Jiang <dave.jiang@intel.com> - * Copyright 2004 Intel Corporation - * Copyright 2004 Zwane Mwaikambo <zwane@arm.linux.org.uk> - * Copyright 2004 Oprofile Authors - * - * Read the file COPYING - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#ifndef OP_MODEL_V7_H -#define OP_MODEL_V7_H - -/* - * Per-CPU PMNC: config reg - */ -#define PMNC_E (1 << 0) /* Enable all counters */ -#define PMNC_P (1 << 1) /* Reset all counters */ -#define PMNC_C (1 << 2) /* Cycle counter reset */ -#define PMNC_D (1 << 3) /* CCNT counts every 64th cpu cycle */ -#define PMNC_X (1 << 4) /* Export to ETM */ -#define PMNC_DP (1 << 5) /* Disable CCNT if non-invasive debug*/ -#define PMNC_MASK 0x3f /* Mask for writable bits */ - -/* - * Available counters - */ -#define CCNT 0 -#define CNT0 1 -#define CNT1 2 -#define CNT2 3 -#define CNT3 4 -#define CNTMAX 5 - -#define CPU_COUNTER(cpu, counter) ((cpu) * CNTMAX + (counter)) - -/* - * CNTENS: counters enable reg - */ -#define CNTENS_P0 (1 << 0) -#define CNTENS_P1 (1 << 1) -#define CNTENS_P2 (1 << 2) -#define CNTENS_P3 (1 << 3) -#define CNTENS_C (1 << 31) -#define CNTENS_MASK 0x8000000f /* Mask for writable bits */ - -/* - * CNTENC: counters disable reg - */ -#define CNTENC_P0 (1 << 0) -#define CNTENC_P1 (1 << 1) -#define CNTENC_P2 (1 << 2) -#define CNTENC_P3 (1 << 3) -#define CNTENC_C (1 << 31) -#define CNTENC_MASK 0x8000000f /* Mask for writable bits */ - -/* - * INTENS: counters overflow interrupt enable reg - */ -#define INTENS_P0 (1 << 0) -#define INTENS_P1 (1 << 1) -#define INTENS_P2 (1 << 2) -#define INTENS_P3 (1 << 3) -#define INTENS_C (1 << 31) -#define INTENS_MASK 0x8000000f /* Mask for writable bits */ - -/* - * EVTSEL: Event selection reg - */ -#define EVTSEL_MASK 0x7f /* Mask for writable bits */ - -/* - * SELECT: Counter selection reg - */ -#define SELECT_MASK 0x1f /* Mask for writable bits */ - -/* - * FLAG: counters overflow flag status reg - */ -#define FLAG_P0 (1 << 0) -#define FLAG_P1 (1 << 1) -#define FLAG_P2 (1 << 2) -#define FLAG_P3 (1 << 3) -#define FLAG_C (1 << 31) -#define FLAG_MASK 0x8000000f /* Mask for writable bits */ - - -int armv7_setup_pmu(void); -int armv7_start_pmu(void); -int armv7_stop_pmu(void); -int armv7_request_interrupts(int *, int); -void armv7_release_interrupts(int *, int); - -#endif diff --git a/arch/arm/oprofile/op_scu.c b/arch/arm/oprofile/op_scu.c new file mode 100644 index 000000000000..62186f0acc82 --- /dev/null +++ b/arch/arm/oprofile/op_scu.c @@ -0,0 +1,175 @@ +/** + * @file op_scu.c + * MPCORE Snoop Control Unit Event Monitor Driver + * @remark Copyright 2004-7 ARM SMP Development Team + * @remark Copyright 2000-2004 Deepak Saxena <dsaxena@mvista.com> + * @remark Copyright 2000-2004 MontaVista Software Inc + * @remark Copyright 2004 Dave Jiang <dave.jiang@intel.com> + * @remark Copyright 2004 Intel Corporation + * @remark Copyright 2004 Zwane Mwaikambo <zwane@arm.linux.org.uk> + * @remark Copyright 2004 Oprofile Authors + * + * @remark Read the file COPYING + * + * @author Zwane Mwaikambo + * + */ + +/* #define DEBUG */ +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/oprofile.h> +#include <linux/interrupt.h> +#include <linux/smp.h> + +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/mach/irq.h> +#include <asm/system.h> +#include <mach/hardware.h> + +#include "op_counter.h" +#include "op_arm_model.h" +#include "op_scu.h" + +struct eventmonitor { + unsigned long PMCR; + unsigned char MCEB[8]; + unsigned long MC[8]; +}; + +#define PMCR_E 1 + +/* + * Bitmask of used SCU counters + */ +static unsigned int scu_em_used; + +/* + * 2 helper fns take a counter number from 0-7 (not the userspace-visible counter number) + */ +static inline void scu_reset_counter(struct eventmonitor __iomem *emc, unsigned int n) +{ + writel(-(u32)counter_config[COUNTER_SCU_MN(n)].count, &emc->MC[n]); +} + +static inline void scu_set_event(struct eventmonitor __iomem *emc, unsigned int n, u32 event) +{ + event &= 0xff; + writeb(event, &emc->MCEB[n]); +} + +/* + * SCU counters' IRQ handler (one IRQ per counter => 2 IRQs per CPU) + */ +static irqreturn_t scu_em_interrupt(int irq, void *arg) +{ + struct eventmonitor __iomem *emc = SCU_EVENTMONITORS_VA_BASE; + unsigned int cnt, tmp; + + cnt = irq - IRQ_PMU_SCU0; + oprofile_add_sample(get_irq_regs(), COUNTER_SCU_MN(cnt)); + scu_reset_counter(emc, cnt); + + /* Clear overflow flag for this counter */ + tmp = readl(&emc->PMCR); + tmp &= 0xff00ffff; /* mask out any other overflow flags */ + tmp |= 1 << (cnt + 16); + writel(tmp, &emc->PMCR); + + return IRQ_HANDLED; +} + +/* Configure just the SCU counters that the user has requested */ +void scu_setup(void) +{ + struct eventmonitor __iomem *emc = SCU_EVENTMONITORS_VA_BASE; + unsigned int i; + + scu_em_used = 0; + + for (i = 0; i < NUM_SCU_COUNTERS; i++) { + if (counter_config[COUNTER_SCU_MN(i)].enabled && + counter_config[COUNTER_SCU_MN(i)].event) { + scu_set_event(emc, i, 0); /* disable counter for now */ + scu_em_used |= 1 << i; + } + } +} + +int scu_start(void) +{ + struct eventmonitor __iomem *emc = SCU_EVENTMONITORS_VA_BASE; + unsigned int temp, i; + unsigned long event; + int ret = 0; + + /* + * request the SCU counter interrupts that we need + */ + for (i = 0; i < NUM_SCU_COUNTERS; i++) { + if (scu_em_used & (1 << i)) { + ret = request_irq(IRQ_PMU_SCU0 + i, scu_em_interrupt, IRQF_DISABLED, + "SCU PMU", NULL); + if (ret) { + printk(KERN_ERR + "oprofile: unable to request IRQ%u for SCU Event Monitor\n", + IRQ_PMU_SCU0 + i); + goto err_free_scu; + } + } + } + + /* + * clear overflow and enable interrupt for all used counters + */ + temp = readl(&emc->PMCR); + for (i = 0; i < NUM_SCU_COUNTERS; i++) { + if (scu_em_used & (1 << i)) { + scu_reset_counter(emc, i); + event = counter_config[COUNTER_SCU_MN(i)].event; + scu_set_event(emc, i, event); + + /* clear overflow/interrupt */ + temp |= 1 << (i + 16); + /* enable interrupt*/ + temp |= 1 << (i + 8); + } + } + + /* Enable all 8 counters */ + temp |= PMCR_E; + writel(temp, &emc->PMCR); + + return 0; + + err_free_scu: + while (i--) + free_irq(IRQ_PMU_SCU0 + i, NULL); + return ret; +} + +void scu_stop(void) +{ + struct eventmonitor __iomem *emc = SCU_EVENTMONITORS_VA_BASE; + unsigned int temp, i; + + /* Disable counter interrupts */ + /* Don't disable all 8 counters (with the E bit) as they may be in use */ + temp = readl(&emc->PMCR); + for (i = 0; i < NUM_SCU_COUNTERS; i++) { + if (scu_em_used & (1 << i)) + temp &= ~(1 << (i + 8)); + } + writel(temp, &emc->PMCR); + + /* Free counter interrupts and reset counters */ + for (i = 0; i < NUM_SCU_COUNTERS; i++) { + if (scu_em_used & (1 << i)) { + scu_reset_counter(emc, i); + free_irq(IRQ_PMU_SCU0 + i, NULL); + } + } +} + diff --git a/arch/arm/oprofile/op_scu.h b/arch/arm/oprofile/op_scu.h new file mode 100644 index 000000000000..572bd7df8264 --- /dev/null +++ b/arch/arm/oprofile/op_scu.h @@ -0,0 +1,23 @@ +/** + * @file op_scu.c + * MPCORE Snoop Control Unit Event Monitor Driver + * @remark Copyright 2004 ARM SMP Development Team + * @remark Copyright 2000-2004 Deepak Saxena <dsaxena@mvista.com> + * @remark Copyright 2000-2004 MontaVista Software Inc + * @remark Copyright 2004 Dave Jiang <dave.jiang@intel.com> + * @remark Copyright 2004 Intel Corporation + * @remark Copyright 2004 Zwane Mwaikambo <zwane@arm.linux.org.uk> + * @remark Copyright 2004 Oprofile Authors + * + * @remark Read the file COPYING + * + * @author Zwane Mwaikambo + */ +#ifndef OP_SCU_H +#define OP_SCU_H + +void scu_setup(void); +int scu_start(void); +void scu_stop(void); + +#endif diff --git a/arch/arm/oprofile/op_v7.c b/arch/arm/oprofile/op_v7.c new file mode 100644 index 000000000000..a71fcccc441e --- /dev/null +++ b/arch/arm/oprofile/op_v7.c @@ -0,0 +1,248 @@ +/** + * @file op_v7.c + * ARM V7 Performance Monitor Unit Driver + * + * @remark Copyright 2007 ARM SMP Development Team + * + * @remark Read the file COPYING + */ +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/oprofile.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/smp.h> + +#include "op_counter.h" +#include "op_arm_model.h" +#include "op_v7.h" + +/* + * We assume each CPU has PMU_COUNTERS_PER_CPU counters, where the + * last one is a cycle counter, the rest are event counters. + * The oprofile event files in userland should ensure that we will not + * access counters that aren't physically present, but we also check + * the counter numbers here. + */ + +#define PMCR_N_MASK 0xf800 +#define PMCR_N_SHIFT 11 + +static unsigned event_counters_per_cpu; + +/* + * ARM V7 PMU support + */ +static inline void v7_write_pmcr(u32 val) +{ + asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val)); +} + +static inline u32 v7_read_pmcr(void) +{ + u32 val; + asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val)); + return val; +} + +static inline void v7_reset_counter(unsigned int cpu, unsigned int cnt) +{ + u32 val = -(u32)counter_config[COUNTER_CPUn_PMNm(cpu, cnt)].count; + + if (cnt == CCNT) + /* Set cycle count in PMCCNTR */ + asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (val)); + else { + /* Select correct counter using PMSELR */ + asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (cnt)); + /* Set the count value */ + asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (val)); + } +} + +static inline void v7_set_event(unsigned int cnt, u32 val) +{ + /* Select correct counter using PMSELR */ + asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (cnt)); + /* Set event type in PMXEVTYPER*/ + asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val)); +} + +static inline void v7_clear_overflow_status(u32 status) +{ + /* Clear overflow bits in PMOVSR */ + asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (status)); +} + +static inline u32 v7_read_overflow_status(void) +{ + u32 status; + + /* Read overflow bits in PMOVSR */ + asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (status)); + + return status; +} + +static inline void v7_enable_counter(unsigned int cnt) +{ + u32 val; + + if (cnt == CCNT) + val = PMCNTEN_CCNT; + else + val = PMCNTEN_PMN0 << cnt; + + /* Set bit in PMCNTEN */ + asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val)); +} + +static inline void v7_disable_counter(unsigned int cnt) +{ + u32 val; + + if (cnt == CCNT) + val = PMCNTEN_CCNT; + else + val = PMCNTEN_PMN0 << cnt; + + /* Clear bit in PMCNTEN */ + asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val)); +} + +static inline void v7_set_interrupts(u32 interrupts) +{ + /* Clear all interrupts in PMINTENCLR */ + asm volatile("mcr p15, 0, %0, c9, c14, 2" : : "r" (0xFFFFFFFFU)); + + /* Set requested interrupts in PMINTENSET */ + asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (interrupts)); +} + +int v7_setup_pmu(void) +{ + unsigned int cnt, cpu; + u32 pmcr, interrupts; + + /* + * No need for get_cpu/put_cpu, because we were either called + * from em_call_function(), which itself uses get_cpu/put_cpu, + * or smp_call_function(), which means we are in IRQ context. + */ + pmcr = v7_read_pmcr(); + cpu = smp_processor_id(); + + if (pmcr & PMCR_E) { + printk(KERN_ERR "oprofile: CPU%u PMU still enabled when setup new event counter.\n", cpu); + return -EBUSY; + } + + /* Discover the number of counters */ + event_counters_per_cpu = (pmcr & PMCR_N_MASK) >> PMCR_N_SHIFT; + + /* initialize PMNC: reset all counters, clear DP,X,D,E bits */ + v7_write_pmcr(PMCR_C | PMCR_P); + for (cnt = 0; cnt < event_counters_per_cpu; cnt++) + v7_disable_counter(cnt); + v7_disable_counter(CCNT); + + interrupts = 0; + + /* Set up the event counters */ + for (cnt = 0; cnt < event_counters_per_cpu; cnt++) { + unsigned long event; + + if (!counter_config[COUNTER_CPUn_PMNm(cpu, cnt)].enabled) + continue; + + event = counter_config[COUNTER_CPUn_PMNm(cpu, cnt)].event & 255; + + v7_set_event(cnt, event); + v7_reset_counter(cpu, cnt); + v7_enable_counter(cnt); + interrupts |= PMINTEN_PMN0 << cnt; + } + + /* Now set up the cycle counter */ + if (counter_config[COUNTER_CPUn_CCNT(cpu)].enabled) { + v7_reset_counter(cpu, CCNT); + v7_enable_counter(CCNT); + interrupts |= PMINTEN_CCNT; + } + + /* Enable the required interrupts */ + v7_set_interrupts(interrupts); + + return 0; +} + +int v7_start_pmu(void) +{ + v7_write_pmcr(v7_read_pmcr() | PMCR_E); + return 0; +} + +int v7_stop_pmu(void) +{ + v7_write_pmcr(v7_read_pmcr() & ~PMCR_E); + return 0; +} + +/* + * CPU counters' IRQ handler (one IRQ per CPU) + */ +static irqreturn_t v7_pmu_interrupt(int irq, void *arg) +{ + struct pt_regs *regs = get_irq_regs(); + unsigned int cnt, cpu; + u32 overflowed; + + overflowed = v7_read_overflow_status(); + cpu = smp_processor_id(); + + /* Check each event counter */ + for (cnt = 0; cnt < CCNT; cnt++) { + if (overflowed & (PMOVSR_PMN0 << cnt)) { + v7_reset_counter(cpu, cnt); + oprofile_add_sample(regs, COUNTER_CPUn_PMNm(cpu, cnt)); + } + } + + /* Check the cycle counter */ + if (overflowed & PMOVSR_CCNT) { + v7_reset_counter(cpu, CCNT); + oprofile_add_sample(regs, COUNTER_CPUn_CCNT(cpu)); + } + + v7_clear_overflow_status(overflowed); + return IRQ_HANDLED; +} + +int v7_request_interrupts(int *irqs, int nr) +{ + unsigned int i; + int ret = 0; + + for(i = 0; i < nr; i++) { + ret = request_irq(irqs[i], v7_pmu_interrupt, IRQF_DISABLED, "CP15 PMU", NULL); + if (ret != 0) { + printk(KERN_ERR "oprofile: unable to request IRQ%u for CP15 PMU\n", + irqs[i]); + break; + } + } + + if (i != nr) + while (i-- != 0) + free_irq(irqs[i], NULL); + + return ret; +} + +void v7_release_interrupts(int *irqs, int nr) +{ + unsigned int i; + + for (i = 0; i < nr; i++) + free_irq(irqs[i], NULL); +} diff --git a/arch/arm/oprofile/op_v7.h b/arch/arm/oprofile/op_v7.h new file mode 100644 index 000000000000..03ca7dfeecf5 --- /dev/null +++ b/arch/arm/oprofile/op_v7.h @@ -0,0 +1,36 @@ +/** + * @file op_v7.h + * ARM V7 Performance Monitor Unit Driver + * + * @remark Copyright 2007 ARM SMP Development Team + * + * @remark Read the file COPYING + */ +#ifndef OP_V7_H +#define OP_V7_H + +/* + * V7 CP15 PMU + */ +#define PMCR_E (1 << 0) /* Enable */ +#define PMCR_P (1 << 1) /* Count reset */ +#define PMCR_C (1 << 2) /* Cycle counter reset */ +#define PMCR_D (1 << 3) /* Cycle counter counts every 64th cpu cycle */ + +#define PMINTEN_PMN0 (1 << 0) +#define PMINTEN_CCNT (1 << 31) + +#define PMOVSR_PMN0 (1 << 0) +#define PMOVSR_CCNT (1 << 31) + +#define PMCNTEN_PMN0 (1 << 0) +#define PMCNTEN_CCNT (1 << 31) + + +int v7_setup_pmu(void); +int v7_start_pmu(void); +int v7_stop_pmu(void); +int v7_request_interrupts(int *, int); +void v7_release_interrupts(int *, int); + +#endif diff --git a/arch/arm/tools/gen-mach-types b/arch/arm/tools/gen-mach-types index ce319ef64bc1..223173670ad0 100644 --- a/arch/arm/tools/gen-mach-types +++ b/arch/arm/tools/gen-mach-types @@ -68,5 +68,11 @@ END { printf("\n#ifndef machine_arch_type\n"); printf("#define machine_arch_type\t__machine_arch_type\n"); printf("#endif\n\n"); + + printf("#define __machine_arch_type\t-1\n"); + printf("#if defined(CONFIG_NAKED_BOOT) && (machine_arch_type < 0)\n"); + printf("#error \"CONFIG_NAKED_BOOT may be used with a single-machine config only\"\n"); + printf("#endif\n"); + printf("#undef __machine_arch_type\n\n"); printf("#endif\n"); } diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types index b4211d8b2ac7..ccdbc266c694 100644 --- a/arch/arm/tools/mach-types +++ b/arch/arm/tools/mach-types @@ -2124,3 +2124,4 @@ mx27wallace MACH_MX27WALLACE MX27WALLACE 2133 fmzwebmodul MACH_FMZWEBMODUL FMZWEBMODUL 2134 rd78x00_masa MACH_RD78X00_MASA RD78X00_MASA 2135 smallogger MACH_SMALLOGGER SMALLOGGER 2136 +mps MACH_MPS MPS 10000 diff --git a/arch/arm/vfp/entry.S b/arch/arm/vfp/entry.S index a2bed62aec21..4fa9903b83cf 100644 --- a/arch/arm/vfp/entry.S +++ b/arch/arm/vfp/entry.S @@ -42,6 +42,7 @@ ENTRY(vfp_null_entry) mov pc, lr ENDPROC(vfp_null_entry) + .align 2 .LCvfp: .word vfp_vector @@ -61,6 +62,7 @@ ENTRY(vfp_testing_entry) mov pc, r9 @ we have handled the fault ENDPROC(vfp_testing_entry) + .align 2 VFP_arch_address: .word VFP_arch diff --git a/arch/arm/vfp/vfp.h b/arch/arm/vfp/vfp.h index c8c98dd44ad4..8a5fe9c6cccd 100644 --- a/arch/arm/vfp/vfp.h +++ b/arch/arm/vfp/vfp.h @@ -37,6 +37,7 @@ static inline u32 vfp_hi64to32jamming(u64 val) asm( "cmp %Q1, #1 @ vfp_hi64to32jamming\n\t" + "ite cc\n\t" "movcc %0, %R1\n\t" "orrcs %0, %R1, #1" : "=r" (v) : "r" (val) : "cc"); diff --git a/arch/arm/vfp/vfphw.S b/arch/arm/vfp/vfphw.S index 83c4e384b16d..f570411e32af 100644 --- a/arch/arm/vfp/vfphw.S +++ b/arch/arm/vfp/vfphw.S @@ -1,5 +1,4 @@ -/* - * linux/arch/arm/vfp/vfphw.S +/* linux/arch/arm/vfp/vfphw.S * * Copyright (C) 2004 ARM Limited. * Written by Deep Blue Solutions Limited. @@ -205,40 +204,52 @@ ENDPROC(vfp_save_state) last_VFP_context_address: .word last_VFP_context + .macro tbl_branch, base, tmp, shift + ARM( add pc, pc, \base, lsl \shift ) + ARM( mov r0, r0 ) + THUMB( adr \tmp, 1f ) + THUMB( add \tmp, \tmp, \base, lsl \shift ) + THUMB( mov pc, \tmp ) +1: + .endm + ENTRY(vfp_get_float) - add pc, pc, r0, lsl #3 - mov r0, r0 + tbl_branch r0, r3, #3 .irp dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 - mrc p10, 0, r0, c\dr, c0, 0 @ fmrs r0, s0 +1: mrc p10, 0, r0, c\dr, c0, 0 @ fmrs r0, s0 mov pc, lr - mrc p10, 0, r0, c\dr, c0, 4 @ fmrs r0, s1 + .org 1b + 8 +1: mrc p10, 0, r0, c\dr, c0, 4 @ fmrs r0, s1 mov pc, lr + .org 1b + 8 .endr ENDPROC(vfp_get_float) ENTRY(vfp_put_float) - add pc, pc, r1, lsl #3 - mov r0, r0 + tbl_branch r1, r3, #3 .irp dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 - mcr p10, 0, r0, c\dr, c0, 0 @ fmsr r0, s0 +1: mcr p10, 0, r0, c\dr, c0, 0 @ fmsr r0, s0 mov pc, lr - mcr p10, 0, r0, c\dr, c0, 4 @ fmsr r0, s1 + .org 1b + 8 +1: mcr p10, 0, r0, c\dr, c0, 4 @ fmsr r0, s1 mov pc, lr + .org 1b + 8 .endr ENDPROC(vfp_put_float) ENTRY(vfp_get_double) - add pc, pc, r0, lsl #3 - mov r0, r0 + tbl_branch r0, r3, #3 .irp dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 - fmrrd r0, r1, d\dr +1: fmrrd r0, r1, d\dr mov pc, lr + .org 1b + 8 .endr #ifdef CONFIG_VFPv3 @ d16 - d31 registers .irp dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 - mrrc p11, 3, r0, r1, c\dr @ fmrrd r0, r1, d\dr +1: mrrc p11, 3, r0, r1, c\dr @ fmrrd r0, r1, d\dr mov pc, lr + .org 1b + 8 .endr #endif @@ -249,17 +260,18 @@ ENTRY(vfp_get_double) ENDPROC(vfp_get_double) ENTRY(vfp_put_double) - add pc, pc, r2, lsl #3 - mov r0, r0 + tbl_branch r2, r3, #3 .irp dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 - fmdrr d\dr, r0, r1 +1: fmdrr d\dr, r0, r1 mov pc, lr + .org 1b + 8 .endr #ifdef CONFIG_VFPv3 @ d16 - d31 registers .irp dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 - mcrr p11, 3, r1, r2, c\dr @ fmdrr r1, r2, d\dr +1: mcrr p11, 3, r1, r2, c\dr @ fmdrr r1, r2, d\dr mov pc, lr + .org 1b + 8 .endr #endif ENDPROC(vfp_put_double) diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c index 01599c4ef726..503eadb31be3 100644 --- a/arch/arm/vfp/vfpmodule.c +++ b/arch/arm/vfp/vfpmodule.c @@ -14,6 +14,7 @@ #include <linux/signal.h> #include <linux/sched.h> #include <linux/init.h> +#include <linux/rcupdate.h> #include <asm/thread_notify.h> #include <asm/vfp.h> @@ -49,14 +50,21 @@ static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v) #ifdef CONFIG_SMP /* + * RCU locking is needed in case last_VFP_context[cpu] is + * released on a different CPU. + */ + rcu_read_lock(); + vfp = last_VFP_context[cpu]; + /* * On SMP, if VFP is enabled, save the old state in * case the thread migrates to a different CPU. The * restoring is done lazily. */ - if ((fpexc & FPEXC_EN) && last_VFP_context[cpu]) { - vfp_save_state(last_VFP_context[cpu], fpexc); - last_VFP_context[cpu]->hard.cpu = cpu; + if ((fpexc & FPEXC_EN) && vfp) { + vfp_save_state(vfp, fpexc); + vfp->hard.cpu = cpu; } + rcu_read_unlock(); /* * Thread migration, just force the reloading of the * state on the new CPU in case the VFP registers @@ -91,8 +99,19 @@ static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v) } /* flush and release case: Per-thread VFP cleanup. */ +#ifndef CONFIG_SMP if (last_VFP_context[cpu] == vfp) last_VFP_context[cpu] = NULL; +#else + /* + * Since release_thread() may be called from a different CPU, we use + * cmpxchg() here to avoid a race with the vfp_support_entry() code + * which modifies last_VFP_context[cpu]. Note that on SMP systems, a + * STR instruction on a different CPU clears the global exclusive + * monitor state. + */ + (void)cmpxchg(&last_VFP_context[cpu], vfp, NULL); +#endif return NOTIFY_DONE; } diff --git a/drivers/i2c/busses/i2c-versatile.c b/drivers/i2c/busses/i2c-versatile.c index 4678babd3ce6..15b5e8a14d60 100644 --- a/drivers/i2c/busses/i2c-versatile.c +++ b/drivers/i2c/busses/i2c-versatile.c @@ -96,13 +96,20 @@ static int i2c_versatile_probe(struct platform_device *dev) writel(SCL | SDA, i2c->base + I2C_CONTROLS); i2c->adap.owner = THIS_MODULE; + if (dev->id >= 0) + i2c->adap.nr = dev->id; strlcpy(i2c->adap.name, "Versatile I2C adapter", sizeof(i2c->adap.name)); i2c->adap.algo_data = &i2c->algo; i2c->adap.dev.parent = &dev->dev; i2c->algo = i2c_versatile_algo; i2c->algo.data = i2c; - ret = i2c_bit_add_bus(&i2c->adap); + if (dev->id >= 0) + /* static bus numbering */ + ret = i2c_bit_add_numbered_bus(&i2c->adap); + else + /* dynamic bus numbering */ + ret = i2c_bit_add_bus(&i2c->adap); if (ret >= 0) { platform_set_drvdata(dev, i2c); return 0; @@ -146,7 +153,7 @@ static void __exit i2c_versatile_exit(void) platform_driver_unregister(&i2c_versatile_driver); } -module_init(i2c_versatile_init); +subsys_initcall(i2c_versatile_init); module_exit(i2c_versatile_exit); MODULE_DESCRIPTION("ARM Versatile I2C bus driver"); diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 2909bbc8ad00..ff2705a1cd6b 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -484,7 +484,7 @@ static void mmci_check_status(unsigned long data) status = host->plat->status(mmc_dev(host->mmc)); if (status ^ host->oldstat) - mmc_detect_change(host->mmc, 0); + mmc_detect_change(host->mmc, (msecs_to_jiffies(500))); host->oldstat = status; mod_timer(&host->timer, jiffies + HZ); diff --git a/drivers/mtd/maps/integrator-flash.c b/drivers/mtd/maps/integrator-flash.c index d2ec262666c7..73d7082fc2c4 100644 --- a/drivers/mtd/maps/integrator-flash.c +++ b/drivers/mtd/maps/integrator-flash.c @@ -35,28 +35,34 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> #include <linux/mtd/partitions.h> +#include <linux/mtd/concat.h> #include <asm/mach/flash.h> #include <mach/hardware.h> #include <asm/io.h> #include <asm/system.h> -#ifdef CONFIG_ARCH_P720T -#define FLASH_BASE (0x04000000) -#define FLASH_SIZE (64*1024*1024) -#endif +#define SUBDEV_NAME_SIZE (BUS_ID_SIZE + 2) -struct armflash_info { +struct armflash_subdev_info { + char name[SUBDEV_NAME_SIZE]; + struct mtd_info *mtd; + struct map_info map; struct flash_platform_data *plat; +}; + +struct armflash_info { struct resource *res; struct mtd_partition *parts; struct mtd_info *mtd; - struct map_info map; + int nr_subdev; + struct armflash_subdev_info subdev[0]; }; static void armflash_set_vpp(struct map_info *map, int on) { - struct armflash_info *info = container_of(map, struct armflash_info, map); + struct armflash_subdev_info *info = + container_of(map, struct armflash_subdev_info, map); if (info->plat && info->plat->set_vpp) info->plat->set_vpp(on); @@ -64,32 +70,17 @@ static void armflash_set_vpp(struct map_info *map, int on) static const char *probes[] = { "cmdlinepart", "RedBoot", "afs", NULL }; -static int armflash_probe(struct platform_device *dev) +static int armflash_subdev_probe(struct armflash_subdev_info *subdev, + struct resource *res) { - struct flash_platform_data *plat = dev->dev.platform_data; - struct resource *res = dev->resource; - unsigned int size = res->end - res->start + 1; - struct armflash_info *info; - int err; + struct flash_platform_data *plat = subdev->plat; + resource_size_t size = res->end - res->start + 1; void __iomem *base; + int err = 0; - info = kzalloc(sizeof(struct armflash_info), GFP_KERNEL); - if (!info) { - err = -ENOMEM; - goto out; - } - - info->plat = plat; - if (plat && plat->init) { - err = plat->init(); - if (err) - goto no_resource; - } - - info->res = request_mem_region(res->start, size, "armflash"); - if (!info->res) { + if (!request_mem_region(res->start, size, subdev->name)) { err = -EBUSY; - goto no_resource; + goto out; } base = ioremap(res->start, size); @@ -108,20 +99,125 @@ static int armflash_probe(struct platform_device *dev) info->map.name = dev_name(&dev->dev); info->map.set_vpp = armflash_set_vpp; - simple_map_init(&info->map); + simple_map_init(&subdev->map); /* * Also, the CFI layer automatically works out what size * of chips we have, and does the necessary identification * for us automatically. */ - info->mtd = do_map_probe(plat->map_name, &info->map); - if (!info->mtd) { + subdev->mtd = do_map_probe(plat->map_name, &subdev->map); + if (!subdev->mtd) { err = -ENXIO; goto no_device; } - info->mtd->owner = THIS_MODULE; + subdev->mtd->owner = THIS_MODULE; + + /* Successful? */ + if (err == 0) + return err; + + if (subdev->mtd) + map_destroy(subdev->mtd); + no_device: + iounmap(base); + no_mem: + release_mem_region(res->start, size); + out: + return err; +} + +static void armflash_subdev_remove(struct armflash_subdev_info *subdev) +{ + if (subdev->mtd) + map_destroy(subdev->mtd); + if (subdev->map.virt) + iounmap(subdev->map.virt); + release_mem_region(subdev->map.phys, subdev->map.size); +} + +static int armflash_probe(struct platform_device *dev) +{ + struct flash_platform_data *plat = dev->dev.platform_data; + unsigned int size; + struct armflash_info *info; + int i, nr, err; + + /* Count the number of devices */ + for (nr = 0; ; nr++) + if (!platform_get_resource(dev, IORESOURCE_MEM, nr)) + break; + if (nr == 0) { + err = -ENODEV; + goto out; + } + + size = sizeof(struct armflash_info) + + sizeof(struct armflash_subdev_info) * nr; + info = kzalloc(size, GFP_KERNEL); + if (!info) { + err = -ENOMEM; + goto out; + } + + if (plat && plat->init) { + err = plat->init(); + if (err) + goto no_resource; + } + + for (i = 0; i < nr; i++) { + struct armflash_subdev_info *subdev = &info->subdev[i]; + struct resource *res; + + res = platform_get_resource(dev, IORESOURCE_MEM, i); + if (!res) + break; + + if (nr == 1) + /* No MTD concatenation, just use the default name */ + snprintf(subdev->name, SUBDEV_NAME_SIZE, "%s", + dev_name(&dev->dev)); + else + snprintf(subdev->name, SUBDEV_NAME_SIZE, "%s-%d", + dev_name(&dev->dev), i); + subdev->plat = plat; + + err = armflash_subdev_probe(subdev, res); + if (err) + break; + } + info->nr_subdev = i; + + if (err) + goto subdev_err; + + if (info->nr_subdev == 1) + info->mtd = info->subdev[0].mtd; + else if (info->nr_subdev > 1) { +#ifdef CONFIG_MTD_CONCAT + struct mtd_info *cdev[info->nr_subdev]; + + /* + * We detected multiple devices. Concatenate them together. + */ + for (i = 0; i < info->nr_subdev; i++) + cdev[i] = info->subdev[i].mtd; + + info->mtd = mtd_concat_create(cdev, info->nr_subdev, + dev_name(&dev->dev)); + if (info->mtd == NULL) + err = -ENXIO; +#else + printk(KERN_ERR "armflash: multiple devices found but " + "MTD concat support disabled.\n"); + err = -ENXIO; +#endif + } + + if (err < 0) + goto cleanup; err = parse_mtd_partitions(info->mtd, probes, &info->parts, 0); if (err > 0) { @@ -131,28 +227,30 @@ static int armflash_probe(struct platform_device *dev) "mtd partition registration failed: %d\n", err); } - if (err == 0) + if (err == 0) { platform_set_drvdata(dev, info); + return err; + } /* - * If we got an error, free all resources. + * We got an error, free all resources. */ - if (err < 0) { - if (info->mtd) { - del_mtd_partitions(info->mtd); - map_destroy(info->mtd); - } - kfree(info->parts); - - no_device: - iounmap(base); - no_mem: - release_mem_region(res->start, size); - no_resource: - if (plat && plat->exit) - plat->exit(); - kfree(info); + cleanup: + if (info->mtd) { + del_mtd_partitions(info->mtd); +#ifdef CONFIG_MTD_CONCAT + if (info->mtd != info->subdev[0].mtd) + mtd_concat_destroy(info->mtd); +#endif } + kfree(info->parts); + subdev_err: + for (i = info->nr_subdev - 1; i >= 0; i--) + armflash_subdev_remove(&info->subdev[i]); + no_resource: + if (plat && plat->exit) + plat->exit(); + kfree(info); out: return err; } @@ -160,22 +258,26 @@ static int armflash_probe(struct platform_device *dev) static int armflash_remove(struct platform_device *dev) { struct armflash_info *info = platform_get_drvdata(dev); + struct flash_platform_data *plat = dev->dev.platform_data; + int i; platform_set_drvdata(dev, NULL); if (info) { if (info->mtd) { del_mtd_partitions(info->mtd); - map_destroy(info->mtd); +#ifdef CONFIG_MTD_CONCAT + if (info->mtd != info->subdev[0].mtd) + mtd_concat_destroy(info->mtd); +#endif } kfree(info->parts); - iounmap(info->map.virt); - release_resource(info->res); - kfree(info->res); + for (i = info->nr_subdev - 1; i >= 0; i--) + armflash_subdev_remove(&info->subdev[i]); - if (info->plat && info->plat->exit) - info->plat->exit(); + if (plat && plat->exit) + plat->exit(); kfree(info); } diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index 1387187543e4..3448e2e45daa 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -159,6 +159,27 @@ static struct phy_driver lan911x_int_driver = { .driver = { .owner = THIS_MODULE, } }; +static struct phy_driver lan911x_int_driver = { + .phy_id = 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */ + .phy_id_mask = 0xfffffff0, + .name = "SMSC LAN911x Internal PHY", + + .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause + | SUPPORTED_Asym_Pause), + .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, + + /* basic functions */ + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, + .config_init = smsc_phy_config_init, + + /* IRQ related */ + .ack_interrupt = smsc_phy_ack_interrupt, + .config_intr = smsc_phy_config_intr, + + .driver = { .owner = THIS_MODULE, } +}; + static int __init smsc_init(void) { int ret; diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index b2ceb4aff233..89299a5ce168 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_USB_SL811_HCD) += host/ obj-$(CONFIG_USB_U132_HCD) += host/ obj-$(CONFIG_USB_R8A66597_HCD) += host/ obj-$(CONFIG_USB_HWA_HCD) += host/ +obj-$(CONFIG_USB_ISP1760_HCD) += host/ obj-$(CONFIG_USB_C67X00_HCD) += c67x00/ diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 2c63bfb1f8d9..56de061ff30f 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -123,7 +123,7 @@ config USB_ISP116X_HCD config USB_ISP1760_HCD tristate "ISP 1760 HCD support" - depends on USB && EXPERIMENTAL && (PCI || PPC_OF) + depends on USB && EXPERIMENTAL ---help--- The ISP1760 chip is a USB 2.0 host controller. diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index b899f1a59c26..a3a7f8c79618 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -819,6 +819,8 @@ static void enqueue_an_ATL_packet(struct usb_hcd *hcd, struct isp1760_qh *qh, u32 atl_regs, payload; u32 buffstatus; + /* wait for the SKIPMAP register to be updated */ + udelay(1); skip_map = isp1760_readl(hcd->regs + HC_ATL_PTD_SKIPMAP_REG); BUG_ON(!skip_map); @@ -853,6 +855,8 @@ static void enqueue_an_INT_packet(struct usb_hcd *hcd, struct isp1760_qh *qh, u32 int_regs, payload; u32 buffstatus; + /* wait for the SKIPMAP register to be updated */ + udelay(1); skip_map = isp1760_readl(hcd->regs + HC_INT_PTD_SKIPMAP_REG); BUG_ON(!skip_map); @@ -2235,9 +2239,10 @@ void deinit_kmem_cache(void) kmem_cache_destroy(qh_cachep); } -struct usb_hcd *isp1760_register(u64 res_start, u64 res_len, int irq, - u64 irqflags, struct device *dev, const char *busname, - unsigned int devflags) +struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len, + int irq, unsigned long irqflags, + struct device *dev, const char *busname, + unsigned int devflags) { struct usb_hcd *hcd; struct isp1760_hcd *priv; diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h index a9daea587962..462f4943cb1b 100644 --- a/drivers/usb/host/isp1760-hcd.h +++ b/drivers/usb/host/isp1760-hcd.h @@ -2,9 +2,10 @@ #define _ISP1760_HCD_H_ /* exports for if */ -struct usb_hcd *isp1760_register(u64 res_start, u64 res_len, int irq, - u64 irqflags, struct device *dev, const char *busname, - unsigned int devflags); +struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len, + int irq, unsigned long irqflags, + struct device *dev, const char *busname, + unsigned int devflags); int init_kmem_once(void); void deinit_kmem_cache(void); diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index 4cf7ca428b33..3fa3a1702796 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -10,6 +10,7 @@ #include <linux/usb.h> #include <linux/io.h> +#include <linux/platform_device.h> #include "../core/hcd.h" #include "isp1760-hcd.h" @@ -300,39 +301,101 @@ static struct pci_driver isp1761_pci_driver = { }; #endif +static int __devinit isp1760_plat_probe(struct platform_device *pdev) +{ + int ret = 0; + struct usb_hcd *hcd; + struct resource *mem_res; + struct resource *irq_res; + resource_size_t mem_size; + unsigned long irqflags = IRQF_SHARED | IRQF_DISABLED; + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem_res) { + pr_warning("isp1760: Memory resource not available\n"); + ret = -ENODEV; + goto out; + } + mem_size = resource_size(mem_res); + if (!request_mem_region(mem_res->start, mem_size, "isp1760")) { + pr_warning("isp1760: Cannot reserve the memory resource\n"); + ret = -EBUSY; + goto out; + } + + irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq_res) { + pr_warning("isp1760: IRQ resource not available\n"); + return -ENODEV; + } + irqflags |= irq_res->flags & IRQF_TRIGGER_MASK; + + hcd = isp1760_register(mem_res->start, mem_size, irq_res->start, + irqflags, &pdev->dev, dev_name(&pdev->dev), 0); + if (IS_ERR(hcd)) { + pr_warning("isp1760: Failed to register the HCD device\n"); + ret = -ENODEV; + goto cleanup; + } + + pr_info("ISP1760 USB device initialised\n"); + return ret; + +cleanup: + release_mem_region(mem_res->start, mem_size); +out: + return ret; +} + +static int __devexit isp1760_plat_remove(struct platform_device *pdev) +{ + struct resource *mem_res; + resource_size_t mem_size; + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mem_size = resource_size(mem_res); + release_mem_region(mem_res->start, mem_size); + + return 0; +} + +static struct platform_driver isp1760_plat_driver = { + .probe = isp1760_plat_probe, + .remove = isp1760_plat_remove, + .driver = { + .name = "isp1760", + }, +}; + static int __init isp1760_init(void) { - int ret; + int ret, any_ret = -ENODEV; init_kmem_once(); + ret = platform_driver_register(&isp1760_plat_driver); + if (!ret) + any_ret = 0; #ifdef CONFIG_PPC_OF ret = of_register_platform_driver(&isp1760_of_driver); - if (ret) { - deinit_kmem_cache(); - return ret; - } + if (!ret) + any_ret = 0; #endif #ifdef CONFIG_PCI ret = pci_register_driver(&isp1761_pci_driver); - if (ret) - goto unreg_of; + if (!ret) + any_ret = 0; #endif - return ret; -#ifdef CONFIG_PCI -unreg_of: -#endif -#ifdef CONFIG_PPC_OF - of_unregister_platform_driver(&isp1760_of_driver); -#endif - deinit_kmem_cache(); - return ret; + if (any_ret) + deinit_kmem_cache(); + return any_ret; } module_init(isp1760_init); static void __exit isp1760_exit(void) { + platform_driver_unregister(&isp1760_plat_driver); #ifdef CONFIG_PPC_OF of_unregister_platform_driver(&isp1760_of_driver); #endif diff --git a/fs/mpage.c b/fs/mpage.c index 16c3ef37eae3..9b174a1dde94 100644 --- a/fs/mpage.c +++ b/fs/mpage.c @@ -51,6 +51,7 @@ static void mpage_end_io_read(struct bio *bio, int err) prefetchw(&bvec->bv_page->flags); if (uptodate) { + flush_dcache_page(page); SetPageUptodate(page); } else { ClearPageUptodate(page); diff --git a/include/asm-arm/hardware/nvic.h b/include/asm-arm/hardware/nvic.h new file mode 100644 index 000000000000..b7f80266bafd --- /dev/null +++ b/include/asm-arm/hardware/nvic.h @@ -0,0 +1,34 @@ +/* + * linux/include/asm-arm/hardware/nvic.h + * + * Copyright (C) 2008 ARM Limited, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __ASM_ARM_HARDWARE_NVIC_H +#define __ASM_ARM_HARDWARE_NVIC_H + +#include <linux/compiler.h> + +#define V7M_SCS 0xe000e000 +#define NVIC_INTR_CTRL (V7M_SCS + 0x004) +#define NVIC_SYSTICK_CTRL (V7M_SCS + 0x010) +#define NVIC_SYSTICK_RELOAD (V7M_SCS + 0x014) +#define NVIC_SYSTICK_CURRENT (V7M_SCS + 0x018) +#define NVIC_SYSTICK_CALIBRATION (V7M_SCS + 0x01c) +#define NVIC_SET_ENABLE (V7M_SCS + 0x100) +#define NVIC_CLEAR_ENABLE (V7M_SCS + 0x180) +#define NVIC_SET_PENDING (V7M_SCS + 0x200) +#define NVIC_CLEAR_PENDING (V7M_SCS + 0x280) +#define NVIC_ACTIVE_BIT (V7M_SCS + 0x300) +#define NVIC_PRIORITY (V7M_SCS + 0x400) +#define NVIC_INTR_CTRL_STATE (V7M_SCS + 0xd04) +#define NVIC_SOFTWARE_INTR (V7M_SCS + 0xf00) + +#ifndef __ASSEMBLY__ +void nvic_init(void); +#endif + +#endif diff --git a/include/asm-arm/hardware/rvidcc.h b/include/asm-arm/hardware/rvidcc.h new file mode 100644 index 000000000000..2aec6bdea1f3 --- /dev/null +++ b/include/asm-arm/hardware/rvidcc.h @@ -0,0 +1,35 @@ +/* + Copyright (C) 2004-2007 ARM Limited. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License version 2 + as published by the Free Software Foundation. + + 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 + + As a special exception, if other files instantiate templates or use macros + or inline functions from this file, or you compile this file and link it + with other works to produce a work based on this file, this file does not + by itself cause the resulting work to be covered by the GNU General Public + License. However the source code for this file must still be made available + in accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work based on + this file might be covered by the GNU General Public License. +*/ + +#ifndef __RVIDCC_H +#define __RVIDCC_H + +/* Ioctl defines */ +#define DCC_SETPOLLPERIOD _IOW('T', 1, int) +#define DCC_GETPOLLPERIOD _IOR('T', 2, int) + +#endif diff --git a/init/Kconfig b/init/Kconfig index bc991549ee6d..9af864429cff 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -672,6 +672,14 @@ config CC_OPTIMIZE_FOR_SIZE If unsure, say Y. +config CC_OPTIMIZE_FOR_DEBUG + bool "Optimize for debug" + depends on !CC_OPTIMIZE_FOR_SIZE + help + Enabling this option will pass "-O1" instead of "-O2" to gcc + resulting in a better debug illusion, but also a larger and + slower kernel. + config SYSCTL bool diff --git a/init/main.c b/init/main.c index 83697e160b3a..52c0a0eed035 100644 --- a/init/main.c +++ b/init/main.c @@ -779,8 +779,11 @@ static void __init do_pre_smp_initcalls(void) static void run_init_process(char *init_filename) { + long ret; + argv_init[0] = init_filename; - kernel_execve(init_filename, argv_init, envp_init); + ret = kernel_execve(init_filename, argv_init, envp_init); + printk(KERN_INFO "%s exit code: %ld\n", init_filename, ret); } /* This is a non __init function. Force it to be noinline otherwise gcc diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 291f03664552..95037fb15ba1 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -104,6 +104,7 @@ int irq_set_affinity(unsigned int irq, const struct cpumask *cpumask) spin_unlock_irqrestore(&desc->lock, flags); return 0; } +EXPORT_SYMBOL(irq_set_affinity); #ifndef CONFIG_AUTO_IRQ_AFFINITY /* diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 1bcf9cd4baa0..c536b37a11cc 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -402,7 +402,7 @@ config LOCKDEP bool depends on DEBUG_KERNEL && TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT select STACKTRACE - select FRAME_POINTER if !X86 && !MIPS && !PPC + select FRAME_POINTER if !X86 && !MIPS && !PPC && !ARM_UNWIND select KALLSYMS select KALLSYMS_ALL diff --git a/localversion b/localversion new file mode 100644 index 000000000000..f4a15e25b055 --- /dev/null +++ b/localversion @@ -0,0 +1 @@ +-arm2 diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c index 772901e41ecb..33e026db7fa2 100644 --- a/sound/arm/aaci.c +++ b/sound/arm/aaci.c @@ -937,6 +937,7 @@ static int __devinit aaci_probe_ac97(struct aaci *aaci) struct snd_ac97 *ac97; int ret; + writel(0, aaci->base + AC97_POWERDOWN); /* * Assert AACIRESET for 2us */ |