diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-03-17 10:11:25 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-03-17 10:11:25 -0700 |
commit | 7b7adc4a016a1decb806eb71ecab98721fa7f146 (patch) | |
tree | 0a6f9a6e5659faa94604fbc575382a18f143c657 /arch | |
parent | 31598e8713ef501c8f6aad2e2ec8a9457e8877c1 (diff) | |
parent | 289d6b0e287e0acd85f3e6b7ea6c2cb5c234909a (diff) |
Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/epip/linux-2.6-unicore32
* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/epip/linux-2.6-unicore32: (40 commits)
unicore32: rewrite arch-specific tlb.h to use asm-generic version
unicore32: modify io_p2v and io_v2p macros, and adjust PKUNITY_mmio_BASEs
unicore32: replace unicore32-specific iomap functions with generic lib implementation
unicore32 machine related: add frame buffer driver for pkunity-v3 soc
unicore32 machine related files: add i2c bus drivers for pkunity-v3 soc
unicore32 io: redefine __REG(x) and re-use readl/writel funcs
unicore32 i8042 upgrade and bugfix: adjust resource request region type
unicore32 upgrade to v2.6.38-rc5: add one more paramter for pte_alloc_map call
unicore32 i8042: adjust io funcs of i8042-unicore32io.h
unicore32: rename PKUNITY_IOSPACE_BASE to PKUNITY_MMIO_BASE
unicore32: modify function names and parameters for irq_chips
unicore32: remove unused lines in arch/unicore32/include/asm/irq.h
unicore32 time.c: change calculate method for clock_event_device
unicore32: ADD MAINTAINER for unicore32 architecture
unicore32 machine related files: ps2 driver
unicore32 machine related files: pci bus handling
unicore32 machine related files: hardware registers
unicore32 machine related files: core files
unicore32 additional architecture files: boot process
unicore32 additional architecture files: low-level lib: misc
...
Acked-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'arch')
148 files changed, 18051 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..4a36db45fb3 --- /dev/null +++ b/arch/unicore32/Kconfig @@ -0,0 +1,275 @@ +config UNICORE32 + def_bool y + select HAVE_MEMBLOCK + select HAVE_GENERIC_DMA_COHERENT + select HAVE_GENERIC_HARDIRQS + select HAVE_DMA_ATTRS + select HAVE_KERNEL_GZIP + select HAVE_KERNEL_BZIP2 + select HAVE_KERNEL_LZO + select HAVE_KERNEL_LZMA + select GENERIC_FIND_FIRST_BIT + select GENERIC_IRQ_PROBE + select GENERIC_HARDIRQS_NO_DEPRECATED + select ARCH_WANT_FRAME_POINTERS + 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 HAVE_PWM + bool + +config GENERIC_GPIO + def_bool y + +config GENERIC_CLOCKEVENTS + bool + +config GENERIC_CSUM + def_bool y + +config GENERIC_IOMAP + def_bool y + +config NO_IOPORT + 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 ARCH_HAS_CPUFREQ + bool + +config GENERIC_HWEIGHT + def_bool y + +config GENERIC_CALIBRATE_DELAY + def_bool y + +config ARCH_MAY_HAVE_PC_FDC + bool + +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 + select ARCH_HAS_CPUFREQ + +# 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 HAVE_PWM + +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/time/Kconfig" + +source "kernel/Kconfig.preempt" + +source "kernel/Kconfig.hz" + +source "mm/Kconfig" + +config LEDS + def_bool y + depends on GENERIC_GPIO + +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" + +if ARCH_HAS_CPUFREQ +source "drivers/cpufreq/Kconfig" +endif + +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 GENERIC_GPIO + select GPIO_SYSFS if EXPERIMENTAL + default y + +config PUV3_PWM + tristate + default BACKLIGHT_PWM + help + Enable support for NB0916 PWM controllers + +config PUV3_RTC + tristate "PKUnity v3 RTC Support" + depends on !ARCH_FPGA + +if PUV3_NB0916 + +menu "PKUnity NetBook-0916 Features" + +config I2C_BATTERY_BQ27200 + tristate "I2C Battery BQ27200 Support" + select PUV3_I2C + select POWER_SUPPLY + select BATTERY_BQ27x00 + +config I2C_EEPROM_AT24 + tristate "I2C EEPROMs AT24 support" + select PUV3_I2C + select MISC_DEVICES + 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..3140151ede4 --- /dev/null +++ b/arch/unicore32/Kconfig.debug @@ -0,0 +1,68 @@ +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. + +config DEBUG_STACK_USAGE + bool "Enable stack utilization instrumentation" + depends on DEBUG_KERNEL + help + Enables the display of the minimum amount of free stack which each + task has ever had available in the sysrq-T output. + +# 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. + +config DEBUG_OCD_BREAKPOINT + bool "Breakpoint support via On-Chip-Debugger" + depends on DEBUG_OCD + +config DEBUG_UART + int "Kernel low-level debugging messages via serial port" + depends on DEBUG_LL + range 0 1 + default "0" + help + Choice for UART for kernel low-level using PKUnity UARTS, + should be between zero and one. The port must have been + initialised by the boot-loader before use. + +endmenu diff --git a/arch/unicore32/Makefile b/arch/unicore32/Makefile new file mode 100644 index 00000000000..e08d6d370a8 --- /dev/null +++ b/arch/unicore32/Makefile @@ -0,0 +1,95 @@ +# +# 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 +head-y += arch/unicore32/kernel/init_task.o + +core-y += arch/unicore32/kernel/ +core-y += arch/unicore32/mm/ + +libs-y += arch/unicore32/lib/ + +ASM_GENERATED_DIR := $(srctree)/arch/unicore32/include/generated +LINUXINCLUDE += -I$(ASM_GENERATED_DIR) + +ASM_GENERIC_HEADERS := atomic.h auxvec.h +ASM_GENERIC_HEADERS += bitsperlong.h bug.h bugs.h +ASM_GENERIC_HEADERS += cputime.h current.h +ASM_GENERIC_HEADERS += device.h div64.h +ASM_GENERIC_HEADERS += emergency-restart.h errno.h +ASM_GENERIC_HEADERS += fb.h fcntl.h ftrace.h +ASM_GENERIC_HEADERS += hardirq.h hw_irq.h +ASM_GENERIC_HEADERS += ioctl.h ioctls.h ipcbuf.h irq_regs.h +ASM_GENERIC_HEADERS += kdebug.h kmap_types.h +ASM_GENERIC_HEADERS += local.h +ASM_GENERIC_HEADERS += mman.h module.h msgbuf.h +ASM_GENERIC_HEADERS += param.h parport.h percpu.h poll.h posix_types.h +ASM_GENERIC_HEADERS += resource.h +ASM_GENERIC_HEADERS += scatterlist.h sections.h segment.h sembuf.h serial.h +ASM_GENERIC_HEADERS += setup.h shmbuf.h shmparam.h +ASM_GENERIC_HEADERS += siginfo.h signal.h sizes.h +ASM_GENERIC_HEADERS += socket.h sockios.h stat.h statfs.h swab.h syscalls.h +ASM_GENERIC_HEADERS += termbits.h termios.h topology.h types.h +ASM_GENERIC_HEADERS += ucontext.h unaligned.h user.h +ASM_GENERIC_HEADERS += vga.h +ASM_GENERIC_HEADERS += xor.h + +archprepare: +ifneq ($(ASM_GENERATED_DIR), $(wildcard $(ASM_GENERATED_DIR))) + $(Q)mkdir -p $(ASM_GENERATED_DIR)/asm + $(Q)$(foreach a, $(ASM_GENERIC_HEADERS), \ + echo '#include <asm-generic/$a>' \ + > $(ASM_GENERATED_DIR)/asm/$a; ) +endif + +boot := arch/unicore32/boot + +# Default target when executing plain make +KBUILD_IMAGE := zImage + +all: $(KBUILD_IMAGE) + +zImage Image uImage: vmlinux + $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ + +MRPROPER_DIRS += $(ASM_GENERATED_DIR) + +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..79e5f88845d --- /dev/null +++ b/arch/unicore32/boot/Makefile @@ -0,0 +1,47 @@ +# +# 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 +# + +MKIMAGE := $(srctree)/scripts/mkuboot.sh + +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' + +quiet_cmd_uimage = UIMAGE $@ + cmd_uimage = $(CONFIG_SHELL) $(MKIMAGE) -A unicore -O linux -T kernel \ + -C none -a $(LOADADDR) -e $(STARTADDR) \ + -n 'Linux-$(KERNELRELEASE)' -d $< $@ + +$(obj)/uImage: LOADADDR=0x0 + +$(obj)/uImage: STARTADDR=$(LOADADDR) + +$(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..95373428cb3 --- /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 +# + +EXTRA_CFLAGS := -fpic -fno-builtin +EXTRA_AFLAGS := -Wa,-march=all + +OBJS := misc.o + +# font.c and font.o +CFLAGS_font.o := -Dstatic= +$(obj)/font.c: $(srctree)/drivers/video/console/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 := 0 +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/debug_defconfig b/arch/unicore32/configs/debug_defconfig new file mode 100644 index 00000000000..b5fbde9f1cb --- /dev/null +++ b/arch/unicore32/configs/debug_defconfig @@ -0,0 +1,215 @@ +### General setup +CONFIG_EXPERIMENTAL=y +CONFIG_LOCALVERSION="-debug" +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_RTC=y +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_DEVICEFS=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=m +CONFIG_RTC_CLASS=m + +### 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..b200fdaca44 --- /dev/null +++ b/arch/unicore32/include/asm/Kbuild @@ -0,0 +1,2 @@ +include include/asm-generic/Kbuild.asm + 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/bitops.h b/arch/unicore32/include/asm/bitops.h new file mode 100644 index 00000000000..1628a632899 --- /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 find_next_bit __uc32_find_next_bit +#define find_next_zero_bit __uc32_find_next_zero_bit + +#define find_first_bit __uc32_find_first_bit +#define find_first_zero_bit __uc32_find_first_zero_bit + +#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> + +#endif /* __UNICORE_BITOPS_H__ */ diff --git a/arch/unicore32/include/asm/byteorder.h b/arch/unicore32/include/asm/byteorder.h new file mode 100644 index 00000000000..ebe1b3fef3e --- /dev/null +++ b/arch/unicore32/include/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/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/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..9258e592f41 --- /dev/null +++ b/arch/unicore32/include/asm/dma-mapping.h @@ -0,0 +1,124 @@ +/* + * 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; +} + +static inline void *dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t flag) +{ + struct dma_map_ops *dma_ops = get_dma_ops(dev); + + return dma_ops->alloc_coherent(dev, size, dma_handle, flag); +} + +static inline void dma_free_coherent(struct device *dev, size_t size, + void *cpu_addr, dma_addr_t dma_handle) +{ + struct dma_map_ops *dma_ops = get_dma_ops(dev); + + dma_ops->free_coherent(dev, size, cpu_addr, dma_handle); +} + +#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/futex.h b/arch/unicore32/include/asm/futex.h new file mode 100644 index 00000000000..07dea617055 --- /dev/null +++ b/arch/unicore32/include/asm/futex.h @@ -0,0 +1,143 @@ +/* + * linux/arch/unicore32/include/asm/futex.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_FUTEX_H__ +#define __UNICORE_FUTEX_H__ + +#ifdef __KERNEL__ + +#include <linux/futex.h> +#include <linux/preempt.h> +#include <linux/uaccess.h> +#include <linux/errno.h> + +#define __futex_atomic_op(insn, ret, oldval, uaddr, oparg) \ + __asm__ __volatile__( \ + "1: ldw.u %1, [%2]\n" \ + " " insn "\n" \ + "2: stw.u %0, [%2]\n" \ + " mov %0, #0\n" \ + "3:\n" \ + " .pushsection __ex_table,\"a\"\n" \ + " .align 3\n" \ + " .long 1b, 4f, 2b, 4f\n" \ + " .popsection\n" \ + " .pushsection .fixup,\"ax\"\n" \ + "4: mov %0, %4\n" \ + " b 3b\n" \ + " .popsection" \ + : "=&r" (ret), "=&r" (oldval) \ + : "r" (uaddr), "r" (oparg), "Ir" (-EFAULT) \ + : "cc", "memory") + +static inline int +futex_atomic_op_inuser(int encoded_op, int __user *uaddr) +{ + int op = (encoded_op >> 28) & 7; + int cmp = (encoded_op >> 24) & 15; + int oparg = (encoded_op << 8) >> 20; + int cmparg = (encoded_op << 20) >> 20; + int oldval = 0, ret; + + if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) + oparg = 1 << oparg; + + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) + return -EFAULT; + + pagefault_disable(); /* implies preempt_disable() */ + + switch (op) { + case FUTEX_OP_SET: + __futex_atomic_op("mov %0, %3", ret, oldval, uaddr, oparg); + break; + case FUTEX_OP_ADD: + __futex_atomic_op("add %0, %1, %3", ret, oldval, uaddr, oparg); + break; + case FUTEX_OP_OR: + __futex_atomic_op("or %0, %1, %3", ret, oldval, uaddr, oparg); + break; + case FUTEX_OP_ANDN: + __futex_atomic_op("and %0, %1, %3", + ret, oldval, uaddr, ~oparg); + break; + case FUTEX_OP_XOR: + __futex_atomic_op("xor %0, %1, %3", ret, oldval, uaddr, oparg); + break; + default: + ret = -ENOSYS; + } + + pagefault_enable(); /* subsumes preempt_enable() */ + + if (!ret) { + switch (cmp) { + case FUTEX_OP_CMP_EQ: + ret = (oldval == cmparg); + break; + case FUTEX_OP_CMP_NE: + ret = (oldval != cmparg); + break; + case FUTEX_OP_CMP_LT: + ret = (oldval < cmparg); + break; + case FUTEX_OP_CMP_GE: + ret = (oldval >= cmparg); + break; + case FUTEX_OP_CMP_LE: + ret = (oldval <= cmparg); + break; + case FUTEX_OP_CMP_GT: + ret = (oldval > cmparg); + break; + default: + ret = -ENOSYS; + } + } + return ret; +} + +static inline int +futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) +{ + int val; + + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) + return -EFAULT; + + pagefault_disable(); /* implies preempt_disable() */ + + __asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n" + "1: ldw.u %0, [%3]\n" + " cmpxor.a %0, %1\n" + " bne 3f\n" + "2: stw.u %2, [%3]\n" + "3:\n" + " .pushsection __ex_table,\"a\"\n" + " .align 3\n" + " .long 1b, 4f, 2b, 4f\n" + " .popsection\n" + " .pushsection .fixup,\"ax\"\n" + "4: mov %0, %4\n" + " b 3b\n" + " .popsection" + : "=&r" (val) + : "r" (oldval), "r" (newval), "r" (uaddr), "Ir" (-EFAULT) + : "cc", "memory"); + + pagefault_enable(); /* subsumes preempt_enable() */ + + return val; +} + +#endif /* __KERNEL__ */ +#endif /* __UNICORE_FUTEX_H__ */ 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/io.h b/arch/unicore32/include/asm/io.h new file mode 100644 index 00000000000..4bd87f3d13d --- /dev/null +++ b/arch/unicore32/include/asm/io.h @@ -0,0 +1,55 @@ +/* + * 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> +#include <asm/system.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 iounmap(cookie) __uc32_iounmap(cookie) + +/* + * Convert a physical pointer to a virtual kernel pointer for /dev/mem + * access + */ +#undef xlate_dev_mem_ptr +#define xlate_dev_mem_ptr(p) __va(p) + +#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) + +#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..5eddb997def --- /dev/null +++ b/arch/unicore32/include/asm/memory.h @@ -0,0 +1,123 @@ +/* + * 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. + * + * Note: when converting an unknown physical address to a + * struct page, the resulting pointer must be validated + * using VALID_PAGE(). It must return an invalid struct page + * for any physical address not corresponding to a system + * RAM 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..fb5e4c658f7 --- /dev/null +++ b/arch/unicore32/include/asm/mmu_context.h @@ -0,0 +1,87 @@ +/* + * 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/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); \ + mm->mmap_cache = NULL; \ + 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..c5b28b45953 --- /dev/null +++ b/arch/unicore32/include/asm/pci.h @@ -0,0 +1,46 @@ +/* + * 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.h> +#include <mach/hardware.h> /* for PCIBIOS_MIN_* */ + +static inline void pcibios_set_master(struct pci_dev *dev) +{ + /* No special bus mastering setup handling */ +} + +static inline void pcibios_penalize_isa_irq(int irq, int active) +{ + /* We don't do dynamic PCI IRQ allocation */ +} + +#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..0213e373a89 --- /dev/null +++ b/arch/unicore32/include/asm/pgalloc.h @@ -0,0 +1,110 @@ +/* + * 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) { + if (!PageHighMem(pte)) { + void *page = page_address(pte); + clean_dcache_area(page, PTRS_PER_PTE * sizeof(pte_t)); + } + pgtable_page_ctor(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..68b2f297ac9 --- /dev/null +++ b/arch/unicore32/include/asm/pgtable.h @@ -0,0 +1,317 @@ +/* + * 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> + +/* + * remap a physical page `pfn' of size `size' with page protection `prot' + * into virtual address `from' + */ +#define io_remap_pfn_range(vma, from, pfn, size, prot) \ + remap_pfn_range(vma, from, pfn, size, prot) + +#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..e11cb078657 --- /dev/null +++ b/arch/unicore32/include/asm/processor.h @@ -0,0 +1,92 @@ +/* + * 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; \ + set_fs(USER_DS); \ + 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 *); + +/* Prepare to copy thread state - unlazy all lazy status */ +#define prepare_to_copy(tsk) do { } while (0) + +unsigned long get_wchan(struct task_struct *p); + +#define cpu_relax() barrier() + +/* + * Create a new kernel thread + */ +extern int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags); + +#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..b9caf9b0997 --- /dev/null +++ b/arch/unicore32/include/asm/ptrace.h @@ -0,0 +1,133 @@ +/* + * 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__ + +#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] + +#ifdef __KERNEL__ + +#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) + +#endif /* __KERNEL__ */ + +#endif /* __ASSEMBLY__ */ + +#endif + diff --git a/arch/unicore32/include/asm/sigcontext.h b/arch/unicore32/include/asm/sigcontext.h new file mode 100644 index 00000000000..6a2d7671c05 --- /dev/null +++ b/arch/unicore32/include/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/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..88a9c0f32b2 --- /dev/null +++ b/arch/unicore32/include/asm/suspend.h @@ -0,0 +1,30 @@ +/* + * 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__ +static inline int arch_prepare_suspend(void) { return 0; } + +#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/system.h b/arch/unicore32/include/asm/system.h new file mode 100644 index 00000000000..246b71c17fd --- /dev/null +++ b/arch/unicore32/include/asm/system.h @@ -0,0 +1,161 @@ +/* + * linux/arch/unicore32/include/asm/system.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_SYSTEM_H__ +#define __UNICORE_SYSTEM_H__ + +#ifdef __KERNEL__ + +/* + * CR1 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__ + +#include <linux/linkage.h> +#include <linux/irqflags.h> + +struct thread_info; +struct task_struct; + +struct pt_regs; + +void die(const char *msg, struct pt_regs *regs, int err); + +struct siginfo; +void uc32_notify_die(const char *str, struct pt_regs *regs, + struct siginfo *info, unsigned long err, unsigned long trap); + +void hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int, + struct pt_regs *), + int sig, int code, const char *name); + +#define xchg(ptr, x) \ + ((__typeof__(*(ptr)))__xchg((unsigned long)(x), (ptr), sizeof(*(ptr)))) + +extern asmlinkage void __backtrace(void); +extern asmlinkage void c_backtrace(unsigned long fp, int pmode); + +struct mm_struct; +extern void show_pte(struct mm_struct *mm, unsigned long addr); +extern void __show_regs(struct pt_regs *); + +extern int cpu_architecture(void); +extern void cpu_init(void); + +#define vectors_high() (cr_alignment & CR_V) + +#define isb() __asm__ __volatile__ ("" : : : "memory") +#define dsb() __asm__ __volatile__ ("" : : : "memory") +#define dmb() __asm__ __volatile__ ("" : : : "memory") + +#define mb() barrier() +#define rmb() barrier() +#define wmb() barrier() +#define smp_mb() barrier() +#define smp_rmb() barrier() +#define smp_wmb() barrier() +#define read_barrier_depends() do { } while (0) +#define smp_read_barrier_depends() do { } while (0) + +#define set_mb(var, value) do { var = value; smp_mb(); } while (0) +#define nop() __asm__ __volatile__("mov\tr0,r0\t@ nop\n\t"); + +extern unsigned long cr_no_alignment; /* defined in entry-unicore.S */ +extern unsigned long cr_alignment; /* defined in entry-unicore.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 @set CR" + : : "r" (val) : "cc"); + isb(); +} + +extern void adjust_cr(unsigned long mask, unsigned long set); + +/* + * 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 *); +extern void panic(const char *fmt, ...); + +#define switch_to(prev, next, last) \ +do { \ + last = __switch_to(prev, \ + task_thread_info(prev), task_thread_info(next)); \ +} while (0) + +static inline unsigned long +__xchg(unsigned long x, volatile void *ptr, int size) +{ + unsigned long ret; + + switch (size) { + case 1: + asm volatile("@ __xchg1\n" + " swapb %0, %1, [%2]" + : "=&r" (ret) + : "r" (x), "r" (ptr) + : "memory", "cc"); + break; + case 4: + asm volatile("@ __xchg4\n" + " swapw %0, %1, [%2]" + : "=&r" (ret) + : "r" (x), "r" (ptr) + : "memory", "cc"); + break; + default: + panic("xchg: bad data size: ptr 0x%p, size %d\n", + ptr, size); + } + + return ret; +} + +#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 /* __ASSEMBLY__ */ + +#define arch_align_stack(x) (x) + +#endif /* __KERNEL__ */ + +#endif diff --git a/arch/unicore32/include/asm/thread_info.h b/arch/unicore32/include/asm/thread_info.h new file mode 100644 index 00000000000..c270e9e0486 --- /dev/null +++ b/arch/unicore32/include/asm/thread_info.h @@ -0,0 +1,154 @@ +/* + * 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 + +/* + * We use bit 30 of the preempt_count to indicate that kernel + * preemption is occurring. See <asm/hardirq.h>. + */ +#define PREEMPT_ACTIVE 0x40000000 + +/* + * 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_FREEZE 19 +#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) +#define _TIF_FREEZE (1 << TIF_FREEZE) +#define _TIF_RESTORE_SIGMASK (1 << TIF_RESTORE_SIGMASK) + +/* + * Change these and you break ASM code in entry-common.S + */ +#define _TIF_WORK_MASK 0x000000ff + +#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..2acda503a6d --- /dev/null +++ b/arch/unicore32/include/asm/uaccess.h @@ -0,0 +1,47 @@ +/* + * 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> +#include <asm/system.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/asm/unistd.h b/arch/unicore32/include/asm/unistd.h new file mode 100644 index 00000000000..9b242801996 --- /dev/null +++ b/arch/unicore32/include/asm/unistd.h @@ -0,0 +1,18 @@ +/* + * 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. + */ +#if !defined(__UNICORE_UNISTD_H__) || defined(__SYSCALL) +#define __UNICORE_UNISTD_H__ + +/* Use the standard ABI for syscalls. */ +#include <asm-generic/unistd.h> + +#endif /* __UNICORE_UNISTD_H__ */ diff --git a/arch/unicore32/include/mach/PKUnity.h b/arch/unicore32/include/mach/PKUnity.h new file mode 100644 index 00000000000..a18bdc3810e --- /dev/null +++ b/arch/unicore32/include/mach/PKUnity.h @@ -0,0 +1,108 @@ +/* + * 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 "bitfield.h" + +/* + * Memory Definitions + */ +#define PKUNITY_SDRAM_BASE 0x00000000 /* 0x00000000 - 0x7FFFFFFF 2GB */ +#define PKUNITY_MMIO_BASE 0x80000000 /* 0x80000000 - 0xFFFFFFFF 2GB */ + +/* + * PKUNITY Memory Map Addresses: 0x0D000000 - 0x0EFFFFFF (32MB) + * 0x0D000000 - 0x0DFFFFFF 16MB: for UVC + * 0x0E000000 - 0x0EFFFFFF 16MB: for UNIGFX + */ +#define PKUNITY_UVC_MMAP_BASE 0x0D000000 +#define PKUNITY_UVC_MMAP_SIZE 0x01000000 /* 16MB */ +#define PKUNITY_UNIGFX_MMAP_BASE 0x0E000000 +#define PKUNITY_UNIGFX_MMAP_SIZE 0x01000000 /* 16MB */ + +/* + * 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 "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 "regs-dmac.h" +#define PKUNITY_UMAL_BASE (PKUNITY_AHB_BASE + 0x300000) /* AHB-5 */ +#include "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 "regs-unigfx.h" +#define PKUNITY_NAND_BASE (PKUNITY_AHB_BASE + 0x900000) /* AHB-12 */ +#include "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 "regs-uart.h" +#define PKUNITY_I2C_BASE (PKUNITY_APB_BASE + 0x200000) /* APB-2 */ +#include "regs-i2c.h" +#define PKUNITY_SPI_BASE (PKUNITY_APB_BASE + 0x300000) /* APB-3 */ +#include "regs-spi.h" +#define PKUNITY_AC97_BASE (PKUNITY_APB_BASE + 0x400000) /* APB-4 */ +#include "regs-ac97.h" +#define PKUNITY_GPIO_BASE (PKUNITY_APB_BASE + 0x500000) /* APB-5 */ +#include "regs-gpio.h" +#define PKUNITY_INTC_BASE (PKUNITY_APB_BASE + 0x600000) /* APB-6 */ +#include "regs-intc.h" +#define PKUNITY_RTC_BASE (PKUNITY_APB_BASE + 0x700000) /* APB-7 */ +#include "regs-rtc.h" +#define PKUNITY_OST_BASE (PKUNITY_APB_BASE + 0x800000) /* APB-8 */ +#include "regs-ost.h" +#define PKUNITY_RESETC_BASE (PKUNITY_APB_BASE + 0x900000) /* APB-9 */ +#include "regs-resetc.h" +#define PKUNITY_PM_BASE (PKUNITY_APB_BASE + 0xA00000) /* APB-10 */ +#include "regs-pm.h" +#define PKUNITY_PS2_BASE (PKUNITY_APB_BASE + 0xB00000) /* APB-11 */ +#include "regs-ps2.h" +#define PKUNITY_SDC_BASE (PKUNITY_APB_BASE + 0xC00000) /* APB-12 */ +#include "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..930bea6e129 --- /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 "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..0bf21c94471 --- /dev/null +++ b/arch/unicore32/include/mach/memory.h @@ -0,0 +1,58 @@ +/* + * 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)) +#define KUSER_UNIGFX_BASE (PAGE_OFFSET + PKUNITY_UNIGFX_MMAP_BASE) +/* 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..7b91fe698ee --- /dev/null +++ b/arch/unicore32/include/mach/regs-ost.h @@ -0,0 +1,92 @@ +/* + * 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 Pulse Width Control Reg OST_PWMPWCR + */ +#define OST_PWMPWCR (PKUNITY_OST_BASE + 0x0080) +/* + * PWM Duty Cycle Control Reg OST_PWMDCCR + */ +#define OST_PWMDCCR (PKUNITY_OST_BASE + 0x0084) +/* + * PWM Period Control Reg OST_PWMPCR + */ +#define OST_PWMPCR (PKUNITY_OST_BASE + 0x0088) + +/* + * 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..885bb62fee7 --- /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 Managment 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 MODUEL 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..142d3e7958a --- /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 "hardware.h" +#include "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/kernel/Makefile b/arch/unicore32/kernel/Makefile new file mode 100644 index 00000000000..ec23a2fb2f5 --- /dev/null +++ b/arch/unicore32/kernel/Makefile @@ -0,0 +1,33 @@ +# +# 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_CPU_FREQ) += cpu-ucv2.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_RTC) += rtc.o +obj-$(CONFIG_PUV3_PWM) += pwm.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) init_task.o 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..18d4563e6fa --- /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 = 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. */ + PM_PLLSYSCFG = pll_rate; + + PM_PMCR = PM_PMCR_CFBSYS; + while ((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/cpu-ucv2.c b/arch/unicore32/kernel/cpu-ucv2.c new file mode 100644 index 00000000000..4a99f62584c --- /dev/null +++ b/arch/unicore32/kernel/cpu-ucv2.c @@ -0,0 +1,93 @@ +/* + * linux/arch/unicore32/kernel/cpu-ucv2.c: clock scaling for the UniCore-II + * + * 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/kernel.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/clk.h> +#include <linux/cpufreq.h> + +#include <mach/hardware.h> + +static struct cpufreq_driver ucv2_driver; + +/* make sure that only the "userspace" governor is run + * -- anything else wouldn't make sense on this platform, anyway. + */ +int ucv2_verify_speed(struct cpufreq_policy *policy) +{ + if (policy->cpu) + return -EINVAL; + + cpufreq_verify_within_limits(policy, + policy->cpuinfo.min_freq, policy->cpuinfo.max_freq); + + return 0; +} + +static unsigned int ucv2_getspeed(unsigned int cpu) +{ + struct clk *mclk = clk_get(NULL, "MAIN_CLK"); + + if (cpu) + return 0; + return clk_get_rate(mclk)/1000; +} + +static int ucv2_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + unsigned int cur = ucv2_getspeed(0); + struct cpufreq_freqs freqs; + struct clk *mclk = clk_get(NULL, "MAIN_CLK"); + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + if (!clk_set_rate(mclk, target_freq * 1000)) { + freqs.old = cur; + freqs.new = target_freq; + freqs.cpu = 0; + } + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + return 0; +} + +static int __init ucv2_cpu_init(struct cpufreq_policy *policy) +{ + if (policy->cpu != 0) + return -EINVAL; + policy->cur = ucv2_getspeed(0); + policy->min = policy->cpuinfo.min_freq = 250000; + policy->max = policy->cpuinfo.max_freq = 1000000; + policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; + return 0; +} + +static struct cpufreq_driver ucv2_driver = { + .flags = CPUFREQ_STICKY, + .verify = ucv2_verify_speed, + .target = ucv2_target, + .get = ucv2_getspeed, + .init = ucv2_cpu_init, + .name = "UniCore-II", +}; + +static int __init ucv2_cpufreq_init(void) +{ + return cpufreq_register_driver(&ucv2_driver); +} + +arch_initcall(ucv2_cpufreq_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..ae441bc3122 --- /dev/null +++ b/arch/unicore32/kernel/dma.c @@ -0,0 +1,183 @@ +/* + * 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/system.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..3922255f1fa --- /dev/null +++ b/arch/unicore32/kernel/early_printk.c @@ -0,0 +1,59 @@ +/* + * 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, +}; + +/* Direct interface for emergencies */ +static struct console *early_console = &early_ocd_console; + +static int __initdata keep_early; + +static int __init setup_early_printk(char *buf) +{ + if (!buf) + return 0; + + if (strstr(buf, "keep")) + keep_early = 1; + + if (!strncmp(buf, "ocd", 3)) + early_console = &early_ocd_console; + + if (keep_early) + 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..00a259f9819 --- /dev/null +++ b/arch/unicore32/kernel/entry.S @@ -0,0 +1,824 @@ +/* + * 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 + cand.a r1, #_TIF_SIGPENDING|_TIF_NOTIFY_RESUME + beq no_work_pending + 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 + get_thread_info tsk + ldw r1, [tsk+], #TI_FLAGS @ check for syscall tracing + mov why, #1 + cand.a r1, #_TIF_SYSCALL_TRACE @ are we tracing syscalls? + beq ret_slow_syscall + mov r1, sp + mov r0, #1 @ trace exit [IP = 1] + b.l syscall_trace + b ret_slow_syscall +ENDPROC(ret_from_fork) + +/*============================================================================= + * 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_execve) + add r3, sp, #S_OFF + b __sys_execve +ENDPROC(sys_execve) + +ENTRY(sys_clone) + add ip, sp, #S_OFF + stw ip, [sp+], #4 + b __sys_clone +ENDPROC(sys_clone) + +ENTRY(sys_rt_sigreturn) + add r0, sp, #S_OFF + mov why, #0 @ prevent syscall restart handling + b __sys_rt_sigreturn +ENDPROC(sys_rt_sigreturn) + +ENTRY(sys_sigaltstack) + ldw r2, [sp+], #S_OFF + S_SP + b do_sigaltstack +ENDPROC(sys_sigaltstack) + + __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..92255f3ab6a --- /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/system.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 stucture 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..7d0f0b7983a --- /dev/null +++ b/arch/unicore32/kernel/hibernate.c @@ -0,0 +1,160 @@ +/* + * 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/system.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/init_task.c b/arch/unicore32/kernel/init_task.c new file mode 100644 index 00000000000..a35a1e50e4f --- /dev/null +++ b/arch/unicore32/kernel/init_task.c @@ -0,0 +1,44 @@ +/* + * linux/arch/unicore32/kernel/init_task.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/module.h> +#include <linux/fs.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/init_task.h> +#include <linux/mqueue.h> +#include <linux/uaccess.h> + +#include <asm/pgtable.h> + +static struct signal_struct init_signals = INIT_SIGNALS(init_signals); +static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); +/* + * Initial thread structure. + * + * We need to make sure that this is 8192-byte aligned due to the + * way process stacks are handled. This is done by making sure + * the linker maps this in the .text segment right after head.S, + * and making head.S ensure the proper alignment. + * + * The things we do for performance.. + */ +union thread_union init_thread_union __init_task_data = { + INIT_THREAD_INFO(init_task) }; + +/* + * Initial task structure. + * + * All other task structs will be allocated on slabs in fork.c + */ +struct task_struct init_task = INIT_TASK(init_task); +EXPORT_SYMBOL(init_task); diff --git a/arch/unicore32/kernel/irq.c b/arch/unicore32/kernel/irq.c new file mode 100644 index 00000000000..b23624cf306 --- /dev/null +++ b/arch/unicore32/kernel/irq.c @@ -0,0 +1,426 @@ +/* + * 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/sysdev.h> +#include <linux/gpio.h> + +#include <asm/system.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(struct sys_device *dev, pm_message_t state) +{ + 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 int puv3_irq_resume(struct sys_device *dev) +{ + 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); + } + return 0; +} + +static struct sysdev_class puv3_irq_sysclass = { + .name = "pkunity-irq", + .suspend = puv3_irq_suspend, + .resume = puv3_irq_resume, +}; + +static struct sys_device puv3_irq_device = { + .id = 0, + .cls = &puv3_irq_sysclass, +}; + +static int __init puv3_irq_init_devicefs(void) +{ + sysdev_class_register(&puv3_irq_sysclass); + return sysdev_register(&puv3_irq_device); +} + +device_initcall(puv3_irq_init_devicefs); + +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++) { + set_irq_chip(irq, &puv3_low_gpio_chip); + set_irq_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++) { + set_irq_chip(irq, &puv3_normal_chip); + set_irq_handler(irq, handle_level_irq); + irq_modify_status(irq, + IRQ_NOREQUEST | IRQ_NOAUTOEN, + IRQ_NOPROBE); + } + + for (irq = IRQ_GPIO0; irq <= IRQ_GPIO27; irq++) { + set_irq_chip(irq, &puv3_high_gpio_chip); + set_irq_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 + */ + set_irq_chip(IRQ_GPIOHIGH, &puv3_normal_chip); + set_irq_chained_handler(IRQ_GPIOHIGH, puv3_gpio_handler); + +#ifdef CONFIG_PUV3_GPIO + puv3_init_gpio(); +#endif +} + +int show_interrupts(struct seq_file *p, void *v) +{ + int i = *(loff_t *) v, cpu; + struct irq_desc *desc; + struct irqaction *action; + unsigned long flags; + + if (i == 0) { + char cpuname[12]; + + seq_printf(p, " "); + for_each_present_cpu(cpu) { + sprintf(cpuname, "CPU%d", cpu); + seq_printf(p, " %10s", cpuname); + } + seq_putc(p, '\n'); + } + + if (i < nr_irqs) { + desc = irq_to_desc(i); + raw_spin_lock_irqsave(&desc->lock, flags); + action = desc->action; + if (!action) + goto unlock; + + seq_printf(p, "%3d: ", i); + for_each_present_cpu(cpu) + seq_printf(p, "%10u ", kstat_irqs_cpu(i, cpu)); + seq_printf(p, " %10s", desc->irq_data.chip->name ? : "-"); + seq_printf(p, " %s", action->name); + for (action = action->next; action; action = action->next) + seq_printf(p, ", %s", action->name); + + seq_putc(p, '\n'); +unlock: + raw_spin_unlock_irqrestore(&desc->lock, flags); + } else if (i == nr_irqs) { + seq_printf(p, "Error in interrupt!\n"); + } + return 0; +} + +/* + * 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..a8970809428 --- /dev/null +++ b/arch/unicore32/kernel/ksyms.c @@ -0,0 +1,99 @@ +/* + * 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 <asm/system.h> + +#include "ksyms.h" + +EXPORT_SYMBOL(__uc32_find_next_zero_bit); +EXPORT_SYMBOL(__uc32_find_next_bit); + +EXPORT_SYMBOL(__backtrace); + + /* platform dependent support */ +EXPORT_SYMBOL(__udelay); +EXPORT_SYMBOL(__const_udelay); + + /* networking */ +EXPORT_SYMBOL(csum_partial); +EXPORT_SYMBOL(csum_partial_copy_from_user); +EXPORT_SYMBOL(csum_partial_copy_nocheck); +EXPORT_SYMBOL(__csum_ipv6_magic); + + /* io */ +#ifndef __raw_readsb +EXPORT_SYMBOL(__raw_readsb); +#endif +#ifndef __raw_readsw +EXPORT_SYMBOL(__raw_readsw); +#endif +#ifndef __raw_readsl +EXPORT_SYMBOL(__raw_readsl); +#endif +#ifndef __raw_writesb +EXPORT_SYMBOL(__raw_writesb); +#endif +#ifndef __raw_writesw +EXPORT_SYMBOL(__raw_writesw); +#endif +#ifndef __raw_writesl +EXPORT_SYMBOL(__raw_writesl); +#endif + + /* 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(__get_user_1); +EXPORT_SYMBOL(__get_user_2); +EXPORT_SYMBOL(__get_user_4); + +EXPORT_SYMBOL(__put_user_1); +EXPORT_SYMBOL(__put_user_2); +EXPORT_SYMBOL(__put_user_4); +EXPORT_SYMBOL(__put_user_8); + +EXPORT_SYMBOL(__ashldi3); +EXPORT_SYMBOL(__ashrdi3); +EXPORT_SYMBOL(__divsi3); +EXPORT_SYMBOL(__lshrdi3); +EXPORT_SYMBOL(__modsi3); +EXPORT_SYMBOL(__muldi3); +EXPORT_SYMBOL(__ucmpdi2); +EXPORT_SYMBOL(__udivsi3); +EXPORT_SYMBOL(__umodsi3); +EXPORT_SYMBOL(__bswapsi2); + diff --git a/arch/unicore32/kernel/ksyms.h b/arch/unicore32/kernel/ksyms.h new file mode 100644 index 00000000000..185cdc712d0 --- /dev/null +++ b/arch/unicore32/kernel/ksyms.h @@ -0,0 +1,15 @@ +/* + * 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 __muldi3(void); +extern void __ucmpdi2(void); +extern void __udivsi3(void); +extern void __umodsi3(void); +extern void __bswapsi2(void); diff --git a/arch/unicore32/kernel/module.c b/arch/unicore32/kernel/module.c new file mode 100644 index 00000000000..3e5a38d71a1 --- /dev/null +++ b/arch/unicore32/kernel/module.c @@ -0,0 +1,152 @@ +/* + * 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) +{ + struct vm_struct *area; + + size = PAGE_ALIGN(size); + if (!size) + return NULL; + + area = __get_vm_area(size, VM_ALLOC, MODULES_VADDR, MODULES_END); + if (!area) + return NULL; + + return __vmalloc_area(area, GFP_KERNEL, PAGE_KERNEL_EXEC); +} + +void module_free(struct module *module, void *region) +{ + vfree(region); +} + +int module_frob_arch_sections(Elf_Ehdr *hdr, + Elf_Shdr *sechdrs, + char *secstrings, + struct module *mod) +{ + return 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; +} + +int +apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab, + unsigned int symindex, unsigned int relsec, + struct module *module) +{ + printk(KERN_ERR "module %s: ADD RELOCATION unsupported\n", + module->name); + return -ENOEXEC; +} + +int +module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs, + struct module *module) +{ + return 0; +} + +void +module_arch_cleanup(struct module *mod) +{ +} diff --git a/arch/unicore32/kernel/pci.c b/arch/unicore32/kernel/pci.c new file mode 100644 index 00000000000..100eab842e6 --- /dev/null +++ b/arch/unicore32/kernel/pci.c @@ -0,0 +1,404 @@ +/* + * 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; +static int use_firmware; + +#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(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; +} + +void __devinit pcibios_update_irq(struct pci_dev *dev, int irq) +{ + if (debug_pci) + printk(KERN_DEBUG "PCI: Assigning IRQ %02d to %s\n", + irq, pci_name(dev)); + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); +} + +/* + * 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 __devinit 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"); +} +#ifdef CONFIG_HOTPLUG +EXPORT_SYMBOL(pcibios_fixup_bus); +#endif + +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 (!use_firmware) { + /* + * Size the bridge windows. + */ + pci_bus_size_bridges(puv3_bus); + + /* + * Assign resources. + */ + pci_bus_assign_resources(puv3_bus); + } + + /* + * Tell drivers about devices found. + */ + pci_bus_add_devices(puv3_bus); + + return 0; +} +subsys_initcall(pci_common_init); + +char * __devinit pcibios_setup(char *str) +{ + if (!strcmp(str, "debug")) { + debug_pci = 1; + return NULL; + } else if (!strcmp(str, "firmware")) { + use_firmware = 1; + return NULL; + } + return str; +} + +/* + * 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..ba401df971e --- /dev/null +++ b/arch/unicore32/kernel/process.c @@ -0,0 +1,389 @@ +/* + * 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/system.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" +}; + +/* + * The idle thread, has rather strange semantics for calling pm_idle, + * but this is what x86 does and we need to do the same, so that + * things like cpuidle get called in the same way. + */ +void cpu_idle(void) +{ + /* endless idle loop with no priority at all */ + while (1) { + tick_nohz_stop_sched_tick(1); + while (!need_resched()) { + local_irq_disable(); + stop_critical_timings(); + cpu_do_idle(); + local_irq_enable(); + start_critical_timings(); + } + tick_nohz_restart_sched_tick(); + preempt_enable_no_resched(); + schedule(); + preempt_disable(); + } +} + +static char reboot_mode = 'h'; + +int __init reboot_setup(char *str) +{ + reboot_mode = str[0]; + return 1; +} + +__setup("reboot=", reboot_setup); + +void machine_halt(void) +{ + gpio_set_value(GPO_SOFT_OFF, 0); +} + +/* + * Function pointers to optional machine specific functions + */ +void (*pm_power_off)(void) = NULL; + +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(reboot_mode); + + /* 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 == 's') { + /* 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]; + + printk(KERN_DEFAULT "CPU: %d %s (%s %.*s)\n", + raw_smp_processor_id(), print_tainted(), + init_utsname()->release, + (int)strcspn(init_utsname()->version, " "), + init_utsname()->version); + 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"); + +int +copy_thread(unsigned long clone_flags, unsigned long stack_start, + unsigned long stk_sz, struct task_struct *p, struct pt_regs *regs) +{ + struct thread_info *thread = task_thread_info(p); + struct pt_regs *childregs = task_pt_regs(p); + + *childregs = *regs; + childregs->UCreg_00 = 0; + childregs->UCreg_sp = stack_start; + + memset(&thread->cpu_context, 0, sizeof(struct cpu_context_save)); + thread->cpu_context.sp = (unsigned long)childregs; + thread->cpu_context.pc = (unsigned long)ret_from_fork; + + if (clone_flags & CLONE_SETTLS) + childregs->UCreg_16 = regs->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); + +/* + * Shuffle the argument into the correct register before calling the + * thread function. r1 is the thread argument, r2 is the pointer to + * the thread function, and r3 points to the exit function. + */ +asm(".pushsection .text\n" +" .align\n" +" .type kernel_thread_helper, #function\n" +"kernel_thread_helper:\n" +" mov.a asr, r7\n" +" mov r0, r4\n" +" mov lr, r6\n" +" mov pc, r5\n" +" .size kernel_thread_helper, . - kernel_thread_helper\n" +" .popsection"); + +/* + * Create a kernel thread. + */ +pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) +{ + struct pt_regs regs; + + memset(®s, 0, sizeof(regs)); + + regs.UCreg_04 = (unsigned long)arg; + regs.UCreg_05 = (unsigned long)fn; + regs.UCreg_06 = (unsigned long)do_exit; + regs.UCreg_07 = PRIV_MODE; + regs.UCreg_pc = (unsigned long)kernel_thread_helper; + regs.UCreg_asr = regs.UCreg_07 | PSR_I_BIT; + + return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); +} +EXPORT_SYMBOL(kernel_thread); + +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_ALWAYSDUMP | VM_RESERVED, + 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..8b1b6beb858 --- /dev/null +++ b/arch/unicore32/kernel/puv3-core.c @@ -0,0 +1,285 @@ +/* + * 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/sysdev.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, + }, + [1] = { + .start = PKUNITY_UNIGFX_MMAP_BASE, + .end = PKUNITY_UNIGFX_MMAP_BASE + PKUNITY_UNIGFX_MMAP_SIZE, + .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..e731c561ed4 --- /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/sysdev.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, +}; + +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_DISABLED | 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_DISABLED | 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/pwm.c b/arch/unicore32/kernel/pwm.c new file mode 100644 index 00000000000..4615d51e3ba --- /dev/null +++ b/arch/unicore32/kernel/pwm.c @@ -0,0 +1,263 @@ +/* + * linux/arch/unicore32/kernel/pwm.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/platform_device.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/pwm.h> + +#include <asm/div64.h> +#include <mach/hardware.h> + +struct pwm_device { + struct list_head node; + struct platform_device *pdev; + + const char *label; + struct clk *clk; + int clk_enabled; + + unsigned int use_count; + unsigned int pwm_id; +}; + +/* + * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE + * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE + */ +int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) +{ + unsigned long long c; + unsigned long period_cycles, prescale, pv, dc; + + if (pwm == NULL || period_ns == 0 || duty_ns > period_ns) + return -EINVAL; + + c = clk_get_rate(pwm->clk); + c = c * period_ns; + do_div(c, 1000000000); + period_cycles = c; + + if (period_cycles < 1) + period_cycles = 1; + prescale = (period_cycles - 1) / 1024; + pv = period_cycles / (prescale + 1) - 1; + + if (prescale > 63) + return -EINVAL; + + if (duty_ns == period_ns) + dc = OST_PWMDCCR_FDCYCLE; + else + dc = (pv + 1) * duty_ns / period_ns; + + /* NOTE: the clock to PWM has to be enabled first + * before writing to the registers + */ + clk_enable(pwm->clk); + OST_PWMPWCR = prescale; + OST_PWMDCCR = pv - dc; + OST_PWMPCR = pv; + clk_disable(pwm->clk); + + return 0; +} +EXPORT_SYMBOL(pwm_config); + +int pwm_enable(struct pwm_device *pwm) +{ + int rc = 0; + + if (!pwm->clk_enabled) { + rc = clk_enable(pwm->clk); + if (!rc) + pwm->clk_enabled = 1; + } + return rc; +} +EXPORT_SYMBOL(pwm_enable); + +void pwm_disable(struct pwm_device *pwm) +{ + if (pwm->clk_enabled) { + clk_disable(pwm->clk); + pwm->clk_enabled = 0; + } +} +EXPORT_SYMBOL(pwm_disable); + +static DEFINE_MUTEX(pwm_lock); +static LIST_HEAD(pwm_list); + +struct pwm_device *pwm_request(int pwm_id, const char *label) +{ + struct pwm_device *pwm; + int found = 0; + + mutex_lock(&pwm_lock); + + list_for_each_entry(pwm, &pwm_list, node) { + if (pwm->pwm_id == pwm_id) { + found = 1; + break; + } + } + + if (found) { + if (pwm->use_count == 0) { + pwm->use_count++; + pwm->label = label; + } else + pwm = ERR_PTR(-EBUSY); + } else + pwm = ERR_PTR(-ENOENT); + + mutex_unlock(&pwm_lock); + return pwm; +} +EXPORT_SYMBOL(pwm_request); + +void pwm_free(struct pwm_device *pwm) +{ + mutex_lock(&pwm_lock); + + if (pwm->use_count) { + pwm->use_count--; + pwm->label = NULL; + } else + pr_warning("PWM device already freed\n"); + + mutex_unlock(&pwm_lock); +} +EXPORT_SYMBOL(pwm_free); + +static inline void __add_pwm(struct pwm_device *pwm) +{ + mutex_lock(&pwm_lock); + list_add_tail(&pwm->node, &pwm_list); + mutex_unlock(&pwm_lock); +} + +static struct pwm_device *pwm_probe(struct platform_device *pdev, + unsigned int pwm_id, struct pwm_device *parent_pwm) +{ + struct pwm_device *pwm; + struct resource *r; + int ret = 0; + + pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); + if (pwm == NULL) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return ERR_PTR(-ENOMEM); + } + + pwm->clk = clk_get(NULL, "OST_CLK"); + if (IS_ERR(pwm->clk)) { + ret = PTR_ERR(pwm->clk); + goto err_free; + } + pwm->clk_enabled = 0; + + pwm->use_count = 0; + pwm->pwm_id = pwm_id; + pwm->pdev = pdev; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (r == NULL) { + dev_err(&pdev->dev, "no memory resource defined\n"); + ret = -ENODEV; + goto err_free_clk; + } + + r = request_mem_region(r->start, resource_size(r), pdev->name); + if (r == NULL) { + dev_err(&pdev->dev, "failed to request memory resource\n"); + ret = -EBUSY; + goto err_free_clk; + } + + __add_pwm(pwm); + platform_set_drvdata(pdev, pwm); + return pwm; + +err_free_clk: + clk_put(pwm->clk); +err_free: + kfree(pwm); + return ERR_PTR(ret); +} + +static int __devinit puv3_pwm_probe(struct platform_device *pdev) +{ + struct pwm_device *pwm = pwm_probe(pdev, pdev->id, NULL); + + if (IS_ERR(pwm)) + return PTR_ERR(pwm); + + return 0; +} + +static int __devexit pwm_remove(struct platform_device *pdev) +{ + struct pwm_device *pwm; + struct resource *r; + + pwm = platform_get_drvdata(pdev); + if (pwm == NULL) + return -ENODEV; + + mutex_lock(&pwm_lock); + list_del(&pwm->node); + mutex_unlock(&pwm_lock); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(r->start, resource_size(r)); + + clk_put(pwm->clk); + kfree(pwm); + return 0; +} + +static struct platform_driver puv3_pwm_driver = { + .driver = { + .name = "PKUnity-v3-PWM", + }, + .probe = puv3_pwm_probe, + .remove = __devexit_p(pwm_remove), +}; + +static int __init pwm_init(void) +{ + int ret = 0; + + ret = platform_driver_register(&puv3_pwm_driver); + if (ret) { + printk(KERN_ERR "failed to register puv3_pwm_driver\n"); + return ret; + } + + return ret; +} +arch_initcall(pwm_init); + +static void __exit pwm_exit(void) +{ + platform_driver_unregister(&puv3_pwm_driver); +} +module_exit(pwm_exit); + +MODULE_LICENSE("GPL v2"); diff --git a/arch/unicore32/kernel/rtc.c b/arch/unicore32/kernel/rtc.c new file mode 100644 index 00000000000..c5f068295b5 --- /dev/null +++ b/arch/unicore32/kernel/rtc.c @@ -0,0 +1,380 @@ +/* + * linux/arch/unicore32/kernel/rtc.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/fs.h> +#include <linux/string.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/rtc.h> +#include <linux/bcd.h> +#include <linux/clk.h> +#include <linux/log2.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/io.h> + +#include <asm/irq.h> +#include <mach/hardware.h> + +static struct resource *puv3_rtc_mem; + +static int puv3_rtc_alarmno = IRQ_RTCAlarm; +static int puv3_rtc_tickno = IRQ_RTC; + +static DEFINE_SPINLOCK(puv3_rtc_pie_lock); + +/* IRQ Handlers */ + +static irqreturn_t puv3_rtc_alarmirq(int irq, void *id) +{ + struct rtc_device *rdev = id; + + writel(readl(RTC_RTSR) | RTC_RTSR_AL, RTC_RTSR); + rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF); + return IRQ_HANDLED; +} + +static irqreturn_t puv3_rtc_tickirq(int irq, void *id) +{ + struct rtc_device *rdev = id; + + writel(readl(RTC_RTSR) | RTC_RTSR_HZ, RTC_RTSR); + rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF); + return IRQ_HANDLED; +} + +/* Update control registers */ +static void puv3_rtc_setaie(int to) +{ + unsigned int tmp; + + pr_debug("%s: aie=%d\n", __func__, to); + + tmp = readl(RTC_RTSR) & ~RTC_RTSR_ALE; + + if (to) + tmp |= RTC_RTSR_ALE; + + writel(tmp, RTC_RTSR); +} + +static int puv3_rtc_setpie(struct device *dev, int enabled) +{ + unsigned int tmp; + + pr_debug("%s: pie=%d\n", __func__, enabled); + + spin_lock_irq(&puv3_rtc_pie_lock); + tmp = readl(RTC_RTSR) & ~RTC_RTSR_HZE; + + if (enabled) + tmp |= RTC_RTSR_HZE; + + writel(tmp, RTC_RTSR); + spin_unlock_irq(&puv3_rtc_pie_lock); + + return 0; +} + +static int puv3_rtc_setfreq(struct device *dev, int freq) +{ + return 0; +} + +/* Time read/write */ + +static int puv3_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) +{ + rtc_time_to_tm(readl(RTC_RCNR), rtc_tm); + + pr_debug("read time %02x.%02x.%02x %02x/%02x/%02x\n", + rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday, + rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec); + + return 0; +} + +static int puv3_rtc_settime(struct device *dev, struct rtc_time *tm) +{ + unsigned long rtc_count = 0; + + pr_debug("set time %02d.%02d.%02d %02d/%02d/%02d\n", + tm->tm_year, tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + rtc_tm_to_time(tm, &rtc_count); + writel(rtc_count, RTC_RCNR); + + return 0; +} + +static int puv3_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rtc_time *alm_tm = &alrm->time; + + rtc_time_to_tm(readl(RTC_RTAR), alm_tm); + + alrm->enabled = readl(RTC_RTSR) & RTC_RTSR_ALE; + + pr_debug("read alarm %02x %02x.%02x.%02x %02x/%02x/%02x\n", + alrm->enabled, + alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday, + alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec); + + return 0; +} + +static int puv3_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rtc_time *tm = &alrm->time; + unsigned long rtcalarm_count = 0; + + pr_debug("puv3_rtc_setalarm: %d, %02x/%02x/%02x %02x.%02x.%02x\n", + alrm->enabled, + tm->tm_mday & 0xff, tm->tm_mon & 0xff, tm->tm_year & 0xff, + tm->tm_hour & 0xff, tm->tm_min & 0xff, tm->tm_sec); + + rtc_tm_to_time(tm, &rtcalarm_count); + writel(rtcalarm_count, RTC_RTAR); + + puv3_rtc_setaie(alrm->enabled); + + if (alrm->enabled) + enable_irq_wake(puv3_rtc_alarmno); + else + disable_irq_wake(puv3_rtc_alarmno); + + return 0; +} + +static int puv3_rtc_proc(struct device *dev, struct seq_file *seq) +{ + seq_printf(seq, "periodic_IRQ\t: %s\n", + (readl(RTC_RTSR) & RTC_RTSR_HZE) ? "yes" : "no"); + return 0; +} + +static int puv3_rtc_open(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rtc_device *rtc_dev = platform_get_drvdata(pdev); + int ret; + + ret = request_irq(puv3_rtc_alarmno, puv3_rtc_alarmirq, + IRQF_DISABLED, "pkunity-rtc alarm", rtc_dev); + + if (ret) { + dev_err(dev, "IRQ%d error %d\n", puv3_rtc_alarmno, ret); + return ret; + } + + ret = request_irq(puv3_rtc_tickno, puv3_rtc_tickirq, + IRQF_DISABLED, "pkunity-rtc tick", rtc_dev); + + if (ret) { + dev_err(dev, "IRQ%d error %d\n", puv3_rtc_tickno, ret); + goto tick_err; + } + + return ret; + + tick_err: + free_irq(puv3_rtc_alarmno, rtc_dev); + return ret; +} + +static void puv3_rtc_release(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rtc_device *rtc_dev = platform_get_drvdata(pdev); + + /* do not clear AIE here, it may be needed for wake */ + + puv3_rtc_setpie(dev, 0); + free_irq(puv3_rtc_alarmno, rtc_dev); + free_irq(puv3_rtc_tickno, rtc_dev); +} + +static const struct rtc_class_ops puv3_rtcops = { + .open = puv3_rtc_open, + .release = puv3_rtc_release, + .read_time = puv3_rtc_gettime, + .set_time = puv3_rtc_settime, + .read_alarm = puv3_rtc_getalarm, + .set_alarm = puv3_rtc_setalarm, + .irq_set_freq = puv3_rtc_setfreq, + .irq_set_state = puv3_rtc_setpie, + .proc = puv3_rtc_proc, +}; + +static void puv3_rtc_enable(struct platform_device *pdev, int en) +{ + if (!en) { + writel(readl(RTC_RTSR) & ~RTC_RTSR_HZE, RTC_RTSR); + } else { + /* re-enable the device, and check it is ok */ + + if ((readl(RTC_RTSR) & RTC_RTSR_HZE) == 0) { + dev_info(&pdev->dev, "rtc disabled, re-enabling\n"); + writel(readl(RTC_RTSR) | RTC_RTSR_HZE, RTC_RTSR); + } + } +} + +static int puv3_rtc_remove(struct platform_device *dev) +{ + struct rtc_device *rtc = platform_get_drvdata(dev); + + platform_set_drvdata(dev, NULL); + rtc_device_unregister(rtc); + + puv3_rtc_setpie(&dev->dev, 0); + puv3_rtc_setaie(0); + + release_resource(puv3_rtc_mem); + kfree(puv3_rtc_mem); + + return 0; +} + +static int puv3_rtc_probe(struct platform_device *pdev) +{ + struct rtc_device *rtc; + struct resource *res; + int ret; + + pr_debug("%s: probe=%p\n", __func__, pdev); + + /* find the IRQs */ + + puv3_rtc_tickno = platform_get_irq(pdev, 1); + if (puv3_rtc_tickno < 0) { + dev_err(&pdev->dev, "no irq for rtc tick\n"); + return -ENOENT; + } + + puv3_rtc_alarmno = platform_get_irq(pdev, 0); + if (puv3_rtc_alarmno < 0) { + dev_err(&pdev->dev, "no irq for alarm\n"); + return -ENOENT; + } + + pr_debug("PKUnity_rtc: tick irq %d, alarm irq %d\n", + puv3_rtc_tickno, puv3_rtc_alarmno); + + /* get the memory region */ + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "failed to get memory region resource\n"); + return -ENOENT; + } + + puv3_rtc_mem = request_mem_region(res->start, + res->end-res->start+1, + pdev->name); + + if (puv3_rtc_mem == NULL) { + dev_err(&pdev->dev, "failed to reserve memory region\n"); + ret = -ENOENT; + goto err_nores; + } + + puv3_rtc_enable(pdev, 1); + + puv3_rtc_setfreq(&pdev->dev, 1); + + /* register RTC and exit */ + + rtc = rtc_device_register("pkunity", &pdev->dev, &puv3_rtcops, + THIS_MODULE); + + if (IS_ERR(rtc)) { + dev_err(&pdev->dev, "cannot attach rtc\n"); + ret = PTR_ERR(rtc); + goto err_nortc; + } + + /* platform setup code should have handled this; sigh */ + if (!device_can_wakeup(&pdev->dev)) + device_init_wakeup(&pdev->dev, 1); + + platform_set_drvdata(pdev, rtc); + return 0; + + err_nortc: + puv3_rtc_enable(pdev, 0); + release_resource(puv3_rtc_mem); + + err_nores: + return ret; +} + +#ifdef CONFIG_PM + +/* RTC Power management control */ + +static int ticnt_save; + +static int puv3_rtc_suspend(struct platform_device *pdev, pm_message_t state) +{ + /* save RTAR for anyone using periodic interrupts */ + ticnt_save = readl(RTC_RTAR); + puv3_rtc_enable(pdev, 0); + return 0; +} + +static int puv3_rtc_resume(struct platform_device *pdev) +{ + puv3_rtc_enable(pdev, 1); + writel(ticnt_save, RTC_RTAR); + return 0; +} +#else +#define puv3_rtc_suspend NULL +#define puv3_rtc_resume NULL +#endif + +static struct platform_driver puv3_rtcdrv = { + .probe = puv3_rtc_probe, + .remove = __devexit_p(puv3_rtc_remove), + .suspend = puv3_rtc_suspend, + .resume = puv3_rtc_resume, + .driver = { + .name = "PKUnity-v3-RTC", + .owner = THIS_MODULE, + } +}; + +static char __initdata banner[] = "PKUnity-v3 RTC, (c) 2009 PKUnity Co.\n"; + +static int __init puv3_rtc_init(void) +{ + printk(banner); + return platform_driver_register(&puv3_rtcdrv); +} + +static void __exit puv3_rtc_exit(void) +{ + platform_driver_unregister(&puv3_rtcdrv); +} + +module_init(puv3_rtc_init); +module_exit(puv3_rtc_exit); + +MODULE_DESCRIPTION("RTC Driver for the PKUnity v3 chip"); +MODULE_AUTHOR("Hu Dongliang"); +MODULE_LICENSE("GPL v2"); + diff --git a/arch/unicore32/kernel/setup.c b/arch/unicore32/kernel/setup.c new file mode 100644 index 00000000000..1e175a82844 --- /dev/null +++ b/arch/unicore32/kernel/setup.c @@ -0,0 +1,360 @@ +/* + * 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 "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]; + +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 = "Video RAM", + .start = 0, + .end = 0, + .flags = IORESOURCE_MEM + }, + { + .name = "Kernel text", + .start = 0, + .end = 0, + .flags = IORESOURCE_MEM + }, + { + .name = "Kernel data", + .start = 0, + .end = 0, + .flags = IORESOURCE_MEM + } +}; + +#define video_ram mem_res[0] +#define kernel_code mem_res[1] +#define kernel_data mem_res[2] + +/* + * 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); + } + + video_ram.start = PKUNITY_UNIGFX_MMAP_BASE; + video_ram.end = PKUNITY_UNIGFX_MMAP_BASE + PKUNITY_UNIGFX_MMAP_SIZE; + request_resource(&iomem_resource, &video_ram); +} + +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..dcd1306eb5c --- /dev/null +++ b/arch/unicore32/kernel/setup.h @@ -0,0 +1,30 @@ +/* + * 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__ + +extern void paging_init(void); +extern void puv3_core_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(char mode); + +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); +#endif diff --git a/arch/unicore32/kernel/signal.c b/arch/unicore32/kernel/signal.c new file mode 100644 index 00000000000..b163fca5678 --- /dev/null +++ b/arch/unicore32/kernel/signal.c @@ -0,0 +1,494 @@ +/* + * 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/freezer.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> + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +/* + * 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) { + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } + + 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 (do_sigaltstack(&frame->sig.uc.uc_stack, NULL, regs->UCreg_sp) + == -EFAULT) + 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)); + stack_t stack; + 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); + + memset(&stack, 0, sizeof(stack)); + stack.ss_sp = (void __user *)current->sas_ss_sp; + stack.ss_flags = sas_ss_flags(regs->UCreg_sp); + stack.ss_size = current->sas_ss_size; + err |= __copy_to_user(&frame->sig.uc.uc_stack, &stack, sizeof(stack)); + + 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 int handle_signal(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, + struct pt_regs *regs, int syscall) +{ + struct thread_info *thread = current_thread_info(); + struct task_struct *tsk = current; + 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 ret; + } + + /* + * Block the signal if we were successful. + */ + spin_lock_irq(&tsk->sighand->siglock); + sigorsets(&tsk->blocked, &tsk->blocked, + &ka->sa.sa_mask); + if (!(ka->sa.sa_flags & SA_NODEFER)) + sigaddset(&tsk->blocked, sig); + recalc_sigpending(); + spin_unlock_irq(&tsk->sighand->siglock); + + return 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; + + if (try_to_freeze()) + goto no_signal; + + signr = get_signal_to_deliver(&info, &ka, regs, NULL); + if (signr > 0) { + sigset_t *oldset; + + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + oldset = ¤t->saved_sigmask; + else + oldset = ¤t->blocked; + if (handle_signal(signr, &ka, &info, oldset, regs, syscall) + == 0) { + /* + * A signal was successfully delivered; the saved + * sigmask will have been stored in the signal frame, + * and will be restored by sigreturn, so we can simply + * clear the TIF_RESTORE_SIGMASK flag. + */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + clear_thread_flag(TIF_RESTORE_SIGMASK); + } + return; + } + + no_signal: + /* + * 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. + */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) { + clear_thread_flag(TIF_RESTORE_SIGMASK); + sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); + } + } +} + +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); + if (current->replacement_session_keyring) + key_replace_session_keyring(); + } +} + +/* + * 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..3afe60a39ac --- /dev/null +++ b/arch/unicore32/kernel/sys.c @@ -0,0 +1,126 @@ +/* + * 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> + +/* Clone a task - this clones the calling program thread. + * This is called indirectly via a small wrapper + */ +asmlinkage long __sys_clone(unsigned long clone_flags, unsigned long newsp, + void __user *parent_tid, void __user *child_tid, + struct pt_regs *regs) +{ + if (!newsp) + newsp = regs->UCreg_sp; + + return do_fork(clone_flags, newsp, regs, 0, + parent_tid, child_tid); +} + +/* sys_execve() executes a new program. + * This is called indirectly via a small wrapper + */ +asmlinkage long __sys_execve(const char __user *filename, + const char __user *const __user *argv, + const char __user *const __user *envp, + struct pt_regs *regs) +{ + int error; + char *fn; + + fn = getname(filename); + error = PTR_ERR(fn); + if (IS_ERR(fn)) + goto out; + error = do_execve(fn, argv, envp, regs); + putname(fn); +out: + return error; +} + +int kernel_execve(const char *filename, + const char *const argv[], + const char *const envp[]) +{ + struct pt_regs regs; + int ret; + + memset(®s, 0, sizeof(struct pt_regs)); + ret = do_execve(filename, + (const char __user *const __user *)argv, + (const char __user *const __user *)envp, ®s); + if (ret < 0) + goto out; + + /* + * Save argc to the register structure for userspace. + */ + regs.UCreg_00 = ret; + + /* + * We were successful. We won't be returning to our caller, but + * instead to user space by manipulating the kernel stack. + */ + asm("add r0, %0, %1\n\t" + "mov r1, %2\n\t" + "mov r2, %3\n\t" + "mov r22, #0\n\t" /* not a syscall */ + "mov r23, %0\n\t" /* thread structure */ + "b.l memmove\n\t" /* copy regs to top of stack */ + "mov sp, r0\n\t" /* reposition stack pointer */ + "b ret_to_user" + : + : "r" (current_thread_info()), + "Ir" (THREAD_START_SP - sizeof(regs)), + "r" (®s), + "Ir" (sizeof(regs)) + : "r0", "r1", "r2", "r3", "ip", "lr", "memory"); + + out: + return ret; +} +EXPORT_SYMBOL(kernel_execve); + +/* Note: used by the compat code even in 64-bit Linux. */ +SYSCALL_DEFINE6(mmap2, unsigned long, addr, unsigned long, len, + unsigned long, prot, unsigned long, flags, + unsigned long, fd, unsigned long, off_4k) +{ + return sys_mmap_pgoff(addr, len, prot, flags, fd, + off_4k); +} + +/* Provide the actual syscall number to call mapping. */ +#undef __SYSCALL +#define __SYSCALL(nr, call) [nr] = (call), + +/* 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..080710c0924 --- /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_DISABLED | 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..25abbb10172 --- /dev/null +++ b/arch/unicore32/kernel/traps.c @@ -0,0 +1,333 @@ +/* + * 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/uaccess.h> +#include <linux/atomic.h> +#include <linux/unistd.h> + +#include <asm/cacheflush.h> +#include <asm/system.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 dump_stack(void) +{ + dump_backtrace(NULL, NULL); +} +EXPORT_SYMBOL(dump_stack); + +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); + sysfs_printk_last_file(); + + /* 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); + 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..0b4eb89729e --- /dev/null +++ b/arch/unicore32/kernel/vmlinux.lds.S @@ -0,0 +1,61 @@ +/* + * 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> + +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(PAGE_SIZE) + __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(32, PAGE_SIZE, THREAD_SIZE) + _edata = .; + + EXCEPTION_TABLE(32) + 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..c360ce905d8 --- /dev/null +++ b/arch/unicore32/lib/findbit.S @@ -0,0 +1,98 @@ +/* + * 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); + */ +__uc32_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 + +/* + * Purpose : Find next 'zero' bit + * Prototype: int find_next_zero_bit + * (void *addr, unsigned int maxbit, int offset) + */ +ENTRY(__uc32_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(__uc32_find_next_zero_bit) + +/* + * Purpose : Find a 'one' bit + * Prototype: int find_first_bit + * (const unsigned long *addr, unsigned int maxbit); + */ +__uc32_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 + +/* + * Purpose : Find next 'one' bit + * Prototype: int find_next_zero_bit + * (void *addr, unsigned int maxbit, int offset) + */ +ENTRY(__uc32_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(__uc32_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..28f576d733e --- /dev/null +++ b/arch/unicore32/mm/alignment.c @@ -0,0 +1,523 @@ +/* + * 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/tlbflush.h> +#include <asm/unaligned.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..bfa9fbb2bbb --- /dev/null +++ b/arch/unicore32/mm/dma-swiotlb.c @@ -0,0 +1,34 @@ +/* + * 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> + +struct dma_map_ops swiotlb_dma_map_ops = { + .alloc_coherent = swiotlb_alloc_coherent, + .free_coherent = 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..283aa4b50b7 --- /dev/null +++ b/arch/unicore32/mm/fault.c @@ -0,0 +1,479 @@ +/* + * 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/system.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, + 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, + (!(fsr ^ 0x12)) ? FAULT_FLAG_WRITE : 0); + if (unlikely(fault & VM_FAULT_ERROR)) + return fault; + if (fault & VM_FAULT_MAJOR) + tsk->maj_flt++; + else + tsk->min_flt++; + 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; + + 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; + + /* + * 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; + 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, tsk); + 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 (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 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_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..93478cc8b26 --- /dev/null +++ b/arch/unicore32/mm/flush.c @@ -0,0 +1,98 @@ +/* + * 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/system.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..3dbe3709b69 --- /dev/null +++ b/arch/unicore32/mm/init.c @@ -0,0 +1,517 @@ +/* + * 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 <asm/sections.h> +#include <asm/setup.h> +#include <asm/sizes.h> +#include <asm/tlb.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(void) +{ + 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(); + + 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); + + memblock_init(); + 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_analyze(); + 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 int free_area(unsigned long pfn, unsigned long end, char *s) +{ + unsigned int pages = 0, size = (end - pfn) << (PAGE_SHIFT - 10); + + for (; pfn < end; pfn++) { + struct page *page = pfn_to_page(pfn); + ClearPageReserved(page); + init_page_count(page); + __free_page(page); + pages++; + } + + if (size && s) + printk(KERN_INFO "Freeing %s memory: %dK\n", s, size); + + return pages; +} + +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) +{ + unsigned long reserved_pages, free_pages; + struct memblock_region *reg; + int i; + + max_mapnr = pfn_to_page(max_pfn + PHYS_PFN_OFFSET) - mem_map; + + /* this will put all unused low memory onto the freelists */ + free_unused_memmap(&meminfo); + + totalram_pages += free_all_bootmem(); + + reserved_pages = free_pages = 0; + + for_each_bank(i, &meminfo) { + struct membank *bank = &meminfo.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 { + if (PageReserved(page)) + reserved_pages++; + else if (!page_count(page)) + free_pages++; + page++; + } while (page < end); + } + + /* + * Since our memory may not be contiguous, calculate the + * real number of pages we have in this system + */ + printk(KERN_INFO "Memory:"); + num_physpages = 0; + for_each_memblock(memory, reg) { + unsigned long pages = memblock_region_memory_end_pfn(reg) - + memblock_region_memory_base_pfn(reg); + num_physpages += pages; + printk(" %ldMB", pages >> (20 - PAGE_SHIFT)); + } + printk(" = %luMB total\n", num_physpages >> (20 - PAGE_SHIFT)); + + printk(KERN_NOTICE "Memory: %luk/%luk available, %luk reserved, %luK highmem\n", + nr_free_pages() << (PAGE_SHIFT-10), + free_pages << (PAGE_SHIFT-10), + reserved_pages << (PAGE_SHIFT-10), + totalhigh_pages << (PAGE_SHIFT-10)); + + 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 && 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) +{ + totalram_pages += free_area(__phys_to_pfn(__pa(__init_begin)), + __phys_to_pfn(__pa(__init_end)), + "init"); +} + +#ifdef CONFIG_BLK_DEV_INITRD + +static int keep_initrd; + +void free_initrd_mem(unsigned long start, unsigned long end) +{ + if (!keep_initrd) + totalram_pages += free_area(__phys_to_pfn(__pa(start)), + __phys_to_pfn(__pa(end)), + "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..b7a605597b0 --- /dev/null +++ b/arch/unicore32/mm/ioremap.c @@ -0,0 +1,261 @@ +/* + * 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)) { + printk(KERN_WARNING "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"); + WARN_ON(1); + } + + 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 **p, *tmp; + + /* + * 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. + */ + write_lock(&vmlist_lock); + for (p = &vmlist ; (tmp = *p) ; p = &tmp->next) { + if ((tmp->flags & VM_IOREMAP) && (tmp->addr == addr)) { + if (tmp->flags & VM_UNICORE_SECTION_MAPPING) { + unmap_area_sections((unsigned long)tmp->addr, + tmp->size); + } + break; + } + } + write_unlock(&vmlist_lock); + + 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..3296bca0f1f --- /dev/null +++ b/arch/unicore32/mm/mm.h @@ -0,0 +1,39 @@ +/* + * 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. + */ +/* 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 *); + +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..7bf3d588631 --- /dev/null +++ b/arch/unicore32/mm/mmu.c @@ -0,0 +1,533 @@ +/* + * 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 <mach/map.h> + +#include "mm.h" + +DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); + +/* + * 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)); + +#ifdef CONFIG_PUV3_UNIGFX + /* + * These should likewise go elsewhere. They pre-reserve the + * screen/video memory region at the 48M~64M of main system memory. + */ + memblock_reserve(PKUNITY_UNIGFX_MMAP_BASE, PKUNITY_UNIGFX_MMAP_SIZE); + memblock_reserve(PKUNITY_UVC_MMAP_BASE, PKUNITY_UVC_MMAP_SIZE); +#endif +} + +/* + * 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 UniGFX VRAM + */ +#ifdef CONFIG_PUV3_UNIGFX + map.pfn = __phys_to_pfn(PKUNITY_UNIGFX_MMAP_BASE); + map.virtual = KUSER_UNIGFX_BASE; + map.length = PKUNITY_UNIGFX_MMAP_SIZE; + map.type = MT_KUSER; + create_mapping(&map); +#endif + + /* + * 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(char mode) +{ + 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..f30071e3665 --- /dev/null +++ b/arch/unicore32/mm/proc-syms.c @@ -0,0 +1,23 @@ +/* + * 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_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 + |