Merge branch 'depends/rmk/devel-stable' into imx/imx6q
This commit is contained in:
		
						commit
						929f58aeeb
					
				| @ -29,6 +29,7 @@ config ARM | ||||
| 	select HAVE_GENERIC_HARDIRQS | ||||
| 	select HAVE_SPARSE_IRQ | ||||
| 	select GENERIC_IRQ_SHOW | ||||
| 	select CPU_PM if (SUSPEND || CPU_IDLE) | ||||
| 	help | ||||
| 	  The ARM series is a line of low-power-consumption RISC chip designs | ||||
| 	  licensed by ARM Ltd and targeted at embedded applications and | ||||
| @ -1832,6 +1833,38 @@ config ZBOOT_ROM_SH_MOBILE_SDHI | ||||
| 
 | ||||
| endchoice | ||||
| 
 | ||||
| config ARM_APPENDED_DTB | ||||
| 	bool "Use appended device tree blob to zImage (EXPERIMENTAL)" | ||||
| 	depends on OF && !ZBOOT_ROM && EXPERIMENTAL | ||||
| 	help | ||||
| 	  With this option, the boot code will look for a device tree binary | ||||
| 	  (DTB) appended to zImage | ||||
| 	  (e.g. cat zImage <filename>.dtb > zImage_w_dtb). | ||||
| 
 | ||||
| 	  This is meant as a backward compatibility convenience for those | ||||
| 	  systems with a bootloader that can't be upgraded to accommodate | ||||
| 	  the documented boot protocol using a device tree. | ||||
| 
 | ||||
| 	  Beware that there is very little in terms of protection against | ||||
| 	  this option being confused by leftover garbage in memory that might | ||||
| 	  look like a DTB header after a reboot if no actual DTB is appended | ||||
| 	  to zImage.  Do not leave this option active in a production kernel | ||||
| 	  if you don't intend to always append a DTB.  Proper passing of the | ||||
| 	  location into r2 of a bootloader provided DTB is always preferable | ||||
| 	  to this option. | ||||
| 
 | ||||
| config ARM_ATAG_DTB_COMPAT | ||||
| 	bool "Supplement the appended DTB with traditional ATAG information" | ||||
| 	depends on ARM_APPENDED_DTB | ||||
| 	help | ||||
| 	  Some old bootloaders can't be updated to a DTB capable one, yet | ||||
| 	  they provide ATAGs with memory configuration, the ramdisk address, | ||||
| 	  the kernel cmdline string, etc.  Such information is dynamically | ||||
| 	  provided by the bootloader and can't always be stored in a static | ||||
| 	  DTB.  To allow a device tree enabled kernel to be used with such | ||||
| 	  bootloaders, this option allows zImage to extract the information | ||||
| 	  from the ATAG list and store it at run time into the appended DTB. | ||||
| 
 | ||||
| config CMDLINE | ||||
| 	string "Default kernel command string" | ||||
| 	default "" | ||||
|  | ||||
| @ -158,4 +158,10 @@ config DEBUG_S3C_UART | ||||
| 	  The uncompressor code port configuration is now handled | ||||
| 	  by CONFIG_S3C_LOWLEVEL_UART_PORT. | ||||
| 
 | ||||
| config ARM_KPROBES_TEST | ||||
| 	tristate "Kprobes test module" | ||||
| 	depends on KPROBES && MODULES | ||||
| 	help | ||||
| 	  Perform tests of kprobes API and instruction set simulation. | ||||
| 
 | ||||
| endmenu | ||||
|  | ||||
							
								
								
									
										9
									
								
								arch/arm/boot/compressed/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								arch/arm/boot/compressed/.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -5,3 +5,12 @@ piggy.lzo | ||||
| piggy.lzma | ||||
| vmlinux | ||||
| vmlinux.lds | ||||
| 
 | ||||
| # borrowed libfdt files | ||||
| fdt.c | ||||
| fdt.h | ||||
| fdt_ro.c | ||||
| fdt_rw.c | ||||
| fdt_wip.c | ||||
| libfdt.h | ||||
| libfdt_internal.h | ||||
|  | ||||
| @ -26,6 +26,10 @@ HEAD	= head.o | ||||
| OBJS	+= misc.o decompress.o | ||||
| FONTC	= $(srctree)/drivers/video/console/font_acorn_8x8.c | ||||
| 
 | ||||
| # string library code (-Os is enforced to keep it much smaller)
 | ||||
| OBJS		+= string.o | ||||
| CFLAGS_string.o	:= -Os | ||||
| 
 | ||||
| #
 | ||||
| # Architecture dependencies
 | ||||
| #
 | ||||
| @ -89,21 +93,41 @@ suffix_$(CONFIG_KERNEL_GZIP) = gzip | ||||
| suffix_$(CONFIG_KERNEL_LZO)  = lzo | ||||
| suffix_$(CONFIG_KERNEL_LZMA) = lzma | ||||
| 
 | ||||
| # Borrowed libfdt files for the ATAG compatibility mode
 | ||||
| 
 | ||||
| libfdt		:= fdt_rw.c fdt_ro.c fdt_wip.c fdt.c | ||||
| libfdt_hdrs	:= fdt.h libfdt.h libfdt_internal.h | ||||
| 
 | ||||
| libfdt_objs	:= $(addsuffix .o, $(basename $(libfdt))) | ||||
| 
 | ||||
| $(addprefix $(obj)/,$(libfdt) $(libfdt_hdrs)): $(obj)/%: $(srctree)/scripts/dtc/libfdt/% | ||||
| 	$(call cmd,shipped) | ||||
| 
 | ||||
| $(addprefix $(obj)/,$(libfdt_objs) atags_to_fdt.o): \ | ||||
| 	$(addprefix $(obj)/,$(libfdt_hdrs)) | ||||
| 
 | ||||
| ifeq ($(CONFIG_ARM_ATAG_DTB_COMPAT),y) | ||||
| OBJS	+= $(libfdt_objs) atags_to_fdt.o | ||||
| endif | ||||
| 
 | ||||
| targets       := vmlinux vmlinux.lds \
 | ||||
| 		 piggy.$(suffix_y) piggy.$(suffix_y).o \
 | ||||
| 		 font.o font.c head.o misc.o $(OBJS) | ||||
| 		 lib1funcs.o lib1funcs.S font.o font.c head.o misc.o $(OBJS) | ||||
| 
 | ||||
| # Make sure files are removed during clean
 | ||||
| extra-y       += piggy.gzip piggy.lzo piggy.lzma lib1funcs.S | ||||
| extra-y       += piggy.gzip piggy.lzo piggy.lzma lib1funcs.S $(libfdt) $(libfdt_hdrs) | ||||
| 
 | ||||
| ifeq ($(CONFIG_FUNCTION_TRACER),y) | ||||
| ORIG_CFLAGS := $(KBUILD_CFLAGS) | ||||
| KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS)) | ||||
| endif | ||||
| 
 | ||||
| ccflags-y := -fpic -fno-builtin | ||||
| ccflags-y := -fpic -fno-builtin -I$(obj) | ||||
| asflags-y := -Wa,-march=all | ||||
| 
 | ||||
| # Supply kernel BSS size to the decompressor via a linker symbol.
 | ||||
| KBSS_SZ = $(shell size $(obj)/../../../../vmlinux | awk 'END{print $$3}') | ||||
| LDFLAGS_vmlinux = --defsym _kernel_bss_size=$(KBSS_SZ) | ||||
| # Supply ZRELADDR to the decompressor via a linker symbol.
 | ||||
| ifneq ($(CONFIG_AUTO_ZRELADDR),y) | ||||
| LDFLAGS_vmlinux += --defsym zreladdr=$(ZRELADDR) | ||||
| @ -123,7 +147,7 @@ LDFLAGS_vmlinux += -T | ||||
| # For __aeabi_uidivmod
 | ||||
| lib1funcs = $(obj)/lib1funcs.o | ||||
| 
 | ||||
| $(obj)/lib1funcs.S: $(srctree)/arch/$(SRCARCH)/lib/lib1funcs.S FORCE | ||||
| $(obj)/lib1funcs.S: $(srctree)/arch/$(SRCARCH)/lib/lib1funcs.S | ||||
| 	$(call cmd,shipped) | ||||
| 
 | ||||
| # We need to prevent any GOTOFF relocs being used with references
 | ||||
|  | ||||
							
								
								
									
										97
									
								
								arch/arm/boot/compressed/atags_to_fdt.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								arch/arm/boot/compressed/atags_to_fdt.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,97 @@ | ||||
| #include <asm/setup.h> | ||||
| #include <libfdt.h> | ||||
| 
 | ||||
| static int node_offset(void *fdt, const char *node_path) | ||||
| { | ||||
| 	int offset = fdt_path_offset(fdt, node_path); | ||||
| 	if (offset == -FDT_ERR_NOTFOUND) | ||||
| 		offset = fdt_add_subnode(fdt, 0, node_path); | ||||
| 	return offset; | ||||
| } | ||||
| 
 | ||||
| static int setprop(void *fdt, const char *node_path, const char *property, | ||||
| 		   uint32_t *val_array, int size) | ||||
| { | ||||
| 	int offset = node_offset(fdt, node_path); | ||||
| 	if (offset < 0) | ||||
| 		return offset; | ||||
| 	return fdt_setprop(fdt, offset, property, val_array, size); | ||||
| } | ||||
| 
 | ||||
| static int setprop_string(void *fdt, const char *node_path, | ||||
| 			  const char *property, const char *string) | ||||
| { | ||||
| 	int offset = node_offset(fdt, node_path); | ||||
| 	if (offset < 0) | ||||
| 		return offset; | ||||
| 	return fdt_setprop_string(fdt, offset, property, string); | ||||
| } | ||||
| 
 | ||||
| static int setprop_cell(void *fdt, const char *node_path, | ||||
| 			const char *property, uint32_t val) | ||||
| { | ||||
| 	int offset = node_offset(fdt, node_path); | ||||
| 	if (offset < 0) | ||||
| 		return offset; | ||||
| 	return fdt_setprop_cell(fdt, offset, property, val); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Convert and fold provided ATAGs into the provided FDT. | ||||
|  * | ||||
|  * REturn values: | ||||
|  *    = 0 -> pretend success | ||||
|  *    = 1 -> bad ATAG (may retry with another possible ATAG pointer) | ||||
|  *    < 0 -> error from libfdt | ||||
|  */ | ||||
| int atags_to_fdt(void *atag_list, void *fdt, int total_space) | ||||
| { | ||||
| 	struct tag *atag = atag_list; | ||||
| 	uint32_t mem_reg_property[2 * NR_BANKS]; | ||||
| 	int memcount = 0; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	/* make sure we've got an aligned pointer */ | ||||
| 	if ((u32)atag_list & 0x3) | ||||
| 		return 1; | ||||
| 
 | ||||
| 	/* if we get a DTB here we're done already */ | ||||
| 	if (*(u32 *)atag_list == fdt32_to_cpu(FDT_MAGIC)) | ||||
| 	       return 0; | ||||
| 
 | ||||
| 	/* validate the ATAG */ | ||||
| 	if (atag->hdr.tag != ATAG_CORE || | ||||
| 	    (atag->hdr.size != tag_size(tag_core) && | ||||
| 	     atag->hdr.size != 2)) | ||||
| 		return 1; | ||||
| 
 | ||||
| 	/* let's give it all the room it could need */ | ||||
| 	ret = fdt_open_into(fdt, fdt, total_space); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	for_each_tag(atag, atag_list) { | ||||
| 		if (atag->hdr.tag == ATAG_CMDLINE) { | ||||
| 			setprop_string(fdt, "/chosen", "bootargs", | ||||
| 					atag->u.cmdline.cmdline); | ||||
| 		} else if (atag->hdr.tag == ATAG_MEM) { | ||||
| 			if (memcount >= sizeof(mem_reg_property)/4) | ||||
| 				continue; | ||||
| 			mem_reg_property[memcount++] = cpu_to_fdt32(atag->u.mem.start); | ||||
| 			mem_reg_property[memcount++] = cpu_to_fdt32(atag->u.mem.size); | ||||
| 		} else if (atag->hdr.tag == ATAG_INITRD2) { | ||||
| 			uint32_t initrd_start, initrd_size; | ||||
| 			initrd_start = atag->u.initrd.start; | ||||
| 			initrd_size = atag->u.initrd.size; | ||||
| 			setprop_cell(fdt, "/chosen", "linux,initrd-start", | ||||
| 					initrd_start); | ||||
| 			setprop_cell(fdt, "/chosen", "linux,initrd-end", | ||||
| 					initrd_start + initrd_size); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (memcount) | ||||
| 		setprop(fdt, "/memory", "reg", mem_reg_property, 4*memcount); | ||||
| 
 | ||||
| 	return fdt_pack(fdt); | ||||
| } | ||||
| @ -216,6 +216,103 @@ restart:	adr	r0, LC0 | ||||
| 		mov	r10, r6 | ||||
| #endif | ||||
| 
 | ||||
| 		mov	r5, #0			@ init dtb size to 0
 | ||||
| #ifdef CONFIG_ARM_APPENDED_DTB | ||||
| /* | ||||
|  *   r0  = delta | ||||
|  *   r2  = BSS start | ||||
|  *   r3  = BSS end | ||||
|  *   r4  = final kernel address | ||||
|  *   r5  = appended dtb size (still unknown) | ||||
|  *   r6  = _edata | ||||
|  *   r7  = architecture ID | ||||
|  *   r8  = atags/device tree pointer | ||||
|  *   r9  = size of decompressed image | ||||
|  *   r10 = end of this image, including  bss/stack/malloc space if non XIP | ||||
|  *   r11 = GOT start | ||||
|  *   r12 = GOT end | ||||
|  *   sp  = stack pointer | ||||
|  * | ||||
|  * if there are device trees (dtb) appended to zImage, advance r10 so that the | ||||
|  * dtb data will get relocated along with the kernel if necessary. | ||||
|  */ | ||||
| 
 | ||||
| 		ldr	lr, [r6, #0] | ||||
| #ifndef __ARMEB__ | ||||
| 		ldr	r1, =0xedfe0dd0		@ sig is 0xd00dfeed big endian
 | ||||
| #else | ||||
| 		ldr	r1, =0xd00dfeed | ||||
| #endif | ||||
| 		cmp	lr, r1 | ||||
| 		bne	dtb_check_done		@ not found
 | ||||
| 
 | ||||
| #ifdef CONFIG_ARM_ATAG_DTB_COMPAT | ||||
| 		/* | ||||
| 		 * OK... Let's do some funky business here. | ||||
| 		 * If we do have a DTB appended to zImage, and we do have | ||||
| 		 * an ATAG list around, we want the later to be translated | ||||
| 		 * and folded into the former here.  To be on the safe side, | ||||
| 		 * let's temporarily move  the stack away into the malloc | ||||
| 		 * area.  No GOT fixup has occurred yet, but none of the | ||||
| 		 * code we're about to call uses any global variable. | ||||
| 		*/ | ||||
| 		add	sp, sp, #0x10000 | ||||
| 		stmfd	sp!, {r0-r3, ip, lr} | ||||
| 		mov	r0, r8 | ||||
| 		mov	r1, r6 | ||||
| 		sub	r2, sp, r6 | ||||
| 		bl	atags_to_fdt | ||||
| 
 | ||||
| 		/* | ||||
| 		 * If returned value is 1, there is no ATAG at the location | ||||
| 		 * pointed by r8.  Try the typical 0x100 offset from start | ||||
| 		 * of RAM and hope for the best. | ||||
| 		 */ | ||||
| 		cmp	r0, #1 | ||||
| 		sub	r0, r4, #(TEXT_OFFSET - 0x100) | ||||
| 		mov	r1, r6 | ||||
| 		sub	r2, sp, r6 | ||||
| 		blne	atags_to_fdt | ||||
| 
 | ||||
| 		ldmfd	sp!, {r0-r3, ip, lr} | ||||
| 		sub	sp, sp, #0x10000 | ||||
| #endif | ||||
| 
 | ||||
| 		mov	r8, r6			@ use the appended device tree
 | ||||
| 
 | ||||
| 		/* | ||||
| 		 * Make sure that the DTB doesn't end up in the final | ||||
| 		 * kernel's .bss area. To do so, we adjust the decompressed | ||||
| 		 * kernel size to compensate if that .bss size is larger | ||||
| 		 * than the relocated code. | ||||
| 		 */ | ||||
| 		ldr	r5, =_kernel_bss_size | ||||
| 		adr	r1, wont_overwrite | ||||
| 		sub	r1, r6, r1 | ||||
| 		subs	r1, r5, r1 | ||||
| 		addhi	r9, r9, r1 | ||||
| 
 | ||||
| 		/* Get the dtb's size */ | ||||
| 		ldr	r5, [r6, #4] | ||||
| #ifndef __ARMEB__ | ||||
| 		/* convert r5 (dtb size) to little endian */ | ||||
| 		eor	r1, r5, r5, ror #16 | ||||
| 		bic	r1, r1, #0x00ff0000 | ||||
| 		mov	r5, r5, ror #8 | ||||
| 		eor	r5, r5, r1, lsr #8 | ||||
| #endif | ||||
| 
 | ||||
| 		/* preserve 64-bit alignment */ | ||||
| 		add	r5, r5, #7 | ||||
| 		bic	r5, r5, #7 | ||||
| 
 | ||||
| 		/* relocate some pointers past the appended dtb */ | ||||
| 		add	r6, r6, r5 | ||||
| 		add	r10, r10, r5 | ||||
| 		add	sp, sp, r5 | ||||
| dtb_check_done: | ||||
| #endif | ||||
| 
 | ||||
| /* | ||||
|  * Check to see if we will overwrite ourselves. | ||||
|  *   r4  = final kernel address | ||||
| @ -223,15 +320,14 @@ restart:	adr	r0, LC0 | ||||
|  *   r10 = end of this image, including  bss/stack/malloc space if non XIP | ||||
|  * We basically want: | ||||
|  *   r4 - 16k page directory >= r10 -> OK | ||||
|  *   r4 + image length <= current position (pc) -> OK | ||||
|  *   r4 + image length <= address of wont_overwrite -> OK | ||||
|  */ | ||||
| 		add	r10, r10, #16384 | ||||
| 		cmp	r4, r10 | ||||
| 		bhs	wont_overwrite | ||||
| 		add	r10, r4, r9 | ||||
|    ARM(		cmp	r10, pc		) | ||||
|  THUMB(		mov	lr, pc		) | ||||
|  THUMB(		cmp	r10, lr		) | ||||
| 		adr	r9, wont_overwrite | ||||
| 		cmp	r10, r9 | ||||
| 		bls	wont_overwrite | ||||
| 
 | ||||
| /* | ||||
| @ -285,14 +381,16 @@ wont_overwrite: | ||||
|  *   r2  = BSS start | ||||
|  *   r3  = BSS end | ||||
|  *   r4  = kernel execution address | ||||
|  *   r5  = appended dtb size (0 if not present) | ||||
|  *   r7  = architecture ID | ||||
|  *   r8  = atags pointer | ||||
|  *   r11 = GOT start | ||||
|  *   r12 = GOT end | ||||
|  *   sp  = stack pointer | ||||
|  */ | ||||
| 		teq	r0, #0 | ||||
| 		orrs	r1, r0, r5 | ||||
| 		beq	not_relocated | ||||
| 
 | ||||
| 		add	r11, r11, r0 | ||||
| 		add	r12, r12, r0 | ||||
| 
 | ||||
| @ -307,12 +405,21 @@ wont_overwrite: | ||||
| 
 | ||||
| 		/* | ||||
| 		 * Relocate all entries in the GOT table. | ||||
| 		 * Bump bss entries to _edata + dtb size | ||||
| 		 */ | ||||
| 1:		ldr	r1, [r11, #0]		@ relocate entries in the GOT
 | ||||
| 		add	r1, r1, r0		@ table.  This fixes up the
 | ||||
| 		str	r1, [r11], #4		@ C references.
 | ||||
| 		add	r1, r1, r0		@ This fixes up C references
 | ||||
| 		cmp	r1, r2			@ if entry >= bss_start &&
 | ||||
| 		cmphs	r3, r1			@       bss_end > entry
 | ||||
| 		addhi	r1, r1, r5		@    entry += dtb size
 | ||||
| 		str	r1, [r11], #4		@ next entry
 | ||||
| 		cmp	r11, r12 | ||||
| 		blo	1b | ||||
| 
 | ||||
| 		/* bump our bss pointers too */ | ||||
| 		add	r2, r2, r5 | ||||
| 		add	r3, r3, r5 | ||||
| 
 | ||||
| #else | ||||
| 
 | ||||
| 		/* | ||||
|  | ||||
							
								
								
									
										15
									
								
								arch/arm/boot/compressed/libfdt_env.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								arch/arm/boot/compressed/libfdt_env.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | ||||
| #ifndef _ARM_LIBFDT_ENV_H | ||||
| #define _ARM_LIBFDT_ENV_H | ||||
| 
 | ||||
| #include <linux/types.h> | ||||
| #include <linux/string.h> | ||||
| #include <asm/byteorder.h> | ||||
| 
 | ||||
| #define fdt16_to_cpu(x)		be16_to_cpu(x) | ||||
| #define cpu_to_fdt16(x)		cpu_to_be16(x) | ||||
| #define fdt32_to_cpu(x)		be32_to_cpu(x) | ||||
| #define cpu_to_fdt32(x)		cpu_to_be32(x) | ||||
| #define fdt64_to_cpu(x)		be64_to_cpu(x) | ||||
| #define cpu_to_fdt64(x)		cpu_to_be64(x) | ||||
| 
 | ||||
| #endif | ||||
| @ -18,14 +18,9 @@ | ||||
| 
 | ||||
| unsigned int __machine_arch_type; | ||||
| 
 | ||||
| #define _LINUX_STRING_H_ | ||||
| 
 | ||||
| #include <linux/compiler.h>	/* for inline */ | ||||
| #include <linux/types.h>	/* for size_t */ | ||||
| #include <linux/stddef.h>	/* for NULL */ | ||||
| #include <linux/types.h> | ||||
| #include <linux/linkage.h> | ||||
| #include <asm/string.h> | ||||
| 
 | ||||
| 
 | ||||
| static void putstr(const char *ptr); | ||||
| extern void error(char *x); | ||||
| @ -101,41 +96,6 @@ static void putstr(const char *ptr) | ||||
| 	flush(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void *memcpy(void *__dest, __const void *__src, size_t __n) | ||||
| { | ||||
| 	int i = 0; | ||||
| 	unsigned char *d = (unsigned char *)__dest, *s = (unsigned char *)__src; | ||||
| 
 | ||||
| 	for (i = __n >> 3; i > 0; i--) { | ||||
| 		*d++ = *s++; | ||||
| 		*d++ = *s++; | ||||
| 		*d++ = *s++; | ||||
| 		*d++ = *s++; | ||||
| 		*d++ = *s++; | ||||
| 		*d++ = *s++; | ||||
| 		*d++ = *s++; | ||||
| 		*d++ = *s++; | ||||
| 	} | ||||
| 
 | ||||
| 	if (__n & 1 << 2) { | ||||
| 		*d++ = *s++; | ||||
| 		*d++ = *s++; | ||||
| 		*d++ = *s++; | ||||
| 		*d++ = *s++; | ||||
| 	} | ||||
| 
 | ||||
| 	if (__n & 1 << 1) { | ||||
| 		*d++ = *s++; | ||||
| 		*d++ = *s++; | ||||
| 	} | ||||
| 
 | ||||
| 	if (__n & 1) | ||||
| 		*d++ = *s++; | ||||
| 
 | ||||
| 	return __dest; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * gzip declarations | ||||
|  */ | ||||
|  | ||||
							
								
								
									
										127
									
								
								arch/arm/boot/compressed/string.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								arch/arm/boot/compressed/string.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,127 @@ | ||||
| /*
 | ||||
|  * arch/arm/boot/compressed/string.c | ||||
|  * | ||||
|  * Small subset of simple string routines | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/string.h> | ||||
| 
 | ||||
| void *memcpy(void *__dest, __const void *__src, size_t __n) | ||||
| { | ||||
| 	int i = 0; | ||||
| 	unsigned char *d = (unsigned char *)__dest, *s = (unsigned char *)__src; | ||||
| 
 | ||||
| 	for (i = __n >> 3; i > 0; i--) { | ||||
| 		*d++ = *s++; | ||||
| 		*d++ = *s++; | ||||
| 		*d++ = *s++; | ||||
| 		*d++ = *s++; | ||||
| 		*d++ = *s++; | ||||
| 		*d++ = *s++; | ||||
| 		*d++ = *s++; | ||||
| 		*d++ = *s++; | ||||
| 	} | ||||
| 
 | ||||
| 	if (__n & 1 << 2) { | ||||
| 		*d++ = *s++; | ||||
| 		*d++ = *s++; | ||||
| 		*d++ = *s++; | ||||
| 		*d++ = *s++; | ||||
| 	} | ||||
| 
 | ||||
| 	if (__n & 1 << 1) { | ||||
| 		*d++ = *s++; | ||||
| 		*d++ = *s++; | ||||
| 	} | ||||
| 
 | ||||
| 	if (__n & 1) | ||||
| 		*d++ = *s++; | ||||
| 
 | ||||
| 	return __dest; | ||||
| } | ||||
| 
 | ||||
| void *memmove(void *__dest, __const void *__src, size_t count) | ||||
| { | ||||
| 	unsigned char *d = __dest; | ||||
| 	const unsigned char *s = __src; | ||||
| 
 | ||||
| 	if (__dest == __src) | ||||
| 		return __dest; | ||||
| 
 | ||||
| 	if (__dest < __src) | ||||
| 		return memcpy(__dest, __src, count); | ||||
| 
 | ||||
| 	while (count--) | ||||
| 		d[count] = s[count]; | ||||
| 	return __dest; | ||||
| } | ||||
| 
 | ||||
| size_t strlen(const char *s) | ||||
| { | ||||
| 	const char *sc = s; | ||||
| 
 | ||||
| 	while (*sc != '\0') | ||||
| 		sc++; | ||||
| 	return sc - s; | ||||
| } | ||||
| 
 | ||||
| int memcmp(const void *cs, const void *ct, size_t count) | ||||
| { | ||||
| 	const unsigned char *su1 = cs, *su2 = ct, *end = su1 + count; | ||||
| 	int res = 0; | ||||
| 
 | ||||
| 	while (su1 < end) { | ||||
| 		res = *su1++ - *su2++; | ||||
| 		if (res) | ||||
| 			break; | ||||
| 	} | ||||
| 	return res; | ||||
| } | ||||
| 
 | ||||
| int strcmp(const char *cs, const char *ct) | ||||
| { | ||||
| 	unsigned char c1, c2; | ||||
| 	int res = 0; | ||||
| 
 | ||||
| 	do { | ||||
| 		c1 = *cs++; | ||||
| 		c2 = *ct++; | ||||
| 		res = c1 - c2; | ||||
| 		if (res) | ||||
| 			break; | ||||
| 	} while (c1); | ||||
| 	return res; | ||||
| } | ||||
| 
 | ||||
| void *memchr(const void *s, int c, size_t count) | ||||
| { | ||||
| 	const unsigned char *p = s; | ||||
| 
 | ||||
| 	while (count--) | ||||
| 		if ((unsigned char)c == *p++) | ||||
| 			return (void *)(p - 1); | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| char *strchr(const char *s, int c) | ||||
| { | ||||
| 	while (*s != (char)c) | ||||
| 		if (*s++ == '\0') | ||||
| 			return NULL; | ||||
| 	return (char *)s; | ||||
| } | ||||
| 
 | ||||
| #undef memset | ||||
| 
 | ||||
| void *memset(void *s, int c, size_t count) | ||||
| { | ||||
| 	char *xs = s; | ||||
| 	while (count--) | ||||
| 		*xs++ = c; | ||||
| 	return s; | ||||
| } | ||||
| 
 | ||||
| void __memzero(void *s, size_t count) | ||||
| { | ||||
| 	memset(s, 0, count); | ||||
| } | ||||
| @ -51,6 +51,10 @@ SECTIONS | ||||
|   _got_start = .; | ||||
|   .got			: { *(.got) } | ||||
|   _got_end = .; | ||||
| 
 | ||||
|   /* ensure the zImage file size is always a multiple of 64 bits */ | ||||
|   /* (without a dummy byte, ld just ignores the empty section) */ | ||||
|   .pad			: { BYTE(0); . = ALIGN(8); } | ||||
|   _edata = .; | ||||
| 
 | ||||
|   . = BSS_START; | ||||
|  | ||||
| @ -26,6 +26,7 @@ | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/list.h> | ||||
| #include <linux/smp.h> | ||||
| #include <linux/cpu_pm.h> | ||||
| #include <linux/cpumask.h> | ||||
| #include <linux/io.h> | ||||
| 
 | ||||
| @ -282,6 +283,8 @@ static void __init gic_dist_init(struct gic_chip_data *gic, | ||||
| 	if (gic_irqs > 1020) | ||||
| 		gic_irqs = 1020; | ||||
| 
 | ||||
| 	gic->gic_irqs = gic_irqs; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Set all global interrupts to be level triggered, active low. | ||||
| 	 */ | ||||
| @ -349,6 +352,189 @@ static void __cpuinit gic_cpu_init(struct gic_chip_data *gic) | ||||
| 	writel_relaxed(1, base + GIC_CPU_CTRL); | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_CPU_PM | ||||
| /*
 | ||||
|  * Saves the GIC distributor registers during suspend or idle.  Must be called | ||||
|  * with interrupts disabled but before powering down the GIC.  After calling | ||||
|  * this function, no interrupts will be delivered by the GIC, and another | ||||
|  * platform-specific wakeup source must be enabled. | ||||
|  */ | ||||
| static void gic_dist_save(unsigned int gic_nr) | ||||
| { | ||||
| 	unsigned int gic_irqs; | ||||
| 	void __iomem *dist_base; | ||||
| 	int i; | ||||
| 
 | ||||
| 	if (gic_nr >= MAX_GIC_NR) | ||||
| 		BUG(); | ||||
| 
 | ||||
| 	gic_irqs = gic_data[gic_nr].gic_irqs; | ||||
| 	dist_base = gic_data[gic_nr].dist_base; | ||||
| 
 | ||||
| 	if (!dist_base) | ||||
| 		return; | ||||
| 
 | ||||
| 	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++) | ||||
| 		gic_data[gic_nr].saved_spi_conf[i] = | ||||
| 			readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4); | ||||
| 
 | ||||
| 	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++) | ||||
| 		gic_data[gic_nr].saved_spi_target[i] = | ||||
| 			readl_relaxed(dist_base + GIC_DIST_TARGET + i * 4); | ||||
| 
 | ||||
| 	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++) | ||||
| 		gic_data[gic_nr].saved_spi_enable[i] = | ||||
| 			readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Restores the GIC distributor registers during resume or when coming out of | ||||
|  * idle.  Must be called before enabling interrupts.  If a level interrupt | ||||
|  * that occured while the GIC was suspended is still present, it will be | ||||
|  * handled normally, but any edge interrupts that occured will not be seen by | ||||
|  * the GIC and need to be handled by the platform-specific wakeup source. | ||||
|  */ | ||||
| static void gic_dist_restore(unsigned int gic_nr) | ||||
| { | ||||
| 	unsigned int gic_irqs; | ||||
| 	unsigned int i; | ||||
| 	void __iomem *dist_base; | ||||
| 
 | ||||
| 	if (gic_nr >= MAX_GIC_NR) | ||||
| 		BUG(); | ||||
| 
 | ||||
| 	gic_irqs = gic_data[gic_nr].gic_irqs; | ||||
| 	dist_base = gic_data[gic_nr].dist_base; | ||||
| 
 | ||||
| 	if (!dist_base) | ||||
| 		return; | ||||
| 
 | ||||
| 	writel_relaxed(0, dist_base + GIC_DIST_CTRL); | ||||
| 
 | ||||
| 	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++) | ||||
| 		writel_relaxed(gic_data[gic_nr].saved_spi_conf[i], | ||||
| 			dist_base + GIC_DIST_CONFIG + i * 4); | ||||
| 
 | ||||
| 	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++) | ||||
| 		writel_relaxed(0xa0a0a0a0, | ||||
| 			dist_base + GIC_DIST_PRI + i * 4); | ||||
| 
 | ||||
| 	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++) | ||||
| 		writel_relaxed(gic_data[gic_nr].saved_spi_target[i], | ||||
| 			dist_base + GIC_DIST_TARGET + i * 4); | ||||
| 
 | ||||
| 	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++) | ||||
| 		writel_relaxed(gic_data[gic_nr].saved_spi_enable[i], | ||||
| 			dist_base + GIC_DIST_ENABLE_SET + i * 4); | ||||
| 
 | ||||
| 	writel_relaxed(1, dist_base + GIC_DIST_CTRL); | ||||
| } | ||||
| 
 | ||||
| static void gic_cpu_save(unsigned int gic_nr) | ||||
| { | ||||
| 	int i; | ||||
| 	u32 *ptr; | ||||
| 	void __iomem *dist_base; | ||||
| 	void __iomem *cpu_base; | ||||
| 
 | ||||
| 	if (gic_nr >= MAX_GIC_NR) | ||||
| 		BUG(); | ||||
| 
 | ||||
| 	dist_base = gic_data[gic_nr].dist_base; | ||||
| 	cpu_base = gic_data[gic_nr].cpu_base; | ||||
| 
 | ||||
| 	if (!dist_base || !cpu_base) | ||||
| 		return; | ||||
| 
 | ||||
| 	ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_enable); | ||||
| 	for (i = 0; i < DIV_ROUND_UP(32, 32); i++) | ||||
| 		ptr[i] = readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4); | ||||
| 
 | ||||
| 	ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_conf); | ||||
| 	for (i = 0; i < DIV_ROUND_UP(32, 16); i++) | ||||
| 		ptr[i] = readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| static void gic_cpu_restore(unsigned int gic_nr) | ||||
| { | ||||
| 	int i; | ||||
| 	u32 *ptr; | ||||
| 	void __iomem *dist_base; | ||||
| 	void __iomem *cpu_base; | ||||
| 
 | ||||
| 	if (gic_nr >= MAX_GIC_NR) | ||||
| 		BUG(); | ||||
| 
 | ||||
| 	dist_base = gic_data[gic_nr].dist_base; | ||||
| 	cpu_base = gic_data[gic_nr].cpu_base; | ||||
| 
 | ||||
| 	if (!dist_base || !cpu_base) | ||||
| 		return; | ||||
| 
 | ||||
| 	ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_enable); | ||||
| 	for (i = 0; i < DIV_ROUND_UP(32, 32); i++) | ||||
| 		writel_relaxed(ptr[i], dist_base + GIC_DIST_ENABLE_SET + i * 4); | ||||
| 
 | ||||
