summaryrefslogtreecommitdiffstats
path: root/arch/cris
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2001-03-09 20:33:35 +0000
committerRalf Baechle <ralf@linux-mips.org>2001-03-09 20:33:35 +0000
commit116674acc97ba75a720329996877077d988443a2 (patch)
tree6a3f2ff0b612ae2ee8a3f3509370c9e6333a53b3 /arch/cris
parent71118c319fcae4a138f16e35b4f7e0a6d53ce2ca (diff)
Merge with Linux 2.4.2.
Diffstat (limited to 'arch/cris')
-rw-r--r--arch/cris/Makefile96
-rw-r--r--arch/cris/README.mm241
-rw-r--r--arch/cris/boot/Makefile14
-rw-r--r--arch/cris/boot/compressed/Makefile37
-rw-r--r--arch/cris/boot/compressed/README25
-rw-r--r--arch/cris/boot/compressed/decompress.ld26
-rw-r--r--arch/cris/boot/compressed/head.S100
-rw-r--r--arch/cris/boot/compressed/misc.c260
-rw-r--r--arch/cris/config.in223
-rw-r--r--arch/cris/cris.ld81
-rw-r--r--arch/cris/defconfig319
-rw-r--r--arch/cris/drivers/Config.in49
-rw-r--r--arch/cris/drivers/Makefile14
-rw-r--r--arch/cris/drivers/axisflashmap.c311
-rw-r--r--arch/cris/drivers/ethernet.c1041
-rw-r--r--arch/cris/drivers/ide.c818
-rw-r--r--arch/cris/drivers/serial.c3039
-rw-r--r--arch/cris/drivers/serial.h106
-rw-r--r--arch/cris/kernel/Makefile25
-rw-r--r--arch/cris/kernel/debugport.c242
-rw-r--r--arch/cris/kernel/entry.S738
-rw-r--r--arch/cris/kernel/head.S519
-rw-r--r--arch/cris/kernel/hexify.c31
-rw-r--r--arch/cris/kernel/irq.c467
-rw-r--r--arch/cris/kernel/kgdb.c1540
-rw-r--r--arch/cris/kernel/ksyms.c2
-rw-r--r--arch/cris/kernel/process.c327
-rw-r--r--arch/cris/kernel/ptrace.c340
-rw-r--r--arch/cris/kernel/semaphore.c238
-rw-r--r--arch/cris/kernel/setup.c264
-rw-r--r--arch/cris/kernel/shadows.c20
-rw-r--r--arch/cris/kernel/signal.c667
-rw-r--r--arch/cris/kernel/sys_cris.c201
-rw-r--r--arch/cris/kernel/time.c453
-rw-r--r--arch/cris/kernel/traps.c167
-rw-r--r--arch/cris/lib/Makefile11
-rw-r--r--arch/cris/lib/checksum.S113
-rw-r--r--arch/cris/lib/checksumcopy.S120
-rw-r--r--arch/cris/lib/dmacopy.c43
-rw-r--r--arch/cris/lib/memset.c245
-rw-r--r--arch/cris/lib/old_checksum.c127
-rw-r--r--arch/cris/lib/string.c223
-rw-r--r--arch/cris/lib/usercopy.c501
-rw-r--r--arch/cris/mm/Makefile13
-rw-r--r--arch/cris/mm/extable.c55
-rw-r--r--arch/cris/mm/fault.c390
-rw-r--r--arch/cris/mm/init.c506
-rw-r--r--arch/cris/mm/tlb.c301
48 files changed, 15689 insertions, 0 deletions
diff --git a/arch/cris/Makefile b/arch/cris/Makefile
new file mode 100644
index 000000000..b63a44711
--- /dev/null
+++ b/arch/cris/Makefile
@@ -0,0 +1,96 @@
+# $Id: Makefile,v 1.11 2000/11/27 17:58:30 bjornw Exp $
+# cris/Makefile
+#
+# This file is included by the global makefile so that you can add your own
+# architecture-specific flags and dependencies. Remember to do have actions
+# for "archclean" and "archdep" for cleaning up and making dependencies for
+# this architecture
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License. See the file "COPYING" in the main directory of this archive
+# for more details.
+
+LD_SCRIPT=$(TOPDIR)/arch/cris/cris.ld
+
+# A bug in ld prevents us from having a (constant-value) symbol in a
+# "ORIGIN =" or "LENGTH =" expression. We fix that by generating a
+# linker file with the symbolic part of those expressions evaluated.
+# Unfortunately, there is trouble making vmlinux depend on anything we
+# generate here, so we *always* regenerate the final linker script and
+# replace the LD macro to get what we want. Thankfully(?) vmlinux is
+# always rebuilt (due to calling make recursively and not knowing if
+# anything was rebuilt).
+# The shell script to build in some kind of dependency is really not
+# necessary for reasons of speed. It's there because always
+# regenerating stuff (even for incremental linking of subsystems!) is
+# even more nauseating.
+LD = if [ ! -e $(LD_SCRIPT).tmp -o $(LD_SCRIPT) -nt $(LD_SCRIPT).tmp ]; then \
+ sed -e s/@ETRAX_DRAM_BASE@/0x$(ETRAX_DRAM_BASE)/ \
+ -e s/@ETRAX_DRAM_SIZE_M@/$(ETRAX_DRAM_SIZE)/ \
+ < $(LD_SCRIPT) > $(LD_SCRIPT).tmp; \
+ else true; \
+ fi && $(CROSS_COMPILE)ld -mcriself
+
+LINKFLAGS =-qmagic -mcriself -T $(LD_SCRIPT).tmp
+
+# objcopy is used to make binary images from the resulting linked file
+
+OBJCOPY := $(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S
+
+# normally, gcc on a linux box adds __linux__ but we do it "manually"
+# gcc-cris defaults to a.out, we need ELF, so -melf
+
+CFLAGS := $(CFLAGS) -march=v10 -fno-strict-aliasing -pipe -D__linux__
+
+ifdef CONFIG_KGDB
+CFLAGS := $(subst -fomit-frame-pointer,,$(CFLAGS)) -g
+CFLAGS += -fno-omit-frame-pointer
+endif
+
+HEAD := arch/cris/kernel/head.o
+
+SUBDIRS += arch/cris/kernel arch/cris/mm arch/cris/lib arch/cris/drivers
+CORE_FILES += arch/cris/kernel/kernel.o arch/cris/mm/mm.o arch/cris/drivers/drivers.o
+LIBGCC = $(shell $(CC) $(CFLAGS) -print-file-name=libgcc.a)
+LIBS := $(TOPDIR)/arch/cris/lib/lib.a $(LIBS) $(TOPDIR)/arch/cris/lib/lib.a $(LIBGCC)
+
+arch/cris/kernel: dummy
+ $(MAKE) linuxsubdirs SUBDIRS=arch/cris/kernel
+
+arch/cris/mm: dummy
+ $(MAKE) linuxsubdirs SUBDIRS=arch/cris/mm
+
+MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
+
+vmlinux.bin: vmlinux
+ $(OBJCOPY) vmlinux vmlinux.bin
+
+timage: vmlinux.bin
+ cat vmlinux.bin cramfs.img >timage
+
+simimage: timage
+ cp vmlinux.bin simvmlinux.bin
+
+# the following will remake timage without compiling the kernel
+# it does of course require that all object files exist...
+
+cramfs:
+## cramfs - Creates a cramfs image
+ mkcramfs -p 8192 root cramfs.img
+ cat vmlinux.bin cramfs.img >timage
+
+zImage: vmlinux
+## zImage - Compressed kernel (gzip)
+ @$(MAKEBOOT) zImage
+
+compressed: zImage
+
+archclean:
+ @$(MAKEBOOT) clean
+ rm -f timage vmlinux.bin cramfs.img
+ rm -rf $(LD_SCRIPT).tmp
+
+archmrproper:
+
+archdep:
+ @$(MAKEBOOT) dep
diff --git a/arch/cris/README.mm b/arch/cris/README.mm
new file mode 100644
index 000000000..6de4e7077
--- /dev/null
+++ b/arch/cris/README.mm
@@ -0,0 +1,241 @@
+Memory management for CRIS/MMU
+------------------------------
+HISTORY:
+
+$Log: README.mm,v $
+Revision 1.1 2000/07/10 16:25:21 bjornw
+Initial revision
+
+Revision 1.4 2000/01/17 02:31:59 bjornw
+Added discussion of paging and VM.
+
+Revision 1.3 1999/12/03 16:43:23 hp
+Blurb about that the 3.5G-limitation is not a MMU limitation
+
+Revision 1.2 1999/12/03 16:04:21 hp
+Picky comment about not mapping the first page
+
+Revision 1.1 1999/12/03 15:41:30 bjornw
+First version of CRIS/MMU memory layout specification.
+
+
+
+
+
+------------------------------
+
+See the ETRAX-NG HSDD for reference.
+
+We use the page-size of 8 kbytes, as opposed to the i386 page-size of 4 kbytes.
+
+The MMU can, apart from the normal mapping of pages, also do a top-level
+segmentation of the kernel memory space. We use this feature to avoid having
+to use page-tables to map the physical memory into the kernel's address
+space. We also use it to keep the user-mode virtual mapping in the same
+map during kernel-mode, so that the kernel easily can access the corresponding
+user-mode process' data.
+
+As a comparision, the Linux/i386 2.0 puts the kernel and physical RAM at
+address 0, overlapping with the user-mode virtual space, so that descriptor
+registers are needed for each memory access to specify which MMU space to
+map through. That changed in 2.2, putting the kernel/physical RAM at
+0xc0000000, to co-exist with the user-mode mapping. We will do something
+quite similar, but with the additional complexity of having to map the
+internal chip I/O registers and the flash memory area (including SRAM
+and peripherial chip-selets).
+
+The kernel-mode segmentation map:
+
+ ------------------------ ------------------------
+FFFFFFFF| | => cached | |
+ | kernel seg_f | flash | |
+F0000000|______________________| | |
+EFFFFFFF| | => uncached | |
+ | kernel seg_e | flash | |
+E0000000|______________________| | DRAM |
+DFFFFFFF| | paged to any | Un-cached |
+ | kernel seg_d | =======> | |
+D0000000|______________________| | |
+CFFFFFFF| | | |
+ | kernel seg_c |==\ | |
+C0000000|______________________| \ |______________________|
+BFFFFFFF| | uncached | |
+ | kernel seg_b |=====\=========>| Registers |
+B0000000|______________________| \c |______________________|
+AFFFFFFF| | \a | |
+ | | \c | FLASH/SRAM/Peripheral|
+ | | \h |______________________|
+ | | \e | |
+ | | \d | |
+ | kernel seg_0 - seg_a | \==>| DRAM |
+ | | | Cached |
+ | | paged to any | |
+ | | =======> |______________________|
+ | | | |
+ | | | Illegal |
+ | | |______________________|
+ | | | |
+ | | | FLASH/SRAM/Peripheral|
+00000000|______________________| |______________________|
+
+In user-mode it looks the same except that only the space 0-AFFFFFFF is
+available. Therefore, in this model, the virtual address space per process
+is limited to 0xb0000000 bytes (minus 8192 bytes, since the first page,
+0..8191, is never mapped, in order to trap NULL references).
+
+It also means that the total physical RAM that can be mapped is 256 MB
+(kseg_c above). More RAM can be mapped by choosing a different segmentation
+and shrinking the user-mode memory space.
+
+The MMU can map all 4 GB in user mode, but doing that would mean that a
+few extra instructions would be needed for each access to user mode
+memory.
+
+The kernel needs access to both cached and uncached flash. Uncached is
+necessary because of the special write/erase sequences. Also, the
+peripherial chip-selects are decoded from that region.
+
+The kernel also needs its own virtual memory space. That is kseg_d. It
+is used by the vmalloc() kernel function to allocate virtual contiguous
+chunks of memory not possible using the normal kmalloc physical RAM
+allocator.
+
+The setting of the actual MMU control registers to use this layout would
+be something like this:
+
+R_MMU_KSEG = ( ( seg_f, seg ) | // Flash cached
+ ( seg_e, seg ) | // Flash uncached
+ ( seg_d, page ) | // kernel vmalloc area
+ ( seg_c, seg ) | // kernel linear segment
+ ( seg_b, seg ) | // kernel linear segment
+ ( seg_a, page ) |
+ ( seg_9, page ) |
+ ( seg_8, page ) |
+ ( seg_7, page ) |
+ ( seg_6, page ) |
+ ( seg_5, page ) |
+ ( seg_4, page ) |
+ ( seg_3, page ) |
+ ( seg_2, page ) |
+ ( seg_1, page ) |
+ ( seg_0, page ) );
+
+R_MMU_KBASE_HI = ( ( base_f, 0x0 ) | // flash/sram/periph cached
+ ( base_e, 0x8 ) | // flash/sram/periph uncached
+ ( base_d, 0x0 ) | // don't care
+ ( base_c, 0x4 ) | // physical RAM cached area
+ ( base_b, 0xb ) | // uncached on-chip registers
+ ( base_a, 0x0 ) | // don't care
+ ( base_9, 0x0 ) | // don't care
+ ( base_8, 0x0 ) ); // don't care
+
+R_MMU_KBASE_LO = ( ( base_7, 0x0 ) | // don't care
+ ( base_6, 0x0 ) | // don't care
+ ( base_5, 0x0 ) | // don't care
+ ( base_4, 0x0 ) | // don't care
+ ( base_3, 0x0 ) | // don't care
+ ( base_2, 0x0 ) | // don't care
+ ( base_1, 0x0 ) | // don't care
+ ( base_0, 0x0 ) ); // don't care
+
+NOTE: while setting up the MMU, we run in a non-mapped mode in the DRAM (0x40
+segment) and need to setup the seg_4 to a unity mapping, so that we don't get
+a fault before we have had time to jump into the real kernel segment (0xc0). This
+is done in head.S temporarily, but fixed by the kernel later in paging_init.
+
+
+Paging - PTE's, PMD's and PGD's
+-------------------------------
+
+[ References: asm/pgtable.h, asm/page.h, asm/mmu.h ]
+
+The paging mechanism uses virtual addresses to split a process memory-space into
+pages, a page being the smallest unit that can be freely remapped in memory. On
+Linux/CRIS, a page is 8192 bytes (for technical reasons not equal to 4096 as in
+most other 32-bit architectures). It would be inefficient to let a virtual memory
+mapping be controlled by a long table of page mappings, so it is broken down into
+a 2-level structure with a Page Directory containing pointers to Page Tables which
+each have maps of up to 2048 pages (8192 / sizeof(void *)). Linux can actually
+handle 3-level structures as well, with a Page Middle Directory in between, but
+in many cases, this is folded into a two-level structure by excluding the Middle
+Directory.
+
+We'll take a look at how an address is translated while we discuss how it's handled
+in the Linux kernel.
+
+The example address is 0xd004000c; in binary this is:
+
+31 23 15 7 0
+11010000 00000100 00000000 00001100
+
+|______| |__________||____________|
+ PGD PTE page offset
+
+Given the top-level Page Directory, the offset in that directory is calculated
+using the upper 8 bits:
+
+extern inline pgd_t * pgd_offset(struct mm_struct * mm, unsigned long address)
+{
+ return mm->pgd + (address >> PGDIR_SHIFT);
+}
+
+PGDIR_SHIFT is the log2 of the amount of memory an entry in the PGD can map; in our
+case it is 24, corresponding to 16 MB. This means that each entry in the PGD
+corresponds to 16 MB of virtual memory.
+
+The pgd_t from our example will therefore be the 208'th (0xd0) entry in mm->pgd.
+
+Since the Middle Directory does not exist, it is a unity mapping:
+
+extern inline pmd_t * pmd_offset(pgd_t * dir, unsigned long address)
+{
+ return (pmd_t *) dir;
+}
+
+The Page Table provides the final lookup by using bits 13 to 23 as index:
+
+extern inline pte_t * pte_offset(pmd_t * dir, unsigned long address)
+{
+ return (pte_t *) pmd_page(*dir) + ((address >> PAGE_SHIFT) &
+ (PTRS_PER_PTE - 1));
+}
+
+PAGE_SHIFT is the log2 of the size of a page; 13 in our case. PTRS_PER_PTE is
+the number of pointers that fit in a Page Table and is used to mask off the
+PGD-part of the address.
+
+The so-far unused bits 0 to 12 are used to index inside a page linearily.
+
+The VM system
+-------------
+
+The kernels own page-directory is the swapper_pg_dir, cleared in paging_init,
+and contains the kernels virtual mappings (the kernel itself is not paged - it
+is mapped linearily using kseg_c as described above). Architectures without
+kernel segments like the i386, need to setup swapper_pg_dir directly in head.S
+to map the kernel itself. swapper_pg_dir is pointed to by init_mm.pgd as the
+init-task's PGD.
+
+To see what support functions are used to setup a page-table, let's look at the
+kernel's internal paged memory system, vmalloc/vfree.
+
+void * vmalloc(unsigned long size)
+
+The vmalloc-system keeps a paged segment in kernel-space at 0xd0000000. What
+happens first is that a virtual address chunk is allocated to the request using
+get_vm_area(size). After that, physical RAM pages are allocated and put into
+the kernel's page-table using alloc_area_pages(addr, size).
+
+static int alloc_area_pages(unsigned long address, unsigned long size)
+
+First the PGD entry is found using init_mm.pgd. This is passed to
+alloc_area_pmd (remember the 3->2 folding). It uses pte_alloc_kernel to
+check if the PGD entry points anywhere - if not, a page table page is
+allocated and the PGD entry updated. Then the alloc_area_pte function is
+used just like alloc_area_pmd to check which page table entry is desired,
+and a physical page is allocated and the table entry updated. All of this
+is repeated at the top-level until the entire address range specified has
+been mapped.
+
+
+
diff --git a/arch/cris/boot/Makefile b/arch/cris/boot/Makefile
new file mode 100644
index 000000000..77b59012e
--- /dev/null
+++ b/arch/cris/boot/Makefile
@@ -0,0 +1,14 @@
+#
+# arch/cris/boot/Makefile
+#
+
+zImage: compressed/vmlinuz
+
+compressed/vmlinuz: $(TOPDIR)/vmlinux
+ @$(MAKE) -C compressed vmlinuz
+
+dep:
+
+clean:
+ rm -f zImage tools/build compressed/vmlinux.out
+ @$(MAKE) -C compressed clean
diff --git a/arch/cris/boot/compressed/Makefile b/arch/cris/boot/compressed/Makefile
new file mode 100644
index 000000000..fbf2bc36b
--- /dev/null
+++ b/arch/cris/boot/compressed/Makefile
@@ -0,0 +1,37 @@
+#
+# linux/arch/etrax100/boot/compressed/Makefile
+#
+# create a compressed vmlinux image from the original vmlinux files and romfs
+#
+
+CC = gcc-cris -melf -I $(TOPDIR)/include
+CFLAGS = -O2
+LD = ld-cris
+OBJCOPY = objcopy-cris
+
+OBJECTS = head.o misc.o
+
+# files to compress
+SYSTEM = $(TOPDIR)/vmlinux.bin
+
+all: vmlinuz
+
+vmlinuz: piggy.img $(OBJECTS)
+ $(LD) -mcriself -T decompress.ld -o decompress.o $(OBJECTS)
+ $(OBJCOPY) -O binary --remove-section=.bss decompress.o decompress.bin
+# save it for mkprod in the topdir.
+ cp decompress.bin $(TOPDIR)
+ cat decompress.bin piggy.img $(TOPDIR)/cramfs.img > vmlinuz
+ rm -f piggy.img
+
+head.o: head.S
+ $(CC) -D__ASSEMBLY__ -traditional -c head.S -o head.o
+
+# gzip the kernel image
+
+piggy.img: $(SYSTEM)
+ cat $(SYSTEM) | gzip -f -9 > piggy.img
+
+clean:
+ rm -f piggy.img vmlinuz vmlinuz.o
+
diff --git a/arch/cris/boot/compressed/README b/arch/cris/boot/compressed/README
new file mode 100644
index 000000000..83e1f1e51
--- /dev/null
+++ b/arch/cris/boot/compressed/README
@@ -0,0 +1,25 @@
+Creation of the self-extracting compressed kernel image (vmlinuz)
+-----------------------------------------------------------------
+$Id: README,v 1.1 2000/11/22 17:20:46 bjornw Exp $
+
+This can be slightly confusing because it's a process with many steps.
+
+The kernel object built by the arch/etrax100/Makefile, vmlinux, is split
+by that makefile into text and data binary files, vmlinux.text and
+vmlinux.data.
+
+Those files together with a ROM filesystem can be catted together and
+burned into a flash or executed directly at the DRAM origin.
+
+They can also be catted together and compressed with gzip, which is what
+happens in this makefile. Together they make up piggy.img.
+
+The decompressor is built into the file decompress.o. It is turned into
+the binary file decompress.bin, which is catted together with piggy.img
+into the file vmlinuz. It can be executed in an arbitrary place in flash.
+
+Be careful - it assumes some things about free locations in DRAM. It
+assumes the DRAM starts at 0x40000000 and that it is at least 8 MB,
+so it puts its code at 0x40700000, and initial stack at 0x40800000.
+
+-Bjorn
diff --git a/arch/cris/boot/compressed/decompress.ld b/arch/cris/boot/compressed/decompress.ld
new file mode 100644
index 000000000..8cdef6a60
--- /dev/null
+++ b/arch/cris/boot/compressed/decompress.ld
@@ -0,0 +1,26 @@
+MEMORY
+ {
+ dram : ORIGIN = 0x40700000,
+ LENGTH = 0x00100000
+ }
+
+SECTIONS
+{
+ .text :
+ {
+ _stext = . ;
+ *(.text)
+ *(.rodata)
+ _etext = . ;
+ } > dram
+ .data :
+ {
+ *(.data)
+ _edata = . ;
+ } > dram
+ .bss :
+ {
+ *(.bss)
+ _end = ALIGN( 0x10 ) ;
+ } > dram
+}
diff --git a/arch/cris/boot/compressed/head.S b/arch/cris/boot/compressed/head.S
new file mode 100644
index 000000000..97243ee13
--- /dev/null
+++ b/arch/cris/boot/compressed/head.S
@@ -0,0 +1,100 @@
+/*
+ * arch/etrax100/boot/compressed/head.S
+ *
+ * Copyright (C) 1999 Axis Communications AB
+ *
+ * Code that sets up the DRAM registers, calls the
+ * decompressor to unpack the piggybacked kernel, and jumps.
+ *
+ */
+
+#include <linux/config.h>
+#define ASSEMBLER_MACROS_ONLY
+#include <asm/sv_addr_ag.h>
+
+ ;; Exported symbols
+
+ .globl _input_data
+
+
+ .text
+
+ nop
+ di
+
+#ifndef CONFIG_SVINTO_SIM
+
+ ;; We need to setup the bus registers before we start using the DRAM
+
+ move.d DEF_R_WAITSTATES, r0
+ move.d r0, [R_WAITSTATES]
+
+ move.d DEF_R_BUS_CONFIG, r0
+ move.d r0, [R_BUS_CONFIG]
+
+ move.d DEF_R_DRAM_CONFIG, r0
+ move.d r0, [R_DRAM_CONFIG]
+
+ move.d DEF_R_DRAM_TIMING, r0
+ move.d r0, [R_DRAM_TIMING]
+
+#endif
+
+ ;; Setup the stack to a suitably high address.
+ ;; We assume 8 MB is the minimum DRAM in an eLinux
+ ;; product and put the sp at the top for now.
+
+ move.d 0x40800000, sp
+
+ ;; Figure out where the compressed piggyback image is
+ ;; in the flash (since we wont try to copy it to DRAM
+ ;; before unpacking). It is at _edata, but in flash.
+ ;; Use (_edata - basse) as offset to the current PC.
+
+basse: move.d pc, r5
+ and.d 0x7fffffff, r5 ; strip any non-cache bit
+ subq 2, r5 ; compensate for the move.d pc instr
+ move.d r5, r0 ; save for later - flash address of 'basse'
+ add.d _edata, r5
+ sub.d basse, r5 ; r5 = flash address of '_edata'
+
+ ;; Copy text+data to DRAM
+
+ move.d basse, r1 ; destination
+ move.d _edata, r2 ; end destination
+1: move.w [r0+], r3
+ move.w r3, [r1+]
+ cmp.d r2, r1
+ bcs 1b
+ nop
+
+ move.d r5, [_input_data] ; for the decompressor
+
+ ;; Clear the decompressors BSS (between _edata and _end)
+
+ moveq 0, r0
+ move.d _edata, r1
+ move.d _end, r2
+1: move.w r0, [r1+]
+ cmp.d r2, r1
+ bcs 1b
+ nop
+
+ ;; Do the decompression and save compressed size in _inptr
+
+ jsr _decompress_kernel
+
+ ;; Put start address of cramfs in r9 so the kernel can use it
+ ;; when mounting from flash
+
+ move.d [_input_data], r9 ; flash address of compressed kernel
+ add.d [_inptr], r9 ; size of compressed kernel
+
+ ;; Enter the decompressed kernel
+
+ jump 0x40004000 ; kernel is linked to this address
+
+ .data
+
+_input_data:
+ .dword 0 ; used by the decompressor
diff --git a/arch/cris/boot/compressed/misc.c b/arch/cris/boot/compressed/misc.c
new file mode 100644
index 000000000..87ce3d8a0
--- /dev/null
+++ b/arch/cris/boot/compressed/misc.c
@@ -0,0 +1,260 @@
+/*
+ * misc.c
+ *
+ * $Id: misc.c,v 1.3 2001/01/17 15:54:18 jonashg Exp $
+ *
+ * This is a collection of several routines from gzip-1.0.3
+ * adapted for Linux.
+ *
+ * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994
+ * puts by Nick Holloway 1993, better puts by Martin Mares 1995
+ * adoptation for Linux/CRIS Axis Communications AB, 1999
+ *
+ */
+
+/* where the piggybacked kernel image expects itself to live.
+ * it is the same address we use when we network load an uncompressed
+ * image into DRAM, and it is the address the kernel is linked to live
+ * at by etrax100.ld.
+ */
+
+#define KERNEL_LOAD_ADR 0x40004000
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <asm/svinto.h>
+
+/*
+ * gzip declarations
+ */
+
+#define OF(args) args
+#define STATIC static
+
+void* memset(void* s, int c, size_t n);
+void* memcpy(void* __dest, __const void* __src,
+ size_t __n);
+
+#define memzero(s, n) memset ((s), 0, (n))
+
+
+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 */
+
+unsigned inptr = 0; /* index of next byte to be processed in inbuf
+ * After decompression it will contain the
+ * compressed size, and head.S will read it.
+ */
+
+static unsigned outcnt = 0; /* 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 */
+
+#define get_byte() inbuf[inptr++]
+
+/* 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(char *m);
+static void gzip_mark(void **);
+static void gzip_release(void **);
+
+extern char *input_data; /* lives in head.S */
+
+static long bytes_out = 0;
+static uch *output_data;
+static unsigned long output_ptr = 0;
+
+static void *malloc(int size);
+static void free(void *where);
+static void error(char *m);
+static void gzip_mark(void **);
+static void gzip_release(void **);
+
+static void puts(const char *);
+
+/* the "heap" is put directly after the BSS ends, at end */
+
+extern int end;
+static long free_mem_ptr = (long)&end;
+
+#include "../../../../lib/inflate.c"
+
+static void *malloc(int size)
+{
+ void *p;
+
+ if (size <0) error("Malloc error\n");
+
+ free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */
+
+ p = (void *)free_mem_ptr;
+ free_mem_ptr += size;
+
+ return p;
+}
+
+static void free(void *where)
+{ /* Don't care */
+}
+
+static void gzip_mark(void **ptr)
+{
+ *ptr = (void *) free_mem_ptr;
+}
+
+static void gzip_release(void **ptr)
+{
+ free_mem_ptr = (long) *ptr;
+}
+
+/* decompressor info and error messages to serial console */
+
+static void
+puts(const char *s)
+{
+#ifndef CONFIG_DEBUG_PORT_NULL
+ while(*s) {
+#ifdef CONFIG_DEBUG_PORT0
+ while(!(*R_SERIAL0_STATUS & (1 << 5))) ;
+ *R_SERIAL0_TR_DATA = *s++;
+#endif
+#ifdef CONFIG_DEBUG_PORT1
+ while(!(*R_SERIAL1_STATUS & (1 << 5))) ;
+ *R_SERIAL1_TR_DATA = *s++;
+#endif
+#ifdef CONFIG_DEBUG_PORT2
+ while(!(*R_SERIAL2_STATUS & (1 << 5))) ;
+ *R_SERIAL2_TR_DATA = *s++;
+#endif
+#ifdef CONFIG_DEBUG_PORT3
+ while(!(*R_SERIAL3_STATUS & (1 << 5))) ;
+ *R_SERIAL3_TR_DATA = *s++;
+#endif
+ }
+#endif
+}
+
+void*
+memset(void* s, int c, size_t n)
+{
+ int i;
+ char *ss = (char*)s;
+
+ for (i=0;i<n;i++) ss[i] = c;
+}
+
+void*
+memcpy(void* __dest, __const void* __src,
+ size_t __n)
+{
+ int i;
+ char *d = (char *)__dest, *s = (char *)__src;
+
+ for (i=0;i<__n;i++) d[i] = s[i];
+}
+
+/* ===========================================================================
+ * Write the output window window[0..outcnt-1] and update crc and bytes_out.
+ * (Used for the decompressed data only.)
+ */
+
+static void
+flush_window()
+{
+ 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
+error(char *x)
+{
+ puts("\n\n");
+ puts(x);
+ puts("\n\n -- System halted\n");
+
+ while(1); /* Halt */
+}
+
+void
+setup_normal_output_buffer()
+{
+ output_data = (char *)KERNEL_LOAD_ADR;
+}
+
+void
+decompress_kernel()
+{
+ /* input_data is set in head.S */
+ inbuf = input_data;
+
+#ifdef CONFIG_DEBUG_PORT0
+ *R_SERIAL0_XOFF = 0;
+ *R_SERIAL0_BAUD = 0x99;
+ *R_SERIAL0_TR_CTRL = 0x40;
+#endif
+#ifdef CONFIG_DEBUG_PORT1
+ *R_SERIAL1_XOFF = 0;
+ *R_SERIAL1_BAUD = 0x99;
+ *R_SERIAL1_TR_CTRL = 0x40;
+#endif
+#ifdef CONFIG_DEBUG_PORT2
+ *R_SERIAL2_XOFF = 0;
+ *R_SERIAL2_BAUD = 0x99;
+ *R_SERIAL2_TR_CTRL = 0x40;
+#endif
+#ifdef CONFIG_DEBUG_PORT3
+ *R_SERIAL3_XOFF = 0;
+ *R_SERIAL3_BAUD = 0x99;
+ *R_SERIAL3_TR_CTRL = 0x40;
+#endif
+
+ setup_normal_output_buffer();
+
+ makecrc();
+ puts("Uncompressing Linux...\n");
+ gunzip();
+ puts("Done. Now booting the kernel.\n");
+}
diff --git a/arch/cris/config.in b/arch/cris/config.in
new file mode 100644
index 000000000..3e234ec43
--- /dev/null
+++ b/arch/cris/config.in
@@ -0,0 +1,223 @@
+#
+# For a description of the syntax of this configuration file,
+# see the Configure script.
+#
+mainmenu_name "Linux/CRIS Kernel Configuration"
+
+define_bool CONFIG_UID16 y
+
+mainmenu_option next_comment
+comment 'Code maturity level options'
+bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL
+endmenu
+
+mainmenu_option next_comment
+comment 'General setup'
+
+bool 'Networking support' CONFIG_NET
+bool 'System V IPC' CONFIG_SYSVIPC
+
+tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ tristate 'Kernel support for JAVA binaries' CONFIG_BINFMT_JAVA
+fi
+
+bool 'Use kernel gdb debugger' CONFIG_KGDB
+
+bool 'Enable Etrax100 watchdog' CONFIG_ETRAX_WATCHDOG
+
+bool 'Use serial console (on the debug port)' CONFIG_USE_SERIAL_CONSOLE
+
+bool 'Use in-kernel ifconfig/route setup' CONFIG_KERNEL_IFCONFIG
+
+endmenu
+
+mainmenu_option next_comment
+comment 'Hardware setup'
+
+choice 'Processor type' \
+ "Etrax-100-LX CONFIG_ETRAX100LX \
+ Etrax-100-LX-for-xsim-simulator CONFIG_SVINTO_SIM" Etrax-100-LX
+
+# For both LX version 1 and the current simulator we enable the low VM mapping
+# Later when LX version 2 and above exist, this should be done with an if
+
+define_bool CONFIG_CRIS_LOW_MAP y
+
+hex 'DRAM base (hex)' ETRAX_DRAM_BASE 40000000
+int 'DRAM size (dec, in MB)' ETRAX_DRAM_SIZE 8
+
+int 'Max possible flash size (dec, in MB)' CONFIG_ETRAX_FLASH_LENGTH 2
+int 'Buswidth of flash in bytes' CONFIG_ETRAX_FLASH_BUSWIDTH 2
+
+choice 'Product LED port' \
+ "Port-PA-LEDs CONFIG_ETRAX_PA_LEDS \
+ Port-PB-LEDs CONFIG_ETRAX_PB_LEDS \
+ Mem-0x90000000-LEDs CONFIG_ETRAX_90000000_LEDS \
+ None CONFIG_ETRAX_NO_LEDS" Port-PA-LEDs
+
+if [ "$CONFIG_ETRAX_NO_LEDS" != "y" ]; then
+ int ' First green LED bit' CONFIG_ETRAX_LED1G 2
+ int ' First red LED bit' CONFIG_ETRAX_LED1R 3
+ int ' Second green LED bit' CONFIG_ETRAX_LED2G 4
+ int ' Second red LED bit' CONFIG_ETRAX_LED2R 5
+ int ' Third green LED bit' CONFIG_ETRAX_LED3R 2
+ int ' Third red LED bit' CONFIG_ETRAX_LED3G 2
+fi
+
+choice 'Product debug-port' \
+ "Serial-0 CONFIG_DEBUG_PORT0 \
+ Serial-1 CONFIG_DEBUG_PORT1 \
+ Serial-2 CONFIG_DEBUG_PORT2 \
+ Serial-3 CONFIG_DEBUG_PORT3" Serial-0
+
+hex 'R_WAITSTATES' DEF_R_WAITSTATES 95a6
+hex 'R_BUS_CONFIG' DEF_R_BUS_CONFIG 104
+hex 'R_DRAM_CONFIG' DEF_R_DRAM_CONFIG 1a200040
+hex 'R_DRAM_TIMING' DEF_R_DRAM_TIMING 5611
+hex 'R_PORT_PA_DIR' DEF_R_PORT_PA_DIR 1c
+hex 'R_PORT_PA_DATA' DEF_R_PORT_PA_DATA 00
+hex 'R_PORT_PB_CONFIG' DEF_R_PORT_PB_CONFIG 00
+hex 'R_PORT_PB_DIR' DEF_R_PORT_PB_DIR 00
+hex 'R_PORT_PB_DATA' DEF_R_PORT_PB_DATA ff
+
+endmenu
+
+# only configure IP numbers if the kernel ifconfig/route setup is enabled
+
+if [ "$CONFIG_KERNEL_IFCONFIG" = "y" ]; then
+ mainmenu_option next_comment
+ comment 'IP address selection'
+
+ comment 'All addresses are in hexadecimal form without 0x prefix'
+
+ hex 'IP address' ELTEST_IPADR ab1005af
+ hex 'Network' ELTEST_NETWORK ab100000
+ hex 'Netmask' ELTEST_NETMASK ffff0000
+ hex 'Broadcast' ELTEST_BROADCAST ab10ffff
+ hex 'Gateway' ELTEST_GATEWAY ab100101
+ hwaddr 'Ethernet address' ELTEST_ETHADR 00408ccd0000
+
+ endmenu
+fi
+
+# bring in Etrax built-in drivers
+
+source arch/cris/drivers/Config.in
+
+# standard linux drivers
+
+source drivers/mtd/Config.in
+
+source drivers/parport/Config.in
+
+source drivers/pnp/Config.in
+
+source drivers/block/Config.in
+
+source drivers/md/Config.in
+
+if [ "$CONFIG_NET" = "y" ]; then
+ source net/Config.in
+fi
+
+source drivers/telephony/Config.in
+
+mainmenu_option next_comment
+comment 'ATA/IDE/MFM/RLL support'
+
+tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE
+
+if [ "$CONFIG_IDE" != "n" ]; then
+ source drivers/ide/Config.in
+else
+ define_bool CONFIG_BLK_DEV_IDE_MODES n
+ define_bool CONFIG_BLK_DEV_HD n
+fi
+endmenu
+
+mainmenu_option next_comment
+comment 'SCSI support'
+
+tristate 'SCSI support' CONFIG_SCSI
+
+if [ "$CONFIG_SCSI" != "n" ]; then
+ source drivers/scsi/Config.in
+fi
+endmenu
+
+source drivers/ieee1394/Config.in
+
+source drivers/i2o/Config.in
+
+if [ "$CONFIG_NET" = "y" ]; then
+ mainmenu_option next_comment
+ comment 'Network device support'
+
+ bool 'Network device support' CONFIG_NETDEVICES
+ if [ "$CONFIG_NETDEVICES" = "y" ]; then
+ source drivers/net/Config.in
+ if [ "$CONFIG_ATM" = "y" ]; then
+ source drivers/atm/Config.in
+ fi
+ fi
+ endmenu
+fi
+
+source net/ax25/Config.in
+
+source net/irda/Config.in
+
+mainmenu_option next_comment
+comment 'ISDN subsystem'
+if [ "$CONFIG_NET" != "n" ]; then
+ tristate 'ISDN support' CONFIG_ISDN
+ if [ "$CONFIG_ISDN" != "n" ]; then
+ source drivers/isdn/Config.in
+ fi
+fi
+endmenu
+
+mainmenu_option next_comment
+comment 'Old CD-ROM drivers (not SCSI, not IDE)'
+
+bool 'Support non-SCSI/IDE/ATAPI CDROM drives' CONFIG_CD_NO_IDESCSI
+if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then
+ source drivers/cdrom/Config.in
+fi
+endmenu
+
+#
+# input before char - char/joystick depends on it. As does USB.
+#
+source drivers/input/Config.in
+source drivers/char/Config.in
+
+#source drivers/misc/Config.in
+
+source drivers/media/Config.in
+
+source fs/Config.in
+
+source drivers/char/Config.in
+
+mainmenu_option next_comment
+comment 'Sound'
+
+tristate 'Sound card support' CONFIG_SOUND
+if [ "$CONFIG_SOUND" != "n" ]; then
+ source drivers/sound/Config.in
+fi
+endmenu
+
+source drivers/usb/Config.in
+
+mainmenu_option next_comment
+comment 'Kernel hacking'
+
+#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC
+bool 'Kernel profiling support' CONFIG_PROFILE
+if [ "$CONFIG_PROFILE" = "y" ]; then
+ int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
+fi
+endmenu
diff --git a/arch/cris/cris.ld b/arch/cris/cris.ld
new file mode 100644
index 000000000..5ba215959
--- /dev/null
+++ b/arch/cris/cris.ld
@@ -0,0 +1,81 @@
+/* ld script to make the Linux/CRIS kernel
+ * Authors: Bjorn Wesen (bjornw@axis.com)
+ *
+ * For now, on Etrax-100 LX, the DRAM starts virtually at 0x6. Normally
+ * it should be at 0xc.
+ */
+
+SECTIONS
+{
+ . = 0x60000000; /* DRAM starts virtually at 0x60000000 */
+ _dram_start = .;
+ _ibr_start = .;
+ . = . + 0x4000; /* see head.S and pages reserved at the start */
+
+ _text = .; /* Text and read-only data */
+ _text_start = .; /* lots of aliases */
+ _stext = .;
+ __stext = .;
+ .text : {
+ *(.text)
+ *(.fixup)
+ *(.text.__*)
+ *(.rodata)
+ }
+
+ . = ALIGN(4); /* Exception table */
+ __start___ex_table = .;
+ __ex_table : { *(__ex_table) }
+ __stop___ex_table = .;
+
+ _etext = . ; /* End of text section */
+ __etext = .;
+
+ . = ALIGN (4);
+ ___data_rom_start = . ;
+ ___data_start = . ;
+ __Sdata = . ;
+ .data : { /* Data */
+ *(.data)
+ }
+ __edata = . ; /* End of data section */
+ _edata = . ;
+
+ . = ALIGN(8192); /* init_task and stack, must be aligned */
+ .data.init_task : { *(.data.init_task) }
+
+ . = ALIGN(8192); /* Init code and data */
+ ___init_begin = .;
+ .text.init : { *(.text.init) }
+ .data.init : { *(.data.init) }
+ . = ALIGN(16);
+ ___setup_start = .;
+ .setup.init : { *(.setup.init) }
+ ___setup_end = .;
+ ___initcall_start = .;
+ .initcall.init : { *(.initcall.init) }
+ ___initcall_end = .;
+ __vmlinux_end = .; /* last address of the physical file */
+ . = ALIGN(8192);
+ ___init_end = .;
+
+ __data_end = . ; /* Move to _edata ? */
+ __bss_start = .; /* BSS */
+ .bss : {
+ *(COMMON)
+ *(.bss)
+ }
+
+ . = ALIGN (0x20);
+ _end = .;
+ __end = .;
+
+ /* Sections to be discarded */
+ /DISCARD/ : {
+ *(.text.exit)
+ *(.data.exit)
+ *(.exitcall.exit)
+ }
+
+ _dram_end = 0x60000000 + @ETRAX_DRAM_SIZE_M@*1024*1024;
+}
diff --git a/arch/cris/defconfig b/arch/cris/defconfig
new file mode 100644
index 000000000..91a25ee23
--- /dev/null
+++ b/arch/cris/defconfig
@@ -0,0 +1,319 @@
+#
+# Automatically generated make config: don't edit
+#
+CONFIG_UID16=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+
+#
+# General setup
+#
+CONFIG_NET=y
+CONFIG_SYSVIPC=y
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_JAVA is not set
+# CONFIG_KGDB is not set
+# CONFIG_ETRAX_WATCHDOG is not set
+CONFIG_USE_SERIAL_CONSOLE=y
+# CONFIG_KERNEL_IFCONFIG is not set
+
+#
+# Hardware setup
+#
+CONFIG_ETRAX100LX=y
+# CONFIG_SVINTO_SIM is not set
+CONFIG_CRIS_LOW_MAP=y
+ETRAX_DRAM_BASE=40000000
+ETRAX_DRAM_SIZE=8
+CONFIG_ETRAX_PA_LEDS=y
+# CONFIG_ETRAX_PB_LEDS is not set
+# CONFIG_ETRAX_90000000_LEDS is not set
+# CONFIG_ETRAX_NO_LEDS is not set
+CONFIG_ETRAX_LED1G=2
+CONFIG_ETRAX_LED1R=2
+CONFIG_ETRAX_LED2G=2
+CONFIG_ETRAX_LED2R=2
+CONFIG_DEBUG_PORT0=y
+# CONFIG_DEBUG_PORT1 is not set
+# CONFIG_DEBUG_PORT2 is not set
+# CONFIG_DEBUG_PORT3 is not set
+DEF_R_WAITSTATES=95a6
+DEF_R_BUS_CONFIG=104
+DEF_R_DRAM_CONFIG=1a200040
+DEF_R_DRAM_TIMING=5611
+DEF_R_PORT_PA_DIR=1d
+DEF_R_PORT_PA_DATA=f0
+DEF_R_PORT_PB_CONFIG=00
+DEF_R_PORT_PB_DIR=1e
+DEF_R_PORT_PB_DATA=f3
+
+#
+# Drivers for Etrax built-in interfaces
+#
+CONFIG_ETRAX_ETHERNET=y
+CONFIG_NET_ETHERNET=y
+CONFIG_ETRAX_SERIAL=y
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_CPQ_CISS_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_BLK_DEV_INITRD is not set
+
+#
+# Networking options
+#
+# CONFIG_PACKET is not set
+# CONFIG_NETLINK is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_FILTER is not set
+CONFIG_UNIX=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+# CONFIG_IP_PNP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_INET_ECN is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_IPV6 is not set
+# CONFIG_KHTTPD is not set
+# CONFIG_ATM is not set
+
+#
+#
+#
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_DECNET is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_LLC is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# SCSI support
+#
+# CONFIG_SCSI is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_NET_SB1000 is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_AT1700 is not set
+# CONFIG_DEPCA is not set
+# CONFIG_NET_ISA is not set
+# CONFIG_NET_PCI is not set
+# CONFIG_NET_POCKET is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+# CONFIG_ACENIC is not set
+# CONFIG_HAMACHI is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_SK98LIN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PLIP is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+
+#
+# Wireless LAN (non-hamradio)
+#
+# CONFIG_NET_RADIO is not set
+
+#
+# Token Ring devices
+#
+# CONFIG_TR is not set
+# CONFIG_NET_FC is not set
+# CONFIG_RCPCI is not set
+# CONFIG_SHAPER is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
+#
+# CONFIG_CD_NO_IDESCSI is not set
+
+#
+# File systems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_ADFS_FS is not set
+# CONFIG_ADFS_FS_RW is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_FAT_FS is not set
+# CONFIG_MSDOS_FS is not set
+# CONFIG_UMSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_JFFS_FS is not set
+CONFIG_CRAMFS=y
+CONFIG_RAMFS=y
+# CONFIG_ISO9660_FS is not set
+# CONFIG_JOLIET is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_NTFS_RW is not set
+# CONFIG_HPFS_FS is not set
+CONFIG_PROC_FS=y
+# CONFIG_DEVFS_FS is not set
+# CONFIG_DEVFS_MOUNT is not set
+# CONFIG_DEVFS_DEBUG is not set
+# CONFIG_DEVPTS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_QNX4FS_RW is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_EXT2_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_SYSV_FS_WRITE is not set
+# CONFIG_UDF_FS is not set
+# CONFIG_UDF_RW is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_UFS_FS_WRITE is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
+# CONFIG_NFS_FS is not set
+# CONFIG_NFS_V3 is not set
+# CONFIG_ROOT_NFS is not set
+# CONFIG_NFSD is not set
+# CONFIG_NFSD_V3 is not set
+# CONFIG_SUNRPC is not set
+# CONFIG_LOCKD is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_NCPFS_PACKET_SIGNING is not set
+# CONFIG_NCPFS_IOCTL_LOCKING is not set
+# CONFIG_NCPFS_STRONG is not set
+# CONFIG_NCPFS_NFS_NS is not set
+# CONFIG_NCPFS_OS2_NS is not set
+# CONFIG_NCPFS_SMALLDOS is not set
+# CONFIG_NCPFS_MOUNT_SUBDIR is not set
+# CONFIG_NCPFS_NDS_DOMAINS is not set
+# CONFIG_NCPFS_NLS is not set
+# CONFIG_NCPFS_EXTRAS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_NLS is not set
+
+#
+# Character devices
+#
+# CONFIG_VT is not set
+# CONFIG_SERIAL is not set
+# CONFIG_SERIAL_EXTENDED is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_UNIX98_PTYS is not set
+# CONFIG_PRINTER is not set
+# CONFIG_PPDEV is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Mice
+#
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MOUSE is not set
+
+#
+# Joysticks
+#
+# CONFIG_JOYSTICK is not set
+
+#
+# Input core support is needed for joysticks
+#
+# CONFIG_QIC02_TAPE is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_INTEL_RNG is not set
+# CONFIG_NVRAM is not set
+# CONFIG_RTC is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+# CONFIG_AGP is not set
+# CONFIG_DRM is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PROFILE is not set
diff --git a/arch/cris/drivers/Config.in b/arch/cris/drivers/Config.in
new file mode 100644
index 000000000..36055ae04
--- /dev/null
+++ b/arch/cris/drivers/Config.in
@@ -0,0 +1,49 @@
+mainmenu_option next_comment
+comment 'Drivers for Etrax built-in interfaces'
+
+bool 'Ethernet support' CONFIG_ETRAX_ETHERNET y
+if [ "$CONFIG_ETRAX_ETHERNET" = "y" ]; then
+# this is just so that the user does not have to go into the
+# normal ethernet driver section just to enable ethernetworking
+ define_bool CONFIG_NET_ETHERNET y
+fi
+
+bool 'Serial-port support' CONFIG_ETRAX_SERIAL y
+
+bool 'ATA/IDE support' CONFIG_ETRAX_IDE n
+
+if [ "$CONFIG_ETRAX_IDE" = "y" ]; then
+# here we should add the CONFIG_'s necessary to enable the basic
+# general ide drivers so the common case does not need to go
+# into that config submenu. enable disk and CD support. others
+# need to go fiddle in the submenu..
+ define_bool CONFIG_IDE y
+
+ define_bool CONFIG_BLK_DEV_IDE y
+ define_bool CONFIG_BLK_DEV_IDEDISK y
+ define_bool CONFIG_BLK_DEV_IDECD y
+
+ define_bool CONFIG_BLK_DEV_IDEDMA y
+ define_bool CONFIG_DMA_NONPCI y
+
+ choice 'IDE reset pin' \
+ "Port_PB_Bit_7 CONFIG_ETRAX_IDE_PB7_RESET\
+ Port_G_Bit_27 CONFIG_ETRAX_IDE_G27_RESET\
+ Port_CSE1_Bit_16 CONFIG_ETRAX_IDE_CSE1_16_RESET" Port_PB_Bit_7
+fi
+
+bool 'Axis flash-map support' CONFIG_ETRAX_AXISFLASHMAP n
+
+if [ "$CONFIG_ETRAX_AXISFLASHMAP" = "y" ]; then
+# here we define the CONFIG_'s necessary to enable MTD support
+# for the flash
+ define_bool CONFIG_MTD y
+
+ define_bool CONFIG_MTD_CFI y
+ define_bool CONFIG_MTD_CFI_INTELEXT n
+ define_bool CONFIG_MTD_CFI_AMDSTD y
+
+ define_bool CONFIG_MTD_CHAR y
+fi
+
+endmenu
diff --git a/arch/cris/drivers/Makefile b/arch/cris/drivers/Makefile
new file mode 100644
index 000000000..47754f647
--- /dev/null
+++ b/arch/cris/drivers/Makefile
@@ -0,0 +1,14 @@
+#
+# Makefile for Etrax-specific drivers
+#
+
+O_TARGET := drivers.o
+
+obj-y :=
+
+obj-$(CONFIG_ETRAX_ETHERNET) += ethernet.o
+obj-$(CONFIG_ETRAX_SERIAL) += serial.o
+obj-$(CONFIG_ETRAX_IDE) += ide.o
+obj-$(CONFIG_ETRAX_AXISFLASHMAP) += axisflashmap.o
+
+include $(TOPDIR)/Rules.make
diff --git a/arch/cris/drivers/axisflashmap.c b/arch/cris/drivers/axisflashmap.c
new file mode 100644
index 000000000..15b0be262
--- /dev/null
+++ b/arch/cris/drivers/axisflashmap.c
@@ -0,0 +1,311 @@
+/*
+ * Physical mapping layer for MTD using the Axis partitiontable format
+ *
+ * Copyright (c) 2001 Axis Communications AB
+ *
+ * This file is under the GPL.
+ *
+ * First partition is always sector 0 regardless of if we find a partitiontable
+ * or not. In the start of the next sector, there can be a partitiontable that
+ * tells us what other partitions to define. If there isn't, we use a default
+ * partition split defined below.
+ *
+ * $Log: axisflashmap.c,v $
+ * Revision 1.1 2001/01/12 17:01:18 bjornw
+ * * Added axisflashmap.c, a physical mapping for MTD that reads and understands
+ * Axis partition-table format.
+ *
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/config.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/axisflashmap.h>
+#include <asm/mmu.h>
+
+#ifdef CONFIG_CRIS_LOW_MAP
+#define FLASH_UNCACHED_ADDR KSEG_E
+#define FLASH_CACHED_ADDR KSEG_5
+#else
+#define FLASH_UNCACHED_ADDR KSEG_E
+#define FLASH_CACHED_ADDR KSEG_F
+#endif
+
+/*
+ * WINDOW_SIZE is the total size where the flash chips are mapped,
+ * my guess is that this can be the total memory area even if there
+ * are many flash chips inside the area or if they are not all mounted.
+ * So possibly we can get rid of the CONFIG_ here and just write something
+ * like 32 MB always.
+ */
+
+#define WINDOW_SIZE (CONFIG_ETRAX_FLASH_LENGTH * 1024 * 1024)
+
+/* Byte-offset where the partition-table is placed in the first chip
+ */
+
+#define PTABLE_SECTOR 65536
+
+/*
+ * Map driver
+ *
+ * Ok this is the scoop - we need to access the flash both with and without
+ * the cache - without when doing all the fancy flash interfacing, and with
+ * when we do actual copying because otherwise it will be slow like molasses.
+ * I hope this works the way it's intended, so that there won't be any cases
+ * of non-synchronicity because of the different access modes below...
+ */
+
+static __u8 flash_read8(struct map_info *map, unsigned long ofs)
+{
+ return *(__u8 *)(FLASH_UNCACHED_ADDR + ofs);
+}
+
+static __u16 flash_read16(struct map_info *map, unsigned long ofs)
+{
+ return *(__u16 *)(FLASH_UNCACHED_ADDR + ofs);
+}
+
+static __u32 flash_read32(struct map_info *map, unsigned long ofs)
+{
+ return *(volatile unsigned int *)(FLASH_UNCACHED_ADDR + ofs);
+}
+
+static void flash_copy_from(struct map_info *map, void *to,
+ unsigned long from, ssize_t len)
+{
+ memcpy(to, (void *)(FLASH_CACHED_ADDR + from), len);
+}
+
+static void flash_write8(struct map_info *map, __u8 d, unsigned long adr)
+{
+ *(__u8 *)(FLASH_UNCACHED_ADDR + adr) = d;
+}
+
+static void flash_write16(struct map_info *map, __u16 d, unsigned long adr)
+{
+ *(__u16 *)(FLASH_UNCACHED_ADDR + adr) = d;
+}
+
+static void flash_write32(struct map_info *map, __u32 d, unsigned long adr)
+{
+ *(__u32 *)(FLASH_UNCACHED_ADDR + adr) = d;
+}
+
+static void flash_copy_to(struct map_info *map, unsigned long to,
+ const void *from, ssize_t len)
+{
+ memcpy((void *)(FLASH_CACHED_ADDR + to), from, len);
+}
+
+static struct map_info axis_map = {
+ name: "Axis flash",
+ size: WINDOW_SIZE,
+ buswidth: CONFIG_ETRAX_FLASH_BUSWIDTH,
+ read8: flash_read8,
+ read16: flash_read16,
+ read32: flash_read32,
+ copy_from: flash_copy_from,
+ write8: flash_write8,
+ write16: flash_write16,
+ write32: flash_write32,
+ copy_to: flash_copy_to
+};
+
+/* If no partition-table was found, we use this default-set.
+ */
+
+#define MAX_PARTITIONS 7
+#define NUM_DEFAULT_PARTITIONS 3
+
+static struct mtd_partition axis_default_partitions[NUM_DEFAULT_PARTITIONS] = {
+ {
+ name: "boot firmware",
+ size: PTABLE_SECTOR,
+ offset: 0
+ },
+ {
+ name: "kernel",
+ size: 0x1a0000,
+ offset: PTABLE_SECTOR
+ },
+ {
+ name: "filesystem",
+ size: 0x50000,
+ offset: (0x1a0000 + PTABLE_SECTOR)
+ }
+};
+
+static struct mtd_partition axis_partitions[MAX_PARTITIONS] = {
+ {
+ name: "part0",
+ size: 0,
+ offset: 0
+ },
+ {
+ name: "part1",
+ size: 0,
+ offset: 0
+ },
+ {
+ name: "part2",
+ size: 0,
+ offset: 0
+ },
+ {
+ name: "part3",
+ size: 0,
+ offset: 0
+ },
+ {
+ name: "part4",
+ size: 0,
+ offset: 0
+ },
+ {
+ name: "part5",
+ size: 0,
+ offset: 0
+ },
+ {
+ name: "part6",
+ size: 0,
+ offset: 0
+ },
+};
+
+/*
+ * This is the master MTD device for which all the others are just
+ * auto-relocating aliases.
+ */
+static struct mtd_info *mymtd;
+
+/* CFI-scan the flash, and if there was a chip, read the partition-table
+ * and register the partitions with MTD.
+ */
+
+static int __init
+init_axis_flash(void)
+{
+ int pidx = 0;
+ struct partitiontable_head *ptable_head;
+ struct partitiontable_entry *ptable;
+ int use_default_ptable = 1; /* Until proven otherwise */
+ const char *pmsg = " /dev/flash%d at 0x%x, size 0x%x\n";
+
+ printk(KERN_NOTICE "Axis flash mapping: %x at %x\n",
+ WINDOW_SIZE, FLASH_CACHED_ADDR);
+
+ mymtd = do_cfi_probe(&axis_map);
+
+ if(!mymtd) {
+ printk("cfi_probe erred %d\n", mymtd);
+ return -ENXIO;
+ }
+
+ mymtd->module = THIS_MODULE;
+
+ /* The partition-table is at an offset within the second
+ * sector of the flash. We _define_ this to be at offset 64k
+ * even if the actual sector-size in the flash changes.. for
+ * now at least.
+ */
+
+ ptable_head = FLASH_CACHED_ADDR + PTABLE_SECTOR + PARTITION_TABLE_OFFSET;
+ pidx++; /* first partition is always set to the default */
+
+ if ((ptable_head->magic == PARTITION_TABLE_MAGIC)
+ && (ptable_head->size
+ < (MAX_PARTITIONS
+ * sizeof(struct partitiontable_entry) + 4))
+ && (*(unsigned long*)
+ ((void*)ptable_head
+ + sizeof(*ptable_head)
+ + ptable_head->size - 4)
+ == PARTITIONTABLE_END_MARKER)) {
+ /* Looks like a start, sane length and end of a
+ * partition table, lets check csum etc.
+ */
+ int ptable_ok = 0;
+ struct partitiontable_entry *max_addr =
+ (struct partitiontable_entry *)
+ ((unsigned long)ptable_head + sizeof(*ptable_head) +
+ ptable_head->size);
+ unsigned long offset = PTABLE_SECTOR;
+ unsigned char *p;
+ unsigned long csum = 0;
+
+ ptable = (struct partitiontable_entry *)
+ ((unsigned long)ptable_head + sizeof(*ptable_head));
+
+ /* Lets be PARANOID, and check the checksum. */
+ p = (unsigned char*) ptable;
+
+ while (p <= (unsigned char*)max_addr) {
+ csum += *p++;
+ csum += *p++;
+ csum += *p++;
+ csum += *p++;
+ }
+ /* printk(" total csum: 0x%08X 0x%08X\n",
+ csum, ptable_head->checksum); */
+ ptable_ok = (csum == ptable_head->checksum);
+
+ /* Read the entries and use/show the info. */
+ ptable = (struct partitiontable_entry *)
+ ((unsigned long)ptable_head + sizeof(*ptable_head));
+
+ printk(" Found %s partition table at 0x%08lX-0x%08lX.\n",
+ (ptable_ok ? "valid" : "invalid"),
+ (unsigned long)ptable_head,
+ (unsigned long)max_addr);
+
+ /* We have found a working bootblock. Now read the
+ partition table. Scan the table. It ends when
+ there is 0xffffffff, that is, empty flash. */
+
+ while (ptable_ok
+ && ptable->offset != 0xffffffff
+ && ptable < max_addr
+ && pidx < MAX_PARTITIONS) {
+#if 0
+ /* wait with multi-chip support until we know
+ * how mtd detects multiple chips
+ */
+ if ((offset + ptable->offset) >= chips[0].size) {
+ partitions[pidx].start
+ = offset + chips[1].start
+ + ptable->offset - chips[0].size;
+ }
+#endif
+ axis_partitions[pidx].offset = offset + ptable->offset;
+ axis_partitions[pidx].size = ptable->size;
+
+ printk(pmsg, pidx, axis_partitions[pidx].offset,
+ axis_partitions[pidx].size);
+ pidx++;
+ ptable++;
+ }
+ use_default_ptable = !ptable_ok;
+ }
+
+ if(use_default_ptable) {
+ printk(" Using default partition table\n");
+ return add_mtd_partitions(mymtd, axis_default_partitions,
+ NUM_DEFAULT_PARTITIONS);
+ } else {
+ return add_mtd_partitions(mymtd, axis_partitions, pidx);
+ }
+}
+
+/* This adds the above to the kernels init-call chain */
+
+module_init(init_axis_flash);
+
diff --git a/arch/cris/drivers/ethernet.c b/arch/cris/drivers/ethernet.c
new file mode 100644
index 000000000..53a9798ae
--- /dev/null
+++ b/arch/cris/drivers/ethernet.c
@@ -0,0 +1,1041 @@
+/* $Id: ethernet.c,v 1.5 2000/11/29 17:22:22 bjornw Exp $
+ *
+ * e100net.c: A network driver for the ETRAX 100LX network controller.
+ *
+ * Copyright (c) 1998-2000 Axis Communications AB.
+ *
+ * The outline of this driver comes from skeleton.c.
+ *
+ * $Log: ethernet.c,v $
+ * Revision 1.5 2000/11/29 17:22:22 bjornw
+ * Get rid of the udword types legacy stuff
+ *
+ * Revision 1.4 2000/11/22 16:36:09 bjornw
+ * Please marketing by using the correct case when spelling Etrax.
+ *
+ * Revision 1.3 2000/11/21 16:43:04 bjornw
+ * Minor short->int change
+ *
+ * Revision 1.2 2000/11/08 14:27:57 bjornw
+ * 2.4 port
+ *
+ * Revision 1.1 2000/11/06 13:56:00 bjornw
+ * Verbatim copy of the 1.24 version of e100net.c from elinux
+ *
+ * Revision 1.24 2000/10/04 15:55:23 bjornw
+ * * Use virt_to_phys etc. for DMA addresses
+ * * Removed bogus CHECKSUM_UNNECESSARY
+ *
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/svinto.h>
+
+//#define ETHDEBUG
+
+#define D(x)
+
+/*
+ * The name of the card. Is used for messages and in the requests for
+ * io regions, irqs and dma channels
+ */
+
+static const char* cardname = "ETRAX 100LX built-in ethernet controller";
+
+/* A default ethernet address. Highlevel SW will set the real one later */
+
+static struct sockaddr default_mac = {
+ 0,
+ { 0x00, 0x40, 0x8C, 0xCD, 0x00, 0x00 }
+};
+
+/* Information that need to be kept for each board. */
+struct net_local {
+ struct net_device_stats stats;
+
+ /* Tx control lock. This protects the transmit buffer ring
+ * state along with the "tx full" state of the driver. This
+ * means all netif_queue flow control actions are protected
+ * by this lock as well.
+ */
+ spinlock_t lock;
+};
+
+#define NETWORK_DMARX_IRQ 17 /* irq 17 is the DMA1 irq */
+#define NETWORK_DMATX_IRQ 16 /* irq 16 is the DMA0 irq */
+#define NETWORK_STATUS_IRQ 6 /* irq 6 is the network irq */
+
+/* Dma descriptors etc. */
+
+#define RX_BUF_SIZE 32768
+
+#define MAX_MEDIA_DATA_SIZE 1518
+
+#define MIN_PACKET_LEN 46
+#define ETHER_HEAD_LEN 14
+
+/*
+** MDIO constants.
+*/
+#define MDIO_BASE_STATUS_REG 0x1
+#define MDIO_BASE_CONTROL_REG 0x0
+#define MDIO_LINK_UP_MASK 0x4
+#define MDIO_START 0x1
+#define MDIO_READ 0x2
+#define MDIO_WRITE 0x1
+#define MDIO_PREAMBLE 0xfffffffful
+
+/* Broadcom specific */
+#define MDIO_AUX_CTRL_STATUS_REG 0x18
+#define MDIO_SPEED 0x2
+#define MDIO_PHYS_ADDR 0x0
+
+/* Network flash constants */
+#define NET_FLASH_TIME 2 /* 20 ms */
+#define NET_LINK_UP_CHECK_INTERVAL 200 /* 2 s */
+
+/* RX_DESC_BUF_SIZE should be a multiple of four to avoid the buffer
+ * alignment bug in Etrax 100 release 1
+ */
+
+#define RX_DESC_BUF_SIZE 256
+#define NBR_OF_RX_DESC (RX_BUF_SIZE / \
+ RX_DESC_BUF_SIZE)
+
+#define GET_BIT(bit,val) (((val) >> (bit)) & 0x01)
+
+static etrax_dma_descr *myNextRxDesc; /* Points to the next descriptor to
+ to be processed */
+static etrax_dma_descr *myLastRxDesc; /* The last processed descriptor */
+static etrax_dma_descr *myPrevRxDesc; /* The descriptor right before myNextRxDesc */
+
+static unsigned char RxBuf[RX_BUF_SIZE];
+
+static etrax_dma_descr RxDescList[NBR_OF_RX_DESC];
+static etrax_dma_descr TxDesc;
+
+static struct sk_buff *tx_skb;
+
+/* Network speed indication. */
+static struct timer_list speed_timer;
+static struct timer_list clear_led_timer;
+static int current_speed;
+static int led_clear_time;
+static int nolink;
+
+/* Index to functions, as function prototypes. */
+
+static int etrax_ethernet_init(struct net_device *dev);
+
+static int e100_open(struct net_device *dev);
+static int e100_set_mac_address(struct net_device *dev, void *addr);
+static int e100_send_packet(struct sk_buff *skb, struct net_device *dev);
+static void e100rx_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void e100tx_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void e100nw_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void e100_rx(struct net_device *dev);
+static int e100_close(struct net_device *dev);
+static struct net_device_stats *e100_get_stats(struct net_device *dev);
+static void set_multicast_list(struct net_device *dev);
+static void e100_hardware_send_packet(char *buf, int length);
+static void update_rx_stats(struct net_device_stats *);
+static void update_tx_stats(struct net_device_stats *);
+
+static void e100_check_speed(unsigned long dummy);
+static unsigned short e100_get_mdio_reg(unsigned char reg_num);
+static void e100_send_mdio_cmd(unsigned short cmd, int write_cmd);
+static void e100_send_mdio_bit(unsigned char bit);
+static unsigned char e100_receive_mdio_bit(void);
+static void e100_reset_tranceiver(void);
+
+static void e100_clear_network_leds(unsigned long dummy);
+
+#define tx_done(dev) (*R_DMA_CH0_CMD == 0)
+
+/*
+ * Check for a network adaptor of this type, and return '0' if one exists.
+ * If dev->base_addr == 0, probe all likely locations.
+ * If dev->base_addr == 1, always return failure.
+ * If dev->base_addr == 2, allocate space for the device and return success
+ * (detachable devices only).
+ */
+
+static int __init
+etrax_ethernet_init(struct net_device *dev)
+{
+ int i;
+ int anOffset = 0;
+
+ printk("ETRAX 100LX 10/100MBit ethernet v2.0 (c) 2000 Axis Communications AB\n");
+
+ dev->base_addr = (unsigned int)R_NETWORK_SA_0; /* just to have something to show */
+
+ printk("%s initialized\n", dev->name);
+
+ /* make Linux aware of the new hardware */
+
+ if (!dev) {
+ printk("dev == NULL. Should this happen?\n");
+ dev = init_etherdev(dev, sizeof(struct net_local));
+ }
+
+ /* setup generic handlers and stuff in the dev struct */
+
+ ether_setup(dev);
+
+ /* make room for the local structure containing stats etc */
+
+ dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ memset(dev->priv, 0, sizeof(struct net_local));
+
+ /* now setup our etrax specific stuff */
+
+ dev->irq = NETWORK_DMARX_IRQ; /* we really use DMATX as well... */
+ dev->dma = 1;
+
+ /* fill in our handlers so the network layer can talk to us in the future */
+
+ dev->open = e100_open;
+ dev->hard_start_xmit = e100_send_packet;
+ dev->stop = e100_close;
+ dev->get_stats = e100_get_stats;
+ dev->set_multicast_list = set_multicast_list;
+ dev->set_mac_address = e100_set_mac_address;
+
+ /* set the default MAC address */
+
+ e100_set_mac_address(dev, &default_mac);
+
+ /* Initialise the list of Etrax DMA-descriptors */
+
+ /* Initialise receive descriptors */
+
+ for(i = 0; i < (NBR_OF_RX_DESC - 1); i++) {
+ RxDescList[i].ctrl = 0;
+ RxDescList[i].sw_len = RX_DESC_BUF_SIZE;
+ RxDescList[i].next = virt_to_phys(&RxDescList[i + 1]);
+ RxDescList[i].buf = virt_to_phys(RxBuf + anOffset);
+ RxDescList[i].status = 0;
+ RxDescList[i].hw_len = 0;
+ anOffset += RX_DESC_BUF_SIZE;
+ }
+
+ RxDescList[i].ctrl = d_eol;
+ RxDescList[i].sw_len = RX_DESC_BUF_SIZE;
+ RxDescList[i].next = virt_to_phys(&RxDescList[0]);
+ RxDescList[i].buf = virt_to_phys(RxBuf + anOffset);
+ RxDescList[i].status = 0;
+ RxDescList[i].hw_len = 0;
+
+ /* Initialise initial pointers */
+
+ myNextRxDesc = &RxDescList[0];
+ myLastRxDesc = &RxDescList[NBR_OF_RX_DESC - 1];
+ myPrevRxDesc = &RxDescList[NBR_OF_RX_DESC - 1];
+
+ /* Initialize speed indicator stuff. */
+
+ nolink = 0;
+ current_speed = 10;
+ speed_timer.expires = jiffies + NET_LINK_UP_CHECK_INTERVAL;
+ speed_timer.function = e100_check_speed;
+ add_timer(&speed_timer);
+ clear_led_timer.function = e100_clear_network_leds;
+ clear_led_timer.expires = jiffies + 10;
+ add_timer(&clear_led_timer);
+
+ return 0;
+}
+
+/* set MAC address of the interface. called from the core after a
+ * SIOCSIFADDR ioctl, and from the bootup above.
+ */
+
+static int
+e100_set_mac_address(struct net_device *dev, void *p)
+{
+ struct sockaddr *addr = p;
+ int i;
+
+ /* remember it */
+
+ memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+
+ /* Write it to the hardware.
+ * Note the way the address is wrapped:
+ * *R_NETWORK_SA_0 = a0_0 | (a0_1 << 8) | (a0_2 << 16) | (a0_3 << 24);
+ * *R_NETWORK_SA_1 = a0_4 | (a0_5 << 8);
+ */
+
+ *R_NETWORK_SA_0 = dev->dev_addr[0] | (dev->dev_addr[1] << 8) |
+ (dev->dev_addr[2] << 16) | (dev->dev_addr[3] << 24);
+ *R_NETWORK_SA_1 = dev->dev_addr[4] | (dev->dev_addr[5] << 8);
+ *R_NETWORK_SA_2 = 0;
+
+ /* show it in the log as well */
+
+ printk("%s: changed MAC to ", dev->name);
+
+ for (i = 0; i < 5; i++)
+ printk("%02X:", dev->dev_addr[i]);
+
+ printk("%02X\n", dev->dev_addr[i]);
+
+ return 0;
+}
+
+/*
+ * Open/initialize the board. This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is non-reboot way to recover if something goes wrong.
+ */
+
+static int
+e100_open(struct net_device *dev)
+{
+ unsigned long flags;
+
+ /* disable the ethernet interface while we configure it */
+
+ *R_NETWORK_GEN_CONFIG =
+ IO_STATE(R_NETWORK_GEN_CONFIG, phy, mii_clk) |
+ IO_STATE(R_NETWORK_GEN_CONFIG, enable, off);
+
+ /* enable the MDIO output pin */
+
+ *R_NETWORK_MGM_CTRL = IO_STATE(R_NETWORK_MGM_CTRL, mdoe, enable);
+
+ *R_IRQ_MASK0_CLR = 0xe0000; /* clear excessive_col, over/underrun irq mask */
+ *R_IRQ_MASK2_CLR = 0xf; /* clear dma0 and 1 eop and descr irq masks */
+
+ /* Reset and wait for the DMA channels */
+
+ RESET_DMA(0);
+ RESET_DMA(1);
+ WAIT_DMA(0);
+ WAIT_DMA(1);
+
+ /* Initialise the etrax network controller */
+
+ /* allocate the irq corresponding to the receiving DMA */
+
+ if (request_irq(NETWORK_DMARX_IRQ, e100rx_interrupt, 0,
+ cardname, (void *)dev)) {
+ goto grace_exit;
+ }
+
+ /* allocate the irq corresponding to the transmitting DMA */
+
+ if (request_irq(NETWORK_DMATX_IRQ, e100tx_interrupt, 0,
+ cardname, (void *)dev)) {
+ goto grace_exit;
+ }
+
+ /* allocate the irq corresponding to the network errors etc */
+
+ if (request_irq(NETWORK_STATUS_IRQ, e100nw_interrupt, 0,
+ cardname, (void *)dev)) {
+ goto grace_exit;
+ }
+
+ /*
+ * Always allocate the DMA channels after the IRQ,
+ * and clean up on failure.
+ */
+
+ if(request_dma(0, cardname)) {
+ goto grace_exit;
+ }
+
+ if(request_dma(1, cardname)) {
+ grace_exit:
+ /* this will cause some 'trying to free free irq' but what the heck... */
+ free_dma(0);
+ free_irq(NETWORK_DMARX_IRQ, NULL);
+ free_irq(NETWORK_DMATX_IRQ, NULL);
+ free_irq(NETWORK_STATUS_IRQ, NULL);
+ return -EAGAIN;
+ }
+
+ /* give the HW an idea of what MAC address we want */
+
+ *R_NETWORK_SA_0 = dev->dev_addr[0] | (dev->dev_addr[1] << 8) |
+ (dev->dev_addr[2] << 16) | (dev->dev_addr[3] << 24);
+ *R_NETWORK_SA_1 = dev->dev_addr[4] | (dev->dev_addr[5] << 8);
+ *R_NETWORK_SA_2 = 0;
+
+#if 0
+ /* use promiscuous mode for testing */
+ *R_NETWORK_GA_0 = 0xffffffff;
+ *R_NETWORK_GA_1 = 0xffffffff;
+
+ *R_NETWORK_REC_CONFIG = 0xd; /* broadcast rec, individ. rec, ma0 enabled */
+#else
+ *R_NETWORK_REC_CONFIG =
+ IO_STATE(R_NETWORK_REC_CONFIG, broadcast, receive) |
+ IO_STATE(R_NETWORK_REC_CONFIG, ma0, enable);
+#endif
+
+ *R_NETWORK_GEN_CONFIG =
+ IO_STATE(R_NETWORK_GEN_CONFIG, phy, mii_clk) |
+ IO_STATE(R_NETWORK_GEN_CONFIG, enable, on);
+
+ save_flags(flags);
+ cli();
+
+ /* enable the irq's for ethernet DMA */
+
+ *R_IRQ_MASK2_SET =
+ IO_STATE(R_IRQ_MASK2_SET, dma0_eop, set) |
+ IO_STATE(R_IRQ_MASK2_SET, dma1_eop, set);
+
+ *R_IRQ_MASK0_SET =
+ IO_STATE(R_IRQ_MASK0_SET, overrun, set) |
+ IO_STATE(R_IRQ_MASK0_SET, underrun, set) |
+ IO_STATE(R_IRQ_MASK0_SET, excessive_col, set);
+
+ tx_skb = 0;
+
+ /* make sure the irqs are cleared */
+
+ *R_DMA_CH0_CLR_INTR = IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do);
+ *R_DMA_CH1_CLR_INTR = IO_STATE(R_DMA_CH1_CLR_INTR, clr_eop, do);
+
+ /* make sure the rec and transmit error counters are cleared */
+
+ (void)*R_REC_COUNTERS; /* dummy read */
+ (void)*R_TR_COUNTERS; /* dummy read */
+
+ /* start the receiving DMA channel so we can receive packets from now on */
+
+ *R_DMA_CH1_FIRST = virt_to_phys(myNextRxDesc);
+ *R_DMA_CH1_CMD = IO_STATE(R_DMA_CH1_CMD, cmd, start);
+
+ restore_flags(flags);
+
+ /* We are now ready to accept transmit requeusts from
+ * the queueing layer of the networking.
+ */
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+
+static void
+e100_check_speed(unsigned long dummy)
+{
+ unsigned long data;
+ data = e100_get_mdio_reg(MDIO_BASE_STATUS_REG);
+ if (!(data & MDIO_LINK_UP_MASK)) {
+ nolink = 1;
+ LED_NETWORK_TX_SET(1); /* Make it red, link is down. */
+ } else {
+ nolink = 0;
+ LED_NETWORK_TX_SET(0); /* Link is up again, clear red LED. */
+ data = e100_get_mdio_reg(MDIO_AUX_CTRL_STATUS_REG);
+ if (data & MDIO_SPEED) {
+ current_speed = 100;
+ } else {
+ current_speed = 10;
+ }
+ }
+
+ /* Reinitialize the timer. */
+ speed_timer.expires = jiffies + NET_LINK_UP_CHECK_INTERVAL;
+ add_timer(&speed_timer);
+}
+
+static unsigned short
+e100_get_mdio_reg(unsigned char reg_num)
+{
+ unsigned long flags;
+ unsigned short cmd; /* Data to be sent on MDIO port */
+ unsigned short data; /* Data read from MDIO */
+ int bitCounter;
+
+ save_flags(flags);
+ cli();
+
+ /* Start of frame, OP Code, Physical Address, Register Address */
+ cmd = (MDIO_START << 14) | (MDIO_READ << 12) | (MDIO_PHYS_ADDR << 7) |
+ (reg_num << 2);
+
+ e100_send_mdio_cmd(cmd, 0);
+
+ data = 0;
+
+ /* Data... */
+ for(bitCounter=15; bitCounter>=0 ; bitCounter--) {
+ data |= (e100_receive_mdio_bit() << bitCounter);
+ }
+
+ restore_flags(flags);
+ return data;
+}
+
+static void
+e100_send_mdio_cmd(unsigned short cmd, int write_cmd)
+{
+ int bitCounter;
+ unsigned char data = 0x2;
+
+ /* Preamble */
+ for(bitCounter = 31; bitCounter>= 0; bitCounter--)
+ e100_send_mdio_bit(GET_BIT(bitCounter, MDIO_PREAMBLE));
+
+ for(bitCounter = 15; bitCounter >= 2; bitCounter--)
+ e100_send_mdio_bit(GET_BIT(bitCounter, cmd));
+
+ /* Turnaround */
+ for(bitCounter = 1; bitCounter >= 0 ; bitCounter--)
+ if (write_cmd)
+ e100_send_mdio_bit(GET_BIT(bitCounter, data));
+ else
+ e100_receive_mdio_bit();
+}
+
+static void
+e100_send_mdio_bit(unsigned char bit)
+{
+ volatile int i;
+ *R_NETWORK_MGM_CTRL = 2 | bit&1;
+ for (i=40; i; i--);
+ *R_NETWORK_MGM_CTRL = 6 | bit&1;
+ for (i=40; i; i--);
+}
+
+static unsigned char
+e100_receive_mdio_bit()
+{
+ unsigned char bit;
+ volatile int i;
+ *R_NETWORK_MGM_CTRL = 0;
+ bit = *R_NETWORK_STAT & 1;
+ for (i=40; i; i--);
+ *R_NETWORK_MGM_CTRL = 4;
+ for (i=40; i; i--);
+ return bit;
+}
+
+static void
+e100_reset_tranceiver(void)
+{
+ unsigned long flags;
+ unsigned short cmd;
+ unsigned short data;
+ int bitCounter;
+
+ save_flags(flags);
+ cli();
+
+ data = e100_get_mdio_reg(MDIO_BASE_CONTROL_REG);
+
+ cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (MDIO_PHYS_ADDR << 7) | (MDIO_BASE_CONTROL_REG << 2);
+
+ e100_send_mdio_cmd(cmd, 0);
+
+ data |= 0x8000;
+
+ for(bitCounter = 15; bitCounter >= 0 ; bitCounter--) {
+ e100_send_mdio_bit(GET_BIT(bitCounter, data));
+ }
+
+ restore_flags(flags);
+}
+
+/* Called by upper layers if they decide it took too long to complete
+ * sending a packet - we need to reset and stuff.
+ */
+
+static void
+e100_tx_timeout(struct net_device *dev)
+{
+ struct net_local *np = (struct net_local *)dev->priv;
+
+ printk(KERN_WARNING "%s: transmit timed out, %s?\n", dev->name,
+ tx_done(dev) ? "IRQ problem" : "network cable problem");
+
+ /* remember we got an error */
+
+ np->stats.tx_errors++;
+
+ /* reset the TX DMA in case it has hung on something */
+
+ RESET_DMA(0);
+ WAIT_DMA(0);
+
+ /* Reset the tranceiver. */
+
+ e100_reset_tranceiver();
+
+ /* and get rid of the packet that never got an interrupt */
+
+ dev_kfree_skb(tx_skb);
+ tx_skb = 0;
+
+ /* tell the upper layers we're ok again */
+
+ netif_wake_queue(dev);
+}
+
+
+/* This will only be invoked if the driver is _not_ in XOFF state.
+ * What this means is that we need not check it, and that this
+ * invariant will hold if we make sure that the netif_*_queue()
+ * calls are done at the proper times.
+ */
+
+static int
+e100_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+ struct net_local *np = (struct net_local *)dev->priv;
+ int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ unsigned char *buf = skb->data;
+
+#ifdef ETHDEBUG
+ printk("send packet len %d\n", length);
+#endif
+ spin_lock_irq(&np->lock); /* protect from tx_interrupt */
+
+ tx_skb = skb; /* remember it so we can free it in the tx irq handler later */
+ dev->trans_start = jiffies;
+
+ e100_hardware_send_packet(buf, length);
+
+ /* this simple TX driver has only one send-descriptor so we're full
+ * directly. If this had a send-ring instead, we would only do this if
+ * the ring got full.
+ */
+
+ netif_stop_queue(dev);
+
+ spin_unlock_irq(&np->lock);
+
+ return 0;
+}
+
+/*
+ * The typical workload of the driver:
+ * Handle the network interface interrupts.
+ */
+
+static void
+e100rx_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ unsigned long irqbits = *R_IRQ_MASK2_RD;
+
+ if(irqbits & (1U << 3)) {
+
+ /* acknowledge the eop interrupt */
+
+ *R_DMA_CH1_CLR_INTR = IO_STATE(R_DMA_CH1_CLR_INTR, clr_eop, do);
+
+ /* check if one or more complete packets were indeed received */
+
+ while(*R_DMA_CH1_FIRST != virt_to_phys(myNextRxDesc)) {
+ /* Take out the buffer and give it to the OS, then
+ * allocate a new buffer to put a packet in.
+ */
+ e100_rx(dev);
+ ((struct net_local *)dev->priv)->stats.rx_packets++;
+ *R_DMA_CH1_CMD = 3; /* restart/continue on the channel, for safety */
+ *R_DMA_CH1_CLR_INTR = 3; /* clear dma channel 1 eop/descr irq bits */
+ /* now, we might have gotten another packet so we have to loop back
+ and check if so */
+ }
+ }
+}
+
+/* the transmit dma channel interrupt
+ *
+ * this is supposed to free the skbuff which was pending during transmission,
+ * and inform the kernel that we can send one more buffer
+ */
+
+static void
+e100tx_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ unsigned long irqbits = *R_IRQ_MASK2_RD;
+ struct net_local *np = (struct net_local *)dev->priv;
+
+ if(irqbits & 2) { /* check for a dma0_eop interrupt */
+
+ /* This protects us from concurrent execution of
+ * our dev->hard_start_xmit function above.
+ */
+
+ spin_lock(&np->lock);
+
+ /* acknowledge the eop interrupt */
+
+ *R_DMA_CH0_CLR_INTR = IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do);
+
+ if(*R_DMA_CH0_FIRST == 0 && tx_skb) {
+ np->stats.tx_bytes += tx_skb->len;
+ np->stats.tx_packets++;
+ /* dma is ready with the transmission of the data in tx_skb, so now
+ we can release the skb memory */
+ dev_kfree_skb_irq(tx_skb);
+ tx_skb = 0;
+ netif_wake_queue(dev);
+ } else {
+ printk("tx weird interrupt\n");
+ }
+
+ spin_unlock(&np->lock);
+ }
+}
+
+static void
+e100nw_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct net_local *np = (struct net_local *)dev->priv;
+ unsigned long irqbits = *R_IRQ_MASK0_RD;
+
+ if(irqbits & (1 << 19)) { /* check for overrun irq */
+ update_rx_stats(&np->stats); /* this will ack the irq */
+ D(printk("ethernet receiver overrun!\n"));
+ }
+ if(irqbits & (1 << 17)) { /* check for excessive collision irq */
+ *R_NETWORK_TR_CTRL = 1 << 8; /* clear the interrupt */
+ np->stats.tx_errors++;
+ D(printk("ethernet excessive collisions!\n"));
+ }
+
+}
+
+/* We have a good packet(s), get it/them out of the buffers. */
+static void
+e100_rx(struct net_device *dev)
+{
+ struct sk_buff *skb;
+ int length=0;
+ int i;
+ struct etrax_dma_descr *mySaveRxDesc = myNextRxDesc;
+ unsigned char *skb_data_ptr;
+
+ /* light the network rx packet depending on the current speed.
+ ** But only if link has been detected.
+ */
+ if (!nolink)
+ if (current_speed == 10) {
+ LED_NETWORK_TX_SET(1);
+ LED_NETWORK_RX_SET(1);
+ } else
+ LED_NETWORK_RX_SET(1);
+
+ /* Set the earliest time we may clear the LED */
+
+ led_clear_time = jiffies + NET_FLASH_TIME;
+
+ /* If the packet is broken down in many small packages then merge
+ * count how much space we will need to alloc with skb_alloc() for
+ * it to fit.
+ */
+
+ while (!(myNextRxDesc->status & d_eop)) {
+ length += myNextRxDesc->sw_len; /* use sw_len for the first descs */
+ myNextRxDesc->status = 0;
+ myNextRxDesc = phys_to_virt(myNextRxDesc->next);
+ }
+
+ length += myNextRxDesc->hw_len; /* use hw_len for the last descr */
+
+#ifdef ETHDEBUG
+ printk("Got a packet of length %d:\n", length);
+ /* dump the first bytes in the packet */
+ skb_data_ptr = (unsigned char *)phys_to_virt(mySaveRxDesc->buf);
+ for(i = 0; i < 8; i++) {
+ printk("%d: %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x\n", i * 8,
+ skb_data_ptr[0],skb_data_ptr[1],skb_data_ptr[2],skb_data_ptr[3],
+ skb_data_ptr[4],skb_data_ptr[5],skb_data_ptr[6],skb_data_ptr[7]);
+ skb_data_ptr += 8;
+ }
+#endif
+
+ skb = dev_alloc_skb(length - ETHER_HEAD_LEN);
+ if (!skb) {
+ printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n",
+ dev->name);
+ return;
+ }
+
+ skb_put(skb, length - ETHER_HEAD_LEN); /* allocate room for the packet body */
+ skb_data_ptr = skb_push(skb, ETHER_HEAD_LEN); /* allocate room for the header */
+
+#if 0
+ printk("head = 0x%x, data = 0x%x, tail = 0x%x, end = 0x%x\n",
+ skb->head, skb->data, skb->tail, skb->end);
+ printk("copying packet to 0x%x.\n", skb_data_ptr);
+#endif
+
+ /* this loop can be made using max two memcpy's if optimized */
+
+ while(mySaveRxDesc != myNextRxDesc) {
+ memcpy(skb_data_ptr, phys_to_virt(mySaveRxDesc->buf),
+ mySaveRxDesc->sw_len);
+ skb_data_ptr += mySaveRxDesc->sw_len;
+ mySaveRxDesc = phys_to_virt(mySaveRxDesc->next);
+ }
+
+ memcpy(skb_data_ptr, phys_to_virt(mySaveRxDesc->buf),
+ mySaveRxDesc->hw_len);
+
+ skb->dev = dev;
+ skb->protocol = eth_type_trans(skb, dev);
+
+ /* Send the packet to the upper layers */
+
+ netif_rx(skb);
+
+ /* Prepare for next packet */
+
+ myNextRxDesc->status = 0;
+ myPrevRxDesc = myNextRxDesc;
+ myNextRxDesc = phys_to_virt(myNextRxDesc->next);
+
+ myPrevRxDesc->ctrl |= d_eol;
+ myLastRxDesc->ctrl &= ~d_eol;
+ myLastRxDesc = myPrevRxDesc;
+
+ return;
+}
+
+/* The inverse routine to net_open(). */
+static int
+e100_close(struct net_device *dev)
+{
+ struct net_local *np = (struct net_local *)dev->priv;
+
+ printk("Closing %s.\n", dev->name);
+
+ netif_stop_queue(dev);
+
+ *R_NETWORK_GEN_CONFIG =
+ IO_STATE(R_NETWORK_GEN_CONFIG, phy, mii_clk) |
+ IO_STATE(R_NETWORK_GEN_CONFIG, enable, off);
+
+ *R_IRQ_MASK0_CLR = 0xe0000; /* clear excessive_col, over/underrun irq mask */
+ *R_IRQ_MASK2_CLR = 0xf; /* clear dma0 and 1 eop and descr irq masks */
+
+ /* Stop the receiver and the transmitter */
+
+ RESET_DMA(0);
+ RESET_DMA(1);
+
+ /* Flush the Tx and disable Rx here. */
+
+ free_irq(NETWORK_DMARX_IRQ, NULL);
+ free_irq(NETWORK_DMATX_IRQ, NULL);
+ free_irq(NETWORK_STATUS_IRQ, NULL);
+
+ free_dma(0);
+ free_dma(1);
+
+ /* Update the statistics here. */
+
+ update_rx_stats(&np->stats);
+ update_tx_stats(&np->stats);
+
+ return 0;
+}
+
+static void
+update_rx_stats(struct net_device_stats *es)
+{
+ unsigned long r = *R_REC_COUNTERS;
+ /* update stats relevant to reception errors */
+ es->rx_fifo_errors += r >> 24; /* fifo overrun */
+ es->rx_crc_errors += r & 0xff; /* crc error */
+ es->rx_frame_errors += (r >> 8) & 0xff; /* alignment error */
+ es->rx_length_errors += (r >> 16) & 0xff; /* oversized frames */
+}
+
+static void
+update_tx_stats(struct net_device_stats *es)
+{
+ unsigned long r = *R_TR_COUNTERS;
+ /* update stats relevant to transmission errors */
+ es->collisions += (r & 0xff) + ((r >> 8) & 0xff); /* single_col + multiple_col */
+ es->tx_errors += (r >> 24) & 0xff; /* deferred transmit frames */
+}
+
+/*
+ * Get the current statistics.
+ * This may be called with the card open or closed.
+ */
+static struct net_device_stats *
+e100_get_stats(struct net_device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+
+ update_rx_stats(&lp->stats);
+ update_tx_stats(&lp->stats);
+
+ return &lp->stats;
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ * num_addrs == -1 Promiscuous mode, receive all packets
+ * num_addrs == 0 Normal mode, clear multicast list
+ * num_addrs > 0 Multicast mode, receive normal and MC packets,
+ * and do best-effort filtering.
+ */
+static void
+set_multicast_list(struct net_device *dev)
+{
+ int num_addr = dev->mc_count;
+ unsigned long int lo_bits;
+ unsigned long int hi_bits;
+ if (num_addr == -1)
+ {
+ /* promiscuous mode */
+ lo_bits = 0xfffffffful;
+ hi_bits = 0xfffffffful;
+ } else if (num_addr == 0) {
+ /* Normal, clear the mc list */
+ lo_bits = 0x00000000ul;
+ hi_bits = 0x00000000ul;
+ } else {
+ /* MC mode, receive normal and MC packets */
+ char hash_ix;
+ struct dev_mc_list *dmi = dev->mc_list;
+ int i;
+ char *baddr;
+ lo_bits = 0x00000000ul;
+ hi_bits = 0x00000000ul;
+ for (i=0; i<num_addr; i++) {
+ /* Calculate the hash index for the GA registers */
+
+ hash_ix = 0;
+ baddr = dmi->dmi_addr;
+ hash_ix ^= (*baddr) & 0x3f;
+ hash_ix ^= ((*baddr) >> 6) & 0x03;
+ ++baddr;
+ hash_ix ^= ((*baddr) << 2) & 0x03c;
+ hash_ix ^= ((*baddr) >> 4) & 0xf;
+ ++baddr;
+ hash_ix ^= ((*baddr) << 4) & 0x30;
+ hash_ix ^= ((*baddr) >> 2) & 0x3f;
+ ++baddr;
+ hash_ix ^= (*baddr) & 0x3f;
+ hash_ix ^= ((*baddr) >> 6) & 0x03;
+ ++baddr;
+ hash_ix ^= ((*baddr) << 2) & 0x03c;
+ hash_ix ^= ((*baddr) >> 4) & 0xf;
+ ++baddr;
+ hash_ix ^= ((*baddr) << 4) & 0x30;
+ hash_ix ^= ((*baddr) >> 2) & 0x3f;
+
+ hash_ix &= 0x3f;
+
+ if (hash_ix > 32) {
+ hi_bits |= (1 << (hash_ix-32));
+ }
+ else {
+ lo_bits |= (1 << hash_ix);
+ }
+ dmi = dmi->next;
+ }
+ }
+ *R_NETWORK_GA_0 = lo_bits;
+ *R_NETWORK_GA_1 = hi_bits;
+}
+
+void
+e100_hardware_send_packet(char *buf, int length)
+{
+ D(printk("e100 send pack, buf 0x%x len %d\n", buf, length));
+ /* light the network leds depending on the current speed.
+ ** But only if link has been detected.
+ */
+ if (!nolink) {
+ if (current_speed == 10) {
+ LED_NETWORK_TX_SET(1);
+ LED_NETWORK_RX_SET(1);
+ } else {
+ LED_NETWORK_RX_SET(1);
+ }
+ }
+
+ /* Set the earliest time we may clear the LED */
+
+ led_clear_time = jiffies + NET_FLASH_TIME;
+
+ /* configure the tx dma descriptor */
+
+ TxDesc.sw_len = length;
+ TxDesc.ctrl = d_eop | d_eol | d_wait;
+ TxDesc.buf = virt_to_phys(buf);
+
+ /* setup the dma channel and start it */
+
+ *R_DMA_CH0_FIRST = virt_to_phys(&TxDesc);
+ *R_DMA_CH0_CMD = IO_STATE(R_DMA_CH0_CMD, cmd, start);
+}
+
+static void
+e100_clear_network_leds(unsigned long dummy)
+{
+ if (jiffies > led_clear_time) {
+ if (nolink)
+ LED_NETWORK_TX_SET(1);
+ else
+ LED_NETWORK_TX_SET(0);
+ LED_NETWORK_RX_SET(0);
+ }
+
+ clear_led_timer.expires = jiffies + 10;
+ add_timer(&clear_led_timer);
+}
+
+static struct net_device dev_etrax_ethernet; /* only got one */
+
+static int
+etrax_init_module(void)
+{
+ struct net_device *d = &dev_etrax_ethernet;
+
+ d->init = etrax_ethernet_init;
+
+ if(register_netdev(d) == 0)
+ return 0;
+ else
+ return -ENODEV;
+}
+
+module_init(etrax_init_module);
diff --git a/arch/cris/drivers/ide.c b/arch/cris/drivers/ide.c
new file mode 100644
index 000000000..e352fdf32
--- /dev/null
+++ b/arch/cris/drivers/ide.c
@@ -0,0 +1,818 @@
+/* $Id: ide.c,v 1.4 2001/01/10 21:14:32 bjornw Exp $
+ *
+ * Etrax specific IDE functions, like init and PIO-mode setting etc.
+ * Almost the entire ide.c is used for the rest of the Etrax ATA driver.
+ * Copyright (c) 2000, 2001 Axis Communications AB
+ *
+ * Authors: Bjorn Wesen (initial version)
+ * Mikael Starvik (pio setup stuff)
+ *
+ * $Log: ide.c,v $
+ * Revision 1.4 2001/01/10 21:14:32 bjornw
+ * Initialize hwif->ideproc, for the new way of handling ide_xxx_data
+ *
+ * Revision 1.3 2000/12/01 17:48:18 bjornw
+ * - atapi_output_bytes now uses DMA
+ * - dma_active check removed - the kernel does proper serializing and it had
+ * a race-condition anyway
+ * - ide_build_dmatable had a nameclash
+ * - re-added the RESET_DMA thingys because sometimes the interface can get
+ * stuck apparently
+ * - added ide_release_dma
+ *
+ * Revision 1.2 2000/11/29 17:31:29 bjornw
+ * 2.4 port
+ *
+ * - The "register addresses" stored in the hwif are now 32-bit fields that
+ * don't need to be shifted into correct positions in R_ATA_CTRL_DATA
+ * - PIO-mode detection temporarily disabled since ide-modes.c is not compiled
+ * - All DMA uses virt_to_phys conversions for DMA buffers and descriptor ptrs
+ * - Probably correct ide_dma_begin semantics in dmaproc now for ATAPI devices
+ * - Removed RESET_DMA when starting a new transfer - why was this necessary ?
+ * - Indentation fix
+ *
+ *
+ */
+
+/* Regarding DMA:
+ *
+ * There are two forms of DMA - "DMA handshaking" between the interface and the drive,
+ * and DMA between the memory and the interface. We can ALWAYS use the latter, since it's
+ * something built-in in the Etrax. However only some drives support the DMA-mode handshaking
+ * on the ATA-bus. The normal PC driver and Triton interface disables memory-if DMA when the
+ * device can't do DMA handshaking for some stupid reason. We don't need to do that.
+ */
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/svinto.h>
+
+/* number of Etrax DMA descriptors */
+#define MAX_DMA_DESCRS 64
+
+#define LOWDB(x)
+#define D(x)
+
+void OUT_BYTE(unsigned char data, ide_ioreg_t reg) {
+ LOWDB(printk("ob: data 0x%x, reg 0x%x\n", data, reg));
+ while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)); /* wait for busy flag */
+ *R_ATA_CTRL_DATA = reg | data; /* write data to the drive's register */
+ while(!(*R_ATA_STATUS_DATA &
+ IO_MASK(R_ATA_STATUS_DATA, tr_rdy))); /* wait for transmitter ready */
+}
+
+unsigned char IN_BYTE(ide_ioreg_t reg) {
+ int status;
+ while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)); /* wait for busy flag */
+ *R_ATA_CTRL_DATA = reg | IO_STATE(R_ATA_CTRL_DATA, rw, read); /* read data */
+ while(!((status = *R_ATA_STATUS_DATA) &
+ IO_MASK(R_ATA_STATUS_DATA, dav))); /* wait for available */
+ LOWDB(printk("inb: 0x%x from reg 0x%x\n", status & 0xff, reg));
+ return (unsigned char)status; /* data was in the lower 16 bits in the status reg */
+}
+
+/* PIO timing (in R_ATA_CONFIG)
+ *
+ * _____________________________
+ * ADDRESS : ________/
+ *
+ * _______________
+ * DIOR : ____________/ \__________
+ *
+ * _______________
+ * DATA : XXXXXXXXXXXXXXXX_______________XXXXXXXX
+ *
+ *
+ * DIOR is unbuffered while address and data is buffered.
+ * This creates two problems:
+ * 1. The DIOR pulse is to early (because it is unbuffered)
+ * 2. The rise time of DIOR is long
+ *
+ * There are at least three different plausible solutions
+ * 1. Use a pad capable of larger currents in Etrax
+ * 2. Use an external buffer
+ * 3. Make the strobe pulse longer
+ *
+ * Some of the strobe timings below are modified to compensate
+ * for this. This implies a slight performance decrease.
+ *
+ * THIS SHOULD NEVER BE CHANGED!
+ *
+ * TODO: Is this true for the latest LX boards still ?
+ */
+
+#define ATA_DMA2_STROBE 4
+#define ATA_DMA2_HOLD 0
+#define ATA_DMA1_STROBE 4
+#define ATA_DMA1_HOLD 1
+#define ATA_DMA0_STROBE 12
+#define ATA_DMA0_HOLD 9
+#define ATA_PIO4_SETUP 1
+#define ATA_PIO4_STROBE 5
+#define ATA_PIO4_HOLD 0
+#define ATA_PIO3_SETUP 1
+#define ATA_PIO3_STROBE 5
+#define ATA_PIO3_HOLD 1
+#define ATA_PIO2_SETUP 1
+#define ATA_PIO2_STROBE 6
+#define ATA_PIO2_HOLD 2
+#define ATA_PIO1_SETUP 2
+#define ATA_PIO1_STROBE 11
+#define ATA_PIO1_HOLD 4
+#define ATA_PIO0_SETUP 4
+#define ATA_PIO0_STROBE 19
+#define ATA_PIO0_HOLD 4
+
+/*
+ * good_dma_drives() lists the model names (from "hdparm -i")
+ * of drives which do not support mword2 DMA but which are
+ * known to work fine with this interface under Linux.
+ */
+
+const char *good_dma_drives[] = {"Micropolis 2112A",
+ "CONNER CTMA 4000",
+ "CONNER CTT8000-A",
+ NULL};
+
+static void tune_e100_ide(ide_drive_t *drive, byte pio)
+{
+ unsigned long flags;
+
+ pio = 4;
+ //pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
+
+ save_flags(flags);
+ cli();
+
+ /* set pio mode! */
+
+ switch(pio) {
+ case 0:
+ *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) |
+ IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) |
+ IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) |
+ IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO0_SETUP ) |
+ IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO0_STROBE ) |
+ IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO0_HOLD ) );
+ break;
+ case 1:
+ *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) |
+ IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) |
+ IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) |
+ IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO1_SETUP ) |
+ IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO1_STROBE ) |
+ IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO1_HOLD ) );
+ break;
+ case 2:
+ *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) |
+ IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) |
+ IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) |
+ IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO2_SETUP ) |
+ IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO2_STROBE ) |
+ IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO2_HOLD ) );
+ break;
+ case 3:
+ *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) |
+ IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) |
+ IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) |
+ IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO3_SETUP ) |
+ IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO3_STROBE ) |
+ IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO3_HOLD ) );
+ break;
+ case 4:
+ *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) |
+ IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) |
+ IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) |
+ IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO4_SETUP ) |
+ IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO4_STROBE ) |
+ IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO4_HOLD ) );
+ break;
+ }
+ restore_flags(flags);
+}
+
+static int e100_dmaproc (ide_dma_action_t func, ide_drive_t *drive); /* defined below */
+static void e100_ideproc (ide_ide_action_t func, ide_drive_t *drive,
+ void *buffer, unsigned int length); /* defined below */
+
+void __init init_e100_ide (void)
+{
+ volatile unsigned int dummy;
+ int h;
+
+ printk("ide: ETRAX 100LX built-in ATA DMA controller\n");
+
+ /* first fill in some stuff in the ide_hwifs fields */
+
+ for(h = 0; h < MAX_HWIFS; h++) {
+ ide_hwif_t *hwif = &ide_hwifs[h];
+ hwif->chipset = ide_etrax100;
+ hwif->tuneproc = &tune_e100_ide;
+ hwif->dmaproc = &e100_dmaproc;
+ hwif->ideproc = &e100_ideproc;
+ }
+ /* actually reset and configure the etrax100 ide/ata interface */
+
+ /* de-assert bus-reset */
+#ifdef CONFIG_ETRAX_IDE_PB7_RESET
+ port_pb_dir_shadow = port_pb_dir_shadow |
+ IO_STATE(R_PORT_PB_DIR, dir7, output);
+ *R_PORT_PB_DIR = port_pb_dir_shadow;
+ REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, 7, 1);
+#endif
+#ifdef CONFIG_ETRAX_IDE_G27_RESET
+ *R_PORT_G_DATA = 0;
+#endif
+
+ *R_ATA_CTRL_DATA = 0;
+ *R_ATA_TRANSFER_CNT = 0;
+ *R_ATA_CONFIG = 0;
+
+ genconfig_shadow = (genconfig_shadow &
+ ~IO_MASK(R_GEN_CONFIG, dma2) &
+ ~IO_MASK(R_GEN_CONFIG, dma3) &
+ ~IO_MASK(R_GEN_CONFIG, ata)) |
+ ( IO_STATE( R_GEN_CONFIG, dma3, ata ) |
+ IO_STATE( R_GEN_CONFIG, dma2, ata ) |
+ IO_STATE( R_GEN_CONFIG, ata, select ) );
+
+ *R_GEN_CONFIG = genconfig_shadow;
+
+#ifdef CONFIG_ETRAX_IDE_CSE1_16_RESET
+ *(volatile long *)(MEM_CSE1_START | MEM_NON_CACHEABLE) = 0;
+#endif
+
+ /* wait some */
+
+ dummy = 1;
+ dummy = 2;
+ dummy = 3;
+
+#ifdef CONFIG_ETRAX_IDE_CSE1_16_RESET
+ *(volatile long *)(MEM_CSE1_START | MEM_NON_CACHEABLE) = 1 << 16;
+ *R_PORT_G_DATA = 0; /* de-assert bus-reset */
+#endif
+
+ /* make a dummy read to set the ata controller in a proper state */
+ dummy = *R_ATA_STATUS_DATA;
+
+ *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) |
+ IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) |
+ IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) |
+ IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO4_SETUP ) |
+ IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO4_STROBE ) |
+ IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO4_HOLD ) );
+
+ *R_ATA_CTRL_DATA = ( IO_STATE( R_ATA_CTRL_DATA, rw, read) |
+ IO_FIELD( R_ATA_CTRL_DATA, addr, 1 ) );
+
+ while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)); /* wait for busy flag*/
+
+ *R_IRQ_MASK0_SET = ( IO_STATE( R_IRQ_MASK0_SET, ata_irq0, set ) |
+ IO_STATE( R_IRQ_MASK0_SET, ata_irq1, set ) |
+ IO_STATE( R_IRQ_MASK0_SET, ata_irq2, set ) |
+ IO_STATE( R_IRQ_MASK0_SET, ata_irq3, set ) );
+
+ printk("ide: waiting 10 seconds for drives to regain consciousness\n");
+
+ h = jiffies + 1000;
+ while(jiffies < h) ;
+
+ /* reset the dma channels we will use */
+
+ RESET_DMA(2);
+ RESET_DMA(3);
+ WAIT_DMA(2);
+ WAIT_DMA(3);
+
+}
+
+static etrax_dma_descr mydescr;
+
+/*
+ * The following routines are mainly used by the ATAPI drivers.
+ *
+ * These routines will round up any request for an odd number of bytes,
+ * so if an odd bytecount is specified, be sure that there's at least one
+ * extra byte allocated for the buffer.
+ */
+static void
+e100_atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount)
+{
+ ide_ioreg_t data_reg = IDE_DATA_REG;
+ unsigned long status;
+
+ D(printk("atapi_input_bytes, dreg 0x%x, buffer 0x%x, count %d\n",
+ data_reg, buffer, bytecount));
+
+ if(bytecount & 1) {
+ printk("warning, odd bytecount in cdrom_in_bytes = %d.\n", bytecount);
+ bytecount++; /* to round off */
+ }
+
+ /* make sure the DMA channel is available */
+ RESET_DMA(3);
+ WAIT_DMA(3);
+
+ /* setup DMA descriptor */
+
+ mydescr.sw_len = bytecount;
+ mydescr.ctrl = d_eol;
+ mydescr.buf = virt_to_phys(buffer);
+
+ /* start the dma channel */
+
+ *R_DMA_CH3_FIRST = virt_to_phys(&mydescr);
+ *R_DMA_CH3_CMD = IO_STATE(R_DMA_CH3_CMD, cmd, start);
+
+ /* initiate a multi word dma read using PIO handshaking */
+
+ *R_ATA_TRANSFER_CNT = bytecount >> 1;
+
+ *R_ATA_CTRL_DATA = data_reg |
+ IO_STATE(R_ATA_CTRL_DATA, rw, read) |
+ IO_STATE(R_ATA_CTRL_DATA, src_dst, dma) |
+ IO_STATE(R_ATA_CTRL_DATA, handsh, pio) |
+ IO_STATE(R_ATA_CTRL_DATA, multi, on) |
+ IO_STATE(R_ATA_CTRL_DATA, dma_size, word);
+
+ /* wait for completion */
+
+ LED_DISK_READ(1);
+ WAIT_DMA(3);
+ LED_DISK_READ(0);
+
+#if 0
+ /* old polled transfer code */
+
+ /* initiate a multi word read */
+
+ *R_ATA_TRANSFER_CNT = wcount << 1;
+
+ *R_ATA_CTRL_DATA = data_reg |
+ IO_STATE(R_ATA_CTRL_DATA, rw, read) |
+ IO_STATE(R_ATA_CTRL_DATA, src_dst, register) |
+ IO_STATE(R_ATA_CTRL_DATA, handsh, pio) |
+ IO_STATE(R_ATA_CTRL_DATA, multi, on) |
+ IO_STATE(R_ATA_CTRL_DATA, dma_size, word);
+
+ /* svinto has a latency until the busy bit actually is set */
+
+ nop(); nop();
+ nop(); nop();
+ nop(); nop();
+ nop(); nop();
+ nop(); nop();
+
+ /* unit should be busy during multi transfer */
+ while((status = *R_ATA_STATUS_DATA) & IO_MASK(R_ATA_STATUS_DATA, busy)) {
+ while(!(status & IO_MASK(R_ATA_STATUS_DATA, dav)))
+ status = *R_ATA_STATUS_DATA;
+ *ptr++ = (unsigned short)(status & 0xffff);
+ }
+#endif
+}
+
+static void
+e100_atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount)
+{
+ ide_ioreg_t data_reg = IDE_DATA_REG;
+ unsigned short *ptr = (unsigned short *)buffer;
+ unsigned long ctrl;
+
+ D(printk("atapi_output_bytes, dreg 0x%x, buffer 0x%x, count %d\n",
+ data_reg, buffer, bytecount));
+
+ if(bytecount & 1) {
+ printk("odd bytecount %d in atapi_out_bytes!\n", bytecount);
+ bytecount++;
+ }
+
+ /* make sure the DMA channel is available */
+ RESET_DMA(2);
+ WAIT_DMA(2);
+
+ /* setup DMA descriptor */
+
+ mydescr.sw_len = bytecount;
+ mydescr.ctrl = d_eol;
+ mydescr.buf = virt_to_phys(buffer);
+
+ /* start the dma channel */
+
+ *R_DMA_CH2_FIRST = virt_to_phys(&mydescr);
+ *R_DMA_CH2_CMD = IO_STATE(R_DMA_CH2_CMD, cmd, start);
+
+ /* initiate a multi word dma write using PIO handshaking */
+
+ *R_ATA_TRANSFER_CNT = bytecount >> 1;
+
+ *R_ATA_CTRL_DATA = data_reg |
+ IO_STATE(R_ATA_CTRL_DATA, rw, write) |
+ IO_STATE(R_ATA_CTRL_DATA, src_dst, dma) |
+ IO_STATE(R_ATA_CTRL_DATA, handsh, pio) |
+ IO_STATE(R_ATA_CTRL_DATA, multi, on) |
+ IO_STATE(R_ATA_CTRL_DATA, dma_size, word);
+
+ /* wait for completion */
+
+ LED_DISK_WRITE(1);
+ WAIT_DMA(2);
+ LED_DISK_WRITE(0);
+
+#if 0
+ /* old polled write code */
+
+ while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)); /* wait for busy flag */
+
+ /* initiate a multi word write */
+
+ *R_ATA_TRANSFER_CNT = bytecount >> 1;
+
+ ctrl = data_reg |
+ IO_STATE(R_ATA_CTRL_DATA, rw, write) |
+ IO_STATE(R_ATA_CTRL_DATA, src_dst, register) |
+ IO_STATE(R_ATA_CTRL_DATA, handsh, pio) |
+ IO_STATE(R_ATA_CTRL_DATA, multi, on) |
+ IO_STATE(R_ATA_CTRL_DATA, dma_size, word);
+
+ LED_DISK_WRITE(1);
+
+ /* Etrax will set busy = 1 until the multi pio transfer has finished
+ * and tr_rdy = 1 after each succesful word transfer.
+ * When the last byte has been transferred Etrax will first set tr_tdy = 1
+ * and then busy = 0 (not in the same cycle). If we read busy before it
+ * has been set to 0 we will think that we should transfer more bytes
+ * and then tr_rdy would be 0 forever. This is solved by checking busy
+ * in the inner loop.
+ */
+
+ do {
+ *R_ATA_CTRL_DATA = ctrl | *ptr++;
+ while(!(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, tr_rdy)) &&
+ (*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)));
+ } while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy));
+
+ LED_DISK_WRITE(0);
+#endif
+}
+
+/*
+ * This is used for most PIO data transfers *from* the IDE interface
+ */
+static void
+e100_ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
+{
+ e100_atapi_input_bytes(drive, buffer, wcount << 2);
+}
+
+/*
+ * This is used for most PIO data transfers *to* the IDE interface
+ */
+static void
+e100_ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
+{
+ e100_atapi_output_bytes(drive, buffer, wcount << 2);
+}
+
+/*
+ * The multiplexor for ide_xxxput_data and atapi calls
+ */
+static void
+e100_ideproc (ide_ide_action_t func, ide_drive_t *drive,
+ void *buffer, unsigned int length)
+{
+ switch (func) {
+ case ideproc_ide_input_data:
+ e100_ide_input_data(drive, buffer, length);
+ break;
+ case ideproc_ide_output_data:
+ e100_ide_input_data(drive, buffer, length);
+ break;
+ case ideproc_atapi_input_bytes:
+ e100_atapi_input_bytes(drive, buffer, length);
+ break;
+ case ideproc_atapi_output_bytes:
+ e100_atapi_output_bytes(drive, buffer, length);
+ break;
+ default:
+ printk("e100_ideproc: unsupported func %d!\n", func);
+ break;
+ }
+}
+
+/* we only have one DMA channel on the chip for ATA, so we can keep these statically */
+static etrax_dma_descr ata_descrs[MAX_DMA_DESCRS];
+static unsigned int ata_tot_size;
+
+/*
+ * e100_ide_build_dmatable() prepares a dma request.
+ * Returns 0 if all went okay, returns 1 otherwise.
+ */
+static int e100_ide_build_dmatable (ide_drive_t *drive)
+{
+ struct request *rq = HWGROUP(drive)->rq;
+ struct buffer_head *bh = rq->bh;
+ unsigned long size, addr;
+ unsigned int count = 0;
+
+ ata_tot_size = 0;
+
+ do {
+ /*
+ * Determine addr and size of next buffer area. We assume that
+ * individual virtual buffers are always composed linearly in
+ * physical memory. For example, we assume that any 8kB buffer
+ * is always composed of two adjacent physical 4kB pages rather
+ * than two possibly non-adjacent physical 4kB pages.
+ */
+ if (bh == NULL) { /* paging and tape requests have (rq->bh == NULL) */
+ addr = virt_to_phys (rq->buffer);
+ size = rq->nr_sectors << 9;
+ } else {
+ /* group sequential buffers into one large buffer */
+ addr = virt_to_phys (bh->b_data);
+ size = bh->b_size;
+ while ((bh = bh->b_reqnext) != NULL) {
+ if ((addr + size) != virt_to_phys (bh->b_data))
+ break;
+ size += bh->b_size;
+ }
+ }
+
+ /* did we run out of descriptors? */
+
+ if(count >= MAX_DMA_DESCRS) {
+ printk("%s: too few DMA descriptors\n", drive->name);
+ return 1;
+ }
+
+ /* uh.. I'm lazy.. if size >= 65536, it should loop below and split it in
+ more than one descriptor */
+
+ if(size >= 65536) {
+ printk("too large ATA DMA request block, size = %d!\n", size);
+ return 1;
+ }
+
+ /* however, this case is more difficult - R_ATA_TRANSFER_CNT cannot be more
+ than 65536 words per transfer, so in that case we need to either
+ 1) use a DMA interrupt to re-trigger R_ATA_TRANSFER_CNT and continue with
+ the descriptors, or
+ 2) simply do the request here, and get dma_intr to only ide_end_request on
+ those blocks that were actually set-up for transfer.
+ */
+
+ if(ata_tot_size + size >= 131072) {
+ printk("too large total ATA DMA request, %d + %d!\n", ata_tot_size, size);
+ return 1;
+ }
+
+ /* ok we want to do IO at addr, size bytes. set up a new descriptor entry */
+
+ ata_descrs[count].sw_len = size;
+ ata_descrs[count].ctrl = 0;
+ ata_descrs[count].buf = addr;
+ ata_descrs[count].next = virt_to_phys(&ata_descrs[count + 1]);
+ count++;
+ ata_tot_size += size;
+
+ } while (bh != NULL);
+
+ if (count) {
+ /* set the end-of-list flag on the last descriptor */
+ ata_descrs[count - 1].ctrl |= d_eol;
+ /* return and say all is ok */
+ return 0;
+ }
+
+ printk("%s: empty DMA table?\n", drive->name);
+ return 1; /* let the PIO routines handle this weirdness */
+}
+
+static int config_drive_for_dma (ide_drive_t *drive)
+{
+ const char **list;
+ struct hd_driveid *id = drive->id;
+
+ if (id && (id->capability & 1)) {
+ /* Enable DMA on any drive that supports mword2 DMA */
+ if ((id->field_valid & 2) && (id->dma_mword & 0x404) == 0x404) {
+ drive->using_dma = 1;
+ return 0; /* DMA enabled */
+ }
+
+ /* Consult the list of known "good" drives */
+ list = good_dma_drives;
+ while (*list) {
+ if (!strcmp(*list++,id->model)) {
+ drive->using_dma = 1;
+ return 0; /* DMA enabled */
+ }
+ }
+ }
+ return 1; /* DMA not enabled */
+}
+
+/*
+ * etrax_dma_intr() is the handler for disk read/write DMA interrupts
+ */
+static ide_startstop_t etrax_dma_intr (ide_drive_t *drive)
+{
+ int i, dma_stat;
+ byte stat;
+
+ LED_DISK_READ(0);
+ LED_DISK_WRITE(0);
+
+ dma_stat = HWIF(drive)->dmaproc(ide_dma_end, drive);
+ stat = GET_STAT(); /* get drive status */
+ if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) {
+ if (!dma_stat) {
+ struct request *rq;
+ rq = HWGROUP(drive)->rq;
+ for (i = rq->nr_sectors; i > 0;) {
+ i -= rq->current_nr_sectors;
+ ide_end_request(1, HWGROUP(drive));
+ }
+ return ide_stopped;
+ }
+ printk("%s: bad DMA status\n", drive->name);
+ }
+ return ide_error(drive, "dma_intr", stat);
+}
+
+/*
+ * e100_dmaproc() initiates/aborts DMA read/write operations on a drive.
+ *
+ * The caller is assumed to have selected the drive and programmed the drive's
+ * sector address using CHS or LBA. All that remains is to prepare for DMA
+ * and then issue the actual read/write DMA/PIO command to the drive.
+ *
+ * For ATAPI devices, we just prepare for DMA and return. The caller should
+ * then issue the packet command to the drive and call us again with
+ * ide_dma_begin afterwards.
+ *
+ * Returns 0 if all went well.
+ * Returns 1 if DMA read/write could not be started, in which case
+ * the caller should revert to PIO for the current request.
+ */
+
+static int e100_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+ static unsigned int reading; /* static to support ide_dma_begin semantics */
+ int atapi = 0;
+
+ D(printk("e100_dmaproc func %d\n", func));
+
+ switch (func) {
+ case ide_dma_verbose:
+ return 0;
+ case ide_dma_check:
+ return config_drive_for_dma (drive);
+ case ide_dma_off:
+ case ide_dma_off_quietly:
+ /* ok.. we don't really need to do anything I think. */
+ return 0;
+ case ide_dma_write:
+ reading = 0;
+ break;
+ case ide_dma_read:
+ reading = 1;
+ break;
+ case ide_dma_begin:
+ /* begin DMA, used by ATAPI devices which want to issue the
+ * appropriate IDE command themselves.
+ *
+ * they have already called ide_dma_read/write to set the
+ * static reading flag, now they call ide_dma_begin to do
+ * the real stuff. we tell our code below not to issue
+ * any IDE commands itself and jump into it.
+ */
+ atapi++;
+ goto dma_begin;
+ case ide_dma_end: /* returns 1 on error, 0 otherwise */
+ /* TODO: check if something went wrong with the DMA */
+ return 0;
+
+ default:
+ printk("e100_dmaproc: unsupported func %d\n", func);
+ return 1;
+ }
+
+ /* ATAPI-devices (not disks) first call ide_dma_read/write to set the direction
+ * then they call ide_dma_begin after they have issued the appropriate drive command
+ * themselves to actually start the chipset DMA. so we just return here if we're
+ * not a diskdrive.
+ */
+
+ if (drive->media != ide_disk)
+ return 0;
+
+ dma_begin:
+
+ if(reading) {
+
+ RESET_DMA(3); /* sometimes the DMA channel get stuck so we need to do this */
+ WAIT_DMA(3);
+
+ /* set up the Etrax DMA descriptors */
+
+ if(e100_ide_build_dmatable (drive))
+ return 1;
+
+ if(!atapi) {
+ /* set the irq handler which will finish the request when DMA is done */
+
+ ide_set_handler(drive, &etrax_dma_intr, WAIT_CMD, NULL);
+
+ /* issue cmd to drive */
+
+ OUT_BYTE(WIN_READDMA, IDE_COMMAND_REG);
+ }
+
+ /* begin DMA */
+
+ *R_DMA_CH3_FIRST = virt_to_phys(ata_descrs);
+ *R_DMA_CH3_CMD = IO_STATE(R_DMA_CH3_CMD, cmd, start);
+
+ /* initiate a multi word dma read using DMA handshaking */
+
+ *R_ATA_TRANSFER_CNT = ata_tot_size >> 1;
+
+ *R_ATA_CTRL_DATA = IDE_DATA_REG |
+ IO_STATE(R_ATA_CTRL_DATA, rw, read) |
+ IO_STATE(R_ATA_CTRL_DATA, src_dst, dma) |
+ IO_STATE(R_ATA_CTRL_DATA, handsh, dma) |
+ IO_STATE(R_ATA_CTRL_DATA, multi, on) |
+ IO_STATE(R_ATA_CTRL_DATA, dma_size, word);
+
+ LED_DISK_READ(1);
+
+ D(printk("dma read of %d bytes.\n", ata_tot_size));
+
+ } else {
+ /* writing */
+
+ RESET_DMA(2); /* sometimes the DMA channel get stuck so we need to do this */
+ WAIT_DMA(2);
+
+ /* set up the Etrax DMA descriptors */
+
+ if(e100_ide_build_dmatable (drive))
+ return 1;
+
+ if(!atapi) {
+ /* set the irq handler which will finish the request when DMA is done */
+
+ ide_set_handler(drive, &etrax_dma_intr, WAIT_CMD, NULL);
+
+ /* issue cmd to drive */
+
+ OUT_BYTE(WIN_WRITEDMA, IDE_COMMAND_REG);
+ }
+
+ /* begin DMA */
+
+ *R_DMA_CH2_FIRST = virt_to_phys(ata_descrs);
+ *R_DMA_CH2_CMD = IO_STATE(R_DMA_CH2_CMD, cmd, start);
+
+ /* initiate a multi word dma write using DMA handshaking */
+
+ *R_ATA_TRANSFER_CNT = ata_tot_size >> 1;
+
+ *R_ATA_CTRL_DATA = IDE_DATA_REG |
+ IO_STATE(R_ATA_CTRL_DATA, rw, write) |
+ IO_STATE(R_ATA_CTRL_DATA, src_dst, dma) |
+ IO_STATE(R_ATA_CTRL_DATA, handsh, dma) |
+ IO_STATE(R_ATA_CTRL_DATA, multi, on) |
+ IO_STATE(R_ATA_CTRL_DATA, dma_size, word);
+
+ LED_DISK_WRITE(1);
+
+ D(printk("dma write of %d bytes.\n", ata_tot_size));
+ }
+
+ /* DMA started successfully */
+ return 0;
+}
+
+/* ide.c calls this, but we don't need to do anything particular */
+
+int ide_release_dma (ide_hwif_t *hwif)
+{
+ return 1;
+}
diff --git a/arch/cris/drivers/serial.c b/arch/cris/drivers/serial.c
new file mode 100644
index 000000000..2dcb17a5e
--- /dev/null
+++ b/arch/cris/drivers/serial.c
@@ -0,0 +1,3039 @@
+/* $Id: serial.c,v 1.6 2000/11/22 16:36:09 bjornw Exp $
+ *
+ * Serial port driver for the ETRAX 100LX chip
+ *
+ * Copyright (C) 1998, 1999, 2000 Axis Communications AB
+ *
+ * Many, many authors. Based once upon a time on serial.c for 16x50.
+ *
+ * $Log: serial.c,v $
+ * Revision 1.6 2000/11/22 16:36:09 bjornw
+ * Please marketing by using the correct case when spelling Etrax.
+ *
+ * Revision 1.5 2000/11/21 16:43:37 bjornw
+ * Fixed so it compiles under CONFIG_SVINTO_SIM
+ *
+ * Revision 1.4 2000/11/15 17:34:12 bjornw
+ * Added a timeout timer for flushing input channels. The interrupt-based
+ * fast flush system should be easy to merge with this later (works the same
+ * way, only with an irq instead of a system timer_list)
+ *
+ * Revision 1.3 2000/11/13 17:19:57 bjornw
+ * * Incredibly, this almost complete rewrite of serial.c worked (at least
+ * for output) the first time.
+ *
+ * Items worth noticing:
+ *
+ * No Etrax100 port 1 workarounds (does only compile on 2.4 anyway now)
+ * RS485 is not ported (why cant it be done in userspace as on x86 ?)
+ * Statistics done through async_icount - if any more stats are needed,
+ * that's the place to put them or in an arch-dep version of it.
+ * timeout_interrupt and the other fast timeout stuff not ported yet
+ * There be dragons in this 3k+ line driver
+ *
+ * Revision 1.2 2000/11/10 16:50:28 bjornw
+ * First shot at a 2.4 port, does not compile totally yet
+ *
+ * Revision 1.1 2000/11/10 16:47:32 bjornw
+ * Added verbatim copy of rev 1.49 etrax100ser.c from elinux
+ *
+ * Revision 1.49 2000/10/30 15:47:14 tobiasa
+ * Changed version number.
+ *
+ * Revision 1.48 2000/10/25 11:02:43 johana
+ * Changed %ul to %lu in printf's
+ *
+ * Revision 1.47 2000/10/18 15:06:53 pkj
+ * Compile correctly with CONFIG_ETRAX100_SERIAL_FLUSH_DMA_FAST and
+ * CONFIG_SERIAL_PROC_ENTRY together.
+ * Some clean-up of the /proc/serial file.
+ *
+ * Revision 1.46 2000/10/16 12:59:40 johana
+ * Added CONFIG_SERIAL_PROC_ENTRY for statistics and debug info.
+ *
+ * Revision 1.45 2000/10/13 17:10:59 pkj
+ * Do not flush DMAs while flipping TTY buffers.
+ *
+ * Revision 1.44 2000/10/13 16:34:29 pkj
+ * Added a delay in ser_interrupt() for 2.3ms when an error is detected.
+ * We do not know why this delay is required yet, but without it the
+ * irmaflash program does not work (this was the program that needed
+ * the ser_interrupt() to be needed in the first place). This should not
+ * affect normal use of the serial ports.
+ *
+ * Revision 1.43 2000/10/13 16:30:44 pkj
+ * New version of the fast flush of serial buffers code. This time
+ * it is localized to the serial driver and uses a fast timer to
+ * do the work.
+ *
+ * Revision 1.42 2000/10/13 14:54:26 bennyo
+ * Fix for switching RTS when using rs485
+ *
+ * Revision 1.41 2000/10/12 11:43:44 pkj
+ * Cleaned up a number of comments.
+ *
+ * Revision 1.40 2000/10/10 11:58:39 johana
+ * Made RS485 support generic for all ports.
+ * Toggle rts in interrupt if no delay wanted.
+ * WARNING: No true transmitter empty check??
+ * Set d_wait bit when sending data so interrupt is delayed until
+ * fifo flushed. (Fix tcdrain() problem)
+ *
+ * Revision 1.39 2000/10/04 16:08:02 bjornw
+ * * Use virt_to_phys etc. for DMA addresses
+ * * Removed CONFIG_FLUSH_DMA_FAST hacks
+ * * Indentation fix
+ *
+ * Revision 1.38 2000/10/02 12:27:10 mattias
+ * * added variable used when using fast flush on serial dma.
+ * (CONFIG_FLUSH_DMA_FAST)
+ *
+ * Revision 1.37 2000/09/27 09:44:24 pkj
+ * Uncomment definition of SERIAL_HANDLE_EARLY_ERRORS.
+ *
+ * Revision 1.36 2000/09/20 13:12:52 johana
+ * Support for CONFIG_ETRAX100_SERIAL_RX_TIMEOUT_TICKS:
+ * Number of timer ticks between flush of receive fifo (1 tick = 10ms).
+ * Try 0-3 for low latency applications. Approx 5 for high load
+ * applications (e.g. PPP). Maybe this should be more adaptive some day...
+ *
+ * Revision 1.35 2000/09/20 10:36:08 johana
+ * Typo in get_lsr_info()
+ *
+ * Revision 1.34 2000/09/20 10:29:59 johana
+ * Let rs_chars_in_buffer() check fifo content as well.
+ * get_lsr_info() might work now (not tested).
+ * Easier to change the port to debug.
+ *
+ * Revision 1.33 2000/09/13 07:52:11 torbjore
+ * Support RS485
+ *
+ * Revision 1.32 2000/08/31 14:45:37 bjornw
+ * After sending a break we need to reset the transmit DMA channel
+ *
+ * Revision 1.31 2000/06/21 12:13:29 johana
+ * Fixed wait for all chars sent when closing port.
+ * (Used to always take 1 second!)
+ * Added shadows for directions of status/ctrl signals.
+ *
+ * Revision 1.30 2000/05/29 16:27:55 bjornw
+ * Simulator ifdef moved a bit
+ *
+ * Revision 1.29 2000/05/09 09:40:30 mattias
+ * * Added description of dma registers used in timeout_interrupt
+ * * Removed old code
+ *
+ * Revision 1.28 2000/05/08 16:38:58 mattias
+ * * Bugfix for flushing fifo in timeout_interrupt
+ * Problem occurs when bluetooth stack waits for a small number of bytes
+ * containing an event acknowledging free buffers in bluetooth HW
+ * As before, data was stuck in fifo until more data came on uart and
+ * flushed it up to the stack.
+ *
+ * Revision 1.27 2000/05/02 09:52:28 jonasd
+ * Added fix for peculiar etrax behaviour when eop is forced on an empty
+ * fifo. This is used when flashing the IRMA chip. Disabled by default.
+ *
+ * Revision 1.26 2000/03/29 15:32:02 bjornw
+ * 2.0.34 updates
+ *
+ * Revision 1.25 2000/02/16 16:59:36 bjornw
+ * * Receive DMA directly into the flip-buffer, eliminating an intermediary
+ * receive buffer and a memcpy. Will avoid some overruns.
+ * * Error message on debug port if an overrun or flip buffer overrun occurs.
+ * * Just use the first byte in the flag flip buffer for errors.
+ * * Check for timeout on the serial ports only each 5/100 s, not 1/100.
+ *
+ * Revision 1.24 2000/02/09 18:02:28 bjornw
+ * * Clear serial errors (overrun, framing, parity) correctly. Before, the
+ * receiver would get stuck if an error occured and we did not restart
+ * the input DMA.
+ * * Cosmetics (indentation, some code made into inlines)
+ * * Some more debug options
+ * * Actually shut down the serial port (DMA irq, DMA reset, receiver stop)
+ * when the last open is closed. Corresponding fixes in startup().
+ * * rs_close() "tx FIFO wait" code moved into right place, bug & -> && fixed
+ * and make a special case out of port 1 (R_DMA_CHx_STATUS is broken for that)
+ * * e100_disable_rx/enable_rx just disables/enables the receiver, not RTS
+ *
+ * Revision 1.23 2000/01/24 17:46:19 johana
+ * Wait for flush of DMA/FIFO when closing port.
+ *
+ * Revision 1.22 2000/01/20 18:10:23 johana
+ * Added TIOCMGET ioctl to return modem status.
+ * Implemented modem status/control that works with the extra signals
+ * (DTR, DSR, RI,CD) as well.
+ * 3 different modes supported:
+ * ser0 on PB (Bundy), ser1 on PB (Lisa) and ser2 on PA (Bundy)
+ * Fixed DEF_TX value that caused the serial transmitter pin (txd) to go to 0 when
+ * closing the last filehandle, NASTY!.
+ * Added break generation, not tested though!
+ * Use SA_SHIRQ when request_irq() for ser2 and ser3 (shared with) par0 and par1.
+ * You can't use them at the same time (yet..), but you can hopefully switch
+ * between ser2/par0, ser3/par1 with the same kernel config.
+ * Replaced some magic constants with defines
+ *
+ *
+ */
+
+static char *serial_version = "$Revision: 1.6 $";
+
+#include <linux/config.h>
+#include <linux/version.h>
+
+#include <linux/types.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/fcntl.h>
+#include <linux/mm.h>
+#if (LINUX_VERSION_CODE >= 131343)
+#include <linux/init.h>
+#endif
+#if (LINUX_VERSION_CODE >= 131336)
+#include <asm/uaccess.h>
+#endif
+#include <linux/kernel.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+#include <asm/delay.h>
+
+#include <asm/svinto.h>
+
+/* non-arch dependant serial structures are in linux/serial.h */
+#include <linux/serial.h>
+/* while we keep our own stuff (struct e100_serial) in a local .h file */
+#include "serial.h"
+
+/*
+ * All of the compatibilty code so we can compile serial.c against
+ * older kernels is hidden in serial_compat.h
+ */
+#if defined(LOCAL_HEADERS) || (LINUX_VERSION_CODE < 0x020317) /* 2.3.23 */
+#include "serial_compat.h"
+#endif
+
+static DECLARE_TASK_QUEUE(tq_serial);
+
+struct tty_driver serial_driver, callout_driver;
+static int serial_refcount;
+
+/* serial subtype definitions */
+#ifndef SERIAL_TYPE_NORMAL
+#define SERIAL_TYPE_NORMAL 1
+#define SERIAL_TYPE_CALLOUT 2
+#endif
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+//#define SERIAL_DEBUG_INTR
+//#define SERIAL_DEBUG_OPEN
+//#define SERIAL_DEBUG_FLOW
+//#define SERIAL_DEBUG_DATA
+//#define SERIAL_DEBUG_THROTTLE
+//#define SERIAL_DEBUG_IO /* Debug for Extra control and status pins */
+#define SERIAL_DEBUG_LINE 0 /* What serport we want to debug */
+
+/* Enable this to use serial interrupts to handle when you
+ expect the first received event on the serial port to
+ be an error, break or similar. Used to be able to flash IRMA
+ from eLinux */
+//#define SERIAL_HANDLE_EARLY_ERRORS
+
+
+#ifndef CONFIG_ETRAX100_SERIAL_RX_TIMEOUT_TICKS
+/* Default number of timer ticks before flushing rx fifo
+ * When using "little data, low latency applications: use 0
+ * When using "much data applications (PPP)" use ~5
+ */
+#define CONFIG_ETRAX100_SERIAL_RX_TIMEOUT_TICKS 5
+#endif
+
+#define MAX_FLUSH_TIME 8
+
+#define _INLINE_ inline
+
+static void change_speed(struct e100_serial *info);
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
+static int rs_write(struct tty_struct * tty, int from_user,
+ const unsigned char *buf, int count);
+
+#define DEF_BAUD 0x99 /* 115.2 kbit/s */
+#define STD_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST )
+#define DEF_RX 0x20 /* or SERIAL_CTRL_W >> 8 */
+/* Default value of tx_ctrl register: has txd(bit 7)=1 (idle) as default */
+#define DEF_TX 0x80 /* or SERIAL_CTRL_B */
+
+/* offsets from R_SERIALx_CTRL */
+
+#define REG_DATA 0
+#define REG_TR_DATA 0
+#define REG_STATUS 1
+#define REG_TR_CTRL 1
+#define REG_REC_CTRL 2
+#define REG_BAUD 3
+#define REG_XOFF 4 /* this is a 32 bit register */
+
+/* this is the data for the four serial ports in the etrax100 */
+/* DMA2(ser2), DMA4(ser3), DMA6(ser0) or DMA8(ser1) */
+/* R_DMA_CHx_CLR_INTR, R_DMA_CHx_FIRST, R_DMA_CHx_CMD */
+
+static struct e100_serial rs_table[] = {
+ { DEF_BAUD, (unsigned char *)R_SERIAL0_CTRL, 1U << 12, /* uses DMA 6 and 7 */
+ R_DMA_CH6_CLR_INTR, R_DMA_CH6_FIRST, R_DMA_CH6_CMD,
+ R_DMA_CH6_STATUS, R_DMA_CH6_HWSW,
+ R_DMA_CH7_CLR_INTR, R_DMA_CH7_FIRST, R_DMA_CH7_CMD,
+ R_DMA_CH7_STATUS, R_DMA_CH7_HWSW,
+ STD_FLAGS, DEF_RX, DEF_TX, 2 }, /* ttyS0 */
+#ifndef CONFIG_SVINTO_SIM
+ { DEF_BAUD, (unsigned char *)R_SERIAL1_CTRL, 1U << 16, /* uses DMA 8 and 9 */
+ R_DMA_CH8_CLR_INTR, R_DMA_CH8_FIRST, R_DMA_CH8_CMD,
+ R_DMA_CH8_STATUS, R_DMA_CH8_HWSW,
+ R_DMA_CH9_CLR_INTR, R_DMA_CH9_FIRST, R_DMA_CH9_CMD,
+ R_DMA_CH9_STATUS, R_DMA_CH9_HWSW,
+ STD_FLAGS, DEF_RX, DEF_TX, 3 }, /* ttyS1 */
+
+ { DEF_BAUD, (unsigned char *)R_SERIAL2_CTRL, 1U << 4, /* uses DMA 2 and 3 */
+ R_DMA_CH2_CLR_INTR, R_DMA_CH2_FIRST, R_DMA_CH2_CMD,
+ R_DMA_CH2_STATUS, R_DMA_CH2_HWSW,
+ R_DMA_CH3_CLR_INTR, R_DMA_CH3_FIRST, R_DMA_CH3_CMD,
+ R_DMA_CH3_STATUS, R_DMA_CH3_HWSW,
+ STD_FLAGS, DEF_RX, DEF_TX, 0 }, /* ttyS2 */
+
+ { DEF_BAUD, (unsigned char *)R_SERIAL3_CTRL, 1U << 8, /* uses DMA 4 and 5 */
+ R_DMA_CH4_CLR_INTR, R_DMA_CH4_FIRST, R_DMA_CH4_CMD,
+ R_DMA_CH4_STATUS, R_DMA_CH4_HWSW,
+ R_DMA_CH5_CLR_INTR, R_DMA_CH5_FIRST, R_DMA_CH5_CMD,
+ R_DMA_CH5_STATUS, R_DMA_CH5_HWSW,
+ STD_FLAGS, DEF_RX, DEF_TX, 1 } /* ttyS3 */
+#endif
+};
+
+
+#define NR_PORTS (sizeof(rs_table)/sizeof(struct e100_serial))
+
+static struct tty_struct *serial_table[NR_PORTS];
+static struct termios *serial_termios[NR_PORTS];
+static struct termios *serial_termios_locked[NR_PORTS];
+
+
+/* RS-485 */
+#if defined(CONFIG_RS485)
+#if defined(CONFIG_RS485_ON_PA)
+static int rs485_pa_bit = CONFIG_RS485_ON_PA_BIT;
+#endif
+#endif
+
+
+/* For now we assume that all bits are on the same port for each serial port */
+
+/* Dummy shadow variables */
+static unsigned char dummy_ser0 = 0x00;
+static unsigned char dummy_ser1 = 0x00;
+static unsigned char dummy_ser2 = 0x00;
+static unsigned char dummy_ser3 = 0x00;
+
+static unsigned char dummy_dir_ser0 = 0x00;
+static unsigned char dummy_dir_ser1 = 0x00;
+static unsigned char dummy_dir_ser2 = 0x00;
+static unsigned char dummy_dir_ser3 = 0x00;
+
+/* Info needed for each ports extra control/status signals.
+ We only supports that all pins uses same register for each port */
+struct control_pins
+{
+ volatile unsigned char *port;
+ volatile unsigned char *shadow;
+ volatile unsigned char *dir_shadow;
+
+ unsigned char dtr_bit;
+ unsigned char ri_bit;
+ unsigned char dsr_bit;
+ unsigned char cd_bit;
+};
+
+static const struct control_pins e100_modem_pins[NR_PORTS] =
+{
+/* Ser 0 */
+ {
+#if defined(CONFIG_ETRAX100_SER0_DTR_RI_DSR_CD_ON_PB)
+ R_PORT_PB_DATA, &port_pb_data_shadow, &port_pb_dir_shadow,
+ CONFIG_ETRAX100_SER0_DTR_ON_PB_BIT,
+ CONFIG_ETRAX100_SER0_RI_ON_PB_BIT,
+ CONFIG_ETRAX100_SER0_DSR_ON_PB_BIT,
+ CONFIG_ETRAX100_SER0_CD_ON_PB_BIT
+#else
+ &dummy_ser0, &dummy_ser0, &dummy_dir_ser0, 0, 1, 2, 3
+#endif
+ },
+/* Ser 1 */
+ {
+#if defined(CONFIG_ETRAX100_SER1_DTR_RI_DSR_CD_ON_PB)
+ R_PORT_PB_DATA, &port_pb_data_shadow, &port_pb_dir_shadow,
+ CONFIG_ETRAX100_SER1_DTR_ON_PB_BIT,
+ CONFIG_ETRAX100_SER1_RI_ON_PB_BIT,
+ CONFIG_ETRAX100_SER1_DSR_ON_PB_BIT,
+ CONFIG_ETRAX100_SER1_CD_ON_PB_BIT
+#else
+ &dummy_ser1, &dummy_ser1, &dummy_dir_ser1, 0, 1, 2, 3
+#endif
+ },
+/* Ser 2 */
+ {
+#if defined(CONFIG_ETRAX100_SER2_DTR_RI_DSR_CD_ON_PA)
+ R_PORT_PA_DATA, &port_pa_data_shadow, &port_pa_dir_shadow,
+ CONFIG_ETRAX100_SER2_DTR_ON_PA_BIT,
+ CONFIG_ETRAX100_SER2_RI_ON_PA_BIT,
+ CONFIG_ETRAX100_SER2_DSR_ON_PA_BIT,
+ CONFIG_ETRAX100_SER2_CD_ON_PA_BIT
+#else
+ &dummy_ser2, &dummy_ser2, &dummy_dir_ser2, 0, 1, 2, 3
+#endif
+ },
+/* Ser 3 */
+ {
+ &dummy_ser3, &dummy_ser3, &dummy_dir_ser3, 0, 1, 2, 3
+ }
+};
+
+#if defined(CONFIG_RS485) && defined(CONFIG_RS485_ON_PA)
+unsigned char rs485_pa_port = CONFIG_RS485_ON_PA_BIT;
+#endif
+
+#define E100_RTS_MASK 0x20
+#define E100_CTS_MASK 0x40
+
+
+/* All serial port signals are active low:
+ * active = 0 -> 3.3V to RS-232 driver -> -12V on RS-232 level
+ * inactive = 1 -> 0V to RS-232 driver -> +12V on RS-232 level
+ *
+ * These macros returns the pin value: 0=0V, >=1 = 3.3V on ETRAX chip
+ */
+
+/* Output */
+#define E100_RTS_GET(info) ((info)->rx_ctrl & E100_RTS_MASK)
+/* Input */
+#define E100_CTS_GET(info) ((info)->port[REG_STATUS] & E100_CTS_MASK)
+
+/* These are typically PA or PB and 0 means 0V, 1 means 3.3V */
+/* Is an output */
+#define E100_DTR_GET(info) ((*e100_modem_pins[(info)->line].shadow) & (1 << e100_modem_pins[(info)->line].dtr_bit))
+
+/* Normally inputs */
+#define E100_RI_GET(info) ((*e100_modem_pins[(info)->line].port) & (1 << e100_modem_pins[(info)->line].ri_bit))
+#define E100_CD_GET(info) ((*e100_modem_pins[(info)->line].port) & (1 << e100_modem_pins[(info)->line].cd_bit))
+
+/* Input */
+#define E100_DSR_GET(info) ((*e100_modem_pins[(info)->line].port) & (1 << e100_modem_pins[(info)->line].dsr_bit))
+
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+/*
+ * tmp_buf is used as a temporary buffer by serial_write. We need to
+ * lock it in case the memcpy_fromfs blocks while swapping in a page,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ports, since it significantly saves
+ * memory if large numbers of serial ports are open.
+ */
+static unsigned char *tmp_buf;
+#ifdef DECLARE_MUTEX
+static DECLARE_MUTEX(tmp_buf_sem);
+#else
+static struct semaphore tmp_buf_sem = MUTEX;
+#endif
+
+#ifdef CONFIG_ETRAX100_SERIAL_FLUSH_DMA_FAST
+#define TIMER1_IRQ_NBR 3
+
+/* clock select 10 for timer 1 gives 230400 Hz */
+#define FASTTIMER_SELECT (10)
+/* we use a source of 230400 Hz and a divider of 15 => 15360 Hz */
+#define FASTTIMER_DIV (15)
+
+/* fast flush timer stuff */
+static int fast_timer_started = 0;
+static unsigned long int fast_timer_ints = 0;
+
+static void _INLINE_ start_flush_timer(void)
+{
+ if (fast_timer_started)
+ return;
+
+ *R_TIMER_CTRL = r_timer_ctrl_shadow =
+ (r_timer_ctrl_shadow &
+ ~IO_MASK(R_TIMER_CTRL, timerdiv1) &
+ ~IO_MASK(R_TIMER_CTRL, tm1) &
+ ~IO_MASK(R_TIMER_CTRL, clksel1)) |
+ IO_FIELD(R_TIMER_CTRL, timerdiv1, FASTTIMER_DIV) |
+ IO_STATE(R_TIMER_CTRL, tm1, stop_ld) |
+ IO_FIELD(R_TIMER_CTRL, clksel1, FASTTIMER_SELECT);
+
+ *R_TIMER_CTRL = r_timer_ctrl_shadow =
+ (r_timer_ctrl_shadow & ~IO_MASK(R_TIMER_CTRL, tm1)) |
+ IO_STATE(R_TIMER_CTRL, tm1, run);
+
+ /* enable timer1 irq */
+
+ *R_IRQ_MASK0_SET = IO_STATE(R_IRQ_MASK0_SET, timer1, set);
+ fast_timer_started = 1;
+}
+#endif /* CONFIG_ETRAX100_SERIAL_FLUSH_DMA_FAST */
+
+/*
+ * This function maps from the Bxxxx defines in asm/termbits.h into real
+ * baud rates.
+ */
+
+static int
+cflag_to_baud(unsigned int cflag)
+{
+ static int baud_table[] = {
+ 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400,
+ 4800, 9600, 19200, 38400 };
+
+ static int ext_baud_table[] = {
+ 0, 57600, 115200, 230400, 460800, 921600, 1843200, 6250000 };
+
+ if(cflag & CBAUDEX)
+ return ext_baud_table[(cflag & CBAUD) & ~CBAUDEX];
+ else
+ return baud_table[cflag & CBAUD];
+}
+
+/* and this maps to an etrax100 hardware baud constant */
+
+static unsigned char
+cflag_to_etrax_baud(unsigned int cflag)
+{
+ char retval;
+
+ static char baud_table[] = {
+ -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, -1, 3, 4, 5, 6, 7 };
+
+ static char ext_baud_table[] = {
+ -1, 8, 9, 10, 11, 12, 13, 14 };
+
+ if(cflag & CBAUDEX)
+ retval = ext_baud_table[(cflag & CBAUD) & ~CBAUDEX];
+ else
+ retval = baud_table[cflag & CBAUD];
+
+ if(retval < 0) {
+ printk("serdriver tried setting invalid baud rate, flags %x.\n", cflag);
+ retval = 5; /* choose default 9600 instead */
+ }
+
+ return retval | (retval << 4); /* choose same for both TX and RX */
+}
+
+
+/* Various static support functions */
+
+/* Functions to set or clear DTR/RTS on the requested line */
+/* It is complicated by the fact that RTS is a serial port register, while
+ * DTR might not be implemented in the HW at all, and if it is, it can be on
+ * any general port.
+ */
+
+static inline void
+e100_dtr(struct e100_serial *info, int set)
+{
+#ifndef CONFIG_SVINTO_SIM
+ unsigned char mask = ( 1 << e100_modem_pins[info->line].dtr_bit);
+#ifdef SERIAL_DEBUG_IO
+ printk("ser%i dtr %i mask: 0x%02X\n", info->line, set, mask);
+ printk("ser%i shadow before 0x%02X get: %i\n",
+ info->line, *e100_modem_pins[info->line].shadow,
+ E100_DTR_GET(info));
+#endif
+ /* DTR is active low */
+ {
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ *e100_modem_pins[info->line].shadow &= ~mask;
+ *e100_modem_pins[info->line].shadow |= (set ? 0 : mask);
+ *e100_modem_pins[info->line].port = *e100_modem_pins[info->line].shadow;
+ restore_flags(flags);
+ }
+
+#if 0
+ REG_SHADOW_SET(e100_modem_pins[info->line].port,
+ *e100_modem_pins[info->line].shadow,
+ e100_modem_pins[info->line].dtr_bit, !set);
+#endif
+#ifdef SERIAL_DEBUG_IO
+ printk("ser%i shadow after 0x%02X get: %i\n",
+ info->line, *e100_modem_pins[info->line].shadow,
+ E100_DTR_GET(info));
+#endif
+#endif
+}
+
+/* set = 0 means 3.3V on the pin, bitvalue: 0=active, 1=inactive
+ * 0=0V , 1=3.3V
+ */
+static inline void
+e100_rts(struct e100_serial *info, int set)
+{
+#ifndef CONFIG_SVINTO_SIM
+#ifdef SERIAL_DEBUG_IO
+ printk("ser%i rts %i\n", info->line, set);
+#endif
+ info->rx_ctrl &= ~E100_RTS_MASK;
+ info->rx_ctrl |= (set ? 0 : E100_RTS_MASK); /* RTS is active low */
+ info->port[REG_REC_CTRL] = info->rx_ctrl;
+#endif
+}
+
+/* If this behaves as a modem, RI and CD is an output */
+static inline void
+e100_ri_out(struct e100_serial *info, int set)
+{
+#ifndef CONFIG_SVINTO_SIM
+ /* RI is active low */
+ {
+ unsigned char mask = ( 1 << e100_modem_pins[info->line].ri_bit);
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ *e100_modem_pins[info->line].shadow &= ~mask;
+ *e100_modem_pins[info->line].shadow |= (set ? 0 : mask);
+ *e100_modem_pins[info->line].port = *e100_modem_pins[info->line].shadow;
+ restore_flags(flags);
+ }
+#if 0
+ REG_SHADOW_SET(e100_modem_pins[info->line].port,
+ *e100_modem_pins[info->line].shadow,
+ e100_modem_pins[info->line].ri_bit, !set);
+#endif
+#endif
+}
+static inline void
+e100_cd_out(struct e100_serial *info, int set)
+{
+#ifndef CONFIG_SVINTO_SIM
+ /* CD is active low */
+ {
+ unsigned char mask = ( 1 << e100_modem_pins[info->line].cd_bit);
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ *e100_modem_pins[info->line].shadow &= ~mask;
+ *e100_modem_pins[info->line].shadow |= (set ? 0 : mask);
+ *e100_modem_pins[info->line].port = *e100_modem_pins[info->line].shadow;
+ restore_flags(flags);
+ }
+#if 0
+ REG_SHADOW_SET(e100_modem_pins[info->line].port,
+ *e100_modem_pins[info->line].shadow,
+ e100_modem_pins[info->line].cd_bit, !set);
+#endif
+#endif
+}
+
+static inline void
+e100_disable_rx(struct e100_serial *info)
+{
+#ifndef CONFIG_SVINTO_SIM
+ /* disable the receiver */
+ info->port[REG_REC_CTRL] = (info->rx_ctrl &= ~0x40);
+#endif
+}
+
+static inline void
+e100_enable_rx(struct e100_serial *info)
+{
+#ifndef CONFIG_SVINTO_SIM
+ /* enable the receiver */
+ info->port[REG_REC_CTRL] = (info->rx_ctrl |= 0x40);
+#endif
+}
+
+/* the rx DMA uses both the dma_descr and the dma_eop interrupts */
+
+static inline void
+e100_disable_rxdma_irq(struct e100_serial *info)
+{
+#ifdef SERIAL_DEBUG_INTR
+ printk("rxdma_irq(%d): 0\n",info->line);
+#endif
+ *R_IRQ_MASK2_CLR = (info->irq << 2) | (info->irq << 3);
+}
+
+static inline void
+e100_enable_rxdma_irq(struct e100_serial *info)
+{
+#ifdef SERIAL_DEBUG_INTR
+ printk("rxdma_irq(%d): 1\n",info->line);
+#endif
+ *R_IRQ_MASK2_SET = (info->irq << 2) | (info->irq << 3);
+}
+
+/* the tx DMA uses only dma_descr interrupt */
+
+static inline void
+e100_disable_txdma_irq(struct e100_serial *info)
+{
+#ifdef SERIAL_DEBUG_INTR
+ printk("txdma_irq(%d): 0\n",info->line);
+#endif
+ *R_IRQ_MASK2_CLR = info->irq;
+}
+
+static inline void
+e100_enable_txdma_irq(struct e100_serial *info)
+{
+#ifdef SERIAL_DEBUG_INTR
+ printk("txdma_irq(%d): 1\n",info->line);
+#endif
+ *R_IRQ_MASK2_SET = info->irq;
+}
+
+#ifdef SERIAL_HANDLE_EARLY_ERRORS
+/* in order to detect and fix errors on the first byte
+ we have to use the serial interrupts as well. */
+
+static inline void
+e100_disable_serial_data_irq(struct e100_serial *info)
+{
+#ifdef SERIAL_DEBUG_INTR
+ printk("ser_irq(%d): 0\n",info->line);
+#endif
+ *R_IRQ_MASK1_CLR = (1U << (8+2*info->line));
+}
+
+static inline void
+e100_enable_serial_data_irq(struct e100_serial *info)
+{
+#ifdef SERIAL_DEBUG_INTR
+ printk("ser_irq(%d): 1\n",info->line);
+ printk("**** %d = %d\n",
+ (8+2*info->line),
+ (1U << (8+2*info->line)));
+#endif
+ *R_IRQ_MASK1_SET = (1U << (8+2*info->line));
+}
+#endif
+
+#if defined(CONFIG_RS485)
+/* Enable RS-485 mode on selected port. This is UGLY. */
+static int
+e100_enable_rs485(struct tty_struct *tty,struct rs485_control *r)
+{
+ struct e100_serial * info = (struct e100_serial *)tty->driver_data;
+
+#if defined(CONFIG_RS485_ON_PA)
+ *R_PORT_PA_DATA = port_pa_data_shadow |= (1 << rs485_pa_bit);
+#endif
+
+ info->rs485.rts_on_send = 0x01 & r->rts_on_send;
+ info->rs485.rts_after_sent = 0x01 & r->rts_after_sent;
+ info->rs485.delay_rts_before_send = r->delay_rts_before_send;
+ info->rs485.enabled = r->enabled;
+
+ return 0;
+}
+
+/* Enable RS-485 mode on selected port. This is UGLY. */
+static int
+e100_write_rs485(struct tty_struct *tty,struct rs485_write *r)
+{
+ int stop_delay;
+ int total, i;
+ int max_j, delay_ms, bits;
+ tcflag_t cflags;
+ int size = (*r).outc_size;
+ struct e100_serial * info = (struct e100_serial *)tty->driver_data;
+ struct wait_queue wait = { current, NULL };
+
+ /* If we are in RS-485 mode, we need to toggle RTS and disable
+ * the receiver before initiating a DMA transfer
+ */
+ e100_rts(info, info->rs485.rts_on_send);
+#if defined(CONFIG_RS485_DISABLE_RECEIVER)
+ e100_disable_rx(info);
+ e100_disable_rxdma_irq(info);
+#endif
+
+ if (info->rs485.delay_rts_before_send > 0){
+ current->timeout = jiffies + (info->rs485.delay_rts_before_send * HZ)/1000;
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ current->timeout = 0;
+ }
+ total = rs_write(tty, 1, (*r).outc, (*r).outc_size);
+
+ /* If we are in RS-485 mode the following things has to be done:
+ * wait until DMA is ready
+ * wait on transmit shift register
+ * wait to toggle RTS
+ * enable the receiver
+ */
+
+ /* wait on transmit shift register */
+ /* All is sent, check if we should wait more before toggling rts */
+
+ /* calc. number of bits / data byte */
+ cflags = info->tty->termios->c_cflag;
+ /* databits + startbit and 1 stopbit */
+ if((cflags & CSIZE) == CS7)
+ bits = 9;
+ else
+ bits = 10;
+
+ if(cflags & CSTOPB) /* 2 stopbits ? */
+ bits++;
+
+ if(cflags & PARENB) /* parity bit ? */
+ bits++;
+
+ /* calc timeout */
+ delay_ms = ((bits * size * 1000) / info->baud) + 1;
+ max_j = jiffies + (delay_ms * HZ)/1000 + 10;
+
+ while (jiffies < max_j ) {
+ if (info->port[REG_STATUS] & 0x20) {
+ for( i=0 ; i<100; i++ ) {};
+ if (info->port[REG_STATUS] & 0x20) {
+ /* ~25 for loops per usec */
+ stop_delay = 25 * (1000000 / info->baud);
+ if(cflags & CSTOPB)
+ stop_delay *= 2;
+ for( i=0 ; i<stop_delay; i++ ) {};
+ break;
+ }
+ }
+ }
+
+ e100_rts(info, info->rs485.rts_after_sent);
+
+#if defined(CONFIG_RS485_DISABLE_RECEIVER)
+ e100_enable_rx(info);
+ e100_enable_rxdma_irq(info);
+#endif
+
+ return total;
+}
+#endif
+
+/*
+ * ------------------------------------------------------------
+ * rs_stop() and rs_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+
+/* FIXME - when are these used and what is the purpose ?
+ * In rs_stop we probably just can block the transmit DMA ready irq
+ * and in rs_start we re-enable it (and then the old one will come).
+ */
+
+static void
+rs_stop(struct tty_struct *tty)
+{
+}
+
+static void
+rs_start(struct tty_struct *tty)
+{
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Here starts the interrupt handling routines. All of the following
+ * subroutines are declared as inline and are folded into
+ * rs_interrupt(). They were separated out for readability's sake.
+ *
+ * Note: rs_interrupt() is a "fast" interrupt, which means that it
+ * runs with interrupts turned off. People who may want to modify
+ * rs_interrupt() should try to keep the interrupt handler as fast as
+ * possible. After you are done making modifications, it is not a bad
+ * idea to do:
+ *
+ * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
+ *
+ * and look at the resulting assemble code in serial.s.
+ *
+ * - Ted Ts'o (tytso@mit.edu), 7-Mar-93
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
+static _INLINE_ void
+rs_sched_event(struct e100_serial *info,
+ int event)
+{
+ info->event |= 1 << event;
+ queue_task(&info->tqueue, &tq_serial);
+ mark_bh(SERIAL_BH);
+}
+
+/* The output DMA channel is free - use it to send as many chars as possible
+ * NOTES:
+ * We don't pay attention to info->x_char, which means if the TTY wants to
+ * use XON/XOFF it will set info->x_char but we won't send any X char!
+ *
+ * To implement this, we'd just start a DMA send of 1 byte pointing at a
+ * buffer containing the X char, and skip updating xmit. We'd also have to
+ * check if the last sent char was the X char when we enter this function
+ * the next time, to avoid updating xmit with the sent X value.
+ */
+
+static void
+transmit_chars(struct e100_serial *info)
+{
+ unsigned int c, sentl;
+ struct etrax_dma_descr *descr;
+
+#ifdef CONFIG_SVINTO_SIM
+ /* This will output too little if tail is not 0 always since
+ * we don't reloop to send the other part. Anyway this SHOULD be a
+ * no-op - transmit_chars would never really be called during sim
+ * since rs_write does not write into the xmit buffer then.
+ */
+ if(info->xmit.tail)
+ printk("Error in serial.c:transmit_chars(), tail!=0\n");
+ if(info->xmit.head != info->xmit.tail) {
+ SIMCOUT(info->xmit.buf + info->xmit.tail,
+ CIRC_CNT(info->xmit.head,
+ info->xmit.tail,
+ SERIAL_XMIT_SIZE));
+ info->xmit.head = info->xmit.tail; /* move back head */
+ info->tr_running = 0;
+ }
+ return;
+#endif
+ /* acknowledge both a dma_descr and dma_eop irq in R_DMAx_CLRINTR */
+ *info->oclrintradr = 3;
+
+#ifdef SERIAL_DEBUG_INTR
+ if(info->line == SERIAL_DEBUG_LINE)
+ printk("tc\n");
+#endif
+ if(!info->tr_running) {
+ /* weirdo... we shouldn't get here! */
+ printk("Achtung: transmit_chars with !tr_running\n");
+ return;
+ }
+
+ descr = &info->tr_descr;
+
+ /* first get the amount of bytes sent during the last DMA transfer,
+ and update xmit accordingly */
+
+ /* if the stop bit was not set, all data has been sent */
+ if(!(descr->status & d_stop)) {
+ sentl = descr->sw_len;
+ } else
+ /* otherwise we find the amount of data sent here */
+ sentl = descr->hw_len;
+
+ /* update stats */
+ info->icount.tx += sentl;
+
+ /* update xmit buffer */
+ info->xmit.tail = (info->xmit.tail + sentl) & (SERIAL_XMIT_SIZE - 1);
+
+ /* if there is only a few chars left in the buf, wake up the blocked
+ write if any */
+ if (CIRC_CNT(info->xmit.head,
+ info->xmit.tail,
+ SERIAL_XMIT_SIZE) < WAKEUP_CHARS)
+ rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+
+ /* find out the largest amount of consecutive bytes we want to send now */
+
+ c = CIRC_CNT_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+
+ if(c <= 0) {
+ /* our job here is done, don't schedule any new DMA transfer */
+ info->tr_running = 0;
+
+#if defined(CONFIG_RS485)
+ /* Check if we should toggle RTS now */
+ if (info->rs485.enabled)
+ {
+ /* Make sure fifo is empty */
+ int in_fifo = 0 ;
+ do{
+ in_fifo = (*info->ostatusadr) & 0x007F ;
+ } while (in_fifo > 0) ;
+ /* Any way to really check transmitter empty? (TEMT) */
+ /* Control RTS to set to RX mode */
+ e100_rts(info, info->rs485.rts_after_sent);
+#if defined(CONFIG_RS485_DISABLE_RECEIVER)
+ e100_enable_rx(info);
+ e100_enable_rxdma_irq(info);
+#endif
+ }
+#endif /* RS485 */
+
+ return;
+ }
+
+ /* ok we can schedule a dma send of c chars starting at info->xmit.tail */
+ /* set up the descriptor correctly for output */
+
+ descr->ctrl = d_int | d_eol | d_wait; /* Wait needed for tty_wait_until_sent() */
+ descr->sw_len = c;
+ descr->buf = virt_to_phys(info->xmit.buf + info->xmit.tail);
+ descr->status = 0;
+
+ *info->ofirstadr = virt_to_phys(descr); /* write to R_DMAx_FIRST */
+ *info->ocmdadr = 1; /* dma command start -> R_DMAx_CMD */
+
+ /* DMA is now running (hopefully) */
+
+}
+
+static void
+start_transmit(struct e100_serial *info)
+{
+#if 0
+ if(info->line == SERIAL_DEBUG_LINE)
+ printk("x\n");
+#endif
+
+ info->tr_descr.sw_len = 0;
+ info->tr_descr.hw_len = 0;
+ info->tr_descr.status = 0;
+ info->tr_running = 1;
+
+ transmit_chars(info);
+}
+
+
+static _INLINE_ void
+receive_chars(struct e100_serial *info)
+{
+ struct tty_struct *tty;
+ unsigned char rstat;
+ unsigned int recvl;
+ struct etrax_dma_descr *descr;
+
+#ifdef CONFIG_SVINTO_SIM
+ /* No receive in the simulator. Will probably be when the rest of
+ * the serial interface works, and this piece will just be removed.
+ */
+ return;
+#endif
+
+ tty = info->tty;
+
+ /* acknowledge both a dma_descr and dma_eop irq in R_DMAx_CLRINTR */
+
+ *info->iclrintradr = 3;
+
+ if(!tty) /* something wrong... */
+ return;
+
+ descr = &info->rec_descr;
+
+ /* find out how many bytes were read */
+
+ /* if the eop bit was not set, all data has been received */
+ if(!(descr->status & d_eop)) {
+ recvl = descr->sw_len;
+ } else {
+ /* otherwise we find the amount of data received here */
+ recvl = descr->hw_len;
+ }
+ if(recvl) {
+ unsigned char *buf;
+ struct async_icount *icount;
+
+ icount = &info->icount;
+
+ /* update stats */
+ icount->rx += recvl;
+
+ /* read the status register so we can detect errors */
+ rstat = info->port[REG_STATUS];
+
+ if(rstat & 0xe) {
+ /* if we got an error, we must reset it by reading the
+ * data_in field
+ */
+ (void)info->port[REG_DATA];
+ }
+
+ /* we only ever write errors into the first byte in the flip
+ * flag buffer, so we dont have to clear it all every time
+ */
+
+ if(rstat & 0x04) {
+ icount->parity++;
+ *tty->flip.flag_buf_ptr = TTY_PARITY;
+ } else if(rstat & 0x08) {
+ icount->overrun++;
+ *tty->flip.flag_buf_ptr = TTY_OVERRUN;
+ } else if(rstat & 0x02) {
+ icount->frame++;
+ *tty->flip.flag_buf_ptr = TTY_FRAME;
+ } else
+ *tty->flip.flag_buf_ptr = 0;
+
+ /* use the flip buffer next in turn to restart DMA into */
+
+ if (tty->flip.buf_num) {
+ buf = tty->flip.char_buf;
+ } else {
+ buf = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
+ }
+
+ if(buf == phys_to_virt(descr->buf)) {
+ printk("ttyS%d flip-buffer overrun!\n", info->line);
+ icount->overrun++;
+ *tty->flip.flag_buf_ptr = TTY_OVERRUN;
+ /* restart old buffer */
+ } else {
+ descr->buf = virt_to_phys(buf);
+
+ /* schedule or push a flip of the buffer */
+
+ info->tty->flip.count = recvl;
+
+#if (LINUX_VERSION_CODE > 131394) /* 2.1.66 */
+ /* this includes a check for low-latency */
+ tty_flip_buffer_push(tty);
+#else
+ queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
+#endif
+ }
+ }
+
+ /* restart the receiving dma */
+
+ descr->sw_len = TTY_FLIPBUF_SIZE;
+ descr->ctrl = d_int | d_eol | d_eop;
+ descr->hw_len = 0;
+ descr->status = 0;
+
+ *info->ifirstadr = virt_to_phys(descr);
+ *info->icmdadr = 1; /* start */
+
+#ifdef SERIAL_HANDLE_EARLY_ERRORS
+ e100_enable_serial_data_irq(info);
+#endif
+ /* input dma should be running now */
+}
+
+static void
+start_receive(struct e100_serial *info)
+{
+ struct etrax_dma_descr *descr;
+
+#ifdef CONFIG_SVINTO_SIM
+ /* No receive in the simulator. Will probably be when the rest of
+ * the serial interface works, and this piece will just be removed.
+ */
+ return;
+#endif
+
+ /* reset the input dma channel to be sure it works */
+
+ *info->icmdadr = 4;
+ while((*info->icmdadr & 7) == 4);
+
+ descr = &info->rec_descr;
+
+ /* start the receiving dma into the flip buffer */
+
+ descr->ctrl = d_int | d_eol | d_eop;
+ descr->sw_len = TTY_FLIPBUF_SIZE;
+ descr->buf = virt_to_phys(info->tty->flip.char_buf_ptr);
+ descr->hw_len = 0;
+ descr->status = 0;
+
+ info->tty->flip.count = 0;
+
+ *info->ifirstadr = virt_to_phys(descr);
+ *info->icmdadr = 1; /* start */
+}
+
+
+static _INLINE_ void
+status_handle(struct e100_serial *info, unsigned short status)
+{
+}
+
+/* the bits in the MASK2 register are laid out like this:
+ DMAI_EOP DMAI_DESCR DMAO_EOP DMAO_DESCR
+ where I is the input channel and O is the output channel for the port.
+ info->irq is the bit number for the DMAO_DESCR so to check the others we
+ shift info->irq to the left.
+*/
+
+/* dma output channel interrupt handler
+ this interrupt is called from DMA2(ser2), DMA4(ser3), DMA6(ser0) or
+ DMA8(ser1) when they have finished a descriptor with the intr flag set.
+*/
+
+static void
+tr_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+ struct e100_serial *info;
+ unsigned long ireg;
+ int i;
+
+#ifdef CONFIG_SVINTO_SIM
+ /* No receive in the simulator. Will probably be when the rest of
+ * the serial interface works, and this piece will just be removed.
+ */
+ {
+ const char *s = "What? tr_interrupt in simulator??\n";
+ SIMCOUT(s,strlen(s));
+ }
+ return;
+#endif
+
+ /* find out the line that caused this irq and get it from rs_table */
+
+ ireg = *R_IRQ_MASK2_RD; /* get the active irq bits for the dma channels */
+
+ for(i = 0; i < NR_PORTS; i++) {
+ info = rs_table + i;
+ /* check for dma_descr (dont need to check for dma_eop in output dma for serial */
+ if(ireg & info->irq) {
+ /* we can send a new dma bunch. make it so. */
+ transmit_chars(info);
+ }
+
+ /* FIXME: here we should really check for a change in the
+ status lines and if so call status_handle(info) */
+ }
+}
+
+/* dma input channel interrupt handler */
+
+static void
+rec_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+ struct e100_serial *info;
+ unsigned long ireg;
+ int i;
+
+#ifdef CONFIG_SVINTO_SIM
+ /* No receive in the simulator. Will probably be when the rest of
+ * the serial interface works, and this piece will just be removed.
+ */
+ {
+ const char *s = "What? rec_interrupt in simulator??\n";
+ SIMCOUT(s,strlen(s));
+ }
+ return;
+#endif
+
+ /* find out the line that caused this irq and get it from rs_table */
+
+ ireg = *R_IRQ_MASK2_RD; /* get the active irq bits for the dma channels */
+
+ for(i = 0; i < NR_PORTS; i++) {
+ info = rs_table + i;
+ /* check for both dma_eop and dma_descr for the input dma channel */
+ if(ireg & ((info->irq << 2) | (info->irq << 3))) {
+ /* we have received something */
+ receive_chars(info);
+ }
+
+ /* FIXME: here we should really check for a change in the
+ status lines and if so call status_handle(info) */
+ }
+}
+
+/* dma fifo/buffer timeout handler
+ forces an end-of-packet for the dma input channel if no chars
+ have been received for CONFIG_ETRAX100_RX_TIMEOUT_TICKS/100 s.
+ If CONFIG_ETRAX100_SERIAL_FLUSH_DMA_FAST is configured then this
+ handler is instead run at 15360 Hz.
+*/
+
+#ifndef CONFIG_ETRAX100_SERIAL_FLUSH_DMA_FAST
+static int timeout_divider = 0;
+#endif
+
+static struct timer_list flush_timer;
+
+static void
+timed_flush_handler(void)
+{
+ struct e100_serial *info;
+ int i;
+ unsigned int magic;
+
+#ifdef CONFIG_SVINTO_SIM
+ return;
+#endif
+
+ for(i = 0; i < NR_PORTS; i++) {
+ info = rs_table + i;
+ if(!(info->flags & ASYNC_INITIALIZED))
+ continue;
+
+ /* istatusadr (bit 6-0) hold number of bytes in fifo
+ * ihwswadr (bit 31-16) holds number of bytes in dma buffer
+ * ihwswadr (bit 15-0) specifies size of dma buffer
+ */
+
+ magic = (*info->istatusadr & 0x3f);
+ magic += ((*info->ihwswadr&0xffff ) - (*info->ihwswadr >> 16));
+
+ /* if magic is equal to fifo_magic (magic in previous
+ * timeout_interrupt) then no new data has arrived since last
+ * interrupt and we'll force eop to flush fifo+dma buffers
+ */
+
+ if(magic != info->fifo_magic) {
+ info->fifo_magic = magic;
+ info->fifo_didmagic = 0;
+ } else {
+ /* hit the timeout, force an EOP for the input
+ * dma channel if we haven't already
+ */
+ if(!info->fifo_didmagic && magic) {
+ info->fifo_didmagic = 1;
+ info->fifo_magic = 0;
+ *R_SET_EOP = 1U << info->iseteop;
+ }
+ }
+ }
+
+ /* restart flush timer */
+
+ mod_timer(&flush_timer, jiffies + MAX_FLUSH_TIME);
+}
+
+
+#ifdef SERIAL_HANDLE_EARLY_ERRORS
+
+/* If there is an error (ie break) when the DMA is running and
+ * there are no bytes in the fifo the DMA is stopped and we get no
+ * eop interrupt. Thus we have to monitor the first bytes on a DMA
+ * transfer, and if it is without error we can turn the serial
+ * interrupts off.
+ */
+
+static void
+ser_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct e100_serial *info;
+ int i;
+ unsigned char rstat;
+
+ for(i = 0; i < NR_PORTS; i++) {
+
+ info = rs_table + i;
+ rstat = info->port[REG_STATUS];
+
+ if(*R_IRQ_MASK1_RD & (1U << (8+2*info->line))) { /* This line caused the irq */
+#ifdef SERIAL_DEBUG_INTR
+ printk("Interrupt from serport %d\n", i);
+#endif
+ if(rstat & 0x0e) {
+ /* FIXME: This is weird, but if this delay is
+ * not present then irmaflash does not work...
+ */
+ udelay(2300);
+
+ /* if we got an error, we must reset it by
+ * reading the data_in field
+ */
+ (void)info->port[REG_DATA];
+
+ PROCSTAT(early_errors_cnt[info->line]++);
+
+ /* restart the DMA */
+ *info->icmdadr = 3;
+ }
+ else { /* it was a valid byte, now let the dma do the rest */
+#ifdef SERIAL_DEBUG_INTR
+ printk("** OK, disabling ser_interupts\n");
+#endif
+ e100_disable_serial_data_irq(info);
+ }
+ }
+ }
+}
+#endif
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This
+ * is where time-consuming activities which can not be done in the
+ * interrupt driver proper are done; the interrupt driver schedules
+ * them using rs_sched_event(), and they get done here.
+ */
+static void
+do_serial_bh(void)
+{
+ run_task_queue(&tq_serial);
+}
+
+static void
+do_softint(void *private_)
+{
+ struct e100_serial *info = (struct e100_serial *) private_;
+ struct tty_struct *tty;
+
+ tty = info->tty;
+ if (!tty)
+ return;
+
+ if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+ wake_up_interruptible(&tty->write_wait);
+ }
+}
+
+/*
+ * This routine is called from the scheduler tqueue when the interrupt
+ * routine has signalled that a hangup has occurred. The path of
+ * hangup processing is:
+ *
+ * serial interrupt routine -> (scheduler tqueue) ->
+ * do_serial_hangup() -> tty->hangup() -> rs_hangup()
+ *
+ */
+static void
+do_serial_hangup(void *private_)
+{
+ struct e100_serial *info = (struct e100_serial *) private_;
+ struct tty_struct *tty;
+
+ tty = info->tty;
+ if (!tty)
+ return;
+
+ tty_hangup(tty);
+}
+
+static int
+startup(struct e100_serial * info)
+{
+ unsigned long flags;
+ unsigned long page;
+
+ page = get_zeroed_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+
+ save_flags(flags); cli();
+
+ /* if it was already initialized, skip this */
+
+ if (info->flags & ASYNC_INITIALIZED) {
+ free_page(page);
+ restore_flags(flags);
+ return -EBUSY;
+ }
+
+ if (info->xmit.buf)
+ free_page(page);
+ else
+ info->xmit.buf = (unsigned char *) page;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("starting up ttyS%d (xmit_buf 0x%x)...\n", info->line, info->xmit_buf);
+#endif
+
+ if(info->tty) {
+
+ /* clear the tty flip flag buffer since we will not
+ * be using it (we only use the first byte..)
+ */
+
+ memset(info->tty->flip.flag_buf, 0, TTY_FLIPBUF_SIZE * 2);
+ }
+
+ save_flags(flags);
+ cli();
+
+#ifdef CONFIG_SVINTO_SIM
+ /* Bits and pieces collected from below. Better to have them
+ in one ifdef:ed clause than to mix in a lot of ifdefs,
+ right? */
+ if (info->tty)
+ clear_bit(TTY_IO_ERROR, &info->tty->flags);
+ info->xmit.head = info->xmit.tail = 0;
+
+ /* No real action in the simulator, but may set info important
+ to ioctl. */
+ change_speed(info);
+#else
+
+ /*
+ * Clear the FIFO buffers and disable them
+ * (they will be reenabled in change_speed())
+ */
+
+ /*
+ * Reset the DMA channels and make sure their interrupts are cleared
+ */
+
+ *info->icmdadr = 4; /* reset command */
+ *info->ocmdadr = 4; /* reset command */
+
+ while((*info->icmdadr & 7) == 4); /* wait until reset cycle is complete */
+ while((*info->ocmdadr & 7) == 4);
+
+ *info->iclrintradr = 3; /* make sure the irqs are cleared */
+ *info->oclrintradr = 3;
+
+ if (info->tty)
+ clear_bit(TTY_IO_ERROR, &info->tty->flags);
+
+ info->xmit.head = info->xmit.tail = 0;
+
+ /*
+ * and set the speed and other flags of the serial port
+ * this will start the rx/tx as well
+ */
+#ifdef SERIAL_HANDLE_EARLY_ERRORS
+ e100_enable_serial_data_irq(info);
+#endif
+ change_speed(info);
+
+ /* dummy read to reset any serial errors */
+
+ (void)info->port[REG_DATA];
+
+ /* enable the interrupts */
+
+ e100_enable_txdma_irq(info);
+ e100_enable_rxdma_irq(info);
+
+ info->tr_running = 0; /* to be sure we dont lock up the transmitter */
+
+ /* setup the dma input descriptor and start dma */
+
+ start_receive(info);
+
+ /* for safety, make sure the descriptors last result is 0 bytes written */
+
+ info->tr_descr.sw_len = 0;
+ info->tr_descr.hw_len = 0;
+ info->tr_descr.status = 0;
+
+ /* enable RTS/DTR last */
+
+ e100_rts(info, 1);
+ e100_dtr(info, 1);
+
+#endif /* CONFIG_SVINTO_SIM */
+
+ info->flags |= ASYNC_INITIALIZED;
+
+ restore_flags(flags);
+ return 0;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void
+shutdown(struct e100_serial * info)
+{
+ unsigned long flags;
+
+#ifndef CONFIG_SVINTO_SIM
+ /* shut down the transmitter and receiver */
+
+ e100_disable_rx(info);
+ info->port[REG_TR_CTRL] = (info->tx_ctrl &= ~0x40);
+
+ e100_disable_rxdma_irq(info);
+ e100_disable_txdma_irq(info);
+
+ info->tr_running = 0;
+
+ /* reset both dma channels */
+
+ *info->icmdadr = 4;
+ *info->ocmdadr = 4;
+
+#endif /* CONFIG_SVINTO_SIM */
+
+ if (!(info->flags & ASYNC_INITIALIZED))
+ return;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("Shutting down serial port %d (irq %d)....\n", info->line,
+ info->irq);
+#endif
+
+ save_flags(flags);
+ cli(); /* Disable interrupts */
+
+ if (info->xmit.buf) {
+ unsigned long pg = (unsigned long) info->xmit.buf;
+ info->xmit.buf = 0;
+ free_page(pg);
+ }
+
+ if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
+ /* hang up DTR and RTS if HUPCL is enabled */
+ e100_dtr(info, 0);
+ e100_rts(info, 0); /* could check CRTSCTS before doing this */
+ }
+
+ if (info->tty)
+ set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+ info->flags &= ~ASYNC_INITIALIZED;
+ restore_flags(flags);
+}
+
+
+/* change baud rate and other assorted parameters */
+
+static void
+change_speed(struct e100_serial *info)
+{
+ unsigned int cflag;
+
+ /* first some safety checks */
+
+ if(!info->tty || !info->tty->termios)
+ return;
+ if (!info->port)
+ return;
+
+ cflag = info->tty->termios->c_cflag;
+
+ /* possibly, the tx/rx should be disabled first to do this safely */
+
+ /* change baud-rate and write it to the hardware */
+
+ info->baud = cflag_to_baud(cflag);
+
+#ifndef CONFIG_SVINTO_SIM
+ info->port[REG_BAUD] = cflag_to_etrax_baud(cflag);
+ /* start with default settings and then fill in changes */
+
+ info->rx_ctrl &= ~(0x07); /* 8 bit, no/even parity */
+ info->tx_ctrl &= ~(0x37); /* 8 bit, no/even parity, 1 stop bit, no cts */
+
+ if ((cflag & CSIZE) == CS7) {
+ /* set 7 bit mode */
+ info->tx_ctrl |= 0x01;
+ info->rx_ctrl |= 0x01;
+ }
+
+ if (cflag & CSTOPB) {
+ /* set 2 stop bit mode */
+ info->tx_ctrl |= 0x10;
+ }
+
+ if (cflag & PARENB) {
+ /* enable parity */
+ info->tx_ctrl |= 0x02;
+ info->rx_ctrl |= 0x02;
+ }
+
+ if (cflag & PARODD) {
+ /* set odd parity */
+ info->tx_ctrl |= 0x04;
+ info->rx_ctrl |= 0x04;
+ }
+
+ if (cflag & CRTSCTS) {
+ /* enable automatic CTS handling */
+ info->tx_ctrl |= 0x20;
+ }
+
+ /* make sure the tx and rx are enabled */
+
+ info->tx_ctrl |= 0x40;
+ info->rx_ctrl |= 0x40;
+
+ /* actually write the control regs to the hardware */
+
+ info->port[REG_TR_CTRL] = info->tx_ctrl;
+ info->port[REG_REC_CTRL] = info->rx_ctrl;
+ *((unsigned long *)&info->port[REG_XOFF]) = 0;
+
+#endif /* CONFIG_SVINTO_SIM */
+}
+
+/* start transmitting chars NOW */
+
+static void
+rs_flush_chars(struct tty_struct *tty)
+{
+ struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+ unsigned long flags;
+
+ if (info->tr_running
+ || info->xmit.head == info->xmit.tail
+ || tty->stopped
+ || tty->hw_stopped
+ || !info->xmit.buf)
+ return;
+
+#ifdef SERIAL_DEBUG_FLOW
+ printk("rs_flush_chars\n");
+#endif
+
+ /* this protection might not exactly be necessary here */
+
+ save_flags(flags);
+ cli();
+ start_transmit(info);
+ restore_flags(flags);
+}
+
+static int
+rs_write(struct tty_struct * tty, int from_user,
+ const unsigned char *buf, int count)
+{
+ int c, ret = 0;
+ struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+ unsigned long flags;
+
+ /* first some sanity checks */
+
+ if (!tty || !info->xmit.buf || !tmp_buf)
+ return 0;
+
+#ifdef SERIAL_DEBUG_DATA
+ if(info->line == SERIAL_DEBUG_LINE)
+ printk("rs_write (%d), status %d\n",
+ count, info->port[REG_STATUS]);
+#endif
+
+#ifdef CONFIG_SVINTO_SIM
+ /* Really simple. The output is here and now. */
+ SIMCOUT(buf, count);
+ return;
+#endif
+ save_flags(flags);
+
+ /* the cli/restore_flags pairs below are needed because the
+ * DMA interrupt handler moves the info->xmit values. the memcpy
+ * needs to be in the critical region unfortunately, because we
+ * need to read xmit values, memcpy, write xmit values in one
+ * atomic operation... this could perhaps be avoided by more clever
+ * design.
+ */
+ if(from_user) {
+ down(&tmp_buf_sem);
+ while (1) {
+ int c1;
+ c = CIRC_SPACE_TO_END(info->xmit.head,
+ info->xmit.tail,
+ SERIAL_XMIT_SIZE);
+ if (count < c)
+ c = count;
+ if (c <= 0)
+ break;
+
+ c -= copy_from_user(tmp_buf, buf, c);
+ if (!c) {
+ if (!ret)
+ ret = -EFAULT;
+ break;
+ }
+ cli();
+ c1 = CIRC_SPACE_TO_END(info->xmit.head,
+ info->xmit.tail,
+ SERIAL_XMIT_SIZE);
+ if (c1 < c)
+ c = c1;
+ memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c);
+ info->xmit.head = ((info->xmit.head + c) &
+ (SERIAL_XMIT_SIZE-1));
+ restore_flags(flags);
+ buf += c;
+ count -= c;
+ ret += c;
+ }
+ up(&tmp_buf_sem);
+ } else {
+ cli();
+ while(1) {
+ c = CIRC_SPACE_TO_END(info->xmit.head,
+ info->xmit.tail,
+ SERIAL_XMIT_SIZE);
+
+ if (count < c)
+ c = count;
+ if (c <= 0)
+ break;
+
+ memcpy(info->xmit.buf + info->xmit.head, buf, c);
+ info->xmit.head = (info->xmit.head + c) &
+ (SERIAL_XMIT_SIZE-1);
+ buf += c;
+ count -= c;
+ ret += c;
+ }
+ restore_flags(flags);
+ }
+
+ /* enable transmitter if not running, unless the tty is stopped
+ * this does not need IRQ protection since if tr_running == 0
+ * the IRQ's are not running anyway for this port.
+ */
+
+ if(info->xmit.head != info->xmit.tail
+ && !tty->stopped &&
+ !tty->hw_stopped &&
+ !info->tr_running) {
+ start_transmit(info);
+ }
+
+ return ret;
+}
+
+/* how much space is available in the xmit buffer? */
+
+static int
+rs_write_room(struct tty_struct *tty)
+{
+ struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+
+ return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+}
+
+/* How many chars are in the xmit buffer?
+ * This does not include any chars in the transmitter FIFO.
+ * Use wait_until_sent for waiting for FIFO drain.
+ */
+
+static int
+rs_chars_in_buffer(struct tty_struct *tty)
+{
+ struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+
+ return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+}
+
+/* discard everything in the xmit buffer */
+
+static void
+rs_flush_buffer(struct tty_struct *tty)
+{
+ struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ info->xmit.head = info->xmit.tail = 0;
+ restore_flags(flags);
+
+ wake_up_interruptible(&tty->write_wait);
+
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+}
+
+/*
+ * This function is used to send a high-priority XON/XOFF character to
+ * the device
+ *
+ * Since we don't bother to check for info->x_char in transmit_chars yet,
+ * we don't really implement this function yet.
+ */
+static void rs_send_xchar(struct tty_struct *tty, char ch)
+{
+ struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+
+ printk("serial.c:rs_send_xchar not implemented!\n");
+
+ info->x_char = ch;
+ if (ch) {
+ /* Make sure transmit interrupts are on */
+ /* TODO. */
+ }
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_throttle()
+ *
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void
+rs_throttle(struct tty_struct * tty)
+{
+ struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+ unsigned long flags;
+#ifdef SERIAL_DEBUG_THROTTLE
+ char buf[64];
+
+ printk("throttle %s: %d....\n", _tty_name(tty, buf),
+ tty->ldisc.chars_in_buffer(tty));
+#endif
+
+ if (I_IXOFF(tty))
+ info->x_char = STOP_CHAR(tty);
+
+ /* Turn off RTS line (do this atomic) should here be an else ?? */
+
+ save_flags(flags);
+ cli();
+ e100_rts(info, 0);
+ restore_flags(flags);
+}
+
+static void
+rs_unthrottle(struct tty_struct * tty)
+{
+ struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+ unsigned long flags;
+#ifdef SERIAL_DEBUG_THROTTLE
+ char buf[64];
+
+ printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
+ tty->ldisc.chars_in_buffer(tty));
+#endif
+
+ if (I_IXOFF(tty)) {
+ if (info->x_char)
+ info->x_char = 0;
+ else
+ info->x_char = START_CHAR(tty);
+ }
+
+ /* Assert RTS line (do this atomic) */
+
+ save_flags(flags);
+ cli();
+ e100_rts(info, 1);
+ restore_flags(flags);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+static int
+get_serial_info(struct e100_serial * info,
+ struct serial_struct * retinfo)
+{
+ struct serial_struct tmp;
+
+ /* this is all probably wrong, there are a lot of fields
+ * here that we don't have in e100_serial and maybe we
+ * should set them to something else than 0.
+ */
+
+ if (!retinfo)
+ return -EFAULT;
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.type = info->type;
+ tmp.line = info->line;
+ tmp.port = (int)info->port;
+ tmp.irq = info->irq;
+ tmp.flags = info->flags;
+ tmp.close_delay = info->close_delay;
+ tmp.closing_wait = info->closing_wait;
+ if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
+ return -EFAULT;
+ return 0;
+}
+
+static int
+set_serial_info(struct e100_serial * info,
+ struct serial_struct * new_info)
+{
+ struct serial_struct new_serial;
+ struct e100_serial old_info;
+ int retval = 0;
+
+ if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
+ return -EFAULT;
+
+ old_info = *info;
+
+ if(!capable(CAP_SYS_ADMIN)) {
+ if((new_serial.type != info->type) ||
+ (new_serial.close_delay != info->close_delay) ||
+ ((new_serial.flags & ~ASYNC_USR_MASK) !=
+ (info->flags & ~ASYNC_USR_MASK)))
+ return -EPERM;
+ info->flags = ((info->flags & ~ASYNC_USR_MASK) |
+ (new_serial.flags & ASYNC_USR_MASK));
+ goto check_and_exit;
+ }
+
+ if (info->count > 1)
+ return -EBUSY;
+
+ /*
+ * OK, past this point, all the error checking has been done.
+ * At this point, we start making changes.....
+ */
+
+ info->flags = ((info->flags & ~ASYNC_FLAGS) |
+ (new_serial.flags & ASYNC_FLAGS));
+ info->type = new_serial.type;
+ info->close_delay = new_serial.close_delay;
+ info->closing_wait = new_serial.closing_wait;
+#if (LINUX_VERSION_CODE > 0x20100)
+ info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+#endif
+
+ check_and_exit:
+ if(info->flags & ASYNC_INITIALIZED) {
+ change_speed(info);
+ } else
+ retval = startup(info);
+ return retval;
+}
+
+/*
+ * get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * is emptied. On bus types like RS485, the transmitter must
+ * release the bus after transmitting. This must be done when
+ * the transmit shift register is empty, not be done when the
+ * transmit holding register is empty. This functionality
+ * allows an RS485 driver to be written in user space.
+ */
+static int
+get_lsr_info(struct e100_serial * info, unsigned int *value)
+{
+ unsigned int result;
+
+#ifdef CONFIG_SVINTO_SIM
+ /* Always open. */
+ result = TIOCSER_TEMT;
+#else
+ if (*info->ostatusadr & 0x007F) /* something in fifo */
+ result = 0;
+ else
+ result = TIOCSER_TEMT;
+#endif
+
+ if (copy_to_user(value, &result, sizeof(int)))
+ return -EFAULT;
+ return 0;
+}
+
+#ifdef SERIAL_DEBUG_IO
+struct state_str
+{
+ int state;
+ const char *str;
+
+};
+
+const struct state_str control_state_str[]={
+ {TIOCM_DTR, "DTR" },
+ {TIOCM_RTS, "RTS"},
+ {TIOCM_ST, "ST?" },
+ {TIOCM_SR, "SR?" },
+ {TIOCM_CTS, "CTS" },
+ {TIOCM_CD, "CD" },
+ {TIOCM_RI, "RI" },
+ {TIOCM_DSR, "DSR" },
+ {0, NULL }
+};
+
+char *get_control_state_str(int MLines, char *s)
+{
+ int i = 0;
+ s[0]='\0';
+ while (control_state_str[i].str != NULL) {
+ if (MLines & control_state_str[i].state) {
+ if (s[0] != '\0') {
+ strcat(s, ", ");
+ }
+ strcat(s, control_state_str[i].str);
+ }
+ i++;
+ }
+ return s;
+}
+#endif
+
+static int
+get_modem_info(struct e100_serial * info, unsigned int *value)
+{
+ unsigned int result;
+ /* Polarity isn't verified */
+#if 0 /*def SERIAL_DEBUG_IO */
+
+ printk("get_modem_info: RTS: %i DTR: %i CD: %i RI: %i DSR: %i CTS: %i\n",
+ E100_RTS_GET(info),
+ E100_DTR_GET(info),
+ E100_CD_GET(info),
+ E100_RI_GET(info),
+ E100_DSR_GET(info),
+ E100_CTS_GET(info));
+#endif
+ result =
+ (!E100_RTS_GET(info) ? TIOCM_RTS : 0)
+ | (!E100_DTR_GET(info) ? TIOCM_DTR : 0)
+ | (!E100_CD_GET(info) ? TIOCM_CAR : 0)
+ | (!E100_RI_GET(info) ? TIOCM_RNG : 0)
+ | (!E100_DSR_GET(info) ? TIOCM_DSR : 0)
+ | (!E100_CTS_GET(info) ? TIOCM_CTS : 0);
+
+#ifdef SERIAL_DEBUG_IO
+ printk("e100ser: modem state: %i 0x%08X\n", result, result);
+ {
+ char s[100];
+
+ get_control_state_str(result, s);
+ printk("state: %s\n", s);
+ }
+#endif
+ if (copy_to_user(value, &result, sizeof(int)))
+ return -EFAULT;
+ return 0;
+}
+
+
+static int
+set_modem_info(struct e100_serial * info, unsigned int cmd,
+ unsigned int *value)
+{
+ unsigned int arg;
+
+ if (copy_from_user(&arg, value, sizeof(int)))
+ return -EFAULT;
+
+ switch (cmd) {
+ case TIOCMBIS:
+ if (arg & TIOCM_RTS) {
+ e100_rts(info, 1);
+ }
+ if (arg & TIOCM_DTR) {
+ e100_dtr(info, 1);
+ }
+ /* Handle FEMALE behaviour */
+ if (arg & TIOCM_RI) {
+ e100_ri_out(info, 1);
+ }
+ if (arg & TIOCM_CD) {
+ e100_cd_out(info, 1);
+ }
+ break;
+ case TIOCMBIC:
+ if (arg & TIOCM_RTS) {
+ e100_rts(info, 0);
+ }
+ if (arg & TIOCM_DTR) {
+ e100_dtr(info, 0);
+ }
+ /* Handle FEMALE behaviour */
+ if (arg & TIOCM_RI) {
+ e100_ri_out(info, 0);
+ }
+ if (arg & TIOCM_CD) {
+ e100_cd_out(info, 0);
+ }
+ break;
+ case TIOCMSET:
+ e100_rts(info, arg & TIOCM_RTS);
+ e100_dtr(info, arg & TIOCM_DTR);
+ /* Handle FEMALE behaviour */
+ e100_ri_out(info, arg & TIOCM_RI);
+ e100_cd_out(info, arg & TIOCM_CD);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * This routine sends a break character out the serial port.
+ */
+#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */
+static void
+send_break(struct e100_serial * info, int duration)
+{
+ unsigned long flags;
+
+ if (!info->port)
+ return;
+
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + duration;
+
+ save_flags(flags);
+ cli();
+
+ /* Go to manual mode and set the txd pin to 0 */
+
+ info->tx_ctrl &= 0x3F; /* Clear bit 7 (txd) and 6 (tr_enable) */
+ info->port[REG_TR_CTRL] = info->tx_ctrl;
+
+ /* wait for "duration" jiffies */
+
+ schedule();
+
+ info->tx_ctrl |= (0x80 | 0x40); /* Set bit 7 (txd) and 6 (tr_enable) */
+ info->port[REG_TR_CTRL] = info->tx_ctrl;
+
+ /* the DMA gets awfully confused if we toggle the tranceiver like this
+ * so we need to reset it
+ */
+ *info->ocmdadr = 4;
+
+ restore_flags(flags);
+}
+#else
+static void
+rs_break(struct tty_struct *tty, int break_state)
+{
+ struct e100_serial * info = (struct e100_serial *)tty->driver_data;
+ unsigned long flags;
+
+ if (!info->port)
+ return;
+
+ save_flags(flags);
+ cli();
+ if (break_state == -1) {
+ /* Go to manual mode and set the txd pin to 0 */
+ info->tx_ctrl &= 0x3F; /* Clear bit 7 (txd) and 6 (tr_enable) */
+ } else {
+ info->tx_ctrl |= (0x80 | 0x40); /* Set bit 7 (txd) and 6 (tr_enable) */
+ }
+ info->port[REG_TR_CTRL] = info->tx_ctrl;
+ restore_flags(flags);
+}
+#endif
+
+static int
+rs_ioctl(struct tty_struct *tty, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ int error;
+ struct e100_serial * info = (struct e100_serial *)tty->driver_data;
+ int retval;
+
+ if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+ (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) &&
+ (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) {
+ if (tty->flags & (1 << TTY_IO_ERROR))
+ return -EIO;
+ }
+
+ switch (cmd) {
+#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */
+ case TCSBRK: /* SVID version: non-zero arg --> no break */
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ if (signal_pending(current))
+ return -EINTR;
+ if (!arg) {
+ send_break(info, HZ/4); /* 1/4 second */
+ if (signal_pending(current))
+ return -EINTR;
+ }
+ return 0;
+ case TCSBRKP: /* support for POSIX tcsendbreak() */
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ if (signal_pending(current))
+ return -EINTR;
+ send_break(info, arg ? arg*(HZ/10) : HZ/4);
+ if (signal_pending(current))
+ return -EINTR;
+ return 0;
+ case TIOCGSOFTCAR:
+ error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(long));
+ if (error)
+ return error;
+ put_fs_long(C_CLOCAL(tty) ? 1 : 0,
+ (unsigned long *) arg);
+ return 0;
+ case TIOCSSOFTCAR:
+ arg = get_fs_long((unsigned long *) arg);
+ tty->termios->c_cflag =
+ ((tty->termios->c_cflag & ~CLOCAL) |
+ (arg ? CLOCAL : 0));
+ return 0;
+#endif
+ case TIOCMGET:
+ return get_modem_info(info, (unsigned int *) arg);
+ case TIOCMBIS:
+ case TIOCMBIC:
+ case TIOCMSET:
+ return set_modem_info(info, cmd, (unsigned int *) arg);
+ case TIOCGSERIAL:
+ return get_serial_info(info,
+ (struct serial_struct *) arg);
+ case TIOCSSERIAL:
+ return set_serial_info(info,
+ (struct serial_struct *) arg);
+ case TIOCSERGETLSR: /* Get line status register */
+ return get_lsr_info(info, (unsigned int *) arg);
+
+ case TIOCSERGSTRUCT:
+ if (copy_to_user((struct e100_serial *) arg,
+ info, sizeof(struct e100_serial)))
+ return -EFAULT;
+ return 0;
+
+#if defined(CONFIG_RS485)
+ case TIOCSERSETRS485:
+ error = verify_area(VERIFY_WRITE, (void *) arg,
+ sizeof(struct rs485_control));
+
+ if (error)
+ return error;
+
+ return e100_enable_rs485(tty, (struct rs485_control *) arg);
+
+ case TIOCSERWRRS485:
+ error = verify_area(VERIFY_WRITE, (void *) arg,
+ sizeof(struct rs485_write));
+
+ if (error)
+ return error;
+
+ return e100_write_rs485(tty, (struct rs485_write *) arg);
+#endif
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static void
+rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+ struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+
+ if (tty->termios->c_cflag == old_termios->c_cflag)
+ return;
+
+ change_speed(info);
+
+ if ((old_termios->c_cflag & CRTSCTS) &&
+ !(tty->termios->c_cflag & CRTSCTS)) {
+ tty->hw_stopped = 0;
+ rs_start(tty);
+ }
+
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_close()
+ *
+ * This routine is called when the serial port gets closed. First, we
+ * wait for the last remaining data to be sent. Then, we unlink its
+ * S structure from the interrupt chain if necessary, and we free
+ * that IRQ if nothing is left in the chain.
+ * ------------------------------------------------------------
+ */
+static void
+rs_close(struct tty_struct *tty, struct file * filp)
+{
+ struct e100_serial * info = (struct e100_serial *)tty->driver_data;
+ unsigned long flags;
+
+ if (!info)
+ return;
+
+ /* interrupts are disabled for this entire function */
+
+ save_flags(flags);
+ cli();
+
+ if (tty_hung_up_p(filp)) {
+ restore_flags(flags);
+ return;
+ }
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("[%d] rs_close ttyS%d, count = %d\n", current->pid,
+ info->line, info->count);
+#endif
+ if ((tty->count == 1) && (info->count != 1)) {
+ /*
+ * Uh, oh. tty->count is 1, which means that the tty
+ * structure will be freed. Info->count should always
+ * be one in these conditions. If it's greater than
+ * one, we've got real problems, since it means the
+ * serial port won't be shutdown.
+ */
+ printk("rs_close: bad serial port count; tty->count is 1, "
+ "info->count is %d\n", info->count);
+ info->count = 1;
+ }
+ if (--info->count < 0) {
+ printk("rs_close: bad serial port count for ttyS%d: %d\n",
+ info->line, info->count);
+ info->count = 0;
+ }
+ if (info->count) {
+ restore_flags(flags);
+ return;
+ }
+ info->flags |= ASYNC_CLOSING;
+ /*
+ * Save the termios structure, since this port may have
+ * separate termios for callout and dialin.
+ */
+ if (info->flags & ASYNC_NORMAL_ACTIVE)
+ info->normal_termios = *tty->termios;
+ if (info->flags & ASYNC_CALLOUT_ACTIVE)
+ info->callout_termios = *tty->termios;
+ /*
+ * Now we wait for the transmit buffer to clear; and we notify
+ * the line discipline to only process XON/XOFF characters.
+ */
+ tty->closing = 1;
+ if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+ tty_wait_until_sent(tty, info->closing_wait);
+ /*
+ * At this point we stop accepting input. To do this, we
+ * disable the serial receiver and the DMA receive interrupt.
+ */
+#ifdef SERIAL_HANDLE_EARLY_ERRORS
+ e100_disable_serial_data_irq(info);
+#endif
+
+#ifndef CONFIG_SVINTO_SIM
+ e100_disable_rx(info);
+ e100_disable_rxdma_irq(info);
+
+ if (info->flags & ASYNC_INITIALIZED) {
+ /*
+ * Before we drop DTR, make sure the UART transmitter
+ * has completely drained; this is especially
+ * important as we have a transmit FIFO!
+ */
+ rs_wait_until_sent(tty, HZ);
+ }
+#endif
+
+ shutdown(info);
+ if (tty->driver.flush_buffer)
+ tty->driver.flush_buffer(tty);
+ if (tty->ldisc.flush_buffer)
+ tty->ldisc.flush_buffer(tty);
+ tty->closing = 0;
+ info->event = 0;
+ info->tty = 0;
+ if (info->blocked_open) {
+ if (info->close_delay) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(info->close_delay);
+ }
+ wake_up_interruptible(&info->open_wait);
+ }
+ info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
+ ASYNC_CLOSING);
+ wake_up_interruptible(&info->close_wait);
+ restore_flags(flags);
+
+ /* port closed */
+
+#if defined(CONFIG_RS485)
+ if (info->rs485.enabled) {
+ info->rs485.enabled = 0;
+#if defined(CONFIG_RS485_ON_PA)
+ *R_PORT_PA_DATA = port_pa_data_shadow &= ~(1 << rs485_pa_bit);
+#endif
+ }
+#endif
+}
+
+/*
+ * rs_wait_until_sent() --- wait until the transmitter is empty
+ */
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+ unsigned long orig_jiffies;
+ struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+
+ /*
+ * Check R_DMA_CHx_STATUS bit 0-6=number of available bytes in FIFO
+ * R_DMA_CHx_HWSW bit 31-16=nbr of bytes left in DMA buffer (0=64k)
+ */
+ orig_jiffies = jiffies;
+ while(info->xmit.head != info->xmit.tail || /* More in send queue */
+ (*info->ostatusadr & 0x007f)) { /* more in FIFO */
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(1);
+ if (signal_pending(current))
+ break;
+ if (timeout && time_after(jiffies, orig_jiffies + timeout))
+ break;
+ }
+ set_current_state(TASK_RUNNING);
+}
+
+/*
+ * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+void
+rs_hangup(struct tty_struct *tty)
+{
+ struct e100_serial * info = (struct e100_serial *)tty->driver_data;
+
+ rs_flush_buffer(tty);
+ shutdown(info);
+ info->event = 0;
+ info->count = 0;
+ info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+ info->tty = 0;
+ wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_open() and friends
+ * ------------------------------------------------------------
+ */
+static int
+block_til_ready(struct tty_struct *tty, struct file * filp,
+ struct e100_serial *info)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ unsigned long flags;
+ int retval;
+ int do_clocal = 0, extra_count = 0;
+
+ /*
+ * If the device is in the middle of being closed, then block
+ * until it's done, and then try again.
+ */
+ if (tty_hung_up_p(filp) ||
+ (info->flags & ASYNC_CLOSING)) {
+ if (info->flags & ASYNC_CLOSING)
+ interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+ if (info->flags & ASYNC_HUP_NOTIFY)
+ return -EAGAIN;
+ else
+ return -ERESTARTSYS;
+#else
+ return -EAGAIN;
+#endif
+ }
+
+ /*
+ * If this is a callout device, then just make sure the normal
+ * device isn't being used.
+ */
+ if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+ if (info->flags & ASYNC_NORMAL_ACTIVE)
+ return -EBUSY;
+ if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (info->flags & ASYNC_SESSION_LOCKOUT) &&
+ (info->session != current->session))
+ return -EBUSY;
+ if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (info->flags & ASYNC_PGRP_LOCKOUT) &&
+ (info->pgrp != current->pgrp))
+ return -EBUSY;
+ info->flags |= ASYNC_CALLOUT_ACTIVE;
+ return 0;
+ }
+
+ /*
+ * If non-blocking mode is set, or the port is not enabled,
+ * then make the check up front and then exit.
+ */
+ if ((filp->f_flags & O_NONBLOCK) ||
+ (tty->flags & (1 << TTY_IO_ERROR))) {
+ if (info->flags & ASYNC_CALLOUT_ACTIVE)
+ return -EBUSY;
+ info->flags |= ASYNC_NORMAL_ACTIVE;
+ return 0;
+ }
+
+ if (info->flags & ASYNC_CALLOUT_ACTIVE) {
+ if (info->normal_termios.c_cflag & CLOCAL)
+ do_clocal = 1;
+ } else {
+ if (tty->termios->c_cflag & CLOCAL)
+ do_clocal = 1;
+ }
+
+ /*
+ * Block waiting for the carrier detect and the line to become
+ * free (i.e., not in use by the callout). While we are in
+ * this loop, info->count is dropped by one, so that
+ * rs_close() knows when to free things. We restore it upon
+ * exit, either normal or abnormal.
+ */
+ retval = 0;
+ add_wait_queue(&info->open_wait, &wait);
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready before block: ttyS%d, count = %d\n",
+ info->line, info->count);
+#endif
+ save_flags(flags);
+ cli();
+ if (!tty_hung_up_p(filp)) {
+ extra_count++;
+ info->count--;
+ }
+ restore_flags(flags);
+ info->blocked_open++;
+ while (1) {
+ save_flags(flags);
+ cli();
+ if (!(info->flags & ASYNC_CALLOUT_ACTIVE)) {
+ /* assert RTS and DTR */
+ e100_rts(info, 1);
+ e100_dtr(info, 1);
+ }
+ restore_flags(flags);
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (tty_hung_up_p(filp) ||
+ !(info->flags & ASYNC_INITIALIZED)) {
+#ifdef SERIAL_DO_RESTART
+ if (info->flags & ASYNC_HUP_NOTIFY)
+ retval = -EAGAIN;
+ else
+ retval = -ERESTARTSYS;
+#else
+ retval = -EAGAIN;
+#endif
+ break;
+ }
+ if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ !(info->flags & ASYNC_CLOSING) && do_clocal)
+ /* && (do_clocal || DCD_IS_ASSERTED) */
+ break;
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready blocking: ttyS%d, count = %d\n",
+ info->line, info->count);
+#endif
+ schedule();
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&info->open_wait, &wait);
+ if (extra_count)
+ info->count++;
+ info->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready after blocking: ttyS%d, count = %d\n",
+ info->line, info->count);
+#endif
+ if (retval)
+ return retval;
+ info->flags |= ASYNC_NORMAL_ACTIVE;
+ return 0;
+}
+
+/*
+ * This routine is called whenever a serial port is opened.
+ * It performs the serial-specific initialization for the tty structure.
+ */
+static int
+rs_open(struct tty_struct *tty, struct file * filp)
+{
+ struct e100_serial *info;
+ int retval, line;
+ unsigned long page;
+
+ /* find which port we want to open */
+
+ line = MINOR(tty->device) - tty->driver.minor_start;
+
+ if (line < 0 || line >= NR_PORTS)
+ return -ENODEV;
+
+ /* dont allow opening ports that are not enabled in the HW config */
+
+#ifndef CONFIG_ETRAX100_SERIAL_PORT2
+ if (line == 2)
+ return -ENODEV;
+#endif
+#ifndef CONFIG_ETRAX100_SERIAL_PORT3
+ if (line == 3)
+ return -ENODEV;
+#endif
+
+ /* find the corresponding e100_serial struct in the table */
+
+ info = rs_table + line;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("[%d] rs_open %s%d, count = %d\n", current->pid,
+ tty->driver.name, info->line,
+ info->count);
+#endif
+
+ info->count++;
+ tty->driver_data = info;
+ info->tty = tty;
+
+#if (LINUX_VERSION_CODE > 0x20100)
+ info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+#endif
+
+ if (!tmp_buf) {
+ page = get_zeroed_page(GFP_KERNEL);
+ if (!page) {
+ return -ENOMEM;
+ }
+ if (tmp_buf)
+ free_page(page);
+ else
+ tmp_buf = (unsigned char *) page;
+ }
+
+ /*
+ * If the port is the middle of closing, bail out now
+ */
+ if (tty_hung_up_p(filp) ||
+ (info->flags & ASYNC_CLOSING)) {
+ if (info->flags & ASYNC_CLOSING)
+ interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+ return ((info->flags & ASYNC_HUP_NOTIFY) ?
+ -EAGAIN : -ERESTARTSYS);
+#else
+ return -EAGAIN;
+#endif
+ }
+
+ /*
+ * Start up the serial port
+ */
+
+ retval = startup(info);
+ if (retval)
+ return retval;
+
+ retval = block_til_ready(tty, filp, info);
+ if (retval) {
+#ifdef SERIAL_DEBUG_OPEN
+ printk("rs_open returning after block_til_ready with %d\n",
+ retval);
+#endif
+ return retval;
+ }
+
+ if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) {
+ if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+ *tty->termios = info->normal_termios;
+ else
+ *tty->termios = info->callout_termios;
+ change_speed(info);
+ }
+
+ info->session = current->session;
+ info->pgrp = current->pgrp;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("rs_open ttyS%d successful...\n", info->line);
+#endif
+ return 0;
+}
+
+/*
+ * /proc fs routines....
+ */
+
+static inline int line_info(char *buf, struct e100_serial *info)
+{
+ char stat_buf[30], control, status;
+ int ret;
+ unsigned long flags;
+
+ ret = sprintf(buf, "%d: uart:E100 port:%lX irq:%d",
+ info->line, info->port, info->irq);
+
+ if (!info->port || (info->type == PORT_UNKNOWN)) {
+ ret += sprintf(buf+ret, "\n");
+ return ret;
+ }
+
+ stat_buf[0] = 0;
+ stat_buf[1] = 0;
+ if (E100_RTS_GET(info))
+ strcat(stat_buf, "|RTS");
+ if (E100_CTS_GET(info))
+ strcat(stat_buf, "|CTS");
+ if (E100_DTR_GET(info))
+ strcat(stat_buf, "|DTR");
+ if (E100_DSR_GET(info))
+ strcat(stat_buf, "|DSR");
+ if (E100_CD_GET(info))
+ strcat(stat_buf, "|CD");
+ if (E100_RI_GET(info))
+ strcat(stat_buf, "|RI");
+
+ ret += sprintf(buf+ret, " baud:%d", info->baud);
+
+ ret += sprintf(buf+ret, " tx:%d rx:%d",
+ info->icount.tx, info->icount.rx);
+
+ if (info->icount.frame)
+ ret += sprintf(buf+ret, " fe:%d", info->icount.frame);
+
+ if (info->icount.parity)
+ ret += sprintf(buf+ret, " pe:%d", info->icount.parity);
+
+ if (info->icount.brk)
+ ret += sprintf(buf+ret, " brk:%d", info->icount.brk);
+
+ if (info->icount.overrun)
+ ret += sprintf(buf+ret, " oe:%d", info->icount.overrun);
+
+ /*
+ * Last thing is the RS-232 status lines
+ */
+ ret += sprintf(buf+ret, " %s\n", stat_buf+1);
+ return ret;
+}
+
+int rs_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ int i, len = 0, l;
+ off_t begin = 0;
+
+ len += sprintf(page, "serinfo:1.0 driver:%s\n",
+ serial_version);
+ for (i = 0; i < NR_PORTS && len < 4000; i++) {
+ l = line_info(page + len, &rs_table[i]);
+ len += l;
+ if (len+begin > off+count)
+ goto done;
+ if (len+begin < off) {
+ begin += len;
+ len = 0;
+ }
+ }
+ *eof = 1;
+done:
+ if (off >= len+begin)
+ return 0;
+ *start = page + (off-begin);
+ return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/* Finally, routines used to initialize the serial driver. */
+
+static void
+show_serial_version(void)
+{
+ printk("ETRAX 100LX serial-driver %s, (c) 2000 Axis Communications AB\r\n",
+ serial_version);
+}
+
+/* rs_init inits the driver at boot (using the module_init chain) */
+
+static int __init
+rs_init(void)
+{
+ int i;
+ struct e100_serial *info;
+
+ show_serial_version();
+
+ init_bh(SERIAL_BH, do_serial_bh);
+
+ /* Setup the timed flush handler system */
+
+ init_timer(&flush_timer);
+ flush_timer.function = timed_flush_handler;
+ mod_timer(&flush_timer, jiffies + MAX_FLUSH_TIME);
+
+ /* Initialize the tty_driver structure */
+
+ memset(&serial_driver, 0, sizeof(struct tty_driver));
+ serial_driver.magic = TTY_DRIVER_MAGIC;
+#if (LINUX_VERSION_CODE > 0x20100)
+ serial_driver.driver_name = "serial";
+#endif
+ serial_driver.name = "ttyS";
+ serial_driver.major = TTY_MAJOR;
+ serial_driver.minor_start = 64;
+ serial_driver.num = NR_PORTS; /* etrax100 has 4 serial ports */
+ serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
+ serial_driver.subtype = SERIAL_TYPE_NORMAL;
+ serial_driver.init_termios = tty_std_termios;
+ serial_driver.init_termios.c_cflag =
+ B115200 | CS8 | CREAD | HUPCL | CLOCAL; /* is normally B9600 default... */
+ serial_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
+ serial_driver.refcount = &serial_refcount;
+ serial_driver.table = serial_table;
+ serial_driver.termios = serial_termios;
+ serial_driver.termios_locked = serial_termios_locked;
+
+ serial_driver.open = rs_open;
+ serial_driver.close = rs_close;
+ serial_driver.write = rs_write;
+ /* should we have an rs_put_char as well here ? */
+ serial_driver.flush_chars = rs_flush_chars;
+ serial_driver.write_room = rs_write_room;
+ serial_driver.chars_in_buffer = rs_chars_in_buffer;
+ serial_driver.flush_buffer = rs_flush_buffer;
+ serial_driver.ioctl = rs_ioctl;
+ serial_driver.throttle = rs_throttle;
+ serial_driver.unthrottle = rs_unthrottle;
+ serial_driver.set_termios = rs_set_termios;
+ serial_driver.stop = rs_stop;
+ serial_driver.start = rs_start;
+ serial_driver.hangup = rs_hangup;
+#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */
+ serial_driver.break_ctl = rs_break;
+#endif
+#if (LINUX_VERSION_CODE >= 131343)
+ serial_driver.send_xchar = rs_send_xchar;
+ serial_driver.wait_until_sent = rs_wait_until_sent;
+ serial_driver.read_proc = rs_read_proc;
+#endif
+
+ /*
+ * The callout device is just like normal device except for
+ * major number and the subtype code.
+ */
+ callout_driver = serial_driver;
+ callout_driver.name = "cua";
+ callout_driver.major = TTYAUX_MAJOR;
+ callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+#if (LINUX_VERSION_CODE >= 131343)
+ callout_driver.read_proc = 0;
+ callout_driver.proc_entry = 0;
+#endif
+
+ if (tty_register_driver(&serial_driver))
+ panic("Couldn't register serial driver\n");
+ if (tty_register_driver(&callout_driver))
+ panic("Couldn't register callout driver\n");
+
+ /* do some initializing for the separate ports */
+
+ for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
+ info->line = i;
+ info->tty = 0;
+ info->type = PORT_ETRAX100;
+ info->tr_running = 0;
+ info->fifo_magic = 0;
+ info->fifo_didmagic = 0;
+ info->flags = 0;
+ info->close_delay = 5*HZ/10;
+ info->closing_wait = 30*HZ;
+ info->x_char = 0;
+ info->event = 0;
+ info->count = 0;
+ info->blocked_open = 0;
+ info->tqueue.routine = do_softint;
+ info->tqueue.data = info;
+ info->callout_termios = callout_driver.init_termios;
+ info->normal_termios = serial_driver.init_termios;
+ init_waitqueue_head(&info->open_wait);
+ init_waitqueue_head(&info->close_wait);
+ info->xmit.buf = 0;
+ info->xmit.tail = info->xmit.head = 0;
+
+ printk(KERN_INFO "%s%d at 0x%x is a builtin UART with DMA\n",
+ serial_driver.name, info->line, (unsigned int)info->port);
+ }
+
+#ifndef CONFIG_SVINTO_SIM
+ /* Not needed in simulator. May only complicate stuff. */
+ /* hook the irq's for DMA channel 6 and 7, serial output and input, and some more... */
+ if(request_irq(22, tr_interrupt, SA_INTERRUPT, "serial 0 dma tr", NULL))
+ panic("irq22");
+ if(request_irq(23, rec_interrupt, SA_INTERRUPT, "serial 0 dma rec", NULL))
+ panic("irq23");
+#ifdef SERIAL_HANDLE_EARLY_ERRORS
+ if(request_irq(8, ser_interrupt, SA_INTERRUPT, "serial ", NULL))
+ panic("irq8");
+#endif
+ if(request_irq(24, tr_interrupt, SA_INTERRUPT, "serial 1 dma tr", NULL))
+ panic("irq24");
+ if(request_irq(25, rec_interrupt, SA_INTERRUPT, "serial 1 dma rec", NULL))
+ panic("irq25");
+#ifdef CONFIG_ETRAX100_SERIAL_PORT2
+ /* DMA Shared with par0 (and SCSI0 and ATA) */
+ if(request_irq(18, tr_interrupt, SA_SHIRQ, "serial 2 dma tr", NULL))
+ panic("irq18");
+ if(request_irq(19, rec_interrupt, SA_SHIRQ, "serial 2 dma rec", NULL))
+ panic("irq19");
+#endif
+#ifdef CONFIG_ETRAX100_SERIAL_PORT3
+ /* DMA Shared with par1 (and SCSI1 and Extern DMA 0) */
+ if(request_irq(20, tr_interrupt, SA_SHIRQ, "serial 3 dma tr", NULL))
+ panic("irq20");
+ if(request_irq(21, rec_interrupt, SA_SHIRQ, "serial 3 dma rec", NULL))
+ panic("irq21");
+#endif
+#ifdef CONFIG_ETRAX100_SERIAL_FLUSH_DMA_FAST
+ /* TODO: a timeout_interrupt needs to be written that calls timeout_handler */
+ if(request_irq(TIMER1_IRQ_NBR, timeout_interrupt, SA_SHIRQ,
+ "fast serial dma timeout", NULL)) {
+ printk("err: timer1 irq\n");
+ }
+#endif
+#endif /* CONFIG_SVINTO_SIM */
+
+ return 0;
+}
+
+/* this makes sure that rs_init is called during kernel boot */
+
+module_init(rs_init);
+
+/*
+ * register_serial and unregister_serial allows for serial ports to be
+ * configured at run-time, to support PCMCIA modems.
+ */
+int
+register_serial(struct serial_struct *req)
+{
+ return -1;
+}
+
+void unregister_serial(int line)
+{
+}
diff --git a/arch/cris/drivers/serial.h b/arch/cris/drivers/serial.h
new file mode 100644
index 000000000..650c5e9b4
--- /dev/null
+++ b/arch/cris/drivers/serial.h
@@ -0,0 +1,106 @@
+/*
+ * serial.h: Arch-dep definitions for the Etrax100 serial driver.
+ *
+ * Copyright (C) 1998, 1999, 2000 Axis Communications AB
+ */
+
+#ifndef _ETRAX100_SERIAL_H
+#define _ETRAX100_SERIAL_H
+
+#include <linux/config.h>
+#include <linux/circ_buf.h>
+#include <asm/termios.h>
+
+/* Software state per channel */
+
+#ifdef __KERNEL__
+/*
+ * This is our internal structure for each serial port's state.
+ *
+ * Many fields are paralleled by the structure used by the serial_struct
+ * structure.
+ *
+ * For definitions of the flags field, see tty.h
+ */
+
+struct e100_serial {
+ int baud;
+ volatile unsigned char * port; /* R_SERIALx_CTRL */
+ unsigned long irq; /* bitnr in R_IRQ_MASK2 for dmaX_descr */
+
+ volatile char *oclrintradr; /* adr to R_DMA_CHx_CLR_INTR, output */
+ volatile unsigned long *ofirstadr; /* adr to R_DMA_CHx_FIRST, output */
+ volatile char *ocmdadr; /* adr to R_DMA_CHx_CMD, output */
+ const volatile unsigned short *ostatusadr; /* adr to R_DMA_CHx_STATUS, output */
+ volatile unsigned long *ohwswadr; /* adr to R_DMA_CHx_HWSW, output */
+
+ volatile char *iclrintradr; /* adr to R_DMA_CHx_CLR_INTR, input */
+ volatile unsigned long *ifirstadr; /* adr to R_DMA_CHx_FIRST, input */
+ volatile char *icmdadr; /* adr to R_DMA_CHx_CMD, input */
+ const volatile unsigned short *istatusadr; /* adr to R_DMA_CHx_STATUS, input */
+ volatile unsigned long *ihwswadr; /* adr to R_DMA_CHx_HWSW, input */
+
+ int flags; /* defined in tty.h */
+
+ unsigned char rx_ctrl; /* shadow for R_SERIALx_REC_CTRL */
+ unsigned char tx_ctrl; /* shadow for R_SERIALx_TR_CTRL */
+ unsigned char iseteop; /* bit number for R_SET_EOP for the input dma */
+/* end of fields defined in rs_table[] in .c-file */
+ unsigned char fifo_didmagic; /* a fifo eop has been forced */
+
+ struct etrax_dma_descr tr_descr, rec_descr;
+
+ int fifo_magic; /* fifo amount - bytes left in dma buffer */
+
+ volatile int tr_running; /* 1 if output is running */
+
+ struct tty_struct *tty;
+ int read_status_mask;
+ int ignore_status_mask;
+ int x_char; /* xon/xoff character */
+ int close_delay;
+ unsigned short closing_wait;
+ unsigned short closing_wait2;
+ unsigned long event;
+ unsigned long last_active;
+ int line;
+ int type; /* PORT_ETRAX100 */
+ int count; /* # of fd on device */
+ int blocked_open; /* # of blocked opens */
+ long session; /* Session of opening process */
+ long pgrp; /* pgrp of opening process */
+ struct circ_buf xmit;
+
+ struct tq_struct tqueue;
+ struct async_icount icount; /* error-statistics etc.*/
+ struct termios normal_termios;
+ struct termios callout_termios;
+#ifdef DECLARE_WAITQUEUE
+ wait_queue_head_t open_wait;
+ wait_queue_head_t close_wait;
+#else
+ struct wait_queue *open_wait;
+ struct wait_queue *close_wait;
+#endif
+
+#ifdef CONFIG_RS485
+ struct rs485_control rs485; /* RS-485 support */
+#endif
+};
+
+/* this PORT is not in the standard serial.h. it's not actually used for
+ * anything since we only have one type of async serial-port anyway in this
+ * system.
+ */
+
+#define PORT_ETRAX100 1
+
+/*
+ * Events are used to schedule things to happen at timer-interrupt
+ * time, instead of at rs interrupt time.
+ */
+#define RS_EVENT_WRITE_WAKEUP 0
+
+#endif /* __KERNEL__ */
+
+#endif /* !(_ETRAX100_SERIAL_H) */
diff --git a/arch/cris/kernel/Makefile b/arch/cris/kernel/Makefile
new file mode 100644
index 000000000..0681e4807
--- /dev/null
+++ b/arch/cris/kernel/Makefile
@@ -0,0 +1,25 @@
+# $Id: Makefile,v 1.3 2001/01/10 21:11:07 bjornw Exp $
+#
+# 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...
+
+.S.o:
+ $(CC) $(AFLAGS) -traditional -c $< -o $*.o
+
+all: kernel.o head.o
+
+O_TARGET := kernel.o
+obj-y := process.o signal.o entry.o traps.o irq.o \
+ ptrace.o setup.o time.o sys_cris.o shadows.o \
+ debugport.o semaphore.o
+
+obj-$(CONFIG_KGDB) += kgdb.o
+
+clean:
+
+include $(TOPDIR)/Rules.make
diff --git a/arch/cris/kernel/debugport.c b/arch/cris/kernel/debugport.c
new file mode 100644
index 000000000..a2b29be95
--- /dev/null
+++ b/arch/cris/kernel/debugport.c
@@ -0,0 +1,242 @@
+/* Serialport functions for debugging
+ *
+ * Copyright (c) 2000 Axis Communications AB
+ *
+ * Authors: Bjorn Wesen
+ *
+ * Exports:
+ * console_print_etrax(char *buf)
+ * int getDebugChar()
+ * putDebugChar(int)
+ * enableDebugIRQ()
+ * init_etrax_debug()
+ *
+ * $Log: debugport.c,v $
+ * Revision 1.4 2000/10/06 12:37:26 bjornw
+ * Use physical addresses when talking to DMA
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/major.h>
+
+#include <asm/system.h>
+#include <asm/svinto.h>
+#include <asm/io.h> /* Get SIMCOUT. */
+
+/* Which serial-port is our debug port ? */
+
+#if defined(CONFIG_DEBUG_PORT0) || defined(CONFIG_DEBUG_PORT_NULL)
+#define DEBUG_PORT_IDX 0
+#define DEBUG_OCMD R_DMA_CH6_CMD
+#define DEBUG_FIRST R_DMA_CH6_FIRST
+#define DEBUG_OCLRINT R_DMA_CH6_CLR_INTR
+#define DEBUG_STATUS R_DMA_CH6_STATUS
+#define DEBUG_READ R_SERIAL0_READ
+#define DEBUG_WRITE R_SERIAL0_TR_DATA
+#define DEBUG_TR_CTRL R_SERIAL0_TR_CTRL
+#define DEBUG_REC_CTRL R_SERIAL0_REC_CTRL
+#define DEBUG_IRQ IO_STATE(R_IRQ_MASK1_SET, ser0_data, set)
+#define DEBUG_DMA_IRQ_CLR IO_STATE(R_IRQ_MASK2_CLR, dma6_descr, clr)
+#endif
+
+#ifdef CONFIG_DEBUG_PORT1
+#define DEBUG_PORT_IDX 1
+#define DEBUG_OCMD R_DMA_CH8_CMD
+#define DEBUG_FIRST R_DMA_CH8_FIRST
+#define DEBUG_OCLRINT R_DMA_CH8_CLR_INTR
+#define DEBUG_STATUS R_DMA_CH8_STATUS
+#define DEBUG_READ R_SERIAL1_READ
+#define DEBUG_WRITE R_SERIAL1_TR_DATA
+#define DEBUG_TR_CTRL R_SERIAL1_TR_CTRL
+#define DEBUG_REC_CTRL R_SERIAL1_REC_CTRL
+#define DEBUG_IRQ IO_STATE(R_IRQ_MASK1_SET, ser1_data, set)
+#define DEBUG_DMA_IRQ_CLR IO_STATE(R_IRQ_MASK2_CLR, dma8_descr, clr)
+#endif
+
+#ifdef CONFIG_DEBUG_PORT2
+#define DEBUG_PORT_IDX 2
+#define DEBUG_OCMD R_DMA_CH2_CMD
+#define DEBUG_FIRST R_DMA_CH2_FIRST
+#define DEBUG_OCLRINT R_DMA_CH2_CLR_INTR
+#define DEBUG_STATUS R_DMA_CH2_STATUS
+#define DEBUG_READ R_SERIAL2_READ
+#define DEBUG_WRITE R_SERIAL2_TR_DATA
+#define DEBUG_TR_CTRL R_SERIAL2_TR_CTRL
+#define DEBUG_REC_CTRL R_SERIAL2_REC_CTRL
+#define DEBUG_IRQ IO_STATE(R_IRQ_MASK1_SET, ser2_data, set)
+#define DEBUG_DMA_IRQ_CLR IO_STATE(R_IRQ_MASK2_CLR, dma2_descr, clr)
+#endif
+
+#ifdef CONFIG_DEBUG_PORT3
+#define DEBUG_PORT_IDX 3
+#define DEBUG_OCMD R_DMA_CH4_CMD
+#define DEBUG_FIRST R_DMA_CH4_FIRST
+#define DEBUG_OCLRINT R_DMA_CH4_CLR_INTR
+#define DEBUG_STATUS R_DMA_CH4_STATUS
+#define DEBUG_READ R_SERIAL3_READ
+#define DEBUG_WRITE R_SERIAL3_TR_DATA
+#define DEBUG_TR_CTRL R_SERIAL3_TR_CTRL
+#define DEBUG_REC_CTRL R_SERIAL3_REC_CTRL
+#define DEBUG_IRQ IO_STATE(R_IRQ_MASK1_SET, ser3_data, set)
+#define DEBUG_DMA_IRQ_CLR IO_STATE(R_IRQ_MASK2_CLR, dma4_descr, clr)
+#endif
+
+/* Write a string of count length to the console (debug port) using DMA, polled
+ * for completion. Interrupts are disabled during the whole process. Some
+ * caution needs to be taken to not interfere with ttyS business on this port.
+ */
+
+static void
+console_write(struct console *co, const char *buf, unsigned int len)
+{
+ static struct etrax_dma_descr descr;
+ unsigned long flags;
+ int in_progress;
+
+#ifdef CONFIG_DEBUG_PORT_NULL
+ /* no debug printout at all */
+ return;
+#endif
+
+#ifdef CONFIG_SVINTO_SIM
+ /* no use to simulate the serial debug output */
+ SIMCOUT(buf,len);
+ return;
+#endif
+
+ save_flags(flags);
+ cli();
+
+#ifdef CONFIG_KGDB
+ /* kgdb needs to output debug info using the gdb protocol */
+ putDebugString(buf, len);
+ restore_flags(flags);
+ return;
+#endif
+
+ /* make sure the transmitter is enabled.
+ * NOTE: this overrides any setting done in ttySx, to 8N1, no auto-CTS.
+ * in the future, move the tr/rec_ctrl shadows from etrax100ser.c to
+ * shadows.c and use it here as well...
+ */
+
+ *DEBUG_TR_CTRL = 0x40;
+
+ /* if the tty has some ongoing business, remember it */
+
+ in_progress = *DEBUG_OCMD & 7;
+
+ if(in_progress) {
+ /* wait until the output dma channel is ready */
+
+ while(*DEBUG_OCMD & 7) /* nothing */ ;
+ }
+
+ descr.ctrl = d_eol;
+ descr.sw_len = len;
+ descr.buf = __pa(buf);
+
+ *DEBUG_FIRST = __pa(&descr); /* write to R_DMAx_FIRST */
+ *DEBUG_OCMD = 1; /* dma command start -> R_DMAx_CMD */
+
+ /* wait until the output dma channel is ready again */
+
+ while(*DEBUG_OCMD & 7) /* nothing */;
+
+ /* clear pending interrupts so we don't get a surprise below */
+
+ if(in_progress)
+ *DEBUG_OCLRINT = 2; /* only clear EOP, leave DESCR for the tty */
+ else
+ *DEBUG_OCLRINT = 3; /* clear both EOP and DESCR */
+
+ while(*DEBUG_STATUS & 0x7f); /* wait until output FIFO is empty as well */
+
+ restore_flags(flags);
+}
+
+/* legacy function */
+
+void
+console_print_etrax(const char *buf)
+{
+ console_write(NULL, buf, strlen(buf));
+}
+
+/* Use polling to get a single character FROM the debug port */
+
+int
+getDebugChar(void)
+{
+ unsigned long readval;
+
+ do {
+ readval = *DEBUG_READ;
+ } while(!(readval & IO_MASK(R_SERIAL0_READ, data_avail)));
+
+ return (readval & IO_MASK(R_SERIAL0_READ, data_in));
+}
+
+/* Use polling to put a single character to the debug port */
+
+void
+putDebugChar(int val)
+{
+ while(!(*DEBUG_READ & IO_MASK(R_SERIAL0_READ, tr_ready))) ;
+;
+ *DEBUG_WRITE = val;
+}
+
+/* Enable irq for receiving chars on the debug port, used by kgdb */
+
+void
+enableDebugIRQ(void)
+{
+ *R_IRQ_MASK1_SET = DEBUG_IRQ;
+ /* use R_VECT_MASK directly, since we really bypass Linux normal
+ * IRQ handling in kgdb anyway, we don't need to use enable_irq
+ */
+ *R_VECT_MASK_SET = IO_STATE(R_VECT_MASK_SET, serial, set);
+
+ *DEBUG_REC_CTRL = IO_STATE(R_SERIAL0_REC_CTRL, rec_enable, enable);
+}
+
+static kdev_t
+console_device(struct console *c)
+{
+ return MKDEV(TTY_MAJOR, 64 + c->index);
+}
+
+static int __init
+console_setup(struct console *co, char *options)
+{
+ return 0;
+}
+
+static struct console sercons = {
+ "ttyS",
+ console_write,
+ NULL,
+ console_device,
+ NULL,
+ NULL,
+ console_setup,
+ CON_PRINTBUFFER,
+ DEBUG_PORT_IDX,
+ 0,
+ NULL
+};
+
+/*
+ * Register console (for printk's etc)
+ */
+
+void __init
+init_etrax_debug(void)
+{
+ register_console(&sercons);
+}
diff --git a/arch/cris/kernel/entry.S b/arch/cris/kernel/entry.S
new file mode 100644
index 000000000..1af62eb2c
--- /dev/null
+++ b/arch/cris/kernel/entry.S
@@ -0,0 +1,738 @@
+/* $Id: entry.S,v 1.11 2001/01/10 21:13:29 bjornw Exp $
+ *
+ * linux/arch/cris/entry.S
+ *
+ * Copyright (C) 2000 Axis Communications AB
+ *
+ * Authors: Bjorn Wesen (bjornw@axis.com)
+ *
+ * $Log: entry.S,v $
+ * Revision 1.11 2001/01/10 21:13:29 bjornw
+ * SYMBOL_NAME is defined incorrectly for the compiler options we currently use
+ *
+ * Revision 1.10 2000/12/18 23:47:56 bjornw
+ * * Added syscall trace support (ptrace), completely untested of course
+ * * Removed redundant check for NULL entries in syscall_table
+ *
+ * Revision 1.9 2000/11/21 16:40:51 bjornw
+ * * New frame type used when an SBFS frame needs to be popped without
+ * actually restarting the instruction
+ * * Enable interrupts in signal_return (they did so in x86, I hope it's a good
+ * idea)
+ *
+ * Revision 1.8 2000/11/17 16:53:35 bjornw
+ * Added detection of frame-type in Rexit, so that mmu_bus_fault can
+ * use ret_from_intr in the return-path to check for signals (like SEGV)
+ * and other foul things that might have occured during the fault.
+ *
+ * Revision 1.7 2000/10/06 15:04:28 bjornw
+ * Include mof in register savings
+ *
+ * Revision 1.6 2000/09/12 16:02:44 bjornw
+ * Linux-2.4.0-test7 derived updates
+ *
+ * Revision 1.5 2000/08/17 15:35:15 bjornw
+ * 2.4.0-test6 changed local_irq_count and friends API
+ *
+ * Revision 1.4 2000/08/02 13:59:30 bjornw
+ * Removed olduname and uname from the syscall list
+ *
+ * Revision 1.3 2000/07/31 13:32:58 bjornw
+ * * Export ret_from_intr
+ * * _resume updated (prev/last tjohejsan)
+ * * timer_interrupt obsolete
+ * * SIGSEGV detection in mmu_bus_fault temporarily disabled
+ *
+ *
+ */
+
+/*
+ * entry.S contains the system-call and fault low-level handling routines.
+ *
+ * NOTE: This code handles signal-recognition, which happens every time
+ * after a timer-interrupt and after each system call.
+ *
+ * Stack layout in 'ret_from_system_call':
+ * ptrace needs to have all regs on the stack.
+ * if the order here is changed, it needs to be
+ * updated in fork.c:copy_process, signal.c:do_signal,
+ * ptrace.c and ptrace.h
+ *
+ */
+
+#include <linux/linkage.h>
+#include <linux/sys.h>
+
+ ;; functions exported from this file
+
+ .globl _system_call
+ .globl _ret_from_intr
+ .globl _ret_from_sys_call
+ .globl _resume
+ .globl _multiple_interrupt
+ .globl _hwbreakpoint
+ .globl _IRQ1_interrupt
+ .globl _timer_interrupt
+ .globl _timer_shortcut
+ .globl _spurious_interrupt
+ .globl _hw_bp_trigs
+ .globl _mmu_bus_fault
+
+ .globl _sys_call_table
+
+ ;; syscall error codes
+
+LENOSYS = 38
+
+ ;; offsets into the task_struct (found at sp aligned to THREAD_SIZE, 8192)
+ ;; linux/sched.h
+
+LTASK_SIGPENDING = 8
+LTASK_NEEDRESCHED = 20
+LTASK_PTRACE = 24
+
+ ;; some pt_regs offsets (from ptrace.h)
+
+LORIG_R10 = 4
+LR13 = 8
+LR12 = 12
+LR11 = 16
+LR10 = 20
+LR1 = 56
+LR0 = 60
+LDCCR = 68
+LSRP = 72
+LIRP = 76
+
+ ;; below are various parts of system_call which are not in the fast-path
+
+ ;; handle software irqs
+
+handle_softirq:
+ push r9
+ jsr _do_softirq ; call the C routine for softirq handling
+ pop r9
+
+ ;; fall-through
+
+_ret_from_intr:
+ ;; check for resched only if we're going back to user-mode
+
+ move ccr, r0
+ btstq 8, r0 ; U-flag
+ bpl Rexit ; go back directly
+ nop
+ ba ret_with_reschedule ; go back but check schedule and signals first
+ nop
+
+reschedule:
+ ;; keep r9 intact
+ push r9
+ jsr _schedule
+ pop r9
+ ba _ret_from_sys_call
+ nop
+
+ ;; return but call do_signal first
+signal_return:
+ ei ; we can get here from an interrupt
+ move.d r9,r10 ; do_signals syscall/irq param
+ moveq 0,r11 ; oldset param - 0 in this case
+ move.d sp,r12 ; another argument to do_signal (the regs param)
+ jsr _do_signal ; arch/cris/kernel/signal.c
+ ba Rexit
+ nop
+
+ ;; The system_call is called by a BREAK instruction, which works like
+ ;; an interrupt call but it stores the return PC in BRP instead of IRP.
+ ;; Since we dont really want to have two epilogues (one for system calls
+ ;; and one for interrupts) we push the contents of BRP instead of IRP in the
+ ;; system call prologue, to make it look like an ordinary interrupt on the
+ ;; stackframe.
+ ;;
+ ;; Since we can't have system calls inside interrupts, it should not matter
+ ;; that we don't stack IRP.
+ ;;
+ ;; In r1 we have the wanted syscall number. Arguments come in r10,r11,r12,r13,r0
+ ;;
+ ;; This function looks on the _surface_ like spaghetti programming, but it's
+ ;; really designed so that the fast-path does not force cache-loading of non-used
+ ;; instructions. Only the non-common cases cause the outlined code to run..
+
+_system_call:
+ ;; stack-frame similar to the irq heads, which is reversed in ret_from_sys_call
+ push brp ; this is normally push irp
+ push srp
+ push dccr
+ push mof
+ subq 14*4,sp ; make room for r0-r13
+ movem r13,[sp] ; push r0-r13
+ push r10 ; push orig_r10
+ clear.d [sp=sp-4] ; frametype == 0, normal stackframe
+
+ move.d r10,r2 ; save for later
+
+ movs.w -LENOSYS,r10
+ move.d r10,[sp+LR10] ; put the default return value in r10 in the frame
+
+ move.d sp,r10
+ jsr _set_esp0 ; save top of frame (clobbers r9...)
+
+ ;; check if this process is syscall-traced
+
+ move.d sp, r10
+ and.d -8192, r10 ; THREAD_SIZE == 8192
+ move.d [r10+LTASK_PTRACE],r10
+ btstq 2, r10 ; PT_TRACESYS
+ bmi tracesys
+ nop
+
+ ;; check for sanity in the requested syscall number
+
+ cmpu.w NR_syscalls,r1
+ bcc _ret_from_sys_call
+ lslq 2,r1 ; multiply by 4, in the delay slot
+
+ ;; read the system call vector into r1
+
+ move.d [r1+_sys_call_table],r1
+
+ ;; the parameter carrying registers r11, r12 and 13 are intact - restore r10.
+ ;; the fifth parameter (if any) was in r0, and we need to put it on the stack
+
+ push r0
+ move.d r2,r10
+
+ jsr r1 ; actually call the corresponding system call
+ addq 4,sp ; pop the r0 parameter
+ move.d r10,[sp+LR10] ; save the return value
+
+ moveq 1,r9 ; "parameter" to ret_from_sys_call to show it was a sys call
+
+ ;; fall through into ret_from_sys_call to return
+
+_ret_from_sys_call:
+ ;; r9 is a parameter - if 1, we came from a syscall, if 0, from an irq
+
+ ;; check if any bottom halves need service
+
+ move.d [_irq_stat],r0 ; softirq_active
+ and.d [_irq_stat+4],r0 ; softirq_mask
+ bne handle_softirq
+ nop
+
+ret_with_reschedule:
+ ;; first get the current task-struct pointer (see top for defs)
+
+ move.d sp, r0
+ and.d -8192, r0 ; THREAD_SIZE == 8192
+
+ ;; see if we want to reschedule into another process
+
+ test.d [r0+LTASK_NEEDRESCHED]
+ bne reschedule
+ nop
+
+ ;; see if we need to run signal checks (important that r9 is intact here)
+
+ test.d [r0+LTASK_SIGPENDING]
+ bne signal_return
+ nop
+
+Rexit:
+ ;; this epilogue MUST match the prologues in multiple_interrupt, irq.h and ptregs.h
+ pop r10 ; frametype
+ bne RBFexit ; was not CRIS_FRAME_NORMAL, handle otherwise
+ addq 4,sp ; skip orig_r10, in delayslot
+ movem [sp+],r13 ; registers r0-r13
+ pop mof ; multiply overflow register
+ pop dccr ; condition codes
+ pop srp ; subroutine return pointer
+ jmpu [sp+] ; return by popping irp and jumping there
+ ;; jmpu takes the U-flag into account to see if we return to
+ ;; user-mode or kernel mode.
+
+RBFexit:
+ cmpq 2, r10 ; was it CRIS_FRAME_FIXUP ?
+ beq 2f
+ movem [sp+],r13 ; registers r0-r13, in delay slot
+ pop mof ; multiply overflow register
+ pop dccr ; condition codes
+ pop srp ; subroutine return pointer
+ rbf [sp+] ; return by popping the CPU status
+
+2: pop mof ; multiply overflow register
+ pop dccr ; condition codes
+ pop srp ; subroutine return pointer
+ ;; now we have a 4-word SBFS frame which we do not want to restore
+ ;; using RBF since we have made a fixup. instead we would like to
+ ;; just get the PC value to restart it with, and skip the rest of
+ ;; the frame.
+ pop irp ; fixup location will be here
+ pop p8 ; null pop
+ pop p8 ; null pop
+ reti ; return to IRP, taking U-flag into account
+ pop p8 ; null pop in delayslot
+
+
+tracesys:
+ ;; this first invocation of syscall_trace _requires_ that
+ ;; LR10 in the frame contains -LENOSYS (as is set in the beginning
+ ;; of system_call
+
+ jsr _syscall_trace
+
+ ;; now we should more or less do the same things as in the system_call
+ ;; but since our argument regs got clobbered during syscall_trace and
+ ;; because syscall_trace might want to alter them, we need to reload them
+ ;; from the stack-frame as we use them.
+
+ ;; check for sanity in the requested syscall number
+
+ move.d [sp+LR1], r1
+ movs.w -LENOSYS, r10
+ cmpu.w NR_syscalls,r1
+ bcc 1f
+ lslq 2,r1 ; multiply by 4, in the delay slot
+
+ ;; read the system call vector entry into r1
+
+ move.d [r1+_sys_call_table],r1
+
+ ;; restore r10, r11, r12, r13 and r0 into the needed registers
+
+ move.d [sp+LORIG_R10], r10 ; LR10 is already filled with -LENOSYS
+ move.d [sp+LR11], r11
+ move.d [sp+LR12], r12
+ move.d [sp+LR13], r13
+ move.d [sp+LR0], r0
+
+ ;; the fifth parameter needs to be put on the stack for the system
+ ;; call to find it
+
+ push r0
+ jsr r1 ; actually call the system-call
+ addq 4,sp ; pop the r0 parameter
+
+1: move.d r10,[sp+LR10] ; save the return value
+
+ ;; second call of syscall_trace, to let it grab the results
+
+ jsr _syscall_trace
+
+ moveq 1,r9 ; "parameter" to ret_from_sys_call to show it was a sys call
+ ba _ret_from_sys_call
+ nop
+
+ ;; from asm/processor.h, the thread_struct
+
+LTHREAD_KSP = 0
+LTHREAD_USP = 4
+LTHREAD_ESP0 = 8
+LTHREAD_DCCR = 12
+
+ ;; _resume performs the actual task-switching, by switching stack pointers
+ ;; input arguments: r10 = prev, r11 = next, r12 = thread offset in task struct
+ ;; returns old current in r10
+ ;;
+ ;; TODO: see the i386 version. The switch_to which calls resume in our version
+ ;; could really be an inline asm of this.
+
+_resume:
+ push srp ; we keep the old/new PC on the stack
+ add.d r12, r10 ; r10 = current tasks tss
+ move dccr, [r10+LTHREAD_DCCR] ; save irq enable state
+ di
+
+ move usp, [r10+LTHREAD_USP] ; save user-mode stackpointer
+
+ subq 10*4, sp
+ movem r9, [sp] ; save non-scratch registers
+
+ move.d sp, [r10+LTHREAD_KSP] ; save the kernel stack pointer for the old task
+ move.d sp, r10 ; return last running task in r10
+ and.d -8192, r10 ; get task ptr from stackpointer
+ add.d r12, r11 ; find the new tasks tss
+ move.d [r11+LTHREAD_KSP], sp ; switch into the new stackframe by restoring kernel sp
+
+ movem [sp+], r9 ; restore non-scratch registers
+
+ move [r11+LTHREAD_USP], usp ; restore user-mode stackpointer
+
+ move [r11+LTHREAD_DCCR], dccr ; restore irq enable status
+ jump [sp+] ; restore PC
+
+ ;; This is the MMU bus fault handler.
+ ;; It needs to stack the CPU status and overall is different
+ ;; from the other interrupt handlers.
+
+_mmu_bus_fault:
+ sbfs [sp=sp-16] ; push the internal CPU status
+ ;; the first longword in the sbfs frame was the interrupted PC
+ ;; which fits nicely with the "IRP" slot in pt_regs normally used to
+ ;; contain the return address. used by Oops to print kernel errors..
+ push srp ; make a stackframe similar to pt_regs
+ push dccr
+ push mof
+ di
+ subq 14*4, sp
+ movem r13, [sp]
+ push r10 ; dummy orig_r10
+ moveq 1, r10
+ push r10 ; frametype == 1, BUSFAULT frame type
+
+ moveq 0, r9 ; busfault is equivalent to an irq
+
+ move.d sp, r10 ; pt_regs argument to handle_mmu_bus_fault
+
+ jsr _handle_mmu_bus_fault ; in arch/cris/mm/fault.c
+
+ ;; now we need to return through the normal path, we cannot just
+ ;; do the RBFexit since we might have killed off the running
+ ;; process due to a SEGV, scheduled due to a page blocking or
+ ;; whatever.
+
+ ba _ret_from_intr
+ nop
+
+ ;; special handlers for breakpoint and NMI
+#if 0
+_hwbreakpoint:
+ push dccr
+ di
+ push r10
+ push r11
+ push r12
+ push r13
+ clearf b
+ move brp,r11
+ move.d [_hw_bp_msg],r10
+ jsr _printk
+ setf b
+ pop r13
+ pop r12
+ pop r11
+ pop r10
+ pop dccr
+ retb
+ nop
+#else
+_hwbreakpoint:
+ push dccr
+ di
+#if 1
+ push r10
+ push r11
+ move.d [_hw_bp_trig_ptr],r10
+ move.d [r10],r11
+ cmp.d 42,r11
+ beq nobp
+ nop
+ move brp,r11
+ move.d r11,[r10+]
+ move.d r10,[_hw_bp_trig_ptr]
+nobp: pop r11
+ pop r10
+#endif
+ pop dccr
+ retb
+ nop
+#endif
+
+_IRQ1_interrupt:
+_spurious_interrupt:
+ di
+ move.b 4,r0
+ move.b r0,[0xb0000030]
+basse2: ba basse2
+ nop
+
+ ;; this handles the case when multiple interrupts arrive at the same time
+ ;; we jump to the first set interrupt bit in a priority fashion
+ ;; the hardware will call the unserved interrupts after the handler finishes
+
+_multiple_interrupt:
+ ;; this prologue MUST match the one in irq.h and the struct in ptregs.h!!!
+ push irp
+ push srp
+ push dccr
+ push mof
+ di
+ subq 14*4,sp
+ movem r13,[sp]
+ push r10 ; push orig_r10
+ clear.d [sp=sp-4] ; frametype == 0, normal frame
+
+ move.d _irq_shortcuts + 8,r1
+ moveq 2,r2 ; first bit we care about is the timer0 irq
+ move.d [0xb00000d8],r0 ; read the irq bits that triggered the multiple irq
+multloop:
+ btst r2,r0 ; check for the irq given by bit r2
+ bmi do_shortcut ; actually do the shortcut
+ nop
+ addq 1,r2 ; next vector bit - remember this is in the delay slot!
+ addq 4,r1 ; next vector
+ cmpq 26,r2
+ bne multloop ; process all irq's up to and including number 25
+ nop
+
+ ;; strange, we didn't get any set vector bits.. oh well, just return
+
+ ba Rexit
+ nop
+
+do_shortcut:
+ test.d [r1]
+ beq Rexit
+ nop
+ jump [r1] ; jump to the irq handlers shortcut
+
+
+ .data
+
+_hw_bp_trigs:
+ .space 64*4
+_hw_bp_trig_ptr:
+ .dword _hw_bp_trigs
+
+/* linux/linkage.h got it wrong for this compiler currently */
+
+#undef SYMBOL_NAME
+#define SYMBOL_NAME(X) _/**/X
+
+_sys_call_table:
+ .long SYMBOL_NAME(sys_ni_syscall) /* 0 - old "setup()" system call*/
+ .long SYMBOL_NAME(sys_exit)
+ .long SYMBOL_NAME(sys_fork)
+ .long SYMBOL_NAME(sys_read)
+ .long SYMBOL_NAME(sys_write)
+ .long SYMBOL_NAME(sys_open) /* 5 */
+ .long SYMBOL_NAME(sys_close)
+ .long SYMBOL_NAME(sys_waitpid)
+ .long SYMBOL_NAME(sys_creat)
+ .long SYMBOL_NAME(sys_link)
+ .long SYMBOL_NAME(sys_unlink) /* 10 */
+ .long SYMBOL_NAME(sys_execve)
+ .long SYMBOL_NAME(sys_chdir)
+ .long SYMBOL_NAME(sys_time)
+ .long SYMBOL_NAME(sys_mknod)
+ .long SYMBOL_NAME(sys_chmod) /* 15 */
+ .long SYMBOL_NAME(sys_lchown16)
+ .long SYMBOL_NAME(sys_ni_syscall) /* old break syscall holder */
+ .long SYMBOL_NAME(sys_stat)
+ .long SYMBOL_NAME(sys_lseek)
+ .long SYMBOL_NAME(sys_getpid) /* 20 */
+ .long SYMBOL_NAME(sys_mount)
+ .long SYMBOL_NAME(sys_oldumount)
+ .long SYMBOL_NAME(sys_setuid16)
+ .long SYMBOL_NAME(sys_getuid16)
+ .long SYMBOL_NAME(sys_stime) /* 25 */
+ .long SYMBOL_NAME(sys_ptrace)
+ .long SYMBOL_NAME(sys_alarm)
+ .long SYMBOL_NAME(sys_fstat)
+ .long SYMBOL_NAME(sys_pause)
+ .long SYMBOL_NAME(sys_utime) /* 30 */
+ .long SYMBOL_NAME(sys_ni_syscall) /* old stty syscall holder */
+ .long SYMBOL_NAME(sys_ni_syscall) /* old gtty syscall holder */
+ .long SYMBOL_NAME(sys_access)
+ .long SYMBOL_NAME(sys_nice)
+ .long SYMBOL_NAME(sys_ni_syscall) /* 35 old ftime syscall holder */
+ .long SYMBOL_NAME(sys_sync)
+ .long SYMBOL_NAME(sys_kill)
+ .long SYMBOL_NAME(sys_rename)
+ .long SYMBOL_NAME(sys_mkdir)
+ .long SYMBOL_NAME(sys_rmdir) /* 40 */
+ .long SYMBOL_NAME(sys_dup)
+ .long SYMBOL_NAME(sys_pipe)
+ .long SYMBOL_NAME(sys_times)
+ .long SYMBOL_NAME(sys_ni_syscall) /* old prof syscall holder */
+ .long SYMBOL_NAME(sys_brk) /* 45 */
+ .long SYMBOL_NAME(sys_setgid16)
+ .long SYMBOL_NAME(sys_getgid16)
+ .long SYMBOL_NAME(sys_signal)
+ .long SYMBOL_NAME(sys_geteuid16)
+ .long SYMBOL_NAME(sys_getegid16) /* 50 */
+ .long SYMBOL_NAME(sys_acct)
+ .long SYMBOL_NAME(sys_umount) /* recycled never used phys() */
+ .long SYMBOL_NAME(sys_ni_syscall) /* old lock syscall holder */
+ .long SYMBOL_NAME(sys_ioctl)
+ .long SYMBOL_NAME(sys_fcntl) /* 55 */
+ .long SYMBOL_NAME(sys_ni_syscall) /* old mpx syscall holder */
+ .long SYMBOL_NAME(sys_setpgid)
+ .long SYMBOL_NAME(sys_ni_syscall) /* old ulimit syscall holder */
+ .long SYMBOL_NAME(sys_ni_syscall) /* old sys_olduname holder */
+ .long SYMBOL_NAME(sys_umask) /* 60 */
+ .long SYMBOL_NAME(sys_chroot)
+ .long SYMBOL_NAME(sys_ustat)
+ .long SYMBOL_NAME(sys_dup2)
+ .long SYMBOL_NAME(sys_getppid)
+ .long SYMBOL_NAME(sys_getpgrp) /* 65 */
+ .long SYMBOL_NAME(sys_setsid)
+ .long SYMBOL_NAME(sys_sigaction)
+ .long SYMBOL_NAME(sys_sgetmask)
+ .long SYMBOL_NAME(sys_ssetmask)
+ .long SYMBOL_NAME(sys_setreuid16) /* 70 */
+ .long SYMBOL_NAME(sys_setregid16)
+ .long SYMBOL_NAME(sys_sigsuspend)
+ .long SYMBOL_NAME(sys_sigpending)
+ .long SYMBOL_NAME(sys_sethostname)
+ .long SYMBOL_NAME(sys_setrlimit) /* 75 */
+ .long SYMBOL_NAME(sys_old_getrlimit)
+ .long SYMBOL_NAME(sys_getrusage)
+ .long SYMBOL_NAME(sys_gettimeofday)
+ .long SYMBOL_NAME(sys_settimeofday)
+ .long SYMBOL_NAME(sys_getgroups16) /* 80 */
+ .long SYMBOL_NAME(sys_setgroups16)
+ .long SYMBOL_NAME(sys_select) /* was old_select in Linux/E100 */
+ .long SYMBOL_NAME(sys_symlink)
+ .long SYMBOL_NAME(sys_lstat)
+ .long SYMBOL_NAME(sys_readlink) /* 85 */
+ .long SYMBOL_NAME(sys_uselib)
+ .long SYMBOL_NAME(sys_swapon)
+ .long SYMBOL_NAME(sys_reboot)
+ .long SYMBOL_NAME(old_readdir)
+ .long SYMBOL_NAME(old_mmap) /* 90 */
+ .long SYMBOL_NAME(sys_munmap)
+ .long SYMBOL_NAME(sys_truncate)
+ .long SYMBOL_NAME(sys_ftruncate)
+ .long SYMBOL_NAME(sys_fchmod)
+ .long SYMBOL_NAME(sys_fchown16) /* 95 */
+ .long SYMBOL_NAME(sys_getpriority)
+ .long SYMBOL_NAME(sys_setpriority)
+ .long SYMBOL_NAME(sys_ni_syscall) /* old profil syscall holder */
+ .long SYMBOL_NAME(sys_statfs)
+ .long SYMBOL_NAME(sys_fstatfs) /* 100 */
+ .long SYMBOL_NAME(sys_ni_syscall) /* sys_ioperm in i386 */
+ .long SYMBOL_NAME(sys_socketcall)
+ .long SYMBOL_NAME(sys_syslog)
+ .long SYMBOL_NAME(sys_setitimer)
+ .long SYMBOL_NAME(sys_getitimer) /* 105 */
+ .long SYMBOL_NAME(sys_newstat)
+ .long SYMBOL_NAME(sys_newlstat)
+ .long SYMBOL_NAME(sys_newfstat)
+ .long SYMBOL_NAME(sys_ni_syscall) /* old sys_uname holder */
+ .long SYMBOL_NAME(sys_ni_syscall) /* sys_iopl in i386 */
+ .long SYMBOL_NAME(sys_vhangup)
+ .long SYMBOL_NAME(sys_ni_syscall) /* old "idle" system call */
+ .long SYMBOL_NAME(sys_ni_syscall) /* vm86old in i386 */
+ .long SYMBOL_NAME(sys_wait4)
+ .long SYMBOL_NAME(sys_swapoff) /* 115 */
+ .long SYMBOL_NAME(sys_sysinfo)
+ .long SYMBOL_NAME(sys_ipc)
+ .long SYMBOL_NAME(sys_fsync)
+ .long SYMBOL_NAME(sys_sigreturn)
+ .long SYMBOL_NAME(sys_clone) /* 120 */
+ .long SYMBOL_NAME(sys_setdomainname)
+ .long SYMBOL_NAME(sys_newuname)
+ .long SYMBOL_NAME(sys_ni_syscall) /* TODO sys_modify_ldt - do something ?*/
+ .long SYMBOL_NAME(sys_adjtimex)
+ .long SYMBOL_NAME(sys_mprotect) /* 125 */
+ .long SYMBOL_NAME(sys_sigprocmask)
+ .long SYMBOL_NAME(sys_create_module)
+ .long SYMBOL_NAME(sys_init_module)
+ .long SYMBOL_NAME(sys_delete_module)
+ .long SYMBOL_NAME(sys_get_kernel_syms) /* 130 */
+ .long SYMBOL_NAME(sys_quotactl)
+ .long SYMBOL_NAME(sys_getpgid)
+ .long SYMBOL_NAME(sys_fchdir)
+ .long SYMBOL_NAME(sys_bdflush)
+ .long SYMBOL_NAME(sys_sysfs) /* 135 */
+ .long SYMBOL_NAME(sys_personality)
+ .long SYMBOL_NAME(sys_ni_syscall) /* for afs_syscall */
+ .long SYMBOL_NAME(sys_setfsuid16)
+ .long SYMBOL_NAME(sys_setfsgid16)
+ .long SYMBOL_NAME(sys_llseek) /* 140 */
+ .long SYMBOL_NAME(sys_getdents)
+ .long SYMBOL_NAME(sys_select)
+ .long SYMBOL_NAME(sys_flock)
+ .long SYMBOL_NAME(sys_msync)
+ .long SYMBOL_NAME(sys_readv) /* 145 */
+ .long SYMBOL_NAME(sys_writev)
+ .long SYMBOL_NAME(sys_getsid)
+ .long SYMBOL_NAME(sys_fdatasync)
+ .long SYMBOL_NAME(sys_sysctl)
+ .long SYMBOL_NAME(sys_mlock) /* 150 */
+ .long SYMBOL_NAME(sys_munlock)
+ .long SYMBOL_NAME(sys_mlockall)
+ .long SYMBOL_NAME(sys_munlockall)
+ .long SYMBOL_NAME(sys_sched_setparam)
+ .long SYMBOL_NAME(sys_sched_getparam) /* 155 */
+ .long SYMBOL_NAME(sys_sched_setscheduler)
+ .long SYMBOL_NAME(sys_sched_getscheduler)
+ .long SYMBOL_NAME(sys_sched_yield)
+ .long SYMBOL_NAME(sys_sched_get_priority_max)
+ .long SYMBOL_NAME(sys_sched_get_priority_min) /* 160 */
+ .long SYMBOL_NAME(sys_sched_rr_get_interval)
+ .long SYMBOL_NAME(sys_nanosleep)
+ .long SYMBOL_NAME(sys_mremap)
+ .long SYMBOL_NAME(sys_setresuid16)
+ .long SYMBOL_NAME(sys_getresuid16) /* 165 */
+ .long SYMBOL_NAME(sys_ni_syscall) /* sys_vm86 */
+ .long SYMBOL_NAME(sys_query_module)
+ .long SYMBOL_NAME(sys_poll)
+ .long SYMBOL_NAME(sys_nfsservctl)
+ .long SYMBOL_NAME(sys_setresgid16) /* 170 */
+ .long SYMBOL_NAME(sys_getresgid16)
+ .long SYMBOL_NAME(sys_prctl)
+ .long SYMBOL_NAME(sys_rt_sigreturn)
+ .long SYMBOL_NAME(sys_rt_sigaction)
+ .long SYMBOL_NAME(sys_rt_sigprocmask) /* 175 */
+ .long SYMBOL_NAME(sys_rt_sigpending)
+ .long SYMBOL_NAME(sys_rt_sigtimedwait)
+ .long SYMBOL_NAME(sys_rt_sigqueueinfo)
+ .long SYMBOL_NAME(sys_rt_sigsuspend)
+ .long SYMBOL_NAME(sys_pread) /* 180 */
+ .long SYMBOL_NAME(sys_pwrite)
+ .long SYMBOL_NAME(sys_chown16)
+ .long SYMBOL_NAME(sys_getcwd)
+ .long SYMBOL_NAME(sys_capget)
+ .long SYMBOL_NAME(sys_capset) /* 185 */
+ .long SYMBOL_NAME(sys_sigaltstack)
+ .long SYMBOL_NAME(sys_sendfile)
+ .long SYMBOL_NAME(sys_ni_syscall) /* streams1 */
+ .long SYMBOL_NAME(sys_ni_syscall) /* streams2 */
+ .long SYMBOL_NAME(sys_vfork) /* 190 */
+ .long SYMBOL_NAME(sys_getrlimit)
+ .long SYMBOL_NAME(sys_mmap2)
+ .long SYMBOL_NAME(sys_truncate64)
+ .long SYMBOL_NAME(sys_ftruncate64)
+ .long SYMBOL_NAME(sys_stat64) /* 195 */
+ .long SYMBOL_NAME(sys_lstat64)
+ .long SYMBOL_NAME(sys_fstat64)
+ .long SYMBOL_NAME(sys_lchown)
+ .long SYMBOL_NAME(sys_getuid)
+ .long SYMBOL_NAME(sys_getgid) /* 200 */
+ .long SYMBOL_NAME(sys_geteuid)
+ .long SYMBOL_NAME(sys_getegid)
+ .long SYMBOL_NAME(sys_setreuid)
+ .long SYMBOL_NAME(sys_setregid)
+ .long SYMBOL_NAME(sys_getgroups) /* 205 */
+ .long SYMBOL_NAME(sys_setgroups)
+ .long SYMBOL_NAME(sys_fchown)
+ .long SYMBOL_NAME(sys_setresuid)
+ .long SYMBOL_NAME(sys_getresuid)
+ .long SYMBOL_NAME(sys_setresgid) /* 210 */
+ .long SYMBOL_NAME(sys_getresgid)
+ .long SYMBOL_NAME(sys_chown)
+ .long SYMBOL_NAME(sys_setuid)
+ .long SYMBOL_NAME(sys_setgid)
+ .long SYMBOL_NAME(sys_setfsuid) /* 215 */
+ .long SYMBOL_NAME(sys_setfsgid)
+ .long SYMBOL_NAME(sys_pivot_root)
+ .long SYMBOL_NAME(sys_mincore)
+ .long SYMBOL_NAME(sys_madvise)
+ .long SYMBOL_NAME(sys_getdents64) /* 220 */
+
+ /*
+ * NOTE!! This doesn't have to be exact - we just have
+ * to make sure we have _enough_ of the "sys_ni_syscall"
+ * entries. Don't panic if you notice that this hasn't
+ * been shrunk every time we add a new system call.
+ */
+
+ ;; TODO: this needs to actually generate sys_ni_syscall entires
+ ;; since we now have removed the check for NULL entries in this
+ ;; table in system_call!
+
+ .space (NR_syscalls-220)*4
+
diff --git a/arch/cris/kernel/head.S b/arch/cris/kernel/head.S
new file mode 100644
index 000000000..b436aa843
--- /dev/null
+++ b/arch/cris/kernel/head.S
@@ -0,0 +1,519 @@
+ ;; $Id: head.S,v 1.11 2001/01/16 16:31:38 bjornw Exp $
+ ;;
+ ;; Head of the kernel - alter with care
+ ;;
+ ;; Copyright (C) 2000, 2001 Axis Communications AB
+ ;;
+ ;; Authors: Bjorn Wesen (bjornw@axis.com)
+ ;;
+ ;; $Log: head.S,v $
+ ;; Revision 1.11 2001/01/16 16:31:38 bjornw
+ ;; * Changed name and semantics of running_from_flash to romfs_in_flash,
+ ;; set by head.S to indicate to setup.c whether there is a cramfs image
+ ;; after the kernels BSS or not. Should work for all three boot-cases
+ ;; (DRAM with cramfs in DRAM, DRAM with cramfs in flash (compressed boot),
+ ;; and flash with cramfs in flash)
+ ;;
+ ;; Revision 1.10 2001/01/16 14:12:21 bjornw
+ ;; * Check for cramfs start passed in r9 from the decompressor, if all other
+ ;; cramfs options fail (if we boot from DRAM but don't find a cramfs image
+ ;; after the kernel in DRAM, it is probably still in the flash)
+ ;; * Check magic in cramfs detection when booting from flash directly
+ ;;
+ ;; Revision 1.9 2001/01/15 17:17:02 bjornw
+ ;; * Corrected the code that detects the cramfs lengths
+ ;; * Added a comment saying that the above does not work due to other
+ ;; reasons..
+ ;;
+ ;; Revision 1.8 2001/01/15 16:27:51 jonashg
+ ;; Made boot after flashing work.
+ ;; * end destination is __vmlinux_end in RAM.
+ ;; * _romfs_start moved because of virtual memory.
+ ;;
+ ;; Revision 1.7 2000/11/21 13:55:29 bjornw
+ ;; Use CONFIG_CRIS_LOW_MAP for the low VM map instead of explicit CPU type
+ ;;
+ ;; Revision 1.6 2000/10/06 12:36:55 bjornw
+ ;; Forgot swapper_pg_dir when changing memory map..
+ ;;
+ ;; Revision 1.5 2000/10/04 16:49:30 bjornw
+ ;; * Fixed memory mapping in LX
+ ;; * Check for cramfs instead of romfs
+ ;;
+ ;;
+
+#include <linux/config.h>
+#define ASSEMBLER_MACROS_ONLY
+#include <asm/sv_addr_ag.h>
+
+#define CRAMFS_MAGIC 0x28cd3d45
+
+ ;; exported symbols
+
+ .globl _etrax_irv
+ .globl _romfs_start
+ .globl _romfs_length
+ .globl _romfs_in_flash
+ .globl _swapper_pg_dir
+
+ .text
+
+ ;; This is the entry point of the kernel. We are in supervisor mode.
+ ;; 0x00000000 if Flash, 0x40004000 if DRAM
+ ;; since etrax actually starts at address 2 when booting from flash, we
+ ;; put a nop (2 bytes) here first so we dont accidentally skip the di
+ ;;
+ ;; NOTICE! The register r9 is used as a parameter carrying register from
+ ;; the decompressor (if the kernel was compressed). It should not be
+ ;; used in the code below until it is read.
+
+ nop
+ di
+
+ ;; First setup the kseg_c mapping from where the kernel is linked
+ ;; to 0x40000000 (where the actual DRAM resides) otherwise
+ ;; we cannot do very much! See arch/cris/README.mm
+ ;;
+ ;; Notice that since we're potentially running at 0x00 or 0x40 right now,
+ ;; we will get a fault as soon as we enable the MMU if we dont
+ ;; temporarily map those segments linearily.
+ ;;
+ ;; Due to a bug in Etrax-100 LX version 1 we need to map the memory
+ ;; slightly different. We also let the simulator get this mapping for now.
+
+#ifdef CONFIG_CRIS_LOW_MAP
+ move.d 0x0800b000, r0 ; kseg mappings
+ move.d r0, [R_MMU_KBASE_HI]
+
+ move.d 0x04040000, r0 ; temporary map of 0x40->0x40 and 0x00->0x00
+ move.d r0, [R_MMU_KBASE_LO]
+
+ move.d 0x80074871, r0 ; mmu enable, segs e,b,6,5,4,0 segment mapped
+ move.d r0, [R_MMU_CONFIG]
+#else
+ move.d 0x0804b000, r0 ; kseg mappings
+ move.d r0, [R_MMU_KBASE_HI]
+
+ move.d 0x00040000, r0 ; temporary map of 0x40->0x40 and 0x00->0x00
+ move.d r0, [R_MMU_KBASE_LO]
+
+ move.d 0x8007d811, r0 ; mmu enable, segs f,e,c,b,4,0 segment mapped
+ move.d r0, [R_MMU_CONFIG]
+#endif
+
+ ;; Now we need to sort out the segments and their locations in RAM or
+ ;; Flash. The image in the Flash (or in DRAM) consists of 3 pieces:
+ ;; 1) kernel text, 2) kernel data, 3) ROM filesystem image
+ ;; But the linker has linked the kernel to expect this layout in
+ ;; DRAM memory:
+ ;; 1) kernel text, 2) kernel data, 3) kernel BSS
+ ;; (the location of the ROM filesystem is determined by the krom driver)
+ ;; If we boot this from Flash, we want to keep the ROM filesystem in
+ ;; the flash, we want to copy the text and need to copy the data to DRAM.
+ ;; But if we boot from DRAM, we need to move the ROMFS image
+ ;; from its position after kernel data, to after kernel BSS, BEFORE the
+ ;; kernel starts using the BSS area (since its "overlayed" with the ROMFS)
+ ;;
+ ;; In both cases, we start in un-cached mode, and need to jump into a
+ ;; cached PC after we're done fiddling around with the segments.
+ ;;
+ ;; arch/etrax100/etrax100.ld sets some symbols that define the start
+ ;; and end of each segment.
+
+ ;; Check if we start from DRAM or FLASH by testing PC
+
+ move.d pc,r0
+ and.d 0x7fffffff,r0 ; get rid of the non-cache bit
+ cmp.d 0x10000,r0 ; arbitrary... just something above this code
+ bcs inflash
+ nop
+
+ jump inram ; enter cached ram
+
+inflash:
+
+#ifndef CONFIG_SVINTO_SIM
+
+ ;; We need to setup the bus registers before we start using the DRAM
+
+ move.d DEF_R_WAITSTATES, r0
+ move.d r0, [R_WAITSTATES]
+
+ move.d DEF_R_BUS_CONFIG, r0
+ move.d r0, [R_BUS_CONFIG]
+
+ move.d DEF_R_DRAM_CONFIG, r0
+ move.d r0, [R_DRAM_CONFIG]
+
+ move.d DEF_R_DRAM_TIMING, r0
+ move.d r0, [R_DRAM_TIMING]
+
+#endif
+ ;; Copy text+data to DRAM
+ ;; This is fragile - the calculation of r4 as the image size depends
+ ;; on that the labels below actually are the first and last positions
+ ;; in the linker-script.
+ ;;
+ ;; Then the locating of the cramfs image depends on the aforementioned
+ ;; image being located in the flash at 0. This is most often not true,
+ ;; thus the following does not work (normally there is a rescue-block
+ ;; between the physical start of the flash and the flash-image start,
+ ;; and when run with compression, the kernel is actually unpacked to
+ ;; DRAM and we never get here in the first place :))
+
+ moveq 0, r0 ; source
+ move.d _text_start, r1 ; destination
+ move.d __vmlinux_end, r2 ; end destination
+ move.d r2, r4
+ sub.d r1, r4 ; r4=__vmlinux_end in flash, used below
+1: move.w [r0+], r3
+ move.w r3, [r1+]
+ cmp.d r2, r1
+ bcs 1b
+ nop
+
+ ;; We keep the cramfs in the flash.
+ ;; There might be none, but that does not matter because
+ ;; we don't do anything than read some bytes here.
+
+ moveq 0, r0
+ move.d r0, [_romfs_length] ; default if there is no cramfs
+
+ move.d [r4], r0 ; cramfs_super.magic
+ cmp.d CRAMFS_MAGIC, r0
+ bne 1f
+ nop
+ move.d [r4 + 4], r0 ; cramfs_super.size
+ move.d r0, [_romfs_length]
+#ifdef CONFIG_CRIS_LOW_MAP
+ add.d 0x50000000, r4 ; add flash start in virtual memory (cached)
+#else
+ add.d 0xf0000000, r4 ; add flash start in virtual memory (cached)
+#endif
+ move.d r4, [_romfs_start]
+1:
+ moveq 1, r0
+ move.d r0, [_romfs_in_flash]
+
+ jump start_it ; enter code, cached this time
+
+inram:
+ ;; Move the ROM fs to after BSS end. This assumes that the cramfs
+ ;; second longword contains the length of the cramfs
+
+ moveq 0, r0
+ move.d r0, [_romfs_length] ; default if there is no cramfs
+
+ ;; First check if there is a cramfs (magic value)
+ ;; Notice that we check for cramfs magic value - which is
+ ;; the "rom fs" we'll possibly use in 2.4 if not JFFS (which does
+ ;; not need this mechanism anyway)
+
+ move.d __vmlinux_end, r0 ; the image will be after the vmlinux end address
+ move.d [r0], r1 ; cramfs assumes same endian on host/target
+ cmp.d CRAMFS_MAGIC, r1; magic value in cramfs superblock
+ bne no_romfs_in_ram
+ nop
+
+ ;; Ok. What is its size ?
+
+ move.d [r0 + 4], r2 ; cramfs_super.size (again, no need to swapwb)
+
+ ;; We want to copy it to the end of the BSS
+
+ move.d _end, r1
+
+ ;; Remember values so cramfs and setup can find this info
+
+ move.d r1, [_romfs_start] ; new romfs location
+ move.d r2, [_romfs_length]
+
+ ;; We need to copy it backwards, since they can be overlapping
+
+ add.d r2, r0
+ add.d r2, r1
+
+ ;; Go ahead. Make my loop.
+
+ lsrq 1, r2 ; size is in bytes, we copy words
+
+1: move.w [r0=r0-2],r3
+ move.w r3,[r1=r1-2]
+ subq 1, r2
+ bne 1b
+ nop
+
+ ;; Dont worry that the BSS is tainted. It will be cleared later.
+
+ moveq 0, r0
+ move.d r0, [_romfs_in_flash]
+
+ jump start_it ; better skip the additional cramfs check below
+
+no_romfs_in_ram:
+
+ ;; We have still one other possibility at this point - the kernel
+ ;; could have been unpacked to DRAM by the loader, but the cramfs
+ ;; image was still in the Flash directly after the compressed kernel
+ ;; image. The loader passes the address of the byte succeeding the
+ ;; last compressed byte in the flash in the register r9 when starting
+ ;; the kernel. Check if r9 points to a decent cramfs image!
+ ;; (Notice that if this is not booted from the loader, r9 will be
+ ;; garbage but we do sanity checks on it, the chance that it points
+ ;; to a cramfs magic is small.. )
+
+ cmp.d 0x0ffffff8, r9
+ bcc 1f ; r9 points outside the flash area
+ nop
+ move.d [r9], r0 ; cramfs_super.magic
+ cmp.d CRAMFS_MAGIC, r0
+ bne 1f
+ nop
+ move.d [r9+4], r0 ; cramfs_super.length
+ move.d r0, [_romfs_length]
+#ifdef CONFIG_CRIS_LOW_MAP
+ add.d 0x50000000, r9 ; add flash start in virtual memory (cached)
+#else
+ add.d 0xf0000000, r9 ; add flash start in virtual memory (cached)
+#endif
+ move.d r9, [_romfs_start]
+
+ moveq 1, r0
+ move.d r0, [_romfs_in_flash]
+1:
+
+ jump start_it ; enter code, cached this time
+
+start_it:
+ ;; the kernel stack is overlayed with the task structure for each
+ ;; task. thus the initial kernel stack is in the same page as the
+ ;; init_task (but starts in the top of the page, size 8192)
+ move.d _init_task_union + 8192,sp
+ move.d _ibr_start,r0 ; this symbol is set by the linker script
+ move r0,ibr
+ move.d r0,[_etrax_irv] ; set the interrupt base register and pointer
+
+ ;; Clear BSS region, from _bss_start to _end
+
+ move.d __bss_start, r0
+ move.d _end, r1
+1: clear.d [r0+]
+ cmp.d r1, r0
+ bcs 1b
+ nop
+
+#ifdef CONFIG_BLK_DEV_ETRAXIDE
+ ;; disable ATA before enabling it in genconfig below
+ moveq 0,r0
+ move.d r0,[R_ATA_CTRL_DATA]
+ move.d r0,[R_ATA_TRANSFER_CNT]
+ move.d r0,[R_ATA_CONFIG]
+#if 0
+ move.d R_PORT_G_DATA,r1
+ move.d r0,[r1]; assert ATA bus-reset
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ move.d 0x08000000,r0
+ move.d r0,[r1]
+#endif
+#endif
+
+#ifdef CONFIG_JULIETTE
+ ;; configure external DMA channel 0 before enabling it in genconfig
+
+ moveq 0,r0
+ move.d r0,[R_EXT_DMA_0_ADDR]
+ move.d 0x860000,r0 ; cnt enable, word size, output, stop, size 0
+ move.d r0,[R_EXT_DMA_0_CMD]
+
+ ;; reset dma4 and wait for completion
+
+ moveq 4,r0
+ move.b r0,[R_DMA_CH4_CMD]
+w4u: move.b [R_DMA_CH4_CMD],r0
+ and.b 7,r0
+ cmp.b 4,r0
+ beq w4u
+ nop
+
+ ;; reset dma5 and wait for completion
+
+ moveq 4,r0
+ move.b r0,[R_DMA_CH5_CMD]
+w5u: move.b [R_DMA_CH5_CMD],r0
+ and.b 7,r0
+ cmp.b 4,r0
+ beq w5u
+ nop
+#endif
+
+ ;; Etrax product HW genconfig setup
+
+ moveq 0,r0
+#if !defined(CONFIG_KGDB) && !defined(CONFIG_DMA_MEMCPY)
+ or.d 0x140000,r0 ; DMA channels 6 and 7 to ser0, kgdb doesnt want DMA
+#endif
+#if !defined(CONFIG_KGDB) || !defined(CONFIG_DEBUG_PORT1)
+ or.d 0xc00000,r0 ; DMA channels 8 and 9 to ser1, kgdb doesnt want DMA
+#endif
+#ifdef CONFIG_DMA_MEMCPY
+ or.d 0x003c0000,r0 ; 6/7 memory-memory DMA
+#endif
+#ifdef CONFIG_ETRAX100_SERIAL_PORT2
+ or.d 0x2808,r0 ; DMA channels 2 and 3 to serport 2, port 2 enabled
+#endif
+#ifdef CONFIG_ETRAX100_SERIAL_PORT3
+ or.d 0x28100,r0 ; DMA channels 4 and 5 to serport 3, port 3 enabled
+#endif
+#if defined(CONFIG_ETRAX100_PARALLEL_PORT0) || defined(CONFIG_ETRAX_ETHERNET_LPSLAVE)
+ or.w 0x4,r0 ; parport 0 enabled using DMA 2/3
+#endif
+#if defined(CONFIG_ETRAX100_PARALLEL_PORT1) || defined(CONFIG_ETRAX_ETHERNET_LPSLAVE)
+ or.w 0x80,r0 ; parport 1 enabled using DMA 4/5
+#endif
+#ifdef CONFIG_BLK_DEV_ETRAXIDE
+ or.d 0x3c02,r0 ; DMA channels 2 and 3 to ATA, ATA enabled
+#endif
+#ifdef CONFIG_JULIETTE
+ or.d 0x3c000,r0 ; DMA channels 4 and 5 to EXTDMA0, for Juliette
+#ifndef CONFIG_BLK_DEV_ETRAXIDE
+ or.d 0x41,r0 ; HACK for now! To make G27 connected for the RTC
+#endif
+#endif
+ move.d r0,[_genconfig_shadow] ; init a shadow register of R_GEN_CONFIG
+
+#ifndef CONFIG_SVINTO_SIM
+ move.d r0,[R_GEN_CONFIG]
+
+#if 0
+ moveq 4,r0
+ move.b r0,[R_DMA_CH6_CMD] ; reset (ser0 dma out)
+ move.b r0,[R_DMA_CH7_CMD] ; reset (ser0 dma in)
+w61: move.b [R_DMA_CH6_CMD],r0 ; wait for reset cycle to finish
+ and.b 7,r0
+ cmp.b 4,r0
+ beq w61
+ nop
+w71: move.b [R_DMA_CH7_CMD],r0 ; wait for reset cycle to finish
+ and.b 7,r0
+ cmp.b 4,r0
+ beq w71
+ nop
+#endif
+
+ moveq 4,r0
+ move.b r0,[R_DMA_CH8_CMD] ; reset (ser1 dma out)
+ move.b r0,[R_DMA_CH9_CMD] ; reset (ser1 dma in)
+w81: move.b [R_DMA_CH8_CMD],r0 ; wait for reset cycle to finish
+ and.b 7,r0
+ cmp.b 4,r0
+ beq w81
+ nop
+w91: move.b [R_DMA_CH9_CMD],r0 ; wait for reset cycle to finish
+ and.b 7,r0
+ cmp.b 4,r0
+ beq w91
+ nop
+
+ ;; setup port PA and PB default initial directions and data
+ ;; including their shadow registers
+
+ move.b DEF_R_PORT_PA_DIR,r0
+ move.b r0,[_port_pa_dir_shadow]
+ move.b r0,[R_PORT_PA_DIR]
+ move.b DEF_R_PORT_PA_DATA,r0
+ move.b r0,[_port_pa_data_shadow]
+ move.b r0,[R_PORT_PA_DATA]
+
+ move.b DEF_R_PORT_PB_CONFIG,r0
+ move.b r0,[_port_pb_config_shadow]
+ move.b r0,[R_PORT_PB_CONFIG]
+ move.b DEF_R_PORT_PB_DIR,r0
+ move.b r0,[_port_pb_dir_shadow]
+ move.b r0,[R_PORT_PB_DIR]
+ move.b DEF_R_PORT_PB_DATA,r0
+ move.b r0,[_port_pb_data_shadow]
+ move.b r0,[R_PORT_PB_DATA]
+
+ moveq 0,r0
+ move.d r0,[_port_g_data_shadow]
+ move.d r0,[R_PORT_G_DATA]
+
+ ;; setup the serial port 0 at 115200 baud for debug purposes
+
+ moveq 0,r0
+ move.d r0,[R_SERIAL0_XOFF]
+
+ move.b 0x99,r0
+ move.b r0,[R_SERIAL0_BAUD] ; 115.2kbaud for both transmit and receive
+
+ move.b 0x40,r0 ; rec enable
+ move.b r0,[R_SERIAL0_REC_CTRL]
+
+ move.b 0x40,r0 ; tr enable
+ move.b r0,[R_SERIAL0_TR_CTRL]
+
+ ;; setup the serial port 1 at 115200 baud for debug purposes
+
+ moveq 0,r0
+ move.d r0,[R_SERIAL1_XOFF]
+
+ move.b 0x99,r0
+ move.b r0,[R_SERIAL1_BAUD] ; 115.2kbaud for both transmit and receive
+
+ move.b 0x40,r0 ; rec enable
+ move.b r0,[R_SERIAL1_REC_CTRL]
+
+ move.b 0x40,r0 ; tr enable
+ move.b r0,[R_SERIAL1_TR_CTRL]
+
+#ifdef CONFIG_ETRAX_90000000_LEDS
+ ;; clear LED's on Stallone and Olga boards
+ moveq -1,r0
+ move.d r0,[_port_90000000_shadow]
+ move.d r0,[0x90000000]
+#endif
+
+#ifdef CONFIG_ETRAX100_SERIAL_PORT3
+ ;; setup the serial port 3 at 115200 baud for debug purposes
+
+ moveq 0,r0
+ move.d r0,[R_SERIAL3_XOFF]
+
+ move.b 0x99,r0
+ move.b r0,[R_SERIAL3_BAUD] ; 115.2kbaud for both transmit and receive
+
+ move.b 0x40,r0 ; rec enable
+ move.b r0,[R_SERIAL3_REC_CTRL]
+
+ move.b 0x40,r0 ; tr enable
+ move.b r0,[R_SERIAL3_TR_CTRL]
+#endif
+
+#endif /* CONFIG_SVINTO_SIM */
+
+ jump _start_kernel ; jump into the C-function _start_kernel in init/main.c
+
+
+ .data
+_etrax_irv:
+ .dword 0
+_romfs_start:
+ .dword 0
+_romfs_length:
+ .dword 0
+_romfs_in_flash:
+ .dword 0
+
+ ;; put some special pages at the beginning of the kernel aligned
+ ;; to page boundaries - the kernel cannot start until after this
+
+#ifdef CONFIG_CRIS_LOW_MAP
+_swapper_pg_dir = 0x60002000
+#else
+_swapper_pg_dir = 0xc0002000
+#endif
diff --git a/arch/cris/kernel/hexify.c b/arch/cris/kernel/hexify.c
new file mode 100644
index 000000000..daa331fec
--- /dev/null
+++ b/arch/cris/kernel/hexify.c
@@ -0,0 +1,31 @@
+#include <stdio.h>
+
+
+void main()
+{
+ int c;
+ int comma=0;
+ int count=0;
+ while((c=getchar())!=EOF)
+ {
+ unsigned char x=c;
+ if(comma)
+ printf(",");
+ else
+ comma=1;
+ if(count==8)
+ {
+ count=0;
+ printf("\n");
+ }
+ if(count==0)
+ printf("\t");
+ printf("0x%02X",c);
+ count++;
+ }
+ if(count)
+ printf("\n");
+ exit(0);
+}
+
+
diff --git a/arch/cris/kernel/irq.c b/arch/cris/kernel/irq.c
new file mode 100644
index 000000000..e55f94d20
--- /dev/null
+++ b/arch/cris/kernel/irq.c
@@ -0,0 +1,467 @@
+/* $Id: irq.c,v 1.5 2000/08/17 15:35:15 bjornw Exp $
+ *
+ * linux/arch/cris/kernel/irq.c
+ *
+ * Copyright (c) 2000 Axis Communications AB
+ *
+ * Authors: Bjorn Wesen (bjornw@axis.com)
+ *
+ * This file contains the code used by various IRQ handling routines:
+ * asking for different IRQ's should be done through these routines
+ * instead of just grabbing them. Thus setups with different IRQ numbers
+ * shouldn't result in any weird surprises, and installing new handlers
+ * should be easier.
+ *
+ * Notice Linux/CRIS: these routines do not care about SMP
+ *
+ */
+
+/*
+ * IRQ's are in fact implemented a bit like signal handlers for the kernel.
+ * Naturally it's not a 1:1 relation, but there are similarities.
+ */
+
+#include <linux/config.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/kernel_stat.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/malloc.h>
+#include <linux/random.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/bitops.h>
+
+#include <asm/svinto.h>
+
+char *hw_bp_msg = "BP 0x%x\n";
+
+static inline void
+mask_irq(unsigned int irq_nr)
+{
+ *R_VECT_MASK_CLR = 1 << irq_nr;
+}
+
+static inline void
+unmask_irq(unsigned int irq_nr)
+{
+ *R_VECT_MASK_SET = 1 << irq_nr;
+}
+
+void
+disable_irq(unsigned int irq_nr)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ mask_irq(irq_nr);
+ restore_flags(flags);
+}
+
+void
+enable_irq(unsigned int irq_nr)
+{
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ unmask_irq(irq_nr);
+ restore_flags(flags);
+}
+
+unsigned long
+probe_irq_on()
+{
+ return 0;
+}
+
+int
+probe_irq_off(unsigned long x)
+{
+ return 0;
+}
+
+irqvectptr irq_shortcuts[NR_IRQS]; /* vector of shortcut jumps after the irq prologue */
+
+/* don't use set_int_vector, it bypasses the linux interrupt handlers. it is
+ * global just so that the kernel gdb can use it.
+ */
+
+void
+set_int_vector(int n, irqvectptr addr, irqvectptr saddr)
+{
+ /* remember the shortcut entry point, after the prologue */
+
+ irq_shortcuts[n] = saddr;
+
+ etrax_irv->v[n + 0x20] = (irqvectptr)addr;
+}
+
+/* the breakpoint vector is obviously not made just like the normal irq handlers
+ * but needs to contain _code_ to jump to addr.
+ *
+ * the BREAK n instruction jumps to IBR + n * 8
+ */
+
+void
+set_break_vector(int n, irqvectptr addr)
+{
+ unsigned short *jinstr = (unsigned short *)&etrax_irv->v[n*2];
+ unsigned long *jaddr = (unsigned long *)(jinstr + 1);
+
+ /* if you don't know what this does, do not touch it! */
+
+ *jinstr = 0x0d3f;
+ *jaddr = (unsigned long)addr;
+
+ /* 00000026 <clrlop+1a> 3f0d82000000 jump 0x82 */
+}
+
+
+/*
+ * This builds up the IRQ handler stubs using some ugly macros in irq.h
+ *
+ * These macros create the low-level assembly IRQ routines that do all
+ * the operations that are needed. They are also written to be fast - and to
+ * disable interrupts as little as humanly possible.
+ *
+ */
+
+/* IRQ0 and 1 are special traps */
+void hwbreakpoint(void);
+void IRQ1_interrupt(void);
+BUILD_IRQ(2, 0x04) /* the timer interrupt */
+BUILD_IRQ(3, 0x08)
+BUILD_IRQ(4, 0x10)
+BUILD_IRQ(5, 0x20)
+BUILD_IRQ(6, 0x40)
+BUILD_IRQ(7, 0x80)
+BUILD_IRQ(8, 0x100)
+BUILD_IRQ(9, 0x200)
+BUILD_IRQ(10, 0x400)
+BUILD_IRQ(11, 0x800)
+BUILD_IRQ(12, 0x1000)
+BUILD_IRQ(13, 0x2000)
+void mmu_bus_fault(void); /* IRQ 14 is the bus fault interrupt */
+void multiple_interrupt(void); /* IRQ 15 is the multiple IRQ interrupt */
+BUILD_IRQ(16, 0x10000)
+BUILD_IRQ(17, 0x20000)
+BUILD_IRQ(18, 0x40000)
+BUILD_IRQ(19, 0x80000)
+BUILD_IRQ(20, 0x100000)
+BUILD_IRQ(21, 0x200000)
+BUILD_IRQ(22, 0x400000)
+BUILD_IRQ(23, 0x800000)
+BUILD_IRQ(24, 0x1000000)
+BUILD_IRQ(25, 0x2000000)
+
+/*
+ * Pointers to the low-level handlers
+ */
+
+static void (*interrupt[NR_IRQS])(void) = {
+ NULL, NULL, IRQ2_interrupt, IRQ3_interrupt,
+ IRQ4_interrupt, IRQ5_interrupt, IRQ6_interrupt, IRQ7_interrupt,
+ IRQ8_interrupt, IRQ9_interrupt, IRQ10_interrupt, IRQ11_interrupt,
+ IRQ12_interrupt, IRQ13_interrupt, NULL, NULL,
+ IRQ16_interrupt, IRQ17_interrupt, IRQ18_interrupt, IRQ19_interrupt,
+ IRQ20_interrupt, IRQ21_interrupt, IRQ22_interrupt, IRQ23_interrupt,
+ IRQ24_interrupt, IRQ25_interrupt
+};
+
+static void (*sinterrupt[NR_IRQS])(void) = {
+ NULL, NULL, sIRQ2_interrupt, sIRQ3_interrupt,
+ sIRQ4_interrupt, sIRQ5_interrupt, sIRQ6_interrupt, sIRQ7_interrupt,
+ sIRQ8_interrupt, sIRQ9_interrupt, sIRQ10_interrupt, sIRQ11_interrupt,
+ sIRQ12_interrupt, sIRQ13_interrupt, NULL, NULL,
+ sIRQ16_interrupt, sIRQ17_interrupt, sIRQ18_interrupt, sIRQ19_interrupt,
+ sIRQ20_interrupt, sIRQ21_interrupt, sIRQ22_interrupt, sIRQ23_interrupt,
+ sIRQ24_interrupt, sIRQ25_interrupt
+};
+
+static void (*bad_interrupt[NR_IRQS])(void) = {
+ NULL, NULL,
+ NULL, bad_IRQ3_interrupt,
+ bad_IRQ4_interrupt, bad_IRQ5_interrupt,
+ bad_IRQ6_interrupt, bad_IRQ7_interrupt,
+ bad_IRQ8_interrupt, bad_IRQ9_interrupt,
+ bad_IRQ10_interrupt, bad_IRQ11_interrupt,
+ bad_IRQ12_interrupt, bad_IRQ13_interrupt,
+ NULL, NULL,
+ bad_IRQ16_interrupt, bad_IRQ17_interrupt,
+ bad_IRQ18_interrupt, bad_IRQ19_interrupt,
+ bad_IRQ20_interrupt, bad_IRQ21_interrupt,
+ bad_IRQ22_interrupt, bad_IRQ23_interrupt,
+ bad_IRQ24_interrupt, bad_IRQ25_interrupt
+};
+
+/*
+ * Initial irq handlers.
+ */
+
+static struct irqaction *irq_action[NR_IRQS] = {
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL
+};
+
+int get_irq_list(char *buf)
+{
+ int i, len = 0;
+ struct irqaction * action;
+
+ for (i = 0; i < NR_IRQS; i++) {
+ action = irq_action[i];
+ if (!action)
+ continue;
+ len += sprintf(buf+len, "%2d: %10u %c %s",
+ i, kstat.irqs[0][i],
+ (action->flags & SA_INTERRUPT) ? '+' : ' ',
+ action->name);
+ for (action = action->next; action; action = action->next) {
+ len += sprintf(buf+len, ",%s %s",
+ (action->flags & SA_INTERRUPT) ? " +" : "",
+ action->name);
+ }
+ len += sprintf(buf+len, "\n");
+ }
+ return len;
+}
+
+/* called by the assembler IRQ entry functions defined in irq.h
+ * to dispatch the interrupts to registred handlers
+ * interrupts are disabled upon entry - depending on if the
+ * interrupt was registred with SA_INTERRUPT or not, interrupts
+ * are re-enabled or not.
+ */
+
+asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
+{
+ struct irqaction *action;
+ int do_random, cpu;
+
+ cpu = smp_processor_id();
+ irq_enter(cpu);
+ kstat.irqs[cpu][irq]++;
+
+ action = *(irq + irq_action);
+ if (action) {
+ if (!(action->flags & SA_INTERRUPT))
+ __sti();
+ action = *(irq + irq_action);
+ do_random = 0;
+ do {
+ do_random |= action->flags;
+ action->handler(irq, action->dev_id, regs);
+ action = action->next;
+ } while (action);
+ if (do_random & SA_SAMPLE_RANDOM)
+ add_interrupt_randomness(irq);
+ __cli();
+ }
+ irq_exit(cpu);
+
+ if (softirq_active(cpu) & softirq_mask(cpu))
+ do_softirq();
+
+ /* unmasking and bottom half handling is done magically for us. */
+}
+
+/* this function links in a handler into the chain of handlers for the
+ given irq, and if the irq has never been registred, the appropriate
+ handler is entered into the interrupt vector
+*/
+
+int setup_etrax_irq(int irq, struct irqaction * new)
+{
+ int shared = 0;
+ struct irqaction *old, **p;
+ unsigned long flags;
+
+ p = irq_action + irq;
+ if ((old = *p) != NULL) {
+ /* Can't share interrupts unless both agree to */
+ if (!(old->flags & new->flags & SA_SHIRQ))
+ return -EBUSY;
+
+ /* Can't share interrupts unless both are same type */
+ if ((old->flags ^ new->flags) & SA_INTERRUPT)
+ return -EBUSY;
+
+ /* add new interrupt at end of irq queue */
+ do {
+ p = &old->next;
+ old = *p;
+ } while (old);
+ shared = 1;
+ }
+
+ if (new->flags & SA_SAMPLE_RANDOM)
+ rand_initialize_irq(irq);
+
+ save_flags(flags);
+ cli();
+ *p = new;
+
+ if (!shared) {
+ /* if the irq wasn't registred before, enter it into the vector table
+ and unmask it physically
+ */
+ set_int_vector(irq, interrupt[irq], sinterrupt[irq]);
+ unmask_irq(irq);
+ }
+
+ restore_flags(flags);
+ return 0;
+}
+
+/* this function is called by a driver to register an irq handler
+ Valid flags:
+ SA_INTERRUPT -> it's a fast interrupt, handler called with irq disabled and
+ no signal checking etc is performed upon exit
+ SA_SHIRQ -> the interrupt can be shared between different handlers, the handler
+ is required to check if the irq was "aimed" at it explicitely
+ SA_RANDOM -> the interrupt will add to the random generators entropy
+*/
+
+int request_irq(unsigned int irq,
+ void (*handler)(int, void *, struct pt_regs *),
+ unsigned long irqflags,
+ const char * devname,
+ void *dev_id)
+{
+ int retval;
+ struct irqaction * action;
+
+ /* interrupts 0 and 1 are hardware breakpoint and NMI and we can't support
+ these yet. interrupt 15 is the multiple irq, it's special. */
+
+ if(irq < 2 || irq == 15 || irq >= NR_IRQS)
+ return -EINVAL;
+
+ if(!handler)
+ return -EINVAL;
+
+ /* allocate and fill in a handler structure and setup the irq */
+
+ action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);
+ if (!action)
+ return -ENOMEM;
+
+ action->handler = handler;
+ action->flags = irqflags;
+ action->mask = 0;
+ action->name = devname;
+ action->next = NULL;
+ action->dev_id = dev_id;
+
+ retval = setup_etrax_irq(irq, action);
+
+ if (retval)
+ kfree(action);
+ return retval;
+}
+
+void free_irq(unsigned int irq, void *dev_id)
+{
+ struct irqaction * action, **p;
+ unsigned long flags;
+
+ if (irq >= NR_IRQS) {
+ printk("Trying to free IRQ%d\n",irq);
+ return;
+ }
+ for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) {
+ if (action->dev_id != dev_id)
+ continue;
+
+ /* Found it - now free it */
+ save_flags(flags);
+ cli();
+ *p = action->next;
+ if (!irq[irq_action]) {
+ mask_irq(irq);
+ set_int_vector(irq, bad_interrupt[irq], 0);
+ }
+ restore_flags(flags);
+ kfree(action);
+ return;
+ }
+ printk("Trying to free free IRQ%d\n",irq);
+}
+
+void weird_irq(void)
+{
+ __asm__("di");
+ printk("weird irq\n");
+ while(1);
+}
+
+/* init_IRQ() is called by start_kernel and is responsible for fixing IRQ masks and
+ setting the irq vector table to point to bad_interrupt ptrs.
+*/
+
+void system_call(void); /* from entry.S */
+
+void init_IRQ(void)
+{
+ int i;
+
+ /* clear all interrupt masks */
+
+#ifndef CONFIG_SVINTO_SIM
+ *R_IRQ_MASK0_CLR = 0xffffffff;
+ *R_IRQ_MASK1_CLR = 0xffffffff;
+ *R_IRQ_MASK2_CLR = 0xffffffff;
+#endif
+
+ *R_VECT_MASK_CLR = 0xffffffff;
+
+ /* clear the shortcut entry points */
+
+ for(i = 0; i < NR_IRQS; i++)
+ irq_shortcuts[i] = NULL;
+
+ for (i = 0; i < 256; i++)
+ etrax_irv->v[i] = weird_irq;
+
+ /* set all etrax irq's to the bad handlers */
+ for (i = 2; i < NR_IRQS; i++)
+ set_int_vector(i, bad_interrupt[i], 0);
+
+ /* except IRQ 15 which is the multiple-IRQ handler on Etrax100 */
+
+ set_int_vector(15, multiple_interrupt, 0);
+
+ /* 0 and 1 which are special breakpoint/NMI traps */
+
+ set_int_vector(0, hwbreakpoint, 0);
+ set_int_vector(1, IRQ1_interrupt, 0);
+
+ /* and irq 14 which is the mmu bus fault handler */
+
+ set_int_vector(14, mmu_bus_fault, 0);
+
+ /* setup the system-call trap, which is reached by BREAK 13 */
+
+ set_break_vector(13, system_call);
+
+#ifdef CONFIG_KGDB
+ /* setup kgdb if its enabled, and break into the debugger */
+
+ kgdb_init();
+
+ breakpoint();
+#endif
+
+}
diff --git a/arch/cris/kernel/kgdb.c b/arch/cris/kernel/kgdb.c
new file mode 100644
index 000000000..98d427b05
--- /dev/null
+++ b/arch/cris/kernel/kgdb.c
@@ -0,0 +1,1540 @@
+/*!**************************************************************************
+*!
+*! FILE NAME : kgdb.c
+*!
+*! DESCRIPTION: Implementation of the gdb stub with respect to ETRAX 100.
+*! It is a mix of arch/m68k/kernel/kgdb.c and cris_stub.c.
+*!
+*!---------------------------------------------------------------------------
+*! HISTORY
+*!
+*! DATE NAME CHANGES
+*! ---- ---- -------
+*! Apr 26 1999 Hendrik Ruijter Initial version.
+*! May 6 1999 Hendrik Ruijter Removed call to strlen in libc and removed
+*! struct assignment as it generates calls to
+*! memcpy in libc.
+*! Jun 17 1999 Hendrik Ruijter Added gdb 4.18 support. 'X', 'qC' and 'qL'.
+*! Jul 21 1999 Bjorn Wesen eLinux port
+*!
+*! $Log: kgdb.c,v $
+*! Revision 1.2 2001/01/12 14:22:25 orjanf
+*! Updated kernel debugging support to work with ETRAX 100LX.
+*!
+*! Revision 1.1 2000/07/10 16:25:21 bjornw
+*! Initial revision
+*!
+*! Revision 1.1.1.1 1999/12/03 14:57:31 bjornw
+*! * Initial version of arch/cris, the latest CRIS architecture with an MMU.
+*! Mostly copied from arch/etrax100 with appropriate renames of files.
+*! The mm/ subdir is copied from arch/i386.
+*! This does not compile yet at all.
+*!
+*!
+*! Revision 1.4 1999/07/22 17:25:25 bjornw
+*! Dont wait for + in putpacket if we havent hit the initial breakpoint yet. Added a kgdb_init function which sets up the break and irq vectors.
+*!
+*! Revision 1.3 1999/07/21 19:51:18 bjornw
+*! Check if the interrupting char is a ctrl-C, ignore otherwise.
+*!
+*! Revision 1.2 1999/07/21 18:09:39 bjornw
+*! Ported to eLinux architecture, and added some kgdb documentation.
+*!
+*!
+*!---------------------------------------------------------------------------
+*!
+*! $Id: kgdb.c,v 1.2 2001/01/12 14:22:25 orjanf Exp $
+*!
+*! (C) Copyright 1999, Axis Communications AB, LUND, SWEDEN
+*!
+*!**************************************************************************/
+/* @(#) cris_stub.c 1.3 06/17/99 */
+
+/*
+ * kgdb usage notes:
+ * -----------------
+ *
+ * If you select CONFIG_KGDB in the configuration, the kernel will be built
+ * with different gcc flags: "-g" is added to get debug infos, and
+ * "-fomit-frame-pointer" is omitted to make debugging easier. Since the
+ * resulting kernel will be quite big (approx. > 7 MB), it will be stripped
+ * before compresion. Such a kernel will behave just as usually, except if
+ * given a "debug=<device>" command line option. (Only serial devices are
+ * allowed for <device>, i.e. no printers or the like; possible values are
+ * machine depedend and are the same as for the usual debug device, the one
+ * for logging kernel messages.) If that option is given and the device can be
+ * initialized, the kernel will connect to the remote gdb in trap_init(). The
+ * serial parameters are fixed to 8N1 and 115200 bps, for easyness of
+ * implementation.
+ *
+ * To start a debugging session, start that gdb with the debugging kernel
+ * image (the one with the symbols, vmlinux.debug) named on the command line.
+ * This file will be used by gdb to get symbol and debugging infos about the
+ * kernel. Next, select remote debug mode by
+ * target remote <device>
+ * where <device> is the name of the serial device over which the debugged
+ * machine is connected. Maybe you have to adjust the baud rate by
+ * set remotebaud <rate>
+ * or also other parameters with stty:
+ * shell stty ... </dev/...
+ * If the kernel to debug has already booted, it waited for gdb and now
+ * connects, and you'll see a breakpoint being reported. If the kernel isn't
+ * running yet, start it now. The order of gdb and the kernel doesn't matter.
+ * Another thing worth knowing about in the getting-started phase is how to
+ * debug the remote protocol itself. This is activated with
+ * set remotedebug 1
+ * gdb will then print out each packet sent or received. You'll also get some
+ * messages about the gdb stub on the console of the debugged machine.
+ *
+ * If all that works, you can use lots of the usual debugging techniques on
+ * the kernel, e.g. inspecting and changing variables/memory, setting
+ * breakpoints, single stepping and so on. It's also possible to interrupt the
+ * debugged kernel by pressing C-c in gdb. Have fun! :-)
+ *
+ * The gdb stub is entered (and thus the remote gdb gets control) in the
+ * following situations:
+ *
+ * - If breakpoint() is called. This is just after kgdb initialization, or if
+ * a breakpoint() call has been put somewhere into the kernel source.
+ * (Breakpoints can of course also be set the usual way in gdb.)
+ * In eLinux, we call breakpoint() in init/main.c after IRQ initialization.
+ *
+ * - If there is a kernel exception, i.e. bad_super_trap() or die_if_kernel()
+ * are entered. All the CPU exceptions are mapped to (more or less..., see
+ * the hard_trap_info array below) appropriate signal, which are reported
+ * to gdb. die_if_kernel() is usually called after some kind of access
+ * error and thus is reported as SIGSEGV.
+ *
+ * - When panic() is called. This is reported as SIGABRT.
+ *
+ * - If C-c is received over the serial line, which is treated as
+ * SIGINT.
+ *
+ * Of course, all these signals are just faked for gdb, since there is no
+ * signal concept as such for the kernel. It also isn't possible --obviously--
+ * to set signal handlers from inside gdb, or restart the kernel with a
+ * signal.
+ *
+ * Current limitations:
+ *
+ * - While the kernel is stopped, interrupts are disabled for safety reasons
+ * (i.e., variables not changing magically or the like). But this also
+ * means that the clock isn't running anymore, and that interrupts from the
+ * hardware may get lost/not be served in time. This can cause some device
+ * errors...
+ *
+ * - When single-stepping, only one instruction of the current thread is
+ * executed, but interrupts are allowed for that time and will be serviced
+ * if pending. Be prepared for that.
+ *
+ * - All debugging happens in kernel virtual address space. There's no way to
+ * access physical memory not mapped in kernel space, or to access user
+ * space. A way to work around this is using get_user_long & Co. in gdb
+ * expressions, but only for the current process.
+ *
+ * - Interrupting the kernel only works if interrupts are currently allowed,
+ * and the interrupt of the serial line isn't blocked by some other means
+ * (IPL too high, disabled, ...)
+ *
+ * - The gdb stub is currently not reentrant, i.e. errors that happen therein
+ * (e.g. accesing invalid memory) may not be caught correctly. This could
+ * be removed in future by introducing a stack of struct registers.
+ *
+ */
+
+/*
+ * To enable debugger support, two things need to happen. One, a
+ * call to kgdb_init() 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().
+ *
+ * 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
+ *
+ */
+
+
+#include <linux/string.h>
+#include <linux/signal.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/linkage.h>
+
+#include <asm/setup.h>
+#include <asm/ptrace.h>
+
+#include <asm/svinto.h>
+#include <asm/irq.h>
+
+static int kgdb_started = 0;
+
+/********************************* Register image ****************************/
+/* Use the order of registers as defined in "AXIS ETRAX CRIS Programmer's
+ Reference", p. 1-1, with the additional register definitions of the
+ ETRAX 100LX in cris-opc.h.
+ There are 16 general 32-bit registers, R0-R15, where R14 is the stack
+ pointer, SP, and R15 is the program counter, PC.
+ There are 16 special registers, P0-P15, where three of the unimplemented
+ registers, P0, P4 and P8, are reserved as zero-registers. A read from
+ any of these registers returns zero and a write has no effect. */
+
+typedef
+struct register_image
+{
+ /* Offset */
+ unsigned int r0; /* 0x00 */
+ unsigned int r1; /* 0x04 */
+ unsigned int r2; /* 0x08 */
+ unsigned int r3; /* 0x0C */
+ unsigned int r4; /* 0x10 */
+ unsigned int r5; /* 0x14 */
+ unsigned int r6; /* 0x18 */
+ unsigned int r7; /* 0x1C */
+ unsigned int r8; /* 0x20 Frame pointer */
+ unsigned int r9; /* 0x24 */
+ unsigned int r10; /* 0x28 */
+ unsigned int r11; /* 0x2C */
+ unsigned int r12; /* 0x30 */
+ unsigned int r13; /* 0x34 */
+ unsigned int sp; /* 0x38 Stack pointer */
+ unsigned int pc; /* 0x3C Program counter */
+
+ unsigned char p0; /* 0x40 8-bit zero-register */
+ unsigned char vr; /* 0x41 Version register */
+
+ unsigned short p4; /* 0x42 16-bit zero-register */
+ unsigned short ccr; /* 0x44 Condition code register */
+
+ unsigned int mof; /* 0x46 Multiply overflow register */
+
+ unsigned int p8; /* 0x4A 32-bit zero-register */
+ unsigned int ibr; /* 0x4E Interrupt base register */
+ unsigned int irp; /* 0x52 Interrupt return pointer */
+ unsigned int srp; /* 0x56 Subroutine return pointer */
+ unsigned int bar; /* 0x5A Breakpoint address register */
+ unsigned int dccr; /* 0x5E Double condition code register */
+ unsigned int brp; /* 0x62 Breakpoint return pointer (pc in caller) */
+ unsigned int usp; /* 0x66 User mode stack pointer */
+} registers;
+
+/************** Prototypes for local library functions ***********************/
+
+/* Copy of strcpy from libc. */
+static char *gdb_cris_strcpy (char *s1, const char *s2);
+
+/* Copy of strlen from libc. */
+static int gdb_cris_strlen (const char *s);
+
+/* Copy of memchr from libc. */
+static void *gdb_cris_memchr (const void *s, int c, int n);
+
+/* Copy of strtol from libc. Does only support base 16. */
+static int gdb_cris_strtol (const char *s, char **endptr, int base);
+
+/********************** Prototypes for local functions. **********************/
+/* Copy the content of a register image into another. The size n is
+ the size of the register image. Due to struct assignment generation of
+ memcpy in libc. */
+static void copy_registers (registers *dptr, registers *sptr, int n);
+
+/* Copy the stored registers from the stack. Put the register contents
+ of thread thread_id in the struct reg. */
+static void copy_registers_from_stack (int thread_id, registers *reg);
+
+/* Copy the registers to the stack. Put the register contents of thread
+ thread_id from struct reg to the stack. */
+static void copy_registers_to_stack (int thread_id, registers *reg);
+
+/* Write a value to a specified register regno in the register image
+ of the current thread. */
+static int write_register (int regno, char *val);
+
+/* Write a value to a specified register in the stack of a thread other
+ than the current thread. */
+static write_stack_register (int thread_id, int regno, char *valptr);
+
+/* Read a value from a specified register in the register image. Returns the
+ status of the read operation. The register value is returned in valptr. */
+static int read_register (char regno, unsigned int *valptr);
+
+/* Serial port, reads one character. ETRAX 100 specific. from debugport.c */
+int getDebugChar (void);
+
+/* Serial port, writes one character. ETRAX 100 specific. from debugport.c */
+void putDebugChar (int val);
+
+void enableDebugIRQ (void);
+
+/* Returns the character equivalent of a nibble, bit 7, 6, 5, and 4 of a byte,
+ represented by int x. */
+static char highhex (int x);
+
+/* Returns the character equivalent of a nibble, bit 3, 2, 1, and 0 of a byte,
+ represented by int x. */
+static char lowhex (int x);
+
+/* Returns the integer equivalent of a hexadecimal character. */
+static int hex (char ch);
+
+/* Convert the memory, pointed to by mem into hexadecimal representation.
+ Put the result in buf, and return a pointer to the last character
+ in buf (null). */
+static char *mem2hex (char *buf, unsigned char *mem, int count);
+
+/* Convert the array, in hexadecimal representation, pointed to by buf into
+ binary representation. Put the result in mem, and return a pointer to
+ the character after the last byte written. */
+static unsigned char *hex2mem (unsigned char *mem, char *buf, int count);
+
+/* Put the content of the array, in binary representation, pointed to by buf
+ into memory pointed to by mem, and return a pointer to
+ the character after the last byte written. */
+static unsigned char *bin2mem (unsigned char *mem, unsigned char *buf, int count);
+
+/* Await the sequence $<data>#<checksum> and store <data> in the array buffer
+ returned. */
+static void getpacket (char *buffer);
+
+/* Send $<data>#<checksum> from the <data> in the array buffer. */
+static void putpacket (char *buffer);
+
+/* Build and send a response packet in order to inform the host the
+ stub is stopped. */
+static void stub_is_stopped (int sigval);
+
+/* All expected commands are sent from remote.c. Send a response according
+ to the description in remote.c. */
+static void handle_exception (int sigval);
+
+/* Performs a complete re-start from scratch. ETRAX specific. */
+static void kill_restart (void);
+
+/******************** Prototypes for global functions. ***********************/
+
+/* The string str is prepended with the GDB printout token and sent. */
+void putDebugString (const unsigned char *str, int length); /* used by etrax100ser.c */
+
+/* The hook for both static (compiled) and dynamic breakpoints set by GDB.
+ ETRAX 100 specific. */
+void handle_breakpoint (void); /* used by irq.c */
+
+/* The hook for an interrupt generated by GDB. ETRAX 100 specific. */
+void handle_interrupt (void); /* used by irq.c */
+
+/* A static breakpoint to be used at startup. */
+void breakpoint (void); /* called by init/main.c */
+
+/* From osys_int.c, executing_task contains the number of the current
+ executing task in osys. Does not know of object-oriented threads. */
+extern unsigned char executing_task;
+
+/* The number of characters used for a 64 bit thread identifier. */
+#define HEXCHARS_IN_THREAD_ID 16
+
+/* Avoid warning as the internal_stack is not used in the C-code. */
+#define USEDVAR(name) { if (name) { ; } }
+#define USEDFUN(name) { void (*pf)(void) = (void *)name; USEDVAR(pf) }
+
+/********************************** Packet I/O ******************************/
+/* BUFMAX defines the maximum number of characters in
+ inbound/outbound buffers */
+#define BUFMAX 512
+
+/* Run-length encoding maximum length. Send 64 at most. */
+#define RUNLENMAX 64
+
+/* Definition of all valid hexadecimal characters */
+static const char hexchars[] = "0123456789abcdef";
+
+/* The inbound/outbound buffers used in packet I/O */
+static char remcomInBuffer[BUFMAX];
+static char remcomOutBuffer[BUFMAX];
+
+/* Error and warning messages. */
+enum error_type
+{
+ SUCCESS, E01, E02, E03, E04, E05, E06, E07
+};
+static char *error_message[] =
+{
+ "",
+ "E01 Set current or general thread - H[c,g] - internal error.",
+ "E02 Change register content - P - cannot change read-only register.",
+ "E03 Thread is not alive.", /* T, not used. */
+ "E04 The command is not supported - [s,C,S,!,R,d,r] - internal error.",
+ "E05 Change register content - P - the register is not implemented..",
+ "E06 Change memory content - M - internal error.",
+ "E07 Change register content - P - the register is not stored on the stack"
+};
+/********************************* Register image ****************************/
+/* Use the order of registers as defined in "AXIS ETRAX CRIS Programmer's
+ Reference", p. 1-1, with the additional register definitions of the
+ ETRAX 100LX in cris-opc.h.
+ There are 16 general 32-bit registers, R0-R15, where R14 is the stack
+ pointer, SP, and R15 is the program counter, PC.
+ There are 16 special registers, P0-P15, where three of the unimplemented
+ registers, P0, P4 and P8, are reserved as zero-registers. A read from
+ any of these registers returns zero and a write has no effect. */
+enum register_name
+{
+ R0, R1, R2, R3,
+ R4, R5, R6, R7,
+ R8, R9, R10, R11,
+ R12, R13, SP, PC,
+ P0, VR, P2, P3,
+ P4, CCR, P6, MOF,
+ P8, IBR, IRP, SRP,
+ BAR, DCCR, BRP, USP
+};
+
+/* The register sizes of the registers in register_name. An unimplemented register
+ is designated by size 0 in this array. */
+static int register_size[] =
+{
+ 4, 4, 4, 4,
+ 4, 4, 4, 4,
+ 4, 4, 4, 4,
+ 4, 4, 4, 4,
+ 1, 1, 0, 0,
+ 2, 2, 0, 4,
+ 4, 4, 4, 4,
+ 4, 4, 4, 4
+};
+
+/* Contains the register image of the executing thread in the assembler
+ part of the code in order to avoid horrible addressing modes. */
+static registers reg;
+
+/* FIXME: Should this be used? Delete otherwise. */
+/* Contains the assumed consistency state of the register image. Uses the
+ enum error_type for state information. */
+static int consistency_status = SUCCESS;
+
+/********************************** Handle exceptions ************************/
+/* The variable reg contains the register image associated with the
+ current_thread_c variable. It is a complete register image created at
+ entry. The reg_g contains a register image of a task where the general
+ registers are taken from the stack and all special registers are taken
+ from the executing task. It is associated with current_thread_g and used
+ in order to provide access mainly for 'g', 'G' and 'P'.
+*/
+
+/* Need two task id pointers in order to handle Hct and Hgt commands. */
+static int current_thread_c = 0;
+static int current_thread_g = 0;
+
+/* Need two register images in order to handle Hct and Hgt commands. The
+ variable reg_g is in addition to reg above. */
+static registers reg_g;
+
+/********************************** Breakpoint *******************************/
+/* Use an internal stack in the breakpoint and interrupt response routines */
+#define INTERNAL_STACK_SIZE 1024
+static char internal_stack[INTERNAL_STACK_SIZE];
+
+/* Due to the breakpoint return pointer, a state variable is needed to keep
+ track of whether it is a static (compiled) or dynamic (gdb-invoked)
+ breakpoint to be handled. A static breakpoint uses the content of register
+ BRP as it is whereas a dynamic breakpoint requires subtraction with 2
+ in order to execute the instruction. The first breakpoint is static. */
+static unsigned char is_dyn_brkp = 0;
+
+/********************************* String library ****************************/
+/* Single-step over library functions creates trap loops. */
+
+/* Copy char s2[] to s1[]. */
+static char*
+gdb_cris_strcpy (char *s1, const char *s2)
+{
+ char *s = s1;
+
+ for (s = s1; (*s++ = *s2++) != '\0'; )
+ ;
+ return (s1);
+}
+
+/* Find length of s[]. */
+static int
+gdb_cris_strlen (const char *s)
+{
+ const char *sc;
+
+ for (sc = s; *sc != '\0'; sc++)
+ ;
+ return (sc - s);
+}
+
+/* Find first occurrence of c in s[n]. */
+static void*
+gdb_cris_memchr (const void *s, int c, int n)
+{
+ const unsigned char uc = c;
+ const unsigned char *su;
+
+ for (su = s; 0 < n; ++su, --n)
+ if (*su == uc)
+ return ((void *)su);
+ return (NULL);
+}
+/******************************* Standard library ****************************/
+/* Single-step over library functions creates trap loops. */
+/* Convert string to long. */
+static int
+gdb_cris_strtol (const char *s, char **endptr, int base)
+{
+ char *s1;
+ char *sd;
+ int x = 0;
+
+ for (s1 = (char*)s; (sd = gdb_cris_memchr(hexchars, *s1, base)) != NULL; ++s1)
+ x = x * base + (sd - hexchars);
+
+ if (endptr)
+ {
+ /* Unconverted suffix is stored in endptr unless endptr is NULL. */
+ *endptr = s1;
+ }
+
+ return x;
+}
+
+int
+double_this(int x)
+{
+ return 2 * x;
+}
+
+/********************************* Register image ****************************/
+/* Copy the content of a register image into another. The size n is
+ the size of the register image. Due to struct assignment generation of
+ memcpy in libc. */
+static void
+copy_registers (registers *dptr, registers *sptr, int n)
+{
+ unsigned char *dreg;
+ unsigned char *sreg;
+
+ for (dreg = (unsigned char*)dptr, sreg = (unsigned char*)sptr; n > 0; n--)
+ *dreg++ = *sreg++;
+}
+
+#ifdef PROCESS_SUPPORT
+/* Copy the stored registers from the stack. Put the register contents
+ of thread thread_id in the struct reg. */
+static void
+copy_registers_from_stack (int thread_id, registers *regptr)
+{
+ int j;
+ stack_registers *s = (stack_registers *)stack_list[thread_id];
+ unsigned int *d = (unsigned int *)regptr;
+
+ for (j = 13; j >= 0; j--)
+ *d++ = s->r[j];
+ regptr->sp = (unsigned int)stack_list[thread_id];
+ regptr->pc = s->pc;
+ regptr->dccr = s->dccr;
+ regptr->srp = s->srp;
+}
+
+/* Copy the registers to the stack. Put the register contents of thread
+ thread_id from struct reg to the stack. */
+static void
+copy_registers_to_stack (int thread_id, registers *regptr)
+{
+ int i;
+ stack_registers *d = (stack_registers *)stack_list[thread_id];
+ unsigned int *s = (unsigned int *)regptr;
+
+ for (i = 0; i < 14; i++) {
+ d->r[i] = *s++;
+ }
+ d->pc = regptr->pc;
+ d->dccr = regptr->dccr;
+ d->srp = regptr->srp;
+}
+#endif
+
+/* Write a value to a specified register in the register image of the current
+ thread. Returns status code SUCCESS, E02 or E05. */
+static int
+write_register (int regno, char *val)
+{
+ int status = SUCCESS;
+ registers *current_reg = &reg;
+
+ if (regno >= R0 && regno <= PC) {
+ /* 32-bit register with simple offset. */
+ hex2mem ((unsigned char *)current_reg + regno * sizeof(unsigned int),
+ val, sizeof(unsigned int));
+ }
+ else if (regno == P0 || regno == VR || regno == P4 || regno == P8) {
+ /* Do not support read-only registers. */
+ status = E02;
+ }
+ else if (regno == CCR) {
+ /* 16 bit register with complex offset. (P4 is read-only, P6 is not implemented,
+ and P7 (MOF) is 32 bits in ETRAX 100LX. */
+ hex2mem ((unsigned char *)&(current_reg->ccr) + (regno-CCR) * sizeof(unsigned short),
+ val, sizeof(unsigned short));
+ }
+ else if (regno >= MOF && regno <= USP) {
+ /* 32 bit register with complex offset. (P8 has been taken care of.) */
+ hex2mem ((unsigned char *)&(current_reg->ibr) + (regno-IBR) * sizeof(unsigned int),
+ val, sizeof(unsigned int));
+ }
+ else {
+ /* Do not support nonexisting or unimplemented registers (P2, P3, and P6). */
+ status = E05;
+ }
+ return status;
+}
+
+#ifdef PROCESS_SUPPORT
+/* Write a value to a specified register in the stack of a thread other
+ than the current thread. Returns status code SUCCESS or E07. */
+static int
+write_stack_register (int thread_id, int regno, char *valptr)
+{
+ int status = SUCCESS;
+ stack_registers *d = (stack_registers *)stack_list[thread_id];
+ unsigned int val;
+
+ hex2mem ((unsigned char *)&val, valptr, sizeof(unsigned int));
+ if (regno >= R0 && regno < SP) {
+ d->r[regno] = val;
+ }
+ else if (regno == SP) {
+ stack_list[thread_id] = val;
+ }
+ else if (regno == PC) {
+ d->pc = val;
+ }
+ else if (regno == SRP) {
+ d->srp = val;
+ }
+ else if (regno == DCCR) {
+ d->dccr = val;
+ }
+ else {
+ /* Do not support registers in the current thread. */
+ status = E07;
+ }
+ return status;
+}
+#endif
+
+/* Read a value from a specified register in the register image. Returns the
+ value in the register or -1 for non-implemented registers.
+ Should check consistency_status after a call which may be E05 after changes
+ in the implementation. */
+static int
+read_register (char regno, unsigned int *valptr)
+{
+ registers *current_reg = &reg;
+
+ if (regno >= R0 && regno <= PC) {
+ /* 32-bit register with simple offset. */
+ *valptr = *(unsigned int *)((char *)current_reg + regno * sizeof(unsigned int));
+ return SUCCESS;
+ }
+ else if (regno == P0 || regno == VR) {
+ /* 8 bit register with complex offset. */
+ *valptr = (unsigned int)(*(unsigned char *)
+ ((char *)&(current_reg->p0) + (regno-P0) * sizeof(char)));
+ return SUCCESS;
+ }
+ else if (regno == P4 || regno == CCR) {
+ /* 16 bit register with complex offset. */
+ *valptr = (unsigned int)(*(unsigned short *)
+ ((char *)&(current_reg->p4) + (regno-P4) * sizeof(unsigned short)));
+ return SUCCESS;
+ }
+ else if (regno >= MOF && regno <= USP) {
+ /* 32 bit register with complex offset. */
+ *valptr = *(unsigned int *)((char *)&(current_reg->p8)
+ + (regno-P8) * sizeof(unsigned int));
+ return SUCCESS;
+ }
+ else {
+ /* Do not support nonexisting or unimplemented registers (P2, P3, and P6). */
+ consistency_status = E05;
+ return E05;
+ }
+}
+
+/********************************** Packet I/O ******************************/
+/* Returns the character equivalent of a nibble, bit 7, 6, 5, and 4 of a byte,
+ represented by int x. */
+static inline char
+highhex(int x)
+{
+ return hexchars[(x >> 4) & 0xf];
+}
+
+/* Returns the character equivalent of a nibble, bit 3, 2, 1, and 0 of a byte,
+ represented by int x. */
+static inline char
+lowhex(int x)
+{
+ return hexchars[x & 0xf];
+}
+
+/* Returns the integer equivalent of a hexadecimal character. */
+static int
+hex (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);
+}
+
+/* Convert the memory, pointed to by mem into hexadecimal representation.
+ Put the result in buf, and return a pointer to the last character
+ in buf (null). */
+
+static int do_printk = 0;
+
+static char *
+mem2hex(char *buf, unsigned char *mem, int count)
+{
+ int i;
+ int ch;
+
+ if (mem == NULL) {
+ /* Bogus read from m0. FIXME: What constitutes a valid address? */
+ for (i = 0; i < count; i++) {
+ *buf++ = '0';
+ *buf++ = '0';
+ }
+ } else {
+ /* Valid mem address. */
+ for (i = 0; i < count; i++) {
+ ch = *mem++;
+ *buf++ = highhex (ch);
+ *buf++ = lowhex (ch);
+ }
+ }
+
+ /* Terminate properly. */
+ *buf = '\0';
+ return (buf);
+}
+
+/* Convert the array, in hexadecimal representation, pointed to by buf into
+ binary representation. Put the result in mem, and return a pointer to
+ the character after the last byte written. */
+static unsigned char*
+hex2mem (unsigned char *mem, char *buf, int count)
+{
+ int i;
+ unsigned char ch;
+ for (i = 0; i < count; i++) {
+ ch = hex (*buf++) << 4;
+ ch = ch + hex (*buf++);
+ *mem++ = ch;
+ }
+ return (mem);
+}
+
+/* Put the content of the array, in binary representation, pointed to by buf
+ into memory pointed to by mem, and return a pointer to the character after
+ the last byte written.
+ Gdb will escape $, #, and the escape char (0x7d). */
+static unsigned char*
+bin2mem (unsigned char *mem, unsigned char *buf, int count)
+{
+ int i;
+ unsigned char *next;
+ for (i = 0; i < count; i++) {
+ /* Check for any escaped characters. Be paranoid and
+ only unescape chars that should be escaped. */
+ if (*buf == 0x7d) {
+ next = buf + 1;
+ if (*next == 0x3 || *next == 0x4 || *next == 0x5D) /* #, $, ESC */
+ {
+ buf++;
+ *buf += 0x20;
+ }
+ }
+ *mem++ = *buf++;
+ }
+ return (mem);
+}
+
+/* Await the sequence $<data>#<checksum> and store <data> in the array buffer
+ returned. */
+static void
+getpacket (char *buffer)
+{
+ unsigned char checksum;
+ unsigned char xmitcsum;
+ int i;
+ int count;
+ char ch;
+ do {
+ while ((ch = getDebugChar ()) != '$')
+ /* Wait for the start character $ and ignore all other characters */;
+ checksum = 0;
+ xmitcsum = -1;
+ count = 0;
+ /* Read until a # or the end of the buffer is reached */
+ while (count < BUFMAX) {
+ ch = getDebugChar ();
+ if (ch == '#')
+ break;
+ checksum = checksum + ch;
+ buffer[count] = ch;
+ count = count + 1;
+ }
+ buffer[count] = '\0';
+
+ if (ch == '#') {
+ xmitcsum = hex (getDebugChar ()) << 4;
+ xmitcsum += hex (getDebugChar ());
+ if (checksum != xmitcsum) {
+ /* Wrong checksum */
+ putDebugChar ('-');
+ }
+ else {
+ /* Correct checksum */
+ putDebugChar ('+');
+ /* If sequence characters are received, reply with them */
+ if (buffer[2] == ':') {
+ putDebugChar (buffer[0]);
+ putDebugChar (buffer[1]);
+ /* Remove the sequence characters from the buffer */
+ count = gdb_cris_strlen (buffer);
+ for (i = 3; i <= count; i++)
+ buffer[i - 3] = buffer[i];
+ }
+ }
+ }
+ } while (checksum != xmitcsum);
+}
+
+/* Send $<data>#<checksum> from the <data> in the array buffer. */
+
+static void
+putpacket(char *buffer)
+{
+ int checksum;
+ int runlen;
+ int encode;
+
+ do {
+ char *src = buffer;
+ putDebugChar ('$');
+ checksum = 0;
+ while (*src) {
+ /* Do run length encoding */
+ putDebugChar (*src);
+ checksum += *src;
+ runlen = 0;
+ while (runlen < RUNLENMAX && *src == src[runlen]) {
+ runlen++;
+ }
+ if (runlen > 3) {
+ /* Got a useful amount */
+ putDebugChar ('*');
+ checksum += '*';
+ encode = runlen + ' ' - 4;
+ putDebugChar (encode);
+ checksum += encode;
+ src += runlen;
+ }
+ else {
+ src++;
+ }
+ }
+ putDebugChar ('#');
+ putDebugChar (highhex (checksum));
+ putDebugChar (lowhex (checksum));
+ } while(kgdb_started && (getDebugChar() != '+'));
+}
+
+/* The string str is prepended with the GDB printout token and sent. Required
+ in traditional implementations. */
+void
+putDebugString (const unsigned char *str, int length)
+{
+ remcomOutBuffer[0] = 'O';
+ mem2hex(&remcomOutBuffer[1], (unsigned char *)str, length);
+ putpacket(remcomOutBuffer);
+}
+
+/********************************** Handle exceptions ************************/
+/* Build and send a response packet in order to inform the host the
+ stub is stopped. TAAn...:r...;n...:r...;n...:r...;
+ AA = signal number
+ n... = register number (hex)
+ r... = register contents
+ n... = `thread'
+ r... = thread process ID. This is a hex integer.
+ n... = other string not starting with valid hex digit.
+ gdb should ignore this n,r pair and go on to the next.
+ This way we can extend the protocol. */
+static void
+stub_is_stopped(int sigval)
+{
+ char *ptr = remcomOutBuffer;
+ int regno;
+
+ unsigned int reg_cont;
+ int status;
+
+ /* Send trap type (converted to signal) */
+
+ *ptr++ = 'T';
+ *ptr++ = highhex (sigval);
+ *ptr++ = lowhex (sigval);
+
+ /* Send register contents. We probably only need to send the
+ * PC, frame pointer and stack pointer here. Other registers will be
+ * explicitely asked for. But for now, send all.
+ */
+
+ for (regno = R0; regno <= USP; regno++) {
+ /* Store n...:r...; for the registers in the buffer. */
+
+ status = read_register (regno, &reg_cont);
+
+ if (status == SUCCESS) {
+
+ *ptr++ = highhex (regno);
+ *ptr++ = lowhex (regno);
+ *ptr++ = ':';
+
+ ptr = mem2hex(ptr, (unsigned char *)&reg_cont,
+ register_size[regno]);
+ *ptr++ = ';';
+ }
+
+ }
+
+#ifdef PROCESS_SUPPORT
+ /* Store the registers of the executing thread. Assume that both step,
+ continue, and register content requests are with respect to this
+ thread. The executing task is from the operating system scheduler. */
+
+ current_thread_c = executing_task;
+ current_thread_g = executing_task;
+
+ /* A struct assignment translates into a libc memcpy call. Avoid
+ all libc functions in order to prevent recursive break points. */
+ copy_registers (&reg_g, &reg, sizeof(registers));
+
+ /* Store thread:r...; with the executing task TID. */
+ gdb_cris_strcpy (&remcomOutBuffer[pos], "thread:");
+ pos += gdb_cris_strlen ("thread:");
+ remcomOutBuffer[pos++] = highhex (executing_task);
+ remcomOutBuffer[pos++] = lowhex (executing_task);
+ gdb_cris_strcpy (&remcomOutBuffer[pos], ";");
+#endif
+
+ /* null-terminate and send it off */
+
+ *ptr = 0;
+
+ putpacket (remcomOutBuffer);
+}
+
+/* All expected commands are sent from remote.c. Send a response according
+ to the description in remote.c. */
+static void
+handle_exception (int sigval)
+{
+ /* Avoid warning of not used. */
+
+ USEDFUN(handle_exception);
+ USEDVAR(internal_stack[0]);
+
+ /* Send response. */
+
+ stub_is_stopped (sigval);
+
+ for (;;) {
+ remcomOutBuffer[0] = '\0';
+ getpacket (remcomInBuffer);
+ switch (remcomInBuffer[0]) {
+ case 'g':
+ /* Read registers: g
+ Success: Each byte of register data is described by two hex digits.
+ Registers are in the internal order for GDB, and the bytes
+ in a register are in the same order the machine uses.
+ Failure: void. */
+
+ {
+#ifdef PROCESS_SUPPORT
+ /* Use the special register content in the executing thread. */
+ copy_registers (&reg_g, &reg, sizeof(registers));
+ /* Replace the content available on the stack. */
+ if (current_thread_g != executing_task) {
+ copy_registers_from_stack (current_thread_g, &reg_g);
+ }
+ mem2hex ((unsigned char *)remcomOutBuffer, (unsigned char *)&reg_g, sizeof(registers));
+#else
+ mem2hex(remcomOutBuffer, (char *)&reg, sizeof(registers));
+#endif
+ }
+ break;
+
+ case 'G':
+ /* Write registers. GXX..XX
+ Each byte of register data is described by two hex digits.
+ Success: OK
+ Failure: void. */
+#ifdef PROCESS_SUPPORT
+ hex2mem ((unsigned char *)&reg_g, &remcomInBuffer[1], sizeof(registers));
+ if (current_thread_g == executing_task) {
+ copy_registers (&reg, &reg_g, sizeof(registers));
+ }
+ else {
+ copy_registers_to_stack(current_thread_g, &reg_g);
+ }
+#else
+ hex2mem((char *)&reg, &remcomInBuffer[1], sizeof(registers));
+#endif
+ gdb_cris_strcpy (remcomOutBuffer, "OK");
+ break;
+
+ case 'P':
+ /* Write register. Pn...=r...
+ Write register n..., hex value without 0x, with value r...,
+ which contains a hex value without 0x and two hex digits
+ for each byte in the register (target byte order). P1f=11223344 means
+ set register 31 to 44332211.
+ Success: OK
+ Failure: E02, E05 */
+ {
+ char *suffix;
+ int regno = gdb_cris_strtol (&remcomInBuffer[1], &suffix, 16);
+ int status;
+#ifdef PROCESS_SUPPORT
+ if (current_thread_g =! executing_task)
+ status = write_stack_register (current_thread_g, regno, suffix+1);
+ else
+#endif
+ status = write_register (regno, suffix+1);
+
+ switch (status) {
+ case E02:
+ /* Do not support read-only registers. */
+ gdb_cris_strcpy (remcomOutBuffer, error_message[E02]);
+ break;
+ case E05:
+ /* Do not support non-existing registers. */
+ gdb_cris_strcpy (remcomOutBuffer, error_message[E05]);
+ break;
+ case E07:
+ /* Do not support non-existing registers on the stack. */
+ gdb_cris_strcpy (remcomOutBuffer, error_message[E07]);
+ break;
+ default:
+ /* Valid register number. */
+ gdb_cris_strcpy (remcomOutBuffer, "OK");
+ break;
+ }
+ }
+ break;
+
+ case 'm':
+ /* Read from memory. mAA..AA,LLLL
+ AA..AA is the address and LLLL is the length.
+ Success: XX..XX is the memory content. Can be fewer bytes than
+ requested if only part of the data may be read. m6000120a,6c means
+ retrieve 108 byte from base address 6000120a.
+ Failure: void. */
+ {
+ char *suffix;
+ unsigned char *addr = (unsigned char *)gdb_cris_strtol(&remcomInBuffer[1],
+ &suffix, 16); int length = gdb_cris_strtol(suffix+1, 0, 16);
+
+ mem2hex(remcomOutBuffer, addr, length);
+ }
+ break;
+
+ case 'X':
+ /* Write to memory. XAA..AA,LLLL:XX..XX
+ AA..AA is the start address, LLLL is the number of bytes, and
+ XX..XX is the binary data.
+ Success: OK
+ Failure: void. */
+ case 'M':
+ /* Write to memory. MAA..AA,LLLL:XX..XX
+ AA..AA is the start address, LLLL is the number of bytes, and
+ XX..XX is the hexadecimal data.
+ Success: OK
+ Failure: void. */
+ {
+ char *lenptr;
+ char *dataptr;
+ unsigned char *addr = (unsigned char *)gdb_cris_strtol(&remcomInBuffer[1],
+ &lenptr, 16);
+ int length = gdb_cris_strtol(lenptr+1, &dataptr, 16);
+ if (*lenptr == ',' && *dataptr == ':') {
+ if (remcomInBuffer[0] == 'M') {
+ hex2mem(addr, dataptr + 1, length);
+ }
+ else /* X */ {
+ bin2mem(addr, dataptr + 1, length);
+ }
+ gdb_cris_strcpy (remcomOutBuffer, "OK");
+ }
+ else {
+ gdb_cris_strcpy (remcomOutBuffer, error_message[E06]);
+ }
+ }
+ break;
+
+ case 'c':
+ /* Continue execution. cAA..AA
+ AA..AA is the address where execution is resumed. If AA..AA is
+ omitted, resume at the present address.
+ Success: return to the executing thread.
+ Failure: will never know. */
+ if (remcomInBuffer[1] != '\0') {
+ reg.pc = gdb_cris_strtol (&remcomInBuffer[1], 0, 16);
+ }
+ enableDebugIRQ();
+ return;
+
+ case 's':
+ /* Step. sAA..AA
+ AA..AA is the address where execution is resumed. If AA..AA is
+ omitted, resume at the present address. Success: return to the
+ executing thread. Failure: will never know.
+
+ Should never be invoked. The single-step is implemented on
+ the host side. If ever invoked, it is an internal error E04. */
+ gdb_cris_strcpy (remcomOutBuffer, error_message[E04]);
+ putpacket (remcomOutBuffer);
+ return;
+
+ case '?':
+ /* The last signal which caused a stop. ?
+ Success: SAA, where AA is the signal number.
+ Failure: void. */
+ remcomOutBuffer[0] = 'S';
+ remcomOutBuffer[1] = highhex (sigval);
+ remcomOutBuffer[2] = lowhex (sigval);
+ remcomOutBuffer[3] = 0;
+ break;
+
+ case 'D':
+ /* Detach from host. D
+ Success: OK, and return to the executing thread.
+ Failure: will never know */
+ putpacket ("OK");
+ return;
+
+ case 'k':
+ case 'r':
+ /* kill request or reset request.
+ Success: restart of target.
+ Failure: will never know. */
+ kill_restart ();
+ break;
+
+ case 'C':
+ case 'S':
+ case '!':
+ case 'R':
+ case 'd':
+ /* Continue with signal sig. Csig;AA..AA
+ Step with signal sig. Ssig;AA..AA
+ Use the extended remote protocol. !
+ Restart the target system. R0
+ Toggle debug flag. d
+ Search backwards. tAA:PP,MM
+ Not supported: E04 */
+ gdb_cris_strcpy (remcomOutBuffer, error_message[E04]);
+ break;
+#ifdef PROCESS_SUPPORT
+
+ case 'T':
+ /* Thread alive. TXX
+ Is thread XX alive?
+ Success: OK, thread XX is alive.
+ Failure: E03, thread XX is dead. */
+ {
+ int thread_id = (int)gdb_cris_strtol (&remcomInBuffer[1], 0, 16);
+ /* Cannot tell whether it is alive or not. */
+ if (thread_id >= 0 && thread_id < number_of_tasks)
+ gdb_cris_strcpy (remcomOutBuffer, "OK");
+ }
+ break;
+
+ case 'H':
+ /* Set thread for subsequent operations: Hct
+ c = 'c' for thread used in step and continue;
+ t can be -1 for all threads.
+ c = 'g' for thread used in other operations.
+ t = 0 means pick any thread.
+ Success: OK
+ Failure: E01 */
+ {
+ int thread_id = gdb_cris_strtol (&remcomInBuffer[2], 0, 16);
+ if (remcomInBuffer[1] == 'c') {
+ /* c = 'c' for thread used in step and continue */
+ /* Do not change current_thread_c here. It would create a mess in
+ the scheduler. */
+ gdb_cris_strcpy (remcomOutBuffer, "OK");
+ }
+ else if (remcomInBuffer[1] == 'g') {
+ /* c = 'g' for thread used in other operations.
+ t = 0 means pick any thread. Impossible since the scheduler does
+ not allow that. */
+ if (thread_id >= 0 && thread_id < number_of_tasks) {
+ current_thread_g = thread_id;
+ gdb_cris_strcpy (remcomOutBuffer, "OK");
+ }
+ else {
+ /* Not expected - send an error message. */
+ gdb_cris_strcpy (remcomOutBuffer, error_message[E01]);
+ }
+ }
+ else {
+ /* Not expected - send an error message. */
+ gdb_cris_strcpy (remcomOutBuffer, error_message[E01]);
+ }
+ }
+ break;
+
+ case 'q':
+ case 'Q':
+ /* Query of general interest. qXXXX
+ Set general value XXXX. QXXXX=yyyy */
+ {
+ int pos;
+ int nextpos;
+ int thread_id;
+
+ switch (remcomInBuffer[1]) {
+ case 'C':
+ /* Identify the remote current thread. */
+ gdb_cris_strcpy (&remcomOutBuffer[0], "QC");
+ remcomOutBuffer[2] = highhex (current_thread_c);
+ remcomOutBuffer[3] = lowhex (current_thread_c);
+ remcomOutBuffer[4] = '\0';
+ break;
+ case 'L':
+ gdb_cris_strcpy (&remcomOutBuffer[0], "QM");
+ /* Reply with number of threads. */
+ if (os_is_started()) {
+ remcomOutBuffer[2] = highhex (number_of_tasks);
+ remcomOutBuffer[3] = lowhex (number_of_tasks);
+ }
+ else {
+ remcomOutBuffer[2] = highhex (0);
+ remcomOutBuffer[3] = lowhex (1);
+ }
+ /* Done with the reply. */
+ remcomOutBuffer[4] = lowhex (1);
+ pos = 5;
+ /* Expects the argument thread id. */
+ for (; pos < (5 + HEXCHARS_IN_THREAD_ID); pos++)
+ remcomOutBuffer[pos] = remcomInBuffer[pos];
+ /* Reply with the thread identifiers. */
+ if (os_is_started()) {
+ /* Store the thread identifiers of all tasks. */
+ for (thread_id = 0; thread_id < number_of_tasks; thread_id++) {
+ nextpos = pos + HEXCHARS_IN_THREAD_ID - 1;
+ for (; pos < nextpos; pos ++)
+ remcomOutBuffer[pos] = lowhex (0);
+ remcomOutBuffer[pos++] = lowhex (thread_id);
+ }
+ }
+ else {
+ /* Store the thread identifier of the boot task. */
+ nextpos = pos + HEXCHARS_IN_THREAD_ID - 1;
+ for (; pos < nextpos; pos ++)
+ remcomOutBuffer[pos] = lowhex (0);
+ remcomOutBuffer[pos++] = lowhex (current_thread_c);
+ }
+ remcomOutBuffer[pos] = '\0';
+ break;
+ default:
+ /* Not supported: "" */
+ /* Request information about section offsets: qOffsets. */
+ remcomOutBuffer[0] = 0;
+ break;
+ }
+ }
+ break;
+#endif /* PROCESS_SUPPORT */
+
+ default:
+ /* The stub should ignore other request and send an empty
+ response ($#<checksum>). This way we can extend the protocol and GDB
+ can tell whether the stub it is talking to uses the old or the new. */
+ remcomOutBuffer[0] = 0;
+ break;
+ }
+ putpacket(remcomOutBuffer);
+ }
+}
+
+/* The jump is to the address 0x00000002. Performs a complete re-start
+ from scratch. */
+static void
+kill_restart ()
+{
+ __asm__ volatile ("jump 2");
+}
+
+/********************************** Breakpoint *******************************/
+/* The hook for both a static (compiled) and a dynamic breakpoint set by GDB.
+ An internal stack is used by the stub. The register image of the caller is
+ stored in the structure register_image.
+ Interactive communication with the host is handled by handle_exception and
+ finally the register image is restored. */
+
+void kgdb_handle_breakpoint(void);
+
+asm ("
+ .global _kgdb_handle_breakpoint
+_kgdb_handle_breakpoint:
+;;
+;; Response to the break-instruction
+;;
+;; Create a register image of the caller
+;;
+ move dccr,[_reg+0x5E] ; Save the flags in DCCR before disable interrupts
+ di ; Disable interrupts
+ move.d r0,[_reg] ; Save R0
+ move.d r1,[_reg+0x04] ; Save R1
+ move.d r2,[_reg+0x08] ; Save R2
+ move.d r3,[_reg+0x0C] ; Save R3
+ move.d r4,[_reg+0x10] ; Save R4
+ move.d r5,[_reg+0x14] ; Save R5
+ move.d r6,[_reg+0x18] ; Save R6
+ move.d r7,[_reg+0x1C] ; Save R7
+ move.d r8,[_reg+0x20] ; Save R8
+ move.d r9,[_reg+0x24] ; Save R9
+ move.d r10,[_reg+0x28] ; Save R10
+ move.d r11,[_reg+0x2C] ; Save R11
+ move.d r12,[_reg+0x30] ; Save R12
+ move.d r13,[_reg+0x34] ; Save R13
+ move.d sp,[_reg+0x38] ; Save SP (R14)
+;; Due to the old assembler-versions BRP might not be recognized
+ .word 0xE670 ; move brp,r0
+ subq 2,r0 ; Set to address of previous instruction.
+ move.d r0,[_reg+0x3c] ; Save the address in PC (R15)
+ clear.b [_reg+0x40] ; Clear P0
+ move vr,[_reg+0x41] ; Save special register P1
+ clear.w [_reg+0x42] ; Clear P4
+ move ccr,[_reg+0x44] ; Save special register CCR
+ move mof,[_reg+0x46] ; P7
+ clear.d [_reg+0x4A] ; Clear P8
+ move ibr,[_reg+0x4E] ; P9,
+ move irp,[_reg+0x52] ; P10,
+ move srp,[_reg+0x56] ; P11,
+ move dtp0,[_reg+0x5A] ; P12, register BAR, assembler might not know BAR
+ ; P13, register DCCR already saved
+;; Due to the old assembler-versions BRP might not be recognized
+ .word 0xE670 ; move brp,r0
+;; Static (compiled) breakpoints must return to the next instruction in order
+;; to avoid infinite loops. Dynamic (gdb-invoked) must restore the instruction
+;; in order to execute it when execution is continued.
+ test.b [_is_dyn_brkp] ; Is this a dynamic breakpoint?
+ beq is_static ; No, a static breakpoint
+ nop
+ subq 2,r0 ; rerun the instruction the break replaced
+is_static:
+ moveq 1,r1
+ move.b r1,[_is_dyn_brkp] ; Set the state variable to dynamic breakpoint
+ move.d r0,[_reg+0x62] ; Save the return address in BRP
+ move usp,[_reg+0x66] ; USP
+;;
+;; Handle the communication
+;;
+ move.d _internal_stack+1020,sp ; Use the internal stack which grows upward
+ moveq 5,r10 ; SIGTRAP
+ jsr _handle_exception ; Interactive routine
+;;
+;; Return to the caller
+;;
+ move.d [_reg],r0 ; Restore R0
+ move.d [_reg+0x04],r1 ; Restore R1
+ move.d [_reg+0x08],r2 ; Restore R2
+ move.d [_reg+0x0C],r3 ; Restore R3
+ move.d [_reg+0x10],r4 ; Restore R4
+ move.d [_reg+0x14],r5 ; Restore R5
+ move.d [_reg+0x18],r6 ; Restore R6
+ move.d [_reg+0x1C],r7 ; Restore R7
+ move.d [_reg+0x20],r8 ; Restore R8
+ move.d [_reg+0x24],r9 ; Restore R9
+ move.d [_reg+0x28],r10 ; Restore R10
+ move.d [_reg+0x2C],r11 ; Restore R11
+ move.d [_reg+0x30],r12 ; Restore R12
+ move.d [_reg+0x34],r13 ; Restore R13
+;;
+;; FIXME: Which registers should be restored?
+;;
+ move.d [_reg+0x38],sp ; Restore SP (R14)
+ move [_reg+0x56],srp ; Restore the subroutine return pointer.
+ move [_reg+0x5E],dccr ; Restore DCCR
+ move [_reg+0x66],usp ; Restore USP
+ jump [_reg+0x62] ; A jump to the content in register BRP works.
+ nop ;
+");
+
+/* The hook for an interrupt generated by GDB. An internal stack is used
+ by the stub. The register image of the caller is stored in the structure
+ register_image. Interactive communication with the host is handled by
+ handle_exception and finally the register image is restored. Due to the
+ old assembler which does not recognise the break instruction and the
+ breakpoint return pointer hex-code is used. */
+
+void kgdb_handle_serial(void);
+
+asm ("
+ .global _kgdb_handle_serial
+_kgdb_handle_serial:
+;;
+;; Response to a serial interrupt
+;;
+
+ move dccr,[_reg+0x5E] ; Save the flags in DCCR
+ di ; Disable interrupts
+ move.d r0,[_reg] ; Save R0
+ move.d r1,[_reg+0x04] ; Save R1
+ move.d r2,[_reg+0x08] ; Save R2
+ move.d r3,[_reg+0x0C] ; Save R3
+ move.d r4,[_reg+0x10] ; Save R4
+ move.d r5,[_reg+0x14] ; Save R5
+ move.d r6,[_reg+0x18] ; Save R6
+ move.d r7,[_reg+0x1C] ; Save R7
+ move.d r8,[_reg+0x20] ; Save R8
+ move.d r9,[_reg+0x24] ; Save R9
+ move.d r10,[_reg+0x28] ; Save R10
+ move.d r11,[_reg+0x2C] ; Save R11
+ move.d r12,[_reg+0x30] ; Save R12
+ move.d r13,[_reg+0x34] ; Save R13
+ move.d sp,[_reg+0x38] ; Save SP (R14)
+ move irp,[_reg+0x3c] ; Save the address in PC (R15)
+ clear.b [_reg+0x40] ; Clear P0
+ move vr,[_reg+0x41] ; Save special register P1,
+ clear.w [_reg+0x42] ; Clear P4
+ move ccr,[_reg+0x44] ; Save special register CCR
+ move mof,[_reg+0x46] ; P7
+ clear.d [_reg+0x4A] ; Clear P8
+ move ibr,[_reg+0x4E] ; P9,
+ move irp,[_reg+0x52] ; P10,
+ move srp,[_reg+0x56] ; P11,
+ move dtp0,[_reg+0x5A] ; P12, register BAR, assembler might not know BAR
+ ; P13, register DCCR already saved
+;; Due to the old assembler-versions BRP might not be recognized
+ .word 0xE670 ; move brp,r0
+ move.d r0,[_reg+0x62] ; Save the return address in BRP
+ move usp,[_reg+0x66] ; USP
+
+;; get the serial character (from debugport.c) and check if its a ctrl-c
+
+ jsr _getDebugChar
+ cmp.b 3, r10
+ bne goback
+ nop
+
+;;
+;; Handle the communication
+;;
+ move.d _internal_stack+1020,sp ; Use the internal stack
+ moveq 2,r10 ; SIGINT
+ jsr _handle_exception ; Interactive routine
+
+goback:
+;;
+;; Return to the caller
+;;
+ move.d [_reg],r0 ; Restore R0
+ move.d [_reg+0x04],r1 ; Restore R1
+ move.d [_reg+0x08],r2 ; Restore R2
+ move.d [_reg+0x0C],r3 ; Restore R3
+ move.d [_reg+0x10],r4 ; Restore R4
+ move.d [_reg+0x14],r5 ; Restore R5
+ move.d [_reg+0x18],r6 ; Restore R6
+ move.d [_reg+0x1C],r7 ; Restore R7
+ move.d [_reg+0x20],r8 ; Restore R8
+ move.d [_reg+0x24],r9 ; Restore R9
+ move.d [_reg+0x28],r10 ; Restore R10
+ move.d [_reg+0x2C],r11 ; Restore R11
+ move.d [_reg+0x30],r12 ; Restore R12
+ move.d [_reg+0x34],r13 ; Restore R13
+;;
+;; FIXME: Which registers should be restored?
+;;
+ move.d [_reg+0x38],sp ; Restore SP (R14)
+ move [_reg+0x56],srp ; Restore the subroutine return pointer.
+ move [_reg+0x5E],dccr ; Restore DCCR
+ move [_reg+0x66],usp ; Restore USP
+ reti ; Return from the interrupt routine
+ nop
+");
+
+/* Use this static breakpoint in the start-up only. */
+
+void
+breakpoint(void)
+{
+ kgdb_started = 1;
+ is_dyn_brkp = 0; /* This is a static, not a dynamic breakpoint. */
+ __asm__ volatile ("break 8"); /* Jump to handle_breakpoint. */
+}
+
+/* initialize kgdb. doesn't break into the debugger, but sets up irq and ports */
+
+void
+kgdb_init(void)
+{
+ /* could initialize debug port as well but it's done in head.S already... */
+
+ set_break_vector(8, kgdb_handle_breakpoint);
+ set_int_vector(8, kgdb_handle_serial, 0);
+
+ enableDebugIRQ();
+}
+
+/****************************** End of file **********************************/
diff --git a/arch/cris/kernel/ksyms.c b/arch/cris/kernel/ksyms.c
new file mode 100644
index 000000000..fa48796dc
--- /dev/null
+++ b/arch/cris/kernel/ksyms.c
@@ -0,0 +1,2 @@
+/* no kernel support yet */
+
diff --git a/arch/cris/kernel/process.c b/arch/cris/kernel/process.c
new file mode 100644
index 000000000..901449879
--- /dev/null
+++ b/arch/cris/kernel/process.c
@@ -0,0 +1,327 @@
+/* $Id: process.c,v 1.8 2000/09/13 14:34:13 bjornw Exp $
+ *
+ * linux/arch/cris/kernel/process.c
+ *
+ * Copyright (C) 1995 Linus Torvalds
+ * Copyright (C) 2000 Axis Communications AB
+ *
+ * Authors: Bjorn Wesen (bjornw@axis.com)
+ *
+ */
+
+/*
+ * This file handles the architecture-dependent parts of process handling..
+ */
+
+#define __KERNEL_SYSCALLS__
+#include <stdarg.h>
+
+#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/malloc.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/smp.h>
+
+//#define DEBUG
+
+/*
+ * Initial task structure. Make this a per-architecture thing,
+ * because different architectures tend to have different
+ * alignment requirements and potentially different initial
+ * setup.
+ */
+
+static struct vm_area_struct init_mmap = INIT_MMAP;
+static struct fs_struct init_fs = INIT_FS;
+static struct files_struct init_files = INIT_FILES;
+static struct signal_struct init_signals = INIT_SIGNALS;
+struct mm_struct init_mm = INIT_MM(init_mm);
+
+/*
+ * Initial task structure.
+ *
+ * We need to make sure that this is 8192-byte aligned due to the
+ * way process stacks are handled. This is done by having a special
+ * "init_task" linker map entry..
+ */
+
+union task_union init_task_union
+ __attribute__((__section__(".data.init_task"))) =
+ { INIT_TASK(init_task_union.task) };
+
+static int hlt_counter=0;
+
+/* in a system call, set_esp0 is called to remember the stack frame, therefore
+ in the implementation of syscalls we can use that value to access the stack
+ frame and saved registers.
+*/
+
+#define currentregs ((struct pt_regs *)current->thread.esp0)
+
+asmlinkage void set_esp0(unsigned long ssp)
+{
+ current->thread.esp0 = ssp;
+}
+
+void disable_hlt(void)
+{
+ hlt_counter++;
+}
+
+void enable_hlt(void)
+{
+ hlt_counter--;
+}
+
+int cpu_idle(void *unused)
+{
+ while(1) {
+ current->counter = -100;
+ schedule();
+ }
+}
+
+/* if the watchdog is enabled, we can simply disable interrupts and go
+ * into an eternal loop, and the watchdog will reset the CPU after 0.1s
+ */
+
+void hard_reset_now (void)
+{
+ printk("*** HARD RESET ***\n");
+ cli();
+ while(1) /* waiting for RETRIBUTION! */ ;
+}
+
+void machine_restart(void)
+{
+ hard_reset_now();
+}
+
+/* can't do much here... */
+
+void machine_halt(void)
+{
+}
+
+void machine_power_off(void)
+{
+}
+
+/*
+ * This is the mechanism for creating a new kernel thread.
+ *
+ * NOTE! Only a kernel-only process(ie the swapper or direct descendants
+ * who haven't done an "execve()") should use this: it will work within
+ * a system call from a "real" process, but the process memory space will
+ * not be free'd until both the parent and the child have exited.
+ */
+
+int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
+{
+ register long __a __asm__ ("r10");
+
+ __asm__ __volatile__
+ ("movu.w %1,r1\n\t" /* r1 contains syscall number, to sys_clone */
+ "clear.d r10\n\t" /* r10 is argument 1 to clone */
+ "move.d %2,r11\n\t" /* r11 is argument 2 to clone, the flags */
+ "break 13\n\t" /* call sys_clone, this will fork */
+ "test.d r10\n\t" /* parent or child? child returns 0 here. */
+ "bne 1f\n\t" /* jump if parent */
+ "nop\n\t" /* delay slot */
+ "move.d %4,r10\n\t" /* set argument to function to call */
+ "jsr %5\n\t" /* call specified function */
+ "movu.w %3,r1\n\t" /* r1 is sys_exit syscall number */
+ "moveq -1,r10\n\t" /* Give a really bad exit-value */
+ "break 13\n\t" /* call sys_exit, killing the child */
+ "1:\n\t"
+ : "=r" (__a)
+ : "g" (__NR_clone), "r" (flags | CLONE_VM), "g" (__NR_exit),
+ "r" (arg), "r" (fn)
+ : "r10", "r11", "r1");
+
+ return __a;
+}
+
+
+
+void flush_thread(void)
+{
+}
+
+asmlinkage void ret_from_sys_call(void);
+
+/* setup the child's kernel stack with a pt_regs and switch_stack on it.
+ * it will be un-nested during _resume and _ret_from_sys_call when the
+ * new thread is scheduled.
+ *
+ * also setup the thread switching structure which is used to keep
+ * thread-specific data during _resumes.
+ *
+ */
+
+int copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
+ unsigned long unused,
+ struct task_struct *p, struct pt_regs *regs)
+{
+ struct pt_regs * childregs;
+ struct switch_stack *swstack;
+
+ /* put the pt_regs structure at the end of the new kernel stack page and fix it up
+ * remember that the task_struct doubles as the kernel stack for the task
+ */
+
+ childregs = ((struct pt_regs *) ((unsigned long)p + THREAD_SIZE)) - 1;
+
+ *childregs = *regs; /* struct copy of pt_regs */
+
+ childregs->r10 = 0; /* child returns 0 after a fork/clone */
+
+ /* put the switch stack right below the pt_regs */
+
+ swstack = ((struct switch_stack *)childregs) - 1;
+
+ swstack->r9 = 0; /* parameter to ret_from_sys_call, 0 == dont restart the syscall */
+
+ /* we want to return into ret_from_sys_call after the _resume */
+
+ swstack->return_ip = (unsigned long) ret_from_sys_call;
+
+ /* fix the user-mode stackpointer */
+
+ p->thread.usp = usp;
+
+ /* and the kernel-mode one */
+
+ p->thread.ksp = (unsigned long) swstack;
+
+ /* esp0 keeps the pt_regs stacked structure pointer */
+
+ p->thread.esp0 = (unsigned long) childregs;
+
+#ifdef DEBUG
+ printk("kern_stack_page 0x%x, used stack %d, thread.usp 0x%x, usp 0x%x\n",
+ current->kernel_stack_page, usedstack, p->thread.usp, usp);
+#endif
+ return 0;
+}
+
+/*
+ * fill in the user structure for a core dump..
+ */
+void dump_thread(struct pt_regs * regs, struct user * dump)
+{
+ int i;
+#if 0
+/* changed the size calculations - should hopefully work better. lbt */
+ dump->magic = CMAGIC;
+ dump->start_code = 0;
+ dump->start_stack = regs->esp & ~(PAGE_SIZE - 1);
+ dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT;
+ dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> PAGE_SHIFT;
+ dump->u_dsize -= dump->u_tsize;
+ dump->u_ssize = 0;
+ for (i = 0; i < 8; i++)
+ dump->u_debugreg[i] = current->debugreg[i];
+
+ if (dump->start_stack < TASK_SIZE)
+ dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT;
+
+ dump->regs = *regs;
+
+ dump->u_fpvalid = dump_fpu (regs, &dump->i387);
+#endif
+}
+
+asmlinkage int sys_fork(void)
+{
+ return do_fork(SIGCHLD, rdusp(), currentregs, 0);
+}
+
+/* if newusp is 0, we just grab the old usp */
+
+asmlinkage int sys_clone(unsigned long newusp, unsigned long flags)
+{
+ if (!newusp)
+ newusp = rdusp();
+ return do_fork(flags, newusp, currentregs, 0);
+}
+
+/* vfork is a system call in i386 because of register-pressure - maybe
+ * we can remove it and handle it in libc but we put it here until then.
+ */
+
+asmlinkage int sys_vfork(void)
+{
+ return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), currentregs, 0);
+}
+
+/*
+ * sys_execve() executes a new program.
+ */
+asmlinkage int sys_execve(const char *fname, char **argv, char **envp)
+{
+ int error;
+ char *filename;
+
+ filename = getname(fname);
+ error = PTR_ERR(filename);
+
+ if (IS_ERR(filename))
+ goto out;
+ error = do_execve(filename, argv, envp, currentregs);
+ putname(filename);
+ out:
+ return error;
+}
+
+/*
+ * These bracket the sleeping functions..
+ */
+
+extern void scheduling_functions_start_here(void);
+extern void scheduling_functions_end_here(void);
+#define first_sched ((unsigned long) scheduling_functions_start_here)
+#define last_sched ((unsigned long) scheduling_functions_end_here)
+
+unsigned long get_wchan(struct task_struct *p)
+{
+#if 0
+ /* YURGH. TODO. */
+
+ unsigned long ebp, esp, eip;
+ unsigned long stack_page;
+ int count = 0;
+ if (!p || p == current || p->state == TASK_RUNNING)
+ return 0;
+ stack_page = (unsigned long)p;
+ esp = p->thread.esp;
+ if (!stack_page || esp < stack_page || esp > 8188+stack_page)
+ return 0;
+ /* include/asm-i386/system.h:switch_to() pushes ebp last. */
+ ebp = *(unsigned long *) esp;
+ do {
+ if (ebp < stack_page || ebp > 8184+stack_page)
+ return 0;
+ eip = *(unsigned long *) (ebp+4);
+ if (eip < first_sched || eip >= last_sched)
+ return eip;
+ ebp = *(unsigned long *) ebp;
+ } while (count++ < 16);
+#endif
+ return 0;
+}
+#undef last_sched
+#undef first_sched
diff --git a/arch/cris/kernel/ptrace.c b/arch/cris/kernel/ptrace.c
new file mode 100644
index 000000000..384952211
--- /dev/null
+++ b/arch/cris/kernel/ptrace.c
@@ -0,0 +1,340 @@
+/*
+ * linux/arch/cris/kernel/ptrace.c
+ *
+ * Parts taken from the m68k port.
+ *
+ * Copyright (c) 2000 Axis Communications AB
+ *
+ * Authors: Bjorn Wesen
+ *
+ * $Log: ptrace.c,v $
+ * Revision 1.3 2000/12/18 23:45:25 bjornw
+ * Linux/CRIS first version
+ *
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/user.h>
+
+#include <asm/uaccess.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/processor.h>
+
+/*
+ * does not yet catch signals sent when the child dies.
+ * in exit.c or in signal.c.
+ */
+
+/* determines which bits in DCCR the user has access to. */
+/* 1 = access 0 = no access */
+#define DCCR_MASK 0x0000001f /* XNZVC */
+
+/*
+ * Get contents of register REGNO in task TASK.
+ */
+static inline long get_reg(struct task_struct *task, unsigned int regno)
+{
+ /* USP is a special case, it's not in the pt_regs struct but
+ * in the tasks thread struct
+ */
+
+ if (regno == PT_USP)
+ return task->thread.usp;
+ else if (regno <= PT_MAX)
+ return ((unsigned long *)(task->thread.esp0))[regno];
+ else
+ return 0;
+}
+
+/*
+ * Write contents of register REGNO in task TASK.
+ */
+static inline int put_reg(struct task_struct *task, unsigned int regno,
+ unsigned long data)
+{
+ unsigned long *addr;
+
+ if (regno == PT_USP)
+ task->thread.usp = data;
+ else if (regno <= PT_MAX)
+ ((unsigned long *)(task->thread.esp0))[regno] = data;
+ else
+ return -1;
+ return 0;
+}
+
+asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
+{
+ struct task_struct *child;
+ int ret;
+
+ lock_kernel();
+ ret = -EPERM;
+ if (request == PTRACE_TRACEME) {
+ /* are we already being traced? */
+ if (current->ptrace & PT_PTRACED)
+ goto out;
+ /* set the ptrace bit in the process flags. */
+ current->ptrace |= PT_PTRACED;
+ ret = 0;
+ goto out;
+ }
+ ret = -ESRCH;
+ read_lock(&tasklist_lock);
+ child = find_task_by_pid(pid);
+ if (child)
+ get_task_struct(child);
+ read_unlock(&tasklist_lock);
+ if (!child)
+ goto out;
+ ret = -EPERM;
+ if (pid == 1) /* you may not mess with init */
+ goto out_tsk;
+ if (request == PTRACE_ATTACH) {
+ if (child == current)
+ goto out_tsk;
+ if ((!child->dumpable ||
+ (current->uid != child->euid) ||
+ (current->uid != child->suid) ||
+ (current->uid != child->uid) ||
+ (current->gid != child->egid) ||
+ (current->gid != child->sgid) ||
+ (!cap_issubset(child->cap_permitted, current->cap_permitted)) ||
+ (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE))
+ goto out_tsk;
+ /* the same process cannot be attached many times */
+ if (child->ptrace & PT_PTRACED)
+ goto out_tsk;
+ child->ptrace |= PT_PTRACED;
+
+ write_lock_irq(&tasklist_lock);
+ if (child->p_pptr != current) {
+ REMOVE_LINKS(child);
+ child->p_pptr = current;
+ SET_LINKS(child);
+ }
+ write_unlock_irq(&tasklist_lock);
+
+ send_sig(SIGSTOP, child, 1);
+ ret = 0;
+ goto out_tsk;
+ }
+ ret = -ESRCH;
+ if (!(child->ptrace & PT_PTRACED))
+ goto out_tsk;
+ if (child->state != TASK_STOPPED) {
+ if (request != PTRACE_KILL)
+ goto out_tsk;
+ }
+ if (child->p_pptr != current)
+ goto out_tsk;
+
+ switch (request) {
+ /* when I and D space are separate, these will need to be fixed. */
+ case PTRACE_PEEKTEXT: /* read word at location addr. */
+ case PTRACE_PEEKDATA: {
+ unsigned long tmp;
+ int copied;
+
+ copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+ ret = -EIO;
+ if (copied != sizeof(tmp))
+ break;
+ ret = put_user(tmp,(unsigned long *) data);
+ break;
+ }
+
+ /* read the word at location addr in the USER area. */
+ case PTRACE_PEEKUSR: {
+ unsigned long tmp;
+
+ ret = -EIO;
+ if ((addr & 3) || addr < 0 || addr >= sizeof(struct user))
+ break;
+
+ tmp = 0; /* Default return condition */
+ ret = -EIO;
+ if (addr < sizeof(struct pt_regs)) {
+ tmp = get_reg(child, addr >> 2);
+ ret = put_user(tmp, (unsigned long *)data);
+ }
+ break;
+ }
+
+ /* when I and D space are separate, this will have to be fixed. */
+ case PTRACE_POKETEXT: /* write the word at location addr. */
+ case PTRACE_POKEDATA:
+ ret = 0;
+ if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
+ break;
+ ret = -EIO;
+ break;
+
+ case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
+ ret = -EIO;
+ if ((addr & 3) || addr < 0 || addr >= sizeof(struct user))
+ break;
+
+ if (addr < sizeof(struct pt_regs)) {
+ addr >>= 2;
+
+ if (addr == PT_DCCR) {
+ /* don't allow the tracing process to change stuff like
+ * interrupt enable, kernel/user bit, dma enables etc.
+ */
+ data &= DCCR_MASK;
+ data |= get_reg(child, PT_DCCR) & ~DCCR_MASK;
+ }
+ if (put_reg(child, addr, data))
+ break;
+ ret = 0;
+ }
+ break;
+
+ case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
+ case PTRACE_CONT: { /* restart after signal. */
+ long tmp;
+
+ ret = -EIO;
+ if ((unsigned long) data > _NSIG)
+ break;
+ if (request == PTRACE_SYSCALL)
+ child->ptrace |= PT_TRACESYS;
+ else
+ child->ptrace &= ~PT_TRACESYS;
+ child->exit_code = data;
+ /* TODO: make sure any pending breakpoint is killed */
+ wake_up_process(child);
+ ret = 0;
+ break;
+ }
+
+/*
+ * make the child exit. Best I can do is send it a sigkill.
+ * perhaps it should be put in the status that it wants to
+ * exit.
+ */
+ case PTRACE_KILL: {
+ long tmp;
+
+ ret = 0;
+ if (child->state == TASK_ZOMBIE) /* already dead */
+ break;
+ child->exit_code = SIGKILL;
+ /* TODO: make sure any pending breakpoint is killed */
+ wake_up_process(child);
+ break;
+ }
+
+ case PTRACE_SINGLESTEP: { /* set the trap flag. */
+ long tmp;
+
+ ret = -EIO;
+ if ((unsigned long) data > _NSIG)
+ break;
+ child->ptrace &= ~PT_TRACESYS;
+
+ /* TODO: set some clever breakpoint mechanism... */
+
+ child->exit_code = data;
+ /* give it a chance to run. */
+ wake_up_process(child);
+ ret = 0;
+ break;
+ }
+
+ case PTRACE_DETACH: { /* detach a process that was attached. */
+ long tmp;
+
+ ret = -EIO;
+ if ((unsigned long) data > _NSIG)
+ break;
+ child->ptrace &= ~(PT_PTRACED | PT_TRACESYS);
+ child->exit_code = data;
+ write_lock_irq(&tasklist_lock);
+ REMOVE_LINKS(child);
+ child->p_pptr = child->p_opptr;
+ SET_LINKS(child);
+ write_unlock_irq(&tasklist_lock);
+ /* TODO: make sure any pending breakpoint is killed */
+ wake_up_process(child);
+ ret = 0;
+ break;
+ }
+
+ case PTRACE_GETREGS: { /* Get all gp regs from the child. */
+ int i;
+ unsigned long tmp;
+ for (i = 0; i <= PT_MAX; i++) {
+ tmp = get_reg(child, i);
+ if (put_user(tmp, (unsigned long *) data)) {
+ ret = -EFAULT;
+ break;
+ }
+ data += sizeof(long);
+ }
+ ret = 0;
+ break;
+ }
+
+ case PTRACE_SETREGS: { /* Set all gp regs in the child. */
+ int i;
+ unsigned long tmp;
+ for (i = 0; i <= PT_MAX; i++) {
+ if (get_user(tmp, (unsigned long *) data)) {
+ ret = -EFAULT;
+ break;
+ }
+ if (i == PT_DCCR) {
+ tmp &= DCCR_MASK;
+ tmp |= get_reg(child, PT_DCCR) & ~DCCR_MASK;
+ }
+ put_reg(child, i, tmp);
+ data += sizeof(long);
+ }
+ ret = 0;
+ break;
+ }
+
+ default:
+ ret = -EIO;
+ break;
+ }
+out_tsk:
+ free_task_struct(child);
+out:
+ unlock_kernel();
+ return ret;
+}
+
+asmlinkage void syscall_trace(void)
+{
+ if ((current->ptrace & (PT_PTRACED | PT_TRACESYS)) !=
+ (PT_PTRACED | PT_TRACESYS))
+ return;
+ /* TODO: make a way to distinguish between a syscall stop and SIGTRAP
+ * delivery like in the i386 port ?
+ */
+ current->exit_code = SIGTRAP;
+ current->state = TASK_STOPPED;
+ notify_parent(current, SIGCHLD);
+ schedule();
+ /*
+ * this isn't the same as continuing with a signal, but it will do
+ * for normal use. strace only continues with a signal if the
+ * stopping signal is not SIGTRAP. -brl
+ */
+ if (current->exit_code) {
+ send_sig(current->exit_code, current, 1);
+ current->exit_code = 0;
+ }
+}
diff --git a/arch/cris/kernel/semaphore.c b/arch/cris/kernel/semaphore.c
new file mode 100644
index 000000000..5a9478f03
--- /dev/null
+++ b/arch/cris/kernel/semaphore.c
@@ -0,0 +1,238 @@
+/*
+ * Generic semaphore code. Buyer beware. Do your own
+ * specific changes in <asm/semaphore-helper.h>
+ */
+
+#include <linux/sched.h>
+#include <asm/semaphore-helper.h>
+
+/*
+ * Semaphores are implemented using a two-way counter:
+ * The "count" variable is decremented for each process
+ * that tries to sleep, while the "waking" variable is
+ * incremented when the "up()" code goes to wake up waiting
+ * processes.
+ *
+ * Notably, the inline "up()" and "down()" functions can
+ * efficiently test if they need to do any extra work (up
+ * needs to do something only if count was negative before
+ * the increment operation.
+ *
+ * waking_non_zero() (from asm/semaphore.h) must execute
+ * atomically.
+ *
+ * When __up() is called, the count was negative before
+ * incrementing it, and we need to wake up somebody.
+ *
+ * This routine adds one to the count of processes that need to
+ * wake up and exit. ALL waiting processes actually wake up but
+ * only the one that gets to the "waking" field first will gate
+ * through and acquire the semaphore. The others will go back
+ * to sleep.
+ *
+ * Note that these functions are only called when there is
+ * contention on the lock, and as such all this is the
+ * "non-critical" part of the whole semaphore business. The
+ * critical part is the inline stuff in <asm/semaphore.h>
+ * where we want to avoid any extra jumps and calls.
+ */
+void __up(struct semaphore *sem)
+{
+ wake_one_more(sem);
+ wake_up(&sem->wait);
+}
+
+/*
+ * Perform the "down" function. Return zero for semaphore acquired,
+ * return negative for signalled out of the function.
+ *
+ * If called from __down, the return is ignored and the wait loop is
+ * not interruptible. This means that a task waiting on a semaphore
+ * using "down()" cannot be killed until someone does an "up()" on
+ * the semaphore.
+ *
+ * If called from __down_interruptible, the return value gets checked
+ * upon return. If the return value is negative then the task continues
+ * with the negative value in the return register (it can be tested by
+ * the caller).
+ *
+ * Either form may be used in conjunction with "up()".
+ *
+ */
+
+#define DOWN_VAR \
+ struct task_struct *tsk = current; \
+ wait_queue_t wait; \
+ init_waitqueue_entry(&wait, tsk);
+
+#define DOWN_HEAD(task_state) \
+ \
+ \
+ tsk->state = (task_state); \
+ add_wait_queue(&sem->wait, &wait); \
+ \
+ /* \
+ * Ok, we're set up. sem->count is known to be less than zero \
+ * so we must wait. \
+ * \
+ * We can let go the lock for purposes of waiting. \
+ * We re-acquire it after awaking so as to protect \
+ * all semaphore operations. \
+ * \
+ * If "up()" is called before we call waking_non_zero() then \
+ * we will catch it right away. If it is called later then \
+ * we will have to go through a wakeup cycle to catch it. \
+ * \
+ * Multiple waiters contend for the semaphore lock to see \
+ * who gets to gate through and who has to wait some more. \
+ */ \
+ for (;;) {
+
+#define DOWN_TAIL(task_state) \
+ tsk->state = (task_state); \
+ } \
+ tsk->state = TASK_RUNNING; \
+ remove_wait_queue(&sem->wait, &wait);
+
+void __down(struct semaphore * sem)
+{
+ DOWN_VAR
+ DOWN_HEAD(TASK_UNINTERRUPTIBLE)
+ if (waking_non_zero(sem))
+ break;
+ schedule();
+ DOWN_TAIL(TASK_UNINTERRUPTIBLE)
+}
+
+int __down_interruptible(struct semaphore * sem)
+{
+ int ret = 0;
+ DOWN_VAR
+ DOWN_HEAD(TASK_INTERRUPTIBLE)
+
+ ret = waking_non_zero_interruptible(sem, tsk);
+ if (ret)
+ {
+ if (ret == 1)
+ /* ret != 0 only if we get interrupted -arca */
+ ret = 0;
+ break;
+ }
+ schedule();
+ DOWN_TAIL(TASK_INTERRUPTIBLE)
+ return ret;
+}
+
+int __down_trylock(struct semaphore * sem)
+{
+ return waking_non_zero_trylock(sem);
+}
+
+/*
+ * RW Semaphores
+ */
+void
+__down_read(struct rw_semaphore *sem, int count)
+{
+ DOWN_VAR;
+
+ retry_down:
+ if (count < 0) {
+ /* Wait for the lock to become unbiased. Readers
+ are non-exclusive. */
+
+ /* This takes care of granting the lock. */
+ up_read(sem);
+
+ add_wait_queue(&sem->wait, &wait);
+ while (atomic_read(&sem->count) < 0) {
+ set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+ if (atomic_read(&sem->count) >= 0)
+ break;
+ schedule();
+ }
+
+ remove_wait_queue(&sem->wait, &wait);
+ tsk->state = TASK_RUNNING;
+
+ mb();
+ count = atomic_dec_return(&sem->count);
+ if (count <= 0)
+ goto retry_down;
+ } else {
+ add_wait_queue(&sem->wait, &wait);
+
+ while (1) {
+ if (test_and_clear_bit(0, &sem->granted))
+ break;
+ set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+ if ((sem->granted & 1) == 0)
+ schedule();
+ }
+
+ remove_wait_queue(&sem->wait, &wait);
+ tsk->state = TASK_RUNNING;
+ }
+}
+
+void
+__down_write(struct rw_semaphore *sem, int count)
+{
+ DOWN_VAR;
+
+ retry_down:
+ if (count + RW_LOCK_BIAS < 0) {
+ up_write(sem);
+
+ add_wait_queue_exclusive(&sem->wait, &wait);
+
+ while (atomic_read(&sem->count) < 0) {
+ set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+ if (atomic_read(&sem->count) >= RW_LOCK_BIAS)
+ break;
+ schedule();
+ }
+
+ remove_wait_queue(&sem->wait, &wait);
+ tsk->state = TASK_RUNNING;
+
+ mb();
+ count = atomic_sub_return(RW_LOCK_BIAS, &sem->count);
+ if (count != 0)
+ goto retry_down;
+ } else {
+ /* Put ourselves at the end of the list. */
+ add_wait_queue_exclusive(&sem->write_bias_wait, &wait);
+
+ while (1) {
+ if (test_and_clear_bit(1, &sem->granted))
+ break;
+ set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+ if ((sem->granted & 2) == 0)
+ schedule();
+ }
+
+ remove_wait_queue(&sem->write_bias_wait, &wait);
+ tsk->state = TASK_RUNNING;
+
+ /* If the lock is currently unbiased, awaken the sleepers.
+ FIXME: This wakes up the readers early in a bit of a
+ stampede -> bad! */
+ if (atomic_read(&sem->count) >= 0)
+ wake_up(&sem->wait);
+ }
+}
+
+void
+__rwsem_wake(struct rw_semaphore *sem, unsigned long readers)
+{
+ if (readers) {
+ if (test_and_set_bit(0, &sem->granted))
+ BUG();
+ wake_up(&sem->wait);
+ } else {
+ if (test_and_set_bit(1, &sem->granted))
+ BUG();
+ wake_up(&sem->write_bias_wait);
+ }
+}
diff --git a/arch/cris/kernel/setup.c b/arch/cris/kernel/setup.c
new file mode 100644
index 000000000..ff7b3e6ac
--- /dev/null
+++ b/arch/cris/kernel/setup.c
@@ -0,0 +1,264 @@
+/* $Id: setup.c,v 1.8 2001/01/16 16:31:38 bjornw Exp $
+ *
+ * linux/arch/cris/kernel/setup.c
+ *
+ * Copyright (C) 1995 Linus Torvalds
+ * Copyright (c) 2000 Axis Communications AB
+ */
+
+/*
+ * This file handles the architecture-dependent parts of initialization
+ */
+
+#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/malloc.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/tty.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/smp.h>
+#include <asm/types.h>
+#include <asm/svinto.h>
+
+/*
+ * Setup options
+ */
+struct drive_info_struct { char dummy[32]; } drive_info;
+struct screen_info screen_info;
+
+unsigned char aux_device_present;
+
+#ifdef CONFIG_BLK_DEV_RAM
+extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */
+extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */
+extern int rd_image_start; /* starting block # of image */
+#endif
+
+extern int root_mountflags;
+extern char _etext, _edata, _end;
+
+#define COMMAND_LINE_SIZE 256
+
+static char command_line[COMMAND_LINE_SIZE] = { 0, };
+ char saved_command_line[COMMAND_LINE_SIZE];
+
+extern const unsigned long text_start, edata; /* set by the linker script */
+
+extern unsigned long romfs_start, romfs_length, romfs_in_flash; /* from head.S */
+
+/* This mainly sets up the memory area, and can be really confusing.
+ *
+ * The physical DRAM is virtually mapped into dram_start to dram_end
+ * (usually c0000000 to c0000000 + DRAM size). The physical address is
+ * given by the macro __pa().
+ *
+ * In this DRAM, the kernel code and data is loaded, in the beginning.
+ * It really starts at c00a0000 to make room for some special pages -
+ * the start address is text_start. The kernel data ends at _end. After
+ * this the ROM filesystem is appended (if there is any).
+ *
+ * Between this address and dram_end, we have RAM pages usable to the
+ * boot code and the system.
+ *
+ */
+
+void __init setup_arch(char **cmdline_p)
+{
+ unsigned long bootmap_size;
+ unsigned long start_pfn, max_pfn;
+ unsigned long memory_start;
+ extern void console_print_etrax(const char *b);
+
+#if (defined(CONFIG_CHR_DEV_FLASH) || defined(CONFIG_BLK_DEV_FLASH))
+ /* TODO: move this into flash_init I think */
+ flash_probe();
+#endif
+
+ /* register an initial console printing routine for printk's */
+
+ init_etrax_debug();
+
+ /* we should really poll for DRAM size! */
+
+ high_memory = &dram_end;
+
+ if(romfs_in_flash || !romfs_length) {
+ /* if we have the romfs in flash, or if there is no rom filesystem,
+ * our free area starts directly after the BSS
+ */
+ memory_start = (unsigned long) &_end;
+ } else {
+ /* otherwise the free area starts after the ROM filesystem */
+ printk("ROM fs in RAM, size %d bytes\n", romfs_length);
+ memory_start = romfs_start + romfs_length;
+ }
+
+ /* process 1's initial memory region is the kernel code/data */
+
+ init_mm.start_code = (unsigned long) &text_start;
+ init_mm.end_code = (unsigned long) &_etext;
+ init_mm.end_data = (unsigned long) &_edata;
+ init_mm.brk = (unsigned long) &_end;
+
+#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT)
+#define PFN_DOWN(x) ((x) >> PAGE_SHIFT)
+#define PFN_PHYS(x) ((x) << PAGE_SHIFT)
+
+ /* min_low_pfn points to the start of DRAM, start_pfn points
+ * to the first DRAM pages after the kernel, and max_low_pfn
+ * to the end of DRAM.
+ */
+
+ /*
+ * partially used pages are not usable - thus
+ * we are rounding upwards:
+ */
+
+ start_pfn = PFN_UP(memory_start); /* usually c0000000 + kernel + romfs */
+ max_pfn = PFN_DOWN((unsigned long)high_memory); /* usually c0000000 + dram size */
+
+ /*
+ * Initialize the boot-time allocator (start, end)
+ *
+ * We give it access to all our DRAM, but we could as well just have
+ * given it a small slice. No point in doing that though, unless we
+ * have non-contiguous memory and want the boot-stuff to be in, say,
+ * the smallest area.
+ *
+ * It will put a bitmap of the allocated pages in the beginning
+ * of the range we give it, but it won't mark the bitmaps pages
+ * as reserved. We have to do that ourselves below.
+ *
+ * We need to use init_bootmem_node instead of init_bootmem
+ * because our map starts at a quite high address (min_low_pfn).
+ */
+
+ max_low_pfn = max_pfn;
+ min_low_pfn = PAGE_OFFSET >> PAGE_SHIFT;
+
+ bootmap_size = init_bootmem_node(NODE_DATA(0), start_pfn,
+ min_low_pfn,
+ max_low_pfn);
+
+ /* And free all memory not belonging to the kernel (addr, size) */
+
+ free_bootmem(PFN_PHYS(start_pfn), PFN_PHYS(max_pfn - start_pfn));
+
+ /*
+ * Reserve the bootmem bitmap itself as well. We do this in two
+ * steps (first step was init_bootmem()) because this catches
+ * the (very unlikely) case of us accidentally initializing the
+ * bootmem allocator with an invalid RAM area.
+ *
+ * Arguments are start, size
+ */
+
+ reserve_bootmem(PFN_PHYS(start_pfn), bootmap_size);
+
+ /* paging_init() sets up the MMU and marks all pages as reserved */
+
+ paging_init();
+
+ /* we dont use a command line yet, so just let it be an empty string */
+
+ *cmdline_p = command_line;
+ strcpy(command_line, "root=/dev/rom"); /* use the appended romdisk as root */
+
+ /* give credit for the CRIS port */
+
+ printk("Linux/CRIS port on ETRAX 100LX (c) 2000 Axis Communications AB\n");
+
+}
+
+#ifdef CONFIG_PROC_FS
+#define HAS_FPU 0x0001
+#define HAS_MMU 0x0002
+#define HAS_ETHERNET100 0x0004
+#define HAS_TOKENRING 0x0008
+#define HAS_SCSI 0x0010
+#define HAS_ATA 0x0020
+#define HAS_USB 0x0040
+#define HAS_IRQ_BUG 0x0080
+
+static struct cpu_info {
+ char *model;
+ unsigned short cache;
+ unsigned short flags;
+} cpu_info[] = {
+ { "ETRAX 1", 0, 0 },
+ { "ETRAX 2", 0, 0 }, /* Don't say it HAS_TOKENRING - there are
+ lethal bugs in that chip that
+ prevents T-R from ever working.
+ Never go there, and never lead anyone
+ into believing it can work. BTW:
+ Anyone working on a T-R network
+ driver? :-) :-) :-) :-/ */
+ { "ETRAX 3", 0, HAS_TOKENRING },
+ { "ETRAX 4", 0, HAS_TOKENRING | HAS_SCSI },
+ { "Unknown", 0, 0 },
+ { "Unknown", 0, 0 },
+ { "Unknown", 0, 0 },
+ { "Simulator", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA },
+ { "ETRAX 100", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_IRQ_BUG },
+ { "ETRAX 100", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA },
+ { "ETRAX 100LX", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB | HAS_MMU },
+ { "Unknown", 0, 0 },
+};
+
+/*
+ * BUFFER is PAGE_SIZE bytes long.
+ */
+int get_cpuinfo(char *buffer)
+{
+ int revision;
+#ifndef CONFIG_SVINTO_SIM
+ unsigned char tmp;
+
+ __asm__ volatile ("move vr,%0" : "=rm" (tmp));
+ revision = tmp;
+#else
+ /* Fake a revision for the simulator */
+ revision = 7;
+#endif
+
+ return sprintf(buffer,
+ "cpu\t\t: CRIS\n"
+ "cpu revision\t: %d\n"
+ "cpu model\t: %s\n"
+ "cache size\t: %d kB\n"
+ "fpu\t\t: %s\n"
+ "mmu\t\t: %s\n"
+ "ethernet\t: %s Mbps\n"
+ "token ring\t: %s\n"
+ "scsi\t\t: %s\n"
+ "ata\t\t: %s\n"
+ "usb\t\t: %s\n"
+ "bogomips\t: %lu.%02lu\n",
+
+ revision,
+ cpu_info[revision].model,
+ cpu_info[revision].cache,
+ cpu_info[revision].flags & HAS_FPU ? "yes" : "no",
+ cpu_info[revision].flags & HAS_MMU ? "yes" : "no",
+ cpu_info[revision].flags & HAS_ETHERNET100 ? "10/100" : "10",
+ cpu_info[revision].flags & HAS_TOKENRING ? "4/16 Mbps" : "no",
+ cpu_info[revision].flags & HAS_SCSI ? "yes" : "no",
+ cpu_info[revision].flags & HAS_ATA ? "yes" : "no",
+ cpu_info[revision].flags & HAS_USB ? "yes" : "no",
+ (loops_per_jiffy * HZ + 500) / 100000,
+ ((loops_per_jiffy * HZ + 500) / 1000) % 100);
+}
+#endif /* CONFIG_PROC_FS */
diff --git a/arch/cris/kernel/shadows.c b/arch/cris/kernel/shadows.c
new file mode 100644
index 000000000..0a6449f4c
--- /dev/null
+++ b/arch/cris/kernel/shadows.c
@@ -0,0 +1,20 @@
+/* $Id: shadows.c,v 1.1 2000/07/10 16:25:21 bjornw Exp $
+ *
+ * Various Etrax shadow registers. Defines for these are in include/asm-etrax100/io.h
+ */
+
+#include <linux/config.h>
+
+unsigned long genconfig_shadow = 42;
+unsigned long port_g_data_shadow = 42;
+unsigned char port_pa_dir_shadow = 42;
+unsigned char port_pa_data_shadow = 42;
+unsigned char port_pb_i2c_shadow = 42;
+unsigned char port_pb_config_shadow = 42;
+unsigned char port_pb_dir_shadow = 42;
+unsigned char port_pb_data_shadow = 42;
+unsigned long r_timer_ctrl_shadow = 42;
+
+#ifdef CONFIG_ETRAX_90000000_LEDS
+unsigned long port_90000000_shadow = 42;
+#endif
diff --git a/arch/cris/kernel/signal.c b/arch/cris/kernel/signal.c
new file mode 100644
index 000000000..8aab31a45
--- /dev/null
+++ b/arch/cris/kernel/signal.c
@@ -0,0 +1,667 @@
+/*
+ * linux/arch/cris/kernel/signal.c
+ *
+ * Based on arch/i386/kernel/signal.c by
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson *
+ *
+ * Ideas also taken from arch/arm.
+ *
+ * Copyright (C) 2000 Axis Communications AB
+ *
+ * Authors: Bjorn Wesen (bjornw@axis.com)
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.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 <asm/processor.h>
+#include <asm/ucontext.h>
+#include <asm/uaccess.h>
+
+#define DEBUG_SIG 0
+
+#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
+
+/* a syscall in Linux/CRIS is a break 13 instruction which is 2 bytes */
+/* manipulate regs so that upon return, it will be re-executed */
+
+#define RESTART_CRIS_SYS(regs) regs->r10 = regs->orig_r10; regs->irp -= 2;
+
+int sys_wait4(pid_t pid, unsigned long *stat_addr,
+ int options, unsigned long *ru);
+
+int do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs);
+
+int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from)
+{
+ if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t)))
+ return -EFAULT;
+ if (from->si_code < 0)
+ return __copy_to_user(to, from, sizeof(siginfo_t));
+ else {
+ int err;
+
+ /* If you change siginfo_t structure, please be sure
+ this code is fixed accordingly.
+ It should never copy any pad contained in the structure
+ to avoid security leaks, but must copy the generic
+ 3 ints plus the relevant union member. */
+ err = __put_user(from->si_signo, &to->si_signo);
+ err |= __put_user(from->si_errno, &to->si_errno);
+ err |= __put_user((short)from->si_code, &to->si_code);
+ /* First 32bits of unions are always present. */
+ err |= __put_user(from->si_pid, &to->si_pid);
+ switch (from->si_code >> 16) {
+ case __SI_FAULT >> 16:
+ break;
+ case __SI_CHLD >> 16:
+ err |= __put_user(from->si_utime, &to->si_utime);
+ err |= __put_user(from->si_stime, &to->si_stime);
+ err |= __put_user(from->si_status, &to->si_status);
+ default:
+ err |= __put_user(from->si_uid, &to->si_uid);
+ break;
+ /* case __SI_RT: This is not generated by the kernel as of now. */
+ }
+ return err;
+ }
+}
+
+/*
+ * Atomically swap in the new signal mask, and wait for a signal.
+ */
+int
+sys_sigsuspend(old_sigset_t mask)
+{
+ struct pt_regs * regs = (struct pt_regs *)current_regs();
+ sigset_t saveset;
+
+ mask &= _BLOCKABLE;
+ spin_lock_irq(&current->sigmask_lock);
+ saveset = current->blocked;
+ siginitset(&current->blocked, mask);
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ regs->r10 = -EINTR;
+ while (1) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ if (do_signal(0, &saveset, regs))
+ return -EINTR;
+ }
+}
+
+int
+sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize)
+{
+ struct pt_regs * regs = (struct pt_regs *)current_regs();
+ sigset_t saveset, newset;
+
+ /* XXX: Don't preclude handling different sized sigset_t's. */
+ if (sigsetsize != sizeof(sigset_t))
+ return -EINVAL;
+
+ if (copy_from_user(&newset, unewset, sizeof(newset)))
+ return -EFAULT;
+ sigdelsetmask(&newset, ~_BLOCKABLE);
+
+ spin_lock_irq(&current->sigmask_lock);
+ saveset = current->blocked;
+ current->blocked = newset;
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ regs->r10 = -EINTR;
+ while (1) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ if (do_signal(0, &saveset, regs))
+ return -EINTR;
+ }
+}
+
+int
+sys_sigaction(int sig, const struct old_sigaction *act,
+ struct old_sigaction *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))
+ return -EFAULT;
+ __get_user(new_ka.sa.sa_flags, &act->sa_flags);
+ __get_user(mask, &act->sa_mask);
+ 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))
+ return -EFAULT;
+ __put_user(old_ka.sa.sa_flags, &oact->sa_flags);
+ __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
+ }
+
+ return ret;
+}
+
+int
+sys_sigaltstack(const stack_t *uss, stack_t *uoss)
+{
+ return do_sigaltstack(uss, uoss, rdusp());
+}
+
+
+/*
+ * Do a signal return; undo the signal stack.
+ */
+
+struct sigframe {
+ struct sigcontext sc;
+ unsigned long extramask[_NSIG_WORDS-1];
+ unsigned char retcode[8]; /* trampoline code */
+};
+
+struct rt_sigframe {
+ struct siginfo *pinfo;
+ void *puc;
+ struct siginfo info;
+ struct ucontext uc;
+ unsigned char retcode[8]; /* trampoline code */
+};
+
+
+static int
+restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc)
+{
+ unsigned int err = 0;
+ unsigned long old_usp;
+
+ /* restore the regs from &sc->regs (same as sc, since regs is first)
+ * (sc is already checked for VERIFY_READ since the sigframe was
+ * checked in sys_sigreturn previously)
+ */
+
+ if (__copy_from_user(regs, sc, sizeof(struct pt_regs)))
+ goto badframe;
+
+ /* make sure the U-flag is set so user-mode cannot fool us */
+
+ regs->dccr |= 1 << 8;
+
+ /* restore the old USP as it was before we stacked the sc etc.
+ * (we cannot just pop the sigcontext since we aligned the sp and
+ * stuff after pushing it)
+ */
+
+ err |= __get_user(old_usp, &sc->usp);
+
+ wrusp(old_usp);
+
+ /* TODO: the other ports use regs->orig_XX to disable syscall checks
+ * after this completes, but we don't use that mechanism. maybe we can
+ * use it now ?
+ */
+
+ return err;
+
+badframe:
+ return 1;
+}
+
+asmlinkage int sys_sigreturn(void)
+{
+ struct pt_regs *regs = (struct pt_regs *)current_regs();
+ struct sigframe *frame = (struct sigframe *)rdusp();
+ sigset_t set;
+
+ /*
+ * Since we stacked the signal on a dword boundary,
+ * then frame should be dword aligned here. If it's
+ * not, then the user is trying to mess with us.
+ */
+ if (((long)frame) & 3)
+ goto badframe;
+
+ if (verify_area(VERIFY_READ, frame, sizeof(*frame)))
+ goto badframe;
+ if (__get_user(set.sig[0], &frame->sc.oldmask)
+ || (_NSIG_WORDS > 1
+ && __copy_from_user(&set.sig[1], &frame->extramask,
+ sizeof(frame->extramask))))
+ goto badframe;
+
+ sigdelsetmask(&set, ~_BLOCKABLE);
+ spin_lock_irq(&current->sigmask_lock);
+ current->blocked = set;
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ if (restore_sigcontext(regs, &frame->sc))
+ goto badframe;
+
+ /* TODO: SIGTRAP when single-stepping as in arm ? */
+
+ return regs->r10;
+
+badframe:
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+asmlinkage int sys_rt_sigreturn(void)
+{
+ struct pt_regs *regs = (struct pt_regs *)current_regs();
+ struct rt_sigframe *frame = (struct rt_sigframe *)rdusp();
+ sigset_t set;
+ stack_t st;
+
+ /*
+ * Since we stacked the signal on a dword boundary,
+ * then frame should be dword aligned here. If it's
+ * not, then the user is trying to mess with us.
+ */
+ if (((long)frame) & 3)
+ goto badframe;
+
+ 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(&current->sigmask_lock);
+ current->blocked = set;
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
+ goto badframe;
+
+ if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st)))
+ goto badframe;
+ /* It is more difficult to avoid calling this function than to
+ call it and ignore errors. */
+ do_sigaltstack(&st, NULL, rdusp());
+
+ return regs->r10;
+
+badframe:
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+/*
+ * Set up a signal frame.
+ */
+
+static int
+setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs, unsigned long mask)
+{
+ int err = 0;
+ unsigned long usp = rdusp();
+
+ /* copy the regs. they are first in sc so we can use sc directly */
+
+ err |= __copy_to_user(sc, regs, sizeof(struct pt_regs));
+
+ /* then some other stuff */
+
+ err |= __put_user(mask, &sc->oldmask);
+
+ err |= __put_user(usp, &sc->usp);
+
+ return err;
+}
+
+/* figure out where we want to put the new signal frame - usually on the stack */
+
+static inline void *
+get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size)
+{
+ unsigned long sp = rdusp();
+
+ /* This is the X/Open sanctioned signal stack switching. */
+ if (ka->sa.sa_flags & SA_ONSTACK) {
+ if (! on_sig_stack(sp))
+ sp = current->sas_ss_sp + current->sas_ss_size;
+ }
+
+ /* make sure the frame is dword-aligned */
+
+ sp &= ~3;
+
+ return (void *)(sp - frame_size);
+}
+
+/* grab and setup a signal frame.
+ *
+ * basically we stack a lot of state info, and arrange for the
+ * user-mode program to return to the kernel using either a
+ * trampoline which performs the syscall sigreturn, or a provided
+ * user-mode trampoline.
+ */
+
+static void setup_frame(int sig, struct k_sigaction *ka,
+ sigset_t *set, struct pt_regs * regs)
+{
+ struct sigframe *frame;
+ unsigned long return_ip;
+ int err = 0;
+
+ frame = get_sigframe(ka, regs, sizeof(*frame));
+
+ if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+ goto give_sigsegv;
+
+ err |= setup_sigcontext(&frame->sc, regs, set->sig[0]);
+ if (err)
+ goto give_sigsegv;
+
+ if (_NSIG_WORDS > 1) {
+ err |= __copy_to_user(frame->extramask, &set->sig[1],
+ sizeof(frame->extramask));
+ }
+ if (err)
+ goto give_sigsegv;
+
+ /* Set up to return from userspace. If provided, use a stub
+ already in userspace. */
+ if (ka->sa.sa_flags & SA_RESTORER) {
+ return_ip = (unsigned long)ka->sa.sa_restorer;
+ } else {
+ /* trampoline - the desired return ip is the retcode itself */
+ return_ip = (unsigned long)&frame->retcode;
+ /* This is movu.w __NR_sigreturn, r1; break 13; */
+ /* TODO: check byteorder */
+ err |= __put_user(0x1c5f, (short *)(frame->retcode+0));
+ err |= __put_user(__NR_sigreturn, (short *)(frame->retcode+2));
+ err |= __put_user(0xe93d, (short *)(frame->retcode+4));
+ }
+
+ if (err)
+ goto give_sigsegv;
+
+ /* Set up registers for signal handler */
+
+ regs->irp = (unsigned long) ka->sa.sa_handler; /* what we enter NOW */
+ regs->srp = return_ip; /* what we enter LATER */
+
+ /* actually move the usp to reflect the stacked frame */
+
+ wrusp((unsigned long)frame);
+
+ return;
+
+give_sigsegv:
+ if (sig == SIGSEGV)
+ ka->sa.sa_handler = SIG_DFL;
+ force_sig(SIGSEGV, current);
+}
+
+static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
+ sigset_t *set, struct pt_regs * regs)
+{
+ struct rt_sigframe *frame;
+ unsigned long return_ip;
+ int err = 0;
+
+ frame = get_sigframe(ka, regs, sizeof(*frame));
+
+ if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+ goto give_sigsegv;
+
+ err |= __put_user(&frame->info, &frame->pinfo);
+ err |= __put_user(&frame->uc, &frame->puc);
+ err |= copy_siginfo_to_user(&frame->info, info);
+ if (err)
+ goto give_sigsegv;
+
+ /* Clear all the bits of the ucontext we don't use. */
+ err |= __clear_user(&frame->uc, offsetof(struct ucontext, uc_mcontext));
+
+ err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, set->sig[0]);
+
+ err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
+
+ if (err)
+ goto give_sigsegv;
+
+ /* Set up to return from userspace. If provided, use a stub
+ already in userspace. */
+ if (ka->sa.sa_flags & SA_RESTORER) {
+ return_ip = (unsigned long)ka->sa.sa_restorer;
+ } else {
+ /* trampoline - the desired return ip is the retcode itself */
+ return_ip = (unsigned long)&frame->retcode;
+ /* This is movu.w __NR_sigreturn, r1; break 13; */
+ /* TODO: check byteorder */
+ err |= __put_user(0x1c5f, (short *)(frame->retcode+0));
+ err |= __put_user(__NR_sigreturn, (short *)(frame->retcode+2));
+ err |= __put_user(0xe93d, (short *)(frame->retcode+4));
+ }
+
+ if (err)
+ goto give_sigsegv;
+
+ /* TODO what is the current->exec_domain stuff and invmap ? */
+
+ /* Set up registers for signal handler */
+
+ regs->irp = (unsigned long) ka->sa.sa_handler; /* what we enter NOW */
+ regs->srp = return_ip; /* what we enter LATER */
+
+ /* actually move the usp to reflect the stacked frame */
+
+ wrusp((unsigned long)frame);
+
+ return;
+
+give_sigsegv:
+ if (sig == SIGSEGV)
+ ka->sa.sa_handler = SIG_DFL;
+ force_sig(SIGSEGV, current);
+}
+
+/*
+ * OK, we're invoking a handler
+ */
+
+static inline void
+handle_signal(int canrestart, unsigned long sig, struct k_sigaction *ka,
+ siginfo_t *info, sigset_t *oldset, struct pt_regs * regs)
+{
+ /* Are we from a system call? */
+ if (canrestart) {
+ /* If so, check system call restarting.. */
+ switch (regs->r10) {
+ case -ERESTARTNOHAND:
+ /* ERESTARTNOHAND means that the syscall should only be
+ restarted if there was no handler for the signal, and since
+ we only get here if there is a handler, we dont restart */
+ regs->r10 = -EINTR;
+ break;
+
+ case -ERESTARTSYS:
+ /* ERESTARTSYS means to restart the syscall if there is no
+ handler or the handler was registered with SA_RESTART */
+ if (!(ka->sa.sa_flags & SA_RESTART)) {
+ regs->r10 = -EINTR;
+ break;
+ }
+ /* fallthrough */
+ case -ERESTARTNOINTR:
+ /* ERESTARTNOINTR means that the syscall should be called again
+ after the signal handler returns. */
+ RESTART_CRIS_SYS(regs);
+ }
+ }
+
+ /* Set up the stack frame */
+ if (ka->sa.sa_flags & SA_SIGINFO)
+ setup_rt_frame(sig, ka, info, oldset, regs);
+ else
+ setup_frame(sig, ka, oldset, regs);
+
+ if (ka->sa.sa_flags & SA_ONESHOT)
+ ka->sa.sa_handler = SIG_DFL;
+
+ if (!(ka->sa.sa_flags & SA_NODEFER)) {
+ spin_lock_irq(&current->sigmask_lock);
+ sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
+ sigaddset(&current->blocked,sig);
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+ }
+}
+
+/*
+ * Note that 'init' is a special process: it doesn't get signals it doesn't
+ * want to handle. Thus you cannot kill init even with a SIGKILL even by
+ * mistake.
+ */
+int do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs)
+{
+ siginfo_t info;
+ struct k_sigaction *ka;
+
+ /*
+ * We want the common case to go fast, which
+ * is why we may in certain cases get here from
+ * kernel mode. Just return without doing anything
+ * if so.
+ */
+ if (!user_mode(regs))
+ return 1;
+
+ if (!oldset)
+ oldset = &current->blocked;
+
+ for (;;) {
+ unsigned long signr;
+
+ spin_lock_irq(&current->sigmask_lock);
+ signr = dequeue_signal(&current->blocked, &info);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ if (!signr)
+ break;
+
+ if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) {
+ /* Let the debugger run. */
+ current->exit_code = signr;
+ current->state = TASK_STOPPED;
+ notify_parent(current, SIGCHLD);
+ schedule();
+
+ /* We're back. Did the debugger cancel the sig? */
+ if (!(signr = current->exit_code))
+ continue;
+ current->exit_code = 0;
+
+ /* The debugger continued. Ignore SIGSTOP. */
+ if (signr == SIGSTOP)
+ continue;
+
+ /* Update the siginfo structure. Is this good? */
+ if (signr != info.si_signo) {
+ info.si_signo = signr;
+ info.si_errno = 0;
+ info.si_code = SI_USER;
+ info.si_pid = current->p_pptr->pid;
+ info.si_uid = current->p_pptr->uid;
+ }
+
+ /* If the (new) signal is now blocked, requeue it. */
+ if (sigismember(&current->blocked, signr)) {
+ send_sig_info(signr, &info, current);
+ continue;
+ }
+ }
+
+ ka = &current->sig->action[signr-1];
+ if (ka->sa.sa_handler == SIG_IGN) {
+ if (signr != SIGCHLD)
+ continue;
+ /* Check for SIGCHLD: it's special. */
+ while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0)
+ /* nothing */;
+ continue;
+ }
+
+ if (ka->sa.sa_handler == SIG_DFL) {
+ int exit_code = signr;
+
+ /* Init gets no signals it doesn't want. */
+ if (current->pid == 1)
+ continue;
+
+ switch (signr) {
+ case SIGCONT: case SIGCHLD: case SIGWINCH:
+ continue;
+
+ case SIGTSTP: case SIGTTIN: case SIGTTOU:
+ if (is_orphaned_pgrp(current->pgrp))
+ continue;
+ /* FALLTHRU */
+
+ case SIGSTOP:
+ current->state = TASK_STOPPED;
+ current->exit_code = signr;
+ if (!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP))
+ notify_parent(current, SIGCHLD);
+ schedule();
+ continue;
+
+ case SIGQUIT: case SIGILL: case SIGTRAP:
+ case SIGABRT: case SIGFPE: case SIGSEGV:
+ case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ:
+ if (do_coredump(signr, regs))
+ exit_code |= 0x80;
+ /* FALLTHRU */
+
+ default:
+ lock_kernel();
+ sigaddset(&current->pending.signal, signr);
+ recalc_sigpending(current);
+ current->flags |= PF_SIGNALED;
+ do_exit(exit_code);
+ /* NOTREACHED */
+ }
+ }
+
+ /* Whee! Actually deliver the signal. */
+ handle_signal(canrestart, signr, ka, &info, oldset, regs);
+ return 1;
+ }
+
+ /* Did we come from a system call? */
+ if (canrestart) {
+ /* Restart the system call - no handlers present */
+ if (regs->r10 == -ERESTARTNOHAND ||
+ regs->r10 == -ERESTARTSYS ||
+ regs->r10 == -ERESTARTNOINTR) {
+ RESTART_CRIS_SYS(regs);
+ }
+ }
+ return 0;
+}
diff --git a/arch/cris/kernel/sys_cris.c b/arch/cris/kernel/sys_cris.c
new file mode 100644
index 000000000..cfcb097f9
--- /dev/null
+++ b/arch/cris/kernel/sys_cris.c
@@ -0,0 +1,201 @@
+/* $Id: sys_cris.c,v 1.3 2000/08/02 13:59:02 bjornw Exp $
+ *
+ * linux/arch/cris/kernel/sys_etrax.c
+ *
+ * This file contains various random system calls that
+ * have a non-standard calling sequence on some platforms.
+ * Since we don't have to do any backwards compatibility, our
+ * versions are done in the most "normal" way possible.
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.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 <asm/uaccess.h>
+#include <asm/ipc.h>
+#include <asm/segment.h>
+
+/*
+ * sys_pipe() is the normal C calling standard for creating
+ * a pipe. It's not the way Unix traditionally does this, though.
+ */
+asmlinkage int sys_pipe(unsigned long * fildes)
+{
+ int fd[2];
+ int error;
+
+ lock_kernel();
+ error = do_pipe(fd);
+ unlock_kernel();
+ if (!error) {
+ if (copy_to_user(fildes, fd, 2*sizeof(int)))
+ error = -EFAULT;
+ }
+ return error;
+}
+
+/* sys_mmap used to take a ptr to a buffer instead containing the args
+ * but we support syscalls with 6 arguments now
+ */
+
+asmlinkage unsigned long sys_mmap(unsigned long addr, size_t len,
+ unsigned long prot, unsigned long flags,
+ unsigned long fd, off_t offset)
+{
+ struct file * file = NULL;
+ int ret = -EBADF;
+
+ lock_kernel();
+ if (!(flags & MAP_ANONYMOUS)) {
+ if (!(file = fget(fd)))
+ goto out;
+ }
+
+ flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
+ down(&current->mm->mmap_sem);
+ ret = do_mmap(file, addr, len, prot, flags, offset);
+ up(&current->mm->mmap_sem);
+ if (file)
+ fput(file);
+ out:
+ unlock_kernel();
+ return ret;
+}
+
+/* common code for old and new mmaps */
+static inline long
+do_mmap2(unsigned long addr, unsigned long len, unsigned long prot,
+ unsigned long flags, unsigned long fd, unsigned long pgoff)
+{
+ int error = -EBADF;
+ struct file * file = NULL;
+
+ flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
+ if (!(flags & MAP_ANONYMOUS)) {
+ file = fget(fd);
+ if (!file)
+ goto out;
+ }
+
+ down(&current->mm->mmap_sem);
+ error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
+ up(&current->mm->mmap_sem);
+
+ if (file)
+ fput(file);
+out:
+ return error;
+}
+
+asmlinkage unsigned long old_mmap(unsigned long addr, size_t len, int prot,
+ int flags, int fd, off_t offset)
+{
+ return do_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT);
+}
+
+asmlinkage long
+sys_mmap2(unsigned long addr, unsigned long len, unsigned long prot,
+ unsigned long flags, unsigned long fd, unsigned long pgoff)
+{
+ return do_mmap2(addr, len, prot, flags, fd, pgoff);
+}
+
+/*
+ * sys_ipc() is the de-multiplexer for the SysV IPC calls..
+ *
+ * This is really horribly ugly. (same as arch/i386)
+ */
+
+asmlinkage int sys_ipc (uint call, int first, int second,
+ int third, void *ptr, long fifth)
+{
+ int version, ret;
+
+ version = call >> 16; /* hack for backward compatibility */
+ call &= 0xffff;
+
+ switch (call) {
+ case SEMOP:
+ return sys_semop (first, (struct sembuf *)ptr, second);
+ case SEMGET:
+ return sys_semget (first, second, third);
+ case SEMCTL: {
+ union semun fourth;
+ if (!ptr)
+ return -EINVAL;
+ if (get_user(fourth.__pad, (void **) ptr))
+ return -EFAULT;
+ return sys_semctl (first, second, third, fourth);
+ }
+
+ case MSGSND:
+ return sys_msgsnd (first, (struct msgbuf *) ptr,
+ second, third);
+ case MSGRCV:
+ switch (version) {
+ case 0: {
+ struct ipc_kludge tmp;
+ if (!ptr)
+ return -EINVAL;
+
+ if (copy_from_user(&tmp,
+ (struct ipc_kludge *) ptr,
+ sizeof (tmp)))
+ return -EFAULT;
+ return sys_msgrcv (first, tmp.msgp, second,
+ tmp.msgtyp, third);
+ }
+ default:
+ return sys_msgrcv (first,
+ (struct msgbuf *) ptr,
+ second, fifth, third);
+ }
+ case MSGGET:
+ return sys_msgget ((key_t) first, second);
+ case MSGCTL:
+ return sys_msgctl (first, second, (struct msqid_ds *) ptr);
+
+ case SHMAT:
+ switch (version) {
+ default: {
+ ulong raddr;
+ ret = sys_shmat (first, (char *) ptr, second, &raddr);
+ if (ret)
+ return ret;
+ return put_user (raddr, (ulong *) third);
+ }
+ case 1: /* iBCS2 emulator entry point */
+ if (!segment_eq(get_fs(), get_ds()))
+ return -EINVAL;
+ return sys_shmat (first, (char *) ptr, second, (ulong *) third);
+ }
+ case SHMDT:
+ return sys_shmdt ((char *)ptr);
+ case SHMGET:
+ return sys_shmget (first, second, third);
+ case SHMCTL:
+ return sys_shmctl (first, second,
+ (struct shmid_ds *) ptr);
+ default:
+ return -EINVAL;
+ }
+}
+
+/* apparently this is legacy - if we don't need this in Linux/CRIS we can remove it. */
+
+asmlinkage int sys_pause(void)
+{
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ return -ERESTARTNOHAND;
+}
diff --git a/arch/cris/kernel/time.c b/arch/cris/kernel/time.c
new file mode 100644
index 000000000..d46ed47fa
--- /dev/null
+++ b/arch/cris/kernel/time.c
@@ -0,0 +1,453 @@
+/* $Id: time.c,v 1.4 2000/10/17 14:44:58 bjornw Exp $
+ *
+ * linux/arch/cris/kernel/time.c
+ *
+ * Copyright (C) 1991, 1992, 1995 Linus Torvalds
+ * Copyright (C) 1999, 2000 Axis Communications AB
+ *
+ * 1994-07-02 Alan Modra
+ * fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime
+ * 1995-03-26 Markus Kuhn
+ * fixed 500 ms bug at call to set_rtc_mmss, fixed DS12887
+ * precision CMOS clock update
+ * 1996-05-03 Ingo Molnar
+ * fixed time warps in do_[slow|fast]_gettimeoffset()
+ * 1997-09-10 Updated NTP code according to technical memorandum Jan '96
+ * "A Kernel Model for Precision Timekeeping" by Dave Mills
+ *
+ * Linux/CRIS specific code:
+ *
+ * Authors: Bjorn Wesen
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/delay.h>
+#include <asm/rtc.h>
+
+#include <linux/timex.h>
+#include <linux/config.h>
+
+#include <asm/svinto.h>
+
+static int have_rtc; /* used to remember if we have an RTC or not */
+
+/* define this if you need to use print_timestamp */
+/* it will make jiffies at 96 hz instead of 100 hz though */
+#undef USE_CASCADE_TIMERS
+
+extern int setup_etrax_irq(int, struct irqaction *);
+
+#define TICK_SIZE tick
+
+static unsigned long do_slow_gettimeoffset(void)
+{
+ unsigned long count;
+
+ static unsigned long count_p = LATCH; /* for the first call after boot */
+ static unsigned long jiffies_p = 0;
+
+ /*
+ * cache volatile jiffies temporarily; we have IRQs turned off.
+ */
+ unsigned long jiffies_t;
+
+ /* The timer interrupt comes from Etrax timer 0. In order to get
+ * better precision, we check the current value. It might have
+ * underflowed already though.
+ */
+
+#ifndef CONFIG_SVINTO_SIM
+ /* Not available in the xsim simulator. */
+ count = *R_TIMER0_DATA;
+#else
+ count = 0;
+#endif
+
+ jiffies_t = jiffies;
+
+ /*
+ * avoiding timer inconsistencies (they are rare, but they happen)...
+ * there are three kinds of problems that must be avoided here:
+ * 1. the timer counter underflows
+ * 2. hardware problem with the timer, not giving us continuous time,
+ * the counter does small "jumps" upwards on some Pentium systems,
+ * thus causes time warps
+ * 3. we are after the timer interrupt, but the bottom half handler
+ * hasn't executed yet.
+ */
+ if( jiffies_t == jiffies_p ) {
+ if( count > count_p ) {
+ }
+ } else
+ jiffies_p = jiffies_t;
+
+ count_p = count;
+
+ count = ((LATCH-1) - count) * TICK_SIZE;
+ count = (count + LATCH/2) / LATCH;
+
+ return count;
+}
+
+static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset;
+
+/*
+ * This version of gettimeofday has near microsecond resolution.
+ */
+void do_gettimeofday(struct timeval *tv)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ *tv = xtime;
+ tv->tv_usec += do_gettimeoffset();
+ if (tv->tv_usec >= 1000000) {
+ tv->tv_usec -= 1000000;
+ tv->tv_sec++;
+ }
+ restore_flags(flags);
+}
+
+void do_settimeofday(struct timeval *tv)
+{
+ cli();
+ /* This is revolting. We need to set the xtime.tv_usec
+ * correctly. However, the value in this location is
+ * is value at the last tick.
+ * Discover what correction gettimeofday
+ * would have done, and then undo it!
+ */
+ tv->tv_usec -= do_gettimeoffset();
+
+ if (tv->tv_usec < 0) {
+ tv->tv_usec += 1000000;
+ tv->tv_sec--;
+ }
+
+ xtime = *tv;
+ time_adjust = 0; /* stop active adjtime() */
+ time_status |= STA_UNSYNC;
+ time_state = TIME_ERROR; /* p. 24, (a) */
+ time_maxerror = NTP_PHASE_LIMIT;
+ time_esterror = NTP_PHASE_LIMIT;
+ sti();
+}
+
+
+/*
+ * 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)
+{
+ int retval = 0;
+ int real_seconds, real_minutes, cmos_minutes;
+ unsigned char save_control, save_freq_select;
+
+ printk("set_rtc_mmss(%d)\n", nowtime);
+
+ if(!have_rtc)
+ return 0;
+
+ cmos_minutes = CMOS_READ(RTC_MINUTES);
+ BCD_TO_BIN(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)
+ real_minutes += 30; /* correct for half hour time zone */
+ real_minutes %= 60;
+
+ if (abs(real_minutes - cmos_minutes) < 30) {
+ BIN_TO_BCD(real_seconds);
+ BIN_TO_BCD(real_minutes);
+ CMOS_WRITE(real_seconds,RTC_SECONDS);
+ CMOS_WRITE(real_minutes,RTC_MINUTES);
+ } else {
+ printk(KERN_WARNING
+ "set_rtc_mmss: can't update from %d to %d\n",
+ cmos_minutes, real_minutes);
+ retval = -1;
+ }
+
+ return retval;
+}
+
+/* Except from the Etrax100 HSDD about the built-in watchdog:
+ *
+ * 3.10.4 Watchdog timer
+
+ * When the watchdog timer is started, it generates an NMI if the watchdog
+ * isn't restarted or stopped within 0.1 s. If it still isn't restarted or
+ * stopped after an additional 3.3 ms, the watchdog resets the chip.
+ * The watchdog timer is stopped after reset. The watchdog timer is controlled
+ * by the R_WATCHDOG register. The R_WATCHDOG register contains an enable bit
+ * and a 3-bit key value. The effect of writing to the R_WATCHDOG register is
+ * described in the table below:
+ *
+ * Watchdog Value written:
+ * state: To enable: To key: Operation:
+ * -------- ---------- ------- ----------
+ * stopped 0 X No effect.
+ * stopped 1 key_val Start watchdog with key = key_val.
+ * started 0 ~key Stop watchdog
+ * started 1 ~key Restart watchdog with key = ~key.
+ * started X new_key_val Change key to new_key_val.
+ *
+ * Note: '~' is the bitwise NOT operator.
+ *
+ */
+
+/* right now, starting the watchdog is the same as resetting it */
+#define start_watchdog reset_watchdog
+
+static int watchdog_key = 0; /* arbitrary number */
+
+/* number of pages to consider "out of memory". it is normal that the memory
+ * is used though, so put this really low.
+ */
+
+#define WATCHDOG_MIN_FREE_PAGES 8
+
+extern int nr_free_pages;
+
+static inline void
+reset_watchdog(void)
+{
+#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
+ /* only keep watchdog happy as long as we have memory left! */
+ if(nr_free_pages > WATCHDOG_MIN_FREE_PAGES) {
+ /* reset the watchdog with the inverse of the old key */
+ watchdog_key ^= 0x7; /* invert key, which is 3 bits */
+ *R_WATCHDOG = IO_FIELD(R_WATCHDOG, key, watchdog_key) |
+ IO_STATE(R_WATCHDOG, enable, start);
+ }
+#endif
+}
+
+/* last time the cmos clock got updated */
+static long last_rtc_update = 0;
+
+/*
+ * timer_interrupt() needs to keep up the real-time clock,
+ * as well as call the "do_timer()" routine every clocktick
+ */
+
+//static unsigned short myjiff; /* used by our debug routine print_timestamp */
+
+static inline void
+timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ /* acknowledge the timer irq */
+
+#ifdef USE_CASCADE_TIMERS
+ *R_TIMER_CTRL =
+ IO_FIELD( R_TIMER_CTRL, timerdiv1, 0) |
+ IO_FIELD( R_TIMER_CTRL, timerdiv0, 0) |
+ IO_STATE( R_TIMER_CTRL, i1, clr) |
+ IO_STATE( R_TIMER_CTRL, tm1, run) |
+ IO_STATE( R_TIMER_CTRL, clksel1, cascade0) |
+ IO_STATE( R_TIMER_CTRL, i0, clr) |
+ IO_STATE( R_TIMER_CTRL, tm0, run) |
+ IO_STATE( R_TIMER_CTRL, clksel0, c6250kHz);
+#else
+ *R_TIMER_CTRL = r_timer_ctrl_shadow |
+ IO_STATE(R_TIMER_CTRL, i0, clr);
+#endif
+
+ /* reset watchdog otherwise it resets us! */
+
+ reset_watchdog();
+
+ /* call the real timer interrupt handler */
+
+ do_timer(regs);
+
+ /*
+ * If we have an externally synchronized Linux clock, then update
+ * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
+ * called as close as possible to 500 ms before the new second starts.
+ */
+
+ if ((time_status & STA_UNSYNC) == 0 &&
+ xtime.tv_sec > last_rtc_update + 660 &&
+ xtime.tv_usec > 500000 - (tick >> 1) &&
+ xtime.tv_usec < 500000 + (tick >> 1))
+ if (set_rtc_mmss(xtime.tv_sec) == 0)
+ last_rtc_update = xtime.tv_sec;
+ else
+ last_rtc_update = xtime.tv_sec - 600;
+
+}
+
+#if 0
+/* some old debug code for testing the microsecond timing of packets */
+static unsigned int lastjiff;
+
+void print_timestamp(const char *s)
+{
+ unsigned long flags;
+ unsigned int newjiff;
+ save_flags(flags);
+ cli();
+ newjiff = (myjiff << 16) | (unsigned short)(-*R_TIMER01_DATA);
+ printk("%s: %x (%x)\n", s, newjiff, newjiff - lastjiff);
+ lastjiff = newjiff;
+ restore_flags(flags);
+}
+#endif
+
+/* grab the time from the RTC chip */
+
+unsigned long
+get_cmos_time(void)
+{
+ unsigned int year, mon, day, hour, min, sec;
+ int i;
+
+ sec = CMOS_READ(RTC_SECONDS);
+ min = CMOS_READ(RTC_MINUTES);
+ hour = CMOS_READ(RTC_HOURS);
+ day = CMOS_READ(RTC_DAY_OF_MONTH);
+ mon = CMOS_READ(RTC_MONTH);
+ year = CMOS_READ(RTC_YEAR);
+
+ printk("rtc: sec 0x%x min 0x%x hour 0x%x day 0x%x mon 0x%x year 0x%x\n",
+ sec, min, hour, day, mon, year);
+
+ BCD_TO_BIN(sec);
+ BCD_TO_BIN(min);
+ BCD_TO_BIN(hour);
+ BCD_TO_BIN(day);
+ BCD_TO_BIN(mon);
+ BCD_TO_BIN(year);
+
+ if ((year += 1900) < 1970)
+ year += 100;
+
+ return mktime(year, mon, day, hour, min, sec);
+}
+
+/* update xtime from the CMOS settings. used when /dev/rtc gets a SET_TIME.
+ * TODO: this doesn't reset the fancy NTP phase stuff as do_settimeofday does.
+ */
+
+void
+update_xtime_from_cmos(void)
+{
+ if(have_rtc) {
+ xtime.tv_sec = get_cmos_time();
+ xtime.tv_usec = 0;
+ }
+}
+
+/* timer is SA_SHIRQ so drivers can add stuff to the timer irq chain
+ * it needs to be SA_INTERRUPT to make the jiffies update work properly
+ */
+
+static struct irqaction irq2 = { timer_interrupt, SA_SHIRQ | SA_INTERRUPT,
+ 0, "timer", NULL, NULL};
+
+void __init
+time_init(void)
+{
+ /* probe for the RTC and read it if it exists */
+
+ if(RTC_INIT() < 0) {
+ /* no RTC, start at 1980 */
+ xtime.tv_sec = 0;
+ xtime.tv_usec = 0;
+ have_rtc = 0;
+ } else {
+ /* get the current time */
+ have_rtc = 1;
+ update_xtime_from_cmos();
+ }
+
+ /* Setup the etrax timers
+ * Base frequency is 19200 hz, divider 192 -> 100 hz as Linux wants
+ * In normal mode, we use timer0, so timer1 is free. In cascade
+ * mode (which we sometimes use for debugging) both timers are used.
+ * Remember that linux/timex.h contains #defines that rely on the
+ * timer settings below (hz and divide factor) !!!
+ */
+
+#ifdef USE_CASCADE_TIMERS
+ *R_TIMER_CTRL =
+ IO_FIELD( R_TIMER_CTRL, timerdiv1, 0) |
+ IO_FIELD( R_TIMER_CTRL, timerdiv0, 0) |
+ IO_STATE( R_TIMER_CTRL, i1, nop) |
+ IO_STATE( R_TIMER_CTRL, tm1, stop_ld) |
+ IO_STATE( R_TIMER_CTRL, clksel1, cascade0) |
+ IO_STATE( R_TIMER_CTRL, i0, nop) |
+ IO_STATE( R_TIMER_CTRL, tm0, stop_ld) |
+ IO_STATE( R_TIMER_CTRL, clksel0, c6250kHz);
+
+ *R_TIMER_CTRL = r_timer_ctrl_shadow =
+ IO_FIELD( R_TIMER_CTRL, timerdiv1, 0) |
+ IO_FIELD( R_TIMER_CTRL, timerdiv0, 0) |
+ IO_STATE( R_TIMER_CTRL, i1, nop) |
+ IO_STATE( R_TIMER_CTRL, tm1, run) |
+ IO_STATE( R_TIMER_CTRL, clksel1, cascade0) |
+ IO_STATE( R_TIMER_CTRL, i0, nop) |
+ IO_STATE( R_TIMER_CTRL, tm0, run) |
+ IO_STATE( R_TIMER_CTRL, clksel0, c6250kHz);
+#else
+ *R_TIMER_CTRL =
+ IO_FIELD(R_TIMER_CTRL, timerdiv1, 192) |
+ IO_FIELD(R_TIMER_CTRL, timerdiv0, 192) |
+ IO_STATE(R_TIMER_CTRL, i1, nop) |
+ IO_STATE(R_TIMER_CTRL, tm1, stop_ld) |
+ IO_STATE(R_TIMER_CTRL, clksel1, c19k2Hz) |
+ IO_STATE(R_TIMER_CTRL, i0, nop) |
+ IO_STATE(R_TIMER_CTRL, tm0, stop_ld) |
+ IO_STATE(R_TIMER_CTRL, clksel0, c19k2Hz);
+
+ *R_TIMER_CTRL = r_timer_ctrl_shadow =
+ IO_FIELD(R_TIMER_CTRL, timerdiv1, 192) |
+ IO_FIELD(R_TIMER_CTRL, timerdiv0, 192) |
+ IO_STATE(R_TIMER_CTRL, i1, nop) |
+ IO_STATE(R_TIMER_CTRL, tm1, run) |
+ IO_STATE(R_TIMER_CTRL, clksel1, c19k2Hz) |
+ IO_STATE(R_TIMER_CTRL, i0, nop) |
+ IO_STATE(R_TIMER_CTRL, tm0, run) |
+ IO_STATE(R_TIMER_CTRL, clksel0, c19k2Hz);
+#endif
+
+ *R_IRQ_MASK0_SET =
+ IO_STATE(R_IRQ_MASK0_SET, timer0, set); /* unmask the timer irq */
+
+ /* now actually register the timer irq handler that calls timer_interrupt() */
+
+ setup_etrax_irq(2, &irq2); /* irq 2 is the timer0 irq in etrax */
+
+ /* enable watchdog if we should use one */
+
+#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
+ printk("Enabling watchdog...\n");
+ start_watchdog();
+#endif
+
+}
diff --git a/arch/cris/kernel/traps.c b/arch/cris/kernel/traps.c
new file mode 100644
index 000000000..9994487d4
--- /dev/null
+++ b/arch/cris/kernel/traps.c
@@ -0,0 +1,167 @@
+/* $Id: traps.c,v 1.3 2000/10/04 16:50:06 bjornw Exp $
+ *
+ * linux/arch/cris/traps.c
+ *
+ * Etrax100 does not have any hardware traps, only IRQ's, which we setup
+ * in irq.c instead. Here we just define the die_if_kernel Oops'er.
+ *
+ * Copyright (C) 2000 Axis Communications AB
+ *
+ * Authors: Bjorn Wesen
+ *
+ */
+
+#include <linux/init.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 <asm/system.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+
+int kstack_depth_to_print = 24;
+
+/*
+ * These constants are for searching for possible module text
+ * segments. MODULE_RANGE is a guess of how much space is likely
+ * to be vmalloced.
+ */
+
+#define MODULE_RANGE (8*1024*1024)
+
+void show_stack(unsigned long *sp)
+{
+ unsigned long *stack, addr, module_start, module_end;
+ int i;
+ extern char _stext, _etext;
+
+ // debugging aid: "show_stack(NULL);" prints the
+ // back trace for this cpu.
+
+ if(sp == NULL)
+ sp = (unsigned long*)rdsp();
+
+ stack = sp;
+
+ for(i = 0; i < kstack_depth_to_print; i++) {
+ if (((long) stack & (THREAD_SIZE-1)) == 0)
+ break;
+ if (i && ((i % 8) == 0))
+ printk("\n ");
+ printk("%08lx ", *stack++);
+ }
+
+ printk("\nCall Trace: ");
+ stack = sp;
+ i = 1;
+ module_start = VMALLOC_START;
+ module_end = VMALLOC_END;
+ while (((long) stack & (THREAD_SIZE-1)) != 0) {
+ addr = *stack++;
+ /*
+ * If the address is either in the text segment of the
+ * kernel, or in the region which contains vmalloc'ed
+ * memory, it *may* be the address of a calling
+ * routine; if so, print it so that someone tracing
+ * down the cause of the crash will be able to figure
+ * out the call path that was taken.
+ */
+ if (((addr >= (unsigned long) &_stext) &&
+ (addr <= (unsigned long) &_etext)) ||
+ ((addr >= module_start) && (addr <= module_end))) {
+ if (i && ((i % 8) == 0))
+ printk("\n ");
+ printk("[<%08lx>] ", addr);
+ i++;
+ }
+ }
+}
+
+#if 0
+/* displays a short stack trace */
+
+int show_stack()
+{
+ unsigned long *sp = (unsigned long *)rdusp();
+ int i;
+ printk("Stack dump [0x%08lx]:\n", (unsigned long)sp);
+ for(i = 0; i < 16; i++)
+ printk("sp + %d: 0x%08lx\n", i*4, sp[i]);
+ return 0;
+}
+#endif
+
+void show_registers(struct pt_regs * regs)
+{
+ unsigned long usp = rdusp();
+
+ printk("IRP: %08lx SRP: %08lx CCR: %08lx USP: %08lx\n",
+ regs->irp, regs->srp, regs->dccr, usp );
+ printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n",
+ regs->r0, regs->r1, regs->r2, regs->r3);
+ printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n",
+ regs->r4, regs->r5, regs->r6, regs->r7);
+ printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n",
+ regs->r8, regs->r9, regs->r10, regs->r11);
+ printk("r12: %08lx r13: %08lx oR10: %08lx\n",
+ regs->r12, regs->r13, regs->orig_r10);
+ printk("Process %s (pid: %d, stackpage=%08lx)\n",
+ current->comm, current->pid, (unsigned long)current);
+
+ // TODO, fix in_kernel detection
+
+#if 0
+ /*
+ * When in-kernel, we also print out the stack and code at the
+ * time of the fault..
+ */
+ if (1) {
+
+ printk("\nStack: ");
+ show_stack((unsigned long*)usp);
+
+ printk("\nCode: ");
+ if(regs->irp < PAGE_OFFSET)
+ goto bad;
+
+ for(i = 0; i < 20; i++)
+ {
+ unsigned char c;
+ if(__get_user(c, &((unsigned char*)regs->irp)[i])) {
+bad:
+ printk(" Bad IP value.");
+ break;
+ }
+ printk("%02x ", c);
+ }
+ }
+ printk("\n");
+#endif
+}
+
+
+
+void die_if_kernel(const char * str, struct pt_regs * regs, long err)
+{
+ if(user_mode(regs))
+ return;
+
+ printk("%s: %04lx\n", str, err & 0xffff);
+
+ show_registers(regs);
+ show_stack(NULL); /* print backtrace for kernel stack on this CPU */
+
+ do_exit(SIGSEGV);
+}
+
+void __init trap_init(void)
+{
+
+
+}
diff --git a/arch/cris/lib/Makefile b/arch/cris/lib/Makefile
new file mode 100644
index 000000000..6ede712e3
--- /dev/null
+++ b/arch/cris/lib/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for Etrax-specific library files..
+#
+
+.S.o:
+ $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o
+
+L_TARGET = lib.a
+obj-y = checksum.o checksumcopy.o string.o usercopy.o memset.o
+
+include $(TOPDIR)/Rules.make
diff --git a/arch/cris/lib/checksum.S b/arch/cris/lib/checksum.S
new file mode 100644
index 000000000..4ee0daa0c
--- /dev/null
+++ b/arch/cris/lib/checksum.S
@@ -0,0 +1,113 @@
+ ;; $Id: checksum.S,v 1.1 2000/07/10 16:25:21 bjornw Exp $
+ ;; A fast checksum routine using movem
+ ;; Copyright (c) 1998 Bjorn Wesen/Axis Communications AB
+
+ ;; csum_partial(const unsigned char * buff, int len, unsigned int sum)
+
+ .globl _csum_partial
+_csum_partial:
+
+ ;; check for breakeven length between movem and normal word looping versions
+
+ cmpu.w 80,r11
+ bcs no_movem
+ nop
+
+ ;; need to save the registers we use below in the movem loop
+ ;; this overhead is why we have a check above for breakeven length
+
+ subq 9*4,sp
+ movem r8,[sp]
+
+ ;; do a movem checksum
+
+ ;; r10 - src
+ ;; r11 - length
+ ;; r12 - checksum
+
+ subq 10*4,r11 ; update length for the first loop
+
+mloop: movem [r10+],r9 ; read 10 longwords
+
+ ;; perform dword checksumming on the 10 longwords
+
+ add.d r0,r12
+ ax
+ add.d r1,r12
+ ax
+ add.d r2,r12
+ ax
+ add.d r3,r12
+ ax
+ add.d r4,r12
+ ax
+ add.d r5,r12
+ ax
+ add.d r6,r12
+ ax
+ add.d r7,r12
+ ax
+ add.d r8,r12
+ ax
+ add.d r9,r12
+
+ ;; fold the carry into the checksum, to avoid having to loop the carry
+ ;; back into the top
+
+ ax
+ addq 0,r12
+ ax ; do it again, since we might have generated a carry
+ addq 0,r12
+
+ subq 10*4,r11
+ bge mloop
+ nop
+
+ addq 10*4,r11 ; compensate for last loop underflowing length
+
+ ;; fold 32-bit checksum into a 16-bit checksum, to avoid carries below
+
+ moveq -1,r1 ; put 0xffff in r1, faster than move.d 0xffff,r1
+ lsrq 16,r1
+
+ move.d r12,r0
+ lsrq 16,r0 ; r0 = checksum >> 16
+ and.d r1,r12 ; checksum = checksum & 0xffff
+ add.d r0,r12 ; checksum += r0
+ move.d r12,r0 ; do the same again, maybe we got a carry last add
+ lsrq 16,r0
+ and.d r1,r12
+ add.d r0,r12
+
+ movem [sp+],r8 ; restore regs
+
+no_movem:
+ cmpq 2,r11
+ blt no_words
+ nop
+
+ ;; checksum the rest of the words
+
+ subq 2,r11
+
+wloop: subq 2,r11
+ bge wloop
+ addu.w [r10+],r12
+
+ addq 2,r11
+
+no_words:
+ ;; see if we have one odd byte more
+ cmpq 1,r11
+ beq do_byte
+ nop
+ ret
+ move.d r12, r10
+
+do_byte:
+ ;; copy and checksum the last byte
+ addu.b [r10],r12
+ ret
+ move.d r12, r10
+
+ \ No newline at end of file
diff --git a/arch/cris/lib/checksumcopy.S b/arch/cris/lib/checksumcopy.S
new file mode 100644
index 000000000..eae9c7ace
--- /dev/null
+++ b/arch/cris/lib/checksumcopy.S
@@ -0,0 +1,120 @@
+ ;; $Id: checksumcopy.S,v 1.2 2000/08/08 16:57:31 bjornw Exp $
+ ;; A fast checksum+copy routine using movem
+ ;; Copyright (c) 1998, 2000 Axis Communications AB
+ ;;
+ ;; Authors: Bjorn Wesen
+ ;;
+ ;; csum_partial_copy_nocheck(const char *src, char *dst,
+ ;; int len, unsigned int sum)
+
+ .globl _csum_partial_copy_nocheck
+_csum_partial_copy_nocheck:
+
+ ;; check for breakeven length between movem and normal word looping versions
+
+ cmpu.w 80,r12
+ bcs no_movem
+ nop
+
+ ;; need to save the registers we use below in the movem loop
+ ;; this overhead is why we have a check above for breakeven length
+
+ subq 9*4,sp
+ movem r8,[sp]
+
+ ;; do a movem copy and checksum
+
+ ;; r10 - src
+ ;; r11 - dst
+ ;; r12 - length
+ ;; r13 - checksum
+
+ subq 10*4,r12 ; update length for the first loop
+
+mloop: movem [r10+],r9 ; read 10 longwords
+ movem r9,[r11+] ; write 10 longwords
+
+ ;; perform dword checksumming on the 10 longwords
+
+ add.d r0,r13
+ ax
+ add.d r1,r13
+ ax
+ add.d r2,r13
+ ax
+ add.d r3,r13
+ ax
+ add.d r4,r13
+ ax
+ add.d r5,r13
+ ax
+ add.d r6,r13
+ ax
+ add.d r7,r13
+ ax
+ add.d r8,r13
+ ax
+ add.d r9,r13
+
+ ;; fold the carry into the checksum, to avoid having to loop the carry
+ ;; back into the top
+
+ ax
+ addq 0,r13
+
+ subq 10*4,r12
+ bge mloop
+ nop
+
+ addq 10*4,r12 ; compensate for last loop underflowing length
+
+ ;; fold 32-bit checksum into a 16-bit checksum, to avoid carries below
+
+ moveq -1,r1 ; put 0xffff in r1, faster than move.d 0xffff,r1
+ lsrq 16,r1
+
+ move.d r13,r0
+ lsrq 16,r0 ; r0 = checksum >> 16
+ and.d r1,r13 ; checksum = checksum & 0xffff
+ add.d r0,r13 ; checksum += r0
+ move.d r13,r0 ; do the same again, maybe we got a carry last add
+ lsrq 16,r0
+ and.d r1,r13
+ add.d r0,r13
+
+ movem [sp+],r8 ; restore regs
+
+no_movem:
+ cmpq 2,r12
+ blt no_words
+ nop
+
+ ;; copy and checksum the rest of the words
+
+ subq 2,r12
+
+wloop: move.w [r10+],r9
+ addu.w r9,r13
+ subq 2,r12
+ bge wloop
+ move.w r9,[r11+]
+
+ addq 2,r12
+
+no_words:
+ ;; see if we have one odd byte more
+ cmpq 1,r12
+ beq do_byte
+ nop
+ ret
+ move.d r13, r10
+
+do_byte:
+ ;; copy and checksum the last byte
+ move.b [r10],r9
+ addu.b r9,r13
+ move.b r9,[r11]
+ ret
+ move.d r13, r10
+
+ \ No newline at end of file
diff --git a/arch/cris/lib/dmacopy.c b/arch/cris/lib/dmacopy.c
new file mode 100644
index 000000000..318577a2d
--- /dev/null
+++ b/arch/cris/lib/dmacopy.c
@@ -0,0 +1,43 @@
+/* $Id: dmacopy.c,v 1.1 2000/07/10 16:25:21 bjornw Exp $
+ *
+ * memcpy for large blocks, using memory-memory DMA channels 6 and 7 in Etrax
+ */
+
+#include <asm/svinto.h>
+#include <asm/io.h>
+
+#define D(x)
+
+void *dma_memcpy(void *pdst,
+ const void *psrc,
+ unsigned int pn)
+{
+ static etrax_dma_descr indma, outdma;
+
+ D(printk("dma_memcpy %d bytes... ", pn));
+
+#if 0
+ *R_GEN_CONFIG = genconfig_shadow =
+ (genconfig_shadow & ~0x3c0000) |
+ IO_STATE(R_GEN_CONFIG, dma6, intdma7) |
+ IO_STATE(R_GEN_CONFIG, dma7, intdma6);
+#endif
+ indma.sw_len = outdma.sw_len = pn;
+ indma.ctrl = d_eol | d_eop;
+ outdma.ctrl = d_eol;
+ indma.buf = psrc;
+ outdma.buf = pdst;
+
+ *R_DMA_CH6_FIRST = &indma;
+ *R_DMA_CH7_FIRST = &outdma;
+ *R_DMA_CH6_CMD = IO_STATE(R_DMA_CH6_CMD, cmd, start);
+ *R_DMA_CH7_CMD = IO_STATE(R_DMA_CH7_CMD, cmd, start);
+
+ while(*R_DMA_CH7_CMD == 1) /* wait for completion */ ;
+
+ D(printk("done\n"));
+
+}
+
+
+
diff --git a/arch/cris/lib/memset.c b/arch/cris/lib/memset.c
new file mode 100644
index 000000000..2f9f3fe37
--- /dev/null
+++ b/arch/cris/lib/memset.c
@@ -0,0 +1,245 @@
+/*#************************************************************************#*/
+/*#-------------------------------------------------------------------------*/
+/*# */
+/*# FUNCTION NAME: memset() */
+/*# */
+/*# PARAMETERS: void* dst; Destination address. */
+/*# int c; Value of byte to write. */
+/*# int len; Number of bytes to write. */
+/*# */
+/*# RETURNS: dst. */
+/*# */
+/*# DESCRIPTION: Sets the memory dst of length len bytes to c, as standard. */
+/*# Framework taken from memcpy. This routine is */
+/*# very sensitive to compiler changes in register allocation. */
+/*# Should really be rewritten to avoid this problem. */
+/*# */
+/*#-------------------------------------------------------------------------*/
+/*# */
+/*# HISTORY */
+/*# */
+/*# DATE NAME CHANGES */
+/*# ---- ---- ------- */
+/*# 990713 HP Tired of watching this function (or */
+/*# really, the nonoptimized generic */
+/*# implementation) take up 90% of simulator */
+/*# output. Measurements needed. */
+/*# */
+/*#-------------------------------------------------------------------------*/
+
+/* No, there's no macro saying 12*4, since it is "hard" to get it into
+ the asm in a good way. Thus better to expose the problem everywhere.
+ */
+
+/* Assuming 1 cycle per dword written or read (ok, not really true), and
+ one per instruction, then 43+3*(n/48-1) <= 24+24*(n/48-1)
+ so n >= 45.7; n >= 0.9; we win on the first full 48-byte block to set. */
+
+#define ZERO_BLOCK_SIZE (1*12*4)
+
+void *memset(void *pdst,
+ int c,
+ unsigned int plen)
+{
+ /* Ok. Now we want the parameters put in special registers.
+ Make sure the compiler is able to make something useful of this. */
+
+ register char *return_dst __asm__ ("r10") = pdst;
+ register int n __asm__ ("r12") = plen;
+ register int lc __asm__ ("r11") = c;
+
+ /* Most apps use memset sanely. Only those memsetting about 3..4
+ bytes or less get penalized compared to the generic implementation
+ - and that's not really sane use. */
+
+ /* Ugh. This is fragile at best. Check with newer GCC releases, if
+ they compile cascaded "x |= x << 8" sanely! */
+ __asm__("movu.b %0,r13\n\tlslq 8,r13\n\tmove.b %0,r13\n\tmove.d r13,%0\n\tlslq 16,r13\n\tor.d r13,%0"
+ : "=r" (lc) : "0" (lc) : "r13");
+
+ {
+ register char *dst __asm__ ("r13") = pdst;
+
+ /* This is NONPORTABLE, but since this whole routine is */
+ /* grossly nonportable that doesn't matter. */
+
+ if (((unsigned long) pdst & 3) != 0
+ /* Oops! n=0 must be a legal call, regardless of alignment. */
+ && n >= 3)
+ {
+ if ((unsigned long)dst & 1)
+ {
+ *dst = (char) lc;
+ n--;
+ dst++;
+ }
+
+ if ((unsigned long)dst & 2)
+ {
+ *(short *)dst = lc;
+ n -= 2;
+ dst += 2;
+ }
+ }
+
+ /* Now the fun part. For the threshold value of this, check the equation
+ above. */
+ /* Decide which copying method to use. */
+ if (n >= ZERO_BLOCK_SIZE)
+ {
+ /* For large copies we use 'movem' */
+
+ /* It is not optimal to tell the compiler about clobbering any
+ registers; that will move the saving/restoring of those registers
+ to the function prologue/epilogue, and make non-movem sizes
+ suboptimal.
+
+ This method is not foolproof; it assumes that the "asm reg"
+ declarations at the beginning of the function really are used
+ here (beware: they may be moved to temporary registers).
+ This way, we do not have to save/move the registers around into
+ temporaries; we can safely use them straight away.
+
+ If you want to check that the allocation was right; then
+ check the equalities in the first comment. It should say
+ "r13=r13, r12=r12, r11=r11" */
+ __asm__ volatile ("
+ ;; Check that the following is true (same register names on
+ ;; both sides of equal sign, as in r8=r8):
+ ;; %0=r13, %1=r12, %4=r11
+ ;;
+ ;; Save the registers we'll clobber in the movem process
+ ;; on the stack. Don't mention them to gcc, it will only be
+ ;; upset.
+ subq 11*4,sp
+ movem r10,[sp]
+
+ move.d r11,r0
+ move.d r11,r1
+ move.d r11,r2
+ move.d r11,r3
+ move.d r11,r4
+ move.d r11,r5
+ move.d r11,r6
+ move.d r11,r7
+ move.d r11,r8
+ move.d r11,r9
+ move.d r11,r10
+
+ ;; Now we've got this:
+ ;; r13 - dst
+ ;; r12 - n
+
+ ;; Update n for the first loop
+ subq 12*4,r12
+0:
+ subq 12*4,r12
+ bge 0b
+ movem r11,[r13+]
+
+ addq 12*4,r12 ;; compensate for last loop underflowing n
+
+ ;; Restore registers from stack
+ movem [sp+],r10"
+
+ /* Outputs */ : "=r" (dst), "=r" (n)
+ /* Inputs */ : "0" (dst), "1" (n), "r" (lc));
+
+ }
+
+ /* Either we directly starts copying, using dword copying
+ in a loop, or we copy as much as possible with 'movem'
+ and then the last block (<44 bytes) is copied here.
+ This will work since 'movem' will have updated src,dst,n. */
+
+ while ( n >= 16 )
+ {
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ n -= 16;
+ }
+
+ /* A switch() is definitely the fastest although it takes a LOT of code.
+ * Particularly if you inline code this.
+ */
+ switch (n)
+ {
+ case 0:
+ break;
+ case 1:
+ *(char*)dst = (char) lc;
+ break;
+ case 2:
+ *(short*)dst = (short) lc;
+ break;
+ case 3:
+ *((short*)dst)++ = (short) lc;
+ *(char*)dst = (char) lc;
+ break;
+ case 4:
+ *((long*)dst)++ = lc;
+ break;
+ case 5:
+ *((long*)dst)++ = lc;
+ *(char*)dst = (char) lc;
+ break;
+ case 6:
+ *((long*)dst)++ = lc;
+ *(short*)dst = (short) lc;
+ break;
+ case 7:
+ *((long*)dst)++ = lc;
+ *((short*)dst)++ = (short) lc;
+ *(char*)dst = (char) lc;
+ break;
+ case 8:
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ break;
+ case 9:
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ *(char*)dst = (char) lc;
+ break;
+ case 10:
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ *(short*)dst = (short) lc;
+ break;
+ case 11:
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ *((short*)dst)++ = (short) lc;
+ *(char*)dst = (char) lc;
+ break;
+ case 12:
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ break;
+ case 13:
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ *(char*)dst = (char) lc;
+ break;
+ case 14:
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ *(short*)dst = (short) lc;
+ break;
+ case 15:
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ *((short*)dst)++ = (short) lc;
+ *(char*)dst = (char) lc;
+ break;
+ }
+ }
+
+ return return_dst; /* destination pointer. */
+} /* memset() */
diff --git a/arch/cris/lib/old_checksum.c b/arch/cris/lib/old_checksum.c
new file mode 100644
index 000000000..6035a48ae
--- /dev/null
+++ b/arch/cris/lib/old_checksum.c
@@ -0,0 +1,127 @@
+/* $Id: old_checksum.c,v 1.1 2000/07/10 16:25:21 bjornw Exp $
+ *
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * IP/TCP/UDP checksumming routines
+ *
+ * Authors: Jorge Cwik, <jorge@laser.satlink.net>
+ * Arnt Gulbrandsen, <agulbra@nvg.unit.no>
+ * Tom May, <ftom@netcom.com>
+ * Lots of code moved from tcp.c and ip.c; see those files
+ * for more names.
+ *
+ * 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 <net/checksum.h>
+
+#undef PROFILE_CHECKSUM
+
+#ifdef PROFILE_CHECKSUM
+/* these are just for profiling the checksum code with an oscillioscope.. uh */
+#if 0
+#define BITOFF *((unsigned char *)0xb0000030) = 0xff
+#define BITON *((unsigned char *)0xb0000030) = 0x0
+#endif
+#include <asm/io.h>
+#define CBITON LED_ACTIVE_SET(1)
+#define CBITOFF LED_ACTIVE_SET(0)
+#define BITOFF
+#define BITON
+#else
+#define BITOFF
+#define BITON
+#define CBITOFF
+#define CBITON
+#endif
+
+/*
+ * computes a partial checksum, e.g. for TCP/UDP fragments
+ */
+
+#include <asm/delay.h>
+
+unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum)
+{
+ /*
+ * Experiments with ethernet and slip connections show that buff
+ * is aligned on either a 2-byte or 4-byte boundary.
+ */
+ const unsigned char *endMarker = buff + len;
+ const unsigned char *marker = endMarker - (len % 16);
+#if 0
+ if((int)buff & 0x3)
+ printk("unaligned buff %p\n", buff);
+ __delay(900); /* extra delay of 90 us to test performance hit */
+#endif
+ BITON;
+ while (buff < marker) {
+ sum += *((unsigned short *)buff)++;
+ sum += *((unsigned short *)buff)++;
+ sum += *((unsigned short *)buff)++;
+ sum += *((unsigned short *)buff)++;
+ sum += *((unsigned short *)buff)++;
+ sum += *((unsigned short *)buff)++;
+ sum += *((unsigned short *)buff)++;
+ sum += *((unsigned short *)buff)++;
+ }
+ marker = endMarker - (len % 2);
+ while(buff < marker) {
+ sum += *((unsigned short *)buff)++;
+ }
+ if(endMarker - buff > 0) {
+ sum += *buff; /* add extra byte seperately */
+ }
+ BITOFF;
+ return(sum);
+}
+
+#if 0
+
+/*
+ * copy while checksumming, otherwise like csum_partial
+ */
+
+unsigned int csum_partial_copy(const unsigned char *src, unsigned char *dst,
+ int len, unsigned int sum)
+{
+ const unsigned char *endMarker;
+ const unsigned char *marker;
+ printk("csum_partial_copy len %d.\n", len);
+#if 0
+ if((int)src & 0x3)
+ printk("unaligned src %p\n", src);
+ if((int)dst & 0x3)
+ printk("unaligned dst %p\n", dst);
+ __delay(1800); /* extra delay of 90 us to test performance hit */
+#endif
+ endMarker = src + len;
+ marker = endMarker - (len % 16);
+ CBITON;
+ while(src < marker) {
+ sum += (*((unsigned short *)dst)++ = *((unsigned short *)src)++);
+ sum += (*((unsigned short *)dst)++ = *((unsigned short *)src)++);
+ sum += (*((unsigned short *)dst)++ = *((unsigned short *)src)++);
+ sum += (*((unsigned short *)dst)++ = *((unsigned short *)src)++);
+ sum += (*((unsigned short *)dst)++ = *((unsigned short *)src)++);
+ sum += (*((unsigned short *)dst)++ = *((unsigned short *)src)++);
+ sum += (*((unsigned short *)dst)++ = *((unsigned short *)src)++);
+ sum += (*((unsigned short *)dst)++ = *((unsigned short *)src)++);
+ }
+ marker = endMarker - (len % 2);
+ while(src < marker) {
+ sum += (*((unsigned short *)dst)++ = *((unsigned short *)src)++);
+ }
+ if(endMarker - src > 0) {
+ sum += (*dst = *src); /* add extra byte seperately */
+ }
+ CBITOFF;
+ return(sum);
+}
+
+#endif
diff --git a/arch/cris/lib/string.c b/arch/cris/lib/string.c
new file mode 100644
index 000000000..6218cad56
--- /dev/null
+++ b/arch/cris/lib/string.c
@@ -0,0 +1,223 @@
+/*#************************************************************************#*/
+/*#-------------------------------------------------------------------------*/
+/*# */
+/*# FUNCTION NAME: memcpy() */
+/*# */
+/*# PARAMETERS: void* dst; Destination address. */
+/*# void* src; Source address. */
+/*# int len; Number of bytes to copy. */
+/*# */
+/*# RETURNS: dst. */
+/*# */
+/*# DESCRIPTION: Copies len bytes of memory from src to dst. No guarantees */
+/*# about copying of overlapping memory areas. This routine is */
+/*# very sensitive to compiler changes in register allocation. */
+/*# Should really be rewritten to avoid this problem. */
+/*# */
+/*#-------------------------------------------------------------------------*/
+/*# */
+/*# HISTORY */
+/*# */
+/*# DATE NAME CHANGES */
+/*# ---- ---- ------- */
+/*# 941007 Kenny R Creation */
+/*# 941011 Kenny R Lots of optimizations and inlining. */
+/*# 941129 Ulf A Adapted for use in libc. */
+/*# 950216 HP N==0 forgotten if non-aligned src/dst. */
+/*# Added some optimizations. */
+/*# 001025 HP Make src and dst char *. Align dst to */
+/*# dword, not just word-if-both-src-and-dst- */
+/*# are-misaligned. */
+/*# */
+/*#-------------------------------------------------------------------------*/
+
+void *memcpy(void *pdst,
+ const void *psrc,
+ unsigned int pn)
+{
+ /* Ok. Now we want the parameters put in special registers.
+ Make sure the compiler is able to make something useful of this.
+ As it is now: r10 -> r13; r11 -> r11 (nop); r12 -> r12 (nop).
+
+ If gcc was allright, it really would need no temporaries, and no
+ stack space to save stuff on. */
+
+ register void *return_dst __asm__ ("r10") = pdst;
+ register char *dst __asm__ ("r13") = pdst;
+ register const char *src __asm__ ("r11") = psrc;
+ register int n __asm__ ("r12") = pn;
+
+
+ /* When src is aligned but not dst, this makes a few extra needless
+ cycles. I believe it would take as many to check that the
+ re-alignment was unnecessary. */
+ if (((unsigned long) dst & 3) != 0
+ /* Don't align if we wouldn't copy more than a few bytes; so we
+ don't have to check further for overflows. */
+ && n >= 3)
+ {
+ if ((unsigned long) dst & 1)
+ {
+ n--;
+ *(char*)dst = *(char*)src;
+ src++;
+ dst++;
+ }
+
+ if ((unsigned long) dst & 2)
+ {
+ n -= 2;
+ *(short*)dst = *(short*)src;
+ src += 2;
+ dst += 2;
+ }
+ }
+
+ /* Decide which copying method to use. */
+ if (n >= 44*2) /* Break even between movem and
+ move16 is at 38.7*2, but modulo 44. */
+ {
+ /* For large copies we use 'movem' */
+
+ /* It is not optimal to tell the compiler about clobbering any
+ registers; that will move the saving/restoring of those registers
+ to the function prologue/epilogue, and make non-movem sizes
+ suboptimal.
+
+ This method is not foolproof; it assumes that the "asm reg"
+ declarations at the beginning of the function really are used
+ here (beware: they may be moved to temporary registers).
+ This way, we do not have to save/move the registers around into
+ temporaries; we can safely use them straight away.
+
+ If you want to check that the allocation was right; then
+ check the equalities in the first comment. It should say
+ "r13=r13, r11=r11, r12=r12" */
+ __asm__ volatile ("
+ ;; Check that the following is true (same register names on
+ ;; both sides of equal sign, as in r8=r8):
+ ;; %0=r13, %1=r11, %2=r12
+ ;;
+ ;; Save the registers we'll use in the movem process
+ ;; on the stack.
+ subq 11*4,sp
+ movem r10,[sp]
+
+ ;; Now we've got this:
+ ;; r11 - src
+ ;; r13 - dst
+ ;; r12 - n
+
+ ;; Update n for the first loop
+ subq 44,r12
+0:
+ movem [r11+],r10
+ subq 44,r12
+ bge 0b
+ movem r10,[r13+]
+
+ addq 44,r12 ;; compensate for last loop underflowing n
+
+ ;; Restore registers from stack
+ movem [sp+],r10"
+
+ /* Outputs */ : "=r" (dst), "=r" (src), "=r" (n)
+ /* Inputs */ : "0" (dst), "1" (src), "2" (n));
+
+ }
+
+ /* Either we directly starts copying, using dword copying
+ in a loop, or we copy as much as possible with 'movem'
+ and then the last block (<44 bytes) is copied here.
+ This will work since 'movem' will have updated src,dst,n. */
+
+ while ( n >= 16 )
+ {
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ n -= 16;
+ }
+
+ /* A switch() is definitely the fastest although it takes a LOT of code.
+ * Particularly if you inline code this.
+ */
+ switch (n)
+ {
+ case 0:
+ break;
+ case 1:
+ *(char*)dst = *(char*)src;
+ break;
+ case 2:
+ *(short*)dst = *(short*)src;
+ break;
+ case 3:
+ *((short*)dst)++ = *((short*)src)++;
+ *(char*)dst = *(char*)src;
+ break;
+ case 4:
+ *((long*)dst)++ = *((long*)src)++;
+ break;
+ case 5:
+ *((long*)dst)++ = *((long*)src)++;
+ *(char*)dst = *(char*)src;
+ break;
+ case 6:
+ *((long*)dst)++ = *((long*)src)++;
+ *(short*)dst = *(short*)src;
+ break;
+ case 7:
+ *((long*)dst)++ = *((long*)src)++;
+ *((short*)dst)++ = *((short*)src)++;
+ *(char*)dst = *(char*)src;
+ break;
+ case 8:
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ break;
+ case 9:
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ *(char*)dst = *(char*)src;
+ break;
+ case 10:
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ *(short*)dst = *(short*)src;
+ break;
+ case 11:
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ *((short*)dst)++ = *((short*)src)++;
+ *(char*)dst = *(char*)src;
+ break;
+ case 12:
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ break;
+ case 13:
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ *(char*)dst = *(char*)src;
+ break;
+ case 14:
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ *(short*)dst = *(short*)src;
+ break;
+ case 15:
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ *((short*)dst)++ = *((short*)src)++;
+ *(char*)dst = *(char*)src;
+ break;
+ }
+
+ return return_dst; /* destination pointer. */
+} /* memcpy() */
diff --git a/arch/cris/lib/usercopy.c b/arch/cris/lib/usercopy.c
new file mode 100644
index 000000000..17eebf2ee
--- /dev/null
+++ b/arch/cris/lib/usercopy.c
@@ -0,0 +1,501 @@
+/*
+ * User address space access functions.
+ * The non-inlined parts of asm-cris/uaccess.h are here.
+ *
+ * Copyright (C) 2000, Axis Communications AB.
+ *
+ * Written by Hans-Peter Nilsson.
+ * Pieces used from memcpy, originally by Kenny Ranerup long time ago.
+ */
+
+#include <asm/uaccess.h>
+
+/* Asm:s have been tweaked (within the domain of correctness) to give
+ satisfactory results for "gcc version 2.96 20000427 (experimental)".
+
+ Check regularly...
+
+ Note that the PC saved at a bus-fault is the address *after* the
+ faulting instruction, which means the branch-target for instructions in
+ delay-slots for taken branches. Note also that the postincrement in
+ the instruction is performed regardless of bus-fault; the register is
+ seen updated in fault handlers.
+
+ Oh, and on the code formatting issue, to whomever feels like "fixing
+ it" to Conformity: I'm too "lazy", but why don't you go ahead and "fix"
+ string.c too. I just don't think too many people will hack this file
+ for the code format to be an issue. */
+
+
+/* Copy to userspace. This is based on the memcpy used for
+ kernel-to-kernel copying; see "string.c". */
+
+unsigned long
+__copy_user (void *pdst, const void *psrc, unsigned long pn)
+{
+ /* We want the parameters put in special registers.
+ Make sure the compiler is able to make something useful of this.
+ As it is now: r10 -> r13; r11 -> r11 (nop); r12 -> r12 (nop).
+
+ FIXME: Comment for old gcc version. Check.
+ If gcc was allright, it really would need no temporaries, and no
+ stack space to save stuff on. */
+
+ register char *dst __asm__ ("r13") = pdst;
+ register const char *src __asm__ ("r11") = psrc;
+ register int n __asm__ ("r12") = pn;
+ register int retn __asm__ ("r10") = 0;
+
+
+ /* When src is aligned but not dst, this makes a few extra needless
+ cycles. I believe it would take as many to check that the
+ re-alignment was unnecessary. */
+ if (((unsigned long) dst & 3) != 0
+ /* Don't align if we wouldn't copy more than a few bytes; so we
+ don't have to check further for overflows. */
+ && n >= 3)
+ {
+ if ((unsigned long) dst & 1)
+ {
+ __asm_copy_to_user_1 (dst, src, retn);
+ n--;
+ }
+
+ if ((unsigned long) dst & 2)
+ {
+ __asm_copy_to_user_2 (dst, src, retn);
+ n -= 2;
+ }
+ }
+
+ /* Decide which copying method to use. */
+ if (n >= 44*2) /* Break even between movem and
+ move16 is at 38.7*2, but modulo 44. */
+ {
+ /* For large copies we use 'movem'. */
+
+ /* It is not optimal to tell the compiler about clobbering any
+ registers; that will move the saving/restoring of those registers
+ to the function prologue/epilogue, and make non-movem sizes
+ suboptimal.
+
+ This method is not foolproof; it assumes that the "asm reg"
+ declarations at the beginning of the function really are used
+ here (beware: they may be moved to temporary registers).
+ This way, we do not have to save/move the registers around into
+ temporaries; we can safely use them straight away.
+
+ If you want to check that the allocation was right; then
+ check the equalities in the first comment. It should say
+ "r13=r13, r11=r11, r12=r12". */
+ __asm__ volatile ("
+ ;; Check that the following is true (same register names on
+ ;; both sides of equal sign, as in r8=r8):
+ ;; %0=r13, %1=r11, %2=r12 %3=r10
+ ;;
+ ;; Save the registers we'll use in the movem process
+ ;; on the stack.
+ subq 11*4,sp
+ movem r10,[sp]
+
+ ;; Now we've got this:
+ ;; r11 - src
+ ;; r13 - dst
+ ;; r12 - n
+
+ ;; Update n for the first loop
+ subq 44,r12
+
+; Since the noted PC of a faulting instruction in a delay-slot of a taken
+; branch, is that of the branch target, we actually point at the from-movem
+; for this case. There is no ambiguity here; if there was a fault in that
+; instruction (meaning a kernel oops), the faulted PC would be the address
+; after *that* movem.
+
+0:
+ movem [r11+],r10
+ subq 44,r12
+ bge 0b
+ movem r10,[r13+]
+1:
+ addq 44,r12 ;; compensate for last loop underflowing n
+
+ ;; Restore registers from stack
+ movem [sp+],r10
+2:
+ .section .fixup,\"ax\"
+
+; To provide a correct count in r10 of bytes that failed to be copied,
+; we jump back into the loop if the loop-branch was taken. There is no
+; performance penalty for sany use; the program will segfault soon enough.
+
+3:
+ move.d [sp],r10
+ addq 44,r10
+ move.d r10,[sp]
+ jump 0b
+4:
+ movem [sp+],r10
+ addq 44,r10
+ addq 44,r12
+ jump 2b
+
+ .previous
+ .section __ex_table,\"a\"
+ .dword 0b,3b
+ .dword 1b,4b
+ .previous"
+
+ /* Outputs */ : "=r" (dst), "=r" (src), "=r" (n), "=r" (retn)
+ /* Inputs */ : "0" (dst), "1" (src), "2" (n), "3" (retn));
+
+ }
+
+ /* Either we directly start copying, using dword copying in a loop, or
+ we copy as much as possible with 'movem' and then the last block (<44
+ bytes) is copied here. This will work since 'movem' will have
+ updated SRC, DST and N. */
+
+ while (n >= 16)
+ {
+ __asm_copy_to_user_16 (dst, src, retn);
+ n -= 16;
+ }
+
+ /* Having a separate by-four loops cuts down on cache footprint.
+ FIXME: Test with and without; increasing switch to be 0..15. */
+ while (n >= 4)
+ {
+ __asm_copy_to_user_4 (dst, src, retn);
+ n -= 4;
+ }
+
+ switch (n)
+ {
+ case 0:
+ break;
+ case 1:
+ __asm_copy_to_user_1 (dst, src, retn);
+ break;
+ case 2:
+ __asm_copy_to_user_2 (dst, src, retn);
+ break;
+ case 3:
+ __asm_copy_to_user_3 (dst, src, retn);
+ break;
+ }
+
+ return retn;
+}
+
+/* Copy from user to kernel, zeroing the bytes that were inaccessible in
+ userland. */
+
+unsigned long
+__copy_user_zeroing (void *pdst, const void *psrc, unsigned long pn)
+{
+ /* We want the parameters put in special registers.
+ Make sure the compiler is able to make something useful of this.
+ As it is now: r10 -> r13; r11 -> r11 (nop); r12 -> r12 (nop).
+
+ FIXME: Comment for old gcc version. Check.
+ If gcc was allright, it really would need no temporaries, and no
+ stack space to save stuff on. */
+
+ register char *dst __asm__ ("r13") = pdst;
+ register const char *src __asm__ ("r11") = psrc;
+ register int n __asm__ ("r12") = pn;
+ register int retn __asm__ ("r10") = 0;
+
+ /* When src is aligned but not dst, this makes a few extra needless
+ cycles. I believe it would take as many to check that the
+ re-alignment was unnecessary. */
+ if (((unsigned long) dst & 3) != 0
+ /* Don't align if we wouldn't copy more than a few bytes; so we
+ don't have to check further for overflows. */
+ && n >= 3)
+ {
+ if ((unsigned long) dst & 1)
+ {
+ __asm_copy_from_user_1 (dst, src, retn);
+ n--;
+ }
+
+ if ((unsigned long) dst & 2)
+ {
+ __asm_copy_from_user_2 (dst, src, retn);
+ n -= 2;
+ }
+ }
+
+ /* Decide which copying method to use. */
+ if (n >= 44*2) /* Break even between movem and
+ move16 is at 38.7*2, but modulo 44. */
+ {
+ /* For large copies we use 'movem' */
+
+ /* It is not optimal to tell the compiler about clobbering any
+ registers; that will move the saving/restoring of those registers
+ to the function prologue/epilogue, and make non-movem sizes
+ suboptimal.
+
+ This method is not foolproof; it assumes that the "asm reg"
+ declarations at the beginning of the function really are used
+ here (beware: they may be moved to temporary registers).
+ This way, we do not have to save/move the registers around into
+ temporaries; we can safely use them straight away.
+
+ If you want to check that the allocation was right; then
+ check the equalities in the first comment. It should say
+ "r13=r13, r11=r11, r12=r12" */
+ __asm__ volatile ("
+ ;; Check that the following is true (same register names on
+ ;; both sides of equal sign, as in r8=r8):
+ ;; %0=r13, %1=r11, %2=r12 %3=r10
+ ;;
+ ;; Save the registers we'll use in the movem process
+ ;; on the stack.
+ subq 11*4,sp
+ movem r10,[sp]
+
+ ;; Now we've got this:
+ ;; r11 - src
+ ;; r13 - dst
+ ;; r12 - n
+
+ ;; Update n for the first loop
+ subq 44,r12
+0:
+ movem [r11+],r10
+1:
+ subq 44,r12
+ bge 0b
+ movem r10,[r13+]
+
+ addq 44,r12 ;; compensate for last loop underflowing n
+
+ ;; Restore registers from stack
+ movem [sp+],r10
+
+ .section .fixup,\"ax\"
+
+; To provide a correct count in r10 of bytes that failed to be copied,
+; we jump back into the loop if the loop-branch was taken.
+; There is no performance penalty; the program will segfault soon
+; enough.
+
+3:
+ move.d [sp],r10
+ addq 44,r10
+ move.d r10,[sp]
+ clear.d r0
+ clear.d r1
+ clear.d r2
+ clear.d r3
+ clear.d r4
+ clear.d r5
+ clear.d r6
+ clear.d r7
+ clear.d r8
+ clear.d r9
+ clear.d r10
+ jump 1b
+
+ .previous
+ .section __ex_table,\"a\"
+ .dword 1b,3b
+ .previous"
+
+ /* Outputs */ : "=r" (dst), "=r" (src), "=r" (n), "=r" (retn)
+ /* Inputs */ : "0" (dst), "1" (src), "2" (n), "3" (retn));
+
+ }
+
+ /* Either we directly start copying here, using dword copying in a loop,
+ or we copy as much as possible with 'movem' and then the last block
+ (<44 bytes) is copied here. This will work since 'movem' will have
+ updated src, dst and n. */
+
+ while (n >= 16)
+ {
+ __asm_copy_from_user_16 (dst, src, retn);
+ n -= 16;
+ }
+
+ /* Having a separate by-four loops cuts down on cache footprint.
+ FIXME: Test with and without; increasing switch to be 0..15. */
+ while (n >= 4)
+ {
+ __asm_copy_from_user_4 (dst, src, retn);
+ n -= 4;
+ }
+
+ switch (n)
+ {
+ case 0:
+ break;
+ case 1:
+ __asm_copy_from_user_1 (dst, src, retn);
+ break;
+ case 2:
+ __asm_copy_from_user_2 (dst, src, retn);
+ break;
+ case 3:
+ __asm_copy_from_user_3 (dst, src, retn);
+ break;
+ }
+
+ return retn;
+}
+
+/* Zero userspace. */
+
+unsigned long
+__do_clear_user (void *pto, unsigned long pn)
+{
+ /* We want the parameters put in special registers.
+ Make sure the compiler is able to make something useful of this.
+ As it is now: r10 -> r13; r11 -> r11 (nop); r12 -> r12 (nop).
+
+ FIXME: Comment for old gcc version. Check.
+ If gcc was allright, it really would need no temporaries, and no
+ stack space to save stuff on. */
+
+ register char *dst __asm__ ("r13") = pto;
+ register int n __asm__ ("r12") = pn;
+ register int retn __asm__ ("r10") = 0;
+
+
+ if (((unsigned long) dst & 3) != 0
+ /* Don't align if we wouldn't copy more than a few bytes. */
+ && n >= 3)
+ {
+ if ((unsigned long) dst & 1)
+ {
+ __asm_clear_1 (dst, retn);
+ n--;
+ }
+
+ if ((unsigned long) dst & 2)
+ {
+ __asm_clear_2 (dst, retn);
+ n -= 2;
+ }
+ }
+
+ /* Decide which copying method to use.
+ FIXME: This number is from the "ordinary" kernel memset. */
+ if (n >= (1*48))
+ {
+ /* For large clears we use 'movem' */
+
+ /* It is not optimal to tell the compiler about clobbering any
+ call-saved registers; that will move the saving/restoring of
+ those registers to the function prologue/epilogue, and make
+ non-movem sizes suboptimal.
+
+ This method is not foolproof; it assumes that the "asm reg"
+ declarations at the beginning of the function really are used
+ here (beware: they may be moved to temporary registers).
+ This way, we do not have to save/move the registers around into
+ temporaries; we can safely use them straight away.
+
+ If you want to check that the allocation was right; then
+ check the equalities in the first comment. It should say
+ something like "r13=r13, r11=r11, r12=r12". */
+ __asm__ volatile ("
+ ;; Check that the following is true (same register names on
+ ;; both sides of equal sign, as in r8=r8):
+ ;; %0=r13, %1=r12 %2=r10
+ ;;
+ ;; Save the registers we'll clobber in the movem process
+ ;; on the stack. Don't mention them to gcc, it will only be
+ ;; upset.
+ subq 11*4,sp
+ movem r10,[sp]
+
+ clear.d r0
+ clear.d r1
+ clear.d r2
+ clear.d r3
+ clear.d r4
+ clear.d r5
+ clear.d r6
+ clear.d r7
+ clear.d r8
+ clear.d r9
+ clear.d r10
+ clear.d r11
+
+ ;; Now we've got this:
+ ;; r13 - dst
+ ;; r12 - n
+
+ ;; Update n for the first loop
+ subq 12*4,r12
+0:
+ subq 12*4,r12
+ bge 0b
+ movem r11,[r13+]
+1:
+ addq 12*4,r12 ;; compensate for last loop underflowing n
+
+ ;; Restore registers from stack
+ movem [sp+],r10
+2:
+ .section .fixup,\"ax\"
+3:
+ move.d [sp],r10
+ addq 12*4,r10
+ move.d r10,[sp]
+ clear.d r10
+ jump 0b
+
+4:
+ movem [sp+],r10
+ addq 12*4,r10
+ addq 12*4,r12
+ jump 2b
+
+ .previous
+ .section __ex_table,\"a\"
+ .dword 0b,3b
+ .dword 1b,4b
+ .previous"
+
+ /* Outputs */ : "=r" (dst), "=r" (n), "=r" (retn)
+ /* Inputs */ : "0" (dst), "1" (n), "2" (retn)
+ /* Clobber */ : "r11");
+ }
+
+ while (n >= 16)
+ {
+ __asm_clear_16 (dst, retn);
+ n -= 16;
+ }
+
+ /* Having a separate by-four loops cuts down on cache footprint.
+ FIXME: Test with and without; increasing switch to be 0..15. */
+ while (n >= 4)
+ {
+ __asm_clear_4 (dst, retn);
+ n -= 4;
+ }
+
+ switch (n)
+ {
+ case 0:
+ break;
+ case 1:
+ __asm_clear_1 (dst, retn);
+ break;
+ case 2:
+ __asm_clear_2 (dst, retn);
+ break;
+ case 3:
+ __asm_clear_3 (dst, retn);
+ break;
+ }
+
+ return retn;
+}
diff --git a/arch/cris/mm/Makefile b/arch/cris/mm/Makefile
new file mode 100644
index 000000000..d1d21a7b7
--- /dev/null
+++ b/arch/cris/mm/Makefile
@@ -0,0 +1,13 @@
+#
+# Makefile for the linux cris-specific parts of the memory manager.
+#
+# 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 definition is now in the main makefile...
+
+O_TARGET := mm.o
+obj-y := init.o fault.o tlb.o extable.o
+
+include $(TOPDIR)/Rules.make
diff --git a/arch/cris/mm/extable.c b/arch/cris/mm/extable.c
new file mode 100644
index 000000000..a4cf00f14
--- /dev/null
+++ b/arch/cris/mm/extable.c
@@ -0,0 +1,55 @@
+/*
+ * linux/arch/cris/mm/extable.c
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <asm/uaccess.h>
+
+extern const struct exception_table_entry _start___ex_table[];
+extern const struct exception_table_entry _stop___ex_table[];
+
+static inline unsigned long
+search_one_table(const struct exception_table_entry *first,
+ const struct exception_table_entry *last,
+ unsigned long value)
+{
+ while (first <= last) {
+ const struct exception_table_entry *mid;
+ long diff;
+
+ mid = (last - first) / 2 + first;
+ diff = mid->insn - value;
+ if (diff == 0)
+ return mid->fixup;
+ else if (diff < 0)
+ first = mid+1;
+ else
+ last = mid-1;
+ }
+ return 0;
+}
+
+unsigned long
+search_exception_table(unsigned long addr)
+{
+ unsigned long ret;
+
+#ifndef CONFIG_MODULES
+ /* There is only the kernel to search. */
+ ret = search_one_table(_start___ex_table, _stop___ex_table-1, addr);
+ if (ret) return ret;
+#else
+ /* The kernel is the last "module" -- no need to treat it special. */
+ struct module *mp;
+ for (mp = module_list; mp != NULL; mp = mp->next) {
+ if (mp->ex_table_start == NULL)
+ continue;
+ ret = search_one_table(mp->ex_table_start,
+ mp->ex_table_end - 1, addr);
+ if (ret) return ret;
+ }
+#endif
+
+ return 0;
+}
diff --git a/arch/cris/mm/fault.c b/arch/cris/mm/fault.c
new file mode 100644
index 000000000..a4bb237b4
--- /dev/null
+++ b/arch/cris/mm/fault.c
@@ -0,0 +1,390 @@
+/*
+ * linux/arch/cris/mm/fault.c
+ *
+ * Copyright (C) 2000 Axis Communications AB
+ *
+ * Authors: Bjorn Wesen
+ *
+ * $Log: fault.c,v $
+ * Revision 1.8 2000/11/22 14:45:31 bjornw
+ * * 2.4.0-test10 removed the set_pgdir instantaneous kernel global mapping
+ * into all processes. Instead we fill in the missing PTE entries on demand.
+ *
+ * Revision 1.7 2000/11/21 16:39:09 bjornw
+ * fixup switches frametype
+ *
+ * Revision 1.6 2000/11/17 16:54:08 bjornw
+ * More detailed siginfo reporting
+ *
+ *
+ */
+
+#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/interrupt.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/pgtable.h>
+#include <asm/uaccess.h>
+#include <asm/svinto.h>
+
+extern void die_if_kernel(const char *,struct pt_regs *,long);
+
+asmlinkage void do_invalid_op (struct pt_regs *, unsigned long);
+asmlinkage void do_page_fault(unsigned long address, struct pt_regs *regs,
+ int error_code);
+
+/* debug of low-level TLB reload */
+#define D(x)
+/* debug of higher-level faults */
+#define DPG(x)
+
+/* fast TLB-fill fault handler */
+
+void
+handle_mmu_bus_fault(struct pt_regs *regs)
+{
+ int cause, select;
+ int index;
+ int page_id;
+ int miss, we, acc, inv;
+ struct mm_struct *mm = current->active_mm;
+ pmd_t *pmd;
+ pte_t pte;
+ int errcode = 0;
+ unsigned long address;
+
+ cause = *R_MMU_CAUSE;
+ select = *R_TLB_SELECT;
+
+ address = cause & PAGE_MASK; /* get faulting address */
+
+ page_id = IO_EXTRACT(R_MMU_CAUSE, page_id, cause);
+ miss = IO_EXTRACT(R_MMU_CAUSE, miss_excp, cause);
+ we = IO_EXTRACT(R_MMU_CAUSE, we_excp, cause);
+ acc = IO_EXTRACT(R_MMU_CAUSE, acc_excp, cause);
+ inv = IO_EXTRACT(R_MMU_CAUSE, inv_excp, cause);
+ index = IO_EXTRACT(R_TLB_SELECT, index, select);
+
+ D(printk("bus_fault from IRP 0x%x: addr 0x%x, miss %d, inv %d, we %d, acc %d, "
+ "idx %d pid %d\n",
+ regs->irp, address, miss, inv, we, acc, index, page_id));
+
+ /* for a miss, we need to reload the TLB entry */
+
+ if(miss) {
+
+ /* see if the pte exists at all */
+
+ pmd = (pmd_t *)pgd_offset(mm, address);
+ if(pmd_none(*pmd))
+ goto dofault;
+ if(pmd_bad(*pmd)) {
+ printk("bad pgdir entry 0x%x at 0x%x\n", *pmd, pmd);
+ pmd_clear(pmd);
+ return;
+ }
+ pte = *pte_offset(pmd, address);
+ if(!pte_present(pte))
+ goto dofault;
+
+ D(printk(" found pte %x pg %x ", pte_val(pte), pte_page(pte)));
+ D(
+ {
+ if(pte_val(pte) & _PAGE_SILENT_WRITE)
+ printk("Silent-W ");
+ if(pte_val(pte) & _PAGE_KERNEL)
+ printk("Kernel ");
+ if(pte_val(pte) & _PAGE_SILENT_READ)
+ printk("Silent-R ");
+ if(pte_val(pte) & _PAGE_GLOBAL)
+ printk("Global ");
+ if(pte_val(pte) & _PAGE_PRESENT)
+ printk("Present ");
+ if(pte_val(pte) & _PAGE_ACCESSED)
+ printk("Accessed ");
+ if(pte_val(pte) & _PAGE_MODIFIED)
+ printk("Modified ");
+ if(pte_val(pte) & _PAGE_READ)
+ printk("Readable ");
+ if(pte_val(pte) & _PAGE_WRITE)
+ printk("Writeable ");
+ printk("\n");
+ });
+
+ /* load up the chosen TLB entry
+ * this assumes the pte format is the same as the TLB_LO layout.
+ *
+ * the write to R_TLB_LO also writes the vpn and page_id fields from
+ * R_MMU_CAUSE, which we in this case obviously want to keep
+ */
+
+ *R_TLB_LO = pte_val(pte);
+
+ return;
+ }
+
+ errcode = 0x01 | (we << 1);
+
+ dofault:
+ /* leave it to the MM system fault handler below */
+ D(printk("do_page_fault %p errcode %d\n", address, errcode));
+ do_page_fault(address, regs, errcode);
+}
+
+/*
+ * This routine handles page faults. It determines the address,
+ * and the problem, and then passes it off to one of the appropriate
+ * routines.
+ *
+ * Notice that the address we're given is aligned to the page the fault
+ * occured in, since we only get the PFN in R_MMU_CAUSE not the complete
+ * address.
+ *
+ * error_code:
+ * bit 0 == 0 means no page found, 1 means protection fault
+ * bit 1 == 0 means read, 1 means write
+ *
+ * If this routine detects a bad access, it returns 1, otherwise it
+ * returns 0.
+ */
+
+asmlinkage void
+do_page_fault(unsigned long address, struct pt_regs *regs,
+ int error_code)
+{
+ struct task_struct *tsk;
+ struct mm_struct *mm;
+ struct vm_area_struct * vma;
+ int writeaccess;
+ int fault;
+ unsigned long fixup;
+ siginfo_t info;
+
+ 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.
+ *
+ * NOTE2: This is done so that, when updating the vmalloc
+ * mappings we don't have to walk all processes pgdirs and
+ * add the high mappings all at once. Instead we do it as they
+ * are used.
+ *
+ * TODO: On CRIS, we have a PTE Global bit which should be set in
+ * all the PTE's related to vmalloc in all processes - that means if
+ * we switch process and a vmalloc PTE is still in the TLB, it won't
+ * need to be reloaded. It's an optimization.
+ *
+ * Linux/CRIS's kernel is not page-mapped, so the comparision below
+ * should really be >= VMALLOC_START, however, kernel fixup errors
+ * will be handled more quickly by going through vmalloc_fault and then
+ * into bad_area_nosemaphore than falling through the find_vma user-mode
+ * tests.
+ */
+
+ if (address >= TASK_SIZE)
+ goto vmalloc_fault;
+
+ mm = tsk->mm;
+ writeaccess = error_code & 2;
+ info.si_code = SEGV_MAPERR;
+
+ /*
+ * If we're in an interrupt or have no user
+ * context, we must not take the fault..
+ */
+
+ if (in_interrupt() || !mm)
+ goto no_context;
+
+ down(&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 (user_mode(regs)) {
+ /*
+ * accessing the stack below usp is always a bug.
+ * we get page-aligned addresses so we can only check
+ * if we're within a page from usp, but that might be
+ * enough to catch brutal errors at least.
+ */
+ if (address + PAGE_SIZE < rdusp())
+ 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;
+
+ /* first do some preliminary protection checks */
+
+ if (writeaccess) {
+ if (!(vma->vm_flags & VM_WRITE))
+ goto bad_area;
+ } else {
+ if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
+ goto bad_area;
+ }
+
+ /*
+ * If for any reason at all we couldn't handle the fault,
+ * make sure we exit gracefully rather than endlessly redo
+ * the fault.
+ */
+
+ switch (handle_mm_fault(mm, vma, address, writeaccess)) {
+ case 1:
+ tsk->min_flt++;
+ break;
+ case 2:
+ tsk->maj_flt++;
+ break;
+ case 0:
+ goto do_sigbus;
+ default:
+ goto out_of_memory;
+ }
+
+ up(&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(&mm->mmap_sem);
+
+ bad_area_nosemaphore:
+ DPG(show_registers(regs));
+
+ /* User mode accesses just cause a SIGSEGV */
+
+ if (user_mode(regs)) {
+ 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?
+ *
+ * (The kernel has valid exception-points in the source
+ * when it acesses user-memory. When it fails in one
+ * of those points, we find it in a table and do a jump
+ * to some fixup code that loads an appropriate error
+ * code)
+ */
+
+ if ((fixup = search_exception_table(regs->irp)) != 0) {
+ regs->irp = fixup;
+ regs->frametype = CRIS_FRAME_FIXUP;
+ D(printk("doing fixup to 0x%x\n", fixup));
+ return;
+ }
+
+/*
+ * Oops. The kernel tried to access some bad page. We'll have to
+ * terminate things with extreme prejudice.
+ */
+ if ((unsigned long) (address) < PAGE_SIZE)
+ printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
+ else
+ printk(KERN_ALERT "Unable to handle kernel access");
+ printk(" at virtual address %08lx\n",address);
+
+ die_if_kernel("Oops", regs, error_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(&mm->mmap_sem);
+ printk("VM: killing process %s\n", tsk->comm);
+ if(user_mode(regs))
+ do_exit(SIGKILL);
+ goto no_context;
+
+ do_sigbus:
+ up(&mm->mmap_sem);
+
+ /*
+ * Send a sigbus, regardless of whether we were in kernel
+ * or user mode.
+ */
+ info.si_code = 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 (!user_mode(regs))
+ goto no_context;
+ return;
+
+vmalloc_fault:
+ {
+ /*
+ * Synchronize this task's top level page-table
+ * with the 'reference' page table.
+ */
+ int offset = pgd_index(address);
+ pgd_t *pgd, *pgd_k;
+ pmd_t *pmd, *pmd_k;
+
+ pgd = tsk->active_mm->pgd + offset;
+ pgd_k = init_mm.pgd + offset;
+
+ if (!pgd_present(*pgd)) {
+ if (!pgd_present(*pgd_k))
+ goto bad_area_nosemaphore;
+ set_pgd(pgd, *pgd_k);
+ return;
+ }
+
+ pmd = pmd_offset(pgd, address);
+ pmd_k = pmd_offset(pgd_k, address);
+
+ if (pmd_present(*pmd) || !pmd_present(*pmd_k))
+ goto bad_area_nosemaphore;
+ set_pmd(pmd, *pmd_k);
+ return;
+ }
+
+}
diff --git a/arch/cris/mm/init.c b/arch/cris/mm/init.c
new file mode 100644
index 000000000..3d0ceeffb
--- /dev/null
+++ b/arch/cris/mm/init.c
@@ -0,0 +1,506 @@
+/*
+ * linux/arch/cris/mm/init.c
+ *
+ * Copyright (C) 1995 Linus Torvalds
+ * Copyright (C) 2000 Axis Communications AB
+ *
+ * Authors: Bjorn Wesen (bjornw@axis.com)
+ *
+ * $Log: init.c,v $
+ * Revision 1.15 2001/01/10 21:12:10 bjornw
+ * loops_per_sec -> loops_per_jiffy
+ *
+ * Revision 1.14 2000/11/22 16:23:20 bjornw
+ * Initialize totalhigh counters to 0 to make /proc/meminfo look nice.
+ *
+ * Revision 1.13 2000/11/21 16:37:51 bjornw
+ * Temporarily disable initmem freeing
+ *
+ * Revision 1.12 2000/11/21 13:55:07 bjornw
+ * Use CONFIG_CRIS_LOW_MAP for the low VM map instead of explicit CPU type
+ *
+ * Revision 1.11 2000/10/06 12:38:22 bjornw
+ * Cast empty_bad_page correctly (should really be of * type from the start..
+ *
+ * Revision 1.10 2000/10/04 16:53:57 bjornw
+ * Fix memory-map due to LX features
+ *
+ * Revision 1.9 2000/09/13 15:47:49 bjornw
+ * Wrong count in reserved-pages loop
+ *
+ * Revision 1.8 2000/09/13 14:35:10 bjornw
+ * 2.4.0-test8 added a new arg to free_area_init_node
+ *
+ * Revision 1.7 2000/08/17 15:35:55 bjornw
+ * 2.4.0-test6 removed MAP_NR and inserted virt_to_page
+ *
+ *
+ */
+
+#include <linux/config.h>
+#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/swap.h>
+#include <linux/smp.h>
+#include <linux/bootmem.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/dma.h>
+#include <asm/svinto.h>
+
+static unsigned long totalram_pages;
+
+struct pgtable_cache_struct quicklists; /* see asm/pgalloc.h */
+
+const char bad_pmd_string[] = "Bad pmd in pte_alloc: %08lx\n";
+
+extern void die_if_kernel(char *,struct pt_regs *,long);
+extern void show_net_buffers(void);
+extern void tlb_init(void);
+
+/*
+ * empty_bad_page is the page that is used for page faults when linux
+ * is out-of-memory. Older versions of linux just did a
+ * do_exit(), but using this instead means there is less risk
+ * for a process dying in kernel mode, possibly leaving a inode
+ * unused etc..
+ *
+ * the main point is that when a page table error occurs, we want to get
+ * out of the kernel safely before killing the process, so we need something
+ * to feed the MMU with when the fault occurs even if we don't have any
+ * real PTE's or page tables.
+ *
+ * empty_bad_page_table is the accompanying page-table: it is initialized
+ * to point to empty_bad_page writable-shared entries.
+ *
+ * empty_zero_page is a special page that is used for zero-initialized
+ * data and COW.
+ */
+
+unsigned long empty_bad_page_table;
+unsigned long empty_bad_page;
+unsigned long empty_zero_page;
+
+pte_t * __bad_pagetable(void)
+{
+ /* somehow it is enough to just clear it and not fill it with
+ * bad page PTE's...
+ */
+ memset((void *)empty_bad_page_table, 0, PAGE_SIZE);
+
+ return (pte_t *) empty_bad_page_table;
+}
+
+pte_t __bad_page(void)
+{
+
+ /* clear the empty_bad_page page. this should perhaps be
+ * a more simple inlined loop like it is on the other
+ * architectures.
+ */
+
+ memset((void *)empty_bad_page, 0, PAGE_SIZE);
+
+ return pte_mkdirty(__mk_pte((void *)empty_bad_page, PAGE_SHARED));
+}
+
+static pte_t * get_bad_pte_table(void)
+{
+ pte_t *empty_bad_pte_table = (pte_t *)empty_bad_page_table;
+ pte_t v;
+ int i;
+
+ v = __bad_page();
+
+ for (i = 0; i < PAGE_SIZE/sizeof(pte_t); i++)
+ empty_bad_pte_table[i] = v;
+
+ return empty_bad_pte_table;
+}
+
+void __handle_bad_pmd(pmd_t *pmd)
+{
+ pmd_ERROR(*pmd);
+ pmd_set(pmd, get_bad_pte_table());
+}
+
+void __handle_bad_pmd_kernel(pmd_t *pmd)
+{
+ pmd_ERROR(*pmd);
+ pmd_set_kernel(pmd, get_bad_pte_table());
+}
+
+pte_t *get_pte_kernel_slow(pmd_t *pmd, unsigned long offset)
+{
+ pte_t *pte;
+
+ pte = (pte_t *) __get_free_page(GFP_KERNEL);
+ if (pmd_none(*pmd)) {
+ if (pte) {
+ clear_page(pte);
+ pmd_set_kernel(pmd, pte);
+ return pte + offset;
+ }
+ pmd_set_kernel(pmd, get_bad_pte_table());
+ return NULL;
+ }
+ free_page((unsigned long)pte);
+ if (pmd_bad(*pmd)) {
+ __handle_bad_pmd_kernel(pmd);
+ return NULL;
+ }
+ return (pte_t *) pmd_page(*pmd) + offset;
+}
+
+pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset)
+{
+ pte_t *pte;
+
+ pte = (pte_t *) __get_free_page(GFP_KERNEL);
+ if (pmd_none(*pmd)) {
+ if (pte) {
+ clear_page(pte);
+ pmd_set(pmd, pte);
+ return pte + offset;
+ }
+ pmd_set(pmd, get_bad_pte_table());
+ return NULL;
+ }
+ free_page((unsigned long)pte);
+ if (pmd_bad(*pmd)) {
+ __handle_bad_pmd(pmd);
+ return NULL;
+ }
+ return (pte_t *) pmd_page(*pmd) + offset;
+}
+
+#ifndef CONFIG_NO_PGT_CACHE
+struct pgtable_cache_struct quicklists;
+
+/* trim the page-table cache if necessary */
+
+int do_check_pgt_cache(int low, int high)
+{
+ int freed = 0;
+
+ if(pgtable_cache_size > high) {
+ do {
+ if(pgd_quicklist) {
+ free_pgd_slow(get_pgd_fast());
+ freed++;
+ }
+ if(pmd_quicklist) {
+ free_pmd_slow(get_pmd_fast());
+ freed++;
+ }
+ if(pte_quicklist) {
+ free_pte_slow(get_pte_fast());
+ freed++;
+ }
+ } while(pgtable_cache_size > low);
+ }
+ return freed;
+}
+#else
+int do_check_pgt_cache(int low, int high)
+{
+ return 0;
+}
+#endif
+
+void show_mem(void)
+{
+ int i,free = 0,total = 0,cached = 0, reserved = 0, nonshared = 0;
+ int shared = 0;
+
+ printk("\nMem-info:\n");
+ show_free_areas();
+ printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10));
+ i = max_mapnr;
+ while (i-- > 0) {
+ total++;
+ if (PageReserved(mem_map+i))
+ reserved++;
+ else if (PageSwapCache(mem_map+i))
+ cached++;
+ else if (!page_count(mem_map+i))
+ free++;
+ else if (page_count(mem_map+i) == 1)
+ nonshared++;
+ else
+ shared += page_count(mem_map+i) - 1;
+ }
+ printk("%d pages of RAM\n",total);
+ printk("%d free pages\n",free);
+ printk("%d reserved pages\n",reserved);
+ printk("%d pages nonshared\n",nonshared);
+ printk("%d pages shared\n",shared);
+ printk("%d pages swap cached\n",cached);
+ printk("%ld pages in page table cache\n",pgtable_cache_size);
+ show_buffers();
+}
+
+/*
+ * The kernel is already mapped with a kernel segment at kseg_c so
+ * we don't need to map it with a page table. However head.S also
+ * temporarily mapped it at kseg_4 so we should set up the ksegs again,
+ * clear the TLB and do some other paging setup stuff.
+ */
+
+void __init
+paging_init(void)
+{
+ int i;
+ unsigned long zones_size[MAX_NR_ZONES];
+
+ printk("Setting up paging and the MMU.\n");
+
+ /* clear out the init_mm.pgd that will contain the kernel's mappings */
+
+ for(i = 0; i < PTRS_PER_PGD; i++)
+ swapper_pg_dir[i] = __pgd(0);
+
+ /* initialise the TLB (tlb.c) */
+
+ tlb_init();
+
+ /* see README.mm for details on the KSEG setup */
+
+#ifdef CONFIG_CRIS_LOW_MAP
+
+ /* Etrax-100 LX version 1 has a bug so that we cannot map anything
+ * across the 0x80000000 boundary, so we need to shrink the user-virtual
+ * area to 0x50000000 instead of 0xb0000000 and map things slightly
+ * different. The unused areas are marked as paged so that we can catch
+ * freak kernel accesses there.
+ *
+ * The Juliette chip is mapped at 0xa so we pass that segment straight
+ * through. We cannot vremap it because the vmalloc area is below 0x8
+ * and Juliette needs an uncached area above 0x8.
+ */
+
+ *R_MMU_KSEG = ( IO_STATE(R_MMU_KSEG, seg_f, page ) |
+ IO_STATE(R_MMU_KSEG, seg_e, seg ) | /* uncached flash */
+ IO_STATE(R_MMU_KSEG, seg_d, page ) |
+ IO_STATE(R_MMU_KSEG, seg_c, page ) |
+ IO_STATE(R_MMU_KSEG, seg_b, seg ) | /* kernel reg area */
+ IO_STATE(R_MMU_KSEG, seg_a, seg ) | /* Juliette etc. */
+ IO_STATE(R_MMU_KSEG, seg_9, page ) |
+ IO_STATE(R_MMU_KSEG, seg_8, page ) |
+ IO_STATE(R_MMU_KSEG, seg_7, page ) | /* kernel vmalloc area */
+ IO_STATE(R_MMU_KSEG, seg_6, seg ) | /* kernel DRAM area */
+ IO_STATE(R_MMU_KSEG, seg_5, seg ) | /* cached flash */
+ IO_STATE(R_MMU_KSEG, seg_4, page ) | /* user area */
+ IO_STATE(R_MMU_KSEG, seg_3, page ) | /* user area */
+ IO_STATE(R_MMU_KSEG, seg_2, page ) | /* user area */
+ IO_STATE(R_MMU_KSEG, seg_1, page ) | /* user area */
+ IO_STATE(R_MMU_KSEG, seg_0, page ) ); /* user area */
+
+ *R_MMU_KBASE_HI = ( IO_FIELD(R_MMU_KBASE_HI, base_f, 0x0 ) |
+ IO_FIELD(R_MMU_KBASE_HI, base_e, 0x8 ) |
+ IO_FIELD(R_MMU_KBASE_HI, base_d, 0x0 ) |
+ IO_FIELD(R_MMU_KBASE_HI, base_c, 0x0 ) |
+ IO_FIELD(R_MMU_KBASE_HI, base_b, 0xb ) |
+ IO_FIELD(R_MMU_KBASE_HI, base_a, 0xa ) |
+ IO_FIELD(R_MMU_KBASE_HI, base_9, 0x0 ) |
+ IO_FIELD(R_MMU_KBASE_HI, base_8, 0x0 ) );
+
+ *R_MMU_KBASE_LO = ( IO_FIELD(R_MMU_KBASE_LO, base_7, 0x0 ) |
+ IO_FIELD(R_MMU_KBASE_LO, base_6, 0x4 ) |
+ IO_FIELD(R_MMU_KBASE_LO, base_5, 0x0 ) |
+ IO_FIELD(R_MMU_KBASE_LO, base_4, 0x0 ) |
+ IO_FIELD(R_MMU_KBASE_LO, base_3, 0x0 ) |
+ IO_FIELD(R_MMU_KBASE_LO, base_2, 0x0 ) |
+ IO_FIELD(R_MMU_KBASE_LO, base_1, 0x0 ) |
+ IO_FIELD(R_MMU_KBASE_LO, base_0, 0x0 ) );
+#else
+ /* This code is for the hopefully corrected Etrax-100 LX version 2... */
+
+ *R_MMU_KSEG = ( IO_STATE(R_MMU_KSEG, seg_f, seg ) | /* cached flash */
+ IO_STATE(R_MMU_KSEG, seg_e, seg ) | /* uncached flash */
+ IO_STATE(R_MMU_KSEG, seg_d, page ) | /* vmalloc area */
+ IO_STATE(R_MMU_KSEG, seg_c, seg ) | /* kernel area */
+ IO_STATE(R_MMU_KSEG, seg_b, seg ) | /* kernel reg area */
+ IO_STATE(R_MMU_KSEG, seg_a, page ) | /* user area */
+ IO_STATE(R_MMU_KSEG, seg_9, page ) |
+ IO_STATE(R_MMU_KSEG, seg_8, page ) |
+ IO_STATE(R_MMU_KSEG, seg_7, page ) |
+ IO_STATE(R_MMU_KSEG, seg_6, page ) |
+ IO_STATE(R_MMU_KSEG, seg_5, page ) |
+ IO_STATE(R_MMU_KSEG, seg_4, page ) |
+ IO_STATE(R_MMU_KSEG, seg_3, page ) |
+ IO_STATE(R_MMU_KSEG, seg_2, page ) |
+ IO_STATE(R_MMU_KSEG, seg_1, page ) |
+ IO_STATE(R_MMU_KSEG, seg_0, page ) );
+
+ *R_MMU_KBASE_HI = ( IO_FIELD(R_MMU_KBASE_HI, base_f, 0x0 ) |
+ IO_FIELD(R_MMU_KBASE_HI, base_e, 0x8 ) |
+ IO_FIELD(R_MMU_KBASE_HI, base_d, 0x0 ) |
+ IO_FIELD(R_MMU_KBASE_HI, base_c, 0x4 ) |
+ IO_FIELD(R_MMU_KBASE_HI, base_b, 0xb ) |
+ IO_FIELD(R_MMU_KBASE_HI, base_a, 0x0 ) |
+ IO_FIELD(R_MMU_KBASE_HI, base_9, 0x0 ) |
+ IO_FIELD(R_MMU_KBASE_HI, base_8, 0x0 ) );
+
+ *R_MMU_KBASE_LO = ( IO_FIELD(R_MMU_KBASE_LO, base_7, 0x0 ) |
+ IO_FIELD(R_MMU_KBASE_LO, base_6, 0x0 ) |
+ IO_FIELD(R_MMU_KBASE_LO, base_5, 0x0 ) |
+ IO_FIELD(R_MMU_KBASE_LO, base_4, 0x0 ) |
+ IO_FIELD(R_MMU_KBASE_LO, base_3, 0x0 ) |
+ IO_FIELD(R_MMU_KBASE_LO, base_2, 0x0 ) |
+ IO_FIELD(R_MMU_KBASE_LO, base_1, 0x0 ) |
+ IO_FIELD(R_MMU_KBASE_LO, base_0, 0x0 ) );
+#endif
+
+ *R_MMU_CONTEXT = ( IO_FIELD(R_MMU_CONTEXT, page_id, 0 ) );
+
+ /* The MMU has been enabled ever since head.S but just to make
+ * it totally obvious we do it here as well.
+ */
+
+ *R_MMU_CTRL = ( IO_STATE(R_MMU_CTRL, inv_excp, enable ) |
+ IO_STATE(R_MMU_CTRL, acc_excp, enable ) |
+ IO_STATE(R_MMU_CTRL, we_excp, enable ) );
+
+ *R_MMU_ENABLE = IO_STATE(R_MMU_ENABLE, mmu_enable, enable);
+
+ /*
+ * initialize the bad page table and bad page to point
+ * to a couple of allocated pages
+ */
+
+ empty_bad_page_table = (unsigned long)alloc_bootmem_pages(PAGE_SIZE);
+ empty_bad_page = (unsigned long)alloc_bootmem_pages(PAGE_SIZE);
+ empty_zero_page = (unsigned long)alloc_bootmem_pages(PAGE_SIZE);
+ memset((void *)empty_zero_page, 0, PAGE_SIZE);
+
+ /* All pages are DMA'able in Etrax, so put all in the DMA'able zone */
+
+ zones_size[0] = ((unsigned long)high_memory - PAGE_OFFSET) >> PAGE_SHIFT;
+
+ for (i = 1; i < MAX_NR_ZONES; i++)
+ zones_size[i] = 0;
+
+ /* Use free_area_init_node instead of free_area_init, because the former
+ * is designed for systems where the DRAM starts at an address substantially
+ * higher than 0, like us (we start at PAGE_OFFSET). This saves space in the
+ * mem_map page array.
+ */
+
+ free_area_init_node(0, 0, 0, zones_size, PAGE_OFFSET, 0);
+}
+
+extern unsigned long loops_per_jiffy; /* init/main.c */
+unsigned long loops_per_usec;
+
+extern char _stext, _edata, _etext;
+extern char __init_begin, __init_end;
+
+void __init
+mem_init(void)
+{
+ int codesize, reservedpages, datasize, initsize;
+ unsigned long tmp;
+
+ if(!mem_map)
+ BUG();
+
+ /* max/min_low_pfn was set by setup.c
+ * now we just copy it to some other necessary places...
+ *
+ * high_memory was also set in setup.c
+ */
+
+ max_mapnr = num_physpages = max_low_pfn - min_low_pfn;
+
+ /* this will put all memory onto the freelists */
+ totalram_pages = free_all_bootmem();
+
+ reservedpages = 0;
+ for (tmp = 0; tmp < max_mapnr; tmp++) {
+ /*
+ * Only count reserved RAM pages
+ */
+ 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("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, "
+ "%dk init)\n" ,
+ (unsigned long) nr_free_pages() << (PAGE_SHIFT-10),
+ max_mapnr << (PAGE_SHIFT-10),
+ codesize >> 10,
+ reservedpages << (PAGE_SHIFT-10),
+ datasize >> 10,
+ initsize >> 10
+ );
+
+ /* HACK alert - calculate a loops_per_usec for asm/delay.h here
+ * since this is called just after calibrate_delay in init/main.c
+ * but before places which use udelay. cannot be in time.c since
+ * that is called _before_ calibrate_delay
+ */
+
+ loops_per_usec = (loops_per_jiffy * HZ) / 1000000;
+
+ return;
+}
+
+/* free the pages occupied by initialization code */
+
+void free_initmem(void)
+{
+#if 0
+ /* currently this is a bad idea since the cramfs image is catted onto
+ * the vmlinux image, and the end of that image is not page-padded so
+ * part of the cramfs image will be freed here
+ */
+ unsigned long addr;
+
+ addr = (unsigned long)(&__init_begin);
+ for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) {
+ ClearPageReserved(virt_to_page(addr));
+ set_page_count(virt_to_page(addr), 1);
+ free_page(addr);
+ totalram_pages++;
+ }
+ printk ("Freeing unused kernel memory: %dk freed\n",
+ (&__init_end - &__init_begin) >> 10);
+#endif
+}
+
+void si_meminfo(struct sysinfo *val)
+{
+ int i;
+
+ i = max_mapnr;
+ val->totalram = 0;
+ val->sharedram = 0;
+ val->freeram = nr_free_pages();
+ val->bufferram = atomic_read(&buffermem_pages);
+ while (i-- > 0) {
+ if (PageReserved(mem_map+i))
+ continue;
+ val->totalram++;
+ if (!atomic_read(&mem_map[i].count))
+ continue;
+ val->sharedram += atomic_read(&mem_map[i].count) - 1;
+ }
+ val->mem_unit = PAGE_SIZE;
+ val->totalhigh = 0;
+ val->freehigh = 0;
+}
diff --git a/arch/cris/mm/tlb.c b/arch/cris/mm/tlb.c
new file mode 100644
index 000000000..ed74305ae
--- /dev/null
+++ b/arch/cris/mm/tlb.c
@@ -0,0 +1,301 @@
+/*
+ * linux/arch/cris/mm/tlb.c
+ *
+ * Copyright (C) 2000 Axis Communications AB
+ *
+ * Authors: Bjorn Wesen (bjornw@axis.com)
+ *
+ */
+
+#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/init.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/pgtable.h>
+#include <asm/svinto.h>
+
+#define D(x)
+
+/* CRIS in Etrax100LX TLB */
+
+#define NUM_TLB_ENTRIES 64
+#define NUM_PAGEID 64
+#define INVALID_PAGEID 63
+#define NO_CONTEXT -1
+
+/* The TLB can host up to 64 different mm contexts at the same time.
+ * The running context is R_MMU_CONTEXT, and each TLB entry contains a
+ * page_id that has to match to give a hit. In page_id_map, we keep track
+ * of which mm's we have assigned which page_id's, so that we know when
+ * to invalidate TLB entries.
+ *
+ * The last page_id is never running - it is used as an invalid page_id
+ * so we can make TLB entries that will never match.
+ */
+
+struct mm_struct *page_id_map[NUM_PAGEID];
+
+static int map_replace_ptr = 1; /* which page_id_map entry to replace next */
+
+/* invalidate all TLB entries */
+
+void
+flush_tlb_all()
+{
+ int i;
+
+ /* the vpn of i & 0xf is so we dont write similar TLB entries
+ * in the same 4-way entry group. details..
+ */
+
+ for(i = 0; i < NUM_TLB_ENTRIES; i++) {
+ *R_TLB_SELECT = ( IO_FIELD(R_TLB_SELECT, index, i) );
+
+ *R_TLB_HI = ( IO_FIELD(R_TLB_HI, page_id, INVALID_PAGEID ) |
+ IO_FIELD(R_TLB_HI, vpn, i & 0xf ) );
+
+ *R_TLB_LO = ( IO_STATE(R_TLB_LO, global,no ) |
+ IO_STATE(R_TLB_LO, valid, no ) |
+ IO_STATE(R_TLB_LO, kernel,no ) |
+ IO_STATE(R_TLB_LO, we, no ) |
+ IO_FIELD(R_TLB_LO, pfn, 0 ) );
+ }
+ D(printk("tlb: flushed all\n"));
+}
+
+/* invalidate the selected mm context only */
+
+void
+flush_tlb_mm(struct mm_struct *mm)
+{
+ int i;
+ int page_id = mm->context;
+
+ D(printk("tlb: flush mm context %d (%p)\n", page_id, mm));
+
+ if(page_id == NO_CONTEXT)
+ return;
+
+ /* mark the TLB entries that match the page_id as invalid.
+ * here we could also check the _PAGE_GLOBAL bit and NOT flush
+ * global pages. is it worth the extra I/O ?
+ */
+
+ for(i = 0; i < NUM_TLB_ENTRIES; i++) {
+ *R_TLB_SELECT = IO_FIELD(R_TLB_SELECT, index, i);
+ if (IO_EXTRACT(R_TLB_HI, page_id, *R_TLB_HI) == page_id) {
+ *R_TLB_HI = ( IO_FIELD(R_TLB_HI, page_id, INVALID_PAGEID ) |
+ IO_FIELD(R_TLB_HI, vpn, i & 0xf ) );
+
+ *R_TLB_LO = ( IO_STATE(R_TLB_LO, global,no ) |
+ IO_STATE(R_TLB_LO, valid, no ) |
+ IO_STATE(R_TLB_LO, kernel,no ) |
+ IO_STATE(R_TLB_LO, we, no ) |
+ IO_FIELD(R_TLB_LO, pfn, 0 ) );
+ }
+ }
+}
+
+/* invalidate a single page */
+
+void
+flush_tlb_page(struct vm_area_struct *vma,
+ unsigned long addr)
+{
+ struct mm_struct *mm = vma->vm_mm;
+ int page_id = mm->context;
+ int i;
+
+ D(printk("tlb: flush page %p in context %d (%p)\n", addr, page_id, mm));
+
+ if(page_id == NO_CONTEXT)
+ return;
+
+ addr &= PAGE_MASK; /* perhaps not necessary */
+
+ /* invalidate those TLB entries that match both the mm context
+ * and the virtual address requested
+ */
+
+ for(i = 0; i < NUM_TLB_ENTRIES; i++) {
+ unsigned long tlb_hi;
+ *R_TLB_SELECT = IO_FIELD(R_TLB_SELECT, index, i);
+ tlb_hi = *R_TLB_HI;
+ if (IO_EXTRACT(R_TLB_HI, page_id, tlb_hi) == page_id &&
+ (tlb_hi & PAGE_MASK) == addr) {
+ *R_TLB_HI = IO_FIELD(R_TLB_HI, page_id, INVALID_PAGEID ) |
+ addr; /* same addr as before works. */
+
+ *R_TLB_LO = ( IO_STATE(R_TLB_LO, global,no ) |
+ IO_STATE(R_TLB_LO, valid, no ) |
+ IO_STATE(R_TLB_LO, kernel,no ) |
+ IO_STATE(R_TLB_LO, we, no ) |
+ IO_FIELD(R_TLB_LO, pfn, 0 ) );
+ }
+ }
+}
+
+/* invalidate a page range */
+
+void
+flush_tlb_range(struct mm_struct *mm,
+ unsigned long start,
+ unsigned long end)
+{
+ int page_id = mm->context;
+ int i;
+
+ D(printk("tlb: flush range %p<->%p in context %d (%p)\n",
+ start, end, page_id, mm));
+
+ if(page_id == NO_CONTEXT)
+ return;
+
+ start &= PAGE_MASK; /* probably not necessary */
+ end &= PAGE_MASK; /* dito */
+
+ /* invalidate those TLB entries that match both the mm context
+ * and the virtual address range
+ */
+
+ for(i = 0; i < NUM_TLB_ENTRIES; i++) {
+ unsigned long tlb_hi, vpn;
+ *R_TLB_SELECT = IO_FIELD(R_TLB_SELECT, index, i);
+ tlb_hi = *R_TLB_HI;
+ vpn = tlb_hi & PAGE_MASK;
+ if (IO_EXTRACT(R_TLB_HI, page_id, tlb_hi) == page_id &&
+ vpn >= start && vpn < end) {
+ *R_TLB_HI = ( IO_FIELD(R_TLB_HI, page_id, INVALID_PAGEID ) |
+ IO_FIELD(R_TLB_HI, vpn, i & 0xf ) );
+
+ *R_TLB_LO = ( IO_STATE(R_TLB_LO, global,no ) |
+ IO_STATE(R_TLB_LO, valid, no ) |
+ IO_STATE(R_TLB_LO, kernel,no ) |
+ IO_STATE(R_TLB_LO, we, no ) |
+ IO_FIELD(R_TLB_LO, pfn, 0 ) );
+ }
+ }
+}
+
+/*
+ * Initialize the context related info for a new mm_struct
+ * instance.
+ */
+
+int
+init_new_context(struct task_struct *tsk, struct mm_struct *mm)
+{
+ mm->context = NO_CONTEXT;
+ return 0;
+}
+
+/* the following functions are similar to those used in the PPC port */
+
+static inline void
+alloc_context(struct mm_struct *mm)
+{
+ struct mm_struct *old_mm;
+
+ D(printk("tlb: alloc context %d (%p)\n", map_replace_ptr, mm));
+
+ /* did we replace an mm ? */
+
+ old_mm = page_id_map[map_replace_ptr];
+
+ if(old_mm) {
+ /* throw out any TLB entries belonging to the mm we replace
+ * in the map
+ */
+ flush_tlb_mm(old_mm);
+
+ old_mm->context = NO_CONTEXT;
+ }
+
+ /* insert it into the page_id_map */
+
+ mm->context = map_replace_ptr;
+ page_id_map[map_replace_ptr] = mm;
+
+ map_replace_ptr++;
+
+ if(map_replace_ptr == INVALID_PAGEID)
+ map_replace_ptr = 0; /* wrap around */
+
+}
+
+/*
+ * if needed, get a new MMU context for the mm. otherwise nothing is done.
+ */
+
+void
+get_mmu_context(struct mm_struct *mm)
+{
+ if(mm->context == NO_CONTEXT)
+ alloc_context(mm);
+}
+
+/* called in schedule() just before actually doing the switch_to */
+
+void
+switch_mm(struct mm_struct *prev, struct mm_struct *next,
+ struct task_struct *tsk, int cpu)
+{
+ /* make sure we have a context */
+
+ get_mmu_context(next);
+
+ /* switch context in the MMU */
+
+ D(printk("switching mmu_context to %d (%p)\n", next->context, next));
+
+ *R_MMU_CONTEXT = IO_FIELD(R_MMU_CONTEXT, page_id, next->context);
+}
+
+
+/* called by __exit_mm to destroy the used MMU context if any before
+ * destroying the mm itself. this is only called when the last user of the mm
+ * drops it.
+ *
+ * the only thing we really need to do here is mark the used PID slot
+ * as empty.
+ */
+
+void
+destroy_context(struct mm_struct *mm)
+{
+ if(mm->context != NO_CONTEXT) {
+ D(printk("destroy_context %d (%p)\n", mm->context, mm));
+ flush_tlb_mm(mm); /* TODO this might be redundant ? */
+ page_id_map[mm->context] = NULL;
+ /* mm->context = NO_CONTEXT; redundant.. mm will be freed */
+ }
+}
+
+/* called once during VM initialization, from init.c */
+
+void __init
+tlb_init(void)
+{
+ int i;
+
+ /* clear the page_id map */
+
+ for(i = 0; i < 64; i++)
+ page_id_map[i] = NULL;
+
+ /* invalidate the entire TLB */
+
+ flush_tlb_all();
+
+ /* the init_mm has context 0 from the boot */
+
+ page_id_map[0] = &init_mm;
+}