Merge with Linus' 2.6 tree

This commit is contained in:
Russell King 2005-07-28 09:30:20 +01:00 committed by Russell King
commit 661299d9d0
628 changed files with 72461 additions and 6599 deletions

View file

@ -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

View file

@ -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
==================

View file

@ -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.

View file

@ -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
View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 \

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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:

View file

@ -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

View file

@ -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

View file

@ -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 */

View file

@ -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 */

View file

@ -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++)
{

View file

@ -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

View file

@ -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;
}

View file

@ -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 */
}

View file

@ -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

View file

@ -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);

View file

@ -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);

View file

@ -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

View file

@ -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");

View file

@ -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

View file

@ -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);

View file

@ -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);

View file

@ -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 @@ kgdb_handle_serial:
bne goback
nop
move.d [reg+0x5E], $r10 ; Get DCCR
btstq 8, $r10 ; Test the U-flag.
bmi goback
nop
;;
;; Handle the communication
;;

View file

@ -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, &regs, 0, NULL, NULL);

View file

@ -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;

View file

@ -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;

View file

@ -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");
}

View file

@ -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;
}

View file

@ -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) */

View file

@ -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 */

296
arch/cris/arch-v32/Kconfig Normal file
View file

@ -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.

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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
}

View file

@ -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"

View file

@ -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");
}

View file

@ -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:

View file

@ -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

View file

@ -0,0 +1,20 @@
MEMORY
{
flash : ORIGIN = 0x00000000,
LENGTH = 0x00100000
}
SECTIONS
{
.text :
{
stext = . ;
*(.text)
etext = . ;
} > flash
.data :
{
*(.data)
edata = . ;
} > flash
}

View file

@ -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.

View file

@ -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/

View file

@ -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

View file

@ -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);

View file

@ -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 ********************************/

View file

@ -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);

View file

@ -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);

View file

@ -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;
}

View file

@ -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);

View file

@ -0,0 +1,5 @@
#
# Makefile for Etrax cardbus driver
#
obj-$(CONFIG_ETRAX_CARDBUS) += bios.o dma.o

View file

@ -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);

View file

@ -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

View file

@ -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:

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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);

View file

@ -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);
}

View file

@ -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

View file

@ -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
}
}

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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(&regs, 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, &regs, 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);
}

View file

@ -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;
}

View file

@ -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");
}

View file

@ -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(&current->sighand->siglock);
saveset = current->blocked;
siginitset(&current->blocked, mask);
recalc_sigpending();
spin_unlock_irq(&current->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(&current->sighand->siglock);
saveset = current->blocked;
current->blocked = newset;
recalc_sigpending();
spin_unlock_irq(&current->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(&current->sighand->siglock);
current->blocked = set;
recalc_sigpending();
spin_unlock_irq(&current->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(&current->sighand->siglock);
current->blocked = set;
recalc_sigpending();
spin_unlock_irq(&current->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(&current->sighand->siglock);
sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
sigaddset(&current->blocked,sig);
recalc_sigpending();
spin_unlock_irq(&current->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 = &current->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);

View file

@ -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;
}

View file

@ -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
}

View file

@ -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);
}

View file

@ -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 );
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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:

View file

@ -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

View file

@ -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() */

View file

@ -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:

View file

@ -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

View file

@ -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() */

View file

@ -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;
}

View file

@ -0,0 +1,3 @@
# Makefile for the Linux/cris parts of the memory manager.
obj-y := mmu.o init.o tlb.o intmem.o

View file

@ -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;
}

View file

@ -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);
}

141
arch/cris/arch-v32/mm/mmu.S Normal file
View file

@ -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