| 	ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_conf); | ||||
| 	for (i = 0; i < DIV_ROUND_UP(32, 16); i++) | ||||
| 		writel_relaxed(ptr[i], dist_base + GIC_DIST_CONFIG + i * 4); | ||||
| 
 | ||||
| 	for (i = 0; i < DIV_ROUND_UP(32, 4); i++) | ||||
| 		writel_relaxed(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4); | ||||
| 
 | ||||
| 	writel_relaxed(0xf0, cpu_base + GIC_CPU_PRIMASK); | ||||
| 	writel_relaxed(1, cpu_base + GIC_CPU_CTRL); | ||||
| } | ||||
| 
 | ||||
| static int gic_notifier(struct notifier_block *self, unsigned long cmd,	void *v) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < MAX_GIC_NR; i++) { | ||||
| 		switch (cmd) { | ||||
| 		case CPU_PM_ENTER: | ||||
| 			gic_cpu_save(i); | ||||
| 			break; | ||||
| 		case CPU_PM_ENTER_FAILED: | ||||
| 		case CPU_PM_EXIT: | ||||
| 			gic_cpu_restore(i); | ||||
| 			break; | ||||
| 		case CPU_CLUSTER_PM_ENTER: | ||||
| 			gic_dist_save(i); | ||||
| 			break; | ||||
| 		case CPU_CLUSTER_PM_ENTER_FAILED: | ||||
| 		case CPU_CLUSTER_PM_EXIT: | ||||
| 			gic_dist_restore(i); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return NOTIFY_OK; | ||||
| } | ||||
| 
 | ||||
| static struct notifier_block gic_notifier_block = { | ||||
| 	.notifier_call = gic_notifier, | ||||
| }; | ||||
| 
 | ||||
| static void __init gic_pm_init(struct gic_chip_data *gic) | ||||
| { | ||||
| 	gic->saved_ppi_enable = __alloc_percpu(DIV_ROUND_UP(32, 32) * 4, | ||||
| 		sizeof(u32)); | ||||
| 	BUG_ON(!gic->saved_ppi_enable); | ||||
| 
 | ||||
| 	gic->saved_ppi_conf = __alloc_percpu(DIV_ROUND_UP(32, 16) * 4, | ||||
| 		sizeof(u32)); | ||||
| 	BUG_ON(!gic->saved_ppi_conf); | ||||
| 
 | ||||
| 	cpu_pm_register_notifier(&gic_notifier_block); | ||||
| } | ||||
| #else | ||||
| static void __init gic_pm_init(struct gic_chip_data *gic) | ||||
| { | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| void __init gic_init(unsigned int gic_nr, unsigned int irq_start, | ||||
| 	void __iomem *dist_base, void __iomem *cpu_base) | ||||
| { | ||||
| @ -364,8 +550,10 @@ void __init gic_init(unsigned int gic_nr, unsigned int irq_start, | ||||
| 	if (gic_nr == 0) | ||||
| 		gic_cpu_base_addr = cpu_base; | ||||
| 
 | ||||
| 	gic_chip.flags |= gic_arch_extn.flags; | ||||
| 	gic_dist_init(gic, irq_start); | ||||
| 	gic_cpu_init(gic); | ||||
| 	gic_pm_init(gic); | ||||
| } | ||||
| 
 | ||||
| void __cpuinit gic_secondary_init(unsigned int gic_nr) | ||||
|  | ||||
| @ -205,6 +205,13 @@ extern void *dma_alloc_writecombine(struct device *, size_t, dma_addr_t *, | ||||
| int dma_mmap_writecombine(struct device *, struct vm_area_struct *, | ||||
| 		void *, dma_addr_t, size_t); | ||||
| 
 | ||||
| /*
 | ||||
|  * This can be called during boot to increase the size of the consistent | ||||
|  * DMA region above it's default value of 2MB. It must be called before the | ||||
|  * memory allocator is initialised, i.e. before any core_initcall. | ||||
|  */ | ||||
| extern void __init init_consistent_dma_size(unsigned long size); | ||||
| 
 | ||||
| 
 | ||||
| #ifdef CONFIG_DMABOUNCE | ||||
| /*
 | ||||
|  | ||||
| @ -46,6 +46,14 @@ struct gic_chip_data { | ||||
| 	unsigned int irq_offset; | ||||
| 	void __iomem *dist_base; | ||||
| 	void __iomem *cpu_base; | ||||
| #ifdef CONFIG_CPU_PM | ||||
| 	u32 saved_spi_enable[DIV_ROUND_UP(1020, 32)]; | ||||
| 	u32 saved_spi_conf[DIV_ROUND_UP(1020, 16)]; | ||||
| 	u32 saved_spi_target[DIV_ROUND_UP(1020, 4)]; | ||||
| 	u32 __percpu *saved_ppi_enable; | ||||
| 	u32 __percpu *saved_ppi_conf; | ||||
| #endif | ||||
| 	unsigned int gic_irqs; | ||||
| }; | ||||
| #endif | ||||
| 
 | ||||
|  | ||||
| @ -50,6 +50,7 @@ static inline void decode_ctrl_reg(u32 reg, | ||||
| #define ARM_DEBUG_ARCH_V6_1	2 | ||||
| #define ARM_DEBUG_ARCH_V7_ECP14	3 | ||||
| #define ARM_DEBUG_ARCH_V7_MM	4 | ||||
| #define ARM_DEBUG_ARCH_V7_1	5 | ||||
| 
 | ||||
| /* Breakpoint */ | ||||
| #define ARM_BREAKPOINT_EXECUTE	0 | ||||
| @ -57,6 +58,7 @@ static inline void decode_ctrl_reg(u32 reg, | ||||
| /* Watchpoints */ | ||||
| #define ARM_BREAKPOINT_LOAD	1 | ||||
| #define ARM_BREAKPOINT_STORE	2 | ||||
| #define ARM_FSR_ACCESS_MASK	(1 << 11) | ||||
| 
 | ||||
| /* Privilege Levels */ | ||||
| #define ARM_BREAKPOINT_PRIV	1 | ||||
|  | ||||
| @ -17,7 +17,7 @@ struct sys_timer; | ||||
| struct machine_desc { | ||||
| 	unsigned int		nr;		/* architecture number	*/ | ||||
| 	const char		*name;		/* architecture name	*/ | ||||
| 	unsigned long		boot_params;	/* tagged list		*/ | ||||
| 	unsigned long		atag_offset;	/* tagged list (relative) */ | ||||
| 	const char		**dt_compat;	/* array of device tree
 | ||||
| 						 * 'compatible' strings	*/ | ||||
| 
 | ||||
|  | ||||
| @ -29,6 +29,7 @@ struct map_desc { | ||||
| #define MT_MEMORY_NONCACHED	11 | ||||
| #define MT_MEMORY_DTCM		12 | ||||
| #define MT_MEMORY_ITCM		13 | ||||
| #define MT_MEMORY_SO		14 | ||||
| 
 | ||||
| #ifdef CONFIG_MMU | ||||
| extern void iotable_init(struct map_desc *, int); | ||||
|  | ||||
| @ -77,16 +77,7 @@ | ||||
|  */ | ||||
| #define IOREMAP_MAX_ORDER	24 | ||||
| 
 | ||||
| /*
 | ||||
|  * Size of DMA-consistent memory region.  Must be multiple of 2M, | ||||
|  * between 2MB and 14MB inclusive. | ||||
|  */ | ||||
| #ifndef CONSISTENT_DMA_SIZE | ||||
| #define CONSISTENT_DMA_SIZE 	SZ_2M | ||||
| #endif | ||||
| 
 | ||||
| #define CONSISTENT_END		(0xffe00000UL) | ||||
| #define CONSISTENT_BASE		(CONSISTENT_END - CONSISTENT_DMA_SIZE) | ||||
| 
 | ||||
| #else /* CONFIG_MMU */ | ||||
| 
 | ||||
|  | ||||
| @ -232,6 +232,9 @@ extern pgprot_t		pgprot_kernel; | ||||
| #define pgprot_writecombine(prot) \ | ||||
| 	__pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_BUFFERABLE) | ||||
| 
 | ||||
| #define pgprot_stronglyordered(prot) \ | ||||
| 	__pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_UNCACHED) | ||||
| 
 | ||||
| #ifdef CONFIG_ARM_DMA_MEM_BUFFERABLE | ||||
| #define pgprot_dmacoherent(prot) \ | ||||
| 	__pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_BUFFERABLE | L_PTE_XN) | ||||
|  | ||||
| @ -13,7 +13,12 @@ | ||||
| #define __ARM_PMU_H__ | ||||
| 
 | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/perf_event.h> | ||||
| 
 | ||||
| /*
 | ||||
|  * Types of PMUs that can be accessed directly and require mutual | ||||
|  * exclusion between profiling tools. | ||||
|  */ | ||||
| enum arm_pmu_type { | ||||
| 	ARM_PMU_DEVICE_CPU	= 0, | ||||
| 	ARM_NUM_PMU_DEVICES, | ||||
| @ -37,21 +42,17 @@ struct arm_pmu_platdata { | ||||
|  * reserve_pmu() - reserve the hardware performance counters | ||||
|  * | ||||
|  * Reserve the hardware performance counters in the system for exclusive use. | ||||
|  * The platform_device for the system is returned on success, ERR_PTR() | ||||
|  * encoded error on failure. | ||||
|  * Returns 0 on success or -EBUSY if the lock is already held. | ||||
|  */ | ||||
| extern struct platform_device * | ||||
| extern int | ||||
| reserve_pmu(enum arm_pmu_type type); | ||||
| 
 | ||||
| /**
 | ||||
|  * release_pmu() - Relinquish control of the performance counters | ||||
|  * | ||||
|  * Release the performance counters and allow someone else to use them. | ||||
|  * Callers must have disabled the counters and released IRQs before calling | ||||
|  * this. The platform_device returned from reserve_pmu() must be passed as | ||||
|  * a cookie. | ||||
|  */ | ||||
| extern int | ||||
| extern void | ||||
| release_pmu(enum arm_pmu_type type); | ||||
| 
 | ||||
| /**
 | ||||
| @ -68,24 +69,78 @@ init_pmu(enum arm_pmu_type type); | ||||
| 
 | ||||
| #include <linux/err.h> | ||||
| 
 | ||||
| static inline struct platform_device * | ||||
| static inline int | ||||
| reserve_pmu(enum arm_pmu_type type) | ||||
| { | ||||
| 	return ERR_PTR(-ENODEV); | ||||
| } | ||||
| 
 | ||||
| static inline int | ||||
| release_pmu(enum arm_pmu_type type) | ||||
| { | ||||
| 	return -ENODEV; | ||||
| } | ||||
| 
 | ||||
| static inline int | ||||
| init_pmu(enum arm_pmu_type type) | ||||
| { | ||||
| 	return -ENODEV; | ||||
| } | ||||
| static inline void | ||||
| release_pmu(enum arm_pmu_type type)	{ } | ||||
| 
 | ||||
| #endif /* CONFIG_CPU_HAS_PMU */ | ||||
| 
 | ||||
| #ifdef CONFIG_HW_PERF_EVENTS | ||||
| 
 | ||||
| /* The events for a given PMU register set. */ | ||||
| struct pmu_hw_events { | ||||
| 	/*
 | ||||
| 	 * The events that are active on the PMU for the given index. | ||||
| 	 */ | ||||
| 	struct perf_event	**events; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * A 1 bit for an index indicates that the counter is being used for | ||||
| 	 * an event. A 0 means that the counter can be used. | ||||
| 	 */ | ||||
| 	unsigned long           *used_mask; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Hardware lock to serialize accesses to PMU registers. Needed for the | ||||
| 	 * read/modify/write sequences. | ||||
| 	 */ | ||||
| 	raw_spinlock_t		pmu_lock; | ||||
| }; | ||||
| 
 | ||||
| struct arm_pmu { | ||||
| 	struct pmu	pmu; | ||||
| 	enum arm_perf_pmu_ids id; | ||||
| 	enum arm_pmu_type type; | ||||
| 	cpumask_t	active_irqs; | ||||
| 	const char	*name; | ||||
| 	irqreturn_t	(*handle_irq)(int irq_num, void *dev); | ||||
| 	void		(*enable)(struct hw_perf_event *evt, int idx); | ||||
| 	void		(*disable)(struct hw_perf_event *evt, int idx); | ||||
| 	int		(*get_event_idx)(struct pmu_hw_events *hw_events, | ||||
| 					 struct hw_perf_event *hwc); | ||||
| 	int		(*set_event_filter)(struct hw_perf_event *evt, | ||||
| 					    struct perf_event_attr *attr); | ||||
| 	u32		(*read_counter)(int idx); | ||||
| 	void		(*write_counter)(int idx, u32 val); | ||||
| 	void		(*start)(void); | ||||
| 	void		(*stop)(void); | ||||
| 	void		(*reset)(void *); | ||||
| 	int		(*map_event)(struct perf_event *event); | ||||
| 	int		num_events; | ||||
| 	atomic_t	active_events; | ||||
| 	struct mutex	reserve_mutex; | ||||
| 	u64		max_period; | ||||
| 	struct platform_device	*plat_device; | ||||
| 	struct pmu_hw_events	*(*get_hw_events)(void); | ||||
| }; | ||||
| 
 | ||||
| #define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu)) | ||||
| 
 | ||||
| int __init armpmu_register(struct arm_pmu *armpmu, char *name, int type); | ||||
| 
 | ||||
| u64 armpmu_event_update(struct perf_event *event, | ||||
| 			struct hw_perf_event *hwc, | ||||
| 			int idx, int overflow); | ||||
| 
 | ||||
| int armpmu_event_set_period(struct perf_event *event, | ||||
| 			    struct hw_perf_event *hwc, | ||||
| 			    int idx); | ||||
| 
 | ||||
| #endif /* CONFIG_HW_PERF_EVENTS */ | ||||
| 
 | ||||
| #endif /* __ARM_PMU_H__ */ | ||||
|  | ||||
| @ -81,6 +81,10 @@ extern void cpu_dcache_clean_area(void *, int); | ||||
| extern void cpu_do_switch_mm(unsigned long pgd_phys, struct mm_struct *mm); | ||||
| extern void cpu_set_pte_ext(pte_t *ptep, pte_t pte, unsigned int ext); | ||||
| extern void cpu_reset(unsigned long addr) __attribute__((noreturn)); | ||||
| 
 | ||||
| /* These three are private to arch/arm/kernel/suspend.c */ | ||||
| extern void cpu_do_suspend(void *); | ||||
| extern void cpu_do_resume(void *); | ||||
| #else | ||||
| #define cpu_proc_init			processor._proc_init | ||||
| #define cpu_proc_fin			processor._proc_fin | ||||
| @ -89,6 +93,10 @@ extern void cpu_reset(unsigned long addr) __attribute__((noreturn)); | ||||
| #define cpu_dcache_clean_area		processor.dcache_clean_area | ||||
| #define cpu_set_pte_ext			processor.set_pte_ext | ||||
| #define cpu_do_switch_mm		processor.switch_mm | ||||
| 
 | ||||
| /* These three are private to arch/arm/kernel/suspend.c */ | ||||
| #define cpu_do_suspend			processor.do_suspend | ||||
| #define cpu_do_resume			processor.do_resume | ||||
| #endif | ||||
| 
 | ||||
| extern void cpu_resume(void); | ||||
|  | ||||
| @ -1,22 +1,7 @@ | ||||
| #ifndef __ASM_ARM_SUSPEND_H | ||||
| #define __ASM_ARM_SUSPEND_H | ||||
| 
 | ||||
| #include <asm/memory.h> | ||||
| #include <asm/tlbflush.h> | ||||
| 
 | ||||
| extern void cpu_resume(void); | ||||
| 
 | ||||
| /*
 | ||||
|  * Hide the first two arguments to __cpu_suspend - these are an implementation | ||||
|  * detail which platform code shouldn't have to know about. | ||||
|  */ | ||||
| static inline int cpu_suspend(unsigned long arg, int (*fn)(unsigned long)) | ||||
| { | ||||
| 	extern int __cpu_suspend(int, long, unsigned long, | ||||
| 				 int (*)(unsigned long)); | ||||
| 	int ret = __cpu_suspend(0, PHYS_OFFSET - PAGE_OFFSET, arg, fn); | ||||
| 	flush_tlb_all(); | ||||
| 	return ret; | ||||
| } | ||||
| extern int cpu_suspend(unsigned long, int (*)(unsigned long)); | ||||
| 
 | ||||
| #endif | ||||
|  | ||||
| @ -29,7 +29,7 @@ obj-$(CONFIG_MODULES)		+= armksyms.o module.o | ||||
| obj-$(CONFIG_ARTHUR)		+= arthur.o | ||||
| obj-$(CONFIG_ISA_DMA)		+= dma-isa.o | ||||
| obj-$(CONFIG_PCI)		+= bios32.o isa.o | ||||
| obj-$(CONFIG_PM_SLEEP)		+= sleep.o | ||||
| obj-$(CONFIG_PM_SLEEP)		+= sleep.o suspend.o | ||||
| obj-$(CONFIG_HAVE_SCHED_CLOCK)	+= sched_clock.o | ||||
| obj-$(CONFIG_SMP)		+= smp.o smp_tlb.o | ||||
| obj-$(CONFIG_HAVE_ARM_SCU)	+= smp_scu.o | ||||
| @ -43,6 +43,13 @@ obj-$(CONFIG_KPROBES)		+= kprobes-thumb.o | ||||
| else | ||||
| obj-$(CONFIG_KPROBES)		+= kprobes-arm.o | ||||
| endif | ||||
| obj-$(CONFIG_ARM_KPROBES_TEST)	+= test-kprobes.o | ||||
| test-kprobes-objs		:= kprobes-test.o | ||||
| ifdef CONFIG_THUMB2_KERNEL | ||||
| test-kprobes-objs		+= kprobes-test-thumb.o | ||||
| else | ||||
| test-kprobes-objs		+= kprobes-test-arm.o | ||||
| endif | ||||
| obj-$(CONFIG_ATAGS_PROC)	+= atags.o | ||||
| obj-$(CONFIG_OABI_COMPAT)	+= sys_oabi-compat.o | ||||
| obj-$(CONFIG_ARM_THUMBEE)	+= thumbee.o | ||||
|  | ||||
| @ -45,7 +45,6 @@ static DEFINE_PER_CPU(struct perf_event *, wp_on_reg[ARM_MAX_WRP]); | ||||
| 
 | ||||
| /* Number of BRP/WRP registers on this CPU. */ | ||||
| static int core_num_brps; | ||||
| static int core_num_reserved_brps; | ||||
| static int core_num_wrps; | ||||
| 
 | ||||
| /* Debug architecture version. */ | ||||
| @ -137,10 +136,11 @@ static u8 get_debug_arch(void) | ||||
| 	u32 didr; | ||||
| 
 | ||||
| 	/* Do we implement the extended CPUID interface? */ | ||||
| 	if (WARN_ONCE((((read_cpuid_id() >> 16) & 0xf) != 0xf), | ||||
| 	    "CPUID feature registers not supported. " | ||||
| 	    "Assuming v6 debug is present.\n")) | ||||
| 	if (((read_cpuid_id() >> 16) & 0xf) != 0xf) { | ||||
| 		pr_warning("CPUID feature registers not supported. " | ||||
| 			   "Assuming v6 debug is present.\n"); | ||||
| 		return ARM_DEBUG_ARCH_V6; | ||||
| 	} | ||||
| 
 | ||||
| 	ARM_DBG_READ(c0, 0, didr); | ||||
| 	return (didr >> 16) & 0xf; | ||||
| @ -154,10 +154,21 @@ u8 arch_get_debug_arch(void) | ||||
| static int debug_arch_supported(void) | ||||
| { | ||||
| 	u8 arch = get_debug_arch(); | ||||
| 	return arch >= ARM_DEBUG_ARCH_V6 && arch <= ARM_DEBUG_ARCH_V7_ECP14; | ||||
| 
 | ||||
| 	/* We don't support the memory-mapped interface. */ | ||||
| 	return (arch >= ARM_DEBUG_ARCH_V6 && arch <= ARM_DEBUG_ARCH_V7_ECP14) || | ||||
| 		arch >= ARM_DEBUG_ARCH_V7_1; | ||||
| } | ||||
| 
 | ||||
| /* Determine number of BRP register available. */ | ||||
| /* Determine number of WRP registers available. */ | ||||
| static int get_num_wrp_resources(void) | ||||
| { | ||||
| 	u32 didr; | ||||
| 	ARM_DBG_READ(c0, 0, didr); | ||||
| 	return ((didr >> 28) & 0xf) + 1; | ||||
| } | ||||
| 
 | ||||
| /* Determine number of BRP registers available. */ | ||||
| static int get_num_brp_resources(void) | ||||
| { | ||||
| 	u32 didr; | ||||
| @ -176,9 +187,10 @@ static int core_has_mismatch_brps(void) | ||||
| static int get_num_wrps(void) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * FIXME: When a watchpoint fires, the only way to work out which | ||||
| 	 * watchpoint it was is by disassembling the faulting instruction | ||||
| 	 * and working out the address of the memory access. | ||||
| 	 * On debug architectures prior to 7.1, when a watchpoint fires, the | ||||
| 	 * only way to work out which watchpoint it was is by disassembling | ||||
| 	 * the faulting instruction and working out the address of the memory | ||||
| 	 * access. | ||||
| 	 * | ||||
| 	 * Furthermore, we can only do this if the watchpoint was precise | ||||
| 	 * since imprecise watchpoints prevent us from calculating register | ||||
| @ -192,36 +204,17 @@ static int get_num_wrps(void) | ||||
| 	 * [the ARM ARM states that the DFAR is UNKNOWN, but experience shows | ||||
| 	 * that it is set on some implementations]. | ||||
| 	 */ | ||||
| 	if (get_debug_arch() < ARM_DEBUG_ARCH_V7_1) | ||||
| 		return 1; | ||||
| 
 | ||||
| #if 0 | ||||
| 	int wrps; | ||||
| 	u32 didr; | ||||
| 	ARM_DBG_READ(c0, 0, didr); | ||||
| 	wrps = ((didr >> 28) & 0xf) + 1; | ||||
| #endif | ||||
| 	int wrps = 1; | ||||
| 
 | ||||
| 	if (core_has_mismatch_brps() && wrps >= get_num_brp_resources()) | ||||
| 		wrps = get_num_brp_resources() - 1; | ||||
| 
 | ||||
| 	return wrps; | ||||
| } | ||||
| 
 | ||||
| /* We reserve one breakpoint for each watchpoint. */ | ||||
| static int get_num_reserved_brps(void) | ||||
| { | ||||
| 	if (core_has_mismatch_brps()) | ||||
| 		return get_num_wrps(); | ||||
| 	return 0; | ||||
| 	return get_num_wrp_resources(); | ||||
| } | ||||
| 
 | ||||
| /* Determine number of usable BRPs available. */ | ||||
| static int get_num_brps(void) | ||||
| { | ||||
| 	int brps = get_num_brp_resources(); | ||||
| 	if (core_has_mismatch_brps()) | ||||
| 		brps -= get_num_reserved_brps(); | ||||
| 	return brps; | ||||
| 	return core_has_mismatch_brps() ? brps - 1 : brps; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
| @ -239,7 +232,7 @@ static int enable_monitor_mode(void) | ||||
| 
 | ||||
| 	/* Ensure that halting mode is disabled. */ | ||||
| 	if (WARN_ONCE(dscr & ARM_DSCR_HDBGEN, | ||||
| 			"halting debug mode enabled. Unable to access hardware resources.\n")) { | ||||
| 		"halting debug mode enabled. Unable to access hardware resources.\n")) { | ||||
| 		ret = -EPERM; | ||||
| 		goto out; | ||||
| 	} | ||||
| @ -255,6 +248,7 @@ static int enable_monitor_mode(void) | ||||
| 		ARM_DBG_WRITE(c1, 0, (dscr | ARM_DSCR_MDBGEN)); | ||||
| 		break; | ||||
| 	case ARM_DEBUG_ARCH_V7_ECP14: | ||||
| 	case ARM_DEBUG_ARCH_V7_1: | ||||
| 		ARM_DBG_WRITE(c2, 2, (dscr | ARM_DSCR_MDBGEN)); | ||||
| 		break; | ||||
| 	default: | ||||
| @ -346,24 +340,10 @@ int arch_install_hw_breakpoint(struct perf_event *bp) | ||||
| 		val_base = ARM_BASE_BVR; | ||||
| 		slots = (struct perf_event **)__get_cpu_var(bp_on_reg); | ||||
| 		max_slots = core_num_brps; | ||||
| 		if (info->step_ctrl.enabled) { | ||||
| 			/* Override the breakpoint data with the step data. */ | ||||
| 			addr = info->trigger & ~0x3; | ||||
| 			ctrl = encode_ctrl_reg(info->step_ctrl); | ||||
| 		} | ||||
| 	} else { | ||||
| 		/* Watchpoint */ | ||||
| 		if (info->step_ctrl.enabled) { | ||||
| 			/* Install into the reserved breakpoint region. */ | ||||
| 			ctrl_base = ARM_BASE_BCR + core_num_brps; | ||||
| 			val_base = ARM_BASE_BVR + core_num_brps; | ||||
| 			/* Override the watchpoint data with the step data. */ | ||||
| 			addr = info->trigger & ~0x3; | ||||
| 			ctrl = encode_ctrl_reg(info->step_ctrl); | ||||
| 		} else { | ||||
| 			ctrl_base = ARM_BASE_WCR; | ||||
| 			val_base = ARM_BASE_WVR; | ||||
| 		} | ||||
| 		ctrl_base = ARM_BASE_WCR; | ||||
| 		val_base = ARM_BASE_WVR; | ||||
| 		slots = (struct perf_event **)__get_cpu_var(wp_on_reg); | ||||
| 		max_slots = core_num_wrps; | ||||
| 	} | ||||
| @ -382,6 +362,17 @@ int arch_install_hw_breakpoint(struct perf_event *bp) | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Override the breakpoint data with the step data. */ | ||||
| 	if (info->step_ctrl.enabled) { | ||||
| 		addr = info->trigger & ~0x3; | ||||
| 		ctrl = encode_ctrl_reg(info->step_ctrl); | ||||
| 		if (info->ctrl.type != ARM_BREAKPOINT_EXECUTE) { | ||||
| 			i = 0; | ||||
| 			ctrl_base = ARM_BASE_BCR + core_num_brps; | ||||
| 			val_base = ARM_BASE_BVR + core_num_brps; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* Setup the address register. */ | ||||
| 	write_wb_reg(val_base + i, addr); | ||||
| 
 | ||||
| @ -405,10 +396,7 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp) | ||||
| 		max_slots = core_num_brps; | ||||
| 	} else { | ||||
| 		/* Watchpoint */ | ||||
| 		if (info->step_ctrl.enabled) | ||||
| 			base = ARM_BASE_BCR + core_num_brps; | ||||
| 		else | ||||
| 			base = ARM_BASE_WCR; | ||||
| 		base = ARM_BASE_WCR; | ||||
| 		slots = (struct perf_event **)__get_cpu_var(wp_on_reg); | ||||
| 		max_slots = core_num_wrps; | ||||
| 	} | ||||
| @ -426,6 +414,13 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp) | ||||
| 	if (WARN_ONCE(i == max_slots, "Can't find any breakpoint slot\n")) | ||||
| 		return; | ||||
| 
 | ||||
| 	/* Ensure that we disable the mismatch breakpoint. */ | ||||
| 	if (info->ctrl.type != ARM_BREAKPOINT_EXECUTE && | ||||
| 	    info->step_ctrl.enabled) { | ||||
| 		i = 0; | ||||
| 		base = ARM_BASE_BCR + core_num_brps; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Reset the control register. */ | ||||
| 	write_wb_reg(base + i, 0); | ||||
| } | ||||
| @ -632,10 +627,9 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp) | ||||
| 	 * we can use the mismatch feature as a poor-man's hardware | ||||
| 	 * single-step, but this only works for per-task breakpoints. | ||||
| 	 */ | ||||
| 	if (WARN_ONCE(!bp->overflow_handler && | ||||
| 		(arch_check_bp_in_kernelspace(bp) || !core_has_mismatch_brps() | ||||
| 		 || !bp->hw.bp_target), | ||||
| 			"overflow handler required but none found\n")) { | ||||
| 	if (!bp->overflow_handler && (arch_check_bp_in_kernelspace(bp) || | ||||
| 	    !core_has_mismatch_brps() || !bp->hw.bp_target)) { | ||||
| 		pr_warning("overflow handler required but none found\n"); | ||||
| 		ret = -EINVAL; | ||||
| 	} | ||||
| out: | ||||
| @ -666,34 +660,62 @@ static void disable_single_step(struct perf_event *bp) | ||||
| 	arch_install_hw_breakpoint(bp); | ||||
| } | ||||
| 
 | ||||
| static void watchpoint_handler(unsigned long unknown, struct pt_regs *regs) | ||||
| static void watchpoint_handler(unsigned long addr, unsigned int fsr, | ||||
| 			       struct pt_regs *regs) | ||||
| { | ||||
| 	int i; | ||||
| 	int i, access; | ||||
| 	u32 val, ctrl_reg, alignment_mask; | ||||
| 	struct perf_event *wp, **slots; | ||||
| 	struct arch_hw_breakpoint *info; | ||||
| 	struct arch_hw_breakpoint_ctrl ctrl; | ||||
| 
 | ||||
| 	slots = (struct perf_event **)__get_cpu_var(wp_on_reg); | ||||
| 
 | ||||
| 	/* Without a disassembler, we can only handle 1 watchpoint. */ | ||||
| 	BUG_ON(core_num_wrps > 1); | ||||
| 
 | ||||
| 	for (i = 0; i < core_num_wrps; ++i) { | ||||
| 		rcu_read_lock(); | ||||
| 
 | ||||
| 		wp = slots[i]; | ||||
| 
 | ||||
| 		if (wp == NULL) { | ||||
| 			rcu_read_unlock(); | ||||
| 			continue; | ||||
| 		if (wp == NULL) | ||||
| 			goto unlock; | ||||
| 
 | ||||
| 		info = counter_arch_bp(wp); | ||||
| 		/*
 | ||||
| 		 * The DFAR is an unknown value on debug architectures prior | ||||
| 		 * to 7.1. Since we only allow a single watchpoint on these | ||||
| 		 * older CPUs, we can set the trigger to the lowest possible | ||||
| 		 * faulting address. | ||||
| 		 */ | ||||
| 		if (debug_arch < ARM_DEBUG_ARCH_V7_1) { | ||||
| 			BUG_ON(i > 0); | ||||
| 			info->trigger = wp->attr.bp_addr; | ||||
| 		} else { | ||||
| 			if (info->ctrl.len == ARM_BREAKPOINT_LEN_8) | ||||
| 				alignment_mask = 0x7; | ||||
| 			else | ||||
| 				alignment_mask = 0x3; | ||||
| 
 | ||||
| 			/* Check if the watchpoint value matches. */ | ||||
| 			val = read_wb_reg(ARM_BASE_WVR + i); | ||||
| 			if (val != (addr & ~alignment_mask)) | ||||
| 				goto unlock; | ||||
| 
 | ||||
| 			/* Possible match, check the byte address select. */ | ||||
| 			ctrl_reg = read_wb_reg(ARM_BASE_WCR + i); | ||||
| 			decode_ctrl_reg(ctrl_reg, &ctrl); | ||||
| 			if (!((1 << (addr & alignment_mask)) & ctrl.len)) | ||||
| 				goto unlock; | ||||
| 
 | ||||
| 			/* Check that the access type matches. */ | ||||
| 			access = (fsr & ARM_FSR_ACCESS_MASK) ? HW_BREAKPOINT_W : | ||||
| 				 HW_BREAKPOINT_R; | ||||
| 			if (!(access & hw_breakpoint_type(wp))) | ||||
| 				goto unlock; | ||||
| 
 | ||||
| 			/* We have a winner. */ | ||||
| 			info->trigger = addr; | ||||
| 		} | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * The DFAR is an unknown value. Since we only allow a | ||||
| 		 * single watchpoint, we can set the trigger to the lowest | ||||
| 		 * possible faulting address. | ||||
| 		 */ | ||||
| 		info = counter_arch_bp(wp); | ||||
| 		info->trigger = wp->attr.bp_addr; | ||||
| 		pr_debug("watchpoint fired: address = 0x%x\n", info->trigger); | ||||
| 		perf_bp_event(wp, regs); | ||||
| 
 | ||||
| @ -705,6 +727,7 @@ static void watchpoint_handler(unsigned long unknown, struct pt_regs *regs) | ||||
| 		if (!wp->overflow_handler) | ||||
| 			enable_single_step(wp, instruction_pointer(regs)); | ||||
| 
 | ||||
| unlock: | ||||
| 		rcu_read_unlock(); | ||||
| 	} | ||||
| } | ||||
| @ -717,7 +740,7 @@ static void watchpoint_single_step_handler(unsigned long pc) | ||||
| 
 | ||||
| 	slots = (struct perf_event **)__get_cpu_var(wp_on_reg); | ||||
| 
 | ||||
| 	for (i = 0; i < core_num_reserved_brps; ++i) { | ||||
| 	for (i = 0; i < core_num_wrps; ++i) { | ||||
| 		rcu_read_lock(); | ||||
| 
 | ||||
| 		wp = slots[i]; | ||||
| @ -820,7 +843,7 @@ static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr, | ||||
| 	case ARM_ENTRY_ASYNC_WATCHPOINT: | ||||
| 		WARN(1, "Asynchronous watchpoint exception taken. Debugging results may be unreliable\n"); | ||||
| 	case ARM_ENTRY_SYNC_WATCHPOINT: | ||||
| 		watchpoint_handler(addr, regs); | ||||
| 		watchpoint_handler(addr, fsr, regs); | ||||
| 		break; | ||||
| 	default: | ||||
| 		ret = 1; /* Unhandled fault. */ | ||||
| @ -834,11 +857,31 @@ static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr, | ||||
| /*
 | ||||
|  * One-time initialisation. | ||||
|  */ | ||||
| static void reset_ctrl_regs(void *info) | ||||
| static cpumask_t debug_err_mask; | ||||
| 
 | ||||
| static int debug_reg_trap(struct pt_regs *regs, unsigned int instr) | ||||
| { | ||||
| 	int i, cpu = smp_processor_id(); | ||||
| 	int cpu = smp_processor_id(); | ||||
| 
 | ||||
| 	pr_warning("Debug register access (0x%x) caused undefined instruction on CPU %d\n", | ||||
| 		   instr, cpu); | ||||
| 
 | ||||
| 	/* Set the error flag for this CPU and skip the faulting instruction. */ | ||||
| 	cpumask_set_cpu(cpu, &debug_err_mask); | ||||
| 	instruction_pointer(regs) += 4; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct undef_hook debug_reg_hook = { | ||||
| 	.instr_mask	= 0x0fe80f10, | ||||
| 	.instr_val	= 0x0e000e10, | ||||
| 	.fn		= debug_reg_trap, | ||||
| }; | ||||
| 
 | ||||
| static void reset_ctrl_regs(void *unused) | ||||
| { | ||||
| 	int i, raw_num_brps, err = 0, cpu = smp_processor_id(); | ||||
| 	u32 dbg_power; | ||||
| 	cpumask_t *cpumask = info; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * v7 debug contains save and restore registers so that debug state | ||||
| @ -848,38 +891,57 @@ static void reset_ctrl_regs(void *info) | ||||
| 	 * Access Register to avoid taking undefined instruction exceptions | ||||
| 	 * later on. | ||||
| 	 */ | ||||
| 	if (debug_arch >= ARM_DEBUG_ARCH_V7_ECP14) { | ||||
| 	switch (debug_arch) { | ||||
| 	case ARM_DEBUG_ARCH_V6: | ||||
| 	case ARM_DEBUG_ARCH_V6_1: | ||||
| 		/* ARMv6 cores just need to reset the registers. */ | ||||
| 		goto reset_regs; | ||||
| 	case ARM_DEBUG_ARCH_V7_ECP14: | ||||
| 		/*
 | ||||
| 		 * Ensure sticky power-down is clear (i.e. debug logic is | ||||
| 		 * powered up). | ||||
| 		 */ | ||||
| 		asm volatile("mrc p14, 0, %0, c1, c5, 4" : "=r" (dbg_power)); | ||||
| 		if ((dbg_power & 0x1) == 0) { | ||||
| 			pr_warning("CPU %d debug is powered down!\n", cpu); | ||||
| 			cpumask_or(cpumask, cpumask, cpumask_of(cpu)); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		if ((dbg_power & 0x1) == 0) | ||||
| 			err = -EPERM; | ||||
| 		break; | ||||
| 	case ARM_DEBUG_ARCH_V7_1: | ||||
| 		/*
 | ||||
| 		 * Unconditionally clear the lock by writing a value | ||||
| 		 * other than 0xC5ACCE55 to the access register. | ||||
| 		 * Ensure the OS double lock is clear. | ||||
| 		 */ | ||||
| 		asm volatile("mcr p14, 0, %0, c1, c0, 4" : : "r" (0)); | ||||
| 		isb(); | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * Clear any configured vector-catch events before | ||||
| 		 * enabling monitor mode. | ||||
| 		 */ | ||||
| 		asm volatile("mcr p14, 0, %0, c0, c7, 0" : : "r" (0)); | ||||
| 		isb(); | ||||
| 		asm volatile("mrc p14, 0, %0, c1, c3, 4" : "=r" (dbg_power)); | ||||
| 		if ((dbg_power & 0x1) == 1) | ||||
| 			err = -EPERM; | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	if (err) { | ||||
| 		pr_warning("CPU %d debug is powered down!\n", cpu); | ||||
| 		cpumask_or(&debug_err_mask, &debug_err_mask, cpumask_of(cpu)); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Unconditionally clear the lock by writing a value | ||||
| 	 * other than 0xC5ACCE55 to the access register. | ||||
| 	 */ | ||||
| 	asm volatile("mcr p14, 0, %0, c1, c0, 4" : : "r" (0)); | ||||
| 	isb(); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Clear any configured vector-catch events before | ||||
| 	 * enabling monitor mode. | ||||
| 	 */ | ||||
| 	asm volatile("mcr p14, 0, %0, c0, c7, 0" : : "r" (0)); | ||||
| 	isb(); | ||||
| 
 | ||||
| reset_regs: | ||||
| 	if (enable_monitor_mode()) | ||||
| 		return; | ||||
| 
 | ||||
| 	/* We must also reset any reserved registers. */ | ||||
| 	for (i = 0; i < core_num_brps + core_num_reserved_brps; ++i) { | ||||
| 	raw_num_brps = get_num_brp_resources(); | ||||
| 	for (i = 0; i < raw_num_brps; ++i) { | ||||
| 		write_wb_reg(ARM_BASE_BCR + i, 0UL); | ||||
| 		write_wb_reg(ARM_BASE_BVR + i, 0UL); | ||||
| 	} | ||||
| @ -895,6 +957,7 @@ static int __cpuinit dbg_reset_notify(struct notifier_block *self, | ||||
| { | ||||
| 	if (action == CPU_ONLINE) | ||||
| 		smp_call_function_single((int)cpu, reset_ctrl_regs, NULL, 1); | ||||
| 
 | ||||
| 	return NOTIFY_OK; | ||||
| } | ||||
| 
 | ||||
| @ -905,7 +968,6 @@ static struct notifier_block __cpuinitdata dbg_reset_nb = { | ||||
| static int __init arch_hw_breakpoint_init(void) | ||||
| { | ||||
| 	u32 dscr; | ||||
| 	cpumask_t cpumask = { CPU_BITS_NONE }; | ||||
| 
 | ||||
| 	debug_arch = get_debug_arch(); | ||||
| 
 | ||||
| @ -916,28 +978,31 @@ static int __init arch_hw_breakpoint_init(void) | ||||
| 
 | ||||
| 	/* Determine how many BRPs/WRPs are available. */ | ||||
| 	core_num_brps = get_num_brps(); | ||||
| 	core_num_reserved_brps = get_num_reserved_brps(); | ||||
| 	core_num_wrps = get_num_wrps(); | ||||
| 
 | ||||
| 	pr_info("found %d breakpoint and %d watchpoint registers.\n", | ||||
| 		core_num_brps + core_num_reserved_brps, core_num_wrps); | ||||
| 
 | ||||
| 	if (core_num_reserved_brps) | ||||
| 		pr_info("%d breakpoint(s) reserved for watchpoint " | ||||
| 				"single-step.\n", core_num_reserved_brps); | ||||
| 	/*
 | ||||
| 	 * We need to tread carefully here because DBGSWENABLE may be | ||||
| 	 * driven low on this core and there isn't an architected way to | ||||
| 	 * determine that. | ||||
| 	 */ | ||||
| 	register_undef_hook(&debug_reg_hook); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Reset the breakpoint resources. We assume that a halting | ||||
| 	 * debugger will leave the world in a nice state for us. | ||||
| 	 */ | ||||
| 	on_each_cpu(reset_ctrl_regs, &cpumask, 1); | ||||
| 	if (!cpumask_empty(&cpumask)) { | ||||
| 	on_each_cpu(reset_ctrl_regs, NULL, 1); | ||||
| 	unregister_undef_hook(&debug_reg_hook); | ||||
| 	if (!cpumask_empty(&debug_err_mask)) { | ||||
| 		core_num_brps = 0; | ||||
| 		core_num_reserved_brps = 0; | ||||
| 		core_num_wrps = 0; | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	pr_info("found %d " "%s" "breakpoint and %d watchpoint registers.\n", | ||||
| 		core_num_brps, core_has_mismatch_brps() ? "(+1 reserved) " : | ||||
| 		"", core_num_wrps); | ||||
| 
 | ||||
| 	ARM_DBG_READ(c1, 0, dscr); | ||||
| 	if (dscr & ARM_DSCR_HDBGEN) { | ||||
| 		max_watchpoint_len = 4; | ||||
|  | ||||
| @ -60,6 +60,7 @@ | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/kprobes.h> | ||||
| #include <linux/module.h> | ||||
| 
 | ||||
| #include "kprobes.h" | ||||
| 
 | ||||
| @ -971,6 +972,9 @@ const union decode_item kprobe_decode_arm_table[] = { | ||||
| 
 | ||||
| 	DECODE_END | ||||
| }; | ||||
| #ifdef CONFIG_ARM_KPROBES_TEST_MODULE | ||||
| EXPORT_SYMBOL_GPL(kprobe_decode_arm_table); | ||||
| #endif | ||||
| 
 | ||||
| static void __kprobes arm_singlestep(struct kprobe *p, struct pt_regs *regs) | ||||
| { | ||||
|  | ||||
							
								
								
									
										1323
									
								
								arch/arm/kernel/kprobes-test-arm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1323
									
								
								arch/arm/kernel/kprobes-test-arm.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1187
									
								
								arch/arm/kernel/kprobes-test-thumb.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1187
									
								
								arch/arm/kernel/kprobes-test-thumb.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1748
									
								
								arch/arm/kernel/kprobes-test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1748
									
								
								arch/arm/kernel/kprobes-test.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										392
									
								
								arch/arm/kernel/kprobes-test.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										392
									
								
								arch/arm/kernel/kprobes-test.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,392 @@ | ||||
| /*
 | ||||
|  * arch/arm/kernel/kprobes-test.h | ||||
|  * | ||||
|  * Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>. | ||||
|  * | ||||
|  * 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. | ||||
|  */ | ||||
| 
 | ||||
| #define VERBOSE 0 /* Set to '1' for more logging of test cases */ | ||||
| 
 | ||||
| #ifdef CONFIG_THUMB2_KERNEL | ||||
| #define NORMAL_ISA "16" | ||||
| #else | ||||
| #define NORMAL_ISA "32" | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| /* Flags used in kprobe_test_flags */ | ||||
| #define TEST_FLAG_NO_ITBLOCK	(1<<0) | ||||
| #define TEST_FLAG_FULL_ITBLOCK	(1<<1) | ||||
| #define TEST_FLAG_NARROW_INSTR	(1<<2) | ||||
| 
 | ||||
| extern int kprobe_test_flags; | ||||
| extern int kprobe_test_cc_position; | ||||
| 
 | ||||
| 
 | ||||
| #define TEST_MEMORY_SIZE 256 | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Test case structures. | ||||
|  * | ||||
|  * The arguments given to test cases can be one of three types. | ||||
|  * | ||||
|  *   ARG_TYPE_REG | ||||
|  *	Load a register with the given value. | ||||
|  * | ||||
|  *   ARG_TYPE_PTR | ||||
|  *	Load a register with a pointer into the stack buffer (SP + given value). | ||||
|  * | ||||
|  *   ARG_TYPE_MEM | ||||
|  *	Store the given value into the stack buffer at [SP+index]. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #define	ARG_TYPE_END	0 | ||||
| #define	ARG_TYPE_REG	1 | ||||
| #define	ARG_TYPE_PTR	2 | ||||
| #define	ARG_TYPE_MEM	3 | ||||
| 
 | ||||
| #define ARG_FLAG_UNSUPPORTED	0x01 | ||||
| #define ARG_FLAG_SUPPORTED	0x02 | ||||
| #define ARG_FLAG_THUMB		0x10	/* Must be 16 so TEST_ISA can be used */ | ||||
| #define ARG_FLAG_ARM		0x20	/* Must be 32 so TEST_ISA can be used */ | ||||
| 
 | ||||
| struct test_arg { | ||||
| 	u8	type;		/* ARG_TYPE_x */ | ||||
| 	u8	_padding[7]; | ||||
| }; | ||||
| 
 | ||||
| struct test_arg_regptr { | ||||
| 	u8	type;		/* ARG_TYPE_REG or ARG_TYPE_PTR */ | ||||
| 	u8	reg; | ||||
| 	u8	_padding[2]; | ||||
| 	u32	val; | ||||
| }; | ||||
| 
 | ||||
| struct test_arg_mem { | ||||
| 	u8	type;		/* ARG_TYPE_MEM */ | ||||
| 	u8	index; | ||||
| 	u8	_padding[2]; | ||||
| 	u32	val; | ||||
| }; | ||||
| 
 | ||||
| struct test_arg_end { | ||||
| 	u8	type;		/* ARG_TYPE_END */ | ||||
| 	u8	flags;		/* ARG_FLAG_x */ | ||||
| 	u16	code_offset; | ||||
| 	u16	branch_offset; | ||||
| 	u16	end_offset; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Building blocks for test cases. | ||||
|  * | ||||
|  * Each test case is wrapped between TESTCASE_START and TESTCASE_END. | ||||
|  * | ||||
|  * To specify arguments for a test case the TEST_ARG_{REG,PTR,MEM} macros are | ||||
|  * used followed by a terminating TEST_ARG_END. | ||||
|  * | ||||
|  * After this, the instruction to be tested is defined with TEST_INSTRUCTION. | ||||
|  * Or for branches, TEST_BRANCH_B and TEST_BRANCH_F (branch forwards/backwards). | ||||
|  * | ||||
|  * Some specific test cases may make use of other custom constructs. | ||||
|  */ | ||||
| 
 | ||||
| #if VERBOSE | ||||
| #define verbose(fmt, ...) pr_info(fmt, ##__VA_ARGS__) | ||||
| #else | ||||
| #define verbose(fmt, ...) | ||||
| #endif | ||||
| 
 | ||||
| #define TEST_GROUP(title)					\ | ||||
| 	verbose("\n");						\ | ||||
| 	verbose(title"\n");					\ | ||||
| 	verbose("---------------------------------------------------------\n"); | ||||
| 
 | ||||
| #define TESTCASE_START(title)					\ | ||||
| 	__asm__ __volatile__ (					\ | ||||
| 	"bl	__kprobes_test_case_start		\n\t"	\ | ||||
| 	/* don't use .asciz here as 'title' may be */		\ | ||||
| 	/* multiple strings to be concatenated.  */		\ | ||||
| 	".ascii "#title"				\n\t"	\ | ||||
| 	".byte	0					\n\t"	\ | ||||
| 	".align	2					\n\t" | ||||
| 
 | ||||
| #define	TEST_ARG_REG(reg, val)					\ | ||||
| 	".byte	"__stringify(ARG_TYPE_REG)"		\n\t"	\ | ||||
| 	".byte	"#reg"					\n\t"	\ | ||||
| 	".short	0					\n\t"	\ | ||||
| 	".word	"#val"					\n\t" | ||||
| 
 | ||||
| #define	TEST_ARG_PTR(reg, val)					\ | ||||
| 	".byte	"__stringify(ARG_TYPE_PTR)"		\n\t"	\ | ||||
| 	".byte	"#reg"					\n\t"	\ | ||||
| 	".short	0					\n\t"	\ | ||||
| 	".word	"#val"					\n\t" | ||||
| 
 | ||||
| #define	TEST_ARG_MEM(index, val)				\ | ||||
| 	".byte	"__stringify(ARG_TYPE_MEM)"		\n\t"	\ | ||||
| 	".byte	"#index"				\n\t"	\ | ||||
| 	".short	0					\n\t"	\ | ||||
| 	".word	"#val"					\n\t" | ||||
| 
 | ||||
| #define	TEST_ARG_END(flags)					\ | ||||
| 	".byte	"__stringify(ARG_TYPE_END)"		\n\t"	\ | ||||
| 	".byte	"TEST_ISA flags"			\n\t"	\ | ||||
| 	".short	50f-0f					\n\t"	\ | ||||
| 	".short	2f-0f					\n\t"	\ | ||||
| 	".short	99f-0f					\n\t"	\ | ||||
| 	".code "TEST_ISA"				\n\t"	\ | ||||
| 	"0:						\n\t" | ||||
| 
 | ||||
| #define TEST_INSTRUCTION(instruction)				\ | ||||
| 	"50:	nop					\n\t"	\ | ||||
| 	"1:	"instruction"				\n\t"	\ | ||||
| 	"	nop					\n\t" | ||||
| 
 | ||||
| #define TEST_BRANCH_F(instruction, xtra_dist)			\ | ||||
| 	TEST_INSTRUCTION(instruction)				\ | ||||
| 	".if "#xtra_dist"				\n\t"	\ | ||||
| 	"	b	99f				\n\t"	\ | ||||
| 	".space "#xtra_dist"				\n\t"	\ | ||||
| 	".endif						\n\t"	\ | ||||
| 	"	b	99f				\n\t"	\ | ||||
| 	"2:	nop					\n\t" | ||||
| 
 | ||||
| #define TEST_BRANCH_B(instruction, xtra_dist)			\ | ||||
| 	"	b	50f				\n\t"	\ | ||||
| 	"	b	99f				\n\t"	\ | ||||
| 	"2:	nop					\n\t"	\ | ||||
| 	"	b	99f				\n\t"	\ | ||||
| 	".if "#xtra_dist"				\n\t"	\ | ||||
| 	".space "#xtra_dist"				\n\t"	\ | ||||
| 	".endif						\n\t"	\ | ||||
| 	TEST_INSTRUCTION(instruction) | ||||
| 
 | ||||
| #define TESTCASE_END						\ | ||||
| 	"2:						\n\t"	\ | ||||
| 	"99:						\n\t"	\ | ||||
| 	"	bl __kprobes_test_case_end_"TEST_ISA"	\n\t"	\ | ||||
| 	".code "NORMAL_ISA"				\n\t"	\ | ||||
| 	: :							\ | ||||
| 	: "r0", "r1", "r2", "r3", "ip", "lr", "memory", "cc"	\ | ||||
| 	); | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Macros to define test cases. | ||||
|  * | ||||
|  * Those of the form TEST_{R,P,M}* can be used to define test cases | ||||
|  * which take combinations of the three basic types of arguments. E.g. | ||||
|  * | ||||
|  *   TEST_R	One register argument | ||||
|  *   TEST_RR	Two register arguments | ||||
|  *   TEST_RPR	A register, a pointer, then a register argument | ||||
|  * | ||||
|  * For testing instructions which may branch, there are macros TEST_BF_* | ||||
|  * and TEST_BB_* for branching forwards and backwards. | ||||
|  * | ||||
|  * TEST_SUPPORTED and TEST_UNSUPPORTED don't cause the code to be executed, | ||||
|  * the just verify that a kprobe is or is not allowed on the given instruction. | ||||
|  */ | ||||
| 
 | ||||
| #define TEST(code)				\ | ||||
| 	TESTCASE_START(code)			\ | ||||
| 	TEST_ARG_END("")			\ | ||||
| 	TEST_INSTRUCTION(code)			\ | ||||
| 	TESTCASE_END | ||||
| 
 | ||||
| #define TEST_UNSUPPORTED(code)					\ | ||||
| 	TESTCASE_START(code)					\ | ||||
| 	TEST_ARG_END("|"__stringify(ARG_FLAG_UNSUPPORTED))	\ | ||||
| 	TEST_INSTRUCTION(code)					\ | ||||
| 	TESTCASE_END | ||||
| 
 | ||||
| #define TEST_SUPPORTED(code)					\ | ||||
| 	TESTCASE_START(code)					\ | ||||
| 	TEST_ARG_END("|"__stringify(ARG_FLAG_SUPPORTED))	\ | ||||
| 	TEST_INSTRUCTION(code)					\ | ||||
| 	TESTCASE_END | ||||
| 
 | ||||
| #define TEST_R(code1, reg, val, code2)			\ | ||||
| 	TESTCASE_START(code1 #reg code2)		\ | ||||
| 	TEST_ARG_REG(reg, val)				\ | ||||
| 	TEST_ARG_END("")				\ | ||||
| 	TEST_INSTRUCTION(code1 #reg code2)		\ | ||||
| 	TESTCASE_END | ||||
| 
 | ||||
| #define TEST_RR(code1, reg1, val1, code2, reg2, val2, code3)	\ | ||||
| 	TESTCASE_START(code1 #reg1 code2 #reg2 code3)		\ | ||||
| 	TEST_ARG_REG(reg1, val1)				\ | ||||
| 	TEST_ARG_REG(reg2, val2)				\ | ||||
| 	TEST_ARG_END("")					\ | ||||
| 	TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3)		\ | ||||
| 	TESTCASE_END | ||||
| 
 | ||||
| #define TEST_RRR(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4)\ | ||||
| 	TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4)		\ | ||||
| 	TEST_ARG_REG(reg1, val1)						\ | ||||
| 	TEST_ARG_REG(reg2, val2)						\ | ||||
| 	TEST_ARG_REG(reg3, val3)						\ | ||||
| 	TEST_ARG_END("")							\ | ||||
| 	TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4)		\ | ||||
| 	TESTCASE_END | ||||
| 
 | ||||
| #define TEST_RRRR(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4, reg4, val4)	\ | ||||
| 	TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4 #reg4)		\ | ||||
| 	TEST_ARG_REG(reg1, val1)						\ | ||||
| 	TEST_ARG_REG(reg2, val2)						\ | ||||
| 	TEST_ARG_REG(reg3, val3)						\ | ||||
| 	TEST_ARG_REG(reg4, val4)						\ | ||||
| 	TEST_ARG_END("")							\ | ||||
| 	TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4 #reg4)	\ | ||||
| 	TESTCASE_END | ||||
| 
 | ||||
| #define TEST_P(code1, reg1, val1, code2)	\ | ||||
| 	TESTCASE_START(code1 #reg1 code2)	\ | ||||
| 	TEST_ARG_PTR(reg1, val1)		\ | ||||
| 	TEST_ARG_END("")			\ | ||||
| 	TEST_INSTRUCTION(code1 #reg1 code2)	\ | ||||
| 	TESTCASE_END | ||||
| 
 | ||||
| #define TEST_PR(code1, reg1, val1, code2, reg2, val2, code3)	\ | ||||
| 	TESTCASE_START(code1 #reg1 code2 #reg2 code3)		\ | ||||
| 	TEST_ARG_PTR(reg1, val1)				\ | ||||
| 	TEST_ARG_REG(reg2, val2)				\ | ||||
| 	TEST_ARG_END("")					\ | ||||
| 	TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3)		\ | ||||
| 	TESTCASE_END | ||||
| 
 | ||||
| #define TEST_RP(code1, reg1, val1, code2, reg2, val2, code3)	\ | ||||
| 	TESTCASE_START(code1 #reg1 code2 #reg2 code3)		\ | ||||
| 	TEST_ARG_REG(reg1, val1)				\ | ||||
| 	TEST_ARG_PTR(reg2, val2)				\ | ||||
| 	TEST_ARG_END("")					\ | ||||
| 	TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3)		\ | ||||
| 	TESTCASE_END | ||||
| 
 | ||||
| #define TEST_PRR(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4)\ | ||||
| 	TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4)		\ | ||||
| 	TEST_ARG_PTR(reg1, val1)						\ | ||||
| 	TEST_ARG_REG(reg2, val2)						\ | ||||
| 	TEST_ARG_REG(reg3, val3)						\ | ||||
| 	TEST_ARG_END("")							\ | ||||
| 	TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4)		\ | ||||
| 	TESTCASE_END | ||||
| 
 | ||||
| #define TEST_RPR(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4)\ | ||||
| 	TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4)		\ | ||||
| 	TEST_ARG_REG(reg1, val1)						\ | ||||
| 	TEST_ARG_PTR(reg2, val2)						\ | ||||
| 	TEST_ARG_REG(reg3, val3)						\ | ||||
| 	TEST_ARG_END("")							\ | ||||
| 	TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4)		\ | ||||
| 	TESTCASE_END | ||||
| 
 | ||||
| #define TEST_RRP(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4)\ | ||||
| 	TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4)		\ | ||||
| 	TEST_ARG_REG(reg1, val1)						\ | ||||
| 	TEST_ARG_REG(reg2, val2)						\ | ||||
| 	TEST_ARG_PTR(reg3, val3)						\ | ||||
| 	TEST_ARG_END("")							\ | ||||
| 	TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4)		\ | ||||
| 	TESTCASE_END | ||||
| 
 | ||||
| #define TEST_BF_P(code1, reg1, val1, code2)	\ | ||||
| 	TESTCASE_START(code1 #reg1 code2)	\ | ||||
| 	TEST_ARG_PTR(reg1, val1)		\ | ||||
| 	TEST_ARG_END("")			\ | ||||
| 	TEST_BRANCH_F(code1 #reg1 code2, 0)	\ | ||||
| 	TESTCASE_END | ||||
| 
 | ||||
| #define TEST_BF_X(code, xtra_dist)		\ | ||||
| 	TESTCASE_START(code)			\ | ||||
| 	TEST_ARG_END("")			\ | ||||
| 	TEST_BRANCH_F(code, xtra_dist)		\ | ||||
| 	TESTCASE_END | ||||
| 
 | ||||
| #define TEST_BB_X(code, xtra_dist)		\ | ||||
| 	TESTCASE_START(code)			\ | ||||
| 	TEST_ARG_END("")			\ | ||||
| 	TEST_BRANCH_B(code, xtra_dist)		\ | ||||
| 	TESTCASE_END | ||||
| 
 | ||||
| #define TEST_BF_RX(code1, reg, val, code2, xtra_dist)	\ | ||||
| 	TESTCASE_START(code1 #reg code2)		\ | ||||
| 	TEST_ARG_REG(reg, val)				\ | ||||
| 	TEST_ARG_END("")				\ | ||||
| 	TEST_BRANCH_F(code1 #reg code2, xtra_dist)	\ | ||||
| 	TESTCASE_END | ||||
| 
 | ||||
| #define TEST_BB_RX(code1, reg, val, code2, xtra_dist)	\ | ||||
| 	TESTCASE_START(code1 #reg code2)		\ | ||||
| 	TEST_ARG_REG(reg, val)				\ | ||||
| 	TEST_ARG_END("")				\ | ||||
| 	TEST_BRANCH_B(code1 #reg code2, xtra_dist)	\ | ||||
| 	TESTCASE_END | ||||
| 
 | ||||
| #define TEST_BF(code)	TEST_BF_X(code, 0) | ||||
| #define TEST_BB(code)	TEST_BB_X(code, 0) | ||||
| 
 | ||||
| #define TEST_BF_R(code1, reg, val, code2) TEST_BF_RX(code1, reg, val, code2, 0) | ||||
| #define TEST_BB_R(code1, reg, val, code2) TEST_BB_RX(code1, reg, val, code2, 0) | ||||
| 
 | ||||
| #define TEST_BF_RR(code1, reg1, val1, code2, reg2, val2, code3)	\ | ||||
| 	TESTCASE_START(code1 #reg1 code2 #reg2 code3)		\ | ||||
| 	TEST_ARG_REG(reg1, val1)				\ | ||||
| 	TEST_ARG_REG(reg2, val2)				\ | ||||
| 	TEST_ARG_END("")					\ | ||||
| 	TEST_BRANCH_F(code1 #reg1 code2 #reg2 code3, 0)		\ | ||||
| 	TESTCASE_END | ||||
| 
 | ||||
| #define TEST_X(code, codex)			\ | ||||
| 	TESTCASE_START(code)			\ | ||||
| 	TEST_ARG_END("")			\ | ||||
| 	TEST_INSTRUCTION(code)			\ | ||||
| 	"	b	99f		\n\t"	\ | ||||
| 	"	"codex"			\n\t"	\ | ||||
| 	TESTCASE_END | ||||
| 
 | ||||
| #define TEST_RX(code1, reg, val, code2, codex)		\ | ||||
| 	TESTCASE_START(code1 #reg code2)		\ | ||||
| 	TEST_ARG_REG(reg, val)				\ | ||||
| 	TEST_ARG_END("")				\ | ||||
| 	TEST_INSTRUCTION(code1 __stringify(reg) code2)	\ | ||||
| 	"	b	99f		\n\t"		\ | ||||
| 	"	"codex"			\n\t"		\ | ||||
| 	TESTCASE_END | ||||
| 
 | ||||
| #define TEST_RRX(code1, reg1, val1, code2, reg2, val2, code3, codex)		\ | ||||
| 	TESTCASE_START(code1 #reg1 code2 #reg2 code3)				\ | ||||
| 	TEST_ARG_REG(reg1, val1)						\ | ||||
| 	TEST_ARG_REG(reg2, val2)						\ | ||||
| 	TEST_ARG_END("")							\ | ||||
| 	TEST_INSTRUCTION(code1 __stringify(reg1) code2 __stringify(reg2) code3)	\ | ||||
| 	"	b	99f		\n\t"					\ | ||||
| 	"	"codex"			\n\t"					\ | ||||
| 	TESTCASE_END | ||||
| 
 | ||||
| 
 | ||||
| /* Various values used in test cases... */ | ||||
| #define N(val)	(val ^ 0xffffffff) | ||||
| #define VAL1	0x12345678 | ||||
| #define VAL2	N(VAL1) | ||||
| #define VAL3	0xa5f801 | ||||
| #define VAL4	N(VAL3) | ||||
| #define VALM	0x456789ab | ||||
| #define VALR	0xdeaddead | ||||
| #define HH1	0x0123fecb | ||||
| #define HH2	0xa9874567 | ||||
| 
 | ||||
| 
 | ||||
| #ifdef CONFIG_THUMB2_KERNEL | ||||
| void kprobe_thumb16_test_cases(void); | ||||
| void kprobe_thumb32_test_cases(void); | ||||
| #else | ||||
| void kprobe_arm_test_cases(void); | ||||
| #endif | ||||
| @ -10,6 +10,7 @@ | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/kprobes.h> | ||||
| #include <linux/module.h> | ||||
| 
 | ||||
| #include "kprobes.h" | ||||
| 
 | ||||
| @ -943,6 +944,9 @@ const union decode_item kprobe_decode_thumb32_table[] = { | ||||
| 	 */ | ||||
| 	DECODE_END | ||||
| }; | ||||
| #ifdef CONFIG_ARM_KPROBES_TEST_MODULE | ||||
| EXPORT_SYMBOL_GPL(kprobe_decode_thumb32_table); | ||||
| #endif | ||||
| 
 | ||||
| static void __kprobes | ||||
| t16_simulate_bxblx(struct kprobe *p, struct pt_regs *regs) | ||||
| @ -1423,6 +1427,9 @@ const union decode_item kprobe_decode_thumb16_table[] = { | ||||
| 
 | ||||
| 	DECODE_END | ||||
| }; | ||||
| #ifdef CONFIG_ARM_KPROBES_TEST_MODULE | ||||
| EXPORT_SYMBOL_GPL(kprobe_decode_thumb16_table); | ||||
| #endif | ||||
| 
 | ||||
| static unsigned long __kprobes thumb_check_cc(unsigned long cpsr) | ||||
| { | ||||
|  | ||||
| @ -413,6 +413,14 @@ struct decode_reject { | ||||
| 	DECODE_HEADER(DECODE_TYPE_REJECT, _mask, _value, 0) | ||||
| 
 | ||||
| 
 | ||||
| #ifdef CONFIG_THUMB2_KERNEL | ||||
| extern const union decode_item kprobe_decode_thumb16_table[]; | ||||
| extern const union decode_item kprobe_decode_thumb32_table[]; | ||||
| #else | ||||
| extern const union decode_item kprobe_decode_arm_table[]; | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| int kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi, | ||||
| 			const union decode_item *table, bool thumb16); | ||||
| 
 | ||||
|  | ||||
| @ -12,6 +12,7 @@ | ||||
|  */ | ||||
| #define pr_fmt(fmt) "hw perfevents: " fmt | ||||
| 
 | ||||
| #include <linux/bitmap.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| @ -26,16 +27,8 @@ | ||||
| #include <asm/pmu.h> | ||||
| #include <asm/stacktrace.h> | ||||
| 
 | ||||
| static struct platform_device *pmu_device; | ||||
| 
 | ||||
| /*
 | ||||
|  * Hardware lock to serialize accesses to PMU registers. Needed for the | ||||
|  * read/modify/write sequences. | ||||
|  */ | ||||
| static DEFINE_RAW_SPINLOCK(pmu_lock); | ||||
| 
 | ||||
| /*
 | ||||
|  * ARMv6 supports a maximum of 3 events, starting from index 1. If we add | ||||
|  * ARMv6 supports a maximum of 3 events, starting from index 0. If we add | ||||
|  * another platform that supports more, we need to increase this to be the | ||||
|  * largest of all platforms. | ||||
|  * | ||||
| @ -43,62 +36,24 @@ static DEFINE_RAW_SPINLOCK(pmu_lock); | ||||
|  *  cycle counter CCNT + 31 events counters CNT0..30. | ||||
|  *  Cortex-A8 has 1+4 counters, Cortex-A9 has 1+6 counters. | ||||
|  */ | ||||
| #define ARMPMU_MAX_HWEVENTS		33 | ||||
| #define ARMPMU_MAX_HWEVENTS		32 | ||||
| 
 | ||||
| /* The events for a given CPU. */ | ||||
| struct cpu_hw_events { | ||||
| 	/*
 | ||||
| 	 * The events that are active on the CPU for the given index. Index 0 | ||||
| 	 * is reserved. | ||||
| 	 */ | ||||
| 	struct perf_event	*events[ARMPMU_MAX_HWEVENTS]; | ||||
| static DEFINE_PER_CPU(struct perf_event * [ARMPMU_MAX_HWEVENTS], hw_events); | ||||
| static DEFINE_PER_CPU(unsigned long [BITS_TO_LONGS(ARMPMU_MAX_HWEVENTS)], used_mask); | ||||
| static DEFINE_PER_CPU(struct pmu_hw_events, cpu_hw_events); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * A 1 bit for an index indicates that the counter is being used for | ||||
| 	 * an event. A 0 means that the counter can be used. | ||||
| 	 */ | ||||
| 	unsigned long		used_mask[BITS_TO_LONGS(ARMPMU_MAX_HWEVENTS)]; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * A 1 bit for an index indicates that the counter is actively being | ||||
| 	 * used. | ||||
| 	 */ | ||||
| 	unsigned long		active_mask[BITS_TO_LONGS(ARMPMU_MAX_HWEVENTS)]; | ||||
| }; | ||||
| static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events); | ||||
| 
 | ||||
| struct arm_pmu { | ||||
| 	enum arm_perf_pmu_ids id; | ||||
| 	const char	*name; | ||||
| 	irqreturn_t	(*handle_irq)(int irq_num, void *dev); | ||||
| 	void		(*enable)(struct hw_perf_event *evt, int idx); | ||||
| 	void		(*disable)(struct hw_perf_event *evt, int idx); | ||||
| 	int		(*get_event_idx)(struct cpu_hw_events *cpuc, | ||||
| 					 struct hw_perf_event *hwc); | ||||
| 	u32		(*read_counter)(int idx); | ||||
| 	void		(*write_counter)(int idx, u32 val); | ||||
| 	void		(*start)(void); | ||||
| 	void		(*stop)(void); | ||||
| 	void		(*reset)(void *); | ||||
| 	const unsigned	(*cache_map)[PERF_COUNT_HW_CACHE_MAX] | ||||
| 				    [PERF_COUNT_HW_CACHE_OP_MAX] | ||||
| 				    [PERF_COUNT_HW_CACHE_RESULT_MAX]; | ||||
| 	const unsigned	(*event_map)[PERF_COUNT_HW_MAX]; | ||||
| 	u32		raw_event_mask; | ||||
| 	int		num_events; | ||||
| 	u64		max_period; | ||||
| }; | ||||
| #define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu)) | ||||
| 
 | ||||
| /* Set at runtime when we know what CPU type we are. */ | ||||
| static const struct arm_pmu *armpmu; | ||||
| static struct arm_pmu *cpu_pmu; | ||||
| 
 | ||||
| enum arm_perf_pmu_ids | ||||
| armpmu_get_pmu_id(void) | ||||
| { | ||||
| 	int id = -ENODEV; | ||||
| 
 | ||||
| 	if (armpmu != NULL) | ||||
| 		id = armpmu->id; | ||||
| 	if (cpu_pmu != NULL) | ||||
| 		id = cpu_pmu->id; | ||||
| 
 | ||||
| 	return id; | ||||
| } | ||||
| @ -109,8 +64,8 @@ armpmu_get_max_events(void) | ||||
| { | ||||
| 	int max_events = 0; | ||||
| 
 | ||||
| 	if (armpmu != NULL) | ||||
| 		max_events = armpmu->num_events; | ||||
| 	if (cpu_pmu != NULL) | ||||
| 		max_events = cpu_pmu->num_events; | ||||
| 
 | ||||
| 	return max_events; | ||||
| } | ||||
| @ -130,7 +85,11 @@ EXPORT_SYMBOL_GPL(perf_num_counters); | ||||
| #define CACHE_OP_UNSUPPORTED		0xFFFF | ||||
| 
 | ||||
| static int | ||||
| armpmu_map_cache_event(u64 config) | ||||
| armpmu_map_cache_event(const unsigned (*cache_map) | ||||
| 				      [PERF_COUNT_HW_CACHE_MAX] | ||||
| 				      [PERF_COUNT_HW_CACHE_OP_MAX] | ||||
| 				      [PERF_COUNT_HW_CACHE_RESULT_MAX], | ||||
| 		       u64 config) | ||||
| { | ||||
| 	unsigned int cache_type, cache_op, cache_result, ret; | ||||
| 
 | ||||
| @ -146,7 +105,7 @@ armpmu_map_cache_event(u64 config) | ||||
| 	if (cache_result >= PERF_COUNT_HW_CACHE_RESULT_MAX) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	ret = (int)(*armpmu->cache_map)[cache_type][cache_op][cache_result]; | ||||
| 	ret = (int)(*cache_map)[cache_type][cache_op][cache_result]; | ||||
| 
 | ||||
| 	if (ret == CACHE_OP_UNSUPPORTED) | ||||
| 		return -ENOENT; | ||||
| @ -155,23 +114,46 @@ armpmu_map_cache_event(u64 config) | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| armpmu_map_event(u64 config) | ||||
| armpmu_map_event(const unsigned (*event_map)[PERF_COUNT_HW_MAX], u64 config) | ||||
| { | ||||
| 	int mapping = (*armpmu->event_map)[config]; | ||||
| 	return mapping == HW_OP_UNSUPPORTED ? -EOPNOTSUPP : mapping; | ||||
| 	int mapping = (*event_map)[config]; | ||||
| 	return mapping == HW_OP_UNSUPPORTED ? -ENOENT : mapping; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| armpmu_map_raw_event(u64 config) | ||||
| armpmu_map_raw_event(u32 raw_event_mask, u64 config) | ||||
| { | ||||
| 	return (int)(config & armpmu->raw_event_mask); | ||||
| 	return (int)(config & raw_event_mask); | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| static int map_cpu_event(struct perf_event *event, | ||||
| 			 const unsigned (*event_map)[PERF_COUNT_HW_MAX], | ||||
| 			 const unsigned (*cache_map) | ||||
| 					[PERF_COUNT_HW_CACHE_MAX] | ||||
| 					[PERF_COUNT_HW_CACHE_OP_MAX] | ||||
| 					[PERF_COUNT_HW_CACHE_RESULT_MAX], | ||||
| 			 u32 raw_event_mask) | ||||
| { | ||||
| 	u64 config = event->attr.config; | ||||
| 
 | ||||
| 	switch (event->attr.type) { | ||||
| 	case PERF_TYPE_HARDWARE: | ||||
| 		return armpmu_map_event(event_map, config); | ||||
| 	case PERF_TYPE_HW_CACHE: | ||||
| 		return armpmu_map_cache_event(cache_map, config); | ||||
| 	case PERF_TYPE_RAW: | ||||
| 		return armpmu_map_raw_event(raw_event_mask, config); | ||||
| 	} | ||||
| 
 | ||||
| 	return -ENOENT; | ||||
| } | ||||
| 
 | ||||
| int | ||||
| armpmu_event_set_period(struct perf_event *event, | ||||
| 			struct hw_perf_event *hwc, | ||||
| 			int idx) | ||||
| { | ||||
| 	struct arm_pmu *armpmu = to_arm_pmu(event->pmu); | ||||
| 	s64 left = local64_read(&hwc->period_left); | ||||
| 	s64 period = hwc->sample_period; | ||||
| 	int ret = 0; | ||||
| @ -202,11 +184,12 @@ armpmu_event_set_period(struct perf_event *event, | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static u64 | ||||
| u64 | ||||
| armpmu_event_update(struct perf_event *event, | ||||
| 		    struct hw_perf_event *hwc, | ||||
| 		    int idx, int overflow) | ||||
| { | ||||
| 	struct arm_pmu *armpmu = to_arm_pmu(event->pmu); | ||||
| 	u64 delta, prev_raw_count, new_raw_count; | ||||
| 
 | ||||
| again: | ||||
| @ -246,11 +229,9 @@ armpmu_read(struct perf_event *event) | ||||
| static void | ||||
| armpmu_stop(struct perf_event *event, int flags) | ||||
| { | ||||
| 	struct arm_pmu *armpmu = to_arm_pmu(event->pmu); | ||||
| 	struct hw_perf_event *hwc = &event->hw; | ||||
| 
 | ||||
| 	if (!armpmu) | ||||
| 		return; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * ARM pmu always has to update the counter, so ignore | ||||
| 	 * PERF_EF_UPDATE, see comments in armpmu_start(). | ||||
| @ -266,11 +247,9 @@ armpmu_stop(struct perf_event *event, int flags) | ||||
| static void | ||||
| armpmu_start(struct perf_event *event, int flags) | ||||
| { | ||||
| 	struct arm_pmu *armpmu = to_arm_pmu(event->pmu); | ||||
| 	struct hw_perf_event *hwc = &event->hw; | ||||
| 
 | ||||
| 	if (!armpmu) | ||||
| 		return; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * ARM pmu always has to reprogram the period, so ignore | ||||
| 	 * PERF_EF_RELOAD, see the comment below. | ||||
| @ -293,16 +272,16 @@ armpmu_start(struct perf_event *event, int flags) | ||||
| static void | ||||
| armpmu_del(struct perf_event *event, int flags) | ||||
| { | ||||
| 	struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | ||||
| 	struct arm_pmu *armpmu = to_arm_pmu(event->pmu); | ||||
| 	struct pmu_hw_events *hw_events = armpmu->get_hw_events(); | ||||
| 	struct hw_perf_event *hwc = &event->hw; | ||||
| 	int idx = hwc->idx; | ||||
| 
 | ||||
| 	WARN_ON(idx < 0); | ||||
| 
 | ||||
| 	clear_bit(idx, cpuc->active_mask); | ||||
| 	armpmu_stop(event, PERF_EF_UPDATE); | ||||
| 	cpuc->events[idx] = NULL; | ||||
| 	clear_bit(idx, cpuc->used_mask); | ||||
| 	hw_events->events[idx] = NULL; | ||||
| 	clear_bit(idx, hw_events->used_mask); | ||||
| 
 | ||||
| 	perf_event_update_userpage(event); | ||||
| } | ||||
| @ -310,7 +289,8 @@ armpmu_del(struct perf_event *event, int flags) | ||||
| static int | ||||
| armpmu_add(struct perf_event *event, int flags) | ||||
| { | ||||
| 	struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | ||||
| 	struct arm_pmu *armpmu = to_arm_pmu(event->pmu); | ||||
| 	struct pmu_hw_events *hw_events = armpmu->get_hw_events(); | ||||
| 	struct hw_perf_event *hwc = &event->hw; | ||||
| 	int idx; | ||||
| 	int err = 0; | ||||
| @ -318,7 +298,7 @@ armpmu_add(struct perf_event *event, int flags) | ||||
| 	perf_pmu_disable(event->pmu); | ||||
| 
 | ||||
| 	/* If we don't have a space for the counter then finish early. */ | ||||
| 	idx = armpmu->get_event_idx(cpuc, hwc); | ||||
| 	idx = armpmu->get_event_idx(hw_events, hwc); | ||||
| 	if (idx < 0) { | ||||
| 		err = idx; | ||||
| 		goto out; | ||||
| @ -330,8 +310,7 @@ armpmu_add(struct perf_event *event, int flags) | ||||
| 	 */ | ||||
| 	event->hw.idx = idx; | ||||
| 	armpmu->disable(hwc, idx); | ||||
| 	cpuc->events[idx] = event; | ||||
| 	set_bit(idx, cpuc->active_mask); | ||||
| 	hw_events->events[idx] = event; | ||||
| 
 | ||||
| 	hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE; | ||||
| 	if (flags & PERF_EF_START) | ||||
| @ -345,25 +324,25 @@ out: | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static struct pmu pmu; | ||||
| 
 | ||||
| static int | ||||
| validate_event(struct cpu_hw_events *cpuc, | ||||
| validate_event(struct pmu_hw_events *hw_events, | ||||
| 	       struct perf_event *event) | ||||
| { | ||||
| 	struct arm_pmu *armpmu = to_arm_pmu(event->pmu); | ||||
| 	struct hw_perf_event fake_event = event->hw; | ||||
| 	struct pmu *leader_pmu = event->group_leader->pmu; | ||||
| 
 | ||||
| 	if (event->pmu != &pmu || event->state <= PERF_EVENT_STATE_OFF) | ||||
| 	if (event->pmu != leader_pmu || event->state <= PERF_EVENT_STATE_OFF) | ||||
| 		return 1; | ||||
| 
 | ||||
| 	return armpmu->get_event_idx(cpuc, &fake_event) >= 0; | ||||
| 	return armpmu->get_event_idx(hw_events, &fake_event) >= 0; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| validate_group(struct perf_event *event) | ||||
| { | ||||
| 	struct perf_event *sibling, *leader = event->group_leader; | ||||
| 	struct cpu_hw_events fake_pmu; | ||||
| 	struct pmu_hw_events fake_pmu; | ||||
| 
 | ||||
| 	memset(&fake_pmu, 0, sizeof(fake_pmu)); | ||||
| 
 | ||||
| @ -383,110 +362,119 @@ validate_group(struct perf_event *event) | ||||
| 
 | ||||
| static irqreturn_t armpmu_platform_irq(int irq, void *dev) | ||||
| { | ||||
| 	struct arm_pmu_platdata *plat = dev_get_platdata(&pmu_device->dev); | ||||
| 	struct arm_pmu *armpmu = (struct arm_pmu *) dev; | ||||
| 	struct platform_device *plat_device = armpmu->plat_device; | ||||
| 	struct arm_pmu_platdata *plat = dev_get_platdata(&plat_device->dev); | ||||
| 
 | ||||
| 	return plat->handle_irq(irq, dev, armpmu->handle_irq); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| armpmu_release_hardware(struct arm_pmu *armpmu) | ||||
| { | ||||
| 	int i, irq, irqs; | ||||
| 	struct platform_device *pmu_device = armpmu->plat_device; | ||||
| 
 | ||||
| 	irqs = min(pmu_device->num_resources, num_possible_cpus()); | ||||
| 
 | ||||
| 	for (i = 0; i < irqs; ++i) { | ||||
| 		if (!cpumask_test_and_clear_cpu(i, &armpmu->active_irqs)) | ||||
| 			continue; | ||||
| 		irq = platform_get_irq(pmu_device, i); | ||||
| 		if (irq >= 0) | ||||
| 			free_irq(irq, armpmu); | ||||
| 	} | ||||
| 
 | ||||
| 	release_pmu(armpmu->type); | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| armpmu_reserve_hardware(void) | ||||
| armpmu_reserve_hardware(struct arm_pmu *armpmu) | ||||
| { | ||||
| 	struct arm_pmu_platdata *plat; | ||||
| 	irq_handler_t handle_irq; | ||||
| 	int i, err = -ENODEV, irq; | ||||
| 	int i, err, irq, irqs; | ||||
| 	struct platform_device *pmu_device = armpmu->plat_device; | ||||
| 
 | ||||
| 	pmu_device = reserve_pmu(ARM_PMU_DEVICE_CPU); | ||||
| 	if (IS_ERR(pmu_device)) { | ||||
| 	err = reserve_pmu(armpmu->type); | ||||
| 	if (err) { | ||||
| 		pr_warning("unable to reserve pmu\n"); | ||||
| 		return PTR_ERR(pmu_device); | ||||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
| 	init_pmu(ARM_PMU_DEVICE_CPU); | ||||
| 
 | ||||
| 	plat = dev_get_platdata(&pmu_device->dev); | ||||
| 	if (plat && plat->handle_irq) | ||||
| 		handle_irq = armpmu_platform_irq; | ||||
| 	else | ||||
| 		handle_irq = armpmu->handle_irq; | ||||
| 
 | ||||
| 	if (pmu_device->num_resources < 1) { | ||||
| 	irqs = min(pmu_device->num_resources, num_possible_cpus()); | ||||
| 	if (irqs < 1) { | ||||
| 		pr_err("no irqs for PMUs defined\n"); | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = 0; i < pmu_device->num_resources; ++i) { | ||||
| 	for (i = 0; i < irqs; ++i) { | ||||
| 		err = 0; | ||||
| 		irq = platform_get_irq(pmu_device, i); | ||||
| 		if (irq < 0) | ||||
| 			continue; | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * If we have a single PMU interrupt that we can't shift, | ||||
| 		 * assume that we're running on a uniprocessor machine and | ||||
| 		 * continue. Otherwise, continue without this interrupt. | ||||
| 		 */ | ||||
| 		if (irq_set_affinity(irq, cpumask_of(i)) && irqs > 1) { | ||||
| 			pr_warning("unable to set irq affinity (irq=%d, cpu=%u)\n", | ||||
| 				    irq, i); | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		err = request_irq(irq, handle_irq, | ||||
| 				  IRQF_DISABLED | IRQF_NOBALANCING, | ||||
| 				  "armpmu", NULL); | ||||
| 				  "arm-pmu", armpmu); | ||||
| 		if (err) { | ||||
| 			pr_warning("unable to request IRQ%d for ARM perf " | ||||
| 				"counters\n", irq); | ||||
| 			break; | ||||
| 			pr_err("unable to request IRQ%d for ARM PMU counters\n", | ||||
| 				irq); | ||||
| 			armpmu_release_hardware(armpmu); | ||||
| 			return err; | ||||
| 		} | ||||
| 
 | ||||
| 		cpumask_set_cpu(i, &armpmu->active_irqs); | ||||
| 	} | ||||
| 
 | ||||
| 	if (err) { | ||||
| 		for (i = i - 1; i >= 0; --i) { | ||||
| 			irq = platform_get_irq(pmu_device, i); | ||||
| 			if (irq >= 0) | ||||
| 				free_irq(irq, NULL); | ||||
| 		} | ||||
| 		release_pmu(ARM_PMU_DEVICE_CPU); | ||||
| 		pmu_device = NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	return err; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| armpmu_release_hardware(void) | ||||
| { | ||||
| 	int i, irq; | ||||
| 
 | ||||
| 	for (i = pmu_device->num_resources - 1; i >= 0; --i) { | ||||
| 		irq = platform_get_irq(pmu_device, i); | ||||
| 		if (irq >= 0) | ||||
| 			free_irq(irq, NULL); | ||||
| 	} | ||||
| 	armpmu->stop(); | ||||
| 
 | ||||
| 	release_pmu(ARM_PMU_DEVICE_CPU); | ||||
| 	pmu_device = NULL; | ||||
| } | ||||
| 
 | ||||
| static atomic_t active_events = ATOMIC_INIT(0); | ||||
| static DEFINE_MUTEX(pmu_reserve_mutex); | ||||
| 
 | ||||
| static void | ||||
| hw_perf_event_destroy(struct perf_event *event) | ||||
| { | ||||
| 	if (atomic_dec_and_mutex_lock(&active_events, &pmu_reserve_mutex)) { | ||||
| 		armpmu_release_hardware(); | ||||
| 		mutex_unlock(&pmu_reserve_mutex); | ||||
| 	struct arm_pmu *armpmu = to_arm_pmu(event->pmu); | ||||
| 	atomic_t *active_events	 = &armpmu->active_events; | ||||
| 	struct mutex *pmu_reserve_mutex = &armpmu->reserve_mutex; | ||||
| 
 | ||||
| 	if (atomic_dec_and_mutex_lock(active_events, pmu_reserve_mutex)) { | ||||
| 		armpmu_release_hardware(armpmu); | ||||
| 		mutex_unlock(pmu_reserve_mutex); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| event_requires_mode_exclusion(struct perf_event_attr *attr) | ||||
| { | ||||
| 	return attr->exclude_idle || attr->exclude_user || | ||||
| 	       attr->exclude_kernel || attr->exclude_hv; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| __hw_perf_event_init(struct perf_event *event) | ||||
| { | ||||
| 	struct arm_pmu *armpmu = to_arm_pmu(event->pmu); | ||||
| 	struct hw_perf_event *hwc = &event->hw; | ||||
| 	int mapping, err; | ||||
| 
 | ||||
| 	/* Decode the generic type into an ARM event identifier. */ | ||||
| 	if (PERF_TYPE_HARDWARE == event->attr.type) { | ||||
| 		mapping = armpmu_map_event(event->attr.config); | ||||
| 	} else if (PERF_TYPE_HW_CACHE == event->attr.type) { | ||||
| 		mapping = armpmu_map_cache_event(event->attr.config); | ||||
| 	} else if (PERF_TYPE_RAW == event->attr.type) { | ||||
| 		mapping = armpmu_map_raw_event(event->attr.config); | ||||
| 	} else { | ||||
| 		pr_debug("event type %x not supported\n", event->attr.type); | ||||
| 		return -EOPNOTSUPP; | ||||
| 	} | ||||
| 	mapping = armpmu->map_event(event); | ||||
| 
 | ||||
| 	if (mapping < 0) { | ||||
| 		pr_debug("event %x:%llx not supported\n", event->attr.type, | ||||
| @ -495,34 +483,31 @@ __hw_perf_event_init(struct perf_event *event) | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Check whether we need to exclude the counter from certain modes. | ||||
| 	 * The ARM performance counters are on all of the time so if someone | ||||
| 	 * has asked us for some excludes then we have to fail. | ||||
| 	 * We don't assign an index until we actually place the event onto | ||||
| 	 * hardware. Use -1 to signify that we haven't decided where to put it | ||||
| 	 * yet. For SMP systems, each core has it's own PMU so we can't do any | ||||
| 	 * clever allocation or constraints checking at this point. | ||||
| 	 */ | ||||
| 	if (event->attr.exclude_kernel || event->attr.exclude_user || | ||||
| 	    event->attr.exclude_hv || event->attr.exclude_idle) { | ||||
| 	hwc->idx		= -1; | ||||
| 	hwc->config_base	= 0; | ||||
| 	hwc->config		= 0; | ||||
| 	hwc->event_base		= 0; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Check whether we need to exclude the counter from certain modes. | ||||
| 	 */ | ||||
| 	if ((!armpmu->set_event_filter || | ||||
| 	     armpmu->set_event_filter(hwc, &event->attr)) && | ||||
| 	     event_requires_mode_exclusion(&event->attr)) { | ||||
| 		pr_debug("ARM performance counters do not support " | ||||
| 			 "mode exclusion\n"); | ||||
| 		return -EPERM; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * We don't assign an index until we actually place the event onto | ||||
| 	 * hardware. Use -1 to signify that we haven't decided where to put it | ||||
| 	 * yet. For SMP systems, each core has it's own PMU so we can't do any | ||||
| 	 * clever allocation or constraints checking at this point. | ||||
| 	 * Store the event encoding into the config_base field. | ||||
| 	 */ | ||||
| 	hwc->idx = -1; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Store the event encoding into the config_base field. config and | ||||
| 	 * event_base are unused as the only 2 things we need to know are | ||||
| 	 * the event mapping and the counter to use. The counter to use is | ||||
| 	 * also the indx and the config_base is the event type. | ||||
| 	 */ | ||||
| 	hwc->config_base	    = (unsigned long)mapping; | ||||
| 	hwc->config		    = 0; | ||||
| 	hwc->event_base		    = 0; | ||||
| 	hwc->config_base	    |= (unsigned long)mapping; | ||||
| 
 | ||||
| 	if (!hwc->sample_period) { | ||||
| 		hwc->sample_period  = armpmu->max_period; | ||||
| @ -542,32 +527,23 @@ __hw_perf_event_init(struct perf_event *event) | ||||
| 
 | ||||
| static int armpmu_event_init(struct perf_event *event) | ||||
| { | ||||
| 	struct arm_pmu *armpmu = to_arm_pmu(event->pmu); | ||||
| 	int err = 0; | ||||
| 	atomic_t *active_events = &armpmu->active_events; | ||||
| 
 | ||||
| 	switch (event->attr.type) { | ||||
| 	case PERF_TYPE_RAW: | ||||
| 	case PERF_TYPE_HARDWARE: | ||||
| 	case PERF_TYPE_HW_CACHE: | ||||
| 		break; | ||||
| 
 | ||||
| 	default: | ||||
| 	if (armpmu->map_event(event) == -ENOENT) | ||||
| 		return -ENOENT; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!armpmu) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	event->destroy = hw_perf_event_destroy; | ||||
| 
 | ||||
| 	if (!atomic_inc_not_zero(&active_events)) { | ||||
| 		mutex_lock(&pmu_reserve_mutex); | ||||
| 		if (atomic_read(&active_events) == 0) { | ||||
| 			err = armpmu_reserve_hardware(); | ||||
| 		} | ||||
| 	if (!atomic_inc_not_zero(active_events)) { | ||||
| 		mutex_lock(&armpmu->reserve_mutex); | ||||
| 		if (atomic_read(active_events) == 0) | ||||
| 			err = armpmu_reserve_hardware(armpmu); | ||||
| 
 | ||||
| 		if (!err) | ||||
| 			atomic_inc(&active_events); | ||||
| 		mutex_unlock(&pmu_reserve_mutex); | ||||
| 			atomic_inc(active_events); | ||||
| 		mutex_unlock(&armpmu->reserve_mutex); | ||||
| 	} | ||||
| 
 | ||||
| 	if (err) | ||||
| @ -582,22 +558,9 @@ static int armpmu_event_init(struct perf_event *event) | ||||
| 
 | ||||
| static void armpmu_enable(struct pmu *pmu) | ||||
| { | ||||
| 	/* Enable all of the perf events on hardware. */ | ||||
| 	int idx, enabled = 0; | ||||
| 	struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | ||||
| 
 | ||||
| 	if (!armpmu) | ||||
| 		return; | ||||
| 
 | ||||
| 	for (idx = 0; idx <= armpmu->num_events; ++idx) { | ||||
| 		struct perf_event *event = cpuc->events[idx]; | ||||
| 
 | ||||
| 		if (!event) | ||||
| 			continue; | ||||
| 
 | ||||
| 		armpmu->enable(&event->hw, idx); | ||||
| 		enabled = 1; | ||||
| 	} | ||||
| 	struct arm_pmu *armpmu = to_arm_pmu(pmu); | ||||
| 	struct pmu_hw_events *hw_events = armpmu->get_hw_events(); | ||||
| 	int enabled = bitmap_weight(hw_events->used_mask, armpmu->num_events); | ||||
| 
 | ||||
| 	if (enabled) | ||||
| 		armpmu->start(); | ||||
| @ -605,20 +568,32 @@ static void armpmu_enable(struct pmu *pmu) | ||||
| 
 | ||||
| static void armpmu_disable(struct pmu *pmu) | ||||
| { | ||||
| 	if (armpmu) | ||||
| 		armpmu->stop(); | ||||
| 	struct arm_pmu *armpmu = to_arm_pmu(pmu); | ||||
| 	armpmu->stop(); | ||||
| } | ||||
| 
 | ||||
| static struct pmu pmu = { | ||||
| 	.pmu_enable	= armpmu_enable, | ||||
| 	.pmu_disable	= armpmu_disable, | ||||
| 	.event_init	= armpmu_event_init, | ||||
| 	.add		= armpmu_add, | ||||
| 	.del		= armpmu_del, | ||||
| 	.start		= armpmu_start, | ||||
| 	.stop		= armpmu_stop, | ||||
| 	.read		= armpmu_read, | ||||
| }; | ||||
| static void __init armpmu_init(struct arm_pmu *armpmu) | ||||
| { | ||||
| 	atomic_set(&armpmu->active_events, 0); | ||||
| 	mutex_init(&armpmu->reserve_mutex); | ||||
| 
 | ||||
| 	armpmu->pmu = (struct pmu) { | ||||
| 		.pmu_enable	= armpmu_enable, | ||||
| 		.pmu_disable	= armpmu_disable, | ||||
| 		.event_init	= armpmu_event_init, | ||||
| 		.add		= armpmu_add, | ||||
| 		.del		= armpmu_del, | ||||
| 		.start		= armpmu_start, | ||||
| 		.stop		= armpmu_stop, | ||||
| 		.read		= armpmu_read, | ||||
| 	}; | ||||
| } | ||||
| 
 | ||||
| int __init armpmu_register(struct arm_pmu *armpmu, char *name, int type) | ||||
| { | ||||
| 	armpmu_init(armpmu); | ||||
| 	return perf_pmu_register(&armpmu->pmu, name, type); | ||||
| } | ||||
| 
 | ||||
| /* Include the PMU-specific implementations. */ | ||||
| #include "perf_event_xscale.c" | ||||
| @ -630,14 +605,72 @@ static struct pmu pmu = { | ||||
|  * This requires SMP to be available, so exists as a separate initcall. | ||||
|  */ | ||||
| static int __init | ||||
| armpmu_reset(void) | ||||
| cpu_pmu_reset(void) | ||||
| { | ||||
| 	if (armpmu && armpmu->reset) | ||||
| 		return on_each_cpu(armpmu->reset, NULL, 1); | ||||
| 	if (cpu_pmu && cpu_pmu->reset) | ||||
| 		return on_each_cpu(cpu_pmu->reset, NULL, 1); | ||||
| 	return 0; | ||||
| } | ||||
| arch_initcall(armpmu_reset); | ||||
| arch_initcall(cpu_pmu_reset); | ||||
| 
 | ||||
| /*
 | ||||
|  * PMU platform driver and devicetree bindings. | ||||
|  */ | ||||
| static struct of_device_id armpmu_of_device_ids[] = { | ||||
| 	{.compatible = "arm,cortex-a9-pmu"}, | ||||
| 	{.compatible = "arm,cortex-a8-pmu"}, | ||||
| 	{.compatible = "arm,arm1136-pmu"}, | ||||
| 	{.compatible = "arm,arm1176-pmu"}, | ||||
| 	{}, | ||||
| }; | ||||
| 
 | ||||
| static struct platform_device_id armpmu_plat_device_ids[] = { | ||||
| 	{.name = "arm-pmu"}, | ||||
| 	{}, | ||||
| }; | ||||
| 
 | ||||
| static int __devinit armpmu_device_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	cpu_pmu->plat_device = pdev; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct platform_driver armpmu_driver = { | ||||
| 	.driver		= { | ||||
| 		.name	= "arm-pmu", | ||||
| 		.of_match_table = armpmu_of_device_ids, | ||||
| 	}, | ||||
| 	.probe		= armpmu_device_probe, | ||||
| 	.id_table	= armpmu_plat_device_ids, | ||||
| }; | ||||
| 
 | ||||
| static int __init register_pmu_driver(void) | ||||
| { | ||||
| 	return platform_driver_register(&armpmu_driver); | ||||
| } | ||||
| device_initcall(register_pmu_driver); | ||||
| 
 | ||||
| static struct pmu_hw_events *armpmu_get_cpu_events(void) | ||||
| { | ||||
| 	return &__get_cpu_var(cpu_hw_events); | ||||
| } | ||||
| 
 | ||||
| static void __init cpu_pmu_init(struct arm_pmu *armpmu) | ||||
| { | ||||
| 	int cpu; | ||||
| 	for_each_possible_cpu(cpu) { | ||||
| 		struct pmu_hw_events *events = &per_cpu(cpu_hw_events, cpu); | ||||
| 		events->events = per_cpu(hw_events, cpu); | ||||
| 		events->used_mask = per_cpu(used_mask, cpu); | ||||
| 		raw_spin_lock_init(&events->pmu_lock); | ||||
| 	} | ||||
| 	armpmu->get_hw_events = armpmu_get_cpu_events; | ||||
| 	armpmu->type = ARM_PMU_DEVICE_CPU; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * CPU PMU identification and registration. | ||||
|  */ | ||||
| static int __init | ||||
| init_hw_perf_events(void) | ||||
| { | ||||
| @ -651,22 +684,22 @@ init_hw_perf_events(void) | ||||
| 		case 0xB360:	/* ARM1136 */ | ||||
| 		case 0xB560:	/* ARM1156 */ | ||||
| 		case 0xB760:	/* ARM1176 */ | ||||
| 			armpmu = armv6pmu_init(); | ||||
| 			cpu_pmu = armv6pmu_init(); | ||||
| 			break; | ||||
| 		case 0xB020:	/* ARM11mpcore */ | ||||
| 			armpmu = armv6mpcore_pmu_init(); | ||||
| 			cpu_pmu = armv6mpcore_pmu_init(); | ||||
| 			break; | ||||
| 		case 0xC080:	/* Cortex-A8 */ | ||||
| 			armpmu = armv7_a8_pmu_init(); | ||||
| 			cpu_pmu = armv7_a8_pmu_init(); | ||||
| 			break; | ||||
| 		case 0xC090:	/* Cortex-A9 */ | ||||
| 			armpmu = armv7_a9_pmu_init(); | ||||
| 			cpu_pmu = armv7_a9_pmu_init(); | ||||
| 			break; | ||||
| 		case 0xC050:	/* Cortex-A5 */ | ||||
| 			armpmu = armv7_a5_pmu_init(); | ||||
| 			cpu_pmu = armv7_a5_pmu_init(); | ||||
| 			break; | ||||
| 		case 0xC0F0:	/* Cortex-A15 */ | ||||
| 			armpmu = armv7_a15_pmu_init(); | ||||
| 			cpu_pmu = armv7_a15_pmu_init(); | ||||
| 			break; | ||||
| 		} | ||||
| 	/* Intel CPUs [xscale]. */ | ||||
| @ -674,23 +707,23 @@ init_hw_perf_events(void) | ||||
| 		part_number = (cpuid >> 13) & 0x7; | ||||
| 		switch (part_number) { | ||||
| 		case 1: | ||||
| 			armpmu = xscale1pmu_init(); | ||||
| 			cpu_pmu = xscale1pmu_init(); | ||||
| 			break; | ||||
| 		case 2: | ||||
| 			armpmu = xscale2pmu_init(); | ||||
| 			cpu_pmu = xscale2pmu_init(); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (armpmu) { | ||||
| 	if (cpu_pmu) { | ||||
| 		pr_info("enabled with %s PMU driver, %d counters available\n", | ||||
| 			armpmu->name, armpmu->num_events); | ||||
| 			cpu_pmu->name, cpu_pmu->num_events); | ||||
| 		cpu_pmu_init(cpu_pmu); | ||||
| 		armpmu_register(cpu_pmu, "cpu", PERF_TYPE_RAW); | ||||
| 	} else { | ||||
| 		pr_info("no hardware support available\n"); | ||||
| 	} | ||||
| 
 | ||||
| 	perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| early_initcall(init_hw_perf_events); | ||||
|  | ||||
| @ -54,7 +54,7 @@ enum armv6_perf_types { | ||||
| }; | ||||
| 
 | ||||
| enum armv6_counters { | ||||
| 	ARMV6_CYCLE_COUNTER = 1, | ||||
| 	ARMV6_CYCLE_COUNTER = 0, | ||||
| 	ARMV6_COUNTER0, | ||||
| 	ARMV6_COUNTER1, | ||||
| }; | ||||
| @ -433,6 +433,7 @@ armv6pmu_enable_event(struct hw_perf_event *hwc, | ||||
| 		      int idx) | ||||
| { | ||||
| 	unsigned long val, mask, evt, flags; | ||||
| 	struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | ||||
| 
 | ||||
| 	if (ARMV6_CYCLE_COUNTER == idx) { | ||||
| 		mask	= 0; | ||||
| @ -454,12 +455,29 @@ armv6pmu_enable_event(struct hw_perf_event *hwc, | ||||
| 	 * Mask out the current event and set the counter to count the event | ||||
| 	 * that we're interested in. | ||||
| 	 */ | ||||
| 	raw_spin_lock_irqsave(&pmu_lock, flags); | ||||
| 	raw_spin_lock_irqsave(&events->pmu_lock, flags); | ||||
| 	val = armv6_pmcr_read(); | ||||
| 	val &= ~mask; | ||||
| 	val |= evt; | ||||
| 	armv6_pmcr_write(val); | ||||
| 	raw_spin_unlock_irqrestore(&pmu_lock, flags); | ||||
| 	raw_spin_unlock_irqrestore(&events->pmu_lock, flags); | ||||
| } | ||||
| 
 | ||||
| static int counter_is_active(unsigned long pmcr, int idx) | ||||
| { | ||||
| 	unsigned long mask = 0; | ||||
| 	if (idx == ARMV6_CYCLE_COUNTER) | ||||
| 		mask = ARMV6_PMCR_CCOUNT_IEN; | ||||
| 	else if (idx == ARMV6_COUNTER0) | ||||
| 		mask = ARMV6_PMCR_COUNT0_IEN; | ||||
| 	else if (idx == ARMV6_COUNTER1) | ||||
| 		mask = ARMV6_PMCR_COUNT1_IEN; | ||||
| 
 | ||||
| 	if (mask) | ||||
| 		return pmcr & mask; | ||||
| 
 | ||||
| 	WARN_ONCE(1, "invalid counter number (%d)\n", idx); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static irqreturn_t | ||||
| @ -468,7 +486,7 @@ armv6pmu_handle_irq(int irq_num, | ||||
| { | ||||
| 	unsigned long pmcr = armv6_pmcr_read(); | ||||
| 	struct perf_sample_data data; | ||||
| 	struct cpu_hw_events *cpuc; | ||||
| 	struct pmu_hw_events *cpuc; | ||||
| 	struct pt_regs *regs; | ||||
| 	int idx; | ||||
| 
 | ||||
| @ -487,11 +505,11 @@ armv6pmu_handle_irq(int irq_num, | ||||
| 	perf_sample_data_init(&data, 0); | ||||
| 
 | ||||
| 	cpuc = &__get_cpu_var(cpu_hw_events); | ||||
| 	for (idx = 0; idx <= armpmu->num_events; ++idx) { | ||||
| 	for (idx = 0; idx < cpu_pmu->num_events; ++idx) { | ||||
| 		struct perf_event *event = cpuc->events[idx]; | ||||
| 		struct hw_perf_event *hwc; | ||||
| 
 | ||||
| 		if (!test_bit(idx, cpuc->active_mask)) | ||||
| 		if (!counter_is_active(pmcr, idx)) | ||||
| 			continue; | ||||
| 
 | ||||
| 		/*
 | ||||
| @ -508,7 +526,7 @@ armv6pmu_handle_irq(int irq_num, | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (perf_event_overflow(event, &data, regs)) | ||||
| 			armpmu->disable(hwc, idx); | ||||
| 			cpu_pmu->disable(hwc, idx); | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| @ -527,28 +545,30 @@ static void | ||||
| armv6pmu_start(void) | ||||
| { | ||||
| 	unsigned long flags, val; | ||||
| 	struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | ||||
| 
 | ||||
| 	raw_spin_lock_irqsave(&pmu_lock, flags); | ||||
| 	raw_spin_lock_irqsave(&events->pmu_lock, flags); | ||||
| 	val = armv6_pmcr_read(); | ||||
| 	val |= ARMV6_PMCR_ENABLE; | ||||
| 	armv6_pmcr_write(val); | ||||
| 	raw_spin_unlock_irqrestore(&pmu_lock, flags); | ||||
| 	raw_spin_unlock_irqrestore(&events->pmu_lock, flags); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| armv6pmu_stop(void) | ||||
| { | ||||
| 	unsigned long flags, val; | ||||
| 	struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | ||||
| 
 | ||||
| 	raw_spin_lock_irqsave(&pmu_lock, flags); | ||||
| 	raw_spin_lock_irqsave(&events->pmu_lock, flags); | ||||
| 	val = armv6_pmcr_read(); | ||||
| 	val &= ~ARMV6_PMCR_ENABLE; | ||||
| 	armv6_pmcr_write(val); | ||||
| 	raw_spin_unlock_irqrestore(&pmu_lock, flags); | ||||
| 	raw_spin_unlock_irqrestore(&events->pmu_lock, flags); | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| armv6pmu_get_event_idx(struct cpu_hw_events *cpuc, | ||||
| armv6pmu_get_event_idx(struct pmu_hw_events *cpuc, | ||||
| 		       struct hw_perf_event *event) | ||||
| { | ||||
| 	/* Always place a cycle counter into the cycle counter. */ | ||||
| @ -578,6 +598,7 @@ armv6pmu_disable_event(struct hw_perf_event *hwc, | ||||
| 		       int idx) | ||||
| { | ||||
| 	unsigned long val, mask, evt, flags; | ||||
| 	struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | ||||
| 
 | ||||
| 	if (ARMV6_CYCLE_COUNTER == idx) { | ||||
| 		mask	= ARMV6_PMCR_CCOUNT_IEN; | ||||
| @ -598,12 +619,12 @@ armv6pmu_disable_event(struct hw_perf_event *hwc, | ||||
| 	 * of ETM bus signal assertion cycles. The external reporting should | ||||
| 	 * be disabled and so this should never increment. | ||||
| 	 */ | ||||
| 	raw_spin_lock_irqsave(&pmu_lock, flags); | ||||
| 	raw_spin_lock_irqsave(&events->pmu_lock, flags); | ||||
| 	val = armv6_pmcr_read(); | ||||
| 	val &= ~mask; | ||||
| 	val |= evt; | ||||
| 	armv6_pmcr_write(val); | ||||
| 	raw_spin_unlock_irqrestore(&pmu_lock, flags); | ||||
| 	raw_spin_unlock_irqrestore(&events->pmu_lock, flags); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| @ -611,6 +632,7 @@ armv6mpcore_pmu_disable_event(struct hw_perf_event *hwc, | ||||
| 			      int idx) | ||||
| { | ||||
| 	unsigned long val, mask, flags, evt = 0; | ||||
| 	struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | ||||
| 
 | ||||
| 	if (ARMV6_CYCLE_COUNTER == idx) { | ||||
| 		mask	= ARMV6_PMCR_CCOUNT_IEN; | ||||
| @ -627,15 +649,21 @@ armv6mpcore_pmu_disable_event(struct hw_perf_event *hwc, | ||||
| 	 * Unlike UP ARMv6, we don't have a way of stopping the counters. We | ||||
| 	 * simply disable the interrupt reporting. | ||||
| 	 */ | ||||
| 	raw_spin_lock_irqsave(&pmu_lock, flags); | ||||
| 	raw_spin_lock_irqsave(&events->pmu_lock, flags); | ||||
| 	val = armv6_pmcr_read(); | ||||
| 	val &= ~mask; | ||||
| 	val |= evt; | ||||
| 	armv6_pmcr_write(val); | ||||
| 	raw_spin_unlock_irqrestore(&pmu_lock, flags); | ||||
| 	raw_spin_unlock_irqrestore(&events->pmu_lock, flags); | ||||
| } | ||||
| 
 | ||||
| static const struct arm_pmu armv6pmu = { | ||||
| static int armv6_map_event(struct perf_event *event) | ||||
| { | ||||
| 	return map_cpu_event(event, &armv6_perf_map, | ||||
| 				&armv6_perf_cache_map, 0xFF); | ||||
| } | ||||
| 
 | ||||
| static struct arm_pmu armv6pmu = { | ||||
| 	.id			= ARM_PERF_PMU_ID_V6, | ||||
| 	.name			= "v6", | ||||
| 	.handle_irq		= armv6pmu_handle_irq, | ||||
| @ -646,14 +674,12 @@ static const struct arm_pmu armv6pmu = { | ||||
| 	.get_event_idx		= armv6pmu_get_event_idx, | ||||
| 	.start			= armv6pmu_start, | ||||
| 	.stop			= armv6pmu_stop, | ||||
| 	.cache_map		= &armv6_perf_cache_map, | ||||
| 	.event_map		= &armv6_perf_map, | ||||
| 	.raw_event_mask		= 0xFF, | ||||
| 	.map_event		= armv6_map_event, | ||||
| 	.num_events		= 3, | ||||
| 	.max_period		= (1LLU << 32) - 1, | ||||
| }; | ||||
| 
 | ||||
| static const struct arm_pmu *__init armv6pmu_init(void) | ||||
| static struct arm_pmu *__init armv6pmu_init(void) | ||||
| { | ||||
| 	return &armv6pmu; | ||||
| } | ||||
| @ -665,7 +691,14 @@ static const struct arm_pmu *__init armv6pmu_init(void) | ||||
|  * disable the interrupt reporting and update the event. When unthrottling we | ||||
|  * reset the period and enable the interrupt reporting. | ||||
|  */ | ||||
| static const struct arm_pmu armv6mpcore_pmu = { | ||||
| 
 | ||||
| static int armv6mpcore_map_event(struct perf_event *event) | ||||
| { | ||||
| 	return map_cpu_event(event, &armv6mpcore_perf_map, | ||||
| 				&armv6mpcore_perf_cache_map, 0xFF); | ||||
| } | ||||
| 
 | ||||
| static struct arm_pmu armv6mpcore_pmu = { | ||||
| 	.id			= ARM_PERF_PMU_ID_V6MP, | ||||
| 	.name			= "v6mpcore", | ||||
| 	.handle_irq		= armv6pmu_handle_irq, | ||||
| @ -676,24 +709,22 @@ static const struct arm_pmu armv6mpcore_pmu = { | ||||
| 	.get_event_idx		= armv6pmu_get_event_idx, | ||||
| 	.start			= armv6pmu_start, | ||||
| 	.stop			= armv6pmu_stop, | ||||
| 	.cache_map		= &armv6mpcore_perf_cache_map, | ||||
| 	.event_map		= &armv6mpcore_perf_map, | ||||
| 	.raw_event_mask		= 0xFF, | ||||
| 	.map_event		= armv6mpcore_map_event, | ||||
| 	.num_events		= 3, | ||||
| 	.max_period		= (1LLU << 32) - 1, | ||||
| }; | ||||
| 
 | ||||
| static const struct arm_pmu *__init armv6mpcore_pmu_init(void) | ||||
| static struct arm_pmu *__init armv6mpcore_pmu_init(void) | ||||
| { | ||||
| 	return &armv6mpcore_pmu; | ||||
| } | ||||
| #else | ||||
| static const struct arm_pmu *__init armv6pmu_init(void) | ||||
| static struct arm_pmu *__init armv6pmu_init(void) | ||||
| { | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static const struct arm_pmu *__init armv6mpcore_pmu_init(void) | ||||
| static struct arm_pmu *__init armv6mpcore_pmu_init(void) | ||||
| { | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| @ -17,6 +17,9 @@ | ||||
|  */ | ||||
| 
 | ||||
| #ifdef CONFIG_CPU_V7 | ||||
| 
 | ||||
| static struct arm_pmu armv7pmu; | ||||
| 
 | ||||
| /*
 | ||||
|  * Common ARMv7 event types | ||||
|  * | ||||
| @ -676,24 +679,25 @@ static const unsigned armv7_a15_perf_cache_map[PERF_COUNT_HW_CACHE_MAX] | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * Perf Events counters | ||||
|  * Perf Events' indices | ||||
|  */ | ||||
| enum armv7_counters { | ||||
| 	ARMV7_CYCLE_COUNTER		= 1,	/* Cycle counter */ | ||||
| 	ARMV7_COUNTER0			= 2,	/* First event counter */ | ||||
| }; | ||||
| #define	ARMV7_IDX_CYCLE_COUNTER	0 | ||||
| #define	ARMV7_IDX_COUNTER0	1 | ||||
| #define	ARMV7_IDX_COUNTER_LAST	(ARMV7_IDX_CYCLE_COUNTER + cpu_pmu->num_events - 1) | ||||
| 
 | ||||
| /*
 | ||||
|  * The cycle counter is ARMV7_CYCLE_COUNTER. | ||||
|  * The first event counter is ARMV7_COUNTER0. | ||||
|  * The last event counter is (ARMV7_COUNTER0 + armpmu->num_events - 1). | ||||
|  */ | ||||
| #define	ARMV7_COUNTER_LAST	(ARMV7_COUNTER0 + armpmu->num_events - 1) | ||||
| #define	ARMV7_MAX_COUNTERS	32 | ||||
| #define	ARMV7_COUNTER_MASK	(ARMV7_MAX_COUNTERS - 1) | ||||
| 
 | ||||
| /*
 | ||||
|  * ARMv7 low level PMNC access | ||||
|  */ | ||||
| 
 | ||||
| /*
 | ||||
|  * Perf Event to low level counters mapping | ||||
|  */ | ||||
| #define	ARMV7_IDX_TO_COUNTER(x)	\ | ||||
| 	(((x) - ARMV7_IDX_COUNTER0) & ARMV7_COUNTER_MASK) | ||||
| 
 | ||||
| /*
 | ||||
|  * Per-CPU PMNC: config reg | ||||
|  */ | ||||
| @ -707,104 +711,77 @@ enum armv7_counters { | ||||
| #define	ARMV7_PMNC_N_MASK	0x1f | ||||
| #define	ARMV7_PMNC_MASK		0x3f	 /* Mask for writable bits */ | ||||
| 
 | ||||
| /*
 | ||||
|  * Available counters | ||||
|  */ | ||||
| #define ARMV7_CNT0		0	/* First event counter */ | ||||
| #define ARMV7_CCNT		31	/* Cycle counter */ | ||||
| 
 | ||||
| /* Perf Event to low level counters mapping */ | ||||
| #define ARMV7_EVENT_CNT_TO_CNTx	(ARMV7_COUNTER0 - ARMV7_CNT0) | ||||
| 
 | ||||
| /*
 | ||||
|  * CNTENS: counters enable reg | ||||
|  */ | ||||
| #define ARMV7_CNTENS_P(idx)	(1 << (idx - ARMV7_EVENT_CNT_TO_CNTx)) | ||||
| #define ARMV7_CNTENS_C		(1 << ARMV7_CCNT) | ||||
| 
 | ||||
| /*
 | ||||
|  * CNTENC: counters disable reg | ||||
|  */ | ||||
| #define ARMV7_CNTENC_P(idx)	(1 << (idx - ARMV7_EVENT_CNT_TO_CNTx)) | ||||
| #define ARMV7_CNTENC_C		(1 << ARMV7_CCNT) | ||||
| 
 | ||||
| /*
 | ||||
|  * INTENS: counters overflow interrupt enable reg | ||||
|  */ | ||||
| #define ARMV7_INTENS_P(idx)	(1 << (idx - ARMV7_EVENT_CNT_TO_CNTx)) | ||||
| #define ARMV7_INTENS_C		(1 << ARMV7_CCNT) | ||||
| 
 | ||||
| /*
 | ||||
|  * INTENC: counters overflow interrupt disable reg | ||||
|  */ | ||||
| #define ARMV7_INTENC_P(idx)	(1 << (idx - ARMV7_EVENT_CNT_TO_CNTx)) | ||||
| #define ARMV7_INTENC_C		(1 << ARMV7_CCNT) | ||||
| 
 | ||||
| /*
 | ||||
|  * EVTSEL: Event selection reg | ||||
|  */ | ||||
| #define	ARMV7_EVTSEL_MASK	0xff		/* Mask for writable bits */ | ||||
| 
 | ||||
| /*
 | ||||
|  * SELECT: Counter selection reg | ||||
|  */ | ||||
| #define	ARMV7_SELECT_MASK	0x1f		/* Mask for writable bits */ | ||||
| 
 | ||||
| /*
 | ||||
|  * FLAG: counters overflow flag status reg | ||||
|  */ | ||||
| #define ARMV7_FLAG_P(idx)	(1 << (idx - ARMV7_EVENT_CNT_TO_CNTx)) | ||||
| #define ARMV7_FLAG_C		(1 << ARMV7_CCNT) | ||||
| #define	ARMV7_FLAG_MASK		0xffffffff	/* Mask for writable bits */ | ||||
| #define	ARMV7_OVERFLOWED_MASK	ARMV7_FLAG_MASK | ||||
| 
 | ||||
| static inline unsigned long armv7_pmnc_read(void) | ||||
| /*
 | ||||
|  * PMXEVTYPER: Event selection reg | ||||
|  */ | ||||
| #define	ARMV7_EVTYPE_MASK	0xc00000ff	/* Mask for writable bits */ | ||||
| #define	ARMV7_EVTYPE_EVENT	0xff		/* Mask for EVENT bits */ | ||||
| 
 | ||||
| /*
 | ||||
|  * Event filters for PMUv2 | ||||
|  */ | ||||
| #define	ARMV7_EXCLUDE_PL1	(1 << 31) | ||||
| #define	ARMV7_EXCLUDE_USER	(1 << 30) | ||||
| #define	ARMV7_INCLUDE_HYP	(1 << 27) | ||||
| 
 | ||||
| static inline u32 armv7_pmnc_read(void) | ||||
| { | ||||
| 	u32 val; | ||||
| 	asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r"(val)); | ||||
| 	return val; | ||||
| } | ||||
| 
 | ||||
| static inline void armv7_pmnc_write(unsigned long val) | ||||
| static inline void armv7_pmnc_write(u32 val) | ||||
| { | ||||
| 	val &= ARMV7_PMNC_MASK; | ||||
| 	isb(); | ||||
| 	asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r"(val)); | ||||
| } | ||||
| 
 | ||||
| static inline int armv7_pmnc_has_overflowed(unsigned long pmnc) | ||||
| static inline int armv7_pmnc_has_overflowed(u32 pmnc) | ||||
| { | ||||
| 	return pmnc & ARMV7_OVERFLOWED_MASK; | ||||
| } | ||||
| 
 | ||||
| static inline int armv7_pmnc_counter_has_overflowed(unsigned long pmnc, | ||||
| 					enum armv7_counters counter) | ||||
| static inline int armv7_pmnc_counter_valid(int idx) | ||||
| { | ||||
| 	return idx >= ARMV7_IDX_CYCLE_COUNTER && idx <= ARMV7_IDX_COUNTER_LAST; | ||||
| } | ||||
| 
 | ||||
| static inline int armv7_pmnc_counter_has_overflowed(u32 pmnc, int idx) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	u32 counter; | ||||
| 
 | ||||
| 	if (counter == ARMV7_CYCLE_COUNTER) | ||||
| 		ret = pmnc & ARMV7_FLAG_C; | ||||
| 	else if ((counter >= ARMV7_COUNTER0) && (counter <= ARMV7_COUNTER_LAST)) | ||||
| 		ret = pmnc & ARMV7_FLAG_P(counter); | ||||
| 	else | ||||
| 	if (!armv7_pmnc_counter_valid(idx)) { | ||||
| 		pr_err("CPU%u checking wrong counter %d overflow status\n", | ||||
| 			smp_processor_id(), counter); | ||||
| 			smp_processor_id(), idx); | ||||
| 	} else { | ||||
| 		counter = ARMV7_IDX_TO_COUNTER(idx); | ||||
| 		ret = pmnc & BIT(counter); | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static inline int armv7_pmnc_select_counter(unsigned int idx) | ||||
| static inline int armv7_pmnc_select_counter(int idx) | ||||
| { | ||||
| 	u32 val; | ||||
| 	u32 counter; | ||||
| 
 | ||||
| 	if ((idx < ARMV7_COUNTER0) || (idx > ARMV7_COUNTER_LAST)) { | ||||
| 		pr_err("CPU%u selecting wrong PMNC counter" | ||||
| 			" %d\n", smp_processor_id(), idx); | ||||
| 		return -1; | ||||
| 	if (!armv7_pmnc_counter_valid(idx)) { | ||||
| 		pr_err("CPU%u selecting wrong PMNC counter %d\n", | ||||
| 			smp_processor_id(), idx); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	val = (idx - ARMV7_EVENT_CNT_TO_CNTx) & ARMV7_SELECT_MASK; | ||||
| 	asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val)); | ||||
| 	counter = ARMV7_IDX_TO_COUNTER(idx); | ||||
| 	asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (counter)); | ||||
| 	isb(); | ||||
| 
 | ||||
| 	return idx; | ||||
| @ -812,124 +789,95 @@ static inline int armv7_pmnc_select_counter(unsigned int idx) | ||||
| 
 | ||||
| static inline u32 armv7pmu_read_counter(int idx) | ||||
| { | ||||
| 	unsigned long value = 0; | ||||
| 	u32 value = 0; | ||||
| 
 | ||||
| 	if (idx == ARMV7_CYCLE_COUNTER) | ||||
| 		asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (value)); | ||||
| 	else if ((idx >= ARMV7_COUNTER0) && (idx <= ARMV7_COUNTER_LAST)) { | ||||
| 		if (armv7_pmnc_select_counter(idx) == idx) | ||||
| 			asm volatile("mrc p15, 0, %0, c9, c13, 2" | ||||
| 				     : "=r" (value)); | ||||
| 	} else | ||||
| 	if (!armv7_pmnc_counter_valid(idx)) | ||||
| 		pr_err("CPU%u reading wrong counter %d\n", | ||||
| 			smp_processor_id(), idx); | ||||
| 	else if (idx == ARMV7_IDX_CYCLE_COUNTER) | ||||
| 		asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (value)); | ||||
| 	else if (armv7_pmnc_select_counter(idx) == idx) | ||||
| 		asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (value)); | ||||
| 
 | ||||
| 	return value; | ||||
| } | ||||
| 
 | ||||
| static inline void armv7pmu_write_counter(int idx, u32 value) | ||||
| { | ||||
| 	if (idx == ARMV7_CYCLE_COUNTER) | ||||
| 		asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (value)); | ||||
| 	else if ((idx >= ARMV7_COUNTER0) && (idx <= ARMV7_COUNTER_LAST)) { | ||||
| 		if (armv7_pmnc_select_counter(idx) == idx) | ||||
| 			asm volatile("mcr p15, 0, %0, c9, c13, 2" | ||||
| 				     : : "r" (value)); | ||||
| 	} else | ||||
| 	if (!armv7_pmnc_counter_valid(idx)) | ||||
| 		pr_err("CPU%u writing wrong counter %d\n", | ||||
| 			smp_processor_id(), idx); | ||||
| 	else if (idx == ARMV7_IDX_CYCLE_COUNTER) | ||||
| 		asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (value)); | ||||
| 	else if (armv7_pmnc_select_counter(idx) == idx) | ||||
| 		asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (value)); | ||||
| } | ||||
| 
 | ||||
| static inline void armv7_pmnc_write_evtsel(unsigned int idx, u32 val) | ||||
| static inline void armv7_pmnc_write_evtsel(int idx, u32 val) | ||||
| { | ||||
| 	if (armv7_pmnc_select_counter(idx) == idx) { | ||||
| 		val &= ARMV7_EVTSEL_MASK; | ||||
| 		val &= ARMV7_EVTYPE_MASK; | ||||
| 		asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val)); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u32 armv7_pmnc_enable_counter(unsigned int idx) | ||||
| static inline int armv7_pmnc_enable_counter(int idx) | ||||
| { | ||||
| 	u32 val; | ||||
| 	u32 counter; | ||||
| 
 | ||||
| 	if ((idx != ARMV7_CYCLE_COUNTER) && | ||||
| 	    ((idx < ARMV7_COUNTER0) || (idx > ARMV7_COUNTER_LAST))) { | ||||
| 		pr_err("CPU%u enabling wrong PMNC counter" | ||||
| 			" %d\n", smp_processor_id(), idx); | ||||
| 		return -1; | ||||
| 	if (!armv7_pmnc_counter_valid(idx)) { | ||||
| 		pr_err("CPU%u enabling wrong PMNC counter %d\n", | ||||
| 			smp_processor_id(), idx); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (idx == ARMV7_CYCLE_COUNTER) | ||||
| 		val = ARMV7_CNTENS_C; | ||||
| 	else | ||||
| 		val = ARMV7_CNTENS_P(idx); | ||||
| 
 | ||||
| 	asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val)); | ||||
| 
 | ||||
| 	counter = ARMV7_IDX_TO_COUNTER(idx); | ||||
| 	asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (BIT(counter))); | ||||
| 	return idx; | ||||
| } | ||||
| 
 | ||||
| static inline u32 armv7_pmnc_disable_counter(unsigned int idx) | ||||
| static inline int armv7_pmnc_disable_counter(int idx) | ||||
| { | ||||
| 	u32 val; | ||||
| 	u32 counter; | ||||
| 
 | ||||
| 
 | ||||
| 	if ((idx != ARMV7_CYCLE_COUNTER) && | ||||
| 	    ((idx < ARMV7_COUNTER0) || (idx > ARMV7_COUNTER_LAST))) { | ||||
| 		pr_err("CPU%u disabling wrong PMNC counter" | ||||
| 			" %d\n", smp_processor_id(), idx); | ||||
| 		return -1; | ||||
| 	if (!armv7_pmnc_counter_valid(idx)) { | ||||
| 		pr_err("CPU%u disabling wrong PMNC counter %d\n", | ||||
| 			smp_processor_id(), idx); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (idx == ARMV7_CYCLE_COUNTER) | ||||
| 		val = ARMV7_CNTENC_C; | ||||
| 	else | ||||
| 		val = ARMV7_CNTENC_P(idx); | ||||
| 
 | ||||
| 	asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val)); | ||||
| 
 | ||||
| 	counter = ARMV7_IDX_TO_COUNTER(idx); | ||||
| 	asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (BIT(counter))); | ||||
| 	return idx; | ||||
| } | ||||
| 
 | ||||
| static inline u32 armv7_pmnc_enable_intens(unsigned int idx) | ||||
| static inline int armv7_pmnc_enable_intens(int idx) | ||||
| { | ||||
| 	u32 val; | ||||
| 	u32 counter; | ||||
| 
 | ||||
| 	if ((idx != ARMV7_CYCLE_COUNTER) && | ||||
| 	    ((idx < ARMV7_COUNTER0) || (idx > ARMV7_COUNTER_LAST))) { | ||||
| 		pr_err("CPU%u enabling wrong PMNC counter" | ||||
| 			" interrupt enable %d\n", smp_processor_id(), idx); | ||||
| 		return -1; | ||||
| 	if (!armv7_pmnc_counter_valid(idx)) { | ||||
| 		pr_err("CPU%u enabling wrong PMNC counter IRQ enable %d\n", | ||||
| 			smp_processor_id(), idx); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (idx == ARMV7_CYCLE_COUNTER) | ||||
| 		val = ARMV7_INTENS_C; | ||||
| 	else | ||||
| 		val = ARMV7_INTENS_P(idx); | ||||
| 
 | ||||
| 	asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (val)); | ||||
| 
 | ||||
| 	counter = ARMV7_IDX_TO_COUNTER(idx); | ||||
| 	asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (BIT(counter))); | ||||
| 	return idx; | ||||
| } | ||||
| 
 | ||||
| static inline u32 armv7_pmnc_disable_intens(unsigned int idx) | ||||
| static inline int armv7_pmnc_disable_intens(int idx) | ||||
| { | ||||
| 	u32 val; | ||||
| 	u32 counter; | ||||
| 
 | ||||
| 	if ((idx != ARMV7_CYCLE_COUNTER) && | ||||
| 	    ((idx < ARMV7_COUNTER0) || (idx > ARMV7_COUNTER_LAST))) { | ||||
| 		pr_err("CPU%u disabling wrong PMNC counter" | ||||
| 			" interrupt enable %d\n", smp_processor_id(), idx); | ||||
| 		return -1; | ||||
| 	if (!armv7_pmnc_counter_valid(idx)) { | ||||
| 		pr_err("CPU%u disabling wrong PMNC counter IRQ enable %d\n", | ||||
| 			smp_processor_id(), idx); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (idx == ARMV7_CYCLE_COUNTER) | ||||
| 		val = ARMV7_INTENC_C; | ||||
| 	else | ||||
| 		val = ARMV7_INTENC_P(idx); | ||||
| 
 | ||||
| 	asm volatile("mcr p15, 0, %0, c9, c14, 2" : : "r" (val)); | ||||
| 
 | ||||
| 	counter = ARMV7_IDX_TO_COUNTER(idx); | ||||
| 	asm volatile("mcr p15, 0, %0, c9, c14, 2" : : "r" (BIT(counter))); | ||||
| 	return idx; | ||||
| } | ||||
| 
 | ||||
| @ -973,14 +921,14 @@ static void armv7_pmnc_dump_regs(void) | ||||
| 	asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val)); | ||||
| 	printk(KERN_INFO "CCNT  =0x%08x\n", val); | ||||
| 
 | ||||
| 	for (cnt = ARMV7_COUNTER0; cnt < ARMV7_COUNTER_LAST; cnt++) { | ||||
| 	for (cnt = ARMV7_IDX_COUNTER0; cnt <= ARMV7_IDX_COUNTER_LAST; 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-ARMV7_EVENT_CNT_TO_CNTx, val); | ||||
| 			ARMV7_IDX_TO_COUNTER(cnt), val); | ||||
| 		asm volatile("mrc p15, 0, %0, c9, c13, 1" : "=r" (val)); | ||||
| 		printk(KERN_INFO "CNT[%d] evtsel=0x%08x\n", | ||||
| 			cnt-ARMV7_EVENT_CNT_TO_CNTx, val); | ||||
| 			ARMV7_IDX_TO_COUNTER(cnt), val); | ||||
| 	} | ||||
| } | ||||
| #endif | ||||
| @ -988,12 +936,13 @@ static void armv7_pmnc_dump_regs(void) | ||||
| static void armv7pmu_enable_event(struct hw_perf_event *hwc, int idx) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 	struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Enable counter and interrupt, and set the counter to count | ||||
| 	 * the event that we're interested in. | ||||
| 	 */ | ||||
| 	raw_spin_lock_irqsave(&pmu_lock, flags); | ||||
| 	raw_spin_lock_irqsave(&events->pmu_lock, flags); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Disable counter | ||||
| @ -1002,9 +951,10 @@ static void armv7pmu_enable_event(struct hw_perf_event *hwc, int idx) | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Set event (if destined for PMNx counters) | ||||
| 	 * We don't need to set the event if it's a cycle count | ||||
| 	 * We only need to set the event for the cycle counter if we | ||||
| 	 * have the ability to perform event filtering. | ||||
| 	 */ | ||||
| 	if (idx != ARMV7_CYCLE_COUNTER) | ||||
| 	if (armv7pmu.set_event_filter || idx != ARMV7_IDX_CYCLE_COUNTER) | ||||
| 		armv7_pmnc_write_evtsel(idx, hwc->config_base); | ||||
| 
 | ||||
| 	/*
 | ||||
| @ -1017,17 +967,18 @@ static void armv7pmu_enable_event(struct hw_perf_event *hwc, int idx) | ||||
| 	 */ | ||||
| 	armv7_pmnc_enable_counter(idx); | ||||
| 
 | ||||
| 	raw_spin_unlock_irqrestore(&pmu_lock, flags); | ||||
| 	raw_spin_unlock_irqrestore(&events->pmu_lock, flags); | ||||
| } | ||||
| 
 | ||||
| static void armv7pmu_disable_event(struct hw_perf_event *hwc, int idx) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 	struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Disable counter and interrupt | ||||
| 	 */ | ||||
| 	raw_spin_lock_irqsave(&pmu_lock, flags); | ||||
| 	raw_spin_lock_irqsave(&events->pmu_lock, flags); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Disable counter | ||||
| @ -1039,14 +990,14 @@ static void armv7pmu_disable_event(struct hw_perf_event *hwc, int idx) | ||||
| 	 */ | ||||
| 	armv7_pmnc_disable_intens(idx); | ||||
| 
 | ||||
| 	raw_spin_unlock_irqrestore(&pmu_lock, flags); | ||||
| 	raw_spin_unlock_irqrestore(&events->pmu_lock, flags); | ||||
| } | ||||
| 
 | ||||
| static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev) | ||||
| { | ||||
| 	unsigned long pmnc; | ||||
| 	u32 pmnc; | ||||
| 	struct perf_sample_data data; | ||||
| 	struct cpu_hw_events *cpuc; | ||||
| 	struct pmu_hw_events *cpuc; | ||||
| 	struct pt_regs *regs; | ||||
| 	int idx; | ||||
| 
 | ||||
| @ -1069,13 +1020,10 @@ static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev) | ||||
| 	perf_sample_data_init(&data, 0); | ||||
| 
 | ||||
| 	cpuc = &__get_cpu_var(cpu_hw_events); | ||||
| 	for (idx = 0; idx <= armpmu->num_events; ++idx) { | ||||
| 	for (idx = 0; idx < cpu_pmu->num_events; ++idx) { | ||||
| 		struct perf_event *event = cpuc->events[idx]; | ||||
| 		struct hw_perf_event *hwc; | ||||
| 
 | ||||
| 		if (!test_bit(idx, cpuc->active_mask)) | ||||
| 			continue; | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * We have a single interrupt for all counters. Check that | ||||
| 		 * each counter has overflowed before we process it. | ||||
| @ -1090,7 +1038,7 @@ static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (perf_event_overflow(event, &data, regs)) | ||||
| 			armpmu->disable(hwc, idx); | ||||
| 			cpu_pmu->disable(hwc, idx); | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| @ -1108,61 +1056,114 @@ static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev) | ||||
| static void armv7pmu_start(void) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 	struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | ||||
| 
 | ||||
| 	raw_spin_lock_irqsave(&pmu_lock, flags); | ||||
| 	raw_spin_lock_irqsave(&events->pmu_lock, flags); | ||||
| 	/* Enable all counters */ | ||||
| 	armv7_pmnc_write(armv7_pmnc_read() | ARMV7_PMNC_E); | ||||
| 	raw_spin_unlock_irqrestore(&pmu_lock, flags); | ||||
| 	raw_spin_unlock_irqrestore(&events->pmu_lock, flags); | ||||
| } | ||||
| 
 | ||||
| static void armv7pmu_stop(void) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 	struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | ||||
| 
 | ||||
| 	raw_spin_lock_irqsave(&pmu_lock, flags); | ||||
| 	raw_spin_lock_irqsave(&events->pmu_lock, flags); | ||||
| 	/* Disable all counters */ | ||||
| 	armv7_pmnc_write(armv7_pmnc_read() & ~ARMV7_PMNC_E); | ||||
| 	raw_spin_unlock_irqrestore(&pmu_lock, flags); | ||||
| 	raw_spin_unlock_irqrestore(&events->pmu_lock, flags); | ||||
| } | ||||
| 
 | ||||
| static int armv7pmu_get_event_idx(struct cpu_hw_events *cpuc, | ||||
| static int armv7pmu_get_event_idx(struct pmu_hw_events *cpuc, | ||||
| 				  struct hw_perf_event *event) | ||||
| { | ||||
| 	int idx; | ||||
| 	unsigned long evtype = event->config_base & ARMV7_EVTYPE_EVENT; | ||||
| 
 | ||||
| 	/* Always place a cycle counter into the cycle counter. */ | ||||
| 	if (event->config_base == ARMV7_PERFCTR_CPU_CYCLES) { | ||||
| 		if (test_and_set_bit(ARMV7_CYCLE_COUNTER, cpuc->used_mask)) | ||||
| 	if (evtype == ARMV7_PERFCTR_CPU_CYCLES) { | ||||
| 		if (test_and_set_bit(ARMV7_IDX_CYCLE_COUNTER, cpuc->used_mask)) | ||||
| 			return -EAGAIN; | ||||
| 
 | ||||
| 		return ARMV7_CYCLE_COUNTER; | ||||
| 	} else { | ||||
| 		/*
 | ||||
| 		 * For anything other than a cycle counter, try and use | ||||
| 		 * the events counters | ||||
| 		 */ | ||||
| 		for (idx = ARMV7_COUNTER0; idx <= armpmu->num_events; ++idx) { | ||||
| 			if (!test_and_set_bit(idx, cpuc->used_mask)) | ||||
| 				return idx; | ||||
| 		} | ||||
| 
 | ||||
| 		/* The counters are all in use. */ | ||||
| 		return -EAGAIN; | ||||
| 		return ARMV7_IDX_CYCLE_COUNTER; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * For anything other than a cycle counter, try and use | ||||
| 	 * the events counters | ||||
| 	 */ | ||||
| 	for (idx = ARMV7_IDX_COUNTER0; idx < cpu_pmu->num_events; ++idx) { | ||||
| 		if (!test_and_set_bit(idx, cpuc->used_mask)) | ||||
| 			return idx; | ||||
| 	} | ||||
| 
 | ||||
| 	/* The counters are all in use. */ | ||||
| 	return -EAGAIN; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Add an event filter to a given event. This will only work for PMUv2 PMUs. | ||||
|  */ | ||||
| static int armv7pmu_set_event_filter(struct hw_perf_event *event, | ||||
| 				     struct perf_event_attr *attr) | ||||
| { | ||||
| 	unsigned long config_base = 0; | ||||
| 
 | ||||
| 	if (attr->exclude_idle) | ||||
| 		return -EPERM; | ||||
| 	if (attr->exclude_user) | ||||
| 		config_base |= ARMV7_EXCLUDE_USER; | ||||
| 	if (attr->exclude_kernel) | ||||
| 		config_base |= ARMV7_EXCLUDE_PL1; | ||||
| 	if (!attr->exclude_hv) | ||||
| 		config_base |= ARMV7_INCLUDE_HYP; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Install the filter into config_base as this is used to | ||||
| 	 * construct the event type. | ||||
| 	 */ | ||||
| 	event->config_base = config_base; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void armv7pmu_reset(void *info) | ||||
| { | ||||
| 	u32 idx, nb_cnt = armpmu->num_events; | ||||
| 	u32 idx, nb_cnt = cpu_pmu->num_events; | ||||
| 
 | ||||
| 	/* The counter and interrupt enable registers are unknown at reset. */ | ||||
| 	for (idx = 1; idx < nb_cnt; ++idx) | ||||
| 	for (idx = ARMV7_IDX_CYCLE_COUNTER; idx < nb_cnt; ++idx) | ||||
| 		armv7pmu_disable_event(NULL, idx); | ||||
| 
 | ||||
| 	/* Initialize & Reset PMNC: C and P bits */ | ||||
| 	armv7_pmnc_write(ARMV7_PMNC_P | ARMV7_PMNC_C); | ||||
| } | ||||
| 
 | ||||
| static int armv7_a8_map_event(struct perf_event *event) | ||||
| { | ||||
| 	return map_cpu_event(event, &armv7_a8_perf_map, | ||||
| 				&armv7_a8_perf_cache_map, 0xFF); | ||||
| } | ||||
| 
 | ||||
| static int armv7_a9_map_event(struct perf_event *event) | ||||
| { | ||||
| 	return map_cpu_event(event, &armv7_a9_perf_map, | ||||
| 				&armv7_a9_perf_cache_map, 0xFF); | ||||
| } | ||||
| 
 | ||||
| static int armv7_a5_map_event(struct perf_event *event) | ||||
| { | ||||
| 	return map_cpu_event(event, &armv7_a5_perf_map, | ||||
| 				&armv7_a5_perf_cache_map, 0xFF); | ||||
| } | ||||
| 
 | ||||
| static int armv7_a15_map_event(struct perf_event *event) | ||||
| { | ||||
| 	return map_cpu_event(event, &armv7_a15_perf_map, | ||||
| 				&armv7_a15_perf_cache_map, 0xFF); | ||||
| } | ||||
| 
 | ||||
| static struct arm_pmu armv7pmu = { | ||||
| 	.handle_irq		= armv7pmu_handle_irq, | ||||
| 	.enable			= armv7pmu_enable_event, | ||||
| @ -1173,7 +1174,6 @@ static struct arm_pmu armv7pmu = { | ||||
| 	.start			= armv7pmu_start, | ||||
| 	.stop			= armv7pmu_stop, | ||||
| 	.reset			= armv7pmu_reset, | ||||
| 	.raw_event_mask		= 0xFF, | ||||
| 	.max_period		= (1LLU << 32) - 1, | ||||
| }; | ||||
| 
 | ||||
| @ -1188,62 +1188,59 @@ static u32 __init armv7_read_num_pmnc_events(void) | ||||
| 	return nb_cnt + 1; | ||||
| } | ||||
| 
 | ||||
| static const struct arm_pmu *__init armv7_a8_pmu_init(void) | ||||
| static struct arm_pmu *__init armv7_a8_pmu_init(void) | ||||
| { | ||||
| 	armv7pmu.id		= ARM_PERF_PMU_ID_CA8; | ||||
| 	armv7pmu.name		= "ARMv7 Cortex-A8"; | ||||
| 	armv7pmu.cache_map	= &armv7_a8_perf_cache_map; | ||||
| 	armv7pmu.event_map	= &armv7_a8_perf_map; | ||||
| 	armv7pmu.map_event	= armv7_a8_map_event; | ||||
| 	armv7pmu.num_events	= armv7_read_num_pmnc_events(); | ||||
| 	return &armv7pmu; | ||||
| } | ||||
| 
 | ||||
| static const struct arm_pmu *__init armv7_a9_pmu_init(void) | ||||
| static struct arm_pmu *__init armv7_a9_pmu_init(void) | ||||
| { | ||||
| 	armv7pmu.id		= ARM_PERF_PMU_ID_CA9; | ||||
| 	armv7pmu.name		= "ARMv7 Cortex-A9"; | ||||
| 	armv7pmu.cache_map	= &armv7_a9_perf_cache_map; | ||||
| 	armv7pmu.event_map	= &armv7_a9_perf_map; | ||||
| 	armv7pmu.map_event	= armv7_a9_map_event; | ||||
| 	armv7pmu.num_events	= armv7_read_num_pmnc_events(); | ||||
| 	return &armv7pmu; | ||||
| } | ||||
| 
 | ||||
| static const struct arm_pmu *__init armv7_a5_pmu_init(void) | ||||
| static struct arm_pmu *__init armv7_a5_pmu_init(void) | ||||
| { | ||||
| 	armv7pmu.id		= ARM_PERF_PMU_ID_CA5; | ||||
| 	armv7pmu.name		= "ARMv7 Cortex-A5"; | ||||
| 	armv7pmu.cache_map	= &armv7_a5_perf_cache_map; | ||||
| 	armv7pmu.event_map	= &armv7_a5_perf_map; | ||||
| 	armv7pmu.map_event	= armv7_a5_map_event; | ||||
| 	armv7pmu.num_events	= armv7_read_num_pmnc_events(); | ||||
| 	return &armv7pmu; | ||||
| } | ||||
| 
 | ||||
| static const struct arm_pmu *__init armv7_a15_pmu_init(void) | ||||
| static struct arm_pmu *__init armv7_a15_pmu_init(void) | ||||
| { | ||||
| 	armv7pmu.id		= ARM_PERF_PMU_ID_CA15; | ||||
| 	armv7pmu.name		= "ARMv7 Cortex-A15"; | ||||
| 	armv7pmu.cache_map	= &armv7_a15_perf_cache_map; | ||||
| 	armv7pmu.event_map	= &armv7_a15_perf_map; | ||||
| 	armv7pmu.map_event	= armv7_a15_map_event; | ||||
| 	armv7pmu.num_events	= armv7_read_num_pmnc_events(); | ||||
| 	armv7pmu.set_event_filter = armv7pmu_set_event_filter; | ||||
| 	return &armv7pmu; | ||||
| } | ||||
| #else | ||||
| static const struct arm_pmu *__init armv7_a8_pmu_init(void) | ||||
| static struct arm_pmu *__init armv7_a8_pmu_init(void) | ||||
| { | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static const struct arm_pmu *__init armv7_a9_pmu_init(void) | ||||
| static struct arm_pmu *__init armv7_a9_pmu_init(void) | ||||
| { | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static const struct arm_pmu *__init armv7_a5_pmu_init(void) | ||||
| static struct arm_pmu *__init armv7_a5_pmu_init(void) | ||||
| { | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static const struct arm_pmu *__init armv7_a15_pmu_init(void) | ||||
| static struct arm_pmu *__init armv7_a15_pmu_init(void) | ||||
| { | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| @ -40,7 +40,7 @@ enum xscale_perf_types { | ||||
| }; | ||||
| 
 | ||||
| enum xscale_counters { | ||||
| 	XSCALE_CYCLE_COUNTER	= 1, | ||||
| 	XSCALE_CYCLE_COUNTER	= 0, | ||||
| 	XSCALE_COUNTER0, | ||||
| 	XSCALE_COUNTER1, | ||||
| 	XSCALE_COUNTER2, | ||||
| @ -222,7 +222,7 @@ xscale1pmu_handle_irq(int irq_num, void *dev) | ||||
| { | ||||
| 	unsigned long pmnc; | ||||
| 	struct perf_sample_data data; | ||||
| 	struct cpu_hw_events *cpuc; | ||||
| 	struct pmu_hw_events *cpuc; | ||||
| 	struct pt_regs *regs; | ||||
| 	int idx; | ||||
| 
 | ||||
| @ -249,13 +249,10 @@ xscale1pmu_handle_irq(int irq_num, void *dev) | ||||
| 	perf_sample_data_init(&data, 0); | ||||
| 
 | ||||
| 	cpuc = &__get_cpu_var(cpu_hw_events); | ||||
| 	for (idx = 0; idx <= armpmu->num_events; ++idx) { | ||||
| 	for (idx = 0; idx < cpu_pmu->num_events; ++idx) { | ||||
| 		struct perf_event *event = cpuc->events[idx]; | ||||
| 		struct hw_perf_event *hwc; | ||||
| 
 | ||||
| 		if (!test_bit(idx, cpuc->active_mask)) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (!xscale1_pmnc_counter_has_overflowed(pmnc, idx)) | ||||
| 			continue; | ||||
| 
 | ||||
| @ -266,7 +263,7 @@ xscale1pmu_handle_irq(int irq_num, void *dev) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (perf_event_overflow(event, &data, regs)) | ||||
| 			armpmu->disable(hwc, idx); | ||||
| 			cpu_pmu->disable(hwc, idx); | ||||
| 	} | ||||
| 
 | ||||
| 	irq_work_run(); | ||||
| @ -284,6 +281,7 @@ static void | ||||
| xscale1pmu_enable_event(struct hw_perf_event *hwc, int idx) | ||||
| { | ||||
| 	unsigned long val, mask, evt, flags; | ||||
| 	struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | ||||
| 
 | ||||
| 	switch (idx) { | ||||
| 	case XSCALE_CYCLE_COUNTER: | ||||
| @ -305,18 +303,19 @@ xscale1pmu_enable_event(struct hw_perf_event *hwc, int idx) | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	raw_spin_lock_irqsave(&pmu_lock, flags); | ||||
| 	raw_spin_lock_irqsave(&events->pmu_lock, flags); | ||||
| 	val = xscale1pmu_read_pmnc(); | ||||
| 	val &= ~mask; | ||||
| 	val |= evt; | ||||
| 	xscale1pmu_write_pmnc(val); | ||||
| 	raw_spin_unlock_irqrestore(&pmu_lock, flags); | ||||
| 	raw_spin_unlock_irqrestore(&events->pmu_lock, flags); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| xscale1pmu_disable_event(struct hw_perf_event *hwc, int idx) | ||||
| { | ||||
| 	unsigned long val, mask, evt, flags; | ||||
| 	struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | ||||
| 
 | ||||
| 	switch (idx) { | ||||
| 	case XSCALE_CYCLE_COUNTER: | ||||
| @ -336,16 +335,16 @@ xscale1pmu_disable_event(struct hw_perf_event *hwc, int idx) | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	raw_spin_lock_irqsave(&pmu_lock, flags); | ||||
| 	raw_spin_lock_irqsave(&events->pmu_lock, flags); | ||||
| 	val = xscale1pmu_read_pmnc(); | ||||
| 	val &= ~mask; | ||||
| 	val |= evt; | ||||
| 	xscale1pmu_write_pmnc(val); | ||||
| 	raw_spin_unlock_irqrestore(&pmu_lock, flags); | ||||
| 	raw_spin_unlock_irqrestore(&events->pmu_lock, flags); | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| xscale1pmu_get_event_idx(struct cpu_hw_events *cpuc, | ||||
| xscale1pmu_get_event_idx(struct pmu_hw_events *cpuc, | ||||
| 			struct hw_perf_event *event) | ||||
| { | ||||
| 	if (XSCALE_PERFCTR_CCNT == event->config_base) { | ||||
| @ -368,24 +367,26 @@ static void | ||||
| xscale1pmu_start(void) | ||||
| { | ||||
| 	unsigned long flags, val; | ||||
| 	struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | ||||
| 
 | ||||
| 	raw_spin_lock_irqsave(&pmu_lock, flags); | ||||
| 	raw_spin_lock_irqsave(&events->pmu_lock, flags); | ||||
| 	val = xscale1pmu_read_pmnc(); | ||||
| 	val |= XSCALE_PMU_ENABLE; | ||||
| 	xscale1pmu_write_pmnc(val); | ||||
| 	raw_spin_unlock_irqrestore(&pmu_lock, flags); | ||||
| 	raw_spin_unlock_irqrestore(&events->pmu_lock, flags); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| xscale1pmu_stop(void) | ||||
| { | ||||
| 	unsigned long flags, val; | ||||
| 	struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | ||||
| 
 | ||||
| 	raw_spin_lock_irqsave(&pmu_lock, flags); | ||||
| 	raw_spin_lock_irqsave(&events->pmu_lock, flags); | ||||
| 	val = xscale1pmu_read_pmnc(); | ||||
| 	val &= ~XSCALE_PMU_ENABLE; | ||||
| 	xscale1pmu_write_pmnc(val); | ||||
| 	raw_spin_unlock_irqrestore(&pmu_lock, flags); | ||||
| 	raw_spin_unlock_irqrestore(&events->pmu_lock, flags); | ||||
| } | ||||
| 
 | ||||
| static inline u32 | ||||
| @ -424,7 +425,13 @@ xscale1pmu_write_counter(int counter, u32 val) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static const struct arm_pmu xscale1pmu = { | ||||
| static int xscale_map_event(struct perf_event *event) | ||||
| { | ||||
| 	return map_cpu_event(event, &xscale_perf_map, | ||||
| 				&xscale_perf_cache_map, 0xFF); | ||||
| } | ||||
| 
 | ||||
| static struct arm_pmu xscale1pmu = { | ||||
| 	.id		= ARM_PERF_PMU_ID_XSCALE1, | ||||
| 	.name		= "xscale1", | ||||
| 	.handle_irq	= xscale1pmu_handle_irq, | ||||
| @ -435,14 +442,12 @@ static const struct arm_pmu xscale1pmu = { | ||||
| 	.get_event_idx	= xscale1pmu_get_event_idx, | ||||
| 	.start		= xscale1pmu_start, | ||||
| 	.stop		= xscale1pmu_stop, | ||||
| 	.cache_map	= &xscale_perf_cache_map, | ||||
| 	.event_map	= &xscale_perf_map, | ||||
| 	.raw_event_mask	= 0xFF, | ||||
| 	.map_event	= xscale_map_event, | ||||
| 	.num_events	= 3, | ||||
| 	.max_period	= (1LLU << 32) - 1, | ||||
| }; | ||||
| 
 | ||||
| static const struct arm_pmu *__init xscale1pmu_init(void) | ||||
| static struct arm_pmu *__init xscale1pmu_init(void) | ||||
| { | ||||
| 	return &xscale1pmu; | ||||
| } | ||||
| @ -560,7 +565,7 @@ xscale2pmu_handle_irq(int irq_num, void *dev) | ||||
| { | ||||
| 	unsigned long pmnc, of_flags; | ||||
| 	struct perf_sample_data data; | ||||
| 	struct cpu_hw_events *cpuc; | ||||
| 	struct pmu_hw_events *cpuc; | ||||
| 	struct pt_regs *regs; | ||||
| 	int idx; | ||||
| 
 | ||||
| @ -581,13 +586,10 @@ xscale2pmu_handle_irq(int irq_num, void *dev) | ||||
| 	perf_sample_data_init(&data, 0); | ||||
| 
 | ||||
| 	cpuc = &__get_cpu_var(cpu_hw_events); | ||||
| 	for (idx = 0; idx <= armpmu->num_events; ++idx) { | ||||
| 	for (idx = 0; idx < cpu_pmu->num_events; ++idx) { | ||||
| 		struct perf_event *event = cpuc->events[idx]; | ||||
| 		struct hw_perf_event *hwc; | ||||
| 
 | ||||
| 		if (!test_bit(idx, cpuc->active_mask)) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (!xscale2_pmnc_counter_has_overflowed(pmnc, idx)) | ||||
| 			continue; | ||||
| 
 | ||||
| @ -598,7 +600,7 @@ xscale2pmu_handle_irq(int irq_num, void *dev) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (perf_event_overflow(event, &data, regs)) | ||||
| 			armpmu->disable(hwc, idx); | ||||
| 			cpu_pmu->disable(hwc, idx); | ||||
| 	} | ||||
| 
 | ||||
| 	irq_work_run(); | ||||
| @ -616,6 +618,7 @@ static void | ||||
| xscale2pmu_enable_event(struct hw_perf_event *hwc, int idx) | ||||
| { | ||||
| 	unsigned long flags, ien, evtsel; | ||||
| 	struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | ||||
| 
 | ||||
| 	ien = xscale2pmu_read_int_enable(); | ||||
| 	evtsel = xscale2pmu_read_event_select(); | ||||
| @ -649,16 +652,17 @@ xscale2pmu_enable_event(struct hw_perf_event *hwc, int idx) | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	raw_spin_lock_irqsave(&pmu_lock, flags); | ||||
| 	raw_spin_lock_irqsave(&events->pmu_lock, flags); | ||||
| 	xscale2pmu_write_event_select(evtsel); | ||||
| 	xscale2pmu_write_int_enable(ien); | ||||
| 	raw_spin_unlock_irqrestore(&pmu_lock, flags); | ||||
| 	raw_spin_unlock_irqrestore(&events->pmu_lock, flags); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| xscale2pmu_disable_event(struct hw_perf_event *hwc, int idx) | ||||
| { | ||||
| 	unsigned long flags, ien, evtsel; | ||||
| 	struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | ||||
| 
 | ||||
| 	ien = xscale2pmu_read_int_enable(); | ||||
| 	evtsel = xscale2pmu_read_event_select(); | ||||
| @ -692,14 +696,14 @@ xscale2pmu_disable_event(struct hw_perf_event *hwc, int idx) | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	raw_spin_lock_irqsave(&pmu_lock, flags); | ||||
| 	raw_spin_lock_irqsave(&events->pmu_lock, flags); | ||||
| 	xscale2pmu_write_event_select(evtsel); | ||||
| 	xscale2pmu_write_int_enable(ien); | ||||
| 	raw_spin_unlock_irqrestore(&pmu_lock, flags); | ||||
| 	raw_spin_unlock_irqrestore(&events->pmu_lock, flags); | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| xscale2pmu_get_event_idx(struct cpu_hw_events *cpuc, | ||||
| xscale2pmu_get_event_idx(struct pmu_hw_events *cpuc, | ||||
| 			struct hw_perf_event *event) | ||||
| { | ||||
| 	int idx = xscale1pmu_get_event_idx(cpuc, event); | ||||
| @ -718,24 +722,26 @@ static void | ||||
| xscale2pmu_start(void) | ||||
| { | ||||
| 	unsigned long flags, val; | ||||
| 	struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | ||||
| 
 | ||||
| 	raw_spin_lock_irqsave(&pmu_lock, flags); | ||||
| 	raw_spin_lock_irqsave(&events->pmu_lock, flags); | ||||
| 	val = xscale2pmu_read_pmnc() & ~XSCALE_PMU_CNT64; | ||||
| 	val |= XSCALE_PMU_ENABLE; | ||||
| 	xscale2pmu_write_pmnc(val); | ||||
| 	raw_spin_unlock_irqrestore(&pmu_lock, flags); | ||||
| 	raw_spin_unlock_irqrestore(&events->pmu_lock, flags); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| xscale2pmu_stop(void) | ||||
| { | ||||
| 	unsigned long flags, val; | ||||
| 	struct pmu_hw_events *events = cpu_pmu->get_hw_events(); | ||||
| 
 | ||||
| 	raw_spin_lock_irqsave(&pmu_lock, flags); | ||||
| 	raw_spin_lock_irqsave(&events->pmu_lock, flags); | ||||
| 	val = xscale2pmu_read_pmnc(); | ||||
| 	val &= ~XSCALE_PMU_ENABLE; | ||||
| 	xscale2pmu_write_pmnc(val); | ||||
| 	raw_spin_unlock_irqrestore(&pmu_lock, flags); | ||||
| 	raw_spin_unlock_irqrestore(&events->pmu_lock, flags); | ||||
| } | ||||
| 
 | ||||
| static inline u32 | ||||
| @ -786,7 +792,7 @@ xscale2pmu_write_counter(int counter, u32 val) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static const struct arm_pmu xscale2pmu = { | ||||
| static struct arm_pmu xscale2pmu = { | ||||
| 	.id		= ARM_PERF_PMU_ID_XSCALE2, | ||||
| 	.name		= "xscale2", | ||||
| 	.handle_irq	= xscale2pmu_handle_irq, | ||||
| @ -797,24 +803,22 @@ static const struct arm_pmu xscale2pmu = { | ||||
| 	.get_event_idx	= xscale2pmu_get_event_idx, | ||||
| 	.start		= xscale2pmu_start, | ||||
| 	.stop		= xscale2pmu_stop, | ||||
| 	.cache_map	= &xscale_perf_cache_map, | ||||
| 	.event_map	= &xscale_perf_map, | ||||
| 	.raw_event_mask	= 0xFF, | ||||
| 	.map_event	= xscale_map_event, | ||||
| 	.num_events	= 5, | ||||
| 	.max_period	= (1LLU << 32) - 1, | ||||
| }; | ||||
| 
 | ||||
| static const struct arm_pmu *__init xscale2pmu_init(void) | ||||
| static struct arm_pmu *__init xscale2pmu_init(void) | ||||
| { | ||||
| 	return &xscale2pmu; | ||||
| } | ||||
| #else | ||||
| static const struct arm_pmu *__init xscale1pmu_init(void) | ||||
| static struct arm_pmu *__init xscale1pmu_init(void) | ||||
| { | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static const struct arm_pmu *__init xscale2pmu_init(void) | ||||
| static struct arm_pmu *__init xscale2pmu_init(void) | ||||
| { | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| @ -10,192 +10,26 @@ | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #define pr_fmt(fmt) "PMU: " fmt | ||||
| 
 | ||||
| #include <linux/cpumask.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/of_device.h> | ||||
| #include <linux/platform_device.h> | ||||
| 
 | ||||
| #include <asm/pmu.h> | ||||
| 
 | ||||
| static volatile long pmu_lock; | ||||
| /*
 | ||||
|  * PMU locking to ensure mutual exclusion between different subsystems. | ||||
|  */ | ||||
| static unsigned long pmu_lock[BITS_TO_LONGS(ARM_NUM_PMU_DEVICES)]; | ||||
| 
 | ||||
| static struct platform_device *pmu_devices[ARM_NUM_PMU_DEVICES]; | ||||
| 
 | ||||
| static int __devinit pmu_register(struct platform_device *pdev, | ||||
| 					enum arm_pmu_type type) | ||||
| { | ||||
| 	if (type < 0 || type >= ARM_NUM_PMU_DEVICES) { | ||||
| 		pr_warning("received registration request for unknown " | ||||
| 				"PMU device type %d\n", type); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (pmu_devices[type]) { | ||||
| 		pr_warning("rejecting duplicate registration of PMU device " | ||||
| 			"type %d.", type); | ||||
| 		return -ENOSPC; | ||||
| 	} | ||||
| 
 | ||||
| 	pr_info("registered new PMU device of type %d\n", type); | ||||
| 	pmu_devices[type] = pdev; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| #define OF_MATCH_PMU(_name, _type) {	\ | ||||
| 	.compatible = _name,		\ | ||||
| 	.data = (void *)_type,		\ | ||||
| } | ||||
| 
 | ||||
| #define OF_MATCH_CPU(name)	OF_MATCH_PMU(name, ARM_PMU_DEVICE_CPU) | ||||
| 
 | ||||
| static struct of_device_id armpmu_of_device_ids[] = { | ||||
| 	OF_MATCH_CPU("arm,cortex-a9-pmu"), | ||||
| 	OF_MATCH_CPU("arm,cortex-a8-pmu"), | ||||
| 	OF_MATCH_CPU("arm,arm1136-pmu"), | ||||
| 	OF_MATCH_CPU("arm,arm1176-pmu"), | ||||
| 	{}, | ||||
| }; | ||||
| 
 | ||||
| #define PLAT_MATCH_PMU(_name, _type) {	\ | ||||
| 	.name		= _name,	\ | ||||
| 	.driver_data	= _type,	\ | ||||
| } | ||||
| 
 | ||||
| #define PLAT_MATCH_CPU(_name)	PLAT_MATCH_PMU(_name, ARM_PMU_DEVICE_CPU) | ||||
| 
 | ||||
| static struct platform_device_id armpmu_plat_device_ids[] = { | ||||
| 	PLAT_MATCH_CPU("arm-pmu"), | ||||
| 	{}, | ||||
| }; | ||||
| 
 | ||||
| enum arm_pmu_type armpmu_device_type(struct platform_device *pdev) | ||||
| { | ||||
| 	const struct of_device_id	*of_id; | ||||
| 	const struct platform_device_id *pdev_id; | ||||
| 
 | ||||
| 	/* provided by of_device_id table */ | ||||
| 	if (pdev->dev.of_node) { | ||||
| 		of_id = of_match_device(armpmu_of_device_ids, &pdev->dev); | ||||
| 		BUG_ON(!of_id); | ||||
| 		return (enum arm_pmu_type)of_id->data; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Provided by platform_device_id table */ | ||||
| 	pdev_id = platform_get_device_id(pdev); | ||||
| 	BUG_ON(!pdev_id); | ||||
| 	return pdev_id->driver_data; | ||||
| } | ||||
| 
 | ||||
| static int __devinit armpmu_device_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	return pmu_register(pdev, armpmu_device_type(pdev)); | ||||
| } | ||||
| 
 | ||||
| static struct platform_driver armpmu_driver = { | ||||
| 	.driver		= { | ||||
| 		.name	= "arm-pmu", | ||||
| 		.of_match_table = armpmu_of_device_ids, | ||||
| 	}, | ||||
| 	.probe		= armpmu_device_probe, | ||||
| 	.id_table	= armpmu_plat_device_ids, | ||||
| }; | ||||
| 
 | ||||
| static int __init register_pmu_driver(void) | ||||
| { | ||||
| 	return platform_driver_register(&armpmu_driver); | ||||
| } | ||||
| device_initcall(register_pmu_driver); | ||||
| 
 | ||||
| struct platform_device * | ||||
| int | ||||
| reserve_pmu(enum arm_pmu_type type) | ||||
| { | ||||
| 	struct platform_device *pdev; | ||||
| 
 | ||||
| 	if (test_and_set_bit_lock(type, &pmu_lock)) { | ||||
| 		pdev = ERR_PTR(-EBUSY); | ||||
| 	} else if (pmu_devices[type] == NULL) { | ||||
| 		clear_bit_unlock(type, &pmu_lock); | ||||
| 		pdev = ERR_PTR(-ENODEV); | ||||
| 	} else { | ||||
| 		pdev = pmu_devices[type]; | ||||
| 	} | ||||
| 
 | ||||
| 	return pdev; | ||||
| 	return test_and_set_bit_lock(type, pmu_lock) ? -EBUSY : 0; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(reserve_pmu); | ||||
| 
 | ||||
| int | ||||
| void | ||||
| release_pmu(enum arm_pmu_type type) | ||||
| { | ||||
| 	if (WARN_ON(!pmu_devices[type])) | ||||
| 		return -EINVAL; | ||||
| 	clear_bit_unlock(type, &pmu_lock); | ||||
| 	return 0; | ||||
| 	clear_bit_unlock(type, pmu_lock); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(release_pmu); | ||||
| 
 | ||||
| static int | ||||
| set_irq_affinity(int irq, | ||||
| 		 unsigned int cpu) | ||||
| { | ||||
| #ifdef CONFIG_SMP | ||||
| 	int err = irq_set_affinity(irq, cpumask_of(cpu)); | ||||
| 	if (err) | ||||
| 		pr_warning("unable to set irq affinity (irq=%d, cpu=%u)\n", | ||||
| 			   irq, cpu); | ||||
| 	return err; | ||||
| #else | ||||
| 	return -EINVAL; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| init_cpu_pmu(void) | ||||
| { | ||||
| 	int i, irqs, err = 0; | ||||
| 	struct platform_device *pdev = pmu_devices[ARM_PMU_DEVICE_CPU]; | ||||
| 
 | ||||
| 	if (!pdev) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	irqs = pdev->num_resources; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * If we have a single PMU interrupt that we can't shift, assume that | ||||
| 	 * we're running on a uniprocessor machine and continue. | ||||
| 	 */ | ||||
| 	if (irqs == 1 && !irq_can_set_affinity(platform_get_irq(pdev, 0))) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	for (i = 0; i < irqs; ++i) { | ||||
| 		err = set_irq_affinity(platform_get_irq(pdev, i), i); | ||||
| 		if (err) | ||||
| 			break; | ||||
| 	} | ||||
| 
 | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| int | ||||
| init_pmu(enum arm_pmu_type type) | ||||
| { | ||||
| 	int err = 0; | ||||
| 
 | ||||
| 	switch (type) { | ||||
| 	case ARM_PMU_DEVICE_CPU: | ||||
| 		err = init_cpu_pmu(); | ||||
| 		break; | ||||
| 	default: | ||||
| 		pr_warning("attempt to initialise PMU of unknown " | ||||
| 			   "type %d\n", type); | ||||
| 		err = -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	return err; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(init_pmu); | ||||
|  | ||||
| @ -820,25 +820,8 @@ static struct machine_desc * __init setup_machine_tags(unsigned int nr) | ||||
| 
 | ||||
| 	if (__atags_pointer) | ||||
| 		tags = phys_to_virt(__atags_pointer); | ||||
| 	else if (mdesc->boot_params) { | ||||
| #ifdef CONFIG_MMU | ||||
| 		/*
 | ||||
| 		 * We still are executing with a minimal MMU mapping created | ||||
| 		 * with the presumption that the machine default for this | ||||
| 		 * is located in the first MB of RAM.  Anything else will | ||||
| 		 * fault and silently hang the kernel at this point. | ||||
| 		 */ | ||||
| 		if (mdesc->boot_params < PHYS_OFFSET || | ||||
| 		    mdesc->boot_params >= PHYS_OFFSET + SZ_1M) { | ||||
| 			printk(KERN_WARNING | ||||
| 			       "Default boot params at physical 0x%08lx out of reach\n", | ||||
| 			       mdesc->boot_params); | ||||
| 		} else | ||||
| #endif | ||||
| 		{ | ||||
| 			tags = phys_to_virt(mdesc->boot_params); | ||||
| 		} | ||||
| 	} | ||||
| 	else if (mdesc->atag_offset) | ||||
| 		tags = (void *)(PAGE_OFFSET + mdesc->atag_offset); | ||||
| 
 | ||||
| #if defined(CONFIG_DEPRECATED_PARAM_STRUCT) | ||||
| 	/*
 | ||||
|  | ||||
| @ -8,92 +8,61 @@ | ||||
| 	.text | ||||
| 
 | ||||
| /* | ||||
|  * Save CPU state for a suspend | ||||
|  *  r1 = v:p offset | ||||
|  *  r2 = suspend function arg0 | ||||
|  *  r3 = suspend function | ||||
|  * Save CPU state for a suspend.  This saves the CPU general purpose | ||||
|  * registers, and allocates space on the kernel stack to save the CPU | ||||
|  * specific registers and some other data for resume. | ||||
|  *  r0 = suspend function arg0 | ||||
|  *  r1 = suspend function | ||||
|  */ | ||||
| ENTRY(__cpu_suspend) | ||||
| 	stmfd	sp!, {r4 - r11, lr} | ||||
| #ifdef MULTI_CPU | ||||
| 	ldr	r10, =processor | ||||
| 	ldr	r5, [r10, #CPU_SLEEP_SIZE] @ size of CPU sleep state | ||||
| 	ldr	ip, [r10, #CPU_DO_RESUME] @ virtual resume function | ||||
| 	ldr	r4, [r10, #CPU_SLEEP_SIZE] @ size of CPU sleep state | ||||
| #else | ||||
| 	ldr	r5, =cpu_suspend_size | ||||
| 	ldr	ip, =cpu_do_resume | ||||
| 	ldr	r4, =cpu_suspend_size | ||||
| #endif | ||||
| 	mov	r6, sp			@ current virtual SP
 | ||||
| 	sub	sp, sp, r5		@ allocate CPU state on stack
 | ||||
| 	mov	r0, sp			@ save pointer to CPU save block
 | ||||
| 	add	ip, ip, r1		@ convert resume fn to phys
 | ||||
| 	stmfd	sp!, {r1, r6, ip}	@ save v:p, virt SP, phys resume fn
 | ||||
| 	ldr	r5, =sleep_save_sp | ||||
| 	add	r6, sp, r1		@ convert SP to phys
 | ||||
| 	stmfd	sp!, {r2, r3}		@ save suspend func arg and pointer
 | ||||
| 	mov	r5, sp			@ current virtual SP
 | ||||
| 	add	r4, r4, #12		@ Space for pgd, virt sp, phys resume fn
 | ||||
| 	sub	sp, sp, r4		@ allocate CPU state on stack
 | ||||
| 	stmfd	sp!, {r0, r1}		@ save suspend func arg and pointer
 | ||||
| 	add	r0, sp, #8		@ save pointer to save block
 | ||||
| 	mov	r1, r4			@ size of save block
 | ||||
| 	mov	r2, r5			@ virtual SP
 | ||||
| 	ldr	r3, =sleep_save_sp | ||||
| #ifdef CONFIG_SMP | ||||
| 	ALT_SMP(mrc p15, 0, lr, c0, c0, 5) | ||||
| 	ALT_UP(mov lr, #0) | ||||
| 	and	lr, lr, #15 | ||||
| 	str	r6, [r5, lr, lsl #2]	@ save phys SP
 | ||||
| #else | ||||
| 	str	r6, [r5]		@ save phys SP
 | ||||
| #endif | ||||
| #ifdef MULTI_CPU | ||||
| 	mov	lr, pc | ||||
| 	ldr	pc, [r10, #CPU_DO_SUSPEND] @ save CPU state | ||||
| #else | ||||
| 	bl	cpu_do_suspend | ||||
| #endif | ||||
| 
 | ||||
| 	@ flush data cache
 | ||||
| #ifdef MULTI_CACHE | ||||
| 	ldr	r10, =cpu_cache | ||||
| 	mov	lr, pc | ||||
| 	ldr	pc, [r10, #CACHE_FLUSH_KERN_ALL] | ||||
| #else | ||||
| 	bl	__cpuc_flush_kern_all | ||||
| 	add	r3, r3, lr, lsl #2 | ||||
| #endif | ||||
| 	bl	__cpu_suspend_save | ||||
| 	adr	lr, BSYM(cpu_suspend_abort) | ||||
| 	ldmfd	sp!, {r0, pc}		@ call suspend fn
 | ||||
| ENDPROC(__cpu_suspend) | ||||
| 	.ltorg | ||||
| 
 | ||||
| cpu_suspend_abort: | ||||
| 	ldmia	sp!, {r1 - r3}		@ pop v:p, virt SP, phys resume fn
 | ||||
| 	ldmia	sp!, {r1 - r3}		@ pop phys pgd, virt SP, phys resume fn
 | ||||
| 	teq	r0, #0 | ||||
| 	moveq	r0, #1			@ force non-zero value
 | ||||
| 	mov	sp, r2 | ||||
| 	ldmfd	sp!, {r4 - r11, pc} | ||||
| ENDPROC(cpu_suspend_abort) | ||||
| 
 | ||||
| /* | ||||
|  * r0 = control register value | ||||
|  * r1 = v:p offset (preserved by cpu_do_resume) | ||||
|  * r2 = phys page table base | ||||
|  * r3 = L1 section flags | ||||
|  */ | ||||
| ENTRY(cpu_resume_mmu) | ||||
| 	adr	r4, cpu_resume_turn_mmu_on | ||||
| 	mov	r4, r4, lsr #20 | ||||
| 	orr	r3, r3, r4, lsl #20 | ||||
| 	ldr	r5, [r2, r4, lsl #2]	@ save old mapping
 | ||||
| 	str	r3, [r2, r4, lsl #2]	@ setup 1:1 mapping for mmu code
 | ||||
| 	sub	r2, r2, r1 | ||||
| 	ldr	r3, =cpu_resume_after_mmu | ||||
| 	bic	r1, r0, #CR_C		@ ensure D-cache is disabled | ||||
| 	b	cpu_resume_turn_mmu_on | ||||
| ENDPROC(cpu_resume_mmu) | ||||
| 	.ltorg | ||||
| 	.align	5
 | ||||
| cpu_resume_turn_mmu_on: | ||||
| 	mcr	p15, 0, r1, c1, c0, 0	@ turn on MMU, I-cache, etc
 | ||||
| 	mrc	p15, 0, r1, c0, c0, 0	@ read id reg
 | ||||
| 	mov	r1, r1 | ||||
| 	mov	r1, r1 | ||||
| ENTRY(cpu_resume_mmu) | ||||
| 	ldr	r3, =cpu_resume_after_mmu | ||||
| 	mcr	p15, 0, r0, c1, c0, 0	@ turn on MMU, I-cache, etc
 | ||||
| 	mrc	p15, 0, r0, c0, c0, 0	@ read id reg
 | ||||
| 	mov	r0, r0 | ||||
| 	mov	r0, r0 | ||||
| 	mov	pc, r3			@ jump to virtual address
 | ||||
| ENDPROC(cpu_resume_turn_mmu_on) | ||||
| ENDPROC(cpu_resume_mmu) | ||||
| cpu_resume_after_mmu: | ||||
| 	str	r5, [r2, r4, lsl #2]	@ restore old mapping
 | ||||
| 	mcr	p15, 0, r0, c1, c0, 0	@ turn on D-cache
 | ||||
| 	bl	cpu_init		@ restore the und/abt/irq banked regs
 | ||||
| 	mov	r0, #0			@ return zero on success
 | ||||
| 	ldmfd	sp!, {r4 - r11, pc} | ||||
| @ -119,7 +88,7 @@ ENTRY(cpu_resume) | ||||
| 	ldr	r0, sleep_save_sp	@ stack phys addr
 | ||||
| #endif | ||||
| 	setmode	PSR_I_BIT | PSR_F_BIT | SVC_MODE, r1  @ set SVC, irqs off
 | ||||
| 	@ load v:p, stack, resume fn
 | ||||
| 	@ load phys pgd, stack, resume fn
 | ||||
|   ARM(	ldmia	r0!, {r1, sp, pc}	) | ||||
| THUMB(	ldmia	r0!, {r1, r2, r3}	) | ||||
| THUMB(	mov	sp, r2			) | ||||
|  | ||||
							
								
								
									
										72
									
								
								arch/arm/kernel/suspend.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								arch/arm/kernel/suspend.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,72 @@ | ||||
| #include <linux/init.h> | ||||
| 
 | ||||
| #include <asm/pgalloc.h> | ||||
| #include <asm/pgtable.h> | ||||
| #include <asm/memory.h> | ||||
| #include <asm/suspend.h> | ||||
| #include <asm/tlbflush.h> | ||||
| 
 | ||||
| static pgd_t *suspend_pgd; | ||||
| 
 | ||||
| extern int __cpu_suspend(unsigned long, int (*)(unsigned long)); | ||||
| extern void cpu_resume_mmu(void); | ||||
| 
 | ||||
| /*
 | ||||
|  * This is called by __cpu_suspend() to save the state, and do whatever | ||||
|  * flushing is required to ensure that when the CPU goes to sleep we have | ||||
|  * the necessary data available when the caches are not searched. | ||||
|  */ | ||||
| void __cpu_suspend_save(u32 *ptr, u32 ptrsz, u32 sp, u32 *save_ptr) | ||||
| { | ||||
| 	*save_ptr = virt_to_phys(ptr); | ||||
| 
 | ||||
| 	/* This must correspond to the LDM in cpu_resume() assembly */ | ||||
| 	*ptr++ = virt_to_phys(suspend_pgd); | ||||
| 	*ptr++ = sp; | ||||
| 	*ptr++ = virt_to_phys(cpu_do_resume); | ||||
| 
 | ||||
| 	cpu_do_suspend(ptr); | ||||
| 
 | ||||
| 	flush_cache_all(); | ||||
| 	outer_clean_range(*save_ptr, *save_ptr + ptrsz); | ||||
| 	outer_clean_range(virt_to_phys(save_ptr), | ||||
| 			  virt_to_phys(save_ptr) + sizeof(*save_ptr)); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Hide the first two arguments to __cpu_suspend - these are an implementation | ||||
|  * detail which platform code shouldn't have to know about. | ||||
|  */ | ||||
| int cpu_suspend(unsigned long arg, int (*fn)(unsigned long)) | ||||
| { | ||||
| 	struct mm_struct *mm = current->active_mm; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (!suspend_pgd) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Provide a temporary page table with an identity mapping for | ||||
| 	 * the MMU-enable code, required for resuming.  On successful | ||||
| 	 * resume (indicated by a zero return code), we need to switch | ||||
| 	 * back to the correct page tables. | ||||
| 	 */ | ||||
| 	ret = __cpu_suspend(arg, fn); | ||||
| 	if (ret == 0) { | ||||
| 		cpu_switch_mm(mm->pgd, mm); | ||||
| 		local_flush_tlb_all(); | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int __init cpu_suspend_init(void) | ||||
| { | ||||
| 	suspend_pgd = pgd_alloc(&init_mm); | ||||
| 	if (suspend_pgd) { | ||||
| 		unsigned long addr = virt_to_phys(cpu_resume_mmu); | ||||
| 		identity_mapping_add(suspend_pgd, addr, addr + SECTION_SIZE); | ||||
| 	} | ||||
| 	return suspend_pgd ? 0 : -ENOMEM; | ||||
| } | ||||
| core_initcall(cpu_suspend_init); | ||||
| @ -12,6 +12,7 @@ | ||||
| 
 | ||||
| #include <linux/module.h> | ||||
| #include <linux/pm.h> | ||||
| #include <linux/dma-mapping.h> | ||||
| 
 | ||||
| #include <asm/irq.h> | ||||
| #include <asm/mach/arch.h> | ||||
| @ -319,6 +320,7 @@ static void at91sam9g45_poweroff(void) | ||||
| static void __init at91sam9g45_map_io(void) | ||||
| { | ||||
| 	at91_init_sram(0, AT91SAM9G45_SRAM_BASE, AT91SAM9G45_SRAM_SIZE); | ||||
| 	init_consistent_dma_size(SZ_4M); | ||||
| } | ||||
| 
 | ||||
| static void __init at91sam9g45_initialize(void) | ||||
|  | ||||
| @ -128,8 +128,6 @@ | ||||
| #define AT91SAM9G45_EHCI_BASE	0x00800000	/* USB Host controller (EHCI) */ | ||||
| #define AT91SAM9G45_VDEC_BASE	0x00900000	/* Video Decoder Controller */ | ||||
| 
 | ||||
| #define CONSISTENT_DMA_SIZE	SZ_4M | ||||
| 
 | ||||
| /*
 | ||||
|  * DMA peripheral identifiers | ||||
|  * for hardware handshaking interface | ||||
|  | ||||
| @ -25,9 +25,4 @@ | ||||
| 
 | ||||
| #define PLAT_PHYS_OFFSET CFG_GLOBAL_RAM_BASE | ||||
| 
 | ||||
| /*
 | ||||
|  * Maximum DMA memory allowed is 14M | ||||
|  */ | ||||
| #define CONSISTENT_DMA_SIZE (SZ_16M - SZ_2M) | ||||
| 
 | ||||
| #endif | ||||
|  | ||||
| @ -13,6 +13,7 @@ | ||||
| *****************************************************************************/ | ||||
| 
 | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/dma-mapping.h> | ||||
| #include <asm/mach/map.h> | ||||
| 
 | ||||
| #include <mach/hardware.h> | ||||
| @ -53,4 +54,6 @@ void __init bcmring_map_io(void) | ||||
| { | ||||
| 
 | ||||
| 	iotable_init(bcmring_io_desc, ARRAY_SIZE(bcmring_io_desc)); | ||||
| 	/* Maximum DMA memory allowed is 14M */ | ||||
| 	init_consistent_dma_size(14 << 20); | ||||
| } | ||||
|  | ||||
| @ -64,7 +64,7 @@ void __init autcpu12_map_io(void) | ||||
| 
 | ||||
| MACHINE_START(AUTCPU12, "autronix autcpu12") | ||||
| 	/* Maintainer: Thomas Gleixner */ | ||||
| 	.boot_params	= 0xc0020000, | ||||
| 	.atag_offset	= 0x20000, | ||||
| 	.map_io		= autcpu12_map_io, | ||||
| 	.init_irq	= clps711x_init_irq, | ||||
| 	.timer		= &clps711x_timer, | ||||
|  | ||||
| @ -55,7 +55,7 @@ static void __init cdb89712_map_io(void) | ||||
| 
 | ||||
| MACHINE_START(CDB89712, "Cirrus-CDB89712") | ||||
| 	/* Maintainer: Ray Lehtiniemi */ | ||||
| 	.boot_params	= 0xc0000100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= cdb89712_map_io, | ||||
| 	.init_irq	= clps711x_init_irq, | ||||
| 	.timer		= &clps711x_timer, | ||||
|  | ||||
| @ -56,7 +56,7 @@ static void __init ceiva_map_io(void) | ||||
| 
 | ||||
| MACHINE_START(CEIVA, "CEIVA/Polaroid Photo MAX Digital Picture Frame") | ||||
| 	/* Maintainer: Rob Scott */ | ||||
| 	.boot_params	= 0xc0000100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= ceiva_map_io, | ||||
| 	.init_irq	= clps711x_init_irq, | ||||
| 	.timer		= &clps711x_timer, | ||||
|  | ||||
| @ -37,7 +37,7 @@ fixup_clep7312(struct machine_desc *desc, struct tag *tags, | ||||
| 
 | ||||
| MACHINE_START(CLEP7212, "Cirrus Logic 7212/7312") | ||||
| 	/* Maintainer: Nobody */ | ||||
| 	.boot_params	= 0xc0000100, | ||||
| 	.atag_offset	= 0x0100, | ||||
| 	.fixup		= fixup_clep7312, | ||||
| 	.map_io		= clps711x_map_io, | ||||
| 	.init_irq	= clps711x_init_irq, | ||||
|  | ||||
| @ -57,7 +57,7 @@ fixup_edb7211(struct machine_desc *desc, struct tag *tags, | ||||
| 
 | ||||
| MACHINE_START(EDB7211, "CL-EDB7211 (EP7211 eval board)") | ||||
| 	/* Maintainer: Jon McClintock */ | ||||
| 	.boot_params	= 0xc0020100,	/* 0xc0000000 - 0xc001ffff can be video RAM */ | ||||
| 	.atag_offset	= 0x20100,	/* 0xc0000000 - 0xc001ffff can be video RAM */ | ||||
| 	.fixup		= fixup_edb7211, | ||||
| 	.map_io		= edb7211_map_io, | ||||
| 	.reserve	= edb7211_reserve, | ||||
|  | ||||
| @ -75,7 +75,6 @@ fortunet_fixup(struct machine_desc *desc, struct tag *tags, | ||||
| 
 | ||||
| MACHINE_START(FORTUNET, "ARM-FortuNet") | ||||
| 	/* Maintainer: FortuNet Inc. */ | ||||
| 	.boot_params	= 0x00000000, | ||||
| 	.fixup		= fortunet_fixup, | ||||
| 	.map_io		= clps711x_map_io, | ||||
| 	.init_irq	= clps711x_init_irq, | ||||
|  | ||||
| @ -89,7 +89,7 @@ static void __init p720t_map_io(void) | ||||
| 
 | ||||
| MACHINE_START(P720T, "ARM-Prospector720T") | ||||
| 	/* Maintainer: ARM Ltd/Deep Blue Solutions Ltd */ | ||||
| 	.boot_params	= 0xc0000100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.fixup		= fixup_p720t, | ||||
| 	.map_io		= p720t_map_io, | ||||
| 	.init_irq	= clps711x_init_irq, | ||||
|  | ||||
| @ -197,7 +197,7 @@ static void __init cns3420_map_io(void) | ||||
| } | ||||
| 
 | ||||
| MACHINE_START(CNS3420VB, "Cavium Networks CNS3420 Validation Board") | ||||
| 	.boot_params	= 0x00000100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= cns3420_map_io, | ||||
| 	.init_irq	= cns3xxx_init_irq, | ||||
| 	.timer		= &cns3xxx_timer, | ||||
|  | ||||
| @ -676,7 +676,7 @@ static void __init da830_evm_map_io(void) | ||||
| } | ||||
| 
 | ||||
| MACHINE_START(DAVINCI_DA830_EVM, "DaVinci DA830/OMAP-L137/AM17x EVM") | ||||
| 	.boot_params	= (DA8XX_DDR_BASE + 0x100), | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= da830_evm_map_io, | ||||
| 	.init_irq	= cp_intc_init, | ||||
| 	.timer		= &davinci_timer, | ||||
|  | ||||
| @ -1291,7 +1291,7 @@ static void __init da850_evm_map_io(void) | ||||
| } | ||||
| 
 | ||||
| MACHINE_START(DAVINCI_DA850_EVM, "DaVinci DA850/OMAP-L138/AM18x EVM") | ||||
| 	.boot_params	= (DA8XX_DDR_BASE + 0x100), | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= da850_evm_map_io, | ||||
| 	.init_irq	= cp_intc_init, | ||||
| 	.timer		= &davinci_timer, | ||||
|  | ||||
| @ -351,7 +351,7 @@ static __init void dm355_evm_init(void) | ||||
| } | ||||
| 
 | ||||
| MACHINE_START(DAVINCI_DM355_EVM, "DaVinci DM355 EVM") | ||||
| 	.boot_params  = (0x80000100), | ||||
| 	.atag_offset  = 0x100, | ||||
| 	.map_io	      = dm355_evm_map_io, | ||||
| 	.init_irq     = davinci_irq_init, | ||||
| 	.timer	      = &davinci_timer, | ||||
|  | ||||
| @ -270,7 +270,7 @@ static __init void dm355_leopard_init(void) | ||||
| } | ||||
| 
 | ||||
| MACHINE_START(DM355_LEOPARD, "DaVinci DM355 leopard") | ||||
| 	.boot_params  = (0x80000100), | ||||
| 	.atag_offset  = 0x100, | ||||
| 	.map_io	      = dm355_leopard_map_io, | ||||
| 	.init_irq     = davinci_irq_init, | ||||
| 	.timer	      = &davinci_timer, | ||||
|  | ||||
| @ -612,7 +612,7 @@ static __init void dm365_evm_init(void) | ||||
| } | ||||
| 
 | ||||
| MACHINE_START(DAVINCI_DM365_EVM, "DaVinci DM365 EVM") | ||||
| 	.boot_params	= (0x80000100), | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= dm365_evm_map_io, | ||||
| 	.init_irq	= davinci_irq_init, | ||||
| 	.timer		= &davinci_timer, | ||||
|  | ||||
| @ -712,7 +712,7 @@ static __init void davinci_evm_init(void) | ||||
| 
 | ||||
| MACHINE_START(DAVINCI_EVM, "DaVinci DM644x EVM") | ||||
| 	/* Maintainer: MontaVista Software <source@mvista.com> */ | ||||
| 	.boot_params  = (DAVINCI_DDR_BASE + 0x100), | ||||
| 	.atag_offset  = 0x100, | ||||
| 	.map_io	      = davinci_evm_map_io, | ||||
| 	.init_irq     = davinci_irq_init, | ||||
| 	.timer	      = &davinci_timer, | ||||
|  | ||||
| @ -792,7 +792,7 @@ static __init void evm_init(void) | ||||
| } | ||||
| 
 | ||||
| MACHINE_START(DAVINCI_DM6467_EVM, "DaVinci DM646x EVM") | ||||
| 	.boot_params  = (0x80000100), | ||||
| 	.atag_offset  = 0x100, | ||||
| 	.map_io       = davinci_map_io, | ||||
| 	.init_irq     = davinci_irq_init, | ||||
| 	.timer        = &davinci_timer, | ||||
| @ -801,7 +801,7 @@ MACHINE_START(DAVINCI_DM6467_EVM, "DaVinci DM646x EVM") | ||||
| MACHINE_END | ||||
| 
 | ||||
| MACHINE_START(DAVINCI_DM6467TEVM, "DaVinci DM6467T EVM") | ||||
| 	.boot_params  = (0x80000100), | ||||
| 	.atag_offset  = 0x100, | ||||
| 	.map_io       = davinci_map_io, | ||||
| 	.init_irq     = davinci_irq_init, | ||||
| 	.timer        = &davinci_timer, | ||||
|  | ||||
| @ -566,7 +566,7 @@ static void __init mityomapl138_map_io(void) | ||||
| } | ||||
| 
 | ||||
| MACHINE_START(MITYOMAPL138, "MityDSP-L138/MityARM-1808") | ||||
| 	.boot_params	= (DA8XX_DDR_BASE + 0x100), | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= mityomapl138_map_io, | ||||
| 	.init_irq	= cp_intc_init, | ||||
| 	.timer		= &davinci_timer, | ||||
|  | ||||
| @ -272,7 +272,7 @@ static __init void davinci_ntosd2_init(void) | ||||
| 
 | ||||
| MACHINE_START(NEUROS_OSD2, "Neuros OSD2") | ||||
| 	/* Maintainer: Neuros Technologies <neuros@groups.google.com> */ | ||||
| 	.boot_params	= (DAVINCI_DDR_BASE + 0x100), | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		 = davinci_ntosd2_map_io, | ||||
| 	.init_irq	= davinci_irq_init, | ||||
| 	.timer		= &davinci_timer, | ||||
|  | ||||
| @ -338,7 +338,7 @@ static void __init omapl138_hawk_map_io(void) | ||||
| } | ||||
| 
 | ||||
| MACHINE_START(OMAPL138_HAWKBOARD, "AM18x/OMAP-L138 Hawkboard") | ||||
| 	.boot_params	= (DA8XX_DDR_BASE + 0x100), | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= omapl138_hawk_map_io, | ||||
| 	.init_irq	= cp_intc_init, | ||||
| 	.timer		= &davinci_timer, | ||||
|  | ||||
| @ -151,7 +151,7 @@ static __init void davinci_sffsdr_init(void) | ||||
| 
 | ||||
| MACHINE_START(SFFSDR, "Lyrtech SFFSDR") | ||||
| 	/* Maintainer: Hugo Villeneuve hugo.villeneuve@lyrtech.com */ | ||||
| 	.boot_params  = (DAVINCI_DDR_BASE + 0x100), | ||||
| 	.atag_offset  = 0x100, | ||||
| 	.map_io	      = davinci_sffsdr_map_io, | ||||
| 	.init_irq     = davinci_irq_init, | ||||
| 	.timer	      = &davinci_timer, | ||||
|  | ||||
| @ -277,7 +277,7 @@ console_initcall(tnetv107x_evm_console_init); | ||||
| #endif | ||||
| 
 | ||||
| MACHINE_START(TNETV107X, "TNETV107X EVM") | ||||
| 	.boot_params	= (TNETV107X_DDR_BASE + 0x100), | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= tnetv107x_init, | ||||
| 	.init_irq	= cp_intc_init, | ||||
| 	.timer		= &davinci_timer, | ||||
|  | ||||
| @ -12,6 +12,7 @@ | ||||
| #include <linux/io.h> | ||||
| #include <linux/etherdevice.h> | ||||
| #include <linux/davinci_emac.h> | ||||
| #include <linux/dma-mapping.h> | ||||
| 
 | ||||
| #include <asm/tlb.h> | ||||
| #include <asm/mach/map.h> | ||||
| @ -86,6 +87,8 @@ void __init davinci_common_init(struct davinci_soc_info *soc_info) | ||||
| 		iotable_init(davinci_soc_info.io_desc, | ||||
| 				davinci_soc_info.io_desc_num); | ||||
| 
 | ||||
| 	init_consistent_dma_size(14 << 20); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Normally devicemaps_init() would flush caches and tlb after | ||||
| 	 * mdesc->map_io(), but we must also do it here because of the CPU | ||||
|  | ||||
| @ -36,9 +36,4 @@ | ||||
| #define DDR2_MCLKSTOPEN_BIT	BIT(30) | ||||
| #define DDR2_LPMODEN_BIT	BIT(31) | ||||
| 
 | ||||
| /*
 | ||||
|  * Increase size of DMA-consistent memory region | ||||
|  */ | ||||
| #define CONSISTENT_DMA_SIZE (14<<20) | ||||
| 
 | ||||
| #endif /* __ASM_ARCH_MEMORY_H */ | ||||
|  | ||||
| @ -87,7 +87,7 @@ static void __init cm_a510_init(void) | ||||
| } | ||||
| 
 | ||||
| MACHINE_START(CM_A510, "Compulab CM-A510 Board") | ||||
| 	.boot_params	= 0x00000100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.init_machine	= cm_a510_init, | ||||
| 	.map_io		= dove_map_io, | ||||
| 	.init_early	= dove_init_early, | ||||
|  | ||||
| @ -94,7 +94,7 @@ static void __init dove_db_init(void) | ||||
| } | ||||
| 
 | ||||
| MACHINE_START(DOVE_DB, "Marvell DB-MV88AP510-BP Development Board") | ||||
| 	.boot_params	= 0x00000100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.init_machine	= dove_db_init, | ||||
| 	.map_io		= dove_map_io, | ||||
| 	.init_early	= dove_init_early, | ||||
|  | ||||
| @ -280,7 +280,7 @@ arch_initcall(ebsa110_init); | ||||
| 
 | ||||
| MACHINE_START(EBSA110, "EBSA110") | ||||
| 	/* Maintainer: Russell King */ | ||||
| 	.boot_params	= 0x00000400, | ||||
| 	.atag_offset	= 0x400, | ||||
| 	.reserve_lp0	= 1, | ||||
| 	.reserve_lp2	= 1, | ||||
| 	.soft_reboot	= 1, | ||||
|  | ||||
| @ -33,7 +33,7 @@ static void __init adssphere_init_machine(void) | ||||
| 
 | ||||
| MACHINE_START(ADSSPHERE, "ADS Sphere board") | ||||
| 	/* Maintainer: Lennert Buytenhek <buytenh@wantstofly.org> */ | ||||
| 	.boot_params	= EP93XX_SDCE3_PHYS_BASE_SYNC + 0x100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= ep93xx_map_io, | ||||
| 	.init_irq	= ep93xx_init_irq, | ||||
| 	.timer		= &ep93xx_timer, | ||||
|  | ||||
| @ -240,7 +240,7 @@ static void __init edb93xx_init_machine(void) | ||||
| #ifdef CONFIG_MACH_EDB9301 | ||||
| MACHINE_START(EDB9301, "Cirrus Logic EDB9301 Evaluation Board") | ||||
| 	/* Maintainer: H Hartley Sweeten <hsweeten@visionengravers.com> */ | ||||
| 	.boot_params	= EP93XX_SDCE3_PHYS_BASE_SYNC + 0x100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= ep93xx_map_io, | ||||
| 	.init_irq	= ep93xx_init_irq, | ||||
| 	.timer		= &ep93xx_timer, | ||||
| @ -251,7 +251,7 @@ MACHINE_END | ||||
| #ifdef CONFIG_MACH_EDB9302 | ||||
| MACHINE_START(EDB9302, "Cirrus Logic EDB9302 Evaluation Board") | ||||
| 	/* Maintainer: George Kashperko <george@chas.com.ua> */ | ||||
| 	.boot_params	= EP93XX_SDCE3_PHYS_BASE_SYNC + 0x100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= ep93xx_map_io, | ||||
| 	.init_irq	= ep93xx_init_irq, | ||||
| 	.timer		= &ep93xx_timer, | ||||
| @ -262,7 +262,7 @@ MACHINE_END | ||||
| #ifdef CONFIG_MACH_EDB9302A | ||||
| MACHINE_START(EDB9302A, "Cirrus Logic EDB9302A Evaluation Board") | ||||
| 	/* Maintainer: Lennert Buytenhek <buytenh@wantstofly.org> */ | ||||
| 	.boot_params	= EP93XX_SDCE0_PHYS_BASE + 0x100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= ep93xx_map_io, | ||||
| 	.init_irq	= ep93xx_init_irq, | ||||
| 	.timer		= &ep93xx_timer, | ||||
| @ -273,7 +273,7 @@ MACHINE_END | ||||
| #ifdef CONFIG_MACH_EDB9307 | ||||
| MACHINE_START(EDB9307, "Cirrus Logic EDB9307 Evaluation Board") | ||||
| 	/* Maintainer: Herbert Valerio Riedel <hvr@gnu.org> */ | ||||
| 	.boot_params	= EP93XX_SDCE3_PHYS_BASE_SYNC + 0x100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= ep93xx_map_io, | ||||
| 	.init_irq	= ep93xx_init_irq, | ||||
| 	.timer		= &ep93xx_timer, | ||||
| @ -284,7 +284,7 @@ MACHINE_END | ||||
| #ifdef CONFIG_MACH_EDB9307A | ||||
| MACHINE_START(EDB9307A, "Cirrus Logic EDB9307A Evaluation Board") | ||||
| 	/* Maintainer: H Hartley Sweeten <hsweeten@visionengravers.com> */ | ||||
| 	.boot_params	= EP93XX_SDCE0_PHYS_BASE + 0x100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= ep93xx_map_io, | ||||
| 	.init_irq	= ep93xx_init_irq, | ||||
| 	.timer		= &ep93xx_timer, | ||||
| @ -295,7 +295,7 @@ MACHINE_END | ||||
| #ifdef CONFIG_MACH_EDB9312 | ||||
| MACHINE_START(EDB9312, "Cirrus Logic EDB9312 Evaluation Board") | ||||
| 	/* Maintainer: Toufeeq Hussain <toufeeq_hussain@infosys.com> */ | ||||
| 	.boot_params	= EP93XX_SDCE3_PHYS_BASE_SYNC + 0x100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= ep93xx_map_io, | ||||
| 	.init_irq	= ep93xx_init_irq, | ||||
| 	.timer		= &ep93xx_timer, | ||||
| @ -306,7 +306,7 @@ MACHINE_END | ||||
| #ifdef CONFIG_MACH_EDB9315 | ||||
| MACHINE_START(EDB9315, "Cirrus Logic EDB9315 Evaluation Board") | ||||
| 	/* Maintainer: Lennert Buytenhek <buytenh@wantstofly.org> */ | ||||
| 	.boot_params	= EP93XX_SDCE3_PHYS_BASE_SYNC + 0x100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= ep93xx_map_io, | ||||
| 	.init_irq	= ep93xx_init_irq, | ||||
| 	.timer		= &ep93xx_timer, | ||||
| @ -317,7 +317,7 @@ MACHINE_END | ||||
| #ifdef CONFIG_MACH_EDB9315A | ||||
| MACHINE_START(EDB9315A, "Cirrus Logic EDB9315A Evaluation Board") | ||||
| 	/* Maintainer: Lennert Buytenhek <buytenh@wantstofly.org> */ | ||||
| 	.boot_params	= EP93XX_SDCE0_PHYS_BASE + 0x100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= ep93xx_map_io, | ||||
| 	.init_irq	= ep93xx_init_irq, | ||||
| 	.timer		= &ep93xx_timer, | ||||
|  | ||||
| @ -33,7 +33,7 @@ static void __init gesbc9312_init_machine(void) | ||||
| 
 | ||||
| MACHINE_START(GESBC9312, "Glomation GESBC-9312-sx") | ||||
| 	/* Maintainer: Lennert Buytenhek <buytenh@wantstofly.org> */ | ||||
| 	.boot_params	= EP93XX_SDCE3_PHYS_BASE_SYNC + 0x100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= ep93xx_map_io, | ||||
| 	.init_irq	= ep93xx_init_irq, | ||||
| 	.timer		= &ep93xx_timer, | ||||
|  | ||||
| @ -77,7 +77,7 @@ static void __init micro9_init_machine(void) | ||||
| #ifdef CONFIG_MACH_MICRO9H | ||||
| MACHINE_START(MICRO9, "Contec Micro9-High") | ||||
| 	/* Maintainer: Hubert Feurstein <hubert.feurstein@contec.at> */ | ||||
| 	.boot_params	= EP93XX_SDCE3_PHYS_BASE_SYNC + 0x100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= ep93xx_map_io, | ||||
| 	.init_irq	= ep93xx_init_irq, | ||||
| 	.timer		= &ep93xx_timer, | ||||
| @ -88,7 +88,7 @@ MACHINE_END | ||||
| #ifdef CONFIG_MACH_MICRO9M | ||||
| MACHINE_START(MICRO9M, "Contec Micro9-Mid") | ||||
| 	/* Maintainer: Hubert Feurstein <hubert.feurstein@contec.at> */ | ||||
| 	.boot_params	= EP93XX_SDCE3_PHYS_BASE_ASYNC + 0x100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= ep93xx_map_io, | ||||
| 	.init_irq	= ep93xx_init_irq, | ||||
| 	.timer		= &ep93xx_timer, | ||||
| @ -99,7 +99,7 @@ MACHINE_END | ||||
| #ifdef CONFIG_MACH_MICRO9L | ||||
| MACHINE_START(MICRO9L, "Contec Micro9-Lite") | ||||
| 	/* Maintainer: Hubert Feurstein <hubert.feurstein@contec.at> */ | ||||
| 	.boot_params	= EP93XX_SDCE3_PHYS_BASE_SYNC + 0x100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= ep93xx_map_io, | ||||
| 	.init_irq	= ep93xx_init_irq, | ||||
| 	.timer		= &ep93xx_timer, | ||||
| @ -110,7 +110,7 @@ MACHINE_END | ||||
| #ifdef CONFIG_MACH_MICRO9S | ||||
| MACHINE_START(MICRO9S, "Contec Micro9-Slim") | ||||
| 	/* Maintainer: Hubert Feurstein <hubert.feurstein@contec.at> */ | ||||
| 	.boot_params	= EP93XX_SDCE3_PHYS_BASE_ASYNC + 0x100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= ep93xx_map_io, | ||||
| 	.init_irq	= ep93xx_init_irq, | ||||
| 	.timer		= &ep93xx_timer, | ||||
|  | ||||
| @ -65,8 +65,8 @@ static void __init simone_init_machine(void) | ||||
| } | ||||
| 
 | ||||
| MACHINE_START(SIM_ONE, "Simplemachines Sim.One Board") | ||||
| /* Maintainer: Ryan Mallon */ | ||||
| 	.boot_params	= EP93XX_SDCE0_PHYS_BASE + 0x100, | ||||
| 	/* Maintainer: Ryan Mallon */ | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= ep93xx_map_io, | ||||
| 	.init_irq	= ep93xx_init_irq, | ||||
| 	.timer		= &ep93xx_timer, | ||||
|  | ||||
| @ -163,7 +163,7 @@ static void __init snappercl15_init_machine(void) | ||||
| 
 | ||||
| MACHINE_START(SNAPPER_CL15, "Bluewater Systems Snapper CL15") | ||||
| 	/* Maintainer: Ryan Mallon */ | ||||
| 	.boot_params	= EP93XX_SDCE0_PHYS_BASE + 0x100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= ep93xx_map_io, | ||||
| 	.init_irq	= ep93xx_init_irq, | ||||
| 	.timer 		= &ep93xx_timer, | ||||
|  | ||||
| @ -257,7 +257,7 @@ static void __init ts72xx_init_machine(void) | ||||
| 
 | ||||
| MACHINE_START(TS72XX, "Technologic Systems TS-72xx SBC") | ||||
| 	/* Maintainer: Lennert Buytenhek <buytenh@wantstofly.org> */ | ||||
| 	.boot_params	= EP93XX_SDCE3_PHYS_BASE_SYNC + 0x100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= ts72xx_map_io, | ||||
| 	.init_irq	= ep93xx_init_irq, | ||||
| 	.timer		= &ep93xx_timer, | ||||
|  | ||||
| @ -207,7 +207,7 @@ static void __init armlex4210_machine_init(void) | ||||
| 
 | ||||
| MACHINE_START(ARMLEX4210, "ARMLEX4210") | ||||
| 	/* Maintainer: Alim Akhtar <alim.akhtar@samsung.com> */ | ||||
| 	.boot_params	= S5P_PA_SDRAM + 0x100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.init_irq	= exynos4_init_irq, | ||||
| 	.map_io		= armlex4210_map_io, | ||||
| 	.init_machine	= armlex4210_machine_init, | ||||
|  | ||||
| @ -1152,7 +1152,7 @@ static void __init nuri_machine_init(void) | ||||
| 
 | ||||
| MACHINE_START(NURI, "NURI") | ||||
| 	/* Maintainer: Kyungmin Park <kyungmin.park@samsung.com> */ | ||||
| 	.boot_params	= S5P_PA_SDRAM + 0x100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.init_irq	= exynos4_init_irq, | ||||
| 	.map_io		= nuri_map_io, | ||||
| 	.init_machine	= nuri_machine_init, | ||||
|  | ||||
| @ -301,7 +301,7 @@ static void __init smdkc210_machine_init(void) | ||||
| 
 | ||||
| MACHINE_START(SMDKC210, "SMDKC210") | ||||
| 	/* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */ | ||||
| 	.boot_params	= S5P_PA_SDRAM + 0x100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.init_irq	= exynos4_init_irq, | ||||
| 	.map_io		= smdkc210_map_io, | ||||
| 	.init_machine	= smdkc210_machine_init, | ||||
|  | ||||
| @ -255,7 +255,7 @@ static void __init smdkv310_machine_init(void) | ||||
| MACHINE_START(SMDKV310, "SMDKV310") | ||||
| 	/* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */ | ||||
| 	/* Maintainer: Changhwan Youn <chaos.youn@samsung.com> */ | ||||
| 	.boot_params	= S5P_PA_SDRAM + 0x100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.init_irq	= exynos4_init_irq, | ||||
| 	.map_io		= smdkv310_map_io, | ||||
| 	.init_machine	= smdkv310_machine_init, | ||||
|  | ||||
| @ -762,7 +762,7 @@ static void __init universal_machine_init(void) | ||||
| 
 | ||||
| MACHINE_START(UNIVERSAL_C210, "UNIVERSAL_C210") | ||||
| 	/* Maintainer: Kyungmin Park <kyungmin.park@samsung.com> */ | ||||
| 	.boot_params	= S5P_PA_SDRAM + 0x100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.init_irq	= exynos4_init_irq, | ||||
| 	.map_io		= universal_map_io, | ||||
| 	.init_machine	= universal_machine_init, | ||||
|  | ||||
| @ -86,7 +86,7 @@ fixup_cats(struct machine_desc *desc, struct tag *tags, | ||||
| 
 | ||||
| MACHINE_START(CATS, "Chalice-CATS") | ||||
| 	/* Maintainer: Philip Blundell */ | ||||
| 	.boot_params	= 0x00000100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.soft_reboot	= 1, | ||||
| 	.fixup		= fixup_cats, | ||||
| 	.map_io		= footbridge_map_io, | ||||
|  | ||||
| @ -15,7 +15,7 @@ | ||||
| 
 | ||||
| MACHINE_START(EBSA285, "EBSA285") | ||||
| 	/* Maintainer: Russell King */ | ||||
| 	.boot_params	= 0x00000100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.video_start	= 0x000a0000, | ||||
| 	.video_end	= 0x000bffff, | ||||
| 	.map_io		= footbridge_map_io, | ||||
|  | ||||
| @ -648,7 +648,7 @@ fixup_netwinder(struct machine_desc *desc, struct tag *tags, | ||||
| 
 | ||||
| MACHINE_START(NETWINDER, "Rebel-NetWinder") | ||||
| 	/* Maintainer: Russell King/Rebel.com */ | ||||
| 	.boot_params	= 0x00000100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.video_start	= 0x000a0000, | ||||
| 	.video_end	= 0x000bffff, | ||||
| 	.reserve_lp0	= 1, | ||||
|  | ||||
| @ -15,7 +15,7 @@ | ||||
| 
 | ||||
| MACHINE_START(PERSONAL_SERVER, "Compaq-PersonalServer") | ||||
| 	/* Maintainer: Jamey Hicks / George France */ | ||||
| 	.boot_params	= 0x00000100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= footbridge_map_io, | ||||
| 	.init_irq	= footbridge_init_irq, | ||||
| 	.timer		= &footbridge_timer, | ||||
|  | ||||
| @ -102,7 +102,7 @@ static void __init ib4220b_init(void) | ||||
| } | ||||
| 
 | ||||
| MACHINE_START(NAS4220B, "Raidsonic NAS IB-4220-B") | ||||
| 	.boot_params	= 0x100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= gemini_map_io, | ||||
| 	.init_irq	= gemini_init_irq, | ||||
| 	.timer		= &ib4220b_timer, | ||||
|  | ||||
| @ -86,7 +86,7 @@ static void __init rut1xx_init(void) | ||||
| } | ||||
| 
 | ||||
| MACHINE_START(RUT100, "Teltonika RUT100") | ||||
| 	.boot_params	= 0x100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= gemini_map_io, | ||||
| 	.init_irq	= gemini_init_irq, | ||||
| 	.timer		= &rut1xx_timer, | ||||
|  | ||||
| @ -129,7 +129,7 @@ static void __init wbd111_init(void) | ||||
| } | ||||
| 
 | ||||
| MACHINE_START(WBD111, "Wiliboard WBD-111") | ||||
| 	.boot_params	= 0x100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= gemini_map_io, | ||||
| 	.init_irq	= gemini_init_irq, | ||||
| 	.timer		= &wbd111_timer, | ||||
|  | ||||
| @ -129,7 +129,7 @@ static void __init wbd222_init(void) | ||||
| } | ||||
| 
 | ||||
| MACHINE_START(WBD222, "Wiliboard WBD-222") | ||||
| 	.boot_params	= 0x100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= gemini_map_io, | ||||
| 	.init_irq	= gemini_init_irq, | ||||
| 	.timer		= &wbd222_timer, | ||||
|  | ||||
| @ -29,7 +29,7 @@ | ||||
| 
 | ||||
| MACHINE_START(H7201, "Hynix GMS30C7201") | ||||
| 	/* Maintainer: Robert Schwebel, Pengutronix */ | ||||
| 	.boot_params	= 0xc0001000, | ||||
| 	.atag_offset	= 0x1000, | ||||
| 	.map_io		= h720x_map_io, | ||||
| 	.init_irq	= h720x_init_irq, | ||||
| 	.timer		= &h7201_timer, | ||||
|  | ||||
| @ -71,7 +71,7 @@ static void __init init_eval_h7202(void) | ||||
| 
 | ||||
| MACHINE_START(H7202, "Hynix HMS30C7202") | ||||
| 	/* Maintainer: Robert Schwebel, Pengutronix */ | ||||
| 	.boot_params	= 0x40000100, | ||||
| 	.atag_offset	= 0x100, | ||||
| 	.map_io		= h720x_map_io, | ||||
| 	.init_irq	= h7202_init_irq, | ||||
| 	.timer		= &h7202_timer, | ||||
|  | ||||
| @ -558,7 +558,7 @@ static struct sys_timer armadillo5x0_timer = { | ||||
| 
 | ||||
| MACHINE_START(ARMADILLO5X0, "Armadillo-500") | ||||
| 	/* Maintainer: Alberto Panizzo  */ | ||||
| 	.boot_params = MX3x_PHYS_OFFSET + 0x100, | ||||
| 	.atag_offset = 0x100, | ||||
| 	.map_io = mx31_map_io, | ||||
| 	.init_early = imx31_init_early, | ||||
| 	.init_irq = mx31_init_irq, | ||||
|  | ||||
| @ -311,7 +311,7 @@ static struct sys_timer eukrea_cpuimx27_timer = { | ||||
| }; | ||||
| 
 | ||||
| MACHINE_START(EUKREA_CPUIMX27, "EUKREA CPUIMX27") | ||||
| 	.boot_params = MX27_PHYS_OFFSET + 0x100, | ||||
| 	.atag_offset = 0x100, | ||||
| 	.map_io = mx27_map_io, | ||||
| 	.init_early = imx27_init_early, | ||||
| 	.init_irq = mx27_init_irq, | ||||
|  | ||||
| @ -194,7 +194,7 @@ struct sys_timer eukrea_cpuimx35_timer = { | ||||
| 
 | ||||
| MACHINE_START(EUKREA_CPUIMX35SD, "Eukrea CPUIMX35") | ||||
| 	/* Maintainer: Eukrea Electromatique */ | ||||
| 	.boot_params = MX3x_PHYS_OFFSET + 0x100, | ||||
| 	.atag_offset = 0x100, | ||||
| 	.map_io = mx35_map_io, | ||||
| 	.init_early = imx35_init_early, | ||||
| 	.init_irq = mx35_init_irq, | ||||
|  | ||||
| @ -163,7 +163,7 @@ static struct sys_timer eukrea_cpuimx25_timer = { | ||||
| 
 | ||||
| MACHINE_START(EUKREA_CPUIMX25SD, "Eukrea CPUIMX25") | ||||
| 	/* Maintainer: Eukrea Electromatique */ | ||||
| 	.boot_params = MX25_PHYS_OFFSET + 0x100, | ||||
| 	.atag_offset = 0x100, | ||||
| 	.map_io = mx25_map_io, | ||||
| 	.init_early = imx25_init_early, | ||||
| 	.init_irq = mx25_init_irq, | ||||
|  | ||||
| @ -275,7 +275,7 @@ static struct sys_timer visstrim_m10_timer = { | ||||
| }; | ||||
| 
 | ||||
| MACHINE_START(IMX27_VISSTRIM_M10, "Vista Silicon Visstrim_M10") | ||||
| 	.boot_params = MX27_PHYS_OFFSET + 0x100, | ||||
| 	.atag_offset = 0x100, | ||||
| 	.map_io = mx27_map_io, | ||||
| 	.init_early = imx27_init_early, | ||||
| 	.init_irq = mx27_init_irq, | ||||
|  | ||||
| @ -71,7 +71,7 @@ static struct sys_timer mx27ipcam_timer = { | ||||
| 
 | ||||
| MACHINE_START(IMX27IPCAM, "Freescale IMX27IPCAM") | ||||
| 	/* maintainer: Freescale Semiconductor, Inc. */ | ||||
| 	.boot_params = MX27_PHYS_OFFSET + 0x100, | ||||
| 	.atag_offset = 0x100, | ||||
| 	.map_io = mx27_map_io, | ||||
| 	.init_early = imx27_init_early, | ||||
| 	.init_irq = mx27_init_irq, | ||||
|  | ||||
| @ -77,7 +77,7 @@ static struct sys_timer mx27lite_timer = { | ||||
| }; | ||||
| 
 | ||||
| MACHINE_START(IMX27LITE, "LogicPD i.MX27LITE") | ||||
| 	.boot_params = MX27_PHYS_OFFSET + 0x100, | ||||
| 	.atag_offset = 0x100, | ||||
| 	.map_io = mx27_map_io, | ||||
| 	.init_early = imx27_init_early, | ||||
| 	.init_irq = mx27_init_irq, | ||||
|  | ||||
| @ -271,7 +271,7 @@ static struct sys_timer kzm_timer = { | ||||
| }; | ||||
| 
 | ||||
| MACHINE_START(KZM_ARM11_01, "Kyoto Microcomputer Co., Ltd. KZM-ARM11-01") | ||||
| 	.boot_params = MX3x_PHYS_OFFSET + 0x100, | ||||
| 	.atag_offset = 0x100, | ||||
| 	.map_io = kzm_map_io, | ||||
| 	.init_early = imx31_init_early, | ||||
| 	.init_irq = mx31_init_irq, | ||||
|  | ||||
| @ -145,7 +145,7 @@ struct sys_timer mx1ads_timer = { | ||||
| 
 | ||||
| MACHINE_START(MX1ADS, "Freescale MX1ADS") | ||||
| 	/* Maintainer: Sascha Hauer, Pengutronix */ | ||||
| 	.boot_params = MX1_PHYS_OFFSET + 0x100, | ||||
| 	.atag_offset = 0x100, | ||||
| 	.map_io = mx1_map_io, | ||||
| 	.init_early = imx1_init_early, | ||||
| 	.init_irq = mx1_init_irq, | ||||
| @ -154,7 +154,7 @@ MACHINE_START(MX1ADS, "Freescale MX1ADS") | ||||
| MACHINE_END | ||||
| 
 | ||||
| MACHINE_START(MXLADS, "Freescale MXLADS") | ||||
| 	.boot_params = MX1_PHYS_OFFSET + 0x100, | ||||
| 	.atag_offset = 0x100, | ||||
| 	.map_io = mx1_map_io, | ||||
| 	.init_early = imx1_init_early, | ||||
| 	.init_irq = mx1_init_irq, | ||||
|  | ||||
| @ -305,7 +305,7 @@ static struct sys_timer mx21ads_timer = { | ||||
| 
 | ||||
| MACHINE_START(MX21ADS, "Freescale i.MX21ADS") | ||||
| 	/* maintainer: Freescale Semiconductor, Inc. */ | ||||
| 	.boot_params = MX21_PHYS_OFFSET + 0x100, | ||||
| 	.atag_offset = 0x100, | ||||
| 	.map_io = mx21ads_map_io, | ||||
| 	.init_early = imx21_init_early, | ||||
| 	.init_irq = mx21_init_irq, | ||||
|  | ||||
| @ -253,7 +253,7 @@ static struct sys_timer mx25pdk_timer = { | ||||
| 
 | ||||
| MACHINE_START(MX25_3DS, "Freescale MX25PDK (3DS)") | ||||
| 	/* Maintainer: Freescale Semiconductor, Inc. */ | ||||
| 	.boot_params = MX25_PHYS_OFFSET + 0x100, | ||||
| 	.atag_offset = 0x100, | ||||
| 	.map_io = mx25_map_io, | ||||
| 	.init_early = imx25_init_early, | ||||
| 	.init_irq = mx25_init_irq, | ||||
|  | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue
	
	Block a user