diff options
Diffstat (limited to 'arch/unicore32')
149 files changed, 16686 insertions, 0 deletions
diff --git a/arch/unicore32/.gitignore b/arch/unicore32/.gitignore new file mode 100644 index 00000000000..947e99c2a95 --- /dev/null +++ b/arch/unicore32/.gitignore @@ -0,0 +1,21 @@ +# +# Generated include files +# +include/generated +# +# Generated ld script file +# +kernel/vmlinux.lds +# +# Generated images in boot +# +boot/Image +boot/zImage +boot/uImage +# +# Generated files in boot/compressed +# +boot/compressed/piggy.S +boot/compressed/piggy.gzip +boot/compressed/vmlinux +boot/compressed/vmlinux.lds diff --git a/arch/unicore32/Kconfig b/arch/unicore32/Kconfig new file mode 100644 index 00000000000..928237a7b9c --- /dev/null +++ b/arch/unicore32/Kconfig @@ -0,0 +1,253 @@ +config UNICORE32 +	def_bool y +	select ARCH_MIGHT_HAVE_PC_PARPORT +	select ARCH_MIGHT_HAVE_PC_SERIO +	select HAVE_MEMBLOCK +	select HAVE_GENERIC_DMA_COHERENT +	select HAVE_DMA_ATTRS +	select HAVE_KERNEL_GZIP +	select HAVE_KERNEL_BZIP2 +	select GENERIC_ATOMIC64 +	select HAVE_KERNEL_LZO +	select HAVE_KERNEL_LZMA +	select VIRT_TO_BUS +	select ARCH_HAVE_CUSTOM_GPIO_H +	select GENERIC_FIND_FIRST_BIT +	select GENERIC_IRQ_PROBE +	select GENERIC_IRQ_SHOW +	select ARCH_WANT_FRAME_POINTERS +	select GENERIC_IOMAP +	select MODULES_USE_ELF_REL +	help +	  UniCore-32 is 32-bit Instruction Set Architecture, +	  including a series of low-power-consumption RISC chip +	  designs licensed by PKUnity Ltd. +	  Please see web page at <http://www.pkunity.com/>. + +config GENERIC_CSUM +	def_bool y + +config NO_IOPORT_MAP +	bool + +config STACKTRACE_SUPPORT +	def_bool y + +config HAVE_LATENCYTOP_SUPPORT +	def_bool y + +config LOCKDEP_SUPPORT +	def_bool y + +config RWSEM_GENERIC_SPINLOCK +	def_bool y + +config RWSEM_XCHGADD_ALGORITHM +	bool + +config ARCH_HAS_ILOG2_U32 +	bool + +config ARCH_HAS_ILOG2_U64 +	bool + +config GENERIC_HWEIGHT +	def_bool y + +config GENERIC_CALIBRATE_DELAY +	def_bool y + +config ARCH_MAY_HAVE_PC_FDC +	bool + +config ZONE_DMA +	def_bool y + +config NEED_DMA_MAP_STATE +       def_bool y + +source "init/Kconfig" + +source "kernel/Kconfig.freezer" + +menu "System Type" + +config MMU +	def_bool y + +config ARCH_FPGA +	bool + +config ARCH_PUV3 +	def_bool y +	select CPU_UCV2 +	select GENERIC_CLOCKEVENTS +	select HAVE_CLK +	select ARCH_REQUIRE_GPIOLIB + +# CONFIGs for ARCH_PUV3 + +if ARCH_PUV3 + +choice +	prompt "Board Selection" +	default PUV3_DB0913 + +config PUV3_FPGA_DLX200 +	select ARCH_FPGA +	bool "FPGA board" + +config PUV3_DB0913 +	bool "DEBUG board (0913)" + +config PUV3_NB0916 +	bool "NetBook board (0916)" +	select PWM +	select PWM_PUV3 + +config PUV3_SMW0919 +	bool "Security Mini-Workstation board (0919)" + +endchoice + +config PUV3_PM +	def_bool y if !ARCH_FPGA + +endif + +source "arch/unicore32/mm/Kconfig" + +comment "Floating poing support" + +config UNICORE_FPU_F64 +	def_bool y if !ARCH_FPGA + +endmenu + +menu "Bus support" + +config PCI +	bool "PCI Support" +	help +	  Find out whether you have a PCI motherboard. PCI is the name of a +	  bus system, i.e. the way the CPU talks to the other stuff inside +	  your box. Other bus systems are ISA, EISA, MicroChannel (MCA) or +	  VESA. If you have PCI, say Y, otherwise N. + +source "drivers/pci/Kconfig" + +source "drivers/pcmcia/Kconfig" + +endmenu + +menu "Kernel Features" + +source "kernel/Kconfig.preempt" + +source "kernel/Kconfig.hz" + +source "mm/Kconfig" + +config LEDS +	def_bool y +	depends on GPIOLIB + +config ALIGNMENT_TRAP +	def_bool y +	help +	  Unicore processors can not fetch/store information which is not +	  naturally aligned on the bus, i.e., a 4 byte fetch must start at an +	  address divisible by 4. On 32-bit Unicore processors, these non-aligned +	  fetch/store instructions will be emulated in software if you say +	  here, which has a severe performance impact. This is necessary for +	  correct operation of some network protocols. With an IP-only +	  configuration it is safe to say N, otherwise say Y. + +endmenu + +menu "Boot options" + +config CMDLINE +	string "Default kernel command string" +	default "" + +config CMDLINE_FORCE +	bool "Always use the default kernel command string" +	depends on CMDLINE != "" +	help +	  Always use the default kernel command string, even if the boot +	  loader passes other arguments to the kernel. +	  This is useful if you cannot or don't want to change the +	  command-line options your boot loader passes to the kernel. + +	  If unsure, say N. + +endmenu + +menu "Userspace binary formats" + +source "fs/Kconfig.binfmt" + +endmenu + +menu "Power management options" + +source "kernel/power/Kconfig" + +source "drivers/cpufreq/Kconfig" + +config ARCH_SUSPEND_POSSIBLE +	def_bool y if !ARCH_FPGA + +config ARCH_HIBERNATION_POSSIBLE +	def_bool y if !ARCH_FPGA + +endmenu + +source "net/Kconfig" + +if ARCH_PUV3 + +config PUV3_GPIO +	bool +	depends on !ARCH_FPGA +	select GPIO_SYSFS +	default y + +if PUV3_NB0916 + +menu "PKUnity NetBook-0916 Features" + +config I2C_BATTERY_BQ27200 +	tristate "I2C Battery BQ27200 Support" +	select I2C_PUV3 +	select POWER_SUPPLY +	select BATTERY_BQ27x00 + +config I2C_EEPROM_AT24 +	tristate "I2C EEPROMs AT24 support" +	select I2C_PUV3 +	select EEPROM_AT24 + +config LCD_BACKLIGHT +	tristate "LCD Backlight support" +	select BACKLIGHT_LCD_SUPPORT +	select BACKLIGHT_PWM + +endmenu + +endif + +endif + +source "drivers/Kconfig" + +source "fs/Kconfig" + +source "arch/unicore32/Kconfig.debug" + +source "security/Kconfig" + +source "crypto/Kconfig" + +source "lib/Kconfig" diff --git a/arch/unicore32/Kconfig.debug b/arch/unicore32/Kconfig.debug new file mode 100644 index 00000000000..1a362623984 --- /dev/null +++ b/arch/unicore32/Kconfig.debug @@ -0,0 +1,47 @@ +menu "Kernel hacking" + +source "lib/Kconfig.debug" + +config STRICT_DEVMEM +	bool "Filter access to /dev/mem" +	depends on MMU +	---help--- +	  If this option is disabled, you allow userspace (root) access to all +	  of memory, including kernel and userspace memory. Accidental +	  access to this is obviously disastrous, but specific access can +	  be used by people debugging the kernel. + +	  If this option is switched on, the /dev/mem file only allows +	  userspace access to memory mapped peripherals. + +          If in doubt, say Y. + +config EARLY_PRINTK +	def_bool DEBUG_OCD +	help +	  Write kernel log output directly into the ocd or to a serial port. + +	  This is useful for kernel debugging when your machine crashes very +	  early before the console code is initialized. For normal operation +	  it is not recommended because it looks ugly and doesn't cooperate +	  with klogd/syslogd or the X server. You should normally N here, +	  unless you want to debug such a crash. + +# These options are only for real kernel hackers who want to get their hands dirty. +config DEBUG_LL +	bool "Kernel low-level debugging functions" +	depends on DEBUG_KERNEL +	help +	  Say Y here to include definitions of printascii, printch, printhex +	  in the kernel.  This is helpful if you are debugging code that +	  executes before the console is initialized. + +config DEBUG_OCD +	bool "Kernel low-level debugging via On-Chip-Debugger" +	depends on DEBUG_LL +	default y +	help +	  Say Y here if you want the debug print routines to direct their +	  output to the UniCore On-Chip-Debugger channel using CP #1. + +endmenu diff --git a/arch/unicore32/Makefile b/arch/unicore32/Makefile new file mode 100644 index 00000000000..b6f5c4c1eaf --- /dev/null +++ b/arch/unicore32/Makefile @@ -0,0 +1,60 @@ +# +# arch/unicore32/Makefile +# +# This file is included by the global makefile so that you can add your own +# architecture-specific flags and dependencies. +# +# This file is subject to the terms and conditions of the GNU General Public +# License.  See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 2002~2010 by Guan Xue-tao +# +ifneq ($(SUBARCH),$(ARCH)) +	ifeq ($(CROSS_COMPILE),) +		CROSS_COMPILE := $(call cc-cross-prefix, unicore32-linux-) +	endif +endif + +LDFLAGS_vmlinux		:= -p --no-undefined -X + +OBJCOPYFLAGS		:= -O binary -R .note -R .note.gnu.build-id -R .comment -S + +# Never generate .eh_frame +KBUILD_CFLAGS		+= $(call cc-option,-fno-dwarf2-cfi-asm) + +# Never use hard float in kernel +KBUILD_CFLAGS		+= -msoft-float + +ifeq ($(CONFIG_FRAME_POINTER),y) +KBUILD_CFLAGS		+= -mno-sched-prolog +endif + +CHECKFLAGS		+= -D__unicore32__ + +head-y			:= arch/unicore32/kernel/head.o + +core-y			+= arch/unicore32/kernel/ +core-y			+= arch/unicore32/mm/ + +libs-y			+= arch/unicore32/lib/ + +boot			:= arch/unicore32/boot + +# Default defconfig and target when executing plain make +KBUILD_DEFCONFIG	:= $(ARCH)_defconfig +KBUILD_IMAGE		:= zImage + +all:	$(KBUILD_IMAGE) + +zImage Image uImage: vmlinux +	$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ + +archclean: +	$(Q)$(MAKE) $(clean)=$(boot) + +define archhelp +  echo  '* zImage        - Compressed kernel image (arch/$(ARCH)/boot/zImage)' +  echo  '  Image         - Uncompressed kernel image (arch/$(ARCH)/boot/Image)' +  echo  '  uImage        - U-Boot wrapped zImage' +endef diff --git a/arch/unicore32/boot/Makefile b/arch/unicore32/boot/Makefile new file mode 100644 index 00000000000..ec7fb70b412 --- /dev/null +++ b/arch/unicore32/boot/Makefile @@ -0,0 +1,39 @@ +# +# arch/unicore32/boot/Makefile +# +# This file is included by the global makefile so that you can add your own +# architecture-specific flags and dependencies. +# +# This file is subject to the terms and conditions of the GNU General Public +# License.  See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 2001~2010 GUAN Xue-tao +# + +targets := Image zImage uImage + +$(obj)/Image: vmlinux FORCE +	$(call if_changed,objcopy) +	@echo '  Kernel: $@ is ready' + +$(obj)/compressed/vmlinux: $(obj)/Image FORCE +	$(Q)$(MAKE) $(build)=$(obj)/compressed $@ + +$(obj)/zImage: $(obj)/compressed/vmlinux FORCE +	$(call if_changed,objcopy) +	@echo '  Kernel: $@ is ready' + +UIMAGE_ARCH = unicore +UIMAGE_LOADADDR = 0x0 + +$(obj)/uImage: $(obj)/zImage FORCE +	$(call if_changed,uimage) +	@echo '  Image $@ is ready' + +PHONY += initrd FORCE +initrd: +	@test "$(INITRD)" != "" || \ +	(echo You must specify INITRD; exit -1) + +subdir- := compressed diff --git a/arch/unicore32/boot/compressed/Makefile b/arch/unicore32/boot/compressed/Makefile new file mode 100644 index 00000000000..96494fb646f --- /dev/null +++ b/arch/unicore32/boot/compressed/Makefile @@ -0,0 +1,68 @@ +# +# linux/arch/unicore32/boot/compressed/Makefile +# +# create a compressed vmlinuz image from the original vmlinux +# +# This file is subject to the terms and conditions of the GNU General Public +# License.  See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 2001~2010 GUAN Xue-tao +# + +ccflags-y	:= -fpic -fno-builtin +asflags-y	:= -Wa,-march=all + +OBJS		:= misc.o + +# font.c and font.o +CFLAGS_font.o	:= -Dstatic= +$(obj)/font.c: $(srctree)/lib/fonts/font_8x8.c +	$(call cmd,shipped) + +# piggy.S and piggy.o +suffix_$(CONFIG_KERNEL_GZIP)	:= gzip +suffix_$(CONFIG_KERNEL_BZIP2)	:= bz2 +suffix_$(CONFIG_KERNEL_LZO)	:= lzo +suffix_$(CONFIG_KERNEL_LZMA)	:= lzma + +$(obj)/piggy.$(suffix_y): $(obj)/../Image FORCE +	$(call if_changed,$(suffix_y)) + +SEDFLAGS_piggy	= s/DECOMP_SUFFIX/$(suffix_y)/ +$(obj)/piggy.S: $(obj)/piggy.S.in +	@sed "$(SEDFLAGS_piggy)" < $< > $@ + +$(obj)/piggy.o:  $(obj)/piggy.$(suffix_y) $(obj)/piggy.S FORCE + +targets		:= vmlinux vmlinux.lds font.o font.c head.o misc.o \ +			piggy.$(suffix_y) piggy.o piggy.S \ + +# Make sure files are removed during clean +extra-y		+= piggy.gzip piggy.bz2 piggy.lzo piggy.lzma + +# ? +LDFLAGS_vmlinux += -p +# Report unresolved symbol references +LDFLAGS_vmlinux += --no-undefined +# Delete all temporary local symbols +LDFLAGS_vmlinux += -X +# Next argument is a linker script +LDFLAGS_vmlinux += -T + +# For uidivmod +$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/head.o $(obj)/piggy.o \ +		$(obj)/misc.o FORCE +	$(call if_changed,ld) +	@: + +# We now have a PIC decompressor implementation.  Decompressors running +# from RAM should not define ZTEXTADDR.  Decompressors running directly +# from ROM or Flash must define ZTEXTADDR (preferably via the config) +ZTEXTADDR	:= 0x03000000 +ZBSSADDR	:= ALIGN(4) + +SEDFLAGS_lds	= s/TEXT_START/$(ZTEXTADDR)/;s/BSS_START/$(ZBSSADDR)/ +$(obj)/vmlinux.lds: $(obj)/vmlinux.lds.in arch/unicore32/boot/Makefile $(KCONFIG_CONFIG) +	@sed "$(SEDFLAGS_lds)" < $< > $@ + diff --git a/arch/unicore32/boot/compressed/head.S b/arch/unicore32/boot/compressed/head.S new file mode 100644 index 00000000000..fbd1e374c68 --- /dev/null +++ b/arch/unicore32/boot/compressed/head.S @@ -0,0 +1,204 @@ +/* + * linux/arch/unicore32/boot/compressed/head.S + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/linkage.h> +#include <mach/memory.h> + +#define csub	cmpsub +#define cand	cmpand +#define nop8	nop; nop; nop; nop; nop; nop; nop; nop + +		.section ".start", #alloc, #execinstr +		.text +start: +		.type	start,#function + +		/* Initialize ASR, PRIV mode and INTR off */ +		mov	r0, #0xD3 +		mov.a	asr, r0 + +		adr	r0, LC0 +		ldm	(r1, r2, r3, r5, r6, r7, r8), [r0]+ +		ldw	sp, [r0+], #28 +		sub.a	r0, r0, r1		@ calculate the delta offset + +		/* +		 * if delta is zero, we are running at the address +		 * we were linked at. +		 */ +		beq	not_relocated + +		/* +		 * We're running at a different address.  We need to fix +		 * up various pointers: +		 *   r5 - zImage base address (_start) +		 *   r7 - GOT start +		 *   r8 - GOT end +		 */ +		add	r5, r5, r0 +		add	r7, r7, r0 +		add	r8, r8, r0 + +		/* +		 * we need to fix up pointers into the BSS region. +		 *   r2 - BSS start +		 *   r3 - BSS end +		 *   sp - stack pointer +		 */ +		add	r2, r2, r0 +		add	r3, r3, r0 +		add	sp, sp, r0 + +		/* +		 * Relocate all entries in the GOT table. +		 * This fixes up the C references. +		 *   r7 - GOT start +		 *   r8 - GOT end +		 */ +1001:		ldw	r1, [r7+], #0 +		add	r1, r1, r0 +		stw.w	r1, [r7]+, #4 +		csub.a	r7, r8 +		bub	1001b + +not_relocated: +		/* +		 * Clear BSS region. +		 *   r2 - BSS start +		 *   r3 - BSS end +		 */ +		mov	r0, #0 +1002:		stw.w	r0, [r2]+, #4 +		csub.a	r2, r3 +		bub	1002b + +		/* +		 * Turn on the cache. +		 */ +                mov     r0, #0 +                movc    p0.c5, r0, #28		@ cache invalidate all +                nop8 +                movc    p0.c6, r0, #6		@ tlb invalidate all +                nop8 + +                mov     r0, #0x1c		@ en icache and wb dcache +                movc    p0.c1, r0, #0 +                nop8 + +		/* +		 * Set up some pointers, for starting decompressing. +		 */ + +		mov	r1, sp			@ malloc space above stack +		add	r2, sp, #0x10000	@ 64k max + +		/* +		 * Check to see if we will overwrite ourselves. +		 *   r4 = final kernel address +		 *   r5 = start of this image +		 *   r6 = size of decompressed image +		 *   r2 = end of malloc space (and therefore this image) +		 * We basically want: +		 *   r4 >= r2 -> OK +		 *   r4 + image length <= r5 -> OK +		 */ +		ldw	r4, =KERNEL_IMAGE_START +		csub.a	r4, r2 +		bea	wont_overwrite +		add	r0, r4, r6 +		csub.a	r0, r5 +		beb	wont_overwrite + +		/* +		 * If overwrite, just print error message +		 */ +		b	__error_overwrite + +		/* +		 * We're not in danger of overwriting ourselves. +		 * Do this the simple way. +		 */ +wont_overwrite: +		/* +		 * decompress_kernel: +		 *   r0: output_start +		 *   r1: free_mem_ptr_p +		 *   r2: free_mem_ptr_end_p +		 */ +		mov	r0, r4 +		b.l	decompress_kernel	@ C functions + +		/* +		 * Clean and flush the cache to maintain consistency. +		 */ +		mov	r0, #0 +                movc    p0.c5, r0, #14		@ flush dcache +		nop8 +                movc    p0.c5, r0, #20		@ icache invalidate all +                nop8 + +		/* +		 * Turn off the Cache and MMU. +		 */ +		mov	r0, #0			@ disable i/d cache and MMU +		movc	p0.c1, r0, #0 +                nop8 + +		mov	r0, #0			@ must be zero +		ldw	r4, =KERNEL_IMAGE_START +		mov	pc, r4			@ call kernel + + +		.align	2 +		.type	LC0, #object +LC0:		.word	LC0			@ r1 +		.word	__bss_start		@ r2 +		.word	_end			@ r3 +		.word	_start			@ r5 +		.word	_image_size		@ r6 +		.word	_got_start		@ r7 +		.word	_got_end		@ r8 +		.word	decompress_stack_end	@ sp +		.size	LC0, . - LC0 + +print_string: +#ifdef CONFIG_DEBUG_OCD +2001:		ldb.w	r1, [r0]+, #1 +		csub.a	r1, #0 +		bne	2002f +		mov	pc, lr +2002: +		movc	r2, p1.c0, #0 +		cand.a	r2, #2 +		bne	2002b +		movc	p1.c1, r1, #1 +		csub.a	r1, #'\n' +		cmoveq	r1, #'\r' +		beq	2002b +		b	2001b +#else +		mov	pc, lr +#endif + +__error_overwrite: +		adr	r0, str_error +		b.l	print_string +2001:		nop8 +		b	2001b +str_error:	.asciz	"\nError: Kernel address OVERWRITE\n" +		.align + +		.ltorg + +		.align	4 +		.section ".stack", "aw", %nobits +decompress_stack:	.space	4096 +decompress_stack_end: diff --git a/arch/unicore32/boot/compressed/misc.c b/arch/unicore32/boot/compressed/misc.c new file mode 100644 index 00000000000..176d5bda355 --- /dev/null +++ b/arch/unicore32/boot/compressed/misc.c @@ -0,0 +1,126 @@ +/* + * linux/arch/unicore32/boot/compressed/misc.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <asm/unaligned.h> +#include <mach/uncompress.h> + +/* + * gzip delarations + */ +unsigned char *output_data; +unsigned long output_ptr; + +unsigned int free_mem_ptr; +unsigned int free_mem_end_ptr; + +#define STATIC static +#define STATIC_RW_DATA	/* non-static please */ + +/* + * arch-dependent implementations + */ +#ifndef ARCH_HAVE_DECOMP_ERROR +#define arch_decomp_error(x) +#endif + +#ifndef ARCH_HAVE_DECOMP_SETUP +#define arch_decomp_setup() +#endif + +#ifndef ARCH_HAVE_DECOMP_PUTS +#define arch_decomp_puts(p) +#endif + +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 error(char *x) +{ +	arch_decomp_puts("\n\n"); +	arch_decomp_puts(x); +	arch_decomp_puts("\n\n -- System halted"); + +	arch_decomp_error(x); + +	for (;;) +		; /* Halt */ +} + +/* Heap size should be adjusted for different decompress method */ +#ifdef CONFIG_KERNEL_GZIP +#include "../../../../lib/decompress_inflate.c" +#endif + +#ifdef CONFIG_KERNEL_BZIP2 +#include "../../../../lib/decompress_bunzip2.c" +#endif + +#ifdef CONFIG_KERNEL_LZO +#include "../../../../lib/decompress_unlzo.c" +#endif + +#ifdef CONFIG_KERNEL_LZMA +#include "../../../../lib/decompress_unlzma.c" +#endif + +unsigned long decompress_kernel(unsigned long output_start, +		unsigned long free_mem_ptr_p, +		unsigned long free_mem_ptr_end_p) +{ +	unsigned char *tmp; + +	output_data		= (unsigned char *)output_start; +	free_mem_ptr		= free_mem_ptr_p; +	free_mem_end_ptr	= free_mem_ptr_end_p; + +	arch_decomp_setup(); + +	tmp = (unsigned char *) (((unsigned long)input_data_end) - 4); +	output_ptr = get_unaligned_le32(tmp); + +	arch_decomp_puts("Uncompressing Linux..."); +	decompress(input_data, input_data_end - input_data, NULL, NULL, +			output_data, NULL, error); +	arch_decomp_puts(" done, booting the kernel.\n"); +	return output_ptr; +} diff --git a/arch/unicore32/boot/compressed/piggy.S.in b/arch/unicore32/boot/compressed/piggy.S.in new file mode 100644 index 00000000000..b79704d5802 --- /dev/null +++ b/arch/unicore32/boot/compressed/piggy.S.in @@ -0,0 +1,6 @@ +	.section .piggydata,#alloc +	.globl	input_data +input_data: +	.incbin	"arch/unicore32/boot/compressed/piggy.DECOMP_SUFFIX" +	.globl	input_data_end +input_data_end: diff --git a/arch/unicore32/boot/compressed/vmlinux.lds.in b/arch/unicore32/boot/compressed/vmlinux.lds.in new file mode 100644 index 00000000000..d5a3ce29623 --- /dev/null +++ b/arch/unicore32/boot/compressed/vmlinux.lds.in @@ -0,0 +1,61 @@ +/* + * linux/arch/unicore/boot/compressed/vmlinux.lds.in + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * 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. + */ +OUTPUT_ARCH(unicore32) +ENTRY(_start) +SECTIONS +{ +  /DISCARD/ : { +    /* +     * Discard any r/w data - this produces a link error if we have any, +     * which is required for PIC decompression.  Local data generates +     * GOTOFF relocations, which prevents it being relocated independently +     * of the text/got segments. +     */ +    *(.data) +  } + +  . = TEXT_START; +  _text = .; + +  .text : { +    _start = .; +    *(.start) +    *(.text) +    *(.text.*) +    *(.fixup) +    *(.gnu.warning) +    *(.rodata) +    *(.rodata.*) +    *(.piggydata) +    . = ALIGN(4); +  } + +  _etext = .; + +  /* Assume size of decompressed image is 4x the compressed image */ +  _image_size = (_etext - _text) * 4; + +  _got_start = .; +  .got			: { *(.got) } +  _got_end = .; +  .got.plt		: { *(.got.plt) } +  _edata = .; + +  . = BSS_START; +  __bss_start = .; +  .bss			: { *(.bss) } +  _end = .; + +  .stack		: { *(.stack) } +  .comment 0		: { *(.comment) } +} + diff --git a/arch/unicore32/configs/unicore32_defconfig b/arch/unicore32/configs/unicore32_defconfig new file mode 100644 index 00000000000..45f47f88d86 --- /dev/null +++ b/arch/unicore32/configs/unicore32_defconfig @@ -0,0 +1,214 @@ +### General setup +CONFIG_EXPERIMENTAL=y +CONFIG_LOCALVERSION="-unicore32" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_HOTPLUG=y +#	Initial RAM filesystem and RAM disk (initramfs/initrd) support +#CONFIG_BLK_DEV_INITRD=y +#CONFIG_INITRAMFS_SOURCE="arch/unicore/ramfs/ramfs_config" + +### Enable loadable module support +CONFIG_MODULES=n +CONFIG_MODULE_UNLOAD=y + +### System Type +CONFIG_ARCH_PUV3=y +#	Board Selection +CONFIG_PUV3_NB0916=y +#	Processor Features +CONFIG_CPU_DCACHE_LINE_DISABLE=y +CONFIG_CPU_TLB_SINGLE_ENTRY_DISABLE=n + +### Bus support +CONFIG_PCI=y +CONFIG_PCI_LEGACY=n + +### Boot options +#	for debug, adding: earlyprintk=ocd,keep initcall_debug +#	others support: test_suspend=mem root=/dev/sda +#	hibernate support: resume=/dev/sda3 +CONFIG_CMDLINE="earlyprintk=ocd,keep ignore_loglevel" +# TODO: mem=512M video=unifb:1024x600-16@75 +# for nfs: root=/dev/nfs rw nfsroot=192.168.10.88:/home/udb/nfs/,rsize=1024,wsize=1024 +#	ip=192.168.10.83:192.168.10.88:192.168.10.1:255.255.255.0::eth0:off +CONFIG_CMDLINE_FORCE=y + +### Power management options +CONFIG_PM=y +CONFIG_HIBERNATION=y +CONFIG_PM_STD_PARTITION="/dev/sda3" +CONFIG_CPU_FREQ=n +CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y + +### Networking support +CONFIG_NET=y +#	Networking options +CONFIG_PACKET=m +CONFIG_UNIX=m +#	TCP/IP networking +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_PNP=y +CONFIG_IPV6=n +#	Wireless +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_MAC80211=m + +### PKUnity SoC Features +CONFIG_USB_WLAN_HED_AQ3=n +CONFIG_USB_CMMB_INNOFIDEI=n +CONFIG_I2C_BATTERY_BQ27200=n +CONFIG_I2C_EEPROM_AT24=n +CONFIG_LCD_BACKLIGHT=n + +CONFIG_PUV3_UMAL=y +CONFIG_PUV3_MUSB=n +CONFIG_PUV3_AC97=n +CONFIG_PUV3_NAND=n +CONFIG_PUV3_MMC=n +CONFIG_PUV3_UART=n + +### Device Drivers +#	Memory Technology Device (MTD) support +CONFIG_MTD=m +CONFIG_MTD_UBI=m +CONFIG_MTD_PARTITIONS=y +CONFIG_MTD_CHAR=m +CONFIG_MTD_BLKDEVS=m +#	RAM/ROM/Flash chip drivers +CONFIG_MTD_CFI=m +CONFIG_MTD_JEDECPROBE=m +CONFIG_MTD_CFI_AMDSTD=m +#	Mapping drivers for chip access +CONFIG_MTD_PHYSMAP=m + +#	Block devices +CONFIG_BLK_DEV_LOOP=m + +#	SCSI device support +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_DEV_SR=m +CONFIG_CHR_DEV_SG=m + +#	Serial ATA (prod) and Parallel ATA (experimental) drivers +CONFIG_ATA=y +CONFIG_SATA_VIA=y + +#	Network device support +CONFIG_NETDEVICES=y +CONFIG_NET_ETHERNET=y +CONFIG_NETDEV_1000=y +#	Wireless LAN +CONFIG_WLAN_80211=n +CONFIG_RT2X00=n +CONFIG_RT73USB=n + +#	Input device support +CONFIG_INPUT_EVDEV=m +#	Keyboards +CONFIG_KEYBOARD_GPIO=m + +#	I2C support +CONFIG_I2C=y +CONFIG_I2C_PUV3=y + +#	Hardware Monitoring support +#CONFIG_SENSORS_LM75=m +#	Generic Thermal sysfs driver +#CONFIG_THERMAL=m +#CONFIG_THERMAL_HWMON=y + +#	Multimedia support +CONFIG_MEDIA_SUPPORT=n +CONFIG_VIDEO_DEV=n +CONFIG_USB_VIDEO_CLASS=n + +#	Graphics support +CONFIG_FB=y +CONFIG_FB_PUV3_UNIGFX=y +#	Console display driver support +CONFIG_VGA_CONSOLE=n +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FONTS=y +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +#	Bootup logo +CONFIG_LOGO=n + +#	Sound card support +CONFIG_SOUND=m +#	Advanced Linux Sound Architecture +CONFIG_SND=m +CONFIG_SND_MIXER_OSS=m +CONFIG_SND_PCM_OSS=m + +#	USB support +CONFIG_USB_ARCH_HAS_HCD=n +CONFIG_USB=n +CONFIG_USB_PRINTER=n +CONFIG_USB_STORAGE=n +#	Inventra Highspeed Dual Role Controller +CONFIG_USB_MUSB_HDRC=n + +#	LED Support +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_GPIO=y +#	LED Triggers +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_IDE_DISK=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y + +#	Real Time Clock +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_PUV3=y + +### File systems +CONFIG_EXT2_FS=m +CONFIG_EXT3_FS=y +CONFIG_EXT4_FS=y +CONFIG_FUSE_FS=m +#	CD-ROM/DVD Filesystems +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +CONFIG_UDF_FS=m +#	DOS/FAT/NT Filesystems +CONFIG_VFAT_FS=m +#	Pseudo filesystems +CONFIG_PROC_FS=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +#	Miscellaneous filesystems +CONFIG_MISC_FILESYSTEMS=y +CONFIG_JFFS2_FS=m +CONFIG_UBIFS_FS=m +#	Network File Systems +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +CONFIG_ROOT_NFS=y +#	Partition Types +CONFIG_PARTITION_ADVANCED=y +CONFIG_MSDOS_PARTITION=y +#	Native language support +CONFIG_NLS=y +CONFIG_NLS_CODEPAGE_437=m +CONFIG_NLS_CODEPAGE_936=m +CONFIG_NLS_ISO8859_1=m +CONFIG_NLS_UTF8=m + +### Kernel hacking +CONFIG_FRAME_WARN=8096 +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_KERNEL=y +CONFIG_PROVE_LOCKING=n +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_FRAME_POINTER=y +CONFIG_DEBUG_LL=y + diff --git a/arch/unicore32/include/asm/Kbuild b/arch/unicore32/include/asm/Kbuild new file mode 100644 index 00000000000..1e5fb872a4a --- /dev/null +++ b/arch/unicore32/include/asm/Kbuild @@ -0,0 +1,65 @@ + +generic-y += atomic.h +generic-y += auxvec.h +generic-y += bitsperlong.h +generic-y += bugs.h +generic-y += clkdev.h +generic-y += cputime.h +generic-y += current.h +generic-y += device.h +generic-y += div64.h +generic-y += emergency-restart.h +generic-y += errno.h +generic-y += exec.h +generic-y += fb.h +generic-y += fcntl.h +generic-y += ftrace.h +generic-y += futex.h +generic-y += hardirq.h +generic-y += hash.h +generic-y += hw_irq.h +generic-y += ioctl.h +generic-y += ioctls.h +generic-y += ipcbuf.h +generic-y += irq_regs.h +generic-y += kdebug.h +generic-y += kmap_types.h +generic-y += local.h +generic-y += mcs_spinlock.h +generic-y += mman.h +generic-y += module.h +generic-y += msgbuf.h +generic-y += param.h +generic-y += parport.h +generic-y += percpu.h +generic-y += poll.h +generic-y += posix_types.h +generic-y += preempt.h +generic-y += resource.h +generic-y += scatterlist.h +generic-y += sections.h +generic-y += segment.h +generic-y += sembuf.h +generic-y += serial.h +generic-y += setup.h +generic-y += shmbuf.h +generic-y += shmparam.h +generic-y += siginfo.h +generic-y += signal.h +generic-y += sizes.h +generic-y += socket.h +generic-y += sockios.h +generic-y += stat.h +generic-y += statfs.h +generic-y += swab.h +generic-y += syscalls.h +generic-y += termbits.h +generic-y += termios.h +generic-y += topology.h +generic-y += trace_clock.h +generic-y += types.h +generic-y += ucontext.h +generic-y += unaligned.h +generic-y += user.h +generic-y += vga.h +generic-y += xor.h diff --git a/arch/unicore32/include/asm/assembler.h b/arch/unicore32/include/asm/assembler.h new file mode 100644 index 00000000000..8e87ed7faeb --- /dev/null +++ b/arch/unicore32/include/asm/assembler.h @@ -0,0 +1,131 @@ +/* + * linux/arch/unicore32/include/asm/assembler.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * 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. + * + *  Do not include any C declarations in this file - it is included by + *  assembler source. + */ +#ifndef __ASSEMBLY__ +#error "Only include this from assembly code" +#endif + +#include <asm/ptrace.h> + +/* + * Little Endian independent macros for shifting bytes within registers. + */ +#define pull            >> +#define push            << +#define get_byte_0      << #0 +#define get_byte_1	>> #8 +#define get_byte_2	>> #16 +#define get_byte_3	>> #24 +#define put_byte_0      << #0 +#define put_byte_1	<< #8 +#define put_byte_2	<< #16 +#define put_byte_3	<< #24 + +#define cadd		cmpadd +#define cand		cmpand +#define csub		cmpsub +#define cxor		cmpxor + +/* + * Enable and disable interrupts + */ +	.macro disable_irq, temp +	mov	\temp, asr +	andn     \temp, \temp, #0xFF +	or	\temp, \temp, #PSR_I_BIT | PRIV_MODE +	mov.a	asr, \temp +	.endm + +	.macro enable_irq, temp +	mov	\temp, asr +	andn     \temp, \temp, #0xFF +	or	\temp, \temp, #PRIV_MODE +	mov.a	asr, \temp +	.endm + +#define USER(x...)				\ +9999:	x;					\ +	.pushsection __ex_table, "a";		\ +	.align	3;				\ +	.long	9999b, 9001f;			\ +	.popsection + +	.macro	notcond, cond, nexti = .+8 +	.ifc	\cond, eq +		bne	\nexti +	.else;	.ifc	\cond, ne +		beq	\nexti +	.else;	.ifc	\cond, ea +		bub	\nexti +	.else;	.ifc	\cond, ub +		bea	\nexti +	.else;	.ifc	\cond, fs +		bns	\nexti +	.else;	.ifc	\cond, ns +		bfs	\nexti +	.else;	.ifc	\cond, fv +		bnv	\nexti +	.else;	.ifc	\cond, nv +		bfv	\nexti +	.else;	.ifc	\cond, ua +		beb	\nexti +	.else;	.ifc	\cond, eb +		bua	\nexti +	.else;	.ifc	\cond, eg +		bsl	\nexti +	.else;	.ifc	\cond, sl +		beg	\nexti +	.else;	.ifc	\cond, sg +		bel	\nexti +	.else;	.ifc	\cond, el +		bsg	\nexti +	.else;	.ifnc	\cond, al +		.error  "Unknown cond in notcond macro argument" +	.endif;	.endif;	.endif;	.endif;	.endif;	.endif;	.endif +	.endif;	.endif;	.endif;	.endif;	.endif;	.endif;	.endif +	.endif +	.endm + +	.macro	usracc, instr, reg, ptr, inc, cond, rept, abort +	.rept	\rept +	notcond	\cond, .+8 +9999 : +	.if	\inc == 1 +	\instr\()b.u \reg, [\ptr], #\inc +	.elseif	\inc == 4 +	\instr\()w.u \reg, [\ptr], #\inc +	.else +	.error	"Unsupported inc macro argument" +	.endif + +	.pushsection __ex_table, "a" +	.align	3 +	.long	9999b, \abort +	.popsection +	.endr +	.endm + +	.macro	strusr, reg, ptr, inc, cond = al, rept = 1, abort = 9001f +	usracc	st, \reg, \ptr, \inc, \cond, \rept, \abort +	.endm + +	.macro	ldrusr, reg, ptr, inc, cond = al, rept = 1, abort = 9001f +	usracc	ld, \reg, \ptr, \inc, \cond, \rept, \abort +	.endm + +	.macro	nop8 +	.rept	8 +		nop +	.endr +	.endm diff --git a/arch/unicore32/include/asm/barrier.h b/arch/unicore32/include/asm/barrier.h new file mode 100644 index 00000000000..83d6a520f4b --- /dev/null +++ b/arch/unicore32/include/asm/barrier.h @@ -0,0 +1,19 @@ +/* + * Memory barrier implementations for PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2012 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_BARRIER_H__ +#define __UNICORE_BARRIER_H__ + +#define isb() __asm__ __volatile__ ("" : : : "memory") +#define dsb() __asm__ __volatile__ ("" : : : "memory") +#define dmb() __asm__ __volatile__ ("" : : : "memory") + +#include <asm-generic/barrier.h> + +#endif /* __UNICORE_BARRIER_H__ */ diff --git a/arch/unicore32/include/asm/bitops.h b/arch/unicore32/include/asm/bitops.h new file mode 100644 index 00000000000..401f597bc38 --- /dev/null +++ b/arch/unicore32/include/asm/bitops.h @@ -0,0 +1,47 @@ +/* + * linux/arch/unicore32/include/asm/bitops.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __UNICORE_BITOPS_H__ +#define __UNICORE_BITOPS_H__ + +#define _ASM_GENERIC_BITOPS_FLS_H_ +#define _ASM_GENERIC_BITOPS___FLS_H_ +#define _ASM_GENERIC_BITOPS_FFS_H_ +#define _ASM_GENERIC_BITOPS___FFS_H_ +/* + * On UNICORE, those functions can be implemented around + * the cntlz instruction for much better code efficiency. + */ + +static inline int fls(int x) +{ +	int ret; + +	asm("cntlz\t%0, %1" : "=r" (ret) : "r" (x) : "cc"); +	ret = 32 - ret; + +	return ret; +} + +#define __fls(x) (fls(x) - 1) +#define ffs(x) ({ unsigned long __t = (x); fls(__t & -__t); }) +#define __ffs(x) (ffs(x) - 1) + +#include <asm-generic/bitops.h> + +/* following definitions: to avoid using codes in lib/find_*.c */ +#define find_next_bit		find_next_bit +#define find_next_zero_bit	find_next_zero_bit +#define find_first_bit		find_first_bit +#define find_first_zero_bit	find_first_zero_bit + +#endif /* __UNICORE_BITOPS_H__ */ diff --git a/arch/unicore32/include/asm/bug.h b/arch/unicore32/include/asm/bug.h new file mode 100644 index 00000000000..93a56f3e234 --- /dev/null +++ b/arch/unicore32/include/asm/bug.h @@ -0,0 +1,22 @@ +/* + * Bug handling for PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2012 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_BUG_H__ +#define __UNICORE_BUG_H__ + +#include <asm-generic/bug.h> + +struct pt_regs; +struct siginfo; + +extern void die(const char *msg, struct pt_regs *regs, int err); +extern void uc32_notify_die(const char *str, struct pt_regs *regs, +		struct siginfo *info, unsigned long err, unsigned long trap); + +#endif /* __UNICORE_BUG_H__ */ diff --git a/arch/unicore32/include/asm/cache.h b/arch/unicore32/include/asm/cache.h new file mode 100644 index 00000000000..ad8f795d86c --- /dev/null +++ b/arch/unicore32/include/asm/cache.h @@ -0,0 +1,27 @@ +/* + * linux/arch/unicore32/include/asm/cache.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_CACHE_H__ +#define __UNICORE_CACHE_H__ + +#define L1_CACHE_SHIFT		(5) +#define L1_CACHE_BYTES		(1 << L1_CACHE_SHIFT) + +/* + * Memory returned by kmalloc() may be used for DMA, so we must make + * sure that all such allocations are cache aligned. Otherwise, + * unrelated code may cause parts of the buffer to be read into the + * cache before the transfer is done, causing old data to be seen by + * the CPU. + */ +#define ARCH_DMA_MINALIGN	L1_CACHE_BYTES + +#endif diff --git a/arch/unicore32/include/asm/cacheflush.h b/arch/unicore32/include/asm/cacheflush.h new file mode 100644 index 00000000000..c0301e6c8b8 --- /dev/null +++ b/arch/unicore32/include/asm/cacheflush.h @@ -0,0 +1,211 @@ +/* + * linux/arch/unicore32/include/asm/cacheflush.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_CACHEFLUSH_H__ +#define __UNICORE_CACHEFLUSH_H__ + +#include <linux/mm.h> + +#include <asm/shmparam.h> + +#define CACHE_COLOUR(vaddr)	((vaddr & (SHMLBA - 1)) >> PAGE_SHIFT) + +/* + * This flag is used to indicate that the page pointed to by a pte is clean + * and does not require cleaning before returning it to the user. + */ +#define PG_dcache_clean PG_arch_1 + +/* + *	MM Cache Management + *	=================== + * + *	The arch/unicore32/mm/cache.S files implement these methods. + * + *	Start addresses are inclusive and end addresses are exclusive; + *	start addresses should be rounded down, end addresses up. + * + *	See Documentation/cachetlb.txt for more information. + *	Please note that the implementation of these, and the required + *	effects are cache-type (VIVT/VIPT/PIPT) specific. + * + *	flush_icache_all() + * + *		Unconditionally clean and invalidate the entire icache. + *		Currently only needed for cache-v6.S and cache-v7.S, see + *		__flush_icache_all for the generic implementation. + * + *	flush_kern_all() + * + *		Unconditionally clean and invalidate the entire cache. + * + *	flush_user_all() + * + *		Clean and invalidate all user space cache entries + *		before a change of page tables. + * + *	flush_user_range(start, end, flags) + * + *		Clean and invalidate a range of cache entries in the + *		specified address space before a change of page tables. + *		- start - user start address (inclusive, page aligned) + *		- end   - user end address   (exclusive, page aligned) + *		- flags - vma->vm_flags field + * + *	coherent_kern_range(start, end) + * + *		Ensure coherency between the Icache and the Dcache in the + *		region described by start, end.  If you have non-snooping + *		Harvard caches, you need to implement this function. + *		- start  - virtual start address + *		- end    - virtual end address + * + *	coherent_user_range(start, end) + * + *		Ensure coherency between the Icache and the Dcache in the + *		region described by start, end.  If you have non-snooping + *		Harvard caches, you need to implement this function. + *		- start  - virtual start address + *		- end    - virtual end address + * + *	flush_kern_dcache_area(kaddr, size) + * + *		Ensure that the data held in page is written back. + *		- kaddr  - page address + *		- size   - region size + * + *	DMA Cache Coherency + *	=================== + * + *	dma_flush_range(start, end) + * + *		Clean and invalidate the specified virtual address range. + *		- start  - virtual start address + *		- end    - virtual end address + */ + +extern void __cpuc_flush_icache_all(void); +extern void __cpuc_flush_kern_all(void); +extern void __cpuc_flush_user_all(void); +extern void __cpuc_flush_user_range(unsigned long, unsigned long, unsigned int); +extern void __cpuc_coherent_kern_range(unsigned long, unsigned long); +extern void __cpuc_coherent_user_range(unsigned long, unsigned long); +extern void __cpuc_flush_dcache_area(void *, size_t); +extern void __cpuc_flush_kern_dcache_area(void *addr, size_t size); + +/* + * These are private to the dma-mapping API.  Do not use directly. + * Their sole purpose is to ensure that data held in the cache + * is visible to DMA, or data written by DMA to system memory is + * visible to the CPU. + */ +extern void __cpuc_dma_clean_range(unsigned long, unsigned long); +extern void __cpuc_dma_flush_range(unsigned long, unsigned long); + +/* + * Copy user data from/to a page which is mapped into a different + * processes address space.  Really, we want to allow our "user + * space" model to handle this. + */ +extern void copy_to_user_page(struct vm_area_struct *, struct page *, +	unsigned long, void *, const void *, unsigned long); +#define copy_from_user_page(vma, page, vaddr, dst, src, len)	\ +	do {							\ +		memcpy(dst, src, len);				\ +	} while (0) + +/* + * Convert calls to our calling convention. + */ +/* Invalidate I-cache */ +static inline void __flush_icache_all(void) +{ +	asm("movc	p0.c5, %0, #20;\n" +	    "nop; nop; nop; nop; nop; nop; nop; nop\n" +	    : +	    : "r" (0)); +} + +#define flush_cache_all()		__cpuc_flush_kern_all() + +extern void flush_cache_mm(struct mm_struct *mm); +extern void flush_cache_range(struct vm_area_struct *vma, +		unsigned long start, unsigned long end); +extern void flush_cache_page(struct vm_area_struct *vma, +		unsigned long user_addr, unsigned long pfn); + +#define flush_cache_dup_mm(mm) flush_cache_mm(mm) + +/* + * flush_cache_user_range is used when we want to ensure that the + * Harvard caches are synchronised for the user space address range. + * This is used for the UniCore private sys_cacheflush system call. + */ +#define flush_cache_user_range(vma, start, end) \ +	__cpuc_coherent_user_range((start) & PAGE_MASK, PAGE_ALIGN(end)) + +/* + * Perform necessary cache operations to ensure that data previously + * stored within this range of addresses can be executed by the CPU. + */ +#define flush_icache_range(s, e)	__cpuc_coherent_kern_range(s, e) + +/* + * Perform necessary cache operations to ensure that the TLB will + * see data written in the specified area. + */ +#define clean_dcache_area(start, size)	cpu_dcache_clean_area(start, size) + +/* + * flush_dcache_page is used when the kernel has written to the page + * cache page at virtual address page->virtual. + * + * If this page isn't mapped (ie, page_mapping == NULL), or it might + * have userspace mappings, then we _must_ always clean + invalidate + * the dcache entries associated with the kernel mapping. + * + * Otherwise we can defer the operation, and clean the cache when we are + * about to change to user space.  This is the same method as used on SPARC64. + * See update_mmu_cache for the user space part. + */ +#define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 1 +extern void flush_dcache_page(struct page *); + +#define flush_dcache_mmap_lock(mapping)			\ +	spin_lock_irq(&(mapping)->tree_lock) +#define flush_dcache_mmap_unlock(mapping)		\ +	spin_unlock_irq(&(mapping)->tree_lock) + +#define flush_icache_user_range(vma, page, addr, len)	\ +	flush_dcache_page(page) + +/* + * We don't appear to need to do anything here.  In fact, if we did, we'd + * duplicate cache flushing elsewhere performed by flush_dcache_page(). + */ +#define flush_icache_page(vma, page)	do { } while (0) + +/* + * flush_cache_vmap() is used when creating mappings (eg, via vmap, + * vmalloc, ioremap etc) in kernel space for pages.  On non-VIPT + * caches, since the direct-mappings of these pages may contain cached + * data, we need to do a full cache flush to ensure that writebacks + * don't corrupt data placed into these pages via the new mappings. + */ +static inline void flush_cache_vmap(unsigned long start, unsigned long end) +{ +} + +static inline void flush_cache_vunmap(unsigned long start, unsigned long end) +{ +} + +#endif diff --git a/arch/unicore32/include/asm/checksum.h b/arch/unicore32/include/asm/checksum.h new file mode 100644 index 00000000000..f55c3f937c3 --- /dev/null +++ b/arch/unicore32/include/asm/checksum.h @@ -0,0 +1,41 @@ +/* + * linux/arch/unicore32/include/asm/checksum.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * 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. + * + * IP checksum routines + */ +#ifndef __UNICORE_CHECKSUM_H__ +#define __UNICORE_CHECKSUM_H__ + +/* + * computes the checksum of the TCP/UDP pseudo-header + * returns a 16-bit checksum, already complemented + */ + +static inline __wsum +csum_tcpudp_nofold(__be32 saddr, __be32 daddr, unsigned short len, +		   unsigned short proto, __wsum sum) +{ +	__asm__( +	"add.a	%0, %1, %2\n" +	"addc.a	%0, %0, %3\n" +	"addc.a	%0, %0, %4 << #8\n" +	"addc.a	%0, %0, %5\n" +	"addc	%0, %0, #0\n" +	: "=&r"(sum) +	: "r" (sum), "r" (daddr), "r" (saddr), "r" (len), "Ir" (htons(proto)) +	: "cc"); +	return sum; +} +#define csum_tcpudp_nofold	csum_tcpudp_nofold + +#include <asm-generic/checksum.h> + +#endif diff --git a/arch/unicore32/include/asm/cmpxchg.h b/arch/unicore32/include/asm/cmpxchg.h new file mode 100644 index 00000000000..8e797ad4fa2 --- /dev/null +++ b/arch/unicore32/include/asm/cmpxchg.h @@ -0,0 +1,61 @@ +/* + * Atomics xchg/cmpxchg for PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2012 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_CMPXCHG_H__ +#define __UNICORE_CMPXCHG_H__ + +/* + * Generate a link failure on undefined symbol if the pointer points to a value + * of unsupported size. + */ +extern void __xchg_bad_pointer(void); + +static inline unsigned long __xchg(unsigned long x, volatile void *ptr, +		int size) +{ +	unsigned long ret; + +	switch (size) { +	case 1: +		asm volatile("swapb	%0, %1, [%2]" +			: "=&r" (ret) +			: "r" (x), "r" (ptr) +			: "memory", "cc"); +		break; +	case 4: +		asm volatile("swapw	%0, %1, [%2]" +			: "=&r" (ret) +			: "r" (x), "r" (ptr) +			: "memory", "cc"); +		break; +	default: +		__xchg_bad_pointer(); +	} + +	return ret; +} + +#define xchg(ptr, x) \ +	((__typeof__(*(ptr)))__xchg((unsigned long)(x), (ptr), sizeof(*(ptr)))) + +#include <asm-generic/cmpxchg-local.h> + +/* + * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make + * them available. + */ +#define cmpxchg_local(ptr, o, n)					\ +		((__typeof__(*(ptr)))__cmpxchg_local_generic((ptr),	\ +		(unsigned long)(o), (unsigned long)(n), sizeof(*(ptr)))) +#define cmpxchg64_local(ptr, o, n)					\ +		__cmpxchg64_local_generic((ptr), (o), (n)) + +#include <asm-generic/cmpxchg.h> + +#endif /* __UNICORE_CMPXCHG_H__ */ diff --git a/arch/unicore32/include/asm/cpu-single.h b/arch/unicore32/include/asm/cpu-single.h new file mode 100644 index 00000000000..0f55d182343 --- /dev/null +++ b/arch/unicore32/include/asm/cpu-single.h @@ -0,0 +1,45 @@ +/* + * linux/arch/unicore32/include/asm/cpu-single.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_CPU_SINGLE_H__ +#define __UNICORE_CPU_SINGLE_H__ + +#include <asm/page.h> +#include <asm/memory.h> + +#ifdef __KERNEL__ +#ifndef __ASSEMBLY__ + +#define cpu_switch_mm(pgd, mm) cpu_do_switch_mm(virt_to_phys(pgd), mm) + +#define cpu_get_pgd()					\ +	({						\ +		unsigned long pg;			\ +		__asm__("movc	%0, p0.c2, #0"		\ +			 : "=r" (pg) : : "cc");		\ +		pg &= ~0x0fff;				\ +		(pgd_t *)phys_to_virt(pg);		\ +	}) + +struct mm_struct; + +/* declare all the functions as extern */ +extern void cpu_proc_fin(void); +extern int cpu_do_idle(void); +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(pte_t *ptep, pte_t pte); +extern void cpu_reset(unsigned long addr) __attribute__((noreturn)); + +#endif /* __ASSEMBLY__ */ +#endif /* __KERNEL__ */ + +#endif /* __UNICORE_CPU_SINGLE_H__ */ diff --git a/arch/unicore32/include/asm/cputype.h b/arch/unicore32/include/asm/cputype.h new file mode 100644 index 00000000000..ec1a30f9807 --- /dev/null +++ b/arch/unicore32/include/asm/cputype.h @@ -0,0 +1,33 @@ +/* + * linux/arch/unicore32/include/asm/cputype.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_CPUTYPE_H__ +#define __UNICORE_CPUTYPE_H__ + +#include <linux/stringify.h> + +#define CPUID_CPUID	0 +#define CPUID_CACHETYPE	1 + +#define read_cpuid(reg)							\ +	({								\ +		unsigned int __val;					\ +		asm("movc	%0, p0.c0, #" __stringify(reg)		\ +		    : "=r" (__val)					\ +		    :							\ +		    : "cc");						\ +		__val;							\ +	}) + +#define uc32_cpuid		read_cpuid(CPUID_CPUID) +#define uc32_cachetype		read_cpuid(CPUID_CACHETYPE) + +#endif diff --git a/arch/unicore32/include/asm/delay.h b/arch/unicore32/include/asm/delay.h new file mode 100644 index 00000000000..164ae61cd6f --- /dev/null +++ b/arch/unicore32/include/asm/delay.h @@ -0,0 +1,52 @@ +/* + * linux/arch/unicore32/include/asm/delay.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * 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. + * + * Delay routines, using a pre-computed "loops_per_second" value. + */ +#ifndef __UNICORE_DELAY_H__ +#define __UNICORE_DELAY_H__ + +#include <asm/param.h>	/* HZ */ + +extern void __delay(int loops); + +/* + * This function intentionally does not exist; if you see references to + * it, it means that you're calling udelay() with an out of range value. + * + * With currently imposed limits, this means that we support a max delay + * of 2000us. Further limits: HZ<=1000 and bogomips<=3355 + */ +extern void __bad_udelay(void); + +/* + * division by multiplication: you don't have to worry about + * loss of precision. + * + * Use only for very small delays ( < 1 msec).  Should probably use a + * lookup table, really, as the multiplications take much too long with + * short delays.  This is a "reasonable" implementation, though (and the + * first constant multiplications gets optimized away if the delay is + * a constant) + */ +extern void __udelay(unsigned long usecs); +extern void __const_udelay(unsigned long); + +#define MAX_UDELAY_MS 2 + +#define udelay(n)							\ +	(__builtin_constant_p(n) ?					\ +	  ((n) > (MAX_UDELAY_MS * 1000) ? __bad_udelay() :		\ +			__const_udelay((n) * ((2199023U*HZ)>>11))) :	\ +	  __udelay(n)) + +#endif /* __UNICORE_DELAY_H__ */ + diff --git a/arch/unicore32/include/asm/dma-mapping.h b/arch/unicore32/include/asm/dma-mapping.h new file mode 100644 index 00000000000..366460a8179 --- /dev/null +++ b/arch/unicore32/include/asm/dma-mapping.h @@ -0,0 +1,130 @@ +/* + * linux/arch/unicore32/include/asm/dma-mapping.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_DMA_MAPPING_H__ +#define __UNICORE_DMA_MAPPING_H__ + +#ifdef __KERNEL__ + +#include <linux/mm_types.h> +#include <linux/scatterlist.h> +#include <linux/swiotlb.h> + +#include <asm-generic/dma-coherent.h> + +#include <asm/memory.h> +#include <asm/cacheflush.h> + +extern struct dma_map_ops swiotlb_dma_map_ops; + +static inline struct dma_map_ops *get_dma_ops(struct device *dev) +{ +	return &swiotlb_dma_map_ops; +} + +static inline int dma_supported(struct device *dev, u64 mask) +{ +	struct dma_map_ops *dma_ops = get_dma_ops(dev); + +	if (unlikely(dma_ops == NULL)) +		return 0; + +	return dma_ops->dma_supported(dev, mask); +} + +static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) +{ +	struct dma_map_ops *dma_ops = get_dma_ops(dev); + +	if (dma_ops->mapping_error) +		return dma_ops->mapping_error(dev, dma_addr); + +	return 0; +} + +#include <asm-generic/dma-mapping-common.h> + +static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size) +{ +	if (dev && dev->dma_mask) +		return addr + size - 1 <= *dev->dma_mask; + +	return 1; +} + +static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr) +{ +	return paddr; +} + +static inline phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr) +{ +	return daddr; +} + +static inline void dma_mark_clean(void *addr, size_t size) {} + +static inline int dma_set_mask(struct device *dev, u64 dma_mask) +{ +	if (!dev->dma_mask || !dma_supported(dev, dma_mask)) +		return -EIO; + +	*dev->dma_mask = dma_mask; + +	return 0; +} + +#define dma_alloc_coherent(d,s,h,f)	dma_alloc_attrs(d,s,h,f,NULL) + +static inline void *dma_alloc_attrs(struct device *dev, size_t size, +				    dma_addr_t *dma_handle, gfp_t flag, +				    struct dma_attrs *attrs) +{ +	struct dma_map_ops *dma_ops = get_dma_ops(dev); + +	return dma_ops->alloc(dev, size, dma_handle, flag, attrs); +} + +#define dma_free_coherent(d,s,c,h) dma_free_attrs(d,s,c,h,NULL) + +static inline void dma_free_attrs(struct device *dev, size_t size, +				  void *cpu_addr, dma_addr_t dma_handle, +				  struct dma_attrs *attrs) +{ +	struct dma_map_ops *dma_ops = get_dma_ops(dev); + +	dma_ops->free(dev, size, cpu_addr, dma_handle, attrs); +} + +#define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f) +#define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h) + +static inline void dma_cache_sync(struct device *dev, void *vaddr, +		size_t size, enum dma_data_direction direction) +{ +	unsigned long start = (unsigned long)vaddr; +	unsigned long end   = start + size; + +	switch (direction) { +	case DMA_NONE: +		BUG(); +	case DMA_FROM_DEVICE: +	case DMA_BIDIRECTIONAL:	/* writeback and invalidate */ +		__cpuc_dma_flush_range(start, end); +		break; +	case DMA_TO_DEVICE:		/* writeback only */ +		__cpuc_dma_clean_range(start, end); +		break; +	} +} + +#endif /* __KERNEL__ */ +#endif diff --git a/arch/unicore32/include/asm/dma.h b/arch/unicore32/include/asm/dma.h new file mode 100644 index 00000000000..38dfff9df32 --- /dev/null +++ b/arch/unicore32/include/asm/dma.h @@ -0,0 +1,23 @@ +/* + * linux/arch/unicore32/include/asm/dma.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __UNICORE_DMA_H__ +#define __UNICORE_DMA_H__ + +#include <asm/memory.h> +#include <asm-generic/dma.h> + +#ifdef CONFIG_PCI +extern int isa_dma_bridge_buggy; +#endif + +#endif /* __UNICORE_DMA_H__ */ diff --git a/arch/unicore32/include/asm/elf.h b/arch/unicore32/include/asm/elf.h new file mode 100644 index 00000000000..829042d0772 --- /dev/null +++ b/arch/unicore32/include/asm/elf.h @@ -0,0 +1,94 @@ +/* + * linux/arch/unicore32/include/asm/elf.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __UNICORE_ELF_H__ +#define __UNICORE_ELF_H__ + +#include <asm/hwcap.h> + +/* + * ELF register definitions.. + */ +#include <asm/ptrace.h> + +typedef unsigned long elf_greg_t; +typedef unsigned long elf_freg_t[3]; + +#define ELF_NGREG (sizeof(struct pt_regs) / sizeof(elf_greg_t)) +typedef elf_greg_t elf_gregset_t[ELF_NGREG]; + +typedef struct fp_state elf_fpregset_t; + +#define EM_UNICORE		110 + +#define R_UNICORE_NONE		0 +#define R_UNICORE_PC24		1 +#define R_UNICORE_ABS32		2 +#define R_UNICORE_CALL		28 +#define R_UNICORE_JUMP24	29 + +/* + * These are used to set parameters in the core dumps. + */ +#define ELF_CLASS	ELFCLASS32 +#define ELF_DATA	ELFDATA2LSB +#define ELF_ARCH	EM_UNICORE + +/* + * This yields a string that ld.so will use to load implementation + * specific libraries for optimization.  This is more specific in + * intent than poking at uname or /proc/cpuinfo. + * + */ +#define ELF_PLATFORM_SIZE 8 +#define ELF_PLATFORM	(elf_platform) + +extern char elf_platform[]; + +struct elf32_hdr; + +/* + * This is used to ensure we don't load something for the wrong architecture. + */ +extern int elf_check_arch(const struct elf32_hdr *); +#define elf_check_arch elf_check_arch + +struct task_struct; +int dump_task_regs(struct task_struct *t, elf_gregset_t *elfregs); +#define ELF_CORE_COPY_TASK_REGS dump_task_regs + +#define ELF_EXEC_PAGESIZE	4096 + +/* This is the location that an ET_DYN program is loaded if exec'ed.  Typical +   use of this is to invoke "./ld.so someprog" to test out a new version of +   the loader.  We need to make sure that it is out of the way of the program +   that it will "exec", and that there is sufficient room for the brk.  */ + +#define ELF_ET_DYN_BASE	(2 * TASK_SIZE / 3) + +/* When the program starts, a1 contains a pointer to a function to be +   registered with atexit, as per the SVR4 ABI.  A value of 0 means we +   have no such handler.  */ +#define ELF_PLAT_INIT(_r, load_addr)	{(_r)->UCreg_00 = 0; } + +extern void elf_set_personality(const struct elf32_hdr *); +#define SET_PERSONALITY(ex)	elf_set_personality(&(ex)) + +struct mm_struct; +extern unsigned long arch_randomize_brk(struct mm_struct *mm); +#define arch_randomize_brk arch_randomize_brk + +extern int vectors_user_mapping(void); +#define arch_setup_additional_pages(bprm, uses_interp) vectors_user_mapping() +#define ARCH_HAS_SETUP_ADDITIONAL_PAGES + +#endif diff --git a/arch/unicore32/include/asm/fpstate.h b/arch/unicore32/include/asm/fpstate.h new file mode 100644 index 00000000000..ba97fac6220 --- /dev/null +++ b/arch/unicore32/include/asm/fpstate.h @@ -0,0 +1,26 @@ +/* + * linux/arch/unicore32/include/asm/fpstate.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __UNICORE_FPSTATE_H__ +#define __UNICORE_FPSTATE_H__ + +#ifndef __ASSEMBLY__ + +#define FP_REGS_NUMBER		33 + +struct fp_state { +	unsigned int regs[FP_REGS_NUMBER]; +} __attribute__((aligned(8))); + +#endif + +#endif diff --git a/arch/unicore32/include/asm/fpu-ucf64.h b/arch/unicore32/include/asm/fpu-ucf64.h new file mode 100644 index 00000000000..16c1457882e --- /dev/null +++ b/arch/unicore32/include/asm/fpu-ucf64.h @@ -0,0 +1,53 @@ +/* + * linux/arch/unicore32/include/asm/fpu-ucf64.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + *	Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn> + *	Copyright (C) 2001-2010 Guan Xuetao + * + * 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 FPSCR			s31 + +/* FPSCR bits */ +#define FPSCR_DEFAULT_NAN	(1<<25) + +#define FPSCR_CMPINSTR_BIT	(1<<31) + +#define FPSCR_CON		(1<<29) +#define FPSCR_TRAP		(1<<27) + +/* RND mode */ +#define FPSCR_ROUND_NEAREST	(0<<0) +#define FPSCR_ROUND_PLUSINF	(2<<0) +#define FPSCR_ROUND_MINUSINF	(3<<0) +#define FPSCR_ROUND_TOZERO	(1<<0) +#define FPSCR_RMODE_BIT		(0) +#define FPSCR_RMODE_MASK	(7 << FPSCR_RMODE_BIT) + +/* trap enable */ +#define FPSCR_IOE		(1<<16) +#define FPSCR_OFE		(1<<14) +#define FPSCR_UFE		(1<<13) +#define FPSCR_IXE		(1<<12) +#define FPSCR_HIE		(1<<11) +#define FPSCR_NDE		(1<<10)	/* non denomal */ + +/* flags */ +#define FPSCR_IDC		(1<<24) +#define FPSCR_HIC		(1<<23) +#define FPSCR_IXC		(1<<22) +#define FPSCR_OFC		(1<<21) +#define FPSCR_UFC		(1<<20) +#define FPSCR_IOC		(1<<19) + +/* stick bits */ +#define FPSCR_IOS		(1<<9) +#define FPSCR_OFS		(1<<7) +#define FPSCR_UFS		(1<<6) +#define FPSCR_IXS		(1<<5) +#define FPSCR_HIS		(1<<4) +#define FPSCR_NDS		(1<<3)	/*non denomal */ diff --git a/arch/unicore32/include/asm/gpio.h b/arch/unicore32/include/asm/gpio.h new file mode 100644 index 00000000000..2716f14e3ff --- /dev/null +++ b/arch/unicore32/include/asm/gpio.h @@ -0,0 +1,104 @@ +/* + * linux/arch/unicore32/include/asm/gpio.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __UNICORE_GPIO_H__ +#define __UNICORE_GPIO_H__ + +#include <linux/io.h> +#include <asm/irq.h> +#include <mach/hardware.h> +#include <asm-generic/gpio.h> + +#define GPI_OTP_INT             0 +#define GPI_PCI_INTA            1 +#define GPI_PCI_INTB            2 +#define GPI_PCI_INTC            3 +#define GPI_PCI_INTD            4 +#define GPI_BAT_DET             5 +#define GPI_SD_CD               6 +#define GPI_SOFF_REQ            7 +#define GPI_SD_WP               8 +#define GPI_LCD_CASE_OFF        9 +#define GPO_WIFI_EN             10 +#define GPO_HDD_LED             11 +#define GPO_VGA_EN              12 +#define GPO_LCD_EN              13 +#define GPO_LED_DATA            14 +#define GPO_LED_CLK             15 +#define GPO_CAM_PWR_EN          16 +#define GPO_LCD_VCC_EN          17 +#define GPO_SOFT_OFF            18 +#define GPO_BT_EN               19 +#define GPO_FAN_ON              20 +#define GPO_SPKR                21 +#define GPO_SET_V1              23 +#define GPO_SET_V2              24 +#define GPO_CPU_HEALTH          25 +#define GPO_LAN_SEL             26 + +#ifdef CONFIG_PUV3_NB0916 +#define GPI_BTN_TOUCH		14 +#define GPIO_IN			0x000043ff /* 1 for input */ +#define GPIO_OUT		0x0fffbc00 /* 1 for output */ +#endif	/* CONFIG_PUV3_NB0916 */ + +#ifdef CONFIG_PUV3_SMW0919 +#define GPIO_IN			0x000003ff /* 1 for input */ +#define GPIO_OUT		0x0ffffc00 /* 1 for output */ +#endif  /* CONFIG_PUV3_SMW0919 */ + +#ifdef CONFIG_PUV3_DB0913 +#define GPIO_IN			0x000001df /* 1 for input */ +#define GPIO_OUT		0x03fee800 /* 1 for output */ +#endif  /* CONFIG_PUV3_DB0913 */ + +#define GPIO_DIR                (~((GPIO_IN) | 0xf0000000)) +				/* 0 input, 1 output */ + +static inline int gpio_get_value(unsigned gpio) +{ +	if (__builtin_constant_p(gpio) && (gpio <= GPIO_MAX)) +		return readl(GPIO_GPLR) & GPIO_GPIO(gpio); +	else +		return __gpio_get_value(gpio); +} + +static inline void gpio_set_value(unsigned gpio, int value) +{ +	if (__builtin_constant_p(gpio) && (gpio <= GPIO_MAX)) +		if (value) +			writel(GPIO_GPIO(gpio), GPIO_GPSR); +		else +			writel(GPIO_GPIO(gpio), GPIO_GPCR); +	else +		__gpio_set_value(gpio, value); +} + +#define gpio_cansleep	__gpio_cansleep + +static inline unsigned gpio_to_irq(unsigned gpio) +{ +	if ((gpio < IRQ_GPIOHIGH) && (FIELD(1, 1, gpio) & readl(GPIO_GPIR))) +		return IRQ_GPIOLOW0 + gpio; +	else +		return IRQ_GPIO0 + gpio; +} + +static inline unsigned irq_to_gpio(unsigned irq) +{ +	if (irq < IRQ_GPIOHIGH) +		return irq - IRQ_GPIOLOW0; +	else +		return irq - IRQ_GPIO0; +} + +#endif /* __UNICORE_GPIO_H__ */ diff --git a/arch/unicore32/include/asm/hwcap.h b/arch/unicore32/include/asm/hwcap.h new file mode 100644 index 00000000000..97bd40fdd4a --- /dev/null +++ b/arch/unicore32/include/asm/hwcap.h @@ -0,0 +1,32 @@ +/* + * linux/arch/unicore32/include/asm/hwcap.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_HWCAP_H__ +#define __UNICORE_HWCAP_H__ + +/* + * HWCAP flags + */ +#define HWCAP_MSP		1 +#define HWCAP_UNICORE16		2 +#define HWCAP_CMOV		4 +#define HWCAP_UNICORE_F64       8 +#define HWCAP_TLS		0x80 + +#if defined(__KERNEL__) && !defined(__ASSEMBLY__) +/* + * This yields a mask that user programs can use to figure out what + * instruction set this cpu supports. + */ +#define ELF_HWCAP		(HWCAP_CMOV | HWCAP_UNICORE_F64) +#endif + +#endif diff --git a/arch/unicore32/include/asm/hwdef-copro.h b/arch/unicore32/include/asm/hwdef-copro.h new file mode 100644 index 00000000000..a3292f039a6 --- /dev/null +++ b/arch/unicore32/include/asm/hwdef-copro.h @@ -0,0 +1,48 @@ +/* + * Co-processor register definitions for PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2012 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_HWDEF_COPRO_H__ +#define __UNICORE_HWDEF_COPRO_H__ + +/* + * Control Register bits (CP#0 CR1) + */ +#define CR_M	(1 << 0)	/* MMU enable				*/ +#define CR_A	(1 << 1)	/* Alignment abort enable		*/ +#define CR_D	(1 << 2)	/* Dcache enable			*/ +#define CR_I	(1 << 3)	/* Icache enable			*/ +#define CR_B	(1 << 4)	/* Dcache write mechanism: write back	*/ +#define CR_T	(1 << 5)	/* Burst enable				*/ +#define CR_V	(1 << 13)	/* Vectors relocated to 0xffff0000	*/ + +#ifndef __ASSEMBLY__ + +#define vectors_high()		(cr_alignment & CR_V) + +extern unsigned long cr_no_alignment;	/* defined in entry.S */ +extern unsigned long cr_alignment;	/* defined in entry.S */ + +static inline unsigned int get_cr(void) +{ +	unsigned int val; +	asm("movc %0, p0.c1, #0" : "=r" (val) : : "cc"); +	return val; +} + +static inline void set_cr(unsigned int val) +{ +	asm volatile("movc p0.c1, %0, #0" : : "r" (val) : "cc"); +	isb(); +} + +extern void adjust_cr(unsigned long mask, unsigned long set); + +#endif /* __ASSEMBLY__ */ + +#endif /* __UNICORE_HWDEF_COPRO_H__ */ diff --git a/arch/unicore32/include/asm/io.h b/arch/unicore32/include/asm/io.h new file mode 100644 index 00000000000..cb1d8fd2b16 --- /dev/null +++ b/arch/unicore32/include/asm/io.h @@ -0,0 +1,75 @@ +/* + * linux/arch/unicore32/include/asm/io.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_IO_H__ +#define __UNICORE_IO_H__ + +#ifdef __KERNEL__ + +#include <asm/byteorder.h> +#include <asm/memory.h> + +#define PCI_IOBASE	PKUNITY_PCILIO_BASE +#include <asm-generic/io.h> + +/* + * __uc32_ioremap and __uc32_ioremap_cached takes CPU physical address. + */ +extern void __iomem *__uc32_ioremap(unsigned long, size_t); +extern void __iomem *__uc32_ioremap_cached(unsigned long, size_t); +extern void __uc32_iounmap(volatile void __iomem *addr); + +/* + * ioremap and friends. + * + * ioremap takes a PCI memory address, as specified in + * Documentation/io-mapping.txt. + * + */ +#define ioremap(cookie, size)		__uc32_ioremap(cookie, size) +#define ioremap_cached(cookie, size)	__uc32_ioremap_cached(cookie, size) +#define ioremap_nocache(cookie, size)	__uc32_ioremap(cookie, size) +#define iounmap(cookie)			__uc32_iounmap(cookie) + +#define readb_relaxed readb +#define readw_relaxed readw +#define readl_relaxed readl + +#define HAVE_ARCH_PIO_SIZE +#define PIO_OFFSET		(unsigned int)(PCI_IOBASE) +#define PIO_MASK		(unsigned int)(IO_SPACE_LIMIT) +#define PIO_RESERVED		(PIO_OFFSET + PIO_MASK + 1) + +#ifdef CONFIG_STRICT_DEVMEM + +#include <linux/ioport.h> +#include <linux/mm.h> + +/* + * devmem_is_allowed() checks to see if /dev/mem access to a certain + * address is valid. The argument is a physical page number. + * We mimic x86 here by disallowing access to system RAM as well as + * device-exclusive MMIO regions. This effectively disable read()/write() + * on /dev/mem. + */ +static inline int devmem_is_allowed(unsigned long pfn) +{ +	if (iomem_is_exclusive(pfn << PAGE_SHIFT)) +		return 0; +	if (!page_is_ram(pfn)) +		return 1; +	return 0; +} + +#endif /* CONFIG_STRICT_DEVMEM */ + +#endif	/* __KERNEL__ */ +#endif	/* __UNICORE_IO_H__ */ diff --git a/arch/unicore32/include/asm/irq.h b/arch/unicore32/include/asm/irq.h new file mode 100644 index 00000000000..baea93e2a6e --- /dev/null +++ b/arch/unicore32/include/asm/irq.h @@ -0,0 +1,105 @@ +/* + * linux/arch/unicore32/include/asm/irq.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_IRQ_H__ +#define __UNICORE_IRQ_H__ + +#include <asm-generic/irq.h> + +#define	IRQ_GPIOLOW0		0x00 +#define	IRQ_GPIOLOW1		0x01 +#define	IRQ_GPIOLOW2		0x02 +#define	IRQ_GPIOLOW3		0x03 +#define	IRQ_GPIOLOW4		0x04 +#define	IRQ_GPIOLOW5		0x05 +#define	IRQ_GPIOLOW6		0x06 +#define	IRQ_GPIOLOW7		0x07 +#define IRQ_GPIOHIGH		0x08 +#define IRQ_USB			0x09 +#define IRQ_SDC			0x0a +#define IRQ_AC97		0x0b +#define IRQ_SATA		0x0c +#define IRQ_MME			0x0d +#define IRQ_PCI_BRIDGE		0x0e +#define	IRQ_DDR			0x0f +#define	IRQ_SPI			0x10 +#define	IRQ_UNIGFX		0x11 +#define	IRQ_I2C			0x11 +#define	IRQ_UART1		0x12 +#define	IRQ_UART0		0x13 +#define IRQ_UMAL		0x14 +#define IRQ_NAND		0x15 +#define IRQ_PS2_KBD		0x16 +#define IRQ_PS2_AUX		0x17 +#define IRQ_DMA			0x18 +#define IRQ_DMAERR		0x19 +#define	IRQ_TIMER0		0x1a +#define	IRQ_TIMER1		0x1b +#define	IRQ_TIMER2		0x1c +#define	IRQ_TIMER3		0x1d +#define	IRQ_RTC			0x1e +#define	IRQ_RTCAlarm		0x1f + +#define	IRQ_GPIO0		0x20 +#define	IRQ_GPIO1		0x21 +#define	IRQ_GPIO2		0x22 +#define	IRQ_GPIO3		0x23 +#define	IRQ_GPIO4		0x24 +#define	IRQ_GPIO5		0x25 +#define	IRQ_GPIO6		0x26 +#define	IRQ_GPIO7		0x27 +#define IRQ_GPIO8		0x28 +#define IRQ_GPIO9		0x29 +#define IRQ_GPIO10		0x2a +#define IRQ_GPIO11		0x2b +#define IRQ_GPIO12		0x2c +#define IRQ_GPIO13		0x2d +#define IRQ_GPIO14		0x2e +#define IRQ_GPIO15		0x2f +#define IRQ_GPIO16		0x30 +#define IRQ_GPIO17		0x31 +#define IRQ_GPIO18		0x32 +#define IRQ_GPIO19		0x33 +#define IRQ_GPIO20		0x34 +#define IRQ_GPIO21		0x35 +#define IRQ_GPIO22		0x36 +#define IRQ_GPIO23		0x37 +#define IRQ_GPIO24		0x38 +#define IRQ_GPIO25		0x39 +#define IRQ_GPIO26		0x3a +#define IRQ_GPIO27		0x3b + +#ifdef CONFIG_ARCH_FPGA +#define IRQ_PCIINTA             IRQ_GPIOLOW2 +#define IRQ_PCIINTB             IRQ_GPIOLOW1 +#define IRQ_PCIINTC             IRQ_GPIOLOW0 +#define IRQ_PCIINTD             IRQ_GPIOLOW6 +#endif + +#if defined(CONFIG_PUV3_DB0913) || defined(CONFIG_PUV3_NB0916)	\ +	|| defined(CONFIG_PUV3_SMW0919) +#define IRQ_PCIINTA             IRQ_GPIOLOW1 +#define IRQ_PCIINTB             IRQ_GPIOLOW2 +#define IRQ_PCIINTC             IRQ_GPIOLOW3 +#define IRQ_PCIINTD             IRQ_GPIOLOW4 +#endif + +#define IRQ_SD_CD               IRQ_GPIO6 /* falling or rising trigger */ + +#ifndef __ASSEMBLY__ +struct pt_regs; + +extern void asm_do_IRQ(unsigned int, struct pt_regs *); + +#endif + +#endif + diff --git a/arch/unicore32/include/asm/irqflags.h b/arch/unicore32/include/asm/irqflags.h new file mode 100644 index 00000000000..6d8a28dfdba --- /dev/null +++ b/arch/unicore32/include/asm/irqflags.h @@ -0,0 +1,53 @@ +/* + * linux/arch/unicore32/include/asm/irqflags.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_IRQFLAGS_H__ +#define __UNICORE_IRQFLAGS_H__ + +#ifdef __KERNEL__ + +#include <asm/ptrace.h> + +#define ARCH_IRQ_DISABLED	(PRIV_MODE | PSR_I_BIT) +#define ARCH_IRQ_ENABLED	(PRIV_MODE) + +/* + * Save the current interrupt enable state. + */ +static inline unsigned long arch_local_save_flags(void) +{ +	unsigned long temp; + +	asm volatile("mov %0, asr" : "=r" (temp) : : "memory", "cc"); + +	return temp & PSR_c; +} + +/* + * restore saved IRQ state + */ +static inline void arch_local_irq_restore(unsigned long flags) +{ +	unsigned long temp; + +	asm volatile( +		"mov	%0, asr\n" +		"mov.a	asr, %1\n" +		"mov.f	asr, %0" +		: "=&r" (temp) +		: "r" (flags) +		: "memory", "cc"); +} + +#include <asm-generic/irqflags.h> + +#endif +#endif diff --git a/arch/unicore32/include/asm/linkage.h b/arch/unicore32/include/asm/linkage.h new file mode 100644 index 00000000000..d1618bd35b6 --- /dev/null +++ b/arch/unicore32/include/asm/linkage.h @@ -0,0 +1,22 @@ +/* + * linux/arch/unicore32/include/asm/linkage.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_LINKAGE_H__ +#define __UNICORE_LINKAGE_H__ + +#define __ALIGN .align 0 +#define __ALIGN_STR ".align 0" + +#define ENDPROC(name) \ +	.type name, %function; \ +	END(name) + +#endif diff --git a/arch/unicore32/include/asm/memblock.h b/arch/unicore32/include/asm/memblock.h new file mode 100644 index 00000000000..a8a5d8d0a26 --- /dev/null +++ b/arch/unicore32/include/asm/memblock.h @@ -0,0 +1,46 @@ +/* + * linux/arch/unicore32/include/asm/memblock.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __UNICORE_MEMBLOCK_H__ +#define __UNICORE_MEMBLOCK_H__ + +/* + * Memory map description + */ +# define NR_BANKS 8 + +struct membank { +	unsigned long start; +	unsigned long size; +	unsigned int highmem; +}; + +struct meminfo { +	int nr_banks; +	struct membank bank[NR_BANKS]; +}; + +extern struct meminfo meminfo; + +#define for_each_bank(iter, mi)				\ +	for (iter = 0; iter < (mi)->nr_banks; iter++) + +#define bank_pfn_start(bank)	__phys_to_pfn((bank)->start) +#define bank_pfn_end(bank)	__phys_to_pfn((bank)->start + (bank)->size) +#define bank_pfn_size(bank)	((bank)->size >> PAGE_SHIFT) +#define bank_phys_start(bank)	((bank)->start) +#define bank_phys_end(bank)	((bank)->start + (bank)->size) +#define bank_phys_size(bank)	((bank)->size) + +extern void uc32_memblock_init(struct meminfo *); + +#endif diff --git a/arch/unicore32/include/asm/memory.h b/arch/unicore32/include/asm/memory.h new file mode 100644 index 00000000000..debafc40200 --- /dev/null +++ b/arch/unicore32/include/asm/memory.h @@ -0,0 +1,117 @@ +/* + * linux/arch/unicore32/include/asm/memory.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * 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. + * + *  Note: this file should not be included by non-asm/.h files + */ +#ifndef __UNICORE_MEMORY_H__ +#define __UNICORE_MEMORY_H__ + +#include <linux/compiler.h> +#include <linux/const.h> +#include <asm/sizes.h> +#include <mach/memory.h> + +/* + * Allow for constants defined here to be used from assembly code + * by prepending the UL suffix only with actual C code compilation. + */ +#define UL(x) _AC(x, UL) + +/* + * PAGE_OFFSET - the virtual address of the start of the kernel image + * TASK_SIZE - the maximum size of a user space task. + * TASK_UNMAPPED_BASE - the lower boundary of the mmap VM area + */ +#define PAGE_OFFSET		UL(0xC0000000) +#define TASK_SIZE		(PAGE_OFFSET - UL(0x41000000)) +#define TASK_UNMAPPED_BASE	(PAGE_OFFSET / 3) + +/* + * The module space lives between the addresses given by TASK_SIZE + * and PAGE_OFFSET - it must be within 32MB of the kernel text. + */ +#define MODULES_VADDR		(PAGE_OFFSET - 16*1024*1024) +#if TASK_SIZE > MODULES_VADDR +#error Top of user space clashes with start of module space +#endif + +#define MODULES_END		(PAGE_OFFSET) + +/* + * Allow 16MB-aligned ioremap pages + */ +#define IOREMAP_MAX_ORDER	24 + +/* + * Physical vs virtual RAM address space conversion.  These are + * private definitions which should NOT be used outside memory.h + * files.  Use virt_to_phys/phys_to_virt/__pa/__va instead. + */ +#ifndef __virt_to_phys +#define __virt_to_phys(x)	((x) - PAGE_OFFSET + PHYS_OFFSET) +#define __phys_to_virt(x)	((x) - PHYS_OFFSET + PAGE_OFFSET) +#endif + +/* + * Convert a physical address to a Page Frame Number and back + */ +#define	__phys_to_pfn(paddr)	((paddr) >> PAGE_SHIFT) +#define	__pfn_to_phys(pfn)	((pfn) << PAGE_SHIFT) + +/* + * Convert a page to/from a physical address + */ +#define page_to_phys(page)	(__pfn_to_phys(page_to_pfn(page))) +#define phys_to_page(phys)	(pfn_to_page(__phys_to_pfn(phys))) + +#ifndef __ASSEMBLY__ + +#ifndef arch_adjust_zones +#define arch_adjust_zones(size, holes) do { } while (0) +#endif + +/* + * PFNs are used to describe any physical page; this means + * PFN 0 == physical address 0. + * + * This is the PFN of the first RAM page in the kernel + * direct-mapped view.  We assume this is the first page + * of RAM in the mem_map as well. + */ +#define PHYS_PFN_OFFSET	(PHYS_OFFSET >> PAGE_SHIFT) + +/* + * Drivers should NOT use these either. + */ +#define __pa(x)			__virt_to_phys((unsigned long)(x)) +#define __va(x)			((void *)__phys_to_virt((unsigned long)(x))) +#define pfn_to_kaddr(pfn)	__va((pfn) << PAGE_SHIFT) + +/* + * Conversion between a struct page and a physical address. + * + *  page_to_pfn(page)	convert a struct page * to a PFN number + *  pfn_to_page(pfn)	convert a _valid_ PFN number to struct page * + * + *  virt_to_page(k)	convert a _valid_ virtual address to struct page * + *  virt_addr_valid(k)	indicates whether a virtual address is valid + */ +#define ARCH_PFN_OFFSET		PHYS_PFN_OFFSET + +#define virt_to_page(kaddr)	pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) +#define virt_addr_valid(kaddr)	((unsigned long)(kaddr) >= PAGE_OFFSET && \ +		(unsigned long)(kaddr) < (unsigned long)high_memory) + +#endif + +#include <asm-generic/memory_model.h> + +#endif diff --git a/arch/unicore32/include/asm/mmu.h b/arch/unicore32/include/asm/mmu.h new file mode 100644 index 00000000000..66fa341dc2c --- /dev/null +++ b/arch/unicore32/include/asm/mmu.h @@ -0,0 +1,17 @@ +/* + * linux/arch/unicore32/include/asm/mmu.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_MMU_H__ +#define __UNICORE_MMU_H__ + +typedef	unsigned long mm_context_t; + +#endif diff --git a/arch/unicore32/include/asm/mmu_context.h b/arch/unicore32/include/asm/mmu_context.h new file mode 100644 index 00000000000..ef470a7a3d0 --- /dev/null +++ b/arch/unicore32/include/asm/mmu_context.h @@ -0,0 +1,89 @@ +/* + * linux/arch/unicore32/include/asm/mmu_context.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_MMU_CONTEXT_H__ +#define __UNICORE_MMU_CONTEXT_H__ + +#include <linux/compiler.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/vmacache.h> +#include <linux/io.h> + +#include <asm/cacheflush.h> +#include <asm/cpu-single.h> + +#define init_new_context(tsk, mm)	0 + +#define destroy_context(mm)		do { } while (0) + +/* + * This is called when "tsk" is about to enter lazy TLB mode. + * + * mm:  describes the currently active mm context + * tsk: task which is entering lazy tlb + * cpu: cpu number which is entering lazy tlb + * + * tsk->mm will be NULL + */ +static inline void +enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) +{ +} + +/* + * This is the actual mm switch as far as the scheduler + * is concerned.  No registers are touched.  We avoid + * calling the CPU specific function when the mm hasn't + * actually changed. + */ +static inline void +switch_mm(struct mm_struct *prev, struct mm_struct *next, +	  struct task_struct *tsk) +{ +	unsigned int cpu = smp_processor_id(); + +	if (!cpumask_test_and_set_cpu(cpu, mm_cpumask(next)) || prev != next) +		cpu_switch_mm(next->pgd, next); +} + +#define deactivate_mm(tsk, mm)	do { } while (0) +#define activate_mm(prev, next)	switch_mm(prev, next, NULL) + +/* + * We are inserting a "fake" vma for the user-accessible vector page so + * gdb and friends can get to it through ptrace and /proc/<pid>/mem. + * But we also want to remove it before the generic code gets to see it + * during process exit or the unmapping of it would  cause total havoc. + * (the macro is used as remove_vma() is static to mm/mmap.c) + */ +#define arch_exit_mmap(mm) \ +do { \ +	struct vm_area_struct *high_vma = find_vma(mm, 0xffff0000); \ +	if (high_vma) { \ +		BUG_ON(high_vma->vm_next);  /* it should be last */ \ +		if (high_vma->vm_prev) \ +			high_vma->vm_prev->vm_next = NULL; \ +		else \ +			mm->mmap = NULL; \ +		rb_erase(&high_vma->vm_rb, &mm->mm_rb); \ +		vmacache_invalidate(mm); \ +		mm->map_count--; \ +		remove_vma(high_vma); \ +	} \ +} while (0) + +static inline void arch_dup_mmap(struct mm_struct *oldmm, +				 struct mm_struct *mm) +{ +} + +#endif diff --git a/arch/unicore32/include/asm/mutex.h b/arch/unicore32/include/asm/mutex.h new file mode 100644 index 00000000000..fab7d0e8adf --- /dev/null +++ b/arch/unicore32/include/asm/mutex.h @@ -0,0 +1,20 @@ +/* + * linux/arch/unicore32/include/asm/mutex.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * 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. + * + * UniCore optimized mutex locking primitives + * + * Please look into asm-generic/mutex-xchg.h for a formal definition. + */ +#ifndef __UNICORE_MUTEX_H__ +#define __UNICORE_MUTEX_H__ + +# include <asm-generic/mutex-xchg.h> +#endif diff --git a/arch/unicore32/include/asm/page.h b/arch/unicore32/include/asm/page.h new file mode 100644 index 00000000000..594b3226250 --- /dev/null +++ b/arch/unicore32/include/asm/page.h @@ -0,0 +1,80 @@ +/* + * linux/arch/unicore32/include/asm/page.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_PAGE_H__ +#define __UNICORE_PAGE_H__ + +/* PAGE_SHIFT determines the page size */ +#define PAGE_SHIFT		12 +#define PAGE_SIZE		(_AC(1, UL) << PAGE_SHIFT) +#define PAGE_MASK		(~(PAGE_SIZE-1)) + +#ifndef __ASSEMBLY__ + +struct page; +struct vm_area_struct; + +#define clear_page(page)	memset((void *)(page), 0, PAGE_SIZE) +extern void copy_page(void *to, const void *from); + +#define clear_user_page(page, vaddr, pg)	clear_page(page) +#define copy_user_page(to, from, vaddr, pg)	copy_page(to, from) + +#undef STRICT_MM_TYPECHECKS + +#ifdef STRICT_MM_TYPECHECKS +/* + * These are used to make use of C type-checking.. + */ +typedef struct { unsigned long pte; } pte_t; +typedef struct { unsigned long pgd; } pgd_t; +typedef struct { unsigned long pgprot; } pgprot_t; + +#define pte_val(x)      ((x).pte) +#define pgd_val(x)	((x).pgd) +#define pgprot_val(x)   ((x).pgprot) + +#define __pte(x)        ((pte_t) { (x) }) +#define __pgd(x)	((pgd_t) { (x) }) +#define __pgprot(x)     ((pgprot_t) { (x) }) + +#else +/* + * .. while these make it easier on the compiler + */ +typedef unsigned long pte_t; +typedef unsigned long pgd_t; +typedef unsigned long pgprot_t; + +#define pte_val(x)      (x) +#define pgd_val(x)      (x) +#define pgprot_val(x)   (x) + +#define __pte(x)        (x) +#define __pgd(x)	(x) +#define __pgprot(x)     (x) + +#endif /* STRICT_MM_TYPECHECKS */ + +typedef struct page *pgtable_t; + +extern int pfn_valid(unsigned long); + +#include <asm/memory.h> + +#endif /* !__ASSEMBLY__ */ + +#define VM_DATA_DEFAULT_FLAGS \ +	(VM_READ | VM_WRITE | VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) + +#include <asm-generic/getorder.h> + +#endif diff --git a/arch/unicore32/include/asm/pci.h b/arch/unicore32/include/asm/pci.h new file mode 100644 index 00000000000..654407e9861 --- /dev/null +++ b/arch/unicore32/include/asm/pci.h @@ -0,0 +1,37 @@ +/* + * linux/arch/unicore32/include/asm/pci.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_PCI_H__ +#define __UNICORE_PCI_H__ + +#ifdef __KERNEL__ +#include <asm-generic/pci-dma-compat.h> +#include <asm-generic/pci-bridge.h> +#include <asm-generic/pci.h> +#include <mach/hardware.h> /* for PCIBIOS_MIN_* */ + +#ifdef CONFIG_PCI +static inline void pci_dma_burst_advice(struct pci_dev *pdev, +					enum pci_dma_burst_strategy *strat, +					unsigned long *strategy_parameter) +{ +	*strat = PCI_DMA_BURST_INFINITY; +	*strategy_parameter = ~0UL; +} +#endif + +#define HAVE_PCI_MMAP +extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, +	enum pci_mmap_state mmap_state, int write_combine); + +#endif /* __KERNEL__ */ + +#endif diff --git a/arch/unicore32/include/asm/pgalloc.h b/arch/unicore32/include/asm/pgalloc.h new file mode 100644 index 00000000000..2e02d1356fd --- /dev/null +++ b/arch/unicore32/include/asm/pgalloc.h @@ -0,0 +1,112 @@ +/* + * linux/arch/unicore32/include/asm/pgalloc.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_PGALLOC_H__ +#define __UNICORE_PGALLOC_H__ + +#include <asm/pgtable-hwdef.h> +#include <asm/processor.h> +#include <asm/cacheflush.h> +#include <asm/tlbflush.h> + +#define check_pgt_cache()		do { } while (0) + +#define _PAGE_USER_TABLE	(PMD_TYPE_TABLE | PMD_PRESENT) +#define _PAGE_KERNEL_TABLE	(PMD_TYPE_TABLE | PMD_PRESENT) + +extern pgd_t *get_pgd_slow(struct mm_struct *mm); +extern void free_pgd_slow(struct mm_struct *mm, pgd_t *pgd); + +#define pgd_alloc(mm)			get_pgd_slow(mm) +#define pgd_free(mm, pgd)		free_pgd_slow(mm, pgd) + +#define PGALLOC_GFP	(GFP_KERNEL | __GFP_NOTRACK | __GFP_REPEAT | __GFP_ZERO) + +/* + * Allocate one PTE table. + */ +static inline pte_t * +pte_alloc_one_kernel(struct mm_struct *mm, unsigned long addr) +{ +	pte_t *pte; + +	pte = (pte_t *)__get_free_page(PGALLOC_GFP); +	if (pte) +		clean_dcache_area(pte, PTRS_PER_PTE * sizeof(pte_t)); + +	return pte; +} + +static inline pgtable_t +pte_alloc_one(struct mm_struct *mm, unsigned long addr) +{ +	struct page *pte; + +	pte = alloc_pages(PGALLOC_GFP, 0); +	if (!pte) +		return NULL; +	if (!PageHighMem(pte)) { +		void *page = page_address(pte); +		clean_dcache_area(page, PTRS_PER_PTE * sizeof(pte_t)); +	} +	if (!pgtable_page_ctor(pte)) { +		__free_page(pte); +	} + +	return pte; +} + +/* + * Free one PTE table. + */ +static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte) +{ +	if (pte) +		free_page((unsigned long)pte); +} + +static inline void pte_free(struct mm_struct *mm, pgtable_t pte) +{ +	pgtable_page_dtor(pte); +	__free_page(pte); +} + +static inline void __pmd_populate(pmd_t *pmdp, unsigned long pmdval) +{ +	set_pmd(pmdp, __pmd(pmdval)); +	flush_pmd_entry(pmdp); +} + +/* + * Populate the pmdp entry with a pointer to the pte.  This pmd is part + * of the mm address space. + */ +static inline void +pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmdp, pte_t *ptep) +{ +	unsigned long pte_ptr = (unsigned long)ptep; + +	/* +	 * The pmd must be loaded with the physical +	 * address of the PTE table +	 */ +	__pmd_populate(pmdp, __pa(pte_ptr) | _PAGE_KERNEL_TABLE); +} + +static inline void +pmd_populate(struct mm_struct *mm, pmd_t *pmdp, pgtable_t ptep) +{ +	__pmd_populate(pmdp, +			page_to_pfn(ptep) << PAGE_SHIFT | _PAGE_USER_TABLE); +} +#define pmd_pgtable(pmd) pmd_page(pmd) + +#endif diff --git a/arch/unicore32/include/asm/pgtable-hwdef.h b/arch/unicore32/include/asm/pgtable-hwdef.h new file mode 100644 index 00000000000..7314e859cca --- /dev/null +++ b/arch/unicore32/include/asm/pgtable-hwdef.h @@ -0,0 +1,55 @@ +/* + * linux/arch/unicore32/include/asm/pgtable-hwdef.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_PGTABLE_HWDEF_H__ +#define __UNICORE_PGTABLE_HWDEF_H__ + +/* + * Hardware page table definitions. + * + * + Level 1 descriptor (PMD) + *   - common + */ +#define PMD_TYPE_MASK		(3 << 0) +#define PMD_TYPE_TABLE		(0 << 0) +/*#define PMD_TYPE_LARGE	(1 << 0) */ +#define PMD_TYPE_INVALID	(2 << 0) +#define PMD_TYPE_SECT		(3 << 0) + +#define PMD_PRESENT		(1 << 2) +#define PMD_YOUNG		(1 << 3) + +/*#define PMD_SECT_DIRTY	(1 << 4) */ +#define PMD_SECT_CACHEABLE	(1 << 5) +#define PMD_SECT_EXEC		(1 << 6) +#define PMD_SECT_WRITE		(1 << 7) +#define PMD_SECT_READ		(1 << 8) + +/* + * + Level 2 descriptor (PTE) + *   - common + */ +#define PTE_TYPE_MASK		(3 << 0) +#define PTE_TYPE_SMALL		(0 << 0) +#define PTE_TYPE_MIDDLE		(1 << 0) +#define PTE_TYPE_LARGE		(2 << 0) +#define PTE_TYPE_INVALID	(3 << 0) + +#define PTE_PRESENT		(1 << 2) +#define PTE_FILE		(1 << 3)	/* only when !PRESENT */ +#define PTE_YOUNG		(1 << 3) +#define PTE_DIRTY		(1 << 4) +#define PTE_CACHEABLE		(1 << 5) +#define PTE_EXEC		(1 << 6) +#define PTE_WRITE		(1 << 7) +#define PTE_READ		(1 << 8) + +#endif diff --git a/arch/unicore32/include/asm/pgtable.h b/arch/unicore32/include/asm/pgtable.h new file mode 100644 index 00000000000..ed6f7d000fb --- /dev/null +++ b/arch/unicore32/include/asm/pgtable.h @@ -0,0 +1,310 @@ +/* + * linux/arch/unicore32/include/asm/pgtable.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_PGTABLE_H__ +#define __UNICORE_PGTABLE_H__ + +#include <asm-generic/pgtable-nopmd.h> +#include <asm/cpu-single.h> + +#include <asm/memory.h> +#include <asm/pgtable-hwdef.h> + +/* + * Just any arbitrary offset to the start of the vmalloc VM area: the + * current 8MB value just means that there will be a 8MB "hole" after the + * physical memory until the kernel virtual memory starts.  That means that + * any out-of-bounds memory accesses will hopefully be caught. + * The vmalloc() routines leaves a hole of 4kB between each vmalloced + * area for the same reason. ;) + * + * Note that platforms may override VMALLOC_START, but they must provide + * VMALLOC_END.  VMALLOC_END defines the (exclusive) limit of this space, + * which may not overlap IO space. + */ +#ifndef VMALLOC_START +#define VMALLOC_OFFSET		SZ_8M +#define VMALLOC_START		(((unsigned long)high_memory + VMALLOC_OFFSET) \ +					& ~(VMALLOC_OFFSET-1)) +#define VMALLOC_END		(0xff000000UL) +#endif + +#define PTRS_PER_PTE		1024 +#define PTRS_PER_PGD		1024 + +/* + * PGDIR_SHIFT determines what a third-level page table entry can map + */ +#define PGDIR_SHIFT		22 + +#ifndef __ASSEMBLY__ +extern void __pte_error(const char *file, int line, unsigned long val); +extern void __pgd_error(const char *file, int line, unsigned long val); + +#define pte_ERROR(pte)		__pte_error(__FILE__, __LINE__, pte_val(pte)) +#define pgd_ERROR(pgd)		__pgd_error(__FILE__, __LINE__, pgd_val(pgd)) +#endif /* !__ASSEMBLY__ */ + +#define PGDIR_SIZE		(1UL << PGDIR_SHIFT) +#define PGDIR_MASK		(~(PGDIR_SIZE-1)) + +/* + * This is the lowest virtual address we can permit any user space + * mapping to be mapped at.  This is particularly important for + * non-high vector CPUs. + */ +#define FIRST_USER_ADDRESS	PAGE_SIZE + +#define FIRST_USER_PGD_NR	1 +#define USER_PTRS_PER_PGD	((TASK_SIZE/PGDIR_SIZE) - FIRST_USER_PGD_NR) + +/* + * section address mask and size definitions. + */ +#define SECTION_SHIFT		22 +#define SECTION_SIZE		(1UL << SECTION_SHIFT) +#define SECTION_MASK		(~(SECTION_SIZE-1)) + +#ifndef __ASSEMBLY__ + +/* + * The pgprot_* and protection_map entries will be fixed up in runtime + * to include the cachable bits based on memory policy, as well as any + * architecture dependent bits. + */ +#define _PTE_DEFAULT		(PTE_PRESENT | PTE_YOUNG | PTE_CACHEABLE) + +extern pgprot_t pgprot_user; +extern pgprot_t pgprot_kernel; + +#define PAGE_NONE		pgprot_user +#define PAGE_SHARED		__pgprot(pgprot_val(pgprot_user | PTE_READ \ +								| PTE_WRITE)) +#define PAGE_SHARED_EXEC	__pgprot(pgprot_val(pgprot_user | PTE_READ \ +								| PTE_WRITE \ +								| PTE_EXEC)) +#define PAGE_COPY		__pgprot(pgprot_val(pgprot_user | PTE_READ) +#define PAGE_COPY_EXEC		__pgprot(pgprot_val(pgprot_user | PTE_READ \ +								| PTE_EXEC)) +#define PAGE_READONLY		__pgprot(pgprot_val(pgprot_user | PTE_READ)) +#define PAGE_READONLY_EXEC	__pgprot(pgprot_val(pgprot_user | PTE_READ \ +								| PTE_EXEC)) +#define PAGE_KERNEL		pgprot_kernel +#define PAGE_KERNEL_EXEC	__pgprot(pgprot_val(pgprot_kernel | PTE_EXEC)) + +#define __PAGE_NONE		__pgprot(_PTE_DEFAULT) +#define __PAGE_SHARED		__pgprot(_PTE_DEFAULT | PTE_READ \ +							| PTE_WRITE) +#define __PAGE_SHARED_EXEC	__pgprot(_PTE_DEFAULT | PTE_READ \ +							| PTE_WRITE \ +							| PTE_EXEC) +#define __PAGE_COPY		__pgprot(_PTE_DEFAULT | PTE_READ) +#define __PAGE_COPY_EXEC	__pgprot(_PTE_DEFAULT | PTE_READ \ +							| PTE_EXEC) +#define __PAGE_READONLY		__pgprot(_PTE_DEFAULT | PTE_READ) +#define __PAGE_READONLY_EXEC	__pgprot(_PTE_DEFAULT | PTE_READ \ +							| PTE_EXEC) + +#endif /* __ASSEMBLY__ */ + +/* + * The table below defines the page protection levels that we insert into our + * Linux page table version.  These get translated into the best that the + * architecture can perform.  Note that on UniCore hardware: + *  1) We cannot do execute protection + *  2) If we could do execute protection, then read is implied + *  3) write implies read permissions + */ +#define __P000  __PAGE_NONE +#define __P001  __PAGE_READONLY +#define __P010  __PAGE_COPY +#define __P011  __PAGE_COPY +#define __P100  __PAGE_READONLY_EXEC +#define __P101  __PAGE_READONLY_EXEC +#define __P110  __PAGE_COPY_EXEC +#define __P111  __PAGE_COPY_EXEC + +#define __S000  __PAGE_NONE +#define __S001  __PAGE_READONLY +#define __S010  __PAGE_SHARED +#define __S011  __PAGE_SHARED +#define __S100  __PAGE_READONLY_EXEC +#define __S101  __PAGE_READONLY_EXEC +#define __S110  __PAGE_SHARED_EXEC +#define __S111  __PAGE_SHARED_EXEC + +#ifndef __ASSEMBLY__ +/* + * ZERO_PAGE is a global shared page that is always zero: used + * for zero-mapped memory areas etc.. + */ +extern struct page *empty_zero_page; +#define ZERO_PAGE(vaddr)		(empty_zero_page) + +#define pte_pfn(pte)			(pte_val(pte) >> PAGE_SHIFT) +#define pfn_pte(pfn, prot)		(__pte(((pfn) << PAGE_SHIFT) \ +						| pgprot_val(prot))) + +#define pte_none(pte)			(!pte_val(pte)) +#define pte_clear(mm, addr, ptep)	set_pte(ptep, __pte(0)) +#define pte_page(pte)			(pfn_to_page(pte_pfn(pte))) +#define pte_offset_kernel(dir, addr)	(pmd_page_vaddr(*(dir)) \ +						+ __pte_index(addr)) + +#define pte_offset_map(dir, addr)	(pmd_page_vaddr(*(dir)) \ +						+ __pte_index(addr)) +#define pte_unmap(pte)			do { } while (0) + +#define set_pte(ptep, pte)	cpu_set_pte(ptep, pte) + +#define set_pte_at(mm, addr, ptep, pteval)	\ +	do {					\ +		set_pte(ptep, pteval);          \ +	} while (0) + +/* + * The following only work if pte_present() is true. + * Undefined behaviour if not.. + */ +#define pte_present(pte)	(pte_val(pte) & PTE_PRESENT) +#define pte_write(pte)		(pte_val(pte) & PTE_WRITE) +#define pte_dirty(pte)		(pte_val(pte) & PTE_DIRTY) +#define pte_young(pte)		(pte_val(pte) & PTE_YOUNG) +#define pte_exec(pte)		(pte_val(pte) & PTE_EXEC) +#define pte_special(pte)	(0) + +#define PTE_BIT_FUNC(fn, op) \ +static inline pte_t pte_##fn(pte_t pte) { pte_val(pte) op; return pte; } + +PTE_BIT_FUNC(wrprotect, &= ~PTE_WRITE); +PTE_BIT_FUNC(mkwrite,   |= PTE_WRITE); +PTE_BIT_FUNC(mkclean,   &= ~PTE_DIRTY); +PTE_BIT_FUNC(mkdirty,   |= PTE_DIRTY); +PTE_BIT_FUNC(mkold,     &= ~PTE_YOUNG); +PTE_BIT_FUNC(mkyoung,   |= PTE_YOUNG); + +static inline pte_t pte_mkspecial(pte_t pte) { return pte; } + +/* + * Mark the prot value as uncacheable. + */ +#define pgprot_noncached(prot)		\ +	__pgprot(pgprot_val(prot) & ~PTE_CACHEABLE) +#define pgprot_writecombine(prot)	\ +	__pgprot(pgprot_val(prot) & ~PTE_CACHEABLE) +#define pgprot_dmacoherent(prot)	\ +	__pgprot(pgprot_val(prot) & ~PTE_CACHEABLE) + +#define pmd_none(pmd)		(!pmd_val(pmd)) +#define pmd_present(pmd)	(pmd_val(pmd) & PMD_PRESENT) +#define pmd_bad(pmd)		(((pmd_val(pmd) &		\ +				(PMD_PRESENT | PMD_TYPE_MASK))	\ +				!= (PMD_PRESENT | PMD_TYPE_TABLE))) + +#define set_pmd(pmdpd, pmdval)		\ +	do {				\ +		*(pmdpd) = pmdval;	\ +	} while (0) + +#define pmd_clear(pmdp)			\ +	do {				\ +		set_pmd(pmdp, __pmd(0));\ +		clean_pmd_entry(pmdp);	\ +	} while (0) + +#define pmd_page_vaddr(pmd) ((pte_t *)__va(pmd_val(pmd) & PAGE_MASK)) +#define pmd_page(pmd)		pfn_to_page(__phys_to_pfn(pmd_val(pmd))) + +/* + * Conversion functions: convert a page and protection to a page entry, + * and a page entry and page directory to the page they refer to. + */ +#define mk_pte(page, prot)	pfn_pte(page_to_pfn(page), prot) + +/* to find an entry in a page-table-directory */ +#define pgd_index(addr)		((addr) >> PGDIR_SHIFT) + +#define pgd_offset(mm, addr)	((mm)->pgd+pgd_index(addr)) + +/* to find an entry in a kernel page-table-directory */ +#define pgd_offset_k(addr)	pgd_offset(&init_mm, addr) + +/* Find an entry in the third-level page table.. */ +#define __pte_index(addr)	(((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) + +static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) +{ +	const unsigned long mask = PTE_EXEC | PTE_WRITE | PTE_READ; +	pte_val(pte) = (pte_val(pte) & ~mask) | (pgprot_val(newprot) & mask); +	return pte; +} + +extern pgd_t swapper_pg_dir[PTRS_PER_PGD]; + +/* + * Encode and decode a swap entry.  Swap entries are stored in the Linux + * page tables as follows: + * + *   3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 + *   1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + *   <--------------- offset --------------> <--- type --> 0 0 0 0 0 + * + * This gives us up to 127 swap files and 32GB per swap file.  Note that + * the offset field is always non-zero. + */ +#define __SWP_TYPE_SHIFT	5 +#define __SWP_TYPE_BITS		7 +#define __SWP_TYPE_MASK		((1 << __SWP_TYPE_BITS) - 1) +#define __SWP_OFFSET_SHIFT	(__SWP_TYPE_BITS + __SWP_TYPE_SHIFT) + +#define __swp_type(x)		(((x).val >> __SWP_TYPE_SHIFT)		\ +				& __SWP_TYPE_MASK) +#define __swp_offset(x)		((x).val >> __SWP_OFFSET_SHIFT) +#define __swp_entry(type, offset) ((swp_entry_t) {			\ +				((type) << __SWP_TYPE_SHIFT) |		\ +				((offset) << __SWP_OFFSET_SHIFT) }) + +#define __pte_to_swp_entry(pte)	((swp_entry_t) { pte_val(pte) }) +#define __swp_entry_to_pte(swp)	((pte_t) { (swp).val }) + +/* + * It is an error for the kernel to have more swap files than we can + * encode in the PTEs.  This ensures that we know when MAX_SWAPFILES + * is increased beyond what we presently support. + */ +#define MAX_SWAPFILES_CHECK()	\ +	BUILD_BUG_ON(MAX_SWAPFILES_SHIFT > __SWP_TYPE_BITS) + +/* + * Encode and decode a file entry.  File entries are stored in the Linux + * page tables as follows: + * + *   3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 + *   1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + *   <----------------------- offset ----------------------> 1 0 0 0 + */ +#define pte_file(pte)		(pte_val(pte) & PTE_FILE) +#define pte_to_pgoff(x)		(pte_val(x) >> 4) +#define pgoff_to_pte(x)		__pte(((x) << 4) | PTE_FILE) + +#define PTE_FILE_MAX_BITS	28 + +/* Needs to be defined here and not in linux/mm.h, as it is arch dependent */ +/* FIXME: this is not correct */ +#define kern_addr_valid(addr)	(1) + +#include <asm-generic/pgtable.h> + +#define pgtable_cache_init() do { } while (0) + +#endif /* !__ASSEMBLY__ */ + +#endif /* __UNICORE_PGTABLE_H__ */ diff --git a/arch/unicore32/include/asm/processor.h b/arch/unicore32/include/asm/processor.h new file mode 100644 index 00000000000..4eaa4216766 --- /dev/null +++ b/arch/unicore32/include/asm/processor.h @@ -0,0 +1,83 @@ +/* + * linux/arch/unicore32/include/asm/processor.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __UNICORE_PROCESSOR_H__ +#define __UNICORE_PROCESSOR_H__ + +/* + * Default implementation of macro that returns current + * instruction pointer ("program counter"). + */ +#define current_text_addr() ({ __label__ _l; _l: &&_l; }) + +#ifdef __KERNEL__ + +#include <asm/ptrace.h> +#include <asm/types.h> + +#ifdef __KERNEL__ +#define STACK_TOP	TASK_SIZE +#define STACK_TOP_MAX	TASK_SIZE +#endif + +struct debug_entry { +	u32			address; +	u32			insn; +}; + +struct debug_info { +	int			nsaved; +	struct debug_entry	bp[2]; +}; + +struct thread_struct { +							/* fault info	  */ +	unsigned long		address; +	unsigned long		trap_no; +	unsigned long		error_code; +							/* debugging	  */ +	struct debug_info	debug; +}; + +#define INIT_THREAD  {	} + +#define start_thread(regs, pc, sp)					\ +({									\ +	unsigned long *stack = (unsigned long *)sp;			\ +	memset(regs->uregs, 0, sizeof(regs->uregs));			\ +	regs->UCreg_asr = USER_MODE;					\ +	regs->UCreg_pc = pc & ~1;	/* pc */                        \ +	regs->UCreg_sp = sp;		/* sp */                        \ +	regs->UCreg_02 = stack[2];	/* r2 (envp) */                 \ +	regs->UCreg_01 = stack[1];	/* r1 (argv) */                 \ +	regs->UCreg_00 = stack[0];	/* r0 (argc) */                 \ +}) + +/* Forward declaration, a strange C thing */ +struct task_struct; + +/* Free all resources held by a thread. */ +extern void release_thread(struct task_struct *); + +unsigned long get_wchan(struct task_struct *p); + +#define cpu_relax()			barrier() + +#define task_pt_regs(p) \ +	((struct pt_regs *)(THREAD_START_SP + task_stack_page(p)) - 1) + +#define KSTK_EIP(tsk)	(task_pt_regs(tsk)->UCreg_pc) +#define KSTK_ESP(tsk)	(task_pt_regs(tsk)->UCreg_sp) + +#endif + +#endif /* __UNICORE_PROCESSOR_H__ */ diff --git a/arch/unicore32/include/asm/ptrace.h b/arch/unicore32/include/asm/ptrace.h new file mode 100644 index 00000000000..02bf5a415bf --- /dev/null +++ b/arch/unicore32/include/asm/ptrace.h @@ -0,0 +1,61 @@ +/* + * linux/arch/unicore32/include/asm/ptrace.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_PTRACE_H__ +#define __UNICORE_PTRACE_H__ + +#include <uapi/asm/ptrace.h> + +#ifndef __ASSEMBLY__ + +#define user_mode(regs)	\ +	(processor_mode(regs) == USER_MODE) + +#define processor_mode(regs) \ +	((regs)->UCreg_asr & MODE_MASK) + +#define interrupts_enabled(regs) \ +	(!((regs)->UCreg_asr & PSR_I_BIT)) + +#define fast_interrupts_enabled(regs) \ +	(!((regs)->UCreg_asr & PSR_R_BIT)) + +/* Are the current registers suitable for user mode? + * (used to maintain security in signal handlers) + */ +static inline int valid_user_regs(struct pt_regs *regs) +{ +	unsigned long mode = regs->UCreg_asr & MODE_MASK; + +	/* +	 * Always clear the R (REAL) bits +	 */ +	regs->UCreg_asr &= ~(PSR_R_BIT); + +	if ((regs->UCreg_asr & PSR_I_BIT) == 0) { +		if (mode == USER_MODE) +			return 1; +	} + +	/* +	 * Force ASR to something logical... +	 */ +	regs->UCreg_asr &= PSR_f | USER_MODE; + +	return 0; +} + +#define instruction_pointer(regs)	((regs)->UCreg_pc) +#define user_stack_pointer(regs)	((regs)->UCreg_sp) +#define profile_pc(regs)		instruction_pointer(regs) + +#endif /* __ASSEMBLY__ */ +#endif diff --git a/arch/unicore32/include/asm/stacktrace.h b/arch/unicore32/include/asm/stacktrace.h new file mode 100644 index 00000000000..76edc65a587 --- /dev/null +++ b/arch/unicore32/include/asm/stacktrace.h @@ -0,0 +1,31 @@ +/* + * linux/arch/unicore32/include/asm/stacktrace.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __UNICORE_STACKTRACE_H__ +#define __UNICORE_STACKTRACE_H__ + +struct stackframe { +	unsigned long fp; +	unsigned long sp; +	unsigned long lr; +	unsigned long pc; +}; + +#ifdef CONFIG_FRAME_POINTER +extern int unwind_frame(struct stackframe *frame); +#else +#define unwind_frame(f) (-EINVAL) +#endif +extern void walk_stackframe(struct stackframe *frame, +			    int (*fn)(struct stackframe *, void *), void *data); + +#endif	/* __UNICORE_STACKTRACE_H__ */ diff --git a/arch/unicore32/include/asm/string.h b/arch/unicore32/include/asm/string.h new file mode 100644 index 00000000000..55264c84369 --- /dev/null +++ b/arch/unicore32/include/asm/string.h @@ -0,0 +1,38 @@ +/* + * linux/arch/unicore32/include/asm/string.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_STRING_H__ +#define __UNICORE_STRING_H__ + +/* + * We don't do inline string functions, since the + * optimised inline asm versions are not small. + */ + +#define __HAVE_ARCH_STRRCHR +extern char *strrchr(const char *s, int c); + +#define __HAVE_ARCH_STRCHR +extern char *strchr(const char *s, int c); + +#define __HAVE_ARCH_MEMCPY +extern void *memcpy(void *, const void *, __kernel_size_t); + +#define __HAVE_ARCH_MEMMOVE +extern void *memmove(void *, const void *, __kernel_size_t); + +#define __HAVE_ARCH_MEMCHR +extern void *memchr(const void *, int, __kernel_size_t); + +#define __HAVE_ARCH_MEMSET +extern void *memset(void *, int, __kernel_size_t); + +#endif diff --git a/arch/unicore32/include/asm/suspend.h b/arch/unicore32/include/asm/suspend.h new file mode 100644 index 00000000000..65bad75c7e9 --- /dev/null +++ b/arch/unicore32/include/asm/suspend.h @@ -0,0 +1,29 @@ +/* + * linux/arch/unicore32/include/asm/suspend.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __UNICORE_SUSPEND_H__ +#define __UNICORE_SUSPEND_H__ + +#ifndef __ASSEMBLY__ + +#include <asm/ptrace.h> + +struct swsusp_arch_regs { +	struct cpu_context_save	cpu_context;	/* cpu context */ +#ifdef CONFIG_UNICORE_FPU_F64 +	struct fp_state		fpstate __attribute__((aligned(8))); +#endif +}; +#endif + +#endif /* __UNICORE_SUSPEND_H__ */ + diff --git a/arch/unicore32/include/asm/switch_to.h b/arch/unicore32/include/asm/switch_to.h new file mode 100644 index 00000000000..39572d2bd69 --- /dev/null +++ b/arch/unicore32/include/asm/switch_to.h @@ -0,0 +1,30 @@ +/* + * Task switching for PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2012 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_SWITCH_TO_H__ +#define __UNICORE_SWITCH_TO_H__ + +struct task_struct; +struct thread_info; + +/* + * switch_to(prev, next) should switch from task `prev' to `next' + * `prev' will never be the same as `next'.  schedule() itself + * contains the memory barrier to tell GCC not to cache `current'. + */ +extern struct task_struct *__switch_to(struct task_struct *, +		struct thread_info *, struct thread_info *); + +#define switch_to(prev, next, last)					\ +	do {								\ +		last = __switch_to(prev, task_thread_info(prev),	\ +					task_thread_info(next));	\ +	} while (0) + +#endif /* __UNICORE_SWITCH_TO_H__ */ diff --git a/arch/unicore32/include/asm/thread_info.h b/arch/unicore32/include/asm/thread_info.h new file mode 100644 index 00000000000..af36d8eabdf --- /dev/null +++ b/arch/unicore32/include/asm/thread_info.h @@ -0,0 +1,146 @@ +/* + * linux/arch/unicore32/include/asm/thread_info.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_THREAD_INFO_H__ +#define __UNICORE_THREAD_INFO_H__ + +#ifdef __KERNEL__ + +#include <linux/compiler.h> +#include <asm/fpstate.h> + +#define THREAD_SIZE_ORDER	1 +#define THREAD_SIZE		8192 +#define THREAD_START_SP		(THREAD_SIZE - 8) + +#ifndef __ASSEMBLY__ + +struct task_struct; +struct exec_domain; + +#include <asm/types.h> + +typedef struct { +	unsigned long seg; +} mm_segment_t; + +struct cpu_context_save { +	__u32	r4; +	__u32	r5; +	__u32	r6; +	__u32	r7; +	__u32	r8; +	__u32	r9; +	__u32	r10; +	__u32	r11; +	__u32	r12; +	__u32	r13; +	__u32	r14; +	__u32	r15; +	__u32	r16; +	__u32	r17; +	__u32	r18; +	__u32	r19; +	__u32	r20; +	__u32	r21; +	__u32	r22; +	__u32	r23; +	__u32	r24; +	__u32	r25; +	__u32	r26; +	__u32	fp; +	__u32	sp; +	__u32	pc; +}; + +/* + * low level task data that entry.S needs immediate access to. + * __switch_to() assumes cpu_context follows immediately after cpu_domain. + */ +struct thread_info { +	unsigned long		flags;		/* low level flags */ +	int			preempt_count;	/* 0 => preemptable */ +						/* <0 => bug */ +	mm_segment_t		addr_limit;	/* address limit */ +	struct task_struct	*task;		/* main task structure */ +	struct exec_domain	*exec_domain;	/* execution domain */ +	__u32			cpu;		/* cpu */ +	struct cpu_context_save	cpu_context;	/* cpu context */ +	__u32			syscall;	/* syscall number */ +	__u8			used_cp[16];	/* thread used copro */ +#ifdef CONFIG_UNICORE_FPU_F64 +	struct fp_state		fpstate __attribute__((aligned(8))); +#endif +	struct restart_block	restart_block; +}; + +#define INIT_THREAD_INFO(tsk)						\ +{									\ +	.task		= &tsk,						\ +	.exec_domain	= &default_exec_domain,				\ +	.flags		= 0,						\ +	.preempt_count	= INIT_PREEMPT_COUNT,				\ +	.addr_limit	= KERNEL_DS,					\ +	.restart_block	= {						\ +		.fn	= do_no_restart_syscall,			\ +	},								\ +} + +#define init_thread_info	(init_thread_union.thread_info) +#define init_stack		(init_thread_union.stack) + +/* + * how to get the thread information struct from C + */ +static inline struct thread_info *current_thread_info(void) __attribute_const__; + +static inline struct thread_info *current_thread_info(void) +{ +	register unsigned long sp asm ("sp"); +	return (struct thread_info *)(sp & ~(THREAD_SIZE - 1)); +} + +#define thread_saved_pc(tsk)	\ +	((unsigned long)(task_thread_info(tsk)->cpu_context.pc)) +#define thread_saved_sp(tsk)	\ +	((unsigned long)(task_thread_info(tsk)->cpu_context.sp)) +#define thread_saved_fp(tsk)	\ +	((unsigned long)(task_thread_info(tsk)->cpu_context.fp)) + +#endif + +/* + * thread information flags: + *  TIF_SYSCALL_TRACE	- syscall trace active + *  TIF_SIGPENDING	- signal pending + *  TIF_NEED_RESCHED	- rescheduling necessary + *  TIF_NOTIFY_RESUME	- callback before returning to user + */ +#define TIF_SIGPENDING		0 +#define TIF_NEED_RESCHED	1 +#define TIF_NOTIFY_RESUME	2	/* callback before returning to user */ +#define TIF_SYSCALL_TRACE	8 +#define TIF_MEMDIE		18 +#define TIF_RESTORE_SIGMASK	20 + +#define _TIF_SIGPENDING		(1 << TIF_SIGPENDING) +#define _TIF_NEED_RESCHED	(1 << TIF_NEED_RESCHED) +#define _TIF_NOTIFY_RESUME	(1 << TIF_NOTIFY_RESUME) +#define _TIF_SYSCALL_TRACE	(1 << TIF_SYSCALL_TRACE) + +/* + * Change these and you break ASM code in entry-common.S + */ +#define _TIF_WORK_MASK \ +	(_TIF_SIGPENDING | _TIF_NEED_RESCHED | _TIF_NOTIFY_RESUME) + +#endif /* __KERNEL__ */ +#endif /* __UNICORE_THREAD_INFO_H__ */ diff --git a/arch/unicore32/include/asm/timex.h b/arch/unicore32/include/asm/timex.h new file mode 100644 index 00000000000..faf16ba4654 --- /dev/null +++ b/arch/unicore32/include/asm/timex.h @@ -0,0 +1,34 @@ +/* + * linux/arch/unicore32/include/asm/timex.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __UNICORE_TIMEX_H__ +#define __UNICORE_TIMEX_H__ + +#ifdef	CONFIG_ARCH_FPGA + +/* in FPGA, APB clock is 33M, and OST clock is 32K, */ +/* so, 1M is selected for timer interrupt correctly */ +#define	CLOCK_TICK_RATE		(32*1024) + +#endif + +#if defined(CONFIG_PUV3_DB0913)		\ +	|| defined(CONFIG_PUV3_NB0916)	\ +	|| defined(CONFIG_PUV3_SMW0919) + +#define  CLOCK_TICK_RATE         (14318000) + +#endif + +#include <asm-generic/timex.h> + +#endif diff --git a/arch/unicore32/include/asm/tlb.h b/arch/unicore32/include/asm/tlb.h new file mode 100644 index 00000000000..9cca15cdae9 --- /dev/null +++ b/arch/unicore32/include/asm/tlb.h @@ -0,0 +1,28 @@ +/* + * linux/arch/unicore32/include/asm/tlb.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_TLB_H__ +#define __UNICORE_TLB_H__ + +#define tlb_start_vma(tlb, vma)				do { } while (0) +#define tlb_end_vma(tlb, vma)				do { } while (0) +#define __tlb_remove_tlb_entry(tlb, ptep, address)	do { } while (0) +#define tlb_flush(tlb) flush_tlb_mm((tlb)->mm) + +#define __pte_free_tlb(tlb, pte, addr)				\ +	do {							\ +		pgtable_page_dtor(pte);				\ +		tlb_remove_page((tlb), (pte));			\ +	} while (0) + +#include <asm-generic/tlb.h> + +#endif diff --git a/arch/unicore32/include/asm/tlbflush.h b/arch/unicore32/include/asm/tlbflush.h new file mode 100644 index 00000000000..e446ac8bb9e --- /dev/null +++ b/arch/unicore32/include/asm/tlbflush.h @@ -0,0 +1,195 @@ +/* + * linux/arch/unicore32/include/asm/tlbflush.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_TLBFLUSH_H__ +#define __UNICORE_TLBFLUSH_H__ + +#ifndef __ASSEMBLY__ + +#include <linux/sched.h> + +extern void __cpu_flush_user_tlb_range(unsigned long, unsigned long, +					struct vm_area_struct *); +extern void __cpu_flush_kern_tlb_range(unsigned long, unsigned long); + +/* + *	TLB Management + *	============== + * + *	The arch/unicore/mm/tlb-*.S files implement these methods. + * + *	The TLB specific code is expected to perform whatever tests it + *	needs to determine if it should invalidate the TLB for each + *	call.  Start addresses are inclusive and end addresses are + *	exclusive; it is safe to round these addresses down. + * + *	flush_tlb_all() + * + *		Invalidate the entire TLB. + * + *	flush_tlb_mm(mm) + * + *		Invalidate all TLB entries in a particular address + *		space. + *		- mm	- mm_struct describing address space + * + *	flush_tlb_range(mm,start,end) + * + *		Invalidate a range of TLB entries in the specified + *		address space. + *		- mm	- mm_struct describing address space + *		- start - start address (may not be aligned) + *		- end	- end address (exclusive, may not be aligned) + * + *	flush_tlb_page(vaddr,vma) + * + *		Invalidate the specified page in the specified address range. + *		- vaddr - virtual address (may not be aligned) + *		- vma	- vma_struct describing address range + * + *	flush_kern_tlb_page(kaddr) + * + *		Invalidate the TLB entry for the specified page.  The address + *		will be in the kernels virtual memory space.  Current uses + *		only require the D-TLB to be invalidated. + *		- kaddr - Kernel virtual memory address + */ + +static inline void local_flush_tlb_all(void) +{ +	const int zero = 0; + +	/* TLB invalidate all */ +	asm("movc p0.c6, %0, #6; nop; nop; nop; nop; nop; nop; nop; nop" +		: : "r" (zero) : "cc"); +} + +static inline void local_flush_tlb_mm(struct mm_struct *mm) +{ +	const int zero = 0; + +	if (cpumask_test_cpu(get_cpu(), mm_cpumask(mm))) { +		/* TLB invalidate all */ +		asm("movc p0.c6, %0, #6; nop; nop; nop; nop; nop; nop; nop; nop" +			: : "r" (zero) : "cc"); +	} +	put_cpu(); +} + +static inline void +local_flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr) +{ +	if (cpumask_test_cpu(smp_processor_id(), mm_cpumask(vma->vm_mm))) { +#ifndef CONFIG_CPU_TLB_SINGLE_ENTRY_DISABLE +		/* iTLB invalidate page */ +		asm("movc p0.c6, %0, #5; nop; nop; nop; nop; nop; nop; nop; nop" +			: : "r" (uaddr & PAGE_MASK) : "cc"); +		/* dTLB invalidate page */ +		asm("movc p0.c6, %0, #3; nop; nop; nop; nop; nop; nop; nop; nop" +			: : "r" (uaddr & PAGE_MASK) : "cc"); +#else +		/* TLB invalidate all */ +		asm("movc p0.c6, %0, #6; nop; nop; nop; nop; nop; nop; nop; nop" +			: : "r" (uaddr & PAGE_MASK) : "cc"); +#endif +	} +} + +static inline void local_flush_tlb_kernel_page(unsigned long kaddr) +{ +#ifndef CONFIG_CPU_TLB_SINGLE_ENTRY_DISABLE +	/* iTLB invalidate page */ +	asm("movc p0.c6, %0, #5; nop; nop; nop; nop; nop; nop; nop; nop" +		: : "r" (kaddr & PAGE_MASK) : "cc"); +	/* dTLB invalidate page */ +	asm("movc p0.c6, %0, #3; nop; nop; nop; nop; nop; nop; nop; nop" +		: : "r" (kaddr & PAGE_MASK) : "cc"); +#else +	/* TLB invalidate all */ +	asm("movc p0.c6, %0, #6; nop; nop; nop; nop; nop; nop; nop; nop" +		: : "r" (kaddr & PAGE_MASK) : "cc"); +#endif +} + +/* + *	flush_pmd_entry + * + *	Flush a PMD entry (word aligned, or double-word aligned) to + *	RAM if the TLB for the CPU we are running on requires this. + *	This is typically used when we are creating PMD entries. + * + *	clean_pmd_entry + * + *	Clean (but don't drain the write buffer) if the CPU requires + *	these operations.  This is typically used when we are removing + *	PMD entries. + */ +static inline void flush_pmd_entry(pmd_t *pmd) +{ +#ifndef CONFIG_CPU_DCACHE_LINE_DISABLE +	/* flush dcache line, see dcacheline_flush in proc-macros.S */ +	asm("mov	r1, %0 << #20\n" +		"ldw	r2, =_stext\n" +		"add	r2, r2, r1 >> #20\n" +		"ldw	r1, [r2+], #0x0000\n" +		"ldw	r1, [r2+], #0x1000\n" +		"ldw	r1, [r2+], #0x2000\n" +		"ldw	r1, [r2+], #0x3000\n" +		: : "r" (pmd) : "r1", "r2"); +#else +	/* flush dcache all */ +	asm("movc p0.c5, %0, #14; nop; nop; nop; nop; nop; nop; nop; nop" +		: : "r" (pmd) : "cc"); +#endif +} + +static inline void clean_pmd_entry(pmd_t *pmd) +{ +#ifndef CONFIG_CPU_DCACHE_LINE_DISABLE +	/* clean dcache line */ +	asm("movc p0.c5, %0, #11; nop; nop; nop; nop; nop; nop; nop; nop" +		: : "r" (__pa(pmd) & ~(L1_CACHE_BYTES - 1)) : "cc"); +#else +	/* clean dcache all */ +	asm("movc p0.c5, %0, #10; nop; nop; nop; nop; nop; nop; nop; nop" +		: : "r" (pmd) : "cc"); +#endif +} + +/* + * Convert calls to our calling convention. + */ +#define local_flush_tlb_range(vma, start, end)	\ +	__cpu_flush_user_tlb_range(start, end, vma) +#define local_flush_tlb_kernel_range(s, e)	\ +	__cpu_flush_kern_tlb_range(s, e) + +#define flush_tlb_all		local_flush_tlb_all +#define flush_tlb_mm		local_flush_tlb_mm +#define flush_tlb_page		local_flush_tlb_page +#define flush_tlb_kernel_page	local_flush_tlb_kernel_page +#define flush_tlb_range		local_flush_tlb_range +#define flush_tlb_kernel_range	local_flush_tlb_kernel_range + +/* + * if PG_dcache_clean is not set for the page, we need to ensure that any + * cache entries for the kernels virtual memory range are written + * back to the page. + */ +extern void update_mmu_cache(struct vm_area_struct *vma, +		unsigned long addr, pte_t *ptep); + +extern void do_bad_area(unsigned long addr, unsigned int fsr, +		struct pt_regs *regs); + +#endif + +#endif diff --git a/arch/unicore32/include/asm/traps.h b/arch/unicore32/include/asm/traps.h new file mode 100644 index 00000000000..66e17a724bf --- /dev/null +++ b/arch/unicore32/include/asm/traps.h @@ -0,0 +1,21 @@ +/* + * linux/arch/unicore32/include/asm/traps.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_TRAP_H__ +#define __UNICORE_TRAP_H__ + +extern void __init early_trap_init(void); +extern void dump_backtrace_entry(unsigned long where, +		unsigned long from, unsigned long frame); + +extern void do_DataAbort(unsigned long addr, unsigned int fsr, +		 struct pt_regs *regs); +#endif diff --git a/arch/unicore32/include/asm/uaccess.h b/arch/unicore32/include/asm/uaccess.h new file mode 100644 index 00000000000..897e11ad812 --- /dev/null +++ b/arch/unicore32/include/asm/uaccess.h @@ -0,0 +1,46 @@ +/* + * linux/arch/unicore32/include/asm/uaccess.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_UACCESS_H__ +#define __UNICORE_UACCESS_H__ + +#include <linux/thread_info.h> +#include <linux/errno.h> + +#include <asm/memory.h> + +#define __copy_from_user	__copy_from_user +#define __copy_to_user		__copy_to_user +#define __strncpy_from_user	__strncpy_from_user +#define __strnlen_user		__strnlen_user +#define __clear_user		__clear_user + +#define __kernel_ok		(segment_eq(get_fs(), KERNEL_DS)) +#define __user_ok(addr, size)	(((size) <= TASK_SIZE)			\ +				&& ((addr) <= TASK_SIZE - (size))) +#define __access_ok(addr, size)	(__kernel_ok || __user_ok((addr), (size))) + +extern unsigned long __must_check +__copy_from_user(void *to, const void __user *from, unsigned long n); +extern unsigned long __must_check +__copy_to_user(void __user *to, const void *from, unsigned long n); +extern unsigned long __must_check +__clear_user(void __user *addr, unsigned long n); +extern unsigned long __must_check +__strncpy_from_user(char *to, const char __user *from, unsigned long count); +extern unsigned long +__strnlen_user(const char __user *s, long n); + +#include <asm-generic/uaccess.h> + +extern int fixup_exception(struct pt_regs *regs); + +#endif /* __UNICORE_UACCESS_H__ */ diff --git a/arch/unicore32/include/mach/PKUnity.h b/arch/unicore32/include/mach/PKUnity.h new file mode 100644 index 00000000000..46705afcbf5 --- /dev/null +++ b/arch/unicore32/include/mach/PKUnity.h @@ -0,0 +1,98 @@ +/* + * linux/arch/unicore32/include/mach/PKUnity.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * 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. + */ + +/* Be sure that virtual mapping is defined right */ +#ifndef __MACH_PUV3_HARDWARE_H__ +#error You must include hardware.h not PKUnity.h +#endif + +#include <mach/bitfield.h> + +/* + * Memory Definitions + */ +#define PKUNITY_SDRAM_BASE		0x00000000 /* 0x00000000 - 0x7FFFFFFF 2GB */ +#define PKUNITY_MMIO_BASE		0x80000000 /* 0x80000000 - 0xFFFFFFFF 2GB */ + +/* + * PKUNITY System Bus Addresses (PCI): 0x80000000 - 0xBFFFFFFF (1GB) + * 0x80000000 - 0x8000000B 12B    PCI Configuration regs + * 0x80010000 - 0x80010250 592B   PCI Bridge Base + * 0x80030000 - 0x8003FFFF 64KB   PCI Legacy IO + * 0x90000000 - 0x97FFFFFF 128MB  PCI AHB-PCI MEM-mapping + * 0x98000000 - 0x9FFFFFFF 128MB  PCI PCI-AHB MEM-mapping + */ +#define PKUNITY_PCI_BASE		io_p2v(0x80000000) /* 0x80000000 - 0xBFFFFFFF 1GB */ +#include <mach/regs-pci.h> + +#define PKUNITY_PCICFG_BASE		(PKUNITY_PCI_BASE + 0x0) +#define PKUNITY_PCIBRI_BASE		(PKUNITY_PCI_BASE + 0x00010000) +#define PKUNITY_PCILIO_BASE		(PKUNITY_PCI_BASE + 0x00030000) +#define PKUNITY_PCIMEM_BASE		(PKUNITY_PCI_BASE + 0x10000000) +#define PKUNITY_PCIAHB_BASE		(PKUNITY_PCI_BASE + 0x18000000) + +/* + * PKUNITY System Bus Addresses (AHB): 0xC0000000 - 0xEDFFFFFF (640MB) + */ +#define PKUNITY_AHB_BASE		io_p2v(0xC0000000) + +/* AHB-0 is DDR2 SDRAM */ +/* AHB-1 is PCI Space */ +#define PKUNITY_ARBITER_BASE		(PKUNITY_AHB_BASE + 0x000000) /* AHB-2 */ +#define PKUNITY_DDR2CTRL_BASE		(PKUNITY_AHB_BASE + 0x100000) /* AHB-3 */ +#define PKUNITY_DMAC_BASE		(PKUNITY_AHB_BASE + 0x200000) /* AHB-4 */ +#include <mach/regs-dmac.h> +#define PKUNITY_UMAL_BASE		(PKUNITY_AHB_BASE + 0x300000) /* AHB-5 */ +#include <mach/regs-umal.h> +#define PKUNITY_USB_BASE		(PKUNITY_AHB_BASE + 0x400000) /* AHB-6 */ +#define PKUNITY_SATA_BASE		(PKUNITY_AHB_BASE + 0x500000) /* AHB-7 */ +#define PKUNITY_SMC_BASE		(PKUNITY_AHB_BASE + 0x600000) /* AHB-8 */ +/* AHB-9 is for APB bridge */ +#define PKUNITY_MME_BASE		(PKUNITY_AHB_BASE + 0x700000) /* AHB-10 */ +#define PKUNITY_UNIGFX_BASE		(PKUNITY_AHB_BASE + 0x800000) /* AHB-11 */ +#include <mach/regs-unigfx.h> +#define PKUNITY_NAND_BASE		(PKUNITY_AHB_BASE + 0x900000) /* AHB-12 */ +#include <mach/regs-nand.h> +#define PKUNITY_H264D_BASE		(PKUNITY_AHB_BASE + 0xA00000) /* AHB-13 */ +#define PKUNITY_H264E_BASE		(PKUNITY_AHB_BASE + 0xB00000) /* AHB-14 */ + +/* + * PKUNITY Peripheral Bus Addresses (APB): 0xEE000000 - 0xEFFFFFFF (128MB) + */ +#define PKUNITY_APB_BASE		io_p2v(0xEE000000) + +#define PKUNITY_UART0_BASE		(PKUNITY_APB_BASE + 0x000000) /* APB-0 */ +#define PKUNITY_UART1_BASE		(PKUNITY_APB_BASE + 0x100000) /* APB-1 */ +#include <mach/regs-uart.h> +#define PKUNITY_I2C_BASE		(PKUNITY_APB_BASE + 0x200000) /* APB-2 */ +#include <mach/regs-i2c.h> +#define PKUNITY_SPI_BASE		(PKUNITY_APB_BASE + 0x300000) /* APB-3 */ +#include <mach/regs-spi.h> +#define PKUNITY_AC97_BASE		(PKUNITY_APB_BASE + 0x400000) /* APB-4 */ +#include <mach/regs-ac97.h> +#define PKUNITY_GPIO_BASE		(PKUNITY_APB_BASE + 0x500000) /* APB-5 */ +#include <mach/regs-gpio.h> +#define PKUNITY_INTC_BASE		(PKUNITY_APB_BASE + 0x600000) /* APB-6 */ +#include <mach/regs-intc.h> +#define PKUNITY_RTC_BASE		(PKUNITY_APB_BASE + 0x700000) /* APB-7 */ +#include <mach/regs-rtc.h> +#define PKUNITY_OST_BASE		(PKUNITY_APB_BASE + 0x800000) /* APB-8 */ +#include <mach/regs-ost.h> +#define PKUNITY_RESETC_BASE		(PKUNITY_APB_BASE + 0x900000) /* APB-9 */ +#include <mach/regs-resetc.h> +#define PKUNITY_PM_BASE			(PKUNITY_APB_BASE + 0xA00000) /* APB-10 */ +#include <mach/regs-pm.h> +#define PKUNITY_PS2_BASE		(PKUNITY_APB_BASE + 0xB00000) /* APB-11 */ +#include <mach/regs-ps2.h> +#define PKUNITY_SDC_BASE		(PKUNITY_APB_BASE + 0xC00000) /* APB-12 */ +#include <mach/regs-sdc.h> + diff --git a/arch/unicore32/include/mach/bitfield.h b/arch/unicore32/include/mach/bitfield.h new file mode 100644 index 00000000000..128a70281ef --- /dev/null +++ b/arch/unicore32/include/mach/bitfield.h @@ -0,0 +1,24 @@ +/* + * linux/arch/unicore32/include/mach/bitfield.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __MACH_PUV3_BITFIELD_H__ +#define __MACH_PUV3_BITFIELD_H__ + +#ifndef __ASSEMBLY__ +#define UData(Data)	((unsigned long) (Data)) +#else +#define UData(Data)	(Data) +#endif + +#define FIELD(val, vmask, vshift)	(((val) & ((UData(1) << (vmask)) - 1)) << (vshift)) +#define FMASK(vmask, vshift)		(((UData(1) << (vmask)) - 1) << (vshift)) + +#endif /* __MACH_PUV3_BITFIELD_H__ */ diff --git a/arch/unicore32/include/mach/dma.h b/arch/unicore32/include/mach/dma.h new file mode 100644 index 00000000000..d655c1b6e08 --- /dev/null +++ b/arch/unicore32/include/mach/dma.h @@ -0,0 +1,48 @@ +/* + * linux/arch/unicore32/include/mach/dma.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __MACH_PUV3_DMA_H__ +#define __MACH_PUV3_DMA_H__ + +/* + * The PKUnity has six internal DMA channels. + */ +#define MAX_DMA_CHANNELS	6 + +typedef enum { +	DMA_PRIO_HIGH = 0, +	DMA_PRIO_MEDIUM = 1, +	DMA_PRIO_LOW = 2 +} puv3_dma_prio; + +/* + * DMA registration + */ + +extern int puv3_request_dma(char *name, +			 puv3_dma_prio prio, +			 void (*irq_handler)(int, void *), +			 void (*err_handler)(int, void *), +			 void *data); + +extern void puv3_free_dma(int dma_ch); + +static inline void puv3_stop_dma(int ch) +{ +	writel(readl(DMAC_CONFIG(ch)) & ~DMAC_CONFIG_EN, DMAC_CONFIG(ch)); +} + +static inline void puv3_resume_dma(int ch) +{ +	writel(readl(DMAC_CONFIG(ch)) | DMAC_CONFIG_EN, DMAC_CONFIG(ch)); +} + +#endif /* __MACH_PUV3_DMA_H__ */ diff --git a/arch/unicore32/include/mach/hardware.h b/arch/unicore32/include/mach/hardware.h new file mode 100644 index 00000000000..9e20b5d9ed5 --- /dev/null +++ b/arch/unicore32/include/mach/hardware.h @@ -0,0 +1,38 @@ +/* + * linux/arch/unicore32/include/mach/hardware.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This file contains the hardware definitions for PKUnity architecture + */ + +#ifndef __MACH_PUV3_HARDWARE_H__ +#define __MACH_PUV3_HARDWARE_H__ + +#include <mach/PKUnity.h> + +#ifndef __ASSEMBLY__ +#define io_p2v(x)	(void __iomem *)((x) - PKUNITY_MMIO_BASE) +#define io_v2p(x)	(phys_addr_t)((x) + PKUNITY_MMIO_BASE) +#else +#define io_p2v(x)	((x) - PKUNITY_MMIO_BASE) +#define io_v2p(x)	((x) + PKUNITY_MMIO_BASE) +#endif + +#define PCIBIOS_MIN_IO			0x4000 /* should lower than 64KB */ +#define PCIBIOS_MIN_MEM			io_v2p(PKUNITY_PCIMEM_BASE) + +/* + * We override the standard dma-mask routines for bouncing. + */ +#define	HAVE_ARCH_PCI_SET_DMA_MASK + +#define pcibios_assign_all_busses()	1 + +#endif  /* __MACH_PUV3_HARDWARE_H__ */ diff --git a/arch/unicore32/include/mach/map.h b/arch/unicore32/include/mach/map.h new file mode 100644 index 00000000000..55c93657374 --- /dev/null +++ b/arch/unicore32/include/mach/map.h @@ -0,0 +1,20 @@ +/* + * linux/arch/unicore32/include/mach/map.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * 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. + * + *  Page table mapping constructs and function prototypes + */ +#define MT_DEVICE		0 +#define MT_DEVICE_CACHED	2 +#define MT_KUSER		7 +#define MT_HIGH_VECTORS		8 +#define MT_MEMORY		9 +#define MT_ROM			10 + diff --git a/arch/unicore32/include/mach/memory.h b/arch/unicore32/include/mach/memory.h new file mode 100644 index 00000000000..4be72c21d49 --- /dev/null +++ b/arch/unicore32/include/mach/memory.h @@ -0,0 +1,57 @@ +/* + * linux/arch/unicore32/include/mach/memory.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __MACH_PUV3_MEMORY_H__ +#define __MACH_PUV3_MEMORY_H__ + +#include <mach/hardware.h> + +/* Physical DRAM offset. */ +#define PHYS_OFFSET	UL(0x00000000) +/* The base address of exception vectors. */ +#define VECTORS_BASE	UL(0xffff0000) +/* The base address of kuser area. */ +#define KUSER_BASE	UL(0x80000000) + +#ifdef __ASSEMBLY__ +/* The byte offset of the kernel image in RAM from the start of RAM. */ +#define KERNEL_IMAGE_START	0x00408000 +#endif + +#if !defined(__ASSEMBLY__) && defined(CONFIG_PCI) + +void puv3_pci_adjust_zones(unsigned long *size, unsigned long *holes); + +#define arch_adjust_zones(size, holes) \ +	puv3_pci_adjust_zones(size, holes) + +#endif + +/* + * PCI controller in PKUnity-3 masks highest 5-bit for upstream channel, + * so we must limit the DMA allocation within 128M physical memory for + * supporting PCI devices. + */ +#define PCI_DMA_THRESHOLD	(PHYS_OFFSET + SZ_128M - 1) + +#define is_pcibus_device(dev)	(dev &&			\ +				(strncmp(dev->bus->name, "pci", 3) == 0)) + +#define __virt_to_pcibus(x)     (__virt_to_phys((x) + PKUNITY_PCIAHB_BASE)) +#define __pcibus_to_virt(x)     (__phys_to_virt(x) - PKUNITY_PCIAHB_BASE) + +/* kuser area */ +#define KUSER_VECPAGE_BASE	(KUSER_BASE + UL(0x3fff0000)) +/* kuser_vecpage (0xbfff0000) is ro, and vectors page (0xffff0000) is rw */ +#define kuser_vecpage_to_vectors(x)	((x) - (KUSER_VECPAGE_BASE)	\ +					+ (VECTORS_BASE)) + +#endif diff --git a/arch/unicore32/include/mach/ocd.h b/arch/unicore32/include/mach/ocd.h new file mode 100644 index 00000000000..189fd71bfa3 --- /dev/null +++ b/arch/unicore32/include/mach/ocd.h @@ -0,0 +1,36 @@ +/* + * linux/arch/unicore32/include/mach/ocd.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __MACH_PUV3_OCD_H__ +#define __MACH_PUV3_OCD_H__ + +#if defined(CONFIG_DEBUG_OCD) +static inline void ocd_putc(unsigned int c) +{ +	int status, i = 0x2000000; + +	do { +		if (--i < 0) +			return; + +		asm volatile ("movc %0, p1.c0, #0" : "=r" (status)); +	} while (status & 2); + +	asm("movc p1.c1, %0, #1" : : "r" (c)); +} + +#define putc(ch)	ocd_putc(ch) +#else +#define putc(ch) +#endif + +#endif diff --git a/arch/unicore32/include/mach/pm.h b/arch/unicore32/include/mach/pm.h new file mode 100644 index 00000000000..4dcd34ae194 --- /dev/null +++ b/arch/unicore32/include/mach/pm.h @@ -0,0 +1,43 @@ +/* + * linux/arch/unicore/include/mach/pm.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __PUV3_PM_H__ +#define __PUV3_PM_H__ + +#include <linux/suspend.h> + +struct puv3_cpu_pm_fns { +	int	save_count; +	void	(*save)(unsigned long *); +	void	(*restore)(unsigned long *); +	int	(*valid)(suspend_state_t state); +	void	(*enter)(suspend_state_t state); +	int	(*prepare)(void); +	void	(*finish)(void); +}; + +extern struct puv3_cpu_pm_fns *puv3_cpu_pm_fns; + +/* sleep.S */ +extern void puv3_cpu_suspend(unsigned int); + +extern void puv3_cpu_resume(void); + +extern int puv3_pm_enter(suspend_state_t state); + +/* Defined in hibernate_asm.S */ +extern int restore_image(pgd_t *resume_pg_dir, struct pbe *restore_pblist); + +/* References to section boundaries */ +extern const void __nosave_begin, __nosave_end; + +extern struct pbe *restore_pblist; +#endif diff --git a/arch/unicore32/include/mach/regs-ac97.h b/arch/unicore32/include/mach/regs-ac97.h new file mode 100644 index 00000000000..b7563e9d650 --- /dev/null +++ b/arch/unicore32/include/mach/regs-ac97.h @@ -0,0 +1,32 @@ +/* + * PKUnity AC97 Registers + */ + +#define PKUNITY_AC97_CONR		(PKUNITY_AC97_BASE + 0x0000) +#define PKUNITY_AC97_OCR		(PKUNITY_AC97_BASE + 0x0004) +#define PKUNITY_AC97_ICR		(PKUNITY_AC97_BASE + 0x0008) +#define PKUNITY_AC97_CRAC		(PKUNITY_AC97_BASE + 0x000C) +#define PKUNITY_AC97_INTR		(PKUNITY_AC97_BASE + 0x0010) +#define PKUNITY_AC97_INTRSTAT		(PKUNITY_AC97_BASE + 0x0014) +#define PKUNITY_AC97_INTRCLEAR		(PKUNITY_AC97_BASE + 0x0018) +#define PKUNITY_AC97_ENABLE		(PKUNITY_AC97_BASE + 0x001C) +#define PKUNITY_AC97_OUT_FIFO		(PKUNITY_AC97_BASE + 0x0020) +#define PKUNITY_AC97_IN_FIFO		(PKUNITY_AC97_BASE + 0x0030) + +#define AC97_CODEC_REG(v)               FIELD((v), 7, 16) +#define AC97_CODEC_VAL(v)               FIELD((v), 16, 0) +#define AC97_CODEC_WRITECOMPLETE        FIELD(1, 1, 2) + +/* + * VAR PLAY SAMPLE RATE + */ +#define AC97_CMD_VPSAMPLE		(FIELD(3, 2, 16) | FIELD(3, 2, 0)) + +/* + * FIX CAPTURE SAMPLE RATE + */ +#define AC97_CMD_FCSAMPLE		FIELD(7, 3, 0) + +#define AC97_CMD_RESET			FIELD(1, 1, 0) +#define AC97_CMD_ENABLE			FIELD(1, 1, 0) +#define AC97_CMD_DISABLE		FIELD(0, 1, 0) diff --git a/arch/unicore32/include/mach/regs-dmac.h b/arch/unicore32/include/mach/regs-dmac.h new file mode 100644 index 00000000000..66de9e7d1c8 --- /dev/null +++ b/arch/unicore32/include/mach/regs-dmac.h @@ -0,0 +1,81 @@ +/* + * PKUnity Direct Memory Access Controller (DMAC) + */ + +/* + * Interrupt Status Reg DMAC_ISR. + */ +#define DMAC_ISR		(PKUNITY_DMAC_BASE + 0x0020) +/* + * Interrupt Transfer Complete Status Reg DMAC_ITCSR. + */ +#define DMAC_ITCSR		(PKUNITY_DMAC_BASE + 0x0050) +/* + * Interrupt Transfer Complete Clear Reg DMAC_ITCCR. + */ +#define DMAC_ITCCR		(PKUNITY_DMAC_BASE + 0x0060) +/* + * Interrupt Error Status Reg DMAC_IESR. + */ +#define DMAC_IESR		(PKUNITY_DMAC_BASE + 0x0080) +/* + * Interrupt Error Clear Reg DMAC_IECR. + */ +#define DMAC_IECR		(PKUNITY_DMAC_BASE + 0x0090) +/* + * Enable Channels Reg DMAC_ENCH. + */ +#define DMAC_ENCH		(PKUNITY_DMAC_BASE + 0x00B0) + +/* + * DMA control reg. Space [byte] + */ +#define DMASp                   0x00000100 + +/* + * Source Addr DMAC_SRCADDR(ch). + */ +#define DMAC_SRCADDR(ch)	(PKUNITY_DMAC_BASE + (ch)*DMASp + 0x00) +/* + * Destination Addr DMAC_DESTADDR(ch). + */ +#define DMAC_DESTADDR(ch)	(PKUNITY_DMAC_BASE + (ch)*DMASp + 0x04) +/* + * Control Reg DMAC_CONTROL(ch). + */ +#define DMAC_CONTROL(ch)	(PKUNITY_DMAC_BASE + (ch)*DMASp + 0x0C) +/* + * Configuration Reg DMAC_CONFIG(ch). + */ +#define DMAC_CONFIG(ch)		(PKUNITY_DMAC_BASE + (ch)*DMASp + 0x10) + +#define DMAC_IR_MASK            FMASK(6, 0) +/* + * select channel (ch) + */ +#define DMAC_CHANNEL(ch)	FIELD(1, 1, (ch)) + +#define DMAC_CONTROL_SIZE_BYTE(v)       (FIELD((v), 12, 14) | \ +					FIELD(0, 3, 9) | FIELD(0, 3, 6)) +#define DMAC_CONTROL_SIZE_HWORD(v)      (FIELD((v) >> 1, 12, 14) | \ +					FIELD(1, 3, 9) | FIELD(1, 3, 6)) +#define DMAC_CONTROL_SIZE_WORD(v)       (FIELD((v) >> 2, 12, 14) | \ +					FIELD(2, 3, 9) | FIELD(2, 3, 6)) +#define DMAC_CONTROL_DI                 FIELD(1, 1, 13) +#define DMAC_CONTROL_SI                 FIELD(1, 1, 12) +#define DMAC_CONTROL_BURST_1BYTE        (FIELD(0, 3, 3) | FIELD(0, 3, 0)) +#define DMAC_CONTROL_BURST_4BYTE        (FIELD(3, 3, 3) | FIELD(3, 3, 0)) +#define DMAC_CONTROL_BURST_8BYTE        (FIELD(5, 3, 3) | FIELD(5, 3, 0)) +#define DMAC_CONTROL_BURST_16BYTE       (FIELD(7, 3, 3) | FIELD(7, 3, 0)) + +#define	DMAC_CONFIG_UART0_WR    (FIELD(2, 4, 11) | FIELD(1, 2, 1)) +#define	DMAC_CONFIG_UART0_RD    (FIELD(2, 4, 7)  | FIELD(2, 2, 1)) +#define	DMAC_CONFIG_UART1_WR    (FIELD(3, 4, 11) | FIELD(1, 2, 1)) +#define	DMAC_CONFIG_UART1RD     (FIELD(3, 4, 7)  | FIELD(2, 2, 1)) +#define	DMAC_CONFIG_AC97WR      (FIELD(4, 4, 11) | FIELD(1, 2, 1)) +#define	DMAC_CONFIG_AC97RD      (FIELD(4, 4, 7)  | FIELD(2, 2, 1)) +#define	DMAC_CONFIG_MMCWR       (FIELD(7, 4, 11) | FIELD(1, 2, 1)) +#define	DMAC_CONFIG_MMCRD       (FIELD(7, 4, 7)  | FIELD(2, 2, 1)) +#define DMAC_CONFIG_MASKITC     FIELD(1, 1, 4) +#define DMAC_CONFIG_MASKIE      FIELD(1, 1, 3) +#define DMAC_CONFIG_EN          FIELD(1, 1, 0) diff --git a/arch/unicore32/include/mach/regs-gpio.h b/arch/unicore32/include/mach/regs-gpio.h new file mode 100644 index 00000000000..0273b861ef9 --- /dev/null +++ b/arch/unicore32/include/mach/regs-gpio.h @@ -0,0 +1,70 @@ +/* + * PKUnity General-Purpose Input/Output (GPIO) Registers + */ + +/* + * Voltage Status Reg GPIO_GPLR. + */ +#define GPIO_GPLR	(PKUNITY_GPIO_BASE + 0x0000) +/* + * Pin Direction Reg GPIO_GPDR. + */ +#define GPIO_GPDR	(PKUNITY_GPIO_BASE + 0x0004) +/* + * Output Pin Set Reg GPIO_GPSR. + */ +#define GPIO_GPSR	(PKUNITY_GPIO_BASE + 0x0008) +/* + * Output Pin Clear Reg GPIO_GPCR. + */ +#define GPIO_GPCR	(PKUNITY_GPIO_BASE + 0x000C) +/* + * Raise Edge Detect Reg GPIO_GRER. + */ +#define GPIO_GRER	(PKUNITY_GPIO_BASE + 0x0010) +/* + * Fall Edge Detect Reg GPIO_GFER. + */ +#define GPIO_GFER	(PKUNITY_GPIO_BASE + 0x0014) +/* + * Edge Status Reg GPIO_GEDR. + */ +#define GPIO_GEDR	(PKUNITY_GPIO_BASE + 0x0018) +/* + * Sepcial Voltage Detect Reg GPIO_GPIR. + */ +#define GPIO_GPIR	(PKUNITY_GPIO_BASE + 0x0020) + +#define GPIO_MIN	(0) +#define GPIO_MAX	(27) + +#define GPIO_GPIO(Nb)	(0x00000001 << (Nb))	/* GPIO [0..27] */ +#define GPIO_GPIO0	GPIO_GPIO(0)	/* GPIO  [0] */ +#define GPIO_GPIO1	GPIO_GPIO(1)	/* GPIO  [1] */ +#define GPIO_GPIO2	GPIO_GPIO(2)	/* GPIO  [2] */ +#define GPIO_GPIO3	GPIO_GPIO(3)	/* GPIO  [3] */ +#define GPIO_GPIO4	GPIO_GPIO(4)	/* GPIO  [4] */ +#define GPIO_GPIO5	GPIO_GPIO(5)	/* GPIO  [5] */ +#define GPIO_GPIO6	GPIO_GPIO(6)	/* GPIO  [6] */ +#define GPIO_GPIO7	GPIO_GPIO(7)	/* GPIO  [7] */ +#define GPIO_GPIO8	GPIO_GPIO(8)	/* GPIO  [8] */ +#define GPIO_GPIO9	GPIO_GPIO(9)	/* GPIO  [9] */ +#define GPIO_GPIO10	GPIO_GPIO(10)	/* GPIO [10] */ +#define GPIO_GPIO11	GPIO_GPIO(11)	/* GPIO [11] */ +#define GPIO_GPIO12	GPIO_GPIO(12)	/* GPIO [12] */ +#define GPIO_GPIO13	GPIO_GPIO(13)	/* GPIO [13] */ +#define GPIO_GPIO14	GPIO_GPIO(14)	/* GPIO [14] */ +#define GPIO_GPIO15	GPIO_GPIO(15)	/* GPIO [15] */ +#define GPIO_GPIO16	GPIO_GPIO(16)	/* GPIO [16] */ +#define GPIO_GPIO17	GPIO_GPIO(17)	/* GPIO [17] */ +#define GPIO_GPIO18	GPIO_GPIO(18)	/* GPIO [18] */ +#define GPIO_GPIO19	GPIO_GPIO(19)	/* GPIO [19] */ +#define GPIO_GPIO20	GPIO_GPIO(20)	/* GPIO [20] */ +#define GPIO_GPIO21	GPIO_GPIO(21)	/* GPIO [21] */ +#define GPIO_GPIO22	GPIO_GPIO(22)	/* GPIO [22] */ +#define GPIO_GPIO23	GPIO_GPIO(23)	/* GPIO [23] */ +#define GPIO_GPIO24	GPIO_GPIO(24)	/* GPIO [24] */ +#define GPIO_GPIO25	GPIO_GPIO(25)	/* GPIO [25] */ +#define GPIO_GPIO26	GPIO_GPIO(26)	/* GPIO [26] */ +#define GPIO_GPIO27	GPIO_GPIO(27)	/* GPIO [27] */ + diff --git a/arch/unicore32/include/mach/regs-i2c.h b/arch/unicore32/include/mach/regs-i2c.h new file mode 100644 index 00000000000..463d108f8bf --- /dev/null +++ b/arch/unicore32/include/mach/regs-i2c.h @@ -0,0 +1,63 @@ +/* + * PKUnity Inter-integrated Circuit (I2C) Registers + */ + +/* + * Control Reg I2C_CON. + */ +#define I2C_CON		(PKUNITY_I2C_BASE + 0x0000) +/* + * Target Address Reg I2C_TAR. + */ +#define I2C_TAR		(PKUNITY_I2C_BASE + 0x0004) +/* + * Data buffer and command Reg I2C_DATACMD. + */ +#define I2C_DATACMD	(PKUNITY_I2C_BASE + 0x0010) +/* + * Enable Reg I2C_ENABLE. + */ +#define I2C_ENABLE	(PKUNITY_I2C_BASE + 0x006C) +/* + * Status Reg I2C_STATUS. + */ +#define I2C_STATUS	(PKUNITY_I2C_BASE + 0x0070) +/* + * Tx FIFO Length Reg I2C_TXFLR. + */ +#define I2C_TXFLR	(PKUNITY_I2C_BASE + 0x0074) +/* + * Rx FIFO Length Reg I2C_RXFLR. + */ +#define I2C_RXFLR	(PKUNITY_I2C_BASE + 0x0078) +/* + * Enable Status Reg I2C_ENSTATUS. + */ +#define I2C_ENSTATUS	(PKUNITY_I2C_BASE + 0x009C) + +#define I2C_CON_MASTER          FIELD(1, 1, 0) +#define I2C_CON_SPEED_STD       FIELD(1, 2, 1) +#define I2C_CON_SPEED_FAST      FIELD(2, 2, 1) +#define I2C_CON_RESTART         FIELD(1, 1, 5) +#define I2C_CON_SLAVEDISABLE    FIELD(1, 1, 6) + +#define I2C_DATACMD_READ        FIELD(1, 1, 8) +#define I2C_DATACMD_WRITE       FIELD(0, 1, 8) +#define I2C_DATACMD_DAT_MASK    FMASK(8, 0) +#define I2C_DATACMD_DAT(v)      FIELD((v), 8, 0) + +#define I2C_ENABLE_ENABLE       FIELD(1, 1, 0) +#define I2C_ENABLE_DISABLE      FIELD(0, 1, 0) + +#define I2C_STATUS_RFF          FIELD(1, 1, 4) +#define I2C_STATUS_RFNE         FIELD(1, 1, 3) +#define I2C_STATUS_TFE          FIELD(1, 1, 2) +#define I2C_STATUS_TFNF         FIELD(1, 1, 1) +#define I2C_STATUS_ACTIVITY     FIELD(1, 1, 0) + +#define I2C_ENSTATUS_ENABLE	FIELD(1, 1, 0) + +#define I2C_TAR_THERMAL	0x4f +#define I2C_TAR_SPD	0x50 +#define I2C_TAR_PWIC    0x55 +#define I2C_TAR_EEPROM	0x57 diff --git a/arch/unicore32/include/mach/regs-intc.h b/arch/unicore32/include/mach/regs-intc.h new file mode 100644 index 00000000000..25648f89cbd --- /dev/null +++ b/arch/unicore32/include/mach/regs-intc.h @@ -0,0 +1,28 @@ +/* + * PKUNITY Interrupt Controller (INTC) Registers + */ +/* + * INTC Level Reg INTC_ICLR. + */ +#define INTC_ICLR	(PKUNITY_INTC_BASE + 0x0000) +/* + * INTC Mask Reg INTC_ICMR. + */ +#define INTC_ICMR	(PKUNITY_INTC_BASE + 0x0004) +/* + * INTC Pending Reg INTC_ICPR. + */ +#define INTC_ICPR	(PKUNITY_INTC_BASE + 0x0008) +/* + * INTC IRQ Pending Reg INTC_ICIP. + */ +#define INTC_ICIP	(PKUNITY_INTC_BASE + 0x000C) +/* + * INTC REAL Pending Reg INTC_ICFP. + */ +#define INTC_ICFP	(PKUNITY_INTC_BASE + 0x0010) +/* + * INTC Control Reg INTC_ICCR. + */ +#define INTC_ICCR	(PKUNITY_INTC_BASE + 0x0014) + diff --git a/arch/unicore32/include/mach/regs-nand.h b/arch/unicore32/include/mach/regs-nand.h new file mode 100644 index 00000000000..a7c5563bb55 --- /dev/null +++ b/arch/unicore32/include/mach/regs-nand.h @@ -0,0 +1,79 @@ +/* + * PKUnity NAND Controller Registers + */ +/* + * ID Reg. 0 NAND_IDR0 + */ +#define NAND_IDR0	(PKUNITY_NAND_BASE + 0x0000) +/* + * ID Reg. 1 NAND_IDR1 + */ +#define NAND_IDR1	(PKUNITY_NAND_BASE + 0x0004) +/* + * ID Reg. 2 NAND_IDR2 + */ +#define NAND_IDR2	(PKUNITY_NAND_BASE + 0x0008) +/* + * ID Reg. 3 NAND_IDR3 + */ +#define NAND_IDR3	(PKUNITY_NAND_BASE + 0x000C) +/* + * Page Address Reg 0 NAND_PAR0 + */ +#define NAND_PAR0	(PKUNITY_NAND_BASE + 0x0010) +/* + * Page Address Reg 1 NAND_PAR1 + */ +#define NAND_PAR1	(PKUNITY_NAND_BASE + 0x0014) +/* + * Page Address Reg 2 NAND_PAR2 + */ +#define NAND_PAR2	(PKUNITY_NAND_BASE + 0x0018) +/* + * ECC Enable Reg NAND_ECCEN + */ +#define NAND_ECCEN	(PKUNITY_NAND_BASE + 0x001C) +/* + * Buffer Reg NAND_BUF + */ +#define NAND_BUF	(PKUNITY_NAND_BASE + 0x0020) +/* + * ECC Status Reg NAND_ECCSR + */ +#define NAND_ECCSR	(PKUNITY_NAND_BASE + 0x0024) +/* + * Command Reg NAND_CMD + */ +#define NAND_CMD	(PKUNITY_NAND_BASE + 0x0028) +/* + * DMA Configure Reg NAND_DMACR + */ +#define NAND_DMACR	(PKUNITY_NAND_BASE + 0x002C) +/* + * Interrupt Reg NAND_IR + */ +#define NAND_IR		(PKUNITY_NAND_BASE + 0x0030) +/* + * Interrupt Mask Reg NAND_IMR + */ +#define NAND_IMR	(PKUNITY_NAND_BASE + 0x0034) +/* + * Chip Enable Reg NAND_CHIPEN + */ +#define NAND_CHIPEN	(PKUNITY_NAND_BASE + 0x0038) +/* + * Address Reg NAND_ADDR + */ +#define NAND_ADDR	(PKUNITY_NAND_BASE + 0x003C) + +/* + * Command bits NAND_CMD_CMD_MASK + */ +#define NAND_CMD_CMD_MASK		FMASK(4, 4) +#define NAND_CMD_CMD_READPAGE		FIELD(0x0, 4, 4) +#define NAND_CMD_CMD_ERASEBLOCK		FIELD(0x6, 4, 4) +#define NAND_CMD_CMD_READSTATUS		FIELD(0x7, 4, 4) +#define NAND_CMD_CMD_WRITEPAGE		FIELD(0x8, 4, 4) +#define NAND_CMD_CMD_READID		FIELD(0x9, 4, 4) +#define NAND_CMD_CMD_RESET		FIELD(0xf, 4, 4) + diff --git a/arch/unicore32/include/mach/regs-ost.h b/arch/unicore32/include/mach/regs-ost.h new file mode 100644 index 00000000000..4a85fb46384 --- /dev/null +++ b/arch/unicore32/include/mach/regs-ost.h @@ -0,0 +1,90 @@ +/* + * PKUnity Operating System Timer (OST) Registers + */ +/* + * Match Reg 0 OST_OSMR0 + */ +#define OST_OSMR0	(PKUNITY_OST_BASE + 0x0000) +/* + * Match Reg 1 OST_OSMR1 + */ +#define OST_OSMR1	(PKUNITY_OST_BASE + 0x0004) +/* + * Match Reg 2 OST_OSMR2 + */ +#define OST_OSMR2	(PKUNITY_OST_BASE + 0x0008) +/* + * Match Reg 3 OST_OSMR3 + */ +#define OST_OSMR3	(PKUNITY_OST_BASE + 0x000C) +/* + * Counter Reg OST_OSCR + */ +#define OST_OSCR	(PKUNITY_OST_BASE + 0x0010) +/* + * Status Reg OST_OSSR + */ +#define OST_OSSR	(PKUNITY_OST_BASE + 0x0014) +/* + * Watchdog Enable Reg OST_OWER + */ +#define OST_OWER	(PKUNITY_OST_BASE + 0x0018) +/* + * Interrupt Enable Reg OST_OIER + */ +#define OST_OIER	(PKUNITY_OST_BASE + 0x001C) + +/* + * PWM Registers: IO base address: PKUNITY_OST_BASE + 0x80 + *      PWCR: Pulse Width Control Reg + *      DCCR: Duty Cycle Control Reg + *      PCR: Period Control Reg + */ +#define OST_PWM_PWCR	(0x00) +#define OST_PWM_DCCR	(0x04) +#define OST_PWM_PCR 	(0x08) + +/* + * Match detected 0 OST_OSSR_M0 + */ +#define OST_OSSR_M0		FIELD(1, 1, 0) +/* + * Match detected 1 OST_OSSR_M1 + */ +#define OST_OSSR_M1		FIELD(1, 1, 1) +/* + * Match detected 2 OST_OSSR_M2 + */ +#define OST_OSSR_M2		FIELD(1, 1, 2) +/* + * Match detected 3 OST_OSSR_M3 + */ +#define OST_OSSR_M3		FIELD(1, 1, 3) + +/* + * Interrupt enable 0 OST_OIER_E0 + */ +#define OST_OIER_E0		FIELD(1, 1, 0) +/* + * Interrupt enable 1 OST_OIER_E1 + */ +#define OST_OIER_E1		FIELD(1, 1, 1) +/* + * Interrupt enable 2 OST_OIER_E2 + */ +#define OST_OIER_E2		FIELD(1, 1, 2) +/* + * Interrupt enable 3 OST_OIER_E3 + */ +#define OST_OIER_E3		FIELD(1, 1, 3) + +/* + * Watchdog Match Enable OST_OWER_WME + */ +#define OST_OWER_WME		FIELD(1, 1, 0) + +/* + * PWM Full Duty Cycle OST_PWMDCCR_FDCYCLE + */ +#define OST_PWMDCCR_FDCYCLE	FIELD(1, 1, 10) + diff --git a/arch/unicore32/include/mach/regs-pci.h b/arch/unicore32/include/mach/regs-pci.h new file mode 100644 index 00000000000..6a9341686bf --- /dev/null +++ b/arch/unicore32/include/mach/regs-pci.h @@ -0,0 +1,94 @@ +/* + * PKUnity AHB-PCI Bridge Registers + */ + +/* + * AHB/PCI fixed physical address for pci addess configuration + */ +/* + * PCICFG Bridge Base Reg. + */ +#define PCICFG_BRIBASE          (PKUNITY_PCICFG_BASE + 0x0000) +/* + * PCICFG Address Reg. + */ +#define PCICFG_ADDR             (PKUNITY_PCICFG_BASE + 0x0004) +/* + * PCICFG Address Reg. + */ +#define PCICFG_DATA             (PKUNITY_PCICFG_BASE + 0x0008) + +/* + * PCI Bridge configuration space + */ +#define PCIBRI_ID		(PKUNITY_PCIBRI_BASE + 0x0000) +#define PCIBRI_CMD		(PKUNITY_PCIBRI_BASE + 0x0004) +#define PCIBRI_CLASS		(PKUNITY_PCIBRI_BASE + 0x0008) +#define PCIBRI_LTR		(PKUNITY_PCIBRI_BASE + 0x000C) +#define PCIBRI_BAR0		(PKUNITY_PCIBRI_BASE + 0x0010) +#define PCIBRI_BAR1		(PKUNITY_PCIBRI_BASE + 0x0014) +#define PCIBRI_BAR2		(PKUNITY_PCIBRI_BASE + 0x0018) +#define PCIBRI_BAR3		(PKUNITY_PCIBRI_BASE + 0x001C) +#define PCIBRI_BAR4		(PKUNITY_PCIBRI_BASE + 0x0020) +#define PCIBRI_BAR5		(PKUNITY_PCIBRI_BASE + 0x0024) + +#define PCIBRI_PCICTL0		(PKUNITY_PCIBRI_BASE + 0x0100) +#define PCIBRI_PCIBAR0		(PKUNITY_PCIBRI_BASE + 0x0104) +#define PCIBRI_PCIAMR0		(PKUNITY_PCIBRI_BASE + 0x0108) +#define PCIBRI_PCITAR0		(PKUNITY_PCIBRI_BASE + 0x010C) +#define PCIBRI_PCICTL1		(PKUNITY_PCIBRI_BASE + 0x0110) +#define PCIBRI_PCIBAR1		(PKUNITY_PCIBRI_BASE + 0x0114) +#define PCIBRI_PCIAMR1		(PKUNITY_PCIBRI_BASE + 0x0118) +#define PCIBRI_PCITAR1		(PKUNITY_PCIBRI_BASE + 0x011C) +#define PCIBRI_PCICTL2		(PKUNITY_PCIBRI_BASE + 0x0120) +#define PCIBRI_PCIBAR2		(PKUNITY_PCIBRI_BASE + 0x0124) +#define PCIBRI_PCIAMR2		(PKUNITY_PCIBRI_BASE + 0x0128) +#define PCIBRI_PCITAR2		(PKUNITY_PCIBRI_BASE + 0x012C) +#define PCIBRI_PCICTL3		(PKUNITY_PCIBRI_BASE + 0x0130) +#define PCIBRI_PCIBAR3		(PKUNITY_PCIBRI_BASE + 0x0134) +#define PCIBRI_PCIAMR3		(PKUNITY_PCIBRI_BASE + 0x0138) +#define PCIBRI_PCITAR3		(PKUNITY_PCIBRI_BASE + 0x013C) +#define PCIBRI_PCICTL4		(PKUNITY_PCIBRI_BASE + 0x0140) +#define PCIBRI_PCIBAR4		(PKUNITY_PCIBRI_BASE + 0x0144) +#define PCIBRI_PCIAMR4		(PKUNITY_PCIBRI_BASE + 0x0148) +#define PCIBRI_PCITAR4		(PKUNITY_PCIBRI_BASE + 0x014C) +#define PCIBRI_PCICTL5		(PKUNITY_PCIBRI_BASE + 0x0150) +#define PCIBRI_PCIBAR5		(PKUNITY_PCIBRI_BASE + 0x0154) +#define PCIBRI_PCIAMR5		(PKUNITY_PCIBRI_BASE + 0x0158) +#define PCIBRI_PCITAR5		(PKUNITY_PCIBRI_BASE + 0x015C) + +#define PCIBRI_AHBCTL0		(PKUNITY_PCIBRI_BASE + 0x0180) +#define PCIBRI_AHBBAR0		(PKUNITY_PCIBRI_BASE + 0x0184) +#define PCIBRI_AHBAMR0		(PKUNITY_PCIBRI_BASE + 0x0188) +#define PCIBRI_AHBTAR0		(PKUNITY_PCIBRI_BASE + 0x018C) +#define PCIBRI_AHBCTL1		(PKUNITY_PCIBRI_BASE + 0x0190) +#define PCIBRI_AHBBAR1		(PKUNITY_PCIBRI_BASE + 0x0194) +#define PCIBRI_AHBAMR1		(PKUNITY_PCIBRI_BASE + 0x0198) +#define PCIBRI_AHBTAR1		(PKUNITY_PCIBRI_BASE + 0x019C) +#define PCIBRI_AHBCTL2		(PKUNITY_PCIBRI_BASE + 0x01A0) +#define PCIBRI_AHBBAR2		(PKUNITY_PCIBRI_BASE + 0x01A4) +#define PCIBRI_AHBAMR2		(PKUNITY_PCIBRI_BASE + 0x01A8) +#define PCIBRI_AHBTAR2		(PKUNITY_PCIBRI_BASE + 0x01AC) +#define PCIBRI_AHBCTL3		(PKUNITY_PCIBRI_BASE + 0x01B0) +#define PCIBRI_AHBBAR3		(PKUNITY_PCIBRI_BASE + 0x01B4) +#define PCIBRI_AHBAMR3		(PKUNITY_PCIBRI_BASE + 0x01B8) +#define PCIBRI_AHBTAR3		(PKUNITY_PCIBRI_BASE + 0x01BC) +#define PCIBRI_AHBCTL4		(PKUNITY_PCIBRI_BASE + 0x01C0) +#define PCIBRI_AHBBAR4		(PKUNITY_PCIBRI_BASE + 0x01C4) +#define PCIBRI_AHBAMR4		(PKUNITY_PCIBRI_BASE + 0x01C8) +#define PCIBRI_AHBTAR4		(PKUNITY_PCIBRI_BASE + 0x01CC) +#define PCIBRI_AHBCTL5		(PKUNITY_PCIBRI_BASE + 0x01D0) +#define PCIBRI_AHBBAR5		(PKUNITY_PCIBRI_BASE + 0x01D4) +#define PCIBRI_AHBAMR5		(PKUNITY_PCIBRI_BASE + 0x01D8) +#define PCIBRI_AHBTAR5		(PKUNITY_PCIBRI_BASE + 0x01DC) + +#define PCIBRI_CTLx_AT          FIELD(1, 1, 2) +#define PCIBRI_CTLx_PREF        FIELD(1, 1, 1) +#define PCIBRI_CTLx_MRL         FIELD(1, 1, 0) + +#define PCIBRI_BARx_ADDR        FIELD(0xFFFFFFFC, 30, 2) +#define PCIBRI_BARx_IO          FIELD(1, 1, 0) +#define PCIBRI_BARx_MEM         FIELD(0, 1, 0) + +#define PCIBRI_CMD_IO           FIELD(1, 1, 0) +#define PCIBRI_CMD_MEM          FIELD(1, 1, 1) diff --git a/arch/unicore32/include/mach/regs-pm.h b/arch/unicore32/include/mach/regs-pm.h new file mode 100644 index 00000000000..854844aa8f4 --- /dev/null +++ b/arch/unicore32/include/mach/regs-pm.h @@ -0,0 +1,126 @@ +/* + * PKUNITY Power Manager (PM) Registers + */ +/* + * PM Control Reg PM_PMCR + */ +#define PM_PMCR                 (PKUNITY_PM_BASE + 0x0000) +/* + * PM General Conf. Reg PM_PGCR + */ +#define PM_PGCR                 (PKUNITY_PM_BASE + 0x0004) +/* + * PM PLL Conf. Reg PM_PPCR + */ +#define PM_PPCR                 (PKUNITY_PM_BASE + 0x0008) +/* + * PM Wakeup Enable Reg PM_PWER + */ +#define PM_PWER                 (PKUNITY_PM_BASE + 0x000C) +/* + * PM GPIO Sleep Status Reg PM_PGSR + */ +#define PM_PGSR                 (PKUNITY_PM_BASE + 0x0010) +/* + * PM Clock Gate Reg PM_PCGR + */ +#define PM_PCGR                 (PKUNITY_PM_BASE + 0x0014) +/* + * PM SYS PLL Conf. Reg PM_PLLSYSCFG + */ +#define PM_PLLSYSCFG            (PKUNITY_PM_BASE + 0x0018) +/* + * PM DDR PLL Conf. Reg PM_PLLDDRCFG + */ +#define PM_PLLDDRCFG            (PKUNITY_PM_BASE + 0x001C) +/* + * PM VGA PLL Conf. Reg PM_PLLVGACFG + */ +#define PM_PLLVGACFG            (PKUNITY_PM_BASE + 0x0020) +/* + * PM Div Conf. Reg PM_DIVCFG + */ +#define PM_DIVCFG               (PKUNITY_PM_BASE + 0x0024) +/* + * PM SYS PLL Status Reg PM_PLLSYSSTATUS + */ +#define PM_PLLSYSSTATUS         (PKUNITY_PM_BASE + 0x0028) +/* + * PM DDR PLL Status Reg PM_PLLDDRSTATUS + */ +#define PM_PLLDDRSTATUS         (PKUNITY_PM_BASE + 0x002C) +/* + * PM VGA PLL Status Reg PM_PLLVGASTATUS + */ +#define PM_PLLVGASTATUS         (PKUNITY_PM_BASE + 0x0030) +/* + * PM Div Status Reg PM_DIVSTATUS + */ +#define PM_DIVSTATUS            (PKUNITY_PM_BASE + 0x0034) +/* + * PM Software Reset Reg PM_SWRESET + */ +#define PM_SWRESET              (PKUNITY_PM_BASE + 0x0038) +/* + * PM DDR2 PAD Start Reg PM_DDR2START + */ +#define PM_DDR2START            (PKUNITY_PM_BASE + 0x003C) +/* + * PM DDR2 PAD Status Reg PM_DDR2CAL0 + */ +#define PM_DDR2CAL0             (PKUNITY_PM_BASE + 0x0040) +/* + * PM PLL DFC Done Reg PM_PLLDFCDONE + */ +#define PM_PLLDFCDONE           (PKUNITY_PM_BASE + 0x0044) + +#define PM_PMCR_SFB             FIELD(1, 1, 0) +#define PM_PMCR_IFB             FIELD(1, 1, 1) +#define PM_PMCR_CFBSYS          FIELD(1, 1, 2) +#define PM_PMCR_CFBDDR          FIELD(1, 1, 3) +#define PM_PMCR_CFBVGA          FIELD(1, 1, 4) +#define PM_PMCR_CFBDIVBCLK      FIELD(1, 1, 5) + +/* + * GPIO 8~27 wake-up enable PM_PWER_GPIOHIGH + */ +#define PM_PWER_GPIOHIGH        FIELD(1, 1, 8) +/* + * RTC alarm wake-up enable PM_PWER_RTC + */ +#define PM_PWER_RTC             FIELD(1, 1, 31) + +#define PM_PCGR_BCLK64DDR	FIELD(1, 1, 0) +#define PM_PCGR_BCLK64VGA	FIELD(1, 1, 1) +#define PM_PCGR_BCLKDDR		FIELD(1, 1, 2) +#define PM_PCGR_BCLKPCI		FIELD(1, 1, 4) +#define PM_PCGR_BCLKDMAC	FIELD(1, 1, 5) +#define PM_PCGR_BCLKUMAL	FIELD(1, 1, 6) +#define PM_PCGR_BCLKUSB		FIELD(1, 1, 7) +#define PM_PCGR_BCLKMME		FIELD(1, 1, 10) +#define PM_PCGR_BCLKNAND	FIELD(1, 1, 11) +#define PM_PCGR_BCLKH264E	FIELD(1, 1, 12) +#define PM_PCGR_BCLKVGA		FIELD(1, 1, 13) +#define PM_PCGR_BCLKH264D	FIELD(1, 1, 14) +#define PM_PCGR_VECLK		FIELD(1, 1, 15) +#define PM_PCGR_HECLK		FIELD(1, 1, 16) +#define PM_PCGR_HDCLK		FIELD(1, 1, 17) +#define PM_PCGR_NANDCLK		FIELD(1, 1, 18) +#define PM_PCGR_GECLK		FIELD(1, 1, 19) +#define PM_PCGR_VGACLK          FIELD(1, 1, 20) +#define PM_PCGR_PCICLK		FIELD(1, 1, 21) +#define PM_PCGR_SATACLK		FIELD(1, 1, 25) + +/* + * [23:20]PM_DIVCFG_VGACLK(v) + */ +#define PM_DIVCFG_VGACLK_MASK   FMASK(4, 20) +#define PM_DIVCFG_VGACLK(v)	FIELD((v), 4, 20) + +#define PM_SWRESET_USB          FIELD(1, 1, 6) +#define PM_SWRESET_VGADIV       FIELD(1, 1, 26) +#define PM_SWRESET_GEDIV        FIELD(1, 1, 27) + +#define PM_PLLDFCDONE_SYSDFC    FIELD(1, 1, 0) +#define PM_PLLDFCDONE_DDRDFC    FIELD(1, 1, 1) +#define PM_PLLDFCDONE_VGADFC    FIELD(1, 1, 2) diff --git a/arch/unicore32/include/mach/regs-ps2.h b/arch/unicore32/include/mach/regs-ps2.h new file mode 100644 index 00000000000..17d4e6dc006 --- /dev/null +++ b/arch/unicore32/include/mach/regs-ps2.h @@ -0,0 +1,20 @@ +/* + * PKUnity PS2 Controller Registers + */ +/* + * the same as I8042_DATA_REG PS2_DATA + */ +#define PS2_DATA	(PKUNITY_PS2_BASE + 0x0060) +/* + * the same as I8042_COMMAND_REG PS2_COMMAND + */ +#define PS2_COMMAND	(PKUNITY_PS2_BASE + 0x0064) +/* + * the same as I8042_STATUS_REG PS2_STATUS + */ +#define PS2_STATUS	(PKUNITY_PS2_BASE + 0x0064) +/* + * counter reg PS2_CNT + */ +#define PS2_CNT		(PKUNITY_PS2_BASE + 0x0068) + diff --git a/arch/unicore32/include/mach/regs-resetc.h b/arch/unicore32/include/mach/regs-resetc.h new file mode 100644 index 00000000000..39900cf4c93 --- /dev/null +++ b/arch/unicore32/include/mach/regs-resetc.h @@ -0,0 +1,34 @@ +/* + * PKUnity Reset Controller (RC) Registers + */ +/* + * Software Reset Register + */ +#define RESETC_SWRR	(PKUNITY_RESETC_BASE + 0x0000) +/* + * Reset Status Register + */ +#define RESETC_RSSR	(PKUNITY_RESETC_BASE + 0x0004) + +/* + * Software Reset Bit + */ +#define RESETC_SWRR_SRB		FIELD(1, 1, 0) + +/* + * Hardware Reset + */ +#define RESETC_RSSR_HWR		FIELD(1, 1, 0) +/* + * Software Reset + */ +#define RESETC_RSSR_SWR		FIELD(1, 1, 1) +/* + * Watchdog Reset + */ +#define RESETC_RSSR_WDR		FIELD(1, 1, 2) +/* + * Sleep Mode Reset + */ +#define RESETC_RSSR_SMR		FIELD(1, 1, 3) + diff --git a/arch/unicore32/include/mach/regs-rtc.h b/arch/unicore32/include/mach/regs-rtc.h new file mode 100644 index 00000000000..e94ca193271 --- /dev/null +++ b/arch/unicore32/include/mach/regs-rtc.h @@ -0,0 +1,37 @@ +/* + * PKUnity Real-Time Clock (RTC) control registers + */ +/* + * RTC Alarm Reg RTC_RTAR + */ +#define RTC_RTAR	(PKUNITY_RTC_BASE + 0x0000) +/* + * RTC Count Reg RTC_RCNR + */ +#define RTC_RCNR	(PKUNITY_RTC_BASE + 0x0004) +/* + * RTC Trim Reg RTC_RTTR + */ +#define RTC_RTTR	(PKUNITY_RTC_BASE + 0x0008) +/* + * RTC Status Reg RTC_RTSR + */ +#define RTC_RTSR	(PKUNITY_RTC_BASE + 0x0010) + +/* + * ALarm detected RTC_RTSR_AL + */ +#define RTC_RTSR_AL		FIELD(1, 1, 0) +/* + * 1 Hz clock detected RTC_RTSR_HZ + */ +#define RTC_RTSR_HZ		FIELD(1, 1, 1) +/* + * ALarm interrupt Enable RTC_RTSR_ALE + */ +#define RTC_RTSR_ALE		FIELD(1, 1, 2) +/* + * 1 Hz clock interrupt Enable RTC_RTSR_HZE + */ +#define RTC_RTSR_HZE		FIELD(1, 1, 3) + diff --git a/arch/unicore32/include/mach/regs-sdc.h b/arch/unicore32/include/mach/regs-sdc.h new file mode 100644 index 00000000000..1303ecf660b --- /dev/null +++ b/arch/unicore32/include/mach/regs-sdc.h @@ -0,0 +1,156 @@ +/* + * PKUnity Multi-Media Card and Security Digital Card (MMC/SD) Registers + */ +/* + * Clock Control Reg SDC_CCR + */ +#define SDC_CCR		(PKUNITY_SDC_BASE + 0x0000) +/* + * Software Reset Reg SDC_SRR + */ +#define SDC_SRR		(PKUNITY_SDC_BASE + 0x0004) +/* + * Argument Reg SDC_ARGUMENT + */ +#define SDC_ARGUMENT	(PKUNITY_SDC_BASE + 0x0008) +/* + * Command Reg SDC_COMMAND + */ +#define SDC_COMMAND	(PKUNITY_SDC_BASE + 0x000C) +/* + * Block Size Reg SDC_BLOCKSIZE + */ +#define SDC_BLOCKSIZE	(PKUNITY_SDC_BASE + 0x0010) +/* + * Block Cound Reg SDC_BLOCKCOUNT + */ +#define SDC_BLOCKCOUNT	(PKUNITY_SDC_BASE + 0x0014) +/* + * Transfer Mode Reg SDC_TMR + */ +#define SDC_TMR		(PKUNITY_SDC_BASE + 0x0018) +/* + * Response Reg. 0 SDC_RES0 + */ +#define SDC_RES0	(PKUNITY_SDC_BASE + 0x001C) +/* + * Response Reg. 1 SDC_RES1 + */ +#define SDC_RES1	(PKUNITY_SDC_BASE + 0x0020) +/* + * Response Reg. 2 SDC_RES2 + */ +#define SDC_RES2	(PKUNITY_SDC_BASE + 0x0024) +/* + * Response Reg. 3 SDC_RES3 + */ +#define SDC_RES3	(PKUNITY_SDC_BASE + 0x0028) +/* + * Read Timeout Control Reg SDC_RTCR + */ +#define SDC_RTCR	(PKUNITY_SDC_BASE + 0x002C) +/* + * Interrupt Status Reg SDC_ISR + */ +#define SDC_ISR		(PKUNITY_SDC_BASE + 0x0030) +/* + * Interrupt Status Mask Reg SDC_ISMR + */ +#define SDC_ISMR	(PKUNITY_SDC_BASE + 0x0034) +/* + * RX FIFO SDC_RXFIFO + */ +#define SDC_RXFIFO	(PKUNITY_SDC_BASE + 0x0038) +/* + * TX FIFO SDC_TXFIFO + */ +#define SDC_TXFIFO	(PKUNITY_SDC_BASE + 0x003C) + +/* + * SD Clock Enable SDC_CCR_CLKEN + */ +#define SDC_CCR_CLKEN			FIELD(1, 1, 2) +/* + * [15:8] SDC_CCR_PDIV(v) + */ +#define SDC_CCR_PDIV(v)			FIELD((v), 8, 8) + +/* + * Software reset enable SDC_SRR_ENABLE + */ +#define SDC_SRR_ENABLE			FIELD(0, 1, 0) +/* + * Software reset disable SDC_SRR_DISABLE + */ +#define SDC_SRR_DISABLE			FIELD(1, 1, 0) + +/* + * Response type SDC_COMMAND_RESTYPE_MASK + */ +#define SDC_COMMAND_RESTYPE_MASK	FMASK(2, 0) +/* + * No response SDC_COMMAND_RESTYPE_NONE + */ +#define SDC_COMMAND_RESTYPE_NONE	FIELD(0, 2, 0) +/* + * 136-bit long response SDC_COMMAND_RESTYPE_LONG + */ +#define SDC_COMMAND_RESTYPE_LONG	FIELD(1, 2, 0) +/* + * 48-bit short response SDC_COMMAND_RESTYPE_SHORT + */ +#define SDC_COMMAND_RESTYPE_SHORT	FIELD(2, 2, 0) +/* + * 48-bit short and test if busy response SDC_COMMAND_RESTYPE_SHORTBUSY + */ +#define SDC_COMMAND_RESTYPE_SHORTBUSY	FIELD(3, 2, 0) +/* + * data ready SDC_COMMAND_DATAREADY + */ +#define SDC_COMMAND_DATAREADY		FIELD(1, 1, 2) +#define SDC_COMMAND_CMDEN		FIELD(1, 1, 3) +/* + * [10:5] SDC_COMMAND_CMDINDEX(v) + */ +#define SDC_COMMAND_CMDINDEX(v)		FIELD((v), 6, 5) + +/* + * [10:0] SDC_BLOCKSIZE_BSMASK(v) + */ +#define SDC_BLOCKSIZE_BSMASK(v)		FIELD((v), 11, 0) +/* + * [11:0] SDC_BLOCKCOUNT_BCMASK(v) + */ +#define SDC_BLOCKCOUNT_BCMASK(v)	FIELD((v), 12, 0) + +/* + * Data Width 1bit SDC_TMR_WTH_1BIT + */ +#define SDC_TMR_WTH_1BIT		FIELD(0, 1, 0) +/* + * Data Width 4bit SDC_TMR_WTH_4BIT + */ +#define SDC_TMR_WTH_4BIT		FIELD(1, 1, 0) +/* + * Read SDC_TMR_DIR_READ + */ +#define SDC_TMR_DIR_READ		FIELD(0, 1, 1) +/* + * Write SDC_TMR_DIR_WRITE + */ +#define SDC_TMR_DIR_WRITE		FIELD(1, 1, 1) + +#define SDC_IR_MASK			FMASK(13, 0) +#define SDC_IR_RESTIMEOUT		FIELD(1, 1, 0) +#define SDC_IR_WRITECRC			FIELD(1, 1, 1) +#define SDC_IR_READCRC			FIELD(1, 1, 2) +#define SDC_IR_TXFIFOREAD		FIELD(1, 1, 3) +#define SDC_IR_RXFIFOWRITE		FIELD(1, 1, 4) +#define SDC_IR_READTIMEOUT		FIELD(1, 1, 5) +#define SDC_IR_DATACOMPLETE		FIELD(1, 1, 6) +#define SDC_IR_CMDCOMPLETE		FIELD(1, 1, 7) +#define SDC_IR_RXFIFOFULL		FIELD(1, 1, 8) +#define SDC_IR_RXFIFOEMPTY		FIELD(1, 1, 9) +#define SDC_IR_TXFIFOFULL		FIELD(1, 1, 10) +#define SDC_IR_TXFIFOEMPTY		FIELD(1, 1, 11) +#define SDC_IR_ENDCMDWITHRES		FIELD(1, 1, 12) diff --git a/arch/unicore32/include/mach/regs-spi.h b/arch/unicore32/include/mach/regs-spi.h new file mode 100644 index 00000000000..de16895e2dc --- /dev/null +++ b/arch/unicore32/include/mach/regs-spi.h @@ -0,0 +1,98 @@ +/* + * PKUnity Serial Peripheral Interface (SPI) Registers + */ +/* + * Control reg. 0 SPI_CR0 + */ +#define SPI_CR0		(PKUNITY_SPI_BASE + 0x0000) +/* + * Control reg. 1 SPI_CR1 + */ +#define SPI_CR1		(PKUNITY_SPI_BASE + 0x0004) +/* + * Enable reg SPI_SSIENR + */ +#define SPI_SSIENR	(PKUNITY_SPI_BASE + 0x0008) +/* + * Status reg SPI_SR + */ +#define SPI_SR		(PKUNITY_SPI_BASE + 0x0028) +/* + * Interrupt Mask reg SPI_IMR + */ +#define SPI_IMR		(PKUNITY_SPI_BASE + 0x002C) +/* + * Interrupt Status reg SPI_ISR + */ +#define SPI_ISR		(PKUNITY_SPI_BASE + 0x0030) + +/* + * Enable SPI Controller SPI_SSIENR_EN + */ +#define SPI_SSIENR_EN		FIELD(1, 1, 0) + +/* + * SPI Busy SPI_SR_BUSY + */ +#define SPI_SR_BUSY		FIELD(1, 1, 0) +/* + * Transmit FIFO Not Full SPI_SR_TFNF + */ +#define SPI_SR_TFNF		FIELD(1, 1, 1) +/* + * Transmit FIFO Empty SPI_SR_TFE + */ +#define SPI_SR_TFE		FIELD(1, 1, 2) +/* + * Receive FIFO Not Empty SPI_SR_RFNE + */ +#define SPI_SR_RFNE		FIELD(1, 1, 3) +/* + * Receive FIFO Full SPI_SR_RFF + */ +#define SPI_SR_RFF		FIELD(1, 1, 4) + +/* + * Trans. FIFO Empty Interrupt Status SPI_ISR_TXEIS + */ +#define SPI_ISR_TXEIS		FIELD(1, 1, 0) +/* + * Trans. FIFO Overflow Interrupt Status SPI_ISR_TXOIS + */ +#define SPI_ISR_TXOIS		FIELD(1, 1, 1) +/* + * Receiv. FIFO Underflow Interrupt Status SPI_ISR_RXUIS + */ +#define SPI_ISR_RXUIS		FIELD(1, 1, 2) +/* + * Receiv. FIFO Overflow Interrupt Status SPI_ISR_RXOIS + */ +#define SPI_ISR_RXOIS		FIELD(1, 1, 3) +/* + * Receiv. FIFO Full Interrupt Status SPI_ISR_RXFIS + */ +#define SPI_ISR_RXFIS		FIELD(1, 1, 4) +#define SPI_ISR_MSTIS		FIELD(1, 1, 5) + +/* + * Trans. FIFO Empty Interrupt Mask SPI_IMR_TXEIM + */ +#define SPI_IMR_TXEIM		FIELD(1, 1, 0) +/* + * Trans. FIFO Overflow Interrupt Mask SPI_IMR_TXOIM + */ +#define SPI_IMR_TXOIM		FIELD(1, 1, 1) +/* + * Receiv. FIFO Underflow Interrupt Mask SPI_IMR_RXUIM + */ +#define SPI_IMR_RXUIM		FIELD(1, 1, 2) +/* + * Receiv. FIFO Overflow Interrupt Mask SPI_IMR_RXOIM + */ +#define SPI_IMR_RXOIM		FIELD(1, 1, 3) +/* + * Receiv. FIFO Full Interrupt Mask SPI_IMR_RXFIM + */ +#define SPI_IMR_RXFIM		FIELD(1, 1, 4) +#define SPI_IMR_MSTIM		FIELD(1, 1, 5) + diff --git a/arch/unicore32/include/mach/regs-uart.h b/arch/unicore32/include/mach/regs-uart.h new file mode 100644 index 00000000000..9fa6b1938b7 --- /dev/null +++ b/arch/unicore32/include/mach/regs-uart.h @@ -0,0 +1,3 @@ +/* + * PKUnity Universal Asynchronous Receiver/Transmitter (UART) Registers + */ diff --git a/arch/unicore32/include/mach/regs-umal.h b/arch/unicore32/include/mach/regs-umal.h new file mode 100644 index 00000000000..aa22df74e11 --- /dev/null +++ b/arch/unicore32/include/mach/regs-umal.h @@ -0,0 +1,229 @@ +/* + * PKUnity Ultra Media Access Layer (UMAL) Ethernet MAC Registers + */ + +/* MAC module of UMAL */ +/* UMAL's MAC module includes G/MII interface, several additional PHY + * interfaces, and MAC control sub-layer, which provides support for control + * frames (e.g. PAUSE frames). + */ +/* + * TX/RX reset and control UMAL_CFG1 + */ +#define UMAL_CFG1		(PKUNITY_UMAL_BASE + 0x0000) +/* + * MAC interface mode control UMAL_CFG2 + */ +#define UMAL_CFG2		(PKUNITY_UMAL_BASE + 0x0004) +/* + * Inter Packet/Frame Gap UMAL_IPGIFG + */ +#define UMAL_IPGIFG		(PKUNITY_UMAL_BASE + 0x0008) +/* + * Collision retry or backoff UMAL_HALFDUPLEX + */ +#define UMAL_HALFDUPLEX		(PKUNITY_UMAL_BASE + 0x000c) +/* + * Maximum Frame Length UMAL_MAXFRAME + */ +#define UMAL_MAXFRAME		(PKUNITY_UMAL_BASE + 0x0010) +/* + * Test Regsiter UMAL_TESTREG + */ +#define UMAL_TESTREG		(PKUNITY_UMAL_BASE + 0x001c) +/* + * MII Management Configure UMAL_MIICFG + */ +#define UMAL_MIICFG		(PKUNITY_UMAL_BASE + 0x0020) +/* + * MII Management Command UMAL_MIICMD + */ +#define UMAL_MIICMD		(PKUNITY_UMAL_BASE + 0x0024) +/* + * MII Management Address UMAL_MIIADDR + */ +#define UMAL_MIIADDR		(PKUNITY_UMAL_BASE + 0x0028) +/* + * MII Management Control UMAL_MIICTRL + */ +#define UMAL_MIICTRL		(PKUNITY_UMAL_BASE + 0x002c) +/* + * MII Management Status UMAL_MIISTATUS + */ +#define UMAL_MIISTATUS		(PKUNITY_UMAL_BASE + 0x0030) +/* + * MII Management Indicator UMAL_MIIIDCT + */ +#define UMAL_MIIIDCT		(PKUNITY_UMAL_BASE + 0x0034) +/* + * Interface Control UMAL_IFCTRL + */ +#define UMAL_IFCTRL		(PKUNITY_UMAL_BASE + 0x0038) +/* + * Interface Status UMAL_IFSTATUS + */ +#define UMAL_IFSTATUS		(PKUNITY_UMAL_BASE + 0x003c) +/* + * MAC address (high 4 bytes) UMAL_STADDR1 + */ +#define UMAL_STADDR1		(PKUNITY_UMAL_BASE + 0x0040) +/* + * MAC address (low 2 bytes) UMAL_STADDR2 + */ +#define UMAL_STADDR2		(PKUNITY_UMAL_BASE + 0x0044) + +/* FIFO MODULE OF UMAL */ +/* UMAL's FIFO module provides data queuing for increased system level + * throughput + */ +#define UMAL_FIFOCFG0		(PKUNITY_UMAL_BASE + 0x0048) +#define UMAL_FIFOCFG1		(PKUNITY_UMAL_BASE + 0x004c) +#define UMAL_FIFOCFG2		(PKUNITY_UMAL_BASE + 0x0050) +#define UMAL_FIFOCFG3		(PKUNITY_UMAL_BASE + 0x0054) +#define UMAL_FIFOCFG4		(PKUNITY_UMAL_BASE + 0x0058) +#define UMAL_FIFOCFG5		(PKUNITY_UMAL_BASE + 0x005c) +#define UMAL_FIFORAM0		(PKUNITY_UMAL_BASE + 0x0060) +#define UMAL_FIFORAM1		(PKUNITY_UMAL_BASE + 0x0064) +#define UMAL_FIFORAM2		(PKUNITY_UMAL_BASE + 0x0068) +#define UMAL_FIFORAM3		(PKUNITY_UMAL_BASE + 0x006c) +#define UMAL_FIFORAM4		(PKUNITY_UMAL_BASE + 0x0070) +#define UMAL_FIFORAM5		(PKUNITY_UMAL_BASE + 0x0074) +#define UMAL_FIFORAM6		(PKUNITY_UMAL_BASE + 0x0078) +#define UMAL_FIFORAM7		(PKUNITY_UMAL_BASE + 0x007c) + +/* MAHBE MODULE OF UMAL */ +/* UMAL's MAHBE module interfaces to the host system through 32-bit AHB Master + * and Slave ports.Registers within the M-AHBE provide Control and Status + * information concerning these transfers. + */ +/* + * Transmit Control UMAL_DMATxCtrl + */ +#define UMAL_DMATxCtrl		(PKUNITY_UMAL_BASE + 0x0180) +/* + * Pointer to TX Descripter UMAL_DMATxDescriptor + */ +#define UMAL_DMATxDescriptor	(PKUNITY_UMAL_BASE + 0x0184) +/* + * Status of Tx Packet Transfers UMAL_DMATxStatus + */ +#define UMAL_DMATxStatus	(PKUNITY_UMAL_BASE + 0x0188) +/* + * Receive Control UMAL_DMARxCtrl + */ +#define UMAL_DMARxCtrl		(PKUNITY_UMAL_BASE + 0x018c) +/* + * Pointer to Rx Descriptor UMAL_DMARxDescriptor + */ +#define UMAL_DMARxDescriptor	(PKUNITY_UMAL_BASE + 0x0190) +/* + * Status of Rx Packet Transfers UMAL_DMARxStatus + */ +#define UMAL_DMARxStatus	(PKUNITY_UMAL_BASE + 0x0194) +/* + * Interrupt Mask UMAL_DMAIntrMask + */ +#define UMAL_DMAIntrMask	(PKUNITY_UMAL_BASE + 0x0198) +/* + * Interrupts, read only UMAL_DMAInterrupt + */ +#define UMAL_DMAInterrupt	(PKUNITY_UMAL_BASE + 0x019c) + +/* + * Commands for UMAL_CFG1 register + */ +#define UMAL_CFG1_TXENABLE	FIELD(1, 1, 0) +#define UMAL_CFG1_RXENABLE	FIELD(1, 1, 2) +#define UMAL_CFG1_TXFLOWCTL	FIELD(1, 1, 4) +#define UMAL_CFG1_RXFLOWCTL	FIELD(1, 1, 5) +#define UMAL_CFG1_CONFLPBK	FIELD(1, 1, 8) +#define UMAL_CFG1_RESET		FIELD(1, 1, 31) +#define UMAL_CFG1_CONFFLCTL	(MAC_TX_FLOW_CTL | MAC_RX_FLOW_CTL) + +/* + * Commands for UMAL_CFG2 register + */ +#define UMAL_CFG2_FULLDUPLEX	FIELD(1, 1, 0) +#define UMAL_CFG2_CRCENABLE	FIELD(1, 1, 1) +#define UMAL_CFG2_PADCRC	FIELD(1, 1, 2) +#define UMAL_CFG2_LENGTHCHECK	FIELD(1, 1, 4) +#define UMAL_CFG2_MODEMASK	FMASK(2, 8) +#define UMAL_CFG2_NIBBLEMODE	FIELD(1, 2, 8) +#define UMAL_CFG2_BYTEMODE	FIELD(2, 2, 8) +#define UMAL_CFG2_PREAMBLENMASK	FMASK(4, 12) +#define UMAL_CFG2_DEFPREAMBLEN	FIELD(7, 4, 12) +#define UMAL_CFG2_FD100		(UMAL_CFG2_DEFPREAMBLEN | UMAL_CFG2_NIBBLEMODE \ +				| UMAL_CFG2_LENGTHCHECK | UMAL_CFG2_PADCRC \ +				| UMAL_CFG2_CRCENABLE | UMAL_CFG2_FULLDUPLEX) +#define UMAL_CFG2_FD1000	(UMAL_CFG2_DEFPREAMBLEN | UMAL_CFG2_BYTEMODE \ +				| UMAL_CFG2_LENGTHCHECK | UMAL_CFG2_PADCRC \ +				| UMAL_CFG2_CRCENABLE | UMAL_CFG2_FULLDUPLEX) +#define UMAL_CFG2_HD100		(UMAL_CFG2_DEFPREAMBLEN | UMAL_CFG2_NIBBLEMODE \ +				| UMAL_CFG2_LENGTHCHECK | UMAL_CFG2_PADCRC \ +				| UMAL_CFG2_CRCENABLE) + +/* + * Command for UMAL_IFCTRL register + */ +#define UMAL_IFCTRL_RESET	FIELD(1, 1, 31) + +/* + * Command for UMAL_MIICFG register + */ +#define UMAL_MIICFG_RESET	FIELD(1, 1, 31) + +/* + * Command for UMAL_MIICMD register + */ +#define UMAL_MIICMD_READ	FIELD(1, 1, 0) + +/* + * Command for UMAL_MIIIDCT register + */ +#define UMAL_MIIIDCT_BUSY	FIELD(1, 1, 0) +#define UMAL_MIIIDCT_NOTVALID	FIELD(1, 1, 2) + +/* + * Commands for DMATxCtrl regesters + */ +#define UMAL_DMA_Enable		FIELD(1, 1, 0) + +/* + * Commands for DMARxCtrl regesters + */ +#define UMAL_DMAIntrMask_ENABLEHALFWORD	FIELD(1, 1, 16) + +/* + * Command for DMARxStatus + */ +#define CLR_RX_BUS_ERR		FIELD(1, 1, 3) +#define CLR_RX_OVERFLOW		FIELD(1, 1, 2) +#define CLR_RX_PKT		FIELD(1, 1, 0) + +/* + * Command for DMATxStatus + */ +#define CLR_TX_BUS_ERR		FIELD(1, 1, 3) +#define CLR_TX_UNDERRUN		FIELD(1, 1, 1) +#define CLR_TX_PKT		FIELD(1, 1, 0) + +/* + * Commands for DMAIntrMask and DMAInterrupt register + */ +#define INT_RX_MASK		FIELD(0xd, 4, 4) +#define INT_TX_MASK		FIELD(0xb, 4, 0) + +#define INT_RX_BUS_ERR		FIELD(1, 1, 7) +#define INT_RX_OVERFLOW		FIELD(1, 1, 6) +#define INT_RX_PKT		FIELD(1, 1, 4) +#define INT_TX_BUS_ERR		FIELD(1, 1, 3) +#define INT_TX_UNDERRUN		FIELD(1, 1, 1) +#define INT_TX_PKT		FIELD(1, 1, 0) + +/* + * MARCOS of UMAL's descriptors + */ +#define UMAL_DESC_PACKETSIZE_EMPTY	FIELD(1, 1, 31) +#define UMAL_DESC_PACKETSIZE_NONEMPTY	FIELD(0, 1, 31) +#define UMAL_DESC_PACKETSIZE_SIZEMASK	FMASK(12, 0) + diff --git a/arch/unicore32/include/mach/regs-unigfx.h b/arch/unicore32/include/mach/regs-unigfx.h new file mode 100644 index 00000000000..faf8b287fcc --- /dev/null +++ b/arch/unicore32/include/mach/regs-unigfx.h @@ -0,0 +1,200 @@ +/* + * PKUnity UNIGFX Registers + */ + +#define UDE_BASE      (PKUNITY_UNIGFX_BASE + 0x1400) +#define UGE_BASE      (PKUNITY_UNIGFX_BASE + 0x0000) + +/* + * command reg for UNIGFX DE + */ +/* + * control reg UDE_CFG + */ +#define UDE_CFG       (UDE_BASE + 0x0000) +/* + * framebuffer start address reg UDE_FSA + */ +#define UDE_FSA       (UDE_BASE + 0x0004) +/* + * line size reg UDE_LS + */ +#define UDE_LS        (UDE_BASE + 0x0008) +/* + * pitch size reg UDE_PS + */ +#define UDE_PS        (UDE_BASE + 0x000C) +/* + * horizontal active time reg UDE_HAT + */ +#define UDE_HAT       (UDE_BASE + 0x0010) +/* + * horizontal blank time reg UDE_HBT + */ +#define UDE_HBT       (UDE_BASE + 0x0014) +/* + * horizontal sync time reg UDE_HST + */ +#define UDE_HST       (UDE_BASE + 0x0018) +/* + * vertival active time reg UDE_VAT + */ +#define UDE_VAT       (UDE_BASE + 0x001C) +/* + * vertival blank time reg UDE_VBT + */ +#define UDE_VBT       (UDE_BASE + 0x0020) +/* + * vertival sync time reg UDE_VST + */ +#define UDE_VST       (UDE_BASE + 0x0024) +/* + * cursor position UDE_CXY + */ +#define UDE_CXY       (UDE_BASE + 0x0028) +/* + * cursor front color UDE_CC0 + */ +#define UDE_CC0       (UDE_BASE + 0x002C) +/* + * cursor background color UDE_CC1 + */ +#define UDE_CC1       (UDE_BASE + 0x0030) +/* + * video position UDE_VXY + */ +#define UDE_VXY       (UDE_BASE + 0x0034) +/* + * video start address reg UDE_VSA + */ +#define UDE_VSA       (UDE_BASE + 0x0040) +/* + * video size reg UDE_VS + */ +#define UDE_VS        (UDE_BASE + 0x004C) + +/* + * command reg for UNIGFX GE + */ +/* + * src xy reg UGE_SRCXY + */ +#define UGE_SRCXY     (UGE_BASE + 0x0000) +/* + * dst xy reg UGE_DSTXY + */ +#define UGE_DSTXY     (UGE_BASE + 0x0004) +/* + * pitch reg UGE_PITCH + */ +#define UGE_PITCH     (UGE_BASE + 0x0008) +/* + * src start reg UGE_SRCSTART + */ +#define UGE_SRCSTART  (UGE_BASE + 0x000C) +/* + * dst start reg UGE_DSTSTART + */ +#define UGE_DSTSTART  (UGE_BASE + 0x0010) +/* + * width height reg UGE_WIDHEIGHT + */ +#define UGE_WIDHEIGHT (UGE_BASE + 0x0014) +/* + * rop alpah reg UGE_ROPALPHA + */ +#define UGE_ROPALPHA  (UGE_BASE + 0x0018) +/* + * front color UGE_FCOLOR + */ +#define UGE_FCOLOR    (UGE_BASE + 0x001C) +/* + * background color UGE_BCOLOR + */ +#define UGE_BCOLOR    (UGE_BASE + 0x0020) +/* + * src color key for high value UGE_SCH + */ +#define UGE_SCH       (UGE_BASE + 0x0024) +/* + * dst color key for high value UGE_DCH + */ +#define UGE_DCH       (UGE_BASE + 0x0028) +/* + * src color key for low value UGE_SCL + */ +#define UGE_SCL       (UGE_BASE + 0x002C) +/* + * dst color key for low value UGE_DCL + */ +#define UGE_DCL       (UGE_BASE + 0x0030) +/* + * clip 0 reg UGE_CLIP0 + */ +#define UGE_CLIP0     (UGE_BASE + 0x0034) +/* + * clip 1 reg UGE_CLIP1 + */ +#define UGE_CLIP1     (UGE_BASE + 0x0038) +/* + * command reg UGE_COMMAND + */ +#define UGE_COMMAND   (UGE_BASE + 0x003C) +/* + * pattern 0 UGE_P0 + */ +#define UGE_P0        (UGE_BASE + 0x0040) +#define UGE_P1        (UGE_BASE + 0x0044) +#define UGE_P2        (UGE_BASE + 0x0048) +#define UGE_P3        (UGE_BASE + 0x004C) +#define UGE_P4        (UGE_BASE + 0x0050) +#define UGE_P5        (UGE_BASE + 0x0054) +#define UGE_P6        (UGE_BASE + 0x0058) +#define UGE_P7        (UGE_BASE + 0x005C) +#define UGE_P8        (UGE_BASE + 0x0060) +#define UGE_P9        (UGE_BASE + 0x0064) +#define UGE_P10       (UGE_BASE + 0x0068) +#define UGE_P11       (UGE_BASE + 0x006C) +#define UGE_P12       (UGE_BASE + 0x0070) +#define UGE_P13       (UGE_BASE + 0x0074) +#define UGE_P14       (UGE_BASE + 0x0078) +#define UGE_P15       (UGE_BASE + 0x007C) +#define UGE_P16       (UGE_BASE + 0x0080) +#define UGE_P17       (UGE_BASE + 0x0084) +#define UGE_P18       (UGE_BASE + 0x0088) +#define UGE_P19       (UGE_BASE + 0x008C) +#define UGE_P20       (UGE_BASE + 0x0090) +#define UGE_P21       (UGE_BASE + 0x0094) +#define UGE_P22       (UGE_BASE + 0x0098) +#define UGE_P23       (UGE_BASE + 0x009C) +#define UGE_P24       (UGE_BASE + 0x00A0) +#define UGE_P25       (UGE_BASE + 0x00A4) +#define UGE_P26       (UGE_BASE + 0x00A8) +#define UGE_P27       (UGE_BASE + 0x00AC) +#define UGE_P28       (UGE_BASE + 0x00B0) +#define UGE_P29       (UGE_BASE + 0x00B4) +#define UGE_P30       (UGE_BASE + 0x00B8) +#define UGE_P31       (UGE_BASE + 0x00BC) + +#define UDE_CFG_DST_MASK	FMASK(2, 8) +#define UDE_CFG_DST8            FIELD(0x0, 2, 8) +#define UDE_CFG_DST16           FIELD(0x1, 2, 8) +#define UDE_CFG_DST24           FIELD(0x2, 2, 8) +#define UDE_CFG_DST32           FIELD(0x3, 2, 8) + +/* + * GDEN enable UDE_CFG_GDEN_ENABLE + */ +#define UDE_CFG_GDEN_ENABLE     FIELD(1, 1, 3) +/* + * VDEN enable UDE_CFG_VDEN_ENABLE + */ +#define UDE_CFG_VDEN_ENABLE     FIELD(1, 1, 4) +/* + * CDEN enable UDE_CFG_CDEN_ENABLE + */ +#define UDE_CFG_CDEN_ENABLE     FIELD(1, 1, 5) +/* + * TIMEUP enable UDE_CFG_TIMEUP_ENABLE + */ +#define UDE_CFG_TIMEUP_ENABLE   FIELD(1, 1, 6) diff --git a/arch/unicore32/include/mach/uncompress.h b/arch/unicore32/include/mach/uncompress.h new file mode 100644 index 00000000000..9be67c9d3b5 --- /dev/null +++ b/arch/unicore32/include/mach/uncompress.h @@ -0,0 +1,34 @@ +/* + * linux/arch/unicore32/include/mach/uncompress.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __MACH_PUV3_UNCOMPRESS_H__ +#define __MACH_PUV3_UNCOMPRESS_H__ + +#include <mach/hardware.h> +#include <mach/ocd.h> + +extern char input_data[]; +extern char input_data_end[]; + +static void arch_decomp_puts(const char *ptr) +{ +	char c; + +	while ((c = *ptr++) != '\0') { +		if (c == '\n') +			putc('\r'); +		putc(c); +	} +} +#define ARCH_HAVE_DECOMP_PUTS + +#endif /* __MACH_PUV3_UNCOMPRESS_H__ */ diff --git a/arch/unicore32/include/uapi/asm/Kbuild b/arch/unicore32/include/uapi/asm/Kbuild new file mode 100644 index 00000000000..0514d7ad685 --- /dev/null +++ b/arch/unicore32/include/uapi/asm/Kbuild @@ -0,0 +1,10 @@ +# UAPI Header export list +include include/uapi/asm-generic/Kbuild.asm + +header-y += byteorder.h +header-y += kvm_para.h +header-y += ptrace.h +header-y += sigcontext.h +header-y += unistd.h + +generic-y += kvm_para.h diff --git a/arch/unicore32/include/uapi/asm/byteorder.h b/arch/unicore32/include/uapi/asm/byteorder.h new file mode 100644 index 00000000000..ebe1b3fef3e --- /dev/null +++ b/arch/unicore32/include/uapi/asm/byteorder.h @@ -0,0 +1,24 @@ +/* + * linux/arch/unicore32/include/asm/byteorder.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * 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. + * + * UniCore ONLY support Little Endian mode, the data bus is connected such + * that byte accesses appear as: + *  0 = d0...d7, 1 = d8...d15, 2 = d16...d23, 3 = d24...d31 + * and word accesses (data or instruction) appear as: + *  d0...d31 + */ +#ifndef __UNICORE_BYTEORDER_H__ +#define __UNICORE_BYTEORDER_H__ + +#include <linux/byteorder/little_endian.h> + +#endif + diff --git a/arch/unicore32/include/uapi/asm/ptrace.h b/arch/unicore32/include/uapi/asm/ptrace.h new file mode 100644 index 00000000000..187aa2e98a5 --- /dev/null +++ b/arch/unicore32/include/uapi/asm/ptrace.h @@ -0,0 +1,90 @@ +/* + * linux/arch/unicore32/include/asm/ptrace.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef _UAPI__UNICORE_PTRACE_H__ +#define _UAPI__UNICORE_PTRACE_H__ + +#define PTRACE_GET_THREAD_AREA	22 + +/* + * PSR bits + */ +#define USER_MODE	0x00000010 +#define REAL_MODE	0x00000011 +#define INTR_MODE	0x00000012 +#define PRIV_MODE	0x00000013 +#define ABRT_MODE	0x00000017 +#define EXTN_MODE	0x0000001b +#define SUSR_MODE	0x0000001f +#define MODE_MASK	0x0000001f +#define PSR_R_BIT	0x00000040 +#define PSR_I_BIT	0x00000080 +#define PSR_V_BIT	0x10000000 +#define PSR_C_BIT	0x20000000 +#define PSR_Z_BIT	0x40000000 +#define PSR_S_BIT	0x80000000 + +/* + * Groups of PSR bits + */ +#define PSR_f		0xff000000	/* Flags		*/ +#define PSR_c		0x000000ff	/* Control		*/ + +#ifndef __ASSEMBLY__ + +/* + * This struct defines the way the registers are stored on the + * stack during a system call.  Note that sizeof(struct pt_regs) + * has to be a multiple of 8. + */ +struct pt_regs { +	unsigned long uregs[34]; +}; + +#define UCreg_asr		uregs[32] +#define UCreg_pc		uregs[31] +#define UCreg_lr		uregs[30] +#define UCreg_sp		uregs[29] +#define UCreg_ip		uregs[28] +#define UCreg_fp		uregs[27] +#define UCreg_26		uregs[26] +#define UCreg_25		uregs[25] +#define UCreg_24		uregs[24] +#define UCreg_23		uregs[23] +#define UCreg_22		uregs[22] +#define UCreg_21		uregs[21] +#define UCreg_20		uregs[20] +#define UCreg_19		uregs[19] +#define UCreg_18		uregs[18] +#define UCreg_17		uregs[17] +#define UCreg_16		uregs[16] +#define UCreg_15		uregs[15] +#define UCreg_14		uregs[14] +#define UCreg_13		uregs[13] +#define UCreg_12		uregs[12] +#define UCreg_11		uregs[11] +#define UCreg_10		uregs[10] +#define UCreg_09		uregs[9] +#define UCreg_08		uregs[8] +#define UCreg_07		uregs[7] +#define UCreg_06		uregs[6] +#define UCreg_05		uregs[5] +#define UCreg_04		uregs[4] +#define UCreg_03		uregs[3] +#define UCreg_02		uregs[2] +#define UCreg_01		uregs[1] +#define UCreg_00		uregs[0] +#define UCreg_ORIG_00		uregs[33] + + +#endif /* __ASSEMBLY__ */ + +#endif /* _UAPI__UNICORE_PTRACE_H__ */ diff --git a/arch/unicore32/include/uapi/asm/sigcontext.h b/arch/unicore32/include/uapi/asm/sigcontext.h new file mode 100644 index 00000000000..6a2d7671c05 --- /dev/null +++ b/arch/unicore32/include/uapi/asm/sigcontext.h @@ -0,0 +1,29 @@ +/* + * linux/arch/unicore32/include/asm/sigcontext.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_SIGCONTEXT_H__ +#define __UNICORE_SIGCONTEXT_H__ + +#include <asm/ptrace.h> +/* + * Signal context structure - contains all info to do with the state + * before the signal handler was invoked.  Note: only add new entries + * to the end of the structure. + */ +struct sigcontext { +	unsigned long trap_no; +	unsigned long error_code; +	unsigned long oldmask; +	unsigned long fault_address; +	struct pt_regs regs; +}; + +#endif diff --git a/arch/unicore32/include/uapi/asm/unistd.h b/arch/unicore32/include/uapi/asm/unistd.h new file mode 100644 index 00000000000..d4cc4559d84 --- /dev/null +++ b/arch/unicore32/include/uapi/asm/unistd.h @@ -0,0 +1,15 @@ +/* + * linux/arch/unicore32/include/asm/unistd.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * 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. + */ + +/* Use the standard ABI for syscalls. */ +#include <asm-generic/unistd.h> +#define __ARCH_WANT_SYS_CLONE diff --git a/arch/unicore32/kernel/Makefile b/arch/unicore32/kernel/Makefile new file mode 100644 index 00000000000..607a72f2ae3 --- /dev/null +++ b/arch/unicore32/kernel/Makefile @@ -0,0 +1,30 @@ +# +# Makefile for the linux kernel. +# + +# Object file lists. +obj-y				:= dma.o elf.o entry.o process.o ptrace.o +obj-y				+= setup.o signal.o sys.o stacktrace.o traps.o + +obj-$(CONFIG_MODULES)		+= ksyms.o module.o +obj-$(CONFIG_EARLY_PRINTK)	+= early_printk.o + +obj-$(CONFIG_UNICORE_FPU_F64)	+= fpu-ucf64.o + +# obj-y for architecture PKUnity v3 +obj-$(CONFIG_ARCH_PUV3)		+= clock.o irq.o time.o + +obj-$(CONFIG_PUV3_GPIO)		+= gpio.o +obj-$(CONFIG_PUV3_PM)		+= pm.o sleep.o +obj-$(CONFIG_HIBERNATION)	+= hibernate.o hibernate_asm.o + +obj-$(CONFIG_PCI)		+= pci.o + +# obj-y for specific machines +obj-$(CONFIG_ARCH_PUV3)		+= puv3-core.o +obj-$(CONFIG_PUV3_NB0916)	+= puv3-nb0916.o + +head-y				:= head.o +obj-$(CONFIG_DEBUG_LL)		+= debug.o + +extra-y				:= $(head-y) vmlinux.lds diff --git a/arch/unicore32/kernel/asm-offsets.c b/arch/unicore32/kernel/asm-offsets.c new file mode 100644 index 00000000000..ffcbe7536ca --- /dev/null +++ b/arch/unicore32/kernel/asm-offsets.c @@ -0,0 +1,112 @@ +/* + * linux/arch/unicore32/kernel/asm-offsets.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * Generate definitions needed by assembly language modules. + * This code generates raw asm output which is post-processed to extract + * and format the required data. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/dma-mapping.h> +#include <linux/kbuild.h> +#include <linux/suspend.h> +#include <linux/thread_info.h> +#include <asm/memory.h> +#include <asm/suspend.h> + +/* + * GCC 3.0, 3.1: general bad code generation. + * GCC 3.2.0: incorrect function argument offset calculation. + * GCC 3.2.x: miscompiles NEW_AUX_ENT in fs/binfmt_elf.c + *	(http://gcc.gnu.org/PR8896) and incorrect structure + *		initialisation in fs/jffs2/erase.c + */ +#if (__GNUC__ < 4) +#error Your compiler should upgrade to uc4 +#error	Known good compilers: 4.2.2 +#endif + +int main(void) +{ +	DEFINE(TSK_ACTIVE_MM,	offsetof(struct task_struct, active_mm)); +	BLANK(); +	DEFINE(TI_FLAGS,	offsetof(struct thread_info, flags)); +	DEFINE(TI_PREEMPT,	offsetof(struct thread_info, preempt_count)); +	DEFINE(TI_ADDR_LIMIT,	offsetof(struct thread_info, addr_limit)); +	DEFINE(TI_TASK,		offsetof(struct thread_info, task)); +	DEFINE(TI_EXEC_DOMAIN,	offsetof(struct thread_info, exec_domain)); +	DEFINE(TI_CPU,		offsetof(struct thread_info, cpu)); +	DEFINE(TI_CPU_SAVE,	offsetof(struct thread_info, cpu_context)); +	DEFINE(TI_USED_CP,	offsetof(struct thread_info, used_cp)); +#ifdef CONFIG_UNICORE_FPU_F64 +	DEFINE(TI_FPSTATE,	offsetof(struct thread_info, fpstate)); +#endif +	BLANK(); +	DEFINE(S_R0,		offsetof(struct pt_regs, UCreg_00)); +	DEFINE(S_R1,		offsetof(struct pt_regs, UCreg_01)); +	DEFINE(S_R2,		offsetof(struct pt_regs, UCreg_02)); +	DEFINE(S_R3,		offsetof(struct pt_regs, UCreg_03)); +	DEFINE(S_R4,		offsetof(struct pt_regs, UCreg_04)); +	DEFINE(S_R5,		offsetof(struct pt_regs, UCreg_05)); +	DEFINE(S_R6,		offsetof(struct pt_regs, UCreg_06)); +	DEFINE(S_R7,		offsetof(struct pt_regs, UCreg_07)); +	DEFINE(S_R8,		offsetof(struct pt_regs, UCreg_08)); +	DEFINE(S_R9,		offsetof(struct pt_regs, UCreg_09)); +	DEFINE(S_R10,		offsetof(struct pt_regs, UCreg_10)); +	DEFINE(S_R11,		offsetof(struct pt_regs, UCreg_11)); +	DEFINE(S_R12,		offsetof(struct pt_regs, UCreg_12)); +	DEFINE(S_R13,		offsetof(struct pt_regs, UCreg_13)); +	DEFINE(S_R14,		offsetof(struct pt_regs, UCreg_14)); +	DEFINE(S_R15,		offsetof(struct pt_regs, UCreg_15)); +	DEFINE(S_R16,		offsetof(struct pt_regs, UCreg_16)); +	DEFINE(S_R17,		offsetof(struct pt_regs, UCreg_17)); +	DEFINE(S_R18,		offsetof(struct pt_regs, UCreg_18)); +	DEFINE(S_R19,		offsetof(struct pt_regs, UCreg_19)); +	DEFINE(S_R20,		offsetof(struct pt_regs, UCreg_20)); +	DEFINE(S_R21,		offsetof(struct pt_regs, UCreg_21)); +	DEFINE(S_R22,		offsetof(struct pt_regs, UCreg_22)); +	DEFINE(S_R23,		offsetof(struct pt_regs, UCreg_23)); +	DEFINE(S_R24,		offsetof(struct pt_regs, UCreg_24)); +	DEFINE(S_R25,		offsetof(struct pt_regs, UCreg_25)); +	DEFINE(S_R26,		offsetof(struct pt_regs, UCreg_26)); +	DEFINE(S_FP,		offsetof(struct pt_regs, UCreg_fp)); +	DEFINE(S_IP,		offsetof(struct pt_regs, UCreg_ip)); +	DEFINE(S_SP,		offsetof(struct pt_regs, UCreg_sp)); +	DEFINE(S_LR,		offsetof(struct pt_regs, UCreg_lr)); +	DEFINE(S_PC,		offsetof(struct pt_regs, UCreg_pc)); +	DEFINE(S_PSR,		offsetof(struct pt_regs, UCreg_asr)); +	DEFINE(S_OLD_R0,	offsetof(struct pt_regs, UCreg_ORIG_00)); +	DEFINE(S_FRAME_SIZE,	sizeof(struct pt_regs)); +	BLANK(); +	DEFINE(VMA_VM_MM,	offsetof(struct vm_area_struct, vm_mm)); +	DEFINE(VMA_VM_FLAGS,	offsetof(struct vm_area_struct, vm_flags)); +	BLANK(); +	DEFINE(VM_EXEC,		VM_EXEC); +	BLANK(); +	DEFINE(PAGE_SZ,		PAGE_SIZE); +	BLANK(); +	DEFINE(SYS_ERROR0,	0x9f0000); +	BLANK(); +	DEFINE(PBE_ADDRESS,		offsetof(struct pbe, address)); +	DEFINE(PBE_ORIN_ADDRESS,	offsetof(struct pbe, orig_address)); +	DEFINE(PBE_NEXT,		offsetof(struct pbe, next)); +	DEFINE(SWSUSP_CPU,		offsetof(struct swsusp_arch_regs, \ +							cpu_context)); +#ifdef	CONFIG_UNICORE_FPU_F64 +	DEFINE(SWSUSP_FPSTATE,		offsetof(struct swsusp_arch_regs, \ +							fpstate)); +#endif +	BLANK(); +	DEFINE(DMA_BIDIRECTIONAL,	DMA_BIDIRECTIONAL); +	DEFINE(DMA_TO_DEVICE,		DMA_TO_DEVICE); +	DEFINE(DMA_FROM_DEVICE,		DMA_FROM_DEVICE); +	return 0; +} diff --git a/arch/unicore32/kernel/clock.c b/arch/unicore32/kernel/clock.c new file mode 100644 index 00000000000..b1ca775f6f6 --- /dev/null +++ b/arch/unicore32/kernel/clock.c @@ -0,0 +1,390 @@ +/* + * linux/arch/unicore32/kernel/clock.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + *	Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn> + *	Copyright (C) 2001-2010 Guan Xuetao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/list.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/string.h> +#include <linux/clk.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/io.h> + +#include <mach/hardware.h> + +/* + * Very simple clock implementation + */ +struct clk { +	struct list_head	node; +	unsigned long		rate; +	const char		*name; +}; + +static struct clk clk_ost_clk = { +	.name		= "OST_CLK", +	.rate		= CLOCK_TICK_RATE, +}; + +static struct clk clk_mclk_clk = { +	.name		= "MAIN_CLK", +}; + +static struct clk clk_bclk32_clk = { +	.name		= "BUS32_CLK", +}; + +static struct clk clk_ddr_clk = { +	.name		= "DDR_CLK", +}; + +static struct clk clk_vga_clk = { +	.name		= "VGA_CLK", +}; + +static LIST_HEAD(clocks); +static DEFINE_MUTEX(clocks_mutex); + +struct clk *clk_get(struct device *dev, const char *id) +{ +	struct clk *p, *clk = ERR_PTR(-ENOENT); + +	mutex_lock(&clocks_mutex); +	list_for_each_entry(p, &clocks, node) { +		if (strcmp(id, p->name) == 0) { +			clk = p; +			break; +		} +	} +	mutex_unlock(&clocks_mutex); + +	return clk; +} +EXPORT_SYMBOL(clk_get); + +void clk_put(struct clk *clk) +{ +} +EXPORT_SYMBOL(clk_put); + +int clk_enable(struct clk *clk) +{ +	return 0; +} +EXPORT_SYMBOL(clk_enable); + +void clk_disable(struct clk *clk) +{ +} +EXPORT_SYMBOL(clk_disable); + +unsigned long clk_get_rate(struct clk *clk) +{ +	return clk->rate; +} +EXPORT_SYMBOL(clk_get_rate); + +struct { +	unsigned long rate; +	unsigned long cfg; +	unsigned long div; +} vga_clk_table[] = { +	{.rate =  25175000, .cfg = 0x00002001, .div = 0x9}, +	{.rate =  31500000, .cfg = 0x00002001, .div = 0x7}, +	{.rate =  40000000, .cfg = 0x00003801, .div = 0x9}, +	{.rate =  49500000, .cfg = 0x00003801, .div = 0x7}, +	{.rate =  65000000, .cfg = 0x00002c01, .div = 0x4}, +	{.rate =  78750000, .cfg = 0x00002400, .div = 0x7}, +	{.rate = 108000000, .cfg = 0x00002c01, .div = 0x2}, +	{.rate = 106500000, .cfg = 0x00003c01, .div = 0x3}, +	{.rate =  50650000, .cfg = 0x00106400, .div = 0x9}, +	{.rate =  61500000, .cfg = 0x00106400, .div = 0xa}, +	{.rate =  85500000, .cfg = 0x00002800, .div = 0x6}, +}; + +struct { +	unsigned long mrate; +	unsigned long prate; +} mclk_clk_table[] = { +	{.mrate = 500000000, .prate = 0x00109801}, +	{.mrate = 525000000, .prate = 0x00104C00}, +	{.mrate = 550000000, .prate = 0x00105000}, +	{.mrate = 575000000, .prate = 0x00105400}, +	{.mrate = 600000000, .prate = 0x00105800}, +	{.mrate = 625000000, .prate = 0x00105C00}, +	{.mrate = 650000000, .prate = 0x00106000}, +	{.mrate = 675000000, .prate = 0x00106400}, +	{.mrate = 700000000, .prate = 0x00106800}, +	{.mrate = 725000000, .prate = 0x00106C00}, +	{.mrate = 750000000, .prate = 0x00107000}, +	{.mrate = 775000000, .prate = 0x00107400}, +	{.mrate = 800000000, .prate = 0x00107800}, +}; + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ +	if (clk == &clk_vga_clk) { +		unsigned long pll_vgacfg, pll_vgadiv; +		int ret, i; + +		/* lookup vga_clk_table */ +		ret = -EINVAL; +		for (i = 0; i < ARRAY_SIZE(vga_clk_table); i++) { +			if (rate == vga_clk_table[i].rate) { +				pll_vgacfg = vga_clk_table[i].cfg; +				pll_vgadiv = vga_clk_table[i].div; +				ret = 0; +				break; +			} +		} + +		if (ret) +			return ret; + +		if (readl(PM_PLLVGACFG) == pll_vgacfg) +			return 0; + +		/* set pll vga cfg reg. */ +		writel(pll_vgacfg, PM_PLLVGACFG); + +		writel(PM_PMCR_CFBVGA, PM_PMCR); +		while ((readl(PM_PLLDFCDONE) & PM_PLLDFCDONE_VGADFC) +				!= PM_PLLDFCDONE_VGADFC) +			udelay(100); /* about 1ms */ + +		/* set div cfg reg. */ +		writel(readl(PM_PCGR) | PM_PCGR_VGACLK, PM_PCGR); + +		writel((readl(PM_DIVCFG) & ~PM_DIVCFG_VGACLK_MASK) +				| PM_DIVCFG_VGACLK(pll_vgadiv), PM_DIVCFG); + +		writel(readl(PM_SWRESET) | PM_SWRESET_VGADIV, PM_SWRESET); +		while ((readl(PM_SWRESET) & PM_SWRESET_VGADIV) +				== PM_SWRESET_VGADIV) +			udelay(100); /* 65536 bclk32, about 320us */ + +		writel(readl(PM_PCGR) & ~PM_PCGR_VGACLK, PM_PCGR); +	} +#ifdef CONFIG_CPU_FREQ +	if (clk == &clk_mclk_clk) { +		u32 pll_rate, divstatus = readl(PM_DIVSTATUS); +		int ret, i; + +		/* lookup mclk_clk_table */ +		ret = -EINVAL; +		for (i = 0; i < ARRAY_SIZE(mclk_clk_table); i++) { +			if (rate == mclk_clk_table[i].mrate) { +				pll_rate = mclk_clk_table[i].prate; +				clk_mclk_clk.rate = mclk_clk_table[i].mrate; +				ret = 0; +				break; +			} +		} + +		if (ret) +			return ret; + +		if (clk_mclk_clk.rate) +			clk_bclk32_clk.rate = clk_mclk_clk.rate +				/ (((divstatus & 0x0000f000) >> 12) + 1); + +		/* set pll sys cfg reg. */ +		writel(pll_rate, PM_PLLSYSCFG); + +		writel(PM_PMCR_CFBSYS, PM_PMCR); +		while ((readl(PM_PLLDFCDONE) & PM_PLLDFCDONE_SYSDFC) +				!= PM_PLLDFCDONE_SYSDFC) +			udelay(100); +			/* about 1ms */ +	} +#endif +	return 0; +} +EXPORT_SYMBOL(clk_set_rate); + +int clk_register(struct clk *clk) +{ +	mutex_lock(&clocks_mutex); +	list_add(&clk->node, &clocks); +	mutex_unlock(&clocks_mutex); +	printk(KERN_DEFAULT "PKUnity PM: %s %lu.%02luM\n", clk->name, +		(clk->rate)/1000000, (clk->rate)/10000 % 100); +	return 0; +} +EXPORT_SYMBOL(clk_register); + +void clk_unregister(struct clk *clk) +{ +	mutex_lock(&clocks_mutex); +	list_del(&clk->node); +	mutex_unlock(&clocks_mutex); +} +EXPORT_SYMBOL(clk_unregister); + +struct { +	unsigned long prate; +	unsigned long rate; +} pllrate_table[] = { +	{.prate = 0x00002001, .rate = 250000000}, +	{.prate = 0x00104801, .rate = 250000000}, +	{.prate = 0x00104C01, .rate = 262500000}, +	{.prate = 0x00002401, .rate = 275000000}, +	{.prate = 0x00105001, .rate = 275000000}, +	{.prate = 0x00105401, .rate = 287500000}, +	{.prate = 0x00002801, .rate = 300000000}, +	{.prate = 0x00105801, .rate = 300000000}, +	{.prate = 0x00105C01, .rate = 312500000}, +	{.prate = 0x00002C01, .rate = 325000000}, +	{.prate = 0x00106001, .rate = 325000000}, +	{.prate = 0x00106401, .rate = 337500000}, +	{.prate = 0x00003001, .rate = 350000000}, +	{.prate = 0x00106801, .rate = 350000000}, +	{.prate = 0x00106C01, .rate = 362500000}, +	{.prate = 0x00003401, .rate = 375000000}, +	{.prate = 0x00107001, .rate = 375000000}, +	{.prate = 0x00107401, .rate = 387500000}, +	{.prate = 0x00003801, .rate = 400000000}, +	{.prate = 0x00107801, .rate = 400000000}, +	{.prate = 0x00107C01, .rate = 412500000}, +	{.prate = 0x00003C01, .rate = 425000000}, +	{.prate = 0x00108001, .rate = 425000000}, +	{.prate = 0x00108401, .rate = 437500000}, +	{.prate = 0x00004001, .rate = 450000000}, +	{.prate = 0x00108801, .rate = 450000000}, +	{.prate = 0x00108C01, .rate = 462500000}, +	{.prate = 0x00004401, .rate = 475000000}, +	{.prate = 0x00109001, .rate = 475000000}, +	{.prate = 0x00109401, .rate = 487500000}, +	{.prate = 0x00004801, .rate = 500000000}, +	{.prate = 0x00109801, .rate = 500000000}, +	{.prate = 0x00104C00, .rate = 525000000}, +	{.prate = 0x00002400, .rate = 550000000}, +	{.prate = 0x00105000, .rate = 550000000}, +	{.prate = 0x00105400, .rate = 575000000}, +	{.prate = 0x00002800, .rate = 600000000}, +	{.prate = 0x00105800, .rate = 600000000}, +	{.prate = 0x00105C00, .rate = 625000000}, +	{.prate = 0x00002C00, .rate = 650000000}, +	{.prate = 0x00106000, .rate = 650000000}, +	{.prate = 0x00106400, .rate = 675000000}, +	{.prate = 0x00003000, .rate = 700000000}, +	{.prate = 0x00106800, .rate = 700000000}, +	{.prate = 0x00106C00, .rate = 725000000}, +	{.prate = 0x00003400, .rate = 750000000}, +	{.prate = 0x00107000, .rate = 750000000}, +	{.prate = 0x00107400, .rate = 775000000}, +	{.prate = 0x00003800, .rate = 800000000}, +	{.prate = 0x00107800, .rate = 800000000}, +	{.prate = 0x00107C00, .rate = 825000000}, +	{.prate = 0x00003C00, .rate = 850000000}, +	{.prate = 0x00108000, .rate = 850000000}, +	{.prate = 0x00108400, .rate = 875000000}, +	{.prate = 0x00004000, .rate = 900000000}, +	{.prate = 0x00108800, .rate = 900000000}, +	{.prate = 0x00108C00, .rate = 925000000}, +	{.prate = 0x00004400, .rate = 950000000}, +	{.prate = 0x00109000, .rate = 950000000}, +	{.prate = 0x00109400, .rate = 975000000}, +	{.prate = 0x00004800, .rate = 1000000000}, +	{.prate = 0x00109800, .rate = 1000000000}, +}; + +struct { +	unsigned long prate; +	unsigned long drate; +} pddr_table[] = { +	{.prate = 0x00100800, .drate = 44236800}, +	{.prate = 0x00100C00, .drate = 66355200}, +	{.prate = 0x00101000, .drate = 88473600}, +	{.prate = 0x00101400, .drate = 110592000}, +	{.prate = 0x00101800, .drate = 132710400}, +	{.prate = 0x00101C01, .drate = 154828800}, +	{.prate = 0x00102001, .drate = 176947200}, +	{.prate = 0x00102401, .drate = 199065600}, +	{.prate = 0x00102801, .drate = 221184000}, +	{.prate = 0x00102C01, .drate = 243302400}, +	{.prate = 0x00103001, .drate = 265420800}, +	{.prate = 0x00103401, .drate = 287539200}, +	{.prate = 0x00103801, .drate = 309657600}, +	{.prate = 0x00103C01, .drate = 331776000}, +	{.prate = 0x00104001, .drate = 353894400}, +}; + +static int __init clk_init(void) +{ +#ifdef CONFIG_PUV3_PM +	u32 pllrate, divstatus = readl(PM_DIVSTATUS); +	u32 pcgr_val = readl(PM_PCGR); +	int i; + +	pcgr_val |= PM_PCGR_BCLKMME | PM_PCGR_BCLKH264E | PM_PCGR_BCLKH264D +			| PM_PCGR_HECLK | PM_PCGR_HDCLK; +	writel(pcgr_val, PM_PCGR); + +	pllrate = readl(PM_PLLSYSSTATUS); + +	/* lookup pmclk_table */ +	clk_mclk_clk.rate = 0; +	for (i = 0; i < ARRAY_SIZE(pllrate_table); i++) { +		if (pllrate == pllrate_table[i].prate) { +			clk_mclk_clk.rate = pllrate_table[i].rate; +			break; +		} +	} + +	if (clk_mclk_clk.rate) +		clk_bclk32_clk.rate = clk_mclk_clk.rate / +			(((divstatus & 0x0000f000) >> 12) + 1); + +	pllrate = readl(PM_PLLDDRSTATUS); + +	/* lookup pddr_table */ +	clk_ddr_clk.rate = 0; +	for (i = 0; i < ARRAY_SIZE(pddr_table); i++) { +		if (pllrate == pddr_table[i].prate) { +			clk_ddr_clk.rate = pddr_table[i].drate; +			break; +		} +	} + +	pllrate = readl(PM_PLLVGASTATUS); + +	/* lookup pvga_table */ +	clk_vga_clk.rate = 0; +	for (i = 0; i < ARRAY_SIZE(pllrate_table); i++) { +		if (pllrate == pllrate_table[i].prate) { +			clk_vga_clk.rate = pllrate_table[i].rate; +			break; +		} +	} + +	if (clk_vga_clk.rate) +		clk_vga_clk.rate = clk_vga_clk.rate / +			(((divstatus & 0x00f00000) >> 20) + 1); + +	clk_register(&clk_vga_clk); +#endif +#ifdef CONFIG_ARCH_FPGA +	clk_ddr_clk.rate = 33000000; +	clk_mclk_clk.rate = 33000000; +	clk_bclk32_clk.rate = 33000000; +#endif +	clk_register(&clk_ddr_clk); +	clk_register(&clk_mclk_clk); +	clk_register(&clk_bclk32_clk); +	clk_register(&clk_ost_clk); +	return 0; +} +core_initcall(clk_init); diff --git a/arch/unicore32/kernel/debug-macro.S b/arch/unicore32/kernel/debug-macro.S new file mode 100644 index 00000000000..2711d6d87d8 --- /dev/null +++ b/arch/unicore32/kernel/debug-macro.S @@ -0,0 +1,89 @@ +/* + * linux/arch/unicore32/kernel/debug-macro.S + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * 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. + * + * Debugging macro include header + */ +#include <generated/asm-offsets.h> +#include <mach/hardware.h> + +		.macro	put_word_ocd, rd, rx=r16 +1001:		movc		\rx, p1.c0, #0 +		cand.a	\rx, #2 +		bne	1001b +		movc		p1.c1, \rd, #1 +		.endm + +#ifdef CONFIG_DEBUG_OCD +		/* debug using UniCore On-Chip-Debugger */ +		.macro	addruart, rx +		.endm + +		.macro	senduart, rd, rx +		put_word_ocd	\rd, \rx +		.endm + +		.macro	busyuart, rd, rx +		.endm + +		.macro	waituart, rd, rx +		.endm +#else +#define UART_CLK_DEFAULT        3686400 * 20 +	/* Uartclk = MCLK/ 2, The MCLK on my board is 3686400 * 40  */ +#define BAUD_RATE_DEFAULT	115200 +	/* The baud rate of the serial port */ + +#define UART_DIVISOR_DEFAULT	(UART_CLK_DEFAULT \ +				/ (16 * BAUD_RATE_DEFAULT) - 1) + +		.macro	addruart,rx +		mrc	p0, #0, \rx, c1, c0 +		tst	\rx, #1			@ MMU enabled? +		moveq	\rx, #0xee000000	@ physical base address +		movne	\rx, #0x6e000000	@ virtual address + +		@ We probe for the active serial port here +		@ However, now we assume UART0 is active:	epip4d +		@ We assume r1 and r2 can be clobbered. + +		movl 	r2, #UART_DIVISOR_DEFAULT +		mov 	r1, #0x80 +		str	r1, [\rx, #UART_LCR_OFFSET] +		and	r1, r2, #0xff00 +		mov	r1, r1, lsr #8 +		str	r1, [\rx, #UART_DLH_OFFSET] +		and	r1, r2, #0xff +		str	r1, [\rx, #UART_DLL_OFFSET] +		mov 	r1, #0x7 +		str	r1, [\rx, #UART_FCR_OFFSET] +		mov 	r1, #0x3 +		str	r1, [\rx, #UART_LCR_OFFSET] +		mov 	r1, #0x0 +		str	r1, [\rx, #UART_IER_OFFSET] +		.endm + +		.macro	senduart,rd,rx +		str	\rd, [\rx, #UART_THR_OFFSET] +		.endm + +		.macro	waituart,rd,rx +1001:		ldr	\rd, [\rx, #UART_LSR_OFFSET] +		tst	\rd, #UART_LSR_THRE +		beq	1001b +		.endm + +		.macro	busyuart,rd,rx +1001:		ldr	\rd, [\rx, #UART_LSR_OFFSET] +		tst	\rd, #UART_LSR_TEMT +		bne	1001b +		.endm +#endif + diff --git a/arch/unicore32/kernel/debug.S b/arch/unicore32/kernel/debug.S new file mode 100644 index 00000000000..029fd12f6ab --- /dev/null +++ b/arch/unicore32/kernel/debug.S @@ -0,0 +1,85 @@ +/* + * linux/arch/unicore32/kernel/debug.S + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * 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. + * + *  32-bit debugging code + */ +#include <linux/linkage.h> +#include <asm/assembler.h> + +		.text + +/* + * Some debugging routines (useful if you've got MM problems and + * printk isn't working).  For DEBUGGING ONLY!!!  Do not leave + * references to these in a production kernel! + */ +#include "debug-macro.S" + +/* + * Useful debugging routines + */ +ENTRY(printhex8) +		mov	r1, #8 +		b	printhex +ENDPROC(printhex8) + +ENTRY(printhex4) +		mov	r1, #4 +		b	printhex +ENDPROC(printhex4) + +ENTRY(printhex2) +		mov	r1, #2 +printhex:	adr	r2, hexbuf +		add	r3, r2, r1 +		mov	r1, #0 +		stb	r1, [r3] +1:		and	r1, r0, #15 +		mov	r0, r0 >> #4 +		csub.a	r1, #10 +		beg	2f +		add	r1, r1, #'0' - 'a' + 10 +2:		add	r1, r1, #'a' - 10 +		stb.w	r1, [r3+], #-1 +		cxor.a	r3, r2 +		bne	1b +		mov	r0, r2 +		b	printascii +ENDPROC(printhex2) + +		.ltorg + +ENTRY(printascii) +		addruart r3 +		b	2f +1:		waituart r2, r3 +		senduart r1, r3 +		busyuart r2, r3 +		cxor.a	r1, #'\n' +		cmoveq	r1, #'\r' +		beq	1b +2:		cxor.a	r0, #0 +		beq	3f +		ldb.w	r1, [r0]+, #1 +		cxor.a	r1, #0 +		bne	1b +3:		mov	pc, lr +ENDPROC(printascii) + +ENTRY(printch) +		addruart r3 +		mov	r1, r0 +		mov	r0, #0 +		b	1b +ENDPROC(printch) + +hexbuf:		.space 16 + diff --git a/arch/unicore32/kernel/dma.c b/arch/unicore32/kernel/dma.c new file mode 100644 index 00000000000..ed2d4d78d9c --- /dev/null +++ b/arch/unicore32/kernel/dma.c @@ -0,0 +1,182 @@ +/* + * linux/arch/unicore32/kernel/dma.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + *	Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn> + *	Copyright (C) 2001-2010 Guan Xuetao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/errno.h> +#include <linux/io.h> + +#include <asm/irq.h> +#include <mach/hardware.h> +#include <mach/dma.h> + +struct dma_channel { +	char *name; +	puv3_dma_prio prio; +	void (*irq_handler)(int, void *); +	void (*err_handler)(int, void *); +	void *data; +}; + +static struct dma_channel dma_channels[MAX_DMA_CHANNELS]; + +int puv3_request_dma(char *name, puv3_dma_prio prio, +			 void (*irq_handler)(int, void *), +			 void (*err_handler)(int, void *), +			 void *data) +{ +	unsigned long flags; +	int i, found = 0; + +	/* basic sanity checks */ +	if (!name) +		return -EINVAL; + +	local_irq_save(flags); + +	do { +		/* try grabbing a DMA channel with the requested priority */ +		for (i = 0; i < MAX_DMA_CHANNELS; i++) { +			if ((dma_channels[i].prio == prio) && +			    !dma_channels[i].name) { +				found = 1; +				break; +			} +		} +		/* if requested prio group is full, try a hier priority */ +	} while (!found && prio--); + +	if (found) { +		dma_channels[i].name = name; +		dma_channels[i].irq_handler = irq_handler; +		dma_channels[i].err_handler = err_handler; +		dma_channels[i].data = data; +	} else { +		printk(KERN_WARNING "No more available DMA channels for %s\n", +				name); +		i = -ENODEV; +	} + +	local_irq_restore(flags); +	return i; +} +EXPORT_SYMBOL(puv3_request_dma); + +void puv3_free_dma(int dma_ch) +{ +	unsigned long flags; + +	if (!dma_channels[dma_ch].name) { +		printk(KERN_CRIT +			"%s: trying to free channel %d which is already freed\n", +			__func__, dma_ch); +		return; +	} + +	local_irq_save(flags); +	dma_channels[dma_ch].name = NULL; +	dma_channels[dma_ch].err_handler = NULL; +	local_irq_restore(flags); +} +EXPORT_SYMBOL(puv3_free_dma); + +static irqreturn_t dma_irq_handler(int irq, void *dev_id) +{ +	int i, dint; + +	dint = readl(DMAC_ITCSR); +	for (i = 0; i < MAX_DMA_CHANNELS; i++) { +		if (dint & DMAC_CHANNEL(i)) { +			struct dma_channel *channel = &dma_channels[i]; + +			/* Clear TC interrupt of channel i */ +			writel(DMAC_CHANNEL(i), DMAC_ITCCR); +			writel(0, DMAC_ITCCR); + +			if (channel->name && channel->irq_handler) { +				channel->irq_handler(i, channel->data); +			} else { +				/* +				 * IRQ for an unregistered DMA channel: +				 * let's clear the interrupts and disable it. +				 */ +				printk(KERN_WARNING "spurious IRQ for" +						" DMA channel %d\n", i); +			} +		} +	} +	return IRQ_HANDLED; +} + +static irqreturn_t dma_err_handler(int irq, void *dev_id) +{ +	int i, dint; + +	dint = readl(DMAC_IESR); +	for (i = 0; i < MAX_DMA_CHANNELS; i++) { +		if (dint & DMAC_CHANNEL(i)) { +			struct dma_channel *channel = &dma_channels[i]; + +			/* Clear Err interrupt of channel i */ +			writel(DMAC_CHANNEL(i), DMAC_IECR); +			writel(0, DMAC_IECR); + +			if (channel->name && channel->err_handler) { +				channel->err_handler(i, channel->data); +			} else { +				/* +				 * IRQ for an unregistered DMA channel: +				 * let's clear the interrupts and disable it. +				 */ +				printk(KERN_WARNING "spurious IRQ for" +						" DMA channel %d\n", i); +			} +		} +	} +	return IRQ_HANDLED; +} + +int __init puv3_init_dma(void) +{ +	int i, ret; + +	/* dma channel priorities on v8 processors: +	 * ch 0 - 1  <--> (0) DMA_PRIO_HIGH +	 * ch 2 - 3  <--> (1) DMA_PRIO_MEDIUM +	 * ch 4 - 5  <--> (2) DMA_PRIO_LOW +	 */ +	for (i = 0; i < MAX_DMA_CHANNELS; i++) { +		puv3_stop_dma(i); +		dma_channels[i].name = NULL; +		dma_channels[i].prio = min((i & 0x7) >> 1, DMA_PRIO_LOW); +	} + +	ret = request_irq(IRQ_DMA, dma_irq_handler, 0, "DMA", NULL); +	if (ret) { +		printk(KERN_CRIT "Can't register IRQ for DMA\n"); +		return ret; +	} + +	ret = request_irq(IRQ_DMAERR, dma_err_handler, 0, "DMAERR", NULL); +	if (ret) { +		printk(KERN_CRIT "Can't register IRQ for DMAERR\n"); +		free_irq(IRQ_DMA, "DMA"); +		return ret; +	} + +	return 0; +} + +postcore_initcall(puv3_init_dma); diff --git a/arch/unicore32/kernel/early_printk.c b/arch/unicore32/kernel/early_printk.c new file mode 100644 index 00000000000..f2f6323c8d6 --- /dev/null +++ b/arch/unicore32/kernel/early_printk.c @@ -0,0 +1,49 @@ +/* + * linux/arch/unicore32/kernel/early_printk.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/console.h> +#include <linux/init.h> +#include <linux/string.h> +#include <mach/ocd.h> + +/* On-Chip-Debugger functions */ + +static void early_ocd_write(struct console *con, const char *s, unsigned n) +{ +	while (*s && n-- > 0) { +		if (*s == '\n') +			ocd_putc((int)'\r'); +		ocd_putc((int)*s); +		s++; +	} +} + +static struct console early_ocd_console = { +	.name =		"earlyocd", +	.write =	early_ocd_write, +	.flags =	CON_PRINTBUFFER, +	.index =	-1, +}; + +static int __init setup_early_printk(char *buf) +{ +	if (!buf || early_console) +		return 0; + +	early_console = &early_ocd_console; +	if (strstr(buf, "keep")) +		early_console->flags &= ~CON_BOOT; +	else +		early_console->flags |= CON_BOOT; +	register_console(early_console); +	return 0; +} +early_param("earlyprintk", setup_early_printk); diff --git a/arch/unicore32/kernel/elf.c b/arch/unicore32/kernel/elf.c new file mode 100644 index 00000000000..0a176734fef --- /dev/null +++ b/arch/unicore32/kernel/elf.c @@ -0,0 +1,38 @@ +/* + * linux/arch/unicore32/kernel/elf.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/personality.h> +#include <linux/binfmts.h> +#include <linux/elf.h> + +int elf_check_arch(const struct elf32_hdr *x) +{ +	/* Make sure it's an UniCore executable */ +	if (x->e_machine != EM_UNICORE) +		return 0; + +	/* Make sure the entry address is reasonable */ +	if (x->e_entry & 3) +		return 0; + +	return 1; +} +EXPORT_SYMBOL(elf_check_arch); + +void elf_set_personality(const struct elf32_hdr *x) +{ +	unsigned int personality = PER_LINUX; + +	set_personality(personality); +} +EXPORT_SYMBOL(elf_set_personality); diff --git a/arch/unicore32/kernel/entry.S b/arch/unicore32/kernel/entry.S new file mode 100644 index 00000000000..bcdedd80890 --- /dev/null +++ b/arch/unicore32/kernel/entry.S @@ -0,0 +1,805 @@ +/* + * linux/arch/unicore32/kernel/entry.S + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + *  Low-level vector interface routines + */ +#include <linux/init.h> +#include <linux/linkage.h> +#include <asm/assembler.h> +#include <asm/errno.h> +#include <asm/thread_info.h> +#include <asm/memory.h> +#include <asm/unistd.h> +#include <generated/asm-offsets.h> +#include "debug-macro.S" + +@ +@ Most of the stack format comes from struct pt_regs, but with +@ the addition of 8 bytes for storing syscall args 5 and 6. +@ +#define S_OFF		8 + +/* + * The SWI code relies on the fact that R0 is at the bottom of the stack + * (due to slow/fast restore user regs). + */ +#if S_R0 != 0 +#error "Please fix" +#endif + +	.macro	zero_fp +#ifdef CONFIG_FRAME_POINTER +	mov	fp, #0 +#endif +	.endm + +	.macro	alignment_trap, rtemp +#ifdef CONFIG_ALIGNMENT_TRAP +	ldw	\rtemp, .LCcralign +	ldw	\rtemp, [\rtemp] +	movc	p0.c1, \rtemp, #0 +#endif +	.endm + +	.macro	load_user_sp_lr, rd, rtemp, offset = 0 +	mov	\rtemp, asr +	xor	\rtemp, \rtemp, #(PRIV_MODE ^ SUSR_MODE) +	mov.a	asr, \rtemp			@ switch to the SUSR mode + +	ldw	sp, [\rd+], #\offset		@ load sp_user +	ldw	lr, [\rd+], #\offset + 4	@ load lr_user + +	xor	\rtemp, \rtemp, #(PRIV_MODE ^ SUSR_MODE) +	mov.a	asr, \rtemp			@ switch back to the PRIV mode +	.endm + +	.macro	priv_exit, rpsr +	mov.a	bsr, \rpsr +	ldm.w	(r0 - r15), [sp]+ +	ldm.b	(r16 - pc), [sp]+		@ load r0 - pc, asr +	.endm + +	.macro	restore_user_regs, fast = 0, offset = 0 +	ldw	r1, [sp+], #\offset + S_PSR	@ get calling asr +	ldw	lr, [sp+], #\offset + S_PC	@ get pc +	mov.a	bsr, r1				@ save in bsr_priv +	.if	\fast +	add	sp, sp, #\offset + S_R1		@ r0 is syscall return value +	ldm.w	(r1 - r15), [sp]+		@ get calling r1 - r15 +	ldur	(r16 - lr), [sp]+		@ get calling r16 - lr +	.else +	ldm.w	(r0 - r15), [sp]+		@ get calling r0 - r15 +	ldur	(r16 - lr), [sp]+		@ get calling r16 - lr +	.endif +	nop +	add	sp, sp, #S_FRAME_SIZE - S_R16 +	mov.a	pc, lr				@ return +						@ and move bsr_priv into asr +	.endm + +	.macro	get_thread_info, rd +	mov	\rd, sp >> #13 +	mov	\rd, \rd << #13 +	.endm + +	.macro	get_irqnr_and_base, irqnr, irqstat, base, tmp +	ldw	\base, =(PKUNITY_INTC_BASE) +	ldw	\irqstat, [\base+], #0xC	@ INTC_ICIP +	ldw	\tmp,	  [\base+], #0x4	@ INTC_ICMR +	and.a	\irqstat, \irqstat, \tmp +	beq	1001f +	cntlz	\irqnr, \irqstat +	rsub	\irqnr, \irqnr, #31 +1001:	/* EQ will be set if no irqs pending */ +	.endm + +#ifdef CONFIG_DEBUG_LL +	.macro	printreg, reg, temp +		adr	\temp, 901f +		stm	(r0-r3), [\temp]+ +		stw	lr, [\temp+], #0x10 +		mov	r0, \reg +		b.l	printhex8 +		mov	r0, #':' +		b.l	printch +		mov	r0, pc +		b.l	printhex8 +		adr	r0, 902f +		b.l	printascii +		adr	\temp, 901f +		ldm	(r0-r3), [\temp]+ +		ldw	lr, [\temp+], #0x10 +		b	903f +901:	.word	0, 0, 0, 0, 0	@ r0-r3, lr +902:	.asciz	": epip4d\n" +	.align +903: +	.endm +#endif + +/* + * These are the registers used in the syscall handler, and allow us to + * have in theory up to 7 arguments to a function - r0 to r6. + * + * Note that tbl == why is intentional. + * + * We must set at least "tsk" and "why" when calling ret_with_reschedule. + */ +scno	.req	r21		@ syscall number +tbl	.req	r22		@ syscall table pointer +why	.req	r22		@ Linux syscall (!= 0) +tsk	.req	r23		@ current thread_info + +/* + * Interrupt handling.  Preserves r17, r18, r19 + */ +	.macro	intr_handler +1:	get_irqnr_and_base r0, r6, r5, lr +	beq	2f +	mov	r1, sp +	@ +	@ routine called with r0 = irq number, r1 = struct pt_regs * +	@ +	adr	lr, 1b +	b	asm_do_IRQ +2: +	.endm + +/* + * PRIV mode handlers + */ +	.macro	priv_entry +	sub	sp, sp, #(S_FRAME_SIZE - 4) +	stm	(r1 - r15), [sp]+ +	add	r5, sp, #S_R15 +	stm	(r16 - r28), [r5]+ + +	ldm	(r1 - r3), [r0]+ +	add	r5, sp, #S_SP - 4	@ here for interlock avoidance +	mov	r4, #-1			@  ""  ""      ""       "" +	add	r0, sp, #(S_FRAME_SIZE - 4) +	stw.w	r1, [sp+], #-4		@ save the "real" r0 copied +					@ from the exception stack + +	mov	r1, lr + +	@ +	@ We are now ready to fill in the remaining blanks on the stack: +	@ +	@  r0 - sp_priv +	@  r1 - lr_priv +	@  r2 - lr_<exception>, already fixed up for correct return/restart +	@  r3 - bsr_<exception> +	@  r4 - orig_r0 (see pt_regs definition in ptrace.h) +	@ +	stm	(r0 - r4), [r5]+ +	.endm + +/* + * User mode handlers + * + */ +	.macro	user_entry +	sub	sp, sp, #S_FRAME_SIZE +	stm	(r1 - r15), [sp+] +	add	r4, sp, #S_R16 +	stm	(r16 - r28), [r4]+ + +	ldm	(r1 - r3), [r0]+ +	add	r0, sp, #S_PC		@ here for interlock avoidance +	mov	r4, #-1			@  ""  ""     ""        "" + +	stw	r1, [sp]		@ save the "real" r0 copied +					@ from the exception stack + +	@ +	@ We are now ready to fill in the remaining blanks on the stack: +	@ +	@  r2 - lr_<exception>, already fixed up for correct return/restart +	@  r3 - bsr_<exception> +	@  r4 - orig_r0 (see pt_regs definition in ptrace.h) +	@ +	@ Also, separately save sp_user and lr_user +	@ +	stm	(r2 - r4), [r0]+ +	stur	(sp, lr), [r0-] + +	@ +	@ Enable the alignment trap while in kernel mode +	@ +	alignment_trap r0 + +	@ +	@ Clear FP to mark the first stack frame +	@ +	zero_fp +	.endm + +	.text + +@ +@ __invalid - generic code for failed exception +@			(re-entrant version of handlers) +@ +__invalid: +	sub	sp, sp, #S_FRAME_SIZE +	stm	(r1 - r15), [sp+] +	add	r1, sp, #S_R16 +	stm	(r16 - r28, sp, lr), [r1]+ + +	zero_fp + +	ldm	(r4 - r6), [r0]+ +	add	r0, sp, #S_PC		@ here for interlock avoidance +	mov	r7, #-1			@  ""   ""    ""        "" +	stw	r4, [sp]		@ save preserved r0 +	stm	(r5 - r7), [r0]+	@ lr_<exception>, +					@ asr_<exception>, "old_r0" + +	mov	r0, sp +	mov	r1, asr +	b	bad_mode +ENDPROC(__invalid) + +	.align	5 +__dabt_priv: +	priv_entry + +	@ +	@ get ready to re-enable interrupts if appropriate +	@ +	mov	r17, asr +	cand.a	r3, #PSR_I_BIT +	bne	1f +	andn	r17, r17, #PSR_I_BIT +1: + +	@ +	@ Call the processor-specific abort handler: +	@ +	@  r2 - aborted context pc +	@  r3 - aborted context asr +	@ +	@ The abort handler must return the aborted address in r0, and +	@ the fault status register in r1. +	@ +	movc	r1, p0.c3, #0		@ get FSR +	movc	r0, p0.c4, #0		@ get FAR + +	@ +	@ set desired INTR state, then call main handler +	@ +	mov.a	asr, r17 +	mov	r2, sp +	b.l	do_DataAbort + +	@ +	@ INTRs off again before pulling preserved data off the stack +	@ +	disable_irq r0 + +	@ +	@ restore BSR and restart the instruction +	@ +	ldw	r2, [sp+], #S_PSR +	priv_exit r2				@ return from exception +ENDPROC(__dabt_priv) + +	.align	5 +__intr_priv: +	priv_entry + +	intr_handler + +	mov	r0, #0				@ epip4d +	movc	p0.c5, r0, #14 +	nop; nop; nop; nop; nop; nop; nop; nop + +	ldw	r4, [sp+], #S_PSR		@ irqs are already disabled + +	priv_exit r4				@ return from exception +ENDPROC(__intr_priv) + +	.ltorg + +	.align	5 +__extn_priv: +	priv_entry + +	mov	r0, sp				@ struct pt_regs *regs +	mov	r1, asr +	b	bad_mode			@ not supported +ENDPROC(__extn_priv) + +	.align	5 +__pabt_priv: +	priv_entry + +	@ +	@ re-enable interrupts if appropriate +	@ +	mov	r17, asr +	cand.a	r3, #PSR_I_BIT +	bne	1f +	andn	r17, r17, #PSR_I_BIT +1: + +	@ +	@ set args, then call main handler +	@ +	@  r0 - address of faulting instruction +	@  r1 - pointer to registers on stack +	@ +	mov	r0, r2			@ pass address of aborted instruction +	mov	r1, #5 +	mov.a	asr, r17 +	mov	r2, sp			@ regs +	b.l	do_PrefetchAbort	@ call abort handler + +	@ +	@ INTRs off again before pulling preserved data off the stack +	@ +	disable_irq r0 + +	@ +	@ restore BSR and restart the instruction +	@ +	ldw	r2, [sp+], #S_PSR +	priv_exit r2			@ return from exception +ENDPROC(__pabt_priv) + +	.align	5 +.LCcralign: +	.word	cr_alignment + +	.align	5 +__dabt_user: +	user_entry + +#ifdef CONFIG_UNICORE_FPU_F64 +	cff	ip, s31 +	cand.a	ip, #0x08000000		@ FPU execption traps? +	beq	209f + +	ldw	ip, [sp+], #S_PC +	add	ip, ip, #4 +	stw	ip, [sp+], #S_PC +	@ +	@ fall through to the emulation code, which returns using r19 if +	@ it has emulated the instruction, or the more conventional lr +	@ if we are to treat this as a real extended instruction +	@ +	@  r0 - instruction +	@ +1:	ldw.u	r0, [r2] +	adr	r19, ret_from_exception +	adr	lr, 209f +	@ +	@ fallthrough to call do_uc_f64 +	@ +/* + * Check whether the instruction is a co-processor instruction. + * If yes, we need to call the relevant co-processor handler. + * + * Note that we don't do a full check here for the co-processor + * instructions; all instructions with bit 27 set are well + * defined.  The only instructions that should fault are the + * co-processor instructions. + * + * Emulators may wish to make use of the following registers: + *  r0  = instruction opcode. + *  r2  = PC + *  r19 = normal "successful" return address + *  r20 = this threads thread_info structure. + *  lr  = unrecognised instruction return address + */ +	get_thread_info r20			@ get current thread +	and	r8, r0, #0x00003c00		@ mask out CP number +	mov	r7, #1 +	stb	r7, [r20+], #TI_USED_CP + 2	@ set appropriate used_cp[] + +	@ F64 hardware support entry point. +	@  r0  = faulted instruction +	@  r19 = return address +	@  r20 = fp_state +	enable_irq r4 +	add	r20, r20, #TI_FPSTATE	@ r20 = workspace +	cff	r1, s31			@ get fpu FPSCR +	andn    r2, r1, #0x08000000 +	ctf     r2, s31			@ clear 27 bit +	mov	r2, sp			@ nothing stacked - regdump is at TOS +	mov	lr, r19			@ setup for a return to the user code + +	@ Now call the C code to package up the bounce to the support code +	@   r0 holds the trigger instruction +	@   r1 holds the FPSCR value +	@   r2 pointer to register dump +	b	ucf64_exchandler +209: +#endif +	@ +	@ Call the processor-specific abort handler: +	@ +	@  r2 - aborted context pc +	@  r3 - aborted context asr +	@ +	@ The abort handler must return the aborted address in r0, and +	@ the fault status register in r1. +	@ +	movc	r1, p0.c3, #0		@ get FSR +	movc	r0, p0.c4, #0		@ get FAR + +	@ +	@ INTRs on, then call the main handler +	@ +	enable_irq r2 +	mov	r2, sp +	adr	lr, ret_from_exception +	b	do_DataAbort +ENDPROC(__dabt_user) + +	.align	5 +__intr_user: +	user_entry + +	get_thread_info tsk + +	intr_handler + +	mov	why, #0 +	b	ret_to_user +ENDPROC(__intr_user) + +	.ltorg + +	.align	5 +__extn_user: +	user_entry + +	mov	r0, sp +	mov	r1, asr +	b	bad_mode +ENDPROC(__extn_user) + +	.align	5 +__pabt_user: +	user_entry + +	mov	r0, r2			@ pass address of aborted instruction. +	mov	r1, #5 +	enable_irq r1			@ Enable interrupts +	mov	r2, sp			@ regs +	b.l	do_PrefetchAbort	@ call abort handler +	/* fall through */ +/* + * This is the return code to user mode for abort handlers + */ +ENTRY(ret_from_exception) +	get_thread_info tsk +	mov	why, #0 +	b	ret_to_user +ENDPROC(__pabt_user) +ENDPROC(ret_from_exception) + +/* + * Register switch for UniCore V2 processors + * r0 = previous task_struct, r1 = previous thread_info, r2 = next thread_info + * previous and next are guaranteed not to be the same. + */ +ENTRY(__switch_to) +	add	ip, r1, #TI_CPU_SAVE +	stm.w	(r4 - r15), [ip]+ +	stm.w	(r16 - r27, sp, lr), [ip]+ + +#ifdef	CONFIG_UNICORE_FPU_F64 +	add	ip, r1, #TI_FPSTATE +	sfm.w	(f0  - f7 ), [ip]+ +	sfm.w	(f8  - f15), [ip]+ +	sfm.w	(f16 - f23), [ip]+ +	sfm.w	(f24 - f31), [ip]+ +	cff	r4, s31 +	stw	r4, [ip] + +	add	ip, r2, #TI_FPSTATE +	lfm.w	(f0  - f7 ), [ip]+ +	lfm.w	(f8  - f15), [ip]+ +	lfm.w	(f16 - f23), [ip]+ +	lfm.w	(f24 - f31), [ip]+ +	ldw	r4, [ip] +	ctf	r4, s31 +#endif +	add	ip, r2, #TI_CPU_SAVE +	ldm.w	(r4 - r15), [ip]+ +	ldm	(r16 - r27, sp, pc), [ip]+	@ Load all regs saved previously +ENDPROC(__switch_to) + +	.align	5 +/* + * This is the fast syscall return path.  We do as little as + * possible here, and this includes saving r0 back into the PRIV + * stack. + */ +ret_fast_syscall: +	disable_irq r1				@ disable interrupts +	ldw	r1, [tsk+], #TI_FLAGS +	cand.a	r1, #_TIF_WORK_MASK +	bne	fast_work_pending + +	@ fast_restore_user_regs +	restore_user_regs fast = 1, offset = S_OFF + +/* + * Ok, we need to do extra processing, enter the slow path. + */ +fast_work_pending: +	stw.w	r0, [sp+], #S_R0+S_OFF		@ returned r0 +work_pending: +	cand.a	r1, #_TIF_NEED_RESCHED +	bne	work_resched +	mov	r0, sp				@ 'regs' +	mov	r2, why				@ 'syscall' +	cand.a	r1, #_TIF_SIGPENDING		@ delivering a signal? +	cmovne	why, #0				@ prevent further restarts +	b.l	do_notify_resume +	b	ret_slow_syscall		@ Check work again + +work_resched: +	b.l	schedule +/* + * "slow" syscall return path.  "why" tells us if this was a real syscall. + */ +ENTRY(ret_to_user) +ret_slow_syscall: +	disable_irq r1				@ disable interrupts +	get_thread_info tsk			@ epip4d, one path error?! +	ldw	r1, [tsk+], #TI_FLAGS +	cand.a	r1, #_TIF_WORK_MASK +	bne	work_pending +no_work_pending: +	@ slow_restore_user_regs +	restore_user_regs fast = 0, offset = 0 +ENDPROC(ret_to_user) + +/* + * This is how we return from a fork. + */ +ENTRY(ret_from_fork) +	b.l	schedule_tail +	b	ret_slow_syscall +ENDPROC(ret_from_fork) + +ENTRY(ret_from_kernel_thread) +	b.l	schedule_tail +	mov	r0, r5 +	adr	lr, ret_slow_syscall +	mov	pc, r4 +ENDPROC(ret_from_kernel_thread) + +/*============================================================================= + * SWI handler + *----------------------------------------------------------------------------- + */ +	.align	5 +ENTRY(vector_swi) +	sub	sp, sp, #S_FRAME_SIZE +	stm	(r0 - r15), [sp]+		@ Calling r0 - r15 +	add	r8, sp, #S_R16 +	stm	(r16 - r28), [r8]+		@ Calling r16 - r28 +	add	r8, sp, #S_PC +	stur	(sp, lr), [r8-]			@ Calling sp, lr +	mov	r8, bsr				@ called from non-REAL mode +	stw	lr, [sp+], #S_PC		@ Save calling PC +	stw	r8, [sp+], #S_PSR		@ Save ASR +	stw	r0, [sp+], #S_OLD_R0		@ Save OLD_R0 +	zero_fp + +	/* +	 * Get the system call number. +	 */ +	sub	ip, lr, #4 +	ldw.u	scno, [ip]			@ get SWI instruction + +#ifdef CONFIG_ALIGNMENT_TRAP +	ldw	ip, __cr_alignment +	ldw	ip, [ip] +	movc	p0.c1, ip, #0                   @ update control register +#endif +	enable_irq ip + +	get_thread_info tsk +	ldw	tbl, =sys_call_table		@ load syscall table pointer + +	andn	scno, scno, #0xff000000		@ mask off SWI op-code +	andn	scno, scno, #0x00ff0000		@ mask off SWI op-code + +	stm.w	(r4, r5), [sp-]			@ push fifth and sixth args +	ldw	ip, [tsk+], #TI_FLAGS		@ check for syscall tracing +	cand.a	ip, #_TIF_SYSCALL_TRACE		@ are we tracing syscalls? +	bne	__sys_trace + +	csub.a	scno, #__NR_syscalls		@ check upper syscall limit +	adr	lr, ret_fast_syscall		@ return address +	bea	1f +	ldw	pc, [tbl+], scno << #2		@ call sys_* routine +1: +	add	r1, sp, #S_OFF +2:	mov	why, #0				@ no longer a real syscall +	b	sys_ni_syscall			@ not private func + +	/* +	 * This is the really slow path.  We're going to be doing +	 * context switches, and waiting for our parent to respond. +	 */ +__sys_trace: +	mov	r2, scno +	add	r1, sp, #S_OFF +	mov	r0, #0				@ trace entry [IP = 0] +	b.l	syscall_trace + +	adr	lr, __sys_trace_return		@ return address +	mov	scno, r0			@ syscall number (possibly new) +	add	r1, sp, #S_R0 + S_OFF		@ pointer to regs +	csub.a	scno, #__NR_syscalls		@ check upper syscall limit +	bea	2b +	ldm	(r0 - r3), [r1]+		@ have to reload r0 - r3 +	ldw	pc, [tbl+], scno << #2		@ call sys_* routine + +__sys_trace_return: +	stw.w	r0, [sp+], #S_R0 + S_OFF	@ save returned r0 +	mov	r2, scno +	mov	r1, sp +	mov	r0, #1				@ trace exit [IP = 1] +	b.l	syscall_trace +	b	ret_slow_syscall + +	.align	5 +#ifdef CONFIG_ALIGNMENT_TRAP +	.type	__cr_alignment, #object +__cr_alignment: +	.word	cr_alignment +#endif +	.ltorg + +ENTRY(sys_rt_sigreturn) +		add	r0, sp, #S_OFF +		mov	why, #0		@ prevent syscall restart handling +		b	__sys_rt_sigreturn +ENDPROC(sys_rt_sigreturn) + +	__INIT + +/* + * Vector stubs. + * + * This code is copied to 0xffff0200 so we can use branches in the + * vectors, rather than ldr's.  Note that this code must not + * exceed 0x300 bytes. + * + * Common stub entry macro: + *   Enter in INTR mode, bsr = PRIV/USER ASR, lr = PRIV/USER PC + * + * SP points to a minimal amount of processor-private memory, the address + * of which is copied into r0 for the mode specific abort handler. + */ +	.macro	vector_stub, name, mode +	.align	5 + +vector_\name: +	@ +	@ Save r0, lr_<exception> (parent PC) and bsr_<exception> +	@ (parent ASR) +	@ +	stw	r0, [sp] +	stw	lr, [sp+], #4		@ save r0, lr +	mov	lr, bsr +	stw	lr, [sp+], #8		@ save bsr + +	@ +	@ Prepare for PRIV mode.  INTRs remain disabled. +	@ +	mov	r0, asr +	xor	r0, r0, #(\mode ^ PRIV_MODE) +	mov.a	bsr, r0 + +	@ +	@ the branch table must immediately follow this code +	@ +	and	lr, lr, #0x03 +	add	lr, lr, #1 +	mov	r0, sp +	ldw	lr, [pc+], lr << #2 +	mov.a	pc, lr			@ branch to handler in PRIV mode +ENDPROC(vector_\name) +	.align	2 +	@ handler addresses follow this label +	.endm + +	.globl	__stubs_start +__stubs_start: +/* + * Interrupt dispatcher + */ +	vector_stub	intr, INTR_MODE + +	.long	__intr_user			@  0  (USER) +	.long	__invalid			@  1 +	.long	__invalid			@  2 +	.long	__intr_priv			@  3  (PRIV) + +/* + * Data abort dispatcher + * Enter in ABT mode, bsr = USER ASR, lr = USER PC + */ +	vector_stub	dabt, ABRT_MODE + +	.long	__dabt_user			@  0  (USER) +	.long	__invalid			@  1 +	.long	__invalid			@  2  (INTR) +	.long	__dabt_priv			@  3  (PRIV) + +/* + * Prefetch abort dispatcher + * Enter in ABT mode, bsr = USER ASR, lr = USER PC + */ +	vector_stub	pabt, ABRT_MODE + +	.long	__pabt_user			@  0 (USER) +	.long	__invalid			@  1 +	.long	__invalid			@  2 (INTR) +	.long	__pabt_priv			@  3 (PRIV) + +/* + * Undef instr entry dispatcher + * Enter in EXTN mode, bsr = PRIV/USER ASR, lr = PRIV/USER PC + */ +	vector_stub	extn, EXTN_MODE + +	.long	__extn_user			@  0 (USER) +	.long	__invalid			@  1 +	.long	__invalid			@  2 (INTR) +	.long	__extn_priv			@  3 (PRIV) + +/* + * We group all the following data together to optimise + * for CPUs with separate I & D caches. + */ +	.align	5 + +.LCvswi: +	.word	vector_swi + +	.globl	__stubs_end +__stubs_end: + +	.equ	stubs_offset, __vectors_start + 0x200 - __stubs_start + +	.globl	__vectors_start +__vectors_start: +	jepriv	SYS_ERROR0 +	b	vector_extn + stubs_offset +	ldw	pc, .LCvswi + stubs_offset +	b	vector_pabt + stubs_offset +	b	vector_dabt + stubs_offset +	jepriv	SYS_ERROR0 +	b	vector_intr + stubs_offset +	jepriv	SYS_ERROR0 + +	.globl	__vectors_end +__vectors_end: + +	.data + +	.globl	cr_alignment +	.globl	cr_no_alignment +cr_alignment: +	.space	4 +cr_no_alignment: +	.space	4 diff --git a/arch/unicore32/kernel/fpu-ucf64.c b/arch/unicore32/kernel/fpu-ucf64.c new file mode 100644 index 00000000000..282a60ac82b --- /dev/null +++ b/arch/unicore32/kernel/fpu-ucf64.c @@ -0,0 +1,126 @@ +/* + * linux/arch/unicore32/kernel/fpu-ucf64.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/init.h> + +#include <asm/fpu-ucf64.h> + +/* + * A special flag to tell the normalisation code not to normalise. + */ +#define F64_NAN_FLAG	0x100 + +/* + * A bit pattern used to indicate the initial (unset) value of the + * exception mask, in case nothing handles an instruction.  This + * doesn't include the NAN flag, which get masked out before + * we check for an error. + */ +#define F64_EXCEPTION_ERROR	((u32)-1 & ~F64_NAN_FLAG) + +/* + * Since we aren't building with -mfpu=f64, we need to code + * these instructions using their MRC/MCR equivalents. + */ +#define f64reg(_f64_) #_f64_ + +#define cff(_f64_) ({			\ +	u32 __v;			\ +	asm("cff %0, " f64reg(_f64_) "@ fmrx	%0, " #_f64_	\ +	    : "=r" (__v) : : "cc");	\ +	__v;				\ +	}) + +#define ctf(_f64_, _var_)		\ +	asm("ctf %0, " f64reg(_f64_) "@ fmxr	" #_f64_ ", %0"	\ +	   : : "r" (_var_) : "cc") + +/* + * Raise a SIGFPE for the current process. + * sicode describes the signal being raised. + */ +void ucf64_raise_sigfpe(unsigned int sicode, struct pt_regs *regs) +{ +	siginfo_t info; + +	memset(&info, 0, sizeof(info)); + +	info.si_signo = SIGFPE; +	info.si_code = sicode; +	info.si_addr = (void __user *)(instruction_pointer(regs) - 4); + +	/* +	 * This is the same as NWFPE, because it's not clear what +	 * this is used for +	 */ +	current->thread.error_code = 0; +	current->thread.trap_no = 6; + +	send_sig_info(SIGFPE, &info, current); +} + +/* + * Handle exceptions of UniCore-F64. + */ +void ucf64_exchandler(u32 inst, u32 fpexc, struct pt_regs *regs) +{ +	u32 tmp = fpexc; +	u32 exc = F64_EXCEPTION_ERROR & fpexc; + +	pr_debug("UniCore-F64: instruction %08x fpscr %08x\n", +			inst, fpexc); + +	if (exc & FPSCR_CMPINSTR_BIT) { +		if (exc & FPSCR_CON) +			tmp |= FPSCR_CON; +		else +			tmp &= ~(FPSCR_CON); +		exc &= ~(FPSCR_CMPINSTR_BIT | FPSCR_CON); +	} else { +		pr_debug(KERN_ERR "UniCore-F64 Error: unhandled exceptions\n"); +		pr_debug(KERN_ERR "UniCore-F64 FPSCR 0x%08x INST 0x%08x\n", +				cff(FPSCR), inst); + +		ucf64_raise_sigfpe(0, regs); +		return; +	} + +	/* +	 * Update the FPSCR with the additional exception flags. +	 * Comparison instructions always return at least one of +	 * these flags set. +	 */ +	tmp &= ~(FPSCR_TRAP | FPSCR_IOS | FPSCR_OFS | FPSCR_UFS | +			FPSCR_IXS | FPSCR_HIS | FPSCR_IOC | FPSCR_OFC | +			FPSCR_UFC | FPSCR_IXC | FPSCR_HIC); + +	tmp |= exc; +	ctf(FPSCR, tmp); +} + +/* + * F64 support code initialisation. + */ +static int __init ucf64_init(void) +{ +	ctf(FPSCR, 0x0);     /* FPSCR_UFE | FPSCR_NDE perhaps better */ + +	printk(KERN_INFO "Enable UniCore-F64 support.\n"); + +	return 0; +} + +late_initcall(ucf64_init); diff --git a/arch/unicore32/kernel/gpio.c b/arch/unicore32/kernel/gpio.c new file mode 100644 index 00000000000..cb12ec39552 --- /dev/null +++ b/arch/unicore32/kernel/gpio.c @@ -0,0 +1,122 @@ +/* + * linux/arch/unicore32/kernel/gpio.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + *	Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn> + *	Copyright (C) 2001-2010 Guan Xuetao + * + * 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. + */ +/* in FPGA, no GPIO support */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/gpio.h> +#include <mach/hardware.h> + +#ifdef CONFIG_LEDS +#include <linux/leds.h> +#include <linux/platform_device.h> + +static const struct gpio_led puv3_gpio_leds[] = { +	{ .name = "cpuhealth", .gpio = GPO_CPU_HEALTH, .active_low = 0, +		.default_trigger = "heartbeat",	}, +	{ .name = "hdd_led", .gpio = GPO_HDD_LED, .active_low = 1, +		.default_trigger = "ide-disk", }, +}; + +static const struct gpio_led_platform_data puv3_gpio_led_data = { +	.num_leds =	ARRAY_SIZE(puv3_gpio_leds), +	.leds =		(void *) puv3_gpio_leds, +}; + +static struct platform_device puv3_gpio_gpio_leds = { +	.name =		"leds-gpio", +	.id =		-1, +	.dev = { +		.platform_data = (void *) &puv3_gpio_led_data, +	} +}; + +static int __init puv3_gpio_leds_init(void) +{ +	platform_device_register(&puv3_gpio_gpio_leds); +	return 0; +} + +device_initcall(puv3_gpio_leds_init); +#endif + +static int puv3_gpio_get(struct gpio_chip *chip, unsigned offset) +{ +	return readl(GPIO_GPLR) & GPIO_GPIO(offset); +} + +static void puv3_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ +	if (value) +		writel(GPIO_GPIO(offset), GPIO_GPSR); +	else +		writel(GPIO_GPIO(offset), GPIO_GPCR); +} + +static int puv3_direction_input(struct gpio_chip *chip, unsigned offset) +{ +	unsigned long flags; + +	local_irq_save(flags); +	writel(readl(GPIO_GPDR) & ~GPIO_GPIO(offset), GPIO_GPDR); +	local_irq_restore(flags); +	return 0; +} + +static int puv3_direction_output(struct gpio_chip *chip, unsigned offset, +		int value) +{ +	unsigned long flags; + +	local_irq_save(flags); +	puv3_gpio_set(chip, offset, value); +	writel(readl(GPIO_GPDR) | GPIO_GPIO(offset), GPIO_GPDR); +	local_irq_restore(flags); +	return 0; +} + +static struct gpio_chip puv3_gpio_chip = { +	.label			= "gpio", +	.direction_input	= puv3_direction_input, +	.direction_output	= puv3_direction_output, +	.set			= puv3_gpio_set, +	.get			= puv3_gpio_get, +	.base			= 0, +	.ngpio			= GPIO_MAX + 1, +}; + +void __init puv3_init_gpio(void) +{ +	writel(GPIO_DIR, GPIO_GPDR); +#if	defined(CONFIG_PUV3_NB0916) || defined(CONFIG_PUV3_SMW0919)	\ +	|| defined(CONFIG_PUV3_DB0913) +	gpio_set_value(GPO_WIFI_EN, 1); +	gpio_set_value(GPO_HDD_LED, 1); +	gpio_set_value(GPO_VGA_EN, 1); +	gpio_set_value(GPO_LCD_EN, 1); +	gpio_set_value(GPO_CAM_PWR_EN, 0); +	gpio_set_value(GPO_LCD_VCC_EN, 1); +	gpio_set_value(GPO_SOFT_OFF, 1); +	gpio_set_value(GPO_BT_EN, 1); +	gpio_set_value(GPO_FAN_ON, 0); +	gpio_set_value(GPO_SPKR, 0); +	gpio_set_value(GPO_CPU_HEALTH, 1); +	gpio_set_value(GPO_LAN_SEL, 1); +/* + * DO NOT modify the GPO_SET_V1 and GPO_SET_V2 in kernel + *	gpio_set_value(GPO_SET_V1, 1); + *	gpio_set_value(GPO_SET_V2, 1); + */ +#endif +	gpiochip_add(&puv3_gpio_chip); +} diff --git a/arch/unicore32/kernel/head.S b/arch/unicore32/kernel/head.S new file mode 100644 index 00000000000..e8f0b98c02e --- /dev/null +++ b/arch/unicore32/kernel/head.S @@ -0,0 +1,252 @@ +/* + * linux/arch/unicore32/kernel/head.S + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/linkage.h> +#include <linux/init.h> + +#include <asm/assembler.h> +#include <asm/ptrace.h> +#include <generated/asm-offsets.h> +#include <asm/memory.h> +#include <asm/thread_info.h> +#include <asm/hwdef-copro.h> +#include <asm/pgtable-hwdef.h> + +#if (PHYS_OFFSET & 0x003fffff) +#error "PHYS_OFFSET must be at an even 4MiB boundary!" +#endif + +#define KERNEL_RAM_VADDR	(PAGE_OFFSET + KERNEL_IMAGE_START) +#define KERNEL_RAM_PADDR	(PHYS_OFFSET + KERNEL_IMAGE_START) + +#define KERNEL_PGD_PADDR	(KERNEL_RAM_PADDR - 0x1000) +#define KERNEL_PGD_VADDR	(KERNEL_RAM_VADDR - 0x1000) + +#define KERNEL_START		KERNEL_RAM_VADDR +#define KERNEL_END		_end + +/* + * swapper_pg_dir is the virtual address of the initial page table. + * We place the page tables 4K below KERNEL_RAM_VADDR.  Therefore, we must + * make sure that KERNEL_RAM_VADDR is correctly set.  Currently, we expect + * the least significant 16 bits to be 0x8000, but we could probably + * relax this restriction to KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x1000. + */ +#if (KERNEL_RAM_VADDR & 0xffff) != 0x8000 +#error KERNEL_RAM_VADDR must start at 0xXXXX8000 +#endif + +	.globl	swapper_pg_dir +	.equ	swapper_pg_dir, KERNEL_RAM_VADDR - 0x1000 + +/* + * Kernel startup entry point. + * --------------------------- + * + * This is normally called from the decompressor code.  The requirements + * are: MMU = off, D-cache = off, I-cache = dont care + * + * This code is mostly position independent, so if you link the kernel at + * 0xc0008000, you call this at __pa(0xc0008000). + */ +	__HEAD +ENTRY(stext) +	@ set asr +	mov	r0, #PRIV_MODE			@ ensure priv mode +	or	r0, #PSR_R_BIT | PSR_I_BIT	@ disable irqs +	mov.a	asr, r0 + +	@ process identify +	movc	r0, p0.c0, #0			@ cpuid +	movl	r1, 0xff00ffff			@ mask +	movl	r2, 0x4d000863			@ value +	and	r0, r1, r0 +	cxor.a	r0, r2 +	bne	__error_p			@ invalid processor id + +	/* +	 * Clear the 4K level 1 swapper page table +	 */ +	movl	r0, #KERNEL_PGD_PADDR		@ page table address +	mov	r1, #0 +	add	r2, r0, #0x1000 +101:	stw.w	r1, [r0]+, #4 +	stw.w	r1, [r0]+, #4 +	stw.w	r1, [r0]+, #4 +	stw.w	r1, [r0]+, #4 +	cxor.a	r0, r2 +	bne	101b + +	movl	r4, #KERNEL_PGD_PADDR		@ page table address +	mov	r7, #PMD_TYPE_SECT | PMD_PRESENT	@ page size: section +	or	r7, r7, #PMD_SECT_CACHEABLE		@ cacheable +	or	r7, r7, #PMD_SECT_READ | PMD_SECT_WRITE | PMD_SECT_EXEC + +	/* +	 * Create identity mapping for first 4MB of kernel to +	 * cater for the MMU enable.  This identity mapping +	 * will be removed by paging_init().  We use our current program +	 * counter to determine corresponding section base address. +	 */ +	mov	r6, pc +	mov	r6, r6 >> #22			@ start of kernel section +	or	r1, r7, r6 << #22		@ flags + kernel base +	stw	r1, [r4+], r6 << #2		@ identity mapping + +	/* +	 * Now setup the pagetables for our kernel direct +	 * mapped region. +	 */ +	add	r0, r4,  #(KERNEL_START & 0xff000000) >> 20 +	stw.w	r1, [r0+], #(KERNEL_START & 0x00c00000) >> 20 +	movl	r6, #(KERNEL_END - 1) +	add	r0, r0, #4 +	add	r6, r4, r6 >> #20 +102:	csub.a	r0, r6 +	add	r1, r1, #1 << 22 +	bua	103f +	stw.w	r1, [r0]+, #4 +	b	102b +103: +	/* +	 * Then map first 4MB of ram in case it contains our boot params. +	 */ +	add	r0, r4, #PAGE_OFFSET >> 20 +	or	r6, r7, #(PHYS_OFFSET & 0xffc00000) +	stw	r6, [r0] + +	ldw	r15, __switch_data		@ address to jump to after + +	/* +	 * Initialise TLB, Caches, and MMU state ready to switch the MMU +	 * on. +	 */ +	mov	r0, #0 +	movc	p0.c5, r0, #28			@ cache invalidate all +	nop8 +	movc	p0.c6, r0, #6			@ TLB invalidate all +	nop8 + +	/* +	 * ..V. .... ..TB IDAM +	 * ..1. .... ..01 1111 +	 */ +	movl	r0, #0x201f			@ control register setting + +	/* +	 * Setup common bits before finally enabling the MMU.  Essentially +	 * this is just loading the page table pointer and domain access +	 * registers. +	 */ +	#ifndef CONFIG_ALIGNMENT_TRAP +		andn	r0, r0, #CR_A +	#endif +	#ifdef CONFIG_CPU_DCACHE_DISABLE +		andn	r0, r0, #CR_D +	#endif +	#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH +		andn	r0, r0, #CR_B +	#endif +	#ifdef CONFIG_CPU_ICACHE_DISABLE +		andn	r0, r0, #CR_I +	#endif + +	movc	p0.c2, r4, #0			@ set pgd +	b	__turn_mmu_on +ENDPROC(stext) + +/* + * Enable the MMU.  This completely changes the structure of the visible + * memory space.  You will not be able to trace execution through this. + * + *  r0  = cp#0 control register + *  r15 = *virtual* address to jump to upon completion + */ +	.align	5 +__turn_mmu_on: +	mov	r0, r0 +	movc	p0.c1, r0, #0			@ write control reg +	nop					@ fetch inst by phys addr +	mov	pc, r15 +	nop8					@ fetch inst by phys addr +ENDPROC(__turn_mmu_on) + +/* + * Setup the initial page tables.  We only setup the barest + * amount which are required to get the kernel running, which + * generally means mapping in the kernel code. + * + * r9  = cpuid + * r10 = procinfo + * + * Returns: + *  r0, r3, r6, r7 corrupted + *  r4 = physical page table address + */ +	.ltorg + +	.align	2 +	.type	__switch_data, %object +__switch_data: +	.long	__mmap_switched +	.long	__bss_start			@ r6 +	.long	_end				@ r7 +	.long	cr_alignment			@ r8 +	.long	init_thread_union + THREAD_START_SP @ sp + +/* + * The following fragment of code is executed with the MMU on in MMU mode, + * and uses absolute addresses; this is not position independent. + * + *  r0  = cp#0 control register + */ +__mmap_switched: +	adr	r3, __switch_data + 4 + +	ldm.w	(r6, r7, r8), [r3]+ +	ldw	sp, [r3] + +	mov	fp, #0				@ Clear BSS (and zero fp) +203:	csub.a	r6, r7 +	bea	204f +	stw.w	fp, [r6]+,#4 +	b	203b +204: +	andn	r1, r0, #CR_A			@ Clear 'A' bit +	stm	(r0, r1), [r8]+			@ Save control register values +	b	start_kernel +ENDPROC(__mmap_switched) + +/* + * Exception handling.  Something went wrong and we can't proceed.  We + * ought to tell the user, but since we don't have any guarantee that + * we're even running on the right architecture, we do virtually nothing. + * + * If CONFIG_DEBUG_LL is set we try to print out something about the error + * and hope for the best (useful if bootloader fails to pass a proper + * machine ID for example). + */ +__error_p: +#ifdef CONFIG_DEBUG_LL +	adr	r0, str_p1 +	b.l	printascii +	mov	r0, r9 +	b.l	printhex8 +	adr	r0, str_p2 +	b.l	printascii +901:	nop8 +	b	901b +str_p1:	.asciz	"\nError: unrecognized processor variant (0x" +str_p2:	.asciz	").\n" +	.align +#endif +ENDPROC(__error_p) + diff --git a/arch/unicore32/kernel/hibernate.c b/arch/unicore32/kernel/hibernate.c new file mode 100644 index 00000000000..d75ef8b6cb5 --- /dev/null +++ b/arch/unicore32/kernel/hibernate.c @@ -0,0 +1,159 @@ +/* + *  linux/arch/unicore32/kernel/hibernate.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + *	Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn> + *	Copyright (C) 2001-2010 Guan Xuetao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/gfp.h> +#include <linux/suspend.h> +#include <linux/bootmem.h> + +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/pgalloc.h> +#include <asm/suspend.h> + +#include "mach/pm.h" + +/* Pointer to the temporary resume page tables */ +pgd_t *resume_pg_dir; + +struct swsusp_arch_regs swsusp_arch_regs_cpu0; + +/* + * Create a middle page table on a resume-safe page and put a pointer to it in + * the given global directory entry.  This only returns the gd entry + * in non-PAE compilation mode, since the middle layer is folded. + */ +static pmd_t *resume_one_md_table_init(pgd_t *pgd) +{ +	pud_t *pud; +	pmd_t *pmd_table; + +	pud = pud_offset(pgd, 0); +	pmd_table = pmd_offset(pud, 0); + +	return pmd_table; +} + +/* + * Create a page table on a resume-safe page and place a pointer to it in + * a middle page directory entry. + */ +static pte_t *resume_one_page_table_init(pmd_t *pmd) +{ +	if (pmd_none(*pmd)) { +		pte_t *page_table = (pte_t *)get_safe_page(GFP_ATOMIC); +		if (!page_table) +			return NULL; + +		set_pmd(pmd, __pmd(__pa(page_table) | _PAGE_KERNEL_TABLE)); + +		BUG_ON(page_table != pte_offset_kernel(pmd, 0)); + +		return page_table; +	} + +	return pte_offset_kernel(pmd, 0); +} + +/* + * This maps the physical memory to kernel virtual address space, a total + * of max_low_pfn pages, by creating page tables starting from address + * PAGE_OFFSET.  The page tables are allocated out of resume-safe pages. + */ +static int resume_physical_mapping_init(pgd_t *pgd_base) +{ +	unsigned long pfn; +	pgd_t *pgd; +	pmd_t *pmd; +	pte_t *pte; +	int pgd_idx, pmd_idx; + +	pgd_idx = pgd_index(PAGE_OFFSET); +	pgd = pgd_base + pgd_idx; +	pfn = 0; + +	for (; pgd_idx < PTRS_PER_PGD; pgd++, pgd_idx++) { +		pmd = resume_one_md_table_init(pgd); +		if (!pmd) +			return -ENOMEM; + +		if (pfn >= max_low_pfn) +			continue; + +		for (pmd_idx = 0; pmd_idx < PTRS_PER_PMD; pmd++, pmd_idx++) { +			pte_t *max_pte; + +			if (pfn >= max_low_pfn) +				break; + +			/* Map with normal page tables. +			 * NOTE: We can mark everything as executable here +			 */ +			pte = resume_one_page_table_init(pmd); +			if (!pte) +				return -ENOMEM; + +			max_pte = pte + PTRS_PER_PTE; +			for (; pte < max_pte; pte++, pfn++) { +				if (pfn >= max_low_pfn) +					break; + +				set_pte(pte, pfn_pte(pfn, PAGE_KERNEL_EXEC)); +			} +		} +	} + +	return 0; +} + +static inline void resume_init_first_level_page_table(pgd_t *pg_dir) +{ +} + +int swsusp_arch_resume(void) +{ +	int error; + +	resume_pg_dir = (pgd_t *)get_safe_page(GFP_ATOMIC); +	if (!resume_pg_dir) +		return -ENOMEM; + +	resume_init_first_level_page_table(resume_pg_dir); +	error = resume_physical_mapping_init(resume_pg_dir); +	if (error) +		return error; + +	/* We have got enough memory and from now on we cannot recover */ +	restore_image(resume_pg_dir, restore_pblist); +	return 0; +} + +/* + *	pfn_is_nosave - check if given pfn is in the 'nosave' section + */ + +int pfn_is_nosave(unsigned long pfn) +{ +	unsigned long begin_pfn = __pa(&__nosave_begin) >> PAGE_SHIFT; +	unsigned long end_pfn = PAGE_ALIGN(__pa(&__nosave_end)) >> PAGE_SHIFT; + +	return (pfn >= begin_pfn) && (pfn < end_pfn); +} + +void save_processor_state(void) +{ +} + +void restore_processor_state(void) +{ +	local_flush_tlb_all(); +} diff --git a/arch/unicore32/kernel/hibernate_asm.S b/arch/unicore32/kernel/hibernate_asm.S new file mode 100644 index 00000000000..cc3c65253c8 --- /dev/null +++ b/arch/unicore32/kernel/hibernate_asm.S @@ -0,0 +1,117 @@ +/* + * linux/arch/unicore32/kernel/hibernate_asm.S + * + * Code specific to PKUnity SoC and UniCore ISA + * + *	Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn> + *	Copyright (C) 2001-2010 Guan Xuetao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/sys.h> +#include <linux/errno.h> +#include <linux/linkage.h> +#include <generated/asm-offsets.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/assembler.h> + +@ restore_image(pgd_t *resume_pg_dir, struct pbe *restore_pblist) +@ r0: resume_pg_dir +@ r1: restore_pblist +@ copy restore_pblist pages +@ restore registers from swsusp_arch_regs_cpu0 +@ +ENTRY(restore_image) +	sub	r0, r0, #PAGE_OFFSET +	mov	r5, #0 +	movc	p0.c6, r5, #6	@invalidate ITLB & DTLB +	movc	p0.c2, r0, #0 +	nop +	nop +	nop +	nop +	nop +	nop +	nop + +	.p2align 4,,7 +101: +	csub.a	r1, #0 +	beq	109f + +	ldw	r6, [r1+], #PBE_ADDRESS +	ldw	r7, [r1+], #PBE_ORIN_ADDRESS + +	movl	ip, #128 +102:	ldm.w	(r8 - r15), [r6]+ +	stm.w	(r8 - r15), [r7]+ +	sub.a	ip, ip, #1 +	bne	102b + +	ldw	r1, [r1+], #PBE_NEXT +	b	101b + +	.p2align 4,,7 +109: +	/* go back to the original page tables */ +	ldw	r0, =swapper_pg_dir +	sub	r0, r0, #PAGE_OFFSET +	mov	r5, #0 +	movc	p0.c6, r5, #6 +	movc	p0.c2, r0, #0 +	nop +	nop +	nop +	nop +	nop +	nop +	nop + +#ifdef	CONFIG_UNICORE_FPU_F64 +	ldw	ip, 1f +	add	ip, ip, #SWSUSP_FPSTATE +	lfm.w	(f0  - f7 ), [ip]+ +	lfm.w	(f8  - f15), [ip]+ +	lfm.w	(f16 - f23), [ip]+ +	lfm.w	(f24 - f31), [ip]+ +	ldw	r4, [ip] +	ctf	r4, s31 +#endif +	mov	r0, #0x0 +	ldw	ip, 1f +	add	ip, ip, #SWSUSP_CPU +	ldm.w	(r4 - r15), [ip]+ +	ldm	(r16 - r27, sp, pc), [ip]+	@ Load all regs saved previously + +	.align	2 +1:	.long	swsusp_arch_regs_cpu0 + + +@ swsusp_arch_suspend() +@ - prepare pc for resume, return from function without swsusp_save on resume +@ - save registers in swsusp_arch_regs_cpu0 +@ - call swsusp_save write suspend image + +ENTRY(swsusp_arch_suspend) +	ldw	ip, 1f +	add	ip, ip, #SWSUSP_CPU +	stm.w	(r4 - r15), [ip]+ +	stm.w	(r16 - r27, sp, lr), [ip]+ + +#ifdef	CONFIG_UNICORE_FPU_F64 +	ldw	ip, 1f +	add	ip, ip, #SWSUSP_FPSTATE +	sfm.w	(f0  - f7 ), [ip]+ +	sfm.w	(f8  - f15), [ip]+ +	sfm.w	(f16 - f23), [ip]+ +	sfm.w	(f24 - f31), [ip]+ +	cff	r4, s31 +	stw	r4, [ip] +#endif +	b	swsusp_save			@ no return + +1:	.long	swsusp_arch_regs_cpu0 diff --git a/arch/unicore32/kernel/irq.c b/arch/unicore32/kernel/irq.c new file mode 100644 index 00000000000..0be5ccd7ccd --- /dev/null +++ b/arch/unicore32/kernel/irq.c @@ -0,0 +1,376 @@ +/* + * linux/arch/unicore32/kernel/irq.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/kernel_stat.h> +#include <linux/module.h> +#include <linux/signal.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/random.h> +#include <linux/smp.h> +#include <linux/init.h> +#include <linux/seq_file.h> +#include <linux/errno.h> +#include <linux/list.h> +#include <linux/kallsyms.h> +#include <linux/proc_fs.h> +#include <linux/syscore_ops.h> +#include <linux/gpio.h> + +#include <mach/hardware.h> + +#include "setup.h" + +/* + * PKUnity GPIO edge detection for IRQs: + * IRQs are generated on Falling-Edge, Rising-Edge, or both. + * Use this instead of directly setting GRER/GFER. + */ +static int GPIO_IRQ_rising_edge; +static int GPIO_IRQ_falling_edge; +static int GPIO_IRQ_mask = 0; + +#define GPIO_MASK(irq)		(1 << (irq - IRQ_GPIO0)) + +static int puv3_gpio_type(struct irq_data *d, unsigned int type) +{ +	unsigned int mask; + +	if (d->irq < IRQ_GPIOHIGH) +		mask = 1 << d->irq; +	else +		mask = GPIO_MASK(d->irq); + +	if (type == IRQ_TYPE_PROBE) { +		if ((GPIO_IRQ_rising_edge | GPIO_IRQ_falling_edge) & mask) +			return 0; +		type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING; +	} + +	if (type & IRQ_TYPE_EDGE_RISING) +		GPIO_IRQ_rising_edge |= mask; +	else +		GPIO_IRQ_rising_edge &= ~mask; +	if (type & IRQ_TYPE_EDGE_FALLING) +		GPIO_IRQ_falling_edge |= mask; +	else +		GPIO_IRQ_falling_edge &= ~mask; + +	writel(GPIO_IRQ_rising_edge & GPIO_IRQ_mask, GPIO_GRER); +	writel(GPIO_IRQ_falling_edge & GPIO_IRQ_mask, GPIO_GFER); + +	return 0; +} + +/* + * GPIO IRQs must be acknowledged.  This is for IRQs from 0 to 7. + */ +static void puv3_low_gpio_ack(struct irq_data *d) +{ +	writel((1 << d->irq), GPIO_GEDR); +} + +static void puv3_low_gpio_mask(struct irq_data *d) +{ +	writel(readl(INTC_ICMR) & ~(1 << d->irq), INTC_ICMR); +} + +static void puv3_low_gpio_unmask(struct irq_data *d) +{ +	writel(readl(INTC_ICMR) | (1 << d->irq), INTC_ICMR); +} + +static int puv3_low_gpio_wake(struct irq_data *d, unsigned int on) +{ +	if (on) +		writel(readl(PM_PWER) | (1 << d->irq), PM_PWER); +	else +		writel(readl(PM_PWER) & ~(1 << d->irq), PM_PWER); +	return 0; +} + +static struct irq_chip puv3_low_gpio_chip = { +	.name		= "GPIO-low", +	.irq_ack	= puv3_low_gpio_ack, +	.irq_mask	= puv3_low_gpio_mask, +	.irq_unmask	= puv3_low_gpio_unmask, +	.irq_set_type	= puv3_gpio_type, +	.irq_set_wake	= puv3_low_gpio_wake, +}; + +/* + * IRQ8 (GPIO0 through 27) handler.  We enter here with the + * irq_controller_lock held, and IRQs disabled.  Decode the IRQ + * and call the handler. + */ +static void +puv3_gpio_handler(unsigned int irq, struct irq_desc *desc) +{ +	unsigned int mask; + +	mask = readl(GPIO_GEDR); +	do { +		/* +		 * clear down all currently active IRQ sources. +		 * We will be processing them all. +		 */ +		writel(mask, GPIO_GEDR); + +		irq = IRQ_GPIO0; +		do { +			if (mask & 1) +				generic_handle_irq(irq); +			mask >>= 1; +			irq++; +		} while (mask); +		mask = readl(GPIO_GEDR); +	} while (mask); +} + +/* + * GPIO0-27 edge IRQs need to be handled specially. + * In addition, the IRQs are all collected up into one bit in the + * interrupt controller registers. + */ +static void puv3_high_gpio_ack(struct irq_data *d) +{ +	unsigned int mask = GPIO_MASK(d->irq); + +	writel(mask, GPIO_GEDR); +} + +static void puv3_high_gpio_mask(struct irq_data *d) +{ +	unsigned int mask = GPIO_MASK(d->irq); + +	GPIO_IRQ_mask &= ~mask; + +	writel(readl(GPIO_GRER) & ~mask, GPIO_GRER); +	writel(readl(GPIO_GFER) & ~mask, GPIO_GFER); +} + +static void puv3_high_gpio_unmask(struct irq_data *d) +{ +	unsigned int mask = GPIO_MASK(d->irq); + +	GPIO_IRQ_mask |= mask; + +	writel(GPIO_IRQ_rising_edge & GPIO_IRQ_mask, GPIO_GRER); +	writel(GPIO_IRQ_falling_edge & GPIO_IRQ_mask, GPIO_GFER); +} + +static int puv3_high_gpio_wake(struct irq_data *d, unsigned int on) +{ +	if (on) +		writel(readl(PM_PWER) | PM_PWER_GPIOHIGH, PM_PWER); +	else +		writel(readl(PM_PWER) & ~PM_PWER_GPIOHIGH, PM_PWER); +	return 0; +} + +static struct irq_chip puv3_high_gpio_chip = { +	.name		= "GPIO-high", +	.irq_ack	= puv3_high_gpio_ack, +	.irq_mask	= puv3_high_gpio_mask, +	.irq_unmask	= puv3_high_gpio_unmask, +	.irq_set_type	= puv3_gpio_type, +	.irq_set_wake	= puv3_high_gpio_wake, +}; + +/* + * We don't need to ACK IRQs on the PKUnity unless they're GPIOs + * this is for internal IRQs i.e. from 8 to 31. + */ +static void puv3_mask_irq(struct irq_data *d) +{ +	writel(readl(INTC_ICMR) & ~(1 << d->irq), INTC_ICMR); +} + +static void puv3_unmask_irq(struct irq_data *d) +{ +	writel(readl(INTC_ICMR) | (1 << d->irq), INTC_ICMR); +} + +/* + * Apart form GPIOs, only the RTC alarm can be a wakeup event. + */ +static int puv3_set_wake(struct irq_data *d, unsigned int on) +{ +	if (d->irq == IRQ_RTCAlarm) { +		if (on) +			writel(readl(PM_PWER) | PM_PWER_RTC, PM_PWER); +		else +			writel(readl(PM_PWER) & ~PM_PWER_RTC, PM_PWER); +		return 0; +	} +	return -EINVAL; +} + +static struct irq_chip puv3_normal_chip = { +	.name		= "PKUnity-v3", +	.irq_ack	= puv3_mask_irq, +	.irq_mask	= puv3_mask_irq, +	.irq_unmask	= puv3_unmask_irq, +	.irq_set_wake	= puv3_set_wake, +}; + +static struct resource irq_resource = { +	.name	= "irqs", +	.start	= io_v2p(PKUNITY_INTC_BASE), +	.end	= io_v2p(PKUNITY_INTC_BASE) + 0xFFFFF, +}; + +static struct puv3_irq_state { +	unsigned int	saved; +	unsigned int	icmr; +	unsigned int	iclr; +	unsigned int	iccr; +} puv3_irq_state; + +static int puv3_irq_suspend(void) +{ +	struct puv3_irq_state *st = &puv3_irq_state; + +	st->saved = 1; +	st->icmr = readl(INTC_ICMR); +	st->iclr = readl(INTC_ICLR); +	st->iccr = readl(INTC_ICCR); + +	/* +	 * Disable all GPIO-based interrupts. +	 */ +	writel(readl(INTC_ICMR) & ~(0x1ff), INTC_ICMR); + +	/* +	 * Set the appropriate edges for wakeup. +	 */ +	writel(readl(PM_PWER) & GPIO_IRQ_rising_edge, GPIO_GRER); +	writel(readl(PM_PWER) & GPIO_IRQ_falling_edge, GPIO_GFER); + +	/* +	 * Clear any pending GPIO interrupts. +	 */ +	writel(readl(GPIO_GEDR), GPIO_GEDR); + +	return 0; +} + +static void puv3_irq_resume(void) +{ +	struct puv3_irq_state *st = &puv3_irq_state; + +	if (st->saved) { +		writel(st->iccr, INTC_ICCR); +		writel(st->iclr, INTC_ICLR); + +		writel(GPIO_IRQ_rising_edge & GPIO_IRQ_mask, GPIO_GRER); +		writel(GPIO_IRQ_falling_edge & GPIO_IRQ_mask, GPIO_GFER); + +		writel(st->icmr, INTC_ICMR); +	} +} + +static struct syscore_ops puv3_irq_syscore_ops = { +	.suspend	= puv3_irq_suspend, +	.resume		= puv3_irq_resume, +}; + +static int __init puv3_irq_init_syscore(void) +{ +	register_syscore_ops(&puv3_irq_syscore_ops); +	return 0; +} + +device_initcall(puv3_irq_init_syscore); + +void __init init_IRQ(void) +{ +	unsigned int irq; + +	request_resource(&iomem_resource, &irq_resource); + +	/* disable all IRQs */ +	writel(0, INTC_ICMR); + +	/* all IRQs are IRQ, not REAL */ +	writel(0, INTC_ICLR); + +	/* clear all GPIO edge detects */ +	writel(FMASK(8, 0) & ~FIELD(1, 1, GPI_SOFF_REQ), GPIO_GPIR); +	writel(0, GPIO_GFER); +	writel(0, GPIO_GRER); +	writel(0x0FFFFFFF, GPIO_GEDR); + +	writel(1, INTC_ICCR); + +	for (irq = 0; irq < IRQ_GPIOHIGH; irq++) { +		irq_set_chip(irq, &puv3_low_gpio_chip); +		irq_set_handler(irq, handle_edge_irq); +		irq_modify_status(irq, +			IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN, +			0); +	} + +	for (irq = IRQ_GPIOHIGH + 1; irq < IRQ_GPIO0; irq++) { +		irq_set_chip(irq, &puv3_normal_chip); +		irq_set_handler(irq, handle_level_irq); +		irq_modify_status(irq, +			IRQ_NOREQUEST | IRQ_NOAUTOEN, +			IRQ_NOPROBE); +	} + +	for (irq = IRQ_GPIO0; irq <= IRQ_GPIO27; irq++) { +		irq_set_chip(irq, &puv3_high_gpio_chip); +		irq_set_handler(irq, handle_edge_irq); +		irq_modify_status(irq, +			IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN, +			0); +	} + +	/* +	 * Install handler for GPIO 0-27 edge detect interrupts +	 */ +	irq_set_chip(IRQ_GPIOHIGH, &puv3_normal_chip); +	irq_set_chained_handler(IRQ_GPIOHIGH, puv3_gpio_handler); + +#ifdef CONFIG_PUV3_GPIO +	puv3_init_gpio(); +#endif +} + +/* + * do_IRQ handles all hardware IRQ's.  Decoded IRQs should not + * come via this function.  Instead, they should provide their + * own 'handler' + */ +asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs) +{ +	struct pt_regs *old_regs = set_irq_regs(regs); + +	irq_enter(); + +	/* +	 * Some hardware gives randomly wrong interrupts.  Rather +	 * than crashing, do something sensible. +	 */ +	if (unlikely(irq >= nr_irqs)) { +		if (printk_ratelimit()) +			printk(KERN_WARNING "Bad IRQ%u\n", irq); +		ack_bad_irq(irq); +	} else { +		generic_handle_irq(irq); +	} + +	irq_exit(); +	set_irq_regs(old_regs); +} + diff --git a/arch/unicore32/kernel/ksyms.c b/arch/unicore32/kernel/ksyms.c new file mode 100644 index 00000000000..0323528a80f --- /dev/null +++ b/arch/unicore32/kernel/ksyms.c @@ -0,0 +1,61 @@ +/* + * linux/arch/unicore32/kernel/ksyms.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/cryptohash.h> +#include <linux/delay.h> +#include <linux/in6.h> +#include <linux/syscalls.h> +#include <linux/uaccess.h> +#include <linux/io.h> + +#include <asm/checksum.h> + +#include "ksyms.h" + +EXPORT_SYMBOL(find_first_bit); +EXPORT_SYMBOL(find_first_zero_bit); +EXPORT_SYMBOL(find_next_zero_bit); +EXPORT_SYMBOL(find_next_bit); + +	/* platform dependent support */ +EXPORT_SYMBOL(__udelay); +EXPORT_SYMBOL(__const_udelay); + +	/* string / mem functions */ +EXPORT_SYMBOL(strchr); +EXPORT_SYMBOL(strrchr); +EXPORT_SYMBOL(memset); +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(memmove); +EXPORT_SYMBOL(memchr); + +	/* user mem (segment) */ +EXPORT_SYMBOL(__strnlen_user); +EXPORT_SYMBOL(__strncpy_from_user); + +EXPORT_SYMBOL(copy_page); + +EXPORT_SYMBOL(__copy_from_user); +EXPORT_SYMBOL(__copy_to_user); +EXPORT_SYMBOL(__clear_user); + +EXPORT_SYMBOL(__ashldi3); +EXPORT_SYMBOL(__ashrdi3); +EXPORT_SYMBOL(__divsi3); +EXPORT_SYMBOL(__lshrdi3); +EXPORT_SYMBOL(__modsi3); +EXPORT_SYMBOL(__ucmpdi2); +EXPORT_SYMBOL(__udivsi3); +EXPORT_SYMBOL(__umodsi3); + diff --git a/arch/unicore32/kernel/ksyms.h b/arch/unicore32/kernel/ksyms.h new file mode 100644 index 00000000000..31472ad9467 --- /dev/null +++ b/arch/unicore32/kernel/ksyms.h @@ -0,0 +1,13 @@ +/* + * libgcc functions - functions that are used internally by the + * compiler...  (prototypes are not correct though, but that + * doesn't really matter since they're not versioned). + */ +extern void __ashldi3(void); +extern void __ashrdi3(void); +extern void __divsi3(void); +extern void __lshrdi3(void); +extern void __modsi3(void); +extern void __ucmpdi2(void); +extern void __udivsi3(void); +extern void __umodsi3(void); diff --git a/arch/unicore32/kernel/module.c b/arch/unicore32/kernel/module.c new file mode 100644 index 00000000000..dc41f6dfedb --- /dev/null +++ b/arch/unicore32/kernel/module.c @@ -0,0 +1,109 @@ +/* + * linux/arch/unicore32/kernel/module.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/moduleloader.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/elf.h> +#include <linux/vmalloc.h> +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/gfp.h> + +#include <asm/pgtable.h> +#include <asm/sections.h> + +void *module_alloc(unsigned long size) +{ +	return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END, +				GFP_KERNEL, PAGE_KERNEL_EXEC, NUMA_NO_NODE, +				__builtin_return_address(0)); +} + +int +apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex, +	       unsigned int relindex, struct module *module) +{ +	Elf32_Shdr *symsec = sechdrs + symindex; +	Elf32_Shdr *relsec = sechdrs + relindex; +	Elf32_Shdr *dstsec = sechdrs + relsec->sh_info; +	Elf32_Rel *rel = (void *)relsec->sh_addr; +	unsigned int i; + +	for (i = 0; i < relsec->sh_size / sizeof(Elf32_Rel); i++, rel++) { +		unsigned long loc; +		Elf32_Sym *sym; +		s32 offset; + +		offset = ELF32_R_SYM(rel->r_info); +		if (offset < 0 || offset > +				(symsec->sh_size / sizeof(Elf32_Sym))) { +			printk(KERN_ERR "%s: bad relocation, " +					"section %d reloc %d\n", +					module->name, relindex, i); +			return -ENOEXEC; +		} + +		sym = ((Elf32_Sym *)symsec->sh_addr) + offset; + +		if (rel->r_offset < 0 || rel->r_offset > +				dstsec->sh_size - sizeof(u32)) { +			printk(KERN_ERR "%s: out of bounds relocation, " +				"section %d reloc %d offset %d size %d\n", +				module->name, relindex, i, rel->r_offset, +				dstsec->sh_size); +			return -ENOEXEC; +		} + +		loc = dstsec->sh_addr + rel->r_offset; + +		switch (ELF32_R_TYPE(rel->r_info)) { +		case R_UNICORE_NONE: +			/* ignore */ +			break; + +		case R_UNICORE_ABS32: +			*(u32 *)loc += sym->st_value; +			break; + +		case R_UNICORE_PC24: +		case R_UNICORE_CALL: +		case R_UNICORE_JUMP24: +			offset = (*(u32 *)loc & 0x00ffffff) << 2; +			if (offset & 0x02000000) +				offset -= 0x04000000; + +			offset += sym->st_value - loc; +			if (offset & 3 || +			    offset <= (s32)0xfe000000 || +			    offset >= (s32)0x02000000) { +				printk(KERN_ERR +				       "%s: relocation out of range, section " +				       "%d reloc %d sym '%s'\n", module->name, +				       relindex, i, strtab + sym->st_name); +				return -ENOEXEC; +			} + +			offset >>= 2; + +			*(u32 *)loc &= 0xff000000; +			*(u32 *)loc |= offset & 0x00ffffff; +			break; + +		default: +			printk(KERN_ERR "%s: unknown relocation: %u\n", +			       module->name, ELF32_R_TYPE(rel->r_info)); +			return -ENOEXEC; +		} +	} +	return 0; +} diff --git a/arch/unicore32/kernel/pci.c b/arch/unicore32/kernel/pci.c new file mode 100644 index 00000000000..374a055a8e6 --- /dev/null +++ b/arch/unicore32/kernel/pci.c @@ -0,0 +1,393 @@ +/* + * linux/arch/unicore32/kernel/pci.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * 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. + * + *  PCI bios-type initialisation for PCI machines + * + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/io.h> + +static int debug_pci; + +#define CONFIG_CMD(bus, devfn, where)	\ +	(0x80000000 | (bus->number << 16) | (devfn << 8) | (where & ~3)) + +static int +puv3_read_config(struct pci_bus *bus, unsigned int devfn, int where, +			int size, u32 *value) +{ +	writel(CONFIG_CMD(bus, devfn, where), PCICFG_ADDR); +	switch (size) { +	case 1: +		*value = (readl(PCICFG_DATA) >> ((where & 3) * 8)) & 0xFF; +		break; +	case 2: +		*value = (readl(PCICFG_DATA) >> ((where & 2) * 8)) & 0xFFFF; +		break; +	case 4: +		*value = readl(PCICFG_DATA); +		break; +	} +	return PCIBIOS_SUCCESSFUL; +} + +static int +puv3_write_config(struct pci_bus *bus, unsigned int devfn, int where, +			int size, u32 value) +{ +	writel(CONFIG_CMD(bus, devfn, where), PCICFG_ADDR); +	switch (size) { +	case 1: +		writel((readl(PCICFG_DATA) & ~FMASK(8, (where&3)*8)) +			| FIELD(value, 8, (where&3)*8), PCICFG_DATA); +		break; +	case 2: +		writel((readl(PCICFG_DATA) & ~FMASK(16, (where&2)*8)) +			| FIELD(value, 16, (where&2)*8), PCICFG_DATA); +		break; +	case 4: +		writel(value, PCICFG_DATA); +		break; +	} +	return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops pci_puv3_ops = { +	.read  = puv3_read_config, +	.write = puv3_write_config, +}; + +void pci_puv3_preinit(void) +{ +	printk(KERN_DEBUG "PCI: PKUnity PCI Controller Initializing ...\n"); +	/* config PCI bridge base */ +	writel(io_v2p(PKUNITY_PCIBRI_BASE), PCICFG_BRIBASE); + +	writel(0, PCIBRI_AHBCTL0); +	writel(io_v2p(PKUNITY_PCIBRI_BASE) | PCIBRI_BARx_MEM, PCIBRI_AHBBAR0); +	writel(0xFFFF0000, PCIBRI_AHBAMR0); +	writel(0, PCIBRI_AHBTAR0); + +	writel(PCIBRI_CTLx_AT, PCIBRI_AHBCTL1); +	writel(io_v2p(PKUNITY_PCILIO_BASE) | PCIBRI_BARx_IO, PCIBRI_AHBBAR1); +	writel(0xFFFF0000, PCIBRI_AHBAMR1); +	writel(0x00000000, PCIBRI_AHBTAR1); + +	writel(PCIBRI_CTLx_PREF, PCIBRI_AHBCTL2); +	writel(io_v2p(PKUNITY_PCIMEM_BASE) | PCIBRI_BARx_MEM, PCIBRI_AHBBAR2); +	writel(0xF8000000, PCIBRI_AHBAMR2); +	writel(0, PCIBRI_AHBTAR2); + +	writel(io_v2p(PKUNITY_PCIAHB_BASE) | PCIBRI_BARx_MEM, PCIBRI_BAR1); + +	writel(PCIBRI_CTLx_AT | PCIBRI_CTLx_PREF, PCIBRI_PCICTL0); +	writel(io_v2p(PKUNITY_PCIAHB_BASE) | PCIBRI_BARx_MEM, PCIBRI_PCIBAR0); +	writel(0xF8000000, PCIBRI_PCIAMR0); +	writel(PKUNITY_SDRAM_BASE, PCIBRI_PCITAR0); + +	writel(readl(PCIBRI_CMD) | PCIBRI_CMD_IO | PCIBRI_CMD_MEM, PCIBRI_CMD); +} + +static int __init pci_puv3_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +{ +	if (dev->bus->number == 0) { +#ifdef CONFIG_ARCH_FPGA /* 4 pci slots */ +		if      (dev->devfn == 0x00) +			return IRQ_PCIINTA; +		else if (dev->devfn == 0x08) +			return IRQ_PCIINTB; +		else if (dev->devfn == 0x10) +			return IRQ_PCIINTC; +		else if (dev->devfn == 0x18) +			return IRQ_PCIINTD; +#endif +#ifdef CONFIG_PUV3_DB0913 /* 3 pci slots */ +		if      (dev->devfn == 0x30) +			return IRQ_PCIINTB; +		else if (dev->devfn == 0x60) +			return IRQ_PCIINTC; +		else if (dev->devfn == 0x58) +			return IRQ_PCIINTD; +#endif +#if	defined(CONFIG_PUV3_NB0916) || defined(CONFIG_PUV3_SMW0919) +		/* only support 2 pci devices */ +		if      (dev->devfn == 0x00) +			return IRQ_PCIINTC; /* sata */ +#endif +	} +	return -1; +} + +/* + * Only first 128MB of memory can be accessed via PCI. + * We use GFP_DMA to allocate safe buffers to do map/unmap. + * This is really ugly and we need a better way of specifying + * DMA-capable regions of memory. + */ +void __init puv3_pci_adjust_zones(unsigned long *zone_size, +	unsigned long *zhole_size) +{ +	unsigned int sz = SZ_128M >> PAGE_SHIFT; + +	/* +	 * Only adjust if > 128M on current system +	 */ +	if (zone_size[0] <= sz) +		return; + +	zone_size[1] = zone_size[0] - sz; +	zone_size[0] = sz; +	zhole_size[1] = zhole_size[0]; +	zhole_size[0] = 0; +} + +/* + * If the bus contains any of these devices, then we must not turn on + * parity checking of any kind. + */ +static inline int pdev_bad_for_parity(struct pci_dev *dev) +{ +	return 0; +} + +/* + * pcibios_fixup_bus - Called after each bus is probed, + * but before its children are examined. + */ +void pcibios_fixup_bus(struct pci_bus *bus) +{ +	struct pci_dev *dev; +	u16 features = PCI_COMMAND_SERR +		| PCI_COMMAND_PARITY +		| PCI_COMMAND_FAST_BACK; + +	bus->resource[0] = &ioport_resource; +	bus->resource[1] = &iomem_resource; + +	/* +	 * Walk the devices on this bus, working out what we can +	 * and can't support. +	 */ +	list_for_each_entry(dev, &bus->devices, bus_list) { +		u16 status; + +		pci_read_config_word(dev, PCI_STATUS, &status); + +		/* +		 * If any device on this bus does not support fast back +		 * to back transfers, then the bus as a whole is not able +		 * to support them.  Having fast back to back transfers +		 * on saves us one PCI cycle per transaction. +		 */ +		if (!(status & PCI_STATUS_FAST_BACK)) +			features &= ~PCI_COMMAND_FAST_BACK; + +		if (pdev_bad_for_parity(dev)) +			features &= ~(PCI_COMMAND_SERR +					| PCI_COMMAND_PARITY); + +		switch (dev->class >> 8) { +		case PCI_CLASS_BRIDGE_PCI: +			pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &status); +			status |= PCI_BRIDGE_CTL_PARITY +				| PCI_BRIDGE_CTL_MASTER_ABORT; +			status &= ~(PCI_BRIDGE_CTL_BUS_RESET +				| PCI_BRIDGE_CTL_FAST_BACK); +			pci_write_config_word(dev, PCI_BRIDGE_CONTROL, status); +			break; + +		case PCI_CLASS_BRIDGE_CARDBUS: +			pci_read_config_word(dev, PCI_CB_BRIDGE_CONTROL, +					&status); +			status |= PCI_CB_BRIDGE_CTL_PARITY +				| PCI_CB_BRIDGE_CTL_MASTER_ABORT; +			pci_write_config_word(dev, PCI_CB_BRIDGE_CONTROL, +					status); +			break; +		} +	} + +	/* +	 * Now walk the devices again, this time setting them up. +	 */ +	list_for_each_entry(dev, &bus->devices, bus_list) { +		u16 cmd; + +		pci_read_config_word(dev, PCI_COMMAND, &cmd); +		cmd |= features; +		pci_write_config_word(dev, PCI_COMMAND, cmd); + +		pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, +				      L1_CACHE_BYTES >> 2); +	} + +	/* +	 * Propagate the flags to the PCI bridge. +	 */ +	if (bus->self && bus->self->hdr_type == PCI_HEADER_TYPE_BRIDGE) { +		if (features & PCI_COMMAND_FAST_BACK) +			bus->bridge_ctl |= PCI_BRIDGE_CTL_FAST_BACK; +		if (features & PCI_COMMAND_PARITY) +			bus->bridge_ctl |= PCI_BRIDGE_CTL_PARITY; +	} + +	/* +	 * Report what we did for this bus +	 */ +	printk(KERN_INFO "PCI: bus%d: Fast back to back transfers %sabled\n", +		bus->number, (features & PCI_COMMAND_FAST_BACK) ? "en" : "dis"); +} +EXPORT_SYMBOL(pcibios_fixup_bus); + +static int __init pci_common_init(void) +{ +	struct pci_bus *puv3_bus; + +	pci_puv3_preinit(); + +	puv3_bus = pci_scan_bus(0, &pci_puv3_ops, NULL); + +	if (!puv3_bus) +		panic("PCI: unable to scan bus!"); + +	pci_fixup_irqs(pci_common_swizzle, pci_puv3_map_irq); + +	if (!pci_has_flag(PCI_PROBE_ONLY)) { +		/* +		 * Size the bridge windows. +		 */ +		pci_bus_size_bridges(puv3_bus); + +		/* +		 * Assign resources. +		 */ +		pci_bus_assign_resources(puv3_bus); +	} + +	return 0; +} +subsys_initcall(pci_common_init); + +char * __init pcibios_setup(char *str) +{ +	if (!strcmp(str, "debug")) { +		debug_pci = 1; +		return NULL; +	} else if (!strcmp(str, "firmware")) { +		pci_add_flags(PCI_PROBE_ONLY); +		return NULL; +	} +	return str; +} + +void pcibios_set_master(struct pci_dev *dev) +{ +	/* No special bus mastering setup handling */ +} + +/* + * From arch/i386/kernel/pci-i386.c: + * + * We need to avoid collisions with `mirrored' VGA ports + * and other strange ISA hardware, so we always want the + * addresses to be allocated in the 0x000-0x0ff region + * modulo 0x400. + * + * Why? Because some silly external IO cards only decode + * the low 10 bits of the IO address. The 0x00-0xff region + * is reserved for motherboard devices that decode all 16 + * bits, so it's ok to allocate at, say, 0x2800-0x28ff, + * but we want to try to avoid allocating at 0x2900-0x2bff + * which might be mirrored at 0x0100-0x03ff.. + */ +resource_size_t pcibios_align_resource(void *data, const struct resource *res, +				resource_size_t size, resource_size_t align) +{ +	resource_size_t start = res->start; + +	if (res->flags & IORESOURCE_IO && start & 0x300) +		start = (start + 0x3ff) & ~0x3ff; + +	start = (start + align - 1) & ~(align - 1); + +	return start; +} + +/** + * pcibios_enable_device - Enable I/O and memory. + * @dev: PCI device to be enabled + */ +int pcibios_enable_device(struct pci_dev *dev, int mask) +{ +	u16 cmd, old_cmd; +	int idx; +	struct resource *r; + +	pci_read_config_word(dev, PCI_COMMAND, &cmd); +	old_cmd = cmd; +	for (idx = 0; idx < 6; idx++) { +		/* Only set up the requested stuff */ +		if (!(mask & (1 << idx))) +			continue; + +		r = dev->resource + idx; +		if (!r->start && r->end) { +			printk(KERN_ERR "PCI: Device %s not available because" +			       " of resource collisions\n", pci_name(dev)); +			return -EINVAL; +		} +		if (r->flags & IORESOURCE_IO) +			cmd |= PCI_COMMAND_IO; +		if (r->flags & IORESOURCE_MEM) +			cmd |= PCI_COMMAND_MEMORY; +	} + +	/* +	 * Bridges (eg, cardbus bridges) need to be fully enabled +	 */ +	if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE) +		cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY; + +	if (cmd != old_cmd) { +		printk("PCI: enabling device %s (%04x -> %04x)\n", +		       pci_name(dev), old_cmd, cmd); +		pci_write_config_word(dev, PCI_COMMAND, cmd); +	} +	return 0; +} + +int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, +			enum pci_mmap_state mmap_state, int write_combine) +{ +	unsigned long phys; + +	if (mmap_state == pci_mmap_io) +		return -EINVAL; + +	phys = vma->vm_pgoff; + +	/* +	 * Mark this as IO +	 */ +	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + +	if (remap_pfn_range(vma, vma->vm_start, phys, +			     vma->vm_end - vma->vm_start, +			     vma->vm_page_prot)) +		return -EAGAIN; + +	return 0; +} diff --git a/arch/unicore32/kernel/pm.c b/arch/unicore32/kernel/pm.c new file mode 100644 index 00000000000..784bc2db3b2 --- /dev/null +++ b/arch/unicore32/kernel/pm.c @@ -0,0 +1,123 @@ +/* + * linux/arch/unicore32/kernel/pm.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + *	Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn> + *	Copyright (C) 2001-2010 Guan Xuetao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/suspend.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/io.h> + +#include <mach/hardware.h> +#include <mach/pm.h> + +#include "setup.h" + +struct puv3_cpu_pm_fns *puv3_cpu_pm_fns; +static unsigned long *sleep_save; + +int puv3_pm_enter(suspend_state_t state) +{ +	unsigned long sleep_save_checksum = 0, checksum = 0; +	int i; + +	/* skip registers saving for standby */ +	if (state != PM_SUSPEND_STANDBY) { +		puv3_cpu_pm_fns->save(sleep_save); +		/* before sleeping, calculate and save a checksum */ +		for (i = 0; i < puv3_cpu_pm_fns->save_count - 1; i++) +			sleep_save_checksum += sleep_save[i]; +	} + +	/* *** go zzz *** */ +	puv3_cpu_pm_fns->enter(state); +	cpu_init(); +#ifdef CONFIG_INPUT_KEYBOARD +	puv3_ps2_init(); +#endif +#ifdef CONFIG_PCI +	pci_puv3_preinit(); +#endif +	if (state != PM_SUSPEND_STANDBY) { +		/* after sleeping, validate the checksum */ +		for (i = 0; i < puv3_cpu_pm_fns->save_count - 1; i++) +			checksum += sleep_save[i]; + +		/* if invalid, display message and wait for a hardware reset */ +		if (checksum != sleep_save_checksum) { +			while (1) +				puv3_cpu_pm_fns->enter(state); +		} +		puv3_cpu_pm_fns->restore(sleep_save); +	} + +	pr_debug("*** made it back from resume\n"); + +	return 0; +} +EXPORT_SYMBOL_GPL(puv3_pm_enter); + +unsigned long sleep_phys_sp(void *sp) +{ +	return virt_to_phys(sp); +} + +static int puv3_pm_valid(suspend_state_t state) +{ +	if (puv3_cpu_pm_fns) +		return puv3_cpu_pm_fns->valid(state); + +	return -EINVAL; +} + +static int puv3_pm_prepare(void) +{ +	int ret = 0; + +	if (puv3_cpu_pm_fns && puv3_cpu_pm_fns->prepare) +		ret = puv3_cpu_pm_fns->prepare(); + +	return ret; +} + +static void puv3_pm_finish(void) +{ +	if (puv3_cpu_pm_fns && puv3_cpu_pm_fns->finish) +		puv3_cpu_pm_fns->finish(); +} + +static struct platform_suspend_ops puv3_pm_ops = { +	.valid		= puv3_pm_valid, +	.enter		= puv3_pm_enter, +	.prepare	= puv3_pm_prepare, +	.finish		= puv3_pm_finish, +}; + +static int __init puv3_pm_init(void) +{ +	if (!puv3_cpu_pm_fns) { +		printk(KERN_ERR "no valid puv3_cpu_pm_fns defined\n"); +		return -EINVAL; +	} + +	sleep_save = kmalloc(puv3_cpu_pm_fns->save_count +				* sizeof(unsigned long), GFP_KERNEL); +	if (!sleep_save) { +		printk(KERN_ERR "failed to alloc memory for pm save\n"); +		return -ENOMEM; +	} + +	suspend_set_ops(&puv3_pm_ops); +	return 0; +} + +device_initcall(puv3_pm_init); diff --git a/arch/unicore32/kernel/process.c b/arch/unicore32/kernel/process.c new file mode 100644 index 00000000000..b008e996146 --- /dev/null +++ b/arch/unicore32/kernel/process.c @@ -0,0 +1,328 @@ +/* + * linux/arch/unicore32/kernel/process.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <stdarg.h> + +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/delay.h> +#include <linux/reboot.h> +#include <linux/interrupt.h> +#include <linux/kallsyms.h> +#include <linux/init.h> +#include <linux/cpu.h> +#include <linux/elfcore.h> +#include <linux/pm.h> +#include <linux/tick.h> +#include <linux/utsname.h> +#include <linux/uaccess.h> +#include <linux/random.h> +#include <linux/gpio.h> +#include <linux/stacktrace.h> + +#include <asm/cacheflush.h> +#include <asm/processor.h> +#include <asm/stacktrace.h> + +#include "setup.h" + +static const char * const processor_modes[] = { +	"UK00", "UK01", "UK02", "UK03", "UK04", "UK05", "UK06", "UK07", +	"UK08", "UK09", "UK0A", "UK0B", "UK0C", "UK0D", "UK0E", "UK0F", +	"USER", "REAL", "INTR", "PRIV", "UK14", "UK15", "UK16", "ABRT", +	"UK18", "UK19", "UK1A", "EXTN", "UK1C", "UK1D", "UK1E", "SUSR" +}; + +void arch_cpu_idle(void) +{ +	cpu_do_idle(); +	local_irq_enable(); +} + +void machine_halt(void) +{ +	gpio_set_value(GPO_SOFT_OFF, 0); +} + +/* + * Function pointers to optional machine specific functions + */ +void (*pm_power_off)(void) = NULL; +EXPORT_SYMBOL(pm_power_off); + +void machine_power_off(void) +{ +	if (pm_power_off) +		pm_power_off(); +	machine_halt(); +} + +void machine_restart(char *cmd) +{ +	/* Disable interrupts first */ +	local_irq_disable(); + +	/* +	 * Tell the mm system that we are going to reboot - +	 * we may need it to insert some 1:1 mappings so that +	 * soft boot works. +	 */ +	setup_mm_for_reboot(); + +	/* Clean and invalidate caches */ +	flush_cache_all(); + +	/* Turn off caching */ +	cpu_proc_fin(); + +	/* Push out any further dirty data, and ensure cache is empty */ +	flush_cache_all(); + +	/* +	 * Now handle reboot code. +	 */ +	if (reboot_mode == REBOOT_SOFT) { +		/* Jump into ROM at address 0xffff0000 */ +		cpu_reset(VECTORS_BASE); +	} else { +		writel(0x00002001, PM_PLLSYSCFG); /* cpu clk = 250M */ +		writel(0x00100800, PM_PLLDDRCFG); /* ddr clk =  44M */ +		writel(0x00002001, PM_PLLVGACFG); /* vga clk = 250M */ + +		/* Use on-chip reset capability */ +		/* following instructions must be in one icache line */ +		__asm__ __volatile__( +			"	.align 5\n\t" +			"	stw	%1, [%0]\n\t" +			"201:	ldw	r0, [%0]\n\t" +			"	cmpsub.a	r0, #0\n\t" +			"	bne	201b\n\t" +			"	stw	%3, [%2]\n\t" +			"	nop; nop; nop\n\t" +			/* prefetch 3 instructions at most */ +			: +			: "r" (PM_PMCR), +			  "r" (PM_PMCR_CFBSYS | PM_PMCR_CFBDDR +				| PM_PMCR_CFBVGA), +			  "r" (RESETC_SWRR), +			  "r" (RESETC_SWRR_SRB) +			: "r0", "memory"); +	} + +	/* +	 * Whoops - the architecture was unable to reboot. +	 * Tell the user! +	 */ +	mdelay(1000); +	printk(KERN_EMERG "Reboot failed -- System halted\n"); +	do { } while (1); +} + +void __show_regs(struct pt_regs *regs) +{ +	unsigned long flags; +	char buf[64]; + +	show_regs_print_info(KERN_DEFAULT); +	print_symbol("PC is at %s\n", instruction_pointer(regs)); +	print_symbol("LR is at %s\n", regs->UCreg_lr); +	printk(KERN_DEFAULT "pc : [<%08lx>]    lr : [<%08lx>]    psr: %08lx\n" +	       "sp : %08lx  ip : %08lx  fp : %08lx\n", +		regs->UCreg_pc, regs->UCreg_lr, regs->UCreg_asr, +		regs->UCreg_sp, regs->UCreg_ip, regs->UCreg_fp); +	printk(KERN_DEFAULT "r26: %08lx  r25: %08lx  r24: %08lx\n", +		regs->UCreg_26, regs->UCreg_25, +		regs->UCreg_24); +	printk(KERN_DEFAULT "r23: %08lx  r22: %08lx  r21: %08lx  r20: %08lx\n", +		regs->UCreg_23, regs->UCreg_22, +		regs->UCreg_21, regs->UCreg_20); +	printk(KERN_DEFAULT "r19: %08lx  r18: %08lx  r17: %08lx  r16: %08lx\n", +		regs->UCreg_19, regs->UCreg_18, +		regs->UCreg_17, regs->UCreg_16); +	printk(KERN_DEFAULT "r15: %08lx  r14: %08lx  r13: %08lx  r12: %08lx\n", +		regs->UCreg_15, regs->UCreg_14, +		regs->UCreg_13, regs->UCreg_12); +	printk(KERN_DEFAULT "r11: %08lx  r10: %08lx  r9 : %08lx  r8 : %08lx\n", +		regs->UCreg_11, regs->UCreg_10, +		regs->UCreg_09, regs->UCreg_08); +	printk(KERN_DEFAULT "r7 : %08lx  r6 : %08lx  r5 : %08lx  r4 : %08lx\n", +		regs->UCreg_07, regs->UCreg_06, +		regs->UCreg_05, regs->UCreg_04); +	printk(KERN_DEFAULT "r3 : %08lx  r2 : %08lx  r1 : %08lx  r0 : %08lx\n", +		regs->UCreg_03, regs->UCreg_02, +		regs->UCreg_01, regs->UCreg_00); + +	flags = regs->UCreg_asr; +	buf[0] = flags & PSR_S_BIT ? 'S' : 's'; +	buf[1] = flags & PSR_Z_BIT ? 'Z' : 'z'; +	buf[2] = flags & PSR_C_BIT ? 'C' : 'c'; +	buf[3] = flags & PSR_V_BIT ? 'V' : 'v'; +	buf[4] = '\0'; + +	printk(KERN_DEFAULT "Flags: %s  INTR o%s  REAL o%s  Mode %s  Segment %s\n", +		buf, interrupts_enabled(regs) ? "n" : "ff", +		fast_interrupts_enabled(regs) ? "n" : "ff", +		processor_modes[processor_mode(regs)], +		segment_eq(get_fs(), get_ds()) ? "kernel" : "user"); +	{ +		unsigned int ctrl; + +		buf[0] = '\0'; +		{ +			unsigned int transbase; +			asm("movc %0, p0.c2, #0\n" +			    : "=r" (transbase)); +			snprintf(buf, sizeof(buf), "  Table: %08x", transbase); +		} +		asm("movc %0, p0.c1, #0\n" : "=r" (ctrl)); + +		printk(KERN_DEFAULT "Control: %08x%s\n", ctrl, buf); +	} +} + +void show_regs(struct pt_regs *regs) +{ +	printk(KERN_DEFAULT "\n"); +	printk(KERN_DEFAULT "Pid: %d, comm: %20s\n", +			task_pid_nr(current), current->comm); +	__show_regs(regs); +	__backtrace(); +} + +/* + * Free current thread data structures etc.. + */ +void exit_thread(void) +{ +} + +void flush_thread(void) +{ +	struct thread_info *thread = current_thread_info(); +	struct task_struct *tsk = current; + +	memset(thread->used_cp, 0, sizeof(thread->used_cp)); +	memset(&tsk->thread.debug, 0, sizeof(struct debug_info)); +#ifdef CONFIG_UNICORE_FPU_F64 +	memset(&thread->fpstate, 0, sizeof(struct fp_state)); +#endif +} + +void release_thread(struct task_struct *dead_task) +{ +} + +asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); +asmlinkage void ret_from_kernel_thread(void) __asm__("ret_from_kernel_thread"); + +int +copy_thread(unsigned long clone_flags, unsigned long stack_start, +	    unsigned long stk_sz, struct task_struct *p) +{ +	struct thread_info *thread = task_thread_info(p); +	struct pt_regs *childregs = task_pt_regs(p); + +	memset(&thread->cpu_context, 0, sizeof(struct cpu_context_save)); +	thread->cpu_context.sp = (unsigned long)childregs; +	if (unlikely(p->flags & PF_KTHREAD)) { +		thread->cpu_context.pc = (unsigned long)ret_from_kernel_thread; +		thread->cpu_context.r4 = stack_start; +		thread->cpu_context.r5 = stk_sz; +		memset(childregs, 0, sizeof(struct pt_regs)); +	} else { +		thread->cpu_context.pc = (unsigned long)ret_from_fork; +		*childregs = *current_pt_regs(); +		childregs->UCreg_00 = 0; +		if (stack_start) +			childregs->UCreg_sp = stack_start; + +		if (clone_flags & CLONE_SETTLS) +			childregs->UCreg_16 = childregs->UCreg_03; +	} +	return 0; +} + +/* + * Fill in the task's elfregs structure for a core dump. + */ +int dump_task_regs(struct task_struct *t, elf_gregset_t *elfregs) +{ +	elf_core_copy_regs(elfregs, task_pt_regs(t)); +	return 1; +} + +/* + * fill in the fpe structure for a core dump... + */ +int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fp) +{ +	struct thread_info *thread = current_thread_info(); +	int used_math = thread->used_cp[1] | thread->used_cp[2]; + +#ifdef CONFIG_UNICORE_FPU_F64 +	if (used_math) +		memcpy(fp, &thread->fpstate, sizeof(*fp)); +#endif +	return used_math != 0; +} +EXPORT_SYMBOL(dump_fpu); + +unsigned long get_wchan(struct task_struct *p) +{ +	struct stackframe frame; +	int count = 0; +	if (!p || p == current || p->state == TASK_RUNNING) +		return 0; + +	frame.fp = thread_saved_fp(p); +	frame.sp = thread_saved_sp(p); +	frame.lr = 0;			/* recovered from the stack */ +	frame.pc = thread_saved_pc(p); +	do { +		int ret = unwind_frame(&frame); +		if (ret < 0) +			return 0; +		if (!in_sched_functions(frame.pc)) +			return frame.pc; +	} while ((count++) < 16); +	return 0; +} + +unsigned long arch_randomize_brk(struct mm_struct *mm) +{ +	unsigned long range_end = mm->brk + 0x02000000; +	return randomize_range(mm->brk, range_end, 0) ? : mm->brk; +} + +/* + * The vectors page is always readable from user space for the + * atomic helpers and the signal restart code.  Let's declare a mapping + * for it so it is visible through ptrace and /proc/<pid>/mem. + */ + +int vectors_user_mapping(void) +{ +	struct mm_struct *mm = current->mm; +	return install_special_mapping(mm, 0xffff0000, PAGE_SIZE, +				       VM_READ | VM_EXEC | +				       VM_MAYREAD | VM_MAYEXEC | +				       VM_DONTEXPAND | VM_DONTDUMP, +				       NULL); +} + +const char *arch_vma_name(struct vm_area_struct *vma) +{ +	return (vma->vm_start == 0xffff0000) ? "[vectors]" : NULL; +} diff --git a/arch/unicore32/kernel/ptrace.c b/arch/unicore32/kernel/ptrace.c new file mode 100644 index 00000000000..9f07c08da05 --- /dev/null +++ b/arch/unicore32/kernel/ptrace.c @@ -0,0 +1,149 @@ +/* + * linux/arch/unicore32/kernel/ptrace.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * By Ross Biro 1/23/92 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/kernel.h> +#include <linux/ptrace.h> +#include <linux/signal.h> +#include <linux/uaccess.h> + +/* + * this routine will get a word off of the processes privileged stack. + * the offset is how far from the base addr as stored in the THREAD. + * this routine assumes that all the privileged stacks are in our + * data space. + */ +static inline long get_user_reg(struct task_struct *task, int offset) +{ +	return task_pt_regs(task)->uregs[offset]; +} + +/* + * this routine will put a word on the processes privileged stack. + * the offset is how far from the base addr as stored in the THREAD. + * this routine assumes that all the privileged stacks are in our + * data space. + */ +static inline int +put_user_reg(struct task_struct *task, int offset, long data) +{ +	struct pt_regs newregs, *regs = task_pt_regs(task); +	int ret = -EINVAL; + +	newregs = *regs; +	newregs.uregs[offset] = data; + +	if (valid_user_regs(&newregs)) { +		regs->uregs[offset] = data; +		ret = 0; +	} + +	return ret; +} + +/* + * Called by kernel/ptrace.c when detaching.. + */ +void ptrace_disable(struct task_struct *child) +{ +} + +/* + * We actually access the pt_regs stored on the kernel stack. + */ +static int ptrace_read_user(struct task_struct *tsk, unsigned long off, +			    unsigned long __user *ret) +{ +	unsigned long tmp; + +	tmp = 0; +	if (off < sizeof(struct pt_regs)) +		tmp = get_user_reg(tsk, off >> 2); + +	return put_user(tmp, ret); +} + +/* + * We actually access the pt_regs stored on the kernel stack. + */ +static int ptrace_write_user(struct task_struct *tsk, unsigned long off, +			     unsigned long val) +{ +	if (off >= sizeof(struct pt_regs)) +		return 0; + +	return put_user_reg(tsk, off >> 2, val); +} + +long arch_ptrace(struct task_struct *child, long request, +		 unsigned long addr, unsigned long data) +{ +	int ret; +	unsigned long __user *datap = (unsigned long __user *) data; + +	switch (request) { +	case PTRACE_PEEKUSR: +		ret = ptrace_read_user(child, addr, datap); +		break; + +	case PTRACE_POKEUSR: +		ret = ptrace_write_user(child, addr, data); +		break; + +	case PTRACE_GET_THREAD_AREA: +		ret = put_user(task_pt_regs(child)->UCreg_16, +			       datap); +		break; + +	default: +		ret = ptrace_request(child, request, addr, data); +		break; +	} + +	return ret; +} + +asmlinkage int syscall_trace(int why, struct pt_regs *regs, int scno) +{ +	unsigned long ip; + +	if (!test_thread_flag(TIF_SYSCALL_TRACE)) +		return scno; +	if (!(current->ptrace & PT_PTRACED)) +		return scno; + +	/* +	 * Save IP.  IP is used to denote syscall entry/exit: +	 *  IP = 0 -> entry, = 1 -> exit +	 */ +	ip = regs->UCreg_ip; +	regs->UCreg_ip = why; + +	current_thread_info()->syscall = scno; + +	/* the 0x80 provides a way for the tracing parent to distinguish +	   between a syscall stop and SIGTRAP delivery */ +	ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) +				 ? 0x80 : 0)); +	/* +	 * this isn't the same as continuing with a signal, but it will do +	 * for normal use.  strace only continues with a signal if the +	 * stopping signal is not SIGTRAP.  -brl +	 */ +	if (current->exit_code) { +		send_sig(current->exit_code, current, 1); +		current->exit_code = 0; +	} +	regs->UCreg_ip = ip; + +	return current_thread_info()->syscall; +} diff --git a/arch/unicore32/kernel/puv3-core.c b/arch/unicore32/kernel/puv3-core.c new file mode 100644 index 00000000000..254adeecc61 --- /dev/null +++ b/arch/unicore32/kernel/puv3-core.c @@ -0,0 +1,279 @@ +/* + *  linux/arch/unicore32/kernel/puv3-core.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + *	Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn> + *	Copyright (C) 2001-2010 Guan Xuetao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/device.h> +#include <linux/amba/bus.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/cnt32_to_63.h> +#include <linux/usb/musb.h> + +#include <asm/irq.h> +#include <mach/hardware.h> +#include <mach/pm.h> + +/* + * This is the PKUnity sched_clock implementation.  This has + * a resolution of 271ns, and a maximum value of 32025597s (370 days). + * + * The return value is guaranteed to be monotonic in that range as + * long as there is always less than 582 seconds between successive + * calls to this function. + * + *  ( * 1E9 / CLOCK_TICK_RATE ) -> about 2235/32 + */ +unsigned long long sched_clock(void) +{ +	unsigned long long v = cnt32_to_63(readl(OST_OSCR)); + +	/* original conservative method, but overflow frequently +	 * v *= NSEC_PER_SEC >> 12; +	 * do_div(v, CLOCK_TICK_RATE >> 12); +	 */ +	v = ((v & 0x7fffffffffffffffULL) * 2235) >> 5; + +	return v; +} + +static struct resource puv3_usb_resources[] = { +	/* order is significant! */ +	{ +		.start		= io_v2p(PKUNITY_USB_BASE), +		.end		= io_v2p(PKUNITY_USB_BASE) + 0x3ff, +		.flags		= IORESOURCE_MEM, +	}, { +		.start		= IRQ_USB, +		.flags		= IORESOURCE_IRQ, +	}, { +		.start		= IRQ_USB, +		.flags		= IORESOURCE_IRQ, +	}, +}; + +static struct musb_hdrc_config	puv3_usb_config[] = { +	{ +		.num_eps = 16, +		.multipoint = 1, +#ifdef CONFIG_USB_INVENTRA_DMA +		.dma = 1, +		.dma_channels = 8, +#endif +	}, +}; + +static struct musb_hdrc_platform_data puv3_usb_plat = { +	.mode		= MUSB_HOST, +	.min_power	= 100, +	.clock		= 0, +	.config		= puv3_usb_config, +}; + +static struct resource puv3_mmc_resources[] = { +	[0] = { +		.start	= io_v2p(PKUNITY_SDC_BASE), +		.end	= io_v2p(PKUNITY_SDC_BASE) + 0xfff, +		.flags	= IORESOURCE_MEM, +	}, +	[1] = { +		.start	= IRQ_SDC, +		.end	= IRQ_SDC, +		.flags	= IORESOURCE_IRQ, +	}, +}; + +static struct resource puv3_unigfx_resources[] = { +	[0] = { +		.start	= io_v2p(PKUNITY_UNIGFX_BASE), +		.end	= io_v2p(PKUNITY_UNIGFX_BASE) + 0xfff, +		.flags	= IORESOURCE_MEM, +	}, +}; + +static struct resource puv3_rtc_resources[] = { +	[0] = { +		.start = io_v2p(PKUNITY_RTC_BASE), +		.end   = io_v2p(PKUNITY_RTC_BASE) + 0xff, +		.flags = IORESOURCE_MEM, +	}, +	[1] = { +		.start = IRQ_RTCAlarm, +		.end   = IRQ_RTCAlarm, +		.flags = IORESOURCE_IRQ, +	}, +	[2] = { +		.start = IRQ_RTC, +		.end   = IRQ_RTC, +		.flags = IORESOURCE_IRQ +	} +}; + +static struct resource puv3_pwm_resources[] = { +	[0] = { +		.start	= io_v2p(PKUNITY_OST_BASE) + 0x80, +		.end	= io_v2p(PKUNITY_OST_BASE) + 0xff, +		.flags	= IORESOURCE_MEM, +	}, +}; + +static struct resource puv3_uart0_resources[] = { +	[0] = { +		.start = io_v2p(PKUNITY_UART0_BASE), +		.end   = io_v2p(PKUNITY_UART0_BASE) + 0xff, +		.flags = IORESOURCE_MEM, +	}, +	[1] = { +		.start = IRQ_UART0, +		.end   = IRQ_UART0, +		.flags = IORESOURCE_IRQ +	} +}; + +static struct resource puv3_uart1_resources[] = { +	[0] = { +		.start = io_v2p(PKUNITY_UART1_BASE), +		.end   = io_v2p(PKUNITY_UART1_BASE) + 0xff, +		.flags = IORESOURCE_MEM, +	}, +	[1] = { +		.start = IRQ_UART1, +		.end   = IRQ_UART1, +		.flags = IORESOURCE_IRQ +	} +}; + +static struct resource puv3_umal_resources[] = { +	[0] = { +		.start = io_v2p(PKUNITY_UMAL_BASE), +		.end   = io_v2p(PKUNITY_UMAL_BASE) + 0x1fff, +		.flags = IORESOURCE_MEM, +	}, +	[1] = { +		.start = IRQ_UMAL, +		.end   = IRQ_UMAL, +		.flags = IORESOURCE_IRQ +	} +}; + +#ifdef CONFIG_PUV3_PM + +#define SAVE(x)		sleep_save[SLEEP_SAVE_##x] = x +#define RESTORE(x)	x = sleep_save[SLEEP_SAVE_##x] + +/* + * List of global PXA peripheral registers to preserve. + * More ones like CP and general purpose register values are preserved + * with the stack pointer in sleep.S. + */ +enum { +	SLEEP_SAVE_PM_PLLDDRCFG, +	SLEEP_SAVE_COUNT +}; + + +static void puv3_cpu_pm_save(unsigned long *sleep_save) +{ +/*	SAVE(PM_PLLDDRCFG); */ +} + +static void puv3_cpu_pm_restore(unsigned long *sleep_save) +{ +/*	RESTORE(PM_PLLDDRCFG); */ +} + +static int puv3_cpu_pm_prepare(void) +{ +	/* set resume return address */ +	writel(virt_to_phys(puv3_cpu_resume), PM_DIVCFG); +	return 0; +} + +static void puv3_cpu_pm_enter(suspend_state_t state) +{ +	/* Clear reset status */ +	writel(RESETC_RSSR_HWR | RESETC_RSSR_WDR +			| RESETC_RSSR_SMR | RESETC_RSSR_SWR, RESETC_RSSR); + +	switch (state) { +/*	case PM_SUSPEND_ON: +		puv3_cpu_idle(); +		break; */ +	case PM_SUSPEND_MEM: +		puv3_cpu_pm_prepare(); +		puv3_cpu_suspend(PM_PMCR_SFB); +		break; +	} +} + +static int puv3_cpu_pm_valid(suspend_state_t state) +{ +	return state == PM_SUSPEND_MEM; +} + +static void puv3_cpu_pm_finish(void) +{ +	/* ensure not to come back here if it wasn't intended */ +	/* PSPR = 0; */ +} + +static struct puv3_cpu_pm_fns puv3_cpu_pm_fnss = { +	.save_count	= SLEEP_SAVE_COUNT, +	.valid		= puv3_cpu_pm_valid, +	.save		= puv3_cpu_pm_save, +	.restore	= puv3_cpu_pm_restore, +	.enter		= puv3_cpu_pm_enter, +	.prepare	= puv3_cpu_pm_prepare, +	.finish		= puv3_cpu_pm_finish, +}; + +static void __init puv3_init_pm(void) +{ +	puv3_cpu_pm_fns = &puv3_cpu_pm_fnss; +} +#else +static inline void puv3_init_pm(void) {} +#endif + +void puv3_ps2_init(void) +{ +	struct clk *bclk32; + +	bclk32 = clk_get(NULL, "BUS32_CLK"); +	writel(clk_get_rate(bclk32) / 200000, PS2_CNT); /* should > 5us */ +} + +void __init puv3_core_init(void) +{ +	puv3_init_pm(); +	puv3_ps2_init(); + +	platform_device_register_simple("PKUnity-v3-RTC", -1, +			puv3_rtc_resources, ARRAY_SIZE(puv3_rtc_resources)); +	platform_device_register_simple("PKUnity-v3-UMAL", -1, +			puv3_umal_resources, ARRAY_SIZE(puv3_umal_resources)); +	platform_device_register_simple("PKUnity-v3-MMC", -1, +			puv3_mmc_resources, ARRAY_SIZE(puv3_mmc_resources)); +	platform_device_register_simple("PKUnity-v3-UNIGFX", -1, +			puv3_unigfx_resources, ARRAY_SIZE(puv3_unigfx_resources)); +	platform_device_register_simple("PKUnity-v3-PWM", -1, +			puv3_pwm_resources, ARRAY_SIZE(puv3_pwm_resources)); +	platform_device_register_simple("PKUnity-v3-UART", 0, +			puv3_uart0_resources, ARRAY_SIZE(puv3_uart0_resources)); +	platform_device_register_simple("PKUnity-v3-UART", 1, +			puv3_uart1_resources, ARRAY_SIZE(puv3_uart1_resources)); +	platform_device_register_simple("PKUnity-v3-AC97", -1, NULL, 0); +	platform_device_register_resndata(&platform_bus, "musb_hdrc", -1, +			puv3_usb_resources, ARRAY_SIZE(puv3_usb_resources), +			&puv3_usb_plat, sizeof(puv3_usb_plat)); +} + diff --git a/arch/unicore32/kernel/puv3-nb0916.c b/arch/unicore32/kernel/puv3-nb0916.c new file mode 100644 index 00000000000..0c6618e7189 --- /dev/null +++ b/arch/unicore32/kernel/puv3-nb0916.c @@ -0,0 +1,145 @@ +/* + * linux/arch/unicore32/kernel/puv3-nb0916.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + *	Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn> + *	Copyright (C) 2001-2010 Guan Xuetao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/mtd/physmap.h> +#include <linux/io.h> +#include <linux/reboot.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/pwm_backlight.h> +#include <linux/gpio.h> +#include <linux/gpio_keys.h> +#include <linux/input.h> + +#include <mach/hardware.h> + +static struct physmap_flash_data physmap_flash_data = { +	.width		= 1, +}; + +static struct resource physmap_flash_resource = { +	.start		= 0xFFF80000, +	.end		= 0xFFFFFFFF, +	.flags		= IORESOURCE_MEM, +}; + +static struct resource puv3_i2c_resources[] = { +	[0] = { +		.start = io_v2p(PKUNITY_I2C_BASE), +		.end   = io_v2p(PKUNITY_I2C_BASE) + 0xff, +		.flags = IORESOURCE_MEM, +	}, +	[1] = { +		.start = IRQ_I2C, +		.end   = IRQ_I2C, +		.flags = IORESOURCE_IRQ, +	} +}; + +static struct platform_pwm_backlight_data nb0916_backlight_data = { +	.pwm_id		= 0, +	.max_brightness	= 100, +	.dft_brightness	= 100, +	.pwm_period_ns	= 70 * 1024, +	.enable_gpio	= -1, +}; + +static struct gpio_keys_button nb0916_gpio_keys[] = { +	{ +		.type	= EV_KEY, +		.code	= KEY_POWER, +		.gpio	= GPI_SOFF_REQ, +		.desc	= "Power Button", +		.wakeup = 1, +		.active_low = 1, +	}, +	{ +		.type	= EV_KEY, +		.code	= BTN_TOUCH, +		.gpio	= GPI_BTN_TOUCH, +		.desc	= "Touchpad Button", +		.wakeup = 1, +		.active_low = 1, +	}, +}; + +static struct gpio_keys_platform_data nb0916_gpio_button_data = { +	.buttons	= nb0916_gpio_keys, +	.nbuttons	= ARRAY_SIZE(nb0916_gpio_keys), +}; + +static irqreturn_t nb0916_lcdcaseoff_handler(int irq, void *dev_id) +{ +	if (gpio_get_value(GPI_LCD_CASE_OFF)) +		gpio_set_value(GPO_LCD_EN, 1); +	else +		gpio_set_value(GPO_LCD_EN, 0); + +	return IRQ_HANDLED; +} + +static irqreturn_t nb0916_overheat_handler(int irq, void *dev_id) +{ +	machine_halt(); +	/* SYSTEM HALT, NO RETURN */ +	return IRQ_HANDLED; +} + +static struct i2c_board_info __initdata puv3_i2c_devices[] = { +	{	I2C_BOARD_INFO("lm75",		I2C_TAR_THERMAL),	}, +	{	I2C_BOARD_INFO("bq27200",	I2C_TAR_PWIC),		}, +	{	I2C_BOARD_INFO("24c02",		I2C_TAR_EEPROM),	}, +}; + +int __init mach_nb0916_init(void) +{ +	i2c_register_board_info(0, puv3_i2c_devices, +			ARRAY_SIZE(puv3_i2c_devices)); + +	platform_device_register_simple("PKUnity-v3-I2C", -1, +			puv3_i2c_resources, ARRAY_SIZE(puv3_i2c_resources)); + +	platform_device_register_data(&platform_bus, "pwm-backlight", -1, +			&nb0916_backlight_data, sizeof(nb0916_backlight_data)); + +	platform_device_register_data(&platform_bus, "gpio-keys", -1, +			&nb0916_gpio_button_data, sizeof(nb0916_gpio_button_data)); + +	platform_device_register_resndata(&platform_bus, "physmap-flash", -1, +			&physmap_flash_resource, 1, +			&physmap_flash_data, sizeof(physmap_flash_data)); + +	if (request_irq(gpio_to_irq(GPI_LCD_CASE_OFF), +		&nb0916_lcdcaseoff_handler, +		IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, +		"NB0916 lcd case off", NULL) < 0) { + +		printk(KERN_DEBUG "LCD-Case-OFF IRQ %d not available\n", +			gpio_to_irq(GPI_LCD_CASE_OFF)); +	} + +	if (request_irq(gpio_to_irq(GPI_OTP_INT), &nb0916_overheat_handler, +		IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, +		"NB0916 overheating protection", NULL) < 0) { + +		printk(KERN_DEBUG "Overheating Protection IRQ %d not available\n", +			gpio_to_irq(GPI_OTP_INT)); +	} + +	return 0; +} + +subsys_initcall_sync(mach_nb0916_init); diff --git a/arch/unicore32/kernel/setup.c b/arch/unicore32/kernel/setup.c new file mode 100644 index 00000000000..3fa317f9612 --- /dev/null +++ b/arch/unicore32/kernel/setup.c @@ -0,0 +1,354 @@ +/* + * linux/arch/unicore32/kernel/setup.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/stddef.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/utsname.h> +#include <linux/initrd.h> +#include <linux/console.h> +#include <linux/bootmem.h> +#include <linux/seq_file.h> +#include <linux/screen_info.h> +#include <linux/init.h> +#include <linux/root_dev.h> +#include <linux/cpu.h> +#include <linux/interrupt.h> +#include <linux/smp.h> +#include <linux/fs.h> +#include <linux/proc_fs.h> +#include <linux/memblock.h> +#include <linux/elf.h> +#include <linux/io.h> + +#include <asm/cputype.h> +#include <asm/sections.h> +#include <asm/setup.h> +#include <asm/cacheflush.h> +#include <asm/tlbflush.h> +#include <asm/traps.h> +#include <asm/memblock.h> + +#include "setup.h" + +#ifndef MEM_SIZE +#define MEM_SIZE	(16*1024*1024) +#endif + +struct stack { +	u32 irq[3]; +	u32 abt[3]; +	u32 und[3]; +} ____cacheline_aligned; + +static struct stack stacks[NR_CPUS]; + +#ifdef CONFIG_VGA_CONSOLE +struct screen_info screen_info; +#endif + +char elf_platform[ELF_PLATFORM_SIZE]; +EXPORT_SYMBOL(elf_platform); + +static char __initdata cmd_line[COMMAND_LINE_SIZE]; + +static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE; + +/* + * Standard memory resources + */ +static struct resource mem_res[] = { +	{ +		.name = "Kernel code", +		.start = 0, +		.end = 0, +		.flags = IORESOURCE_MEM +	}, +	{ +		.name = "Kernel data", +		.start = 0, +		.end = 0, +		.flags = IORESOURCE_MEM +	} +}; + +#define kernel_code mem_res[0] +#define kernel_data mem_res[1] + +/* + * These functions re-use the assembly code in head.S, which + * already provide the required functionality. + */ +static void __init setup_processor(void) +{ +	printk(KERN_DEFAULT "CPU: UniCore-II [%08x] revision %d, cr=%08lx\n", +	       uc32_cpuid, (int)(uc32_cpuid >> 16) & 15, cr_alignment); + +	sprintf(init_utsname()->machine, "puv3"); +	sprintf(elf_platform, "ucv2"); +} + +/* + * cpu_init - initialise one CPU. + * + * cpu_init sets up the per-CPU stacks. + */ +void cpu_init(void) +{ +	unsigned int cpu = smp_processor_id(); +	struct stack *stk = &stacks[cpu]; + +	/* +	 * setup stacks for re-entrant exception handlers +	 */ +	__asm__ ( +	"mov.a	asr, %1\n\t" +	"add	sp, %0, %2\n\t" +	"mov.a	asr, %3\n\t" +	"add	sp, %0, %4\n\t" +	"mov.a	asr, %5\n\t" +	"add	sp, %0, %6\n\t" +	"mov.a	asr, %7" +	    : +	    : "r" (stk), +	      "r" (PSR_R_BIT | PSR_I_BIT | INTR_MODE), +	      "I" (offsetof(struct stack, irq[0])), +	      "r" (PSR_R_BIT | PSR_I_BIT | ABRT_MODE), +	      "I" (offsetof(struct stack, abt[0])), +	      "r" (PSR_R_BIT | PSR_I_BIT | EXTN_MODE), +	      "I" (offsetof(struct stack, und[0])), +	      "r" (PSR_R_BIT | PSR_I_BIT | PRIV_MODE) +	: "r30", "cc"); +} + +static int __init uc32_add_memory(unsigned long start, unsigned long size) +{ +	struct membank *bank = &meminfo.bank[meminfo.nr_banks]; + +	if (meminfo.nr_banks >= NR_BANKS) { +		printk(KERN_CRIT "NR_BANKS too low, " +			"ignoring memory at %#lx\n", start); +		return -EINVAL; +	} + +	/* +	 * Ensure that start/size are aligned to a page boundary. +	 * Size is appropriately rounded down, start is rounded up. +	 */ +	size -= start & ~PAGE_MASK; + +	bank->start = PAGE_ALIGN(start); +	bank->size  = size & PAGE_MASK; + +	/* +	 * Check whether this memory region has non-zero size or +	 * invalid node number. +	 */ +	if (bank->size == 0) +		return -EINVAL; + +	meminfo.nr_banks++; +	return 0; +} + +/* + * Pick out the memory size.  We look for mem=size@start, + * where start and size are "size[KkMm]" + */ +static int __init early_mem(char *p) +{ +	static int usermem __initdata = 1; +	unsigned long size, start; +	char *endp; + +	/* +	 * If the user specifies memory size, we +	 * blow away any automatically generated +	 * size. +	 */ +	if (usermem) { +		usermem = 0; +		meminfo.nr_banks = 0; +	} + +	start = PHYS_OFFSET; +	size  = memparse(p, &endp); +	if (*endp == '@') +		start = memparse(endp + 1, NULL); + +	uc32_add_memory(start, size); + +	return 0; +} +early_param("mem", early_mem); + +static void __init +request_standard_resources(struct meminfo *mi) +{ +	struct resource *res; +	int i; + +	kernel_code.start   = virt_to_phys(_stext); +	kernel_code.end     = virt_to_phys(_etext - 1); +	kernel_data.start   = virt_to_phys(_sdata); +	kernel_data.end     = virt_to_phys(_end - 1); + +	for (i = 0; i < mi->nr_banks; i++) { +		if (mi->bank[i].size == 0) +			continue; + +		res = alloc_bootmem_low(sizeof(*res)); +		res->name  = "System RAM"; +		res->start = mi->bank[i].start; +		res->end   = mi->bank[i].start + mi->bank[i].size - 1; +		res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; + +		request_resource(&iomem_resource, res); + +		if (kernel_code.start >= res->start && +		    kernel_code.end <= res->end) +			request_resource(res, &kernel_code); +		if (kernel_data.start >= res->start && +		    kernel_data.end <= res->end) +			request_resource(res, &kernel_data); +	} +} + +static void (*init_machine)(void) __initdata; + +static int __init customize_machine(void) +{ +	/* customizes platform devices, or adds new ones */ +	if (init_machine) +		init_machine(); +	return 0; +} +arch_initcall(customize_machine); + +void __init setup_arch(char **cmdline_p) +{ +	char *from = default_command_line; + +	setup_processor(); + +	init_mm.start_code = (unsigned long) _stext; +	init_mm.end_code   = (unsigned long) _etext; +	init_mm.end_data   = (unsigned long) _edata; +	init_mm.brk	   = (unsigned long) _end; + +	/* parse_early_param needs a boot_command_line */ +	strlcpy(boot_command_line, from, COMMAND_LINE_SIZE); + +	/* populate cmd_line too for later use, preserving boot_command_line */ +	strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE); +	*cmdline_p = cmd_line; + +	parse_early_param(); + +	uc32_memblock_init(&meminfo); + +	paging_init(); +	request_standard_resources(&meminfo); + +	cpu_init(); + +	/* +	 * Set up various architecture-specific pointers +	 */ +	init_machine = puv3_core_init; + +#ifdef CONFIG_VT +#if defined(CONFIG_VGA_CONSOLE) +	conswitchp = &vga_con; +#elif defined(CONFIG_DUMMY_CONSOLE) +	conswitchp = &dummy_con; +#endif +#endif +	early_trap_init(); +} + +static struct cpu cpuinfo_unicore; + +static int __init topology_init(void) +{ +	int i; + +	for_each_possible_cpu(i) +		register_cpu(&cpuinfo_unicore, i); + +	return 0; +} +subsys_initcall(topology_init); + +#ifdef CONFIG_HAVE_PROC_CPU +static int __init proc_cpu_init(void) +{ +	struct proc_dir_entry *res; + +	res = proc_mkdir("cpu", NULL); +	if (!res) +		return -ENOMEM; +	return 0; +} +fs_initcall(proc_cpu_init); +#endif + +static int c_show(struct seq_file *m, void *v) +{ +	seq_printf(m, "Processor\t: UniCore-II rev %d (%s)\n", +		   (int)(uc32_cpuid >> 16) & 15, elf_platform); + +	seq_printf(m, "BogoMIPS\t: %lu.%02lu\n", +		   loops_per_jiffy / (500000/HZ), +		   (loops_per_jiffy / (5000/HZ)) % 100); + +	/* dump out the processor features */ +	seq_puts(m, "Features\t: CMOV UC-F64"); + +	seq_printf(m, "\nCPU implementer\t: 0x%02x\n", uc32_cpuid >> 24); +	seq_printf(m, "CPU architecture: 2\n"); +	seq_printf(m, "CPU revision\t: %d\n", (uc32_cpuid >> 16) & 15); + +	seq_printf(m, "Cache type\t: write-back\n" +			"Cache clean\t: cp0 c5 ops\n" +			"Cache lockdown\t: not support\n" +			"Cache format\t: Harvard\n"); + +	seq_puts(m, "\n"); + +	seq_printf(m, "Hardware\t: PKUnity v3\n"); + +	return 0; +} + +static void *c_start(struct seq_file *m, loff_t *pos) +{ +	return *pos < 1 ? (void *)1 : NULL; +} + +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ +	++*pos; +	return NULL; +} + +static void c_stop(struct seq_file *m, void *v) +{ +} + +const struct seq_operations cpuinfo_op = { +	.start	= c_start, +	.next	= c_next, +	.stop	= c_stop, +	.show	= c_show +}; diff --git a/arch/unicore32/kernel/setup.h b/arch/unicore32/kernel/setup.h new file mode 100644 index 00000000000..f5c51b85ad2 --- /dev/null +++ b/arch/unicore32/kernel/setup.h @@ -0,0 +1,39 @@ +/* + * linux/arch/unicore32/kernel/setup.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UNICORE_KERNEL_SETUP_H__ +#define __UNICORE_KERNEL_SETUP_H__ + +#include <asm/hwdef-copro.h> + +extern void paging_init(void); +extern void puv3_core_init(void); +extern void cpu_init(void); + +extern void puv3_ps2_init(void); +extern void pci_puv3_preinit(void); +extern void __init puv3_init_gpio(void); + +extern void setup_mm_for_reboot(void); + +extern char __stubs_start[], __stubs_end[]; +extern char __vectors_start[], __vectors_end[]; + +extern void kernel_thread_helper(void); + +extern void __init early_signal_init(void); + +extern asmlinkage void __backtrace(void); +extern asmlinkage void c_backtrace(unsigned long fp, int pmode); + +extern void __show_regs(struct pt_regs *); + +#endif diff --git a/arch/unicore32/kernel/signal.c b/arch/unicore32/kernel/signal.c new file mode 100644 index 00000000000..6905f0ebdc7 --- /dev/null +++ b/arch/unicore32/kernel/signal.c @@ -0,0 +1,441 @@ +/* + * linux/arch/unicore32/kernel/signal.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/personality.h> +#include <linux/uaccess.h> +#include <linux/tracehook.h> +#include <linux/elf.h> +#include <linux/unistd.h> + +#include <asm/cacheflush.h> +#include <asm/ucontext.h> + +/* + * For UniCore syscalls, we encode the syscall number into the instruction. + */ +#define SWI_SYS_SIGRETURN	(0xff000000) /* error number for new abi */ +#define SWI_SYS_RT_SIGRETURN	(0xff000000 | (__NR_rt_sigreturn)) +#define SWI_SYS_RESTART		(0xff000000 | (__NR_restart_syscall)) + +#define KERN_SIGRETURN_CODE	(KUSER_VECPAGE_BASE + 0x00000500) +#define KERN_RESTART_CODE	(KERN_SIGRETURN_CODE + sizeof(sigreturn_codes)) + +const unsigned long sigreturn_codes[3] = { +	SWI_SYS_SIGRETURN, SWI_SYS_RT_SIGRETURN, +}; + +const unsigned long syscall_restart_code[2] = { +	SWI_SYS_RESTART,	/* swi	__NR_restart_syscall */ +	0x69efc004,		/* ldr	pc, [sp], #4 */ +}; + +/* + * Do a signal return; undo the signal stack.  These are aligned to 64-bit. + */ +struct sigframe { +	struct ucontext uc; +	unsigned long retcode[2]; +}; + +struct rt_sigframe { +	struct siginfo info; +	struct sigframe sig; +}; + +static int restore_sigframe(struct pt_regs *regs, struct sigframe __user *sf) +{ +	sigset_t set; +	int err; + +	err = __copy_from_user(&set, &sf->uc.uc_sigmask, sizeof(set)); +	if (err == 0) +		set_current_blocked(&set); + +	err |= __get_user(regs->UCreg_00, &sf->uc.uc_mcontext.regs.UCreg_00); +	err |= __get_user(regs->UCreg_01, &sf->uc.uc_mcontext.regs.UCreg_01); +	err |= __get_user(regs->UCreg_02, &sf->uc.uc_mcontext.regs.UCreg_02); +	err |= __get_user(regs->UCreg_03, &sf->uc.uc_mcontext.regs.UCreg_03); +	err |= __get_user(regs->UCreg_04, &sf->uc.uc_mcontext.regs.UCreg_04); +	err |= __get_user(regs->UCreg_05, &sf->uc.uc_mcontext.regs.UCreg_05); +	err |= __get_user(regs->UCreg_06, &sf->uc.uc_mcontext.regs.UCreg_06); +	err |= __get_user(regs->UCreg_07, &sf->uc.uc_mcontext.regs.UCreg_07); +	err |= __get_user(regs->UCreg_08, &sf->uc.uc_mcontext.regs.UCreg_08); +	err |= __get_user(regs->UCreg_09, &sf->uc.uc_mcontext.regs.UCreg_09); +	err |= __get_user(regs->UCreg_10, &sf->uc.uc_mcontext.regs.UCreg_10); +	err |= __get_user(regs->UCreg_11, &sf->uc.uc_mcontext.regs.UCreg_11); +	err |= __get_user(regs->UCreg_12, &sf->uc.uc_mcontext.regs.UCreg_12); +	err |= __get_user(regs->UCreg_13, &sf->uc.uc_mcontext.regs.UCreg_13); +	err |= __get_user(regs->UCreg_14, &sf->uc.uc_mcontext.regs.UCreg_14); +	err |= __get_user(regs->UCreg_15, &sf->uc.uc_mcontext.regs.UCreg_15); +	err |= __get_user(regs->UCreg_16, &sf->uc.uc_mcontext.regs.UCreg_16); +	err |= __get_user(regs->UCreg_17, &sf->uc.uc_mcontext.regs.UCreg_17); +	err |= __get_user(regs->UCreg_18, &sf->uc.uc_mcontext.regs.UCreg_18); +	err |= __get_user(regs->UCreg_19, &sf->uc.uc_mcontext.regs.UCreg_19); +	err |= __get_user(regs->UCreg_20, &sf->uc.uc_mcontext.regs.UCreg_20); +	err |= __get_user(regs->UCreg_21, &sf->uc.uc_mcontext.regs.UCreg_21); +	err |= __get_user(regs->UCreg_22, &sf->uc.uc_mcontext.regs.UCreg_22); +	err |= __get_user(regs->UCreg_23, &sf->uc.uc_mcontext.regs.UCreg_23); +	err |= __get_user(regs->UCreg_24, &sf->uc.uc_mcontext.regs.UCreg_24); +	err |= __get_user(regs->UCreg_25, &sf->uc.uc_mcontext.regs.UCreg_25); +	err |= __get_user(regs->UCreg_26, &sf->uc.uc_mcontext.regs.UCreg_26); +	err |= __get_user(regs->UCreg_fp, &sf->uc.uc_mcontext.regs.UCreg_fp); +	err |= __get_user(regs->UCreg_ip, &sf->uc.uc_mcontext.regs.UCreg_ip); +	err |= __get_user(regs->UCreg_sp, &sf->uc.uc_mcontext.regs.UCreg_sp); +	err |= __get_user(regs->UCreg_lr, &sf->uc.uc_mcontext.regs.UCreg_lr); +	err |= __get_user(regs->UCreg_pc, &sf->uc.uc_mcontext.regs.UCreg_pc); +	err |= __get_user(regs->UCreg_asr, &sf->uc.uc_mcontext.regs.UCreg_asr); + +	err |= !valid_user_regs(regs); + +	return err; +} + +asmlinkage int __sys_rt_sigreturn(struct pt_regs *regs) +{ +	struct rt_sigframe __user *frame; + +	/* Always make any pending restarted system calls return -EINTR */ +	current_thread_info()->restart_block.fn = do_no_restart_syscall; + +	/* +	 * Since we stacked the signal on a 64-bit boundary, +	 * then 'sp' should be word aligned here.  If it's +	 * not, then the user is trying to mess with us. +	 */ +	if (regs->UCreg_sp & 7) +		goto badframe; + +	frame = (struct rt_sigframe __user *)regs->UCreg_sp; + +	if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) +		goto badframe; + +	if (restore_sigframe(regs, &frame->sig)) +		goto badframe; + +	if (restore_altstack(&frame->sig.uc.uc_stack)) +		goto badframe; + +	return regs->UCreg_00; + +badframe: +	force_sig(SIGSEGV, current); +	return 0; +} + +static int setup_sigframe(struct sigframe __user *sf, struct pt_regs *regs, +		sigset_t *set) +{ +	int err = 0; + +	err |= __put_user(regs->UCreg_00, &sf->uc.uc_mcontext.regs.UCreg_00); +	err |= __put_user(regs->UCreg_01, &sf->uc.uc_mcontext.regs.UCreg_01); +	err |= __put_user(regs->UCreg_02, &sf->uc.uc_mcontext.regs.UCreg_02); +	err |= __put_user(regs->UCreg_03, &sf->uc.uc_mcontext.regs.UCreg_03); +	err |= __put_user(regs->UCreg_04, &sf->uc.uc_mcontext.regs.UCreg_04); +	err |= __put_user(regs->UCreg_05, &sf->uc.uc_mcontext.regs.UCreg_05); +	err |= __put_user(regs->UCreg_06, &sf->uc.uc_mcontext.regs.UCreg_06); +	err |= __put_user(regs->UCreg_07, &sf->uc.uc_mcontext.regs.UCreg_07); +	err |= __put_user(regs->UCreg_08, &sf->uc.uc_mcontext.regs.UCreg_08); +	err |= __put_user(regs->UCreg_09, &sf->uc.uc_mcontext.regs.UCreg_09); +	err |= __put_user(regs->UCreg_10, &sf->uc.uc_mcontext.regs.UCreg_10); +	err |= __put_user(regs->UCreg_11, &sf->uc.uc_mcontext.regs.UCreg_11); +	err |= __put_user(regs->UCreg_12, &sf->uc.uc_mcontext.regs.UCreg_12); +	err |= __put_user(regs->UCreg_13, &sf->uc.uc_mcontext.regs.UCreg_13); +	err |= __put_user(regs->UCreg_14, &sf->uc.uc_mcontext.regs.UCreg_14); +	err |= __put_user(regs->UCreg_15, &sf->uc.uc_mcontext.regs.UCreg_15); +	err |= __put_user(regs->UCreg_16, &sf->uc.uc_mcontext.regs.UCreg_16); +	err |= __put_user(regs->UCreg_17, &sf->uc.uc_mcontext.regs.UCreg_17); +	err |= __put_user(regs->UCreg_18, &sf->uc.uc_mcontext.regs.UCreg_18); +	err |= __put_user(regs->UCreg_19, &sf->uc.uc_mcontext.regs.UCreg_19); +	err |= __put_user(regs->UCreg_20, &sf->uc.uc_mcontext.regs.UCreg_20); +	err |= __put_user(regs->UCreg_21, &sf->uc.uc_mcontext.regs.UCreg_21); +	err |= __put_user(regs->UCreg_22, &sf->uc.uc_mcontext.regs.UCreg_22); +	err |= __put_user(regs->UCreg_23, &sf->uc.uc_mcontext.regs.UCreg_23); +	err |= __put_user(regs->UCreg_24, &sf->uc.uc_mcontext.regs.UCreg_24); +	err |= __put_user(regs->UCreg_25, &sf->uc.uc_mcontext.regs.UCreg_25); +	err |= __put_user(regs->UCreg_26, &sf->uc.uc_mcontext.regs.UCreg_26); +	err |= __put_user(regs->UCreg_fp, &sf->uc.uc_mcontext.regs.UCreg_fp); +	err |= __put_user(regs->UCreg_ip, &sf->uc.uc_mcontext.regs.UCreg_ip); +	err |= __put_user(regs->UCreg_sp, &sf->uc.uc_mcontext.regs.UCreg_sp); +	err |= __put_user(regs->UCreg_lr, &sf->uc.uc_mcontext.regs.UCreg_lr); +	err |= __put_user(regs->UCreg_pc, &sf->uc.uc_mcontext.regs.UCreg_pc); +	err |= __put_user(regs->UCreg_asr, &sf->uc.uc_mcontext.regs.UCreg_asr); + +	err |= __put_user(current->thread.trap_no, +			&sf->uc.uc_mcontext.trap_no); +	err |= __put_user(current->thread.error_code, +			&sf->uc.uc_mcontext.error_code); +	err |= __put_user(current->thread.address, +			&sf->uc.uc_mcontext.fault_address); +	err |= __put_user(set->sig[0], &sf->uc.uc_mcontext.oldmask); + +	err |= __copy_to_user(&sf->uc.uc_sigmask, set, sizeof(*set)); + +	return err; +} + +static inline void __user *get_sigframe(struct k_sigaction *ka, +		struct pt_regs *regs, int framesize) +{ +	unsigned long sp = regs->UCreg_sp; +	void __user *frame; + +	/* +	 * This is the X/Open sanctioned signal stack switching. +	 */ +	if ((ka->sa.sa_flags & SA_ONSTACK) && !sas_ss_flags(sp)) +		sp = current->sas_ss_sp + current->sas_ss_size; + +	/* +	 * ATPCS B01 mandates 8-byte alignment +	 */ +	frame = (void __user *)((sp - framesize) & ~7); + +	/* +	 * Check that we can actually write to the signal frame. +	 */ +	if (!access_ok(VERIFY_WRITE, frame, framesize)) +		frame = NULL; + +	return frame; +} + +static int setup_return(struct pt_regs *regs, struct k_sigaction *ka, +	     unsigned long __user *rc, void __user *frame, int usig) +{ +	unsigned long handler = (unsigned long)ka->sa.sa_handler; +	unsigned long retcode; +	unsigned long asr = regs->UCreg_asr & ~PSR_f; + +	unsigned int idx = 0; + +	if (ka->sa.sa_flags & SA_SIGINFO) +		idx += 1; + +	if (__put_user(sigreturn_codes[idx],   rc) || +	    __put_user(sigreturn_codes[idx+1], rc+1)) +		return 1; + +	retcode = KERN_SIGRETURN_CODE + (idx << 2); + +	regs->UCreg_00 = usig; +	regs->UCreg_sp = (unsigned long)frame; +	regs->UCreg_lr = retcode; +	regs->UCreg_pc = handler; +	regs->UCreg_asr = asr; + +	return 0; +} + +static int setup_frame(int usig, struct k_sigaction *ka, +		sigset_t *set, struct pt_regs *regs) +{ +	struct sigframe __user *frame = get_sigframe(ka, regs, sizeof(*frame)); +	int err = 0; + +	if (!frame) +		return 1; + +	/* +	 * Set uc.uc_flags to a value which sc.trap_no would never have. +	 */ +	err |= __put_user(0x5ac3c35a, &frame->uc.uc_flags); + +	err |= setup_sigframe(frame, regs, set); +	if (err == 0) +		err |= setup_return(regs, ka, frame->retcode, frame, usig); + +	return err; +} + +static int setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info, +	       sigset_t *set, struct pt_regs *regs) +{ +	struct rt_sigframe __user *frame = +			get_sigframe(ka, regs, sizeof(*frame)); +	int err = 0; + +	if (!frame) +		return 1; + +	err |= copy_siginfo_to_user(&frame->info, info); + +	err |= __put_user(0, &frame->sig.uc.uc_flags); +	err |= __put_user(NULL, &frame->sig.uc.uc_link); +	err |= __save_altstack(&frame->sig.uc.uc_stack, regs->UCreg_sp); +	err |= setup_sigframe(&frame->sig, regs, set); +	if (err == 0) +		err |= setup_return(regs, ka, frame->sig.retcode, frame, usig); + +	if (err == 0) { +		/* +		 * For realtime signals we must also set the second and third +		 * arguments for the signal handler. +		 */ +		regs->UCreg_01 = (unsigned long)&frame->info; +		regs->UCreg_02 = (unsigned long)&frame->sig.uc; +	} + +	return err; +} + +static inline void setup_syscall_restart(struct pt_regs *regs) +{ +	regs->UCreg_00 = regs->UCreg_ORIG_00; +	regs->UCreg_pc -= 4; +} + +/* + * OK, we're invoking a handler + */ +static void handle_signal(unsigned long sig, struct k_sigaction *ka, +	      siginfo_t *info, struct pt_regs *regs, int syscall) +{ +	struct thread_info *thread = current_thread_info(); +	struct task_struct *tsk = current; +	sigset_t *oldset = sigmask_to_save(); +	int usig = sig; +	int ret; + +	/* +	 * If we were from a system call, check for system call restarting... +	 */ +	if (syscall) { +		switch (regs->UCreg_00) { +		case -ERESTART_RESTARTBLOCK: +		case -ERESTARTNOHAND: +			regs->UCreg_00 = -EINTR; +			break; +		case -ERESTARTSYS: +			if (!(ka->sa.sa_flags & SA_RESTART)) { +				regs->UCreg_00 = -EINTR; +				break; +			} +			/* fallthrough */ +		case -ERESTARTNOINTR: +			setup_syscall_restart(regs); +		} +	} + +	/* +	 * translate the signal +	 */ +	if (usig < 32 && thread->exec_domain +			&& thread->exec_domain->signal_invmap) +		usig = thread->exec_domain->signal_invmap[usig]; + +	/* +	 * Set up the stack frame +	 */ +	if (ka->sa.sa_flags & SA_SIGINFO) +		ret = setup_rt_frame(usig, ka, info, oldset, regs); +	else +		ret = setup_frame(usig, ka, oldset, regs); + +	/* +	 * Check that the resulting registers are actually sane. +	 */ +	ret |= !valid_user_regs(regs); + +	if (ret != 0) { +		force_sigsegv(sig, tsk); +		return; +	} + +	signal_delivered(sig, info, ka, regs, 0); +} + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + * + * Note that we go through the signals twice: once to check the signals that + * the kernel can handle, and then we build all the user-level signal handling + * stack-frames in one go after that. + */ +static void do_signal(struct pt_regs *regs, int syscall) +{ +	struct k_sigaction ka; +	siginfo_t info; +	int signr; + +	/* +	 * We want the common case to go fast, which +	 * is why we may in certain cases get here from +	 * kernel mode. Just return without doing anything +	 * if so. +	 */ +	if (!user_mode(regs)) +		return; + +	signr = get_signal_to_deliver(&info, &ka, regs, NULL); +	if (signr > 0) { +		handle_signal(signr, &ka, &info, regs, syscall); +		return; +	} + +	/* +	 * No signal to deliver to the process - restart the syscall. +	 */ +	if (syscall) { +		if (regs->UCreg_00 == -ERESTART_RESTARTBLOCK) { +				u32 __user *usp; + +				regs->UCreg_sp -= 4; +				usp = (u32 __user *)regs->UCreg_sp; + +				if (put_user(regs->UCreg_pc, usp) == 0) { +					regs->UCreg_pc = KERN_RESTART_CODE; +				} else { +					regs->UCreg_sp += 4; +					force_sigsegv(0, current); +				} +		} +		if (regs->UCreg_00 == -ERESTARTNOHAND || +		    regs->UCreg_00 == -ERESTARTSYS || +		    regs->UCreg_00 == -ERESTARTNOINTR) { +			setup_syscall_restart(regs); +		} +	} +	/* If there's no signal to deliver, we just put the saved +	 * sigmask back. +	 */ +	restore_saved_sigmask(); +} + +asmlinkage void do_notify_resume(struct pt_regs *regs, +		unsigned int thread_flags, int syscall) +{ +	if (thread_flags & _TIF_SIGPENDING) +		do_signal(regs, syscall); + +	if (thread_flags & _TIF_NOTIFY_RESUME) { +		clear_thread_flag(TIF_NOTIFY_RESUME); +		tracehook_notify_resume(regs); +	} +} + +/* + * Copy signal return handlers into the vector page, and + * set sigreturn to be a pointer to these. + */ +void __init early_signal_init(void) +{ +	memcpy((void *)kuser_vecpage_to_vectors(KERN_SIGRETURN_CODE), +			sigreturn_codes, sizeof(sigreturn_codes)); +	memcpy((void *)kuser_vecpage_to_vectors(KERN_RESTART_CODE), +			syscall_restart_code, sizeof(syscall_restart_code)); +	/* Need not to flush icache, since early_trap_init will do it last. */ +} diff --git a/arch/unicore32/kernel/sleep.S b/arch/unicore32/kernel/sleep.S new file mode 100644 index 00000000000..607a104aec5 --- /dev/null +++ b/arch/unicore32/kernel/sleep.S @@ -0,0 +1,202 @@ +/* + * linux/arch/unicore32/kernel/sleep.S + * + * Code specific to PKUnity SoC and UniCore ISA + * + *	Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn> + *	Copyright (C) 2001-2010 Guan Xuetao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/linkage.h> +#include <asm/assembler.h> +#include <mach/hardware.h> + +		.text + +pkunity_cpu_save_cp: + +	@ get coprocessor registers + +	movc	r3, p0.c7, #0			@ PID +	movc	r4, p0.c2, #0			@ translation table base addr +	movc	r5, p0.c1, #0			@ control reg + + +	@ store them plus current virtual stack ptr on stack +	mov	r6, sp +	stm.w	(r3 - r6), [sp-] + +	mov	pc, lr + +pkunity_cpu_save_sp: +	@ preserve phys address of stack +	mov	r0, sp +	stw.w	lr, [sp+], #-4 +	b.l	sleep_phys_sp +	ldw	r1, =sleep_save_sp +	stw	r0, [r1] +	ldw.w	pc, [sp]+, #4 + +/* + * puv3_cpu_suspend() + * + * Forces CPU into sleep state. + * + * r0 = value for PWRMODE M field for desired sleep state + */ + +ENTRY(puv3_cpu_suspend) +	stm.w	(r16 - r27, lr), [sp-]		@ save registers on stack +	stm.w	(r4 - r15), [sp-]		@ save registers on stack + +#ifdef	CONFIG_UNICORE_FPU_F64 +	sfm.w	(f0  - f7 ), [sp-] +	sfm.w	(f8  - f15), [sp-] +	sfm.w	(f16 - f23), [sp-] +	sfm.w	(f24 - f31), [sp-] +	cff	r4, s31 +	stm.w	(r4), [sp-] +#endif +	b.l	pkunity_cpu_save_cp + +	b.l	pkunity_cpu_save_sp + +	@ clean data cache +	mov	r1, #0 +	movc	p0.c5, r1, #14 +	nop +	nop +	nop +	nop + + + +	@ DDR2 BaseAddr +	ldw	r0, =(PKUNITY_DDR2CTRL_BASE) + +	@ PM BaseAddr +	ldw	r1, =(PKUNITY_PM_BASE) + +	@ set PLL_SYS_CFG reg, 275 +	movl	r6, #0x00002401 +	stw	r6, [r1+], #0x18 +	@ set PLL_DDR_CFG reg, 66MHz +	movl	r6, #0x00100c00 +	stw	r6, [r1+], #0x1c + +	@ set wake up source +	movl	r8, #0x800001ff		@ epip4d +	stw	r8, [r1+], #0xc + +	@ set PGSR +	movl	r5, #0x40000 +	stw	r5, [r1+], #0x10 + +	@ prepare DDR2 refresh settings +	ldw	r5, [r0+], #0x24 +	or	r5, r5, #0x00000001 + +	@ prepare PMCR for PLL changing +	movl	r6, #0xc + +	@ prepare for closing PLL +	movl	r7, #0x1 + +	@ prepare sleep mode +	mov	r8, #0x1 + +@	movl	r0, 0x11111111 +@	put_word_ocd r0 +	b	pkunity_cpu_do_suspend + +	.ltorg +	.align	5 +pkunity_cpu_do_suspend: +	b	101f +	@ put DDR2 into self-refresh +100:	stw	r5, [r0+], #0x24 +	@ change PLL +	stw	r6, [r1] +	b	1f + +	.ltorg +	.align	5 +101:	b	102f +	@ wait for PLL changing complete +1:	ldw	r6, [r1+], #0x44 +	csub.a	r6, #0x1 +	bne	1b +	b	2f + +	.ltorg +	.align	5 +102:	b	100b +	@ close PLL +2:	stw	r7, [r1+], #0x4 +	@ enter sleep mode +	stw	r8, [r1] +3:	b	3b + + + + +/* + * puv3_cpu_resume() + * + * entry point from bootloader into kernel during resume + * + * Note: Yes, part of the following code is located into the .data section. + *       This is to allow sleep_save_sp to be accessed with a relative load + *       while we can't rely on any MMU translation.  We could have put + *       sleep_save_sp in the .text section as well, but some setups might + *       insist on it to be truly read-only. + */ + +	.data +	.align 5 +ENTRY(puv3_cpu_resume) +@	movl	r0, 0x20202020 +@	put_word_ocd r0 + +	ldw	r0, sleep_save_sp		@ stack phys addr +	ldw	r2, =resume_after_mmu		@ its absolute virtual address +	ldm	(r3 - r6), [r0]+		@ CP regs + virt stack ptr +	mov	sp, r6				@ CP regs + virt stack ptr + +	mov	r1, #0 +	movc	p0.c6, r1, #6			@ invalidate I & D TLBs +	movc	p0.c5, r1, #28			@ invalidate I & D caches, BTB + +	movc	p0.c7, r3, #0			@ PID +	movc	p0.c2, r4, #0			@ translation table base addr +	movc	p0.c1, r5, #0			@ control reg, turn on mmu +	nop +	jump	r2 +	nop +	nop +	nop +	nop +	nop + +sleep_save_sp: +	.word	0				@ preserve stack phys ptr here + +	.text +resume_after_mmu: +@	movl	r0, 0x30303030 +@	put_word_ocd r0 + +#ifdef	CONFIG_UNICORE_FPU_F64 +	lfm.w	(f0  - f7 ), [sp]+ +	lfm.w	(f8  - f15), [sp]+ +	lfm.w	(f16 - f23), [sp]+ +	lfm.w	(f24 - f31), [sp]+ +	ldm.w	(r4), [sp]+ +	ctf	r4, s31 +#endif +	ldm.w	(r4 - r15), [sp]+		@ restore registers from stack +	ldm.w	(r16 - r27, pc), [sp]+		@ return to caller diff --git a/arch/unicore32/kernel/stacktrace.c b/arch/unicore32/kernel/stacktrace.c new file mode 100644 index 00000000000..b34030bdabe --- /dev/null +++ b/arch/unicore32/kernel/stacktrace.c @@ -0,0 +1,131 @@ +/* + * linux/arch/unicore32/kernel/stacktrace.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/stacktrace.h> + +#include <asm/stacktrace.h> + +#if defined(CONFIG_FRAME_POINTER) +/* + * Unwind the current stack frame and store the new register values in the + * structure passed as argument. Unwinding is equivalent to a function return, + * hence the new PC value rather than LR should be used for backtrace. + * + * With framepointer enabled, a simple function prologue looks like this: + *	mov	ip, sp + *	stmdb	sp!, {fp, ip, lr, pc} + *	sub	fp, ip, #4 + * + * A simple function epilogue looks like this: + *	ldm	sp, {fp, sp, pc} + * + * Note that with framepointer enabled, even the leaf functions have the same + * prologue and epilogue, therefore we can ignore the LR value in this case. + */ +int notrace unwind_frame(struct stackframe *frame) +{ +	unsigned long high, low; +	unsigned long fp = frame->fp; + +	/* only go to a higher address on the stack */ +	low = frame->sp; +	high = ALIGN(low, THREAD_SIZE); + +	/* check current frame pointer is within bounds */ +	if (fp < (low + 12) || fp + 4 >= high) +		return -EINVAL; + +	/* restore the registers from the stack frame */ +	frame->fp = *(unsigned long *)(fp - 12); +	frame->sp = *(unsigned long *)(fp - 8); +	frame->pc = *(unsigned long *)(fp - 4); + +	return 0; +} +#endif + +void notrace walk_stackframe(struct stackframe *frame, +		     int (*fn)(struct stackframe *, void *), void *data) +{ +	while (1) { +		int ret; + +		if (fn(frame, data)) +			break; +		ret = unwind_frame(frame); +		if (ret < 0) +			break; +	} +} +EXPORT_SYMBOL(walk_stackframe); + +#ifdef CONFIG_STACKTRACE +struct stack_trace_data { +	struct stack_trace *trace; +	unsigned int no_sched_functions; +	unsigned int skip; +}; + +static int save_trace(struct stackframe *frame, void *d) +{ +	struct stack_trace_data *data = d; +	struct stack_trace *trace = data->trace; +	unsigned long addr = frame->pc; + +	if (data->no_sched_functions && in_sched_functions(addr)) +		return 0; +	if (data->skip) { +		data->skip--; +		return 0; +	} + +	trace->entries[trace->nr_entries++] = addr; + +	return trace->nr_entries >= trace->max_entries; +} + +void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) +{ +	struct stack_trace_data data; +	struct stackframe frame; + +	data.trace = trace; +	data.skip = trace->skip; + +	if (tsk != current) { +		data.no_sched_functions = 1; +		frame.fp = thread_saved_fp(tsk); +		frame.sp = thread_saved_sp(tsk); +		frame.lr = 0;		/* recovered from the stack */ +		frame.pc = thread_saved_pc(tsk); +	} else { +		register unsigned long current_sp asm("sp"); + +		data.no_sched_functions = 0; +		frame.fp = (unsigned long)__builtin_frame_address(0); +		frame.sp = current_sp; +		frame.lr = (unsigned long)__builtin_return_address(0); +		frame.pc = (unsigned long)save_stack_trace_tsk; +	} + +	walk_stackframe(&frame, save_trace, &data); +	if (trace->nr_entries < trace->max_entries) +		trace->entries[trace->nr_entries++] = ULONG_MAX; +} + +void save_stack_trace(struct stack_trace *trace) +{ +	save_stack_trace_tsk(current, trace); +} +EXPORT_SYMBOL_GPL(save_stack_trace); +#endif diff --git a/arch/unicore32/kernel/sys.c b/arch/unicore32/kernel/sys.c new file mode 100644 index 00000000000..f9e86253931 --- /dev/null +++ b/arch/unicore32/kernel/sys.c @@ -0,0 +1,40 @@ +/* + * linux/arch/unicore32/kernel/sys.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/sem.h> +#include <linux/msg.h> +#include <linux/shm.h> +#include <linux/stat.h> +#include <linux/syscalls.h> +#include <linux/mman.h> +#include <linux/fs.h> +#include <linux/file.h> +#include <linux/ipc.h> +#include <linux/uaccess.h> + +#include <asm/syscalls.h> +#include <asm/cacheflush.h> + +/* Provide the actual syscall number to call mapping. */ +#undef __SYSCALL +#define __SYSCALL(nr, call)	[nr] = (call), + +#define sys_mmap2 sys_mmap_pgoff +/* Note that we don't include <linux/unistd.h> but <asm/unistd.h> */ +void *sys_call_table[__NR_syscalls] = { +	[0 ... __NR_syscalls-1] = sys_ni_syscall, +#include <asm/unistd.h> +}; diff --git a/arch/unicore32/kernel/time.c b/arch/unicore32/kernel/time.c new file mode 100644 index 00000000000..d3824b2ff64 --- /dev/null +++ b/arch/unicore32/kernel/time.c @@ -0,0 +1,143 @@ +/* + * linux/arch/unicore32/kernel/time.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + *	Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn> + *	Copyright (C) 2001-2010 Guan Xuetao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/timex.h> +#include <linux/clockchips.h> + +#include <mach/hardware.h> + +#define MIN_OSCR_DELTA 2 + +static irqreturn_t puv3_ost0_interrupt(int irq, void *dev_id) +{ +	struct clock_event_device *c = dev_id; + +	/* Disarm the compare/match, signal the event. */ +	writel(readl(OST_OIER) & ~OST_OIER_E0, OST_OIER); +	writel(readl(OST_OSSR) & ~OST_OSSR_M0, OST_OSSR); +	c->event_handler(c); + +	return IRQ_HANDLED; +} + +static int +puv3_osmr0_set_next_event(unsigned long delta, struct clock_event_device *c) +{ +	unsigned long next, oscr; + +	writel(readl(OST_OIER) | OST_OIER_E0, OST_OIER); +	next = readl(OST_OSCR) + delta; +	writel(next, OST_OSMR0); +	oscr = readl(OST_OSCR); + +	return (signed)(next - oscr) <= MIN_OSCR_DELTA ? -ETIME : 0; +} + +static void +puv3_osmr0_set_mode(enum clock_event_mode mode, struct clock_event_device *c) +{ +	switch (mode) { +	case CLOCK_EVT_MODE_ONESHOT: +	case CLOCK_EVT_MODE_UNUSED: +	case CLOCK_EVT_MODE_SHUTDOWN: +		writel(readl(OST_OIER) & ~OST_OIER_E0, OST_OIER); +		writel(readl(OST_OSSR) & ~OST_OSSR_M0, OST_OSSR); +		break; + +	case CLOCK_EVT_MODE_RESUME: +	case CLOCK_EVT_MODE_PERIODIC: +		break; +	} +} + +static struct clock_event_device ckevt_puv3_osmr0 = { +	.name		= "osmr0", +	.features	= CLOCK_EVT_FEAT_ONESHOT, +	.rating		= 200, +	.set_next_event	= puv3_osmr0_set_next_event, +	.set_mode	= puv3_osmr0_set_mode, +}; + +static cycle_t puv3_read_oscr(struct clocksource *cs) +{ +	return readl(OST_OSCR); +} + +static struct clocksource cksrc_puv3_oscr = { +	.name		= "oscr", +	.rating		= 200, +	.read		= puv3_read_oscr, +	.mask		= CLOCKSOURCE_MASK(32), +	.flags		= CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static struct irqaction puv3_timer_irq = { +	.name		= "ost0", +	.flags		= IRQF_TIMER | IRQF_IRQPOLL, +	.handler	= puv3_ost0_interrupt, +	.dev_id		= &ckevt_puv3_osmr0, +}; + +void __init time_init(void) +{ +	writel(0, OST_OIER);		/* disable any timer interrupts */ +	writel(0, OST_OSSR);		/* clear status on all timers */ + +	clockevents_calc_mult_shift(&ckevt_puv3_osmr0, CLOCK_TICK_RATE, 5); + +	ckevt_puv3_osmr0.max_delta_ns = +		clockevent_delta2ns(0x7fffffff, &ckevt_puv3_osmr0); +	ckevt_puv3_osmr0.min_delta_ns = +		clockevent_delta2ns(MIN_OSCR_DELTA * 2, &ckevt_puv3_osmr0) + 1; +	ckevt_puv3_osmr0.cpumask = cpumask_of(0); + +	setup_irq(IRQ_TIMER0, &puv3_timer_irq); + +	clocksource_register_hz(&cksrc_puv3_oscr, CLOCK_TICK_RATE); +	clockevents_register_device(&ckevt_puv3_osmr0); +} + +#ifdef CONFIG_PM +unsigned long osmr[4], oier; + +void puv3_timer_suspend(void) +{ +	osmr[0] = readl(OST_OSMR0); +	osmr[1] = readl(OST_OSMR1); +	osmr[2] = readl(OST_OSMR2); +	osmr[3] = readl(OST_OSMR3); +	oier = readl(OST_OIER); +} + +void puv3_timer_resume(void) +{ +	writel(0, OST_OSSR); +	writel(osmr[0], OST_OSMR0); +	writel(osmr[1], OST_OSMR1); +	writel(osmr[2], OST_OSMR2); +	writel(osmr[3], OST_OSMR3); +	writel(oier, OST_OIER); + +	/* +	 * OSMR0 is the system timer: make sure OSCR is sufficiently behind +	 */ +	writel(readl(OST_OSMR0) - LATCH, OST_OSCR); +} +#else +void puv3_timer_suspend(void) { }; +void puv3_timer_resume(void) { }; +#endif + diff --git a/arch/unicore32/kernel/traps.c b/arch/unicore32/kernel/traps.c new file mode 100644 index 00000000000..c54e32410ea --- /dev/null +++ b/arch/unicore32/kernel/traps.c @@ -0,0 +1,324 @@ +/* + * linux/arch/unicore32/kernel/traps.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * 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. + * + *  'traps.c' handles hardware exceptions after we have saved some state. + *  Mostly a debugging aid, but will probably kill the offending process. + */ +#include <linux/module.h> +#include <linux/signal.h> +#include <linux/spinlock.h> +#include <linux/personality.h> +#include <linux/kallsyms.h> +#include <linux/kdebug.h> +#include <linux/uaccess.h> +#include <linux/delay.h> +#include <linux/hardirq.h> +#include <linux/init.h> +#include <linux/atomic.h> +#include <linux/unistd.h> + +#include <asm/cacheflush.h> +#include <asm/traps.h> + +#include "setup.h" + +static void dump_mem(const char *, const char *, unsigned long, unsigned long); + +void dump_backtrace_entry(unsigned long where, +		unsigned long from, unsigned long frame) +{ +#ifdef CONFIG_KALLSYMS +	printk(KERN_DEFAULT "[<%08lx>] (%pS) from [<%08lx>] (%pS)\n", +			where, (void *)where, from, (void *)from); +#else +	printk(KERN_DEFAULT "Function entered at [<%08lx>] from [<%08lx>]\n", +			where, from); +#endif +} + +/* + * Stack pointers should always be within the kernels view of + * physical memory.  If it is not there, then we can't dump + * out any information relating to the stack. + */ +static int verify_stack(unsigned long sp) +{ +	if (sp < PAGE_OFFSET || +	    (sp > (unsigned long)high_memory && high_memory != NULL)) +		return -EFAULT; + +	return 0; +} + +/* + * Dump out the contents of some memory nicely... + */ +static void dump_mem(const char *lvl, const char *str, unsigned long bottom, +		     unsigned long top) +{ +	unsigned long first; +	mm_segment_t fs; +	int i; + +	/* +	 * We need to switch to kernel mode so that we can use __get_user +	 * to safely read from kernel space.  Note that we now dump the +	 * code first, just in case the backtrace kills us. +	 */ +	fs = get_fs(); +	set_fs(KERNEL_DS); + +	printk(KERN_DEFAULT "%s%s(0x%08lx to 0x%08lx)\n", +			lvl, str, bottom, top); + +	for (first = bottom & ~31; first < top; first += 32) { +		unsigned long p; +		char str[sizeof(" 12345678") * 8 + 1]; + +		memset(str, ' ', sizeof(str)); +		str[sizeof(str) - 1] = '\0'; + +		for (p = first, i = 0; i < 8 && p < top; i++, p += 4) { +			if (p >= bottom && p < top) { +				unsigned long val; +				if (__get_user(val, (unsigned long *)p) == 0) +					sprintf(str + i * 9, " %08lx", val); +				else +					sprintf(str + i * 9, " ????????"); +			} +		} +		printk(KERN_DEFAULT "%s%04lx:%s\n", lvl, first & 0xffff, str); +	} + +	set_fs(fs); +} + +static void dump_instr(const char *lvl, struct pt_regs *regs) +{ +	unsigned long addr = instruction_pointer(regs); +	const int width = 8; +	mm_segment_t fs; +	char str[sizeof("00000000 ") * 5 + 2 + 1], *p = str; +	int i; + +	/* +	 * We need to switch to kernel mode so that we can use __get_user +	 * to safely read from kernel space.  Note that we now dump the +	 * code first, just in case the backtrace kills us. +	 */ +	fs = get_fs(); +	set_fs(KERNEL_DS); + +	for (i = -4; i < 1; i++) { +		unsigned int val, bad; + +		bad = __get_user(val, &((u32 *)addr)[i]); + +		if (!bad) +			p += sprintf(p, i == 0 ? "(%0*x) " : "%0*x ", +					width, val); +		else { +			p += sprintf(p, "bad PC value"); +			break; +		} +	} +	printk(KERN_DEFAULT "%sCode: %s\n", lvl, str); + +	set_fs(fs); +} + +static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) +{ +	unsigned int fp, mode; +	int ok = 1; + +	printk(KERN_DEFAULT "Backtrace: "); + +	if (!tsk) +		tsk = current; + +	if (regs) { +		fp = regs->UCreg_fp; +		mode = processor_mode(regs); +	} else if (tsk != current) { +		fp = thread_saved_fp(tsk); +		mode = 0x10; +	} else { +		asm("mov %0, fp" : "=r" (fp) : : "cc"); +		mode = 0x10; +	} + +	if (!fp) { +		printk("no frame pointer"); +		ok = 0; +	} else if (verify_stack(fp)) { +		printk("invalid frame pointer 0x%08x", fp); +		ok = 0; +	} else if (fp < (unsigned long)end_of_stack(tsk)) +		printk("frame pointer underflow"); +	printk("\n"); + +	if (ok) +		c_backtrace(fp, mode); +} + +void show_stack(struct task_struct *tsk, unsigned long *sp) +{ +	dump_backtrace(NULL, tsk); +	barrier(); +} + +static int __die(const char *str, int err, struct thread_info *thread, +		struct pt_regs *regs) +{ +	struct task_struct *tsk = thread->task; +	static int die_counter; +	int ret; + +	printk(KERN_EMERG "Internal error: %s: %x [#%d]\n", +	       str, err, ++die_counter); + +	/* trap and error numbers are mostly meaningless on UniCore */ +	ret = notify_die(DIE_OOPS, str, regs, err, tsk->thread.trap_no, \ +			SIGSEGV); +	if (ret == NOTIFY_STOP) +		return ret; + +	print_modules(); +	__show_regs(regs); +	printk(KERN_EMERG "Process %.*s (pid: %d, stack limit = 0x%p)\n", +		TASK_COMM_LEN, tsk->comm, task_pid_nr(tsk), thread + 1); + +	if (!user_mode(regs) || in_interrupt()) { +		dump_mem(KERN_EMERG, "Stack: ", regs->UCreg_sp, +			 THREAD_SIZE + (unsigned long)task_stack_page(tsk)); +		dump_backtrace(regs, tsk); +		dump_instr(KERN_EMERG, regs); +	} + +	return ret; +} + +DEFINE_SPINLOCK(die_lock); + +/* + * This function is protected against re-entrancy. + */ +void die(const char *str, struct pt_regs *regs, int err) +{ +	struct thread_info *thread = current_thread_info(); +	int ret; + +	oops_enter(); + +	spin_lock_irq(&die_lock); +	console_verbose(); +	bust_spinlocks(1); +	ret = __die(str, err, thread, regs); + +	bust_spinlocks(0); +	add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); +	spin_unlock_irq(&die_lock); +	oops_exit(); + +	if (in_interrupt()) +		panic("Fatal exception in interrupt"); +	if (panic_on_oops) +		panic("Fatal exception"); +	if (ret != NOTIFY_STOP) +		do_exit(SIGSEGV); +} + +void uc32_notify_die(const char *str, struct pt_regs *regs, +		struct siginfo *info, unsigned long err, unsigned long trap) +{ +	if (user_mode(regs)) { +		current->thread.error_code = err; +		current->thread.trap_no = trap; + +		force_sig_info(info->si_signo, info, current); +	} else +		die(str, regs, err); +} + +/* + * bad_mode handles the impossible case in the vectors.  If you see one of + * these, then it's extremely serious, and could mean you have buggy hardware. + * It never returns, and never tries to sync.  We hope that we can at least + * dump out some state information... + */ +asmlinkage void bad_mode(struct pt_regs *regs, unsigned int reason) +{ +	console_verbose(); + +	printk(KERN_CRIT "Bad mode detected with reason 0x%x\n", reason); + +	die("Oops - bad mode", regs, 0); +	local_irq_disable(); +	panic("bad mode"); +} + +void __pte_error(const char *file, int line, unsigned long val) +{ +	printk(KERN_DEFAULT "%s:%d: bad pte %08lx.\n", file, line, val); +} + +void __pmd_error(const char *file, int line, unsigned long val) +{ +	printk(KERN_DEFAULT "%s:%d: bad pmd %08lx.\n", file, line, val); +} + +void __pgd_error(const char *file, int line, unsigned long val) +{ +	printk(KERN_DEFAULT "%s:%d: bad pgd %08lx.\n", file, line, val); +} + +asmlinkage void __div0(void) +{ +	printk(KERN_DEFAULT "Division by zero in kernel.\n"); +	dump_stack(); +} +EXPORT_SYMBOL(__div0); + +void abort(void) +{ +	BUG(); + +	/* if that doesn't kill us, halt */ +	panic("Oops failed to kill thread"); +} +EXPORT_SYMBOL(abort); + +void __init trap_init(void) +{ +	return; +} + +void __init early_trap_init(void) +{ +	unsigned long vectors = VECTORS_BASE; + +	/* +	 * Copy the vectors, stubs (in entry-unicore.S) +	 * into the vector page, mapped at 0xffff0000, and ensure these +	 * are visible to the instruction stream. +	 */ +	memcpy((void *)vectors, +			__vectors_start, +			__vectors_end - __vectors_start); +	memcpy((void *)vectors + 0x200, +			__stubs_start, +			__stubs_end - __stubs_start); + +	early_signal_init(); + +	flush_icache_range(vectors, vectors + PAGE_SIZE); +} diff --git a/arch/unicore32/kernel/vmlinux.lds.S b/arch/unicore32/kernel/vmlinux.lds.S new file mode 100644 index 00000000000..77e407e49a6 --- /dev/null +++ b/arch/unicore32/kernel/vmlinux.lds.S @@ -0,0 +1,62 @@ +/* + * linux/arch/unicore32/kernel/vmlinux.lds.S + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <asm-generic/vmlinux.lds.h> +#include <asm/thread_info.h> +#include <asm/memory.h> +#include <asm/page.h> +#include <asm/cache.h> + +OUTPUT_ARCH(unicore32) +ENTRY(stext) + +jiffies = jiffies_64; + +SECTIONS +{ +	. = PAGE_OFFSET + KERNEL_IMAGE_START; + +	_text = .; +	__init_begin = .; +	HEAD_TEXT_SECTION +	INIT_TEXT_SECTION(PAGE_SIZE) +	INIT_DATA_SECTION(16) +	PERCPU_SECTION(L1_CACHE_BYTES) +	__init_end = .; + +	_stext = .; +	.text : {		/* Real text segment */ +		TEXT_TEXT +		SCHED_TEXT +		LOCK_TEXT + +		*(.fixup) +		*(.gnu.warning) +	} +	_etext = .; + +	_sdata = .; +	RO_DATA_SECTION(PAGE_SIZE) +	RW_DATA_SECTION(L1_CACHE_BYTES, PAGE_SIZE, THREAD_SIZE) +	_edata = .; + +	EXCEPTION_TABLE(L1_CACHE_BYTES) +	NOTES + +	BSS_SECTION(0, 0, 0) +	_end = .; + +	STABS_DEBUG +	DWARF_DEBUG + +	DISCARDS		/* Exit code and data */ +} diff --git a/arch/unicore32/lib/Makefile b/arch/unicore32/lib/Makefile new file mode 100644 index 00000000000..87229a558b3 --- /dev/null +++ b/arch/unicore32/lib/Makefile @@ -0,0 +1,27 @@ +# +# linux/arch/unicore32/lib/Makefile +# +# Copyright (C) 2001-2010 GUAN Xue-tao +# + +lib-y	:= backtrace.o delay.o findbit.o +lib-y	+= strncpy_from_user.o strnlen_user.o +lib-y	+= clear_user.o copy_page.o +lib-y	+= copy_from_user.o copy_to_user.o + +GNU_LIBC_A		:= $(shell $(CC) $(KBUILD_CFLAGS) -print-file-name=libc.a) +GNU_LIBC_A_OBJS		:= memchr.o memcpy.o memmove.o memset.o +GNU_LIBC_A_OBJS		+= strchr.o strrchr.o +GNU_LIBC_A_OBJS		+= rawmemchr.o			# needed by strrchr.o + +GNU_LIBGCC_A		:= $(shell $(CC) $(KBUILD_CFLAGS) -print-file-name=libgcc.a) +GNU_LIBGCC_A_OBJS	:= _ashldi3.o _ashrdi3.o _lshrdi3.o +GNU_LIBGCC_A_OBJS	+= _divsi3.o _modsi3.o _ucmpdi2.o _umodsi3.o _udivsi3.o + +lib-y	+= $(GNU_LIBC_A_OBJS) $(GNU_LIBGCC_A_OBJS) + +$(addprefix $(obj)/, $(GNU_LIBC_A_OBJS)): +	$(Q)$(AR) p $(GNU_LIBC_A) $(notdir $@) > $@ + +$(addprefix $(obj)/, $(GNU_LIBGCC_A_OBJS)): +	$(Q)$(AR) p $(GNU_LIBGCC_A) $(notdir $@) > $@ diff --git a/arch/unicore32/lib/backtrace.S b/arch/unicore32/lib/backtrace.S new file mode 100644 index 00000000000..ef01d77f2f6 --- /dev/null +++ b/arch/unicore32/lib/backtrace.S @@ -0,0 +1,163 @@ +/* + * linux/arch/unicore32/lib/backtrace.S + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/linkage.h> +#include <asm/assembler.h> +		.text + +@ fp is 0 or stack frame + +#define frame	v4 +#define sv_fp	v5 +#define sv_pc	v6 +#define offset	v8 + +ENTRY(__backtrace) +		mov	r0, fp + +ENTRY(c_backtrace) + +#if !defined(CONFIG_FRAME_POINTER) || !defined(CONFIG_PRINTK) +		mov	pc, lr +ENDPROC(__backtrace) +ENDPROC(c_backtrace) +#else +		stm.w	(v4 - v8, lr), [sp-]	@ Save an extra register +						@ so we have a location... +		mov.a	frame, r0		@ if frame pointer is zero +		beq	no_frame		@ we have no stack frames + +1:		stm.w	(pc), [sp-]		@ calculate offset of PC stored +		ldw.w	r0, [sp]+, #4		@ by stmfd for this CPU +		adr	r1, 1b +		sub	offset, r0, r1 + +/* + * Stack frame layout: + *             optionally saved caller registers (r4 - r10) + *             saved fp + *             saved sp + *             saved lr + *    frame => saved pc + *             optionally saved arguments (r0 - r3) + * saved sp => <next word> + * + * Functions start with the following code sequence: + *                  mov   ip, sp + *                  stm.w (r0 - r3), [sp-] (optional) + * corrected pc =>  stm.w sp, (..., fp, ip, lr, pc) + */ +for_each_frame: + +1001:		ldw	sv_pc, [frame+], #0	@ get saved pc +1002:		ldw	sv_fp, [frame+], #-12	@ get saved fp + +		sub	sv_pc, sv_pc, offset	@ Correct PC for prefetching + +1003:		ldw	r2, [sv_pc+], #-4	@ if stmfd sp, {args} exists, +		ldw	r3, .Ldsi+4		@ adjust saved 'pc' back one +		cxor.a	r3, r2 >> #14		@ instruction +		beq	201f +		sub	r0, sv_pc, #4		@ allow for mov +		b	202f +201: +		sub	r0, sv_pc, #8		@ allow for mov + stmia +202: +		ldw	r1, [frame+], #-4	@ get saved lr +		mov	r2, frame +		b.l	dump_backtrace_entry + +		ldw	r1, [sv_pc+], #-4	@ if stmfd sp, {args} exists, +		ldw	r3, .Ldsi+4 +		cxor.a	r3, r1 >> #14 +		bne	1004f +		ldw	r0, [frame+], #-8	@ get sp +		sub	r0, r0, #4		@ point at the last arg +		b.l	.Ldumpstm		@ dump saved registers + +1004:		ldw	r1, [sv_pc+], #0	@ if stmfd {, fp, ip, lr, pc} +		ldw	r3, .Ldsi		@ instruction exists, +		cxor.a	r3, r1 >> #14 +		bne	201f +		sub	r0, frame, #16 +		b.l	.Ldumpstm		@ dump saved registers +201: +		cxor.a	sv_fp, #0		@ zero saved fp means +		beq	no_frame		@ no further frames + +		csub.a	sv_fp, frame		@ next frame must be +		mov	frame, sv_fp		@ above the current frame +		bua	for_each_frame + +1006:		adr	r0, .Lbad +		mov	r1, frame +		b.l	printk +no_frame:	ldm.w	(v4 - v8, pc), [sp]+ +ENDPROC(__backtrace) +ENDPROC(c_backtrace) + +		.pushsection __ex_table,"a" +		.align	3 +		.long	1001b, 1006b +		.long	1002b, 1006b +		.long	1003b, 1006b +		.long	1004b, 1006b +		.popsection + +#define instr v4 +#define reg   v5 +#define stack v6 + +.Ldumpstm:	stm.w	(instr, reg, stack, v7, lr), [sp-] +		mov	stack, r0 +		mov	instr, r1 +		mov	reg, #14 +		mov	v7, #0 +1:		mov	r3, #1 +		csub.a	reg, #8 +		bne	201f +		sub	reg, reg, #3 +201: +		cand.a	instr, r3 << reg +		beq	2f +		add	v7, v7, #1 +		cxor.a	v7, #6 +		cmoveq	v7, #1 +		cmoveq	r1, #'\n' +		cmovne	r1, #' ' +		ldw.w	r3, [stack]+, #-4 +		mov	r2, reg +		csub.a	r2, #8 +		bsl	201f +		sub	r2, r2, #3 +201: +		cand.a	instr, #0x40		@ if H is 1, high 16 regs +		beq	201f +		add	r2, r2, #0x10		@ so r2 need add 16 +201: +		adr	r0, .Lfp +		b.l	printk +2:		sub.a	reg, reg, #1 +		bns	1b +		cxor.a	v7, #0 +		beq	201f +		adr	r0, .Lcr +		b.l	printk +201:		ldm.w	(instr, reg, stack, v7, pc), [sp]+ + +.Lfp:		.asciz	"%cr%d:%08x" +.Lcr:		.asciz	"\n" +.Lbad:		.asciz	"Backtrace aborted due to bad frame pointer <%p>\n" +		.align +.Ldsi:		.word	0x92eec000 >> 14	@ stm.w sp, (... fp, ip, lr, pc) +		.word	0x92e10000 >> 14	@ stm.w sp, () + +#endif diff --git a/arch/unicore32/lib/clear_user.S b/arch/unicore32/lib/clear_user.S new file mode 100644 index 00000000000..20047f7224f --- /dev/null +++ b/arch/unicore32/lib/clear_user.S @@ -0,0 +1,57 @@ +/* + * linux/arch/unicore32/lib/clear_user.S + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/linkage.h> +#include <asm/assembler.h> + +		.text + +/* Prototype: int __clear_user(void *addr, size_t sz) + * Purpose  : clear some user memory + * Params   : addr - user memory address to clear + *          : sz   - number of bytes to clear + * Returns  : number of bytes NOT cleared + */ +WEAK(__clear_user) +		stm.w	(lr), [sp-] +		stm.w	(r1), [sp-] +		mov	r2, #0 +		csub.a	r1, #4 +		bsl	2f +		and.a	ip, r0, #3 +		beq	1f +		csub.a	ip, #2 +		strusr	r2, r0, 1 +		strusr	r2, r0, 1, el +		strusr	r2, r0, 1, sl +		rsub	ip, ip, #4 +		sub	r1, r1, ip		@  7  6  5  4  3  2  1 +1:		sub.a	r1, r1, #8		@ -1 -2 -3 -4 -5 -6 -7 +		strusr	r2, r0, 4, ns, rept=2 +		bns	1b +		add.a	r1, r1, #4		@  3  2  1  0 -1 -2 -3 +		strusr	r2, r0, 4, ns +2:		cand.a	r1, #2			@ 1x 1x 0x 0x 1x 1x 0x +		strusr	r2, r0, 1, ne, rept=2 +		cand.a	r1, #1			@ x1 x0 x1 x0 x1 x0 x1 +		beq	3f +USER(		stb.u	r2, [r0]) +3:		mov	r0, #0 +		ldm.w	(r1), [sp]+ +		ldm.w	(pc), [sp]+ +ENDPROC(__clear_user) + +		.pushsection .fixup,"ax" +		.align	0 +9001:		ldm.w	(r0), [sp]+ +		ldm.w	(pc), [sp]+ +		.popsection + diff --git a/arch/unicore32/lib/copy_from_user.S b/arch/unicore32/lib/copy_from_user.S new file mode 100644 index 00000000000..ab0767ea5db --- /dev/null +++ b/arch/unicore32/lib/copy_from_user.S @@ -0,0 +1,108 @@ +/* + * linux/arch/unicore32/lib/copy_from_user.S + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/linkage.h> +#include <asm/assembler.h> + +/* + * Prototype: + * + *	size_t __copy_from_user(void *to, const void *from, size_t n) + * + * Purpose: + * + *	copy a block to kernel memory from user memory + * + * Params: + * + *	to = kernel memory + *	from = user memory + *	n = number of bytes to copy + * + * Return value: + * + *	Number of bytes NOT copied. + */ + +	.macro ldr1w ptr reg abort +	ldrusr	\reg, \ptr, 4, abort=\abort +	.endm + +	.macro ldr4w ptr reg1 reg2 reg3 reg4 abort +100:	ldm.w	(\reg1, \reg2, \reg3, \reg4), [\ptr]+ +	.pushsection __ex_table, "a" +	.align	3 +	.long 100b, \abort +	.popsection +	.endm + +	.macro ldr8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort +100:	ldm.w (\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8), [\ptr]+ +	.pushsection __ex_table, "a" +	.align	3 +	.long 100b, \abort +	.popsection +	.endm + +	.macro ldr1b ptr reg cond=al abort +	ldrusr	\reg, \ptr, 1, \cond, abort=\abort +	.endm + +	.macro str1w ptr reg abort +	stw.w \reg, [\ptr]+, #4 +	.endm + +	.macro str8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort +	stm.w (\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8), [\ptr]+ +	.endm + +	.macro str1b ptr reg cond=al abort +	.ifnc	\cond, al +	b\cond	201f +	b	202f +	.endif +201:	stb.w \reg, [\ptr]+, #1 +202: +	.endm + +	.macro enter +	mov	r3, #0 +	stm.w	(r0, r2, r3), [sp-] +	.endm + +	.macro exit +	add	sp, sp, #8 +	ldm.w	(r0), [sp]+ +	mov	pc, lr +	.endm + +	.text + +ENTRY(__copy_from_user) + +#include "copy_template.S" + +ENDPROC(__copy_from_user) + +	.pushsection .fixup,"ax" +	.align 0 +	copy_abort_preamble +	ldm.w	(r1, r2), [sp]+ +	sub	r3, r0, r1 +	rsub	r2, r3, r2 +	stw	r2, [sp] +	mov	r1, #0 +	b.l	memset +	ldw.w	r0, [sp]+, #4 +	copy_abort_end +	.popsection + diff --git a/arch/unicore32/lib/copy_page.S b/arch/unicore32/lib/copy_page.S new file mode 100644 index 00000000000..3a448d755ad --- /dev/null +++ b/arch/unicore32/lib/copy_page.S @@ -0,0 +1,39 @@ +/* + * linux/arch/unicore32/lib/copy_page.S + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * 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. + * + *  ASM optimised string functions + */ +#include <linux/linkage.h> +#include <asm/assembler.h> +#include <generated/asm-offsets.h> +#include <asm/cache.h> + +#define COPY_COUNT (PAGE_SZ/256) + +		.text +		.align	5 +/* + * UniCore optimised copy_page routine + */ +ENTRY(copy_page) +		stm.w	(r17 - r19, lr), [sp-] +		mov	r17, r0 +		mov	r18, r1 +		mov	r19, #COPY_COUNT +1: +	.rept	4 +		ldm.w	(r0 - r15), [r18]+ +		stm.w	(r0 - r15), [r17]+ +	.endr +		sub.a	r19, r19, #1 +		bne	1b +		ldm.w	(r17 - r19, pc), [sp]+ +ENDPROC(copy_page) diff --git a/arch/unicore32/lib/copy_template.S b/arch/unicore32/lib/copy_template.S new file mode 100644 index 00000000000..524287fc012 --- /dev/null +++ b/arch/unicore32/lib/copy_template.S @@ -0,0 +1,214 @@ +/* + * linux/arch/unicore32/lib/copy_template.S + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * 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. + */ + +/* + * Theory of operation + * ------------------- + * + * This file provides the core code for a forward memory copy used in + * the implementation of memcopy(), copy_to_user() and copy_from_user(). + * + * The including file must define the following accessor macros + * according to the need of the given function: + * + * ldr1w ptr reg abort + * + *	This loads one word from 'ptr', stores it in 'reg' and increments + *	'ptr' to the next word. The 'abort' argument is used for fixup tables. + * + * ldr4w ptr reg1 reg2 reg3 reg4 abort + * ldr8w ptr, reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + * + *	This loads four or eight words starting from 'ptr', stores them + *	in provided registers and increments 'ptr' past those words. + *	The'abort' argument is used for fixup tables. + * + * ldr1b ptr reg cond abort + * + *	Similar to ldr1w, but it loads a byte and increments 'ptr' one byte. + *	It also must apply the condition code if provided, otherwise the + *	"al" condition is assumed by default. + * + * str1w ptr reg abort + * str8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + * str1b ptr reg cond abort + * + *	Same as their ldr* counterparts, but data is stored to 'ptr' location + *	rather than being loaded. + * + * enter + * + *	Preserve the provided registers on the stack plus any additional + *	data as needed by the implementation including this code. Called + *	upon code entry. + * + * exit + * + *	Restore registers with the values previously saved with the + *	'preserv' macro. Called upon code termination. + */ + + +		enter + +		sub.a	r2, r2, #4 +		bsl	8f +		and.a	ip, r0, #3 +		bne	9f +		and.a	ip, r1, #3 +		bne	10f + +1:		sub.a	r2, r2, #(28) +		stm.w	(r5 - r8), [sp-] +		bsl	5f + +3: +4:		ldr8w	r1, r3, r4, r5, r6, r7, r8, r10, r11, abort=20f +		sub.a	r2, r2, #32 +		str8w	r0, r3, r4, r5, r6, r7, r8, r10, r11, abort=20f +		beg	3b + +5:		and.a	ip, r2, #28 +		rsub	ip, ip, #32 +		beq	7f +		add	pc, pc, ip		@ C is always clear here +		nop + +		ldr1w	r1, r3, abort=20f +		ldr1w	r1, r4, abort=20f +		ldr1w	r1, r5, abort=20f +		ldr1w	r1, r6, abort=20f +		ldr1w	r1, r7, abort=20f +		ldr1w	r1, r8, abort=20f +		ldr1w	r1, r11, abort=20f + +		add	pc, pc, ip +		nop + +		str1w	r0, r3, abort=20f +		str1w	r0, r4, abort=20f +		str1w	r0, r5, abort=20f +		str1w	r0, r6, abort=20f +		str1w	r0, r7, abort=20f +		str1w	r0, r8, abort=20f +		str1w	r0, r11, abort=20f + +7:		ldm.w	(r5 - r8), [sp]+ + +8:		mov.a	r2, r2 << #31 +		ldr1b	r1, r3, ne, abort=21f +		ldr1b	r1, r4, ea, abort=21f +		ldr1b	r1, r10, ea, abort=21f +		str1b	r0, r3, ne, abort=21f +		str1b	r0, r4, ea, abort=21f +		str1b	r0, r10, ea, abort=21f + +		exit + +9:		rsub	ip, ip, #4 +		csub.a	ip, #2 +		ldr1b	r1, r3, sg, abort=21f +		ldr1b	r1, r4, eg, abort=21f +		ldr1b	r1, r11, abort=21f +		str1b	r0, r3, sg, abort=21f +		str1b	r0, r4, eg, abort=21f +		sub.a	r2, r2, ip +		str1b	r0, r11, abort=21f +		bsl	8b +		and.a	ip, r1, #3 +		beq	1b + +10:		andn	r1, r1, #3 +		csub.a	ip, #2 +		ldr1w	r1, r11, abort=21f +		beq	17f +		bsg	18f + + +		.macro	forward_copy_shift a b + +		sub.a	r2, r2, #28 +		bsl	14f + +11:		stm.w	(r5 - r9), [sp-] + +12: +		ldr4w	r1, r4, r5, r6, r7, abort=19f +		mov	r3, r11 pull #\a +		sub.a	r2, r2, #32 +		ldr4w	r1, r8, r9, r10, r11, abort=19f +		or	r3, r3, r4 push #\b +		mov	r4, r4 pull #\a +		or	r4, r4, r5 push #\b +		mov	r5, r5 pull #\a +		or	r5, r5, r6 push #\b +		mov	r6, r6 pull #\a +		or	r6, r6, r7 push #\b +		mov	r7, r7 pull #\a +		or	r7, r7, r8 push #\b +		mov	r8, r8 pull #\a +		or	r8, r8, r9 push #\b +		mov	r9, r9 pull #\a +		or	r9, r9, r10 push #\b +		mov	r10, r10 pull #\a +		or	r10, r10, r11 push #\b +		str8w	r0, r3, r4, r5, r6, r7, r8, r9, r10, , abort=19f +		beg	12b + +		ldm.w	(r5 - r9), [sp]+ + +14:		and.a	ip, r2, #28 +		beq	16f + +15:		mov	r3, r11 pull #\a +		ldr1w	r1, r11, abort=21f +		sub.a	ip, ip, #4 +		or	r3, r3, r11 push #\b +		str1w	r0, r3, abort=21f +		bsg	15b + +16:		sub	r1, r1, #(\b / 8) +		b	8b + +		.endm + + +		forward_copy_shift	a=8	b=24 + +17:		forward_copy_shift	a=16	b=16 + +18:		forward_copy_shift	a=24	b=8 + + +/* + * Abort preamble and completion macros. + * If a fixup handler is required then those macros must surround it. + * It is assumed that the fixup code will handle the private part of + * the exit macro. + */ + +	.macro	copy_abort_preamble +19:	ldm.w	(r5 - r9), [sp]+ +	b	21f +299:	.word	0			@ store lr +					@ to avoid function call in fixup +20:	ldm.w	(r5 - r8), [sp]+ +21: +	adr	r1, 299b +	stw	lr, [r1] +	.endm + +	.macro	copy_abort_end +	adr	lr, 299b +	ldw	pc, [lr] +	.endm + diff --git a/arch/unicore32/lib/copy_to_user.S b/arch/unicore32/lib/copy_to_user.S new file mode 100644 index 00000000000..6e22151c840 --- /dev/null +++ b/arch/unicore32/lib/copy_to_user.S @@ -0,0 +1,96 @@ +/* + * linux/arch/unicore32/lib/copy_to_user.S + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/linkage.h> +#include <asm/assembler.h> + +/* + * Prototype: + * + *	size_t __copy_to_user(void *to, const void *from, size_t n) + * + * Purpose: + * + *	copy a block to user memory from kernel memory + * + * Params: + * + *	to = user memory + *	from = kernel memory + *	n = number of bytes to copy + * + * Return value: + * + *	Number of bytes NOT copied. + */ + +	.macro ldr1w ptr reg abort +	ldw.w \reg, [\ptr]+, #4 +	.endm + +	.macro ldr4w ptr reg1 reg2 reg3 reg4 abort +	ldm.w	(\reg1, \reg2, \reg3, \reg4), [\ptr]+ +	.endm + +	.macro ldr8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort +	ldm.w (\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8), [\ptr]+ +	.endm + +	.macro ldr1b ptr reg cond=al abort +	notcond	\cond, .+8 +	ldb.w \reg, [\ptr]+, #1 +	.endm + +	.macro str1w ptr reg abort +	strusr	\reg, \ptr, 4, abort=\abort +	.endm + +	.macro str8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort +100:	stm.w (\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8), [\ptr]+ + +	.pushsection __ex_table, "a" +	.long 100b, \abort +	.popsection +	.endm + +	.macro str1b ptr reg cond=al abort +	strusr	\reg, \ptr, 1, \cond, abort=\abort +	.endm + +	.macro enter +	mov	r3, #0 +	stm.w	(r0, r2, r3), [sp-] +	.endm + +	.macro exit +	add	sp, sp, #8 +	ldm.w	(r0), [sp]+ +	mov	pc, lr +	.endm + +	.text + +WEAK(__copy_to_user) + +#include "copy_template.S" + +ENDPROC(__copy_to_user) + +	.pushsection .fixup,"ax" +	.align 0 +	copy_abort_preamble +	ldm.w	(r1, r2, r3), [sp]+ +	sub	r0, r0, r1 +	rsub	r0, r0, r2 +	copy_abort_end +	.popsection + diff --git a/arch/unicore32/lib/delay.S b/arch/unicore32/lib/delay.S new file mode 100644 index 00000000000..24664c009e7 --- /dev/null +++ b/arch/unicore32/lib/delay.S @@ -0,0 +1,51 @@ +/* + * linux/arch/unicore32/lib/delay.S + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/linkage.h> +#include <asm/assembler.h> +#include <asm/param.h> +		.text + +.LC0:		.word	loops_per_jiffy +.LC1:		.word	(2199023*HZ)>>11 + +/* + * r0  <= 2000 + * lpj <= 0x01ffffff (max. 3355 bogomips) + * HZ  <= 1000 + */ + +ENTRY(__udelay) +		ldw	r2, .LC1 +		mul	r0, r2, r0 +ENTRY(__const_udelay)				@ 0 <= r0 <= 0x7fffff06 +		ldw	r2, .LC0 +		ldw	r2, [r2]		@ max = 0x01ffffff +		mov	r0, r0 >> #14		@ max = 0x0001ffff +		mov	r2, r2 >> #10		@ max = 0x00007fff +		mul	r0, r2, r0		@ max = 2^32-1 +		mov.a	r0, r0 >> #6 +		cmoveq	pc, lr + +/* + * loops = r0 * HZ * loops_per_jiffy / 1000000 + * + * Oh, if only we had a cycle counter... + */ + +@ Delay routine +ENTRY(__delay) +		sub.a	r0, r0, #2 +		bua	__delay +		mov	pc, lr +ENDPROC(__udelay) +ENDPROC(__const_udelay) +ENDPROC(__delay) diff --git a/arch/unicore32/lib/findbit.S b/arch/unicore32/lib/findbit.S new file mode 100644 index 00000000000..c77746247d3 --- /dev/null +++ b/arch/unicore32/lib/findbit.S @@ -0,0 +1,100 @@ +/* + * linux/arch/unicore32/lib/findbit.S + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/linkage.h> +#include <asm/assembler.h> +                .text + +/* + * Purpose  : Find a 'zero' bit + * Prototype: int find_first_zero_bit(void *addr, unsigned int maxbit); + */ +ENTRY(find_first_zero_bit) +		cxor.a	r1, #0 +		beq	3f +		mov	r2, #0 +1:		ldb	r3, [r0+], r2 >> #3 +		xor.a	r3, r3, #0xff		@ invert bits +		bne	.L_found		@ any now set - found zero bit +		add	r2, r2, #8		@ next bit pointer +2:		csub.a	r2, r1			@ any more? +		bub	1b +3:		mov	r0, r1			@ no free bits +		mov	pc, lr +ENDPROC(find_first_zero_bit) + +/* + * Purpose  : Find next 'zero' bit + * Prototype: int find_next_zero_bit + *		(void *addr, unsigned int maxbit, int offset) + */ +ENTRY(find_next_zero_bit) +		cxor.a	r1, #0 +		beq	3b +		and.a	ip, r2, #7 +		beq	1b			@ If new byte, goto old routine +		ldb	r3, [r0+], r2 >> #3 +		xor	r3, r3, #0xff		@ now looking for a 1 bit +		mov.a	r3, r3 >> ip		@ shift off unused bits +		bne	.L_found +		or	r2, r2, #7		@ if zero, then no bits here +		add	r2, r2, #1		@ align bit pointer +		b	2b			@ loop for next bit +ENDPROC(find_next_zero_bit) + +/* + * Purpose  : Find a 'one' bit + * Prototype: int find_first_bit + *		(const unsigned long *addr, unsigned int maxbit); + */ +ENTRY(find_first_bit) +		cxor.a	r1, #0 +		beq	3f +		mov	r2, #0 +1:		ldb	r3, [r0+], r2 >> #3 +		mov.a	r3, r3 +		bne	.L_found		@ any now set - found zero bit +		add	r2, r2, #8		@ next bit pointer +2:		csub.a	r2, r1			@ any more? +		bub	1b +3:		mov	r0, r1			@ no free bits +		mov	pc, lr +ENDPROC(find_first_bit) + +/* + * Purpose  : Find next 'one' bit + * Prototype: int find_next_zero_bit + *		(void *addr, unsigned int maxbit, int offset) + */ +ENTRY(find_next_bit) +		cxor.a	r1, #0 +		beq	3b +		and.a	ip, r2, #7 +		beq	1b			@ If new byte, goto old routine +		ldb	r3, [r0+], r2 >> #3 +		mov.a	r3, r3 >> ip		@ shift off unused bits +		bne	.L_found +		or	r2, r2, #7		@ if zero, then no bits here +		add	r2, r2, #1		@ align bit pointer +		b	2b			@ loop for next bit +ENDPROC(find_next_bit) + +/* + * One or more bits in the LSB of r3 are assumed to be set. + */ +.L_found: +		rsub	r1, r3, #0 +		and	r3, r3, r1 +		cntlz	r3, r3 +		rsub	r3, r3, #31 +		add	r0, r2, r3 +		mov	pc, lr + diff --git a/arch/unicore32/lib/strncpy_from_user.S b/arch/unicore32/lib/strncpy_from_user.S new file mode 100644 index 00000000000..ff6c304d5c7 --- /dev/null +++ b/arch/unicore32/lib/strncpy_from_user.S @@ -0,0 +1,45 @@ +/* + * linux/arch/unicore32/lib/strncpy_from_user.S + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/linkage.h> +#include <asm/assembler.h> +#include <asm/errno.h> + +	.text +	.align	5 + +/* + * Copy a string from user space to kernel space. + *  r0 = dst, r1 = src, r2 = byte length + * returns the number of characters copied (strlen of copied string), + *  -EFAULT on exception, or "len" if we fill the whole buffer + */ +ENTRY(__strncpy_from_user) +	mov	ip, r1 +1:	sub.a	r2, r2, #1 +	ldrusr	r3, r1, 1, ns +	bfs	2f +	stb.w	r3, [r0]+, #1 +	cxor.a	r3, #0 +	bne	1b +	sub	r1, r1, #1	@ take NUL character out of count +2:	sub	r0, r1, ip +	mov	pc, lr +ENDPROC(__strncpy_from_user) + +	.pushsection .fixup,"ax" +	.align	0 +9001:	mov	r3, #0 +	stb	r3, [r0+], #0	@ null terminate +	mov	r0, #-EFAULT +	mov	pc, lr +	.popsection + diff --git a/arch/unicore32/lib/strnlen_user.S b/arch/unicore32/lib/strnlen_user.S new file mode 100644 index 00000000000..75863030f21 --- /dev/null +++ b/arch/unicore32/lib/strnlen_user.S @@ -0,0 +1,42 @@ +/* + * linux/arch/unicore32/lib/strnlen_user.S + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/linkage.h> +#include <asm/assembler.h> +#include <asm/errno.h> + +	.text +	.align	5 + +/* Prototype: unsigned long __strnlen_user(const char *str, long n) + * Purpose  : get length of a string in user memory + * Params   : str - address of string in user memory + * Returns  : length of string *including terminator* + *	      or zero on exception, or n + 1 if too long + */ +ENTRY(__strnlen_user) +	mov	r2, r0 +1: +	ldrusr	r3, r0, 1 +	cxor.a	r3, #0 +	beq	2f +	sub.a	r1, r1, #1 +	bne	1b +	add	r0, r0, #1 +2:	sub	r0, r0, r2 +	mov	pc, lr +ENDPROC(__strnlen_user) + +	.pushsection .fixup,"ax" +	.align	0 +9001:	mov	r0, #0 +	mov	pc, lr +	.popsection diff --git a/arch/unicore32/mm/Kconfig b/arch/unicore32/mm/Kconfig new file mode 100644 index 00000000000..5f77fb3c63b --- /dev/null +++ b/arch/unicore32/mm/Kconfig @@ -0,0 +1,50 @@ +comment "Processor Type" + +# Select CPU types depending on the architecture selected.  This selects +# which CPUs we support in the kernel image, and the compiler instruction +# optimiser behaviour. + +config CPU_UCV2 +	def_bool y + +comment "Processor Features" + +config CPU_ICACHE_DISABLE +	bool "Disable I-Cache (I-bit)" +	help +	  Say Y here to disable the processor instruction cache. Unless +	  you have a reason not to or are unsure, say N. + +config CPU_DCACHE_DISABLE +	bool "Disable D-Cache (D-bit)" +	help +	  Say Y here to disable the processor data cache. Unless +	  you have a reason not to or are unsure, say N. + +config CPU_DCACHE_WRITETHROUGH +	bool "Force write through D-cache" +	help +	  Say Y here to use the data cache in writethrough mode. Unless you +	  specifically require this or are unsure, say N. + +config CPU_DCACHE_LINE_DISABLE +	bool "Disable D-cache line ops" +	default y +	help +	  Say Y here to disable the data cache line operations. + +config CPU_TLB_SINGLE_ENTRY_DISABLE +	bool "Disable TLB single entry ops" +	default y +	help +	  Say Y here to disable the TLB single entry operations. + +config SWIOTLB +	def_bool y + +config IOMMU_HELPER +	def_bool SWIOTLB + +config NEED_SG_DMA_LENGTH +	def_bool SWIOTLB + diff --git a/arch/unicore32/mm/Makefile b/arch/unicore32/mm/Makefile new file mode 100644 index 00000000000..46c16669931 --- /dev/null +++ b/arch/unicore32/mm/Makefile @@ -0,0 +1,15 @@ +# +# Makefile for the linux unicore-specific parts of the memory manager. +# + +obj-y				:= extable.o fault.o init.o pgd.o mmu.o +obj-y				+= flush.o ioremap.o + +obj-$(CONFIG_SWIOTLB)		+= dma-swiotlb.o + +obj-$(CONFIG_MODULES)		+= proc-syms.o + +obj-$(CONFIG_ALIGNMENT_TRAP)	+= alignment.o + +obj-$(CONFIG_CPU_UCV2)		+= cache-ucv2.o tlb-ucv2.o proc-ucv2.o + diff --git a/arch/unicore32/mm/alignment.c b/arch/unicore32/mm/alignment.c new file mode 100644 index 00000000000..24e836023e6 --- /dev/null +++ b/arch/unicore32/mm/alignment.c @@ -0,0 +1,526 @@ +/* + * linux/arch/unicore32/mm/alignment.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * 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. + */ +/* + * TODO: + *  FPU ldm/stm not handling + */ +#include <linux/compiler.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/uaccess.h> + +#include <asm/pgtable.h> +#include <asm/tlbflush.h> +#include <asm/unaligned.h> + +#include "mm.h" + +#define CODING_BITS(i)	(i & 0xe0000120) + +#define LDST_P_BIT(i)	(i & (1 << 28))	/* Preindex             */ +#define LDST_U_BIT(i)	(i & (1 << 27))	/* Add offset           */ +#define LDST_W_BIT(i)	(i & (1 << 25))	/* Writeback            */ +#define LDST_L_BIT(i)	(i & (1 << 24))	/* Load                 */ + +#define LDST_P_EQ_U(i)	((((i) ^ ((i) >> 1)) & (1 << 27)) == 0) + +#define LDSTH_I_BIT(i)	(i & (1 << 26))	/* half-word immed      */ +#define LDM_S_BIT(i)	(i & (1 << 26))	/* write ASR from BSR */ +#define LDM_H_BIT(i)	(i & (1 << 6))	/* select r0-r15 or r16-r31 */ + +#define RN_BITS(i)	((i >> 19) & 31)	/* Rn                   */ +#define RD_BITS(i)	((i >> 14) & 31)	/* Rd                   */ +#define RM_BITS(i)	(i & 31)	/* Rm                   */ + +#define REGMASK_BITS(i)	(((i & 0x7fe00) >> 3) | (i & 0x3f)) +#define OFFSET_BITS(i)	(i & 0x03fff) + +#define SHIFT_BITS(i)	((i >> 9) & 0x1f) +#define SHIFT_TYPE(i)	(i & 0xc0) +#define SHIFT_LSL	0x00 +#define SHIFT_LSR	0x40 +#define SHIFT_ASR	0x80 +#define SHIFT_RORRRX	0xc0 + +union offset_union { +	unsigned long un; +	signed long sn; +}; + +#define TYPE_ERROR	0 +#define TYPE_FAULT	1 +#define TYPE_LDST	2 +#define TYPE_DONE	3 +#define TYPE_SWAP  4 +#define TYPE_COLS  5		/* Coprocessor load/store */ + +#define get8_unaligned_check(val, addr, err)		\ +	__asm__(					\ +	"1:	ldb.u	%1, [%2], #1\n"			\ +	"2:\n"						\ +	"	.pushsection .fixup,\"ax\"\n"		\ +	"	.align	2\n"				\ +	"3:	mov	%0, #1\n"			\ +	"	b	2b\n"				\ +	"	.popsection\n"				\ +	"	.pushsection __ex_table,\"a\"\n"		\ +	"	.align	3\n"				\ +	"	.long	1b, 3b\n"			\ +	"	.popsection\n"				\ +	: "=r" (err), "=&r" (val), "=r" (addr)		\ +	: "0" (err), "2" (addr)) + +#define get8t_unaligned_check(val, addr, err)		\ +	__asm__(					\ +	"1:	ldb.u	%1, [%2], #1\n"			\ +	"2:\n"						\ +	"	.pushsection .fixup,\"ax\"\n"		\ +	"	.align	2\n"				\ +	"3:	mov	%0, #1\n"			\ +	"	b	2b\n"				\ +	"	.popsection\n"				\ +	"	.pushsection __ex_table,\"a\"\n"		\ +	"	.align	3\n"				\ +	"	.long	1b, 3b\n"			\ +	"	.popsection\n"				\ +	: "=r" (err), "=&r" (val), "=r" (addr)		\ +	: "0" (err), "2" (addr)) + +#define get16_unaligned_check(val, addr)			\ +	do {							\ +		unsigned int err = 0, v, a = addr;		\ +		get8_unaligned_check(val, a, err);		\ +		get8_unaligned_check(v, a, err);		\ +		val |= v << 8;					\ +		if (err)					\ +			goto fault;				\ +	} while (0) + +#define put16_unaligned_check(val, addr)			\ +	do {							\ +		unsigned int err = 0, v = val, a = addr;	\ +		__asm__(					\ +		"1:	stb.u	%1, [%2], #1\n"			\ +		"	mov	%1, %1 >> #8\n"			\ +		"2:	stb.u	%1, [%2]\n"			\ +		"3:\n"						\ +		"	.pushsection .fixup,\"ax\"\n"		\ +		"	.align	2\n"				\ +		"4:	mov	%0, #1\n"			\ +		"	b	3b\n"				\ +		"	.popsection\n"				\ +		"	.pushsection __ex_table,\"a\"\n"		\ +		"	.align	3\n"				\ +		"	.long	1b, 4b\n"			\ +		"	.long	2b, 4b\n"			\ +		"	.popsection\n"				\ +		: "=r" (err), "=&r" (v), "=&r" (a)		\ +		: "0" (err), "1" (v), "2" (a));			\ +		if (err)					\ +			goto fault;				\ +	} while (0) + +#define __put32_unaligned_check(ins, val, addr)			\ +	do {							\ +		unsigned int err = 0, v = val, a = addr;	\ +		__asm__(					\ +		"1:	"ins"	%1, [%2], #1\n"			\ +		"	mov	%1, %1 >> #8\n"			\ +		"2:	"ins"	%1, [%2], #1\n"			\ +		"	mov	%1, %1 >> #8\n"			\ +		"3:	"ins"	%1, [%2], #1\n"			\ +		"	mov	%1, %1 >> #8\n"			\ +		"4:	"ins"	%1, [%2]\n"			\ +		"5:\n"						\ +		"	.pushsection .fixup,\"ax\"\n"		\ +		"	.align	2\n"				\ +		"6:	mov	%0, #1\n"			\ +		"	b	5b\n"				\ +		"	.popsection\n"				\ +		"	.pushsection __ex_table,\"a\"\n"		\ +		"	.align	3\n"				\ +		"	.long	1b, 6b\n"			\ +		"	.long	2b, 6b\n"			\ +		"	.long	3b, 6b\n"			\ +		"	.long	4b, 6b\n"			\ +		"	.popsection\n"				\ +		: "=r" (err), "=&r" (v), "=&r" (a)		\ +		: "0" (err), "1" (v), "2" (a));			\ +		if (err)					\ +			goto fault;				\ +	} while (0) + +#define get32_unaligned_check(val, addr)			\ +	do {							\ +		unsigned int err = 0, v, a = addr;		\ +		get8_unaligned_check(val, a, err);		\ +		get8_unaligned_check(v, a, err);		\ +		val |= v << 8;					\ +		get8_unaligned_check(v, a, err);		\ +		val |= v << 16;					\ +		get8_unaligned_check(v, a, err);		\ +		val |= v << 24;					\ +		if (err)					\ +			goto fault;				\ +	} while (0) + +#define put32_unaligned_check(val, addr)			\ +	__put32_unaligned_check("stb.u", val, addr) + +#define get32t_unaligned_check(val, addr)			\ +	do {							\ +		unsigned int err = 0, v, a = addr;		\ +		get8t_unaligned_check(val, a, err);		\ +		get8t_unaligned_check(v, a, err);		\ +		val |= v << 8;					\ +		get8t_unaligned_check(v, a, err);		\ +		val |= v << 16;					\ +		get8t_unaligned_check(v, a, err);		\ +		val |= v << 24;					\ +		if (err)					\ +			goto fault;				\ +	} while (0) + +#define put32t_unaligned_check(val, addr)			\ +	__put32_unaligned_check("stb.u", val, addr) + +static void +do_alignment_finish_ldst(unsigned long addr, unsigned long instr, +			 struct pt_regs *regs, union offset_union offset) +{ +	if (!LDST_U_BIT(instr)) +		offset.un = -offset.un; + +	if (!LDST_P_BIT(instr)) +		addr += offset.un; + +	if (!LDST_P_BIT(instr) || LDST_W_BIT(instr)) +		regs->uregs[RN_BITS(instr)] = addr; +} + +static int +do_alignment_ldrhstrh(unsigned long addr, unsigned long instr, +		      struct pt_regs *regs) +{ +	unsigned int rd = RD_BITS(instr); + +	/* old value 0x40002120, can't judge swap instr correctly */ +	if ((instr & 0x4b003fe0) == 0x40000120) +		goto swp; + +	if (LDST_L_BIT(instr)) { +		unsigned long val; +		get16_unaligned_check(val, addr); + +		/* signed half-word? */ +		if (instr & 0x80) +			val = (signed long)((signed short)val); + +		regs->uregs[rd] = val; +	} else +		put16_unaligned_check(regs->uregs[rd], addr); + +	return TYPE_LDST; + +swp: +	/* only handle swap word +	 * for swap byte should not active this alignment exception */ +	get32_unaligned_check(regs->uregs[RD_BITS(instr)], addr); +	put32_unaligned_check(regs->uregs[RM_BITS(instr)], addr); +	return TYPE_SWAP; + +fault: +	return TYPE_FAULT; +} + +static int +do_alignment_ldrstr(unsigned long addr, unsigned long instr, +		    struct pt_regs *regs) +{ +	unsigned int rd = RD_BITS(instr); + +	if (!LDST_P_BIT(instr) && LDST_W_BIT(instr)) +		goto trans; + +	if (LDST_L_BIT(instr)) +		get32_unaligned_check(regs->uregs[rd], addr); +	else +		put32_unaligned_check(regs->uregs[rd], addr); +	return TYPE_LDST; + +trans: +	if (LDST_L_BIT(instr)) +		get32t_unaligned_check(regs->uregs[rd], addr); +	else +		put32t_unaligned_check(regs->uregs[rd], addr); +	return TYPE_LDST; + +fault: +	return TYPE_FAULT; +} + +/* + * LDM/STM alignment handler. + * + * There are 4 variants of this instruction: + * + * B = rn pointer before instruction, A = rn pointer after instruction + *              ------ increasing address -----> + *	        |    | r0 | r1 | ... | rx |    | + * PU = 01             B                    A + * PU = 11        B                    A + * PU = 00        A                    B + * PU = 10             A                    B + */ +static int +do_alignment_ldmstm(unsigned long addr, unsigned long instr, +		    struct pt_regs *regs) +{ +	unsigned int rd, rn, pc_correction, reg_correction, nr_regs, regbits; +	unsigned long eaddr, newaddr; + +	if (LDM_S_BIT(instr)) +		goto bad; + +	pc_correction = 4;	/* processor implementation defined */ + +	/* count the number of registers in the mask to be transferred */ +	nr_regs = hweight16(REGMASK_BITS(instr)) * 4; + +	rn = RN_BITS(instr); +	newaddr = eaddr = regs->uregs[rn]; + +	if (!LDST_U_BIT(instr)) +		nr_regs = -nr_regs; +	newaddr += nr_regs; +	if (!LDST_U_BIT(instr)) +		eaddr = newaddr; + +	if (LDST_P_EQ_U(instr))	/* U = P */ +		eaddr += 4; + +	/* +	 * This is a "hint" - we already have eaddr worked out by the +	 * processor for us. +	 */ +	if (addr != eaddr) { +		printk(KERN_ERR "LDMSTM: PC = %08lx, instr = %08lx, " +		       "addr = %08lx, eaddr = %08lx\n", +		       instruction_pointer(regs), instr, addr, eaddr); +		show_regs(regs); +	} + +	if (LDM_H_BIT(instr)) +		reg_correction = 0x10; +	else +		reg_correction = 0x00; + +	for (regbits = REGMASK_BITS(instr), rd = 0; regbits; +	     regbits >>= 1, rd += 1) +		if (regbits & 1) { +			if (LDST_L_BIT(instr)) +				get32_unaligned_check(regs-> +					uregs[rd + reg_correction], eaddr); +			else +				put32_unaligned_check(regs-> +					uregs[rd + reg_correction], eaddr); +			eaddr += 4; +		} + +	if (LDST_W_BIT(instr)) +		regs->uregs[rn] = newaddr; +	return TYPE_DONE; + +fault: +	regs->UCreg_pc -= pc_correction; +	return TYPE_FAULT; + +bad: +	printk(KERN_ERR "Alignment trap: not handling ldm with s-bit set\n"); +	return TYPE_ERROR; +} + +static int +do_alignment(unsigned long addr, unsigned int error_code, struct pt_regs *regs) +{ +	union offset_union offset; +	unsigned long instr, instrptr; +	int (*handler) (unsigned long addr, unsigned long instr, +			struct pt_regs *regs); +	unsigned int type; + +	instrptr = instruction_pointer(regs); +	if (instrptr >= PAGE_OFFSET) +		instr = *(unsigned long *)instrptr; +	else { +		__asm__ __volatile__( +				"ldw.u	%0, [%1]\n" +				: "=&r"(instr) +				: "r"(instrptr)); +	} + +	regs->UCreg_pc += 4; + +	switch (CODING_BITS(instr)) { +	case 0x40000120:	/* ldrh or strh */ +		if (LDSTH_I_BIT(instr)) +			offset.un = (instr & 0x3e00) >> 4 | (instr & 31); +		else +			offset.un = regs->uregs[RM_BITS(instr)]; +		handler = do_alignment_ldrhstrh; +		break; + +	case 0x60000000:	/* ldr or str immediate */ +	case 0x60000100:	/* ldr or str immediate */ +	case 0x60000020:	/* ldr or str immediate */ +	case 0x60000120:	/* ldr or str immediate */ +		offset.un = OFFSET_BITS(instr); +		handler = do_alignment_ldrstr; +		break; + +	case 0x40000000:	/* ldr or str register */ +		offset.un = regs->uregs[RM_BITS(instr)]; +		{ +			unsigned int shiftval = SHIFT_BITS(instr); + +			switch (SHIFT_TYPE(instr)) { +			case SHIFT_LSL: +				offset.un <<= shiftval; +				break; + +			case SHIFT_LSR: +				offset.un >>= shiftval; +				break; + +			case SHIFT_ASR: +				offset.sn >>= shiftval; +				break; + +			case SHIFT_RORRRX: +				if (shiftval == 0) { +					offset.un >>= 1; +					if (regs->UCreg_asr & PSR_C_BIT) +						offset.un |= 1 << 31; +				} else +					offset.un = offset.un >> shiftval | +					    offset.un << (32 - shiftval); +				break; +			} +		} +		handler = do_alignment_ldrstr; +		break; + +	case 0x80000000:	/* ldm or stm */ +	case 0x80000020:	/* ldm or stm */ +		handler = do_alignment_ldmstm; +		break; + +	default: +		goto bad; +	} + +	type = handler(addr, instr, regs); + +	if (type == TYPE_ERROR || type == TYPE_FAULT) +		goto bad_or_fault; + +	if (type == TYPE_LDST) +		do_alignment_finish_ldst(addr, instr, regs, offset); + +	return 0; + +bad_or_fault: +	if (type == TYPE_ERROR) +		goto bad; +	regs->UCreg_pc -= 4; +	/* +	 * We got a fault - fix it up, or die. +	 */ +	do_bad_area(addr, error_code, regs); +	return 0; + +bad: +	/* +	 * Oops, we didn't handle the instruction. +	 * However, we must handle fpu instr firstly. +	 */ +#ifdef CONFIG_UNICORE_FPU_F64 +	/* handle co.load/store */ +#define CODING_COLS                0xc0000000 +#define COLS_OFFSET_BITS(i)	(i & 0x1FF) +#define COLS_L_BITS(i)		(i & (1<<24)) +#define COLS_FN_BITS(i)		((i>>14) & 31) +	if ((instr & 0xe0000000) == CODING_COLS) { +		unsigned int fn = COLS_FN_BITS(instr); +		unsigned long val = 0; +		if (COLS_L_BITS(instr)) { +			get32t_unaligned_check(val, addr); +			switch (fn) { +#define ASM_MTF(n)	case n:						\ +			__asm__ __volatile__("MTF %0, F" __stringify(n)	\ +				: : "r"(val));				\ +			break; +			ASM_MTF(0); ASM_MTF(1); ASM_MTF(2); ASM_MTF(3); +			ASM_MTF(4); ASM_MTF(5); ASM_MTF(6); ASM_MTF(7); +			ASM_MTF(8); ASM_MTF(9); ASM_MTF(10); ASM_MTF(11); +			ASM_MTF(12); ASM_MTF(13); ASM_MTF(14); ASM_MTF(15); +			ASM_MTF(16); ASM_MTF(17); ASM_MTF(18); ASM_MTF(19); +			ASM_MTF(20); ASM_MTF(21); ASM_MTF(22); ASM_MTF(23); +			ASM_MTF(24); ASM_MTF(25); ASM_MTF(26); ASM_MTF(27); +			ASM_MTF(28); ASM_MTF(29); ASM_MTF(30); ASM_MTF(31); +#undef ASM_MTF +			} +		} else { +			switch (fn) { +#define ASM_MFF(n)	case n:						\ +			__asm__ __volatile__("MFF %0, F" __stringify(n)	\ +				: : "r"(val));				\ +			break; +			ASM_MFF(0); ASM_MFF(1); ASM_MFF(2); ASM_MFF(3); +			ASM_MFF(4); ASM_MFF(5); ASM_MFF(6); ASM_MFF(7); +			ASM_MFF(8); ASM_MFF(9); ASM_MFF(10); ASM_MFF(11); +			ASM_MFF(12); ASM_MFF(13); ASM_MFF(14); ASM_MFF(15); +			ASM_MFF(16); ASM_MFF(17); ASM_MFF(18); ASM_MFF(19); +			ASM_MFF(20); ASM_MFF(21); ASM_MFF(22); ASM_MFF(23); +			ASM_MFF(24); ASM_MFF(25); ASM_MFF(26); ASM_MFF(27); +			ASM_MFF(28); ASM_MFF(29); ASM_MFF(30); ASM_MFF(31); +#undef ASM_MFF +			} +			put32t_unaligned_check(val, addr); +		} +		return TYPE_COLS; +	} +fault: +	return TYPE_FAULT; +#endif +	printk(KERN_ERR "Alignment trap: not handling instruction " +	       "%08lx at [<%08lx>]\n", instr, instrptr); +	return 1; +} + +/* + * This needs to be done after sysctl_init, otherwise sys/ will be + * overwritten.  Actually, this shouldn't be in sys/ at all since + * it isn't a sysctl, and it doesn't contain sysctl information. + */ +static int __init alignment_init(void) +{ +	hook_fault_code(1, do_alignment, SIGBUS, BUS_ADRALN, +			"alignment exception"); + +	return 0; +} + +fs_initcall(alignment_init); diff --git a/arch/unicore32/mm/cache-ucv2.S b/arch/unicore32/mm/cache-ucv2.S new file mode 100644 index 00000000000..ecaa1727f90 --- /dev/null +++ b/arch/unicore32/mm/cache-ucv2.S @@ -0,0 +1,212 @@ +/* + * linux/arch/unicore32/mm/cache-ucv2.S + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + *  This is the "shell" of the UniCore-v2 processor support. + */ +#include <linux/linkage.h> +#include <linux/init.h> +#include <asm/assembler.h> +#include <asm/page.h> + +#include "proc-macros.S" + +/* + *	__cpuc_flush_icache_all() + *	__cpuc_flush_kern_all() + *	__cpuc_flush_user_all() + * + *	Flush the entire cache. + */ +ENTRY(__cpuc_flush_icache_all) +	/*FALLTHROUGH*/ +ENTRY(__cpuc_flush_kern_all) +	/*FALLTHROUGH*/ +ENTRY(__cpuc_flush_user_all) +	mov	r0, #0 +	movc	p0.c5, r0, #14			@ Dcache flush all +	nop8 + +	mov	r0, #0 +	movc	p0.c5, r0, #20			@ Icache invalidate all +	nop8 + +	mov	pc, lr + +/* + *	__cpuc_flush_user_range(start, end, flags) + * + *	Flush a range of TLB entries in the specified address space. + * + *	- start - start address (may not be aligned) + *	- end   - end address (exclusive, may not be aligned) + *	- flags	- vm_area_struct flags describing address space + */ +ENTRY(__cpuc_flush_user_range) +	cxor.a	r2, #0 +	beq	__cpuc_dma_flush_range + +#ifndef CONFIG_CPU_DCACHE_LINE_DISABLE +	andn	r0, r0, #CACHE_LINESIZE - 1	@ Safety check +	sub	r1, r1, r0 +	csub.a	r1, #MAX_AREA_SIZE +	bsg	2f + +	andn	r1, r1, #CACHE_LINESIZE - 1 +	add	r1, r1, #CACHE_LINESIZE + +101:	dcacheline_flush	r0, r11, r12 + +	add	r0, r0, #CACHE_LINESIZE +	sub.a	r1, r1, #CACHE_LINESIZE +	bns	101b +	b	3f +#endif +2:	mov	ip, #0 +	movc	p0.c5, ip, #14			@ Dcache flush all +	nop8 + +3:	mov	ip, #0 +	movc	p0.c5, ip, #20			@ Icache invalidate all +	nop8 + +	mov	pc, lr + +/* + *	__cpuc_coherent_kern_range(start,end) + *	__cpuc_coherent_user_range(start,end) + * + *	Ensure that the I and D caches are coherent within specified + *	region.  This is typically used when code has been written to + *	a memory region, and will be executed. + * + *	- start   - virtual start address of region + *	- end     - virtual end address of region + */ +ENTRY(__cpuc_coherent_kern_range) +	/* FALLTHROUGH */ +ENTRY(__cpuc_coherent_user_range) +#ifndef CONFIG_CPU_DCACHE_LINE_DISABLE +	andn	r0, r0, #CACHE_LINESIZE - 1	@ Safety check +	sub	r1, r1, r0 +	csub.a	r1, #MAX_AREA_SIZE +	bsg	2f + +	andn	r1, r1, #CACHE_LINESIZE - 1 +	add	r1, r1, #CACHE_LINESIZE + +	@ r0 va2pa r10 +	mov	r9, #PAGE_SZ +	sub	r9, r9, #1			@ PAGE_MASK +101:	va2pa	r0, r10, r11, r12, r13, 2f	@ r10 is PA +	b	103f +102:	cand.a	r0, r9 +	beq	101b + +103:	movc	p0.c5, r10, #11			@ Dcache clean line of R10 +	nop8 + +	add	r0, r0, #CACHE_LINESIZE +	add	r10, r10, #CACHE_LINESIZE +	sub.a	r1, r1, #CACHE_LINESIZE +	bns	102b +	b	3f +#endif +2:	mov	ip, #0 +	movc	p0.c5, ip, #10			@ Dcache clean all +	nop8 + +3:	mov	ip, #0 +	movc	p0.c5, ip, #20			@ Icache invalidate all +	nop8 + +	mov	pc, lr + +/* + *	__cpuc_flush_kern_dcache_area(void *addr, size_t size) + * + *	- addr	- kernel address + *	- size	- region size + */ +ENTRY(__cpuc_flush_kern_dcache_area) +	mov	ip, #0 +	movc	p0.c5, ip, #14			@ Dcache flush all +	nop8 +	mov	pc, lr + +/* + *	__cpuc_dma_clean_range(start,end) + *	- start   - virtual start address of region + *	- end     - virtual end address of region + */ +ENTRY(__cpuc_dma_clean_range) +#ifndef CONFIG_CPU_DCACHE_LINE_DISABLE +	andn	r0, r0, #CACHE_LINESIZE - 1 +	sub	r1, r1, r0 +	andn	r1, r1, #CACHE_LINESIZE - 1 +	add	r1, r1, #CACHE_LINESIZE + +	csub.a	r1, #MAX_AREA_SIZE +	bsg	2f + +	@ r0 va2pa r10 +	mov	r9, #PAGE_SZ +	sub	r9, r9, #1			@ PAGE_MASK +101:	va2pa	r0, r10, r11, r12, r13, 2f	@ r10 is PA +	b	1f +102:	cand.a	r0, r9 +	beq	101b + +1:	movc	p0.c5, r10, #11			@ Dcache clean line of R10 +	nop8 +	add	r0, r0, #CACHE_LINESIZE +	add	r10, r10, #CACHE_LINESIZE +	sub.a	r1, r1, #CACHE_LINESIZE +	bns	102b +	mov	pc, lr +#endif +2:	mov	ip, #0 +	movc	p0.c5, ip, #10			@ Dcache clean all +	nop8 + +	mov	pc, lr + +/* + *	__cpuc_dma_inv_range(start,end) + *	__cpuc_dma_flush_range(start,end) + *	- start   - virtual start address of region + *	- end     - virtual end address of region + */ +__cpuc_dma_inv_range: +	/* FALLTHROUGH */ +ENTRY(__cpuc_dma_flush_range) +#ifndef CONFIG_CPU_DCACHE_LINE_DISABLE +	andn	r0, r0, #CACHE_LINESIZE - 1 +	sub	r1, r1, r0 +	andn	r1, r1, #CACHE_LINESIZE - 1 +	add	r1, r1, #CACHE_LINESIZE + +	csub.a	r1, #MAX_AREA_SIZE +	bsg	2f + +	@ r0 va2pa r10 +101:	dcacheline_flush	r0, r11, r12 + +	add	r0, r0, #CACHE_LINESIZE +	sub.a	r1, r1, #CACHE_LINESIZE +	bns	101b +	mov	pc, lr +#endif +2:	mov	ip, #0 +	movc	p0.c5, ip, #14			@ Dcache flush all +	nop8 + +	mov	pc, lr + diff --git a/arch/unicore32/mm/dma-swiotlb.c b/arch/unicore32/mm/dma-swiotlb.c new file mode 100644 index 00000000000..16c08b2143a --- /dev/null +++ b/arch/unicore32/mm/dma-swiotlb.c @@ -0,0 +1,48 @@ +/* + * Contains routines needed to support swiotlb for UniCore32. + * + * Copyright (C) 2010 Guan Xuetao + * + * This program is free software; you can redistribute  it and/or modify it + * under  the terms of  the GNU General  Public License as published by the + * Free Software Foundation;  either version 2 of the  License, or (at your + * option) any later version. + */ +#include <linux/pci.h> +#include <linux/cache.h> +#include <linux/module.h> +#include <linux/dma-mapping.h> +#include <linux/swiotlb.h> +#include <linux/bootmem.h> + +#include <asm/dma.h> + +static void *unicore_swiotlb_alloc_coherent(struct device *dev, size_t size, +					    dma_addr_t *dma_handle, gfp_t flags, +					    struct dma_attrs *attrs) +{ +	return swiotlb_alloc_coherent(dev, size, dma_handle, flags); +} + +static void unicore_swiotlb_free_coherent(struct device *dev, size_t size, +					  void *vaddr, dma_addr_t dma_addr, +					  struct dma_attrs *attrs) +{ +	swiotlb_free_coherent(dev, size, vaddr, dma_addr); +} + +struct dma_map_ops swiotlb_dma_map_ops = { +	.alloc = unicore_swiotlb_alloc_coherent, +	.free = unicore_swiotlb_free_coherent, +	.map_sg = swiotlb_map_sg_attrs, +	.unmap_sg = swiotlb_unmap_sg_attrs, +	.dma_supported = swiotlb_dma_supported, +	.map_page = swiotlb_map_page, +	.unmap_page = swiotlb_unmap_page, +	.sync_single_for_cpu = swiotlb_sync_single_for_cpu, +	.sync_single_for_device = swiotlb_sync_single_for_device, +	.sync_sg_for_cpu = swiotlb_sync_sg_for_cpu, +	.sync_sg_for_device = swiotlb_sync_sg_for_device, +	.mapping_error = swiotlb_dma_mapping_error, +}; +EXPORT_SYMBOL(swiotlb_dma_map_ops); diff --git a/arch/unicore32/mm/extable.c b/arch/unicore32/mm/extable.c new file mode 100644 index 00000000000..6564180eb28 --- /dev/null +++ b/arch/unicore32/mm/extable.c @@ -0,0 +1,24 @@ +/* + * linux/arch/unicore32/mm/extable.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/uaccess.h> + +int fixup_exception(struct pt_regs *regs) +{ +	const struct exception_table_entry *fixup; + +	fixup = search_exception_tables(instruction_pointer(regs)); +	if (fixup) +		regs->UCreg_pc = fixup->fixup; + +	return fixup != NULL; +} diff --git a/arch/unicore32/mm/fault.c b/arch/unicore32/mm/fault.c new file mode 100644 index 00000000000..0dc922dba91 --- /dev/null +++ b/arch/unicore32/mm/fault.c @@ -0,0 +1,499 @@ +/* + * linux/arch/unicore32/mm/fault.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/signal.h> +#include <linux/mm.h> +#include <linux/hardirq.h> +#include <linux/init.h> +#include <linux/kprobes.h> +#include <linux/uaccess.h> +#include <linux/page-flags.h> +#include <linux/sched.h> +#include <linux/io.h> + +#include <asm/pgtable.h> +#include <asm/tlbflush.h> + +/* + * Fault status register encodings.  We steal bit 31 for our own purposes. + */ +#define FSR_LNX_PF		(1 << 31) + +static inline int fsr_fs(unsigned int fsr) +{ +	/* xyabcde will be abcde+xy */ +	return (fsr & 31) + ((fsr & (3 << 5)) >> 5); +} + +/* + * This is useful to dump out the page tables associated with + * 'addr' in mm 'mm'. + */ +void show_pte(struct mm_struct *mm, unsigned long addr) +{ +	pgd_t *pgd; + +	if (!mm) +		mm = &init_mm; + +	printk(KERN_ALERT "pgd = %p\n", mm->pgd); +	pgd = pgd_offset(mm, addr); +	printk(KERN_ALERT "[%08lx] *pgd=%08lx", addr, pgd_val(*pgd)); + +	do { +		pmd_t *pmd; +		pte_t *pte; + +		if (pgd_none(*pgd)) +			break; + +		if (pgd_bad(*pgd)) { +			printk("(bad)"); +			break; +		} + +		pmd = pmd_offset((pud_t *) pgd, addr); +		if (PTRS_PER_PMD != 1) +			printk(", *pmd=%08lx", pmd_val(*pmd)); + +		if (pmd_none(*pmd)) +			break; + +		if (pmd_bad(*pmd)) { +			printk("(bad)"); +			break; +		} + +		/* We must not map this if we have highmem enabled */ +		if (PageHighMem(pfn_to_page(pmd_val(*pmd) >> PAGE_SHIFT))) +			break; + +		pte = pte_offset_map(pmd, addr); +		printk(", *pte=%08lx", pte_val(*pte)); +		pte_unmap(pte); +	} while (0); + +	printk("\n"); +} + +/* + * Oops.  The kernel tried to access some page that wasn't present. + */ +static void __do_kernel_fault(struct mm_struct *mm, unsigned long addr, +		unsigned int fsr, struct pt_regs *regs) +{ +	/* +	 * Are we prepared to handle this kernel fault? +	 */ +	if (fixup_exception(regs)) +		return; + +	/* +	 * No handler, we'll have to terminate things with extreme prejudice. +	 */ +	bust_spinlocks(1); +	printk(KERN_ALERT +	       "Unable to handle kernel %s at virtual address %08lx\n", +	       (addr < PAGE_SIZE) ? "NULL pointer dereference" : +	       "paging request", addr); + +	show_pte(mm, addr); +	die("Oops", regs, fsr); +	bust_spinlocks(0); +	do_exit(SIGKILL); +} + +/* + * Something tried to access memory that isn't in our memory map.. + * User mode accesses just cause a SIGSEGV + */ +static void __do_user_fault(struct task_struct *tsk, unsigned long addr, +		unsigned int fsr, unsigned int sig, int code, +		struct pt_regs *regs) +{ +	struct siginfo si; + +	tsk->thread.address = addr; +	tsk->thread.error_code = fsr; +	tsk->thread.trap_no = 14; +	si.si_signo = sig; +	si.si_errno = 0; +	si.si_code = code; +	si.si_addr = (void __user *)addr; +	force_sig_info(sig, &si, tsk); +} + +void do_bad_area(unsigned long addr, unsigned int fsr, struct pt_regs *regs) +{ +	struct task_struct *tsk = current; +	struct mm_struct *mm = tsk->active_mm; + +	/* +	 * If we are in kernel mode at this point, we +	 * have no context to handle this fault with. +	 */ +	if (user_mode(regs)) +		__do_user_fault(tsk, addr, fsr, SIGSEGV, SEGV_MAPERR, regs); +	else +		__do_kernel_fault(mm, addr, fsr, regs); +} + +#define VM_FAULT_BADMAP		0x010000 +#define VM_FAULT_BADACCESS	0x020000 + +/* + * Check that the permissions on the VMA allow for the fault which occurred. + * If we encountered a write fault, we must have write permission, otherwise + * we allow any permission. + */ +static inline bool access_error(unsigned int fsr, struct vm_area_struct *vma) +{ +	unsigned int mask = VM_READ | VM_WRITE | VM_EXEC; + +	if (!(fsr ^ 0x12))	/* write? */ +		mask = VM_WRITE; +	if (fsr & FSR_LNX_PF) +		mask = VM_EXEC; + +	return vma->vm_flags & mask ? false : true; +} + +static int __do_pf(struct mm_struct *mm, unsigned long addr, unsigned int fsr, +		unsigned int flags, struct task_struct *tsk) +{ +	struct vm_area_struct *vma; +	int fault; + +	vma = find_vma(mm, addr); +	fault = VM_FAULT_BADMAP; +	if (unlikely(!vma)) +		goto out; +	if (unlikely(vma->vm_start > addr)) +		goto check_stack; + +	/* +	 * Ok, we have a good vm_area for this +	 * memory access, so we can handle it. +	 */ +good_area: +	if (access_error(fsr, vma)) { +		fault = VM_FAULT_BADACCESS; +		goto out; +	} + +	/* +	 * If for any reason at all we couldn't handle the fault, make +	 * sure we exit gracefully rather than endlessly redo the fault. +	 */ +	fault = handle_mm_fault(mm, vma, addr & PAGE_MASK, flags); +	return fault; + +check_stack: +	if (vma->vm_flags & VM_GROWSDOWN && !expand_stack(vma, addr)) +		goto good_area; +out: +	return fault; +} + +static int do_pf(unsigned long addr, unsigned int fsr, struct pt_regs *regs) +{ +	struct task_struct *tsk; +	struct mm_struct *mm; +	int fault, sig, code; +	unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE; + +	tsk = current; +	mm = tsk->mm; + +	/* +	 * If we're in an interrupt or have no user +	 * context, we must not take the fault.. +	 */ +	if (in_atomic() || !mm) +		goto no_context; + +	if (user_mode(regs)) +		flags |= FAULT_FLAG_USER; +	if (!(fsr ^ 0x12)) +		flags |= FAULT_FLAG_WRITE; + +	/* +	 * As per x86, we may deadlock here.  However, since the kernel only +	 * validly references user space from well defined areas of the code, +	 * we can bug out early if this is from code which shouldn't. +	 */ +	if (!down_read_trylock(&mm->mmap_sem)) { +		if (!user_mode(regs) +		    && !search_exception_tables(regs->UCreg_pc)) +			goto no_context; +retry: +		down_read(&mm->mmap_sem); +	} else { +		/* +		 * The above down_read_trylock() might have succeeded in +		 * which case, we'll have missed the might_sleep() from +		 * down_read() +		 */ +		might_sleep(); +#ifdef CONFIG_DEBUG_VM +		if (!user_mode(regs) && +		    !search_exception_tables(regs->UCreg_pc)) +			goto no_context; +#endif +	} + +	fault = __do_pf(mm, addr, fsr, flags, tsk); + +	/* If we need to retry but a fatal signal is pending, handle the +	 * signal first. We do not need to release the mmap_sem because +	 * it would already be released in __lock_page_or_retry in +	 * mm/filemap.c. */ +	if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current)) +		return 0; + +	if (!(fault & VM_FAULT_ERROR) && (flags & FAULT_FLAG_ALLOW_RETRY)) { +		if (fault & VM_FAULT_MAJOR) +			tsk->maj_flt++; +		else +			tsk->min_flt++; +		if (fault & VM_FAULT_RETRY) { +			/* Clear FAULT_FLAG_ALLOW_RETRY to avoid any risk +			* of starvation. */ +			flags &= ~FAULT_FLAG_ALLOW_RETRY; +			goto retry; +		} +	} + +	up_read(&mm->mmap_sem); + +	/* +	 * Handle the "normal" case first - VM_FAULT_MAJOR / VM_FAULT_MINOR +	 */ +	if (likely(!(fault & +	       (VM_FAULT_ERROR | VM_FAULT_BADMAP | VM_FAULT_BADACCESS)))) +		return 0; + +	/* +	 * If we are in kernel mode at this point, we +	 * have no context to handle this fault with. +	 */ +	if (!user_mode(regs)) +		goto no_context; + +	if (fault & VM_FAULT_OOM) { +		/* +		 * We ran out of memory, call the OOM killer, and return to +		 * userspace (which will retry the fault, or kill us if we +		 * got oom-killed) +		 */ +		pagefault_out_of_memory(); +		return 0; +	} + +	if (fault & VM_FAULT_SIGBUS) { +		/* +		 * We had some memory, but were unable to +		 * successfully fix up this page fault. +		 */ +		sig = SIGBUS; +		code = BUS_ADRERR; +	} else { +		/* +		 * Something tried to access memory that +		 * isn't in our memory map.. +		 */ +		sig = SIGSEGV; +		code = fault == VM_FAULT_BADACCESS ? SEGV_ACCERR : SEGV_MAPERR; +	} + +	__do_user_fault(tsk, addr, fsr, sig, code, regs); +	return 0; + +no_context: +	__do_kernel_fault(mm, addr, fsr, regs); +	return 0; +} + +/* + * First Level Translation Fault Handler + * + * We enter here because the first level page table doesn't contain + * a valid entry for the address. + * + * If the address is in kernel space (>= TASK_SIZE), then we are + * probably faulting in the vmalloc() area. + * + * If the init_task's first level page tables contains the relevant + * entry, we copy the it to this task.  If not, we send the process + * a signal, fixup the exception, or oops the kernel. + * + * NOTE! We MUST NOT take any locks for this case. We may be in an + * interrupt or a critical region, and should only copy the information + * from the master page table, nothing more. + */ +static int do_ifault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) +{ +	unsigned int index; +	pgd_t *pgd, *pgd_k; +	pmd_t *pmd, *pmd_k; + +	if (addr < TASK_SIZE) +		return do_pf(addr, fsr, regs); + +	if (user_mode(regs)) +		goto bad_area; + +	index = pgd_index(addr); + +	pgd = cpu_get_pgd() + index; +	pgd_k = init_mm.pgd + index; + +	if (pgd_none(*pgd_k)) +		goto bad_area; + +	pmd_k = pmd_offset((pud_t *) pgd_k, addr); +	pmd = pmd_offset((pud_t *) pgd, addr); + +	if (pmd_none(*pmd_k)) +		goto bad_area; + +	set_pmd(pmd, *pmd_k); +	flush_pmd_entry(pmd); +	return 0; + +bad_area: +	do_bad_area(addr, fsr, regs); +	return 0; +} + +/* + * This abort handler always returns "fault". + */ +static int do_bad(unsigned long addr, unsigned int fsr, struct pt_regs *regs) +{ +	return 1; +} + +static int do_good(unsigned long addr, unsigned int fsr, struct pt_regs *regs) +{ +	unsigned int res1, res2; + +	printk("dabt exception but no error!\n"); + +	__asm__ __volatile__( +			"mff %0,f0\n" +			"mff %1,f1\n" +			: "=r"(res1), "=r"(res2) +			: +			: "memory"); + +	printk(KERN_EMERG "r0 :%08x  r1 :%08x\n", res1, res2); +	panic("shut up\n"); +	return 0; +} + +static struct fsr_info { +	int (*fn) (unsigned long addr, unsigned int fsr, struct pt_regs *regs); +	int sig; +	int code; +	const char *name; +} fsr_info[] = { +	/* +	 * The following are the standard Unicore-I and UniCore-II aborts. +	 */ +	{ do_good,	SIGBUS,  0,		"no error"		}, +	{ do_bad,	SIGBUS,  BUS_ADRALN,	"alignment exception"	}, +	{ do_bad,	SIGBUS,  BUS_OBJERR,	"external exception"	}, +	{ do_bad,	SIGBUS,  0,		"burst operation"	}, +	{ do_bad,	SIGBUS,  0,		"unknown 00100"		}, +	{ do_ifault,	SIGSEGV, SEGV_MAPERR,	"2nd level pt non-exist"}, +	{ do_bad,	SIGBUS,  0,		"2nd lvl large pt non-exist" }, +	{ do_bad,	SIGBUS,  0,		"invalid pte"		}, +	{ do_pf,	SIGSEGV, SEGV_MAPERR,	"page miss"		}, +	{ do_bad,	SIGBUS,  0,		"middle page miss"	}, +	{ do_bad,	SIGBUS,	 0,		"large page miss"	}, +	{ do_pf,	SIGSEGV, SEGV_MAPERR,	"super page (section) miss" }, +	{ do_bad,	SIGBUS,  0,		"unknown 01100"		}, +	{ do_bad,	SIGBUS,  0,		"unknown 01101"		}, +	{ do_bad,	SIGBUS,  0,		"unknown 01110"		}, +	{ do_bad,	SIGBUS,  0,		"unknown 01111"		}, +	{ do_bad,	SIGBUS,  0,		"addr: up 3G or IO"	}, +	{ do_pf,	SIGSEGV, SEGV_ACCERR,	"read unreadable addr"	}, +	{ do_pf,	SIGSEGV, SEGV_ACCERR,	"write unwriteable addr"}, +	{ do_pf,	SIGSEGV, SEGV_ACCERR,	"exec unexecutable addr"}, +	{ do_bad,	SIGBUS,  0,		"unknown 10100"		}, +	{ do_bad,	SIGBUS,  0,		"unknown 10101"		}, +	{ do_bad,	SIGBUS,  0,		"unknown 10110"		}, +	{ do_bad,	SIGBUS,  0,		"unknown 10111"		}, +	{ do_bad,	SIGBUS,  0,		"unknown 11000"		}, +	{ do_bad,	SIGBUS,  0,		"unknown 11001"		}, +	{ do_bad,	SIGBUS,  0,		"unknown 11010"		}, +	{ do_bad,	SIGBUS,  0,		"unknown 11011"		}, +	{ do_bad,	SIGBUS,  0,		"unknown 11100"		}, +	{ do_bad,	SIGBUS,  0,		"unknown 11101"		}, +	{ do_bad,	SIGBUS,  0,		"unknown 11110"		}, +	{ do_bad,	SIGBUS,  0,		"unknown 11111"		} +}; + +void __init hook_fault_code(int nr, +		int (*fn) (unsigned long, unsigned int, struct pt_regs *), +		int sig, int code, const char *name) +{ +	if (nr < 0 || nr >= ARRAY_SIZE(fsr_info)) +		BUG(); + +	fsr_info[nr].fn   = fn; +	fsr_info[nr].sig  = sig; +	fsr_info[nr].code = code; +	fsr_info[nr].name = name; +} + +/* + * Dispatch a data abort to the relevant handler. + */ +asmlinkage void do_DataAbort(unsigned long addr, unsigned int fsr, +			struct pt_regs *regs) +{ +	const struct fsr_info *inf = fsr_info + fsr_fs(fsr); +	struct siginfo info; + +	if (!inf->fn(addr, fsr & ~FSR_LNX_PF, regs)) +		return; + +	printk(KERN_ALERT "Unhandled fault: %s (0x%03x) at 0x%08lx\n", +	       inf->name, fsr, addr); + +	info.si_signo = inf->sig; +	info.si_errno = 0; +	info.si_code = inf->code; +	info.si_addr = (void __user *)addr; +	uc32_notify_die("", regs, &info, fsr, 0); +} + +asmlinkage void do_PrefetchAbort(unsigned long addr, +			unsigned int ifsr, struct pt_regs *regs) +{ +	const struct fsr_info *inf = fsr_info + fsr_fs(ifsr); +	struct siginfo info; + +	if (!inf->fn(addr, ifsr | FSR_LNX_PF, regs)) +		return; + +	printk(KERN_ALERT "Unhandled prefetch abort: %s (0x%03x) at 0x%08lx\n", +	       inf->name, ifsr, addr); + +	info.si_signo = inf->sig; +	info.si_errno = 0; +	info.si_code = inf->code; +	info.si_addr = (void __user *)addr; +	uc32_notify_die("", regs, &info, ifsr, 0); +} diff --git a/arch/unicore32/mm/flush.c b/arch/unicore32/mm/flush.c new file mode 100644 index 00000000000..6d4c096ffa2 --- /dev/null +++ b/arch/unicore32/mm/flush.c @@ -0,0 +1,97 @@ +/* + * linux/arch/unicore32/mm/flush.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/mm.h> +#include <linux/pagemap.h> + +#include <asm/cacheflush.h> +#include <asm/tlbflush.h> + +void flush_cache_mm(struct mm_struct *mm) +{ +} + +void flush_cache_range(struct vm_area_struct *vma, unsigned long start, +		unsigned long end) +{ +	if (vma->vm_flags & VM_EXEC) +		__flush_icache_all(); +} + +void flush_cache_page(struct vm_area_struct *vma, unsigned long user_addr, +		unsigned long pfn) +{ +} + +static void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, +			 unsigned long uaddr, void *kaddr, unsigned long len) +{ +	/* VIPT non-aliasing D-cache */ +	if (vma->vm_flags & VM_EXEC) { +		unsigned long addr = (unsigned long)kaddr; + +		__cpuc_coherent_kern_range(addr, addr + len); +	} +} + +/* + * Copy user data from/to a page which is mapped into a different + * processes address space.  Really, we want to allow our "user + * space" model to handle this. + * + * Note that this code needs to run on the current CPU. + */ +void copy_to_user_page(struct vm_area_struct *vma, struct page *page, +		       unsigned long uaddr, void *dst, const void *src, +		       unsigned long len) +{ +	memcpy(dst, src, len); +	flush_ptrace_access(vma, page, uaddr, dst, len); +} + +void __flush_dcache_page(struct address_space *mapping, struct page *page) +{ +	/* +	 * Writeback any data associated with the kernel mapping of this +	 * page.  This ensures that data in the physical page is mutually +	 * coherent with the kernels mapping. +	 */ +	__cpuc_flush_kern_dcache_area(page_address(page), PAGE_SIZE); +} + +/* + * Ensure cache coherency between kernel mapping and userspace mapping + * of this page. + */ +void flush_dcache_page(struct page *page) +{ +	struct address_space *mapping; + +	/* +	 * The zero page is never written to, so never has any dirty +	 * cache lines, and therefore never needs to be flushed. +	 */ +	if (page == ZERO_PAGE(0)) +		return; + +	mapping = page_mapping(page); + +	if (mapping && !mapping_mapped(mapping)) +		clear_bit(PG_dcache_clean, &page->flags); +	else { +		__flush_dcache_page(mapping, page); +		if (mapping) +			__flush_icache_all(); +		set_bit(PG_dcache_clean, &page->flags); +	} +} +EXPORT_SYMBOL(flush_dcache_page); diff --git a/arch/unicore32/mm/init.c b/arch/unicore32/mm/init.c new file mode 100644 index 00000000000..be2bde9b07c --- /dev/null +++ b/arch/unicore32/mm/init.c @@ -0,0 +1,451 @@ +/* + *  linux/arch/unicore32/mm/init.c + * + *  Copyright (C) 2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/swap.h> +#include <linux/init.h> +#include <linux/bootmem.h> +#include <linux/mman.h> +#include <linux/nodemask.h> +#include <linux/initrd.h> +#include <linux/highmem.h> +#include <linux/gfp.h> +#include <linux/memblock.h> +#include <linux/sort.h> +#include <linux/dma-mapping.h> +#include <linux/export.h> + +#include <asm/sections.h> +#include <asm/setup.h> +#include <asm/sizes.h> +#include <asm/tlb.h> +#include <asm/memblock.h> +#include <mach/map.h> + +#include "mm.h" + +static unsigned long phys_initrd_start __initdata = 0x01000000; +static unsigned long phys_initrd_size __initdata = SZ_8M; + +static int __init early_initrd(char *p) +{ +	unsigned long start, size; +	char *endp; + +	start = memparse(p, &endp); +	if (*endp == ',') { +		size = memparse(endp + 1, NULL); + +		phys_initrd_start = start; +		phys_initrd_size = size; +	} +	return 0; +} +early_param("initrd", early_initrd); + +/* + * This keeps memory configuration data used by a couple memory + * initialization functions, as well as show_mem() for the skipping + * of holes in the memory map.  It is populated by uc32_add_memory(). + */ +struct meminfo meminfo; + +void show_mem(unsigned int filter) +{ +	int free = 0, total = 0, reserved = 0; +	int shared = 0, cached = 0, slab = 0, i; +	struct meminfo *mi = &meminfo; + +	printk(KERN_DEFAULT "Mem-info:\n"); +	show_free_areas(filter); + +	for_each_bank(i, mi) { +		struct membank *bank = &mi->bank[i]; +		unsigned int pfn1, pfn2; +		struct page *page, *end; + +		pfn1 = bank_pfn_start(bank); +		pfn2 = bank_pfn_end(bank); + +		page = pfn_to_page(pfn1); +		end  = pfn_to_page(pfn2 - 1) + 1; + +		do { +			total++; +			if (PageReserved(page)) +				reserved++; +			else if (PageSwapCache(page)) +				cached++; +			else if (PageSlab(page)) +				slab++; +			else if (!page_count(page)) +				free++; +			else +				shared += page_count(page) - 1; +			page++; +		} while (page < end); +	} + +	printk(KERN_DEFAULT "%d pages of RAM\n", total); +	printk(KERN_DEFAULT "%d free pages\n", free); +	printk(KERN_DEFAULT "%d reserved pages\n", reserved); +	printk(KERN_DEFAULT "%d slab pages\n", slab); +	printk(KERN_DEFAULT "%d pages shared\n", shared); +	printk(KERN_DEFAULT "%d pages swap cached\n", cached); +} + +static void __init find_limits(unsigned long *min, unsigned long *max_low, +	unsigned long *max_high) +{ +	struct meminfo *mi = &meminfo; +	int i; + +	*min = -1UL; +	*max_low = *max_high = 0; + +	for_each_bank(i, mi) { +		struct membank *bank = &mi->bank[i]; +		unsigned long start, end; + +		start = bank_pfn_start(bank); +		end = bank_pfn_end(bank); + +		if (*min > start) +			*min = start; +		if (*max_high < end) +			*max_high = end; +		if (bank->highmem) +			continue; +		if (*max_low < end) +			*max_low = end; +	} +} + +static void __init uc32_bootmem_init(unsigned long start_pfn, +	unsigned long end_pfn) +{ +	struct memblock_region *reg; +	unsigned int boot_pages; +	phys_addr_t bitmap; +	pg_data_t *pgdat; + +	/* +	 * Allocate the bootmem bitmap page.  This must be in a region +	 * of memory which has already been mapped. +	 */ +	boot_pages = bootmem_bootmap_pages(end_pfn - start_pfn); +	bitmap = memblock_alloc_base(boot_pages << PAGE_SHIFT, L1_CACHE_BYTES, +				__pfn_to_phys(end_pfn)); + +	/* +	 * Initialise the bootmem allocator, handing the +	 * memory banks over to bootmem. +	 */ +	node_set_online(0); +	pgdat = NODE_DATA(0); +	init_bootmem_node(pgdat, __phys_to_pfn(bitmap), start_pfn, end_pfn); + +	/* Free the lowmem regions from memblock into bootmem. */ +	for_each_memblock(memory, reg) { +		unsigned long start = memblock_region_memory_base_pfn(reg); +		unsigned long end = memblock_region_memory_end_pfn(reg); + +		if (end >= end_pfn) +			end = end_pfn; +		if (start >= end) +			break; + +		free_bootmem(__pfn_to_phys(start), (end - start) << PAGE_SHIFT); +	} + +	/* Reserve the lowmem memblock reserved regions in bootmem. */ +	for_each_memblock(reserved, reg) { +		unsigned long start = memblock_region_reserved_base_pfn(reg); +		unsigned long end = memblock_region_reserved_end_pfn(reg); + +		if (end >= end_pfn) +			end = end_pfn; +		if (start >= end) +			break; + +		reserve_bootmem(__pfn_to_phys(start), +			(end - start) << PAGE_SHIFT, BOOTMEM_DEFAULT); +	} +} + +static void __init uc32_bootmem_free(unsigned long min, unsigned long max_low, +	unsigned long max_high) +{ +	unsigned long zone_size[MAX_NR_ZONES], zhole_size[MAX_NR_ZONES]; +	struct memblock_region *reg; + +	/* +	 * initialise the zones. +	 */ +	memset(zone_size, 0, sizeof(zone_size)); + +	/* +	 * The memory size has already been determined.  If we need +	 * to do anything fancy with the allocation of this memory +	 * to the zones, now is the time to do it. +	 */ +	zone_size[0] = max_low - min; + +	/* +	 * Calculate the size of the holes. +	 *  holes = node_size - sum(bank_sizes) +	 */ +	memcpy(zhole_size, zone_size, sizeof(zhole_size)); +	for_each_memblock(memory, reg) { +		unsigned long start = memblock_region_memory_base_pfn(reg); +		unsigned long end = memblock_region_memory_end_pfn(reg); + +		if (start < max_low) { +			unsigned long low_end = min(end, max_low); +			zhole_size[0] -= low_end - start; +		} +	} + +	/* +	 * Adjust the sizes according to any special requirements for +	 * this machine type. +	 */ +	arch_adjust_zones(zone_size, zhole_size); + +	free_area_init_node(0, zone_size, min, zhole_size); +} + +int pfn_valid(unsigned long pfn) +{ +	return memblock_is_memory(pfn << PAGE_SHIFT); +} +EXPORT_SYMBOL(pfn_valid); + +static void uc32_memory_present(void) +{ +} + +static int __init meminfo_cmp(const void *_a, const void *_b) +{ +	const struct membank *a = _a, *b = _b; +	long cmp = bank_pfn_start(a) - bank_pfn_start(b); +	return cmp < 0 ? -1 : cmp > 0 ? 1 : 0; +} + +void __init uc32_memblock_init(struct meminfo *mi) +{ +	int i; + +	sort(&meminfo.bank, meminfo.nr_banks, sizeof(meminfo.bank[0]), +		meminfo_cmp, NULL); + +	for (i = 0; i < mi->nr_banks; i++) +		memblock_add(mi->bank[i].start, mi->bank[i].size); + +	/* Register the kernel text, kernel data and initrd with memblock. */ +	memblock_reserve(__pa(_text), _end - _text); + +#ifdef CONFIG_BLK_DEV_INITRD +	if (phys_initrd_size) { +		memblock_reserve(phys_initrd_start, phys_initrd_size); + +		/* Now convert initrd to virtual addresses */ +		initrd_start = __phys_to_virt(phys_initrd_start); +		initrd_end = initrd_start + phys_initrd_size; +	} +#endif + +	uc32_mm_memblock_reserve(); + +	memblock_allow_resize(); +	memblock_dump_all(); +} + +void __init bootmem_init(void) +{ +	unsigned long min, max_low, max_high; + +	max_low = max_high = 0; + +	find_limits(&min, &max_low, &max_high); + +	uc32_bootmem_init(min, max_low); + +#ifdef CONFIG_SWIOTLB +	swiotlb_init(1); +#endif +	/* +	 * Sparsemem tries to allocate bootmem in memory_present(), +	 * so must be done after the fixed reservations +	 */ +	uc32_memory_present(); + +	/* +	 * sparse_init() needs the bootmem allocator up and running. +	 */ +	sparse_init(); + +	/* +	 * Now free the memory - free_area_init_node needs +	 * the sparse mem_map arrays initialized by sparse_init() +	 * for memmap_init_zone(), otherwise all PFNs are invalid. +	 */ +	uc32_bootmem_free(min, max_low, max_high); + +	high_memory = __va((max_low << PAGE_SHIFT) - 1) + 1; + +	/* +	 * This doesn't seem to be used by the Linux memory manager any +	 * more, but is used by ll_rw_block.  If we can get rid of it, we +	 * also get rid of some of the stuff above as well. +	 * +	 * Note: max_low_pfn and max_pfn reflect the number of _pages_ in +	 * the system, not the maximum PFN. +	 */ +	max_low_pfn = max_low - PHYS_PFN_OFFSET; +	max_pfn = max_high - PHYS_PFN_OFFSET; +} + +static inline void +free_memmap(unsigned long start_pfn, unsigned long end_pfn) +{ +	struct page *start_pg, *end_pg; +	unsigned long pg, pgend; + +	/* +	 * Convert start_pfn/end_pfn to a struct page pointer. +	 */ +	start_pg = pfn_to_page(start_pfn - 1) + 1; +	end_pg = pfn_to_page(end_pfn); + +	/* +	 * Convert to physical addresses, and +	 * round start upwards and end downwards. +	 */ +	pg = PAGE_ALIGN(__pa(start_pg)); +	pgend = __pa(end_pg) & PAGE_MASK; + +	/* +	 * If there are free pages between these, +	 * free the section of the memmap array. +	 */ +	if (pg < pgend) +		free_bootmem(pg, pgend - pg); +} + +/* + * The mem_map array can get very big.  Free the unused area of the memory map. + */ +static void __init free_unused_memmap(struct meminfo *mi) +{ +	unsigned long bank_start, prev_bank_end = 0; +	unsigned int i; + +	/* +	 * This relies on each bank being in address order. +	 * The banks are sorted previously in bootmem_init(). +	 */ +	for_each_bank(i, mi) { +		struct membank *bank = &mi->bank[i]; + +		bank_start = bank_pfn_start(bank); + +		/* +		 * If we had a previous bank, and there is a space +		 * between the current bank and the previous, free it. +		 */ +		if (prev_bank_end && prev_bank_end < bank_start) +			free_memmap(prev_bank_end, bank_start); + +		/* +		 * Align up here since the VM subsystem insists that the +		 * memmap entries are valid from the bank end aligned to +		 * MAX_ORDER_NR_PAGES. +		 */ +		prev_bank_end = ALIGN(bank_pfn_end(bank), MAX_ORDER_NR_PAGES); +	} +} + +/* + * mem_init() marks the free areas in the mem_map and tells us how much + * memory is free.  This is done after various parts of the system have + * claimed their memory after the kernel image. + */ +void __init mem_init(void) +{ +	max_mapnr   = pfn_to_page(max_pfn + PHYS_PFN_OFFSET) - mem_map; + +	free_unused_memmap(&meminfo); + +	/* this will put all unused low memory onto the freelists */ +	free_all_bootmem(); + +	mem_init_print_info(NULL); +	printk(KERN_NOTICE "Virtual kernel memory layout:\n" +		"    vector  : 0x%08lx - 0x%08lx   (%4ld kB)\n" +		"    vmalloc : 0x%08lx - 0x%08lx   (%4ld MB)\n" +		"    lowmem  : 0x%08lx - 0x%08lx   (%4ld MB)\n" +		"    modules : 0x%08lx - 0x%08lx   (%4ld MB)\n" +		"      .init : 0x%p" " - 0x%p" "   (%4d kB)\n" +		"      .text : 0x%p" " - 0x%p" "   (%4d kB)\n" +		"      .data : 0x%p" " - 0x%p" "   (%4d kB)\n", + +		VECTORS_BASE, VECTORS_BASE + PAGE_SIZE, +		DIV_ROUND_UP(PAGE_SIZE, SZ_1K), +		VMALLOC_START, VMALLOC_END, +		DIV_ROUND_UP((VMALLOC_END - VMALLOC_START), SZ_1M), +		PAGE_OFFSET, (unsigned long)high_memory, +		DIV_ROUND_UP(((unsigned long)high_memory - PAGE_OFFSET), SZ_1M), +		MODULES_VADDR, MODULES_END, +		DIV_ROUND_UP((MODULES_END - MODULES_VADDR), SZ_1M), + +		__init_begin, __init_end, +		DIV_ROUND_UP((__init_end - __init_begin), SZ_1K), +		_stext, _etext, +		DIV_ROUND_UP((_etext - _stext), SZ_1K), +		_sdata, _edata, +		DIV_ROUND_UP((_edata - _sdata), SZ_1K)); + +	BUILD_BUG_ON(TASK_SIZE				> MODULES_VADDR); +	BUG_ON(TASK_SIZE				> MODULES_VADDR); + +	if (PAGE_SIZE >= 16384 && get_num_physpages() <= 128) { +		/* +		 * On a machine this small we won't get +		 * anywhere without overcommit, so turn +		 * it on by default. +		 */ +		sysctl_overcommit_memory = OVERCOMMIT_ALWAYS; +	} +} + +void free_initmem(void) +{ +	free_initmem_default(-1); +} + +#ifdef CONFIG_BLK_DEV_INITRD + +static int keep_initrd; + +void free_initrd_mem(unsigned long start, unsigned long end) +{ +	if (!keep_initrd) +		free_reserved_area((void *)start, (void *)end, -1, "initrd"); +} + +static int __init keepinitrd_setup(char *__unused) +{ +	keep_initrd = 1; +	return 1; +} + +__setup("keepinitrd", keepinitrd_setup); +#endif diff --git a/arch/unicore32/mm/ioremap.c b/arch/unicore32/mm/ioremap.c new file mode 100644 index 00000000000..bf012b2b71a --- /dev/null +++ b/arch/unicore32/mm/ioremap.c @@ -0,0 +1,254 @@ +/* + * linux/arch/unicore32/mm/ioremap.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * 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. + * + * + * Re-map IO memory to kernel address space so that we can access it. + * + * This allows a driver to remap an arbitrary region of bus memory into + * virtual space.  One should *only* use readl, writel, memcpy_toio and + * so on with such remapped areas. + * + * Because UniCore only has a 32-bit address space we can't address the + * whole of the (physical) PCI space at once.  PCI huge-mode addressing + * allows us to circumvent this restriction by splitting PCI space into + * two 2GB chunks and mapping only one at a time into processor memory. + * We use MMU protection domains to trap any attempt to access the bank + * that is not currently mapped.  (This isn't fully implemented yet.) + */ +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/mm.h> +#include <linux/vmalloc.h> +#include <linux/io.h> + +#include <asm/cputype.h> +#include <asm/cacheflush.h> +#include <asm/mmu_context.h> +#include <asm/pgalloc.h> +#include <asm/tlbflush.h> +#include <asm/sizes.h> + +#include <mach/map.h> +#include "mm.h" + +/* + * Used by ioremap() and iounmap() code to mark (super)section-mapped + * I/O regions in vm_struct->flags field. + */ +#define VM_UNICORE_SECTION_MAPPING	0x80000000 + +int ioremap_page(unsigned long virt, unsigned long phys, +		 const struct mem_type *mtype) +{ +	return ioremap_page_range(virt, virt + PAGE_SIZE, phys, +				  __pgprot(mtype->prot_pte)); +} +EXPORT_SYMBOL(ioremap_page); + +/* + * Section support is unsafe on SMP - If you iounmap and ioremap a region, + * the other CPUs will not see this change until their next context switch. + * Meanwhile, (eg) if an interrupt comes in on one of those other CPUs + * which requires the new ioremap'd region to be referenced, the CPU will + * reference the _old_ region. + * + * Note that get_vm_area_caller() allocates a guard 4K page, so we need to + * mask the size back to 4MB aligned or we will overflow in the loop below. + */ +static void unmap_area_sections(unsigned long virt, unsigned long size) +{ +	unsigned long addr = virt, end = virt + (size & ~(SZ_4M - 1)); +	pgd_t *pgd; + +	flush_cache_vunmap(addr, end); +	pgd = pgd_offset_k(addr); +	do { +		pmd_t pmd, *pmdp = pmd_offset((pud_t *)pgd, addr); + +		pmd = *pmdp; +		if (!pmd_none(pmd)) { +			/* +			 * Clear the PMD from the page table, and +			 * increment the kvm sequence so others +			 * notice this change. +			 * +			 * Note: this is still racy on SMP machines. +			 */ +			pmd_clear(pmdp); + +			/* +			 * Free the page table, if there was one. +			 */ +			if ((pmd_val(pmd) & PMD_TYPE_MASK) == PMD_TYPE_TABLE) +				pte_free_kernel(&init_mm, pmd_page_vaddr(pmd)); +		} + +		addr += PGDIR_SIZE; +		pgd++; +	} while (addr < end); + +	flush_tlb_kernel_range(virt, end); +} + +static int +remap_area_sections(unsigned long virt, unsigned long pfn, +		    size_t size, const struct mem_type *type) +{ +	unsigned long addr = virt, end = virt + size; +	pgd_t *pgd; + +	/* +	 * Remove and free any PTE-based mapping, and +	 * sync the current kernel mapping. +	 */ +	unmap_area_sections(virt, size); + +	pgd = pgd_offset_k(addr); +	do { +		pmd_t *pmd = pmd_offset((pud_t *)pgd, addr); + +		set_pmd(pmd, __pmd(__pfn_to_phys(pfn) | type->prot_sect)); +		pfn += SZ_4M >> PAGE_SHIFT; +		flush_pmd_entry(pmd); + +		addr += PGDIR_SIZE; +		pgd++; +	} while (addr < end); + +	return 0; +} + +void __iomem *__uc32_ioremap_pfn_caller(unsigned long pfn, +	unsigned long offset, size_t size, unsigned int mtype, void *caller) +{ +	const struct mem_type *type; +	int err; +	unsigned long addr; +	struct vm_struct *area; + +	/* +	 * High mappings must be section aligned +	 */ +	if (pfn >= 0x100000 && (__pfn_to_phys(pfn) & ~SECTION_MASK)) +		return NULL; + +	/* +	 * Don't allow RAM to be mapped +	 */ +	if (pfn_valid(pfn)) { +		WARN(1, "BUG: Your driver calls ioremap() on\n" +			"system memory.  This leads to architecturally\n" +			"unpredictable behaviour, and ioremap() will fail in\n" +			"the next kernel release. Please fix your driver.\n"); +		return NULL; +	} + +	type = get_mem_type(mtype); +	if (!type) +		return NULL; + +	/* +	 * Page align the mapping size, taking account of any offset. +	 */ +	size = PAGE_ALIGN(offset + size); + +	area = get_vm_area_caller(size, VM_IOREMAP, caller); +	if (!area) +		return NULL; +	addr = (unsigned long)area->addr; + +	if (!((__pfn_to_phys(pfn) | size | addr) & ~PMD_MASK)) { +		area->flags |= VM_UNICORE_SECTION_MAPPING; +		err = remap_area_sections(addr, pfn, size, type); +	} else +		err = ioremap_page_range(addr, addr + size, __pfn_to_phys(pfn), +					 __pgprot(type->prot_pte)); + +	if (err) { +		vunmap((void *)addr); +		return NULL; +	} + +	flush_cache_vmap(addr, addr + size); +	return (void __iomem *) (offset + addr); +} + +void __iomem *__uc32_ioremap_caller(unsigned long phys_addr, size_t size, +	unsigned int mtype, void *caller) +{ +	unsigned long last_addr; +	unsigned long offset = phys_addr & ~PAGE_MASK; +	unsigned long pfn = __phys_to_pfn(phys_addr); + +	/* +	 * Don't allow wraparound or zero size +	 */ +	last_addr = phys_addr + size - 1; +	if (!size || last_addr < phys_addr) +		return NULL; + +	return __uc32_ioremap_pfn_caller(pfn, offset, size, mtype, caller); +} + +/* + * Remap an arbitrary physical address space into the kernel virtual + * address space. Needed when the kernel wants to access high addresses + * directly. + * + * NOTE! We need to allow non-page-aligned mappings too: we will obviously + * have to convert them into an offset in a page-aligned mapping, but the + * caller shouldn't need to know that small detail. + */ +void __iomem * +__uc32_ioremap_pfn(unsigned long pfn, unsigned long offset, size_t size, +		  unsigned int mtype) +{ +	return __uc32_ioremap_pfn_caller(pfn, offset, size, mtype, +			__builtin_return_address(0)); +} +EXPORT_SYMBOL(__uc32_ioremap_pfn); + +void __iomem * +__uc32_ioremap(unsigned long phys_addr, size_t size) +{ +	return __uc32_ioremap_caller(phys_addr, size, MT_DEVICE, +			__builtin_return_address(0)); +} +EXPORT_SYMBOL(__uc32_ioremap); + +void __iomem * +__uc32_ioremap_cached(unsigned long phys_addr, size_t size) +{ +	return __uc32_ioremap_caller(phys_addr, size, MT_DEVICE_CACHED, +			__builtin_return_address(0)); +} +EXPORT_SYMBOL(__uc32_ioremap_cached); + +void __uc32_iounmap(volatile void __iomem *io_addr) +{ +	void *addr = (void *)(PAGE_MASK & (unsigned long)io_addr); +	struct vm_struct *vm; + +	/* +	 * If this is a section based mapping we need to handle it +	 * specially as the VM subsystem does not know how to handle +	 * such a beast. We need the lock here b/c we need to clear +	 * all the mappings before the area can be reclaimed +	 * by someone else. +	 */ +	vm = find_vm_area(addr); +	if (vm && (vm->flags & VM_IOREMAP) && +		(vm->flags & VM_UNICORE_SECTION_MAPPING)) +		unmap_area_sections((unsigned long)vm->addr, vm->size); + +	vunmap(addr); +} +EXPORT_SYMBOL(__uc32_iounmap); diff --git a/arch/unicore32/mm/mm.h b/arch/unicore32/mm/mm.h new file mode 100644 index 00000000000..05c7f532eee --- /dev/null +++ b/arch/unicore32/mm/mm.h @@ -0,0 +1,44 @@ +/* + * linux/arch/unicore32/mm/mm.h + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <asm/hwdef-copro.h> + +/* the upper-most page table pointer */ +extern pmd_t *top_pmd; +extern int sysctl_overcommit_memory; + +#define TOP_PTE(x)	pte_offset_kernel(top_pmd, x) + +static inline pmd_t *pmd_off(pgd_t *pgd, unsigned long virt) +{ +	return pmd_offset((pud_t *)pgd, virt); +} + +static inline pmd_t *pmd_off_k(unsigned long virt) +{ +	return pmd_off(pgd_offset_k(virt), virt); +} + +struct mem_type { +	unsigned int prot_pte; +	unsigned int prot_l1; +	unsigned int prot_sect; +}; + +const struct mem_type *get_mem_type(unsigned int type); + +extern void __flush_dcache_page(struct address_space *, struct page *); +extern void hook_fault_code(int nr, int (*fn) +		(unsigned long, unsigned int, struct pt_regs *), +		int sig, int code, const char *name); + +void __init bootmem_init(void); +void uc32_mm_memblock_reserve(void); diff --git a/arch/unicore32/mm/mmu.c b/arch/unicore32/mm/mmu.c new file mode 100644 index 00000000000..4f5a532bee1 --- /dev/null +++ b/arch/unicore32/mm/mmu.c @@ -0,0 +1,512 @@ +/* + * linux/arch/unicore32/mm/mmu.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/mman.h> +#include <linux/nodemask.h> +#include <linux/memblock.h> +#include <linux/fs.h> +#include <linux/bootmem.h> +#include <linux/io.h> + +#include <asm/cputype.h> +#include <asm/sections.h> +#include <asm/setup.h> +#include <asm/sizes.h> +#include <asm/tlb.h> +#include <asm/memblock.h> + +#include <mach/map.h> + +#include "mm.h" + +/* + * empty_zero_page is a special page that is used for + * zero-initialized data and COW. + */ +struct page *empty_zero_page; +EXPORT_SYMBOL(empty_zero_page); + +/* + * The pmd table for the upper-most set of pages. + */ +pmd_t *top_pmd; + +pgprot_t pgprot_user; +EXPORT_SYMBOL(pgprot_user); + +pgprot_t pgprot_kernel; +EXPORT_SYMBOL(pgprot_kernel); + +static int __init noalign_setup(char *__unused) +{ +	cr_alignment &= ~CR_A; +	cr_no_alignment &= ~CR_A; +	set_cr(cr_alignment); +	return 1; +} +__setup("noalign", noalign_setup); + +void adjust_cr(unsigned long mask, unsigned long set) +{ +	unsigned long flags; + +	mask &= ~CR_A; + +	set &= mask; + +	local_irq_save(flags); + +	cr_no_alignment = (cr_no_alignment & ~mask) | set; +	cr_alignment = (cr_alignment & ~mask) | set; + +	set_cr((get_cr() & ~mask) | set); + +	local_irq_restore(flags); +} + +struct map_desc { +	unsigned long virtual; +	unsigned long pfn; +	unsigned long length; +	unsigned int type; +}; + +#define PROT_PTE_DEVICE		(PTE_PRESENT | PTE_YOUNG |	\ +				PTE_DIRTY | PTE_READ | PTE_WRITE) +#define PROT_SECT_DEVICE	(PMD_TYPE_SECT | PMD_PRESENT |	\ +				PMD_SECT_READ | PMD_SECT_WRITE) + +static struct mem_type mem_types[] = { +	[MT_DEVICE] = {		  /* Strongly ordered */ +		.prot_pte	= PROT_PTE_DEVICE, +		.prot_l1	= PMD_TYPE_TABLE | PMD_PRESENT, +		.prot_sect	= PROT_SECT_DEVICE, +	}, +	/* +	 * MT_KUSER: pte for vecpage -- cacheable, +	 *       and sect for unigfx mmap -- noncacheable +	 */ +	[MT_KUSER] = { +		.prot_pte  = PTE_PRESENT | PTE_YOUNG | PTE_DIRTY | +				PTE_CACHEABLE | PTE_READ | PTE_EXEC, +		.prot_l1   = PMD_TYPE_TABLE | PMD_PRESENT, +		.prot_sect = PROT_SECT_DEVICE, +	}, +	[MT_HIGH_VECTORS] = { +		.prot_pte  = PTE_PRESENT | PTE_YOUNG | PTE_DIRTY | +				PTE_CACHEABLE | PTE_READ | PTE_WRITE | +				PTE_EXEC, +		.prot_l1   = PMD_TYPE_TABLE | PMD_PRESENT, +	}, +	[MT_MEMORY] = { +		.prot_pte  = PTE_PRESENT | PTE_YOUNG | PTE_DIRTY | +				PTE_WRITE | PTE_EXEC, +		.prot_l1   = PMD_TYPE_TABLE | PMD_PRESENT, +		.prot_sect = PMD_TYPE_SECT | PMD_PRESENT | PMD_SECT_CACHEABLE | +				PMD_SECT_READ | PMD_SECT_WRITE | PMD_SECT_EXEC, +	}, +	[MT_ROM] = { +		.prot_sect = PMD_TYPE_SECT | PMD_PRESENT | PMD_SECT_CACHEABLE | +				PMD_SECT_READ, +	}, +}; + +const struct mem_type *get_mem_type(unsigned int type) +{ +	return type < ARRAY_SIZE(mem_types) ? &mem_types[type] : NULL; +} +EXPORT_SYMBOL(get_mem_type); + +/* + * Adjust the PMD section entries according to the CPU in use. + */ +static void __init build_mem_type_table(void) +{ +	pgprot_user   = __pgprot(PTE_PRESENT | PTE_YOUNG | PTE_CACHEABLE); +	pgprot_kernel = __pgprot(PTE_PRESENT | PTE_YOUNG | +				 PTE_DIRTY | PTE_READ | PTE_WRITE | +				 PTE_EXEC | PTE_CACHEABLE); +} + +#define vectors_base()	(vectors_high() ? 0xffff0000 : 0) + +static void __init *early_alloc(unsigned long sz) +{ +	void *ptr = __va(memblock_alloc(sz, sz)); +	memset(ptr, 0, sz); +	return ptr; +} + +static pte_t * __init early_pte_alloc(pmd_t *pmd, unsigned long addr, +		unsigned long prot) +{ +	if (pmd_none(*pmd)) { +		pte_t *pte = early_alloc(PTRS_PER_PTE * sizeof(pte_t)); +		__pmd_populate(pmd, __pa(pte) | prot); +	} +	BUG_ON(pmd_bad(*pmd)); +	return pte_offset_kernel(pmd, addr); +} + +static void __init alloc_init_pte(pmd_t *pmd, unsigned long addr, +				  unsigned long end, unsigned long pfn, +				  const struct mem_type *type) +{ +	pte_t *pte = early_pte_alloc(pmd, addr, type->prot_l1); +	do { +		set_pte(pte, pfn_pte(pfn, __pgprot(type->prot_pte))); +		pfn++; +	} while (pte++, addr += PAGE_SIZE, addr != end); +} + +static void __init alloc_init_section(pgd_t *pgd, unsigned long addr, +				      unsigned long end, unsigned long phys, +				      const struct mem_type *type) +{ +	pmd_t *pmd = pmd_offset((pud_t *)pgd, addr); + +	/* +	 * Try a section mapping - end, addr and phys must all be aligned +	 * to a section boundary. +	 */ +	if (((addr | end | phys) & ~SECTION_MASK) == 0) { +		pmd_t *p = pmd; + +		do { +			set_pmd(pmd, __pmd(phys | type->prot_sect)); +			phys += SECTION_SIZE; +		} while (pmd++, addr += SECTION_SIZE, addr != end); + +		flush_pmd_entry(p); +	} else { +		/* +		 * No need to loop; pte's aren't interested in the +		 * individual L1 entries. +		 */ +		alloc_init_pte(pmd, addr, end, __phys_to_pfn(phys), type); +	} +} + +/* + * Create the page directory entries and any necessary + * page tables for the mapping specified by `md'.  We + * are able to cope here with varying sizes and address + * offsets, and we take full advantage of sections. + */ +static void __init create_mapping(struct map_desc *md) +{ +	unsigned long phys, addr, length, end; +	const struct mem_type *type; +	pgd_t *pgd; + +	if (md->virtual != vectors_base() && md->virtual < TASK_SIZE) { +		printk(KERN_WARNING "BUG: not creating mapping for " +		       "0x%08llx at 0x%08lx in user region\n", +		       __pfn_to_phys((u64)md->pfn), md->virtual); +		return; +	} + +	if ((md->type == MT_DEVICE || md->type == MT_ROM) && +	    md->virtual >= PAGE_OFFSET && md->virtual < VMALLOC_END) { +		printk(KERN_WARNING "BUG: mapping for 0x%08llx at 0x%08lx " +		       "overlaps vmalloc space\n", +		       __pfn_to_phys((u64)md->pfn), md->virtual); +	} + +	type = &mem_types[md->type]; + +	addr = md->virtual & PAGE_MASK; +	phys = (unsigned long)__pfn_to_phys(md->pfn); +	length = PAGE_ALIGN(md->length + (md->virtual & ~PAGE_MASK)); + +	if (type->prot_l1 == 0 && ((addr | phys | length) & ~SECTION_MASK)) { +		printk(KERN_WARNING "BUG: map for 0x%08lx at 0x%08lx can not " +		       "be mapped using pages, ignoring.\n", +		       __pfn_to_phys(md->pfn), addr); +		return; +	} + +	pgd = pgd_offset_k(addr); +	end = addr + length; +	do { +		unsigned long next = pgd_addr_end(addr, end); + +		alloc_init_section(pgd, addr, next, phys, type); + +		phys += next - addr; +		addr = next; +	} while (pgd++, addr != end); +} + +static void * __initdata vmalloc_min = (void *)(VMALLOC_END - SZ_128M); + +/* + * vmalloc=size forces the vmalloc area to be exactly 'size' + * bytes. This can be used to increase (or decrease) the vmalloc + * area - the default is 128m. + */ +static int __init early_vmalloc(char *arg) +{ +	unsigned long vmalloc_reserve = memparse(arg, NULL); + +	if (vmalloc_reserve < SZ_16M) { +		vmalloc_reserve = SZ_16M; +		printk(KERN_WARNING +			"vmalloc area too small, limiting to %luMB\n", +			vmalloc_reserve >> 20); +	} + +	if (vmalloc_reserve > VMALLOC_END - (PAGE_OFFSET + SZ_32M)) { +		vmalloc_reserve = VMALLOC_END - (PAGE_OFFSET + SZ_32M); +		printk(KERN_WARNING +			"vmalloc area is too big, limiting to %luMB\n", +			vmalloc_reserve >> 20); +	} + +	vmalloc_min = (void *)(VMALLOC_END - vmalloc_reserve); +	return 0; +} +early_param("vmalloc", early_vmalloc); + +static phys_addr_t lowmem_limit __initdata = SZ_1G; + +static void __init sanity_check_meminfo(void) +{ +	int i, j; + +	lowmem_limit = __pa(vmalloc_min - 1) + 1; +	memblock_set_current_limit(lowmem_limit); + +	for (i = 0, j = 0; i < meminfo.nr_banks; i++) { +		struct membank *bank = &meminfo.bank[j]; +		*bank = meminfo.bank[i]; +		j++; +	} +	meminfo.nr_banks = j; +} + +static inline void prepare_page_table(void) +{ +	unsigned long addr; +	phys_addr_t end; + +	/* +	 * Clear out all the mappings below the kernel image. +	 */ +	for (addr = 0; addr < MODULES_VADDR; addr += PGDIR_SIZE) +		pmd_clear(pmd_off_k(addr)); + +	for ( ; addr < PAGE_OFFSET; addr += PGDIR_SIZE) +		pmd_clear(pmd_off_k(addr)); + +	/* +	 * Find the end of the first block of lowmem. +	 */ +	end = memblock.memory.regions[0].base + memblock.memory.regions[0].size; +	if (end >= lowmem_limit) +		end = lowmem_limit; + +	/* +	 * Clear out all the kernel space mappings, except for the first +	 * memory bank, up to the end of the vmalloc region. +	 */ +	for (addr = __phys_to_virt(end); +	     addr < VMALLOC_END; addr += PGDIR_SIZE) +		pmd_clear(pmd_off_k(addr)); +} + +/* + * Reserve the special regions of memory + */ +void __init uc32_mm_memblock_reserve(void) +{ +	/* +	 * Reserve the page tables.  These are already in use, +	 * and can only be in node 0. +	 */ +	memblock_reserve(__pa(swapper_pg_dir), PTRS_PER_PGD * sizeof(pgd_t)); +} + +/* + * Set up device the mappings.  Since we clear out the page tables for all + * mappings above VMALLOC_END, we will remove any debug device mappings. + * This means you have to be careful how you debug this function, or any + * called function.  This means you can't use any function or debugging + * method which may touch any device, otherwise the kernel _will_ crash. + */ +static void __init devicemaps_init(void) +{ +	struct map_desc map; +	unsigned long addr; +	void *vectors; + +	/* +	 * Allocate the vector page early. +	 */ +	vectors = early_alloc(PAGE_SIZE); + +	for (addr = VMALLOC_END; addr; addr += PGDIR_SIZE) +		pmd_clear(pmd_off_k(addr)); + +	/* +	 * Create a mapping for the machine vectors at the high-vectors +	 * location (0xffff0000).  If we aren't using high-vectors, also +	 * create a mapping at the low-vectors virtual address. +	 */ +	map.pfn = __phys_to_pfn(virt_to_phys(vectors)); +	map.virtual = VECTORS_BASE; +	map.length = PAGE_SIZE; +	map.type = MT_HIGH_VECTORS; +	create_mapping(&map); + +	/* +	 * Create a mapping for the kuser page at the special +	 * location (0xbfff0000) to the same vectors location. +	 */ +	map.pfn = __phys_to_pfn(virt_to_phys(vectors)); +	map.virtual = KUSER_VECPAGE_BASE; +	map.length = PAGE_SIZE; +	map.type = MT_KUSER; +	create_mapping(&map); + +	/* +	 * Finally flush the caches and tlb to ensure that we're in a +	 * consistent state wrt the writebuffer.  This also ensures that +	 * any write-allocated cache lines in the vector page are written +	 * back.  After this point, we can start to touch devices again. +	 */ +	local_flush_tlb_all(); +	flush_cache_all(); +} + +static void __init map_lowmem(void) +{ +	struct memblock_region *reg; + +	/* Map all the lowmem memory banks. */ +	for_each_memblock(memory, reg) { +		phys_addr_t start = reg->base; +		phys_addr_t end = start + reg->size; +		struct map_desc map; + +		if (end > lowmem_limit) +			end = lowmem_limit; +		if (start >= end) +			break; + +		map.pfn = __phys_to_pfn(start); +		map.virtual = __phys_to_virt(start); +		map.length = end - start; +		map.type = MT_MEMORY; + +		create_mapping(&map); +	} +} + +/* + * paging_init() sets up the page tables, initialises the zone memory + * maps, and sets up the zero page, bad page and bad page tables. + */ +void __init paging_init(void) +{ +	void *zero_page; + +	build_mem_type_table(); +	sanity_check_meminfo(); +	prepare_page_table(); +	map_lowmem(); +	devicemaps_init(); + +	top_pmd = pmd_off_k(0xffff0000); + +	/* allocate the zero page. */ +	zero_page = early_alloc(PAGE_SIZE); + +	bootmem_init(); + +	empty_zero_page = virt_to_page(zero_page); +	__flush_dcache_page(NULL, empty_zero_page); +} + +/* + * In order to soft-boot, we need to insert a 1:1 mapping in place of + * the user-mode pages.  This will then ensure that we have predictable + * results when turning the mmu off + */ +void setup_mm_for_reboot(void) +{ +	unsigned long base_pmdval; +	pgd_t *pgd; +	int i; + +	/* +	 * We need to access to user-mode page tables here. For kernel threads +	 * we don't have any user-mode mappings so we use the context that we +	 * "borrowed". +	 */ +	pgd = current->active_mm->pgd; + +	base_pmdval = PMD_SECT_WRITE | PMD_SECT_READ | PMD_TYPE_SECT; + +	for (i = 0; i < FIRST_USER_PGD_NR + USER_PTRS_PER_PGD; i++, pgd++) { +		unsigned long pmdval = (i << PGDIR_SHIFT) | base_pmdval; +		pmd_t *pmd; + +		pmd = pmd_off(pgd, i << PGDIR_SHIFT); +		set_pmd(pmd, __pmd(pmdval)); +		flush_pmd_entry(pmd); +	} + +	local_flush_tlb_all(); +} + +/* + * Take care of architecture specific things when placing a new PTE into + * a page table, or changing an existing PTE.  Basically, there are two + * things that we need to take care of: + * + *  1. If PG_dcache_clean is not set for the page, we need to ensure + *     that any cache entries for the kernels virtual memory + *     range are written back to the page. + *  2. If we have multiple shared mappings of the same space in + *     an object, we need to deal with the cache aliasing issues. + * + * Note that the pte lock will be held. + */ +void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, +	pte_t *ptep) +{ +	unsigned long pfn = pte_pfn(*ptep); +	struct address_space *mapping; +	struct page *page; + +	if (!pfn_valid(pfn)) +		return; + +	/* +	 * The zero page is never written to, so never has any dirty +	 * cache lines, and therefore never needs to be flushed. +	 */ +	page = pfn_to_page(pfn); +	if (page == ZERO_PAGE(0)) +		return; + +	mapping = page_mapping(page); +	if (!test_and_set_bit(PG_dcache_clean, &page->flags)) +		__flush_dcache_page(mapping, page); +	if (mapping) +		if (vma->vm_flags & VM_EXEC) +			__flush_icache_all(); +} diff --git a/arch/unicore32/mm/pgd.c b/arch/unicore32/mm/pgd.c new file mode 100644 index 00000000000..08b8d4295e7 --- /dev/null +++ b/arch/unicore32/mm/pgd.c @@ -0,0 +1,102 @@ +/* + * linux/arch/unicore32/mm/pgd.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/mm.h> +#include <linux/gfp.h> +#include <linux/highmem.h> + +#include <asm/pgalloc.h> +#include <asm/page.h> +#include <asm/tlbflush.h> + +#include "mm.h" + +#define FIRST_KERNEL_PGD_NR	(FIRST_USER_PGD_NR + USER_PTRS_PER_PGD) + +/* + * need to get a 4k page for level 1 + */ +pgd_t *get_pgd_slow(struct mm_struct *mm) +{ +	pgd_t *new_pgd, *init_pgd; +	pmd_t *new_pmd, *init_pmd; +	pte_t *new_pte, *init_pte; + +	new_pgd = (pgd_t *)__get_free_pages(GFP_KERNEL, 0); +	if (!new_pgd) +		goto no_pgd; + +	memset(new_pgd, 0, FIRST_KERNEL_PGD_NR * sizeof(pgd_t)); + +	/* +	 * Copy over the kernel and IO PGD entries +	 */ +	init_pgd = pgd_offset_k(0); +	memcpy(new_pgd + FIRST_KERNEL_PGD_NR, init_pgd + FIRST_KERNEL_PGD_NR, +		       (PTRS_PER_PGD - FIRST_KERNEL_PGD_NR) * sizeof(pgd_t)); + +	clean_dcache_area(new_pgd, PTRS_PER_PGD * sizeof(pgd_t)); + +	if (!vectors_high()) { +		/* +		 * On UniCore, first page must always be allocated since it +		 * contains the machine vectors. +		 */ +		new_pmd = pmd_alloc(mm, (pud_t *)new_pgd, 0); +		if (!new_pmd) +			goto no_pmd; + +		new_pte = pte_alloc_map(mm, NULL, new_pmd, 0); +		if (!new_pte) +			goto no_pte; + +		init_pmd = pmd_offset((pud_t *)init_pgd, 0); +		init_pte = pte_offset_map(init_pmd, 0); +		set_pte(new_pte, *init_pte); +		pte_unmap(init_pte); +		pte_unmap(new_pte); +	} + +	return new_pgd; + +no_pte: +	pmd_free(mm, new_pmd); +no_pmd: +	free_pages((unsigned long)new_pgd, 0); +no_pgd: +	return NULL; +} + +void free_pgd_slow(struct mm_struct *mm, pgd_t *pgd) +{ +	pmd_t *pmd; +	pgtable_t pte; + +	if (!pgd) +		return; + +	/* pgd is always present and good */ +	pmd = pmd_off(pgd, 0); +	if (pmd_none(*pmd)) +		goto free; +	if (pmd_bad(*pmd)) { +		pmd_ERROR(*pmd); +		pmd_clear(pmd); +		goto free; +	} + +	pte = pmd_pgtable(*pmd); +	pmd_clear(pmd); +	pte_free(mm, pte); +	pmd_free(mm, pmd); +free: +	free_pages((unsigned long) pgd, 0); +} diff --git a/arch/unicore32/mm/proc-macros.S b/arch/unicore32/mm/proc-macros.S new file mode 100644 index 00000000000..51560d68c89 --- /dev/null +++ b/arch/unicore32/mm/proc-macros.S @@ -0,0 +1,145 @@ +/* + * linux/arch/unicore32/mm/proc-macros.S + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * 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. + * + * We need constants.h for: + *  VMA_VM_MM + *  VMA_VM_FLAGS + *  VM_EXEC + */ +#include <generated/asm-offsets.h> +#include <asm/thread_info.h> +#include <asm/memory.h> + +/* + * the cache line sizes of the I and D cache are the same + */ +#define CACHE_LINESIZE	32 + +/* + * This is the maximum size of an area which will be invalidated + * using the single invalidate entry instructions.  Anything larger + * than this, and we go for the whole cache. + * + * This value should be chosen such that we choose the cheapest + * alternative. + */ +#ifdef CONFIG_CPU_UCV2 +#define MAX_AREA_SIZE	0x800		/* 64 cache line */ +#endif + +/* + * vma_vm_mm - get mm pointer from vma pointer (vma->vm_mm) + */ +	.macro	vma_vm_mm, rd, rn +	ldw	\rd, [\rn+], #VMA_VM_MM +	.endm + +/* + * vma_vm_flags - get vma->vm_flags + */ +	.macro	vma_vm_flags, rd, rn +	ldw	\rd, [\rn+], #VMA_VM_FLAGS +	.endm + +	.macro	tsk_mm, rd, rn +	ldw	\rd, [\rn+], #TI_TASK +	ldw	\rd, [\rd+], #TSK_ACTIVE_MM +	.endm + +/* + * act_mm - get current->active_mm + */ +	.macro	act_mm, rd +	andn	\rd, sp, #8128 +	andn	\rd, \rd, #63 +	ldw	\rd, [\rd+], #TI_TASK +	ldw	\rd, [\rd+], #TSK_ACTIVE_MM +	.endm + +/* + * mmid - get context id from mm pointer (mm->context.id) + */ +	.macro	mmid, rd, rn +	ldw	\rd, [\rn+], #MM_CONTEXT_ID +	.endm + +/* + * mask_asid - mask the ASID from the context ID + */ +	.macro	asid, rd, rn +	and	\rd, \rn, #255 +	.endm + +	.macro	crval, clear, mmuset, ucset +	.word	\clear +	.word	\mmuset +	.endm + +#ifndef CONFIG_CPU_DCACHE_LINE_DISABLE +/* + * va2pa va, pa, tbl, msk, off, err + *	This macro is used to translate virtual address to its physical address. + * + *	va: virtual address + *	pa: physical address, result is stored in this register + *	tbl, msk, off:	temp registers, will be destroyed + *	err: jump to error label if the physical address not exist + * NOTE: all regs must be different + */ +	.macro	va2pa, va, pa, tbl, msk, off, err=990f +	movc	\pa, p0.c2, #0 +	mov	\off, \va >> #22		@ off <- index of 1st page table +	adr	\tbl, 910f			@ tbl <- table of 1st page table +900:						@ ---- handle 1, 2 page table +	add	\pa, \pa, #PAGE_OFFSET		@ pa <- virt addr of page table +	ldw	\pa, [\pa+], \off << #2		@ pa <- the content of pt +	cand.a	\pa, #4				@ test exist bit +	beq	\err				@ if not exist +	and	\off, \pa, #3			@ off <- the last 2 bits +	add	\tbl, \tbl, \off << #3		@ cmove table pointer +	ldw	\msk, [\tbl+], #0		@ get the mask +	ldw	pc, [\tbl+], #4 +930:						@ ---- handle 2nd page table +	and	\pa, \pa, \msk			@ pa <- phys addr of 2nd pt +	mov	\off, \va << #10 +	cntlo	\tbl, \msk			@ use tbl as temp reg +	mov	\off, \off >> \tbl +	mov	\off, \off >> #2		@ off <- index of 2nd pt +	adr	\tbl, 920f			@ tbl <- table of 2nd pt +	b	900b +910:						@ 1st level page table +	.word	0xfffff000, 930b		@ second level page table +	.word	0xfffffc00, 930b		@ second level large page table +	.word	0x00000000, \err		@ invalid +	.word	0xffc00000, 980f		@ super page + +920:						@ 2nd level page table +	.word	0xfffff000, 980f		@ page +	.word	0xffffc000, 980f		@ middle page +	.word	0xffff0000, 980f		@ large page +	.word	0x00000000, \err		@ invalid +980: +	andn	\tbl, \va, \msk +	and	\pa, \pa, \msk +	or	\pa, \pa, \tbl +990: +	.endm +#endif + +	.macro dcacheline_flush, addr, t1, t2 +	mov	\t1, \addr << #20 +	ldw	\t2, =_stext			@ _stext must ALIGN(4096) +	add	\t2, \t2, \t1 >> #20 +	ldw	\t1, [\t2+], #0x0000 +	ldw	\t1, [\t2+], #0x1000 +	ldw	\t1, [\t2+], #0x2000 +	ldw	\t1, [\t2+], #0x3000 +	.endm diff --git a/arch/unicore32/mm/proc-syms.c b/arch/unicore32/mm/proc-syms.c new file mode 100644 index 00000000000..21c00fc85c9 --- /dev/null +++ b/arch/unicore32/mm/proc-syms.c @@ -0,0 +1,25 @@ +/* + * linux/arch/unicore32/mm/proc-syms.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/mm.h> + +#include <asm/cacheflush.h> +#include <asm/tlbflush.h> +#include <asm/page.h> + +EXPORT_SYMBOL(cpu_dcache_clean_area); +EXPORT_SYMBOL(cpu_set_pte); + +EXPORT_SYMBOL(__cpuc_coherent_kern_range); + +EXPORT_SYMBOL(__cpuc_dma_flush_range); +EXPORT_SYMBOL(__cpuc_dma_clean_range); diff --git a/arch/unicore32/mm/proc-ucv2.S b/arch/unicore32/mm/proc-ucv2.S new file mode 100644 index 00000000000..9d296092e36 --- /dev/null +++ b/arch/unicore32/mm/proc-ucv2.S @@ -0,0 +1,134 @@ +/* + * linux/arch/unicore32/mm/proc-ucv2.S + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/init.h> +#include <linux/linkage.h> +#include <asm/assembler.h> +#include <asm/hwcap.h> +#include <asm/pgtable-hwdef.h> +#include <asm/pgtable.h> + +#include "proc-macros.S" + +ENTRY(cpu_proc_fin) +	stm.w	(lr), [sp-] +	mov	ip, #PSR_R_BIT | PSR_I_BIT | PRIV_MODE +	mov.a	asr, ip +	b.l	__cpuc_flush_kern_all +	ldm.w	(pc), [sp]+ + +/* + *	cpu_reset(loc) + * + *	Perform a soft reset of the system.  Put the CPU into the + *	same state as it would be if it had been reset, and branch + *	to what would be the reset vector. + * + *	- loc   - location to jump to for soft reset + */ +	.align	5 +ENTRY(cpu_reset) +	mov	ip, #0 +	movc	p0.c5, ip, #28			@ Cache invalidate all +	nop8 + +	movc	p0.c6, ip, #6			@ TLB invalidate all +	nop8 + +	movc	ip, p0.c1, #0			@ ctrl register +	or	ip, ip, #0x2000			@ vector base address +	andn	ip, ip, #0x000f			@ ............idam +	movc	p0.c1, ip, #0			@ disable caches and mmu +	nop +	mov	pc, r0				@ jump to loc +	nop8 + +/* + *	cpu_do_idle() + * + *	Idle the processor (eg, wait for interrupt). + * + *	IRQs are already disabled. + */ +ENTRY(cpu_do_idle) +	mov	r0, #0				@ PCI address +	.rept	8 +	ldw	r1, [r0] +	.endr +	mov	pc, lr + +ENTRY(cpu_dcache_clean_area) +#ifndef CONFIG_CPU_DCACHE_LINE_DISABLE +	csub.a	r1, #MAX_AREA_SIZE +	bsg	101f +	mov	r9, #PAGE_SZ +	sub	r9, r9, #1			@ PAGE_MASK +1:	va2pa	r0, r10, r11, r12, r13		@ r10 is PA +	b	3f +2:	cand.a	r0, r9 +	beq	1b +3:	movc	p0.c5, r10, #11			@ clean D entry +	nop8 +	add	r0, r0, #CACHE_LINESIZE +	add	r10, r10, #CACHE_LINESIZE +	sub.a	r1, r1, #CACHE_LINESIZE +	bua	2b +	mov	pc, lr +#endif +101:	mov	ip, #0 +	movc	p0.c5, ip, #10			@ Dcache clean all +	nop8 + +	mov	pc, lr + +/* + *	cpu_do_switch_mm(pgd_phys) + * + *	Set the translation table base pointer to be pgd_phys + * + *	- pgd_phys - physical address of new pgd + * + *	It is assumed that: + *	- we are not using split page tables + */ +	.align	5 +ENTRY(cpu_do_switch_mm) +	movc	p0.c2, r0, #0			@ update page table ptr +	nop8 + +	movc	p0.c6, ip, #6			@ TLB invalidate all +	nop8 + +	mov	pc, lr + +/* + *	cpu_set_pte(ptep, pte) + * + *	Set a level 2 translation table entry. + * + *	- ptep  - pointer to level 2 translation table entry + *	- pte   - PTE value to store + */ +	.align	5 +ENTRY(cpu_set_pte) +	stw	r1, [r0] +#ifndef CONFIG_CPU_DCACHE_LINE_DISABLE +	sub	r2, r0, #PAGE_OFFSET +	movc	p0.c5, r2, #11				@ Dcache clean line +	nop8 +#else +	mov	ip, #0 +	movc	p0.c5, ip, #10				@ Dcache clean all +	nop8 +	@dcacheline_flush	r0, r2, ip +#endif +	mov	pc, lr + diff --git a/arch/unicore32/mm/tlb-ucv2.S b/arch/unicore32/mm/tlb-ucv2.S new file mode 100644 index 00000000000..061d455f9a1 --- /dev/null +++ b/arch/unicore32/mm/tlb-ucv2.S @@ -0,0 +1,89 @@ +/* + * linux/arch/unicore32/mm/tlb-ucv2.S + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/init.h> +#include <linux/linkage.h> +#include <asm/assembler.h> +#include <asm/page.h> +#include <asm/tlbflush.h> +#include "proc-macros.S" + +/* + *	__cpu_flush_user_tlb_range(start, end, vma) + * + *	Invalidate a range of TLB entries in the specified address space. + * + *	- start - start address (may not be aligned) + *	- end   - end address (exclusive, may not be aligned) + *	- vma   - vma_struct describing address range + */ +ENTRY(__cpu_flush_user_tlb_range) +#ifndef	CONFIG_CPU_TLB_SINGLE_ENTRY_DISABLE +	mov	r0, r0 >> #PAGE_SHIFT		@ align address +	mov	r0, r0 << #PAGE_SHIFT +	vma_vm_flags r2, r2			@ get vma->vm_flags +1: +	movc	p0.c6, r0, #3 +	nop8 + +	cand.a	r2, #VM_EXEC			@ Executable area ? +	beq	2f + +	movc	p0.c6, r0, #5 +	nop8 +2: +	add	r0, r0, #PAGE_SZ +	csub.a	r0, r1 +	beb	1b +#else +	movc	p0.c6, r0, #2 +	nop8 + +	cand.a	r2, #VM_EXEC			@ Executable area ? +	beq	2f + +	movc	p0.c6, r0, #4 +	nop8 +2: +#endif +	mov	pc, lr + +/* + *	__cpu_flush_kern_tlb_range(start,end) + * + *	Invalidate a range of kernel TLB entries + * + *	- start - start address (may not be aligned) + *	- end   - end address (exclusive, may not be aligned) + */ +ENTRY(__cpu_flush_kern_tlb_range) +#ifndef	CONFIG_CPU_TLB_SINGLE_ENTRY_DISABLE +	mov	r0, r0 >> #PAGE_SHIFT		@ align address +	mov	r0, r0 << #PAGE_SHIFT +1: +	movc	p0.c6, r0, #3 +	nop8 + +	movc	p0.c6, r0, #5 +	nop8 + +	add	r0, r0, #PAGE_SZ +	csub.a	r0, r1 +	beb	1b +#else +	movc	p0.c6, r0, #2 +	nop8 + +	movc	p0.c6, r0, #4 +	nop8 +#endif +	mov	pc, lr +  | 
