mirror of https://gitee.com/openkylin/linux.git
Merge with /home/shaggy/git/linus-clean/
/home/shaggy/git/linus-clean/ /home/shaggy/git/linus-clean/ Signed-off-by: Dave Kleikamp <shaggy@austin.ibm.com>
This commit is contained in:
commit
da28c12089
6
CREDITS
6
CREDITS
|
@ -1624,10 +1624,10 @@ E: ajoshi@shell.unixbox.com
|
|||
D: fbdev hacking
|
||||
|
||||
N: Jesper Juhl
|
||||
E: juhl-lkml@dif.dk
|
||||
D: Various small janitor fixes, cleanups etc.
|
||||
E: jesper.juhl@gmail.com
|
||||
D: Various fixes, cleanups and minor features.
|
||||
S: Lemnosvej 1, 3.tv
|
||||
S: 2300 Copenhagen S
|
||||
S: 2300 Copenhagen S.
|
||||
S: Denmark
|
||||
|
||||
N: Jozsef Kadlecsik
|
||||
|
|
|
@ -65,6 +65,7 @@ o isdn4k-utils 3.1pre1 # isdnctrl 2>&1|grep version
|
|||
o nfs-utils 1.0.5 # showmount --version
|
||||
o procps 3.2.0 # ps --version
|
||||
o oprofile 0.9 # oprofiled --version
|
||||
o udev 058 # udevinfo -V
|
||||
|
||||
Kernel compilation
|
||||
==================
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
INFINIBAND MIDLAYER LOCKING
|
||||
|
||||
This guide is an attempt to make explicit the locking assumptions
|
||||
made by the InfiniBand midlayer. It describes the requirements on
|
||||
both low-level drivers that sit below the midlayer and upper level
|
||||
protocols that use the midlayer.
|
||||
|
||||
Sleeping and interrupt context
|
||||
|
||||
With the following exceptions, a low-level driver implementation of
|
||||
all of the methods in struct ib_device may sleep. The exceptions
|
||||
are any methods from the list:
|
||||
|
||||
create_ah
|
||||
modify_ah
|
||||
query_ah
|
||||
destroy_ah
|
||||
bind_mw
|
||||
post_send
|
||||
post_recv
|
||||
poll_cq
|
||||
req_notify_cq
|
||||
map_phys_fmr
|
||||
|
||||
which may not sleep and must be callable from any context.
|
||||
|
||||
The corresponding functions exported to upper level protocol
|
||||
consumers:
|
||||
|
||||
ib_create_ah
|
||||
ib_modify_ah
|
||||
ib_query_ah
|
||||
ib_destroy_ah
|
||||
ib_bind_mw
|
||||
ib_post_send
|
||||
ib_post_recv
|
||||
ib_req_notify_cq
|
||||
ib_map_phys_fmr
|
||||
|
||||
are therefore safe to call from any context.
|
||||
|
||||
In addition, the function
|
||||
|
||||
ib_dispatch_event
|
||||
|
||||
used by low-level drivers to dispatch asynchronous events through
|
||||
the midlayer is also safe to call from any context.
|
||||
|
||||
Reentrancy
|
||||
|
||||
All of the methods in struct ib_device exported by a low-level
|
||||
driver must be fully reentrant. The low-level driver is required to
|
||||
perform all synchronization necessary to maintain consistency, even
|
||||
if multiple function calls using the same object are run
|
||||
simultaneously.
|
||||
|
||||
The IB midlayer does not perform any serialization of function calls.
|
||||
|
||||
Because low-level drivers are reentrant, upper level protocol
|
||||
consumers are not required to perform any serialization. However,
|
||||
some serialization may be required to get sensible results. For
|
||||
example, a consumer may safely call ib_poll_cq() on multiple CPUs
|
||||
simultaneously. However, the ordering of the work completion
|
||||
information between different calls of ib_poll_cq() is not defined.
|
||||
|
||||
Callbacks
|
||||
|
||||
A low-level driver must not perform a callback directly from the
|
||||
same callchain as an ib_device method call. For example, it is not
|
||||
allowed for a low-level driver to call a consumer's completion event
|
||||
handler directly from its post_send method. Instead, the low-level
|
||||
driver should defer this callback by, for example, scheduling a
|
||||
tasklet to perform the callback.
|
||||
|
||||
The low-level driver is responsible for ensuring that multiple
|
||||
completion event handlers for the same CQ are not called
|
||||
simultaneously. The driver must guarantee that only one CQ event
|
||||
handler for a given CQ is running at a time. In other words, the
|
||||
following situation is not allowed:
|
||||
|
||||
CPU1 CPU2
|
||||
|
||||
low-level driver ->
|
||||
consumer CQ event callback:
|
||||
/* ... */
|
||||
ib_req_notify_cq(cq, ...);
|
||||
low-level driver ->
|
||||
/* ... */ consumer CQ event callback:
|
||||
/* ... */
|
||||
return from CQ event handler
|
||||
|
||||
The context in which completion event and asynchronous event
|
||||
callbacks run is not defined. Depending on the low-level driver, it
|
||||
may be process context, softirq context, or interrupt context.
|
||||
Upper level protocol consumers may not sleep in a callback.
|
||||
|
||||
Hot-plug
|
||||
|
||||
A low-level driver announces that a device is ready for use by
|
||||
consumers when it calls ib_register_device(), all initialization
|
||||
must be complete before this call. The device must remain usable
|
||||
until the driver's call to ib_unregister_device() has returned.
|
||||
|
||||
A low-level driver must call ib_register_device() and
|
||||
ib_unregister_device() from process context. It must not hold any
|
||||
semaphores that could cause deadlock if a consumer calls back into
|
||||
the driver across these calls.
|
||||
|
||||
An upper level protocol consumer may begin using an IB device as
|
||||
soon as the add method of its struct ib_client is called for that
|
||||
device. A consumer must finish all cleanup and free all resources
|
||||
relating to a device before returning from the remove method.
|
||||
|
||||
A consumer is permitted to sleep in its add and remove methods.
|
|
@ -28,13 +28,37 @@ Creating MAD agents
|
|||
|
||||
Receiving MADs
|
||||
|
||||
MADs are received using read(). The buffer passed to read() must be
|
||||
large enough to hold at least one struct ib_user_mad. For example:
|
||||
MADs are received using read(). The receive side now supports
|
||||
RMPP. The buffer passed to read() must be at least one
|
||||
struct ib_user_mad + 256 bytes. For example:
|
||||
|
||||
struct ib_user_mad mad;
|
||||
ret = read(fd, &mad, sizeof mad);
|
||||
if (ret != sizeof mad)
|
||||
If the buffer passed is not large enough to hold the received
|
||||
MAD (RMPP), the errno is set to ENOSPC and the length of the
|
||||
buffer needed is set in mad.length.
|
||||
|
||||
Example for normal MAD (non RMPP) reads:
|
||||
struct ib_user_mad *mad;
|
||||
mad = malloc(sizeof *mad + 256);
|
||||
ret = read(fd, mad, sizeof *mad + 256);
|
||||
if (ret != sizeof mad + 256) {
|
||||
perror("read");
|
||||
free(mad);
|
||||
}
|
||||
|
||||
Example for RMPP reads:
|
||||
struct ib_user_mad *mad;
|
||||
mad = malloc(sizeof *mad + 256);
|
||||
ret = read(fd, mad, sizeof *mad + 256);
|
||||
if (ret == -ENOSPC)) {
|
||||
length = mad.length;
|
||||
free(mad);
|
||||
mad = malloc(sizeof *mad + length);
|
||||
ret = read(fd, mad, sizeof *mad + length);
|
||||
}
|
||||
if (ret < 0) {
|
||||
perror("read");
|
||||
free(mad);
|
||||
}
|
||||
|
||||
In addition to the actual MAD contents, the other struct ib_user_mad
|
||||
fields will be filled in with information on the received MAD. For
|
||||
|
@ -50,18 +74,21 @@ Sending MADs
|
|||
|
||||
MADs are sent using write(). The agent ID for sending should be
|
||||
filled into the id field of the MAD, the destination LID should be
|
||||
filled into the lid field, and so on. For example:
|
||||
filled into the lid field, and so on. The send side does support
|
||||
RMPP so arbitrary length MAD can be sent. For example:
|
||||
|
||||
struct ib_user_mad mad;
|
||||
struct ib_user_mad *mad;
|
||||
|
||||
/* fill in mad.data */
|
||||
mad = malloc(sizeof *mad + mad_length);
|
||||
|
||||
mad.id = my_agent; /* req.id from agent registration */
|
||||
mad.lid = my_dest; /* in network byte order... */
|
||||
/* fill in mad->data */
|
||||
|
||||
mad->hdr.id = my_agent; /* req.id from agent registration */
|
||||
mad->hdr.lid = my_dest; /* in network byte order... */
|
||||
/* etc. */
|
||||
|
||||
ret = write(fd, &mad, sizeof mad);
|
||||
if (ret != sizeof mad)
|
||||
ret = write(fd, &mad, sizeof *mad + mad_length);
|
||||
if (ret != sizeof *mad + mad_length)
|
||||
perror("write");
|
||||
|
||||
Setting IsSM Capability Bit
|
||||
|
|
10
README
10
README
|
@ -87,6 +87,16 @@ INSTALLING the kernel:
|
|||
kernel source. Patches are applied from the current directory, but
|
||||
an alternative directory can be specified as the second argument.
|
||||
|
||||
- If you are upgrading between releases using the stable series patches
|
||||
(for example, patch-2.6.xx.y), note that these "dot-releases" are
|
||||
not incremental and must be applied to the 2.6.xx base tree. For
|
||||
example, if your base kernel is 2.6.12 and you want to apply the
|
||||
2.6.12.3 patch, you do not and indeed must not first apply the
|
||||
2.6.12.1 and 2.6.12.2 patches. Similarly, if you are running kernel
|
||||
version 2.6.12.2 and want to jump to 2.6.12.3, you must first
|
||||
reverse the 2.6.12.2 patch (that is, patch -R) _before_ applying
|
||||
the 2.6.12.3 patch.
|
||||
|
||||
- Make sure you have no stale .o files and dependencies lying around:
|
||||
|
||||
cd linux
|
||||
|
|
|
@ -461,6 +461,11 @@ sys_call_table:
|
|||
.quad sys_add_key
|
||||
.quad sys_request_key /* 440 */
|
||||
.quad sys_keyctl
|
||||
.quad sys_ioprio_set
|
||||
.quad sys_ioprio_get
|
||||
.quad sys_inotify_init
|
||||
.quad sys_inotify_add_watch /* 445 */
|
||||
.quad sys_inotify_rm_watch
|
||||
|
||||
.size sys_call_table, . - sys_call_table
|
||||
.type sys_call_table, @object
|
||||
|
|
|
@ -19,9 +19,9 @@
|
|||
mov r3, r2, lsl r3 @ create mask
|
||||
1: ldrexb r2, [r1]
|
||||
ands r0, r2, r3 @ save old value of bit
|
||||
\instr ip, r2, r3 @ toggle bit
|
||||
strexb r2, ip, [r1]
|
||||
cmp r2, #0
|
||||
\instr r2, r2, r3 @ toggle bit
|
||||
strexb ip, r2, [r1]
|
||||
cmp ip, #0
|
||||
bne 1b
|
||||
cmp r0, #0
|
||||
movne r0, #1
|
||||
|
|
|
@ -38,4 +38,9 @@ config FRAME_POINTER
|
|||
If you don't debug the kernel, you can say N, but we may not be able
|
||||
to solve problems without frame pointers.
|
||||
|
||||
config DEBUG_NMI_OOPS
|
||||
bool "NMI causes oops printout"
|
||||
help
|
||||
If the system locks up without any debug information you can say Y
|
||||
here to make it possible to dump an OOPS with an external NMI.
|
||||
endmenu
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# $Id: Makefile,v 1.23 2004/10/19 13:07:34 starvik Exp $
|
||||
# $Id: Makefile,v 1.28 2005/03/17 10:44:37 larsv Exp $
|
||||
# cris/Makefile
|
||||
#
|
||||
# This file is included by the global makefile so that you can add your own
|
||||
|
@ -15,6 +15,7 @@
|
|||
|
||||
arch-y := v10
|
||||
arch-$(CONFIG_ETRAX_ARCH_V10) := v10
|
||||
arch-$(CONFIG_ETRAX_ARCH_V32) := v32
|
||||
|
||||
# No config avaiable for make clean etc
|
||||
ifneq ($(arch-y),)
|
||||
|
@ -46,6 +47,21 @@ core-y += arch/$(ARCH)/$(SARCH)/kernel/ arch/$(ARCH)/$(SARCH)/mm/
|
|||
drivers-y += arch/$(ARCH)/$(SARCH)/drivers/
|
||||
libs-y += arch/$(ARCH)/$(SARCH)/lib/ $(LIBGCC)
|
||||
|
||||
# cris source path
|
||||
SRC_ARCH = $(srctree)/arch/$(ARCH)
|
||||
# cris object files path
|
||||
OBJ_ARCH = $(objtree)/arch/$(ARCH)
|
||||
|
||||
target_boot_arch_dir = $(OBJ_ARCH)/$(SARCH)/boot
|
||||
target_boot_dir = $(OBJ_ARCH)/boot
|
||||
src_boot_dir = $(SRC_ARCH)/boot
|
||||
target_compressed_dir = $(OBJ_ARCH)/boot/compressed
|
||||
src_compressed_dir = $(SRC_ARCH)/boot/compressed
|
||||
target_rescue_dir = $(OBJ_ARCH)/boot/rescue
|
||||
src_rescue_dir = $(SRC_ARCH)/boot/rescue
|
||||
|
||||
export target_boot_arch_dir target_boot_dir src_boot_dir target_compressed_dir src_compressed_dir target_rescue_dir src_rescue_dir
|
||||
|
||||
vmlinux.bin: vmlinux
|
||||
$(OBJCOPY) $(OBJCOPYFLAGS) vmlinux vmlinux.bin
|
||||
|
||||
|
@ -65,44 +81,52 @@ cramfs:
|
|||
|
||||
clinux: vmlinux.bin decompress.bin rescue.bin
|
||||
|
||||
decompress.bin: FORCE
|
||||
@make -C arch/$(ARCH)/boot/compressed decompress.bin
|
||||
decompress.bin: $(target_boot_dir)
|
||||
@$(MAKE) -f $(src_compressed_dir)/Makefile $(target_compressed_dir)/decompress.bin
|
||||
|
||||
rescue.bin: FORCE
|
||||
@make -C arch/$(ARCH)/boot/rescue rescue.bin
|
||||
$(target_rescue_dir)/rescue.bin: $(target_boot_dir)
|
||||
@$(MAKE) -f $(src_rescue_dir)/Makefile $(target_rescue_dir)/rescue.bin
|
||||
|
||||
zImage: vmlinux.bin rescue.bin
|
||||
zImage: $(target_boot_dir) vmlinux.bin $(target_rescue_dir)/rescue.bin
|
||||
## zImage - Compressed kernel (gzip)
|
||||
@make -C arch/$(ARCH)/boot/ zImage
|
||||
@$(MAKE) -f $(src_boot_dir)/Makefile zImage
|
||||
|
||||
$(target_boot_dir): $(target_boot_arch_dir)
|
||||
ln -sfn $< $@
|
||||
|
||||
$(target_boot_arch_dir):
|
||||
mkdir -p $@
|
||||
|
||||
compressed: zImage
|
||||
|
||||
archmrproper:
|
||||
archclean:
|
||||
$(Q)$(MAKE) $(clean)=arch/$(ARCH)/boot
|
||||
@if [ -d arch/$(ARCH)/boot ]; then \
|
||||
$(MAKE) $(clean)=arch/$(ARCH)/boot ; \
|
||||
fi
|
||||
rm -f timage vmlinux.bin decompress.bin rescue.bin cramfs.img
|
||||
rm -rf $(LD_SCRIPT).tmp
|
||||
|
||||
prepare: arch/$(ARCH)/.links include/asm-$(ARCH)/.arch \
|
||||
prepare: $(SRC_ARCH)/.links $(srctree)/include/asm-$(ARCH)/.arch \
|
||||
include/asm-$(ARCH)/$(SARCH)/offset.h
|
||||
|
||||
# Create some links to make all tools happy
|
||||
arch/$(ARCH)/.links:
|
||||
@rm -rf arch/$(ARCH)/drivers
|
||||
@ln -sfn $(SARCH)/drivers arch/$(ARCH)/drivers
|
||||
@rm -rf arch/$(ARCH)/boot
|
||||
@ln -sfn $(SARCH)/boot arch/$(ARCH)/boot
|
||||
@rm -rf arch/$(ARCH)/lib
|
||||
@ln -sfn $(SARCH)/lib arch/$(ARCH)/lib
|
||||
@ln -sfn $(SARCH) arch/$(ARCH)/arch
|
||||
@ln -sfn ../$(SARCH)/vmlinux.lds.S arch/$(ARCH)/kernel/vmlinux.lds.S
|
||||
$(SRC_ARCH)/.links:
|
||||
@rm -rf $(SRC_ARCH)/drivers
|
||||
@ln -sfn $(SRC_ARCH)/$(SARCH)/drivers $(SRC_ARCH)/drivers
|
||||
@rm -rf $(SRC_ARCH)/boot
|
||||
@ln -sfn $(SRC_ARCH)/$(SARCH)/boot $(SRC_ARCH)/boot
|
||||
@rm -rf $(SRC_ARCH)/lib
|
||||
@ln -sfn $(SRC_ARCH)/$(SARCH)/lib $(SRC_ARCH)/lib
|
||||
@ln -sfn $(SRC_ARCH)/$(SARCH) $(SRC_ARCH)/arch
|
||||
@ln -sfn $(SRC_ARCH)/$(SARCH)/vmlinux.lds.S $(SRC_ARCH)/kernel/vmlinux.lds.S
|
||||
@touch $@
|
||||
|
||||
# Create link to sub arch includes
|
||||
include/asm-$(ARCH)/.arch: $(wildcard include/config/arch/*.h)
|
||||
@echo ' Making asm-$(ARCH)/arch -> asm-$(ARCH)/$(SARCH) symlink'
|
||||
$(srctree)/include/asm-$(ARCH)/.arch: $(wildcard include/config/arch/*.h)
|
||||
@echo ' Making $(srctree)/include/asm-$(ARCH)/arch -> $(srctree)/include/asm-$(ARCH)/$(SARCH) symlink'
|
||||
@rm -f include/asm-$(ARCH)/arch
|
||||
@ln -sf $(SARCH) include/asm-$(ARCH)/arch
|
||||
@ln -sf $(srctree)/include/asm-$(ARCH)/$(SARCH) $(srctree)/include/asm-$(ARCH)/arch
|
||||
@touch $@
|
||||
|
||||
arch/$(ARCH)/$(SARCH)/kernel/asm-offsets.s: include/asm include/linux/version.h \
|
||||
|
|
|
@ -259,6 +259,37 @@ config ETRAX_DEBUG_PORT_NULL
|
|||
|
||||
endchoice
|
||||
|
||||
choice
|
||||
prompt "Kernel GDB port"
|
||||
depends on ETRAX_KGDB
|
||||
default ETRAX_KGDB_PORT0
|
||||
help
|
||||
Choose a serial port for kernel debugging. NOTE: This port should
|
||||
not be enabled under Drivers for built-in interfaces (as it has its
|
||||
own initialization code) and should not be the same as the debug port.
|
||||
|
||||
config ETRAX_KGDB_PORT0
|
||||
bool "Serial-0"
|
||||
help
|
||||
Use serial port 0 for kernel debugging.
|
||||
|
||||
config ETRAX_KGDB_PORT1
|
||||
bool "Serial-1"
|
||||
help
|
||||
Use serial port 1 for kernel debugging.
|
||||
|
||||
config ETRAX_KGDB_PORT2
|
||||
bool "Serial-2"
|
||||
help
|
||||
Use serial port 2 for kernel debugging.
|
||||
|
||||
config ETRAX_KGDB_PORT3
|
||||
bool "Serial-3"
|
||||
help
|
||||
Use serial port 3 for kernel debugging.
|
||||
|
||||
endchoice
|
||||
|
||||
choice
|
||||
prompt "Product rescue-port"
|
||||
depends on ETRAX_ARCH_V10
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
#
|
||||
# arch/cris/boot/Makefile
|
||||
#
|
||||
target = $(target_boot_dir)
|
||||
src = $(src_boot_dir)
|
||||
|
||||
zImage: compressed/vmlinuz
|
||||
|
||||
compressed/vmlinuz: $(TOPDIR)/vmlinux
|
||||
@$(MAKE) -C compressed vmlinuz
|
||||
compressed/vmlinuz:
|
||||
@$(MAKE) -f $(src)/compressed/Makefile $(target_compressed_dir)/vmlinuz
|
||||
|
||||
clean:
|
||||
rm -f zImage tools/build compressed/vmlinux.out
|
||||
@$(MAKE) -C compressed clean
|
||||
@$(MAKE) -f $(src)/compressed/Makefile clean
|
||||
|
|
|
@ -1,40 +1,45 @@
|
|||
#
|
||||
# linux/arch/etrax100/boot/compressed/Makefile
|
||||
#
|
||||
# create a compressed vmlinux image from the original vmlinux files and romfs
|
||||
# create a compressed vmlinuz image from the binary vmlinux.bin file
|
||||
#
|
||||
target = $(target_compressed_dir)
|
||||
src = $(src_compressed_dir)
|
||||
|
||||
CC = gcc-cris -melf -I $(TOPDIR)/include
|
||||
CC = gcc-cris -melf $(LINUXINCLUDE)
|
||||
CFLAGS = -O2
|
||||
LD = ld-cris
|
||||
OBJCOPY = objcopy-cris
|
||||
OBJCOPYFLAGS = -O binary --remove-section=.bss
|
||||
OBJECTS = head.o misc.o
|
||||
OBJECTS = $(target)/head.o $(target)/misc.o
|
||||
|
||||
# files to compress
|
||||
SYSTEM = $(TOPDIR)/vmlinux.bin
|
||||
SYSTEM = $(objtree)/vmlinux.bin
|
||||
|
||||
all: vmlinuz
|
||||
all: $(target_compressed_dir)/vmlinuz
|
||||
|
||||
decompress.bin: $(OBJECTS)
|
||||
$(LD) -T decompress.ld -o decompress.o $(OBJECTS)
|
||||
$(OBJCOPY) $(OBJCOPYFLAGS) decompress.o decompress.bin
|
||||
# save it for mkprod in the topdir.
|
||||
cp decompress.bin $(TOPDIR)
|
||||
$(target)/decompress.bin: $(OBJECTS)
|
||||
$(LD) -T $(src)/decompress.ld -o $(target)/decompress.o $(OBJECTS)
|
||||
$(OBJCOPY) $(OBJCOPYFLAGS) $(target)/decompress.o $(target)/decompress.bin
|
||||
|
||||
# Create vmlinuz image in top-level build directory
|
||||
$(target_compressed_dir)/vmlinuz: $(target) piggy.img $(target)/decompress.bin
|
||||
@echo " COMPR vmlinux.bin --> vmlinuz"
|
||||
@cat $(target)/decompress.bin piggy.img > $(target_compressed_dir)/vmlinuz
|
||||
@rm -f piggy.img
|
||||
|
||||
vmlinuz: piggy.img decompress.bin
|
||||
cat decompress.bin piggy.img > vmlinuz
|
||||
rm -f piggy.img
|
||||
$(target)/head.o: $(src)/head.S
|
||||
$(CC) -D__ASSEMBLY__ -traditional -c $< -o $@
|
||||
|
||||
head.o: head.S
|
||||
$(CC) -D__ASSEMBLY__ -traditional -c head.S -o head.o
|
||||
$(target)/misc.o: $(src)/misc.c
|
||||
$(CC) -D__KERNEL__ -c $< -o $@
|
||||
|
||||
# gzip the kernel image
|
||||
|
||||
piggy.img: $(SYSTEM)
|
||||
cat $(SYSTEM) | gzip -f -9 > piggy.img
|
||||
@cat $(SYSTEM) | gzip -f -9 > piggy.img
|
||||
|
||||
$(target):
|
||||
mkdir -p $(target)
|
||||
|
||||
clean:
|
||||
rm -f piggy.img vmlinuz vmlinuz.o
|
||||
rm -f piggy.img $(objtree)/vmlinuz
|
||||
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
#include <asm/arch/sv_addr_ag.h>
|
||||
|
||||
#define RAM_INIT_MAGIC 0x56902387
|
||||
|
||||
#define COMMAND_LINE_MAGIC 0x87109563
|
||||
|
||||
;; Exported symbols
|
||||
|
||||
.globl _input_data
|
||||
|
@ -88,6 +89,12 @@ basse: move.d pc, r5
|
|||
cmp.d r2, r1
|
||||
bcs 1b
|
||||
nop
|
||||
|
||||
;; Save command line magic and address.
|
||||
move.d _cmd_line_magic, $r12
|
||||
move.d $r10, [$r12]
|
||||
move.d _cmd_line_addr, $r12
|
||||
move.d $r11, [$r12]
|
||||
|
||||
;; Do the decompression and save compressed size in _inptr
|
||||
|
||||
|
@ -98,7 +105,13 @@ basse: move.d pc, r5
|
|||
|
||||
move.d [_input_data], r9 ; flash address of compressed kernel
|
||||
add.d [_inptr], r9 ; size of compressed kernel
|
||||
|
||||
|
||||
;; Restore command line magic and address.
|
||||
move.d _cmd_line_magic, $r10
|
||||
move.d [$r10], $r10
|
||||
move.d _cmd_line_addr, $r11
|
||||
move.d [$r11], $r11
|
||||
|
||||
;; Enter the decompressed kernel
|
||||
move.d RAM_INIT_MAGIC, r8 ; Tell kernel that DRAM is initialized
|
||||
jump 0x40004000 ; kernel is linked to this address
|
||||
|
@ -107,5 +120,8 @@ basse: move.d pc, r5
|
|||
|
||||
_input_data:
|
||||
.dword 0 ; used by the decompressor
|
||||
|
||||
_cmd_line_magic:
|
||||
.dword 0
|
||||
_cmd_line_addr:
|
||||
.dword 0
|
||||
#include "../../lib/hw_settings.S"
|
||||
|
|
|
@ -1,52 +1,53 @@
|
|||
#
|
||||
# Makefile for rescue code
|
||||
#
|
||||
ifndef TOPDIR
|
||||
TOPDIR = ../../../..
|
||||
endif
|
||||
CC = gcc-cris -mlinux -I $(TOPDIR)/include
|
||||
target = $(target_rescue_dir)
|
||||
src = $(src_rescue_dir)
|
||||
|
||||
CC = gcc-cris -mlinux $(LINUXINCLUDE)
|
||||
CFLAGS = -O2
|
||||
LD = gcc-cris -mlinux -nostdlib
|
||||
OBJCOPY = objcopy-cris
|
||||
OBJCOPYFLAGS = -O binary --remove-section=.bss
|
||||
|
||||
all: rescue.bin testrescue.bin kimagerescue.bin
|
||||
all: $(target)/rescue.bin $(target)/testrescue.bin $(target)/kimagerescue.bin
|
||||
|
||||
rescue: rescue.bin
|
||||
# do nothing
|
||||
$(target)/rescue.bin: $(target) $(target)/head.o
|
||||
$(LD) -T $(src)/rescue.ld -o $(target)/rescue.o $(target)/head.o
|
||||
$(OBJCOPY) $(OBJCOPYFLAGS) $(target)/rescue.o $(target)/rescue.bin
|
||||
# Place a copy in top-level build directory
|
||||
cp -p $(target)/rescue.bin $(objtree)
|
||||
|
||||
rescue.bin: head.o
|
||||
$(LD) -T rescue.ld -o rescue.o head.o
|
||||
$(OBJCOPY) $(OBJCOPYFLAGS) rescue.o rescue.bin
|
||||
cp rescue.bin $(TOPDIR)
|
||||
|
||||
testrescue.bin: testrescue.o
|
||||
$(OBJCOPY) $(OBJCOPYFLAGS) testrescue.o tr.bin
|
||||
$(target)/testrescue.bin: $(target) $(target)/testrescue.o
|
||||
$(OBJCOPY) $(OBJCOPYFLAGS) $(target)/testrescue.o tr.bin
|
||||
# Pad it to 784 bytes
|
||||
dd if=/dev/zero of=tmp2423 bs=1 count=784
|
||||
cat tr.bin tmp2423 >testrescue_tmp.bin
|
||||
dd if=testrescue_tmp.bin of=testrescue.bin bs=1 count=784
|
||||
dd if=testrescue_tmp.bin of=$(target)/testrescue.bin bs=1 count=784
|
||||
rm tr.bin tmp2423 testrescue_tmp.bin
|
||||
|
||||
kimagerescue.bin: kimagerescue.o
|
||||
$(OBJCOPY) $(OBJCOPYFLAGS) kimagerescue.o ktr.bin
|
||||
$(target)/kimagerescue.bin: $(target) $(target)/kimagerescue.o
|
||||
$(OBJCOPY) $(OBJCOPYFLAGS) $(target)/kimagerescue.o ktr.bin
|
||||
# Pad it to 784 bytes, that's what the rescue loader expects
|
||||
dd if=/dev/zero of=tmp2423 bs=1 count=784
|
||||
cat ktr.bin tmp2423 >kimagerescue_tmp.bin
|
||||
dd if=kimagerescue_tmp.bin of=kimagerescue.bin bs=1 count=784
|
||||
dd if=kimagerescue_tmp.bin of=$(target)/kimagerescue.bin bs=1 count=784
|
||||
rm ktr.bin tmp2423 kimagerescue_tmp.bin
|
||||
|
||||
head.o: head.S
|
||||
$(target):
|
||||
mkdir -p $(target)
|
||||
|
||||
$(target)/head.o: $(src)/head.S
|
||||
$(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o
|
||||
|
||||
testrescue.o: testrescue.S
|
||||
$(target)/testrescue.o: $(src)/testrescue.S
|
||||
$(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o
|
||||
|
||||
kimagerescue.o: kimagerescue.S
|
||||
$(target)/kimagerescue.o: $(src)/kimagerescue.S
|
||||
$(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o
|
||||
|
||||
clean:
|
||||
rm -f *.o *.bin
|
||||
rm -f $(target)/*.o $(target)/*.bin
|
||||
|
||||
fastdep:
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $Id: head.S,v 1.6 2003/04/09 08:12:43 pkj Exp $
|
||||
/* $Id: head.S,v 1.7 2005/03/07 12:11:06 starvik Exp $
|
||||
*
|
||||
* Rescue code, made to reside at the beginning of the
|
||||
* flash-memory. when it starts, it checks a partition
|
||||
|
@ -121,12 +121,13 @@
|
|||
;; 0x80000000 if loaded in flash (as it should be)
|
||||
;; 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
|
||||
|
||||
|
||||
nop
|
||||
di
|
||||
|
||||
jump in_cache ; enter cached area instead
|
||||
in_cache:
|
||||
in_cache:
|
||||
|
||||
|
||||
;; first put a jump test to give a possibility of upgrading the rescue code
|
||||
;; without erasing/reflashing the sector. we put a longword of -1 here and if
|
||||
|
@ -325,9 +326,29 @@ flash_ok:
|
|||
;; result will be in r0
|
||||
checksum:
|
||||
moveq 0, $r0
|
||||
1: addu.b [$r1+], $r0
|
||||
subq 1, $r2
|
||||
bne 1b
|
||||
moveq CONFIG_ETRAX_FLASH1_SIZE, $r6
|
||||
|
||||
;; If the first physical flash memory is exceeded wrap to the second one.
|
||||
btstq 26, $r1 ; Are we addressing first flash?
|
||||
bpl 1f
|
||||
nop
|
||||
clear.d $r6
|
||||
|
||||
1: test.d $r6 ; 0 = no wrapping
|
||||
beq 2f
|
||||
nop
|
||||
lslq 20, $r6 ; Convert MB to bytes
|
||||
sub.d $r1, $r6
|
||||
|
||||
2: addu.b [$r1+], $r0
|
||||
subq 1, $r6 ; Flash memory left
|
||||
beq 3f
|
||||
subq 1, $r2 ; Length left
|
||||
bne 2b
|
||||
nop
|
||||
ret
|
||||
nop
|
||||
|
||||
3: move.d MEM_CSE1_START, $r1 ; wrap to second flash
|
||||
ba 2b
|
||||
nop
|
||||
|
|
|
@ -1,17 +1,11 @@
|
|||
config ETRAX_ETHERNET
|
||||
bool "Ethernet support"
|
||||
depends on ETRAX_ARCH_V10
|
||||
select NET_ETHERNET
|
||||
help
|
||||
This option enables the ETRAX 100LX built-in 10/100Mbit Ethernet
|
||||
controller.
|
||||
|
||||
# this is just so that the user does not have to go into the
|
||||
# normal ethernet driver section just to enable ethernetworking
|
||||
config NET_ETHERNET
|
||||
bool
|
||||
depends on ETRAX_ETHERNET
|
||||
default y
|
||||
|
||||
choice
|
||||
prompt "Network LED behavior"
|
||||
depends on ETRAX_ETHERNET
|
||||
|
@ -20,26 +14,26 @@ choice
|
|||
config ETRAX_NETWORK_LED_ON_WHEN_LINK
|
||||
bool "LED_on_when_link"
|
||||
help
|
||||
Selecting LED_on_when_link will light the LED when there is a
|
||||
connection and will flash off when there is activity.
|
||||
Selecting LED_on_when_link will light the LED when there is a
|
||||
connection and will flash off when there is activity.
|
||||
|
||||
Selecting LED_on_when_activity will light the LED only when
|
||||
Selecting LED_on_when_activity will light the LED only when
|
||||
there is activity.
|
||||
|
||||
This setting will also affect the behaviour of other activity LEDs
|
||||
e.g. Bluetooth.
|
||||
This setting will also affect the behaviour of other activity LEDs
|
||||
e.g. Bluetooth.
|
||||
|
||||
config ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY
|
||||
bool "LED_on_when_activity"
|
||||
help
|
||||
Selecting LED_on_when_link will light the LED when there is a
|
||||
connection and will flash off when there is activity.
|
||||
Selecting LED_on_when_link will light the LED when there is a
|
||||
connection and will flash off when there is activity.
|
||||
|
||||
Selecting LED_on_when_activity will light the LED only when
|
||||
Selecting LED_on_when_activity will light the LED only when
|
||||
there is activity.
|
||||
|
||||
This setting will also affect the behaviour of other activity LEDs
|
||||
e.g. Bluetooth.
|
||||
This setting will also affect the behaviour of other activity LEDs
|
||||
e.g. Bluetooth.
|
||||
|
||||
endchoice
|
||||
|
||||
|
@ -91,11 +85,11 @@ choice
|
|||
depends on ETRAX_SERIAL_PORT0
|
||||
default ETRAX_SERIAL_PORT0_DMA6_OUT
|
||||
|
||||
config CONFIG_ETRAX_SERIAL_PORT0_NO_DMA_OUT
|
||||
bool "No DMA out"
|
||||
config ETRAX_SERIAL_PORT0_NO_DMA_OUT
|
||||
bool "No DMA out"
|
||||
|
||||
config CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT
|
||||
bool "DMA 6"
|
||||
config ETRAX_SERIAL_PORT0_DMA6_OUT
|
||||
bool "DMA 6"
|
||||
|
||||
endchoice
|
||||
|
||||
|
@ -104,11 +98,11 @@ choice
|
|||
depends on ETRAX_SERIAL_PORT0
|
||||
default ETRAX_SERIAL_PORT0_DMA7_IN
|
||||
|
||||
config CONFIG_ETRAX_SERIAL_PORT0_NO_DMA_IN
|
||||
bool "No DMA in"
|
||||
config ETRAX_SERIAL_PORT0_NO_DMA_IN
|
||||
bool "No DMA in"
|
||||
|
||||
config CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN
|
||||
bool "DMA 7"
|
||||
config ETRAX_SERIAL_PORT0_DMA7_IN
|
||||
bool "DMA 7"
|
||||
|
||||
endchoice
|
||||
|
||||
|
@ -205,11 +199,11 @@ choice
|
|||
depends on ETRAX_SERIAL_PORT1
|
||||
default ETRAX_SERIAL_PORT1_DMA8_OUT
|
||||
|
||||
config CONFIG_ETRAX_SERIAL_PORT1_NO_DMA_OUT
|
||||
bool "No DMA out"
|
||||
config ETRAX_SERIAL_PORT1_NO_DMA_OUT
|
||||
bool "No DMA out"
|
||||
|
||||
config CONFIG_ETRAX_SERIAL_PORT1_DMA8_OUT
|
||||
bool "DMA 8"
|
||||
config ETRAX_SERIAL_PORT1_DMA8_OUT
|
||||
bool "DMA 8"
|
||||
|
||||
endchoice
|
||||
|
||||
|
@ -218,11 +212,11 @@ choice
|
|||
depends on ETRAX_SERIAL_PORT1
|
||||
default ETRAX_SERIAL_PORT1_DMA9_IN
|
||||
|
||||
config CONFIG_ETRAX_SERIAL_PORT1_NO_DMA_IN
|
||||
bool "No DMA in"
|
||||
config ETRAX_SERIAL_PORT1_NO_DMA_IN
|
||||
bool "No DMA in"
|
||||
|
||||
config CONFIG_ETRAX_SERIAL_PORT1_DMA9_IN
|
||||
bool "DMA 9"
|
||||
config ETRAX_SERIAL_PORT1_DMA9_IN
|
||||
bool "DMA 9"
|
||||
|
||||
endchoice
|
||||
|
||||
|
@ -308,7 +302,7 @@ config ETRAX_SER1_CD_ON_PB_BIT
|
|||
Specify the pin of the PB port to carry the CD signal for serial
|
||||
port 1.
|
||||
|
||||
comment "Make sure you dont have the same PB bits more than once!"
|
||||
comment "Make sure you do not have the same PB bits more than once!"
|
||||
depends on ETRAX_SERIAL && ETRAX_SER0_DTR_RI_DSR_CD_ON_PB && ETRAX_SER1_DTR_RI_DSR_CD_ON_PB
|
||||
|
||||
config ETRAX_SERIAL_PORT2
|
||||
|
@ -322,11 +316,11 @@ choice
|
|||
depends on ETRAX_SERIAL_PORT2
|
||||
default ETRAX_SERIAL_PORT2_DMA2_OUT
|
||||
|
||||
config CONFIG_ETRAX_SERIAL_PORT2_NO_DMA_OUT
|
||||
bool "No DMA out"
|
||||
config ETRAX_SERIAL_PORT2_NO_DMA_OUT
|
||||
bool "No DMA out"
|
||||
|
||||
config CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT
|
||||
bool "DMA 2"
|
||||
config ETRAX_SERIAL_PORT2_DMA2_OUT
|
||||
bool "DMA 2"
|
||||
|
||||
endchoice
|
||||
|
||||
|
@ -335,11 +329,11 @@ choice
|
|||
depends on ETRAX_SERIAL_PORT2
|
||||
default ETRAX_SERIAL_PORT2_DMA3_IN
|
||||
|
||||
config CONFIG_ETRAX_SERIAL_PORT2_NO_DMA_IN
|
||||
bool "No DMA in"
|
||||
config ETRAX_SERIAL_PORT2_NO_DMA_IN
|
||||
bool "No DMA in"
|
||||
|
||||
config CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN
|
||||
bool "DMA 3"
|
||||
config ETRAX_SERIAL_PORT2_DMA3_IN
|
||||
bool "DMA 3"
|
||||
|
||||
endchoice
|
||||
|
||||
|
@ -436,11 +430,11 @@ choice
|
|||
depends on ETRAX_SERIAL_PORT3
|
||||
default ETRAX_SERIAL_PORT3_DMA4_OUT
|
||||
|
||||
config CONFIG_ETRAX_SERIAL_PORT3_NO_DMA_OUT
|
||||
bool "No DMA out"
|
||||
config ETRAX_SERIAL_PORT3_NO_DMA_OUT
|
||||
bool "No DMA out"
|
||||
|
||||
config CONFIG_ETRAX_SERIAL_PORT3_DMA4_OUT
|
||||
bool "DMA 4"
|
||||
config ETRAX_SERIAL_PORT3_DMA4_OUT
|
||||
bool "DMA 4"
|
||||
|
||||
endchoice
|
||||
|
||||
|
@ -449,11 +443,11 @@ choice
|
|||
depends on ETRAX_SERIAL_PORT3
|
||||
default ETRAX_SERIAL_PORT3_DMA5_IN
|
||||
|
||||
config CONFIG_ETRAX_SERIAL_PORT3_NO_DMA_IN
|
||||
bool "No DMA in"
|
||||
config ETRAX_SERIAL_PORT3_NO_DMA_IN
|
||||
bool "No DMA in"
|
||||
|
||||
config CONFIG_ETRAX_SERIAL_PORT3_DMA5_IN
|
||||
bool "DMA 5"
|
||||
config ETRAX_SERIAL_PORT3_DMA5_IN
|
||||
bool "DMA 5"
|
||||
|
||||
endchoice
|
||||
|
||||
|
@ -554,7 +548,6 @@ config ETRAX_IDE
|
|||
select BLK_DEV_IDEDISK
|
||||
select BLK_DEV_IDECD
|
||||
select BLK_DEV_IDEDMA
|
||||
select DMA_NONPCI
|
||||
help
|
||||
Enable this to get support for ATA/IDE.
|
||||
You can't use paralell ports or SCSI ports
|
||||
|
@ -579,7 +572,7 @@ config ETRAX_IDE_PB7_RESET
|
|||
IDE reset on pin 7 on port B
|
||||
|
||||
config ETRAX_IDE_G27_RESET
|
||||
bool "Port_G_Bit_27"
|
||||
bool "Port_G_Bit_27"
|
||||
help
|
||||
IDE reset on pin 27 on port G
|
||||
|
||||
|
@ -588,30 +581,36 @@ endchoice
|
|||
|
||||
config ETRAX_USB_HOST
|
||||
bool "USB host"
|
||||
select USB
|
||||
help
|
||||
This option enables the host functionality of the ETRAX 100LX
|
||||
built-in USB controller. In host mode the controller is designed
|
||||
for CTRL and BULK traffic only, INTR traffic may work as well
|
||||
however (depending on the requirements of timeliness).
|
||||
|
||||
config USB
|
||||
tristate
|
||||
depends on ETRAX_USB_HOST
|
||||
default y
|
||||
|
||||
config ETRAX_USB_HOST_PORT1
|
||||
bool " USB port 1 enabled"
|
||||
depends on ETRAX_USB_HOST
|
||||
default n
|
||||
bool "USB port 1 enabled"
|
||||
depends on ETRAX_USB_HOST
|
||||
default n
|
||||
|
||||
config ETRAX_USB_HOST_PORT2
|
||||
bool " USB port 2 enabled"
|
||||
depends on ETRAX_USB_HOST
|
||||
default n
|
||||
bool "USB port 2 enabled"
|
||||
depends on ETRAX_USB_HOST
|
||||
default n
|
||||
|
||||
config ETRAX_AXISFLASHMAP
|
||||
bool "Axis flash-map support"
|
||||
depends on ETRAX_ARCH_V10
|
||||
select MTD
|
||||
select MTD_CFI
|
||||
select MTD_CFI_AMDSTD
|
||||
select MTD_OBSOLETE_CHIPS
|
||||
select MTD_AMDSTD
|
||||
select MTD_CHAR
|
||||
select MTD_BLOCK
|
||||
select MTD_PARTITIONS
|
||||
select MTD_CONCAT
|
||||
select MTD_COMPLEX_MAPPINGS
|
||||
help
|
||||
This option enables MTD mapping of flash devices. Needed to use
|
||||
flash memories. If unsure, say Y.
|
||||
|
@ -627,119 +626,6 @@ config ETRAX_PTABLE_SECTOR
|
|||
for changing this is when the flash block size is bigger
|
||||
than 64kB (e.g. when using two parallel 16 bit flashes).
|
||||
|
||||
# here we define the CONFIG_'s necessary to enable MTD support
|
||||
# for the flash
|
||||
config MTD
|
||||
tristate
|
||||
depends on ETRAX_AXISFLASHMAP
|
||||
default y
|
||||
help
|
||||
Memory Technology Devices are flash, RAM and similar chips, often
|
||||
used for solid state file systems on embedded devices. This option
|
||||
will provide the generic support for MTD drivers to register
|
||||
themselves with the kernel and for potential users of MTD devices
|
||||
to enumerate the devices which are present and obtain a handle on
|
||||
them. It will also allow you to select individual drivers for
|
||||
particular hardware and users of MTD devices. If unsure, say N.
|
||||
|
||||
config MTD_CFI
|
||||
tristate
|
||||
depends on ETRAX_AXISFLASHMAP
|
||||
default y
|
||||
help
|
||||
The Common Flash Interface specification was developed by Intel,
|
||||
AMD and other flash manufactures that provides a universal method
|
||||
for probing the capabilities of flash devices. If you wish to
|
||||
support any device that is CFI-compliant, you need to enable this
|
||||
option. Visit <http://www.amd.com/products/nvd/overview/cfi.html>
|
||||
for more information on CFI.
|
||||
|
||||
config MTD_CFI_AMDSTD
|
||||
tristate
|
||||
depends on ETRAX_AXISFLASHMAP
|
||||
default y
|
||||
help
|
||||
The Common Flash Interface defines a number of different command
|
||||
sets which a CFI-compliant chip may claim to implement. This code
|
||||
provides support for one of those command sets, used on chips
|
||||
chips including the AMD Am29LV320.
|
||||
|
||||
config MTD_OBSOLETE_CHIPS
|
||||
bool
|
||||
depends on ETRAX_AXISFLASHMAP
|
||||
default y
|
||||
help
|
||||
This option does not enable any code directly, but will allow you to
|
||||
select some other chip drivers which are now considered obsolete,
|
||||
because the generic CONFIG_JEDEC_PROBE code above should now detect
|
||||
the chips which are supported by these drivers, and allow the generic
|
||||
CFI-compatible drivers to drive the chips. Say 'N' here unless you have
|
||||
already tried the CONFIG_JEDEC_PROBE method and reported its failure
|
||||
to the MTD mailing list at <linux-mtd@lists.infradead.org>
|
||||
|
||||
config MTD_AMDSTD
|
||||
tristate
|
||||
depends on ETRAX_AXISFLASHMAP
|
||||
default y
|
||||
help
|
||||
This option enables support for flash chips using AMD-compatible
|
||||
commands, including some which are not CFI-compatible and hence
|
||||
cannot be used with the CONFIG_MTD_CFI_AMDSTD option.
|
||||
|
||||
It also works on AMD compatible chips that do conform to CFI.
|
||||
|
||||
config MTD_CHAR
|
||||
tristate
|
||||
depends on ETRAX_AXISFLASHMAP
|
||||
default y
|
||||
help
|
||||
This provides a character device for each MTD device present in
|
||||
the system, allowing the user to read and write directly to the
|
||||
memory chips, and also use ioctl() to obtain information about
|
||||
the device, or to erase parts of it.
|
||||
|
||||
config MTD_BLOCK
|
||||
tristate
|
||||
depends on ETRAX_AXISFLASHMAP
|
||||
default y
|
||||
---help---
|
||||
Although most flash chips have an erase size too large to be useful
|
||||
as block devices, it is possible to use MTD devices which are based
|
||||
on RAM chips in this manner. This block device is a user of MTD
|
||||
devices performing that function.
|
||||
|
||||
At the moment, it is also required for the Journalling Flash File
|
||||
System(s) to obtain a handle on the MTD device when it's mounted
|
||||
(although JFFS and JFFS2 don't actually use any of the functionality
|
||||
of the mtdblock device).
|
||||
|
||||
Later, it may be extended to perform read/erase/modify/write cycles
|
||||
on flash chips to emulate a smaller block size. Needless to say,
|
||||
this is very unsafe, but could be useful for file systems which are
|
||||
almost never written to.
|
||||
|
||||
You do not need this option for use with the DiskOnChip devices. For
|
||||
those, enable NFTL support (CONFIG_NFTL) instead.
|
||||
|
||||
config MTD_PARTITIONS
|
||||
tristate
|
||||
depends on ETRAX_AXISFLASHMAP
|
||||
default y
|
||||
help
|
||||
If you have a device which needs to divide its flash chip(s) up
|
||||
into multiple 'partitions', each of which appears to the user as
|
||||
a separate MTD device, you require this option to be enabled. If
|
||||
unsure, say 'Y'.
|
||||
|
||||
Note, however, that you don't need this option for the DiskOnChip
|
||||
devices. Partitioning on NFTL 'devices' is a different - that's the
|
||||
'normal' form of partitioning used on a block device.
|
||||
|
||||
config MTD_CONCAT
|
||||
tristate
|
||||
depends on ETRAX_AXISFLASHMAP
|
||||
default y
|
||||
|
||||
config ETRAX_I2C
|
||||
bool "I2C support"
|
||||
depends on ETRAX_ARCH_V10
|
||||
|
@ -752,7 +638,7 @@ config ETRAX_I2C
|
|||
val = ioctl(fd, _IO(ETRAXI2C_IOCTYPE, I2C_READREG), i2c_arg);
|
||||
|
||||
# this is true for most products since PB-I2C seems to be somewhat
|
||||
# flawed..
|
||||
# flawed..
|
||||
config ETRAX_I2C_USES_PB_NOT_PB_I2C
|
||||
bool "I2C uses PB not PB-I2C"
|
||||
depends on ETRAX_I2C
|
||||
|
@ -886,7 +772,7 @@ config ETRAX_RTC
|
|||
bool "Real Time Clock support"
|
||||
depends on ETRAX_ARCH_V10
|
||||
help
|
||||
Enables drivers for the Real-Time Clock battery-backed chips on
|
||||
Enables drivers for the Real-Time Clock battery-backed chips on
|
||||
some products. The kernel reads the time when booting, and
|
||||
the date can be set using ioctl(fd, RTC_SET_TIME, &rt) with rt a
|
||||
rtc_time struct (see <file:include/asm-cris/rtc.h>) on the /dev/rtc
|
||||
|
@ -903,13 +789,13 @@ config ETRAX_DS1302
|
|||
bool "DS1302"
|
||||
help
|
||||
Enables the driver for the DS1302 Real-Time Clock battery-backed
|
||||
chip on some products.
|
||||
chip on some products.
|
||||
|
||||
config ETRAX_PCF8563
|
||||
bool "PCF8563"
|
||||
help
|
||||
Enables the driver for the PCF8563 Real-Time Clock battery-backed
|
||||
chip on some products.
|
||||
chip on some products.
|
||||
|
||||
endchoice
|
||||
|
||||
|
@ -954,10 +840,8 @@ config ETRAX_DS1302_TRICKLE_CHARGE
|
|||
help
|
||||
This controls the initial value of the trickle charge register.
|
||||
0 = disabled (use this if you are unsure or have a non rechargable battery)
|
||||
Otherwise the following values can be OR:ed together to control the
|
||||
Otherwise the following values can be OR:ed together to control the
|
||||
charge current:
|
||||
1 = 2kohm, 2 = 4kohm, 3 = 4kohm
|
||||
4 = 1 diode, 8 = 2 diodes
|
||||
Allowed values are (increasing current): 0, 11, 10, 9, 7, 6, 5
|
||||
|
||||
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
* partition split defined below.
|
||||
*
|
||||
* $Log: axisflashmap.c,v $
|
||||
* Revision 1.11 2004/11/15 10:27:14 starvik
|
||||
* Corrected typo (Thanks to Milton Miller <miltonm@bga.com>).
|
||||
*
|
||||
* Revision 1.10 2004/08/16 12:37:22 starvik
|
||||
* Merge of Linux 2.6.8
|
||||
*
|
||||
|
@ -161,7 +164,7 @@
|
|||
#elif CONFIG_ETRAX_FLASH_BUSWIDTH==2
|
||||
#define flash_data __u16
|
||||
#elif CONFIG_ETRAX_FLASH_BUSWIDTH==4
|
||||
#define flash_data __u16
|
||||
#define flash_data __u32
|
||||
#endif
|
||||
|
||||
/* From head.S */
|
||||
|
|
|
@ -7,6 +7,15 @@
|
|||
*! Functions exported: ds1302_readreg, ds1302_writereg, ds1302_init
|
||||
*!
|
||||
*! $Log: ds1302.c,v $
|
||||
*! Revision 1.18 2005/01/24 09:11:26 mikaelam
|
||||
*! Minor changes to get DS1302 RTC chip driver to work
|
||||
*!
|
||||
*! Revision 1.17 2005/01/05 06:11:22 starvik
|
||||
*! No need to do local_irq_disable after local_irq_save.
|
||||
*!
|
||||
*! Revision 1.16 2004/12/13 12:21:52 starvik
|
||||
*! Added I/O and DMA allocators from Linux 2.4
|
||||
*!
|
||||
*! Revision 1.14 2004/08/24 06:48:43 starvik
|
||||
*! Whitespace cleanup
|
||||
*!
|
||||
|
@ -124,9 +133,9 @@
|
|||
*!
|
||||
*! ---------------------------------------------------------------------------
|
||||
*!
|
||||
*! (C) Copyright 1999, 2000, 2001 Axis Communications AB, LUND, SWEDEN
|
||||
*! (C) Copyright 1999, 2000, 2001, 2002, 2003, 2004 Axis Communications AB, LUND, SWEDEN
|
||||
*!
|
||||
*! $Id: ds1302.c,v 1.14 2004/08/24 06:48:43 starvik Exp $
|
||||
*! $Id: ds1302.c,v 1.18 2005/01/24 09:11:26 mikaelam Exp $
|
||||
*!
|
||||
*!***************************************************************************/
|
||||
|
||||
|
@ -145,6 +154,7 @@
|
|||
#include <asm/arch/svinto.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/rtc.h>
|
||||
#include <asm/arch/io_interface_mux.h>
|
||||
|
||||
#define RTC_MAJOR_NR 121 /* local major, change later */
|
||||
|
||||
|
@ -320,7 +330,6 @@ get_rtc_time(struct rtc_time *rtc_tm)
|
|||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
local_irq_disable();
|
||||
|
||||
rtc_tm->tm_sec = CMOS_READ(RTC_SECONDS);
|
||||
rtc_tm->tm_min = CMOS_READ(RTC_MINUTES);
|
||||
|
@ -358,7 +367,7 @@ static int
|
|||
rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long flags;
|
||||
|
||||
switch(cmd) {
|
||||
case RTC_RD_TIME: /* read the time/date from RTC */
|
||||
|
@ -382,7 +391,7 @@ rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
|
|||
return -EPERM;
|
||||
|
||||
if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, sizeof(struct rtc_time)))
|
||||
return -EFAULT;
|
||||
return -EFAULT;
|
||||
|
||||
yrs = rtc_tm.tm_year + 1900;
|
||||
mon = rtc_tm.tm_mon + 1; /* tm_mon starts at zero */
|
||||
|
@ -419,7 +428,6 @@ rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
|
|||
BIN_TO_BCD(yrs);
|
||||
|
||||
local_irq_save(flags);
|
||||
local_irq_disable();
|
||||
CMOS_WRITE(yrs, RTC_YEAR);
|
||||
CMOS_WRITE(mon, RTC_MONTH);
|
||||
CMOS_WRITE(day, RTC_DAY_OF_MONTH);
|
||||
|
@ -438,7 +446,7 @@ rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
|
|||
|
||||
case RTC_SET_CHARGE: /* set the RTC TRICKLE CHARGE register */
|
||||
{
|
||||
int tcs_val;
|
||||
int tcs_val;
|
||||
|
||||
if (!capable(CAP_SYS_TIME))
|
||||
return -EPERM;
|
||||
|
@ -492,8 +500,8 @@ print_rtc_status(void)
|
|||
/* The various file operations we support. */
|
||||
|
||||
static struct file_operations rtc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.ioctl = rtc_ioctl,
|
||||
.owner = THIS_MODULE,
|
||||
.ioctl = rtc_ioctl,
|
||||
};
|
||||
|
||||
/* Probe for the chip by writing something to its RAM and try reading it back. */
|
||||
|
@ -532,7 +540,7 @@ ds1302_probe(void)
|
|||
"PB",
|
||||
#endif
|
||||
CONFIG_ETRAX_DS1302_RSTBIT);
|
||||
print_rtc_status();
|
||||
print_rtc_status();
|
||||
retval = 1;
|
||||
} else {
|
||||
stop();
|
||||
|
@ -548,7 +556,9 @@ ds1302_probe(void)
|
|||
int __init
|
||||
ds1302_init(void)
|
||||
{
|
||||
#ifdef CONFIG_ETRAX_I2C
|
||||
i2c_init();
|
||||
#endif
|
||||
|
||||
if (!ds1302_probe()) {
|
||||
#ifdef CONFIG_ETRAX_DS1302_RST_ON_GENERIC_PORT
|
||||
|
@ -558,25 +568,42 @@ ds1302_init(void)
|
|||
*
|
||||
* Make sure that R_GEN_CONFIG is setup correct.
|
||||
*/
|
||||
genconfig_shadow = ((genconfig_shadow &
|
||||
~IO_MASK(R_GEN_CONFIG, ata)) |
|
||||
(IO_STATE(R_GEN_CONFIG, ata, select)));
|
||||
*R_GEN_CONFIG = genconfig_shadow;
|
||||
/* Allocating the ATA interface will grab almost all
|
||||
* pins in I/O groups a, b, c and d. A consequence of
|
||||
* allocating the ATA interface is that the fixed
|
||||
* interfaces shared RAM, parallel port 0, parallel
|
||||
* port 1, parallel port W, SCSI-8 port 0, SCSI-8 port
|
||||
* 1, SCSI-W, serial port 2, serial port 3,
|
||||
* synchronous serial port 3 and USB port 2 and almost
|
||||
* all GPIO pins on port g cannot be used.
|
||||
*/
|
||||
if (cris_request_io_interface(if_ata, "ds1302/ATA")) {
|
||||
printk(KERN_WARNING "ds1302: Failed to get IO interface\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#elif CONFIG_ETRAX_DS1302_RSTBIT == 0
|
||||
|
||||
/* Set the direction of this bit to out. */
|
||||
genconfig_shadow = ((genconfig_shadow &
|
||||
~IO_MASK(R_GEN_CONFIG, g0dir)) |
|
||||
(IO_STATE(R_GEN_CONFIG, g0dir, out)));
|
||||
*R_GEN_CONFIG = genconfig_shadow;
|
||||
if (cris_io_interface_allocate_pins(if_gpio_grp_a,
|
||||
'g',
|
||||
CONFIG_ETRAX_DS1302_RSTBIT,
|
||||
CONFIG_ETRAX_DS1302_RSTBIT)) {
|
||||
printk(KERN_WARNING "ds1302: Failed to get IO interface\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set the direction of this bit to out. */
|
||||
genconfig_shadow = ((genconfig_shadow &
|
||||
~IO_MASK(R_GEN_CONFIG, g0dir)) |
|
||||
(IO_STATE(R_GEN_CONFIG, g0dir, out)));
|
||||
*R_GEN_CONFIG = genconfig_shadow;
|
||||
#endif
|
||||
if (!ds1302_probe()) {
|
||||
printk(KERN_WARNING "%s: RTC not found.\n", ds1302_name);
|
||||
return -1;
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
printk(KERN_WARNING "%s: RTC not found.\n", ds1302_name);
|
||||
return -1;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
/* Initialise trickle charger */
|
||||
|
|
|
@ -20,6 +20,12 @@
|
|||
*! in the spin-lock.
|
||||
*!
|
||||
*! $Log: eeprom.c,v $
|
||||
*! Revision 1.12 2005/06/19 17:06:46 starvik
|
||||
*! Merge of Linux 2.6.12.
|
||||
*!
|
||||
*! Revision 1.11 2005/01/26 07:14:46 starvik
|
||||
*! Applied diff from kernel janitors (Nish Aravamudan).
|
||||
*!
|
||||
*! Revision 1.10 2003/09/11 07:29:48 starvik
|
||||
*! Merge of Linux 2.6.0-test5
|
||||
*!
|
||||
|
@ -94,6 +100,7 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/wait.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include "i2c.h"
|
||||
|
||||
|
@ -526,15 +533,10 @@ static ssize_t eeprom_read(struct file * file, char * buf, size_t count, loff_t
|
|||
return -EFAULT;
|
||||
}
|
||||
|
||||
while(eeprom.busy)
|
||||
{
|
||||
interruptible_sleep_on(&eeprom.wait_q);
|
||||
wait_event_interruptible(eeprom.wait_q, !eeprom.busy);
|
||||
if (signal_pending(current))
|
||||
return -EINTR;
|
||||
|
||||
/* bail out if we get interrupted */
|
||||
if (signal_pending(current))
|
||||
return -EINTR;
|
||||
|
||||
}
|
||||
eeprom.busy++;
|
||||
|
||||
page = (unsigned char) (p >> 8);
|
||||
|
@ -604,13 +606,10 @@ static ssize_t eeprom_write(struct file * file, const char * buf, size_t count,
|
|||
return -EFAULT;
|
||||
}
|
||||
|
||||
while(eeprom.busy)
|
||||
{
|
||||
interruptible_sleep_on(&eeprom.wait_q);
|
||||
/* bail out if we get interrupted */
|
||||
if (signal_pending(current))
|
||||
return -EINTR;
|
||||
}
|
||||
wait_event_interruptible(eeprom.wait_q, !eeprom.busy);
|
||||
/* bail out if we get interrupted */
|
||||
if (signal_pending(current))
|
||||
return -EINTR;
|
||||
eeprom.busy++;
|
||||
for(i = 0; (i < EEPROM_RETRIES) && (restart > 0); i++)
|
||||
{
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $Id: gpio.c,v 1.12 2004/08/24 07:19:59 starvik Exp $
|
||||
/* $Id: gpio.c,v 1.17 2005/06/19 17:06:46 starvik Exp $
|
||||
*
|
||||
* Etrax general port I/O device
|
||||
*
|
||||
|
@ -9,6 +9,18 @@
|
|||
* Johan Adolfsson (read/set directions, write, port G)
|
||||
*
|
||||
* $Log: gpio.c,v $
|
||||
* Revision 1.17 2005/06/19 17:06:46 starvik
|
||||
* Merge of Linux 2.6.12.
|
||||
*
|
||||
* Revision 1.16 2005/03/07 13:02:29 starvik
|
||||
* Protect driver global states with spinlock
|
||||
*
|
||||
* Revision 1.15 2005/01/05 06:08:55 starvik
|
||||
* No need to do local_irq_disable after local_irq_save.
|
||||
*
|
||||
* Revision 1.14 2004/12/13 12:21:52 starvik
|
||||
* Added I/O and DMA allocators from Linux 2.4
|
||||
*
|
||||
* Revision 1.12 2004/08/24 07:19:59 starvik
|
||||
* Whitespace cleanup
|
||||
*
|
||||
|
@ -142,6 +154,7 @@
|
|||
#include <asm/io.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/arch/io_interface_mux.h>
|
||||
|
||||
#define GPIO_MAJOR 120 /* experimental MAJOR number */
|
||||
|
||||
|
@ -194,6 +207,8 @@ static struct gpio_private *alarmlist = 0;
|
|||
static int gpio_some_alarms = 0; /* Set if someone uses alarm */
|
||||
static unsigned long gpio_pa_irq_enabled_mask = 0;
|
||||
|
||||
static DEFINE_SPINLOCK(gpio_lock); /* Protect directions etc */
|
||||
|
||||
/* Port A and B use 8 bit access, but Port G is 32 bit */
|
||||
#define NUM_PORTS (GPIO_MINOR_B+1)
|
||||
|
||||
|
@ -241,6 +256,9 @@ static volatile unsigned char *dir_shadow[NUM_PORTS] = {
|
|||
&port_pb_dir_shadow
|
||||
};
|
||||
|
||||
/* All bits in port g that can change dir. */
|
||||
static const unsigned long int changeable_dir_g_mask = 0x01FFFF01;
|
||||
|
||||
/* Port G is 32 bit, handle it special, some bits are both inputs
|
||||
and outputs at the same time, only some of the bits can change direction
|
||||
and some of them in groups of 8 bit. */
|
||||
|
@ -260,6 +278,7 @@ gpio_poll(struct file *file,
|
|||
unsigned int mask = 0;
|
||||
struct gpio_private *priv = (struct gpio_private *)file->private_data;
|
||||
unsigned long data;
|
||||
spin_lock(&gpio_lock);
|
||||
poll_wait(file, &priv->alarm_wq, wait);
|
||||
if (priv->minor == GPIO_MINOR_A) {
|
||||
unsigned long flags;
|
||||
|
@ -270,10 +289,10 @@ gpio_poll(struct file *file,
|
|||
*/
|
||||
tmp = ~data & priv->highalarm & 0xFF;
|
||||
tmp = (tmp << R_IRQ_MASK1_SET__pa0__BITNR);
|
||||
save_flags(flags); cli();
|
||||
local_irq_save(flags);
|
||||
gpio_pa_irq_enabled_mask |= tmp;
|
||||
*R_IRQ_MASK1_SET = tmp;
|
||||
restore_flags(flags);
|
||||
local_irq_restore(flags);
|
||||
|
||||
} else if (priv->minor == GPIO_MINOR_B)
|
||||
data = *R_PORT_PB_DATA;
|
||||
|
@ -286,8 +305,11 @@ gpio_poll(struct file *file,
|
|||
(~data & priv->lowalarm)) {
|
||||
mask = POLLIN|POLLRDNORM;
|
||||
}
|
||||
|
||||
spin_unlock(&gpio_lock);
|
||||
|
||||
DP(printk("gpio_poll ready: mask 0x%08X\n", mask));
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
|
@ -296,6 +318,7 @@ int etrax_gpio_wake_up_check(void)
|
|||
struct gpio_private *priv = alarmlist;
|
||||
unsigned long data = 0;
|
||||
int ret = 0;
|
||||
spin_lock(&gpio_lock);
|
||||
while (priv) {
|
||||
if (USE_PORTS(priv)) {
|
||||
data = *priv->port;
|
||||
|
@ -310,6 +333,7 @@ int etrax_gpio_wake_up_check(void)
|
|||
}
|
||||
priv = priv->next;
|
||||
}
|
||||
spin_unlock(&gpio_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -327,6 +351,7 @@ static irqreturn_t
|
|||
gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long tmp;
|
||||
spin_lock(&gpio_lock);
|
||||
/* Find what PA interrupts are active */
|
||||
tmp = (*R_IRQ_READ1);
|
||||
|
||||
|
@ -337,6 +362,8 @@ gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs)
|
|||
*R_IRQ_MASK1_CLR = tmp;
|
||||
gpio_pa_irq_enabled_mask &= ~tmp;
|
||||
|
||||
spin_unlock(&gpio_lock);
|
||||
|
||||
if (gpio_some_alarms) {
|
||||
return IRQ_RETVAL(etrax_gpio_wake_up_check());
|
||||
}
|
||||
|
@ -350,6 +377,9 @@ static ssize_t gpio_write(struct file * file, const char * buf, size_t count,
|
|||
struct gpio_private *priv = (struct gpio_private *)file->private_data;
|
||||
unsigned char data, clk_mask, data_mask, write_msb;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock(&gpio_lock);
|
||||
|
||||
ssize_t retval = count;
|
||||
if (priv->minor !=GPIO_MINOR_A && priv->minor != GPIO_MINOR_B) {
|
||||
return -EFAULT;
|
||||
|
@ -372,7 +402,7 @@ static ssize_t gpio_write(struct file * file, const char * buf, size_t count,
|
|||
data = *buf++;
|
||||
if (priv->write_msb) {
|
||||
for (i = 7; i >= 0;i--) {
|
||||
local_irq_save(flags); local_irq_disable();
|
||||
local_irq_save(flags);
|
||||
*priv->port = *priv->shadow &= ~clk_mask;
|
||||
if (data & 1<<i)
|
||||
*priv->port = *priv->shadow |= data_mask;
|
||||
|
@ -384,7 +414,7 @@ static ssize_t gpio_write(struct file * file, const char * buf, size_t count,
|
|||
}
|
||||
} else {
|
||||
for (i = 0; i <= 7;i++) {
|
||||
local_irq_save(flags); local_irq_disable();
|
||||
local_irq_save(flags);
|
||||
*priv->port = *priv->shadow &= ~clk_mask;
|
||||
if (data & 1<<i)
|
||||
*priv->port = *priv->shadow |= data_mask;
|
||||
|
@ -396,6 +426,7 @@ static ssize_t gpio_write(struct file * file, const char * buf, size_t count,
|
|||
}
|
||||
}
|
||||
}
|
||||
spin_unlock(&gpio_lock);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
@ -452,9 +483,14 @@ gpio_open(struct inode *inode, struct file *filp)
|
|||
static int
|
||||
gpio_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct gpio_private *p = alarmlist;
|
||||
struct gpio_private *todel = (struct gpio_private *)filp->private_data;
|
||||
|
||||
struct gpio_private *p;
|
||||
struct gpio_private *todel;
|
||||
|
||||
spin_lock(&gpio_lock);
|
||||
|
||||
p = alarmlist;
|
||||
todel = (struct gpio_private *)filp->private_data;
|
||||
|
||||
/* unlink from alarmlist and free the private structure */
|
||||
|
||||
if (p == todel) {
|
||||
|
@ -476,7 +512,7 @@ gpio_release(struct inode *inode, struct file *filp)
|
|||
p = p->next;
|
||||
}
|
||||
gpio_some_alarms = 0;
|
||||
|
||||
spin_unlock(&gpio_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -491,14 +527,14 @@ unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg)
|
|||
*/
|
||||
unsigned long flags;
|
||||
if (USE_PORTS(priv)) {
|
||||
local_irq_save(flags); local_irq_disable();
|
||||
local_irq_save(flags);
|
||||
*priv->dir = *priv->dir_shadow &=
|
||||
~((unsigned char)arg & priv->changeable_dir);
|
||||
local_irq_restore(flags);
|
||||
return ~(*priv->dir_shadow) & 0xFF; /* Only 8 bits */
|
||||
} else if (priv->minor == GPIO_MINOR_G) {
|
||||
/* We must fiddle with R_GEN_CONFIG to change dir */
|
||||
save_flags(flags); cli();
|
||||
local_irq_save(flags);
|
||||
if (((arg & dir_g_in_bits) != arg) &&
|
||||
(arg & changeable_dir_g)) {
|
||||
arg &= changeable_dir_g;
|
||||
|
@ -533,7 +569,7 @@ unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg)
|
|||
/* Must be a >120 ns delay before writing this again */
|
||||
|
||||
}
|
||||
restore_flags(flags);
|
||||
local_irq_restore(flags);
|
||||
return dir_g_in_bits;
|
||||
}
|
||||
return 0;
|
||||
|
@ -543,14 +579,14 @@ unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg)
|
|||
{
|
||||
unsigned long flags;
|
||||
if (USE_PORTS(priv)) {
|
||||
local_irq_save(flags); local_irq_disable();
|
||||
local_irq_save(flags);
|
||||
*priv->dir = *priv->dir_shadow |=
|
||||
((unsigned char)arg & priv->changeable_dir);
|
||||
local_irq_restore(flags);
|
||||
return *priv->dir_shadow;
|
||||
} else if (priv->minor == GPIO_MINOR_G) {
|
||||
/* We must fiddle with R_GEN_CONFIG to change dir */
|
||||
save_flags(flags); cli();
|
||||
local_irq_save(flags);
|
||||
if (((arg & dir_g_out_bits) != arg) &&
|
||||
(arg & changeable_dir_g)) {
|
||||
/* Set bits in genconfig to set to output */
|
||||
|
@ -583,7 +619,7 @@ unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg)
|
|||
*R_GEN_CONFIG = genconfig_shadow;
|
||||
/* Must be a >120 ns delay before writing this again */
|
||||
}
|
||||
restore_flags(flags);
|
||||
local_irq_restore(flags);
|
||||
return dir_g_out_bits & 0x7FFFFFFF;
|
||||
}
|
||||
return 0;
|
||||
|
@ -598,22 +634,26 @@ gpio_ioctl(struct inode *inode, struct file *file,
|
|||
{
|
||||
unsigned long flags;
|
||||
unsigned long val;
|
||||
int ret = 0;
|
||||
|
||||
struct gpio_private *priv = (struct gpio_private *)file->private_data;
|
||||
if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock(&gpio_lock);
|
||||
|
||||
switch (_IOC_NR(cmd)) {
|
||||
case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */
|
||||
// read the port
|
||||
if (USE_PORTS(priv)) {
|
||||
return *priv->port;
|
||||
ret = *priv->port;
|
||||
} else if (priv->minor == GPIO_MINOR_G) {
|
||||
return (*R_PORT_G_DATA) & 0x7FFFFFFF;
|
||||
ret = (*R_PORT_G_DATA) & 0x7FFFFFFF;
|
||||
}
|
||||
break;
|
||||
case IO_SETBITS:
|
||||
local_irq_save(flags); local_irq_disable();
|
||||
local_irq_save(flags);
|
||||
// set changeable bits with a 1 in arg
|
||||
if (USE_PORTS(priv)) {
|
||||
*priv->port = *priv->shadow |=
|
||||
|
@ -624,7 +664,7 @@ gpio_ioctl(struct inode *inode, struct file *file,
|
|||
local_irq_restore(flags);
|
||||
break;
|
||||
case IO_CLRBITS:
|
||||
local_irq_save(flags); local_irq_disable();
|
||||
local_irq_save(flags);
|
||||
// clear changeable bits with a 1 in arg
|
||||
if (USE_PORTS(priv)) {
|
||||
*priv->port = *priv->shadow &=
|
||||
|
@ -666,33 +706,34 @@ gpio_ioctl(struct inode *inode, struct file *file,
|
|||
case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */
|
||||
/* Read direction 0=input 1=output */
|
||||
if (USE_PORTS(priv)) {
|
||||
return *priv->dir_shadow;
|
||||
ret = *priv->dir_shadow;
|
||||
} else if (priv->minor == GPIO_MINOR_G) {
|
||||
/* Note: Some bits are both in and out,
|
||||
* Those that are dual is set here as well.
|
||||
*/
|
||||
return (dir_g_shadow | dir_g_out_bits) & 0x7FFFFFFF;
|
||||
ret = (dir_g_shadow | dir_g_out_bits) & 0x7FFFFFFF;
|
||||
}
|
||||
break;
|
||||
case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */
|
||||
/* Set direction 0=unchanged 1=input,
|
||||
* return mask with 1=input
|
||||
*/
|
||||
return setget_input(priv, arg) & 0x7FFFFFFF;
|
||||
ret = setget_input(priv, arg) & 0x7FFFFFFF;
|
||||
break;
|
||||
case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */
|
||||
/* Set direction 0=unchanged 1=output,
|
||||
* return mask with 1=output
|
||||
*/
|
||||
return setget_output(priv, arg) & 0x7FFFFFFF;
|
||||
|
||||
ret = setget_output(priv, arg) & 0x7FFFFFFF;
|
||||
break;
|
||||
case IO_SHUTDOWN:
|
||||
SOFT_SHUTDOWN();
|
||||
break;
|
||||
case IO_GET_PWR_BT:
|
||||
#if defined (CONFIG_ETRAX_SOFT_SHUTDOWN)
|
||||
return (*R_PORT_G_DATA & ( 1 << CONFIG_ETRAX_POWERBUTTON_BIT));
|
||||
ret = (*R_PORT_G_DATA & ( 1 << CONFIG_ETRAX_POWERBUTTON_BIT));
|
||||
#else
|
||||
return 0;
|
||||
ret = 0;
|
||||
#endif
|
||||
break;
|
||||
case IO_CFG_WRITE_MODE:
|
||||
|
@ -709,7 +750,7 @@ gpio_ioctl(struct inode *inode, struct file *file,
|
|||
{
|
||||
priv->clk_mask = 0;
|
||||
priv->data_mask = 0;
|
||||
return -EPERM;
|
||||
ret = -EPERM;
|
||||
}
|
||||
break;
|
||||
case IO_READ_INBITS:
|
||||
|
@ -720,8 +761,7 @@ gpio_ioctl(struct inode *inode, struct file *file,
|
|||
val = *R_PORT_G_DATA;
|
||||
}
|
||||
if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
case IO_READ_OUTBITS:
|
||||
/* *arg is result of reading the output shadow */
|
||||
|
@ -731,36 +771,43 @@ gpio_ioctl(struct inode *inode, struct file *file,
|
|||
val = port_g_data_shadow;
|
||||
}
|
||||
if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
|
||||
return -EFAULT;
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
case IO_SETGET_INPUT:
|
||||
/* bits set in *arg is set to input,
|
||||
* *arg updated with current input pins.
|
||||
*/
|
||||
if (copy_from_user(&val, (unsigned long*)arg, sizeof(val)))
|
||||
return -EFAULT;
|
||||
{
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
val = setget_input(priv, val);
|
||||
if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
|
||||
return -EFAULT;
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
case IO_SETGET_OUTPUT:
|
||||
/* bits set in *arg is set to output,
|
||||
* *arg updated with current output pins.
|
||||
*/
|
||||
if (copy_from_user(&val, (unsigned long*)arg, sizeof(val)))
|
||||
return -EFAULT;
|
||||
{
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
val = setget_output(priv, val);
|
||||
if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
|
||||
return -EFAULT;
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
default:
|
||||
if (priv->minor == GPIO_MINOR_LEDS)
|
||||
return gpio_leds_ioctl(cmd, arg);
|
||||
ret = gpio_leds_ioctl(cmd, arg);
|
||||
else
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
} /* switch */
|
||||
|
||||
return 0;
|
||||
|
||||
spin_unlock(&gpio_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -802,60 +849,20 @@ struct file_operations gpio_fops = {
|
|||
};
|
||||
|
||||
|
||||
static void __init gpio_init_port_g(void)
|
||||
void ioif_watcher(const unsigned int gpio_in_available,
|
||||
const unsigned int gpio_out_available,
|
||||
const unsigned char pa_available,
|
||||
const unsigned char pb_available)
|
||||
{
|
||||
#define GROUPA (0x0000FF3F)
|
||||
#define GROUPB (1<<6 | 1<<7)
|
||||
#define GROUPC (1<<30 | 1<<31)
|
||||
#define GROUPD (0x3FFF0000)
|
||||
#define GROUPD_LOW (0x00FF0000)
|
||||
unsigned long used_in_bits = 0;
|
||||
unsigned long used_out_bits = 0;
|
||||
if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, scsi0, select)){
|
||||
used_in_bits |= GROUPA | GROUPB | 0 | 0;
|
||||
used_out_bits |= GROUPA | GROUPB | 0 | 0;
|
||||
}
|
||||
if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, ata, select)) {
|
||||
used_in_bits |= GROUPA | GROUPB | GROUPC | (GROUPD & ~(1<<25|1<<26));
|
||||
used_out_bits |= GROUPA | GROUPB | GROUPC | GROUPD;
|
||||
}
|
||||
unsigned long int flags;
|
||||
D(printk("gpio.c: ioif_watcher called\n"));
|
||||
D(printk("gpio.c: G in: 0x%08x G out: 0x%08x PA: 0x%02x PB: 0x%02x\n",
|
||||
gpio_in_available, gpio_out_available, pa_available, pb_available));
|
||||
|
||||
if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, par0, select)) {
|
||||
used_in_bits |= (GROUPA & ~(1<<0)) | 0 | 0 | 0;
|
||||
used_out_bits |= (GROUPA & ~(1<<0)) | 0 | 0 | 0;
|
||||
}
|
||||
if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, ser2, select)) {
|
||||
used_in_bits |= 0 | GROUPB | 0 | 0;
|
||||
used_out_bits |= 0 | GROUPB | 0 | 0;
|
||||
}
|
||||
/* mio same as shared RAM ? */
|
||||
if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, mio, select)) {
|
||||
used_in_bits |= (GROUPA & ~(1<<0)) | 0 |0 |GROUPD_LOW;
|
||||
used_out_bits |= (GROUPA & ~(1<<0|1<<1|1<<2)) | 0 |0 |GROUPD_LOW;
|
||||
}
|
||||
if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, scsi1, select)) {
|
||||
used_in_bits |= 0 | 0 | GROUPC | GROUPD;
|
||||
used_out_bits |= 0 | 0 | GROUPC | GROUPD;
|
||||
}
|
||||
if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, scsi0w, select)) {
|
||||
used_in_bits |= GROUPA | GROUPB | 0 | (GROUPD_LOW | 1<<24);
|
||||
used_out_bits |= GROUPA | GROUPB | 0 | (GROUPD_LOW | 1<<24 | 1<<25|1<<26);
|
||||
}
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
|
||||
if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, par1, select)) {
|
||||
used_in_bits |= 0 | 0 | 0 | (GROUPD & ~(1<<24));
|
||||
used_out_bits |= 0 | 0 | 0 | (GROUPD & ~(1<<24));
|
||||
}
|
||||
if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, ser3, select)) {
|
||||
used_in_bits |= 0 | 0 | GROUPC | 0;
|
||||
used_out_bits |= 0 | 0 | GROUPC | 0;
|
||||
}
|
||||
/* mio same as shared RAM-W? */
|
||||
if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, mio_w, select)) {
|
||||
used_in_bits |= (GROUPA & ~(1<<0)) | 0 | 0 |GROUPD_LOW;
|
||||
used_out_bits |= (GROUPA & ~(1<<0|1<<1|1<<2)) | 0 | 0 |GROUPD_LOW;
|
||||
}
|
||||
/* TODO: USB p2, parw, sync ser3? */
|
||||
dir_g_in_bits = gpio_in_available;
|
||||
dir_g_out_bits = gpio_out_available;
|
||||
|
||||
/* Initialise the dir_g_shadow etc. depending on genconfig */
|
||||
/* 0=input 1=output */
|
||||
|
@ -868,10 +875,7 @@ static void __init gpio_init_port_g(void)
|
|||
if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g24dir, out))
|
||||
dir_g_shadow |= (1 << 24);
|
||||
|
||||
dir_g_in_bits = ~used_in_bits;
|
||||
dir_g_out_bits = ~used_out_bits;
|
||||
|
||||
changeable_dir_g = 0x01FFFF01; /* all that can change dir */
|
||||
changeable_dir_g = changeable_dir_g_mask;
|
||||
changeable_dir_g &= dir_g_out_bits;
|
||||
changeable_dir_g &= dir_g_in_bits;
|
||||
/* Correct the bits that can change direction */
|
||||
|
@ -880,6 +884,7 @@ static void __init gpio_init_port_g(void)
|
|||
dir_g_in_bits &= ~changeable_dir_g;
|
||||
dir_g_in_bits |= (~dir_g_shadow & changeable_dir_g);
|
||||
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
|
||||
printk(KERN_INFO "GPIO port G: in_bits: 0x%08lX out_bits: 0x%08lX val: %08lX\n",
|
||||
dir_g_in_bits, dir_g_out_bits, (unsigned long)*R_PORT_G_DATA);
|
||||
|
@ -896,6 +901,7 @@ gpio_init(void)
|
|||
#if defined (CONFIG_ETRAX_CSP0_LEDS)
|
||||
int i;
|
||||
#endif
|
||||
printk("gpio init\n");
|
||||
|
||||
/* do the formalities */
|
||||
|
||||
|
@ -919,8 +925,13 @@ gpio_init(void)
|
|||
#endif
|
||||
|
||||
#endif
|
||||
gpio_init_port_g();
|
||||
printk(KERN_INFO "ETRAX 100LX GPIO driver v2.5, (c) 2001, 2002 Axis Communications AB\n");
|
||||
/* The I/O interface allocation watcher will be called when
|
||||
* registering it. */
|
||||
if (cris_io_interface_register_watcher(ioif_watcher)){
|
||||
printk(KERN_WARNING "gpio_init: Failed to install IO if allocator watcher\n");
|
||||
}
|
||||
|
||||
printk(KERN_INFO "ETRAX 100LX GPIO driver v2.5, (c) 2001, 2002, 2003, 2004 Axis Communications AB\n");
|
||||
/* We call etrax_gpio_wake_up_check() from timer interrupt and
|
||||
* from cpu_idle() in kernel/process.c
|
||||
* The check in cpu_idle() reduces latency from ~15 ms to ~6 ms
|
||||
|
|
|
@ -12,6 +12,15 @@
|
|||
*! don't use PB_I2C if DS1302 uses same bits,
|
||||
*! use PB.
|
||||
*! $Log: i2c.c,v $
|
||||
*! Revision 1.13 2005/03/07 13:13:07 starvik
|
||||
*! Added spinlocks to protect states etc
|
||||
*!
|
||||
*! Revision 1.12 2005/01/05 06:11:22 starvik
|
||||
*! No need to do local_irq_disable after local_irq_save.
|
||||
*!
|
||||
*! Revision 1.11 2004/12/13 12:21:52 starvik
|
||||
*! Added I/O and DMA allocators from Linux 2.4
|
||||
*!
|
||||
*! Revision 1.9 2004/08/24 06:49:14 starvik
|
||||
*! Whitespace cleanup
|
||||
*!
|
||||
|
@ -75,7 +84,7 @@
|
|||
*! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN
|
||||
*!
|
||||
*!***************************************************************************/
|
||||
/* $Id: i2c.c,v 1.9 2004/08/24 06:49:14 starvik Exp $ */
|
||||
/* $Id: i2c.c,v 1.13 2005/03/07 13:13:07 starvik Exp $ */
|
||||
|
||||
/****************** INCLUDE FILES SECTION ***********************************/
|
||||
|
||||
|
@ -95,6 +104,7 @@
|
|||
#include <asm/arch/svinto.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/delay.h>
|
||||
#include <asm/arch/io_interface_mux.h>
|
||||
|
||||
#include "i2c.h"
|
||||
|
||||
|
@ -184,6 +194,7 @@ static const char i2c_name[] = "i2c";
|
|||
|
||||
#define i2c_delay(usecs) udelay(usecs)
|
||||
|
||||
static DEFINE_SPINLOCK(i2c_lock); /* Protect directions etc */
|
||||
|
||||
/****************** FUNCTION DEFINITION SECTION *************************/
|
||||
|
||||
|
@ -488,13 +499,14 @@ i2c_writereg(unsigned char theSlave, unsigned char theReg,
|
|||
int error, cntr = 3;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock(&i2c_lock);
|
||||
|
||||
do {
|
||||
error = 0;
|
||||
/*
|
||||
* we don't like to be interrupted
|
||||
*/
|
||||
local_irq_save(flags);
|
||||
local_irq_disable();
|
||||
|
||||
i2c_start();
|
||||
/*
|
||||
|
@ -538,6 +550,8 @@ i2c_writereg(unsigned char theSlave, unsigned char theReg,
|
|||
|
||||
i2c_delay(CLOCK_LOW_TIME);
|
||||
|
||||
spin_unlock(&i2c_lock);
|
||||
|
||||
return -error;
|
||||
}
|
||||
|
||||
|
@ -555,13 +569,14 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg)
|
|||
int error, cntr = 3;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock(&i2c_lock);
|
||||
|
||||
do {
|
||||
error = 0;
|
||||
/*
|
||||
* we don't like to be interrupted
|
||||
*/
|
||||
local_irq_save(flags);
|
||||
local_irq_disable();
|
||||
/*
|
||||
* generate start condition
|
||||
*/
|
||||
|
@ -620,6 +635,8 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg)
|
|||
|
||||
} while(error && cntr--);
|
||||
|
||||
spin_unlock(&i2c_lock);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
|
@ -686,15 +703,26 @@ static struct file_operations i2c_fops = {
|
|||
int __init
|
||||
i2c_init(void)
|
||||
{
|
||||
static int res = 0;
|
||||
static int first = 1;
|
||||
|
||||
if (!first) {
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Setup and enable the Port B I2C interface */
|
||||
|
||||
#ifndef CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C
|
||||
if ((res = cris_request_io_interface(if_i2c, "I2C"))) {
|
||||
printk(KERN_CRIT "i2c_init: Failed to get IO interface\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
*R_PORT_PB_I2C = port_pb_i2c_shadow |=
|
||||
IO_STATE(R_PORT_PB_I2C, i2c_en, on) |
|
||||
IO_FIELD(R_PORT_PB_I2C, i2c_d, 1) |
|
||||
IO_FIELD(R_PORT_PB_I2C, i2c_clk, 1) |
|
||||
IO_STATE(R_PORT_PB_I2C, i2c_oe_, enable);
|
||||
#endif
|
||||
|
||||
port_pb_dir_shadow &= ~IO_MASK(R_PORT_PB_DIR, dir0);
|
||||
port_pb_dir_shadow &= ~IO_MASK(R_PORT_PB_DIR, dir1);
|
||||
|
@ -702,8 +730,26 @@ i2c_init(void)
|
|||
*R_PORT_PB_DIR = (port_pb_dir_shadow |=
|
||||
IO_STATE(R_PORT_PB_DIR, dir0, input) |
|
||||
IO_STATE(R_PORT_PB_DIR, dir1, output));
|
||||
#else
|
||||
if ((res = cris_io_interface_allocate_pins(if_i2c,
|
||||
'b',
|
||||
CONFIG_ETRAX_I2C_DATA_PORT,
|
||||
CONFIG_ETRAX_I2C_DATA_PORT))) {
|
||||
printk(KERN_WARNING "i2c_init: Failed to get IO pin for I2C data port\n");
|
||||
return res;
|
||||
} else if ((res = cris_io_interface_allocate_pins(if_i2c,
|
||||
'b',
|
||||
CONFIG_ETRAX_I2C_CLK_PORT,
|
||||
CONFIG_ETRAX_I2C_CLK_PORT))) {
|
||||
cris_io_interface_free_pins(if_i2c,
|
||||
'b',
|
||||
CONFIG_ETRAX_I2C_DATA_PORT,
|
||||
CONFIG_ETRAX_I2C_DATA_PORT);
|
||||
printk(KERN_WARNING "i2c_init: Failed to get IO pin for I2C clk port\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
static int __init
|
||||
|
@ -711,14 +757,16 @@ i2c_register(void)
|
|||
{
|
||||
int res;
|
||||
|
||||
i2c_init();
|
||||
res = i2c_init();
|
||||
if (res < 0)
|
||||
return res;
|
||||
res = register_chrdev(I2C_MAJOR, i2c_name, &i2c_fops);
|
||||
if(res < 0) {
|
||||
printk(KERN_ERR "i2c: couldn't get a major number.\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "I2C driver v2.2, (c) 1999-2001 Axis Communications AB\n");
|
||||
printk(KERN_INFO "I2C driver v2.2, (c) 1999-2004 Axis Communications AB\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*
|
||||
* Author: Tobias Anderberg <tobiasa@axis.com>.
|
||||
*
|
||||
* $Id: pcf8563.c,v 1.8 2004/08/24 06:42:51 starvik Exp $
|
||||
* $Id: pcf8563.c,v 1.11 2005/03/07 13:13:07 starvik Exp $
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
|
@ -40,7 +40,7 @@
|
|||
#define PCF8563_MAJOR 121 /* Local major number. */
|
||||
#define DEVICE_NAME "rtc" /* Name which is registered in /proc/devices. */
|
||||
#define PCF8563_NAME "PCF8563"
|
||||
#define DRIVER_VERSION "$Revision: 1.8 $"
|
||||
#define DRIVER_VERSION "$Revision: 1.11 $"
|
||||
|
||||
/* I2C bus slave registers. */
|
||||
#define RTC_I2C_READ 0xa3
|
||||
|
@ -49,6 +49,8 @@
|
|||
/* Two simple wrapper macros, saves a few keystrokes. */
|
||||
#define rtc_read(x) i2c_readreg(RTC_I2C_READ, x)
|
||||
#define rtc_write(x,y) i2c_writereg(RTC_I2C_WRITE, x, y)
|
||||
|
||||
static DEFINE_SPINLOCK(rtc_lock); /* Protect state etc */
|
||||
|
||||
static const unsigned char days_in_month[] =
|
||||
{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
||||
|
@ -125,9 +127,12 @@ get_rtc_time(struct rtc_time *tm)
|
|||
int __init
|
||||
pcf8563_init(void)
|
||||
{
|
||||
unsigned char ret;
|
||||
int ret;
|
||||
|
||||
i2c_init();
|
||||
if ((ret = i2c_init())) {
|
||||
printk(KERN_CRIT "pcf8563_init: failed to init i2c\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* First of all we need to reset the chip. This is done by
|
||||
|
@ -200,12 +205,15 @@ pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned
|
|||
{
|
||||
struct rtc_time tm;
|
||||
|
||||
spin_lock(&rtc_lock);
|
||||
get_rtc_time(&tm);
|
||||
|
||||
if (copy_to_user((struct rtc_time *) arg, &tm, sizeof(struct rtc_time))) {
|
||||
spin_unlock(&rtc_lock);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
spin_unlock(&rtc_lock);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
@ -250,6 +258,8 @@ pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned
|
|||
BIN_TO_BCD(tm.tm_min);
|
||||
BIN_TO_BCD(tm.tm_sec);
|
||||
tm.tm_mon |= century;
|
||||
|
||||
spin_lock(&rtc_lock);
|
||||
|
||||
rtc_write(RTC_YEAR, tm.tm_year);
|
||||
rtc_write(RTC_MONTH, tm.tm_mon);
|
||||
|
@ -258,6 +268,8 @@ pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned
|
|||
rtc_write(RTC_MINUTES, tm.tm_min);
|
||||
rtc_write(RTC_SECONDS, tm.tm_sec);
|
||||
|
||||
spin_unlock(&rtc_lock);
|
||||
|
||||
return 0;
|
||||
#endif /* !CONFIG_ETRAX_RTC_READONLY */
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# $Id: Makefile,v 1.5 2004/06/02 08:24:38 starvik Exp $
|
||||
# $Id: Makefile,v 1.6 2004/12/13 12:21:51 starvik Exp $
|
||||
#
|
||||
# Makefile for the linux kernel.
|
||||
#
|
||||
|
@ -7,7 +7,8 @@ extra-y := head.o
|
|||
|
||||
|
||||
obj-y := entry.o traps.o shadows.o debugport.o irq.o \
|
||||
process.o setup.o signal.o traps.o time.o ptrace.o
|
||||
process.o setup.o signal.o traps.o time.o ptrace.o \
|
||||
dma.o io_interface_mux.o
|
||||
|
||||
obj-$(CONFIG_ETRAX_KGDB) += kgdb.o
|
||||
obj-$(CONFIG_ETRAX_FAST_TIMER) += fasttimer.o
|
||||
|
|
|
@ -12,6 +12,31 @@
|
|||
* init_etrax_debug()
|
||||
*
|
||||
* $Log: debugport.c,v $
|
||||
* Revision 1.27 2005/06/10 10:34:14 starvik
|
||||
* Real console support
|
||||
*
|
||||
* Revision 1.26 2005/06/07 07:06:07 starvik
|
||||
* Added LF->CR translation to make ETRAX customers happy.
|
||||
*
|
||||
* Revision 1.25 2005/03/08 08:56:47 mikaelam
|
||||
* Do only set index as port->index if port is defined, otherwise use the index from the command line
|
||||
*
|
||||
* Revision 1.24 2005/01/19 10:26:33 mikaelam
|
||||
* Return the cris serial driver in console device driver callback function
|
||||
*
|
||||
* Revision 1.23 2005/01/14 10:12:17 starvik
|
||||
* KGDB on separate port.
|
||||
* Console fixes from 2.4.
|
||||
*
|
||||
* Revision 1.22 2005/01/11 16:06:13 starvik
|
||||
* typo
|
||||
*
|
||||
* Revision 1.21 2005/01/11 13:49:14 starvik
|
||||
* Added raw_printk to be used where we don't trust the console.
|
||||
*
|
||||
* Revision 1.20 2004/12/27 11:18:32 starvik
|
||||
* Merge of Linux 2.6.10 (not functional yet).
|
||||
*
|
||||
* Revision 1.19 2004/10/21 07:26:16 starvik
|
||||
* Made it possible to specify console settings on kernel command line.
|
||||
*
|
||||
|
@ -114,7 +139,11 @@ struct dbg_port ports[]=
|
|||
R_SERIAL0_BAUD,
|
||||
R_SERIAL0_TR_CTRL,
|
||||
R_SERIAL0_REC_CTRL,
|
||||
IO_STATE(R_IRQ_MASK1_SET, ser0_data, set)
|
||||
IO_STATE(R_IRQ_MASK1_SET, ser0_data, set),
|
||||
0,
|
||||
115200,
|
||||
'N',
|
||||
8
|
||||
},
|
||||
{
|
||||
1,
|
||||
|
@ -124,7 +153,11 @@ struct dbg_port ports[]=
|
|||
R_SERIAL1_BAUD,
|
||||
R_SERIAL1_TR_CTRL,
|
||||
R_SERIAL1_REC_CTRL,
|
||||
IO_STATE(R_IRQ_MASK1_SET, ser1_data, set)
|
||||
IO_STATE(R_IRQ_MASK1_SET, ser1_data, set),
|
||||
0,
|
||||
115200,
|
||||
'N',
|
||||
8
|
||||
},
|
||||
{
|
||||
2,
|
||||
|
@ -134,7 +167,11 @@ struct dbg_port ports[]=
|
|||
R_SERIAL2_BAUD,
|
||||
R_SERIAL2_TR_CTRL,
|
||||
R_SERIAL2_REC_CTRL,
|
||||
IO_STATE(R_IRQ_MASK1_SET, ser2_data, set)
|
||||
IO_STATE(R_IRQ_MASK1_SET, ser2_data, set),
|
||||
0,
|
||||
115200,
|
||||
'N',
|
||||
8
|
||||
},
|
||||
{
|
||||
3,
|
||||
|
@ -144,11 +181,15 @@ struct dbg_port ports[]=
|
|||
R_SERIAL3_BAUD,
|
||||
R_SERIAL3_TR_CTRL,
|
||||
R_SERIAL3_REC_CTRL,
|
||||
IO_STATE(R_IRQ_MASK1_SET, ser3_data, set)
|
||||
IO_STATE(R_IRQ_MASK1_SET, ser3_data, set),
|
||||
0,
|
||||
115200,
|
||||
'N',
|
||||
8
|
||||
}
|
||||
};
|
||||
|
||||
static struct tty_driver *serial_driver;
|
||||
extern struct tty_driver *serial_driver;
|
||||
|
||||
struct dbg_port* port =
|
||||
#if defined(CONFIG_ETRAX_DEBUG_PORT0)
|
||||
|
@ -162,37 +203,44 @@ struct dbg_port* port =
|
|||
#else
|
||||
NULL;
|
||||
#endif
|
||||
/* Used by serial.c to register a debug_write_function so that the normal
|
||||
* serial driver is used for kernel debug output
|
||||
*/
|
||||
typedef int (*debugport_write_function)(int i, const char *buf, unsigned int len);
|
||||
|
||||
debugport_write_function debug_write_function = NULL;
|
||||
static struct dbg_port* kgdb_port =
|
||||
#if defined(CONFIG_ETRAX_KGDB_PORT0)
|
||||
&ports[0];
|
||||
#elif defined(CONFIG_ETRAX_KGDB_PORT1)
|
||||
&ports[1];
|
||||
#elif defined(CONFIG_ETRAX_KGDB_PORT2)
|
||||
&ports[2];
|
||||
#elif defined(CONFIG_ETRAX_KGDB_PORT3)
|
||||
&ports[3];
|
||||
#else
|
||||
NULL;
|
||||
#endif
|
||||
|
||||
static void
|
||||
start_port(void)
|
||||
start_port(struct dbg_port* p)
|
||||
{
|
||||
unsigned long rec_ctrl = 0;
|
||||
unsigned long tr_ctrl = 0;
|
||||
|
||||
if (!port)
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
if (port->started)
|
||||
if (p->started)
|
||||
return;
|
||||
port->started = 1;
|
||||
p->started = 1;
|
||||
|
||||
if (port->index == 0)
|
||||
if (p->index == 0)
|
||||
{
|
||||
genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma6);
|
||||
genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma6, unused);
|
||||
}
|
||||
else if (port->index == 1)
|
||||
else if (p->index == 1)
|
||||
{
|
||||
genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma8);
|
||||
genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma8, usb);
|
||||
}
|
||||
else if (port->index == 2)
|
||||
else if (p->index == 2)
|
||||
{
|
||||
genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma2);
|
||||
genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma2, par0);
|
||||
|
@ -211,69 +259,69 @@ start_port(void)
|
|||
|
||||
*R_GEN_CONFIG = genconfig_shadow;
|
||||
|
||||
*port->xoff =
|
||||
*p->xoff =
|
||||
IO_STATE(R_SERIAL0_XOFF, tx_stop, enable) |
|
||||
IO_STATE(R_SERIAL0_XOFF, auto_xoff, disable) |
|
||||
IO_FIELD(R_SERIAL0_XOFF, xoff_char, 0);
|
||||
|
||||
switch (port->baudrate)
|
||||
switch (p->baudrate)
|
||||
{
|
||||
case 0:
|
||||
case 115200:
|
||||
*port->baud =
|
||||
*p->baud =
|
||||
IO_STATE(R_SERIAL0_BAUD, tr_baud, c115k2Hz) |
|
||||
IO_STATE(R_SERIAL0_BAUD, rec_baud, c115k2Hz);
|
||||
break;
|
||||
case 1200:
|
||||
*port->baud =
|
||||
*p->baud =
|
||||
IO_STATE(R_SERIAL0_BAUD, tr_baud, c1200Hz) |
|
||||
IO_STATE(R_SERIAL0_BAUD, rec_baud, c1200Hz);
|
||||
break;
|
||||
case 2400:
|
||||
*port->baud =
|
||||
*p->baud =
|
||||
IO_STATE(R_SERIAL0_BAUD, tr_baud, c2400Hz) |
|
||||
IO_STATE(R_SERIAL0_BAUD, rec_baud, c2400Hz);
|
||||
break;
|
||||
case 4800:
|
||||
*port->baud =
|
||||
*p->baud =
|
||||
IO_STATE(R_SERIAL0_BAUD, tr_baud, c4800Hz) |
|
||||
IO_STATE(R_SERIAL0_BAUD, rec_baud, c4800Hz);
|
||||
break;
|
||||
case 9600:
|
||||
*port->baud =
|
||||
*p->baud =
|
||||
IO_STATE(R_SERIAL0_BAUD, tr_baud, c9600Hz) |
|
||||
IO_STATE(R_SERIAL0_BAUD, rec_baud, c9600Hz);
|
||||
break;
|
||||
case 19200:
|
||||
*port->baud =
|
||||
*p->baud =
|
||||
IO_STATE(R_SERIAL0_BAUD, tr_baud, c19k2Hz) |
|
||||
IO_STATE(R_SERIAL0_BAUD, rec_baud, c19k2Hz);
|
||||
break;
|
||||
case 38400:
|
||||
*port->baud =
|
||||
*p->baud =
|
||||
IO_STATE(R_SERIAL0_BAUD, tr_baud, c38k4Hz) |
|
||||
IO_STATE(R_SERIAL0_BAUD, rec_baud, c38k4Hz);
|
||||
break;
|
||||
case 57600:
|
||||
*port->baud =
|
||||
*p->baud =
|
||||
IO_STATE(R_SERIAL0_BAUD, tr_baud, c57k6Hz) |
|
||||
IO_STATE(R_SERIAL0_BAUD, rec_baud, c57k6Hz);
|
||||
break;
|
||||
default:
|
||||
*port->baud =
|
||||
*p->baud =
|
||||
IO_STATE(R_SERIAL0_BAUD, tr_baud, c115k2Hz) |
|
||||
IO_STATE(R_SERIAL0_BAUD, rec_baud, c115k2Hz);
|
||||
break;
|
||||
}
|
||||
|
||||
if (port->parity == 'E') {
|
||||
if (p->parity == 'E') {
|
||||
rec_ctrl =
|
||||
IO_STATE(R_SERIAL0_REC_CTRL, rec_par, even) |
|
||||
IO_STATE(R_SERIAL0_REC_CTRL, rec_par_en, enable);
|
||||
tr_ctrl =
|
||||
IO_STATE(R_SERIAL0_TR_CTRL, tr_par, even) |
|
||||
IO_STATE(R_SERIAL0_TR_CTRL, tr_par_en, enable);
|
||||
} else if (port->parity == 'O') {
|
||||
} else if (p->parity == 'O') {
|
||||
rec_ctrl =
|
||||
IO_STATE(R_SERIAL0_REC_CTRL, rec_par, odd) |
|
||||
IO_STATE(R_SERIAL0_REC_CTRL, rec_par_en, enable);
|
||||
|
@ -288,8 +336,7 @@ start_port(void)
|
|||
IO_STATE(R_SERIAL0_TR_CTRL, tr_par, even) |
|
||||
IO_STATE(R_SERIAL0_TR_CTRL, tr_par_en, disable);
|
||||
}
|
||||
|
||||
if (port->bits == 7)
|
||||
if (p->bits == 7)
|
||||
{
|
||||
rec_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_bitnr, rec_7bit);
|
||||
tr_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_bitnr, tr_7bit);
|
||||
|
@ -300,7 +347,7 @@ start_port(void)
|
|||
tr_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_bitnr, tr_8bit);
|
||||
}
|
||||
|
||||
*port->rec_ctrl =
|
||||
*p->rec_ctrl =
|
||||
IO_STATE(R_SERIAL0_REC_CTRL, dma_err, stop) |
|
||||
IO_STATE(R_SERIAL0_REC_CTRL, rec_enable, enable) |
|
||||
IO_STATE(R_SERIAL0_REC_CTRL, rts_, active) |
|
||||
|
@ -308,7 +355,7 @@ start_port(void)
|
|||
IO_STATE(R_SERIAL0_REC_CTRL, rec_stick_par, normal) |
|
||||
rec_ctrl;
|
||||
|
||||
*port->tr_ctrl =
|
||||
*p->tr_ctrl =
|
||||
IO_FIELD(R_SERIAL0_TR_CTRL, txd, 0) |
|
||||
IO_STATE(R_SERIAL0_TR_CTRL, tr_enable, enable) |
|
||||
IO_STATE(R_SERIAL0_TR_CTRL, auto_cts, disabled) |
|
||||
|
@ -323,8 +370,18 @@ console_write_direct(struct console *co, const char *buf, unsigned int len)
|
|||
int i;
|
||||
unsigned long flags;
|
||||
local_irq_save(flags);
|
||||
|
||||
if (!port)
|
||||
return;
|
||||
|
||||
/* Send data */
|
||||
for (i = 0; i < len; i++) {
|
||||
/* LF -> CRLF */
|
||||
if (buf[i] == '\n') {
|
||||
while (!(*port->read & IO_MASK(R_SERIAL0_READ, tr_ready)))
|
||||
;
|
||||
*port->write = '\r';
|
||||
}
|
||||
/* Wait until transmitter is ready and send.*/
|
||||
while (!(*port->read & IO_MASK(R_SERIAL0_READ, tr_ready)))
|
||||
;
|
||||
|
@ -333,6 +390,25 @@ console_write_direct(struct console *co, const char *buf, unsigned int len)
|
|||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
int raw_printk(const char *fmt, ...)
|
||||
{
|
||||
static char buf[1024];
|
||||
int printed_len;
|
||||
static int first = 1;
|
||||
if (first) {
|
||||
/* Force reinitialization of the port to get manual mode. */
|
||||
port->started = 0;
|
||||
start_port(port);
|
||||
first = 0;
|
||||
}
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
printed_len = vsnprintf(buf, sizeof(buf), fmt, args);
|
||||
va_end(args);
|
||||
console_write_direct(NULL, buf, strlen(buf));
|
||||
return printed_len;
|
||||
}
|
||||
|
||||
static void
|
||||
console_write(struct console *co, const char *buf, unsigned int len)
|
||||
{
|
||||
|
@ -345,18 +421,7 @@ console_write(struct console *co, const char *buf, unsigned int len)
|
|||
return;
|
||||
#endif
|
||||
|
||||
start_port();
|
||||
|
||||
#ifdef CONFIG_ETRAX_KGDB
|
||||
/* kgdb needs to output debug info using the gdb protocol */
|
||||
putDebugString(buf, len);
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (debug_write_function)
|
||||
debug_write_function(co->index, buf, len);
|
||||
else
|
||||
console_write_direct(co, buf, len);
|
||||
console_write_direct(co, buf, len);
|
||||
}
|
||||
|
||||
/* legacy function */
|
||||
|
@ -374,8 +439,11 @@ getDebugChar(void)
|
|||
{
|
||||
unsigned long readval;
|
||||
|
||||
if (!kgdb_port)
|
||||
return 0;
|
||||
|
||||
do {
|
||||
readval = *port->read;
|
||||
readval = *kgdb_port->read;
|
||||
} while (!(readval & IO_MASK(R_SERIAL0_READ, data_avail)));
|
||||
|
||||
return (readval & IO_MASK(R_SERIAL0_READ, data_in));
|
||||
|
@ -386,9 +454,12 @@ getDebugChar(void)
|
|||
void
|
||||
putDebugChar(int val)
|
||||
{
|
||||
while (!(*port->read & IO_MASK(R_SERIAL0_READ, tr_ready)))
|
||||
if (!kgdb_port)
|
||||
return;
|
||||
|
||||
while (!(*kgdb_port->read & IO_MASK(R_SERIAL0_READ, tr_ready)))
|
||||
;
|
||||
*port->write = val;
|
||||
*kgdb_port->write = val;
|
||||
}
|
||||
|
||||
/* Enable irq for receiving chars on the debug port, used by kgdb */
|
||||
|
@ -396,19 +467,16 @@ putDebugChar(int val)
|
|||
void
|
||||
enableDebugIRQ(void)
|
||||
{
|
||||
*R_IRQ_MASK1_SET = port->irq;
|
||||
if (!kgdb_port)
|
||||
return;
|
||||
|
||||
*R_IRQ_MASK1_SET = kgdb_port->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);
|
||||
|
||||
*port->rec_ctrl = IO_STATE(R_SERIAL0_REC_CTRL, rec_enable, enable);
|
||||
}
|
||||
|
||||
static struct tty_driver*
|
||||
etrax_console_device(struct console* co, int *index)
|
||||
{
|
||||
return serial_driver;
|
||||
*kgdb_port->rec_ctrl = IO_STATE(R_SERIAL0_REC_CTRL, rec_enable, enable);
|
||||
}
|
||||
|
||||
static int __init
|
||||
|
@ -428,11 +496,69 @@ console_setup(struct console *co, char *options)
|
|||
if (*s) port->parity = *s++;
|
||||
if (*s) port->bits = *s++ - '0';
|
||||
port->started = 0;
|
||||
start_port();
|
||||
start_port(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This is a dummy serial device that throws away anything written to it.
|
||||
* This is used when no debug output is wanted.
|
||||
*/
|
||||
static struct tty_driver dummy_driver;
|
||||
|
||||
static int dummy_open(struct tty_struct *tty, struct file * filp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dummy_close(struct tty_struct *tty, struct file * filp)
|
||||
{
|
||||
}
|
||||
|
||||
static int dummy_write(struct tty_struct * tty,
|
||||
const unsigned char *buf, int count)
|
||||
{
|
||||
return count;
|
||||
}
|
||||
|
||||
static int
|
||||
dummy_write_room(struct tty_struct *tty)
|
||||
{
|
||||
return 8192;
|
||||
}
|
||||
|
||||
void __init
|
||||
init_dummy_console(void)
|
||||
{
|
||||
memset(&dummy_driver, 0, sizeof(struct tty_driver));
|
||||
dummy_driver.driver_name = "serial";
|
||||
dummy_driver.name = "ttyS";
|
||||
dummy_driver.major = TTY_MAJOR;
|
||||
dummy_driver.minor_start = 68;
|
||||
dummy_driver.num = 1; /* etrax100 has 4 serial ports */
|
||||
dummy_driver.type = TTY_DRIVER_TYPE_SERIAL;
|
||||
dummy_driver.subtype = SERIAL_TYPE_NORMAL;
|
||||
dummy_driver.init_termios = tty_std_termios;
|
||||
dummy_driver.init_termios.c_cflag =
|
||||
B115200 | CS8 | CREAD | HUPCL | CLOCAL; /* is normally B9600 default... */
|
||||
dummy_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
|
||||
|
||||
dummy_driver.open = dummy_open;
|
||||
dummy_driver.close = dummy_close;
|
||||
dummy_driver.write = dummy_write;
|
||||
dummy_driver.write_room = dummy_write_room;
|
||||
if (tty_register_driver(&dummy_driver))
|
||||
panic("Couldn't register dummy serial driver\n");
|
||||
}
|
||||
|
||||
static struct tty_driver*
|
||||
etrax_console_device(struct console* co, int *index)
|
||||
{
|
||||
if (port)
|
||||
*index = port->index;
|
||||
return port ? serial_driver : &dummy_driver;
|
||||
}
|
||||
|
||||
static struct console sercons = {
|
||||
name : "ttyS",
|
||||
write: console_write,
|
||||
|
@ -504,28 +630,21 @@ init_etrax_debug(void)
|
|||
static int first = 1;
|
||||
|
||||
if (!first) {
|
||||
if (!port) {
|
||||
register_console(&sercons0);
|
||||
register_console(&sercons1);
|
||||
register_console(&sercons2);
|
||||
register_console(&sercons3);
|
||||
unregister_console(&sercons);
|
||||
}
|
||||
unregister_console(&sercons);
|
||||
register_console(&sercons0);
|
||||
register_console(&sercons1);
|
||||
register_console(&sercons2);
|
||||
register_console(&sercons3);
|
||||
init_dummy_console();
|
||||
return 0;
|
||||
}
|
||||
|
||||
first = 0;
|
||||
if (port)
|
||||
register_console(&sercons);
|
||||
register_console(&sercons);
|
||||
start_port(port);
|
||||
#ifdef CONFIG_ETRAX_KGDB
|
||||
start_port(kgdb_port);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init
|
||||
init_console(void)
|
||||
{
|
||||
serial_driver = alloc_tty_driver(1);
|
||||
if (!serial_driver)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__initcall(init_etrax_debug);
|
||||
|
|
|
@ -0,0 +1,287 @@
|
|||
/* Wrapper for DMA channel allocator that updates DMA client muxing.
|
||||
* Copyright 2004, Axis Communications AB
|
||||
* $Id: dma.c,v 1.1 2004/12/13 12:21:51 starvik Exp $
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
|
||||
#include <asm/dma.h>
|
||||
#include <asm/arch/svinto.h>
|
||||
|
||||
/* Macro to access ETRAX 100 registers */
|
||||
#define SETS(var, reg, field, val) var = (var & ~IO_MASK_(reg##_, field##_)) | \
|
||||
IO_STATE_(reg##_, field##_, _##val)
|
||||
|
||||
|
||||
static char used_dma_channels[MAX_DMA_CHANNELS];
|
||||
static const char * used_dma_channels_users[MAX_DMA_CHANNELS];
|
||||
|
||||
int cris_request_dma(unsigned int dmanr, const char * device_id,
|
||||
unsigned options, enum dma_owner owner)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long int gens;
|
||||
int fail = -EINVAL;
|
||||
|
||||
if ((dmanr < 0) || (dmanr >= MAX_DMA_CHANNELS)) {
|
||||
printk(KERN_CRIT "cris_request_dma: invalid DMA channel %u\n", dmanr);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
local_irq_save(flags);
|
||||
if (used_dma_channels[dmanr]) {
|
||||
local_irq_restore(flags);
|
||||
if (options & DMA_VERBOSE_ON_ERROR) {
|
||||
printk(KERN_CRIT "Failed to request DMA %i for %s, already allocated by %s\n", dmanr, device_id, used_dma_channels_users[dmanr]);
|
||||
}
|
||||
if (options & DMA_PANIC_ON_ERROR) {
|
||||
panic("request_dma error!");
|
||||
}
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
gens = genconfig_shadow;
|
||||
|
||||
switch(owner)
|
||||
{
|
||||
case dma_eth:
|
||||
if ((dmanr != NETWORK_TX_DMA_NBR) &&
|
||||
(dmanr != NETWORK_RX_DMA_NBR)) {
|
||||
printk(KERN_CRIT "Invalid DMA channel for eth\n");
|
||||
goto bail;
|
||||
}
|
||||
break;
|
||||
case dma_ser0:
|
||||
if (dmanr == SER0_TX_DMA_NBR) {
|
||||
SETS(gens, R_GEN_CONFIG, dma6, serial0);
|
||||
} else if (dmanr == SER0_RX_DMA_NBR) {
|
||||
SETS(gens, R_GEN_CONFIG, dma7, serial0);
|
||||
} else {
|
||||
printk(KERN_CRIT "Invalid DMA channel for ser0\n");
|
||||
goto bail;
|
||||
}
|
||||
break;
|
||||
case dma_ser1:
|
||||
if (dmanr == SER1_TX_DMA_NBR) {
|
||||
SETS(gens, R_GEN_CONFIG, dma8, serial1);
|
||||
} else if (dmanr == SER1_RX_DMA_NBR) {
|
||||
SETS(gens, R_GEN_CONFIG, dma9, serial1);
|
||||
} else {
|
||||
printk(KERN_CRIT "Invalid DMA channel for ser1\n");
|
||||
goto bail;
|
||||
}
|
||||
break;
|
||||
case dma_ser2:
|
||||
if (dmanr == SER2_TX_DMA_NBR) {
|
||||
SETS(gens, R_GEN_CONFIG, dma2, serial2);
|
||||
} else if (dmanr == SER2_RX_DMA_NBR) {
|
||||
SETS(gens, R_GEN_CONFIG, dma3, serial2);
|
||||
} else {
|
||||
printk(KERN_CRIT "Invalid DMA channel for ser2\n");
|
||||
goto bail;
|
||||
}
|
||||
break;
|
||||
case dma_ser3:
|
||||
if (dmanr == SER3_TX_DMA_NBR) {
|
||||
SETS(gens, R_GEN_CONFIG, dma4, serial3);
|
||||
} else if (dmanr == SER3_RX_DMA_NBR) {
|
||||
SETS(gens, R_GEN_CONFIG, dma5, serial3);
|
||||
} else {
|
||||
printk(KERN_CRIT "Invalid DMA channel for ser3\n");
|
||||
goto bail;
|
||||
}
|
||||
break;
|
||||
case dma_ata:
|
||||
if (dmanr == ATA_TX_DMA_NBR) {
|
||||
SETS(gens, R_GEN_CONFIG, dma2, ata);
|
||||
} else if (dmanr == ATA_RX_DMA_NBR) {
|
||||
SETS(gens, R_GEN_CONFIG, dma3, ata);
|
||||
} else {
|
||||
printk(KERN_CRIT "Invalid DMA channel for ata\n");
|
||||
goto bail;
|
||||
}
|
||||
break;
|
||||
case dma_ext0:
|
||||
if (dmanr == EXTDMA0_TX_DMA_NBR) {
|
||||
SETS(gens, R_GEN_CONFIG, dma4, extdma0);
|
||||
} else if (dmanr == EXTDMA0_RX_DMA_NBR) {
|
||||
SETS(gens, R_GEN_CONFIG, dma5, extdma0);
|
||||
} else {
|
||||
printk(KERN_CRIT "Invalid DMA channel for ext0\n");
|
||||
goto bail;
|
||||
}
|
||||
break;
|
||||
case dma_ext1:
|
||||
if (dmanr == EXTDMA1_TX_DMA_NBR) {
|
||||
SETS(gens, R_GEN_CONFIG, dma6, extdma1);
|
||||
} else if (dmanr == EXTDMA1_RX_DMA_NBR) {
|
||||
SETS(gens, R_GEN_CONFIG, dma7, extdma1);
|
||||
} else {
|
||||
printk(KERN_CRIT "Invalid DMA channel for ext1\n");
|
||||
goto bail;
|
||||
}
|
||||
break;
|
||||
case dma_int6:
|
||||
if (dmanr == MEM2MEM_RX_DMA_NBR) {
|
||||
SETS(gens, R_GEN_CONFIG, dma7, intdma6);
|
||||
} else {
|
||||
printk(KERN_CRIT "Invalid DMA channel for int6\n");
|
||||
goto bail;
|
||||
}
|
||||
break;
|
||||
case dma_int7:
|
||||
if (dmanr == MEM2MEM_TX_DMA_NBR) {
|
||||
SETS(gens, R_GEN_CONFIG, dma6, intdma7);
|
||||
} else {
|
||||
printk(KERN_CRIT "Invalid DMA channel for int7\n");
|
||||
goto bail;
|
||||
}
|
||||
break;
|
||||
case dma_usb:
|
||||
if (dmanr == USB_TX_DMA_NBR) {
|
||||
SETS(gens, R_GEN_CONFIG, dma8, usb);
|
||||
} else if (dmanr == USB_RX_DMA_NBR) {
|
||||
SETS(gens, R_GEN_CONFIG, dma9, usb);
|
||||
} else {
|
||||
printk(KERN_CRIT "Invalid DMA channel for usb\n");
|
||||
goto bail;
|
||||
}
|
||||
break;
|
||||
case dma_scsi0:
|
||||
if (dmanr == SCSI0_TX_DMA_NBR) {
|
||||
SETS(gens, R_GEN_CONFIG, dma2, scsi0);
|
||||
} else if (dmanr == SCSI0_RX_DMA_NBR) {
|
||||
SETS(gens, R_GEN_CONFIG, dma3, scsi0);
|
||||
} else {
|
||||
printk(KERN_CRIT "Invalid DMA channel for scsi0\n");
|
||||
goto bail;
|
||||
}
|
||||
break;
|
||||
case dma_scsi1:
|
||||
if (dmanr == SCSI1_TX_DMA_NBR) {
|
||||
SETS(gens, R_GEN_CONFIG, dma4, scsi1);
|
||||
} else if (dmanr == SCSI1_RX_DMA_NBR) {
|
||||
SETS(gens, R_GEN_CONFIG, dma5, scsi1);
|
||||
} else {
|
||||
printk(KERN_CRIT "Invalid DMA channel for scsi1\n");
|
||||
goto bail;
|
||||
}
|
||||
break;
|
||||
case dma_par0:
|
||||
if (dmanr == PAR0_TX_DMA_NBR) {
|
||||
SETS(gens, R_GEN_CONFIG, dma2, par0);
|
||||
} else if (dmanr == PAR0_RX_DMA_NBR) {
|
||||
SETS(gens, R_GEN_CONFIG, dma3, par0);
|
||||
} else {
|
||||
printk(KERN_CRIT "Invalid DMA channel for par0\n");
|
||||
goto bail;
|
||||
}
|
||||
break;
|
||||
case dma_par1:
|
||||
if (dmanr == PAR1_TX_DMA_NBR) {
|
||||
SETS(gens, R_GEN_CONFIG, dma4, par1);
|
||||
} else if (dmanr == PAR1_RX_DMA_NBR) {
|
||||
SETS(gens, R_GEN_CONFIG, dma5, par1);
|
||||
} else {
|
||||
printk(KERN_CRIT "Invalid DMA channel for par1\n");
|
||||
goto bail;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printk(KERN_CRIT "Invalid DMA owner.\n");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
used_dma_channels[dmanr] = 1;
|
||||
used_dma_channels_users[dmanr] = device_id;
|
||||
|
||||
{
|
||||
volatile int i;
|
||||
genconfig_shadow = gens;
|
||||
*R_GEN_CONFIG = genconfig_shadow;
|
||||
/* Wait 12 cycles before doing any DMA command */
|
||||
for(i = 6; i > 0; i--)
|
||||
nop();
|
||||
}
|
||||
fail = 0;
|
||||
bail:
|
||||
local_irq_restore(flags);
|
||||
return fail;
|
||||
}
|
||||
|
||||
void cris_free_dma(unsigned int dmanr, const char * device_id)
|
||||
{
|
||||
unsigned long flags;
|
||||
if ((dmanr < 0) || (dmanr >= MAX_DMA_CHANNELS)) {
|
||||
printk(KERN_CRIT "cris_free_dma: invalid DMA channel %u\n", dmanr);
|
||||
return;
|
||||
}
|
||||
|
||||
local_irq_save(flags);
|
||||
if (!used_dma_channels[dmanr]) {
|
||||
printk(KERN_CRIT "cris_free_dma: DMA channel %u not allocated\n", dmanr);
|
||||
} else if (device_id != used_dma_channels_users[dmanr]) {
|
||||
printk(KERN_CRIT "cris_free_dma: DMA channel %u not allocated by device\n", dmanr);
|
||||
} else {
|
||||
switch(dmanr)
|
||||
{
|
||||
case 0:
|
||||
*R_DMA_CH0_CMD = IO_STATE(R_DMA_CH0_CMD, cmd, reset);
|
||||
while (IO_EXTRACT(R_DMA_CH0_CMD, cmd, *R_DMA_CH0_CMD) ==
|
||||
IO_STATE_VALUE(R_DMA_CH0_CMD, cmd, reset));
|
||||
break;
|
||||
case 1:
|
||||
*R_DMA_CH1_CMD = IO_STATE(R_DMA_CH1_CMD, cmd, reset);
|
||||
while (IO_EXTRACT(R_DMA_CH1_CMD, cmd, *R_DMA_CH1_CMD) ==
|
||||
IO_STATE_VALUE(R_DMA_CH1_CMD, cmd, reset));
|
||||
break;
|
||||
case 2:
|
||||
*R_DMA_CH2_CMD = IO_STATE(R_DMA_CH2_CMD, cmd, reset);
|
||||
while (IO_EXTRACT(R_DMA_CH2_CMD, cmd, *R_DMA_CH2_CMD) ==
|
||||
IO_STATE_VALUE(R_DMA_CH2_CMD, cmd, reset));
|
||||
break;
|
||||
case 3:
|
||||
*R_DMA_CH3_CMD = IO_STATE(R_DMA_CH3_CMD, cmd, reset);
|
||||
while (IO_EXTRACT(R_DMA_CH3_CMD, cmd, *R_DMA_CH3_CMD) ==
|
||||
IO_STATE_VALUE(R_DMA_CH3_CMD, cmd, reset));
|
||||
break;
|
||||
case 4:
|
||||
*R_DMA_CH4_CMD = IO_STATE(R_DMA_CH4_CMD, cmd, reset);
|
||||
while (IO_EXTRACT(R_DMA_CH4_CMD, cmd, *R_DMA_CH4_CMD) ==
|
||||
IO_STATE_VALUE(R_DMA_CH4_CMD, cmd, reset));
|
||||
break;
|
||||
case 5:
|
||||
*R_DMA_CH5_CMD = IO_STATE(R_DMA_CH5_CMD, cmd, reset);
|
||||
while (IO_EXTRACT(R_DMA_CH5_CMD, cmd, *R_DMA_CH5_CMD) ==
|
||||
IO_STATE_VALUE(R_DMA_CH5_CMD, cmd, reset));
|
||||
break;
|
||||
case 6:
|
||||
*R_DMA_CH6_CMD = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
|
||||
while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *R_DMA_CH6_CMD) ==
|
||||
IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
|
||||
break;
|
||||
case 7:
|
||||
*R_DMA_CH7_CMD = IO_STATE(R_DMA_CH7_CMD, cmd, reset);
|
||||
while (IO_EXTRACT(R_DMA_CH7_CMD, cmd, *R_DMA_CH7_CMD) ==
|
||||
IO_STATE_VALUE(R_DMA_CH7_CMD, cmd, reset));
|
||||
break;
|
||||
case 8:
|
||||
*R_DMA_CH8_CMD = IO_STATE(R_DMA_CH8_CMD, cmd, reset);
|
||||
while (IO_EXTRACT(R_DMA_CH8_CMD, cmd, *R_DMA_CH8_CMD) ==
|
||||
IO_STATE_VALUE(R_DMA_CH8_CMD, cmd, reset));
|
||||
break;
|
||||
case 9:
|
||||
*R_DMA_CH9_CMD = IO_STATE(R_DMA_CH9_CMD, cmd, reset);
|
||||
while (IO_EXTRACT(R_DMA_CH9_CMD, cmd, *R_DMA_CH9_CMD) ==
|
||||
IO_STATE_VALUE(R_DMA_CH9_CMD, cmd, reset));
|
||||
break;
|
||||
}
|
||||
used_dma_channels[dmanr] = 0;
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(cris_request_dma);
|
||||
EXPORT_SYMBOL(cris_free_dma);
|
|
@ -1,4 +1,4 @@
|
|||
/* $Id: entry.S,v 1.23 2004/10/19 13:07:37 starvik Exp $
|
||||
/* $Id: entry.S,v 1.28 2005/06/20 05:06:30 starvik Exp $
|
||||
*
|
||||
* linux/arch/cris/entry.S
|
||||
*
|
||||
|
@ -7,6 +7,22 @@
|
|||
* Authors: Bjorn Wesen (bjornw@axis.com)
|
||||
*
|
||||
* $Log: entry.S,v $
|
||||
* Revision 1.28 2005/06/20 05:06:30 starvik
|
||||
* Remove unnecessary diff to kernel.org tree
|
||||
*
|
||||
* Revision 1.27 2005/03/04 08:16:16 starvik
|
||||
* Merge of Linux 2.6.11.
|
||||
*
|
||||
* Revision 1.26 2005/01/11 13:49:47 starvik
|
||||
* Added NMI handler.
|
||||
*
|
||||
* Revision 1.25 2004/12/27 11:18:32 starvik
|
||||
* Merge of Linux 2.6.10 (not functional yet).
|
||||
*
|
||||
* Revision 1.24 2004/12/22 10:41:23 starvik
|
||||
* Updates to make v10 compile with the latest SMP aware generic code (even
|
||||
* though v10 will never have SMP).
|
||||
*
|
||||
* Revision 1.23 2004/10/19 13:07:37 starvik
|
||||
* Merge of Linux 2.6.9
|
||||
*
|
||||
|
@ -279,6 +295,7 @@
|
|||
#ifdef CONFIG_PREEMPT
|
||||
; Check if preemptive kernel scheduling should be done
|
||||
_resume_kernel:
|
||||
di
|
||||
; Load current task struct
|
||||
movs.w -8192, $r0 ; THREAD_SIZE = 8192
|
||||
and.d $sp, $r0
|
||||
|
@ -291,12 +308,7 @@ _need_resched:
|
|||
bpl _Rexit
|
||||
nop
|
||||
; Ok, lets's do some preemptive kernel scheduling
|
||||
move.d PREEMPT_ACTIVE, $r10
|
||||
move.d $r10, [$r0+TI_preempt_count] ; Mark as active
|
||||
ei
|
||||
jsr schedule
|
||||
clear.d [$r0+TI_preempt_count] ; Mark as inactive
|
||||
di
|
||||
jsr preempt_schedule_irq
|
||||
; Load new task struct
|
||||
movs.w -8192, $r0 ; THREAD_SIZE = 8192
|
||||
and.d $sp, $r0
|
||||
|
@ -590,15 +602,15 @@ mmu_bus_fault:
|
|||
move.d $r0, [$sp+16]
|
||||
1: btstq 12, $r1 ; Refill?
|
||||
bpl 2f
|
||||
lsrq PMD_SHIFT, $r1 ; Get PMD index into PGD (bit 24-31)
|
||||
move.d [current_pgd], $r0 ; PGD for the current process
|
||||
lsrq 24, $r1 ; Get PGD index (bit 24-31)
|
||||
move.d [per_cpu__current_pgd], $r0 ; PGD for the current process
|
||||
move.d [$r0+$r1.d], $r0 ; Get PMD
|
||||
beq 2f
|
||||
nop
|
||||
and.w PAGE_MASK, $r0 ; Remove PMD flags
|
||||
move.d [R_MMU_CAUSE], $r1
|
||||
lsrq PAGE_SHIFT, $r1
|
||||
and.d 0x7ff, $r1 ; Get PTE index into PMD (bit 13-24)
|
||||
and.d 0x7ff, $r1 ; Get PTE index into PGD (bit 13-23)
|
||||
move.d [$r0+$r1.d], $r1 ; Get PTE
|
||||
beq 2f
|
||||
nop
|
||||
|
@ -656,11 +668,6 @@ hwbreakpoint:
|
|||
nop
|
||||
|
||||
IRQ1_interrupt:
|
||||
|
||||
#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
|
||||
;; If we receive a watchdog interrupt while it is not expected, then set
|
||||
;; up a canonical frame and dump register contents before dying.
|
||||
|
||||
;; this prologue MUST match the one in irq.h and the struct in ptregs.h!!!
|
||||
move $brp,[$sp=$sp-16]; instruction pointer and room for a fake SBFS frame
|
||||
push $srp
|
||||
|
@ -672,9 +679,16 @@ IRQ1_interrupt:
|
|||
push $r10 ; push orig_r10
|
||||
clear.d [$sp=$sp-4] ; frametype == 0, normal frame
|
||||
|
||||
;; We don't check that we actually were bit by the watchdog as opposed to
|
||||
;; an external NMI, since there is currently no handler for external NMI.
|
||||
|
||||
move.d [R_IRQ_MASK0_RD], $r1 ; External NMI or watchdog?
|
||||
and.d 0x80000000, $r1
|
||||
beq wdog
|
||||
move.d $sp, $r10
|
||||
jsr handle_nmi
|
||||
setf m ; Enable NMI again
|
||||
retb ; Return from NMI
|
||||
nop
|
||||
wdog:
|
||||
#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
|
||||
;; Check if we're waiting for reset to happen, as signalled by
|
||||
;; hard_reset_now setting cause_of_death to a magic value. If so, just
|
||||
;; get stuck until reset happens.
|
||||
|
@ -1118,6 +1132,10 @@ sys_call_table:
|
|||
.long sys_mq_getsetattr
|
||||
.long sys_ni_syscall /* reserved for kexec */
|
||||
.long sys_waitid
|
||||
.long sys_ni_syscall /* 285 */ /* available */
|
||||
.long sys_add_key
|
||||
.long sys_request_key
|
||||
.long sys_keyctl
|
||||
|
||||
/*
|
||||
* NOTE!! This doesn't have to be exact - we just have
|
||||
|
|
|
@ -1,10 +1,20 @@
|
|||
/* $Id: fasttimer.c,v 1.6 2004/05/14 10:18:39 starvik Exp $
|
||||
/* $Id: fasttimer.c,v 1.9 2005/03/04 08:16:16 starvik Exp $
|
||||
* linux/arch/cris/kernel/fasttimer.c
|
||||
*
|
||||
* Fast timers for ETRAX100/ETRAX100LX
|
||||
* This may be useful in other OS than Linux so use 2 space indentation...
|
||||
*
|
||||
* $Log: fasttimer.c,v $
|
||||
* Revision 1.9 2005/03/04 08:16:16 starvik
|
||||
* Merge of Linux 2.6.11.
|
||||
*
|
||||
* Revision 1.8 2005/01/05 06:09:29 starvik
|
||||
* cli()/sti() will be obsolete in 2.6.11.
|
||||
*
|
||||
* Revision 1.7 2005/01/03 13:35:46 starvik
|
||||
* Removed obsolete stuff.
|
||||
* Mark fast timer IRQ as not shared.
|
||||
*
|
||||
* Revision 1.6 2004/05/14 10:18:39 starvik
|
||||
* Export fast_timer_list
|
||||
*
|
||||
|
@ -148,8 +158,7 @@ static int debug_log_cnt_wrapped = 0;
|
|||
#define DEBUG_LOG(string, value) \
|
||||
{ \
|
||||
unsigned long log_flags; \
|
||||
save_flags(log_flags); \
|
||||
cli(); \
|
||||
local_irq_save(log_flags); \
|
||||
debug_log_string[debug_log_cnt] = (string); \
|
||||
debug_log_value[debug_log_cnt] = (unsigned long)(value); \
|
||||
if (++debug_log_cnt >= DEBUG_LOG_MAX) \
|
||||
|
@ -157,7 +166,7 @@ static int debug_log_cnt_wrapped = 0;
|
|||
debug_log_cnt = debug_log_cnt % DEBUG_LOG_MAX; \
|
||||
debug_log_cnt_wrapped = 1; \
|
||||
} \
|
||||
restore_flags(log_flags); \
|
||||
local_irq_restore(log_flags); \
|
||||
}
|
||||
#else
|
||||
#define DEBUG_LOG(string, value)
|
||||
|
@ -320,8 +329,7 @@ void start_one_shot_timer(struct fast_timer *t,
|
|||
|
||||
D1(printk("sft %s %d us\n", name, delay_us));
|
||||
|
||||
save_flags(flags);
|
||||
cli();
|
||||
local_irq_save(flags);
|
||||
|
||||
do_gettimeofday_fast(&t->tv_set);
|
||||
tmp = fast_timer_list;
|
||||
|
@ -395,7 +403,7 @@ void start_one_shot_timer(struct fast_timer *t,
|
|||
|
||||
D2(printk("start_one_shot_timer: %d us done\n", delay_us));
|
||||
|
||||
restore_flags(flags);
|
||||
local_irq_restore(flags);
|
||||
} /* start_one_shot_timer */
|
||||
|
||||
static inline int fast_timer_pending (const struct fast_timer * t)
|
||||
|
@ -425,11 +433,10 @@ int del_fast_timer(struct fast_timer * t)
|
|||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
save_flags(flags);
|
||||
cli();
|
||||
local_irq_save(flags);
|
||||
ret = detach_fast_timer(t);
|
||||
t->next = t->prev = NULL;
|
||||
restore_flags(flags);
|
||||
local_irq_restore(flags);
|
||||
return ret;
|
||||
} /* del_fast_timer */
|
||||
|
||||
|
@ -444,8 +451,7 @@ timer1_handler(int irq, void *dev_id, struct pt_regs *regs)
|
|||
struct fast_timer *t;
|
||||
unsigned long flags;
|
||||
|
||||
save_flags(flags);
|
||||
cli();
|
||||
local_irq_save(flags);
|
||||
|
||||
/* Clear timer1 irq */
|
||||
*R_IRQ_MASK0_CLR = IO_STATE(R_IRQ_MASK0_CLR, timer1, clr);
|
||||
|
@ -462,7 +468,7 @@ timer1_handler(int irq, void *dev_id, struct pt_regs *regs)
|
|||
fast_timer_running = 0;
|
||||
fast_timer_ints++;
|
||||
|
||||
restore_flags(flags);
|
||||
local_irq_restore(flags);
|
||||
|
||||
t = fast_timer_list;
|
||||
while (t)
|
||||
|
@ -482,8 +488,7 @@ timer1_handler(int irq, void *dev_id, struct pt_regs *regs)
|
|||
fast_timers_expired++;
|
||||
|
||||
/* Remove this timer before call, since it may reuse the timer */
|
||||
save_flags(flags);
|
||||
cli();
|
||||
local_irq_save(flags);
|
||||
if (t->prev)
|
||||
{
|
||||
t->prev->next = t->next;
|
||||
|
@ -498,7 +503,7 @@ timer1_handler(int irq, void *dev_id, struct pt_regs *regs)
|
|||
}
|
||||
t->prev = NULL;
|
||||
t->next = NULL;
|
||||
restore_flags(flags);
|
||||
local_irq_restore(flags);
|
||||
|
||||
if (t->function != NULL)
|
||||
{
|
||||
|
@ -515,8 +520,7 @@ timer1_handler(int irq, void *dev_id, struct pt_regs *regs)
|
|||
D1(printk(".\n"));
|
||||
}
|
||||
|
||||
save_flags(flags);
|
||||
cli();
|
||||
local_irq_save(flags);
|
||||
if ((t = fast_timer_list) != NULL)
|
||||
{
|
||||
/* Start next timer.. */
|
||||
|
@ -535,7 +539,7 @@ timer1_handler(int irq, void *dev_id, struct pt_regs *regs)
|
|||
#endif
|
||||
start_timer1(us);
|
||||
}
|
||||
restore_flags(flags);
|
||||
local_irq_restore(flags);
|
||||
break;
|
||||
}
|
||||
else
|
||||
|
@ -546,7 +550,7 @@ timer1_handler(int irq, void *dev_id, struct pt_regs *regs)
|
|||
D1(printk("e! %d\n", us));
|
||||
}
|
||||
}
|
||||
restore_flags(flags);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
if (!t)
|
||||
|
@ -748,13 +752,12 @@ static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len
|
|||
#endif
|
||||
|
||||
used += sprintf(bigbuf + used, "Active timers:\n");
|
||||
save_flags(flags);
|
||||
cli();
|
||||
local_irq_save(flags);
|
||||
t = fast_timer_list;
|
||||
while (t != NULL && (used+100 < BIG_BUF_SIZE))
|
||||
{
|
||||
nextt = t->next;
|
||||
restore_flags(flags);
|
||||
local_irq_restore(flags);
|
||||
used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu "
|
||||
"d: %6li us data: 0x%08lX"
|
||||
/* " func: 0x%08lX" */
|
||||
|
@ -768,14 +771,14 @@ static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len
|
|||
t->data
|
||||
/* , t->function */
|
||||
);
|
||||
cli();
|
||||
local_irq_disable();
|
||||
if (t->next != nextt)
|
||||
{
|
||||
printk(KERN_WARNING "timer removed!\n");
|
||||
}
|
||||
t = nextt;
|
||||
}
|
||||
restore_flags(flags);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
if (used - offset < len)
|
||||
|
@ -963,7 +966,7 @@ void fast_timer_init(void)
|
|||
if ((fasttimer_proc_entry = create_proc_entry( "fasttimer", 0, 0 )))
|
||||
fasttimer_proc_entry->read_proc = proc_fasttimer_read;
|
||||
#endif /* PROC_FS */
|
||||
if(request_irq(TIMER1_IRQ_NBR, timer1_handler, SA_SHIRQ,
|
||||
if(request_irq(TIMER1_IRQ_NBR, timer1_handler, 0,
|
||||
"fast timer int", NULL))
|
||||
{
|
||||
printk("err: timer1 irq\n");
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $Id: head.S,v 1.7 2004/05/14 07:58:01 starvik Exp $
|
||||
/* $Id: head.S,v 1.10 2005/06/20 05:12:54 starvik Exp $
|
||||
*
|
||||
* Head of the kernel - alter with care
|
||||
*
|
||||
|
@ -7,6 +7,16 @@
|
|||
* Authors: Bjorn Wesen (bjornw@axis.com)
|
||||
*
|
||||
* $Log: head.S,v $
|
||||
* Revision 1.10 2005/06/20 05:12:54 starvik
|
||||
* Remove unnecessary diff to kernel.org tree
|
||||
*
|
||||
* Revision 1.9 2004/12/13 12:21:51 starvik
|
||||
* Added I/O and DMA allocators from Linux 2.4
|
||||
*
|
||||
* Revision 1.8 2004/11/22 11:41:14 starvik
|
||||
* Kernel command line may be supplied to kernel. Not used by Axis but may
|
||||
* be used by customers.
|
||||
*
|
||||
* Revision 1.7 2004/05/14 07:58:01 starvik
|
||||
* Merge of changes from 2.4
|
||||
*
|
||||
|
@ -181,6 +191,7 @@
|
|||
|
||||
#define CRAMFS_MAGIC 0x28cd3d45
|
||||
#define RAM_INIT_MAGIC 0x56902387
|
||||
#define COMMAND_LINE_MAGIC 0x87109563
|
||||
|
||||
#define START_ETHERNET_CLOCK IO_STATE(R_NETWORK_GEN_CONFIG, enable, on) |\
|
||||
IO_STATE(R_NETWORK_GEN_CONFIG, phy, mii_clk)
|
||||
|
@ -490,6 +501,23 @@ _no_romfs_in_flash:
|
|||
|
||||
_start_it:
|
||||
|
||||
;; Check if kernel command line is supplied
|
||||
cmp.d COMMAND_LINE_MAGIC, $r10
|
||||
bne no_command_line
|
||||
nop
|
||||
|
||||
move.d 256, $r13
|
||||
move.d cris_command_line, $r10
|
||||
or.d 0x80000000, $r11 ; Make it virtual
|
||||
1:
|
||||
move.b [$r11+], $r12
|
||||
move.b $r12, [$r10+]
|
||||
subq 1, $r13
|
||||
bne 1b
|
||||
nop
|
||||
|
||||
no_command_line:
|
||||
|
||||
;; 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)
|
||||
|
@ -567,76 +595,32 @@ _start_it:
|
|||
;; Etrax product HW genconfig setup
|
||||
|
||||
moveq 0,$r0
|
||||
#if (!defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT0)) \
|
||||
&& !defined(CONFIG_DMA_MEMCPY)
|
||||
; DMA channels 6 and 7 to ser0, kgdb doesnt want DMA
|
||||
or.d IO_STATE (R_GEN_CONFIG, dma7, serial0) \
|
||||
| IO_STATE (R_GEN_CONFIG, dma6, serial0),$r0
|
||||
#endif
|
||||
#if !defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT1)
|
||||
; DMA channels 8 and 9 to ser1, kgdb doesnt want DMA
|
||||
or.d IO_STATE (R_GEN_CONFIG, dma9, serial1) \
|
||||
| IO_STATE (R_GEN_CONFIG, dma8, serial1),$r0
|
||||
#endif
|
||||
#ifdef CONFIG_DMA_MEMCPY
|
||||
; 6/7 memory-memory DMA
|
||||
or.d IO_STATE (R_GEN_CONFIG, dma7, intdma6) \
|
||||
| IO_STATE (R_GEN_CONFIG, dma6, intdma7),$r0
|
||||
#endif
|
||||
#ifdef CONFIG_ETRAX_SERIAL_PORT2
|
||||
; Enable serial port 2
|
||||
or.w IO_STATE (R_GEN_CONFIG, ser2, select),$r0
|
||||
#if !defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT2)
|
||||
; DMA channels 2 and 3 to ser2, kgdb doesnt want DMA
|
||||
or.d IO_STATE (R_GEN_CONFIG, dma3, serial2) \
|
||||
| IO_STATE (R_GEN_CONFIG, dma2, serial2),$r0
|
||||
#endif
|
||||
#endif
|
||||
#if defined(CONFIG_ETRAX_SERIAL_PORT3) || defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1)
|
||||
; Enable serial port 3
|
||||
or.w IO_STATE (R_GEN_CONFIG, ser3, select),$r0
|
||||
#if !defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT3)
|
||||
; DMA channels 4 and 5 to ser3, kgdb doesnt want DMA
|
||||
or.d IO_STATE (R_GEN_CONFIG, dma5, serial3) \
|
||||
| IO_STATE (R_GEN_CONFIG, dma4, serial3),$r0
|
||||
#endif
|
||||
#endif
|
||||
#if defined(CONFIG_ETRAX_PARALLEL_PORT0) || defined(CONFIG_ETRAX_ETHERNET_LPSLAVE)
|
||||
; parport 0 enabled using DMA 2/3
|
||||
or.w IO_STATE (R_GEN_CONFIG, par0, select),$r0
|
||||
#endif
|
||||
#if defined(CONFIG_ETRAX_PARALLEL_PORT1) || defined(CONFIG_ETRAX_ETHERNET_LPSLAVE)
|
||||
; parport 1 enabled using DMA 4/5
|
||||
or.w IO_STATE (R_GEN_CONFIG, par1, select),$r0
|
||||
#endif
|
||||
#ifdef CONFIG_ETRAX_IDE
|
||||
; DMA channels 2 and 3 to ATA, ATA enabled
|
||||
or.d IO_STATE (R_GEN_CONFIG, dma3, ata) \
|
||||
| IO_STATE (R_GEN_CONFIG, dma2, ata) \
|
||||
| IO_STATE (R_GEN_CONFIG, ata, select),$r0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ETRAX_USB_HOST_PORT1
|
||||
; Set the USB port 1 enable bit
|
||||
or.d IO_STATE (R_GEN_CONFIG, usb1, select),$r0
|
||||
#endif
|
||||
#ifdef CONFIG_ETRAX_USB_HOST_PORT2
|
||||
; Set the USB port 2 enable bit
|
||||
or.d IO_STATE (R_GEN_CONFIG, usb2, select),$r0
|
||||
#endif
|
||||
#ifdef CONFIG_ETRAX_USB_HOST
|
||||
; Connect DMA channels 8 and 9 to USB
|
||||
and.d (~(IO_MASK (R_GEN_CONFIG, dma9) \
|
||||
| IO_MASK (R_GEN_CONFIG, dma8))) \
|
||||
| IO_STATE (R_GEN_CONFIG, dma9, usb) \
|
||||
| IO_STATE (R_GEN_CONFIG, dma8, usb),$r0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_JULIETTE
|
||||
; DMA channels 4 and 5 to EXTDMA0, for Juliette
|
||||
or.d IO_STATE (R_GEN_CONFIG, dma5, extdma0) \
|
||||
| IO_STATE (R_GEN_CONFIG, dma4, extdma0),$r0
|
||||
#endif
|
||||
|
||||
;; Init interfaces (disable them).
|
||||
or.d IO_STATE (R_GEN_CONFIG, scsi0, disable) \
|
||||
| IO_STATE (R_GEN_CONFIG, ata, disable) \
|
||||
| IO_STATE (R_GEN_CONFIG, par0, disable) \
|
||||
| IO_STATE (R_GEN_CONFIG, ser2, disable) \
|
||||
| IO_STATE (R_GEN_CONFIG, mio, disable) \
|
||||
| IO_STATE (R_GEN_CONFIG, scsi1, disable) \
|
||||
| IO_STATE (R_GEN_CONFIG, scsi0w, disable) \
|
||||
| IO_STATE (R_GEN_CONFIG, par1, disable) \
|
||||
| IO_STATE (R_GEN_CONFIG, ser3, disable) \
|
||||
| IO_STATE (R_GEN_CONFIG, mio_w, disable) \
|
||||
| IO_STATE (R_GEN_CONFIG, usb1, disable) \
|
||||
| IO_STATE (R_GEN_CONFIG, usb2, disable) \
|
||||
| IO_STATE (R_GEN_CONFIG, par_w, disable),$r0
|
||||
|
||||
;; Init DMA channel muxing (set to unused clients).
|
||||
or.d IO_STATE (R_GEN_CONFIG, dma2, ata) \
|
||||
| IO_STATE (R_GEN_CONFIG, dma3, ata) \
|
||||
| IO_STATE (R_GEN_CONFIG, dma4, scsi1) \
|
||||
| IO_STATE (R_GEN_CONFIG, dma5, scsi1) \
|
||||
| IO_STATE (R_GEN_CONFIG, dma6, unused) \
|
||||
| IO_STATE (R_GEN_CONFIG, dma7, unused) \
|
||||
| IO_STATE (R_GEN_CONFIG, dma8, usb) \
|
||||
| IO_STATE (R_GEN_CONFIG, dma9, usb),$r0
|
||||
|
||||
|
||||
#if defined(CONFIG_ETRAX_DEF_R_PORT_G0_DIR_OUT)
|
||||
or.d IO_STATE (R_GEN_CONFIG, g0dir, out),$r0
|
||||
|
|
|
@ -0,0 +1,879 @@
|
|||
/* IO interface mux allocator for ETRAX100LX.
|
||||
* Copyright 2004, Axis Communications AB
|
||||
* $Id: io_interface_mux.c,v 1.2 2004/12/21 12:08:38 starvik Exp $
|
||||
*/
|
||||
|
||||
|
||||
/* C.f. ETRAX100LX Designer's Reference 20.9 */
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/arch/svinto.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/io_interface_mux.h>
|
||||
|
||||
|
||||
#define DBG(s)
|
||||
|
||||
/* Macro to access ETRAX 100 registers */
|
||||
#define SETS(var, reg, field, val) var = (var & ~IO_MASK_(reg##_, field##_)) | \
|
||||
IO_STATE_(reg##_, field##_, _##val)
|
||||
|
||||
enum io_if_group {
|
||||
group_a = (1<<0),
|
||||
group_b = (1<<1),
|
||||
group_c = (1<<2),
|
||||
group_d = (1<<3),
|
||||
group_e = (1<<4),
|
||||
group_f = (1<<5)
|
||||
};
|
||||
|
||||
struct watcher
|
||||
{
|
||||
void (*notify)(const unsigned int gpio_in_available,
|
||||
const unsigned int gpio_out_available,
|
||||
const unsigned char pa_available,
|
||||
const unsigned char pb_available);
|
||||
struct watcher *next;
|
||||
};
|
||||
|
||||
|
||||
struct if_group
|
||||
{
|
||||
enum io_if_group group;
|
||||
unsigned char used;
|
||||
enum cris_io_interface owner;
|
||||
};
|
||||
|
||||
|
||||
struct interface
|
||||
{
|
||||
enum cris_io_interface ioif;
|
||||
unsigned char groups;
|
||||
unsigned char used;
|
||||
char *owner;
|
||||
unsigned int gpio_g_in;
|
||||
unsigned int gpio_g_out;
|
||||
unsigned char gpio_b;
|
||||
};
|
||||
|
||||
static struct if_group if_groups[6] = {
|
||||
{
|
||||
.group = group_a,
|
||||
.used = 0,
|
||||
},
|
||||
{
|
||||
.group = group_b,
|
||||
.used = 0,
|
||||
},
|
||||
{
|
||||
.group = group_c,
|
||||
.used = 0,
|
||||
},
|
||||
{
|
||||
.group = group_d,
|
||||
.used = 0,
|
||||
},
|
||||
{
|
||||
.group = group_e,
|
||||
.used = 0,
|
||||
},
|
||||
{
|
||||
.group = group_f,
|
||||
.used = 0,
|
||||
}
|
||||
};
|
||||
|
||||
/* The order in the array must match the order of enum
|
||||
* cris_io_interface in io_interface_mux.h */
|
||||
static struct interface interfaces[] = {
|
||||
/* Begin Non-multiplexed interfaces */
|
||||
{
|
||||
.ioif = if_eth,
|
||||
.groups = 0,
|
||||
.gpio_g_in = 0,
|
||||
.gpio_g_out = 0,
|
||||
.gpio_b = 0
|
||||
},
|
||||
{
|
||||
.ioif = if_serial_0,
|
||||
.groups = 0,
|
||||
.gpio_g_in = 0,
|
||||
.gpio_g_out = 0,
|
||||
.gpio_b = 0
|
||||
},
|
||||
/* End Non-multiplexed interfaces */
|
||||
{
|
||||
.ioif = if_serial_1,
|
||||
.groups = group_e,
|
||||
.gpio_g_in = 0x00000000,
|
||||
.gpio_g_out = 0x00000000,
|
||||
.gpio_b = 0x00
|
||||
},
|
||||
{
|
||||
.ioif = if_serial_2,
|
||||
.groups = group_b,
|
||||
.gpio_g_in = 0x000000c0,
|
||||
.gpio_g_out = 0x000000c0,
|
||||
.gpio_b = 0x00
|
||||
},
|
||||
{
|
||||
.ioif = if_serial_3,
|
||||
.groups = group_c,
|
||||
.gpio_g_in = 0xc0000000,
|
||||
.gpio_g_out = 0xc0000000,
|
||||
.gpio_b = 0x00
|
||||
},
|
||||
{
|
||||
.ioif = if_sync_serial_1,
|
||||
.groups = group_e | group_f, /* if_sync_serial_1 and if_sync_serial_3
|
||||
can be used simultaneously */
|
||||
.gpio_g_in = 0x00000000,
|
||||
.gpio_g_out = 0x00000000,
|
||||
.gpio_b = 0x10
|
||||
},
|
||||
{
|
||||
.ioif = if_sync_serial_3,
|
||||
.groups = group_c | group_f,
|
||||
.gpio_g_in = 0xc0000000,
|
||||
.gpio_g_out = 0xc0000000,
|
||||
.gpio_b = 0x80
|
||||
},
|
||||
{
|
||||
.ioif = if_shared_ram,
|
||||
.groups = group_a,
|
||||
.gpio_g_in = 0x0000ff3e,
|
||||
.gpio_g_out = 0x0000ff38,
|
||||
.gpio_b = 0x00
|
||||
},
|
||||
{
|
||||
.ioif = if_shared_ram_w,
|
||||
.groups = group_a | group_d,
|
||||
.gpio_g_in = 0x00ffff3e,
|
||||
.gpio_g_out = 0x00ffff38,
|
||||
.gpio_b = 0x00
|
||||
},
|
||||
{
|
||||
.ioif = if_par_0,
|
||||
.groups = group_a,
|
||||
.gpio_g_in = 0x0000ff3e,
|
||||
.gpio_g_out = 0x0000ff3e,
|
||||
.gpio_b = 0x00
|
||||
},
|
||||
{
|
||||
.ioif = if_par_1,
|
||||
.groups = group_d,
|
||||
.gpio_g_in = 0x3eff0000,
|
||||
.gpio_g_out = 0x3eff0000,
|
||||
.gpio_b = 0x00
|
||||
},
|
||||
{
|
||||
.ioif = if_par_w,
|
||||
.groups = group_a | group_d,
|
||||
.gpio_g_in = 0x00ffff3e,
|
||||
.gpio_g_out = 0x00ffff3e,
|
||||
.gpio_b = 0x00
|
||||
},
|
||||
{
|
||||
.ioif = if_scsi8_0,
|
||||
.groups = group_a | group_b | group_f, /* if_scsi8_0 and if_scsi8_1
|
||||
can be used simultaneously */
|
||||
.gpio_g_in = 0x0000ffff,
|
||||
.gpio_g_out = 0x0000ffff,
|
||||
.gpio_b = 0x10
|
||||
},
|
||||
{
|
||||
.ioif = if_scsi8_1,
|
||||
.groups = group_c | group_d | group_f, /* if_scsi8_0 and if_scsi8_1
|
||||
can be used simultaneously */
|
||||
.gpio_g_in = 0xffff0000,
|
||||
.gpio_g_out = 0xffff0000,
|
||||
.gpio_b = 0x80
|
||||
},
|
||||
{
|
||||
.ioif = if_scsi_w,
|
||||
.groups = group_a | group_b | group_d | group_f,
|
||||
.gpio_g_in = 0x01ffffff,
|
||||
.gpio_g_out = 0x07ffffff,
|
||||
.gpio_b = 0x80
|
||||
},
|
||||
{
|
||||
.ioif = if_ata,
|
||||
.groups = group_a | group_b | group_c | group_d,
|
||||
.gpio_g_in = 0xf9ffffff,
|
||||
.gpio_g_out = 0xffffffff,
|
||||
.gpio_b = 0x80
|
||||
},
|
||||
{
|
||||
.ioif = if_csp,
|
||||
.groups = group_f, /* if_csp and if_i2c can be used simultaneously */
|
||||
.gpio_g_in = 0x00000000,
|
||||
.gpio_g_out = 0x00000000,
|
||||
.gpio_b = 0xfc
|
||||
},
|
||||
{
|
||||
.ioif = if_i2c,
|
||||
.groups = group_f, /* if_csp and if_i2c can be used simultaneously */
|
||||
.gpio_g_in = 0x00000000,
|
||||
.gpio_g_out = 0x00000000,
|
||||
.gpio_b = 0x03
|
||||
},
|
||||
{
|
||||
.ioif = if_usb_1,
|
||||
.groups = group_e | group_f,
|
||||
.gpio_g_in = 0x00000000,
|
||||
.gpio_g_out = 0x00000000,
|
||||
.gpio_b = 0x2c
|
||||
},
|
||||
{
|
||||
.ioif = if_usb_2,
|
||||
.groups = group_d,
|
||||
.gpio_g_in = 0x0e000000,
|
||||
.gpio_g_out = 0x3c000000,
|
||||
.gpio_b = 0x00
|
||||
},
|
||||
/* GPIO pins */
|
||||
{
|
||||
.ioif = if_gpio_grp_a,
|
||||
.groups = group_a,
|
||||
.gpio_g_in = 0x0000ff3f,
|
||||
.gpio_g_out = 0x0000ff3f,
|
||||
.gpio_b = 0x00
|
||||
},
|
||||
{
|
||||
.ioif = if_gpio_grp_b,
|
||||
.groups = group_b,
|
||||
.gpio_g_in = 0x000000c0,
|
||||
.gpio_g_out = 0x000000c0,
|
||||
.gpio_b = 0x00
|
||||
},
|
||||
{
|
||||
.ioif = if_gpio_grp_c,
|
||||
.groups = group_c,
|
||||
.gpio_g_in = 0xc0000000,
|
||||
.gpio_g_out = 0xc0000000,
|
||||
.gpio_b = 0x00
|
||||
},
|
||||
{
|
||||
.ioif = if_gpio_grp_d,
|
||||
.groups = group_d,
|
||||
.gpio_g_in = 0x3fff0000,
|
||||
.gpio_g_out = 0x3fff0000,
|
||||
.gpio_b = 0x00
|
||||
},
|
||||
{
|
||||
.ioif = if_gpio_grp_e,
|
||||
.groups = group_e,
|
||||
.gpio_g_in = 0x00000000,
|
||||
.gpio_g_out = 0x00000000,
|
||||
.gpio_b = 0x00
|
||||
},
|
||||
{
|
||||
.ioif = if_gpio_grp_f,
|
||||
.groups = group_f,
|
||||
.gpio_g_in = 0x00000000,
|
||||
.gpio_g_out = 0x00000000,
|
||||
.gpio_b = 0xff
|
||||
}
|
||||
/* Array end */
|
||||
};
|
||||
|
||||
static struct watcher *watchers = NULL;
|
||||
|
||||
static unsigned int gpio_in_pins = 0xffffffff;
|
||||
static unsigned int gpio_out_pins = 0xffffffff;
|
||||
static unsigned char gpio_pb_pins = 0xff;
|
||||
static unsigned char gpio_pa_pins = 0xff;
|
||||
|
||||
static enum cris_io_interface gpio_pa_owners[8];
|
||||
static enum cris_io_interface gpio_pb_owners[8];
|
||||
static enum cris_io_interface gpio_pg_owners[32];
|
||||
|
||||
static int cris_io_interface_init(void);
|
||||
|
||||
static unsigned char clear_group_from_set(const unsigned char groups, struct if_group *group)
|
||||
{
|
||||
return (groups & ~group->group);
|
||||
}
|
||||
|
||||
|
||||
static struct if_group *get_group(const unsigned char groups)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < sizeof(if_groups)/sizeof(struct if_group); i++) {
|
||||
if (groups & if_groups[i].group) {
|
||||
return &if_groups[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void notify_watchers(void)
|
||||
{
|
||||
struct watcher *w = watchers;
|
||||
|
||||
DBG(printk("io_interface_mux: notifying watchers\n"));
|
||||
|
||||
while (NULL != w) {
|
||||
w->notify((const unsigned int)gpio_in_pins,
|
||||
(const unsigned int)gpio_out_pins,
|
||||
(const unsigned char)gpio_pa_pins,
|
||||
(const unsigned char)gpio_pb_pins);
|
||||
w = w->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int cris_request_io_interface(enum cris_io_interface ioif, const char *device_id)
|
||||
{
|
||||
int set_gen_config = 0;
|
||||
int set_gen_config_ii = 0;
|
||||
unsigned long int gens;
|
||||
unsigned long int gens_ii;
|
||||
struct if_group *grp;
|
||||
unsigned char group_set;
|
||||
unsigned long flags;
|
||||
|
||||
(void)cris_io_interface_init();
|
||||
|
||||
DBG(printk("cris_request_io_interface(%d, \"%s\")\n", ioif, device_id));
|
||||
|
||||
if ((ioif >= if_max_interfaces) || (ioif < 0)) {
|
||||
printk(KERN_CRIT "cris_request_io_interface: Bad interface %u submitted for %s\n",
|
||||
ioif,
|
||||
device_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
if (interfaces[ioif].used) {
|
||||
local_irq_restore(flags);
|
||||
printk(KERN_CRIT "cris_io_interface: Cannot allocate interface for %s, in use by %s\n",
|
||||
device_id,
|
||||
interfaces[ioif].owner);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* Check that all required groups are free before allocating, */
|
||||
group_set = interfaces[ioif].groups;
|
||||
while (NULL != (grp = get_group(group_set))) {
|
||||
if (grp->used) {
|
||||
if (grp->group == group_f) {
|
||||
if ((if_sync_serial_1 == ioif) ||
|
||||
(if_sync_serial_3 == ioif)) {
|
||||
if ((grp->owner != if_sync_serial_1) &&
|
||||
(grp->owner != if_sync_serial_3)) {
|
||||
local_irq_restore(flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
} else if ((if_scsi8_0 == ioif) ||
|
||||
(if_scsi8_1 == ioif)) {
|
||||
if ((grp->owner != if_scsi8_0) &&
|
||||
(grp->owner != if_scsi8_1)) {
|
||||
local_irq_restore(flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
local_irq_restore(flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
group_set = clear_group_from_set(group_set, grp);
|
||||
}
|
||||
|
||||
/* Are the required GPIO pins available too? */
|
||||
if (((interfaces[ioif].gpio_g_in & gpio_in_pins) != interfaces[ioif].gpio_g_in) ||
|
||||
((interfaces[ioif].gpio_g_out & gpio_out_pins) != interfaces[ioif].gpio_g_out) ||
|
||||
((interfaces[ioif].gpio_b & gpio_pb_pins) != interfaces[ioif].gpio_b)) {
|
||||
printk(KERN_CRIT "cris_request_io_interface: Could not get required pins for interface %u\n",
|
||||
ioif);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* All needed I/O pins and pin groups are free, allocate. */
|
||||
group_set = interfaces[ioif].groups;
|
||||
while (NULL != (grp = get_group(group_set))) {
|
||||
grp->used = 1;
|
||||
grp->owner = ioif;
|
||||
group_set = clear_group_from_set(group_set, grp);
|
||||
}
|
||||
|
||||
gens = genconfig_shadow;
|
||||
gens_ii = gen_config_ii_shadow;
|
||||
|
||||
set_gen_config = 1;
|
||||
switch (ioif)
|
||||
{
|
||||
/* Begin Non-multiplexed interfaces */
|
||||
case if_eth:
|
||||
/* fall through */
|
||||
case if_serial_0:
|
||||
set_gen_config = 0;
|
||||
break;
|
||||
/* End Non-multiplexed interfaces */
|
||||
case if_serial_1:
|
||||
set_gen_config_ii = 1;
|
||||
SETS(gens_ii, R_GEN_CONFIG_II, sermode1, async);
|
||||
break;
|
||||
case if_serial_2:
|
||||
SETS(gens, R_GEN_CONFIG, ser2, select);
|
||||
break;
|
||||
case if_serial_3:
|
||||
SETS(gens, R_GEN_CONFIG, ser3, select);
|
||||
set_gen_config_ii = 1;
|
||||
SETS(gens_ii, R_GEN_CONFIG_II, sermode3, async);
|
||||
break;
|
||||
case if_sync_serial_1:
|
||||
set_gen_config_ii = 1;
|
||||
SETS(gens_ii, R_GEN_CONFIG_II, sermode1, sync);
|
||||
break;
|
||||
case if_sync_serial_3:
|
||||
SETS(gens, R_GEN_CONFIG, ser3, select);
|
||||
set_gen_config_ii = 1;
|
||||
SETS(gens_ii, R_GEN_CONFIG_II, sermode3, sync);
|
||||
break;
|
||||
case if_shared_ram:
|
||||
SETS(gens, R_GEN_CONFIG, mio, select);
|
||||
break;
|
||||
case if_shared_ram_w:
|
||||
SETS(gens, R_GEN_CONFIG, mio_w, select);
|
||||
break;
|
||||
case if_par_0:
|
||||
SETS(gens, R_GEN_CONFIG, par0, select);
|
||||
break;
|
||||
case if_par_1:
|
||||
SETS(gens, R_GEN_CONFIG, par1, select);
|
||||
break;
|
||||
case if_par_w:
|
||||
SETS(gens, R_GEN_CONFIG, par0, select);
|
||||
SETS(gens, R_GEN_CONFIG, par_w, select);
|
||||
break;
|
||||
case if_scsi8_0:
|
||||
SETS(gens, R_GEN_CONFIG, scsi0, select);
|
||||
break;
|
||||
case if_scsi8_1:
|
||||
SETS(gens, R_GEN_CONFIG, scsi1, select);
|
||||
break;
|
||||
case if_scsi_w:
|
||||
SETS(gens, R_GEN_CONFIG, scsi0, select);
|
||||
SETS(gens, R_GEN_CONFIG, scsi0w, select);
|
||||
break;
|
||||
case if_ata:
|
||||
SETS(gens, R_GEN_CONFIG, ata, select);
|
||||
break;
|
||||
case if_csp:
|
||||
/* fall through */
|
||||
case if_i2c:
|
||||
set_gen_config = 0;
|
||||
break;
|
||||
case if_usb_1:
|
||||
SETS(gens, R_GEN_CONFIG, usb1, select);
|
||||
break;
|
||||
case if_usb_2:
|
||||
SETS(gens, R_GEN_CONFIG, usb2, select);
|
||||
break;
|
||||
case if_gpio_grp_a:
|
||||
/* GPIO groups are only accounted, don't do configuration changes. */
|
||||
/* fall through */
|
||||
case if_gpio_grp_b:
|
||||
/* fall through */
|
||||
case if_gpio_grp_c:
|
||||
/* fall through */
|
||||
case if_gpio_grp_d:
|
||||
/* fall through */
|
||||
case if_gpio_grp_e:
|
||||
/* fall through */
|
||||
case if_gpio_grp_f:
|
||||
set_gen_config = 0;
|
||||
break;
|
||||
default:
|
||||
panic("cris_request_io_interface: Bad interface %u submitted for %s\n",
|
||||
ioif,
|
||||
device_id);
|
||||
}
|
||||
|
||||
interfaces[ioif].used = 1;
|
||||
interfaces[ioif].owner = (char*)device_id;
|
||||
|
||||
if (set_gen_config) {
|
||||
volatile int i;
|
||||
genconfig_shadow = gens;
|
||||
*R_GEN_CONFIG = genconfig_shadow;
|
||||
/* Wait 12 cycles before doing any DMA command */
|
||||
for(i = 6; i > 0; i--)
|
||||
nop();
|
||||
}
|
||||
if (set_gen_config_ii) {
|
||||
gen_config_ii_shadow = gens_ii;
|
||||
*R_GEN_CONFIG_II = gen_config_ii_shadow;
|
||||
}
|
||||
|
||||
DBG(printk("GPIO pins: available before: g_in=0x%08x g_out=0x%08x pb=0x%02x\n",
|
||||
gpio_in_pins, gpio_out_pins, gpio_pb_pins));
|
||||
DBG(printk("grabbing pins: g_in=0x%08x g_out=0x%08x pb=0x%02x\n",
|
||||
interfaces[ioif].gpio_g_in,
|
||||
interfaces[ioif].gpio_g_out,
|
||||
interfaces[ioif].gpio_b));
|
||||
|
||||
gpio_in_pins &= ~interfaces[ioif].gpio_g_in;
|
||||
gpio_out_pins &= ~interfaces[ioif].gpio_g_out;
|
||||
gpio_pb_pins &= ~interfaces[ioif].gpio_b;
|
||||
|
||||
DBG(printk("GPIO pins: available after: g_in=0x%08x g_out=0x%08x pb=0x%02x\n",
|
||||
gpio_in_pins, gpio_out_pins, gpio_pb_pins));
|
||||
|
||||
local_irq_restore(flags);
|
||||
|
||||
notify_watchers();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void cris_free_io_interface(enum cris_io_interface ioif)
|
||||
{
|
||||
struct if_group *grp;
|
||||
unsigned char group_set;
|
||||
unsigned long flags;
|
||||
|
||||
(void)cris_io_interface_init();
|
||||
|
||||
if ((ioif >= if_max_interfaces) || (ioif < 0)) {
|
||||
printk(KERN_CRIT "cris_free_io_interface: Bad interface %u\n",
|
||||
ioif);
|
||||
return;
|
||||
}
|
||||
local_irq_save(flags);
|
||||
if (!interfaces[ioif].used) {
|
||||
printk(KERN_CRIT "cris_free_io_interface: Freeing free interface %u\n",
|
||||
ioif);
|
||||
local_irq_restore(flags);
|
||||
return;
|
||||
}
|
||||
group_set = interfaces[ioif].groups;
|
||||
while (NULL != (grp = get_group(group_set))) {
|
||||
if (grp->group == group_f) {
|
||||
switch (ioif)
|
||||
{
|
||||
case if_sync_serial_1:
|
||||
if ((grp->owner == if_sync_serial_1) &&
|
||||
interfaces[if_sync_serial_3].used) {
|
||||
grp->owner = if_sync_serial_3;
|
||||
} else
|
||||
grp->used = 0;
|
||||
break;
|
||||
case if_sync_serial_3:
|
||||
if ((grp->owner == if_sync_serial_3) &&
|
||||
interfaces[if_sync_serial_1].used) {
|
||||
grp->owner = if_sync_serial_1;
|
||||
} else
|
||||
grp->used = 0;
|
||||
break;
|
||||
case if_scsi8_0:
|
||||
if ((grp->owner == if_scsi8_0) &&
|
||||
interfaces[if_scsi8_1].used) {
|
||||
grp->owner = if_scsi8_1;
|
||||
} else
|
||||
grp->used = 0;
|
||||
break;
|
||||
case if_scsi8_1:
|
||||
if ((grp->owner == if_scsi8_1) &&
|
||||
interfaces[if_scsi8_0].used) {
|
||||
grp->owner = if_scsi8_0;
|
||||
} else
|
||||
grp->used = 0;
|
||||
break;
|
||||
default:
|
||||
grp->used = 0;
|
||||
}
|
||||
} else {
|
||||
grp->used = 0;
|
||||
}
|
||||
group_set = clear_group_from_set(group_set, grp);
|
||||
}
|
||||
interfaces[ioif].used = 0;
|
||||
interfaces[ioif].owner = NULL;
|
||||
|
||||
DBG(printk("GPIO pins: available before: g_in=0x%08x g_out=0x%08x pb=0x%02x\n",
|
||||
gpio_in_pins, gpio_out_pins, gpio_pb_pins));
|
||||
DBG(printk("freeing pins: g_in=0x%08x g_out=0x%08x pb=0x%02x\n",
|
||||
interfaces[ioif].gpio_g_in,
|
||||
interfaces[ioif].gpio_g_out,
|
||||
interfaces[ioif].gpio_b));
|
||||
|
||||
gpio_in_pins |= interfaces[ioif].gpio_g_in;
|
||||
gpio_out_pins |= interfaces[ioif].gpio_g_out;
|
||||
gpio_pb_pins |= interfaces[ioif].gpio_b;
|
||||
|
||||
DBG(printk("GPIO pins: available after: g_in=0x%08x g_out=0x%08x pb=0x%02x\n",
|
||||
gpio_in_pins, gpio_out_pins, gpio_pb_pins));
|
||||
|
||||
local_irq_restore(flags);
|
||||
|
||||
notify_watchers();
|
||||
}
|
||||
|
||||
/* Create a bitmask from bit 0 (inclusive) to bit stop_bit
|
||||
(non-inclusive). stop_bit == 0 returns 0x0 */
|
||||
static inline unsigned int create_mask(const unsigned stop_bit)
|
||||
{
|
||||
/* Avoid overflow */
|
||||
if (stop_bit >= 32) {
|
||||
return 0xffffffff;
|
||||
}
|
||||
return (1<<stop_bit)-1;
|
||||
}
|
||||
|
||||
|
||||
/* port can be 'a', 'b' or 'g' */
|
||||
int cris_io_interface_allocate_pins(const enum cris_io_interface ioif,
|
||||
const char port,
|
||||
const unsigned start_bit,
|
||||
const unsigned stop_bit)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int mask = 0;
|
||||
unsigned int tmp_mask;
|
||||
unsigned long int flags;
|
||||
enum cris_io_interface *owners;
|
||||
|
||||
(void)cris_io_interface_init();
|
||||
|
||||
DBG(printk("cris_io_interface_allocate_pins: if=%d port=%c start=%u stop=%u\n",
|
||||
ioif, port, start_bit, stop_bit));
|
||||
|
||||
if (!((start_bit <= stop_bit) &&
|
||||
((((port == 'a') || (port == 'b')) && (stop_bit < 8)) ||
|
||||
((port == 'g') && (stop_bit < 32))))) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mask = create_mask(stop_bit + 1);
|
||||
tmp_mask = create_mask(start_bit);
|
||||
mask &= ~tmp_mask;
|
||||
|
||||
DBG(printk("cris_io_interface_allocate_pins: port=%c start=%u stop=%u mask=0x%08x\n",
|
||||
port, start_bit, stop_bit, mask));
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
switch (port) {
|
||||
case 'a':
|
||||
if ((gpio_pa_pins & mask) != mask) {
|
||||
local_irq_restore(flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
owners = gpio_pa_owners;
|
||||
gpio_pa_pins &= ~mask;
|
||||
break;
|
||||
case 'b':
|
||||
if ((gpio_pb_pins & mask) != mask) {
|
||||
local_irq_restore(flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
owners = gpio_pb_owners;
|
||||
gpio_pb_pins &= ~mask;
|
||||
break;
|
||||
case 'g':
|
||||
if (((gpio_in_pins & mask) != mask) ||
|
||||
((gpio_out_pins & mask) != mask)) {
|
||||
local_irq_restore(flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
owners = gpio_pg_owners;
|
||||
gpio_in_pins &= ~mask;
|
||||
gpio_out_pins &= ~mask;
|
||||
break;
|
||||
default:
|
||||
local_irq_restore(flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = start_bit; i <= stop_bit; i++) {
|
||||
owners[i] = ioif;
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
|
||||
notify_watchers();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* port can be 'a', 'b' or 'g' */
|
||||
int cris_io_interface_free_pins(const enum cris_io_interface ioif,
|
||||
const char port,
|
||||
const unsigned start_bit,
|
||||
const unsigned stop_bit)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int mask = 0;
|
||||
unsigned int tmp_mask;
|
||||
unsigned long int flags;
|
||||
enum cris_io_interface *owners;
|
||||
|
||||
(void)cris_io_interface_init();
|
||||
|
||||
if (!((start_bit <= stop_bit) &&
|
||||
((((port == 'a') || (port == 'b')) && (stop_bit < 8)) ||
|
||||
((port == 'g') && (stop_bit < 32))))) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mask = create_mask(stop_bit + 1);
|
||||
tmp_mask = create_mask(start_bit);
|
||||
mask &= ~tmp_mask;
|
||||
|
||||
DBG(printk("cris_io_interface_free_pins: port=%c start=%u stop=%u mask=0x%08x\n",
|
||||
port, start_bit, stop_bit, mask));
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
switch (port) {
|
||||
case 'a':
|
||||
if ((~gpio_pa_pins & mask) != mask) {
|
||||
local_irq_restore(flags);
|
||||
printk(KERN_CRIT "cris_io_interface_free_pins: Freeing free pins");
|
||||
}
|
||||
owners = gpio_pa_owners;
|
||||
break;
|
||||
case 'b':
|
||||
if ((~gpio_pb_pins & mask) != mask) {
|
||||
local_irq_restore(flags);
|
||||
printk(KERN_CRIT "cris_io_interface_free_pins: Freeing free pins");
|
||||
}
|
||||
owners = gpio_pb_owners;
|
||||
break;
|
||||
case 'g':
|
||||
if (((~gpio_in_pins & mask) != mask) ||
|
||||
((~gpio_out_pins & mask) != mask)) {
|
||||
local_irq_restore(flags);
|
||||
printk(KERN_CRIT "cris_io_interface_free_pins: Freeing free pins");
|
||||
}
|
||||
owners = gpio_pg_owners;
|
||||
break;
|
||||
default:
|
||||
owners = NULL; /* Cannot happen. Shut up, gcc! */
|
||||
}
|
||||
|
||||
for (i = start_bit; i <= stop_bit; i++) {
|
||||
if (owners[i] != ioif) {
|
||||
printk(KERN_CRIT "cris_io_interface_free_pins: Freeing unowned pins");
|
||||
}
|
||||
}
|
||||
|
||||
/* All was ok, change data. */
|
||||
switch (port) {
|
||||
case 'a':
|
||||
gpio_pa_pins |= mask;
|
||||
break;
|
||||
case 'b':
|
||||
gpio_pb_pins |= mask;
|
||||
break;
|
||||
case 'g':
|
||||
gpio_in_pins |= mask;
|
||||
gpio_out_pins |= mask;
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = start_bit; i <= stop_bit; i++) {
|
||||
owners[i] = if_unclaimed;
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
notify_watchers();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int cris_io_interface_register_watcher(void (*notify)(const unsigned int gpio_in_available,
|
||||
const unsigned int gpio_out_available,
|
||||
const unsigned char pa_available,
|
||||
const unsigned char pb_available))
|
||||
{
|
||||
struct watcher *w;
|
||||
|
||||
(void)cris_io_interface_init();
|
||||
|
||||
if (NULL == notify) {
|
||||
return -EINVAL;
|
||||
}
|
||||
w = kmalloc(sizeof(*w), GFP_KERNEL);
|
||||
if (!w) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
w->notify = notify;
|
||||
w->next = watchers;
|
||||
watchers = w;
|
||||
|
||||
w->notify((const unsigned int)gpio_in_pins,
|
||||
(const unsigned int)gpio_out_pins,
|
||||
(const unsigned char)gpio_pa_pins,
|
||||
(const unsigned char)gpio_pb_pins);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cris_io_interface_delete_watcher(void (*notify)(const unsigned int gpio_in_available,
|
||||
const unsigned int gpio_out_available,
|
||||
const unsigned char pa_available,
|
||||
const unsigned char pb_available))
|
||||
{
|
||||
struct watcher *w = watchers, *prev = NULL;
|
||||
|
||||
(void)cris_io_interface_init();
|
||||
|
||||
while ((NULL != w) && (w->notify != notify)){
|
||||
prev = w;
|
||||
w = w->next;
|
||||
}
|
||||
if (NULL != w) {
|
||||
if (NULL != prev) {
|
||||
prev->next = w->next;
|
||||
} else {
|
||||
watchers = w->next;
|
||||
}
|
||||
kfree(w);
|
||||
return;
|
||||
}
|
||||
printk(KERN_WARNING "cris_io_interface_delete_watcher: Deleting unknown watcher 0x%p\n", notify);
|
||||
}
|
||||
|
||||
|
||||
static int cris_io_interface_init(void)
|
||||
{
|
||||
static int first = 1;
|
||||
int i;
|
||||
|
||||
if (!first) {
|
||||
return 0;
|
||||
}
|
||||
first = 0;
|
||||
|
||||
for (i = 0; i<8; i++) {
|
||||
gpio_pa_owners[i] = if_unclaimed;
|
||||
gpio_pb_owners[i] = if_unclaimed;
|
||||
gpio_pg_owners[i] = if_unclaimed;
|
||||
}
|
||||
for (; i<32; i++) {
|
||||
gpio_pg_owners[i] = if_unclaimed;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
module_init(cris_io_interface_init);
|
||||
|
||||
|
||||
EXPORT_SYMBOL(cris_request_io_interface);
|
||||
EXPORT_SYMBOL(cris_free_io_interface);
|
||||
EXPORT_SYMBOL(cris_io_interface_allocate_pins);
|
||||
EXPORT_SYMBOL(cris_io_interface_free_pins);
|
||||
EXPORT_SYMBOL(cris_io_interface_register_watcher);
|
||||
EXPORT_SYMBOL(cris_io_interface_delete_watcher);
|
|
@ -1,4 +1,4 @@
|
|||
/* $Id: irq.c,v 1.2 2004/06/09 05:30:27 starvik Exp $
|
||||
/* $Id: irq.c,v 1.4 2005/01/04 12:22:28 starvik Exp $
|
||||
*
|
||||
* linux/arch/cris/kernel/irq.c
|
||||
*
|
||||
|
@ -12,11 +12,13 @@
|
|||
*/
|
||||
|
||||
#include <asm/irq.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/config.h>
|
||||
|
||||
irqvectptr irq_shortcuts[NR_IRQS]; /* vector of shortcut jumps after the irq prologue */
|
||||
#define mask_irq(irq_nr) (*R_VECT_MASK_CLR = 1 << (irq_nr));
|
||||
#define unmask_irq(irq_nr) (*R_VECT_MASK_SET = 1 << (irq_nr));
|
||||
|
||||
/* don't use set_int_vector, it bypasses the linux interrupt handlers. it is
|
||||
* global just so that the kernel gdb can use it.
|
||||
|
@ -102,41 +104,52 @@ static void (*interrupt[NR_IRQS])(void) = {
|
|||
IRQ31_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,
|
||||
NULL, NULL, NULL, NULL, NULL,
|
||||
bad_IRQ31_interrupt
|
||||
static void enable_crisv10_irq(unsigned int irq);
|
||||
|
||||
static unsigned int startup_crisv10_irq(unsigned int irq)
|
||||
{
|
||||
enable_crisv10_irq(irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define shutdown_crisv10_irq disable_crisv10_irq
|
||||
|
||||
static void enable_crisv10_irq(unsigned int irq)
|
||||
{
|
||||
unmask_irq(irq);
|
||||
}
|
||||
|
||||
static void disable_crisv10_irq(unsigned int irq)
|
||||
{
|
||||
mask_irq(irq);
|
||||
}
|
||||
|
||||
static void ack_crisv10_irq(unsigned int irq)
|
||||
{
|
||||
}
|
||||
|
||||
static void end_crisv10_irq(unsigned int irq)
|
||||
{
|
||||
}
|
||||
|
||||
static struct hw_interrupt_type crisv10_irq_type = {
|
||||
.typename = "CRISv10",
|
||||
.startup = startup_crisv10_irq,
|
||||
.shutdown = shutdown_crisv10_irq,
|
||||
.enable = enable_crisv10_irq,
|
||||
.disable = disable_crisv10_irq,
|
||||
.ack = ack_crisv10_irq,
|
||||
.end = end_crisv10_irq,
|
||||
.set_affinity = NULL
|
||||
};
|
||||
|
||||
void arch_setup_irq(int irq)
|
||||
{
|
||||
set_int_vector(irq, interrupt[irq]);
|
||||
}
|
||||
|
||||
void arch_free_irq(int irq)
|
||||
{
|
||||
set_int_vector(irq, bad_interrupt[irq]);
|
||||
}
|
||||
|
||||
void weird_irq(void);
|
||||
void system_call(void); /* from entry.S */
|
||||
void do_sigtrap(void); /* from entry.S */
|
||||
void gdb_handle_breakpoint(void); /* from entry.S */
|
||||
|
||||
/* 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.
|
||||
setting the irq vector table.
|
||||
*/
|
||||
|
||||
void __init
|
||||
|
@ -154,14 +167,15 @@ init_IRQ(void)
|
|||
|
||||
*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;
|
||||
|
||||
/* Initialize IRQ handler descriptiors. */
|
||||
for(i = 2; i < NR_IRQS; i++) {
|
||||
irq_desc[i].handler = &crisv10_irq_type;
|
||||
set_int_vector(i, interrupt[i]);
|
||||
}
|
||||
|
||||
/* the entries in the break vector contain actual code to be
|
||||
executed by the associated break handler, rather than just a jump
|
||||
address. therefore we need to setup a default breakpoint handler
|
||||
|
@ -170,10 +184,6 @@ init_IRQ(void)
|
|||
for (i = 0; i < 16; i++)
|
||||
set_break_vector(i, do_sigtrap);
|
||||
|
||||
/* set all etrax irq's to the bad handlers */
|
||||
for (i = 2; i < NR_IRQS; i++)
|
||||
set_int_vector(i, bad_interrupt[i]);
|
||||
|
||||
/* except IRQ 15 which is the multiple-IRQ handler on Etrax100 */
|
||||
|
||||
set_int_vector(15, multiple_interrupt);
|
||||
|
|
|
@ -18,6 +18,10 @@
|
|||
*! Jul 21 1999 Bjorn Wesen eLinux port
|
||||
*!
|
||||
*! $Log: kgdb.c,v $
|
||||
*! Revision 1.6 2005/01/14 10:12:17 starvik
|
||||
*! KGDB on separate port.
|
||||
*! Console fixes from 2.4.
|
||||
*!
|
||||
*! Revision 1.5 2004/10/07 13:59:08 starvik
|
||||
*! Corrected call to set_int_vector
|
||||
*!
|
||||
|
@ -71,7 +75,7 @@
|
|||
*!
|
||||
*!---------------------------------------------------------------------------
|
||||
*!
|
||||
*! $Id: kgdb.c,v 1.5 2004/10/07 13:59:08 starvik Exp $
|
||||
*! $Id: kgdb.c,v 1.6 2005/01/14 10:12:17 starvik Exp $
|
||||
*!
|
||||
*! (C) Copyright 1999, Axis Communications AB, LUND, SWEDEN
|
||||
*!
|
||||
|
@ -225,6 +229,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/linkage.h>
|
||||
#include <linux/reboot.h>
|
||||
|
||||
#include <asm/setup.h>
|
||||
#include <asm/ptrace.h>
|
||||
|
@ -1344,12 +1349,11 @@ handle_exception (int sigval)
|
|||
}
|
||||
}
|
||||
|
||||
/* The jump is to the address 0x00000002. Performs a complete re-start
|
||||
from scratch. */
|
||||
/* Performs a complete re-start from scratch. */
|
||||
static void
|
||||
kill_restart ()
|
||||
{
|
||||
__asm__ volatile ("jump 2");
|
||||
machine_restart("");
|
||||
}
|
||||
|
||||
/********************************** Breakpoint *******************************/
|
||||
|
@ -1506,6 +1510,11 @@ asm ("
|
|||
bne goback
|
||||
nop
|
||||
|
||||
move.d [reg+0x5E], $r10 ; Get DCCR
|
||||
btstq 8, $r10 ; Test the U-flag.
|
||||
bmi goback
|
||||
nop
|
||||
|
||||
;;
|
||||
;; Handle the communication
|
||||
;;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $Id: process.c,v 1.9 2004/10/19 13:07:37 starvik Exp $
|
||||
/* $Id: process.c,v 1.12 2004/12/27 11:18:32 starvik Exp $
|
||||
*
|
||||
* linux/arch/cris/kernel/process.c
|
||||
*
|
||||
|
@ -101,6 +101,7 @@ int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
|
|||
regs.r11 = (unsigned long)fn;
|
||||
regs.r12 = (unsigned long)arg;
|
||||
regs.irp = (unsigned long)kernel_thread_helper;
|
||||
regs.dccr = 1 << I_DCCR_BITNR;
|
||||
|
||||
/* Ok, create the new process.. */
|
||||
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/ptrace.h>
|
||||
#include <linux/user.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/security.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/page.h>
|
||||
|
@ -86,9 +87,13 @@ sys_ptrace(long request, long pid, long addr, long data)
|
|||
ret = -EPERM;
|
||||
|
||||
if (request == PTRACE_TRACEME) {
|
||||
/* are we already being traced? */
|
||||
if (current->ptrace & PT_PTRACED)
|
||||
goto out;
|
||||
|
||||
ret = security_ptrace(current->parent, current);
|
||||
if (ret)
|
||||
goto out;
|
||||
/* set the ptrace bit in the process flags. */
|
||||
current->ptrace |= PT_PTRACED;
|
||||
ret = 0;
|
||||
goto out;
|
||||
|
@ -207,7 +212,7 @@ sys_ptrace(long request, long pid, long addr, long data)
|
|||
case PTRACE_KILL:
|
||||
ret = 0;
|
||||
|
||||
if (child->state == TASK_ZOMBIE)
|
||||
if (child->exit_state == EXIT_ZOMBIE)
|
||||
break;
|
||||
|
||||
child->exit_code = SIGKILL;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $Id: shadows.c,v 1.1 2001/12/17 13:59:27 bjornw Exp $
|
||||
/* $Id: shadows.c,v 1.2 2004/12/13 12:21:51 starvik Exp $
|
||||
*
|
||||
* Various shadow registers. Defines for these are in include/asm-etrax100/io.h
|
||||
*/
|
||||
|
@ -6,6 +6,7 @@
|
|||
/* Shadows for internal Etrax-registers */
|
||||
|
||||
unsigned long genconfig_shadow;
|
||||
unsigned long gen_config_ii_shadow;
|
||||
unsigned long port_g_data_shadow;
|
||||
unsigned char port_pa_dir_shadow;
|
||||
unsigned char port_pa_data_shadow;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $Id: traps.c,v 1.2 2003/07/04 08:27:41 starvik Exp $
|
||||
/* $Id: traps.c,v 1.4 2005/04/24 18:47:55 starvik Exp $
|
||||
*
|
||||
* linux/arch/cris/arch-v10/traps.c
|
||||
*
|
||||
|
@ -16,6 +16,8 @@
|
|||
#include <asm/uaccess.h>
|
||||
#include <asm/arch/sv_addr_ag.h>
|
||||
|
||||
extern int raw_printk(const char *fmt, ...);
|
||||
|
||||
void
|
||||
show_registers(struct pt_regs * regs)
|
||||
{
|
||||
|
@ -26,18 +28,18 @@ show_registers(struct pt_regs * regs)
|
|||
register. */
|
||||
unsigned long usp = rdusp();
|
||||
|
||||
printk("IRP: %08lx SRP: %08lx DCCR: %08lx USP: %08lx MOF: %08lx\n",
|
||||
raw_printk("IRP: %08lx SRP: %08lx DCCR: %08lx USP: %08lx MOF: %08lx\n",
|
||||
regs->irp, regs->srp, regs->dccr, usp, regs->mof );
|
||||
printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n",
|
||||
raw_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",
|
||||
raw_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",
|
||||
raw_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("R_MMU_CAUSE: %08lx\n", (unsigned long)*R_MMU_CAUSE);
|
||||
printk("Process %s (pid: %d, stackpage=%08lx)\n",
|
||||
raw_printk("r12: %08lx r13: %08lx oR10: %08lx sp: %08lx\n",
|
||||
regs->r12, regs->r13, regs->orig_r10, regs);
|
||||
raw_printk("R_MMU_CAUSE: %08lx\n", (unsigned long)*R_MMU_CAUSE);
|
||||
raw_printk("Process %s (pid: %d, stackpage=%08lx)\n",
|
||||
current->comm, current->pid, (unsigned long)current);
|
||||
|
||||
/*
|
||||
|
@ -53,7 +55,7 @@ show_registers(struct pt_regs * regs)
|
|||
if (usp != 0)
|
||||
show_stack (NULL, NULL);
|
||||
|
||||
printk("\nCode: ");
|
||||
raw_printk("\nCode: ");
|
||||
if(regs->irp < PAGE_OFFSET)
|
||||
goto bad;
|
||||
|
||||
|
@ -70,16 +72,16 @@ show_registers(struct pt_regs * regs)
|
|||
unsigned char c;
|
||||
if(__get_user(c, &((unsigned char*)regs->irp)[i])) {
|
||||
bad:
|
||||
printk(" Bad IP value.");
|
||||
raw_printk(" Bad IP value.");
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == 0)
|
||||
printk("(%02x) ", c);
|
||||
raw_printk("(%02x) ", c);
|
||||
else
|
||||
printk("%02x ", c);
|
||||
raw_printk("%02x ", c);
|
||||
}
|
||||
printk("\n");
|
||||
raw_printk("\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,7 +123,7 @@ die_if_kernel(const char * str, struct pt_regs * regs, long err)
|
|||
stop_watchdog();
|
||||
#endif
|
||||
|
||||
printk("%s: %04lx\n", str, err & 0xffff);
|
||||
raw_printk("%s: %04lx\n", str, err & 0xffff);
|
||||
|
||||
show_registers(regs);
|
||||
|
||||
|
@ -130,3 +132,8 @@ die_if_kernel(const char * str, struct pt_regs * regs, long err)
|
|||
#endif
|
||||
do_exit(SIGSEGV);
|
||||
}
|
||||
|
||||
void arch_enable_nmi(void)
|
||||
{
|
||||
asm volatile("setf m");
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <asm/uaccess.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/arch/svinto.h>
|
||||
#include <asm/mmu_context.h>
|
||||
|
||||
/* debug of low-level TLB reload */
|
||||
#undef DEBUG
|
||||
|
@ -24,8 +25,6 @@
|
|||
#define D(x)
|
||||
#endif
|
||||
|
||||
extern volatile pgd_t *current_pgd;
|
||||
|
||||
extern const struct exception_table_entry
|
||||
*search_exception_tables(unsigned long addr);
|
||||
|
||||
|
@ -46,7 +45,7 @@ handle_mmu_bus_fault(struct pt_regs *regs)
|
|||
int page_id;
|
||||
int acc, inv;
|
||||
#endif
|
||||
pgd_t* pgd = (pgd_t*)current_pgd;
|
||||
pgd_t* pgd = (pgd_t*)per_cpu(current_pgd, smp_processor_id());
|
||||
pmd_t *pmd;
|
||||
pte_t pte;
|
||||
int miss, we, writeac;
|
||||
|
@ -94,24 +93,3 @@ handle_mmu_bus_fault(struct pt_regs *regs)
|
|||
*R_TLB_LO = pte_val(pte);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
/* Called from arch/cris/mm/fault.c to find fixup code. */
|
||||
int
|
||||
find_fixup_code(struct pt_regs *regs)
|
||||
{
|
||||
const struct exception_table_entry *fixup;
|
||||
|
||||
if ((fixup = search_exception_tables(regs->irp)) != 0) {
|
||||
/* Adjust the instruction pointer in the stackframe. */
|
||||
regs->irp = fixup->fixup;
|
||||
|
||||
/*
|
||||
* Don't return by restoring the CPU state, so switch
|
||||
* frame-type.
|
||||
*/
|
||||
regs->frametype = CRIS_FRAME_NORMAL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ paging_init(void)
|
|||
* switch_mm)
|
||||
*/
|
||||
|
||||
current_pgd = init_mm.pgd;
|
||||
per_cpu(current_pgd, smp_processor_id()) = init_mm.pgd;
|
||||
|
||||
/* initialise the TLB (tlb.c) */
|
||||
|
||||
|
|
|
@ -139,53 +139,6 @@ flush_tlb_page(struct vm_area_struct *vma,
|
|||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
/* invalidate a page range */
|
||||
|
||||
void
|
||||
flush_tlb_range(struct vm_area_struct *vma,
|
||||
unsigned long start,
|
||||
unsigned long end)
|
||||
{
|
||||
struct mm_struct *mm = vma->vm_mm;
|
||||
int page_id = mm->context.page_id;
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
local_save_flags(flags);
|
||||
local_irq_disable();
|
||||
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 ) );
|
||||
}
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
/* dump the entire TLB for debug purposes */
|
||||
|
||||
#if 0
|
||||
|
@ -237,7 +190,7 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next,
|
|||
* the pgd.
|
||||
*/
|
||||
|
||||
current_pgd = next->pgd;
|
||||
per_cpu(current_pgd, smp_processor_id()) = next->pgd;
|
||||
|
||||
/* switch context in the MMU */
|
||||
|
||||
|
|
|
@ -0,0 +1,296 @@
|
|||
config ETRAX_DRAM_VIRTUAL_BASE
|
||||
hex
|
||||
depends on ETRAX_ARCH_V32
|
||||
default "c0000000"
|
||||
|
||||
config ETRAX_LED1G
|
||||
string "First green LED bit"
|
||||
depends on ETRAX_ARCH_V32
|
||||
default "PA3"
|
||||
help
|
||||
Bit to use for the first green LED (network LED).
|
||||
Most Axis products use bit A3 here.
|
||||
|
||||
config ETRAX_LED1R
|
||||
string "First red LED bit"
|
||||
depends on ETRAX_ARCH_V32
|
||||
default "PA4"
|
||||
help
|
||||
Bit to use for the first red LED (network LED).
|
||||
Most Axis products use bit A4 here.
|
||||
|
||||
config ETRAX_LED2G
|
||||
string "Second green LED bit"
|
||||
depends on ETRAX_ARCH_V32
|
||||
default "PA5"
|
||||
help
|
||||
Bit to use for the first green LED (status LED).
|
||||
Most Axis products use bit A5 here.
|
||||
|
||||
config ETRAX_LED2R
|
||||
string "Second red LED bit"
|
||||
depends on ETRAX_ARCH_V32
|
||||
default "PA6"
|
||||
help
|
||||
Bit to use for the first red LED (network LED).
|
||||
Most Axis products use bit A6 here.
|
||||
|
||||
config ETRAX_LED3G
|
||||
string "Third green LED bit"
|
||||
depends on ETRAX_ARCH_V32
|
||||
default "PA7"
|
||||
help
|
||||
Bit to use for the first green LED (drive/power LED).
|
||||
Most Axis products use bit A7 here.
|
||||
|
||||
config ETRAX_LED3R
|
||||
string "Third red LED bit"
|
||||
depends on ETRAX_ARCH_V32
|
||||
default "PA7"
|
||||
help
|
||||
Bit to use for the first red LED (drive/power LED).
|
||||
Most Axis products use bit A7 here.
|
||||
|
||||
choice
|
||||
prompt "Product debug-port"
|
||||
depends on ETRAX_ARCH_V32
|
||||
default ETRAX_DEBUG_PORT0
|
||||
|
||||
config ETRAX_DEBUG_PORT0
|
||||
bool "Serial-0"
|
||||
help
|
||||
Choose a serial port for the ETRAX debug console. Default to
|
||||
port 0.
|
||||
|
||||
config ETRAX_DEBUG_PORT1
|
||||
bool "Serial-1"
|
||||
help
|
||||
Use serial port 1 for the console.
|
||||
|
||||
config ETRAX_DEBUG_PORT2
|
||||
bool "Serial-2"
|
||||
help
|
||||
Use serial port 2 for the console.
|
||||
|
||||
config ETRAX_DEBUG_PORT3
|
||||
bool "Serial-3"
|
||||
help
|
||||
Use serial port 3 for the console.
|
||||
|
||||
config ETRAX_DEBUG_PORT_NULL
|
||||
bool "disabled"
|
||||
help
|
||||
Disable serial-port debugging.
|
||||
|
||||
endchoice
|
||||
|
||||
choice
|
||||
prompt "Kernel GDB port"
|
||||
depends on ETRAX_KGDB
|
||||
default ETRAX_KGDB_PORT0
|
||||
help
|
||||
Choose a serial port for kernel debugging. NOTE: This port should
|
||||
not be enabled under Drivers for built-in interfaces (as it has its
|
||||
own initialization code) and should not be the same as the debug port.
|
||||
|
||||
config ETRAX_KGDB_PORT0
|
||||
bool "Serial-0"
|
||||
help
|
||||
Use serial port 0 for kernel debugging.
|
||||
|
||||
config ETRAX_KGDB_PORT1
|
||||
bool "Serial-1"
|
||||
help
|
||||
Use serial port 1 for kernel debugging.
|
||||
|
||||
config ETRAX_KGDB_PORT2
|
||||
bool "Serial-2"
|
||||
help
|
||||
Use serial port 2 for kernel debugging.
|
||||
|
||||
config ETRAX_KGDB_PORT3
|
||||
bool "Serial-3"
|
||||
help
|
||||
Use serial port 3 for kernel debugging.
|
||||
|
||||
endchoice
|
||||
|
||||
config ETRAX_MEM_GRP1_CONFIG
|
||||
hex "MEM_GRP1_CONFIG"
|
||||
depends on ETRAX_ARCH_V32
|
||||
default "4044a"
|
||||
help
|
||||
Waitstates for flash. The default value is suitable for the
|
||||
standard flashes used in axis products (120 ns).
|
||||
|
||||
config ETRAX_MEM_GRP2_CONFIG
|
||||
hex "MEM_GRP2_CONFIG"
|
||||
depends on ETRAX_ARCH_V32
|
||||
default "0"
|
||||
help
|
||||
Waitstates for SRAM. 0 is a good choice for most Axis products.
|
||||
|
||||
config ETRAX_MEM_GRP3_CONFIG
|
||||
hex "MEM_GRP3_CONFIG"
|
||||
depends on ETRAX_ARCH_V32
|
||||
default "0"
|
||||
help
|
||||
Waitstates for CSP0-3. 0 is a good choice for most Axis products.
|
||||
It may need to be changed if external devices such as extra
|
||||
register-mapped LEDs are used.
|
||||
|
||||
config ETRAX_MEM_GRP4_CONFIG
|
||||
hex "MEM_GRP4_CONFIG"
|
||||
depends on ETRAX_ARCH_V32
|
||||
default "0"
|
||||
help
|
||||
Waitstates for CSP4-6. 0 is a good choice for most Axis products.
|
||||
|
||||
config ETRAX_SDRAM_GRP0_CONFIG
|
||||
hex "SDRAM_GRP0_CONFIG"
|
||||
depends on ETRAX_ARCH_V32
|
||||
default "336"
|
||||
help
|
||||
SDRAM configuration for group 0. The value depends on the
|
||||
hardware configuration. The default value is suitable
|
||||
for 32 MB organized as two 16 bits chips (e.g. Axis
|
||||
part number 18550) connected as one 32 bit device (i.e. in
|
||||
the same group).
|
||||
|
||||
config ETRAX_SDRAM_GRP1_CONFIG
|
||||
hex "SDRAM_GRP1_CONFIG"
|
||||
depends on ETRAX_ARCH_V32
|
||||
default "0"
|
||||
help
|
||||
SDRAM configuration for group 1. The defult value is 0
|
||||
because group 1 is not used in the default configuration,
|
||||
described in the help for SDRAM_GRP0_CONFIG.
|
||||
|
||||
config ETRAX_SDRAM_TIMING
|
||||
hex "SDRAM_TIMING"
|
||||
depends on ETRAX_ARCH_V32
|
||||
default "104a"
|
||||
help
|
||||
SDRAM timing parameters. The default value is ok for
|
||||
most hardwares but large SDRAMs may require a faster
|
||||
refresh (a.k.a 8K refresh). The default value implies
|
||||
100MHz clock and SDR mode.
|
||||
|
||||
config ETRAX_SDRAM_COMMAND
|
||||
hex "SDRAM_COMMAND"
|
||||
depends on ETRAX_ARCH_V32
|
||||
default "0"
|
||||
help
|
||||
SDRAM command. Should be 0 unless you really know what
|
||||
you are doing (may be != 0 for unusual address line
|
||||
mappings such as in a MCM)..
|
||||
|
||||
config ETRAX_DEF_GIO_PA_OE
|
||||
hex "GIO_PA_OE"
|
||||
depends on ETRAX_ARCH_V32
|
||||
default "1c"
|
||||
help
|
||||
Configures the direction of general port A bits. 1 is out, 0 is in.
|
||||
This is often totally different depending on the product used.
|
||||
There are some guidelines though - if you know that only LED's are
|
||||
connected to port PA, then they are usually connected to bits 2-4
|
||||
and you can therefore use 1c. On other boards which don't have the
|
||||
LED's at the general ports, these bits are used for all kinds of
|
||||
stuff. If you don't know what to use, it is always safe to put all
|
||||
as inputs, although floating inputs isn't good.
|
||||
|
||||
config ETRAX_DEF_GIO_PA_OUT
|
||||
hex "GIO_PA_OUT"
|
||||
depends on ETRAX_ARCH_V32
|
||||
default "00"
|
||||
help
|
||||
Configures the initial data for the general port A bits. Most
|
||||
products should use 00 here.
|
||||
|
||||
config ETRAX_DEF_GIO_PB_OE
|
||||
hex "GIO_PB_OE"
|
||||
depends on ETRAX_ARCH_V32
|
||||
default "00000"
|
||||
help
|
||||
Configures the direction of general port B bits. 1 is out, 0 is in.
|
||||
This is often totally different depending on the product used.
|
||||
There are some guidelines though - if you know that only LED's are
|
||||
connected to port PA, then they are usually connected to bits 2-4
|
||||
and you can therefore use 1c. On other boards which don't have the
|
||||
LED's at the general ports, these bits are used for all kinds of
|
||||
stuff. If you don't know what to use, it is always safe to put all
|
||||
as inputs, although floating inputs isn't good.
|
||||
|
||||
config ETRAX_DEF_GIO_PB_OUT
|
||||
hex "GIO_PB_OUT"
|
||||
depends on ETRAX_ARCH_V32
|
||||
default "00000"
|
||||
help
|
||||
Configures the initial data for the general port B bits. Most
|
||||
products should use 00000 here.
|
||||
|
||||
config ETRAX_DEF_GIO_PC_OE
|
||||
hex "GIO_PC_OE"
|
||||
depends on ETRAX_ARCH_V32
|
||||
default "00000"
|
||||
help
|
||||
Configures the direction of general port C bits. 1 is out, 0 is in.
|
||||
This is often totally different depending on the product used.
|
||||
There are some guidelines though - if you know that only LED's are
|
||||
connected to port PA, then they are usually connected to bits 2-4
|
||||
and you can therefore use 1c. On other boards which don't have the
|
||||
LED's at the general ports, these bits are used for all kinds of
|
||||
stuff. If you don't know what to use, it is always safe to put all
|
||||
as inputs, although floating inputs isn't good.
|
||||
|
||||
config ETRAX_DEF_GIO_PC_OUT
|
||||
hex "GIO_PC_OUT"
|
||||
depends on ETRAX_ARCH_V32
|
||||
default "00000"
|
||||
help
|
||||
Configures the initial data for the general port C bits. Most
|
||||
products should use 00000 here.
|
||||
|
||||
config ETRAX_DEF_GIO_PD_OE
|
||||
hex "GIO_PD_OE"
|
||||
depends on ETRAX_ARCH_V32
|
||||
default "00000"
|
||||
help
|
||||
Configures the direction of general port D bits. 1 is out, 0 is in.
|
||||
This is often totally different depending on the product used.
|
||||
There are some guidelines though - if you know that only LED's are
|
||||
connected to port PA, then they are usually connected to bits 2-4
|
||||
and you can therefore use 1c. On other boards which don't have the
|
||||
LED's at the general ports, these bits are used for all kinds of
|
||||
stuff. If you don't know what to use, it is always safe to put all
|
||||
as inputs, although floating inputs isn't good.
|
||||
|
||||
config ETRAX_DEF_GIO_PD_OUT
|
||||
hex "GIO_PD_OUT"
|
||||
depends on ETRAX_ARCH_V32
|
||||
default "00000"
|
||||
help
|
||||
Configures the initial data for the general port D bits. Most
|
||||
products should use 00000 here.
|
||||
|
||||
config ETRAX_DEF_GIO_PE_OE
|
||||
hex "GIO_PE_OE"
|
||||
depends on ETRAX_ARCH_V32
|
||||
default "00000"
|
||||
help
|
||||
Configures the direction of general port E bits. 1 is out, 0 is in.
|
||||
This is often totally different depending on the product used.
|
||||
There are some guidelines though - if you know that only LED's are
|
||||
connected to port PA, then they are usually connected to bits 2-4
|
||||
and you can therefore use 1c. On other boards which don't have the
|
||||
LED's at the general ports, these bits are used for all kinds of
|
||||
stuff. If you don't know what to use, it is always safe to put all
|
||||
as inputs, although floating inputs isn't good.
|
||||
|
||||
config ETRAX_DEF_GIO_PE_OUT
|
||||
hex "GIO_PE_OUT"
|
||||
depends on ETRAX_ARCH_V32
|
||||
default "00000"
|
||||
help
|
||||
Configures the initial data for the general port E bits. Most
|
||||
products should use 00000 here.
|
|
@ -0,0 +1,14 @@
|
|||
#
|
||||
# arch/cris/arch-v32/boot/Makefile
|
||||
#
|
||||
target = $(target_boot_dir)
|
||||
src = $(src_boot_dir)
|
||||
|
||||
zImage: compressed/vmlinuz
|
||||
|
||||
compressed/vmlinuz: $(objtree)/vmlinux
|
||||
@$(MAKE) -f $(src)/compressed/Makefile $(objtree)/vmlinuz
|
||||
|
||||
clean:
|
||||
rm -f zImage tools/build compressed/vmlinux.out
|
||||
@$(MAKE) -f $(src)/compressed/Makefile clean
|
|
@ -0,0 +1,41 @@
|
|||
#
|
||||
# lx25/arch/cris/arch-v32/boot/compressed/Makefile
|
||||
#
|
||||
# create a compressed vmlinux image from the original vmlinux files and romfs
|
||||
#
|
||||
|
||||
target = $(target_compressed_dir)
|
||||
src = $(src_compressed_dir)
|
||||
|
||||
CC = gcc-cris -mlinux -march=v32 -I $(TOPDIR)/include
|
||||
CFLAGS = -O2
|
||||
LD = gcc-cris -mlinux -march=v32 -nostdlib
|
||||
OBJCOPY = objcopy-cris
|
||||
OBJCOPYFLAGS = -O binary --remove-section=.bss
|
||||
OBJECTS = $(target)/head.o $(target)/misc.o
|
||||
|
||||
# files to compress
|
||||
SYSTEM = $(objtree)/vmlinux.bin
|
||||
|
||||
all: vmlinuz
|
||||
|
||||
$(target)/decompress.bin: $(OBJECTS)
|
||||
$(LD) -T $(src)/decompress.ld -o $(target)/decompress.o $(OBJECTS)
|
||||
$(OBJCOPY) $(OBJCOPYFLAGS) $(target)/decompress.o $(target)/decompress.bin
|
||||
|
||||
$(objtree)/vmlinuz: $(target) piggy.img $(target)/decompress.bin
|
||||
cat $(target)/decompress.bin piggy.img > $(objtree)/vmlinuz
|
||||
rm -f piggy.img
|
||||
cp $(objtree)/vmlinuz $(src)
|
||||
|
||||
$(target)/head.o: $(src)/head.S
|
||||
$(CC) -D__ASSEMBLY__ -c $< -o $@
|
||||
|
||||
# gzip the kernel image
|
||||
|
||||
piggy.img: $(SYSTEM)
|
||||
cat $(SYSTEM) | gzip -f -9 > piggy.img
|
||||
|
||||
clean:
|
||||
rm -f piggy.img $(objtree)/vmlinuz vmlinuz.o decompress.o decompress.bin $(OBJECTS)
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
Creation of the self-extracting compressed kernel image (vmlinuz)
|
||||
-----------------------------------------------------------------
|
||||
$Id: README,v 1.1 2003/08/21 09:37:03 johana 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
|
|
@ -0,0 +1,30 @@
|
|||
/*#OUTPUT_FORMAT(elf32-us-cris) */
|
||||
OUTPUT_ARCH (crisv32)
|
||||
|
||||
MEMORY
|
||||
{
|
||||
dram : ORIGIN = 0x40700000,
|
||||
LENGTH = 0x00100000
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.text :
|
||||
{
|
||||
_stext = . ;
|
||||
*(.text)
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
_etext = . ;
|
||||
} > dram
|
||||
.data :
|
||||
{
|
||||
*(.data)
|
||||
_edata = . ;
|
||||
} > dram
|
||||
.bss :
|
||||
{
|
||||
*(.bss)
|
||||
_end = ALIGN( 0x10 ) ;
|
||||
} > dram
|
||||
}
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* Code that sets up the DRAM registers, calls the
|
||||
* decompressor to unpack the piggybacked kernel, and jumps.
|
||||
*
|
||||
* Copyright (C) 1999 - 2003, Axis Communications AB
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#define ASSEMBLER_MACROS_ONLY
|
||||
#include <asm/arch/hwregs/asm/reg_map_asm.h>
|
||||
#include <asm/arch/hwregs/asm/gio_defs_asm.h>
|
||||
#include <asm/arch/hwregs/asm/config_defs_asm.h>
|
||||
|
||||
#define RAM_INIT_MAGIC 0x56902387
|
||||
#define COMMAND_LINE_MAGIC 0x87109563
|
||||
|
||||
;; Exported symbols
|
||||
|
||||
.globl input_data
|
||||
|
||||
.text
|
||||
start:
|
||||
di
|
||||
|
||||
;; Start clocks for used blocks.
|
||||
move.d REG_ADDR(config, regi_config, rw_clk_ctrl), $r1
|
||||
move.d [$r1], $r0
|
||||
or.d REG_STATE(config, rw_clk_ctrl, cpu, yes) | \
|
||||
REG_STATE(config, rw_clk_ctrl, bif, yes) | \
|
||||
REG_STATE(config, rw_clk_ctrl, fix_io, yes), $r0
|
||||
move.d $r0, [$r1]
|
||||
|
||||
;; If booting from NAND flash we first have to copy some
|
||||
;; data from NAND flash to internal RAM to get the code
|
||||
;; that initializes the SDRAM. Lets copy 20 KB. This
|
||||
;; code executes at 0x38010000 if booting from NAND and
|
||||
;; we are guaranted that at least 0x200 bytes are good so
|
||||
;; lets start from there. The first 8192 bytes in the nand
|
||||
;; flash is spliced with zeroes and is thus 16384 bytes.
|
||||
move.d 0x38010200, $r10
|
||||
move.d 0x14200, $r11 ; Start offset in NAND flash 0x10200 + 16384
|
||||
move.d 0x5000, $r12 ; Length of copy
|
||||
|
||||
;; Before this code the tools add a partitiontable so the PC
|
||||
;; has an offset from the linked address.
|
||||
offset1:
|
||||
lapcq ., $r13 ; get PC
|
||||
add.d first_copy_complete-offset1, $r13
|
||||
|
||||
#include "../../lib/nand_init.S"
|
||||
|
||||
first_copy_complete:
|
||||
;; Initialze the DRAM registers.
|
||||
cmp.d RAM_INIT_MAGIC, $r8 ; Already initialized?
|
||||
beq dram_init_finished
|
||||
nop
|
||||
|
||||
#include "../../lib/dram_init.S"
|
||||
|
||||
dram_init_finished:
|
||||
lapcq ., $r13 ; get PC
|
||||
add.d second_copy_complete-dram_init_finished, $r13
|
||||
|
||||
move.d REG_ADDR(config, regi_config, r_bootsel), $r0
|
||||
move.d [$r0], $r0
|
||||
and.d REG_MASK(config, r_bootsel, boot_mode), $r0
|
||||
cmp.d REG_STATE(config, r_bootsel, boot_mode, nand), $r0
|
||||
bne second_copy_complete ; No NAND boot
|
||||
nop
|
||||
|
||||
;; Copy 2MB from NAND flash to SDRAM (at 2-4MB into the SDRAM)
|
||||
move.d 0x40204000, $r10
|
||||
move.d 0x8000, $r11
|
||||
move.d 0x200000, $r12
|
||||
ba copy_nand_to_ram
|
||||
nop
|
||||
second_copy_complete:
|
||||
|
||||
;; Initiate the PA port.
|
||||
move.d CONFIG_ETRAX_DEF_GIO_PA_OUT, $r0
|
||||
move.d REG_ADDR(gio, regi_gio, rw_pa_dout), $r1
|
||||
move.d $r0, [$r1]
|
||||
|
||||
move.d CONFIG_ETRAX_DEF_GIO_PA_OE, $r0
|
||||
move.d REG_ADDR(gio, regi_gio, rw_pa_oe), $r1
|
||||
move.d $r0, [$r1]
|
||||
|
||||
;; Setup the stack to a suitably high address.
|
||||
;; We assume 8 MB is the minimum DRAM 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 - herami) as offset to the current PC.
|
||||
|
||||
move.d REG_ADDR(config, regi_config, r_bootsel), $r0
|
||||
move.d [$r0], $r0
|
||||
and.d REG_MASK(config, r_bootsel, boot_mode), $r0
|
||||
cmp.d REG_STATE(config, r_bootsel, boot_mode, nand), $r0
|
||||
beq hereami2
|
||||
nop
|
||||
hereami:
|
||||
lapcq ., $r5 ; get PC
|
||||
and.d 0x7fffffff, $r5 ; strip any non-cache bit
|
||||
move.d $r5, $r0 ; save for later - flash address of 'herami'
|
||||
add.d _edata, $r5
|
||||
sub.d hereami, $r5 ; r5 = flash address of '_edata'
|
||||
move.d hereami, $r1 ; destination
|
||||
ba 2f
|
||||
nop
|
||||
hereami2:
|
||||
lapcq ., $r5 ; get PC
|
||||
and.d 0x00ffffff, $r5 ; strip any non-cache bit
|
||||
move.d $r5, $r6
|
||||
or.d 0x40200000, $r6
|
||||
move.d $r6, $r0 ; save for later - flash address of 'herami'
|
||||
add.d _edata, $r5
|
||||
sub.d hereami2, $r5 ; r5 = flash address of '_edata'
|
||||
add.d 0x40200000, $r5
|
||||
move.d hereami2, $r1 ; destination
|
||||
2:
|
||||
;; Copy text+data to DRAM
|
||||
|
||||
move.d _edata, $r2 ; end destination
|
||||
1: move.w [$r0+], $r3
|
||||
move.w $r3, [$r1+]
|
||||
cmp.d $r2, $r1
|
||||
bcs 1b
|
||||
nop
|
||||
|
||||
move.d input_data, $r0 ; for the decompressor
|
||||
move.d $r5, [$r0] ; 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
|
||||
|
||||
;; Save command line magic and address.
|
||||
move.d _cmd_line_magic, $r12
|
||||
move.d $r10, [$r12]
|
||||
move.d _cmd_line_addr, $r12
|
||||
move.d $r11, [$r12]
|
||||
|
||||
;; Do the decompression and save compressed size in _inptr
|
||||
|
||||
jsr decompress_kernel
|
||||
nop
|
||||
|
||||
;; Restore command line magic and address.
|
||||
move.d _cmd_line_magic, $r10
|
||||
move.d [$r10], $r10
|
||||
move.d _cmd_line_addr, $r11
|
||||
move.d [$r11], $r11
|
||||
|
||||
;; Put start address of root partition in r9 so the kernel can use it
|
||||
;; when mounting from flash
|
||||
move.d input_data, $r0
|
||||
move.d [$r0], $r9 ; flash address of compressed kernel
|
||||
move.d inptr, $r0
|
||||
add.d [$r0], $r9 ; size of compressed kernel
|
||||
cmp.d 0x40200000, $r9
|
||||
blo enter_kernel
|
||||
nop
|
||||
sub.d 0x40200000, $r9
|
||||
add.d 0x4000, $r9
|
||||
|
||||
enter_kernel:
|
||||
;; Enter the decompressed kernel
|
||||
move.d RAM_INIT_MAGIC, $r8 ; Tell kernel that DRAM is initialized
|
||||
jump 0x40004000 ; kernel is linked to this address
|
||||
nop
|
||||
|
||||
.data
|
||||
|
||||
input_data:
|
||||
.dword 0 ; used by the decompressor
|
||||
_cmd_line_magic:
|
||||
.dword 0
|
||||
_cmd_line_addr:
|
||||
.dword 0
|
||||
is_nand_boot:
|
||||
.dword 0
|
||||
|
||||
#include "../../lib/hw_settings.S"
|
|
@ -0,0 +1,318 @@
|
|||
/*
|
||||
* misc.c
|
||||
*
|
||||
* $Id: misc.c,v 1.8 2005/04/24 18:34:29 starvik 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 vmlinux.lds.S
|
||||
*/
|
||||
|
||||
#define KERNEL_LOAD_ADR 0x40004000
|
||||
|
||||
#include <linux/config.h>
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <asm/arch/hwregs/reg_rdwr.h>
|
||||
#include <asm/arch/hwregs/reg_map.h>
|
||||
#include <asm/arch/hwregs/ser_defs.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");
|
||||
|
||||
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 inline void
|
||||
serout(const char *s, reg_scope_instances regi_ser)
|
||||
{
|
||||
reg_ser_rs_stat_din rs;
|
||||
reg_ser_rw_dout dout = {.data = *s};
|
||||
|
||||
do {
|
||||
rs = REG_RD(ser, regi_ser, rs_stat_din);
|
||||
}
|
||||
while (!rs.tr_rdy);/* Wait for tranceiver. */
|
||||
|
||||
REG_WR(ser, regi_ser, rw_dout, dout);
|
||||
}
|
||||
|
||||
static void
|
||||
puts(const char *s)
|
||||
{
|
||||
#ifndef CONFIG_ETRAX_DEBUG_PORT_NULL
|
||||
while (*s) {
|
||||
#ifdef CONFIG_ETRAX_DEBUG_PORT0
|
||||
serout(s, regi_ser0);
|
||||
#endif
|
||||
#ifdef CONFIG_ETRAX_DEBUG_PORT1
|
||||
serout(s, regi_ser1);
|
||||
#endif
|
||||
#ifdef CONFIG_ETRAX_DEBUG_PORT2
|
||||
serout(s, regi_ser2);
|
||||
#endif
|
||||
#ifdef CONFIG_ETRAX_DEBUG_PORT3
|
||||
serout(s, regi_ser3);
|
||||
#endif
|
||||
*s++;
|
||||
}
|
||||
/* CONFIG_ETRAX_DEBUG_PORT_NULL */
|
||||
#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;
|
||||
}
|
||||
|
||||
static inline void
|
||||
serial_setup(reg_scope_instances regi_ser)
|
||||
{
|
||||
reg_ser_rw_xoff xoff;
|
||||
reg_ser_rw_tr_ctrl tr_ctrl;
|
||||
reg_ser_rw_rec_ctrl rec_ctrl;
|
||||
reg_ser_rw_tr_baud_div tr_baud;
|
||||
reg_ser_rw_rec_baud_div rec_baud;
|
||||
|
||||
/* Turn off XOFF. */
|
||||
xoff = REG_RD(ser, regi_ser, rw_xoff);
|
||||
|
||||
xoff.chr = 0;
|
||||
xoff.automatic = regk_ser_no;
|
||||
|
||||
REG_WR(ser, regi_ser, rw_xoff, xoff);
|
||||
|
||||
/* Set baudrate and stopbits. */
|
||||
tr_ctrl = REG_RD(ser, regi_ser, rw_tr_ctrl);
|
||||
rec_ctrl = REG_RD(ser, regi_ser, rw_rec_ctrl);
|
||||
tr_baud = REG_RD(ser, regi_ser, rw_tr_baud_div);
|
||||
rec_baud = REG_RD(ser, regi_ser, rw_rec_baud_div);
|
||||
|
||||
tr_ctrl.stop_bits = 1; /* 2 stop bits. */
|
||||
|
||||
/*
|
||||
* The baudrate setup is a bit fishy, but in the end the tranceiver is
|
||||
* set to 4800 and the receiver to 115200. The magic value is
|
||||
* 29.493 MHz.
|
||||
*/
|
||||
tr_ctrl.base_freq = regk_ser_f29_493;
|
||||
rec_ctrl.base_freq = regk_ser_f29_493;
|
||||
tr_baud.div = (29493000 / 8) / 4800;
|
||||
rec_baud.div = (29493000 / 8) / 115200;
|
||||
|
||||
REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl);
|
||||
REG_WR(ser, regi_ser, rw_tr_baud_div, tr_baud);
|
||||
REG_WR(ser, regi_ser, rw_rec_ctrl, rec_ctrl);
|
||||
REG_WR(ser, regi_ser, rw_rec_baud_div, rec_baud);
|
||||
}
|
||||
|
||||
void
|
||||
decompress_kernel()
|
||||
{
|
||||
char revision;
|
||||
|
||||
/* input_data is set in head.S */
|
||||
inbuf = input_data;
|
||||
|
||||
#ifdef CONFIG_ETRAX_DEBUG_PORT0
|
||||
serial_setup(regi_ser0);
|
||||
#endif
|
||||
#ifdef CONFIG_ETRAX_DEBUG_PORT1
|
||||
serial_setup(regi_ser1);
|
||||
#endif
|
||||
#ifdef CONFIG_ETRAX_DEBUG_PORT2
|
||||
serial_setup(regi_ser2);
|
||||
#endif
|
||||
#ifdef CONFIG_ETRAX_DEBUG_PORT3
|
||||
serial_setup(regi_ser3);
|
||||
#endif
|
||||
|
||||
setup_normal_output_buffer();
|
||||
|
||||
makecrc();
|
||||
|
||||
__asm__ volatile ("move $vr,%0" : "=rm" (revision));
|
||||
if (revision < 32)
|
||||
{
|
||||
puts("You need an ETRAX FS to run Linux 2.6/crisv32.\n");
|
||||
while(1);
|
||||
}
|
||||
|
||||
puts("Uncompressing Linux...\n");
|
||||
gunzip();
|
||||
puts("Done. Now booting the kernel.\n");
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
#
|
||||
# Makefile for rescue code
|
||||
#
|
||||
target = $(target_rescue_dir)
|
||||
src = $(src_rescue_dir)
|
||||
|
||||
CC = gcc-cris -mlinux -march=v32 $(LINUXINCLUDE)
|
||||
CFLAGS = -O2
|
||||
LD = gcc-cris -mlinux -march=v32 -nostdlib
|
||||
OBJCOPY = objcopy-cris
|
||||
OBJCOPYFLAGS = -O binary --remove-section=.bss
|
||||
|
||||
all: $(target)/rescue.bin
|
||||
|
||||
rescue: rescue.bin
|
||||
# do nothing
|
||||
|
||||
$(target)/rescue.bin: $(target) $(target)/head.o
|
||||
$(LD) -T $(src)/rescue.ld -o $(target)/rescue.o $(target)/head.o
|
||||
$(OBJCOPY) $(OBJCOPYFLAGS) $(target)/rescue.o $(target)/rescue.bin
|
||||
cp -p $(target)/rescue.bin $(objtree)
|
||||
|
||||
$(target):
|
||||
mkdir -p $(target)
|
||||
|
||||
$(target)/head.o: $(src)/head.S
|
||||
$(CC) -D__ASSEMBLY__ -c $< -o $*.o
|
||||
|
||||
clean:
|
||||
rm -f $(target)/*.o $(target)/*.bin
|
||||
|
||||
fastdep:
|
||||
|
||||
modules:
|
||||
|
||||
modules-install:
|
|
@ -0,0 +1,39 @@
|
|||
/* $Id: head.S,v 1.4 2004/11/01 16:10:28 starvik Exp $
|
||||
*
|
||||
* This used to be the rescue code but now that is handled by the
|
||||
* RedBoot based RFL instead. Nothing to see here, move along.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <asm/arch/hwregs/reg_map_asm.h>
|
||||
#include <asm/arch/hwregs/config_defs_asm.h>
|
||||
|
||||
.text
|
||||
|
||||
;; Start clocks for used blocks.
|
||||
move.d REG_ADDR(config, regi_config, rw_clk_ctrl), $r1
|
||||
move.d [$r1], $r0
|
||||
or.d REG_STATE(config, rw_clk_ctrl, cpu, yes) | \
|
||||
REG_STATE(config, rw_clk_ctrl, bif, yes) | \
|
||||
REG_STATE(config, rw_clk_ctrl, fix_io, yes), $r0
|
||||
move.d $r0, [$r1]
|
||||
|
||||
;; Copy 68KB NAND flash to Internal RAM (if NAND boot)
|
||||
move.d 0x38004000, $r10
|
||||
move.d 0x8000, $r11
|
||||
move.d 0x11000, $r12
|
||||
move.d copy_complete, $r13
|
||||
and.d 0x000fffff, $r13
|
||||
or.d 0x38000000, $r13
|
||||
|
||||
#include "../../lib/nand_init.S"
|
||||
|
||||
;; No NAND found
|
||||
move.d CONFIG_ETRAX_PTABLE_SECTOR, $r10
|
||||
jump $r10 ; Jump to decompresser
|
||||
nop
|
||||
|
||||
copy_complete:
|
||||
move.d 0x38000000 + CONFIG_ETRAX_PTABLE_SECTOR, $r10
|
||||
jump $r10 ; Jump to decompresser
|
||||
nop
|
|
@ -0,0 +1,20 @@
|
|||
MEMORY
|
||||
{
|
||||
flash : ORIGIN = 0x00000000,
|
||||
LENGTH = 0x00100000
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.text :
|
||||
{
|
||||
stext = . ;
|
||||
*(.text)
|
||||
etext = . ;
|
||||
} > flash
|
||||
.data :
|
||||
{
|
||||
*(.data)
|
||||
edata = . ;
|
||||
} > flash
|
||||
}
|
|
@ -0,0 +1,625 @@
|
|||
config ETRAX_ETHERNET
|
||||
bool "Ethernet support"
|
||||
depends on ETRAX_ARCH_V32
|
||||
select NET_ETHERNET
|
||||
help
|
||||
This option enables the ETRAX FS built-in 10/100Mbit Ethernet
|
||||
controller.
|
||||
|
||||
config ETRAX_ETHERNET_HW_CSUM
|
||||
bool "Hardware accelerated ethernet checksum and scatter/gather"
|
||||
depends on ETRAX_ETHERNET
|
||||
depends on ETRAX_STREAMCOPROC
|
||||
default y
|
||||
help
|
||||
Hardware acceleration of checksumming and scatter/gather
|
||||
|
||||
config ETRAX_ETHERNET_IFACE0
|
||||
depends on ETRAX_ETHERNET
|
||||
bool "Enable network interface 0"
|
||||
|
||||
config ETRAX_ETHERNET_IFACE1
|
||||
depends on ETRAX_ETHERNET
|
||||
bool "Enable network interface 1 (uses DMA6 and DMA7)"
|
||||
|
||||
choice
|
||||
prompt "Network LED behavior"
|
||||
depends on ETRAX_ETHERNET
|
||||
default ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY
|
||||
|
||||
config ETRAX_NETWORK_LED_ON_WHEN_LINK
|
||||
bool "LED_on_when_link"
|
||||
help
|
||||
Selecting LED_on_when_link will light the LED when there is a
|
||||
connection and will flash off when there is activity.
|
||||
|
||||
Selecting LED_on_when_activity will light the LED only when
|
||||
there is activity.
|
||||
|
||||
This setting will also affect the behaviour of other activity LEDs
|
||||
e.g. Bluetooth.
|
||||
|
||||
config ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY
|
||||
bool "LED_on_when_activity"
|
||||
help
|
||||
Selecting LED_on_when_link will light the LED when there is a
|
||||
connection and will flash off when there is activity.
|
||||
|
||||
Selecting LED_on_when_activity will light the LED only when
|
||||
there is activity.
|
||||
|
||||
This setting will also affect the behaviour of other activity LEDs
|
||||
e.g. Bluetooth.
|
||||
|
||||
endchoice
|
||||
|
||||
config ETRAXFS_SERIAL
|
||||
bool "Serial-port support"
|
||||
depends on ETRAX_ARCH_V32
|
||||
help
|
||||
Enables the ETRAX FS serial driver for ser0 (ttyS0)
|
||||
You probably want this enabled.
|
||||
|
||||
config ETRAX_SERIAL_PORT0
|
||||
bool "Serial port 0 enabled"
|
||||
depends on ETRAXFS_SERIAL
|
||||
help
|
||||
Enables the ETRAX FS serial driver for ser0 (ttyS0)
|
||||
Normally you want this on. You can control what DMA channels to use
|
||||
if you do not need DMA to something else.
|
||||
ser0 can use dma4 or dma6 for output and dma5 or dma7 for input.
|
||||
|
||||
choice
|
||||
prompt "Ser0 DMA in channel "
|
||||
depends on ETRAX_SERIAL_PORT0
|
||||
default ETRAX_SERIAL_PORT0_NO_DMA_IN
|
||||
help
|
||||
What DMA channel to use for ser0.
|
||||
|
||||
|
||||
config ETRAX_SERIAL_PORT0_NO_DMA_IN
|
||||
bool "Ser0 uses no DMA for input"
|
||||
help
|
||||
Do not use DMA for ser0 input.
|
||||
|
||||
config ETRAX_SERIAL_PORT0_DMA7_IN
|
||||
bool "Ser0 uses DMA7 for input"
|
||||
depends on ETRAX_SERIAL_PORT0
|
||||
help
|
||||
Enables the DMA7 input channel for ser0 (ttyS0).
|
||||
If you do not enable DMA, an interrupt for each character will be
|
||||
used when receiveing data.
|
||||
Normally you want to use DMA, unless you use the DMA channel for
|
||||
something else.
|
||||
|
||||
endchoice
|
||||
|
||||
choice
|
||||
prompt "Ser0 DMA out channel"
|
||||
depends on ETRAX_SERIAL_PORT0
|
||||
default ETRAX_SERIAL_PORT0_NO_DMA_OUT
|
||||
|
||||
config ETRAX_SERIAL_PORT0_NO_DMA_OUT
|
||||
bool "Ser0 uses no DMA for output"
|
||||
help
|
||||
Do not use DMA for ser0 output.
|
||||
|
||||
config ETRAX_SERIAL_PORT0_DMA6_OUT
|
||||
bool "Ser0 uses DMA6 for output"
|
||||
depends on ETRAX_SERIAL_PORT0
|
||||
help
|
||||
Enables the DMA6 output channel for ser0 (ttyS0).
|
||||
If you do not enable DMA, an interrupt for each character will be
|
||||
used when transmitting data.
|
||||
Normally you want to use DMA, unless you use the DMA channel for
|
||||
something else.
|
||||
|
||||
endchoice
|
||||
|
||||
config ETRAX_SER0_DTR_BIT
|
||||
string "Ser 0 DTR bit (empty = not used)"
|
||||
depends on ETRAX_SERIAL_PORT0
|
||||
|
||||
config ETRAX_SER0_RI_BIT
|
||||
string "Ser 0 RI bit (empty = not used)"
|
||||
depends on ETRAX_SERIAL_PORT0
|
||||
|
||||
config ETRAX_SER0_DSR_BIT
|
||||
string "Ser 0 DSR bit (empty = not used)"
|
||||
depends on ETRAX_SERIAL_PORT0
|
||||
|
||||
config ETRAX_SER0_CD_BIT
|
||||
string "Ser 0 CD bit (empty = not used)"
|
||||
depends on ETRAX_SERIAL_PORT0
|
||||
|
||||
config ETRAX_SERIAL_PORT1
|
||||
bool "Serial port 1 enabled"
|
||||
depends on ETRAXFS_SERIAL
|
||||
help
|
||||
Enables the ETRAX FS serial driver for ser1 (ttyS1).
|
||||
|
||||
choice
|
||||
prompt "Ser1 DMA in channel "
|
||||
depends on ETRAX_SERIAL_PORT1
|
||||
default ETRAX_SERIAL_PORT1_NO_DMA_IN
|
||||
help
|
||||
What DMA channel to use for ser1.
|
||||
|
||||
|
||||
config ETRAX_SERIAL_PORT1_NO_DMA_IN
|
||||
bool "Ser1 uses no DMA for input"
|
||||
help
|
||||
Do not use DMA for ser1 input.
|
||||
|
||||
config ETRAX_SERIAL_PORT1_DMA5_IN
|
||||
bool "Ser1 uses DMA5 for input"
|
||||
depends on ETRAX_SERIAL_PORT1
|
||||
help
|
||||
Enables the DMA5 input channel for ser1 (ttyS1).
|
||||
If you do not enable DMA, an interrupt for each character will be
|
||||
used when receiveing data.
|
||||
Normally you want this on, unless you use the DMA channel for
|
||||
something else.
|
||||
|
||||
endchoice
|
||||
|
||||
choice
|
||||
prompt "Ser1 DMA out channel "
|
||||
depends on ETRAX_SERIAL_PORT1
|
||||
default ETRAX_SERIAL_PORT1_NO_DMA_OUT
|
||||
help
|
||||
What DMA channel to use for ser1.
|
||||
|
||||
config ETRAX_SERIAL_PORT1_NO_DMA_OUT
|
||||
bool "Ser1 uses no DMA for output"
|
||||
help
|
||||
Do not use DMA for ser1 output.
|
||||
|
||||
config ETRAX_SERIAL_PORT1_DMA4_OUT
|
||||
bool "Ser1 uses DMA4 for output"
|
||||
depends on ETRAX_SERIAL_PORT1
|
||||
help
|
||||
Enables the DMA4 output channel for ser1 (ttyS1).
|
||||
If you do not enable DMA, an interrupt for each character will be
|
||||
used when transmitting data.
|
||||
Normally you want this on, unless you use the DMA channel for
|
||||
something else.
|
||||
|
||||
endchoice
|
||||
|
||||
config ETRAX_SER1_DTR_BIT
|
||||
string "Ser 1 DTR bit (empty = not used)"
|
||||
depends on ETRAX_SERIAL_PORT1
|
||||
|
||||
config ETRAX_SER1_RI_BIT
|
||||
string "Ser 1 RI bit (empty = not used)"
|
||||
depends on ETRAX_SERIAL_PORT1
|
||||
|
||||
config ETRAX_SER1_DSR_BIT
|
||||
string "Ser 1 DSR bit (empty = not used)"
|
||||
depends on ETRAX_SERIAL_PORT1
|
||||
|
||||
config ETRAX_SER1_CD_BIT
|
||||
string "Ser 1 CD bit (empty = not used)"
|
||||
depends on ETRAX_SERIAL_PORT1
|
||||
|
||||
config ETRAX_SERIAL_PORT2
|
||||
bool "Serial port 2 enabled"
|
||||
depends on ETRAXFS_SERIAL
|
||||
help
|
||||
Enables the ETRAX FS serial driver for ser2 (ttyS2).
|
||||
|
||||
choice
|
||||
prompt "Ser2 DMA in channel "
|
||||
depends on ETRAX_SERIAL_PORT2
|
||||
default ETRAX_SERIAL_PORT2_NO_DMA_IN
|
||||
help
|
||||
What DMA channel to use for ser2.
|
||||
|
||||
|
||||
config ETRAX_SERIAL_PORT2_NO_DMA_IN
|
||||
bool "Ser2 uses no DMA for input"
|
||||
help
|
||||
Do not use DMA for ser2 input.
|
||||
|
||||
config ETRAX_SERIAL_PORT2_DMA3_IN
|
||||
bool "Ser2 uses DMA3 for input"
|
||||
depends on ETRAX_SERIAL_PORT2
|
||||
help
|
||||
Enables the DMA3 input channel for ser2 (ttyS2).
|
||||
If you do not enable DMA, an interrupt for each character will be
|
||||
used when receiveing data.
|
||||
Normally you want to use DMA, unless you use the DMA channel for
|
||||
something else.
|
||||
|
||||
endchoice
|
||||
|
||||
choice
|
||||
prompt "Ser2 DMA out channel"
|
||||
depends on ETRAX_SERIAL_PORT2
|
||||
default ETRAX_SERIAL_PORT2_NO_DMA_OUT
|
||||
|
||||
config ETRAX_SERIAL_PORT2_NO_DMA_OUT
|
||||
bool "Ser2 uses no DMA for output"
|
||||
help
|
||||
Do not use DMA for ser2 output.
|
||||
|
||||
config ETRAX_SERIAL_PORT2_DMA2_OUT
|
||||
bool "Ser2 uses DMA2 for output"
|
||||
depends on ETRAX_SERIAL_PORT2
|
||||
help
|
||||
Enables the DMA2 output channel for ser2 (ttyS2).
|
||||
If you do not enable DMA, an interrupt for each character will be
|
||||
used when transmitting data.
|
||||
Normally you want to use DMA, unless you use the DMA channel for
|
||||
something else.
|
||||
|
||||
endchoice
|
||||
|
||||
config ETRAX_SER2_DTR_BIT
|
||||
string "Ser 2 DTR bit (empty = not used)"
|
||||
depends on ETRAX_SERIAL_PORT2
|
||||
|
||||
config ETRAX_SER2_RI_BIT
|
||||
string "Ser 2 RI bit (empty = not used)"
|
||||
depends on ETRAX_SERIAL_PORT2
|
||||
|
||||
config ETRAX_SER2_DSR_BIT
|
||||
string "Ser 2 DSR bit (empty = not used)"
|
||||
depends on ETRAX_SERIAL_PORT2
|
||||
|
||||
config ETRAX_SER2_CD_BIT
|
||||
string "Ser 2 CD bit (empty = not used)"
|
||||
depends on ETRAX_SERIAL_PORT2
|
||||
|
||||
config ETRAX_SERIAL_PORT3
|
||||
bool "Serial port 3 enabled"
|
||||
depends on ETRAXFS_SERIAL
|
||||
help
|
||||
Enables the ETRAX FS serial driver for ser3 (ttyS3).
|
||||
|
||||
choice
|
||||
prompt "Ser3 DMA in channel "
|
||||
depends on ETRAX_SERIAL_PORT3
|
||||
default ETRAX_SERIAL_PORT3_NO_DMA_IN
|
||||
help
|
||||
What DMA channel to use for ser3.
|
||||
|
||||
|
||||
config ETRAX_SERIAL_PORT3_NO_DMA_IN
|
||||
bool "Ser3 uses no DMA for input"
|
||||
help
|
||||
Do not use DMA for ser3 input.
|
||||
|
||||
config ETRAX_SERIAL_PORT3_DMA9_IN
|
||||
bool "Ser3 uses DMA9 for input"
|
||||
depends on ETRAX_SERIAL_PORT3
|
||||
help
|
||||
Enables the DMA9 input channel for ser3 (ttyS3).
|
||||
If you do not enable DMA, an interrupt for each character will be
|
||||
used when receiveing data.
|
||||
Normally you want to use DMA, unless you use the DMA channel for
|
||||
something else.
|
||||
|
||||
endchoice
|
||||
|
||||
choice
|
||||
prompt "Ser3 DMA out channel"
|
||||
depends on ETRAX_SERIAL_PORT3
|
||||
default ETRAX_SERIAL_PORT3_NO_DMA_OUT
|
||||
|
||||
config ETRAX_SERIAL_PORT3_NO_DMA_OUT
|
||||
bool "Ser3 uses no DMA for output"
|
||||
help
|
||||
Do not use DMA for ser3 output.
|
||||
|
||||
config ETRAX_SERIAL_PORT3_DMA8_OUT
|
||||
bool "Ser3 uses DMA8 for output"
|
||||
depends on ETRAX_SERIAL_PORT3
|
||||
help
|
||||
Enables the DMA8 output channel for ser3 (ttyS3).
|
||||
If you do not enable DMA, an interrupt for each character will be
|
||||
used when transmitting data.
|
||||
Normally you want to use DMA, unless you use the DMA channel for
|
||||
something else.
|
||||
|
||||
endchoice
|
||||
|
||||
config ETRAX_SER3_DTR_BIT
|
||||
string "Ser 3 DTR bit (empty = not used)"
|
||||
depends on ETRAX_SERIAL_PORT3
|
||||
|
||||
config ETRAX_SER3_RI_BIT
|
||||
string "Ser 3 RI bit (empty = not used)"
|
||||
depends on ETRAX_SERIAL_PORT3
|
||||
|
||||
config ETRAX_SER3_DSR_BIT
|
||||
string "Ser 3 DSR bit (empty = not used)"
|
||||
depends on ETRAX_SERIAL_PORT3
|
||||
|
||||
config ETRAX_SER3_CD_BIT
|
||||
string "Ser 3 CD bit (empty = not used)"
|
||||
depends on ETRAX_SERIAL_PORT3
|
||||
|
||||
config ETRAX_RS485
|
||||
bool "RS-485 support"
|
||||
depends on ETRAX_SERIAL
|
||||
help
|
||||
Enables support for RS-485 serial communication. For a primer on
|
||||
RS-485, see <http://www.hw.cz/english/docs/rs485/rs485.html>.
|
||||
|
||||
config ETRAX_RS485_DISABLE_RECEIVER
|
||||
bool "Disable serial receiver"
|
||||
depends on ETRAX_RS485
|
||||
help
|
||||
It is necessary to disable the serial receiver to avoid serial
|
||||
loopback. Not all products are able to do this in software only.
|
||||
Axis 2400/2401 must disable receiver.
|
||||
|
||||
config ETRAX_AXISFLASHMAP
|
||||
bool "Axis flash-map support"
|
||||
depends on ETRAX_ARCH_V32
|
||||
select MTD
|
||||
select MTD_CFI
|
||||
select MTD_CFI_AMDSTD
|
||||
select MTD_OBSOLETE_CHIPS
|
||||
select MTD_AMDSTD
|
||||
select MTD_CHAR
|
||||
select MTD_BLOCK
|
||||
select MTD_PARTITIONS
|
||||
select MTD_CONCAT
|
||||
select MTD_COMPLEX_MAPPINGS
|
||||
help
|
||||
This option enables MTD mapping of flash devices. Needed to use
|
||||
flash memories. If unsure, say Y.
|
||||
|
||||
config ETRAX_SYNCHRONOUS_SERIAL
|
||||
bool "Synchronous serial-port support"
|
||||
depends on ETRAX_ARCH_V32
|
||||
help
|
||||
Enables the ETRAX FS synchronous serial driver.
|
||||
|
||||
config ETRAX_SYNCHRONOUS_SERIAL_PORT0
|
||||
bool "Synchronous serial port 0 enabled"
|
||||
depends on ETRAX_SYNCHRONOUS_SERIAL
|
||||
help
|
||||
Enabled synchronous serial port 0.
|
||||
|
||||
config ETRAX_SYNCHRONOUS_SERIAL0_DMA
|
||||
bool "Enable DMA on synchronous serial port 0."
|
||||
depends on ETRAX_SYNCHRONOUS_SERIAL_PORT0
|
||||
help
|
||||
A synchronous serial port can run in manual or DMA mode.
|
||||
Selecting this option will make it run in DMA mode.
|
||||
|
||||
config ETRAX_SYNCHRONOUS_SERIAL_PORT1
|
||||
bool "Synchronous serial port 1 enabled"
|
||||
depends on ETRAX_SYNCHRONOUS_SERIAL
|
||||
help
|
||||
Enabled synchronous serial port 1.
|
||||
|
||||
config ETRAX_SYNCHRONOUS_SERIAL1_DMA
|
||||
bool "Enable DMA on synchronous serial port 1."
|
||||
depends on ETRAX_SYNCHRONOUS_SERIAL_PORT1
|
||||
help
|
||||
A synchronous serial port can run in manual or DMA mode.
|
||||
Selecting this option will make it run in DMA mode.
|
||||
|
||||
config ETRAX_PTABLE_SECTOR
|
||||
int "Byte-offset of partition table sector"
|
||||
depends on ETRAX_AXISFLASHMAP
|
||||
default "65536"
|
||||
help
|
||||
Byte-offset of the partition table in the first flash chip.
|
||||
The default value is 64kB and should not be changed unless
|
||||
you know exactly what you are doing. The only valid reason
|
||||
for changing this is when the flash block size is bigger
|
||||
than 64kB (e.g. when using two parallel 16 bit flashes).
|
||||
|
||||
config ETRAX_NANDFLASH
|
||||
bool "NAND flash support"
|
||||
depends on ETRAX_ARCH_V32
|
||||
select MTD_NAND
|
||||
select MTD_NAND_IDS
|
||||
help
|
||||
This option enables MTD mapping of NAND flash devices. Needed to use
|
||||
NAND flash memories. If unsure, say Y.
|
||||
|
||||
config ETRAX_I2C
|
||||
bool "I2C driver"
|
||||
depends on ETRAX_ARCH_V32
|
||||
help
|
||||
This option enabled the I2C driver used by e.g. the RTC driver.
|
||||
|
||||
config ETRAX_I2C_DATA_PORT
|
||||
string "I2C data pin"
|
||||
depends on ETRAX_I2C
|
||||
help
|
||||
The pin to use for I2C data.
|
||||
|
||||
config ETRAX_I2C_CLK_PORT
|
||||
string "I2C clock pin"
|
||||
depends on ETRAX_I2C
|
||||
help
|
||||
The pin to use for I2C clock.
|
||||
|
||||
config ETRAX_RTC
|
||||
bool "Real Time Clock support"
|
||||
depends on ETRAX_ARCH_V32
|
||||
help
|
||||
Enabled RTC support.
|
||||
|
||||
choice
|
||||
prompt "RTC chip"
|
||||
depends on ETRAX_RTC
|
||||
default ETRAX_PCF8563
|
||||
|
||||
config ETRAX_PCF8563
|
||||
bool "PCF8563"
|
||||
help
|
||||
Philips PCF8563 RTC
|
||||
|
||||
endchoice
|
||||
|
||||
config ETRAX_GPIO
|
||||
bool "GPIO support"
|
||||
depends on ETRAX_ARCH_V32
|
||||
---help---
|
||||
Enables the ETRAX general port device (major 120, minors 0-4).
|
||||
You can use this driver to access the general port bits. It supports
|
||||
these ioctl's:
|
||||
#include <linux/etraxgpio.h>
|
||||
fd = open("/dev/gpioa", O_RDWR); // or /dev/gpiob
|
||||
ioctl(fd, _IO(ETRAXGPIO_IOCTYPE, IO_SETBITS), bits_to_set);
|
||||
ioctl(fd, _IO(ETRAXGPIO_IOCTYPE, IO_CLRBITS), bits_to_clear);
|
||||
err = ioctl(fd, _IO(ETRAXGPIO_IOCTYPE, IO_READ_INBITS), &val);
|
||||
Remember that you need to setup the port directions appropriately in
|
||||
the General configuration.
|
||||
|
||||
config ETRAX_PA_BUTTON_BITMASK
|
||||
hex "PA-buttons bitmask"
|
||||
depends on ETRAX_GPIO
|
||||
default "0x02"
|
||||
help
|
||||
This is a bitmask (8 bits) with information about what bits on PA
|
||||
that are used for buttons.
|
||||
Most products has a so called TEST button on PA1, if that is true
|
||||
use 0x02 here.
|
||||
Use 00 if there are no buttons on PA.
|
||||
If the bitmask is <> 00 a button driver will be included in the gpio
|
||||
driver. ETRAX general I/O support must be enabled.
|
||||
|
||||
config ETRAX_PA_CHANGEABLE_DIR
|
||||
hex "PA user changeable dir mask"
|
||||
depends on ETRAX_GPIO
|
||||
default "0x00"
|
||||
help
|
||||
This is a bitmask (8 bits) with information of what bits in PA that a
|
||||
user can change direction on using ioctl's.
|
||||
Bit set = changeable.
|
||||
You probably want 0x00 here, but it depends on your hardware.
|
||||
|
||||
config ETRAX_PA_CHANGEABLE_BITS
|
||||
hex "PA user changeable bits mask"
|
||||
depends on ETRAX_GPIO
|
||||
default "0x00"
|
||||
help
|
||||
This is a bitmask (8 bits) with information of what bits in PA
|
||||
that a user can change the value on using ioctl's.
|
||||
Bit set = changeable.
|
||||
|
||||
config ETRAX_PB_CHANGEABLE_DIR
|
||||
hex "PB user changeable dir mask"
|
||||
depends on ETRAX_GPIO
|
||||
default "0x00000"
|
||||
help
|
||||
This is a bitmask (18 bits) with information of what bits in PB
|
||||
that a user can change direction on using ioctl's.
|
||||
Bit set = changeable.
|
||||
You probably want 0x00000 here, but it depends on your hardware.
|
||||
|
||||
config ETRAX_PB_CHANGEABLE_BITS
|
||||
hex "PB user changeable bits mask"
|
||||
depends on ETRAX_GPIO
|
||||
default "0x00000"
|
||||
help
|
||||
This is a bitmask (18 bits) with information of what bits in PB
|
||||
that a user can change the value on using ioctl's.
|
||||
Bit set = changeable.
|
||||
|
||||
config ETRAX_PC_CHANGEABLE_DIR
|
||||
hex "PC user changeable dir mask"
|
||||
depends on ETRAX_GPIO
|
||||
default "0x00000"
|
||||
help
|
||||
This is a bitmask (18 bits) with information of what bits in PC
|
||||
that a user can change direction on using ioctl's.
|
||||
Bit set = changeable.
|
||||
You probably want 0x00000 here, but it depends on your hardware.
|
||||
|
||||
config ETRAX_PC_CHANGEABLE_BITS
|
||||
hex "PC user changeable bits mask"
|
||||
depends on ETRAX_GPIO
|
||||
default "0x00000"
|
||||
help
|
||||
This is a bitmask (18 bits) with information of what bits in PC
|
||||
that a user can change the value on using ioctl's.
|
||||
Bit set = changeable.
|
||||
|
||||
config ETRAX_PD_CHANGEABLE_DIR
|
||||
hex "PD user changeable dir mask"
|
||||
depends on ETRAX_GPIO
|
||||
default "0x00000"
|
||||
help
|
||||
This is a bitmask (18 bits) with information of what bits in PD
|
||||
that a user can change direction on using ioctl's.
|
||||
Bit set = changeable.
|
||||
You probably want 0x00000 here, but it depends on your hardware.
|
||||
|
||||
config ETRAX_PD_CHANGEABLE_BITS
|
||||
hex "PD user changeable bits mask"
|
||||
depends on ETRAX_GPIO
|
||||
default "0x00000"
|
||||
help
|
||||
This is a bitmask (18 bits) with information of what bits in PD
|
||||
that a user can change the value on using ioctl's.
|
||||
Bit set = changeable.
|
||||
|
||||
config ETRAX_PE_CHANGEABLE_DIR
|
||||
hex "PE user changeable dir mask"
|
||||
depends on ETRAX_GPIO
|
||||
default "0x00000"
|
||||
help
|
||||
This is a bitmask (18 bits) with information of what bits in PE
|
||||
that a user can change direction on using ioctl's.
|
||||
Bit set = changeable.
|
||||
You probably want 0x00000 here, but it depends on your hardware.
|
||||
|
||||
config ETRAX_PE_CHANGEABLE_BITS
|
||||
hex "PE user changeable bits mask"
|
||||
depends on ETRAX_GPIO
|
||||
default "0x00000"
|
||||
help
|
||||
This is a bitmask (18 bits) with information of what bits in PE
|
||||
that a user can change the value on using ioctl's.
|
||||
Bit set = changeable.
|
||||
|
||||
config ETRAX_IDE
|
||||
bool "ATA/IDE support"
|
||||
depends on ETRAX_ARCH_V32
|
||||
select IDE
|
||||
select BLK_DEV_IDE
|
||||
select BLK_DEV_IDEDISK
|
||||
select BLK_DEV_IDECD
|
||||
select BLK_DEV_IDEDMA
|
||||
help
|
||||
Enables the ETRAX IDE driver.
|
||||
|
||||
config ETRAX_CARDBUS
|
||||
bool "Cardbus support"
|
||||
depends on ETRAX_ARCH_V32
|
||||
select PCCARD
|
||||
select CARDBUS
|
||||
select HOTPLUG
|
||||
select PCCARD_NONSTATIC
|
||||
help
|
||||
Enabled the ETRAX Carbus driver.
|
||||
|
||||
config PCI
|
||||
bool
|
||||
depends on ETRAX_CARDBUS
|
||||
default y
|
||||
|
||||
config ETRAX_IOP_FW_LOAD
|
||||
tristate "IO-processor hotplug firmware loading support"
|
||||
depends on ETRAX_ARCH_V32
|
||||
select FW_LOADER
|
||||
help
|
||||
Enables IO-processor hotplug firmware loading support.
|
||||
|
||||
config ETRAX_STREAMCOPROC
|
||||
tristate "Stream co-processor driver enabled"
|
||||
depends on ETRAX_ARCH_V32
|
||||
help
|
||||
This option enables a driver for the stream co-processor
|
||||
for cryptographic operations.
|
|
@ -0,0 +1,13 @@
|
|||
#
|
||||
# Makefile for Etrax-specific drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_ETRAX_STREAMCOPROC) += cryptocop.o
|
||||
obj-$(CONFIG_ETRAX_AXISFLASHMAP) += axisflashmap.o
|
||||
obj-$(CONFIG_ETRAX_NANDFLASH) += nandflash.o
|
||||
obj-$(CONFIG_ETRAX_GPIO) += gpio.o
|
||||
obj-$(CONFIG_ETRAX_IOP_FW_LOAD) += iop_fw_load.o
|
||||
obj-$(CONFIG_ETRAX_PCF8563) += pcf8563.o
|
||||
obj-$(CONFIG_ETRAX_I2C) += i2c.o
|
||||
obj-$(CONFIG_ETRAX_SYNCHRONOUS_SERIAL) += sync_serial.o
|
||||
obj-$(CONFIG_PCI) += pci/
|
|
@ -0,0 +1,455 @@
|
|||
/*
|
||||
* Physical mapping layer for MTD using the Axis partitiontable format
|
||||
*
|
||||
* Copyright (c) 2001, 2002, 2003 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.
|
||||
*
|
||||
* Copy of os/lx25/arch/cris/arch-v10/drivers/axisflashmap.c 1.5
|
||||
* with minor changes.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/config.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <linux/mtd/concat.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/mtdram.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include <asm/arch/hwregs/config_defs.h>
|
||||
#include <asm/axisflashmap.h>
|
||||
#include <asm/mmu.h>
|
||||
|
||||
#define MEM_CSE0_SIZE (0x04000000)
|
||||
#define MEM_CSE1_SIZE (0x04000000)
|
||||
|
||||
#define FLASH_UNCACHED_ADDR KSEG_E
|
||||
#define FLASH_CACHED_ADDR KSEG_F
|
||||
|
||||
#if CONFIG_ETRAX_FLASH_BUSWIDTH==1
|
||||
#define flash_data __u8
|
||||
#elif CONFIG_ETRAX_FLASH_BUSWIDTH==2
|
||||
#define flash_data __u16
|
||||
#elif CONFIG_ETRAX_FLASH_BUSWIDTH==4
|
||||
#define flash_data __u16
|
||||
#endif
|
||||
|
||||
/* From head.S */
|
||||
extern unsigned long romfs_start, romfs_length, romfs_in_flash;
|
||||
|
||||
/* The master mtd for the entire flash. */
|
||||
struct mtd_info* axisflash_mtd = NULL;
|
||||
|
||||
/* Map driver functions. */
|
||||
|
||||
static map_word flash_read(struct map_info *map, unsigned long ofs)
|
||||
{
|
||||
map_word tmp;
|
||||
tmp.x[0] = *(flash_data *)(map->map_priv_1 + ofs);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static void flash_copy_from(struct map_info *map, void *to,
|
||||
unsigned long from, ssize_t len)
|
||||
{
|
||||
memcpy(to, (void *)(map->map_priv_1 + from), len);
|
||||
}
|
||||
|
||||
static void flash_write(struct map_info *map, map_word d, unsigned long adr)
|
||||
{
|
||||
*(flash_data *)(map->map_priv_1 + adr) = (flash_data)d.x[0];
|
||||
}
|
||||
|
||||
/*
|
||||
* The map for chip select e0.
|
||||
*
|
||||
* We run into tricky coherence situations if we mix cached with uncached
|
||||
* accesses to we only use the uncached version here.
|
||||
*
|
||||
* The size field is the total size where the flash chips may be mapped on the
|
||||
* chip select. MTD probes should find all devices there and it does not matter
|
||||
* if there are unmapped gaps or aliases (mirrors of flash devices). The MTD
|
||||
* probes will ignore them.
|
||||
*
|
||||
* The start address in map_priv_1 is in virtual memory so we cannot use
|
||||
* MEM_CSE0_START but must rely on that FLASH_UNCACHED_ADDR is the start
|
||||
* address of cse0.
|
||||
*/
|
||||
static struct map_info map_cse0 = {
|
||||
.name = "cse0",
|
||||
.size = MEM_CSE0_SIZE,
|
||||
.bankwidth = CONFIG_ETRAX_FLASH_BUSWIDTH,
|
||||
.read = flash_read,
|
||||
.copy_from = flash_copy_from,
|
||||
.write = flash_write,
|
||||
.map_priv_1 = FLASH_UNCACHED_ADDR
|
||||
};
|
||||
|
||||
/*
|
||||
* The map for chip select e1.
|
||||
*
|
||||
* If there was a gap between cse0 and cse1, map_priv_1 would get the wrong
|
||||
* address, but there isn't.
|
||||
*/
|
||||
static struct map_info map_cse1 = {
|
||||
.name = "cse1",
|
||||
.size = MEM_CSE1_SIZE,
|
||||
.bankwidth = CONFIG_ETRAX_FLASH_BUSWIDTH,
|
||||
.read = flash_read,
|
||||
.copy_from = flash_copy_from,
|
||||
.write = flash_write,
|
||||
.map_priv_1 = FLASH_UNCACHED_ADDR + MEM_CSE0_SIZE
|
||||
};
|
||||
|
||||
/* If no partition-table was found, we use this default-set. */
|
||||
#define MAX_PARTITIONS 7
|
||||
#define NUM_DEFAULT_PARTITIONS 3
|
||||
|
||||
/*
|
||||
* Default flash size is 2MB. CONFIG_ETRAX_PTABLE_SECTOR is most likely the
|
||||
* size of one flash block and "filesystem"-partition needs 5 blocks to be able
|
||||
* to use JFFS.
|
||||
*/
|
||||
static struct mtd_partition axis_default_partitions[NUM_DEFAULT_PARTITIONS] = {
|
||||
{
|
||||
.name = "boot firmware",
|
||||
.size = CONFIG_ETRAX_PTABLE_SECTOR,
|
||||
.offset = 0
|
||||
},
|
||||
{
|
||||
.name = "kernel",
|
||||
.size = 0x200000 - (6 * CONFIG_ETRAX_PTABLE_SECTOR),
|
||||
.offset = CONFIG_ETRAX_PTABLE_SECTOR
|
||||
},
|
||||
{
|
||||
.name = "filesystem",
|
||||
.size = 5 * CONFIG_ETRAX_PTABLE_SECTOR,
|
||||
.offset = 0x200000 - (5 * CONFIG_ETRAX_PTABLE_SECTOR)
|
||||
}
|
||||
};
|
||||
|
||||
/* Initialize the ones normally used. */
|
||||
static struct mtd_partition axis_partitions[MAX_PARTITIONS] = {
|
||||
{
|
||||
.name = "part0",
|
||||
.size = CONFIG_ETRAX_PTABLE_SECTOR,
|
||||
.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
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* Probe a chip select for AMD-compatible (JEDEC) or CFI-compatible flash
|
||||
* chips in that order (because the amd_flash-driver is faster).
|
||||
*/
|
||||
static struct mtd_info *probe_cs(struct map_info *map_cs)
|
||||
{
|
||||
struct mtd_info *mtd_cs = NULL;
|
||||
|
||||
printk(KERN_INFO
|
||||
"%s: Probing a 0x%08lx bytes large window at 0x%08lx.\n",
|
||||
map_cs->name, map_cs->size, map_cs->map_priv_1);
|
||||
|
||||
#ifdef CONFIG_MTD_AMDSTD
|
||||
mtd_cs = do_map_probe("amd_flash", map_cs);
|
||||
#endif
|
||||
#ifdef CONFIG_MTD_CFI
|
||||
if (!mtd_cs) {
|
||||
mtd_cs = do_map_probe("cfi_probe", map_cs);
|
||||
}
|
||||
#endif
|
||||
|
||||
return mtd_cs;
|
||||
}
|
||||
|
||||
/*
|
||||
* Probe each chip select individually for flash chips. If there are chips on
|
||||
* both cse0 and cse1, the mtd_info structs will be concatenated to one struct
|
||||
* so that MTD partitions can cross chip boundries.
|
||||
*
|
||||
* The only known restriction to how you can mount your chips is that each
|
||||
* chip select must hold similar flash chips. But you need external hardware
|
||||
* to do that anyway and you can put totally different chips on cse0 and cse1
|
||||
* so it isn't really much of a restriction.
|
||||
*/
|
||||
extern struct mtd_info* __init crisv32_nand_flash_probe (void);
|
||||
static struct mtd_info *flash_probe(void)
|
||||
{
|
||||
struct mtd_info *mtd_cse0;
|
||||
struct mtd_info *mtd_cse1;
|
||||
struct mtd_info *mtd_nand = NULL;
|
||||
struct mtd_info *mtd_total;
|
||||
struct mtd_info *mtds[3];
|
||||
int count = 0;
|
||||
|
||||
if ((mtd_cse0 = probe_cs(&map_cse0)) != NULL)
|
||||
mtds[count++] = mtd_cse0;
|
||||
if ((mtd_cse1 = probe_cs(&map_cse1)) != NULL)
|
||||
mtds[count++] = mtd_cse1;
|
||||
|
||||
#ifdef CONFIG_ETRAX_NANDFLASH
|
||||
if ((mtd_nand = crisv32_nand_flash_probe()) != NULL)
|
||||
mtds[count++] = mtd_nand;
|
||||
#endif
|
||||
|
||||
if (!mtd_cse0 && !mtd_cse1 && !mtd_nand) {
|
||||
/* No chip found. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (count > 1) {
|
||||
#ifdef CONFIG_MTD_CONCAT
|
||||
/* Since the concatenation layer adds a small overhead we
|
||||
* could try to figure out if the chips in cse0 and cse1 are
|
||||
* identical and reprobe the whole cse0+cse1 window. But since
|
||||
* flash chips are slow, the overhead is relatively small.
|
||||
* So we use the MTD concatenation layer instead of further
|
||||
* complicating the probing procedure.
|
||||
*/
|
||||
mtd_total = mtd_concat_create(mtds,
|
||||
count,
|
||||
"cse0+cse1+nand");
|
||||
#else
|
||||
printk(KERN_ERR "%s and %s: Cannot concatenate due to kernel "
|
||||
"(mis)configuration!\n", map_cse0.name, map_cse1.name);
|
||||
mtd_toal = NULL;
|
||||
#endif
|
||||
if (!mtd_total) {
|
||||
printk(KERN_ERR "%s and %s: Concatenation failed!\n",
|
||||
map_cse0.name, map_cse1.name);
|
||||
|
||||
/* The best we can do now is to only use what we found
|
||||
* at cse0.
|
||||
*/
|
||||
mtd_total = mtd_cse0;
|
||||
map_destroy(mtd_cse1);
|
||||
}
|
||||
} else {
|
||||
mtd_total = mtd_cse0? mtd_cse0 : mtd_cse1 ? mtd_cse1 : mtd_nand;
|
||||
|
||||
}
|
||||
|
||||
return mtd_total;
|
||||
}
|
||||
|
||||
extern unsigned long crisv32_nand_boot;
|
||||
extern unsigned long crisv32_nand_cramfs_offset;
|
||||
|
||||
/*
|
||||
* Probe the flash chip(s) and, if it succeeds, read the partition-table
|
||||
* and register the partitions with MTD.
|
||||
*/
|
||||
static int __init init_axis_flash(void)
|
||||
{
|
||||
struct mtd_info *mymtd;
|
||||
int err = 0;
|
||||
int pidx = 0;
|
||||
struct partitiontable_head *ptable_head = NULL;
|
||||
struct partitiontable_entry *ptable;
|
||||
int use_default_ptable = 1; /* Until proven otherwise. */
|
||||
const char *pmsg = KERN_INFO " /dev/flash%d at 0x%08x, size 0x%08x\n";
|
||||
static char page[512];
|
||||
size_t len;
|
||||
|
||||
#ifndef CONFIG_ETRAXFS_SIM
|
||||
mymtd = flash_probe();
|
||||
mymtd->read(mymtd, CONFIG_ETRAX_PTABLE_SECTOR, 512, &len, page);
|
||||
ptable_head = (struct partitiontable_head *)(page + PARTITION_TABLE_OFFSET);
|
||||
|
||||
if (!mymtd) {
|
||||
/* There's no reason to use this module if no flash chip can
|
||||
* be identified. Make sure that's understood.
|
||||
*/
|
||||
printk(KERN_INFO "axisflashmap: Found no flash chip.\n");
|
||||
} else {
|
||||
printk(KERN_INFO "%s: 0x%08x bytes of flash memory.\n",
|
||||
mymtd->name, mymtd->size);
|
||||
axisflash_mtd = mymtd;
|
||||
}
|
||||
|
||||
if (mymtd) {
|
||||
mymtd->owner = THIS_MODULE;
|
||||
}
|
||||
pidx++; /* First partition is always set to the default. */
|
||||
|
||||
if (ptable_head && (ptable_head->magic == PARTITION_TABLE_MAGIC)
|
||||
&& (ptable_head->size <
|
||||
(MAX_PARTITIONS * sizeof(struct partitiontable_entry) +
|
||||
PARTITIONTABLE_END_MARKER_SIZE))
|
||||
&& (*(unsigned long*)((void*)ptable_head + sizeof(*ptable_head) +
|
||||
ptable_head->size -
|
||||
PARTITIONTABLE_END_MARKER_SIZE)
|
||||
== 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 = CONFIG_ETRAX_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++;
|
||||
}
|
||||
ptable_ok = (csum == ptable_head->checksum);
|
||||
|
||||
/* Read the entries and use/show the info. */
|
||||
printk(KERN_INFO " Found a%s partition table at 0x%p-0x%p.\n",
|
||||
(ptable_ok ? " valid" : "n invalid"), ptable_head,
|
||||
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) {
|
||||
|
||||
axis_partitions[pidx].offset = offset + ptable->offset + (crisv32_nand_boot ? 16384 : 0);
|
||||
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 (romfs_in_flash) {
|
||||
/* Add an overlapping device for the root partition (romfs). */
|
||||
|
||||
axis_partitions[pidx].name = "romfs";
|
||||
if (crisv32_nand_boot) {
|
||||
char* data = kmalloc(1024, GFP_KERNEL);
|
||||
int len;
|
||||
int offset = crisv32_nand_cramfs_offset & ~(1024-1);
|
||||
char* tmp;
|
||||
|
||||
mymtd->read(mymtd, offset, 1024, &len, data);
|
||||
tmp = &data[crisv32_nand_cramfs_offset % 512];
|
||||
axis_partitions[pidx].size = *(unsigned*)(tmp + 4);
|
||||
axis_partitions[pidx].offset = crisv32_nand_cramfs_offset;
|
||||
kfree(data);
|
||||
} else {
|
||||
axis_partitions[pidx].size = romfs_length;
|
||||
axis_partitions[pidx].offset = romfs_start - FLASH_CACHED_ADDR;
|
||||
}
|
||||
|
||||
axis_partitions[pidx].mask_flags |= MTD_WRITEABLE;
|
||||
|
||||
printk(KERN_INFO
|
||||
" Adding readonly flash partition for romfs image:\n");
|
||||
printk(pmsg, pidx, axis_partitions[pidx].offset,
|
||||
axis_partitions[pidx].size);
|
||||
pidx++;
|
||||
}
|
||||
|
||||
if (mymtd) {
|
||||
if (use_default_ptable) {
|
||||
printk(KERN_INFO " Using default partition table.\n");
|
||||
err = add_mtd_partitions(mymtd, axis_default_partitions,
|
||||
NUM_DEFAULT_PARTITIONS);
|
||||
} else {
|
||||
err = add_mtd_partitions(mymtd, axis_partitions, pidx);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
panic("axisflashmap could not add MTD partitions!\n");
|
||||
}
|
||||
}
|
||||
/* CONFIG_EXTRAXFS_SIM */
|
||||
#endif
|
||||
|
||||
if (!romfs_in_flash) {
|
||||
/* Create an RAM device for the root partition (romfs). */
|
||||
|
||||
#if !defined(CONFIG_MTD_MTDRAM) || (CONFIG_MTDRAM_TOTAL_SIZE != 0) || (CONFIG_MTDRAM_ABS_POS != 0)
|
||||
/* No use trying to boot this kernel from RAM. Panic! */
|
||||
printk(KERN_EMERG "axisflashmap: Cannot create an MTD RAM "
|
||||
"device due to kernel (mis)configuration!\n");
|
||||
panic("This kernel cannot boot from RAM!\n");
|
||||
#else
|
||||
struct mtd_info *mtd_ram;
|
||||
|
||||
mtd_ram = (struct mtd_info *)kmalloc(sizeof(struct mtd_info),
|
||||
GFP_KERNEL);
|
||||
if (!mtd_ram) {
|
||||
panic("axisflashmap couldn't allocate memory for "
|
||||
"mtd_info!\n");
|
||||
}
|
||||
|
||||
printk(KERN_INFO " Adding RAM partition for romfs image:\n");
|
||||
printk(pmsg, pidx, romfs_start, romfs_length);
|
||||
|
||||
err = mtdram_init_device(mtd_ram, (void*)romfs_start,
|
||||
romfs_length, "romfs");
|
||||
if (err) {
|
||||
panic("axisflashmap could not initialize MTD RAM "
|
||||
"device!\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* This adds the above to the kernels init-call chain. */
|
||||
module_init(init_axis_flash);
|
||||
|
||||
EXPORT_SYMBOL(axisflash_mtd);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,766 @@
|
|||
/* $Id: gpio.c,v 1.16 2005/06/19 17:06:49 starvik Exp $
|
||||
*
|
||||
* ETRAX CRISv32 general port I/O device
|
||||
*
|
||||
* Copyright (c) 1999, 2000, 2001, 2002, 2003 Axis Communications AB
|
||||
*
|
||||
* Authors: Bjorn Wesen (initial version)
|
||||
* Ola Knutsson (LED handling)
|
||||
* Johan Adolfsson (read/set directions, write, port G,
|
||||
* port to ETRAX FS.
|
||||
*
|
||||
* $Log: gpio.c,v $
|
||||
* Revision 1.16 2005/06/19 17:06:49 starvik
|
||||
* Merge of Linux 2.6.12.
|
||||
*
|
||||
* Revision 1.15 2005/05/25 08:22:20 starvik
|
||||
* Changed GPIO port order to fit packages/devices/axis-2.4.
|
||||
*
|
||||
* Revision 1.14 2005/04/24 18:35:08 starvik
|
||||
* Updated with final register headers.
|
||||
*
|
||||
* Revision 1.13 2005/03/15 15:43:00 starvik
|
||||
* dev_id needs to be supplied for shared IRQs.
|
||||
*
|
||||
* Revision 1.12 2005/03/10 17:12:00 starvik
|
||||
* Protect alarm list with spinlock.
|
||||
*
|
||||
* Revision 1.11 2005/01/05 06:08:59 starvik
|
||||
* No need to do local_irq_disable after local_irq_save.
|
||||
*
|
||||
* Revision 1.10 2004/11/19 08:38:31 starvik
|
||||
* Removed old crap.
|
||||
*
|
||||
* Revision 1.9 2004/05/14 07:58:02 starvik
|
||||
* Merge of changes from 2.4
|
||||
*
|
||||
* Revision 1.8 2003/09/11 07:29:50 starvik
|
||||
* Merge of Linux 2.6.0-test5
|
||||
*
|
||||
* Revision 1.7 2003/07/10 13:25:46 starvik
|
||||
* Compiles for 2.5.74
|
||||
* Lindented ethernet.c
|
||||
*
|
||||
* Revision 1.6 2003/07/04 08:27:46 starvik
|
||||
* Merge of Linux 2.5.74
|
||||
*
|
||||
* Revision 1.5 2003/06/10 08:26:37 johana
|
||||
* Etrax -> ETRAX CRISv32
|
||||
*
|
||||
* Revision 1.4 2003/06/05 14:22:48 johana
|
||||
* Initialise some_alarms.
|
||||
*
|
||||
* Revision 1.3 2003/06/05 10:15:46 johana
|
||||
* New INTR_VECT macros.
|
||||
* Enable interrupts in global config.
|
||||
*
|
||||
* Revision 1.2 2003/06/03 15:52:50 johana
|
||||
* Initial CRIS v32 version.
|
||||
*
|
||||
* Revision 1.1 2003/06/03 08:53:15 johana
|
||||
* Copy of os/lx25/arch/cris/arch-v10/drivers/gpio.c version 1.7.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include <asm/etraxgpio.h>
|
||||
#include <asm/arch/hwregs/reg_map.h>
|
||||
#include <asm/arch/hwregs/reg_rdwr.h>
|
||||
#include <asm/arch/hwregs/gio_defs.h>
|
||||
#include <asm/arch/hwregs/intr_vect_defs.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
/* The following gio ports on ETRAX FS is available:
|
||||
* pa 8 bits, supports interrupts off, hi, low, set, posedge, negedge anyedge
|
||||
* pb 18 bits
|
||||
* pc 18 bits
|
||||
* pd 18 bits
|
||||
* pe 18 bits
|
||||
* each port has a rw_px_dout, r_px_din and rw_px_oe register.
|
||||
*/
|
||||
|
||||
#define GPIO_MAJOR 120 /* experimental MAJOR number */
|
||||
|
||||
#define D(x)
|
||||
|
||||
#if 0
|
||||
static int dp_cnt;
|
||||
#define DP(x) do { dp_cnt++; if (dp_cnt % 1000 == 0) x; }while(0)
|
||||
#else
|
||||
#define DP(x)
|
||||
#endif
|
||||
|
||||
static char gpio_name[] = "etrax gpio";
|
||||
|
||||
#if 0
|
||||
static wait_queue_head_t *gpio_wq;
|
||||
#endif
|
||||
|
||||
static int gpio_ioctl(struct inode *inode, struct file *file,
|
||||
unsigned int cmd, unsigned long arg);
|
||||
static ssize_t gpio_write(struct file * file, const char * buf, size_t count,
|
||||
loff_t *off);
|
||||
static int gpio_open(struct inode *inode, struct file *filp);
|
||||
static int gpio_release(struct inode *inode, struct file *filp);
|
||||
static unsigned int gpio_poll(struct file *filp, struct poll_table_struct *wait);
|
||||
|
||||
/* private data per open() of this driver */
|
||||
|
||||
struct gpio_private {
|
||||
struct gpio_private *next;
|
||||
/* The IO_CFG_WRITE_MODE_VALUE only support 8 bits: */
|
||||
unsigned char clk_mask;
|
||||
unsigned char data_mask;
|
||||
unsigned char write_msb;
|
||||
unsigned char pad1;
|
||||
/* These fields are generic */
|
||||
unsigned long highalarm, lowalarm;
|
||||
wait_queue_head_t alarm_wq;
|
||||
int minor;
|
||||
};
|
||||
|
||||
/* linked list of alarms to check for */
|
||||
|
||||
static struct gpio_private *alarmlist = 0;
|
||||
|
||||
static int gpio_some_alarms = 0; /* Set if someone uses alarm */
|
||||
static unsigned long gpio_pa_high_alarms = 0;
|
||||
static unsigned long gpio_pa_low_alarms = 0;
|
||||
|
||||
static DEFINE_SPINLOCK(alarm_lock);
|
||||
|
||||
#define NUM_PORTS (GPIO_MINOR_LAST+1)
|
||||
#define GIO_REG_RD_ADDR(reg) (volatile unsigned long*) (regi_gio + REG_RD_ADDR_gio_##reg )
|
||||
#define GIO_REG_WR_ADDR(reg) (volatile unsigned long*) (regi_gio + REG_RD_ADDR_gio_##reg )
|
||||
unsigned long led_dummy;
|
||||
|
||||
static volatile unsigned long *data_out[NUM_PORTS] = {
|
||||
GIO_REG_WR_ADDR(rw_pa_dout),
|
||||
GIO_REG_WR_ADDR(rw_pb_dout),
|
||||
&led_dummy,
|
||||
GIO_REG_WR_ADDR(rw_pc_dout),
|
||||
GIO_REG_WR_ADDR(rw_pd_dout),
|
||||
GIO_REG_WR_ADDR(rw_pe_dout),
|
||||
};
|
||||
|
||||
static volatile unsigned long *data_in[NUM_PORTS] = {
|
||||
GIO_REG_RD_ADDR(r_pa_din),
|
||||
GIO_REG_RD_ADDR(r_pb_din),
|
||||
&led_dummy,
|
||||
GIO_REG_RD_ADDR(r_pc_din),
|
||||
GIO_REG_RD_ADDR(r_pd_din),
|
||||
GIO_REG_RD_ADDR(r_pe_din),
|
||||
};
|
||||
|
||||
static unsigned long changeable_dir[NUM_PORTS] = {
|
||||
CONFIG_ETRAX_PA_CHANGEABLE_DIR,
|
||||
CONFIG_ETRAX_PB_CHANGEABLE_DIR,
|
||||
0,
|
||||
CONFIG_ETRAX_PC_CHANGEABLE_DIR,
|
||||
CONFIG_ETRAX_PD_CHANGEABLE_DIR,
|
||||
CONFIG_ETRAX_PE_CHANGEABLE_DIR,
|
||||
};
|
||||
|
||||
static unsigned long changeable_bits[NUM_PORTS] = {
|
||||
CONFIG_ETRAX_PA_CHANGEABLE_BITS,
|
||||
CONFIG_ETRAX_PB_CHANGEABLE_BITS,
|
||||
0,
|
||||
CONFIG_ETRAX_PC_CHANGEABLE_BITS,
|
||||
CONFIG_ETRAX_PD_CHANGEABLE_BITS,
|
||||
CONFIG_ETRAX_PE_CHANGEABLE_BITS,
|
||||
};
|
||||
|
||||
static volatile unsigned long *dir_oe[NUM_PORTS] = {
|
||||
GIO_REG_WR_ADDR(rw_pa_oe),
|
||||
GIO_REG_WR_ADDR(rw_pb_oe),
|
||||
&led_dummy,
|
||||
GIO_REG_WR_ADDR(rw_pc_oe),
|
||||
GIO_REG_WR_ADDR(rw_pd_oe),
|
||||
GIO_REG_WR_ADDR(rw_pe_oe),
|
||||
};
|
||||
|
||||
|
||||
|
||||
static unsigned int
|
||||
gpio_poll(struct file *file,
|
||||
poll_table *wait)
|
||||
{
|
||||
unsigned int mask = 0;
|
||||
struct gpio_private *priv = (struct gpio_private *)file->private_data;
|
||||
unsigned long data;
|
||||
poll_wait(file, &priv->alarm_wq, wait);
|
||||
if (priv->minor == GPIO_MINOR_A) {
|
||||
reg_gio_rw_intr_cfg intr_cfg;
|
||||
unsigned long tmp;
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
data = REG_TYPE_CONV(unsigned long, reg_gio_r_pa_din, REG_RD(gio, regi_gio, r_pa_din));
|
||||
/* PA has support for interrupt
|
||||
* lets activate high for those low and with highalarm set
|
||||
*/
|
||||
intr_cfg = REG_RD(gio, regi_gio, rw_intr_cfg);
|
||||
|
||||
tmp = ~data & priv->highalarm & 0xFF;
|
||||
if (tmp & (1 << 0)) {
|
||||
intr_cfg.pa0 = regk_gio_hi;
|
||||
}
|
||||
if (tmp & (1 << 1)) {
|
||||
intr_cfg.pa1 = regk_gio_hi;
|
||||
}
|
||||
if (tmp & (1 << 2)) {
|
||||
intr_cfg.pa2 = regk_gio_hi;
|
||||
}
|
||||
if (tmp & (1 << 3)) {
|
||||
intr_cfg.pa3 = regk_gio_hi;
|
||||
}
|
||||
if (tmp & (1 << 4)) {
|
||||
intr_cfg.pa4 = regk_gio_hi;
|
||||
}
|
||||
if (tmp & (1 << 5)) {
|
||||
intr_cfg.pa5 = regk_gio_hi;
|
||||
}
|
||||
if (tmp & (1 << 6)) {
|
||||
intr_cfg.pa6 = regk_gio_hi;
|
||||
}
|
||||
if (tmp & (1 << 7)) {
|
||||
intr_cfg.pa7 = regk_gio_hi;
|
||||
}
|
||||
/*
|
||||
* lets activate low for those high and with lowalarm set
|
||||
*/
|
||||
tmp = data & priv->lowalarm & 0xFF;
|
||||
if (tmp & (1 << 0)) {
|
||||
intr_cfg.pa0 = regk_gio_lo;
|
||||
}
|
||||
if (tmp & (1 << 1)) {
|
||||
intr_cfg.pa1 = regk_gio_lo;
|
||||
}
|
||||
if (tmp & (1 << 2)) {
|
||||
intr_cfg.pa2 = regk_gio_lo;
|
||||
}
|
||||
if (tmp & (1 << 3)) {
|
||||
intr_cfg.pa3 = regk_gio_lo;
|
||||
}
|
||||
if (tmp & (1 << 4)) {
|
||||
intr_cfg.pa4 = regk_gio_lo;
|
||||
}
|
||||
if (tmp & (1 << 5)) {
|
||||
intr_cfg.pa5 = regk_gio_lo;
|
||||
}
|
||||
if (tmp & (1 << 6)) {
|
||||
intr_cfg.pa6 = regk_gio_lo;
|
||||
}
|
||||
if (tmp & (1 << 7)) {
|
||||
intr_cfg.pa7 = regk_gio_lo;
|
||||
}
|
||||
|
||||
REG_WR(gio, regi_gio, rw_intr_cfg, intr_cfg);
|
||||
local_irq_restore(flags);
|
||||
} else if (priv->minor <= GPIO_MINOR_E)
|
||||
data = *data_in[priv->minor];
|
||||
else
|
||||
return 0;
|
||||
|
||||
if ((data & priv->highalarm) ||
|
||||
(~data & priv->lowalarm)) {
|
||||
mask = POLLIN|POLLRDNORM;
|
||||
}
|
||||
|
||||
DP(printk("gpio_poll ready: mask 0x%08X\n", mask));
|
||||
return mask;
|
||||
}
|
||||
|
||||
int etrax_gpio_wake_up_check(void)
|
||||
{
|
||||
struct gpio_private *priv = alarmlist;
|
||||
unsigned long data = 0;
|
||||
int ret = 0;
|
||||
while (priv) {
|
||||
data = *data_in[priv->minor];
|
||||
if ((data & priv->highalarm) ||
|
||||
(~data & priv->lowalarm)) {
|
||||
DP(printk("etrax_gpio_wake_up_check %i\n",priv->minor));
|
||||
wake_up_interruptible(&priv->alarm_wq);
|
||||
ret = 1;
|
||||
}
|
||||
priv = priv->next;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t
|
||||
gpio_poll_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
|
||||
{
|
||||
if (gpio_some_alarms) {
|
||||
return IRQ_RETVAL(etrax_gpio_wake_up_check());
|
||||
}
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static irqreturn_t
|
||||
gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs)
|
||||
{
|
||||
reg_gio_rw_intr_mask intr_mask;
|
||||
reg_gio_r_masked_intr masked_intr;
|
||||
reg_gio_rw_ack_intr ack_intr;
|
||||
unsigned long tmp;
|
||||
unsigned long tmp2;
|
||||
|
||||
/* Find what PA interrupts are active */
|
||||
masked_intr = REG_RD(gio, regi_gio, r_masked_intr);
|
||||
tmp = REG_TYPE_CONV(unsigned long, reg_gio_r_masked_intr, masked_intr);
|
||||
|
||||
/* Find those that we have enabled */
|
||||
spin_lock(&alarm_lock);
|
||||
tmp &= (gpio_pa_high_alarms | gpio_pa_low_alarms);
|
||||
spin_unlock(&alarm_lock);
|
||||
|
||||
/* Ack them */
|
||||
ack_intr = REG_TYPE_CONV(reg_gio_rw_ack_intr, unsigned long, tmp);
|
||||
REG_WR(gio, regi_gio, rw_ack_intr, ack_intr);
|
||||
|
||||
/* Disable those interrupts.. */
|
||||
intr_mask = REG_RD(gio, regi_gio, rw_intr_mask);
|
||||
tmp2 = REG_TYPE_CONV(unsigned long, reg_gio_rw_intr_mask, intr_mask);
|
||||
tmp2 &= ~tmp;
|
||||
intr_mask = REG_TYPE_CONV(reg_gio_rw_intr_mask, unsigned long, tmp2);
|
||||
REG_WR(gio, regi_gio, rw_intr_mask, intr_mask);
|
||||
|
||||
if (gpio_some_alarms) {
|
||||
return IRQ_RETVAL(etrax_gpio_wake_up_check());
|
||||
}
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
|
||||
static ssize_t gpio_write(struct file * file, const char * buf, size_t count,
|
||||
loff_t *off)
|
||||
{
|
||||
struct gpio_private *priv = (struct gpio_private *)file->private_data;
|
||||
unsigned char data, clk_mask, data_mask, write_msb;
|
||||
unsigned long flags;
|
||||
unsigned long shadow;
|
||||
volatile unsigned long *port;
|
||||
ssize_t retval = count;
|
||||
/* Only bits 0-7 may be used for write operations but allow all
|
||||
devices except leds... */
|
||||
if (priv->minor == GPIO_MINOR_LEDS) {
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (!access_ok(VERIFY_READ, buf, count)) {
|
||||
return -EFAULT;
|
||||
}
|
||||
clk_mask = priv->clk_mask;
|
||||
data_mask = priv->data_mask;
|
||||
/* It must have been configured using the IO_CFG_WRITE_MODE */
|
||||
/* Perhaps a better error code? */
|
||||
if (clk_mask == 0 || data_mask == 0) {
|
||||
return -EPERM;
|
||||
}
|
||||
write_msb = priv->write_msb;
|
||||
D(printk("gpio_write: %lu to data 0x%02X clk 0x%02X msb: %i\n",count, data_mask, clk_mask, write_msb));
|
||||
port = data_out[priv->minor];
|
||||
|
||||
while (count--) {
|
||||
int i;
|
||||
data = *buf++;
|
||||
if (priv->write_msb) {
|
||||
for (i = 7; i >= 0;i--) {
|
||||
local_irq_save(flags);
|
||||
shadow = *port;
|
||||
*port = shadow &= ~clk_mask;
|
||||
if (data & 1<<i)
|
||||
*port = shadow |= data_mask;
|
||||
else
|
||||
*port = shadow &= ~data_mask;
|
||||
/* For FPGA: min 5.0ns (DCC) before CCLK high */
|
||||
*port = shadow |= clk_mask;
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i <= 7;i++) {
|
||||
local_irq_save(flags);
|
||||
shadow = *port;
|
||||
*port = shadow &= ~clk_mask;
|
||||
if (data & 1<<i)
|
||||
*port = shadow |= data_mask;
|
||||
else
|
||||
*port = shadow &= ~data_mask;
|
||||
/* For FPGA: min 5.0ns (DCC) before CCLK high */
|
||||
*port = shadow |= clk_mask;
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int
|
||||
gpio_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct gpio_private *priv;
|
||||
int p = MINOR(inode->i_rdev);
|
||||
|
||||
if (p > GPIO_MINOR_LAST)
|
||||
return -EINVAL;
|
||||
|
||||
priv = (struct gpio_private *)kmalloc(sizeof(struct gpio_private),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->minor = p;
|
||||
|
||||
/* initialize the io/alarm struct and link it into our alarmlist */
|
||||
|
||||
priv->next = alarmlist;
|
||||
alarmlist = priv;
|
||||
priv->clk_mask = 0;
|
||||
priv->data_mask = 0;
|
||||
priv->highalarm = 0;
|
||||
priv->lowalarm = 0;
|
||||
init_waitqueue_head(&priv->alarm_wq);
|
||||
|
||||
filp->private_data = (void *)priv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
gpio_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct gpio_private *p = alarmlist;
|
||||
struct gpio_private *todel = (struct gpio_private *)filp->private_data;
|
||||
/* local copies while updating them: */
|
||||
unsigned long a_high, a_low;
|
||||
unsigned long some_alarms;
|
||||
|
||||
/* unlink from alarmlist and free the private structure */
|
||||
|
||||
if (p == todel) {
|
||||
alarmlist = todel->next;
|
||||
} else {
|
||||
while (p->next != todel)
|
||||
p = p->next;
|
||||
p->next = todel->next;
|
||||
}
|
||||
|
||||
kfree(todel);
|
||||
/* Check if there are still any alarms set */
|
||||
p = alarmlist;
|
||||
some_alarms = 0;
|
||||
a_high = 0;
|
||||
a_low = 0;
|
||||
while (p) {
|
||||
if (p->minor == GPIO_MINOR_A) {
|
||||
a_high |= p->highalarm;
|
||||
a_low |= p->lowalarm;
|
||||
}
|
||||
|
||||
if (p->highalarm | p->lowalarm) {
|
||||
some_alarms = 1;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
spin_lock(&alarm_lock);
|
||||
gpio_some_alarms = some_alarms;
|
||||
gpio_pa_high_alarms = a_high;
|
||||
gpio_pa_low_alarms = a_low;
|
||||
spin_unlock(&alarm_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Main device API. ioctl's to read/set/clear bits, as well as to
|
||||
* set alarms to wait for using a subsequent select().
|
||||
*/
|
||||
|
||||
unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg)
|
||||
{
|
||||
/* Set direction 0=unchanged 1=input,
|
||||
* return mask with 1=input
|
||||
*/
|
||||
unsigned long flags;
|
||||
unsigned long dir_shadow;
|
||||
|
||||
local_irq_save(flags);
|
||||
dir_shadow = *dir_oe[priv->minor];
|
||||
dir_shadow &= ~(arg & changeable_dir[priv->minor]);
|
||||
*dir_oe[priv->minor] = dir_shadow;
|
||||
local_irq_restore(flags);
|
||||
|
||||
if (priv->minor == GPIO_MINOR_A)
|
||||
dir_shadow ^= 0xFF; /* Only 8 bits */
|
||||
else
|
||||
dir_shadow ^= 0x3FFFF; /* Only 18 bits */
|
||||
return dir_shadow;
|
||||
|
||||
} /* setget_input */
|
||||
|
||||
unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long dir_shadow;
|
||||
|
||||
local_irq_save(flags);
|
||||
dir_shadow = *dir_oe[priv->minor];
|
||||
dir_shadow |= (arg & changeable_dir[priv->minor]);
|
||||
*dir_oe[priv->minor] = dir_shadow;
|
||||
local_irq_restore(flags);
|
||||
return dir_shadow;
|
||||
} /* setget_output */
|
||||
|
||||
static int
|
||||
gpio_leds_ioctl(unsigned int cmd, unsigned long arg);
|
||||
|
||||
static int
|
||||
gpio_ioctl(struct inode *inode, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long val;
|
||||
unsigned long shadow;
|
||||
struct gpio_private *priv = (struct gpio_private *)file->private_data;
|
||||
if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (_IOC_NR(cmd)) {
|
||||
case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */
|
||||
// read the port
|
||||
return *data_in[priv->minor];
|
||||
break;
|
||||
case IO_SETBITS:
|
||||
local_irq_save(flags);
|
||||
if (arg & 0x04)
|
||||
printk("GPIO SET 2\n");
|
||||
// set changeable bits with a 1 in arg
|
||||
shadow = *data_out[priv->minor];
|
||||
shadow |= (arg & changeable_bits[priv->minor]);
|
||||
*data_out[priv->minor] = shadow;
|
||||
local_irq_restore(flags);
|
||||
break;
|
||||
case IO_CLRBITS:
|
||||
local_irq_save(flags);
|
||||
if (arg & 0x04)
|
||||
printk("GPIO CLR 2\n");
|
||||
// clear changeable bits with a 1 in arg
|
||||
shadow = *data_out[priv->minor];
|
||||
shadow &= ~(arg & changeable_bits[priv->minor]);
|
||||
*data_out[priv->minor] = shadow;
|
||||
local_irq_restore(flags);
|
||||
break;
|
||||
case IO_HIGHALARM:
|
||||
// set alarm when bits with 1 in arg go high
|
||||
priv->highalarm |= arg;
|
||||
spin_lock(&alarm_lock);
|
||||
gpio_some_alarms = 1;
|
||||
if (priv->minor == GPIO_MINOR_A) {
|
||||
gpio_pa_high_alarms |= arg;
|
||||
}
|
||||
spin_unlock(&alarm_lock);
|
||||
break;
|
||||
case IO_LOWALARM:
|
||||
// set alarm when bits with 1 in arg go low
|
||||
priv->lowalarm |= arg;
|
||||
spin_lock(&alarm_lock);
|
||||
gpio_some_alarms = 1;
|
||||
if (priv->minor == GPIO_MINOR_A) {
|
||||
gpio_pa_low_alarms |= arg;
|
||||
}
|
||||
spin_unlock(&alarm_lock);
|
||||
break;
|
||||
case IO_CLRALARM:
|
||||
// clear alarm for bits with 1 in arg
|
||||
priv->highalarm &= ~arg;
|
||||
priv->lowalarm &= ~arg;
|
||||
spin_lock(&alarm_lock);
|
||||
if (priv->minor == GPIO_MINOR_A) {
|
||||
if (gpio_pa_high_alarms & arg ||
|
||||
gpio_pa_low_alarms & arg) {
|
||||
/* Must update the gpio_pa_*alarms masks */
|
||||
}
|
||||
}
|
||||
spin_unlock(&alarm_lock);
|
||||
break;
|
||||
case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */
|
||||
/* Read direction 0=input 1=output */
|
||||
return *dir_oe[priv->minor];
|
||||
case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */
|
||||
/* Set direction 0=unchanged 1=input,
|
||||
* return mask with 1=input
|
||||
*/
|
||||
return setget_input(priv, arg);
|
||||
break;
|
||||
case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */
|
||||
/* Set direction 0=unchanged 1=output,
|
||||
* return mask with 1=output
|
||||
*/
|
||||
return setget_output(priv, arg);
|
||||
|
||||
case IO_CFG_WRITE_MODE:
|
||||
{
|
||||
unsigned long dir_shadow;
|
||||
dir_shadow = *dir_oe[priv->minor];
|
||||
|
||||
priv->clk_mask = arg & 0xFF;
|
||||
priv->data_mask = (arg >> 8) & 0xFF;
|
||||
priv->write_msb = (arg >> 16) & 0x01;
|
||||
/* Check if we're allowed to change the bits and
|
||||
* the direction is correct
|
||||
*/
|
||||
if (!((priv->clk_mask & changeable_bits[priv->minor]) &&
|
||||
(priv->data_mask & changeable_bits[priv->minor]) &&
|
||||
(priv->clk_mask & dir_shadow) &&
|
||||
(priv->data_mask & dir_shadow)))
|
||||
{
|
||||
priv->clk_mask = 0;
|
||||
priv->data_mask = 0;
|
||||
return -EPERM;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IO_READ_INBITS:
|
||||
/* *arg is result of reading the input pins */
|
||||
val = *data_in[priv->minor];
|
||||
if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
break;
|
||||
case IO_READ_OUTBITS:
|
||||
/* *arg is result of reading the output shadow */
|
||||
val = *data_out[priv->minor];
|
||||
if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
case IO_SETGET_INPUT:
|
||||
/* bits set in *arg is set to input,
|
||||
* *arg updated with current input pins.
|
||||
*/
|
||||
if (copy_from_user(&val, (unsigned long*)arg, sizeof(val)))
|
||||
return -EFAULT;
|
||||
val = setget_input(priv, val);
|
||||
if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
case IO_SETGET_OUTPUT:
|
||||
/* bits set in *arg is set to output,
|
||||
* *arg updated with current output pins.
|
||||
*/
|
||||
if (copy_from_user(&val, (unsigned long*)arg, sizeof(val)))
|
||||
return -EFAULT;
|
||||
val = setget_output(priv, val);
|
||||
if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
default:
|
||||
if (priv->minor == GPIO_MINOR_LEDS)
|
||||
return gpio_leds_ioctl(cmd, arg);
|
||||
else
|
||||
return -EINVAL;
|
||||
} /* switch */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
gpio_leds_ioctl(unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
unsigned char green;
|
||||
unsigned char red;
|
||||
|
||||
switch (_IOC_NR(cmd)) {
|
||||
case IO_LEDACTIVE_SET:
|
||||
green = ((unsigned char) arg) & 1;
|
||||
red = (((unsigned char) arg) >> 1) & 1;
|
||||
LED_ACTIVE_SET_G(green);
|
||||
LED_ACTIVE_SET_R(red);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
} /* switch */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct file_operations gpio_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.poll = gpio_poll,
|
||||
.ioctl = gpio_ioctl,
|
||||
.write = gpio_write,
|
||||
.open = gpio_open,
|
||||
.release = gpio_release,
|
||||
};
|
||||
|
||||
|
||||
/* main driver initialization routine, called from mem.c */
|
||||
|
||||
static __init int
|
||||
gpio_init(void)
|
||||
{
|
||||
int res;
|
||||
reg_intr_vect_rw_mask intr_mask;
|
||||
|
||||
/* do the formalities */
|
||||
|
||||
res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops);
|
||||
if (res < 0) {
|
||||
printk(KERN_ERR "gpio: couldn't get a major number.\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Clear all leds */
|
||||
LED_NETWORK_SET(0);
|
||||
LED_ACTIVE_SET(0);
|
||||
LED_DISK_READ(0);
|
||||
LED_DISK_WRITE(0);
|
||||
|
||||
printk("ETRAX FS GPIO driver v2.5, (c) 2003-2005 Axis Communications AB\n");
|
||||
/* We call etrax_gpio_wake_up_check() from timer interrupt and
|
||||
* from cpu_idle() in kernel/process.c
|
||||
* The check in cpu_idle() reduces latency from ~15 ms to ~6 ms
|
||||
* in some tests.
|
||||
*/
|
||||
if (request_irq(TIMER_INTR_VECT, gpio_poll_timer_interrupt,
|
||||
SA_SHIRQ | SA_INTERRUPT,"gpio poll", &alarmlist)) {
|
||||
printk("err: timer0 irq for gpio\n");
|
||||
}
|
||||
if (request_irq(GEN_IO_INTR_VECT, gpio_pa_interrupt,
|
||||
SA_SHIRQ | SA_INTERRUPT,"gpio PA", &alarmlist)) {
|
||||
printk("err: PA irq for gpio\n");
|
||||
}
|
||||
/* enable the gio and timer irq in global config */
|
||||
intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
|
||||
intr_mask.timer = 1;
|
||||
intr_mask.gen_io = 1;
|
||||
REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* this makes sure that gpio_init is called during kernel boot */
|
||||
|
||||
module_init(gpio_init);
|
|
@ -0,0 +1,611 @@
|
|||
/*!***************************************************************************
|
||||
*!
|
||||
*! FILE NAME : i2c.c
|
||||
*!
|
||||
*! DESCRIPTION: implements an interface for IIC/I2C, both directly from other
|
||||
*! kernel modules (i2c_writereg/readreg) and from userspace using
|
||||
*! ioctl()'s
|
||||
*!
|
||||
*! Nov 30 1998 Torbjorn Eliasson Initial version.
|
||||
*! Bjorn Wesen Elinux kernel version.
|
||||
*! Jan 14 2000 Johan Adolfsson Fixed PB shadow register stuff -
|
||||
*! don't use PB_I2C if DS1302 uses same bits,
|
||||
*! use PB.
|
||||
*| June 23 2003 Pieter Grimmerink Added 'i2c_sendnack'. i2c_readreg now
|
||||
*| generates nack on last received byte,
|
||||
*| instead of ack.
|
||||
*| i2c_getack changed data level while clock
|
||||
*| was high, causing DS75 to see a stop condition
|
||||
*!
|
||||
*! ---------------------------------------------------------------------------
|
||||
*!
|
||||
*! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN
|
||||
*!
|
||||
*!***************************************************************************/
|
||||
/* $Id: i2c.c,v 1.2 2005/05/09 15:29:49 starvik Exp $ */
|
||||
/****************** INCLUDE FILES SECTION ***********************************/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/config.h>
|
||||
|
||||
#include <asm/etraxi2c.h>
|
||||
|
||||
#include <asm/system.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/delay.h>
|
||||
|
||||
#include "i2c.h"
|
||||
|
||||
/****************** I2C DEFINITION SECTION *************************/
|
||||
|
||||
#define D(x)
|
||||
|
||||
#define I2C_MAJOR 123 /* LOCAL/EXPERIMENTAL */
|
||||
static const char i2c_name[] = "i2c";
|
||||
|
||||
#define CLOCK_LOW_TIME 8
|
||||
#define CLOCK_HIGH_TIME 8
|
||||
#define START_CONDITION_HOLD_TIME 8
|
||||
#define STOP_CONDITION_HOLD_TIME 8
|
||||
#define ENABLE_OUTPUT 0x01
|
||||
#define ENABLE_INPUT 0x00
|
||||
#define I2C_CLOCK_HIGH 1
|
||||
#define I2C_CLOCK_LOW 0
|
||||
#define I2C_DATA_HIGH 1
|
||||
#define I2C_DATA_LOW 0
|
||||
|
||||
#define i2c_enable()
|
||||
#define i2c_disable()
|
||||
|
||||
/* enable or disable output-enable, to select output or input on the i2c bus */
|
||||
|
||||
#define i2c_dir_out() crisv32_io_set_dir(&cris_i2c_data, crisv32_io_dir_out)
|
||||
#define i2c_dir_in() crisv32_io_set_dir(&cris_i2c_data, crisv32_io_dir_in)
|
||||
|
||||
/* control the i2c clock and data signals */
|
||||
|
||||
#define i2c_clk(x) crisv32_io_set(&cris_i2c_clk, x)
|
||||
#define i2c_data(x) crisv32_io_set(&cris_i2c_data, x)
|
||||
|
||||
/* read a bit from the i2c interface */
|
||||
|
||||
#define i2c_getbit() crisv32_io_rd(&cris_i2c_data)
|
||||
|
||||
#define i2c_delay(usecs) udelay(usecs)
|
||||
|
||||
/****************** VARIABLE SECTION ************************************/
|
||||
|
||||
static struct crisv32_iopin cris_i2c_clk;
|
||||
static struct crisv32_iopin cris_i2c_data;
|
||||
|
||||
/****************** FUNCTION DEFINITION SECTION *************************/
|
||||
|
||||
|
||||
/* generate i2c start condition */
|
||||
|
||||
void
|
||||
i2c_start(void)
|
||||
{
|
||||
/*
|
||||
* SCL=1 SDA=1
|
||||
*/
|
||||
i2c_dir_out();
|
||||
i2c_delay(CLOCK_HIGH_TIME/6);
|
||||
i2c_data(I2C_DATA_HIGH);
|
||||
i2c_clk(I2C_CLOCK_HIGH);
|
||||
i2c_delay(CLOCK_HIGH_TIME);
|
||||
/*
|
||||
* SCL=1 SDA=0
|
||||
*/
|
||||
i2c_data(I2C_DATA_LOW);
|
||||
i2c_delay(START_CONDITION_HOLD_TIME);
|
||||
/*
|
||||
* SCL=0 SDA=0
|
||||
*/
|
||||
i2c_clk(I2C_CLOCK_LOW);
|
||||
i2c_delay(CLOCK_LOW_TIME);
|
||||
}
|
||||
|
||||
/* generate i2c stop condition */
|
||||
|
||||
void
|
||||
i2c_stop(void)
|
||||
{
|
||||
i2c_dir_out();
|
||||
|
||||
/*
|
||||
* SCL=0 SDA=0
|
||||
*/
|
||||
i2c_clk(I2C_CLOCK_LOW);
|
||||
i2c_data(I2C_DATA_LOW);
|
||||
i2c_delay(CLOCK_LOW_TIME*2);
|
||||
/*
|
||||
* SCL=1 SDA=0
|
||||
*/
|
||||
i2c_clk(I2C_CLOCK_HIGH);
|
||||
i2c_delay(CLOCK_HIGH_TIME*2);
|
||||
/*
|
||||
* SCL=1 SDA=1
|
||||
*/
|
||||
i2c_data(I2C_DATA_HIGH);
|
||||
i2c_delay(STOP_CONDITION_HOLD_TIME);
|
||||
|
||||
i2c_dir_in();
|
||||
}
|
||||
|
||||
/* write a byte to the i2c interface */
|
||||
|
||||
void
|
||||
i2c_outbyte(unsigned char x)
|
||||
{
|
||||
int i;
|
||||
|
||||
i2c_dir_out();
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (x & 0x80) {
|
||||
i2c_data(I2C_DATA_HIGH);
|
||||
} else {
|
||||
i2c_data(I2C_DATA_LOW);
|
||||
}
|
||||
|
||||
i2c_delay(CLOCK_LOW_TIME/2);
|
||||
i2c_clk(I2C_CLOCK_HIGH);
|
||||
i2c_delay(CLOCK_HIGH_TIME);
|
||||
i2c_clk(I2C_CLOCK_LOW);
|
||||
i2c_delay(CLOCK_LOW_TIME/2);
|
||||
x <<= 1;
|
||||
}
|
||||
i2c_data(I2C_DATA_LOW);
|
||||
i2c_delay(CLOCK_LOW_TIME/2);
|
||||
|
||||
/*
|
||||
* enable input
|
||||
*/
|
||||
i2c_dir_in();
|
||||
}
|
||||
|
||||
/* read a byte from the i2c interface */
|
||||
|
||||
unsigned char
|
||||
i2c_inbyte(void)
|
||||
{
|
||||
unsigned char aBitByte = 0;
|
||||
int i;
|
||||
|
||||
/* Switch off I2C to get bit */
|
||||
i2c_disable();
|
||||
i2c_dir_in();
|
||||
i2c_delay(CLOCK_HIGH_TIME/2);
|
||||
|
||||
/* Get bit */
|
||||
aBitByte |= i2c_getbit();
|
||||
|
||||
/* Enable I2C */
|
||||
i2c_enable();
|
||||
i2c_delay(CLOCK_LOW_TIME/2);
|
||||
|
||||
for (i = 1; i < 8; i++) {
|
||||
aBitByte <<= 1;
|
||||
/* Clock pulse */
|
||||
i2c_clk(I2C_CLOCK_HIGH);
|
||||
i2c_delay(CLOCK_HIGH_TIME);
|
||||
i2c_clk(I2C_CLOCK_LOW);
|
||||
i2c_delay(CLOCK_LOW_TIME);
|
||||
|
||||
/* Switch off I2C to get bit */
|
||||
i2c_disable();
|
||||
i2c_dir_in();
|
||||
i2c_delay(CLOCK_HIGH_TIME/2);
|
||||
|
||||
/* Get bit */
|
||||
aBitByte |= i2c_getbit();
|
||||
|
||||
/* Enable I2C */
|
||||
i2c_enable();
|
||||
i2c_delay(CLOCK_LOW_TIME/2);
|
||||
}
|
||||
i2c_clk(I2C_CLOCK_HIGH);
|
||||
i2c_delay(CLOCK_HIGH_TIME);
|
||||
|
||||
/*
|
||||
* we leave the clock low, getbyte is usually followed
|
||||
* by sendack/nack, they assume the clock to be low
|
||||
*/
|
||||
i2c_clk(I2C_CLOCK_LOW);
|
||||
return aBitByte;
|
||||
}
|
||||
|
||||
/*#---------------------------------------------------------------------------
|
||||
*#
|
||||
*# FUNCTION NAME: i2c_getack
|
||||
*#
|
||||
*# DESCRIPTION : checks if ack was received from ic2
|
||||
*#
|
||||
*#--------------------------------------------------------------------------*/
|
||||
|
||||
int
|
||||
i2c_getack(void)
|
||||
{
|
||||
int ack = 1;
|
||||
/*
|
||||
* enable output
|
||||
*/
|
||||
i2c_dir_out();
|
||||
/*
|
||||
* Release data bus by setting
|
||||
* data high
|
||||
*/
|
||||
i2c_data(I2C_DATA_HIGH);
|
||||
/*
|
||||
* enable input
|
||||
*/
|
||||
i2c_dir_in();
|
||||
i2c_delay(CLOCK_HIGH_TIME/4);
|
||||
/*
|
||||
* generate ACK clock pulse
|
||||
*/
|
||||
i2c_clk(I2C_CLOCK_HIGH);
|
||||
/*
|
||||
* Use PORT PB instead of I2C
|
||||
* for input. (I2C not working)
|
||||
*/
|
||||
i2c_clk(1);
|
||||
i2c_data(1);
|
||||
/*
|
||||
* switch off I2C
|
||||
*/
|
||||
i2c_data(1);
|
||||
i2c_disable();
|
||||
i2c_dir_in();
|
||||
/*
|
||||
* now wait for ack
|
||||
*/
|
||||
i2c_delay(CLOCK_HIGH_TIME/2);
|
||||
/*
|
||||
* check for ack
|
||||
*/
|
||||
if(i2c_getbit())
|
||||
ack = 0;
|
||||
i2c_delay(CLOCK_HIGH_TIME/2);
|
||||
if(!ack){
|
||||
if(!i2c_getbit()) /* receiver pulld SDA low */
|
||||
ack = 1;
|
||||
i2c_delay(CLOCK_HIGH_TIME/2);
|
||||
}
|
||||
|
||||
/*
|
||||
* our clock is high now, make sure data is low
|
||||
* before we enable our output. If we keep data high
|
||||
* and enable output, we would generate a stop condition.
|
||||
*/
|
||||
i2c_data(I2C_DATA_LOW);
|
||||
|
||||
/*
|
||||
* end clock pulse
|
||||
*/
|
||||
i2c_enable();
|
||||
i2c_dir_out();
|
||||
i2c_clk(I2C_CLOCK_LOW);
|
||||
i2c_delay(CLOCK_HIGH_TIME/4);
|
||||
/*
|
||||
* enable output
|
||||
*/
|
||||
i2c_dir_out();
|
||||
/*
|
||||
* remove ACK clock pulse
|
||||
*/
|
||||
i2c_data(I2C_DATA_HIGH);
|
||||
i2c_delay(CLOCK_LOW_TIME/2);
|
||||
return ack;
|
||||
}
|
||||
|
||||
/*#---------------------------------------------------------------------------
|
||||
*#
|
||||
*# FUNCTION NAME: I2C::sendAck
|
||||
*#
|
||||
*# DESCRIPTION : Send ACK on received data
|
||||
*#
|
||||
*#--------------------------------------------------------------------------*/
|
||||
void
|
||||
i2c_sendack(void)
|
||||
{
|
||||
/*
|
||||
* enable output
|
||||
*/
|
||||
i2c_delay(CLOCK_LOW_TIME);
|
||||
i2c_dir_out();
|
||||
/*
|
||||
* set ack pulse high
|
||||
*/
|
||||
i2c_data(I2C_DATA_LOW);
|
||||
/*
|
||||
* generate clock pulse
|
||||
*/
|
||||
i2c_delay(CLOCK_HIGH_TIME/6);
|
||||
i2c_clk(I2C_CLOCK_HIGH);
|
||||
i2c_delay(CLOCK_HIGH_TIME);
|
||||
i2c_clk(I2C_CLOCK_LOW);
|
||||
i2c_delay(CLOCK_LOW_TIME/6);
|
||||
/*
|
||||
* reset data out
|
||||
*/
|
||||
i2c_data(I2C_DATA_HIGH);
|
||||
i2c_delay(CLOCK_LOW_TIME);
|
||||
|
||||
i2c_dir_in();
|
||||
}
|
||||
|
||||
/*#---------------------------------------------------------------------------
|
||||
*#
|
||||
*# FUNCTION NAME: i2c_sendnack
|
||||
*#
|
||||
*# DESCRIPTION : Sends NACK on received data
|
||||
*#
|
||||
*#--------------------------------------------------------------------------*/
|
||||
void
|
||||
i2c_sendnack(void)
|
||||
{
|
||||
/*
|
||||
* enable output
|
||||
*/
|
||||
i2c_delay(CLOCK_LOW_TIME);
|
||||
i2c_dir_out();
|
||||
/*
|
||||
* set data high
|
||||
*/
|
||||
i2c_data(I2C_DATA_HIGH);
|
||||
/*
|
||||
* generate clock pulse
|
||||
*/
|
||||
i2c_delay(CLOCK_HIGH_TIME/6);
|
||||
i2c_clk(I2C_CLOCK_HIGH);
|
||||
i2c_delay(CLOCK_HIGH_TIME);
|
||||
i2c_clk(I2C_CLOCK_LOW);
|
||||
i2c_delay(CLOCK_LOW_TIME);
|
||||
|
||||
i2c_dir_in();
|
||||
}
|
||||
|
||||
/*#---------------------------------------------------------------------------
|
||||
*#
|
||||
*# FUNCTION NAME: i2c_writereg
|
||||
*#
|
||||
*# DESCRIPTION : Writes a value to an I2C device
|
||||
*#
|
||||
*#--------------------------------------------------------------------------*/
|
||||
int
|
||||
i2c_writereg(unsigned char theSlave, unsigned char theReg,
|
||||
unsigned char theValue)
|
||||
{
|
||||
int error, cntr = 3;
|
||||
unsigned long flags;
|
||||
|
||||
do {
|
||||
error = 0;
|
||||
/*
|
||||
* we don't like to be interrupted
|
||||
*/
|
||||
local_irq_save(flags);
|
||||
|
||||
i2c_start();
|
||||
/*
|
||||
* send slave address
|
||||
*/
|
||||
i2c_outbyte((theSlave & 0xfe));
|
||||
/*
|
||||
* wait for ack
|
||||
*/
|
||||
if(!i2c_getack())
|
||||
error = 1;
|
||||
/*
|
||||
* now select register
|
||||
*/
|
||||
i2c_dir_out();
|
||||
i2c_outbyte(theReg);
|
||||
/*
|
||||
* now it's time to wait for ack
|
||||
*/
|
||||
if(!i2c_getack())
|
||||
error |= 2;
|
||||
/*
|
||||
* send register register data
|
||||
*/
|
||||
i2c_outbyte(theValue);
|
||||
/*
|
||||
* now it's time to wait for ack
|
||||
*/
|
||||
if(!i2c_getack())
|
||||
error |= 4;
|
||||
/*
|
||||
* end byte stream
|
||||
*/
|
||||
i2c_stop();
|
||||
/*
|
||||
* enable interrupt again
|
||||
*/
|
||||
local_irq_restore(flags);
|
||||
|
||||
} while(error && cntr--);
|
||||
|
||||
i2c_delay(CLOCK_LOW_TIME);
|
||||
|
||||
return -error;
|
||||
}
|
||||
|
||||
/*#---------------------------------------------------------------------------
|
||||
*#
|
||||
*# FUNCTION NAME: i2c_readreg
|
||||
*#
|
||||
*# DESCRIPTION : Reads a value from the decoder registers.
|
||||
*#
|
||||
*#--------------------------------------------------------------------------*/
|
||||
unsigned char
|
||||
i2c_readreg(unsigned char theSlave, unsigned char theReg)
|
||||
{
|
||||
unsigned char b = 0;
|
||||
int error, cntr = 3;
|
||||
unsigned long flags;
|
||||
|
||||
do {
|
||||
error = 0;
|
||||
/*
|
||||
* we don't like to be interrupted
|
||||
*/
|
||||
local_irq_save(flags);
|
||||
/*
|
||||
* generate start condition
|
||||
*/
|
||||
i2c_start();
|
||||
|
||||
/*
|
||||
* send slave address
|
||||
*/
|
||||
i2c_outbyte((theSlave & 0xfe));
|
||||
/*
|
||||
* wait for ack
|
||||
*/
|
||||
if(!i2c_getack())
|
||||
error = 1;
|
||||
/*
|
||||
* now select register
|
||||
*/
|
||||
i2c_dir_out();
|
||||
i2c_outbyte(theReg);
|
||||
/*
|
||||
* now it's time to wait for ack
|
||||
*/
|
||||
if(!i2c_getack())
|
||||
error = 1;
|
||||
/*
|
||||
* repeat start condition
|
||||
*/
|
||||
i2c_delay(CLOCK_LOW_TIME);
|
||||
i2c_start();
|
||||
/*
|
||||
* send slave address
|
||||
*/
|
||||
i2c_outbyte(theSlave | 0x01);
|
||||
/*
|
||||
* wait for ack
|
||||
*/
|
||||
if(!i2c_getack())
|
||||
error = 1;
|
||||
/*
|
||||
* fetch register
|
||||
*/
|
||||
b = i2c_inbyte();
|
||||
/*
|
||||
* last received byte needs to be nacked
|
||||
* instead of acked
|
||||
*/
|
||||
i2c_sendnack();
|
||||
/*
|
||||
* end sequence
|
||||
*/
|
||||
i2c_stop();
|
||||
/*
|
||||
* enable interrupt again
|
||||
*/
|
||||
local_irq_restore(flags);
|
||||
|
||||
} while(error && cntr--);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
static int
|
||||
i2c_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
i2c_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Main device API. ioctl's to write or read to/from i2c registers.
|
||||
*/
|
||||
|
||||
static int
|
||||
i2c_ioctl(struct inode *inode, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
if(_IOC_TYPE(cmd) != ETRAXI2C_IOCTYPE) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (_IOC_NR(cmd)) {
|
||||
case I2C_WRITEREG:
|
||||
/* write to an i2c slave */
|
||||
D(printk("i2cw %d %d %d\n",
|
||||
I2C_ARGSLAVE(arg),
|
||||
I2C_ARGREG(arg),
|
||||
I2C_ARGVALUE(arg)));
|
||||
|
||||
return i2c_writereg(I2C_ARGSLAVE(arg),
|
||||
I2C_ARGREG(arg),
|
||||
I2C_ARGVALUE(arg));
|
||||
case I2C_READREG:
|
||||
{
|
||||
unsigned char val;
|
||||
/* read from an i2c slave */
|
||||
D(printk("i2cr %d %d ",
|
||||
I2C_ARGSLAVE(arg),
|
||||
I2C_ARGREG(arg)));
|
||||
val = i2c_readreg(I2C_ARGSLAVE(arg), I2C_ARGREG(arg));
|
||||
D(printk("= %d\n", val));
|
||||
return val;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct file_operations i2c_fops = {
|
||||
owner: THIS_MODULE,
|
||||
ioctl: i2c_ioctl,
|
||||
open: i2c_open,
|
||||
release: i2c_release,
|
||||
};
|
||||
|
||||
int __init
|
||||
i2c_init(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
/* Setup and enable the Port B I2C interface */
|
||||
|
||||
crisv32_io_get_name(&cris_i2c_data, CONFIG_ETRAX_I2C_DATA_PORT);
|
||||
crisv32_io_get_name(&cris_i2c_clk, CONFIG_ETRAX_I2C_CLK_PORT);
|
||||
|
||||
/* register char device */
|
||||
|
||||
res = register_chrdev(I2C_MAJOR, i2c_name, &i2c_fops);
|
||||
if(res < 0) {
|
||||
printk(KERN_ERR "i2c: couldn't get a major number.\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "I2C driver v2.2, (c) 1999-2001 Axis Communications AB\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* this makes sure that i2c_init is called during boot */
|
||||
|
||||
module_init(i2c_init);
|
||||
|
||||
/****************** END OF FILE i2c.c ********************************/
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
#include <linux/init.h>
|
||||
|
||||
/* High level I2C actions */
|
||||
int __init i2c_init(void);
|
||||
int i2c_writereg(unsigned char theSlave, unsigned char theReg, unsigned char theValue);
|
||||
unsigned char i2c_readreg(unsigned char theSlave, unsigned char theReg);
|
||||
|
||||
/* Low level I2C */
|
||||
void i2c_start(void);
|
||||
void i2c_stop(void);
|
||||
void i2c_outbyte(unsigned char x);
|
||||
unsigned char i2c_inbyte(void);
|
||||
int i2c_getack(void);
|
||||
void i2c_sendack(void);
|
|
@ -0,0 +1,219 @@
|
|||
/* $Id: iop_fw_load.c,v 1.4 2005/04/07 09:27:46 larsv Exp $
|
||||
*
|
||||
* Firmware loader for ETRAX FS IO-Processor
|
||||
*
|
||||
* Copyright (C) 2004 Axis Communications AB
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/firmware.h>
|
||||
|
||||
#include <asm/arch/hwregs/reg_map.h>
|
||||
#include <asm/arch/hwregs/iop/iop_reg_space.h>
|
||||
#include <asm/arch/hwregs/iop/iop_mpu_macros.h>
|
||||
#include <asm/arch/hwregs/iop/iop_mpu_defs.h>
|
||||
#include <asm/arch/hwregs/iop/iop_spu_defs.h>
|
||||
#include <asm/arch/hwregs/iop/iop_sw_cpu_defs.h>
|
||||
|
||||
#define IOP_TIMEOUT 100
|
||||
|
||||
static struct device iop_spu_device[2] = {
|
||||
{ .bus_id = "iop-spu0", },
|
||||
{ .bus_id = "iop-spu1", },
|
||||
};
|
||||
|
||||
static struct device iop_mpu_device = {
|
||||
.bus_id = "iop-mpu",
|
||||
};
|
||||
|
||||
static int wait_mpu_idle(void)
|
||||
{
|
||||
reg_iop_mpu_r_stat mpu_stat;
|
||||
unsigned int timeout = IOP_TIMEOUT;
|
||||
|
||||
do {
|
||||
mpu_stat = REG_RD(iop_mpu, regi_iop_mpu, r_stat);
|
||||
} while (mpu_stat.instr_reg_busy == regk_iop_mpu_yes && --timeout > 0);
|
||||
if (timeout == 0) {
|
||||
printk(KERN_ERR "Timeout waiting for MPU to be idle\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iop_fw_load_spu(const unsigned char *fw_name, unsigned int spu_inst)
|
||||
{
|
||||
reg_iop_sw_cpu_rw_mc_ctrl mc_ctrl = {
|
||||
.wr_spu0_mem = regk_iop_sw_cpu_no,
|
||||
.wr_spu1_mem = regk_iop_sw_cpu_no,
|
||||
.size = 4,
|
||||
.cmd = regk_iop_sw_cpu_reg_copy,
|
||||
.keep_owner = regk_iop_sw_cpu_yes
|
||||
};
|
||||
reg_iop_spu_rw_ctrl spu_ctrl = {
|
||||
.en = regk_iop_spu_no,
|
||||
.fsm = regk_iop_spu_no,
|
||||
};
|
||||
reg_iop_sw_cpu_r_mc_stat mc_stat;
|
||||
const struct firmware *fw_entry;
|
||||
u32 *data;
|
||||
unsigned int timeout;
|
||||
int retval, i;
|
||||
|
||||
if (spu_inst > 1)
|
||||
return -ENODEV;
|
||||
|
||||
/* get firmware */
|
||||
retval = request_firmware(&fw_entry,
|
||||
fw_name,
|
||||
&iop_spu_device[spu_inst]);
|
||||
if (retval != 0)
|
||||
{
|
||||
printk(KERN_ERR
|
||||
"iop_load_spu: Failed to load firmware \"%s\"\n",
|
||||
fw_name);
|
||||
return retval;
|
||||
}
|
||||
data = (u32 *) fw_entry->data;
|
||||
|
||||
/* acquire ownership of memory controller */
|
||||
switch (spu_inst) {
|
||||
case 0:
|
||||
mc_ctrl.wr_spu0_mem = regk_iop_sw_cpu_yes;
|
||||
REG_WR(iop_spu, regi_iop_spu0, rw_ctrl, spu_ctrl);
|
||||
break;
|
||||
case 1:
|
||||
mc_ctrl.wr_spu1_mem = regk_iop_sw_cpu_yes;
|
||||
REG_WR(iop_spu, regi_iop_spu1, rw_ctrl, spu_ctrl);
|
||||
break;
|
||||
}
|
||||
timeout = IOP_TIMEOUT;
|
||||
do {
|
||||
REG_WR(iop_sw_cpu, regi_iop_sw_cpu, rw_mc_ctrl, mc_ctrl);
|
||||
mc_stat = REG_RD(iop_sw_cpu, regi_iop_sw_cpu, r_mc_stat);
|
||||
} while (mc_stat.owned_by_cpu == regk_iop_sw_cpu_no && --timeout > 0);
|
||||
if (timeout == 0) {
|
||||
printk(KERN_ERR "Timeout waiting to acquire MC\n");
|
||||
retval = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* write to SPU memory */
|
||||
for (i = 0; i < (fw_entry->size/4); i++) {
|
||||
switch (spu_inst) {
|
||||
case 0:
|
||||
REG_WR_INT(iop_spu, regi_iop_spu0, rw_seq_pc, (i*4));
|
||||
break;
|
||||
case 1:
|
||||
REG_WR_INT(iop_spu, regi_iop_spu1, rw_seq_pc, (i*4));
|
||||
break;
|
||||
}
|
||||
REG_WR_INT(iop_sw_cpu, regi_iop_sw_cpu, rw_mc_data, *data);
|
||||
data++;
|
||||
}
|
||||
|
||||
/* release ownership of memory controller */
|
||||
(void) REG_RD(iop_sw_cpu, regi_iop_sw_cpu, rs_mc_data);
|
||||
|
||||
out:
|
||||
release_firmware(fw_entry);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int iop_fw_load_mpu(unsigned char *fw_name)
|
||||
{
|
||||
const unsigned int start_addr = 0;
|
||||
reg_iop_mpu_rw_ctrl mpu_ctrl;
|
||||
const struct firmware *fw_entry;
|
||||
u32 *data;
|
||||
int retval, i;
|
||||
|
||||
/* get firmware */
|
||||
retval = request_firmware(&fw_entry, fw_name, &iop_mpu_device);
|
||||
if (retval != 0)
|
||||
{
|
||||
printk(KERN_ERR
|
||||
"iop_load_spu: Failed to load firmware \"%s\"\n",
|
||||
fw_name);
|
||||
return retval;
|
||||
}
|
||||
data = (u32 *) fw_entry->data;
|
||||
|
||||
/* disable MPU */
|
||||
mpu_ctrl.en = regk_iop_mpu_no;
|
||||
REG_WR(iop_mpu, regi_iop_mpu, rw_ctrl, mpu_ctrl);
|
||||
/* put start address in R0 */
|
||||
REG_WR_VECT(iop_mpu, regi_iop_mpu, rw_r, 0, start_addr);
|
||||
/* write to memory by executing 'SWX i, 4, R0' for each word */
|
||||
if ((retval = wait_mpu_idle()) != 0)
|
||||
goto out;
|
||||
REG_WR(iop_mpu, regi_iop_mpu, rw_instr, MPU_SWX_IIR_INSTR(0, 4, 0));
|
||||
for (i = 0; i < (fw_entry->size / 4); i++) {
|
||||
REG_WR_INT(iop_mpu, regi_iop_mpu, rw_immediate, *data);
|
||||
if ((retval = wait_mpu_idle()) != 0)
|
||||
goto out;
|
||||
data++;
|
||||
}
|
||||
|
||||
out:
|
||||
release_firmware(fw_entry);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int iop_start_mpu(unsigned int start_addr)
|
||||
{
|
||||
reg_iop_mpu_rw_ctrl mpu_ctrl = { .en = regk_iop_mpu_yes };
|
||||
int retval;
|
||||
|
||||
/* disable MPU */
|
||||
if ((retval = wait_mpu_idle()) != 0)
|
||||
goto out;
|
||||
REG_WR(iop_mpu, regi_iop_mpu, rw_instr, MPU_HALT());
|
||||
if ((retval = wait_mpu_idle()) != 0)
|
||||
goto out;
|
||||
/* set PC and wait for it to bite */
|
||||
if ((retval = wait_mpu_idle()) != 0)
|
||||
goto out;
|
||||
REG_WR_INT(iop_mpu, regi_iop_mpu, rw_instr, MPU_BA_I(start_addr));
|
||||
if ((retval = wait_mpu_idle()) != 0)
|
||||
goto out;
|
||||
/* make sure the MPU starts executing with interrupts disabled */
|
||||
REG_WR(iop_mpu, regi_iop_mpu, rw_instr, MPU_DI());
|
||||
if ((retval = wait_mpu_idle()) != 0)
|
||||
goto out;
|
||||
/* enable MPU */
|
||||
REG_WR(iop_mpu, regi_iop_mpu, rw_ctrl, mpu_ctrl);
|
||||
out:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int __init iop_fw_load_init(void)
|
||||
{
|
||||
device_initialize(&iop_spu_device[0]);
|
||||
kobject_set_name(&iop_spu_device[0].kobj, "iop-spu0");
|
||||
kobject_add(&iop_spu_device[0].kobj);
|
||||
device_initialize(&iop_spu_device[1]);
|
||||
kobject_set_name(&iop_spu_device[1].kobj, "iop-spu1");
|
||||
kobject_add(&iop_spu_device[1].kobj);
|
||||
device_initialize(&iop_mpu_device);
|
||||
kobject_set_name(&iop_mpu_device.kobj, "iop-mpu");
|
||||
kobject_add(&iop_mpu_device.kobj);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit iop_fw_load_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(iop_fw_load_init);
|
||||
module_exit(iop_fw_load_exit);
|
||||
|
||||
MODULE_DESCRIPTION("ETRAX FS IO-Processor Firmware Loader");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
EXPORT_SYMBOL(iop_fw_load_spu);
|
||||
EXPORT_SYMBOL(iop_fw_load_mpu);
|
||||
EXPORT_SYMBOL(iop_start_mpu);
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* arch/cris/arch-v32/drivers/nandflash.c
|
||||
*
|
||||
* Copyright (c) 2004
|
||||
*
|
||||
* Derived from drivers/mtd/nand/spia.c
|
||||
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
|
||||
*
|
||||
* $Id: nandflash.c,v 1.3 2005/06/01 10:57:12 starvik Exp $
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <asm/arch/memmap.h>
|
||||
#include <asm/arch/hwregs/reg_map.h>
|
||||
#include <asm/arch/hwregs/reg_rdwr.h>
|
||||
#include <asm/arch/hwregs/gio_defs.h>
|
||||
#include <asm/arch/hwregs/bif_core_defs.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define CE_BIT 4
|
||||
#define CLE_BIT 5
|
||||
#define ALE_BIT 6
|
||||
#define BY_BIT 7
|
||||
|
||||
static struct mtd_info *crisv32_mtd = NULL;
|
||||
/*
|
||||
* hardware specific access to control-lines
|
||||
*/
|
||||
static void crisv32_hwcontrol(struct mtd_info *mtd, int cmd)
|
||||
{
|
||||
unsigned long flags;
|
||||
reg_gio_rw_pa_dout dout = REG_RD(gio, regi_gio, rw_pa_dout);
|
||||
|
||||
local_irq_save(flags);
|
||||
switch(cmd){
|
||||
case NAND_CTL_SETCLE:
|
||||
dout.data |= (1<<CLE_BIT);
|
||||
break;
|
||||
case NAND_CTL_CLRCLE:
|
||||
dout.data &= ~(1<<CLE_BIT);
|
||||
break;
|
||||
case NAND_CTL_SETALE:
|
||||
dout.data |= (1<<ALE_BIT);
|
||||
break;
|
||||
case NAND_CTL_CLRALE:
|
||||
dout.data &= ~(1<<ALE_BIT);
|
||||
break;
|
||||
case NAND_CTL_SETNCE:
|
||||
dout.data |= (1<<CE_BIT);
|
||||
break;
|
||||
case NAND_CTL_CLRNCE:
|
||||
dout.data &= ~(1<<CE_BIT);
|
||||
break;
|
||||
}
|
||||
REG_WR(gio, regi_gio, rw_pa_dout, dout);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* read device ready pin
|
||||
*/
|
||||
int crisv32_device_ready(struct mtd_info *mtd)
|
||||
{
|
||||
reg_gio_r_pa_din din = REG_RD(gio, regi_gio, r_pa_din);
|
||||
return ((din.data & (1 << BY_BIT)) >> BY_BIT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Main initialization routine
|
||||
*/
|
||||
struct mtd_info* __init crisv32_nand_flash_probe (void)
|
||||
{
|
||||
void __iomem *read_cs;
|
||||
void __iomem *write_cs;
|
||||
|
||||
reg_bif_core_rw_grp3_cfg bif_cfg = REG_RD(bif_core, regi_bif_core, rw_grp3_cfg);
|
||||
reg_gio_rw_pa_oe pa_oe = REG_RD(gio, regi_gio, rw_pa_oe);
|
||||
struct nand_chip *this;
|
||||
int err = 0;
|
||||
|
||||
/* Allocate memory for MTD device structure and private data */
|
||||
crisv32_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
|
||||
GFP_KERNEL);
|
||||
if (!crisv32_mtd) {
|
||||
printk ("Unable to allocate CRISv32 NAND MTD device structure.\n");
|
||||
err = -ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
read_cs = ioremap(MEM_CSP0_START | MEM_NON_CACHEABLE, 8192);
|
||||
write_cs = ioremap(MEM_CSP1_START | MEM_NON_CACHEABLE, 8192);
|
||||
|
||||
if (!read_cs || !write_cs) {
|
||||
printk("CRISv32 NAND ioremap failed\n");
|
||||
err = -EIO;
|
||||
goto out_mtd;
|
||||
}
|
||||
|
||||
/* Get pointer to private data */
|
||||
this = (struct nand_chip *) (&crisv32_mtd[1]);
|
||||
|
||||
pa_oe.oe |= 1 << CE_BIT;
|
||||
pa_oe.oe |= 1 << ALE_BIT;
|
||||
pa_oe.oe |= 1 << CLE_BIT;
|
||||
pa_oe.oe &= ~ (1 << BY_BIT);
|
||||
REG_WR(gio, regi_gio, rw_pa_oe, pa_oe);
|
||||
|
||||
bif_cfg.gated_csp0 = regk_bif_core_rd;
|
||||
bif_cfg.gated_csp1 = regk_bif_core_wr;
|
||||
REG_WR(bif_core, regi_bif_core, rw_grp3_cfg, bif_cfg);
|
||||
|
||||
/* Initialize structures */
|
||||
memset((char *) crisv32_mtd, 0, sizeof(struct mtd_info));
|
||||
memset((char *) this, 0, sizeof(struct nand_chip));
|
||||
|
||||
/* Link the private data with the MTD structure */
|
||||
crisv32_mtd->priv = this;
|
||||
|
||||
/* Set address of NAND IO lines */
|
||||
this->IO_ADDR_R = read_cs;
|
||||
this->IO_ADDR_W = write_cs;
|
||||
this->hwcontrol = crisv32_hwcontrol;
|
||||
this->dev_ready = crisv32_device_ready;
|
||||
/* 20 us command delay time */
|
||||
this->chip_delay = 20;
|
||||
this->eccmode = NAND_ECC_SOFT;
|
||||
|
||||
/* Enable the following for a flash based bad block table */
|
||||
this->options = NAND_USE_FLASH_BBT;
|
||||
|
||||
/* Scan to find existance of the device */
|
||||
if (nand_scan (crisv32_mtd, 1)) {
|
||||
err = -ENXIO;
|
||||
goto out_ior;
|
||||
}
|
||||
|
||||
return crisv32_mtd;
|
||||
|
||||
out_ior:
|
||||
iounmap((void *)read_cs);
|
||||
iounmap((void *)write_cs);
|
||||
out_mtd:
|
||||
kfree (crisv32_mtd);
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -0,0 +1,341 @@
|
|||
/*
|
||||
* PCF8563 RTC
|
||||
*
|
||||
* From Phillips' datasheet:
|
||||
*
|
||||
* The PCF8563 is a CMOS real-time clock/calendar optimized for low power
|
||||
* consumption. A programmable clock output, interupt output and voltage
|
||||
* low detector are also provided. All address and data are transferred
|
||||
* serially via two-line bidirectional I2C-bus. Maximum bus speed is
|
||||
* 400 kbits/s. The built-in word address register is incremented
|
||||
* automatically after each written or read byte.
|
||||
*
|
||||
* Copyright (c) 2002-2003, Axis Communications AB
|
||||
* All rights reserved.
|
||||
*
|
||||
* Author: Tobias Anderberg <tobiasa@axis.com>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/bcd.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/rtc.h>
|
||||
|
||||
#include "i2c.h"
|
||||
|
||||
#define PCF8563_MAJOR 121 /* Local major number. */
|
||||
#define DEVICE_NAME "rtc" /* Name which is registered in /proc/devices. */
|
||||
#define PCF8563_NAME "PCF8563"
|
||||
#define DRIVER_VERSION "$Revision: 1.1 $"
|
||||
|
||||
/* Two simple wrapper macros, saves a few keystrokes. */
|
||||
#define rtc_read(x) i2c_readreg(RTC_I2C_READ, x)
|
||||
#define rtc_write(x,y) i2c_writereg(RTC_I2C_WRITE, x, y)
|
||||
|
||||
static const unsigned char days_in_month[] =
|
||||
{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
||||
|
||||
int pcf8563_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
|
||||
int pcf8563_open(struct inode *, struct file *);
|
||||
int pcf8563_release(struct inode *, struct file *);
|
||||
|
||||
static struct file_operations pcf8563_fops = {
|
||||
owner: THIS_MODULE,
|
||||
ioctl: pcf8563_ioctl,
|
||||
open: pcf8563_open,
|
||||
release: pcf8563_release,
|
||||
};
|
||||
|
||||
unsigned char
|
||||
pcf8563_readreg(int reg)
|
||||
{
|
||||
unsigned char res = rtc_read(reg);
|
||||
|
||||
/* The PCF8563 does not return 0 for unimplemented bits */
|
||||
switch (reg) {
|
||||
case RTC_SECONDS:
|
||||
case RTC_MINUTES:
|
||||
res &= 0x7F;
|
||||
break;
|
||||
case RTC_HOURS:
|
||||
case RTC_DAY_OF_MONTH:
|
||||
res &= 0x3F;
|
||||
break;
|
||||
case RTC_WEEKDAY:
|
||||
res &= 0x07;
|
||||
break;
|
||||
case RTC_MONTH:
|
||||
res &= 0x1F;
|
||||
break;
|
||||
case RTC_CONTROL1:
|
||||
res &= 0xA8;
|
||||
break;
|
||||
case RTC_CONTROL2:
|
||||
res &= 0x1F;
|
||||
break;
|
||||
case RTC_CLOCKOUT_FREQ:
|
||||
case RTC_TIMER_CONTROL:
|
||||
res &= 0x83;
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void
|
||||
pcf8563_writereg(int reg, unsigned char val)
|
||||
{
|
||||
#ifdef CONFIG_ETRAX_RTC_READONLY
|
||||
if (reg == RTC_CONTROL1 || (reg >= RTC_SECONDS && reg <= RTC_YEAR))
|
||||
return;
|
||||
#endif
|
||||
|
||||
rtc_write(reg, val);
|
||||
}
|
||||
|
||||
void
|
||||
get_rtc_time(struct rtc_time *tm)
|
||||
{
|
||||
tm->tm_sec = rtc_read(RTC_SECONDS);
|
||||
tm->tm_min = rtc_read(RTC_MINUTES);
|
||||
tm->tm_hour = rtc_read(RTC_HOURS);
|
||||
tm->tm_mday = rtc_read(RTC_DAY_OF_MONTH);
|
||||
tm->tm_wday = rtc_read(RTC_WEEKDAY);
|
||||
tm->tm_mon = rtc_read(RTC_MONTH);
|
||||
tm->tm_year = rtc_read(RTC_YEAR);
|
||||
|
||||
if (tm->tm_sec & 0x80)
|
||||
printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time "
|
||||
"information is no longer guaranteed!\n", PCF8563_NAME);
|
||||
|
||||
tm->tm_year = BCD_TO_BIN(tm->tm_year) + ((tm->tm_mon & 0x80) ? 100 : 0);
|
||||
tm->tm_sec &= 0x7F;
|
||||
tm->tm_min &= 0x7F;
|
||||
tm->tm_hour &= 0x3F;
|
||||
tm->tm_mday &= 0x3F;
|
||||
tm->tm_wday &= 0x07; /* Not coded in BCD. */
|
||||
tm->tm_mon &= 0x1F;
|
||||
|
||||
BCD_TO_BIN(tm->tm_sec);
|
||||
BCD_TO_BIN(tm->tm_min);
|
||||
BCD_TO_BIN(tm->tm_hour);
|
||||
BCD_TO_BIN(tm->tm_mday);
|
||||
BCD_TO_BIN(tm->tm_mon);
|
||||
tm->tm_mon--; /* Month is 1..12 in RTC but 0..11 in linux */
|
||||
}
|
||||
|
||||
int __init
|
||||
pcf8563_init(void)
|
||||
{
|
||||
/* Initiate the i2c protocol. */
|
||||
i2c_init();
|
||||
|
||||
/*
|
||||
* First of all we need to reset the chip. This is done by
|
||||
* clearing control1, control2 and clk freq and resetting
|
||||
* all alarms.
|
||||
*/
|
||||
if (rtc_write(RTC_CONTROL1, 0x00) < 0)
|
||||
goto err;
|
||||
|
||||
if (rtc_write(RTC_CONTROL2, 0x00) < 0)
|
||||
goto err;
|
||||
|
||||
if (rtc_write(RTC_CLOCKOUT_FREQ, 0x00) < 0)
|
||||
goto err;
|
||||
|
||||
if (rtc_write(RTC_TIMER_CONTROL, 0x03) < 0)
|
||||
goto err;
|
||||
|
||||
/* Reset the alarms. */
|
||||
if (rtc_write(RTC_MINUTE_ALARM, 0x80) < 0)
|
||||
goto err;
|
||||
|
||||
if (rtc_write(RTC_HOUR_ALARM, 0x80) < 0)
|
||||
goto err;
|
||||
|
||||
if (rtc_write(RTC_DAY_ALARM, 0x80) < 0)
|
||||
goto err;
|
||||
|
||||
if (rtc_write(RTC_WEEKDAY_ALARM, 0x80) < 0)
|
||||
goto err;
|
||||
|
||||
if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) {
|
||||
printk(KERN_INFO "%s: Unable to get major numer %d for RTC device.\n",
|
||||
PCF8563_NAME, PCF8563_MAJOR);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, DRIVER_VERSION);
|
||||
|
||||
/* Check for low voltage, and warn about it.. */
|
||||
if (rtc_read(RTC_SECONDS) & 0x80)
|
||||
printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time "
|
||||
"information is no longer guaranteed!\n", PCF8563_NAME);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
printk(KERN_INFO "%s: Error initializing chip.\n", PCF8563_NAME);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void __exit
|
||||
pcf8563_exit(void)
|
||||
{
|
||||
if (unregister_chrdev(PCF8563_MAJOR, DEVICE_NAME) < 0) {
|
||||
printk(KERN_INFO "%s: Unable to unregister device.\n", PCF8563_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ioctl calls for this driver. Why return -ENOTTY upon error? Because
|
||||
* POSIX says so!
|
||||
*/
|
||||
int
|
||||
pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
/* Some sanity checks. */
|
||||
if (_IOC_TYPE(cmd) != RTC_MAGIC)
|
||||
return -ENOTTY;
|
||||
|
||||
if (_IOC_NR(cmd) > RTC_MAX_IOCTL)
|
||||
return -ENOTTY;
|
||||
|
||||
switch (cmd) {
|
||||
case RTC_RD_TIME:
|
||||
{
|
||||
struct rtc_time tm;
|
||||
|
||||
memset(&tm, 0, sizeof (struct rtc_time));
|
||||
get_rtc_time(&tm);
|
||||
|
||||
if (copy_to_user((struct rtc_time *) arg, &tm, sizeof tm)) {
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
case RTC_SET_TIME:
|
||||
{
|
||||
#ifdef CONFIG_ETRAX_RTC_READONLY
|
||||
return -EPERM;
|
||||
#else
|
||||
int leap;
|
||||
int year;
|
||||
int century;
|
||||
struct rtc_time tm;
|
||||
|
||||
if (!capable(CAP_SYS_TIME))
|
||||
return -EPERM;
|
||||
|
||||
if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof tm))
|
||||
return -EFAULT;
|
||||
|
||||
/* Convert from struct tm to struct rtc_time. */
|
||||
tm.tm_year += 1900;
|
||||
tm.tm_mon += 1;
|
||||
|
||||
/*
|
||||
* Check if tm.tm_year is a leap year. A year is a leap
|
||||
* year if it is divisible by 4 but not 100, except
|
||||
* that years divisible by 400 _are_ leap years.
|
||||
*/
|
||||
year = tm.tm_year;
|
||||
leap = (tm.tm_mon == 2) && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
|
||||
|
||||
/* Perform some sanity checks. */
|
||||
if ((tm.tm_year < 1970) ||
|
||||
(tm.tm_mon > 12) ||
|
||||
(tm.tm_mday == 0) ||
|
||||
(tm.tm_mday > days_in_month[tm.tm_mon] + leap) ||
|
||||
(tm.tm_wday >= 7) ||
|
||||
(tm.tm_hour >= 24) ||
|
||||
(tm.tm_min >= 60) ||
|
||||
(tm.tm_sec >= 60))
|
||||
return -EINVAL;
|
||||
|
||||
century = (tm.tm_year >= 2000) ? 0x80 : 0;
|
||||
tm.tm_year = tm.tm_year % 100;
|
||||
|
||||
BIN_TO_BCD(tm.tm_year);
|
||||
BIN_TO_BCD(tm.tm_mday);
|
||||
BIN_TO_BCD(tm.tm_hour);
|
||||
BIN_TO_BCD(tm.tm_min);
|
||||
BIN_TO_BCD(tm.tm_sec);
|
||||
tm.tm_mon |= century;
|
||||
|
||||
rtc_write(RTC_YEAR, tm.tm_year);
|
||||
rtc_write(RTC_MONTH, tm.tm_mon);
|
||||
rtc_write(RTC_WEEKDAY, tm.tm_wday); /* Not coded in BCD. */
|
||||
rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday);
|
||||
rtc_write(RTC_HOURS, tm.tm_hour);
|
||||
rtc_write(RTC_MINUTES, tm.tm_min);
|
||||
rtc_write(RTC_SECONDS, tm.tm_sec);
|
||||
|
||||
return 0;
|
||||
#endif /* !CONFIG_ETRAX_RTC_READONLY */
|
||||
}
|
||||
|
||||
case RTC_VLOW_RD:
|
||||
{
|
||||
int vl_bit = 0;
|
||||
|
||||
if (rtc_read(RTC_SECONDS) & 0x80) {
|
||||
vl_bit = 1;
|
||||
printk(KERN_WARNING "%s: RTC Voltage Low - reliable "
|
||||
"date/time information is no longer guaranteed!\n",
|
||||
PCF8563_NAME);
|
||||
}
|
||||
if (copy_to_user((int *) arg, &vl_bit, sizeof(int)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
case RTC_VLOW_SET:
|
||||
{
|
||||
/* Clear the VL bit in the seconds register */
|
||||
int ret = rtc_read(RTC_SECONDS);
|
||||
|
||||
rtc_write(RTC_SECONDS, (ret & 0x7F));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pcf8563_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
MOD_INC_USE_COUNT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pcf8563_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
MOD_DEC_USE_COUNT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(pcf8563_init);
|
||||
module_exit(pcf8563_exit);
|
|
@ -0,0 +1,5 @@
|
|||
#
|
||||
# Makefile for Etrax cardbus driver
|
||||
#
|
||||
|
||||
obj-$(CONFIG_ETRAX_CARDBUS) += bios.o dma.o
|
|
@ -0,0 +1,131 @@
|
|||
#include <linux/pci.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <asm/arch/hwregs/intr_vect.h>
|
||||
|
||||
void __devinit pcibios_fixup_bus(struct pci_bus *b)
|
||||
{
|
||||
}
|
||||
|
||||
char * __devinit pcibios_setup(char *str)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void pcibios_set_master(struct pci_dev *dev)
|
||||
{
|
||||
u8 lat;
|
||||
pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
|
||||
printk(KERN_DEBUG "PCI: Setting latency timer of device %s to %d\n", pci_name(dev), lat);
|
||||
pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat);
|
||||
}
|
||||
|
||||
int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
|
||||
enum pci_mmap_state mmap_state, int write_combine)
|
||||
{
|
||||
unsigned long prot;
|
||||
|
||||
/* Leave vm_pgoff as-is, the PCI space address is the physical
|
||||
* address on this platform.
|
||||
*/
|
||||
vma->vm_flags |= (VM_SHM | VM_LOCKED | VM_IO);
|
||||
|
||||
prot = pgprot_val(vma->vm_page_prot);
|
||||
vma->vm_page_prot = __pgprot(prot);
|
||||
|
||||
/* Write-combine setting is ignored, it is changed via the mtrr
|
||||
* interfaces on this platform.
|
||||
*/
|
||||
if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
|
||||
vma->vm_end - vma->vm_start,
|
||||
vma->vm_page_prot))
|
||||
return -EAGAIN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
pcibios_align_resource(void *data, struct resource *res,
|
||||
unsigned long size, unsigned long align)
|
||||
{
|
||||
if (res->flags & IORESOURCE_IO) {
|
||||
unsigned long start = res->start;
|
||||
|
||||
if (start & 0x300) {
|
||||
start = (start + 0x3ff) & ~0x3ff;
|
||||
res->start = start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int pcibios_enable_resources(struct pci_dev *dev, int mask)
|
||||
{
|
||||
u16 cmd, old_cmd;
|
||||
int idx;
|
||||
struct resource *r;
|
||||
|
||||
pci_read_config_word(dev, PCI_COMMAND, &cmd);
|
||||
old_cmd = cmd;
|
||||
for(idx=0; idx<6; idx++) {
|
||||
/* Only set up the requested stuff */
|
||||
if (!(mask & (1<<idx)))
|
||||
continue;
|
||||
|
||||
r = &dev->resource[idx];
|
||||
if (!r->start && r->end) {
|
||||
printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", pci_name(dev));
|
||||
return -EINVAL;
|
||||
}
|
||||
if (r->flags & IORESOURCE_IO)
|
||||
cmd |= PCI_COMMAND_IO;
|
||||
if (r->flags & IORESOURCE_MEM)
|
||||
cmd |= PCI_COMMAND_MEMORY;
|
||||
}
|
||||
if (dev->resource[PCI_ROM_RESOURCE].start)
|
||||
cmd |= PCI_COMMAND_MEMORY;
|
||||
if (cmd != old_cmd) {
|
||||
printk("PCI: Enabling device %s (%04x -> %04x)\n", pci_name(dev), old_cmd, cmd);
|
||||
pci_write_config_word(dev, PCI_COMMAND, cmd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pcibios_enable_irq(struct pci_dev *dev)
|
||||
{
|
||||
dev->irq = EXT_INTR_VECT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pcibios_enable_device(struct pci_dev *dev, int mask)
|
||||
{
|
||||
int err;
|
||||
|
||||
if ((err = pcibios_enable_resources(dev, mask)) < 0)
|
||||
return err;
|
||||
|
||||
return pcibios_enable_irq(dev);
|
||||
}
|
||||
|
||||
int pcibios_assign_resources(void)
|
||||
{
|
||||
struct pci_dev *dev = NULL;
|
||||
int idx;
|
||||
struct resource *r;
|
||||
|
||||
while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
|
||||
int class = dev->class >> 8;
|
||||
|
||||
/* Don't touch classless devices and host bridges */
|
||||
if (!class || class == PCI_CLASS_BRIDGE_HOST)
|
||||
continue;
|
||||
|
||||
for(idx=0; idx<6; idx++) {
|
||||
r = &dev->resource[idx];
|
||||
|
||||
if (!r->start && r->end)
|
||||
pci_assign_resource(dev, idx);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(pcibios_assign_resources);
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* Dynamic DMA mapping support.
|
||||
*
|
||||
* On cris there is no hardware dynamic DMA address translation,
|
||||
* so consistent alloc/free are merely page allocation/freeing.
|
||||
* The rest of the dynamic DMA mapping interface is implemented
|
||||
* in asm/pci.h.
|
||||
*
|
||||
* Borrowed from i386.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/pci.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
struct dma_coherent_mem {
|
||||
void *virt_base;
|
||||
u32 device_base;
|
||||
int size;
|
||||
int flags;
|
||||
unsigned long *bitmap;
|
||||
};
|
||||
|
||||
void *dma_alloc_coherent(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, unsigned int __nocast gfp)
|
||||
{
|
||||
void *ret;
|
||||
struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL;
|
||||
int order = get_order(size);
|
||||
/* ignore region specifiers */
|
||||
gfp &= ~(__GFP_DMA | __GFP_HIGHMEM);
|
||||
|
||||
if (mem) {
|
||||
int page = bitmap_find_free_region(mem->bitmap, mem->size,
|
||||
order);
|
||||
if (page >= 0) {
|
||||
*dma_handle = mem->device_base + (page << PAGE_SHIFT);
|
||||
ret = mem->virt_base + (page << PAGE_SHIFT);
|
||||
memset(ret, 0, size);
|
||||
return ret;
|
||||
}
|
||||
if (mem->flags & DMA_MEMORY_EXCLUSIVE)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (dev == NULL || (dev->coherent_dma_mask < 0xffffffff))
|
||||
gfp |= GFP_DMA;
|
||||
|
||||
ret = (void *)__get_free_pages(gfp, order);
|
||||
|
||||
if (ret != NULL) {
|
||||
memset(ret, 0, size);
|
||||
*dma_handle = virt_to_phys(ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void dma_free_coherent(struct device *dev, size_t size,
|
||||
void *vaddr, dma_addr_t dma_handle)
|
||||
{
|
||||
struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL;
|
||||
int order = get_order(size);
|
||||
|
||||
if (mem && vaddr >= mem->virt_base && vaddr < (mem->virt_base + (mem->size << PAGE_SHIFT))) {
|
||||
int page = (vaddr - mem->virt_base) >> PAGE_SHIFT;
|
||||
|
||||
bitmap_release_region(mem->bitmap, page, order);
|
||||
} else
|
||||
free_pages((unsigned long)vaddr, order);
|
||||
}
|
||||
|
||||
int dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr,
|
||||
dma_addr_t device_addr, size_t size, int flags)
|
||||
{
|
||||
void __iomem *mem_base;
|
||||
int pages = size >> PAGE_SHIFT;
|
||||
int bitmap_size = (pages + 31)/32;
|
||||
|
||||
if ((flags & (DMA_MEMORY_MAP | DMA_MEMORY_IO)) == 0)
|
||||
goto out;
|
||||
if (!size)
|
||||
goto out;
|
||||
if (dev->dma_mem)
|
||||
goto out;
|
||||
|
||||
/* FIXME: this routine just ignores DMA_MEMORY_INCLUDES_CHILDREN */
|
||||
|
||||
mem_base = ioremap(bus_addr, size);
|
||||
if (!mem_base)
|
||||
goto out;
|
||||
|
||||
dev->dma_mem = kmalloc(sizeof(struct dma_coherent_mem), GFP_KERNEL);
|
||||
if (!dev->dma_mem)
|
||||
goto out;
|
||||
memset(dev->dma_mem, 0, sizeof(struct dma_coherent_mem));
|
||||
dev->dma_mem->bitmap = kmalloc(bitmap_size, GFP_KERNEL);
|
||||
if (!dev->dma_mem->bitmap)
|
||||
goto free1_out;
|
||||
memset(dev->dma_mem->bitmap, 0, bitmap_size);
|
||||
|
||||
dev->dma_mem->virt_base = mem_base;
|
||||
dev->dma_mem->device_base = device_addr;
|
||||
dev->dma_mem->size = pages;
|
||||
dev->dma_mem->flags = flags;
|
||||
|
||||
if (flags & DMA_MEMORY_MAP)
|
||||
return DMA_MEMORY_MAP;
|
||||
|
||||
return DMA_MEMORY_IO;
|
||||
|
||||
free1_out:
|
||||
kfree(dev->dma_mem->bitmap);
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(dma_declare_coherent_memory);
|
||||
|
||||
void dma_release_declared_memory(struct device *dev)
|
||||
{
|
||||
struct dma_coherent_mem *mem = dev->dma_mem;
|
||||
|
||||
if(!mem)
|
||||
return;
|
||||
dev->dma_mem = NULL;
|
||||
iounmap(mem->virt_base);
|
||||
kfree(mem->bitmap);
|
||||
kfree(mem);
|
||||
}
|
||||
EXPORT_SYMBOL(dma_release_declared_memory);
|
||||
|
||||
void *dma_mark_declared_memory_occupied(struct device *dev,
|
||||
dma_addr_t device_addr, size_t size)
|
||||
{
|
||||
struct dma_coherent_mem *mem = dev->dma_mem;
|
||||
int pages = (size + (device_addr & ~PAGE_MASK) + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
||||
int pos, err;
|
||||
|
||||
if (!mem)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
pos = (device_addr - mem->device_base) >> PAGE_SHIFT;
|
||||
err = bitmap_allocate_region(mem->bitmap, pos, get_order(pages));
|
||||
if (err != 0)
|
||||
return ERR_PTR(err);
|
||||
return mem->virt_base + (pos << PAGE_SHIFT);
|
||||
}
|
||||
EXPORT_SYMBOL(dma_mark_declared_memory_occupied);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,21 @@
|
|||
# $Id: Makefile,v 1.11 2004/12/17 10:16:13 starvik Exp $
|
||||
#
|
||||
# Makefile for the linux kernel.
|
||||
#
|
||||
|
||||
extra-y := head.o
|
||||
|
||||
|
||||
obj-y := entry.o traps.o irq.o debugport.o dma.o pinmux.o \
|
||||
process.o ptrace.o setup.o signal.o traps.o time.o \
|
||||
arbiter.o io.o
|
||||
|
||||
obj-$(CONFIG_ETRAXFS_SIM) += vcs_hook.o
|
||||
|
||||
obj-$(CONFIG_SMP) += smp.o
|
||||
obj-$(CONFIG_ETRAX_KGDB) += kgdb.o kgdb_asm.o
|
||||
obj-$(CONFIG_ETRAX_FAST_TIMER) += fasttimer.o
|
||||
obj-$(CONFIG_MODULES) += crisksyms.o
|
||||
|
||||
clean:
|
||||
|
|
@ -0,0 +1,297 @@
|
|||
/*
|
||||
* Memory arbiter functions. Allocates bandwith through the
|
||||
* arbiter and sets up arbiter breakpoints.
|
||||
*
|
||||
* The algorithm first assigns slots to the clients that has specified
|
||||
* bandwith (e.g. ethernet) and then the remaining slots are divided
|
||||
* on all the active clients.
|
||||
*
|
||||
* Copyright (c) 2004, 2005 Axis Communications AB.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <asm/arch/hwregs/reg_map.h>
|
||||
#include <asm/arch/hwregs/reg_rdwr.h>
|
||||
#include <asm/arch/hwregs/marb_defs.h>
|
||||
#include <asm/arch/arbiter.h>
|
||||
#include <asm/arch/hwregs/intr_vect.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
struct crisv32_watch_entry
|
||||
{
|
||||
unsigned long instance;
|
||||
watch_callback* cb;
|
||||
unsigned long start;
|
||||
unsigned long end;
|
||||
int used;
|
||||
};
|
||||
|
||||
#define NUMBER_OF_BP 4
|
||||
#define NBR_OF_CLIENTS 14
|
||||
#define NBR_OF_SLOTS 64
|
||||
#define SDRAM_BANDWIDTH 100000000 /* Some kind of expected value */
|
||||
#define INTMEM_BANDWIDTH 400000000
|
||||
#define NBR_OF_REGIONS 2
|
||||
|
||||
static struct crisv32_watch_entry watches[NUMBER_OF_BP] =
|
||||
{
|
||||
{regi_marb_bp0},
|
||||
{regi_marb_bp1},
|
||||
{regi_marb_bp2},
|
||||
{regi_marb_bp3}
|
||||
};
|
||||
|
||||
static int requested_slots[NBR_OF_REGIONS][NBR_OF_CLIENTS];
|
||||
static int active_clients[NBR_OF_REGIONS][NBR_OF_CLIENTS];
|
||||
static int max_bandwidth[NBR_OF_REGIONS] = {SDRAM_BANDWIDTH, INTMEM_BANDWIDTH};
|
||||
|
||||
DEFINE_SPINLOCK(arbiter_lock);
|
||||
|
||||
static irqreturn_t
|
||||
crisv32_arbiter_irq(int irq, void* dev_id, struct pt_regs* regs);
|
||||
|
||||
static void crisv32_arbiter_config(int region)
|
||||
{
|
||||
int slot;
|
||||
int client;
|
||||
int interval = 0;
|
||||
int val[NBR_OF_SLOTS];
|
||||
|
||||
for (slot = 0; slot < NBR_OF_SLOTS; slot++)
|
||||
val[slot] = NBR_OF_CLIENTS + 1;
|
||||
|
||||
for (client = 0; client < NBR_OF_CLIENTS; client++)
|
||||
{
|
||||
int pos;
|
||||
if (!requested_slots[region][client])
|
||||
continue;
|
||||
interval = NBR_OF_SLOTS / requested_slots[region][client];
|
||||
pos = 0;
|
||||
while (pos < NBR_OF_SLOTS)
|
||||
{
|
||||
if (val[pos] != NBR_OF_CLIENTS + 1)
|
||||
pos++;
|
||||
else
|
||||
{
|
||||
val[pos] = client;
|
||||
pos += interval;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client = 0;
|
||||
for (slot = 0; slot < NBR_OF_SLOTS; slot++)
|
||||
{
|
||||
if (val[slot] == NBR_OF_CLIENTS + 1)
|
||||
{
|
||||
int first = client;
|
||||
while(!active_clients[region][client]) {
|
||||
client = (client + 1) % NBR_OF_CLIENTS;
|
||||
if (client == first)
|
||||
break;
|
||||
}
|
||||
val[slot] = client;
|
||||
client = (client + 1) % NBR_OF_CLIENTS;
|
||||
}
|
||||
if (region == EXT_REGION)
|
||||
REG_WR_INT_VECT(marb, regi_marb, rw_ext_slots, slot, val[slot]);
|
||||
else if (region == INT_REGION)
|
||||
REG_WR_INT_VECT(marb, regi_marb, rw_int_slots, slot, val[slot]);
|
||||
}
|
||||
}
|
||||
|
||||
extern char _stext, _etext;
|
||||
|
||||
static void crisv32_arbiter_init(void)
|
||||
{
|
||||
static int initialized = 0;
|
||||
|
||||
if (initialized)
|
||||
return;
|
||||
|
||||
initialized = 1;
|
||||
|
||||
/* CPU caches are active. */
|
||||
active_clients[EXT_REGION][10] = active_clients[EXT_REGION][11] = 1;
|
||||
crisv32_arbiter_config(EXT_REGION);
|
||||
crisv32_arbiter_config(INT_REGION);
|
||||
|
||||
if (request_irq(MEMARB_INTR_VECT, crisv32_arbiter_irq, SA_INTERRUPT,
|
||||
"arbiter", NULL))
|
||||
printk(KERN_ERR "Couldn't allocate arbiter IRQ\n");
|
||||
|
||||
#ifndef CONFIG_ETRAX_KGDB
|
||||
/* Global watch for writes to kernel text segment. */
|
||||
crisv32_arbiter_watch(virt_to_phys(&_stext), &_etext - &_stext,
|
||||
arbiter_all_clients, arbiter_all_write, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
int crisv32_arbiter_allocate_bandwith(int client, int region,
|
||||
unsigned long bandwidth)
|
||||
{
|
||||
int i;
|
||||
int total_assigned = 0;
|
||||
int total_clients = 0;
|
||||
int req;
|
||||
|
||||
crisv32_arbiter_init();
|
||||
|
||||
for (i = 0; i < NBR_OF_CLIENTS; i++)
|
||||
{
|
||||
total_assigned += requested_slots[region][i];
|
||||
total_clients += active_clients[region][i];
|
||||
}
|
||||
req = NBR_OF_SLOTS / (max_bandwidth[region] / bandwidth);
|
||||
|
||||
if (total_assigned + total_clients + req + 1 > NBR_OF_SLOTS)
|
||||
return -ENOMEM;
|
||||
|
||||
active_clients[region][client] = 1;
|
||||
requested_slots[region][client] = req;
|
||||
crisv32_arbiter_config(region);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int crisv32_arbiter_watch(unsigned long start, unsigned long size,
|
||||
unsigned long clients, unsigned long accesses,
|
||||
watch_callback* cb)
|
||||
{
|
||||
int i;
|
||||
|
||||
crisv32_arbiter_init();
|
||||
|
||||
if (start > 0x80000000) {
|
||||
printk("Arbiter: %lX doesn't look like a physical address", start);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
spin_lock(&arbiter_lock);
|
||||
|
||||
for (i = 0; i < NUMBER_OF_BP; i++) {
|
||||
if (!watches[i].used) {
|
||||
reg_marb_rw_intr_mask intr_mask = REG_RD(marb, regi_marb, rw_intr_mask);
|
||||
|
||||
watches[i].used = 1;
|
||||
watches[i].start = start;
|
||||
watches[i].end = start + size;
|
||||
watches[i].cb = cb;
|
||||
|
||||
REG_WR_INT(marb_bp, watches[i].instance, rw_first_addr, watches[i].start);
|
||||
REG_WR_INT(marb_bp, watches[i].instance, rw_last_addr, watches[i].end);
|
||||
REG_WR_INT(marb_bp, watches[i].instance, rw_op, accesses);
|
||||
REG_WR_INT(marb_bp, watches[i].instance, rw_clients, clients);
|
||||
|
||||
if (i == 0)
|
||||
intr_mask.bp0 = regk_marb_yes;
|
||||
else if (i == 1)
|
||||
intr_mask.bp1 = regk_marb_yes;
|
||||
else if (i == 2)
|
||||
intr_mask.bp2 = regk_marb_yes;
|
||||
else if (i == 3)
|
||||
intr_mask.bp3 = regk_marb_yes;
|
||||
|
||||
REG_WR(marb, regi_marb, rw_intr_mask, intr_mask);
|
||||
spin_unlock(&arbiter_lock);
|
||||
|
||||
return i;
|
||||
}
|
||||
}
|
||||
spin_unlock(&arbiter_lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
int crisv32_arbiter_unwatch(int id)
|
||||
{
|
||||
reg_marb_rw_intr_mask intr_mask = REG_RD(marb, regi_marb, rw_intr_mask);
|
||||
|
||||
crisv32_arbiter_init();
|
||||
|
||||
spin_lock(&arbiter_lock);
|
||||
|
||||
if ((id < 0) || (id >= NUMBER_OF_BP) || (!watches[id].used)) {
|
||||
spin_unlock(&arbiter_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(&watches[id], 0, sizeof(struct crisv32_watch_entry));
|
||||
|
||||
if (id == 0)
|
||||
intr_mask.bp0 = regk_marb_no;
|
||||
else if (id == 1)
|
||||
intr_mask.bp2 = regk_marb_no;
|
||||
else if (id == 2)
|
||||
intr_mask.bp2 = regk_marb_no;
|
||||
else if (id == 3)
|
||||
intr_mask.bp3 = regk_marb_no;
|
||||
|
||||
REG_WR(marb, regi_marb, rw_intr_mask, intr_mask);
|
||||
|
||||
spin_unlock(&arbiter_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern void show_registers(struct pt_regs *regs);
|
||||
|
||||
static irqreturn_t
|
||||
crisv32_arbiter_irq(int irq, void* dev_id, struct pt_regs* regs)
|
||||
{
|
||||
reg_marb_r_masked_intr masked_intr = REG_RD(marb, regi_marb, r_masked_intr);
|
||||
reg_marb_bp_r_brk_clients r_clients;
|
||||
reg_marb_bp_r_brk_addr r_addr;
|
||||
reg_marb_bp_r_brk_op r_op;
|
||||
reg_marb_bp_r_brk_first_client r_first;
|
||||
reg_marb_bp_r_brk_size r_size;
|
||||
reg_marb_bp_rw_ack ack = {0};
|
||||
reg_marb_rw_ack_intr ack_intr = {.bp0=1,.bp1=1,.bp2=1,.bp3=1};
|
||||
struct crisv32_watch_entry* watch;
|
||||
|
||||
if (masked_intr.bp0) {
|
||||
watch = &watches[0];
|
||||
ack_intr.bp0 = regk_marb_yes;
|
||||
} else if (masked_intr.bp1) {
|
||||
watch = &watches[1];
|
||||
ack_intr.bp1 = regk_marb_yes;
|
||||
} else if (masked_intr.bp2) {
|
||||
watch = &watches[2];
|
||||
ack_intr.bp2 = regk_marb_yes;
|
||||
} else if (masked_intr.bp3) {
|
||||
watch = &watches[3];
|
||||
ack_intr.bp3 = regk_marb_yes;
|
||||
} else {
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/* Retrieve all useful information and print it. */
|
||||
r_clients = REG_RD(marb_bp, watch->instance, r_brk_clients);
|
||||
r_addr = REG_RD(marb_bp, watch->instance, r_brk_addr);
|
||||
r_op = REG_RD(marb_bp, watch->instance, r_brk_op);
|
||||
r_first = REG_RD(marb_bp, watch->instance, r_brk_first_client);
|
||||
r_size = REG_RD(marb_bp, watch->instance, r_brk_size);
|
||||
|
||||
printk("Arbiter IRQ\n");
|
||||
printk("Clients %X addr %X op %X first %X size %X\n",
|
||||
REG_TYPE_CONV(int, reg_marb_bp_r_brk_clients, r_clients),
|
||||
REG_TYPE_CONV(int, reg_marb_bp_r_brk_addr, r_addr),
|
||||
REG_TYPE_CONV(int, reg_marb_bp_r_brk_op, r_op),
|
||||
REG_TYPE_CONV(int, reg_marb_bp_r_brk_first_client, r_first),
|
||||
REG_TYPE_CONV(int, reg_marb_bp_r_brk_size, r_size));
|
||||
|
||||
REG_WR(marb_bp, watch->instance, rw_ack, ack);
|
||||
REG_WR(marb, regi_marb, rw_ack_intr, ack_intr);
|
||||
|
||||
printk("IRQ occured at %lX\n", regs->erp);
|
||||
|
||||
if (watch->cb)
|
||||
watch->cb();
|
||||
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
#include <linux/sched.h>
|
||||
#include <asm/thread_info.h>
|
||||
|
||||
/*
|
||||
* Generate definitions needed by assembly language modules.
|
||||
* This code generates raw asm output which is post-processed to extract
|
||||
* and format the required data.
|
||||
*/
|
||||
|
||||
#define DEFINE(sym, val) \
|
||||
asm volatile("\n->" #sym " %0 " #val : : "i" (val))
|
||||
|
||||
#define BLANK() asm volatile("\n->" : : )
|
||||
|
||||
int main(void)
|
||||
{
|
||||
#define ENTRY(entry) DEFINE(PT_ ## entry, offsetof(struct pt_regs, entry))
|
||||
ENTRY(orig_r10);
|
||||
ENTRY(r13);
|
||||
ENTRY(r12);
|
||||
ENTRY(r11);
|
||||
ENTRY(r10);
|
||||
ENTRY(r9);
|
||||
ENTRY(acr);
|
||||
ENTRY(srs);
|
||||
ENTRY(mof);
|
||||
ENTRY(ccs);
|
||||
ENTRY(srp);
|
||||
BLANK();
|
||||
#undef ENTRY
|
||||
#define ENTRY(entry) DEFINE(TI_ ## entry, offsetof(struct thread_info, entry))
|
||||
ENTRY(task);
|
||||
ENTRY(flags);
|
||||
ENTRY(preempt_count);
|
||||
BLANK();
|
||||
#undef ENTRY
|
||||
#define ENTRY(entry) DEFINE(THREAD_ ## entry, offsetof(struct thread_struct, entry))
|
||||
ENTRY(ksp);
|
||||
ENTRY(usp);
|
||||
ENTRY(ccs);
|
||||
BLANK();
|
||||
#undef ENTRY
|
||||
#define ENTRY(entry) DEFINE(TASK_ ## entry, offsetof(struct task_struct, entry))
|
||||
ENTRY(pid);
|
||||
BLANK();
|
||||
DEFINE(LCLONE_VM, CLONE_VM);
|
||||
DEFINE(LCLONE_UNTRACED, CLONE_UNTRACED);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/irq.h>
|
||||
#include <asm/arch/dma.h>
|
||||
#include <asm/arch/intmem.h>
|
||||
#include <asm/arch/pinmux.h>
|
||||
|
||||
/* Functions for allocating DMA channels */
|
||||
EXPORT_SYMBOL(crisv32_request_dma);
|
||||
EXPORT_SYMBOL(crisv32_free_dma);
|
||||
|
||||
/* Functions for handling internal RAM */
|
||||
EXPORT_SYMBOL(crisv32_intmem_alloc);
|
||||
EXPORT_SYMBOL(crisv32_intmem_free);
|
||||
EXPORT_SYMBOL(crisv32_intmem_phys_to_virt);
|
||||
EXPORT_SYMBOL(crisv32_intmem_virt_to_phys);
|
||||
|
||||
/* Functions for handling pinmux */
|
||||
EXPORT_SYMBOL(crisv32_pinmux_alloc);
|
||||
EXPORT_SYMBOL(crisv32_pinmux_dealloc);
|
||||
|
||||
/* Functions masking/unmasking interrupts */
|
||||
EXPORT_SYMBOL(mask_irq);
|
||||
EXPORT_SYMBOL(unmask_irq);
|
|
@ -0,0 +1,461 @@
|
|||
/*
|
||||
* Copyright (C) 2003, Axis Communications AB.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/tty.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/hwregs/ser_defs.h>
|
||||
#include <asm/arch/hwregs/dma_defs.h>
|
||||
#include <asm/arch/pinmux.h>
|
||||
|
||||
#include <asm/irq.h>
|
||||
#include <asm/arch/hwregs/intr_vect_defs.h>
|
||||
|
||||
struct dbg_port
|
||||
{
|
||||
unsigned char nbr;
|
||||
unsigned long instance;
|
||||
unsigned int started;
|
||||
unsigned long baudrate;
|
||||
unsigned char parity;
|
||||
unsigned int bits;
|
||||
};
|
||||
|
||||
struct dbg_port ports[] =
|
||||
{
|
||||
{
|
||||
0,
|
||||
regi_ser0,
|
||||
0,
|
||||
115200,
|
||||
'N',
|
||||
8
|
||||
},
|
||||
{
|
||||
1,
|
||||
regi_ser1,
|
||||
0,
|
||||
115200,
|
||||
'N',
|
||||
8
|
||||
},
|
||||
{
|
||||
2,
|
||||
regi_ser2,
|
||||
0,
|
||||
115200,
|
||||
'N',
|
||||
8
|
||||
},
|
||||
{
|
||||
3,
|
||||
regi_ser3,
|
||||
0,
|
||||
115200,
|
||||
'N',
|
||||
8
|
||||
}
|
||||
};
|
||||
static struct dbg_port *port =
|
||||
#if defined(CONFIG_ETRAX_DEBUG_PORT0)
|
||||
&ports[0];
|
||||
#elif defined(CONFIG_ETRAX_DEBUG_PORT1)
|
||||
&ports[1];
|
||||
#elif defined(CONFIG_ETRAX_DEBUG_PORT2)
|
||||
&ports[2];
|
||||
#elif defined(CONFIG_ETRAX_DEBUG_PORT3)
|
||||
&ports[3];
|
||||
#else
|
||||
NULL;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ETRAX_KGDB
|
||||
static struct dbg_port *kgdb_port =
|
||||
#if defined(CONFIG_ETRAX_KGDB_PORT0)
|
||||
&ports[0];
|
||||
#elif defined(CONFIG_ETRAX_KGDB_PORT1)
|
||||
&ports[1];
|
||||
#elif defined(CONFIG_ETRAX_KGDB_PORT2)
|
||||
&ports[2];
|
||||
#elif defined(CONFIG_ETRAX_KGDB_PORT3)
|
||||
&ports[3];
|
||||
#else
|
||||
NULL;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ETRAXFS_SIM
|
||||
extern void print_str( const char *str );
|
||||
static char buffer[1024];
|
||||
static char msg[] = "Debug: ";
|
||||
static int buffer_pos = sizeof(msg) - 1;
|
||||
#endif
|
||||
|
||||
extern struct tty_driver *serial_driver;
|
||||
|
||||
static void
|
||||
start_port(struct dbg_port* p)
|
||||
{
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
if (p->started)
|
||||
return;
|
||||
p->started = 1;
|
||||
|
||||
if (p->nbr == 1)
|
||||
crisv32_pinmux_alloc_fixed(pinmux_ser1);
|
||||
else if (p->nbr == 2)
|
||||
crisv32_pinmux_alloc_fixed(pinmux_ser2);
|
||||
else if (p->nbr == 3)
|
||||
crisv32_pinmux_alloc_fixed(pinmux_ser3);
|
||||
|
||||
/* Set up serial port registers */
|
||||
reg_ser_rw_tr_ctrl tr_ctrl = {0};
|
||||
reg_ser_rw_tr_dma_en tr_dma_en = {0};
|
||||
|
||||
reg_ser_rw_rec_ctrl rec_ctrl = {0};
|
||||
reg_ser_rw_tr_baud_div tr_baud_div = {0};
|
||||
reg_ser_rw_rec_baud_div rec_baud_div = {0};
|
||||
|
||||
tr_ctrl.base_freq = rec_ctrl.base_freq = regk_ser_f29_493;
|
||||
tr_dma_en.en = rec_ctrl.dma_mode = regk_ser_no;
|
||||
tr_baud_div.div = rec_baud_div.div = 29493000 / p->baudrate / 8;
|
||||
tr_ctrl.en = rec_ctrl.en = 1;
|
||||
|
||||
if (p->parity == 'O')
|
||||
{
|
||||
tr_ctrl.par_en = regk_ser_yes;
|
||||
tr_ctrl.par = regk_ser_odd;
|
||||
rec_ctrl.par_en = regk_ser_yes;
|
||||
rec_ctrl.par = regk_ser_odd;
|
||||
}
|
||||
else if (p->parity == 'E')
|
||||
{
|
||||
tr_ctrl.par_en = regk_ser_yes;
|
||||
tr_ctrl.par = regk_ser_even;
|
||||
rec_ctrl.par_en = regk_ser_yes;
|
||||
rec_ctrl.par = regk_ser_odd;
|
||||
}
|
||||
|
||||
if (p->bits == 7)
|
||||
{
|
||||
tr_ctrl.data_bits = regk_ser_bits7;
|
||||
rec_ctrl.data_bits = regk_ser_bits7;
|
||||
}
|
||||
|
||||
REG_WR (ser, p->instance, rw_tr_baud_div, tr_baud_div);
|
||||
REG_WR (ser, p->instance, rw_rec_baud_div, rec_baud_div);
|
||||
REG_WR (ser, p->instance, rw_tr_dma_en, tr_dma_en);
|
||||
REG_WR (ser, p->instance, rw_tr_ctrl, tr_ctrl);
|
||||
REG_WR (ser, p->instance, rw_rec_ctrl, rec_ctrl);
|
||||
}
|
||||
|
||||
/* No debug */
|
||||
#ifdef CONFIG_ETRAX_DEBUG_PORT_NULL
|
||||
|
||||
static void
|
||||
console_write(struct console *co, const char *buf, unsigned int len)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Target debug */
|
||||
#elif !defined(CONFIG_ETRAXFS_SIM)
|
||||
|
||||
static void
|
||||
console_write_direct(struct console *co, const char *buf, unsigned int len)
|
||||
{
|
||||
int i;
|
||||
reg_ser_r_stat_din stat;
|
||||
reg_ser_rw_tr_dma_en tr_dma_en, old;
|
||||
|
||||
/* Switch to manual mode */
|
||||
tr_dma_en = old = REG_RD (ser, port->instance, rw_tr_dma_en);
|
||||
if (tr_dma_en.en == regk_ser_yes) {
|
||||
tr_dma_en.en = regk_ser_no;
|
||||
REG_WR(ser, port->instance, rw_tr_dma_en, tr_dma_en);
|
||||
}
|
||||
|
||||
/* Send data */
|
||||
for (i = 0; i < len; i++) {
|
||||
/* LF -> CRLF */
|
||||
if (buf[i] == '\n') {
|
||||
do {
|
||||
stat = REG_RD (ser, port->instance, r_stat_din);
|
||||
} while (!stat.tr_rdy);
|
||||
REG_WR_INT (ser, port->instance, rw_dout, '\r');
|
||||
}
|
||||
/* Wait until transmitter is ready and send.*/
|
||||
do {
|
||||
stat = REG_RD (ser, port->instance, r_stat_din);
|
||||
} while (!stat.tr_rdy);
|
||||
REG_WR_INT (ser, port->instance, rw_dout, buf[i]);
|
||||
}
|
||||
|
||||
/* Restore mode */
|
||||
if (tr_dma_en.en != old.en)
|
||||
REG_WR(ser, port->instance, rw_tr_dma_en, old);
|
||||
}
|
||||
|
||||
static void
|
||||
console_write(struct console *co, const char *buf, unsigned int len)
|
||||
{
|
||||
if (!port)
|
||||
return;
|
||||
console_write_direct(co, buf, len);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#else
|
||||
|
||||
/* VCS debug */
|
||||
|
||||
static void
|
||||
console_write(struct console *co, const char *buf, unsigned int len)
|
||||
{
|
||||
char* pos;
|
||||
pos = memchr(buf, '\n', len);
|
||||
if (pos) {
|
||||
int l = ++pos - buf;
|
||||
memcpy(buffer + buffer_pos, buf, l);
|
||||
memcpy(buffer, msg, sizeof(msg) - 1);
|
||||
buffer[buffer_pos + l] = '\0';
|
||||
print_str(buffer);
|
||||
buffer_pos = sizeof(msg) - 1;
|
||||
if (pos - buf != len) {
|
||||
memcpy(buffer + buffer_pos, pos, len - l);
|
||||
buffer_pos += len - l;
|
||||
}
|
||||
} else {
|
||||
memcpy(buffer + buffer_pos, buf, len);
|
||||
buffer_pos += len;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int raw_printk(const char *fmt, ...)
|
||||
{
|
||||
static char buf[1024];
|
||||
int printed_len;
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
printed_len = vsnprintf(buf, sizeof(buf), fmt, args);
|
||||
va_end(args);
|
||||
console_write(NULL, buf, strlen(buf));
|
||||
return printed_len;
|
||||
}
|
||||
|
||||
void
|
||||
stupid_debug(char* buf)
|
||||
{
|
||||
console_write(NULL, buf, strlen(buf));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ETRAX_KGDB
|
||||
/* Use polling to get a single character from the kernel debug port */
|
||||
int
|
||||
getDebugChar(void)
|
||||
{
|
||||
reg_ser_rs_status_data stat;
|
||||
reg_ser_rw_ack_intr ack_intr = { 0 };
|
||||
|
||||
do {
|
||||
stat = REG_RD(ser, kgdb_instance, rs_status_data);
|
||||
} while (!stat.data_avail);
|
||||
|
||||
/* Ack the data_avail interrupt. */
|
||||
ack_intr.data_avail = 1;
|
||||
REG_WR(ser, kgdb_instance, rw_ack_intr, ack_intr);
|
||||
|
||||
return stat.data;
|
||||
}
|
||||
|
||||
/* Use polling to put a single character to the kernel debug port */
|
||||
void
|
||||
putDebugChar(int val)
|
||||
{
|
||||
reg_ser_r_status_data stat;
|
||||
do {
|
||||
stat = REG_RD (ser, kgdb_instance, r_status_data);
|
||||
} while (!stat.tr_ready);
|
||||
REG_WR (ser, kgdb_instance, rw_data_out, REG_TYPE_CONV(reg_ser_rw_data_out, int, val));
|
||||
}
|
||||
#endif /* CONFIG_ETRAX_KGDB */
|
||||
|
||||
static int __init
|
||||
console_setup(struct console *co, char *options)
|
||||
{
|
||||
char* s;
|
||||
|
||||
if (options) {
|
||||
port = &ports[co->index];
|
||||
port->baudrate = 115200;
|
||||
port->parity = 'N';
|
||||
port->bits = 8;
|
||||
port->baudrate = simple_strtoul(options, NULL, 10);
|
||||
s = options;
|
||||
while(*s >= '0' && *s <= '9')
|
||||
s++;
|
||||
if (*s) port->parity = *s++;
|
||||
if (*s) port->bits = *s++ - '0';
|
||||
port->started = 0;
|
||||
start_port(port);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This is a dummy serial device that throws away anything written to it.
|
||||
* This is used when no debug output is wanted.
|
||||
*/
|
||||
static struct tty_driver dummy_driver;
|
||||
|
||||
static int dummy_open(struct tty_struct *tty, struct file * filp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dummy_close(struct tty_struct *tty, struct file * filp)
|
||||
{
|
||||
}
|
||||
|
||||
static int dummy_write(struct tty_struct * tty,
|
||||
const unsigned char *buf, int count)
|
||||
{
|
||||
return count;
|
||||
}
|
||||
|
||||
static int
|
||||
dummy_write_room(struct tty_struct *tty)
|
||||
{
|
||||
return 8192;
|
||||
}
|
||||
|
||||
void __init
|
||||
init_dummy_console(void)
|
||||
{
|
||||
memset(&dummy_driver, 0, sizeof(struct tty_driver));
|
||||
dummy_driver.driver_name = "serial";
|
||||
dummy_driver.name = "ttyS";
|
||||
dummy_driver.major = TTY_MAJOR;
|
||||
dummy_driver.minor_start = 68;
|
||||
dummy_driver.num = 1; /* etrax100 has 4 serial ports */
|
||||
dummy_driver.type = TTY_DRIVER_TYPE_SERIAL;
|
||||
dummy_driver.subtype = SERIAL_TYPE_NORMAL;
|
||||
dummy_driver.init_termios = tty_std_termios;
|
||||
dummy_driver.init_termios.c_cflag =
|
||||
B115200 | CS8 | CREAD | HUPCL | CLOCAL; /* is normally B9600 default... */
|
||||
dummy_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
|
||||
|
||||
dummy_driver.open = dummy_open;
|
||||
dummy_driver.close = dummy_close;
|
||||
dummy_driver.write = dummy_write;
|
||||
dummy_driver.write_room = dummy_write_room;
|
||||
if (tty_register_driver(&dummy_driver))
|
||||
panic("Couldn't register dummy serial driver\n");
|
||||
}
|
||||
|
||||
static struct tty_driver*
|
||||
crisv32_console_device(struct console* co, int *index)
|
||||
{
|
||||
if (port)
|
||||
*index = port->nbr;
|
||||
return port ? serial_driver : &dummy_driver;
|
||||
}
|
||||
|
||||
static struct console sercons = {
|
||||
name : "ttyS",
|
||||
write: console_write,
|
||||
read : NULL,
|
||||
device : crisv32_console_device,
|
||||
unblank : NULL,
|
||||
setup : console_setup,
|
||||
flags : CON_PRINTBUFFER,
|
||||
index : -1,
|
||||
cflag : 0,
|
||||
next : NULL
|
||||
};
|
||||
static struct console sercons0 = {
|
||||
name : "ttyS",
|
||||
write: console_write,
|
||||
read : NULL,
|
||||
device : crisv32_console_device,
|
||||
unblank : NULL,
|
||||
setup : console_setup,
|
||||
flags : CON_PRINTBUFFER,
|
||||
index : 0,
|
||||
cflag : 0,
|
||||
next : NULL
|
||||
};
|
||||
|
||||
static struct console sercons1 = {
|
||||
name : "ttyS",
|
||||
write: console_write,
|
||||
read : NULL,
|
||||
device : crisv32_console_device,
|
||||
unblank : NULL,
|
||||
setup : console_setup,
|
||||
flags : CON_PRINTBUFFER,
|
||||
index : 1,
|
||||
cflag : 0,
|
||||
next : NULL
|
||||
};
|
||||
static struct console sercons2 = {
|
||||
name : "ttyS",
|
||||
write: console_write,
|
||||
read : NULL,
|
||||
device : crisv32_console_device,
|
||||
unblank : NULL,
|
||||
setup : console_setup,
|
||||
flags : CON_PRINTBUFFER,
|
||||
index : 2,
|
||||
cflag : 0,
|
||||
next : NULL
|
||||
};
|
||||
static struct console sercons3 = {
|
||||
name : "ttyS",
|
||||
write: console_write,
|
||||
read : NULL,
|
||||
device : crisv32_console_device,
|
||||
unblank : NULL,
|
||||
setup : console_setup,
|
||||
flags : CON_PRINTBUFFER,
|
||||
index : 3,
|
||||
cflag : 0,
|
||||
next : NULL
|
||||
};
|
||||
|
||||
/* Register console for printk's, etc. */
|
||||
int __init
|
||||
init_etrax_debug(void)
|
||||
{
|
||||
static int first = 1;
|
||||
|
||||
if (!first) {
|
||||
unregister_console(&sercons);
|
||||
register_console(&sercons0);
|
||||
register_console(&sercons1);
|
||||
register_console(&sercons2);
|
||||
register_console(&sercons3);
|
||||
init_dummy_console();
|
||||
return 0;
|
||||
}
|
||||
first = 0;
|
||||
register_console(&sercons);
|
||||
start_port(port);
|
||||
|
||||
#ifdef CONFIG_ETRAX_KGDB
|
||||
start_port(kgdb_port);
|
||||
#endif /* CONFIG_ETRAX_KGDB */
|
||||
return 0;
|
||||
}
|
||||
|
||||
__initcall(init_etrax_debug);
|
|
@ -0,0 +1,224 @@
|
|||
/* Wrapper for DMA channel allocator that starts clocks etc */
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <asm/dma.h>
|
||||
#include <asm/arch/hwregs/reg_map.h>
|
||||
#include <asm/arch/hwregs/reg_rdwr.h>
|
||||
#include <asm/arch/hwregs/marb_defs.h>
|
||||
#include <asm/arch/hwregs/config_defs.h>
|
||||
#include <asm/arch/hwregs/strmux_defs.h>
|
||||
#include <linux/errno.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/arch/arbiter.h>
|
||||
|
||||
static char used_dma_channels[MAX_DMA_CHANNELS];
|
||||
static const char * used_dma_channels_users[MAX_DMA_CHANNELS];
|
||||
|
||||
static DEFINE_SPINLOCK(dma_lock);
|
||||
|
||||
int crisv32_request_dma(unsigned int dmanr, const char * device_id,
|
||||
unsigned options, unsigned int bandwidth,
|
||||
enum dma_owner owner)
|
||||
{
|
||||
unsigned long flags;
|
||||
reg_config_rw_clk_ctrl clk_ctrl;
|
||||
reg_strmux_rw_cfg strmux_cfg;
|
||||
|
||||
if (crisv32_arbiter_allocate_bandwith(dmanr,
|
||||
options & DMA_INT_MEM ? INT_REGION : EXT_REGION,
|
||||
bandwidth))
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_irqsave(&dma_lock, flags);
|
||||
|
||||
if (used_dma_channels[dmanr]) {
|
||||
spin_unlock_irqrestore(&dma_lock, flags);
|
||||
if (options & DMA_VERBOSE_ON_ERROR) {
|
||||
printk("Failed to request DMA %i for %s, already allocated by %s\n", dmanr, device_id, used_dma_channels_users[dmanr]);
|
||||
}
|
||||
if (options & DMA_PANIC_ON_ERROR)
|
||||
panic("request_dma error!");
|
||||
return -EBUSY;
|
||||
}
|
||||
clk_ctrl = REG_RD(config, regi_config, rw_clk_ctrl);
|
||||
strmux_cfg = REG_RD(strmux, regi_strmux, rw_cfg);
|
||||
|
||||
switch(dmanr)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
clk_ctrl.dma01_eth0 = 1;
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
clk_ctrl.dma23 = 1;
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
clk_ctrl.dma45 = 1;
|
||||
break;
|
||||
case 6:
|
||||
case 7:
|
||||
clk_ctrl.dma67 = 1;
|
||||
break;
|
||||
case 8:
|
||||
case 9:
|
||||
clk_ctrl.dma89_strcop = 1;
|
||||
break;
|
||||
#if MAX_DMA_CHANNELS-1 != 9
|
||||
#error Check dma.c
|
||||
#endif
|
||||
default:
|
||||
spin_unlock_irqrestore(&dma_lock, flags);
|
||||
if (options & DMA_VERBOSE_ON_ERROR) {
|
||||
printk("Failed to request DMA %i for %s, only 0-%i valid)\n", dmanr, device_id, MAX_DMA_CHANNELS-1);
|
||||
}
|
||||
|
||||
if (options & DMA_PANIC_ON_ERROR)
|
||||
panic("request_dma error!");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch(owner)
|
||||
{
|
||||
case dma_eth0:
|
||||
if (dmanr == 0)
|
||||
strmux_cfg.dma0 = regk_strmux_eth0;
|
||||
else if (dmanr == 1)
|
||||
strmux_cfg.dma1 = regk_strmux_eth0;
|
||||
else
|
||||
panic("Invalid DMA channel for eth0\n");
|
||||
break;
|
||||
case dma_eth1:
|
||||
if (dmanr == 6)
|
||||
strmux_cfg.dma6 = regk_strmux_eth1;
|
||||
else if (dmanr == 7)
|
||||
strmux_cfg.dma7 = regk_strmux_eth1;
|
||||
else
|
||||
panic("Invalid DMA channel for eth1\n");
|
||||
break;
|
||||
case dma_iop0:
|
||||
if (dmanr == 2)
|
||||
strmux_cfg.dma2 = regk_strmux_iop0;
|
||||
else if (dmanr == 3)
|
||||
strmux_cfg.dma3 = regk_strmux_iop0;
|
||||
else
|
||||
panic("Invalid DMA channel for iop0\n");
|
||||
break;
|
||||
case dma_iop1:
|
||||
if (dmanr == 4)
|
||||
strmux_cfg.dma4 = regk_strmux_iop1;
|
||||
else if (dmanr == 5)
|
||||
strmux_cfg.dma5 = regk_strmux_iop1;
|
||||
else
|
||||
panic("Invalid DMA channel for iop1\n");
|
||||
break;
|
||||
case dma_ser0:
|
||||
if (dmanr == 6)
|
||||
strmux_cfg.dma6 = regk_strmux_ser0;
|
||||
else if (dmanr == 7)
|
||||
strmux_cfg.dma7 = regk_strmux_ser0;
|
||||
else
|
||||
panic("Invalid DMA channel for ser0\n");
|
||||
break;
|
||||
case dma_ser1:
|
||||
if (dmanr == 4)
|
||||
strmux_cfg.dma4 = regk_strmux_ser1;
|
||||
else if (dmanr == 5)
|
||||
strmux_cfg.dma5 = regk_strmux_ser1;
|
||||
else
|
||||
panic("Invalid DMA channel for ser1\n");
|
||||
break;
|
||||
case dma_ser2:
|
||||
if (dmanr == 2)
|
||||
strmux_cfg.dma2 = regk_strmux_ser2;
|
||||
else if (dmanr == 3)
|
||||
strmux_cfg.dma3 = regk_strmux_ser2;
|
||||
else
|
||||
panic("Invalid DMA channel for ser2\n");
|
||||
break;
|
||||
case dma_ser3:
|
||||
if (dmanr == 8)
|
||||
strmux_cfg.dma8 = regk_strmux_ser3;
|
||||
else if (dmanr == 9)
|
||||
strmux_cfg.dma9 = regk_strmux_ser3;
|
||||
else
|
||||
panic("Invalid DMA channel for ser3\n");
|
||||
break;
|
||||
case dma_sser0:
|
||||
if (dmanr == 4)
|
||||
strmux_cfg.dma4 = regk_strmux_sser0;
|
||||
else if (dmanr == 5)
|
||||
strmux_cfg.dma5 = regk_strmux_sser0;
|
||||
else
|
||||
panic("Invalid DMA channel for sser0\n");
|
||||
break;
|
||||
case dma_sser1:
|
||||
if (dmanr == 6)
|
||||
strmux_cfg.dma6 = regk_strmux_sser1;
|
||||
else if (dmanr == 7)
|
||||
strmux_cfg.dma7 = regk_strmux_sser1;
|
||||
else
|
||||
panic("Invalid DMA channel for sser1\n");
|
||||
break;
|
||||
case dma_ata:
|
||||
if (dmanr == 2)
|
||||
strmux_cfg.dma2 = regk_strmux_ata;
|
||||
else if (dmanr == 3)
|
||||
strmux_cfg.dma3 = regk_strmux_ata;
|
||||
else
|
||||
panic("Invalid DMA channel for ata\n");
|
||||
break;
|
||||
case dma_strp:
|
||||
if (dmanr == 8)
|
||||
strmux_cfg.dma8 = regk_strmux_strcop;
|
||||
else if (dmanr == 9)
|
||||
strmux_cfg.dma9 = regk_strmux_strcop;
|
||||
else
|
||||
panic("Invalid DMA channel for strp\n");
|
||||
break;
|
||||
case dma_ext0:
|
||||
if (dmanr == 6)
|
||||
strmux_cfg.dma6 = regk_strmux_ext0;
|
||||
else
|
||||
panic("Invalid DMA channel for ext0\n");
|
||||
break;
|
||||
case dma_ext1:
|
||||
if (dmanr == 7)
|
||||
strmux_cfg.dma7 = regk_strmux_ext1;
|
||||
else
|
||||
panic("Invalid DMA channel for ext1\n");
|
||||
break;
|
||||
case dma_ext2:
|
||||
if (dmanr == 2)
|
||||
strmux_cfg.dma2 = regk_strmux_ext2;
|
||||
else if (dmanr == 8)
|
||||
strmux_cfg.dma8 = regk_strmux_ext2;
|
||||
else
|
||||
panic("Invalid DMA channel for ext2\n");
|
||||
break;
|
||||
case dma_ext3:
|
||||
if (dmanr == 3)
|
||||
strmux_cfg.dma3 = regk_strmux_ext3;
|
||||
else if (dmanr == 9)
|
||||
strmux_cfg.dma9 = regk_strmux_ext2;
|
||||
else
|
||||
panic("Invalid DMA channel for ext2\n");
|
||||
break;
|
||||
}
|
||||
|
||||
used_dma_channels[dmanr] = 1;
|
||||
used_dma_channels_users[dmanr] = device_id;
|
||||
REG_WR(config, regi_config, rw_clk_ctrl, clk_ctrl);
|
||||
REG_WR(strmux, regi_strmux, rw_cfg, strmux_cfg);
|
||||
spin_unlock_irqrestore(&dma_lock,flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void crisv32_free_dma(unsigned int dmanr)
|
||||
{
|
||||
spin_lock(&dma_lock);
|
||||
used_dma_channels[dmanr] = 0;
|
||||
spin_unlock(&dma_lock);
|
||||
}
|
|
@ -0,0 +1,820 @@
|
|||
/*
|
||||
* Copyright (C) 2000-2003 Axis Communications AB
|
||||
*
|
||||
* Authors: Bjorn Wesen (bjornw@axis.com)
|
||||
* Tobias Anderberg (tobiasa@axis.com), CRISv32 port.
|
||||
*
|
||||
* Code for 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/config.h>
|
||||
#include <linux/linkage.h>
|
||||
#include <linux/sys.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/thread_info.h>
|
||||
#include <asm/arch/offset.h>
|
||||
|
||||
#include <asm/arch/hwregs/asm/reg_map_asm.h>
|
||||
#include <asm/arch/hwregs/asm/intr_vect_defs_asm.h>
|
||||
|
||||
;; Exported functions.
|
||||
.globl system_call
|
||||
.globl ret_from_intr
|
||||
.globl ret_from_fork
|
||||
.globl resume
|
||||
.globl multiple_interrupt
|
||||
.globl nmi_interrupt
|
||||
.globl spurious_interrupt
|
||||
.globl do_sigtrap
|
||||
.globl gdb_handle_exception
|
||||
.globl sys_call_table
|
||||
|
||||
; Check if preemptive kernel scheduling should be done.
|
||||
#ifdef CONFIG_PREEMPT
|
||||
_resume_kernel:
|
||||
di
|
||||
; Load current task struct.
|
||||
movs.w -8192, $r0 ; THREAD_SIZE = 8192
|
||||
and.d $sp, $r0
|
||||
|
||||
addoq +TI_preempt_count, $r0, $acr
|
||||
move.d [$acr], $r10 ; Preemption disabled?
|
||||
bne _Rexit
|
||||
nop
|
||||
|
||||
_need_resched:
|
||||
addoq +TI_flags, $r0, $acr
|
||||
move.d [$acr], $r10
|
||||
btstq TIF_NEED_RESCHED, $r10 ; Check if need_resched is set.
|
||||
bpl _Rexit
|
||||
nop
|
||||
|
||||
; Do preemptive kernel scheduling.
|
||||
jsr preempt_schedule_irq
|
||||
nop
|
||||
|
||||
; Load new task struct.
|
||||
movs.w -8192, $r0 ; THREAD_SIZE = 8192.
|
||||
and.d $sp, $r0
|
||||
|
||||
; One more time with new task.
|
||||
ba _need_resched
|
||||
nop
|
||||
#else
|
||||
#define _resume_kernel _Rexit
|
||||
#endif
|
||||
|
||||
; Called at exit from fork. schedule_tail must be called to drop
|
||||
; spinlock if CONFIG_PREEMPT.
|
||||
ret_from_fork:
|
||||
jsr schedule_tail
|
||||
nop
|
||||
ba ret_from_sys_call
|
||||
nop
|
||||
|
||||
ret_from_intr:
|
||||
;; Check for resched if preemptive kernel, or if we're going back to
|
||||
;; user-mode. This test matches the user_regs(regs) macro. Don't simply
|
||||
;; test CCS since that doesn't necessarily reflect what mode we'll
|
||||
;; return into.
|
||||
addoq +PT_ccs, $sp, $acr
|
||||
move.d [$acr], $r0
|
||||
btstq 16, $r0 ; User-mode flag.
|
||||
bpl _resume_kernel
|
||||
|
||||
; Note that di below is in delay slot.
|
||||
|
||||
_resume_userspace:
|
||||
di ; So need_resched and sigpending don't change.
|
||||
|
||||
movs.w -8192, $r0 ; THREAD_SIZE == 8192
|
||||
and.d $sp, $r0
|
||||
|
||||
addoq +TI_flags, $r0, $acr ; current->work
|
||||
move.d [$acr], $r10
|
||||
and.d _TIF_WORK_MASK, $r10 ; Work to be done on return?
|
||||
bne _work_pending
|
||||
nop
|
||||
ba _Rexit
|
||||
nop
|
||||
|
||||
;; The system_call is called by a BREAK instruction, which looks pretty
|
||||
;; much like any other exception.
|
||||
;;
|
||||
;; System calls can't be made from interrupts but we still stack ERP
|
||||
;; to have a complete stack frame.
|
||||
;;
|
||||
;; In r9 we have the wanted syscall number. Arguments come in r10,r11,r12,
|
||||
;; r13,mof,srp
|
||||
;;
|
||||
;; 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.
|
||||
subq 12, $sp ; Skip EXS, EDA.
|
||||
move $erp, [$sp]
|
||||
subq 4, $sp
|
||||
move $srp, [$sp]
|
||||
subq 4, $sp
|
||||
move $ccs, [$sp]
|
||||
subq 4, $sp
|
||||
ei ; Allow IRQs while handling system call
|
||||
move $spc, [$sp]
|
||||
subq 4, $sp
|
||||
move $mof, [$sp]
|
||||
subq 4, $sp
|
||||
move $srs, [$sp]
|
||||
subq 4, $sp
|
||||
move.d $acr, [$sp]
|
||||
subq 14*4, $sp ; Make room for R0-R13.
|
||||
movem $r13, [$sp] ; Push R0-R13
|
||||
subq 4, $sp
|
||||
move.d $r10, [$sp] ; Push orig_r10.
|
||||
|
||||
; Set S-bit when kernel debugging to keep hardware breakpoints active.
|
||||
#ifdef CONFIG_ETRAX_KGDB
|
||||
move $ccs, $r0
|
||||
or.d (1<<9), $r0
|
||||
move $r0, $ccs
|
||||
#endif
|
||||
|
||||
movs.w -ENOSYS, $r0
|
||||
addoq +PT_r10, $sp, $acr
|
||||
move.d $r0, [$acr]
|
||||
|
||||
;; Check if this process is syscall-traced.
|
||||
movs.w -8192, $r0 ; THREAD_SIZE == 8192
|
||||
and.d $sp, $r0
|
||||
|
||||
addoq +TI_flags, $r0, $acr
|
||||
move.d [$acr], $r0
|
||||
btstq TIF_SYSCALL_TRACE, $r0
|
||||
bmi _syscall_trace_entry
|
||||
nop
|
||||
|
||||
_syscall_traced:
|
||||
;; Check for sanity in the requested syscall number.
|
||||
cmpu.w NR_syscalls, $r9
|
||||
bhs ret_from_sys_call
|
||||
lslq 2, $r9 ; Multiply by 4, in the delay slot.
|
||||
|
||||
;; The location on the stack for the register structure is passed as a
|
||||
;; seventh argument. Some system calls need this.
|
||||
move.d $sp, $r0
|
||||
subq 4, $sp
|
||||
move.d $r0, [$sp]
|
||||
|
||||
;; The registers carrying parameters (R10-R13) are intact. The optional
|
||||
;; fifth and sixth parameters is in MOF and SRP respectivly. Put them
|
||||
;; back on the stack.
|
||||
subq 4, $sp
|
||||
move $srp, [$sp]
|
||||
subq 4, $sp
|
||||
move $mof, [$sp]
|
||||
|
||||
;; Actually to the system call.
|
||||
addo.d +sys_call_table, $r9, $acr
|
||||
move.d [$acr], $acr
|
||||
jsr $acr
|
||||
nop
|
||||
|
||||
addq 3*4, $sp ; Pop the mof, srp and regs parameters.
|
||||
addoq +PT_r10, $sp, $acr
|
||||
move.d $r10, [$acr] ; 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:
|
||||
;; >= 1 from syscall
|
||||
;; 0 from irq
|
||||
|
||||
;; Get the current task-struct pointer.
|
||||
movs.w -8192, $r0 ; THREAD_SIZE == 8192
|
||||
and.d $sp, $r0
|
||||
|
||||
di ; Make sure need_resched and sigpending don't change.
|
||||
|
||||
addoq +TI_flags, $r0, $acr
|
||||
move.d [$acr], $r1
|
||||
and.d _TIF_ALLWORK_MASK, $r1
|
||||
bne _syscall_exit_work
|
||||
nop
|
||||
|
||||
_Rexit:
|
||||
;; This epilogue MUST match the prologues in multiple_interrupt, irq.h
|
||||
;; and ptregs.h.
|
||||
addq 4, $sp ; Skip orig_r10.
|
||||
movem [$sp+], $r13 ; Registers R0-R13.
|
||||
move.d [$sp+], $acr
|
||||
move [$sp], $srs
|
||||
addq 4, $sp
|
||||
move [$sp+], $mof
|
||||
move [$sp+], $spc
|
||||
move [$sp+], $ccs
|
||||
move [$sp+], $srp
|
||||
move [$sp+], $erp
|
||||
addq 8, $sp ; Skip EXS, EDA.
|
||||
jump $erp
|
||||
rfe ; Restore condition code stack in delay-slot.
|
||||
|
||||
;; We get here after doing a syscall if extra work might need to be done
|
||||
;; perform syscall exit tracing if needed.
|
||||
|
||||
_syscall_exit_work:
|
||||
;; R0 contains current at this point and irq's are disabled.
|
||||
|
||||
addoq +TI_flags, $r0, $acr
|
||||
move.d [$acr], $r1
|
||||
btstq TIF_SYSCALL_TRACE, $r1
|
||||
bpl _work_pending
|
||||
nop
|
||||
ei
|
||||
move.d $r9, $r1 ; Preserve R9.
|
||||
jsr do_syscall_trace
|
||||
nop
|
||||
move.d $r1, $r9
|
||||
ba _resume_userspace
|
||||
nop
|
||||
|
||||
_work_pending:
|
||||
addoq +TI_flags, $r0, $acr
|
||||
move.d [$acr], $r10
|
||||
btstq TIF_NEED_RESCHED, $r10 ; Need resched?
|
||||
bpl _work_notifysig ; No, must be signal/notify.
|
||||
nop
|
||||
|
||||
_work_resched:
|
||||
move.d $r9, $r1 ; Preserve R9.
|
||||
jsr schedule
|
||||
nop
|
||||
move.d $r1, $r9
|
||||
di
|
||||
|
||||
addoq +TI_flags, $r0, $acr
|
||||
move.d [$acr], $r1
|
||||
and.d _TIF_WORK_MASK, $r1 ; Ignore sycall trace counter.
|
||||
beq _Rexit
|
||||
nop
|
||||
btstq TIF_NEED_RESCHED, $r1
|
||||
bmi _work_resched ; current->work.need_resched.
|
||||
nop
|
||||
|
||||
_work_notifysig:
|
||||
;; Deal with pending signals and notify-resume requests.
|
||||
|
||||
addoq +TI_flags, $r0, $acr
|
||||
move.d [$acr], $r13 ; The thread_info_flags parameter.
|
||||
move.d $r9, $r10 ; do_notify_resume syscall/irq param.
|
||||
moveq 0, $r11 ; oldset param - 0 in this case.
|
||||
move.d $sp, $r12 ; The regs param.
|
||||
jsr do_notify_resume
|
||||
nop
|
||||
|
||||
ba _Rexit
|
||||
nop
|
||||
|
||||
;; We get here as a sidetrack when we've entered a syscall with the
|
||||
;; trace-bit set. We need to call do_syscall_trace and then continue
|
||||
;; with the call.
|
||||
|
||||
_syscall_trace_entry:
|
||||
;; PT_r10 in the frame contains -ENOSYS as required, at this point.
|
||||
|
||||
jsr do_syscall_trace
|
||||
nop
|
||||
|
||||
;; Now re-enter the syscall code to do the syscall itself. We need to
|
||||
;; restore R9 here to contain the wanted syscall, and the other
|
||||
;; parameter-bearing registers.
|
||||
addoq +PT_r9, $sp, $acr
|
||||
move.d [$acr], $r9
|
||||
addoq +PT_orig_r10, $sp, $acr
|
||||
move.d [$acr], $r10 ; PT_r10 is already -ENOSYS.
|
||||
addoq +PT_r11, $sp, $acr
|
||||
move.d [$acr], $r11
|
||||
addoq +PT_r12, $sp, $acr
|
||||
move.d [$acr], $r12
|
||||
addoq +PT_r13, $sp, $acr
|
||||
move.d [$acr], $r13
|
||||
addoq +PT_mof, $sp, $acr
|
||||
move [$acr], $mof
|
||||
addoq +PT_srp, $sp, $acr
|
||||
move [$acr], $srp
|
||||
|
||||
ba _syscall_traced
|
||||
nop
|
||||
|
||||
;; Resume performs the actual task-switching, by switching stack
|
||||
;; pointers. Input arguments are:
|
||||
;;
|
||||
;; R10 = prev
|
||||
;; R11 = next
|
||||
;; R12 = thread offset in task struct.
|
||||
;;
|
||||
;; Returns old current in R10.
|
||||
|
||||
resume:
|
||||
subq 4, $sp
|
||||
move $srp, [$sp] ; Keep old/new PC on the stack.
|
||||
add.d $r12, $r10 ; R10 = current tasks tss.
|
||||
addoq +THREAD_ccs, $r10, $acr
|
||||
move $ccs, [$acr] ; Save IRQ enable state.
|
||||
di
|
||||
|
||||
addoq +THREAD_usp, $r10, $acr
|
||||
move $usp, [$acr] ; Save user-mode stackpointer.
|
||||
|
||||
;; See copy_thread for the reason why register R9 is saved.
|
||||
subq 10*4, $sp
|
||||
movem $r9, [$sp] ; Save non-scratch registers and R9.
|
||||
|
||||
addoq +THREAD_ksp, $r10, $acr
|
||||
move.d $sp, [$acr] ; Save kernel SP for old task.
|
||||
|
||||
move.d $sp, $r10 ; Return last running task in R10.
|
||||
and.d -8192, $r10 ; Get thread_info from stackpointer.
|
||||
addoq +TI_task, $r10, $acr
|
||||
move.d [$acr], $r10 ; Get task.
|
||||
add.d $r12, $r11 ; Find the new tasks tss.
|
||||
addoq +THREAD_ksp, $r11, $acr
|
||||
move.d [$acr], $sp ; Switch to new stackframe.
|
||||
movem [$sp+], $r9 ; Restore non-scratch registers and R9.
|
||||
|
||||
addoq +THREAD_usp, $r11, $acr
|
||||
move [$acr], $usp ; Restore user-mode stackpointer.
|
||||
|
||||
addoq +THREAD_ccs, $r11, $acr
|
||||
move [$acr], $ccs ; Restore IRQ enable status.
|
||||
move.d [$sp+], $acr
|
||||
jump $acr ; Restore PC.
|
||||
nop
|
||||
|
||||
nmi_interrupt:
|
||||
|
||||
;; If we receive a watchdog interrupt while it is not expected, then set
|
||||
;; up a canonical frame and dump register contents before dying.
|
||||
|
||||
;; This prologue MUST match the one in irq.h and the struct in ptregs.h!
|
||||
subq 12, $sp ; Skip EXS, EDA.
|
||||
move $nrp, [$sp]
|
||||
subq 4, $sp
|
||||
move $srp, [$sp]
|
||||
subq 4, $sp
|
||||
move $ccs, [$sp]
|
||||
subq 4, $sp
|
||||
move $spc, [$sp]
|
||||
subq 4, $sp
|
||||
move $mof, [$sp]
|
||||
subq 4, $sp
|
||||
move $srs, [$sp]
|
||||
subq 4, $sp
|
||||
move.d $acr, [$sp]
|
||||
subq 14*4, $sp ; Make room for R0-R13.
|
||||
movem $r13, [$sp] ; Push R0-R13.
|
||||
subq 4, $sp
|
||||
move.d $r10, [$sp] ; Push orig_r10.
|
||||
move.d REG_ADDR(intr_vect, regi_irq, r_nmi), $r0
|
||||
move.d [$r0], $r0
|
||||
btstq REG_BIT(intr_vect, r_nmi, watchdog), $r0
|
||||
bpl 1f
|
||||
nop
|
||||
jsr handle_watchdog_bite ; In time.c.
|
||||
move.d $sp, $r10 ; Pointer to registers
|
||||
1: btstq REG_BIT(intr_vect, r_nmi, ext), $r0
|
||||
bpl 1f
|
||||
nop
|
||||
jsr handle_nmi
|
||||
move.d $sp, $r10 ; Pointer to registers
|
||||
1: addq 4, $sp ; Skip orig_r10
|
||||
movem [$sp+], $r13
|
||||
move.d [$sp+], $acr
|
||||
move [$sp], $srs
|
||||
addq 4, $sp
|
||||
move [$sp+], $mof
|
||||
move [$sp+], $spc
|
||||
move [$sp+], $ccs
|
||||
move [$sp+], $srp
|
||||
move [$sp+], $nrp
|
||||
addq 8, $sp ; Skip EXS, EDA.
|
||||
jump $nrp
|
||||
rfn
|
||||
|
||||
.comm cause_of_death, 4 ;; Don't declare this anywhere.
|
||||
|
||||
spurious_interrupt:
|
||||
di
|
||||
jump hard_reset_now
|
||||
nop
|
||||
|
||||
;; This handles the case when multiple interrupts arrive at the same
|
||||
;; time. Jump to the first set interrupt bit in a priotiry 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!
|
||||
subq 12, $sp ; Skip EXS, EDA.
|
||||
move $erp, [$sp]
|
||||
subq 4, $sp
|
||||
move $srp, [$sp]
|
||||
subq 4, $sp
|
||||
move $ccs, [$sp]
|
||||
subq 4, $sp
|
||||
move $spc, [$sp]
|
||||
subq 4, $sp
|
||||
move $mof, [$sp]
|
||||
subq 4, $sp
|
||||
move $srs, [$sp]
|
||||
subq 4, $sp
|
||||
move.d $acr, [$sp]
|
||||
subq 14*4, $sp ; Make room for R0-R13.
|
||||
movem $r13, [$sp] ; Push R0-R13.
|
||||
subq 4, $sp
|
||||
move.d $r10, [$sp] ; Push orig_r10.
|
||||
|
||||
; Set S-bit when kernel debugging to keep hardware breakpoints active.
|
||||
#ifdef CONFIG_ETRAX_KGDB
|
||||
move $ccs, $r0
|
||||
or.d (1<<9), $r0
|
||||
move $r0, $ccs
|
||||
#endif
|
||||
|
||||
jsr crisv32_do_multiple
|
||||
move.d $sp, $r10
|
||||
jump ret_from_intr
|
||||
nop
|
||||
|
||||
do_sigtrap:
|
||||
;; Sigtraps the process that executed the BREAK instruction. Creates a
|
||||
;; frame that Rexit expects.
|
||||
subq 4, $sp
|
||||
move $eda, [$sp]
|
||||
subq 4, $sp
|
||||
move $exs, [$sp]
|
||||
subq 4, $sp
|
||||
move $erp, [$sp]
|
||||
subq 4, $sp
|
||||
move $srp, [$sp]
|
||||
subq 4, $sp
|
||||
move $ccs, [$sp]
|
||||
subq 4, $sp
|
||||
move $spc, [$sp]
|
||||
subq 4, $sp
|
||||
move $mof, [$sp]
|
||||
subq 4, $sp
|
||||
move $srs, [$sp]
|
||||
subq 4, $sp
|
||||
move.d $acr, [$sp]
|
||||
di ; Need to disable irq's at this point.
|
||||
subq 14*4, $sp ; Make room for r0-r13.
|
||||
movem $r13, [$sp] ; Push the r0-r13 registers.
|
||||
subq 4, $sp
|
||||
move.d $r10, [$sp] ; Push orig_r10.
|
||||
|
||||
movs.w -8192, $r9 ; THREAD_SIZE == 8192
|
||||
and.d $sp, $r9
|
||||
|
||||
;; thread_info as first parameter
|
||||
move.d $r9, $r10
|
||||
moveq 5, $r11 ; SIGTRAP as second argument.
|
||||
jsr ugdb_trap_user
|
||||
nop
|
||||
jump ret_from_intr ; Use the return routine for interrupts.
|
||||
nop
|
||||
|
||||
gdb_handle_exception:
|
||||
subq 4, $sp
|
||||
move.d $r0, [$sp]
|
||||
#ifdef CONFIG_ETRAX_KGDB
|
||||
move $ccs, $r0 ; U-flag not affected by previous insns.
|
||||
btstq 16, $r0 ; Test the U-flag.
|
||||
bmi _ugdb_handle_exception ; Go to user mode debugging.
|
||||
nop ; Empty delay-slot (cannot pop R0 here).
|
||||
ba kgdb_handle_exception ; Go to kernel debugging.
|
||||
move.d [$sp+], $r0 ; Restore R0 in delay slot.
|
||||
#endif
|
||||
|
||||
_ugdb_handle_exception:
|
||||
ba do_sigtrap ; SIGTRAP the offending process.
|
||||
move.d [$sp+], $r0 ; Restore R0 in delay slot.
|
||||
|
||||
.data
|
||||
|
||||
.section .rodata,"a"
|
||||
sys_call_table:
|
||||
.long sys_restart_syscall ; 0 - old "setup()" system call, used
|
||||
; for restarting.
|
||||
.long sys_exit
|
||||
.long sys_fork
|
||||
.long sys_read
|
||||
.long sys_write
|
||||
.long sys_open /* 5 */
|
||||
.long sys_close
|
||||
.long sys_waitpid
|
||||
.long sys_creat
|
||||
.long sys_link
|
||||
.long sys_unlink /* 10 */
|
||||
.long sys_execve
|
||||
.long sys_chdir
|
||||
.long sys_time
|
||||
.long sys_mknod
|
||||
.long sys_chmod /* 15 */
|
||||
.long sys_lchown16
|
||||
.long sys_ni_syscall /* old break syscall holder */
|
||||
.long sys_stat
|
||||
.long sys_lseek
|
||||
.long sys_getpid /* 20 */
|
||||
.long sys_mount
|
||||
.long sys_oldumount
|
||||
.long sys_setuid16
|
||||
.long sys_getuid16
|
||||
.long sys_stime /* 25 */
|
||||
.long sys_ptrace
|
||||
.long sys_alarm
|
||||
.long sys_fstat
|
||||
.long sys_pause
|
||||
.long sys_utime /* 30 */
|
||||
.long sys_ni_syscall /* old stty syscall holder */
|
||||
.long sys_ni_syscall /* old gtty syscall holder */
|
||||
.long sys_access
|
||||
.long sys_nice
|
||||
.long sys_ni_syscall /* 35 old ftime syscall holder */
|
||||
.long sys_sync
|
||||
.long sys_kill
|
||||
.long sys_rename
|
||||
.long sys_mkdir
|
||||
.long sys_rmdir /* 40 */
|
||||
.long sys_dup
|
||||
.long sys_pipe
|
||||
.long sys_times
|
||||
.long sys_ni_syscall /* old prof syscall holder */
|
||||
.long sys_brk /* 45 */
|
||||
.long sys_setgid16
|
||||
.long sys_getgid16
|
||||
.long sys_signal
|
||||
.long sys_geteuid16
|
||||
.long sys_getegid16 /* 50 */
|
||||
.long sys_acct
|
||||
.long sys_umount /* recycled never used phys( */
|
||||
.long sys_ni_syscall /* old lock syscall holder */
|
||||
.long sys_ioctl
|
||||
.long sys_fcntl /* 55 */
|
||||
.long sys_ni_syscall /* old mpx syscall holder */
|
||||
.long sys_setpgid
|
||||
.long sys_ni_syscall /* old ulimit syscall holder */
|
||||
.long sys_ni_syscall /* old sys_olduname holder */
|
||||
.long sys_umask /* 60 */
|
||||
.long sys_chroot
|
||||
.long sys_ustat
|
||||
.long sys_dup2
|
||||
.long sys_getppid
|
||||
.long sys_getpgrp /* 65 */
|
||||
.long sys_setsid
|
||||
.long sys_sigaction
|
||||
.long sys_sgetmask
|
||||
.long sys_ssetmask
|
||||
.long sys_setreuid16 /* 70 */
|
||||
.long sys_setregid16
|
||||
.long sys_sigsuspend
|
||||
.long sys_sigpending
|
||||
.long sys_sethostname
|
||||
.long sys_setrlimit /* 75 */
|
||||
.long sys_old_getrlimit
|
||||
.long sys_getrusage
|
||||
.long sys_gettimeofday
|
||||
.long sys_settimeofday
|
||||
.long sys_getgroups16 /* 80 */
|
||||
.long sys_setgroups16
|
||||
.long sys_select /* was old_select in Linux/E100 */
|
||||
.long sys_symlink
|
||||
.long sys_lstat
|
||||
.long sys_readlink /* 85 */
|
||||
.long sys_uselib
|
||||
.long sys_swapon
|
||||
.long sys_reboot
|
||||
.long old_readdir
|
||||
.long old_mmap /* 90 */
|
||||
.long sys_munmap
|
||||
.long sys_truncate
|
||||
.long sys_ftruncate
|
||||
.long sys_fchmod
|
||||
.long sys_fchown16 /* 95 */
|
||||
.long sys_getpriority
|
||||
.long sys_setpriority
|
||||
.long sys_ni_syscall /* old profil syscall holder */
|
||||
.long sys_statfs
|
||||
.long sys_fstatfs /* 100 */
|
||||
.long sys_ni_syscall /* sys_ioperm in i386 */
|
||||
.long sys_socketcall
|
||||
.long sys_syslog
|
||||
.long sys_setitimer
|
||||
.long sys_getitimer /* 105 */
|
||||
.long sys_newstat
|
||||
.long sys_newlstat
|
||||
.long sys_newfstat
|
||||
.long sys_ni_syscall /* old sys_uname holder */
|
||||
.long sys_ni_syscall /* sys_iopl in i386 */
|
||||
.long sys_vhangup
|
||||
.long sys_ni_syscall /* old "idle" system call */
|
||||
.long sys_ni_syscall /* vm86old in i386 */
|
||||
.long sys_wait4
|
||||
.long sys_swapoff /* 115 */
|
||||
.long sys_sysinfo
|
||||
.long sys_ipc
|
||||
.long sys_fsync
|
||||
.long sys_sigreturn
|
||||
.long sys_clone /* 120 */
|
||||
.long sys_setdomainname
|
||||
.long sys_newuname
|
||||
.long sys_ni_syscall /* sys_modify_ldt */
|
||||
.long sys_adjtimex
|
||||
.long sys_mprotect /* 125 */
|
||||
.long sys_sigprocmask
|
||||
.long sys_ni_syscall /* old "create_module" */
|
||||
.long sys_init_module
|
||||
.long sys_delete_module
|
||||
.long sys_ni_syscall /* 130: old "get_kernel_syms" */
|
||||
.long sys_quotactl
|
||||
.long sys_getpgid
|
||||
.long sys_fchdir
|
||||
.long sys_bdflush
|
||||
.long sys_sysfs /* 135 */
|
||||
.long sys_personality
|
||||
.long sys_ni_syscall /* for afs_syscall */
|
||||
.long sys_setfsuid16
|
||||
.long sys_setfsgid16
|
||||
.long sys_llseek /* 140 */
|
||||
.long sys_getdents
|
||||
.long sys_select
|
||||
.long sys_flock
|
||||
.long sys_msync
|
||||
.long sys_readv /* 145 */
|
||||
.long sys_writev
|
||||
.long sys_getsid
|
||||
.long sys_fdatasync
|
||||
.long sys_sysctl
|
||||
.long sys_mlock /* 150 */
|
||||
.long sys_munlock
|
||||
.long sys_mlockall
|
||||
.long sys_munlockall
|
||||
.long sys_sched_setparam
|
||||
.long sys_sched_getparam /* 155 */
|
||||
.long sys_sched_setscheduler
|
||||
.long sys_sched_getscheduler
|
||||
.long sys_sched_yield
|
||||
.long sys_sched_get_priority_max
|
||||
.long sys_sched_get_priority_min /* 160 */
|
||||
.long sys_sched_rr_get_interval
|
||||
.long sys_nanosleep
|
||||
.long sys_mremap
|
||||
.long sys_setresuid16
|
||||
.long sys_getresuid16 /* 165 */
|
||||
.long sys_ni_syscall /* sys_vm86 */
|
||||
.long sys_ni_syscall /* Old sys_query_module */
|
||||
.long sys_poll
|
||||
.long sys_nfsservctl
|
||||
.long sys_setresgid16 /* 170 */
|
||||
.long sys_getresgid16
|
||||
.long sys_prctl
|
||||
.long sys_rt_sigreturn
|
||||
.long sys_rt_sigaction
|
||||
.long sys_rt_sigprocmask /* 175 */
|
||||
.long sys_rt_sigpending
|
||||
.long sys_rt_sigtimedwait
|
||||
.long sys_rt_sigqueueinfo
|
||||
.long sys_rt_sigsuspend
|
||||
.long sys_pread64 /* 180 */
|
||||
.long sys_pwrite64
|
||||
.long sys_chown16
|
||||
.long sys_getcwd
|
||||
.long sys_capget
|
||||
.long sys_capset /* 185 */
|
||||
.long sys_sigaltstack
|
||||
.long sys_sendfile
|
||||
.long sys_ni_syscall /* streams1 */
|
||||
.long sys_ni_syscall /* streams2 */
|
||||
.long sys_vfork /* 190 */
|
||||
.long sys_getrlimit
|
||||
.long sys_mmap2
|
||||
.long sys_truncate64
|
||||
.long sys_ftruncate64
|
||||
.long sys_stat64 /* 195 */
|
||||
.long sys_lstat64
|
||||
.long sys_fstat64
|
||||
.long sys_lchown
|
||||
.long sys_getuid
|
||||
.long sys_getgid /* 200 */
|
||||
.long sys_geteuid
|
||||
.long sys_getegid
|
||||
.long sys_setreuid
|
||||
.long sys_setregid
|
||||
.long sys_getgroups /* 205 */
|
||||
.long sys_setgroups
|
||||
.long sys_fchown
|
||||
.long sys_setresuid
|
||||
.long sys_getresuid
|
||||
.long sys_setresgid /* 210 */
|
||||
.long sys_getresgid
|
||||
.long sys_chown
|
||||
.long sys_setuid
|
||||
.long sys_setgid
|
||||
.long sys_setfsuid /* 215 */
|
||||
.long sys_setfsgid
|
||||
.long sys_pivot_root
|
||||
.long sys_mincore
|
||||
.long sys_madvise
|
||||
.long sys_getdents64 /* 220 */
|
||||
.long sys_fcntl64
|
||||
.long sys_ni_syscall /* reserved for TUX */
|
||||
.long sys_ni_syscall
|
||||
.long sys_gettid
|
||||
.long sys_readahead /* 225 */
|
||||
.long sys_setxattr
|
||||
.long sys_lsetxattr
|
||||
.long sys_fsetxattr
|
||||
.long sys_getxattr
|
||||
.long sys_lgetxattr /* 230 */
|
||||
.long sys_fgetxattr
|
||||
.long sys_listxattr
|
||||
.long sys_llistxattr
|
||||
.long sys_flistxattr
|
||||
.long sys_removexattr /* 235 */
|
||||
.long sys_lremovexattr
|
||||
.long sys_fremovexattr
|
||||
.long sys_tkill
|
||||
.long sys_sendfile64
|
||||
.long sys_futex /* 240 */
|
||||
.long sys_sched_setaffinity
|
||||
.long sys_sched_getaffinity
|
||||
.long sys_ni_syscall /* sys_set_thread_area */
|
||||
.long sys_ni_syscall /* sys_get_thread_area */
|
||||
.long sys_io_setup /* 245 */
|
||||
.long sys_io_destroy
|
||||
.long sys_io_getevents
|
||||
.long sys_io_submit
|
||||
.long sys_io_cancel
|
||||
.long sys_fadvise64 /* 250 */
|
||||
.long sys_ni_syscall
|
||||
.long sys_exit_group
|
||||
.long sys_lookup_dcookie
|
||||
.long sys_epoll_create
|
||||
.long sys_epoll_ctl /* 255 */
|
||||
.long sys_epoll_wait
|
||||
.long sys_remap_file_pages
|
||||
.long sys_set_tid_address
|
||||
.long sys_timer_create
|
||||
.long sys_timer_settime /* 260 */
|
||||
.long sys_timer_gettime
|
||||
.long sys_timer_getoverrun
|
||||
.long sys_timer_delete
|
||||
.long sys_clock_settime
|
||||
.long sys_clock_gettime /* 265 */
|
||||
.long sys_clock_getres
|
||||
.long sys_clock_nanosleep
|
||||
.long sys_statfs64
|
||||
.long sys_fstatfs64
|
||||
.long sys_tgkill /* 270 */
|
||||
.long sys_utimes
|
||||
.long sys_fadvise64_64
|
||||
.long sys_ni_syscall /* sys_vserver */
|
||||
.long sys_ni_syscall /* sys_mbind */
|
||||
.long sys_ni_syscall /* 275 sys_get_mempolicy */
|
||||
.long sys_ni_syscall /* sys_set_mempolicy */
|
||||
.long sys_mq_open
|
||||
.long sys_mq_unlink
|
||||
.long sys_mq_timedsend
|
||||
.long sys_mq_timedreceive /* 280 */
|
||||
.long sys_mq_notify
|
||||
.long sys_mq_getsetattr
|
||||
.long sys_ni_syscall /* reserved for kexec */
|
||||
.long sys_waitid
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.rept NR_syscalls - (.-sys_call_table) / 4
|
||||
.long sys_ni_syscall
|
||||
.endr
|
||||
|
|
@ -0,0 +1,996 @@
|
|||
/* $Id: fasttimer.c,v 1.11 2005/01/04 11:15:46 starvik Exp $
|
||||
* linux/arch/cris/kernel/fasttimer.c
|
||||
*
|
||||
* Fast timers for ETRAX FS
|
||||
* This may be useful in other OS than Linux so use 2 space indentation...
|
||||
*
|
||||
* $Log: fasttimer.c,v $
|
||||
* Revision 1.11 2005/01/04 11:15:46 starvik
|
||||
* Don't share timer IRQ.
|
||||
*
|
||||
* Revision 1.10 2004/12/07 09:19:38 starvik
|
||||
* Corrected includes.
|
||||
* Use correct interrupt macros.
|
||||
*
|
||||
* Revision 1.9 2004/05/14 10:18:58 starvik
|
||||
* Export fast_timer_list
|
||||
*
|
||||
* Revision 1.8 2004/05/14 07:58:03 starvik
|
||||
* Merge of changes from 2.4
|
||||
*
|
||||
* Revision 1.7 2003/07/10 12:06:14 starvik
|
||||
* Return IRQ_NONE if irq wasn't handled
|
||||
*
|
||||
* Revision 1.6 2003/07/04 08:27:49 starvik
|
||||
* Merge of Linux 2.5.74
|
||||
*
|
||||
* Revision 1.5 2003/06/05 10:16:22 johana
|
||||
* New INTR_VECT macros.
|
||||
*
|
||||
* Revision 1.4 2003/06/03 08:49:45 johana
|
||||
* Fixed typo.
|
||||
*
|
||||
* Revision 1.3 2003/06/02 12:51:27 johana
|
||||
* Now compiles.
|
||||
* Commented some include files that probably can be removed.
|
||||
*
|
||||
* Revision 1.2 2003/06/02 12:09:41 johana
|
||||
* Ported to ETRAX FS using the trig interrupt instead of timer1.
|
||||
*
|
||||
* Revision 1.3 2002/12/12 08:26:32 starvik
|
||||
* Don't use C-comments inside CVS comments
|
||||
*
|
||||
* Revision 1.2 2002/12/11 15:42:02 starvik
|
||||
* Extracted v10 (ETRAX 100LX) specific stuff from arch/cris/kernel/
|
||||
*
|
||||
* Revision 1.1 2002/11/18 07:58:06 starvik
|
||||
* Fast timers (from Linux 2.4)
|
||||
*
|
||||
* Revision 1.5 2002/10/15 06:21:39 starvik
|
||||
* Added call to init_waitqueue_head
|
||||
*
|
||||
* Revision 1.4 2002/05/28 17:47:59 johana
|
||||
* Added del_fast_timer()
|
||||
*
|
||||
* Revision 1.3 2002/05/28 16:16:07 johana
|
||||
* Handle empty fast_timer_list
|
||||
*
|
||||
* Revision 1.2 2002/05/27 15:38:42 johana
|
||||
* Made it compile without warnings on Linux 2.4.
|
||||
* (includes, wait_queue, PROC_FS and snprintf)
|
||||
*
|
||||
* Revision 1.1 2002/05/27 15:32:25 johana
|
||||
* arch/etrax100/kernel/fasttimer.c v1.8 from the elinux tree.
|
||||
*
|
||||
* Revision 1.8 2001/11/27 13:50:40 pkj
|
||||
* Disable interrupts while stopping the timer and while modifying the
|
||||
* list of active timers in timer1_handler() as it may be interrupted
|
||||
* by other interrupts (e.g., the serial interrupt) which may add fast
|
||||
* timers.
|
||||
*
|
||||
* Revision 1.7 2001/11/22 11:50:32 pkj
|
||||
* * Only store information about the last 16 timers.
|
||||
* * proc_fasttimer_read() now uses an allocated buffer, since it
|
||||
* requires more space than just a page even for only writing the
|
||||
* last 16 timers. The buffer is only allocated on request, so
|
||||
* unless /proc/fasttimer is read, it is never allocated.
|
||||
* * Renamed fast_timer_started to fast_timers_started to match
|
||||
* fast_timers_added and fast_timers_expired.
|
||||
* * Some clean-up.
|
||||
*
|
||||
* Revision 1.6 2000/12/13 14:02:08 johana
|
||||
* Removed volatile for fast_timer_list
|
||||
*
|
||||
* Revision 1.5 2000/12/13 13:55:35 johana
|
||||
* Added DEBUG_LOG, added som cli() and cleanup
|
||||
*
|
||||
* Revision 1.4 2000/12/05 13:48:50 johana
|
||||
* Added range check when writing proc file, modified timer int handling
|
||||
*
|
||||
* Revision 1.3 2000/11/23 10:10:20 johana
|
||||
* More debug/logging possibilities.
|
||||
* Moved GET_JIFFIES_USEC() to timex.h and time.c
|
||||
*
|
||||
* Revision 1.2 2000/11/01 13:41:04 johana
|
||||
* Clean up and bugfixes.
|
||||
* Created new do_gettimeofday_fast() that gets a timeval struct
|
||||
* with time based on jiffies and *R_TIMER0_DATA, uses a table
|
||||
* for fast conversion of timer value to microseconds.
|
||||
* (Much faster the standard do_gettimeofday() and we don't really
|
||||
* wan't to use the true time - we wan't the "uptime" so timers don't screw up
|
||||
* when we change the time.
|
||||
* TODO: Add efficient support for continuous timers as well.
|
||||
*
|
||||
* Revision 1.1 2000/10/26 15:49:16 johana
|
||||
* Added fasttimer, highresolution timers.
|
||||
*
|
||||
* Copyright (C) 2000,2001 2002, 2003 Axis Communications AB, Lund, Sweden
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/param.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <asm/irq.h>
|
||||
#include <asm/system.h>
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include <asm/arch/hwregs/reg_map.h>
|
||||
#include <asm/arch/hwregs/reg_rdwr.h>
|
||||
#include <asm/arch/hwregs/timer_defs.h>
|
||||
#include <asm/fasttimer.h>
|
||||
#include <linux/proc_fs.h>
|
||||
|
||||
/*
|
||||
* timer0 is running at 100MHz and generating jiffies timer ticks
|
||||
* at 100 or 1000 HZ.
|
||||
* fasttimer gives an API that gives timers that expire "between" the jiffies
|
||||
* giving microsecond resolution (10 ns).
|
||||
* fasttimer uses reg_timer_rw_trig register to get interrupt when
|
||||
* r_time reaches a certain value.
|
||||
*/
|
||||
|
||||
|
||||
#define DEBUG_LOG_INCLUDED
|
||||
#define FAST_TIMER_LOG
|
||||
//#define FAST_TIMER_TEST
|
||||
|
||||
#define FAST_TIMER_SANITY_CHECKS
|
||||
|
||||
#ifdef FAST_TIMER_SANITY_CHECKS
|
||||
#define SANITYCHECK(x) x
|
||||
static int sanity_failed = 0;
|
||||
#else
|
||||
#define SANITYCHECK(x)
|
||||
#endif
|
||||
|
||||
#define D1(x)
|
||||
#define D2(x)
|
||||
#define DP(x)
|
||||
|
||||
#define __INLINE__ inline
|
||||
|
||||
static int fast_timer_running = 0;
|
||||
static int fast_timers_added = 0;
|
||||
static int fast_timers_started = 0;
|
||||
static int fast_timers_expired = 0;
|
||||
static int fast_timers_deleted = 0;
|
||||
static int fast_timer_is_init = 0;
|
||||
static int fast_timer_ints = 0;
|
||||
|
||||
struct fast_timer *fast_timer_list = NULL;
|
||||
|
||||
#ifdef DEBUG_LOG_INCLUDED
|
||||
#define DEBUG_LOG_MAX 128
|
||||
static const char * debug_log_string[DEBUG_LOG_MAX];
|
||||
static unsigned long debug_log_value[DEBUG_LOG_MAX];
|
||||
static int debug_log_cnt = 0;
|
||||
static int debug_log_cnt_wrapped = 0;
|
||||
|
||||
#define DEBUG_LOG(string, value) \
|
||||
{ \
|
||||
unsigned long log_flags; \
|
||||
local_irq_save(log_flags); \
|
||||
debug_log_string[debug_log_cnt] = (string); \
|
||||
debug_log_value[debug_log_cnt] = (unsigned long)(value); \
|
||||
if (++debug_log_cnt >= DEBUG_LOG_MAX) \
|
||||
{ \
|
||||
debug_log_cnt = debug_log_cnt % DEBUG_LOG_MAX; \
|
||||
debug_log_cnt_wrapped = 1; \
|
||||
} \
|
||||
local_irq_restore(log_flags); \
|
||||
}
|
||||
#else
|
||||
#define DEBUG_LOG(string, value)
|
||||
#endif
|
||||
|
||||
|
||||
#define NUM_TIMER_STATS 16
|
||||
#ifdef FAST_TIMER_LOG
|
||||
struct fast_timer timer_added_log[NUM_TIMER_STATS];
|
||||
struct fast_timer timer_started_log[NUM_TIMER_STATS];
|
||||
struct fast_timer timer_expired_log[NUM_TIMER_STATS];
|
||||
#endif
|
||||
|
||||
int timer_div_settings[NUM_TIMER_STATS];
|
||||
int timer_delay_settings[NUM_TIMER_STATS];
|
||||
|
||||
|
||||
static void
|
||||
timer_trig_handler(void);
|
||||
|
||||
|
||||
|
||||
/* Not true gettimeofday, only checks the jiffies (uptime) + useconds */
|
||||
void __INLINE__ do_gettimeofday_fast(struct timeval *tv)
|
||||
{
|
||||
unsigned long sec = jiffies;
|
||||
unsigned long usec = GET_JIFFIES_USEC();
|
||||
|
||||
usec += (sec % HZ) * (1000000 / HZ);
|
||||
sec = sec / HZ;
|
||||
|
||||
if (usec > 1000000)
|
||||
{
|
||||
usec -= 1000000;
|
||||
sec++;
|
||||
}
|
||||
tv->tv_sec = sec;
|
||||
tv->tv_usec = usec;
|
||||
}
|
||||
|
||||
int __INLINE__ timeval_cmp(struct timeval *t0, struct timeval *t1)
|
||||
{
|
||||
if (t0->tv_sec < t1->tv_sec)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if (t0->tv_sec > t1->tv_sec)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (t0->tv_usec < t1->tv_usec)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if (t0->tv_usec > t1->tv_usec)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called with ints off */
|
||||
void __INLINE__ start_timer_trig(unsigned long delay_us)
|
||||
{
|
||||
reg_timer_rw_ack_intr ack_intr = { 0 };
|
||||
reg_timer_rw_intr_mask intr_mask;
|
||||
reg_timer_rw_trig trig;
|
||||
reg_timer_rw_trig_cfg trig_cfg = { 0 };
|
||||
reg_timer_r_time r_time;
|
||||
|
||||
r_time = REG_RD(timer, regi_timer, r_time);
|
||||
|
||||
D1(printk("start_timer_trig : %d us freq: %i div: %i\n",
|
||||
delay_us, freq_index, div));
|
||||
/* Clear trig irq */
|
||||
intr_mask = REG_RD(timer, regi_timer, rw_intr_mask);
|
||||
intr_mask.trig = 0;
|
||||
REG_WR(timer, regi_timer, rw_intr_mask, intr_mask);
|
||||
|
||||
/* Set timer values */
|
||||
/* r_time is 100MHz (10 ns resolution) */
|
||||
trig = r_time + delay_us*(1000/10);
|
||||
|
||||
timer_div_settings[fast_timers_started % NUM_TIMER_STATS] = trig;
|
||||
timer_delay_settings[fast_timers_started % NUM_TIMER_STATS] = delay_us;
|
||||
|
||||
/* Ack interrupt */
|
||||
ack_intr.trig = 1;
|
||||
REG_WR(timer, regi_timer, rw_ack_intr, ack_intr);
|
||||
|
||||
/* Start timer */
|
||||
REG_WR(timer, regi_timer, rw_trig, trig);
|
||||
trig_cfg.tmr = regk_timer_time;
|
||||
REG_WR(timer, regi_timer, rw_trig_cfg, trig_cfg);
|
||||
|
||||
/* Check if we have already passed the trig time */
|
||||
r_time = REG_RD(timer, regi_timer, r_time);
|
||||
if (r_time < trig) {
|
||||
/* No, Enable trig irq */
|
||||
intr_mask = REG_RD(timer, regi_timer, rw_intr_mask);
|
||||
intr_mask.trig = 1;
|
||||
REG_WR(timer, regi_timer, rw_intr_mask, intr_mask);
|
||||
fast_timers_started++;
|
||||
fast_timer_running = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We have passed the time, disable trig point, ack intr */
|
||||
trig_cfg.tmr = regk_timer_off;
|
||||
REG_WR(timer, regi_timer, rw_trig_cfg, trig_cfg);
|
||||
REG_WR(timer, regi_timer, rw_ack_intr, ack_intr);
|
||||
/* call the int routine directly */
|
||||
timer_trig_handler();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* In version 1.4 this function takes 27 - 50 us */
|
||||
void start_one_shot_timer(struct fast_timer *t,
|
||||
fast_timer_function_type *function,
|
||||
unsigned long data,
|
||||
unsigned long delay_us,
|
||||
const char *name)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct fast_timer *tmp;
|
||||
|
||||
D1(printk("sft %s %d us\n", name, delay_us));
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
do_gettimeofday_fast(&t->tv_set);
|
||||
tmp = fast_timer_list;
|
||||
|
||||
SANITYCHECK({ /* Check so this is not in the list already... */
|
||||
while (tmp != NULL)
|
||||
{
|
||||
if (tmp == t)
|
||||
{
|
||||
printk("timer name: %s data: 0x%08lX already in list!\n", name, data);
|
||||
sanity_failed++;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp = tmp->next;
|
||||
}
|
||||
}
|
||||
tmp = fast_timer_list;
|
||||
});
|
||||
|
||||
t->delay_us = delay_us;
|
||||
t->function = function;
|
||||
t->data = data;
|
||||
t->name = name;
|
||||
|
||||
t->tv_expires.tv_usec = t->tv_set.tv_usec + delay_us % 1000000;
|
||||
t->tv_expires.tv_sec = t->tv_set.tv_sec + delay_us / 1000000;
|
||||
if (t->tv_expires.tv_usec > 1000000)
|
||||
{
|
||||
t->tv_expires.tv_usec -= 1000000;
|
||||
t->tv_expires.tv_sec++;
|
||||
}
|
||||
#ifdef FAST_TIMER_LOG
|
||||
timer_added_log[fast_timers_added % NUM_TIMER_STATS] = *t;
|
||||
#endif
|
||||
fast_timers_added++;
|
||||
|
||||
/* Check if this should timeout before anything else */
|
||||
if (tmp == NULL || timeval_cmp(&t->tv_expires, &tmp->tv_expires) < 0)
|
||||
{
|
||||
/* Put first in list and modify the timer value */
|
||||
t->prev = NULL;
|
||||
t->next = fast_timer_list;
|
||||
if (fast_timer_list)
|
||||
{
|
||||
fast_timer_list->prev = t;
|
||||
}
|
||||
fast_timer_list = t;
|
||||
#ifdef FAST_TIMER_LOG
|
||||
timer_started_log[fast_timers_started % NUM_TIMER_STATS] = *t;
|
||||
#endif
|
||||
start_timer_trig(delay_us);
|
||||
} else {
|
||||
/* Put in correct place in list */
|
||||
while (tmp->next &&
|
||||
timeval_cmp(&t->tv_expires, &tmp->next->tv_expires) > 0)
|
||||
{
|
||||
tmp = tmp->next;
|
||||
}
|
||||
/* Insert t after tmp */
|
||||
t->prev = tmp;
|
||||
t->next = tmp->next;
|
||||
if (tmp->next)
|
||||
{
|
||||
tmp->next->prev = t;
|
||||
}
|
||||
tmp->next = t;
|
||||
}
|
||||
|
||||
D2(printk("start_one_shot_timer: %d us done\n", delay_us));
|
||||
|
||||
local_irq_restore(flags);
|
||||
} /* start_one_shot_timer */
|
||||
|
||||
static inline int fast_timer_pending (const struct fast_timer * t)
|
||||
{
|
||||
return (t->next != NULL) || (t->prev != NULL) || (t == fast_timer_list);
|
||||
}
|
||||
|
||||
static inline int detach_fast_timer (struct fast_timer *t)
|
||||
{
|
||||
struct fast_timer *next, *prev;
|
||||
if (!fast_timer_pending(t))
|
||||
return 0;
|
||||
next = t->next;
|
||||
prev = t->prev;
|
||||
if (next)
|
||||
next->prev = prev;
|
||||
if (prev)
|
||||
prev->next = next;
|
||||
else
|
||||
fast_timer_list = next;
|
||||
fast_timers_deleted++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int del_fast_timer(struct fast_timer * t)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
local_irq_save(flags);
|
||||
ret = detach_fast_timer(t);
|
||||
t->next = t->prev = NULL;
|
||||
local_irq_restore(flags);
|
||||
return ret;
|
||||
} /* del_fast_timer */
|
||||
|
||||
|
||||
/* Interrupt routines or functions called in interrupt context */
|
||||
|
||||
/* Timer interrupt handler for trig interrupts */
|
||||
|
||||
static irqreturn_t
|
||||
timer_trig_interrupt(int irq, void *dev_id, struct pt_regs *regs)
|
||||
{
|
||||
reg_timer_r_masked_intr masked_intr;
|
||||
|
||||
/* Check if the timer interrupt is for us (a trig int) */
|
||||
masked_intr = REG_RD(timer, regi_timer, r_masked_intr);
|
||||
if (!masked_intr.trig)
|
||||
return IRQ_NONE;
|
||||
timer_trig_handler();
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void timer_trig_handler(void)
|
||||
{
|
||||
reg_timer_rw_ack_intr ack_intr = { 0 };
|
||||
reg_timer_rw_intr_mask intr_mask;
|
||||
reg_timer_rw_trig_cfg trig_cfg = { 0 };
|
||||
struct fast_timer *t;
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
/* Clear timer trig interrupt */
|
||||
intr_mask = REG_RD(timer, regi_timer, rw_intr_mask);
|
||||
intr_mask.trig = 0;
|
||||
REG_WR(timer, regi_timer, rw_intr_mask, intr_mask);
|
||||
|
||||
/* First stop timer, then ack interrupt */
|
||||
/* Stop timer */
|
||||
trig_cfg.tmr = regk_timer_off;
|
||||
REG_WR(timer, regi_timer, rw_trig_cfg, trig_cfg);
|
||||
|
||||
/* Ack interrupt */
|
||||
ack_intr.trig = 1;
|
||||
REG_WR(timer, regi_timer, rw_ack_intr, ack_intr);
|
||||
|
||||
fast_timer_running = 0;
|
||||
fast_timer_ints++;
|
||||
|
||||
local_irq_restore(flags);
|
||||
|
||||
t = fast_timer_list;
|
||||
while (t)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
/* Has it really expired? */
|
||||
do_gettimeofday_fast(&tv);
|
||||
D1(printk("t: %is %06ius\n", tv.tv_sec, tv.tv_usec));
|
||||
|
||||
if (timeval_cmp(&t->tv_expires, &tv) <= 0)
|
||||
{
|
||||
/* Yes it has expired */
|
||||
#ifdef FAST_TIMER_LOG
|
||||
timer_expired_log[fast_timers_expired % NUM_TIMER_STATS] = *t;
|
||||
#endif
|
||||
fast_timers_expired++;
|
||||
|
||||
/* Remove this timer before call, since it may reuse the timer */
|
||||
local_irq_save(flags);
|
||||
if (t->prev)
|
||||
{
|
||||
t->prev->next = t->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
fast_timer_list = t->next;
|
||||
}
|
||||
if (t->next)
|
||||
{
|
||||
t->next->prev = t->prev;
|
||||
}
|
||||
t->prev = NULL;
|
||||
t->next = NULL;
|
||||
local_irq_restore(flags);
|
||||
|
||||
if (t->function != NULL)
|
||||
{
|
||||
t->function(t->data);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG("!trimertrig %i function==NULL!\n", fast_timer_ints);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Timer is to early, let's set it again using the normal routines */
|
||||
D1(printk(".\n"));
|
||||
}
|
||||
|
||||
local_irq_save(flags);
|
||||
if ((t = fast_timer_list) != NULL)
|
||||
{
|
||||
/* Start next timer.. */
|
||||
long us;
|
||||
struct timeval tv;
|
||||
|
||||
do_gettimeofday_fast(&tv);
|
||||
us = ((t->tv_expires.tv_sec - tv.tv_sec) * 1000000 +
|
||||
t->tv_expires.tv_usec - tv.tv_usec);
|
||||
if (us > 0)
|
||||
{
|
||||
if (!fast_timer_running)
|
||||
{
|
||||
#ifdef FAST_TIMER_LOG
|
||||
timer_started_log[fast_timers_started % NUM_TIMER_STATS] = *t;
|
||||
#endif
|
||||
start_timer_trig(us);
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Timer already expired, let's handle it better late than never.
|
||||
* The normal loop handles it
|
||||
*/
|
||||
D1(printk("e! %d\n", us));
|
||||
}
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
if (!t)
|
||||
{
|
||||
D1(printk("ttrig stop!\n"));
|
||||
}
|
||||
}
|
||||
|
||||
static void wake_up_func(unsigned long data)
|
||||
{
|
||||
#ifdef DECLARE_WAITQUEUE
|
||||
wait_queue_head_t *sleep_wait_p = (wait_queue_head_t*)data;
|
||||
#else
|
||||
struct wait_queue **sleep_wait_p = (struct wait_queue **)data;
|
||||
#endif
|
||||
wake_up(sleep_wait_p);
|
||||
}
|
||||
|
||||
|
||||
/* Useful API */
|
||||
|
||||
void schedule_usleep(unsigned long us)
|
||||
{
|
||||
struct fast_timer t;
|
||||
#ifdef DECLARE_WAITQUEUE
|
||||
wait_queue_head_t sleep_wait;
|
||||
init_waitqueue_head(&sleep_wait);
|
||||
{
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
#else
|
||||
struct wait_queue *sleep_wait = NULL;
|
||||
struct wait_queue wait = { current, NULL };
|
||||
#endif
|
||||
|
||||
D1(printk("schedule_usleep(%d)\n", us));
|
||||
add_wait_queue(&sleep_wait, &wait);
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
start_one_shot_timer(&t, wake_up_func, (unsigned long)&sleep_wait, us,
|
||||
"usleep");
|
||||
schedule();
|
||||
set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&sleep_wait, &wait);
|
||||
D1(printk("done schedule_usleep(%d)\n", us));
|
||||
#ifdef DECLARE_WAITQUEUE
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
|
||||
,int *eof, void *data_unused
|
||||
#else
|
||||
,int unused
|
||||
#endif
|
||||
);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
|
||||
static struct proc_dir_entry *fasttimer_proc_entry;
|
||||
#else
|
||||
static struct proc_dir_entry fasttimer_proc_entry =
|
||||
{
|
||||
0, 9, "fasttimer",
|
||||
S_IFREG | S_IRUGO, 1, 0, 0,
|
||||
0, NULL /* ops -- default to array */,
|
||||
&proc_fasttimer_read /* get_info */,
|
||||
};
|
||||
#endif
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
||||
/* This value is very much based on testing */
|
||||
#define BIG_BUF_SIZE (500 + NUM_TIMER_STATS * 300)
|
||||
|
||||
static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
|
||||
,int *eof, void *data_unused
|
||||
#else
|
||||
,int unused
|
||||
#endif
|
||||
)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i = 0;
|
||||
int num_to_show;
|
||||
struct timeval tv;
|
||||
struct fast_timer *t, *nextt;
|
||||
static char *bigbuf = NULL;
|
||||
static unsigned long used;
|
||||
|
||||
if (!bigbuf && !(bigbuf = vmalloc(BIG_BUF_SIZE)))
|
||||
{
|
||||
used = 0;
|
||||
bigbuf[0] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!offset || !used)
|
||||
{
|
||||
do_gettimeofday_fast(&tv);
|
||||
|
||||
used = 0;
|
||||
used += sprintf(bigbuf + used, "Fast timers added: %i\n",
|
||||
fast_timers_added);
|
||||
used += sprintf(bigbuf + used, "Fast timers started: %i\n",
|
||||
fast_timers_started);
|
||||
used += sprintf(bigbuf + used, "Fast timer interrupts: %i\n",
|
||||
fast_timer_ints);
|
||||
used += sprintf(bigbuf + used, "Fast timers expired: %i\n",
|
||||
fast_timers_expired);
|
||||
used += sprintf(bigbuf + used, "Fast timers deleted: %i\n",
|
||||
fast_timers_deleted);
|
||||
used += sprintf(bigbuf + used, "Fast timer running: %s\n",
|
||||
fast_timer_running ? "yes" : "no");
|
||||
used += sprintf(bigbuf + used, "Current time: %lu.%06lu\n",
|
||||
(unsigned long)tv.tv_sec,
|
||||
(unsigned long)tv.tv_usec);
|
||||
#ifdef FAST_TIMER_SANITY_CHECKS
|
||||
used += sprintf(bigbuf + used, "Sanity failed: %i\n",
|
||||
sanity_failed);
|
||||
#endif
|
||||
used += sprintf(bigbuf + used, "\n");
|
||||
|
||||
#ifdef DEBUG_LOG_INCLUDED
|
||||
{
|
||||
int end_i = debug_log_cnt;
|
||||
i = 0;
|
||||
|
||||
if (debug_log_cnt_wrapped)
|
||||
{
|
||||
i = debug_log_cnt;
|
||||
}
|
||||
|
||||
while ((i != end_i || (debug_log_cnt_wrapped && !used)) &&
|
||||
used+100 < BIG_BUF_SIZE)
|
||||
{
|
||||
used += sprintf(bigbuf + used, debug_log_string[i],
|
||||
debug_log_value[i]);
|
||||
i = (i+1) % DEBUG_LOG_MAX;
|
||||
}
|
||||
}
|
||||
used += sprintf(bigbuf + used, "\n");
|
||||
#endif
|
||||
|
||||
num_to_show = (fast_timers_started < NUM_TIMER_STATS ? fast_timers_started:
|
||||
NUM_TIMER_STATS);
|
||||
used += sprintf(bigbuf + used, "Timers started: %i\n", fast_timers_started);
|
||||
for (i = 0; i < num_to_show && (used+100 < BIG_BUF_SIZE) ; i++)
|
||||
{
|
||||
int cur = (fast_timers_started - i - 1) % NUM_TIMER_STATS;
|
||||
|
||||
#if 1 //ndef FAST_TIMER_LOG
|
||||
used += sprintf(bigbuf + used, "div: %i delay: %i"
|
||||
"\n",
|
||||
timer_div_settings[cur],
|
||||
timer_delay_settings[cur]
|
||||
);
|
||||
#endif
|
||||
#ifdef FAST_TIMER_LOG
|
||||
t = &timer_started_log[cur];
|
||||
used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu "
|
||||
"d: %6li us data: 0x%08lX"
|
||||
"\n",
|
||||
t->name,
|
||||
(unsigned long)t->tv_set.tv_sec,
|
||||
(unsigned long)t->tv_set.tv_usec,
|
||||
(unsigned long)t->tv_expires.tv_sec,
|
||||
(unsigned long)t->tv_expires.tv_usec,
|
||||
t->delay_us,
|
||||
t->data
|
||||
);
|
||||
#endif
|
||||
}
|
||||
used += sprintf(bigbuf + used, "\n");
|
||||
|
||||
#ifdef FAST_TIMER_LOG
|
||||
num_to_show = (fast_timers_added < NUM_TIMER_STATS ? fast_timers_added:
|
||||
NUM_TIMER_STATS);
|
||||
used += sprintf(bigbuf + used, "Timers added: %i\n", fast_timers_added);
|
||||
for (i = 0; i < num_to_show && (used+100 < BIG_BUF_SIZE); i++)
|
||||
{
|
||||
t = &timer_added_log[(fast_timers_added - i - 1) % NUM_TIMER_STATS];
|
||||
used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu "
|
||||
"d: %6li us data: 0x%08lX"
|
||||
"\n",
|
||||
t->name,
|
||||
(unsigned long)t->tv_set.tv_sec,
|
||||
(unsigned long)t->tv_set.tv_usec,
|
||||
(unsigned long)t->tv_expires.tv_sec,
|
||||
(unsigned long)t->tv_expires.tv_usec,
|
||||
t->delay_us,
|
||||
t->data
|
||||
);
|
||||
}
|
||||
used += sprintf(bigbuf + used, "\n");
|
||||
|
||||
num_to_show = (fast_timers_expired < NUM_TIMER_STATS ? fast_timers_expired:
|
||||
NUM_TIMER_STATS);
|
||||
used += sprintf(bigbuf + used, "Timers expired: %i\n", fast_timers_expired);
|
||||
for (i = 0; i < num_to_show && (used+100 < BIG_BUF_SIZE); i++)
|
||||
{
|
||||
t = &timer_expired_log[(fast_timers_expired - i - 1) % NUM_TIMER_STATS];
|
||||
used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu "
|
||||
"d: %6li us data: 0x%08lX"
|
||||
"\n",
|
||||
t->name,
|
||||
(unsigned long)t->tv_set.tv_sec,
|
||||
(unsigned long)t->tv_set.tv_usec,
|
||||
(unsigned long)t->tv_expires.tv_sec,
|
||||
(unsigned long)t->tv_expires.tv_usec,
|
||||
t->delay_us,
|
||||
t->data
|
||||
);
|
||||
}
|
||||
used += sprintf(bigbuf + used, "\n");
|
||||
#endif
|
||||
|
||||
used += sprintf(bigbuf + used, "Active timers:\n");
|
||||
local_irq_save(flags);
|
||||
local_irq_save(flags);
|
||||
t = fast_timer_list;
|
||||
while (t != NULL && (used+100 < BIG_BUF_SIZE))
|
||||
{
|
||||
nextt = t->next;
|
||||
local_irq_restore(flags);
|
||||
used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu "
|
||||
"d: %6li us data: 0x%08lX"
|
||||
/* " func: 0x%08lX" */
|
||||
"\n",
|
||||
t->name,
|
||||
(unsigned long)t->tv_set.tv_sec,
|
||||
(unsigned long)t->tv_set.tv_usec,
|
||||
(unsigned long)t->tv_expires.tv_sec,
|
||||
(unsigned long)t->tv_expires.tv_usec,
|
||||
t->delay_us,
|
||||
t->data
|
||||
/* , t->function */
|
||||
);
|
||||
local_irq_disable();
|
||||
if (t->next != nextt)
|
||||
{
|
||||
printk("timer removed!\n");
|
||||
}
|
||||
t = nextt;
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
if (used - offset < len)
|
||||
{
|
||||
len = used - offset;
|
||||
}
|
||||
|
||||
memcpy(buf, bigbuf + offset, len);
|
||||
*start = buf;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
|
||||
*eof = 1;
|
||||
#endif
|
||||
|
||||
return len;
|
||||
}
|
||||
#endif /* PROC_FS */
|
||||
|
||||
#ifdef FAST_TIMER_TEST
|
||||
static volatile unsigned long i = 0;
|
||||
static volatile int num_test_timeout = 0;
|
||||
static struct fast_timer tr[10];
|
||||
static int exp_num[10];
|
||||
|
||||
static struct timeval tv_exp[100];
|
||||
|
||||
static void test_timeout(unsigned long data)
|
||||
{
|
||||
do_gettimeofday_fast(&tv_exp[data]);
|
||||
exp_num[data] = num_test_timeout;
|
||||
|
||||
num_test_timeout++;
|
||||
}
|
||||
|
||||
static void test_timeout1(unsigned long data)
|
||||
{
|
||||
do_gettimeofday_fast(&tv_exp[data]);
|
||||
exp_num[data] = num_test_timeout;
|
||||
if (data < 7)
|
||||
{
|
||||
start_one_shot_timer(&tr[i], test_timeout1, i, 1000, "timeout1");
|
||||
i++;
|
||||
}
|
||||
num_test_timeout++;
|
||||
}
|
||||
|
||||
DP(
|
||||
static char buf0[2000];
|
||||
static char buf1[2000];
|
||||
static char buf2[2000];
|
||||
static char buf3[2000];
|
||||
static char buf4[2000];
|
||||
);
|
||||
|
||||
static char buf5[6000];
|
||||
static int j_u[1000];
|
||||
|
||||
static void fast_timer_test(void)
|
||||
{
|
||||
int prev_num;
|
||||
int j;
|
||||
|
||||
struct timeval tv, tv0, tv1, tv2;
|
||||
|
||||
printk("fast_timer_test() start\n");
|
||||
do_gettimeofday_fast(&tv);
|
||||
|
||||
for (j = 0; j < 1000; j++)
|
||||
{
|
||||
j_u[j] = GET_JIFFIES_USEC();
|
||||
}
|
||||
for (j = 0; j < 100; j++)
|
||||
{
|
||||
do_gettimeofday_fast(&tv_exp[j]);
|
||||
}
|
||||
printk("fast_timer_test() %is %06i\n", tv.tv_sec, tv.tv_usec);
|
||||
|
||||
for (j = 0; j < 1000; j++)
|
||||
{
|
||||
printk("%i %i %i %i %i\n",j_u[j], j_u[j+1], j_u[j+2], j_u[j+3], j_u[j+4]);
|
||||
j += 4;
|
||||
}
|
||||
for (j = 0; j < 100; j++)
|
||||
{
|
||||
printk("%i.%i %i.%i %i.%i %i.%i %i.%i\n",
|
||||
tv_exp[j].tv_sec,tv_exp[j].tv_usec,
|
||||
tv_exp[j+1].tv_sec,tv_exp[j+1].tv_usec,
|
||||
tv_exp[j+2].tv_sec,tv_exp[j+2].tv_usec,
|
||||
tv_exp[j+3].tv_sec,tv_exp[j+3].tv_usec,
|
||||
tv_exp[j+4].tv_sec,tv_exp[j+4].tv_usec);
|
||||
j += 4;
|
||||
}
|
||||
do_gettimeofday_fast(&tv0);
|
||||
start_one_shot_timer(&tr[i], test_timeout, i, 50000, "test0");
|
||||
DP(proc_fasttimer_read(buf0, NULL, 0, 0, 0));
|
||||
i++;
|
||||
start_one_shot_timer(&tr[i], test_timeout, i, 70000, "test1");
|
||||
DP(proc_fasttimer_read(buf1, NULL, 0, 0, 0));
|
||||
i++;
|
||||
start_one_shot_timer(&tr[i], test_timeout, i, 40000, "test2");
|
||||
DP(proc_fasttimer_read(buf2, NULL, 0, 0, 0));
|
||||
i++;
|
||||
start_one_shot_timer(&tr[i], test_timeout, i, 60000, "test3");
|
||||
DP(proc_fasttimer_read(buf3, NULL, 0, 0, 0));
|
||||
i++;
|
||||
start_one_shot_timer(&tr[i], test_timeout1, i, 55000, "test4xx");
|
||||
DP(proc_fasttimer_read(buf4, NULL, 0, 0, 0));
|
||||
i++;
|
||||
do_gettimeofday_fast(&tv1);
|
||||
|
||||
proc_fasttimer_read(buf5, NULL, 0, 0, 0);
|
||||
|
||||
prev_num = num_test_timeout;
|
||||
while (num_test_timeout < i)
|
||||
{
|
||||
if (num_test_timeout != prev_num)
|
||||
{
|
||||
prev_num = num_test_timeout;
|
||||
}
|
||||
}
|
||||
do_gettimeofday_fast(&tv2);
|
||||
printk("Timers started %is %06i\n", tv0.tv_sec, tv0.tv_usec);
|
||||
printk("Timers started at %is %06i\n", tv1.tv_sec, tv1.tv_usec);
|
||||
printk("Timers done %is %06i\n", tv2.tv_sec, tv2.tv_usec);
|
||||
DP(printk("buf0:\n");
|
||||
printk(buf0);
|
||||
printk("buf1:\n");
|
||||
printk(buf1);
|
||||
printk("buf2:\n");
|
||||
printk(buf2);
|
||||
printk("buf3:\n");
|
||||
printk(buf3);
|
||||
printk("buf4:\n");
|
||||
printk(buf4);
|
||||
);
|
||||
printk("buf5:\n");
|
||||
printk(buf5);
|
||||
|
||||
printk("timers set:\n");
|
||||
for(j = 0; j<i; j++)
|
||||
{
|
||||
struct fast_timer *t = &tr[j];
|
||||
printk("%-10s set: %6is %06ius exp: %6is %06ius "
|
||||
"data: 0x%08X func: 0x%08X\n",
|
||||
t->name,
|
||||
t->tv_set.tv_sec,
|
||||
t->tv_set.tv_usec,
|
||||
t->tv_expires.tv_sec,
|
||||
t->tv_expires.tv_usec,
|
||||
t->data,
|
||||
t->function
|
||||
);
|
||||
|
||||
printk(" del: %6ius did exp: %6is %06ius as #%i error: %6li\n",
|
||||
t->delay_us,
|
||||
tv_exp[j].tv_sec,
|
||||
tv_exp[j].tv_usec,
|
||||
exp_num[j],
|
||||
(tv_exp[j].tv_sec - t->tv_expires.tv_sec)*1000000 + tv_exp[j].tv_usec - t->tv_expires.tv_usec);
|
||||
}
|
||||
proc_fasttimer_read(buf5, NULL, 0, 0, 0);
|
||||
printk("buf5 after all done:\n");
|
||||
printk(buf5);
|
||||
printk("fast_timer_test() done\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void fast_timer_init(void)
|
||||
{
|
||||
/* For some reason, request_irq() hangs when called froom time_init() */
|
||||
if (!fast_timer_is_init)
|
||||
{
|
||||
printk("fast_timer_init()\n");
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
|
||||
if ((fasttimer_proc_entry = create_proc_entry( "fasttimer", 0, 0 )))
|
||||
fasttimer_proc_entry->read_proc = proc_fasttimer_read;
|
||||
#else
|
||||
proc_register_dynamic(&proc_root, &fasttimer_proc_entry);
|
||||
#endif
|
||||
#endif /* PROC_FS */
|
||||
if(request_irq(TIMER_INTR_VECT, timer_trig_interrupt, SA_INTERRUPT,
|
||||
"fast timer int", NULL))
|
||||
{
|
||||
printk("err: timer1 irq\n");
|
||||
}
|
||||
fast_timer_is_init = 1;
|
||||
#ifdef FAST_TIMER_TEST
|
||||
printk("do test\n");
|
||||
fast_timer_test();
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -0,0 +1,448 @@
|
|||
/*
|
||||
* CRISv32 kernel startup code.
|
||||
*
|
||||
* Copyright (C) 2003, Axis Communications AB
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
|
||||
#define ASSEMBLER_MACROS_ONLY
|
||||
|
||||
/*
|
||||
* The macros found in mmu_defs_asm.h uses the ## concatenation operator, so
|
||||
* -traditional must not be used when assembling this file.
|
||||
*/
|
||||
#include <asm/arch/hwregs/reg_rdwr.h>
|
||||
#include <asm/arch/hwregs/asm/mmu_defs_asm.h>
|
||||
#include <asm/arch/hwregs/asm/reg_map_asm.h>
|
||||
#include <asm/arch/hwregs/asm/config_defs_asm.h>
|
||||
#include <asm/arch/hwregs/asm/bif_core_defs_asm.h>
|
||||
|
||||
#define CRAMFS_MAGIC 0x28cd3d45
|
||||
#define RAM_INIT_MAGIC 0x56902387
|
||||
#define COMMAND_LINE_MAGIC 0x87109563
|
||||
|
||||
;; NOTE: R8 and R9 carry information from the decompressor (if the
|
||||
;; kernel was compressed). They must not be used in the code below
|
||||
;; until they are read!
|
||||
|
||||
;; Exported symbols.
|
||||
.global etrax_irv
|
||||
.global romfs_start
|
||||
.global romfs_length
|
||||
.global romfs_in_flash
|
||||
.global swapper_pg_dir
|
||||
.global crisv32_nand_boot
|
||||
.global crisv32_nand_cramfs_offset
|
||||
|
||||
;; Dummy section to make it bootable with current VCS simulator
|
||||
#ifdef CONFIG_ETRAXFS_SIM
|
||||
.section ".boot", "ax"
|
||||
ba tstart
|
||||
nop
|
||||
#endif
|
||||
|
||||
.text
|
||||
tstart:
|
||||
;; This is the entry point of the kernel. The CPU is currently in
|
||||
;; supervisor mode.
|
||||
;;
|
||||
;; 0x00000000 if flash.
|
||||
;; 0x40004000 if DRAM.
|
||||
;;
|
||||
di
|
||||
|
||||
;; Start clocks for used blocks.
|
||||
move.d REG_ADDR(config, regi_config, rw_clk_ctrl), $r1
|
||||
move.d [$r1], $r0
|
||||
or.d REG_STATE(config, rw_clk_ctrl, cpu, yes) | \
|
||||
REG_STATE(config, rw_clk_ctrl, bif, yes) | \
|
||||
REG_STATE(config, rw_clk_ctrl, fix_io, yes), $r0
|
||||
move.d $r0, [$r1]
|
||||
|
||||
;; Set up waitstates etc
|
||||
move.d REG_ADDR(bif_core, regi_bif_core, rw_grp1_cfg), $r0
|
||||
move.d CONFIG_ETRAX_MEM_GRP1_CONFIG, $r1
|
||||
move.d $r1, [$r0]
|
||||
move.d REG_ADDR(bif_core, regi_bif_core, rw_grp2_cfg), $r0
|
||||
move.d CONFIG_ETRAX_MEM_GRP2_CONFIG, $r1
|
||||
move.d $r1, [$r0]
|
||||
move.d REG_ADDR(bif_core, regi_bif_core, rw_grp3_cfg), $r0
|
||||
move.d CONFIG_ETRAX_MEM_GRP3_CONFIG, $r1
|
||||
move.d $r1, [$r0]
|
||||
move.d REG_ADDR(bif_core, regi_bif_core, rw_grp4_cfg), $r0
|
||||
move.d CONFIG_ETRAX_MEM_GRP4_CONFIG, $r1
|
||||
move.d $r1, [$r0]
|
||||
|
||||
#ifdef CONFIG_ETRAXFS_SIM
|
||||
;; Set up minimal flash waitstates
|
||||
move.d 0, $r10
|
||||
move.d REG_ADDR(bif_core, regi_bif_core, rw_grp1_cfg), $r11
|
||||
move.d $r10, [$r11]
|
||||
#endif
|
||||
|
||||
;; Setup and enable the MMU. Use same configuration for both the data
|
||||
;; and the instruction MMU.
|
||||
;;
|
||||
;; Note; 3 cycles is needed for a bank-select to take effect. Further;
|
||||
;; bank 1 is the instruction MMU, bank 2 is the data MMU.
|
||||
#ifndef CONFIG_ETRAXFS_SIM
|
||||
move.d REG_FIELD(mmu, rw_mm_kbase_hi, base_e, 8) \
|
||||
| REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 4) \
|
||||
| REG_FIELD(mmu, rw_mm_kbase_hi, base_b, 0xb), $r0
|
||||
#else
|
||||
;; Map the virtual DRAM to the RW eprom area at address 0.
|
||||
;; Also map 0xa for the hook calls,
|
||||
move.d REG_FIELD(mmu, rw_mm_kbase_hi, base_e, 8) \
|
||||
| REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 0) \
|
||||
| REG_FIELD(mmu, rw_mm_kbase_hi, base_b, 0xb) \
|
||||
| REG_FIELD(mmu, rw_mm_kbase_hi, base_a, 0xa), $r0
|
||||
#endif
|
||||
|
||||
;; Temporary map of 0x40 -> 0x40 and 0x00 -> 0x00.
|
||||
move.d REG_FIELD(mmu, rw_mm_kbase_lo, base_4, 4) \
|
||||
| REG_FIELD(mmu, rw_mm_kbase_lo, base_0, 0), $r1
|
||||
|
||||
;; Enable certain page protections and setup linear mapping
|
||||
;; for f,e,c,b,4,0.
|
||||
#ifndef CONFIG_ETRAXFS_SIM
|
||||
move.d REG_STATE(mmu, rw_mm_cfg, we, on) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, acc, on) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, ex, on) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, inv, on) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_f, linear) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_e, linear) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_d, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_c, linear) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_b, linear) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_a, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_9, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_8, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_7, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_6, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_5, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_4, linear) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_3, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_2, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_1, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_0, linear), $r2
|
||||
#else
|
||||
move.d REG_STATE(mmu, rw_mm_cfg, we, on) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, acc, on) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, ex, on) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, inv, on) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_f, linear) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_e, linear) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_d, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_c, linear) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_b, linear) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_a, linear) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_9, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_8, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_7, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_6, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_5, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_4, linear) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_3, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_2, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_1, page) \
|
||||
| REG_STATE(mmu, rw_mm_cfg, seg_0, linear), $r2
|
||||
#endif
|
||||
|
||||
;; Update instruction MMU.
|
||||
move 1, $srs
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
move $r0, $s2 ; kbase_hi.
|
||||
move $r1, $s1 ; kbase_lo.
|
||||
move $r2, $s0 ; mm_cfg, virtual memory configuration.
|
||||
|
||||
;; Update data MMU.
|
||||
move 2, $srs
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
move $r0, $s2 ; kbase_hi.
|
||||
move $r1, $s1 ; kbase_lo
|
||||
move $r2, $s0 ; mm_cfg, virtual memory configuration.
|
||||
|
||||
;; Enable data and instruction MMU.
|
||||
move 0, $srs
|
||||
moveq 0xf, $r0 ; IMMU, DMMU, DCache, Icache on
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
move $r0, $s0
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
;; Read CPU ID
|
||||
move 0, $srs
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
move $s10, $r0
|
||||
cmpq 0, $r0
|
||||
beq master_cpu
|
||||
nop
|
||||
slave_cpu:
|
||||
; A slave waits for cpu_now_booting to be equal to CPU ID.
|
||||
move.d cpu_now_booting, $r1
|
||||
slave_wait:
|
||||
cmp.d [$r1], $r0
|
||||
bne slave_wait
|
||||
nop
|
||||
; Time to boot-up. Get stack location provided by master CPU.
|
||||
move.d smp_init_current_idle_thread, $r1
|
||||
move.d [$r1], $sp
|
||||
add.d 8192, $sp
|
||||
move.d ebp_start, $r0 ; Defined in linker-script.
|
||||
move $r0, $ebp
|
||||
jsr smp_callin
|
||||
nop
|
||||
master_cpu:
|
||||
#endif
|
||||
#ifndef CONFIG_ETRAXFS_SIM
|
||||
;; Check if starting from DRAM or flash.
|
||||
lapcq ., $r0
|
||||
and.d 0x7fffffff, $r0 ; Mask off the non-cache bit.
|
||||
cmp.d 0x10000, $r0 ; Arbitrary, something above this code.
|
||||
blo _inflash0
|
||||
nop
|
||||
#endif
|
||||
|
||||
jump _inram ; Jump to cached RAM.
|
||||
nop
|
||||
|
||||
;; Jumpgate.
|
||||
_inflash0:
|
||||
jump _inflash
|
||||
nop
|
||||
|
||||
;; Put the following in a section so that storage for it can be
|
||||
;; reclaimed after init is finished.
|
||||
.section ".init.text", "ax"
|
||||
|
||||
_inflash:
|
||||
|
||||
;; Initialize DRAM.
|
||||
cmp.d RAM_INIT_MAGIC, $r8 ; Already initialized?
|
||||
beq _dram_initialized
|
||||
nop
|
||||
|
||||
#include "../lib/dram_init.S"
|
||||
|
||||
_dram_initialized:
|
||||
;; Copy the text and data section to DRAM. This depends on that the
|
||||
;; variables used below are correctly set up by the linker script.
|
||||
;; The calculated value stored in R4 is used below.
|
||||
moveq 0, $r0 ; Source.
|
||||
move.d text_start, $r1 ; Destination.
|
||||
move.d __vmlinux_end, $r2
|
||||
move.d $r2, $r4
|
||||
sub.d $r1, $r4
|
||||
1: move.w [$r0+], $r3
|
||||
move.w $r3, [$r1+]
|
||||
cmp.d $r2, $r1
|
||||
blo 1b
|
||||
nop
|
||||
|
||||
;; Keep CRAMFS in flash.
|
||||
moveq 0, $r0
|
||||
move.d romfs_length, $r1
|
||||
move.d $r0, [$r1]
|
||||
move.d [$r4], $r0 ; cramfs_super.magic
|
||||
cmp.d CRAMFS_MAGIC, $r0
|
||||
bne 1f
|
||||
nop
|
||||
|
||||
addoq +4, $r4, $acr
|
||||
move.d [$acr], $r0
|
||||
move.d romfs_length, $r1
|
||||
move.d $r0, [$r1]
|
||||
add.d 0xf0000000, $r4 ; Add cached flash start in virtual memory.
|
||||
move.d romfs_start, $r1
|
||||
move.d $r4, [$r1]
|
||||
1: moveq 1, $r0
|
||||
move.d romfs_in_flash, $r1
|
||||
move.d $r0, [$r1]
|
||||
|
||||
jump _start_it ; Jump to cached code.
|
||||
nop
|
||||
|
||||
_inram:
|
||||
;; Check if booting from NAND flash (in that case we just remember the offset
|
||||
;; into the flash where cramfs should be).
|
||||
move.d REG_ADDR(config, regi_config, r_bootsel), $r0
|
||||
move.d [$r0], $r0
|
||||
and.d REG_MASK(config, r_bootsel, boot_mode), $r0
|
||||
cmp.d REG_STATE(config, r_bootsel, boot_mode, nand), $r0
|
||||
bne move_cramfs
|
||||
moveq 1,$r0
|
||||
move.d crisv32_nand_boot, $r1
|
||||
move.d $r0, [$r1]
|
||||
move.d crisv32_nand_cramfs_offset, $r1
|
||||
move.d $r9, [$r1]
|
||||
moveq 1, $r0
|
||||
move.d romfs_in_flash, $r1
|
||||
move.d $r0, [$r1]
|
||||
jump _start_it
|
||||
nop
|
||||
|
||||
move_cramfs:
|
||||
;; Move the cramfs after BSS.
|
||||
moveq 0, $r0
|
||||
move.d romfs_length, $r1
|
||||
move.d $r0, [$r1]
|
||||
|
||||
#ifndef CONFIG_ETRAXFS_SIM
|
||||
;; The kernel could have been unpacked to DRAM by the loader, but
|
||||
;; the cramfs image could still be inte the flash immediately
|
||||
;; following the compressed kernel image. The loaded passes the address
|
||||
;; of the bute succeeding the last compressed byte in the flash in
|
||||
;; register R9 when starting the kernel.
|
||||
cmp.d 0x0ffffff8, $r9
|
||||
bhs _no_romfs_in_flash ; R9 points outside the flash area.
|
||||
nop
|
||||
#else
|
||||
ba _no_romfs_in_flash
|
||||
nop
|
||||
#endif
|
||||
move.d [$r9], $r0 ; cramfs_super.magic
|
||||
cmp.d CRAMFS_MAGIC, $r0
|
||||
bne _no_romfs_in_flash
|
||||
nop
|
||||
|
||||
addoq +4, $r9, $acr
|
||||
move.d [$acr], $r0
|
||||
move.d romfs_length, $r1
|
||||
move.d $r0, [$r1]
|
||||
add.d 0xf0000000, $r9 ; Add cached flash start in virtual memory.
|
||||
move.d romfs_start, $r1
|
||||
move.d $r9, [$r1]
|
||||
moveq 1, $r0
|
||||
move.d romfs_in_flash, $r1
|
||||
move.d $r0, [$r1]
|
||||
|
||||
jump _start_it ; Jump to cached code.
|
||||
nop
|
||||
|
||||
_no_romfs_in_flash:
|
||||
;; Look for cramfs.
|
||||
#ifndef CONFIG_ETRAXFS_SIM
|
||||
move.d __vmlinux_end, $r0
|
||||
#else
|
||||
move.d __end, $r0
|
||||
#endif
|
||||
move.d [$r0], $r1
|
||||
cmp.d CRAMFS_MAGIC, $r1
|
||||
bne 2f
|
||||
nop
|
||||
|
||||
addoq +4, $r0, $acr
|
||||
move.d [$acr], $r2
|
||||
move.d _end, $r1
|
||||
move.d romfs_start, $r3
|
||||
move.d $r1, [$r3]
|
||||
move.d romfs_length, $r3
|
||||
move.d $r2, [$r3]
|
||||
|
||||
#ifndef CONFIG_ETRAXFS_SIM
|
||||
add.d $r2, $r0
|
||||
add.d $r2, $r1
|
||||
|
||||
lsrq 1, $r2 ; Size is in bytes, we copy words.
|
||||
addq 1, $r2
|
||||
1:
|
||||
move.w [$r0], $r3
|
||||
move.w $r3, [$r1]
|
||||
subq 2, $r0
|
||||
subq 2, $r1
|
||||
subq 1, $r2
|
||||
bne 1b
|
||||
nop
|
||||
#endif
|
||||
|
||||
2:
|
||||
moveq 0, $r0
|
||||
move.d romfs_in_flash, $r1
|
||||
move.d $r0, [$r1]
|
||||
|
||||
jump _start_it ; Jump to cached code.
|
||||
nop
|
||||
|
||||
_start_it:
|
||||
|
||||
;; Check if kernel command line is supplied
|
||||
cmp.d COMMAND_LINE_MAGIC, $r10
|
||||
bne no_command_line
|
||||
nop
|
||||
|
||||
move.d 256, $r13
|
||||
move.d cris_command_line, $r10
|
||||
or.d 0x80000000, $r11 ; Make it virtual
|
||||
1:
|
||||
move.b [$r11+], $r12
|
||||
move.b $r12, [$r10+]
|
||||
subq 1, $r13
|
||||
bne 1b
|
||||
nop
|
||||
|
||||
no_command_line:
|
||||
|
||||
;; The kernel stack contains a task structure for each task. This
|
||||
;; the initial kernel stack is in the same page as the init_task,
|
||||
;; but starts at the top of the page, i.e. + 8192 bytes.
|
||||
move.d init_thread_union + 8192, $sp
|
||||
move.d ebp_start, $r0 ; Defined in linker-script.
|
||||
move $r0, $ebp
|
||||
move.d etrax_irv, $r1 ; Set the exception base register and pointer.
|
||||
move.d $r0, [$r1]
|
||||
|
||||
#ifndef CONFIG_ETRAXFS_SIM
|
||||
;; Clear the BSS region from _bss_start to _end.
|
||||
move.d __bss_start, $r0
|
||||
move.d _end, $r1
|
||||
1: clear.d [$r0+]
|
||||
cmp.d $r1, $r0
|
||||
blo 1b
|
||||
nop
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ETRAXFS_SIM
|
||||
/* Set the watchdog timeout to something big. Will be removed when */
|
||||
/* watchdog can be disabled with command line option */
|
||||
move.d 0x7fffffff, $r10
|
||||
jsr CPU_WATCHDOG_TIMEOUT
|
||||
nop
|
||||
#endif
|
||||
|
||||
; Initialize registers to increase determinism
|
||||
move.d __bss_start, $r0
|
||||
movem [$r0], $r13
|
||||
|
||||
jump start_kernel ; Jump to start_kernel() in init/main.c.
|
||||
nop
|
||||
|
||||
.data
|
||||
etrax_irv:
|
||||
.dword 0
|
||||
romfs_start:
|
||||
.dword 0
|
||||
romfs_length:
|
||||
.dword 0
|
||||
romfs_in_flash:
|
||||
.dword 0
|
||||
crisv32_nand_boot:
|
||||
.dword 0
|
||||
crisv32_nand_cramfs_offset:
|
||||
.dword 0
|
||||
|
||||
swapper_pg_dir = 0xc0002000
|
||||
|
||||
.section ".init.data", "aw"
|
||||
|
||||
#include "../lib/hw_settings.S"
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* Helper functions for I/O pins.
|
||||
*
|
||||
* Copyright (c) 2004 Axis Communications AB.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/pinmux.h>
|
||||
#include <asm/arch/hwregs/gio_defs.h>
|
||||
|
||||
struct crisv32_ioport crisv32_ioports[] =
|
||||
{
|
||||
{
|
||||
(unsigned long*)REG_ADDR(gio, regi_gio, rw_pa_oe),
|
||||
(unsigned long*)REG_ADDR(gio, regi_gio, rw_pa_dout),
|
||||
(unsigned long*)REG_ADDR(gio, regi_gio, r_pa_din),
|
||||
8
|
||||
},
|
||||
{
|
||||
(unsigned long*)REG_ADDR(gio, regi_gio, rw_pb_oe),
|
||||
(unsigned long*)REG_ADDR(gio, regi_gio, rw_pb_dout),
|
||||
(unsigned long*)REG_ADDR(gio, regi_gio, r_pb_din),
|
||||
18
|
||||
},
|
||||
{
|
||||
(unsigned long*)REG_ADDR(gio, regi_gio, rw_pc_oe),
|
||||
(unsigned long*)REG_ADDR(gio, regi_gio, rw_pc_dout),
|
||||
(unsigned long*)REG_ADDR(gio, regi_gio, r_pc_din),
|
||||
18
|
||||
},
|
||||
{
|
||||
(unsigned long*)REG_ADDR(gio, regi_gio, rw_pd_oe),
|
||||
(unsigned long*)REG_ADDR(gio, regi_gio, rw_pd_dout),
|
||||
(unsigned long*)REG_ADDR(gio, regi_gio, r_pd_din),
|
||||
18
|
||||
},
|
||||
{
|
||||
(unsigned long*)REG_ADDR(gio, regi_gio, rw_pe_oe),
|
||||
(unsigned long*)REG_ADDR(gio, regi_gio, rw_pe_dout),
|
||||
(unsigned long*)REG_ADDR(gio, regi_gio, r_pe_din),
|
||||
18
|
||||
}
|
||||
};
|
||||
|
||||
#define NBR_OF_PORTS sizeof(crisv32_ioports)/sizeof(struct crisv32_ioport)
|
||||
|
||||
struct crisv32_iopin crisv32_led1_green;
|
||||
struct crisv32_iopin crisv32_led1_red;
|
||||
struct crisv32_iopin crisv32_led2_green;
|
||||
struct crisv32_iopin crisv32_led2_red;
|
||||
struct crisv32_iopin crisv32_led3_green;
|
||||
struct crisv32_iopin crisv32_led3_red;
|
||||
|
||||
/* Dummy port used when green LED and red LED is on the same bit */
|
||||
static unsigned long io_dummy;
|
||||
static struct crisv32_ioport dummy_port =
|
||||
{
|
||||
&io_dummy,
|
||||
&io_dummy,
|
||||
&io_dummy,
|
||||
18
|
||||
};
|
||||
static struct crisv32_iopin dummy_led =
|
||||
{
|
||||
&dummy_port,
|
||||
0
|
||||
};
|
||||
|
||||
static int __init crisv32_io_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
/* Initialize LEDs */
|
||||
ret += crisv32_io_get_name(&crisv32_led1_green, CONFIG_ETRAX_LED1G);
|
||||
ret += crisv32_io_get_name(&crisv32_led1_red, CONFIG_ETRAX_LED1R);
|
||||
ret += crisv32_io_get_name(&crisv32_led2_green, CONFIG_ETRAX_LED2G);
|
||||
ret += crisv32_io_get_name(&crisv32_led2_red, CONFIG_ETRAX_LED2R);
|
||||
ret += crisv32_io_get_name(&crisv32_led3_green, CONFIG_ETRAX_LED3G);
|
||||
ret += crisv32_io_get_name(&crisv32_led3_red, CONFIG_ETRAX_LED3R);
|
||||
crisv32_io_set_dir(&crisv32_led1_green, crisv32_io_dir_out);
|
||||
crisv32_io_set_dir(&crisv32_led1_red, crisv32_io_dir_out);
|
||||
crisv32_io_set_dir(&crisv32_led2_green, crisv32_io_dir_out);
|
||||
crisv32_io_set_dir(&crisv32_led2_red, crisv32_io_dir_out);
|
||||
crisv32_io_set_dir(&crisv32_led3_green, crisv32_io_dir_out);
|
||||
crisv32_io_set_dir(&crisv32_led3_red, crisv32_io_dir_out);
|
||||
|
||||
if (!strcmp(CONFIG_ETRAX_LED1G, CONFIG_ETRAX_LED1R))
|
||||
crisv32_led1_red = dummy_led;
|
||||
if (!strcmp(CONFIG_ETRAX_LED2G, CONFIG_ETRAX_LED2R))
|
||||
crisv32_led2_red = dummy_led;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
__initcall(crisv32_io_init);
|
||||
|
||||
int crisv32_io_get(struct crisv32_iopin* iopin,
|
||||
unsigned int port, unsigned int pin)
|
||||
{
|
||||
if (port > NBR_OF_PORTS)
|
||||
return -EINVAL;
|
||||
if (port > crisv32_ioports[port].pin_count)
|
||||
return -EINVAL;
|
||||
|
||||
iopin->bit = 1 << pin;
|
||||
iopin->port = &crisv32_ioports[port];
|
||||
|
||||
if (crisv32_pinmux_alloc(port, pin, pin, pinmux_gpio))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int crisv32_io_get_name(struct crisv32_iopin* iopin,
|
||||
char* name)
|
||||
{
|
||||
int port;
|
||||
int pin;
|
||||
|
||||
if (toupper(*name) == 'P')
|
||||
name++;
|
||||
|
||||
if (toupper(*name) < 'A' || toupper(*name) > 'E')
|
||||
return -EINVAL;
|
||||
|
||||
port = toupper(*name) - 'A';
|
||||
name++;
|
||||
pin = simple_strtoul(name, NULL, 10);
|
||||
|
||||
if (pin < 0 || pin > crisv32_ioports[port].pin_count)
|
||||
return -EINVAL;
|
||||
|
||||
iopin->bit = 1 << pin;
|
||||
iopin->port = &crisv32_ioports[port];
|
||||
|
||||
if (crisv32_pinmux_alloc(port, pin, pin, pinmux_gpio))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
/* PCI I/O access stuff */
|
||||
struct cris_io_operations* cris_iops = NULL;
|
||||
EXPORT_SYMBOL(cris_iops);
|
||||
#endif
|
||||
|
|
@ -0,0 +1,413 @@
|
|||
/*
|
||||
* Copyright (C) 2003, Axis Communications AB.
|
||||
*/
|
||||
|
||||
#include <asm/irq.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/profile.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/threads.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/kernel_stat.h>
|
||||
#include <asm/arch/hwregs/reg_map.h>
|
||||
#include <asm/arch/hwregs/reg_rdwr.h>
|
||||
#include <asm/arch/hwregs/intr_vect.h>
|
||||
#include <asm/arch/hwregs/intr_vect_defs.h>
|
||||
|
||||
#define CPU_FIXED -1
|
||||
|
||||
/* IRQ masks (refer to comment for crisv32_do_multiple) */
|
||||
#define TIMER_MASK (1 << (TIMER_INTR_VECT - FIRST_IRQ))
|
||||
#ifdef CONFIG_ETRAX_KGDB
|
||||
#if defined(CONFIG_ETRAX_KGDB_PORT0)
|
||||
#define IGNOREMASK (1 << (SER0_INTR_VECT - FIRST_IRQ))
|
||||
#elif defined(CONFIG_ETRAX_KGDB_PORT1)
|
||||
#define IGNOREMASK (1 << (SER1_INTR_VECT - FIRST_IRQ))
|
||||
#elif defined(CONFIG_ETRAX_KGB_PORT2)
|
||||
#define IGNOREMASK (1 << (SER2_INTR_VECT - FIRST_IRQ))
|
||||
#elif defined(CONFIG_ETRAX_KGDB_PORT3)
|
||||
#define IGNOREMASK (1 << (SER3_INTR_VECT - FIRST_IRQ))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
DEFINE_SPINLOCK(irq_lock);
|
||||
|
||||
struct cris_irq_allocation
|
||||
{
|
||||
int cpu; /* The CPU to which the IRQ is currently allocated. */
|
||||
cpumask_t mask; /* The CPUs to which the IRQ may be allocated. */
|
||||
};
|
||||
|
||||
struct cris_irq_allocation irq_allocations[NR_IRQS] =
|
||||
{[0 ... NR_IRQS - 1] = {0, CPU_MASK_ALL}};
|
||||
|
||||
static unsigned long irq_regs[NR_CPUS] =
|
||||
{
|
||||
regi_irq,
|
||||
#ifdef CONFIG_SMP
|
||||
regi_irq2,
|
||||
#endif
|
||||
};
|
||||
|
||||
unsigned long cpu_irq_counters[NR_CPUS];
|
||||
unsigned long irq_counters[NR_REAL_IRQS];
|
||||
|
||||
/* From irq.c. */
|
||||
extern void weird_irq(void);
|
||||
|
||||
/* From entry.S. */
|
||||
extern void system_call(void);
|
||||
extern void nmi_interrupt(void);
|
||||
extern void multiple_interrupt(void);
|
||||
extern void gdb_handle_exception(void);
|
||||
extern void i_mmu_refill(void);
|
||||
extern void i_mmu_invalid(void);
|
||||
extern void i_mmu_access(void);
|
||||
extern void i_mmu_execute(void);
|
||||
extern void d_mmu_refill(void);
|
||||
extern void d_mmu_invalid(void);
|
||||
extern void d_mmu_access(void);
|
||||
extern void d_mmu_write(void);
|
||||
|
||||
/* From kgdb.c. */
|
||||
extern void kgdb_init(void);
|
||||
extern void breakpoint(void);
|
||||
|
||||
/*
|
||||
* Build the IRQ handler stubs using macros from irq.h. First argument is the
|
||||
* IRQ number, the second argument is the corresponding bit in
|
||||
* intr_rw_vect_mask found in asm/arch/hwregs/intr_vect_defs.h.
|
||||
*/
|
||||
BUILD_IRQ(0x31, (1 << 0)) /* memarb */
|
||||
BUILD_IRQ(0x32, (1 << 1)) /* gen_io */
|
||||
BUILD_IRQ(0x33, (1 << 2)) /* iop0 */
|
||||
BUILD_IRQ(0x34, (1 << 3)) /* iop1 */
|
||||
BUILD_IRQ(0x35, (1 << 4)) /* iop2 */
|
||||
BUILD_IRQ(0x36, (1 << 5)) /* iop3 */
|
||||
BUILD_IRQ(0x37, (1 << 6)) /* dma0 */
|
||||
BUILD_IRQ(0x38, (1 << 7)) /* dma1 */
|
||||
BUILD_IRQ(0x39, (1 << 8)) /* dma2 */
|
||||
BUILD_IRQ(0x3a, (1 << 9)) /* dma3 */
|
||||
BUILD_IRQ(0x3b, (1 << 10)) /* dma4 */
|
||||
BUILD_IRQ(0x3c, (1 << 11)) /* dma5 */
|
||||
BUILD_IRQ(0x3d, (1 << 12)) /* dma6 */
|
||||
BUILD_IRQ(0x3e, (1 << 13)) /* dma7 */
|
||||
BUILD_IRQ(0x3f, (1 << 14)) /* dma8 */
|
||||
BUILD_IRQ(0x40, (1 << 15)) /* dma9 */
|
||||
BUILD_IRQ(0x41, (1 << 16)) /* ata */
|
||||
BUILD_IRQ(0x42, (1 << 17)) /* sser0 */
|
||||
BUILD_IRQ(0x43, (1 << 18)) /* sser1 */
|
||||
BUILD_IRQ(0x44, (1 << 19)) /* ser0 */
|
||||
BUILD_IRQ(0x45, (1 << 20)) /* ser1 */
|
||||
BUILD_IRQ(0x46, (1 << 21)) /* ser2 */
|
||||
BUILD_IRQ(0x47, (1 << 22)) /* ser3 */
|
||||
BUILD_IRQ(0x48, (1 << 23))
|
||||
BUILD_IRQ(0x49, (1 << 24)) /* eth0 */
|
||||
BUILD_IRQ(0x4a, (1 << 25)) /* eth1 */
|
||||
BUILD_TIMER_IRQ(0x4b, (1 << 26))/* timer */
|
||||
BUILD_IRQ(0x4c, (1 << 27)) /* bif_arb */
|
||||
BUILD_IRQ(0x4d, (1 << 28)) /* bif_dma */
|
||||
BUILD_IRQ(0x4e, (1 << 29)) /* ext */
|
||||
BUILD_IRQ(0x4f, (1 << 29)) /* ipi */
|
||||
|
||||
/* Pointers to the low-level handlers. */
|
||||
static void (*interrupt[NR_IRQS])(void) = {
|
||||
IRQ0x31_interrupt, IRQ0x32_interrupt, IRQ0x33_interrupt,
|
||||
IRQ0x34_interrupt, IRQ0x35_interrupt, IRQ0x36_interrupt,
|
||||
IRQ0x37_interrupt, IRQ0x38_interrupt, IRQ0x39_interrupt,
|
||||
IRQ0x3a_interrupt, IRQ0x3b_interrupt, IRQ0x3c_interrupt,
|
||||
IRQ0x3d_interrupt, IRQ0x3e_interrupt, IRQ0x3f_interrupt,
|
||||
IRQ0x40_interrupt, IRQ0x41_interrupt, IRQ0x42_interrupt,
|
||||
IRQ0x43_interrupt, IRQ0x44_interrupt, IRQ0x45_interrupt,
|
||||
IRQ0x46_interrupt, IRQ0x47_interrupt, IRQ0x48_interrupt,
|
||||
IRQ0x49_interrupt, IRQ0x4a_interrupt, IRQ0x4b_interrupt,
|
||||
IRQ0x4c_interrupt, IRQ0x4d_interrupt, IRQ0x4e_interrupt,
|
||||
IRQ0x4f_interrupt
|
||||
};
|
||||
|
||||
void
|
||||
block_irq(int irq, int cpu)
|
||||
{
|
||||
int intr_mask;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&irq_lock, flags);
|
||||
intr_mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask);
|
||||
|
||||
/* Remember; 1 let thru, 0 block. */
|
||||
intr_mask &= ~(1 << (irq - FIRST_IRQ));
|
||||
|
||||
REG_WR_INT(intr_vect, irq_regs[cpu], rw_mask, intr_mask);
|
||||
spin_unlock_irqrestore(&irq_lock, flags);
|
||||
}
|
||||
|
||||
void
|
||||
unblock_irq(int irq, int cpu)
|
||||
{
|
||||
int intr_mask;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&irq_lock, flags);
|
||||
intr_mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask);
|
||||
|
||||
/* Remember; 1 let thru, 0 block. */
|
||||
intr_mask |= (1 << (irq - FIRST_IRQ));
|
||||
|
||||
REG_WR_INT(intr_vect, irq_regs[cpu], rw_mask, intr_mask);
|
||||
spin_unlock_irqrestore(&irq_lock, flags);
|
||||
}
|
||||
|
||||
/* Find out which CPU the irq should be allocated to. */
|
||||
static int irq_cpu(int irq)
|
||||
{
|
||||
int cpu;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&irq_lock, flags);
|
||||
cpu = irq_allocations[irq - FIRST_IRQ].cpu;
|
||||
|
||||
/* Fixed interrupts stay on the local CPU. */
|
||||
if (cpu == CPU_FIXED)
|
||||
{
|
||||
spin_unlock_irqrestore(&irq_lock, flags);
|
||||
return smp_processor_id();
|
||||
}
|
||||
|
||||
|
||||
/* Let the interrupt stay if possible */
|
||||
if (cpu_isset(cpu, irq_allocations[irq - FIRST_IRQ].mask))
|
||||
goto out;
|
||||
|
||||
/* IRQ must be moved to another CPU. */
|
||||
cpu = first_cpu(irq_allocations[irq - FIRST_IRQ].mask);
|
||||
irq_allocations[irq - FIRST_IRQ].cpu = cpu;
|
||||
out:
|
||||
spin_unlock_irqrestore(&irq_lock, flags);
|
||||
return cpu;
|
||||
}
|
||||
|
||||
void
|
||||
mask_irq(int irq)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
for (cpu = 0; cpu < NR_CPUS; cpu++)
|
||||
block_irq(irq, cpu);
|
||||
}
|
||||
|
||||
void
|
||||
unmask_irq(int irq)
|
||||
{
|
||||
unblock_irq(irq, irq_cpu(irq));
|
||||
}
|
||||
|
||||
|
||||
static unsigned int startup_crisv32_irq(unsigned int irq)
|
||||
{
|
||||
unmask_irq(irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void shutdown_crisv32_irq(unsigned int irq)
|
||||
{
|
||||
mask_irq(irq);
|
||||
}
|
||||
|
||||
static void enable_crisv32_irq(unsigned int irq)
|
||||
{
|
||||
unmask_irq(irq);
|
||||
}
|
||||
|
||||
static void disable_crisv32_irq(unsigned int irq)
|
||||
{
|
||||
mask_irq(irq);
|
||||
}
|
||||
|
||||
static void ack_crisv32_irq(unsigned int irq)
|
||||
{
|
||||
}
|
||||
|
||||
static void end_crisv32_irq(unsigned int irq)
|
||||
{
|
||||
}
|
||||
|
||||
void set_affinity_crisv32_irq(unsigned int irq, cpumask_t dest)
|
||||
{
|
||||
unsigned long flags;
|
||||
spin_lock_irqsave(&irq_lock, flags);
|
||||
irq_allocations[irq - FIRST_IRQ].mask = dest;
|
||||
spin_unlock_irqrestore(&irq_lock, flags);
|
||||
}
|
||||
|
||||
static struct hw_interrupt_type crisv32_irq_type = {
|
||||
.typename = "CRISv32",
|
||||
.startup = startup_crisv32_irq,
|
||||
.shutdown = shutdown_crisv32_irq,
|
||||
.enable = enable_crisv32_irq,
|
||||
.disable = disable_crisv32_irq,
|
||||
.ack = ack_crisv32_irq,
|
||||
.end = end_crisv32_irq,
|
||||
.set_affinity = set_affinity_crisv32_irq
|
||||
};
|
||||
|
||||
void
|
||||
set_exception_vector(int n, irqvectptr addr)
|
||||
{
|
||||
etrax_irv->v[n] = (irqvectptr) addr;
|
||||
}
|
||||
|
||||
extern void do_IRQ(int irq, struct pt_regs * regs);
|
||||
|
||||
void
|
||||
crisv32_do_IRQ(int irq, int block, struct pt_regs* regs)
|
||||
{
|
||||
/* Interrupts that may not be moved to another CPU and
|
||||
* are SA_INTERRUPT may skip blocking. This is currently
|
||||
* only valid for the timer IRQ and the IPI and is used
|
||||
* for the timer interrupt to avoid watchdog starvation.
|
||||
*/
|
||||
if (!block) {
|
||||
do_IRQ(irq, regs);
|
||||
return;
|
||||
}
|
||||
|
||||
block_irq(irq, smp_processor_id());
|
||||
do_IRQ(irq, regs);
|
||||
|
||||
unblock_irq(irq, irq_cpu(irq));
|
||||
}
|
||||
|
||||
/* If multiple interrupts occur simultaneously we get a multiple
|
||||
* interrupt from the CPU and software has to sort out which
|
||||
* interrupts that happened. There are two special cases here:
|
||||
*
|
||||
* 1. Timer interrupts may never be blocked because of the
|
||||
* watchdog (refer to comment in include/asr/arch/irq.h)
|
||||
* 2. GDB serial port IRQs are unhandled here and will be handled
|
||||
* as a single IRQ when it strikes again because the GDB
|
||||
* stubb wants to save the registers in its own fashion.
|
||||
*/
|
||||
void
|
||||
crisv32_do_multiple(struct pt_regs* regs)
|
||||
{
|
||||
int cpu;
|
||||
int mask;
|
||||
int masked;
|
||||
int bit;
|
||||
|
||||
cpu = smp_processor_id();
|
||||
|
||||
/* An extra irq_enter here to prevent softIRQs to run after
|
||||
* each do_IRQ. This will decrease the interrupt latency.
|
||||
*/
|
||||
irq_enter();
|
||||
|
||||
/* Get which IRQs that happend. */
|
||||
masked = REG_RD_INT(intr_vect, irq_regs[cpu], r_masked_vect);
|
||||
|
||||
/* Calculate new IRQ mask with these IRQs disabled. */
|
||||
mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask);
|
||||
mask &= ~masked;
|
||||
|
||||
/* Timer IRQ is never masked */
|
||||
if (masked & TIMER_MASK)
|
||||
mask |= TIMER_MASK;
|
||||
|
||||
/* Block all the IRQs */
|
||||
REG_WR_INT(intr_vect, irq_regs[cpu], rw_mask, mask);
|
||||
|
||||
/* Check for timer IRQ and handle it special. */
|
||||
if (masked & TIMER_MASK) {
|
||||
masked &= ~TIMER_MASK;
|
||||
do_IRQ(TIMER_INTR_VECT, regs);
|
||||
}
|
||||
|
||||
#ifdef IGNORE_MASK
|
||||
/* Remove IRQs that can't be handled as multiple. */
|
||||
masked &= ~IGNORE_MASK;
|
||||
#endif
|
||||
|
||||
/* Handle the rest of the IRQs. */
|
||||
for (bit = 0; bit < 32; bit++)
|
||||
{
|
||||
if (masked & (1 << bit))
|
||||
do_IRQ(bit + FIRST_IRQ, regs);
|
||||
}
|
||||
|
||||
/* Unblock all the IRQs. */
|
||||
mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask);
|
||||
mask |= masked;
|
||||
REG_WR_INT(intr_vect, irq_regs[cpu], rw_mask, mask);
|
||||
|
||||
/* This irq_exit() will trigger the soft IRQs. */
|
||||
irq_exit();
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called by start_kernel. It fixes the IRQ masks and setup the
|
||||
* interrupt vector table to point to bad_interrupt pointers.
|
||||
*/
|
||||
void __init
|
||||
init_IRQ(void)
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
reg_intr_vect_rw_mask vect_mask = {0};
|
||||
|
||||
/* Clear all interrupts masks. */
|
||||
REG_WR(intr_vect, regi_irq, rw_mask, vect_mask);
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
etrax_irv->v[i] = weird_irq;
|
||||
|
||||
/* Point all IRQ's to bad handlers. */
|
||||
for (i = FIRST_IRQ, j = 0; j < NR_IRQS; i++, j++) {
|
||||
irq_desc[j].handler = &crisv32_irq_type;
|
||||
set_exception_vector(i, interrupt[j]);
|
||||
}
|
||||
|
||||
/* Mark Timer and IPI IRQs as CPU local */
|
||||
irq_allocations[TIMER_INTR_VECT - FIRST_IRQ].cpu = CPU_FIXED;
|
||||
irq_desc[TIMER_INTR_VECT].status |= IRQ_PER_CPU;
|
||||
irq_allocations[IPI_INTR_VECT - FIRST_IRQ].cpu = CPU_FIXED;
|
||||
irq_desc[IPI_INTR_VECT].status |= IRQ_PER_CPU;
|
||||
|
||||
set_exception_vector(0x00, nmi_interrupt);
|
||||
set_exception_vector(0x30, multiple_interrupt);
|
||||
|
||||
/* Set up handler for various MMU bus faults. */
|
||||
set_exception_vector(0x04, i_mmu_refill);
|
||||
set_exception_vector(0x05, i_mmu_invalid);
|
||||
set_exception_vector(0x06, i_mmu_access);
|
||||
set_exception_vector(0x07, i_mmu_execute);
|
||||
set_exception_vector(0x08, d_mmu_refill);
|
||||
set_exception_vector(0x09, d_mmu_invalid);
|
||||
set_exception_vector(0x0a, d_mmu_access);
|
||||
set_exception_vector(0x0b, d_mmu_write);
|
||||
|
||||
/* The system-call trap is reached by "break 13". */
|
||||
set_exception_vector(0x1d, system_call);
|
||||
|
||||
/* Exception handlers for debugging, both user-mode and kernel-mode. */
|
||||
|
||||
/* Break 8. */
|
||||
set_exception_vector(0x18, gdb_handle_exception);
|
||||
/* Hardware single step. */
|
||||
set_exception_vector(0x3, gdb_handle_exception);
|
||||
/* Hardware breakpoint. */
|
||||
set_exception_vector(0xc, gdb_handle_exception);
|
||||
|
||||
#ifdef CONFIG_ETRAX_KGDB
|
||||
kgdb_init();
|
||||
/* Everything is set up; now trap the kernel. */
|
||||
breakpoint();
|
||||
#endif
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,552 @@
|
|||
/*
|
||||
* Copyright (C) 2004 Axis Communications AB
|
||||
*
|
||||
* Code for handling break 8, hardware breakpoint, single step, and serial
|
||||
* port exceptions for kernel debugging purposes.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <asm/arch/hwregs/intr_vect.h>
|
||||
|
||||
;; Exported functions.
|
||||
.globl kgdb_handle_exception
|
||||
|
||||
kgdb_handle_exception:
|
||||
|
||||
;; Create a register image of the caller.
|
||||
;;
|
||||
;; First of all, save the ACR on the stack since we need it for address calculations.
|
||||
;; We put it into the register struct later.
|
||||
|
||||
subq 4, $sp
|
||||
move.d $acr, [$sp]
|
||||
|
||||
;; Now we are free to use ACR all we want.
|
||||
;; If we were running this handler with interrupts on, we would have to be careful
|
||||
;; to save and restore CCS manually, but since we aren't we treat it like every other
|
||||
;; register.
|
||||
|
||||
move.d reg, $acr
|
||||
move.d $r0, [$acr] ; Save R0 (start of register struct)
|
||||
addq 4, $acr
|
||||
move.d $r1, [$acr] ; Save R1
|
||||
addq 4, $acr
|
||||
move.d $r2, [$acr] ; Save R2
|
||||
addq 4, $acr
|
||||
move.d $r3, [$acr] ; Save R3
|
||||
addq 4, $acr
|
||||
move.d $r4, [$acr] ; Save R4
|
||||
addq 4, $acr
|
||||
move.d $r5, [$acr] ; Save R5
|
||||
addq 4, $acr
|
||||
move.d $r6, [$acr] ; Save R6
|
||||
addq 4, $acr
|
||||
move.d $r7, [$acr] ; Save R7
|
||||
addq 4, $acr
|
||||
move.d $r8, [$acr] ; Save R8
|
||||
addq 4, $acr
|
||||
move.d $r9, [$acr] ; Save R9
|
||||
addq 4, $acr
|
||||
move.d $r10, [$acr] ; Save R10
|
||||
addq 4, $acr
|
||||
move.d $r11, [$acr] ; Save R11
|
||||
addq 4, $acr
|
||||
move.d $r12, [$acr] ; Save R12
|
||||
addq 4, $acr
|
||||
move.d $r13, [$acr] ; Save R13
|
||||
addq 4, $acr
|
||||
move.d $sp, [$acr] ; Save SP (R14)
|
||||
addq 4, $acr
|
||||
|
||||
;; The ACR register is already saved on the stack, so pop it from there.
|
||||
move.d [$sp],$r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
|
||||
move $bz, [$acr]
|
||||
addq 1, $acr
|
||||
move $vr, [$acr]
|
||||
addq 1, $acr
|
||||
move $pid, [$acr]
|
||||
addq 4, $acr
|
||||
move $srs, [$acr]
|
||||
addq 1, $acr
|
||||
move $wz, [$acr]
|
||||
addq 2, $acr
|
||||
move $exs, [$acr]
|
||||
addq 4, $acr
|
||||
move $eda, [$acr]
|
||||
addq 4, $acr
|
||||
move $mof, [$acr]
|
||||
addq 4, $acr
|
||||
move $dz, [$acr]
|
||||
addq 4, $acr
|
||||
move $ebp, [$acr]
|
||||
addq 4, $acr
|
||||
move $erp, [$acr]
|
||||
addq 4, $acr
|
||||
move $srp, [$acr]
|
||||
addq 4, $acr
|
||||
move $nrp, [$acr]
|
||||
addq 4, $acr
|
||||
move $ccs, [$acr]
|
||||
addq 4, $acr
|
||||
move $usp, [$acr]
|
||||
addq 4, $acr
|
||||
move $spc, [$acr]
|
||||
addq 4, $acr
|
||||
|
||||
;; Skip the pseudo-PC.
|
||||
addq 4, $acr
|
||||
|
||||
;; Save the support registers in bank 0 - 3.
|
||||
clear.d $r1 ; Bank counter
|
||||
move.d sreg, $acr
|
||||
|
||||
;; Bank 0
|
||||
move $r1, $srs
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
move $s0, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s1, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s2, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s3, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s4, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s5, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s6, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s7, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s8, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s9, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s10, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s11, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s12, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
|
||||
;; Nothing in S13 - S15, bank 0
|
||||
clear.d [$acr]
|
||||
addq 4, $acr
|
||||
clear.d [$acr]
|
||||
addq 4, $acr
|
||||
clear.d [$acr]
|
||||
addq 4, $acr
|
||||
|
||||
;; Bank 1 and bank 2 have the same layout, hence the loop.
|
||||
addq 1, $r1
|
||||
1:
|
||||
move $r1, $srs
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
move $s0, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s1, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s2, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s3, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s4, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s5, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s6, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
|
||||
;; Nothing in S7 - S15, bank 1 and 2
|
||||
clear.d [$acr]
|
||||
addq 4, $acr
|
||||
clear.d [$acr]
|
||||
addq 4, $acr
|
||||
clear.d [$acr]
|
||||
addq 4, $acr
|
||||
clear.d [$acr]
|
||||
addq 4, $acr
|
||||
clear.d [$acr]
|
||||
addq 4, $acr
|
||||
clear.d [$acr]
|
||||
addq 4, $acr
|
||||
clear.d [$acr]
|
||||
addq 4, $acr
|
||||
clear.d [$acr]
|
||||
addq 4, $acr
|
||||
clear.d [$acr]
|
||||
addq 4, $acr
|
||||
|
||||
addq 1, $r1
|
||||
cmpq 3, $r1
|
||||
bne 1b
|
||||
nop
|
||||
|
||||
;; Bank 3
|
||||
move $r1, $srs
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
move $s0, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s1, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s2, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s3, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s4, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s5, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s6, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s7, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s8, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s9, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s10, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s11, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s12, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s13, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
move $s14, $r0
|
||||
move.d $r0, [$acr]
|
||||
addq 4, $acr
|
||||
;; Nothing in S15, bank 3
|
||||
clear.d [$acr]
|
||||
addq 4, $acr
|
||||
|
||||
;; Check what got us here: get IDX field of EXS.
|
||||
move $exs, $r10
|
||||
and.d 0xff00, $r10
|
||||
lsrq 8, $r10
|
||||
#if defined(CONFIG_ETRAX_KGDB_PORT0)
|
||||
cmp.d SER0_INTR_VECT, $r10 ; IRQ for serial port 0
|
||||
beq sigint
|
||||
nop
|
||||
#elif defined(CONFIG_ETRAX_KGDB_PORT1)
|
||||
cmp.d SER1_INTR_VECT, $r10 ; IRQ for serial port 1
|
||||
beq sigint
|
||||
nop
|
||||
#elif defined(CONFIG_ETRAX_KGDB_PORT2)
|
||||
cmp.d SER2_INTR_VECT, $r10 ; IRQ for serial port 2
|
||||
beq sigint
|
||||
nop
|
||||
#elif defined(CONFIG_ETRAX_KGDB_PORT3)
|
||||
cmp.d SER3_INTR_VECT, $r10 ; IRQ for serial port 3
|
||||
beq sigint
|
||||
nop
|
||||
#endif
|
||||
;; Multiple interrupt must be due to serial break.
|
||||
cmp.d 0x30, $r10 ; Multiple interrupt
|
||||
beq sigint
|
||||
nop
|
||||
;; Neither of those? Then it's a sigtrap.
|
||||
ba handle_comm
|
||||
moveq 5, $r10 ; Set SIGTRAP (delay slot)
|
||||
|
||||
sigint:
|
||||
;; Serial interrupt; get character
|
||||
jsr getDebugChar
|
||||
nop ; Delay slot
|
||||
cmp.b 3, $r10 ; \003 (Ctrl-C)?
|
||||
bne return ; No, get out of here
|
||||
nop
|
||||
moveq 2, $r10 ; Set SIGINT
|
||||
|
||||
;;
|
||||
;; Handle the communication
|
||||
;;
|
||||
handle_comm:
|
||||
move.d internal_stack+1020, $sp ; Use the internal stack which grows upwards
|
||||
jsr handle_exception ; Interactive routine
|
||||
nop
|
||||
|
||||
;;
|
||||
;; Return to the caller
|
||||
;;
|
||||
return:
|
||||
|
||||
;; First of all, write the support registers.
|
||||
clear.d $r1 ; Bank counter
|
||||
move.d sreg, $acr
|
||||
|
||||
;; Bank 0
|
||||
move $r1, $srs
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s0
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s1
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s2
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s3
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s4
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s5
|
||||
addq 4, $acr
|
||||
|
||||
;; Nothing in S6 - S7, bank 0.
|
||||
addq 4, $acr
|
||||
addq 4, $acr
|
||||
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s8
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s9
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s10
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s11
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s12
|
||||
addq 4, $acr
|
||||
|
||||
;; Nothing in S13 - S15, bank 0
|
||||
addq 4, $acr
|
||||
addq 4, $acr
|
||||
addq 4, $acr
|
||||
|
||||
;; Bank 1 and bank 2 have the same layout, hence the loop.
|
||||
addq 1, $r1
|
||||
2:
|
||||
move $r1, $srs
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s0
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s1
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s2
|
||||
addq 4, $acr
|
||||
|
||||
;; S3 (MM_CAUSE) is read-only.
|
||||
addq 4, $acr
|
||||
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s4
|
||||
addq 4, $acr
|
||||
|
||||
;; FIXME: Actually write S5/S6? (Affects MM_CAUSE.)
|
||||
addq 4, $acr
|
||||
addq 4, $acr
|
||||
|
||||
;; Nothing in S7 - S15, bank 1 and 2
|
||||
addq 4, $acr
|
||||
addq 4, $acr
|
||||
addq 4, $acr
|
||||
addq 4, $acr
|
||||
addq 4, $acr
|
||||
addq 4, $acr
|
||||
addq 4, $acr
|
||||
addq 4, $acr
|
||||
addq 4, $acr
|
||||
|
||||
addq 1, $r1
|
||||
cmpq 3, $r1
|
||||
bne 2b
|
||||
nop
|
||||
|
||||
;; Bank 3
|
||||
move $r1, $srs
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s0
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s1
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s2
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s3
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s4
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s5
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s6
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s7
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s8
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s9
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s10
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s11
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s12
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s13
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r0
|
||||
move $r0, $s14
|
||||
addq 4, $acr
|
||||
|
||||
;; Nothing in S15, bank 3
|
||||
addq 4, $acr
|
||||
|
||||
;; Now, move on to the regular register restoration process.
|
||||
|
||||
move.d reg, $acr ; Reset ACR to point at the beginning of the register image
|
||||
move.d [$acr], $r0 ; Restore R0
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r1 ; Restore R1
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r2 ; Restore R2
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r3 ; Restore R3
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r4 ; Restore R4
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r5 ; Restore R5
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r6 ; Restore R6
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r7 ; Restore R7
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r8 ; Restore R8
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r9 ; Restore R9
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r10 ; Restore R10
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r11 ; Restore R11
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r12 ; Restore R12
|
||||
addq 4, $acr
|
||||
move.d [$acr], $r13 ; Restore R13
|
||||
|
||||
;;
|
||||
;; We restore all registers, even though some of them probably haven't changed.
|
||||
;;
|
||||
|
||||
addq 4, $acr
|
||||
move.d [$acr], $sp ; Restore SP (R14)
|
||||
|
||||
;; ACR cannot be restored just yet.
|
||||
addq 8, $acr
|
||||
|
||||
;; Skip BZ, VR.
|
||||
addq 2, $acr
|
||||
|
||||
move [$acr], $pid ; Restore PID
|
||||
addq 4, $acr
|
||||
move [$acr], $srs ; Restore SRS
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
addq 1, $acr
|
||||
|
||||
;; Skip WZ.
|
||||
addq 2, $acr
|
||||
|
||||
move [$acr], $exs ; Restore EXS.
|
||||
addq 4, $acr
|
||||
move [$acr], $eda ; Restore EDA.
|
||||
addq 4, $acr
|
||||
move [$acr], $mof ; Restore MOF.
|
||||
|
||||
;; Skip DZ.
|
||||
addq 8, $acr
|
||||
|
||||
move [$acr], $ebp ; Restore EBP.
|
||||
addq 4, $acr
|
||||
move [$acr], $erp ; Restore ERP.
|
||||
addq 4, $acr
|
||||
move [$acr], $srp ; Restore SRP.
|
||||
addq 4, $acr
|
||||
move [$acr], $nrp ; Restore NRP.
|
||||
addq 4, $acr
|
||||
move [$acr], $ccs ; Restore CCS like an ordinary register.
|
||||
addq 4, $acr
|
||||
move [$acr], $usp ; Restore USP
|
||||
addq 4, $acr
|
||||
move [$acr], $spc ; Restore SPC
|
||||
; No restoration of pseudo-PC of course.
|
||||
|
||||
move.d reg, $acr ; Reset ACR to point at the beginning of the register image
|
||||
add.d 15*4, $acr
|
||||
move.d [$acr], $acr ; Finally, restore ACR.
|
||||
rete ; Same as jump ERP
|
||||
rfe ; Shifts CCS
|
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* Allocator for I/O pins. All pins are allocated to GPIO at bootup.
|
||||
* Unassigned pins and GPIO pins can be allocated to a fixed interface
|
||||
* or the I/O processor instead.
|
||||
*
|
||||
* Copyright (c) 2004 Axis Communications AB.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <asm/arch/hwregs/reg_map.h>
|
||||
#include <asm/arch/hwregs/reg_rdwr.h>
|
||||
#include <asm/arch/pinmux.h>
|
||||
#include <asm/arch/hwregs/pinmux_defs.h>
|
||||
|
||||
#undef DEBUG
|
||||
|
||||
#define PORT_PINS 18
|
||||
#define PORTS 4
|
||||
|
||||
static char pins[PORTS][PORT_PINS];
|
||||
static DEFINE_SPINLOCK(pinmux_lock);
|
||||
|
||||
static void crisv32_pinmux_set(int port);
|
||||
|
||||
int
|
||||
crisv32_pinmux_init(void)
|
||||
{
|
||||
static int initialized = 0;
|
||||
|
||||
if (!initialized) {
|
||||
reg_pinmux_rw_pa pa = REG_RD(pinmux, regi_pinmux, rw_pa);
|
||||
initialized = 1;
|
||||
pa.pa0 = pa.pa1 = pa.pa2 = pa.pa3 =
|
||||
pa.pa4 = pa.pa5 = pa.pa6 = pa.pa7 = regk_pinmux_yes;
|
||||
REG_WR(pinmux, regi_pinmux, rw_pa, pa);
|
||||
crisv32_pinmux_alloc(PORT_B, 0, PORT_PINS - 1, pinmux_gpio);
|
||||
crisv32_pinmux_alloc(PORT_C, 0, PORT_PINS - 1, pinmux_gpio);
|
||||
crisv32_pinmux_alloc(PORT_D, 0, PORT_PINS - 1, pinmux_gpio);
|
||||
crisv32_pinmux_alloc(PORT_E, 0, PORT_PINS - 1, pinmux_gpio);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
crisv32_pinmux_alloc(int port, int first_pin, int last_pin, enum pin_mode mode)
|
||||
{
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
crisv32_pinmux_init();
|
||||
|
||||
if (port > PORTS)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&pinmux_lock, flags);
|
||||
|
||||
for (i = first_pin; i <= last_pin; i++)
|
||||
{
|
||||
if ((pins[port][i] != pinmux_none) && (pins[port][i] != pinmux_gpio) &&
|
||||
(pins[port][i] != mode))
|
||||
{
|
||||
spin_unlock_irqrestore(&pinmux_lock, flags);
|
||||
#ifdef DEBUG
|
||||
panic("Pinmux alloc failed!\n");
|
||||
#endif
|
||||
return -EPERM;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = first_pin; i <= last_pin; i++)
|
||||
pins[port][i] = mode;
|
||||
|
||||
crisv32_pinmux_set(port);
|
||||
|
||||
spin_unlock_irqrestore(&pinmux_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
crisv32_pinmux_alloc_fixed(enum fixed_function function)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
char saved[sizeof pins];
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&pinmux_lock, flags);
|
||||
|
||||
/* Save internal data for recovery */
|
||||
memcpy(saved, pins, sizeof pins);
|
||||
|
||||
reg_pinmux_rw_hwprot hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot);
|
||||
|
||||
switch(function)
|
||||
{
|
||||
case pinmux_ser1:
|
||||
ret = crisv32_pinmux_alloc(PORT_C, 4, 7, pinmux_fixed);
|
||||
hwprot.ser1 = regk_pinmux_yes;
|
||||
break;
|
||||
case pinmux_ser2:
|
||||
ret = crisv32_pinmux_alloc(PORT_C, 8, 11, pinmux_fixed);
|
||||
hwprot.ser2 = regk_pinmux_yes;
|
||||
break;
|
||||
case pinmux_ser3:
|
||||
ret = crisv32_pinmux_alloc(PORT_C, 12, 15, pinmux_fixed);
|
||||
hwprot.ser3 = regk_pinmux_yes;
|
||||
break;
|
||||
case pinmux_sser0:
|
||||
ret = crisv32_pinmux_alloc(PORT_C, 0, 3, pinmux_fixed);
|
||||
ret |= crisv32_pinmux_alloc(PORT_C, 16, 16, pinmux_fixed);
|
||||
hwprot.sser0 = regk_pinmux_yes;
|
||||
break;
|
||||
case pinmux_sser1:
|
||||
ret = crisv32_pinmux_alloc(PORT_D, 0, 4, pinmux_fixed);
|
||||
hwprot.sser1 = regk_pinmux_yes;
|
||||
break;
|
||||
case pinmux_ata0:
|
||||
ret = crisv32_pinmux_alloc(PORT_D, 5, 7, pinmux_fixed);
|
||||
ret |= crisv32_pinmux_alloc(PORT_D, 15, 17, pinmux_fixed);
|
||||
hwprot.ata0 = regk_pinmux_yes;
|
||||
break;
|
||||
case pinmux_ata1:
|
||||
ret = crisv32_pinmux_alloc(PORT_D, 0, 4, pinmux_fixed);
|
||||
ret |= crisv32_pinmux_alloc(PORT_E, 17, 17, pinmux_fixed);
|
||||
hwprot.ata1 = regk_pinmux_yes;
|
||||
break;
|
||||
case pinmux_ata2:
|
||||
ret = crisv32_pinmux_alloc(PORT_C, 11, 15, pinmux_fixed);
|
||||
ret |= crisv32_pinmux_alloc(PORT_E, 3, 3, pinmux_fixed);
|
||||
hwprot.ata2 = regk_pinmux_yes;
|
||||
break;
|
||||
case pinmux_ata3:
|
||||
ret = crisv32_pinmux_alloc(PORT_C, 8, 10, pinmux_fixed);
|
||||
ret |= crisv32_pinmux_alloc(PORT_C, 0, 2, pinmux_fixed);
|
||||
hwprot.ata2 = regk_pinmux_yes;
|
||||
break;
|
||||
case pinmux_ata:
|
||||
ret = crisv32_pinmux_alloc(PORT_B, 0, 15, pinmux_fixed);
|
||||
ret |= crisv32_pinmux_alloc(PORT_D, 8, 15, pinmux_fixed);
|
||||
hwprot.ata = regk_pinmux_yes;
|
||||
break;
|
||||
case pinmux_eth1:
|
||||
ret = crisv32_pinmux_alloc(PORT_E, 0, 17, pinmux_fixed);
|
||||
hwprot.eth1 = regk_pinmux_yes;
|
||||
hwprot.eth1_mgm = regk_pinmux_yes;
|
||||
break;
|
||||
case pinmux_timer:
|
||||
ret = crisv32_pinmux_alloc(PORT_C, 16, 16, pinmux_fixed);
|
||||
hwprot.timer = regk_pinmux_yes;
|
||||
spin_unlock_irqrestore(&pinmux_lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot);
|
||||
else
|
||||
memcpy(pins, saved, sizeof pins);
|
||||
|
||||
spin_unlock_irqrestore(&pinmux_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
crisv32_pinmux_set(int port)
|
||||
{
|
||||
int i;
|
||||
int gpio_val = 0;
|
||||
int iop_val = 0;
|
||||
|
||||
for (i = 0; i < PORT_PINS; i++)
|
||||
{
|
||||
if (pins[port][i] == pinmux_gpio)
|
||||
gpio_val |= (1 << i);
|
||||
else if (pins[port][i] == pinmux_iop)
|
||||
iop_val |= (1 << i);
|
||||
}
|
||||
|
||||
REG_WRITE(int, regi_pinmux + REG_RD_ADDR_pinmux_rw_pb_gio + 8*port, gpio_val);
|
||||
REG_WRITE(int, regi_pinmux + REG_RD_ADDR_pinmux_rw_pb_iop + 8*port, iop_val);
|
||||
|
||||
#ifdef DEBUG
|
||||
crisv32_pinmux_dump();
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
crisv32_pinmux_dealloc(int port, int first_pin, int last_pin)
|
||||
{
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
crisv32_pinmux_init();
|
||||
|
||||
if (port > PORTS)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&pinmux_lock, flags);
|
||||
|
||||
for (i = first_pin; i <= last_pin; i++)
|
||||
pins[port][i] = pinmux_none;
|
||||
|
||||
crisv32_pinmux_set(port);
|
||||
spin_unlock_irqrestore(&pinmux_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
crisv32_pinmux_dump(void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
crisv32_pinmux_init();
|
||||
|
||||
for (i = 0; i < PORTS; i++)
|
||||
{
|
||||
printk("Port %c\n", 'B'+i);
|
||||
for (j = 0; j < PORT_PINS; j++)
|
||||
printk(" Pin %d = %d\n", j, pins[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
__initcall(crisv32_pinmux_init);
|
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
* Copyright (C) 2000-2003 Axis Communications AB
|
||||
*
|
||||
* Authors: Bjorn Wesen (bjornw@axis.com)
|
||||
* Mikael Starvik (starvik@axis.com)
|
||||
* Tobias Anderberg (tobiasa@axis.com), CRISv32 port.
|
||||
*
|
||||
* This file handles the architecture-dependent parts of process handling..
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/arch/hwregs/reg_rdwr.h>
|
||||
#include <asm/arch/hwregs/reg_map.h>
|
||||
#include <asm/arch/hwregs/timer_defs.h>
|
||||
#include <asm/arch/hwregs/intr_vect_defs.h>
|
||||
|
||||
extern void stop_watchdog(void);
|
||||
|
||||
#ifdef CONFIG_ETRAX_GPIO
|
||||
extern void etrax_gpio_wake_up_check(void); /* Defined in drivers/gpio.c. */
|
||||
#endif
|
||||
|
||||
extern int cris_hlt_counter;
|
||||
|
||||
/* We use this if we don't have any better idle routine. */
|
||||
void default_idle(void)
|
||||
{
|
||||
local_irq_disable();
|
||||
if (!need_resched() && !cris_hlt_counter) {
|
||||
/* Halt until exception. */
|
||||
__asm__ volatile("ei \n\t"
|
||||
"halt ");
|
||||
}
|
||||
local_irq_enable();
|
||||
}
|
||||
|
||||
/*
|
||||
* Free current thread data structures etc..
|
||||
*/
|
||||
|
||||
extern void deconfigure_bp(long pid);
|
||||
void exit_thread(void)
|
||||
{
|
||||
deconfigure_bp(current->pid);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the watchdog is enabled, disable interrupts and enter an infinite loop.
|
||||
* The watchdog will reset the CPU after 0.1s. If the watchdog isn't enabled
|
||||
* then enable it and wait.
|
||||
*/
|
||||
extern void arch_enable_nmi(void);
|
||||
|
||||
void
|
||||
hard_reset_now(void)
|
||||
{
|
||||
/*
|
||||
* Don't declare this variable elsewhere. We don't want any other
|
||||
* code to know about it than the watchdog handler in entry.S and
|
||||
* this code, implementing hard reset through the watchdog.
|
||||
*/
|
||||
#if defined(CONFIG_ETRAX_WATCHDOG)
|
||||
extern int cause_of_death;
|
||||
#endif
|
||||
|
||||
printk("*** HARD RESET ***\n");
|
||||
local_irq_disable();
|
||||
|
||||
#if defined(CONFIG_ETRAX_WATCHDOG)
|
||||
cause_of_death = 0xbedead;
|
||||
#else
|
||||
{
|
||||
reg_timer_rw_wd_ctrl wd_ctrl = {0};
|
||||
|
||||
stop_watchdog();
|
||||
|
||||
wd_ctrl.key = 16; /* Arbitrary key. */
|
||||
wd_ctrl.cnt = 1; /* Minimum time. */
|
||||
wd_ctrl.cmd = regk_timer_start;
|
||||
|
||||
arch_enable_nmi();
|
||||
REG_WR(timer, regi_timer, rw_wd_ctrl, wd_ctrl);
|
||||
}
|
||||
#endif
|
||||
|
||||
while (1)
|
||||
; /* Wait for reset. */
|
||||
}
|
||||
|
||||
/*
|
||||
* Return saved PC of a blocked thread.
|
||||
*/
|
||||
unsigned long thread_saved_pc(struct task_struct *t)
|
||||
{
|
||||
return (unsigned long)user_regs(t->thread_info)->erp;
|
||||
}
|
||||
|
||||
static void
|
||||
kernel_thread_helper(void* dummy, int (*fn)(void *), void * arg)
|
||||
{
|
||||
fn(arg);
|
||||
do_exit(-1); /* Should never be called, return bad exit value. */
|
||||
}
|
||||
|
||||
/* Create a kernel thread. */
|
||||
int
|
||||
kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
|
||||
{
|
||||
struct pt_regs regs;
|
||||
|
||||
memset(®s, 0, sizeof(regs));
|
||||
|
||||
/* Don't use r10 since that is set to 0 in copy_thread. */
|
||||
regs.r11 = (unsigned long) fn;
|
||||
regs.r12 = (unsigned long) arg;
|
||||
regs.erp = (unsigned long) kernel_thread_helper;
|
||||
regs.ccs = 1 << (I_CCS_BITNR + CCS_SHIFT);
|
||||
|
||||
/* Create the new process. */
|
||||
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup the child's kernel stack with a pt_regs and call switch_stack() on it.
|
||||
* It will be unnested 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.
|
||||
*/
|
||||
|
||||
extern asmlinkage void ret_from_fork(void);
|
||||
|
||||
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. Note: the task_struct doubles as the kernel stack for the
|
||||
* task.
|
||||
*/
|
||||
childregs = user_regs(p->thread_info);
|
||||
*childregs = *regs; /* Struct copy of pt_regs. */
|
||||
p->set_child_tid = p->clear_child_tid = NULL;
|
||||
childregs->r10 = 0; /* Child returns 0 after a fork/clone. */
|
||||
|
||||
/* Set a new TLS ?
|
||||
* The TLS is in $mof beacuse it is the 5th argument to sys_clone.
|
||||
*/
|
||||
if (p->mm && (clone_flags & CLONE_SETTLS)) {
|
||||
p->thread_info->tls = regs->mof;
|
||||
}
|
||||
|
||||
/* Put the switch stack right below the pt_regs. */
|
||||
swstack = ((struct switch_stack *) childregs) - 1;
|
||||
|
||||
/* Paramater to ret_from_sys_call. 0 is don't restart the syscall. */
|
||||
swstack->r9 = 0;
|
||||
|
||||
/*
|
||||
* We want to return into ret_from_sys_call after the _resume.
|
||||
* ret_from_fork will call ret_from_sys_call.
|
||||
*/
|
||||
swstack->return_ip = (unsigned long) ret_from_fork;
|
||||
|
||||
/* Fix the user-mode and kernel-mode stackpointer. */
|
||||
p->thread.usp = usp;
|
||||
p->thread.ksp = (unsigned long) swstack;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Be aware of the "magic" 7th argument in the four system-calls below.
|
||||
* They need the latest stackframe, which is put as the 7th argument by
|
||||
* entry.S. The previous arguments are dummies or actually used, but need
|
||||
* to be defined to reach the 7th argument.
|
||||
*
|
||||
* N.B.: Another method to get the stackframe is to use current_regs(). But
|
||||
* it returns the latest stack-frame stacked when going from _user mode_ and
|
||||
* some of these (at least sys_clone) are called from kernel-mode sometimes
|
||||
* (for example during kernel_thread, above) and thus cannot use it. Thus,
|
||||
* to be sure not to get any surprises, we use the method for the other calls
|
||||
* as well.
|
||||
*/
|
||||
asmlinkage int
|
||||
sys_fork(long r10, long r11, long r12, long r13, long mof, long srp,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
return do_fork(SIGCHLD, rdusp(), regs, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
/* FIXME: Is parent_tid/child_tid really third/fourth argument? Update lib? */
|
||||
asmlinkage int
|
||||
sys_clone(unsigned long newusp, unsigned long flags, int *parent_tid, int *child_tid,
|
||||
unsigned long tls, long srp, struct pt_regs *regs)
|
||||
{
|
||||
if (!newusp)
|
||||
newusp = rdusp();
|
||||
|
||||
return do_fork(flags, newusp, regs, 0, parent_tid, child_tid);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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(long r10, long r11, long r12, long r13, long mof, long srp,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), regs, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
/* sys_execve() executes a new program. */
|
||||
asmlinkage int
|
||||
sys_execve(const char *fname, char **argv, char **envp, long r13, long mof, long srp,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
int error;
|
||||
char *filename;
|
||||
|
||||
filename = getname(fname);
|
||||
error = PTR_ERR(filename);
|
||||
|
||||
if (IS_ERR(filename))
|
||||
goto out;
|
||||
|
||||
error = do_execve(filename, argv, envp, regs);
|
||||
putname(filename);
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
unsigned long
|
||||
get_wchan(struct task_struct *p)
|
||||
{
|
||||
/* TODO */
|
||||
return 0;
|
||||
}
|
||||
#undef last_sched
|
||||
#undef first_sched
|
||||
|
||||
void show_regs(struct pt_regs * regs)
|
||||
{
|
||||
unsigned long usp = rdusp();
|
||||
printk("ERP: %08lx SRP: %08lx CCS: %08lx USP: %08lx MOF: %08lx\n",
|
||||
regs->erp, regs->srp, regs->ccs, usp, regs->mof);
|
||||
|
||||
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);
|
||||
}
|
|
@ -0,0 +1,597 @@
|
|||
/*
|
||||
* Copyright (C) 2000-2003, Axis Communications AB.
|
||||
*/
|
||||
|
||||
#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 <linux/signal.h>
|
||||
#include <linux/security.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/arch/hwregs/supp_reg.h>
|
||||
|
||||
/*
|
||||
* Determines which bits in CCS the user has access to.
|
||||
* 1 = access, 0 = no access.
|
||||
*/
|
||||
#define CCS_MASK 0x00087c00 /* SXNZVC */
|
||||
|
||||
#define SBIT_USER (1 << (S_CCS_BITNR + CCS_SHIFT))
|
||||
|
||||
static int put_debugreg(long pid, unsigned int regno, long data);
|
||||
static long get_debugreg(long pid, unsigned int regno);
|
||||
static unsigned long get_pseudo_pc(struct task_struct *child);
|
||||
void deconfigure_bp(long pid);
|
||||
|
||||
extern unsigned long cris_signal_return_page;
|
||||
|
||||
/*
|
||||
* Get contents of register REGNO in task TASK.
|
||||
*/
|
||||
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
|
||||
*/
|
||||
unsigned long ret;
|
||||
|
||||
if (regno <= PT_EDA)
|
||||
ret = ((unsigned long *)user_regs(task->thread_info))[regno];
|
||||
else if (regno == PT_USP)
|
||||
ret = task->thread.usp;
|
||||
else if (regno == PT_PPC)
|
||||
ret = get_pseudo_pc(task);
|
||||
else if (regno <= PT_MAX)
|
||||
ret = get_debugreg(task->pid, regno);
|
||||
else
|
||||
ret = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write contents of register REGNO in task TASK.
|
||||
*/
|
||||
int put_reg(struct task_struct *task, unsigned int regno, unsigned long data)
|
||||
{
|
||||
if (regno <= PT_EDA)
|
||||
((unsigned long *)user_regs(task->thread_info))[regno] = data;
|
||||
else if (regno == PT_USP)
|
||||
task->thread.usp = data;
|
||||
else if (regno == PT_PPC) {
|
||||
/* Write pseudo-PC to ERP only if changed. */
|
||||
if (data != get_pseudo_pc(task))
|
||||
((unsigned long *)user_regs(task->thread_info))[PT_ERP] = data;
|
||||
} else if (regno <= PT_MAX)
|
||||
return put_debugreg(task->pid, regno, data);
|
||||
else
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by kernel/ptrace.c when detaching.
|
||||
*
|
||||
* Make sure the single step bit is not set.
|
||||
*/
|
||||
void
|
||||
ptrace_disable(struct task_struct *child)
|
||||
{
|
||||
unsigned long tmp;
|
||||
|
||||
/* Deconfigure SPC and S-bit. */
|
||||
tmp = get_reg(child, PT_CCS) & ~SBIT_USER;
|
||||
put_reg(child, PT_CCS, tmp);
|
||||
put_reg(child, PT_SPC, 0);
|
||||
|
||||
/* Deconfigure any watchpoints associated with the child. */
|
||||
deconfigure_bp(child->pid);
|
||||
}
|
||||
|
||||
|
||||
asmlinkage int
|
||||
sys_ptrace(long request, long pid, long addr, long data)
|
||||
{
|
||||
struct task_struct *child;
|
||||
int ret;
|
||||
unsigned long __user *datap = (unsigned long __user *)data;
|
||||
|
||||
lock_kernel();
|
||||
ret = -EPERM;
|
||||
|
||||
if (request == PTRACE_TRACEME) {
|
||||
/* are we already being traced? */
|
||||
if (current->ptrace & PT_PTRACED)
|
||||
goto out;
|
||||
ret = security_ptrace(current->parent, current);
|
||||
if (ret)
|
||||
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) /* Leave the init process alone! */
|
||||
goto out_tsk;
|
||||
|
||||
if (request == PTRACE_ATTACH) {
|
||||
ret = ptrace_attach(child);
|
||||
goto out_tsk;
|
||||
}
|
||||
|
||||
ret = ptrace_check_attach(child, request == PTRACE_KILL);
|
||||
if (ret < 0)
|
||||
goto out_tsk;
|
||||
|
||||
switch (request) {
|
||||
/* Read word at location address. */
|
||||
case PTRACE_PEEKTEXT:
|
||||
case PTRACE_PEEKDATA: {
|
||||
unsigned long tmp;
|
||||
int copied;
|
||||
|
||||
ret = -EIO;
|
||||
|
||||
/* The signal trampoline page is outside the normal user-addressable
|
||||
* space but still accessible. This is hack to make it possible to
|
||||
* access the signal handler code in GDB.
|
||||
*/
|
||||
if ((addr & PAGE_MASK) == cris_signal_return_page) {
|
||||
/* The trampoline page is globally mapped, no page table to traverse.*/
|
||||
tmp = *(unsigned long*)addr;
|
||||
} else {
|
||||
copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
|
||||
|
||||
if (copied != sizeof(tmp))
|
||||
break;
|
||||
}
|
||||
|
||||
ret = put_user(tmp,datap);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Read the word at location address in the USER area. */
|
||||
case PTRACE_PEEKUSR: {
|
||||
unsigned long tmp;
|
||||
|
||||
ret = -EIO;
|
||||
if ((addr & 3) || addr < 0 || addr > PT_MAX << 2)
|
||||
break;
|
||||
|
||||
tmp = get_reg(child, addr >> 2);
|
||||
ret = put_user(tmp, datap);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Write the word at location address. */
|
||||
case PTRACE_POKETEXT:
|
||||
case PTRACE_POKEDATA:
|
||||
ret = 0;
|
||||
|
||||
if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
|
||||
break;
|
||||
|
||||
ret = -EIO;
|
||||
break;
|
||||
|
||||
/* Write the word at location address in the USER area. */
|
||||
case PTRACE_POKEUSR:
|
||||
ret = -EIO;
|
||||
if ((addr & 3) || addr < 0 || addr > PT_MAX << 2)
|
||||
break;
|
||||
|
||||
addr >>= 2;
|
||||
|
||||
if (addr == PT_CCS) {
|
||||
/* don't allow the tracing process to change stuff like
|
||||
* interrupt enable, kernel/user bit, dma enables etc.
|
||||
*/
|
||||
data &= CCS_MASK;
|
||||
data |= get_reg(child, PT_CCS) & ~CCS_MASK;
|
||||
}
|
||||
if (put_reg(child, addr, data))
|
||||
break;
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
case PTRACE_SYSCALL:
|
||||
case PTRACE_CONT:
|
||||
ret = -EIO;
|
||||
|
||||
if (!valid_signal(data))
|
||||
break;
|
||||
|
||||
/* Continue means no single-step. */
|
||||
put_reg(child, PT_SPC, 0);
|
||||
|
||||
if (!get_debugreg(child->pid, PT_BP_CTRL)) {
|
||||
unsigned long tmp;
|
||||
/* If no h/w bp configured, disable S bit. */
|
||||
tmp = get_reg(child, PT_CCS) & ~SBIT_USER;
|
||||
put_reg(child, PT_CCS, tmp);
|
||||
}
|
||||
|
||||
if (request == PTRACE_SYSCALL) {
|
||||
set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
|
||||
}
|
||||
else {
|
||||
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
|
||||
}
|
||||
|
||||
child->exit_code = data;
|
||||
|
||||
/* TODO: make sure any pending breakpoint is killed */
|
||||
wake_up_process(child);
|
||||
ret = 0;
|
||||
|
||||
break;
|
||||
|
||||
/* Make the child exit by sending it a sigkill. */
|
||||
case PTRACE_KILL:
|
||||
ret = 0;
|
||||
|
||||
if (child->exit_state == EXIT_ZOMBIE)
|
||||
break;
|
||||
|
||||
child->exit_code = SIGKILL;
|
||||
|
||||
/* Deconfigure single-step and h/w bp. */
|
||||
ptrace_disable(child);
|
||||
|
||||
/* TODO: make sure any pending breakpoint is killed */
|
||||
wake_up_process(child);
|
||||
break;
|
||||
|
||||
/* Set the trap flag. */
|
||||
case PTRACE_SINGLESTEP: {
|
||||
unsigned long tmp;
|
||||
ret = -EIO;
|
||||
|
||||
/* Set up SPC if not set already (in which case we have
|
||||
no other choice but to trust it). */
|
||||
if (!get_reg(child, PT_SPC)) {
|
||||
/* In case we're stopped in a delay slot. */
|
||||
tmp = get_reg(child, PT_ERP) & ~1;
|
||||
put_reg(child, PT_SPC, tmp);
|
||||
}
|
||||
tmp = get_reg(child, PT_CCS) | SBIT_USER;
|
||||
put_reg(child, PT_CCS, tmp);
|
||||
|
||||
if (!valid_signal(data))
|
||||
break;
|
||||
|
||||
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
|
||||
|
||||
/* TODO: set some clever breakpoint mechanism... */
|
||||
|
||||
child->exit_code = data;
|
||||
wake_up_process(child);
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
}
|
||||
case PTRACE_DETACH:
|
||||
ret = ptrace_detach(child, data);
|
||||
break;
|
||||
|
||||
/* Get all GP registers from the child. */
|
||||
case PTRACE_GETREGS: {
|
||||
int i;
|
||||
unsigned long tmp;
|
||||
|
||||
for (i = 0; i <= PT_MAX; i++) {
|
||||
tmp = get_reg(child, i);
|
||||
|
||||
if (put_user(tmp, datap)) {
|
||||
ret = -EFAULT;
|
||||
goto out_tsk;
|
||||
}
|
||||
|
||||
datap++;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set all GP registers in the child. */
|
||||
case PTRACE_SETREGS: {
|
||||
int i;
|
||||
unsigned long tmp;
|
||||
|
||||
for (i = 0; i <= PT_MAX; i++) {
|
||||
if (get_user(tmp, datap)) {
|
||||
ret = -EFAULT;
|
||||
goto out_tsk;
|
||||
}
|
||||
|
||||
if (i == PT_CCS) {
|
||||
tmp &= CCS_MASK;
|
||||
tmp |= get_reg(child, PT_CCS) & ~CCS_MASK;
|
||||
}
|
||||
|
||||
put_reg(child, i, tmp);
|
||||
datap++;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ret = ptrace_request(child, request, addr, data);
|
||||
break;
|
||||
}
|
||||
out_tsk:
|
||||
put_task_struct(child);
|
||||
out:
|
||||
unlock_kernel();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void do_syscall_trace(void)
|
||||
{
|
||||
if (!test_thread_flag(TIF_SYSCALL_TRACE))
|
||||
return;
|
||||
|
||||
if (!(current->ptrace & PT_PTRACED))
|
||||
return;
|
||||
|
||||
/* the 0x80 provides a way for the tracing parent to distinguish
|
||||
between a syscall stop and SIGTRAP delivery */
|
||||
ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
|
||||
? 0x80 : 0));
|
||||
|
||||
/*
|
||||
* This isn't the same as continuing with a signal, but it will do for
|
||||
* normal use.
|
||||
*/
|
||||
if (current->exit_code) {
|
||||
send_sig(current->exit_code, current, 1);
|
||||
current->exit_code = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns the size of an instruction that has a delay slot. */
|
||||
|
||||
static int insn_size(struct task_struct *child, unsigned long pc)
|
||||
{
|
||||
unsigned long opcode;
|
||||
int copied;
|
||||
int opsize = 0;
|
||||
|
||||
/* Read the opcode at pc (do what PTRACE_PEEKTEXT would do). */
|
||||
copied = access_process_vm(child, pc, &opcode, sizeof(opcode), 0);
|
||||
if (copied != sizeof(opcode))
|
||||
return 0;
|
||||
|
||||
switch ((opcode & 0x0f00) >> 8) {
|
||||
case 0x0:
|
||||
case 0x9:
|
||||
case 0xb:
|
||||
opsize = 2;
|
||||
break;
|
||||
case 0xe:
|
||||
case 0xf:
|
||||
opsize = 6;
|
||||
break;
|
||||
case 0xd:
|
||||
/* Could be 4 or 6; check more bits. */
|
||||
if ((opcode & 0xff) == 0xff)
|
||||
opsize = 4;
|
||||
else
|
||||
opsize = 6;
|
||||
break;
|
||||
default:
|
||||
panic("ERROR: Couldn't find size of opcode 0x%lx at 0x%lx\n",
|
||||
opcode, pc);
|
||||
}
|
||||
|
||||
return opsize;
|
||||
}
|
||||
|
||||
static unsigned long get_pseudo_pc(struct task_struct *child)
|
||||
{
|
||||
/* Default value for PC is ERP. */
|
||||
unsigned long pc = get_reg(child, PT_ERP);
|
||||
|
||||
if (pc & 0x1) {
|
||||
unsigned long spc = get_reg(child, PT_SPC);
|
||||
/* Delay slot bit set. Report as stopped on proper
|
||||
instruction. */
|
||||
if (spc) {
|
||||
/* Rely on SPC if set. FIXME: We might want to check
|
||||
that EXS indicates we stopped due to a single-step
|
||||
exception. */
|
||||
pc = spc;
|
||||
} else {
|
||||
/* Calculate the PC from the size of the instruction
|
||||
that the delay slot we're in belongs to. */
|
||||
pc += insn_size(child, pc & ~1) - 1;
|
||||
}
|
||||
}
|
||||
return pc;
|
||||
}
|
||||
|
||||
static long bp_owner = 0;
|
||||
|
||||
/* Reachable from exit_thread in signal.c, so not static. */
|
||||
void deconfigure_bp(long pid)
|
||||
{
|
||||
int bp;
|
||||
|
||||
/* Only deconfigure if the pid is the owner. */
|
||||
if (bp_owner != pid)
|
||||
return;
|
||||
|
||||
for (bp = 0; bp < 6; bp++) {
|
||||
unsigned long tmp;
|
||||
/* Deconfigure start and end address (also gets rid of ownership). */
|
||||
put_debugreg(pid, PT_BP + 3 + (bp * 2), 0);
|
||||
put_debugreg(pid, PT_BP + 4 + (bp * 2), 0);
|
||||
|
||||
/* Deconfigure relevant bits in control register. */
|
||||
tmp = get_debugreg(pid, PT_BP_CTRL) & ~(3 << (2 + (bp * 4)));
|
||||
put_debugreg(pid, PT_BP_CTRL, tmp);
|
||||
}
|
||||
/* No owner now. */
|
||||
bp_owner = 0;
|
||||
}
|
||||
|
||||
static int put_debugreg(long pid, unsigned int regno, long data)
|
||||
{
|
||||
int ret = 0;
|
||||
register int old_srs;
|
||||
|
||||
#ifdef CONFIG_ETRAX_KGDB
|
||||
/* Ignore write, but pretend it was ok if value is 0
|
||||
(we don't want POKEUSR/SETREGS failing unnessecarily). */
|
||||
return (data == 0) ? ret : -1;
|
||||
#endif
|
||||
|
||||
/* Simple owner management. */
|
||||
if (!bp_owner)
|
||||
bp_owner = pid;
|
||||
else if (bp_owner != pid) {
|
||||
/* Ignore write, but pretend it was ok if value is 0
|
||||
(we don't want POKEUSR/SETREGS failing unnessecarily). */
|
||||
return (data == 0) ? ret : -1;
|
||||
}
|
||||
|
||||
/* Remember old SRS. */
|
||||
SPEC_REG_RD(SPEC_REG_SRS, old_srs);
|
||||
/* Switch to BP bank. */
|
||||
SUPP_BANK_SEL(BANK_BP);
|
||||
|
||||
switch (regno - PT_BP) {
|
||||
case 0:
|
||||
SUPP_REG_WR(0, data); break;
|
||||
case 1:
|
||||
case 2:
|
||||
if (data)
|
||||
ret = -1;
|
||||
break;
|
||||
case 3:
|
||||
SUPP_REG_WR(3, data); break;
|
||||
case 4:
|
||||
SUPP_REG_WR(4, data); break;
|
||||
case 5:
|
||||
SUPP_REG_WR(5, data); break;
|
||||
case 6:
|
||||
SUPP_REG_WR(6, data); break;
|
||||
case 7:
|
||||
SUPP_REG_WR(7, data); break;
|
||||
case 8:
|
||||
SUPP_REG_WR(8, data); break;
|
||||
case 9:
|
||||
SUPP_REG_WR(9, data); break;
|
||||
case 10:
|
||||
SUPP_REG_WR(10, data); break;
|
||||
case 11:
|
||||
SUPP_REG_WR(11, data); break;
|
||||
case 12:
|
||||
SUPP_REG_WR(12, data); break;
|
||||
case 13:
|
||||
SUPP_REG_WR(13, data); break;
|
||||
case 14:
|
||||
SUPP_REG_WR(14, data); break;
|
||||
default:
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Restore SRS. */
|
||||
SPEC_REG_WR(SPEC_REG_SRS, old_srs);
|
||||
/* Just for show. */
|
||||
NOP();
|
||||
NOP();
|
||||
NOP();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long get_debugreg(long pid, unsigned int regno)
|
||||
{
|
||||
register int old_srs;
|
||||
register long data;
|
||||
|
||||
if (pid != bp_owner) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Remember old SRS. */
|
||||
SPEC_REG_RD(SPEC_REG_SRS, old_srs);
|
||||
/* Switch to BP bank. */
|
||||
SUPP_BANK_SEL(BANK_BP);
|
||||
|
||||
switch (regno - PT_BP) {
|
||||
case 0:
|
||||
SUPP_REG_RD(0, data); break;
|
||||
case 1:
|
||||
case 2:
|
||||
/* error return value? */
|
||||
data = 0;
|
||||
break;
|
||||
case 3:
|
||||
SUPP_REG_RD(3, data); break;
|
||||
case 4:
|
||||
SUPP_REG_RD(4, data); break;
|
||||
case 5:
|
||||
SUPP_REG_RD(5, data); break;
|
||||
case 6:
|
||||
SUPP_REG_RD(6, data); break;
|
||||
case 7:
|
||||
SUPP_REG_RD(7, data); break;
|
||||
case 8:
|
||||
SUPP_REG_RD(8, data); break;
|
||||
case 9:
|
||||
SUPP_REG_RD(9, data); break;
|
||||
case 10:
|
||||
SUPP_REG_RD(10, data); break;
|
||||
case 11:
|
||||
SUPP_REG_RD(11, data); break;
|
||||
case 12:
|
||||
SUPP_REG_RD(12, data); break;
|
||||
case 13:
|
||||
SUPP_REG_RD(13, data); break;
|
||||
case 14:
|
||||
SUPP_REG_RD(14, data); break;
|
||||
default:
|
||||
/* error return value? */
|
||||
data = 0;
|
||||
}
|
||||
|
||||
/* Restore SRS. */
|
||||
SPEC_REG_WR(SPEC_REG_SRS, old_srs);
|
||||
/* Just for show. */
|
||||
NOP();
|
||||
NOP();
|
||||
NOP();
|
||||
|
||||
return data;
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Display CPU info in /proc/cpuinfo.
|
||||
*
|
||||
* Copyright (C) 2003, Axis Communications AB.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/param.h>
|
||||
|
||||
#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
|
||||
#define HAS_MMU_BUG 0x0100
|
||||
|
||||
struct cpu_info {
|
||||
char *cpu_model;
|
||||
unsigned short rev;
|
||||
unsigned short cache_size;
|
||||
unsigned short flags;
|
||||
};
|
||||
|
||||
/* Some of these model are here for historical reasons only. */
|
||||
static struct cpu_info cpinfo[] = {
|
||||
{"ETRAX 1", 0, 0, 0},
|
||||
{"ETRAX 2", 1, 0, 0},
|
||||
{"ETRAX 3", 2, 0, 0},
|
||||
{"ETRAX 4", 3, 0, 0},
|
||||
{"Simulator", 7, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA},
|
||||
{"ETRAX 100", 8, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_IRQ_BUG},
|
||||
{"ETRAX 100", 9, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA},
|
||||
|
||||
{"ETRAX 100LX", 10, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB
|
||||
| HAS_MMU | HAS_MMU_BUG},
|
||||
|
||||
{"ETRAX 100LX v2", 11, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB
|
||||
| HAS_MMU},
|
||||
|
||||
{"ETRAX FS", 32, 32, HAS_ETHERNET100 | HAS_ATA | HAS_MMU},
|
||||
|
||||
{"Unknown", 0, 0, 0}
|
||||
};
|
||||
|
||||
int
|
||||
show_cpuinfo(struct seq_file *m, void *v)
|
||||
{
|
||||
int i;
|
||||
int cpu = (int)v - 1;
|
||||
int entries;
|
||||
unsigned long revision;
|
||||
struct cpu_info *info;
|
||||
|
||||
entries = sizeof cpinfo / sizeof(struct cpu_info);
|
||||
info = &cpinfo[entries - 1];
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
if (!cpu_online(cpu))
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
revision = rdvr();
|
||||
|
||||
for (i = 0; i < entries; i++) {
|
||||
if (cpinfo[i].rev == revision) {
|
||||
info = &cpinfo[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return seq_printf(m,
|
||||
"processor\t: %d\n"
|
||||
"cpu\t\t: CRIS\n"
|
||||
"cpu revision\t: %lu\n"
|
||||
"cpu model\t: %s\n"
|
||||
"cache size\t: %d KB\n"
|
||||
"fpu\t\t: %s\n"
|
||||
"mmu\t\t: %s\n"
|
||||
"mmu DMA bug\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\n",
|
||||
|
||||
cpu,
|
||||
revision,
|
||||
info->cpu_model,
|
||||
info->cache_size,
|
||||
info->flags & HAS_FPU ? "yes" : "no",
|
||||
info->flags & HAS_MMU ? "yes" : "no",
|
||||
info->flags & HAS_MMU_BUG ? "yes" : "no",
|
||||
info->flags & HAS_ETHERNET100 ? "10/100" : "10",
|
||||
info->flags & HAS_TOKENRING ? "4/16 Mbps" : "no",
|
||||
info->flags & HAS_SCSI ? "yes" : "no",
|
||||
info->flags & HAS_ATA ? "yes" : "no",
|
||||
info->flags & HAS_USB ? "yes" : "no",
|
||||
(loops_per_jiffy * HZ + 500) / 500000,
|
||||
((loops_per_jiffy * HZ + 500) / 5000) % 100);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
|
||||
void
|
||||
show_etrax_copyright(void)
|
||||
{
|
||||
printk(KERN_INFO
|
||||
"Linux/CRISv32 port on ETRAX FS (C) 2003, 2004 Axis Communications AB\n");
|
||||
}
|
|
@ -0,0 +1,708 @@
|
|||
/*
|
||||
* Copyright (C) 2003, Axis Communications AB.
|
||||
*/
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/ucontext.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/arch/ptrace.h>
|
||||
#include <asm/arch/hwregs/cpu_vect.h>
|
||||
|
||||
extern unsigned long cris_signal_return_page;
|
||||
|
||||
/* Flag to check if a signal is blockable. */
|
||||
#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
|
||||
|
||||
/*
|
||||
* A syscall in CRIS is really a "break 13" instruction, which is 2
|
||||
* bytes. The registers is manipulated so upon return the instruction
|
||||
* will be executed again.
|
||||
*
|
||||
* This relies on that PC points to the instruction after the break call.
|
||||
*/
|
||||
#define RESTART_CRIS_SYS(regs) regs->r10 = regs->orig_r10; regs->erp -= 2;
|
||||
|
||||
/* Signal frames. */
|
||||
struct signal_frame {
|
||||
struct sigcontext sc;
|
||||
unsigned long extramask[_NSIG_WORDS - 1];
|
||||
unsigned char retcode[8]; /* Trampoline code. */
|
||||
};
|
||||
|
||||
struct rt_signal_frame {
|
||||
struct siginfo *pinfo;
|
||||
void *puc;
|
||||
struct siginfo info;
|
||||
struct ucontext uc;
|
||||
unsigned char retcode[8]; /* Trampoline code. */
|
||||
};
|
||||
|
||||
int do_signal(int restart, sigset_t *oldset, struct pt_regs *regs);
|
||||
void keep_debug_flags(unsigned long oldccs, unsigned long oldspc,
|
||||
struct pt_regs *regs);
|
||||
/*
|
||||
* Swap in the new signal mask, and wait for a signal. Define some
|
||||
* dummy arguments to be able to reach the regs argument.
|
||||
*/
|
||||
int
|
||||
sys_sigsuspend(old_sigset_t mask, long r11, long r12, long r13, long mof,
|
||||
long srp, struct pt_regs *regs)
|
||||
{
|
||||
sigset_t saveset;
|
||||
|
||||
mask &= _BLOCKABLE;
|
||||
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
|
||||
saveset = current->blocked;
|
||||
|
||||
siginitset(¤t->blocked, mask);
|
||||
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
|
||||
regs->r10 = -EINTR;
|
||||
|
||||
while (1) {
|
||||
current->state = TASK_INTERRUPTIBLE;
|
||||
schedule();
|
||||
|
||||
if (do_signal(0, &saveset, regs)) {
|
||||
/*
|
||||
* This point is reached twice: once to call
|
||||
* the signal handler, then again to return
|
||||
* from the sigsuspend system call. When
|
||||
* calling the signal handler, R10 hold the
|
||||
* signal number as set by do_signal(). The
|
||||
* sigsuspend call will always return with
|
||||
* the restored value above; -EINTR.
|
||||
*/
|
||||
return regs->r10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Define some dummy arguments to be able to reach the regs argument. */
|
||||
int
|
||||
sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, long r12, long r13,
|
||||
long mof, long srp, struct pt_regs *regs)
|
||||
{
|
||||
sigset_t saveset;
|
||||
sigset_t newset;
|
||||
|
||||
if (sigsetsize != sizeof(sigset_t))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&newset, unewset, sizeof(newset)))
|
||||
return -EFAULT;
|
||||
|
||||
sigdelsetmask(&newset, ~_BLOCKABLE);
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
|
||||
saveset = current->blocked;
|
||||
current->blocked = newset;
|
||||
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
|
||||
regs->r10 = -EINTR;
|
||||
|
||||
while (1) {
|
||||
current->state = TASK_INTERRUPTIBLE;
|
||||
schedule();
|
||||
|
||||
if (do_signal(0, &saveset, regs)) {
|
||||
/* See comment in function above. */
|
||||
return regs->r10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
sys_sigaction(int signal, const struct old_sigaction *act,
|
||||
struct old_sigaction *oact)
|
||||
{
|
||||
int retval;
|
||||
struct k_sigaction newk;
|
||||
struct k_sigaction oldk;
|
||||
|
||||
if (act) {
|
||||
old_sigset_t mask;
|
||||
|
||||
if (!access_ok(VERIFY_READ, act, sizeof(*act)) ||
|
||||
__get_user(newk.sa.sa_handler, &act->sa_handler) ||
|
||||
__get_user(newk.sa.sa_restorer, &act->sa_restorer))
|
||||
return -EFAULT;
|
||||
|
||||
__get_user(newk.sa.sa_flags, &act->sa_flags);
|
||||
__get_user(mask, &act->sa_mask);
|
||||
siginitset(&newk.sa.sa_mask, mask);
|
||||
}
|
||||
|
||||
retval = do_sigaction(signal, act ? &newk : NULL, oact ? &oldk : NULL);
|
||||
|
||||
if (!retval && oact) {
|
||||
if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) ||
|
||||
__put_user(oldk.sa.sa_handler, &oact->sa_handler) ||
|
||||
__put_user(oldk.sa.sa_restorer, &oact->sa_restorer))
|
||||
return -EFAULT;
|
||||
|
||||
__put_user(oldk.sa.sa_flags, &oact->sa_flags);
|
||||
__put_user(oldk.sa.sa_mask.sig[0], &oact->sa_mask);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss)
|
||||
{
|
||||
return do_sigaltstack(uss, uoss, rdusp());
|
||||
}
|
||||
|
||||
static int
|
||||
restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
|
||||
{
|
||||
unsigned int err = 0;
|
||||
unsigned long old_usp;
|
||||
|
||||
/* Always make any pending restarted system calls return -EINTR */
|
||||
current_thread_info()->restart_block.fn = do_no_restart_syscall;
|
||||
|
||||
/*
|
||||
* Restore the registers from &sc->regs. sc is already checked
|
||||
* for VERIFY_READ since the signal_frame was previously
|
||||
* checked in sys_sigreturn().
|
||||
*/
|
||||
if (__copy_from_user(regs, sc, sizeof(struct pt_regs)))
|
||||
goto badframe;
|
||||
|
||||
/* Make that the user-mode flag is set. */
|
||||
regs->ccs |= (1 << (U_CCS_BITNR + CCS_SHIFT));
|
||||
|
||||
/* Restore the old USP. */
|
||||
err |= __get_user(old_usp, &sc->usp);
|
||||
wrusp(old_usp);
|
||||
|
||||
return err;
|
||||
|
||||
badframe:
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Define some dummy arguments to be able to reach the regs argument. */
|
||||
asmlinkage int
|
||||
sys_sigreturn(long r10, long r11, long r12, long r13, long mof, long srp,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
sigset_t set;
|
||||
struct signal_frame __user *frame;
|
||||
unsigned long oldspc = regs->spc;
|
||||
unsigned long oldccs = regs->ccs;
|
||||
|
||||
frame = (struct signal_frame *) rdusp();
|
||||
|
||||
/*
|
||||
* Since the signal is stacked on a dword boundary, the frame
|
||||
* should be dword aligned here as well. It it's not, then the
|
||||
* user is trying some funny business.
|
||||
*/
|
||||
if (((long)frame) & 3)
|
||||
goto badframe;
|
||||
|
||||
if (!access_ok(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(¤t->sighand->siglock);
|
||||
|
||||
current->blocked = set;
|
||||
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
|
||||
if (restore_sigcontext(regs, &frame->sc))
|
||||
goto badframe;
|
||||
|
||||
keep_debug_flags(oldccs, oldspc, regs);
|
||||
|
||||
return regs->r10;
|
||||
|
||||
badframe:
|
||||
force_sig(SIGSEGV, current);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Define some dummy variables to be able to reach the regs argument. */
|
||||
asmlinkage int
|
||||
sys_rt_sigreturn(long r10, long r11, long r12, long r13, long mof, long srp,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
sigset_t set;
|
||||
struct rt_signal_frame __user *frame;
|
||||
unsigned long oldspc = regs->spc;
|
||||
unsigned long oldccs = regs->ccs;
|
||||
|
||||
frame = (struct rt_signal_frame *) rdusp();
|
||||
|
||||
/*
|
||||
* Since the signal is stacked on a dword boundary, the frame
|
||||
* should be dword aligned here as well. It it's not, then the
|
||||
* user is trying some funny business.
|
||||
*/
|
||||
if (((long)frame) & 3)
|
||||
goto badframe;
|
||||
|
||||
if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
|
||||
goto badframe;
|
||||
|
||||
if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
|
||||
goto badframe;
|
||||
|
||||
sigdelsetmask(&set, ~_BLOCKABLE);
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
|
||||
current->blocked = set;
|
||||
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
|
||||
if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
|
||||
goto badframe;
|
||||
|
||||
if (do_sigaltstack(&frame->uc.uc_stack, NULL, rdusp()) == -EFAULT)
|
||||
goto badframe;
|
||||
|
||||
keep_debug_flags(oldccs, oldspc, regs);
|
||||
|
||||
return regs->r10;
|
||||
|
||||
badframe:
|
||||
force_sig(SIGSEGV, current);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Setup a signal frame. */
|
||||
static int
|
||||
setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
|
||||
unsigned long mask)
|
||||
{
|
||||
int err;
|
||||
unsigned long usp;
|
||||
|
||||
err = 0;
|
||||
usp = rdusp();
|
||||
|
||||
/*
|
||||
* Copy the registers. They are located first in sc, so it's
|
||||
* possible to use sc directly.
|
||||
*/
|
||||
err |= __copy_to_user(sc, regs, sizeof(struct pt_regs));
|
||||
|
||||
err |= __put_user(mask, &sc->oldmask);
|
||||
err |= __put_user(usp, &sc->usp);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Figure out where to put the new signal frame - usually on the stack. */
|
||||
static inline void __user *
|
||||
get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size)
|
||||
{
|
||||
unsigned long sp;
|
||||
|
||||
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 __user *)(sp - frame_size);
|
||||
}
|
||||
|
||||
/* Grab and setup a signal frame.
|
||||
*
|
||||
* Basically a lot of state-info is stacked, and arranged for the
|
||||
* user-mode program to return to the kernel using either a trampiline
|
||||
* 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)
|
||||
{
|
||||
int err;
|
||||
unsigned long return_ip;
|
||||
struct signal_frame __user *frame;
|
||||
|
||||
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 user-space. If provided, use a stub
|
||||
* already located in user-space.
|
||||
*/
|
||||
if (ka->sa.sa_flags & SA_RESTORER) {
|
||||
return_ip = (unsigned long)ka->sa.sa_restorer;
|
||||
} else {
|
||||
/* Trampoline - the desired return ip is in the signal return page. */
|
||||
return_ip = cris_signal_return_page;
|
||||
|
||||
/*
|
||||
* This is movu.w __NR_sigreturn, r9; break 13;
|
||||
*
|
||||
* WE DO NOT USE IT ANY MORE! It's only left here for historical
|
||||
* reasons and because gdb uses it as a signature to notice
|
||||
* signal handler stack frames.
|
||||
*/
|
||||
err |= __put_user(0x9c5f, (short __user*)(frame->retcode+0));
|
||||
err |= __put_user(__NR_sigreturn, (short __user*)(frame->retcode+2));
|
||||
err |= __put_user(0xe93d, (short __user*)(frame->retcode+4));
|
||||
}
|
||||
|
||||
if (err)
|
||||
goto give_sigsegv;
|
||||
|
||||
/*
|
||||
* Set up registers for signal handler.
|
||||
*
|
||||
* Where the code enters now.
|
||||
* Where the code enter later.
|
||||
* First argument, signo.
|
||||
*/
|
||||
regs->erp = (unsigned long) ka->sa.sa_handler;
|
||||
regs->srp = return_ip;
|
||||
regs->r10 = sig;
|
||||
|
||||
/* 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)
|
||||
{
|
||||
int err;
|
||||
unsigned long return_ip;
|
||||
struct rt_signal_frame __user *frame;
|
||||
|
||||
err = 0;
|
||||
frame = get_sigframe(ka, regs, sizeof(*frame));
|
||||
|
||||
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
|
||||
goto give_sigsegv;
|
||||
|
||||
/* TODO: what is the current->exec_domain stuff and invmap ? */
|
||||
|
||||
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 user-space. If provided, use a stub
|
||||
* already located in user-space.
|
||||
*/
|
||||
if (ka->sa.sa_flags & SA_RESTORER) {
|
||||
return_ip = (unsigned long) ka->sa.sa_restorer;
|
||||
} else {
|
||||
/* Trampoline - the desired return ip is in the signal return page. */
|
||||
return_ip = cris_signal_return_page + 6;
|
||||
|
||||
/*
|
||||
* This is movu.w __NR_rt_sigreturn, r9; break 13;
|
||||
*
|
||||
* WE DO NOT USE IT ANY MORE! It's only left here for historical
|
||||
* reasons and because gdb uses it as a signature to notice
|
||||
* signal handler stack frames.
|
||||
*/
|
||||
err |= __put_user(0x9c5f, (short __user*)(frame->retcode+0));
|
||||
|
||||
err |= __put_user(__NR_rt_sigreturn,
|
||||
(short __user*)(frame->retcode+2));
|
||||
|
||||
err |= __put_user(0xe93d, (short __user*)(frame->retcode+4));
|
||||
}
|
||||
|
||||
if (err)
|
||||
goto give_sigsegv;
|
||||
|
||||
/*
|
||||
* Set up registers for signal handler.
|
||||
*
|
||||
* Where the code enters now.
|
||||
* Where the code enters later.
|
||||
* First argument is signo.
|
||||
* Second argument is (siginfo_t *).
|
||||
* Third argument is unused.
|
||||
*/
|
||||
regs->erp = (unsigned long) ka->sa.sa_handler;
|
||||
regs->srp = return_ip;
|
||||
regs->r10 = sig;
|
||||
regs->r11 = (unsigned long) &frame->info;
|
||||
regs->r12 = 0;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* Invoke a singal handler to, well, handle the signal. */
|
||||
extern inline void
|
||||
handle_signal(int canrestart, unsigned long sig,
|
||||
siginfo_t *info, struct k_sigaction *ka,
|
||||
sigset_t *oldset, struct pt_regs * regs)
|
||||
{
|
||||
/* Check if this got called from a system call. */
|
||||
if (canrestart) {
|
||||
/* If so, check system call restarting. */
|
||||
switch (regs->r10) {
|
||||
case -ERESTART_RESTARTBLOCK:
|
||||
case -ERESTARTNOHAND:
|
||||
/*
|
||||
* This means that the syscall should
|
||||
* only be restarted if there was no
|
||||
* handler for the signal, and since
|
||||
* this point isn't reached unless
|
||||
* there is a handler, there's no need
|
||||
* to restart.
|
||||
*/
|
||||
regs->r10 = -EINTR;
|
||||
break;
|
||||
|
||||
case -ERESTARTSYS:
|
||||
/*
|
||||
* This means 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;
|
||||
}
|
||||
|
||||
/* Fall through. */
|
||||
|
||||
case -ERESTARTNOINTR:
|
||||
/*
|
||||
* This means that the syscall should
|
||||
* be called again after the signal
|
||||
* handler returns.
|
||||
*/
|
||||
RESTART_CRIS_SYS(regs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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(¤t->sighand->siglock);
|
||||
sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask);
|
||||
sigaddset(¤t->blocked,sig);
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Also note that the regs structure given here as an argument, is the latest
|
||||
* pushed pt_regs. It may or may not be the same as the first pushed registers
|
||||
* when the initial usermode->kernelmode transition took place. Therefore
|
||||
* we can use user_mode(regs) to see if we came directly from kernel or user
|
||||
* mode below.
|
||||
*/
|
||||
int
|
||||
do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs)
|
||||
{
|
||||
int signr;
|
||||
siginfo_t info;
|
||||
struct k_sigaction ka;
|
||||
|
||||
/*
|
||||
* The common case should go fast, which is why this point is
|
||||
* reached from kernel-mode. If that's the case, just return
|
||||
* without doing anything.
|
||||
*/
|
||||
if (!user_mode(regs))
|
||||
return 1;
|
||||
|
||||
if (!oldset)
|
||||
oldset = ¤t->blocked;
|
||||
|
||||
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
|
||||
|
||||
if (signr > 0) {
|
||||
/* Deliver the signal. */
|
||||
handle_signal(canrestart, signr, &info, &ka, oldset, regs);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Got here 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);
|
||||
}
|
||||
|
||||
if (regs->r10 == -ERESTART_RESTARTBLOCK){
|
||||
regs->r10 = __NR_restart_syscall;
|
||||
regs->erp -= 2;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
asmlinkage void
|
||||
ugdb_trap_user(struct thread_info *ti, int sig)
|
||||
{
|
||||
if (((user_regs(ti)->exs & 0xff00) >> 8) != SINGLE_STEP_INTR_VECT) {
|
||||
/* Zero single-step PC if the reason we stopped wasn't a single
|
||||
step exception. This is to avoid relying on it when it isn't
|
||||
reliable. */
|
||||
user_regs(ti)->spc = 0;
|
||||
}
|
||||
/* FIXME: Filter out false h/w breakpoint hits (i.e. EDA
|
||||
not withing any configured h/w breakpoint range). Synchronize with
|
||||
what already exists for kernel debugging. */
|
||||
if (((user_regs(ti)->exs & 0xff00) >> 8) == BREAK_8_INTR_VECT) {
|
||||
/* Break 8: subtract 2 from ERP unless in a delay slot. */
|
||||
if (!(user_regs(ti)->erp & 0x1))
|
||||
user_regs(ti)->erp -= 2;
|
||||
}
|
||||
sys_kill(ti->task->pid, sig);
|
||||
}
|
||||
|
||||
void
|
||||
keep_debug_flags(unsigned long oldccs, unsigned long oldspc,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
if (oldccs & (1 << Q_CCS_BITNR)) {
|
||||
/* Pending single step due to single-stepping the break 13
|
||||
in the signal trampoline: keep the Q flag. */
|
||||
regs->ccs |= (1 << Q_CCS_BITNR);
|
||||
/* S flag should be set - complain if it's not. */
|
||||
if (!(oldccs & (1 << (S_CCS_BITNR + CCS_SHIFT)))) {
|
||||
printk("Q flag but no S flag?");
|
||||
}
|
||||
regs->ccs |= (1 << (S_CCS_BITNR + CCS_SHIFT));
|
||||
/* Assume the SPC is valid and interesting. */
|
||||
regs->spc = oldspc;
|
||||
|
||||
} else if (oldccs & (1 << (S_CCS_BITNR + CCS_SHIFT))) {
|
||||
/* If a h/w bp was set in the signal handler we need
|
||||
to keep the S flag. */
|
||||
regs->ccs |= (1 << (S_CCS_BITNR + CCS_SHIFT));
|
||||
/* Don't keep the old SPC though; if we got here due to
|
||||
a single-step, the Q flag should have been set. */
|
||||
} else if (regs->spc) {
|
||||
/* If we were single-stepping *before* the signal was taken,
|
||||
we don't want to restore that state now, because GDB will
|
||||
have forgotten all about it. */
|
||||
regs->spc = 0;
|
||||
regs->ccs &= ~(1 << (S_CCS_BITNR + CCS_SHIFT));
|
||||
}
|
||||
}
|
||||
|
||||
/* Set up the trampolines on the signal return page. */
|
||||
int __init
|
||||
cris_init_signal(void)
|
||||
{
|
||||
u16* data = (u16*)kmalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
|
||||
/* This is movu.w __NR_sigreturn, r9; break 13; */
|
||||
data[0] = 0x9c5f;
|
||||
data[1] = __NR_sigreturn;
|
||||
data[2] = 0xe93d;
|
||||
/* This is movu.w __NR_rt_sigreturn, r9; break 13; */
|
||||
data[3] = 0x9c5f;
|
||||
data[4] = __NR_rt_sigreturn;
|
||||
data[5] = 0xe93d;
|
||||
|
||||
/* Map to userspace with appropriate permissions (no write access...) */
|
||||
cris_signal_return_page = (unsigned long)
|
||||
__ioremap_prot(virt_to_phys(data), PAGE_SIZE, PAGE_SIGNAL_TRAMPOLINE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
__initcall(cris_init_signal);
|
|
@ -0,0 +1,348 @@
|
|||
#include <asm/delay.h>
|
||||
#include <asm/arch/irq.h>
|
||||
#include <asm/arch/hwregs/intr_vect.h>
|
||||
#include <asm/arch/hwregs/intr_vect_defs.h>
|
||||
#include <asm/tlbflush.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/arch/hwregs/mmu_defs_asm.h>
|
||||
#include <asm/arch/hwregs/supp_reg.h>
|
||||
#include <asm/atomic.h>
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/timex.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#define IPI_SCHEDULE 1
|
||||
#define IPI_CALL 2
|
||||
#define IPI_FLUSH_TLB 4
|
||||
|
||||
#define FLUSH_ALL (void*)0xffffffff
|
||||
|
||||
/* Vector of locks used for various atomic operations */
|
||||
spinlock_t cris_atomic_locks[] = { [0 ... LOCK_COUNT - 1] = SPIN_LOCK_UNLOCKED};
|
||||
|
||||
/* CPU masks */
|
||||
cpumask_t cpu_online_map = CPU_MASK_NONE;
|
||||
cpumask_t phys_cpu_present_map = CPU_MASK_NONE;
|
||||
|
||||
/* Variables used during SMP boot */
|
||||
volatile int cpu_now_booting = 0;
|
||||
volatile struct thread_info *smp_init_current_idle_thread;
|
||||
|
||||
/* Variables used during IPI */
|
||||
static DEFINE_SPINLOCK(call_lock);
|
||||
static DEFINE_SPINLOCK(tlbstate_lock);
|
||||
|
||||
struct call_data_struct {
|
||||
void (*func) (void *info);
|
||||
void *info;
|
||||
int wait;
|
||||
};
|
||||
|
||||
static struct call_data_struct * call_data;
|
||||
|
||||
static struct mm_struct* flush_mm;
|
||||
static struct vm_area_struct* flush_vma;
|
||||
static unsigned long flush_addr;
|
||||
|
||||
extern int setup_irq(int, struct irqaction *);
|
||||
|
||||
/* Mode registers */
|
||||
static unsigned long irq_regs[NR_CPUS] =
|
||||
{
|
||||
regi_irq,
|
||||
regi_irq2
|
||||
};
|
||||
|
||||
static irqreturn_t crisv32_ipi_interrupt(int irq, void *dev_id, struct pt_regs *regs);
|
||||
static int send_ipi(int vector, int wait, cpumask_t cpu_mask);
|
||||
static struct irqaction irq_ipi = { crisv32_ipi_interrupt, SA_INTERRUPT,
|
||||
CPU_MASK_NONE, "ipi", NULL, NULL};
|
||||
|
||||
extern void cris_mmu_init(void);
|
||||
extern void cris_timer_init(void);
|
||||
|
||||
/* SMP initialization */
|
||||
void __init smp_prepare_cpus(unsigned int max_cpus)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* From now on we can expect IPIs so set them up */
|
||||
setup_irq(IPI_INTR_VECT, &irq_ipi);
|
||||
|
||||
/* Mark all possible CPUs as present */
|
||||
for (i = 0; i < max_cpus; i++)
|
||||
cpu_set(i, phys_cpu_present_map);
|
||||
}
|
||||
|
||||
void __devinit smp_prepare_boot_cpu(void)
|
||||
{
|
||||
/* PGD pointer has moved after per_cpu initialization so
|
||||
* update the MMU.
|
||||
*/
|
||||
pgd_t **pgd;
|
||||
pgd = (pgd_t**)&per_cpu(current_pgd, smp_processor_id());
|
||||
|
||||
SUPP_BANK_SEL(1);
|
||||
SUPP_REG_WR(RW_MM_TLB_PGD, pgd);
|
||||
SUPP_BANK_SEL(2);
|
||||
SUPP_REG_WR(RW_MM_TLB_PGD, pgd);
|
||||
|
||||
cpu_set(0, cpu_online_map);
|
||||
cpu_set(0, phys_cpu_present_map);
|
||||
}
|
||||
|
||||
void __init smp_cpus_done(unsigned int max_cpus)
|
||||
{
|
||||
}
|
||||
|
||||
/* Bring one cpu online.*/
|
||||
static int __init
|
||||
smp_boot_one_cpu(int cpuid)
|
||||
{
|
||||
unsigned timeout;
|
||||
struct task_struct *idle;
|
||||
|
||||
idle = fork_idle(cpuid);
|
||||
if (IS_ERR(idle))
|
||||
panic("SMP: fork failed for CPU:%d", cpuid);
|
||||
|
||||
idle->thread_info->cpu = cpuid;
|
||||
|
||||
/* Information to the CPU that is about to boot */
|
||||
smp_init_current_idle_thread = idle->thread_info;
|
||||
cpu_now_booting = cpuid;
|
||||
|
||||
/* Wait for CPU to come online */
|
||||
for (timeout = 0; timeout < 10000; timeout++) {
|
||||
if(cpu_online(cpuid)) {
|
||||
cpu_now_booting = 0;
|
||||
smp_init_current_idle_thread = NULL;
|
||||
return 0; /* CPU online */
|
||||
}
|
||||
udelay(100);
|
||||
barrier();
|
||||
}
|
||||
|
||||
put_task_struct(idle);
|
||||
idle = NULL;
|
||||
|
||||
printk(KERN_CRIT "SMP: CPU:%d is stuck.\n", cpuid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Secondary CPUs starts uing C here. Here we need to setup CPU
|
||||
* specific stuff such as the local timer and the MMU. */
|
||||
void __init smp_callin(void)
|
||||
{
|
||||
extern void cpu_idle(void);
|
||||
|
||||
int cpu = cpu_now_booting;
|
||||
reg_intr_vect_rw_mask vect_mask = {0};
|
||||
|
||||
/* Initialise the idle task for this CPU */
|
||||
atomic_inc(&init_mm.mm_count);
|
||||
current->active_mm = &init_mm;
|
||||
|
||||
/* Set up MMU */
|
||||
cris_mmu_init();
|
||||
__flush_tlb_all();
|
||||
|
||||
/* Setup local timer. */
|
||||
cris_timer_init();
|
||||
|
||||
/* Enable IRQ and idle */
|
||||
REG_WR(intr_vect, irq_regs[cpu], rw_mask, vect_mask);
|
||||
unmask_irq(IPI_INTR_VECT);
|
||||
unmask_irq(TIMER_INTR_VECT);
|
||||
local_irq_enable();
|
||||
|
||||
cpu_set(cpu, cpu_online_map);
|
||||
cpu_idle();
|
||||
}
|
||||
|
||||
/* Stop execution on this CPU.*/
|
||||
void stop_this_cpu(void* dummy)
|
||||
{
|
||||
local_irq_disable();
|
||||
asm volatile("halt");
|
||||
}
|
||||
|
||||
/* Other calls */
|
||||
void smp_send_stop(void)
|
||||
{
|
||||
smp_call_function(stop_this_cpu, NULL, 1, 0);
|
||||
}
|
||||
|
||||
int setup_profiling_timer(unsigned int multiplier)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
/* cache_decay_ticks is used by the scheduler to decide if a process
|
||||
* is "hot" on one CPU. A higher value means a higher penalty to move
|
||||
* a process to another CPU. Our cache is rather small so we report
|
||||
* 1 tick.
|
||||
*/
|
||||
unsigned long cache_decay_ticks = 1;
|
||||
|
||||
int __devinit __cpu_up(unsigned int cpu)
|
||||
{
|
||||
smp_boot_one_cpu(cpu);
|
||||
return cpu_online(cpu) ? 0 : -ENOSYS;
|
||||
}
|
||||
|
||||
void smp_send_reschedule(int cpu)
|
||||
{
|
||||
cpumask_t cpu_mask = CPU_MASK_NONE;
|
||||
cpu_set(cpu, cpu_mask);
|
||||
send_ipi(IPI_SCHEDULE, 0, cpu_mask);
|
||||
}
|
||||
|
||||
/* TLB flushing
|
||||
*
|
||||
* Flush needs to be done on the local CPU and on any other CPU that
|
||||
* may have the same mapping. The mm->cpu_vm_mask is used to keep track
|
||||
* of which CPUs that a specific process has been executed on.
|
||||
*/
|
||||
void flush_tlb_common(struct mm_struct* mm, struct vm_area_struct* vma, unsigned long addr)
|
||||
{
|
||||
unsigned long flags;
|
||||
cpumask_t cpu_mask;
|
||||
|
||||
spin_lock_irqsave(&tlbstate_lock, flags);
|
||||
cpu_mask = (mm == FLUSH_ALL ? CPU_MASK_ALL : mm->cpu_vm_mask);
|
||||
cpu_clear(smp_processor_id(), cpu_mask);
|
||||
flush_mm = mm;
|
||||
flush_vma = vma;
|
||||
flush_addr = addr;
|
||||
send_ipi(IPI_FLUSH_TLB, 1, cpu_mask);
|
||||
spin_unlock_irqrestore(&tlbstate_lock, flags);
|
||||
}
|
||||
|
||||
void flush_tlb_all(void)
|
||||
{
|
||||
__flush_tlb_all();
|
||||
flush_tlb_common(FLUSH_ALL, FLUSH_ALL, 0);
|
||||
}
|
||||
|
||||
void flush_tlb_mm(struct mm_struct *mm)
|
||||
{
|
||||
__flush_tlb_mm(mm);
|
||||
flush_tlb_common(mm, FLUSH_ALL, 0);
|
||||
/* No more mappings in other CPUs */
|
||||
cpus_clear(mm->cpu_vm_mask);
|
||||
cpu_set(smp_processor_id(), mm->cpu_vm_mask);
|
||||
}
|
||||
|
||||
void flush_tlb_page(struct vm_area_struct *vma,
|
||||
unsigned long addr)
|
||||
{
|
||||
__flush_tlb_page(vma, addr);
|
||||
flush_tlb_common(vma->vm_mm, vma, addr);
|
||||
}
|
||||
|
||||
/* Inter processor interrupts
|
||||
*
|
||||
* The IPIs are used for:
|
||||
* * Force a schedule on a CPU
|
||||
* * FLush TLB on other CPUs
|
||||
* * Call a function on other CPUs
|
||||
*/
|
||||
|
||||
int send_ipi(int vector, int wait, cpumask_t cpu_mask)
|
||||
{
|
||||
int i = 0;
|
||||
reg_intr_vect_rw_ipi ipi = REG_RD(intr_vect, irq_regs[i], rw_ipi);
|
||||
int ret = 0;
|
||||
|
||||
/* Calculate CPUs to send to. */
|
||||
cpus_and(cpu_mask, cpu_mask, cpu_online_map);
|
||||
|
||||
/* Send the IPI. */
|
||||
for_each_cpu_mask(i, cpu_mask)
|
||||
{
|
||||
ipi.vector |= vector;
|
||||
REG_WR(intr_vect, irq_regs[i], rw_ipi, ipi);
|
||||
}
|
||||
|
||||
/* Wait for IPI to finish on other CPUS */
|
||||
if (wait) {
|
||||
for_each_cpu_mask(i, cpu_mask) {
|
||||
int j;
|
||||
for (j = 0 ; j < 1000; j++) {
|
||||
ipi = REG_RD(intr_vect, irq_regs[i], rw_ipi);
|
||||
if (!ipi.vector)
|
||||
break;
|
||||
udelay(100);
|
||||
}
|
||||
|
||||
/* Timeout? */
|
||||
if (ipi.vector) {
|
||||
printk("SMP call timeout from %d to %d\n", smp_processor_id(), i);
|
||||
ret = -ETIMEDOUT;
|
||||
dump_stack();
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* You must not call this function with disabled interrupts or from a
|
||||
* hardware interrupt handler or from a bottom half handler.
|
||||
*/
|
||||
int smp_call_function(void (*func)(void *info), void *info,
|
||||
int nonatomic, int wait)
|
||||
{
|
||||
cpumask_t cpu_mask = CPU_MASK_ALL;
|
||||
struct call_data_struct data;
|
||||
int ret;
|
||||
|
||||
cpu_clear(smp_processor_id(), cpu_mask);
|
||||
|
||||
WARN_ON(irqs_disabled());
|
||||
|
||||
data.func = func;
|
||||
data.info = info;
|
||||
data.wait = wait;
|
||||
|
||||
spin_lock(&call_lock);
|
||||
call_data = &data;
|
||||
ret = send_ipi(IPI_CALL, wait, cpu_mask);
|
||||
spin_unlock(&call_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
irqreturn_t crisv32_ipi_interrupt(int irq, void *dev_id, struct pt_regs *regs)
|
||||
{
|
||||
void (*func) (void *info) = call_data->func;
|
||||
void *info = call_data->info;
|
||||
reg_intr_vect_rw_ipi ipi;
|
||||
|
||||
ipi = REG_RD(intr_vect, irq_regs[smp_processor_id()], rw_ipi);
|
||||
|
||||
if (ipi.vector & IPI_CALL) {
|
||||
func(info);
|
||||
}
|
||||
if (ipi.vector & IPI_FLUSH_TLB) {
|
||||
if (flush_mm == FLUSH_ALL)
|
||||
__flush_tlb_all();
|
||||
else if (flush_vma == FLUSH_ALL)
|
||||
__flush_tlb_mm(flush_mm);
|
||||
else
|
||||
__flush_tlb_page(flush_vma, flush_addr);
|
||||
}
|
||||
|
||||
ipi.vector = 0;
|
||||
REG_WR(intr_vect, irq_regs[smp_processor_id()], rw_ipi, ipi);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
|
@ -0,0 +1,341 @@
|
|||
/* $Id: time.c,v 1.19 2005/04/29 05:40:09 starvik Exp $
|
||||
*
|
||||
* linux/arch/cris/arch-v32/kernel/time.c
|
||||
*
|
||||
* Copyright (C) 2003 Axis Communications AB
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/timex.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/swap.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/threads.h>
|
||||
#include <asm/types.h>
|
||||
#include <asm/signal.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/delay.h>
|
||||
#include <asm/rtc.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
#include <asm/arch/hwregs/reg_map.h>
|
||||
#include <asm/arch/hwregs/reg_rdwr.h>
|
||||
#include <asm/arch/hwregs/timer_defs.h>
|
||||
#include <asm/arch/hwregs/intr_vect_defs.h>
|
||||
|
||||
/* Watchdog defines */
|
||||
#define ETRAX_WD_KEY_MASK 0x7F /* key is 7 bit */
|
||||
#define ETRAX_WD_HZ 763 /* watchdog counts at 763 Hz */
|
||||
#define ETRAX_WD_CNT ((2*ETRAX_WD_HZ)/HZ + 1) /* Number of 763 counts before watchdog bites */
|
||||
|
||||
unsigned long timer_regs[NR_CPUS] =
|
||||
{
|
||||
regi_timer,
|
||||
#ifdef CONFIG_SMP
|
||||
regi_timer2
|
||||
#endif
|
||||
};
|
||||
|
||||
extern void update_xtime_from_cmos(void);
|
||||
extern int set_rtc_mmss(unsigned long nowtime);
|
||||
extern int setup_irq(int, struct irqaction *);
|
||||
extern int have_rtc;
|
||||
|
||||
unsigned long get_ns_in_jiffie(void)
|
||||
{
|
||||
reg_timer_r_tmr0_data data;
|
||||
unsigned long ns;
|
||||
|
||||
data = REG_RD(timer, regi_timer, r_tmr0_data);
|
||||
ns = (TIMER0_DIV - data) * 10;
|
||||
return ns;
|
||||
}
|
||||
|
||||
unsigned long do_slow_gettimeoffset(void)
|
||||
{
|
||||
unsigned long count;
|
||||
unsigned long usec_count = 0;
|
||||
|
||||
static unsigned long count_p = TIMER0_DIV;/* 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.
|
||||
*/
|
||||
|
||||
count = REG_RD(timer, regi_timer, r_tmr0_data);
|
||||
jiffies_t = jiffies;
|
||||
|
||||
/*
|
||||
* avoiding timer inconsistencies (they are rare, but they happen)...
|
||||
* there are one problem that must be avoided here:
|
||||
* 1. the timer counter underflows
|
||||
*/
|
||||
if( jiffies_t == jiffies_p ) {
|
||||
if( count > count_p ) {
|
||||
/* Timer wrapped, use new count and prescale
|
||||
* increase the time corresponding to one jiffie
|
||||
*/
|
||||
usec_count = 1000000/HZ;
|
||||
}
|
||||
} else
|
||||
jiffies_p = jiffies_t;
|
||||
count_p = count;
|
||||
/* Convert timer value to usec */
|
||||
/* 100 MHz timer, divide by 100 to get usec */
|
||||
usec_count += (TIMER0_DIV - count) / 100;
|
||||
return usec_count;
|
||||
}
|
||||
|
||||
/* From timer MDS describing the hardware watchdog:
|
||||
* 4.3.1 Watchdog Operation
|
||||
* The watchdog timer is an 8-bit timer with a configurable start value.
|
||||
* Once started the whatchdog counts downwards with a frequency of 763 Hz
|
||||
* (100/131072 MHz). When the watchdog counts down to 1, it generates an
|
||||
* NMI (Non Maskable Interrupt), and when it counts down to 0, it resets the
|
||||
* chip.
|
||||
*/
|
||||
/* This gives us 1.3 ms to do something useful when the NMI comes */
|
||||
|
||||
/* right now, starting the watchdog is the same as resetting it */
|
||||
#define start_watchdog reset_watchdog
|
||||
|
||||
#if defined(CONFIG_ETRAX_WATCHDOG)
|
||||
static short int watchdog_key = 42; /* arbitrary 7 bit number */
|
||||
#endif
|
||||
|
||||
/* 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
|
||||
|
||||
void
|
||||
reset_watchdog(void)
|
||||
{
|
||||
#if defined(CONFIG_ETRAX_WATCHDOG)
|
||||
reg_timer_rw_wd_ctrl wd_ctrl = { 0 };
|
||||
|
||||
/* 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 ^= ETRAX_WD_KEY_MASK; /* invert key, which is 7 bits */
|
||||
wd_ctrl.cnt = ETRAX_WD_CNT;
|
||||
wd_ctrl.cmd = regk_timer_start;
|
||||
wd_ctrl.key = watchdog_key;
|
||||
REG_WR(timer, regi_timer, rw_wd_ctrl, wd_ctrl);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* stop the watchdog - we still need the correct key */
|
||||
|
||||
void
|
||||
stop_watchdog(void)
|
||||
{
|
||||
#if defined(CONFIG_ETRAX_WATCHDOG)
|
||||
reg_timer_rw_wd_ctrl wd_ctrl = { 0 };
|
||||
watchdog_key ^= ETRAX_WD_KEY_MASK; /* invert key, which is 7 bits */
|
||||
wd_ctrl.cnt = ETRAX_WD_CNT;
|
||||
wd_ctrl.cmd = regk_timer_stop;
|
||||
wd_ctrl.key = watchdog_key;
|
||||
REG_WR(timer, regi_timer, rw_wd_ctrl, wd_ctrl);
|
||||
#endif
|
||||
}
|
||||
|
||||
extern void show_registers(struct pt_regs *regs);
|
||||
|
||||
void
|
||||
handle_watchdog_bite(struct pt_regs* regs)
|
||||
{
|
||||
#if defined(CONFIG_ETRAX_WATCHDOG)
|
||||
extern int cause_of_death;
|
||||
|
||||
raw_printk("Watchdog bite\n");
|
||||
|
||||
/* Check if forced restart or unexpected watchdog */
|
||||
if (cause_of_death == 0xbedead) {
|
||||
while(1);
|
||||
}
|
||||
|
||||
/* Unexpected watchdog, stop the watchdog and dump registers*/
|
||||
stop_watchdog();
|
||||
raw_printk("Oops: bitten by watchdog\n");
|
||||
show_registers(regs);
|
||||
#ifndef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
|
||||
reset_watchdog();
|
||||
#endif
|
||||
while(1) /* nothing */;
|
||||
#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 */
|
||||
|
||||
extern void cris_do_profile(struct pt_regs *regs);
|
||||
|
||||
static inline irqreturn_t
|
||||
timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
|
||||
{
|
||||
int cpu = smp_processor_id();
|
||||
reg_timer_r_masked_intr masked_intr;
|
||||
reg_timer_rw_ack_intr ack_intr = { 0 };
|
||||
|
||||
/* Check if the timer interrupt is for us (a tmr0 int) */
|
||||
masked_intr = REG_RD(timer, timer_regs[cpu], r_masked_intr);
|
||||
if (!masked_intr.tmr0)
|
||||
return IRQ_NONE;
|
||||
|
||||
/* acknowledge the timer irq */
|
||||
ack_intr.tmr0 = 1;
|
||||
REG_WR(timer, timer_regs[cpu], rw_ack_intr, ack_intr);
|
||||
|
||||
/* reset watchdog otherwise it resets us! */
|
||||
reset_watchdog();
|
||||
|
||||
/* Update statistics. */
|
||||
update_process_times(user_mode(regs));
|
||||
|
||||
cris_do_profile(regs); /* Save profiling information */
|
||||
|
||||
/* The master CPU is responsible for the time keeping. */
|
||||
if (cpu != 0)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
/* 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.
|
||||
*
|
||||
* The division here is not time critical since it will run once in
|
||||
* 11 minutes
|
||||
*/
|
||||
if ((time_status & STA_UNSYNC) == 0 &&
|
||||
xtime.tv_sec > last_rtc_update + 660 &&
|
||||
(xtime.tv_nsec / 1000) >= 500000 - (tick_nsec / 1000) / 2 &&
|
||||
(xtime.tv_nsec / 1000) <= 500000 + (tick_nsec / 1000) / 2) {
|
||||
if (set_rtc_mmss(xtime.tv_sec) == 0)
|
||||
last_rtc_update = xtime.tv_sec;
|
||||
else
|
||||
last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* 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 irq_timer = { timer_interrupt, SA_SHIRQ | SA_INTERRUPT,
|
||||
CPU_MASK_NONE, "timer", NULL, NULL};
|
||||
|
||||
void __init
|
||||
cris_timer_init(void)
|
||||
{
|
||||
int cpu = smp_processor_id();
|
||||
reg_timer_rw_tmr0_ctrl tmr0_ctrl = { 0 };
|
||||
reg_timer_rw_tmr0_div tmr0_div = TIMER0_DIV;
|
||||
reg_timer_rw_intr_mask timer_intr_mask;
|
||||
|
||||
/* Setup the etrax timers
|
||||
* Base frequency is 100MHz, divider 1000000 -> 100 HZ
|
||||
* We use timer0, so timer1 is free.
|
||||
* The trig timer is used by the fasttimer API if enabled.
|
||||
*/
|
||||
|
||||
tmr0_ctrl.op = regk_timer_ld;
|
||||
tmr0_ctrl.freq = regk_timer_f100;
|
||||
REG_WR(timer, timer_regs[cpu], rw_tmr0_div, tmr0_div);
|
||||
REG_WR(timer, timer_regs[cpu], rw_tmr0_ctrl, tmr0_ctrl); /* Load */
|
||||
tmr0_ctrl.op = regk_timer_run;
|
||||
REG_WR(timer, timer_regs[cpu], rw_tmr0_ctrl, tmr0_ctrl); /* Start */
|
||||
|
||||
/* enable the timer irq */
|
||||
timer_intr_mask = REG_RD(timer, timer_regs[cpu], rw_intr_mask);
|
||||
timer_intr_mask.tmr0 = 1;
|
||||
REG_WR(timer, timer_regs[cpu], rw_intr_mask, timer_intr_mask);
|
||||
}
|
||||
|
||||
void __init
|
||||
time_init(void)
|
||||
{
|
||||
reg_intr_vect_rw_mask intr_mask;
|
||||
|
||||
/* probe for the RTC and read it if it exists
|
||||
* Before the RTC can be probed the loops_per_usec variable needs
|
||||
* to be initialized to make usleep work. A better value for
|
||||
* loops_per_usec is calculated by the kernel later once the
|
||||
* clock has started.
|
||||
*/
|
||||
loops_per_usec = 50;
|
||||
|
||||
if(RTC_INIT() < 0) {
|
||||
/* no RTC, start at 1980 */
|
||||
xtime.tv_sec = 0;
|
||||
xtime.tv_nsec = 0;
|
||||
have_rtc = 0;
|
||||
} else {
|
||||
/* get the current time */
|
||||
have_rtc = 1;
|
||||
update_xtime_from_cmos();
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize wall_to_monotonic such that adding it to xtime will yield zero, the
|
||||
* tv_nsec field must be normalized (i.e., 0 <= nsec < NSEC_PER_SEC).
|
||||
*/
|
||||
set_normalized_timespec(&wall_to_monotonic, -xtime.tv_sec, -xtime.tv_nsec);
|
||||
|
||||
/* Start CPU local timer */
|
||||
cris_timer_init();
|
||||
|
||||
/* enable the timer irq in global config */
|
||||
intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
|
||||
intr_mask.timer = 1;
|
||||
REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
|
||||
|
||||
/* now actually register the timer irq handler that calls timer_interrupt() */
|
||||
|
||||
setup_irq(TIMER_INTR_VECT, &irq_timer);
|
||||
|
||||
/* enable watchdog if we should use one */
|
||||
|
||||
#if defined(CONFIG_ETRAX_WATCHDOG)
|
||||
printk("Enabling watchdog...\n");
|
||||
start_watchdog();
|
||||
|
||||
/* If we use the hardware watchdog, we want to trap it as an NMI
|
||||
and dump registers before it resets us. For this to happen, we
|
||||
must set the "m" NMI enable flag (which once set, is unset only
|
||||
when an NMI is taken).
|
||||
|
||||
The same goes for the external NMI, but that doesn't have any
|
||||
driver or infrastructure support yet. */
|
||||
{
|
||||
unsigned long flags;
|
||||
local_save_flags(flags);
|
||||
flags |= (1<<30); /* NMI M flag is at bit 30 */
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* Copyright (C) 2003, Axis Communications AB.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include <asm/arch/hwregs/supp_reg.h>
|
||||
|
||||
extern void reset_watchdog(void);
|
||||
extern void stop_watchdog(void);
|
||||
|
||||
extern int raw_printk(const char *fmt, ...);
|
||||
|
||||
void
|
||||
show_registers(struct pt_regs *regs)
|
||||
{
|
||||
/*
|
||||
* It's possible to use either the USP register or current->thread.usp.
|
||||
* USP might not correspond to the current proccess for all cases this
|
||||
* function is called, and current->thread.usp isn't up to date for the
|
||||
* current proccess. Experience shows that using USP is the way to go.
|
||||
*/
|
||||
unsigned long usp;
|
||||
unsigned long d_mmu_cause;
|
||||
unsigned long i_mmu_cause;
|
||||
|
||||
usp = rdusp();
|
||||
|
||||
raw_printk("CPU: %d\n", smp_processor_id());
|
||||
|
||||
raw_printk("ERP: %08lx SRP: %08lx CCS: %08lx USP: %08lx MOF: %08lx\n",
|
||||
regs->erp, regs->srp, regs->ccs, usp, regs->mof);
|
||||
|
||||
raw_printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n",
|
||||
regs->r0, regs->r1, regs->r2, regs->r3);
|
||||
|
||||
raw_printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n",
|
||||
regs->r4, regs->r5, regs->r6, regs->r7);
|
||||
|
||||
raw_printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n",
|
||||
regs->r8, regs->r9, regs->r10, regs->r11);
|
||||
|
||||
raw_printk("r12: %08lx r13: %08lx oR10: %08lx acr: %08lx\n",
|
||||
regs->r12, regs->r13, regs->orig_r10, regs->acr);
|
||||
|
||||
raw_printk("sp: %08lx\n", regs);
|
||||
|
||||
SUPP_BANK_SEL(BANK_IM);
|
||||
SUPP_REG_RD(RW_MM_CAUSE, i_mmu_cause);
|
||||
|
||||
SUPP_BANK_SEL(BANK_DM);
|
||||
SUPP_REG_RD(RW_MM_CAUSE, d_mmu_cause);
|
||||
|
||||
raw_printk(" Data MMU Cause: %08lx\n", d_mmu_cause);
|
||||
raw_printk("Instruction MMU Cause: %08lx\n", i_mmu_cause);
|
||||
|
||||
raw_printk("Process %s (pid: %d, stackpage: %08lx)\n",
|
||||
current->comm, current->pid, (unsigned long) current);
|
||||
|
||||
/* Show additional info if in kernel-mode. */
|
||||
if (!user_mode(regs)) {
|
||||
int i;
|
||||
unsigned char c;
|
||||
|
||||
show_stack(NULL, (unsigned long *) usp);
|
||||
|
||||
/*
|
||||
* If the previous stack-dump wasn't a kernel one, dump the
|
||||
* kernel stack now.
|
||||
*/
|
||||
if (usp != 0)
|
||||
show_stack(NULL, NULL);
|
||||
|
||||
raw_printk("\nCode: ");
|
||||
|
||||
if (regs->erp < PAGE_OFFSET)
|
||||
goto bad_value;
|
||||
|
||||
/*
|
||||
* Quite often the value at regs->erp doesn't point to the
|
||||
* interesting instruction, which often is the previous
|
||||
* instruction. So dump at an offset large enough that the
|
||||
* instruction decoding should be in sync at the interesting
|
||||
* point, but small enough to fit on a row. The regs->erp
|
||||
* location is pointed out in a ksymoops-friendly way by
|
||||
* wrapping the byte for that address in parenthesis.
|
||||
*/
|
||||
for (i = -12; i < 12; i++) {
|
||||
if (__get_user(c, &((unsigned char *) regs->erp)[i])) {
|
||||
bad_value:
|
||||
raw_printk(" Bad IP value.");
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == 0)
|
||||
raw_printk("(%02x) ", c);
|
||||
else
|
||||
raw_printk("%02x ", c);
|
||||
}
|
||||
|
||||
raw_printk("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This gets called from entry.S when the watchdog has bitten. Show something
|
||||
* similiar to an Oops dump, and if the kernel if configured to be a nice doggy;
|
||||
* halt instead of reboot.
|
||||
*/
|
||||
void
|
||||
watchdog_bite_hook(struct pt_regs *regs)
|
||||
{
|
||||
#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
|
||||
local_irq_disable();
|
||||
stop_watchdog();
|
||||
show_registers(regs);
|
||||
|
||||
while (1)
|
||||
; /* Do nothing. */
|
||||
#else
|
||||
show_registers(regs);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* This is normally the Oops function. */
|
||||
void
|
||||
die_if_kernel(const char *str, struct pt_regs *regs, long err)
|
||||
{
|
||||
if (user_mode(regs))
|
||||
return;
|
||||
|
||||
#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
|
||||
/*
|
||||
* This printout might take too long and could trigger
|
||||
* the watchdog normally. If NICE_DOGGY is set, simply
|
||||
* stop the watchdog during the printout.
|
||||
*/
|
||||
stop_watchdog();
|
||||
#endif
|
||||
|
||||
raw_printk("%s: %04lx\n", str, err & 0xffff);
|
||||
|
||||
show_registers(regs);
|
||||
|
||||
#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
|
||||
reset_watchdog();
|
||||
#endif
|
||||
|
||||
do_exit(SIGSEGV);
|
||||
}
|
||||
|
||||
void arch_enable_nmi(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
local_save_flags(flags);
|
||||
flags |= (1<<30); /* NMI M flag is at bit 30 */
|
||||
local_irq_restore(flags);
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
// $Id: vcs_hook.c,v 1.2 2003/08/12 12:01:06 starvik Exp $
|
||||
//
|
||||
// Call simulator hook. This is the part running in the
|
||||
// simulated program.
|
||||
//
|
||||
|
||||
#include "vcs_hook.h"
|
||||
#include <stdarg.h>
|
||||
#include <asm/arch-v32/hwregs/reg_map.h>
|
||||
#include <asm/arch-v32/hwregs/intr_vect_defs.h>
|
||||
|
||||
#define HOOK_TRIG_ADDR 0xb7000000 /* hook cvlog model reg address */
|
||||
#define HOOK_MEM_BASE_ADDR 0xa0000000 /* csp4 (shared mem) base addr */
|
||||
|
||||
#define HOOK_DATA(offset) ((unsigned*) HOOK_MEM_BASE_ADDR)[offset]
|
||||
#define VHOOK_DATA(offset) ((volatile unsigned*) HOOK_MEM_BASE_ADDR)[offset]
|
||||
#define HOOK_TRIG(funcid) do { *((unsigned *) HOOK_TRIG_ADDR) = funcid; } while(0)
|
||||
#define HOOK_DATA_BYTE(offset) ((unsigned char*) HOOK_MEM_BASE_ADDR)[offset]
|
||||
|
||||
|
||||
// ------------------------------------------------------------------ hook_call
|
||||
int hook_call( unsigned id, unsigned pcnt, ...) {
|
||||
va_list ap;
|
||||
unsigned i;
|
||||
unsigned ret;
|
||||
#ifdef USING_SOS
|
||||
PREEMPT_OFF_SAVE();
|
||||
#endif
|
||||
|
||||
// pass parameters
|
||||
HOOK_DATA(0) = id;
|
||||
|
||||
/* Have to make hook_print_str a special case since we call with a
|
||||
parameter of byte type. Should perhaps be a separate
|
||||
hook_call. */
|
||||
|
||||
if (id == hook_print_str) {
|
||||
int i;
|
||||
char *str;
|
||||
|
||||
HOOK_DATA(1) = pcnt;
|
||||
|
||||
va_start(ap, pcnt);
|
||||
str = (char*)va_arg(ap,unsigned);
|
||||
|
||||
for (i=0; i!=pcnt; i++) {
|
||||
HOOK_DATA_BYTE(8+i) = str[i];
|
||||
}
|
||||
HOOK_DATA_BYTE(8+i) = 0; /* null byte */
|
||||
}
|
||||
else {
|
||||
va_start(ap, pcnt);
|
||||
for( i = 1; i <= pcnt; i++ ) HOOK_DATA(i) = va_arg(ap,unsigned);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
// read from mem to make sure data has propagated to memory before trigging
|
||||
*((volatile unsigned*) HOOK_MEM_BASE_ADDR);
|
||||
|
||||
// trigger hook
|
||||
HOOK_TRIG(id);
|
||||
|
||||
// wait for call to finish
|
||||
while( VHOOK_DATA(0) > 0 ) {}
|
||||
|
||||
// extract return value
|
||||
|
||||
ret = VHOOK_DATA(1);
|
||||
|
||||
#ifdef USING_SOS
|
||||
PREEMPT_RESTORE();
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned
|
||||
hook_buf(unsigned i)
|
||||
{
|
||||
return (HOOK_DATA(i));
|
||||
}
|
||||
|
||||
void print_str( const char *str ) {
|
||||
int i;
|
||||
for (i=1; str[i]; i++); /* find null at end of string */
|
||||
hook_call(hook_print_str, i, str);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------- CPU_KICK_DOG
|
||||
void CPU_KICK_DOG(void) {
|
||||
(void) hook_call( hook_kick_dog, 0 );
|
||||
}
|
||||
|
||||
// ------------------------------------------------------- CPU_WATCHDOG_TIMEOUT
|
||||
void CPU_WATCHDOG_TIMEOUT( unsigned t ) {
|
||||
(void) hook_call( hook_dog_timeout, 1, t );
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
// $Id: vcs_hook.h,v 1.1 2003/08/12 12:01:06 starvik Exp $
|
||||
//
|
||||
// Call simulator hook functions
|
||||
|
||||
#ifndef HOOK_H
|
||||
#define HOOK_H
|
||||
|
||||
int hook_call( unsigned id, unsigned pcnt, ...);
|
||||
|
||||
enum hook_ids {
|
||||
hook_debug_on = 1,
|
||||
hook_debug_off,
|
||||
hook_stop_sim_ok,
|
||||
hook_stop_sim_fail,
|
||||
hook_alloc_shared,
|
||||
hook_ptr_shared,
|
||||
hook_free_shared,
|
||||
hook_file2shared,
|
||||
hook_cmp_shared,
|
||||
hook_print_params,
|
||||
hook_sim_time,
|
||||
hook_stop_sim,
|
||||
hook_kick_dog,
|
||||
hook_dog_timeout,
|
||||
hook_rand,
|
||||
hook_srand,
|
||||
hook_rand_range,
|
||||
hook_print_str,
|
||||
hook_print_hex,
|
||||
hook_cmp_offset_shared,
|
||||
hook_fill_random_shared,
|
||||
hook_alloc_random_data,
|
||||
hook_calloc_random_data,
|
||||
hook_print_int,
|
||||
hook_print_uint,
|
||||
hook_fputc,
|
||||
hook_init_fd,
|
||||
hook_sbrk
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,6 @@
|
|||
#
|
||||
# Makefile for Etrax-specific library files..
|
||||
#
|
||||
|
||||
lib-y = checksum.o checksumcopy.o string.o usercopy.o memset.o csumcpfruser.o spinlock.o
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* A fast checksum routine using movem
|
||||
* Copyright (c) 1998-2001, 2003 Axis Communications AB
|
||||
*
|
||||
* csum_partial(const unsigned char * buff, int len, unsigned int sum)
|
||||
*/
|
||||
|
||||
.globl csum_partial
|
||||
csum_partial:
|
||||
|
||||
;; r10 - src
|
||||
;; r11 - length
|
||||
;; r12 - checksum
|
||||
|
||||
;; check for breakeven length between movem and normal word looping versions
|
||||
;; we also do _NOT_ want to compute a checksum over more than the
|
||||
;; actual length when length < 40
|
||||
|
||||
cmpu.w 80,$r11
|
||||
blo _word_loop
|
||||
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
|
||||
;; only r0 - r8 have to be saved, the other ones are clobber-able
|
||||
;; according to the ABI
|
||||
|
||||
subq 9*4,$sp
|
||||
subq 10*4,$r11 ; update length for the first loop
|
||||
movem $r8,[$sp]
|
||||
|
||||
;; do a movem checksum
|
||||
|
||||
_mloop: movem [$r10+],$r9 ; read 10 longwords
|
||||
|
||||
;; perform dword checksumming on the 10 longwords
|
||||
|
||||
add.d $r0,$r12
|
||||
addc $r1,$r12
|
||||
addc $r2,$r12
|
||||
addc $r3,$r12
|
||||
addc $r4,$r12
|
||||
addc $r5,$r12
|
||||
addc $r6,$r12
|
||||
addc $r7,$r12
|
||||
addc $r8,$r12
|
||||
addc $r9,$r12
|
||||
|
||||
;; fold the carry into the checksum, to avoid having to loop the carry
|
||||
;; back into the top
|
||||
|
||||
addc 0,$r12
|
||||
addc 0,$r12 ; do it again, since we might have generated a carry
|
||||
|
||||
subq 10*4,$r11
|
||||
bge _mloop
|
||||
nop
|
||||
|
||||
addq 10*4,$r11 ; compensate for last loop underflowing length
|
||||
|
||||
movem [$sp+],$r8 ; restore regs
|
||||
|
||||
_word_loop:
|
||||
;; only fold if there is anything to fold.
|
||||
|
||||
cmpq 0,$r12
|
||||
beq _no_fold
|
||||
|
||||
;; fold 32-bit checksum into a 16-bit checksum, to avoid carries below.
|
||||
;; r9 and r13 can be used as temporaries.
|
||||
|
||||
moveq -1,$r9 ; put 0xffff in r9, faster than move.d 0xffff,r9
|
||||
lsrq 16,$r9
|
||||
|
||||
move.d $r12,$r13
|
||||
lsrq 16,$r13 ; r13 = checksum >> 16
|
||||
and.d $r9,$r12 ; checksum = checksum & 0xffff
|
||||
add.d $r13,$r12 ; checksum += r13
|
||||
move.d $r12,$r13 ; do the same again, maybe we got a carry last add
|
||||
lsrq 16,$r13
|
||||
and.d $r9,$r12
|
||||
add.d $r13,$r12
|
||||
|
||||
_no_fold:
|
||||
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
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* A fast checksum+copy routine using movem
|
||||
* Copyright (c) 1998, 2001, 2003 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:
|
||||
|
||||
;; r10 - src
|
||||
;; r11 - dst
|
||||
;; r12 - length
|
||||
;; r13 - checksum
|
||||
|
||||
;; check for breakeven length between movem and normal word looping versions
|
||||
;; we also do _NOT_ want to compute a checksum over more than the
|
||||
;; actual length when length < 40
|
||||
|
||||
cmpu.w 80,$r12
|
||||
blo _word_loop
|
||||
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
|
||||
;; only r0 - r8 have to be saved, the other ones are clobber-able
|
||||
;; according to the ABI
|
||||
|
||||
subq 9*4,$sp
|
||||
subq 10*4,$r12 ; update length for the first loop
|
||||
movem $r8,[$sp]
|
||||
|
||||
;; do a movem copy and checksum
|
||||
|
||||
1: ;; A failing userspace access (the read) will have this as PC.
|
||||
_mloop: movem [$r10+],$r9 ; read 10 longwords
|
||||
movem $r9,[$r11+] ; write 10 longwords
|
||||
|
||||
;; perform dword checksumming on the 10 longwords
|
||||
|
||||
add.d $r0,$r13
|
||||
addc $r1,$r13
|
||||
addc $r2,$r13
|
||||
addc $r3,$r13
|
||||
addc $r4,$r13
|
||||
addc $r5,$r13
|
||||
addc $r6,$r13
|
||||
addc $r7,$r13
|
||||
addc $r8,$r13
|
||||
addc $r9,$r13
|
||||
|
||||
;; fold the carry into the checksum, to avoid having to loop the carry
|
||||
;; back into the top
|
||||
|
||||
addc 0,$r13
|
||||
addc 0,$r13 ; do it again, since we might have generated a carry
|
||||
|
||||
subq 10*4,$r12
|
||||
bge _mloop
|
||||
nop
|
||||
|
||||
addq 10*4,$r12 ; compensate for last loop underflowing length
|
||||
|
||||
movem [$sp+],$r8 ; restore regs
|
||||
|
||||
_word_loop:
|
||||
;; only fold if there is anything to fold.
|
||||
|
||||
cmpq 0,$r13
|
||||
beq _no_fold
|
||||
|
||||
;; fold 32-bit checksum into a 16-bit checksum, to avoid carries below
|
||||
;; r9 can be used as temporary.
|
||||
|
||||
move.d $r13,$r9
|
||||
lsrq 16,$r9 ; r0 = checksum >> 16
|
||||
and.d 0xffff,$r13 ; checksum = checksum & 0xffff
|
||||
add.d $r9,$r13 ; checksum += r0
|
||||
move.d $r13,$r9 ; do the same again, maybe we got a carry last add
|
||||
lsrq 16,$r9
|
||||
and.d 0xffff,$r13
|
||||
add.d $r9,$r13
|
||||
|
||||
_no_fold:
|
||||
cmpq 2,$r12
|
||||
blt _no_words
|
||||
nop
|
||||
|
||||
;; copy and checksum the rest of the words
|
||||
|
||||
subq 2,$r12
|
||||
|
||||
2: ;; A failing userspace access for the read below will have this as PC.
|
||||
_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
|
||||
3: ;; A failing userspace access for the read below will have this as PC.
|
||||
move.b [$r10],$r9
|
||||
addu.b $r9,$r13
|
||||
move.b $r9,[$r11]
|
||||
ret
|
||||
move.d $r13,$r10
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Add-on to transform csum_partial_copy_nocheck in checksumcopy.S into
|
||||
* csum_partial_copy_from_user by adding exception records.
|
||||
*
|
||||
* Copyright (C) 2001, 2003 Axis Communications AB.
|
||||
*
|
||||
* Author: Hans-Peter Nilsson.
|
||||
*/
|
||||
|
||||
#include <asm/errno.h>
|
||||
|
||||
/* Same function body, but a different name. If we just added exception
|
||||
records to _csum_partial_copy_nocheck and made it generic, we wouldn't
|
||||
know a user fault from a kernel fault and we would have overhead in
|
||||
each kernel caller for the error-pointer argument.
|
||||
|
||||
unsigned int csum_partial_copy_from_user
|
||||
(const char *src, char *dst, int len, unsigned int sum, int *errptr);
|
||||
|
||||
Note that the errptr argument is only set if we encounter an error.
|
||||
It is conveniently located on the stack, so the normal function body
|
||||
does not have to handle it. */
|
||||
|
||||
#define csum_partial_copy_nocheck csum_partial_copy_from_user
|
||||
|
||||
/* There are local labels numbered 1, 2 and 3 present to mark the
|
||||
different from-user accesses. */
|
||||
#include "checksumcopy.S"
|
||||
|
||||
.section .fixup,"ax"
|
||||
|
||||
;; Here from the movem loop; restore stack.
|
||||
4:
|
||||
movem [$sp+],$r8
|
||||
;; r12 is already decremented. Add back chunk_size-2.
|
||||
addq 40-2,$r12
|
||||
|
||||
;; Here from the word loop; r12 is off by 2; add it back.
|
||||
5:
|
||||
addq 2,$r12
|
||||
|
||||
;; Here from a failing single byte.
|
||||
6:
|
||||
|
||||
;; Signal in *errptr that we had a failing access.
|
||||
move.d [$sp],$acr
|
||||
moveq -EFAULT,$r9
|
||||
subq 4,$sp
|
||||
move.d $r9,[$acr]
|
||||
|
||||
;; Clear the rest of the destination area using memset. Preserve the
|
||||
;; checksum for the readable bytes.
|
||||
move.d $r13,[$sp]
|
||||
subq 4,$sp
|
||||
move.d $r11,$r10
|
||||
move $srp,[$sp]
|
||||
jsr memset
|
||||
clear.d $r11
|
||||
|
||||
move [$sp+],$srp
|
||||
ret
|
||||
move.d [$sp+],$r10
|
||||
|
||||
.previous
|
||||
.section __ex_table,"a"
|
||||
.dword 1b,4b
|
||||
.dword 2b,5b
|
||||
.dword 3b,6b
|
||||
.previous
|
|
@ -0,0 +1,120 @@
|
|||
/* $Id: dram_init.S,v 1.4 2005/04/24 18:48:32 starvik Exp $
|
||||
*
|
||||
* DRAM/SDRAM initialization - alter with care
|
||||
* This file is intended to be included from other assembler files
|
||||
*
|
||||
* Note: This file may not modify r8 or r9 because they are used to
|
||||
* carry information from the decompresser to the kernel
|
||||
*
|
||||
* Copyright (C) 2000-2003 Axis Communications AB
|
||||
*
|
||||
* Authors: Mikael Starvik (starvik@axis.com)
|
||||
*/
|
||||
|
||||
/* Just to be certain the config file is included, we include it here
|
||||
* explicitely instead of depending on it being included in the file that
|
||||
* uses this code.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <asm/arch/hwregs/asm/reg_map_asm.h>
|
||||
#include <asm/arch/hwregs/asm/bif_core_defs_asm.h>
|
||||
|
||||
;; WARNING! The registers r8 and r9 are used as parameters carrying
|
||||
;; information from the decompressor (if the kernel was compressed).
|
||||
;; They should not be used in the code below.
|
||||
|
||||
; Refer to BIF MDS for a description of SDRAM initialization
|
||||
|
||||
; Bank configuration
|
||||
move.d REG_ADDR(bif_core, regi_bif_core, rw_sdram_cfg_grp0), $r0
|
||||
move.d CONFIG_ETRAX_SDRAM_GRP0_CONFIG, $r1
|
||||
move.d $r1, [$r0]
|
||||
move.d REG_ADDR(bif_core, regi_bif_core, rw_sdram_cfg_grp1), $r0
|
||||
move.d CONFIG_ETRAX_SDRAM_GRP1_CONFIG, $r1
|
||||
move.d $r1, [$r0]
|
||||
|
||||
; Calculate value of mrs_data
|
||||
; CAS latency = 2 && bus_width = 32 => 0x40
|
||||
; CAS latency = 3 && bus_width = 32 => 0x60
|
||||
; CAS latency = 2 && bus_width = 16 => 0x20
|
||||
; CAS latency = 3 && bus_width = 16 => 0x30
|
||||
|
||||
; Check if value is already supplied in kernel config
|
||||
move.d CONFIG_ETRAX_SDRAM_COMMAND, $r2
|
||||
bne _set_timing
|
||||
nop
|
||||
|
||||
move.d 0x40, $r4 ; Assume 32 bits and CAS latency = 2
|
||||
move.d CONFIG_ETRAX_SDRAM_TIMING, $r1
|
||||
and.d 0x07, $r1 ; Get CAS latency
|
||||
cmpq 2, $r1 ; CL = 2 ?
|
||||
beq _bw_check
|
||||
nop
|
||||
move.d 0x60, $r4
|
||||
|
||||
_bw_check:
|
||||
; Assume that group 0 width is equal to group 1. This assumption
|
||||
; is wrong for a group 1 only hardware (such as the grand old
|
||||
; StorPoint+).
|
||||
move.d CONFIG_ETRAX_SDRAM_GRP0_CONFIG, $r1
|
||||
and.d 0x200, $r1 ; DRAM width is bit 9
|
||||
beq _set_timing
|
||||
lslq 2, $r4 ; mrs_data starts at bit 2
|
||||
lsrq 1, $r4 ; 16 bits. Shift down value.
|
||||
|
||||
; Set timing parameters (refresh off to avoid Guinness TR 83)
|
||||
_set_timing:
|
||||
move.d CONFIG_ETRAX_SDRAM_TIMING, $r1
|
||||
and.d ~(3 << reg_bif_core_rw_sdram_timing___ref___lsb), $r1
|
||||
move.d REG_ADDR(bif_core, regi_bif_core, rw_sdram_timing), $r0
|
||||
move.d $r1, [$r0]
|
||||
|
||||
; Issue NOP command
|
||||
move.d REG_ADDR(bif_core, regi_bif_core, rw_sdram_cmd), $r5
|
||||
moveq regk_bif_core_nop, $r1
|
||||
move.d $r1, [$r5]
|
||||
|
||||
; Wait 200us
|
||||
move.d 10000, $r2
|
||||
1: bne 1b
|
||||
subq 1, $r2
|
||||
|
||||
; Issue initialization command sequence
|
||||
move.d _sdram_commands_start, $r2
|
||||
and.d 0x000fffff, $r2 ; Make sure commands are read from flash
|
||||
move.d _sdram_commands_end, $r3
|
||||
and.d 0x000fffff, $r3
|
||||
1: clear.d $r6
|
||||
move.b [$r2+], $r6 ; Load command
|
||||
or.d $r4, $r6 ; Add calculated mrs
|
||||
move.d $r6, [$r5] ; Write rw_sdram_cmd
|
||||
; Wait 80 ns between each command
|
||||
move.d 4000, $r7
|
||||
2: bne 2b
|
||||
subq 1, $r7
|
||||
cmp.d $r2, $r3 ; Last command?
|
||||
bne 1b
|
||||
nop
|
||||
|
||||
; Start refresh
|
||||
move.d CONFIG_ETRAX_SDRAM_TIMING, $r1
|
||||
move.d REG_ADDR(bif_core, regi_bif_core, rw_sdram_timing), $r0
|
||||
move.d $r1, [$r0]
|
||||
|
||||
; Initialization finished
|
||||
ba _sdram_commands_end
|
||||
nop
|
||||
|
||||
_sdram_commands_start:
|
||||
.byte regk_bif_core_pre ; Precharge
|
||||
.byte regk_bif_core_ref ; refresh
|
||||
.byte regk_bif_core_ref ; refresh
|
||||
.byte regk_bif_core_ref ; refresh
|
||||
.byte regk_bif_core_ref ; refresh
|
||||
.byte regk_bif_core_ref ; refresh
|
||||
.byte regk_bif_core_ref ; refresh
|
||||
.byte regk_bif_core_ref ; refresh
|
||||
.byte regk_bif_core_ref ; refresh
|
||||
.byte regk_bif_core_mrs ; mrs
|
||||
_sdram_commands_end:
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* $Id: hw_settings.S,v 1.3 2005/04/24 18:36:57 starvik Exp $
|
||||
*
|
||||
* This table is used by some tools to extract hardware parameters.
|
||||
* The table should be included in the kernel and the decompressor.
|
||||
* Don't forget to update the tools if you change this table.
|
||||
*
|
||||
* Copyright (C) 2001 Axis Communications AB
|
||||
*
|
||||
* Authors: Mikael Starvik (starvik@axis.com)
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <asm/arch/hwregs/asm/reg_map_asm.h>
|
||||
#include <asm/arch/hwregs/asm/bif_core_defs_asm.h>
|
||||
#include <asm/arch/hwregs/asm/gio_defs_asm.h>
|
||||
|
||||
.ascii "HW_PARAM_MAGIC" ; Magic number
|
||||
.dword 0xc0004000 ; Kernel start address
|
||||
|
||||
; Debug port
|
||||
#ifdef CONFIG_ETRAX_DEBUG_PORT0
|
||||
.dword 0
|
||||
#elif defined(CONFIG_ETRAX_DEBUG_PORT1)
|
||||
.dword 1
|
||||
#elif defined(CONFIG_ETRAX_DEBUG_PORT2)
|
||||
.dword 2
|
||||
#elif defined(CONFIG_ETRAX_DEBUG_PORT3)
|
||||
.dword 3
|
||||
#else
|
||||
.dword 4 ; No debug
|
||||
#endif
|
||||
|
||||
; Register values
|
||||
.dword REG_ADDR(bif_core, regi_bif_core, rw_grp1_cfg)
|
||||
.dword CONFIG_ETRAX_MEM_GRP1_CONFIG
|
||||
.dword REG_ADDR(bif_core, regi_bif_core, rw_grp2_cfg)
|
||||
.dword CONFIG_ETRAX_MEM_GRP2_CONFIG
|
||||
.dword REG_ADDR(bif_core, regi_bif_core, rw_grp3_cfg)
|
||||
.dword CONFIG_ETRAX_MEM_GRP3_CONFIG
|
||||
.dword REG_ADDR(bif_core, regi_bif_core, rw_grp4_cfg)
|
||||
.dword CONFIG_ETRAX_MEM_GRP4_CONFIG
|
||||
.dword REG_ADDR(bif_core, regi_bif_core, rw_sdram_cfg_grp0)
|
||||
.dword CONFIG_ETRAX_SDRAM_GRP0_CONFIG
|
||||
.dword REG_ADDR(bif_core, regi_bif_core, rw_sdram_cfg_grp1)
|
||||
.dword CONFIG_ETRAX_SDRAM_GRP1_CONFIG
|
||||
.dword REG_ADDR(bif_core, regi_bif_core, rw_sdram_timing)
|
||||
.dword CONFIG_ETRAX_SDRAM_TIMING
|
||||
.dword REG_ADDR(bif_core, regi_bif_core, rw_sdram_cmd)
|
||||
.dword CONFIG_ETRAX_SDRAM_COMMAND
|
||||
|
||||
.dword REG_ADDR(gio, regi_gio, rw_pa_dout)
|
||||
.dword CONFIG_ETRAX_DEF_GIO_PA_OUT
|
||||
.dword REG_ADDR(gio, regi_gio, rw_pa_oe)
|
||||
.dword CONFIG_ETRAX_DEF_GIO_PA_OE
|
||||
.dword REG_ADDR(gio, regi_gio, rw_pb_dout)
|
||||
.dword CONFIG_ETRAX_DEF_GIO_PB_OUT
|
||||
.dword REG_ADDR(gio, regi_gio, rw_pb_oe)
|
||||
.dword CONFIG_ETRAX_DEF_GIO_PB_OE
|
||||
.dword REG_ADDR(gio, regi_gio, rw_pc_dout)
|
||||
.dword CONFIG_ETRAX_DEF_GIO_PC_OUT
|
||||
.dword REG_ADDR(gio, regi_gio, rw_pc_oe)
|
||||
.dword CONFIG_ETRAX_DEF_GIO_PC_OE
|
||||
.dword REG_ADDR(gio, regi_gio, rw_pd_dout)
|
||||
.dword CONFIG_ETRAX_DEF_GIO_PD_OUT
|
||||
.dword REG_ADDR(gio, regi_gio, rw_pd_oe)
|
||||
.dword CONFIG_ETRAX_DEF_GIO_PD_OE
|
||||
.dword REG_ADDR(gio, regi_gio, rw_pe_dout)
|
||||
.dword CONFIG_ETRAX_DEF_GIO_PE_OUT
|
||||
.dword REG_ADDR(gio, regi_gio, rw_pe_oe)
|
||||
.dword CONFIG_ETRAX_DEF_GIO_PE_OE
|
||||
|
||||
.dword 0 ; No more register values
|
|
@ -0,0 +1,253 @@
|
|||
/*#************************************************************************#*/
|
||||
/*#-------------------------------------------------------------------------*/
|
||||
/*# */
|
||||
/*# 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. */
|
||||
/*# */
|
||||
/*#-------------------------------------------------------------------------*/
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* 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,
|
||||
size_t 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\
|
||||
lslq 8,$r13 \n\
|
||||
move.b %0,$r13 \n\
|
||||
move.d $r13,%0 \n\
|
||||
lslq 16,$r13 \n\
|
||||
or.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 (" \n\
|
||||
;; Check that the register asm declaration got right. \n\
|
||||
;; The GCC manual says it will work, but there *has* been bugs. \n\
|
||||
.ifnc %0-%1-%4,$r13-$r12-$r11 \n\
|
||||
.err \n\
|
||||
.endif \n\
|
||||
\n\
|
||||
;; Save the registers we'll clobber in the movem process \n\
|
||||
;; on the stack. Don't mention them to gcc, it will only be \n\
|
||||
;; upset. \n\
|
||||
subq 11*4,$sp \n\
|
||||
movem $r10,[$sp] \n\
|
||||
\n\
|
||||
move.d $r11,$r0 \n\
|
||||
move.d $r11,$r1 \n\
|
||||
move.d $r11,$r2 \n\
|
||||
move.d $r11,$r3 \n\
|
||||
move.d $r11,$r4 \n\
|
||||
move.d $r11,$r5 \n\
|
||||
move.d $r11,$r6 \n\
|
||||
move.d $r11,$r7 \n\
|
||||
move.d $r11,$r8 \n\
|
||||
move.d $r11,$r9 \n\
|
||||
move.d $r11,$r10 \n\
|
||||
\n\
|
||||
;; Now we've got this: \n\
|
||||
;; r13 - dst \n\
|
||||
;; r12 - n \n\
|
||||
\n\
|
||||
;; Update n for the first loop \n\
|
||||
subq 12*4,$r12 \n\
|
||||
0: \n\
|
||||
subq 12*4,$r12 \n\
|
||||
bge 0b \n\
|
||||
movem $r11,[$r13+] \n\
|
||||
\n\
|
||||
addq 12*4,$r12 ;; compensate for last loop underflowing n \n\
|
||||
\n\
|
||||
;; Restore registers from stack \n\
|
||||
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() */
|
|
@ -0,0 +1,179 @@
|
|||
##=============================================================================
|
||||
##
|
||||
## nand_init.S
|
||||
##
|
||||
## The bootrom copies data from the NAND flash to the internal RAM but
|
||||
## due to a bug/feature we can only trust the 256 first bytes. So this
|
||||
## code copies more data from NAND flash to internal RAM. Obvioulsy this
|
||||
## code must fit in the first 256 bytes so alter with care.
|
||||
##
|
||||
## Some notes about the bug/feature for future reference:
|
||||
## The bootrom copies the first 127 KB from NAND flash to internal
|
||||
## memory. The problem is that it does a bytewise copy. NAND flashes
|
||||
## does autoincrement on the address so for a 16-bite device each
|
||||
## read/write increases the address by two. So the copy loop in the
|
||||
## bootrom will discard every second byte. This is solved by inserting
|
||||
## zeroes in every second byte in the first erase block.
|
||||
##
|
||||
## The bootrom also incorrectly assumes that it can read the flash
|
||||
## linear with only one read command but the flash will actually
|
||||
## switch between normal area and spare area if you do that so we
|
||||
## can't trust more than the first 256 bytes.
|
||||
##
|
||||
##=============================================================================
|
||||
|
||||
#include <asm/arch/hwregs/asm/reg_map_asm.h>
|
||||
#include <asm/arch/hwregs/asm/gio_defs_asm.h>
|
||||
#include <asm/arch/hwregs/asm/pinmux_defs_asm.h>
|
||||
#include <asm/arch/hwregs/asm/bif_core_defs_asm.h>
|
||||
#include <asm/arch/hwregs/asm/config_defs_asm.h>
|
||||
#include <linux/config.h>
|
||||
|
||||
;; There are 8-bit NAND flashes and 16-bit NAND flashes.
|
||||
;; We need to treat them slightly different.
|
||||
#if CONFIG_ETRAX_FLASH_BUSWIDTH==2
|
||||
#define PAGE_SIZE 256
|
||||
#else
|
||||
#error 2
|
||||
#define PAGE_SIZE 512
|
||||
#endif
|
||||
#define ERASE_BLOCK 16384
|
||||
|
||||
;; GPIO pins connected to NAND flash
|
||||
#define CE 4
|
||||
#define CLE 5
|
||||
#define ALE 6
|
||||
#define BY 7
|
||||
|
||||
;; Address space for NAND flash
|
||||
#define NAND_RD_ADDR 0x90000000
|
||||
#define NAND_WR_ADDR 0x94000000
|
||||
|
||||
#define READ_CMD 0x00
|
||||
|
||||
;; Readability macros
|
||||
#define CSP_MASK \
|
||||
REG_MASK(bif_core, rw_grp3_cfg, gated_csp0) | \
|
||||
REG_MASK(bif_core, rw_grp3_cfg, gated_csp1)
|
||||
#define CSP_VAL \
|
||||
REG_STATE(bif_core, rw_grp3_cfg, gated_csp0, rd) | \
|
||||
REG_STATE(bif_core, rw_grp3_cfg, gated_csp1, wr)
|
||||
|
||||
;;----------------------------------------------------------------------------
|
||||
;; Macros to set/clear GPIO bits
|
||||
|
||||
.macro SET x
|
||||
or.b (1<<\x),$r9
|
||||
move.d $r9, [$r2]
|
||||
.endm
|
||||
|
||||
.macro CLR x
|
||||
and.b ~(1<<\x),$r9
|
||||
move.d $r9, [$r2]
|
||||
.endm
|
||||
|
||||
;;----------------------------------------------------------------------------
|
||||
|
||||
nand_boot:
|
||||
;; Check if nand boot was selected
|
||||
move.d REG_ADDR(config, regi_config, r_bootsel), $r0
|
||||
move.d [$r0], $r0
|
||||
and.d REG_MASK(config, r_bootsel, boot_mode), $r0
|
||||
cmp.d REG_STATE(config, r_bootsel, boot_mode, nand), $r0
|
||||
bne normal_boot ; No NAND boot
|
||||
nop
|
||||
|
||||
copy_nand_to_ram:
|
||||
;; copy_nand_to_ram
|
||||
;; Arguments
|
||||
;; r10 - destination
|
||||
;; r11 - source offset
|
||||
;; r12 - size
|
||||
;; r13 - Address to jump to after completion
|
||||
;; Note : r10-r12 are clobbered on return
|
||||
;; Registers used:
|
||||
;; r0 - NAND_RD_ADDR
|
||||
;; r1 - NAND_WR_ADDR
|
||||
;; r2 - reg_gio_rw_pa_dout
|
||||
;; r3 - reg_gio_r_pa_din
|
||||
;; r4 - tmp
|
||||
;; r5 - byte counter within a page
|
||||
;; r6 - reg_pinmux_rw_pa
|
||||
;; r7 - reg_gio_rw_pa_oe
|
||||
;; r8 - reg_bif_core_rw_grp3_cfg
|
||||
;; r9 - reg_gio_rw_pa_dout shadow
|
||||
move.d 0x90000000, $r0
|
||||
move.d 0x94000000, $r1
|
||||
move.d REG_ADDR(gio, regi_gio, rw_pa_dout), $r2
|
||||
move.d REG_ADDR(gio, regi_gio, r_pa_din), $r3
|
||||
move.d REG_ADDR(pinmux, regi_pinmux, rw_pa), $r6
|
||||
move.d REG_ADDR(gio, regi_gio, rw_pa_oe), $r7
|
||||
move.d REG_ADDR(bif_core, regi_bif_core, rw_grp3_cfg), $r8
|
||||
|
||||
#if CONFIG_ETRAX_FLASH_BUSWIDTH==2
|
||||
lsrq 1, $r11
|
||||
#endif
|
||||
;; Set up GPIO
|
||||
move.d [$r2], $r9
|
||||
move.d [$r7], $r4
|
||||
or.b (1<<ALE) | (1 << CLE) | (1<<CE), $r4
|
||||
move.d $r4, [$r7]
|
||||
|
||||
;; Set up bif
|
||||
move.d [$r8], $r4
|
||||
and.d CSP_MASK, $r4
|
||||
or.d CSP_VAL, $r4
|
||||
move.d $r4, [$r8]
|
||||
|
||||
1: ;; Copy one page
|
||||
CLR CE
|
||||
SET CLE
|
||||
moveq READ_CMD, $r4
|
||||
move.b $r4, [$r1]
|
||||
moveq 20, $r4
|
||||
2: bne 2b
|
||||
subq 1, $r4
|
||||
CLR CLE
|
||||
SET ALE
|
||||
clear.w [$r1] ; Column address = 0
|
||||
move.d $r11, $r4
|
||||
lsrq 8, $r4
|
||||
move.b $r4, [$r1] ; Row address
|
||||
lsrq 8, $r4
|
||||
move.b $r4, [$r1] ; Row adddress
|
||||
moveq 20, $r4
|
||||
2: bne 2b
|
||||
subq 1, $r4
|
||||
CLR ALE
|
||||
2: move.d [$r3], $r4
|
||||
and.d 1 << BY, $r4
|
||||
beq 2b
|
||||
movu.w PAGE_SIZE, $r5
|
||||
2: ; Copy one byte/word
|
||||
#if CONFIG_ETRAX_FLASH_BUSWIDTH==2
|
||||
move.w [$r0], $r4
|
||||
#else
|
||||
move.b [$r0], $r4
|
||||
#endif
|
||||
subq 1, $r5
|
||||
bne 2b
|
||||
#if CONFIG_ETRAX_FLASH_BUSWIDTH==2
|
||||
move.w $r4, [$r10+]
|
||||
subu.w PAGE_SIZE*2, $r12
|
||||
#else
|
||||
move.b $r4, [$r10+]
|
||||
subu.w PAGE_SIZE, $r12
|
||||
#endif
|
||||
bpl 1b
|
||||
addu.w PAGE_SIZE, $r11
|
||||
|
||||
;; End of copy
|
||||
jump $r13
|
||||
nop
|
||||
|
||||
;; This will warn if the code above is too large. If you consider
|
||||
;; to remove this you don't understand the bug/feature.
|
||||
.org 256
|
||||
.org ERASE_BLOCK
|
||||
|
||||
normal_boot:
|
|
@ -0,0 +1,33 @@
|
|||
;; Core of the spinlock implementation
|
||||
;;
|
||||
;; Copyright (C) 2004 Axis Communications AB.
|
||||
;;
|
||||
;; Author: Mikael Starvik
|
||||
|
||||
|
||||
.global cris_spin_lock
|
||||
.global cris_spin_trylock
|
||||
|
||||
.text
|
||||
|
||||
cris_spin_lock:
|
||||
clearf p
|
||||
1: test.d [$r10]
|
||||
beq 1b
|
||||
clearf p
|
||||
ax
|
||||
clear.d [$r10]
|
||||
bcs 1b
|
||||
clearf p
|
||||
ret
|
||||
nop
|
||||
|
||||
cris_spin_trylock:
|
||||
clearf p
|
||||
1: move.d [$r10], $r11
|
||||
ax
|
||||
clear.d [$r10]
|
||||
bcs 1b
|
||||
clearf p
|
||||
ret
|
||||
move.d $r11,$r10
|
|
@ -0,0 +1,219 @@
|
|||
/*#************************************************************************#*/
|
||||
/*#-------------------------------------------------------------------------*/
|
||||
/*# */
|
||||
/*# 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. */
|
||||
/*# */
|
||||
/*#-------------------------------------------------------------------------*/
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
void *memcpy(void *pdst,
|
||||
const void *psrc,
|
||||
size_t 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. Movem is dirt cheap, so the
|
||||
overheap is low enough to always use the minimum block size as the
|
||||
threshold. */
|
||||
if (n >= 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. */
|
||||
__asm__ volatile (" \n\
|
||||
;; Check that the register asm declaration got right. \n\
|
||||
;; The GCC manual explicitly says TRT will happen. \n\
|
||||
.ifnc %0-%1-%2,$r13-$r11-$r12 \n\
|
||||
.err \n\
|
||||
.endif \n\
|
||||
\n\
|
||||
;; Save the registers we'll use in the movem process \n\
|
||||
\n\
|
||||
;; on the stack. \n\
|
||||
subq 11*4,$sp \n\
|
||||
movem $r10,[$sp] \n\
|
||||
\n\
|
||||
;; Now we've got this: \n\
|
||||
;; r11 - src \n\
|
||||
;; r13 - dst \n\
|
||||
;; r12 - n \n\
|
||||
\n\
|
||||
;; Update n for the first loop \n\
|
||||
subq 44,$r12 \n\
|
||||
0: \n\
|
||||
movem [$r11+],$r10 \n\
|
||||
subq 44,$r12 \n\
|
||||
bge 0b \n\
|
||||
movem $r10,[$r13+] \n\
|
||||
\n\
|
||||
addq 44,$r12 ;; compensate for last loop underflowing n \n\
|
||||
\n\
|
||||
;; Restore registers from stack \n\
|
||||
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() */
|
|
@ -0,0 +1,470 @@
|
|||
/*
|
||||
* User address space access functions.
|
||||
* The non-inlined parts of asm-cris/uaccess.h are here.
|
||||
*
|
||||
* Copyright (C) 2000, 2003 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 3.2.1 Axis release R53/1.53-v32".
|
||||
|
||||
Check regularly...
|
||||
|
||||
Note that for CRISv32, the PC saved at a bus-fault is the address
|
||||
*at* the faulting instruction, with a special case for instructions
|
||||
in delay slots: then it's the address of the branch. Note also that
|
||||
in contrast to v10, a postincrement in the instruction is *not*
|
||||
performed at a bus-fault; the register is seen having the original
|
||||
value in fault handlers. */
|
||||
|
||||
|
||||
/* Copy to userspace. This is based on the memcpy used for
|
||||
kernel-to-kernel copying; see "string.c". */
|
||||
|
||||
unsigned long
|
||||
__copy_user (void __user *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;
|
||||
}
|
||||
}
|
||||
|
||||
/* Movem is dirt cheap. The overheap is low enough to always use the
|
||||
minimum possible block size as the threshold. */
|
||||
if (n >= 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. */
|
||||
__asm__ volatile ("\
|
||||
;; Check that the register asm declaration got right. \n\
|
||||
;; The GCC manual explicitly says TRT will happen. \n\
|
||||
.ifnc %0%1%2%3,$r13$r11$r12$r10 \n\
|
||||
.err \n\
|
||||
.endif \n\
|
||||
\n\
|
||||
;; Save the registers we'll use in the movem process \n\
|
||||
;; on the stack. \n\
|
||||
subq 11*4,$sp \n\
|
||||
movem $r10,[$sp] \n\
|
||||
\n\
|
||||
;; Now we've got this: \n\
|
||||
;; r11 - src \n\
|
||||
;; r13 - dst \n\
|
||||
;; r12 - n \n\
|
||||
\n\
|
||||
;; Update n for the first loop \n\
|
||||
subq 44,$r12 \n\
|
||||
0: \n\
|
||||
movem [$r11+],$r10 \n\
|
||||
subq 44,$r12 \n\
|
||||
1: bge 0b \n\
|
||||
movem $r10,[$r13+] \n\
|
||||
3: \n\
|
||||
addq 44,$r12 ;; compensate for last loop underflowing n \n\
|
||||
\n\
|
||||
;; Restore registers from stack \n\
|
||||
movem [$sp+],$r10 \n\
|
||||
2: \n\
|
||||
.section .fixup,\"ax\" \n\
|
||||
4: \n\
|
||||
; When failing on any of the 1..44 bytes in a chunk, we adjust back the \n\
|
||||
; source pointer and just drop through to the by-16 and by-4 loops to \n\
|
||||
; get the correct number of failing bytes. This necessarily means a \n\
|
||||
; few extra exceptions, but invalid user pointers shouldn't happen in \n\
|
||||
; time-critical code anyway. \n\
|
||||
jump 3b \n\
|
||||
subq 44,$r11 \n\
|
||||
\n\
|
||||
.previous \n\
|
||||
.section __ex_table,\"a\" \n\
|
||||
.dword 1b,4b \n\
|
||||
.previous"
|
||||
|
||||
/* Outputs */ : "=r" (dst), "=r" (src), "=r" (n), "=r" (retn)
|
||||
/* Inputs */ : "0" (dst), "1" (src), "2" (n), "3" (retn));
|
||||
|
||||
}
|
||||
|
||||
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. The return-value is the number of bytes that were
|
||||
inaccessible. */
|
||||
|
||||
unsigned long
|
||||
__copy_user_zeroing (void __user *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;
|
||||
|
||||
/* The best reason to align src is that we then know that a read-fault
|
||||
was for aligned bytes; there's no 1..3 remaining good bytes to
|
||||
pickle. */
|
||||
if (((unsigned long) src & 3) != 0)
|
||||
{
|
||||
if (((unsigned long) src & 1) && n != 0)
|
||||
{
|
||||
__asm_copy_from_user_1 (dst, src, retn);
|
||||
n--;
|
||||
}
|
||||
|
||||
if (((unsigned long) src & 2) && n >= 2)
|
||||
{
|
||||
__asm_copy_from_user_2 (dst, src, retn);
|
||||
n -= 2;
|
||||
}
|
||||
|
||||
/* We only need one check after the unalignment-adjustments, because
|
||||
if both adjustments were done, either both or neither reference
|
||||
had an exception. */
|
||||
if (retn != 0)
|
||||
goto copy_exception_bytes;
|
||||
}
|
||||
|
||||
/* Movem is dirt cheap. The overheap is low enough to always use the
|
||||
minimum possible block size as the threshold. */
|
||||
if (n >= 44)
|
||||
{
|
||||
/* 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. */
|
||||
__asm__ volatile ("\
|
||||
.ifnc %0%1%2%3,$r13$r11$r12$r10 \n\
|
||||
.err \n\
|
||||
.endif \n\
|
||||
\n\
|
||||
;; Save the registers we'll use in the movem process \n\
|
||||
;; on the stack. \n\
|
||||
subq 11*4,$sp \n\
|
||||
movem $r10,[$sp] \n\
|
||||
\n\
|
||||
;; Now we've got this: \n\
|
||||
;; r11 - src \n\
|
||||
;; r13 - dst \n\
|
||||
;; r12 - n \n\
|
||||
\n\
|
||||
;; Update n for the first loop \n\
|
||||
subq 44,$r12 \n\
|
||||
0: \n\
|
||||
movem [$r11+],$r10 \n\
|
||||
\n\
|
||||
subq 44,$r12 \n\
|
||||
bge 0b \n\
|
||||
movem $r10,[$r13+] \n\
|
||||
\n\
|
||||
4: \n\
|
||||
addq 44,$r12 ;; compensate for last loop underflowing n \n\
|
||||
\n\
|
||||
;; Restore registers from stack \n\
|
||||
movem [$sp+],$r10 \n\
|
||||
.section .fixup,\"ax\" \n\
|
||||
\n\
|
||||
;; Do not jump back into the loop if we fail. For some uses, we get a \n\
|
||||
;; page fault somewhere on the line. Without checking for page limits, \n\
|
||||
;; we don't know where, but we need to copy accurately and keep an \n\
|
||||
;; accurate count; not just clear the whole line. To do that, we fall \n\
|
||||
;; down in the code below, proceeding with smaller amounts. It should \n\
|
||||
;; be kept in mind that we have to cater to code like what at one time \n\
|
||||
;; was in fs/super.c: \n\
|
||||
;; i = size - copy_from_user((void *)page, data, size); \n\
|
||||
;; which would cause repeated faults while clearing the remainder of \n\
|
||||
;; the SIZE bytes at PAGE after the first fault. \n\
|
||||
;; A caveat here is that we must not fall through from a failing page \n\
|
||||
;; to a valid page. \n\
|
||||
\n\
|
||||
3: \n\
|
||||
jump 4b ;; Fall through, pretending the fault didn't happen. \n\
|
||||
nop \n\
|
||||
\n\
|
||||
.previous \n\
|
||||
.section __ex_table,\"a\" \n\
|
||||
.dword 0b,3b \n\
|
||||
.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. (Except with failing src.)
|
||||
|
||||
Since we want to keep src accurate, we can't use
|
||||
__asm_copy_from_user_N with N != (1, 2, 4); it updates dst and
|
||||
retn, but not src (by design; it's value is ignored elsewhere). */
|
||||
|
||||
while (n >= 4)
|
||||
{
|
||||
__asm_copy_from_user_4 (dst, src, retn);
|
||||
n -= 4;
|
||||
|
||||
if (retn)
|
||||
goto copy_exception_bytes;
|
||||
}
|
||||
|
||||
/* If we get here, there were no memory read faults. */
|
||||
switch (n)
|
||||
{
|
||||
/* These copies are at least "naturally aligned" (so we don't have
|
||||
to check each byte), due to the src alignment code before the
|
||||
movem loop. The *_3 case *will* get the correct count for retn. */
|
||||
case 0:
|
||||
/* This case deliberately left in (if you have doubts check the
|
||||
generated assembly code). */
|
||||
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;
|
||||
}
|
||||
|
||||
/* If we get here, retn correctly reflects the number of failing
|
||||
bytes. */
|
||||
return retn;
|
||||
|
||||
copy_exception_bytes:
|
||||
/* We already have "retn" bytes cleared, and need to clear the
|
||||
remaining "n" bytes. A non-optimized simple byte-for-byte in-line
|
||||
memset is preferred here, since this isn't speed-critical code and
|
||||
we'd rather have this a leaf-function than calling memset. */
|
||||
{
|
||||
char *endp;
|
||||
for (endp = dst + n; dst < endp; dst++)
|
||||
*dst = 0;
|
||||
}
|
||||
|
||||
return retn + n;
|
||||
}
|
||||
|
||||
/* Zero userspace. */
|
||||
|
||||
unsigned long
|
||||
__do_clear_user (void __user *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 >= 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 ("\
|
||||
.ifnc %0%1%2,$r13$r12$r10 \n\
|
||||
.err \n\
|
||||
.endif \n\
|
||||
\n\
|
||||
;; Save the registers we'll clobber in the movem process \n\
|
||||
;; on the stack. Don't mention them to gcc, it will only be \n\
|
||||
;; upset. \n\
|
||||
subq 11*4,$sp \n\
|
||||
movem $r10,[$sp] \n\
|
||||
\n\
|
||||
clear.d $r0 \n\
|
||||
clear.d $r1 \n\
|
||||
clear.d $r2 \n\
|
||||
clear.d $r3 \n\
|
||||
clear.d $r4 \n\
|
||||
clear.d $r5 \n\
|
||||
clear.d $r6 \n\
|
||||
clear.d $r7 \n\
|
||||
clear.d $r8 \n\
|
||||
clear.d $r9 \n\
|
||||
clear.d $r10 \n\
|
||||
clear.d $r11 \n\
|
||||
\n\
|
||||
;; Now we've got this: \n\
|
||||
;; r13 - dst \n\
|
||||
;; r12 - n \n\
|
||||
\n\
|
||||
;; Update n for the first loop \n\
|
||||
subq 12*4,$r12 \n\
|
||||
0: \n\
|
||||
subq 12*4,$r12 \n\
|
||||
1: \n\
|
||||
bge 0b \n\
|
||||
movem $r11,[$r13+] \n\
|
||||
\n\
|
||||
addq 12*4,$r12 ;; compensate for last loop underflowing n \n\
|
||||
\n\
|
||||
;; Restore registers from stack \n\
|
||||
movem [$sp+],$r10 \n\
|
||||
2: \n\
|
||||
.section .fixup,\"ax\" \n\
|
||||
3: \n\
|
||||
movem [$sp],$r10 \n\
|
||||
addq 12*4,$r10 \n\
|
||||
addq 12*4,$r13 \n\
|
||||
movem $r10,[$sp] \n\
|
||||
jump 0b \n\
|
||||
clear.d $r10 \n\
|
||||
\n\
|
||||
.previous \n\
|
||||
.section __ex_table,\"a\" \n\
|
||||
.dword 1b,3b \n\
|
||||
.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;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
# Makefile for the Linux/cris parts of the memory manager.
|
||||
|
||||
obj-y := mmu.o init.o tlb.o intmem.o
|
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* Set up paging and the MMU.
|
||||
*
|
||||
* Copyright (C) 2000-2003, Axis Communications AB.
|
||||
*
|
||||
* Authors: Bjorn Wesen <bjornw@axis.com>
|
||||
* Tobias Anderberg <tobiasa@axis.com>, CRISv32 port.
|
||||
*/
|
||||
#include <linux/config.h>
|
||||
#include <linux/mmzone.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/bootmem.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/config.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/types.h>
|
||||
#include <asm/mmu.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/arch/hwregs/asm/mmu_defs_asm.h>
|
||||
#include <asm/arch/hwregs/supp_reg.h>
|
||||
|
||||
extern void tlb_init(void);
|
||||
|
||||
/*
|
||||
* The kernel is already mapped with linear mapping at kseg_c so there's no
|
||||
* need to map it with a page table. However, head.S also temporarily mapped it
|
||||
* at kseg_4 thus the ksegs are set up again. Also clear the TLB and do various
|
||||
* other paging stuff.
|
||||
*/
|
||||
void __init
|
||||
cris_mmu_init(void)
|
||||
{
|
||||
unsigned long mmu_config;
|
||||
unsigned long mmu_kbase_hi;
|
||||
unsigned long mmu_kbase_lo;
|
||||
unsigned short mmu_page_id;
|
||||
|
||||
/*
|
||||
* Make sure the current pgd table points to something sane, even if it
|
||||
* is most probably not used until the next switch_mm.
|
||||
*/
|
||||
per_cpu(current_pgd, smp_processor_id()) = init_mm.pgd;
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
{
|
||||
pgd_t **pgd;
|
||||
pgd = (pgd_t**)&per_cpu(current_pgd, smp_processor_id());
|
||||
SUPP_BANK_SEL(1);
|
||||
SUPP_REG_WR(RW_MM_TLB_PGD, pgd);
|
||||
SUPP_BANK_SEL(2);
|
||||
SUPP_REG_WR(RW_MM_TLB_PGD, pgd);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Initialise the TLB. Function found in tlb.c. */
|
||||
tlb_init();
|
||||
|
||||
/* Enable exceptions and initialize the kernel segments. */
|
||||
mmu_config = ( REG_STATE(mmu, rw_mm_cfg, we, on) |
|
||||
REG_STATE(mmu, rw_mm_cfg, acc, on) |
|
||||
REG_STATE(mmu, rw_mm_cfg, ex, on) |
|
||||
REG_STATE(mmu, rw_mm_cfg, inv, on) |
|
||||
REG_STATE(mmu, rw_mm_cfg, seg_f, linear) |
|
||||
REG_STATE(mmu, rw_mm_cfg, seg_e, linear) |
|
||||
REG_STATE(mmu, rw_mm_cfg, seg_d, page) |
|
||||
REG_STATE(mmu, rw_mm_cfg, seg_c, linear) |
|
||||
REG_STATE(mmu, rw_mm_cfg, seg_b, linear) |
|
||||
#ifndef CONFIG_ETRAXFS_SIM
|
||||
REG_STATE(mmu, rw_mm_cfg, seg_a, page) |
|
||||
#else
|
||||
REG_STATE(mmu, rw_mm_cfg, seg_a, linear) |
|
||||
#endif
|
||||
REG_STATE(mmu, rw_mm_cfg, seg_9, page) |
|
||||
REG_STATE(mmu, rw_mm_cfg, seg_8, page) |
|
||||
REG_STATE(mmu, rw_mm_cfg, seg_7, page) |
|
||||
REG_STATE(mmu, rw_mm_cfg, seg_6, page) |
|
||||
REG_STATE(mmu, rw_mm_cfg, seg_5, page) |
|
||||
REG_STATE(mmu, rw_mm_cfg, seg_4, page) |
|
||||
REG_STATE(mmu, rw_mm_cfg, seg_3, page) |
|
||||
REG_STATE(mmu, rw_mm_cfg, seg_2, page) |
|
||||
REG_STATE(mmu, rw_mm_cfg, seg_1, page) |
|
||||
REG_STATE(mmu, rw_mm_cfg, seg_0, page));
|
||||
|
||||
mmu_kbase_hi = ( REG_FIELD(mmu, rw_mm_kbase_hi, base_f, 0x0) |
|
||||
REG_FIELD(mmu, rw_mm_kbase_hi, base_e, 0x8) |
|
||||
REG_FIELD(mmu, rw_mm_kbase_hi, base_d, 0x0) |
|
||||
#ifndef CONFIG_ETRAXFS_SIM
|
||||
REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 0x4) |
|
||||
#else
|
||||
REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 0x0) |
|
||||
#endif
|
||||
REG_FIELD(mmu, rw_mm_kbase_hi, base_b, 0xb) |
|
||||
#ifndef CONFIG_ETRAXFS_SIM
|
||||
REG_FIELD(mmu, rw_mm_kbase_hi, base_a, 0x0) |
|
||||
#else
|
||||
REG_FIELD(mmu, rw_mm_kbase_hi, base_a, 0xa) |
|
||||
#endif
|
||||
REG_FIELD(mmu, rw_mm_kbase_hi, base_9, 0x0) |
|
||||
REG_FIELD(mmu, rw_mm_kbase_hi, base_8, 0x0));
|
||||
|
||||
mmu_kbase_lo = ( REG_FIELD(mmu, rw_mm_kbase_lo, base_7, 0x0) |
|
||||
REG_FIELD(mmu, rw_mm_kbase_lo, base_6, 0x0) |
|
||||
REG_FIELD(mmu, rw_mm_kbase_lo, base_5, 0x0) |
|
||||
REG_FIELD(mmu, rw_mm_kbase_lo, base_4, 0x0) |
|
||||
REG_FIELD(mmu, rw_mm_kbase_lo, base_3, 0x0) |
|
||||
REG_FIELD(mmu, rw_mm_kbase_lo, base_2, 0x0) |
|
||||
REG_FIELD(mmu, rw_mm_kbase_lo, base_1, 0x0) |
|
||||
REG_FIELD(mmu, rw_mm_kbase_lo, base_0, 0x0));
|
||||
|
||||
mmu_page_id = REG_FIELD(mmu, rw_mm_tlb_hi, pid, 0);
|
||||
|
||||
/* Update the instruction MMU. */
|
||||
SUPP_BANK_SEL(BANK_IM);
|
||||
SUPP_REG_WR(RW_MM_CFG, mmu_config);
|
||||
SUPP_REG_WR(RW_MM_KBASE_HI, mmu_kbase_hi);
|
||||
SUPP_REG_WR(RW_MM_KBASE_LO, mmu_kbase_lo);
|
||||
SUPP_REG_WR(RW_MM_TLB_HI, mmu_page_id);
|
||||
|
||||
/* Update the data MMU. */
|
||||
SUPP_BANK_SEL(BANK_DM);
|
||||
SUPP_REG_WR(RW_MM_CFG, mmu_config);
|
||||
SUPP_REG_WR(RW_MM_KBASE_HI, mmu_kbase_hi);
|
||||
SUPP_REG_WR(RW_MM_KBASE_LO, mmu_kbase_lo);
|
||||
SUPP_REG_WR(RW_MM_TLB_HI, mmu_page_id);
|
||||
|
||||
SPEC_REG_WR(SPEC_REG_PID, 0);
|
||||
|
||||
/*
|
||||
* The MMU has been enabled ever since head.S but just to make it
|
||||
* totally obvious enable it here as well.
|
||||
*/
|
||||
SUPP_BANK_SEL(BANK_GC);
|
||||
SUPP_REG_WR(RW_GC_CFG, 0xf); /* IMMU, DMMU, ICache, DCache on */
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
cris_mmu_init();
|
||||
|
||||
/*
|
||||
* Initialize the bad page table and bad page to point to a couple of
|
||||
* allocated pages.
|
||||
*/
|
||||
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 it 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, &contig_page_data, zones_size, PAGE_OFFSET >> PAGE_SHIFT, 0);
|
||||
|
||||
mem_map = contig_page_data.node_mem_map;
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Simple allocator for internal RAM in ETRAX FS
|
||||
*
|
||||
* Copyright (c) 2004 Axis Communications AB.
|
||||
*/
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/memmap.h>
|
||||
|
||||
#define STATUS_FREE 0
|
||||
#define STATUS_ALLOCATED 1
|
||||
|
||||
struct intmem_allocation {
|
||||
struct list_head entry;
|
||||
unsigned int size;
|
||||
unsigned offset;
|
||||
char status;
|
||||
};
|
||||
|
||||
|
||||
static struct list_head intmem_allocations;
|
||||
static void* intmem_virtual;
|
||||
|
||||
static void crisv32_intmem_init(void)
|
||||
{
|
||||
static int initiated = 0;
|
||||
if (!initiated) {
|
||||
struct intmem_allocation* alloc =
|
||||
(struct intmem_allocation*)kmalloc(sizeof *alloc, GFP_KERNEL);
|
||||
INIT_LIST_HEAD(&intmem_allocations);
|
||||
intmem_virtual = ioremap(MEM_INTMEM_START, MEM_INTMEM_SIZE);
|
||||
initiated = 1;
|
||||
alloc->size = MEM_INTMEM_SIZE;
|
||||
alloc->offset = 0;
|
||||
alloc->status = STATUS_FREE;
|
||||
list_add_tail(&alloc->entry, &intmem_allocations);
|
||||
}
|
||||
}
|
||||
|
||||
void* crisv32_intmem_alloc(unsigned size, unsigned align)
|
||||
{
|
||||
struct intmem_allocation* allocation;
|
||||
struct intmem_allocation* tmp;
|
||||
void* ret = NULL;
|
||||
|
||||
preempt_disable();
|
||||
crisv32_intmem_init();
|
||||
|
||||
list_for_each_entry_safe(allocation, tmp, &intmem_allocations, entry) {
|
||||
int alignment = allocation->offset % align;
|
||||
alignment = alignment ? align - alignment : alignment;
|
||||
|
||||
if (allocation->status == STATUS_FREE &&
|
||||
allocation->size >= size + alignment) {
|
||||
if (allocation->size > size + alignment) {
|
||||
struct intmem_allocation* alloc =
|
||||
(struct intmem_allocation*)
|
||||
kmalloc(sizeof *alloc, GFP_ATOMIC);
|
||||
alloc->status = STATUS_FREE;
|
||||
alloc->size = allocation->size - size - alignment;
|
||||
alloc->offset = allocation->offset + size;
|
||||
list_add(&alloc->entry, &allocation->entry);
|
||||
|
||||
if (alignment) {
|
||||
struct intmem_allocation* tmp;
|
||||
tmp = (struct intmem_allocation*)
|
||||
kmalloc(sizeof *tmp, GFP_ATOMIC);
|
||||
tmp->offset = allocation->offset;
|
||||
tmp->size = alignment;
|
||||
tmp->status = STATUS_FREE;
|
||||
allocation->offset += alignment;
|
||||
list_add_tail(&tmp->entry, &allocation->entry);
|
||||
}
|
||||
}
|
||||
allocation->status = STATUS_ALLOCATED;
|
||||
allocation->size = size;
|
||||
ret = (void*)((int)intmem_virtual + allocation->offset);
|
||||
}
|
||||
}
|
||||
preempt_enable();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void crisv32_intmem_free(void* addr)
|
||||
{
|
||||
struct intmem_allocation* allocation;
|
||||
struct intmem_allocation* tmp;
|
||||
|
||||
if (addr == NULL)
|
||||
return;
|
||||
|
||||
preempt_disable();
|
||||
crisv32_intmem_init();
|
||||
|
||||
list_for_each_entry_safe(allocation, tmp, &intmem_allocations, entry) {
|
||||
if (allocation->offset == (int)(addr - intmem_virtual)) {
|
||||
struct intmem_allocation* prev =
|
||||
list_entry(allocation->entry.prev,
|
||||
struct intmem_allocation, entry);
|
||||
struct intmem_allocation* next =
|
||||
list_entry(allocation->entry.next,
|
||||
struct intmem_allocation, entry);
|
||||
|
||||
allocation->status = STATUS_FREE;
|
||||
/* Join with prev and/or next if also free */
|
||||
if (prev->status == STATUS_FREE) {
|
||||
prev->size += allocation->size;
|
||||
list_del(&allocation->entry);
|
||||
kfree(allocation);
|
||||
allocation = prev;
|
||||
}
|
||||
if (next->status == STATUS_FREE) {
|
||||
allocation->size += next->size;
|
||||
list_del(&next->entry);
|
||||
kfree(next);
|
||||
}
|
||||
preempt_enable();
|
||||
return;
|
||||
}
|
||||
}
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
void* crisv32_intmem_phys_to_virt(unsigned long addr)
|
||||
{
|
||||
return (void*)(addr - MEM_INTMEM_START+
|
||||
(unsigned long)intmem_virtual);
|
||||
}
|
||||
|
||||
unsigned long crisv32_intmem_virt_to_phys(void* addr)
|
||||
{
|
||||
return (unsigned long)((unsigned long )addr -
|
||||
(unsigned long)intmem_virtual + MEM_INTMEM_START);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* Copyright (C) 2003 Axis Communications AB
|
||||
*
|
||||
* Authors: Mikael Starvik (starvik@axis.com)
|
||||
*
|
||||
* Code for the fault low-level handling routines.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <asm/page.h>
|
||||
#include <asm/pgtable.h>
|
||||
|
||||
; Save all register. Must save in same order as struct pt_regs.
|
||||
.macro SAVE_ALL
|
||||
subq 12, $sp
|
||||
move $erp, [$sp]
|
||||
subq 4, $sp
|
||||
move $srp, [$sp]
|
||||
subq 4, $sp
|
||||
move $ccs, [$sp]
|
||||
subq 4, $sp
|
||||
move $spc, [$sp]
|
||||
subq 4, $sp
|
||||
move $mof, [$sp]
|
||||
subq 4, $sp
|
||||
move $srs, [$sp]
|
||||
subq 4, $sp
|
||||
move.d $acr, [$sp]
|
||||
subq 14*4, $sp
|
||||
movem $r13, [$sp]
|
||||
subq 4, $sp
|
||||
move.d $r10, [$sp]
|
||||
.endm
|
||||
|
||||
; Bus fault handler. Extracts relevant information and calls mm subsystem
|
||||
; to handle the fault.
|
||||
.macro MMU_BUS_FAULT_HANDLER handler, mmu, we, ex
|
||||
.globl \handler
|
||||
\handler:
|
||||
SAVE_ALL
|
||||
move \mmu, $srs ; Select MMU support register bank
|
||||
move.d $sp, $r11 ; regs
|
||||
moveq 1, $r12 ; protection fault
|
||||
moveq \we, $r13 ; write exception?
|
||||
orq \ex << 1, $r13 ; execute?
|
||||
move $s3, $r10 ; rw_mm_cause
|
||||
and.d ~8191, $r10 ; Get faulting page start address
|
||||
|
||||
jsr do_page_fault
|
||||
nop
|
||||
ba ret_from_intr
|
||||
nop
|
||||
.endm
|
||||
|
||||
; Refill handler. Three cases may occur:
|
||||
; 1. PMD and PTE exists in mm subsystem but not in TLB
|
||||
; 2. PMD exists but not PTE
|
||||
; 3. PMD doesn't exist
|
||||
; The code below handles case 1 and calls the mm subsystem for case 2 and 3.
|
||||
; Do not touch this code without very good reasons and extensive testing.
|
||||
; Note that the code is optimized to minimize stalls (makes the code harder
|
||||
; to read).
|
||||
;
|
||||
; Each page is 8 KB. Each PMD holds 8192/4 PTEs (each PTE is 4 bytes) so each
|
||||
; PMD holds 16 MB of virtual memory.
|
||||
; Bits 0-12 : Offset within a page
|
||||
; Bits 13-23 : PTE offset within a PMD
|
||||
; Bits 24-31 : PMD offset within the PGD
|
||||
|
||||
.macro MMU_REFILL_HANDLER handler, mmu
|
||||
.globl \handler
|
||||
\handler:
|
||||
subq 4, $sp
|
||||
; (The pipeline stalls for one cycle; $sp used as address in the next cycle.)
|
||||
move $srs, [$sp]
|
||||
subq 4, $sp
|
||||
move \mmu, $srs ; Select MMU support register bank
|
||||
move.d $acr, [$sp]
|
||||
subq 4, $sp
|
||||
move.d $r0, [$sp]
|
||||
#ifdef CONFIG_SMP
|
||||
move $s7, $acr ; PGD
|
||||
#else
|
||||
move.d per_cpu__current_pgd, $acr ; PGD
|
||||
#endif
|
||||
; Look up PMD in PGD
|
||||
move $s3, $r0 ; rw_mm_cause
|
||||
lsrq 24, $r0 ; Get PMD index into PGD (bit 24-31)
|
||||
move.d [$acr], $acr ; PGD for the current process
|
||||
addi $r0.d, $acr, $acr
|
||||
move $s3, $r0 ; rw_mm_cause
|
||||
move.d [$acr], $acr ; Get PMD
|
||||
beq 1f
|
||||
; Look up PTE in PMD
|
||||
lsrq PAGE_SHIFT, $r0
|
||||
and.w PAGE_MASK, $acr ; Remove PMD flags
|
||||
and.d 0x7ff, $r0 ; Get PTE index into PMD (bit 13-23)
|
||||
addi $r0.d, $acr, $acr
|
||||
move.d [$acr], $acr ; Get PTE
|
||||
beq 2f
|
||||
move.d [$sp+], $r0 ; Pop r0 in delayslot
|
||||
; Store in TLB
|
||||
move $acr, $s5
|
||||
; Return
|
||||
move.d [$sp+], $acr
|
||||
move [$sp], $srs
|
||||
addq 4, $sp
|
||||
rete
|
||||
rfe
|
||||
1: ; PMD missing, let the mm subsystem fix it up.
|
||||
move.d [$sp+], $r0 ; Pop r0
|
||||
2: ; PTE missing, let the mm subsystem fix it up.
|
||||
move.d [$sp+], $acr
|
||||
move [$sp], $srs
|
||||
addq 4, $sp
|
||||
SAVE_ALL
|
||||
move \mmu, $srs
|
||||
move.d $sp, $r11 ; regs
|
||||
clear.d $r12 ; Not a protection fault
|
||||
move.w PAGE_MASK, $acr
|
||||
move $s3, $r10 ; rw_mm_cause
|
||||
btstq 9, $r10 ; Check if write access
|
||||
smi $r13
|
||||
and.w PAGE_MASK, $r10 ; Get VPN (virtual address)
|
||||
jsr do_page_fault
|
||||
and.w $acr, $r10
|
||||
; Return
|
||||
ba ret_from_intr
|
||||
nop
|
||||
.endm
|
||||
|
||||
; This is the MMU bus fault handlers.
|
||||
|
||||
MMU_REFILL_HANDLER i_mmu_refill, 1
|
||||
MMU_BUS_FAULT_HANDLER i_mmu_invalid, 1, 0, 0
|
||||
MMU_BUS_FAULT_HANDLER i_mmu_access, 1, 0, 0
|
||||
MMU_BUS_FAULT_HANDLER i_mmu_execute, 1, 0, 1
|
||||
MMU_REFILL_HANDLER d_mmu_refill, 2
|
||||
MMU_BUS_FAULT_HANDLER d_mmu_invalid, 2, 0, 0
|
||||
MMU_BUS_FAULT_HANDLER d_mmu_access, 2, 0, 0
|
||||
MMU_BUS_FAULT_HANDLER d_mmu_write, 2, 1, 0
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue