diff options
author | root <root@artemis.panaceas.org> | 2015-12-25 04:40:36 +0000 |
---|---|---|
committer | root <root@artemis.panaceas.org> | 2015-12-25 04:40:36 +0000 |
commit | 849369d6c66d3054688672f97d31fceb8e8230fb (patch) | |
tree | 6135abc790ca67dedbe07c39806591e70eda81ce /arch/mn10300 | |
download | linux-3.0.35-kobo-849369d6c66d3054688672f97d31fceb8e8230fb.tar.gz linux-3.0.35-kobo-849369d6c66d3054688672f97d31fceb8e8230fb.tar.bz2 linux-3.0.35-kobo-849369d6c66d3054688672f97d31fceb8e8230fb.zip |
initial_commit
Diffstat (limited to 'arch/mn10300')
278 files changed, 35259 insertions, 0 deletions
diff --git a/arch/mn10300/Kconfig b/arch/mn10300/Kconfig new file mode 100644 index 00000000..1f870340 --- /dev/null +++ b/arch/mn10300/Kconfig @@ -0,0 +1,497 @@ +config MN10300 + def_bool y + select HAVE_OPROFILE + select HAVE_GENERIC_HARDIRQS + select GENERIC_IRQ_SHOW + select HAVE_ARCH_TRACEHOOK + select HAVE_ARCH_KGDB + +config AM33_2 + def_bool n + +config AM33_3 + def_bool n + +config AM34_2 + def_bool n + select MN10300_HAS_ATOMIC_OPS_UNIT + select MN10300_HAS_CACHE_SNOOP + +config ERRATUM_NEED_TO_RELOAD_MMUCTR + def_bool y if AM33_3 || AM34_2 + +config MMU + def_bool y + +config HIGHMEM + def_bool n + +config NUMA + def_bool n + +config UID16 + def_bool y + +config RWSEM_GENERIC_SPINLOCK + def_bool y + +config RWSEM_XCHGADD_ALGORITHM + bool + +config GENERIC_CALIBRATE_DELAY + def_bool y + +config GENERIC_CMOS_UPDATE + def_bool n + +config GENERIC_HWEIGHT + def_bool y + +config GENERIC_TIME + def_bool y + +config GENERIC_CLOCKEVENTS + def_bool y + +config GENERIC_BUG + def_bool y + +config QUICKLIST + def_bool y + +config ARCH_HAS_ILOG2_U32 + def_bool y + +config HOTPLUG_CPU + def_bool n + +source "init/Kconfig" + +source "kernel/Kconfig.freezer" + + +menu "Panasonic MN10300 system setup" + +choice + prompt "Unit type" + default MN10300_UNIT_ASB2303 + help + This option specifies board for which the kernel will be + compiled. It affects the external peripherals catered for. + +config MN10300_UNIT_ASB2303 + bool "ASB2303" + +config MN10300_UNIT_ASB2305 + bool "ASB2305" + +config MN10300_UNIT_ASB2364 + bool "ASB2364" + select SMSC911X_ARCH_HOOKS if SMSC911X + +endchoice + +choice + prompt "Processor support" + default MN10300_PROC_MN103E010 + help + This option specifies the processor for which the kernel will be + compiled. It affects the on-chip peripherals catered for. + +config MN10300_PROC_MN103E010 + bool "MN103E010" + depends on MN10300_UNIT_ASB2303 || MN10300_UNIT_ASB2305 + select AM33_2 + select MN10300_PROC_HAS_TTYSM0 + select MN10300_PROC_HAS_TTYSM1 + select MN10300_PROC_HAS_TTYSM2 + +config MN10300_PROC_MN2WS0050 + bool "MN2WS0050" + depends on MN10300_UNIT_ASB2364 + select AM34_2 + select MN10300_PROC_HAS_TTYSM0 + select MN10300_PROC_HAS_TTYSM1 + select MN10300_PROC_HAS_TTYSM2 + +endchoice + +config MN10300_HAS_ATOMIC_OPS_UNIT + def_bool n + help + This should be enabled if the processor has an atomic ops unit + capable of doing LL/SC equivalent operations. + +config FPU + bool "FPU present" + default y + depends on MN10300_PROC_MN103E010 || MN10300_PROC_MN2WS0050 + +config LAZY_SAVE_FPU + bool "Save FPU state lazily" + default y + depends on FPU && !SMP + help + Enable this to be lazy in the saving of the FPU state to the owning + task's thread struct. This is useful if most tasks on the system + don't use the FPU as only those tasks that use it will pass it + between them, and the state needn't be saved for a task that isn't + using it. + + This can't be so easily used on SMP as the process that owns the FPU + state on a CPU may be currently running on another CPU, so for the + moment, it is disabled. + +source "arch/mn10300/mm/Kconfig.cache" + +config MN10300_TLB_USE_PIDR + def_bool y + +menu "Memory layout options" + +config KERNEL_RAM_BASE_ADDRESS + hex "Base address of kernel RAM" + default "0x90000000" + +config INTERRUPT_VECTOR_BASE + hex "Base address of vector table" + default "0x90000000" + help + The base address of the vector table will be programmed into + the TBR register. It must be on 16MiB address boundary. + +config KERNEL_TEXT_ADDRESS + hex "Base address of kernel" + default "0x90001000" + +config KERNEL_ZIMAGE_BASE_ADDRESS + hex "Base address of compressed vmlinux image" + default "0x50700000" + +config BOOT_STACK_OFFSET + hex + default "0xF00" if SMP + default "0xFF0" if !SMP + +config BOOT_STACK_SIZE + hex + depends on SMP + default "0x100" +endmenu + +config SMP + bool "Symmetric multi-processing support" + default y + select USE_GENERIC_SMP_HELPERS + depends on MN10300_PROC_MN2WS0038 || MN10300_PROC_MN2WS0050 + ---help--- + This enables support for systems with more than one CPU. If you have + a system with only one CPU, like most personal computers, say N. If + you have a system with more than one CPU, say Y. + + If you say N here, the kernel will run on single and multiprocessor + machines, but will use only one CPU of a multiprocessor machine. If + you say Y here, the kernel will run on many, but not all, + singleprocessor machines. On a singleprocessor machine, the kernel + will run faster if you say N here. + + See also <file:Documentation/i386/IO-APIC.txt>, + <file:Documentation/nmi_watchdog.txt> and the SMP-HOWTO available at + <http://www.tldp.org/docs.html#howto>. + + If you don't know what to do here, say N. + +config NR_CPUS + int + depends on SMP + default "2" + +source "kernel/Kconfig.preempt" + +config MN10300_CURRENT_IN_E2 + bool "Hold current task address in E2 register" + depends on !SMP + default y + help + This option removes the E2/R2 register from the set available to gcc + for normal use and instead uses it to store the address of the + current process's task_struct whilst in the kernel. + + This means the kernel doesn't need to calculate the address each time + "current" is used (take SP, AND with mask and dereference pointer + just to get the address), and instead can just use E2+offset + addressing each time. + + This has no effect on userspace. + +config MN10300_USING_JTAG + bool "Using JTAG to debug kernel" + default y + help + This options indicates that JTAG will be used to debug the kernel. It + suppresses the use of certain hardware debugging features, such as + single-stepping, which are taken over completely by the JTAG unit. + +source "kernel/Kconfig.hz" +source "kernel/time/Kconfig" + +config MN10300_RTC + bool "Using MN10300 RTC" + depends on MN10300_PROC_MN103E010 || MN10300_PROC_MN2WS0050 + select GENERIC_CMOS_UPDATE + default n + help + This option enables support for the RTC, thus enabling time to be + tracked, even when system is powered down. This is available on-chip + on the MN103E010. + +config MN10300_WD_TIMER + bool "Using MN10300 watchdog timer" + default y + help + This options indicates that the watchdog timer will be used. + +config PCI + bool "Use PCI" + depends on MN10300_UNIT_ASB2305 + default y + help + Some systems (such as the ASB2305) have PCI onboard. If you have one + of these boards and you wish to use the PCI facilities, say Y here. + + The PCI-HOWTO, available from + <http://www.tldp.org/docs.html#howto>, contains valuable + information about which PCI hardware does work under Linux and which + doesn't. + +source "drivers/pci/Kconfig" + +source "drivers/pcmcia/Kconfig" + +menu "MN10300 internal serial options" + +config MN10300_PROC_HAS_TTYSM0 + bool + default n + +config MN10300_PROC_HAS_TTYSM1 + bool + default n + +config MN10300_PROC_HAS_TTYSM2 + bool + default n + +config MN10300_TTYSM + bool "Support for ttySM serial ports" + depends on MN10300 + default y + select SERIAL_CORE + help + This option enables support for the on-chip serial ports that the + MN10300 has available. + +config MN10300_TTYSM_CONSOLE + bool "Support for console on ttySM serial ports" + depends on MN10300_TTYSM + select SERIAL_CORE_CONSOLE + help + This option enables support for a console on the on-chip serial ports + that the MN10300 has available. + +# +# /dev/ttySM0 +# +config MN10300_TTYSM0 + bool "Enable SIF0 (/dev/ttySM0)" + depends on MN10300_TTYSM && MN10300_PROC_HAS_TTYSM0 + help + Enable access to SIF0 through /dev/ttySM0 or gdb-stub + +choice + prompt "Select the timer to supply the clock for SIF0" + default MN10300_TTYSM0_TIMER8 + depends on MN10300_TTYSM0 + +config MN10300_TTYSM0_TIMER8 + bool "Use timer 8 (16-bit)" + +config MN10300_TTYSM0_TIMER2 + bool "Use timer 2 (8-bit)" + +endchoice + +# +# /dev/ttySM1 +# +config MN10300_TTYSM1 + bool "Enable SIF1 (/dev/ttySM1)" + depends on MN10300_TTYSM && MN10300_PROC_HAS_TTYSM1 + help + Enable access to SIF1 through /dev/ttySM1 or gdb-stub + +choice + prompt "Select the timer to supply the clock for SIF1" + default MN10300_TTYSM1_TIMER12 \ + if !(AM33_2 || AM33_3) + default MN10300_TTYSM1_TIMER9 \ + if AM33_2 || AM33_3 + depends on MN10300_TTYSM1 + +config MN10300_TTYSM1_TIMER12 + bool "Use timer 12 (16-bit)" + depends on !(AM33_2 || AM33_3) + +config MN10300_TTYSM1_TIMER9 + bool "Use timer 9 (16-bit)" + depends on AM33_2 || AM33_3 + +config MN10300_TTYSM1_TIMER3 + bool "Use timer 3 (8-bit)" + depends on AM33_2 || AM33_3 + +endchoice + +# +# /dev/ttySM2 +# +config MN10300_TTYSM2 + bool "Enable SIF2 (/dev/ttySM2)" + depends on MN10300_TTYSM && MN10300_PROC_HAS_TTYSM2 + help + Enable access to SIF2 through /dev/ttySM2 or gdb-stub + +choice + prompt "Select the timer to supply the clock for SIF2" + default MN10300_TTYSM2_TIMER3 \ + if !(AM33_2 || AM33_3) + default MN10300_TTYSM2_TIMER10 \ + if AM33_2 || AM33_3 + depends on MN10300_TTYSM2 + +config MN10300_TTYSM2_TIMER9 + bool "Use timer 9 (16-bit)" + depends on !(AM33_2 || AM33_3) + +config MN10300_TTYSM2_TIMER1 + bool "Use timer 1 (8-bit)" + depends on !(AM33_2 || AM33_3) + +config MN10300_TTYSM2_TIMER3 + bool "Use timer 3 (8-bit)" + depends on !(AM33_2 || AM33_3) + +config MN10300_TTYSM2_TIMER10 + bool "Use timer 10 (16-bit)" + depends on AM33_2 || AM33_3 + +endchoice + +config MN10300_TTYSM2_CTS + bool "Enable the use of the CTS line /dev/ttySM2" + depends on MN10300_TTYSM2 && AM33_2 + +endmenu + +menu "Interrupt request priority options" + +comment "[!] NOTE: A lower number/level indicates a higher priority (0 is highest, 6 is lowest)" + +comment "____Non-maskable interrupt levels____" +comment "The following must be set to a higher priority than local_irq_disable() and on-chip serial" + +config DEBUGGER_IRQ_LEVEL + int "DEBUGGER interrupt priority" + depends on KERNEL_DEBUGGER + range 0 1 if LINUX_CLI_LEVEL = 2 + range 0 2 if LINUX_CLI_LEVEL = 3 + range 0 3 if LINUX_CLI_LEVEL = 4 + range 0 4 if LINUX_CLI_LEVEL = 5 + range 0 5 if LINUX_CLI_LEVEL = 6 + default 0 + +comment "The following must be set to a higher priority than local_irq_disable()" + +config MN10300_SERIAL_IRQ_LEVEL + int "MN10300 on-chip serial interrupt priority" + depends on MN10300_TTYSM + range 1 1 if LINUX_CLI_LEVEL = 2 + range 1 2 if LINUX_CLI_LEVEL = 3 + range 1 3 if LINUX_CLI_LEVEL = 4 + range 1 4 if LINUX_CLI_LEVEL = 5 + range 1 5 if LINUX_CLI_LEVEL = 6 + default 1 + +comment "-" +comment "____Maskable interrupt levels____" + +config LINUX_CLI_LEVEL + int "The highest interrupt priority excluded by local_irq_disable() (2-6)" + range 2 6 + default 2 + help + local_irq_disable() doesn't actually disable maskable interrupts - + what it does is restrict the levels of interrupt which are permitted + (a lower level indicates a higher priority) by lowering the value in + EPSW.IM from 7. Any interrupt is permitted for which the level is + lower than EPSW.IM. + + Certain interrupts, such as DEBUGGER and virtual MN10300 on-chip + serial DMA interrupts are allowed to interrupt normal disabled + sections. + +comment "The following must be set to a equal to or lower priority than LINUX_CLI_LEVEL" + +config TIMER_IRQ_LEVEL + int "Kernel timer interrupt priority" + range LINUX_CLI_LEVEL 6 + default 4 + +config PCI_IRQ_LEVEL + int "PCI interrupt priority" + depends on PCI + range LINUX_CLI_LEVEL 6 + default 5 + +config ETHERNET_IRQ_LEVEL + int "Ethernet interrupt priority" + depends on SMC91X || SMC911X || SMSC911X + range LINUX_CLI_LEVEL 6 + default 6 + +config EXT_SERIAL_IRQ_LEVEL + int "External serial port interrupt priority" + depends on SERIAL_8250 + range LINUX_CLI_LEVEL 6 + default 6 + +endmenu + +source "mm/Kconfig" + +menu "Power management options" +source kernel/power/Kconfig +endmenu + +endmenu + + +menu "Executable formats" + +source "fs/Kconfig.binfmt" + +endmenu + +source "net/Kconfig" + +source "drivers/Kconfig" + +source "fs/Kconfig" + +source "arch/mn10300/Kconfig.debug" + +source "security/Kconfig" + +source "crypto/Kconfig" + +source "lib/Kconfig" diff --git a/arch/mn10300/Kconfig.debug b/arch/mn10300/Kconfig.debug new file mode 100644 index 00000000..bdbfd444 --- /dev/null +++ b/arch/mn10300/Kconfig.debug @@ -0,0 +1,159 @@ +menu "Kernel hacking" + +source "lib/Kconfig.debug" + +config DEBUG_STACKOVERFLOW + bool "Check for stack overflows" + depends on DEBUG_KERNEL + +config DEBUG_DECOMPRESS_KERNEL + bool "Using serial port during decompressing kernel" + depends on DEBUG_KERNEL + default n + help + If you say Y here you will confirm the start and the end of + decompressing Linux seeing "Uncompressing Linux... " and + "Ok, booting the kernel.\n" on console. + +config TEST_MISALIGNMENT_HANDLER + bool "Run tests on the misalignment handler" + depends on DEBUG_KERNEL + default n + help + If you say Y here the kernel will execute a list of misaligned memory + accesses to make sure the misalignment handler deals them with + correctly. If it does not, the kernel will throw a BUG. + +config KPROBES + bool "Kprobes" + depends on DEBUG_KERNEL + help + Kprobes allows you to trap at almost any kernel address and + execute a callback function. register_kprobe() establishes + a probepoint and specifies the callback. Kprobes is useful + for kernel debugging, non-intrusive instrumentation and testing. + If in doubt, say "N". + +config GDBSTUB + bool "Remote GDB kernel debugging" + depends on DEBUG_KERNEL && DEPRECATED + select DEBUG_INFO + select FRAME_POINTER + help + If you say Y here, it will be possible to remotely debug the kernel + using gdb. This enlarges your kernel ELF image disk size by several + megabytes and requires a machine with more than 16 MB, better 32 MB + RAM to avoid excessive linking time. This is only useful for kernel + hackers. If unsure, say N. + + This is deprecated in favour of KGDB and will be removed in a later + version. + +config GDBSTUB_IMMEDIATE + bool "Break into GDB stub immediately" + depends on GDBSTUB + help + If you say Y here, GDB stub will break into the program as soon as + possible, leaving the program counter at the beginning of + start_kernel() in init/main.c. + +config GDBSTUB_ALLOW_SINGLE_STEP + bool "Allow software single-stepping in GDB stub" + depends on GDBSTUB && !SMP && !PREEMPT + help + Allow GDB stub to perform software single-stepping through the + kernel. This doesn't work very well on SMP or preemptible kernels as + it uses temporary breakpoints to emulate single-stepping. + +config GDB_CONSOLE + bool "Console output to GDB" + depends on GDBSTUB + help + If you are using GDB for remote debugging over a serial port and + would like kernel messages to be formatted into GDB $O packets so + that GDB prints them as program output, say 'Y'. + +config GDBSTUB_DEBUGGING + bool "Debug GDB stub by messages to serial port" + depends on GDBSTUB + help + This causes debugging messages to be displayed at various points + during execution of the GDB stub routines. Such messages will be + displayed on ttyS0 if that isn't the GDB stub's port, or ttySM0 + otherwise. + +config GDBSTUB_DEBUG_ENTRY + bool "Debug GDB stub entry" + depends on GDBSTUB_DEBUGGING + help + This option causes information to be displayed about entry to or exit + from the main GDB stub routine. + +config GDBSTUB_DEBUG_PROTOCOL + bool "Debug GDB stub protocol" + depends on GDBSTUB_DEBUGGING + help + This option causes information to be displayed about the GDB remote + protocol messages generated exchanged with GDB. + +config GDBSTUB_DEBUG_IO + bool "Debug GDB stub I/O" + depends on GDBSTUB_DEBUGGING + help + This option causes information to be displayed about GDB stub's + low-level I/O. + +config GDBSTUB_DEBUG_BREAKPOINT + bool "Debug GDB stub breakpoint management" + depends on GDBSTUB_DEBUGGING + help + This option causes information to be displayed about GDB stub's + breakpoint management. + +choice + prompt "GDB stub port" + default GDBSTUB_ON_TTYSM0 + depends on GDBSTUB + help + Select the serial port used for GDB-stub. + +config GDBSTUB_ON_TTYSM0 + bool "/dev/ttySM0 [SIF0]" + depends on MN10300_TTYSM0 + select GDBSTUB_ON_TTYSMx + +config GDBSTUB_ON_TTYSM1 + bool "/dev/ttySM1 [SIF1]" + depends on MN10300_TTYSM1 + select GDBSTUB_ON_TTYSMx + +config GDBSTUB_ON_TTYSM2 + bool "/dev/ttySM2 [SIF2]" + depends on MN10300_TTYSM2 + select GDBSTUB_ON_TTYSMx + +config GDBSTUB_ON_TTYS0 + bool "/dev/ttyS0" + select GDBSTUB_ON_TTYSx + +config GDBSTUB_ON_TTYS1 + bool "/dev/ttyS1" + select GDBSTUB_ON_TTYSx + +endchoice + +config GDBSTUB_ON_TTYSMx + bool + depends on GDBSTUB_ON_TTYSM0 || GDBSTUB_ON_TTYSM1 || GDBSTUB_ON_TTYSM2 + default y + +config GDBSTUB_ON_TTYSx + bool + depends on GDBSTUB_ON_TTYS0 || GDBSTUB_ON_TTYS1 + default y + +endmenu + +config KERNEL_DEBUGGER + def_bool y + depends on GDBSTUB || KGDB diff --git a/arch/mn10300/Makefile b/arch/mn10300/Makefile new file mode 100644 index 00000000..7120282b --- /dev/null +++ b/arch/mn10300/Makefile @@ -0,0 +1,107 @@ +############################################################################### +# +# MN10300 Kernel makefile system specifications +# +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Modified by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### + +KBUILD_DEFCONFIG := asb2303_defconfig + +CCSPECS := $(shell $(CC) -v 2>&1 | grep "^Reading specs from " | head -1 | cut -c20-) +CCDIR := $(strip $(patsubst %/specs,%,$(CCSPECS))) +KBUILD_CPPFLAGS += -nostdinc -I$(CCDIR)/include + +LDFLAGS := +OBJCOPYFLAGS := -O binary -R .note -R .comment -R .GCC-command-line -R .note.gnu.build-id -S +#LDFLAGS_vmlinux := -Map linkmap.txt +CHECKFLAGS += + +PROCESSOR := unset +UNIT := unset + +KBUILD_CFLAGS += -mam33 -mmem-funcs -DCPU=AM33 +KBUILD_AFLAGS += -mam33 -DCPU=AM33 + +ifeq ($(CONFIG_MN10300_CURRENT_IN_E2),y) +KBUILD_CFLAGS += -ffixed-e2 -fcall-saved-e5 +endif + +ifeq ($(CONFIG_MN10300_PROC_MN103E010),y) +PROCESSOR := mn103e010 +endif +ifeq ($(CONFIG_MN10300_PROC_MN2WS0050),y) +PROCESSOR := mn2ws0050 +endif + +ifeq ($(CONFIG_MN10300_UNIT_ASB2303),y) +UNIT := asb2303 +endif +ifeq ($(CONFIG_MN10300_UNIT_ASB2305),y) +UNIT := asb2305 +endif +ifeq ($(CONFIG_MN10300_UNIT_ASB2364),y) +UNIT := asb2364 +endif + + +head-y := arch/mn10300/kernel/head.o arch/mn10300/kernel/init_task.o + +core-y += arch/mn10300/kernel/ arch/mn10300/mm/ + +ifneq ($(PROCESSOR),unset) +core-y += arch/mn10300/proc-$(PROCESSOR)/ +endif +ifneq ($(UNIT),unset) +core-y += arch/mn10300/unit-$(UNIT)/ +endif +libs-y += arch/mn10300/lib/ + +drivers-$(CONFIG_OPROFILE) += arch/mn10300/oprofile/ + +boot := arch/mn10300/boot + +.PHONY: zImage + +KBUILD_IMAGE := $(boot)/zImage +CLEAN_FILES += $(boot)/zImage +CLEAN_FILES += $(boot)/compressed/vmlinux +CLEAN_FILES += $(boot)/compressed/vmlinux.bin +CLEAN_FILES += $(boot)/compressed/vmlinux.bin.gz + +zImage: vmlinux + $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ + +all: zImage + +bootstrap: + $(Q)$(MAKEBOOT) bootstrap + +archclean: + $(Q)$(MAKE) $(clean)=arch/mn10300/proc-mn103e010 + $(Q)$(MAKE) $(clean)=arch/mn10300/unit-asb2303 + $(Q)$(MAKE) $(clean)=arch/mn10300/unit-asb2305 + +define archhelp + echo '* zImage - Compressed kernel image (arch/$(ARCH)/boot/zImage)' +endef + +# If you make sure the .S files get compiled with debug info, +# uncomment the following to disable optimisations +# that are unhelpful whilst debugging. +ifdef CONFIG_DEBUG_INFO +#KBUILD_CFLAGS += -O1 +KBUILD_AFLAGS += -Wa,--gdwarf2 +endif + +# +# include the appropriate processor- and unit-specific headers +# +KBUILD_CPPFLAGS += -I$(srctree)/arch/mn10300/proc-$(PROCESSOR)/include +KBUILD_CPPFLAGS += -I$(srctree)/arch/mn10300/unit-$(UNIT)/include diff --git a/arch/mn10300/boot/.gitignore b/arch/mn10300/boot/.gitignore new file mode 100644 index 00000000..b6718de2 --- /dev/null +++ b/arch/mn10300/boot/.gitignore @@ -0,0 +1 @@ +zImage diff --git a/arch/mn10300/boot/Makefile b/arch/mn10300/boot/Makefile new file mode 100644 index 00000000..36c9caf8 --- /dev/null +++ b/arch/mn10300/boot/Makefile @@ -0,0 +1,28 @@ +# MN10300 kernel compressor and wrapper +# +# Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# + +targets := vmlinux.bin zImage + +subdir- := compressed + +# --------------------------------------------------------------------------- + + +$(obj)/zImage: $(obj)/compressed/vmlinux FORCE + $(call if_changed,objcopy) + @echo 'Kernel: $@ is ready' + +$(obj)/vmlinux.bin: $(obj)/compressed/vmlinux FORCE + $(call if_changed,objcopy) + +$(obj)/compressed/vmlinux: FORCE + $(Q)$(MAKE) $(build)=$(obj)/compressed IMAGE_OFFSET=$(IMAGE_OFFSET) $@ diff --git a/arch/mn10300/boot/compressed/Makefile b/arch/mn10300/boot/compressed/Makefile new file mode 100644 index 00000000..08a95e17 --- /dev/null +++ b/arch/mn10300/boot/compressed/Makefile @@ -0,0 +1,22 @@ +# +# Create a compressed vmlinux image from the original vmlinux +# + +targets := vmlinux vmlinux.bin vmlinux.bin.gz head.o misc.o piggy.o + +LDFLAGS_vmlinux := -Ttext $(CONFIG_KERNEL_ZIMAGE_BASE_ADDRESS) -e startup_32 + +$(obj)/vmlinux: $(obj)/head.o $(obj)/misc.o $(obj)/piggy.o FORCE + $(call if_changed,ld) + @: + +$(obj)/vmlinux.bin: vmlinux FORCE + $(call if_changed,objcopy) + +$(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin FORCE + $(call if_changed,gzip) + +LDFLAGS_piggy.o := -r --format binary --oformat elf32-am33lin -T + +$(obj)/piggy.o: $(obj)/vmlinux.lds $(obj)/vmlinux.bin.gz FORCE + $(call if_changed,ld) diff --git a/arch/mn10300/boot/compressed/head.S b/arch/mn10300/boot/compressed/head.S new file mode 100644 index 00000000..7b50345b --- /dev/null +++ b/arch/mn10300/boot/compressed/head.S @@ -0,0 +1,151 @@ +/* Boot entry point for a compressed MN10300 kernel + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + .section .text + +#define DEBUG + +#include <linux/linkage.h> +#include <asm/cpu-regs.h> +#include <asm/cache.h> +#ifdef CONFIG_SMP +#include <proc/smp-regs.h> +#endif + + .globl startup_32 +startup_32: +#ifdef CONFIG_SMP + # + # Secondary CPUs jump directly to the kernel entry point + # + # Must save primary CPU's D0-D2 registers as they hold boot parameters + # + mov (CPUID), d3 + and CPUID_MASK,d3 + beq startup_primary + mov CONFIG_KERNEL_TEXT_ADDRESS,a0 + jmp (a0) + +startup_primary: +#endif /* CONFIG_SMP */ + + # first save parameters from bootloader + mov param_save_area,a0 + mov d0,(a0) + mov d1,(4,a0) + mov d2,(8,a0) + + mov sp,a3 + mov decomp_stack+0x2000-4,a0 + mov a0,sp + + # invalidate and enable both of the caches + mov CHCTR,a0 + clr d0 + movhu d0,(a0) # turn off first + mov CHCTR_ICINV|CHCTR_DCINV,d0 + movhu d0,(a0) + setlb + mov (a0),d0 + btst CHCTR_ICBUSY|CHCTR_DCBUSY,d0 # wait till not busy + lne + +#ifdef CONFIG_MN10300_CACHE_ENABLED +#ifdef CONFIG_MN10300_CACHE_WBACK + mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD_WRBACK,d0 +#else + mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD_WRTHROUGH,d0 +#endif /* WBACK */ + movhu d0,(a0) # enable +#endif /* !ENABLED */ + + # clear the BSS area + mov __bss_start,a0 + mov _end,a1 + clr d0 +bssclear: + cmp a1,a0 + bge bssclear_end + movbu d0,(a0) + inc a0 + bra bssclear +bssclear_end: + + # decompress the kernel + call decompress_kernel[],0 +#ifdef CONFIG_MN10300_CACHE_WBACK + call mn10300_dcache_flush_inv[],0 +#endif + + # disable caches again + mov CHCTR,a0 + clr d0 + movhu d0,(a0) + setlb + mov (a0),d0 + btst CHCTR_ICBUSY|CHCTR_DCBUSY,d0 # wait till not busy + lne + + mov param_save_area,a0 + mov (a0),d0 + mov (4,a0),d1 + mov (8,a0),d2 + + # jump to the kernel proper entry point + mov a3,sp + mov CONFIG_KERNEL_TEXT_ADDRESS,a0 + jmp (a0) + + +############################################################################### +# +# Cache flush routines +# +############################################################################### +#ifdef CONFIG_MN10300_CACHE_WBACK +mn10300_dcache_flush_inv: + movhu (CHCTR),d0 + btst CHCTR_DCEN,d0 + beq mn10300_dcache_flush_inv_end + + mov L1_CACHE_NENTRIES,d1 + clr a1 + +mn10300_dcache_flush_inv_loop: + mov (DCACHE_PURGE_WAY0(0),a1),d0 # unconditional purge + mov (DCACHE_PURGE_WAY1(0),a1),d0 # unconditional purge + mov (DCACHE_PURGE_WAY2(0),a1),d0 # unconditional purge + mov (DCACHE_PURGE_WAY3(0),a1),d0 # unconditional purge + + add L1_CACHE_BYTES,a1 + add -1,d1 + bne mn10300_dcache_flush_inv_loop + +mn10300_dcache_flush_inv_end: + ret [],0 +#endif /* CONFIG_MN10300_CACHE_WBACK */ + + +############################################################################### +# +# Data areas +# +############################################################################### + .data + .align 4 +param_save_area: + .rept 3 + .word 0 + .endr + + .section .bss + .align 4 +decomp_stack: + .space 0x2000 diff --git a/arch/mn10300/boot/compressed/misc.c b/arch/mn10300/boot/compressed/misc.c new file mode 100644 index 00000000..42cbd77b --- /dev/null +++ b/arch/mn10300/boot/compressed/misc.c @@ -0,0 +1,393 @@ +/* MN10300 Miscellaneous helper routines for kernel decompressor + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Modified by David Howells (dhowells@redhat.com) + * - Derived from arch/x86/boot/compressed/misc_32.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/compiler.h> +#include <asm/serial-regs.h> +#include "misc.h" + +#ifndef CONFIG_GDBSTUB_ON_TTYSx +/* display 'Uncompressing Linux... ' messages on ttyS0 or ttyS1 */ +#if 1 /* ttyS0 */ +#define CYG_DEV_BASE 0xA6FB0000 +#else /* ttyS1 */ +#define CYG_DEV_BASE 0xA6FC0000 +#endif + +#define CYG_DEV_THR (*((volatile __u8*)(CYG_DEV_BASE + 0x00))) +#define CYG_DEV_MCR (*((volatile __u8*)(CYG_DEV_BASE + 0x10))) +#define SIO_MCR_DTR 0x01 +#define SIO_MCR_RTS 0x02 +#define CYG_DEV_LSR (*((volatile __u8*)(CYG_DEV_BASE + 0x14))) +#define SIO_LSR_THRE 0x20 /* transmitter holding register empty */ +#define SIO_LSR_TEMT 0x40 /* transmitter register empty */ +#define CYG_DEV_MSR (*((volatile __u8*)(CYG_DEV_BASE + 0x18))) +#define SIO_MSR_CTS 0x10 /* clear to send */ +#define SIO_MSR_DSR 0x20 /* data set ready */ + +#define LSR_WAIT_FOR(STATE) \ + do { while (!(CYG_DEV_LSR & SIO_LSR_##STATE)) {} } while (0) +#define FLOWCTL_QUERY(LINE) \ + ({ CYG_DEV_MSR & SIO_MSR_##LINE; }) +#define FLOWCTL_WAIT_FOR(LINE) \ + do { while (!(CYG_DEV_MSR & SIO_MSR_##LINE)) {} } while (0) +#define FLOWCTL_CLEAR(LINE) \ + do { CYG_DEV_MCR &= ~SIO_MCR_##LINE; } while (0) +#define FLOWCTL_SET(LINE) \ + do { CYG_DEV_MCR |= SIO_MCR_##LINE; } while (0) +#endif + +/* + * gzip declarations + */ + +#define OF(args) args +#define STATIC static + +#undef memset +#undef memcpy + +static inline void *memset(const void *s, int c, size_t n) +{ + int i; + char *ss = (char *) s; + + for (i = 0; i < n; i++) + ss[i] = c; + return (void *)s; +} + +#define memzero(s, n) memset((s), 0, (n)) + +static inline void *memcpy(void *__dest, const void *__src, size_t __n) +{ + int i; + const char *s = __src; + char *d = __dest; + + for (i = 0; i < __n; i++) + d[i] = s[i]; + return __dest; +} + +typedef unsigned char uch; +typedef unsigned short ush; +typedef unsigned long ulg; + +#define WSIZE 0x8000 /* Window size must be at least 32k, and a power of + * two */ + +static uch *inbuf; /* input buffer */ +static uch window[WSIZE]; /* sliding window buffer */ + +static unsigned insize; /* valid bytes in inbuf */ +static unsigned inptr; /* index of next byte to be processed in inbuf */ +static unsigned outcnt; /* bytes in output buffer */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */ +#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ +#define RESERVED 0xC0 /* bit 6,7: reserved */ + +/* Diagnostic functions */ +#ifdef DEBUG +# define Assert(cond, msg) { if (!(cond)) error(msg); } +# define Trace(x) fprintf x +# define Tracev(x) { if (verbose) fprintf x ; } +# define Tracevv(x) { if (verbose > 1) fprintf x ; } +# define Tracec(c, x) { if (verbose && (c)) fprintf x ; } +# define Tracecv(c, x) { if (verbose > 1 && (c)) fprintf x ; } +#else +# define Assert(cond, msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c, x) +# define Tracecv(c, x) +#endif + +static int fill_inbuf(void); +static void flush_window(void); +static void error(const char *) __attribute__((noreturn)); +static void kputs(const char *); + +static inline unsigned char get_byte(void) +{ + unsigned char ch = inptr < insize ? inbuf[inptr++] : fill_inbuf(); + +#if 0 + char hex[3]; + hex[0] = ((ch & 0x0f) > 9) ? + ((ch & 0x0f) + 'A' - 0xa) : ((ch & 0x0f) + '0'); + hex[1] = ((ch >> 4) > 9) ? + ((ch >> 4) + 'A' - 0xa) : ((ch >> 4) + '0'); + hex[2] = 0; + kputs(hex); +#endif + return ch; +} + +/* + * This is set up by the setup-routine at boot-time + */ +#define EXT_MEM_K (*(unsigned short *)0x90002) +#ifndef STANDARD_MEMORY_BIOS_CALL +#define ALT_MEM_K (*(unsigned long *) 0x901e0) +#endif +#define SCREEN_INFO (*(struct screen_info *)0x90000) + +static long bytes_out; +static uch *output_data; +static unsigned long output_ptr; + + +static unsigned long free_mem_ptr = (unsigned long) &end; +static unsigned long free_mem_end_ptr = (unsigned long) &end + 0x90000; + +#define INPLACE_MOVE_ROUTINE 0x1000 +#define LOW_BUFFER_START 0x2000 +#define LOW_BUFFER_END 0x90000 +#define LOW_BUFFER_SIZE (LOW_BUFFER_END - LOW_BUFFER_START) +#define HEAP_SIZE 0x3000 +static int high_loaded; +static uch *high_buffer_start /* = (uch *)(((ulg)&end) + HEAP_SIZE)*/; + +static char *vidmem = (char *)0xb8000; +static int lines, cols; + +#define BOOTLOADER_INFLATE +#include "../../../../lib/inflate.c" + +static inline void scroll(void) +{ + int i; + + memcpy(vidmem, vidmem + cols * 2, (lines - 1) * cols * 2); + for (i = (lines - 1) * cols * 2; i < lines * cols * 2; i += 2) + vidmem[i] = ' '; +} + +static inline void kputchar(unsigned char ch) +{ +#ifdef CONFIG_MN10300_UNIT_ASB2305 + while (SC0STR & SC01STR_TBF) + continue; + + if (ch == 0x0a) { + SC0TXB = 0x0d; + while (SC0STR & SC01STR_TBF) + continue; + } + + SC0TXB = ch; + +#else + while (SC1STR & SC01STR_TBF) + continue; + + if (ch == 0x0a) { + SC1TXB = 0x0d; + while (SC1STR & SC01STR_TBF) + continue; + } + + SC1TXB = ch; + +#endif +} + +static void kputs(const char *s) +{ +#ifdef CONFIG_DEBUG_DECOMPRESS_KERNEL +#ifndef CONFIG_GDBSTUB_ON_TTYSx + char ch; + + FLOWCTL_SET(DTR); + + while (*s) { + LSR_WAIT_FOR(THRE); + + ch = *s++; + if (ch == 0x0a) { + CYG_DEV_THR = 0x0d; + LSR_WAIT_FOR(THRE); + } + CYG_DEV_THR = ch; + } + + FLOWCTL_CLEAR(DTR); +#else + + for (; *s; s++) + kputchar(*s); + +#endif +#endif /* CONFIG_DEBUG_DECOMPRESS_KERNEL */ +} + +/* =========================================================================== + * Fill the input buffer. This is called only when the buffer is empty + * and at least one byte is really needed. + */ +static int fill_inbuf() +{ + if (insize != 0) + error("ran out of input data\n"); + + inbuf = input_data; + insize = input_len; + inptr = 1; + return inbuf[0]; +} + +/* =========================================================================== + * Write the output window window[0..outcnt-1] and update crc and bytes_out. + * (Used for the decompressed data only.) + */ +static void flush_window_low(void) +{ + ulg c = crc; /* temporary variable */ + unsigned n; + uch *in, *out, ch; + + in = window; + out = &output_data[output_ptr]; + for (n = 0; n < outcnt; n++) { + ch = *out++ = *in++; + c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); + } + crc = c; + bytes_out += (ulg)outcnt; + output_ptr += (ulg)outcnt; + outcnt = 0; +} + +static void flush_window_high(void) +{ + ulg c = crc; /* temporary variable */ + unsigned n; + uch *in, ch; + in = window; + for (n = 0; n < outcnt; n++) { + ch = *output_data++ = *in++; + if ((ulg) output_data == LOW_BUFFER_END) + output_data = high_buffer_start; + c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); + } + crc = c; + bytes_out += (ulg)outcnt; + outcnt = 0; +} + +static void flush_window(void) +{ + if (high_loaded) + flush_window_high(); + else + flush_window_low(); +} + +static void error(const char *x) +{ + kputs("\n\n"); + kputs(x); + kputs("\n\n -- System halted"); + + while (1) + /* Halt */; +} + +#define STACK_SIZE (4096) + +long user_stack[STACK_SIZE]; + +struct { + long *a; + short b; +} stack_start = { &user_stack[STACK_SIZE], 0 }; + +void setup_normal_output_buffer(void) +{ +#ifdef STANDARD_MEMORY_BIOS_CALL + if (EXT_MEM_K < 1024) + error("Less than 2MB of memory.\n"); +#else + if ((ALT_MEM_K > EXT_MEM_K ? ALT_MEM_K : EXT_MEM_K) < 1024) + error("Less than 2MB of memory.\n"); +#endif + output_data = (char *) 0x100000; /* Points to 1M */ +} + +struct moveparams { + uch *low_buffer_start; + int lcount; + uch *high_buffer_start; + int hcount; +}; + +void setup_output_buffer_if_we_run_high(struct moveparams *mv) +{ + high_buffer_start = (uch *)(((ulg) &end) + HEAP_SIZE); +#ifdef STANDARD_MEMORY_BIOS_CALL + if (EXT_MEM_K < (3 * 1024)) + error("Less than 4MB of memory.\n"); +#else + if ((ALT_MEM_K > EXT_MEM_K ? ALT_MEM_K : EXT_MEM_K) < (3 * 1024)) + error("Less than 4MB of memory.\n"); +#endif + mv->low_buffer_start = output_data = (char *) LOW_BUFFER_START; + high_loaded = 1; + free_mem_end_ptr = (long) high_buffer_start; + if (0x100000 + LOW_BUFFER_SIZE > (ulg) high_buffer_start) { + high_buffer_start = (uch *)(0x100000 + LOW_BUFFER_SIZE); + mv->hcount = 0; /* say: we need not to move high_buffer */ + } else { + mv->hcount = -1; + } + mv->high_buffer_start = high_buffer_start; +} + +void close_output_buffer_if_we_run_high(struct moveparams *mv) +{ + mv->lcount = bytes_out; + if (bytes_out > LOW_BUFFER_SIZE) { + mv->lcount = LOW_BUFFER_SIZE; + if (mv->hcount) + mv->hcount = bytes_out - LOW_BUFFER_SIZE; + } else { + mv->hcount = 0; + } +} + +#undef DEBUGFLAG +#ifdef DEBUGFLAG +int debugflag; +#endif + +int decompress_kernel(struct moveparams *mv) +{ +#ifdef DEBUGFLAG + while (!debugflag) + barrier(); +#endif + + output_data = (char *) CONFIG_KERNEL_TEXT_ADDRESS; + + makecrc(); + kputs("Uncompressing Linux... "); + gunzip(); + kputs("Ok, booting the kernel.\n"); + return 0; +} diff --git a/arch/mn10300/boot/compressed/misc.h b/arch/mn10300/boot/compressed/misc.h new file mode 100644 index 00000000..da921cd1 --- /dev/null +++ b/arch/mn10300/boot/compressed/misc.h @@ -0,0 +1,18 @@ +/* Internal definitions for the MN10300 kernel decompressor + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +extern int end; + +/* + * vmlinux.lds + */ +extern char input_data[]; +extern int input_len; diff --git a/arch/mn10300/boot/compressed/vmlinux.lds b/arch/mn10300/boot/compressed/vmlinux.lds new file mode 100644 index 00000000..a0849036 --- /dev/null +++ b/arch/mn10300/boot/compressed/vmlinux.lds @@ -0,0 +1,9 @@ +SECTIONS +{ + .data : { + input_len = .; + LONG(input_data_end - input_data) input_data = .; + *(.data) + input_data_end = .; + } +} diff --git a/arch/mn10300/boot/install.sh b/arch/mn10300/boot/install.sh new file mode 100644 index 00000000..abba3097 --- /dev/null +++ b/arch/mn10300/boot/install.sh @@ -0,0 +1,67 @@ +#!/bin/sh +# +# arch/mn10300/boot/install -c.sh +# +# This file is subject to the terms and conditions of the GNU General Public +# Licence. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 1995 by Linus Torvalds +# +# Adapted from code in arch/i386/boot/Makefile by H. Peter Anvin +# +# "make install -c" script for i386 architecture +# +# Arguments: +# $1 - kernel version +# $2 - kernel image file +# $3 - kernel map file +# $4 - default install -c path (blank if root directory) +# $5 - boot rom file +# + +# User may have a custom install -c script + +rm -fr $4/../usr/include/linux $4/../usr/include/asm +install -c -m 0755 $2 $4/vmlinuz +install -c -m 0755 $5 $4/boot.rom +install -c -m 0755 -d $4/../usr/include/linux +cd ${srctree}/include/linux +for i in `find . -maxdepth 1 -name '*.h' -print`; do + install -c -m 0644 $i $4/../usr/include/linux +done +install -c -m 0755 -d $4/../usr/include/linux/byteorder +cd ${srctree}/include/linux/byteorder +for i in `find . -name '*.h' -print`; do + install -c -m 0644 $i $4/../usr/include/linux/byteorder +done +install -c -m 0755 -d $4/../usr/include/linux/lockd +cd ${srctree}/include/linux/lockd +for i in `find . -name '*.h' -print`; do + install -c -m 0644 $i $4/../usr/include/linux/lockd +done +install -c -m 0755 -d $4/../usr/include/linux/netfilter_ipv4 +cd ${srctree}/include/linux/netfilter_ipv4 +for i in `find . -name '*.h' -print`; do + install -c -m 0644 $i $4/../usr/include/linux/netfilter_ipv4 +done +install -c -m 0755 -d $4/../usr/include/linux/nfsd +cd ${srctree}/include/linux/nfsd +for i in `find . -name '*.h' -print`; do + install -c -m 0644 $i $4/../usr/include/linux/nfsd/$i +done +install -c -m 0755 -d $4/../usr/include/linux/raid +cd ${srctree}/include/linux/raid +for i in `find . -name '*.h' -print`; do + install -c -m 0644 $i $4/../usr/include/linux/raid +done +install -c -m 0755 -d $4/../usr/include/linux/sunrpc +cd ${srctree}/include/linux/sunrpc +for i in `find . -name '*.h' -print`; do + install -c -m 0644 $i $4/../usr/include/linux/sunrpc +done +install -c -m 0755 -d $4/../usr/include/asm +cd ${srctree}/include/asm +for i in `find . -name '*.h' -print`; do + install -c -m 0644 $i $4/../usr/include/asm +done diff --git a/arch/mn10300/boot/tools/build.c b/arch/mn10300/boot/tools/build.c new file mode 100644 index 00000000..4f552ead --- /dev/null +++ b/arch/mn10300/boot/tools/build.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 1997 Martin Mares + */ + +/* + * This file builds a disk-image from three different files: + * + * - bootsect: exactly 512 bytes of 8086 machine code, loads the rest + * - setup: 8086 machine code, sets up system parm + * - system: 80386 code for actual system + * + * It does some checking that all files are of the correct type, and + * just writes the result to stdout, removing headers and padding to + * the right amount. It also writes some system data to stderr. + */ + +/* + * Changes by tytso to allow root device specification + * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996 + * Cross compiling fixes by Gertjan van Wingerde, July 1996 + * Rewritten by Martin Mares, April 1997 + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include <unistd.h> +#include <fcntl.h> +#include <asm/boot.h> + +#define DEFAULT_MAJOR_ROOT 0 +#define DEFAULT_MINOR_ROOT 0 + +/* Minimal number of setup sectors (see also bootsect.S) */ +#define SETUP_SECTS 4 + +uint8_t buf[1024]; +int fd; +int is_big_kernel; + +__attribute__((noreturn)) +void die(const char *str, ...) +{ + va_list args; + va_start(args, str); + vfprintf(stderr, str, args); + fputc('\n', stderr); + exit(1); +} + +void file_open(const char *name) +{ + fd = open(name, O_RDONLY, 0); + if (fd < 0) + die("Unable to open `%s': %m", name); +} + +__attribute__((noreturn)) +void usage(void) +{ + die("Usage: build [-b] bootsect setup system [rootdev] [> image]"); +} + +int main(int argc, char **argv) +{ + unsigned int i, c, sz, setup_sectors; + uint32_t sys_size; + uint8_t major_root, minor_root; + struct stat sb; + + if (argc > 2 && !strcmp(argv[1], "-b")) { + is_big_kernel = 1; + argc--, argv++; + } + if ((argc < 4) || (argc > 5)) + usage(); + if (argc > 4) { + if (!strcmp(argv[4], "CURRENT")) { + if (stat("/", &sb)) { + perror("/"); + die("Couldn't stat /"); + } + major_root = major(sb.st_dev); + minor_root = minor(sb.st_dev); + } else if (strcmp(argv[4], "FLOPPY")) { + if (stat(argv[4], &sb)) { + perror(argv[4]); + die("Couldn't stat root device."); + } + major_root = major(sb.st_rdev); + minor_root = minor(sb.st_rdev); + } else { + major_root = 0; + minor_root = 0; + } + } else { + major_root = DEFAULT_MAJOR_ROOT; + minor_root = DEFAULT_MINOR_ROOT; + } + fprintf(stderr, "Root device is (%d, %d)\n", major_root, minor_root); + + file_open(argv[1]); + i = read(fd, buf, sizeof(buf)); + fprintf(stderr, "Boot sector %d bytes.\n", i); + if (i != 512) + die("Boot block must be exactly 512 bytes"); + if (buf[510] != 0x55 || buf[511] != 0xaa) + die("Boot block hasn't got boot flag (0xAA55)"); + buf[508] = minor_root; + buf[509] = major_root; + if (write(1, buf, 512) != 512) + die("Write call failed"); + close(fd); + + /* Copy the setup code */ + file_open(argv[2]); + for (i = 0; (c = read(fd, buf, sizeof(buf))) > 0; i += c) + if (write(1, buf, c) != c) + die("Write call failed"); + if (c != 0) + die("read-error on `setup'"); + close(fd); + + /* Pad unused space with zeros */ + setup_sectors = (i + 511) / 512; + /* for compatibility with ancient versions of LILO. */ + if (setup_sectors < SETUP_SECTS) + setup_sectors = SETUP_SECTS; + fprintf(stderr, "Setup is %d bytes.\n", i); + memset(buf, 0, sizeof(buf)); + while (i < setup_sectors * 512) { + c = setup_sectors * 512 - i; + if (c > sizeof(buf)) + c = sizeof(buf); + if (write(1, buf, c) != c) + die("Write call failed"); + i += c; + } + + file_open(argv[3]); + if (fstat(fd, &sb)) + die("Unable to stat `%s': %m", argv[3]); + sz = sb.st_size; + fprintf(stderr, "System is %d kB\n", sz / 1024); + sys_size = (sz + 15) / 16; + /* 0x28000*16 = 2.5 MB, conservative estimate for the current maximum */ + if (sys_size > (is_big_kernel ? 0x28000 : DEF_SYSSIZE)) + die("System is too big. Try using %smodules.", + is_big_kernel ? "" : "bzImage or "); + if (sys_size > 0xffff) + fprintf(stderr, + "warning: kernel is too big for standalone boot " + "from floppy\n"); + while (sz > 0) { + int l, n; + + l = (sz > sizeof(buf)) ? sizeof(buf) : sz; + n = read(fd, buf, l); + if (n != l) { + if (n < 0) + die("Error reading %s: %m", argv[3]); + else + die("%s: Unexpected EOF", argv[3]); + } + if (write(1, buf, l) != l) + die("Write failed"); + sz -= l; + } + close(fd); + + /* Write sizes to the bootsector */ + if (lseek(1, 497, SEEK_SET) != 497) + die("Output: seek failed"); + buf[0] = setup_sectors; + if (write(1, buf, 1) != 1) + die("Write of setup sector count failed"); + if (lseek(1, 500, SEEK_SET) != 500) + die("Output: seek failed"); + buf[0] = (sys_size & 0xff); + buf[1] = ((sys_size >> 8) & 0xff); + if (write(1, buf, 2) != 2) + die("Write of image length failed"); + + return 0; +} diff --git a/arch/mn10300/configs/asb2303_defconfig b/arch/mn10300/configs/asb2303_defconfig new file mode 100644 index 00000000..1fd41ec1 --- /dev/null +++ b/arch/mn10300/configs/asb2303_defconfig @@ -0,0 +1,73 @@ +CONFIG_EXPERIMENTAL=y +CONFIG_SYSVIPC=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_TINY_RCU=y +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_EXPERT=y +# CONFIG_KALLSYMS is not set +# CONFIG_HOTPLUG is not set +# CONFIG_VM_EVENT_COUNTERS is not set +CONFIG_SLAB=y +CONFIG_PROFILING=y +# CONFIG_BLOCK is not set +CONFIG_PREEMPT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_MN10300_RTC=y +CONFIG_MN10300_TTYSM_CONSOLE=y +CONFIG_MN10300_TTYSM0=y +CONFIG_MN10300_TTYSM1=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_BOOTP=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +# CONFIG_INET_DIAG is not set +# CONFIG_IPV6 is not set +# CONFIG_WIRELESS is not set +CONFIG_MTD=y +CONFIG_MTD_DEBUG=y +CONFIG_MTD_PARTITIONS=y +CONFIG_MTD_REDBOOT_PARTS=y +CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED=y +CONFIG_MTD_CHAR=y +CONFIG_MTD_CFI=y +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_GEOMETRY=y +CONFIG_MTD_CFI_I4=y +CONFIG_MTD_CFI_AMDSTD=y +CONFIG_MTD_PHYSMAP=y +CONFIG_NETDEVICES=y +CONFIG_NET_ETHERNET=y +CONFIG_SMC91X=y +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set +# CONFIG_WLAN is not set +# CONFIG_INPUT is not set +# CONFIG_SERIO is not set +# CONFIG_VT is not set +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +# CONFIG_HW_RANDOM is not set +CONFIG_RTC=y +# CONFIG_HWMON is not set +# CONFIG_USB_SUPPORT is not set +CONFIG_PROC_KCORE=y +# CONFIG_PROC_PAGE_MONITOR is not set +CONFIG_TMPFS=y +CONFIG_JFFS2_FS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +CONFIG_ROOT_NFS=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_STRIP_ASM_SYMS=y diff --git a/arch/mn10300/configs/asb2364_defconfig b/arch/mn10300/configs/asb2364_defconfig new file mode 100644 index 00000000..fbb96ae3 --- /dev/null +++ b/arch/mn10300/configs/asb2364_defconfig @@ -0,0 +1,97 @@ +CONFIG_EXPERIMENTAL=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_CGROUPS=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +CONFIG_RELAY=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_EXPERT=y +# CONFIG_KALLSYMS is not set +# CONFIG_VM_EVENT_COUNTERS is not set +CONFIG_SLAB=y +CONFIG_PROFILING=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_BLOCK is not set +CONFIG_MN10300_UNIT_ASB2364=y +CONFIG_PREEMPT=y +# CONFIG_MN10300_USING_JTAG is not set +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_MN10300_TTYSM_CONSOLE=y +CONFIG_MN10300_TTYSM0=y +CONFIG_MN10300_TTYSM0_TIMER2=y +CONFIG_MN10300_TTYSM1=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_BOOTP=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +# CONFIG_INET_DIAG is not set +CONFIG_IPV6=y +# CONFIG_INET6_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET6_XFRM_MODE_TUNNEL is not set +# CONFIG_INET6_XFRM_MODE_BEET is not set +# CONFIG_FIRMWARE_IN_KERNEL is not set +CONFIG_CONNECTOR=y +CONFIG_MTD=y +CONFIG_MTD_DEBUG=y +CONFIG_MTD_PARTITIONS=y +CONFIG_MTD_REDBOOT_PARTS=y +CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED=y +CONFIG_MTD_CHAR=y +CONFIG_MTD_CFI=y +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_GEOMETRY=y +CONFIG_MTD_CFI_I4=y +CONFIG_MTD_CFI_AMDSTD=y +CONFIG_MTD_PHYSMAP=y +CONFIG_NETDEVICES=y +CONFIG_NET_ETHERNET=y +CONFIG_SMSC911X=y +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_SERIO is not set +# CONFIG_VT is not set +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +# CONFIG_HW_RANDOM is not set +# CONFIG_HWMON is not set +# CONFIG_HID_SUPPORT is not set +# CONFIG_USB_SUPPORT is not set +CONFIG_PROC_KCORE=y +# CONFIG_PROC_PAGE_MONITOR is not set +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_JFFS2_FS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +CONFIG_ROOT_NFS=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_STRIP_ASM_SYMS=y +CONFIG_DEBUG_KERNEL=y +CONFIG_DETECT_HUNG_TASK=y +# CONFIG_DEBUG_BUGVERBOSE is not set +CONFIG_DEBUG_INFO=y +# CONFIG_RCU_CPU_STALL_DETECTOR is not set diff --git a/arch/mn10300/include/asm/Kbuild b/arch/mn10300/include/asm/Kbuild new file mode 100644 index 00000000..c68e1680 --- /dev/null +++ b/arch/mn10300/include/asm/Kbuild @@ -0,0 +1 @@ +include include/asm-generic/Kbuild.asm diff --git a/arch/mn10300/include/asm/asm-offsets.h b/arch/mn10300/include/asm/asm-offsets.h new file mode 100644 index 00000000..d370ee36 --- /dev/null +++ b/arch/mn10300/include/asm/asm-offsets.h @@ -0,0 +1 @@ +#include <generated/asm-offsets.h> diff --git a/arch/mn10300/include/asm/atomic.h b/arch/mn10300/include/asm/atomic.h new file mode 100644 index 00000000..9d773a63 --- /dev/null +++ b/arch/mn10300/include/asm/atomic.h @@ -0,0 +1,351 @@ +/* MN10300 Atomic counter operations + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_ATOMIC_H +#define _ASM_ATOMIC_H + +#include <asm/irqflags.h> + +#ifndef __ASSEMBLY__ + +#ifdef CONFIG_SMP +#ifdef CONFIG_MN10300_HAS_ATOMIC_OPS_UNIT +static inline +unsigned long __xchg(volatile unsigned long *m, unsigned long val) +{ + unsigned long status; + unsigned long oldval; + + asm volatile( + "1: mov %4,(_AAR,%3) \n" + " mov (_ADR,%3),%1 \n" + " mov %5,(_ADR,%3) \n" + " mov (_ADR,%3),%0 \n" /* flush */ + " mov (_ASR,%3),%0 \n" + " or %0,%0 \n" + " bne 1b \n" + : "=&r"(status), "=&r"(oldval), "=m"(*m) + : "a"(ATOMIC_OPS_BASE_ADDR), "r"(m), "r"(val) + : "memory", "cc"); + + return oldval; +} + +static inline unsigned long __cmpxchg(volatile unsigned long *m, + unsigned long old, unsigned long new) +{ + unsigned long status; + unsigned long oldval; + + asm volatile( + "1: mov %4,(_AAR,%3) \n" + " mov (_ADR,%3),%1 \n" + " cmp %5,%1 \n" + " bne 2f \n" + " mov %6,(_ADR,%3) \n" + "2: mov (_ADR,%3),%0 \n" /* flush */ + " mov (_ASR,%3),%0 \n" + " or %0,%0 \n" + " bne 1b \n" + : "=&r"(status), "=&r"(oldval), "=m"(*m) + : "a"(ATOMIC_OPS_BASE_ADDR), "r"(m), + "r"(old), "r"(new) + : "memory", "cc"); + + return oldval; +} +#else /* CONFIG_MN10300_HAS_ATOMIC_OPS_UNIT */ +#error "No SMP atomic operation support!" +#endif /* CONFIG_MN10300_HAS_ATOMIC_OPS_UNIT */ + +#else /* CONFIG_SMP */ + +/* + * Emulate xchg for non-SMP MN10300 + */ +struct __xchg_dummy { unsigned long a[100]; }; +#define __xg(x) ((struct __xchg_dummy *)(x)) + +static inline +unsigned long __xchg(volatile unsigned long *m, unsigned long val) +{ + unsigned long oldval; + unsigned long flags; + + flags = arch_local_cli_save(); + oldval = *m; + *m = val; + arch_local_irq_restore(flags); + return oldval; +} + +/* + * Emulate cmpxchg for non-SMP MN10300 + */ +static inline unsigned long __cmpxchg(volatile unsigned long *m, + unsigned long old, unsigned long new) +{ + unsigned long oldval; + unsigned long flags; + + flags = arch_local_cli_save(); + oldval = *m; + if (oldval == old) + *m = new; + arch_local_irq_restore(flags); + return oldval; +} + +#endif /* CONFIG_SMP */ + +#define xchg(ptr, v) \ + ((__typeof__(*(ptr))) __xchg((unsigned long *)(ptr), \ + (unsigned long)(v))) + +#define cmpxchg(ptr, o, n) \ + ((__typeof__(*(ptr))) __cmpxchg((unsigned long *)(ptr), \ + (unsigned long)(o), \ + (unsigned long)(n))) + +#define atomic_xchg(ptr, v) (xchg(&(ptr)->counter, (v))) +#define atomic_cmpxchg(v, old, new) (cmpxchg(&((v)->counter), (old), (new))) + +#endif /* !__ASSEMBLY__ */ + +#ifndef CONFIG_SMP +#include <asm-generic/atomic.h> +#else + +/* + * Atomic operations that C can't guarantee us. Useful for + * resource counting etc.. + */ + +#define ATOMIC_INIT(i) { (i) } + +#ifdef __KERNEL__ + +/** + * atomic_read - read atomic variable + * @v: pointer of type atomic_t + * + * Atomically reads the value of @v. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +#define atomic_read(v) (ACCESS_ONCE((v)->counter)) + +/** + * atomic_set - set atomic variable + * @v: pointer of type atomic_t + * @i: required value + * + * Atomically sets the value of @v to @i. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +#define atomic_set(v, i) (((v)->counter) = (i)) + +/** + * atomic_add_return - add integer to atomic variable + * @i: integer value to add + * @v: pointer of type atomic_t + * + * Atomically adds @i to @v and returns the result + * Note that the guaranteed useful range of an atomic_t is only 24 bits. + */ +static inline int atomic_add_return(int i, atomic_t *v) +{ + int retval; +#ifdef CONFIG_SMP + int status; + + asm volatile( + "1: mov %4,(_AAR,%3) \n" + " mov (_ADR,%3),%1 \n" + " add %5,%1 \n" + " mov %1,(_ADR,%3) \n" + " mov (_ADR,%3),%0 \n" /* flush */ + " mov (_ASR,%3),%0 \n" + " or %0,%0 \n" + " bne 1b \n" + : "=&r"(status), "=&r"(retval), "=m"(v->counter) + : "a"(ATOMIC_OPS_BASE_ADDR), "r"(&v->counter), "r"(i) + : "memory", "cc"); + +#else + unsigned long flags; + + flags = arch_local_cli_save(); + retval = v->counter; + retval += i; + v->counter = retval; + arch_local_irq_restore(flags); +#endif + return retval; +} + +/** + * atomic_sub_return - subtract integer from atomic variable + * @i: integer value to subtract + * @v: pointer of type atomic_t + * + * Atomically subtracts @i from @v and returns the result + * Note that the guaranteed useful range of an atomic_t is only 24 bits. + */ +static inline int atomic_sub_return(int i, atomic_t *v) +{ + int retval; +#ifdef CONFIG_SMP + int status; + + asm volatile( + "1: mov %4,(_AAR,%3) \n" + " mov (_ADR,%3),%1 \n" + " sub %5,%1 \n" + " mov %1,(_ADR,%3) \n" + " mov (_ADR,%3),%0 \n" /* flush */ + " mov (_ASR,%3),%0 \n" + " or %0,%0 \n" + " bne 1b \n" + : "=&r"(status), "=&r"(retval), "=m"(v->counter) + : "a"(ATOMIC_OPS_BASE_ADDR), "r"(&v->counter), "r"(i) + : "memory", "cc"); + +#else + unsigned long flags; + flags = arch_local_cli_save(); + retval = v->counter; + retval -= i; + v->counter = retval; + arch_local_irq_restore(flags); +#endif + return retval; +} + +static inline int atomic_add_negative(int i, atomic_t *v) +{ + return atomic_add_return(i, v) < 0; +} + +static inline void atomic_add(int i, atomic_t *v) +{ + atomic_add_return(i, v); +} + +static inline void atomic_sub(int i, atomic_t *v) +{ + atomic_sub_return(i, v); +} + +static inline void atomic_inc(atomic_t *v) +{ + atomic_add_return(1, v); +} + +static inline void atomic_dec(atomic_t *v) +{ + atomic_sub_return(1, v); +} + +#define atomic_dec_return(v) atomic_sub_return(1, (v)) +#define atomic_inc_return(v) atomic_add_return(1, (v)) + +#define atomic_sub_and_test(i, v) (atomic_sub_return((i), (v)) == 0) +#define atomic_dec_and_test(v) (atomic_sub_return(1, (v)) == 0) +#define atomic_inc_and_test(v) (atomic_add_return(1, (v)) == 0) + +#define atomic_add_unless(v, a, u) \ +({ \ + int c, old; \ + c = atomic_read(v); \ + while (c != (u) && (old = atomic_cmpxchg((v), c, c + (a))) != c) \ + c = old; \ + c != (u); \ +}) + +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) + +/** + * atomic_clear_mask - Atomically clear bits in memory + * @mask: Mask of the bits to be cleared + * @v: pointer to word in memory + * + * Atomically clears the bits set in mask from the memory word specified. + */ +static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) +{ +#ifdef CONFIG_SMP + int status; + + asm volatile( + "1: mov %3,(_AAR,%2) \n" + " mov (_ADR,%2),%0 \n" + " and %4,%0 \n" + " mov %0,(_ADR,%2) \n" + " mov (_ADR,%2),%0 \n" /* flush */ + " mov (_ASR,%2),%0 \n" + " or %0,%0 \n" + " bne 1b \n" + : "=&r"(status), "=m"(*addr) + : "a"(ATOMIC_OPS_BASE_ADDR), "r"(addr), "r"(~mask) + : "memory", "cc"); +#else + unsigned long flags; + + mask = ~mask; + flags = arch_local_cli_save(); + *addr &= mask; + arch_local_irq_restore(flags); +#endif +} + +/** + * atomic_set_mask - Atomically set bits in memory + * @mask: Mask of the bits to be set + * @v: pointer to word in memory + * + * Atomically sets the bits set in mask from the memory word specified. + */ +static inline void atomic_set_mask(unsigned long mask, unsigned long *addr) +{ +#ifdef CONFIG_SMP + int status; + + asm volatile( + "1: mov %3,(_AAR,%2) \n" + " mov (_ADR,%2),%0 \n" + " or %4,%0 \n" + " mov %0,(_ADR,%2) \n" + " mov (_ADR,%2),%0 \n" /* flush */ + " mov (_ASR,%2),%0 \n" + " or %0,%0 \n" + " bne 1b \n" + : "=&r"(status), "=m"(*addr) + : "a"(ATOMIC_OPS_BASE_ADDR), "r"(addr), "r"(mask) + : "memory", "cc"); +#else + unsigned long flags; + + flags = arch_local_cli_save(); + *addr |= mask; + arch_local_irq_restore(flags); +#endif +} + +/* Atomic operations are already serializing on MN10300??? */ +#define smp_mb__before_atomic_dec() barrier() +#define smp_mb__after_atomic_dec() barrier() +#define smp_mb__before_atomic_inc() barrier() +#define smp_mb__after_atomic_inc() barrier() + +#include <asm-generic/atomic-long.h> + +#endif /* __KERNEL__ */ +#endif /* CONFIG_SMP */ +#endif /* _ASM_ATOMIC_H */ diff --git a/arch/mn10300/include/asm/auxvec.h b/arch/mn10300/include/asm/auxvec.h new file mode 100644 index 00000000..4fdb60b2 --- /dev/null +++ b/arch/mn10300/include/asm/auxvec.h @@ -0,0 +1,4 @@ +#ifndef _ASM_AUXVEC_H +#define _ASM_AUXVEC_H + +#endif diff --git a/arch/mn10300/include/asm/bitops.h b/arch/mn10300/include/asm/bitops.h new file mode 100644 index 00000000..09394629 --- /dev/null +++ b/arch/mn10300/include/asm/bitops.h @@ -0,0 +1,239 @@ +/* MN10300 bit operations + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + * + * These have to be done with inline assembly: that way the bit-setting + * is guaranteed to be atomic. All bit operations return 0 if the bit + * was cleared before the operation and != 0 if it was not. + * + * bit 0 is the LSB of addr; bit 32 is the LSB of (addr+1). + */ +#ifndef __ASM_BITOPS_H +#define __ASM_BITOPS_H + +#include <asm/cpu-regs.h> + +#define smp_mb__before_clear_bit() barrier() +#define smp_mb__after_clear_bit() barrier() + +/* + * set bit + */ +#define __set_bit(nr, addr) \ +({ \ + volatile unsigned char *_a = (unsigned char *)(addr); \ + const unsigned shift = (nr) & 7; \ + _a += (nr) >> 3; \ + \ + asm volatile("bset %2,(%1) # set_bit reg" \ + : "=m"(*_a) \ + : "a"(_a), "d"(1 << shift), "m"(*_a) \ + : "memory", "cc"); \ +}) + +#define set_bit(nr, addr) __set_bit((nr), (addr)) + +/* + * clear bit + */ +#define ___clear_bit(nr, addr) \ +({ \ + volatile unsigned char *_a = (unsigned char *)(addr); \ + const unsigned shift = (nr) & 7; \ + _a += (nr) >> 3; \ + \ + asm volatile("bclr %2,(%1) # clear_bit reg" \ + : "=m"(*_a) \ + : "a"(_a), "d"(1 << shift), "m"(*_a) \ + : "memory", "cc"); \ +}) + +#define clear_bit(nr, addr) ___clear_bit((nr), (addr)) + + +static inline void __clear_bit(unsigned long nr, volatile void *addr) +{ + unsigned int *a = (unsigned int *) addr; + int mask; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + *a &= ~mask; +} + +/* + * test bit + */ +static inline int test_bit(unsigned long nr, const volatile void *addr) +{ + return 1UL & (((const volatile unsigned int *) addr)[nr >> 5] >> (nr & 31)); +} + +/* + * change bit + */ +static inline void __change_bit(unsigned long nr, volatile void *addr) +{ + int mask; + unsigned int *a = (unsigned int *) addr; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + *a ^= mask; +} + +extern void change_bit(unsigned long nr, volatile void *addr); + +/* + * test and set bit + */ +#define __test_and_set_bit(nr,addr) \ +({ \ + volatile unsigned char *_a = (unsigned char *)(addr); \ + const unsigned shift = (nr) & 7; \ + unsigned epsw; \ + _a += (nr) >> 3; \ + \ + asm volatile("bset %3,(%2) # test_set_bit reg\n" \ + "mov epsw,%1" \ + : "=m"(*_a), "=d"(epsw) \ + : "a"(_a), "d"(1 << shift), "m"(*_a) \ + : "memory", "cc"); \ + \ + !(epsw & EPSW_FLAG_Z); \ +}) + +#define test_and_set_bit(nr, addr) __test_and_set_bit((nr), (addr)) + +/* + * test and clear bit + */ +#define __test_and_clear_bit(nr, addr) \ +({ \ + volatile unsigned char *_a = (unsigned char *)(addr); \ + const unsigned shift = (nr) & 7; \ + unsigned epsw; \ + _a += (nr) >> 3; \ + \ + asm volatile("bclr %3,(%2) # test_clear_bit reg\n" \ + "mov epsw,%1" \ + : "=m"(*_a), "=d"(epsw) \ + : "a"(_a), "d"(1 << shift), "m"(*_a) \ + : "memory", "cc"); \ + \ + !(epsw & EPSW_FLAG_Z); \ +}) + +#define test_and_clear_bit(nr, addr) __test_and_clear_bit((nr), (addr)) + +/* + * test and change bit + */ +static inline int __test_and_change_bit(unsigned long nr, volatile void *addr) +{ + int mask, retval; + unsigned int *a = (unsigned int *)addr; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + retval = (mask & *a) != 0; + *a ^= mask; + + return retval; +} + +extern int test_and_change_bit(unsigned long nr, volatile void *addr); + +#include <asm-generic/bitops/lock.h> + +#ifdef __KERNEL__ + +/** + * __ffs - find first bit set + * @x: the word to search + * + * - return 31..0 to indicate bit 31..0 most least significant bit set + * - if no bits are set in x, the result is undefined + */ +static inline __attribute__((const)) +unsigned long __ffs(unsigned long x) +{ + int bit; + asm("bsch %2,%0" : "=r"(bit) : "0"(0), "r"(x & -x) : "cc"); + return bit; +} + +/* + * special slimline version of fls() for calculating ilog2_u32() + * - note: no protection against n == 0 + */ +static inline __attribute__((const)) +int __ilog2_u32(u32 n) +{ + int bit; + asm("bsch %2,%0" : "=r"(bit) : "0"(0), "r"(n) : "cc"); + return bit; +} + +/** + * fls - find last bit set + * @x: the word to search + * + * This is defined the same way as ffs: + * - return 32..1 to indicate bit 31..0 most significant bit set + * - return 0 to indicate no bits set + */ +static inline __attribute__((const)) +int fls(int x) +{ + return (x != 0) ? __ilog2_u32(x) + 1 : 0; +} + +/** + * __fls - find last (most-significant) set bit in a long word + * @word: the word to search + * + * Undefined if no set bit exists, so code should check against 0 first. + */ +static inline unsigned long __fls(unsigned long word) +{ + return __ilog2_u32(word); +} + +/** + * ffs - find first bit set + * @x: the word to search + * + * - return 32..1 to indicate bit 31..0 most least significant bit set + * - return 0 to indicate no bits set + */ +static inline __attribute__((const)) +int ffs(int x) +{ + /* Note: (x & -x) gives us a mask that is the least significant + * (rightmost) 1-bit of the value in x. + */ + return fls(x & -x); +} + +#include <asm-generic/bitops/ffz.h> +#include <asm-generic/bitops/fls64.h> +#include <asm-generic/bitops/find.h> +#include <asm-generic/bitops/sched.h> +#include <asm-generic/bitops/hweight.h> + +#define ext2_set_bit_atomic(lock, nr, addr) \ + test_and_set_bit((nr), (addr)) +#define ext2_clear_bit_atomic(lock, nr, addr) \ + test_and_clear_bit((nr), (addr)) + +#include <asm-generic/bitops/le.h> + +#endif /* __KERNEL__ */ +#endif /* __ASM_BITOPS_H */ diff --git a/arch/mn10300/include/asm/bitsperlong.h b/arch/mn10300/include/asm/bitsperlong.h new file mode 100644 index 00000000..6dc0bb0c --- /dev/null +++ b/arch/mn10300/include/asm/bitsperlong.h @@ -0,0 +1 @@ +#include <asm-generic/bitsperlong.h> diff --git a/arch/mn10300/include/asm/bug.h b/arch/mn10300/include/asm/bug.h new file mode 100644 index 00000000..aa6a3888 --- /dev/null +++ b/arch/mn10300/include/asm/bug.h @@ -0,0 +1,37 @@ +/* MN10300 Kernel bug reporting + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_BUG_H +#define _ASM_BUG_H + +#ifdef CONFIG_BUG + +/* + * Tell the user there is some problem. + */ +#define BUG() \ +do { \ + asm volatile( \ + " syscall 15 \n" \ + "0: \n" \ + " .section __bug_table,\"a\" \n" \ + " .long 0b,%0,%1 \n" \ + " .previous \n" \ + : \ + : "i"(__FILE__), "i"(__LINE__) \ + ); \ +} while (1) + +#define HAVE_ARCH_BUG +#endif /* CONFIG_BUG */ + +#include <asm-generic/bug.h> + +#endif /* _ASM_BUG_H */ diff --git a/arch/mn10300/include/asm/bugs.h b/arch/mn10300/include/asm/bugs.h new file mode 100644 index 00000000..31c8bc59 --- /dev/null +++ b/arch/mn10300/include/asm/bugs.h @@ -0,0 +1,20 @@ +/* MN10300 Checks for architecture-dependent bugs + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_BUGS_H +#define _ASM_BUGS_H + +#include <asm/processor.h> + +static inline void __init check_bugs(void) +{ +} + +#endif /* _ASM_BUGS_H */ diff --git a/arch/mn10300/include/asm/busctl-regs.h b/arch/mn10300/include/asm/busctl-regs.h new file mode 100644 index 00000000..1632aef7 --- /dev/null +++ b/arch/mn10300/include/asm/busctl-regs.h @@ -0,0 +1,151 @@ +/* AM33v2 on-board bus controller registers + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _ASM_BUSCTL_REGS_H +#define _ASM_BUSCTL_REGS_H + +#include <asm/cpu-regs.h> + +#ifdef __KERNEL__ + +/* bus controller registers */ +#define BCCR __SYSREG(0xc0002000, u32) /* bus controller control reg */ +#define BCCR_B0AD 0x00000003 /* block 0 (80000000-83ffffff) bus allocation */ +#define BCCR_B1AD 0x0000000c /* block 1 (84000000-87ffffff) bus allocation */ +#define BCCR_B2AD 0x00000030 /* block 2 (88000000-8bffffff) bus allocation */ +#define BCCR_B3AD 0x000000c0 /* block 3 (8c000000-8fffffff) bus allocation */ +#define BCCR_B4AD 0x00000300 /* block 4 (90000000-93ffffff) bus allocation */ +#define BCCR_B5AD 0x00000c00 /* block 5 (94000000-97ffffff) bus allocation */ +#define BCCR_B6AD 0x00003000 /* block 6 (98000000-9bffffff) bus allocation */ +#define BCCR_B7AD 0x0000c000 /* block 7 (9c000000-9fffffff) bus allocation */ +#define BCCR_BxAD_EXBUS 0x0 /* - direct to system bus controller */ +#define BCCR_BxAD_OPEXBUS 0x1 /* - direct to memory bus controller */ +#define BCCR_BxAD_OCMBUS 0x2 /* - direct to on chip memory */ +#define BCCR_API 0x00070000 /* bus arbitration priority */ +#define BCCR_API_DMACICD 0x00000000 /* - DMA > CI > CD */ +#define BCCR_API_DMACDCI 0x00010000 /* - DMA > CD > CI */ +#define BCCR_API_CICDDMA 0x00020000 /* - CI > CD > DMA */ +#define BCCR_API_CDCIDMA 0x00030000 /* - CD > CI > DMA */ +#define BCCR_API_ROUNDROBIN 0x00040000 /* - round robin */ +#define BCCR_BEPRI_DMACICD 0x00c00000 /* bus error address priority */ +#define BCCR_BEPRI_DMACDCI 0x00000000 /* - DMA > CI > CD */ +#define BCCR_BEPRI_CICDDMA 0x00400000 /* - DMA > CD > CI */ +#define BCCR_BEPRI_CDCIDMA 0x00800000 /* - CI > CD > DMA */ +#define BCCR_BEPRI 0x00c00000 /* - CD > CI > DMA */ +#define BCCR_TMON 0x03000000 /* timeout value settings */ +#define BCCR_TMON_16IOCLK 0x00000000 /* - 16 IOCLK cycles */ +#define BCCR_TMON_256IOCLK 0x01000000 /* - 256 IOCLK cycles */ +#define BCCR_TMON_4096IOCLK 0x02000000 /* - 4096 IOCLK cycles */ +#define BCCR_TMON_65536IOCLK 0x03000000 /* - 65536 IOCLK cycles */ +#define BCCR_TMOE 0x10000000 /* timeout detection enable */ + +#define BCBERR __SYSREG(0xc0002010, u32) /* bus error source reg */ +#define BCBERR_BESB 0x0000001f /* erroneous access destination space */ +#define BCBERR_BESB_MON 0x00000001 /* - monitor space */ +#define BCBERR_BESB_IO 0x00000002 /* - IO bus */ +#define BCBERR_BESB_EX 0x00000004 /* - EX bus */ +#define BCBERR_BESB_OPEX 0x00000008 /* - OpEX bus */ +#define BCBERR_BESB_OCM 0x00000010 /* - on chip memory */ +#define BCBERR_BERW 0x00000100 /* type of access */ +#define BCBERR_BERW_WRITE 0x00000000 /* - write */ +#define BCBERR_BERW_READ 0x00000100 /* - read */ +#define BCBERR_BESD 0x00000200 /* error detector */ +#define BCBERR_BESD_BCU 0x00000000 /* - BCU detected error */ +#define BCBERR_BESD_SLAVE_BUS 0x00000200 /* - slave bus detected error */ +#define BCBERR_BEBST 0x00000400 /* type of access */ +#define BCBERR_BEBST_SINGLE 0x00000000 /* - single */ +#define BCBERR_BEBST_BURST 0x00000400 /* - burst */ +#define BCBERR_BEME 0x00000800 /* multiple bus error flag */ +#define BCBERR_BEMR 0x00007000 /* master bus that caused the error */ +#define BCBERR_BEMR_NOERROR 0x00000000 /* - no error */ +#define BCBERR_BEMR_CI 0x00001000 /* - CPU instruction fetch bus caused error */ +#define BCBERR_BEMR_CD 0x00002000 /* - CPU data bus caused error */ +#define BCBERR_BEMR_DMA 0x00004000 /* - DMA bus caused error */ + +#define BCBEAR __SYSREGC(0xc0002020, u32) /* bus error address reg */ + +/* system bus controller registers */ +#define SBBASE(X) __SYSREG(0xd8c00100 + (X) * 0x10, u32) /* SBC base addr regs */ +#define SBBASE_BE 0x00000001 /* bank enable */ +#define SBBASE_BAM 0x0000fffe /* bank address mask [31:17] */ +#define SBBASE_BBA 0xfffe0000 /* bank base address [31:17] */ + +#define SBCNTRL0(X) __SYSREG(0xd8c00200 + (X) * 0x10, u32) /* SBC bank ctrl0 regs */ +#define SBCNTRL0_WEH 0x00000f00 /* write enable hold */ +#define SBCNTRL0_REH 0x0000f000 /* read enable hold */ +#define SBCNTRL0_RWH 0x000f0000 /* SRW signal hold */ +#define SBCNTRL0_CSH 0x00f00000 /* chip select hold */ +#define SBCNTRL0_DAH 0x0f000000 /* data hold */ +#define SBCNTRL0_ADH 0xf0000000 /* address hold */ + +#define SBCNTRL1(X) __SYSREG(0xd8c00204 + (X) * 0x10, u32) /* SBC bank ctrl1 regs */ +#define SBCNTRL1_WED 0x00000f00 /* write enable delay */ +#define SBCNTRL1_RED 0x0000f000 /* read enable delay */ +#define SBCNTRL1_RWD 0x000f0000 /* SRW signal delay */ +#define SBCNTRL1_ASW 0x00f00000 /* address strobe width */ +#define SBCNTRL1_CSD 0x0f000000 /* chip select delay */ +#define SBCNTRL1_ASD 0xf0000000 /* address strobe delay */ + +#define SBCNTRL2(X) __SYSREG(0xd8c00208 + (X) * 0x10, u32) /* SBC bank ctrl2 regs */ +#define SBCNTRL2_WC 0x000000ff /* wait count */ +#define SBCNTRL2_BWC 0x00000f00 /* burst wait count */ +#define SBCNTRL2_WM 0x01000000 /* wait mode setting */ +#define SBCNTRL2_WM_FIXEDWAIT 0x00000000 /* - fixed wait access */ +#define SBCNTRL2_WM_HANDSHAKE 0x01000000 /* - handshake access */ +#define SBCNTRL2_BM 0x02000000 /* bus synchronisation mode */ +#define SBCNTRL2_BM_SYNC 0x00000000 /* - synchronous mode */ +#define SBCNTRL2_BM_ASYNC 0x02000000 /* - asynchronous mode */ +#define SBCNTRL2_BW 0x04000000 /* bus width */ +#define SBCNTRL2_BW_32 0x00000000 /* - 32 bits */ +#define SBCNTRL2_BW_16 0x04000000 /* - 16 bits */ +#define SBCNTRL2_RWINV 0x08000000 /* R/W signal invert polarity */ +#define SBCNTRL2_RWINV_NORM 0x00000000 /* - normal (read high) */ +#define SBCNTRL2_RWINV_INV 0x08000000 /* - inverted (read low) */ +#define SBCNTRL2_BT 0x70000000 /* bus type setting */ +#define SBCNTRL2_BT_SRAM 0x00000000 /* - SRAM interface */ +#define SBCNTRL2_BT_ADMUX 0x00000000 /* - addr/data multiplexed interface */ +#define SBCNTRL2_BT_BROM 0x00000000 /* - burst ROM interface */ +#define SBCNTRL2_BTSE 0x80000000 /* burst enable */ + +/* memory bus controller */ +#define SDBASE(X) __SYSREG(0xda000008 + (X) * 0x4, u32) /* MBC base addr regs */ +#define SDBASE_CE 0x00000001 /* chip enable */ +#define SDBASE_CBAM 0x0000fff0 /* chip base address mask [31:20] */ +#define SDBASE_CBAM_SHIFT 16 +#define SDBASE_CBA 0xfff00000 /* chip base address [31:20] */ + +#define SDRAMBUS __SYSREG(0xda000000, u32) /* bus mode control reg */ +#define SDRAMBUS_REFEN 0x00000004 /* refresh enable */ +#define SDRAMBUS_TRC 0x00000018 /* refresh command delay time */ +#define SDRAMBUS_BSTPT 0x00000020 /* burst stop command enable */ +#define SDRAMBUS_PONSEQ 0x00000040 /* power on sequence */ +#define SDRAMBUS_SELFREQ 0x00000080 /* self-refresh mode request */ +#define SDRAMBUS_SELFON 0x00000100 /* self-refresh mode on */ +#define SDRAMBUS_SIZE 0x00030000 /* SDRAM size */ +#define SDRAMBUS_SIZE_64Mbit 0x00010000 /* 64Mbit SDRAM (x16) */ +#define SDRAMBUS_SIZE_128Mbit 0x00020000 /* 128Mbit SDRAM (x16) */ +#define SDRAMBUS_SIZE_256Mbit 0x00030000 /* 256Mbit SDRAM (x16) */ +#define SDRAMBUS_TRASWAIT 0x000c0000 /* row address precharge command cycle number */ +#define SDRAMBUS_REFNUM 0x00300000 /* refresh command number */ +#define SDRAMBUS_BSTWAIT 0x00c00000 /* burst stop command cycle */ +#define SDRAMBUS_SETWAIT 0x03000000 /* mode register setting command cycle */ +#define SDRAMBUS_PREWAIT 0x0c000000 /* precharge command cycle */ +#define SDRAMBUS_RASLATE 0x30000000 /* RAS latency */ +#define SDRAMBUS_CASLATE 0xc0000000 /* CAS latency */ + +#define SDREFCNT __SYSREG(0xda000004, u32) /* refresh period reg */ +#define SDREFCNT_PERI 0x00000fff /* refresh period */ + +#define SDSHDW __SYSREG(0xda000010, u32) /* test reg */ + +#endif /* __KERNEL__ */ + +#endif /* _ASM_BUSCTL_REGS_H */ diff --git a/arch/mn10300/include/asm/byteorder.h b/arch/mn10300/include/asm/byteorder.h new file mode 100644 index 00000000..5dd0bdd9 --- /dev/null +++ b/arch/mn10300/include/asm/byteorder.h @@ -0,0 +1,6 @@ +#ifndef _ASM_BYTEORDER_H +#define _ASM_BYTEORDER_H + +#include <linux/byteorder/little_endian.h> + +#endif /* _ASM_BYTEORDER_H */ diff --git a/arch/mn10300/include/asm/cache.h b/arch/mn10300/include/asm/cache.h new file mode 100644 index 00000000..f29cde2c --- /dev/null +++ b/arch/mn10300/include/asm/cache.h @@ -0,0 +1,60 @@ +/* MN10300 cache management registers + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _ASM_CACHE_H +#define _ASM_CACHE_H + +#include <asm/cpu-regs.h> +#include <proc/cache.h> + +#ifndef __ASSEMBLY__ +#define L1_CACHE_DISPARITY (L1_CACHE_NENTRIES * L1_CACHE_BYTES) +#else +#define L1_CACHE_DISPARITY L1_CACHE_NENTRIES * L1_CACHE_BYTES +#endif + +#define ARCH_DMA_MINALIGN L1_CACHE_BYTES + +/* data cache purge registers + * - read from the register to unconditionally purge that cache line + * - write address & 0xffffff00 to conditionally purge that cache line + * - clear LSB to request invalidation as well + */ +#define DCACHE_PURGE(WAY, ENTRY) \ + __SYSREG(0xc8400000 + (WAY) * L1_CACHE_WAYDISP + \ + (ENTRY) * L1_CACHE_BYTES, u32) + +#define DCACHE_PURGE_WAY0(ENTRY) \ + __SYSREG(0xc8400000 + 0 * L1_CACHE_WAYDISP + (ENTRY) * L1_CACHE_BYTES, u32) +#define DCACHE_PURGE_WAY1(ENTRY) \ + __SYSREG(0xc8400000 + 1 * L1_CACHE_WAYDISP + (ENTRY) * L1_CACHE_BYTES, u32) +#define DCACHE_PURGE_WAY2(ENTRY) \ + __SYSREG(0xc8400000 + 2 * L1_CACHE_WAYDISP + (ENTRY) * L1_CACHE_BYTES, u32) +#define DCACHE_PURGE_WAY3(ENTRY) \ + __SYSREG(0xc8400000 + 3 * L1_CACHE_WAYDISP + (ENTRY) * L1_CACHE_BYTES, u32) + +/* instruction cache access registers */ +#define ICACHE_DATA(WAY, ENTRY, OFF) \ + __SYSREG(0xc8000000 + (WAY) * L1_CACHE_WAYDISP + \ + (ENTRY) * L1_CACHE_BYTES + (OFF) * 4, u32) +#define ICACHE_TAG(WAY, ENTRY) \ + __SYSREG(0xc8100000 + (WAY) * L1_CACHE_WAYDISP + \ + (ENTRY) * L1_CACHE_BYTES, u32) + +/* data cache access registers */ +#define DCACHE_DATA(WAY, ENTRY, OFF) \ + __SYSREG(0xc8200000 + (WAY) * L1_CACHE_WAYDISP + \ + (ENTRY) * L1_CACHE_BYTES + (OFF) * 4, u32) +#define DCACHE_TAG(WAY, ENTRY) \ + __SYSREG(0xc8300000 + (WAY) * L1_CACHE_WAYDISP + \ + (ENTRY) * L1_CACHE_BYTES, u32) + +#endif /* _ASM_CACHE_H */ diff --git a/arch/mn10300/include/asm/cacheflush.h b/arch/mn10300/include/asm/cacheflush.h new file mode 100644 index 00000000..faed9024 --- /dev/null +++ b/arch/mn10300/include/asm/cacheflush.h @@ -0,0 +1,171 @@ +/* MN10300 Cache flushing + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_CACHEFLUSH_H +#define _ASM_CACHEFLUSH_H + +#ifndef __ASSEMBLY__ + +/* Keep includes the same across arches. */ +#include <linux/mm.h> + +/* + * Primitive routines + */ +#ifdef CONFIG_MN10300_CACHE_ENABLED +extern void mn10300_local_icache_inv(void); +extern void mn10300_local_icache_inv_page(unsigned long start); +extern void mn10300_local_icache_inv_range(unsigned long start, unsigned long end); +extern void mn10300_local_icache_inv_range2(unsigned long start, unsigned long size); +extern void mn10300_local_dcache_inv(void); +extern void mn10300_local_dcache_inv_page(unsigned long start); +extern void mn10300_local_dcache_inv_range(unsigned long start, unsigned long end); +extern void mn10300_local_dcache_inv_range2(unsigned long start, unsigned long size); +extern void mn10300_icache_inv(void); +extern void mn10300_icache_inv_page(unsigned long start); +extern void mn10300_icache_inv_range(unsigned long start, unsigned long end); +extern void mn10300_icache_inv_range2(unsigned long start, unsigned long size); +extern void mn10300_dcache_inv(void); +extern void mn10300_dcache_inv_page(unsigned long start); +extern void mn10300_dcache_inv_range(unsigned long start, unsigned long end); +extern void mn10300_dcache_inv_range2(unsigned long start, unsigned long size); +#ifdef CONFIG_MN10300_CACHE_WBACK +extern void mn10300_local_dcache_flush(void); +extern void mn10300_local_dcache_flush_page(unsigned long start); +extern void mn10300_local_dcache_flush_range(unsigned long start, unsigned long end); +extern void mn10300_local_dcache_flush_range2(unsigned long start, unsigned long size); +extern void mn10300_local_dcache_flush_inv(void); +extern void mn10300_local_dcache_flush_inv_page(unsigned long start); +extern void mn10300_local_dcache_flush_inv_range(unsigned long start, unsigned long end); +extern void mn10300_local_dcache_flush_inv_range2(unsigned long start, unsigned long size); +extern void mn10300_dcache_flush(void); +extern void mn10300_dcache_flush_page(unsigned long start); +extern void mn10300_dcache_flush_range(unsigned long start, unsigned long end); +extern void mn10300_dcache_flush_range2(unsigned long start, unsigned long size); +extern void mn10300_dcache_flush_inv(void); +extern void mn10300_dcache_flush_inv_page(unsigned long start); +extern void mn10300_dcache_flush_inv_range(unsigned long start, unsigned long end); +extern void mn10300_dcache_flush_inv_range2(unsigned long start, unsigned long size); +#else +#define mn10300_local_dcache_flush() do {} while (0) +#define mn10300_local_dcache_flush_page(start) do {} while (0) +#define mn10300_local_dcache_flush_range(start, end) do {} while (0) +#define mn10300_local_dcache_flush_range2(start, size) do {} while (0) +#define mn10300_local_dcache_flush_inv() \ + mn10300_local_dcache_inv() +#define mn10300_local_dcache_flush_inv_page(start) \ + mn10300_local_dcache_inv_page(start) +#define mn10300_local_dcache_flush_inv_range(start, end) \ + mn10300_local_dcache_inv_range(start, end) +#define mn10300_local_dcache_flush_inv_range2(start, size) \ + mn10300_local_dcache_inv_range2(start, size) +#define mn10300_dcache_flush() do {} while (0) +#define mn10300_dcache_flush_page(start) do {} while (0) +#define mn10300_dcache_flush_range(start, end) do {} while (0) +#define mn10300_dcache_flush_range2(start, size) do {} while (0) +#define mn10300_dcache_flush_inv() mn10300_dcache_inv() +#define mn10300_dcache_flush_inv_page(start) \ + mn10300_dcache_inv_page((start)) +#define mn10300_dcache_flush_inv_range(start, end) \ + mn10300_dcache_inv_range((start), (end)) +#define mn10300_dcache_flush_inv_range2(start, size) \ + mn10300_dcache_inv_range2((start), (size)) +#endif /* CONFIG_MN10300_CACHE_WBACK */ +#else +#define mn10300_local_icache_inv() do {} while (0) +#define mn10300_local_icache_inv_page(start) do {} while (0) +#define mn10300_local_icache_inv_range(start, end) do {} while (0) +#define mn10300_local_icache_inv_range2(start, size) do {} while (0) +#define mn10300_local_dcache_inv() do {} while (0) +#define mn10300_local_dcache_inv_page(start) do {} while (0) +#define mn10300_local_dcache_inv_range(start, end) do {} while (0) +#define mn10300_local_dcache_inv_range2(start, size) do {} while (0) +#define mn10300_local_dcache_flush() do {} while (0) +#define mn10300_local_dcache_flush_inv_page(start) do {} while (0) +#define mn10300_local_dcache_flush_inv() do {} while (0) +#define mn10300_local_dcache_flush_inv_range(start, end)do {} while (0) +#define mn10300_local_dcache_flush_inv_range2(start, size) do {} while (0) +#define mn10300_local_dcache_flush_page(start) do {} while (0) +#define mn10300_local_dcache_flush_range(start, end) do {} while (0) +#define mn10300_local_dcache_flush_range2(start, size) do {} while (0) +#define mn10300_icache_inv() do {} while (0) +#define mn10300_icache_inv_page(start) do {} while (0) +#define mn10300_icache_inv_range(start, end) do {} while (0) +#define mn10300_icache_inv_range2(start, size) do {} while (0) +#define mn10300_dcache_inv() do {} while (0) +#define mn10300_dcache_inv_page(start) do {} while (0) +#define mn10300_dcache_inv_range(start, end) do {} while (0) +#define mn10300_dcache_inv_range2(start, size) do {} while (0) +#define mn10300_dcache_flush() do {} while (0) +#define mn10300_dcache_flush_inv_page(start) do {} while (0) +#define mn10300_dcache_flush_inv() do {} while (0) +#define mn10300_dcache_flush_inv_range(start, end) do {} while (0) +#define mn10300_dcache_flush_inv_range2(start, size) do {} while (0) +#define mn10300_dcache_flush_page(start) do {} while (0) +#define mn10300_dcache_flush_range(start, end) do {} while (0) +#define mn10300_dcache_flush_range2(start, size) do {} while (0) +#endif /* CONFIG_MN10300_CACHE_ENABLED */ + +/* + * Virtually-indexed cache management (our cache is physically indexed) + */ +#define flush_cache_all() do {} while (0) +#define flush_cache_mm(mm) do {} while (0) +#define flush_cache_dup_mm(mm) do {} while (0) +#define flush_cache_range(mm, start, end) do {} while (0) +#define flush_cache_page(vma, vmaddr, pfn) do {} while (0) +#define flush_cache_vmap(start, end) do {} while (0) +#define flush_cache_vunmap(start, end) do {} while (0) +#define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 0 +#define flush_dcache_page(page) do {} while (0) +#define flush_dcache_mmap_lock(mapping) do {} while (0) +#define flush_dcache_mmap_unlock(mapping) do {} while (0) + +/* + * Physically-indexed cache management + */ +#if defined(CONFIG_MN10300_CACHE_FLUSH_ICACHE) +extern void flush_icache_page(struct vm_area_struct *vma, struct page *page); +extern void flush_icache_range(unsigned long start, unsigned long end); +#elif defined(CONFIG_MN10300_CACHE_INV_ICACHE) +static inline void flush_icache_page(struct vm_area_struct *vma, + struct page *page) +{ + mn10300_icache_inv_page(page_to_phys(page)); +} +extern void flush_icache_range(unsigned long start, unsigned long end); +#else +#define flush_icache_range(start, end) do {} while (0) +#define flush_icache_page(vma, pg) do {} while (0) +#endif + + +#define flush_icache_user_range(vma, pg, adr, len) \ + flush_icache_range(adr, adr + len) + +#define copy_to_user_page(vma, page, vaddr, dst, src, len) \ + do { \ + memcpy(dst, src, len); \ + flush_icache_page(vma, page); \ + } while (0) + +#define copy_from_user_page(vma, page, vaddr, dst, src, len) \ + memcpy(dst, src, len) + +/* + * Internal debugging function + */ +#ifdef CONFIG_DEBUG_PAGEALLOC +extern void kernel_map_pages(struct page *page, int numpages, int enable); +#endif + +#endif /* __ASSEMBLY__ */ + +#endif /* _ASM_CACHEFLUSH_H */ diff --git a/arch/mn10300/include/asm/checksum.h b/arch/mn10300/include/asm/checksum.h new file mode 100644 index 00000000..9fb2a8d8 --- /dev/null +++ b/arch/mn10300/include/asm/checksum.h @@ -0,0 +1,86 @@ +/* MN10300 Optimised checksumming code + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_CHECKSUM_H +#define _ASM_CHECKSUM_H + +extern __wsum csum_partial(const void *buff, int len, __wsum sum); +extern __wsum csum_partial_copy_nocheck(const void *src, void *dst, + int len, __wsum sum); +extern __wsum csum_partial_copy_from_user(const void *src, void *dst, + int len, __wsum sum, + int *err_ptr); +extern __sum16 ip_fast_csum(const void *iph, unsigned int ihl); +extern __wsum csum_partial(const void *buff, int len, __wsum sum); +extern __sum16 ip_compute_csum(const void *buff, int len); + +#define csum_partial_copy_fromuser csum_partial_copy +extern __wsum csum_partial_copy(const void *src, void *dst, int len, + __wsum sum); + +static inline __sum16 csum_fold(__wsum sum) +{ + asm( + " add %1,%0 \n" + " addc 0xffff,%0 \n" + : "=r" (sum) + : "r" (sum << 16), "0" (sum & 0xffff0000) + : "cc" + ); + return (~sum) >> 16; +} + +static inline __wsum csum_tcpudp_nofold(unsigned long saddr, + unsigned long daddr, + unsigned short len, + unsigned short proto, + __wsum sum) +{ + __wsum tmp; + + tmp = (__wsum) ntohs(len) << 16; + tmp += (__wsum) proto << 8; + + asm( + " add %1,%0 \n" + " addc %2,%0 \n" + " addc %3,%0 \n" + " addc 0,%0 \n" + : "=r" (sum) + : "r" (daddr), "r"(saddr), "r"(tmp), "0"(sum) + : "cc" + ); + return sum; +} + +/* + * computes the checksum of the TCP/UDP pseudo-header + * returns a 16-bit checksum, already complemented + */ +static inline __sum16 csum_tcpudp_magic(unsigned long saddr, + unsigned long daddr, + unsigned short len, + unsigned short proto, + __wsum sum) +{ + return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum)); +} + +#undef _HAVE_ARCH_IPV6_CSUM + +/* + * Copy and checksum to user + */ +#define HAVE_CSUM_COPY_USER +extern __wsum csum_and_copy_to_user(const void *src, void *dst, int len, + __wsum sum, int *err_ptr); + + +#endif /* _ASM_CHECKSUM_H */ diff --git a/arch/mn10300/include/asm/cpu-regs.h b/arch/mn10300/include/asm/cpu-regs.h new file mode 100644 index 00000000..c54effae --- /dev/null +++ b/arch/mn10300/include/asm/cpu-regs.h @@ -0,0 +1,353 @@ +/* MN10300 Core system registers + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_CPU_REGS_H +#define _ASM_CPU_REGS_H + +#ifndef __ASSEMBLY__ +#include <linux/types.h> +#endif + +/* we tell the compiler to pretend to be AM33 so that it doesn't try and use + * the FP regs, but tell the assembler that we're actually allowed AM33v2 + * instructions */ +#ifndef __ASSEMBLY__ +asm(" .am33_2\n"); +#else +.am33_2 +#endif + +#ifdef __KERNEL__ + +#ifndef __ASSEMBLY__ +#define __SYSREG(ADDR, TYPE) (*(volatile TYPE *)(ADDR)) +#define __SYSREGC(ADDR, TYPE) (*(const volatile TYPE *)(ADDR)) +#else +#define __SYSREG(ADDR, TYPE) ADDR +#define __SYSREGC(ADDR, TYPE) ADDR +#endif + +/* CPU registers */ +#define EPSW_FLAG_Z 0x00000001 /* zero flag */ +#define EPSW_FLAG_N 0x00000002 /* negative flag */ +#define EPSW_FLAG_C 0x00000004 /* carry flag */ +#define EPSW_FLAG_V 0x00000008 /* overflow flag */ +#define EPSW_IM 0x00000700 /* interrupt mode */ +#define EPSW_IM_0 0x00000000 /* interrupt mode 0 */ +#define EPSW_IM_1 0x00000100 /* interrupt mode 1 */ +#define EPSW_IM_2 0x00000200 /* interrupt mode 2 */ +#define EPSW_IM_3 0x00000300 /* interrupt mode 3 */ +#define EPSW_IM_4 0x00000400 /* interrupt mode 4 */ +#define EPSW_IM_5 0x00000500 /* interrupt mode 5 */ +#define EPSW_IM_6 0x00000600 /* interrupt mode 6 */ +#define EPSW_IM_7 0x00000700 /* interrupt mode 7 */ +#define EPSW_IE 0x00000800 /* interrupt enable */ +#define EPSW_S 0x00003000 /* software auxiliary bits */ +#define EPSW_T 0x00008000 /* trace enable */ +#define EPSW_nSL 0x00010000 /* not supervisor level */ +#define EPSW_NMID 0x00020000 /* nonmaskable interrupt disable */ +#define EPSW_nAR 0x00040000 /* register bank control */ +#define EPSW_ML 0x00080000 /* monitor level */ +#define EPSW_FE 0x00100000 /* FPU enable */ +#define EPSW_IM_SHIFT 8 /* EPSW_IM_SHIFT determines the interrupt mode */ + +#define NUM2EPSW_IM(num) ((num) << EPSW_IM_SHIFT) + +/* FPU registers */ +#define FPCR_EF_I 0x00000001 /* inexact result FPU exception flag */ +#define FPCR_EF_U 0x00000002 /* underflow FPU exception flag */ +#define FPCR_EF_O 0x00000004 /* overflow FPU exception flag */ +#define FPCR_EF_Z 0x00000008 /* zero divide FPU exception flag */ +#define FPCR_EF_V 0x00000010 /* invalid operand FPU exception flag */ +#define FPCR_EE_I 0x00000020 /* inexact result FPU exception enable */ +#define FPCR_EE_U 0x00000040 /* underflow FPU exception enable */ +#define FPCR_EE_O 0x00000080 /* overflow FPU exception enable */ +#define FPCR_EE_Z 0x00000100 /* zero divide FPU exception enable */ +#define FPCR_EE_V 0x00000200 /* invalid operand FPU exception enable */ +#define FPCR_EC_I 0x00000400 /* inexact result FPU exception cause */ +#define FPCR_EC_U 0x00000800 /* underflow FPU exception cause */ +#define FPCR_EC_O 0x00001000 /* overflow FPU exception cause */ +#define FPCR_EC_Z 0x00002000 /* zero divide FPU exception cause */ +#define FPCR_EC_V 0x00004000 /* invalid operand FPU exception cause */ +#define FPCR_RM 0x00030000 /* rounding mode */ +#define FPCR_RM_NEAREST 0x00000000 /* - round to nearest value */ +#define FPCR_FCC_U 0x00040000 /* FPU unordered condition code */ +#define FPCR_FCC_E 0x00080000 /* FPU equal condition code */ +#define FPCR_FCC_G 0x00100000 /* FPU greater than condition code */ +#define FPCR_FCC_L 0x00200000 /* FPU less than condition code */ +#define FPCR_INIT 0x00000000 /* no exceptions, rounding to nearest */ + +/* CPU control registers */ +#define CPUP __SYSREG(0xc0000020, u16) /* CPU pipeline register */ +#define CPUP_DWBD 0x0020 /* write buffer disable flag */ +#define CPUP_IPFD 0x0040 /* instruction prefetch disable flag */ +#define CPUP_EXM 0x0080 /* exception operation mode */ +#define CPUP_EXM_AM33V1 0x0000 /* - AM33 v1 exception mode */ +#define CPUP_EXM_AM33V2 0x0080 /* - AM33 v2 exception mode */ + +#define CPUM __SYSREG(0xc0000040, u16) /* CPU mode register */ +#define CPUM_SLEEP 0x0004 /* set to enter sleep state */ +#define CPUM_HALT 0x0008 /* set to enter halt state */ +#define CPUM_STOP 0x0010 /* set to enter stop state */ + +#define CPUREV __SYSREGC(0xc0000050, u32) /* CPU revision register */ +#define CPUREV_TYPE 0x0000000f /* CPU type */ +#define CPUREV_TYPE_S 0 +#define CPUREV_TYPE_AM33_1 0x00000000 /* - AM33-1 core, AM33/1.00 arch */ +#define CPUREV_TYPE_AM33_2 0x00000001 /* - AM33-2 core, AM33/2.00 arch */ +#define CPUREV_TYPE_AM34_1 0x00000002 /* - AM34-1 core, AM33/2.00 arch */ +#define CPUREV_TYPE_AM33_3 0x00000003 /* - AM33-3 core, AM33/2.00 arch */ +#define CPUREV_TYPE_AM34_2 0x00000004 /* - AM34-2 core, AM33/3.00 arch */ +#define CPUREV_REVISION 0x000000f0 /* CPU revision */ +#define CPUREV_REVISION_S 4 +#define CPUREV_ICWAY 0x00000f00 /* number of instruction cache ways */ +#define CPUREV_ICWAY_S 8 +#define CPUREV_ICSIZE 0x0000f000 /* instruction cache way size */ +#define CPUREV_ICSIZE_S 12 +#define CPUREV_DCWAY 0x000f0000 /* number of data cache ways */ +#define CPUREV_DCWAY_S 16 +#define CPUREV_DCSIZE 0x00f00000 /* data cache way size */ +#define CPUREV_DCSIZE_S 20 +#define CPUREV_FPUTYPE 0x0f000000 /* FPU core type */ +#define CPUREV_FPUTYPE_NONE 0x00000000 /* - no FPU core implemented */ +#define CPUREV_OCDCTG 0xf0000000 /* on-chip debug function category */ + +#define DCR __SYSREG(0xc0000030, u16) /* Debug control register */ + +/* interrupt/exception control registers */ +#define IVAR0 __SYSREG(0xc0000000, u16) /* interrupt vector 0 */ +#define IVAR1 __SYSREG(0xc0000004, u16) /* interrupt vector 1 */ +#define IVAR2 __SYSREG(0xc0000008, u16) /* interrupt vector 2 */ +#define IVAR3 __SYSREG(0xc000000c, u16) /* interrupt vector 3 */ +#define IVAR4 __SYSREG(0xc0000010, u16) /* interrupt vector 4 */ +#define IVAR5 __SYSREG(0xc0000014, u16) /* interrupt vector 5 */ +#define IVAR6 __SYSREG(0xc0000018, u16) /* interrupt vector 6 */ + +#define TBR __SYSREG(0xc0000024, u32) /* Trap table base */ +#define TBR_TB 0xff000000 /* table base address bits 31-24 */ +#define TBR_INT_CODE 0x00ffffff /* interrupt code */ + +#define DEAR __SYSREG(0xc0000038, u32) /* Data access exception address */ + +#define sISR __SYSREG(0xc0000044, u32) /* Supervisor interrupt status */ +#define sISR_IRQICE 0x00000001 /* ICE interrupt */ +#define sISR_ISTEP 0x00000002 /* single step interrupt */ +#define sISR_MISSA 0x00000004 /* memory access address misalignment fault */ +#define sISR_UNIMP 0x00000008 /* unimplemented instruction execution fault */ +#define sISR_PIEXE 0x00000010 /* program interrupt */ +#define sISR_MEMERR 0x00000020 /* illegal memory access fault */ +#define sISR_IBREAK 0x00000040 /* instraction break interrupt */ +#define sISR_DBSRL 0x00000080 /* debug serial interrupt */ +#define sISR_PERIDB 0x00000100 /* peripheral debug interrupt */ +#define sISR_EXUNIMP 0x00000200 /* unimplemented ex-instruction execution fault */ +#define sISR_OBREAK 0x00000400 /* operand break interrupt */ +#define sISR_PRIV 0x00000800 /* privileged instruction execution fault */ +#define sISR_BUSERR 0x00001000 /* bus error fault */ +#define sISR_DBLFT 0x00002000 /* double fault */ +#define sISR_DBG 0x00008000 /* debug reserved interrupt */ +#define sISR_ITMISS 0x00010000 /* instruction TLB miss */ +#define sISR_DTMISS 0x00020000 /* data TLB miss */ +#define sISR_ITEX 0x00040000 /* instruction TLB access exception */ +#define sISR_DTEX 0x00080000 /* data TLB access exception */ +#define sISR_ILGIA 0x00100000 /* illegal instruction access exception */ +#define sISR_ILGDA 0x00200000 /* illegal data access exception */ +#define sISR_IOIA 0x00400000 /* internal I/O space instruction access excep */ +#define sISR_PRIVA 0x00800000 /* privileged space instruction access excep */ +#define sISR_PRIDA 0x01000000 /* privileged space data access excep */ +#define sISR_DISA 0x02000000 /* data space instruction access excep */ +#define sISR_SYSC 0x04000000 /* system call instruction excep */ +#define sISR_FPUD 0x08000000 /* FPU disabled excep */ +#define sISR_FPUUI 0x10000000 /* FPU unimplemented instruction excep */ +#define sISR_FPUOP 0x20000000 /* FPU operation excep */ +#define sISR_NE 0x80000000 /* multiple synchronous exceptions excep */ + +/* cache control registers */ +#define CHCTR __SYSREG(0xc0000070, u16) /* cache control */ +#define CHCTR_ICEN 0x0001 /* instruction cache enable */ +#define CHCTR_DCEN 0x0002 /* data cache enable */ +#define CHCTR_ICBUSY 0x0004 /* instruction cache busy */ +#define CHCTR_DCBUSY 0x0008 /* data cache busy */ +#define CHCTR_ICINV 0x0010 /* instruction cache invalidate */ +#define CHCTR_DCINV 0x0020 /* data cache invalidate */ +#define CHCTR_DCWTMD 0x0040 /* data cache writing mode */ +#define CHCTR_DCWTMD_WRBACK 0x0000 /* - write back mode */ +#define CHCTR_DCWTMD_WRTHROUGH 0x0040 /* - write through mode */ +#define CHCTR_DCALMD 0x0080 /* data cache allocation mode */ +#define CHCTR_ICWMD 0x0f00 /* instruction cache way mode */ +#define CHCTR_DCWMD 0xf000 /* data cache way mode */ + +#ifdef CONFIG_AM34_2 +#define ICIVCR __SYSREG(0xc0000c00, u32) /* icache area invalidate control */ +#define ICIVCR_ICIVBSY 0x00000008 /* icache area invalidate busy */ +#define ICIVCR_ICI 0x00000001 /* icache area invalidate */ + +#define ICIVMR __SYSREG(0xc0000c04, u32) /* icache area invalidate mask */ + +#define DCPGCR __SYSREG(0xc0000c10, u32) /* data cache area purge control */ +#define DCPGCR_DCPGBSY 0x00000008 /* data cache area purge busy */ +#define DCPGCR_DCP 0x00000002 /* data cache area purge */ +#define DCPGCR_DCI 0x00000001 /* data cache area invalidate */ + +#define DCPGMR __SYSREG(0xc0000c14, u32) /* data cache area purge mask */ +#endif /* CONFIG_AM34_2 */ + +/* MMU control registers */ +#define MMUCTR __SYSREG(0xc0000090, u32) /* MMU control register */ +#define MMUCTR_IRP 0x0000003f /* instruction TLB replace pointer */ +#define MMUCTR_ITE 0x00000040 /* instruction TLB enable */ +#define MMUCTR_IIV 0x00000080 /* instruction TLB invalidate */ +#define MMUCTR_ITL 0x00000700 /* instruction TLB lock pointer */ +#define MMUCTR_ITL_NOLOCK 0x00000000 /* - no lock */ +#define MMUCTR_ITL_LOCK0 0x00000100 /* - entry 0 locked */ +#define MMUCTR_ITL_LOCK0_1 0x00000200 /* - entry 0-1 locked */ +#define MMUCTR_ITL_LOCK0_3 0x00000300 /* - entry 0-3 locked */ +#define MMUCTR_ITL_LOCK0_7 0x00000400 /* - entry 0-7 locked */ +#define MMUCTR_ITL_LOCK0_15 0x00000500 /* - entry 0-15 locked */ +#define MMUCTR_CE 0x00008000 /* cacheable bit enable */ +#define MMUCTR_DRP 0x003f0000 /* data TLB replace pointer */ +#define MMUCTR_DTE 0x00400000 /* data TLB enable */ +#define MMUCTR_DIV 0x00800000 /* data TLB invalidate */ +#define MMUCTR_DTL 0x07000000 /* data TLB lock pointer */ +#define MMUCTR_DTL_NOLOCK 0x00000000 /* - no lock */ +#define MMUCTR_DTL_LOCK0 0x01000000 /* - entry 0 locked */ +#define MMUCTR_DTL_LOCK0_1 0x02000000 /* - entry 0-1 locked */ +#define MMUCTR_DTL_LOCK0_3 0x03000000 /* - entry 0-3 locked */ +#define MMUCTR_DTL_LOCK0_7 0x04000000 /* - entry 0-7 locked */ +#define MMUCTR_DTL_LOCK0_15 0x05000000 /* - entry 0-15 locked */ +#ifdef CONFIG_AM34_2 +#define MMUCTR_WTE 0x80000000 /* write-through cache TLB entry bit enable */ +#endif + +#define PIDR __SYSREG(0xc0000094, u16) /* PID register */ +#define PIDR_PID 0x00ff /* process identifier */ + +#define PTBR __SYSREG(0xc0000098, unsigned long) /* Page table base register */ + +#define IPTEL __SYSREG(0xc00000a0, u32) /* instruction TLB entry */ +#define DPTEL __SYSREG(0xc00000b0, u32) /* data TLB entry */ +#define xPTEL_V 0x00000001 /* TLB entry valid */ +#define xPTEL_UNUSED1 0x00000002 /* unused bit */ +#define xPTEL_UNUSED2 0x00000004 /* unused bit */ +#define xPTEL_C 0x00000008 /* cached if set */ +#define xPTEL_PV 0x00000010 /* page valid */ +#define xPTEL_D 0x00000020 /* dirty */ +#define xPTEL_PR 0x000001c0 /* page protection */ +#define xPTEL_PR_ROK 0x00000000 /* - R/O kernel */ +#define xPTEL_PR_RWK 0x00000100 /* - R/W kernel */ +#define xPTEL_PR_ROK_ROU 0x00000080 /* - R/O kernel and R/O user */ +#define xPTEL_PR_RWK_ROU 0x00000180 /* - R/W kernel and R/O user */ +#define xPTEL_PR_RWK_RWU 0x000001c0 /* - R/W kernel and R/W user */ +#define xPTEL_G 0x00000200 /* global (use PID if 0) */ +#define xPTEL_PS 0x00000c00 /* page size */ +#define xPTEL_PS_4Kb 0x00000000 /* - 4Kb page */ +#define xPTEL_PS_128Kb 0x00000400 /* - 128Kb page */ +#define xPTEL_PS_1Kb 0x00000800 /* - 1Kb page */ +#define xPTEL_PS_4Mb 0x00000c00 /* - 4Mb page */ +#define xPTEL_PPN 0xfffff006 /* physical page number */ + +#define IPTEU __SYSREG(0xc00000a4, u32) /* instruction TLB virtual addr */ +#define DPTEU __SYSREG(0xc00000b4, u32) /* data TLB virtual addr */ +#define xPTEU_VPN 0xfffffc00 /* virtual page number */ +#define xPTEU_PID 0x000000ff /* process identifier to which applicable */ + +#define IPTEL2 __SYSREG(0xc00000a8, u32) /* instruction TLB entry */ +#define DPTEL2 __SYSREG(0xc00000b8, u32) /* data TLB entry */ +#define xPTEL2_V 0x00000001 /* TLB entry valid */ +#define xPTEL2_C 0x00000002 /* cacheable */ +#define xPTEL2_PV 0x00000004 /* page valid */ +#define xPTEL2_D 0x00000008 /* dirty */ +#define xPTEL2_PR 0x00000070 /* page protection */ +#define xPTEL2_PR_ROK 0x00000000 /* - R/O kernel */ +#define xPTEL2_PR_RWK 0x00000040 /* - R/W kernel */ +#define xPTEL2_PR_ROK_ROU 0x00000020 /* - R/O kernel and R/O user */ +#define xPTEL2_PR_RWK_ROU 0x00000060 /* - R/W kernel and R/O user */ +#define xPTEL2_PR_RWK_RWU 0x00000070 /* - R/W kernel and R/W user */ +#define xPTEL2_G 0x00000080 /* global (use PID if 0) */ +#define xPTEL2_PS 0x00000300 /* page size */ +#define xPTEL2_PS_4Kb 0x00000000 /* - 4Kb page */ +#define xPTEL2_PS_128Kb 0x00000100 /* - 128Kb page */ +#define xPTEL2_PS_1Kb 0x00000200 /* - 1Kb page */ +#define xPTEL2_PS_4Mb 0x00000300 /* - 4Mb page */ +#define xPTEL2_CWT 0x00000400 /* cacheable write-through */ +#define xPTEL2_UNUSED1 0x00000800 /* unused bit (broadcast mask) */ +#define xPTEL2_PPN 0xfffff000 /* physical page number */ + +#define xPTEL2_V_BIT 0 /* bit numbers corresponding to above masks */ +#define xPTEL2_C_BIT 1 +#define xPTEL2_PV_BIT 2 +#define xPTEL2_D_BIT 3 +#define xPTEL2_G_BIT 7 +#define xPTEL2_UNUSED1_BIT 11 + +#define MMUFCR __SYSREGC(0xc000009c, u32) /* MMU exception cause */ +#define MMUFCR_IFC __SYSREGC(0xc000009c, u16) /* MMU instruction excep cause */ +#define MMUFCR_DFC __SYSREGC(0xc000009e, u16) /* MMU data exception cause */ +#define MMUFCR_xFC_TLBMISS 0x0001 /* TLB miss flag */ +#define MMUFCR_xFC_INITWR 0x0002 /* initial write excep flag */ +#define MMUFCR_xFC_PGINVAL 0x0004 /* page invalid excep flag */ +#define MMUFCR_xFC_PROTVIOL 0x0008 /* protection violation excep flag */ +#define MMUFCR_xFC_ACCESS 0x0010 /* access level flag */ +#define MMUFCR_xFC_ACCESS_USR 0x0000 /* - user mode */ +#define MMUFCR_xFC_ACCESS_SR 0x0010 /* - supervisor mode */ +#define MMUFCR_xFC_TYPE 0x0020 /* access type flag */ +#define MMUFCR_xFC_TYPE_READ 0x0000 /* - read */ +#define MMUFCR_xFC_TYPE_WRITE 0x0020 /* - write */ +#define MMUFCR_xFC_PR 0x01c0 /* page protection flag */ +#define MMUFCR_xFC_PR_ROK 0x0000 /* - R/O kernel */ +#define MMUFCR_xFC_PR_RWK 0x0100 /* - R/W kernel */ +#define MMUFCR_xFC_PR_ROK_ROU 0x0080 /* - R/O kernel and R/O user */ +#define MMUFCR_xFC_PR_RWK_ROU 0x0180 /* - R/W kernel and R/O user */ +#define MMUFCR_xFC_PR_RWK_RWU 0x01c0 /* - R/W kernel and R/W user */ +#define MMUFCR_xFC_ILLADDR 0x0200 /* illegal address excep flag */ + +#ifdef CONFIG_MN10300_HAS_ATOMIC_OPS_UNIT +/* atomic operation registers */ +#define AAR __SYSREG(0xc0000a00, u32) /* cacheable address */ +#define AAR2 __SYSREG(0xc0000a04, u32) /* uncacheable address */ +#define ADR __SYSREG(0xc0000a08, u32) /* data */ +#define ASR __SYSREG(0xc0000a0c, u32) /* status */ +#define AARU __SYSREG(0xd400aa00, u32) /* user address */ +#define ADRU __SYSREG(0xd400aa08, u32) /* user data */ +#define ASRU __SYSREG(0xd400aa0c, u32) /* user status */ + +#define ASR_RW 0x00000008 /* read */ +#define ASR_BW 0x00000004 /* bus error */ +#define ASR_IW 0x00000002 /* interrupt */ +#define ASR_LW 0x00000001 /* bus lock */ + +#define ASRU_RW ASR_RW /* read */ +#define ASRU_BW ASR_BW /* bus error */ +#define ASRU_IW ASR_IW /* interrupt */ +#define ASRU_LW ASR_LW /* bus lock */ + +/* in inline ASM, we stick the base pointer in to a reg and use offsets from + * it */ +#define ATOMIC_OPS_BASE_ADDR 0xc0000a00 +#ifndef __ASSEMBLY__ +asm( + "_AAR = 0\n" + "_AAR2 = 4\n" + "_ADR = 8\n" + "_ASR = 12\n"); +#else +#define _AAR 0 +#define _AAR2 4 +#define _ADR 8 +#define _ASR 12 +#endif + +/* physical page address for userspace atomic operations registers */ +#define USER_ATOMIC_OPS_PAGE_ADDR 0xd400a000 + +#endif /* CONFIG_MN10300_HAS_ATOMIC_OPS_UNIT */ + +#endif /* __KERNEL__ */ + +#endif /* _ASM_CPU_REGS_H */ diff --git a/arch/mn10300/include/asm/cputime.h b/arch/mn10300/include/asm/cputime.h new file mode 100644 index 00000000..6d68ad7e --- /dev/null +++ b/arch/mn10300/include/asm/cputime.h @@ -0,0 +1 @@ +#include <asm-generic/cputime.h> diff --git a/arch/mn10300/include/asm/current.h b/arch/mn10300/include/asm/current.h new file mode 100644 index 00000000..ca6027d8 --- /dev/null +++ b/arch/mn10300/include/asm/current.h @@ -0,0 +1,37 @@ +/* MN10300 Current task structure accessor + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_CURRENT_H +#define _ASM_CURRENT_H + +#include <linux/thread_info.h> + +/* + * dedicate E2 to keeping the current task pointer + */ +#ifdef CONFIG_MN10300_CURRENT_IN_E2 + +register struct task_struct *const current asm("e2") __attribute__((used)); + +#define get_current() current + +extern struct task_struct *__current; + +#else +static inline __attribute__((const)) +struct task_struct *get_current(void) +{ + return current_thread_info()->task; +} + +#define current get_current() +#endif + +#endif /* _ASM_CURRENT_H */ diff --git a/arch/mn10300/include/asm/debugger.h b/arch/mn10300/include/asm/debugger.h new file mode 100644 index 00000000..e1d3b083 --- /dev/null +++ b/arch/mn10300/include/asm/debugger.h @@ -0,0 +1,43 @@ +/* Kernel debugger for MN10300 + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _ASM_DEBUGGER_H +#define _ASM_DEBUGGER_H + +#if defined(CONFIG_KERNEL_DEBUGGER) + +extern int debugger_intercept(enum exception_code, int, int, struct pt_regs *); +extern int at_debugger_breakpoint(struct pt_regs *); + +#ifndef CONFIG_MN10300_DEBUGGER_CACHE_NO_FLUSH +extern void debugger_local_cache_flushinv(void); +extern void debugger_local_cache_flushinv_one(u8 *); +#else +static inline void debugger_local_cache_flushinv(void) {} +static inline void debugger_local_cache_flushinv_one(u8 *addr) {} +#endif + +#else /* CONFIG_KERNEL_DEBUGGER */ + +static inline int debugger_intercept(enum exception_code excep, + int signo, int si_code, + struct pt_regs *regs) +{ + return 0; +} + +static inline int at_debugger_breakpoint(struct pt_regs *regs) +{ + return 0; +} + +#endif /* CONFIG_KERNEL_DEBUGGER */ +#endif /* _ASM_DEBUGGER_H */ diff --git a/arch/mn10300/include/asm/delay.h b/arch/mn10300/include/asm/delay.h new file mode 100644 index 00000000..34517b35 --- /dev/null +++ b/arch/mn10300/include/asm/delay.h @@ -0,0 +1,19 @@ +/* MN10300 Uninterruptible delay routines + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_DELAY_H +#define _ASM_DELAY_H + +extern void __udelay(unsigned long usecs); +extern void __delay(unsigned long loops); + +#define udelay(n) __udelay(n) + +#endif /* _ASM_DELAY_H */ diff --git a/arch/mn10300/include/asm/device.h b/arch/mn10300/include/asm/device.h new file mode 100644 index 00000000..f0a4c256 --- /dev/null +++ b/arch/mn10300/include/asm/device.h @@ -0,0 +1 @@ +#include <asm-generic/device.h> diff --git a/arch/mn10300/include/asm/div64.h b/arch/mn10300/include/asm/div64.h new file mode 100644 index 00000000..503efab2 --- /dev/null +++ b/arch/mn10300/include/asm/div64.h @@ -0,0 +1,115 @@ +/* MN10300 64-bit division + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_DIV64 +#define _ASM_DIV64 + +#include <linux/types.h> + +extern void ____unhandled_size_in_do_div___(void); + +/* + * Beginning with gcc 4.6, the MDR register is represented explicitly. We + * must, therefore, at least explicitly clobber the register when we make + * changes to it. The following assembly fragments *could* be rearranged in + * order to leave the moves to/from the MDR register to the compiler, but the + * gains would be minimal at best. + */ +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +# define CLOBBER_MDR_CC "mdr", "cc" +#else +# define CLOBBER_MDR_CC "cc" +#endif + +/* + * divide n by base, leaving the result in n and returning the remainder + * - we can do this quite efficiently on the MN10300 by cascading the divides + * through the MDR register + */ +#define do_div(n, base) \ +({ \ + unsigned __rem = 0; \ + if (sizeof(n) <= 4) { \ + asm("mov %1,mdr \n" \ + "divu %2,%0 \n" \ + "mov mdr,%1 \n" \ + : "+r"(n), "=d"(__rem) \ + : "r"(base), "1"(__rem) \ + : CLOBBER_MDR_CC \ + ); \ + } else if (sizeof(n) <= 8) { \ + union { \ + unsigned long long l; \ + u32 w[2]; \ + } __quot; \ + __quot.l = n; \ + asm("mov %0,mdr \n" /* MDR = 0 */ \ + "divu %3,%1 \n" \ + /* __quot.MSL = __div.MSL / base, */ \ + /* MDR = MDR:__div.MSL % base */ \ + "divu %3,%2 \n" \ + /* __quot.LSL = MDR:__div.LSL / base, */ \ + /* MDR = MDR:__div.LSL % base */ \ + "mov mdr,%0 \n" \ + : "=d"(__rem), "=r"(__quot.w[1]), "=r"(__quot.w[0]) \ + : "r"(base), "0"(__rem), "1"(__quot.w[1]), \ + "2"(__quot.w[0]) \ + : CLOBBER_MDR_CC \ + ); \ + n = __quot.l; \ + } else { \ + ____unhandled_size_in_do_div___(); \ + } \ + __rem; \ +}) + +/* + * do an unsigned 32-bit multiply and divide with intermediate 64-bit product + * so as not to lose accuracy + * - we use the MDR register to hold the MSW of the product + */ +static inline __attribute__((const)) +unsigned __muldiv64u(unsigned val, unsigned mult, unsigned div) +{ + unsigned result; + + asm("mulu %2,%0 \n" /* MDR:val = val*mult */ + "divu %3,%0 \n" /* val = MDR:val/div; + * MDR = MDR:val%div */ + : "=r"(result) + : "0"(val), "ir"(mult), "r"(div) + : CLOBBER_MDR_CC + ); + + return result; +} + +/* + * do a signed 32-bit multiply and divide with intermediate 64-bit product so + * as not to lose accuracy + * - we use the MDR register to hold the MSW of the product + */ +static inline __attribute__((const)) +signed __muldiv64s(signed val, signed mult, signed div) +{ + signed result; + + asm("mul %2,%0 \n" /* MDR:val = val*mult */ + "div %3,%0 \n" /* val = MDR:val/div; + * MDR = MDR:val%div */ + : "=r"(result) + : "0"(val), "ir"(mult), "r"(div) + : CLOBBER_MDR_CC + ); + + return result; +} + +#endif /* _ASM_DIV64 */ diff --git a/arch/mn10300/include/asm/dma-mapping.h b/arch/mn10300/include/asm/dma-mapping.h new file mode 100644 index 00000000..c1be4397 --- /dev/null +++ b/arch/mn10300/include/asm/dma-mapping.h @@ -0,0 +1,171 @@ +/* DMA mapping routines for the MN10300 arch + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_DMA_MAPPING_H +#define _ASM_DMA_MAPPING_H + +#include <linux/mm.h> +#include <linux/scatterlist.h> + +#include <asm/cache.h> +#include <asm/io.h> + +/* + * See Documentation/DMA-API.txt for the description of how the + * following DMA API should work. + */ + +extern void *dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, int flag); + +extern void dma_free_coherent(struct device *dev, size_t size, + void *vaddr, dma_addr_t 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 +dma_addr_t dma_map_single(struct device *dev, void *ptr, size_t size, + enum dma_data_direction direction) +{ + BUG_ON(direction == DMA_NONE); + mn10300_dcache_flush_inv(); + return virt_to_bus(ptr); +} + +static inline +void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, + enum dma_data_direction direction) +{ + BUG_ON(direction == DMA_NONE); +} + +static inline +int dma_map_sg(struct device *dev, struct scatterlist *sglist, int nents, + enum dma_data_direction direction) +{ + struct scatterlist *sg; + int i; + + BUG_ON(!valid_dma_direction(direction)); + WARN_ON(nents == 0 || sglist[0].length == 0); + + for_each_sg(sglist, sg, nents, i) { + BUG_ON(!sg_page(sg)); + + sg->dma_address = sg_phys(sg); + } + + mn10300_dcache_flush_inv(); + return nents; +} + +static inline +void dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nhwentries, + enum dma_data_direction direction) +{ + BUG_ON(!valid_dma_direction(direction)); +} + +static inline +dma_addr_t dma_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + BUG_ON(direction == DMA_NONE); + return page_to_bus(page) + offset; +} + +static inline +void dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size, + enum dma_data_direction direction) +{ + BUG_ON(direction == DMA_NONE); +} + +static inline +void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction direction) +{ +} + +static inline +void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction direction) +{ + mn10300_dcache_flush_inv(); +} + +static inline +void dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ +} + +static inline void +dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + mn10300_dcache_flush_inv(); +} + + +static inline +void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, + int nelems, enum dma_data_direction direction) +{ +} + +static inline +void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, + int nelems, enum dma_data_direction direction) +{ + mn10300_dcache_flush_inv(); +} + +static inline +int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) +{ + return 0; +} + +static inline +int dma_supported(struct device *dev, u64 mask) +{ + /* + * we fall back to GFP_DMA when the mask isn't all 1s, so we can't + * guarantee allocations that must be within a tighter range than + * GFP_DMA + */ + if (mask < 0x00ffffff) + return 0; + return 1; +} + +static inline +int dma_set_mask(struct device *dev, u64 mask) +{ + if (!dev->dma_mask || !dma_supported(dev, mask)) + return -EIO; + + *dev->dma_mask = mask; + return 0; +} + +static inline +void dma_cache_sync(void *vaddr, size_t size, + enum dma_data_direction direction) +{ + mn10300_dcache_flush_inv(); +} + +#endif diff --git a/arch/mn10300/include/asm/dma.h b/arch/mn10300/include/asm/dma.h new file mode 100644 index 00000000..098df2e6 --- /dev/null +++ b/arch/mn10300/include/asm/dma.h @@ -0,0 +1,118 @@ +/* MN10300 ISA DMA handlers and definitions + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_DMA_H +#define _ASM_DMA_H + +#include <asm/system.h> +#include <linux/spinlock.h> +#include <asm/io.h> +#include <linux/delay.h> + +#undef MAX_DMA_CHANNELS /* switch off linux/kernel/dma.c */ +#define MAX_DMA_ADDRESS 0xbfffffff + +extern spinlock_t dma_spin_lock; + +static inline unsigned long claim_dma_lock(void) +{ + unsigned long flags; + spin_lock_irqsave(&dma_spin_lock, flags); + return flags; +} + +static inline void release_dma_lock(unsigned long flags) +{ + spin_unlock_irqrestore(&dma_spin_lock, flags); +} + +/* enable/disable a specific DMA channel */ +static inline void enable_dma(unsigned int dmanr) +{ +} + +static inline void disable_dma(unsigned int dmanr) +{ +} + +/* Clear the 'DMA Pointer Flip Flop'. + * Write 0 for LSB/MSB, 1 for MSB/LSB access. + * Use this once to initialize the FF to a known state. + * After that, keep track of it. :-) + * --- In order to do that, the DMA routines below should --- + * --- only be used while holding the DMA lock ! --- + */ +static inline void clear_dma_ff(unsigned int dmanr) +{ +} + +/* set mode (above) for a specific DMA channel */ +static inline void set_dma_mode(unsigned int dmanr, char mode) +{ +} + +/* Set only the page register bits of the transfer address. + * This is used for successive transfers when we know the contents of + * the lower 16 bits of the DMA current address register, but a 64k boundary + * may have been crossed. + */ +static inline void set_dma_page(unsigned int dmanr, char pagenr) +{ +} + + +/* Set transfer address & page bits for specific DMA channel. + * Assumes dma flipflop is clear. + */ +static inline void set_dma_addr(unsigned int dmanr, unsigned int a) +{ +} + + +/* Set transfer size (max 64k for DMA1..3, 128k for DMA5..7) for + * a specific DMA channel. + * You must ensure the parameters are valid. + * NOTE: from a manual: "the number of transfers is one more + * than the initial word count"! This is taken into account. + * Assumes dma flip-flop is clear. + * NOTE 2: "count" represents _bytes_ and must be even for channels 5-7. + */ +static inline void set_dma_count(unsigned int dmanr, unsigned int count) +{ +} + + +/* Get DMA residue count. After a DMA transfer, this + * should return zero. Reading this while a DMA transfer is + * still in progress will return unpredictable results. + * If called before the channel has been used, it may return 1. + * Otherwise, it returns the number of _bytes_ left to transfer. + * + * Assumes DMA flip-flop is clear. + */ +static inline int get_dma_residue(unsigned int dmanr) +{ + return 0; +} + + +/* These are in kernel/dma.c: */ +extern int request_dma(unsigned int dmanr, const char *device_id); +extern void free_dma(unsigned int dmanr); + +/* From PCI */ + +#ifdef CONFIG_PCI +extern int isa_dma_bridge_buggy; +#else +#define isa_dma_bridge_buggy (0) +#endif + +#endif /* _ASM_DMA_H */ diff --git a/arch/mn10300/include/asm/dmactl-regs.h b/arch/mn10300/include/asm/dmactl-regs.h new file mode 100644 index 00000000..80337b33 --- /dev/null +++ b/arch/mn10300/include/asm/dmactl-regs.h @@ -0,0 +1,16 @@ +/* MN10300 on-board DMA controller registers + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_DMACTL_REGS_H +#define _ASM_DMACTL_REGS_H + +#include <proc/dmactl-regs.h> + +#endif /* _ASM_DMACTL_REGS_H */ diff --git a/arch/mn10300/include/asm/elf.h b/arch/mn10300/include/asm/elf.h new file mode 100644 index 00000000..8157c926 --- /dev/null +++ b/arch/mn10300/include/asm/elf.h @@ -0,0 +1,157 @@ +/* MN10300 ELF constant and register definitions + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_ELF_H +#define _ASM_ELF_H + +#include <linux/utsname.h> +#include <asm/ptrace.h> +#include <asm/user.h> + +/* + * AM33 relocations + */ +#define R_MN10300_NONE 0 /* No reloc. */ +#define R_MN10300_32 1 /* Direct 32 bit. */ +#define R_MN10300_16 2 /* Direct 16 bit. */ +#define R_MN10300_8 3 /* Direct 8 bit. */ +#define R_MN10300_PCREL32 4 /* PC-relative 32-bit. */ +#define R_MN10300_PCREL16 5 /* PC-relative 16-bit signed. */ +#define R_MN10300_PCREL8 6 /* PC-relative 8-bit signed. */ +#define R_MN10300_24 9 /* Direct 24 bit. */ +#define R_MN10300_RELATIVE 23 /* Adjust by program base. */ +#define R_MN10300_SYM_DIFF 33 /* Adjustment when relaxing. */ +#define R_MN10300_ALIGN 34 /* Alignment requirement. */ + +/* + * AM33/AM34 HW Capabilities + */ +#define HWCAP_MN10300_ATOMIC_OP_UNIT 1 /* Has AM34 Atomic Operations */ + + +/* + * ELF register definitions.. + */ +typedef unsigned long elf_greg_t; + +#define ELF_NGREG ((sizeof(struct pt_regs) / sizeof(elf_greg_t)) - 1) +typedef elf_greg_t elf_gregset_t[ELF_NGREG]; + +#define ELF_NFPREG 32 +typedef float elf_fpreg_t; + +typedef struct { + elf_fpreg_t fpregs[ELF_NFPREG]; + u_int32_t fpcr; +} elf_fpregset_t; + +/* + * This is used to ensure we don't load something for the wrong architecture + */ +#define elf_check_arch(x) \ + (((x)->e_machine == EM_CYGNUS_MN10300) || \ + ((x)->e_machine == EM_MN10300)) + +/* + * These are used to set parameters in the core dumps. + */ +#define ELF_CLASS ELFCLASS32 +#define ELF_DATA ELFDATA2LSB +#define ELF_ARCH EM_MN10300 + +/* + * ELF process initialiser + */ +#define ELF_PLAT_INIT(_r, load_addr) \ +do { \ + struct pt_regs *_ur = current->thread.uregs; \ + _ur->a3 = 0; _ur->a2 = 0; _ur->d3 = 0; _ur->d2 = 0; \ + _ur->mcvf = 0; _ur->mcrl = 0; _ur->mcrh = 0; _ur->mdrq = 0; \ + _ur->e1 = 0; _ur->e0 = 0; _ur->e7 = 0; _ur->e6 = 0; \ + _ur->e5 = 0; _ur->e4 = 0; _ur->e3 = 0; _ur->e2 = 0; \ + _ur->lar = 0; _ur->lir = 0; _ur->mdr = 0; \ + _ur->a1 = 0; _ur->a0 = 0; _ur->d1 = 0; _ur->d0 = 0; \ +} while (0) + +#define CORE_DUMP_USE_REGSET +#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. + * - must clear the VMALLOC area + */ +#define ELF_ET_DYN_BASE 0x04000000 + +/* + * regs is struct pt_regs, pr_reg is elf_gregset_t (which is + * now struct user_regs, they are different) + * - ELF_CORE_COPY_REGS has been guessed, and may be wrong + */ +#define ELF_CORE_COPY_REGS(pr_reg, regs) \ +do { \ + pr_reg[0] = regs->a3; \ + pr_reg[1] = regs->a2; \ + pr_reg[2] = regs->d3; \ + pr_reg[3] = regs->d2; \ + pr_reg[4] = regs->mcvf; \ + pr_reg[5] = regs->mcrl; \ + pr_reg[6] = regs->mcrh; \ + pr_reg[7] = regs->mdrq; \ + pr_reg[8] = regs->e1; \ + pr_reg[9] = regs->e0; \ + pr_reg[10] = regs->e7; \ + pr_reg[11] = regs->e6; \ + pr_reg[12] = regs->e5; \ + pr_reg[13] = regs->e4; \ + pr_reg[14] = regs->e3; \ + pr_reg[15] = regs->e2; \ + pr_reg[16] = regs->sp; \ + pr_reg[17] = regs->lar; \ + pr_reg[18] = regs->lir; \ + pr_reg[19] = regs->mdr; \ + pr_reg[20] = regs->a1; \ + pr_reg[21] = regs->a0; \ + pr_reg[22] = regs->d1; \ + pr_reg[23] = regs->d0; \ + pr_reg[24] = regs->orig_d0; \ + pr_reg[25] = regs->epsw; \ + pr_reg[26] = regs->pc; \ +} while (0); + +/* + * This yields a mask that user programs can use to figure out what + * instruction set this CPU supports. This could be done in user space, + * but it's not easy, and we've already done it here. + */ +#ifdef CONFIG_MN10300_HAS_ATOMIC_OPS_UNIT +#define ELF_HWCAP (HWCAP_MN10300_ATOMIC_OP_UNIT) +#else +#define ELF_HWCAP (0) +#endif + +/* + * 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. + * + * For the moment, we have only optimizations for the Intel generations, + * but that could change... + */ +#define ELF_PLATFORM (NULL) + +#ifdef __KERNEL__ +#define SET_PERSONALITY(ex) set_personality(PER_LINUX) +#endif + +#endif /* _ASM_ELF_H */ diff --git a/arch/mn10300/include/asm/emergency-restart.h b/arch/mn10300/include/asm/emergency-restart.h new file mode 100644 index 00000000..3711bd9d --- /dev/null +++ b/arch/mn10300/include/asm/emergency-restart.h @@ -0,0 +1 @@ +#include <asm-generic/emergency-restart.h> diff --git a/arch/mn10300/include/asm/errno.h b/arch/mn10300/include/asm/errno.h new file mode 100644 index 00000000..4c82b503 --- /dev/null +++ b/arch/mn10300/include/asm/errno.h @@ -0,0 +1 @@ +#include <asm-generic/errno.h> diff --git a/arch/mn10300/include/asm/exceptions.h b/arch/mn10300/include/asm/exceptions.h new file mode 100644 index 00000000..ca3e2050 --- /dev/null +++ b/arch/mn10300/include/asm/exceptions.h @@ -0,0 +1,121 @@ +/* MN10300 Microcontroller core exceptions + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_EXCEPTIONS_H +#define _ASM_EXCEPTIONS_H + +#include <linux/linkage.h> + +/* + * define the breakpoint instruction opcode to use + * - note that the JTAG unit steals 0xFF, so you can't use JTAG and GDBSTUB at + * the same time. + */ +#define GDBSTUB_BKPT 0xFF + +#ifndef __ASSEMBLY__ + +/* + * enumeration of exception codes (as extracted from TBR MSW) + */ +enum exception_code { + EXCEP_RESET = 0x000000, /* reset */ + + /* MMU exceptions */ + EXCEP_ITLBMISS = 0x000100, /* instruction TLB miss */ + EXCEP_DTLBMISS = 0x000108, /* data TLB miss */ + EXCEP_IAERROR = 0x000110, /* instruction address */ + EXCEP_DAERROR = 0x000118, /* data address */ + + /* system exceptions */ + EXCEP_TRAP = 0x000128, /* program interrupt (PI instruction) */ + EXCEP_ISTEP = 0x000130, /* single step */ + EXCEP_IBREAK = 0x000150, /* instruction breakpoint */ + EXCEP_OBREAK = 0x000158, /* operand breakpoint */ + EXCEP_PRIVINS = 0x000160, /* privileged instruction execution */ + EXCEP_UNIMPINS = 0x000168, /* unimplemented instruction execution */ + EXCEP_UNIMPEXINS = 0x000170, /* unimplemented extended instruction execution */ + EXCEP_MEMERR = 0x000178, /* illegal memory access */ + EXCEP_MISALIGN = 0x000180, /* misalignment */ + EXCEP_BUSERROR = 0x000188, /* bus error */ + EXCEP_ILLINSACC = 0x000190, /* illegal instruction access */ + EXCEP_ILLDATACC = 0x000198, /* illegal data access */ + EXCEP_IOINSACC = 0x0001a0, /* I/O space instruction access */ + EXCEP_PRIVINSACC = 0x0001a8, /* privileged space instruction access */ + EXCEP_PRIVDATACC = 0x0001b0, /* privileged space data access */ + EXCEP_DATINSACC = 0x0001b8, /* data space instruction access */ + EXCEP_DOUBLE_FAULT = 0x000200, /* double fault */ + + /* FPU exceptions */ + EXCEP_FPU_DISABLED = 0x0001c0, /* FPU disabled */ + EXCEP_FPU_UNIMPINS = 0x0001c8, /* FPU unimplemented operation */ + EXCEP_FPU_OPERATION = 0x0001d0, /* FPU operation */ + + /* interrupts */ + EXCEP_WDT = 0x000240, /* watchdog timer overflow */ + EXCEP_NMI = 0x000248, /* non-maskable interrupt */ + EXCEP_IRQ_LEVEL0 = 0x000280, /* level 0 maskable interrupt */ + EXCEP_IRQ_LEVEL1 = 0x000288, /* level 1 maskable interrupt */ + EXCEP_IRQ_LEVEL2 = 0x000290, /* level 2 maskable interrupt */ + EXCEP_IRQ_LEVEL3 = 0x000298, /* level 3 maskable interrupt */ + EXCEP_IRQ_LEVEL4 = 0x0002a0, /* level 4 maskable interrupt */ + EXCEP_IRQ_LEVEL5 = 0x0002a8, /* level 5 maskable interrupt */ + EXCEP_IRQ_LEVEL6 = 0x0002b0, /* level 6 maskable interrupt */ + + /* system calls */ + EXCEP_SYSCALL0 = 0x000300, /* system call 0 */ + EXCEP_SYSCALL1 = 0x000308, /* system call 1 */ + EXCEP_SYSCALL2 = 0x000310, /* system call 2 */ + EXCEP_SYSCALL3 = 0x000318, /* system call 3 */ + EXCEP_SYSCALL4 = 0x000320, /* system call 4 */ + EXCEP_SYSCALL5 = 0x000328, /* system call 5 */ + EXCEP_SYSCALL6 = 0x000330, /* system call 6 */ + EXCEP_SYSCALL7 = 0x000338, /* system call 7 */ + EXCEP_SYSCALL8 = 0x000340, /* system call 8 */ + EXCEP_SYSCALL9 = 0x000348, /* system call 9 */ + EXCEP_SYSCALL10 = 0x000350, /* system call 10 */ + EXCEP_SYSCALL11 = 0x000358, /* system call 11 */ + EXCEP_SYSCALL12 = 0x000360, /* system call 12 */ + EXCEP_SYSCALL13 = 0x000368, /* system call 13 */ + EXCEP_SYSCALL14 = 0x000370, /* system call 14 */ + EXCEP_SYSCALL15 = 0x000378, /* system call 15 */ +}; + +extern void __set_intr_stub(enum exception_code code, void *handler); +extern void set_intr_stub(enum exception_code code, void *handler); + +struct pt_regs; + +extern asmlinkage void __common_exception(void); +extern asmlinkage void itlb_miss(void); +extern asmlinkage void dtlb_miss(void); +extern asmlinkage void itlb_aerror(void); +extern asmlinkage void dtlb_aerror(void); +extern asmlinkage void raw_bus_error(void); +extern asmlinkage void double_fault(void); +extern asmlinkage int system_call(struct pt_regs *); +extern asmlinkage void nmi(struct pt_regs *, enum exception_code); +extern asmlinkage void uninitialised_exception(struct pt_regs *, + enum exception_code); +extern asmlinkage void irq_handler(void); +extern asmlinkage void profile_handler(void); +extern asmlinkage void nmi_handler(void); +extern asmlinkage void misalignment(struct pt_regs *, enum exception_code); + +extern void die(const char *, struct pt_regs *, enum exception_code) + ATTRIB_NORET; + +extern int die_if_no_fixup(const char *, struct pt_regs *, enum exception_code); + +#define NUM2EXCEP_IRQ_LEVEL(num) (EXCEP_IRQ_LEVEL0 + (num) * 8) + +#endif /* __ASSEMBLY__ */ + +#endif /* _ASM_EXCEPTIONS_H */ diff --git a/arch/mn10300/include/asm/fb.h b/arch/mn10300/include/asm/fb.h new file mode 100644 index 00000000..697b24a9 --- /dev/null +++ b/arch/mn10300/include/asm/fb.h @@ -0,0 +1,23 @@ +/* MN10300 Frame buffer stuff + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_FB_H +#define _ASM_FB_H + +#include <linux/fb.h> + +#define fb_pgprotect(...) do {} while (0) + +static inline int fb_is_primary_device(struct fb_info *info) +{ + return 0; +} + +#endif /* _ASM_FB_H */ diff --git a/arch/mn10300/include/asm/fcntl.h b/arch/mn10300/include/asm/fcntl.h new file mode 100644 index 00000000..46ab12db --- /dev/null +++ b/arch/mn10300/include/asm/fcntl.h @@ -0,0 +1 @@ +#include <asm-generic/fcntl.h> diff --git a/arch/mn10300/include/asm/fpu.h b/arch/mn10300/include/asm/fpu.h new file mode 100644 index 00000000..738ff726 --- /dev/null +++ b/arch/mn10300/include/asm/fpu.h @@ -0,0 +1,134 @@ +/* MN10300 FPU definitions + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * Derived from include/asm-i386/i387.h: Copyright (C) 1994 Linus Torvalds + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_FPU_H +#define _ASM_FPU_H + +#ifndef __ASSEMBLY__ + +#include <linux/sched.h> +#include <asm/exceptions.h> +#include <asm/sigcontext.h> + +#ifdef __KERNEL__ + +extern asmlinkage void fpu_disabled(void); + +#ifdef CONFIG_FPU + +#ifdef CONFIG_LAZY_SAVE_FPU +/* the task that currently owns the FPU state */ +extern struct task_struct *fpu_state_owner; +#endif + +#if (THREAD_USING_FPU & ~0xff) +#error THREAD_USING_FPU must be smaller than 0x100. +#endif + +static inline void set_using_fpu(struct task_struct *tsk) +{ + asm volatile( + "bset %0,(0,%1)" + : + : "i"(THREAD_USING_FPU), "a"(&tsk->thread.fpu_flags) + : "memory", "cc"); +} + +static inline void clear_using_fpu(struct task_struct *tsk) +{ + asm volatile( + "bclr %0,(0,%1)" + : + : "i"(THREAD_USING_FPU), "a"(&tsk->thread.fpu_flags) + : "memory", "cc"); +} + +#define is_using_fpu(tsk) ((tsk)->thread.fpu_flags & THREAD_USING_FPU) + +extern asmlinkage void fpu_kill_state(struct task_struct *); +extern asmlinkage void fpu_exception(struct pt_regs *, enum exception_code); +extern asmlinkage void fpu_init_state(void); +extern asmlinkage void fpu_save(struct fpu_state_struct *); +extern int fpu_setup_sigcontext(struct fpucontext *buf); +extern int fpu_restore_sigcontext(struct fpucontext *buf); + +static inline void unlazy_fpu(struct task_struct *tsk) +{ + preempt_disable(); +#ifndef CONFIG_LAZY_SAVE_FPU + if (tsk->thread.fpu_flags & THREAD_HAS_FPU) { + fpu_save(&tsk->thread.fpu_state); + tsk->thread.fpu_flags &= ~THREAD_HAS_FPU; + tsk->thread.uregs->epsw &= ~EPSW_FE; + } +#else + if (fpu_state_owner == tsk) + fpu_save(&tsk->thread.fpu_state); +#endif + preempt_enable(); +} + +static inline void exit_fpu(void) +{ +#ifdef CONFIG_LAZY_SAVE_FPU + struct task_struct *tsk = current; + + preempt_disable(); + if (fpu_state_owner == tsk) + fpu_state_owner = NULL; + preempt_enable(); +#endif +} + +static inline void flush_fpu(void) +{ + struct task_struct *tsk = current; + + preempt_disable(); +#ifndef CONFIG_LAZY_SAVE_FPU + if (tsk->thread.fpu_flags & THREAD_HAS_FPU) { + tsk->thread.fpu_flags &= ~THREAD_HAS_FPU; + tsk->thread.uregs->epsw &= ~EPSW_FE; + } +#else + if (fpu_state_owner == tsk) { + fpu_state_owner = NULL; + tsk->thread.uregs->epsw &= ~EPSW_FE; + } +#endif + preempt_enable(); + clear_using_fpu(tsk); +} + +#else /* CONFIG_FPU */ + +extern asmlinkage +void unexpected_fpu_exception(struct pt_regs *, enum exception_code); +#define fpu_exception unexpected_fpu_exception + +struct task_struct; +struct fpu_state_struct; +static inline bool is_using_fpu(struct task_struct *tsk) { return false; } +static inline void set_using_fpu(struct task_struct *tsk) {} +static inline void clear_using_fpu(struct task_struct *tsk) {} +static inline void fpu_init_state(void) {} +static inline void fpu_save(struct fpu_state_struct *s) {} +static inline void fpu_kill_state(struct task_struct *tsk) {} +static inline void unlazy_fpu(struct task_struct *tsk) {} +static inline void exit_fpu(void) {} +static inline void flush_fpu(void) {} +static inline int fpu_setup_sigcontext(struct fpucontext *buf) { return 0; } +static inline int fpu_restore_sigcontext(struct fpucontext *buf) { return 0; } +#endif /* CONFIG_FPU */ + +#endif /* __KERNEL__ */ +#endif /* !__ASSEMBLY__ */ +#endif /* _ASM_FPU_H */ diff --git a/arch/mn10300/include/asm/frame.inc b/arch/mn10300/include/asm/frame.inc new file mode 100644 index 00000000..2ee58e3e --- /dev/null +++ b/arch/mn10300/include/asm/frame.inc @@ -0,0 +1,97 @@ +/* MN10300 Microcontroller core system register definitions -*- asm -*- + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_FRAME_INC +#define _ASM_FRAME_INC + +#ifndef __ASSEMBLY__ +#error not for use in C files +#endif + +#ifndef __ASM_OFFSETS_H__ +#include <asm/asm-offsets.h> +#endif +#include <asm/thread_info.h> + +#define pi break + +#define fp a3 + +############################################################################### +# +# build a stack frame from the registers +# - the caller has subtracted 4 from SP before coming here +# +############################################################################### +.macro SAVE_ALL + add -4,sp # next exception frame ptr save area + movm [other],(sp) + mov usp,a1 + mov a1,(sp) # USP in MOVM[other] dummy slot + movm [d2,d3,a2,a3,exreg0,exreg1,exother],(sp) + mov sp,fp # FRAME pointer in A3 + add -12,sp # allow for calls to be made + + # push the exception frame onto the front of the list + GET_THREAD_INFO a1 + mov (TI_frame,a1),a0 + mov a0,(REG_NEXT,fp) + mov fp,(TI_frame,a1) + + # disable the FPU inside the kernel + and ~EPSW_FE,epsw + + # we may be holding current in E2 +#ifdef CONFIG_MN10300_CURRENT_IN_E2 + mov (__current),e2 +#endif +.endm + +############################################################################### +# +# restore the registers from a stack frame +# +############################################################################### +.macro RESTORE_ALL + # peel back the stack to the calling frame + # - this permits execve() to discard extra frames due to kernel syscalls + GET_THREAD_INFO a0 + mov (TI_frame,a0),fp + mov fp,sp + mov (REG_NEXT,fp),d0 + mov d0,(TI_frame,a0) # userspace has regs->next == 0 + +#ifndef CONFIG_MN10300_USING_JTAG + mov (REG_EPSW,fp),d0 + btst EPSW_T,d0 + beq 99f + + or EPSW_NMID,epsw + movhu (DCR),d1 + or 0x0001, d1 + movhu d1,(DCR) + +99: +#endif + movm (sp),[d2,d3,a2,a3,exreg0,exreg1,exother] + + # must restore usp even if returning to kernel space, + # when CONFIG_PREEMPT is enabled. + mov (sp),a1 # USP in MOVM[other] dummy slot + mov a1,usp + + movm (sp),[other] + add 8,sp + rti + +.endm + + +#endif /* _ASM_FRAME_INC */ diff --git a/arch/mn10300/include/asm/ftrace.h b/arch/mn10300/include/asm/ftrace.h new file mode 100644 index 00000000..40a8c178 --- /dev/null +++ b/arch/mn10300/include/asm/ftrace.h @@ -0,0 +1 @@ +/* empty */ diff --git a/arch/mn10300/include/asm/futex.h b/arch/mn10300/include/asm/futex.h new file mode 100644 index 00000000..0b745828 --- /dev/null +++ b/arch/mn10300/include/asm/futex.h @@ -0,0 +1 @@ +#include <asm-generic/futex.h> diff --git a/arch/mn10300/include/asm/gdb-stub.h b/arch/mn10300/include/asm/gdb-stub.h new file mode 100644 index 00000000..f5495ad8 --- /dev/null +++ b/arch/mn10300/include/asm/gdb-stub.h @@ -0,0 +1,182 @@ +/* MN10300 Kernel GDB stub definitions + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * - Derived from asm-mips/gdb-stub.h (c) 1995 Andreas Busse + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_GDB_STUB_H +#define _ASM_GDB_STUB_H + +#include <asm/exceptions.h> + +/* + * register ID numbers in GDB remote protocol + */ + +#define GDB_REGID_PC 9 +#define GDB_REGID_FP 7 +#define GDB_REGID_SP 8 + +/* + * virtual stack layout for the GDB exception handler + */ +#define NUMREGS 64 + +#define GDB_FR_D0 (0 * 4) +#define GDB_FR_D1 (1 * 4) +#define GDB_FR_D2 (2 * 4) +#define GDB_FR_D3 (3 * 4) +#define GDB_FR_A0 (4 * 4) +#define GDB_FR_A1 (5 * 4) +#define GDB_FR_A2 (6 * 4) +#define GDB_FR_A3 (7 * 4) + +#define GDB_FR_SP (8 * 4) +#define GDB_FR_PC (9 * 4) +#define GDB_FR_MDR (10 * 4) +#define GDB_FR_EPSW (11 * 4) +#define GDB_FR_LIR (12 * 4) +#define GDB_FR_LAR (13 * 4) +#define GDB_FR_MDRQ (14 * 4) + +#define GDB_FR_E0 (15 * 4) +#define GDB_FR_E1 (16 * 4) +#define GDB_FR_E2 (17 * 4) +#define GDB_FR_E3 (18 * 4) +#define GDB_FR_E4 (19 * 4) +#define GDB_FR_E5 (20 * 4) +#define GDB_FR_E6 (21 * 4) +#define GDB_FR_E7 (22 * 4) + +#define GDB_FR_SSP (23 * 4) +#define GDB_FR_MSP (24 * 4) +#define GDB_FR_USP (25 * 4) +#define GDB_FR_MCRH (26 * 4) +#define GDB_FR_MCRL (27 * 4) +#define GDB_FR_MCVF (28 * 4) + +#define GDB_FR_FPCR (29 * 4) +#define GDB_FR_DUMMY0 (30 * 4) +#define GDB_FR_DUMMY1 (31 * 4) + +#define GDB_FR_FS0 (32 * 4) + +#define GDB_FR_SIZE (NUMREGS * 4) + +#ifndef __ASSEMBLY__ + +/* + * This is the same as above, but for the high-level + * part of the GDB stub. + */ + +struct gdb_regs { + /* saved main processor registers */ + u32 d0, d1, d2, d3, a0, a1, a2, a3; + u32 sp, pc, mdr, epsw, lir, lar, mdrq; + u32 e0, e1, e2, e3, e4, e5, e6, e7; + u32 ssp, msp, usp, mcrh, mcrl, mcvf; + + /* saved floating point registers */ + u32 fpcr, _dummy0, _dummy1; + u32 fs0, fs1, fs2, fs3, fs4, fs5, fs6, fs7; + u32 fs8, fs9, fs10, fs11, fs12, fs13, fs14, fs15; + u32 fs16, fs17, fs18, fs19, fs20, fs21, fs22, fs23; + u32 fs24, fs25, fs26, fs27, fs28, fs29, fs30, fs31; +}; + +/* + * Prototypes + */ +extern void show_registers_only(struct pt_regs *regs); + +extern asmlinkage void gdbstub_init(void); +extern asmlinkage void gdbstub_exit(int status); +extern asmlinkage void gdbstub_io_init(void); +extern asmlinkage void gdbstub_io_set_baud(unsigned baud); +extern asmlinkage int gdbstub_io_rx_char(unsigned char *_ch, int nonblock); +extern asmlinkage void gdbstub_io_tx_char(unsigned char ch); +extern asmlinkage void gdbstub_io_tx_flush(void); + +extern asmlinkage void gdbstub_io_rx_handler(void); +extern asmlinkage void gdbstub_rx_irq(struct pt_regs *, enum exception_code); +extern asmlinkage int gdbstub_intercept(struct pt_regs *, enum exception_code); +extern asmlinkage void gdbstub_exception(struct pt_regs *, enum exception_code); +extern asmlinkage void __gdbstub_bug_trap(void); +extern asmlinkage void __gdbstub_pause(void); + +#ifdef CONFIG_MN10300_CACHE_ENABLED +extern asmlinkage void gdbstub_purge_cache(void); +#else +#define gdbstub_purge_cache() do {} while (0) +#endif + +/* Used to prevent crashes in memory access */ +extern asmlinkage int gdbstub_read_byte(const u8 *, u8 *); +extern asmlinkage int gdbstub_read_word(const u8 *, u8 *); +extern asmlinkage int gdbstub_read_dword(const u8 *, u8 *); +extern asmlinkage int gdbstub_write_byte(u32, u8 *); +extern asmlinkage int gdbstub_write_word(u32, u8 *); +extern asmlinkage int gdbstub_write_dword(u32, u8 *); + +extern asmlinkage void gdbstub_read_byte_guard(void); +extern asmlinkage void gdbstub_read_byte_cont(void); +extern asmlinkage void gdbstub_read_word_guard(void); +extern asmlinkage void gdbstub_read_word_cont(void); +extern asmlinkage void gdbstub_read_dword_guard(void); +extern asmlinkage void gdbstub_read_dword_cont(void); +extern asmlinkage void gdbstub_write_byte_guard(void); +extern asmlinkage void gdbstub_write_byte_cont(void); +extern asmlinkage void gdbstub_write_word_guard(void); +extern asmlinkage void gdbstub_write_word_cont(void); +extern asmlinkage void gdbstub_write_dword_guard(void); +extern asmlinkage void gdbstub_write_dword_cont(void); + +extern u8 gdbstub_rx_buffer[PAGE_SIZE]; +extern u32 gdbstub_rx_inp; +extern u32 gdbstub_rx_outp; +extern u8 gdbstub_rx_overflow; +extern u8 gdbstub_busy; +extern u8 gdbstub_rx_unget; + +#ifdef CONFIG_GDBSTUB_DEBUGGING +extern void gdbstub_printk(const char *fmt, ...) + __attribute__((format(printf, 1, 2))); +#else +static inline __attribute__((format(printf, 1, 2))) +void gdbstub_printk(const char *fmt, ...) +{ +} +#endif + +#ifdef CONFIG_GDBSTUB_DEBUG_ENTRY +#define gdbstub_entry(FMT, ...) gdbstub_printk(FMT, ##__VA_ARGS__) +#else +#define gdbstub_entry(FMT, ...) no_printk(FMT, ##__VA_ARGS__) +#endif + +#ifdef CONFIG_GDBSTUB_DEBUG_PROTOCOL +#define gdbstub_proto(FMT, ...) gdbstub_printk(FMT, ##__VA_ARGS__) +#else +#define gdbstub_proto(FMT, ...) no_printk(FMT, ##__VA_ARGS__) +#endif + +#ifdef CONFIG_GDBSTUB_DEBUG_IO +#define gdbstub_io(FMT, ...) gdbstub_printk(FMT, ##__VA_ARGS__) +#else +#define gdbstub_io(FMT, ...) no_printk(FMT, ##__VA_ARGS__) +#endif + +#ifdef CONFIG_GDBSTUB_DEBUG_BREAKPOINT +#define gdbstub_bkpt(FMT, ...) gdbstub_printk(FMT, ##__VA_ARGS__) +#else +#define gdbstub_bkpt(FMT, ...) no_printk(FMT, ##__VA_ARGS__) +#endif + +#endif /* !__ASSEMBLY__ */ +#endif /* _ASM_GDB_STUB_H */ diff --git a/arch/mn10300/include/asm/hardirq.h b/arch/mn10300/include/asm/hardirq.h new file mode 100644 index 00000000..0000d650 --- /dev/null +++ b/arch/mn10300/include/asm/hardirq.h @@ -0,0 +1,49 @@ +/* MN10300 Hardware IRQ statistics and management + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Modified by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_HARDIRQ_H +#define _ASM_HARDIRQ_H + +#include <linux/threads.h> +#include <linux/irq.h> +#include <asm/exceptions.h> + +/* assembly code in softirq.h is sensitive to the offsets of these fields */ +typedef struct { + unsigned int __softirq_pending; +#ifdef CONFIG_MN10300_WD_TIMER + unsigned int __nmi_count; /* arch dependent */ + unsigned int __irq_count; /* arch dependent */ +#endif +} ____cacheline_aligned irq_cpustat_t; + +#include <linux/irq_cpustat.h> /* Standard mappings for irq_cpustat_t above */ + +extern void ack_bad_irq(int irq); + +/* + * manipulate stubs in the MN10300 CPU Trap/Interrupt Vector table + * - these should jump to __common_exception in entry.S unless there's a good + * reason to do otherwise (see trap_preinit() in traps.c) + */ +typedef void (*intr_stub_fnx)(struct pt_regs *regs, + enum exception_code intcode); + +/* + * manipulate pointers in the Exception table (see entry.S) + * - these are indexed by decoding the lower 24 bits of the TBR register + * - note that the MN103E010 doesn't always trap through the correct vector, + * but does always set the TBR correctly + */ +extern asmlinkage void set_excp_vector(enum exception_code code, + intr_stub_fnx handler); + +#endif /* _ASM_HARDIRQ_H */ diff --git a/arch/mn10300/include/asm/highmem.h b/arch/mn10300/include/asm/highmem.h new file mode 100644 index 00000000..bfe2d886 --- /dev/null +++ b/arch/mn10300/include/asm/highmem.h @@ -0,0 +1,128 @@ +/* MN10300 Virtual kernel memory mappings for high memory + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * - Derived from include/asm-i386/highmem.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_HIGHMEM_H +#define _ASM_HIGHMEM_H + +#ifdef __KERNEL__ + +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/highmem.h> +#include <asm/kmap_types.h> +#include <asm/pgtable.h> + +/* undef for production */ +#undef HIGHMEM_DEBUG + +/* declarations for highmem.c */ +extern unsigned long highstart_pfn, highend_pfn; + +extern pte_t *kmap_pte; +extern pgprot_t kmap_prot; +extern pte_t *pkmap_page_table; + +extern void __init kmap_init(void); + +/* + * Right now we initialize only a single pte table. It can be extended + * easily, subsequent pte tables have to be allocated in one physical + * chunk of RAM. + */ +#define PKMAP_BASE 0xfe000000UL +#define LAST_PKMAP 1024 +#define LAST_PKMAP_MASK (LAST_PKMAP - 1) +#define PKMAP_NR(virt) ((virt - PKMAP_BASE) >> PAGE_SHIFT) +#define PKMAP_ADDR(nr) (PKMAP_BASE + ((nr) << PAGE_SHIFT)) + +extern unsigned long kmap_high(struct page *page); +extern void kunmap_high(struct page *page); + +static inline unsigned long kmap(struct page *page) +{ + if (in_interrupt()) + BUG(); + if (page < highmem_start_page) + return page_address(page); + return kmap_high(page); +} + +static inline void kunmap(struct page *page) +{ + if (in_interrupt()) + BUG(); + if (page < highmem_start_page) + return; + kunmap_high(page); +} + +/* + * The use of kmap_atomic/kunmap_atomic is discouraged - kmap/kunmap + * gives a more generic (and caching) interface. But kmap_atomic can + * be used in IRQ contexts, so in some (very limited) cases we need + * it. + */ +static inline unsigned long __kmap_atomic(struct page *page) +{ + unsigned long vaddr; + int idx, type; + + pagefault_disable(); + if (page < highmem_start_page) + return page_address(page); + + type = kmap_atomic_idx_push(); + idx = type + KM_TYPE_NR * smp_processor_id(); + vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx); +#if HIGHMEM_DEBUG + if (!pte_none(*(kmap_pte - idx))) + BUG(); +#endif + set_pte(kmap_pte - idx, mk_pte(page, kmap_prot)); + local_flush_tlb_one(vaddr); + + return vaddr; +} + +static inline void __kunmap_atomic(unsigned long vaddr) +{ + int type; + + if (vaddr < FIXADDR_START) { /* FIXME */ + pagefault_enable(); + return; + } + + type = kmap_atomic_idx(); + +#if HIGHMEM_DEBUG + { + unsigned int idx; + idx = type + KM_TYPE_NR * smp_processor_id(); + + if (vaddr != __fix_to_virt(FIX_KMAP_BEGIN + idx)) + BUG(); + + /* + * force other mappings to Oops if they'll try to access + * this pte without first remap it + */ + pte_clear(kmap_pte - idx); + local_flush_tlb_one(vaddr); + } +#endif + + kmap_atomic_idx_pop(); + pagefault_enable(); +} +#endif /* __KERNEL__ */ + +#endif /* _ASM_HIGHMEM_H */ diff --git a/arch/mn10300/include/asm/hw_irq.h b/arch/mn10300/include/asm/hw_irq.h new file mode 100644 index 00000000..70619901 --- /dev/null +++ b/arch/mn10300/include/asm/hw_irq.h @@ -0,0 +1,14 @@ +/* MN10300 Hardware interrupt definitions + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_HW_IRQ_H +#define _ASM_HW_IRQ_H + +#endif /* _ASM_HW_IRQ_H */ diff --git a/arch/mn10300/include/asm/intctl-regs.h b/arch/mn10300/include/asm/intctl-regs.h new file mode 100644 index 00000000..d65bbeeb --- /dev/null +++ b/arch/mn10300/include/asm/intctl-regs.h @@ -0,0 +1,71 @@ +/* MN10300 On-board interrupt controller registers + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_INTCTL_REGS_H +#define _ASM_INTCTL_REGS_H + +#include <asm/cpu-regs.h> + +#ifdef __KERNEL__ + +/* + * Interrupt controller registers + * - Registers 64-191 are at addresses offset from the main array + */ +#define GxICR(X) \ + __SYSREG(0xd4000000 + (X) * 4 + \ + (((X) >= 64) && ((X) < 192)) * 0xf00, u16) + +#define GxICR_u8(X) \ + __SYSREG(0xd4000000 + (X) * 4 + \ + (((X) >= 64) && ((X) < 192)) * 0xf00, u8) + +#include <proc/intctl-regs.h> + +#define XIRQ_TRIGGER_LOWLEVEL 0 +#define XIRQ_TRIGGER_HILEVEL 1 +#define XIRQ_TRIGGER_NEGEDGE 2 +#define XIRQ_TRIGGER_POSEDGE 3 + +/* non-maskable interrupt control */ +#define NMIIRQ 0 +#define NMICR GxICR(NMIIRQ) /* NMI control register */ +#define NMICR_NMIF 0x0001 /* NMI pin interrupt flag */ +#define NMICR_WDIF 0x0002 /* watchdog timer overflow flag */ +#define NMICR_ABUSERR 0x0008 /* async bus error flag */ + +/* maskable interrupt control */ +#define GxICR_DETECT 0x0001 /* interrupt detect flag */ +#define GxICR_REQUEST 0x0010 /* interrupt request flag */ +#define GxICR_ENABLE 0x0100 /* interrupt enable flag */ +#define GxICR_LEVEL 0x7000 /* interrupt priority level */ +#define GxICR_LEVEL_0 0x0000 /* - level 0 */ +#define GxICR_LEVEL_1 0x1000 /* - level 1 */ +#define GxICR_LEVEL_2 0x2000 /* - level 2 */ +#define GxICR_LEVEL_3 0x3000 /* - level 3 */ +#define GxICR_LEVEL_4 0x4000 /* - level 4 */ +#define GxICR_LEVEL_5 0x5000 /* - level 5 */ +#define GxICR_LEVEL_6 0x6000 /* - level 6 */ +#define GxICR_LEVEL_SHIFT 12 +#define GxICR_NMI 0x8000 /* nmi request flag */ + +#define NUM2GxICR_LEVEL(num) ((num) << GxICR_LEVEL_SHIFT) + +#ifndef __ASSEMBLY__ +extern void set_intr_level(int irq, u16 level); +extern void mn10300_set_lateack_irq_type(int irq); +#endif + +/* external interrupts */ +#define XIRQxICR(X) GxICR((X)) /* external interrupt control regs */ + +#endif /* __KERNEL__ */ + +#endif /* _ASM_INTCTL_REGS_H */ diff --git a/arch/mn10300/include/asm/io.h b/arch/mn10300/include/asm/io.h new file mode 100644 index 00000000..787255da --- /dev/null +++ b/arch/mn10300/include/asm/io.h @@ -0,0 +1,314 @@ +/* MN10300 I/O port emulation and memory-mapped I/O + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_IO_H +#define _ASM_IO_H + +#include <asm/page.h> /* I/O is all done through memory accesses */ +#include <asm/cpu-regs.h> +#include <asm/cacheflush.h> + +#define mmiowb() do {} while (0) + +/*****************************************************************************/ +/* + * readX/writeX() are used to access memory mapped devices. On some + * architectures the memory mapped IO stuff needs to be accessed + * differently. On the x86 architecture, we just read/write the + * memory location directly. + */ +static inline u8 readb(const volatile void __iomem *addr) +{ + return *(const volatile u8 *) addr; +} + +static inline u16 readw(const volatile void __iomem *addr) +{ + return *(const volatile u16 *) addr; +} + +static inline u32 readl(const volatile void __iomem *addr) +{ + return *(const volatile u32 *) addr; +} + +#define __raw_readb readb +#define __raw_readw readw +#define __raw_readl readl + +#define readb_relaxed readb +#define readw_relaxed readw +#define readl_relaxed readl + +static inline void writeb(u8 b, volatile void __iomem *addr) +{ + *(volatile u8 *) addr = b; +} + +static inline void writew(u16 b, volatile void __iomem *addr) +{ + *(volatile u16 *) addr = b; +} + +static inline void writel(u32 b, volatile void __iomem *addr) +{ + *(volatile u32 *) addr = b; +} + +#define __raw_writeb writeb +#define __raw_writew writew +#define __raw_writel writel + +/*****************************************************************************/ +/* + * traditional input/output functions + */ +static inline u8 inb_local(unsigned long addr) +{ + return readb((volatile void __iomem *) addr); +} + +static inline void outb_local(u8 b, unsigned long addr) +{ + return writeb(b, (volatile void __iomem *) addr); +} + +static inline u8 inb(unsigned long addr) +{ + return readb((volatile void __iomem *) addr); +} + +static inline u16 inw(unsigned long addr) +{ + return readw((volatile void __iomem *) addr); +} + +static inline u32 inl(unsigned long addr) +{ + return readl((volatile void __iomem *) addr); +} + +static inline void outb(u8 b, unsigned long addr) +{ + return writeb(b, (volatile void __iomem *) addr); +} + +static inline void outw(u16 b, unsigned long addr) +{ + return writew(b, (volatile void __iomem *) addr); +} + +static inline void outl(u32 b, unsigned long addr) +{ + return writel(b, (volatile void __iomem *) addr); +} + +#define inb_p(addr) inb(addr) +#define inw_p(addr) inw(addr) +#define inl_p(addr) inl(addr) +#define outb_p(x, addr) outb((x), (addr)) +#define outw_p(x, addr) outw((x), (addr)) +#define outl_p(x, addr) outl((x), (addr)) + +static inline void insb(unsigned long addr, void *buffer, int count) +{ + if (count) { + u8 *buf = buffer; + do { + u8 x = inb(addr); + *buf++ = x; + } while (--count); + } +} + +static inline void insw(unsigned long addr, void *buffer, int count) +{ + if (count) { + u16 *buf = buffer; + do { + u16 x = inw(addr); + *buf++ = x; + } while (--count); + } +} + +static inline void insl(unsigned long addr, void *buffer, int count) +{ + if (count) { + u32 *buf = buffer; + do { + u32 x = inl(addr); + *buf++ = x; + } while (--count); + } +} + +static inline void outsb(unsigned long addr, const void *buffer, int count) +{ + if (count) { + const u8 *buf = buffer; + do { + outb(*buf++, addr); + } while (--count); + } +} + +static inline void outsw(unsigned long addr, const void *buffer, int count) +{ + if (count) { + const u16 *buf = buffer; + do { + outw(*buf++, addr); + } while (--count); + } +} + +extern void __outsl(unsigned long addr, const void *buffer, int count); +static inline void outsl(unsigned long addr, const void *buffer, int count) +{ + if ((unsigned long) buffer & 0x3) + return __outsl(addr, buffer, count); + + if (count) { + const u32 *buf = buffer; + do { + outl(*buf++, addr); + } while (--count); + } +} + +#define ioread8(addr) readb(addr) +#define ioread16(addr) readw(addr) +#define ioread32(addr) readl(addr) + +#define iowrite8(v, addr) writeb((v), (addr)) +#define iowrite16(v, addr) writew((v), (addr)) +#define iowrite32(v, addr) writel((v), (addr)) + +#define ioread8_rep(p, dst, count) \ + insb((unsigned long) (p), (dst), (count)) +#define ioread16_rep(p, dst, count) \ + insw((unsigned long) (p), (dst), (count)) +#define ioread32_rep(p, dst, count) \ + insl((unsigned long) (p), (dst), (count)) + +#define iowrite8_rep(p, src, count) \ + outsb((unsigned long) (p), (src), (count)) +#define iowrite16_rep(p, src, count) \ + outsw((unsigned long) (p), (src), (count)) +#define iowrite32_rep(p, src, count) \ + outsl((unsigned long) (p), (src), (count)) + +#define readsb(p, dst, count) \ + insb((unsigned long) (p), (dst), (count)) +#define readsw(p, dst, count) \ + insw((unsigned long) (p), (dst), (count)) +#define readsl(p, dst, count) \ + insl((unsigned long) (p), (dst), (count)) + +#define writesb(p, src, count) \ + outsb((unsigned long) (p), (src), (count)) +#define writesw(p, src, count) \ + outsw((unsigned long) (p), (src), (count)) +#define writesl(p, src, count) \ + outsl((unsigned long) (p), (src), (count)) + +#define IO_SPACE_LIMIT 0xffffffff + +#ifdef __KERNEL__ + +#include <linux/vmalloc.h> +#define __io_virt(x) ((void *) (x)) + +/* Create a virtual mapping cookie for a PCI BAR (memory or IO) */ +struct pci_dev; +extern void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max); +static inline void pci_iounmap(struct pci_dev *dev, void __iomem *p) +{ +} + +/* + * Change virtual addresses to physical addresses and vv. + * These are pretty trivial + */ +static inline unsigned long virt_to_phys(volatile void *address) +{ + return __pa(address); +} + +static inline void *phys_to_virt(unsigned long address) +{ + return __va(address); +} + +/* + * Change "struct page" to physical address. + */ +static inline void *__ioremap(unsigned long offset, unsigned long size, + unsigned long flags) +{ + return (void *) offset; +} + +static inline void *ioremap(unsigned long offset, unsigned long size) +{ + return (void *) offset; +} + +/* + * This one maps high address device memory and turns off caching for that + * area. it's useful if some control registers are in such an area and write + * combining or read caching is not desirable: + */ +static inline void *ioremap_nocache(unsigned long offset, unsigned long size) +{ + return (void *) (offset | 0x20000000); +} + +#define ioremap_wc ioremap_nocache + +static inline void iounmap(void *addr) +{ +} + +static inline void __iomem *ioport_map(unsigned long port, unsigned int nr) +{ + return (void __iomem *) port; +} + +static inline void ioport_unmap(void __iomem *p) +{ +} + +#define xlate_dev_kmem_ptr(p) ((void *) (p)) +#define xlate_dev_mem_ptr(p) ((void *) (p)) + +/* + * PCI bus iomem addresses must be in the region 0x80000000-0x9fffffff + */ +static inline unsigned long virt_to_bus(volatile void *address) +{ + return ((unsigned long) address) & ~0x20000000; +} + +static inline void *bus_to_virt(unsigned long address) +{ + return (void *) address; +} + +#define page_to_bus page_to_phys + +#define memset_io(a, b, c) memset(__io_virt(a), (b), (c)) +#define memcpy_fromio(a, b, c) memcpy((a), __io_virt(b), (c)) +#define memcpy_toio(a, b, c) memcpy(__io_virt(a), (b), (c)) + +#endif /* __KERNEL__ */ + +#endif /* _ASM_IO_H */ diff --git a/arch/mn10300/include/asm/ioctl.h b/arch/mn10300/include/asm/ioctl.h new file mode 100644 index 00000000..b279fe06 --- /dev/null +++ b/arch/mn10300/include/asm/ioctl.h @@ -0,0 +1 @@ +#include <asm-generic/ioctl.h> diff --git a/arch/mn10300/include/asm/ioctls.h b/arch/mn10300/include/asm/ioctls.h new file mode 100644 index 00000000..0212f4b2 --- /dev/null +++ b/arch/mn10300/include/asm/ioctls.h @@ -0,0 +1,6 @@ +#ifndef _ASM_IOCTLS_H +#define _ASM_IOCTLS_H + +#include <asm-generic/ioctls.h> + +#endif /* _ASM_IOCTLS_H */ diff --git a/arch/mn10300/include/asm/ipc.h b/arch/mn10300/include/asm/ipc.h new file mode 100644 index 00000000..a46e3d9c --- /dev/null +++ b/arch/mn10300/include/asm/ipc.h @@ -0,0 +1 @@ +#include <asm-generic/ipc.h> diff --git a/arch/mn10300/include/asm/ipcbuf.h b/arch/mn10300/include/asm/ipcbuf.h new file mode 100644 index 00000000..f6f63d44 --- /dev/null +++ b/arch/mn10300/include/asm/ipcbuf.h @@ -0,0 +1,29 @@ +#ifndef _ASM_IPCBUF_H +#define _ASM_IPCBUF_H + +/* + * The ipc64_perm structure for MN10300 architecture. + * Note extra padding because this structure is passed back and forth + * between kernel and user space. + * + * Pad space is left for: + * - 32-bit mode_t and seq + * - 2 miscellaneous 32-bit values + */ + +struct ipc64_perm +{ + __kernel_key_t key; + __kernel_uid32_t uid; + __kernel_gid32_t gid; + __kernel_uid32_t cuid; + __kernel_gid32_t cgid; + __kernel_mode_t mode; + unsigned short __pad1; + unsigned short seq; + unsigned short __pad2; + unsigned long __unused1; + unsigned long __unused2; +}; + +#endif /* _ASM_IPCBUF_H */ diff --git a/arch/mn10300/include/asm/irq.h b/arch/mn10300/include/asm/irq.h new file mode 100644 index 00000000..1a73fb3f --- /dev/null +++ b/arch/mn10300/include/asm/irq.h @@ -0,0 +1,40 @@ +/* MN10300 Hardware interrupt definitions + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Modified by David Howells (dhowells@redhat.com) + * - Derived from include/asm-i386/irq.h: + * - (C) 1992, 1993 Linus Torvalds, (C) 1997 Ingo Molnar + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_IRQ_H +#define _ASM_IRQ_H + +#include <asm/intctl-regs.h> +#include <asm/reset-regs.h> +#include <proc/irq.h> + +/* this number is used when no interrupt has been assigned */ +#define NO_IRQ INT_MAX + +/* + * hardware irq numbers + * - the ASB2364 has an FPGA with an IRQ multiplexer on it + */ +#ifdef CONFIG_MN10300_UNIT_ASB2364 +#include <unit/irq.h> +#else +#define NR_CPU_IRQS GxICR_NUM_IRQS +#define NR_IRQS NR_CPU_IRQS +#endif + +/* external hardware irq numbers */ +#define NR_XIRQS GxICR_NUM_XIRQS + +#define irq_canonicalize(IRQ) (IRQ) + +#endif /* _ASM_IRQ_H */ diff --git a/arch/mn10300/include/asm/irq_regs.h b/arch/mn10300/include/asm/irq_regs.h new file mode 100644 index 00000000..97d0cb5a --- /dev/null +++ b/arch/mn10300/include/asm/irq_regs.h @@ -0,0 +1,28 @@ +/* MN10300 IRQ registers pointer definition + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_IRQ_REGS_H +#define _ASM_IRQ_REGS_H + +/* + * Per-cpu current frame pointer - the location of the last exception frame on + * the stack + */ +#define ARCH_HAS_OWN_IRQ_REGS + +#ifndef __ASSEMBLY__ +static inline __attribute__((const)) +struct pt_regs *get_irq_regs(void) +{ + return current_frame(); +} +#endif + +#endif /* _ASM_IRQ_REGS_H */ diff --git a/arch/mn10300/include/asm/irqflags.h b/arch/mn10300/include/asm/irqflags.h new file mode 100644 index 00000000..678f68d5 --- /dev/null +++ b/arch/mn10300/include/asm/irqflags.h @@ -0,0 +1,216 @@ +/* MN10300 IRQ flag handling + * + * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _ASM_IRQFLAGS_H +#define _ASM_IRQFLAGS_H + +#include <asm/cpu-regs.h> +#ifndef __ASSEMBLY__ +#include <linux/smp.h> +#endif + +/* + * interrupt control + * - "disabled": run in IM1/2 + * - level 0 - kernel debugger + * - level 1 - virtual serial DMA (if present) + * - level 5 - normal interrupt priority + * - level 6 - timer interrupt + * - "enabled": run in IM7 + */ +#define MN10300_CLI_LEVEL (CONFIG_LINUX_CLI_LEVEL << EPSW_IM_SHIFT) + +#ifndef __ASSEMBLY__ + +static inline unsigned long arch_local_save_flags(void) +{ + unsigned long flags; + + asm volatile("mov epsw,%0" : "=d"(flags)); + return flags; +} + +static inline void arch_local_irq_disable(void) +{ + asm volatile( + " and %0,epsw \n" + " or %1,epsw \n" + " nop \n" + " nop \n" + " nop \n" + : + : "i"(~EPSW_IM), "i"(EPSW_IE | MN10300_CLI_LEVEL) + : "memory"); +} + +static inline unsigned long arch_local_irq_save(void) +{ + unsigned long flags; + + flags = arch_local_save_flags(); + arch_local_irq_disable(); + return flags; +} + +/* + * we make sure arch_irq_enable() doesn't cause priority inversion + */ +extern unsigned long __mn10300_irq_enabled_epsw[]; + +static inline void arch_local_irq_enable(void) +{ + unsigned long tmp; + int cpu = raw_smp_processor_id(); + + asm volatile( + " mov epsw,%0 \n" + " and %1,%0 \n" + " or %2,%0 \n" + " mov %0,epsw \n" + : "=&d"(tmp) + : "i"(~EPSW_IM), "r"(__mn10300_irq_enabled_epsw[cpu]) + : "memory", "cc"); +} + +static inline void arch_local_irq_restore(unsigned long flags) +{ + asm volatile( + " mov %0,epsw \n" + " nop \n" + " nop \n" + " nop \n" + : + : "d"(flags) + : "memory", "cc"); +} + +static inline bool arch_irqs_disabled_flags(unsigned long flags) +{ + return (flags & (EPSW_IE | EPSW_IM)) != (EPSW_IE | EPSW_IM_7); +} + +static inline bool arch_irqs_disabled(void) +{ + return arch_irqs_disabled_flags(arch_local_save_flags()); +} + +/* + * Hook to save power by halting the CPU + * - called from the idle loop + * - must reenable interrupts (which takes three instruction cycles to complete) + */ +static inline void arch_safe_halt(void) +{ +#ifdef CONFIG_SMP + arch_local_irq_enable(); +#else + asm volatile( + " or %0,epsw \n" + " nop \n" + " nop \n" + " bset %2,(%1) \n" + : + : "i"(EPSW_IE|EPSW_IM), "n"(&CPUM), "i"(CPUM_SLEEP) + : "cc"); +#endif +} + +#define __sleep_cpu() \ +do { \ + asm volatile( \ + " bset %1,(%0)\n" \ + "1: btst %1,(%0)\n" \ + " bne 1b\n" \ + : \ + : "i"(&CPUM), "i"(CPUM_SLEEP) \ + : "cc" \ + ); \ +} while (0) + +static inline void arch_local_cli(void) +{ + asm volatile( + " and %0,epsw \n" + " nop \n" + " nop \n" + " nop \n" + : + : "i"(~EPSW_IE) + : "memory" + ); +} + +static inline unsigned long arch_local_cli_save(void) +{ + unsigned long flags = arch_local_save_flags(); + arch_local_cli(); + return flags; +} + +static inline void arch_local_sti(void) +{ + asm volatile( + " or %0,epsw \n" + : + : "i"(EPSW_IE) + : "memory"); +} + +static inline void arch_local_change_intr_mask_level(unsigned long level) +{ + asm volatile( + " and %0,epsw \n" + " or %1,epsw \n" + : + : "i"(~EPSW_IM), "i"(EPSW_IE | level) + : "cc", "memory"); +} + +#else /* !__ASSEMBLY__ */ + +#define LOCAL_SAVE_FLAGS(reg) \ + mov epsw,reg + +#define LOCAL_IRQ_DISABLE \ + and ~EPSW_IM,epsw; \ + or EPSW_IE|MN10300_CLI_LEVEL,epsw; \ + nop; \ + nop; \ + nop + +#define LOCAL_IRQ_ENABLE \ + or EPSW_IE|EPSW_IM_7,epsw + +#define LOCAL_IRQ_RESTORE(reg) \ + mov reg,epsw + +#define LOCAL_CLI_SAVE(reg) \ + mov epsw,reg; \ + and ~EPSW_IE,epsw; \ + nop; \ + nop; \ + nop + +#define LOCAL_CLI \ + and ~EPSW_IE,epsw; \ + nop; \ + nop; \ + nop + +#define LOCAL_STI \ + or EPSW_IE,epsw + +#define LOCAL_CHANGE_INTR_MASK_LEVEL(level) \ + and ~EPSW_IM,epsw; \ + or EPSW_IE|(level),epsw + +#endif /* __ASSEMBLY__ */ +#endif /* _ASM_IRQFLAGS_H */ diff --git a/arch/mn10300/include/asm/kdebug.h b/arch/mn10300/include/asm/kdebug.h new file mode 100644 index 00000000..0f47e112 --- /dev/null +++ b/arch/mn10300/include/asm/kdebug.h @@ -0,0 +1,22 @@ +/* MN10300 In-kernel death knells + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _ASM_KDEBUG_H +#define _ASM_KDEBUG_H + +/* Grossly misnamed. */ +enum die_val { + DIE_OOPS = 1, + DIE_BREAKPOINT, + DIE_GPF, +}; + +#endif /* _ASM_KDEBUG_H */ diff --git a/arch/mn10300/include/asm/kgdb.h b/arch/mn10300/include/asm/kgdb.h new file mode 100644 index 00000000..eb245f18 --- /dev/null +++ b/arch/mn10300/include/asm/kgdb.h @@ -0,0 +1,81 @@ +/* Kernel debugger for MN10300 + * + * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _ASM_KGDB_H +#define _ASM_KGDB_H + +/* + * BUFMAX defines the maximum number of characters in inbound/outbound + * buffers at least NUMREGBYTES*2 are needed for register packets + * Longer buffer is needed to list all threads + */ +#define BUFMAX 1024 + +/* + * Note that this register image is in a different order than the register + * image that Linux produces at interrupt time. + */ +enum regnames { + GDB_FR_D0 = 0, + GDB_FR_D1 = 1, + GDB_FR_D2 = 2, + GDB_FR_D3 = 3, + GDB_FR_A0 = 4, + GDB_FR_A1 = 5, + GDB_FR_A2 = 6, + GDB_FR_A3 = 7, + + GDB_FR_SP = 8, + GDB_FR_PC = 9, + GDB_FR_MDR = 10, + GDB_FR_EPSW = 11, + GDB_FR_LIR = 12, + GDB_FR_LAR = 13, + GDB_FR_MDRQ = 14, + + GDB_FR_E0 = 15, + GDB_FR_E1 = 16, + GDB_FR_E2 = 17, + GDB_FR_E3 = 18, + GDB_FR_E4 = 19, + GDB_FR_E5 = 20, + GDB_FR_E6 = 21, + GDB_FR_E7 = 22, + + GDB_FR_SSP = 23, + GDB_FR_MSP = 24, + GDB_FR_USP = 25, + GDB_FR_MCRH = 26, + GDB_FR_MCRL = 27, + GDB_FR_MCVF = 28, + + GDB_FR_FPCR = 29, + GDB_FR_DUMMY0 = 30, + GDB_FR_DUMMY1 = 31, + + GDB_FR_FS0 = 32, + + GDB_FR_SIZE = 64, +}; + +#define GDB_ORIG_D0 41 +#define NUMREGBYTES (GDB_FR_SIZE*4) + +static inline void arch_kgdb_breakpoint(void) +{ + asm(".globl __arch_kgdb_breakpoint; __arch_kgdb_breakpoint: break"); +} +extern u8 __arch_kgdb_breakpoint; + +#define BREAK_INSTR_SIZE 1 +#define CACHE_FLUSH_IS_SAFE 1 + +#endif /* _ASM_KGDB_H */ diff --git a/arch/mn10300/include/asm/kmap_types.h b/arch/mn10300/include/asm/kmap_types.h new file mode 100644 index 00000000..76d093b5 --- /dev/null +++ b/arch/mn10300/include/asm/kmap_types.h @@ -0,0 +1,6 @@ +#ifndef _ASM_KMAP_TYPES_H +#define _ASM_KMAP_TYPES_H + +#include <asm-generic/kmap_types.h> + +#endif /* _ASM_KMAP_TYPES_H */ diff --git a/arch/mn10300/include/asm/kprobes.h b/arch/mn10300/include/asm/kprobes.h new file mode 100644 index 00000000..c800b590 --- /dev/null +++ b/arch/mn10300/include/asm/kprobes.h @@ -0,0 +1,50 @@ +/* MN10300 Kernel Probes support + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by Mark Salter (msalter@redhat.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public Licence as published by + * the Free Software Foundation; either version 2 of the Licence, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public Licence for more details. + * + * You should have received a copy of the GNU General Public Licence + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ +#ifndef _ASM_KPROBES_H +#define _ASM_KPROBES_H + +#include <linux/types.h> +#include <linux/ptrace.h> + +struct kprobe; + +typedef unsigned char kprobe_opcode_t; +#define BREAKPOINT_INSTRUCTION 0xff +#define MAX_INSN_SIZE 8 +#define MAX_STACK_SIZE 128 + +/* Architecture specific copy of original instruction */ +struct arch_specific_insn { + /* copy of original instruction + */ + kprobe_opcode_t insn[MAX_INSN_SIZE]; +}; + +extern const int kretprobe_blacklist_size; + +extern int kprobe_exceptions_notify(struct notifier_block *self, + unsigned long val, void *data); + +#define flush_insn_slot(p) do {} while (0) + +extern void arch_remove_kprobe(struct kprobe *p); + +#endif /* _ASM_KPROBES_H */ diff --git a/arch/mn10300/include/asm/linkage.h b/arch/mn10300/include/asm/linkage.h new file mode 100644 index 00000000..dda3002a --- /dev/null +++ b/arch/mn10300/include/asm/linkage.h @@ -0,0 +1,20 @@ +/* MN10300 Linkage and calling-convention overrides + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_LINKAGE_H +#define _ASM_LINKAGE_H + +/* don't override anything */ +#define asmlinkage + +#define __ALIGN .align 4,0xcb +#define __ALIGN_STR ".align 4,0xcb" + +#endif diff --git a/arch/mn10300/include/asm/local.h b/arch/mn10300/include/asm/local.h new file mode 100644 index 00000000..c11c530f --- /dev/null +++ b/arch/mn10300/include/asm/local.h @@ -0,0 +1 @@ +#include <asm-generic/local.h> diff --git a/arch/mn10300/include/asm/local64.h b/arch/mn10300/include/asm/local64.h new file mode 100644 index 00000000..36c93b5c --- /dev/null +++ b/arch/mn10300/include/asm/local64.h @@ -0,0 +1 @@ +#include <asm-generic/local64.h> diff --git a/arch/mn10300/include/asm/mc146818rtc.h b/arch/mn10300/include/asm/mc146818rtc.h new file mode 100644 index 00000000..df6bc6e0 --- /dev/null +++ b/arch/mn10300/include/asm/mc146818rtc.h @@ -0,0 +1 @@ +#include <asm/rtc-regs.h> diff --git a/arch/mn10300/include/asm/mman.h b/arch/mn10300/include/asm/mman.h new file mode 100644 index 00000000..db5c53da --- /dev/null +++ b/arch/mn10300/include/asm/mman.h @@ -0,0 +1,6 @@ +#include <asm-generic/mman.h> + +#define MIN_MAP_ADDR PAGE_SIZE /* minimum fixed mmap address */ + +#define arch_mmap_check(addr, len, flags) \ + (((flags) & MAP_FIXED && (addr) < MIN_MAP_ADDR) ? -EINVAL : 0) diff --git a/arch/mn10300/include/asm/mmu.h b/arch/mn10300/include/asm/mmu.h new file mode 100644 index 00000000..2d2d097e --- /dev/null +++ b/arch/mn10300/include/asm/mmu.h @@ -0,0 +1,19 @@ +/* MN10300 Memory management context + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * - Derived from include/asm-frv/mmu.h + */ + +#ifndef _ASM_MMU_H +#define _ASM_MMU_H + +/* + * MMU context + */ +typedef struct { + unsigned long tlbpid[NR_CPUS]; /* TLB PID for this process on + * each CPU */ +} mm_context_t; + +#endif /* _ASM_MMU_H */ diff --git a/arch/mn10300/include/asm/mmu_context.h b/arch/mn10300/include/asm/mmu_context.h new file mode 100644 index 00000000..c8f6c826 --- /dev/null +++ b/arch/mn10300/include/asm/mmu_context.h @@ -0,0 +1,161 @@ +/* MN10300 MMU context management + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Modified by David Howells (dhowells@redhat.com) + * - Derived from include/asm-m32r/mmu_context.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + * + * + * This implements an algorithm to provide TLB PID mappings to provide + * selective access to the TLB for processes, thus reducing the number of TLB + * flushes required. + * + * Note, however, that the M32R algorithm is technically broken as it does not + * handle version wrap-around, and could, theoretically, have a problem with a + * very long lived program that sleeps long enough for the version number to + * wrap all the way around so that its TLB mappings appear valid once again. + */ +#ifndef _ASM_MMU_CONTEXT_H +#define _ASM_MMU_CONTEXT_H + +#include <asm/atomic.h> +#include <asm/pgalloc.h> +#include <asm/tlbflush.h> +#include <asm-generic/mm_hooks.h> + +#define MMU_CONTEXT_TLBPID_NR 256 +#define MMU_CONTEXT_TLBPID_MASK 0x000000ffUL +#define MMU_CONTEXT_VERSION_MASK 0xffffff00UL +#define MMU_CONTEXT_FIRST_VERSION 0x00000100UL +#define MMU_NO_CONTEXT 0x00000000UL +#define MMU_CONTEXT_TLBPID_LOCK_NR 0 + +#define enter_lazy_tlb(mm, tsk) do {} while (0) + +static inline void cpu_ran_vm(int cpu, struct mm_struct *mm) +{ +#ifdef CONFIG_SMP + cpumask_set_cpu(cpu, mm_cpumask(mm)); +#endif +} + +static inline bool cpu_maybe_ran_vm(int cpu, struct mm_struct *mm) +{ +#ifdef CONFIG_SMP + return cpumask_test_and_set_cpu(cpu, mm_cpumask(mm)); +#else + return true; +#endif +} + +#ifdef CONFIG_MN10300_TLB_USE_PIDR +extern unsigned long mmu_context_cache[NR_CPUS]; +#define mm_context(mm) (mm->context.tlbpid[smp_processor_id()]) + +/** + * allocate_mmu_context - Allocate storage for the arch-specific MMU data + * @mm: The userspace VM context being set up + */ +static inline unsigned long allocate_mmu_context(struct mm_struct *mm) +{ + unsigned long *pmc = &mmu_context_cache[smp_processor_id()]; + unsigned long mc = ++(*pmc); + + if (!(mc & MMU_CONTEXT_TLBPID_MASK)) { + /* we exhausted the TLB PIDs of this version on this CPU, so we + * flush this CPU's TLB in its entirety and start new cycle */ + local_flush_tlb_all(); + + /* fix the TLB version if needed (we avoid version #0 so as to + * distingush MMU_NO_CONTEXT) */ + if (!mc) + *pmc = mc = MMU_CONTEXT_FIRST_VERSION; + } + mm_context(mm) = mc; + return mc; +} + +/* + * get an MMU context if one is needed + */ +static inline unsigned long get_mmu_context(struct mm_struct *mm) +{ + unsigned long mc = MMU_NO_CONTEXT, cache; + + if (mm) { + cache = mmu_context_cache[smp_processor_id()]; + mc = mm_context(mm); + + /* if we have an old version of the context, replace it */ + if ((mc ^ cache) & MMU_CONTEXT_VERSION_MASK) + mc = allocate_mmu_context(mm); + } + return mc; +} + +/* + * initialise the context related info for a new mm_struct instance + */ +static inline int init_new_context(struct task_struct *tsk, + struct mm_struct *mm) +{ + int num_cpus = NR_CPUS, i; + + for (i = 0; i < num_cpus; i++) + mm->context.tlbpid[i] = MMU_NO_CONTEXT; + return 0; +} + +/* + * after we have set current->mm to a new value, this activates the context for + * the new mm so we see the new mappings. + */ +static inline void activate_context(struct mm_struct *mm) +{ + PIDR = get_mmu_context(mm) & MMU_CONTEXT_TLBPID_MASK; +} +#else /* CONFIG_MN10300_TLB_USE_PIDR */ + +#define init_new_context(tsk, mm) (0) +#define activate_context(mm) local_flush_tlb() + +#endif /* CONFIG_MN10300_TLB_USE_PIDR */ + +/** + * destroy_context - Destroy mm context information + * @mm: The MM being destroyed. + * + * Destroy context related info for an mm_struct that is about to be put to + * rest + */ +#define destroy_context(mm) do {} while (0) + +/** + * switch_mm - Change between userspace virtual memory contexts + * @prev: The outgoing MM context. + * @next: The incoming MM context. + * @tsk: The incoming task. + */ +static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, + struct task_struct *tsk) +{ + int cpu = smp_processor_id(); + + if (prev != next) { +#ifdef CONFIG_SMP + per_cpu(cpu_tlbstate, cpu).active_mm = next; +#endif + cpu_ran_vm(cpu, next); + PTBR = (unsigned long) next->pgd; + activate_context(next); + } +} + +#define deactivate_mm(tsk, mm) do {} while (0) +#define activate_mm(prev, next) switch_mm((prev), (next), NULL) + +#endif /* _ASM_MMU_CONTEXT_H */ diff --git a/arch/mn10300/include/asm/module.h b/arch/mn10300/include/asm/module.h new file mode 100644 index 00000000..5d7057d0 --- /dev/null +++ b/arch/mn10300/include/asm/module.h @@ -0,0 +1,27 @@ +/* MN10300 Arch-specific module definitions + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by Mark Salter (msalter@redhat.com) + * Derived from include/asm-i386/module.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_MODULE_H +#define _ASM_MODULE_H + +struct mod_arch_specific { +}; + +#define Elf_Shdr Elf32_Shdr +#define Elf_Sym Elf32_Sym +#define Elf_Ehdr Elf32_Ehdr + +/* + * Include the MN10300 architecture version. + */ +#define MODULE_ARCH_VERMAGIC __stringify(PROCESSOR_MODEL_NAME) " " + +#endif /* _ASM_MODULE_H */ diff --git a/arch/mn10300/include/asm/msgbuf.h b/arch/mn10300/include/asm/msgbuf.h new file mode 100644 index 00000000..8b602450 --- /dev/null +++ b/arch/mn10300/include/asm/msgbuf.h @@ -0,0 +1,31 @@ +#ifndef _ASM_MSGBUF_H +#define _ASM_MSGBUF_H + +/* + * The msqid64_ds structure for MN10300 architecture. + * Note extra padding because this structure is passed back and forth + * between kernel and user space. + * + * Pad space is left for: + * - 64-bit time_t to solve y2038 problem + * - 2 miscellaneous 32-bit values + */ + +struct msqid64_ds { + struct ipc64_perm msg_perm; + __kernel_time_t msg_stime; /* last msgsnd time */ + unsigned long __unused1; + __kernel_time_t msg_rtime; /* last msgrcv time */ + unsigned long __unused2; + __kernel_time_t msg_ctime; /* last change time */ + unsigned long __unused3; + unsigned long msg_cbytes; /* current number of bytes on queue */ + unsigned long msg_qnum; /* number of messages in queue */ + unsigned long msg_qbytes; /* max number of bytes on queue */ + __kernel_pid_t msg_lspid; /* pid of last msgsnd */ + __kernel_pid_t msg_lrpid; /* last receive pid */ + unsigned long __unused4; + unsigned long __unused5; +}; + +#endif /* _ASM_MSGBUF_H */ diff --git a/arch/mn10300/include/asm/mutex.h b/arch/mn10300/include/asm/mutex.h new file mode 100644 index 00000000..84f5490c --- /dev/null +++ b/arch/mn10300/include/asm/mutex.h @@ -0,0 +1,16 @@ +/* MN10300 Mutex fastpath + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + * + * + * TODO: implement optimized primitives instead, or leave the generic + * implementation in place, or pick the atomic_xchg() based generic + * implementation. (see asm-generic/mutex-xchg.h for details) + */ +#include <asm-generic/mutex-null.h> diff --git a/arch/mn10300/include/asm/nmi.h b/arch/mn10300/include/asm/nmi.h new file mode 100644 index 00000000..f3671cbb --- /dev/null +++ b/arch/mn10300/include/asm/nmi.h @@ -0,0 +1,14 @@ +/* MN10300 NMI handling + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_NMI_H +#define _ASM_NMI_H + +#endif /* _ASM_NMI_H */ diff --git a/arch/mn10300/include/asm/page.h b/arch/mn10300/include/asm/page.h new file mode 100644 index 00000000..8288e124 --- /dev/null +++ b/arch/mn10300/include/asm/page.h @@ -0,0 +1,128 @@ +/* MN10300 Page table definitions + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_PAGE_H +#define _ASM_PAGE_H + +/* PAGE_SHIFT determines the page size */ +#define PAGE_SHIFT 12 + +#ifndef __ASSEMBLY__ +#define PAGE_SIZE (1UL << PAGE_SHIFT) +#define PAGE_MASK (~(PAGE_SIZE - 1)) +#else +#define PAGE_SIZE +(1 << PAGE_SHIFT) /* unary plus marks an + * immediate val not an addr */ +#define PAGE_MASK +(~(PAGE_SIZE - 1)) +#endif + +#ifdef __KERNEL__ +#ifndef __ASSEMBLY__ + +#define clear_page(page) memset((void *)(page), 0, PAGE_SIZE) +#define copy_page(to, from) memcpy((void *)(to), (void *)(from), PAGE_SIZE) + +#define clear_user_page(addr, vaddr, page) clear_page(addr) +#define copy_user_page(vto, vfrom, vaddr, to) copy_page(vto, vfrom) + +/* + * 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; +typedef struct page *pgtable_t; + +#define PTE_MASK PAGE_MASK +#define HPAGE_SHIFT 22 + +#ifdef CONFIG_HUGETLB_PAGE +#define HPAGE_SIZE ((1UL) << HPAGE_SHIFT) +#define HPAGE_MASK (~(HPAGE_SIZE - 1)) +#define HUGETLB_PAGE_ORDER (HPAGE_SHIFT - PAGE_SHIFT) +#endif + +#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) }) + +#include <asm-generic/pgtable-nopmd.h> + +#endif /* !__ASSEMBLY__ */ + +/* + * This handles the memory map.. We could make this a config + * option, but too many people screw it up, and too few need + * it. + * + * A __PAGE_OFFSET of 0xC0000000 means that the kernel has + * a virtual address space of one gigabyte, which limits the + * amount of physical memory you can use to about 950MB. + */ + +#ifndef __ASSEMBLY__ + +/* Pure 2^n version of get_order */ +static inline int get_order(unsigned long size) __attribute__((const)); +static inline int get_order(unsigned long size) +{ + int order; + + size = (size - 1) >> (PAGE_SHIFT - 1); + order = -1; + do { + size >>= 1; + order++; + } while (size); + return order; +} + +#endif /* __ASSEMBLY__ */ + +#include <asm/page_offset.h> + +#define __PAGE_OFFSET (PAGE_OFFSET_RAW) +#define PAGE_OFFSET ((unsigned long) __PAGE_OFFSET) + +/* + * main RAM and kernel working space are coincident at 0x90000000, but to make + * life more interesting, there's also an uncached virtual shadow at 0xb0000000 + * - these mappings are fixed in the MMU + */ +#define __pfn_disp (CONFIG_KERNEL_RAM_BASE_ADDRESS >> PAGE_SHIFT) + +#define __pa(x) ((unsigned long)(x)) +#define __va(x) ((void *)(unsigned long)(x)) +#define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) +#define pfn_to_page(pfn) (mem_map + ((pfn) - __pfn_disp)) +#define page_to_pfn(page) ((unsigned long)((page) - mem_map) + __pfn_disp) + +#define pfn_valid(pfn) \ +({ \ + unsigned long __pfn = (pfn) - __pfn_disp; \ + __pfn < max_mapnr; \ +}) + +#define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) +#define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT) +#define page_to_phys(page) (page_to_pfn(page) << PAGE_SHIFT) + +#define VM_DATA_DEFAULT_FLAGS \ + (VM_READ | VM_WRITE | \ + ((current->personality & READ_IMPLIES_EXEC) ? VM_EXEC : 0) | \ + VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) + +#endif /* __KERNEL__ */ + +#endif /* _ASM_PAGE_H */ diff --git a/arch/mn10300/include/asm/page_offset.h b/arch/mn10300/include/asm/page_offset.h new file mode 100644 index 00000000..8eb5b16a --- /dev/null +++ b/arch/mn10300/include/asm/page_offset.h @@ -0,0 +1,11 @@ +/* MN10300 Kernel base address + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ +#ifndef _ASM_PAGE_OFFSET_H +#define _ASM_PAGE_OFFSET_H + +#define PAGE_OFFSET_RAW CONFIG_KERNEL_RAM_BASE_ADDRESS + +#endif diff --git a/arch/mn10300/include/asm/param.h b/arch/mn10300/include/asm/param.h new file mode 100644 index 00000000..789b1df4 --- /dev/null +++ b/arch/mn10300/include/asm/param.h @@ -0,0 +1,34 @@ +/* MN10300 Kernel parameters + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_PARAM_H +#define _ASM_PARAM_H + +#ifdef __KERNEL__ +#define HZ CONFIG_HZ /* Internal kernel timer frequency */ +#define USER_HZ 100 /* .. some user interfaces are in + * "ticks" */ +#define CLOCKS_PER_SEC (USER_HZ) /* like times() */ +#endif + +#ifndef HZ +#define HZ 100 +#endif + +#define EXEC_PAGESIZE 4096 + +#ifndef NOGROUP +#define NOGROUP (-1) +#endif + +#define MAXHOSTNAMELEN 64 /* max length of hostname */ +#define COMMAND_LINE_SIZE 256 + +#endif /* _ASM_PARAM_H */ diff --git a/arch/mn10300/include/asm/pci.h b/arch/mn10300/include/asm/pci.h new file mode 100644 index 00000000..6095a285 --- /dev/null +++ b/arch/mn10300/include/asm/pci.h @@ -0,0 +1,122 @@ +/* MN10300 PCI definitions + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_PCI_H +#define _ASM_PCI_H + +#ifdef __KERNEL__ +#include <linux/mm.h> /* for struct page */ + +#if 0 +#define __pcbdebug(FMT, ADDR, ...) \ + printk(KERN_DEBUG "PCIBRIDGE[%08x]: "FMT"\n", \ + (u32)(ADDR), ##__VA_ARGS__) + +#define __pcidebug(FMT, BUS, DEVFN, WHERE,...) \ +do { \ + printk(KERN_DEBUG "PCI[%02x:%02x.%x + %02x]: "FMT"\n", \ + (BUS)->number, \ + PCI_SLOT(DEVFN), \ + PCI_FUNC(DEVFN), \ + (u32)(WHERE), ##__VA_ARGS__); \ +} while (0) + +#else +#define __pcbdebug(FMT, ADDR, ...) do {} while (0) +#define __pcidebug(FMT, BUS, DEVFN, WHERE, ...) do {} while (0) +#endif + +/* Can be used to override the logic in pci_scan_bus for skipping + * already-configured bus numbers - to be used for buggy BIOSes or + * architectures with incomplete PCI setup by the loader */ + +#ifdef CONFIG_PCI +#define pcibios_assign_all_busses() 1 +extern void unit_pci_init(void); +#else +#define pcibios_assign_all_busses() 0 +#endif + +extern unsigned long pci_mem_start; +#define PCIBIOS_MIN_IO 0xBE000004 +#define PCIBIOS_MIN_MEM 0xB8000000 + +void pcibios_set_master(struct pci_dev *dev); +void pcibios_penalize_isa_irq(int irq); + +/* Dynamic DMA mapping stuff. + * i386 has everything mapped statically. + */ + +#include <linux/types.h> +#include <linux/slab.h> +#include <asm/scatterlist.h> +#include <linux/string.h> +#include <asm/io.h> + +struct pci_dev; + +/* The PCI address space does equal the physical memory + * address space. The networking and block device layers use + * this boolean for bounce buffer decisions. + */ +#define PCI_DMA_BUS_IS_PHYS (1) + +/* Return the index of the PCI controller for device. */ +static inline int pci_controller_num(struct pci_dev *dev) +{ + return 0; +} + +#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__ */ + +/* implement the pci_ DMA API in terms of the generic device dma_ one */ +#include <asm-generic/pci-dma-compat.h> + +/** + * pcibios_resource_to_bus - convert resource to PCI bus address + * @dev: device which owns this resource + * @region: converted bus-centric region (start,end) + * @res: resource to convert + * + * Convert a resource to a PCI device bus address or bus window. + */ +extern void pcibios_resource_to_bus(struct pci_dev *dev, + struct pci_bus_region *region, + struct resource *res); + +extern void pcibios_bus_to_resource(struct pci_dev *dev, + struct resource *res, + struct pci_bus_region *region); + +static inline struct resource * +pcibios_select_root(struct pci_dev *pdev, struct resource *res) +{ + struct resource *root = NULL; + + if (res->flags & IORESOURCE_IO) + root = &ioport_resource; + if (res->flags & IORESOURCE_MEM) + root = &iomem_resource; + + return root; +} + +static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel) +{ + return channel ? 15 : 14; +} + +#endif /* _ASM_PCI_H */ diff --git a/arch/mn10300/include/asm/percpu.h b/arch/mn10300/include/asm/percpu.h new file mode 100644 index 00000000..06a959d6 --- /dev/null +++ b/arch/mn10300/include/asm/percpu.h @@ -0,0 +1 @@ +#include <asm-generic/percpu.h> diff --git a/arch/mn10300/include/asm/pgalloc.h b/arch/mn10300/include/asm/pgalloc.h new file mode 100644 index 00000000..146bacf1 --- /dev/null +++ b/arch/mn10300/include/asm/pgalloc.h @@ -0,0 +1,55 @@ +/* MN10300 Page and page table/directory allocation + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_PGALLOC_H +#define _ASM_PGALLOC_H + +#include <asm/page.h> +#include <linux/threads.h> +#include <linux/mm.h> /* for struct page */ + +struct mm_struct; +struct page; + +/* attach a page table to a PMD entry */ +#define pmd_populate_kernel(mm, pmd, pte) \ + set_pmd(pmd, __pmd(__pa(pte) | _PAGE_TABLE)) + +static inline +void pmd_populate(struct mm_struct *mm, pmd_t *pmd, struct page *pte) +{ + set_pmd(pmd, __pmd((page_to_pfn(pte) << PAGE_SHIFT) | _PAGE_TABLE)); +} +#define pmd_pgtable(pmd) pmd_page(pmd) + +/* + * Allocate and free page tables. + */ + +extern pgd_t *pgd_alloc(struct mm_struct *); +extern void pgd_free(struct mm_struct *, pgd_t *); + +extern pte_t *pte_alloc_one_kernel(struct mm_struct *, unsigned long); +extern struct page *pte_alloc_one(struct mm_struct *, unsigned long); + +static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte) +{ + free_page((unsigned long) pte); +} + +static inline void pte_free(struct mm_struct *mm, struct page *pte) +{ + __free_page(pte); +} + + +#define __pte_free_tlb(tlb, pte, addr) tlb_remove_page((tlb), (pte)) + +#endif /* _ASM_PGALLOC_H */ diff --git a/arch/mn10300/include/asm/pgtable.h b/arch/mn10300/include/asm/pgtable.h new file mode 100644 index 00000000..a1e894b5 --- /dev/null +++ b/arch/mn10300/include/asm/pgtable.h @@ -0,0 +1,506 @@ +/* MN10300 Page table manipulators and constants + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + * + * + * The Linux memory management assumes a three-level page table setup. On + * the i386, we use that, but "fold" the mid level into the top-level page + * table, so that we physically have the same two-level page table as the + * i386 mmu expects. + * + * This file contains the functions and defines necessary to modify and use + * the i386 page table tree for the purposes of the MN10300 TLB handler + * functions. + */ +#ifndef _ASM_PGTABLE_H +#define _ASM_PGTABLE_H + +#include <asm/cpu-regs.h> + +#ifndef __ASSEMBLY__ +#include <asm/processor.h> +#include <asm/cache.h> +#include <linux/threads.h> + +#include <asm/bitops.h> + +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/spinlock.h> + +/* + * ZERO_PAGE is a global shared page that is always zero: used + * for zero-mapped memory areas etc.. + */ +#define ZERO_PAGE(vaddr) (virt_to_page(empty_zero_page)) +extern unsigned long empty_zero_page[1024]; +extern spinlock_t pgd_lock; +extern struct page *pgd_list; + +extern void pmd_ctor(void *, struct kmem_cache *, unsigned long); +extern void pgtable_cache_init(void); +extern void paging_init(void); + +#endif /* !__ASSEMBLY__ */ + +/* + * The Linux mn10300 paging architecture only implements both the traditional + * 2-level page tables + */ +#define PGDIR_SHIFT 22 +#define PTRS_PER_PGD 1024 +#define PTRS_PER_PUD 1 /* we don't really have any PUD physically */ +#define PTRS_PER_PMD 1 /* we don't really have any PMD physically */ +#define PTRS_PER_PTE 1024 + +#define PGD_SIZE PAGE_SIZE +#define PMD_SIZE (1UL << PMD_SHIFT) +#define PGDIR_SIZE (1UL << PGDIR_SHIFT) +#define PGDIR_MASK (~(PGDIR_SIZE - 1)) + +#define USER_PTRS_PER_PGD (TASK_SIZE / PGDIR_SIZE) +#define FIRST_USER_ADDRESS 0 + +#define USER_PGD_PTRS (PAGE_OFFSET >> PGDIR_SHIFT) +#define KERNEL_PGD_PTRS (PTRS_PER_PGD - USER_PGD_PTRS) + +#define TWOLEVEL_PGDIR_SHIFT 22 +#define BOOT_USER_PGD_PTRS (__PAGE_OFFSET >> TWOLEVEL_PGDIR_SHIFT) +#define BOOT_KERNEL_PGD_PTRS (1024 - BOOT_USER_PGD_PTRS) + +#ifndef __ASSEMBLY__ +extern pgd_t swapper_pg_dir[PTRS_PER_PGD]; +#endif + +/* + * Unfortunately, due to the way the MMU works on the MN10300, the vmalloc VM + * area has to be in the lower half of the virtual address range (the upper + * half is not translated through the TLB). + * + * So in this case, the vmalloc area goes at the bottom of the address map + * (leaving a hole at the very bottom to catch addressing errors), and + * userspace starts immediately above. + * + * The vmalloc() routines also leaves a hole of 4kB between each vmalloced + * area to catch addressing errors. + */ +#ifndef __ASSEMBLY__ +#define VMALLOC_OFFSET (8UL * 1024 * 1024) +#define VMALLOC_START (0x70000000UL) +#define VMALLOC_END (0x7C000000UL) +#else +#define VMALLOC_OFFSET (8 * 1024 * 1024) +#define VMALLOC_START (0x70000000) +#define VMALLOC_END (0x7C000000) +#endif + +#ifndef __ASSEMBLY__ +extern pte_t kernel_vmalloc_ptes[(VMALLOC_END - VMALLOC_START) / PAGE_SIZE]; +#endif + +/* IPTEL2/DPTEL2 bit assignments */ +#define _PAGE_BIT_VALID xPTEL2_V_BIT +#define _PAGE_BIT_CACHE xPTEL2_C_BIT +#define _PAGE_BIT_PRESENT xPTEL2_PV_BIT +#define _PAGE_BIT_DIRTY xPTEL2_D_BIT +#define _PAGE_BIT_GLOBAL xPTEL2_G_BIT +#define _PAGE_BIT_ACCESSED xPTEL2_UNUSED1_BIT /* mustn't be loaded into IPTEL2/DPTEL2 */ + +#define _PAGE_VALID xPTEL2_V +#define _PAGE_CACHE xPTEL2_C +#define _PAGE_PRESENT xPTEL2_PV +#define _PAGE_DIRTY xPTEL2_D +#define _PAGE_PROT xPTEL2_PR +#define _PAGE_PROT_RKNU xPTEL2_PR_ROK +#define _PAGE_PROT_WKNU xPTEL2_PR_RWK +#define _PAGE_PROT_RKRU xPTEL2_PR_ROK_ROU +#define _PAGE_PROT_WKRU xPTEL2_PR_RWK_ROU +#define _PAGE_PROT_WKWU xPTEL2_PR_RWK_RWU +#define _PAGE_GLOBAL xPTEL2_G +#define _PAGE_PS_MASK xPTEL2_PS +#define _PAGE_PS_4Kb xPTEL2_PS_4Kb +#define _PAGE_PS_128Kb xPTEL2_PS_128Kb +#define _PAGE_PS_1Kb xPTEL2_PS_1Kb +#define _PAGE_PS_4Mb xPTEL2_PS_4Mb +#define _PAGE_PSE xPTEL2_PS_4Mb /* 4MB page */ +#define _PAGE_CACHE_WT xPTEL2_CWT +#define _PAGE_ACCESSED xPTEL2_UNUSED1 +#define _PAGE_NX 0 /* no-execute bit */ + +/* If _PAGE_VALID is clear, we use these: */ +#define _PAGE_FILE xPTEL2_C /* set:pagecache unset:swap */ +#define _PAGE_PROTNONE 0x000 /* If not present */ + +#define __PAGE_PROT_UWAUX 0x010 +#define __PAGE_PROT_USER 0x020 +#define __PAGE_PROT_WRITE 0x040 + +#define _PAGE_PRESENTV (_PAGE_PRESENT|_PAGE_VALID) + +#ifndef __ASSEMBLY__ + +#define VMALLOC_VMADDR(x) ((unsigned long)(x)) + +#define _PAGE_TABLE (_PAGE_PRESENTV | _PAGE_PROT_WKNU | _PAGE_ACCESSED | _PAGE_DIRTY) +#define _PAGE_CHG_MASK (PTE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY) + +#define __PAGE_NONE (_PAGE_PRESENTV | _PAGE_PROT_RKNU | _PAGE_ACCESSED | _PAGE_CACHE) +#define __PAGE_SHARED (_PAGE_PRESENTV | _PAGE_PROT_WKWU | _PAGE_ACCESSED | _PAGE_CACHE) +#define __PAGE_COPY (_PAGE_PRESENTV | _PAGE_PROT_RKRU | _PAGE_ACCESSED | _PAGE_CACHE) +#define __PAGE_READONLY (_PAGE_PRESENTV | _PAGE_PROT_RKRU | _PAGE_ACCESSED | _PAGE_CACHE) + +#define PAGE_NONE __pgprot(__PAGE_NONE | _PAGE_NX) +#define PAGE_SHARED_NOEXEC __pgprot(__PAGE_SHARED | _PAGE_NX) +#define PAGE_COPY_NOEXEC __pgprot(__PAGE_COPY | _PAGE_NX) +#define PAGE_READONLY_NOEXEC __pgprot(__PAGE_READONLY | _PAGE_NX) +#define PAGE_SHARED_EXEC __pgprot(__PAGE_SHARED) +#define PAGE_COPY_EXEC __pgprot(__PAGE_COPY) +#define PAGE_READONLY_EXEC __pgprot(__PAGE_READONLY) +#define PAGE_COPY PAGE_COPY_NOEXEC +#define PAGE_READONLY PAGE_READONLY_NOEXEC +#define PAGE_SHARED PAGE_SHARED_EXEC + +#define __PAGE_KERNEL_BASE (_PAGE_PRESENTV | _PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_GLOBAL) + +#define __PAGE_KERNEL (__PAGE_KERNEL_BASE | _PAGE_PROT_WKNU | _PAGE_CACHE | _PAGE_NX) +#define __PAGE_KERNEL_NOCACHE (__PAGE_KERNEL_BASE | _PAGE_PROT_WKNU | _PAGE_NX) +#define __PAGE_KERNEL_EXEC (__PAGE_KERNEL & ~_PAGE_NX) +#define __PAGE_KERNEL_RO (__PAGE_KERNEL_BASE | _PAGE_PROT_RKNU | _PAGE_CACHE | _PAGE_NX) +#define __PAGE_KERNEL_LARGE (__PAGE_KERNEL | _PAGE_PSE) +#define __PAGE_KERNEL_LARGE_EXEC (__PAGE_KERNEL_EXEC | _PAGE_PSE) + +#define PAGE_KERNEL __pgprot(__PAGE_KERNEL) +#define PAGE_KERNEL_RO __pgprot(__PAGE_KERNEL_RO) +#define PAGE_KERNEL_EXEC __pgprot(__PAGE_KERNEL_EXEC) +#define PAGE_KERNEL_NOCACHE __pgprot(__PAGE_KERNEL_NOCACHE) +#define PAGE_KERNEL_LARGE __pgprot(__PAGE_KERNEL_LARGE) +#define PAGE_KERNEL_LARGE_EXEC __pgprot(__PAGE_KERNEL_LARGE_EXEC) + +#define __PAGE_USERIO (__PAGE_KERNEL_BASE | _PAGE_PROT_WKWU | _PAGE_NX) +#define PAGE_USERIO __pgprot(__PAGE_USERIO) + +/* + * Whilst the MN10300 can do page protection for execute (given separate data + * and insn TLBs), we are not supporting it at the moment. Write permission, + * however, always implies read permission (but not execute permission). + */ +#define __P000 PAGE_NONE +#define __P001 PAGE_READONLY_NOEXEC +#define __P010 PAGE_COPY_NOEXEC +#define __P011 PAGE_COPY_NOEXEC +#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_NOEXEC +#define __S010 PAGE_SHARED_NOEXEC +#define __S011 PAGE_SHARED_NOEXEC +#define __S100 PAGE_READONLY_EXEC +#define __S101 PAGE_READONLY_EXEC +#define __S110 PAGE_SHARED_EXEC +#define __S111 PAGE_SHARED_EXEC + +/* + * Define this to warn about kernel memory accesses that are + * done without a 'verify_area(VERIFY_WRITE,..)' + */ +#undef TEST_VERIFY_AREA + +#define pte_present(x) (pte_val(x) & _PAGE_VALID) +#define pte_clear(mm, addr, xp) \ +do { \ + set_pte_at((mm), (addr), (xp), __pte(0)); \ +} while (0) + +#define pmd_none(x) (!pmd_val(x)) +#define pmd_present(x) (!pmd_none(x)) +#define pmd_clear(xp) do { set_pmd(xp, __pmd(0)); } while (0) +#define pmd_bad(x) 0 + + +#define pages_to_mb(x) ((x) >> (20 - PAGE_SHIFT)) + +#ifndef __ASSEMBLY__ + +/* + * The following only work if pte_present() is true. + * Undefined behaviour if not.. + */ +static inline int pte_user(pte_t pte) { return pte_val(pte) & __PAGE_PROT_USER; } +static inline int pte_read(pte_t pte) { return pte_val(pte) & __PAGE_PROT_USER; } +static inline int pte_dirty(pte_t pte) { return pte_val(pte) & _PAGE_DIRTY; } +static inline int pte_young(pte_t pte) { return pte_val(pte) & _PAGE_ACCESSED; } +static inline int pte_write(pte_t pte) { return pte_val(pte) & __PAGE_PROT_WRITE; } +static inline int pte_special(pte_t pte){ return 0; } + +/* + * The following only works if pte_present() is not true. + */ +static inline int pte_file(pte_t pte) { return pte_val(pte) & _PAGE_FILE; } + +static inline pte_t pte_rdprotect(pte_t pte) +{ + pte_val(pte) &= ~(__PAGE_PROT_USER|__PAGE_PROT_UWAUX); return pte; +} +static inline pte_t pte_exprotect(pte_t pte) +{ + pte_val(pte) |= _PAGE_NX; return pte; +} + +static inline pte_t pte_wrprotect(pte_t pte) +{ + pte_val(pte) &= ~(__PAGE_PROT_WRITE|__PAGE_PROT_UWAUX); return pte; +} + +static inline pte_t pte_mkclean(pte_t pte) { pte_val(pte) &= ~_PAGE_DIRTY; return pte; } +static inline pte_t pte_mkold(pte_t pte) { pte_val(pte) &= ~_PAGE_ACCESSED; return pte; } +static inline pte_t pte_mkdirty(pte_t pte) { pte_val(pte) |= _PAGE_DIRTY; return pte; } +static inline pte_t pte_mkyoung(pte_t pte) { pte_val(pte) |= _PAGE_ACCESSED; return pte; } +static inline pte_t pte_mkexec(pte_t pte) { pte_val(pte) &= ~_PAGE_NX; return pte; } + +static inline pte_t pte_mkread(pte_t pte) +{ + pte_val(pte) |= __PAGE_PROT_USER; + if (pte_write(pte)) + pte_val(pte) |= __PAGE_PROT_UWAUX; + return pte; +} +static inline pte_t pte_mkwrite(pte_t pte) +{ + pte_val(pte) |= __PAGE_PROT_WRITE; + if (pte_val(pte) & __PAGE_PROT_USER) + pte_val(pte) |= __PAGE_PROT_UWAUX; + return pte; +} + +static inline pte_t pte_mkspecial(pte_t pte) { return pte; } + +#define pte_ERROR(e) \ + printk(KERN_ERR "%s:%d: bad pte %08lx.\n", \ + __FILE__, __LINE__, pte_val(e)) +#define pgd_ERROR(e) \ + printk(KERN_ERR "%s:%d: bad pgd %08lx.\n", \ + __FILE__, __LINE__, pgd_val(e)) + +/* + * The "pgd_xxx()" functions here are trivial for a folded two-level + * setup: the pgd is never bad, and a pmd always exists (as it's folded + * into the pgd entry) + */ +#define pgd_clear(xp) do { } while (0) + +/* + * Certain architectures need to do special things when PTEs + * within a page table are directly modified. Thus, the following + * hook is made available. + */ +#define set_pte(pteptr, pteval) (*(pteptr) = pteval) +#define set_pte_at(mm, addr, ptep, pteval) set_pte((ptep), (pteval)) +#define set_pte_atomic(pteptr, pteval) set_pte((pteptr), (pteval)) + +/* + * (pmds are folded into pgds so this doesn't get actually called, + * but the define is needed for a generic inline function.) + */ +#define set_pmd(pmdptr, pmdval) (*(pmdptr) = pmdval) + +#define ptep_get_and_clear(mm, addr, ptep) \ + __pte(xchg(&(ptep)->pte, 0)) +#define pte_same(a, b) (pte_val(a) == pte_val(b)) +#define pte_page(x) pfn_to_page(pte_pfn(x)) +#define pte_none(x) (!pte_val(x)) +#define pte_pfn(x) ((unsigned long) (pte_val(x) >> PAGE_SHIFT)) +#define __pfn_addr(pfn) ((pfn) << PAGE_SHIFT) +#define pfn_pte(pfn, prot) __pte(__pfn_addr(pfn) | pgprot_val(prot)) +#define pfn_pmd(pfn, prot) __pmd(__pfn_addr(pfn) | pgprot_val(prot)) + +/* + * All present user pages are user-executable: + */ +static inline int pte_exec(pte_t pte) +{ + return pte_user(pte); +} + +/* + * All present pages are kernel-executable: + */ +static inline int pte_exec_kernel(pte_t pte) +{ + return 1; +} + +#define PTE_FILE_MAX_BITS 30 + +#define pte_to_pgoff(pte) (pte_val(pte) >> 2) +#define pgoff_to_pte(off) __pte((off) << 2 | _PAGE_FILE) + +/* Encode and de-code a swap entry */ +#define __swp_type(x) (((x).val >> 2) & 0x3f) +#define __swp_offset(x) ((x).val >> 8) +#define __swp_entry(type, offset) \ + ((swp_entry_t) { ((type) << 2) | ((offset) << 8) }) +#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) +#define __swp_entry_to_pte(x) __pte((x).val) + +static inline +int ptep_test_and_clear_dirty(struct vm_area_struct *vma, unsigned long addr, + pte_t *ptep) +{ + if (!pte_dirty(*ptep)) + return 0; + return test_and_clear_bit(_PAGE_BIT_DIRTY, &ptep->pte); +} + +static inline +int ptep_test_and_clear_young(struct vm_area_struct *vma, unsigned long addr, + pte_t *ptep) +{ + if (!pte_young(*ptep)) + return 0; + return test_and_clear_bit(_PAGE_BIT_ACCESSED, &ptep->pte); +} + +static inline +void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr, pte_t *ptep) +{ + pte_val(*ptep) &= ~(__PAGE_PROT_WRITE|__PAGE_PROT_UWAUX); +} + +static inline void ptep_mkdirty(pte_t *ptep) +{ + set_bit(_PAGE_BIT_DIRTY, &ptep->pte); +} + +/* + * Macro to mark a page protection value as "uncacheable". On processors which + * do not support it, this is a no-op. + */ +#define pgprot_noncached(prot) __pgprot(pgprot_val(prot) & ~_PAGE_CACHE) + +/* + * Macro to mark a page protection value as "Write-Through". + * On processors which do not support it, this is a no-op. + */ +#define pgprot_through(prot) __pgprot(pgprot_val(prot) | _PAGE_CACHE_WT) + +/* + * 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, pgprot) pfn_pte(page_to_pfn(page), (pgprot)) +#define mk_pte_huge(entry) \ + ((entry).pte |= _PAGE_PRESENT | _PAGE_PSE | _PAGE_VALID) + +static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) +{ + pte_val(pte) &= _PAGE_CHG_MASK; + pte_val(pte) |= pgprot_val(newprot); + return pte; +} + +#define page_pte(page) page_pte_prot((page), __pgprot(0)) + +#define pmd_page_kernel(pmd) \ + ((unsigned long) __va(pmd_val(pmd) & PAGE_MASK)) + +#define pmd_page(pmd) pfn_to_page(pmd_val(pmd) >> PAGE_SHIFT) + +#define pmd_large(pmd) \ + ((pmd_val(pmd) & (_PAGE_PSE | _PAGE_PRESENT)) == \ + (_PAGE_PSE | _PAGE_PRESENT)) + +/* + * the pgd page can be thought of an array like this: pgd_t[PTRS_PER_PGD] + * + * this macro returns the index of the entry in the pgd page which would + * control the given virtual address + */ +#define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1)) + +/* + * pgd_offset() returns a (pgd_t *) + * pgd_index() is used get the offset into the pgd page's array of pgd_t's; + */ +#define pgd_offset(mm, address) ((mm)->pgd + pgd_index(address)) + +/* + * a shortcut which implies the use of the kernel's pgd, instead + * of a process's + */ +#define pgd_offset_k(address) pgd_offset(&init_mm, address) + +/* + * the pmd page can be thought of an array like this: pmd_t[PTRS_PER_PMD] + * + * this macro returns the index of the entry in the pmd page which would + * control the given virtual address + */ +#define pmd_index(address) \ + (((address) >> PMD_SHIFT) & (PTRS_PER_PMD - 1)) + +/* + * the pte page can be thought of an array like this: pte_t[PTRS_PER_PTE] + * + * this macro returns the index of the entry in the pte page which would + * control the given virtual address + */ +#define pte_index(address) \ + (((address) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) + +#define pte_offset_kernel(dir, address) \ + ((pte_t *) pmd_page_kernel(*(dir)) + pte_index(address)) + +/* + * Make a given kernel text page executable/non-executable. + * Returns the previous executability setting of that page (which + * is used to restore the previous state). Used by the SMP bootup code. + * NOTE: this is an __init function for security reasons. + */ +static inline int set_kernel_exec(unsigned long vaddr, int enable) +{ + return 0; +} + +#define pte_offset_map(dir, address) \ + ((pte_t *) page_address(pmd_page(*(dir))) + pte_index(address)) +#define pte_unmap(pte) do {} while (0) + +/* + * The MN10300 has external MMU info in the form of a TLB: this is adapted from + * the kernel page tables containing the necessary information by tlb-mn10300.S + */ +extern void update_mmu_cache(struct vm_area_struct *vma, + unsigned long address, pte_t *ptep); + +#endif /* !__ASSEMBLY__ */ + +#define kern_addr_valid(addr) (1) + +#define io_remap_pfn_range(vma, vaddr, pfn, size, prot) \ + remap_pfn_range((vma), (vaddr), (pfn), (size), (prot)) + +#define MK_IOSPACE_PFN(space, pfn) (pfn) +#define GET_IOSPACE(pfn) 0 +#define GET_PFN(pfn) (pfn) + +#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG +#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_DIRTY +#define __HAVE_ARCH_PTEP_GET_AND_CLEAR +#define __HAVE_ARCH_PTEP_SET_WRPROTECT +#define __HAVE_ARCH_PTEP_MKDIRTY +#define __HAVE_ARCH_PTE_SAME +#include <asm-generic/pgtable.h> + +#endif /* !__ASSEMBLY__ */ + +#endif /* _ASM_PGTABLE_H */ diff --git a/arch/mn10300/include/asm/pio-regs.h b/arch/mn10300/include/asm/pio-regs.h new file mode 100644 index 00000000..96bc8182 --- /dev/null +++ b/arch/mn10300/include/asm/pio-regs.h @@ -0,0 +1,233 @@ +/* MN10300 On-board I/O port module registers + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_PIO_REGS_H +#define _ASM_PIO_REGS_H + +#include <asm/cpu-regs.h> +#include <asm/intctl-regs.h> + +#ifdef __KERNEL__ + +/* I/O port 0 */ +#define P0MD __SYSREG(0xdb000000, u16) /* mode reg */ +#define P0MD_0 0x0003 /* mask */ +#define P0MD_0_IN 0x0000 /* input mode */ +#define P0MD_0_OUT 0x0001 /* output mode */ +#define P0MD_0_TM0IO 0x0002 /* timer 0 I/O mode */ +#define P0MD_0_EYECLK 0x0003 /* test signal output (clock) */ +#define P0MD_1 0x000c +#define P0MD_1_IN 0x0000 +#define P0MD_1_OUT 0x0004 +#define P0MD_1_TM1IO 0x0008 /* timer 1 I/O mode */ +#define P0MD_1_EYED 0x000c /* test signal output (data) */ +#define P0MD_2 0x0030 +#define P0MD_2_IN 0x0000 +#define P0MD_2_OUT 0x0010 +#define P0MD_2_TM2IO 0x0020 /* timer 2 I/O mode */ +#define P0MD_3 0x00c0 +#define P0MD_3_IN 0x0000 +#define P0MD_3_OUT 0x0040 +#define P0MD_3_TM3IO 0x0080 /* timer 3 I/O mode */ +#define P0MD_4 0x0300 +#define P0MD_4_IN 0x0000 +#define P0MD_4_OUT 0x0100 +#define P0MD_4_TM4IO 0x0200 /* timer 4 I/O mode */ +#define P0MD_4_XCTS 0x0300 /* XCTS input for serial port 2 */ +#define P0MD_5 0x0c00 +#define P0MD_5_IN 0x0000 +#define P0MD_5_OUT 0x0400 +#define P0MD_5_TM5IO 0x0800 /* timer 5 I/O mode */ +#define P0MD_6 0x3000 +#define P0MD_6_IN 0x0000 +#define P0MD_6_OUT 0x1000 +#define P0MD_6_TM6IOA 0x2000 /* timer 6 I/O mode A */ +#define P0MD_7 0xc000 +#define P0MD_7_IN 0x0000 +#define P0MD_7_OUT 0x4000 +#define P0MD_7_TM6IOB 0x8000 /* timer 6 I/O mode B */ + +#define P0IN __SYSREG(0xdb000004, u8) /* in reg */ +#define P0OUT __SYSREG(0xdb000008, u8) /* out reg */ + +#define P0TMIO __SYSREG(0xdb00000c, u8) /* TM pin I/O control reg */ +#define P0TMIO_TM0_IN 0x00 +#define P0TMIO_TM0_OUT 0x01 +#define P0TMIO_TM1_IN 0x00 +#define P0TMIO_TM1_OUT 0x02 +#define P0TMIO_TM2_IN 0x00 +#define P0TMIO_TM2_OUT 0x04 +#define P0TMIO_TM3_IN 0x00 +#define P0TMIO_TM3_OUT 0x08 +#define P0TMIO_TM4_IN 0x00 +#define P0TMIO_TM4_OUT 0x10 +#define P0TMIO_TM5_IN 0x00 +#define P0TMIO_TM5_OUT 0x20 +#define P0TMIO_TM6A_IN 0x00 +#define P0TMIO_TM6A_OUT 0x40 +#define P0TMIO_TM6B_IN 0x00 +#define P0TMIO_TM6B_OUT 0x80 + +/* I/O port 1 */ +#define P1MD __SYSREG(0xdb000100, u16) /* mode reg */ +#define P1MD_0 0x0003 /* mask */ +#define P1MD_0_IN 0x0000 /* input mode */ +#define P1MD_0_OUT 0x0001 /* output mode */ +#define P1MD_0_TM7IO 0x0002 /* timer 7 I/O mode */ +#define P1MD_0_ADTRG 0x0003 /* A/D converter trigger mode */ +#define P1MD_1 0x000c +#define P1MD_1_IN 0x0000 +#define P1MD_1_OUT 0x0004 +#define P1MD_1_TM8IO 0x0008 /* timer 8 I/O mode */ +#define P1MD_1_XDMR0 0x000c /* DMA request input 0 mode */ +#define P1MD_2 0x0030 +#define P1MD_2_IN 0x0000 +#define P1MD_2_OUT 0x0010 +#define P1MD_2_TM9IO 0x0020 /* timer 9 I/O mode */ +#define P1MD_2_XDMR1 0x0030 /* DMA request input 1 mode */ +#define P1MD_3 0x00c0 +#define P1MD_3_IN 0x0000 +#define P1MD_3_OUT 0x0040 +#define P1MD_3_TM10IO 0x0080 /* timer 10 I/O mode */ +#define P1MD_3_FRQS0 0x00c0 /* CPU clock multiplier setting input 0 mode */ +#define P1MD_4 0x0300 +#define P1MD_4_IN 0x0000 +#define P1MD_4_OUT 0x0100 +#define P1MD_4_TM11IO 0x0200 /* timer 11 I/O mode */ +#define P1MD_4_FRQS1 0x0300 /* CPU clock multiplier setting input 1 mode */ + +#define P1IN __SYSREG(0xdb000104, u8) /* in reg */ +#define P1OUT __SYSREG(0xdb000108, u8) /* out reg */ +#define P1TMIO __SYSREG(0xdb00010c, u8) /* TM pin I/O control reg */ +#define P1TMIO_TM11_IN 0x00 +#define P1TMIO_TM11_OUT 0x01 +#define P1TMIO_TM10_IN 0x00 +#define P1TMIO_TM10_OUT 0x02 +#define P1TMIO_TM9_IN 0x00 +#define P1TMIO_TM9_OUT 0x04 +#define P1TMIO_TM8_IN 0x00 +#define P1TMIO_TM8_OUT 0x08 +#define P1TMIO_TM7_IN 0x00 +#define P1TMIO_TM7_OUT 0x10 + +/* I/O port 2 */ +#define P2MD __SYSREG(0xdb000200, u16) /* mode reg */ +#define P2MD_0 0x0003 /* mask */ +#define P2MD_0_IN 0x0000 /* input mode */ +#define P2MD_0_OUT 0x0001 /* output mode */ +#define P2MD_0_BOOTBW 0x0003 /* boot bus width selector mode */ +#define P2MD_1 0x000c +#define P2MD_1_IN 0x0000 +#define P2MD_1_OUT 0x0004 +#define P2MD_1_BOOTSEL 0x000c /* boot device selector mode */ +#define P2MD_2 0x0030 +#define P2MD_2_IN 0x0000 +#define P2MD_2_OUT 0x0010 +#define P2MD_3 0x00c0 +#define P2MD_3_IN 0x0000 +#define P2MD_3_OUT 0x0040 +#define P2MD_3_CKIO 0x00c0 /* mode */ +#define P2MD_4 0x0300 +#define P2MD_4_IN 0x0000 +#define P2MD_4_OUT 0x0100 +#define P2MD_4_CMOD 0x0300 /* mode */ + +#define P2IN __SYSREG(0xdb000204, u8) /* in reg */ +#define P2OUT __SYSREG(0xdb000208, u8) /* out reg */ +#define P2TMIO __SYSREG(0xdb00020c, u8) /* TM pin I/O control reg */ + +/* I/O port 3 */ +#define P3MD __SYSREG(0xdb000300, u16) /* mode reg */ +#define P3MD_0 0x0003 /* mask */ +#define P3MD_0_IN 0x0000 /* input mode */ +#define P3MD_0_OUT 0x0001 /* output mode */ +#define P3MD_0_AFRXD 0x0002 /* AFR interface mode */ +#define P3MD_1 0x000c +#define P3MD_1_IN 0x0000 +#define P3MD_1_OUT 0x0004 +#define P3MD_1_AFTXD 0x0008 /* AFR interface mode */ +#define P3MD_2 0x0030 +#define P3MD_2_IN 0x0000 +#define P3MD_2_OUT 0x0010 +#define P3MD_2_AFSCLK 0x0020 /* AFR interface mode */ +#define P3MD_3 0x00c0 +#define P3MD_3_IN 0x0000 +#define P3MD_3_OUT 0x0040 +#define P3MD_3_AFFS 0x0080 /* AFR interface mode */ +#define P3MD_4 0x0300 +#define P3MD_4_IN 0x0000 +#define P3MD_4_OUT 0x0100 +#define P3MD_4_AFEHC 0x0200 /* AFR interface mode */ + +#define P3IN __SYSREG(0xdb000304, u8) /* in reg */ +#define P3OUT __SYSREG(0xdb000308, u8) /* out reg */ + +/* I/O port 4 */ +#define P4MD __SYSREG(0xdb000400, u16) /* mode reg */ +#define P4MD_0 0x0003 /* mask */ +#define P4MD_0_IN 0x0000 /* input mode */ +#define P4MD_0_OUT 0x0001 /* output mode */ +#define P4MD_0_SCL0 0x0002 /* I2C/serial mode */ +#define P4MD_1 0x000c +#define P4MD_1_IN 0x0000 +#define P4MD_1_OUT 0x0004 +#define P4MD_1_SDA0 0x0008 +#define P4MD_2 0x0030 +#define P4MD_2_IN 0x0000 +#define P4MD_2_OUT 0x0010 +#define P4MD_2_SCL1 0x0020 +#define P4MD_3 0x00c0 +#define P4MD_3_IN 0x0000 +#define P4MD_3_OUT 0x0040 +#define P4MD_3_SDA1 0x0080 +#define P4MD_4 0x0300 +#define P4MD_4_IN 0x0000 +#define P4MD_4_OUT 0x0100 +#define P4MD_4_SBO0 0x0200 +#define P4MD_5 0x0c00 +#define P4MD_5_IN 0x0000 +#define P4MD_5_OUT 0x0400 +#define P4MD_5_SBO1 0x0800 +#define P4MD_6 0x3000 +#define P4MD_6_IN 0x0000 +#define P4MD_6_OUT 0x1000 +#define P4MD_6_SBT0 0x2000 +#define P4MD_7 0xc000 +#define P4MD_7_IN 0x0000 +#define P4MD_7_OUT 0x4000 +#define P4MD_7_SBT1 0x8000 + +#define P4IN __SYSREG(0xdb000404, u8) /* in reg */ +#define P4OUT __SYSREG(0xdb000408, u8) /* out reg */ + +/* I/O port 5 */ +#define P5MD __SYSREG(0xdb000500, u16) /* mode reg */ +#define P5MD_0 0x0003 /* mask */ +#define P5MD_0_IN 0x0000 /* input mode */ +#define P5MD_0_OUT 0x0001 /* output mode */ +#define P5MD_0_IRTXD 0x0002 /* IrDA mode */ +#define P5MD_0_SOUT 0x0004 /* serial mode */ +#define P5MD_1 0x000c +#define P5MD_1_IN 0x0000 +#define P5MD_1_OUT 0x0004 +#define P5MD_1_IRRXDS 0x0008 /* IrDA mode */ +#define P5MD_1_SIN 0x000c /* serial mode */ +#define P5MD_2 0x0030 +#define P5MD_2_IN 0x0000 +#define P5MD_2_OUT 0x0010 +#define P5MD_2_IRRXDF 0x0020 /* IrDA mode */ + +#define P5IN __SYSREG(0xdb000504, u8) /* in reg */ +#define P5OUT __SYSREG(0xdb000508, u8) /* out reg */ + + +#endif /* __KERNEL__ */ + +#endif /* _ASM_PIO_REGS_H */ diff --git a/arch/mn10300/include/asm/poll.h b/arch/mn10300/include/asm/poll.h new file mode 100644 index 00000000..c98509d3 --- /dev/null +++ b/arch/mn10300/include/asm/poll.h @@ -0,0 +1 @@ +#include <asm-generic/poll.h> diff --git a/arch/mn10300/include/asm/posix_types.h b/arch/mn10300/include/asm/posix_types.h new file mode 100644 index 00000000..56ffbc15 --- /dev/null +++ b/arch/mn10300/include/asm/posix_types.h @@ -0,0 +1,137 @@ +/* MN10300 POSIX types + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_POSIX_TYPES_H +#define _ASM_POSIX_TYPES_H + +/* + * This file is generally used by user-level software, so you need to + * be a little careful about namespace pollution etc. Also, we cannot + * assume GCC is being used. + */ + +typedef unsigned long __kernel_ino_t; +typedef unsigned short __kernel_mode_t; +typedef unsigned short __kernel_nlink_t; +typedef long __kernel_off_t; +typedef int __kernel_pid_t; +typedef unsigned short __kernel_ipc_pid_t; +typedef unsigned short __kernel_uid_t; +typedef unsigned short __kernel_gid_t; +#if __GNUC__ == 4 +typedef unsigned int __kernel_size_t; +typedef signed int __kernel_ssize_t; +#else +typedef unsigned long __kernel_size_t; +typedef signed long __kernel_ssize_t; +#endif +typedef int __kernel_ptrdiff_t; +typedef long __kernel_time_t; +typedef long __kernel_suseconds_t; +typedef long __kernel_clock_t; +typedef int __kernel_timer_t; +typedef int __kernel_clockid_t; +typedef int __kernel_daddr_t; +typedef char * __kernel_caddr_t; +typedef unsigned short __kernel_uid16_t; +typedef unsigned short __kernel_gid16_t; +typedef unsigned int __kernel_uid32_t; +typedef unsigned int __kernel_gid32_t; + +typedef unsigned short __kernel_old_uid_t; +typedef unsigned short __kernel_old_gid_t; +typedef unsigned short __kernel_old_dev_t; + +#ifdef __GNUC__ +typedef long long __kernel_loff_t; +#endif + +typedef struct { +#if defined(__KERNEL__) || defined(__USE_ALL) + int val[2]; +#else /* !defined(__KERNEL__) && !defined(__USE_ALL) */ + int __val[2]; +#endif /* !defined(__KERNEL__) && !defined(__USE_ALL) */ +} __kernel_fsid_t; + +#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2) + +#undef __FD_SET +static inline void __FD_SET(unsigned long __fd, __kernel_fd_set *__fdsetp) +{ + unsigned long __tmp = __fd / __NFDBITS; + unsigned long __rem = __fd % __NFDBITS; + __fdsetp->fds_bits[__tmp] |= (1UL<<__rem); +} + +#undef __FD_CLR +static inline void __FD_CLR(unsigned long __fd, __kernel_fd_set *__fdsetp) +{ + unsigned long __tmp = __fd / __NFDBITS; + unsigned long __rem = __fd % __NFDBITS; + __fdsetp->fds_bits[__tmp] &= ~(1UL<<__rem); +} + + +#undef __FD_ISSET +static inline int __FD_ISSET(unsigned long __fd, const __kernel_fd_set *__p) +{ + unsigned long __tmp = __fd / __NFDBITS; + unsigned long __rem = __fd % __NFDBITS; + return (__p->fds_bits[__tmp] & (1UL<<__rem)) != 0; +} + +/* + * This will unroll the loop for the normal constant case (8 ints, + * for a 256-bit fd_set) + */ +#undef __FD_ZERO +static inline void __FD_ZERO(__kernel_fd_set *__p) +{ + unsigned long *__tmp = __p->fds_bits; + int __i; + + if (__builtin_constant_p(__FDSET_LONGS)) { + switch (__FDSET_LONGS) { + case 16: + __tmp[ 0] = 0; __tmp[ 1] = 0; + __tmp[ 2] = 0; __tmp[ 3] = 0; + __tmp[ 4] = 0; __tmp[ 5] = 0; + __tmp[ 6] = 0; __tmp[ 7] = 0; + __tmp[ 8] = 0; __tmp[ 9] = 0; + __tmp[10] = 0; __tmp[11] = 0; + __tmp[12] = 0; __tmp[13] = 0; + __tmp[14] = 0; __tmp[15] = 0; + return; + + case 8: + __tmp[ 0] = 0; __tmp[ 1] = 0; + __tmp[ 2] = 0; __tmp[ 3] = 0; + __tmp[ 4] = 0; __tmp[ 5] = 0; + __tmp[ 6] = 0; __tmp[ 7] = 0; + return; + + case 4: + __tmp[ 0] = 0; __tmp[ 1] = 0; + __tmp[ 2] = 0; __tmp[ 3] = 0; + return; + } + } + __i = __FDSET_LONGS; + while (__i) { + __i--; + *__tmp = 0; + __tmp++; + } +} + +#endif /* defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2) */ + +#endif /* _ASM_POSIX_TYPES_H */ diff --git a/arch/mn10300/include/asm/processor.h b/arch/mn10300/include/asm/processor.h new file mode 100644 index 00000000..4c1b5cc1 --- /dev/null +++ b/arch/mn10300/include/asm/processor.h @@ -0,0 +1,192 @@ +/* MN10300 Processor specifics + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _ASM_PROCESSOR_H +#define _ASM_PROCESSOR_H + +#include <linux/threads.h> +#include <linux/thread_info.h> +#include <asm/page.h> +#include <asm/ptrace.h> +#include <asm/cpu-regs.h> +#include <asm/uaccess.h> +#include <asm/current.h> + +/* Forward declaration, a strange C thing */ +struct task_struct; +struct mm_struct; + +/* + * Default implementation of macro that returns current + * instruction pointer ("program counter"). + */ +#define current_text_addr() \ +({ \ + void *__pc; \ + asm("mov pc,%0" : "=a"(__pc)); \ + __pc; \ +}) + +extern void get_mem_info(unsigned long *mem_base, unsigned long *mem_size); + +extern void show_registers(struct pt_regs *regs); + +/* + * CPU type and hardware bug flags. Kept separately for each CPU. + * Members of this structure are referenced in head.S, so think twice + * before touching them. [mj] + */ + +struct mn10300_cpuinfo { + int type; + unsigned long loops_per_jiffy; + char hard_math; +}; + +extern struct mn10300_cpuinfo boot_cpu_data; + +#ifdef CONFIG_SMP +#if CONFIG_NR_CPUS < 2 || CONFIG_NR_CPUS > 8 +# error Sorry, NR_CPUS should be 2 to 8 +#endif +extern struct mn10300_cpuinfo cpu_data[]; +#define current_cpu_data cpu_data[smp_processor_id()] +#else /* CONFIG_SMP */ +#define cpu_data &boot_cpu_data +#define current_cpu_data boot_cpu_data +#endif /* CONFIG_SMP */ + +extern void identify_cpu(struct mn10300_cpuinfo *); +extern void print_cpu_info(struct mn10300_cpuinfo *); +extern void dodgy_tsc(void); +#define cpu_relax() barrier() + +/* + * User space process size: 1.75GB (default). + */ +#define TASK_SIZE 0x70000000 + +/* + * Where to put the userspace stack by default + */ +#define STACK_TOP 0x70000000 +#define STACK_TOP_MAX STACK_TOP + +/* This decides where the kernel will search for a free chunk of vm + * space during mmap's. + */ +#define TASK_UNMAPPED_BASE 0x30000000 + +struct fpu_state_struct { + unsigned long fs[32]; /* fpu registers */ + unsigned long fpcr; /* fpu control register */ +}; + +struct thread_struct { + struct pt_regs *uregs; /* userspace register frame */ + unsigned long pc; /* kernel PC */ + unsigned long sp; /* kernel SP */ + unsigned long a3; /* kernel FP */ + unsigned long wchan; + unsigned long usp; + unsigned long fpu_flags; +#define THREAD_USING_FPU 0x00000001 /* T if this task is using the FPU */ +#define THREAD_HAS_FPU 0x00000002 /* T if this task owns the FPU right now */ + struct fpu_state_struct fpu_state; +}; + +#define INIT_THREAD \ +{ \ + .uregs = init_uregs, \ + .pc = 0, \ + .sp = 0, \ + .a3 = 0, \ + .wchan = 0, \ +} + +#define INIT_MMAP \ +{ &init_mm, 0, 0, NULL, PAGE_SHARED, VM_READ | VM_WRITE | VM_EXEC, 1, \ + NULL, NULL } + +/* + * do necessary setup to start up a newly executed thread + * - need to discard the frame stacked by the kernel thread invoking the execve + * syscall (see RESTORE_ALL macro) + */ +static inline void start_thread(struct pt_regs *regs, + unsigned long new_pc, unsigned long new_sp) +{ + struct thread_info *ti = current_thread_info(); + struct pt_regs *frame0; + set_fs(USER_DS); + + frame0 = thread_info_to_uregs(ti); + frame0->epsw = EPSW_nSL | EPSW_IE | EPSW_IM; + frame0->pc = new_pc; + frame0->sp = new_sp; + ti->frame = frame0; +} + + +/* Free all resources held by a thread. */ +extern void release_thread(struct task_struct *); + +/* Prepare to copy thread state - unlazy all lazy status */ +extern void prepare_to_copy(struct task_struct *tsk); + +/* + * create a kernel thread without removing it from tasklists + */ +extern int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags); + +/* + * Return saved PC of a blocked thread. + */ +extern unsigned long thread_saved_pc(struct task_struct *tsk); + +unsigned long get_wchan(struct task_struct *p); + +#define task_pt_regs(task) ((task)->thread.uregs) +#define KSTK_EIP(task) (task_pt_regs(task)->pc) +#define KSTK_ESP(task) (task_pt_regs(task)->sp) + +#define KSTK_TOP(info) \ +({ \ + (unsigned long)(info) + THREAD_SIZE; \ +}) + +#define ARCH_HAS_PREFETCH +#define ARCH_HAS_PREFETCHW + +static inline void prefetch(const void *x) +{ +#ifdef CONFIG_MN10300_CACHE_ENABLED +#ifdef CONFIG_MN10300_PROC_MN103E010 + asm volatile ("nop; nop; dcpf (%0)" : : "r"(x)); +#else + asm volatile ("dcpf (%0)" : : "r"(x)); +#endif +#endif +} + +static inline void prefetchw(const void *x) +{ +#ifdef CONFIG_MN10300_CACHE_ENABLED +#ifdef CONFIG_MN10300_PROC_MN103E010 + asm volatile ("nop; nop; dcpf (%0)" : : "r"(x)); +#else + asm volatile ("dcpf (%0)" : : "r"(x)); +#endif +#endif +} + +#endif /* _ASM_PROCESSOR_H */ diff --git a/arch/mn10300/include/asm/ptrace.h b/arch/mn10300/include/asm/ptrace.h new file mode 100644 index 00000000..b6961811 --- /dev/null +++ b/arch/mn10300/include/asm/ptrace.h @@ -0,0 +1,99 @@ +/* MN10300 Exception frame layout and ptrace constants + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_PTRACE_H +#define _ASM_PTRACE_H + +#define PT_A3 0 +#define PT_A2 1 +#define PT_D3 2 +#define PT_D2 3 +#define PT_MCVF 4 +#define PT_MCRL 5 +#define PT_MCRH 6 +#define PT_MDRQ 7 +#define PT_E1 8 +#define PT_E0 9 +#define PT_E7 10 +#define PT_E6 11 +#define PT_E5 12 +#define PT_E4 13 +#define PT_E3 14 +#define PT_E2 15 +#define PT_SP 16 +#define PT_LAR 17 +#define PT_LIR 18 +#define PT_MDR 19 +#define PT_A1 20 +#define PT_A0 21 +#define PT_D1 22 +#define PT_D0 23 +#define PT_ORIG_D0 24 +#define PT_EPSW 25 +#define PT_PC 26 +#define NR_PTREGS 27 + +/* + * This defines the way registers are stored in the event of an exception + * - the strange order is due to the MOVM instruction + */ +struct pt_regs { + unsigned long a3; /* syscall arg 3 */ + unsigned long a2; /* syscall arg 4 */ + unsigned long d3; /* syscall arg 5 */ + unsigned long d2; /* syscall arg 6 */ + unsigned long mcvf; + unsigned long mcrl; + unsigned long mcrh; + unsigned long mdrq; + unsigned long e1; + unsigned long e0; + unsigned long e7; + unsigned long e6; + unsigned long e5; + unsigned long e4; + unsigned long e3; + unsigned long e2; + unsigned long sp; + unsigned long lar; + unsigned long lir; + unsigned long mdr; + unsigned long a1; + unsigned long a0; /* syscall arg 1 */ + unsigned long d1; /* syscall arg 2 */ + unsigned long d0; /* syscall ret */ + struct pt_regs *next; /* next frame pointer */ + unsigned long orig_d0; /* syscall number */ + unsigned long epsw; + unsigned long pc; +}; + +/* Arbitrarily choose the same ptrace numbers as used by the Sparc code. */ +#define PTRACE_GETREGS 12 +#define PTRACE_SETREGS 13 +#define PTRACE_GETFPREGS 14 +#define PTRACE_SETFPREGS 15 + +/* options set using PTRACE_SETOPTIONS */ +#define PTRACE_O_TRACESYSGOOD 0x00000001 + +#ifdef __KERNEL__ + +#define user_mode(regs) (((regs)->epsw & EPSW_nSL) == EPSW_nSL) +#define instruction_pointer(regs) ((regs)->pc) +#define user_stack_pointer(regs) ((regs)->sp) +extern void show_regs(struct pt_regs *); + +#define arch_has_single_step() (1) + +#define profile_pc(regs) ((regs)->pc) + +#endif /* __KERNEL__ */ +#endif /* _ASM_PTRACE_H */ diff --git a/arch/mn10300/include/asm/reset-regs.h b/arch/mn10300/include/asm/reset-regs.h new file mode 100644 index 00000000..10c7502a --- /dev/null +++ b/arch/mn10300/include/asm/reset-regs.h @@ -0,0 +1,64 @@ +/* MN10300 Reset controller and watchdog timer definitions + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _ASM_RESET_REGS_H +#define _ASM_RESET_REGS_H + +#include <asm/cpu-regs.h> +#include <asm/exceptions.h> + +#ifdef __KERNEL__ + +#ifdef CONFIG_MN10300_WD_TIMER +#define ARCH_HAS_NMI_WATCHDOG /* See include/linux/nmi.h */ +#endif + +/* + * watchdog timer registers + */ +#define WDBC __SYSREGC(0xc0001000, u8) /* watchdog binary counter reg */ + +#define WDCTR __SYSREG(0xc0001002, u8) /* watchdog timer control reg */ +#define WDCTR_WDCK 0x07 /* clock source selection */ +#define WDCTR_WDCK_256th 0x00 /* - OSCI/256 */ +#define WDCTR_WDCK_1024th 0x01 /* - OSCI/1024 */ +#define WDCTR_WDCK_2048th 0x02 /* - OSCI/2048 */ +#define WDCTR_WDCK_16384th 0x03 /* - OSCI/16384 */ +#define WDCTR_WDCK_65536th 0x04 /* - OSCI/65536 */ +#define WDCTR_WDRST 0x40 /* binary counter reset */ +#define WDCTR_WDCNE 0x80 /* watchdog timer enable */ + +#define RSTCTR __SYSREG(0xc0001004, u8) /* reset control reg */ +#define RSTCTR_CHIPRST 0x01 /* chip reset */ +#define RSTCTR_DBFRST 0x02 /* double fault reset flag */ +#define RSTCTR_WDTRST 0x04 /* watchdog timer reset flag */ +#define RSTCTR_WDREN 0x08 /* watchdog timer reset enable */ + +#ifndef __ASSEMBLY__ + +static inline void mn10300_proc_hard_reset(void) +{ + RSTCTR &= ~RSTCTR_CHIPRST; + RSTCTR |= RSTCTR_CHIPRST; +} + +extern unsigned int watchdog_alert_counter[]; + +extern void watchdog_go(void); +extern asmlinkage void watchdog_handler(void); +extern asmlinkage +void watchdog_interrupt(struct pt_regs *, enum exception_code); + +#endif + +#endif /* __KERNEL__ */ + +#endif /* _ASM_RESET_REGS_H */ diff --git a/arch/mn10300/include/asm/resource.h b/arch/mn10300/include/asm/resource.h new file mode 100644 index 00000000..04bc4db8 --- /dev/null +++ b/arch/mn10300/include/asm/resource.h @@ -0,0 +1 @@ +#include <asm-generic/resource.h> diff --git a/arch/mn10300/include/asm/rtc-regs.h b/arch/mn10300/include/asm/rtc-regs.h new file mode 100644 index 00000000..c42deefa --- /dev/null +++ b/arch/mn10300/include/asm/rtc-regs.h @@ -0,0 +1,86 @@ +/* MN10300 on-chip Real-Time Clock registers + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_RTC_REGS_H +#define _ASM_RTC_REGS_H + +#include <asm/intctl-regs.h> + +#ifdef __KERNEL__ + +#define RTSCR __SYSREG(0xd8600000, u8) /* RTC seconds count reg */ +#define RTSAR __SYSREG(0xd8600001, u8) /* RTC seconds alarm reg */ +#define RTMCR __SYSREG(0xd8600002, u8) /* RTC minutes count reg */ +#define RTMAR __SYSREG(0xd8600003, u8) /* RTC minutes alarm reg */ +#define RTHCR __SYSREG(0xd8600004, u8) /* RTC hours count reg */ +#define RTHAR __SYSREG(0xd8600005, u8) /* RTC hours alarm reg */ +#define RTDWCR __SYSREG(0xd8600006, u8) /* RTC day of the week count reg */ +#define RTDMCR __SYSREG(0xd8600007, u8) /* RTC days count reg */ +#define RTMTCR __SYSREG(0xd8600008, u8) /* RTC months count reg */ +#define RTYCR __SYSREG(0xd8600009, u8) /* RTC years count reg */ + +#define RTCRA __SYSREG(0xd860000a, u8)/* RTC control reg A */ +#define RTCRA_RS 0x0f /* periodic timer interrupt cycle setting */ +#define RTCRA_RS_NONE 0x00 /* - off */ +#define RTCRA_RS_3_90625ms 0x01 /* - 3.90625ms (1/256s) */ +#define RTCRA_RS_7_8125ms 0x02 /* - 7.8125ms (1/128s) */ +#define RTCRA_RS_122_070us 0x03 /* - 122.070us (1/8192s) */ +#define RTCRA_RS_244_141us 0x04 /* - 244.141us (1/4096s) */ +#define RTCRA_RS_488_281us 0x05 /* - 488.281us (1/2048s) */ +#define RTCRA_RS_976_5625us 0x06 /* - 976.5625us (1/1024s) */ +#define RTCRA_RS_1_953125ms 0x07 /* - 1.953125ms (1/512s) */ +#define RTCRA_RS_3_90624ms 0x08 /* - 3.90624ms (1/256s) */ +#define RTCRA_RS_7_8125ms_b 0x09 /* - 7.8125ms (1/128s) */ +#define RTCRA_RS_15_625ms 0x0a /* - 15.625ms (1/64s) */ +#define RTCRA_RS_31_25ms 0x0b /* - 31.25ms (1/32s) */ +#define RTCRA_RS_62_5ms 0x0c /* - 62.5ms (1/16s) */ +#define RTCRA_RS_125ms 0x0d /* - 125ms (1/8s) */ +#define RTCRA_RS_250ms 0x0e /* - 250ms (1/4s) */ +#define RTCRA_RS_500ms 0x0f /* - 500ms (1/2s) */ +#define RTCRA_DVR 0x40 /* divider reset */ +#define RTCRA_UIP 0x80 /* clock update flag */ + +#define RTCRB __SYSREG(0xd860000b, u8) /* RTC control reg B */ +#define RTCRB_DSE 0x01 /* daylight savings time enable */ +#define RTCRB_TM 0x02 /* time format */ +#define RTCRB_TM_12HR 0x00 /* - 12 hour format */ +#define RTCRB_TM_24HR 0x02 /* - 24 hour format */ +#define RTCRB_DM 0x04 /* numeric value format */ +#define RTCRB_DM_BCD 0x00 /* - BCD */ +#define RTCRB_DM_BINARY 0x04 /* - binary */ +#define RTCRB_UIE 0x10 /* update interrupt disable */ +#define RTCRB_AIE 0x20 /* alarm interrupt disable */ +#define RTCRB_PIE 0x40 /* periodic interrupt disable */ +#define RTCRB_SET 0x80 /* clock update enable */ + +#define RTSRC __SYSREG(0xd860000c, u8) /* RTC status reg C */ +#define RTSRC_UF 0x10 /* update end interrupt flag */ +#define RTSRC_AF 0x20 /* alarm interrupt flag */ +#define RTSRC_PF 0x40 /* periodic interrupt flag */ +#define RTSRC_IRQF 0x80 /* interrupt flag */ + +#define RTIRQ 32 +#define RTICR GxICR(RTIRQ) + +/* + * MC146818 RTC compatibility defs for the MN10300 on-chip RTC + */ +#define RTC_PORT(x) 0xd8600000 +#define RTC_ALWAYS_BCD 1 /* RTC operates in binary mode */ + +#define CMOS_READ(addr) __SYSREG(0xd8600000 + (addr), u8) +#define CMOS_WRITE(val, addr) \ + do { __SYSREG(0xd8600000 + (addr), u8) = val; } while (0) + +#define RTC_IRQ RTIRQ + +#endif /* __KERNEL__ */ + +#endif /* _ASM_RTC_REGS_H */ diff --git a/arch/mn10300/include/asm/rtc.h b/arch/mn10300/include/asm/rtc.h new file mode 100644 index 00000000..6c14bb1d --- /dev/null +++ b/arch/mn10300/include/asm/rtc.h @@ -0,0 +1,30 @@ +/* MN10300 Real time clock definitions + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_RTC_H +#define _ASM_RTC_H + +#ifdef CONFIG_MN10300_RTC + +#include <linux/init.h> + +extern void __init calibrate_clock(void); + +#else /* !CONFIG_MN10300_RTC */ + +static inline void calibrate_clock(void) +{ +} + +#endif /* !CONFIG_MN10300_RTC */ + +#include <asm-generic/rtc.h> + +#endif /* _ASM_RTC_H */ diff --git a/arch/mn10300/include/asm/rwlock.h b/arch/mn10300/include/asm/rwlock.h new file mode 100644 index 00000000..6d594d4a --- /dev/null +++ b/arch/mn10300/include/asm/rwlock.h @@ -0,0 +1,125 @@ +/* + * Helpers used by both rw spinlocks and rw semaphores. + * + * Based in part on code from semaphore.h and + * spinlock.h Copyright 1996 Linus Torvalds. + * + * Copyright 1999 Red Hat, Inc. + * + * Written by Benjamin LaHaise. + * + * Modified by Matsushita Electric Industrial Co., Ltd. + * Modifications: + * 13-Nov-2006 MEI Temporarily delete lock functions for SMP support. + * + * 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. + */ +#ifndef _ASM_RWLOCK_H +#define _ASM_RWLOCK_H + +#define RW_LOCK_BIAS 0x01000000 + +#ifndef CONFIG_SMP + +typedef struct { unsigned long a[100]; } __dummy_lock_t; +#define __dummy_lock(lock) (*(__dummy_lock_t *)(lock)) + +#define RW_LOCK_BIAS_STR "0x01000000" + +#define __build_read_lock_ptr(rw, helper) \ + do { \ + asm volatile( \ + " mov (%0),d3 \n" \ + " sub 1,d3 \n" \ + " mov d3,(%0) \n" \ + " blt 1f \n" \ + " bra 2f \n" \ + "1: jmp 3f \n" \ + "2: \n" \ + " .section .text.lock,\"ax\" \n" \ + "3: call "helper"[],0 \n" \ + " jmp 2b \n" \ + " .previous" \ + : \ + : "d" (rw) \ + : "memory", "d3", "cc"); \ + } while (0) + +#define __build_read_lock_const(rw, helper) \ + do { \ + asm volatile( \ + " mov (%0),d3 \n" \ + " sub 1,d3 \n" \ + " mov d3,(%0) \n" \ + " blt 1f \n" \ + " bra 2f \n" \ + "1: jmp 3f \n" \ + "2: \n" \ + " .section .text.lock,\"ax\" \n" \ + "3: call "helper"[],0 \n" \ + " jmp 2b \n" \ + " .previous" \ + : \ + : "d" (rw) \ + : "memory", "d3", "cc"); \ + } while (0) + +#define __build_read_lock(rw, helper) \ + do { \ + if (__builtin_constant_p(rw)) \ + __build_read_lock_const(rw, helper); \ + else \ + __build_read_lock_ptr(rw, helper); \ + } while (0) + +#define __build_write_lock_ptr(rw, helper) \ + do { \ + asm volatile( \ + " mov (%0),d3 \n" \ + " sub 1,d3 \n" \ + " mov d3,(%0) \n" \ + " blt 1f \n" \ + " bra 2f \n" \ + "1: jmp 3f \n" \ + "2: \n" \ + " .section .text.lock,\"ax\" \n" \ + "3: call "helper"[],0 \n" \ + " jmp 2b \n" \ + " .previous" \ + : \ + : "d" (rw) \ + : "memory", "d3", "cc"); \ + } while (0) + +#define __build_write_lock_const(rw, helper) \ + do { \ + asm volatile( \ + " mov (%0),d3 \n" \ + " sub 1,d3 \n" \ + " mov d3,(%0) \n" \ + " blt 1f \n" \ + " bra 2f \n" \ + "1: jmp 3f \n" \ + "2: \n" \ + " .section .text.lock,\"ax\" \n" \ + "3: call "helper"[],0 \n" \ + " jmp 2b \n" \ + " .previous" \ + : \ + : "d" (rw) \ + : "memory", "d3", "cc"); \ + } while (0) + +#define __build_write_lock(rw, helper) \ + do { \ + if (__builtin_constant_p(rw)) \ + __build_write_lock_const(rw, helper); \ + else \ + __build_write_lock_ptr(rw, helper); \ + } while (0) + +#endif /* CONFIG_SMP */ +#endif /* _ASM_RWLOCK_H */ diff --git a/arch/mn10300/include/asm/scatterlist.h b/arch/mn10300/include/asm/scatterlist.h new file mode 100644 index 00000000..7baa4006 --- /dev/null +++ b/arch/mn10300/include/asm/scatterlist.h @@ -0,0 +1,16 @@ +/* MN10300 Scatterlist definitions + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_SCATTERLIST_H +#define _ASM_SCATTERLIST_H + +#include <asm-generic/scatterlist.h> + +#endif /* _ASM_SCATTERLIST_H */ diff --git a/arch/mn10300/include/asm/sections.h b/arch/mn10300/include/asm/sections.h new file mode 100644 index 00000000..2b8c5160 --- /dev/null +++ b/arch/mn10300/include/asm/sections.h @@ -0,0 +1 @@ +#include <asm-generic/sections.h> diff --git a/arch/mn10300/include/asm/sembuf.h b/arch/mn10300/include/asm/sembuf.h new file mode 100644 index 00000000..301f3f9d --- /dev/null +++ b/arch/mn10300/include/asm/sembuf.h @@ -0,0 +1,25 @@ +#ifndef _ASM_SEMBUF_H +#define _ASM_SEMBUF_H + +/* + * The semid64_ds structure for MN10300 architecture. + * Note extra padding because this structure is passed back and forth + * between kernel and user space. + * + * Pad space is left for: + * - 64-bit time_t to solve y2038 problem + * - 2 miscellaneous 32-bit values + */ + +struct semid64_ds { + struct ipc64_perm sem_perm; /* permissions .. see ipc.h */ + __kernel_time_t sem_otime; /* last semop time */ + unsigned long __unused1; + __kernel_time_t sem_ctime; /* last change time */ + unsigned long __unused2; + unsigned long sem_nsems; /* no. of semaphores in array */ + unsigned long __unused3; + unsigned long __unused4; +}; + +#endif /* _ASM_SEMBUF_H */ diff --git a/arch/mn10300/include/asm/serial-regs.h b/arch/mn10300/include/asm/serial-regs.h new file mode 100644 index 00000000..8320cda3 --- /dev/null +++ b/arch/mn10300/include/asm/serial-regs.h @@ -0,0 +1,191 @@ +/* MN10300 on-board serial port module registers + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _ASM_SERIAL_REGS_H +#define _ASM_SERIAL_REGS_H + +#include <asm/cpu-regs.h> +#include <asm/intctl-regs.h> + +#ifdef __KERNEL__ + +/* serial port 0 */ +#define SC0CTR __SYSREG(0xd4002000, u16) /* control reg */ +#define SC01CTR_CK 0x0007 /* clock source select */ +#define SC01CTR_CK_IOCLK_8 0x0001 /* - 1/8 IOCLK */ +#define SC01CTR_CK_IOCLK_32 0x0002 /* - 1/32 IOCLK */ +#define SC01CTR_CK_EXTERN_8 0x0006 /* - 1/8 external closk */ +#define SC01CTR_CK_EXTERN 0x0007 /* - external closk */ +#if defined(CONFIG_AM33_2) || defined(CONFIG_AM33_3) +#define SC0CTR_CK_TM8UFLOW_8 0x0000 /* - 1/8 timer 8 underflow (serial port 0 only) */ +#define SC0CTR_CK_TM2UFLOW_2 0x0003 /* - 1/2 timer 2 underflow (serial port 0 only) */ +#define SC0CTR_CK_TM0UFLOW_8 0x0004 /* - 1/8 timer 0 underflow (serial port 0 only) */ +#define SC0CTR_CK_TM2UFLOW_8 0x0005 /* - 1/8 timer 2 underflow (serial port 0 only) */ +#define SC1CTR_CK_TM9UFLOW_8 0x0000 /* - 1/8 timer 9 underflow (serial port 1 only) */ +#define SC1CTR_CK_TM3UFLOW_2 0x0003 /* - 1/2 timer 3 underflow (serial port 1 only) */ +#define SC1CTR_CK_TM1UFLOW_8 0x0004 /* - 1/8 timer 1 underflow (serial port 1 only) */ +#define SC1CTR_CK_TM3UFLOW_8 0x0005 /* - 1/8 timer 3 underflow (serial port 1 only) */ +#else /* CONFIG_AM33_2 || CONFIG_AM33_3 */ +#define SC0CTR_CK_TM8UFLOW_8 0x0000 /* - 1/8 timer 8 underflow (serial port 0 only) */ +#define SC0CTR_CK_TM0UFLOW_8 0x0004 /* - 1/8 timer 0 underflow (serial port 0 only) */ +#define SC0CTR_CK_TM2UFLOW_8 0x0005 /* - 1/8 timer 2 underflow (serial port 0 only) */ +#define SC1CTR_CK_TM12UFLOW_8 0x0000 /* - 1/8 timer 12 underflow (serial port 1 only) */ +#endif /* CONFIG_AM33_2 || CONFIG_AM33_3 */ +#define SC01CTR_STB 0x0008 /* stop bit select */ +#define SC01CTR_STB_1BIT 0x0000 /* - 1 stop bit */ +#define SC01CTR_STB_2BIT 0x0008 /* - 2 stop bits */ +#define SC01CTR_PB 0x0070 /* parity bit select */ +#define SC01CTR_PB_NONE 0x0000 /* - no parity */ +#define SC01CTR_PB_FIXED0 0x0040 /* - fixed at 0 */ +#define SC01CTR_PB_FIXED1 0x0050 /* - fixed at 1 */ +#define SC01CTR_PB_EVEN 0x0060 /* - even parity */ +#define SC01CTR_PB_ODD 0x0070 /* - odd parity */ +#define SC01CTR_CLN 0x0080 /* character length */ +#define SC01CTR_CLN_7BIT 0x0000 /* - 7 bit chars */ +#define SC01CTR_CLN_8BIT 0x0080 /* - 8 bit chars */ +#define SC01CTR_TOE 0x0100 /* T input output enable */ +#define SC01CTR_OD 0x0200 /* bit order select */ +#define SC01CTR_OD_LSBFIRST 0x0000 /* - LSB first */ +#define SC01CTR_OD_MSBFIRST 0x0200 /* - MSB first */ +#define SC01CTR_MD 0x0c00 /* mode select */ +#define SC01CTR_MD_STST_SYNC 0x0000 /* - start-stop synchronous */ +#define SC01CTR_MD_CLOCK_SYNC1 0x0400 /* - clock synchronous 1 */ +#define SC01CTR_MD_I2C 0x0800 /* - I2C mode */ +#define SC01CTR_MD_CLOCK_SYNC2 0x0c00 /* - clock synchronous 2 */ +#define SC01CTR_IIC 0x1000 /* I2C mode select */ +#define SC01CTR_BKE 0x2000 /* break transmit enable */ +#define SC01CTR_RXE 0x4000 /* receive enable */ +#define SC01CTR_TXE 0x8000 /* transmit enable */ + +#define SC0ICR __SYSREG(0xd4002004, u8) /* interrupt control reg */ +#define SC01ICR_DMD 0x80 /* output data mode */ +#define SC01ICR_TD 0x20 /* transmit DMA trigger cause */ +#define SC01ICR_TI 0x10 /* transmit interrupt cause */ +#define SC01ICR_RES 0x04 /* receive error select */ +#define SC01ICR_RI 0x01 /* receive interrupt cause */ + +#define SC0TXB __SYSREG(0xd4002008, u8) /* transmit buffer reg */ +#define SC0RXB __SYSREG(0xd4002009, u8) /* receive buffer reg */ + +#define SC0STR __SYSREG(0xd400200c, u16) /* status reg */ +#define SC01STR_OEF 0x0001 /* overrun error found */ +#define SC01STR_PEF 0x0002 /* parity error found */ +#define SC01STR_FEF 0x0004 /* framing error found */ +#define SC01STR_RBF 0x0010 /* receive buffer status */ +#define SC01STR_TBF 0x0020 /* transmit buffer status */ +#define SC01STR_RXF 0x0040 /* receive status */ +#define SC01STR_TXF 0x0080 /* transmit status */ +#define SC01STR_STF 0x0100 /* I2C start sequence found */ +#define SC01STR_SPF 0x0200 /* I2C stop sequence found */ + +#define SC0RXIRQ 20 /* timer 0 Receive IRQ */ +#define SC0TXIRQ 21 /* timer 0 Transmit IRQ */ + +#define SC0RXICR GxICR(SC0RXIRQ) /* serial 0 receive intr ctrl reg */ +#define SC0TXICR GxICR(SC0TXIRQ) /* serial 0 transmit intr ctrl reg */ + +/* serial port 1 */ +#define SC1CTR __SYSREG(0xd4002010, u16) /* serial port 1 control */ +#define SC1ICR __SYSREG(0xd4002014, u8) /* interrupt control reg */ +#define SC1TXB __SYSREG(0xd4002018, u8) /* transmit buffer reg */ +#define SC1RXB __SYSREG(0xd4002019, u8) /* receive buffer reg */ +#define SC1STR __SYSREG(0xd400201c, u16) /* status reg */ + +#define SC1RXIRQ 22 /* timer 1 Receive IRQ */ +#define SC1TXIRQ 23 /* timer 1 Transmit IRQ */ + +#define SC1RXICR GxICR(SC1RXIRQ) /* serial 1 receive intr ctrl reg */ +#define SC1TXICR GxICR(SC1TXIRQ) /* serial 1 transmit intr ctrl reg */ + +/* serial port 2 */ +#define SC2CTR __SYSREG(0xd4002020, u16) /* control reg */ +#ifdef CONFIG_AM33_2 +#define SC2CTR_CK 0x0003 /* clock source select */ +#define SC2CTR_CK_TM10UFLOW 0x0000 /* - timer 10 underflow */ +#define SC2CTR_CK_TM2UFLOW 0x0001 /* - timer 2 underflow */ +#define SC2CTR_CK_EXTERN 0x0002 /* - external closk */ +#define SC2CTR_CK_TM3UFLOW 0x0003 /* - timer 3 underflow */ +#else /* CONFIG_AM33_2 */ +#define SC2CTR_CK 0x0007 /* clock source select */ +#define SC2CTR_CK_TM9UFLOW_8 0x0000 /* - 1/8 timer 9 underflow */ +#define SC2CTR_CK_IOCLK_8 0x0001 /* - 1/8 IOCLK */ +#define SC2CTR_CK_IOCLK_32 0x0002 /* - 1/32 IOCLK */ +#define SC2CTR_CK_TM3UFLOW_2 0x0003 /* - 1/2 timer 3 underflow */ +#define SC2CTR_CK_TM1UFLOW_8 0x0004 /* - 1/8 timer 1 underflow */ +#define SC2CTR_CK_TM3UFLOW_8 0x0005 /* - 1/8 timer 3 underflow */ +#define SC2CTR_CK_EXTERN_8 0x0006 /* - 1/8 external closk */ +#define SC2CTR_CK_EXTERN 0x0007 /* - external closk */ +#endif /* CONFIG_AM33_2 */ +#define SC2CTR_STB 0x0008 /* stop bit select */ +#define SC2CTR_STB_1BIT 0x0000 /* - 1 stop bit */ +#define SC2CTR_STB_2BIT 0x0008 /* - 2 stop bits */ +#define SC2CTR_PB 0x0070 /* parity bit select */ +#define SC2CTR_PB_NONE 0x0000 /* - no parity */ +#define SC2CTR_PB_FIXED0 0x0040 /* - fixed at 0 */ +#define SC2CTR_PB_FIXED1 0x0050 /* - fixed at 1 */ +#define SC2CTR_PB_EVEN 0x0060 /* - even parity */ +#define SC2CTR_PB_ODD 0x0070 /* - odd parity */ +#define SC2CTR_CLN 0x0080 /* character length */ +#define SC2CTR_CLN_7BIT 0x0000 /* - 7 bit chars */ +#define SC2CTR_CLN_8BIT 0x0080 /* - 8 bit chars */ +#define SC2CTR_TWE 0x0100 /* transmit wait enable (enable XCTS control) */ +#define SC2CTR_OD 0x0200 /* bit order select */ +#define SC2CTR_OD_LSBFIRST 0x0000 /* - LSB first */ +#define SC2CTR_OD_MSBFIRST 0x0200 /* - MSB first */ +#define SC2CTR_TWS 0x1000 /* transmit wait select */ +#define SC2CTR_TWS_XCTS_HIGH 0x0000 /* - interrupt TX when XCTS high */ +#define SC2CTR_TWS_XCTS_LOW 0x1000 /* - interrupt TX when XCTS low */ +#define SC2CTR_BKE 0x2000 /* break transmit enable */ +#define SC2CTR_RXE 0x4000 /* receive enable */ +#define SC2CTR_TXE 0x8000 /* transmit enable */ + +#define SC2ICR __SYSREG(0xd4002024, u8) /* interrupt control reg */ +#define SC2ICR_TD 0x20 /* transmit DMA trigger cause */ +#define SC2ICR_TI 0x10 /* transmit interrupt cause */ +#define SC2ICR_RES 0x04 /* receive error select */ +#define SC2ICR_RI 0x01 /* receive interrupt cause */ + +#define SC2TXB __SYSREG(0xd4002028, u8) /* transmit buffer reg */ +#define SC2RXB __SYSREG(0xd4002029, u8) /* receive buffer reg */ + +#ifdef CONFIG_AM33_2 +#define SC2STR __SYSREG(0xd400202c, u8) /* status reg */ +#else /* CONFIG_AM33_2 */ +#define SC2STR __SYSREG(0xd400202c, u16) /* status reg */ +#endif /* CONFIG_AM33_2 */ +#define SC2STR_OEF 0x0001 /* overrun error found */ +#define SC2STR_PEF 0x0002 /* parity error found */ +#define SC2STR_FEF 0x0004 /* framing error found */ +#define SC2STR_CTS 0x0008 /* XCTS input pin status (0 means high) */ +#define SC2STR_RBF 0x0010 /* receive buffer status */ +#define SC2STR_TBF 0x0020 /* transmit buffer status */ +#define SC2STR_RXF 0x0040 /* receive status */ +#define SC2STR_TXF 0x0080 /* transmit status */ + +#ifdef CONFIG_AM33_2 +#define SC2TIM __SYSREG(0xd400202d, u8) /* status reg */ +#endif + +#ifdef CONFIG_AM33_2 +#define SC2RXIRQ 24 /* serial 2 Receive IRQ */ +#define SC2TXIRQ 25 /* serial 2 Transmit IRQ */ +#else /* CONFIG_AM33_2 */ +#define SC2RXIRQ 68 /* serial 2 Receive IRQ */ +#define SC2TXIRQ 69 /* serial 2 Transmit IRQ */ +#endif /* CONFIG_AM33_2 */ + +#define SC2RXICR GxICR(SC2RXIRQ) /* serial 2 receive intr ctrl reg */ +#define SC2TXICR GxICR(SC2TXIRQ) /* serial 2 transmit intr ctrl reg */ + + +#endif /* __KERNEL__ */ + +#endif /* _ASM_SERIAL_REGS_H */ diff --git a/arch/mn10300/include/asm/serial.h b/arch/mn10300/include/asm/serial.h new file mode 100644 index 00000000..23a79929 --- /dev/null +++ b/arch/mn10300/include/asm/serial.h @@ -0,0 +1,36 @@ +/* Standard UART definitions + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _ASM_SERIAL_H +#define _ASM_SERIAL_H + +/* Standard COM flags (except for COM4, because of the 8514 problem) */ +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ) +#define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST) +#define STD_COM4_FLAGS ASYNC_BOOT_AUTOCONF +#endif + +#ifdef CONFIG_SERIAL_MANY_PORTS +#define FOURPORT_FLAGS ASYNC_FOURPORT +#define ACCENT_FLAGS 0 +#define BOCA_FLAGS 0 +#define HUB6_FLAGS 0 +#define RS_TABLE_SIZE 64 +#else +#define RS_TABLE_SIZE +#endif + +#include <unit/serial.h> + +#endif /* _ASM_SERIAL_H */ diff --git a/arch/mn10300/include/asm/setup.h b/arch/mn10300/include/asm/setup.h new file mode 100644 index 00000000..c229d1e3 --- /dev/null +++ b/arch/mn10300/include/asm/setup.h @@ -0,0 +1,18 @@ +/* MN10300 Setup declarations + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_SETUP_H +#define _ASM_SETUP_H + +#ifdef __KERNEL__ +extern void __init unit_setup(void); +extern void __init unit_init_IRQ(void); +#endif +#endif /* _ASM_SETUP_H */ diff --git a/arch/mn10300/include/asm/shmbuf.h b/arch/mn10300/include/asm/shmbuf.h new file mode 100644 index 00000000..8f300cc3 --- /dev/null +++ b/arch/mn10300/include/asm/shmbuf.h @@ -0,0 +1,42 @@ +#ifndef _ASM_SHMBUF_H +#define _ASM_SHMBUF_H + +/* + * The shmid64_ds structure for MN10300 architecture. + * Note extra padding because this structure is passed back and forth + * between kernel and user space. + * + * Pad space is left for: + * - 64-bit time_t to solve y2038 problem + * - 2 miscellaneous 32-bit values + */ + +struct shmid64_ds { + struct ipc64_perm shm_perm; /* operation perms */ + size_t shm_segsz; /* size of segment (bytes) */ + __kernel_time_t shm_atime; /* last attach time */ + unsigned long __unused1; + __kernel_time_t shm_dtime; /* last detach time */ + unsigned long __unused2; + __kernel_time_t shm_ctime; /* last change time */ + unsigned long __unused3; + __kernel_pid_t shm_cpid; /* pid of creator */ + __kernel_pid_t shm_lpid; /* pid of last operator */ + unsigned long shm_nattch; /* no. of current attaches */ + unsigned long __unused4; + unsigned long __unused5; +}; + +struct shminfo64 { + unsigned long shmmax; + unsigned long shmmin; + unsigned long shmmni; + unsigned long shmseg; + unsigned long shmall; + unsigned long __unused1; + unsigned long __unused2; + unsigned long __unused3; + unsigned long __unused4; +}; + +#endif /* _ASM_SHMBUF_H */ diff --git a/arch/mn10300/include/asm/shmparam.h b/arch/mn10300/include/asm/shmparam.h new file mode 100644 index 00000000..ab666ed1 --- /dev/null +++ b/arch/mn10300/include/asm/shmparam.h @@ -0,0 +1,6 @@ +#ifndef _ASM_SHMPARAM_H +#define _ASM_SHMPARAM_H + +#define SHMLBA PAGE_SIZE /* attach addr a multiple of this */ + +#endif /* _ASM_SHMPARAM_H */ diff --git a/arch/mn10300/include/asm/sigcontext.h b/arch/mn10300/include/asm/sigcontext.h new file mode 100644 index 00000000..4de3afff --- /dev/null +++ b/arch/mn10300/include/asm/sigcontext.h @@ -0,0 +1,52 @@ +/* MN10300 Userspace signal context + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_SIGCONTEXT_H +#define _ASM_SIGCONTEXT_H + +struct fpucontext { + /* Regular FPU environment */ + unsigned long fs[32]; /* fpu registers */ + unsigned long fpcr; /* fpu control register */ +}; + +struct sigcontext { + unsigned long d0; + unsigned long d1; + unsigned long d2; + unsigned long d3; + unsigned long a0; + unsigned long a1; + unsigned long a2; + unsigned long a3; + unsigned long e0; + unsigned long e1; + unsigned long e2; + unsigned long e3; + unsigned long e4; + unsigned long e5; + unsigned long e6; + unsigned long e7; + unsigned long lar; + unsigned long lir; + unsigned long mdr; + unsigned long mcvf; + unsigned long mcrl; + unsigned long mcrh; + unsigned long mdrq; + unsigned long sp; + unsigned long epsw; + unsigned long pc; + struct fpucontext *fpucontext; + unsigned long oldmask; +}; + + +#endif /* _ASM_SIGCONTEXT_H */ diff --git a/arch/mn10300/include/asm/siginfo.h b/arch/mn10300/include/asm/siginfo.h new file mode 100644 index 00000000..0815d29d --- /dev/null +++ b/arch/mn10300/include/asm/siginfo.h @@ -0,0 +1 @@ +#include <asm-generic/siginfo.h> diff --git a/arch/mn10300/include/asm/signal.h b/arch/mn10300/include/asm/signal.h new file mode 100644 index 00000000..1865d72a --- /dev/null +++ b/arch/mn10300/include/asm/signal.h @@ -0,0 +1,171 @@ +/* MN10300 Signal definitions + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_SIGNAL_H +#define _ASM_SIGNAL_H + +#include <linux/types.h> + +/* Avoid too many header ordering problems. */ +struct siginfo; + +#ifdef __KERNEL__ +/* Most things should be clean enough to redefine this at will, if care + is taken to make libc match. */ + +#define _NSIG 64 +#define _NSIG_BPW 32 +#define _NSIG_WORDS (_NSIG / _NSIG_BPW) + +typedef unsigned long old_sigset_t; /* at least 32 bits */ + +typedef struct { + unsigned long sig[_NSIG_WORDS]; +} sigset_t; + +#else +/* Here we must cater to libcs that poke about in kernel headers. */ + +#define NSIG 32 +typedef unsigned long sigset_t; + +#endif /* __KERNEL__ */ + +#define SIGHUP 1 +#define SIGINT 2 +#define SIGQUIT 3 +#define SIGILL 4 +#define SIGTRAP 5 +#define SIGABRT 6 +#define SIGIOT 6 +#define SIGBUS 7 +#define SIGFPE 8 +#define SIGKILL 9 +#define SIGUSR1 10 +#define SIGSEGV 11 +#define SIGUSR2 12 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGTERM 15 +#define SIGSTKFLT 16 +#define SIGCHLD 17 +#define SIGCONT 18 +#define SIGSTOP 19 +#define SIGTSTP 20 +#define SIGTTIN 21 +#define SIGTTOU 22 +#define SIGURG 23 +#define SIGXCPU 24 +#define SIGXFSZ 25 +#define SIGVTALRM 26 +#define SIGPROF 27 +#define SIGWINCH 28 +#define SIGIO 29 +#define SIGPOLL SIGIO +/* +#define SIGLOST 29 +*/ +#define SIGPWR 30 +#define SIGSYS 31 +#define SIGUNUSED 31 + +/* These should not be considered constants from userland. */ +#define SIGRTMIN 32 +#define SIGRTMAX _NSIG + +/* + * SA_FLAGS values: + * + * SA_ONSTACK indicates that a registered stack_t will be used. + * SA_RESTART flag to get restarting signals (which were the default long ago) + * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. + * SA_RESETHAND clears the handler when the signal is delivered. + * SA_NOCLDWAIT flag on SIGCHLD to inhibit zombies. + * SA_NODEFER prevents the current signal from being masked in the handler. + * + * SA_ONESHOT and SA_NOMASK are the historical Linux names for the Single + * Unix names RESETHAND and NODEFER respectively. + */ +#define SA_NOCLDSTOP 0x00000001U +#define SA_NOCLDWAIT 0x00000002U +#define SA_SIGINFO 0x00000004U +#define SA_ONSTACK 0x08000000U +#define SA_RESTART 0x10000000U +#define SA_NODEFER 0x40000000U +#define SA_RESETHAND 0x80000000U + +#define SA_NOMASK SA_NODEFER +#define SA_ONESHOT SA_RESETHAND + +#define SA_RESTORER 0x04000000 + +/* + * sigaltstack controls + */ +#define SS_ONSTACK 1 +#define SS_DISABLE 2 + +#define MINSIGSTKSZ 2048 +#define SIGSTKSZ 8192 + +#include <asm-generic/signal-defs.h> + +#ifdef __KERNEL__ +struct old_sigaction { + __sighandler_t sa_handler; + old_sigset_t sa_mask; + unsigned long sa_flags; + __sigrestore_t sa_restorer; +}; + +struct sigaction { + __sighandler_t sa_handler; + unsigned long sa_flags; + __sigrestore_t sa_restorer; + sigset_t sa_mask; /* mask last for extensibility */ +}; + +struct k_sigaction { + struct sigaction sa; +}; +#else +/* Here we must cater to libcs that poke about in kernel headers. */ + +struct sigaction { + union { + __sighandler_t _sa_handler; + void (*_sa_sigaction)(int, struct siginfo *, void *); + } _u; + sigset_t sa_mask; + unsigned long sa_flags; + void (*sa_restorer)(void); +}; + +#define sa_handler _u._sa_handler +#define sa_sigaction _u._sa_sigaction + +#endif /* __KERNEL__ */ + +typedef struct sigaltstack { + void __user *ss_sp; + int ss_flags; + size_t ss_size; +} stack_t; + +#ifdef __KERNEL__ +#include <asm/sigcontext.h> + + +struct pt_regs; +#define ptrace_signal_deliver(regs, cookie) do { } while (0) + +#endif /* __KERNEL__ */ + +#endif /* _ASM_SIGNAL_H */ diff --git a/arch/mn10300/include/asm/smp.h b/arch/mn10300/include/asm/smp.h new file mode 100644 index 00000000..6745dbe6 --- /dev/null +++ b/arch/mn10300/include/asm/smp.h @@ -0,0 +1,107 @@ +/* MN10300 SMP support + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * Modified by Matsushita Electric Industrial Co., Ltd. + * Modifications: + * 13-Nov-2006 MEI Define IPI-IRQ number and add inline/macro function + * for SMP support. + * 22-Jan-2007 MEI Add the define related to SMP_BOOT_IRQ. + * 23-Feb-2007 MEI Add the define related to SMP icahce invalidate. + * 23-Jun-2008 MEI Delete INTC_IPI. + * 22-Jul-2008 MEI Add smp_nmi_call_function and related defines. + * 04-Aug-2008 MEI Delete USE_DOIRQ_CACHE_IPI. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_SMP_H +#define _ASM_SMP_H + +#ifndef __ASSEMBLY__ +#include <linux/threads.h> +#include <linux/cpumask.h> +#endif + +#ifdef CONFIG_SMP +#include <proc/smp-regs.h> + +#define RESCHEDULE_IPI 63 +#define CALL_FUNC_SINGLE_IPI 192 +#define LOCAL_TIMER_IPI 193 +#define FLUSH_CACHE_IPI 194 +#define CALL_FUNCTION_NMI_IPI 195 +#define DEBUGGER_NMI_IPI 196 + +#define SMP_BOOT_IRQ 195 + +#define RESCHEDULE_GxICR_LV GxICR_LEVEL_6 +#define CALL_FUNCTION_GxICR_LV GxICR_LEVEL_4 +#define LOCAL_TIMER_GxICR_LV GxICR_LEVEL_4 +#define FLUSH_CACHE_GxICR_LV GxICR_LEVEL_0 +#define SMP_BOOT_GxICR_LV GxICR_LEVEL_0 +#define DEBUGGER_GxICR_LV CONFIG_DEBUGGER_IRQ_LEVEL + +#define TIME_OUT_COUNT_BOOT_IPI 100 +#define DELAY_TIME_BOOT_IPI 75000 + + +#ifndef __ASSEMBLY__ + +/** + * raw_smp_processor_id - Determine the raw CPU ID of the CPU running it + * + * What we really want to do is to use the CPUID hardware CPU register to get + * this information, but accesses to that aren't cached, and run at system bus + * speed, not CPU speed. A copy of this value is, however, stored in the + * thread_info struct, and that can be cached. + * + * An alternate way of dealing with this could be to use the EPSW.S bits to + * cache this information for systems with up to four CPUs. + */ +#define arch_smp_processor_id() (CPUID) +#if 0 +#define raw_smp_processor_id() (arch_smp_processor_id()) +#else +#define raw_smp_processor_id() (current_thread_info()->cpu) +#endif + +static inline int cpu_logical_map(int cpu) +{ + return cpu; +} + +static inline int cpu_number_map(int cpu) +{ + return cpu; +} + + +extern cpumask_t cpu_boot_map; + +extern void smp_init_cpus(void); +extern void smp_cache_interrupt(void); +extern void send_IPI_allbutself(int irq); +extern int smp_nmi_call_function(smp_call_func_t func, void *info, int wait); + +extern void arch_send_call_function_single_ipi(int cpu); +extern void arch_send_call_function_ipi_mask(const struct cpumask *mask); + +#ifdef CONFIG_HOTPLUG_CPU +extern int __cpu_disable(void); +extern void __cpu_die(unsigned int cpu); +#endif /* CONFIG_HOTPLUG_CPU */ + +#endif /* __ASSEMBLY__ */ +#else /* CONFIG_SMP */ +#ifndef __ASSEMBLY__ + +static inline void smp_init_cpus(void) {} + +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_SMP */ + +#endif /* _ASM_SMP_H */ diff --git a/arch/mn10300/include/asm/smsc911x.h b/arch/mn10300/include/asm/smsc911x.h new file mode 100644 index 00000000..2fcd1080 --- /dev/null +++ b/arch/mn10300/include/asm/smsc911x.h @@ -0,0 +1 @@ +#include <unit/smsc911x.h> diff --git a/arch/mn10300/include/asm/socket.h b/arch/mn10300/include/asm/socket.h new file mode 100644 index 00000000..4e60c428 --- /dev/null +++ b/arch/mn10300/include/asm/socket.h @@ -0,0 +1,65 @@ +#ifndef _ASM_SOCKET_H +#define _ASM_SOCKET_H + +#include <asm/sockios.h> + +/* For setsockopt(2) */ +#define SOL_SOCKET 1 + +#define SO_DEBUG 1 +#define SO_REUSEADDR 2 +#define SO_TYPE 3 +#define SO_ERROR 4 +#define SO_DONTROUTE 5 +#define SO_BROADCAST 6 +#define SO_SNDBUF 7 +#define SO_RCVBUF 8 +#define SO_SNDBUFFORCE 32 +#define SO_RCVBUFFORCE 33 +#define SO_KEEPALIVE 9 +#define SO_OOBINLINE 10 +#define SO_NO_CHECK 11 +#define SO_PRIORITY 12 +#define SO_LINGER 13 +#define SO_BSDCOMPAT 14 +/* To add :#define SO_REUSEPORT 15 */ +#define SO_PASSCRED 16 +#define SO_PEERCRED 17 +#define SO_RCVLOWAT 18 +#define SO_SNDLOWAT 19 +#define SO_RCVTIMEO 20 +#define SO_SNDTIMEO 21 + +/* Security levels - as per NRL IPv6 - don't actually do anything */ +#define SO_SECURITY_AUTHENTICATION 22 +#define SO_SECURITY_ENCRYPTION_TRANSPORT 23 +#define SO_SECURITY_ENCRYPTION_NETWORK 24 + +#define SO_BINDTODEVICE 25 + +/* Socket filtering */ +#define SO_ATTACH_FILTER 26 +#define SO_DETACH_FILTER 27 + +#define SO_PEERNAME 28 +#define SO_TIMESTAMP 29 +#define SCM_TIMESTAMP SO_TIMESTAMP + +#define SO_ACCEPTCONN 30 + +#define SO_PEERSEC 31 +#define SO_PASSSEC 34 +#define SO_TIMESTAMPNS 35 +#define SCM_TIMESTAMPNS SO_TIMESTAMPNS + +#define SO_MARK 36 + +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + +#define SO_PROTOCOL 38 +#define SO_DOMAIN 39 + +#define SO_RXQ_OVFL 40 + +#endif /* _ASM_SOCKET_H */ diff --git a/arch/mn10300/include/asm/sockios.h b/arch/mn10300/include/asm/sockios.h new file mode 100644 index 00000000..b03043a1 --- /dev/null +++ b/arch/mn10300/include/asm/sockios.h @@ -0,0 +1,13 @@ +#ifndef _ASM_SOCKIOS_H +#define _ASM_SOCKIOS_H + +/* Socket-level I/O control calls. */ +#define FIOSETOWN 0x8901 +#define SIOCSPGRP 0x8902 +#define FIOGETOWN 0x8903 +#define SIOCGPGRP 0x8904 +#define SIOCATMARK 0x8905 +#define SIOCGSTAMP 0x8906 /* Get stamp */ +#define SIOCGSTAMPNS 0x8907 /* Get stamp (timespec) */ + +#endif /* _ASM_SOCKIOS_H */ diff --git a/arch/mn10300/include/asm/spinlock.h b/arch/mn10300/include/asm/spinlock.h new file mode 100644 index 00000000..93429154 --- /dev/null +++ b/arch/mn10300/include/asm/spinlock.h @@ -0,0 +1,193 @@ +/* MN10300 spinlock support + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_SPINLOCK_H +#define _ASM_SPINLOCK_H + +#include <asm/atomic.h> +#include <asm/rwlock.h> +#include <asm/page.h> + +/* + * Simple spin lock operations. There are two variants, one clears IRQ's + * on the local processor, one does not. + * + * We make no fairness assumptions. They have a cost. + */ + +#define arch_spin_is_locked(x) (*(volatile signed char *)(&(x)->slock) != 0) +#define arch_spin_unlock_wait(x) do { barrier(); } while (arch_spin_is_locked(x)) + +static inline void arch_spin_unlock(arch_spinlock_t *lock) +{ + asm volatile( + " bclr 1,(0,%0) \n" + : + : "a"(&lock->slock) + : "memory", "cc"); +} + +static inline int arch_spin_trylock(arch_spinlock_t *lock) +{ + int ret; + + asm volatile( + " mov 1,%0 \n" + " bset %0,(%1) \n" + " bne 1f \n" + " clr %0 \n" + "1: xor 1,%0 \n" + : "=d"(ret) + : "a"(&lock->slock) + : "memory", "cc"); + + return ret; +} + +static inline void arch_spin_lock(arch_spinlock_t *lock) +{ + asm volatile( + "1: bset 1,(0,%0) \n" + " bne 1b \n" + : + : "a"(&lock->slock) + : "memory", "cc"); +} + +static inline void arch_spin_lock_flags(arch_spinlock_t *lock, + unsigned long flags) +{ + int temp; + + asm volatile( + "1: bset 1,(0,%2) \n" + " beq 3f \n" + " mov %1,epsw \n" + "2: mov (0,%2),%0 \n" + " or %0,%0 \n" + " bne 2b \n" + " mov %3,%0 \n" + " mov %0,epsw \n" + " nop \n" + " nop \n" + " bra 1b\n" + "3: \n" + : "=&d" (temp) + : "d" (flags), "a"(&lock->slock), "i"(EPSW_IE | MN10300_CLI_LEVEL) + : "memory", "cc"); +} + +#ifdef __KERNEL__ + +/* + * Read-write spinlocks, allowing multiple readers + * but only one writer. + * + * NOTE! it is quite common to have readers in interrupts + * but no interrupt writers. For those circumstances we + * can "mix" irq-safe locks - any writer needs to get a + * irq-safe write-lock, but readers can get non-irqsafe + * read-locks. + */ + +/** + * read_can_lock - would read_trylock() succeed? + * @lock: the rwlock in question. + */ +#define arch_read_can_lock(x) ((int)(x)->lock > 0) + +/** + * write_can_lock - would write_trylock() succeed? + * @lock: the rwlock in question. + */ +#define arch_write_can_lock(x) ((x)->lock == RW_LOCK_BIAS) + +/* + * On mn10300, we implement read-write locks as a 32-bit counter + * with the high bit (sign) being the "contended" bit. + */ +static inline void arch_read_lock(arch_rwlock_t *rw) +{ +#if 0 //def CONFIG_MN10300_HAS_ATOMIC_OPS_UNIT + __build_read_lock(rw, "__read_lock_failed"); +#else + { + atomic_t *count = (atomic_t *)rw; + while (atomic_dec_return(count) < 0) + atomic_inc(count); + } +#endif +} + +static inline void arch_write_lock(arch_rwlock_t *rw) +{ +#if 0 //def CONFIG_MN10300_HAS_ATOMIC_OPS_UNIT + __build_write_lock(rw, "__write_lock_failed"); +#else + { + atomic_t *count = (atomic_t *)rw; + while (!atomic_sub_and_test(RW_LOCK_BIAS, count)) + atomic_add(RW_LOCK_BIAS, count); + } +#endif +} + +static inline void arch_read_unlock(arch_rwlock_t *rw) +{ +#if 0 //def CONFIG_MN10300_HAS_ATOMIC_OPS_UNIT + __build_read_unlock(rw); +#else + { + atomic_t *count = (atomic_t *)rw; + atomic_inc(count); + } +#endif +} + +static inline void arch_write_unlock(arch_rwlock_t *rw) +{ +#if 0 //def CONFIG_MN10300_HAS_ATOMIC_OPS_UNIT + __build_write_unlock(rw); +#else + { + atomic_t *count = (atomic_t *)rw; + atomic_add(RW_LOCK_BIAS, count); + } +#endif +} + +static inline int arch_read_trylock(arch_rwlock_t *lock) +{ + atomic_t *count = (atomic_t *)lock; + atomic_dec(count); + if (atomic_read(count) >= 0) + return 1; + atomic_inc(count); + return 0; +} + +static inline int arch_write_trylock(arch_rwlock_t *lock) +{ + atomic_t *count = (atomic_t *)lock; + if (atomic_sub_and_test(RW_LOCK_BIAS, count)) + return 1; + atomic_add(RW_LOCK_BIAS, count); + return 0; +} + +#define arch_read_lock_flags(lock, flags) arch_read_lock(lock) +#define arch_write_lock_flags(lock, flags) arch_write_lock(lock) + +#define _raw_spin_relax(lock) cpu_relax() +#define _raw_read_relax(lock) cpu_relax() +#define _raw_write_relax(lock) cpu_relax() + +#endif /* __KERNEL__ */ +#endif /* _ASM_SPINLOCK_H */ diff --git a/arch/mn10300/include/asm/spinlock_types.h b/arch/mn10300/include/asm/spinlock_types.h new file mode 100644 index 00000000..653dc519 --- /dev/null +++ b/arch/mn10300/include/asm/spinlock_types.h @@ -0,0 +1,20 @@ +#ifndef _ASM_SPINLOCK_TYPES_H +#define _ASM_SPINLOCK_TYPES_H + +#ifndef __LINUX_SPINLOCK_TYPES_H +# error "please don't include this file directly" +#endif + +typedef struct arch_spinlock { + unsigned int slock; +} arch_spinlock_t; + +#define __ARCH_SPIN_LOCK_UNLOCKED { 0 } + +typedef struct { + unsigned int lock; +} arch_rwlock_t; + +#define __ARCH_RW_LOCK_UNLOCKED { RW_LOCK_BIAS } + +#endif /* _ASM_SPINLOCK_TYPES_H */ diff --git a/arch/mn10300/include/asm/stat.h b/arch/mn10300/include/asm/stat.h new file mode 100644 index 00000000..63ff8371 --- /dev/null +++ b/arch/mn10300/include/asm/stat.h @@ -0,0 +1,78 @@ +#ifndef _ASM_STAT_H +#define _ASM_STAT_H + +struct __old_kernel_stat { + unsigned short st_dev; + unsigned short st_ino; + unsigned short st_mode; + unsigned short st_nlink; + unsigned short st_uid; + unsigned short st_gid; + unsigned short st_rdev; + unsigned long st_size; + unsigned long st_atime; + unsigned long st_mtime; + unsigned long st_ctime; +}; + +struct stat { + unsigned long st_dev; + unsigned long st_ino; + unsigned short st_mode; + unsigned short st_nlink; + unsigned short st_uid; + unsigned short st_gid; + unsigned long st_rdev; + unsigned long st_size; + unsigned long st_blksize; + unsigned long st_blocks; + unsigned long st_atime; + unsigned long st_atime_nsec; + unsigned long st_mtime; + unsigned long st_mtime_nsec; + unsigned long st_ctime; + unsigned long st_ctime_nsec; + unsigned long __unused4; + unsigned long __unused5; +}; + +/* This matches struct stat64 in glibc2.1, hence the absolutely + * insane amounts of padding around dev_t's. + */ +struct stat64 { + unsigned long long st_dev; + unsigned char __pad0[4]; + +#define STAT64_HAS_BROKEN_ST_INO 1 + unsigned long __st_ino; + + unsigned int st_mode; + unsigned int st_nlink; + + unsigned long st_uid; + unsigned long st_gid; + + unsigned long long st_rdev; + unsigned char __pad3[4]; + + long long st_size; + unsigned long st_blksize; + + unsigned long st_blocks; /* Number 512-byte blocks allocated. */ + unsigned long __pad4; /* future possible st_blocks high bits */ + + unsigned long st_atime; + unsigned long st_atime_nsec; + + unsigned long st_mtime; + unsigned int st_mtime_nsec; + + unsigned long st_ctime; + unsigned long st_ctime_nsec; + + unsigned long long st_ino; +}; + +#define STAT_HAVE_NSEC 1 + +#endif /* _ASM_STAT_H */ diff --git a/arch/mn10300/include/asm/statfs.h b/arch/mn10300/include/asm/statfs.h new file mode 100644 index 00000000..0b91fe19 --- /dev/null +++ b/arch/mn10300/include/asm/statfs.h @@ -0,0 +1 @@ +#include <asm-generic/statfs.h> diff --git a/arch/mn10300/include/asm/string.h b/arch/mn10300/include/asm/string.h new file mode 100644 index 00000000..47dbd434 --- /dev/null +++ b/arch/mn10300/include/asm/string.h @@ -0,0 +1,32 @@ +/* MN10300 Optimised string functions + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Modified by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_STRING_H +#define _ASM_STRING_H + +#define __HAVE_ARCH_MEMSET +#define __HAVE_ARCH_MEMCPY +#define __HAVE_ARCH_MEMMOVE + +extern void *memset(void *dest, int ch, size_t count); +extern void *memcpy(void *dest, const void *src, size_t count); +extern void *memmove(void *dest, const void *src, size_t count); + + +extern void __struct_cpy_bug(void); +#define struct_cpy(x, y) \ +({ \ + if (sizeof(*(x)) != sizeof(*(y))) \ + __struct_cpy_bug; \ + memcpy(x, y, sizeof(*(x))); \ +}) + +#endif /* _ASM_STRING_H */ diff --git a/arch/mn10300/include/asm/swab.h b/arch/mn10300/include/asm/swab.h new file mode 100644 index 00000000..bd818a82 --- /dev/null +++ b/arch/mn10300/include/asm/swab.h @@ -0,0 +1,42 @@ +/* MN10300 Byte-order primitive construction + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_SWAB_H +#define _ASM_SWAB_H + +#include <linux/types.h> + +#ifdef __GNUC__ + +static inline __attribute__((const)) +__u32 __arch_swab32(__u32 x) +{ + __u32 ret; + asm("swap %1,%0" : "=r" (ret) : "r" (x)); + return ret; +} +#define __arch_swab32 __arch_swab32 + +static inline __attribute__((const)) +__u16 __arch_swab16(__u16 x) +{ + __u16 ret; + asm("swaph %1,%0" : "=r" (ret) : "r" (x)); + return ret; +} +#define __arch_swab32 __arch_swab32 + +#if !defined(__STRICT_ANSI__) || defined(__KERNEL__) +# define __SWAB_64_THRU_32__ +#endif + +#endif /* __GNUC__ */ + +#endif /* _ASM_SWAB_H */ diff --git a/arch/mn10300/include/asm/syscall.h b/arch/mn10300/include/asm/syscall.h new file mode 100644 index 00000000..b44b0bb7 --- /dev/null +++ b/arch/mn10300/include/asm/syscall.h @@ -0,0 +1,117 @@ +/* Access to user system call parameters and results + * + * See asm-generic/syscall.h for function descriptions. + * + * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _ASM_SYSCALL_H +#define _ASM_SYSCALL_H + +#include <linux/sched.h> +#include <linux/err.h> + +extern const unsigned long sys_call_table[]; + +static inline int syscall_get_nr(struct task_struct *task, struct pt_regs *regs) +{ + return regs->orig_d0; +} + +static inline void syscall_rollback(struct task_struct *task, + struct pt_regs *regs) +{ + regs->d0 = regs->orig_d0; +} + +static inline long syscall_get_error(struct task_struct *task, + struct pt_regs *regs) +{ + unsigned long error = regs->d0; + return IS_ERR_VALUE(error) ? error : 0; +} + +static inline long syscall_get_return_value(struct task_struct *task, + struct pt_regs *regs) +{ + return regs->d0; +} + +static inline void syscall_set_return_value(struct task_struct *task, + struct pt_regs *regs, + int error, long val) +{ + regs->d0 = (long) error ?: val; +} + +static inline void syscall_get_arguments(struct task_struct *task, + struct pt_regs *regs, + unsigned int i, unsigned int n, + unsigned long *args) +{ + switch (i) { + case 0: + if (!n--) break; + *args++ = regs->a0; + case 1: + if (!n--) break; + *args++ = regs->d1; + case 2: + if (!n--) break; + *args++ = regs->a3; + case 3: + if (!n--) break; + *args++ = regs->a2; + case 4: + if (!n--) break; + *args++ = regs->d3; + case 5: + if (!n--) break; + *args++ = regs->d2; + case 6: + if (!n--) break; + default: + BUG(); + break; + } +} + +static inline void syscall_set_arguments(struct task_struct *task, + struct pt_regs *regs, + unsigned int i, unsigned int n, + const unsigned long *args) +{ + switch (i) { + case 0: + if (!n--) break; + regs->a0 = *args++; + case 1: + if (!n--) break; + regs->d1 = *args++; + case 2: + if (!n--) break; + regs->a3 = *args++; + case 3: + if (!n--) break; + regs->a2 = *args++; + case 4: + if (!n--) break; + regs->d3 = *args++; + case 5: + if (!n--) break; + regs->d2 = *args++; + case 6: + if (!n--) break; + default: + BUG(); + break; + } +} + +#endif /* _ASM_SYSCALL_H */ diff --git a/arch/mn10300/include/asm/system.h b/arch/mn10300/include/asm/system.h new file mode 100644 index 00000000..8ff3e5aa --- /dev/null +++ b/arch/mn10300/include/asm/system.h @@ -0,0 +1,102 @@ +/* MN10300 System definitions + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_SYSTEM_H +#define _ASM_SYSTEM_H + +#include <asm/cpu-regs.h> +#include <asm/intctl-regs.h> + +#ifdef __KERNEL__ +#ifndef __ASSEMBLY__ + +#include <linux/kernel.h> +#include <linux/irqflags.h> +#include <asm/atomic.h> + +#if !defined(CONFIG_LAZY_SAVE_FPU) +struct fpu_state_struct; +extern asmlinkage void fpu_save(struct fpu_state_struct *); +#define switch_fpu(prev, next) \ + do { \ + if ((prev)->thread.fpu_flags & THREAD_HAS_FPU) { \ + (prev)->thread.fpu_flags &= ~THREAD_HAS_FPU; \ + (prev)->thread.uregs->epsw &= ~EPSW_FE; \ + fpu_save(&(prev)->thread.fpu_state); \ + } \ + } while (0) +#else +#define switch_fpu(prev, next) do {} while (0) +#endif + +struct task_struct; +struct thread_struct; + +extern asmlinkage +struct task_struct *__switch_to(struct thread_struct *prev, + struct thread_struct *next, + struct task_struct *prev_task); + +/* context switching is now performed out-of-line in switch_to.S */ +#define switch_to(prev, next, last) \ +do { \ + switch_fpu(prev, next); \ + current->thread.wchan = (u_long) __builtin_return_address(0); \ + (last) = __switch_to(&(prev)->thread, &(next)->thread, (prev)); \ + mb(); \ + current->thread.wchan = 0; \ +} while (0) + +#define arch_align_stack(x) (x) + +#define nop() asm volatile ("nop") + +/* + * Force strict CPU ordering. + * And yes, this is required on UP too when we're talking + * to devices. + * + * For now, "wmb()" doesn't actually do anything, as all + * Intel CPU's follow what Intel calls a *Processor Order*, + * in which all writes are seen in the program order even + * outside the CPU. + * + * I expect future Intel CPU's to have a weaker ordering, + * but I'd also expect them to finally get their act together + * and add some real memory barriers if so. + * + * Some non intel clones support out of order store. wmb() ceases to be a + * nop for these. + */ + +#define mb() asm volatile ("": : :"memory") +#define rmb() mb() +#define wmb() asm volatile ("": : :"memory") + +#ifdef CONFIG_SMP +#define smp_mb() mb() +#define smp_rmb() rmb() +#define smp_wmb() wmb() +#define set_mb(var, value) do { xchg(&var, value); } while (0) +#else /* CONFIG_SMP */ +#define smp_mb() barrier() +#define smp_rmb() barrier() +#define smp_wmb() barrier() +#define set_mb(var, value) do { var = value; mb(); } while (0) +#endif /* CONFIG_SMP */ + +#define set_wmb(var, value) do { var = value; wmb(); } while (0) + +#define read_barrier_depends() do {} while (0) +#define smp_read_barrier_depends() do {} while (0) + +#endif /* !__ASSEMBLY__ */ +#endif /* __KERNEL__ */ +#endif /* _ASM_SYSTEM_H */ diff --git a/arch/mn10300/include/asm/termbits.h b/arch/mn10300/include/asm/termbits.h new file mode 100644 index 00000000..130d4249 --- /dev/null +++ b/arch/mn10300/include/asm/termbits.h @@ -0,0 +1,201 @@ +#ifndef _ASM_TERMBITS_H +#define _ASM_TERMBITS_H + +#include <linux/posix_types.h> + +typedef unsigned char cc_t; +typedef unsigned int speed_t; +typedef unsigned int tcflag_t; + +#define NCCS 19 +struct termios { + tcflag_t c_iflag; /* input mode flags */ + tcflag_t c_oflag; /* output mode flags */ + tcflag_t c_cflag; /* control mode flags */ + tcflag_t c_lflag; /* local mode flags */ + cc_t c_line; /* line discipline */ + cc_t c_cc[NCCS]; /* control characters */ +}; + +struct termios2 { + tcflag_t c_iflag; /* input mode flags */ + tcflag_t c_oflag; /* output mode flags */ + tcflag_t c_cflag; /* control mode flags */ + tcflag_t c_lflag; /* local mode flags */ + cc_t c_line; /* line discipline */ + cc_t c_cc[NCCS]; /* control characters */ + speed_t c_ispeed; /* input speed */ + speed_t c_ospeed; /* output speed */ +}; + +struct ktermios { + tcflag_t c_iflag; /* input mode flags */ + tcflag_t c_oflag; /* output mode flags */ + tcflag_t c_cflag; /* control mode flags */ + tcflag_t c_lflag; /* local mode flags */ + cc_t c_line; /* line discipline */ + cc_t c_cc[NCCS]; /* control characters */ + speed_t c_ispeed; /* input speed */ + speed_t c_ospeed; /* output speed */ +}; + +/* c_cc characters */ +#define VINTR 0 +#define VQUIT 1 +#define VERASE 2 +#define VKILL 3 +#define VEOF 4 +#define VTIME 5 +#define VMIN 6 +#define VSWTC 7 +#define VSTART 8 +#define VSTOP 9 +#define VSUSP 10 +#define VEOL 11 +#define VREPRINT 12 +#define VDISCARD 13 +#define VWERASE 14 +#define VLNEXT 15 +#define VEOL2 16 + + +/* c_iflag bits */ +#define IGNBRK 0000001 +#define BRKINT 0000002 +#define IGNPAR 0000004 +#define PARMRK 0000010 +#define INPCK 0000020 +#define ISTRIP 0000040 +#define INLCR 0000100 +#define IGNCR 0000200 +#define ICRNL 0000400 +#define IUCLC 0001000 +#define IXON 0002000 +#define IXANY 0004000 +#define IXOFF 0010000 +#define IMAXBEL 0020000 +#define IUTF8 0040000 + +/* c_oflag bits */ +#define OPOST 0000001 +#define OLCUC 0000002 +#define ONLCR 0000004 +#define OCRNL 0000010 +#define ONOCR 0000020 +#define ONLRET 0000040 +#define OFILL 0000100 +#define OFDEL 0000200 +#define NLDLY 0000400 +#define NL0 0000000 +#define NL1 0000400 +#define CRDLY 0003000 +#define CR0 0000000 +#define CR1 0001000 +#define CR2 0002000 +#define CR3 0003000 +#define TABDLY 0014000 +#define TAB0 0000000 +#define TAB1 0004000 +#define TAB2 0010000 +#define TAB3 0014000 +#define XTABS 0014000 +#define BSDLY 0020000 +#define BS0 0000000 +#define BS1 0020000 +#define VTDLY 0040000 +#define VT0 0000000 +#define VT1 0040000 +#define FFDLY 0100000 +#define FF0 0000000 +#define FF1 0100000 + +/* c_cflag bit meaning */ +#define CBAUD 0010017 +#define B0 0000000 /* hang up */ +#define B50 0000001 +#define B75 0000002 +#define B110 0000003 +#define B134 0000004 +#define B150 0000005 +#define B200 0000006 +#define B300 0000007 +#define B600 0000010 +#define B1200 0000011 +#define B1800 0000012 +#define B2400 0000013 +#define B4800 0000014 +#define B9600 0000015 +#define B19200 0000016 +#define B38400 0000017 +#define EXTA B19200 +#define EXTB B38400 +#define CSIZE 0000060 +#define CS5 0000000 +#define CS6 0000020 +#define CS7 0000040 +#define CS8 0000060 +#define CSTOPB 0000100 +#define CREAD 0000200 +#define PARENB 0000400 +#define PARODD 0001000 +#define HUPCL 0002000 +#define CLOCAL 0004000 +#define CBAUDEX 0010000 +#define BOTHER 0010000 +#define B57600 0010001 +#define B115200 0010002 +#define B230400 0010003 +#define B460800 0010004 +#define B500000 0010005 +#define B576000 0010006 +#define B921600 0010007 +#define B1000000 0010010 +#define B1152000 0010011 +#define B1500000 0010012 +#define B2000000 0010013 +#define B2500000 0010014 +#define B3000000 0010015 +#define B3500000 0010016 +#define B4000000 0010017 +#define CIBAUD 002003600000 /* input baud rate (not used) */ +#define CTVB 004000000000 /* VisioBraille Terminal flow control */ +#define CMSPAR 010000000000 /* mark or space (stick) parity */ +#define CRTSCTS 020000000000 /* flow control */ + +#define IBSHIFT 16 /* Shift from CBAUD to CIBAUD */ + +/* c_lflag bits */ +#define ISIG 0000001 +#define ICANON 0000002 +#define XCASE 0000004 +#define ECHO 0000010 +#define ECHOE 0000020 +#define ECHOK 0000040 +#define ECHONL 0000100 +#define NOFLSH 0000200 +#define TOSTOP 0000400 +#define ECHOCTL 0001000 +#define ECHOPRT 0002000 +#define ECHOKE 0004000 +#define FLUSHO 0010000 +#define PENDIN 0040000 +#define IEXTEN 0100000 +#define EXTPROC 0200000 + +/* tcflow() and TCXONC use these */ +#define TCOOFF 0 +#define TCOON 1 +#define TCIOFF 2 +#define TCION 3 + +/* tcflush() and TCFLSH use these */ +#define TCIFLUSH 0 +#define TCOFLUSH 1 +#define TCIOFLUSH 2 + +/* tcsetattr uses these */ +#define TCSANOW 0 +#define TCSADRAIN 1 +#define TCSAFLUSH 2 + +#endif /* _ASM_TERMBITS_H */ diff --git a/arch/mn10300/include/asm/termios.h b/arch/mn10300/include/asm/termios.h new file mode 100644 index 00000000..dd7cf617 --- /dev/null +++ b/arch/mn10300/include/asm/termios.h @@ -0,0 +1,92 @@ +#ifndef _ASM_TERMIOS_H +#define _ASM_TERMIOS_H + +#include <asm/termbits.h> +#include <asm/ioctls.h> + +struct winsize { + unsigned short ws_row; + unsigned short ws_col; + unsigned short ws_xpixel; + unsigned short ws_ypixel; +}; + +#define NCC 8 +struct termio { + unsigned short c_iflag; /* input mode flags */ + unsigned short c_oflag; /* output mode flags */ + unsigned short c_cflag; /* control mode flags */ + unsigned short c_lflag; /* local mode flags */ + unsigned char c_line; /* line discipline */ + unsigned char c_cc[NCC]; /* control characters */ +}; + +#ifdef __KERNEL__ +/* intr=^C quit=^| erase=del kill=^U + eof=^D vtime=\0 vmin=\1 sxtc=\0 + start=^Q stop=^S susp=^Z eol=\0 + reprint=^R discard=^U werase=^W lnext=^V + eol2=\0 +*/ +#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0" +#endif + +/* modem lines */ +#define TIOCM_LE 0x001 +#define TIOCM_DTR 0x002 +#define TIOCM_RTS 0x004 +#define TIOCM_ST 0x008 +#define TIOCM_SR 0x010 +#define TIOCM_CTS 0x020 +#define TIOCM_CAR 0x040 +#define TIOCM_RNG 0x080 +#define TIOCM_DSR 0x100 +#define TIOCM_CD TIOCM_CAR +#define TIOCM_RI TIOCM_RNG +#define TIOCM_OUT1 0x2000 +#define TIOCM_OUT2 0x4000 +#define TIOCM_LOOP 0x8000 + +#define TIOCM_MODEM_BITS TIOCM_OUT2 /* IRDA support */ + +/* + * Translate a "termio" structure into a "termios". Ugh. + */ +#define SET_LOW_TERMIOS_BITS(termios, termio, x) { \ + unsigned short __tmp; \ + get_user(__tmp, &(termio)->x); \ + *(unsigned short *) &(termios)->x = __tmp; \ +} + +#define user_termio_to_kernel_termios(termios, termio) \ +({ \ + SET_LOW_TERMIOS_BITS(termios, termio, c_iflag); \ + SET_LOW_TERMIOS_BITS(termios, termio, c_oflag); \ + SET_LOW_TERMIOS_BITS(termios, termio, c_cflag); \ + SET_LOW_TERMIOS_BITS(termios, termio, c_lflag); \ + copy_from_user((termios)->c_cc, (termio)->c_cc, NCC); \ +}) + +/* + * Translate a "termios" structure into a "termio". Ugh. + */ +#define kernel_termios_to_user_termio(termio, termios) \ +({ \ + put_user((termios)->c_iflag, &(termio)->c_iflag); \ + put_user((termios)->c_oflag, &(termio)->c_oflag); \ + put_user((termios)->c_cflag, &(termio)->c_cflag); \ + put_user((termios)->c_lflag, &(termio)->c_lflag); \ + put_user((termios)->c_line, &(termio)->c_line); \ + copy_to_user((termio)->c_cc, (termios)->c_cc, NCC); \ +}) + +#define user_termios_to_kernel_termios(k, u) \ + copy_from_user(k, u, sizeof(struct termios2)) +#define kernel_termios_to_user_termios(u, k) \ + copy_to_user(u, k, sizeof(struct termios2)) +#define user_termios_to_kernel_termios_1(k, u) \ + copy_from_user(k, u, sizeof(struct termios)) +#define kernel_termios_to_user_termios_1(u, k) \ + copy_to_user(u, k, sizeof(struct termios)) + +#endif /* _ASM_TERMIOS_H */ diff --git a/arch/mn10300/include/asm/thread_info.h b/arch/mn10300/include/asm/thread_info.h new file mode 100644 index 00000000..87c21300 --- /dev/null +++ b/arch/mn10300/include/asm/thread_info.h @@ -0,0 +1,184 @@ +/* MN10300 Low-level thread information + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _ASM_THREAD_INFO_H +#define _ASM_THREAD_INFO_H + +#ifdef __KERNEL__ + +#include <asm/page.h> + +#define PREEMPT_ACTIVE 0x10000000 + +#ifdef CONFIG_4KSTACKS +#define THREAD_SIZE (4096) +#else +#define THREAD_SIZE (8192) +#endif + +#define STACK_WARN (THREAD_SIZE / 8) + +/* + * low level task data that entry.S needs immediate access to + * - this struct should fit entirely inside of one cache line + * - this struct shares the supervisor stack pages + * - if the contents of this structure are changed, the assembly constants + * must also be changed + */ +#ifndef __ASSEMBLY__ +typedef struct { + unsigned long seg; +} mm_segment_t; + +struct thread_info { + struct task_struct *task; /* main task structure */ + struct exec_domain *exec_domain; /* execution domain */ + struct pt_regs *frame; /* current exception frame */ + unsigned long flags; /* low level flags */ + __u32 cpu; /* current CPU */ + __s32 preempt_count; /* 0 => preemptable, <0 => BUG */ + + mm_segment_t addr_limit; /* thread address space: + 0-0xBFFFFFFF for user-thead + 0-0xFFFFFFFF for kernel-thread + */ + struct restart_block restart_block; + + __u8 supervisor_stack[0]; +}; + +#define thread_info_to_uregs(ti) \ + ((struct pt_regs *) \ + ((unsigned long)ti + THREAD_SIZE - sizeof(struct pt_regs))) + +#else /* !__ASSEMBLY__ */ + +#ifndef __ASM_OFFSETS_H__ +#include <asm/asm-offsets.h> +#endif + +#endif + +/* + * macros/functions for gaining access to the thread information structure + */ +#ifndef __ASSEMBLY__ + +#define INIT_THREAD_INFO(tsk) \ +{ \ + .task = &tsk, \ + .exec_domain = &default_exec_domain, \ + .flags = 0, \ + .cpu = 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) +#define init_uregs \ + ((struct pt_regs *) \ + ((unsigned long) init_stack + THREAD_SIZE - sizeof(struct pt_regs))) + +extern struct thread_info *__current_ti; + +/* how to get the thread information struct from C */ +static inline __attribute__((const)) +struct thread_info *current_thread_info(void) +{ + struct thread_info *ti; + asm("mov sp,%0\n" + "and %1,%0\n" + : "=d" (ti) + : "i" (~(THREAD_SIZE - 1)) + : "cc"); + return ti; +} + +static inline __attribute__((const)) +struct pt_regs *current_frame(void) +{ + return current_thread_info()->frame; +} + +/* how to get the current stack pointer from C */ +static inline unsigned long current_stack_pointer(void) +{ + unsigned long sp; + asm("mov sp,%0; ":"=r" (sp)); + return sp; +} + +#define __HAVE_ARCH_THREAD_INFO_ALLOCATOR + +/* thread information allocation */ +#ifdef CONFIG_DEBUG_STACK_USAGE +#define alloc_thread_info_node(tsk, node) \ + kzalloc_node(THREAD_SIZE, GFP_KERNEL, node) +#else +#define alloc_thread_info_node(tsk, node) \ + kmalloc_node(THREAD_SIZE, GFP_KERNEL, node) +#endif + +#ifndef CONFIG_KGDB +#define free_thread_info(ti) kfree((ti)) +#else +extern void free_thread_info(struct thread_info *); +#endif +#define get_thread_info(ti) get_task_struct((ti)->task) +#define put_thread_info(ti) put_task_struct((ti)->task) + +#else /* !__ASSEMBLY__ */ + +#ifndef __VMLINUX_LDS__ +/* how to get the thread information struct from ASM */ +.macro GET_THREAD_INFO reg + mov sp,\reg + and -THREAD_SIZE,\reg +.endm +#endif +#endif + +/* + * thread information flags + * - these are process state flags that various assembly files may need to + * access + * - pending work-to-be-done flags are in LSW + * - other flags in MSW + */ +#define TIF_SYSCALL_TRACE 0 /* syscall trace active */ +#define TIF_NOTIFY_RESUME 1 /* resumption notification requested */ +#define TIF_SIGPENDING 2 /* signal pending */ +#define TIF_NEED_RESCHED 3 /* rescheduling necessary */ +#define TIF_SINGLESTEP 4 /* restore singlestep on return to user mode */ +#define TIF_RESTORE_SIGMASK 5 /* restore signal mask in do_signal() */ +#define TIF_POLLING_NRFLAG 16 /* true if poll_idle() is polling TIF_NEED_RESCHED */ +#define TIF_MEMDIE 17 /* is terminating due to OOM killer */ +#define TIF_FREEZE 18 /* freezing for suspend */ + +#define _TIF_SYSCALL_TRACE +(1 << TIF_SYSCALL_TRACE) +#define _TIF_NOTIFY_RESUME +(1 << TIF_NOTIFY_RESUME) +#define _TIF_SIGPENDING +(1 << TIF_SIGPENDING) +#define _TIF_NEED_RESCHED +(1 << TIF_NEED_RESCHED) +#define _TIF_SINGLESTEP +(1 << TIF_SINGLESTEP) +#define _TIF_RESTORE_SIGMASK +(1 << TIF_RESTORE_SIGMASK) +#define _TIF_POLLING_NRFLAG +(1 << TIF_POLLING_NRFLAG) +#define _TIF_FREEZE +(1 << TIF_FREEZE) + +#define _TIF_WORK_MASK 0x0000FFFE /* work to do on interrupt/exception return */ +#define _TIF_ALLWORK_MASK 0x0000FFFF /* work to do on any return to u-space */ + +#endif /* __KERNEL__ */ + +#endif /* _ASM_THREAD_INFO_H */ diff --git a/arch/mn10300/include/asm/timer-regs.h b/arch/mn10300/include/asm/timer-regs.h new file mode 100644 index 00000000..c634977c --- /dev/null +++ b/arch/mn10300/include/asm/timer-regs.h @@ -0,0 +1,452 @@ +/* AM33v2 on-board timer module registers + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _ASM_TIMER_REGS_H +#define _ASM_TIMER_REGS_H + +#include <asm/cpu-regs.h> +#include <asm/intctl-regs.h> + +#ifdef __KERNEL__ + +/* + * Timer prescalar control + */ +#define TMPSCNT __SYSREG(0xd4003071, u8) /* timer prescaler control */ +#define TMPSCNT_ENABLE 0x80 /* timer prescaler enable */ +#define TMPSCNT_DISABLE 0x00 /* timer prescaler disable */ + +/* + * 8-bit timers + */ +#define TM0MD __SYSREG(0xd4003000, u8) /* timer 0 mode register */ +#define TM0MD_SRC 0x07 /* timer source */ +#define TM0MD_SRC_IOCLK 0x00 /* - IOCLK */ +#define TM0MD_SRC_IOCLK_8 0x01 /* - 1/8 IOCLK */ +#define TM0MD_SRC_IOCLK_32 0x02 /* - 1/32 IOCLK */ +#define TM0MD_SRC_TM1UFLOW 0x05 /* - timer 1 underflow */ +#define TM0MD_SRC_TM2UFLOW 0x06 /* - timer 2 underflow */ +#if defined(CONFIG_AM33_2) +#define TM0MD_SRC_TM2IO 0x03 /* - TM2IO pin input */ +#define TM0MD_SRC_TM0IO 0x07 /* - TM0IO pin input */ +#endif /* CONFIG_AM33_2 */ +#define TM0MD_INIT_COUNTER 0x40 /* initialize TMnBC = TMnBR */ +#define TM0MD_COUNT_ENABLE 0x80 /* timer count enable */ + +#define TM1MD __SYSREG(0xd4003001, u8) /* timer 1 mode register */ +#define TM1MD_SRC 0x07 /* timer source */ +#define TM1MD_SRC_IOCLK 0x00 /* - IOCLK */ +#define TM1MD_SRC_IOCLK_8 0x01 /* - 1/8 IOCLK */ +#define TM1MD_SRC_IOCLK_32 0x02 /* - 1/32 IOCLK */ +#define TM1MD_SRC_TM0CASCADE 0x03 /* - cascade with timer 0 */ +#define TM1MD_SRC_TM0UFLOW 0x04 /* - timer 0 underflow */ +#define TM1MD_SRC_TM2UFLOW 0x06 /* - timer 2 underflow */ +#if defined(CONFIG_AM33_2) +#define TM1MD_SRC_TM1IO 0x07 /* - TM1IO pin input */ +#endif /* CONFIG_AM33_2 */ +#define TM1MD_INIT_COUNTER 0x40 /* initialize TMnBC = TMnBR */ +#define TM1MD_COUNT_ENABLE 0x80 /* timer count enable */ + +#define TM2MD __SYSREG(0xd4003002, u8) /* timer 2 mode register */ +#define TM2MD_SRC 0x07 /* timer source */ +#define TM2MD_SRC_IOCLK 0x00 /* - IOCLK */ +#define TM2MD_SRC_IOCLK_8 0x01 /* - 1/8 IOCLK */ +#define TM2MD_SRC_IOCLK_32 0x02 /* - 1/32 IOCLK */ +#define TM2MD_SRC_TM1CASCADE 0x03 /* - cascade with timer 1 */ +#define TM2MD_SRC_TM0UFLOW 0x04 /* - timer 0 underflow */ +#define TM2MD_SRC_TM1UFLOW 0x05 /* - timer 1 underflow */ +#if defined(CONFIG_AM33_2) +#define TM2MD_SRC_TM2IO 0x07 /* - TM2IO pin input */ +#endif /* CONFIG_AM33_2 */ +#define TM2MD_INIT_COUNTER 0x40 /* initialize TMnBC = TMnBR */ +#define TM2MD_COUNT_ENABLE 0x80 /* timer count enable */ + +#define TM3MD __SYSREG(0xd4003003, u8) /* timer 3 mode register */ +#define TM3MD_SRC 0x07 /* timer source */ +#define TM3MD_SRC_IOCLK 0x00 /* - IOCLK */ +#define TM3MD_SRC_IOCLK_8 0x01 /* - 1/8 IOCLK */ +#define TM3MD_SRC_IOCLK_32 0x02 /* - 1/32 IOCLK */ +#define TM3MD_SRC_TM2CASCADE 0x03 /* - cascade with timer 2 */ +#define TM3MD_SRC_TM0UFLOW 0x04 /* - timer 0 underflow */ +#define TM3MD_SRC_TM1UFLOW 0x05 /* - timer 1 underflow */ +#define TM3MD_SRC_TM2UFLOW 0x06 /* - timer 2 underflow */ +#if defined(CONFIG_AM33_2) +#define TM3MD_SRC_TM3IO 0x07 /* - TM3IO pin input */ +#endif /* CONFIG_AM33_2 */ +#define TM3MD_INIT_COUNTER 0x40 /* initialize TMnBC = TMnBR */ +#define TM3MD_COUNT_ENABLE 0x80 /* timer count enable */ + +#define TM01MD __SYSREG(0xd4003000, u16) /* timer 0:1 mode register */ + +#define TM0BR __SYSREG(0xd4003010, u8) /* timer 0 base register */ +#define TM1BR __SYSREG(0xd4003011, u8) /* timer 1 base register */ +#define TM2BR __SYSREG(0xd4003012, u8) /* timer 2 base register */ +#define TM3BR __SYSREG(0xd4003013, u8) /* timer 3 base register */ +#define TM01BR __SYSREG(0xd4003010, u16) /* timer 0:1 base register */ + +#define TM0BC __SYSREGC(0xd4003020, u8) /* timer 0 binary counter */ +#define TM1BC __SYSREGC(0xd4003021, u8) /* timer 1 binary counter */ +#define TM2BC __SYSREGC(0xd4003022, u8) /* timer 2 binary counter */ +#define TM3BC __SYSREGC(0xd4003023, u8) /* timer 3 binary counter */ +#define TM01BC __SYSREGC(0xd4003020, u16) /* timer 0:1 binary counter */ + +#define TM0IRQ 2 /* timer 0 IRQ */ +#define TM1IRQ 3 /* timer 1 IRQ */ +#define TM2IRQ 4 /* timer 2 IRQ */ +#define TM3IRQ 5 /* timer 3 IRQ */ + +#define TM0ICR GxICR(TM0IRQ) /* timer 0 uflow intr ctrl reg */ +#define TM1ICR GxICR(TM1IRQ) /* timer 1 uflow intr ctrl reg */ +#define TM2ICR GxICR(TM2IRQ) /* timer 2 uflow intr ctrl reg */ +#define TM3ICR GxICR(TM3IRQ) /* timer 3 uflow intr ctrl reg */ + +/* + * 16-bit timers 4,5 & 7-15 + */ +#define TM4MD __SYSREG(0xd4003080, u8) /* timer 4 mode register */ +#define TM4MD_SRC 0x07 /* timer source */ +#define TM4MD_SRC_IOCLK 0x00 /* - IOCLK */ +#define TM4MD_SRC_IOCLK_8 0x01 /* - 1/8 IOCLK */ +#define TM4MD_SRC_IOCLK_32 0x02 /* - 1/32 IOCLK */ +#define TM4MD_SRC_TM0UFLOW 0x04 /* - timer 0 underflow */ +#define TM4MD_SRC_TM1UFLOW 0x05 /* - timer 1 underflow */ +#define TM4MD_SRC_TM2UFLOW 0x06 /* - timer 2 underflow */ +#if defined(CONFIG_AM33_2) +#define TM4MD_SRC_TM4IO 0x07 /* - TM4IO pin input */ +#endif /* CONFIG_AM33_2 */ +#define TM4MD_INIT_COUNTER 0x40 /* initialize TMnBC = TMnBR */ +#define TM4MD_COUNT_ENABLE 0x80 /* timer count enable */ + +#define TM5MD __SYSREG(0xd4003082, u8) /* timer 5 mode register */ +#define TM5MD_SRC 0x07 /* timer source */ +#define TM5MD_SRC_IOCLK 0x00 /* - IOCLK */ +#define TM5MD_SRC_IOCLK_8 0x01 /* - 1/8 IOCLK */ +#define TM5MD_SRC_IOCLK_32 0x02 /* - 1/32 IOCLK */ +#define TM5MD_SRC_TM4CASCADE 0x03 /* - cascade with timer 4 */ +#define TM5MD_SRC_TM0UFLOW 0x04 /* - timer 0 underflow */ +#define TM5MD_SRC_TM1UFLOW 0x05 /* - timer 1 underflow */ +#define TM5MD_SRC_TM2UFLOW 0x06 /* - timer 2 underflow */ +#if defined(CONFIG_AM33_2) +#define TM5MD_SRC_TM5IO 0x07 /* - TM5IO pin input */ +#else /* !CONFIG_AM33_2 */ +#define TM5MD_SRC_TM7UFLOW 0x07 /* - timer 7 underflow */ +#endif /* CONFIG_AM33_2 */ +#define TM5MD_INIT_COUNTER 0x40 /* initialize TMnBC = TMnBR */ +#define TM5MD_COUNT_ENABLE 0x80 /* timer count enable */ + +#define TM7MD __SYSREG(0xd4003086, u8) /* timer 7 mode register */ +#define TM7MD_SRC 0x07 /* timer source */ +#define TM7MD_SRC_IOCLK 0x00 /* - IOCLK */ +#define TM7MD_SRC_IOCLK_8 0x01 /* - 1/8 IOCLK */ +#define TM7MD_SRC_IOCLK_32 0x02 /* - 1/32 IOCLK */ +#define TM7MD_SRC_TM0UFLOW 0x04 /* - timer 0 underflow */ +#define TM7MD_SRC_TM1UFLOW 0x05 /* - timer 1 underflow */ +#define TM7MD_SRC_TM2UFLOW 0x06 /* - timer 2 underflow */ +#if defined(CONFIG_AM33_2) +#define TM7MD_SRC_TM7IO 0x07 /* - TM7IO pin input */ +#endif /* CONFIG_AM33_2 */ +#define TM7MD_INIT_COUNTER 0x40 /* initialize TMnBC = TMnBR */ +#define TM7MD_COUNT_ENABLE 0x80 /* timer count enable */ + +#define TM8MD __SYSREG(0xd4003088, u8) /* timer 8 mode register */ +#define TM8MD_SRC 0x07 /* timer source */ +#define TM8MD_SRC_IOCLK 0x00 /* - IOCLK */ +#define TM8MD_SRC_IOCLK_8 0x01 /* - 1/8 IOCLK */ +#define TM8MD_SRC_IOCLK_32 0x02 /* - 1/32 IOCLK */ +#define TM8MD_SRC_TM7CASCADE 0x03 /* - cascade with timer 7 */ +#define TM8MD_SRC_TM0UFLOW 0x04 /* - timer 0 underflow */ +#define TM8MD_SRC_TM1UFLOW 0x05 /* - timer 1 underflow */ +#define TM8MD_SRC_TM2UFLOW 0x06 /* - timer 2 underflow */ +#if defined(CONFIG_AM33_2) +#define TM8MD_SRC_TM8IO 0x07 /* - TM8IO pin input */ +#else /* !CONFIG_AM33_2 */ +#define TM8MD_SRC_TM7UFLOW 0x07 /* - timer 7 underflow */ +#endif /* CONFIG_AM33_2 */ +#define TM8MD_INIT_COUNTER 0x40 /* initialize TMnBC = TMnBR */ +#define TM8MD_COUNT_ENABLE 0x80 /* timer count enable */ + +#define TM9MD __SYSREG(0xd400308a, u8) /* timer 9 mode register */ +#define TM9MD_SRC 0x07 /* timer source */ +#define TM9MD_SRC_IOCLK 0x00 /* - IOCLK */ +#define TM9MD_SRC_IOCLK_8 0x01 /* - 1/8 IOCLK */ +#define TM9MD_SRC_IOCLK_32 0x02 /* - 1/32 IOCLK */ +#define TM9MD_SRC_TM8CASCADE 0x03 /* - cascade with timer 8 */ +#define TM9MD_SRC_TM0UFLOW 0x04 /* - timer 0 underflow */ +#define TM9MD_SRC_TM1UFLOW 0x05 /* - timer 1 underflow */ +#define TM9MD_SRC_TM2UFLOW 0x06 /* - timer 2 underflow */ +#if defined(CONFIG_AM33_2) +#define TM9MD_SRC_TM9IO 0x07 /* - TM9IO pin input */ +#else /* !CONFIG_AM33_2 */ +#define TM9MD_SRC_TM7UFLOW 0x07 /* - timer 7 underflow */ +#endif /* CONFIG_AM33_2 */ +#define TM9MD_INIT_COUNTER 0x40 /* initialize TMnBC = TMnBR */ +#define TM9MD_COUNT_ENABLE 0x80 /* timer count enable */ + +#define TM10MD __SYSREG(0xd400308c, u8) /* timer 10 mode register */ +#define TM10MD_SRC 0x07 /* timer source */ +#define TM10MD_SRC_IOCLK 0x00 /* - IOCLK */ +#define TM10MD_SRC_IOCLK_8 0x01 /* - 1/8 IOCLK */ +#define TM10MD_SRC_IOCLK_32 0x02 /* - 1/32 IOCLK */ +#define TM10MD_SRC_TM9CASCADE 0x03 /* - cascade with timer 9 */ +#define TM10MD_SRC_TM0UFLOW 0x04 /* - timer 0 underflow */ +#define TM10MD_SRC_TM1UFLOW 0x05 /* - timer 1 underflow */ +#define TM10MD_SRC_TM2UFLOW 0x06 /* - timer 2 underflow */ +#if defined(CONFIG_AM33_2) +#define TM10MD_SRC_TM10IO 0x07 /* - TM10IO pin input */ +#else /* !CONFIG_AM33_2 */ +#define TM10MD_SRC_TM7UFLOW 0x07 /* - timer 7 underflow */ +#endif /* CONFIG_AM33_2 */ +#define TM10MD_INIT_COUNTER 0x40 /* initialize TMnBC = TMnBR */ +#define TM10MD_COUNT_ENABLE 0x80 /* timer count enable */ + +#define TM11MD __SYSREG(0xd400308e, u8) /* timer 11 mode register */ +#define TM11MD_SRC 0x07 /* timer source */ +#define TM11MD_SRC_IOCLK 0x00 /* - IOCLK */ +#define TM11MD_SRC_IOCLK_8 0x01 /* - 1/8 IOCLK */ +#define TM11MD_SRC_IOCLK_32 0x02 /* - 1/32 IOCLK */ +#define TM11MD_SRC_TM0UFLOW 0x04 /* - timer 0 underflow */ +#define TM11MD_SRC_TM1UFLOW 0x05 /* - timer 1 underflow */ +#define TM11MD_SRC_TM2UFLOW 0x06 /* - timer 2 underflow */ +#if defined(CONFIG_AM33_2) +#define TM11MD_SRC_TM11IO 0x07 /* - TM11IO pin input */ +#else /* !CONFIG_AM33_2 */ +#define TM11MD_SRC_TM7UFLOW 0x07 /* - timer 7 underflow */ +#endif /* CONFIG_AM33_2 */ +#define TM11MD_INIT_COUNTER 0x40 /* initialize TMnBC = TMnBR */ +#define TM11MD_COUNT_ENABLE 0x80 /* timer count enable */ + +#if defined(CONFIG_AM34_2) +#define TM12MD __SYSREG(0xd4003180, u8) /* timer 11 mode register */ +#define TM12MD_SRC 0x07 /* timer source */ +#define TM12MD_SRC_IOCLK 0x00 /* - IOCLK */ +#define TM12MD_SRC_IOCLK_8 0x01 /* - 1/8 IOCLK */ +#define TM12MD_SRC_IOCLK_32 0x02 /* - 1/32 IOCLK */ +#define TM12MD_SRC_TM0UFLOW 0x04 /* - timer 0 underflow */ +#define TM12MD_SRC_TM1UFLOW 0x05 /* - timer 1 underflow */ +#define TM12MD_SRC_TM2UFLOW 0x06 /* - timer 2 underflow */ +#define TM12MD_SRC_TM7UFLOW 0x07 /* - timer 7 underflow */ +#define TM12MD_INIT_COUNTER 0x40 /* initialize TMnBC = TMnBR */ +#define TM12MD_COUNT_ENABLE 0x80 /* timer count enable */ + +#define TM13MD __SYSREG(0xd4003182, u8) /* timer 11 mode register */ +#define TM13MD_SRC 0x07 /* timer source */ +#define TM13MD_SRC_IOCLK 0x00 /* - IOCLK */ +#define TM13MD_SRC_IOCLK_8 0x01 /* - 1/8 IOCLK */ +#define TM13MD_SRC_IOCLK_32 0x02 /* - 1/32 IOCLK */ +#define TM13MD_SRC_TM12CASCADE 0x03 /* - cascade with timer 12 */ +#define TM13MD_SRC_TM0UFLOW 0x04 /* - timer 0 underflow */ +#define TM13MD_SRC_TM1UFLOW 0x05 /* - timer 1 underflow */ +#define TM13MD_SRC_TM2UFLOW 0x06 /* - timer 2 underflow */ +#define TM13MD_SRC_TM7UFLOW 0x07 /* - timer 7 underflow */ +#define TM13MD_INIT_COUNTER 0x40 /* initialize TMnBC = TMnBR */ +#define TM13MD_COUNT_ENABLE 0x80 /* timer count enable */ + +#define TM14MD __SYSREG(0xd4003184, u8) /* timer 11 mode register */ +#define TM14MD_SRC 0x07 /* timer source */ +#define TM14MD_SRC_IOCLK 0x00 /* - IOCLK */ +#define TM14MD_SRC_IOCLK_8 0x01 /* - 1/8 IOCLK */ +#define TM14MD_SRC_IOCLK_32 0x02 /* - 1/32 IOCLK */ +#define TM14MD_SRC_TM13CASCADE 0x03 /* - cascade with timer 13 */ +#define TM14MD_SRC_TM0UFLOW 0x04 /* - timer 0 underflow */ +#define TM14MD_SRC_TM1UFLOW 0x05 /* - timer 1 underflow */ +#define TM14MD_SRC_TM2UFLOW 0x06 /* - timer 2 underflow */ +#define TM14MD_SRC_TM7UFLOW 0x07 /* - timer 7 underflow */ +#define TM14MD_INIT_COUNTER 0x40 /* initialize TMnBC = TMnBR */ +#define TM14MD_COUNT_ENABLE 0x80 /* timer count enable */ + +#define TM15MD __SYSREG(0xd4003186, u8) /* timer 11 mode register */ +#define TM15MD_SRC 0x07 /* timer source */ +#define TM15MD_SRC_IOCLK 0x00 /* - IOCLK */ +#define TM15MD_SRC_IOCLK_8 0x01 /* - 1/8 IOCLK */ +#define TM15MD_SRC_IOCLK_32 0x02 /* - 1/32 IOCLK */ +#define TM15MD_SRC_TM0UFLOW 0x04 /* - timer 0 underflow */ +#define TM15MD_SRC_TM1UFLOW 0x05 /* - timer 1 underflow */ +#define TM15MD_SRC_TM2UFLOW 0x06 /* - timer 2 underflow */ +#define TM15MD_SRC_TM7UFLOW 0x07 /* - timer 7 underflow */ +#define TM15MD_INIT_COUNTER 0x40 /* initialize TMnBC = TMnBR */ +#define TM15MD_COUNT_ENABLE 0x80 /* timer count enable */ +#endif /* CONFIG_AM34_2 */ + + +#define TM4BR __SYSREG(0xd4003090, u16) /* timer 4 base register */ +#define TM5BR __SYSREG(0xd4003092, u16) /* timer 5 base register */ +#define TM45BR __SYSREG(0xd4003090, u32) /* timer 4:5 base register */ +#define TM7BR __SYSREG(0xd4003096, u16) /* timer 7 base register */ +#define TM8BR __SYSREG(0xd4003098, u16) /* timer 8 base register */ +#define TM9BR __SYSREG(0xd400309a, u16) /* timer 9 base register */ +#define TM89BR __SYSREG(0xd4003098, u32) /* timer 8:9 base register */ +#define TM10BR __SYSREG(0xd400309c, u16) /* timer 10 base register */ +#define TM11BR __SYSREG(0xd400309e, u16) /* timer 11 base register */ +#if defined(CONFIG_AM34_2) +#define TM12BR __SYSREG(0xd4003190, u16) /* timer 12 base register */ +#define TM13BR __SYSREG(0xd4003192, u16) /* timer 13 base register */ +#define TM14BR __SYSREG(0xd4003194, u16) /* timer 14 base register */ +#define TM15BR __SYSREG(0xd4003196, u16) /* timer 15 base register */ +#endif /* CONFIG_AM34_2 */ + +#define TM4BC __SYSREG(0xd40030a0, u16) /* timer 4 binary counter */ +#define TM5BC __SYSREG(0xd40030a2, u16) /* timer 5 binary counter */ +#define TM45BC __SYSREG(0xd40030a0, u32) /* timer 4:5 binary counter */ +#define TM7BC __SYSREG(0xd40030a6, u16) /* timer 7 binary counter */ +#define TM8BC __SYSREG(0xd40030a8, u16) /* timer 8 binary counter */ +#define TM9BC __SYSREG(0xd40030aa, u16) /* timer 9 binary counter */ +#define TM89BC __SYSREG(0xd40030a8, u32) /* timer 8:9 binary counter */ +#define TM10BC __SYSREG(0xd40030ac, u16) /* timer 10 binary counter */ +#define TM11BC __SYSREG(0xd40030ae, u16) /* timer 11 binary counter */ +#if defined(CONFIG_AM34_2) +#define TM12BC __SYSREG(0xd40031a0, u16) /* timer 12 binary counter */ +#define TM13BC __SYSREG(0xd40031a2, u16) /* timer 13 binary counter */ +#define TM14BC __SYSREG(0xd40031a4, u16) /* timer 14 binary counter */ +#define TM15BC __SYSREG(0xd40031a6, u16) /* timer 15 binary counter */ +#endif /* CONFIG_AM34_2 */ + +#define TM4IRQ 6 /* timer 4 IRQ */ +#define TM5IRQ 7 /* timer 5 IRQ */ +#define TM7IRQ 11 /* timer 7 IRQ */ +#define TM8IRQ 12 /* timer 8 IRQ */ +#define TM9IRQ 13 /* timer 9 IRQ */ +#define TM10IRQ 14 /* timer 10 IRQ */ +#define TM11IRQ 15 /* timer 11 IRQ */ +#if defined(CONFIG_AM34_2) +#define TM12IRQ 64 /* timer 12 IRQ */ +#define TM13IRQ 65 /* timer 13 IRQ */ +#define TM14IRQ 66 /* timer 14 IRQ */ +#define TM15IRQ 67 /* timer 15 IRQ */ +#endif /* CONFIG_AM34_2 */ + +#define TM4ICR GxICR(TM4IRQ) /* timer 4 uflow intr ctrl reg */ +#define TM5ICR GxICR(TM5IRQ) /* timer 5 uflow intr ctrl reg */ +#define TM7ICR GxICR(TM7IRQ) /* timer 7 uflow intr ctrl reg */ +#define TM8ICR GxICR(TM8IRQ) /* timer 8 uflow intr ctrl reg */ +#define TM9ICR GxICR(TM9IRQ) /* timer 9 uflow intr ctrl reg */ +#define TM10ICR GxICR(TM10IRQ) /* timer 10 uflow intr ctrl reg */ +#define TM11ICR GxICR(TM11IRQ) /* timer 11 uflow intr ctrl reg */ +#if defined(CONFIG_AM34_2) +#define TM12ICR GxICR(TM12IRQ) /* timer 12 uflow intr ctrl reg */ +#define TM13ICR GxICR(TM13IRQ) /* timer 13 uflow intr ctrl reg */ +#define TM14ICR GxICR(TM14IRQ) /* timer 14 uflow intr ctrl reg */ +#define TM15ICR GxICR(TM15IRQ) /* timer 15 uflow intr ctrl reg */ +#endif /* CONFIG_AM34_2 */ + +/* + * 16-bit timer 6 + */ +#define TM6MD __SYSREG(0xd4003084, u16) /* timer6 mode register */ +#define TM6MD_SRC 0x0007 /* timer source */ +#define TM6MD_SRC_IOCLK 0x0000 /* - IOCLK */ +#define TM6MD_SRC_IOCLK_8 0x0001 /* - 1/8 IOCLK */ +#define TM6MD_SRC_IOCLK_32 0x0002 /* - 1/32 IOCLK */ +#define TM6MD_SRC_TM0UFLOW 0x0004 /* - timer 0 underflow */ +#define TM6MD_SRC_TM1UFLOW 0x0005 /* - timer 1 underflow */ +#define TM6MD_SRC_TM2UFLOW 0x0006 /* - timer 2 underflow */ +#if defined(CONFIG_AM33_2) +/* #define TM6MD_SRC_TM6IOB_BOTH 0x0006 */ /* - TM6IOB pin input (both edges) */ +#define TM6MD_SRC_TM6IOB_SINGLE 0x0007 /* - TM6IOB pin input (single edge) */ +#endif /* CONFIG_AM33_2 */ +#define TM6MD_ONESHOT_ENABLE 0x0040 /* oneshot count */ +#define TM6MD_CLR_ENABLE 0x0010 /* clear count enable */ +#if defined(CONFIG_AM33_2) +#define TM6MD_TRIG_ENABLE 0x0080 /* TM6IOB pin trigger enable */ +#define TM6MD_PWM 0x3800 /* PWM output mode */ +#define TM6MD_PWM_DIS 0x0000 /* - disabled */ +#define TM6MD_PWM_10BIT 0x1000 /* - 10 bits mode */ +#define TM6MD_PWM_11BIT 0x1800 /* - 11 bits mode */ +#define TM6MD_PWM_12BIT 0x3000 /* - 12 bits mode */ +#define TM6MD_PWM_14BIT 0x3800 /* - 14 bits mode */ +#endif /* CONFIG_AM33_2 */ + +#define TM6MD_INIT_COUNTER 0x4000 /* initialize TMnBC to zero */ +#define TM6MD_COUNT_ENABLE 0x8000 /* timer count enable */ + +#define TM6MDA __SYSREG(0xd40030b4, u8) /* timer6 cmp/cap A mode reg */ +#define TM6MDA_MODE_CMP_SINGLE 0x00 /* - compare, single buffer mode */ +#define TM6MDA_MODE_CMP_DOUBLE 0x40 /* - compare, double buffer mode */ +#if defined(CONFIG_AM33_2) +#define TM6MDA_OUT 0x07 /* output select */ +#define TM6MDA_OUT_SETA_RESETB 0x00 /* - set at match A, reset at match B */ +#define TM6MDA_OUT_SETA_RESETOV 0x01 /* - set at match A, reset at overflow */ +#define TM6MDA_OUT_SETA 0x02 /* - set at match A */ +#define TM6MDA_OUT_RESETA 0x03 /* - reset at match A */ +#define TM6MDA_OUT_TOGGLE 0x04 /* - toggle on match A */ +#define TM6MDA_MODE 0xc0 /* compare A register mode */ +#define TM6MDA_MODE_CAP_S_EDGE 0x80 /* - capture, single edge mode */ +#define TM6MDA_MODE_CAP_D_EDGE 0xc0 /* - capture, double edge mode */ +#define TM6MDA_EDGE 0x20 /* compare A edge select */ +#define TM6MDA_EDGE_FALLING 0x00 /* capture on falling edge */ +#define TM6MDA_EDGE_RISING 0x20 /* capture on rising edge */ +#define TM6MDA_CAPTURE_ENABLE 0x10 /* capture enable */ +#else /* !CONFIG_AM33_2 */ +#define TM6MDA_MODE 0x40 /* compare A register mode */ +#endif /* CONFIG_AM33_2 */ + +#define TM6MDB __SYSREG(0xd40030b5, u8) /* timer6 cmp/cap B mode reg */ +#define TM6MDB_MODE_CMP_SINGLE 0x00 /* - compare, single buffer mode */ +#define TM6MDB_MODE_CMP_DOUBLE 0x40 /* - compare, double buffer mode */ +#if defined(CONFIG_AM33_2) +#define TM6MDB_OUT 0x07 /* output select */ +#define TM6MDB_OUT_SETB_RESETA 0x00 /* - set at match B, reset at match A */ +#define TM6MDB_OUT_SETB_RESETOV 0x01 /* - set at match B */ +#define TM6MDB_OUT_RESETB 0x03 /* - reset at match B */ +#define TM6MDB_OUT_TOGGLE 0x04 /* - toggle on match B */ +#define TM6MDB_MODE 0xc0 /* compare B register mode */ +#define TM6MDB_MODE_CAP_S_EDGE 0x80 /* - capture, single edge mode */ +#define TM6MDB_MODE_CAP_D_EDGE 0xc0 /* - capture, double edge mode */ +#define TM6MDB_EDGE 0x20 /* compare B edge select */ +#define TM6MDB_EDGE_FALLING 0x00 /* capture on falling edge */ +#define TM6MDB_EDGE_RISING 0x20 /* capture on rising edge */ +#define TM6MDB_CAPTURE_ENABLE 0x10 /* capture enable */ +#else /* !CONFIG_AM33_2 */ +#define TM6MDB_MODE 0x40 /* compare B register mode */ +#endif /* CONFIG_AM33_2 */ + +#define TM6CA __SYSREG(0xd40030c4, u16) /* timer6 cmp/capture reg A */ +#define TM6CB __SYSREG(0xd40030d4, u16) /* timer6 cmp/capture reg B */ +#define TM6BC __SYSREG(0xd40030a4, u16) /* timer6 binary counter */ + +#define TM6IRQ 6 /* timer 6 IRQ */ +#define TM6AIRQ 9 /* timer 6A IRQ */ +#define TM6BIRQ 10 /* timer 6B IRQ */ + +#define TM6ICR GxICR(TM6IRQ) /* timer 6 uflow intr ctrl reg */ +#define TM6AICR GxICR(TM6AIRQ) /* timer 6A intr control reg */ +#define TM6BICR GxICR(TM6BIRQ) /* timer 6B intr control reg */ + +#if defined(CONFIG_AM34_2) +/* + * MTM: OS Tick-Timer + */ +#define TMTMD __SYSREG(0xd4004100, u8) /* Tick Timer mode register */ +#define TMTMD_TMTLDE 0x40 /* initialize TMTBC = TMTBR */ +#define TMTMD_TMTCNE 0x80 /* timer count enable */ + +#define TMTBR __SYSREG(0xd4004110, u32) /* Tick Timer mode reg */ +#define TMTBC __SYSREG(0xd4004120, u32) /* Tick Timer mode reg */ + +/* + * MTM: OS Timestamp-Timer + */ +#define TMSMD __SYSREG(0xd4004140, u8) /* Tick Timer mode register */ +#define TMSMD_TMSLDE 0x40 /* initialize TMSBC = TMSBR */ +#define TMSMD_TMSCNE 0x80 /* timer count enable */ + +#define TMSBR __SYSREG(0xd4004150, u32) /* Tick Timer mode register */ +#define TMSBC __SYSREG(0xd4004160, u32) /* Tick Timer mode register */ + +#define TMTIRQ 119 /* OS Tick timer IRQ */ +#define TMSIRQ 120 /* Timestamp timer IRQ */ + +#define TMTICR GxICR(TMTIRQ) /* OS Tick timer uflow intr ctrl reg */ +#define TMSICR GxICR(TMSIRQ) /* Timestamp timer uflow intr ctrl reg */ +#endif /* CONFIG_AM34_2 */ + +#endif /* __KERNEL__ */ + +#endif /* _ASM_TIMER_REGS_H */ diff --git a/arch/mn10300/include/asm/timex.h b/arch/mn10300/include/asm/timex.h new file mode 100644 index 00000000..bd4e90df --- /dev/null +++ b/arch/mn10300/include/asm/timex.h @@ -0,0 +1,45 @@ +/* MN10300 Architecture time management specifications + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_TIMEX_H +#define _ASM_TIMEX_H + +#include <asm/hardirq.h> +#include <unit/timex.h> + +#define TICK_SIZE (tick_nsec / 1000) + +#define CLOCK_TICK_RATE MN10300_JCCLK /* Underlying HZ */ + +#ifdef __KERNEL__ + +extern cycles_t cacheflush_time; + +static inline cycles_t get_cycles(void) +{ + return read_timestamp_counter(); +} + +extern int init_clockevents(void); +extern int init_clocksource(void); + +static inline void setup_jiffies_interrupt(int irq, + struct irqaction *action) +{ + u16 tmp; + setup_irq(irq, action); + set_intr_level(irq, NUM2GxICR_LEVEL(CONFIG_TIMER_IRQ_LEVEL)); + GxICR(irq) |= GxICR_ENABLE | GxICR_DETECT | GxICR_REQUEST; + tmp = GxICR(irq); +} + +#endif /* __KERNEL__ */ + +#endif /* _ASM_TIMEX_H */ diff --git a/arch/mn10300/include/asm/tlb.h b/arch/mn10300/include/asm/tlb.h new file mode 100644 index 00000000..65d232b9 --- /dev/null +++ b/arch/mn10300/include/asm/tlb.h @@ -0,0 +1,34 @@ +/* MN10300 TLB definitions + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _ASM_TLB_H +#define _ASM_TLB_H + +#include <asm/tlbflush.h> + +extern void check_pgt_cache(void); + +/* + * we don't need any special per-pte or per-vma handling... + */ +#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) + +/* + * .. because we flush the whole mm when it fills up + */ +#define tlb_flush(tlb) flush_tlb_mm((tlb)->mm) + +/* for now, just use the generic stuff */ +#include <asm-generic/tlb.h> + +#endif /* _ASM_TLB_H */ diff --git a/arch/mn10300/include/asm/tlbflush.h b/arch/mn10300/include/asm/tlbflush.h new file mode 100644 index 00000000..efddd6e1 --- /dev/null +++ b/arch/mn10300/include/asm/tlbflush.h @@ -0,0 +1,154 @@ +/* MN10300 TLB flushing functions + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_TLBFLUSH_H +#define _ASM_TLBFLUSH_H + +#include <linux/mm.h> +#include <asm/processor.h> + +struct tlb_state { + struct mm_struct *active_mm; + int state; +}; +DECLARE_PER_CPU_SHARED_ALIGNED(struct tlb_state, cpu_tlbstate); + +/** + * local_flush_tlb - Flush the current MM's entries from the local CPU's TLBs + */ +static inline void local_flush_tlb(void) +{ + int w; + asm volatile( + " mov %1,%0 \n" + " or %2,%0 \n" + " mov %0,%1 \n" + : "=d"(w) + : "m"(MMUCTR), "i"(MMUCTR_IIV|MMUCTR_DIV) + : "cc", "memory"); +} + +/** + * local_flush_tlb_all - Flush all entries from the local CPU's TLBs + */ +static inline void local_flush_tlb_all(void) +{ + local_flush_tlb(); +} + +/** + * local_flush_tlb_one - Flush one entry from the local CPU's TLBs + */ +static inline void local_flush_tlb_one(unsigned long addr) +{ + local_flush_tlb(); +} + +/** + * local_flush_tlb_page - Flush a page's entry from the local CPU's TLBs + * @mm: The MM to flush for + * @addr: The address of the target page in RAM (not its page struct) + */ +static inline +void local_flush_tlb_page(struct mm_struct *mm, unsigned long addr) +{ + unsigned long pteu, flags, cnx; + + addr &= PAGE_MASK; + + local_irq_save(flags); + + cnx = 1; +#ifdef CONFIG_MN10300_TLB_USE_PIDR + cnx = mm->context.tlbpid[smp_processor_id()]; +#endif + if (cnx) { + pteu = addr; +#ifdef CONFIG_MN10300_TLB_USE_PIDR + pteu |= cnx & xPTEU_PID; +#endif + IPTEU = pteu; + DPTEU = pteu; + if (IPTEL & xPTEL_V) + IPTEL = 0; + if (DPTEL & xPTEL_V) + DPTEL = 0; + } + local_irq_restore(flags); +} + +/* + * TLB flushing: + * + * - flush_tlb() flushes the current mm struct TLBs + * - flush_tlb_all() flushes all processes TLBs + * - flush_tlb_mm(mm) flushes the specified mm context TLB's + * - flush_tlb_page(vma, vmaddr) flushes one page + * - flush_tlb_range(mm, start, end) flushes a range of pages + * - flush_tlb_pgtables(mm, start, end) flushes a range of page tables + */ +#ifdef CONFIG_SMP + +#include <asm/smp.h> + +extern void flush_tlb_all(void); +extern void flush_tlb_current_task(void); +extern void flush_tlb_mm(struct mm_struct *); +extern void flush_tlb_page(struct vm_area_struct *, unsigned long); + +#define flush_tlb() flush_tlb_current_task() + +static inline void flush_tlb_range(struct vm_area_struct *vma, + unsigned long start, unsigned long end) +{ + flush_tlb_mm(vma->vm_mm); +} + +#else /* CONFIG_SMP */ + +static inline void flush_tlb_all(void) +{ + preempt_disable(); + local_flush_tlb_all(); + preempt_enable(); +} + +static inline void flush_tlb_mm(struct mm_struct *mm) +{ + preempt_disable(); + local_flush_tlb_all(); + preempt_enable(); +} + +static inline void flush_tlb_range(struct vm_area_struct *vma, + unsigned long start, unsigned long end) +{ + preempt_disable(); + local_flush_tlb_all(); + preempt_enable(); +} + +#define flush_tlb_page(vma, addr) local_flush_tlb_page((vma)->vm_mm, addr) +#define flush_tlb() flush_tlb_all() + +#endif /* CONFIG_SMP */ + +static inline void flush_tlb_kernel_range(unsigned long start, + unsigned long end) +{ + flush_tlb_all(); +} + +static inline void flush_tlb_pgtables(struct mm_struct *mm, + unsigned long start, unsigned long end) +{ +} + +#endif /* _ASM_TLBFLUSH_H */ diff --git a/arch/mn10300/include/asm/topology.h b/arch/mn10300/include/asm/topology.h new file mode 100644 index 00000000..5428f333 --- /dev/null +++ b/arch/mn10300/include/asm/topology.h @@ -0,0 +1 @@ +#include <asm-generic/topology.h> diff --git a/arch/mn10300/include/asm/types.h b/arch/mn10300/include/asm/types.h new file mode 100644 index 00000000..c1833eb1 --- /dev/null +++ b/arch/mn10300/include/asm/types.h @@ -0,0 +1,31 @@ +/* MN10300 Basic type definitions + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_TYPES_H +#define _ASM_TYPES_H + +#include <asm-generic/int-ll64.h> + +#ifndef __ASSEMBLY__ + +typedef unsigned short umode_t; + +#endif /* __ASSEMBLY__ */ + +/* + * These aren't exported outside the kernel to avoid name space clashes + */ +#ifdef __KERNEL__ + +#define BITS_PER_LONG 32 + +#endif /* __KERNEL__ */ + +#endif /* _ASM_TYPES_H */ diff --git a/arch/mn10300/include/asm/uaccess.h b/arch/mn10300/include/asm/uaccess.h new file mode 100644 index 00000000..780560b3 --- /dev/null +++ b/arch/mn10300/include/asm/uaccess.h @@ -0,0 +1,495 @@ +/* MN10300 userspace access functions + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_UACCESS_H +#define _ASM_UACCESS_H + +/* + * User space memory access functions + */ +#include <linux/thread_info.h> +#include <linux/kernel.h> +#include <asm/page.h> +#include <asm/errno.h> + +#define VERIFY_READ 0 +#define VERIFY_WRITE 1 + +/* + * The fs value determines whether argument validity checking should be + * performed or not. If get_fs() == USER_DS, checking is performed, with + * get_fs() == KERNEL_DS, checking is bypassed. + * + * For historical reasons, these macros are grossly misnamed. + */ +#define MAKE_MM_SEG(s) ((mm_segment_t) { (s) }) + +#define KERNEL_XDS MAKE_MM_SEG(0xBFFFFFFF) +#define KERNEL_DS MAKE_MM_SEG(0x9FFFFFFF) +#define USER_DS MAKE_MM_SEG(TASK_SIZE) + +#define get_ds() (KERNEL_DS) +#define get_fs() (current_thread_info()->addr_limit) +#define set_fs(x) (current_thread_info()->addr_limit = (x)) +#define __kernel_ds_p() (current_thread_info()->addr_limit.seg == 0x9FFFFFFF) + +#define segment_eq(a, b) ((a).seg == (b).seg) + +#define __addr_ok(addr) \ + ((unsigned long)(addr) < (current_thread_info()->addr_limit.seg)) + +/* + * check that a range of addresses falls within the current address limit + */ +static inline int ___range_ok(unsigned long addr, unsigned int size) +{ + int flag = 1, tmp; + + asm(" add %3,%1 \n" /* set C-flag if addr + size > 4Gb */ + " bcs 0f \n" + " cmp %4,%1 \n" /* jump if addr+size>limit (error) */ + " bhi 0f \n" + " clr %0 \n" /* mark okay */ + "0: \n" + : "=r"(flag), "=&r"(tmp) + : "1"(addr), "ir"(size), + "r"(current_thread_info()->addr_limit.seg), "0"(flag) + : "cc" + ); + + return flag; +} + +#define __range_ok(addr, size) ___range_ok((unsigned long)(addr), (u32)(size)) + +#define access_ok(type, addr, size) (__range_ok((addr), (size)) == 0) +#define __access_ok(addr, size) (__range_ok((addr), (size)) == 0) + +static inline int verify_area(int type, const void *addr, unsigned long size) +{ + return access_ok(type, addr, size) ? 0 : -EFAULT; +} + + +/* + * The exception table consists of pairs of addresses: the first is the + * address of an instruction that is allowed to fault, and the second is + * the address at which the program should continue. No registers are + * modified, so it is entirely up to the continuation code to figure out + * what to do. + * + * All the routines below use bits of fixup code that are out of line + * with the main instruction path. This means when everything is well, + * we don't even have to jump over them. Further, they do not intrude + * on our cache or tlb entries. + */ + +struct exception_table_entry +{ + unsigned long insn, fixup; +}; + +/* Returns 0 if exception not found and fixup otherwise. */ +extern int fixup_exception(struct pt_regs *regs); + +#define put_user(x, ptr) __put_user_check((x), (ptr), sizeof(*(ptr))) +#define get_user(x, ptr) __get_user_check((x), (ptr), sizeof(*(ptr))) + +/* + * The "__xxx" versions do not do address space checking, useful when + * doing multiple accesses to the same area (the user has to do the + * checks by hand with "access_ok()") + */ +#define __put_user(x, ptr) __put_user_nocheck((x), (ptr), sizeof(*(ptr))) +#define __get_user(x, ptr) __get_user_nocheck((x), (ptr), sizeof(*(ptr))) + +/* + * The "xxx_ret" versions return constant specified in third argument, if + * something bad happens. These macros can be optimized for the + * case of just returning from the function xxx_ret is used. + */ + +#define put_user_ret(x, ptr, ret) \ + ({ if (put_user((x), (ptr))) return (ret); }) +#define get_user_ret(x, ptr, ret) \ + ({ if (get_user((x), (ptr))) return (ret); }) +#define __put_user_ret(x, ptr, ret) \ + ({ if (__put_user((x), (ptr))) return (ret); }) +#define __get_user_ret(x, ptr, ret) \ + ({ if (__get_user((x), (ptr))) return (ret); }) + +struct __large_struct { unsigned long buf[100]; }; +#define __m(x) (*(struct __large_struct *)(x)) + +#define __get_user_nocheck(x, ptr, size) \ +({ \ + unsigned long __gu_addr; \ + int __gu_err; \ + __gu_addr = (unsigned long) (ptr); \ + switch (size) { \ + case 1: { \ + unsigned char __gu_val; \ + __get_user_asm("bu"); \ + (x) = *(__force __typeof__(*(ptr))*) &__gu_val; \ + break; \ + } \ + case 2: { \ + unsigned short __gu_val; \ + __get_user_asm("hu"); \ + (x) = *(__force __typeof__(*(ptr))*) &__gu_val; \ + break; \ + } \ + case 4: { \ + unsigned int __gu_val; \ + __get_user_asm(""); \ + (x) = *(__force __typeof__(*(ptr))*) &__gu_val; \ + break; \ + } \ + default: \ + __get_user_unknown(); \ + break; \ + } \ + __gu_err; \ +}) + +#define __get_user_check(x, ptr, size) \ +({ \ + const __typeof__(ptr) __guc_ptr = (ptr); \ + int _e; \ + if (likely(__access_ok((unsigned long) __guc_ptr, (size)))) \ + _e = __get_user_nocheck((x), __guc_ptr, (size)); \ + else { \ + _e = -EFAULT; \ + (x) = (__typeof__(x))0; \ + } \ + _e; \ +}) + +#define __get_user_asm(INSN) \ +({ \ + asm volatile( \ + "1:\n" \ + " mov"INSN" %2,%1\n" \ + " mov 0,%0\n" \ + "2:\n" \ + " .section .fixup,\"ax\"\n" \ + "3:\n\t" \ + " mov %3,%0\n" \ + " jmp 2b\n" \ + " .previous\n" \ + " .section __ex_table,\"a\"\n" \ + " .balign 4\n" \ + " .long 1b, 3b\n" \ + " .previous" \ + : "=&r" (__gu_err), "=&r" (__gu_val) \ + : "m" (__m(__gu_addr)), "i" (-EFAULT)); \ +}) + +extern int __get_user_unknown(void); + +#define __put_user_nocheck(x, ptr, size) \ +({ \ + union { \ + __typeof__(*(ptr)) val; \ + u32 bits[2]; \ + } __pu_val; \ + unsigned long __pu_addr; \ + int __pu_err; \ + __pu_val.val = (x); \ + __pu_addr = (unsigned long) (ptr); \ + switch (size) { \ + case 1: __put_user_asm("bu"); break; \ + case 2: __put_user_asm("hu"); break; \ + case 4: __put_user_asm("" ); break; \ + case 8: __put_user_asm8(); break; \ + default: __pu_err = __put_user_unknown(); break; \ + } \ + __pu_err; \ +}) + +#define __put_user_check(x, ptr, size) \ +({ \ + union { \ + __typeof__(*(ptr)) val; \ + u32 bits[2]; \ + } __pu_val; \ + unsigned long __pu_addr; \ + int __pu_err; \ + __pu_val.val = (x); \ + __pu_addr = (unsigned long) (ptr); \ + if (likely(__access_ok(__pu_addr, size))) { \ + switch (size) { \ + case 1: __put_user_asm("bu"); break; \ + case 2: __put_user_asm("hu"); break; \ + case 4: __put_user_asm("" ); break; \ + case 8: __put_user_asm8(); break; \ + default: __pu_err = __put_user_unknown(); break; \ + } \ + } \ + else { \ + __pu_err = -EFAULT; \ + } \ + __pu_err; \ +}) + +#define __put_user_asm(INSN) \ +({ \ + asm volatile( \ + "1:\n" \ + " mov"INSN" %1,%2\n" \ + " mov 0,%0\n" \ + "2:\n" \ + " .section .fixup,\"ax\"\n" \ + "3:\n" \ + " mov %3,%0\n" \ + " jmp 2b\n" \ + " .previous\n" \ + " .section __ex_table,\"a\"\n" \ + " .balign 4\n" \ + " .long 1b, 3b\n" \ + " .previous" \ + : "=&r" (__pu_err) \ + : "r" (__pu_val.val), "m" (__m(__pu_addr)), \ + "i" (-EFAULT) \ + ); \ +}) + +#define __put_user_asm8() \ +({ \ + asm volatile( \ + "1: mov %1,%3 \n" \ + "2: mov %2,%4 \n" \ + " mov 0,%0 \n" \ + "3: \n" \ + " .section .fixup,\"ax\" \n" \ + "4: \n" \ + " mov %5,%0 \n" \ + " jmp 3b \n" \ + " .previous \n" \ + " .section __ex_table,\"a\"\n" \ + " .balign 4 \n" \ + " .long 1b, 4b \n" \ + " .long 2b, 4b \n" \ + " .previous \n" \ + : "=&r" (__pu_err) \ + : "r" (__pu_val.bits[0]), "r" (__pu_val.bits[1]), \ + "m" (__m(__pu_addr)), "m" (__m(__pu_addr+4)), \ + "i" (-EFAULT) \ + ); \ +}) + +extern int __put_user_unknown(void); + + +/* + * Copy To/From Userspace + */ +/* Generic arbitrary sized copy. */ +#define __copy_user(to, from, size) \ +do { \ + if (size) { \ + void *__to = to; \ + const void *__from = from; \ + int w; \ + asm volatile( \ + "0: movbu (%0),%3;\n" \ + "1: movbu %3,(%1);\n" \ + " inc %0;\n" \ + " inc %1;\n" \ + " add -1,%2;\n" \ + " bne 0b;\n" \ + "2:\n" \ + " .section .fixup,\"ax\"\n" \ + "3: jmp 2b\n" \ + " .previous\n" \ + " .section __ex_table,\"a\"\n" \ + " .balign 4\n" \ + " .long 0b,3b\n" \ + " .long 1b,3b\n" \ + " .previous\n" \ + : "=a"(__from), "=a"(__to), "=r"(size), "=&r"(w)\ + : "0"(__from), "1"(__to), "2"(size) \ + : "cc", "memory"); \ + } \ +} while (0) + +#define __copy_user_zeroing(to, from, size) \ +do { \ + if (size) { \ + void *__to = to; \ + const void *__from = from; \ + int w; \ + asm volatile( \ + "0: movbu (%0),%3;\n" \ + "1: movbu %3,(%1);\n" \ + " inc %0;\n" \ + " inc %1;\n" \ + " add -1,%2;\n" \ + " bne 0b;\n" \ + "2:\n" \ + " .section .fixup,\"ax\"\n" \ + "3:\n" \ + " mov %2,%0\n" \ + " clr %3\n" \ + "4: movbu %3,(%1);\n" \ + " inc %1;\n" \ + " add -1,%2;\n" \ + " bne 4b;\n" \ + " mov %0,%2\n" \ + " jmp 2b\n" \ + " .previous\n" \ + " .section __ex_table,\"a\"\n" \ + " .balign 4\n" \ + " .long 0b,3b\n" \ + " .long 1b,3b\n" \ + " .previous\n" \ + : "=a"(__from), "=a"(__to), "=r"(size), "=&r"(w)\ + : "0"(__from), "1"(__to), "2"(size) \ + : "cc", "memory"); \ + } \ +} while (0) + +/* We let the __ versions of copy_from/to_user inline, because they're often + * used in fast paths and have only a small space overhead. + */ +static inline +unsigned long __generic_copy_from_user_nocheck(void *to, const void *from, + unsigned long n) +{ + __copy_user_zeroing(to, from, n); + return n; +} + +static inline +unsigned long __generic_copy_to_user_nocheck(void *to, const void *from, + unsigned long n) +{ + __copy_user(to, from, n); + return n; +} + + +#if 0 +#error "don't use - these macros don't increment to & from pointers" +/* Optimize just a little bit when we know the size of the move. */ +#define __constant_copy_user(to, from, size) \ +do { \ + asm volatile( \ + " mov %0,a0;\n" \ + "0: movbu (%1),d3;\n" \ + "1: movbu d3,(%2);\n" \ + " add -1,a0;\n" \ + " bne 0b;\n" \ + "2:;" \ + ".section .fixup,\"ax\"\n" \ + "3: jmp 2b\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .balign 4\n" \ + " .long 0b,3b\n" \ + " .long 1b,3b\n" \ + ".previous" \ + : \ + : "d"(size), "d"(to), "d"(from) \ + : "d3", "a0"); \ +} while (0) + +/* Optimize just a little bit when we know the size of the move. */ +#define __constant_copy_user_zeroing(to, from, size) \ +do { \ + asm volatile( \ + " mov %0,a0;\n" \ + "0: movbu (%1),d3;\n" \ + "1: movbu d3,(%2);\n" \ + " add -1,a0;\n" \ + " bne 0b;\n" \ + "2:;" \ + ".section .fixup,\"ax\"\n" \ + "3: jmp 2b\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .balign 4\n" \ + " .long 0b,3b\n" \ + " .long 1b,3b\n" \ + ".previous" \ + : \ + : "d"(size), "d"(to), "d"(from) \ + : "d3", "a0"); \ +} while (0) + +static inline +unsigned long __constant_copy_to_user(void *to, const void *from, + unsigned long n) +{ + if (access_ok(VERIFY_WRITE, to, n)) + __constant_copy_user(to, from, n); + return n; +} + +static inline +unsigned long __constant_copy_from_user(void *to, const void *from, + unsigned long n) +{ + if (access_ok(VERIFY_READ, from, n)) + __constant_copy_user_zeroing(to, from, n); + return n; +} + +static inline +unsigned long __constant_copy_to_user_nocheck(void *to, const void *from, + unsigned long n) +{ + __constant_copy_user(to, from, n); + return n; +} + +static inline +unsigned long __constant_copy_from_user_nocheck(void *to, const void *from, + unsigned long n) +{ + __constant_copy_user_zeroing(to, from, n); + return n; +} +#endif + +extern unsigned long __generic_copy_to_user(void __user *, const void *, + unsigned long); +extern unsigned long __generic_copy_from_user(void *, const void __user *, + unsigned long); + +#define __copy_to_user_inatomic(to, from, n) \ + __generic_copy_to_user_nocheck((to), (from), (n)) +#define __copy_from_user_inatomic(to, from, n) \ + __generic_copy_from_user_nocheck((to), (from), (n)) + +#define __copy_to_user(to, from, n) \ +({ \ + might_sleep(); \ + __copy_to_user_inatomic((to), (from), (n)); \ +}) + +#define __copy_from_user(to, from, n) \ +({ \ + might_sleep(); \ + __copy_from_user_inatomic((to), (from), (n)); \ +}) + + +#define copy_to_user(to, from, n) __generic_copy_to_user((to), (from), (n)) +#define copy_from_user(to, from, n) __generic_copy_from_user((to), (from), (n)) + +extern long strncpy_from_user(char *dst, const char __user *src, long count); +extern long __strncpy_from_user(char *dst, const char __user *src, long count); +extern long strnlen_user(const char __user *str, long n); +#define strlen_user(str) strnlen_user(str, ~0UL >> 1) +extern unsigned long clear_user(void __user *mem, unsigned long len); +extern unsigned long __clear_user(void __user *mem, unsigned long len); + +#endif /* _ASM_UACCESS_H */ diff --git a/arch/mn10300/include/asm/ucontext.h b/arch/mn10300/include/asm/ucontext.h new file mode 100644 index 00000000..fcab5c1d --- /dev/null +++ b/arch/mn10300/include/asm/ucontext.h @@ -0,0 +1,22 @@ +/* MN10300 User context + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_UCONTEXT_H +#define _ASM_UCONTEXT_H + +struct ucontext { + unsigned long uc_flags; + struct ucontext *uc_link; + stack_t uc_stack; + struct sigcontext uc_mcontext; + sigset_t uc_sigmask; /* mask last for extensibility */ +}; + +#endif /* _ASM_UCONTEXT_H */ diff --git a/arch/mn10300/include/asm/unaligned.h b/arch/mn10300/include/asm/unaligned.h new file mode 100644 index 00000000..0df67131 --- /dev/null +++ b/arch/mn10300/include/asm/unaligned.h @@ -0,0 +1,20 @@ +/* MN10300 Unaligned memory access handling + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_MN10300_UNALIGNED_H +#define _ASM_MN10300_UNALIGNED_H + +#include <linux/unaligned/access_ok.h> +#include <linux/unaligned/generic.h> + +#define get_unaligned __get_unaligned_le +#define put_unaligned __put_unaligned_le + +#endif /* _ASM_MN10300_UNALIGNED_H */ diff --git a/arch/mn10300/include/asm/unistd.h b/arch/mn10300/include/asm/unistd.h new file mode 100644 index 00000000..9051f921 --- /dev/null +++ b/arch/mn10300/include/asm/unistd.h @@ -0,0 +1,398 @@ +/* MN10300 System call number list + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_UNISTD_H +#define _ASM_UNISTD_H + +#define __NR_restart_syscall 0 +#define __NR_exit 1 +#define __NR_fork 2 +#define __NR_read 3 +#define __NR_write 4 +#define __NR_open 5 +#define __NR_close 6 +#define __NR_waitpid 7 +#define __NR_creat 8 +#define __NR_link 9 +#define __NR_unlink 10 +#define __NR_execve 11 +#define __NR_chdir 12 +#define __NR_time 13 +#define __NR_mknod 14 +#define __NR_chmod 15 +#define __NR_lchown 16 +#define __NR_break 17 +#define __NR_oldstat 18 +#define __NR_lseek 19 +#define __NR_getpid 20 +#define __NR_mount 21 +#define __NR_umount 22 +#define __NR_setuid 23 +#define __NR_getuid 24 +#define __NR_stime 25 +#define __NR_ptrace 26 +#define __NR_alarm 27 +#define __NR_oldfstat 28 +#define __NR_pause 29 +#define __NR_utime 30 +#define __NR_stty 31 +#define __NR_gtty 32 +#define __NR_access 33 +#define __NR_nice 34 +#define __NR_ftime 35 +#define __NR_sync 36 +#define __NR_kill 37 +#define __NR_rename 38 +#define __NR_mkdir 39 +#define __NR_rmdir 40 +#define __NR_dup 41 +#define __NR_pipe 42 +#define __NR_times 43 +#define __NR_prof 44 +#define __NR_brk 45 +#define __NR_setgid 46 +#define __NR_getgid 47 +#define __NR_signal 48 +#define __NR_geteuid 49 +#define __NR_getegid 50 +#define __NR_acct 51 +#define __NR_umount2 52 +#define __NR_lock 53 +#define __NR_ioctl 54 +#define __NR_fcntl 55 +#define __NR_mpx 56 +#define __NR_setpgid 57 +#define __NR_ulimit 58 +#define __NR_oldolduname 59 +#define __NR_umask 60 +#define __NR_chroot 61 +#define __NR_ustat 62 +#define __NR_dup2 63 +#define __NR_getppid 64 +#define __NR_getpgrp 65 +#define __NR_setsid 66 +#define __NR_sigaction 67 +#define __NR_sgetmask 68 +#define __NR_ssetmask 69 +#define __NR_setreuid 70 +#define __NR_setregid 71 +#define __NR_sigsuspend 72 +#define __NR_sigpending 73 +#define __NR_sethostname 74 +#define __NR_setrlimit 75 +#define __NR_getrlimit 76 /* Back compatible 2Gig limited rlimit */ +#define __NR_getrusage 77 +#define __NR_gettimeofday 78 +#define __NR_settimeofday 79 +#define __NR_getgroups 80 +#define __NR_setgroups 81 +#define __NR_select 82 +#define __NR_symlink 83 +#define __NR_oldlstat 84 +#define __NR_readlink 85 +#define __NR_uselib 86 +#define __NR_swapon 87 +#define __NR_reboot 88 +#define __NR_readdir 89 +#define __NR_mmap 90 +#define __NR_munmap 91 +#define __NR_truncate 92 +#define __NR_ftruncate 93 +#define __NR_fchmod 94 +#define __NR_fchown 95 +#define __NR_getpriority 96 +#define __NR_setpriority 97 +#define __NR_profil 98 +#define __NR_statfs 99 +#define __NR_fstatfs 100 +#define __NR_ioperm 101 +#define __NR_socketcall 102 +#define __NR_syslog 103 +#define __NR_setitimer 104 +#define __NR_getitimer 105 +#define __NR_stat 106 +#define __NR_lstat 107 +#define __NR_fstat 108 +#define __NR_olduname 109 +#define __NR_iopl 110 +#define __NR_vhangup 111 +#define __NR_idle 112 +#define __NR_vm86old 113 +#define __NR_wait4 114 +#define __NR_swapoff 115 +#define __NR_sysinfo 116 +#define __NR_ipc 117 +#define __NR_fsync 118 +#define __NR_sigreturn 119 +#define __NR_clone 120 +#define __NR_setdomainname 121 +#define __NR_uname 122 +#define __NR_modify_ldt 123 +#define __NR_adjtimex 124 +#define __NR_mprotect 125 +#define __NR_sigprocmask 126 +#define __NR_create_module 127 +#define __NR_init_module 128 +#define __NR_delete_module 129 +#define __NR_get_kernel_syms 130 +#define __NR_quotactl 131 +#define __NR_getpgid 132 +#define __NR_fchdir 133 +#define __NR_bdflush 134 +#define __NR_sysfs 135 +#define __NR_personality 136 +#define __NR_afs_syscall 137 /* Syscall for Andrew File System */ +#define __NR_setfsuid 138 +#define __NR_setfsgid 139 +#define __NR__llseek 140 +#define __NR_getdents 141 +#define __NR__newselect 142 +#define __NR_flock 143 +#define __NR_msync 144 +#define __NR_readv 145 +#define __NR_writev 146 +#define __NR_getsid 147 +#define __NR_fdatasync 148 +#define __NR__sysctl 149 +#define __NR_mlock 150 +#define __NR_munlock 151 +#define __NR_mlockall 152 +#define __NR_munlockall 153 +#define __NR_sched_setparam 154 +#define __NR_sched_getparam 155 +#define __NR_sched_setscheduler 156 +#define __NR_sched_getscheduler 157 +#define __NR_sched_yield 158 +#define __NR_sched_get_priority_max 159 +#define __NR_sched_get_priority_min 160 +#define __NR_sched_rr_get_interval 161 +#define __NR_nanosleep 162 +#define __NR_mremap 163 +#define __NR_setresuid 164 +#define __NR_getresuid 165 +#define __NR_vm86 166 +#define __NR_query_module 167 +#define __NR_poll 168 +#define __NR_nfsservctl 169 +#define __NR_setresgid 170 +#define __NR_getresgid 171 +#define __NR_prctl 172 +#define __NR_rt_sigreturn 173 +#define __NR_rt_sigaction 174 +#define __NR_rt_sigprocmask 175 +#define __NR_rt_sigpending 176 +#define __NR_rt_sigtimedwait 177 +#define __NR_rt_sigqueueinfo 178 +#define __NR_rt_sigsuspend 179 +#define __NR_pread64 180 +#define __NR_pwrite64 181 +#define __NR_chown 182 +#define __NR_getcwd 183 +#define __NR_capget 184 +#define __NR_capset 185 +#define __NR_sigaltstack 186 +#define __NR_sendfile 187 +#define __NR_getpmsg 188 /* some people actually want streams */ +#define __NR_putpmsg 189 /* some people actually want streams */ +#define __NR_vfork 190 +#define __NR_ugetrlimit 191 /* SuS compliant getrlimit */ +#define __NR_mmap2 192 +#define __NR_truncate64 193 +#define __NR_ftruncate64 194 +#define __NR_stat64 195 +#define __NR_lstat64 196 +#define __NR_fstat64 197 +#define __NR_lchown32 198 +#define __NR_getuid32 199 +#define __NR_getgid32 200 +#define __NR_geteuid32 201 +#define __NR_getegid32 202 +#define __NR_setreuid32 203 +#define __NR_setregid32 204 +#define __NR_getgroups32 205 +#define __NR_setgroups32 206 +#define __NR_fchown32 207 +#define __NR_setresuid32 208 +#define __NR_getresuid32 209 +#define __NR_setresgid32 210 +#define __NR_getresgid32 211 +#define __NR_chown32 212 +#define __NR_setuid32 213 +#define __NR_setgid32 214 +#define __NR_setfsuid32 215 +#define __NR_setfsgid32 216 +#define __NR_pivot_root 217 +#define __NR_mincore 218 +#define __NR_madvise 219 +#define __NR_madvise1 219 /* delete when C lib stub is removed */ +#define __NR_getdents64 220 +#define __NR_fcntl64 221 +/* 223 is unused */ +#define __NR_gettid 224 +#define __NR_readahead 225 +#define __NR_setxattr 226 +#define __NR_lsetxattr 227 +#define __NR_fsetxattr 228 +#define __NR_getxattr 229 +#define __NR_lgetxattr 230 +#define __NR_fgetxattr 231 +#define __NR_listxattr 232 +#define __NR_llistxattr 233 +#define __NR_flistxattr 234 +#define __NR_removexattr 235 +#define __NR_lremovexattr 236 +#define __NR_fremovexattr 237 +#define __NR_tkill 238 +#define __NR_sendfile64 239 +#define __NR_futex 240 +#define __NR_sched_setaffinity 241 +#define __NR_sched_getaffinity 242 +#define __NR_set_thread_area 243 +#define __NR_get_thread_area 244 +#define __NR_io_setup 245 +#define __NR_io_destroy 246 +#define __NR_io_getevents 247 +#define __NR_io_submit 248 +#define __NR_io_cancel 249 +#define __NR_fadvise64 250 + +#define __NR_exit_group 252 +#define __NR_lookup_dcookie 253 +#define __NR_epoll_create 254 +#define __NR_epoll_ctl 255 +#define __NR_epoll_wait 256 +#define __NR_remap_file_pages 257 +#define __NR_set_tid_address 258 +#define __NR_timer_create 259 +#define __NR_timer_settime (__NR_timer_create+1) +#define __NR_timer_gettime (__NR_timer_create+2) +#define __NR_timer_getoverrun (__NR_timer_create+3) +#define __NR_timer_delete (__NR_timer_create+4) +#define __NR_clock_settime (__NR_timer_create+5) +#define __NR_clock_gettime (__NR_timer_create+6) +#define __NR_clock_getres (__NR_timer_create+7) +#define __NR_clock_nanosleep (__NR_timer_create+8) +#define __NR_statfs64 268 +#define __NR_fstatfs64 269 +#define __NR_tgkill 270 +#define __NR_utimes 271 +#define __NR_fadvise64_64 272 +#define __NR_vserver 273 +#define __NR_mbind 274 +#define __NR_get_mempolicy 275 +#define __NR_set_mempolicy 276 +#define __NR_mq_open 277 +#define __NR_mq_unlink (__NR_mq_open+1) +#define __NR_mq_timedsend (__NR_mq_open+2) +#define __NR_mq_timedreceive (__NR_mq_open+3) +#define __NR_mq_notify (__NR_mq_open+4) +#define __NR_mq_getsetattr (__NR_mq_open+5) +#define __NR_kexec_load 283 +#define __NR_waitid 284 +#define __NR_add_key 286 +#define __NR_request_key 287 +#define __NR_keyctl 288 +#define __NR_cacheflush 289 +#define __NR_ioprio_set 290 +#define __NR_ioprio_get 291 +#define __NR_inotify_init 292 +#define __NR_inotify_add_watch 293 +#define __NR_inotify_rm_watch 294 +#define __NR_migrate_pages 295 +#define __NR_openat 296 +#define __NR_mkdirat 297 +#define __NR_mknodat 298 +#define __NR_fchownat 299 +#define __NR_futimesat 300 +#define __NR_fstatat64 301 +#define __NR_unlinkat 302 +#define __NR_renameat 303 +#define __NR_linkat 304 +#define __NR_symlinkat 305 +#define __NR_readlinkat 306 +#define __NR_fchmodat 307 +#define __NR_faccessat 308 +#define __NR_pselect6 309 +#define __NR_ppoll 310 +#define __NR_unshare 311 +#define __NR_set_robust_list 312 +#define __NR_get_robust_list 313 +#define __NR_splice 314 +#define __NR_sync_file_range 315 +#define __NR_tee 316 +#define __NR_vmsplice 317 +#define __NR_move_pages 318 +#define __NR_getcpu 319 +#define __NR_epoll_pwait 320 +#define __NR_utimensat 321 +#define __NR_signalfd 322 +#define __NR_timerfd_create 323 +#define __NR_eventfd 324 +#define __NR_fallocate 325 +#define __NR_timerfd_settime 326 +#define __NR_timerfd_gettime 327 +#define __NR_signalfd4 328 +#define __NR_eventfd2 329 +#define __NR_epoll_create1 330 +#define __NR_dup3 331 +#define __NR_pipe2 332 +#define __NR_inotify_init1 333 +#define __NR_preadv 334 +#define __NR_pwritev 335 +#define __NR_rt_tgsigqueueinfo 336 +#define __NR_perf_event_open 337 +#define __NR_recvmmsg 338 +#define __NR_setns 339 + +#ifdef __KERNEL__ + +#define NR_syscalls 340 + +/* + * specify the deprecated syscalls we want to support on this arch + */ +#define __ARCH_WANT_IPC_PARSE_VERSION +#define __ARCH_WANT_OLD_READDIR +#define __ARCH_WANT_OLD_STAT +#define __ARCH_WANT_STAT64 +#define __ARCH_WANT_SYS_ALARM +#define __ARCH_WANT_SYS_GETHOSTNAME +#define __ARCH_WANT_SYS_IPC +#define __ARCH_WANT_SYS_PAUSE +#define __ARCH_WANT_SYS_SGETMASK +#define __ARCH_WANT_SYS_SIGNAL +#define __ARCH_WANT_SYS_TIME +#define __ARCH_WANT_SYS_UTIME +#define __ARCH_WANT_SYS_WAITPID +#define __ARCH_WANT_SYS_SOCKETCALL +#define __ARCH_WANT_SYS_FADVISE64 +#define __ARCH_WANT_SYS_GETPGRP +#define __ARCH_WANT_SYS_LLSEEK +#define __ARCH_WANT_SYS_NICE +#define __ARCH_WANT_SYS_OLD_GETRLIMIT +#define __ARCH_WANT_SYS_OLD_SELECT +#define __ARCH_WANT_SYS_OLDUMOUNT +#define __ARCH_WANT_SYS_SIGPENDING +#define __ARCH_WANT_SYS_SIGPROCMASK +#define __ARCH_WANT_SYS_RT_SIGACTION +#define __ARCH_WANT_SYS_RT_SIGSUSPEND + +/* + * "Conditional" syscalls + * + * What we want is __attribute__((weak,alias("sys_ni_syscall"))), + * but it doesn't work on all toolchains, so we just do it by hand + */ +#ifndef cond_syscall +#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall"); +#endif + +#endif /* __KERNEL__ */ +#endif /* _ASM_UNISTD_H */ diff --git a/arch/mn10300/include/asm/user.h b/arch/mn10300/include/asm/user.h new file mode 100644 index 00000000..e1193908 --- /dev/null +++ b/arch/mn10300/include/asm/user.h @@ -0,0 +1,53 @@ +/* MN10300 User process data + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_USER_H +#define _ASM_USER_H + +#include <asm/page.h> +#include <linux/ptrace.h> + +#ifndef __ASSEMBLY__ +/* + * When the kernel dumps core, it starts by dumping the user struct - this will + * be used by gdb to figure out where the data and stack segments are within + * the file, and what virtual addresses to use. + */ +struct user { + /* We start with the registers, to mimic the way that "memory" is + * returned from the ptrace(3,...) function. + */ + struct pt_regs regs; /* Where the registers are actually stored */ + + /* The rest of this junk is to help gdb figure out what goes where */ + unsigned long int u_tsize; /* Text segment size (pages). */ + unsigned long int u_dsize; /* Data segment size (pages). */ + unsigned long int u_ssize; /* Stack segment size (pages). */ + unsigned long start_code; /* Starting virtual address of text. */ + unsigned long start_stack; /* Starting virtual address of stack area. + This is actually the bottom of the stack, + the top of the stack is always found in the + esp register. */ + long int signal; /* Signal that caused the core dump. */ + int reserved; /* No longer used */ + struct user_pt_regs *u_ar0; /* Used by gdb to help find the values for */ + + /* the registers */ + unsigned long magic; /* To uniquely identify a core file */ + char u_comm[32]; /* User command that was responsible */ +}; +#endif + +#define NBPG PAGE_SIZE +#define UPAGES 1 +#define HOST_TEXT_START_ADDR +(u.start_code) +#define HOST_STACK_END_ADDR +(u.start_stack + u.u_ssize * NBPG) + +#endif /* _ASM_USER_H */ diff --git a/arch/mn10300/include/asm/vga.h b/arch/mn10300/include/asm/vga.h new file mode 100644 index 00000000..0163e50a --- /dev/null +++ b/arch/mn10300/include/asm/vga.h @@ -0,0 +1,17 @@ +/* MN10300 VGA register definitions + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _ASM_VGA_H +#define _ASM_VGA_H + + + +#endif /* _ASM_VGA_H */ diff --git a/arch/mn10300/include/asm/xor.h b/arch/mn10300/include/asm/xor.h new file mode 100644 index 00000000..c82eb12a --- /dev/null +++ b/arch/mn10300/include/asm/xor.h @@ -0,0 +1 @@ +#include <asm-generic/xor.h> diff --git a/arch/mn10300/kernel/Makefile b/arch/mn10300/kernel/Makefile new file mode 100644 index 00000000..47ed30fe --- /dev/null +++ b/arch/mn10300/kernel/Makefile @@ -0,0 +1,28 @@ +# +# Makefile for the MN10300-specific core kernel code +# +extra-y := head.o init_task.o vmlinux.lds + +fpu-obj-y := fpu-nofpu.o fpu-nofpu-low.o +fpu-obj-$(CONFIG_FPU) := fpu.o fpu-low.o + +obj-y := process.o signal.o entry.o traps.o irq.o \ + ptrace.o setup.o time.o sys_mn10300.o io.o kthread.o \ + switch_to.o mn10300_ksyms.o kernel_execve.o $(fpu-obj-y) \ + csrc-mn10300.o cevt-mn10300.o + +obj-$(CONFIG_SMP) += smp.o smp-low.o + +obj-$(CONFIG_MN10300_WD_TIMER) += mn10300-watchdog.o mn10300-watchdog-low.o + +obj-$(CONFIG_MN10300_TTYSM) += mn10300-serial.o mn10300-serial-low.o \ + mn10300-debug.o +obj-$(CONFIG_GDBSTUB) += gdb-stub.o gdb-low.o +obj-$(CONFIG_GDBSTUB_ON_TTYSx) += gdb-io-serial.o gdb-io-serial-low.o +obj-$(CONFIG_GDBSTUB_ON_TTYSMx) += gdb-io-ttysm.o gdb-io-ttysm-low.o + +obj-$(CONFIG_MN10300_RTC) += rtc.o +obj-$(CONFIG_PROFILE) += profile.o profile-low.o +obj-$(CONFIG_MODULES) += module.o +obj-$(CONFIG_KPROBES) += kprobes.o +obj-$(CONFIG_KGDB) += kgdb.o diff --git a/arch/mn10300/kernel/asm-offsets.c b/arch/mn10300/kernel/asm-offsets.c new file mode 100644 index 00000000..96f24fab --- /dev/null +++ b/arch/mn10300/kernel/asm-offsets.c @@ -0,0 +1,110 @@ +/* + * Generate definitions needed by assembly language modules. + * This code generates raw asm output which is post-processed + * to extract and format the required data. + */ + +#include <linux/sched.h> +#include <linux/signal.h> +#include <linux/personality.h> +#include <linux/kbuild.h> +#include <asm/ucontext.h> +#include <asm/processor.h> +#include <asm/thread_info.h> +#include <asm/ptrace.h> +#include "sigframe.h" +#include "mn10300-serial.h" + +void foo(void) +{ + OFFSET(SIGCONTEXT_d0, sigcontext, d0); + OFFSET(SIGCONTEXT_d1, sigcontext, d1); + BLANK(); + + OFFSET(TI_task, thread_info, task); + OFFSET(TI_exec_domain, thread_info, exec_domain); + OFFSET(TI_frame, thread_info, frame); + OFFSET(TI_flags, thread_info, flags); + OFFSET(TI_cpu, thread_info, cpu); + OFFSET(TI_preempt_count, thread_info, preempt_count); + OFFSET(TI_addr_limit, thread_info, addr_limit); + OFFSET(TI_restart_block, thread_info, restart_block); + BLANK(); + + OFFSET(REG_D0, pt_regs, d0); + OFFSET(REG_D1, pt_regs, d1); + OFFSET(REG_D2, pt_regs, d2); + OFFSET(REG_D3, pt_regs, d3); + OFFSET(REG_A0, pt_regs, a0); + OFFSET(REG_A1, pt_regs, a1); + OFFSET(REG_A2, pt_regs, a2); + OFFSET(REG_A3, pt_regs, a3); + OFFSET(REG_E0, pt_regs, e0); + OFFSET(REG_E1, pt_regs, e1); + OFFSET(REG_E2, pt_regs, e2); + OFFSET(REG_E3, pt_regs, e3); + OFFSET(REG_E4, pt_regs, e4); + OFFSET(REG_E5, pt_regs, e5); + OFFSET(REG_E6, pt_regs, e6); + OFFSET(REG_E7, pt_regs, e7); + OFFSET(REG_SP, pt_regs, sp); + OFFSET(REG_EPSW, pt_regs, epsw); + OFFSET(REG_PC, pt_regs, pc); + OFFSET(REG_LAR, pt_regs, lar); + OFFSET(REG_LIR, pt_regs, lir); + OFFSET(REG_MDR, pt_regs, mdr); + OFFSET(REG_MCVF, pt_regs, mcvf); + OFFSET(REG_MCRL, pt_regs, mcrl); + OFFSET(REG_MCRH, pt_regs, mcrh); + OFFSET(REG_MDRQ, pt_regs, mdrq); + OFFSET(REG_ORIG_D0, pt_regs, orig_d0); + OFFSET(REG_NEXT, pt_regs, next); + DEFINE(REG__END, sizeof(struct pt_regs)); + BLANK(); + + OFFSET(THREAD_UREGS, thread_struct, uregs); + OFFSET(THREAD_PC, thread_struct, pc); + OFFSET(THREAD_SP, thread_struct, sp); + OFFSET(THREAD_A3, thread_struct, a3); + OFFSET(THREAD_USP, thread_struct, usp); +#ifdef CONFIG_FPU + OFFSET(THREAD_FPU_FLAGS, thread_struct, fpu_flags); + OFFSET(THREAD_FPU_STATE, thread_struct, fpu_state); + DEFINE(__THREAD_USING_FPU, THREAD_USING_FPU); + DEFINE(__THREAD_HAS_FPU, THREAD_HAS_FPU); +#endif /* CONFIG_FPU */ + BLANK(); + + OFFSET(TASK_THREAD, task_struct, thread); + BLANK(); + + DEFINE(CLONE_VM_asm, CLONE_VM); + DEFINE(CLONE_FS_asm, CLONE_FS); + DEFINE(CLONE_FILES_asm, CLONE_FILES); + DEFINE(CLONE_SIGHAND_asm, CLONE_SIGHAND); + DEFINE(CLONE_UNTRACED_asm, CLONE_UNTRACED); + DEFINE(SIGCHLD_asm, SIGCHLD); + BLANK(); + + OFFSET(EXEC_DOMAIN_handler, exec_domain, handler); + OFFSET(RT_SIGFRAME_sigcontext, rt_sigframe, uc.uc_mcontext); + + DEFINE(PAGE_SIZE_asm, PAGE_SIZE); + + OFFSET(__rx_buffer, mn10300_serial_port, rx_buffer); + OFFSET(__rx_inp, mn10300_serial_port, rx_inp); + OFFSET(__rx_outp, mn10300_serial_port, rx_outp); + OFFSET(__uart_state, mn10300_serial_port, uart.state); + OFFSET(__tx_xchar, mn10300_serial_port, tx_xchar); + OFFSET(__tx_break, mn10300_serial_port, tx_break); + OFFSET(__intr_flags, mn10300_serial_port, intr_flags); + OFFSET(__rx_icr, mn10300_serial_port, rx_icr); + OFFSET(__tx_icr, mn10300_serial_port, tx_icr); + OFFSET(__tm_icr, mn10300_serial_port, _tmicr); + OFFSET(__iobase, mn10300_serial_port, _iobase); + + DEFINE(__UART_XMIT_SIZE, UART_XMIT_SIZE); + OFFSET(__xmit_buffer, uart_state, xmit.buf); + OFFSET(__xmit_head, uart_state, xmit.head); + OFFSET(__xmit_tail, uart_state, xmit.tail); +} diff --git a/arch/mn10300/kernel/cevt-mn10300.c b/arch/mn10300/kernel/cevt-mn10300.c new file mode 100644 index 00000000..69cae026 --- /dev/null +++ b/arch/mn10300/kernel/cevt-mn10300.c @@ -0,0 +1,132 @@ +/* MN10300 clockevents + * + * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved. + * Written by Mark Salter (msalter@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/clockchips.h> +#include <linux/interrupt.h> +#include <linux/percpu.h> +#include <linux/smp.h> +#include <asm/timex.h> +#include "internal.h" + +#ifdef CONFIG_SMP +#if (CONFIG_NR_CPUS > 2) && !defined(CONFIG_GEENERIC_CLOCKEVENTS_BROADCAST) +#error "This doesn't scale well! Need per-core local timers." +#endif +#else /* CONFIG_SMP */ +#define stop_jiffies_counter1() +#define reload_jiffies_counter1(x) +#define TMJC1IRQ TMJCIRQ +#endif + + +static int next_event(unsigned long delta, + struct clock_event_device *evt) +{ + unsigned int cpu = smp_processor_id(); + + if (cpu == 0) { + stop_jiffies_counter(); + reload_jiffies_counter(delta - 1); + } else { + stop_jiffies_counter1(); + reload_jiffies_counter1(delta - 1); + } + return 0; +} + +static void set_clock_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + /* Nothing to do ... */ +} + +static DEFINE_PER_CPU(struct clock_event_device, mn10300_clockevent_device); +static DEFINE_PER_CPU(struct irqaction, timer_irq); + +static irqreturn_t timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *cd; + unsigned int cpu = smp_processor_id(); + + if (cpu == 0) + stop_jiffies_counter(); + else + stop_jiffies_counter1(); + + cd = &per_cpu(mn10300_clockevent_device, cpu); + cd->event_handler(cd); + + return IRQ_HANDLED; +} + +static void event_handler(struct clock_event_device *dev) +{ +} + +int __init init_clockevents(void) +{ + struct clock_event_device *cd; + struct irqaction *iact; + unsigned int cpu = smp_processor_id(); + + cd = &per_cpu(mn10300_clockevent_device, cpu); + + if (cpu == 0) { + stop_jiffies_counter(); + cd->irq = TMJCIRQ; + } else { + stop_jiffies_counter1(); + cd->irq = TMJC1IRQ; + } + + cd->name = "Timestamp"; + cd->features = CLOCK_EVT_FEAT_ONESHOT; + + /* Calculate shift/mult. We want to spawn at least 1 second */ + clockevents_calc_mult_shift(cd, MN10300_JCCLK, 1); + + /* Calculate the min / max delta */ + cd->max_delta_ns = clockevent_delta2ns(TMJCBR_MAX, cd); + cd->min_delta_ns = clockevent_delta2ns(100, cd); + + cd->rating = 200; + cd->cpumask = cpumask_of(smp_processor_id()); + cd->set_mode = set_clock_mode; + cd->event_handler = event_handler; + cd->set_next_event = next_event; + + iact = &per_cpu(timer_irq, cpu); + iact->flags = IRQF_DISABLED | IRQF_SHARED | IRQF_TIMER; + iact->handler = timer_interrupt; + + clockevents_register_device(cd); + +#if defined(CONFIG_SMP) && !defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) + /* setup timer irq affinity so it only runs on this cpu */ + { + struct irq_data *data; + data = irq_get_irq_data(cd->irq); + cpumask_copy(data->affinity, cpumask_of(cpu)); + iact->flags |= IRQF_NOBALANCING; + } +#endif + + if (cpu == 0) { + reload_jiffies_counter(MN10300_JC_PER_HZ - 1); + iact->name = "CPU0 Timer"; + } else { + reload_jiffies_counter1(MN10300_JC_PER_HZ - 1); + iact->name = "CPU1 Timer"; + } + + setup_jiffies_interrupt(cd->irq, iact); + + return 0; +} diff --git a/arch/mn10300/kernel/csrc-mn10300.c b/arch/mn10300/kernel/csrc-mn10300.c new file mode 100644 index 00000000..45644cf1 --- /dev/null +++ b/arch/mn10300/kernel/csrc-mn10300.c @@ -0,0 +1,34 @@ +/* MN10300 clocksource + * + * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved. + * Written by Mark Salter (msalter@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/clocksource.h> +#include <linux/init.h> +#include <asm/timex.h> +#include "internal.h" + +static cycle_t mn10300_read(struct clocksource *cs) +{ + return read_timestamp_counter(); +} + +static struct clocksource clocksource_mn10300 = { + .name = "TSC", + .rating = 200, + .read = mn10300_read, + .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +int __init init_clocksource(void) +{ + startup_timestamp_counter(); + clocksource_register_hz(&clocksource_mn10300, MN10300_TSCCLK); + return 0; +} diff --git a/arch/mn10300/kernel/entry.S b/arch/mn10300/kernel/entry.S new file mode 100644 index 00000000..ae435e1d --- /dev/null +++ b/arch/mn10300/kernel/entry.S @@ -0,0 +1,765 @@ +############################################################################### +# +# MN10300 Exception and interrupt entry points +# +# Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Modified by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/system.h> +#include <asm/irqflags.h> +#include <asm/thread_info.h> +#include <asm/intctl-regs.h> +#include <asm/busctl-regs.h> +#include <asm/timer-regs.h> +#include <unit/leds.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/errno.h> +#include <asm/asm-offsets.h> +#include <asm/frame.inc> + +#if defined(CONFIG_SMP) && defined(CONFIG_GDBSTUB) +#include <asm/gdb-stub.h> +#endif /* CONFIG_SMP && CONFIG_GDBSTUB */ + +#ifdef CONFIG_PREEMPT +#define preempt_stop LOCAL_IRQ_DISABLE +#else +#define preempt_stop +#define resume_kernel restore_all +#endif + + .am33_2 + +############################################################################### +# +# the return path for a forked child +# - on entry, D0 holds the address of the previous task to run +# +############################################################################### +ENTRY(ret_from_fork) + call schedule_tail[],0 + GET_THREAD_INFO a2 + + # return 0 to indicate child process + clr d0 + mov d0,(REG_D0,fp) + jmp syscall_exit + +############################################################################### +# +# system call handler +# +############################################################################### +ENTRY(system_call) + add -4,sp + SAVE_ALL + mov d0,(REG_ORIG_D0,fp) + GET_THREAD_INFO a2 + cmp nr_syscalls,d0 + bcc syscall_badsys + btst _TIF_SYSCALL_TRACE,(TI_flags,a2) + bne syscall_entry_trace +syscall_call: + add d0,d0,a1 + add a1,a1 + mov (REG_A0,fp),d0 + mov (sys_call_table,a1),a0 + calls (a0) + mov d0,(REG_D0,fp) +syscall_exit: + # make sure we don't miss an interrupt setting need_resched or + # sigpending between sampling and the rti + LOCAL_IRQ_DISABLE + mov (TI_flags,a2),d2 + btst _TIF_ALLWORK_MASK,d2 + bne syscall_exit_work +restore_all: + RESTORE_ALL + +############################################################################### +# +# perform work that needs to be done immediately before resumption and syscall +# tracing +# +############################################################################### + ALIGN +syscall_exit_work: + btst _TIF_SYSCALL_TRACE,d2 + beq work_pending + LOCAL_IRQ_ENABLE # could let syscall_trace_exit() call + # schedule() instead + mov fp,d0 + call syscall_trace_exit[],0 # do_syscall_trace(regs) + jmp resume_userspace + + ALIGN +work_pending: + btst _TIF_NEED_RESCHED,d2 + beq work_notifysig + +work_resched: + call schedule[],0 + + # make sure we don't miss an interrupt setting need_resched or + # sigpending between sampling and the rti + LOCAL_IRQ_DISABLE + + # is there any work to be done other than syscall tracing? + mov (TI_flags,a2),d2 + btst _TIF_WORK_MASK,d2 + beq restore_all + btst _TIF_NEED_RESCHED,d2 + bne work_resched + + # deal with pending signals and notify-resume requests +work_notifysig: + mov fp,d0 + mov d2,d1 + call do_notify_resume[],0 + jmp resume_userspace + + # perform syscall entry tracing +syscall_entry_trace: + mov -ENOSYS,d0 + mov d0,(REG_D0,fp) + mov fp,d0 + call syscall_trace_entry[],0 # returns the syscall number to actually use + mov (REG_D1,fp),d1 + cmp nr_syscalls,d0 + bcs syscall_call + jmp syscall_exit + +syscall_badsys: + mov -ENOSYS,d0 + mov d0,(REG_D0,fp) + jmp resume_userspace + + # userspace resumption stub bypassing syscall exit tracing + .globl ret_from_exception, ret_from_intr + ALIGN +ret_from_exception: + preempt_stop +ret_from_intr: + GET_THREAD_INFO a2 + mov (REG_EPSW,fp),d0 # need to deliver signals before + # returning to userspace + and EPSW_nSL,d0 + beq resume_kernel # returning to supervisor mode + +ENTRY(resume_userspace) + # make sure we don't miss an interrupt setting need_resched or + # sigpending between sampling and the rti + LOCAL_IRQ_DISABLE + + # is there any work to be done on int/exception return? + mov (TI_flags,a2),d2 + btst _TIF_WORK_MASK,d2 + bne work_pending + jmp restore_all + +#ifdef CONFIG_PREEMPT +ENTRY(resume_kernel) + LOCAL_IRQ_DISABLE + mov (TI_preempt_count,a2),d0 # non-zero preempt_count ? + cmp 0,d0 + bne restore_all + +need_resched: + btst _TIF_NEED_RESCHED,(TI_flags,a2) + beq restore_all + mov (REG_EPSW,fp),d0 + and EPSW_IM,d0 + cmp EPSW_IM_7,d0 # interrupts off (exception path) ? + bne restore_all + call preempt_schedule_irq[],0 + jmp need_resched +#endif + + +############################################################################### +# +# IRQ handler entry point +# - intended to be entered at multiple priorities +# +############################################################################### +ENTRY(irq_handler) + add -4,sp + SAVE_ALL + + # it's not a syscall + mov 0xffffffff,d0 + mov d0,(REG_ORIG_D0,fp) + + mov fp,d0 + call do_IRQ[],0 # do_IRQ(regs) + + jmp ret_from_intr + +############################################################################### +# +# Double Fault handler entry point +# - note that there will not be a stack, D0/A0 will hold EPSW/PC as were +# +############################################################################### + .section .bss + .balign THREAD_SIZE + .space THREAD_SIZE +__df_stack: + .previous + +ENTRY(double_fault) + mov a0,(__df_stack-4) # PC as was + mov d0,(__df_stack-8) # EPSW as was + mn10300_set_dbfleds # display 'db-f' on the LEDs + mov 0xaa55aa55,d0 + mov d0,(__df_stack-12) # no ORIG_D0 + mov sp,a0 # save corrupted SP + mov __df_stack-12,sp # emergency supervisor stack + SAVE_ALL + mov a0,(REG_A0,fp) # save corrupted SP as A0 (which got + # clobbered by the CPU) + mov fp,d0 + calls do_double_fault +double_fault_loop: + bra double_fault_loop + +############################################################################### +# +# Bus Error handler entry point +# - handle external (async) bus errors separately +# +############################################################################### +ENTRY(raw_bus_error) + add -4,sp + mov d0,(sp) +#if defined(CONFIG_ERRATUM_NEED_TO_RELOAD_MMUCTR) + mov (MMUCTR),d0 + mov d0,(MMUCTR) +#endif + mov (BCBERR),d0 # what + btst BCBERR_BEMR_DMA,d0 # see if it was an external bus error + beq __common_exception_aux # it wasn't + + SAVE_ALL + mov (BCBEAR),d1 # destination of erroneous access + + mov (REG_ORIG_D0,fp),d2 + mov d2,(REG_D0,fp) + mov -1,d2 + mov d2,(REG_ORIG_D0,fp) + + add -4,sp + mov fp,(12,sp) # frame pointer + call io_bus_error[],0 + jmp restore_all + +############################################################################### +# +# NMI exception entry points +# +# This is used by ordinary interrupt channels that have the GxICR_NMI bit set +# in addition to the main NMI and Watchdog channels. SMP NMI IPIs use this +# facility. +# +############################################################################### +ENTRY(nmi_handler) + add -4,sp + mov d0,(sp) + mov (TBR),d0 + +#ifdef CONFIG_SMP + add -4,sp + mov d0,(sp) # save d0(TBR) + movhu (NMIAGR),d0 + and NMIAGR_GN,d0 + lsr 0x2,d0 + cmp CALL_FUNCTION_NMI_IPI,d0 + bne nmi_not_smp_callfunc # if not call function, jump + + # function call nmi ipi + add 4,sp # no need to store TBR + mov GxICR_DETECT,d0 # clear NMI request + movbu d0,(GxICR(CALL_FUNCTION_NMI_IPI)) + movhu (GxICR(CALL_FUNCTION_NMI_IPI)),d0 + and ~EPSW_NMID,epsw # enable NMI + + mov (sp),d0 # restore d0 + SAVE_ALL + call smp_nmi_call_function_interrupt[],0 + RESTORE_ALL + +nmi_not_smp_callfunc: +#ifdef CONFIG_KERNEL_DEBUGGER + cmp DEBUGGER_NMI_IPI,d0 + bne nmi_not_debugger # if not kernel debugger NMI IPI, jump + + # kernel debugger NMI IPI + add 4,sp # no need to store TBR + mov GxICR_DETECT,d0 # clear NMI + movbu d0,(GxICR(DEBUGGER_NMI_IPI)) + movhu (GxICR(DEBUGGER_NMI_IPI)),d0 + and ~EPSW_NMID,epsw # enable NMI + + mov (sp),d0 + SAVE_ALL + mov fp,d0 # arg 0: stacked register file + mov a2,d1 # arg 1: exception number + call debugger_nmi_interrupt[],0 + RESTORE_ALL + +nmi_not_debugger: +#endif /* CONFIG_KERNEL_DEBUGGER */ + mov (sp),d0 # restore TBR to d0 + add 4,sp +#endif /* CONFIG_SMP */ + + bra __common_exception_nonmi + +############################################################################### +# +# General exception entry point +# +############################################################################### +ENTRY(__common_exception) + add -4,sp + mov d0,(sp) +#if defined(CONFIG_ERRATUM_NEED_TO_RELOAD_MMUCTR) + mov (MMUCTR),d0 + mov d0,(MMUCTR) +#endif + +__common_exception_aux: + mov (TBR),d0 + and ~EPSW_NMID,epsw # turn NMIs back on if not NMI + or EPSW_IE,epsw + +__common_exception_nonmi: + and 0x0000FFFF,d0 # turn the exception code into a vector + # table index + + btst 0x00000007,d0 + bne 1f + cmp 0x00000400,d0 + bge 1f + + SAVE_ALL # build the stack frame + + mov (REG_D0,fp),a2 # get the exception number + mov (REG_ORIG_D0,fp),d0 + mov d0,(REG_D0,fp) + mov -1,d0 + mov d0,(REG_ORIG_D0,fp) + +#ifdef CONFIG_GDBSTUB +#ifdef CONFIG_SMP + call gdbstub_busy_check[],0 + and d0,d0 # check return value + beq 2f +#else /* CONFIG_SMP */ + btst 0x01,(gdbstub_busy) + beq 2f +#endif /* CONFIG_SMP */ + and ~EPSW_IE,epsw + mov fp,d0 + mov a2,d1 + call gdbstub_exception[],0 # gdbstub itself caused an exception + bra restore_all +2: +#endif /* CONFIG_GDBSTUB */ + + mov fp,d0 # arg 0: stacked register file + mov a2,d1 # arg 1: exception number + lsr 1,a2 + + mov (exception_table,a2),a2 + calls (a2) + jmp ret_from_exception + +1: pi # BUG() equivalent + +############################################################################### +# +# Exception handler functions table +# +############################################################################### + .data +ENTRY(exception_table) + .rept 0x400>>1 + .long uninitialised_exception + .endr + .previous + +############################################################################### +# +# Change an entry in the exception table +# - D0 exception code, D1 handler +# +############################################################################### +ENTRY(set_excp_vector) + lsr 1,d0 + add exception_table,d0 + mov d1,(d0) + mov 4,d1 + ret [],0 + +############################################################################### +# +# System call table +# +############################################################################### + .data +ENTRY(sys_call_table) + .long sys_restart_syscall /* 0 */ + .long sys_exit + .long sys_fork + .long sys_read + .long sys_write + .long sys_open /* 5 */ + .long sys_close + .long sys_waitpid + .long sys_creat + .long sys_link + .long sys_unlink /* 10 */ + .long sys_execve + .long sys_chdir + .long sys_time + .long sys_mknod + .long sys_chmod /* 15 */ + .long sys_lchown16 + .long sys_ni_syscall /* old break syscall holder */ + .long sys_stat + .long sys_lseek + .long sys_getpid /* 20 */ + .long sys_mount + .long sys_oldumount + .long sys_setuid16 + .long sys_getuid16 + .long sys_stime /* 25 */ + .long sys_ptrace + .long sys_alarm + .long sys_fstat + .long sys_pause + .long sys_utime /* 30 */ + .long sys_ni_syscall /* old stty syscall holder */ + .long sys_ni_syscall /* old gtty syscall holder */ + .long sys_access + .long sys_nice + .long sys_ni_syscall /* 35 - old ftime syscall holder */ + .long sys_sync + .long sys_kill + .long sys_rename + .long sys_mkdir + .long sys_rmdir /* 40 */ + .long sys_dup + .long sys_pipe + .long sys_times + .long sys_ni_syscall /* old prof syscall holder */ + .long sys_brk /* 45 */ + .long sys_setgid16 + .long sys_getgid16 + .long sys_signal + .long sys_geteuid16 + .long sys_getegid16 /* 50 */ + .long sys_acct + .long sys_umount /* recycled never used phys() */ + .long sys_ni_syscall /* old lock syscall holder */ + .long sys_ioctl + .long sys_fcntl /* 55 */ + .long sys_ni_syscall /* old mpx syscall holder */ + .long sys_setpgid + .long sys_ni_syscall /* old ulimit syscall holder */ + .long sys_ni_syscall /* old sys_olduname */ + .long sys_umask /* 60 */ + .long sys_chroot + .long sys_ustat + .long sys_dup2 + .long sys_getppid + .long sys_getpgrp /* 65 */ + .long sys_setsid + .long sys_sigaction + .long sys_sgetmask + .long sys_ssetmask + .long sys_setreuid16 /* 70 */ + .long sys_setregid16 + .long sys_sigsuspend + .long sys_sigpending + .long sys_sethostname + .long sys_setrlimit /* 75 */ + .long sys_old_getrlimit + .long sys_getrusage + .long sys_gettimeofday + .long sys_settimeofday + .long sys_getgroups16 /* 80 */ + .long sys_setgroups16 + .long sys_old_select + .long sys_symlink + .long sys_lstat + .long sys_readlink /* 85 */ + .long sys_uselib + .long sys_swapon + .long sys_reboot + .long sys_old_readdir + .long old_mmap /* 90 */ + .long sys_munmap + .long sys_truncate + .long sys_ftruncate + .long sys_fchmod + .long sys_fchown16 /* 95 */ + .long sys_getpriority + .long sys_setpriority + .long sys_ni_syscall /* old profil syscall holder */ + .long sys_statfs + .long sys_fstatfs /* 100 */ + .long sys_ni_syscall /* ioperm */ + .long sys_socketcall + .long sys_syslog + .long sys_setitimer + .long sys_getitimer /* 105 */ + .long sys_newstat + .long sys_newlstat + .long sys_newfstat + .long sys_ni_syscall /* old sys_uname */ + .long sys_ni_syscall /* 110 - iopl */ + .long sys_vhangup + .long sys_ni_syscall /* old "idle" system call */ + .long sys_ni_syscall /* vm86old */ + .long sys_wait4 + .long sys_swapoff /* 115 */ + .long sys_sysinfo + .long sys_ipc + .long sys_fsync + .long sys_sigreturn + .long sys_clone /* 120 */ + .long sys_setdomainname + .long sys_newuname + .long sys_ni_syscall /* modify_ldt */ + .long sys_adjtimex + .long sys_mprotect /* 125 */ + .long sys_sigprocmask + .long sys_ni_syscall /* old "create_module" */ + .long sys_init_module + .long sys_delete_module + .long sys_ni_syscall /* 130: old "get_kernel_syms" */ + .long sys_quotactl + .long sys_getpgid + .long sys_fchdir + .long sys_bdflush + .long sys_sysfs /* 135 */ + .long sys_personality + .long sys_ni_syscall /* reserved for afs_syscall */ + .long sys_setfsuid16 + .long sys_setfsgid16 + .long sys_llseek /* 140 */ + .long sys_getdents + .long sys_select + .long sys_flock + .long sys_msync + .long sys_readv /* 145 */ + .long sys_writev + .long sys_getsid + .long sys_fdatasync + .long sys_sysctl + .long sys_mlock /* 150 */ + .long sys_munlock + .long sys_mlockall + .long sys_munlockall + .long sys_sched_setparam + .long sys_sched_getparam /* 155 */ + .long sys_sched_setscheduler + .long sys_sched_getscheduler + .long sys_sched_yield + .long sys_sched_get_priority_max + .long sys_sched_get_priority_min /* 160 */ + .long sys_sched_rr_get_interval + .long sys_nanosleep + .long sys_mremap + .long sys_setresuid16 + .long sys_getresuid16 /* 165 */ + .long sys_ni_syscall /* vm86 */ + .long sys_ni_syscall /* Old sys_query_module */ + .long sys_poll + .long sys_nfsservctl + .long sys_setresgid16 /* 170 */ + .long sys_getresgid16 + .long sys_prctl + .long sys_rt_sigreturn + .long sys_rt_sigaction + .long sys_rt_sigprocmask /* 175 */ + .long sys_rt_sigpending + .long sys_rt_sigtimedwait + .long sys_rt_sigqueueinfo + .long sys_rt_sigsuspend + .long sys_pread64 /* 180 */ + .long sys_pwrite64 + .long sys_chown16 + .long sys_getcwd + .long sys_capget + .long sys_capset /* 185 */ + .long sys_sigaltstack + .long sys_sendfile + .long sys_ni_syscall /* reserved for streams1 */ + .long sys_ni_syscall /* reserved for streams2 */ + .long sys_vfork /* 190 */ + .long sys_getrlimit + .long sys_mmap_pgoff + .long sys_truncate64 + .long sys_ftruncate64 + .long sys_stat64 /* 195 */ + .long sys_lstat64 + .long sys_fstat64 + .long sys_lchown + .long sys_getuid + .long sys_getgid /* 200 */ + .long sys_geteuid + .long sys_getegid + .long sys_setreuid + .long sys_setregid + .long sys_getgroups /* 205 */ + .long sys_setgroups + .long sys_fchown + .long sys_setresuid + .long sys_getresuid + .long sys_setresgid /* 210 */ + .long sys_getresgid + .long sys_chown + .long sys_setuid + .long sys_setgid + .long sys_setfsuid /* 215 */ + .long sys_setfsgid + .long sys_pivot_root + .long sys_mincore + .long sys_madvise + .long sys_getdents64 /* 220 */ + .long sys_fcntl64 + .long sys_ni_syscall /* reserved for TUX */ + .long sys_ni_syscall + .long sys_gettid + .long sys_readahead /* 225 */ + .long sys_setxattr + .long sys_lsetxattr + .long sys_fsetxattr + .long sys_getxattr + .long sys_lgetxattr /* 230 */ + .long sys_fgetxattr + .long sys_listxattr + .long sys_llistxattr + .long sys_flistxattr + .long sys_removexattr /* 235 */ + .long sys_lremovexattr + .long sys_fremovexattr + .long sys_tkill + .long sys_sendfile64 + .long sys_futex /* 240 */ + .long sys_sched_setaffinity + .long sys_sched_getaffinity + .long sys_ni_syscall /* sys_set_thread_area */ + .long sys_ni_syscall /* sys_get_thread_area */ + .long sys_io_setup /* 245 */ + .long sys_io_destroy + .long sys_io_getevents + .long sys_io_submit + .long sys_io_cancel + .long sys_fadvise64 /* 250 */ + .long sys_ni_syscall + .long sys_exit_group + .long sys_lookup_dcookie + .long sys_epoll_create + .long sys_epoll_ctl /* 255 */ + .long sys_epoll_wait + .long sys_remap_file_pages + .long sys_set_tid_address + .long sys_timer_create + .long sys_timer_settime /* 260 */ + .long sys_timer_gettime + .long sys_timer_getoverrun + .long sys_timer_delete + .long sys_clock_settime + .long sys_clock_gettime /* 265 */ + .long sys_clock_getres + .long sys_clock_nanosleep + .long sys_statfs64 + .long sys_fstatfs64 + .long sys_tgkill /* 270 */ + .long sys_utimes + .long sys_fadvise64_64 + .long sys_ni_syscall /* sys_vserver */ + .long sys_mbind + .long sys_get_mempolicy /* 275 */ + .long sys_set_mempolicy + .long sys_mq_open + .long sys_mq_unlink + .long sys_mq_timedsend + .long sys_mq_timedreceive /* 280 */ + .long sys_mq_notify + .long sys_mq_getsetattr + .long sys_kexec_load + .long sys_waitid + .long sys_ni_syscall /* 285 */ /* available */ + .long sys_add_key + .long sys_request_key + .long sys_keyctl + .long sys_cacheflush + .long sys_ioprio_set /* 290 */ + .long sys_ioprio_get + .long sys_inotify_init + .long sys_inotify_add_watch + .long sys_inotify_rm_watch + .long sys_migrate_pages /* 295 */ + .long sys_openat + .long sys_mkdirat + .long sys_mknodat + .long sys_fchownat + .long sys_futimesat /* 300 */ + .long sys_fstatat64 + .long sys_unlinkat + .long sys_renameat + .long sys_linkat + .long sys_symlinkat /* 305 */ + .long sys_readlinkat + .long sys_fchmodat + .long sys_faccessat + .long sys_pselect6 + .long sys_ppoll /* 310 */ + .long sys_unshare + .long sys_set_robust_list + .long sys_get_robust_list + .long sys_splice + .long sys_sync_file_range /* 315 */ + .long sys_tee + .long sys_vmsplice + .long sys_move_pages + .long sys_getcpu + .long sys_epoll_pwait /* 320 */ + .long sys_utimensat + .long sys_signalfd + .long sys_timerfd_create + .long sys_eventfd + .long sys_fallocate /* 325 */ + .long sys_timerfd_settime + .long sys_timerfd_gettime + .long sys_signalfd4 + .long sys_eventfd2 + .long sys_epoll_create1 /* 330 */ + .long sys_dup3 + .long sys_pipe2 + .long sys_inotify_init1 + .long sys_preadv + .long sys_pwritev /* 335 */ + .long sys_rt_tgsigqueueinfo + .long sys_perf_event_open + .long sys_recvmmsg + .long sys_setns + + +nr_syscalls=(.-sys_call_table)/4 diff --git a/arch/mn10300/kernel/fpu-low.S b/arch/mn10300/kernel/fpu-low.S new file mode 100644 index 00000000..78df25cf --- /dev/null +++ b/arch/mn10300/kernel/fpu-low.S @@ -0,0 +1,258 @@ +/* MN10300 Low level FPU management operations + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/linkage.h> +#include <asm/cpu-regs.h> +#include <asm/smp.h> +#include <asm/thread_info.h> +#include <asm/asm-offsets.h> +#include <asm/frame.inc> + +.macro FPU_INIT_STATE_ALL + fmov 0,fs0 + fmov fs0,fs1 + fmov fs0,fs2 + fmov fs0,fs3 + fmov fs0,fs4 + fmov fs0,fs5 + fmov fs0,fs6 + fmov fs0,fs7 + fmov fs0,fs8 + fmov fs0,fs9 + fmov fs0,fs10 + fmov fs0,fs11 + fmov fs0,fs12 + fmov fs0,fs13 + fmov fs0,fs14 + fmov fs0,fs15 + fmov fs0,fs16 + fmov fs0,fs17 + fmov fs0,fs18 + fmov fs0,fs19 + fmov fs0,fs20 + fmov fs0,fs21 + fmov fs0,fs22 + fmov fs0,fs23 + fmov fs0,fs24 + fmov fs0,fs25 + fmov fs0,fs26 + fmov fs0,fs27 + fmov fs0,fs28 + fmov fs0,fs29 + fmov fs0,fs30 + fmov fs0,fs31 + fmov FPCR_INIT,fpcr +.endm + +.macro FPU_SAVE_ALL areg,dreg + fmov fs0,(\areg+) + fmov fs1,(\areg+) + fmov fs2,(\areg+) + fmov fs3,(\areg+) + fmov fs4,(\areg+) + fmov fs5,(\areg+) + fmov fs6,(\areg+) + fmov fs7,(\areg+) + fmov fs8,(\areg+) + fmov fs9,(\areg+) + fmov fs10,(\areg+) + fmov fs11,(\areg+) + fmov fs12,(\areg+) + fmov fs13,(\areg+) + fmov fs14,(\areg+) + fmov fs15,(\areg+) + fmov fs16,(\areg+) + fmov fs17,(\areg+) + fmov fs18,(\areg+) + fmov fs19,(\areg+) + fmov fs20,(\areg+) + fmov fs21,(\areg+) + fmov fs22,(\areg+) + fmov fs23,(\areg+) + fmov fs24,(\areg+) + fmov fs25,(\areg+) + fmov fs26,(\areg+) + fmov fs27,(\areg+) + fmov fs28,(\areg+) + fmov fs29,(\areg+) + fmov fs30,(\areg+) + fmov fs31,(\areg+) + fmov fpcr,\dreg + mov \dreg,(\areg) +.endm + +.macro FPU_RESTORE_ALL areg,dreg + fmov (\areg+),fs0 + fmov (\areg+),fs1 + fmov (\areg+),fs2 + fmov (\areg+),fs3 + fmov (\areg+),fs4 + fmov (\areg+),fs5 + fmov (\areg+),fs6 + fmov (\areg+),fs7 + fmov (\areg+),fs8 + fmov (\areg+),fs9 + fmov (\areg+),fs10 + fmov (\areg+),fs11 + fmov (\areg+),fs12 + fmov (\areg+),fs13 + fmov (\areg+),fs14 + fmov (\areg+),fs15 + fmov (\areg+),fs16 + fmov (\areg+),fs17 + fmov (\areg+),fs18 + fmov (\areg+),fs19 + fmov (\areg+),fs20 + fmov (\areg+),fs21 + fmov (\areg+),fs22 + fmov (\areg+),fs23 + fmov (\areg+),fs24 + fmov (\areg+),fs25 + fmov (\areg+),fs26 + fmov (\areg+),fs27 + fmov (\areg+),fs28 + fmov (\areg+),fs29 + fmov (\areg+),fs30 + fmov (\areg+),fs31 + mov (\areg),\dreg + fmov \dreg,fpcr +.endm + +############################################################################### +# +# void fpu_init_state(void) +# - initialise the FPU +# +############################################################################### + .globl fpu_init_state + .type fpu_init_state,@function +fpu_init_state: + mov epsw,d0 + or EPSW_FE,epsw + +#ifdef CONFIG_MN10300_PROC_MN103E010 + nop + nop + nop +#endif + FPU_INIT_STATE_ALL +#ifdef CONFIG_MN10300_PROC_MN103E010 + nop + nop + nop +#endif + mov d0,epsw + ret [],0 + + .size fpu_init_state,.-fpu_init_state + +############################################################################### +# +# void fpu_save(struct fpu_state_struct *) +# - save the fpu state +# - note that an FPU Operational exception might occur during this process +# +############################################################################### + .globl fpu_save + .type fpu_save,@function +fpu_save: + mov epsw,d1 + or EPSW_FE,epsw /* enable the FPU so we can access it */ + +#ifdef CONFIG_MN10300_PROC_MN103E010 + nop + nop +#endif + mov d0,a0 + FPU_SAVE_ALL a0,d0 +#ifdef CONFIG_MN10300_PROC_MN103E010 + nop + nop +#endif + + mov d1,epsw + ret [],0 + + .size fpu_save,.-fpu_save + +############################################################################### +# +# void fpu_disabled(void) +# - handle an exception due to the FPU being disabled +# when CONFIG_FPU is enabled +# +############################################################################### + .type fpu_disabled,@function + .globl fpu_disabled +fpu_disabled: + or EPSW_nAR|EPSW_FE,epsw + nop + nop + nop + + mov sp,a1 + mov (a1),d1 /* get epsw of user context */ + and ~(THREAD_SIZE-1),a1 /* a1: (thread_info *ti) */ + mov (TI_task,a1),a2 /* a2: (task_struct *tsk) */ + btst EPSW_nSL,d1 + beq fpu_used_in_kernel + + or EPSW_FE,d1 + mov d1,(sp) + mov (TASK_THREAD+THREAD_FPU_FLAGS,a2),d1 +#ifndef CONFIG_LAZY_SAVE_FPU + or __THREAD_HAS_FPU,d1 + mov d1,(TASK_THREAD+THREAD_FPU_FLAGS,a2) +#else /* !CONFIG_LAZY_SAVE_FPU */ + mov (fpu_state_owner),a0 + cmp 0,a0 + beq fpu_regs_save_end + + mov (TASK_THREAD+THREAD_UREGS,a0),a1 + add TASK_THREAD+THREAD_FPU_STATE,a0 + FPU_SAVE_ALL a0,d0 + + mov (REG_EPSW,a1),d0 + and ~EPSW_FE,d0 + mov d0,(REG_EPSW,a1) + +fpu_regs_save_end: + mov a2,(fpu_state_owner) +#endif /* !CONFIG_LAZY_SAVE_FPU */ + + btst __THREAD_USING_FPU,d1 + beq fpu_regs_init + add TASK_THREAD+THREAD_FPU_STATE,a2 + FPU_RESTORE_ALL a2,d0 + rti + +fpu_regs_init: + FPU_INIT_STATE_ALL + add TASK_THREAD+THREAD_FPU_FLAGS,a2 + bset __THREAD_USING_FPU,(0,a2) + rti + +fpu_used_in_kernel: + and ~(EPSW_nAR|EPSW_FE),epsw + nop + nop + + add -4,sp + SAVE_ALL + mov -1,d0 + mov d0,(REG_ORIG_D0,fp) + + and ~EPSW_NMID,epsw + + mov fp,d0 + call fpu_disabled_in_kernel[],0 + jmp ret_from_exception + + .size fpu_disabled,.-fpu_disabled diff --git a/arch/mn10300/kernel/fpu-nofpu-low.S b/arch/mn10300/kernel/fpu-nofpu-low.S new file mode 100644 index 00000000..7ea087a5 --- /dev/null +++ b/arch/mn10300/kernel/fpu-nofpu-low.S @@ -0,0 +1,39 @@ +/* MN10300 Low level FPU management operations + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/linkage.h> +#include <asm/cpu-regs.h> +#include <asm/smp.h> +#include <asm/thread_info.h> +#include <asm/asm-offsets.h> +#include <asm/frame.inc> + +############################################################################### +# +# void fpu_disabled(void) +# - handle an exception due to the FPU being disabled +# when CONFIG_FPU is disabled +# +############################################################################### + .type fpu_disabled,@function + .globl fpu_disabled +fpu_disabled: + add -4,sp + SAVE_ALL + mov -1,d0 + mov d0,(REG_ORIG_D0,fp) + + and ~EPSW_NMID,epsw + + mov fp,d0 + call unexpected_fpu_exception[],0 + jmp ret_from_exception + + .size fpu_disabled,.-fpu_disabled diff --git a/arch/mn10300/kernel/fpu-nofpu.c b/arch/mn10300/kernel/fpu-nofpu.c new file mode 100644 index 00000000..31c765b9 --- /dev/null +++ b/arch/mn10300/kernel/fpu-nofpu.c @@ -0,0 +1,30 @@ +/* MN10300 FPU management + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <asm/fpu.h> + +/* + * handle an FPU operational exception + * - there's a possibility that if the FPU is asynchronous, the signal might + * be meant for a process other than the current one + */ +asmlinkage +void unexpected_fpu_exception(struct pt_regs *regs, enum exception_code code) +{ + panic("An FPU exception was received, but there's no FPU enabled."); +} + +/* + * fill in the FPU structure for a core dump + */ +int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpreg) +{ + return 0; /* not valid */ +} diff --git a/arch/mn10300/kernel/fpu.c b/arch/mn10300/kernel/fpu.c new file mode 100644 index 00000000..bb5fa7df --- /dev/null +++ b/arch/mn10300/kernel/fpu.c @@ -0,0 +1,176 @@ +/* MN10300 FPU management + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <asm/uaccess.h> +#include <asm/fpu.h> +#include <asm/elf.h> +#include <asm/exceptions.h> +#include <asm/system.h> + +#ifdef CONFIG_LAZY_SAVE_FPU +struct task_struct *fpu_state_owner; +#endif + +/* + * error functions in FPU disabled exception + */ +asmlinkage void fpu_disabled_in_kernel(struct pt_regs *regs) +{ + die_if_no_fixup("An FPU Disabled exception happened in kernel space\n", + regs, EXCEP_FPU_DISABLED); +} + +/* + * handle an FPU operational exception + * - there's a possibility that if the FPU is asynchronous, the signal might + * be meant for a process other than the current one + */ +asmlinkage void fpu_exception(struct pt_regs *regs, enum exception_code code) +{ + struct task_struct *tsk = current; + siginfo_t info; + u32 fpcr; + + if (!user_mode(regs)) + die_if_no_fixup("An FPU Operation exception happened in" + " kernel space\n", + regs, code); + + if (!is_using_fpu(tsk)) + die_if_no_fixup("An FPU Operation exception happened," + " but the FPU is not in use", + regs, code); + + info.si_signo = SIGFPE; + info.si_errno = 0; + info.si_addr = (void *) tsk->thread.uregs->pc; + info.si_code = FPE_FLTINV; + + unlazy_fpu(tsk); + + fpcr = tsk->thread.fpu_state.fpcr; + + if (fpcr & FPCR_EC_Z) + info.si_code = FPE_FLTDIV; + else if (fpcr & FPCR_EC_O) + info.si_code = FPE_FLTOVF; + else if (fpcr & FPCR_EC_U) + info.si_code = FPE_FLTUND; + else if (fpcr & FPCR_EC_I) + info.si_code = FPE_FLTRES; + + force_sig_info(SIGFPE, &info, tsk); +} + +/* + * save the FPU state to a signal context + */ +int fpu_setup_sigcontext(struct fpucontext *fpucontext) +{ + struct task_struct *tsk = current; + + if (!is_using_fpu(tsk)) + return 0; + + /* transfer the current FPU state to memory and cause fpu_init() to be + * triggered by the next attempted FPU operation by the current + * process. + */ + preempt_disable(); + +#ifndef CONFIG_LAZY_SAVE_FPU + if (tsk->thread.fpu_flags & THREAD_HAS_FPU) { + fpu_save(&tsk->thread.fpu_state); + tsk->thread.uregs->epsw &= ~EPSW_FE; + tsk->thread.fpu_flags &= ~THREAD_HAS_FPU; + } +#else /* !CONFIG_LAZY_SAVE_FPU */ + if (fpu_state_owner == tsk) { + fpu_save(&tsk->thread.fpu_state); + fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE; + fpu_state_owner = NULL; + } +#endif /* !CONFIG_LAZY_SAVE_FPU */ + + preempt_enable(); + + /* we no longer have a valid current FPU state */ + clear_using_fpu(tsk); + + /* transfer the saved FPU state onto the userspace stack */ + if (copy_to_user(fpucontext, + &tsk->thread.fpu_state, + min(sizeof(struct fpu_state_struct), + sizeof(struct fpucontext)))) + return -1; + + return 1; +} + +/* + * kill a process's FPU state during restoration after signal handling + */ +void fpu_kill_state(struct task_struct *tsk) +{ + /* disown anything left in the FPU */ + preempt_disable(); + +#ifndef CONFIG_LAZY_SAVE_FPU + if (tsk->thread.fpu_flags & THREAD_HAS_FPU) { + tsk->thread.uregs->epsw &= ~EPSW_FE; + tsk->thread.fpu_flags &= ~THREAD_HAS_FPU; + } +#else /* !CONFIG_LAZY_SAVE_FPU */ + if (fpu_state_owner == tsk) { + fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE; + fpu_state_owner = NULL; + } +#endif /* !CONFIG_LAZY_SAVE_FPU */ + + preempt_enable(); + + /* we no longer have a valid current FPU state */ + clear_using_fpu(tsk); +} + +/* + * restore the FPU state from a signal context + */ +int fpu_restore_sigcontext(struct fpucontext *fpucontext) +{ + struct task_struct *tsk = current; + int ret; + + /* load up the old FPU state */ + ret = copy_from_user(&tsk->thread.fpu_state, fpucontext, + min(sizeof(struct fpu_state_struct), + sizeof(struct fpucontext))); + if (!ret) + set_using_fpu(tsk); + + return ret; +} + +/* + * fill in the FPU structure for a core dump + */ +int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpreg) +{ + struct task_struct *tsk = current; + int fpvalid; + + fpvalid = is_using_fpu(tsk); + if (fpvalid) { + unlazy_fpu(tsk); + memcpy(fpreg, &tsk->thread.fpu_state, sizeof(*fpreg)); + } + + return fpvalid; +} diff --git a/arch/mn10300/kernel/gdb-io-serial-low.S b/arch/mn10300/kernel/gdb-io-serial-low.S new file mode 100644 index 00000000..b1d0152e --- /dev/null +++ b/arch/mn10300/kernel/gdb-io-serial-low.S @@ -0,0 +1,91 @@ +############################################################################### +# +# 16550 serial Rx interrupt handler for gdbstub I/O +# +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/cpu-regs.h> +#include <asm/thread_info.h> +#include <asm/frame.inc> +#include <asm/intctl-regs.h> +#include <asm/irqflags.h> +#include <unit/serial.h> + + .text + +############################################################################### +# +# GDB stub serial receive interrupt entry point +# - intended to run at interrupt priority 0 +# +############################################################################### + .globl gdbstub_io_rx_handler + .type gdbstub_io_rx_handler,@function +gdbstub_io_rx_handler: + movm [d2,d3,a2,a3],(sp) + +#if 1 + movbu (GDBPORT_SERIAL_IIR),d2 +#endif + + mov (gdbstub_rx_inp),a3 +gdbstub_io_rx_more: + mov a3,a2 + add 2,a3 + and 0x00000fff,a3 + mov (gdbstub_rx_outp),d3 + cmp a3,d3 + beq gdbstub_io_rx_overflow + + movbu (GDBPORT_SERIAL_LSR),d3 + btst UART_LSR_DR,d3 + beq gdbstub_io_rx_done + movbu (GDBPORT_SERIAL_RX),d2 + movbu d3,(gdbstub_rx_buffer+1,a2) + movbu d2,(gdbstub_rx_buffer,a2) + mov a3,(gdbstub_rx_inp) + bra gdbstub_io_rx_more + +gdbstub_io_rx_done: + mov GxICR_DETECT,d2 + movbu d2,(XIRQxICR(GDBPORT_SERIAL_IRQ)) # ACK the interrupt + movhu (XIRQxICR(GDBPORT_SERIAL_IRQ)),d2 # flush + movm (sp),[d2,d3,a2,a3] + bset 0x01,(gdbstub_busy) + beq gdbstub_io_rx_enter + rti + +gdbstub_io_rx_overflow: + bset 0x01,(gdbstub_rx_overflow) + bra gdbstub_io_rx_done + +gdbstub_io_rx_enter: + LOCAL_CHANGE_INTR_MASK_LEVEL(NUM2EPSW_IM(CONFIG_GDBSTUB_IRQ_LEVEL+1)) + add -4,sp + SAVE_ALL + + mov 0xffffffff,d0 + mov d0,(REG_ORIG_D0,fp) + mov 0x280,d1 + + mov fp,d0 + call gdbstub_rx_irq[],0 # gdbstub_rx_irq(regs,excep) + + LOCAL_CLI + bclr 0x01,(gdbstub_busy) + + .globl gdbstub_return +gdbstub_return: + RESTORE_ALL + + .size gdbstub_io_rx_handler,.-gdbstub_io_rx_handler diff --git a/arch/mn10300/kernel/gdb-io-serial.c b/arch/mn10300/kernel/gdb-io-serial.c new file mode 100644 index 00000000..f28dc99c --- /dev/null +++ b/arch/mn10300/kernel/gdb-io-serial.c @@ -0,0 +1,175 @@ +/* 16550 serial driver for gdbstub I/O + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/console.h> +#include <linux/init.h> +#include <linux/nmi.h> + +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/gdb-stub.h> +#include <asm/exceptions.h> +#include <asm/serial-regs.h> +#include <unit/serial.h> +#include <asm/smp.h> + +/* + * initialise the GDB stub + */ +void gdbstub_io_init(void) +{ + u16 tmp; + + /* set up the serial port */ + GDBPORT_SERIAL_LCR = UART_LCR_WLEN8; /* 1N8 */ + GDBPORT_SERIAL_FCR = (UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT); + + FLOWCTL_CLEAR(DTR); + FLOWCTL_SET(RTS); + + gdbstub_io_set_baud(115200); + + /* we want to get serial receive interrupts */ + XIRQxICR(GDBPORT_SERIAL_IRQ) = 0; + tmp = XIRQxICR(GDBPORT_SERIAL_IRQ); + +#if CONFIG_GDBSTUB_IRQ_LEVEL == 0 + IVAR0 = EXCEP_IRQ_LEVEL0; +#elif CONFIG_GDBSTUB_IRQ_LEVEL == 1 + IVAR1 = EXCEP_IRQ_LEVEL1; +#elif CONFIG_GDBSTUB_IRQ_LEVEL == 2 + IVAR2 = EXCEP_IRQ_LEVEL2; +#elif CONFIG_GDBSTUB_IRQ_LEVEL == 3 + IVAR3 = EXCEP_IRQ_LEVEL3; +#elif CONFIG_GDBSTUB_IRQ_LEVEL == 4 + IVAR4 = EXCEP_IRQ_LEVEL4; +#elif CONFIG_GDBSTUB_IRQ_LEVEL == 5 + IVAR5 = EXCEP_IRQ_LEVEL5; +#else +#error "Unknown irq level for gdbstub." +#endif + + set_intr_stub(NUM2EXCEP_IRQ_LEVEL(CONFIG_GDBSTUB_IRQ_LEVEL), + gdbstub_io_rx_handler); + + XIRQxICR(GDBPORT_SERIAL_IRQ) &= ~GxICR_REQUEST; + XIRQxICR(GDBPORT_SERIAL_IRQ) = + GxICR_ENABLE | NUM2GxICR_LEVEL(CONFIG_GDBSTUB_IRQ_LEVEL); + tmp = XIRQxICR(GDBPORT_SERIAL_IRQ); + + GDBPORT_SERIAL_IER = UART_IER_RDI | UART_IER_RLSI; + + /* permit level 0 IRQs to take place */ + arch_local_change_intr_mask_level( + NUM2EPSW_IM(CONFIG_GDBSTUB_IRQ_LEVEL + 1)); +} + +/* + * set up the GDB stub serial port baud rate timers + */ +void gdbstub_io_set_baud(unsigned baud) +{ + unsigned value; + u8 lcr; + + value = 18432000 / 16 / baud; + + lcr = GDBPORT_SERIAL_LCR; + GDBPORT_SERIAL_LCR |= UART_LCR_DLAB; + GDBPORT_SERIAL_DLL = value & 0xff; + GDBPORT_SERIAL_DLM = (value >> 8) & 0xff; + GDBPORT_SERIAL_LCR = lcr; +} + +/* + * wait for a character to come from the debugger + */ +int gdbstub_io_rx_char(unsigned char *_ch, int nonblock) +{ + unsigned ix; + u8 ch, st; +#if defined(CONFIG_MN10300_WD_TIMER) + int cpu; +#endif + + *_ch = 0xff; + + if (gdbstub_rx_unget) { + *_ch = gdbstub_rx_unget; + gdbstub_rx_unget = 0; + return 0; + } + + try_again: + /* pull chars out of the buffer */ + ix = gdbstub_rx_outp; + barrier(); + if (ix == gdbstub_rx_inp) { + if (nonblock) + return -EAGAIN; +#ifdef CONFIG_MN10300_WD_TIMER + for (cpu = 0; cpu < NR_CPUS; cpu++) + watchdog_alert_counter[cpu] = 0; +#endif + goto try_again; + } + + ch = gdbstub_rx_buffer[ix++]; + st = gdbstub_rx_buffer[ix++]; + barrier(); + gdbstub_rx_outp = ix & 0x00000fff; + + if (st & UART_LSR_BI) { + gdbstub_proto("### GDB Rx Break Detected ###\n"); + return -EINTR; + } else if (st & (UART_LSR_FE | UART_LSR_OE | UART_LSR_PE)) { + gdbstub_proto("### GDB Rx Error (st=%02x) ###\n", st); + return -EIO; + } else { + gdbstub_proto("### GDB Rx %02x (st=%02x) ###\n", ch, st); + *_ch = ch & 0x7f; + return 0; + } +} + +/* + * send a character to the debugger + */ +void gdbstub_io_tx_char(unsigned char ch) +{ + FLOWCTL_SET(DTR); + LSR_WAIT_FOR(THRE); + /* FLOWCTL_WAIT_FOR(CTS); */ + + if (ch == 0x0a) { + GDBPORT_SERIAL_TX = 0x0d; + LSR_WAIT_FOR(THRE); + /* FLOWCTL_WAIT_FOR(CTS); */ + } + GDBPORT_SERIAL_TX = ch; + + FLOWCTL_CLEAR(DTR); +} + +/* + * send a character to the debugger + */ +void gdbstub_io_tx_flush(void) +{ + LSR_WAIT_FOR(TEMT); + LSR_WAIT_FOR(THRE); + FLOWCTL_CLEAR(DTR); +} diff --git a/arch/mn10300/kernel/gdb-io-ttysm-low.S b/arch/mn10300/kernel/gdb-io-ttysm-low.S new file mode 100644 index 00000000..060b7cca --- /dev/null +++ b/arch/mn10300/kernel/gdb-io-ttysm-low.S @@ -0,0 +1,93 @@ +############################################################################### +# +# MN10300 On-chip serial Rx interrupt handler for GDB stub I/O +# +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/thread_info.h> +#include <asm/cpu-regs.h> +#include <asm/frame.inc> +#include <asm/intctl-regs.h> +#include <unit/serial.h> +#include "mn10300-serial.h" + + .text + +############################################################################### +# +# GDB stub serial receive interrupt entry point +# - intended to run at interrupt priority 0 +# +############################################################################### + .globl gdbstub_io_rx_handler + .type gdbstub_io_rx_handler,@function +gdbstub_io_rx_handler: + movm [d2,d3,a2,a3],(sp) + + mov (gdbstub_rx_inp),a3 +gdbstub_io_rx_more: + mov a3,a2 + add 2,a3 + and PAGE_SIZE_asm-1,a3 + mov (gdbstub_rx_outp),d3 + cmp a3,d3 + beq gdbstub_io_rx_overflow + + movbu (SCgSTR),d3 + btst SC01STR_RBF,d3 + beq gdbstub_io_rx_done + movbu (SCgRXB),d2 + movbu d3,(gdbstub_rx_buffer+1,a2) + movbu d2,(gdbstub_rx_buffer,a2) + mov a3,(gdbstub_rx_inp) + bra gdbstub_io_rx_more + +gdbstub_io_rx_done: + mov GxICR_DETECT,d2 + movbu d2,(GxICR(SCgRXIRQ)) # ACK the interrupt + movhu (GxICR(SCgRXIRQ)),d2 # flush + + movm (sp),[d2,d3,a2,a3] + bset 0x01,(gdbstub_busy) + beq gdbstub_io_rx_enter + rti + +gdbstub_io_rx_overflow: + bset 0x01,(gdbstub_rx_overflow) + bra gdbstub_io_rx_done + +############################################################################### +# +# debugging interrupt - enter the GDB stub proper +# +############################################################################### +gdbstub_io_rx_enter: + or EPSW_IE|EPSW_IM_1,epsw + add -4,sp + SAVE_ALL + + mov 0xffffffff,d0 + mov d0,(REG_ORIG_D0,fp) + mov 0x280,d1 + + mov fp,d0 + call gdbstub_rx_irq[],0 # gdbstub_io_rx_irq(regs,excep) + + and ~EPSW_IE,epsw + bclr 0x01,(gdbstub_busy) + + .globl gdbstub_return +gdbstub_return: + RESTORE_ALL + + .size gdbstub_io_rx_handler,.-gdbstub_io_rx_handler diff --git a/arch/mn10300/kernel/gdb-io-ttysm.c b/arch/mn10300/kernel/gdb-io-ttysm.c new file mode 100644 index 00000000..c859cacb --- /dev/null +++ b/arch/mn10300/kernel/gdb-io-ttysm.c @@ -0,0 +1,304 @@ +/* MN10300 On-chip serial driver for gdbstub I/O + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/console.h> +#include <linux/init.h> +#include <linux/tty.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/gdb-stub.h> +#include <asm/exceptions.h> +#include <unit/clock.h> +#include "mn10300-serial.h" + +#if defined(CONFIG_GDBSTUB_ON_TTYSM0) +struct mn10300_serial_port *const gdbstub_port = &mn10300_serial_port_sif0; +#elif defined(CONFIG_GDBSTUB_ON_TTYSM1) +struct mn10300_serial_port *const gdbstub_port = &mn10300_serial_port_sif1; +#else +struct mn10300_serial_port *const gdbstub_port = &mn10300_serial_port_sif2; +#endif + + +/* + * initialise the GDB stub I/O routines + */ +void __init gdbstub_io_init(void) +{ + uint16_t scxctr; + int tmp; + + switch (gdbstub_port->clock_src) { + case MNSCx_CLOCK_SRC_IOCLK: + gdbstub_port->ioclk = MN10300_IOCLK; + break; + +#ifdef MN10300_IOBCLK + case MNSCx_CLOCK_SRC_IOBCLK: + gdbstub_port->ioclk = MN10300_IOBCLK; + break; +#endif + default: + BUG(); + } + + /* set up the serial port */ + gdbstub_io_set_baud(115200); + + /* we want to get serial receive interrupts */ + set_intr_level(gdbstub_port->rx_irq, + NUM2GxICR_LEVEL(CONFIG_DEBUGGER_IRQ_LEVEL)); + set_intr_level(gdbstub_port->tx_irq, + NUM2GxICR_LEVEL(CONFIG_DEBUGGER_IRQ_LEVEL)); + set_intr_stub(NUM2EXCEP_IRQ_LEVEL(CONFIG_DEBUGGER_IRQ_LEVEL), + gdbstub_io_rx_handler); + + *gdbstub_port->rx_icr |= GxICR_ENABLE; + tmp = *gdbstub_port->rx_icr; + + /* enable the device */ + scxctr = SC01CTR_CLN_8BIT; /* 1N8 */ + switch (gdbstub_port->div_timer) { + case MNSCx_DIV_TIMER_16BIT: + scxctr |= SC0CTR_CK_TM8UFLOW_8; /* == SC1CTR_CK_TM9UFLOW_8 + == SC2CTR_CK_TM10UFLOW_8 */ + break; + + case MNSCx_DIV_TIMER_8BIT: + scxctr |= SC0CTR_CK_TM2UFLOW_8; + break; + } + + scxctr |= SC01CTR_TXE | SC01CTR_RXE; + + *gdbstub_port->_control = scxctr; + tmp = *gdbstub_port->_control; + + /* permit level 0 IRQs only */ + arch_local_change_intr_mask_level( + NUM2EPSW_IM(CONFIG_DEBUGGER_IRQ_LEVEL + 1)); +} + +/* + * set up the GDB stub serial port baud rate timers + */ +void gdbstub_io_set_baud(unsigned baud) +{ + const unsigned bits = 10; /* 1 [start] + 8 [data] + 0 [parity] + + * 1 [stop] */ + unsigned long ioclk = gdbstub_port->ioclk; + unsigned xdiv, tmp; + uint16_t tmxbr; + uint8_t tmxmd; + + if (!baud) { + baud = 9600; + } else if (baud == 134) { + baud = 269; /* 134 is really 134.5 */ + xdiv = 2; + } + +try_alternative: + xdiv = 1; + + switch (gdbstub_port->div_timer) { + case MNSCx_DIV_TIMER_16BIT: + tmxmd = TM8MD_SRC_IOCLK; + tmxbr = tmp = (ioclk / (baud * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 65535) + goto timer_okay; + + tmxmd = TM8MD_SRC_IOCLK_8; + tmxbr = tmp = (ioclk / (baud * 8 * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 65535) + goto timer_okay; + + tmxmd = TM8MD_SRC_IOCLK_32; + tmxbr = tmp = (ioclk / (baud * 32 * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 65535) + goto timer_okay; + + break; + + case MNSCx_DIV_TIMER_8BIT: + tmxmd = TM2MD_SRC_IOCLK; + tmxbr = tmp = (ioclk / (baud * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 255) + goto timer_okay; + + tmxmd = TM2MD_SRC_IOCLK_8; + tmxbr = tmp = (ioclk / (baud * 8 * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 255) + goto timer_okay; + + tmxmd = TM2MD_SRC_IOCLK_32; + tmxbr = tmp = (ioclk / (baud * 32 * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 255) + goto timer_okay; + break; + } + + /* as a last resort, if the quotient is zero, default to 9600 bps */ + baud = 9600; + goto try_alternative; + +timer_okay: + gdbstub_port->uart.timeout = (2 * bits * HZ) / baud; + gdbstub_port->uart.timeout += HZ / 50; + + /* set the timer to produce the required baud rate */ + switch (gdbstub_port->div_timer) { + case MNSCx_DIV_TIMER_16BIT: + *gdbstub_port->_tmxmd = 0; + *gdbstub_port->_tmxbr = tmxbr; + *gdbstub_port->_tmxmd = TM8MD_INIT_COUNTER; + *gdbstub_port->_tmxmd = tmxmd | TM8MD_COUNT_ENABLE; + break; + + case MNSCx_DIV_TIMER_8BIT: + *gdbstub_port->_tmxmd = 0; + *(volatile u8 *) gdbstub_port->_tmxbr = (u8)tmxbr; + *gdbstub_port->_tmxmd = TM2MD_INIT_COUNTER; + *gdbstub_port->_tmxmd = tmxmd | TM2MD_COUNT_ENABLE; + break; + } +} + +/* + * wait for a character to come from the debugger + */ +int gdbstub_io_rx_char(unsigned char *_ch, int nonblock) +{ + unsigned ix; + u8 ch, st; +#if defined(CONFIG_MN10300_WD_TIMER) + int cpu; +#endif + + *_ch = 0xff; + + if (gdbstub_rx_unget) { + *_ch = gdbstub_rx_unget; + gdbstub_rx_unget = 0; + return 0; + } + +try_again: + /* pull chars out of the buffer */ + ix = gdbstub_rx_outp; + barrier(); + if (ix == gdbstub_rx_inp) { + if (nonblock) + return -EAGAIN; +#ifdef CONFIG_MN10300_WD_TIMER + for (cpu = 0; cpu < NR_CPUS; cpu++) + watchdog_alert_counter[cpu] = 0; +#endif + goto try_again; + } + + ch = gdbstub_rx_buffer[ix++]; + st = gdbstub_rx_buffer[ix++]; + barrier(); + gdbstub_rx_outp = ix & (PAGE_SIZE - 1); + + st &= SC01STR_RXF | SC01STR_RBF | SC01STR_FEF | SC01STR_PEF | + SC01STR_OEF; + + /* deal with what we've got + * - note that the UART doesn't do BREAK-detection for us + */ + if (st & SC01STR_FEF && ch == 0) { + switch (gdbstub_port->rx_brk) { + case 0: gdbstub_port->rx_brk = 1; goto try_again; + case 1: gdbstub_port->rx_brk = 2; goto try_again; + case 2: + gdbstub_port->rx_brk = 3; + gdbstub_proto("### GDB MNSERIAL Rx Break Detected" + " ###\n"); + return -EINTR; + default: + goto try_again; + } + } else if (st & SC01STR_FEF) { + if (gdbstub_port->rx_brk) + goto try_again; + + gdbstub_proto("### GDB MNSERIAL Framing Error ###\n"); + return -EIO; + } else if (st & SC01STR_OEF) { + if (gdbstub_port->rx_brk) + goto try_again; + + gdbstub_proto("### GDB MNSERIAL Overrun Error ###\n"); + return -EIO; + } else if (st & SC01STR_PEF) { + if (gdbstub_port->rx_brk) + goto try_again; + + gdbstub_proto("### GDB MNSERIAL Parity Error ###\n"); + return -EIO; + } else { + /* look for the tail-end char on a break run */ + if (gdbstub_port->rx_brk == 3) { + switch (ch) { + case 0xFF: + case 0xFE: + case 0xFC: + case 0xF8: + case 0xF0: + case 0xE0: + case 0xC0: + case 0x80: + case 0x00: + gdbstub_port->rx_brk = 0; + goto try_again; + default: + break; + } + } + + gdbstub_port->rx_brk = 0; + gdbstub_io("### GDB Rx %02x (st=%02x) ###\n", ch, st); + *_ch = ch & 0x7f; + return 0; + } +} + +/* + * send a character to the debugger + */ +void gdbstub_io_tx_char(unsigned char ch) +{ + while (*gdbstub_port->_status & SC01STR_TBF) + continue; + + if (ch == 0x0a) { + *(u8 *) gdbstub_port->_txb = 0x0d; + while (*gdbstub_port->_status & SC01STR_TBF) + continue; + } + + *(u8 *) gdbstub_port->_txb = ch; +} + +/* + * flush the transmission buffers + */ +void gdbstub_io_tx_flush(void) +{ + while (*gdbstub_port->_status & (SC01STR_TBF | SC01STR_TXF)) + continue; +} diff --git a/arch/mn10300/kernel/gdb-low.S b/arch/mn10300/kernel/gdb-low.S new file mode 100644 index 00000000..e2725552 --- /dev/null +++ b/arch/mn10300/kernel/gdb-low.S @@ -0,0 +1,115 @@ +############################################################################### +# +# MN10300 Low-level gdbstub routines +# +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/cache.h> +#include <asm/cpu-regs.h> +#include <asm/exceptions.h> +#include <asm/frame.inc> +#include <asm/serial-regs.h> + + .text + +############################################################################### +# +# GDB stub read memory with guard +# - D0 holds the memory address to read +# - D1 holds the address to store the byte into +# +############################################################################### + .globl gdbstub_read_byte_guard + .globl gdbstub_read_byte_cont +ENTRY(gdbstub_read_byte) + mov d0,a0 + mov d1,a1 + clr d0 +gdbstub_read_byte_guard: + movbu (a0),d1 +gdbstub_read_byte_cont: + movbu d1,(a1) + ret [],0 + + .globl gdbstub_read_word_guard + .globl gdbstub_read_word_cont +ENTRY(gdbstub_read_word) + mov d0,a0 + mov d1,a1 + clr d0 +gdbstub_read_word_guard: + movhu (a0),d1 +gdbstub_read_word_cont: + movhu d1,(a1) + ret [],0 + + .globl gdbstub_read_dword_guard + .globl gdbstub_read_dword_cont +ENTRY(gdbstub_read_dword) + mov d0,a0 + mov d1,a1 + clr d0 +gdbstub_read_dword_guard: + mov (a0),d1 +gdbstub_read_dword_cont: + mov d1,(a1) + ret [],0 + +############################################################################### +# +# GDB stub write memory with guard +# - D0 holds the byte to store +# - D1 holds the memory address to write +# +############################################################################### + .globl gdbstub_write_byte_guard + .globl gdbstub_write_byte_cont +ENTRY(gdbstub_write_byte) + mov d0,a0 + mov d1,a1 + clr d0 +gdbstub_write_byte_guard: + movbu a0,(a1) +gdbstub_write_byte_cont: + ret [],0 + + .globl gdbstub_write_word_guard + .globl gdbstub_write_word_cont +ENTRY(gdbstub_write_word) + mov d0,a0 + mov d1,a1 + clr d0 +gdbstub_write_word_guard: + movhu a0,(a1) +gdbstub_write_word_cont: + ret [],0 + + .globl gdbstub_write_dword_guard + .globl gdbstub_write_dword_cont +ENTRY(gdbstub_write_dword) + mov d0,a0 + mov d1,a1 + clr d0 +gdbstub_write_dword_guard: + mov a0,(a1) +gdbstub_write_dword_cont: + ret [],0 + +############################################################################### +# +# GDB stub BUG() trap +# +############################################################################### +ENTRY(__gdbstub_bug_trap) + .byte 0xF7,0xF7 # don't use 0xFF as the JTAG unit preempts that + ret [],0 diff --git a/arch/mn10300/kernel/gdb-stub.c b/arch/mn10300/kernel/gdb-stub.c new file mode 100644 index 00000000..538266b2 --- /dev/null +++ b/arch/mn10300/kernel/gdb-stub.c @@ -0,0 +1,1924 @@ +/* MN10300 GDB stub + * + * Originally written by Glenn Engel, Lake Stevens Instrument Division + * + * Contributed by HP Systems + * + * Modified for SPARC by Stu Grossman, Cygnus Support. + * + * Modified for Linux/MIPS (and MIPS in general) by Andreas Busse + * Send complaints, suggestions etc. to <andy@waldorf-gmbh.de> + * + * Copyright (C) 1995 Andreas Busse + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Modified for Linux/mn10300 by David Howells <dhowells@redhat.com> + */ + +/* + * To enable debugger support, two things need to happen. One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing a BREAK instruction. + * + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * bBB..BB Set baud rate to BB..BB OK or BNN, then sets + * baud rate + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $<packet info>#<checksum>. + * + * where + * <packet info> :: <characters representing the command or response> + * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>> + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + * + * ============== + * MORE EXAMPLES: + * ============== + * + * For reference -- the following are the steps that one + * company took (RidgeRun Inc) to get remote gdb debugging + * going. In this scenario the host machine was a PC and the + * target platform was a Galileo EVB64120A MIPS evaluation + * board. + * + * Step 1: + * First download gdb-5.0.tar.gz from the internet. + * and then build/install the package. + * + * Example: + * $ tar zxf gdb-5.0.tar.gz + * $ cd gdb-5.0 + * $ ./configure --target=am33_2.0-linux-gnu + * $ make + * $ install + * am33_2.0-linux-gnu-gdb + * + * Step 2: + * Configure linux for remote debugging and build it. + * + * Example: + * $ cd ~/linux + * $ make menuconfig <go to "Kernel Hacking" and turn on remote debugging> + * $ make dep; make vmlinux + * + * Step 3: + * Download the kernel to the remote target and start + * the kernel running. It will promptly halt and wait + * for the host gdb session to connect. It does this + * since the "Kernel Hacking" option has defined + * CONFIG_REMOTE_DEBUG which in turn enables your calls + * to: + * set_debug_traps(); + * breakpoint(); + * + * Step 4: + * Start the gdb session on the host. + * + * Example: + * $ am33_2.0-linux-gnu-gdb vmlinux + * (gdb) set remotebaud 115200 + * (gdb) target remote /dev/ttyS1 + * ...at this point you are connected to + * the remote target and can use gdb + * in the normal fasion. Setting + * breakpoints, single stepping, + * printing variables, etc. + * + */ + +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/console.h> +#include <linux/init.h> +#include <linux/bug.h> + +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/gdb-stub.h> +#include <asm/exceptions.h> +#include <asm/debugger.h> +#include <asm/serial-regs.h> +#include <asm/busctl-regs.h> +#include <unit/leds.h> +#include <unit/serial.h> + +/* define to use F7F7 rather than FF which is subverted by JTAG debugger */ +#undef GDBSTUB_USE_F7F7_AS_BREAKPOINT + +/* + * BUFMAX defines the maximum number of characters in inbound/outbound buffers + * at least NUMREGBYTES*2 are needed for register packets + */ +#define BUFMAX 2048 + +static const char gdbstub_banner[] = + "Linux/MN10300 GDB Stub (c) RedHat 2007\n"; + +u8 gdbstub_rx_buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); +u32 gdbstub_rx_inp; +u32 gdbstub_rx_outp; +u8 gdbstub_busy; +u8 gdbstub_rx_overflow; +u8 gdbstub_rx_unget; + +static u8 gdbstub_flush_caches; +static char input_buffer[BUFMAX]; +static char output_buffer[BUFMAX]; +static char trans_buffer[BUFMAX]; + +struct gdbstub_bkpt { + u8 *addr; /* address of breakpoint */ + u8 len; /* size of breakpoint */ + u8 origbytes[7]; /* original bytes */ +}; + +static struct gdbstub_bkpt gdbstub_bkpts[256]; + +/* + * local prototypes + */ +static void getpacket(char *buffer); +static int putpacket(char *buffer); +static int computeSignal(enum exception_code excep); +static int hex(unsigned char ch); +static int hexToInt(char **ptr, int *intValue); +static unsigned char *mem2hex(const void *mem, char *buf, int count, + int may_fault); +static const char *hex2mem(const char *buf, void *_mem, int count, + int may_fault); + +/* + * Convert ch from a hex digit to an int + */ +static int hex(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'f') + return ch - 'a' + 10; + if (ch >= '0' && ch <= '9') + return ch - '0'; + if (ch >= 'A' && ch <= 'F') + return ch - 'A' + 10; + return -1; +} + +#ifdef CONFIG_GDBSTUB_DEBUGGING + +void debug_to_serial(const char *p, int n) +{ + __debug_to_serial(p, n); + /* gdbstub_console_write(NULL, p, n); */ +} + +void gdbstub_printk(const char *fmt, ...) +{ + va_list args; + int len; + + /* Emit the output into the temporary buffer */ + va_start(args, fmt); + len = vsnprintf(trans_buffer, sizeof(trans_buffer), fmt, args); + va_end(args); + debug_to_serial(trans_buffer, len); +} + +#endif + +static inline char *gdbstub_strcpy(char *dst, const char *src) +{ + int loop = 0; + while ((dst[loop] = src[loop])) + loop++; + return dst; +} + +/* + * scan for the sequence $<data>#<checksum> + */ +static void getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + unsigned char ch; + int count, i, ret, error; + + for (;;) { + /* + * wait around for the start character, + * ignore all other characters + */ + do { + gdbstub_io_rx_char(&ch, 0); + } while (ch != '$'); + + checksum = 0; + xmitcsum = -1; + count = 0; + error = 0; + + /* + * now, read until a # or end of buffer is found + */ + while (count < BUFMAX) { + ret = gdbstub_io_rx_char(&ch, 0); + if (ret < 0) + error = ret; + + if (ch == '#') + break; + checksum += ch; + buffer[count] = ch; + count++; + } + + if (error == -EIO) { + gdbstub_proto("### GDB Rx Error - Skipping packet" + " ###\n"); + gdbstub_proto("### GDB Tx NAK\n"); + gdbstub_io_tx_char('-'); + continue; + } + + if (count >= BUFMAX || error) + continue; + + buffer[count] = 0; + + /* read the checksum */ + ret = gdbstub_io_rx_char(&ch, 0); + if (ret < 0) + error = ret; + xmitcsum = hex(ch) << 4; + + ret = gdbstub_io_rx_char(&ch, 0); + if (ret < 0) + error = ret; + xmitcsum |= hex(ch); + + if (error) { + if (error == -EIO) + gdbstub_io("### GDB Rx Error -" + " Skipping packet\n"); + gdbstub_io("### GDB Tx NAK\n"); + gdbstub_io_tx_char('-'); + continue; + } + + /* check the checksum */ + if (checksum != xmitcsum) { + gdbstub_io("### GDB Tx NAK\n"); + gdbstub_io_tx_char('-'); /* failed checksum */ + continue; + } + + gdbstub_proto("### GDB Rx '$%s#%02x' ###\n", buffer, checksum); + gdbstub_io("### GDB Tx ACK\n"); + gdbstub_io_tx_char('+'); /* successful transfer */ + + /* + * if a sequence char is present, + * reply the sequence ID + */ + if (buffer[2] == ':') { + gdbstub_io_tx_char(buffer[0]); + gdbstub_io_tx_char(buffer[1]); + + /* + * remove sequence chars from buffer + */ + count = 0; + while (buffer[count]) + count++; + for (i = 3; i <= count; i++) + buffer[i - 3] = buffer[i]; + } + + break; + } +} + +/* + * send the packet in buffer. + * - return 0 if successfully ACK'd + * - return 1 if abandoned due to new incoming packet + */ +static int putpacket(char *buffer) +{ + unsigned char checksum; + unsigned char ch; + int count; + + /* + * $<packet info>#<checksum>. + */ + gdbstub_proto("### GDB Tx $'%s'#?? ###\n", buffer); + + do { + gdbstub_io_tx_char('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count]) != 0) { + gdbstub_io_tx_char(ch); + checksum += ch; + count += 1; + } + + gdbstub_io_tx_char('#'); + gdbstub_io_tx_char(hex_asc_hi(checksum)); + gdbstub_io_tx_char(hex_asc_lo(checksum)); + + } while (gdbstub_io_rx_char(&ch, 0), + ch == '-' && (gdbstub_io("### GDB Rx NAK\n"), 0), + ch != '-' && ch != '+' && + (gdbstub_io("### GDB Rx ??? %02x\n", ch), 0), + ch != '+' && ch != '$'); + + if (ch == '+') { + gdbstub_io("### GDB Rx ACK\n"); + return 0; + } + + gdbstub_io("### GDB Tx Abandoned\n"); + gdbstub_rx_unget = ch; + return 1; +} + +/* + * While we find nice hex chars, build an int. + * Return number of chars processed. + */ +static int hexToInt(char **ptr, int *intValue) +{ + int numChars = 0; + int hexValue; + + *intValue = 0; + + while (**ptr) { + hexValue = hex(**ptr); + if (hexValue < 0) + break; + + *intValue = (*intValue << 4) | hexValue; + numChars++; + + (*ptr)++; + } + + return (numChars); +} + +#ifdef CONFIG_GDBSTUB_ALLOW_SINGLE_STEP +/* + * We single-step by setting breakpoints. When an exception + * is handled, we need to restore the instructions hoisted + * when the breakpoints were set. + * + * This is where we save the original instructions. + */ +static struct gdb_bp_save { + u8 *addr; + u8 opcode[2]; +} step_bp[2]; + +static const unsigned char gdbstub_insn_sizes[256] = +{ + /* 1 2 3 4 5 6 7 8 9 a b c d e f */ + 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, /* 0 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 1 */ + 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, /* 2 */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, /* 3 */ + 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, /* 4 */ + 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, /* 5 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 7 */ + 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 8 */ + 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 9 */ + 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* a */ + 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* b */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, /* c */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* d */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* e */ + 0, 2, 2, 2, 2, 2, 2, 4, 0, 3, 0, 4, 0, 6, 7, 1 /* f */ +}; + +static int __gdbstub_mark_bp(u8 *addr, int ix) +{ + /* vmalloc area */ + if (((u8 *) VMALLOC_START <= addr) && (addr < (u8 *) VMALLOC_END)) + goto okay; + /* SRAM, SDRAM */ + if (((u8 *) 0x80000000UL <= addr) && (addr < (u8 *) 0xa0000000UL)) + goto okay; + return 0; + +okay: + if (gdbstub_read_byte(addr + 0, &step_bp[ix].opcode[0]) < 0 || + gdbstub_read_byte(addr + 1, &step_bp[ix].opcode[1]) < 0) + return 0; + + step_bp[ix].addr = addr; + return 1; +} + +static inline void __gdbstub_restore_bp(void) +{ +#ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT + if (step_bp[0].addr) { + gdbstub_write_byte(step_bp[0].opcode[0], step_bp[0].addr + 0); + gdbstub_write_byte(step_bp[0].opcode[1], step_bp[0].addr + 1); + } + if (step_bp[1].addr) { + gdbstub_write_byte(step_bp[1].opcode[0], step_bp[1].addr + 0); + gdbstub_write_byte(step_bp[1].opcode[1], step_bp[1].addr + 1); + } +#else + if (step_bp[0].addr) + gdbstub_write_byte(step_bp[0].opcode[0], step_bp[0].addr + 0); + if (step_bp[1].addr) + gdbstub_write_byte(step_bp[1].opcode[0], step_bp[1].addr + 0); +#endif + + gdbstub_flush_caches = 1; + + step_bp[0].addr = NULL; + step_bp[0].opcode[0] = 0; + step_bp[0].opcode[1] = 0; + step_bp[1].addr = NULL; + step_bp[1].opcode[0] = 0; + step_bp[1].opcode[1] = 0; +} + +/* + * emulate single stepping by means of breakpoint instructions + */ +static int gdbstub_single_step(struct pt_regs *regs) +{ + unsigned size; + uint32_t x; + uint8_t cur, *pc, *sp; + + step_bp[0].addr = NULL; + step_bp[0].opcode[0] = 0; + step_bp[0].opcode[1] = 0; + step_bp[1].addr = NULL; + step_bp[1].opcode[0] = 0; + step_bp[1].opcode[1] = 0; + x = 0; + + pc = (u8 *) regs->pc; + sp = (u8 *) (regs + 1); + if (gdbstub_read_byte(pc, &cur) < 0) + return -EFAULT; + + gdbstub_bkpt("Single Step from %p { %02x }\n", pc, cur); + + gdbstub_flush_caches = 1; + + size = gdbstub_insn_sizes[cur]; + if (size > 0) { + if (!__gdbstub_mark_bp(pc + size, 0)) + goto fault; + } else { + switch (cur) { + /* Bxx (d8,PC) */ + case 0xc0 ... 0xca: + if (gdbstub_read_byte(pc + 1, (u8 *) &x) < 0) + goto fault; + if (!__gdbstub_mark_bp(pc + 2, 0)) + goto fault; + if ((x < 0 || x > 2) && + !__gdbstub_mark_bp(pc + (s8) x, 1)) + goto fault; + break; + + /* LXX (d8,PC) */ + case 0xd0 ... 0xda: + if (!__gdbstub_mark_bp(pc + 1, 0)) + goto fault; + if (regs->pc != regs->lar && + !__gdbstub_mark_bp((u8 *) regs->lar, 1)) + goto fault; + break; + + /* SETLB - loads the next for bytes into the LIR + * register */ + case 0xdb: + if (!__gdbstub_mark_bp(pc + 1, 0)) + goto fault; + break; + + /* JMP (d16,PC) or CALL (d16,PC) */ + case 0xcc: + case 0xcd: + if (gdbstub_read_byte(pc + 1, ((u8 *) &x) + 0) < 0 || + gdbstub_read_byte(pc + 2, ((u8 *) &x) + 1) < 0) + goto fault; + if (!__gdbstub_mark_bp(pc + (s16) x, 0)) + goto fault; + break; + + /* JMP (d32,PC) or CALL (d32,PC) */ + case 0xdc: + case 0xdd: + if (gdbstub_read_byte(pc + 1, ((u8 *) &x) + 0) < 0 || + gdbstub_read_byte(pc + 2, ((u8 *) &x) + 1) < 0 || + gdbstub_read_byte(pc + 3, ((u8 *) &x) + 2) < 0 || + gdbstub_read_byte(pc + 4, ((u8 *) &x) + 3) < 0) + goto fault; + if (!__gdbstub_mark_bp(pc + (s32) x, 0)) + goto fault; + break; + + /* RETF */ + case 0xde: + if (!__gdbstub_mark_bp((u8 *) regs->mdr, 0)) + goto fault; + break; + + /* RET */ + case 0xdf: + if (gdbstub_read_byte(pc + 2, (u8 *) &x) < 0) + goto fault; + sp += (s8)x; + if (gdbstub_read_byte(sp + 0, ((u8 *) &x) + 0) < 0 || + gdbstub_read_byte(sp + 1, ((u8 *) &x) + 1) < 0 || + gdbstub_read_byte(sp + 2, ((u8 *) &x) + 2) < 0 || + gdbstub_read_byte(sp + 3, ((u8 *) &x) + 3) < 0) + goto fault; + if (!__gdbstub_mark_bp((u8 *) x, 0)) + goto fault; + break; + + case 0xf0: + if (gdbstub_read_byte(pc + 1, &cur) < 0) + goto fault; + + if (cur >= 0xf0 && cur <= 0xf7) { + /* JMP (An) / CALLS (An) */ + switch (cur & 3) { + case 0: x = regs->a0; break; + case 1: x = regs->a1; break; + case 2: x = regs->a2; break; + case 3: x = regs->a3; break; + } + if (!__gdbstub_mark_bp((u8 *) x, 0)) + goto fault; + } else if (cur == 0xfc) { + /* RETS */ + if (gdbstub_read_byte( + sp + 0, ((u8 *) &x) + 0) < 0 || + gdbstub_read_byte( + sp + 1, ((u8 *) &x) + 1) < 0 || + gdbstub_read_byte( + sp + 2, ((u8 *) &x) + 2) < 0 || + gdbstub_read_byte( + sp + 3, ((u8 *) &x) + 3) < 0) + goto fault; + if (!__gdbstub_mark_bp((u8 *) x, 0)) + goto fault; + } else if (cur == 0xfd) { + /* RTI */ + if (gdbstub_read_byte( + sp + 4, ((u8 *) &x) + 0) < 0 || + gdbstub_read_byte( + sp + 5, ((u8 *) &x) + 1) < 0 || + gdbstub_read_byte( + sp + 6, ((u8 *) &x) + 2) < 0 || + gdbstub_read_byte( + sp + 7, ((u8 *) &x) + 3) < 0) + goto fault; + if (!__gdbstub_mark_bp((u8 *) x, 0)) + goto fault; + } else { + if (!__gdbstub_mark_bp(pc + 2, 0)) + goto fault; + } + + break; + + /* potential 3-byte conditional branches */ + case 0xf8: + if (gdbstub_read_byte(pc + 1, &cur) < 0) + goto fault; + if (!__gdbstub_mark_bp(pc + 3, 0)) + goto fault; + + if (cur >= 0xe8 && cur <= 0xeb) { + if (gdbstub_read_byte( + pc + 2, ((u8 *) &x) + 0) < 0) + goto fault; + if ((x < 0 || x > 3) && + !__gdbstub_mark_bp(pc + (s8) x, 1)) + goto fault; + } + break; + + case 0xfa: + if (gdbstub_read_byte(pc + 1, &cur) < 0) + goto fault; + + if (cur == 0xff) { + /* CALLS (d16,PC) */ + if (gdbstub_read_byte( + pc + 2, ((u8 *) &x) + 0) < 0 || + gdbstub_read_byte( + pc + 3, ((u8 *) &x) + 1) < 0) + goto fault; + if (!__gdbstub_mark_bp(pc + (s16) x, 0)) + goto fault; + } else { + if (!__gdbstub_mark_bp(pc + 4, 0)) + goto fault; + } + break; + + case 0xfc: + if (gdbstub_read_byte(pc + 1, &cur) < 0) + goto fault; + if (cur == 0xff) { + /* CALLS (d32,PC) */ + if (gdbstub_read_byte( + pc + 2, ((u8 *) &x) + 0) < 0 || + gdbstub_read_byte( + pc + 3, ((u8 *) &x) + 1) < 0 || + gdbstub_read_byte( + pc + 4, ((u8 *) &x) + 2) < 0 || + gdbstub_read_byte( + pc + 5, ((u8 *) &x) + 3) < 0) + goto fault; + if (!__gdbstub_mark_bp( + pc + (s32) x, 0)) + goto fault; + } else { + if (!__gdbstub_mark_bp( + pc + 6, 0)) + goto fault; + } + break; + + } + } + + gdbstub_bkpt("Step: %02x at %p; %02x at %p\n", + step_bp[0].opcode[0], step_bp[0].addr, + step_bp[1].opcode[0], step_bp[1].addr); + + if (step_bp[0].addr) { +#ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT + if (gdbstub_write_byte(0xF7, step_bp[0].addr + 0) < 0 || + gdbstub_write_byte(0xF7, step_bp[0].addr + 1) < 0) + goto fault; +#else + if (gdbstub_write_byte(0xFF, step_bp[0].addr + 0) < 0) + goto fault; +#endif + } + + if (step_bp[1].addr) { +#ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT + if (gdbstub_write_byte(0xF7, step_bp[1].addr + 0) < 0 || + gdbstub_write_byte(0xF7, step_bp[1].addr + 1) < 0) + goto fault; +#else + if (gdbstub_write_byte(0xFF, step_bp[1].addr + 0) < 0) + goto fault; +#endif + } + + return 0; + + fault: + /* uh-oh - silly address alert, try and restore things */ + __gdbstub_restore_bp(); + return -EFAULT; +} +#endif /* CONFIG_GDBSTUB_ALLOW_SINGLE_STEP */ + +#ifdef CONFIG_GDBSTUB_CONSOLE + +void gdbstub_console_write(struct console *con, const char *p, unsigned n) +{ + static const char gdbstub_cr[] = { 0x0d }; + char outbuf[26]; + int qty; + u8 busy; + + busy = gdbstub_busy; + gdbstub_busy = 1; + + outbuf[0] = 'O'; + + while (n > 0) { + qty = 1; + + while (n > 0 && qty < 20) { + mem2hex(p, outbuf + qty, 2, 0); + qty += 2; + if (*p == 0x0a) { + mem2hex(gdbstub_cr, outbuf + qty, 2, 0); + qty += 2; + } + p++; + n--; + } + + outbuf[qty] = 0; + putpacket(outbuf); + } + + gdbstub_busy = busy; +} + +static kdev_t gdbstub_console_dev(struct console *con) +{ + return MKDEV(1, 3); /* /dev/null */ +} + +static struct console gdbstub_console = { + .name = "gdb", + .write = gdbstub_console_write, + .device = gdbstub_console_dev, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +#endif + +/* + * Convert the memory pointed to by mem into hex, placing result in buf. + * - if successful, return a pointer to the last char put in buf (NUL) + * - in case of mem fault, return NULL + * may_fault is non-zero if we are reading from arbitrary memory, but is + * currently not used. + */ +static +unsigned char *mem2hex(const void *_mem, char *buf, int count, int may_fault) +{ + const u8 *mem = _mem; + u8 ch[4]; + + if ((u32) mem & 1 && count >= 1) { + if (gdbstub_read_byte(mem, ch) != 0) + return 0; + buf = pack_hex_byte(buf, ch[0]); + mem++; + count--; + } + + if ((u32) mem & 3 && count >= 2) { + if (gdbstub_read_word(mem, ch) != 0) + return 0; + buf = pack_hex_byte(buf, ch[0]); + buf = pack_hex_byte(buf, ch[1]); + mem += 2; + count -= 2; + } + + while (count >= 4) { + if (gdbstub_read_dword(mem, ch) != 0) + return 0; + buf = pack_hex_byte(buf, ch[0]); + buf = pack_hex_byte(buf, ch[1]); + buf = pack_hex_byte(buf, ch[2]); + buf = pack_hex_byte(buf, ch[3]); + mem += 4; + count -= 4; + } + + if (count >= 2) { + if (gdbstub_read_word(mem, ch) != 0) + return 0; + buf = pack_hex_byte(buf, ch[0]); + buf = pack_hex_byte(buf, ch[1]); + mem += 2; + count -= 2; + } + + if (count >= 1) { + if (gdbstub_read_byte(mem, ch) != 0) + return 0; + buf = pack_hex_byte(buf, ch[0]); + } + + *buf = 0; + return buf; +} + +/* + * convert the hex array pointed to by buf into binary to be placed in mem + * return a pointer to the character AFTER the last byte written + * may_fault is non-zero if we are reading from arbitrary memory, but is + * currently not used. + */ +static +const char *hex2mem(const char *buf, void *_mem, int count, int may_fault) +{ + u8 *mem = _mem; + union { + u32 val; + u8 b[4]; + } ch; + + if ((u32) mem & 1 && count >= 1) { + ch.b[0] = hex(*buf++) << 4; + ch.b[0] |= hex(*buf++); + if (gdbstub_write_byte(ch.val, mem) != 0) + return 0; + mem++; + count--; + } + + if ((u32) mem & 3 && count >= 2) { + ch.b[0] = hex(*buf++) << 4; + ch.b[0] |= hex(*buf++); + ch.b[1] = hex(*buf++) << 4; + ch.b[1] |= hex(*buf++); + if (gdbstub_write_word(ch.val, mem) != 0) + return 0; + mem += 2; + count -= 2; + } + + while (count >= 4) { + ch.b[0] = hex(*buf++) << 4; + ch.b[0] |= hex(*buf++); + ch.b[1] = hex(*buf++) << 4; + ch.b[1] |= hex(*buf++); + ch.b[2] = hex(*buf++) << 4; + ch.b[2] |= hex(*buf++); + ch.b[3] = hex(*buf++) << 4; + ch.b[3] |= hex(*buf++); + if (gdbstub_write_dword(ch.val, mem) != 0) + return 0; + mem += 4; + count -= 4; + } + + if (count >= 2) { + ch.b[0] = hex(*buf++) << 4; + ch.b[0] |= hex(*buf++); + ch.b[1] = hex(*buf++) << 4; + ch.b[1] |= hex(*buf++); + if (gdbstub_write_word(ch.val, mem) != 0) + return 0; + mem += 2; + count -= 2; + } + + if (count >= 1) { + ch.b[0] = hex(*buf++) << 4; + ch.b[0] |= hex(*buf++); + if (gdbstub_write_byte(ch.val, mem) != 0) + return 0; + } + + return buf; +} + +/* + * This table contains the mapping between MN10300 exception codes, and + * signals, which are primarily what GDB understands. It also indicates + * which hardware traps we need to commandeer when initializing the stub. + */ +static const struct excep_to_sig_map { + enum exception_code excep; /* MN10300 exception code */ + unsigned char signo; /* Signal that we map this into */ +} excep_to_sig_map[] = { + { EXCEP_ITLBMISS, SIGSEGV }, + { EXCEP_DTLBMISS, SIGSEGV }, + { EXCEP_TRAP, SIGTRAP }, + { EXCEP_ISTEP, SIGTRAP }, + { EXCEP_IBREAK, SIGTRAP }, + { EXCEP_OBREAK, SIGTRAP }, + { EXCEP_UNIMPINS, SIGILL }, + { EXCEP_UNIMPEXINS, SIGILL }, + { EXCEP_MEMERR, SIGSEGV }, + { EXCEP_MISALIGN, SIGSEGV }, + { EXCEP_BUSERROR, SIGBUS }, + { EXCEP_ILLINSACC, SIGSEGV }, + { EXCEP_ILLDATACC, SIGSEGV }, + { EXCEP_IOINSACC, SIGSEGV }, + { EXCEP_PRIVINSACC, SIGSEGV }, + { EXCEP_PRIVDATACC, SIGSEGV }, + { EXCEP_FPU_DISABLED, SIGFPE }, + { EXCEP_FPU_UNIMPINS, SIGFPE }, + { EXCEP_FPU_OPERATION, SIGFPE }, + { EXCEP_WDT, SIGALRM }, + { EXCEP_NMI, SIGQUIT }, + { EXCEP_IRQ_LEVEL0, SIGINT }, + { EXCEP_IRQ_LEVEL1, SIGINT }, + { EXCEP_IRQ_LEVEL2, SIGINT }, + { EXCEP_IRQ_LEVEL3, SIGINT }, + { EXCEP_IRQ_LEVEL4, SIGINT }, + { EXCEP_IRQ_LEVEL5, SIGINT }, + { EXCEP_IRQ_LEVEL6, SIGINT }, + { 0, 0} +}; + +/* + * convert the MN10300 exception code into a UNIX signal number + */ +static int computeSignal(enum exception_code excep) +{ + const struct excep_to_sig_map *map; + + for (map = excep_to_sig_map; map->signo; map++) + if (map->excep == excep) + return map->signo; + + return SIGHUP; /* default for things we don't know about */ +} + +static u32 gdbstub_fpcr, gdbstub_fpufs_array[32]; + +/* + * + */ +static void gdbstub_store_fpu(void) +{ +#ifdef CONFIG_FPU + + asm volatile( + "or %2,epsw\n" +#ifdef CONFIG_MN10300_PROC_MN103E010 + "nop\n" + "nop\n" +#endif + "mov %1, a1\n" + "fmov fs0, (a1+)\n" + "fmov fs1, (a1+)\n" + "fmov fs2, (a1+)\n" + "fmov fs3, (a1+)\n" + "fmov fs4, (a1+)\n" + "fmov fs5, (a1+)\n" + "fmov fs6, (a1+)\n" + "fmov fs7, (a1+)\n" + "fmov fs8, (a1+)\n" + "fmov fs9, (a1+)\n" + "fmov fs10, (a1+)\n" + "fmov fs11, (a1+)\n" + "fmov fs12, (a1+)\n" + "fmov fs13, (a1+)\n" + "fmov fs14, (a1+)\n" + "fmov fs15, (a1+)\n" + "fmov fs16, (a1+)\n" + "fmov fs17, (a1+)\n" + "fmov fs18, (a1+)\n" + "fmov fs19, (a1+)\n" + "fmov fs20, (a1+)\n" + "fmov fs21, (a1+)\n" + "fmov fs22, (a1+)\n" + "fmov fs23, (a1+)\n" + "fmov fs24, (a1+)\n" + "fmov fs25, (a1+)\n" + "fmov fs26, (a1+)\n" + "fmov fs27, (a1+)\n" + "fmov fs28, (a1+)\n" + "fmov fs29, (a1+)\n" + "fmov fs30, (a1+)\n" + "fmov fs31, (a1+)\n" + "fmov fpcr, %0\n" + : "=d"(gdbstub_fpcr) + : "g" (&gdbstub_fpufs_array), "i"(EPSW_FE) + : "a1" + ); +#endif +} + +/* + * + */ +static void gdbstub_load_fpu(void) +{ +#ifdef CONFIG_FPU + + asm volatile( + "or %1,epsw\n" +#ifdef CONFIG_MN10300_PROC_MN103E010 + "nop\n" + "nop\n" +#endif + "mov %0, a1\n" + "fmov (a1+), fs0\n" + "fmov (a1+), fs1\n" + "fmov (a1+), fs2\n" + "fmov (a1+), fs3\n" + "fmov (a1+), fs4\n" + "fmov (a1+), fs5\n" + "fmov (a1+), fs6\n" + "fmov (a1+), fs7\n" + "fmov (a1+), fs8\n" + "fmov (a1+), fs9\n" + "fmov (a1+), fs10\n" + "fmov (a1+), fs11\n" + "fmov (a1+), fs12\n" + "fmov (a1+), fs13\n" + "fmov (a1+), fs14\n" + "fmov (a1+), fs15\n" + "fmov (a1+), fs16\n" + "fmov (a1+), fs17\n" + "fmov (a1+), fs18\n" + "fmov (a1+), fs19\n" + "fmov (a1+), fs20\n" + "fmov (a1+), fs21\n" + "fmov (a1+), fs22\n" + "fmov (a1+), fs23\n" + "fmov (a1+), fs24\n" + "fmov (a1+), fs25\n" + "fmov (a1+), fs26\n" + "fmov (a1+), fs27\n" + "fmov (a1+), fs28\n" + "fmov (a1+), fs29\n" + "fmov (a1+), fs30\n" + "fmov (a1+), fs31\n" + "fmov %2, fpcr\n" + : + : "g" (&gdbstub_fpufs_array), "i"(EPSW_FE), "d"(gdbstub_fpcr) + : "a1" + ); +#endif +} + +/* + * set a software breakpoint + */ +int gdbstub_set_breakpoint(u8 *addr, int len) +{ + int bkpt, loop, xloop; + +#ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT + len = (len + 1) & ~1; +#endif + + gdbstub_bkpt("setbkpt(%p,%d)\n", addr, len); + + for (bkpt = 255; bkpt >= 0; bkpt--) + if (!gdbstub_bkpts[bkpt].addr) + break; + if (bkpt < 0) + return -ENOSPC; + + for (loop = 0; loop < len; loop++) + if (gdbstub_read_byte(&addr[loop], + &gdbstub_bkpts[bkpt].origbytes[loop] + ) < 0) + return -EFAULT; + + gdbstub_flush_caches = 1; + +#ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT + for (loop = 0; loop < len; loop++) + if (gdbstub_write_byte(0xF7, &addr[loop]) < 0) + goto restore; +#else + for (loop = 0; loop < len; loop++) + if (gdbstub_write_byte(0xFF, &addr[loop]) < 0) + goto restore; +#endif + + gdbstub_bkpts[bkpt].addr = addr; + gdbstub_bkpts[bkpt].len = len; + + gdbstub_bkpt("Set BKPT[%02x]: %p-%p {%02x%02x%02x%02x%02x%02x%02x}\n", + bkpt, + gdbstub_bkpts[bkpt].addr, + gdbstub_bkpts[bkpt].addr + gdbstub_bkpts[bkpt].len - 1, + gdbstub_bkpts[bkpt].origbytes[0], + gdbstub_bkpts[bkpt].origbytes[1], + gdbstub_bkpts[bkpt].origbytes[2], + gdbstub_bkpts[bkpt].origbytes[3], + gdbstub_bkpts[bkpt].origbytes[4], + gdbstub_bkpts[bkpt].origbytes[5], + gdbstub_bkpts[bkpt].origbytes[6] + ); + + return 0; + +restore: + for (xloop = 0; xloop < loop; xloop++) + gdbstub_write_byte(gdbstub_bkpts[bkpt].origbytes[xloop], + addr + xloop); + return -EFAULT; +} + +/* + * clear a software breakpoint + */ +int gdbstub_clear_breakpoint(u8 *addr, int len) +{ + int bkpt, loop; + +#ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT + len = (len + 1) & ~1; +#endif + + gdbstub_bkpt("clearbkpt(%p,%d)\n", addr, len); + + for (bkpt = 255; bkpt >= 0; bkpt--) + if (gdbstub_bkpts[bkpt].addr == addr && + gdbstub_bkpts[bkpt].len == len) + break; + if (bkpt < 0) + return -ENOENT; + + gdbstub_bkpts[bkpt].addr = NULL; + + gdbstub_flush_caches = 1; + + for (loop = 0; loop < len; loop++) + if (gdbstub_write_byte(gdbstub_bkpts[bkpt].origbytes[loop], + addr + loop) < 0) + return -EFAULT; + + return 0; +} + +/* + * This function does all command processing for interfacing to gdb + * - returns 0 if the exception should be skipped, -ERROR otherwise. + */ +static int gdbstub(struct pt_regs *regs, enum exception_code excep) +{ + unsigned long *stack; + unsigned long epsw, mdr; + uint32_t zero, ssp; + uint8_t broke; + char *ptr; + int sigval; + int addr; + int length; + int loop; + + if (excep == EXCEP_FPU_DISABLED) + return -ENOTSUPP; + + gdbstub_flush_caches = 0; + + mn10300_set_gdbleds(1); + + asm volatile("mov mdr,%0" : "=d"(mdr)); + local_save_flags(epsw); + arch_local_change_intr_mask_level( + NUM2EPSW_IM(CONFIG_DEBUGGER_IRQ_LEVEL + 1)); + + gdbstub_store_fpu(); + +#ifdef CONFIG_GDBSTUB_IMMEDIATE + /* skip the initial pause loop */ + if (regs->pc == (unsigned long) __gdbstub_pause) + regs->pc = (unsigned long) start_kernel; +#endif + + /* if we were single stepping, restore the opcodes hoisted for the + * breakpoint[s] */ + broke = 0; +#ifdef CONFIG_GDBSTUB_ALLOW_SINGLE_STEP + if ((step_bp[0].addr && step_bp[0].addr == (u8 *) regs->pc) || + (step_bp[1].addr && step_bp[1].addr == (u8 *) regs->pc)) + broke = 1; + + __gdbstub_restore_bp(); +#endif + + if (gdbstub_rx_unget) { + sigval = SIGINT; + if (gdbstub_rx_unget != 3) + goto packet_waiting; + gdbstub_rx_unget = 0; + } + + stack = (unsigned long *) regs->sp; + sigval = broke ? SIGTRAP : computeSignal(excep); + + /* send information about a BUG() */ + if (!user_mode(regs) && excep == EXCEP_SYSCALL15) { + const struct bug_entry *bug; + + bug = find_bug(regs->pc); + if (bug) + goto found_bug; + length = snprintf(trans_buffer, sizeof(trans_buffer), + "BUG() at address %lx\n", regs->pc); + goto send_bug_pkt; + + found_bug: + length = snprintf(trans_buffer, sizeof(trans_buffer), + "BUG() at address %lx (%s:%d)\n", + regs->pc, bug->file, bug->line); + + send_bug_pkt: + ptr = output_buffer; + *ptr++ = 'O'; + ptr = mem2hex(trans_buffer, ptr, length, 0); + *ptr = 0; + putpacket(output_buffer); + + regs->pc -= 2; + sigval = SIGABRT; + } else if (regs->pc == (unsigned long) __gdbstub_bug_trap) { + regs->pc = regs->mdr; + sigval = SIGABRT; + } + + /* + * send a message to the debugger's user saying what happened if it may + * not be clear cut (we can't map exceptions onto signals properly) + */ + if (sigval != SIGINT && sigval != SIGTRAP && sigval != SIGILL) { + static const char title[] = "Excep ", tbcberr[] = "BCBERR "; + static const char crlf[] = "\r\n"; + char hx; + u32 bcberr = BCBERR; + + ptr = output_buffer; + *ptr++ = 'O'; + ptr = mem2hex(title, ptr, sizeof(title) - 1, 0); + + hx = hex_asc_hi(excep >> 8); + ptr = pack_hex_byte(ptr, hx); + hx = hex_asc_lo(excep >> 8); + ptr = pack_hex_byte(ptr, hx); + hx = hex_asc_hi(excep); + ptr = pack_hex_byte(ptr, hx); + hx = hex_asc_lo(excep); + ptr = pack_hex_byte(ptr, hx); + + ptr = mem2hex(crlf, ptr, sizeof(crlf) - 1, 0); + *ptr = 0; + putpacket(output_buffer); /* send it off... */ + + /* BCBERR */ + ptr = output_buffer; + *ptr++ = 'O'; + ptr = mem2hex(tbcberr, ptr, sizeof(tbcberr) - 1, 0); + + hx = hex_asc_hi(bcberr >> 24); + ptr = pack_hex_byte(ptr, hx); + hx = hex_asc_lo(bcberr >> 24); + ptr = pack_hex_byte(ptr, hx); + hx = hex_asc_hi(bcberr >> 16); + ptr = pack_hex_byte(ptr, hx); + hx = hex_asc_lo(bcberr >> 16); + ptr = pack_hex_byte(ptr, hx); + hx = hex_asc_hi(bcberr >> 8); + ptr = pack_hex_byte(ptr, hx); + hx = hex_asc_lo(bcberr >> 8); + ptr = pack_hex_byte(ptr, hx); + hx = hex_asc_hi(bcberr); + ptr = pack_hex_byte(ptr, hx); + hx = hex_asc_lo(bcberr); + ptr = pack_hex_byte(ptr, hx); + + ptr = mem2hex(crlf, ptr, sizeof(crlf) - 1, 0); + *ptr = 0; + putpacket(output_buffer); /* send it off... */ + } + + /* + * tell the debugger that an exception has occurred + */ + ptr = output_buffer; + + /* + * Send trap type (converted to signal) + */ + *ptr++ = 'T'; + ptr = pack_hex_byte(ptr, sigval); + + /* + * Send Error PC + */ + ptr = pack_hex_byte(ptr, GDB_REGID_PC); + *ptr++ = ':'; + ptr = mem2hex(®s->pc, ptr, 4, 0); + *ptr++ = ';'; + + /* + * Send frame pointer + */ + ptr = pack_hex_byte(ptr, GDB_REGID_FP); + *ptr++ = ':'; + ptr = mem2hex(®s->a3, ptr, 4, 0); + *ptr++ = ';'; + + /* + * Send stack pointer + */ + ssp = (unsigned long) (regs + 1); + ptr = pack_hex_byte(ptr, GDB_REGID_SP); + *ptr++ = ':'; + ptr = mem2hex(&ssp, ptr, 4, 0); + *ptr++ = ';'; + + *ptr++ = 0; + putpacket(output_buffer); /* send it off... */ + +packet_waiting: + /* + * Wait for input from remote GDB + */ + while (1) { + output_buffer[0] = 0; + getpacket(input_buffer); + + switch (input_buffer[0]) { + /* request repeat of last signal number */ + case '?': + output_buffer[0] = 'S'; + output_buffer[1] = hex_asc_hi(sigval); + output_buffer[2] = hex_asc_lo(sigval); + output_buffer[3] = 0; + break; + + case 'd': + /* toggle debug flag */ + break; + + /* + * Return the value of the CPU registers + */ + case 'g': + zero = 0; + ssp = (u32) (regs + 1); + ptr = output_buffer; + ptr = mem2hex(®s->d0, ptr, 4, 0); + ptr = mem2hex(®s->d1, ptr, 4, 0); + ptr = mem2hex(®s->d2, ptr, 4, 0); + ptr = mem2hex(®s->d3, ptr, 4, 0); + ptr = mem2hex(®s->a0, ptr, 4, 0); + ptr = mem2hex(®s->a1, ptr, 4, 0); + ptr = mem2hex(®s->a2, ptr, 4, 0); + ptr = mem2hex(®s->a3, ptr, 4, 0); + + ptr = mem2hex(&ssp, ptr, 4, 0); /* 8 */ + ptr = mem2hex(®s->pc, ptr, 4, 0); + ptr = mem2hex(®s->mdr, ptr, 4, 0); + ptr = mem2hex(®s->epsw, ptr, 4, 0); + ptr = mem2hex(®s->lir, ptr, 4, 0); + ptr = mem2hex(®s->lar, ptr, 4, 0); + ptr = mem2hex(®s->mdrq, ptr, 4, 0); + + ptr = mem2hex(®s->e0, ptr, 4, 0); /* 15 */ + ptr = mem2hex(®s->e1, ptr, 4, 0); + ptr = mem2hex(®s->e2, ptr, 4, 0); + ptr = mem2hex(®s->e3, ptr, 4, 0); + ptr = mem2hex(®s->e4, ptr, 4, 0); + ptr = mem2hex(®s->e5, ptr, 4, 0); + ptr = mem2hex(®s->e6, ptr, 4, 0); + ptr = mem2hex(®s->e7, ptr, 4, 0); + + ptr = mem2hex(&ssp, ptr, 4, 0); + ptr = mem2hex(®s, ptr, 4, 0); + ptr = mem2hex(®s->sp, ptr, 4, 0); + ptr = mem2hex(®s->mcrh, ptr, 4, 0); /* 26 */ + ptr = mem2hex(®s->mcrl, ptr, 4, 0); + ptr = mem2hex(®s->mcvf, ptr, 4, 0); + + ptr = mem2hex(&gdbstub_fpcr, ptr, 4, 0); /* 29 - FPCR */ + ptr = mem2hex(&zero, ptr, 4, 0); + ptr = mem2hex(&zero, ptr, 4, 0); + for (loop = 0; loop < 32; loop++) + ptr = mem2hex(&gdbstub_fpufs_array[loop], + ptr, 4, 0); /* 32 - FS0-31 */ + + break; + + /* + * set the value of the CPU registers - return OK + */ + case 'G': + { + const char *ptr; + + ptr = &input_buffer[1]; + ptr = hex2mem(ptr, ®s->d0, 4, 0); + ptr = hex2mem(ptr, ®s->d1, 4, 0); + ptr = hex2mem(ptr, ®s->d2, 4, 0); + ptr = hex2mem(ptr, ®s->d3, 4, 0); + ptr = hex2mem(ptr, ®s->a0, 4, 0); + ptr = hex2mem(ptr, ®s->a1, 4, 0); + ptr = hex2mem(ptr, ®s->a2, 4, 0); + ptr = hex2mem(ptr, ®s->a3, 4, 0); + + ptr = hex2mem(ptr, &ssp, 4, 0); /* 8 */ + ptr = hex2mem(ptr, ®s->pc, 4, 0); + ptr = hex2mem(ptr, ®s->mdr, 4, 0); + ptr = hex2mem(ptr, ®s->epsw, 4, 0); + ptr = hex2mem(ptr, ®s->lir, 4, 0); + ptr = hex2mem(ptr, ®s->lar, 4, 0); + ptr = hex2mem(ptr, ®s->mdrq, 4, 0); + + ptr = hex2mem(ptr, ®s->e0, 4, 0); /* 15 */ + ptr = hex2mem(ptr, ®s->e1, 4, 0); + ptr = hex2mem(ptr, ®s->e2, 4, 0); + ptr = hex2mem(ptr, ®s->e3, 4, 0); + ptr = hex2mem(ptr, ®s->e4, 4, 0); + ptr = hex2mem(ptr, ®s->e5, 4, 0); + ptr = hex2mem(ptr, ®s->e6, 4, 0); + ptr = hex2mem(ptr, ®s->e7, 4, 0); + + ptr = hex2mem(ptr, &ssp, 4, 0); + ptr = hex2mem(ptr, &zero, 4, 0); + ptr = hex2mem(ptr, ®s->sp, 4, 0); + ptr = hex2mem(ptr, ®s->mcrh, 4, 0); /* 26 */ + ptr = hex2mem(ptr, ®s->mcrl, 4, 0); + ptr = hex2mem(ptr, ®s->mcvf, 4, 0); + + ptr = hex2mem(ptr, &zero, 4, 0); /* 29 - FPCR */ + ptr = hex2mem(ptr, &zero, 4, 0); + ptr = hex2mem(ptr, &zero, 4, 0); + for (loop = 0; loop < 32; loop++) /* 32 - FS0-31 */ + ptr = hex2mem(ptr, &zero, 4, 0); + +#if 0 + /* + * See if the stack pointer has moved. If so, then copy + * the saved locals and ins to the new location. + */ + unsigned long *newsp = (unsigned long *) registers[SP]; + if (sp != newsp) + sp = memcpy(newsp, sp, 16 * 4); +#endif + + gdbstub_strcpy(output_buffer, "OK"); + } + break; + + /* + * mAA..AA,LLLL Read LLLL bytes at address AA..AA + */ + case 'm': + ptr = &input_buffer[1]; + + if (hexToInt(&ptr, &addr) && + *ptr++ == ',' && + hexToInt(&ptr, &length) + ) { + if (mem2hex((char *) addr, output_buffer, + length, 1)) + break; + gdbstub_strcpy(output_buffer, "E03"); + } else { + gdbstub_strcpy(output_buffer, "E01"); + } + break; + + /* + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA + * return OK + */ + case 'M': + ptr = &input_buffer[1]; + + if (hexToInt(&ptr, &addr) && + *ptr++ == ',' && + hexToInt(&ptr, &length) && + *ptr++ == ':' + ) { + if (hex2mem(ptr, (char *) addr, length, 1)) + gdbstub_strcpy(output_buffer, "OK"); + else + gdbstub_strcpy(output_buffer, "E03"); + + gdbstub_flush_caches = 1; + } else { + gdbstub_strcpy(output_buffer, "E02"); + } + break; + + /* + * cAA..AA Continue at address AA..AA(optional) + */ + case 'c': + /* try to read optional parameter, pc unchanged if no + * parm */ + + ptr = &input_buffer[1]; + if (hexToInt(&ptr, &addr)) + regs->pc = addr; + goto done; + + /* + * kill the program + */ + case 'k' : + goto done; /* just continue */ + + /* + * Reset the whole machine (FIXME: system dependent) + */ + case 'r': + break; + + /* + * Step to next instruction + */ + case 's': + /* Using the T flag doesn't seem to perform single + * stepping (it seems to wind up being caught by the + * JTAG unit), so we have to use breakpoints and + * continue instead. + */ +#ifdef CONFIG_GDBSTUB_ALLOW_SINGLE_STEP + if (gdbstub_single_step(regs) < 0) + /* ignore any fault error for now */ + gdbstub_printk("unable to set single-step" + " bp\n"); + goto done; +#else + gdbstub_strcpy(output_buffer, "E01"); + break; +#endif + + /* + * Set baud rate (bBB) + */ + case 'b': + do { + int baudrate; + + ptr = &input_buffer[1]; + if (!hexToInt(&ptr, &baudrate)) { + gdbstub_strcpy(output_buffer, "B01"); + break; + } + + if (baudrate) { + /* ACK before changing speed */ + putpacket("OK"); + gdbstub_io_set_baud(baudrate); + } + } while (0); + break; + + /* + * Set breakpoint + */ + case 'Z': + ptr = &input_buffer[1]; + + if (!hexToInt(&ptr, &loop) || *ptr++ != ',' || + !hexToInt(&ptr, &addr) || *ptr++ != ',' || + !hexToInt(&ptr, &length) + ) { + gdbstub_strcpy(output_buffer, "E01"); + break; + } + + /* only support software breakpoints */ + gdbstub_strcpy(output_buffer, "E03"); + if (loop != 0 || + length < 1 || + length > 7 || + (unsigned long) addr < 4096) + break; + + if (gdbstub_set_breakpoint((u8 *) addr, length) < 0) + break; + + gdbstub_strcpy(output_buffer, "OK"); + break; + + /* + * Clear breakpoint + */ + case 'z': + ptr = &input_buffer[1]; + + if (!hexToInt(&ptr, &loop) || *ptr++ != ',' || + !hexToInt(&ptr, &addr) || *ptr++ != ',' || + !hexToInt(&ptr, &length) + ) { + gdbstub_strcpy(output_buffer, "E01"); + break; + } + + /* only support software breakpoints */ + gdbstub_strcpy(output_buffer, "E03"); + if (loop != 0 || + length < 1 || + length > 7 || + (unsigned long) addr < 4096) + break; + + if (gdbstub_clear_breakpoint((u8 *) addr, length) < 0) + break; + + gdbstub_strcpy(output_buffer, "OK"); + break; + + default: + gdbstub_proto("### GDB Unsupported Cmd '%s'\n", + input_buffer); + break; + } + + /* reply to the request */ + putpacket(output_buffer); + } + +done: + /* + * Need to flush the instruction cache here, as we may + * have deposited a breakpoint, and the icache probably + * has no way of knowing that a data ref to some location + * may have changed something that is in the instruction + * cache. + * NB: We flush both caches, just to be sure... + */ + if (gdbstub_flush_caches) + debugger_local_cache_flushinv(); + + gdbstub_load_fpu(); + mn10300_set_gdbleds(0); + if (excep == EXCEP_NMI) + NMICR = NMICR_NMIF; + + touch_softlockup_watchdog(); + + local_irq_restore(epsw); + return 0; +} + +/* + * Determine if we hit a debugger special breakpoint that needs skipping over + * automatically. + */ +int at_debugger_breakpoint(struct pt_regs *regs) +{ + return 0; +} + +/* + * handle event interception + */ +asmlinkage int debugger_intercept(enum exception_code excep, + int signo, int si_code, struct pt_regs *regs) +{ + static u8 notfirst = 1; + int ret; + + if (gdbstub_busy) + gdbstub_printk("--> gdbstub reentered itself\n"); + gdbstub_busy = 1; + + if (notfirst) { + unsigned long mdr; + asm("mov mdr,%0" : "=d"(mdr)); + + gdbstub_entry( + "--> debugger_intercept(%p,%04x) [MDR=%lx PC=%lx]\n", + regs, excep, mdr, regs->pc); + + gdbstub_entry( + "PC: %08lx EPSW: %08lx SSP: %08lx mode: %s\n", + regs->pc, regs->epsw, (unsigned long) &ret, + user_mode(regs) ? "User" : "Super"); + gdbstub_entry( + "d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n", + regs->d0, regs->d1, regs->d2, regs->d3); + gdbstub_entry( + "a0: %08lx a1: %08lx a2: %08lx a3: %08lx\n", + regs->a0, regs->a1, regs->a2, regs->a3); + gdbstub_entry( + "e0: %08lx e1: %08lx e2: %08lx e3: %08lx\n", + regs->e0, regs->e1, regs->e2, regs->e3); + gdbstub_entry( + "e4: %08lx e5: %08lx e6: %08lx e7: %08lx\n", + regs->e4, regs->e5, regs->e6, regs->e7); + gdbstub_entry( + "lar: %08lx lir: %08lx mdr: %08lx usp: %08lx\n", + regs->lar, regs->lir, regs->mdr, regs->sp); + gdbstub_entry( + "cvf: %08lx crl: %08lx crh: %08lx drq: %08lx\n", + regs->mcvf, regs->mcrl, regs->mcrh, regs->mdrq); + gdbstub_entry( + "threadinfo=%p task=%p)\n", + current_thread_info(), current); + } else { + notfirst = 1; + } + + ret = gdbstub(regs, excep); + + gdbstub_entry("<-- debugger_intercept()\n"); + gdbstub_busy = 0; + return ret; +} + +/* + * handle the GDB stub itself causing an exception + */ +asmlinkage void gdbstub_exception(struct pt_regs *regs, + enum exception_code excep) +{ + unsigned long mdr; + + asm("mov mdr,%0" : "=d"(mdr)); + gdbstub_entry("--> gdbstub exception({%p},%04x) [MDR=%lx]\n", + regs, excep, mdr); + + while ((unsigned long) regs == 0xffffffff) {} + + /* handle guarded memory accesses where we know it might fault */ + if (regs->pc == (unsigned) gdbstub_read_byte_guard) { + regs->pc = (unsigned) gdbstub_read_byte_cont; + goto fault; + } + + if (regs->pc == (unsigned) gdbstub_read_word_guard) { + regs->pc = (unsigned) gdbstub_read_word_cont; + goto fault; + } + + if (regs->pc == (unsigned) gdbstub_read_dword_guard) { + regs->pc = (unsigned) gdbstub_read_dword_cont; + goto fault; + } + + if (regs->pc == (unsigned) gdbstub_write_byte_guard) { + regs->pc = (unsigned) gdbstub_write_byte_cont; + goto fault; + } + + if (regs->pc == (unsigned) gdbstub_write_word_guard) { + regs->pc = (unsigned) gdbstub_write_word_cont; + goto fault; + } + + if (regs->pc == (unsigned) gdbstub_write_dword_guard) { + regs->pc = (unsigned) gdbstub_write_dword_cont; + goto fault; + } + + gdbstub_printk("\n### GDB stub caused an exception ###\n"); + + /* something went horribly wrong */ + console_verbose(); + show_registers(regs); + + panic("GDB Stub caused an unexpected exception - can't continue\n"); + + /* we caught an attempt by the stub to access silly memory */ +fault: + gdbstub_entry("<-- gdbstub exception() = EFAULT\n"); + regs->d0 = -EFAULT; + return; +} + +/* + * send an exit message to GDB + */ +void gdbstub_exit(int status) +{ + unsigned char checksum; + unsigned char ch; + int count; + + gdbstub_busy = 1; + output_buffer[0] = 'W'; + output_buffer[1] = hex_asc_hi(status); + output_buffer[2] = hex_asc_lo(status); + output_buffer[3] = 0; + + gdbstub_io_tx_char('$'); + checksum = 0; + count = 0; + + while ((ch = output_buffer[count]) != 0) { + gdbstub_io_tx_char(ch); + checksum += ch; + count += 1; + } + + gdbstub_io_tx_char('#'); + gdbstub_io_tx_char(hex_asc_hi(checksum)); + gdbstub_io_tx_char(hex_asc_lo(checksum)); + + /* make sure the output is flushed, or else RedBoot might clobber it */ + gdbstub_io_tx_flush(); + + gdbstub_busy = 0; +} + +/* + * initialise the GDB stub + */ +asmlinkage void __init gdbstub_init(void) +{ +#ifdef CONFIG_GDBSTUB_IMMEDIATE + unsigned char ch; + int ret; +#endif + + gdbstub_busy = 1; + + printk(KERN_INFO "%s", gdbstub_banner); + + gdbstub_io_init(); + + gdbstub_entry("--> gdbstub_init\n"); + + /* try to talk to GDB (or anyone insane enough to want to type GDB + * protocol by hand) */ + gdbstub_io("### GDB Tx ACK\n"); + gdbstub_io_tx_char('+'); /* 'hello world' */ + +#ifdef CONFIG_GDBSTUB_IMMEDIATE + gdbstub_printk("GDB Stub waiting for packet\n"); + + /* in case GDB is started before us, ACK any packets that are already + * sitting there (presumably "$?#xx") + */ + do { gdbstub_io_rx_char(&ch, 0); } while (ch != '$'); + do { gdbstub_io_rx_char(&ch, 0); } while (ch != '#'); + /* eat first csum byte */ + do { ret = gdbstub_io_rx_char(&ch, 0); } while (ret != 0); + /* eat second csum byte */ + do { ret = gdbstub_io_rx_char(&ch, 0); } while (ret != 0); + + gdbstub_io("### GDB Tx NAK\n"); + gdbstub_io_tx_char('-'); /* NAK it */ + +#else + printk("GDB Stub ready\n"); +#endif + + gdbstub_busy = 0; + gdbstub_entry("<-- gdbstub_init\n"); +} + +/* + * register the console at a more appropriate time + */ +#ifdef CONFIG_GDBSTUB_CONSOLE +static int __init gdbstub_postinit(void) +{ + printk(KERN_NOTICE "registering console\n"); + register_console(&gdbstub_console); + return 0; +} + +__initcall(gdbstub_postinit); +#endif + +/* + * handle character reception on GDB serial port + * - jump into the GDB stub if BREAK is detected on the serial line + */ +asmlinkage void gdbstub_rx_irq(struct pt_regs *regs, enum exception_code excep) +{ + char ch; + int ret; + + gdbstub_entry("--> gdbstub_rx_irq\n"); + + do { + ret = gdbstub_io_rx_char(&ch, 1); + if (ret != -EIO && ret != -EAGAIN) { + if (ret != -EINTR) + gdbstub_rx_unget = ch; + gdbstub(regs, excep); + } + } while (ret != -EAGAIN); + + gdbstub_entry("<-- gdbstub_rx_irq\n"); +} diff --git a/arch/mn10300/kernel/head.S b/arch/mn10300/kernel/head.S new file mode 100644 index 00000000..73e00fc7 --- /dev/null +++ b/arch/mn10300/kernel/head.S @@ -0,0 +1,450 @@ +/* Boot entry point for MN10300 kernel + * + * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/init.h> +#include <linux/threads.h> +#include <linux/linkage.h> +#include <linux/serial_reg.h> +#include <asm/thread_info.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/frame.inc> +#include <asm/param.h> +#include <unit/serial.h> +#ifdef CONFIG_SMP +#include <asm/smp.h> +#include <asm/intctl-regs.h> +#include <asm/cpu-regs.h> +#include <proc/smp-regs.h> +#endif /* CONFIG_SMP */ + + __HEAD + +############################################################################### +# +# bootloader entry point +# +############################################################################### + .globl _start + .type _start,@function +_start: +#ifdef CONFIG_SMP + # + # If this is a secondary CPU (AP), then deal with that elsewhere + # + mov (CPUID),d3 + and CPUID_MASK,d3 + bne startup_secondary + + # + # We're dealing with the primary CPU (BP) here, then. + # Keep BP's D0,D1,D2 register for boot check. + # + + # Set up the Boot IPI for each secondary CPU + mov 0x1,a0 +loop_set_secondary_icr: + mov a0,a1 + asl CROSS_ICR_CPU_SHIFT,a1 + add CROSS_GxICR(SMP_BOOT_IRQ,0),a1 + movhu (a1),d3 + or GxICR_ENABLE|GxICR_LEVEL_0,d3 + movhu d3,(a1) + movhu (a1),d3 # flush + inc a0 + cmp NR_CPUS,a0 + bne loop_set_secondary_icr +#endif /* CONFIG_SMP */ + + # save commandline pointer + mov d0,a3 + + # preload the PGD pointer register + mov swapper_pg_dir,d0 + mov d0,(PTBR) + clr d0 + movbu d0,(PIDR) + + # turn on the TLBs + mov MMUCTR_IIV|MMUCTR_DIV,d0 + mov d0,(MMUCTR) +#ifdef CONFIG_AM34_2 + mov MMUCTR_ITE|MMUCTR_DTE|MMUCTR_CE|MMUCTR_WTE,d0 +#else + mov MMUCTR_ITE|MMUCTR_DTE|MMUCTR_CE,d0 +#endif + mov d0,(MMUCTR) + + # turn on AM33v2 exception handling mode and set the trap table base + movhu (CPUP),d0 + or CPUP_EXM_AM33V2,d0 + movhu d0,(CPUP) + mov CONFIG_INTERRUPT_VECTOR_BASE,d0 + mov d0,(TBR) + + # invalidate and enable both of the caches +#ifdef CONFIG_SMP + mov ECHCTR,a0 + clr d0 + mov d0,(a0) +#endif + mov CHCTR,a0 + clr d0 + movhu d0,(a0) # turn off first + mov CHCTR_ICINV|CHCTR_DCINV,d0 + movhu d0,(a0) + setlb + mov (a0),d0 + btst CHCTR_ICBUSY|CHCTR_DCBUSY,d0 # wait till not busy + lne + +#ifdef CONFIG_MN10300_CACHE_ENABLED +#ifdef CONFIG_MN10300_CACHE_WBACK +#ifndef CONFIG_MN10300_CACHE_WBACK_NOWRALLOC + mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD_WRBACK,d0 +#else + mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD_WRBACK|CHCTR_DCALMD,d0 +#endif /* NOWRALLOC */ +#else + mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD_WRTHROUGH,d0 +#endif /* WBACK */ + movhu d0,(a0) # enable +#endif /* ENABLED */ + + # turn on RTS on the debug serial port if applicable +#ifdef CONFIG_MN10300_UNIT_ASB2305 + bset UART_MCR_RTS,(ASB2305_DEBUG_MCR) +#endif + + # clear the BSS area + mov __bss_start,a0 + mov __bss_stop,a1 + clr d0 +bssclear: + cmp a1,a0 + bge bssclear_end + mov d0,(a0) + inc4 a0 + bra bssclear +bssclear_end: + + # retrieve the parameters (including command line) before we overwrite + # them + cmp 0xabadcafe,d1 + bne __no_parameters + +__copy_parameters: + mov redboot_command_line,a0 + mov a0,a1 + add COMMAND_LINE_SIZE,a1 +1: + movbu (a3),d0 + inc a3 + movbu d0,(a0) + inc a0 + cmp a1,a0 + blt 1b + + mov redboot_platform_name,a0 + mov a0,a1 + add COMMAND_LINE_SIZE,a1 + mov d2,a3 +1: + movbu (a3),d0 + inc a3 + movbu d0,(a0) + inc a0 + cmp a1,a0 + blt 1b + +__no_parameters: + + # set up the registers with recognisable rubbish in them + mov init_thread_union+THREAD_SIZE-12,sp + + mov 0xea01eaea,d0 + mov d0,(4,sp) # EPSW save area + mov 0xea02eaea,d0 + mov d0,(8,sp) # PC save area + + mov 0xeb0060ed,d0 + mov d0,mdr + mov 0xeb0061ed,d0 + mov d0,mdrq + mov 0xeb0062ed,d0 + mov d0,mcrh + mov 0xeb0063ed,d0 + mov d0,mcrl + mov 0xeb0064ed,d0 + mov d0,mcvf + mov 0xed0065ed,a3 + mov a3,usp + + mov 0xed00e0ed,e0 + mov 0xed00e1ed,e1 + mov 0xed00e2ed,e2 + mov 0xed00e3ed,e3 + mov 0xed00e4ed,e4 + mov 0xed00e5ed,e5 + mov 0xed00e6ed,e6 + mov 0xed00e7ed,e7 + + mov 0xed00d0ed,d0 + mov 0xed00d1ed,d1 + mov 0xed00d2ed,d2 + mov 0xed00d3ed,d3 + mov 0xed00a0ed,a0 + mov 0xed00a1ed,a1 + mov 0xed00a2ed,a2 + mov 0,a3 + + # set up the initial kernel stack + SAVE_ALL + mov 0xffffffff,d0 + mov d0,(REG_ORIG_D0,fp) + + # put different recognisable rubbish in the regs + mov 0xfb0060ed,d0 + mov d0,mdr + mov 0xfb0061ed,d0 + mov d0,mdrq + mov 0xfb0062ed,d0 + mov d0,mcrh + mov 0xfb0063ed,d0 + mov d0,mcrl + mov 0xfb0064ed,d0 + mov d0,mcvf + mov 0xfd0065ed,a0 + mov a0,usp + + mov 0xfd00e0ed,e0 + mov 0xfd00e1ed,e1 + mov 0xfd00e2ed,e2 + mov 0xfd00e3ed,e3 + mov 0xfd00e4ed,e4 + mov 0xfd00e5ed,e5 + mov 0xfd00e6ed,e6 + mov 0xfd00e7ed,e7 + + mov 0xfd00d0ed,d0 + mov 0xfd00d1ed,d1 + mov 0xfd00d2ed,d2 + mov 0xfd00d3ed,d3 + mov 0xfd00a0ed,a0 + mov 0xfd00a1ed,a1 + mov 0xfd00a2ed,a2 + + # we may be holding current in E2 +#ifdef CONFIG_MN10300_CURRENT_IN_E2 + mov init_task,e2 +#endif + + # initialise the processor and the unit + call processor_init[],0 + call unit_init[],0 + +#ifdef CONFIG_SMP + # mark the primary CPU in cpu_boot_map + mov cpu_boot_map,a0 + mov 0x1,d0 + mov d0,(a0) + + # signal each secondary CPU to begin booting + mov 0x1,d2 # CPU ID + +loop_request_boot_secondary: + mov d2,a0 + # send SMP_BOOT_IPI to secondary CPU + asl CROSS_ICR_CPU_SHIFT,a0 + add CROSS_GxICR(SMP_BOOT_IRQ,0),a0 + movhu (a0),d0 + or GxICR_REQUEST|GxICR_DETECT,d0 + movhu d0,(a0) + movhu (a0),d0 # flush + + # wait up to 100ms for AP's IPI to be received + clr d3 +wait_on_secondary_boot: + mov DELAY_TIME_BOOT_IPI,d0 + call __delay[],0 + inc d3 + mov cpu_boot_map,a0 + mov (a0),d0 + lsr d2,d0 + btst 0x1,d0 + bne 1f + cmp TIME_OUT_COUNT_BOOT_IPI,d3 + bne wait_on_secondary_boot +1: + inc d2 + cmp NR_CPUS,d2 + bne loop_request_boot_secondary +#endif /* CONFIG_SMP */ + +#ifdef CONFIG_GDBSTUB + call gdbstub_init[],0 + +#ifdef CONFIG_GDBSTUB_IMMEDIATE + .globl __gdbstub_pause +__gdbstub_pause: + bra __gdbstub_pause +#endif +#endif + + jmp start_kernel + .size _start,.-_start + +############################################################################### +# +# Secondary CPU boot point +# +############################################################################### +#ifdef CONFIG_SMP +startup_secondary: + # preload the PGD pointer register + mov swapper_pg_dir,d0 + mov d0,(PTBR) + clr d0 + movbu d0,(PIDR) + + # turn on the TLBs + mov MMUCTR_IIV|MMUCTR_DIV,d0 + mov d0,(MMUCTR) +#ifdef CONFIG_AM34_2 + mov MMUCTR_ITE|MMUCTR_DTE|MMUCTR_CE|MMUCTR_WTE,d0 +#else + mov MMUCTR_ITE|MMUCTR_DTE|MMUCTR_CE,d0 +#endif + mov d0,(MMUCTR) + + # turn on AM33v2 exception handling mode and set the trap table base + movhu (CPUP),d0 + or CPUP_EXM_AM33V2,d0 + movhu d0,(CPUP) + + # set the interrupt vector table + mov CONFIG_INTERRUPT_VECTOR_BASE,d0 + mov d0,(TBR) + + # invalidate and enable both of the caches + mov ECHCTR,a0 + clr d0 + mov d0,(a0) + mov CHCTR,a0 + clr d0 + movhu d0,(a0) # turn off first + mov CHCTR_ICINV|CHCTR_DCINV,d0 + movhu d0,(a0) + setlb + mov (a0),d0 + btst CHCTR_ICBUSY|CHCTR_DCBUSY,d0 # wait till not busy (use CPU loop buffer) + lne + +#ifdef CONFIG_MN10300_CACHE_ENABLED +#ifdef CONFIG_MN10300_CACHE_WBACK +#ifndef CONFIG_MN10300_CACHE_WBACK_NOWRALLOC + mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD_WRBACK,d0 +#else + mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD_WRBACK|CHCTR_DCALMD,d0 +#endif /* !NOWRALLOC */ +#else + mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD_WRTHROUGH,d0 +#endif /* WBACK */ + movhu d0,(a0) # enable +#endif /* ENABLED */ + + # Clear the boot IPI interrupt for this CPU + movhu (GxICR(SMP_BOOT_IRQ)),d0 + and ~GxICR_REQUEST,d0 + movhu d0,(GxICR(SMP_BOOT_IRQ)) + movhu (GxICR(SMP_BOOT_IRQ)),d0 # flush + + /* get stack */ + mov CONFIG_INTERRUPT_VECTOR_BASE + CONFIG_BOOT_STACK_OFFSET,a0 + mov (CPUID),d0 + and CPUID_MASK,d0 + mulu CONFIG_BOOT_STACK_SIZE,d0 + sub d0,a0 + mov a0,sp + + # init interrupt for AP + call smp_prepare_cpu_init[],0 + + # mark this secondary CPU in cpu_boot_map + mov (CPUID),d0 + mov 0x1,d1 + asl d0,d1 + mov cpu_boot_map,a0 + bset d1,(a0) + + or EPSW_IE|EPSW_IM_1,epsw # permit level 0 interrupts + nop + nop +#ifdef CONFIG_MN10300_CACHE_WBACK + # flush the local cache if it's in writeback mode + call mn10300_local_dcache_flush_inv[],0 + setlb + mov (CHCTR),d0 + btst CHCTR_DCBUSY,d0 # wait till not busy (use CPU loop buffer) + lne +#endif + + # now sleep waiting for further instructions +secondary_sleep: + mov CPUM_SLEEP,d0 + movhu d0,(CPUM) + nop + nop + bra secondary_sleep + .size startup_secondary,.-startup_secondary +#endif /* CONFIG_SMP */ + +############################################################################### +# +# +# +############################################################################### +ENTRY(__head_end) + +/* + * This is initialized to disallow all access to the low 2G region + * - the high 2G region is managed directly by the MMU + * - range 0x70000000-0x7C000000 are initialised for use by VMALLOC + */ + .section .bss + .balign PAGE_SIZE +ENTRY(swapper_pg_dir) + .space PTRS_PER_PGD*4 + +/* + * The page tables are initialized to only 8MB here - the final page + * tables are set up later depending on memory size. + */ + + .balign PAGE_SIZE +ENTRY(empty_zero_page) + .space PAGE_SIZE + + .balign PAGE_SIZE +ENTRY(empty_bad_page) + .space PAGE_SIZE + + .balign PAGE_SIZE +ENTRY(empty_bad_pte_table) + .space PAGE_SIZE + + .balign PAGE_SIZE +ENTRY(large_page_table) + .space PAGE_SIZE + + .balign PAGE_SIZE +ENTRY(kernel_vmalloc_ptes) + .space ((VMALLOC_END-VMALLOC_START)/PAGE_SIZE)*4 diff --git a/arch/mn10300/kernel/init_task.c b/arch/mn10300/kernel/init_task.c new file mode 100644 index 00000000..a481b043 --- /dev/null +++ b/arch/mn10300/kernel/init_task.c @@ -0,0 +1,39 @@ +/* MN10300 Initial task definitions + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/init_task.h> +#include <linux/fs.h> +#include <linux/mqueue.h> +#include <asm/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 THREAD_SIZE aligned due to the + * way process stacks are handled. This is done by having a special + * "init_task" linker map entry.. + */ +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/mn10300/kernel/internal.h b/arch/mn10300/kernel/internal.h new file mode 100644 index 00000000..a5ac755d --- /dev/null +++ b/arch/mn10300/kernel/internal.h @@ -0,0 +1,42 @@ +/* Internal definitions for the arch part of the core kernel + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +struct clocksource; +struct clock_event_device; + +/* + * kthread.S + */ +extern int kernel_thread_helper(int); + +/* + * entry.S + */ +extern void ret_from_fork(struct task_struct *) __attribute__((noreturn)); + +/* + * smp-low.S + */ +#ifdef CONFIG_SMP +extern void mn10300_low_ipi_handler(void); +#endif + +/* + * smp.c + */ +#ifdef CONFIG_SMP +extern void smp_jump_to_debugger(void); +#endif + +/* + * time.c + */ +extern irqreturn_t local_timer_interrupt(void); diff --git a/arch/mn10300/kernel/io.c b/arch/mn10300/kernel/io.c new file mode 100644 index 00000000..e96fdf6b --- /dev/null +++ b/arch/mn10300/kernel/io.c @@ -0,0 +1,30 @@ +/* MN10300 Misaligned multibyte-word I/O + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <asm/io.h> + +/* + * output data from a potentially misaligned buffer + */ +void __outsl(unsigned long addr, const void *buffer, int count) +{ + const unsigned char *buf = buffer; + unsigned long val; + + while (count--) { + memcpy(&val, buf, 4); + outl(val, addr); + buf += 4; + } +} +EXPORT_SYMBOL(__outsl); diff --git a/arch/mn10300/kernel/irq.c b/arch/mn10300/kernel/irq.c new file mode 100644 index 00000000..2623d19f --- /dev/null +++ b/arch/mn10300/kernel/irq.c @@ -0,0 +1,402 @@ +/* MN10300 Arch-specific interrupt handling + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/kernel_stat.h> +#include <linux/seq_file.h> +#include <linux/cpumask.h> +#include <asm/setup.h> +#include <asm/serial-regs.h> + +unsigned long __mn10300_irq_enabled_epsw[NR_CPUS] __cacheline_aligned_in_smp = { + [0 ... NR_CPUS - 1] = EPSW_IE | EPSW_IM_7 +}; +EXPORT_SYMBOL(__mn10300_irq_enabled_epsw); + +#ifdef CONFIG_SMP +static char irq_affinity_online[NR_IRQS] = { + [0 ... NR_IRQS - 1] = 0 +}; + +#define NR_IRQ_WORDS ((NR_IRQS + 31) / 32) +static unsigned long irq_affinity_request[NR_IRQ_WORDS] = { + [0 ... NR_IRQ_WORDS - 1] = 0 +}; +#endif /* CONFIG_SMP */ + +atomic_t irq_err_count; + +/* + * MN10300 interrupt controller operations + */ +static void mn10300_cpupic_ack(struct irq_data *d) +{ + unsigned int irq = d->irq; + unsigned long flags; + u16 tmp; + + flags = arch_local_cli_save(); + GxICR_u8(irq) = GxICR_DETECT; + tmp = GxICR(irq); + arch_local_irq_restore(flags); +} + +static void __mask_and_set_icr(unsigned int irq, + unsigned int mask, unsigned int set) +{ + unsigned long flags; + u16 tmp; + + flags = arch_local_cli_save(); + tmp = GxICR(irq); + GxICR(irq) = (tmp & mask) | set; + tmp = GxICR(irq); + arch_local_irq_restore(flags); +} + +static void mn10300_cpupic_mask(struct irq_data *d) +{ + __mask_and_set_icr(d->irq, GxICR_LEVEL, 0); +} + +static void mn10300_cpupic_mask_ack(struct irq_data *d) +{ + unsigned int irq = d->irq; +#ifdef CONFIG_SMP + unsigned long flags; + u16 tmp; + + flags = arch_local_cli_save(); + + if (!test_and_clear_bit(irq, irq_affinity_request)) { + tmp = GxICR(irq); + GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_DETECT; + tmp = GxICR(irq); + } else { + u16 tmp2; + tmp = GxICR(irq); + GxICR(irq) = (tmp & GxICR_LEVEL); + tmp2 = GxICR(irq); + + irq_affinity_online[irq] = + cpumask_any_and(d->affinity, cpu_online_mask); + CROSS_GxICR(irq, irq_affinity_online[irq]) = + (tmp & (GxICR_LEVEL | GxICR_ENABLE)) | GxICR_DETECT; + tmp = CROSS_GxICR(irq, irq_affinity_online[irq]); + } + + arch_local_irq_restore(flags); +#else /* CONFIG_SMP */ + __mask_and_set_icr(irq, GxICR_LEVEL, GxICR_DETECT); +#endif /* CONFIG_SMP */ +} + +static void mn10300_cpupic_unmask(struct irq_data *d) +{ + __mask_and_set_icr(d->irq, GxICR_LEVEL, GxICR_ENABLE); +} + +static void mn10300_cpupic_unmask_clear(struct irq_data *d) +{ + unsigned int irq = d->irq; + /* the MN10300 PIC latches its interrupt request bit, even after the + * device has ceased to assert its interrupt line and the interrupt + * channel has been disabled in the PIC, so for level-triggered + * interrupts we need to clear the request bit when we re-enable */ +#ifdef CONFIG_SMP + unsigned long flags; + u16 tmp; + + flags = arch_local_cli_save(); + + if (!test_and_clear_bit(irq, irq_affinity_request)) { + tmp = GxICR(irq); + GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_ENABLE | GxICR_DETECT; + tmp = GxICR(irq); + } else { + tmp = GxICR(irq); + + irq_affinity_online[irq] = cpumask_any_and(d->affinity, + cpu_online_mask); + CROSS_GxICR(irq, irq_affinity_online[irq]) = (tmp & GxICR_LEVEL) | GxICR_ENABLE | GxICR_DETECT; + tmp = CROSS_GxICR(irq, irq_affinity_online[irq]); + } + + arch_local_irq_restore(flags); +#else /* CONFIG_SMP */ + __mask_and_set_icr(irq, GxICR_LEVEL, GxICR_ENABLE | GxICR_DETECT); +#endif /* CONFIG_SMP */ +} + +#ifdef CONFIG_SMP +static int +mn10300_cpupic_setaffinity(struct irq_data *d, const struct cpumask *mask, + bool force) +{ + unsigned long flags; + int err; + + flags = arch_local_cli_save(); + + /* check irq no */ + switch (d->irq) { + case TMJCIRQ: + case RESCHEDULE_IPI: + case CALL_FUNC_SINGLE_IPI: + case LOCAL_TIMER_IPI: + case FLUSH_CACHE_IPI: + case CALL_FUNCTION_NMI_IPI: + case DEBUGGER_NMI_IPI: +#ifdef CONFIG_MN10300_TTYSM0 + case SC0RXIRQ: + case SC0TXIRQ: +#ifdef CONFIG_MN10300_TTYSM0_TIMER8 + case TM8IRQ: +#elif CONFIG_MN10300_TTYSM0_TIMER2 + case TM2IRQ: +#endif /* CONFIG_MN10300_TTYSM0_TIMER8 */ +#endif /* CONFIG_MN10300_TTYSM0 */ + +#ifdef CONFIG_MN10300_TTYSM1 + case SC1RXIRQ: + case SC1TXIRQ: +#ifdef CONFIG_MN10300_TTYSM1_TIMER12 + case TM12IRQ: +#elif CONFIG_MN10300_TTYSM1_TIMER9 + case TM9IRQ: +#elif CONFIG_MN10300_TTYSM1_TIMER3 + case TM3IRQ: +#endif /* CONFIG_MN10300_TTYSM1_TIMER12 */ +#endif /* CONFIG_MN10300_TTYSM1 */ + +#ifdef CONFIG_MN10300_TTYSM2 + case SC2RXIRQ: + case SC2TXIRQ: + case TM10IRQ: +#endif /* CONFIG_MN10300_TTYSM2 */ + err = -1; + break; + + default: + set_bit(d->irq, irq_affinity_request); + err = 0; + break; + } + + arch_local_irq_restore(flags); + return err; +} +#endif /* CONFIG_SMP */ + +/* + * MN10300 PIC level-triggered IRQ handling. + * + * The PIC has no 'ACK' function per se. It is possible to clear individual + * channel latches, but each latch relatches whether or not the channel is + * masked, so we need to clear the latch when we unmask the channel. + * + * Also for this reason, we don't supply an ack() op (it's unused anyway if + * mask_ack() is provided), and mask_ack() just masks. + */ +static struct irq_chip mn10300_cpu_pic_level = { + .name = "cpu_l", + .irq_disable = mn10300_cpupic_mask, + .irq_enable = mn10300_cpupic_unmask_clear, + .irq_ack = NULL, + .irq_mask = mn10300_cpupic_mask, + .irq_mask_ack = mn10300_cpupic_mask, + .irq_unmask = mn10300_cpupic_unmask_clear, +#ifdef CONFIG_SMP + .irq_set_affinity = mn10300_cpupic_setaffinity, +#endif +}; + +/* + * MN10300 PIC edge-triggered IRQ handling. + * + * We use the latch clearing function of the PIC as the 'ACK' function. + */ +static struct irq_chip mn10300_cpu_pic_edge = { + .name = "cpu_e", + .irq_disable = mn10300_cpupic_mask, + .irq_enable = mn10300_cpupic_unmask, + .irq_ack = mn10300_cpupic_ack, + .irq_mask = mn10300_cpupic_mask, + .irq_mask_ack = mn10300_cpupic_mask_ack, + .irq_unmask = mn10300_cpupic_unmask, +#ifdef CONFIG_SMP + .irq_set_affinity = mn10300_cpupic_setaffinity, +#endif +}; + +/* + * 'what should we do if we get a hw irq event on an illegal vector'. + * each architecture has to answer this themselves. + */ +void ack_bad_irq(int irq) +{ + printk(KERN_WARNING "unexpected IRQ trap at vector %02x\n", irq); +} + +/* + * change the level at which an IRQ executes + * - must not be called whilst interrupts are being processed! + */ +void set_intr_level(int irq, u16 level) +{ + BUG_ON(in_interrupt()); + + __mask_and_set_icr(irq, GxICR_ENABLE, level); +} + +/* + * mark an interrupt to be ACK'd after interrupt handlers have been run rather + * than before + * - see Documentation/mn10300/features.txt + */ +void mn10300_set_lateack_irq_type(int irq) +{ + irq_set_chip_and_handler(irq, &mn10300_cpu_pic_level, + handle_level_irq); +} + +/* + * initialise the interrupt system + */ +void __init init_IRQ(void) +{ + int irq; + + for (irq = 0; irq < NR_IRQS; irq++) + if (irq_get_chip(irq) == &no_irq_chip) + /* due to the PIC latching interrupt requests, even + * when the IRQ is disabled, IRQ_PENDING is superfluous + * and we can use handle_level_irq() for edge-triggered + * interrupts */ + irq_set_chip_and_handler(irq, &mn10300_cpu_pic_edge, + handle_level_irq); + + unit_init_IRQ(); +} + +/* + * handle normal device IRQs + */ +asmlinkage void do_IRQ(void) +{ + unsigned long sp, epsw, irq_disabled_epsw, old_irq_enabled_epsw; + unsigned int cpu_id = smp_processor_id(); + int irq; + + sp = current_stack_pointer(); + BUG_ON(sp - (sp & ~(THREAD_SIZE - 1)) < STACK_WARN); + + /* make sure local_irq_enable() doesn't muck up the interrupt priority + * setting in EPSW */ + old_irq_enabled_epsw = __mn10300_irq_enabled_epsw[cpu_id]; + local_save_flags(epsw); + __mn10300_irq_enabled_epsw[cpu_id] = EPSW_IE | (EPSW_IM & epsw); + irq_disabled_epsw = EPSW_IE | MN10300_CLI_LEVEL; + +#ifdef CONFIG_MN10300_WD_TIMER + __IRQ_STAT(cpu_id, __irq_count)++; +#endif + + irq_enter(); + + for (;;) { + /* ask the interrupt controller for the next IRQ to process + * - the result we get depends on EPSW.IM + */ + irq = IAGR & IAGR_GN; + if (!irq) + break; + + local_irq_restore(irq_disabled_epsw); + + generic_handle_irq(irq >> 2); + + /* restore IRQ controls for IAGR access */ + local_irq_restore(epsw); + } + + __mn10300_irq_enabled_epsw[cpu_id] = old_irq_enabled_epsw; + + irq_exit(); +} + +/* + * Display interrupt management information through /proc/interrupts + */ +int arch_show_interrupts(struct seq_file *p, int prec) +{ +#ifdef CONFIG_MN10300_WD_TIMER + int j; + + seq_printf(p, "%*s: ", prec, "NMI"); + for (j = 0; j < NR_CPUS; j++) + if (cpu_online(j)) + seq_printf(p, "%10u ", nmi_count(j)); + seq_putc(p, '\n'); +#endif + + seq_printf(p, "%*s: ", prec, "ERR"); + seq_printf(p, "%10u\n", atomic_read(&irq_err_count)); + return 0; +} + +#ifdef CONFIG_HOTPLUG_CPU +void migrate_irqs(void) +{ + int irq; + unsigned int self, new; + unsigned long flags; + + self = smp_processor_id(); + for (irq = 0; irq < NR_IRQS; irq++) { + struct irq_data *data = irq_get_irq_data(irq); + + if (irqd_is_per_cpu(data)) + continue; + + if (cpumask_test_cpu(self, &data->affinity) && + !cpumask_intersects(&irq_affinity[irq], cpu_online_mask)) { + int cpu_id; + cpu_id = cpumask_first(cpu_online_mask); + cpumask_set_cpu(cpu_id, &data->affinity); + } + /* We need to operate irq_affinity_online atomically. */ + arch_local_cli_save(flags); + if (irq_affinity_online[irq] == self) { + u16 x, tmp; + + x = GxICR(irq); + GxICR(irq) = x & GxICR_LEVEL; + tmp = GxICR(irq); + + new = cpumask_any_and(&data->affinity, + cpu_online_mask); + irq_affinity_online[irq] = new; + + CROSS_GxICR(irq, new) = + (x & GxICR_LEVEL) | GxICR_DETECT; + tmp = CROSS_GxICR(irq, new); + + x &= GxICR_LEVEL | GxICR_ENABLE; + if (GxICR(irq) & GxICR_REQUEST) + x |= GxICR_REQUEST | GxICR_DETECT; + CROSS_GxICR(irq, new) = x; + tmp = CROSS_GxICR(irq, new); + } + arch_local_irq_restore(flags); + } +} +#endif /* CONFIG_HOTPLUG_CPU */ diff --git a/arch/mn10300/kernel/kernel_execve.S b/arch/mn10300/kernel/kernel_execve.S new file mode 100644 index 00000000..86039f10 --- /dev/null +++ b/arch/mn10300/kernel/kernel_execve.S @@ -0,0 +1,37 @@ +/* MN10300 In-kernel program execution + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/linkage.h> +#include <asm/unistd.h> + +############################################################################### +# +# Do a system call from kernel instead of calling sys_execve so we end up with +# proper pt_regs. +# +# int kernel_execve(const char *filename, char *const argv[], +# char *const envp[]) +# +# On entry: D0/D1/8(SP): arguments to function +# On return: D0: syscall return. +# +############################################################################### + .globl kernel_execve + .type kernel_execve,@function +kernel_execve: + mov a3,a1 + mov d0,a0 + mov (12,sp),a3 + mov +__NR_execve,d0 + syscall 0 + mov a1,a3 + rets + + .size kernel_execve,.-kernel_execve diff --git a/arch/mn10300/kernel/kgdb.c b/arch/mn10300/kernel/kgdb.c new file mode 100644 index 00000000..f6c981db --- /dev/null +++ b/arch/mn10300/kernel/kgdb.c @@ -0,0 +1,502 @@ +/* kgdb support for MN10300 + * + * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/slab.h> +#include <linux/ptrace.h> +#include <linux/kgdb.h> +#include <linux/uaccess.h> +#include <unit/leds.h> +#include <unit/serial.h> +#include <asm/debugger.h> +#include <asm/serial-regs.h> +#include "internal.h" + +/* + * Software single-stepping breakpoint save (used by __switch_to()) + */ +static struct thread_info *kgdb_sstep_thread; +u8 *kgdb_sstep_bp_addr[2]; +u8 kgdb_sstep_bp[2]; + +/* + * Copy kernel exception frame registers to the GDB register file + */ +void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + unsigned long ssp = (unsigned long) (regs + 1); + + gdb_regs[GDB_FR_D0] = regs->d0; + gdb_regs[GDB_FR_D1] = regs->d1; + gdb_regs[GDB_FR_D2] = regs->d2; + gdb_regs[GDB_FR_D3] = regs->d3; + gdb_regs[GDB_FR_A0] = regs->a0; + gdb_regs[GDB_FR_A1] = regs->a1; + gdb_regs[GDB_FR_A2] = regs->a2; + gdb_regs[GDB_FR_A3] = regs->a3; + gdb_regs[GDB_FR_SP] = (regs->epsw & EPSW_nSL) ? regs->sp : ssp; + gdb_regs[GDB_FR_PC] = regs->pc; + gdb_regs[GDB_FR_MDR] = regs->mdr; + gdb_regs[GDB_FR_EPSW] = regs->epsw; + gdb_regs[GDB_FR_LIR] = regs->lir; + gdb_regs[GDB_FR_LAR] = regs->lar; + gdb_regs[GDB_FR_MDRQ] = regs->mdrq; + gdb_regs[GDB_FR_E0] = regs->e0; + gdb_regs[GDB_FR_E1] = regs->e1; + gdb_regs[GDB_FR_E2] = regs->e2; + gdb_regs[GDB_FR_E3] = regs->e3; + gdb_regs[GDB_FR_E4] = regs->e4; + gdb_regs[GDB_FR_E5] = regs->e5; + gdb_regs[GDB_FR_E6] = regs->e6; + gdb_regs[GDB_FR_E7] = regs->e7; + gdb_regs[GDB_FR_SSP] = ssp; + gdb_regs[GDB_FR_MSP] = 0; + gdb_regs[GDB_FR_USP] = regs->sp; + gdb_regs[GDB_FR_MCRH] = regs->mcrh; + gdb_regs[GDB_FR_MCRL] = regs->mcrl; + gdb_regs[GDB_FR_MCVF] = regs->mcvf; + gdb_regs[GDB_FR_DUMMY0] = 0; + gdb_regs[GDB_FR_DUMMY1] = 0; + gdb_regs[GDB_FR_FS0] = 0; +} + +/* + * Extracts kernel SP/PC values understandable by gdb from the values + * saved by switch_to(). + */ +void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) +{ + gdb_regs[GDB_FR_SSP] = p->thread.sp; + gdb_regs[GDB_FR_PC] = p->thread.pc; + gdb_regs[GDB_FR_A3] = p->thread.a3; + gdb_regs[GDB_FR_USP] = p->thread.usp; + gdb_regs[GDB_FR_FPCR] = p->thread.fpu_state.fpcr; +} + +/* + * Fill kernel exception frame registers from the GDB register file + */ +void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + regs->d0 = gdb_regs[GDB_FR_D0]; + regs->d1 = gdb_regs[GDB_FR_D1]; + regs->d2 = gdb_regs[GDB_FR_D2]; + regs->d3 = gdb_regs[GDB_FR_D3]; + regs->a0 = gdb_regs[GDB_FR_A0]; + regs->a1 = gdb_regs[GDB_FR_A1]; + regs->a2 = gdb_regs[GDB_FR_A2]; + regs->a3 = gdb_regs[GDB_FR_A3]; + regs->sp = gdb_regs[GDB_FR_SP]; + regs->pc = gdb_regs[GDB_FR_PC]; + regs->mdr = gdb_regs[GDB_FR_MDR]; + regs->epsw = gdb_regs[GDB_FR_EPSW]; + regs->lir = gdb_regs[GDB_FR_LIR]; + regs->lar = gdb_regs[GDB_FR_LAR]; + regs->mdrq = gdb_regs[GDB_FR_MDRQ]; + regs->e0 = gdb_regs[GDB_FR_E0]; + regs->e1 = gdb_regs[GDB_FR_E1]; + regs->e2 = gdb_regs[GDB_FR_E2]; + regs->e3 = gdb_regs[GDB_FR_E3]; + regs->e4 = gdb_regs[GDB_FR_E4]; + regs->e5 = gdb_regs[GDB_FR_E5]; + regs->e6 = gdb_regs[GDB_FR_E6]; + regs->e7 = gdb_regs[GDB_FR_E7]; + regs->sp = gdb_regs[GDB_FR_SSP]; + /* gdb_regs[GDB_FR_MSP]; */ + // regs->usp = gdb_regs[GDB_FR_USP]; + regs->mcrh = gdb_regs[GDB_FR_MCRH]; + regs->mcrl = gdb_regs[GDB_FR_MCRL]; + regs->mcvf = gdb_regs[GDB_FR_MCVF]; + /* gdb_regs[GDB_FR_DUMMY0]; */ + /* gdb_regs[GDB_FR_DUMMY1]; */ + + // regs->fpcr = gdb_regs[GDB_FR_FPCR]; + // regs->fs0 = gdb_regs[GDB_FR_FS0]; +} + +struct kgdb_arch arch_kgdb_ops = { + .gdb_bpt_instr = { 0xff }, + .flags = KGDB_HW_BREAKPOINT, +}; + +static const unsigned char mn10300_kgdb_insn_sizes[256] = +{ + /* 1 2 3 4 5 6 7 8 9 a b c d e f */ + 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, /* 0 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 1 */ + 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, /* 2 */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, /* 3 */ + 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, /* 4 */ + 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, /* 5 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 7 */ + 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 8 */ + 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 9 */ + 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* a */ + 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* b */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, /* c */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* d */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* e */ + 0, 2, 2, 2, 2, 2, 2, 4, 0, 3, 0, 4, 0, 6, 7, 1 /* f */ +}; + +/* + * Attempt to emulate single stepping by means of breakpoint instructions. + * Although there is a single-step trace flag in EPSW, its use is not + * sufficiently documented and is only intended for use with the JTAG debugger. + */ +static int kgdb_arch_do_singlestep(struct pt_regs *regs) +{ + unsigned long arg; + unsigned size; + u8 *pc = (u8 *)regs->pc, *sp = (u8 *)(regs + 1), cur; + u8 *x = NULL, *y = NULL; + int ret; + + ret = probe_kernel_read(&cur, pc, 1); + if (ret < 0) + return ret; + + size = mn10300_kgdb_insn_sizes[cur]; + if (size > 0) { + x = pc + size; + goto set_x; + } + + switch (cur) { + /* Bxx (d8,PC) */ + case 0xc0 ... 0xca: + ret = probe_kernel_read(&arg, pc + 1, 1); + if (ret < 0) + return ret; + x = pc + 2; + if (arg >= 0 && arg <= 2) + goto set_x; + y = pc + (s8)arg; + goto set_x_and_y; + + /* LXX (d8,PC) */ + case 0xd0 ... 0xda: + x = pc + 1; + if (regs->pc == regs->lar) + goto set_x; + y = (u8 *)regs->lar; + goto set_x_and_y; + + /* SETLB - loads the next four bytes into the LIR register + * (which mustn't include a breakpoint instruction) */ + case 0xdb: + x = pc + 5; + goto set_x; + + /* JMP (d16,PC) or CALL (d16,PC) */ + case 0xcc: + case 0xcd: + ret = probe_kernel_read(&arg, pc + 1, 2); + if (ret < 0) + return ret; + x = pc + (s16)arg; + goto set_x; + + /* JMP (d32,PC) or CALL (d32,PC) */ + case 0xdc: + case 0xdd: + ret = probe_kernel_read(&arg, pc + 1, 4); + if (ret < 0) + return ret; + x = pc + (s32)arg; + goto set_x; + + /* RETF */ + case 0xde: + x = (u8 *)regs->mdr; + goto set_x; + + /* RET */ + case 0xdf: + ret = probe_kernel_read(&arg, pc + 2, 1); + if (ret < 0) + return ret; + ret = probe_kernel_read(&x, sp + (s8)arg, 4); + if (ret < 0) + return ret; + goto set_x; + + case 0xf0: + ret = probe_kernel_read(&cur, pc + 1, 1); + if (ret < 0) + return ret; + + if (cur >= 0xf0 && cur <= 0xf7) { + /* JMP (An) / CALLS (An) */ + switch (cur & 3) { + case 0: x = (u8 *)regs->a0; break; + case 1: x = (u8 *)regs->a1; break; + case 2: x = (u8 *)regs->a2; break; + case 3: x = (u8 *)regs->a3; break; + } + goto set_x; + } else if (cur == 0xfc) { + /* RETS */ + ret = probe_kernel_read(&x, sp, 4); + if (ret < 0) + return ret; + goto set_x; + } else if (cur == 0xfd) { + /* RTI */ + ret = probe_kernel_read(&x, sp + 4, 4); + if (ret < 0) + return ret; + goto set_x; + } else { + x = pc + 2; + goto set_x; + } + break; + + /* potential 3-byte conditional branches */ + case 0xf8: + ret = probe_kernel_read(&cur, pc + 1, 1); + if (ret < 0) + return ret; + x = pc + 3; + + if (cur >= 0xe8 && cur <= 0xeb) { + ret = probe_kernel_read(&arg, pc + 2, 1); + if (ret < 0) + return ret; + if (arg >= 0 && arg <= 3) + goto set_x; + y = pc + (s8)arg; + goto set_x_and_y; + } + goto set_x; + + case 0xfa: + ret = probe_kernel_read(&cur, pc + 1, 1); + if (ret < 0) + return ret; + + if (cur == 0xff) { + /* CALLS (d16,PC) */ + ret = probe_kernel_read(&arg, pc + 2, 2); + if (ret < 0) + return ret; + x = pc + (s16)arg; + goto set_x; + } + + x = pc + 4; + goto set_x; + + case 0xfc: + ret = probe_kernel_read(&cur, pc + 1, 1); + if (ret < 0) + return ret; + + if (cur == 0xff) { + /* CALLS (d32,PC) */ + ret = probe_kernel_read(&arg, pc + 2, 4); + if (ret < 0) + return ret; + x = pc + (s32)arg; + goto set_x; + } + + x = pc + 6; + goto set_x; + } + + return 0; + +set_x: + kgdb_sstep_bp_addr[0] = x; + kgdb_sstep_bp_addr[1] = NULL; + ret = probe_kernel_read(&kgdb_sstep_bp[0], x, 1); + if (ret < 0) + return ret; + ret = probe_kernel_write(x, &arch_kgdb_ops.gdb_bpt_instr, 1); + if (ret < 0) + return ret; + kgdb_sstep_thread = current_thread_info(); + debugger_local_cache_flushinv_one(x); + return ret; + +set_x_and_y: + kgdb_sstep_bp_addr[0] = x; + kgdb_sstep_bp_addr[1] = y; + ret = probe_kernel_read(&kgdb_sstep_bp[0], x, 1); + if (ret < 0) + return ret; + ret = probe_kernel_read(&kgdb_sstep_bp[1], y, 1); + if (ret < 0) + return ret; + ret = probe_kernel_write(x, &arch_kgdb_ops.gdb_bpt_instr, 1); + if (ret < 0) + return ret; + ret = probe_kernel_write(y, &arch_kgdb_ops.gdb_bpt_instr, 1); + if (ret < 0) { + probe_kernel_write(kgdb_sstep_bp_addr[0], + &kgdb_sstep_bp[0], 1); + } else { + kgdb_sstep_thread = current_thread_info(); + } + debugger_local_cache_flushinv_one(x); + debugger_local_cache_flushinv_one(y); + return ret; +} + +/* + * Remove emplaced single-step breakpoints, returning true if we hit one of + * them. + */ +static bool kgdb_arch_undo_singlestep(struct pt_regs *regs) +{ + bool hit = false; + u8 *x = kgdb_sstep_bp_addr[0], *y = kgdb_sstep_bp_addr[1]; + u8 opcode; + + if (kgdb_sstep_thread == current_thread_info()) { + if (x) { + if (x == (u8 *)regs->pc) + hit = true; + if (probe_kernel_read(&opcode, x, + 1) < 0 || + opcode != 0xff) + BUG(); + probe_kernel_write(x, &kgdb_sstep_bp[0], 1); + debugger_local_cache_flushinv_one(x); + } + if (y) { + if (y == (u8 *)regs->pc) + hit = true; + if (probe_kernel_read(&opcode, y, + 1) < 0 || + opcode != 0xff) + BUG(); + probe_kernel_write(y, &kgdb_sstep_bp[1], 1); + debugger_local_cache_flushinv_one(y); + } + } + + kgdb_sstep_bp_addr[0] = NULL; + kgdb_sstep_bp_addr[1] = NULL; + kgdb_sstep_thread = NULL; + return hit; +} + +/* + * Catch a single-step-pending thread being deleted and make sure the global + * single-step state is cleared. At this point the breakpoints should have + * been removed by __switch_to(). + */ +void free_thread_info(struct thread_info *ti) +{ + if (kgdb_sstep_thread == ti) { + kgdb_sstep_thread = NULL; + + /* However, we may now be running in degraded mode, with most + * of the CPUs disabled until such a time as KGDB is reentered, + * so force immediate reentry */ + kgdb_breakpoint(); + } + kfree(ti); +} + +/* + * Handle unknown packets and [CcsDk] packets + * - at this point breakpoints have been installed + */ +int kgdb_arch_handle_exception(int vector, int signo, int err_code, + char *remcom_in_buffer, char *remcom_out_buffer, + struct pt_regs *regs) +{ + long addr; + char *ptr; + + switch (remcom_in_buffer[0]) { + case 'c': + case 's': + /* try to read optional parameter, pc unchanged if no parm */ + ptr = &remcom_in_buffer[1]; + if (kgdb_hex2long(&ptr, &addr)) + regs->pc = addr; + case 'D': + case 'k': + atomic_set(&kgdb_cpu_doing_single_step, -1); + + if (remcom_in_buffer[0] == 's') { + kgdb_arch_do_singlestep(regs); + kgdb_single_step = 1; + atomic_set(&kgdb_cpu_doing_single_step, + raw_smp_processor_id()); + } + return 0; + } + return -1; /* this means that we do not want to exit from the handler */ +} + +/* + * Handle event interception + * - returns 0 if the exception should be skipped, -ERROR otherwise. + */ +int debugger_intercept(enum exception_code excep, int signo, int si_code, + struct pt_regs *regs) +{ + int ret; + + if (kgdb_arch_undo_singlestep(regs)) { + excep = EXCEP_TRAP; + signo = SIGTRAP; + si_code = TRAP_TRACE; + } + + ret = kgdb_handle_exception(excep, signo, si_code, regs); + + debugger_local_cache_flushinv(); + + return ret; +} + +/* + * Determine if we've hit a debugger special breakpoint + */ +int at_debugger_breakpoint(struct pt_regs *regs) +{ + return regs->pc == (unsigned long)&__arch_kgdb_breakpoint; +} + +/* + * Initialise kgdb + */ +int kgdb_arch_init(void) +{ + return 0; +} + +/* + * Do something, perhaps, but don't know what. + */ +void kgdb_arch_exit(void) +{ +} + +#ifdef CONFIG_SMP +void debugger_nmi_interrupt(struct pt_regs *regs, enum exception_code code) +{ + kgdb_nmicallback(arch_smp_processor_id(), regs); + debugger_local_cache_flushinv(); +} + +void kgdb_roundup_cpus(unsigned long flags) +{ + smp_jump_to_debugger(); +} +#endif diff --git a/arch/mn10300/kernel/kprobes.c b/arch/mn10300/kernel/kprobes.c new file mode 100644 index 00000000..0311a7fc --- /dev/null +++ b/arch/mn10300/kernel/kprobes.c @@ -0,0 +1,656 @@ +/* MN10300 Kernel probes implementation + * + * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved. + * Written by Mark Salter (msalter@redhat.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public Licence as published by + * the Free Software Foundation; either version 2 of the Licence, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public Licence for more details. + * + * You should have received a copy of the GNU General Public Licence + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <linux/kprobes.h> +#include <linux/ptrace.h> +#include <linux/spinlock.h> +#include <linux/preempt.h> +#include <linux/kdebug.h> +#include <asm/cacheflush.h> + +struct kretprobe_blackpoint kretprobe_blacklist[] = { { NULL, NULL } }; +const int kretprobe_blacklist_size = ARRAY_SIZE(kretprobe_blacklist); + +/* kprobe_status settings */ +#define KPROBE_HIT_ACTIVE 0x00000001 +#define KPROBE_HIT_SS 0x00000002 + +static struct kprobe *cur_kprobe; +static unsigned long cur_kprobe_orig_pc; +static unsigned long cur_kprobe_next_pc; +static int cur_kprobe_ss_flags; +static unsigned long kprobe_status; +static kprobe_opcode_t cur_kprobe_ss_buf[MAX_INSN_SIZE + 2]; +static unsigned long cur_kprobe_bp_addr; + +DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; + + +/* singlestep flag bits */ +#define SINGLESTEP_BRANCH 1 +#define SINGLESTEP_PCREL 2 + +#define READ_BYTE(p, valp) \ + do { *(u8 *)(valp) = *(u8 *)(p); } while (0) + +#define READ_WORD16(p, valp) \ + do { \ + READ_BYTE((p), (valp)); \ + READ_BYTE((u8 *)(p) + 1, (u8 *)(valp) + 1); \ + } while (0) + +#define READ_WORD32(p, valp) \ + do { \ + READ_BYTE((p), (valp)); \ + READ_BYTE((u8 *)(p) + 1, (u8 *)(valp) + 1); \ + READ_BYTE((u8 *)(p) + 2, (u8 *)(valp) + 2); \ + READ_BYTE((u8 *)(p) + 3, (u8 *)(valp) + 3); \ + } while (0) + + +static const u8 mn10300_insn_sizes[256] = +{ + /* 1 2 3 4 5 6 7 8 9 a b c d e f */ + 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, /* 0 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 1 */ + 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, /* 2 */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, /* 3 */ + 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, /* 4 */ + 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, /* 5 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 7 */ + 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 8 */ + 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 9 */ + 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* a */ + 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* b */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, /* c */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* d */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* e */ + 0, 2, 2, 2, 2, 2, 2, 4, 0, 3, 0, 4, 0, 6, 7, 1 /* f */ +}; + +#define LT (1 << 0) +#define GT (1 << 1) +#define GE (1 << 2) +#define LE (1 << 3) +#define CS (1 << 4) +#define HI (1 << 5) +#define CC (1 << 6) +#define LS (1 << 7) +#define EQ (1 << 8) +#define NE (1 << 9) +#define RA (1 << 10) +#define VC (1 << 11) +#define VS (1 << 12) +#define NC (1 << 13) +#define NS (1 << 14) + +static const u16 cond_table[] = { + /* V C N Z */ + /* 0 0 0 0 */ (NE | NC | CC | VC | GE | GT | HI), + /* 0 0 0 1 */ (EQ | NC | CC | VC | GE | LE | LS), + /* 0 0 1 0 */ (NE | NS | CC | VC | LT | LE | HI), + /* 0 0 1 1 */ (EQ | NS | CC | VC | LT | LE | LS), + /* 0 1 0 0 */ (NE | NC | CS | VC | GE | GT | LS), + /* 0 1 0 1 */ (EQ | NC | CS | VC | GE | LE | LS), + /* 0 1 1 0 */ (NE | NS | CS | VC | LT | LE | LS), + /* 0 1 1 1 */ (EQ | NS | CS | VC | LT | LE | LS), + /* 1 0 0 0 */ (NE | NC | CC | VS | LT | LE | HI), + /* 1 0 0 1 */ (EQ | NC | CC | VS | LT | LE | LS), + /* 1 0 1 0 */ (NE | NS | CC | VS | GE | GT | HI), + /* 1 0 1 1 */ (EQ | NS | CC | VS | GE | LE | LS), + /* 1 1 0 0 */ (NE | NC | CS | VS | LT | LE | LS), + /* 1 1 0 1 */ (EQ | NC | CS | VS | LT | LE | LS), + /* 1 1 1 0 */ (NE | NS | CS | VS | GE | GT | LS), + /* 1 1 1 1 */ (EQ | NS | CS | VS | GE | LE | LS), +}; + +/* + * Calculate what the PC will be after executing next instruction + */ +static unsigned find_nextpc(struct pt_regs *regs, int *flags) +{ + unsigned size; + s8 x8; + s16 x16; + s32 x32; + u8 opc, *pc, *sp, *next; + + next = 0; + *flags = SINGLESTEP_PCREL; + + pc = (u8 *) regs->pc; + sp = (u8 *) (regs + 1); + opc = *pc; + + size = mn10300_insn_sizes[opc]; + if (size > 0) { + next = pc + size; + } else { + switch (opc) { + /* Bxx (d8,PC) */ + case 0xc0 ... 0xca: + x8 = 2; + if (cond_table[regs->epsw & 0xf] & (1 << (opc & 0xf))) + x8 = (s8)pc[1]; + next = pc + x8; + *flags |= SINGLESTEP_BRANCH; + break; + + /* JMP (d16,PC) or CALL (d16,PC) */ + case 0xcc: + case 0xcd: + READ_WORD16(pc + 1, &x16); + next = pc + x16; + *flags |= SINGLESTEP_BRANCH; + break; + + /* JMP (d32,PC) or CALL (d32,PC) */ + case 0xdc: + case 0xdd: + READ_WORD32(pc + 1, &x32); + next = pc + x32; + *flags |= SINGLESTEP_BRANCH; + break; + + /* RETF */ + case 0xde: + next = (u8 *)regs->mdr; + *flags &= ~SINGLESTEP_PCREL; + *flags |= SINGLESTEP_BRANCH; + break; + + /* RET */ + case 0xdf: + sp += pc[2]; + READ_WORD32(sp, &x32); + next = (u8 *)x32; + *flags &= ~SINGLESTEP_PCREL; + *flags |= SINGLESTEP_BRANCH; + break; + + case 0xf0: + next = pc + 2; + opc = pc[1]; + if (opc >= 0xf0 && opc <= 0xf7) { + /* JMP (An) / CALLS (An) */ + switch (opc & 3) { + case 0: + next = (u8 *)regs->a0; + break; + case 1: + next = (u8 *)regs->a1; + break; + case 2: + next = (u8 *)regs->a2; + break; + case 3: + next = (u8 *)regs->a3; + break; + } + *flags &= ~SINGLESTEP_PCREL; + *flags |= SINGLESTEP_BRANCH; + } else if (opc == 0xfc) { + /* RETS */ + READ_WORD32(sp, &x32); + next = (u8 *)x32; + *flags &= ~SINGLESTEP_PCREL; + *flags |= SINGLESTEP_BRANCH; + } else if (opc == 0xfd) { + /* RTI */ + READ_WORD32(sp + 4, &x32); + next = (u8 *)x32; + *flags &= ~SINGLESTEP_PCREL; + *flags |= SINGLESTEP_BRANCH; + } + break; + + /* potential 3-byte conditional branches */ + case 0xf8: + next = pc + 3; + opc = pc[1]; + if (opc >= 0xe8 && opc <= 0xeb && + (cond_table[regs->epsw & 0xf] & + (1 << ((opc & 0xf) + 3))) + ) { + READ_BYTE(pc+2, &x8); + next = pc + x8; + *flags |= SINGLESTEP_BRANCH; + } + break; + + case 0xfa: + if (pc[1] == 0xff) { + /* CALLS (d16,PC) */ + READ_WORD16(pc + 2, &x16); + next = pc + x16; + } else + next = pc + 4; + *flags |= SINGLESTEP_BRANCH; + break; + + case 0xfc: + x32 = 6; + if (pc[1] == 0xff) { + /* CALLS (d32,PC) */ + READ_WORD32(pc + 2, &x32); + } + next = pc + x32; + *flags |= SINGLESTEP_BRANCH; + break; + /* LXX (d8,PC) */ + /* SETLB - loads the next four bytes into the LIR reg */ + case 0xd0 ... 0xda: + case 0xdb: + panic("Can't singlestep Lxx/SETLB\n"); + break; + } + } + return (unsigned)next; + +} + +/* + * set up out of place singlestep of some branching instructions + */ +static unsigned __kprobes singlestep_branch_setup(struct pt_regs *regs) +{ + u8 opc, *pc, *sp, *next; + + next = NULL; + pc = (u8 *) regs->pc; + sp = (u8 *) (regs + 1); + + switch (pc[0]) { + case 0xc0 ... 0xca: /* Bxx (d8,PC) */ + case 0xcc: /* JMP (d16,PC) */ + case 0xdc: /* JMP (d32,PC) */ + case 0xf8: /* Bxx (d8,PC) 3-byte version */ + /* don't really need to do anything except cause trap */ + next = pc; + break; + + case 0xcd: /* CALL (d16,PC) */ + pc[1] = 5; + pc[2] = 0; + next = pc + 5; + break; + + case 0xdd: /* CALL (d32,PC) */ + pc[1] = 7; + pc[2] = 0; + pc[3] = 0; + pc[4] = 0; + next = pc + 7; + break; + + case 0xde: /* RETF */ + next = pc + 3; + regs->mdr = (unsigned) next; + break; + + case 0xdf: /* RET */ + sp += pc[2]; + next = pc + 3; + *(unsigned *)sp = (unsigned) next; + break; + + case 0xf0: + next = pc + 2; + opc = pc[1]; + if (opc >= 0xf0 && opc <= 0xf3) { + /* CALLS (An) */ + /* use CALLS (d16,PC) to avoid mucking with An */ + pc[0] = 0xfa; + pc[1] = 0xff; + pc[2] = 4; + pc[3] = 0; + next = pc + 4; + } else if (opc >= 0xf4 && opc <= 0xf7) { + /* JMP (An) */ + next = pc; + } else if (opc == 0xfc) { + /* RETS */ + next = pc + 2; + *(unsigned *) sp = (unsigned) next; + } else if (opc == 0xfd) { + /* RTI */ + next = pc + 2; + *(unsigned *)(sp + 4) = (unsigned) next; + } + break; + + case 0xfa: /* CALLS (d16,PC) */ + pc[2] = 4; + pc[3] = 0; + next = pc + 4; + break; + + case 0xfc: /* CALLS (d32,PC) */ + pc[2] = 6; + pc[3] = 0; + pc[4] = 0; + pc[5] = 0; + next = pc + 6; + break; + + case 0xd0 ... 0xda: /* LXX (d8,PC) */ + case 0xdb: /* SETLB */ + panic("Can't singlestep Lxx/SETLB\n"); + } + + return (unsigned) next; +} + +int __kprobes arch_prepare_kprobe(struct kprobe *p) +{ + return 0; +} + +void __kprobes arch_copy_kprobe(struct kprobe *p) +{ + memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE); +} + +void __kprobes arch_arm_kprobe(struct kprobe *p) +{ + *p->addr = BREAKPOINT_INSTRUCTION; + flush_icache_range((unsigned long) p->addr, + (unsigned long) p->addr + sizeof(kprobe_opcode_t)); +} + +void __kprobes arch_disarm_kprobe(struct kprobe *p) +{ +#ifndef CONFIG_MN10300_CACHE_SNOOP + mn10300_dcache_flush(); + mn10300_icache_inv(); +#endif +} + +void arch_remove_kprobe(struct kprobe *p) +{ +} + +static inline +void __kprobes disarm_kprobe(struct kprobe *p, struct pt_regs *regs) +{ + *p->addr = p->opcode; + regs->pc = (unsigned long) p->addr; +#ifndef CONFIG_MN10300_CACHE_SNOOP + mn10300_dcache_flush(); + mn10300_icache_inv(); +#endif +} + +static inline +void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs) +{ + unsigned long nextpc; + + cur_kprobe_orig_pc = regs->pc; + memcpy(cur_kprobe_ss_buf, &p->ainsn.insn[0], MAX_INSN_SIZE); + regs->pc = (unsigned long) cur_kprobe_ss_buf; + + nextpc = find_nextpc(regs, &cur_kprobe_ss_flags); + if (cur_kprobe_ss_flags & SINGLESTEP_PCREL) + cur_kprobe_next_pc = cur_kprobe_orig_pc + (nextpc - regs->pc); + else + cur_kprobe_next_pc = nextpc; + + /* branching instructions need special handling */ + if (cur_kprobe_ss_flags & SINGLESTEP_BRANCH) + nextpc = singlestep_branch_setup(regs); + + cur_kprobe_bp_addr = nextpc; + + *(u8 *) nextpc = BREAKPOINT_INSTRUCTION; + mn10300_dcache_flush_range2((unsigned) cur_kprobe_ss_buf, + sizeof(cur_kprobe_ss_buf)); + mn10300_icache_inv(); +} + +static inline int __kprobes kprobe_handler(struct pt_regs *regs) +{ + struct kprobe *p; + int ret = 0; + unsigned int *addr = (unsigned int *) regs->pc; + + /* We're in an interrupt, but this is clear and BUG()-safe. */ + preempt_disable(); + + /* Check we're not actually recursing */ + if (kprobe_running()) { + /* We *are* holding lock here, so this is safe. + Disarm the probe we just hit, and ignore it. */ + p = get_kprobe(addr); + if (p) { + disarm_kprobe(p, regs); + ret = 1; + } else { + p = cur_kprobe; + if (p->break_handler && p->break_handler(p, regs)) + goto ss_probe; + } + /* If it's not ours, can't be delete race, (we hold lock). */ + goto no_kprobe; + } + + p = get_kprobe(addr); + if (!p) { + if (*addr != BREAKPOINT_INSTRUCTION) { + /* The breakpoint instruction was removed right after + * we hit it. Another cpu has removed either a + * probepoint or a debugger breakpoint at this address. + * In either case, no further handling of this + * interrupt is appropriate. + */ + ret = 1; + } + /* Not one of ours: let kernel handle it */ + goto no_kprobe; + } + + kprobe_status = KPROBE_HIT_ACTIVE; + cur_kprobe = p; + if (p->pre_handler(p, regs)) { + /* handler has already set things up, so skip ss setup */ + return 1; + } + +ss_probe: + prepare_singlestep(p, regs); + kprobe_status = KPROBE_HIT_SS; + return 1; + +no_kprobe: + preempt_enable_no_resched(); + return ret; +} + +/* + * Called after single-stepping. p->addr is the address of the + * instruction whose first byte has been replaced by the "breakpoint" + * instruction. To avoid the SMP problems that can occur when we + * temporarily put back the original opcode to single-step, we + * single-stepped a copy of the instruction. The address of this + * copy is p->ainsn.insn. + */ +static void __kprobes resume_execution(struct kprobe *p, struct pt_regs *regs) +{ + /* we may need to fixup regs/stack after singlestepping a call insn */ + if (cur_kprobe_ss_flags & SINGLESTEP_BRANCH) { + regs->pc = cur_kprobe_orig_pc; + switch (p->ainsn.insn[0]) { + case 0xcd: /* CALL (d16,PC) */ + *(unsigned *) regs->sp = regs->mdr = regs->pc + 5; + break; + case 0xdd: /* CALL (d32,PC) */ + /* fixup mdr and return address on stack */ + *(unsigned *) regs->sp = regs->mdr = regs->pc + 7; + break; + case 0xf0: + if (p->ainsn.insn[1] >= 0xf0 && + p->ainsn.insn[1] <= 0xf3) { + /* CALLS (An) */ + /* fixup MDR and return address on stack */ + regs->mdr = regs->pc + 2; + *(unsigned *) regs->sp = regs->mdr; + } + break; + + case 0xfa: /* CALLS (d16,PC) */ + /* fixup MDR and return address on stack */ + *(unsigned *) regs->sp = regs->mdr = regs->pc + 4; + break; + + case 0xfc: /* CALLS (d32,PC) */ + /* fixup MDR and return address on stack */ + *(unsigned *) regs->sp = regs->mdr = regs->pc + 6; + break; + } + } + + regs->pc = cur_kprobe_next_pc; + cur_kprobe_bp_addr = 0; +} + +static inline int __kprobes post_kprobe_handler(struct pt_regs *regs) +{ + if (!kprobe_running()) + return 0; + + if (cur_kprobe->post_handler) + cur_kprobe->post_handler(cur_kprobe, regs, 0); + + resume_execution(cur_kprobe, regs); + reset_current_kprobe(); + preempt_enable_no_resched(); + return 1; +} + +/* Interrupts disabled, kprobe_lock held. */ +static inline +int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr) +{ + if (cur_kprobe->fault_handler && + cur_kprobe->fault_handler(cur_kprobe, regs, trapnr)) + return 1; + + if (kprobe_status & KPROBE_HIT_SS) { + resume_execution(cur_kprobe, regs); + reset_current_kprobe(); + preempt_enable_no_resched(); + } + return 0; +} + +/* + * Wrapper routine to for handling exceptions. + */ +int __kprobes kprobe_exceptions_notify(struct notifier_block *self, + unsigned long val, void *data) +{ + struct die_args *args = data; + + switch (val) { + case DIE_BREAKPOINT: + if (cur_kprobe_bp_addr != args->regs->pc) { + if (kprobe_handler(args->regs)) + return NOTIFY_STOP; + } else { + if (post_kprobe_handler(args->regs)) + return NOTIFY_STOP; + } + break; + case DIE_GPF: + if (kprobe_running() && + kprobe_fault_handler(args->regs, args->trapnr)) + return NOTIFY_STOP; + break; + default: + break; + } + return NOTIFY_DONE; +} + +/* Jprobes support. */ +static struct pt_regs jprobe_saved_regs; +static struct pt_regs *jprobe_saved_regs_location; +static kprobe_opcode_t jprobe_saved_stack[MAX_STACK_SIZE]; + +int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct jprobe *jp = container_of(p, struct jprobe, kp); + + jprobe_saved_regs_location = regs; + memcpy(&jprobe_saved_regs, regs, sizeof(struct pt_regs)); + + /* Save a whole stack frame, this gets arguments + * pushed onto the stack after using up all the + * arg registers. + */ + memcpy(&jprobe_saved_stack, regs + 1, sizeof(jprobe_saved_stack)); + + /* setup return addr to the jprobe handler routine */ + regs->pc = (unsigned long) jp->entry; + return 1; +} + +void __kprobes jprobe_return(void) +{ + void *orig_sp = jprobe_saved_regs_location + 1; + + preempt_enable_no_resched(); + asm volatile(" mov %0,sp\n" + ".globl jprobe_return_bp_addr\n" + "jprobe_return_bp_addr:\n\t" + " .byte 0xff\n" + : : "d" (orig_sp)); +} + +extern void jprobe_return_bp_addr(void); + +int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) +{ + u8 *addr = (u8 *) regs->pc; + + if (addr == (u8 *) jprobe_return_bp_addr) { + if (jprobe_saved_regs_location != regs) { + printk(KERN_ERR"JPROBE:" + " Current regs (%p) does not match saved regs" + " (%p).\n", + regs, jprobe_saved_regs_location); + BUG(); + } + + /* Restore old register state. + */ + memcpy(regs, &jprobe_saved_regs, sizeof(struct pt_regs)); + + memcpy(regs + 1, &jprobe_saved_stack, + sizeof(jprobe_saved_stack)); + return 1; + } + return 0; +} + +int __init arch_init_kprobes(void) +{ + return 0; +} diff --git a/arch/mn10300/kernel/kthread.S b/arch/mn10300/kernel/kthread.S new file mode 100644 index 00000000..b5ae467a --- /dev/null +++ b/arch/mn10300/kernel/kthread.S @@ -0,0 +1,31 @@ +/* MN10300 Kernel thread trampoline function + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by Mark Salter (msalter@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + .text + +############################################################################### +# +# kernel_thread_helper - trampoline for kernel_thread() +# +# On entry: +# A2 = address of function to call +# D2 = function argument +# +############################################################################### + .globl kernel_thread_helper + .type kernel_thread_helper,@function +kernel_thread_helper: + mov do_exit,d1 + mov d1,(sp) + mov d1,mdr + mov d2,d0 + jmp (a2) + + .size kernel_thread_helper,.-kernel_thread_helper diff --git a/arch/mn10300/kernel/mn10300-debug.c b/arch/mn10300/kernel/mn10300-debug.c new file mode 100644 index 00000000..bd819647 --- /dev/null +++ b/arch/mn10300/kernel/mn10300-debug.c @@ -0,0 +1,58 @@ +/* Debugging stuff for the MN10300-based processors + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/sched.h> +#include <asm/serial-regs.h> + +#undef MN10300_CONSOLE_ON_SERIO + +/* + * write a string directly through one of the serial ports on-board the MN10300 + */ +#ifdef MN10300_CONSOLE_ON_SERIO +void debug_to_serial_mnser(const char *p, int n) +{ + char ch; + + for (; n > 0; n--) { + ch = *p++; + +#if MN10300_CONSOLE_ON_SERIO == 0 + while (SC0STR & (SC01STR_TBF)) continue; + SC0TXB = ch; + while (SC0STR & (SC01STR_TBF)) continue; + if (ch == 0x0a) { + SC0TXB = 0x0d; + while (SC0STR & (SC01STR_TBF)) continue; + } + +#elif MN10300_CONSOLE_ON_SERIO == 1 + while (SC1STR & (SC01STR_TBF)) continue; + SC1TXB = ch; + while (SC1STR & (SC01STR_TBF)) continue; + if (ch == 0x0a) { + SC1TXB = 0x0d; + while (SC1STR & (SC01STR_TBF)) continue; + } + +#elif MN10300_CONSOLE_ON_SERIO == 2 + while (SC2STR & (SC2STR_TBF)) continue; + SC2TXB = ch; + while (SC2STR & (SC2STR_TBF)) continue; + if (ch == 0x0a) { + SC2TXB = 0x0d; + while (SC2STR & (SC2STR_TBF)) continue; + } + +#endif + } +} +#endif + diff --git a/arch/mn10300/kernel/mn10300-serial-low.S b/arch/mn10300/kernel/mn10300-serial-low.S new file mode 100644 index 00000000..dfc1b6f2 --- /dev/null +++ b/arch/mn10300/kernel/mn10300-serial-low.S @@ -0,0 +1,191 @@ +############################################################################### +# +# Virtual DMA driver for MN10300 serial ports +# +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/page.h> +#include <asm/smp.h> +#include <asm/cpu-regs.h> +#include <asm/frame.inc> +#include <asm/timer-regs.h> +#include <proc/cache.h> +#include <unit/timex.h> +#include "mn10300-serial.h" + +#define SCxCTR 0x00 +#define SCxICR 0x04 +#define SCxTXB 0x08 +#define SCxRXB 0x09 +#define SCxSTR 0x0c +#define SCxTIM 0x0d + + .text + +############################################################################### +# +# serial port interrupt virtual DMA entry point +# - intended to run at interrupt priority 1 (not affected by local_irq_disable) +# +############################################################################### + .balign L1_CACHE_BYTES +ENTRY(mn10300_serial_vdma_interrupt) +# or EPSW_IE,psw # permit overriding by + # debugging interrupts + movm [d2,d3,a2,a3,exreg0],(sp) + + movhu (IAGR),a2 # see if which interrupt is + # pending + and IAGR_GN,a2 + add a2,a2 + add mn10300_serial_int_tbl,a2 + + mov (a2+),a3 + mov (__iobase,a3),e2 + mov (a2),a2 + jmp (a2) + +############################################################################### +# +# serial port receive interrupt virtual DMA entry point +# - intended to run at interrupt priority 1 (not affected by local_irq_disable) +# - stores data/status byte pairs in the ring buffer +# - induces a scheduler tick timer interrupt when done, which we then subvert +# on entry: +# A3 struct mn10300_serial_port * +# E2 I/O port base +# +############################################################################### +ENTRY(mn10300_serial_vdma_rx_handler) + mov (__rx_icr,a3),e3 + mov GxICR_DETECT,d2 + movbu d2,(e3) # ACK the interrupt + movhu (e3),d2 # flush + + mov (__rx_inp,a3),d3 + mov d3,a2 + add 2,d3 + and MNSC_BUFFER_SIZE-1,d3 + mov (__rx_outp,a3),d2 + cmp d3,d2 + beq mnsc_vdma_rx_overflow + + mov (__rx_buffer,a3),d2 + add d2,a2 + movhu (SCxSTR,e2),d2 + movbu d2,(1,a2) + movbu (SCxRXB,e2),d2 + movbu d2,(a2) + mov d3,(__rx_inp,a3) + bset MNSCx_RX_AVAIL,(__intr_flags,a3) + +mnsc_vdma_rx_done: + mov (__tm_icr,a3),a2 + mov GxICR_LEVEL_6|GxICR_ENABLE|GxICR_REQUEST|GxICR_DETECT,d2 + movhu d2,(a2) # request a slow interrupt + movhu (a2),d2 # flush + + movm (sp),[d2,d3,a2,a3,exreg0] + rti + +mnsc_vdma_rx_overflow: + bset MNSCx_RX_OVERF,(__intr_flags,a3) + bra mnsc_vdma_rx_done + +############################################################################### +# +# serial port transmit interrupt virtual DMA entry point +# - intended to run at interrupt priority 1 (not affected by local_irq_disable) +# - retrieves data bytes from the ring buffer and passes them to the serial port +# - induces a scheduler tick timer interrupt when done, which we then subvert +# A3 struct mn10300_serial_port * +# E2 I/O port base +# +############################################################################### + .balign L1_CACHE_BYTES +ENTRY(mn10300_serial_vdma_tx_handler) + mov (__tx_icr,a3),e3 + mov GxICR_DETECT,d2 + movbu d2,(e3) # ACK the interrupt + movhu (e3),d2 # flush + + btst 0x01,(__tx_break,a3) # handle transmit break request + bne mnsc_vdma_tx_break + + movbu (SCxSTR,e2),d2 # don't try and transmit a char if the + # buffer is not empty + btst SC01STR_TBF,d2 # (may have tried to jumpstart) + bne mnsc_vdma_tx_noint + + movbu (__tx_xchar,a3),d2 # handle hi-pri XON/XOFF + or d2,d2 + bne mnsc_vdma_tx_xchar + + mov (__uart_state,a3),a2 # see if the TTY Tx queue has anything in it + mov (__xmit_tail,a2),d3 + mov (__xmit_head,a2),d2 + cmp d3,d2 + beq mnsc_vdma_tx_empty + + mov (__xmit_buffer,a2),d2 # get a char from the buffer and + # transmit it + movbu (d3,d2),d2 + movbu d2,(SCxTXB,e2) # Tx + + inc d3 # advance the buffer pointer + and __UART_XMIT_SIZE-1,d3 + mov (__xmit_head,a2),d2 + mov d3,(__xmit_tail,a2) + + sub d3,d2 # see if we've written everything + beq mnsc_vdma_tx_empty + + and __UART_XMIT_SIZE-1,d2 # see if we just made a hole + cmp __UART_XMIT_SIZE-2,d2 + beq mnsc_vdma_tx_made_hole + +mnsc_vdma_tx_done: + mov (__tm_icr,a3),a2 + mov GxICR_LEVEL_6|GxICR_ENABLE|GxICR_REQUEST|GxICR_DETECT,d2 + movhu d2,(a2) # request a slow interrupt + movhu (a2),d2 # flush + +mnsc_vdma_tx_noint: + movm (sp),[d2,d3,a2,a3,exreg0] + rti + +mnsc_vdma_tx_empty: + mov +(NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL)|GxICR_DETECT),d2 + movhu d2,(e3) # disable the interrupt + movhu (e3),d2 # flush + + bset MNSCx_TX_EMPTY,(__intr_flags,a3) + bra mnsc_vdma_tx_done + +mnsc_vdma_tx_break: + movhu (SCxCTR,e2),d2 # turn on break mode + or SC01CTR_BKE,d2 + movhu d2,(SCxCTR,e2) + mov +(NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL)|GxICR_DETECT),d2 + movhu d2,(e3) # disable transmit interrupts on this + # channel + movhu (e3),d2 # flush + bra mnsc_vdma_tx_noint + +mnsc_vdma_tx_xchar: + bclr 0xff,(__tx_xchar,a3) + movbu d2,(SCxTXB,e2) + bra mnsc_vdma_tx_done + +mnsc_vdma_tx_made_hole: + bset MNSCx_TX_SPACE,(__intr_flags,a3) + bra mnsc_vdma_tx_done diff --git a/arch/mn10300/kernel/mn10300-serial.c b/arch/mn10300/kernel/mn10300-serial.c new file mode 100644 index 00000000..94901c56 --- /dev/null +++ b/arch/mn10300/kernel/mn10300-serial.c @@ -0,0 +1,1711 @@ +/* MN10300 On-chip serial port UART driver + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +static const char serial_name[] = "MN10300 Serial driver"; +static const char serial_version[] = "mn10300_serial-1.0"; +static const char serial_revdate[] = "2007-11-06"; + +#if defined(CONFIG_MN10300_TTYSM_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include <linux/module.h> +#include <linux/serial.h> +#include <linux/circ_buf.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/major.h> +#include <linux/string.h> +#include <linux/ioport.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/console.h> +#include <linux/sysrq.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/bitops.h> +#include <asm/serial-regs.h> +#include <unit/timex.h> +#include "mn10300-serial.h" + +#ifdef CONFIG_SMP +#undef GxICR +#define GxICR(X) CROSS_GxICR(X, 0) +#endif /* CONFIG_SMP */ + +#define kenter(FMT, ...) \ + printk(KERN_DEBUG "-->%s(" FMT ")\n", __func__, ##__VA_ARGS__) +#define _enter(FMT, ...) \ + no_printk(KERN_DEBUG "-->%s(" FMT ")\n", __func__, ##__VA_ARGS__) +#define kdebug(FMT, ...) \ + printk(KERN_DEBUG "--- " FMT "\n", ##__VA_ARGS__) +#define _debug(FMT, ...) \ + no_printk(KERN_DEBUG "--- " FMT "\n", ##__VA_ARGS__) +#define kproto(FMT, ...) \ + printk(KERN_DEBUG "### MNSERIAL " FMT " ###\n", ##__VA_ARGS__) +#define _proto(FMT, ...) \ + no_printk(KERN_DEBUG "### MNSERIAL " FMT " ###\n", ##__VA_ARGS__) + +#ifndef CODMSB +/* c_cflag bit meaning */ +#define CODMSB 004000000000 /* change Transfer bit-order */ +#endif + +#define NR_UARTS 3 + +#ifdef CONFIG_MN10300_TTYSM_CONSOLE +static void mn10300_serial_console_write(struct console *co, + const char *s, unsigned count); +static int __init mn10300_serial_console_setup(struct console *co, + char *options); + +static struct uart_driver mn10300_serial_driver; +static struct console mn10300_serial_console = { + .name = "ttySM", + .write = mn10300_serial_console_write, + .device = uart_console_device, + .setup = mn10300_serial_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &mn10300_serial_driver, +}; +#endif + +static struct uart_driver mn10300_serial_driver = { + .owner = NULL, + .driver_name = "mn10300-serial", + .dev_name = "ttySM", + .major = TTY_MAJOR, + .minor = 128, + .nr = NR_UARTS, +#ifdef CONFIG_MN10300_TTYSM_CONSOLE + .cons = &mn10300_serial_console, +#endif +}; + +static unsigned int mn10300_serial_tx_empty(struct uart_port *); +static void mn10300_serial_set_mctrl(struct uart_port *, unsigned int mctrl); +static unsigned int mn10300_serial_get_mctrl(struct uart_port *); +static void mn10300_serial_stop_tx(struct uart_port *); +static void mn10300_serial_start_tx(struct uart_port *); +static void mn10300_serial_send_xchar(struct uart_port *, char ch); +static void mn10300_serial_stop_rx(struct uart_port *); +static void mn10300_serial_enable_ms(struct uart_port *); +static void mn10300_serial_break_ctl(struct uart_port *, int ctl); +static int mn10300_serial_startup(struct uart_port *); +static void mn10300_serial_shutdown(struct uart_port *); +static void mn10300_serial_set_termios(struct uart_port *, + struct ktermios *new, + struct ktermios *old); +static const char *mn10300_serial_type(struct uart_port *); +static void mn10300_serial_release_port(struct uart_port *); +static int mn10300_serial_request_port(struct uart_port *); +static void mn10300_serial_config_port(struct uart_port *, int); +static int mn10300_serial_verify_port(struct uart_port *, + struct serial_struct *); +#ifdef CONFIG_CONSOLE_POLL +static void mn10300_serial_poll_put_char(struct uart_port *, unsigned char); +static int mn10300_serial_poll_get_char(struct uart_port *); +#endif + +static const struct uart_ops mn10300_serial_ops = { + .tx_empty = mn10300_serial_tx_empty, + .set_mctrl = mn10300_serial_set_mctrl, + .get_mctrl = mn10300_serial_get_mctrl, + .stop_tx = mn10300_serial_stop_tx, + .start_tx = mn10300_serial_start_tx, + .send_xchar = mn10300_serial_send_xchar, + .stop_rx = mn10300_serial_stop_rx, + .enable_ms = mn10300_serial_enable_ms, + .break_ctl = mn10300_serial_break_ctl, + .startup = mn10300_serial_startup, + .shutdown = mn10300_serial_shutdown, + .set_termios = mn10300_serial_set_termios, + .type = mn10300_serial_type, + .release_port = mn10300_serial_release_port, + .request_port = mn10300_serial_request_port, + .config_port = mn10300_serial_config_port, + .verify_port = mn10300_serial_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_put_char = mn10300_serial_poll_put_char, + .poll_get_char = mn10300_serial_poll_get_char, +#endif +}; + +static irqreturn_t mn10300_serial_interrupt(int irq, void *dev_id); + +/* + * the first on-chip serial port: ttySM0 (aka SIF0) + */ +#ifdef CONFIG_MN10300_TTYSM0 +struct mn10300_serial_port mn10300_serial_port_sif0 = { + .uart.ops = &mn10300_serial_ops, + .uart.membase = (void __iomem *) &SC0CTR, + .uart.mapbase = (unsigned long) &SC0CTR, + .uart.iotype = UPIO_MEM, + .uart.irq = 0, + .uart.uartclk = 0, /* MN10300_IOCLK, */ + .uart.fifosize = 1, + .uart.flags = UPF_BOOT_AUTOCONF, + .uart.line = 0, + .uart.type = PORT_MN10300, + .uart.lock = + __SPIN_LOCK_UNLOCKED(mn10300_serial_port_sif0.uart.lock), + .name = "ttySM0", + ._iobase = &SC0CTR, + ._control = &SC0CTR, + ._status = (volatile u8 *)&SC0STR, + ._intr = &SC0ICR, + ._rxb = &SC0RXB, + ._txb = &SC0TXB, + .rx_name = "ttySM0:Rx", + .tx_name = "ttySM0:Tx", +#if defined(CONFIG_MN10300_TTYSM0_TIMER8) + .tm_name = "ttySM0:Timer8", + ._tmxmd = &TM8MD, + ._tmxbr = &TM8BR, + ._tmicr = &TM8ICR, + .tm_irq = TM8IRQ, + .div_timer = MNSCx_DIV_TIMER_16BIT, +#elif defined(CONFIG_MN10300_TTYSM0_TIMER0) + .tm_name = "ttySM0:Timer0", + ._tmxmd = &TM0MD, + ._tmxbr = (volatile u16 *)&TM0BR, + ._tmicr = &TM0ICR, + .tm_irq = TM0IRQ, + .div_timer = MNSCx_DIV_TIMER_8BIT, +#elif defined(CONFIG_MN10300_TTYSM0_TIMER2) + .tm_name = "ttySM0:Timer2", + ._tmxmd = &TM2MD, + ._tmxbr = (volatile u16 *)&TM2BR, + ._tmicr = &TM2ICR, + .tm_irq = TM2IRQ, + .div_timer = MNSCx_DIV_TIMER_8BIT, +#else +#error "Unknown config for ttySM0" +#endif + .rx_irq = SC0RXIRQ, + .tx_irq = SC0TXIRQ, + .rx_icr = &GxICR(SC0RXIRQ), + .tx_icr = &GxICR(SC0TXIRQ), + .clock_src = MNSCx_CLOCK_SRC_IOCLK, + .options = 0, +#ifdef CONFIG_GDBSTUB_ON_TTYSM0 + .gdbstub = 1, +#endif +}; +#endif /* CONFIG_MN10300_TTYSM0 */ + +/* + * the second on-chip serial port: ttySM1 (aka SIF1) + */ +#ifdef CONFIG_MN10300_TTYSM1 +struct mn10300_serial_port mn10300_serial_port_sif1 = { + .uart.ops = &mn10300_serial_ops, + .uart.membase = (void __iomem *) &SC1CTR, + .uart.mapbase = (unsigned long) &SC1CTR, + .uart.iotype = UPIO_MEM, + .uart.irq = 0, + .uart.uartclk = 0, /* MN10300_IOCLK, */ + .uart.fifosize = 1, + .uart.flags = UPF_BOOT_AUTOCONF, + .uart.line = 1, + .uart.type = PORT_MN10300, + .uart.lock = + __SPIN_LOCK_UNLOCKED(mn10300_serial_port_sif1.uart.lock), + .name = "ttySM1", + ._iobase = &SC1CTR, + ._control = &SC1CTR, + ._status = (volatile u8 *)&SC1STR, + ._intr = &SC1ICR, + ._rxb = &SC1RXB, + ._txb = &SC1TXB, + .rx_name = "ttySM1:Rx", + .tx_name = "ttySM1:Tx", +#if defined(CONFIG_MN10300_TTYSM1_TIMER9) + .tm_name = "ttySM1:Timer9", + ._tmxmd = &TM9MD, + ._tmxbr = &TM9BR, + ._tmicr = &TM9ICR, + .tm_irq = TM9IRQ, + .div_timer = MNSCx_DIV_TIMER_16BIT, +#elif defined(CONFIG_MN10300_TTYSM1_TIMER3) + .tm_name = "ttySM1:Timer3", + ._tmxmd = &TM3MD, + ._tmxbr = (volatile u16 *)&TM3BR, + ._tmicr = &TM3ICR, + .tm_irq = TM3IRQ, + .div_timer = MNSCx_DIV_TIMER_8BIT, +#elif defined(CONFIG_MN10300_TTYSM1_TIMER12) + .tm_name = "ttySM1/Timer12", + ._tmxmd = &TM12MD, + ._tmxbr = &TM12BR, + ._tmicr = &TM12ICR, + .tm_irq = TM12IRQ, + .div_timer = MNSCx_DIV_TIMER_16BIT, +#else +#error "Unknown config for ttySM1" +#endif + .rx_irq = SC1RXIRQ, + .tx_irq = SC1TXIRQ, + .rx_icr = &GxICR(SC1RXIRQ), + .tx_icr = &GxICR(SC1TXIRQ), + .clock_src = MNSCx_CLOCK_SRC_IOCLK, + .options = 0, +#ifdef CONFIG_GDBSTUB_ON_TTYSM1 + .gdbstub = 1, +#endif +}; +#endif /* CONFIG_MN10300_TTYSM1 */ + +/* + * the third on-chip serial port: ttySM2 (aka SIF2) + */ +#ifdef CONFIG_MN10300_TTYSM2 +struct mn10300_serial_port mn10300_serial_port_sif2 = { + .uart.ops = &mn10300_serial_ops, + .uart.membase = (void __iomem *) &SC2CTR, + .uart.mapbase = (unsigned long) &SC2CTR, + .uart.iotype = UPIO_MEM, + .uart.irq = 0, + .uart.uartclk = 0, /* MN10300_IOCLK, */ + .uart.fifosize = 1, + .uart.flags = UPF_BOOT_AUTOCONF, + .uart.line = 2, +#ifdef CONFIG_MN10300_TTYSM2_CTS + .uart.type = PORT_MN10300_CTS, +#else + .uart.type = PORT_MN10300, +#endif + .uart.lock = + __SPIN_LOCK_UNLOCKED(mn10300_serial_port_sif2.uart.lock), + .name = "ttySM2", + ._iobase = &SC2CTR, + ._control = &SC2CTR, + ._status = (volatile u8 *)&SC2STR, + ._intr = &SC2ICR, + ._rxb = &SC2RXB, + ._txb = &SC2TXB, + .rx_name = "ttySM2:Rx", + .tx_name = "ttySM2:Tx", +#if defined(CONFIG_MN10300_TTYSM2_TIMER10) + .tm_name = "ttySM2/Timer10", + ._tmxmd = &TM10MD, + ._tmxbr = &TM10BR, + ._tmicr = &TM10ICR, + .tm_irq = TM10IRQ, + .div_timer = MNSCx_DIV_TIMER_16BIT, +#elif defined(CONFIG_MN10300_TTYSM2_TIMER9) + .tm_name = "ttySM2/Timer9", + ._tmxmd = &TM9MD, + ._tmxbr = &TM9BR, + ._tmicr = &TM9ICR, + .tm_irq = TM9IRQ, + .div_timer = MNSCx_DIV_TIMER_16BIT, +#elif defined(CONFIG_MN10300_TTYSM2_TIMER1) + .tm_name = "ttySM2/Timer1", + ._tmxmd = &TM1MD, + ._tmxbr = (volatile u16 *)&TM1BR, + ._tmicr = &TM1ICR, + .tm_irq = TM1IRQ, + .div_timer = MNSCx_DIV_TIMER_8BIT, +#elif defined(CONFIG_MN10300_TTYSM2_TIMER3) + .tm_name = "ttySM2/Timer3", + ._tmxmd = &TM3MD, + ._tmxbr = (volatile u16 *)&TM3BR, + ._tmicr = &TM3ICR, + .tm_irq = TM3IRQ, + .div_timer = MNSCx_DIV_TIMER_8BIT, +#else +#error "Unknown config for ttySM2" +#endif + .rx_irq = SC2RXIRQ, + .tx_irq = SC2TXIRQ, + .rx_icr = &GxICR(SC2RXIRQ), + .tx_icr = &GxICR(SC2TXIRQ), + .clock_src = MNSCx_CLOCK_SRC_IOCLK, +#ifdef CONFIG_MN10300_TTYSM2_CTS + .options = MNSCx_OPT_CTS, +#else + .options = 0, +#endif +#ifdef CONFIG_GDBSTUB_ON_TTYSM2 + .gdbstub = 1, +#endif +}; +#endif /* CONFIG_MN10300_TTYSM2 */ + + +/* + * list of available serial ports + */ +struct mn10300_serial_port *mn10300_serial_ports[NR_UARTS + 1] = { +#ifdef CONFIG_MN10300_TTYSM0 + [0] = &mn10300_serial_port_sif0, +#endif +#ifdef CONFIG_MN10300_TTYSM1 + [1] = &mn10300_serial_port_sif1, +#endif +#ifdef CONFIG_MN10300_TTYSM2 + [2] = &mn10300_serial_port_sif2, +#endif + [NR_UARTS] = NULL, +}; + + +/* + * we abuse the serial ports' baud timers' interrupt lines to get the ability + * to deliver interrupts to userspace as we use the ports' interrupt lines to + * do virtual DMA on account of the ports having no hardware FIFOs + * + * we can generate an interrupt manually in the assembly stubs by writing to + * the enable and detect bits in the interrupt control register, so all we need + * to do here is disable the interrupt line + * + * note that we can't just leave the line enabled as the baud rate timer *also* + * generates interrupts + */ +static void mn10300_serial_mask_ack(unsigned int irq) +{ + unsigned long flags; + u16 tmp; + + flags = arch_local_cli_save(); + GxICR(irq) = GxICR_LEVEL_6; + tmp = GxICR(irq); /* flush write buffer */ + arch_local_irq_restore(flags); +} + +static void mn10300_serial_chip_mask_ack(struct irq_data *d) +{ + mn10300_serial_mask_ack(d->irq); +} + +static void mn10300_serial_nop(struct irq_data *d) +{ +} + +static struct irq_chip mn10300_serial_pic = { + .name = "mnserial", + .irq_ack = mn10300_serial_chip_mask_ack, + .irq_mask = mn10300_serial_chip_mask_ack, + .irq_mask_ack = mn10300_serial_chip_mask_ack, + .irq_unmask = mn10300_serial_nop, +}; + + +/* + * serial virtual DMA interrupt jump table + */ +struct mn10300_serial_int mn10300_serial_int_tbl[NR_IRQS]; + +static void mn10300_serial_dis_tx_intr(struct mn10300_serial_port *port) +{ + unsigned long flags; + u16 x; + + flags = arch_local_cli_save(); + *port->tx_icr = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL); + x = *port->tx_icr; + arch_local_irq_restore(flags); +} + +static void mn10300_serial_en_tx_intr(struct mn10300_serial_port *port) +{ + unsigned long flags; + u16 x; + + flags = arch_local_cli_save(); + *port->tx_icr = + NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL) | GxICR_ENABLE; + x = *port->tx_icr; + arch_local_irq_restore(flags); +} + +static void mn10300_serial_dis_rx_intr(struct mn10300_serial_port *port) +{ + unsigned long flags; + u16 x; + + flags = arch_local_cli_save(); + *port->rx_icr = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL); + x = *port->rx_icr; + arch_local_irq_restore(flags); +} + +/* + * multi-bit equivalent of test_and_clear_bit() + */ +static int mask_test_and_clear(volatile u8 *ptr, u8 mask) +{ + u32 epsw; + asm volatile(" bclr %1,(%2) \n" + " mov epsw,%0 \n" + : "=d"(epsw) : "d"(mask), "a"(ptr) + : "cc", "memory"); + return !(epsw & EPSW_FLAG_Z); +} + +/* + * receive chars from the ring buffer for this serial port + * - must do break detection here (not done in the UART) + */ +static void mn10300_serial_receive_interrupt(struct mn10300_serial_port *port) +{ + struct uart_icount *icount = &port->uart.icount; + struct tty_struct *tty = port->uart.state->port.tty; + unsigned ix; + int count; + u8 st, ch, push, status, overrun; + + _enter("%s", port->name); + + push = 0; + + count = CIRC_CNT(port->rx_inp, port->rx_outp, MNSC_BUFFER_SIZE); + count = tty_buffer_request_room(tty, count); + if (count == 0) { + if (!tty->low_latency) + tty_flip_buffer_push(tty); + return; + } + +try_again: + /* pull chars out of the hat */ + ix = port->rx_outp; + if (ix == port->rx_inp) { + if (push && !tty->low_latency) + tty_flip_buffer_push(tty); + return; + } + + ch = port->rx_buffer[ix++]; + st = port->rx_buffer[ix++]; + smp_rmb(); + port->rx_outp = ix & (MNSC_BUFFER_SIZE - 1); + port->uart.icount.rx++; + + st &= SC01STR_FEF | SC01STR_PEF | SC01STR_OEF; + status = 0; + overrun = 0; + + /* the UART doesn't detect BREAK, so we have to do that ourselves + * - it starts as a framing error on a NUL character + * - then we count another two NUL characters before issuing TTY_BREAK + * - then we end on a normal char or one that has all the bottom bits + * zero and the top bits set + */ + switch (port->rx_brk) { + case 0: + /* not breaking at the moment */ + break; + + case 1: + if (st & SC01STR_FEF && ch == 0) { + port->rx_brk = 2; + goto try_again; + } + goto not_break; + + case 2: + if (st & SC01STR_FEF && ch == 0) { + port->rx_brk = 3; + _proto("Rx Break Detected"); + icount->brk++; + if (uart_handle_break(&port->uart)) + goto ignore_char; + status |= 1 << TTY_BREAK; + goto insert; + } + goto not_break; + + default: + if (st & (SC01STR_FEF | SC01STR_PEF | SC01STR_OEF)) + goto try_again; /* still breaking */ + + port->rx_brk = 0; /* end of the break */ + + switch (ch) { + case 0xFF: + case 0xFE: + case 0xFC: + case 0xF8: + case 0xF0: + case 0xE0: + case 0xC0: + case 0x80: + case 0x00: + /* discard char at probable break end */ + goto try_again; + } + break; + } + +process_errors: + /* handle framing error */ + if (st & SC01STR_FEF) { + if (ch == 0) { + /* framing error with NUL char is probably a BREAK */ + port->rx_brk = 1; + goto try_again; + } + + _proto("Rx Framing Error"); + icount->frame++; + status |= 1 << TTY_FRAME; + } + + /* handle parity error */ + if (st & SC01STR_PEF) { + _proto("Rx Parity Error"); + icount->parity++; + status = TTY_PARITY; + } + + /* handle normal char */ + if (status == 0) { + if (uart_handle_sysrq_char(&port->uart, ch)) + goto ignore_char; + status = (1 << TTY_NORMAL); + } + + /* handle overrun error */ + if (st & SC01STR_OEF) { + if (port->rx_brk) + goto try_again; + + _proto("Rx Overrun Error"); + icount->overrun++; + overrun = 1; + } + +insert: + status &= port->uart.read_status_mask; + + if (!overrun && !(status & port->uart.ignore_status_mask)) { + int flag; + + if (status & (1 << TTY_BREAK)) + flag = TTY_BREAK; + else if (status & (1 << TTY_PARITY)) + flag = TTY_PARITY; + else if (status & (1 << TTY_FRAME)) + flag = TTY_FRAME; + else + flag = TTY_NORMAL; + + tty_insert_flip_char(tty, ch, flag); + } + + /* overrun is special, since it's reported immediately, and doesn't + * affect the current character + */ + if (overrun) + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + + count--; + if (count <= 0) { + if (!tty->low_latency) + tty_flip_buffer_push(tty); + return; + } + +ignore_char: + push = 1; + goto try_again; + +not_break: + port->rx_brk = 0; + goto process_errors; +} + +/* + * handle an interrupt from the serial transmission "virtual DMA" driver + * - note: the interrupt routine will disable its own interrupts when the Tx + * buffer is empty + */ +static void mn10300_serial_transmit_interrupt(struct mn10300_serial_port *port) +{ + _enter("%s", port->name); + + if (!port->uart.state || !port->uart.state->port.tty) { + mn10300_serial_dis_tx_intr(port); + return; + } + + if (uart_tx_stopped(&port->uart) || + uart_circ_empty(&port->uart.state->xmit)) + mn10300_serial_dis_tx_intr(port); + + if (uart_circ_chars_pending(&port->uart.state->xmit) < WAKEUP_CHARS) + uart_write_wakeup(&port->uart); +} + +/* + * deal with a change in the status of the CTS line + */ +static void mn10300_serial_cts_changed(struct mn10300_serial_port *port, u8 st) +{ + u16 ctr; + + port->tx_cts = st; + port->uart.icount.cts++; + + /* flip the CTS state selector flag to interrupt when it changes + * back */ + ctr = *port->_control; + ctr ^= SC2CTR_TWS; + *port->_control = ctr; + + uart_handle_cts_change(&port->uart, st & SC2STR_CTS); + wake_up_interruptible(&port->uart.state->port.delta_msr_wait); +} + +/* + * handle a virtual interrupt generated by the lower level "virtual DMA" + * routines (irq is the baud timer interrupt) + */ +static irqreturn_t mn10300_serial_interrupt(int irq, void *dev_id) +{ + struct mn10300_serial_port *port = dev_id; + u8 st; + + spin_lock(&port->uart.lock); + + if (port->intr_flags) { + _debug("INT %s: %x", port->name, port->intr_flags); + + if (mask_test_and_clear(&port->intr_flags, MNSCx_RX_AVAIL)) + mn10300_serial_receive_interrupt(port); + + if (mask_test_and_clear(&port->intr_flags, + MNSCx_TX_SPACE | MNSCx_TX_EMPTY)) + mn10300_serial_transmit_interrupt(port); + } + + /* the only modem control line amongst the whole lot is CTS on + * serial port 2 */ + if (port->type == PORT_MN10300_CTS) { + st = *port->_status; + if ((port->tx_cts ^ st) & SC2STR_CTS) + mn10300_serial_cts_changed(port, st); + } + + spin_unlock(&port->uart.lock); + + return IRQ_HANDLED; +} + +/* + * return indication of whether the hardware transmit buffer is empty + */ +static unsigned int mn10300_serial_tx_empty(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s", port->name); + + return (*port->_status & (SC01STR_TXF | SC01STR_TBF)) ? + 0 : TIOCSER_TEMT; +} + +/* + * set the modem control lines (we don't have any) + */ +static void mn10300_serial_set_mctrl(struct uart_port *_port, + unsigned int mctrl) +{ + struct mn10300_serial_port *port __attribute__ ((unused)) = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s,%x", port->name, mctrl); +} + +/* + * get the modem control line statuses + */ +static unsigned int mn10300_serial_get_mctrl(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s", port->name); + + if (port->type == PORT_MN10300_CTS && !(*port->_status & SC2STR_CTS)) + return TIOCM_CAR | TIOCM_DSR; + + return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR; +} + +/* + * stop transmitting characters + */ +static void mn10300_serial_stop_tx(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s", port->name); + + /* disable the virtual DMA */ + mn10300_serial_dis_tx_intr(port); +} + +/* + * start transmitting characters + * - jump-start transmission if it has stalled + * - enable the serial Tx interrupt (used by the virtual DMA controller) + * - force an interrupt to happen if necessary + */ +static void mn10300_serial_start_tx(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + u16 x; + + _enter("%s{%lu}", + port->name, + CIRC_CNT(&port->uart.state->xmit.head, + &port->uart.state->xmit.tail, + UART_XMIT_SIZE)); + + /* kick the virtual DMA controller */ + arch_local_cli(); + x = *port->tx_icr; + x |= GxICR_ENABLE; + + if (*port->_status & SC01STR_TBF) + x &= ~(GxICR_REQUEST | GxICR_DETECT); + else + x |= GxICR_REQUEST | GxICR_DETECT; + + _debug("CTR=%04hx ICR=%02hx STR=%04x TMD=%02hx TBR=%04hx ICR=%04hx", + *port->_control, *port->_intr, *port->_status, + *port->_tmxmd, + (port->div_timer == MNSCx_DIV_TIMER_8BIT) ? + *(volatile u8 *)port->_tmxbr : *port->_tmxbr, + *port->tx_icr); + + *port->tx_icr = x; + x = *port->tx_icr; + arch_local_sti(); +} + +/* + * transmit a high-priority XON/XOFF character + */ +static void mn10300_serial_send_xchar(struct uart_port *_port, char ch) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s,%02x", port->name, ch); + + if (likely(port->gdbstub)) { + port->tx_xchar = ch; + if (ch) + mn10300_serial_en_tx_intr(port); + } +} + +/* + * stop receiving characters + * - called whilst the port is being closed + */ +static void mn10300_serial_stop_rx(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + u16 ctr; + + _enter("%s", port->name); + + ctr = *port->_control; + ctr &= ~SC01CTR_RXE; + *port->_control = ctr; + + mn10300_serial_dis_rx_intr(port); +} + +/* + * enable modem status interrupts + */ +static void mn10300_serial_enable_ms(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + u16 ctr, cts; + + _enter("%s", port->name); + + if (port->type == PORT_MN10300_CTS) { + /* want to interrupt when CTS goes low if CTS is now high and + * vice versa + */ + port->tx_cts = *port->_status; + + cts = (port->tx_cts & SC2STR_CTS) ? + SC2CTR_TWE : SC2CTR_TWE | SC2CTR_TWS; + + ctr = *port->_control; + ctr &= ~SC2CTR_TWS; + ctr |= cts; + *port->_control = ctr; + + mn10300_serial_en_tx_intr(port); + } +} + +/* + * transmit or cease transmitting a break signal + */ +static void mn10300_serial_break_ctl(struct uart_port *_port, int ctl) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s,%d", port->name, ctl); + + if (ctl) { + /* tell the virtual DMA handler to assert BREAK */ + port->tx_break = 1; + mn10300_serial_en_tx_intr(port); + } else { + port->tx_break = 0; + *port->_control &= ~SC01CTR_BKE; + mn10300_serial_en_tx_intr(port); + } +} + +/* + * grab the interrupts and enable the port for reception + */ +static int mn10300_serial_startup(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + struct mn10300_serial_int *pint; + + _enter("%s{%d}", port->name, port->gdbstub); + + if (unlikely(port->gdbstub)) + return -EBUSY; + + /* allocate an Rx buffer for the virtual DMA handler */ + port->rx_buffer = kmalloc(MNSC_BUFFER_SIZE, GFP_KERNEL); + if (!port->rx_buffer) + return -ENOMEM; + + port->rx_inp = port->rx_outp = 0; + + /* finally, enable the device */ + *port->_intr = SC01ICR_TI; + *port->_control |= SC01CTR_TXE | SC01CTR_RXE; + + pint = &mn10300_serial_int_tbl[port->rx_irq]; + pint->port = port; + pint->vdma = mn10300_serial_vdma_rx_handler; + pint = &mn10300_serial_int_tbl[port->tx_irq]; + pint->port = port; + pint->vdma = mn10300_serial_vdma_tx_handler; + + set_intr_level(port->rx_irq, + NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL)); + set_intr_level(port->tx_irq, + NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL)); + irq_set_chip(port->tm_irq, &mn10300_serial_pic); + + if (request_irq(port->rx_irq, mn10300_serial_interrupt, + IRQF_DISABLED, port->rx_name, port) < 0) + goto error; + + if (request_irq(port->tx_irq, mn10300_serial_interrupt, + IRQF_DISABLED, port->tx_name, port) < 0) + goto error2; + + if (request_irq(port->tm_irq, mn10300_serial_interrupt, + IRQF_DISABLED, port->tm_name, port) < 0) + goto error3; + mn10300_serial_mask_ack(port->tm_irq); + + return 0; + +error3: + free_irq(port->tx_irq, port); +error2: + free_irq(port->rx_irq, port); +error: + kfree(port->rx_buffer); + port->rx_buffer = NULL; + return -EBUSY; +} + +/* + * shutdown the port and release interrupts + */ +static void mn10300_serial_shutdown(struct uart_port *_port) +{ + u16 x; + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s", port->name); + + /* disable the serial port and its baud rate timer */ + port->tx_break = 0; + *port->_control &= ~(SC01CTR_TXE | SC01CTR_RXE | SC01CTR_BKE); + *port->_tmxmd = 0; + + if (port->rx_buffer) { + void *buf = port->rx_buffer; + port->rx_buffer = NULL; + kfree(buf); + } + + /* disable all intrs */ + free_irq(port->tm_irq, port); + free_irq(port->rx_irq, port); + free_irq(port->tx_irq, port); + + arch_local_cli(); + *port->rx_icr = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL); + x = *port->rx_icr; + *port->tx_icr = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL); + x = *port->tx_icr; + arch_local_sti(); +} + +/* + * this routine is called to set the UART divisor registers to match the + * specified baud rate for a serial port. + */ +static void mn10300_serial_change_speed(struct mn10300_serial_port *port, + struct ktermios *new, + struct ktermios *old) +{ + unsigned long flags; + unsigned long ioclk = port->ioclk; + unsigned cflag; + int baud, bits, xdiv, tmp; + u16 tmxbr, scxctr; + u8 tmxmd, battempt; + u8 div_timer = port->div_timer; + + _enter("%s{%lu}", port->name, ioclk); + + /* byte size and parity */ + cflag = new->c_cflag; + switch (cflag & CSIZE) { + case CS7: scxctr = SC01CTR_CLN_7BIT; bits = 9; break; + case CS8: scxctr = SC01CTR_CLN_8BIT; bits = 10; break; + default: scxctr = SC01CTR_CLN_8BIT; bits = 10; break; + } + + if (cflag & CSTOPB) { + scxctr |= SC01CTR_STB_2BIT; + bits++; + } + + if (cflag & PARENB) { + bits++; + if (cflag & PARODD) + scxctr |= SC01CTR_PB_ODD; +#ifdef CMSPAR + else if (cflag & CMSPAR) + scxctr |= SC01CTR_PB_FIXED0; +#endif + else + scxctr |= SC01CTR_PB_EVEN; + } + + /* Determine divisor based on baud rate */ + battempt = 0; + + switch (port->uart.line) { +#ifdef CONFIG_MN10300_TTYSM0 + case 0: /* ttySM0 */ +#if defined(CONFIG_MN10300_TTYSM0_TIMER8) + scxctr |= SC0CTR_CK_TM8UFLOW_8; +#elif defined(CONFIG_MN10300_TTYSM0_TIMER0) + scxctr |= SC0CTR_CK_TM0UFLOW_8; +#elif defined(CONFIG_MN10300_TTYSM0_TIMER2) + scxctr |= SC0CTR_CK_TM2UFLOW_8; +#else +#error "Unknown config for ttySM0" +#endif + break; +#endif /* CONFIG_MN10300_TTYSM0 */ + +#ifdef CONFIG_MN10300_TTYSM1 + case 1: /* ttySM1 */ +#if defined(CONFIG_AM33_2) || defined(CONFIG_AM33_3) +#if defined(CONFIG_MN10300_TTYSM1_TIMER9) + scxctr |= SC1CTR_CK_TM9UFLOW_8; +#elif defined(CONFIG_MN10300_TTYSM1_TIMER3) + scxctr |= SC1CTR_CK_TM3UFLOW_8; +#else +#error "Unknown config for ttySM1" +#endif +#else /* CONFIG_AM33_2 || CONFIG_AM33_3 */ +#if defined(CONFIG_MN10300_TTYSM1_TIMER12) + scxctr |= SC1CTR_CK_TM12UFLOW_8; +#else +#error "Unknown config for ttySM1" +#endif +#endif /* CONFIG_AM33_2 || CONFIG_AM33_3 */ + break; +#endif /* CONFIG_MN10300_TTYSM1 */ + +#ifdef CONFIG_MN10300_TTYSM2 + case 2: /* ttySM2 */ +#if defined(CONFIG_AM33_2) +#if defined(CONFIG_MN10300_TTYSM2_TIMER10) + scxctr |= SC2CTR_CK_TM10UFLOW; +#else +#error "Unknown config for ttySM2" +#endif +#else /* CONFIG_AM33_2 */ +#if defined(CONFIG_MN10300_TTYSM2_TIMER9) + scxctr |= SC2CTR_CK_TM9UFLOW_8; +#elif defined(CONFIG_MN10300_TTYSM2_TIMER1) + scxctr |= SC2CTR_CK_TM1UFLOW_8; +#elif defined(CONFIG_MN10300_TTYSM2_TIMER3) + scxctr |= SC2CTR_CK_TM3UFLOW_8; +#else +#error "Unknown config for ttySM2" +#endif +#endif /* CONFIG_AM33_2 */ + break; +#endif /* CONFIG_MN10300_TTYSM2 */ + + default: + break; + } + +try_alternative: + baud = uart_get_baud_rate(&port->uart, new, old, 0, + port->ioclk / 8); + + _debug("ALT %d [baud %d]", battempt, baud); + + if (!baud) + baud = 9600; /* B0 transition handled in rs_set_termios */ + xdiv = 1; + if (baud == 134) { + baud = 269; /* 134 is really 134.5 */ + xdiv = 2; + } + + if (baud == 38400 && + (port->uart.flags & UPF_SPD_MASK) == UPF_SPD_CUST + ) { + _debug("CUSTOM %u", port->uart.custom_divisor); + + if (div_timer == MNSCx_DIV_TIMER_16BIT) { + if (port->uart.custom_divisor <= 65535) { + tmxmd = TM8MD_SRC_IOCLK; + tmxbr = port->uart.custom_divisor; + port->uart.uartclk = ioclk; + goto timer_okay; + } + if (port->uart.custom_divisor / 8 <= 65535) { + tmxmd = TM8MD_SRC_IOCLK_8; + tmxbr = port->uart.custom_divisor / 8; + port->uart.custom_divisor = tmxbr * 8; + port->uart.uartclk = ioclk / 8; + goto timer_okay; + } + if (port->uart.custom_divisor / 32 <= 65535) { + tmxmd = TM8MD_SRC_IOCLK_32; + tmxbr = port->uart.custom_divisor / 32; + port->uart.custom_divisor = tmxbr * 32; + port->uart.uartclk = ioclk / 32; + goto timer_okay; + } + + } else if (div_timer == MNSCx_DIV_TIMER_8BIT) { + if (port->uart.custom_divisor <= 255) { + tmxmd = TM2MD_SRC_IOCLK; + tmxbr = port->uart.custom_divisor; + port->uart.uartclk = ioclk; + goto timer_okay; + } + if (port->uart.custom_divisor / 8 <= 255) { + tmxmd = TM2MD_SRC_IOCLK_8; + tmxbr = port->uart.custom_divisor / 8; + port->uart.custom_divisor = tmxbr * 8; + port->uart.uartclk = ioclk / 8; + goto timer_okay; + } + if (port->uart.custom_divisor / 32 <= 255) { + tmxmd = TM2MD_SRC_IOCLK_32; + tmxbr = port->uart.custom_divisor / 32; + port->uart.custom_divisor = tmxbr * 32; + port->uart.uartclk = ioclk / 32; + goto timer_okay; + } + } + } + + switch (div_timer) { + case MNSCx_DIV_TIMER_16BIT: + port->uart.uartclk = ioclk; + tmxmd = TM8MD_SRC_IOCLK; + tmxbr = tmp = (ioclk / (baud * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 65535) + goto timer_okay; + + port->uart.uartclk = ioclk / 8; + tmxmd = TM8MD_SRC_IOCLK_8; + tmxbr = tmp = (ioclk / (baud * 8 * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 65535) + goto timer_okay; + + port->uart.uartclk = ioclk / 32; + tmxmd = TM8MD_SRC_IOCLK_32; + tmxbr = tmp = (ioclk / (baud * 32 * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 65535) + goto timer_okay; + break; + + case MNSCx_DIV_TIMER_8BIT: + port->uart.uartclk = ioclk; + tmxmd = TM2MD_SRC_IOCLK; + tmxbr = tmp = (ioclk / (baud * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 255) + goto timer_okay; + + port->uart.uartclk = ioclk / 8; + tmxmd = TM2MD_SRC_IOCLK_8; + tmxbr = tmp = (ioclk / (baud * 8 * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 255) + goto timer_okay; + + port->uart.uartclk = ioclk / 32; + tmxmd = TM2MD_SRC_IOCLK_32; + tmxbr = tmp = (ioclk / (baud * 32 * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 255) + goto timer_okay; + break; + + default: + BUG(); + return; + } + + /* refuse to change to a baud rate we can't support */ + _debug("CAN'T SUPPORT"); + + switch (battempt) { + case 0: + if (old) { + new->c_cflag &= ~CBAUD; + new->c_cflag |= (old->c_cflag & CBAUD); + battempt = 1; + goto try_alternative; + } + + case 1: + /* as a last resort, if the quotient is zero, default to 9600 + * bps */ + new->c_cflag &= ~CBAUD; + new->c_cflag |= B9600; + battempt = 2; + goto try_alternative; + + default: + /* hmmm... can't seem to support 9600 either + * - we could try iterating through the speeds we know about to + * find the lowest + */ + new->c_cflag &= ~CBAUD; + new->c_cflag |= B0; + + if (div_timer == MNSCx_DIV_TIMER_16BIT) + tmxmd = TM8MD_SRC_IOCLK_32; + else if (div_timer == MNSCx_DIV_TIMER_8BIT) + tmxmd = TM2MD_SRC_IOCLK_32; + tmxbr = 1; + + port->uart.uartclk = ioclk / 32; + break; + } +timer_okay: + + _debug("UARTCLK: %u / %hu", port->uart.uartclk, tmxbr); + + /* make the changes */ + spin_lock_irqsave(&port->uart.lock, flags); + + uart_update_timeout(&port->uart, new->c_cflag, baud); + + /* set the timer to produce the required baud rate */ + switch (div_timer) { + case MNSCx_DIV_TIMER_16BIT: + *port->_tmxmd = 0; + *port->_tmxbr = tmxbr; + *port->_tmxmd = TM8MD_INIT_COUNTER; + *port->_tmxmd = tmxmd | TM8MD_COUNT_ENABLE; + break; + + case MNSCx_DIV_TIMER_8BIT: + *port->_tmxmd = 0; + *(volatile u8 *) port->_tmxbr = (u8) tmxbr; + *port->_tmxmd = TM2MD_INIT_COUNTER; + *port->_tmxmd = tmxmd | TM2MD_COUNT_ENABLE; + break; + } + + /* CTS flow control flag and modem status interrupts */ + scxctr &= ~(SC2CTR_TWE | SC2CTR_TWS); + + if (port->type == PORT_MN10300_CTS && cflag & CRTSCTS) { + /* want to interrupt when CTS goes low if CTS is now + * high and vice versa + */ + port->tx_cts = *port->_status; + + if (port->tx_cts & SC2STR_CTS) + scxctr |= SC2CTR_TWE; + else + scxctr |= SC2CTR_TWE | SC2CTR_TWS; + } + + /* set up parity check flag */ + port->uart.read_status_mask = (1 << TTY_NORMAL) | (1 << TTY_OVERRUN); + if (new->c_iflag & INPCK) + port->uart.read_status_mask |= + (1 << TTY_PARITY) | (1 << TTY_FRAME); + if (new->c_iflag & (BRKINT | PARMRK)) + port->uart.read_status_mask |= (1 << TTY_BREAK); + + /* characters to ignore */ + port->uart.ignore_status_mask = 0; + if (new->c_iflag & IGNPAR) + port->uart.ignore_status_mask |= + (1 << TTY_PARITY) | (1 << TTY_FRAME); + if (new->c_iflag & IGNBRK) { + port->uart.ignore_status_mask |= (1 << TTY_BREAK); + /* + * If we're ignoring parity and break indicators, + * ignore overruns to (for real raw support). + */ + if (new->c_iflag & IGNPAR) + port->uart.ignore_status_mask |= (1 << TTY_OVERRUN); + } + + /* Ignore all characters if CREAD is not set */ + if ((new->c_cflag & CREAD) == 0) + port->uart.ignore_status_mask |= (1 << TTY_NORMAL); + + scxctr |= *port->_control & (SC01CTR_TXE | SC01CTR_RXE | SC01CTR_BKE); + *port->_control = scxctr; + + spin_unlock_irqrestore(&port->uart.lock, flags); +} + +/* + * set the terminal I/O parameters + */ +static void mn10300_serial_set_termios(struct uart_port *_port, + struct ktermios *new, + struct ktermios *old) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s,%p,%p", port->name, new, old); + + mn10300_serial_change_speed(port, new, old); + + /* handle turning off CRTSCTS */ + if (!(new->c_cflag & CRTSCTS)) { + u16 ctr = *port->_control; + ctr &= ~SC2CTR_TWE; + *port->_control = ctr; + } + + /* change Transfer bit-order (LSB/MSB) */ + if (new->c_cflag & CODMSB) + *port->_control |= SC01CTR_OD_MSBFIRST; /* MSB MODE */ + else + *port->_control &= ~SC01CTR_OD_MSBFIRST; /* LSB MODE */ +} + +/* + * return description of port type + */ +static const char *mn10300_serial_type(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + if (port->uart.type == PORT_MN10300_CTS) + return "MN10300 SIF_CTS"; + + return "MN10300 SIF"; +} + +/* + * release I/O and memory regions in use by port + */ +static void mn10300_serial_release_port(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s", port->name); + + release_mem_region((unsigned long) port->_iobase, 16); +} + +/* + * request I/O and memory regions for port + */ +static int mn10300_serial_request_port(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s", port->name); + + request_mem_region((unsigned long) port->_iobase, 16, port->name); + return 0; +} + +/* + * configure the type and reserve the ports + */ +static void mn10300_serial_config_port(struct uart_port *_port, int type) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s", port->name); + + port->uart.type = PORT_MN10300; + + if (port->options & MNSCx_OPT_CTS) + port->uart.type = PORT_MN10300_CTS; + + mn10300_serial_request_port(_port); +} + +/* + * verify serial parameters are suitable for this port type + */ +static int mn10300_serial_verify_port(struct uart_port *_port, + struct serial_struct *ss) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + void *mapbase = (void *) (unsigned long) port->uart.mapbase; + + _enter("%s", port->name); + + /* these things may not be changed */ + if (ss->irq != port->uart.irq || + ss->port != port->uart.iobase || + ss->io_type != port->uart.iotype || + ss->iomem_base != mapbase || + ss->iomem_reg_shift != port->uart.regshift || + ss->hub6 != port->uart.hub6 || + ss->xmit_fifo_size != port->uart.fifosize) + return -EINVAL; + + /* type may be changed on a port that supports CTS */ + if (ss->type != port->uart.type) { + if (!(port->options & MNSCx_OPT_CTS)) + return -EINVAL; + + if (ss->type != PORT_MN10300 && + ss->type != PORT_MN10300_CTS) + return -EINVAL; + } + + return 0; +} + +/* + * initialise the MN10300 on-chip UARTs + */ +static int __init mn10300_serial_init(void) +{ + struct mn10300_serial_port *port; + int ret, i; + + printk(KERN_INFO "%s version %s (%s)\n", + serial_name, serial_version, serial_revdate); + +#if defined(CONFIG_MN10300_TTYSM2) && defined(CONFIG_AM33_2) + { + int tmp; + SC2TIM = 8; /* make the baud base of timer 2 IOCLK/8 */ + tmp = SC2TIM; + } +#endif + + set_intr_stub(NUM2EXCEP_IRQ_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL), + mn10300_serial_vdma_interrupt); + + ret = uart_register_driver(&mn10300_serial_driver); + if (!ret) { + for (i = 0 ; i < NR_PORTS ; i++) { + port = mn10300_serial_ports[i]; + if (!port || port->gdbstub) + continue; + + switch (port->clock_src) { + case MNSCx_CLOCK_SRC_IOCLK: + port->ioclk = MN10300_IOCLK; + break; + +#ifdef MN10300_IOBCLK + case MNSCx_CLOCK_SRC_IOBCLK: + port->ioclk = MN10300_IOBCLK; + break; +#endif + default: + BUG(); + } + + ret = uart_add_one_port(&mn10300_serial_driver, + &port->uart); + + if (ret < 0) { + _debug("ERROR %d", -ret); + break; + } + } + + if (ret) + uart_unregister_driver(&mn10300_serial_driver); + } + + return ret; +} + +__initcall(mn10300_serial_init); + + +#ifdef CONFIG_MN10300_TTYSM_CONSOLE + +/* + * print a string to the serial port without disturbing the real user of the + * port too much + * - the console must be locked by the caller + */ +static void mn10300_serial_console_write(struct console *co, + const char *s, unsigned count) +{ + struct mn10300_serial_port *port; + unsigned i; + u16 scxctr, txicr, tmp; + u8 tmxmd; + + port = mn10300_serial_ports[co->index]; + + /* firstly hijack the serial port from the "virtual DMA" controller */ + arch_local_cli(); + txicr = *port->tx_icr; + *port->tx_icr = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL); + tmp = *port->tx_icr; + arch_local_sti(); + + /* the transmitter may be disabled */ + scxctr = *port->_control; + if (!(scxctr & SC01CTR_TXE)) { + /* restart the UART clock */ + tmxmd = *port->_tmxmd; + + switch (port->div_timer) { + case MNSCx_DIV_TIMER_16BIT: + *port->_tmxmd = 0; + *port->_tmxmd = TM8MD_INIT_COUNTER; + *port->_tmxmd = tmxmd | TM8MD_COUNT_ENABLE; + break; + + case MNSCx_DIV_TIMER_8BIT: + *port->_tmxmd = 0; + *port->_tmxmd = TM2MD_INIT_COUNTER; + *port->_tmxmd = tmxmd | TM2MD_COUNT_ENABLE; + break; + } + + /* enable the transmitter */ + *port->_control = (scxctr & ~SC01CTR_BKE) | SC01CTR_TXE; + + } else if (scxctr & SC01CTR_BKE) { + /* stop transmitting BREAK */ + *port->_control = (scxctr & ~SC01CTR_BKE); + } + + /* send the chars into the serial port (with LF -> LFCR conversion) */ + for (i = 0; i < count; i++) { + char ch = *s++; + + while (*port->_status & SC01STR_TBF) + continue; + *(u8 *) port->_txb = ch; + + if (ch == 0x0a) { + while (*port->_status & SC01STR_TBF) + continue; + *(u8 *) port->_txb = 0xd; + } + } + + /* can't let the transmitter be turned off if it's actually + * transmitting */ + while (*port->_status & (SC01STR_TXF | SC01STR_TBF)) + continue; + + /* disable the transmitter if we re-enabled it */ + if (!(scxctr & SC01CTR_TXE)) + *port->_control = scxctr; + + arch_local_cli(); + *port->tx_icr = txicr; + tmp = *port->tx_icr; + arch_local_sti(); +} + +/* + * set up a serial port as a console + * - construct a cflag setting for the first rs_open() + * - initialize the serial port + * - return non-zero if we didn't find a serial port. + */ +static int __init mn10300_serial_console_setup(struct console *co, + char *options) +{ + struct mn10300_serial_port *port; + int i, parity = 'n', baud = 9600, bits = 8, flow = 0; + + for (i = 0 ; i < NR_PORTS ; i++) { + port = mn10300_serial_ports[i]; + if (port && !port->gdbstub && port->uart.line == co->index) + goto found_device; + } + + return -ENODEV; + +found_device: + switch (port->clock_src) { + case MNSCx_CLOCK_SRC_IOCLK: + port->ioclk = MN10300_IOCLK; + break; + +#ifdef MN10300_IOBCLK + case MNSCx_CLOCK_SRC_IOBCLK: + port->ioclk = MN10300_IOBCLK; + break; +#endif + default: + BUG(); + } + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&port->uart, co, baud, parity, bits, flow); +} + +/* + * register console + */ +static int __init mn10300_serial_console_init(void) +{ + register_console(&mn10300_serial_console); + return 0; +} + +console_initcall(mn10300_serial_console_init); +#endif + +#ifdef CONFIG_CONSOLE_POLL +/* + * Polled character reception for the kernel debugger + */ +static int mn10300_serial_poll_get_char(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + unsigned ix; + u8 st, ch; + + _enter("%s", port->name); + + do { + /* pull chars out of the hat */ + ix = port->rx_outp; + if (ix == port->rx_inp) + return NO_POLL_CHAR; + + ch = port->rx_buffer[ix++]; + st = port->rx_buffer[ix++]; + smp_rmb(); + port->rx_outp = ix & (MNSC_BUFFER_SIZE - 1); + + } while (st & (SC01STR_FEF | SC01STR_PEF | SC01STR_OEF)); + + return ch; +} + + +/* + * Polled character transmission for the kernel debugger + */ +static void mn10300_serial_poll_put_char(struct uart_port *_port, + unsigned char ch) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + u8 intr, tmp; + + /* wait for the transmitter to finish anything it might be doing (and + * this includes the virtual DMA handler, so it might take a while) */ + while (*port->_status & (SC01STR_TBF | SC01STR_TXF)) + continue; + + /* disable the Tx ready interrupt */ + intr = *port->_intr; + *port->_intr = intr & ~SC01ICR_TI; + tmp = *port->_intr; + + if (ch == 0x0a) { + *(u8 *) port->_txb = 0x0d; + while (*port->_status & SC01STR_TBF) + continue; + } + + *(u8 *) port->_txb = ch; + while (*port->_status & SC01STR_TBF) + continue; + + /* restore the Tx interrupt flag */ + *port->_intr = intr; + tmp = *port->_intr; +} + +#endif /* CONFIG_CONSOLE_POLL */ diff --git a/arch/mn10300/kernel/mn10300-serial.h b/arch/mn10300/kernel/mn10300-serial.h new file mode 100644 index 00000000..6796499b --- /dev/null +++ b/arch/mn10300/kernel/mn10300-serial.h @@ -0,0 +1,126 @@ +/* MN10300 On-chip serial port driver definitions + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _MN10300_SERIAL_H +#define _MN10300_SERIAL_H + +#ifndef __ASSEMBLY__ +#include <linux/serial_core.h> +#include <linux/termios.h> +#endif + +#include <asm/page.h> +#include <asm/serial-regs.h> + +#define NR_PORTS 3 /* should be set 3 or 9 or 16 */ + +#define MNSC_BUFFER_SIZE +(PAGE_SIZE / 2) + +/* intr_flags bits */ +#define MNSCx_RX_AVAIL 0x01 +#define MNSCx_RX_OVERF 0x02 +#define MNSCx_TX_SPACE 0x04 +#define MNSCx_TX_EMPTY 0x08 + +#ifndef __ASSEMBLY__ + +struct mn10300_serial_port { + char *rx_buffer; /* reception buffer base */ + unsigned rx_inp; /* pointer to rx input offset */ + unsigned rx_outp; /* pointer to rx output offset */ + u8 tx_xchar; /* high-priority XON/XOFF buffer */ + u8 tx_break; /* transmit break request */ + u8 intr_flags; /* interrupt flags */ + volatile u16 *rx_icr; /* Rx interrupt control register */ + volatile u16 *tx_icr; /* Tx interrupt control register */ + int rx_irq; /* reception IRQ */ + int tx_irq; /* transmission IRQ */ + int tm_irq; /* timer IRQ */ + + const char *name; /* name of serial port */ + const char *rx_name; /* Rx interrupt handler name of serial port */ + const char *tx_name; /* Tx interrupt handler name of serial port */ + const char *tm_name; /* Timer interrupt handler name */ + unsigned short type; /* type of serial port */ + unsigned char isconsole; /* T if it's a console */ + volatile void *_iobase; /* pointer to base of I/O control regs */ + volatile u16 *_control; /* control register pointer */ + volatile u8 *_status; /* status register pointer */ + volatile u8 *_intr; /* interrupt register pointer */ + volatile void *_rxb; /* receive buffer register pointer */ + volatile void *_txb; /* transmit buffer register pointer */ + volatile u16 *_tmicr; /* timer interrupt control register */ + volatile u8 *_tmxmd; /* baud rate timer mode register */ + volatile u16 *_tmxbr; /* baud rate timer base register */ + + /* this must come down here so that assembly can use BSET to access the + * above fields */ + struct uart_port uart; + + unsigned short rx_brk; /* current break reception status */ + u16 tx_cts; /* current CTS status */ + int gdbstub; /* preemptively stolen by GDB stub */ + + u8 clock_src; /* clock source */ +#define MNSCx_CLOCK_SRC_IOCLK 0 +#define MNSCx_CLOCK_SRC_IOBCLK 1 + + u8 div_timer; /* timer used as divisor */ +#define MNSCx_DIV_TIMER_16BIT 0 +#define MNSCx_DIV_TIMER_8BIT 1 + + u16 options; /* options */ +#define MNSCx_OPT_CTS 0x0001 + + unsigned long ioclk; /* base clock rate */ +}; + +#ifdef CONFIG_MN10300_TTYSM0 +extern struct mn10300_serial_port mn10300_serial_port_sif0; +#endif + +#ifdef CONFIG_MN10300_TTYSM1 +extern struct mn10300_serial_port mn10300_serial_port_sif1; +#endif + +#ifdef CONFIG_MN10300_TTYSM2 +extern struct mn10300_serial_port mn10300_serial_port_sif2; +#endif + +extern struct mn10300_serial_port *mn10300_serial_ports[]; + +struct mn10300_serial_int { + struct mn10300_serial_port *port; + asmlinkage void (*vdma)(void); +}; + +extern struct mn10300_serial_int mn10300_serial_int_tbl[]; + +extern asmlinkage void mn10300_serial_vdma_interrupt(void); +extern asmlinkage void mn10300_serial_vdma_rx_handler(void); +extern asmlinkage void mn10300_serial_vdma_tx_handler(void); + +#endif /* __ASSEMBLY__ */ + +#if defined(CONFIG_GDBSTUB_ON_TTYSM0) +#define SCgSTR SC0STR +#define SCgRXB SC0RXB +#define SCgRXIRQ SC0RXIRQ +#elif defined(CONFIG_GDBSTUB_ON_TTYSM1) +#define SCgSTR SC1STR +#define SCgRXB SC1RXB +#define SCgRXIRQ SC1RXIRQ +#elif defined(CONFIG_GDBSTUB_ON_TTYSM2) +#define SCgSTR SC2STR +#define SCgRXB SC2RXB +#define SCgRXIRQ SC2RXIRQ +#endif + +#endif /* _MN10300_SERIAL_H */ diff --git a/arch/mn10300/kernel/mn10300-watchdog-low.S b/arch/mn10300/kernel/mn10300-watchdog-low.S new file mode 100644 index 00000000..f2f5c9cf --- /dev/null +++ b/arch/mn10300/kernel/mn10300-watchdog-low.S @@ -0,0 +1,66 @@ +############################################################################### +# +# MN10300 Watchdog interrupt handler +# +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/intctl-regs.h> +#include <asm/timer-regs.h> +#include <asm/frame.inc> +#include <linux/threads.h> + + .text + +############################################################################### +# +# Watchdog handler entry point +# - special non-maskable interrupt +# +############################################################################### + .globl watchdog_handler + .type watchdog_handler,@function +watchdog_handler: + add -4,sp + SAVE_ALL + + mov 0xffffffff,d0 + mov d0,(REG_ORIG_D0,fp) + + mov fp,d0 + lsr 2,d1 + call watchdog_interrupt[],0 # watchdog_interrupt(regs,irq) + + jmp ret_from_intr + + .size watchdog_handler,.-watchdog_handler + +############################################################################### +# +# Watchdog touch entry point +# - kept to absolute minimum (unfortunately, it's prototyped in linux/nmi.h so +# we can't inline it) +# +############################################################################### + .globl touch_nmi_watchdog + .type touch_nmi_watchdog,@function +touch_nmi_watchdog: + clr d0 + clr d1 + mov watchdog_alert_counter, a0 + setlb + mov d0, (a0+) + inc d1 + cmp NR_CPUS, d1 + lne + ret [],0 + + .size touch_nmi_watchdog,.-touch_nmi_watchdog diff --git a/arch/mn10300/kernel/mn10300-watchdog.c b/arch/mn10300/kernel/mn10300-watchdog.c new file mode 100644 index 00000000..c5e12bfd --- /dev/null +++ b/arch/mn10300/kernel/mn10300-watchdog.c @@ -0,0 +1,206 @@ +/* MN10300 Watchdog timer + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * - Derived from arch/i386/kernel/nmi.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/kernel_stat.h> +#include <linux/nmi.h> +#include <asm/processor.h> +#include <asm/system.h> +#include <asm/atomic.h> +#include <asm/intctl-regs.h> +#include <asm/rtc-regs.h> +#include <asm/div64.h> +#include <asm/smp.h> +#include <asm/gdb-stub.h> +#include <proc/clock.h> + +static DEFINE_SPINLOCK(watchdog_print_lock); +static unsigned int watchdog; +static unsigned int watchdog_hz = 1; +unsigned int watchdog_alert_counter[NR_CPUS]; + +EXPORT_SYMBOL(touch_nmi_watchdog); + +/* + * the best way to detect whether a CPU has a 'hard lockup' problem + * is to check its timer makes IRQ counts. If they are not + * changing then that CPU has some problem. + * + * since NMIs dont listen to _any_ locks, we have to be extremely + * careful not to rely on unsafe variables. The printk might lock + * up though, so we have to break up any console locks first ... + * [when there will be more tty-related locks, break them up + * here too!] + */ +static unsigned int last_irq_sums[NR_CPUS]; + +int __init check_watchdog(void) +{ + irq_cpustat_t tmp[1]; + + printk(KERN_INFO "Testing Watchdog... "); + + memcpy(tmp, irq_stat, sizeof(tmp)); + local_irq_enable(); + mdelay((10 * 1000) / watchdog_hz); /* wait 10 ticks */ + local_irq_disable(); + + if (nmi_count(0) - tmp[0].__nmi_count <= 5) { + printk(KERN_WARNING "CPU#%d: Watchdog appears to be stuck!\n", + 0); + return -1; + } + + printk(KERN_INFO "OK.\n"); + + /* now that we know it works we can reduce NMI frequency to something + * more reasonable; makes a difference in some configs + */ + watchdog_hz = 1; + + return 0; +} + +static int __init setup_watchdog(char *str) +{ + unsigned tmp; + int opt; + u8 ctr; + + get_option(&str, &opt); + if (opt != 1) + return 0; + + watchdog = opt; + if (watchdog) { + set_intr_stub(EXCEP_WDT, watchdog_handler); + ctr = WDCTR_WDCK_65536th; + WDCTR = WDCTR_WDRST | ctr; + WDCTR = ctr; + tmp = WDCTR; + + tmp = __muldiv64u(1 << (16 + ctr * 2), 1000000, MN10300_WDCLK); + tmp = 1000000000 / tmp; + watchdog_hz = (tmp + 500) / 1000; + } + + return 1; +} + +__setup("watchdog=", setup_watchdog); + +void __init watchdog_go(void) +{ + u8 wdt; + + if (watchdog) { + printk(KERN_INFO "Watchdog: running at %uHz\n", watchdog_hz); + wdt = WDCTR & ~WDCTR_WDCNE; + WDCTR = wdt | WDCTR_WDRST; + wdt = WDCTR; + WDCTR = wdt | WDCTR_WDCNE; + wdt = WDCTR; + + check_watchdog(); + } +} + +#ifdef CONFIG_SMP +static void watchdog_dump_register(void *dummy) +{ + printk(KERN_ERR "--- Register Dump (CPU%d) ---\n", CPUID); + show_registers(current_frame()); +} +#endif + +asmlinkage +void watchdog_interrupt(struct pt_regs *regs, enum exception_code excep) +{ + /* + * Since current-> is always on the stack, and we always switch + * the stack NMI-atomically, it's safe to use smp_processor_id(). + */ + int sum, cpu; + int irq = NMIIRQ; + u8 wdt, tmp; + + wdt = WDCTR & ~WDCTR_WDCNE; + WDCTR = wdt; + tmp = WDCTR; + NMICR = NMICR_WDIF; + + nmi_count(smp_processor_id())++; + kstat_incr_irqs_this_cpu(irq, irq_to_desc(irq)); + + for_each_online_cpu(cpu) { + + sum = irq_stat[cpu].__irq_count; + + if ((last_irq_sums[cpu] == sum) +#if defined(CONFIG_GDBSTUB) && defined(CONFIG_SMP) + && !(CHK_GDBSTUB_BUSY() + || atomic_read(&cpu_doing_single_step)) +#endif + ) { + /* + * Ayiee, looks like this CPU is stuck ... + * wait a few IRQs (5 seconds) before doing the oops ... + */ + watchdog_alert_counter[cpu]++; + if (watchdog_alert_counter[cpu] == 5 * watchdog_hz) { + spin_lock(&watchdog_print_lock); + /* + * We are in trouble anyway, lets at least try + * to get a message out. + */ + bust_spinlocks(1); + printk(KERN_ERR + "NMI Watchdog detected LOCKUP on CPU%d," + " pc %08lx, registers:\n", + cpu, regs->pc); +#ifdef CONFIG_SMP + printk(KERN_ERR + "--- Register Dump (CPU%d) ---\n", + CPUID); +#endif + show_registers(regs); +#ifdef CONFIG_SMP + smp_nmi_call_function(watchdog_dump_register, + NULL, 1); +#endif + printk(KERN_NOTICE "console shuts up ...\n"); + console_silent(); + spin_unlock(&watchdog_print_lock); + bust_spinlocks(0); +#ifdef CONFIG_GDBSTUB + if (CHK_GDBSTUB_BUSY_AND_ACTIVE()) + gdbstub_exception(regs, excep); + else + gdbstub_intercept(regs, excep); +#endif + do_exit(SIGSEGV); + } + } else { + last_irq_sums[cpu] = sum; + watchdog_alert_counter[cpu] = 0; + } + } + + WDCTR = wdt | WDCTR_WDRST; + tmp = WDCTR; + WDCTR = wdt | WDCTR_WDCNE; + tmp = WDCTR; +} diff --git a/arch/mn10300/kernel/mn10300_ksyms.c b/arch/mn10300/kernel/mn10300_ksyms.c new file mode 100644 index 00000000..f9eb9753 --- /dev/null +++ b/arch/mn10300/kernel/mn10300_ksyms.c @@ -0,0 +1,42 @@ +/* MN10300 Miscellaneous and library kernel exports + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <asm/uaccess.h> +#include <asm/pgtable.h> + + +EXPORT_SYMBOL(empty_zero_page); + +EXPORT_SYMBOL(change_bit); +EXPORT_SYMBOL(test_and_change_bit); + +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(memmove); +EXPORT_SYMBOL(memset); + +EXPORT_SYMBOL(strncpy_from_user); +EXPORT_SYMBOL(__strncpy_from_user); +EXPORT_SYMBOL(clear_user); +EXPORT_SYMBOL(__clear_user); +EXPORT_SYMBOL(__generic_copy_from_user); +EXPORT_SYMBOL(__generic_copy_to_user); +EXPORT_SYMBOL(strnlen_user); + +extern u64 __ashrdi3(u64, unsigned); +extern u64 __ashldi3(u64, unsigned); +extern u64 __lshrdi3(u64, unsigned); +extern s64 __negdi2(s64); +extern int __ucmpdi2(u64, u64); +EXPORT_SYMBOL(__ashrdi3); +EXPORT_SYMBOL(__ashldi3); +EXPORT_SYMBOL(__lshrdi3); +EXPORT_SYMBOL(__negdi2); +EXPORT_SYMBOL(__ucmpdi2); diff --git a/arch/mn10300/kernel/module.c b/arch/mn10300/kernel/module.c new file mode 100644 index 00000000..196a111e --- /dev/null +++ b/arch/mn10300/kernel/module.c @@ -0,0 +1,217 @@ +/* MN10300 Kernel module helper routines + * + * Copyright (C) 2007, 2008, 2009 Red Hat, Inc. All Rights Reserved. + * Written by Mark Salter (msalter@redhat.com) + * - Derived from arch/i386/kernel/module.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public Licence as published by + * the Free Software Foundation; either version 2 of the Licence, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public Licence for more details. + * + * You should have received a copy of the GNU General Public Licence + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/moduleloader.h> +#include <linux/elf.h> +#include <linux/vmalloc.h> +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/bug.h> + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(fmt, ...) +#endif + +/* + * allocate storage for a module + */ +void *module_alloc(unsigned long size) +{ + if (size == 0) + return NULL; + return vmalloc_exec(size); +} + +/* + * free memory returned from module_alloc() + */ +void module_free(struct module *mod, void *module_region) +{ + vfree(module_region); +} + +/* + * allow the arch to fix up the section table + * - we don't need anything special + */ +int module_frob_arch_sections(Elf_Ehdr *hdr, + Elf_Shdr *sechdrs, + char *secstrings, + struct module *mod) +{ + return 0; +} + +static void reloc_put16(uint8_t *p, uint32_t val) +{ + p[0] = val & 0xff; + p[1] = (val >> 8) & 0xff; +} + +static void reloc_put24(uint8_t *p, uint32_t val) +{ + reloc_put16(p, val); + p[2] = (val >> 16) & 0xff; +} + +static void reloc_put32(uint8_t *p, uint32_t val) +{ + reloc_put16(p, val); + reloc_put16(p+2, val >> 16); +} + +/* + * apply a REL relocation + */ +int apply_relocate(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + printk(KERN_ERR "module %s: RELOCATION unsupported\n", + me->name); + return -ENOEXEC; +} + +/* + * apply a RELA relocation + */ +int apply_relocate_add(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + unsigned int i, sym_diff_seen = 0; + Elf32_Rela *rel = (void *)sechdrs[relsec].sh_addr; + Elf32_Sym *sym; + Elf32_Addr relocation, sym_diff_val = 0; + uint8_t *location; + uint32_t value; + + DEBUGP("Applying relocate section %u to %u\n", + relsec, sechdrs[relsec].sh_info); + + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { + /* this is where to make the change */ + location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + + rel[i].r_offset; + + /* this is the symbol the relocation is referring to (note that + * all undefined symbols have been resolved by the caller) */ + sym = (Elf32_Sym *)sechdrs[symindex].sh_addr + + ELF32_R_SYM(rel[i].r_info); + + /* this is the adjustment to be made */ + relocation = sym->st_value + rel[i].r_addend; + + if (sym_diff_seen) { + switch (ELF32_R_TYPE(rel[i].r_info)) { + case R_MN10300_32: + case R_MN10300_24: + case R_MN10300_16: + case R_MN10300_8: + relocation -= sym_diff_val; + sym_diff_seen = 0; + break; + default: + printk(KERN_ERR "module %s: Unexpected SYM_DIFF relocation: %u\n", + me->name, ELF32_R_TYPE(rel[i].r_info)); + return -ENOEXEC; + } + } + + switch (ELF32_R_TYPE(rel[i].r_info)) { + /* for the first four relocation types, we simply + * store the adjustment at the location given */ + case R_MN10300_32: + reloc_put32(location, relocation); + break; + case R_MN10300_24: + reloc_put24(location, relocation); + break; + case R_MN10300_16: + reloc_put16(location, relocation); + break; + case R_MN10300_8: + *location = relocation; + break; + + /* for the next three relocation types, we write the + * adjustment with the address subtracted over the + * value at the location given */ + case R_MN10300_PCREL32: + value = relocation - (uint32_t) location; + reloc_put32(location, value); + break; + case R_MN10300_PCREL16: + value = relocation - (uint32_t) location; + reloc_put16(location, value); + break; + case R_MN10300_PCREL8: + *location = relocation - (uint32_t) location; + break; + + case R_MN10300_SYM_DIFF: + /* This is used to adjust the next reloc as required + * by relaxation. */ + sym_diff_seen = 1; + sym_diff_val = sym->st_value; + break; + + case R_MN10300_ALIGN: + /* Just ignore the ALIGN relocs. + * Only interesting if kernel performed relaxation. */ + continue; + + default: + printk(KERN_ERR "module %s: Unknown relocation: %u\n", + me->name, ELF32_R_TYPE(rel[i].r_info)); + return -ENOEXEC; + } + } + if (sym_diff_seen) { + printk(KERN_ERR "module %s: Nothing follows SYM_DIFF relocation: %u\n", + me->name, ELF32_R_TYPE(rel[i].r_info)); + return -ENOEXEC; + } + return 0; +} + +/* + * finish loading the module + */ +int module_finalize(const Elf_Ehdr *hdr, + const Elf_Shdr *sechdrs, + struct module *me) +{ + return 0; +} + +/* + * finish clearing the module + */ +void module_arch_cleanup(struct module *mod) +{ +} diff --git a/arch/mn10300/kernel/process.c b/arch/mn10300/kernel/process.c new file mode 100644 index 00000000..28eec310 --- /dev/null +++ b/arch/mn10300/kernel/process.c @@ -0,0 +1,322 @@ +/* MN10300 Process handling code + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/user.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/reboot.h> +#include <linux/percpu.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <asm/uaccess.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/mmu_context.h> +#include <asm/fpu.h> +#include <asm/reset-regs.h> +#include <asm/gdb-stub.h> +#include "internal.h" + +/* + * power management idle function, if any.. + */ +void (*pm_idle)(void); +EXPORT_SYMBOL(pm_idle); + +/* + * return saved PC of a blocked thread. + */ +unsigned long thread_saved_pc(struct task_struct *tsk) +{ + return ((unsigned long *) tsk->thread.sp)[3]; +} + +/* + * power off function, if any + */ +void (*pm_power_off)(void); +EXPORT_SYMBOL(pm_power_off); + +#if !defined(CONFIG_SMP) || defined(CONFIG_HOTPLUG_CPU) +/* + * we use this if we don't have any better idle routine + */ +static void default_idle(void) +{ + local_irq_disable(); + if (!need_resched()) + safe_halt(); + else + local_irq_enable(); +} + +#else /* !CONFIG_SMP || CONFIG_HOTPLUG_CPU */ +/* + * On SMP it's slightly faster (but much more power-consuming!) + * to poll the ->work.need_resched flag instead of waiting for the + * cross-CPU IPI to arrive. Use this option with caution. + */ +static inline void poll_idle(void) +{ + int oldval; + + local_irq_enable(); + + /* + * Deal with another CPU just having chosen a thread to + * run here: + */ + oldval = test_and_clear_thread_flag(TIF_NEED_RESCHED); + + if (!oldval) { + set_thread_flag(TIF_POLLING_NRFLAG); + while (!need_resched()) + cpu_relax(); + clear_thread_flag(TIF_POLLING_NRFLAG); + } else { + set_need_resched(); + } +} +#endif /* !CONFIG_SMP || CONFIG_HOTPLUG_CPU */ + +/* + * the idle thread + * - there's no useful work to be done, so just try to conserve power and have + * a low exit latency (ie sit in a loop waiting for somebody to say that + * they'd like to reschedule) + */ +void cpu_idle(void) +{ + /* endless idle loop with no priority at all */ + for (;;) { + while (!need_resched()) { + void (*idle)(void); + + smp_rmb(); + idle = pm_idle; + if (!idle) { +#if defined(CONFIG_SMP) && !defined(CONFIG_HOTPLUG_CPU) + idle = poll_idle; +#else /* CONFIG_SMP && !CONFIG_HOTPLUG_CPU */ + idle = default_idle; +#endif /* CONFIG_SMP && !CONFIG_HOTPLUG_CPU */ + } + idle(); + } + + preempt_enable_no_resched(); + schedule(); + preempt_disable(); + } +} + +void release_segments(struct mm_struct *mm) +{ +} + +void machine_restart(char *cmd) +{ +#ifdef CONFIG_KERNEL_DEBUGGER + gdbstub_exit(0); +#endif + +#ifdef mn10300_unit_hard_reset + mn10300_unit_hard_reset(); +#else + mn10300_proc_hard_reset(); +#endif +} + +void machine_halt(void) +{ +#ifdef CONFIG_KERNEL_DEBUGGER + gdbstub_exit(0); +#endif +} + +void machine_power_off(void) +{ +#ifdef CONFIG_KERNEL_DEBUGGER + gdbstub_exit(0); +#endif +} + +void show_regs(struct pt_regs *regs) +{ +} + +/* + * create a kernel thread + */ +int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) +{ + struct pt_regs regs; + + memset(®s, 0, sizeof(regs)); + + regs.a2 = (unsigned long) fn; + regs.d2 = (unsigned long) arg; + regs.pc = (unsigned long) kernel_thread_helper; + local_save_flags(regs.epsw); + regs.epsw |= EPSW_IE | EPSW_IM_7; + + /* Ok, create the new process.. */ + return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, + NULL, NULL); +} +EXPORT_SYMBOL(kernel_thread); + +/* + * free current thread data structures etc.. + */ +void exit_thread(void) +{ + exit_fpu(); +} + +void flush_thread(void) +{ + flush_fpu(); +} + +void release_thread(struct task_struct *dead_task) +{ +} + +/* + * we do not have to muck with descriptors here, that is + * done in switch_mm() as needed. + */ +void copy_segments(struct task_struct *p, struct mm_struct *new_mm) +{ +} + +/* + * this gets called before we allocate a new thread and copy the current task + * into it so that we can store lazy state into memory + */ +void prepare_to_copy(struct task_struct *tsk) +{ + unlazy_fpu(tsk); +} + +/* + * set up the kernel stack for a new thread and copy arch-specific thread + * control information + */ +int copy_thread(unsigned long clone_flags, + unsigned long c_usp, unsigned long ustk_size, + struct task_struct *p, struct pt_regs *kregs) +{ + struct thread_info *ti = task_thread_info(p); + struct pt_regs *c_uregs, *c_kregs, *uregs; + unsigned long c_ksp; + + uregs = current->thread.uregs; + + c_ksp = (unsigned long) task_stack_page(p) + THREAD_SIZE; + + /* allocate the userspace exception frame and set it up */ + c_ksp -= sizeof(struct pt_regs); + c_uregs = (struct pt_regs *) c_ksp; + + p->thread.uregs = c_uregs; + *c_uregs = *uregs; + c_uregs->sp = c_usp; + c_uregs->epsw &= ~EPSW_FE; /* my FPU */ + + c_ksp -= 12; /* allocate function call ABI slack */ + + /* the new TLS pointer is passed in as arg #5 to sys_clone() */ + if (clone_flags & CLONE_SETTLS) + c_uregs->e2 = current_frame()->d3; + + /* set up the return kernel frame if called from kernel_thread() */ + c_kregs = c_uregs; + if (kregs != uregs) { + c_ksp -= sizeof(struct pt_regs); + c_kregs = (struct pt_regs *) c_ksp; + *c_kregs = *kregs; + c_kregs->sp = c_usp; + c_kregs->next = c_uregs; +#ifdef CONFIG_MN10300_CURRENT_IN_E2 + c_kregs->e2 = (unsigned long) p; /* current */ +#endif + + c_ksp -= 12; /* allocate function call ABI slack */ + } + + /* set up things up so the scheduler can start the new task */ + ti->frame = c_kregs; + p->thread.a3 = (unsigned long) c_kregs; + p->thread.sp = c_ksp; + p->thread.pc = (unsigned long) ret_from_fork; + p->thread.wchan = (unsigned long) ret_from_fork; + p->thread.usp = c_usp; + + return 0; +} + +/* + * clone a process + * - tlsptr is retrieved by copy_thread() from current_frame()->d3 + */ +asmlinkage long sys_clone(unsigned long clone_flags, unsigned long newsp, + int __user *parent_tidptr, int __user *child_tidptr, + int __user *tlsptr) +{ + return do_fork(clone_flags, newsp ?: current_frame()->sp, + current_frame(), 0, parent_tidptr, child_tidptr); +} + +asmlinkage long sys_fork(void) +{ + return do_fork(SIGCHLD, current_frame()->sp, + current_frame(), 0, NULL, NULL); +} + +asmlinkage long sys_vfork(void) +{ + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, current_frame()->sp, + current_frame(), 0, NULL, NULL); +} + +asmlinkage long sys_execve(const char __user *name, + const char __user *const __user *argv, + const char __user *const __user *envp) +{ + char *filename; + int error; + + filename = getname(name); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + return error; + error = do_execve(filename, argv, envp, current_frame()); + putname(filename); + return error; +} + +unsigned long get_wchan(struct task_struct *p) +{ + return p->thread.wchan; +} diff --git a/arch/mn10300/kernel/profile-low.S b/arch/mn10300/kernel/profile-low.S new file mode 100644 index 00000000..94ffac12 --- /dev/null +++ b/arch/mn10300/kernel/profile-low.S @@ -0,0 +1,72 @@ +############################################################################### +# +# Fast profiling interrupt handler +# +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/segment.h> +#include <asm/smp.h> +#include <asm/intctl-regs.h> +#include <asm/timer-regs.h> + +#define pi break + + .balign 4 +counter: + .long -1 + +############################################################################### +# +# Profiling interrupt entry point +# - intended to run at interrupt priority 1 +# +############################################################################### +ENTRY(profile_handler) + movm [d2,d3,a2],(sp) + + # ignore userspace + mov (12,sp),d2 + and EPSW_nSL,d2 + bne out + + # do nothing if there's no buffer + mov (prof_buffer),a2 + and a2,a2 + beq out + or 0x20000000,a2 + + # calculate relative position in text segment + mov (16,sp),d2 + sub _stext,d2 + mov (prof_shift),d3 + lsr d3,d2 + mov (prof_len),d3 + cmp d3,d2 + bcc outside_text + + # increment the appropriate profile bucket +do_inc: + asl2 d2 + mov (a2,d2),d3 + inc d3 + mov d3,(a2,d2) +out: + mov GxICR_DETECT,d2 + movbu d2,(TM11ICR) # ACK the interrupt + movbu (TM11ICR),d2 + movm (sp),[d2,d3,a2] + rti + +outside_text: + sub 1,d3 + mov d3,d2 + bra do_inc diff --git a/arch/mn10300/kernel/profile.c b/arch/mn10300/kernel/profile.c new file mode 100644 index 00000000..4f342f75 --- /dev/null +++ b/arch/mn10300/kernel/profile.c @@ -0,0 +1,51 @@ +/* MN10300 Profiling setup + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +/* + * initialise the profiling if enabled + * - using with gdbstub will give anomalous results + * - can't be used with gdbstub if running at IRQ priority 0 + */ +static __init int profile_init(void) +{ + u16 tmp; + + if (!prof_buffer) + return 0; + + /* use timer 11 to drive the profiling interrupts */ + set_intr_stub(EXCEP_IRQ_LEVEL0, profile_handler); + + /* set IRQ priority at which to run */ + set_intr_level(TM11IRQ, GxICR_LEVEL_0); + + /* set up timer 11 + * - source: (IOCLK 33MHz)*2 = 66MHz + * - frequency: (33330000*2) / 8 / 20625 = 202Hz + */ + TM11BR = 20625 - 1; + TM11MD = TM8MD_SRC_IOCLK_8; + TM11MD |= TM8MD_INIT_COUNTER; + TM11MD &= ~TM8MD_INIT_COUNTER; + TM11MD |= TM8MD_COUNT_ENABLE; + + TM11ICR |= GxICR_ENABLE; + tmp = TM11ICR; + + printk(KERN_INFO "Profiling initiated on timer 11, priority 0, %uHz\n", + MN10300_IOCLK / 8 / (TM11BR + 1)); + printk(KERN_INFO "Profile histogram stored %p-%p\n", + prof_buffer, (u8 *)(prof_buffer + prof_len) - 1); + + return 0; +} + +__initcall(profile_init); diff --git a/arch/mn10300/kernel/ptrace.c b/arch/mn10300/kernel/ptrace.c new file mode 100644 index 00000000..5c0b07e6 --- /dev/null +++ b/arch/mn10300/kernel/ptrace.c @@ -0,0 +1,386 @@ +/* MN10300 Process tracing + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Modified by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/user.h> +#include <linux/regset.h> +#include <linux/elf.h> +#include <linux/tracehook.h> +#include <asm/uaccess.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/processor.h> +#include <asm/cacheflush.h> +#include <asm/fpu.h> +#include <asm/asm-offsets.h> + +/* + * translate ptrace register IDs into struct pt_regs offsets + */ +static const u8 ptrace_regid_to_frame[] = { + [PT_A3 << 2] = REG_A3, + [PT_A2 << 2] = REG_A2, + [PT_D3 << 2] = REG_D3, + [PT_D2 << 2] = REG_D2, + [PT_MCVF << 2] = REG_MCVF, + [PT_MCRL << 2] = REG_MCRL, + [PT_MCRH << 2] = REG_MCRH, + [PT_MDRQ << 2] = REG_MDRQ, + [PT_E1 << 2] = REG_E1, + [PT_E0 << 2] = REG_E0, + [PT_E7 << 2] = REG_E7, + [PT_E6 << 2] = REG_E6, + [PT_E5 << 2] = REG_E5, + [PT_E4 << 2] = REG_E4, + [PT_E3 << 2] = REG_E3, + [PT_E2 << 2] = REG_E2, + [PT_SP << 2] = REG_SP, + [PT_LAR << 2] = REG_LAR, + [PT_LIR << 2] = REG_LIR, + [PT_MDR << 2] = REG_MDR, + [PT_A1 << 2] = REG_A1, + [PT_A0 << 2] = REG_A0, + [PT_D1 << 2] = REG_D1, + [PT_D0 << 2] = REG_D0, + [PT_ORIG_D0 << 2] = REG_ORIG_D0, + [PT_EPSW << 2] = REG_EPSW, + [PT_PC << 2] = REG_PC, +}; + +static inline int get_stack_long(struct task_struct *task, int offset) +{ + return *(unsigned long *) + ((unsigned long) task->thread.uregs + offset); +} + +static inline +int put_stack_long(struct task_struct *task, int offset, unsigned long data) +{ + unsigned long stack; + + stack = (unsigned long) task->thread.uregs + offset; + *(unsigned long *) stack = data; + return 0; +} + +/* + * retrieve the contents of MN10300 userspace general registers + */ +static int genregs_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + const struct pt_regs *regs = task_pt_regs(target); + int ret; + + /* we need to skip regs->next */ + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + regs, 0, PT_ORIG_D0 * sizeof(long)); + if (ret < 0) + return ret; + + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + ®s->orig_d0, PT_ORIG_D0 * sizeof(long), + NR_PTREGS * sizeof(long)); + if (ret < 0) + return ret; + + return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + NR_PTREGS * sizeof(long), -1); +} + +/* + * update the contents of the MN10300 userspace general registers + */ +static int genregs_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + struct pt_regs *regs = task_pt_regs(target); + unsigned long tmp; + int ret; + + /* we need to skip regs->next */ + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + regs, 0, PT_ORIG_D0 * sizeof(long)); + if (ret < 0) + return ret; + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + ®s->orig_d0, PT_ORIG_D0 * sizeof(long), + PT_EPSW * sizeof(long)); + if (ret < 0) + return ret; + + /* we need to mask off changes to EPSW */ + tmp = regs->epsw; + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &tmp, PT_EPSW * sizeof(long), + PT_PC * sizeof(long)); + tmp &= EPSW_FLAG_V | EPSW_FLAG_C | EPSW_FLAG_N | EPSW_FLAG_Z; + tmp |= regs->epsw & ~(EPSW_FLAG_V | EPSW_FLAG_C | EPSW_FLAG_N | + EPSW_FLAG_Z); + regs->epsw = tmp; + + if (ret < 0) + return ret; + + /* and finally load the PC */ + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + ®s->pc, PT_PC * sizeof(long), + NR_PTREGS * sizeof(long)); + + if (ret < 0) + return ret; + + return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + NR_PTREGS * sizeof(long), -1); +} + +/* + * retrieve the contents of MN10300 userspace FPU registers + */ +static int fpuregs_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + const struct fpu_state_struct *fpregs = &target->thread.fpu_state; + int ret; + + unlazy_fpu(target); + + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + fpregs, 0, sizeof(*fpregs)); + if (ret < 0) + return ret; + + return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + sizeof(*fpregs), -1); +} + +/* + * update the contents of the MN10300 userspace FPU registers + */ +static int fpuregs_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + struct fpu_state_struct fpu_state = target->thread.fpu_state; + int ret; + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &fpu_state, 0, sizeof(fpu_state)); + if (ret < 0) + return ret; + + fpu_kill_state(target); + target->thread.fpu_state = fpu_state; + set_using_fpu(target); + + return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + sizeof(fpu_state), -1); +} + +/* + * determine if the FPU registers have actually been used + */ +static int fpuregs_active(struct task_struct *target, + const struct user_regset *regset) +{ + return is_using_fpu(target) ? regset->n : 0; +} + +/* + * Define the register sets available on the MN10300 under Linux + */ +enum mn10300_regset { + REGSET_GENERAL, + REGSET_FPU, +}; + +static const struct user_regset mn10300_regsets[] = { + /* + * General register format is: + * A3, A2, D3, D2, MCVF, MCRL, MCRH, MDRQ + * E1, E0, E7...E2, SP, LAR, LIR, MDR + * A1, A0, D1, D0, ORIG_D0, EPSW, PC + */ + [REGSET_GENERAL] = { + .core_note_type = NT_PRSTATUS, + .n = ELF_NGREG, + .size = sizeof(long), + .align = sizeof(long), + .get = genregs_get, + .set = genregs_set, + }, + /* + * FPU register format is: + * FS0-31, FPCR + */ + [REGSET_FPU] = { + .core_note_type = NT_PRFPREG, + .n = sizeof(struct fpu_state_struct) / sizeof(long), + .size = sizeof(long), + .align = sizeof(long), + .get = fpuregs_get, + .set = fpuregs_set, + .active = fpuregs_active, + }, +}; + +static const struct user_regset_view user_mn10300_native_view = { + .name = "mn10300", + .e_machine = EM_MN10300, + .regsets = mn10300_regsets, + .n = ARRAY_SIZE(mn10300_regsets), +}; + +const struct user_regset_view *task_user_regset_view(struct task_struct *task) +{ + return &user_mn10300_native_view; +} + +/* + * set the single-step bit + */ +void user_enable_single_step(struct task_struct *child) +{ +#ifndef CONFIG_MN10300_USING_JTAG + struct user *dummy = NULL; + long tmp; + + tmp = get_stack_long(child, (unsigned long) &dummy->regs.epsw); + tmp |= EPSW_T; + put_stack_long(child, (unsigned long) &dummy->regs.epsw, tmp); +#endif +} + +/* + * make sure the single-step bit is not set + */ +void user_disable_single_step(struct task_struct *child) +{ +#ifndef CONFIG_MN10300_USING_JTAG + struct user *dummy = NULL; + long tmp; + + tmp = get_stack_long(child, (unsigned long) &dummy->regs.epsw); + tmp &= ~EPSW_T; + put_stack_long(child, (unsigned long) &dummy->regs.epsw, tmp); +#endif +} + +void ptrace_disable(struct task_struct *child) +{ + user_disable_single_step(child); +} + +/* + * handle the arch-specific side of process tracing + */ +long arch_ptrace(struct task_struct *child, long request, + unsigned long addr, unsigned long data) +{ + unsigned long tmp; + int ret; + unsigned long __user *datap = (unsigned long __user *) data; + + switch (request) { + /* read the word at location addr in the USER area. */ + case PTRACE_PEEKUSR: + ret = -EIO; + if ((addr & 3) || addr > sizeof(struct user) - 3) + break; + + tmp = 0; /* Default return condition */ + if (addr < NR_PTREGS << 2) + tmp = get_stack_long(child, + ptrace_regid_to_frame[addr]); + ret = put_user(tmp, datap); + break; + + /* write the word at location addr in the USER area */ + case PTRACE_POKEUSR: + ret = -EIO; + if ((addr & 3) || addr > sizeof(struct user) - 3) + break; + + ret = 0; + if (addr < NR_PTREGS << 2) + ret = put_stack_long(child, ptrace_regid_to_frame[addr], + data); + break; + + case PTRACE_GETREGS: /* Get all integer regs from the child. */ + return copy_regset_to_user(child, &user_mn10300_native_view, + REGSET_GENERAL, + 0, NR_PTREGS * sizeof(long), + datap); + + case PTRACE_SETREGS: /* Set all integer regs in the child. */ + return copy_regset_from_user(child, &user_mn10300_native_view, + REGSET_GENERAL, + 0, NR_PTREGS * sizeof(long), + datap); + + case PTRACE_GETFPREGS: /* Get the child FPU state. */ + return copy_regset_to_user(child, &user_mn10300_native_view, + REGSET_FPU, + 0, sizeof(struct fpu_state_struct), + datap); + + case PTRACE_SETFPREGS: /* Set the child FPU state. */ + return copy_regset_from_user(child, &user_mn10300_native_view, + REGSET_FPU, + 0, sizeof(struct fpu_state_struct), + datap); + + default: + ret = ptrace_request(child, request, addr, data); + break; + } + + return ret; +} + +/* + * handle tracing of system call entry + * - return the revised system call number or ULONG_MAX to cause ENOSYS + */ +asmlinkage unsigned long syscall_trace_entry(struct pt_regs *regs) +{ + if (tracehook_report_syscall_entry(regs)) + /* tracing decided this syscall should not happen, so + * We'll return a bogus call number to get an ENOSYS + * error, but leave the original number in + * regs->orig_d0 + */ + return ULONG_MAX; + + return regs->orig_d0; +} + +/* + * handle tracing of system call exit + */ +asmlinkage void syscall_trace_exit(struct pt_regs *regs) +{ + tracehook_report_syscall_exit(regs, 0); +} diff --git a/arch/mn10300/kernel/rtc.c b/arch/mn10300/kernel/rtc.c new file mode 100644 index 00000000..48d7058b --- /dev/null +++ b/arch/mn10300/kernel/rtc.c @@ -0,0 +1,132 @@ +/* MN10300 RTC management + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/mc146818rtc.h> +#include <linux/bcd.h> +#include <linux/timex.h> +#include <asm/rtc-regs.h> +#include <asm/rtc.h> + +DEFINE_SPINLOCK(rtc_lock); +EXPORT_SYMBOL(rtc_lock); + +/* + * Read the current RTC time + */ +void read_persistent_clock(struct timespec *ts) +{ + struct rtc_time tm; + + get_rtc_time(&tm); + + ts->tv_nsec = 0; + ts->tv_sec = mktime(tm.tm_year, tm.tm_mon, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + + /* if rtc is way off in the past, set something reasonable */ + if (ts->tv_sec < 0) + ts->tv_sec = mktime(2009, 1, 1, 12, 0, 0); +} + +/* + * In order to set the CMOS clock precisely, set_rtc_mmss has to be called 500 + * ms after the second nowtime has started, because when nowtime is written + * into the registers of the CMOS clock, it will jump to the next second + * precisely 500 ms later. Check the Motorola MC146818A or Dallas DS12887 data + * sheet for details. + * + * BUG: This routine does not handle hour overflow properly; it just + * sets the minutes. Usually you'll only notice that after reboot! + */ +static int set_rtc_mmss(unsigned long nowtime) +{ + unsigned char save_control, save_freq_select; + int retval = 0; + int real_seconds, real_minutes, cmos_minutes; + + /* gets recalled with irq locally disabled */ + spin_lock(&rtc_lock); + save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being + * set */ + CMOS_WRITE(save_control | RTC_SET, RTC_CONTROL); + + save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset + * prescaler */ + CMOS_WRITE(save_freq_select | RTC_DIV_RESET2, RTC_FREQ_SELECT); + + cmos_minutes = CMOS_READ(RTC_MINUTES); + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) + cmos_minutes = bcd2bin(cmos_minutes); + + /* + * since we're only adjusting minutes and seconds, + * don't interfere with hour overflow. This avoids + * messing with unknown time zones but requires your + * RTC not to be off by more than 15 minutes + */ + real_seconds = nowtime % 60; + real_minutes = nowtime / 60; + if (((abs(real_minutes - cmos_minutes) + 15) / 30) & 1) + /* correct for half hour time zone */ + real_minutes += 30; + real_minutes %= 60; + + if (abs(real_minutes - cmos_minutes) < 30) { + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + real_seconds = bin2bcd(real_seconds); + real_minutes = bin2bcd(real_minutes); + } + CMOS_WRITE(real_seconds, RTC_SECONDS); + CMOS_WRITE(real_minutes, RTC_MINUTES); + } else { + printk_once(KERN_NOTICE + "set_rtc_mmss: can't update from %d to %d\n", + cmos_minutes, real_minutes); + retval = -1; + } + + /* The following flags have to be released exactly in this order, + * otherwise the DS12887 (popular MC146818A clone with integrated + * battery and quartz) will not reset the oscillator and will not + * update precisely 500 ms later. You won't find this mentioned in + * the Dallas Semiconductor data sheets, but who believes data + * sheets anyway ... -- Markus Kuhn + */ + CMOS_WRITE(save_control, RTC_CONTROL); + CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); + spin_unlock(&rtc_lock); + + return retval; +} + +int update_persistent_clock(struct timespec now) +{ + return set_rtc_mmss(now.tv_sec); +} + +/* + * calibrate the TSC clock against the RTC + */ +void __init calibrate_clock(void) +{ + unsigned char status; + + /* make sure the RTC is running and is set to operate in 24hr mode */ + status = RTSRC; + RTCRB |= RTCRB_SET; + RTCRB |= RTCRB_TM_24HR; + RTCRB &= ~RTCRB_DM_BINARY; + RTCRA |= RTCRA_DVR; + RTCRA &= ~RTCRA_DVR; + RTCRB &= ~RTCRB_SET; +} diff --git a/arch/mn10300/kernel/setup.c b/arch/mn10300/kernel/setup.c new file mode 100644 index 00000000..9e7a3209 --- /dev/null +++ b/arch/mn10300/kernel/setup.c @@ -0,0 +1,299 @@ +/* MN10300 Arch-specific initialisation + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/user.h> +#include <linux/tty.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/bootmem.h> +#include <linux/seq_file.h> +#include <linux/cpu.h> +#include <asm/processor.h> +#include <linux/console.h> +#include <asm/uaccess.h> +#include <asm/system.h> +#include <asm/setup.h> +#include <asm/io.h> +#include <asm/smp.h> +#include <proc/proc.h> +#include <asm/fpu.h> +#include <asm/sections.h> + +struct mn10300_cpuinfo boot_cpu_data; + +/* For PCI or other memory-mapped resources */ +unsigned long pci_mem_start = 0x18000000; + +char redboot_command_line[COMMAND_LINE_SIZE] = + "console=ttyS0,115200 root=/dev/mtdblock3 rw"; + +char __initdata redboot_platform_name[COMMAND_LINE_SIZE]; + +static struct resource code_resource = { + .start = 0x100000, + .end = 0, + .name = "Kernel code", +}; + +static struct resource data_resource = { + .start = 0, + .end = 0, + .name = "Kernel data", +}; + +static unsigned long __initdata phys_memory_base; +static unsigned long __initdata phys_memory_end; +static unsigned long __initdata memory_end; +unsigned long memory_size; + +struct thread_info *__current_ti = &init_thread_union.thread_info; +struct task_struct *__current = &init_task; + +#define mn10300_known_cpus 5 +static const char *const mn10300_cputypes[] = { + "am33-1", + "am33-2", + "am34-1", + "am33-3", + "am34-2", + "unknown" +}; + +/* + * + */ +static void __init parse_mem_cmdline(char **cmdline_p) +{ + char *from, *to, c; + + /* save unparsed command line copy for /proc/cmdline */ + strcpy(boot_command_line, redboot_command_line); + + /* see if there's an explicit memory size option */ + from = redboot_command_line; + to = redboot_command_line; + c = ' '; + + for (;;) { + if (c == ' ' && !memcmp(from, "mem=", 4)) { + if (to != redboot_command_line) + to--; + memory_size = memparse(from + 4, &from); + } + + c = *(from++); + if (!c) + break; + + *(to++) = c; + } + + *to = '\0'; + *cmdline_p = redboot_command_line; + + if (memory_size == 0) + panic("Memory size not known\n"); + + memory_end = (unsigned long) CONFIG_KERNEL_RAM_BASE_ADDRESS + + memory_size; + if (memory_end > phys_memory_end) + memory_end = phys_memory_end; +} + +/* + * architecture specific setup + */ +void __init setup_arch(char **cmdline_p) +{ + unsigned long bootmap_size; + unsigned long kstart_pfn, start_pfn, free_pfn, end_pfn; + + cpu_init(); + unit_setup(); + smp_init_cpus(); + parse_mem_cmdline(cmdline_p); + + init_mm.start_code = (unsigned long)&_text; + init_mm.end_code = (unsigned long) &_etext; + init_mm.end_data = (unsigned long) &_edata; + init_mm.brk = (unsigned long) &_end; + + code_resource.start = virt_to_bus(&_text); + code_resource.end = virt_to_bus(&_etext)-1; + data_resource.start = virt_to_bus(&_etext); + data_resource.end = virt_to_bus(&_edata)-1; + + start_pfn = (CONFIG_KERNEL_RAM_BASE_ADDRESS >> PAGE_SHIFT); + kstart_pfn = PFN_UP(__pa(&_text)); + free_pfn = PFN_UP(__pa(&_end)); + end_pfn = PFN_DOWN(__pa(memory_end)); + + bootmap_size = init_bootmem_node(&contig_page_data, + free_pfn, + start_pfn, + end_pfn); + + if (kstart_pfn > start_pfn) + free_bootmem(PFN_PHYS(start_pfn), + PFN_PHYS(kstart_pfn - start_pfn)); + + free_bootmem(PFN_PHYS(free_pfn), + PFN_PHYS(end_pfn - free_pfn)); + + /* If interrupt vector table is in main ram, then we need to + reserve the page it is occupying. */ + if (CONFIG_INTERRUPT_VECTOR_BASE >= CONFIG_KERNEL_RAM_BASE_ADDRESS && + CONFIG_INTERRUPT_VECTOR_BASE < memory_end) + reserve_bootmem(CONFIG_INTERRUPT_VECTOR_BASE, PAGE_SIZE, + BOOTMEM_DEFAULT); + + reserve_bootmem(PAGE_ALIGN(PFN_PHYS(free_pfn)), bootmap_size, + BOOTMEM_DEFAULT); + +#ifdef CONFIG_VT +#if defined(CONFIG_VGA_CONSOLE) + conswitchp = &vga_con; +#elif defined(CONFIG_DUMMY_CONSOLE) + conswitchp = &dummy_con; +#endif +#endif + + paging_init(); +} + +/* + * perform CPU initialisation + */ +void __init cpu_init(void) +{ + unsigned long cpurev = CPUREV, type; + + type = (CPUREV & CPUREV_TYPE) >> CPUREV_TYPE_S; + if (type > mn10300_known_cpus) + type = mn10300_known_cpus; + + printk(KERN_INFO "Panasonic %s, rev %ld\n", + mn10300_cputypes[type], + (cpurev & CPUREV_REVISION) >> CPUREV_REVISION_S); + + get_mem_info(&phys_memory_base, &memory_size); + phys_memory_end = phys_memory_base + memory_size; + + fpu_init_state(); +} + +static struct cpu cpu_devices[NR_CPUS]; + +static int __init topology_init(void) +{ + int i; + + for_each_present_cpu(i) + register_cpu(&cpu_devices[i], i); + + return 0; +} + +subsys_initcall(topology_init); + +/* + * Get CPU information for use by the procfs. + */ +static int show_cpuinfo(struct seq_file *m, void *v) +{ +#ifdef CONFIG_SMP + struct mn10300_cpuinfo *c = v; + unsigned long cpu_id = c - cpu_data; + unsigned long cpurev = c->type, type, icachesz, dcachesz; +#else /* CONFIG_SMP */ + unsigned long cpu_id = 0; + unsigned long cpurev = CPUREV, type, icachesz, dcachesz; +#endif /* CONFIG_SMP */ + +#ifdef CONFIG_SMP + if (!cpu_online(cpu_id)) + return 0; +#endif + + type = (cpurev & CPUREV_TYPE) >> CPUREV_TYPE_S; + if (type > mn10300_known_cpus) + type = mn10300_known_cpus; + + icachesz = + ((cpurev & CPUREV_ICWAY ) >> CPUREV_ICWAY_S) * + ((cpurev & CPUREV_ICSIZE) >> CPUREV_ICSIZE_S) * + 1024; + + dcachesz = + ((cpurev & CPUREV_DCWAY ) >> CPUREV_DCWAY_S) * + ((cpurev & CPUREV_DCSIZE) >> CPUREV_DCSIZE_S) * + 1024; + + seq_printf(m, + "processor : %ld\n" + "vendor_id : " PROCESSOR_VENDOR_NAME "\n" + "cpu core : %s\n" + "cpu rev : %lu\n" + "model name : " PROCESSOR_MODEL_NAME "\n" + "icache size: %lu\n" + "dcache size: %lu\n", + cpu_id, + mn10300_cputypes[type], + (cpurev & CPUREV_REVISION) >> CPUREV_REVISION_S, + icachesz, + dcachesz + ); + + seq_printf(m, + "ioclk speed: %lu.%02luMHz\n" + "bogomips : %lu.%02lu\n\n", + MN10300_IOCLK / 1000000, + (MN10300_IOCLK / 10000) % 100, +#ifdef CONFIG_SMP + c->loops_per_jiffy / (500000 / HZ), + (c->loops_per_jiffy / (5000 / HZ)) % 100 +#else /* CONFIG_SMP */ + loops_per_jiffy / (500000 / HZ), + (loops_per_jiffy / (5000 / HZ)) % 100 +#endif /* CONFIG_SMP */ + ); + + return 0; +} + +static void *c_start(struct seq_file *m, loff_t *pos) +{ + return *pos < NR_CPUS ? cpu_data + *pos : NULL; +} + +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return c_start(m, pos); +} + +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 = show_cpuinfo, +}; diff --git a/arch/mn10300/kernel/sigframe.h b/arch/mn10300/kernel/sigframe.h new file mode 100644 index 00000000..0decba28 --- /dev/null +++ b/arch/mn10300/kernel/sigframe.h @@ -0,0 +1,33 @@ +/* MN10300 Signal frame definitions + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +struct sigframe +{ + void (*pretcode)(void); + int sig; + struct sigcontext *psc; + struct sigcontext sc; + struct fpucontext fpuctx; + unsigned long extramask[_NSIG_WORDS-1]; + char retcode[8]; +}; + +struct rt_sigframe +{ + void (*pretcode)(void); + int sig; + struct siginfo *pinfo; + void *puc; + struct siginfo info; + struct ucontext uc; + struct fpucontext fpuctx; + char retcode[8]; +}; diff --git a/arch/mn10300/kernel/signal.c b/arch/mn10300/kernel/signal.c new file mode 100644 index 00000000..690f4e95 --- /dev/null +++ b/arch/mn10300/kernel/signal.c @@ -0,0 +1,581 @@ +/* MN10300 Signal handling + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/wait.h> +#include <linux/ptrace.h> +#include <linux/unistd.h> +#include <linux/stddef.h> +#include <linux/tty.h> +#include <linux/personality.h> +#include <linux/suspend.h> +#include <linux/tracehook.h> +#include <asm/cacheflush.h> +#include <asm/ucontext.h> +#include <asm/uaccess.h> +#include <asm/fpu.h> +#include "sigframe.h" + +#define DEBUG_SIG 0 + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +/* + * atomically swap in the new signal mask, and wait for a signal. + */ +asmlinkage long sys_sigsuspend(int history0, int history1, old_sigset_t mask) +{ + mask &= _BLOCKABLE; + spin_lock_irq(¤t->sighand->siglock); + current->saved_sigmask = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + current->state = TASK_INTERRUPTIBLE; + schedule(); + set_thread_flag(TIF_RESTORE_SIGMASK); + return -ERESTARTNOHAND; +} + +/* + * set signal action syscall + */ +asmlinkage long sys_sigaction(int sig, + const struct old_sigaction __user *act, + struct old_sigaction __user *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + if (act) { + old_sigset_t mask; + if (verify_area(VERIFY_READ, act, sizeof(*act)) || + __get_user(new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_restorer, &act->sa_restorer) || + __get_user(new_ka.sa.sa_flags, &act->sa_flags) || + __get_user(mask, &act->sa_mask)) + return -EFAULT; + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer) || + __put_user(old_ka.sa.sa_flags, &oact->sa_flags) || + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask)) + return -EFAULT; + } + + return ret; +} + +/* + * set alternate signal stack syscall + */ +asmlinkage long sys_sigaltstack(const stack_t __user *uss, stack_t *uoss) +{ + return do_sigaltstack(uss, uoss, current_frame()->sp); +} + +/* + * do a signal return; undo the signal stack. + */ +static int restore_sigcontext(struct pt_regs *regs, + struct sigcontext __user *sc, long *_d0) +{ + unsigned int err = 0; + + /* Always make any pending restarted system calls return -EINTR */ + current_thread_info()->restart_block.fn = do_no_restart_syscall; + + if (is_using_fpu(current)) + fpu_kill_state(current); + +#define COPY(x) err |= __get_user(regs->x, &sc->x) + COPY(d1); COPY(d2); COPY(d3); + COPY(a0); COPY(a1); COPY(a2); COPY(a3); + COPY(e0); COPY(e1); COPY(e2); COPY(e3); + COPY(e4); COPY(e5); COPY(e6); COPY(e7); + COPY(lar); COPY(lir); + COPY(mdr); COPY(mdrq); + COPY(mcvf); COPY(mcrl); COPY(mcrh); + COPY(sp); COPY(pc); +#undef COPY + + { + unsigned int tmpflags; +#ifndef CONFIG_MN10300_USING_JTAG +#define USER_EPSW (EPSW_FLAG_Z | EPSW_FLAG_N | EPSW_FLAG_C | EPSW_FLAG_V | \ + EPSW_T | EPSW_nAR) +#else +#define USER_EPSW (EPSW_FLAG_Z | EPSW_FLAG_N | EPSW_FLAG_C | EPSW_FLAG_V | \ + EPSW_nAR) +#endif + err |= __get_user(tmpflags, &sc->epsw); + regs->epsw = (regs->epsw & ~USER_EPSW) | + (tmpflags & USER_EPSW); + regs->orig_d0 = -1; /* disable syscall checks */ + } + + { + struct fpucontext *buf; + err |= __get_user(buf, &sc->fpucontext); + if (buf) { + if (verify_area(VERIFY_READ, buf, sizeof(*buf))) + goto badframe; + err |= fpu_restore_sigcontext(buf); + } + } + + err |= __get_user(*_d0, &sc->d0); + return err; + +badframe: + return 1; +} + +/* + * standard signal return syscall + */ +asmlinkage long sys_sigreturn(void) +{ + struct sigframe __user *frame; + sigset_t set; + long d0; + + frame = (struct sigframe __user *) current_frame()->sp; + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__get_user(set.sig[0], &frame->sc.oldmask)) + goto badframe; + + if (_NSIG_WORDS > 1 && + __copy_from_user(&set.sig[1], &frame->extramask, + sizeof(frame->extramask))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (restore_sigcontext(current_frame(), &frame->sc, &d0)) + goto badframe; + + return d0; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +/* + * realtime signal return syscall + */ +asmlinkage long sys_rt_sigreturn(void) +{ + struct rt_sigframe __user *frame; + sigset_t set; + long d0; + + frame = (struct rt_sigframe __user *) current_frame()->sp; + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (restore_sigcontext(current_frame(), &frame->uc.uc_mcontext, &d0)) + goto badframe; + + if (do_sigaltstack(&frame->uc.uc_stack, NULL, current_frame()->sp) == + -EFAULT) + goto badframe; + + return d0; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +/* + * store the userspace context into a signal frame + */ +static int setup_sigcontext(struct sigcontext __user *sc, + struct fpucontext *fpuctx, + struct pt_regs *regs, + unsigned long mask) +{ + int tmp, err = 0; + +#define COPY(x) err |= __put_user(regs->x, &sc->x) + COPY(d0); COPY(d1); COPY(d2); COPY(d3); + COPY(a0); COPY(a1); COPY(a2); COPY(a3); + COPY(e0); COPY(e1); COPY(e2); COPY(e3); + COPY(e4); COPY(e5); COPY(e6); COPY(e7); + COPY(lar); COPY(lir); + COPY(mdr); COPY(mdrq); + COPY(mcvf); COPY(mcrl); COPY(mcrh); + COPY(sp); COPY(epsw); COPY(pc); +#undef COPY + + tmp = fpu_setup_sigcontext(fpuctx); + if (tmp < 0) + err = 1; + else + err |= __put_user(tmp ? fpuctx : NULL, &sc->fpucontext); + + /* non-iBCS2 extensions.. */ + err |= __put_user(mask, &sc->oldmask); + + return err; +} + +/* + * determine which stack to use.. + */ +static inline void __user *get_sigframe(struct k_sigaction *ka, + struct pt_regs *regs, + size_t frame_size) +{ + unsigned long sp; + + /* default to using normal stack */ + sp = regs->sp; + + /* this is the X/Open sanctioned signal stack switching. */ + if (ka->sa.sa_flags & SA_ONSTACK) { + if (sas_ss_flags(sp) == 0) + sp = current->sas_ss_sp + current->sas_ss_size; + } + + return (void __user *) ((sp - frame_size) & ~7UL); +} + +/* + * set up a normal signal frame + */ +static int setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, + struct pt_regs *regs) +{ + struct sigframe __user *frame; + int rsig; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + rsig = sig; + if (sig < 32 && + current_thread_info()->exec_domain && + current_thread_info()->exec_domain->signal_invmap) + rsig = current_thread_info()->exec_domain->signal_invmap[sig]; + + if (__put_user(rsig, &frame->sig) < 0 || + __put_user(&frame->sc, &frame->psc) < 0) + goto give_sigsegv; + + if (setup_sigcontext(&frame->sc, &frame->fpuctx, regs, set->sig[0])) + goto give_sigsegv; + + if (_NSIG_WORDS > 1) { + if (__copy_to_user(frame->extramask, &set->sig[1], + sizeof(frame->extramask))) + goto give_sigsegv; + } + + /* set up to return from userspace. If provided, use a stub already in + * userspace */ + if (ka->sa.sa_flags & SA_RESTORER) { + if (__put_user(ka->sa.sa_restorer, &frame->pretcode)) + goto give_sigsegv; + } else { + if (__put_user((void (*)(void))frame->retcode, + &frame->pretcode)) + goto give_sigsegv; + /* this is mov $,d0; syscall 0 */ + if (__put_user(0x2c, (char *)(frame->retcode + 0)) || + __put_user(__NR_sigreturn, (char *)(frame->retcode + 1)) || + __put_user(0x00, (char *)(frame->retcode + 2)) || + __put_user(0xf0, (char *)(frame->retcode + 3)) || + __put_user(0xe0, (char *)(frame->retcode + 4))) + goto give_sigsegv; + flush_icache_range((unsigned long) frame->retcode, + (unsigned long) frame->retcode + 5); + } + + /* set up registers for signal handler */ + regs->sp = (unsigned long) frame; + regs->pc = (unsigned long) ka->sa.sa_handler; + regs->d0 = sig; + regs->d1 = (unsigned long) &frame->sc; + + /* the tracer may want to single-step inside the handler */ + if (test_thread_flag(TIF_SINGLESTEP)) + ptrace_notify(SIGTRAP); + +#if DEBUG_SIG + printk(KERN_DEBUG "SIG deliver %d (%s:%d): sp=%p pc=%lx ra=%p\n", + sig, current->comm, current->pid, frame, regs->pc, + frame->pretcode); +#endif + + return 0; + +give_sigsegv: + force_sigsegv(sig, current); + return -EFAULT; +} + +/* + * set up a realtime signal frame + */ +static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs *regs) +{ + struct rt_sigframe __user *frame; + int rsig; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + rsig = sig; + if (sig < 32 && + current_thread_info()->exec_domain && + current_thread_info()->exec_domain->signal_invmap) + rsig = current_thread_info()->exec_domain->signal_invmap[sig]; + + if (__put_user(rsig, &frame->sig) || + __put_user(&frame->info, &frame->pinfo) || + __put_user(&frame->uc, &frame->puc) || + copy_siginfo_to_user(&frame->info, info)) + goto give_sigsegv; + + /* create the ucontext. */ + if (__put_user(0, &frame->uc.uc_flags) || + __put_user(0, &frame->uc.uc_link) || + __put_user((void *)current->sas_ss_sp, &frame->uc.uc_stack.ss_sp) || + __put_user(sas_ss_flags(regs->sp), &frame->uc.uc_stack.ss_flags) || + __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size) || + setup_sigcontext(&frame->uc.uc_mcontext, + &frame->fpuctx, regs, set->sig[0]) || + __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set))) + goto give_sigsegv; + + /* set up to return from userspace. If provided, use a stub already in + * userspace */ + if (ka->sa.sa_flags & SA_RESTORER) { + if (__put_user(ka->sa.sa_restorer, &frame->pretcode)) + goto give_sigsegv; + } else { + if (__put_user((void(*)(void))frame->retcode, + &frame->pretcode) || + /* This is mov $,d0; syscall 0 */ + __put_user(0x2c, (char *)(frame->retcode + 0)) || + __put_user(__NR_rt_sigreturn, + (char *)(frame->retcode + 1)) || + __put_user(0x00, (char *)(frame->retcode + 2)) || + __put_user(0xf0, (char *)(frame->retcode + 3)) || + __put_user(0xe0, (char *)(frame->retcode + 4))) + goto give_sigsegv; + + flush_icache_range((u_long) frame->retcode, + (u_long) frame->retcode + 5); + } + + /* Set up registers for signal handler */ + regs->sp = (unsigned long) frame; + regs->pc = (unsigned long) ka->sa.sa_handler; + regs->d0 = sig; + regs->d1 = (long) &frame->info; + + /* the tracer may want to single-step inside the handler */ + if (test_thread_flag(TIF_SINGLESTEP)) + ptrace_notify(SIGTRAP); + +#if DEBUG_SIG + printk(KERN_DEBUG "SIG deliver %d (%s:%d): sp=%p pc=%lx ra=%p\n", + sig, current->comm, current->pid, frame, regs->pc, + frame->pretcode); +#endif + + return 0; + +give_sigsegv: + force_sigsegv(sig, current); + return -EFAULT; +} + +static inline void stepback(struct pt_regs *regs) +{ + regs->pc -= 2; + regs->orig_d0 = -1; +} + +/* + * handle the actual delivery of a signal to userspace + */ +static int handle_signal(int sig, + siginfo_t *info, struct k_sigaction *ka, + sigset_t *oldset, struct pt_regs *regs) +{ + int ret; + + /* Are we from a system call? */ + if (regs->orig_d0 >= 0) { + /* If so, check system call restarting.. */ + switch (regs->d0) { + case -ERESTART_RESTARTBLOCK: + case -ERESTARTNOHAND: + regs->d0 = -EINTR; + break; + + case -ERESTARTSYS: + if (!(ka->sa.sa_flags & SA_RESTART)) { + regs->d0 = -EINTR; + break; + } + + /* fallthrough */ + case -ERESTARTNOINTR: + regs->d0 = regs->orig_d0; + stepback(regs); + } + } + + /* Set up the stack frame */ + if (ka->sa.sa_flags & SA_SIGINFO) + ret = setup_rt_frame(sig, ka, info, oldset, regs); + else + ret = setup_frame(sig, ka, oldset, regs); + + if (ret == 0) { + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked, ¤t->blocked, + &ka->sa.sa_mask); + if (!(ka->sa.sa_flags & SA_NODEFER)) + sigaddset(¤t->blocked, sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } + + return ret; +} + +/* + * handle a potential signal + */ +static void do_signal(struct pt_regs *regs) +{ + struct k_sigaction ka; + siginfo_t info; + sigset_t *oldset; + int signr; + + /* we want the common case to go fast, which is why we may in certain + * cases get here from kernel mode */ + if (!user_mode(regs)) + return; + + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + oldset = ¤t->saved_sigmask; + else + oldset = ¤t->blocked; + + signr = get_signal_to_deliver(&info, &ka, regs, NULL); + if (signr > 0) { + if (handle_signal(signr, &info, &ka, oldset, regs) == 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); + + tracehook_signal_handler(signr, &info, &ka, regs, + test_thread_flag(TIF_SINGLESTEP)); + } + + return; + } + + /* did we come from a system call? */ + if (regs->orig_d0 >= 0) { + /* restart the system call - no handlers present */ + switch (regs->d0) { + case -ERESTARTNOHAND: + case -ERESTARTSYS: + case -ERESTARTNOINTR: + regs->d0 = regs->orig_d0; + stepback(regs); + break; + + case -ERESTART_RESTARTBLOCK: + regs->d0 = __NR_restart_syscall; + stepback(regs); + break; + } + } + + /* 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); + } +} + +/* + * notification of userspace execution resumption + * - triggered by current->work.notify_resume + */ +asmlinkage void do_notify_resume(struct pt_regs *regs, u32 thread_info_flags) +{ + /* Pending single-step? */ + if (thread_info_flags & _TIF_SINGLESTEP) { +#ifndef CONFIG_MN10300_USING_JTAG + regs->epsw |= EPSW_T; + clear_thread_flag(TIF_SINGLESTEP); +#else + BUG(); /* no h/w single-step if using JTAG unit */ +#endif + } + + /* deal with pending signal delivery */ + if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK)) + do_signal(regs); + + if (thread_info_flags & _TIF_NOTIFY_RESUME) { + clear_thread_flag(TIF_NOTIFY_RESUME); + tracehook_notify_resume(current_frame()); + if (current->replacement_session_keyring) + key_replace_session_keyring(); + } +} diff --git a/arch/mn10300/kernel/smp-low.S b/arch/mn10300/kernel/smp-low.S new file mode 100644 index 00000000..72938cef --- /dev/null +++ b/arch/mn10300/kernel/smp-low.S @@ -0,0 +1,97 @@ +/* SMP IPI low-level handler + * + * Copyright (C) 2006-2007 Matsushita Electric Industrial Co., Ltd. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/system.h> +#include <asm/thread_info.h> +#include <asm/cpu-regs.h> +#include <proc/smp-regs.h> +#include <asm/asm-offsets.h> +#include <asm/frame.inc> + + .am33_2 + +############################################################################### +# +# IPI interrupt handler +# +############################################################################### + .globl mn10300_low_ipi_handler +mn10300_low_ipi_handler: + add -4,sp + mov d0,(sp) + movhu (IAGR),d0 + and IAGR_GN,d0 + lsr 0x2,d0 +#ifdef CONFIG_MN10300_CACHE_ENABLED + cmp FLUSH_CACHE_IPI,d0 + beq mn10300_flush_cache_ipi +#endif + cmp SMP_BOOT_IRQ,d0 + beq mn10300_smp_boot_ipi + /* OTHERS */ + mov (sp),d0 + add 4,sp +#ifdef CONFIG_GDBSTUB + jmp gdbstub_io_rx_handler +#else + jmp end +#endif + +############################################################################### +# +# Cache flush IPI interrupt handler +# +############################################################################### +#ifdef CONFIG_MN10300_CACHE_ENABLED +mn10300_flush_cache_ipi: + mov (sp),d0 + add 4,sp + + /* FLUSH_CACHE_IPI */ + add -4,sp + SAVE_ALL + mov GxICR_DETECT,d2 + movbu d2,(GxICR(FLUSH_CACHE_IPI)) # ACK the interrupt + movhu (GxICR(FLUSH_CACHE_IPI)),d2 + call smp_cache_interrupt[],0 + RESTORE_ALL + jmp end +#endif + +############################################################################### +# +# SMP boot CPU IPI interrupt handler +# +############################################################################### +mn10300_smp_boot_ipi: + /* clear interrupt */ + movhu (GxICR(SMP_BOOT_IRQ)),d0 + and ~GxICR_REQUEST,d0 + movhu d0,(GxICR(SMP_BOOT_IRQ)) + mov (sp),d0 + add 4,sp + + # get stack + mov (CPUID),a0 + add -1,a0 + add a0,a0 + add a0,a0 + mov (start_stack,a0),a0 + mov a0,sp + jmp initialize_secondary + + +# Jump here after RTI to suppress the icache lookahead +end: diff --git a/arch/mn10300/kernel/smp.c b/arch/mn10300/kernel/smp.c new file mode 100644 index 00000000..9242e9fc --- /dev/null +++ b/arch/mn10300/kernel/smp.c @@ -0,0 +1,1182 @@ +/* SMP support routines. + * + * Copyright (C) 2006-2008 Panasonic Corporation + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/init.h> +#include <linux/jiffies.h> +#include <linux/cpumask.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/profile.h> +#include <linux/smp.h> +#include <asm/tlbflush.h> +#include <asm/system.h> +#include <asm/bitops.h> +#include <asm/processor.h> +#include <asm/bug.h> +#include <asm/exceptions.h> +#include <asm/hardirq.h> +#include <asm/fpu.h> +#include <asm/mmu_context.h> +#include <asm/thread_info.h> +#include <asm/cpu-regs.h> +#include <asm/intctl-regs.h> +#include "internal.h" + +#ifdef CONFIG_HOTPLUG_CPU +#include <linux/cpu.h> +#include <asm/cacheflush.h> + +static unsigned long sleep_mode[NR_CPUS]; + +static void run_sleep_cpu(unsigned int cpu); +static void run_wakeup_cpu(unsigned int cpu); +#endif /* CONFIG_HOTPLUG_CPU */ + +/* + * Debug Message function + */ + +#undef DEBUG_SMP +#ifdef DEBUG_SMP +#define Dprintk(fmt, ...) printk(KERN_DEBUG fmt, ##__VA_ARGS__) +#else +#define Dprintk(fmt, ...) no_printk(KERN_DEBUG fmt, ##__VA_ARGS__) +#endif + +/* timeout value in msec for smp_nmi_call_function. zero is no timeout. */ +#define CALL_FUNCTION_NMI_IPI_TIMEOUT 0 + +/* + * Structure and data for smp_nmi_call_function(). + */ +struct nmi_call_data_struct { + smp_call_func_t func; + void *info; + cpumask_t started; + cpumask_t finished; + int wait; + char size_alignment[0] + __attribute__ ((__aligned__(SMP_CACHE_BYTES))); +} __attribute__ ((__aligned__(SMP_CACHE_BYTES))); + +static DEFINE_SPINLOCK(smp_nmi_call_lock); +static struct nmi_call_data_struct *nmi_call_data; + +/* + * Data structures and variables + */ +static cpumask_t cpu_callin_map; /* Bitmask of callin CPUs */ +static cpumask_t cpu_callout_map; /* Bitmask of callout CPUs */ +cpumask_t cpu_boot_map; /* Bitmask of boot APs */ +unsigned long start_stack[NR_CPUS - 1]; + +/* + * Per CPU parameters + */ +struct mn10300_cpuinfo cpu_data[NR_CPUS] __cacheline_aligned; + +static int cpucount; /* The count of boot CPUs */ +static cpumask_t smp_commenced_mask; +cpumask_t cpu_initialized __initdata = CPU_MASK_NONE; + +/* + * Function Prototypes + */ +static int do_boot_cpu(int); +static void smp_show_cpu_info(int cpu_id); +static void smp_callin(void); +static void smp_online(void); +static void smp_store_cpu_info(int); +static void smp_cpu_init(void); +static void smp_tune_scheduling(void); +static void send_IPI_mask(const cpumask_t *cpumask, int irq); +static void init_ipi(void); + +/* + * IPI Initialization interrupt definitions + */ +static void mn10300_ipi_disable(unsigned int irq); +static void mn10300_ipi_enable(unsigned int irq); +static void mn10300_ipi_chip_disable(struct irq_data *d); +static void mn10300_ipi_chip_enable(struct irq_data *d); +static void mn10300_ipi_ack(struct irq_data *d); +static void mn10300_ipi_nop(struct irq_data *d); + +static struct irq_chip mn10300_ipi_type = { + .name = "cpu_ipi", + .irq_disable = mn10300_ipi_chip_disable, + .irq_enable = mn10300_ipi_chip_enable, + .irq_ack = mn10300_ipi_ack, + .irq_eoi = mn10300_ipi_nop +}; + +static irqreturn_t smp_reschedule_interrupt(int irq, void *dev_id); +static irqreturn_t smp_call_function_interrupt(int irq, void *dev_id); + +static struct irqaction reschedule_ipi = { + .handler = smp_reschedule_interrupt, + .name = "smp reschedule IPI" +}; +static struct irqaction call_function_ipi = { + .handler = smp_call_function_interrupt, + .name = "smp call function IPI" +}; + +#if !defined(CONFIG_GENERIC_CLOCKEVENTS) || defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) +static irqreturn_t smp_ipi_timer_interrupt(int irq, void *dev_id); +static struct irqaction local_timer_ipi = { + .handler = smp_ipi_timer_interrupt, + .flags = IRQF_DISABLED, + .name = "smp local timer IPI" +}; +#endif + +/** + * init_ipi - Initialise the IPI mechanism + */ +static void init_ipi(void) +{ + unsigned long flags; + u16 tmp16; + + /* set up the reschedule IPI */ + irq_set_chip_and_handler(RESCHEDULE_IPI, &mn10300_ipi_type, + handle_percpu_irq); + setup_irq(RESCHEDULE_IPI, &reschedule_ipi); + set_intr_level(RESCHEDULE_IPI, RESCHEDULE_GxICR_LV); + mn10300_ipi_enable(RESCHEDULE_IPI); + + /* set up the call function IPI */ + irq_set_chip_and_handler(CALL_FUNC_SINGLE_IPI, &mn10300_ipi_type, + handle_percpu_irq); + setup_irq(CALL_FUNC_SINGLE_IPI, &call_function_ipi); + set_intr_level(CALL_FUNC_SINGLE_IPI, CALL_FUNCTION_GxICR_LV); + mn10300_ipi_enable(CALL_FUNC_SINGLE_IPI); + + /* set up the local timer IPI */ +#if !defined(CONFIG_GENERIC_CLOCKEVENTS) || \ + defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) + irq_set_chip_and_handler(LOCAL_TIMER_IPI, &mn10300_ipi_type, + handle_percpu_irq); + setup_irq(LOCAL_TIMER_IPI, &local_timer_ipi); + set_intr_level(LOCAL_TIMER_IPI, LOCAL_TIMER_GxICR_LV); + mn10300_ipi_enable(LOCAL_TIMER_IPI); +#endif + +#ifdef CONFIG_MN10300_CACHE_ENABLED + /* set up the cache flush IPI */ + flags = arch_local_cli_save(); + __set_intr_stub(NUM2EXCEP_IRQ_LEVEL(FLUSH_CACHE_GxICR_LV), + mn10300_low_ipi_handler); + GxICR(FLUSH_CACHE_IPI) = FLUSH_CACHE_GxICR_LV | GxICR_DETECT; + mn10300_ipi_enable(FLUSH_CACHE_IPI); + arch_local_irq_restore(flags); +#endif + + /* set up the NMI call function IPI */ + flags = arch_local_cli_save(); + GxICR(CALL_FUNCTION_NMI_IPI) = GxICR_NMI | GxICR_ENABLE | GxICR_DETECT; + tmp16 = GxICR(CALL_FUNCTION_NMI_IPI); + arch_local_irq_restore(flags); + + /* set up the SMP boot IPI */ + flags = arch_local_cli_save(); + __set_intr_stub(NUM2EXCEP_IRQ_LEVEL(SMP_BOOT_GxICR_LV), + mn10300_low_ipi_handler); + arch_local_irq_restore(flags); +} + +/** + * mn10300_ipi_shutdown - Shut down handling of an IPI + * @irq: The IPI to be shut down. + */ +static void mn10300_ipi_shutdown(unsigned int irq) +{ + unsigned long flags; + u16 tmp; + + flags = arch_local_cli_save(); + + tmp = GxICR(irq); + GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_DETECT; + tmp = GxICR(irq); + + arch_local_irq_restore(flags); +} + +/** + * mn10300_ipi_enable - Enable an IPI + * @irq: The IPI to be enabled. + */ +static void mn10300_ipi_enable(unsigned int irq) +{ + unsigned long flags; + u16 tmp; + + flags = arch_local_cli_save(); + + tmp = GxICR(irq); + GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_ENABLE; + tmp = GxICR(irq); + + arch_local_irq_restore(flags); +} + +static void mn10300_ipi_chip_enable(struct irq_data *d) +{ + mn10300_ipi_enable(d->irq); +} + +/** + * mn10300_ipi_disable - Disable an IPI + * @irq: The IPI to be disabled. + */ +static void mn10300_ipi_disable(unsigned int irq) +{ + unsigned long flags; + u16 tmp; + + flags = arch_local_cli_save(); + + tmp = GxICR(irq); + GxICR(irq) = tmp & GxICR_LEVEL; + tmp = GxICR(irq); + + arch_local_irq_restore(flags); +} + +static void mn10300_ipi_chip_disable(struct irq_data *d) +{ + mn10300_ipi_disable(d->irq); +} + + +/** + * mn10300_ipi_ack - Acknowledge an IPI interrupt in the PIC + * @irq: The IPI to be acknowledged. + * + * Clear the interrupt detection flag for the IPI on the appropriate interrupt + * channel in the PIC. + */ +static void mn10300_ipi_ack(struct irq_data *d) +{ + unsigned int irq = d->irq; + unsigned long flags; + u16 tmp; + + flags = arch_local_cli_save(); + GxICR_u8(irq) = GxICR_DETECT; + tmp = GxICR(irq); + arch_local_irq_restore(flags); +} + +/** + * mn10300_ipi_nop - Dummy IPI action + * @irq: The IPI to be acted upon. + */ +static void mn10300_ipi_nop(struct irq_data *d) +{ +} + +/** + * send_IPI_mask - Send IPIs to all CPUs in list + * @cpumask: The list of CPUs to target. + * @irq: The IPI request to be sent. + * + * Send the specified IPI to all the CPUs in the list, not waiting for them to + * finish before returning. The caller is responsible for synchronisation if + * that is needed. + */ +static void send_IPI_mask(const cpumask_t *cpumask, int irq) +{ + int i; + u16 tmp; + + for (i = 0; i < NR_CPUS; i++) { + if (cpumask_test_cpu(i, cpumask)) { + /* send IPI */ + tmp = CROSS_GxICR(irq, i); + CROSS_GxICR(irq, i) = + tmp | GxICR_REQUEST | GxICR_DETECT; + tmp = CROSS_GxICR(irq, i); /* flush write buffer */ + } + } +} + +/** + * send_IPI_self - Send an IPI to this CPU. + * @irq: The IPI request to be sent. + * + * Send the specified IPI to the current CPU. + */ +void send_IPI_self(int irq) +{ + send_IPI_mask(cpumask_of(smp_processor_id()), irq); +} + +/** + * send_IPI_allbutself - Send IPIs to all the other CPUs. + * @irq: The IPI request to be sent. + * + * Send the specified IPI to all CPUs in the system barring the current one, + * not waiting for them to finish before returning. The caller is responsible + * for synchronisation if that is needed. + */ +void send_IPI_allbutself(int irq) +{ + cpumask_t cpumask; + + cpumask_copy(&cpumask, cpu_online_mask); + cpumask_clear_cpu(smp_processor_id(), &cpumask); + send_IPI_mask(&cpumask, irq); +} + +void arch_send_call_function_ipi_mask(const struct cpumask *mask) +{ + BUG(); + /*send_IPI_mask(mask, CALL_FUNCTION_IPI);*/ +} + +void arch_send_call_function_single_ipi(int cpu) +{ + send_IPI_mask(cpumask_of(cpu), CALL_FUNC_SINGLE_IPI); +} + +/** + * smp_send_reschedule - Send reschedule IPI to a CPU + * @cpu: The CPU to target. + */ +void smp_send_reschedule(int cpu) +{ + send_IPI_mask(cpumask_of(cpu), RESCHEDULE_IPI); +} + +/** + * smp_nmi_call_function - Send a call function NMI IPI to all CPUs + * @func: The function to ask to be run. + * @info: The context data to pass to that function. + * @wait: If true, wait (atomically) until function is run on all CPUs. + * + * Send a non-maskable request to all CPUs in the system, requesting them to + * run the specified function with the given context data, and, potentially, to + * wait for completion of that function on all CPUs. + * + * Returns 0 if successful, -ETIMEDOUT if we were asked to wait, but hit the + * timeout. + */ +int smp_nmi_call_function(smp_call_func_t func, void *info, int wait) +{ + struct nmi_call_data_struct data; + unsigned long flags; + unsigned int cnt; + int cpus, ret = 0; + + cpus = num_online_cpus() - 1; + if (cpus < 1) + return 0; + + data.func = func; + data.info = info; + cpumask_copy(&data.started, cpu_online_mask); + cpumask_clear_cpu(smp_processor_id(), &data.started); + data.wait = wait; + if (wait) + data.finished = data.started; + + spin_lock_irqsave(&smp_nmi_call_lock, flags); + nmi_call_data = &data; + smp_mb(); + + /* Send a message to all other CPUs and wait for them to respond */ + send_IPI_allbutself(CALL_FUNCTION_NMI_IPI); + + /* Wait for response */ + if (CALL_FUNCTION_NMI_IPI_TIMEOUT > 0) { + for (cnt = 0; + cnt < CALL_FUNCTION_NMI_IPI_TIMEOUT && + !cpumask_empty(&data.started); + cnt++) + mdelay(1); + + if (wait && cnt < CALL_FUNCTION_NMI_IPI_TIMEOUT) { + for (cnt = 0; + cnt < CALL_FUNCTION_NMI_IPI_TIMEOUT && + !cpumask_empty(&data.finished); + cnt++) + mdelay(1); + } + + if (cnt >= CALL_FUNCTION_NMI_IPI_TIMEOUT) + ret = -ETIMEDOUT; + + } else { + /* If timeout value is zero, wait until cpumask has been + * cleared */ + while (!cpumask_empty(&data.started)) + barrier(); + if (wait) + while (!cpumask_empty(&data.finished)) + barrier(); + } + + spin_unlock_irqrestore(&smp_nmi_call_lock, flags); + return ret; +} + +/** + * smp_jump_to_debugger - Make other CPUs enter the debugger by sending an IPI + * + * Send a non-maskable request to all other CPUs in the system, instructing + * them to jump into the debugger. The caller is responsible for checking that + * the other CPUs responded to the instruction. + * + * The caller should make sure that this CPU's debugger IPI is disabled. + */ +void smp_jump_to_debugger(void) +{ + if (num_online_cpus() > 1) + /* Send a message to all other CPUs */ + send_IPI_allbutself(DEBUGGER_NMI_IPI); +} + +/** + * stop_this_cpu - Callback to stop a CPU. + * @unused: Callback context (ignored). + */ +void stop_this_cpu(void *unused) +{ + static volatile int stopflag; + unsigned long flags; + +#ifdef CONFIG_GDBSTUB + /* In case of single stepping smp_send_stop by other CPU, + * clear procindebug to avoid deadlock. + */ + atomic_set(&procindebug[smp_processor_id()], 0); +#endif /* CONFIG_GDBSTUB */ + + flags = arch_local_cli_save(); + set_cpu_online(smp_processor_id(), false); + + while (!stopflag) + cpu_relax(); + + set_cpu_online(smp_processor_id(), true); + arch_local_irq_restore(flags); +} + +/** + * smp_send_stop - Send a stop request to all CPUs. + */ +void smp_send_stop(void) +{ + smp_nmi_call_function(stop_this_cpu, NULL, 0); +} + +/** + * smp_reschedule_interrupt - Reschedule IPI handler + * @irq: The interrupt number. + * @dev_id: The device ID. + * + * Returns IRQ_HANDLED to indicate we handled the interrupt successfully. + */ +static irqreturn_t smp_reschedule_interrupt(int irq, void *dev_id) +{ + scheduler_ipi(); + return IRQ_HANDLED; +} + +/** + * smp_call_function_interrupt - Call function IPI handler + * @irq: The interrupt number. + * @dev_id: The device ID. + * + * Returns IRQ_HANDLED to indicate we handled the interrupt successfully. + */ +static irqreturn_t smp_call_function_interrupt(int irq, void *dev_id) +{ + /* generic_smp_call_function_interrupt(); */ + generic_smp_call_function_single_interrupt(); + return IRQ_HANDLED; +} + +/** + * smp_nmi_call_function_interrupt - Non-maskable call function IPI handler + */ +void smp_nmi_call_function_interrupt(void) +{ + smp_call_func_t func = nmi_call_data->func; + void *info = nmi_call_data->info; + int wait = nmi_call_data->wait; + + /* Notify the initiating CPU that I've grabbed the data and am about to + * execute the function + */ + smp_mb(); + cpumask_clear_cpu(smp_processor_id(), &nmi_call_data->started); + (*func)(info); + + if (wait) { + smp_mb(); + cpumask_clear_cpu(smp_processor_id(), + &nmi_call_data->finished); + } +} + +#if !defined(CONFIG_GENERIC_CLOCKEVENTS) || \ + defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) +/** + * smp_ipi_timer_interrupt - Local timer IPI handler + * @irq: The interrupt number. + * @dev_id: The device ID. + * + * Returns IRQ_HANDLED to indicate we handled the interrupt successfully. + */ +static irqreturn_t smp_ipi_timer_interrupt(int irq, void *dev_id) +{ + return local_timer_interrupt(); +} +#endif + +void __init smp_init_cpus(void) +{ + int i; + for (i = 0; i < NR_CPUS; i++) { + set_cpu_possible(i, true); + set_cpu_present(i, true); + } +} + +/** + * smp_cpu_init - Initialise AP in start_secondary. + * + * For this Application Processor, set up init_mm, initialise FPU and set + * interrupt level 0-6 setting. + */ +static void __init smp_cpu_init(void) +{ + unsigned long flags; + int cpu_id = smp_processor_id(); + u16 tmp16; + + if (test_and_set_bit(cpu_id, &cpu_initialized)) { + printk(KERN_WARNING "CPU#%d already initialized!\n", cpu_id); + for (;;) + local_irq_enable(); + } + printk(KERN_INFO "Initializing CPU#%d\n", cpu_id); + + atomic_inc(&init_mm.mm_count); + current->active_mm = &init_mm; + BUG_ON(current->mm); + + enter_lazy_tlb(&init_mm, current); + + /* Force FPU initialization */ + clear_using_fpu(current); + + GxICR(CALL_FUNC_SINGLE_IPI) = CALL_FUNCTION_GxICR_LV | GxICR_DETECT; + mn10300_ipi_enable(CALL_FUNC_SINGLE_IPI); + + GxICR(LOCAL_TIMER_IPI) = LOCAL_TIMER_GxICR_LV | GxICR_DETECT; + mn10300_ipi_enable(LOCAL_TIMER_IPI); + + GxICR(RESCHEDULE_IPI) = RESCHEDULE_GxICR_LV | GxICR_DETECT; + mn10300_ipi_enable(RESCHEDULE_IPI); + +#ifdef CONFIG_MN10300_CACHE_ENABLED + GxICR(FLUSH_CACHE_IPI) = FLUSH_CACHE_GxICR_LV | GxICR_DETECT; + mn10300_ipi_enable(FLUSH_CACHE_IPI); +#endif + + mn10300_ipi_shutdown(SMP_BOOT_IRQ); + + /* Set up the non-maskable call function IPI */ + flags = arch_local_cli_save(); + GxICR(CALL_FUNCTION_NMI_IPI) = GxICR_NMI | GxICR_ENABLE | GxICR_DETECT; + tmp16 = GxICR(CALL_FUNCTION_NMI_IPI); + arch_local_irq_restore(flags); +} + +/** + * smp_prepare_cpu_init - Initialise CPU in startup_secondary + * + * Set interrupt level 0-6 setting and init ICR of the kernel debugger. + */ +void smp_prepare_cpu_init(void) +{ + int loop; + + /* Set the interrupt vector registers */ + IVAR0 = EXCEP_IRQ_LEVEL0; + IVAR1 = EXCEP_IRQ_LEVEL1; + IVAR2 = EXCEP_IRQ_LEVEL2; + IVAR3 = EXCEP_IRQ_LEVEL3; + IVAR4 = EXCEP_IRQ_LEVEL4; + IVAR5 = EXCEP_IRQ_LEVEL5; + IVAR6 = EXCEP_IRQ_LEVEL6; + + /* Disable all interrupts and set to priority 6 (lowest) */ + for (loop = 0; loop < GxICR_NUM_IRQS; loop++) + GxICR(loop) = GxICR_LEVEL_6 | GxICR_DETECT; + +#ifdef CONFIG_KERNEL_DEBUGGER + /* initialise the kernel debugger interrupt */ + do { + unsigned long flags; + u16 tmp16; + + flags = arch_local_cli_save(); + GxICR(DEBUGGER_NMI_IPI) = GxICR_NMI | GxICR_ENABLE | GxICR_DETECT; + tmp16 = GxICR(DEBUGGER_NMI_IPI); + arch_local_irq_restore(flags); + } while (0); +#endif +} + +/** + * start_secondary - Activate a secondary CPU (AP) + * @unused: Thread parameter (ignored). + */ +int __init start_secondary(void *unused) +{ + smp_cpu_init(); + smp_callin(); + while (!cpumask_test_cpu(smp_processor_id(), &smp_commenced_mask)) + cpu_relax(); + + local_flush_tlb(); + preempt_disable(); + smp_online(); + +#ifdef CONFIG_GENERIC_CLOCKEVENTS + init_clockevents(); +#endif + cpu_idle(); + return 0; +} + +/** + * smp_prepare_cpus - Boot up secondary CPUs (APs) + * @max_cpus: Maximum number of CPUs to boot. + * + * Call do_boot_cpu, and boot up APs. + */ +void __init smp_prepare_cpus(unsigned int max_cpus) +{ + int phy_id; + + /* Setup boot CPU information */ + smp_store_cpu_info(0); + smp_tune_scheduling(); + + init_ipi(); + + /* If SMP should be disabled, then finish */ + if (max_cpus == 0) { + printk(KERN_INFO "SMP mode deactivated.\n"); + goto smp_done; + } + + /* Boot secondary CPUs (for which phy_id > 0) */ + for (phy_id = 0; phy_id < NR_CPUS; phy_id++) { + /* Don't boot primary CPU */ + if (max_cpus <= cpucount + 1) + continue; + if (phy_id != 0) + do_boot_cpu(phy_id); + set_cpu_possible(phy_id, true); + smp_show_cpu_info(phy_id); + } + +smp_done: + Dprintk("Boot done.\n"); +} + +/** + * smp_store_cpu_info - Save a CPU's information + * @cpu: The CPU to save for. + * + * Save boot_cpu_data and jiffy for the specified CPU. + */ +static void __init smp_store_cpu_info(int cpu) +{ + struct mn10300_cpuinfo *ci = &cpu_data[cpu]; + + *ci = boot_cpu_data; + ci->loops_per_jiffy = loops_per_jiffy; + ci->type = CPUREV; +} + +/** + * smp_tune_scheduling - Set time slice value + * + * Nothing to do here. + */ +static void __init smp_tune_scheduling(void) +{ +} + +/** + * do_boot_cpu: Boot up one CPU + * @phy_id: Physical ID of CPU to boot. + * + * Send an IPI to a secondary CPU to boot it. Returns 0 on success, 1 + * otherwise. + */ +static int __init do_boot_cpu(int phy_id) +{ + struct task_struct *idle; + unsigned long send_status, callin_status; + int timeout, cpu_id; + + send_status = GxICR_REQUEST; + callin_status = 0; + timeout = 0; + cpu_id = phy_id; + + cpucount++; + + /* Create idle thread for this CPU */ + idle = fork_idle(cpu_id); + if (IS_ERR(idle)) + panic("Failed fork for CPU#%d.", cpu_id); + + idle->thread.pc = (unsigned long)start_secondary; + + printk(KERN_NOTICE "Booting CPU#%d\n", cpu_id); + start_stack[cpu_id - 1] = idle->thread.sp; + + task_thread_info(idle)->cpu = cpu_id; + + /* Send boot IPI to AP */ + send_IPI_mask(cpumask_of(phy_id), SMP_BOOT_IRQ); + + Dprintk("Waiting for send to finish...\n"); + + /* Wait for AP's IPI receive in 100[ms] */ + do { + udelay(1000); + send_status = + CROSS_GxICR(SMP_BOOT_IRQ, phy_id) & GxICR_REQUEST; + } while (send_status == GxICR_REQUEST && timeout++ < 100); + + Dprintk("Waiting for cpu_callin_map.\n"); + + if (send_status == 0) { + /* Allow AP to start initializing */ + cpumask_set_cpu(cpu_id, &cpu_callout_map); + + /* Wait for setting cpu_callin_map */ + timeout = 0; + do { + udelay(1000); + callin_status = cpumask_test_cpu(cpu_id, + &cpu_callin_map); + } while (callin_status == 0 && timeout++ < 5000); + + if (callin_status == 0) + Dprintk("Not responding.\n"); + } else { + printk(KERN_WARNING "IPI not delivered.\n"); + } + + if (send_status == GxICR_REQUEST || callin_status == 0) { + cpumask_clear_cpu(cpu_id, &cpu_callout_map); + cpumask_clear_cpu(cpu_id, &cpu_callin_map); + cpumask_clear_cpu(cpu_id, &cpu_initialized); + cpucount--; + return 1; + } + return 0; +} + +/** + * smp_show_cpu_info - Show SMP CPU information + * @cpu: The CPU of interest. + */ +static void __init smp_show_cpu_info(int cpu) +{ + struct mn10300_cpuinfo *ci = &cpu_data[cpu]; + + printk(KERN_INFO + "CPU#%d : ioclk speed: %lu.%02luMHz : bogomips : %lu.%02lu\n", + cpu, + MN10300_IOCLK / 1000000, + (MN10300_IOCLK / 10000) % 100, + ci->loops_per_jiffy / (500000 / HZ), + (ci->loops_per_jiffy / (5000 / HZ)) % 100); +} + +/** + * smp_callin - Set cpu_callin_map of the current CPU ID + */ +static void __init smp_callin(void) +{ + unsigned long timeout; + int cpu; + + cpu = smp_processor_id(); + timeout = jiffies + (2 * HZ); + + if (cpumask_test_cpu(cpu, &cpu_callin_map)) { + printk(KERN_ERR "CPU#%d already present.\n", cpu); + BUG(); + } + Dprintk("CPU#%d waiting for CALLOUT\n", cpu); + + /* Wait for AP startup 2s total */ + while (time_before(jiffies, timeout)) { + if (cpumask_test_cpu(cpu, &cpu_callout_map)) + break; + cpu_relax(); + } + + if (!time_before(jiffies, timeout)) { + printk(KERN_ERR + "BUG: CPU#%d started up but did not get a callout!\n", + cpu); + BUG(); + } + +#ifdef CONFIG_CALIBRATE_DELAY + calibrate_delay(); /* Get our bogomips */ +#endif + + /* Save our processor parameters */ + smp_store_cpu_info(cpu); + + /* Allow the boot processor to continue */ + cpumask_set_cpu(cpu, &cpu_callin_map); +} + +/** + * smp_online - Set cpu_online_mask + */ +static void __init smp_online(void) +{ + int cpu; + + cpu = smp_processor_id(); + + local_irq_enable(); + + set_cpu_online(cpu, true); + smp_wmb(); +} + +/** + * smp_cpus_done - + * @max_cpus: Maximum CPU count. + * + * Do nothing. + */ +void __init smp_cpus_done(unsigned int max_cpus) +{ +} + +/* + * smp_prepare_boot_cpu - Set up stuff for the boot processor. + * + * Set up the cpu_online_mask, cpu_callout_map and cpu_callin_map of the boot + * processor (CPU 0). + */ +void __devinit smp_prepare_boot_cpu(void) +{ + cpumask_set_cpu(0, &cpu_callout_map); + cpumask_set_cpu(0, &cpu_callin_map); + current_thread_info()->cpu = 0; +} + +/* + * initialize_secondary - Initialise a secondary CPU (Application Processor). + * + * Set SP register and jump to thread's PC address. + */ +void initialize_secondary(void) +{ + asm volatile ( + "mov %0,sp \n" + "jmp (%1) \n" + : + : "a"(current->thread.sp), "a"(current->thread.pc)); +} + +/** + * __cpu_up - Set smp_commenced_mask for the nominated CPU + * @cpu: The target CPU. + */ +int __devinit __cpu_up(unsigned int cpu) +{ + int timeout; + +#ifdef CONFIG_HOTPLUG_CPU + if (num_online_cpus() == 1) + disable_hlt(); + if (sleep_mode[cpu]) + run_wakeup_cpu(cpu); +#endif /* CONFIG_HOTPLUG_CPU */ + + cpumask_set_cpu(cpu, &smp_commenced_mask); + + /* Wait 5s total for a response */ + for (timeout = 0 ; timeout < 5000 ; timeout++) { + if (cpu_online(cpu)) + break; + udelay(1000); + } + + BUG_ON(!cpu_online(cpu)); + return 0; +} + +/** + * setup_profiling_timer - Set up the profiling timer + * @multiplier - The frequency multiplier to use + * + * The frequency of the profiling timer can be changed by writing a multiplier + * value into /proc/profile. + */ +int setup_profiling_timer(unsigned int multiplier) +{ + return -EINVAL; +} + +/* + * CPU hotplug routines + */ +#ifdef CONFIG_HOTPLUG_CPU + +static DEFINE_PER_CPU(struct cpu, cpu_devices); + +static int __init topology_init(void) +{ + int cpu, ret; + + for_each_cpu(cpu) { + ret = register_cpu(&per_cpu(cpu_devices, cpu), cpu, NULL); + if (ret) + printk(KERN_WARNING + "topology_init: register_cpu %d failed (%d)\n", + cpu, ret); + } + return 0; +} + +subsys_initcall(topology_init); + +int __cpu_disable(void) +{ + int cpu = smp_processor_id(); + if (cpu == 0) + return -EBUSY; + + migrate_irqs(); + cpumask_clear_cpu(cpu, &mm_cpumask(current->active_mm)); + return 0; +} + +void __cpu_die(unsigned int cpu) +{ + run_sleep_cpu(cpu); + + if (num_online_cpus() == 1) + enable_hlt(); +} + +#ifdef CONFIG_MN10300_CACHE_ENABLED +static inline void hotplug_cpu_disable_cache(void) +{ + int tmp; + asm volatile( + " movhu (%1),%0 \n" + " and %2,%0 \n" + " movhu %0,(%1) \n" + "1: movhu (%1),%0 \n" + " btst %3,%0 \n" + " bne 1b \n" + : "=&r"(tmp) + : "a"(&CHCTR), + "i"(~(CHCTR_ICEN | CHCTR_DCEN)), + "i"(CHCTR_ICBUSY | CHCTR_DCBUSY) + : "memory", "cc"); +} + +static inline void hotplug_cpu_enable_cache(void) +{ + int tmp; + asm volatile( + "movhu (%1),%0 \n" + "or %2,%0 \n" + "movhu %0,(%1) \n" + : "=&r"(tmp) + : "a"(&CHCTR), + "i"(CHCTR_ICEN | CHCTR_DCEN) + : "memory", "cc"); +} + +static inline void hotplug_cpu_invalidate_cache(void) +{ + int tmp; + asm volatile ( + "movhu (%1),%0 \n" + "or %2,%0 \n" + "movhu %0,(%1) \n" + : "=&r"(tmp) + : "a"(&CHCTR), + "i"(CHCTR_ICINV | CHCTR_DCINV) + : "cc"); +} + +#else /* CONFIG_MN10300_CACHE_ENABLED */ +#define hotplug_cpu_disable_cache() do {} while (0) +#define hotplug_cpu_enable_cache() do {} while (0) +#define hotplug_cpu_invalidate_cache() do {} while (0) +#endif /* CONFIG_MN10300_CACHE_ENABLED */ + +/** + * hotplug_cpu_nmi_call_function - Call a function on other CPUs for hotplug + * @cpumask: List of target CPUs. + * @func: The function to call on those CPUs. + * @info: The context data for the function to be called. + * @wait: Whether to wait for the calls to complete. + * + * Non-maskably call a function on another CPU for hotplug purposes. + * + * This function must be called with maskable interrupts disabled. + */ +static int hotplug_cpu_nmi_call_function(cpumask_t cpumask, + smp_call_func_t func, void *info, + int wait) +{ + /* + * The address and the size of nmi_call_func_mask_data + * need to be aligned on L1_CACHE_BYTES. + */ + static struct nmi_call_data_struct nmi_call_func_mask_data + __cacheline_aligned; + unsigned long start, end; + + start = (unsigned long)&nmi_call_func_mask_data; + end = start + sizeof(struct nmi_call_data_struct); + + nmi_call_func_mask_data.func = func; + nmi_call_func_mask_data.info = info; + nmi_call_func_mask_data.started = cpumask; + nmi_call_func_mask_data.wait = wait; + if (wait) + nmi_call_func_mask_data.finished = cpumask; + + spin_lock(&smp_nmi_call_lock); + nmi_call_data = &nmi_call_func_mask_data; + mn10300_local_dcache_flush_range(start, end); + smp_wmb(); + + send_IPI_mask(cpumask, CALL_FUNCTION_NMI_IPI); + + do { + mn10300_local_dcache_inv_range(start, end); + barrier(); + } while (!cpumask_empty(&nmi_call_func_mask_data.started)); + + if (wait) { + do { + mn10300_local_dcache_inv_range(start, end); + barrier(); + } while (!cpumask_empty(&nmi_call_func_mask_data.finished)); + } + + spin_unlock(&smp_nmi_call_lock); + return 0; +} + +static void restart_wakeup_cpu(void) +{ + unsigned int cpu = smp_processor_id(); + + cpumask_set_cpu(cpu, &cpu_callin_map); + local_flush_tlb(); + set_cpu_online(cpu, true); + smp_wmb(); +} + +static void prepare_sleep_cpu(void *unused) +{ + sleep_mode[smp_processor_id()] = 1; + smp_mb(); + mn10300_local_dcache_flush_inv(); + hotplug_cpu_disable_cache(); + hotplug_cpu_invalidate_cache(); +} + +/* when this function called, IE=0, NMID=0. */ +static void sleep_cpu(void *unused) +{ + unsigned int cpu_id = smp_processor_id(); + /* + * CALL_FUNCTION_NMI_IPI for wakeup_cpu() shall not be requested, + * before this cpu goes in SLEEP mode. + */ + do { + smp_mb(); + __sleep_cpu(); + } while (sleep_mode[cpu_id]); + restart_wakeup_cpu(); +} + +static void run_sleep_cpu(unsigned int cpu) +{ + unsigned long flags; + cpumask_t cpumask; + + cpumask_copy(&cpumask, &cpumask_of(cpu)); + flags = arch_local_cli_save(); + hotplug_cpu_nmi_call_function(cpumask, prepare_sleep_cpu, NULL, 1); + hotplug_cpu_nmi_call_function(cpumask, sleep_cpu, NULL, 0); + udelay(1); /* delay for the cpu to sleep. */ + arch_local_irq_restore(flags); +} + +static void wakeup_cpu(void) +{ + hotplug_cpu_invalidate_cache(); + hotplug_cpu_enable_cache(); + smp_mb(); + sleep_mode[smp_processor_id()] = 0; +} + +static void run_wakeup_cpu(unsigned int cpu) +{ + unsigned long flags; + + flags = arch_local_cli_save(); +#if NR_CPUS == 2 + mn10300_local_dcache_flush_inv(); +#else + /* + * Before waking up the cpu, + * all online cpus should stop and flush D-Cache for global data. + */ +#error not support NR_CPUS > 2, when CONFIG_HOTPLUG_CPU=y. +#endif + hotplug_cpu_nmi_call_function(cpumask_of(cpu), wakeup_cpu, NULL, 1); + arch_local_irq_restore(flags); +} + +#endif /* CONFIG_HOTPLUG_CPU */ diff --git a/arch/mn10300/kernel/switch_to.S b/arch/mn10300/kernel/switch_to.S new file mode 100644 index 00000000..de3e74fc --- /dev/null +++ b/arch/mn10300/kernel/switch_to.S @@ -0,0 +1,179 @@ +############################################################################### +# +# MN10300 Context switch operation +# +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/thread_info.h> +#include <asm/cpu-regs.h> +#ifdef CONFIG_SMP +#include <proc/smp-regs.h> +#endif /* CONFIG_SMP */ + + .text + +############################################################################### +# +# struct task_struct *__switch_to(struct thread_struct *prev, +# struct thread_struct *next, +# struct task_struct *prev_task) +# +############################################################################### +ENTRY(__switch_to) + movm [d2,d3,a2,a3,exreg1],(sp) + or EPSW_NMID,epsw + + mov (44,sp),d2 + + mov d0,a0 + mov d1,a1 + + # save prev context + mov __switch_back,d0 + mov sp,a2 + mov a2,(THREAD_SP,a0) + mov a3,(THREAD_A3,a0) + +#ifdef CONFIG_KGDB + btst 0xff,(kgdb_single_step) + bne __switch_to__lift_sstep_bp +__switch_to__continue: +#endif + mov d0,(THREAD_PC,a0) + + mov (THREAD_A3,a1),a3 + mov (THREAD_SP,a1),a2 + + # switch + mov a2,sp + + # load next context + GET_THREAD_INFO a2 + mov a2,(__current_ti) + mov (TI_task,a2),a2 + mov a2,(__current) +#ifdef CONFIG_MN10300_CURRENT_IN_E2 + mov a2,e2 +#endif + + mov (THREAD_PC,a1),a2 + mov d2,d0 # for ret_from_fork + mov d0,a0 # for __switch_to + + jmp (a2) + +__switch_back: + and ~EPSW_NMID,epsw + ret [d2,d3,a2,a3,exreg1],32 + +#ifdef CONFIG_KGDB +############################################################################### +# +# Lift the single-step breakpoints when the task being traced is switched out +# A0 = prev +# A1 = next +# +############################################################################### +__switch_to__lift_sstep_bp: + add -12,sp + mov a0,e4 + mov a1,e5 + + # Clear the single-step flag to prevent us coming this way until we get + # switched back in + bclr 0xff,(kgdb_single_step) + + # Remove first breakpoint + mov (kgdb_sstep_bp_addr),a2 + cmp 0,a2 + beq 1f + movbu (kgdb_sstep_bp),d0 + movbu d0,(a2) +#if defined(CONFIG_MN10300_CACHE_FLUSH_ICACHE) || defined(CONFIG_MN10300_CACHE_INV_ICACHE) + mov a2,d0 + mov a2,d1 + add 1,d1 + calls flush_icache_range +#endif +1: + + # Remove second breakpoint + mov (kgdb_sstep_bp_addr+4),a2 + cmp 0,a2 + beq 2f + movbu (kgdb_sstep_bp+1),d0 + movbu d0,(a2) +#if defined(CONFIG_MN10300_CACHE_FLUSH_ICACHE) || defined(CONFIG_MN10300_CACHE_INV_ICACHE) + mov a2,d0 + mov a2,d1 + add 1,d1 + calls flush_icache_range +#endif +2: + + # Change the resumption address and return + mov __switch_back__reinstall_sstep_bp,d0 + mov e4,a0 + mov e5,a1 + add 12,sp + bra __switch_to__continue + +############################################################################### +# +# Reinstall the single-step breakpoints when the task being traced is switched +# back in (A1 points to the new thread_struct). +# +############################################################################### +__switch_back__reinstall_sstep_bp: + add -12,sp + mov a0,e4 # save the return value + mov 0xff,d3 + + # Reinstall first breakpoint + mov (kgdb_sstep_bp_addr),a2 + cmp 0,a2 + beq 1f + movbu (a2),d0 + movbu d0,(kgdb_sstep_bp) + movbu d3,(a2) +#if defined(CONFIG_MN10300_CACHE_FLUSH_ICACHE) || defined(CONFIG_MN10300_CACHE_INV_ICACHE) + mov a2,d0 + mov a2,d1 + add 1,d1 + calls flush_icache_range +#endif +1: + + # Reinstall second breakpoint + mov (kgdb_sstep_bp_addr+4),a2 + cmp 0,a2 + beq 2f + movbu (a2),d0 + movbu d0,(kgdb_sstep_bp+1) + movbu d3,(a2) +#if defined(CONFIG_MN10300_CACHE_FLUSH_ICACHE) || defined(CONFIG_MN10300_CACHE_INV_ICACHE) + mov a2,d0 + mov a2,d1 + add 1,d1 + calls flush_icache_range +#endif +2: + + mov d3,(kgdb_single_step) + + # Restore the return value (the previous thread_struct pointer) + mov e4,a0 + mov a0,d0 + add 12,sp + bra __switch_back + +#endif /* CONFIG_KGDB */ diff --git a/arch/mn10300/kernel/sys_mn10300.c b/arch/mn10300/kernel/sys_mn10300.c new file mode 100644 index 00000000..815f1355 --- /dev/null +++ b/arch/mn10300/kernel/sys_mn10300.c @@ -0,0 +1,33 @@ +/* MN10300 Weird system calls + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/syscalls.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/sem.h> +#include <linux/msg.h> +#include <linux/shm.h> +#include <linux/stat.h> +#include <linux/mman.h> +#include <linux/file.h> +#include <linux/tty.h> + +#include <asm/uaccess.h> + +asmlinkage long old_mmap(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long offset) +{ + if (offset & ~PAGE_MASK) + return -EINVAL; + return sys_mmap_pgoff(addr, len, prot, flags, fd, offset >> PAGE_SHIFT); +} diff --git a/arch/mn10300/kernel/time.c b/arch/mn10300/kernel/time.c new file mode 100644 index 00000000..67c6416a --- /dev/null +++ b/arch/mn10300/kernel/time.c @@ -0,0 +1,124 @@ +/* MN10300 Low level time management + * + * Copyright (C) 2007-2008 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * - Derived from arch/i386/kernel/time.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/time.h> +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/profile.h> +#include <linux/cnt32_to_63.h> +#include <linux/clocksource.h> +#include <linux/clockchips.h> +#include <asm/irq.h> +#include <asm/div64.h> +#include <asm/processor.h> +#include <asm/intctl-regs.h> +#include <asm/rtc.h> +#include "internal.h" + +static unsigned long mn10300_last_tsc; /* time-stamp counter at last time + * interrupt occurred */ + +static unsigned long sched_clock_multiplier; + +/* + * scheduler clock - returns current time in nanosec units. + */ +unsigned long long sched_clock(void) +{ + union { + unsigned long long ll; + unsigned l[2]; + } tsc64, result; + unsigned long tmp; + unsigned product[3]; /* 96-bit intermediate value */ + + /* cnt32_to_63() is not safe with preemption */ + preempt_disable(); + + /* expand the tsc to 64-bits. + * - sched_clock() must be called once a minute or better or the + * following will go horribly wrong - see cnt32_to_63() + */ + tsc64.ll = cnt32_to_63(get_cycles()) & 0x7fffffffffffffffULL; + + preempt_enable(); + + /* scale the 64-bit TSC value to a nanosecond value via a 96-bit + * intermediate + */ + asm("mulu %2,%0,%3,%0 \n" /* LSW * mult -> 0:%3:%0 */ + "mulu %2,%1,%2,%1 \n" /* MSW * mult -> %2:%1:0 */ + "add %3,%1 \n" + "addc 0,%2 \n" /* result in %2:%1:%0 */ + : "=r"(product[0]), "=r"(product[1]), "=r"(product[2]), "=r"(tmp) + : "0"(tsc64.l[0]), "1"(tsc64.l[1]), "2"(sched_clock_multiplier) + : "cc"); + + result.l[0] = product[1] << 16 | product[0] >> 16; + result.l[1] = product[2] << 16 | product[1] >> 16; + + return result.ll; +} + +/* + * initialise the scheduler clock + */ +static void __init mn10300_sched_clock_init(void) +{ + sched_clock_multiplier = + __muldiv64u(NSEC_PER_SEC, 1 << 16, MN10300_TSCCLK); +} + +/** + * local_timer_interrupt - Local timer interrupt handler + * + * Handle local timer interrupts for this CPU. They may have been propagated + * to this CPU from the CPU that actually gets them by way of an IPI. + */ +irqreturn_t local_timer_interrupt(void) +{ + profile_tick(CPU_PROFILING); + update_process_times(user_mode(get_irq_regs())); + return IRQ_HANDLED; +} + +/* + * initialise the various timers used by the main part of the kernel + */ +void __init time_init(void) +{ + /* we need the prescalar running to be able to use IOCLK/8 + * - IOCLK runs at 1/4 (ST5 open) or 1/8 (ST5 closed) internal CPU clock + * - IOCLK runs at Fosc rate (crystal speed) + */ + TMPSCNT |= TMPSCNT_ENABLE; + + init_clocksource(); + + printk(KERN_INFO + "timestamp counter I/O clock running at %lu.%02lu" + " (calibrated against RTC)\n", + MN10300_TSCCLK / 1000000, (MN10300_TSCCLK / 10000) % 100); + + mn10300_last_tsc = read_timestamp_counter(); + + init_clockevents(); + +#ifdef CONFIG_MN10300_WD_TIMER + /* start the watchdog timer */ + watchdog_go(); +#endif + + mn10300_sched_clock_init(); +} diff --git a/arch/mn10300/kernel/traps.c b/arch/mn10300/kernel/traps.c new file mode 100644 index 00000000..bd3e5e73 --- /dev/null +++ b/arch/mn10300/kernel/traps.c @@ -0,0 +1,627 @@ +/* MN10300 Exception handling + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Modified by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/timer.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/kallsyms.h> +#include <linux/pci.h> +#include <linux/kdebug.h> +#include <linux/bug.h> +#include <linux/irq.h> +#include <asm/processor.h> +#include <asm/system.h> +#include <linux/uaccess.h> +#include <asm/io.h> +#include <asm/atomic.h> +#include <asm/smp.h> +#include <asm/pgalloc.h> +#include <asm/cacheflush.h> +#include <asm/cpu-regs.h> +#include <asm/busctl-regs.h> +#include <unit/leds.h> +#include <asm/fpu.h> +#include <asm/sections.h> +#include <asm/debugger.h> +#include "internal.h" + +#if (CONFIG_INTERRUPT_VECTOR_BASE & 0xffffff) +#error "INTERRUPT_VECTOR_BASE not aligned to 16MiB boundary!" +#endif + +int kstack_depth_to_print = 24; + +spinlock_t die_lock = __SPIN_LOCK_UNLOCKED(die_lock); + +struct exception_to_signal_map { + u8 signo; + u32 si_code; +}; + +static const struct exception_to_signal_map exception_to_signal_map[256] = { + /* MMU exceptions */ + [EXCEP_ITLBMISS >> 3] = { 0, 0 }, + [EXCEP_DTLBMISS >> 3] = { 0, 0 }, + [EXCEP_IAERROR >> 3] = { 0, 0 }, + [EXCEP_DAERROR >> 3] = { 0, 0 }, + + /* system exceptions */ + [EXCEP_TRAP >> 3] = { SIGTRAP, TRAP_BRKPT }, + [EXCEP_ISTEP >> 3] = { SIGTRAP, TRAP_TRACE }, /* Monitor */ + [EXCEP_IBREAK >> 3] = { SIGTRAP, TRAP_HWBKPT }, /* Monitor */ + [EXCEP_OBREAK >> 3] = { SIGTRAP, TRAP_HWBKPT }, /* Monitor */ + [EXCEP_PRIVINS >> 3] = { SIGILL, ILL_PRVOPC }, + [EXCEP_UNIMPINS >> 3] = { SIGILL, ILL_ILLOPC }, + [EXCEP_UNIMPEXINS >> 3] = { SIGILL, ILL_ILLOPC }, + [EXCEP_MEMERR >> 3] = { SIGSEGV, SEGV_ACCERR }, + [EXCEP_MISALIGN >> 3] = { SIGBUS, BUS_ADRALN }, + [EXCEP_BUSERROR >> 3] = { SIGBUS, BUS_ADRERR }, + [EXCEP_ILLINSACC >> 3] = { SIGSEGV, SEGV_ACCERR }, + [EXCEP_ILLDATACC >> 3] = { SIGSEGV, SEGV_ACCERR }, + [EXCEP_IOINSACC >> 3] = { SIGSEGV, SEGV_ACCERR }, + [EXCEP_PRIVINSACC >> 3] = { SIGSEGV, SEGV_ACCERR }, /* userspace */ + [EXCEP_PRIVDATACC >> 3] = { SIGSEGV, SEGV_ACCERR }, /* userspace */ + [EXCEP_DATINSACC >> 3] = { SIGSEGV, SEGV_ACCERR }, + [EXCEP_DOUBLE_FAULT >> 3] = { SIGILL, ILL_BADSTK }, + + /* FPU exceptions */ + [EXCEP_FPU_DISABLED >> 3] = { SIGILL, ILL_COPROC }, + [EXCEP_FPU_UNIMPINS >> 3] = { SIGILL, ILL_COPROC }, + [EXCEP_FPU_OPERATION >> 3] = { SIGFPE, FPE_INTDIV }, + + /* interrupts */ + [EXCEP_WDT >> 3] = { SIGALRM, 0 }, + [EXCEP_NMI >> 3] = { SIGQUIT, 0 }, + [EXCEP_IRQ_LEVEL0 >> 3] = { SIGINT, 0 }, + [EXCEP_IRQ_LEVEL1 >> 3] = { 0, 0 }, + [EXCEP_IRQ_LEVEL2 >> 3] = { 0, 0 }, + [EXCEP_IRQ_LEVEL3 >> 3] = { 0, 0 }, + [EXCEP_IRQ_LEVEL4 >> 3] = { 0, 0 }, + [EXCEP_IRQ_LEVEL5 >> 3] = { 0, 0 }, + [EXCEP_IRQ_LEVEL6 >> 3] = { 0, 0 }, + + /* system calls */ + [EXCEP_SYSCALL0 >> 3] = { 0, 0 }, + [EXCEP_SYSCALL1 >> 3] = { SIGILL, ILL_ILLTRP }, + [EXCEP_SYSCALL2 >> 3] = { SIGILL, ILL_ILLTRP }, + [EXCEP_SYSCALL3 >> 3] = { SIGILL, ILL_ILLTRP }, + [EXCEP_SYSCALL4 >> 3] = { SIGILL, ILL_ILLTRP }, + [EXCEP_SYSCALL5 >> 3] = { SIGILL, ILL_ILLTRP }, + [EXCEP_SYSCALL6 >> 3] = { SIGILL, ILL_ILLTRP }, + [EXCEP_SYSCALL7 >> 3] = { SIGILL, ILL_ILLTRP }, + [EXCEP_SYSCALL8 >> 3] = { SIGILL, ILL_ILLTRP }, + [EXCEP_SYSCALL9 >> 3] = { SIGILL, ILL_ILLTRP }, + [EXCEP_SYSCALL10 >> 3] = { SIGILL, ILL_ILLTRP }, + [EXCEP_SYSCALL11 >> 3] = { SIGILL, ILL_ILLTRP }, + [EXCEP_SYSCALL12 >> 3] = { SIGILL, ILL_ILLTRP }, + [EXCEP_SYSCALL13 >> 3] = { SIGILL, ILL_ILLTRP }, + [EXCEP_SYSCALL14 >> 3] = { SIGILL, ILL_ILLTRP }, + [EXCEP_SYSCALL15 >> 3] = { SIGABRT, 0 }, +}; + +/* + * Handle kernel exceptions. + * + * See if there's a fixup handler we can force a jump to when an exception + * happens due to something kernel code did + */ +int die_if_no_fixup(const char *str, struct pt_regs *regs, + enum exception_code code) +{ + u8 opcode; + int signo, si_code; + + if (user_mode(regs)) + return 0; + + peripheral_leds_display_exception(code); + + signo = exception_to_signal_map[code >> 3].signo; + si_code = exception_to_signal_map[code >> 3].si_code; + + switch (code) { + /* see if we can fixup the kernel accessing memory */ + case EXCEP_ITLBMISS: + case EXCEP_DTLBMISS: + case EXCEP_IAERROR: + case EXCEP_DAERROR: + case EXCEP_MEMERR: + case EXCEP_MISALIGN: + case EXCEP_BUSERROR: + case EXCEP_ILLDATACC: + case EXCEP_IOINSACC: + case EXCEP_PRIVINSACC: + case EXCEP_PRIVDATACC: + case EXCEP_DATINSACC: + if (fixup_exception(regs)) + return 1; + break; + + case EXCEP_TRAP: + case EXCEP_UNIMPINS: + if (probe_kernel_read(&opcode, (u8 *)regs->pc, 1) < 0) + break; + if (opcode == 0xff) { + if (notify_die(DIE_BREAKPOINT, str, regs, code, 0, 0)) + return 1; + if (at_debugger_breakpoint(regs)) + regs->pc++; + signo = SIGTRAP; + si_code = TRAP_BRKPT; + } + break; + + case EXCEP_SYSCALL1 ... EXCEP_SYSCALL14: + /* syscall return addr is _after_ the instruction */ + regs->pc -= 2; + break; + + case EXCEP_SYSCALL15: + if (report_bug(regs->pc, regs) == BUG_TRAP_TYPE_WARN) + return 1; + + /* syscall return addr is _after_ the instruction */ + regs->pc -= 2; + break; + + default: + break; + } + + if (debugger_intercept(code, signo, si_code, regs) == 0) + return 1; + + if (notify_die(DIE_GPF, str, regs, code, 0, 0)) + return 1; + + /* make the process die as the last resort */ + die(str, regs, code); +} + +/* + * General exception handler + */ +asmlinkage void handle_exception(struct pt_regs *regs, u32 intcode) +{ + siginfo_t info; + + /* deal with kernel exceptions here */ + if (die_if_no_fixup(NULL, regs, intcode)) + return; + + /* otherwise it's a userspace exception */ + info.si_signo = exception_to_signal_map[intcode >> 3].signo; + info.si_code = exception_to_signal_map[intcode >> 3].si_code; + info.si_errno = 0; + info.si_addr = (void *) regs->pc; + force_sig_info(info.si_signo, &info, current); +} + +/* + * handle NMI + */ +asmlinkage void nmi(struct pt_regs *regs, enum exception_code code) +{ + /* see if gdbstub wants to deal with it */ + if (debugger_intercept(code, SIGQUIT, 0, regs)) + return; + + printk(KERN_WARNING "--- Register Dump ---\n"); + show_registers(regs); + printk(KERN_WARNING "---------------------\n"); +} + +/* + * show a stack trace from the specified stack pointer + */ +void show_trace(unsigned long *sp) +{ + unsigned long bottom, stack, addr, fp, raslot; + + printk(KERN_EMERG "\nCall Trace:\n"); + + //stack = (unsigned long)sp; + asm("mov sp,%0" : "=a"(stack)); + asm("mov a3,%0" : "=r"(fp)); + + raslot = ULONG_MAX; + bottom = (stack + THREAD_SIZE) & ~(THREAD_SIZE - 1); + for (; stack < bottom; stack += sizeof(addr)) { + addr = *(unsigned long *)stack; + if (stack == fp) { + if (addr > stack && addr < bottom) { + fp = addr; + raslot = stack + sizeof(addr); + continue; + } + fp = 0; + raslot = ULONG_MAX; + } + + if (__kernel_text_address(addr)) { + printk(" [<%08lx>]", addr); + if (stack >= raslot) + raslot = ULONG_MAX; + else + printk(" ?"); + print_symbol(" %s", addr); + printk("\n"); + } + } + + printk("\n"); +} + +/* + * show the raw stack from the specified stack pointer + */ +void show_stack(struct task_struct *task, unsigned long *sp) +{ + unsigned long *stack; + int i; + + if (!sp) + sp = (unsigned long *) &sp; + + stack = sp; + printk(KERN_EMERG "Stack:"); + for (i = 0; i < kstack_depth_to_print; i++) { + if (((long) stack & (THREAD_SIZE - 1)) == 0) + break; + if ((i % 8) == 0) + printk(KERN_EMERG " "); + printk("%08lx ", *stack++); + } + + show_trace(sp); +} + +/* + * the architecture-independent dump_stack generator + */ +void dump_stack(void) +{ + unsigned long stack; + + show_stack(current, &stack); +} +EXPORT_SYMBOL(dump_stack); + +/* + * dump the register file in the specified exception frame + */ +void show_registers_only(struct pt_regs *regs) +{ + unsigned long ssp; + + ssp = (unsigned long) regs + sizeof(*regs); + + printk(KERN_EMERG "PC: %08lx EPSW: %08lx SSP: %08lx mode: %s\n", + regs->pc, regs->epsw, ssp, user_mode(regs) ? "User" : "Super"); + printk(KERN_EMERG "d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n", + regs->d0, regs->d1, regs->d2, regs->d3); + printk(KERN_EMERG "a0: %08lx a1: %08lx a2: %08lx a3: %08lx\n", + regs->a0, regs->a1, regs->a2, regs->a3); + printk(KERN_EMERG "e0: %08lx e1: %08lx e2: %08lx e3: %08lx\n", + regs->e0, regs->e1, regs->e2, regs->e3); + printk(KERN_EMERG "e4: %08lx e5: %08lx e6: %08lx e7: %08lx\n", + regs->e4, regs->e5, regs->e6, regs->e7); + printk(KERN_EMERG "lar: %08lx lir: %08lx mdr: %08lx usp: %08lx\n", + regs->lar, regs->lir, regs->mdr, regs->sp); + printk(KERN_EMERG "cvf: %08lx crl: %08lx crh: %08lx drq: %08lx\n", + regs->mcvf, regs->mcrl, regs->mcrh, regs->mdrq); + printk(KERN_EMERG "threadinfo=%p task=%p)\n", + current_thread_info(), current); + + if ((unsigned long) current >= PAGE_OFFSET && + (unsigned long) current < (unsigned long)high_memory) + printk(KERN_EMERG "Process %s (pid: %d)\n", + current->comm, current->pid); + +#ifdef CONFIG_SMP + printk(KERN_EMERG "CPUID: %08x\n", CPUID); +#endif + printk(KERN_EMERG "CPUP: %04hx\n", CPUP); + printk(KERN_EMERG "TBR: %08x\n", TBR); + printk(KERN_EMERG "DEAR: %08x\n", DEAR); + printk(KERN_EMERG "sISR: %08x\n", sISR); + printk(KERN_EMERG "NMICR: %04hx\n", NMICR); + printk(KERN_EMERG "BCBERR: %08x\n", BCBERR); + printk(KERN_EMERG "BCBEAR: %08x\n", BCBEAR); + printk(KERN_EMERG "MMUFCR: %08x\n", MMUFCR); + printk(KERN_EMERG "IPTEU : %08x IPTEL2: %08x\n", IPTEU, IPTEL2); + printk(KERN_EMERG "DPTEU: %08x DPTEL2: %08x\n", DPTEU, DPTEL2); +} + +/* + * dump the registers and the stack + */ +void show_registers(struct pt_regs *regs) +{ + unsigned long sp; + int i; + + show_registers_only(regs); + + if (!user_mode(regs)) + sp = (unsigned long) regs + sizeof(*regs); + else + sp = regs->sp; + + /* when in-kernel, we also print out the stack and code at the + * time of the fault.. + */ + if (!user_mode(regs)) { + printk(KERN_EMERG "\n"); + show_stack(current, (unsigned long *) sp); + +#if 0 + printk(KERN_EMERG "\nCode: "); + if (regs->pc < PAGE_OFFSET) + goto bad; + + for (i = 0; i < 20; i++) { + unsigned char c; + if (__get_user(c, &((unsigned char *) regs->pc)[i])) + goto bad; + printk("%02x ", c); + } +#else + i = 0; +#endif + } + + printk("\n"); + return; + +#if 0 +bad: + printk(KERN_EMERG " Bad PC value."); + break; +#endif +} + +/* + * + */ +void show_trace_task(struct task_struct *tsk) +{ + unsigned long sp = tsk->thread.sp; + + /* User space on another CPU? */ + if ((sp ^ (unsigned long) tsk) & (PAGE_MASK << 1)) + return; + + show_trace((unsigned long *) sp); +} + +/* + * note the untimely death of part of the kernel + */ +void die(const char *str, struct pt_regs *regs, enum exception_code code) +{ + console_verbose(); + spin_lock_irq(&die_lock); + printk(KERN_EMERG "\n%s: %04x\n", + str, code & 0xffff); + show_registers(regs); + + if (regs->pc >= 0x02000000 && regs->pc < 0x04000000 && + (regs->epsw & (EPSW_IM | EPSW_IE)) != (EPSW_IM | EPSW_IE)) { + printk(KERN_EMERG "Exception in usermode interrupt handler\n"); + printk(KERN_EMERG "\nPlease connect to kernel debugger !!\n"); + asm volatile ("0: bra 0b"); + } + + spin_unlock_irq(&die_lock); + do_exit(SIGSEGV); +} + +/* + * display the register file when the stack pointer gets clobbered + */ +asmlinkage void do_double_fault(struct pt_regs *regs) +{ + struct task_struct *tsk = current; + + strcpy(tsk->comm, "emergency tsk"); + tsk->pid = 0; + console_verbose(); + printk(KERN_EMERG "--- double fault ---\n"); + show_registers(regs); +} + +/* + * asynchronous bus error (external, usually I/O DMA) + */ +asmlinkage void io_bus_error(u32 bcberr, u32 bcbear, struct pt_regs *regs) +{ + console_verbose(); + + printk(KERN_EMERG "Asynchronous I/O Bus Error\n"); + printk(KERN_EMERG "==========================\n"); + + if (bcberr & BCBERR_BEME) + printk(KERN_EMERG "- Multiple recorded errors\n"); + + printk(KERN_EMERG "- Faulting Buses:%s%s%s\n", + bcberr & BCBERR_BEMR_CI ? " CPU-Ins-Fetch" : "", + bcberr & BCBERR_BEMR_CD ? " CPU-Data" : "", + bcberr & BCBERR_BEMR_DMA ? " DMA" : ""); + + printk(KERN_EMERG "- %s %s access made to %s at address %08x\n", + bcberr & BCBERR_BEBST ? "Burst" : "Single", + bcberr & BCBERR_BERW ? "Read" : "Write", + bcberr & BCBERR_BESB_MON ? "Monitor Space" : + bcberr & BCBERR_BESB_IO ? "Internal CPU I/O Space" : + bcberr & BCBERR_BESB_EX ? "External I/O Bus" : + bcberr & BCBERR_BESB_OPEX ? "External Memory Bus" : + "On Chip Memory", + bcbear + ); + + printk(KERN_EMERG "- Detected by the %s\n", + bcberr&BCBERR_BESD ? "Bus Control Unit" : "Slave Bus"); + +#ifdef CONFIG_PCI +#define BRIDGEREGB(X) (*(volatile __u8 *)(0xBE040000 + (X))) +#define BRIDGEREGW(X) (*(volatile __u16 *)(0xBE040000 + (X))) +#define BRIDGEREGL(X) (*(volatile __u32 *)(0xBE040000 + (X))) + + printk(KERN_EMERG "- PCI Memory Paging Reg: %08x\n", + *(volatile __u32 *) (0xBFFFFFF4)); + printk(KERN_EMERG "- PCI Bridge Base Address 0: %08x\n", + BRIDGEREGL(PCI_BASE_ADDRESS_0)); + printk(KERN_EMERG "- PCI Bridge AMPCI Base Address: %08x\n", + BRIDGEREGL(0x48)); + printk(KERN_EMERG "- PCI Bridge Command: %04hx\n", + BRIDGEREGW(PCI_COMMAND)); + printk(KERN_EMERG "- PCI Bridge Status: %04hx\n", + BRIDGEREGW(PCI_STATUS)); + printk(KERN_EMERG "- PCI Bridge Int Status: %08hx\n", + BRIDGEREGL(0x4c)); +#endif + + printk(KERN_EMERG "\n"); + show_registers(regs); + + panic("Halted due to asynchronous I/O Bus Error\n"); +} + +/* + * handle an exception for which a handler has not yet been installed + */ +asmlinkage void uninitialised_exception(struct pt_regs *regs, + enum exception_code code) +{ + + /* see if gdbstub wants to deal with it */ + if (debugger_intercept(code, SIGSYS, 0, regs) == 0) + return; + + peripheral_leds_display_exception(code); + printk(KERN_EMERG "Uninitialised Exception 0x%04x\n", code & 0xFFFF); + show_registers(regs); + + for (;;) + continue; +} + +/* + * set an interrupt stub to jump to a handler + * ! NOTE: this does *not* flush the caches + */ +void __init __set_intr_stub(enum exception_code code, void *handler) +{ + unsigned long addr; + u8 *vector = (u8 *)(CONFIG_INTERRUPT_VECTOR_BASE + code); + + addr = (unsigned long) handler - (unsigned long) vector; + vector[0] = 0xdc; /* JMP handler */ + vector[1] = addr; + vector[2] = addr >> 8; + vector[3] = addr >> 16; + vector[4] = addr >> 24; + vector[5] = 0xcb; + vector[6] = 0xcb; + vector[7] = 0xcb; +} + +/* + * set an interrupt stub to jump to a handler + */ +void __init set_intr_stub(enum exception_code code, void *handler) +{ + unsigned long addr; + u8 *vector = (u8 *)(CONFIG_INTERRUPT_VECTOR_BASE + code); + unsigned long flags; + + addr = (unsigned long) handler - (unsigned long) vector; + + flags = arch_local_cli_save(); + + vector[0] = 0xdc; /* JMP handler */ + vector[1] = addr; + vector[2] = addr >> 8; + vector[3] = addr >> 16; + vector[4] = addr >> 24; + vector[5] = 0xcb; + vector[6] = 0xcb; + vector[7] = 0xcb; + + arch_local_irq_restore(flags); + +#ifndef CONFIG_MN10300_CACHE_SNOOP + mn10300_dcache_flush_inv(); + mn10300_icache_inv(); +#endif +} + +/* + * initialise the exception table + */ +void __init trap_init(void) +{ + set_excp_vector(EXCEP_TRAP, handle_exception); + set_excp_vector(EXCEP_ISTEP, handle_exception); + set_excp_vector(EXCEP_IBREAK, handle_exception); + set_excp_vector(EXCEP_OBREAK, handle_exception); + + set_excp_vector(EXCEP_PRIVINS, handle_exception); + set_excp_vector(EXCEP_UNIMPINS, handle_exception); + set_excp_vector(EXCEP_UNIMPEXINS, handle_exception); + set_excp_vector(EXCEP_MEMERR, handle_exception); + set_excp_vector(EXCEP_MISALIGN, misalignment); + set_excp_vector(EXCEP_BUSERROR, handle_exception); + set_excp_vector(EXCEP_ILLINSACC, handle_exception); + set_excp_vector(EXCEP_ILLDATACC, handle_exception); + set_excp_vector(EXCEP_IOINSACC, handle_exception); + set_excp_vector(EXCEP_PRIVINSACC, handle_exception); + set_excp_vector(EXCEP_PRIVDATACC, handle_exception); + set_excp_vector(EXCEP_DATINSACC, handle_exception); + set_excp_vector(EXCEP_FPU_UNIMPINS, handle_exception); + set_excp_vector(EXCEP_FPU_OPERATION, fpu_exception); + + set_excp_vector(EXCEP_NMI, nmi); + + set_excp_vector(EXCEP_SYSCALL1, handle_exception); + set_excp_vector(EXCEP_SYSCALL2, handle_exception); + set_excp_vector(EXCEP_SYSCALL3, handle_exception); + set_excp_vector(EXCEP_SYSCALL4, handle_exception); + set_excp_vector(EXCEP_SYSCALL5, handle_exception); + set_excp_vector(EXCEP_SYSCALL6, handle_exception); + set_excp_vector(EXCEP_SYSCALL7, handle_exception); + set_excp_vector(EXCEP_SYSCALL8, handle_exception); + set_excp_vector(EXCEP_SYSCALL9, handle_exception); + set_excp_vector(EXCEP_SYSCALL10, handle_exception); + set_excp_vector(EXCEP_SYSCALL11, handle_exception); + set_excp_vector(EXCEP_SYSCALL12, handle_exception); + set_excp_vector(EXCEP_SYSCALL13, handle_exception); + set_excp_vector(EXCEP_SYSCALL14, handle_exception); + set_excp_vector(EXCEP_SYSCALL15, handle_exception); +} + +/* + * determine if a program counter value is a valid bug address + */ +int is_valid_bugaddr(unsigned long pc) +{ + return pc >= PAGE_OFFSET; +} diff --git a/arch/mn10300/kernel/vmlinux.lds.S b/arch/mn10300/kernel/vmlinux.lds.S new file mode 100644 index 00000000..13c4814c --- /dev/null +++ b/arch/mn10300/kernel/vmlinux.lds.S @@ -0,0 +1,93 @@ +/* MN10300 Main kernel linker script + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#define __VMLINUX_LDS__ +#include <asm-generic/vmlinux.lds.h> +#include <asm/thread_info.h> +#include <asm/page.h> + +OUTPUT_FORMAT("elf32-am33lin", "elf32-am33lin", "elf32-am33lin") +OUTPUT_ARCH(mn10300) +ENTRY(_start) +jiffies = jiffies_64; +#ifndef CONFIG_MN10300_CURRENT_IN_E2 +current = __current; +#endif +SECTIONS +{ + . = CONFIG_KERNEL_TEXT_ADDRESS; + /* read-only */ + _stext = .; + _text = .; /* Text and read-only data */ + .text : { + HEAD_TEXT + TEXT_TEXT + SCHED_TEXT + LOCK_TEXT + KPROBES_TEXT + *(.fixup) + *(.gnu.warning) + } = 0xcb + + _etext = .; /* End of text section */ + + EXCEPTION_TABLE(16) + BUG_TABLE + + RO_DATA(PAGE_SIZE) + + /* writeable */ + _sdata = .; /* Start of rw data section */ + RW_DATA_SECTION(32, PAGE_SIZE, THREAD_SIZE) + _edata = .; + + /* might get freed after init */ + . = ALIGN(PAGE_SIZE); + .smp_locks : AT(ADDR(.smp_locks) - LOAD_OFFSET) { + __smp_locks = .; + *(.smp_locks) + __smp_locks_end = .; + } + + /* will be freed after init */ + . = ALIGN(PAGE_SIZE); /* Init code and data */ + __init_begin = .; + INIT_TEXT_SECTION(PAGE_SIZE) + INIT_DATA_SECTION(16) + . = ALIGN(4); + __alt_instructions = .; + .altinstructions : { *(.altinstructions) } + __alt_instructions_end = .; + .altinstr_replacement : { *(.altinstr_replacement) } + /* .exit.text is discard at runtime, not link time, to deal with references + from .altinstructions and .eh_frame */ + .exit.text : { EXIT_TEXT; } + .exit.data : { EXIT_DATA; } + + PERCPU_SECTION(32) + . = ALIGN(PAGE_SIZE); + __init_end = .; + /* freed after init ends here */ + + BSS_SECTION(0, PAGE_SIZE, 4) + + _end = . ; + + /* This is where the kernel creates the early boot page tables */ + . = ALIGN(PAGE_SIZE); + pg0 = .; + + STABS_DEBUG + + DWARF_DEBUG + + /* Sections to be discarded */ + DISCARDS +} diff --git a/arch/mn10300/lib/Makefile b/arch/mn10300/lib/Makefile new file mode 100644 index 00000000..0cd2346f --- /dev/null +++ b/arch/mn10300/lib/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the MN10300-specific library files.. +# + +lib-y = delay.o usercopy.o checksum.o bitops.o memcpy.o memmove.o memset.o +lib-y += do_csum.o +lib-y += __ashldi3.o __ashrdi3.o __lshrdi3.o negdi2.o __ucmpdi2.o diff --git a/arch/mn10300/lib/__ashldi3.S b/arch/mn10300/lib/__ashldi3.S new file mode 100644 index 00000000..a51a9506 --- /dev/null +++ b/arch/mn10300/lib/__ashldi3.S @@ -0,0 +1,51 @@ +/* MN10300 64-bit arithmetic left shift + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <asm/cache.h> + + .text + .balign L1_CACHE_BYTES + +############################################################################### +# +# unsigned long long __ashldi3(unsigned long long value [D1:D0], +# unsigned by [(12,SP)]) +# +############################################################################### + .globl __ashldi3 + .type __ashldi3,@function +__ashldi3: + mov (12,sp),a0 + and +63,a0 + beq __ashldi3_zero + + cmp +31,a0 + bhi __ashldi3_32plus + + # the count is in the range 1-31 + asl a0,d1 + + mov +32,a1 + sub a0,a1,a1 # a1 = 32 - count + lsr a1,d0,a1 # get overflow from LSW -> MSW + + or_asl a1,d1,a0,d0 # insert overflow into MSW and + # shift the LSW + rets + + .balign L1_CACHE_BYTES + # the count is in the range 32-63 +__ashldi3_32plus: + asl a0,d0,d1 + clr d0 +__ashldi3_zero: + rets + + .size __ashldi3, .-__ashldi3 diff --git a/arch/mn10300/lib/__ashrdi3.S b/arch/mn10300/lib/__ashrdi3.S new file mode 100644 index 00000000..6f423827 --- /dev/null +++ b/arch/mn10300/lib/__ashrdi3.S @@ -0,0 +1,52 @@ +/* MN10300 64-bit arithmetic right shift + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <asm/cache.h> + + .text + .balign L1_CACHE_BYTES + +############################################################################### +# +# unsigned long long __ashrdi3(unsigned long long value [D1:D0], +# unsigned by [(12,SP)]) +# +############################################################################### + .globl __ashrdi3 + .type __ashrdi3,@function +__ashrdi3: + mov (12,sp),a0 + and +63,a0 + beq __ashrdi3_zero + + cmp +31,a0 + bhi __ashrdi3_32plus + + # the count is in the range 1-31 + lsr a0,d0 + + mov +32,a1 + sub a0,a1,a1 # a1 = 32 - count + asl a1,d1,a1 # get underflow from MSW -> LSW + + or_asr a1,d0,a0,d1 # insert underflow into LSW and + # shift the MSW + rets + + .balign L1_CACHE_BYTES + # the count is in the range 32-63 +__ashrdi3_32plus: + asr a0,d1,d0 + ext d0 # sign-extend result through MDR + mov mdr,d1 +__ashrdi3_zero: + rets + + .size __ashrdi3, .-__ashrdi3 diff --git a/arch/mn10300/lib/__lshrdi3.S b/arch/mn10300/lib/__lshrdi3.S new file mode 100644 index 00000000..a686aef3 --- /dev/null +++ b/arch/mn10300/lib/__lshrdi3.S @@ -0,0 +1,52 @@ +/* MN10300 64-bit logical right shift + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <asm/cache.h> + + .text + .balign L1_CACHE_BYTES + +############################################################################### +# +# unsigned long long __lshrdi3(unsigned long long value [D1:D0], +# unsigned by [(12,SP)]) +# +############################################################################### + .globl __lshrdi3 + .type __lshrdi3,@function +__lshrdi3: + mov (12,sp),a0 + and +63,a0 + beq __lshrdi3_zero + + cmp +31,a0 + bhi __lshrdi3_32plus + + # the count is in the range 1-31 + lsr a0,d0 + + mov +32,a1 + sub a0,a1,a1 # a1 = 32 - count + asl a1,d1,a1 # get underflow from MSW -> LSW + + or_lsr a1,d0,a0,d1 # insert underflow into LSW and + # shift the MSW + rets + + .balign L1_CACHE_BYTES + # the count is in the range 32-63 +__lshrdi3_32plus: + lsr a0,d1,d0 + clr d1 +__lshrdi3_zero: + rets + + .size __lshrdi3, .-__lshrdi3 diff --git a/arch/mn10300/lib/__ucmpdi2.S b/arch/mn10300/lib/__ucmpdi2.S new file mode 100644 index 00000000..60dcbdfe --- /dev/null +++ b/arch/mn10300/lib/__ucmpdi2.S @@ -0,0 +1,43 @@ +/* __ucmpdi2.S: 64-bit unsigned compare + * + * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + + + .text + .p2align 4 + +############################################################################### +# +# int __ucmpdi2(unsigned long long a [D0:D1], +# unsigned long long b [(SP,12),(SP,16)]) +# +# - returns 0, 1, or 2 as a <, =, > b respectively. +# +############################################################################### + .globl __ucmpdi2 + .type __ucmpdi2,@function +__ucmpdi2: + mov (12,sp),a0 # b.lsw + mov (16,sp),a1 # b.msw + + sub a0,d0 + subc a1,d1 # may clear Z, never sets it + bne __ucmpdi2_differ # a.msw != b.msw + mov +1,d0 + rets + +__ucmpdi2_differ: + # C flag is set if LE, clear if GE + subc d0,d0 # -1 if LE, 0 if GE + add +1,d0 # 0 if LE, 1 if GE + add d0,d0 # 0 if LE, 2 if GE + rets + + .size __ucmpdi2, .-__ucmpdi2 diff --git a/arch/mn10300/lib/ashrdi3.c b/arch/mn10300/lib/ashrdi3.c new file mode 100644 index 00000000..c54f61dd --- /dev/null +++ b/arch/mn10300/lib/ashrdi3.c @@ -0,0 +1,61 @@ +/* ashrdi3.c extracted from gcc-2.7.2/libgcc2.c which is: */ +/* Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public Licence as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public Licence for more details. + +You should have received a copy of the GNU General Public Licence +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#define BITS_PER_UNIT 8 + +typedef int SItype __attribute__((mode(SI))); +typedef unsigned int USItype __attribute__((mode(SI))); +typedef int DItype __attribute__((mode(DI))); +typedef int word_type __attribute__((mode(__word__))); + +struct DIstruct { + SItype low; + SItype high; +}; + +union DIunion { + struct DIstruct s; + DItype ll; +}; + +DItype __ashrdi3(DItype u, word_type b) +{ + union DIunion w; + union DIunion uu; + word_type bm; + + if (b == 0) + return u; + + uu.ll = u; + + bm = (sizeof(SItype) * BITS_PER_UNIT) - b; + if (bm <= 0) { + /* w.s.high = 1..1 or 0..0 */ + w.s.high = uu.s.high >> (sizeof(SItype) * BITS_PER_UNIT - 1); + w.s.low = uu.s.high >> -bm; + } else { + USItype carries = (USItype)uu.s.high << bm; + w.s.high = uu.s.high >> b; + w.s.low = ((USItype)uu.s.low >> b) | carries; + } + + return w.ll; +} diff --git a/arch/mn10300/lib/bitops.c b/arch/mn10300/lib/bitops.c new file mode 100644 index 00000000..a66c6cda --- /dev/null +++ b/arch/mn10300/lib/bitops.c @@ -0,0 +1,51 @@ +/* MN10300 Non-trivial bit operations + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <asm/bitops.h> +#include <asm/system.h> + +/* + * try flipping a bit using BSET and BCLR + */ +void change_bit(unsigned long nr, volatile void *addr) +{ + if (test_bit(nr, addr)) + goto try_clear_bit; + +try_set_bit: + if (!test_and_set_bit(nr, addr)) + return; + +try_clear_bit: + if (test_and_clear_bit(nr, addr)) + return; + + goto try_set_bit; +} + +/* + * try flipping a bit using BSET and BCLR and returning the old value + */ +int test_and_change_bit(unsigned long nr, volatile void *addr) +{ + if (test_bit(nr, addr)) + goto try_clear_bit; + +try_set_bit: + if (!test_and_set_bit(nr, addr)) + return 0; + +try_clear_bit: + if (test_and_clear_bit(nr, addr)) + return 1; + + goto try_set_bit; +} diff --git a/arch/mn10300/lib/checksum.c b/arch/mn10300/lib/checksum.c new file mode 100644 index 00000000..b6580f5d --- /dev/null +++ b/arch/mn10300/lib/checksum.c @@ -0,0 +1,100 @@ +/* MN10300 Optimised checksumming wrappers + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/errno.h> +#include <asm/byteorder.h> +#include <asm/uaccess.h> +#include <asm/checksum.h> +#include "internal.h" + +static inline unsigned short from32to16(__wsum sum) +{ + asm(" add %1,%0 \n" + " addc 0xffff,%0 \n" + : "=r" (sum) + : "r" (sum << 16), "0" (sum & 0xffff0000) + : "cc" + ); + return sum >> 16; +} + +__sum16 ip_fast_csum(const void *iph, unsigned int ihl) +{ + return ~do_csum(iph, ihl * 4); +} +EXPORT_SYMBOL(ip_fast_csum); + +__wsum csum_partial(const void *buff, int len, __wsum sum) +{ + __wsum result; + + result = do_csum(buff, len); + result += sum; + if (sum > result) + result++; + return result; +} +EXPORT_SYMBOL(csum_partial); + +__sum16 ip_compute_csum(const void *buff, int len) +{ + return ~from32to16(do_csum(buff, len)); +} +EXPORT_SYMBOL(ip_compute_csum); + +__wsum csum_partial_copy(const void *src, void *dst, int len, __wsum sum) +{ + copy_from_user(dst, src, len); + return csum_partial(dst, len, sum); +} +EXPORT_SYMBOL(csum_partial_copy); + +__wsum csum_partial_copy_nocheck(const void *src, void *dst, + int len, __wsum sum) +{ + sum = csum_partial(src, len, sum); + memcpy(dst, src, len); + return sum; +} +EXPORT_SYMBOL(csum_partial_copy_nocheck); + +__wsum csum_partial_copy_from_user(const void *src, void *dst, + int len, __wsum sum, + int *err_ptr) +{ + int missing; + + missing = copy_from_user(dst, src, len); + if (missing) { + memset(dst + len - missing, 0, missing); + *err_ptr = -EFAULT; + } + + return csum_partial(dst, len, sum); +} +EXPORT_SYMBOL(csum_partial_copy_from_user); + +__wsum csum_and_copy_to_user(const void *src, void *dst, + int len, __wsum sum, + int *err_ptr) +{ + int missing; + + missing = copy_to_user(dst, src, len); + if (missing) { + memset(dst + len - missing, 0, missing); + *err_ptr = -EFAULT; + } + + return csum_partial(src, len, sum); +} +EXPORT_SYMBOL(csum_and_copy_to_user); diff --git a/arch/mn10300/lib/delay.c b/arch/mn10300/lib/delay.c new file mode 100644 index 00000000..8e7ceb8b --- /dev/null +++ b/arch/mn10300/lib/delay.c @@ -0,0 +1,51 @@ +/* MN10300 Short delay interpolation routines + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <asm/div64.h> + +/* + * basic delay loop + */ +void __delay(unsigned long loops) +{ + int d0; + + asm volatile( + " bra 1f \n" + " .align 4 \n" + "1: bra 2f \n" + " .align 4 \n" + "2: add -1,%0 \n" + " bne 2b \n" + : "=&d" (d0) + : "0" (loops) + : "cc"); +} +EXPORT_SYMBOL(__delay); + +/* + * handle a delay specified in terms of microseconds + */ +void __udelay(unsigned long usecs) +{ + unsigned long start, stop, cnt; + + /* usecs * CLK / 1E6 */ + stop = __muldiv64u(usecs, MN10300_TSCCLK, 1000000); + start = TMTSCBC; + + do { + cnt = start - TMTSCBC; + } while (cnt < stop); +} +EXPORT_SYMBOL(__udelay); diff --git a/arch/mn10300/lib/do_csum.S b/arch/mn10300/lib/do_csum.S new file mode 100644 index 00000000..1d27bba0 --- /dev/null +++ b/arch/mn10300/lib/do_csum.S @@ -0,0 +1,157 @@ +/* Optimised simple memory checksum + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <asm/cache.h> + + .section .text + .balign L1_CACHE_BYTES + +############################################################################### +# +# unsigned int do_csum(const unsigned char *buff, int len) +# +############################################################################### + .globl do_csum + .type do_csum,@function +do_csum: + movm [d2,d3],(sp) + mov d1,d2 # count + mov d0,a0 # buff + mov a0,a1 + clr d1 # accumulator + + cmp +0,d2 + ble do_csum_done # check for zero length or negative + + # 4-byte align the buffer pointer + btst +3,a0 + beq do_csum_now_4b_aligned + + btst +1,a0 + beq do_csum_addr_not_odd + movbu (a0),d0 + inc a0 + asl +8,d0 + add d0,d1 + add -1,d2 + +do_csum_addr_not_odd: + cmp +2,d2 + bcs do_csum_fewer_than_4 + btst +2,a0 + beq do_csum_now_4b_aligned + movhu (a0+),d0 + add d0,d1 + add -2,d2 + cmp +4,d2 + bcs do_csum_fewer_than_4 + +do_csum_now_4b_aligned: + # we want to checksum as much as we can in chunks of 32 bytes + cmp +31,d2 + bls do_csum_remainder # 4-byte aligned remainder + + add -32,d2 + mov +32,d3 + +do_csum_loop: + mov (a0+),d0 + mov (a0+),e0 + mov (a0+),e1 + mov (a0+),e3 + add d0,d1 + addc e0,d1 + addc e1,d1 + addc e3,d1 + mov (a0+),d0 + mov (a0+),e0 + mov (a0+),e1 + mov (a0+),e3 + addc d0,d1 + addc e0,d1 + addc e1,d1 + addc e3,d1 + addc +0,d1 + + sub d3,d2 + bcc do_csum_loop + + add d3,d2 + beq do_csum_done + +do_csum_remainder: + # cut 16-31 bytes down to 0-15 + cmp +16,d2 + bcs do_csum_fewer_than_16 + mov (a0+),d0 + mov (a0+),e0 + mov (a0+),e1 + mov (a0+),e3 + add d0,d1 + addc e0,d1 + addc e1,d1 + addc e3,d1 + addc +0,d1 + add -16,d2 + beq do_csum_done + +do_csum_fewer_than_16: + # copy the remaining whole words + cmp +4,d2 + bcs do_csum_fewer_than_4 + cmp +8,d2 + bcs do_csum_one_word + cmp +12,d2 + bcs do_csum_two_words + mov (a0+),d0 + add d0,d1 + addc +0,d1 +do_csum_two_words: + mov (a0+),d0 + add d0,d1 + addc +0,d1 +do_csum_one_word: + mov (a0+),d0 + add d0,d1 + addc +0,d1 + +do_csum_fewer_than_4: + and +3,d2 + beq do_csum_done + xor_cmp d0,d0,+2,d2 + bcs do_csum_fewer_than_2 + movhu (a0+),d0 + and +1,d2 + beq do_csum_add_last_bit +do_csum_fewer_than_2: + movbu (a0),d3 + add d3,d0 +do_csum_add_last_bit: + add d0,d1 + addc +0,d1 + +do_csum_done: + # compress the checksum down to 16 bits + mov +0xffff0000,d0 + and d1,d0 + asl +16,d1 + add d1,d0 + addc +0xffff,d0 + lsr +16,d0 + + # flip the halves of the word result if the buffer was oddly aligned + and +1,a1 + beq do_csum_not_oddly_aligned + swaph d0,d0 # exchange bits 15:8 with 7:0 + +do_csum_not_oddly_aligned: + ret [d2,d3],8 + + .size do_csum, .-do_csum diff --git a/arch/mn10300/lib/internal.h b/arch/mn10300/lib/internal.h new file mode 100644 index 00000000..0014eee5 --- /dev/null +++ b/arch/mn10300/lib/internal.h @@ -0,0 +1,15 @@ +/* Internal definitions for the arch part of the kernel library + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +/* + * do_csum.S + */ +extern unsigned int do_csum(const unsigned char *, size_t); diff --git a/arch/mn10300/lib/lshrdi3.c b/arch/mn10300/lib/lshrdi3.c new file mode 100644 index 00000000..e05e64e9 --- /dev/null +++ b/arch/mn10300/lib/lshrdi3.c @@ -0,0 +1,60 @@ +/* lshrdi3.c extracted from gcc-2.7.2/libgcc2.c which is: */ +/* Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public Licence as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public Licence for more details. + +You should have received a copy of the GNU General Public Licence +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#define BITS_PER_UNIT 8 + +typedef int SItype __attribute__((mode(SI))); +typedef unsigned int USItype __attribute__((mode(SI))); +typedef int DItype __attribute__((mode(DI))); +typedef int word_type __attribute__((mode(__word__))); + +struct DIstruct { + SItype low; + SItype high; +}; + +union DIunion { + struct DIstruct s; + DItype ll; +}; + +DItype __lshrdi3(DItype u, word_type b) +{ + union DIunion w; + word_type bm; + union DIunion uu; + + if (b == 0) + return u; + + uu.ll = u; + + bm = (sizeof(SItype) * BITS_PER_UNIT) - b; + if (bm <= 0) { + w.s.high = 0; + w.s.low = (USItype) uu.s.high >> -bm; + } else { + USItype carries = (USItype) uu.s.high << bm; + w.s.high = (USItype) uu.s.high >> b; + w.s.low = ((USItype) uu.s.low >> b) | carries; + } + + return w.ll; +} diff --git a/arch/mn10300/lib/memcpy.S b/arch/mn10300/lib/memcpy.S new file mode 100644 index 00000000..25fb9bb2 --- /dev/null +++ b/arch/mn10300/lib/memcpy.S @@ -0,0 +1,135 @@ +/* MN10300 Optimised simple memory to memory copy + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <asm/cache.h> + + .section .text + .balign L1_CACHE_BYTES + +############################################################################### +# +# void *memcpy(void *dst, const void *src, size_t n) +# +############################################################################### + .globl memcpy + .type memcpy,@function +memcpy: + movm [d2,d3],(sp) + mov d0,(12,sp) + mov d1,(16,sp) + mov (20,sp),d2 # count + mov d0,a0 # dst + mov d1,a1 # src + mov d0,e3 # the return value + + cmp +0,d2 + beq memcpy_done # return if zero-length copy + + # see if the three parameters are all four-byte aligned + or d0,d1,d3 + or d2,d3 + and +3,d3 + bne memcpy_1 # jump if not + + # we want to transfer as much as we can in chunks of 32 bytes + cmp +31,d2 + bls memcpy_4_remainder # 4-byte aligned remainder + + movm [exreg1],(sp) + add -32,d2 + mov +32,d3 + +memcpy_4_loop: + mov (a1+),d0 + mov (a1+),d1 + mov (a1+),e0 + mov (a1+),e1 + mov (a1+),e4 + mov (a1+),e5 + mov (a1+),e6 + mov (a1+),e7 + mov d0,(a0+) + mov d1,(a0+) + mov e0,(a0+) + mov e1,(a0+) + mov e4,(a0+) + mov e5,(a0+) + mov e6,(a0+) + mov e7,(a0+) + + sub d3,d2 + bcc memcpy_4_loop + + movm (sp),[exreg1] + add d3,d2 + beq memcpy_4_no_remainder + +memcpy_4_remainder: + # cut 4-7 words down to 0-3 + cmp +16,d2 + bcs memcpy_4_three_or_fewer_words + mov (a1+),d0 + mov (a1+),d1 + mov (a1+),e0 + mov (a1+),e1 + mov d0,(a0+) + mov d1,(a0+) + mov e0,(a0+) + mov e1,(a0+) + add -16,d2 + beq memcpy_4_no_remainder + + # copy the remaining 1, 2 or 3 words +memcpy_4_three_or_fewer_words: + cmp +8,d2 + bcs memcpy_4_one_word + beq memcpy_4_two_words + mov (a1+),d0 + mov d0,(a0+) +memcpy_4_two_words: + mov (a1+),d0 + mov d0,(a0+) +memcpy_4_one_word: + mov (a1+),d0 + mov d0,(a0+) + +memcpy_4_no_remainder: + # check we copied the correct amount + # TODO: REMOVE CHECK + sub e3,a0,d2 + mov (20,sp),d1 + cmp d2,d1 + beq memcpy_done + break + break + break + +memcpy_done: + mov e3,a0 + ret [d2,d3],8 + + # handle misaligned copying +memcpy_1: + add -1,d2 + mov +1,d3 + setlb # setlb requires the next insns + # to occupy exactly 4 bytes + + sub d3,d2 + movbu (a1),d0 + movbu d0,(a0) + add_add d3,a1,d3,a0 + lcc + + mov e3,a0 + ret [d2,d3],8 + +memcpy_end: + .size memcpy, memcpy_end-memcpy diff --git a/arch/mn10300/lib/memmove.S b/arch/mn10300/lib/memmove.S new file mode 100644 index 00000000..20b07b62 --- /dev/null +++ b/arch/mn10300/lib/memmove.S @@ -0,0 +1,160 @@ +/* MN10300 Optimised simple memory to memory copy, with support for overlapping + * regions + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <asm/cache.h> + + .section .text + .balign L1_CACHE_BYTES + +############################################################################### +# +# void *memmove(void *dst, const void *src, size_t n) +# +############################################################################### + .globl memmove + .type memmove,@function +memmove: + # fall back to memcpy if dst < src to work bottom up + cmp d1,d0 + bcs memmove_memcpy + + # work top down + movm [d2,d3],(sp) + mov d0,(12,sp) + mov d1,(16,sp) + mov (20,sp),d2 # count + add d0,d2,a0 # dst end + add d1,d2,a1 # src end + mov d0,e3 # the return value + + cmp +0,d2 + beq memmove_done # return if zero-length copy + + # see if the three parameters are all four-byte aligned + or d0,d1,d3 + or d2,d3 + and +3,d3 + bne memmove_1 # jump if not + + # we want to transfer as much as we can in chunks of 32 bytes + add -4,a1 + cmp +31,d2 + bls memmove_4_remainder # 4-byte aligned remainder + + add -32,d2 + mov +32,d3 + +memmove_4_loop: + mov (a1),d0 + sub_sub +4,a1,+4,a0 + mov d0,(a0) + mov (a1),d1 + sub_sub +4,a1,+4,a0 + mov d1,(a0) + + mov (a1),d0 + sub_sub +4,a1,+4,a0 + mov d0,(a0) + mov (a1),d1 + sub_sub +4,a1,+4,a0 + mov d1,(a0) + + mov (a1),d0 + sub_sub +4,a1,+4,a0 + mov d0,(a0) + mov (a1),d1 + sub_sub +4,a1,+4,a0 + mov d1,(a0) + + mov (a1),d0 + sub_sub +4,a1,+4,a0 + mov d0,(a0) + mov (a1),d1 + sub_sub +4,a1,+4,a0 + mov d1,(a0) + + sub d3,d2 + bcc memmove_4_loop + + add d3,d2 + beq memmove_4_no_remainder + +memmove_4_remainder: + # cut 4-7 words down to 0-3 + cmp +16,d2 + bcs memmove_4_three_or_fewer_words + mov (a1),d0 + sub_sub +4,a1,+4,a0 + mov d0,(a0) + mov (a1),d1 + sub_sub +4,a1,+4,a0 + mov d1,(a0) + mov (a1),e0 + sub_sub +4,a1,+4,a0 + mov e0,(a0) + mov (a1),e1 + sub_sub +4,a1,+4,a0 + mov e1,(a0) + add -16,d2 + beq memmove_4_no_remainder + + # copy the remaining 1, 2 or 3 words +memmove_4_three_or_fewer_words: + cmp +8,d2 + bcs memmove_4_one_word + beq memmove_4_two_words + mov (a1),d0 + sub_sub +4,a1,+4,a0 + mov d0,(a0) +memmove_4_two_words: + mov (a1),d0 + sub_sub +4,a1,+4,a0 + mov d0,(a0) +memmove_4_one_word: + mov (a1),d0 + sub_sub +4,a1,+4,a0 + mov d0,(a0) + +memmove_4_no_remainder: + # check we copied the correct amount + # TODO: REMOVE CHECK + sub e3,a0,d2 + beq memmove_done + break + break + break + +memmove_done: + mov e3,a0 + ret [d2,d3],8 + + # handle misaligned copying +memmove_1: + add -1,a1 + add -1,d2 + mov +1,d3 + setlb # setlb requires the next insns + # to occupy exactly 4 bytes + + sub d3,d2 + movbu (a1),d0 + sub_sub d3,a1,d3,a0 + movbu d0,(a0) + lcc + + mov e3,a0 + ret [d2,d3],8 + +memmove_memcpy: + jmp memcpy + +memmove_end: + .size memmove, memmove_end-memmove diff --git a/arch/mn10300/lib/memset.S b/arch/mn10300/lib/memset.S new file mode 100644 index 00000000..bc02e396 --- /dev/null +++ b/arch/mn10300/lib/memset.S @@ -0,0 +1,121 @@ +/* Optimised simple memory fill + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <asm/cache.h> + + .section .text + .balign L1_CACHE_BYTES + +############################################################################### +# +# void *memset(void *dst, int c, size_t n) +# +############################################################################### + .globl memset + .type memset,@function +memset: + movm [d2,d3],(sp) + mov d0,(12,sp) + mov d1,(16,sp) + mov (20,sp),d2 # count + mov d0,a0 # dst + mov d0,e3 # the return value + + cmp +0,d2 + beq memset_done # return if zero-length fill + + # see if the region parameters are four-byte aligned + or d0,d2,d3 + and +3,d3 + bne memset_1 # jump if not + + extbu d1 + mov_asl d1,d3,8,d1 + or_asl d1,d3,8,d1 + or_asl d1,d3,8,d1 + or d3,d1 + + # we want to transfer as much as we can in chunks of 32 bytes + cmp +31,d2 + bls memset_4_remainder # 4-byte aligned remainder + + add -32,d2 + mov +32,d3 + +memset_4_loop: + mov d1,(a0+) + mov d1,(a0+) + mov d1,(a0+) + mov d1,(a0+) + mov d1,(a0+) + mov d1,(a0+) + mov d1,(a0+) + mov d1,(a0+) + + sub d3,d2 + bcc memset_4_loop + + add d3,d2 + beq memset_4_no_remainder + +memset_4_remainder: + # cut 4-7 words down to 0-3 + cmp +16,d2 + bcs memset_4_three_or_fewer_words + mov d1,(a0+) + mov d1,(a0+) + mov d1,(a0+) + mov d1,(a0+) + add -16,d2 + beq memset_4_no_remainder + + # copy the remaining 1, 2 or 3 words +memset_4_three_or_fewer_words: + cmp +8,d2 + bcs memset_4_one_word + beq memset_4_two_words + mov d1,(a0+) +memset_4_two_words: + mov d1,(a0+) +memset_4_one_word: + mov d1,(a0+) + +memset_4_no_remainder: + # check we set the correct amount + # TODO: REMOVE CHECK + sub e3,a0,d2 + mov (20,sp),d1 + cmp d2,d1 + beq memset_done + break + break + break + +memset_done: + mov e3,a0 + ret [d2,d3],8 + + # handle misaligned copying +memset_1: + add -1,d2 + mov +1,d3 + setlb # setlb requires the next insns + # to occupy exactly 4 bytes + + sub d3,d2 + movbu d1,(a0) + inc a0 + lcc + + mov e3,a0 + ret [d2,d3],8 + +memset_end: + .size memset, memset_end-memset diff --git a/arch/mn10300/lib/negdi2.c b/arch/mn10300/lib/negdi2.c new file mode 100644 index 00000000..eae4ecdd --- /dev/null +++ b/arch/mn10300/lib/negdi2.c @@ -0,0 +1,57 @@ +/* More subroutines needed by GCC output code on some machines. */ +/* Compile this one with gcc. */ +/* Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, + 2000, 2001 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public Licence as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +In addition to the permissions in the GNU General Public Licence, the +Free Software Foundation gives you unlimited permission to link the +compiled version of this file into combinations with other programs, +and to distribute those combinations without any restriction coming +from the use of this file. (The General Public Licence restrictions +do apply in other respects; for example, they cover modification of +the file, and distribution when not linked into a combine +executable.) + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public Licence for more details. + +You should have received a copy of the GNU General Public Licence +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* It is incorrect to include config.h here, because this file is being + compiled for the target, and hence definitions concerning only the host + do not apply. */ + +#include <linux/types.h> + +union DWunion { + s64 ll; + struct { + s32 low; + s32 high; + } s; +}; + +s64 __negdi2(s64 u) +{ + union DWunion w; + union DWunion uu; + + uu.ll = u; + + w.s.low = -uu.s.low; + w.s.high = -uu.s.high - ((u32) w.s.low > 0); + + return w.ll; +} diff --git a/arch/mn10300/lib/usercopy.c b/arch/mn10300/lib/usercopy.c new file mode 100644 index 00000000..7826e6c3 --- /dev/null +++ b/arch/mn10300/lib/usercopy.c @@ -0,0 +1,166 @@ +/* MN10300 Userspace accessor functions + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <asm/uaccess.h> + +unsigned long +__generic_copy_to_user(void *to, const void *from, unsigned long n) +{ + if (access_ok(VERIFY_WRITE, to, n)) + __copy_user(to, from, n); + return n; +} + +unsigned long +__generic_copy_from_user(void *to, const void *from, unsigned long n) +{ + if (access_ok(VERIFY_READ, from, n)) + __copy_user_zeroing(to, from, n); + return n; +} + +/* + * Copy a null terminated string from userspace. + */ +#define __do_strncpy_from_user(dst, src, count, res) \ +do { \ + int w; \ + asm volatile( \ + " mov %1,%0\n" \ + " cmp 0,%1\n" \ + " beq 2f\n" \ + "0:\n" \ + " movbu (%5),%2\n" \ + "1:\n" \ + " movbu %2,(%6)\n" \ + " inc %5\n" \ + " inc %6\n" \ + " cmp 0,%2\n" \ + " beq 2f\n" \ + " add -1,%1\n" \ + " bne 0b\n" \ + "2:\n" \ + " sub %1,%0\n" \ + "3:\n" \ + " .section .fixup,\"ax\"\n" \ + "4:\n" \ + " mov %3,%0\n" \ + " jmp 3b\n" \ + " .previous\n" \ + " .section __ex_table,\"a\"\n" \ + " .balign 4\n" \ + " .long 0b,4b\n" \ + " .long 1b,4b\n" \ + " .previous" \ + :"=&r"(res), "=r"(count), "=&r"(w) \ + :"i"(-EFAULT), "1"(count), "a"(src), "a"(dst) \ + : "memory", "cc"); \ +} while (0) + +long +__strncpy_from_user(char *dst, const char *src, long count) +{ + long res; + __do_strncpy_from_user(dst, src, count, res); + return res; +} + +long +strncpy_from_user(char *dst, const char *src, long count) +{ + long res = -EFAULT; + if (access_ok(VERIFY_READ, src, 1)) + __do_strncpy_from_user(dst, src, count, res); + return res; +} + + +/* + * Clear a userspace memory + */ +#define __do_clear_user(addr, size) \ +do { \ + int w; \ + asm volatile( \ + " cmp 0,%0\n" \ + " beq 1f\n" \ + " clr %1\n" \ + "0: movbu %1,(%3,%2)\n" \ + " inc %3\n" \ + " cmp %0,%3\n" \ + " bne 0b\n" \ + "1:\n" \ + " sub %3,%0\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: jmp 2b\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .balign 4\n" \ + " .long 0b,3b\n" \ + ".previous\n" \ + : "+r"(size), "=&r"(w) \ + : "a"(addr), "d"(0) \ + : "memory", "cc"); \ +} while (0) + +unsigned long +__clear_user(void *to, unsigned long n) +{ + __do_clear_user(to, n); + return n; +} + +unsigned long +clear_user(void *to, unsigned long n) +{ + if (access_ok(VERIFY_WRITE, to, n)) + __do_clear_user(to, n); + return n; +} + +/* + * Return the size of a string (including the ending 0) + * + * Return 0 on exception, a value greater than N if too long + */ +long strnlen_user(const char *s, long n) +{ + unsigned long res, w; + + if (!__addr_ok(s)) + return 0; + + if (n < 0 || n + (u_long) s > current_thread_info()->addr_limit.seg) + n = current_thread_info()->addr_limit.seg - (u_long)s; + + asm volatile( + "0: cmp %4,%0\n" + " beq 2f\n" + "1: movbu (%0,%3),%1\n" + " inc %0\n" + " cmp 0,%1\n" + " beq 3f\n" + " bra 0b\n" + "2: clr %0\n" + "3:\n" + ".section .fixup,\"ax\"\n" + "4: jmp 2b\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .balign 4\n" + " .long 1b,4b\n" + ".previous\n" + :"=d"(res), "=&r"(w) + :"0"(0), "a"(s), "r"(n) + : "memory", "cc"); + return res; +} diff --git a/arch/mn10300/mm/Kconfig.cache b/arch/mn10300/mm/Kconfig.cache new file mode 100644 index 00000000..bfbe5269 --- /dev/null +++ b/arch/mn10300/mm/Kconfig.cache @@ -0,0 +1,147 @@ +# +# MN10300 CPU cache options +# + +choice + prompt "CPU Caching mode" + default MN10300_CACHE_WBACK + help + This option determines the caching mode for the kernel. + + Write-Back caching mode involves the all reads and writes causing + the affected cacheline to be read into the cache first before being + operated upon. Memory is not then updated by a write until the cache + is filled and a cacheline needs to be displaced from the cache to + make room. Only at that point is it written back. + + Write-Through caching only fetches cachelines from memory on a + read. Writes always get written directly to memory. If the affected + cacheline is also in cache, it will be updated too. + + The final option is to turn of caching entirely. + +config MN10300_CACHE_WBACK + bool "Write-Back" + help + The dcache operates in delayed write-back mode. It must be manually + flushed if writes are made that subsequently need to be executed or + to be DMA'd by a device. + +config MN10300_CACHE_WTHRU + bool "Write-Through" + help + The dcache operates in immediate write-through mode. Writes are + committed to RAM immediately in addition to being stored in the + cache. This means that the written data is immediately available for + execution or DMA. + + This is not available for use with an SMP kernel if cache flushing + and invalidation by automatic purge register is not selected. + +config MN10300_CACHE_DISABLED + bool "Disabled" + help + The icache and dcache are disabled. + +endchoice + +config MN10300_CACHE_ENABLED + def_bool y if !MN10300_CACHE_DISABLED + + +choice + prompt "CPU cache flush/invalidate method" + default MN10300_CACHE_MANAGE_BY_TAG if !AM34_2 + default MN10300_CACHE_MANAGE_BY_REG if AM34_2 + depends on MN10300_CACHE_ENABLED + help + This determines the method by which CPU cache flushing and + invalidation is performed. + +config MN10300_CACHE_MANAGE_BY_TAG + bool "Use the cache tag registers directly" + depends on !(SMP && MN10300_CACHE_WTHRU) + +config MN10300_CACHE_MANAGE_BY_REG + bool "Flush areas by way of automatic purge registers (AM34 only)" + depends on AM34_2 + +endchoice + +config MN10300_CACHE_INV_BY_TAG + def_bool y if MN10300_CACHE_MANAGE_BY_TAG && MN10300_CACHE_ENABLED + +config MN10300_CACHE_INV_BY_REG + def_bool y if MN10300_CACHE_MANAGE_BY_REG && MN10300_CACHE_ENABLED + +config MN10300_CACHE_FLUSH_BY_TAG + def_bool y if MN10300_CACHE_MANAGE_BY_TAG && MN10300_CACHE_WBACK + +config MN10300_CACHE_FLUSH_BY_REG + def_bool y if MN10300_CACHE_MANAGE_BY_REG && MN10300_CACHE_WBACK + + +config MN10300_HAS_CACHE_SNOOP + def_bool n + +config MN10300_CACHE_SNOOP + bool "Use CPU Cache Snooping" + depends on MN10300_CACHE_ENABLED && MN10300_HAS_CACHE_SNOOP + default y + +config MN10300_CACHE_FLUSH_ICACHE + def_bool y if MN10300_CACHE_WBACK && !MN10300_CACHE_SNOOP + help + Set if we need the dcache flushing before the icache is invalidated. + +config MN10300_CACHE_INV_ICACHE + def_bool y if MN10300_CACHE_WTHRU && !MN10300_CACHE_SNOOP + help + Set if we need the icache to be invalidated, even if the dcache is in + write-through mode and doesn't need flushing. + +# +# The kernel debugger gets its own separate cache flushing functions +# +config MN10300_DEBUGGER_CACHE_FLUSH_BY_TAG + def_bool y if KERNEL_DEBUGGER && \ + MN10300_CACHE_WBACK && \ + !MN10300_CACHE_SNOOP && \ + MN10300_CACHE_MANAGE_BY_TAG + help + Set if the debugger needs to flush the dcache and invalidate the + icache using the cache tag registers to make breakpoints work. + +config MN10300_DEBUGGER_CACHE_FLUSH_BY_REG + def_bool y if KERNEL_DEBUGGER && \ + MN10300_CACHE_WBACK && \ + !MN10300_CACHE_SNOOP && \ + MN10300_CACHE_MANAGE_BY_REG + help + Set if the debugger needs to flush the dcache and invalidate the + icache using automatic purge registers to make breakpoints work. + +config MN10300_DEBUGGER_CACHE_INV_BY_TAG + def_bool y if KERNEL_DEBUGGER && \ + MN10300_CACHE_WTHRU && \ + !MN10300_CACHE_SNOOP && \ + MN10300_CACHE_MANAGE_BY_TAG + help + Set if the debugger needs to invalidate the icache using the cache + tag registers to make breakpoints work. + +config MN10300_DEBUGGER_CACHE_INV_BY_REG + def_bool y if KERNEL_DEBUGGER && \ + MN10300_CACHE_WTHRU && \ + !MN10300_CACHE_SNOOP && \ + MN10300_CACHE_MANAGE_BY_REG + help + Set if the debugger needs to invalidate the icache using automatic + purge registers to make breakpoints work. + +config MN10300_DEBUGGER_CACHE_NO_FLUSH + def_bool y if KERNEL_DEBUGGER && \ + (MN10300_CACHE_DISABLED || MN10300_CACHE_SNOOP) + help + Set if the debugger does not need to flush the dcache and/or + invalidate the icache to make breakpoints work. diff --git a/arch/mn10300/mm/Makefile b/arch/mn10300/mm/Makefile new file mode 100644 index 00000000..11f38466 --- /dev/null +++ b/arch/mn10300/mm/Makefile @@ -0,0 +1,31 @@ +# +# Makefile for the MN10300-specific memory management code +# + +cache-smp-wback-$(CONFIG_MN10300_CACHE_WBACK) := cache-smp-flush.o + +cacheflush-y := cache.o +cacheflush-$(CONFIG_SMP) += cache-smp.o cache-smp-inv.o $(cache-smp-wback-y) +cacheflush-$(CONFIG_MN10300_CACHE_INV_ICACHE) += cache-inv-icache.o +cacheflush-$(CONFIG_MN10300_CACHE_FLUSH_ICACHE) += cache-flush-icache.o +cacheflush-$(CONFIG_MN10300_CACHE_INV_BY_TAG) += cache-inv-by-tag.o +cacheflush-$(CONFIG_MN10300_CACHE_INV_BY_REG) += cache-inv-by-reg.o +cacheflush-$(CONFIG_MN10300_CACHE_FLUSH_BY_TAG) += cache-flush-by-tag.o +cacheflush-$(CONFIG_MN10300_CACHE_FLUSH_BY_REG) += cache-flush-by-reg.o + +cacheflush-$(CONFIG_MN10300_DEBUGGER_CACHE_FLUSH_BY_TAG) += \ + cache-dbg-flush-by-tag.o cache-dbg-inv-by-tag.o +cacheflush-$(CONFIG_MN10300_DEBUGGER_CACHE_FLUSH_BY_REG) += \ + cache-dbg-flush-by-reg.o +cacheflush-$(CONFIG_MN10300_DEBUGGER_CACHE_INV_BY_TAG) += \ + cache-dbg-inv-by-tag.o cache-dbg-inv.o +cacheflush-$(CONFIG_MN10300_DEBUGGER_CACHE_INV_BY_REG) += \ + cache-dbg-inv-by-reg.o cache-dbg-inv.o + +cacheflush-$(CONFIG_MN10300_CACHE_DISABLED) := cache-disabled.o + +obj-y := \ + init.o fault.o pgtable.o extable.o tlb-mn10300.o mmu-context.o \ + misalignment.o dma-alloc.o $(cacheflush-y) + +obj-$(CONFIG_SMP) += tlb-smp.o diff --git a/arch/mn10300/mm/cache-dbg-flush-by-reg.S b/arch/mn10300/mm/cache-dbg-flush-by-reg.S new file mode 100644 index 00000000..a775ea5d --- /dev/null +++ b/arch/mn10300/mm/cache-dbg-flush-by-reg.S @@ -0,0 +1,160 @@ +/* MN10300 CPU cache invalidation routines, using automatic purge registers + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/page.h> +#include <asm/cache.h> +#include <asm/irqflags.h> +#include <asm/cacheflush.h> +#include "cache.inc" + + .am33_2 + +############################################################################### +# +# void debugger_local_cache_flushinv(void) +# Flush the entire data cache back to RAM and invalidate the icache +# +############################################################################### + ALIGN + .globl debugger_local_cache_flushinv + .type debugger_local_cache_flushinv,@function +debugger_local_cache_flushinv: + # + # firstly flush the dcache + # + movhu (CHCTR),d0 + btst CHCTR_DCEN|CHCTR_ICEN,d0 + beq debugger_local_cache_flushinv_end + + mov DCPGCR,a0 + + mov epsw,d1 + and ~EPSW_IE,epsw + or EPSW_NMID,epsw + nop + + btst CHCTR_DCEN,d0 + beq debugger_local_cache_flushinv_no_dcache + + # wait for busy bit of area purge + setlb + mov (a0),d0 + btst DCPGCR_DCPGBSY,d0 + lne + + # set mask + clr d0 + mov d0,(DCPGMR) + + # area purge + # + # DCPGCR = DCPGCR_DCP + # + mov DCPGCR_DCP,d0 + mov d0,(a0) + + # wait for busy bit of area purge + setlb + mov (a0),d0 + btst DCPGCR_DCPGBSY,d0 + lne + +debugger_local_cache_flushinv_no_dcache: + # + # secondly, invalidate the icache if it is enabled + # + mov CHCTR,a0 + movhu (a0),d0 + btst CHCTR_ICEN,d0 + beq debugger_local_cache_flushinv_done + + invalidate_icache 0 + +debugger_local_cache_flushinv_done: + mov d1,epsw + +debugger_local_cache_flushinv_end: + ret [],0 + .size debugger_local_cache_flushinv,.-debugger_local_cache_flushinv + +############################################################################### +# +# void debugger_local_cache_flushinv_one(u8 *addr) +# +# Invalidate one particular cacheline if it's in the icache +# +############################################################################### + ALIGN + .globl debugger_local_cache_flushinv_one + .type debugger_local_cache_flushinv_one,@function +debugger_local_cache_flushinv_one: + movhu (CHCTR),d1 + btst CHCTR_DCEN|CHCTR_ICEN,d1 + beq debugger_local_cache_flushinv_one_end + btst CHCTR_DCEN,d1 + beq debugger_local_cache_flushinv_one_no_dcache + + # round cacheline addr down + and L1_CACHE_TAG_MASK,d0 + mov d0,a1 + mov d0,d1 + + # determine the dcache purge control reg address + mov DCACHE_PURGE(0,0),a0 + and L1_CACHE_TAG_ENTRY,d0 + add d0,a0 + + # retain valid entries in the cache + or L1_CACHE_TAG_VALID,d1 + + # conditionally purge this line in all ways + mov d1,(L1_CACHE_WAYDISP*0,a0) + +debugger_local_cache_flushinv_one_no_dcache: + # + # now try to flush the icache + # + mov CHCTR,a0 + movhu (a0),d0 + btst CHCTR_ICEN,d0 + beq debugger_local_cache_flushinv_one_end + + LOCAL_CLI_SAVE(d1) + + mov ICIVCR,a0 + + # wait for the invalidator to quiesce + setlb + mov (a0),d0 + btst ICIVCR_ICIVBSY,d0 + lne + + # set the mask + mov L1_CACHE_TAG_MASK,d0 + mov d0,(ICIVMR) + + # invalidate the cache line at the given address + or ICIVCR_ICI,a1 + mov a1,(a0) + + # wait for the invalidator to quiesce again + setlb + mov (a0),d0 + btst ICIVCR_ICIVBSY,d0 + lne + + LOCAL_IRQ_RESTORE(d1) + +debugger_local_cache_flushinv_one_end: + ret [],0 + .size debugger_local_cache_flushinv_one,.-debugger_local_cache_flushinv_one diff --git a/arch/mn10300/mm/cache-dbg-flush-by-tag.S b/arch/mn10300/mm/cache-dbg-flush-by-tag.S new file mode 100644 index 00000000..bf56930e --- /dev/null +++ b/arch/mn10300/mm/cache-dbg-flush-by-tag.S @@ -0,0 +1,114 @@ +/* MN10300 CPU cache invalidation routines, using direct tag flushing + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/page.h> +#include <asm/cache.h> +#include <asm/irqflags.h> +#include <asm/cacheflush.h> +#include "cache.inc" + + .am33_2 + +############################################################################### +# +# void debugger_local_cache_flushinv(void) +# +# Flush the entire data cache back to RAM and invalidate the icache +# +############################################################################### + ALIGN + .globl debugger_local_cache_flushinv + .type debugger_local_cache_flushinv,@function +debugger_local_cache_flushinv: + # + # firstly flush the dcache + # + movhu (CHCTR),d0 + btst CHCTR_DCEN|CHCTR_ICEN,d0 + beq debugger_local_cache_flushinv_end + + btst CHCTR_DCEN,d0 + beq debugger_local_cache_flushinv_no_dcache + + # read the addresses tagged in the cache's tag RAM and attempt to flush + # those addresses specifically + # - we rely on the hardware to filter out invalid tag entry addresses + mov DCACHE_TAG(0,0),a0 # dcache tag RAM access address + mov DCACHE_PURGE(0,0),a1 # dcache purge request address + mov L1_CACHE_NWAYS*L1_CACHE_NENTRIES,e0 # total number of entries + +mn10300_local_dcache_flush_loop: + mov (a0),d0 + and L1_CACHE_TAG_MASK,d0 + or L1_CACHE_TAG_VALID,d0 # retain valid entries in the + # cache + mov d0,(a1) # conditional purge + + add L1_CACHE_BYTES,a0 + add L1_CACHE_BYTES,a1 + add -1,e0 + bne mn10300_local_dcache_flush_loop + +debugger_local_cache_flushinv_no_dcache: + # + # secondly, invalidate the icache if it is enabled + # + mov CHCTR,a0 + movhu (a0),d0 + btst CHCTR_ICEN,d0 + beq debugger_local_cache_flushinv_end + + invalidate_icache 1 + +debugger_local_cache_flushinv_end: + ret [],0 + .size debugger_local_cache_flushinv,.-debugger_local_cache_flushinv + +############################################################################### +# +# void debugger_local_cache_flushinv_one(u8 *addr) +# +# Invalidate one particular cacheline if it's in the icache +# +############################################################################### + ALIGN + .globl debugger_local_cache_flushinv_one + .type debugger_local_cache_flushinv_one,@function +debugger_local_cache_flushinv_one: + movhu (CHCTR),d1 + btst CHCTR_DCEN|CHCTR_ICEN,d1 + beq debugger_local_cache_flushinv_one_end + btst CHCTR_DCEN,d1 + beq debugger_local_cache_flushinv_one_icache + + # round cacheline addr down + and L1_CACHE_TAG_MASK,d0 + mov d0,a1 + + # determine the dcache purge control reg address + mov DCACHE_PURGE(0,0),a0 + and L1_CACHE_TAG_ENTRY,d0 + add d0,a0 + + # retain valid entries in the cache + or L1_CACHE_TAG_VALID,a1 + + # conditionally purge this line in all ways + mov a1,(L1_CACHE_WAYDISP*0,a0) + + # now go and do the icache + bra debugger_local_cache_flushinv_one_icache + +debugger_local_cache_flushinv_one_end: + ret [],0 + .size debugger_local_cache_flushinv_one,.-debugger_local_cache_flushinv_one diff --git a/arch/mn10300/mm/cache-dbg-inv-by-reg.S b/arch/mn10300/mm/cache-dbg-inv-by-reg.S new file mode 100644 index 00000000..c4e62529 --- /dev/null +++ b/arch/mn10300/mm/cache-dbg-inv-by-reg.S @@ -0,0 +1,69 @@ +/* MN10300 CPU cache invalidation routines, using automatic purge registers + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/cache.h> +#include <asm/irqflags.h> +#include <asm/cacheflush.h> +#include "cache.inc" + + .am33_2 + + .globl debugger_local_cache_flushinv_one + +############################################################################### +# +# void debugger_local_cache_flushinv_one(u8 *addr) +# +# Invalidate one particular cacheline if it's in the icache +# +############################################################################### + ALIGN + .globl debugger_local_cache_flushinv_one + .type debugger_local_cache_flushinv_one,@function +debugger_local_cache_flushinv_one: + mov d0,a1 + + mov CHCTR,a0 + movhu (a0),d0 + btst CHCTR_ICEN,d0 + beq mn10300_local_icache_inv_range_reg_end + + LOCAL_CLI_SAVE(d1) + + mov ICIVCR,a0 + + # wait for the invalidator to quiesce + setlb + mov (a0),d0 + btst ICIVCR_ICIVBSY,d0 + lne + + # set the mask + mov ~L1_CACHE_TAG_MASK,d0 + mov d0,(ICIVMR) + + # invalidate the cache line at the given address + and ~L1_CACHE_TAG_MASK,a1 + or ICIVCR_ICI,a1 + mov a1,(a0) + + # wait for the invalidator to quiesce again + setlb + mov (a0),d0 + btst ICIVCR_ICIVBSY,d0 + lne + + LOCAL_IRQ_RESTORE(d1) + +mn10300_local_icache_inv_range_reg_end: + ret [],0 + .size debugger_local_cache_flushinv_one,.-debugger_local_cache_flushinv_one diff --git a/arch/mn10300/mm/cache-dbg-inv-by-tag.S b/arch/mn10300/mm/cache-dbg-inv-by-tag.S new file mode 100644 index 00000000..d8ec821e --- /dev/null +++ b/arch/mn10300/mm/cache-dbg-inv-by-tag.S @@ -0,0 +1,120 @@ +/* MN10300 CPU cache invalidation routines, using direct tag flushing + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/page.h> +#include <asm/cache.h> +#include <asm/irqflags.h> +#include <asm/cacheflush.h> +#include "cache.inc" + + .am33_2 + + .globl debugger_local_cache_flushinv_one_icache + +############################################################################### +# +# void debugger_local_cache_flushinv_one(u8 *addr) +# +# Invalidate one particular cacheline if it's in the icache +# +############################################################################### + ALIGN + .globl debugger_local_cache_flushinv_one_icache + .type debugger_local_cache_flushinv_one_icache,@function +debugger_local_cache_flushinv_one_icache: + movm [d3,a2],(sp) + + mov CHCTR,a2 + movhu (a2),d0 + btst CHCTR_ICEN,d0 + beq debugger_local_cache_flushinv_one_icache_end + + mov d0,a1 + and L1_CACHE_TAG_MASK,a1 + + # read the tags from the tag RAM, and if they indicate a matching valid + # cache line then we invalidate that line + mov ICACHE_TAG(0,0),a0 + mov a1,d0 + and L1_CACHE_TAG_ENTRY,d0 + add d0,a0 # starting icache tag RAM + # access address + + and ~(L1_CACHE_DISPARITY-1),a1 # determine comparator base + or L1_CACHE_TAG_VALID,a1 + mov L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_VALID,d1 + + LOCAL_CLI_SAVE(d3) + + # disable the icache + movhu (a2),d0 + and ~CHCTR_ICEN,d0 + movhu d0,(a2) + + # and wait for it to calm down + setlb + movhu (a2),d0 + btst CHCTR_ICBUSY,d0 + lne + + # check all the way tags for this cache entry + mov (a0),d0 # read the tag in the way 0 slot + xor a1,d0 + and d1,d0 + beq debugger_local_icache_kill # jump if matched + + add L1_CACHE_WAYDISP,a0 + mov (a0),d0 # read the tag in the way 1 slot + xor a1,d0 + and d1,d0 + beq debugger_local_icache_kill # jump if matched + + add L1_CACHE_WAYDISP,a0 + mov (a0),d0 # read the tag in the way 2 slot + xor a1,d0 + and d1,d0 + beq debugger_local_icache_kill # jump if matched + + add L1_CACHE_WAYDISP,a0 + mov (a0),d0 # read the tag in the way 3 slot + xor a1,d0 + and d1,d0 + bne debugger_local_icache_finish # jump if not matched + +debugger_local_icache_kill: + mov d0,(a0) # kill the tag (D0 is 0 at this point) + +debugger_local_icache_finish: + # wait for the cache to finish what it's doing + setlb + movhu (a2),d0 + btst CHCTR_ICBUSY,d0 + lne + + # and reenable it + or CHCTR_ICEN,d0 + movhu d0,(a2) + movhu (a2),d0 + + # re-enable interrupts + LOCAL_IRQ_RESTORE(d3) + +debugger_local_cache_flushinv_one_icache_end: + ret [d3,a2],8 + .size debugger_local_cache_flushinv_one_icache,.-debugger_local_cache_flushinv_one_icache + +#ifdef CONFIG_MN10300_DEBUGGER_CACHE_INV_BY_TAG + .globl debugger_local_cache_flushinv_one + .type debugger_local_cache_flushinv_one,@function +debugger_local_cache_flushinv_one = debugger_local_cache_flushinv_one_icache +#endif diff --git a/arch/mn10300/mm/cache-dbg-inv.S b/arch/mn10300/mm/cache-dbg-inv.S new file mode 100644 index 00000000..eba2d6dc --- /dev/null +++ b/arch/mn10300/mm/cache-dbg-inv.S @@ -0,0 +1,47 @@ +/* MN10300 CPU cache invalidation routines + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/page.h> +#include <asm/cache.h> +#include <asm/irqflags.h> +#include <asm/cacheflush.h> +#include "cache.inc" + + .am33_2 + + .globl debugger_local_cache_flushinv + +############################################################################### +# +# void debugger_local_cache_flushinv(void) +# +# Invalidate the entire icache +# +############################################################################### + ALIGN + .globl debugger_local_cache_flushinv + .type debugger_local_cache_flushinv,@function +debugger_local_cache_flushinv: + # + # we only need to invalidate the icache in this cache mode + # + mov CHCTR,a0 + movhu (a0),d0 + btst CHCTR_ICEN,d0 + beq debugger_local_cache_flushinv_end + + invalidate_icache 1 + +debugger_local_cache_flushinv_end: + ret [],0 + .size debugger_local_cache_flushinv,.-debugger_local_cache_flushinv diff --git a/arch/mn10300/mm/cache-disabled.c b/arch/mn10300/mm/cache-disabled.c new file mode 100644 index 00000000..f669ea42 --- /dev/null +++ b/arch/mn10300/mm/cache-disabled.c @@ -0,0 +1,21 @@ +/* Handle the cache being disabled + * + * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/mm.h> + +/* + * allow userspace to flush the instruction cache + */ +asmlinkage long sys_cacheflush(unsigned long start, unsigned long end) +{ + if (end < start) + return -EINVAL; + return 0; +} diff --git a/arch/mn10300/mm/cache-flush-by-reg.S b/arch/mn10300/mm/cache-flush-by-reg.S new file mode 100644 index 00000000..1dcae021 --- /dev/null +++ b/arch/mn10300/mm/cache-flush-by-reg.S @@ -0,0 +1,308 @@ +/* MN10300 CPU core caching routines, using indirect regs on cache controller + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/page.h> +#include <asm/cache.h> +#include <asm/irqflags.h> + + .am33_2 + +#ifndef CONFIG_SMP + .globl mn10300_dcache_flush + .globl mn10300_dcache_flush_page + .globl mn10300_dcache_flush_range + .globl mn10300_dcache_flush_range2 + .globl mn10300_dcache_flush_inv + .globl mn10300_dcache_flush_inv_page + .globl mn10300_dcache_flush_inv_range + .globl mn10300_dcache_flush_inv_range2 + +mn10300_dcache_flush = mn10300_local_dcache_flush +mn10300_dcache_flush_page = mn10300_local_dcache_flush_page +mn10300_dcache_flush_range = mn10300_local_dcache_flush_range +mn10300_dcache_flush_range2 = mn10300_local_dcache_flush_range2 +mn10300_dcache_flush_inv = mn10300_local_dcache_flush_inv +mn10300_dcache_flush_inv_page = mn10300_local_dcache_flush_inv_page +mn10300_dcache_flush_inv_range = mn10300_local_dcache_flush_inv_range +mn10300_dcache_flush_inv_range2 = mn10300_local_dcache_flush_inv_range2 + +#endif /* !CONFIG_SMP */ + +############################################################################### +# +# void mn10300_local_dcache_flush(void) +# Flush the entire data cache back to RAM +# +############################################################################### + ALIGN + .globl mn10300_local_dcache_flush + .type mn10300_local_dcache_flush,@function +mn10300_local_dcache_flush: + movhu (CHCTR),d0 + btst CHCTR_DCEN,d0 + beq mn10300_local_dcache_flush_end + + mov DCPGCR,a0 + + LOCAL_CLI_SAVE(d1) + + # wait for busy bit of area purge + setlb + mov (a0),d0 + btst DCPGCR_DCPGBSY,d0 + lne + + # set mask + clr d0 + mov d0,(DCPGMR) + + # area purge + # + # DCPGCR = DCPGCR_DCP + # + mov DCPGCR_DCP,d0 + mov d0,(a0) + + # wait for busy bit of area purge + setlb + mov (a0),d0 + btst DCPGCR_DCPGBSY,d0 + lne + + LOCAL_IRQ_RESTORE(d1) + +mn10300_local_dcache_flush_end: + ret [],0 + .size mn10300_local_dcache_flush,.-mn10300_local_dcache_flush + +############################################################################### +# +# void mn10300_local_dcache_flush_page(unsigned long start) +# void mn10300_local_dcache_flush_range(unsigned long start, unsigned long end) +# void mn10300_local_dcache_flush_range2(unsigned long start, unsigned long size) +# Flush a range of addresses on a page in the dcache +# +############################################################################### + ALIGN + .globl mn10300_local_dcache_flush_page + .globl mn10300_local_dcache_flush_range + .globl mn10300_local_dcache_flush_range2 + .type mn10300_local_dcache_flush_page,@function + .type mn10300_local_dcache_flush_range,@function + .type mn10300_local_dcache_flush_range2,@function +mn10300_local_dcache_flush_page: + and ~(PAGE_SIZE-1),d0 + mov PAGE_SIZE,d1 +mn10300_local_dcache_flush_range2: + add d0,d1 +mn10300_local_dcache_flush_range: + movm [d2,d3,a2],(sp) + + movhu (CHCTR),d2 + btst CHCTR_DCEN,d2 + beq mn10300_local_dcache_flush_range_end + + # calculate alignsize + # + # alignsize = L1_CACHE_BYTES; + # for (i = (end - start - 1) / L1_CACHE_BYTES ; i > 0; i >>= 1) + # alignsize <<= 1; + # d2 = alignsize; + # + mov L1_CACHE_BYTES,d2 + sub d0,d1,d3 + add -1,d3 + lsr L1_CACHE_SHIFT,d3 + beq 2f +1: + add d2,d2 + lsr 1,d3 + bne 1b +2: + mov d1,a1 # a1 = end + + LOCAL_CLI_SAVE(d3) + mov DCPGCR,a0 + + # wait for busy bit of area purge + setlb + mov (a0),d1 + btst DCPGCR_DCPGBSY,d1 + lne + + # determine the mask + mov d2,d1 + add -1,d1 + not d1 # d1 = mask = ~(alignsize-1) + mov d1,(DCPGMR) + + and d1,d0,a2 # a2 = mask & start + +dcpgloop: + # area purge + mov a2,d0 + or DCPGCR_DCP,d0 + mov d0,(a0) # DCPGCR = (mask & start) | DCPGCR_DCP + + # wait for busy bit of area purge + setlb + mov (a0),d1 + btst DCPGCR_DCPGBSY,d1 + lne + + # check purge of end address + add d2,a2 # a2 += alignsize + cmp a1,a2 # if (a2 < end) goto dcpgloop + bns dcpgloop + + LOCAL_IRQ_RESTORE(d3) + +mn10300_local_dcache_flush_range_end: + ret [d2,d3,a2],12 + + .size mn10300_local_dcache_flush_page,.-mn10300_local_dcache_flush_page + .size mn10300_local_dcache_flush_range,.-mn10300_local_dcache_flush_range + .size mn10300_local_dcache_flush_range2,.-mn10300_local_dcache_flush_range2 + +############################################################################### +# +# void mn10300_local_dcache_flush_inv(void) +# Flush the entire data cache and invalidate all entries +# +############################################################################### + ALIGN + .globl mn10300_local_dcache_flush_inv + .type mn10300_local_dcache_flush_inv,@function +mn10300_local_dcache_flush_inv: + movhu (CHCTR),d0 + btst CHCTR_DCEN,d0 + beq mn10300_local_dcache_flush_inv_end + + mov DCPGCR,a0 + + LOCAL_CLI_SAVE(d1) + + # wait for busy bit of area purge & invalidate + setlb + mov (a0),d0 + btst DCPGCR_DCPGBSY,d0 + lne + + # set the mask to cover everything + clr d0 + mov d0,(DCPGMR) + + # area purge & invalidate + mov DCPGCR_DCP|DCPGCR_DCI,d0 + mov d0,(a0) + + # wait for busy bit of area purge & invalidate + setlb + mov (a0),d0 + btst DCPGCR_DCPGBSY,d0 + lne + + LOCAL_IRQ_RESTORE(d1) + +mn10300_local_dcache_flush_inv_end: + ret [],0 + .size mn10300_local_dcache_flush_inv,.-mn10300_local_dcache_flush_inv + +############################################################################### +# +# void mn10300_local_dcache_flush_inv_page(unsigned long start) +# void mn10300_local_dcache_flush_inv_range(unsigned long start, unsigned long end) +# void mn10300_local_dcache_flush_inv_range2(unsigned long start, unsigned long size) +# Flush and invalidate a range of addresses on a page in the dcache +# +############################################################################### + ALIGN + .globl mn10300_local_dcache_flush_inv_page + .globl mn10300_local_dcache_flush_inv_range + .globl mn10300_local_dcache_flush_inv_range2 + .type mn10300_local_dcache_flush_inv_page,@function + .type mn10300_local_dcache_flush_inv_range,@function + .type mn10300_local_dcache_flush_inv_range2,@function +mn10300_local_dcache_flush_inv_page: + and ~(PAGE_SIZE-1),d0 + mov PAGE_SIZE,d1 +mn10300_local_dcache_flush_inv_range2: + add d0,d1 +mn10300_local_dcache_flush_inv_range: + movm [d2,d3,a2],(sp) + + movhu (CHCTR),d2 + btst CHCTR_DCEN,d2 + beq mn10300_local_dcache_flush_inv_range_end + + # calculate alignsize + # + # alignsize = L1_CACHE_BYTES; + # for (i = (end - start - 1) / L1_CACHE_BYTES; i > 0; i >>= 1) + # alignsize <<= 1; + # d2 = alignsize + # + mov L1_CACHE_BYTES,d2 + sub d0,d1,d3 + add -1,d3 + lsr L1_CACHE_SHIFT,d3 + beq 2f +1: + add d2,d2 + lsr 1,d3 + bne 1b +2: + mov d1,a1 # a1 = end + + LOCAL_CLI_SAVE(d3) + mov DCPGCR,a0 + + # wait for busy bit of area purge & invalidate + setlb + mov (a0),d1 + btst DCPGCR_DCPGBSY,d1 + lne + + # set the mask + mov d2,d1 + add -1,d1 + not d1 # d1 = mask = ~(alignsize-1) + mov d1,(DCPGMR) + + and d1,d0,a2 # a2 = mask & start + +dcpgivloop: + # area purge & invalidate + mov a2,d0 + or DCPGCR_DCP|DCPGCR_DCI,d0 + mov d0,(a0) # DCPGCR = (mask & start)|DCPGCR_DCP|DCPGCR_DCI + + # wait for busy bit of area purge & invalidate + setlb + mov (a0),d1 + btst DCPGCR_DCPGBSY,d1 + lne + + # check purge & invalidate of end address + add d2,a2 # a2 += alignsize + cmp a1,a2 # if (a2 < end) goto dcpgivloop + bns dcpgivloop + + LOCAL_IRQ_RESTORE(d3) + +mn10300_local_dcache_flush_inv_range_end: + ret [d2,d3,a2],12 + .size mn10300_local_dcache_flush_inv_page,.-mn10300_local_dcache_flush_inv_page + .size mn10300_local_dcache_flush_inv_range,.-mn10300_local_dcache_flush_inv_range + .size mn10300_local_dcache_flush_inv_range2,.-mn10300_local_dcache_flush_inv_range2 diff --git a/arch/mn10300/mm/cache-flush-by-tag.S b/arch/mn10300/mm/cache-flush-by-tag.S new file mode 100644 index 00000000..1ddc0684 --- /dev/null +++ b/arch/mn10300/mm/cache-flush-by-tag.S @@ -0,0 +1,250 @@ +/* MN10300 CPU core caching routines, using direct tag flushing + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/page.h> +#include <asm/cache.h> +#include <asm/irqflags.h> + + .am33_2 + +#ifndef CONFIG_SMP + .globl mn10300_dcache_flush + .globl mn10300_dcache_flush_page + .globl mn10300_dcache_flush_range + .globl mn10300_dcache_flush_range2 + .globl mn10300_dcache_flush_inv + .globl mn10300_dcache_flush_inv_page + .globl mn10300_dcache_flush_inv_range + .globl mn10300_dcache_flush_inv_range2 + +mn10300_dcache_flush = mn10300_local_dcache_flush +mn10300_dcache_flush_page = mn10300_local_dcache_flush_page +mn10300_dcache_flush_range = mn10300_local_dcache_flush_range +mn10300_dcache_flush_range2 = mn10300_local_dcache_flush_range2 +mn10300_dcache_flush_inv = mn10300_local_dcache_flush_inv +mn10300_dcache_flush_inv_page = mn10300_local_dcache_flush_inv_page +mn10300_dcache_flush_inv_range = mn10300_local_dcache_flush_inv_range +mn10300_dcache_flush_inv_range2 = mn10300_local_dcache_flush_inv_range2 + +#endif /* !CONFIG_SMP */ + +############################################################################### +# +# void mn10300_local_dcache_flush(void) +# Flush the entire data cache back to RAM +# +############################################################################### + ALIGN + .globl mn10300_local_dcache_flush + .type mn10300_local_dcache_flush,@function +mn10300_local_dcache_flush: + movhu (CHCTR),d0 + btst CHCTR_DCEN,d0 + beq mn10300_local_dcache_flush_end + + # read the addresses tagged in the cache's tag RAM and attempt to flush + # those addresses specifically + # - we rely on the hardware to filter out invalid tag entry addresses + mov DCACHE_TAG(0,0),a0 # dcache tag RAM access address + mov DCACHE_PURGE(0,0),a1 # dcache purge request address + mov L1_CACHE_NWAYS*L1_CACHE_NENTRIES,d1 # total number of entries + +mn10300_local_dcache_flush_loop: + mov (a0),d0 + and L1_CACHE_TAG_MASK,d0 + or L1_CACHE_TAG_VALID,d0 # retain valid entries in the + # cache + mov d0,(a1) # conditional purge + + add L1_CACHE_BYTES,a0 + add L1_CACHE_BYTES,a1 + add -1,d1 + bne mn10300_local_dcache_flush_loop + +mn10300_local_dcache_flush_end: + ret [],0 + .size mn10300_local_dcache_flush,.-mn10300_local_dcache_flush + +############################################################################### +# +# void mn10300_local_dcache_flush_page(unsigned long start) +# void mn10300_local_dcache_flush_range(unsigned long start, unsigned long end) +# void mn10300_local_dcache_flush_range2(unsigned long start, unsigned long size) +# Flush a range of addresses on a page in the dcache +# +############################################################################### + ALIGN + .globl mn10300_local_dcache_flush_page + .globl mn10300_local_dcache_flush_range + .globl mn10300_local_dcache_flush_range2 + .type mn10300_local_dcache_flush_page,@function + .type mn10300_local_dcache_flush_range,@function + .type mn10300_local_dcache_flush_range2,@function +mn10300_local_dcache_flush_page: + and ~(PAGE_SIZE-1),d0 + mov PAGE_SIZE,d1 +mn10300_local_dcache_flush_range2: + add d0,d1 +mn10300_local_dcache_flush_range: + movm [d2],(sp) + + movhu (CHCTR),d2 + btst CHCTR_DCEN,d2 + beq mn10300_local_dcache_flush_range_end + + sub d0,d1,a0 + cmp MN10300_DCACHE_FLUSH_BORDER,a0 + ble 1f + + movm (sp),[d2] + bra mn10300_local_dcache_flush +1: + + # round start addr down + and L1_CACHE_TAG_MASK,d0 + mov d0,a1 + + add L1_CACHE_BYTES,d1 # round end addr up + and L1_CACHE_TAG_MASK,d1 + + # write a request to flush all instances of an address from the cache + mov DCACHE_PURGE(0,0),a0 + mov a1,d0 + and L1_CACHE_TAG_ENTRY,d0 + add d0,a0 # starting dcache purge control + # reg address + + sub a1,d1 + lsr L1_CACHE_SHIFT,d1 # total number of entries to + # examine + + or L1_CACHE_TAG_VALID,a1 # retain valid entries in the + # cache + +mn10300_local_dcache_flush_range_loop: + mov a1,(L1_CACHE_WAYDISP*0,a0) # conditionally purge this line + # all ways + + add L1_CACHE_BYTES,a0 + add L1_CACHE_BYTES,a1 + and ~L1_CACHE_WAYDISP,a0 # make sure way stay on way 0 + add -1,d1 + bne mn10300_local_dcache_flush_range_loop + +mn10300_local_dcache_flush_range_end: + ret [d2],4 + + .size mn10300_local_dcache_flush_page,.-mn10300_local_dcache_flush_page + .size mn10300_local_dcache_flush_range,.-mn10300_local_dcache_flush_range + .size mn10300_local_dcache_flush_range2,.-mn10300_local_dcache_flush_range2 + +############################################################################### +# +# void mn10300_local_dcache_flush_inv(void) +# Flush the entire data cache and invalidate all entries +# +############################################################################### + ALIGN + .globl mn10300_local_dcache_flush_inv + .type mn10300_local_dcache_flush_inv,@function +mn10300_local_dcache_flush_inv: + movhu (CHCTR),d0 + btst CHCTR_DCEN,d0 + beq mn10300_local_dcache_flush_inv_end + + mov L1_CACHE_NENTRIES,d1 + clr a1 + +mn10300_local_dcache_flush_inv_loop: + mov (DCACHE_PURGE_WAY0(0),a1),d0 # unconditional purge + mov (DCACHE_PURGE_WAY1(0),a1),d0 # unconditional purge + mov (DCACHE_PURGE_WAY2(0),a1),d0 # unconditional purge + mov (DCACHE_PURGE_WAY3(0),a1),d0 # unconditional purge + + add L1_CACHE_BYTES,a1 + add -1,d1 + bne mn10300_local_dcache_flush_inv_loop + +mn10300_local_dcache_flush_inv_end: + ret [],0 + .size mn10300_local_dcache_flush_inv,.-mn10300_local_dcache_flush_inv + +############################################################################### +# +# void mn10300_local_dcache_flush_inv_page(unsigned long start) +# void mn10300_local_dcache_flush_inv_range(unsigned long start, unsigned long end) +# void mn10300_local_dcache_flush_inv_range2(unsigned long start, unsigned long size) +# Flush and invalidate a range of addresses on a page in the dcache +# +############################################################################### + ALIGN + .globl mn10300_local_dcache_flush_inv_page + .globl mn10300_local_dcache_flush_inv_range + .globl mn10300_local_dcache_flush_inv_range2 + .type mn10300_local_dcache_flush_inv_page,@function + .type mn10300_local_dcache_flush_inv_range,@function + .type mn10300_local_dcache_flush_inv_range2,@function +mn10300_local_dcache_flush_inv_page: + and ~(PAGE_SIZE-1),d0 + mov PAGE_SIZE,d1 +mn10300_local_dcache_flush_inv_range2: + add d0,d1 +mn10300_local_dcache_flush_inv_range: + movm [d2],(sp) + + movhu (CHCTR),d2 + btst CHCTR_DCEN,d2 + beq mn10300_local_dcache_flush_inv_range_end + + sub d0,d1,a0 + cmp MN10300_DCACHE_FLUSH_INV_BORDER,a0 + ble 1f + + movm (sp),[d2] + bra mn10300_local_dcache_flush_inv +1: + + and L1_CACHE_TAG_MASK,d0 # round start addr down + mov d0,a1 + + add L1_CACHE_BYTES,d1 # round end addr up + and L1_CACHE_TAG_MASK,d1 + + # write a request to flush and invalidate all instances of an address + # from the cache + mov DCACHE_PURGE(0,0),a0 + mov a1,d0 + and L1_CACHE_TAG_ENTRY,d0 + add d0,a0 # starting dcache purge control + # reg address + + sub a1,d1 + lsr L1_CACHE_SHIFT,d1 # total number of entries to + # examine + +mn10300_local_dcache_flush_inv_range_loop: + mov a1,(L1_CACHE_WAYDISP*0,a0) # conditionally purge this line + # in all ways + + add L1_CACHE_BYTES,a0 + add L1_CACHE_BYTES,a1 + and ~L1_CACHE_WAYDISP,a0 # make sure way stay on way 0 + add -1,d1 + bne mn10300_local_dcache_flush_inv_range_loop + +mn10300_local_dcache_flush_inv_range_end: + ret [d2],4 + .size mn10300_local_dcache_flush_inv_page,.-mn10300_local_dcache_flush_inv_page + .size mn10300_local_dcache_flush_inv_range,.-mn10300_local_dcache_flush_inv_range + .size mn10300_local_dcache_flush_inv_range2,.-mn10300_local_dcache_flush_inv_range2 diff --git a/arch/mn10300/mm/cache-flush-icache.c b/arch/mn10300/mm/cache-flush-icache.c new file mode 100644 index 00000000..fdb1a9db --- /dev/null +++ b/arch/mn10300/mm/cache-flush-icache.c @@ -0,0 +1,155 @@ +/* Flush dcache and invalidate icache when the dcache is in writeback mode + * + * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/mm.h> +#include <asm/cacheflush.h> +#include <asm/smp.h> +#include "cache-smp.h" + +/** + * flush_icache_page - Flush a page from the dcache and invalidate the icache + * @vma: The VMA the page is part of. + * @page: The page to be flushed. + * + * Write a page back from the dcache and invalidate the icache so that we can + * run code from it that we've just written into it + */ +void flush_icache_page(struct vm_area_struct *vma, struct page *page) +{ + unsigned long start = page_to_phys(page); + unsigned long flags; + + flags = smp_lock_cache(); + + mn10300_local_dcache_flush_page(start); + mn10300_local_icache_inv_page(start); + + smp_cache_call(SMP_IDCACHE_INV_FLUSH_RANGE, start, start + PAGE_SIZE); + smp_unlock_cache(flags); +} +EXPORT_SYMBOL(flush_icache_page); + +/** + * flush_icache_page_range - Flush dcache and invalidate icache for part of a + * single page + * @start: The starting virtual address of the page part. + * @end: The ending virtual address of the page part. + * + * Flush the dcache and invalidate the icache for part of a single page, as + * determined by the virtual addresses given. The page must be in the paged + * area. + */ +static void flush_icache_page_range(unsigned long start, unsigned long end) +{ + unsigned long addr, size, off; + struct page *page; + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *ppte, pte; + + /* work out how much of the page to flush */ + off = start & ~PAGE_MASK; + size = end - start; + + /* get the physical address the page is mapped to from the page + * tables */ + pgd = pgd_offset(current->mm, start); + if (!pgd || !pgd_val(*pgd)) + return; + + pud = pud_offset(pgd, start); + if (!pud || !pud_val(*pud)) + return; + + pmd = pmd_offset(pud, start); + if (!pmd || !pmd_val(*pmd)) + return; + + ppte = pte_offset_map(pmd, start); + if (!ppte) + return; + pte = *ppte; + pte_unmap(ppte); + + if (pte_none(pte)) + return; + + page = pte_page(pte); + if (!page) + return; + + addr = page_to_phys(page); + + /* flush the dcache and invalidate the icache coverage on that + * region */ + mn10300_local_dcache_flush_range2(addr + off, size); + mn10300_local_icache_inv_range2(addr + off, size); + smp_cache_call(SMP_IDCACHE_INV_FLUSH_RANGE, start, end); +} + +/** + * flush_icache_range - Globally flush dcache and invalidate icache for region + * @start: The starting virtual address of the region. + * @end: The ending virtual address of the region. + * + * This is used by the kernel to globally flush some code it has just written + * from the dcache back to RAM and then to globally invalidate the icache over + * that region so that that code can be run on all CPUs in the system. + */ +void flush_icache_range(unsigned long start, unsigned long end) +{ + unsigned long start_page, end_page; + unsigned long flags; + + flags = smp_lock_cache(); + + if (end > 0x80000000UL) { + /* addresses above 0xa0000000 do not go through the cache */ + if (end > 0xa0000000UL) { + end = 0xa0000000UL; + if (start >= end) + goto done; + } + + /* kernel addresses between 0x80000000 and 0x9fffffff do not + * require page tables, so we just map such addresses + * directly */ + start_page = (start >= 0x80000000UL) ? start : 0x80000000UL; + mn10300_local_dcache_flush_range(start_page, end); + mn10300_local_icache_inv_range(start_page, end); + smp_cache_call(SMP_IDCACHE_INV_FLUSH_RANGE, start_page, end); + if (start_page == start) + goto done; + end = start_page; + } + + start_page = start & PAGE_MASK; + end_page = (end - 1) & PAGE_MASK; + + if (start_page == end_page) { + /* the first and last bytes are on the same page */ + flush_icache_page_range(start, end); + } else if (start_page + 1 == end_page) { + /* split over two virtually contiguous pages */ + flush_icache_page_range(start, end_page); + flush_icache_page_range(end_page, end); + } else { + /* more than 2 pages; just flush the entire cache */ + mn10300_dcache_flush(); + mn10300_icache_inv(); + smp_cache_call(SMP_IDCACHE_INV_FLUSH, 0, 0); + } + +done: + smp_unlock_cache(flags); +} +EXPORT_SYMBOL(flush_icache_range); diff --git a/arch/mn10300/mm/cache-inv-by-reg.S b/arch/mn10300/mm/cache-inv-by-reg.S new file mode 100644 index 00000000..a60825b9 --- /dev/null +++ b/arch/mn10300/mm/cache-inv-by-reg.S @@ -0,0 +1,350 @@ +/* MN10300 CPU cache invalidation routines, using automatic purge registers + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/page.h> +#include <asm/cache.h> +#include <asm/irqflags.h> +#include <asm/cacheflush.h> +#include "cache.inc" + +#define mn10300_local_dcache_inv_range_intr_interval \ + +((1 << MN10300_DCACHE_INV_RANGE_INTR_LOG2_INTERVAL) - 1) + +#if mn10300_local_dcache_inv_range_intr_interval > 0xff +#error MN10300_DCACHE_INV_RANGE_INTR_LOG2_INTERVAL must be 8 or less +#endif + + .am33_2 + +#ifndef CONFIG_SMP + .globl mn10300_icache_inv + .globl mn10300_icache_inv_page + .globl mn10300_icache_inv_range + .globl mn10300_icache_inv_range2 + .globl mn10300_dcache_inv + .globl mn10300_dcache_inv_page + .globl mn10300_dcache_inv_range + .globl mn10300_dcache_inv_range2 + +mn10300_icache_inv = mn10300_local_icache_inv +mn10300_icache_inv_page = mn10300_local_icache_inv_page +mn10300_icache_inv_range = mn10300_local_icache_inv_range +mn10300_icache_inv_range2 = mn10300_local_icache_inv_range2 +mn10300_dcache_inv = mn10300_local_dcache_inv +mn10300_dcache_inv_page = mn10300_local_dcache_inv_page +mn10300_dcache_inv_range = mn10300_local_dcache_inv_range +mn10300_dcache_inv_range2 = mn10300_local_dcache_inv_range2 + +#endif /* !CONFIG_SMP */ + +############################################################################### +# +# void mn10300_local_icache_inv(void) +# Invalidate the entire icache +# +############################################################################### + ALIGN + .globl mn10300_local_icache_inv + .type mn10300_local_icache_inv,@function +mn10300_local_icache_inv: + mov CHCTR,a0 + + movhu (a0),d0 + btst CHCTR_ICEN,d0 + beq mn10300_local_icache_inv_end + + invalidate_icache 1 + +mn10300_local_icache_inv_end: + ret [],0 + .size mn10300_local_icache_inv,.-mn10300_local_icache_inv + +############################################################################### +# +# void mn10300_local_dcache_inv(void) +# Invalidate the entire dcache +# +############################################################################### + ALIGN + .globl mn10300_local_dcache_inv + .type mn10300_local_dcache_inv,@function +mn10300_local_dcache_inv: + mov CHCTR,a0 + + movhu (a0),d0 + btst CHCTR_DCEN,d0 + beq mn10300_local_dcache_inv_end + + invalidate_dcache 1 + +mn10300_local_dcache_inv_end: + ret [],0 + .size mn10300_local_dcache_inv,.-mn10300_local_dcache_inv + +############################################################################### +# +# void mn10300_local_dcache_inv_range(unsigned long start, unsigned long end) +# void mn10300_local_dcache_inv_range2(unsigned long start, unsigned long size) +# void mn10300_local_dcache_inv_page(unsigned long start) +# Invalidate a range of addresses on a page in the dcache +# +############################################################################### + ALIGN + .globl mn10300_local_dcache_inv_page + .globl mn10300_local_dcache_inv_range + .globl mn10300_local_dcache_inv_range2 + .type mn10300_local_dcache_inv_page,@function + .type mn10300_local_dcache_inv_range,@function + .type mn10300_local_dcache_inv_range2,@function +mn10300_local_dcache_inv_page: + and ~(PAGE_SIZE-1),d0 + mov PAGE_SIZE,d1 +mn10300_local_dcache_inv_range2: + add d0,d1 +mn10300_local_dcache_inv_range: + # If we are in writeback mode we check the start and end alignments, + # and if they're not cacheline-aligned, we must flush any bits outside + # the range that share cachelines with stuff inside the range +#ifdef CONFIG_MN10300_CACHE_WBACK + btst ~L1_CACHE_TAG_MASK,d0 + bne 1f + btst ~L1_CACHE_TAG_MASK,d1 + beq 2f +1: + bra mn10300_local_dcache_flush_inv_range +2: +#endif /* CONFIG_MN10300_CACHE_WBACK */ + + movm [d2,d3,a2],(sp) + + mov CHCTR,a0 + movhu (a0),d2 + btst CHCTR_DCEN,d2 + beq mn10300_local_dcache_inv_range_end + + # round the addresses out to be full cachelines, unless we're in + # writeback mode, in which case we would be in flush and invalidate by + # now +#ifndef CONFIG_MN10300_CACHE_WBACK + and L1_CACHE_TAG_MASK,d0 # round start addr down + + mov L1_CACHE_BYTES-1,d2 + add d2,d1 + and L1_CACHE_TAG_MASK,d1 # round end addr up +#endif /* !CONFIG_MN10300_CACHE_WBACK */ + + sub d0,d1,d2 # calculate the total size + mov d0,a2 # A2 = start address + mov d1,a1 # A1 = end address + + LOCAL_CLI_SAVE(d3) + + mov DCPGCR,a0 # make sure the purger isn't busy + setlb + mov (a0),d0 + btst DCPGCR_DCPGBSY,d0 + lne + + # skip initial address alignment calculation if address is zero + mov d2,d1 + cmp 0,a2 + beq 1f + +dcivloop: + /* calculate alignsize + * + * alignsize = L1_CACHE_BYTES; + * while (! start & alignsize) { + * alignsize <<=1; + * } + * d1 = alignsize; + */ + mov L1_CACHE_BYTES,d1 + lsr 1,d1 + setlb + add d1,d1 + mov d1,d0 + and a2,d0 + leq + +1: + /* calculate invsize + * + * if (totalsize > alignsize) { + * invsize = alignsize; + * } else { + * invsize = totalsize; + * tmp = 0x80000000; + * while (! invsize & tmp) { + * tmp >>= 1; + * } + * invsize = tmp; + * } + * d1 = invsize + */ + cmp d2,d1 + bns 2f + mov d2,d1 + + mov 0x80000000,d0 # start from 31bit=1 + setlb + lsr 1,d0 + mov d0,e0 + and d1,e0 + leq + mov d0,d1 + +2: + /* set mask + * + * mask = ~(invsize-1); + * DCPGMR = mask; + */ + mov d1,d0 + add -1,d0 + not d0 + mov d0,(DCPGMR) + + # invalidate area + mov a2,d0 + or DCPGCR_DCI,d0 + mov d0,(a0) # DCPGCR = (mask & start) | DCPGCR_DCI + + setlb # wait for the purge to complete + mov (a0),d0 + btst DCPGCR_DCPGBSY,d0 + lne + + sub d1,d2 # decrease size remaining + add d1,a2 # increase next start address + + /* check invalidating of end address + * + * a2 = a2 + invsize + * if (a2 < end) { + * goto dcivloop; + * } */ + cmp a1,a2 + bns dcivloop + + LOCAL_IRQ_RESTORE(d3) + +mn10300_local_dcache_inv_range_end: + ret [d2,d3,a2],12 + .size mn10300_local_dcache_inv_page,.-mn10300_local_dcache_inv_page + .size mn10300_local_dcache_inv_range,.-mn10300_local_dcache_inv_range + .size mn10300_local_dcache_inv_range2,.-mn10300_local_dcache_inv_range2 + +############################################################################### +# +# void mn10300_local_icache_inv_page(unsigned long start) +# void mn10300_local_icache_inv_range2(unsigned long start, unsigned long size) +# void mn10300_local_icache_inv_range(unsigned long start, unsigned long end) +# Invalidate a range of addresses on a page in the icache +# +############################################################################### + ALIGN + .globl mn10300_local_icache_inv_page + .globl mn10300_local_icache_inv_range + .globl mn10300_local_icache_inv_range2 + .type mn10300_local_icache_inv_page,@function + .type mn10300_local_icache_inv_range,@function + .type mn10300_local_icache_inv_range2,@function +mn10300_local_icache_inv_page: + and ~(PAGE_SIZE-1),d0 + mov PAGE_SIZE,d1 +mn10300_local_icache_inv_range2: + add d0,d1 +mn10300_local_icache_inv_range: + movm [d2,d3,a2],(sp) + + mov CHCTR,a0 + movhu (a0),d2 + btst CHCTR_ICEN,d2 + beq mn10300_local_icache_inv_range_reg_end + + /* calculate alignsize + * + * alignsize = L1_CACHE_BYTES; + * for (i = (end - start - 1) / L1_CACHE_BYTES ; i > 0; i >>= 1) { + * alignsize <<= 1; + * } + * d2 = alignsize; + */ + mov L1_CACHE_BYTES,d2 + sub d0,d1,d3 + add -1,d3 + lsr L1_CACHE_SHIFT,d3 + beq 2f +1: + add d2,d2 + lsr 1,d3 + bne 1b +2: + + /* a1 = end */ + mov d1,a1 + + LOCAL_CLI_SAVE(d3) + + mov ICIVCR,a0 + /* wait for busy bit of area invalidation */ + setlb + mov (a0),d1 + btst ICIVCR_ICIVBSY,d1 + lne + + /* set mask + * + * mask = ~(alignsize-1); + * ICIVMR = mask; + */ + mov d2,d1 + add -1,d1 + not d1 + mov d1,(ICIVMR) + /* a2 = mask & start */ + and d1,d0,a2 + +icivloop: + /* area invalidate + * + * ICIVCR = (mask & start) | ICIVCR_ICI + */ + mov a2,d0 + or ICIVCR_ICI,d0 + mov d0,(a0) + + /* wait for busy bit of area invalidation */ + setlb + mov (a0),d1 + btst ICIVCR_ICIVBSY,d1 + lne + + /* check invalidating of end address + * + * a2 = a2 + alignsize + * if (a2 < end) { + * goto icivloop; + * } */ + add d2,a2 + cmp a1,a2 + bns icivloop + + LOCAL_IRQ_RESTORE(d3) + +mn10300_local_icache_inv_range_reg_end: + ret [d2,d3,a2],12 + .size mn10300_local_icache_inv_page,.-mn10300_local_icache_inv_page + .size mn10300_local_icache_inv_range,.-mn10300_local_icache_inv_range + .size mn10300_local_icache_inv_range2,.-mn10300_local_icache_inv_range2 diff --git a/arch/mn10300/mm/cache-inv-by-tag.S b/arch/mn10300/mm/cache-inv-by-tag.S new file mode 100644 index 00000000..ccedce9c --- /dev/null +++ b/arch/mn10300/mm/cache-inv-by-tag.S @@ -0,0 +1,276 @@ +/* MN10300 CPU core caching routines + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/page.h> +#include <asm/cache.h> +#include <asm/irqflags.h> +#include <asm/cacheflush.h> +#include "cache.inc" + +#define mn10300_local_dcache_inv_range_intr_interval \ + +((1 << MN10300_DCACHE_INV_RANGE_INTR_LOG2_INTERVAL) - 1) + +#if mn10300_local_dcache_inv_range_intr_interval > 0xff +#error MN10300_DCACHE_INV_RANGE_INTR_LOG2_INTERVAL must be 8 or less +#endif + + .am33_2 + + .globl mn10300_local_icache_inv_page + .globl mn10300_local_icache_inv_range + .globl mn10300_local_icache_inv_range2 + +mn10300_local_icache_inv_page = mn10300_local_icache_inv +mn10300_local_icache_inv_range = mn10300_local_icache_inv +mn10300_local_icache_inv_range2 = mn10300_local_icache_inv + +#ifndef CONFIG_SMP + .globl mn10300_icache_inv + .globl mn10300_icache_inv_page + .globl mn10300_icache_inv_range + .globl mn10300_icache_inv_range2 + .globl mn10300_dcache_inv + .globl mn10300_dcache_inv_page + .globl mn10300_dcache_inv_range + .globl mn10300_dcache_inv_range2 + +mn10300_icache_inv = mn10300_local_icache_inv +mn10300_icache_inv_page = mn10300_local_icache_inv_page +mn10300_icache_inv_range = mn10300_local_icache_inv_range +mn10300_icache_inv_range2 = mn10300_local_icache_inv_range2 +mn10300_dcache_inv = mn10300_local_dcache_inv +mn10300_dcache_inv_page = mn10300_local_dcache_inv_page +mn10300_dcache_inv_range = mn10300_local_dcache_inv_range +mn10300_dcache_inv_range2 = mn10300_local_dcache_inv_range2 + +#endif /* !CONFIG_SMP */ + +############################################################################### +# +# void mn10300_local_icache_inv(void) +# Invalidate the entire icache +# +############################################################################### + ALIGN + .globl mn10300_local_icache_inv + .type mn10300_local_icache_inv,@function +mn10300_local_icache_inv: + mov CHCTR,a0 + + movhu (a0),d0 + btst CHCTR_ICEN,d0 + beq mn10300_local_icache_inv_end + + invalidate_icache 1 + +mn10300_local_icache_inv_end: + ret [],0 + .size mn10300_local_icache_inv,.-mn10300_local_icache_inv + +############################################################################### +# +# void mn10300_local_dcache_inv(void) +# Invalidate the entire dcache +# +############################################################################### + ALIGN + .globl mn10300_local_dcache_inv + .type mn10300_local_dcache_inv,@function +mn10300_local_dcache_inv: + mov CHCTR,a0 + + movhu (a0),d0 + btst CHCTR_DCEN,d0 + beq mn10300_local_dcache_inv_end + + invalidate_dcache 1 + +mn10300_local_dcache_inv_end: + ret [],0 + .size mn10300_local_dcache_inv,.-mn10300_local_dcache_inv + +############################################################################### +# +# void mn10300_local_dcache_inv_range(unsigned long start, unsigned long end) +# void mn10300_local_dcache_inv_range2(unsigned long start, unsigned long size) +# void mn10300_local_dcache_inv_page(unsigned long start) +# Invalidate a range of addresses on a page in the dcache +# +############################################################################### + ALIGN + .globl mn10300_local_dcache_inv_page + .globl mn10300_local_dcache_inv_range + .globl mn10300_local_dcache_inv_range2 + .type mn10300_local_dcache_inv_page,@function + .type mn10300_local_dcache_inv_range,@function + .type mn10300_local_dcache_inv_range2,@function +mn10300_local_dcache_inv_page: + and ~(PAGE_SIZE-1),d0 + mov PAGE_SIZE,d1 +mn10300_local_dcache_inv_range2: + add d0,d1 +mn10300_local_dcache_inv_range: + # If we are in writeback mode we check the start and end alignments, + # and if they're not cacheline-aligned, we must flush any bits outside + # the range that share cachelines with stuff inside the range +#ifdef CONFIG_MN10300_CACHE_WBACK + btst ~L1_CACHE_TAG_MASK,d0 + bne 1f + btst ~L1_CACHE_TAG_MASK,d1 + beq 2f +1: + bra mn10300_local_dcache_flush_inv_range +2: +#endif /* CONFIG_MN10300_CACHE_WBACK */ + + movm [d2,d3,a2],(sp) + + mov CHCTR,a2 + movhu (a2),d2 + btst CHCTR_DCEN,d2 + beq mn10300_local_dcache_inv_range_end + +#ifndef CONFIG_MN10300_CACHE_WBACK + and L1_CACHE_TAG_MASK,d0 # round start addr down + + add L1_CACHE_BYTES,d1 # round end addr up + and L1_CACHE_TAG_MASK,d1 +#endif /* !CONFIG_MN10300_CACHE_WBACK */ + mov d0,a1 + + clr d2 # we're going to clear tag RAM + # entries + + # read the tags from the tag RAM, and if they indicate a valid dirty + # cache line then invalidate that line + mov DCACHE_TAG(0,0),a0 + mov a1,d0 + and L1_CACHE_TAG_ENTRY,d0 + add d0,a0 # starting dcache tag RAM + # access address + + sub a1,d1 + lsr L1_CACHE_SHIFT,d1 # total number of entries to + # examine + + and ~(L1_CACHE_DISPARITY-1),a1 # determine comparator base + +mn10300_local_dcache_inv_range_outer_loop: + LOCAL_CLI_SAVE(d3) + + # disable the dcache + movhu (a2),d0 + and ~CHCTR_DCEN,d0 + movhu d0,(a2) + + # and wait for it to calm down + setlb + movhu (a2),d0 + btst CHCTR_DCBUSY,d0 + lne + +mn10300_local_dcache_inv_range_loop: + + # process the way 0 slot + mov (L1_CACHE_WAYDISP*0,a0),d0 # read the tag in the way 0 slot + btst L1_CACHE_TAG_VALID,d0 + beq mn10300_local_dcache_inv_range_skip_0 # jump if this cacheline + # is not valid + + xor a1,d0 + lsr 12,d0 + bne mn10300_local_dcache_inv_range_skip_0 # jump if not this cacheline + + mov d2,(L1_CACHE_WAYDISP*0,a0) # kill the tag + +mn10300_local_dcache_inv_range_skip_0: + + # process the way 1 slot + mov (L1_CACHE_WAYDISP*1,a0),d0 # read the tag in the way 1 slot + btst L1_CACHE_TAG_VALID,d0 + beq mn10300_local_dcache_inv_range_skip_1 # jump if this cacheline + # is not valid + + xor a1,d0 + lsr 12,d0 + bne mn10300_local_dcache_inv_range_skip_1 # jump if not this cacheline + + mov d2,(L1_CACHE_WAYDISP*1,a0) # kill the tag + +mn10300_local_dcache_inv_range_skip_1: + + # process the way 2 slot + mov (L1_CACHE_WAYDISP*2,a0),d0 # read the tag in the way 2 slot + btst L1_CACHE_TAG_VALID,d0 + beq mn10300_local_dcache_inv_range_skip_2 # jump if this cacheline + # is not valid + + xor a1,d0 + lsr 12,d0 + bne mn10300_local_dcache_inv_range_skip_2 # jump if not this cacheline + + mov d2,(L1_CACHE_WAYDISP*2,a0) # kill the tag + +mn10300_local_dcache_inv_range_skip_2: + + # process the way 3 slot + mov (L1_CACHE_WAYDISP*3,a0),d0 # read the tag in the way 3 slot + btst L1_CACHE_TAG_VALID,d0 + beq mn10300_local_dcache_inv_range_skip_3 # jump if this cacheline + # is not valid + + xor a1,d0 + lsr 12,d0 + bne mn10300_local_dcache_inv_range_skip_3 # jump if not this cacheline + + mov d2,(L1_CACHE_WAYDISP*3,a0) # kill the tag + +mn10300_local_dcache_inv_range_skip_3: + + # approx every N steps we re-enable the cache and see if there are any + # interrupts to be processed + # we also break out if we've reached the end of the loop + # (the bottom nibble of the count is zero in both cases) + add L1_CACHE_BYTES,a0 + add L1_CACHE_BYTES,a1 + and ~L1_CACHE_WAYDISP,a0 + add -1,d1 + btst mn10300_local_dcache_inv_range_intr_interval,d1 + bne mn10300_local_dcache_inv_range_loop + + # wait for the cache to finish what it's doing + setlb + movhu (a2),d0 + btst CHCTR_DCBUSY,d0 + lne + + # and reenable it + or CHCTR_DCEN,d0 + movhu d0,(a2) + movhu (a2),d0 + + # re-enable interrupts + # - we don't bother with delay NOPs as we'll have enough instructions + # before we disable interrupts again to give the interrupts a chance + # to happen + LOCAL_IRQ_RESTORE(d3) + + # go around again if the counter hasn't yet reached zero + add 0,d1 + bne mn10300_local_dcache_inv_range_outer_loop + +mn10300_local_dcache_inv_range_end: + ret [d2,d3,a2],12 + .size mn10300_local_dcache_inv_page,.-mn10300_local_dcache_inv_page + .size mn10300_local_dcache_inv_range,.-mn10300_local_dcache_inv_range + .size mn10300_local_dcache_inv_range2,.-mn10300_local_dcache_inv_range2 diff --git a/arch/mn10300/mm/cache-inv-icache.c b/arch/mn10300/mm/cache-inv-icache.c new file mode 100644 index 00000000..a6b63dde --- /dev/null +++ b/arch/mn10300/mm/cache-inv-icache.c @@ -0,0 +1,129 @@ +/* Invalidate icache when dcache doesn't need invalidation as it's in + * write-through mode + * + * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/mm.h> +#include <asm/cacheflush.h> +#include <asm/smp.h> +#include "cache-smp.h" + +/** + * flush_icache_page_range - Flush dcache and invalidate icache for part of a + * single page + * @start: The starting virtual address of the page part. + * @end: The ending virtual address of the page part. + * + * Invalidate the icache for part of a single page, as determined by the + * virtual addresses given. The page must be in the paged area. The dcache is + * not flushed as the cache must be in write-through mode to get here. + */ +static void flush_icache_page_range(unsigned long start, unsigned long end) +{ + unsigned long addr, size, off; + struct page *page; + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *ppte, pte; + + /* work out how much of the page to flush */ + off = start & ~PAGE_MASK; + size = end - start; + + /* get the physical address the page is mapped to from the page + * tables */ + pgd = pgd_offset(current->mm, start); + if (!pgd || !pgd_val(*pgd)) + return; + + pud = pud_offset(pgd, start); + if (!pud || !pud_val(*pud)) + return; + + pmd = pmd_offset(pud, start); + if (!pmd || !pmd_val(*pmd)) + return; + + ppte = pte_offset_map(pmd, start); + if (!ppte) + return; + pte = *ppte; + pte_unmap(ppte); + + if (pte_none(pte)) + return; + + page = pte_page(pte); + if (!page) + return; + + addr = page_to_phys(page); + + /* invalidate the icache coverage on that region */ + mn10300_local_icache_inv_range2(addr + off, size); + smp_cache_call(SMP_ICACHE_INV_RANGE, start, end); +} + +/** + * flush_icache_range - Globally flush dcache and invalidate icache for region + * @start: The starting virtual address of the region. + * @end: The ending virtual address of the region. + * + * This is used by the kernel to globally flush some code it has just written + * from the dcache back to RAM and then to globally invalidate the icache over + * that region so that that code can be run on all CPUs in the system. + */ +void flush_icache_range(unsigned long start, unsigned long end) +{ + unsigned long start_page, end_page; + unsigned long flags; + + flags = smp_lock_cache(); + + if (end > 0x80000000UL) { + /* addresses above 0xa0000000 do not go through the cache */ + if (end > 0xa0000000UL) { + end = 0xa0000000UL; + if (start >= end) + goto done; + } + + /* kernel addresses between 0x80000000 and 0x9fffffff do not + * require page tables, so we just map such addresses + * directly */ + start_page = (start >= 0x80000000UL) ? start : 0x80000000UL; + mn10300_icache_inv_range(start_page, end); + smp_cache_call(SMP_ICACHE_INV_RANGE, start, end); + if (start_page == start) + goto done; + end = start_page; + } + + start_page = start & PAGE_MASK; + end_page = (end - 1) & PAGE_MASK; + + if (start_page == end_page) { + /* the first and last bytes are on the same page */ + flush_icache_page_range(start, end); + } else if (start_page + 1 == end_page) { + /* split over two virtually contiguous pages */ + flush_icache_page_range(start, end_page); + flush_icache_page_range(end_page, end); + } else { + /* more than 2 pages; just flush the entire cache */ + mn10300_local_icache_inv(); + smp_cache_call(SMP_ICACHE_INV, 0, 0); + } + +done: + smp_unlock_cache(flags); +} +EXPORT_SYMBOL(flush_icache_range); diff --git a/arch/mn10300/mm/cache-smp-flush.c b/arch/mn10300/mm/cache-smp-flush.c new file mode 100644 index 00000000..fd51af5e --- /dev/null +++ b/arch/mn10300/mm/cache-smp-flush.c @@ -0,0 +1,156 @@ +/* Functions for global dcache flush when writeback caching in SMP + * + * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/mm.h> +#include <asm/cacheflush.h> +#include "cache-smp.h" + +/** + * mn10300_dcache_flush - Globally flush data cache + * + * Flush the data cache on all CPUs. + */ +void mn10300_dcache_flush(void) +{ + unsigned long flags; + + flags = smp_lock_cache(); + mn10300_local_dcache_flush(); + smp_cache_call(SMP_DCACHE_FLUSH, 0, 0); + smp_unlock_cache(flags); +} + +/** + * mn10300_dcache_flush_page - Globally flush a page of data cache + * @start: The address of the page of memory to be flushed. + * + * Flush a range of addresses in the data cache on all CPUs covering + * the page that includes the given address. + */ +void mn10300_dcache_flush_page(unsigned long start) +{ + unsigned long flags; + + start &= ~(PAGE_SIZE-1); + + flags = smp_lock_cache(); + mn10300_local_dcache_flush_page(start); + smp_cache_call(SMP_DCACHE_FLUSH_RANGE, start, start + PAGE_SIZE); + smp_unlock_cache(flags); +} + +/** + * mn10300_dcache_flush_range - Globally flush range of data cache + * @start: The start address of the region to be flushed. + * @end: The end address of the region to be flushed. + * + * Flush a range of addresses in the data cache on all CPUs, between start and + * end-1 inclusive. + */ +void mn10300_dcache_flush_range(unsigned long start, unsigned long end) +{ + unsigned long flags; + + flags = smp_lock_cache(); + mn10300_local_dcache_flush_range(start, end); + smp_cache_call(SMP_DCACHE_FLUSH_RANGE, start, end); + smp_unlock_cache(flags); +} + +/** + * mn10300_dcache_flush_range2 - Globally flush range of data cache + * @start: The start address of the region to be flushed. + * @size: The size of the region to be flushed. + * + * Flush a range of addresses in the data cache on all CPUs, between start and + * start+size-1 inclusive. + */ +void mn10300_dcache_flush_range2(unsigned long start, unsigned long size) +{ + unsigned long flags; + + flags = smp_lock_cache(); + mn10300_local_dcache_flush_range2(start, size); + smp_cache_call(SMP_DCACHE_FLUSH_RANGE, start, start + size); + smp_unlock_cache(flags); +} + +/** + * mn10300_dcache_flush_inv - Globally flush and invalidate data cache + * + * Flush and invalidate the data cache on all CPUs. + */ +void mn10300_dcache_flush_inv(void) +{ + unsigned long flags; + + flags = smp_lock_cache(); + mn10300_local_dcache_flush_inv(); + smp_cache_call(SMP_DCACHE_FLUSH_INV, 0, 0); + smp_unlock_cache(flags); +} + +/** + * mn10300_dcache_flush_inv_page - Globally flush and invalidate a page of data + * cache + * @start: The address of the page of memory to be flushed and invalidated. + * + * Flush and invalidate a range of addresses in the data cache on all CPUs + * covering the page that includes the given address. + */ +void mn10300_dcache_flush_inv_page(unsigned long start) +{ + unsigned long flags; + + start &= ~(PAGE_SIZE-1); + + flags = smp_lock_cache(); + mn10300_local_dcache_flush_inv_page(start); + smp_cache_call(SMP_DCACHE_FLUSH_INV_RANGE, start, start + PAGE_SIZE); + smp_unlock_cache(flags); +} + +/** + * mn10300_dcache_flush_inv_range - Globally flush and invalidate range of data + * cache + * @start: The start address of the region to be flushed and invalidated. + * @end: The end address of the region to be flushed and invalidated. + * + * Flush and invalidate a range of addresses in the data cache on all CPUs, + * between start and end-1 inclusive. + */ +void mn10300_dcache_flush_inv_range(unsigned long start, unsigned long end) +{ + unsigned long flags; + + flags = smp_lock_cache(); + mn10300_local_dcache_flush_inv_range(start, end); + smp_cache_call(SMP_DCACHE_FLUSH_INV_RANGE, start, end); + smp_unlock_cache(flags); +} + +/** + * mn10300_dcache_flush_inv_range2 - Globally flush and invalidate range of data + * cache + * @start: The start address of the region to be flushed and invalidated. + * @size: The size of the region to be flushed and invalidated. + * + * Flush and invalidate a range of addresses in the data cache on all CPUs, + * between start and start+size-1 inclusive. + */ +void mn10300_dcache_flush_inv_range2(unsigned long start, unsigned long size) +{ + unsigned long flags; + + flags = smp_lock_cache(); + mn10300_local_dcache_flush_inv_range2(start, size); + smp_cache_call(SMP_DCACHE_FLUSH_INV_RANGE, start, start + size); + smp_unlock_cache(flags); +} diff --git a/arch/mn10300/mm/cache-smp-inv.c b/arch/mn10300/mm/cache-smp-inv.c new file mode 100644 index 00000000..ff178735 --- /dev/null +++ b/arch/mn10300/mm/cache-smp-inv.c @@ -0,0 +1,153 @@ +/* Functions for global i/dcache invalidation when caching in SMP + * + * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/mm.h> +#include <asm/cacheflush.h> +#include "cache-smp.h" + +/** + * mn10300_icache_inv - Globally invalidate instruction cache + * + * Invalidate the instruction cache on all CPUs. + */ +void mn10300_icache_inv(void) +{ + unsigned long flags; + + flags = smp_lock_cache(); + mn10300_local_icache_inv(); + smp_cache_call(SMP_ICACHE_INV, 0, 0); + smp_unlock_cache(flags); +} + +/** + * mn10300_icache_inv_page - Globally invalidate a page of instruction cache + * @start: The address of the page of memory to be invalidated. + * + * Invalidate a range of addresses in the instruction cache on all CPUs + * covering the page that includes the given address. + */ +void mn10300_icache_inv_page(unsigned long start) +{ + unsigned long flags; + + start &= ~(PAGE_SIZE-1); + + flags = smp_lock_cache(); + mn10300_local_icache_inv_page(start); + smp_cache_call(SMP_ICACHE_INV_RANGE, start, start + PAGE_SIZE); + smp_unlock_cache(flags); +} + +/** + * mn10300_icache_inv_range - Globally invalidate range of instruction cache + * @start: The start address of the region to be invalidated. + * @end: The end address of the region to be invalidated. + * + * Invalidate a range of addresses in the instruction cache on all CPUs, + * between start and end-1 inclusive. + */ +void mn10300_icache_inv_range(unsigned long start, unsigned long end) +{ + unsigned long flags; + + flags = smp_lock_cache(); + mn10300_local_icache_inv_range(start, end); + smp_cache_call(SMP_ICACHE_INV_RANGE, start, end); + smp_unlock_cache(flags); +} + +/** + * mn10300_icache_inv_range2 - Globally invalidate range of instruction cache + * @start: The start address of the region to be invalidated. + * @size: The size of the region to be invalidated. + * + * Invalidate a range of addresses in the instruction cache on all CPUs, + * between start and start+size-1 inclusive. + */ +void mn10300_icache_inv_range2(unsigned long start, unsigned long size) +{ + unsigned long flags; + + flags = smp_lock_cache(); + mn10300_local_icache_inv_range2(start, size); + smp_cache_call(SMP_ICACHE_INV_RANGE, start, start + size); + smp_unlock_cache(flags); +} + +/** + * mn10300_dcache_inv - Globally invalidate data cache + * + * Invalidate the data cache on all CPUs. + */ +void mn10300_dcache_inv(void) +{ + unsigned long flags; + + flags = smp_lock_cache(); + mn10300_local_dcache_inv(); + smp_cache_call(SMP_DCACHE_INV, 0, 0); + smp_unlock_cache(flags); +} + +/** + * mn10300_dcache_inv_page - Globally invalidate a page of data cache + * @start: The address of the page of memory to be invalidated. + * + * Invalidate a range of addresses in the data cache on all CPUs covering the + * page that includes the given address. + */ +void mn10300_dcache_inv_page(unsigned long start) +{ + unsigned long flags; + + start &= ~(PAGE_SIZE-1); + + flags = smp_lock_cache(); + mn10300_local_dcache_inv_page(start); + smp_cache_call(SMP_DCACHE_INV_RANGE, start, start + PAGE_SIZE); + smp_unlock_cache(flags); +} + +/** + * mn10300_dcache_inv_range - Globally invalidate range of data cache + * @start: The start address of the region to be invalidated. + * @end: The end address of the region to be invalidated. + * + * Invalidate a range of addresses in the data cache on all CPUs, between start + * and end-1 inclusive. + */ +void mn10300_dcache_inv_range(unsigned long start, unsigned long end) +{ + unsigned long flags; + + flags = smp_lock_cache(); + mn10300_local_dcache_inv_range(start, end); + smp_cache_call(SMP_DCACHE_INV_RANGE, start, end); + smp_unlock_cache(flags); +} + +/** + * mn10300_dcache_inv_range2 - Globally invalidate range of data cache + * @start: The start address of the region to be invalidated. + * @size: The size of the region to be invalidated. + * + * Invalidate a range of addresses in the data cache on all CPUs, between start + * and start+size-1 inclusive. + */ +void mn10300_dcache_inv_range2(unsigned long start, unsigned long size) +{ + unsigned long flags; + + flags = smp_lock_cache(); + mn10300_local_dcache_inv_range2(start, size); + smp_cache_call(SMP_DCACHE_INV_RANGE, start, start + size); + smp_unlock_cache(flags); +} diff --git a/arch/mn10300/mm/cache-smp.c b/arch/mn10300/mm/cache-smp.c new file mode 100644 index 00000000..2d23b9ee --- /dev/null +++ b/arch/mn10300/mm/cache-smp.c @@ -0,0 +1,105 @@ +/* SMP global caching code + * + * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/mm.h> +#include <linux/mman.h> +#include <linux/threads.h> +#include <linux/interrupt.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/processor.h> +#include <asm/cacheflush.h> +#include <asm/io.h> +#include <asm/uaccess.h> +#include <asm/smp.h> +#include "cache-smp.h" + +DEFINE_SPINLOCK(smp_cache_lock); +static unsigned long smp_cache_mask; +static unsigned long smp_cache_start; +static unsigned long smp_cache_end; +static cpumask_t smp_cache_ipi_map; /* Bitmask of cache IPI done CPUs */ + +/** + * smp_cache_interrupt - Handle IPI request to flush caches. + * + * Handle a request delivered by IPI to flush the current CPU's + * caches. The parameters are stored in smp_cache_*. + */ +void smp_cache_interrupt(void) +{ + unsigned long opr_mask = smp_cache_mask; + + switch ((enum smp_dcache_ops)(opr_mask & SMP_DCACHE_OP_MASK)) { + case SMP_DCACHE_NOP: + break; + case SMP_DCACHE_INV: + mn10300_local_dcache_inv(); + break; + case SMP_DCACHE_INV_RANGE: + mn10300_local_dcache_inv_range(smp_cache_start, smp_cache_end); + break; + case SMP_DCACHE_FLUSH: + mn10300_local_dcache_flush(); + break; + case SMP_DCACHE_FLUSH_RANGE: + mn10300_local_dcache_flush_range(smp_cache_start, + smp_cache_end); + break; + case SMP_DCACHE_FLUSH_INV: + mn10300_local_dcache_flush_inv(); + break; + case SMP_DCACHE_FLUSH_INV_RANGE: + mn10300_local_dcache_flush_inv_range(smp_cache_start, + smp_cache_end); + break; + } + + switch ((enum smp_icache_ops)(opr_mask & SMP_ICACHE_OP_MASK)) { + case SMP_ICACHE_NOP: + break; + case SMP_ICACHE_INV: + mn10300_local_icache_inv(); + break; + case SMP_ICACHE_INV_RANGE: + mn10300_local_icache_inv_range(smp_cache_start, smp_cache_end); + break; + } + + cpumask_clear_cpu(smp_processor_id(), &smp_cache_ipi_map); +} + +/** + * smp_cache_call - Issue an IPI to request the other CPUs flush caches + * @opr_mask: Cache operation flags + * @start: Start address of request + * @end: End address of request + * + * Send cache flush IPI to other CPUs. This invokes smp_cache_interrupt() + * above on those other CPUs and then waits for them to finish. + * + * The caller must hold smp_cache_lock. + */ +void smp_cache_call(unsigned long opr_mask, + unsigned long start, unsigned long end) +{ + smp_cache_mask = opr_mask; + smp_cache_start = start; + smp_cache_end = end; + cpumask_copy(&smp_cache_ipi_map, cpu_online_mask); + cpumask_clear_cpu(smp_processor_id(), &smp_cache_ipi_map); + + send_IPI_allbutself(FLUSH_CACHE_IPI); + + while (!cpumask_empty(&smp_cache_ipi_map)) + /* nothing. lockup detection does not belong here */ + mb(); +} diff --git a/arch/mn10300/mm/cache-smp.h b/arch/mn10300/mm/cache-smp.h new file mode 100644 index 00000000..cb52892a --- /dev/null +++ b/arch/mn10300/mm/cache-smp.h @@ -0,0 +1,69 @@ +/* SMP caching definitions + * + * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + + +/* + * Operation requests for smp_cache_call(). + * + * One of smp_icache_ops and one of smp_dcache_ops can be OR'd together. + */ +enum smp_icache_ops { + SMP_ICACHE_NOP = 0x0000, + SMP_ICACHE_INV = 0x0001, + SMP_ICACHE_INV_RANGE = 0x0002, +}; +#define SMP_ICACHE_OP_MASK 0x0003 + +enum smp_dcache_ops { + SMP_DCACHE_NOP = 0x0000, + SMP_DCACHE_INV = 0x0004, + SMP_DCACHE_INV_RANGE = 0x0008, + SMP_DCACHE_FLUSH = 0x000c, + SMP_DCACHE_FLUSH_RANGE = 0x0010, + SMP_DCACHE_FLUSH_INV = 0x0014, + SMP_DCACHE_FLUSH_INV_RANGE = 0x0018, +}; +#define SMP_DCACHE_OP_MASK 0x001c + +#define SMP_IDCACHE_INV_FLUSH (SMP_ICACHE_INV | SMP_DCACHE_FLUSH) +#define SMP_IDCACHE_INV_FLUSH_RANGE (SMP_ICACHE_INV_RANGE | SMP_DCACHE_FLUSH_RANGE) + +/* + * cache-smp.c + */ +#ifdef CONFIG_SMP +extern spinlock_t smp_cache_lock; + +extern void smp_cache_call(unsigned long opr_mask, + unsigned long addr, unsigned long end); + +static inline unsigned long smp_lock_cache(void) + __acquires(&smp_cache_lock) +{ + unsigned long flags; + spin_lock_irqsave(&smp_cache_lock, flags); + return flags; +} + +static inline void smp_unlock_cache(unsigned long flags) + __releases(&smp_cache_lock) +{ + spin_unlock_irqrestore(&smp_cache_lock, flags); +} + +#else +static inline unsigned long smp_lock_cache(void) { return 0; } +static inline void smp_unlock_cache(unsigned long flags) {} +static inline void smp_cache_call(unsigned long opr_mask, + unsigned long addr, unsigned long end) +{ +} +#endif /* CONFIG_SMP */ diff --git a/arch/mn10300/mm/cache.c b/arch/mn10300/mm/cache.c new file mode 100644 index 00000000..0a1f0aa9 --- /dev/null +++ b/arch/mn10300/mm/cache.c @@ -0,0 +1,54 @@ +/* MN10300 Cache flushing routines + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/mm.h> +#include <linux/mman.h> +#include <linux/threads.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/processor.h> +#include <asm/cacheflush.h> +#include <asm/io.h> +#include <asm/uaccess.h> +#include <asm/smp.h> +#include "cache-smp.h" + +EXPORT_SYMBOL(mn10300_icache_inv); +EXPORT_SYMBOL(mn10300_icache_inv_range); +EXPORT_SYMBOL(mn10300_icache_inv_range2); +EXPORT_SYMBOL(mn10300_icache_inv_page); +EXPORT_SYMBOL(mn10300_dcache_inv); +EXPORT_SYMBOL(mn10300_dcache_inv_range); +EXPORT_SYMBOL(mn10300_dcache_inv_range2); +EXPORT_SYMBOL(mn10300_dcache_inv_page); + +#ifdef CONFIG_MN10300_CACHE_WBACK +EXPORT_SYMBOL(mn10300_dcache_flush); +EXPORT_SYMBOL(mn10300_dcache_flush_inv); +EXPORT_SYMBOL(mn10300_dcache_flush_inv_range); +EXPORT_SYMBOL(mn10300_dcache_flush_inv_range2); +EXPORT_SYMBOL(mn10300_dcache_flush_inv_page); +EXPORT_SYMBOL(mn10300_dcache_flush_range); +EXPORT_SYMBOL(mn10300_dcache_flush_range2); +EXPORT_SYMBOL(mn10300_dcache_flush_page); +#endif + +/* + * allow userspace to flush the instruction cache + */ +asmlinkage long sys_cacheflush(unsigned long start, unsigned long end) +{ + if (end < start) + return -EINVAL; + + flush_icache_range(start, end); + return 0; +} diff --git a/arch/mn10300/mm/cache.inc b/arch/mn10300/mm/cache.inc new file mode 100644 index 00000000..394a119b --- /dev/null +++ b/arch/mn10300/mm/cache.inc @@ -0,0 +1,133 @@ +/* MN10300 CPU core caching macros -*- asm -*- + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + + +############################################################################### +# +# Invalidate the instruction cache. +# A0: Should hold CHCTR +# D0: Should have been read from CHCTR +# D1: Will be clobbered +# +# On some cores it is necessary to disable the icache whilst we do this. +# +############################################################################### + .macro invalidate_icache,disable_irq + +#if defined(CONFIG_AM33_2) || defined(CONFIG_AM33_3) + .if \disable_irq + # don't want an interrupt routine seeing a disabled cache + mov epsw,d1 + and ~EPSW_IE,epsw + or EPSW_NMID,epsw + nop + nop + .endif + + # disable the icache + and ~CHCTR_ICEN,d0 + movhu d0,(a0) + + # and wait for it to calm down + setlb + movhu (a0),d0 + btst CHCTR_ICBUSY,d0 + lne + + # invalidate + or CHCTR_ICINV,d0 + movhu d0,(a0) + + # wait for the cache to finish + setlb + movhu (a0),d0 + btst CHCTR_ICBUSY,d0 + lne + + # and reenable it + or CHCTR_ICEN,d0 + movhu d0,(a0) + movhu (a0),d0 + + .if \disable_irq + LOCAL_IRQ_RESTORE(d1) + .endif + +#else /* CONFIG_AM33_2 || CONFIG_AM33_3 */ + + # invalidate + or CHCTR_ICINV,d0 + movhu d0,(a0) + movhu (a0),d0 + +#endif /* CONFIG_AM33_2 || CONFIG_AM33_3 */ + .endm + +############################################################################### +# +# Invalidate the data cache. +# A0: Should hold CHCTR +# D0: Should have been read from CHCTR +# D1: Will be clobbered +# +# On some cores it is necessary to disable the dcache whilst we do this. +# +############################################################################### + .macro invalidate_dcache,disable_irq + +#if defined(CONFIG_AM33_2) || defined(CONFIG_AM33_3) + .if \disable_irq + # don't want an interrupt routine seeing a disabled cache + mov epsw,d1 + and ~EPSW_IE,epsw + or EPSW_NMID,epsw + nop + nop + .endif + + # disable the dcache + and ~CHCTR_DCEN,d0 + movhu d0,(a0) + + # and wait for it to calm down + setlb + movhu (a0),d0 + btst CHCTR_DCBUSY,d0 + lne + + # invalidate + or CHCTR_DCINV,d0 + movhu d0,(a0) + + # wait for the cache to finish + setlb + movhu (a0),d0 + btst CHCTR_DCBUSY,d0 + lne + + # and reenable it + or CHCTR_DCEN,d0 + movhu d0,(a0) + movhu (a0),d0 + + .if \disable_irq + LOCAL_IRQ_RESTORE(d1) + .endif + +#else /* CONFIG_AM33_2 || CONFIG_AM33_3 */ + + # invalidate + or CHCTR_DCINV,d0 + movhu d0,(a0) + movhu (a0),d0 + +#endif /* CONFIG_AM33_2 || CONFIG_AM33_3 */ + .endm diff --git a/arch/mn10300/mm/dma-alloc.c b/arch/mn10300/mm/dma-alloc.c new file mode 100644 index 00000000..159acb02 --- /dev/null +++ b/arch/mn10300/mm/dma-alloc.c @@ -0,0 +1,75 @@ +/* MN10300 Dynamic DMA mapping support + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * Derived from: arch/i386/kernel/pci-dma.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/types.h> +#include <linux/mm.h> +#include <linux/string.h> +#include <linux/pci.h> +#include <linux/gfp.h> +#include <asm/io.h> + +static unsigned long pci_sram_allocated = 0xbc000000; + +void *dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, int gfp) +{ + unsigned long addr; + void *ret; + + pr_debug("dma_alloc_coherent(%s,%zu,%x)\n", + dev ? dev_name(dev) : "?", size, gfp); + + if (0xbe000000 - pci_sram_allocated >= size) { + size = (size + 255) & ~255; + addr = pci_sram_allocated; + pci_sram_allocated += size; + ret = (void *) addr; + goto done; + } + + /* ignore region specifiers */ + gfp &= ~(__GFP_DMA | __GFP_HIGHMEM); + + if (dev == NULL || dev->coherent_dma_mask < 0xffffffff) + gfp |= GFP_DMA; + + addr = __get_free_pages(gfp, get_order(size)); + if (!addr) + return NULL; + + /* map the coherent memory through the uncached memory window */ + ret = (void *) (addr | 0x20000000); + + /* fill the memory with obvious rubbish */ + memset((void *) addr, 0xfb, size); + + /* write back and evict all cache lines covering this region */ + mn10300_dcache_flush_inv_range2(virt_to_phys((void *) addr), PAGE_SIZE); + +done: + *dma_handle = virt_to_bus((void *) addr); + printk("dma_alloc_coherent() = %p [%x]\n", ret, *dma_handle); + return ret; +} +EXPORT_SYMBOL(dma_alloc_coherent); + +void dma_free_coherent(struct device *dev, size_t size, void *vaddr, + dma_addr_t dma_handle) +{ + unsigned long addr = (unsigned long) vaddr & ~0x20000000; + + if (addr >= 0x9c000000) + return; + + free_pages(addr, get_order(size)); +} +EXPORT_SYMBOL(dma_free_coherent); diff --git a/arch/mn10300/mm/extable.c b/arch/mn10300/mm/extable.c new file mode 100644 index 00000000..25e5485a --- /dev/null +++ b/arch/mn10300/mm/extable.c @@ -0,0 +1,26 @@ +/* MN10300 In-kernel exception handling + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/spinlock.h> +#include <asm/uaccess.h> + +int fixup_exception(struct pt_regs *regs) +{ + const struct exception_table_entry *fixup; + + fixup = search_exception_tables(regs->pc); + if (fixup) { + regs->pc = fixup->fixup; + return 1; + } + + return 0; +} diff --git a/arch/mn10300/mm/fault.c b/arch/mn10300/mm/fault.c new file mode 100644 index 00000000..0945409a --- /dev/null +++ b/arch/mn10300/mm/fault.c @@ -0,0 +1,393 @@ +/* MN10300 MMU Fault handler + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Modified by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/mman.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/vt_kern.h> /* For unblank_screen() */ + +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/pgalloc.h> +#include <asm/hardirq.h> +#include <asm/cpu-regs.h> +#include <asm/debugger.h> +#include <asm/gdb-stub.h> + +/* + * Unlock any spinlocks which will prevent us from getting the + * message out + */ +void bust_spinlocks(int yes) +{ + if (yes) { + oops_in_progress = 1; + } else { + int loglevel_save = console_loglevel; +#ifdef CONFIG_VT + unblank_screen(); +#endif + oops_in_progress = 0; + /* + * OK, the message is on the console. Now we call printk() + * without oops_in_progress set so that printk will give klogd + * a poke. Hold onto your hats... + */ + console_loglevel = 15; /* NMI oopser may have shut the console + * up */ + printk(" "); + console_loglevel = loglevel_save; + } +} + +void do_BUG(const char *file, int line) +{ + bust_spinlocks(1); + printk(KERN_EMERG "------------[ cut here ]------------\n"); + printk(KERN_EMERG "kernel BUG at %s:%d!\n", file, line); +} + +#if 0 +static void print_pagetable_entries(pgd_t *pgdir, unsigned long address) +{ + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + + pgd = pgdir + __pgd_offset(address); + printk(KERN_DEBUG "pgd entry %p: %016Lx\n", + pgd, (long long) pgd_val(*pgd)); + + if (!pgd_present(*pgd)) { + printk(KERN_DEBUG "... pgd not present!\n"); + return; + } + pmd = pmd_offset(pgd, address); + printk(KERN_DEBUG "pmd entry %p: %016Lx\n", + pmd, (long long)pmd_val(*pmd)); + + if (!pmd_present(*pmd)) { + printk(KERN_DEBUG "... pmd not present!\n"); + return; + } + pte = pte_offset(pmd, address); + printk(KERN_DEBUG "pte entry %p: %016Lx\n", + pte, (long long) pte_val(*pte)); + + if (!pte_present(*pte)) + printk(KERN_DEBUG "... pte not present!\n"); +} +#endif + +/* + * This routine handles page faults. It determines the address, + * and the problem, and then passes it off to one of the appropriate + * routines. + * + * fault_code: + * - LSW: either MMUFCR_IFC or MMUFCR_DFC as appropriate + * - MSW: 0 if data access, 1 if instruction access + * - bit 0: TLB miss flag + * - bit 1: initial write + * - bit 2: page invalid + * - bit 3: protection violation + * - bit 4: accessor (0=user 1=kernel) + * - bit 5: 0=read 1=write + * - bit 6-8: page protection spec + * - bit 9: illegal address + * - bit 16: 0=data 1=ins + * + */ +asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long fault_code, + unsigned long address) +{ + struct vm_area_struct *vma; + struct task_struct *tsk; + struct mm_struct *mm; + unsigned long page; + siginfo_t info; + int write, fault; + +#ifdef CONFIG_GDBSTUB + /* handle GDB stub causing a fault */ + if (gdbstub_busy) { + gdbstub_exception(regs, TBR & TBR_INT_CODE); + return; + } +#endif + +#if 0 + printk(KERN_DEBUG "--- do_page_fault(%p,%s:%04lx,%08lx)\n", + regs, + fault_code & 0x10000 ? "ins" : "data", + fault_code & 0xffff, address); +#endif + + tsk = current; + + /* + * We fault-in kernel-space virtual memory on-demand. The + * 'reference' page table is init_mm.pgd. + * + * 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. + * + * This verifies that the fault happens in kernel space + * and that the fault was a page not present (invalid) error + */ + if (address >= VMALLOC_START && address < VMALLOC_END && + (fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_SR && + (fault_code & MMUFCR_xFC_PGINVAL) == MMUFCR_xFC_PGINVAL + ) + goto vmalloc_fault; + + mm = tsk->mm; + info.si_code = SEGV_MAPERR; + + /* + * If we're in an interrupt or have no user + * context, we must not take the fault.. + */ + if (in_atomic() || !mm) + goto no_context; + + down_read(&mm->mmap_sem); + + vma = find_vma(mm, address); + if (!vma) + goto bad_area; + if (vma->vm_start <= address) + goto good_area; + if (!(vma->vm_flags & VM_GROWSDOWN)) + goto bad_area; + + if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_USR) { + /* accessing the stack below the stack pointer is always a + * bug */ + if ((address & PAGE_MASK) + 2 * PAGE_SIZE < regs->sp) { +#if 0 + printk(KERN_WARNING + "[%d] ### Access below stack @%lx (sp=%lx)\n", + current->pid, address, regs->sp); + printk(KERN_WARNING + "vma [%08x - %08x]\n", + vma->vm_start, vma->vm_end); + show_registers(regs); + printk(KERN_WARNING + "[%d] ### Code: [%08lx]" + " %02x %02x %02x %02x %02x %02x %02x %02x\n", + current->pid, + regs->pc, + ((u8 *) regs->pc)[0], + ((u8 *) regs->pc)[1], + ((u8 *) regs->pc)[2], + ((u8 *) regs->pc)[3], + ((u8 *) regs->pc)[4], + ((u8 *) regs->pc)[5], + ((u8 *) regs->pc)[6], + ((u8 *) regs->pc)[7] + ); +#endif + goto bad_area; + } + } + + if (expand_stack(vma, address)) + goto bad_area; + +/* + * Ok, we have a good vm_area for this memory access, so + * we can handle it.. + */ +good_area: + info.si_code = SEGV_ACCERR; + write = 0; + switch (fault_code & (MMUFCR_xFC_PGINVAL|MMUFCR_xFC_TYPE)) { + default: /* 3: write, present */ + case MMUFCR_xFC_TYPE_WRITE: +#ifdef TEST_VERIFY_AREA + if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_SR) + printk(KERN_DEBUG "WP fault at %08lx\n", regs->pc); +#endif + /* write to absent page */ + case MMUFCR_xFC_PGINVAL | MMUFCR_xFC_TYPE_WRITE: + if (!(vma->vm_flags & VM_WRITE)) + goto bad_area; + write++; + break; + + /* read from protected page */ + case MMUFCR_xFC_TYPE_READ: + goto bad_area; + + /* read from absent page present */ + case MMUFCR_xFC_PGINVAL | MMUFCR_xFC_TYPE_READ: + if (!(vma->vm_flags & (VM_READ | VM_EXEC))) + goto bad_area; + break; + } + + /* + * 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, address, write ? FAULT_FLAG_WRITE : 0); + if (unlikely(fault & VM_FAULT_ERROR)) { + if (fault & VM_FAULT_OOM) + goto out_of_memory; + else if (fault & VM_FAULT_SIGBUS) + goto do_sigbus; + BUG(); + } + if (fault & VM_FAULT_MAJOR) + current->maj_flt++; + else + current->min_flt++; + + up_read(&mm->mmap_sem); + return; + +/* + * Something tried to access memory that isn't in our memory map.. + * Fix it, but check if it's kernel or user first.. + */ +bad_area: + up_read(&mm->mmap_sem); + + /* User mode accesses just cause a SIGSEGV */ + if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_USR) { + info.si_signo = SIGSEGV; + info.si_errno = 0; + /* info.si_code has been set above */ + info.si_addr = (void *)address; + force_sig_info(SIGSEGV, &info, tsk); + return; + } + +no_context: + /* Are we prepared to handle this kernel fault? */ + if (fixup_exception(regs)) + return; + +/* + * Oops. The kernel tried to access some bad page. We'll have to + * terminate things with extreme prejudice. + */ + + bust_spinlocks(1); + + if (address < PAGE_SIZE) + printk(KERN_ALERT + "Unable to handle kernel NULL pointer dereference"); + else + printk(KERN_ALERT + "Unable to handle kernel paging request"); + printk(" at virtual address %08lx\n", address); + printk(" printing pc:\n"); + printk(KERN_ALERT "%08lx\n", regs->pc); + + debugger_intercept(fault_code & 0x00010000 ? EXCEP_IAERROR : EXCEP_DAERROR, + SIGSEGV, SEGV_ACCERR, regs); + + page = PTBR; + page = ((unsigned long *) __va(page))[address >> 22]; + printk(KERN_ALERT "*pde = %08lx\n", page); + if (page & 1) { + page &= PAGE_MASK; + address &= 0x003ff000; + page = ((unsigned long *) __va(page))[address >> PAGE_SHIFT]; + printk(KERN_ALERT "*pte = %08lx\n", page); + } + + die("Oops", regs, fault_code); + do_exit(SIGKILL); + +/* + * We ran out of memory, or some other thing happened to us that made + * us unable to handle the page fault gracefully. + */ +out_of_memory: + up_read(&mm->mmap_sem); + printk(KERN_ALERT "VM: killing process %s\n", tsk->comm); + if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_USR) + do_exit(SIGKILL); + goto no_context; + +do_sigbus: + up_read(&mm->mmap_sem); + + /* + * Send a sigbus, regardless of whether we were in kernel + * or user mode. + */ + info.si_signo = SIGBUS; + info.si_errno = 0; + info.si_code = BUS_ADRERR; + info.si_addr = (void *)address; + force_sig_info(SIGBUS, &info, tsk); + + /* Kernel mode? Handle exceptions or die */ + if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_SR) + goto no_context; + return; + +vmalloc_fault: + { + /* + * Synchronize this task's top level page-table + * with the 'reference' page table. + * + * Do _not_ use "tsk" here. We might be inside + * an interrupt in the middle of a task switch.. + */ + int index = pgd_index(address); + pgd_t *pgd, *pgd_k; + pud_t *pud, *pud_k; + pmd_t *pmd, *pmd_k; + pte_t *pte_k; + + pgd_k = init_mm.pgd + index; + + if (!pgd_present(*pgd_k)) + goto no_context; + + pud_k = pud_offset(pgd_k, address); + if (!pud_present(*pud_k)) + goto no_context; + + pmd_k = pmd_offset(pud_k, address); + if (!pmd_present(*pmd_k)) + goto no_context; + + pgd = (pgd_t *) PTBR + index; + pud = pud_offset(pgd, address); + pmd = pmd_offset(pud, address); + set_pmd(pmd, *pmd_k); + + pte_k = pte_offset_kernel(pmd_k, address); + if (!pte_present(*pte_k)) + goto no_context; + return; + } +} diff --git a/arch/mn10300/mm/init.c b/arch/mn10300/mm/init.c new file mode 100644 index 00000000..13801824 --- /dev/null +++ b/arch/mn10300/mm/init.c @@ -0,0 +1,177 @@ +/* MN10300 Memory management initialisation + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Modified by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/mman.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/swap.h> +#include <linux/smp.h> +#include <linux/init.h> +#include <linux/initrd.h> +#include <linux/highmem.h> +#include <linux/pagemap.h> +#include <linux/bootmem.h> +#include <linux/gfp.h> + +#include <asm/processor.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/pgtable.h> +#include <asm/pgalloc.h> +#include <asm/dma.h> +#include <asm/tlb.h> +#include <asm/sections.h> + +unsigned long highstart_pfn, highend_pfn; + +#ifdef CONFIG_MN10300_HAS_ATOMIC_OPS_UNIT +static struct vm_struct user_iomap_vm; +#endif + +/* + * set up paging + */ +void __init paging_init(void) +{ + unsigned long zones_size[MAX_NR_ZONES] = {0,}; + pte_t *ppte; + int loop; + + /* main kernel space -> RAM mapping is handled as 1:1 transparent by + * the MMU */ + memset(swapper_pg_dir, 0, sizeof(swapper_pg_dir)); + memset(kernel_vmalloc_ptes, 0, sizeof(kernel_vmalloc_ptes)); + + /* load the VMALLOC area PTE table addresses into the kernel PGD */ + ppte = kernel_vmalloc_ptes; + for (loop = VMALLOC_START / (PAGE_SIZE * PTRS_PER_PTE); + loop < VMALLOC_END / (PAGE_SIZE * PTRS_PER_PTE); + loop++ + ) { + set_pgd(swapper_pg_dir + loop, __pgd(__pa(ppte) | _PAGE_TABLE)); + ppte += PAGE_SIZE / sizeof(pte_t); + } + + /* declare the sizes of the RAM zones (only use the normal zone) */ + zones_size[ZONE_NORMAL] = + contig_page_data.bdata->node_low_pfn - + contig_page_data.bdata->node_min_pfn; + + /* pass the memory from the bootmem allocator to the main allocator */ + free_area_init(zones_size); + +#ifdef CONFIG_MN10300_HAS_ATOMIC_OPS_UNIT + /* The Atomic Operation Unit registers need to be mapped to userspace + * for all processes. The following uses vm_area_register_early() to + * reserve the first page of the vmalloc area and sets the pte for that + * page. + * + * glibc hardcodes this virtual mapping, so we're pretty much stuck with + * it from now on. + */ + user_iomap_vm.flags = VM_USERMAP; + user_iomap_vm.size = 1 << PAGE_SHIFT; + vm_area_register_early(&user_iomap_vm, PAGE_SIZE); + ppte = kernel_vmalloc_ptes; + set_pte(ppte, pfn_pte(USER_ATOMIC_OPS_PAGE_ADDR >> PAGE_SHIFT, + PAGE_USERIO)); +#endif + + local_flush_tlb_all(); +} + +/* + * transfer all the memory from the bootmem allocator to the runtime allocator + */ +void __init mem_init(void) +{ + int codesize, reservedpages, datasize, initsize; + int tmp; + + BUG_ON(!mem_map); + +#define START_PFN (contig_page_data.bdata->node_min_pfn) +#define MAX_LOW_PFN (contig_page_data.bdata->node_low_pfn) + + max_mapnr = num_physpages = MAX_LOW_PFN - START_PFN; + high_memory = (void *) __va(MAX_LOW_PFN * PAGE_SIZE); + + /* clear the zero-page */ + memset(empty_zero_page, 0, PAGE_SIZE); + + /* this will put all low memory onto the freelists */ + totalram_pages += free_all_bootmem(); + + reservedpages = 0; + for (tmp = 0; tmp < num_physpages; tmp++) + if (PageReserved(&mem_map[tmp])) + reservedpages++; + + codesize = (unsigned long) &_etext - (unsigned long) &_stext; + datasize = (unsigned long) &_edata - (unsigned long) &_etext; + initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; + + printk(KERN_INFO + "Memory: %luk/%luk available" + " (%dk kernel code, %dk reserved, %dk data, %dk init," + " %ldk highmem)\n", + nr_free_pages() << (PAGE_SHIFT - 10), + max_mapnr << (PAGE_SHIFT - 10), + codesize >> 10, + reservedpages << (PAGE_SHIFT - 10), + datasize >> 10, + initsize >> 10, + totalhigh_pages << (PAGE_SHIFT - 10)); +} + +/* + * + */ +void free_init_pages(char *what, unsigned long begin, unsigned long end) +{ + unsigned long addr; + + for (addr = begin; addr < end; addr += PAGE_SIZE) { + ClearPageReserved(virt_to_page(addr)); + init_page_count(virt_to_page(addr)); + memset((void *) addr, 0xcc, PAGE_SIZE); + free_page(addr); + totalram_pages++; + } + printk(KERN_INFO "Freeing %s: %ldk freed\n", what, (end - begin) >> 10); +} + +/* + * recycle memory containing stuff only required for initialisation + */ +void free_initmem(void) +{ + free_init_pages("unused kernel memory", + (unsigned long) &__init_begin, + (unsigned long) &__init_end); +} + +/* + * dispose of the memory on which the initial ramdisk resided + */ +#ifdef CONFIG_BLK_DEV_INITRD +void free_initrd_mem(unsigned long start, unsigned long end) +{ + free_init_pages("initrd memory", start, end); +} +#endif diff --git a/arch/mn10300/mm/misalignment.c b/arch/mn10300/mm/misalignment.c new file mode 100644 index 00000000..eef989c1 --- /dev/null +++ b/arch/mn10300/mm/misalignment.c @@ -0,0 +1,967 @@ +/* MN10300 Misalignment fixup handler + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/timer.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <asm/processor.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/atomic.h> +#include <asm/smp.h> +#include <asm/pgalloc.h> +#include <asm/cpu-regs.h> +#include <asm/busctl-regs.h> +#include <asm/fpu.h> +#include <asm/gdb-stub.h> +#include <asm/asm-offsets.h> + +#if 0 +#define kdebug(FMT, ...) printk(KERN_DEBUG "MISALIGN: "FMT"\n", ##__VA_ARGS__) +#else +#define kdebug(FMT, ...) do {} while (0) +#endif + +static int misalignment_addr(unsigned long *registers, unsigned long sp, + unsigned params, unsigned opcode, + unsigned long disp, + void **_address, unsigned long **_postinc, + unsigned long *_inc); + +static int misalignment_reg(unsigned long *registers, unsigned params, + unsigned opcode, unsigned long disp, + unsigned long **_register); + +static void misalignment_MOV_Lcc(struct pt_regs *regs, uint32_t opcode); + +static const unsigned Dreg_index[] = { + REG_D0 >> 2, REG_D1 >> 2, REG_D2 >> 2, REG_D3 >> 2 +}; + +static const unsigned Areg_index[] = { + REG_A0 >> 2, REG_A1 >> 2, REG_A2 >> 2, REG_A3 >> 2 +}; + +static const unsigned Rreg_index[] = { + REG_E0 >> 2, REG_E1 >> 2, REG_E2 >> 2, REG_E3 >> 2, + REG_E4 >> 2, REG_E5 >> 2, REG_E6 >> 2, REG_E7 >> 2, + REG_A0 >> 2, REG_A1 >> 2, REG_A2 >> 2, REG_A3 >> 2, + REG_D0 >> 2, REG_D1 >> 2, REG_D2 >> 2, REG_D3 >> 2 +}; + +enum format_id { + FMT_S0, + FMT_S1, + FMT_S2, + FMT_S4, + FMT_D0, + FMT_D1, + FMT_D2, + FMT_D4, + FMT_D6, + FMT_D7, + FMT_D8, + FMT_D9, + FMT_D10, +}; + +static const struct { + u_int8_t opsz, dispsz; +} format_tbl[16] = { + [FMT_S0] = { 8, 0 }, + [FMT_S1] = { 8, 8 }, + [FMT_S2] = { 8, 16 }, + [FMT_S4] = { 8, 32 }, + [FMT_D0] = { 16, 0 }, + [FMT_D1] = { 16, 8 }, + [FMT_D2] = { 16, 16 }, + [FMT_D4] = { 16, 32 }, + [FMT_D6] = { 24, 0 }, + [FMT_D7] = { 24, 8 }, + [FMT_D8] = { 24, 24 }, + [FMT_D9] = { 24, 32 }, + [FMT_D10] = { 32, 0 }, +}; + +enum value_id { + DM0, /* data reg in opcode in bits 0-1 */ + DM1, /* data reg in opcode in bits 2-3 */ + DM2, /* data reg in opcode in bits 4-5 */ + AM0, /* addr reg in opcode in bits 0-1 */ + AM1, /* addr reg in opcode in bits 2-3 */ + AM2, /* addr reg in opcode in bits 4-5 */ + RM0, /* reg in opcode in bits 0-3 */ + RM1, /* reg in opcode in bits 2-5 */ + RM2, /* reg in opcode in bits 4-7 */ + RM4, /* reg in opcode in bits 8-11 */ + RM6, /* reg in opcode in bits 12-15 */ + + RD0, /* reg in displacement in bits 0-3 */ + RD2, /* reg in displacement in bits 4-7 */ + + SP, /* stack pointer */ + + SD8, /* 8-bit signed displacement */ + SD16, /* 16-bit signed displacement */ + SD24, /* 24-bit signed displacement */ + SIMM4_2, /* 4-bit signed displacement in opcode bits 4-7 */ + SIMM8, /* 8-bit signed immediate */ + IMM8, /* 8-bit unsigned immediate */ + IMM16, /* 16-bit unsigned immediate */ + IMM24, /* 24-bit unsigned immediate */ + IMM32, /* 32-bit unsigned immediate */ + IMM32_HIGH8, /* 32-bit unsigned immediate, LSB in opcode */ + + IMM32_MEM, /* 32-bit unsigned displacement */ + IMM32_HIGH8_MEM, /* 32-bit unsigned displacement, LSB in opcode */ + + DN0 = DM0, + DN1 = DM1, + DN2 = DM2, + AN0 = AM0, + AN1 = AM1, + AN2 = AM2, + RN0 = RM0, + RN1 = RM1, + RN2 = RM2, + RN4 = RM4, + RN6 = RM6, + DI = DM1, + RI = RM2, + +}; + +struct mn10300_opcode { + const char name[8]; + u_int32_t opcode; + u_int32_t opmask; + unsigned exclusion; + + enum format_id format; + + unsigned cpu_mask; +#define AM33 330 + + unsigned params[2]; +#define MEM(ADDR) (0x80000000 | (ADDR)) +#define MEM2(ADDR1, ADDR2) (0x80000000 | (ADDR1) << 8 | (ADDR2)) +#define MEMINC(ADDR) (0x81000000 | (ADDR)) +#define MEMINC2(ADDR, INC) (0x81000000 | (ADDR) << 8 | (INC)) +}; + +/* LIBOPCODES EXCERPT + Assemble Matsushita MN10300 instructions. + Copyright 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public Licence as published by + the Free Software Foundation; either version 2 of the Licence, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public Licence for more details. + + You should have received a copy of the GNU General Public Licence + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +static const struct mn10300_opcode mn10300_opcodes[] = { +{ "mov", 0x4200, 0xf300, 0, FMT_S1, 0, {DM1, MEM2(IMM8, SP)}}, +{ "mov", 0x4300, 0xf300, 0, FMT_S1, 0, {AM1, MEM2(IMM8, SP)}}, +{ "mov", 0x5800, 0xfc00, 0, FMT_S1, 0, {MEM2(IMM8, SP), DN0}}, +{ "mov", 0x5c00, 0xfc00, 0, FMT_S1, 0, {MEM2(IMM8, SP), AN0}}, +{ "mov", 0x60, 0xf0, 0, FMT_S0, 0, {DM1, MEM(AN0)}}, +{ "mov", 0x70, 0xf0, 0, FMT_S0, 0, {MEM(AM0), DN1}}, +{ "mov", 0xf000, 0xfff0, 0, FMT_D0, 0, {MEM(AM0), AN1}}, +{ "mov", 0xf010, 0xfff0, 0, FMT_D0, 0, {AM1, MEM(AN0)}}, +{ "mov", 0xf300, 0xffc0, 0, FMT_D0, 0, {MEM2(DI, AM0), DN2}}, +{ "mov", 0xf340, 0xffc0, 0, FMT_D0, 0, {DM2, MEM2(DI, AN0)}}, +{ "mov", 0xf380, 0xffc0, 0, FMT_D0, 0, {MEM2(DI, AM0), AN2}}, +{ "mov", 0xf3c0, 0xffc0, 0, FMT_D0, 0, {AM2, MEM2(DI, AN0)}}, +{ "mov", 0xf80000, 0xfff000, 0, FMT_D1, 0, {MEM2(SD8, AM0), DN1}}, +{ "mov", 0xf81000, 0xfff000, 0, FMT_D1, 0, {DM1, MEM2(SD8, AN0)}}, +{ "mov", 0xf82000, 0xfff000, 0, FMT_D1, 0, {MEM2(SD8,AM0), AN1}}, +{ "mov", 0xf83000, 0xfff000, 0, FMT_D1, 0, {AM1, MEM2(SD8, AN0)}}, +{ "mov", 0xf90a00, 0xffff00, 0, FMT_D6, AM33, {MEM(RM0), RN2}}, +{ "mov", 0xf91a00, 0xffff00, 0, FMT_D6, AM33, {RM2, MEM(RN0)}}, +{ "mov", 0xf96a00, 0xffff00, 0x12, FMT_D6, AM33, {MEMINC(RM0), RN2}}, +{ "mov", 0xf97a00, 0xffff00, 0, FMT_D6, AM33, {RM2, MEMINC(RN0)}}, +{ "mov", 0xfa000000, 0xfff00000, 0, FMT_D2, 0, {MEM2(SD16, AM0), DN1}}, +{ "mov", 0xfa100000, 0xfff00000, 0, FMT_D2, 0, {DM1, MEM2(SD16, AN0)}}, +{ "mov", 0xfa200000, 0xfff00000, 0, FMT_D2, 0, {MEM2(SD16, AM0), AN1}}, +{ "mov", 0xfa300000, 0xfff00000, 0, FMT_D2, 0, {AM1, MEM2(SD16, AN0)}}, +{ "mov", 0xfa900000, 0xfff30000, 0, FMT_D2, 0, {AM1, MEM2(IMM16, SP)}}, +{ "mov", 0xfa910000, 0xfff30000, 0, FMT_D2, 0, {DM1, MEM2(IMM16, SP)}}, +{ "mov", 0xfab00000, 0xfffc0000, 0, FMT_D2, 0, {MEM2(IMM16, SP), AN0}}, +{ "mov", 0xfab40000, 0xfffc0000, 0, FMT_D2, 0, {MEM2(IMM16, SP), DN0}}, +{ "mov", 0xfb0a0000, 0xffff0000, 0, FMT_D7, AM33, {MEM2(SD8, RM0), RN2}}, +{ "mov", 0xfb1a0000, 0xffff0000, 0, FMT_D7, AM33, {RM2, MEM2(SD8, RN0)}}, +{ "mov", 0xfb6a0000, 0xffff0000, 0x22, FMT_D7, AM33, {MEMINC2 (RM0, SIMM8), RN2}}, +{ "mov", 0xfb7a0000, 0xffff0000, 0, FMT_D7, AM33, {RM2, MEMINC2 (RN0, SIMM8)}}, +{ "mov", 0xfb8a0000, 0xffff0f00, 0, FMT_D7, AM33, {MEM2(IMM8, SP), RN2}}, +{ "mov", 0xfb8e0000, 0xffff000f, 0, FMT_D7, AM33, {MEM2(RI, RM0), RD2}}, +{ "mov", 0xfb9a0000, 0xffff0f00, 0, FMT_D7, AM33, {RM2, MEM2(IMM8, SP)}}, +{ "mov", 0xfb9e0000, 0xffff000f, 0, FMT_D7, AM33, {RD2, MEM2(RI, RN0)}}, +{ "mov", 0xfc000000, 0xfff00000, 0, FMT_D4, 0, {MEM2(IMM32,AM0), DN1}}, +{ "mov", 0xfc100000, 0xfff00000, 0, FMT_D4, 0, {DM1, MEM2(IMM32,AN0)}}, +{ "mov", 0xfc200000, 0xfff00000, 0, FMT_D4, 0, {MEM2(IMM32,AM0), AN1}}, +{ "mov", 0xfc300000, 0xfff00000, 0, FMT_D4, 0, {AM1, MEM2(IMM32,AN0)}}, +{ "mov", 0xfc800000, 0xfff30000, 0, FMT_D4, 0, {AM1, MEM(IMM32_MEM)}}, +{ "mov", 0xfc810000, 0xfff30000, 0, FMT_D4, 0, {DM1, MEM(IMM32_MEM)}}, +{ "mov", 0xfc900000, 0xfff30000, 0, FMT_D4, 0, {AM1, MEM2(IMM32, SP)}}, +{ "mov", 0xfc910000, 0xfff30000, 0, FMT_D4, 0, {DM1, MEM2(IMM32, SP)}}, +{ "mov", 0xfca00000, 0xfffc0000, 0, FMT_D4, 0, {MEM(IMM32_MEM), AN0}}, +{ "mov", 0xfca40000, 0xfffc0000, 0, FMT_D4, 0, {MEM(IMM32_MEM), DN0}}, +{ "mov", 0xfcb00000, 0xfffc0000, 0, FMT_D4, 0, {MEM2(IMM32, SP), AN0}}, +{ "mov", 0xfcb40000, 0xfffc0000, 0, FMT_D4, 0, {MEM2(IMM32, SP), DN0}}, +{ "mov", 0xfd0a0000, 0xffff0000, 0, FMT_D8, AM33, {MEM2(SD24, RM0), RN2}}, +{ "mov", 0xfd1a0000, 0xffff0000, 0, FMT_D8, AM33, {RM2, MEM2(SD24, RN0)}}, +{ "mov", 0xfd6a0000, 0xffff0000, 0x22, FMT_D8, AM33, {MEMINC2 (RM0, IMM24), RN2}}, +{ "mov", 0xfd7a0000, 0xffff0000, 0, FMT_D8, AM33, {RM2, MEMINC2 (RN0, IMM24)}}, +{ "mov", 0xfd8a0000, 0xffff0f00, 0, FMT_D8, AM33, {MEM2(IMM24, SP), RN2}}, +{ "mov", 0xfd9a0000, 0xffff0f00, 0, FMT_D8, AM33, {RM2, MEM2(IMM24, SP)}}, +{ "mov", 0xfe0a0000, 0xffff0000, 0, FMT_D9, AM33, {MEM2(IMM32_HIGH8,RM0), RN2}}, +{ "mov", 0xfe0a0000, 0xffff0000, 0, FMT_D9, AM33, {MEM2(IMM32_HIGH8,RM0), RN2}}, +{ "mov", 0xfe0e0000, 0xffff0f00, 0, FMT_D9, AM33, {MEM(IMM32_HIGH8_MEM), RN2}}, +{ "mov", 0xfe1a0000, 0xffff0000, 0, FMT_D9, AM33, {RM2, MEM2(IMM32_HIGH8, RN0)}}, +{ "mov", 0xfe1a0000, 0xffff0000, 0, FMT_D9, AM33, {RM2, MEM2(IMM32_HIGH8, RN0)}}, +{ "mov", 0xfe1e0000, 0xffff0f00, 0, FMT_D9, AM33, {RM2, MEM(IMM32_HIGH8_MEM)}}, +{ "mov", 0xfe6a0000, 0xffff0000, 0x22, FMT_D9, AM33, {MEMINC2 (RM0, IMM32_HIGH8), RN2}}, +{ "mov", 0xfe7a0000, 0xffff0000, 0, FMT_D9, AM33, {RN2, MEMINC2 (RM0, IMM32_HIGH8)}}, +{ "mov", 0xfe8a0000, 0xffff0f00, 0, FMT_D9, AM33, {MEM2(IMM32_HIGH8, SP), RN2}}, +{ "mov", 0xfe9a0000, 0xffff0f00, 0, FMT_D9, AM33, {RM2, MEM2(IMM32_HIGH8, SP)}}, + +{ "movhu", 0xf060, 0xfff0, 0, FMT_D0, 0, {MEM(AM0), DN1}}, +{ "movhu", 0xf070, 0xfff0, 0, FMT_D0, 0, {DM1, MEM(AN0)}}, +{ "movhu", 0xf480, 0xffc0, 0, FMT_D0, 0, {MEM2(DI, AM0), DN2}}, +{ "movhu", 0xf4c0, 0xffc0, 0, FMT_D0, 0, {DM2, MEM2(DI, AN0)}}, +{ "movhu", 0xf86000, 0xfff000, 0, FMT_D1, 0, {MEM2(SD8, AM0), DN1}}, +{ "movhu", 0xf87000, 0xfff000, 0, FMT_D1, 0, {DM1, MEM2(SD8, AN0)}}, +{ "movhu", 0xf89300, 0xfff300, 0, FMT_D1, 0, {DM1, MEM2(IMM8, SP)}}, +{ "movhu", 0xf8bc00, 0xfffc00, 0, FMT_D1, 0, {MEM2(IMM8, SP), DN0}}, +{ "movhu", 0xf94a00, 0xffff00, 0, FMT_D6, AM33, {MEM(RM0), RN2}}, +{ "movhu", 0xf95a00, 0xffff00, 0, FMT_D6, AM33, {RM2, MEM(RN0)}}, +{ "movhu", 0xf9ea00, 0xffff00, 0x12, FMT_D6, AM33, {MEMINC(RM0), RN2}}, +{ "movhu", 0xf9fa00, 0xffff00, 0, FMT_D6, AM33, {RM2, MEMINC(RN0)}}, +{ "movhu", 0xfa600000, 0xfff00000, 0, FMT_D2, 0, {MEM2(SD16, AM0), DN1}}, +{ "movhu", 0xfa700000, 0xfff00000, 0, FMT_D2, 0, {DM1, MEM2(SD16, AN0)}}, +{ "movhu", 0xfa930000, 0xfff30000, 0, FMT_D2, 0, {DM1, MEM2(IMM16, SP)}}, +{ "movhu", 0xfabc0000, 0xfffc0000, 0, FMT_D2, 0, {MEM2(IMM16, SP), DN0}}, +{ "movhu", 0xfb4a0000, 0xffff0000, 0, FMT_D7, AM33, {MEM2(SD8, RM0), RN2}}, +{ "movhu", 0xfb5a0000, 0xffff0000, 0, FMT_D7, AM33, {RM2, MEM2(SD8, RN0)}}, +{ "movhu", 0xfbca0000, 0xffff0f00, 0, FMT_D7, AM33, {MEM2(IMM8, SP), RN2}}, +{ "movhu", 0xfbce0000, 0xffff000f, 0, FMT_D7, AM33, {MEM2(RI, RM0), RD2}}, +{ "movhu", 0xfbda0000, 0xffff0f00, 0, FMT_D7, AM33, {RM2, MEM2(IMM8, SP)}}, +{ "movhu", 0xfbde0000, 0xffff000f, 0, FMT_D7, AM33, {RD2, MEM2(RI, RN0)}}, +{ "movhu", 0xfbea0000, 0xffff0000, 0x22, FMT_D7, AM33, {MEMINC2 (RM0, SIMM8), RN2}}, +{ "movhu", 0xfbfa0000, 0xffff0000, 0, FMT_D7, AM33, {RM2, MEMINC2 (RN0, SIMM8)}}, +{ "movhu", 0xfc600000, 0xfff00000, 0, FMT_D4, 0, {MEM2(IMM32,AM0), DN1}}, +{ "movhu", 0xfc700000, 0xfff00000, 0, FMT_D4, 0, {DM1, MEM2(IMM32,AN0)}}, +{ "movhu", 0xfc830000, 0xfff30000, 0, FMT_D4, 0, {DM1, MEM(IMM32_MEM)}}, +{ "movhu", 0xfc930000, 0xfff30000, 0, FMT_D4, 0, {DM1, MEM2(IMM32, SP)}}, +{ "movhu", 0xfcac0000, 0xfffc0000, 0, FMT_D4, 0, {MEM(IMM32_MEM), DN0}}, +{ "movhu", 0xfcbc0000, 0xfffc0000, 0, FMT_D4, 0, {MEM2(IMM32, SP), DN0}}, +{ "movhu", 0xfd4a0000, 0xffff0000, 0, FMT_D8, AM33, {MEM2(SD24, RM0), RN2}}, +{ "movhu", 0xfd5a0000, 0xffff0000, 0, FMT_D8, AM33, {RM2, MEM2(SD24, RN0)}}, +{ "movhu", 0xfdca0000, 0xffff0f00, 0, FMT_D8, AM33, {MEM2(IMM24, SP), RN2}}, +{ "movhu", 0xfdda0000, 0xffff0f00, 0, FMT_D8, AM33, {RM2, MEM2(IMM24, SP)}}, +{ "movhu", 0xfdea0000, 0xffff0000, 0x22, FMT_D8, AM33, {MEMINC2 (RM0, IMM24), RN2}}, +{ "movhu", 0xfdfa0000, 0xffff0000, 0, FMT_D8, AM33, {RM2, MEMINC2 (RN0, IMM24)}}, +{ "movhu", 0xfe4a0000, 0xffff0000, 0, FMT_D9, AM33, {MEM2(IMM32_HIGH8,RM0), RN2}}, +{ "movhu", 0xfe4e0000, 0xffff0f00, 0, FMT_D9, AM33, {MEM(IMM32_HIGH8_MEM), RN2}}, +{ "movhu", 0xfe5a0000, 0xffff0000, 0, FMT_D9, AM33, {RM2, MEM2(IMM32_HIGH8, RN0)}}, +{ "movhu", 0xfe5e0000, 0xffff0f00, 0, FMT_D9, AM33, {RM2, MEM(IMM32_HIGH8_MEM)}}, +{ "movhu", 0xfeca0000, 0xffff0f00, 0, FMT_D9, AM33, {MEM2(IMM32_HIGH8, SP), RN2}}, +{ "movhu", 0xfeda0000, 0xffff0f00, 0, FMT_D9, AM33, {RM2, MEM2(IMM32_HIGH8, SP)}}, +{ "movhu", 0xfeea0000, 0xffff0000, 0x22, FMT_D9, AM33, {MEMINC2 (RM0, IMM32_HIGH8), RN2}}, +{ "movhu", 0xfefa0000, 0xffff0000, 0, FMT_D9, AM33, {RN2, MEMINC2 (RM0, IMM32_HIGH8)}}, + +{ "mov_llt", 0xf7e00000, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}}, +{ "mov_lgt", 0xf7e00001, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}}, +{ "mov_lge", 0xf7e00002, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}}, +{ "mov_lle", 0xf7e00003, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}}, +{ "mov_lcs", 0xf7e00004, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}}, +{ "mov_lhi", 0xf7e00005, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}}, +{ "mov_lcc", 0xf7e00006, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}}, +{ "mov_lls", 0xf7e00007, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}}, +{ "mov_leq", 0xf7e00008, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}}, +{ "mov_lne", 0xf7e00009, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}}, +{ "mov_lra", 0xf7e0000a, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}}, + +{ "", 0, 0, 0, 0, 0, {0}}, +}; + +/* + * fix up misalignment problems where possible + */ +asmlinkage void misalignment(struct pt_regs *regs, enum exception_code code) +{ + const struct exception_table_entry *fixup; + const struct mn10300_opcode *pop; + unsigned long *registers = (unsigned long *) regs; + unsigned long data, *store, *postinc, disp, inc, sp; + mm_segment_t seg; + siginfo_t info; + uint32_t opcode, noc, xo, xm; + uint8_t *pc, byte, datasz; + void *address; + unsigned tmp, npop, dispsz, loop; + + /* we don't fix up userspace misalignment faults */ + if (user_mode(regs)) + goto bus_error; + + sp = (unsigned long) regs + sizeof(*regs); + + kdebug("==>misalignment({pc=%lx,sp=%lx})", regs->pc, sp); + + if (regs->epsw & EPSW_IE) + asm volatile("or %0,epsw" : : "i"(EPSW_IE)); + + seg = get_fs(); + set_fs(KERNEL_DS); + + fixup = search_exception_tables(regs->pc); + + /* first thing to do is to match the opcode */ + pc = (u_int8_t *) regs->pc; + + if (__get_user(byte, pc) != 0) + goto fetch_error; + opcode = byte; + noc = 8; + + for (pop = mn10300_opcodes; pop->name[0]; pop++) { + npop = ilog2(pop->opcode | pop->opmask); + if (npop <= 0 || npop > 31) + continue; + npop = (npop + 8) & ~7; + + got_more_bits: + if (npop == noc) { + if ((opcode & pop->opmask) == pop->opcode) + goto found_opcode; + } else if (npop > noc) { + xo = pop->opcode >> (npop - noc); + xm = pop->opmask >> (npop - noc); + + if ((opcode & xm) != xo) + continue; + + /* we've got a partial match (an exact match on the + * first N bytes), so we need to get some more data */ + pc++; + if (__get_user(byte, pc) != 0) + goto fetch_error; + opcode = opcode << 8 | byte; + noc += 8; + goto got_more_bits; + } else { + /* there's already been a partial match as long as the + * complete match we're now considering, so this one + * should't match */ + continue; + } + } + + /* didn't manage to find a fixup */ + printk(KERN_CRIT "MISALIGN: %lx: unsupported instruction %x\n", + regs->pc, opcode); + +failed: + set_fs(seg); + if (die_if_no_fixup("misalignment error", regs, code)) + return; + +bus_error: + info.si_signo = SIGBUS; + info.si_errno = 0; + info.si_code = BUS_ADRALN; + info.si_addr = (void *) regs->pc; + force_sig_info(SIGBUS, &info, current); + return; + + /* error reading opcodes */ +fetch_error: + printk(KERN_CRIT + "MISALIGN: %p: fault whilst reading instruction data\n", + pc); + goto failed; + +bad_addr_mode: + printk(KERN_CRIT + "MISALIGN: %lx: unsupported addressing mode %x\n", + regs->pc, opcode); + goto failed; + +bad_reg_mode: + printk(KERN_CRIT + "MISALIGN: %lx: unsupported register mode %x\n", + regs->pc, opcode); + goto failed; + +unsupported_instruction: + printk(KERN_CRIT + "MISALIGN: %lx: unsupported instruction %x (%s)\n", + regs->pc, opcode, pop->name); + goto failed; + +transfer_failed: + set_fs(seg); + if (fixup) { + regs->pc = fixup->fixup; + return; + } + if (die_if_no_fixup("misalignment fixup", regs, code)) + return; + + info.si_signo = SIGSEGV; + info.si_errno = 0; + info.si_code = 0; + info.si_addr = (void *) regs->pc; + force_sig_info(SIGSEGV, &info, current); + return; + + /* we matched the opcode */ +found_opcode: + kdebug("%lx: %x==%x { %x, %x }", + regs->pc, opcode, pop->opcode, pop->params[0], pop->params[1]); + + tmp = format_tbl[pop->format].opsz; + BUG_ON(tmp > noc); /* match was less complete than it ought to have been */ + + if (tmp < noc) { + tmp = noc - tmp; + opcode >>= tmp; + pc -= tmp >> 3; + } + + /* grab the extra displacement (note it's LSB first) */ + disp = 0; + dispsz = format_tbl[pop->format].dispsz; + for (loop = 0; loop < dispsz; loop += 8) { + pc++; + if (__get_user(byte, pc) != 0) + goto fetch_error; + disp |= byte << loop; + kdebug("{%p} disp[%02x]=%02x", pc, loop, byte); + } + + kdebug("disp=%lx", disp); + + set_fs(KERNEL_XDS); + if (fixup) + set_fs(seg); + + tmp = (pop->params[0] ^ pop->params[1]) & 0x80000000; + if (!tmp) { + printk(KERN_CRIT + "MISALIGN: %lx: insn not move to/from memory %x\n", + regs->pc, opcode); + goto failed; + } + + /* determine the data transfer size of the move */ + if (pop->name[3] == 0 || /* "mov" */ + pop->name[4] == 'l') /* mov_lcc */ + inc = datasz = 4; + else if (pop->name[3] == 'h') /* movhu */ + inc = datasz = 2; + else + goto unsupported_instruction; + + if (pop->params[0] & 0x80000000) { + /* move memory to register */ + if (!misalignment_addr(registers, sp, + pop->params[0], opcode, disp, + &address, &postinc, &inc)) + goto bad_addr_mode; + + if (!misalignment_reg(registers, pop->params[1], opcode, disp, + &store)) + goto bad_reg_mode; + + kdebug("mov%u (%p),DARn", datasz, address); + if (copy_from_user(&data, (void *) address, datasz) != 0) + goto transfer_failed; + if (pop->params[0] & 0x1000000) { + kdebug("inc=%lx", inc); + *postinc += inc; + } + + *store = data; + kdebug("loaded %lx", data); + } else { + /* move register to memory */ + if (!misalignment_reg(registers, pop->params[0], opcode, disp, + &store)) + goto bad_reg_mode; + + if (!misalignment_addr(registers, sp, + pop->params[1], opcode, disp, + &address, &postinc, &inc)) + goto bad_addr_mode; + + data = *store; + + kdebug("mov%u %lx,(%p)", datasz, data, address); + if (copy_to_user((void *) address, &data, datasz) != 0) + goto transfer_failed; + if (pop->params[1] & 0x1000000) + *postinc += inc; + } + + tmp = format_tbl[pop->format].opsz + format_tbl[pop->format].dispsz; + regs->pc += tmp >> 3; + + /* handle MOV_Lcc, which are currently the only FMT_D10 insns that + * access memory */ + if (pop->format == FMT_D10) + misalignment_MOV_Lcc(regs, opcode); + + set_fs(seg); +} + +/* + * determine the address that was being accessed + */ +static int misalignment_addr(unsigned long *registers, unsigned long sp, + unsigned params, unsigned opcode, + unsigned long disp, + void **_address, unsigned long **_postinc, + unsigned long *_inc) +{ + unsigned long *postinc = NULL, address = 0, tmp; + + if (!(params & 0x1000000)) { + kdebug("noinc"); + *_inc = 0; + _inc = NULL; + } + + params &= 0x00ffffff; + + do { + switch (params & 0xff) { + case DM0: + postinc = ®isters[Dreg_index[opcode & 0x03]]; + address += *postinc; + break; + case DM1: + postinc = ®isters[Dreg_index[opcode >> 2 & 0x03]]; + address += *postinc; + break; + case DM2: + postinc = ®isters[Dreg_index[opcode >> 4 & 0x03]]; + address += *postinc; + break; + case AM0: + postinc = ®isters[Areg_index[opcode & 0x03]]; + address += *postinc; + break; + case AM1: + postinc = ®isters[Areg_index[opcode >> 2 & 0x03]]; + address += *postinc; + break; + case AM2: + postinc = ®isters[Areg_index[opcode >> 4 & 0x03]]; + address += *postinc; + break; + case RM0: + postinc = ®isters[Rreg_index[opcode & 0x0f]]; + address += *postinc; + break; + case RM1: + postinc = ®isters[Rreg_index[opcode >> 2 & 0x0f]]; + address += *postinc; + break; + case RM2: + postinc = ®isters[Rreg_index[opcode >> 4 & 0x0f]]; + address += *postinc; + break; + case RM4: + postinc = ®isters[Rreg_index[opcode >> 8 & 0x0f]]; + address += *postinc; + break; + case RM6: + postinc = ®isters[Rreg_index[opcode >> 12 & 0x0f]]; + address += *postinc; + break; + case RD0: + postinc = ®isters[Rreg_index[disp & 0x0f]]; + address += *postinc; + break; + case RD2: + postinc = ®isters[Rreg_index[disp >> 4 & 0x0f]]; + address += *postinc; + break; + case SP: + address += sp; + break; + + /* displacements are either to be added to the address + * before use, or, in the case of post-inc addressing, + * to be added into the base register after use */ + case SD8: + case SIMM8: + disp = (long) (int8_t) (disp & 0xff); + goto displace_or_inc; + case SD16: + disp = (long) (int16_t) (disp & 0xffff); + goto displace_or_inc; + case SD24: + tmp = disp << 8; + asm("asr 8,%0" : "=r"(tmp) : "0"(tmp) : "cc"); + disp = (long) tmp; + goto displace_or_inc; + case SIMM4_2: + tmp = opcode >> 4 & 0x0f; + tmp <<= 28; + asm("asr 28,%0" : "=r"(tmp) : "0"(tmp) : "cc"); + disp = (long) tmp; + goto displace_or_inc; + case IMM8: + disp &= 0x000000ff; + goto displace_or_inc; + case IMM16: + disp &= 0x0000ffff; + goto displace_or_inc; + case IMM24: + disp &= 0x00ffffff; + goto displace_or_inc; + case IMM32: + case IMM32_MEM: + case IMM32_HIGH8: + case IMM32_HIGH8_MEM: + displace_or_inc: + kdebug("%s %lx", _inc ? "incr" : "disp", disp); + if (!_inc) + address += disp; + else + *_inc = disp; + break; + default: + BUG(); + return 0; + } + } while ((params >>= 8)); + + *_address = (void *) address; + *_postinc = postinc; + return 1; +} + +/* + * determine the register that is acting as source/dest + */ +static int misalignment_reg(unsigned long *registers, unsigned params, + unsigned opcode, unsigned long disp, + unsigned long **_register) +{ + params &= 0x7fffffff; + + if (params & 0xffffff00) + return 0; + + switch (params & 0xff) { + case DM0: + *_register = ®isters[Dreg_index[opcode & 0x03]]; + break; + case DM1: + *_register = ®isters[Dreg_index[opcode >> 2 & 0x03]]; + break; + case DM2: + *_register = ®isters[Dreg_index[opcode >> 4 & 0x03]]; + break; + case AM0: + *_register = ®isters[Areg_index[opcode & 0x03]]; + break; + case AM1: + *_register = ®isters[Areg_index[opcode >> 2 & 0x03]]; + break; + case AM2: + *_register = ®isters[Areg_index[opcode >> 4 & 0x03]]; + break; + case RM0: + *_register = ®isters[Rreg_index[opcode & 0x0f]]; + break; + case RM1: + *_register = ®isters[Rreg_index[opcode >> 2 & 0x0f]]; + break; + case RM2: + *_register = ®isters[Rreg_index[opcode >> 4 & 0x0f]]; + break; + case RM4: + *_register = ®isters[Rreg_index[opcode >> 8 & 0x0f]]; + break; + case RM6: + *_register = ®isters[Rreg_index[opcode >> 12 & 0x0f]]; + break; + case RD0: + *_register = ®isters[Rreg_index[disp & 0x0f]]; + break; + case RD2: + *_register = ®isters[Rreg_index[disp >> 4 & 0x0f]]; + break; + case SP: + *_register = ®isters[REG_SP >> 2]; + break; + + default: + BUG(); + return 0; + } + + return 1; +} + +/* + * handle the conditional loop part of the move-and-loop instructions + */ +static void misalignment_MOV_Lcc(struct pt_regs *regs, uint32_t opcode) +{ + unsigned long epsw = regs->epsw; + unsigned long NxorV; + + kdebug("MOV_Lcc %x [flags=%lx]", opcode, epsw & 0xf); + + /* calculate N^V and shift onto the same bit position as Z */ + NxorV = ((epsw >> 3) ^ epsw >> 1) & 1; + + switch (opcode & 0xf) { + case 0x0: /* MOV_LLT: N^V */ + if (NxorV) + goto take_the_loop; + return; + case 0x1: /* MOV_LGT: ~(Z or (N^V))*/ + if (!((epsw & EPSW_FLAG_Z) | NxorV)) + goto take_the_loop; + return; + case 0x2: /* MOV_LGE: ~(N^V) */ + if (!NxorV) + goto take_the_loop; + return; + case 0x3: /* MOV_LLE: Z or (N^V) */ + if ((epsw & EPSW_FLAG_Z) | NxorV) + goto take_the_loop; + return; + + case 0x4: /* MOV_LCS: C */ + if (epsw & EPSW_FLAG_C) + goto take_the_loop; + return; + case 0x5: /* MOV_LHI: ~(C or Z) */ + if (!(epsw & (EPSW_FLAG_C | EPSW_FLAG_Z))) + goto take_the_loop; + return; + case 0x6: /* MOV_LCC: ~C */ + if (!(epsw & EPSW_FLAG_C)) + goto take_the_loop; + return; + case 0x7: /* MOV_LLS: C or Z */ + if (epsw & (EPSW_FLAG_C | EPSW_FLAG_Z)) + goto take_the_loop; + return; + + case 0x8: /* MOV_LEQ: Z */ + if (epsw & EPSW_FLAG_Z) + goto take_the_loop; + return; + case 0x9: /* MOV_LNE: ~Z */ + if (!(epsw & EPSW_FLAG_Z)) + goto take_the_loop; + return; + case 0xa: /* MOV_LRA: always */ + goto take_the_loop; + + default: + BUG(); + } + +take_the_loop: + /* wind the PC back to just after the SETLB insn */ + kdebug("loop LAR=%lx", regs->lar); + regs->pc = regs->lar - 4; +} + +/* + * misalignment handler tests + */ +#ifdef CONFIG_TEST_MISALIGNMENT_HANDLER +static u8 __initdata testbuf[512] __attribute__((aligned(16))) = { + [257] = 0x11, + [258] = 0x22, + [259] = 0x33, + [260] = 0x44, +}; + +#define ASSERTCMP(X, OP, Y) \ +do { \ + if (unlikely(!((X) OP (Y)))) { \ + printk(KERN_ERR "\n"); \ + printk(KERN_ERR "MISALIGN: Assertion failed at line %u\n", \ + __LINE__); \ + printk(KERN_ERR "0x%lx " #OP " 0x%lx is false\n", \ + (unsigned long)(X), (unsigned long)(Y)); \ + BUG(); \ + } \ +} while(0) + +static int __init test_misalignment(void) +{ + register void *r asm("e0"); + register u32 y asm("e1"); + void *p = testbuf, *q; + u32 tmp, tmp2, x; + + printk(KERN_NOTICE "==>test_misalignment() [testbuf=%p]\n", p); + p++; + + printk(KERN_NOTICE "___ MOV (Am),Dn ___\n"); + q = p + 256; + asm volatile("mov (%0),%1" : "+a"(q), "=d"(x)); + ASSERTCMP(q, ==, p + 256); + ASSERTCMP(x, ==, 0x44332211); + + printk(KERN_NOTICE "___ MOV (256,Am),Dn ___\n"); + q = p; + asm volatile("mov (256,%0),%1" : "+a"(q), "=d"(x)); + ASSERTCMP(q, ==, p); + ASSERTCMP(x, ==, 0x44332211); + + printk(KERN_NOTICE "___ MOV (Di,Am),Dn ___\n"); + tmp = 256; + q = p; + asm volatile("mov (%2,%0),%1" : "+a"(q), "=d"(x), "+d"(tmp)); + ASSERTCMP(q, ==, p); + ASSERTCMP(x, ==, 0x44332211); + ASSERTCMP(tmp, ==, 256); + + printk(KERN_NOTICE "___ MOV (256,Rm),Rn ___\n"); + r = p; + asm volatile("mov (256,%0),%1" : "+r"(r), "=r"(y)); + ASSERTCMP(r, ==, p); + ASSERTCMP(y, ==, 0x44332211); + + printk(KERN_NOTICE "___ MOV (Rm+),Rn ___\n"); + r = p + 256; + asm volatile("mov (%0+),%1" : "+r"(r), "=r"(y)); + ASSERTCMP(r, ==, p + 256 + 4); + ASSERTCMP(y, ==, 0x44332211); + + printk(KERN_NOTICE "___ MOV (Rm+,8),Rn ___\n"); + r = p + 256; + asm volatile("mov (%0+,8),%1" : "+r"(r), "=r"(y)); + ASSERTCMP(r, ==, p + 256 + 8); + ASSERTCMP(y, ==, 0x44332211); + + printk(KERN_NOTICE "___ MOV (7,SP),Rn ___\n"); + asm volatile( + "add -16,sp \n" + "mov +0x11,%0 \n" + "movbu %0,(7,sp) \n" + "mov +0x22,%0 \n" + "movbu %0,(8,sp) \n" + "mov +0x33,%0 \n" + "movbu %0,(9,sp) \n" + "mov +0x44,%0 \n" + "movbu %0,(10,sp) \n" + "mov (7,sp),%1 \n" + "add +16,sp \n" + : "+a"(q), "=d"(x)); + ASSERTCMP(x, ==, 0x44332211); + + printk(KERN_NOTICE "___ MOV (259,SP),Rn ___\n"); + asm volatile( + "add -264,sp \n" + "mov +0x11,%0 \n" + "movbu %0,(259,sp) \n" + "mov +0x22,%0 \n" + "movbu %0,(260,sp) \n" + "mov +0x33,%0 \n" + "movbu %0,(261,sp) \n" + "mov +0x55,%0 \n" + "movbu %0,(262,sp) \n" + "mov (259,sp),%1 \n" + "add +264,sp \n" + : "+d"(tmp), "=d"(x)); + ASSERTCMP(x, ==, 0x55332211); + + printk(KERN_NOTICE "___ MOV (260,SP),Rn ___\n"); + asm volatile( + "add -264,sp \n" + "mov +0x11,%0 \n" + "movbu %0,(260,sp) \n" + "mov +0x22,%0 \n" + "movbu %0,(261,sp) \n" + "mov +0x33,%0 \n" + "movbu %0,(262,sp) \n" + "mov +0x55,%0 \n" + "movbu %0,(263,sp) \n" + "mov (260,sp),%1 \n" + "add +264,sp \n" + : "+d"(tmp), "=d"(x)); + ASSERTCMP(x, ==, 0x55332211); + + + printk(KERN_NOTICE "___ MOV_LNE ___\n"); + tmp = 1; + tmp2 = 2; + q = p + 256; + asm volatile( + "setlb \n" + "mov %2,%3 \n" + "mov %1,%2 \n" + "cmp +0,%1 \n" + "mov_lne (%0+,4),%1" + : "+r"(q), "+d"(tmp), "+d"(tmp2), "=d"(x) + : + : "cc"); + ASSERTCMP(q, ==, p + 256 + 12); + ASSERTCMP(x, ==, 0x44332211); + + printk(KERN_NOTICE "___ MOV in SETLB ___\n"); + tmp = 1; + tmp2 = 2; + q = p + 256; + asm volatile( + "setlb \n" + "mov %1,%3 \n" + "mov (%0+),%1 \n" + "cmp +0,%1 \n" + "lne " + : "+a"(q), "+d"(tmp), "+d"(tmp2), "=d"(x) + : + : "cc"); + + ASSERTCMP(q, ==, p + 256 + 8); + ASSERTCMP(x, ==, 0x44332211); + + printk(KERN_NOTICE "<==test_misalignment()\n"); + return 0; +} + +arch_initcall(test_misalignment); + +#endif /* CONFIG_TEST_MISALIGNMENT_HANDLER */ diff --git a/arch/mn10300/mm/mmu-context.c b/arch/mn10300/mm/mmu-context.c new file mode 100644 index 00000000..a4f7d3dc --- /dev/null +++ b/arch/mn10300/mm/mmu-context.c @@ -0,0 +1,62 @@ +/* MN10300 MMU context allocation and management + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/sched.h> +#include <linux/mm.h> +#include <asm/mmu_context.h> +#include <asm/tlbflush.h> + +#ifdef CONFIG_MN10300_TLB_USE_PIDR +/* + * list of the MMU contexts last allocated on each CPU + */ +unsigned long mmu_context_cache[NR_CPUS] = { + [0 ... NR_CPUS - 1] = + MMU_CONTEXT_FIRST_VERSION * 2 - (1 - MMU_CONTEXT_TLBPID_LOCK_NR), +}; +#endif /* CONFIG_MN10300_TLB_USE_PIDR */ + +/* + * preemptively set a TLB entry + */ +void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep) +{ + unsigned long pteu, ptel, cnx, flags; + pte_t pte = *ptep; + + addr &= PAGE_MASK; + ptel = pte_val(pte) & ~(xPTEL_UNUSED1 | xPTEL_UNUSED2); + + /* make sure the context doesn't migrate and defend against + * interference from vmalloc'd regions */ + local_irq_save(flags); + + cnx = ~MMU_NO_CONTEXT; +#ifdef CONFIG_MN10300_TLB_USE_PIDR + cnx = mm_context(vma->vm_mm); +#endif + + if (cnx != MMU_NO_CONTEXT) { + pteu = addr; +#ifdef CONFIG_MN10300_TLB_USE_PIDR + pteu |= cnx & MMU_CONTEXT_TLBPID_MASK; +#endif + if (!(pte_val(pte) & _PAGE_NX)) { + IPTEU = pteu; + if (IPTEL & xPTEL_V) + IPTEL = ptel; + } + DPTEU = pteu; + if (DPTEL & xPTEL_V) + DPTEL = ptel; + } + + local_irq_restore(flags); +} diff --git a/arch/mn10300/mm/pgtable.c b/arch/mn10300/mm/pgtable.c new file mode 100644 index 00000000..450f7ba3 --- /dev/null +++ b/arch/mn10300/mm/pgtable.c @@ -0,0 +1,170 @@ +/* MN10300 Page table management + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Modified by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/gfp.h> +#include <linux/mm.h> +#include <linux/swap.h> +#include <linux/smp.h> +#include <linux/highmem.h> +#include <linux/pagemap.h> +#include <linux/spinlock.h> +#include <linux/quicklist.h> + +#include <asm/system.h> +#include <asm/pgtable.h> +#include <asm/pgalloc.h> +#include <asm/tlb.h> +#include <asm/tlbflush.h> + +/* + * Associate a large virtual page frame with a given physical page frame + * and protection flags for that frame. pfn is for the base of the page, + * vaddr is what the page gets mapped to - both must be properly aligned. + * The pmd must already be instantiated. Assumes PAE mode. + */ +void set_pmd_pfn(unsigned long vaddr, unsigned long pfn, pgprot_t flags) +{ + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + + if (vaddr & (PMD_SIZE-1)) { /* vaddr is misaligned */ + printk(KERN_ERR "set_pmd_pfn: vaddr misaligned\n"); + return; /* BUG(); */ + } + if (pfn & (PTRS_PER_PTE-1)) { /* pfn is misaligned */ + printk(KERN_ERR "set_pmd_pfn: pfn misaligned\n"); + return; /* BUG(); */ + } + pgd = swapper_pg_dir + pgd_index(vaddr); + if (pgd_none(*pgd)) { + printk(KERN_ERR "set_pmd_pfn: pgd_none\n"); + return; /* BUG(); */ + } + pud = pud_offset(pgd, vaddr); + pmd = pmd_offset(pud, vaddr); + set_pmd(pmd, pfn_pmd(pfn, flags)); + /* + * It's enough to flush this one mapping. + * (PGE mappings get flushed as well) + */ + local_flush_tlb_one(vaddr); +} + +pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) +{ + pte_t *pte = (pte_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT); + if (pte) + clear_page(pte); + return pte; +} + +struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) +{ + struct page *pte; + +#ifdef CONFIG_HIGHPTE + pte = alloc_pages(GFP_KERNEL|__GFP_HIGHMEM|__GFP_REPEAT, 0); +#else + pte = alloc_pages(GFP_KERNEL|__GFP_REPEAT, 0); +#endif + if (pte) + clear_highpage(pte); + return pte; +} + +/* + * List of all pgd's needed for non-PAE so it can invalidate entries + * in both cached and uncached pgd's; not needed for PAE since the + * kernel pmd is shared. If PAE were not to share the pmd a similar + * tactic would be needed. This is essentially codepath-based locking + * against pageattr.c; it is the unique case in which a valid change + * of kernel pagetables can't be lazily synchronized by vmalloc faults. + * vmalloc faults work because attached pagetables are never freed. + * If the locking proves to be non-performant, a ticketing scheme with + * checks at dup_mmap(), exec(), and other mmlist addition points + * could be used. The locking scheme was chosen on the basis of + * manfred's recommendations and having no core impact whatsoever. + * -- wli + */ +DEFINE_SPINLOCK(pgd_lock); +struct page *pgd_list; + +static inline void pgd_list_add(pgd_t *pgd) +{ + struct page *page = virt_to_page(pgd); + page->index = (unsigned long) pgd_list; + if (pgd_list) + set_page_private(pgd_list, (unsigned long) &page->index); + pgd_list = page; + set_page_private(page, (unsigned long) &pgd_list); +} + +static inline void pgd_list_del(pgd_t *pgd) +{ + struct page *next, **pprev, *page = virt_to_page(pgd); + next = (struct page *) page->index; + pprev = (struct page **) page_private(page); + *pprev = next; + if (next) + set_page_private(next, (unsigned long) pprev); +} + +void pgd_ctor(void *pgd) +{ + unsigned long flags; + + if (PTRS_PER_PMD == 1) + spin_lock_irqsave(&pgd_lock, flags); + + memcpy((pgd_t *)pgd + USER_PTRS_PER_PGD, + swapper_pg_dir + USER_PTRS_PER_PGD, + (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); + + if (PTRS_PER_PMD > 1) + return; + + pgd_list_add(pgd); + spin_unlock_irqrestore(&pgd_lock, flags); + memset(pgd, 0, USER_PTRS_PER_PGD * sizeof(pgd_t)); +} + +/* never called when PTRS_PER_PMD > 1 */ +void pgd_dtor(void *pgd) +{ + unsigned long flags; /* can be called from interrupt context */ + + spin_lock_irqsave(&pgd_lock, flags); + pgd_list_del(pgd); + spin_unlock_irqrestore(&pgd_lock, flags); +} + +pgd_t *pgd_alloc(struct mm_struct *mm) +{ + return quicklist_alloc(0, GFP_KERNEL, pgd_ctor); +} + +void pgd_free(struct mm_struct *mm, pgd_t *pgd) +{ + quicklist_free(0, pgd_dtor, pgd); +} + +void __init pgtable_cache_init(void) +{ +} + +void check_pgt_cache(void) +{ + quicklist_trim(0, pgd_dtor, 25, 16); +} diff --git a/arch/mn10300/mm/tlb-mn10300.S b/arch/mn10300/mm/tlb-mn10300.S new file mode 100644 index 00000000..b9940177 --- /dev/null +++ b/arch/mn10300/mm/tlb-mn10300.S @@ -0,0 +1,220 @@ +############################################################################### +# +# TLB loading functions +# +# Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Modified by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/intctl-regs.h> +#include <asm/frame.inc> +#include <asm/page.h> +#include <asm/pgtable.h> + +############################################################################### +# +# Instruction TLB Miss handler entry point +# +############################################################################### + .type itlb_miss,@function +ENTRY(itlb_miss) +#ifdef CONFIG_GDBSTUB + movm [d2,d3,a2],(sp) +#else + or EPSW_nAR,epsw # switch D0-D3 & A0-A3 to the alternate + # register bank + nop + nop + nop +#endif + +#if defined(CONFIG_ERRATUM_NEED_TO_RELOAD_MMUCTR) + mov (MMUCTR),d2 + mov d2,(MMUCTR) +#endif + + and ~EPSW_NMID,epsw + mov (IPTEU),d3 + mov (PTBR),a2 + mov d3,d2 + and 0xffc00000,d2 + lsr 20,d2 + mov (a2,d2),a2 # PTD *ptd = PGD[addr 31..22] + btst _PAGE_VALID,a2 + beq itlb_miss_fault # jump if doesn't point anywhere + + and ~(PAGE_SIZE-1),a2 + mov d3,d2 + and 0x003ff000,d2 + lsr 10,d2 + add d2,a2 + mov (a2),d2 # get pte from PTD[addr 21..12] + btst _PAGE_VALID,d2 + beq itlb_miss_fault # jump if doesn't point to a page + # (might be a swap id) +#if ((_PAGE_ACCESSED & 0xffffff00) == 0) + bset _PAGE_ACCESSED,(0,a2) +#elif ((_PAGE_ACCESSED & 0xffff00ff) == 0) + bset +(_PAGE_ACCESSED >> 8),(1,a2) +#else +#error "_PAGE_ACCESSED value is out of range" +#endif + and ~xPTEL2_UNUSED1,d2 +itlb_miss_set: + mov d2,(IPTEL2) # change the TLB +#ifdef CONFIG_GDBSTUB + movm (sp),[d2,d3,a2] +#endif + rti + +itlb_miss_fault: + mov _PAGE_VALID,d2 # force address error handler to be + # invoked + bra itlb_miss_set + + .size itlb_miss, . - itlb_miss + +############################################################################### +# +# Data TLB Miss handler entry point +# +############################################################################### + .type dtlb_miss,@function +ENTRY(dtlb_miss) +#ifdef CONFIG_GDBSTUB + movm [d2,d3,a2],(sp) +#else + or EPSW_nAR,epsw # switch D0-D3 & A0-A3 to the alternate + # register bank + nop + nop + nop +#endif + +#if defined(CONFIG_ERRATUM_NEED_TO_RELOAD_MMUCTR) + mov (MMUCTR),d2 + mov d2,(MMUCTR) +#endif + + and ~EPSW_NMID,epsw + mov (DPTEU),d3 + mov (PTBR),a2 + mov d3,d2 + and 0xffc00000,d2 + lsr 20,d2 + mov (a2,d2),a2 # PTD *ptd = PGD[addr 31..22] + btst _PAGE_VALID,a2 + beq dtlb_miss_fault # jump if doesn't point anywhere + + and ~(PAGE_SIZE-1),a2 + mov d3,d2 + and 0x003ff000,d2 + lsr 10,d2 + add d2,a2 + mov (a2),d2 # get pte from PTD[addr 21..12] + btst _PAGE_VALID,d2 + beq dtlb_miss_fault # jump if doesn't point to a page + # (might be a swap id) +#if ((_PAGE_ACCESSED & 0xffffff00) == 0) + bset _PAGE_ACCESSED,(0,a2) +#elif ((_PAGE_ACCESSED & 0xffff00ff) == 0) + bset +(_PAGE_ACCESSED >> 8),(1,a2) +#else +#error "_PAGE_ACCESSED value is out of range" +#endif + and ~xPTEL2_UNUSED1,d2 +dtlb_miss_set: + mov d2,(DPTEL2) # change the TLB +#ifdef CONFIG_GDBSTUB + movm (sp),[d2,d3,a2] +#endif + rti + +dtlb_miss_fault: + mov _PAGE_VALID,d2 # force address error handler to be + # invoked + bra dtlb_miss_set + .size dtlb_miss, . - dtlb_miss + +############################################################################### +# +# Instruction TLB Address Error handler entry point +# +############################################################################### + .type itlb_aerror,@function +ENTRY(itlb_aerror) + add -4,sp + SAVE_ALL + +#if defined(CONFIG_ERRATUM_NEED_TO_RELOAD_MMUCTR) + mov (MMUCTR),d1 + mov d1,(MMUCTR) +#endif + + and ~EPSW_NMID,epsw + add -4,sp # need to pass three params + + # calculate the fault code + movhu (MMUFCR_IFC),d1 + or 0x00010000,d1 # it's an instruction fetch + + # determine the page address + mov (IPTEU),d0 + and PAGE_MASK,d0 + mov d0,(12,sp) + + clr d0 + mov d0,(IPTEL2) + + or EPSW_IE,epsw + mov fp,d0 + call do_page_fault[],0 # do_page_fault(regs,code,addr + + jmp ret_from_exception + .size itlb_aerror, . - itlb_aerror + +############################################################################### +# +# Data TLB Address Error handler entry point +# +############################################################################### + .type dtlb_aerror,@function +ENTRY(dtlb_aerror) + add -4,sp + SAVE_ALL + +#if defined(CONFIG_ERRATUM_NEED_TO_RELOAD_MMUCTR) + mov (MMUCTR),d1 + mov d1,(MMUCTR) +#endif + + add -4,sp # need to pass three params + and ~EPSW_NMID,epsw + + # calculate the fault code + movhu (MMUFCR_DFC),d1 + + # determine the page address + mov (DPTEU),a2 + mov a2,d0 + and PAGE_MASK,d0 + mov d0,(12,sp) + + clr d0 + mov d0,(DPTEL2) + + or EPSW_IE,epsw + mov fp,d0 + call do_page_fault[],0 # do_page_fault(regs,code,addr + + jmp ret_from_exception + .size dtlb_aerror, . - dtlb_aerror diff --git a/arch/mn10300/mm/tlb-smp.c b/arch/mn10300/mm/tlb-smp.c new file mode 100644 index 00000000..9a777498 --- /dev/null +++ b/arch/mn10300/mm/tlb-smp.c @@ -0,0 +1,214 @@ +/* SMP TLB support routines. + * + * Copyright (C) 2006-2008 Panasonic Corporation + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/init.h> +#include <linux/jiffies.h> +#include <linux/cpumask.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/profile.h> +#include <linux/smp.h> +#include <asm/tlbflush.h> +#include <asm/system.h> +#include <asm/bitops.h> +#include <asm/processor.h> +#include <asm/bug.h> +#include <asm/exceptions.h> +#include <asm/hardirq.h> +#include <asm/fpu.h> +#include <asm/mmu_context.h> +#include <asm/thread_info.h> +#include <asm/cpu-regs.h> +#include <asm/intctl-regs.h> + +/* + * For flush TLB + */ +#define FLUSH_ALL 0xffffffff + +static cpumask_t flush_cpumask; +static struct mm_struct *flush_mm; +static unsigned long flush_va; +static DEFINE_SPINLOCK(tlbstate_lock); + +DEFINE_PER_CPU_SHARED_ALIGNED(struct tlb_state, cpu_tlbstate) = { + &init_mm, 0 +}; + +static void flush_tlb_others(cpumask_t cpumask, struct mm_struct *mm, + unsigned long va); +static void do_flush_tlb_all(void *info); + +/** + * smp_flush_tlb - Callback to invalidate the TLB. + * @unused: Callback context (ignored). + */ +void smp_flush_tlb(void *unused) +{ + unsigned long cpu_id; + + cpu_id = get_cpu(); + + if (!cpumask_test_cpu(cpu_id, &flush_cpumask)) + /* This was a BUG() but until someone can quote me the line + * from the intel manual that guarantees an IPI to multiple + * CPUs is retried _only_ on the erroring CPUs its staying as a + * return + * + * BUG(); + */ + goto out; + + if (flush_va == FLUSH_ALL) + local_flush_tlb(); + else + local_flush_tlb_page(flush_mm, flush_va); + + smp_mb__before_clear_bit(); + cpumask_clear_cpu(cpu_id, &flush_cpumask); + smp_mb__after_clear_bit(); +out: + put_cpu(); +} + +/** + * flush_tlb_others - Tell the specified CPUs to invalidate their TLBs + * @cpumask: The list of CPUs to target. + * @mm: The VM context to flush from (if va!=FLUSH_ALL). + * @va: Virtual address to flush or FLUSH_ALL to flush everything. + */ +static void flush_tlb_others(cpumask_t cpumask, struct mm_struct *mm, + unsigned long va) +{ + cpumask_t tmp; + + /* A couple of sanity checks (to be removed): + * - mask must not be empty + * - current CPU must not be in mask + * - we do not send IPIs to as-yet unbooted CPUs. + */ + BUG_ON(!mm); + BUG_ON(cpumask_empty(&cpumask)); + BUG_ON(cpumask_test_cpu(smp_processor_id(), &cpumask)); + + cpumask_and(&tmp, &cpumask, cpu_online_mask); + BUG_ON(!cpumask_equal(&cpumask, &tmp)); + + /* I'm not happy about this global shared spinlock in the MM hot path, + * but we'll see how contended it is. + * + * Temporarily this turns IRQs off, so that lockups are detected by the + * NMI watchdog. + */ + spin_lock(&tlbstate_lock); + + flush_mm = mm; + flush_va = va; +#if NR_CPUS <= BITS_PER_LONG + atomic_set_mask(cpumask.bits[0], &flush_cpumask.bits[0]); +#else +#error Not supported. +#endif + + /* FIXME: if NR_CPUS>=3, change send_IPI_mask */ + smp_call_function(smp_flush_tlb, NULL, 1); + + while (!cpumask_empty(&flush_cpumask)) + /* Lockup detection does not belong here */ + smp_mb(); + + flush_mm = NULL; + flush_va = 0; + spin_unlock(&tlbstate_lock); +} + +/** + * flush_tlb_mm - Invalidate TLB of specified VM context + * @mm: The VM context to invalidate. + */ +void flush_tlb_mm(struct mm_struct *mm) +{ + cpumask_t cpu_mask; + + preempt_disable(); + cpumask_copy(&cpu_mask, mm_cpumask(mm)); + cpumask_clear_cpu(smp_processor_id(), &cpu_mask); + + local_flush_tlb(); + if (!cpumask_empty(&cpu_mask)) + flush_tlb_others(cpu_mask, mm, FLUSH_ALL); + + preempt_enable(); +} + +/** + * flush_tlb_current_task - Invalidate TLB of current task + */ +void flush_tlb_current_task(void) +{ + struct mm_struct *mm = current->mm; + cpumask_t cpu_mask; + + preempt_disable(); + cpumask_copy(&cpu_mask, mm_cpumask(mm)); + cpumask_clear_cpu(smp_processor_id(), &cpu_mask); + + local_flush_tlb(); + if (!cpumask_empty(&cpu_mask)) + flush_tlb_others(cpu_mask, mm, FLUSH_ALL); + + preempt_enable(); +} + +/** + * flush_tlb_page - Invalidate TLB of page + * @vma: The VM context to invalidate the page for. + * @va: The virtual address of the page to invalidate. + */ +void flush_tlb_page(struct vm_area_struct *vma, unsigned long va) +{ + struct mm_struct *mm = vma->vm_mm; + cpumask_t cpu_mask; + + preempt_disable(); + cpumask_copy(&cpu_mask, mm_cpumask(mm)); + cpumask_clear_cpu(smp_processor_id(), &cpu_mask); + + local_flush_tlb_page(mm, va); + if (!cpumask_empty(&cpu_mask)) + flush_tlb_others(cpu_mask, mm, va); + + preempt_enable(); +} + +/** + * do_flush_tlb_all - Callback to completely invalidate a TLB + * @unused: Callback context (ignored). + */ +static void do_flush_tlb_all(void *unused) +{ + local_flush_tlb_all(); +} + +/** + * flush_tlb_all - Completely invalidate TLBs on all CPUs + */ +void flush_tlb_all(void) +{ + on_each_cpu(do_flush_tlb_all, 0, 1); +} diff --git a/arch/mn10300/oprofile/Makefile b/arch/mn10300/oprofile/Makefile new file mode 100644 index 00000000..918dbe60 --- /dev/null +++ b/arch/mn10300/oprofile/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for the MN10300-specific profiling code +# +obj-$(CONFIG_OPROFILE) += oprofile.o + +DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ + oprof.o cpu_buffer.o buffer_sync.o \ + event_buffer.o oprofile_files.o \ + oprofilefs.o oprofile_stats.o \ + timer_int.o ) + +oprofile-y := $(DRIVER_OBJS) op_model_null.o + diff --git a/arch/mn10300/oprofile/op_model_null.c b/arch/mn10300/oprofile/op_model_null.c new file mode 100644 index 00000000..cd4ab374 --- /dev/null +++ b/arch/mn10300/oprofile/op_model_null.c @@ -0,0 +1,22 @@ +/* Null profiling driver + * + * Copyright (C) 2003 Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * Licence. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include <linux/kernel.h> +#include <linux/oprofile.h> +#include <linux/init.h> +#include <linux/errno.h> + +int __init oprofile_arch_init(struct oprofile_operations *ops) +{ + return -ENODEV; +} + +void oprofile_arch_exit(void) +{ +} + diff --git a/arch/mn10300/proc-mn103e010/Makefile b/arch/mn10300/proc-mn103e010/Makefile new file mode 100644 index 00000000..ac2c9784 --- /dev/null +++ b/arch/mn10300/proc-mn103e010/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the MN103E010 processor chip specific code +# +obj-y := proc-init.o + diff --git a/arch/mn10300/proc-mn103e010/include/proc/cache.h b/arch/mn10300/proc-mn103e010/include/proc/cache.h new file mode 100644 index 00000000..967d144f --- /dev/null +++ b/arch/mn10300/proc-mn103e010/include/proc/cache.h @@ -0,0 +1,43 @@ +/* MN103E010 Cache specification + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_PROC_CACHE_H +#define _ASM_PROC_CACHE_H + +/* L1 cache */ + +#define L1_CACHE_NWAYS 4 /* number of ways in caches */ +#define L1_CACHE_NENTRIES 256 /* number of entries in each way */ +#define L1_CACHE_BYTES 16 /* bytes per entry */ +#define L1_CACHE_SHIFT 4 /* shift for bytes per entry */ +#define L1_CACHE_WAYDISP 0x1000 /* displacement of one way from the next */ + +#define L1_CACHE_TAG_VALID 0x00000001 /* cache tag valid bit */ +#define L1_CACHE_TAG_DIRTY 0x00000008 /* data cache tag dirty bit */ +#define L1_CACHE_TAG_ENTRY 0x00000ff0 /* cache tag entry address mask */ +#define L1_CACHE_TAG_ADDRESS 0xfffff000 /* cache tag line address mask */ +#define L1_CACHE_TAG_MASK +(L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY) + +/* + * specification of the interval between interrupt checking intervals whilst + * managing the cache with the interrupts disabled + */ +#define MN10300_DCACHE_INV_RANGE_INTR_LOG2_INTERVAL 4 + +/* + * The size of range at which it becomes more economical to just flush the + * whole cache rather than trying to flush the specified range. + */ +#define MN10300_DCACHE_FLUSH_BORDER \ + +(L1_CACHE_NWAYS * L1_CACHE_NENTRIES * L1_CACHE_BYTES) +#define MN10300_DCACHE_FLUSH_INV_BORDER \ + +(L1_CACHE_NWAYS * L1_CACHE_NENTRIES * L1_CACHE_BYTES) + +#endif /* _ASM_PROC_CACHE_H */ diff --git a/arch/mn10300/proc-mn103e010/include/proc/clock.h b/arch/mn10300/proc-mn103e010/include/proc/clock.h new file mode 100644 index 00000000..704a819f --- /dev/null +++ b/arch/mn10300/proc-mn103e010/include/proc/clock.h @@ -0,0 +1,16 @@ +/* MN103E010-specific clocks + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_PROC_CLOCK_H +#define _ASM_PROC_CLOCK_H + +#include <unit/clock.h> + +#endif /* _ASM_PROC_CLOCK_H */ diff --git a/arch/mn10300/proc-mn103e010/include/proc/dmactl-regs.h b/arch/mn10300/proc-mn103e010/include/proc/dmactl-regs.h new file mode 100644 index 00000000..d72d328d --- /dev/null +++ b/arch/mn10300/proc-mn103e010/include/proc/dmactl-regs.h @@ -0,0 +1,102 @@ +/* MN103E010 on-board DMA controller registers + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _ASM_PROC_DMACTL_REGS_H +#define _ASM_PROC_DMACTL_REGS_H + +#include <asm/cpu-regs.h> + +#ifdef __KERNEL__ + +/* DMA registers */ +#define DMxCTR(N) __SYSREG(0xd2000000 + ((N) * 0x100), u32) /* control reg */ +#define DMxCTR_BG 0x0000001f /* transfer request source */ +#define DMxCTR_BG_SOFT 0x00000000 /* - software source */ +#define DMxCTR_BG_SC0TX 0x00000002 /* - serial port 0 transmission */ +#define DMxCTR_BG_SC0RX 0x00000003 /* - serial port 0 reception */ +#define DMxCTR_BG_SC1TX 0x00000004 /* - serial port 1 transmission */ +#define DMxCTR_BG_SC1RX 0x00000005 /* - serial port 1 reception */ +#define DMxCTR_BG_SC2TX 0x00000006 /* - serial port 2 transmission */ +#define DMxCTR_BG_SC2RX 0x00000007 /* - serial port 2 reception */ +#define DMxCTR_BG_TM0UFLOW 0x00000008 /* - timer 0 underflow */ +#define DMxCTR_BG_TM1UFLOW 0x00000009 /* - timer 1 underflow */ +#define DMxCTR_BG_TM2UFLOW 0x0000000a /* - timer 2 underflow */ +#define DMxCTR_BG_TM3UFLOW 0x0000000b /* - timer 3 underflow */ +#define DMxCTR_BG_TM6ACMPCAP 0x0000000c /* - timer 6A compare/capture */ +#define DMxCTR_BG_AFE 0x0000000d /* - analogue front-end interrupt source */ +#define DMxCTR_BG_ADC 0x0000000e /* - A/D conversion end interrupt source */ +#define DMxCTR_BG_IRDA 0x0000000f /* - IrDA interrupt source */ +#define DMxCTR_BG_RTC 0x00000010 /* - RTC interrupt source */ +#define DMxCTR_BG_XIRQ0 0x00000011 /* - XIRQ0 pin interrupt source */ +#define DMxCTR_BG_XIRQ1 0x00000012 /* - XIRQ1 pin interrupt source */ +#define DMxCTR_BG_XDMR0 0x00000013 /* - external request 0 source (XDMR0 pin) */ +#define DMxCTR_BG_XDMR1 0x00000014 /* - external request 1 source (XDMR1 pin) */ +#define DMxCTR_SAM 0x000000e0 /* DMA transfer src addr mode */ +#define DMxCTR_SAM_INCR 0x00000000 /* - increment */ +#define DMxCTR_SAM_DECR 0x00000020 /* - decrement */ +#define DMxCTR_SAM_FIXED 0x00000040 /* - fixed */ +#define DMxCTR_DAM 0x00000000 /* DMA transfer dest addr mode */ +#define DMxCTR_DAM_INCR 0x00000000 /* - increment */ +#define DMxCTR_DAM_DECR 0x00000100 /* - decrement */ +#define DMxCTR_DAM_FIXED 0x00000200 /* - fixed */ +#define DMxCTR_TM 0x00001800 /* DMA transfer mode */ +#define DMxCTR_TM_BATCH 0x00000000 /* - batch transfer */ +#define DMxCTR_TM_INTERM 0x00001000 /* - intermittent transfer */ +#define DMxCTR_UT 0x00006000 /* DMA transfer unit */ +#define DMxCTR_UT_1 0x00000000 /* - 1 byte */ +#define DMxCTR_UT_2 0x00002000 /* - 2 byte */ +#define DMxCTR_UT_4 0x00004000 /* - 4 byte */ +#define DMxCTR_UT_16 0x00006000 /* - 16 byte */ +#define DMxCTR_TEN 0x00010000 /* DMA channel transfer enable */ +#define DMxCTR_RQM 0x00060000 /* external request input source mode */ +#define DMxCTR_RQM_FALLEDGE 0x00000000 /* - falling edge */ +#define DMxCTR_RQM_RISEEDGE 0x00020000 /* - rising edge */ +#define DMxCTR_RQM_LOLEVEL 0x00040000 /* - low level */ +#define DMxCTR_RQM_HILEVEL 0x00060000 /* - high level */ +#define DMxCTR_RQF 0x01000000 /* DMA transfer request flag */ +#define DMxCTR_XEND 0x80000000 /* DMA transfer end flag */ + +#define DMxSRC(N) __SYSREG(0xd2000004 + ((N) * 0x100), u32) /* control reg */ + +#define DMxDST(N) __SYSREG(0xd2000008 + ((N) * 0x100), u32) /* src addr reg */ + +#define DMxSIZ(N) __SYSREG(0xd200000c + ((N) * 0x100), u32) /* dest addr reg */ +#define DMxSIZ_CT 0x000fffff /* number of bytes to transfer */ + +#define DMxCYC(N) __SYSREG(0xd2000010 + ((N) * 0x100), u32) /* intermittent + * size reg */ +#define DMxCYC_CYC 0x000000ff /* number of interrmittent transfers -1 */ + +#define DM0IRQ 16 /* DMA channel 0 complete IRQ */ +#define DM1IRQ 17 /* DMA channel 1 complete IRQ */ +#define DM2IRQ 18 /* DMA channel 2 complete IRQ */ +#define DM3IRQ 19 /* DMA channel 3 complete IRQ */ + +#define DM0ICR GxICR(DM0IRQ) /* DMA channel 0 complete intr ctrl reg */ +#define DM1ICR GxICR(DM0IR1) /* DMA channel 1 complete intr ctrl reg */ +#define DM2ICR GxICR(DM0IR2) /* DMA channel 2 complete intr ctrl reg */ +#define DM3ICR GxICR(DM0IR3) /* DMA channel 3 complete intr ctrl reg */ + +#ifndef __ASSEMBLY__ + +struct mn10300_dmactl_regs { + u32 ctr; + const void *src; + void *dst; + u32 siz; + u32 cyc; +} __attribute__((aligned(0x100))); + +#endif /* __ASSEMBLY__ */ + +#endif /* __KERNEL__ */ + +#endif /* _ASM_PROC_DMACTL_REGS_H */ diff --git a/arch/mn10300/proc-mn103e010/include/proc/intctl-regs.h b/arch/mn10300/proc-mn103e010/include/proc/intctl-regs.h new file mode 100644 index 00000000..f537801a --- /dev/null +++ b/arch/mn10300/proc-mn103e010/include/proc/intctl-regs.h @@ -0,0 +1,29 @@ +#ifndef _ASM_PROC_INTCTL_REGS_H +#define _ASM_PROC_INTCTL_REGS_H + +#ifndef _ASM_INTCTL_REGS_H +# error "please don't include this file directly" +#endif + +/* intr acceptance group reg */ +#define IAGR __SYSREG(0xd4000100, u16) + +/* group number register */ +#define IAGR_GN 0x00fc + +#define __GET_XIRQ_TRIGGER(X, Z) (((Z) >> ((X) * 2)) & 3) + +#define __SET_XIRQ_TRIGGER(X, Y, Z) \ +({ \ + typeof(Z) x = (Z); \ + x &= ~(3 << ((X) * 2)); \ + x |= ((Y) & 3) << ((X) * 2); \ + (Z) = x; \ +}) + +/* external pin intr spec reg */ +#define EXTMD __SYSREG(0xd4000200, u16) +#define GET_XIRQ_TRIGGER(X) __GET_XIRQ_TRIGGER(X, EXTMD) +#define SET_XIRQ_TRIGGER(X, Y) __SET_XIRQ_TRIGGER(X, Y, EXTMD) + +#endif /* _ASM_PROC_INTCTL_REGS_H */ diff --git a/arch/mn10300/proc-mn103e010/include/proc/irq.h b/arch/mn10300/proc-mn103e010/include/proc/irq.h new file mode 100644 index 00000000..aa6ee8f9 --- /dev/null +++ b/arch/mn10300/proc-mn103e010/include/proc/irq.h @@ -0,0 +1,34 @@ +/* MN103E010 On-board interrupt controller numbers + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _ASM_PROC_IRQ_H +#define _ASM_PROC_IRQ_H + +#ifdef __KERNEL__ + +#define GxICR_NUM_IRQS 42 + +#define GxICR_NUM_XIRQS 8 + +#define XIRQ0 34 +#define XIRQ1 35 +#define XIRQ2 36 +#define XIRQ3 37 +#define XIRQ4 38 +#define XIRQ5 39 +#define XIRQ6 40 +#define XIRQ7 41 + +#define XIRQ2IRQ(num) (XIRQ0 + num) + +#endif /* __KERNEL__ */ + +#endif /* _ASM_PROC_IRQ_H */ diff --git a/arch/mn10300/proc-mn103e010/include/proc/proc.h b/arch/mn10300/proc-mn103e010/include/proc/proc.h new file mode 100644 index 00000000..39c4f8e7 --- /dev/null +++ b/arch/mn10300/proc-mn103e010/include/proc/proc.h @@ -0,0 +1,18 @@ +/* MN103E010 Processor description + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _ASM_PROC_PROC_H +#define _ASM_PROC_PROC_H + +#define PROCESSOR_VENDOR_NAME "Panasonic" +#define PROCESSOR_MODEL_NAME "mn103e010" + +#endif /* _ASM_PROC_PROC_H */ diff --git a/arch/mn10300/proc-mn103e010/proc-init.c b/arch/mn10300/proc-mn103e010/proc-init.c new file mode 100644 index 00000000..27b97980 --- /dev/null +++ b/arch/mn10300/proc-mn103e010/proc-init.c @@ -0,0 +1,112 @@ +/* MN103E010 Processor initialisation + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/kernel.h> +#include <asm/fpu.h> +#include <asm/rtc.h> +#include <asm/busctl-regs.h> + +/* + * initialise the on-silicon processor peripherals + */ +asmlinkage void __init processor_init(void) +{ + int loop; + + /* set up the exception table first */ + for (loop = 0x000; loop < 0x400; loop += 8) + __set_intr_stub(loop, __common_exception); + + __set_intr_stub(EXCEP_ITLBMISS, itlb_miss); + __set_intr_stub(EXCEP_DTLBMISS, dtlb_miss); + __set_intr_stub(EXCEP_IAERROR, itlb_aerror); + __set_intr_stub(EXCEP_DAERROR, dtlb_aerror); + __set_intr_stub(EXCEP_BUSERROR, raw_bus_error); + __set_intr_stub(EXCEP_DOUBLE_FAULT, double_fault); + __set_intr_stub(EXCEP_FPU_DISABLED, fpu_disabled); + __set_intr_stub(EXCEP_SYSCALL0, system_call); + + __set_intr_stub(EXCEP_NMI, nmi_handler); + __set_intr_stub(EXCEP_WDT, nmi_handler); + __set_intr_stub(EXCEP_IRQ_LEVEL0, irq_handler); + __set_intr_stub(EXCEP_IRQ_LEVEL1, irq_handler); + __set_intr_stub(EXCEP_IRQ_LEVEL2, irq_handler); + __set_intr_stub(EXCEP_IRQ_LEVEL3, irq_handler); + __set_intr_stub(EXCEP_IRQ_LEVEL4, irq_handler); + __set_intr_stub(EXCEP_IRQ_LEVEL5, irq_handler); + __set_intr_stub(EXCEP_IRQ_LEVEL6, irq_handler); + + IVAR0 = EXCEP_IRQ_LEVEL0; + IVAR1 = EXCEP_IRQ_LEVEL1; + IVAR2 = EXCEP_IRQ_LEVEL2; + IVAR3 = EXCEP_IRQ_LEVEL3; + IVAR4 = EXCEP_IRQ_LEVEL4; + IVAR5 = EXCEP_IRQ_LEVEL5; + IVAR6 = EXCEP_IRQ_LEVEL6; + + mn10300_dcache_flush_inv(); + mn10300_icache_inv(); + + /* disable all interrupts and set to priority 6 (lowest) */ + for (loop = 0; loop < NR_IRQS; loop++) + GxICR(loop) = GxICR_LEVEL_6 | GxICR_DETECT; + + /* clear the timers */ + TM0MD = 0; + TM1MD = 0; + TM2MD = 0; + TM3MD = 0; + TM4MD = 0; + TM5MD = 0; + TM6MD = 0; + TM6MDA = 0; + TM6MDB = 0; + TM7MD = 0; + TM8MD = 0; + TM9MD = 0; + TM10MD = 0; + TM11MD = 0; + + calibrate_clock(); +} + +/* + * determine the memory size and base from the memory controller regs + */ +void __init get_mem_info(unsigned long *mem_base, unsigned long *mem_size) +{ + unsigned long base, size; + + *mem_base = 0; + *mem_size = 0; + + base = SDBASE(0); + if (base & SDBASE_CE) { + size = (base & SDBASE_CBAM) << SDBASE_CBAM_SHIFT; + size = ~size + 1; + base &= SDBASE_CBA; + + printk(KERN_INFO "SDRAM[0]: %luMb @%08lx\n", size >> 20, base); + *mem_size += size; + *mem_base = base; + } + + base = SDBASE(1); + if (base & SDBASE_CE) { + size = (base & SDBASE_CBAM) << SDBASE_CBAM_SHIFT; + size = ~size + 1; + base &= SDBASE_CBA; + + printk(KERN_INFO "SDRAM[1]: %luMb @%08lx\n", size >> 20, base); + *mem_size += size; + if (*mem_base == 0) + *mem_base = base; + } +} diff --git a/arch/mn10300/proc-mn2ws0050/Makefile b/arch/mn10300/proc-mn2ws0050/Makefile new file mode 100644 index 00000000..d4ca1330 --- /dev/null +++ b/arch/mn10300/proc-mn2ws0050/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the linux kernel. +# + +obj-y := proc-init.o diff --git a/arch/mn10300/proc-mn2ws0050/include/proc/cache.h b/arch/mn10300/proc-mn2ws0050/include/proc/cache.h new file mode 100644 index 00000000..bcb5df2d --- /dev/null +++ b/arch/mn10300/proc-mn2ws0050/include/proc/cache.h @@ -0,0 +1,49 @@ +/* Cache specification + * + * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * Modified by Matsushita Electric Industrial Co., Ltd. + * Modifications: + * 13-Nov-2006 MEI Add L1_CACHE_SHIFT_MAX definition. + * 29-Jul-2008 MEI Add define for MN10300_HAS_AREAPURGE_REG. + * + * 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. + */ +#ifndef _ASM_PROC_CACHE_H +#define _ASM_PROC_CACHE_H + +/* + * L1 cache + */ +#define L1_CACHE_NWAYS 4 /* number of ways in caches */ +#define L1_CACHE_NENTRIES 128 /* number of entries in each way */ +#define L1_CACHE_BYTES 32 /* bytes per entry */ +#define L1_CACHE_SHIFT 5 /* shift for bytes per entry */ +#define L1_CACHE_WAYDISP 0x1000 /* distance from one way to the next */ + +#define L1_CACHE_TAG_VALID 0x00000001 /* cache tag valid bit */ +#define L1_CACHE_TAG_DIRTY 0x00000008 /* data cache tag dirty bit */ +#define L1_CACHE_TAG_ENTRY 0x00000fe0 /* cache tag entry address mask */ +#define L1_CACHE_TAG_ADDRESS 0xfffff000 /* cache tag line address mask */ +#define L1_CACHE_TAG_MASK +(L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY) + +/* + * specification of the interval between interrupt checking intervals whilst + * managing the cache with the interrupts disabled + */ +#define MN10300_DCACHE_INV_RANGE_INTR_LOG2_INTERVAL 4 + +/* + * The size of range at which it becomes more economical to just flush the + * whole cache rather than trying to flush the specified range. + */ +#define MN10300_DCACHE_FLUSH_BORDER \ + +(L1_CACHE_NWAYS * L1_CACHE_NENTRIES * L1_CACHE_BYTES) +#define MN10300_DCACHE_FLUSH_INV_BORDER \ + +(L1_CACHE_NWAYS * L1_CACHE_NENTRIES * L1_CACHE_BYTES) + +#endif /* _ASM_PROC_CACHE_H */ diff --git a/arch/mn10300/proc-mn2ws0050/include/proc/clock.h b/arch/mn10300/proc-mn2ws0050/include/proc/clock.h new file mode 100644 index 00000000..fe4c0a4a --- /dev/null +++ b/arch/mn10300/proc-mn2ws0050/include/proc/clock.h @@ -0,0 +1,20 @@ +/* clock.h: proc-specific clocks + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * Modified by Matsushita Electric Industrial Co., Ltd. + * Modifications: + * 23-Feb-2007 MEI Delete define for watchdog timer. + * + * 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. + */ +#ifndef _ASM_PROC_CLOCK_H +#define _ASM_PROC_CLOCK_H + +#include <unit/clock.h> + +#endif /* _ASM_PROC_CLOCK_H */ diff --git a/arch/mn10300/proc-mn2ws0050/include/proc/dmactl-regs.h b/arch/mn10300/proc-mn2ws0050/include/proc/dmactl-regs.h new file mode 100644 index 00000000..4c4319e2 --- /dev/null +++ b/arch/mn10300/proc-mn2ws0050/include/proc/dmactl-regs.h @@ -0,0 +1,103 @@ +/* MN2WS0050 on-board DMA controller registers + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#ifndef _ASM_PROC_DMACTL_REGS_H +#define _ASM_PROC_DMACTL_REGS_H + +#include <asm/cpu-regs.h> + +#ifdef __KERNEL__ + +/* DMA registers */ +#define DMxCTR(N) __SYSREG(0xd4005000+(N*0x100), u32) /* control reg */ +#define DMxCTR_BG 0x0000001f /* transfer request source */ +#define DMxCTR_BG_SOFT 0x00000000 /* - software source */ +#define DMxCTR_BG_SC0TX 0x00000002 /* - serial port 0 transmission */ +#define DMxCTR_BG_SC0RX 0x00000003 /* - serial port 0 reception */ +#define DMxCTR_BG_SC1TX 0x00000004 /* - serial port 1 transmission */ +#define DMxCTR_BG_SC1RX 0x00000005 /* - serial port 1 reception */ +#define DMxCTR_BG_SC2TX 0x00000006 /* - serial port 2 transmission */ +#define DMxCTR_BG_SC2RX 0x00000007 /* - serial port 2 reception */ +#define DMxCTR_BG_TM0UFLOW 0x00000008 /* - timer 0 underflow */ +#define DMxCTR_BG_TM1UFLOW 0x00000009 /* - timer 1 underflow */ +#define DMxCTR_BG_TM2UFLOW 0x0000000a /* - timer 2 underflow */ +#define DMxCTR_BG_TM3UFLOW 0x0000000b /* - timer 3 underflow */ +#define DMxCTR_BG_TM6ACMPCAP 0x0000000c /* - timer 6A compare/capture */ +#define DMxCTR_BG_RYBY 0x0000000d /* - NAND Flash RY/BY request source */ +#define DMxCTR_BG_RMC 0x0000000e /* - remote controller output */ +#define DMxCTR_BG_XIRQ12 0x00000011 /* - XIRQ12 pin interrupt source */ +#define DMxCTR_BG_XIRQ13 0x00000012 /* - XIRQ13 pin interrupt source */ +#define DMxCTR_BG_TCK 0x00000014 /* - tick timer underflow */ +#define DMxCTR_BG_SC4TX 0x00000019 /* - serial port4 transmission */ +#define DMxCTR_BG_SC4RX 0x0000001a /* - serial port4 reception */ +#define DMxCTR_BG_SC5TX 0x0000001b /* - serial port5 transmission */ +#define DMxCTR_BG_SC5RX 0x0000001c /* - serial port5 reception */ +#define DMxCTR_BG_SC6TX 0x0000001d /* - serial port6 transmission */ +#define DMxCTR_BG_SC6RX 0x0000001e /* - serial port6 reception */ +#define DMxCTR_BG_TMSUFLOW 0x0000001f /* - timestamp timer underflow */ +#define DMxCTR_SAM 0x00000060 /* DMA transfer src addr mode */ +#define DMxCTR_SAM_INCR 0x00000000 /* - increment */ +#define DMxCTR_SAM_DECR 0x00000020 /* - decrement */ +#define DMxCTR_SAM_FIXED 0x00000040 /* - fixed */ +#define DMxCTR_DAM 0x00000300 /* DMA transfer dest addr mode */ +#define DMxCTR_DAM_INCR 0x00000000 /* - increment */ +#define DMxCTR_DAM_DECR 0x00000100 /* - decrement */ +#define DMxCTR_DAM_FIXED 0x00000200 /* - fixed */ +#define DMxCTR_UT 0x00006000 /* DMA transfer unit */ +#define DMxCTR_UT_1 0x00000000 /* - 1 byte */ +#define DMxCTR_UT_2 0x00002000 /* - 2 byte */ +#define DMxCTR_UT_4 0x00004000 /* - 4 byte */ +#define DMxCTR_UT_16 0x00006000 /* - 16 byte */ +#define DMxCTR_RRE 0x00008000 /* DMA round robin enable */ +#define DMxCTR_TEN 0x00010000 /* DMA channel transfer enable */ +#define DMxCTR_RQM 0x00060000 /* external request input source mode */ +#define DMxCTR_RQM_FALLEDGE 0x00000000 /* - falling edge */ +#define DMxCTR_RQM_RISEEDGE 0x00020000 /* - rising edge */ +#define DMxCTR_RQM_LOLEVEL 0x00040000 /* - low level */ +#define DMxCTR_RQM_HILEVEL 0x00060000 /* - high level */ +#define DMxCTR_RQF 0x01000000 /* DMA transfer request flag */ +#define DMxCTR_PERR 0x40000000 /* DMA transfer parameter error flag */ +#define DMxCTR_XEND 0x80000000 /* DMA transfer end flag */ + +#define DMxSRC(N) __SYSREG(0xd4005004+(N*0x100), u32) /* control reg */ + +#define DMxDST(N) __SYSREG(0xd4005008+(N*0x100), u32) /* source addr reg */ + +#define DMxSIZ(N) __SYSREG(0xd400500c+(N*0x100), u32) /* dest addr reg */ +#define DMxSIZ_CT 0x000fffff /* number of bytes to transfer */ + +#define DMxCYC(N) __SYSREG(0xd4005010+(N*0x100), u32) /* intermittent size reg */ +#define DMxCYC_CYC 0x000000ff /* number of interrmittent transfers -1 */ + +#define DM0IRQ 16 /* DMA channel 0 complete IRQ */ +#define DM1IRQ 17 /* DMA channel 1 complete IRQ */ +#define DM2IRQ 18 /* DMA channel 2 complete IRQ */ +#define DM3IRQ 19 /* DMA channel 3 complete IRQ */ + +#define DM0ICR GxICR(DM0IRQ) /* DMA channel 0 complete intr ctrl reg */ +#define DM1ICR GxICR(DM0IR1) /* DMA channel 1 complete intr ctrl reg */ +#define DM2ICR GxICR(DM0IR2) /* DMA channel 2 complete intr ctrl reg */ +#define DM3ICR GxICR(DM0IR3) /* DMA channel 3 complete intr ctrl reg */ + +#ifndef __ASSEMBLY__ + +struct mn10300_dmactl_regs { + u32 ctr; + const void *src; + void *dst; + u32 siz; + u32 cyc; +} __attribute__((aligned(0x100))); + +#endif /* __ASSEMBLY__ */ + +#endif /* __KERNEL__ */ + +#endif /* _ASM_PROC_DMACTL_REGS_H */ diff --git a/arch/mn10300/proc-mn2ws0050/include/proc/intctl-regs.h b/arch/mn10300/proc-mn2ws0050/include/proc/intctl-regs.h new file mode 100644 index 00000000..a1e97727 --- /dev/null +++ b/arch/mn10300/proc-mn2ws0050/include/proc/intctl-regs.h @@ -0,0 +1,29 @@ +#ifndef _ASM_PROC_INTCTL_REGS_H +#define _ASM_PROC_INTCTL_REGS_H + +#ifndef _ASM_INTCTL_REGS_H +# error "please don't include this file directly" +#endif + +/* intr acceptance group reg */ +#define IAGR __SYSREG(0xd4000100, u16) + +/* group number register */ +#define IAGR_GN 0x003fc + +#define __GET_XIRQ_TRIGGER(X, Z) (((Z) >> ((X) * 2)) & 3) + +#define __SET_XIRQ_TRIGGER(X, Y, Z) \ +({ \ + typeof(Z) x = (Z); \ + x &= ~(3 << ((X) * 2)); \ + x |= ((Y) & 3) << ((X) * 2); \ + (Z) = x; \ +}) + +/* external pin intr spec reg */ +#define EXTMD0 __SYSREG(0xd4000200, u32) +#define GET_XIRQ_TRIGGER(X) __GET_XIRQ_TRIGGER(X, EXTMD0) +#define SET_XIRQ_TRIGGER(X, Y) __SET_XIRQ_TRIGGER(X, Y, EXTMD0) + +#endif /* _ASM_PROC_INTCTL_REGS_H */ diff --git a/arch/mn10300/proc-mn2ws0050/include/proc/irq.h b/arch/mn10300/proc-mn2ws0050/include/proc/irq.h new file mode 100644 index 00000000..37777a85 --- /dev/null +++ b/arch/mn10300/proc-mn2ws0050/include/proc/irq.h @@ -0,0 +1,49 @@ +/* MN2WS0050 on-board interrupt controller registers + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * Modified by Matsushita Electric Industrial Co., Ltd. + * Modifications: + * 13-Nov-2006 MEI Define extended IRQ number for SMP support. + * + * 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. + */ + +#ifndef _PROC_IRQ_H +#define _PROC_IRQ_H + +#ifdef __KERNEL__ + +#define GxICR_NUM_IRQS 163 +#ifdef CONFIG_SMP +#define GxICR_NUM_EXT_IRQS 197 +#endif /* CONFIG_SMP */ + +#define GxICR_NUM_XIRQS 16 + +#define XIRQ0 34 +#define XIRQ1 35 +#define XIRQ2 36 +#define XIRQ3 37 +#define XIRQ4 38 +#define XIRQ5 39 +#define XIRQ6 40 +#define XIRQ7 41 +#define XIRQ8 42 +#define XIRQ9 43 +#define XIRQ10 44 +#define XIRQ11 45 +#define XIRQ12 46 +#define XIRQ13 47 +#define XIRQ14 48 +#define XIRQ15 49 + +#define XIRQ2IRQ(num) (XIRQ0 + num) + +#endif /* __KERNEL__ */ + +#endif /* _PROC_IRQ_H */ diff --git a/arch/mn10300/proc-mn2ws0050/include/proc/nand-regs.h b/arch/mn10300/proc-mn2ws0050/include/proc/nand-regs.h new file mode 100644 index 00000000..84448f38 --- /dev/null +++ b/arch/mn10300/proc-mn2ws0050/include/proc/nand-regs.h @@ -0,0 +1,120 @@ +/* NAND flash interface register definitions + * + * Copyright (C) 2008-2009 Panasonic Corporation + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _PROC_NAND_REGS_H_ +#define _PROC_NAND_REGS_H_ + +/* command register */ +#define FCOMMAND_0 __SYSREG(0xd8f00000, u8) /* fcommand[24:31] */ +#define FCOMMAND_1 __SYSREG(0xd8f00001, u8) /* fcommand[16:23] */ +#define FCOMMAND_2 __SYSREG(0xd8f00002, u8) /* fcommand[8:15] */ +#define FCOMMAND_3 __SYSREG(0xd8f00003, u8) /* fcommand[0:7] */ + +/* for dma 16 byte trans, use FCOMMAND2 register */ +#define FCOMMAND2_0 __SYSREG(0xd8f00110, u8) /* fcommand2[24:31] */ +#define FCOMMAND2_1 __SYSREG(0xd8f00111, u8) /* fcommand2[16:23] */ +#define FCOMMAND2_2 __SYSREG(0xd8f00112, u8) /* fcommand2[8:15] */ +#define FCOMMAND2_3 __SYSREG(0xd8f00113, u8) /* fcommand2[0:7] */ + +#define FCOMMAND_FIEN 0x80 /* nand flash I/F enable */ +#define FCOMMAND_BW_8BIT 0x00 /* 8bit bus width */ +#define FCOMMAND_BW_16BIT 0x40 /* 16bit bus width */ +#define FCOMMAND_BLOCKSZ_SMALL 0x00 /* small block */ +#define FCOMMAND_BLOCKSZ_LARGE 0x20 /* large block */ +#define FCOMMAND_DMASTART 0x10 /* dma start */ +#define FCOMMAND_RYBY 0x08 /* ready/busy flag */ +#define FCOMMAND_RYBYINTMSK 0x04 /* mask ready/busy interrupt */ +#define FCOMMAND_XFWP 0x02 /* write protect enable */ +#define FCOMMAND_XFCE 0x01 /* flash device disable */ +#define FCOMMAND_SEQKILL 0x10 /* stop seq-read */ +#define FCOMMAND_ANUM 0x07 /* address cycle */ +#define FCOMMAND_ANUM_NONE 0x00 /* address cycle none */ +#define FCOMMAND_ANUM_1CYC 0x01 /* address cycle 1cycle */ +#define FCOMMAND_ANUM_2CYC 0x02 /* address cycle 2cycle */ +#define FCOMMAND_ANUM_3CYC 0x03 /* address cycle 3cycle */ +#define FCOMMAND_ANUM_4CYC 0x04 /* address cycle 4cycle */ +#define FCOMMAND_ANUM_5CYC 0x05 /* address cycle 5cycle */ +#define FCOMMAND_FCMD_READ0 0x00 /* read1 command */ +#define FCOMMAND_FCMD_SEQIN 0x80 /* page program 1st command */ +#define FCOMMAND_FCMD_PAGEPROG 0x10 /* page program 2nd command */ +#define FCOMMAND_FCMD_RESET 0xff /* reset command */ +#define FCOMMAND_FCMD_ERASE1 0x60 /* erase 1st command */ +#define FCOMMAND_FCMD_ERASE2 0xd0 /* erase 2nd command */ +#define FCOMMAND_FCMD_STATUS 0x70 /* read status command */ +#define FCOMMAND_FCMD_READID 0x90 /* read id command */ +#define FCOMMAND_FCMD_READOOB 0x50 /* read3 command */ +/* address register */ +#define FADD __SYSREG(0xd8f00004, u32) +/* address register 2 */ +#define FADD2 __SYSREG(0xd8f00008, u32) +/* error judgement register */ +#define FJUDGE __SYSREG(0xd8f0000c, u32) +#define FJUDGE_NOERR 0x0 /* no error */ +#define FJUDGE_1BITERR 0x1 /* 1bit error in data area */ +#define FJUDGE_PARITYERR 0x2 /* parity error */ +#define FJUDGE_UNCORRECTABLE 0x3 /* uncorrectable error */ +#define FJUDGE_ERRJDG_MSK 0x3 /* mask of judgement result */ +/* 1st ECC store register */ +#define FECC11 __SYSREG(0xd8f00010, u32) +/* 2nd ECC store register */ +#define FECC12 __SYSREG(0xd8f00014, u32) +/* 3rd ECC store register */ +#define FECC21 __SYSREG(0xd8f00018, u32) +/* 4th ECC store register */ +#define FECC22 __SYSREG(0xd8f0001c, u32) +/* 5th ECC store register */ +#define FECC31 __SYSREG(0xd8f00020, u32) +/* 6th ECC store register */ +#define FECC32 __SYSREG(0xd8f00024, u32) +/* 7th ECC store register */ +#define FECC41 __SYSREG(0xd8f00028, u32) +/* 8th ECC store register */ +#define FECC42 __SYSREG(0xd8f0002c, u32) +/* data register */ +#define FDATA __SYSREG(0xd8f00030, u32) +/* access pulse register */ +#define FPWS __SYSREG(0xd8f00100, u32) +#define FPWS_PWS1W_2CLK 0x00000000 /* write pulse width 1clock */ +#define FPWS_PWS1W_3CLK 0x01000000 /* write pulse width 2clock */ +#define FPWS_PWS1W_4CLK 0x02000000 /* write pulse width 4clock */ +#define FPWS_PWS1W_5CLK 0x03000000 /* write pulse width 5clock */ +#define FPWS_PWS1W_6CLK 0x04000000 /* write pulse width 6clock */ +#define FPWS_PWS1W_7CLK 0x05000000 /* write pulse width 7clock */ +#define FPWS_PWS1W_8CLK 0x06000000 /* write pulse width 8clock */ +#define FPWS_PWS1R_3CLK 0x00010000 /* read pulse width 3clock */ +#define FPWS_PWS1R_4CLK 0x00020000 /* read pulse width 4clock */ +#define FPWS_PWS1R_5CLK 0x00030000 /* read pulse width 5clock */ +#define FPWS_PWS1R_6CLK 0x00040000 /* read pulse width 6clock */ +#define FPWS_PWS1R_7CLK 0x00050000 /* read pulse width 7clock */ +#define FPWS_PWS1R_8CLK 0x00060000 /* read pulse width 8clock */ +#define FPWS_PWS2W_2CLK 0x00000100 /* write pulse interval 2clock */ +#define FPWS_PWS2W_3CLK 0x00000200 /* write pulse interval 3clock */ +#define FPWS_PWS2W_4CLK 0x00000300 /* write pulse interval 4clock */ +#define FPWS_PWS2W_5CLK 0x00000400 /* write pulse interval 5clock */ +#define FPWS_PWS2W_6CLK 0x00000500 /* write pulse interval 6clock */ +#define FPWS_PWS2R_2CLK 0x00000001 /* read pulse interval 2clock */ +#define FPWS_PWS2R_3CLK 0x00000002 /* read pulse interval 3clock */ +#define FPWS_PWS2R_4CLK 0x00000003 /* read pulse interval 4clock */ +#define FPWS_PWS2R_5CLK 0x00000004 /* read pulse interval 5clock */ +#define FPWS_PWS2R_6CLK 0x00000005 /* read pulse interval 6clock */ +/* command register 2 */ +#define FCOMMAND2 __SYSREG(0xd8f00110, u32) +/* transfer frequency register */ +#define FNUM __SYSREG(0xd8f00114, u32) +#define FSDATA_ADDR 0xd8f00400 +/* active data register */ +#define FSDATA __SYSREG(FSDATA_ADDR, u32) + +#endif /* _PROC_NAND_REGS_H_ */ diff --git a/arch/mn10300/proc-mn2ws0050/include/proc/proc.h b/arch/mn10300/proc-mn2ws0050/include/proc/proc.h new file mode 100644 index 00000000..90d5cadd --- /dev/null +++ b/arch/mn10300/proc-mn2ws0050/include/proc/proc.h @@ -0,0 +1,18 @@ +/* proc.h: MN2WS0050 processor description + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _ASM_PROC_PROC_H +#define _ASM_PROC_PROC_H + +#define PROCESSOR_VENDOR_NAME "Panasonic" +#define PROCESSOR_MODEL_NAME "mn2ws0050" + +#endif /* _ASM_PROC_PROC_H */ diff --git a/arch/mn10300/proc-mn2ws0050/include/proc/smp-regs.h b/arch/mn10300/proc-mn2ws0050/include/proc/smp-regs.h new file mode 100644 index 00000000..22f277fb --- /dev/null +++ b/arch/mn10300/proc-mn2ws0050/include/proc/smp-regs.h @@ -0,0 +1,51 @@ +/* MN10300/AM33v2 Microcontroller SMP registers + * + * Copyright (C) 2006 Matsushita Electric Industrial Co., Ltd. + * All Rights Reserved. + * Created: + * 13-Nov-2006 MEI Add extended cache and atomic operation register + * for SMP support. + * 23-Feb-2007 MEI Add define for gdbstub SMP. + * + * 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. + */ + +#ifndef _ASM_PROC_SMP_REGS_H +#define _ASM_PROC_SMP_REGS_H + +#ifdef __KERNEL__ + +#ifndef __ASSEMBLY__ +#include <linux/types.h> +#endif +#include <asm/cpu-regs.h> + +/* + * Reference to the interrupt controllers of other CPUs + */ +#define CROSS_ICR_CPU_SHIFT 16 + +#define CROSS_GxICR(X, CPU) __SYSREG(0xc4000000 + (X) * 4 + \ + ((X) >= 64 && (X) < 192) * 0xf00 + ((CPU) << CROSS_ICR_CPU_SHIFT), u16) +#define CROSS_GxICR_u8(X, CPU) __SYSREG(0xc4000000 + (X) * 4 + \ + (((X) >= 64) && ((X) < 192)) * 0xf00 + ((CPU) << CROSS_ICR_CPU_SHIFT), u8) + +/* CPU ID register */ +#define CPUID __SYSREGC(0xc0000054, u32) +#define CPUID_MASK 0x00000007 /* CPU ID mask */ + +/* extended cache control register */ +#define ECHCTR __SYSREG(0xc0000c20, u32) +#define ECHCTR_IBCM 0x00000001 /* instruction cache broad cast mask */ +#define ECHCTR_DBCM 0x00000002 /* data cache broad cast mask */ +#define ECHCTR_ISPM 0x00000004 /* instruction cache snoop mask */ +#define ECHCTR_DSPM 0x00000008 /* data cache snoop mask */ + +#define NMIAGR __SYSREG(0xd400013c, u16) +#define NMIAGR_GN 0x03fc + +#endif /* __KERNEL__ */ +#endif /* _ASM_PROC_SMP_REGS_H */ diff --git a/arch/mn10300/proc-mn2ws0050/proc-init.c b/arch/mn10300/proc-mn2ws0050/proc-init.c new file mode 100644 index 00000000..c58249b9 --- /dev/null +++ b/arch/mn10300/proc-mn2ws0050/proc-init.c @@ -0,0 +1,134 @@ +/* MN2WS0050 processor initialisation + * + * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/interrupt.h> + +#include <asm/processor.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/atomic.h> +#include <asm/smp.h> +#include <asm/pgalloc.h> +#include <asm/busctl-regs.h> +#include <unit/timex.h> +#include <asm/fpu.h> +#include <asm/rtc.h> + +#define MEMCONF __SYSREGC(0xdf800400, u32) + +/* + * initialise the on-silicon processor peripherals + */ +asmlinkage void __init processor_init(void) +{ + int loop; + + /* set up the exception table first */ + for (loop = 0x000; loop < 0x400; loop += 8) + __set_intr_stub(loop, __common_exception); + + __set_intr_stub(EXCEP_ITLBMISS, itlb_miss); + __set_intr_stub(EXCEP_DTLBMISS, dtlb_miss); + __set_intr_stub(EXCEP_IAERROR, itlb_aerror); + __set_intr_stub(EXCEP_DAERROR, dtlb_aerror); + __set_intr_stub(EXCEP_BUSERROR, raw_bus_error); + __set_intr_stub(EXCEP_DOUBLE_FAULT, double_fault); + __set_intr_stub(EXCEP_FPU_DISABLED, fpu_disabled); + __set_intr_stub(EXCEP_SYSCALL0, system_call); + + __set_intr_stub(EXCEP_NMI, nmi_handler); + __set_intr_stub(EXCEP_WDT, nmi_handler); + __set_intr_stub(EXCEP_IRQ_LEVEL0, irq_handler); + __set_intr_stub(EXCEP_IRQ_LEVEL1, irq_handler); + __set_intr_stub(EXCEP_IRQ_LEVEL2, irq_handler); + __set_intr_stub(EXCEP_IRQ_LEVEL3, irq_handler); + __set_intr_stub(EXCEP_IRQ_LEVEL4, irq_handler); + __set_intr_stub(EXCEP_IRQ_LEVEL5, irq_handler); + __set_intr_stub(EXCEP_IRQ_LEVEL6, irq_handler); + + IVAR0 = EXCEP_IRQ_LEVEL0; + IVAR1 = EXCEP_IRQ_LEVEL1; + IVAR2 = EXCEP_IRQ_LEVEL2; + IVAR3 = EXCEP_IRQ_LEVEL3; + IVAR4 = EXCEP_IRQ_LEVEL4; + IVAR5 = EXCEP_IRQ_LEVEL5; + IVAR6 = EXCEP_IRQ_LEVEL6; + +#ifndef CONFIG_MN10300_HAS_CACHE_SNOOP + mn10300_dcache_flush_inv(); + mn10300_icache_inv(); +#endif + + /* disable all interrupts and set to priority 6 (lowest) */ +#ifdef CONFIG_SMP + for (loop = 0; loop < GxICR_NUM_IRQS; loop++) + GxICR(loop) = GxICR_LEVEL_6 | GxICR_DETECT; +#else /* !CONFIG_SMP */ + for (loop = 0; loop < NR_IRQS; loop++) + GxICR(loop) = GxICR_LEVEL_6 | GxICR_DETECT; +#endif /* !CONFIG_SMP */ + + /* clear the timers */ + TM0MD = 0; + TM1MD = 0; + TM2MD = 0; + TM3MD = 0; + TM4MD = 0; + TM5MD = 0; + TM6MD = 0; + TM6MDA = 0; + TM6MDB = 0; + TM7MD = 0; + TM8MD = 0; + TM9MD = 0; + TM10MD = 0; + TM11MD = 0; + TM12MD = 0; + TM13MD = 0; + TM14MD = 0; + TM15MD = 0; + + calibrate_clock(); +} + +/* + * determine the memory size and base from the memory controller regs + */ +void __init get_mem_info(unsigned long *mem_base, unsigned long *mem_size) +{ + unsigned long memconf = MEMCONF; + unsigned long size = 0; /* order: MByte */ + + *mem_base = 0x90000000; /* fixed address */ + + switch (memconf & 0x00000003) { + case 0x01: + size = 256 / 8; /* 256 Mbit per chip */ + break; + case 0x02: + size = 512 / 8; /* 512 Mbit per chip */ + break; + case 0x03: + size = 1024 / 8; /* 1 Gbit per chip */ + break; + default: + panic("Invalid SDRAM size"); + break; + } + + printk(KERN_INFO "DDR2-SDRAM: %luMB x 2 @%08lx\n", size, *mem_base); + + *mem_size = (size * 2) << 20; +} diff --git a/arch/mn10300/unit-asb2303/Makefile b/arch/mn10300/unit-asb2303/Makefile new file mode 100644 index 00000000..38a5bb43 --- /dev/null +++ b/arch/mn10300/unit-asb2303/Makefile @@ -0,0 +1,6 @@ +############################################################################### +# +# Makefile for the ASB2303 board +# +############################################################################### +obj-y := unit-init.o smc91111.o flash.o leds.o diff --git a/arch/mn10300/unit-asb2303/flash.c b/arch/mn10300/unit-asb2303/flash.c new file mode 100644 index 00000000..17fe083f --- /dev/null +++ b/arch/mn10300/unit-asb2303/flash.c @@ -0,0 +1,100 @@ +/* Handle mapping of the flash on the ASB2303 board + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.h> + +#define ASB2303_PROM_ADDR 0xA0000000 /* Boot PROM */ +#define ASB2303_PROM_SIZE (2 * 1024 * 1024) +#define ASB2303_FLASH_ADDR 0xA4000000 /* System Flash */ +#define ASB2303_FLASH_SIZE (32 * 1024 * 1024) +#define ASB2303_CONFIG_ADDR 0xA6000000 /* System Config EEPROM */ +#define ASB2303_CONFIG_SIZE (8 * 1024) + +/* + * default MTD partition table for both main flash devices, expected to be + * overridden by RedBoot + */ +static struct mtd_partition asb2303_partitions[] = { + { + .name = "Bootloader", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_CAP_ROM /* force read-only */ + }, { + .name = "Kernel", + .size = 0x00400000, + .offset = 0x00040000, + }, { + .name = "Filesystem", + .size = MTDPART_SIZ_FULL, + .offset = 0x00440000 + } +}; + +/* + * the ASB2303 Boot PROM definition + */ +static struct physmap_flash_data asb2303_bootprom_data = { + .width = 2, + .nr_parts = 1, + .parts = asb2303_partitions, +}; + +static struct resource asb2303_bootprom_resource = { + .start = ASB2303_PROM_ADDR, + .end = ASB2303_PROM_ADDR + ASB2303_PROM_SIZE, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device asb2303_bootprom = { + .name = "physmap-flash", + .id = 0, + .dev.platform_data = &asb2303_bootprom_data, + .num_resources = 1, + .resource = &asb2303_bootprom_resource, +}; + +/* + * the ASB2303 System Flash definition + */ +static struct physmap_flash_data asb2303_sysflash_data = { + .width = 4, + .nr_parts = 1, + .parts = asb2303_partitions, +}; + +static struct resource asb2303_sysflash_resource = { + .start = ASB2303_FLASH_ADDR, + .end = ASB2303_FLASH_ADDR + ASB2303_FLASH_SIZE, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device asb2303_sysflash = { + .name = "physmap-flash", + .id = 1, + .dev.platform_data = &asb2303_sysflash_data, + .num_resources = 1, + .resource = &asb2303_sysflash_resource, +}; + +/* + * register the ASB2303 flashes + */ +static int __init asb2303_mtd_init(void) +{ + platform_device_register(&asb2303_bootprom); + platform_device_register(&asb2303_sysflash); + return 0; +} + +module_init(asb2303_mtd_init); diff --git a/arch/mn10300/unit-asb2303/include/unit/clock.h b/arch/mn10300/unit-asb2303/include/unit/clock.h new file mode 100644 index 00000000..0316907a --- /dev/null +++ b/arch/mn10300/unit-asb2303/include/unit/clock.h @@ -0,0 +1,24 @@ +/* ASB2303-specific clocks + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _ASM_UNIT_CLOCK_H +#define _ASM_UNIT_CLOCK_H + +#ifndef __ASSEMBLY__ + +#define MN10300_IOCLK 33333333UL +/* #define MN10300_IOBCLK 66666666UL */ + +#endif /* !__ASSEMBLY__ */ + +#define MN10300_WDCLK MN10300_IOCLK + +#endif /* _ASM_UNIT_CLOCK_H */ diff --git a/arch/mn10300/unit-asb2303/include/unit/leds.h b/arch/mn10300/unit-asb2303/include/unit/leds.h new file mode 100644 index 00000000..3a7543ea --- /dev/null +++ b/arch/mn10300/unit-asb2303/include/unit/leds.h @@ -0,0 +1,43 @@ +/* ASB2303-specific LEDs + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _ASM_UNIT_LEDS_H +#define _ASM_UNIT_LEDS_H + +#include <asm/pio-regs.h> +#include <asm/cpu-regs.h> +#include <asm/exceptions.h> + +#define ASB2303_GPIO0DEF __SYSREG(0xDB000000, u32) +#define ASB2303_7SEGLEDS __SYSREG(0xDB000008, u32) + +/* + * use the 7-segment LEDs to indicate states + */ + +/* flip the 7-segment LEDs between "G" and "-" */ +#define mn10300_set_gdbleds(ONOFF) \ +do { \ + ASB2303_7SEGLEDS = (ONOFF) ? 0x85 : 0x7f; \ +} while (0) + +/* indicate double-fault by displaying "d" on the LEDs */ +#define mn10300_set_dbfleds \ + mov 0x43,d0 ; \ + movbu d0,(ASB2303_7SEGLEDS) + +#ifndef __ASSEMBLY__ +extern void peripheral_leds_display_exception(enum exception_code code); +extern void peripheral_leds_led_chase(void); +extern void debug_to_serial(const char *p, int n); +#endif /* __ASSEMBLY__ */ + +#endif /* _ASM_UNIT_LEDS_H */ diff --git a/arch/mn10300/unit-asb2303/include/unit/serial.h b/arch/mn10300/unit-asb2303/include/unit/serial.h new file mode 100644 index 00000000..991e356b --- /dev/null +++ b/arch/mn10300/unit-asb2303/include/unit/serial.h @@ -0,0 +1,141 @@ +/* ASB2303-specific 8250 serial ports + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _ASM_UNIT_SERIAL_H +#define _ASM_UNIT_SERIAL_H + +#include <asm/cpu-regs.h> +#include <proc/irq.h> +#include <linux/serial_reg.h> + +#define SERIAL_PORT0_BASE_ADDRESS 0xA6FB0000 +#define SERIAL_PORT1_BASE_ADDRESS 0xA6FC0000 + +#define SERIAL_IRQ XIRQ0 /* Dual serial (PC16552) (Hi) */ + +/* + * The ASB2303 has an 18.432 MHz clock the UART + */ +#define BASE_BAUD (18432000 / 16) + +/* + * dispose of the /dev/ttyS0 and /dev/ttyS1 serial ports + */ +#ifndef CONFIG_GDBSTUB_ON_TTYSx + +#define SERIAL_PORT_DFNS \ + { \ + .baud_base = BASE_BAUD, \ + .irq = SERIAL_IRQ, \ + .flags = STD_COM_FLAGS, \ + .iomem_base = (u8 *) SERIAL_PORT0_BASE_ADDRESS, \ + .iomem_reg_shift = 2, \ + .io_type = SERIAL_IO_MEM, \ + }, \ + { \ + .baud_base = BASE_BAUD, \ + .irq = SERIAL_IRQ, \ + .flags = STD_COM_FLAGS, \ + .iomem_base = (u8 *) SERIAL_PORT1_BASE_ADDRESS, \ + .iomem_reg_shift = 2, \ + .io_type = SERIAL_IO_MEM, \ + }, + +#ifndef __ASSEMBLY__ + +static inline void __debug_to_serial(const char *p, int n) +{ +} + +#endif /* !__ASSEMBLY__ */ + +#else /* CONFIG_GDBSTUB_ON_TTYSx */ + +#define SERIAL_PORT_DFNS /* both stolen by gdb-stub because they share an IRQ */ + +#if defined(CONFIG_GDBSTUB_ON_TTYS0) +#define GDBPORT_SERIAL_RX __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_RX * 4, u8) +#define GDBPORT_SERIAL_TX __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_TX * 4, u8) +#define GDBPORT_SERIAL_DLL __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_DLL * 4, u8) +#define GDBPORT_SERIAL_DLM __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_DLM * 4, u8) +#define GDBPORT_SERIAL_IER __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_IER * 4, u8) +#define GDBPORT_SERIAL_IIR __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_IIR * 4, u8) +#define GDBPORT_SERIAL_FCR __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_FCR * 4, u8) +#define GDBPORT_SERIAL_LCR __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_LCR * 4, u8) +#define GDBPORT_SERIAL_MCR __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_MCR * 4, u8) +#define GDBPORT_SERIAL_LSR __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_LSR * 4, u8) +#define GDBPORT_SERIAL_MSR __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_MSR * 4, u8) +#define GDBPORT_SERIAL_SCR __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_SCR * 4, u8) +#define GDBPORT_SERIAL_IRQ SERIAL_IRQ + +#elif defined(CONFIG_GDBSTUB_ON_TTYS1) +#define GDBPORT_SERIAL_RX __SYSREG(SERIAL_PORT1_BASE_ADDRESS + UART_RX * 4, u8) +#define GDBPORT_SERIAL_TX __SYSREG(SERIAL_PORT1_BASE_ADDRESS + UART_TX * 4, u8) +#define GDBPORT_SERIAL_DLL __SYSREG(SERIAL_PORT1_BASE_ADDRESS + UART_DLL * 4, u8) +#define GDBPORT_SERIAL_DLM __SYSREG(SERIAL_PORT1_BASE_ADDRESS + UART_DLM * 4, u8) +#define GDBPORT_SERIAL_IER __SYSREG(SERIAL_PORT1_BASE_ADDRESS + UART_IER * 4, u8) +#define GDBPORT_SERIAL_IIR __SYSREG(SERIAL_PORT1_BASE_ADDRESS + UART_IIR * 4, u8) +#define GDBPORT_SERIAL_FCR __SYSREG(SERIAL_PORT1_BASE_ADDRESS + UART_FCR * 4, u8) +#define GDBPORT_SERIAL_LCR __SYSREG(SERIAL_PORT1_BASE_ADDRESS + UART_LCR * 4, u8) +#define GDBPORT_SERIAL_MCR __SYSREG(SERIAL_PORT1_BASE_ADDRESS + UART_MCR * 4, u8) +#define GDBPORT_SERIAL_LSR __SYSREG(SERIAL_PORT1_BASE_ADDRESS + UART_LSR * 4, u8) +#define GDBPORT_SERIAL_MSR __SYSREG(SERIAL_PORT1_BASE_ADDRESS + UART_MSR * 4, u8) +#define GDBPORT_SERIAL_SCR __SYSREG(SERIAL_PORT1_BASE_ADDRESS + UART_SCR * 4, u8) +#define GDBPORT_SERIAL_IRQ SERIAL_IRQ +#endif + +#ifndef __ASSEMBLY__ + +#define LSR_WAIT_FOR(STATE) \ +do { \ + while (!(GDBPORT_SERIAL_LSR & UART_LSR_##STATE)) {} \ +} while (0) +#define FLOWCTL_WAIT_FOR(LINE) \ +do { \ + while (!(GDBPORT_SERIAL_MSR & UART_MSR_##LINE)) {} \ +} while (0) +#define FLOWCTL_CLEAR(LINE) \ +do { \ + GDBPORT_SERIAL_MCR &= ~UART_MCR_##LINE; \ +} while (0) +#define FLOWCTL_SET(LINE) \ +do { \ + GDBPORT_SERIAL_MCR |= UART_MCR_##LINE; \ +} while (0) +#define FLOWCTL_QUERY(LINE) ({ GDBPORT_SERIAL_MSR & UART_MSR_##LINE; }) + +static inline void __debug_to_serial(const char *p, int n) +{ + char ch; + + FLOWCTL_SET(DTR); + + for (; n > 0; n--) { + LSR_WAIT_FOR(THRE); + FLOWCTL_WAIT_FOR(CTS); + + ch = *p++; + if (ch == 0x0a) { + GDBPORT_SERIAL_TX = 0x0d; + LSR_WAIT_FOR(THRE); + FLOWCTL_WAIT_FOR(CTS); + } + GDBPORT_SERIAL_TX = ch; + } + + FLOWCTL_CLEAR(DTR); +} + +#endif /* !__ASSEMBLY__ */ + +#endif /* CONFIG_GDBSTUB_ON_TTYSx */ + +#endif /* _ASM_UNIT_SERIAL_H */ diff --git a/arch/mn10300/unit-asb2303/include/unit/smc91111.h b/arch/mn10300/unit-asb2303/include/unit/smc91111.h new file mode 100644 index 00000000..dd456e9c --- /dev/null +++ b/arch/mn10300/unit-asb2303/include/unit/smc91111.h @@ -0,0 +1,50 @@ +/* Support for the SMC91C111 NIC on an ASB2303 + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_UNIT_SMC91111_H +#define _ASM_UNIT_SMC91111_H + +#include <asm/intctl-regs.h> + +#define SMC91111_BASE 0xAA000300UL +#define SMC91111_BASE_END 0xAA000400UL +#define SMC91111_IRQ XIRQ3 + +#define SMC_CAN_USE_8BIT 0 +#define SMC_CAN_USE_16BIT 1 +#define SMC_CAN_USE_32BIT 0 +#define SMC_NOWAIT 1 +#define SMC_IRQ_FLAGS (0) + +#if SMC_CAN_USE_8BIT +#define SMC_inb(a, r) inb((unsigned long) ((a) + (r))) +#define SMC_outb(v, a, r) outb(v, (unsigned long) ((a) + (r))) +#endif + +#if SMC_CAN_USE_16BIT +#define SMC_inw(a, r) inw((unsigned long) ((a) + (r))) +#define SMC_outw(v, a, r) outw(v, (unsigned long) ((a) + (r))) +#define SMC_insw(a, r, p, l) insw((unsigned long) ((a) + (r)), (p), (l)) +#define SMC_outsw(a, r, p, l) outsw((unsigned long) ((a) + (r)), (p), (l)) +#endif + +#if SMC_CAN_USE_32BIT +#define SMC_inl(a, r) inl((unsigned long) ((a) + (r))) +#define SMC_outl(v, a, r) outl(v, (unsigned long) ((a) + (r))) +#define SMC_insl(a, r, p, l) insl((unsigned long) ((a) + (r)), (p), (l)) +#define SMC_outsl(a, r, p, l) outsl((unsigned long) ((a) + (r)), (p), (l)) +#endif + +#define RPC_LSA_DEFAULT RPC_LED_100_10 +#define RPC_LSB_DEFAULT RPC_LED_TX_RX + +#define set_irq_type(irq, type) + +#endif /* _ASM_UNIT_SMC91111_H */ diff --git a/arch/mn10300/unit-asb2303/include/unit/timex.h b/arch/mn10300/unit-asb2303/include/unit/timex.h new file mode 100644 index 00000000..cc18fe7d --- /dev/null +++ b/arch/mn10300/unit-asb2303/include/unit/timex.h @@ -0,0 +1,150 @@ +/* ASB2303-specific timer specifications + * + * Copyright (C) 2007, 2010 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_UNIT_TIMEX_H +#define _ASM_UNIT_TIMEX_H + +#ifndef __ASSEMBLY__ +#include <linux/irq.h> +#endif /* __ASSEMBLY__ */ + +#include <asm/timer-regs.h> +#include <unit/clock.h> +#include <asm/param.h> + +/* + * jiffies counter specifications + */ + +#define TMJCBR_MAX 0xffff +#define TMJCIRQ TM1IRQ +#define TMJCICR TM1ICR + +#ifndef __ASSEMBLY__ + +#define MN10300_SRC_IOCLK MN10300_IOCLK + +#ifndef HZ +# error HZ undeclared. +#endif /* !HZ */ +/* use as little prescaling as possible to avoid losing accuracy */ +#if (MN10300_SRC_IOCLK + HZ / 2) / HZ - 1 <= TMJCBR_MAX +# define IOCLK_PRESCALE 1 +# define JC_TIMER_CLKSRC TM0MD_SRC_IOCLK +# define TSC_TIMER_CLKSRC TM4MD_SRC_IOCLK +#elif (MN10300_SRC_IOCLK / 8 + HZ / 2) / HZ - 1 <= TMJCBR_MAX +# define IOCLK_PRESCALE 8 +# define JC_TIMER_CLKSRC TM0MD_SRC_IOCLK_8 +# define TSC_TIMER_CLKSRC TM4MD_SRC_IOCLK_8 +#elif (MN10300_SRC_IOCLK / 32 + HZ / 2) / HZ - 1 <= TMJCBR_MAX +# define IOCLK_PRESCALE 32 +# define JC_TIMER_CLKSRC TM0MD_SRC_IOCLK_32 +# define TSC_TIMER_CLKSRC TM4MD_SRC_IOCLK_32 +#else +# error You lose. +#endif + +#define MN10300_JCCLK (MN10300_SRC_IOCLK / IOCLK_PRESCALE) +#define MN10300_TSCCLK (MN10300_SRC_IOCLK / IOCLK_PRESCALE) + +#define MN10300_JC_PER_HZ ((MN10300_JCCLK + HZ / 2) / HZ) +#define MN10300_TSC_PER_HZ ((MN10300_TSCCLK + HZ / 2) / HZ) + +static inline void stop_jiffies_counter(void) +{ + u16 tmp; + TM01MD = JC_TIMER_CLKSRC | TM1MD_SRC_TM0CASCADE << 8; + tmp = TM01MD; +} + +static inline void reload_jiffies_counter(u32 cnt) +{ + u32 tmp; + + TM01BR = cnt; + tmp = TM01BR; + + TM01MD = JC_TIMER_CLKSRC | \ + TM1MD_SRC_TM0CASCADE << 8 | \ + TM0MD_INIT_COUNTER | \ + TM1MD_INIT_COUNTER << 8; + + + TM01MD = JC_TIMER_CLKSRC | \ + TM1MD_SRC_TM0CASCADE << 8 | \ + TM0MD_COUNT_ENABLE | \ + TM1MD_COUNT_ENABLE << 8; + + tmp = TM01MD; +} + +#endif /* !__ASSEMBLY__ */ + + +/* + * timestamp counter specifications + */ + +#define TMTSCBR_MAX 0xffffffff +#define TMTSCBC TM45BC + +#ifndef __ASSEMBLY__ + +static inline void startup_timestamp_counter(void) +{ + u32 t32; + + /* set up timer 4 & 5 cascaded as a 32-bit counter to count real time + * - count down from 4Gig-1 to 0 and wrap at IOCLK rate + */ + TM45BR = TMTSCBR_MAX; + t32 = TM45BR; + + TM4MD = TSC_TIMER_CLKSRC; + TM4MD |= TM4MD_INIT_COUNTER; + TM4MD &= ~TM4MD_INIT_COUNTER; + TM4ICR = 0; + t32 = TM4ICR; + + TM5MD = TM5MD_SRC_TM4CASCADE; + TM5MD |= TM5MD_INIT_COUNTER; + TM5MD &= ~TM5MD_INIT_COUNTER; + TM5ICR = 0; + t32 = TM5ICR; + + TM5MD |= TM5MD_COUNT_ENABLE; + TM4MD |= TM4MD_COUNT_ENABLE; + t32 = TM5MD; + t32 = TM4MD; +} + +static inline void shutdown_timestamp_counter(void) +{ + u8 t8; + TM4MD = 0; + TM5MD = 0; + t8 = TM4MD; + t8 = TM5MD; +} + +/* + * we use a cascaded pair of 16-bit down-counting timers to count I/O + * clock cycles for the purposes of time keeping + */ +typedef unsigned long cycles_t; + +static inline cycles_t read_timestamp_counter(void) +{ + return (cycles_t)~TMTSCBC; +} + +#endif /* !__ASSEMBLY__ */ + +#endif /* _ASM_UNIT_TIMEX_H */ diff --git a/arch/mn10300/unit-asb2303/leds.c b/arch/mn10300/unit-asb2303/leds.c new file mode 100644 index 00000000..c0383935 --- /dev/null +++ b/arch/mn10300/unit-asb2303/leds.c @@ -0,0 +1,52 @@ +/* ASB2303 peripheral 7-segment LEDs x1 support + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/init.h> + +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/intctl-regs.h> +#include <asm/rtc-regs.h> +#include <unit/leds.h> + +#if 0 +static const u8 asb2303_led_hex_tbl[16] = { + 0x80, 0xf2, 0x48, 0x60, 0x32, 0x24, 0x04, 0xf0, + 0x00, 0x20, 0x10, 0x06, 0x8c, 0x42, 0x0c, 0x1c +}; +#endif + +static const u8 asb2303_led_chase_tbl[6] = { + ~0x02, /* top - segA */ + ~0x04, /* right top - segB */ + ~0x08, /* right bottom - segC */ + ~0x10, /* bottom - segD */ + ~0x20, /* left bottom - segE */ + ~0x40, /* left top - segF */ +}; + +static unsigned asb2303_led_chase; + +void peripheral_leds_display_exception(enum exception_code code) +{ + ASB2303_GPIO0DEF = 0x5555; /* configure as an output port */ + ASB2303_7SEGLEDS = 0x6d; /* triple horizontal bar */ +} + +void peripheral_leds_led_chase(void) +{ + ASB2303_GPIO0DEF = 0x5555; /* configure as an output port */ + ASB2303_7SEGLEDS = asb2303_led_chase_tbl[asb2303_led_chase]; + asb2303_led_chase++; + if (asb2303_led_chase >= 6) + asb2303_led_chase = 0; +} diff --git a/arch/mn10300/unit-asb2303/smc91111.c b/arch/mn10300/unit-asb2303/smc91111.c new file mode 100644 index 00000000..43c24643 --- /dev/null +++ b/arch/mn10300/unit-asb2303/smc91111.c @@ -0,0 +1,52 @@ +/* ASB2303 initialisation + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/init.h> +#include <linux/platform_device.h> + +#include <asm/io.h> +#include <asm/timex.h> +#include <asm/processor.h> +#include <asm/intctl-regs.h> +#include <unit/smc91111.h> + +static struct resource smc91c111_resources[] = { + [0] = { + .start = SMC91111_BASE, + .end = SMC91111_BASE_END, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = SMC91111_IRQ, + .end = SMC91111_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device smc91c111_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(smc91c111_resources), + .resource = smc91c111_resources, +}; + +/* + * add platform devices + */ +static int __init unit_device_init(void) +{ + platform_device_register(&smc91c111_device); + return 0; +} + +device_initcall(unit_device_init); diff --git a/arch/mn10300/unit-asb2303/unit-init.c b/arch/mn10300/unit-asb2303/unit-init.c new file mode 100644 index 00000000..834a76aa --- /dev/null +++ b/arch/mn10300/unit-asb2303/unit-init.c @@ -0,0 +1,68 @@ +/* ASB2303 initialisation + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/init.h> +#include <linux/device.h> + +#include <asm/io.h> +#include <asm/setup.h> +#include <asm/processor.h> +#include <asm/irq.h> +#include <asm/intctl-regs.h> + +/* + * initialise some of the unit hardware before gdbstub is set up + */ +asmlinkage void __init unit_init(void) +{ + /* set up the external interrupts */ + SET_XIRQ_TRIGGER(0, XIRQ_TRIGGER_HILEVEL); + SET_XIRQ_TRIGGER(2, XIRQ_TRIGGER_LOWLEVEL); + SET_XIRQ_TRIGGER(3, XIRQ_TRIGGER_HILEVEL); + SET_XIRQ_TRIGGER(4, XIRQ_TRIGGER_LOWLEVEL); + SET_XIRQ_TRIGGER(5, XIRQ_TRIGGER_LOWLEVEL); + +#ifdef CONFIG_EXT_SERIAL_IRQ_LEVEL + set_intr_level(XIRQ0, NUM2GxICR_LEVEL(CONFIG_EXT_SERIAL_IRQ_LEVEL)); +#endif + +#ifdef CONFIG_ETHERNET_IRQ_LEVEL + set_intr_level(XIRQ3, NUM2GxICR_LEVEL(CONFIG_ETHERNET_IRQ_LEVEL)); +#endif +} + +/* + * initialise the rest of the unit hardware after gdbstub is ready + */ +void __init unit_setup(void) +{ +} + +/* + * initialise the external interrupts used by a unit of this type + */ +void __init unit_init_IRQ(void) +{ + unsigned int extnum; + + for (extnum = 0; extnum < NR_XIRQS; extnum++) { + switch (GET_XIRQ_TRIGGER(extnum)) { + case XIRQ_TRIGGER_HILEVEL: + case XIRQ_TRIGGER_LOWLEVEL: + mn10300_set_lateack_irq_type(XIRQ2IRQ(extnum)); + break; + default: + break; + } + } +} diff --git a/arch/mn10300/unit-asb2305/Makefile b/arch/mn10300/unit-asb2305/Makefile new file mode 100644 index 00000000..05510222 --- /dev/null +++ b/arch/mn10300/unit-asb2305/Makefile @@ -0,0 +1,8 @@ +############################################################################### +# +# Makefile for the ASB2305 board +# +############################################################################### +obj-y := unit-init.o leds.o + +obj-$(CONFIG_PCI) += pci.o pci-asb2305.o pci-irq.o pci-iomap.o diff --git a/arch/mn10300/unit-asb2305/include/unit/clock.h b/arch/mn10300/unit-asb2305/include/unit/clock.h new file mode 100644 index 00000000..29e34254 --- /dev/null +++ b/arch/mn10300/unit-asb2305/include/unit/clock.h @@ -0,0 +1,24 @@ +/* ASB2305-specific clocks + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _ASM_UNIT_CLOCK_H +#define _ASM_UNIT_CLOCK_H + +#ifndef __ASSEMBLY__ + +#define MN10300_IOCLK 33333333UL +/* #define MN10300_IOBCLK 66666666UL */ + +#endif /* !__ASSEMBLY__ */ + +#define MN10300_WDCLK MN10300_IOCLK + +#endif /* _ASM_UNIT_CLOCK_H */ diff --git a/arch/mn10300/unit-asb2305/include/unit/leds.h b/arch/mn10300/unit-asb2305/include/unit/leds.h new file mode 100644 index 00000000..bc471f61 --- /dev/null +++ b/arch/mn10300/unit-asb2305/include/unit/leds.h @@ -0,0 +1,51 @@ +/* ASB2305-specific LEDs + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _ASM_UNIT_LEDS_H +#define _ASM_UNIT_LEDS_H + +#include <asm/pio-regs.h> +#include <asm/cpu-regs.h> +#include <asm/exceptions.h> + +#define ASB2305_7SEGLEDS __SYSREG(0xA6F90000, u32) + +/* perform a hard reset by driving PIO06 low */ +#define mn10300_unit_hard_reset() \ +do { \ + P0OUT &= 0xbf; \ + P0MD = (P0MD & P0MD_6) | P0MD_6_OUT; \ +} while (0) + +/* + * use the 7-segment LEDs to indicate states + */ +/* indicate double-fault by displaying "db-f" on the LEDs */ +#define mn10300_set_dbfleds \ + mov 0x43077f1d,d0 ; \ + mov d0,(ASB2305_7SEGLEDS) + +/* flip the 7-segment LEDs between "Gdb-" and "----" */ +#define mn10300_set_gdbleds(ONOFF) \ +do { \ + ASB2305_7SEGLEDS = (ONOFF) ? 0x8543077f : 0x7f7f7f7f; \ +} while (0) + +#ifndef __ASSEMBLY__ +extern void peripheral_leds_display_exception(enum exception_code); +extern void peripheral_leds_led_chase(void); +extern void peripheral_leds7x4_display_dec(unsigned int, unsigned int); +extern void peripheral_leds7x4_display_hex(unsigned int, unsigned int); +extern void peripheral_leds7x4_display_minssecs(unsigned int, unsigned int); +extern void peripheral_leds7x4_display_rtc(void); +#endif /* __ASSEMBLY__ */ + +#endif /* _ASM_UNIT_LEDS_H */ diff --git a/arch/mn10300/unit-asb2305/include/unit/serial.h b/arch/mn10300/unit-asb2305/include/unit/serial.h new file mode 100644 index 00000000..88c08219 --- /dev/null +++ b/arch/mn10300/unit-asb2305/include/unit/serial.h @@ -0,0 +1,125 @@ +/* ASB2305-specific 8250 serial ports + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_UNIT_SERIAL_H +#define _ASM_UNIT_SERIAL_H + +#include <asm/cpu-regs.h> +#include <proc/irq.h> +#include <linux/serial_reg.h> + +#define SERIAL_PORT0_BASE_ADDRESS 0xA6FB0000 +#define ASB2305_DEBUG_MCR __SYSREG(0xA6FB0000 + UART_MCR * 2, u8) + +#define SERIAL_IRQ XIRQ0 /* Dual serial (PC16552) (Hi) */ + +/* + * The ASB2305 has an 18.432 MHz clock the UART + */ +#define BASE_BAUD (18432000 / 16) + +/* + * dispose of the /dev/ttyS0 serial port + */ +#ifndef CONFIG_GDBSTUB_ON_TTYSx + +#define SERIAL_PORT_DFNS \ + { \ + .baud_base = BASE_BAUD, \ + .irq = SERIAL_IRQ, \ + .flags = STD_COM_FLAGS, \ + .iomem_base = (u8 *) SERIAL_PORT0_BASE_ADDRESS, \ + .iomem_reg_shift = 2, \ + .io_type = SERIAL_IO_MEM, \ + }, + +#ifndef __ASSEMBLY__ + +static inline void __debug_to_serial(const char *p, int n) +{ +} + +#endif /* !__ASSEMBLY__ */ + +#else /* CONFIG_GDBSTUB_ON_TTYSx */ + +#define SERIAL_PORT_DFNS /* stolen by gdb-stub */ + +#if defined(CONFIG_GDBSTUB_ON_TTYS0) +#define GDBPORT_SERIAL_RX __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_RX * 4, u8) +#define GDBPORT_SERIAL_TX __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_TX * 4, u8) +#define GDBPORT_SERIAL_DLL __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_DLL * 4, u8) +#define GDBPORT_SERIAL_DLM __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_DLM * 4, u8) +#define GDBPORT_SERIAL_IER __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_IER * 4, u8) +#define GDBPORT_SERIAL_IIR __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_IIR * 4, u8) +#define GDBPORT_SERIAL_FCR __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_FCR * 4, u8) +#define GDBPORT_SERIAL_LCR __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_LCR * 4, u8) +#define GDBPORT_SERIAL_MCR __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_MCR * 4, u8) +#define GDBPORT_SERIAL_LSR __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_LSR * 4, u8) +#define GDBPORT_SERIAL_MSR __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_MSR * 4, u8) +#define GDBPORT_SERIAL_SCR __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_SCR * 4, u8) +#define GDBPORT_SERIAL_IRQ SERIAL_IRQ + +#elif defined(CONFIG_GDBSTUB_ON_TTYS1) +#error The ASB2305 doesnt have a /dev/ttyS1 +#endif + +#ifndef __ASSEMBLY__ + +#define TTYS0_TX __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_TX * 4, u8) +#define TTYS0_MCR __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_MCR * 4, u8) +#define TTYS0_LSR __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_LSR * 4, u8) +#define TTYS0_MSR __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_MSR * 4, u8) + +#define LSR_WAIT_FOR(STATE) \ +do { \ + while (!(TTYS0_LSR & UART_LSR_##STATE)) {} \ +} while (0) +#define FLOWCTL_WAIT_FOR(LINE) \ +do { \ + while (!(TTYS0_MSR & UART_MSR_##LINE)) {} \ +} while (0) +#define FLOWCTL_CLEAR(LINE) \ +do { \ + TTYS0_MCR &= ~UART_MCR_##LINE; \ +} while (0) +#define FLOWCTL_SET(LINE) \ +do { \ + TTYS0_MCR |= UART_MCR_##LINE; \ +} while (0) +#define FLOWCTL_QUERY(LINE) ({ TTYS0_MSR & UART_MSR_##LINE; }) + +static inline void __debug_to_serial(const char *p, int n) +{ + char ch; + + FLOWCTL_SET(DTR); + + for (; n > 0; n--) { + LSR_WAIT_FOR(THRE); + FLOWCTL_WAIT_FOR(CTS); + + ch = *p++; + if (ch == 0x0a) { + TTYS0_TX = 0x0d; + LSR_WAIT_FOR(THRE); + FLOWCTL_WAIT_FOR(CTS); + } + TTYS0_TX = ch; + } + + FLOWCTL_CLEAR(DTR); +} + +#endif /* !__ASSEMBLY__ */ + +#endif /* CONFIG_GDBSTUB_ON_TTYSx */ + +#endif /* _ASM_UNIT_SERIAL_H */ diff --git a/arch/mn10300/unit-asb2305/include/unit/timex.h b/arch/mn10300/unit-asb2305/include/unit/timex.h new file mode 100644 index 00000000..758af30d --- /dev/null +++ b/arch/mn10300/unit-asb2305/include/unit/timex.h @@ -0,0 +1,150 @@ +/* ASB2305-specific timer specifications + * + * Copyright (C) 2007, 2010 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _ASM_UNIT_TIMEX_H +#define _ASM_UNIT_TIMEX_H + +#ifndef __ASSEMBLY__ +#include <linux/irq.h> +#endif /* __ASSEMBLY__ */ + +#include <asm/timer-regs.h> +#include <unit/clock.h> +#include <asm/param.h> + +/* + * jiffies counter specifications + */ + +#define TMJCBR_MAX 0xffff +#define TMJCIRQ TM1IRQ +#define TMJCICR TM1ICR + +#ifndef __ASSEMBLY__ + +#define MN10300_SRC_IOCLK MN10300_IOCLK + +#ifndef HZ +# error HZ undeclared. +#endif /* !HZ */ +/* use as little prescaling as possible to avoid losing accuracy */ +#if (MN10300_SRC_IOCLK + HZ / 2) / HZ - 1 <= TMJCBR_MAX +# define IOCLK_PRESCALE 1 +# define JC_TIMER_CLKSRC TM0MD_SRC_IOCLK +# define TSC_TIMER_CLKSRC TM4MD_SRC_IOCLK +#elif (MN10300_SRC_IOCLK / 8 + HZ / 2) / HZ - 1 <= TMJCBR_MAX +# define IOCLK_PRESCALE 8 +# define JC_TIMER_CLKSRC TM0MD_SRC_IOCLK_8 +# define TSC_TIMER_CLKSRC TM4MD_SRC_IOCLK_8 +#elif (MN10300_SRC_IOCLK / 32 + HZ / 2) / HZ - 1 <= TMJCBR_MAX +# define IOCLK_PRESCALE 32 +# define JC_TIMER_CLKSRC TM0MD_SRC_IOCLK_32 +# define TSC_TIMER_CLKSRC TM4MD_SRC_IOCLK_32 +#else +# error You lose. +#endif + +#define MN10300_JCCLK (MN10300_SRC_IOCLK / IOCLK_PRESCALE) +#define MN10300_TSCCLK (MN10300_SRC_IOCLK / IOCLK_PRESCALE) + +#define MN10300_JC_PER_HZ ((MN10300_JCCLK + HZ / 2) / HZ) +#define MN10300_TSC_PER_HZ ((MN10300_TSCCLK + HZ / 2) / HZ) + +static inline void stop_jiffies_counter(void) +{ + u16 tmp; + TM01MD = JC_TIMER_CLKSRC | TM1MD_SRC_TM0CASCADE << 8; + tmp = TM01MD; +} + +static inline void reload_jiffies_counter(u32 cnt) +{ + u32 tmp; + + TM01BR = cnt; + tmp = TM01BR; + + TM01MD = JC_TIMER_CLKSRC | \ + TM1MD_SRC_TM0CASCADE << 8 | \ + TM0MD_INIT_COUNTER | \ + TM1MD_INIT_COUNTER << 8; + + + TM01MD = JC_TIMER_CLKSRC | \ + TM1MD_SRC_TM0CASCADE << 8 | \ + TM0MD_COUNT_ENABLE | \ + TM1MD_COUNT_ENABLE << 8; + + tmp = TM01MD; +} + +#endif /* !__ASSEMBLY__ */ + + +/* + * timestamp counter specifications + */ + +#define TMTSCBR_MAX 0xffffffff +#define TMTSCBC TM45BC + +#ifndef __ASSEMBLY__ + +static inline void startup_timestamp_counter(void) +{ + u32 t32; + + /* set up timer 4 & 5 cascaded as a 32-bit counter to count real time + * - count down from 4Gig-1 to 0 and wrap at IOCLK rate + */ + TM45BR = TMTSCBR_MAX; + t32 = TM45BR; + + TM4MD = TSC_TIMER_CLKSRC; + TM4MD |= TM4MD_INIT_COUNTER; + TM4MD &= ~TM4MD_INIT_COUNTER; + TM4ICR = 0; + t32 = TM4ICR; + + TM5MD = TM5MD_SRC_TM4CASCADE; + TM5MD |= TM5MD_INIT_COUNTER; + TM5MD &= ~TM5MD_INIT_COUNTER; + TM5ICR = 0; + t32 = TM5ICR; + + TM5MD |= TM5MD_COUNT_ENABLE; + TM4MD |= TM4MD_COUNT_ENABLE; + t32 = TM5MD; + t32 = TM4MD; +} + +static inline void shutdown_timestamp_counter(void) +{ + u8 t8; + TM4MD = 0; + TM5MD = 0; + t8 = TM4MD; + t8 = TM5MD; +} + +/* + * we use a cascaded pair of 16-bit down-counting timers to count I/O + * clock cycles for the purposes of time keeping + */ +typedef unsigned long cycles_t; + +static inline cycles_t read_timestamp_counter(void) +{ + return (cycles_t)~TMTSCBC; +} + +#endif /* !__ASSEMBLY__ */ + +#endif /* _ASM_UNIT_TIMEX_H */ diff --git a/arch/mn10300/unit-asb2305/leds.c b/arch/mn10300/unit-asb2305/leds.c new file mode 100644 index 00000000..6f8de995 --- /dev/null +++ b/arch/mn10300/unit-asb2305/leds.c @@ -0,0 +1,124 @@ +/* ASB2305 Peripheral 7-segment LEDs x4 support + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/init.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/intctl-regs.h> +#include <asm/rtc-regs.h> +#include <unit/leds.h> + +static const u8 asb2305_led_hex_tbl[16] = { + 0x80, 0xf2, 0x48, 0x60, 0x32, 0x24, 0x04, 0xf0, + 0x00, 0x20, 0x10, 0x06, 0x8c, 0x42, 0x0c, 0x1c +}; + +static const u32 asb2305_led_chase_tbl[6] = { + ~0x02020202, /* top - segA */ + ~0x04040404, /* right top - segB */ + ~0x08080808, /* right bottom - segC */ + ~0x10101010, /* bottom - segD */ + ~0x20202020, /* left bottom - segE */ + ~0x40404040, /* left top - segF */ +}; + +static unsigned asb2305_led_chase; + +void peripheral_leds7x4_display_dec(unsigned int val, unsigned int points) +{ + u32 leds; + + leds = asb2305_led_hex_tbl[(val/1000) % 10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[(val/100) % 10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[(val/10) % 10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[val % 10]; + leds |= points^0x01010101; + + ASB2305_7SEGLEDS = leds; +} + +void peripheral_leds7x4_display_hex(unsigned int val, unsigned int points) +{ + u32 leds; + + leds = asb2305_led_hex_tbl[(val/1000) % 10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[(val/100) % 10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[(val/10) % 10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[val % 10]; + leds |= points^0x01010101; + + ASB2305_7SEGLEDS = leds; +} + +void peripheral_leds_display_exception(enum exception_code code) +{ + u32 leds; + + leds = asb2305_led_hex_tbl[(code/0x100) % 0x10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[(code/0x10) % 0x10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[code % 0x10]; + leds |= 0x6d010101; + + ASB2305_7SEGLEDS = leds; +} + +void peripheral_leds7x4_display_minssecs(unsigned int time, unsigned int points) +{ + u32 leds; + + leds = asb2305_led_hex_tbl[(time/600) % 6]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[(time/60) % 10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[(time/10) % 6]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[time % 10]; + leds |= points^0x01010101; + + ASB2305_7SEGLEDS = leds; +} + +void peripheral_leds7x4_display_rtc(void) +{ + unsigned int clock; + u8 mins, secs; + + mins = RTMCR; + secs = RTSCR; + + clock = ((mins & 0xf0) >> 4); + clock *= 10; + clock += (mins & 0x0f); + clock *= 6; + + clock += ((secs & 0xf0) >> 4); + clock *= 10; + clock += (secs & 0x0f); + + peripheral_leds7x4_display_minssecs(clock, 0); +} + +void peripheral_leds_led_chase(void) +{ + ASB2305_7SEGLEDS = asb2305_led_chase_tbl[asb2305_led_chase]; + asb2305_led_chase++; + if (asb2305_led_chase >= 6) + asb2305_led_chase = 0; +} diff --git a/arch/mn10300/unit-asb2305/pci-asb2305.c b/arch/mn10300/unit-asb2305/pci-asb2305.c new file mode 100644 index 00000000..8e6763e6 --- /dev/null +++ b/arch/mn10300/unit-asb2305/pci-asb2305.c @@ -0,0 +1,259 @@ +/* ASB2305 PCI resource stuff + * + * Copyright (C) 2001 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * - Derived from arch/i386/pci-i386.c + * - Copyright 1997--2000 Martin Mares <mj@suse.cz> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/errno.h> +#include "pci-asb2305.h" + +/* + * 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 have 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 0 + struct pci_dev *dev = data; + + printk(KERN_DEBUG + "### PCIBIOS_ALIGN_RESOURCE(%s,,{%08lx-%08lx,%08lx},%lx)\n", + pci_name(dev), + res->start, + res->end, + res->flags, + size + ); +#endif + + if ((res->flags & IORESOURCE_IO) && (start & 0x300)) + start = (start + 0x3ff) & ~0x3ff; + + return start; +} + + +/* + * Handle resources of PCI devices. If the world were perfect, we could + * just allocate all the resource regions and do nothing more. It isn't. + * On the other hand, we cannot just re-allocate all devices, as it would + * require us to know lots of host bridge internals. So we attempt to + * keep as much of the original configuration as possible, but tweak it + * when it's found to be wrong. + * + * Known BIOS problems we have to work around: + * - I/O or memory regions not configured + * - regions configured, but not enabled in the command register + * - bogus I/O addresses above 64K used + * - expansion ROMs left enabled (this may sound harmless, but given + * the fact the PCI specs explicitly allow address decoders to be + * shared between expansion ROMs and other resource regions, it's + * at least dangerous) + * + * Our solution: + * (1) Allocate resources for all buses behind PCI-to-PCI bridges. + * This gives us fixed barriers on where we can allocate. + * (2) Allocate resources for all enabled devices. If there is + * a collision, just mark the resource as unallocated. Also + * disable expansion ROMs during this step. + * (3) Try to allocate resources for disabled devices. If the + * resources were assigned correctly, everything goes well, + * if they weren't, they won't disturb allocation of other + * resources. + * (4) Assign new addresses to resources which were either + * not configured at all or misconfigured. If explicitly + * requested by the user, configure expansion ROM address + * as well. + */ +static void __init pcibios_allocate_bus_resources(struct list_head *bus_list) +{ + struct pci_bus *bus; + struct pci_dev *dev; + int idx; + struct resource *r; + + /* Depth-First Search on bus tree */ + list_for_each_entry(bus, bus_list, node) { + dev = bus->self; + if (dev) { + for (idx = PCI_BRIDGE_RESOURCES; + idx < PCI_NUM_RESOURCES; + idx++) { + r = &dev->resource[idx]; + if (!r->flags) + continue; + if (!r->start || + pci_claim_resource(dev, idx) < 0) { + printk(KERN_ERR "PCI:" + " Cannot allocate resource" + " region %d of bridge %s\n", + idx, pci_name(dev)); + /* Something is wrong with the region. + * Invalidate the resource to prevent + * child resource allocations in this + * range. */ + r->start = r->end = 0; + r->flags = 0; + } + } + } + pcibios_allocate_bus_resources(&bus->children); + } +} + +static void __init pcibios_allocate_resources(int pass) +{ + struct pci_dev *dev = NULL; + int idx, disabled; + u16 command; + struct resource *r; + + for_each_pci_dev(dev) { + pci_read_config_word(dev, PCI_COMMAND, &command); + for (idx = 0; idx < 6; idx++) { + r = &dev->resource[idx]; + if (r->parent) /* Already allocated */ + continue; + if (!r->start) /* Address not assigned */ + continue; + if (r->flags & IORESOURCE_IO) + disabled = !(command & PCI_COMMAND_IO); + else + disabled = !(command & PCI_COMMAND_MEMORY); + if (pass == disabled) { + DBG("PCI[%s]: Resource %08lx-%08lx" + " (f=%lx, d=%d, p=%d)\n", + pci_name(dev), r->start, r->end, r->flags, + disabled, pass); + if (pci_claim_resource(dev, idx) < 0) { + printk(KERN_ERR "PCI:" + " Cannot allocate resource" + " region %d of device %s\n", + idx, pci_name(dev)); + /* We'll assign a new address later */ + r->end -= r->start; + r->start = 0; + } + } + } + if (!pass) { + r = &dev->resource[PCI_ROM_RESOURCE]; + if (r->flags & IORESOURCE_ROM_ENABLE) { + /* Turn the ROM off, leave the resource region, + * but keep it unregistered. */ + u32 reg; + DBG("PCI: Switching off ROM of %s\n", + pci_name(dev)); + r->flags &= ~IORESOURCE_ROM_ENABLE; + pci_read_config_dword( + dev, dev->rom_base_reg, ®); + pci_write_config_dword( + dev, dev->rom_base_reg, + reg & ~PCI_ROM_ADDRESS_ENABLE); + } + } + } +} + +static int __init pcibios_assign_resources(void) +{ + struct pci_dev *dev = NULL; + struct resource *r; + + if (!(pci_probe & PCI_ASSIGN_ROMS)) { + /* Try to use BIOS settings for ROMs, otherwise let + pci_assign_unassigned_resources() allocate the new + addresses. */ + for_each_pci_dev(dev) { + r = &dev->resource[PCI_ROM_RESOURCE]; + if (!r->flags || !r->start) + continue; + if (pci_claim_resource(dev, PCI_ROM_RESOURCE) < 0) { + r->end -= r->start; + r->start = 0; + } + } + } + + pci_assign_unassigned_resources(); + + return 0; +} + +fs_initcall(pcibios_assign_resources); + +void __init pcibios_resource_survey(void) +{ + DBG("PCI: Allocating resources\n"); + pcibios_allocate_bus_resources(&pci_root_buses); + pcibios_allocate_resources(0); + pcibios_allocate_resources(1); +} + +/* + * If we set up a device for bus mastering, we need to check the latency + * timer as certain crappy BIOSes forget to set it properly. + */ +unsigned int pcibios_max_latency = 255; + +void pcibios_set_master(struct pci_dev *dev) +{ + u8 lat; + + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); + + if (lat < 16) + lat = (64 <= pcibios_max_latency) ? 64 : pcibios_max_latency; + else if (lat > pcibios_max_latency) + lat = pcibios_max_latency; + else + return; + + pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat); +} + +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 prot; + + /* Leave vm_pgoff as-is, the PCI space address is the physical + * address on this platform. + */ + vma->vm_flags |= VM_LOCKED | VM_IO; + + prot = pgprot_val(vma->vm_page_prot); + prot &= ~_PAGE_CACHE; + vma->vm_page_prot = __pgprot(prot); + + /* Write-combine setting is ignored */ + if (io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) + return -EAGAIN; + + return 0; +} diff --git a/arch/mn10300/unit-asb2305/pci-asb2305.h b/arch/mn10300/unit-asb2305/pci-asb2305.h new file mode 100644 index 00000000..c3fa294b --- /dev/null +++ b/arch/mn10300/unit-asb2305/pci-asb2305.h @@ -0,0 +1,79 @@ +/* ASB2305 Arch-specific PCI declarations + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * Derived from: arch/i386/kernel/pci-i386.h: (c) 1999 Martin Mares <mj@ucw.cz> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _PCI_ASB2305_H +#define _PCI_ASB2305_H + +#undef DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +#define PCI_PROBE_BIOS 1 +#define PCI_PROBE_CONF1 2 +#define PCI_PROBE_CONF2 4 +#define PCI_NO_CHECKS 0x400 +#define PCI_ASSIGN_ROMS 0x1000 +#define PCI_BIOS_IRQ_SCAN 0x2000 + +extern unsigned int pci_probe; + +/* pci-asb2305.c */ + +extern unsigned int pcibios_max_latency; + +extern void pcibios_resource_survey(void); + +/* pci.c */ + +extern int pcibios_last_bus; +extern struct pci_bus *pci_root_bus; +extern struct pci_ops *pci_root_ops; + +extern struct irq_routing_table *pcibios_get_irq_routing_table(void); +extern int pcibios_set_irq_routing(struct pci_dev *dev, int pin, int irq); + +/* pci-irq.c */ + +struct irq_info { + u8 bus, devfn; /* Bus, device and function */ + struct { + u8 link; /* IRQ line ID, chipset dependent, + * 0=not routed */ + u16 bitmap; /* Available IRQs */ + } __attribute__((packed)) irq[4]; + u8 slot; /* Slot number, 0=onboard */ + u8 rfu; +} __attribute__((packed)); + +struct irq_routing_table { + u32 signature; /* PIRQ_SIGNATURE should be here */ + u16 version; /* PIRQ_VERSION */ + u16 size; /* Table size in bytes */ + u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */ + u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */ + u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */ + u32 miniport_data; /* Crap */ + u8 rfu[11]; + u8 checksum; /* Modulo 256 checksum must give zero */ + struct irq_info slots[0]; +} __attribute__((packed)); + +extern unsigned int pcibios_irq_mask; + +extern void pcibios_irq_init(void); +extern void pcibios_fixup_irqs(void); +extern void pcibios_enable_irq(struct pci_dev *dev); + +#endif /* PCI_ASB2305_H */ diff --git a/arch/mn10300/unit-asb2305/pci-iomap.c b/arch/mn10300/unit-asb2305/pci-iomap.c new file mode 100644 index 00000000..c1a8d8f9 --- /dev/null +++ b/arch/mn10300/unit-asb2305/pci-iomap.c @@ -0,0 +1,31 @@ +/* ASB2305 PCI I/O mapping handler + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/pci.h> +#include <linux/module.h> + +/* + * Create a virtual mapping cookie for a PCI BAR (memory or IO) + */ +void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen) +{ + resource_size_t start = pci_resource_start(dev, bar); + resource_size_t len = pci_resource_len(dev, bar); + unsigned long flags = pci_resource_flags(dev, bar); + + if (!len || !start) + return NULL; + + if ((flags & IORESOURCE_IO) || (flags & IORESOURCE_MEM)) + return (void __iomem *) start; + + return NULL; +} +EXPORT_SYMBOL(pci_iomap); diff --git a/arch/mn10300/unit-asb2305/pci-irq.c b/arch/mn10300/unit-asb2305/pci-irq.c new file mode 100644 index 00000000..91212ea7 --- /dev/null +++ b/arch/mn10300/unit-asb2305/pci-irq.c @@ -0,0 +1,50 @@ +/* PCI IRQ routing on the MN103E010 based ASB2305 + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + * + * This is simple: All PCI interrupts route through the CPU's XIRQ1 pin [IRQ 35] + */ +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <asm/io.h> +#include <asm/smp.h> +#include "pci-asb2305.h" + +void __init pcibios_irq_init(void) +{ +} + +void __init pcibios_fixup_irqs(void) +{ + struct pci_dev *dev = NULL; + u8 line, pin; + + while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + if (pin) { + dev->irq = XIRQ1; + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, + dev->irq); + } + pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &line); + } +} + +void __init pcibios_penalize_isa_irq(int irq) +{ +} + +void pcibios_enable_irq(struct pci_dev *dev) +{ + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); +} diff --git a/arch/mn10300/unit-asb2305/pci.c b/arch/mn10300/unit-asb2305/pci.c new file mode 100644 index 00000000..a4954fe8 --- /dev/null +++ b/arch/mn10300/unit-asb2305/pci.c @@ -0,0 +1,557 @@ +/* ASB2305 PCI support + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * Derived from arch/i386/kernel/pci-pc.c + * (c) 1999--2000 Martin Mares <mj@suse.cz> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <asm/io.h> +#include "pci-asb2305.h" + +unsigned int pci_probe = 1; + +int pcibios_last_bus = -1; +struct pci_bus *pci_root_bus; +struct pci_ops *pci_root_ops; + +/* + * The accessible PCI window does not cover the entire CPU address space, but + * there are devices we want to access outside of that window, so we need to + * insert specific PCI bus resources instead of using the platform-level bus + * resources directly for the PCI root bus. + * + * These are configured and inserted by pcibios_init() and are attached to the + * root bus by pcibios_fixup_bus(). + */ +static struct resource pci_ioport_resource = { + .name = "PCI IO", + .start = 0xbe000000, + .end = 0xbe03ffff, + .flags = IORESOURCE_IO, +}; + +static struct resource pci_iomem_resource = { + .name = "PCI mem", + .start = 0xb8000000, + .end = 0xbbffffff, + .flags = IORESOURCE_MEM, +}; + +/* + * Functions for accessing PCI configuration space + */ + +#define CONFIG_CMD(bus, devfn, where) \ + (0x80000000 | (bus->number << 16) | (devfn << 8) | (where & ~3)) + +#define MEM_PAGING_REG (*(volatile __u32 *) 0xBFFFFFF4) +#define CONFIG_ADDRESS (*(volatile __u32 *) 0xBFFFFFF8) +#define CONFIG_DATAL(X) (*(volatile __u32 *) 0xBFFFFFFC) +#define CONFIG_DATAW(X) (*(volatile __u16 *) (0xBFFFFFFC + ((X) & 2))) +#define CONFIG_DATAB(X) (*(volatile __u8 *) (0xBFFFFFFC + ((X) & 3))) + +#define BRIDGEREGB(X) (*(volatile __u8 *) (0xBE040000 + (X))) +#define BRIDGEREGW(X) (*(volatile __u16 *) (0xBE040000 + (X))) +#define BRIDGEREGL(X) (*(volatile __u32 *) (0xBE040000 + (X))) + +static inline int __query(const struct pci_bus *bus, unsigned int devfn) +{ +#if 0 + return bus->number == 0 && (devfn == PCI_DEVFN(0, 0)); + return bus->number == 1; + return bus->number == 0 && + (devfn == PCI_DEVFN(2, 0) || devfn == PCI_DEVFN(3, 0)); +#endif + return 1; +} + +/* + * translate Linuxcentric addresses to PCI bus addresses + */ +void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, + struct resource *res) +{ + if (res->flags & IORESOURCE_IO) { + region->start = (res->start & 0x00ffffff); + region->end = (res->end & 0x00ffffff); + } + + if (res->flags & IORESOURCE_MEM) { + region->start = (res->start & 0x03ffffff) | MEM_PAGING_REG; + region->end = (res->end & 0x03ffffff) | MEM_PAGING_REG; + } + +#if 0 + printk(KERN_DEBUG "RES->BUS: %lx-%lx => %lx-%lx\n", + res->start, res->end, region->start, region->end); +#endif +} +EXPORT_SYMBOL(pcibios_resource_to_bus); + +/* + * translate PCI bus addresses to Linuxcentric addresses + */ +void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, + struct pci_bus_region *region) +{ + if (res->flags & IORESOURCE_IO) { + res->start = (region->start & 0x00ffffff) | 0xbe000000; + res->end = (region->end & 0x00ffffff) | 0xbe000000; + } + + if (res->flags & IORESOURCE_MEM) { + res->start = (region->start & 0x03ffffff) | 0xb8000000; + res->end = (region->end & 0x03ffffff) | 0xb8000000; + } + +#if 0 + printk(KERN_INFO "BUS->RES: %lx-%lx => %lx-%lx\n", + region->start, region->end, res->start, res->end); +#endif +} +EXPORT_SYMBOL(pcibios_bus_to_resource); + +/* + * + */ +static int pci_ampci_read_config_byte(struct pci_bus *bus, unsigned int devfn, + int where, u32 *_value) +{ + u32 rawval, value; + + if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) { + value = BRIDGEREGB(where); + __pcbdebug("=> %02hx", &BRIDGEREGL(where), value); + } else { + CONFIG_ADDRESS = CONFIG_CMD(bus, devfn, where); + rawval = CONFIG_ADDRESS; + value = CONFIG_DATAB(where); + if (__query(bus, devfn)) + __pcidebug("=> %02hx", bus, devfn, where, value); + } + + *_value = value; + return PCIBIOS_SUCCESSFUL; +} + +static int pci_ampci_read_config_word(struct pci_bus *bus, unsigned int devfn, + int where, u32 *_value) +{ + u32 rawval, value; + + if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) { + value = BRIDGEREGW(where); + __pcbdebug("=> %04hx", &BRIDGEREGL(where), value); + } else { + CONFIG_ADDRESS = CONFIG_CMD(bus, devfn, where); + rawval = CONFIG_ADDRESS; + value = CONFIG_DATAW(where); + if (__query(bus, devfn)) + __pcidebug("=> %04hx", bus, devfn, where, value); + } + + *_value = value; + return PCIBIOS_SUCCESSFUL; +} + +static int pci_ampci_read_config_dword(struct pci_bus *bus, unsigned int devfn, + int where, u32 *_value) +{ + u32 rawval, value; + + if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) { + value = BRIDGEREGL(where); + __pcbdebug("=> %08x", &BRIDGEREGL(where), value); + } else { + CONFIG_ADDRESS = CONFIG_CMD(bus, devfn, where); + rawval = CONFIG_ADDRESS; + value = CONFIG_DATAL(where); + if (__query(bus, devfn)) + __pcidebug("=> %08x", bus, devfn, where, value); + } + + *_value = value; + return PCIBIOS_SUCCESSFUL; +} + +static int pci_ampci_write_config_byte(struct pci_bus *bus, unsigned int devfn, + int where, u8 value) +{ + u32 rawval; + + if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) { + __pcbdebug("<= %02x", &BRIDGEREGB(where), value); + BRIDGEREGB(where) = value; + } else { + if (bus->number == 0 && + (devfn == PCI_DEVFN(2, 0) || devfn == PCI_DEVFN(3, 0)) + ) + __pcidebug("<= %02x", bus, devfn, where, value); + CONFIG_ADDRESS = CONFIG_CMD(bus, devfn, where); + rawval = CONFIG_ADDRESS; + CONFIG_DATAB(where) = value; + } + return PCIBIOS_SUCCESSFUL; +} + +static int pci_ampci_write_config_word(struct pci_bus *bus, unsigned int devfn, + int where, u16 value) +{ + u32 rawval; + + if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) { + __pcbdebug("<= %04hx", &BRIDGEREGW(where), value); + BRIDGEREGW(where) = value; + } else { + if (__query(bus, devfn)) + __pcidebug("<= %04hx", bus, devfn, where, value); + CONFIG_ADDRESS = CONFIG_CMD(bus, devfn, where); + rawval = CONFIG_ADDRESS; + CONFIG_DATAW(where) = value; + } + return PCIBIOS_SUCCESSFUL; +} + +static int pci_ampci_write_config_dword(struct pci_bus *bus, unsigned int devfn, + int where, u32 value) +{ + u32 rawval; + + if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) { + __pcbdebug("<= %08x", &BRIDGEREGL(where), value); + BRIDGEREGL(where) = value; + } else { + if (__query(bus, devfn)) + __pcidebug("<= %08x", bus, devfn, where, value); + CONFIG_ADDRESS = CONFIG_CMD(bus, devfn, where); + rawval = CONFIG_ADDRESS; + CONFIG_DATAL(where) = value; + } + return PCIBIOS_SUCCESSFUL; +} + +static int pci_ampci_read_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + switch (size) { + case 1: + return pci_ampci_read_config_byte(bus, devfn, where, val); + case 2: + return pci_ampci_read_config_word(bus, devfn, where, val); + case 4: + return pci_ampci_read_config_dword(bus, devfn, where, val); + default: + BUG(); + return -EOPNOTSUPP; + } +} + +static int pci_ampci_write_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + switch (size) { + case 1: + return pci_ampci_write_config_byte(bus, devfn, where, val); + case 2: + return pci_ampci_write_config_word(bus, devfn, where, val); + case 4: + return pci_ampci_write_config_dword(bus, devfn, where, val); + default: + BUG(); + return -EOPNOTSUPP; + } +} + +static struct pci_ops pci_direct_ampci = { + pci_ampci_read_config, + pci_ampci_write_config, +}; + +/* + * Before we decide to use direct hardware access mechanisms, we try to do some + * trivial checks to ensure it at least _seems_ to be working -- we just test + * whether bus 00 contains a host bridge (this is similar to checking + * techniques used in XFree86, but ours should be more reliable since we + * attempt to make use of direct access hints provided by the PCI BIOS). + * + * This should be close to trivial, but it isn't, because there are buggy + * chipsets (yes, you guessed it, by Intel and Compaq) that have no class ID. + */ +static int __init pci_sanity_check(struct pci_ops *o) +{ + struct pci_bus bus; /* Fake bus and device */ + u32 x; + + bus.number = 0; + + if ((!o->read(&bus, 0, PCI_CLASS_DEVICE, 2, &x) && + (x == PCI_CLASS_BRIDGE_HOST || x == PCI_CLASS_DISPLAY_VGA)) || + (!o->read(&bus, 0, PCI_VENDOR_ID, 2, &x) && + (x == PCI_VENDOR_ID_INTEL || x == PCI_VENDOR_ID_COMPAQ))) + return 1; + + printk(KERN_ERR "PCI: Sanity check failed\n"); + return 0; +} + +static int __init pci_check_direct(void) +{ + unsigned long flags; + + local_irq_save(flags); + + /* + * Check if access works. + */ + if (pci_sanity_check(&pci_direct_ampci)) { + local_irq_restore(flags); + printk(KERN_INFO "PCI: Using configuration ampci\n"); + request_mem_region(0xBE040000, 256, "AMPCI bridge"); + request_mem_region(0xBFFFFFF4, 12, "PCI ampci"); + request_mem_region(0xBC000000, 32 * 1024 * 1024, "PCI SRAM"); + return 0; + } + + local_irq_restore(flags); + return -ENODEV; +} + +static int __devinit is_valid_resource(struct pci_dev *dev, int idx) +{ + unsigned int i, type_mask = IORESOURCE_IO | IORESOURCE_MEM; + struct resource *devr = &dev->resource[idx], *busr; + + if (dev->bus) { + pci_bus_for_each_resource(dev->bus, busr, i) { + if (!busr || (busr->flags ^ devr->flags) & type_mask) + continue; + + if (devr->start && + devr->start >= busr->start && + devr->end <= busr->end) + return 1; + } + } + + return 0; +} + +static void __devinit pcibios_fixup_device_resources(struct pci_dev *dev) +{ + struct pci_bus_region region; + int i; + int limit; + + if (dev->bus->number != 0) + return; + + limit = (dev->hdr_type == PCI_HEADER_TYPE_NORMAL) ? + PCI_BRIDGE_RESOURCES : PCI_NUM_RESOURCES; + + for (i = 0; i < limit; i++) { + if (!dev->resource[i].flags) + continue; + + region.start = dev->resource[i].start; + region.end = dev->resource[i].end; + pcibios_bus_to_resource(dev, &dev->resource[i], ®ion); + if (is_valid_resource(dev, i)) + pci_claim_resource(dev, i); + } +} + +/* + * 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; + + if (bus->number == 0) { + bus->resource[0] = &pci_ioport_resource; + bus->resource[1] = &pci_iomem_resource; + } + + if (bus->self) { + pci_read_bridge_bases(bus); + pcibios_fixup_device_resources(bus->self); + } + + list_for_each_entry(dev, &bus->devices, bus_list) + pcibios_fixup_device_resources(dev); +} + +/* + * Initialization. Try all known PCI access methods. Note that we support + * using both PCI BIOS and direct access: in such cases, we use I/O ports + * to access config space, but we still keep BIOS order of cards to be + * compatible with 2.0.X. This should go away some day. + */ +static int __init pcibios_init(void) +{ + ioport_resource.start = 0xA0000000; + ioport_resource.end = 0xDFFFFFFF; + iomem_resource.start = 0xA0000000; + iomem_resource.end = 0xDFFFFFFF; + + if (insert_resource(&iomem_resource, &pci_iomem_resource) < 0) + panic("Unable to insert PCI IOMEM resource\n"); + if (insert_resource(&ioport_resource, &pci_ioport_resource) < 0) + panic("Unable to insert PCI IOPORT resource\n"); + + if (!pci_probe) + return 0; + + if (pci_check_direct() < 0) { + printk(KERN_WARNING "PCI: No PCI bus detected\n"); + return 0; + } + + printk(KERN_INFO "PCI: Probing PCI hardware [mempage %08x]\n", + MEM_PAGING_REG); + + pci_root_bus = pci_scan_bus(0, &pci_direct_ampci, NULL); + + pcibios_irq_init(); + pcibios_fixup_irqs(); + pcibios_resource_survey(); + return 0; +} + +arch_initcall(pcibios_init); + +char *__init pcibios_setup(char *str) +{ + if (!strcmp(str, "off")) { + pci_probe = 0; + return NULL; + + } else if (!strncmp(str, "lastbus=", 8)) { + pcibios_last_bus = simple_strtol(str+8, NULL, 0); + return NULL; + } + + return str; +} + +int pcibios_enable_device(struct pci_dev *dev, int mask) +{ + int err; + + err = pci_enable_resources(dev, mask); + if (err == 0) + pcibios_enable_irq(dev); + return err; +} + +/* + * disable the ethernet chipset + */ +static void __init unit_disable_pcnet(struct pci_bus *bus, struct pci_ops *o) +{ + u32 x; + + bus->number = 0; + + o->read (bus, PCI_DEVFN(2, 0), PCI_VENDOR_ID, 4, &x); + o->read (bus, PCI_DEVFN(2, 0), PCI_COMMAND, 2, &x); + x |= PCI_COMMAND_MASTER | + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | + PCI_COMMAND_SERR | PCI_COMMAND_PARITY; + o->write(bus, PCI_DEVFN(2, 0), PCI_COMMAND, 2, x); + o->read (bus, PCI_DEVFN(2, 0), PCI_COMMAND, 2, &x); + o->write(bus, PCI_DEVFN(2, 0), PCI_BASE_ADDRESS_0, 4, 0x00030001); + o->read (bus, PCI_DEVFN(2, 0), PCI_BASE_ADDRESS_0, 4, &x); + +#define RDP (*(volatile u32 *) 0xBE030010) +#define RAP (*(volatile u32 *) 0xBE030014) +#define __set_RAP(X) do { RAP = (X); x = RAP; } while (0) +#define __set_RDP(X) do { RDP = (X); x = RDP; } while (0) +#define __get_RDP() ({ RDP & 0xffff; }) + + __set_RAP(0); + __set_RDP(0x0004); /* CSR0 = STOP */ + + __set_RAP(88); /* check CSR88 indicates an Am79C973 */ + BUG_ON(__get_RDP() != 0x5003); + + for (x = 0; x < 100; x++) + asm volatile("nop"); + + __set_RDP(0x0004); /* CSR0 = STOP */ +} + +/* + * initialise the unit hardware + */ +asmlinkage void __init unit_pci_init(void) +{ + struct pci_bus bus; /* Fake bus and device */ + struct pci_ops *o = &pci_direct_ampci; + u32 x; + + set_intr_level(XIRQ1, NUM2GxICR_LEVEL(CONFIG_PCI_IRQ_LEVEL)); + + memset(&bus, 0, sizeof(bus)); + + MEM_PAGING_REG = 0xE8000000; + + /* we need to set up the bridge _now_ or we won't be able to access the + * PCI config registers + */ + BRIDGEREGW(PCI_COMMAND) |= + PCI_COMMAND_SERR | PCI_COMMAND_PARITY | + PCI_COMMAND_MEMORY | PCI_COMMAND_IO | PCI_COMMAND_MASTER; + BRIDGEREGW(PCI_STATUS) = 0xF800; + BRIDGEREGB(PCI_LATENCY_TIMER) = 0x10; + BRIDGEREGL(PCI_BASE_ADDRESS_0) = 0x80000000; + BRIDGEREGB(PCI_INTERRUPT_LINE) = 1; + BRIDGEREGL(0x48) = 0x98000000; /* AMPCI base addr */ + BRIDGEREGB(0x41) = 0x00; /* secondary bus + * number */ + BRIDGEREGB(0x42) = 0x01; /* subordinate bus + * number */ + BRIDGEREGB(0x44) = 0x01; + BRIDGEREGL(0x50) = 0x00000001; + BRIDGEREGL(0x58) = 0x00001002; + BRIDGEREGL(0x5C) = 0x00000011; + + /* we also need to set up the PCI-PCI bridge */ + bus.number = 0; + + /* IO: 0x00000000-0x00020000 */ + o->read (&bus, PCI_DEVFN(3, 0), PCI_COMMAND, 2, &x); + x |= PCI_COMMAND_MASTER | + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | + PCI_COMMAND_SERR | PCI_COMMAND_PARITY; + o->write(&bus, PCI_DEVFN(3, 0), PCI_COMMAND, 2, x); + + o->read (&bus, PCI_DEVFN(3, 0), PCI_IO_BASE, 1, &x); + o->read (&bus, PCI_DEVFN(3, 0), PCI_IO_BASE_UPPER16, 4, &x); + o->read (&bus, PCI_DEVFN(3, 0), PCI_MEMORY_BASE, 4, &x); + o->read (&bus, PCI_DEVFN(3, 0), PCI_PREF_MEMORY_BASE, 4, &x); + + o->write(&bus, PCI_DEVFN(3, 0), PCI_IO_BASE, 1, 0x01); + o->read (&bus, PCI_DEVFN(3, 0), PCI_IO_BASE, 1, &x); + o->write(&bus, PCI_DEVFN(3, 0), PCI_IO_BASE_UPPER16, 4, 0x00020000); + o->read (&bus, PCI_DEVFN(3, 0), PCI_IO_BASE_UPPER16, 4, &x); + o->write(&bus, PCI_DEVFN(3, 0), PCI_MEMORY_BASE, 4, 0xEBB0EA00); + o->read (&bus, PCI_DEVFN(3, 0), PCI_MEMORY_BASE, 4, &x); + o->write(&bus, PCI_DEVFN(3, 0), PCI_PREF_MEMORY_BASE, 4, 0xE9F0E800); + o->read (&bus, PCI_DEVFN(3, 0), PCI_PREF_MEMORY_BASE, 4, &x); + + unit_disable_pcnet(&bus, o); +} diff --git a/arch/mn10300/unit-asb2305/unit-init.c b/arch/mn10300/unit-asb2305/unit-init.c new file mode 100644 index 00000000..e1becd6b --- /dev/null +++ b/arch/mn10300/unit-asb2305/unit-init.c @@ -0,0 +1,62 @@ +/* ASB2305 Initialisation + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <asm/io.h> +#include <asm/setup.h> +#include <asm/processor.h> +#include <asm/intctl-regs.h> +#include <asm/serial-regs.h> +#include <unit/serial.h> + +/* + * initialise some of the unit hardware before gdbstub is set up + */ +asmlinkage void __init unit_init(void) +{ +#ifndef CONFIG_GDBSTUB_ON_TTYSx + /* set the 16550 interrupt line to level 3 if not being used for GDB */ +#ifdef CONFIG_EXT_SERIAL_IRQ_LEVEL + set_intr_level(XIRQ0, NUM2GxICR_LEVEL(CONFIG_EXT_SERIAL_IRQ_LEVEL)); +#endif +#endif /* CONFIG_GDBSTUB_ON_TTYSx */ +} + +/* + * initialise the rest of the unit hardware after gdbstub is ready + */ +void __init unit_setup(void) +{ +#ifdef CONFIG_PCI + unit_pci_init(); +#endif +} + +/* + * initialise the external interrupts used by a unit of this type + */ +void __init unit_init_IRQ(void) +{ + unsigned int extnum; + + for (extnum = 0; extnum < NR_XIRQS; extnum++) { + switch (GET_XIRQ_TRIGGER(extnum)) { + case XIRQ_TRIGGER_HILEVEL: + case XIRQ_TRIGGER_LOWLEVEL: + mn10300_set_lateack_irq_type(XIRQ2IRQ(extnum)); + break; + default: + break; + } + } +} diff --git a/arch/mn10300/unit-asb2364/Makefile b/arch/mn10300/unit-asb2364/Makefile new file mode 100644 index 00000000..b3263ecf --- /dev/null +++ b/arch/mn10300/unit-asb2364/Makefile @@ -0,0 +1,12 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +obj-y := unit-init.o leds.o irq-fpga.o + +obj-$(CONFIG_SMSC911X) += smsc911x.o diff --git a/arch/mn10300/unit-asb2364/include/unit/clock.h b/arch/mn10300/unit-asb2364/include/unit/clock.h new file mode 100644 index 00000000..d34ac9a7 --- /dev/null +++ b/arch/mn10300/unit-asb2364/include/unit/clock.h @@ -0,0 +1,29 @@ +/* clock.h: unit-specific clocks + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * Modified by Matsushita Electric Industrial Co., Ltd. + * Modifications: + * 23-Feb-2007 MEI Add define for watchdog timer. + * + * 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. + */ + +#ifndef _ASM_UNIT_CLOCK_H +#define _ASM_UNIT_CLOCK_H + +#ifndef __ASSEMBLY__ + +#define MN10300_IOCLK 100000000UL /* for DDR800 */ +/*#define MN10300_IOCLK 83333333UL */ /* for DDR667 */ +#define MN10300_IOBCLK MN10300_IOCLK /* IOBCLK is equal to IOCLK */ + +#endif /* !__ASSEMBLY__ */ + +#define MN10300_WDCLK 27000000UL + +#endif /* _ASM_UNIT_CLOCK_H */ diff --git a/arch/mn10300/unit-asb2364/include/unit/fpga-regs.h b/arch/mn10300/unit-asb2364/include/unit/fpga-regs.h new file mode 100644 index 00000000..33f100f9 --- /dev/null +++ b/arch/mn10300/unit-asb2364/include/unit/fpga-regs.h @@ -0,0 +1,52 @@ +/* ASB2364 FPGA registers + */ + +#ifndef _ASM_UNIT_FPGA_REGS_H +#define _ASM_UNIT_FPGA_REGS_H + +#include <asm/cpu-regs.h> + +#ifdef __KERNEL__ + +#define ASB2364_FPGA_REG_RESET_LAN __SYSREG(0xa9001300, u16) +#define ASB2364_FPGA_REG_RESET_UART __SYSREG(0xa9001304, u16) +#define ASB2364_FPGA_REG_RESET_I2C __SYSREG(0xa9001308, u16) +#define ASB2364_FPGA_REG_RESET_USB __SYSREG(0xa900130c, u16) +#define ASB2364_FPGA_REG_RESET_AV __SYSREG(0xa9001310, u16) + +#define ASB2364_FPGA_REG_IRQ(X) __SYSREG(0xa9001510+((X)*4), u16) +#define ASB2364_FPGA_REG_IRQ_LAN ASB2364_FPGA_REG_IRQ(0) +#define ASB2364_FPGA_REG_IRQ_UART ASB2364_FPGA_REG_IRQ(1) +#define ASB2364_FPGA_REG_IRQ_I2C ASB2364_FPGA_REG_IRQ(2) +#define ASB2364_FPGA_REG_IRQ_USB ASB2364_FPGA_REG_IRQ(3) +#define ASB2364_FPGA_REG_IRQ_FPGA ASB2364_FPGA_REG_IRQ(5) + +#define ASB2364_FPGA_REG_MASK(X) __SYSREG(0xa9001590+((X)*4), u16) +#define ASB2364_FPGA_REG_MASK_LAN ASB2364_FPGA_REG_MASK(0) +#define ASB2364_FPGA_REG_MASK_UART ASB2364_FPGA_REG_MASK(1) +#define ASB2364_FPGA_REG_MASK_I2C ASB2364_FPGA_REG_MASK(2) +#define ASB2364_FPGA_REG_MASK_USB ASB2364_FPGA_REG_MASK(3) +#define ASB2364_FPGA_REG_MASK_FPGA ASB2364_FPGA_REG_MASK(5) + +#define ASB2364_FPGA_REG_CPLD5_SET1 __SYSREG(0xa9002500, u16) +#define ASB2364_FPGA_REG_CPLD5_SET2 __SYSREG(0xa9002504, u16) +#define ASB2364_FPGA_REG_CPLD6_SET1 __SYSREG(0xa9002600, u16) +#define ASB2364_FPGA_REG_CPLD6_SET2 __SYSREG(0xa9002604, u16) +#define ASB2364_FPGA_REG_CPLD7_SET1 __SYSREG(0xa9002700, u16) +#define ASB2364_FPGA_REG_CPLD7_SET2 __SYSREG(0xa9002704, u16) +#define ASB2364_FPGA_REG_CPLD8_SET1 __SYSREG(0xa9002800, u16) +#define ASB2364_FPGA_REG_CPLD8_SET2 __SYSREG(0xa9002804, u16) +#define ASB2364_FPGA_REG_CPLD9_SET1 __SYSREG(0xa9002900, u16) +#define ASB2364_FPGA_REG_CPLD9_SET2 __SYSREG(0xa9002904, u16) +#define ASB2364_FPGA_REG_CPLD10_SET1 __SYSREG(0xa9002a00, u16) +#define ASB2364_FPGA_REG_CPLD10_SET2 __SYSREG(0xa9002a04, u16) + +#define SyncExBus() \ + do { \ + unsigned short w; \ + w = *(volatile short *)0xa9000000; \ + } while (0) + +#endif /* __KERNEL__ */ + +#endif /* _ASM_UNIT_FPGA_REGS_H */ diff --git a/arch/mn10300/unit-asb2364/include/unit/irq.h b/arch/mn10300/unit-asb2364/include/unit/irq.h new file mode 100644 index 00000000..786148e4 --- /dev/null +++ b/arch/mn10300/unit-asb2364/include/unit/irq.h @@ -0,0 +1,35 @@ +/* ASB2364 FPGA irq numbers + * + * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _UNIT_IRQ_H +#define _UNIT_IRQ_H + +#ifndef __ASSEMBLY__ + +#ifdef CONFIG_SMP +#define NR_CPU_IRQS GxICR_NUM_EXT_IRQS +#else +#define NR_CPU_IRQS GxICR_NUM_IRQS +#endif + +enum { + FPGA_LAN_IRQ = NR_CPU_IRQS, + FPGA_UART_IRQ, + FPGA_I2C_IRQ, + FPGA_USB_IRQ, + FPGA_RESERVED_IRQ, + FPGA_FPGA_IRQ, + NR_IRQS +}; + +extern void __init irq_fpga_init(void); + +#endif /* !__ASSEMBLY__ */ +#endif /* _UNIT_IRQ_H */ diff --git a/arch/mn10300/unit-asb2364/include/unit/leds.h b/arch/mn10300/unit-asb2364/include/unit/leds.h new file mode 100644 index 00000000..03a3933a --- /dev/null +++ b/arch/mn10300/unit-asb2364/include/unit/leds.h @@ -0,0 +1,54 @@ +/* Unit-specific leds + * + * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _ASM_UNIT_LEDS_H +#define _ASM_UNIT_LEDS_H + +#include <asm/pio-regs.h> +#include <asm/cpu-regs.h> +#include <asm/exceptions.h> + +#define MN10300_USE_7SEGLEDS 0 + +#define ASB2364_7SEGLEDS __SYSREG(0xA9001630, u32) + +/* + * use the 7-segment LEDs to indicate states + */ + +#if MN10300_USE_7SEGLEDS +/* flip the 7-segment LEDs between "Gdb-" and "----" */ +#define mn10300_set_gdbleds(ONOFF) \ + do { \ + ASB2364_7SEGLEDS = (ONOFF) ? 0x8543077f : 0x7f7f7f7f; \ + } while (0) +#else +#define mn10300_set_gdbleds(ONOFF) do {} while (0) +#endif + +#if MN10300_USE_7SEGLEDS +/* indicate double-fault by displaying "db-f" on the LEDs */ +#define mn10300_set_dbfleds \ + mov 0x43077f1d,d0 ; \ + mov d0,(ASB2364_7SEGLEDS) +#else +#define mn10300_set_dbfleds +#endif + +#ifndef __ASSEMBLY__ +extern void peripheral_leds_display_exception(enum exception_code); +extern void peripheral_leds_led_chase(void); +extern void peripheral_leds7x4_display_dec(unsigned int, unsigned int); +extern void peripheral_leds7x4_display_hex(unsigned int, unsigned int); +extern void debug_to_serial(const char *, int); +#endif /* __ASSEMBLY__ */ + +#endif /* _ASM_UNIT_LEDS_H */ diff --git a/arch/mn10300/unit-asb2364/include/unit/serial.h b/arch/mn10300/unit-asb2364/include/unit/serial.h new file mode 100644 index 00000000..92f224a9 --- /dev/null +++ b/arch/mn10300/unit-asb2364/include/unit/serial.h @@ -0,0 +1,151 @@ +/* Unit-specific 8250 serial ports + * + * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _ASM_UNIT_SERIAL_H +#define _ASM_UNIT_SERIAL_H + +#include <asm/cpu-regs.h> +#include <proc/irq.h> +#include <unit/fpga-regs.h> +#include <linux/serial_reg.h> + +#define SERIAL_PORT0_BASE_ADDRESS 0xA8200000 + +#define SERIAL_IRQ XIRQ1 /* single serial (TL16C550C) (Lo) */ + +/* + * The ASB2364 has an 12.288 MHz clock + * for your UART. + * + * It'd be nice if someone built a serial card with a 24.576 MHz + * clock, since the 16550A is capable of handling a top speed of 1.5 + * megabits/second; but this requires the faster clock. + */ +#define BASE_BAUD (12288000 / 16) + +/* + * dispose of the /dev/ttyS0 and /dev/ttyS1 serial ports + */ +#ifndef CONFIG_GDBSTUB_ON_TTYSx + +#define SERIAL_PORT_DFNS \ + { \ + .baud_base = BASE_BAUD, \ + .irq = SERIAL_IRQ, \ + .flags = STD_COM_FLAGS, \ + .iomem_base = (u8 *) SERIAL_PORT0_BASE_ADDRESS, \ + .iomem_reg_shift = 1, \ + .io_type = SERIAL_IO_MEM, \ + }, + +#ifndef __ASSEMBLY__ + +static inline void __debug_to_serial(const char *p, int n) +{ +} + +#endif /* !__ASSEMBLY__ */ + +#else /* CONFIG_GDBSTUB_ON_TTYSx */ + +#define SERIAL_PORT_DFNS /* stolen by gdb-stub */ + +#if defined(CONFIG_GDBSTUB_ON_TTYS0) +#define GDBPORT_SERIAL_RX __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_RX * 2, u8) +#define GDBPORT_SERIAL_TX __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_TX * 2, u8) +#define GDBPORT_SERIAL_DLL __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_DLL * 2, u8) +#define GDBPORT_SERIAL_DLM __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_DLM * 2, u8) +#define GDBPORT_SERIAL_IER __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_IER * 2, u8) +#define GDBPORT_SERIAL_IIR __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_IIR * 2, u8) +#define GDBPORT_SERIAL_FCR __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_FCR * 2, u8) +#define GDBPORT_SERIAL_LCR __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_LCR * 2, u8) +#define GDBPORT_SERIAL_MCR __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_MCR * 2, u8) +#define GDBPORT_SERIAL_LSR __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_LSR * 2, u8) +#define GDBPORT_SERIAL_MSR __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_MSR * 2, u8) +#define GDBPORT_SERIAL_SCR __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_SCR * 2, u8) +#define GDBPORT_SERIAL_IRQ SERIAL_IRQ + +#elif defined(CONFIG_GDBSTUB_ON_TTYS1) +#error The ASB2364 does not have a /dev/ttyS1 +#endif + +#ifndef __ASSEMBLY__ + +static inline void __debug_to_serial(const char *p, int n) +{ + char ch; + +#define LSR_WAIT_FOR(STATE) \ + do {} while (!(GDBPORT_SERIAL_LSR & UART_LSR_##STATE)) +#define FLOWCTL_QUERY(LINE) \ + ({ GDBPORT_SERIAL_MSR & UART_MSR_##LINE; }) +#define FLOWCTL_WAIT_FOR(LINE) \ + do {} while (!(GDBPORT_SERIAL_MSR & UART_MSR_##LINE)) +#define FLOWCTL_CLEAR(LINE) \ + do { GDBPORT_SERIAL_MCR &= ~UART_MCR_##LINE; } while (0) +#define FLOWCTL_SET(LINE) \ + do { GDBPORT_SERIAL_MCR |= UART_MCR_##LINE; } while (0) + + FLOWCTL_SET(DTR); + + for (; n > 0; n--) { + LSR_WAIT_FOR(THRE); + FLOWCTL_WAIT_FOR(CTS); + + ch = *p++; + if (ch == 0x0a) { + GDBPORT_SERIAL_TX = 0x0d; + LSR_WAIT_FOR(THRE); + FLOWCTL_WAIT_FOR(CTS); + } + GDBPORT_SERIAL_TX = ch; + } + + FLOWCTL_CLEAR(DTR); +} + +#endif /* !__ASSEMBLY__ */ + +#endif /* CONFIG_GDBSTUB_ON_TTYSx */ + +#define SERIAL_INITIALIZE \ +do { \ + /* release reset */ \ + ASB2364_FPGA_REG_RESET_UART = 0x0001; \ + SyncExBus(); \ +} while (0) + +#define SERIAL_CHECK_INTERRUPT \ +do { \ + if ((ASB2364_FPGA_REG_IRQ_UART & 0x0001) == 0x0001) { \ + return IRQ_NONE; \ + } \ +} while (0) + +#define SERIAL_CLEAR_INTERRUPT \ +do { \ + ASB2364_FPGA_REG_IRQ_UART = 0x0001; \ + SyncExBus(); \ +} while (0) + +#define SERIAL_SET_INT_MASK \ +do { \ + ASB2364_FPGA_REG_MASK_UART = 0x0001; \ + SyncExBus(); \ +} while (0) + +#define SERIAL_CLEAR_INT_MASK \ +do { \ + ASB2364_FPGA_REG_MASK_UART = 0x0000; \ + SyncExBus(); \ +} while (0) + +#endif /* _ASM_UNIT_SERIAL_H */ diff --git a/arch/mn10300/unit-asb2364/include/unit/smsc911x.h b/arch/mn10300/unit-asb2364/include/unit/smsc911x.h new file mode 100644 index 00000000..4c1ede53 --- /dev/null +++ b/arch/mn10300/unit-asb2364/include/unit/smsc911x.h @@ -0,0 +1,171 @@ +/* Support for the SMSC911x NIC + * + * Copyright (C) 2006 Matsushita Electric Industrial Co., Ltd. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef _ASM_UNIT_SMSC911X_H +#define _ASM_UNIT_SMSC911X_H + +#include <linux/netdevice.h> +#include <proc/irq.h> +#include <unit/fpga-regs.h> + +#define MN10300_USE_EXT_EEPROM + + +#define SMSC911X_BASE 0xA8000000UL +#define SMSC911X_BASE_END 0xA8000100UL +#define SMSC911X_IRQ FPGA_LAN_IRQ + +/* + * Allow the FPGA to be initialised by the SMSC911x driver + */ +#undef SMSC_INITIALIZE +#define SMSC_INITIALIZE() \ +do { \ + /* release reset */ \ + ASB2364_FPGA_REG_RESET_LAN = 0x0001; \ + SyncExBus(); \ +} while (0) + +#ifdef MN10300_USE_EXT_EEPROM +#include <linux/delay.h> +#include <unit/clock.h> + +#define EEPROM_ADDRESS 0xA0 +#define MAC_OFFSET 0x0008 +#define USE_IIC_CH 0 /* 0 or 1 */ +#define IIC_OFFSET (0x80000 * USE_IIC_CH) +#define IIC_DTRM __SYSREG(0xd8400000 + IIC_OFFSET, u32) +#define IIC_DREC __SYSREG(0xd8400004 + IIC_OFFSET, u32) +#define IIC_MYADD __SYSREG(0xd8400008 + IIC_OFFSET, u32) +#define IIC_CLK __SYSREG(0xd840000c + IIC_OFFSET, u32) +#define IIC_BRST __SYSREG(0xd8400010 + IIC_OFFSET, u32) +#define IIC_HOLD __SYSREG(0xd8400014 + IIC_OFFSET, u32) +#define IIC_BSTS __SYSREG(0xd8400018 + IIC_OFFSET, u32) +#define IIC_ICR __SYSREG(0xd4000080 + 4 * USE_IIC_CH, u16) + +#define IIC_CLK_PLS ((unsigned short)(MN10300_IOCLK / 100000 - 1)) +#define IIC_CLK_LOW ((unsigned short)(IIC_CLK_PLS / 2)) + +#define SYS_IIC_DTRM_Bit_STA ((unsigned short)0x0400) +#define SYS_IIC_DTRM_Bit_STO ((unsigned short)0x0200) +#define SYS_IIC_DTRM_Bit_ACK ((unsigned short)0x0100) +#define SYS_IIC_DTRM_Bit_DATA ((unsigned short)0x00FF) + +static inline void POLL_INT_REQ(volatile u16 *icr) +{ + unsigned long flags; + u16 tmp; + + while (!(*icr & GxICR_REQUEST)) + ; + flags = arch_local_cli_save(); + tmp = *icr; + *icr = (tmp & GxICR_LEVEL) | GxICR_DETECT; + tmp = *icr; + arch_local_irq_restore(flags); +} + +/* + * Implement the SMSC911x hook for MAC address retrieval + */ +#undef smsc_get_mac +static inline int smsc_get_mac(struct net_device *dev) +{ + unsigned char *mac_buf = dev->dev_addr; + int i; + unsigned short value; + unsigned int data; + int mac_length = 6; + int check; + u16 orig_gicr, tmp; + unsigned long flags; + + /* save original GnICR and clear GnICR.IE */ + flags = arch_local_cli_save(); + orig_gicr = IIC_ICR; + IIC_ICR = orig_gicr & GxICR_LEVEL; + tmp = IIC_ICR; + arch_local_irq_restore(flags); + + IIC_MYADD = 0x00000008; + IIC_CLK = (IIC_CLK_LOW << 16) + (IIC_CLK_PLS); + /* bus hung recovery */ + + while (1) { + check = 0; + for (i = 0; i < 3; i++) { + if ((IIC_BSTS & 0x00000003) == 0x00000003) + check++; + udelay(3); + } + + if (check == 3) { + IIC_BRST = 0x00000003; + break; + } else { + for (i = 0; i < 3; i++) { + IIC_BRST = 0x00000002; + udelay(8); + IIC_BRST = 0x00000003; + udelay(8); + } + } + } + + IIC_BRST = 0x00000002; + IIC_BRST = 0x00000003; + + value = SYS_IIC_DTRM_Bit_STA | SYS_IIC_DTRM_Bit_ACK; + value |= (((unsigned short)EEPROM_ADDRESS & SYS_IIC_DTRM_Bit_DATA) | + (unsigned short)0x0000); + IIC_DTRM = value; + POLL_INT_REQ(&IIC_ICR); + + /** send offset of MAC address in EEPROM **/ + IIC_DTRM = (unsigned char)((MAC_OFFSET & 0xFF00) >> 8); + POLL_INT_REQ(&IIC_ICR); + + IIC_DTRM = (unsigned char)(MAC_OFFSET & 0x00FF); + POLL_INT_REQ(&IIC_ICR); + + udelay(1000); + + value = SYS_IIC_DTRM_Bit_STA; + value |= (((unsigned short)EEPROM_ADDRESS & SYS_IIC_DTRM_Bit_DATA) | + (unsigned short)0x0001); + IIC_DTRM = value; + POLL_INT_REQ(&IIC_ICR); + + IIC_DTRM = 0x00000000; + while (mac_length > 0) { + POLL_INT_REQ(&IIC_ICR); + + data = IIC_DREC; + mac_length--; + if (mac_length == 0) + value = 0x00000300; /* stop IIC bus */ + else if (mac_length == 1) + value = 0x00000100; /* no ack */ + else + value = 0x00000000; /* ack */ + IIC_DTRM = value; + *mac_buf++ = (unsigned char)(data & 0xff); + } + + /* restore GnICR.LV and GnICR.IE */ + flags = arch_local_cli_save(); + IIC_ICR = (orig_gicr & (GxICR_LEVEL | GxICR_ENABLE)); + tmp = IIC_ICR; + arch_local_irq_restore(flags); + + return 0; +} +#endif /* MN10300_USE_EXT_EEPROM */ +#endif /* _ASM_UNIT_SMSC911X_H */ diff --git a/arch/mn10300/unit-asb2364/include/unit/timex.h b/arch/mn10300/unit-asb2364/include/unit/timex.h new file mode 100644 index 00000000..ddb7ed01 --- /dev/null +++ b/arch/mn10300/unit-asb2364/include/unit/timex.h @@ -0,0 +1,159 @@ +/* timex.h: MN2WS0038 architecture timer specifications + * + * Copyright (C) 2002, 2010 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef _ASM_UNIT_TIMEX_H +#define _ASM_UNIT_TIMEX_H + +#ifndef __ASSEMBLY__ +#include <linux/irq.h> +#endif /* __ASSEMBLY__ */ + +#include <asm/timer-regs.h> +#include <unit/clock.h> +#include <asm/param.h> + +/* + * jiffies counter specifications + */ + +#define TMJCBR_MAX 0xffffff /* 24bit */ +#define TMJCIRQ TMTIRQ + +#ifndef __ASSEMBLY__ + +#define MN10300_SRC_IOBCLK MN10300_IOBCLK + +#ifndef HZ +# error HZ undeclared. +#endif /* !HZ */ + +#define MN10300_JCCLK (MN10300_SRC_IOBCLK) +#define MN10300_TSCCLK (MN10300_SRC_IOBCLK) + +#define MN10300_JC_PER_HZ ((MN10300_JCCLK + HZ / 2) / HZ) +#define MN10300_TSC_PER_HZ ((MN10300_TSCCLK + HZ / 2) / HZ) + +/* Check bit width of MTM interval value that sets base register */ +#if (MN10300_JC_PER_HZ - 1) > TMJCBR_MAX +# error MTM tick timer interval value is overflow. +#endif + +static inline void stop_jiffies_counter(void) +{ + u16 tmp; + TMTMD = 0; + tmp = TMTMD; +} + +static inline void reload_jiffies_counter(u32 cnt) +{ + u32 tmp; + + TMTBR = cnt; + tmp = TMTBR; + + TMTMD = TMTMD_TMTLDE; + TMTMD = TMTMD_TMTCNE; + tmp = TMTMD; +} + +#if defined(CONFIG_SMP) && defined(CONFIG_GENERIC_CLOCKEVENTS) && \ + !defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) +/* + * If we aren't using broadcasting, each core needs its own event timer. + * Since CPU0 uses the tick timer which is 24-bits, we use timer 4 & 5 + * cascaded to 32-bits for CPU1 (but only really use 24-bits to match + * CPU0). + */ + +#define TMJC1IRQ TM5IRQ + +static inline void stop_jiffies_counter1(void) +{ + u8 tmp; + TM4MD = 0; + TM5MD = 0; + tmp = TM4MD; + tmp = TM5MD; +} + +static inline void reload_jiffies_counter1(u32 cnt) +{ + u32 tmp; + + TM45BR = cnt; + tmp = TM45BR; + + TM4MD = TM4MD_INIT_COUNTER; + tmp = TM4MD; + + TM5MD = TM5MD_SRC_TM4CASCADE | TM5MD_INIT_COUNTER; + TM5MD = TM5MD_SRC_TM4CASCADE | TM5MD_COUNT_ENABLE; + tmp = TM5MD; + + TM4MD = TM4MD_COUNT_ENABLE; + tmp = TM4MD; +} +#endif /* CONFIG_SMP&GENERIC_CLOCKEVENTS&!GENERIC_CLOCKEVENTS_BROADCAST */ + +#endif /* !__ASSEMBLY__ */ + + +/* + * timestamp counter specifications + */ +#define TMTSCBR_MAX 0xffffffff + +#ifndef __ASSEMBLY__ + +/* Use 32-bit timestamp counter */ +#define TMTSCMD TMSMD +#define TMTSCBR TMSBR +#define TMTSCBC TMSBC +#define TMTSCICR TMSICR + +static inline void startup_timestamp_counter(void) +{ + u32 sync; + + /* set up TMS(Timestamp) 32bit timer register to count real time + * - count down from 4Gig-1 to 0 and wrap at IOBCLK rate + */ + + TMTSCBR = TMTSCBR_MAX; + sync = TMTSCBR; + + TMTSCICR = 0; + sync = TMTSCICR; + + TMTSCMD = TMTMD_TMTLDE; + TMTSCMD = TMTMD_TMTCNE; + sync = TMTSCMD; +} + +static inline void shutdown_timestamp_counter(void) +{ + TMTSCMD = 0; +} + +/* + * we use a cascaded pair of 16-bit down-counting timers to count I/O + * clock cycles for the purposes of time keeping + */ +typedef unsigned long cycles_t; + +static inline cycles_t read_timestamp_counter(void) +{ + return (cycles_t)~TMTSCBC; +} + +#endif /* !__ASSEMBLY__ */ + +#endif /* _ASM_UNIT_TIMEX_H */ diff --git a/arch/mn10300/unit-asb2364/irq-fpga.c b/arch/mn10300/unit-asb2364/irq-fpga.c new file mode 100644 index 00000000..e16c216f --- /dev/null +++ b/arch/mn10300/unit-asb2364/irq-fpga.c @@ -0,0 +1,108 @@ +/* ASB2364 FPGA interrupt multiplexing + * + * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <unit/fpga-regs.h> + +/* + * FPGA PIC operations + */ +static void asb2364_fpga_mask(struct irq_data *d) +{ + ASB2364_FPGA_REG_MASK(d->irq - NR_CPU_IRQS) = 0x0001; + SyncExBus(); +} + +static void asb2364_fpga_ack(struct irq_data *d) +{ + ASB2364_FPGA_REG_IRQ(d->irq - NR_CPU_IRQS) = 0x0001; + SyncExBus(); +} + +static void asb2364_fpga_mask_ack(struct irq_data *d) +{ + ASB2364_FPGA_REG_MASK(d->irq - NR_CPU_IRQS) = 0x0001; + SyncExBus(); + ASB2364_FPGA_REG_IRQ(d->irq - NR_CPU_IRQS) = 0x0001; + SyncExBus(); +} + +static void asb2364_fpga_unmask(struct irq_data *d) +{ + ASB2364_FPGA_REG_MASK(d->irq - NR_CPU_IRQS) = 0x0000; + SyncExBus(); +} + +static struct irq_chip asb2364_fpga_pic = { + .name = "fpga", + .irq_ack = asb2364_fpga_ack, + .irq_mask = asb2364_fpga_mask, + .irq_mask_ack = asb2364_fpga_mask_ack, + .irq_unmask = asb2364_fpga_unmask, +}; + +/* + * FPGA PIC interrupt handler + */ +static irqreturn_t fpga_interrupt(int irq, void *_mask) +{ + if ((ASB2364_FPGA_REG_IRQ_LAN & 0x0001) != 0x0001) + generic_handle_irq(FPGA_LAN_IRQ); + if ((ASB2364_FPGA_REG_IRQ_UART & 0x0001) != 0x0001) + generic_handle_irq(FPGA_UART_IRQ); + if ((ASB2364_FPGA_REG_IRQ_I2C & 0x0001) != 0x0001) + generic_handle_irq(FPGA_I2C_IRQ); + if ((ASB2364_FPGA_REG_IRQ_USB & 0x0001) != 0x0001) + generic_handle_irq(FPGA_USB_IRQ); + if ((ASB2364_FPGA_REG_IRQ_FPGA & 0x0001) != 0x0001) + generic_handle_irq(FPGA_FPGA_IRQ); + + return IRQ_HANDLED; +} + +/* + * Define an interrupt action for each FPGA PIC output + */ +static struct irqaction fpga_irq[] = { + [0] = { + .handler = fpga_interrupt, + .flags = IRQF_DISABLED | IRQF_SHARED, + .name = "fpga", + }, +}; + +/* + * Initialise the FPGA's PIC + */ +void __init irq_fpga_init(void) +{ + int irq; + + ASB2364_FPGA_REG_MASK_LAN = 0x0001; + SyncExBus(); + ASB2364_FPGA_REG_MASK_UART = 0x0001; + SyncExBus(); + ASB2364_FPGA_REG_MASK_I2C = 0x0001; + SyncExBus(); + ASB2364_FPGA_REG_MASK_USB = 0x0001; + SyncExBus(); + ASB2364_FPGA_REG_MASK_FPGA = 0x0001; + SyncExBus(); + + for (irq = NR_CPU_IRQS; irq < NR_IRQS; irq++) + irq_set_chip_and_handler(irq, &asb2364_fpga_pic, + handle_level_irq); + + /* the FPGA drives the XIRQ1 input on the CPU PIC */ + setup_irq(XIRQ1, &fpga_irq[0]); +} diff --git a/arch/mn10300/unit-asb2364/leds.c b/arch/mn10300/unit-asb2364/leds.c new file mode 100644 index 00000000..1ff830c3 --- /dev/null +++ b/arch/mn10300/unit-asb2364/leds.c @@ -0,0 +1,98 @@ +/* leds.c: ASB2364 peripheral 7seg LEDs x4 support + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/init.h> + +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/intctl-regs.h> +#include <asm/rtc-regs.h> +#include <unit/leds.h> + +#if MN10300_USE_7SEGLEDS +static const u8 asb2364_led_hex_tbl[16] = { + 0x80, 0xf2, 0x48, 0x60, 0x32, 0x24, 0x04, 0xf0, + 0x00, 0x20, 0x10, 0x06, 0x8c, 0x42, 0x0c, 0x1c +}; + +static const u32 asb2364_led_chase_tbl[6] = { + ~0x02020202, /* top - segA */ + ~0x04040404, /* right top - segB */ + ~0x08080808, /* right bottom - segC */ + ~0x10101010, /* bottom - segD */ + ~0x20202020, /* left bottom - segE */ + ~0x40404040, /* left top - segF */ +}; + +static unsigned asb2364_led_chase; + +void peripheral_leds7x4_display_dec(unsigned int val, unsigned int points) +{ + u32 leds; + + leds = asb2364_led_hex_tbl[(val/1000) % 10]; + leds <<= 8; + leds |= asb2364_led_hex_tbl[(val/100) % 10]; + leds <<= 8; + leds |= asb2364_led_hex_tbl[(val/10) % 10]; + leds <<= 8; + leds |= asb2364_led_hex_tbl[val % 10]; + leds |= points^0x01010101; + + ASB2364_7SEGLEDS = leds; +} + +void peripheral_leds7x4_display_hex(unsigned int val, unsigned int points) +{ + u32 leds; + + leds = asb2364_led_hex_tbl[(val/1000) % 10]; + leds <<= 8; + leds |= asb2364_led_hex_tbl[(val/100) % 10]; + leds <<= 8; + leds |= asb2364_led_hex_tbl[(val/10) % 10]; + leds <<= 8; + leds |= asb2364_led_hex_tbl[val % 10]; + leds |= points^0x01010101; + + ASB2364_7SEGLEDS = leds; +} + +/* display triple horizontal bar and exception code */ +void peripheral_leds_display_exception(enum exception_code code) +{ + u32 leds; + + leds = asb2364_led_hex_tbl[(code/0x100) % 0x10]; + leds <<= 8; + leds |= asb2364_led_hex_tbl[(code/0x10) % 0x10]; + leds <<= 8; + leds |= asb2364_led_hex_tbl[code % 0x10]; + leds |= 0x6d010101; + + ASB2364_7SEGLEDS = leds; +} + +void peripheral_leds_led_chase(void) +{ + ASB2364_7SEGLEDS = asb2364_led_chase_tbl[asb2364_led_chase]; + asb2364_led_chase++; + if (asb2364_led_chase >= 6) + asb2364_led_chase = 0; +} +#else /* MN10300_USE_7SEGLEDS */ +void peripheral_leds7x4_display_dec(unsigned int val, unsigned int points) { } +void peripheral_leds7x4_display_hex(unsigned int val, unsigned int points) { } +void peripheral_leds_display_exception(enum exception_code code) { } +void peripheral_leds_led_chase(void) { } +#endif /* MN10300_USE_7SEGLEDS */ diff --git a/arch/mn10300/unit-asb2364/smsc911x.c b/arch/mn10300/unit-asb2364/smsc911x.c new file mode 100644 index 00000000..544a73e9 --- /dev/null +++ b/arch/mn10300/unit-asb2364/smsc911x.c @@ -0,0 +1,58 @@ +/* Specification for the SMSC911x NIC + * + * Copyright (C) 2006 Matsushita Electric Industrial Co., Ltd. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/smsc911x.h> +#include <unit/smsc911x.h> + +static struct smsc911x_platform_config smsc911x_config = { + .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, + .irq_type = SMSC911X_IRQ_TYPE_OPEN_DRAIN, + .flags = SMSC911X_USE_32BIT, +}; + +static struct resource smsc911x_resources[] = { + [0] = { + .start = SMSC911X_BASE, + .end = SMSC911X_BASE_END, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = SMSC911X_IRQ, + .end = SMSC911X_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device smsc911x_device = { + .name = "smsc911x", + .id = 0, + .num_resources = ARRAY_SIZE(smsc911x_resources), + .resource = smsc911x_resources, + .dev = { + .platform_data = &smsc911x_config, + } +}; + +/* + * add platform devices + */ +static int __init unit_device_init(void) +{ + platform_device_register(&smsc911x_device); + return 0; +} + +device_initcall(unit_device_init); diff --git a/arch/mn10300/unit-asb2364/unit-init.c b/arch/mn10300/unit-asb2364/unit-init.c new file mode 100644 index 00000000..6359b41c --- /dev/null +++ b/arch/mn10300/unit-asb2364/unit-init.c @@ -0,0 +1,132 @@ +/* ASB2364 initialisation + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/delay.h> + +#include <asm/io.h> +#include <asm/setup.h> +#include <asm/processor.h> +#include <asm/irq.h> +#include <asm/intctl-regs.h> +#include <asm/serial-regs.h> +#include <unit/fpga-regs.h> +#include <unit/serial.h> +#include <unit/smsc911x.h> + +#define TTYS0_SERIAL_IER __SYSREG(SERIAL_PORT0_BASE_ADDRESS + UART_IER * 2, u8) +#define LAN_IRQ_CFG __SYSREG(SMSC911X_BASE + 0x54, u32) +#define LAN_INT_EN __SYSREG(SMSC911X_BASE + 0x5c, u32) + +/* + * initialise some of the unit hardware before gdbstub is set up + */ +asmlinkage void __init unit_init(void) +{ + /* Make sure we aren't going to get unexpected interrupts */ + TTYS0_SERIAL_IER = 0; + SC0RXICR = 0; + SC0TXICR = 0; + SC1RXICR = 0; + SC1TXICR = 0; + SC2RXICR = 0; + SC2TXICR = 0; + + /* Attempt to reset the FPGA attached peripherals */ + ASB2364_FPGA_REG_RESET_LAN = 0x0000; + SyncExBus(); + ASB2364_FPGA_REG_RESET_UART = 0x0000; + SyncExBus(); + ASB2364_FPGA_REG_RESET_I2C = 0x0000; + SyncExBus(); + ASB2364_FPGA_REG_RESET_USB = 0x0000; + SyncExBus(); + ASB2364_FPGA_REG_RESET_AV = 0x0000; + SyncExBus(); + + /* set up the external interrupts */ + + /* XIRQ[0]: NAND RXBY */ + /* SET_XIRQ_TRIGGER(0, XIRQ_TRIGGER_LOWLEVEL); */ + + /* XIRQ[1]: LAN, UART, I2C, USB, PCI, FPGA */ + SET_XIRQ_TRIGGER(1, XIRQ_TRIGGER_LOWLEVEL); + + /* XIRQ[2]: Extend Slot 1-9 */ + /* SET_XIRQ_TRIGGER(2, XIRQ_TRIGGER_LOWLEVEL); */ + +#if defined(CONFIG_EXT_SERIAL_IRQ_LEVEL) && \ + defined(CONFIG_ETHERNET_IRQ_LEVEL) && \ + (CONFIG_EXT_SERIAL_IRQ_LEVEL != CONFIG_ETHERNET_IRQ_LEVEL) +# error CONFIG_EXT_SERIAL_IRQ_LEVEL != CONFIG_ETHERNET_IRQ_LEVEL +#endif + +#if defined(CONFIG_EXT_SERIAL_IRQ_LEVEL) + set_intr_level(XIRQ1, NUM2GxICR_LEVEL(CONFIG_EXT_SERIAL_IRQ_LEVEL)); +#elif defined(CONFIG_ETHERNET_IRQ_LEVEL) + set_intr_level(XIRQ1, NUM2GxICR_LEVEL(CONFIG_ETHERNET_IRQ_LEVEL)); +#endif +} + +/* + * initialise the rest of the unit hardware after gdbstub is ready + */ +asmlinkage void __init unit_setup(void) +{ + /* Release the reset on the SMSC911X so that it is ready by the time we + * need it */ + ASB2364_FPGA_REG_RESET_LAN = 0x0001; + SyncExBus(); + ASB2364_FPGA_REG_RESET_UART = 0x0001; + SyncExBus(); + ASB2364_FPGA_REG_RESET_I2C = 0x0001; + SyncExBus(); + ASB2364_FPGA_REG_RESET_USB = 0x0001; + SyncExBus(); + ASB2364_FPGA_REG_RESET_AV = 0x0001; + SyncExBus(); + + /* Make sure the ethernet chipset isn't going to give us an interrupt + * storm from stuff it was doing pre-reset */ + LAN_IRQ_CFG = 0; + LAN_INT_EN = 0; +} + +/* + * initialise the external interrupts used by a unit of this type + */ +void __init unit_init_IRQ(void) +{ + unsigned int extnum; + + for (extnum = 0 ; extnum < NR_XIRQS ; extnum++) { + switch (GET_XIRQ_TRIGGER(extnum)) { + /* LEVEL triggered interrupts should be made + * post-ACK'able as they hold their lines until + * serviced + */ + case XIRQ_TRIGGER_HILEVEL: + case XIRQ_TRIGGER_LOWLEVEL: + mn10300_set_lateack_irq_type(XIRQ2IRQ(extnum)); + break; + default: + break; + } + } + +#define IRQCTL __SYSREG(0xd5000090, u32) + IRQCTL |= 0x02; + + irq_fpga_init(); +} |